Compare commits
3 Commits
claude/iss
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ccfa25388 | |||
| 5acce0f015 | |||
| 64d711e256 |
10
api/health/index.json
Normal file
10
api/health/index.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"status": "ok",
|
||||
"services": {
|
||||
"api": true,
|
||||
"agent_loop": false,
|
||||
"websocket": false
|
||||
},
|
||||
"uptime": null,
|
||||
"version": "20260322.230710"
|
||||
}
|
||||
@@ -17,14 +17,17 @@ Generates:
|
||||
"""
|
||||
|
||||
import html
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
SITE_URL = "https://alexanderwhitestone.com"
|
||||
BLOG_DIR = Path(__file__).parent.parent / "blog"
|
||||
ROOT_DIR = Path(__file__).parent.parent
|
||||
BLOG_DIR = ROOT_DIR / "blog"
|
||||
POSTS_DIR = BLOG_DIR / "posts"
|
||||
HEALTH_DIR = ROOT_DIR / "api" / "health"
|
||||
|
||||
PAGE_STYLE = """\
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
@@ -291,6 +294,23 @@ def generate_feed(posts):
|
||||
print(f" Generated feed with {len(entries)} entry/entries.")
|
||||
|
||||
|
||||
def generate_health():
|
||||
"""Generate api/health/index.json with build-time metadata."""
|
||||
HEALTH_DIR.mkdir(parents=True, exist_ok=True)
|
||||
health = {
|
||||
"status": "ok",
|
||||
"services": {
|
||||
"api": True,
|
||||
"agent_loop": False,
|
||||
"websocket": False,
|
||||
},
|
||||
"uptime": None,
|
||||
"version": datetime.now(timezone.utc).strftime("%Y%m%d.%H%M%S"),
|
||||
}
|
||||
(HEALTH_DIR / "index.json").write_text(json.dumps(health, indent=2) + "\n")
|
||||
print(" Generated api/health endpoint.")
|
||||
|
||||
|
||||
def main():
|
||||
print("Building The Scrolls...")
|
||||
posts = load_posts()
|
||||
@@ -299,6 +319,7 @@ def main():
|
||||
print(f" Built: {out.relative_to(BLOG_DIR.parent)}")
|
||||
generate_index(posts)
|
||||
generate_feed(posts)
|
||||
generate_health()
|
||||
print("Build complete.")
|
||||
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
</div>
|
||||
<div class="status-line">AGENTS: <span id="agent-count">0</span></div>
|
||||
<button id="retry-btn">Retry connection</button>
|
||||
<a id="fallback-link" href="/api/ui" style="display:none; margin-top:6px; color:#8a7f6a; font-size:0.7rem;">Open API dashboard →</a>
|
||||
</div>
|
||||
|
||||
<!-- Reject unknown sub-paths: only /world/ is valid -->
|
||||
@@ -99,6 +100,24 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<!-- Fallback: if main.js fails to load, don't leave user on INITIALIZING forever -->
|
||||
<script>
|
||||
(function() {
|
||||
var INIT_TIMEOUT_MS = 8000;
|
||||
window.__workshopBooted = false;
|
||||
setTimeout(function() {
|
||||
if (window.__workshopBooted) return;
|
||||
var dot = document.getElementById('status-dot');
|
||||
var text = document.getElementById('status-text');
|
||||
var btn = document.getElementById('retry-btn');
|
||||
var fallback = document.getElementById('fallback-link');
|
||||
if (dot) { dot.className = 'dot offline'; }
|
||||
if (text) { text.textContent = 'WORKSHOP UNREACHABLE'; }
|
||||
if (btn) { btn.style.display = 'block'; }
|
||||
if (fallback) { fallback.style.display = 'block'; }
|
||||
}, INIT_TIMEOUT_MS);
|
||||
})();
|
||||
</script>
|
||||
<!-- Three.js scene will mount to #scene -->
|
||||
<script type="module" src="main.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -23,8 +23,12 @@ const dom = {
|
||||
text: document.getElementById('status-text'),
|
||||
agents: document.getElementById('agent-count'),
|
||||
retryBtn: document.getElementById('retry-btn'),
|
||||
fallback: document.getElementById('fallback-link'),
|
||||
};
|
||||
|
||||
// Signal to inline fallback script that main.js loaded successfully
|
||||
window.__workshopBooted = true;
|
||||
|
||||
let ws = null;
|
||||
let autoRetries = 0;
|
||||
let connectTimer = null;
|
||||
@@ -32,7 +36,9 @@ let connectTimer = null;
|
||||
function setStatus(state, message) {
|
||||
dom.dot.className = 'dot ' + state;
|
||||
dom.text.textContent = message;
|
||||
dom.retryBtn.style.display = state === Status.OFFLINE ? 'block' : 'none';
|
||||
var isOffline = state === Status.OFFLINE;
|
||||
dom.retryBtn.style.display = isOffline ? 'block' : 'none';
|
||||
if (dom.fallback) dom.fallback.style.display = isOffline ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function setAgentCount(n) {
|
||||
@@ -105,7 +111,7 @@ function onFail() {
|
||||
setStatus(Status.CONNECTING, 'RETRYING (' + autoRetries + '/' + MAX_AUTO_RETRIES + ')\u2026');
|
||||
setTimeout(connect, RETRY_DELAY_MS);
|
||||
} else {
|
||||
setStatus(Status.OFFLINE, 'OFFLINE \u2014 backend unreachable');
|
||||
setStatus(Status.OFFLINE, 'WORKSHOP OFFLINE');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user