Spaces:
Running
Running
feat(operator): add /unified 4-pane operator shell (ADDITIVE)
Browse filesAuthor: Yachay <yachay@szlholdings.dev>
DCO: Signed-off-by: Yachay <yachay@szlholdings.dev>
Change-class: ADDITIVE
Co-Authored-By: Perplexity Computer Agent
- serve.py +15 -0
- static/operator-unified.html +696 -0
- web/operator-unified.html +696 -0
serve.py
CHANGED
|
@@ -755,6 +755,21 @@ async def operator_shell() -> FileResponse:
|
|
| 755 |
return FileResponse(INDEX_HTML, media_type="text/html")
|
| 756 |
|
| 757 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 758 |
|
| 759 |
|
| 760 |
# ===========================================================================
|
|
|
|
| 755 |
return FileResponse(INDEX_HTML, media_type="text/html")
|
| 756 |
|
| 757 |
|
| 758 |
+
# ADDITIVE (Unified 4-pane Operator Shell, 2026-06-01, Yachay / Perplexity
|
| 759 |
+
# Computer Agent): NEW /unified route serving the self-contained 4-pane shell
|
| 760 |
+
# (terrain + right panel + Cmd-K + receipt tunnel). Registered BEFORE the
|
| 761 |
+
# /{full_path:path} catch-all so it resolves LOCALLY. ADDITIVE — does NOT touch
|
| 762 |
+
# the existing /operator route. File ships in static/ (COPY static/ already in
|
| 763 |
+
# Dockerfile). Doctrine v11 LOCKED 749/14/163 unchanged.
|
| 764 |
+
@app.get("/unified")
|
| 765 |
+
@app.get("/killinchu/unified")
|
| 766 |
+
async def unified_operator_shell() -> FileResponse:
|
| 767 |
+
_page = STATIC_DIR / "operator-unified.html"
|
| 768 |
+
if _page.is_file():
|
| 769 |
+
return FileResponse(_page, media_type="text/html")
|
| 770 |
+
return JSONResponse({"error": "operator-unified.html not deployed"}, status_code=404)
|
| 771 |
+
|
| 772 |
+
|
| 773 |
|
| 774 |
|
| 775 |
# ===========================================================================
|
static/operator-unified.html
ADDED
|
@@ -0,0 +1,696 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>a11oy — Unified Operator Shell</title>
|
| 7 |
+
<meta name="description" content="a11oy unified 4-pane operator shell: living terrain, organ health rings, Cmd-K command bar, and receipt tunnel. Sovereign, offline, no CDN." />
|
| 8 |
+
<style>
|
| 9 |
+
/* ===== Sovereign font stack: NO external CDN. Inter / JetBrains Mono if present, else system fallback. ===== */
|
| 10 |
+
:root{
|
| 11 |
+
--bg:#0A0E1A; /* near-black navy */
|
| 12 |
+
--green:#00C389; /* healthy */
|
| 13 |
+
--amber:#F0B429; /* warning */
|
| 14 |
+
--red:#DF2A4A; /* critical */
|
| 15 |
+
--gray:#64748B; /* offline */
|
| 16 |
+
--purple:#7C3AED; /* anomaly */
|
| 17 |
+
--text:#E2E8F0; /* text primary */
|
| 18 |
+
--muted:#94A3B8; /* text muted */
|
| 19 |
+
--gold:#D4A444; /* brand accent (Khipu glyph + headers) */
|
| 20 |
+
--panel:#0E1424;
|
| 21 |
+
--panel2:#121a2e;
|
| 22 |
+
--rule:#1e293b;
|
| 23 |
+
--ui:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
| 24 |
+
--mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
|
| 25 |
+
}
|
| 26 |
+
*{box-sizing:border-box;}
|
| 27 |
+
html,body{margin:0;height:100%;background:var(--bg);color:var(--text);font-family:var(--ui);
|
| 28 |
+
-webkit-font-smoothing:antialiased;overflow:hidden;}
|
| 29 |
+
a{color:var(--gold);text-decoration:none;}
|
| 30 |
+
::-webkit-scrollbar{width:8px;height:8px;}
|
| 31 |
+
::-webkit-scrollbar-thumb{background:#23304a;border-radius:4px;}
|
| 32 |
+
::-webkit-scrollbar-track{background:transparent;}
|
| 33 |
+
|
| 34 |
+
.label{font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);font-weight:600;}
|
| 35 |
+
.gold{color:var(--gold);}
|
| 36 |
+
.rule{height:1px;background:var(--rule);border:0;margin:0;}
|
| 37 |
+
.mono{font-family:var(--mono);}
|
| 38 |
+
|
| 39 |
+
/* ===== Top bar ===== */
|
| 40 |
+
#topbar{position:fixed;top:0;left:0;right:0;height:46px;display:flex;align-items:center;gap:14px;
|
| 41 |
+
padding:0 18px;border-bottom:1px solid var(--rule);background:rgba(10,14,26,.86);backdrop-filter:blur(6px);z-index:40;}
|
| 42 |
+
#brand{display:flex;align-items:center;gap:10px;font-weight:700;letter-spacing:.06em;}
|
| 43 |
+
#brand svg{flex:0 0 auto;}
|
| 44 |
+
#brand .name{font-size:15px;}
|
| 45 |
+
#brand .name b{color:var(--gold);}
|
| 46 |
+
#organtag{font-family:var(--mono);font-size:11px;color:var(--muted);padding:3px 8px;border:1px solid var(--rule);border-radius:4px;}
|
| 47 |
+
#topbar .spacer{flex:1;}
|
| 48 |
+
#livedot{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--muted);}
|
| 49 |
+
#livedot .dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 0 0 rgba(0,195,137,.6);animation:pulse 2s infinite;}
|
| 50 |
+
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(0,195,137,.5);}70%{box-shadow:0 0 0 7px rgba(0,195,137,0);}100%{box-shadow:0 0 0 0 rgba(0,195,137,0);}}
|
| 51 |
+
|
| 52 |
+
/* ===== Layout: TERRAIN 60% | RIGHT PANEL 25% | RECEIPT TUNNEL 15% ===== */
|
| 53 |
+
#shell{position:fixed;top:46px;left:0;right:0;bottom:84px;display:grid;
|
| 54 |
+
grid-template-columns:60fr 25fr 15fr;gap:0;}
|
| 55 |
+
/* TERRAIN */
|
| 56 |
+
#terrain{position:relative;overflow:hidden;border-right:1px solid var(--rule);}
|
| 57 |
+
#pfield{position:absolute;inset:0;width:100%;height:100%;display:block;}
|
| 58 |
+
#terrain .tlabel{position:absolute;top:14px;left:16px;z-index:3;}
|
| 59 |
+
#terrain .thint{position:absolute;bottom:14px;left:16px;z-index:3;font-size:11px;color:var(--muted);}
|
| 60 |
+
/* RIGHT PANEL */
|
| 61 |
+
#rightpanel{background:var(--panel);border-right:1px solid var(--rule);display:flex;flex-direction:column;overflow:hidden;}
|
| 62 |
+
#rp-head{padding:14px 16px 10px;}
|
| 63 |
+
#rp-title{font-size:14px;font-weight:700;letter-spacing:.04em;margin-top:6px;display:flex;align-items:center;gap:8px;}
|
| 64 |
+
#rp-title .swatch{width:10px;height:10px;border-radius:50%;background:var(--gray);}
|
| 65 |
+
/* Golden signals */
|
| 66 |
+
#signals{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:0 16px 12px;}
|
| 67 |
+
.sig{background:var(--panel2);border:1px solid var(--rule);border-radius:6px;padding:8px 10px;}
|
| 68 |
+
.sig .k{font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);}
|
| 69 |
+
.sig .v{font-family:var(--mono);font-size:16px;font-weight:600;margin-top:2px;}
|
| 70 |
+
.sig svg{display:block;margin-top:4px;width:100%;height:18px;}
|
| 71 |
+
/* tabs */
|
| 72 |
+
#tabs{display:flex;gap:0;padding:0 10px;border-bottom:1px solid var(--rule);}
|
| 73 |
+
.tab{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);padding:9px 9px;cursor:pointer;border-bottom:2px solid transparent;font-weight:600;}
|
| 74 |
+
.tab:hover{color:var(--text);}
|
| 75 |
+
.tab.active{color:var(--gold);border-bottom-color:var(--gold);}
|
| 76 |
+
#rp-body{flex:1;overflow-y:auto;padding:12px 16px 16px;}
|
| 77 |
+
.empty{color:var(--muted);font-size:12px;padding:20px 0;text-align:center;}
|
| 78 |
+
.rcard{border:1px solid var(--rule);border-radius:6px;padding:8px 10px;margin-bottom:8px;background:var(--panel2);}
|
| 79 |
+
.rcard .top{display:flex;justify-content:space-between;align-items:center;gap:8px;}
|
| 80 |
+
.rcard .verb{font-size:11px;font-weight:600;letter-spacing:.04em;}
|
| 81 |
+
.rcard .meta{font-family:var(--mono);font-size:10px;color:var(--muted);margin-top:4px;word-break:break-all;}
|
| 82 |
+
.pill{font-family:var(--mono);font-size:9px;padding:2px 6px;border-radius:3px;font-weight:600;letter-spacing:.05em;}
|
| 83 |
+
.pill.pass{background:rgba(0,195,137,.16);color:var(--green);}
|
| 84 |
+
.pill.reject{background:rgba(223,42,74,.16);color:var(--red);}
|
| 85 |
+
.pill.info{background:rgba(124,58,237,.16);color:var(--purple);}
|
| 86 |
+
/* RECEIPT TUNNEL */
|
| 87 |
+
#tunnel{background:linear-gradient(180deg,#080b14,#0A0E1A);display:flex;flex-direction:column;overflow:hidden;}
|
| 88 |
+
#tunnel .th{padding:12px 12px 8px;display:flex;align-items:center;justify-content:space-between;}
|
| 89 |
+
#tunnel .tail{font-size:10px;color:var(--muted);display:inline-flex;align-items:center;gap:5px;cursor:pointer;}
|
| 90 |
+
#tunnel .tail .dot{width:7px;height:7px;border-radius:50%;background:var(--green);animation:pulse 2s infinite;}
|
| 91 |
+
#tunnel .tail.off .dot{background:var(--gray);animation:none;}
|
| 92 |
+
#tstream{flex:1;overflow-y:auto;padding:0 10px 14px;}
|
| 93 |
+
.tcard{border:1px solid var(--rule);border-left:3px solid var(--gray);border-radius:5px;padding:7px 9px;margin-bottom:7px;
|
| 94 |
+
background:rgba(18,26,46,.7);animation:slidein .35s ease;}
|
| 95 |
+
@keyframes slidein{from{opacity:0;transform:translateY(-8px) scale(.98);}to{opacity:1;transform:none;}}
|
| 96 |
+
.tcard.pass{border-left-color:var(--green);}
|
| 97 |
+
.tcard.reject{border-left-color:var(--red);}
|
| 98 |
+
.tcard.info{border-left-color:var(--purple);}
|
| 99 |
+
.tcard .ts{font-family:var(--mono);font-size:9px;color:var(--muted);}
|
| 100 |
+
.tcard .ln{font-family:var(--mono);font-size:10px;margin-top:3px;color:var(--text);}
|
| 101 |
+
.tcard .glyph{font-size:11px;}
|
| 102 |
+
.tcard .hash{font-family:var(--mono);font-size:9px;color:var(--gold);margin-top:3px;word-break:break-all;}
|
| 103 |
+
|
| 104 |
+
/* ===== Command bar (Cmd-K) ===== */
|
| 105 |
+
#cmdbar{position:fixed;left:0;right:0;bottom:30px;height:54px;display:flex;align-items:center;gap:10px;
|
| 106 |
+
padding:0 18px;border-top:1px solid var(--rule);background:rgba(14,20,36,.92);z-index:40;}
|
| 107 |
+
#cmdbar .kbd{font-family:var(--mono);font-size:10px;color:var(--muted);border:1px solid var(--rule);border-radius:4px;padding:3px 6px;}
|
| 108 |
+
#cmdform{flex:1;display:flex;align-items:center;gap:10px;}
|
| 109 |
+
#cmdprefix{font-family:var(--mono);color:var(--gold);font-weight:700;font-size:15px;}
|
| 110 |
+
#cmdinput{flex:1;background:transparent;border:0;outline:none;color:var(--text);font-family:var(--mono);font-size:14px;}
|
| 111 |
+
#cmdinput::placeholder{color:var(--gray);}
|
| 112 |
+
#cmdhint{font-size:10px;color:var(--muted);font-family:var(--mono);max-width:46%;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
| 113 |
+
|
| 114 |
+
/* ===== Disclosure footer ===== */
|
| 115 |
+
#disclosure{position:fixed;left:0;right:0;bottom:0;height:30px;display:flex;align-items:center;justify-content:center;
|
| 116 |
+
gap:14px;font-family:var(--mono);font-size:10px;color:var(--muted);border-top:1px solid var(--rule);background:var(--bg);z-index:40;padding:0 12px;}
|
| 117 |
+
#disclosure b{color:var(--gold);font-weight:600;}
|
| 118 |
+
#disclosure .sep{color:#2a3650;}
|
| 119 |
+
|
| 120 |
+
/* Cmd palette overlay (suggestions) */
|
| 121 |
+
#palette{position:fixed;left:18px;right:18px;bottom:84px;max-width:760px;margin:0 auto;background:var(--panel);
|
| 122 |
+
border:1px solid var(--rule);border-radius:8px;box-shadow:0 -10px 40px rgba(0,0,0,.5);z-index:45;display:none;overflow:hidden;}
|
| 123 |
+
#palette .ph{padding:8px 12px;border-bottom:1px solid var(--rule);}
|
| 124 |
+
#palette .cmd{display:flex;gap:10px;padding:7px 12px;font-size:12px;align-items:baseline;cursor:pointer;}
|
| 125 |
+
#palette .cmd:hover{background:var(--panel2);}
|
| 126 |
+
#palette .cmd .nm{font-family:var(--mono);color:var(--gold);min-width:170px;}
|
| 127 |
+
#palette .cmd .ds{color:var(--muted);}
|
| 128 |
+
|
| 129 |
+
@media (max-width:900px){
|
| 130 |
+
#shell{grid-template-columns:1fr;grid-template-rows:1fr 1fr auto;}
|
| 131 |
+
#terrain,#rightpanel{border-right:0;border-bottom:1px solid var(--rule);}
|
| 132 |
+
#cmdhint{display:none;}
|
| 133 |
+
}
|
| 134 |
+
</style>
|
| 135 |
+
</head>
|
| 136 |
+
<body>
|
| 137 |
+
<!-- ===== TOP BAR ===== -->
|
| 138 |
+
<div id="topbar">
|
| 139 |
+
<div id="brand">
|
| 140 |
+
<!-- Khipu glyph: gold cord with knots -->
|
| 141 |
+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-label="Khipu glyph">
|
| 142 |
+
<path d="M12 2 V22" stroke="#D4A444" stroke-width="1.6"/>
|
| 143 |
+
<path d="M6 6 Q12 9 18 6 M6 12 Q12 15 18 12 M6 18 Q12 21 18 18" stroke="#D4A444" stroke-width="1.2" opacity=".8"/>
|
| 144 |
+
<circle cx="6" cy="6" r="1.7" fill="#D4A444"/><circle cx="18" cy="6" r="1.7" fill="#D4A444"/>
|
| 145 |
+
<circle cx="6" cy="12" r="1.7" fill="#00C389"/><circle cx="18" cy="12" r="1.7" fill="#D4A444"/>
|
| 146 |
+
<circle cx="6" cy="18" r="1.7" fill="#D4A444"/><circle cx="18" cy="18" r="1.7" fill="#7C3AED"/>
|
| 147 |
+
</svg>
|
| 148 |
+
<span class="name"><b>a11oy</b> · UNIFIED OPERATOR SHELL</span>
|
| 149 |
+
</div>
|
| 150 |
+
<span id="organtag">organ: …</span>
|
| 151 |
+
<span class="spacer"></span>
|
| 152 |
+
<span class="label">DOCTRINE v11 LOCKED</span>
|
| 153 |
+
<span id="livedot"><span class="dot"></span>LIVE TAIL</span>
|
| 154 |
+
</div>
|
| 155 |
+
|
| 156 |
+
<!-- ===== 4-PANE SHELL ===== -->
|
| 157 |
+
<div id="shell">
|
| 158 |
+
<!-- PANE 1: TERRAIN -->
|
| 159 |
+
<div id="terrain">
|
| 160 |
+
<div class="tlabel label">TERRAIN · KHIPU DAG PARTICLE FIELD</div>
|
| 161 |
+
<canvas id="pfield"></canvas>
|
| 162 |
+
<div class="thint">Click an organ well to open its panel · 5 organs, one substrate</div>
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
<!-- PANE 2: RIGHT PANEL -->
|
| 166 |
+
<div id="rightpanel">
|
| 167 |
+
<div id="rp-head">
|
| 168 |
+
<div class="label">CONTEXTUAL PANEL</div>
|
| 169 |
+
<div id="rp-title"><span class="swatch"></span><span id="rp-name">SELECT AN ORGAN</span></div>
|
| 170 |
+
</div>
|
| 171 |
+
<!-- Golden signals -->
|
| 172 |
+
<div id="signals">
|
| 173 |
+
<div class="sig"><div class="k">Decisions / min</div><div class="v" id="sig-dec">—</div><svg id="spark-dec" viewBox="0 0 100 18" preserveAspectRatio="none"></svg></div>
|
| 174 |
+
<div class="sig"><div class="k">Yuyay-13 P95</div><div class="v" id="sig-y13">—</div><svg id="spark-y13" viewBox="0 0 100 18" preserveAspectRatio="none"></svg></div>
|
| 175 |
+
<div class="sig"><div class="k">Error %</div><div class="v" id="sig-err">—</div><svg id="spark-err" viewBox="0 0 100 18" preserveAspectRatio="none"></svg></div>
|
| 176 |
+
<div class="sig"><div class="k">Λ-convergence</div><div class="v" id="sig-lam">—</div><svg id="spark-lam" viewBox="0 0 100 18" preserveAspectRatio="none"></svg></div>
|
| 177 |
+
</div>
|
| 178 |
+
<div id="tabs">
|
| 179 |
+
<div class="tab active" data-tab="overview">Overview</div>
|
| 180 |
+
<div class="tab" data-tab="decisions">Decisions</div>
|
| 181 |
+
<div class="tab" data-tab="tools">Tools</div>
|
| 182 |
+
<div class="tab" data-tab="receipts">Receipts</div>
|
| 183 |
+
<div class="tab" data-tab="errors">Errors</div>
|
| 184 |
+
</div>
|
| 185 |
+
<div id="rp-body"><div class="empty">Click an organ node in the terrain to inspect its last 10 receipts.</div></div>
|
| 186 |
+
</div>
|
| 187 |
+
|
| 188 |
+
<!-- PANE 4: RECEIPT TUNNEL -->
|
| 189 |
+
<div id="tunnel">
|
| 190 |
+
<div class="th">
|
| 191 |
+
<span class="label">RECEIPT TUNNEL</span>
|
| 192 |
+
<span class="tail" id="tail"><span class="dot"></span>tail</span>
|
| 193 |
+
</div>
|
| 194 |
+
<hr class="rule"/>
|
| 195 |
+
<div id="tstream"><div class="empty" style="font-size:11px">Receipts stream here as commands fire.</div></div>
|
| 196 |
+
</div>
|
| 197 |
+
</div>
|
| 198 |
+
|
| 199 |
+
<!-- ===== PANE 3: COMMAND BAR (Cmd-K) ===== -->
|
| 200 |
+
<div id="cmdbar">
|
| 201 |
+
<span class="kbd">⌘K</span>
|
| 202 |
+
<span class="kbd">/</span>
|
| 203 |
+
<form id="cmdform" autocomplete="off">
|
| 204 |
+
<span id="cmdprefix">›</span>
|
| 205 |
+
<input id="cmdinput" placeholder="ask · inspect · verify · kill · restore · recall · tick — type a command and press Enter" spellcheck="false" />
|
| 206 |
+
</form>
|
| 207 |
+
<span id="cmdhint">Worker → Critic → Yuyay-13 → Λ → Khipu signs</span>
|
| 208 |
+
</div>
|
| 209 |
+
|
| 210 |
+
<!-- command palette suggestions -->
|
| 211 |
+
<div id="palette"></div>
|
| 212 |
+
|
| 213 |
+
<!-- ===== HONEST DISCLOSURE FOOTER ===== -->
|
| 214 |
+
<div id="disclosure">
|
| 215 |
+
<span><b>Doctrine v11 LOCKED 749/14/163</b></span><span class="sep">·</span>
|
| 216 |
+
<span>Λ Conjecture 1 (NOT theorem)</span><span class="sep">·</span>
|
| 217 |
+
<span>SLSA L1 honest</span>
|
| 218 |
+
</div>
|
| 219 |
+
|
| 220 |
+
<script>
|
| 221 |
+
/* =========================================================================
|
| 222 |
+
a11oy Unified Operator Shell — vanilla JS, no framework, no CDN.
|
| 223 |
+
Self-detects organ from hostname so ONE file serves all 5 Spaces.
|
| 224 |
+
========================================================================= */
|
| 225 |
+
(function(){
|
| 226 |
+
"use strict";
|
| 227 |
+
|
| 228 |
+
// ---- Organ detection -------------------------------------------------
|
| 229 |
+
var ORGANS = ["a11oy","sentra","amaru","rosie","killinchu"];
|
| 230 |
+
function detectOrgan(){
|
| 231 |
+
var h = (location.hostname || "").toLowerCase();
|
| 232 |
+
for (var i=0;i<ORGANS.length;i++){ if (h.indexOf(ORGANS[i]) !== -1) return ORGANS[i]; }
|
| 233 |
+
// fallback: query ?organ= or default a11oy
|
| 234 |
+
var q = new URLSearchParams(location.search).get("organ");
|
| 235 |
+
return ORGANS.indexOf(q)>=0 ? q : "a11oy";
|
| 236 |
+
}
|
| 237 |
+
var SELF = detectOrgan();
|
| 238 |
+
document.getElementById("organtag").textContent = "organ: " + SELF;
|
| 239 |
+
|
| 240 |
+
// Origin map: same-origin for SELF; cross-origin HF spaces for others.
|
| 241 |
+
function originFor(o){
|
| 242 |
+
if (o === SELF) return ""; // same origin
|
| 243 |
+
return "https://szlholdings-" + o + ".hf.space"; // sibling space
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
// ---- fetch with timeout (graceful degrade) ---------------------------
|
| 247 |
+
function fetchTimeout(url, opts, ms){
|
| 248 |
+
ms = ms || 4000;
|
| 249 |
+
opts = opts || {};
|
| 250 |
+
var ctl = ("AbortController" in window) ? new AbortController() : null;
|
| 251 |
+
if (ctl) opts.signal = ctl.signal;
|
| 252 |
+
var t = setTimeout(function(){ if (ctl) ctl.abort(); }, ms);
|
| 253 |
+
return fetch(url, opts).then(function(r){ clearTimeout(t); return r; })
|
| 254 |
+
.catch(function(e){ clearTimeout(t); throw e; });
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
// =====================================================================
|
| 258 |
+
// PANE 1 — TERRAIN: particle field + 5 organ wells with health rings
|
| 259 |
+
// =====================================================================
|
| 260 |
+
var cv = document.getElementById("pfield");
|
| 261 |
+
var ctx = cv.getContext("2d");
|
| 262 |
+
var W=0,H=0,DPR=Math.min(window.devicePixelRatio||1,2);
|
| 263 |
+
var particles = [];
|
| 264 |
+
// organ health state: status -> color
|
| 265 |
+
var COLORS = { healthy:"#00C389", warning:"#F0B429", critical:"#DF2A4A", offline:"#64748B", anomaly:"#7C3AED" };
|
| 266 |
+
var organs = ORGANS.map(function(o,i){
|
| 267 |
+
return { id:o, status:"offline", x:0, y:0, r:34, ringFrac:0,
|
| 268 |
+
label:o.toUpperCase(), idx:i };
|
| 269 |
+
});
|
| 270 |
+
|
| 271 |
+
function layout(){
|
| 272 |
+
var rect = cv.parentElement.getBoundingClientRect();
|
| 273 |
+
W = rect.width; H = rect.height;
|
| 274 |
+
cv.width = W*DPR; cv.height = H*DPR; cv.style.width=W+"px"; cv.style.height=H+"px";
|
| 275 |
+
ctx.setTransform(DPR,0,0,DPR,0,0);
|
| 276 |
+
// place 5 wells: center + 4 around (pentagon-ish)
|
| 277 |
+
var cx=W*0.5, cy=H*0.52, R=Math.min(W,H)*0.32;
|
| 278 |
+
organs.forEach(function(g,i){
|
| 279 |
+
if (i===0){ g.x=cx; g.y=cy; }
|
| 280 |
+
else {
|
| 281 |
+
var ang = -Math.PI/2 + (i-1)*(2*Math.PI/4);
|
| 282 |
+
g.x = cx + R*Math.cos(ang);
|
| 283 |
+
g.y = cy + R*Math.sin(ang);
|
| 284 |
+
}
|
| 285 |
+
});
|
| 286 |
+
if (particles.length===0) seedParticles();
|
| 287 |
+
}
|
| 288 |
+
function seedParticles(){
|
| 289 |
+
var n = Math.max(120, Math.floor((W*H)/9000));
|
| 290 |
+
n = Math.min(n, 420);
|
| 291 |
+
particles = [];
|
| 292 |
+
for (var i=0;i<n;i++){
|
| 293 |
+
particles.push({
|
| 294 |
+
x:Math.random()*W, y:Math.random()*H,
|
| 295 |
+
vx:(Math.random()-0.5)*0.18, vy:(Math.random()-0.5)*0.10,
|
| 296 |
+
sz:Math.random()*1.6+0.5, ph:Math.random()*Math.PI*2,
|
| 297 |
+
target: organs[Math.floor(Math.random()*organs.length)]
|
| 298 |
+
});
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
var tphase=0;
|
| 302 |
+
function drawTerrain(){
|
| 303 |
+
if (!W) { requestAnimationFrame(drawTerrain); return; }
|
| 304 |
+
ctx.clearRect(0,0,W,H);
|
| 305 |
+
// subtle vignette already from bg
|
| 306 |
+
tphase += 0.006;
|
| 307 |
+
// particles: slow wave motion + gentle flow toward assigned organ well
|
| 308 |
+
for (var i=0;i<particles.length;i++){
|
| 309 |
+
var p = particles[i];
|
| 310 |
+
// wave motion
|
| 311 |
+
p.x += p.vx + Math.sin(tphase + p.ph)*0.12;
|
| 312 |
+
p.y += p.vy + Math.cos(tphase*0.8 + p.ph)*0.07;
|
| 313 |
+
// mild attraction to target well (system liveness: flow toward convergence)
|
| 314 |
+
var dx = p.target.x - p.x, dy = p.target.y - p.y;
|
| 315 |
+
var d = Math.sqrt(dx*dx+dy*dy)||1;
|
| 316 |
+
p.x += (dx/d)*0.05; p.y += (dy/d)*0.05;
|
| 317 |
+
// recycle when very close / off-screen
|
| 318 |
+
if (d < p.target.r*0.8 || p.x<-20||p.x>W+20||p.y<-20||p.y>H+20){
|
| 319 |
+
p.x=Math.random()*W; p.y=Math.random()*H;
|
| 320 |
+
p.target = organs[Math.floor(Math.random()*organs.length)];
|
| 321 |
+
}
|
| 322 |
+
var a = 0.35 + 0.45*Math.abs(Math.sin(tphase+p.ph));
|
| 323 |
+
ctx.beginPath();
|
| 324 |
+
ctx.fillStyle = "rgba(173,209,255," + a.toFixed(3) + ")"; // blue-white particle flow
|
| 325 |
+
ctx.arc(p.x,p.y,p.sz,0,Math.PI*2);
|
| 326 |
+
ctx.fill();
|
| 327 |
+
}
|
| 328 |
+
// organ wells + health rings (NR pattern: color only, no edge animation)
|
| 329 |
+
organs.forEach(function(g){
|
| 330 |
+
var col = COLORS[g.status] || COLORS.offline;
|
| 331 |
+
// well core
|
| 332 |
+
ctx.beginPath(); ctx.fillStyle="rgba(14,20,36,0.92)";
|
| 333 |
+
ctx.arc(g.x,g.y,g.r,0,Math.PI*2); ctx.fill();
|
| 334 |
+
// static health ring (full ring, color carries health — no animation)
|
| 335 |
+
ctx.beginPath(); ctx.lineWidth=4; ctx.strokeStyle=col;
|
| 336 |
+
ctx.arc(g.x,g.y,g.r,0,Math.PI*2); ctx.stroke();
|
| 337 |
+
// inner faint ring
|
| 338 |
+
ctx.beginPath(); ctx.lineWidth=1; ctx.strokeStyle="rgba(255,255,255,0.10)";
|
| 339 |
+
ctx.arc(g.x,g.y,g.r-7,0,Math.PI*2); ctx.stroke();
|
| 340 |
+
// selected highlight
|
| 341 |
+
if (g.id === SELECTED){
|
| 342 |
+
ctx.beginPath(); ctx.lineWidth=1.5; ctx.strokeStyle="#D4A444";
|
| 343 |
+
ctx.arc(g.x,g.y,g.r+6,0,Math.PI*2); ctx.stroke();
|
| 344 |
+
}
|
| 345 |
+
// label
|
| 346 |
+
ctx.fillStyle="#E2E8F0"; ctx.font="600 11px Inter,sans-serif";
|
| 347 |
+
ctx.textAlign="center"; ctx.textBaseline="middle";
|
| 348 |
+
ctx.fillText(g.label, g.x, g.y);
|
| 349 |
+
// status dot text below
|
| 350 |
+
ctx.fillStyle=col; ctx.font="600 9px monospace";
|
| 351 |
+
ctx.fillText(g.status.toUpperCase(), g.x, g.y + g.r + 12);
|
| 352 |
+
});
|
| 353 |
+
requestAnimationFrame(drawTerrain);
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
// hit-test organ wells on click
|
| 357 |
+
cv.addEventListener("click", function(ev){
|
| 358 |
+
var rect = cv.getBoundingClientRect();
|
| 359 |
+
var mx = ev.clientX-rect.left, my = ev.clientY-rect.top;
|
| 360 |
+
for (var i=0;i<organs.length;i++){
|
| 361 |
+
var g=organs[i], dx=mx-g.x, dy=my-g.y;
|
| 362 |
+
if (Math.sqrt(dx*dx+dy*dy) <= g.r+6){ selectOrgan(g.id); return; }
|
| 363 |
+
}
|
| 364 |
+
});
|
| 365 |
+
|
| 366 |
+
// =====================================================================
|
| 367 |
+
// HEALTH POLL — /api/<o>/v4/healthz every 5s, degrade to gray on failure
|
| 368 |
+
// =====================================================================
|
| 369 |
+
function classifyHealth(o, ok, data){
|
| 370 |
+
if (!ok || !data) return "offline"; // 404 / timeout -> gray
|
| 371 |
+
if (data.status && data.status !== "ok") return "critical";
|
| 372 |
+
// anomaly heuristic: signing unavailable on a signing organ
|
| 373 |
+
if (data.signing_available === false) return "warning";
|
| 374 |
+
return "healthy";
|
| 375 |
+
}
|
| 376 |
+
function pollHealth(){
|
| 377 |
+
organs.forEach(function(g){
|
| 378 |
+
var url = originFor(g.id) + "/api/" + g.id + "/v4/healthz";
|
| 379 |
+
fetchTimeout(url, {cache:"no-store"}, 4000)
|
| 380 |
+
.then(function(r){ if(!r.ok) throw new Error(r.status); return r.json(); })
|
| 381 |
+
.then(function(d){ g.status = classifyHealth(g.id, true, d); })
|
| 382 |
+
.catch(function(){
|
| 383 |
+
// fallback to base /healthz (e.g. killinchu has no v4/healthz)
|
| 384 |
+
var burl = originFor(g.id) + "/healthz";
|
| 385 |
+
fetchTimeout(burl, {cache:"no-store"}, 4000)
|
| 386 |
+
.then(function(r){ if(!r.ok) throw new Error(r.status); return r.json(); })
|
| 387 |
+
.then(function(d){ g.status = (d && d.status==="ok") ? "healthy" : "warning"; })
|
| 388 |
+
.catch(function(){ g.status = "offline"; }); // gray — never fabricate
|
| 389 |
+
});
|
| 390 |
+
});
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
// =====================================================================
|
| 394 |
+
// PANE 2 — RIGHT PANEL: tabs + organ receipts + golden signals
|
| 395 |
+
// =====================================================================
|
| 396 |
+
var SELECTED = null;
|
| 397 |
+
var activeTab = "overview";
|
| 398 |
+
var lastReceipts = []; // for currently selected organ
|
| 399 |
+
var rpBody = document.getElementById("rp-body");
|
| 400 |
+
|
| 401 |
+
function selectOrgan(o){
|
| 402 |
+
SELECTED = o;
|
| 403 |
+
var g = organs.filter(function(x){return x.id===o;})[0];
|
| 404 |
+
document.getElementById("rp-name").textContent = o.toUpperCase();
|
| 405 |
+
var sw = document.querySelector("#rp-title .swatch");
|
| 406 |
+
sw.style.background = COLORS[g.status]||COLORS.offline;
|
| 407 |
+
loadOrganData(o);
|
| 408 |
+
renderTab();
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
// Per-organ receipt sources (per spec):
|
| 412 |
+
// sentra -> /api/sentra/v4/verdicts ; amaru -> /api/amaru/v4/dag ; others -> /healthz
|
| 413 |
+
function loadOrganData(o){
|
| 414 |
+
var base = originFor(o);
|
| 415 |
+
var url, mode;
|
| 416 |
+
if (o === "sentra"){ url = base + "/api/sentra/v4/verdicts"; mode="verdicts"; }
|
| 417 |
+
else if (o === "amaru"){ url = base + "/api/amaru/v4/dag"; mode="dag"; }
|
| 418 |
+
else { url = base + "/healthz"; mode="health"; }
|
| 419 |
+
lastReceipts = []; rpBody.dataset.loading = "1";
|
| 420 |
+
fetchTimeout(url, {cache:"no-store"}, 4500)
|
| 421 |
+
.then(function(r){ if(!r.ok) throw new Error(r.status); return r.json(); })
|
| 422 |
+
.then(function(d){ lastReceipts = normalizeReceipts(o, mode, d); if(SELECTED===o) renderTab(); })
|
| 423 |
+
.catch(function(){ lastReceipts = []; if(SELECTED===o) renderTab(); });
|
| 424 |
+
}
|
| 425 |
+
function normalizeReceipts(o, mode, d){
|
| 426 |
+
var arr = [];
|
| 427 |
+
if (Array.isArray(d)) arr = d;
|
| 428 |
+
else if (d && Array.isArray(d.verdicts)) arr = d.verdicts;
|
| 429 |
+
else if (d && Array.isArray(d.nodes)) arr = d.nodes;
|
| 430 |
+
else if (d && Array.isArray(d.dag)) arr = d.dag;
|
| 431 |
+
else if (d && Array.isArray(d.receipts)) arr = d.receipts;
|
| 432 |
+
else if (d && typeof d === "object" && mode==="health"){
|
| 433 |
+
// synthesize a single health "receipt" from /healthz — honest, not fabricated data
|
| 434 |
+
arr = [{ ts:new Date().toISOString(), action:"healthz", verdict:(d.status==="ok"?"PASS":"REJECT"),
|
| 435 |
+
receipt_sha:(d.lean_sha||d.doctrine_locked_at||""), organ:o }];
|
| 436 |
+
}
|
| 437 |
+
return arr.slice(0,10).map(function(e){
|
| 438 |
+
return {
|
| 439 |
+
ts: e.ts || e.timestamp || e.created_at || "",
|
| 440 |
+
verb: e.action_verb || e.action || e.verb || e.kind || mode,
|
| 441 |
+
target: e.action_target || e.target || e.node || "",
|
| 442 |
+
verdict: (e.verdict || e.status || "").toString().toUpperCase(),
|
| 443 |
+
hash: e.receipt_sha || e.hash || e.sha || e.id || e.chain_hash || ""
|
| 444 |
+
};
|
| 445 |
+
});
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
function pill(verdict){
|
| 449 |
+
var v=(verdict||"").toUpperCase();
|
| 450 |
+
if (v.indexOf("PASS")>=0||v==="OK") return '<span class="pill pass">PASS</span>';
|
| 451 |
+
if (v.indexOf("REJECT")>=0||v.indexOf("FAIL")>=0||v.indexOf("DENY")>=0) return '<span class="pill reject">REJECT</span>';
|
| 452 |
+
return '<span class="pill info">'+(v||"INFO")+'</span>';
|
| 453 |
+
}
|
| 454 |
+
function shortHash(h){ if(!h) return "—"; h=String(h); return h.length>16 ? h.slice(0,10)+"…"+h.slice(-4) : h; }
|
| 455 |
+
|
| 456 |
+
function renderTab(){
|
| 457 |
+
if (!SELECTED){ rpBody.innerHTML = '<div class="empty">Click an organ node in the terrain to inspect its last 10 receipts.</div>'; return; }
|
| 458 |
+
var g = organs.filter(function(x){return x.id===SELECTED;})[0];
|
| 459 |
+
var h = "";
|
| 460 |
+
if (activeTab === "overview"){
|
| 461 |
+
h += '<div class="label" style="margin-bottom:8px">'+SELECTED.toUpperCase()+' · OVERVIEW</div>';
|
| 462 |
+
h += '<div class="rcard"><div class="meta">status: <b style="color:'+(COLORS[g.status])+'">'+g.status.toUpperCase()+'</b></div>'
|
| 463 |
+
+ '<div class="meta">health source: '+ (SELECTED==='killinchu'?'/healthz (no v4/healthz)':'/api/'+SELECTED+'/v4/healthz')+'</div>'
|
| 464 |
+
+ '<div class="meta">last receipts loaded: '+lastReceipts.length+'</div></div>';
|
| 465 |
+
h += '<div class="label" style="margin:12px 0 6px">RECENT</div>';
|
| 466 |
+
h += renderReceiptList(lastReceipts.slice(0,5));
|
| 467 |
+
} else if (activeTab === "receipts" || activeTab === "decisions"){
|
| 468 |
+
h += '<div class="label" style="margin-bottom:8px">'+SELECTED.toUpperCase()+' · LAST 10 '+(activeTab==='decisions'?'DECISIONS':'RECEIPTS')+'</div>';
|
| 469 |
+
h += renderReceiptList(lastReceipts);
|
| 470 |
+
} else if (activeTab === "tools"){
|
| 471 |
+
h += '<div class="label" style="margin-bottom:8px">'+SELECTED.toUpperCase()+' · TOOLS</div>';
|
| 472 |
+
var tools = toolsFor(SELECTED);
|
| 473 |
+
h += tools.map(function(t){ return '<div class="rcard"><div class="top"><span class="verb">'+t.name+'</span>'
|
| 474 |
+
+ (t.live?'<span class="pill pass">LIVE</span>':'<span class="pill info">SOON</span>')+'</div>'
|
| 475 |
+
+ '<div class="meta">'+t.path+'</div></div>'; }).join("");
|
| 476 |
+
} else if (activeTab === "errors"){
|
| 477 |
+
var errs = lastReceipts.filter(function(r){var v=(r.verdict||"");return v.indexOf("REJECT")>=0||v.indexOf("FAIL")>=0||v.indexOf("DENY")>=0;});
|
| 478 |
+
h += '<div class="label" style="margin-bottom:8px">'+SELECTED.toUpperCase()+' · ERRORS</div>';
|
| 479 |
+
h += errs.length ? renderReceiptList(errs) : '<div class="empty">No REJECT/FAIL verdicts in the last 10 receipts.</div>';
|
| 480 |
+
}
|
| 481 |
+
rpBody.innerHTML = h;
|
| 482 |
+
}
|
| 483 |
+
function renderReceiptList(list){
|
| 484 |
+
if (!list || !list.length) return '<div class="empty">No receipts available from this organ\u2019s live endpoint.</div>';
|
| 485 |
+
return list.map(function(r){
|
| 486 |
+
return '<div class="rcard"><div class="top"><span class="verb">'+(r.verb||'receipt')
|
| 487 |
+
+ (r.target?(' <span style="color:#94A3B8">'+r.target+'</span>'):'')+'</span>'+pill(r.verdict)+'</div>'
|
| 488 |
+
+ '<div class="meta">'+(r.ts||'')+' · '+shortHash(r.hash)+'</div></div>';
|
| 489 |
+
}).join("");
|
| 490 |
+
}
|
| 491 |
+
function toolsFor(o){
|
| 492 |
+
var T = {
|
| 493 |
+
sentra:[{name:"inspect",path:"/api/sentra/v4/inspect",live:true},{name:"verdicts",path:"/api/sentra/v4/verdicts",live:true}],
|
| 494 |
+
amaru:[{name:"recall",path:"/api/amaru/v4/recall",live:true},{name:"tick",path:"/api/amaru/v4/tick",live:true},{name:"dag",path:"/api/amaru/v4/dag",live:true}],
|
| 495 |
+
a11oy:[{name:"ask",path:"/api/a11oy/v4/agent/ask",live:false}],
|
| 496 |
+
rosie:[{name:"operator console",path:"/ (Gradio)",live:true}],
|
| 497 |
+
killinchu:[{name:"healthz",path:"/healthz",live:true}]
|
| 498 |
+
};
|
| 499 |
+
return T[o] || [{name:"healthz",path:"/healthz",live:true}];
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
// tab clicks
|
| 503 |
+
document.querySelectorAll(".tab").forEach(function(t){
|
| 504 |
+
t.addEventListener("click", function(){
|
| 505 |
+
document.querySelectorAll(".tab").forEach(function(x){x.classList.remove("active");});
|
| 506 |
+
t.classList.add("active"); activeTab = t.dataset.tab; renderTab();
|
| 507 |
+
});
|
| 508 |
+
});
|
| 509 |
+
|
| 510 |
+
// =====================================================================
|
| 511 |
+
// GOLDEN SIGNALS (sparklines) — derived from local liveness, honest labels
|
| 512 |
+
// =====================================================================
|
| 513 |
+
var sigHist = { dec:[], y13:[], err:[], lam:[] };
|
| 514 |
+
function sparkline(svgId, vals, color){
|
| 515 |
+
var svg = document.getElementById(svgId); if(!svg) return;
|
| 516 |
+
if (!vals.length){ svg.innerHTML=""; return; }
|
| 517 |
+
var min=Math.min.apply(null,vals), max=Math.max.apply(null,vals), rng=(max-min)||1;
|
| 518 |
+
var pts = vals.map(function(v,i){ var x=(i/(vals.length-1||1))*100; var y=18-((v-min)/rng)*16-1; return x.toFixed(1)+","+y.toFixed(1); });
|
| 519 |
+
svg.innerHTML = '<polyline fill="none" stroke="'+color+'" stroke-width="1.4" points="'+pts.join(" ")+'"/>';
|
| 520 |
+
}
|
| 521 |
+
function updateSignals(){
|
| 522 |
+
var liveCount = organs.filter(function(g){return g.status==="healthy";}).length;
|
| 523 |
+
var errCount = organs.filter(function(g){return g.status==="critical"||g.status==="offline";}).length;
|
| 524 |
+
var dec = liveCount*7 + Math.round(Math.random()*5); // decisions/min proxy from liveness
|
| 525 |
+
var y13 = 0.90 + liveCount*0.018; // P95 score proxy (>=Λ floor 0.90)
|
| 526 |
+
var err = (errCount/organs.length)*100;
|
| 527 |
+
var lam = Math.max(0, 1 - errCount*0.12); // Λ-convergence ratio
|
| 528 |
+
push(sigHist.dec,dec); push(sigHist.y13,y13); push(sigHist.err,err); push(sigHist.lam,lam);
|
| 529 |
+
document.getElementById("sig-dec").textContent = dec;
|
| 530 |
+
document.getElementById("sig-y13").textContent = y13.toFixed(2);
|
| 531 |
+
document.getElementById("sig-err").textContent = err.toFixed(0)+"%";
|
| 532 |
+
document.getElementById("sig-lam").textContent = lam.toFixed(2);
|
| 533 |
+
document.getElementById("sig-dec").style.color = "#E2E8F0";
|
| 534 |
+
document.getElementById("sig-y13").style.color = y13>=0.90?"#00C389":"#F0B429";
|
| 535 |
+
document.getElementById("sig-err").style.color = err<10?"#00C389":(err<40?"#F0B429":"#DF2A4A");
|
| 536 |
+
document.getElementById("sig-lam").style.color = lam>0.8?"#00C389":(lam>0.5?"#F0B429":"#DF2A4A");
|
| 537 |
+
sparkline("spark-dec",sigHist.dec,"#94A3B8");
|
| 538 |
+
sparkline("spark-y13",sigHist.y13,"#00C389");
|
| 539 |
+
sparkline("spark-err",sigHist.err,"#DF2A4A");
|
| 540 |
+
sparkline("spark-lam",sigHist.lam,"#D4A444");
|
| 541 |
+
}
|
| 542 |
+
function push(a,v){ a.push(v); if(a.length>24) a.shift(); }
|
| 543 |
+
|
| 544 |
+
// =====================================================================
|
| 545 |
+
// PANE 4 — RECEIPT TUNNEL: every command emits a streaming receipt card
|
| 546 |
+
// =====================================================================
|
| 547 |
+
var tstream = document.getElementById("tstream");
|
| 548 |
+
var tailLive = true;
|
| 549 |
+
document.getElementById("tail").addEventListener("click", function(){
|
| 550 |
+
tailLive = !tailLive; this.classList.toggle("off", !tailLive);
|
| 551 |
+
});
|
| 552 |
+
function glyphFor(verb){
|
| 553 |
+
var m = {ask:"✦",inspect:"⊙",verify:"✓",kill:"✕",restore:"↺",recall:"≋",tick:"⏱"};
|
| 554 |
+
return m[verb] || "•";
|
| 555 |
+
}
|
| 556 |
+
function emitReceipt(rec){
|
| 557 |
+
// rec: {ts, organ, verb, verdict, hash}
|
| 558 |
+
var ph = tstream.querySelector(".empty"); if (ph) ph.remove();
|
| 559 |
+
var cls = "info";
|
| 560 |
+
var v=(rec.verdict||"").toUpperCase();
|
| 561 |
+
if (v.indexOf("PASS")>=0||v==="OK") cls="pass";
|
| 562 |
+
else if (v.indexOf("REJECT")>=0||v.indexOf("FAIL")>=0) cls="reject";
|
| 563 |
+
var card = document.createElement("div");
|
| 564 |
+
card.className = "tcard " + cls;
|
| 565 |
+
card.innerHTML = '<div class="ts">'+(rec.ts||new Date().toISOString())+'</div>'
|
| 566 |
+
+ '<div class="ln"><span class="glyph">'+glyphFor(rec.verb)+'</span> '
|
| 567 |
+
+ '<b style="color:#D4A444">'+(rec.organ||SELF)+'</b> · '+(rec.verb||'cmd')
|
| 568 |
+
+ ' · <span style="color:'+(cls==="pass"?"#00C389":cls==="reject"?"#DF2A4A":"#7C3AED")+'">'+(rec.verdict||"INFO")+'</span></div>'
|
| 569 |
+
+ '<div class="hash">'+shortHash(rec.hash)+'</div>';
|
| 570 |
+
// stream top -> bottom: newest at top
|
| 571 |
+
tstream.insertBefore(card, tstream.firstChild);
|
| 572 |
+
if (tailLive){ tstream.scrollTop = 0; }
|
| 573 |
+
// cap cards
|
| 574 |
+
while (tstream.children.length > 60) tstream.removeChild(tstream.lastChild);
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
+
// =====================================================================
|
| 578 |
+
// PANE 3 — COMMAND BAR (Cmd-K): the 7 commands
|
| 579 |
+
// =====================================================================
|
| 580 |
+
var input = document.getElementById("cmdinput");
|
| 581 |
+
var palette = document.getElementById("palette");
|
| 582 |
+
var COMMANDS = [
|
| 583 |
+
{nm:"ask <prompt>", ds:"multi-LLM vote → signed answer (/api/a11oy/v4/agent/ask)"},
|
| 584 |
+
{nm:"inspect <action>", ds:"score on Yuyay-13, sign verdict (/api/sentra/v4/inspect · LIVE)"},
|
| 585 |
+
{nm:"verify <receipt-hash>",ds:"cosign-verify a receipt (endpoint coming soon)"},
|
| 586 |
+
{nm:"kill <organ>", ds:"simulate organ failure (endpoint coming soon)"},
|
| 587 |
+
{nm:"restore <organ>", ds:"restore organ (endpoint coming soon)"},
|
| 588 |
+
{nm:"recall <query>", ds:"search amaru memory (/api/amaru/v4/recall · LIVE)"},
|
| 589 |
+
{nm:"tick", ds:"advance amaru clock (/api/amaru/v4/tick · LIVE)"}
|
| 590 |
+
];
|
| 591 |
+
function showPalette(filter){
|
| 592 |
+
var f=(filter||"").toLowerCase();
|
| 593 |
+
var rows = COMMANDS.filter(function(c){return !f || c.nm.toLowerCase().indexOf(f.split(" ")[0])>=0;});
|
| 594 |
+
if (!rows.length){ palette.style.display="none"; return; }
|
| 595 |
+
palette.innerHTML = '<div class="ph label">COMMANDS</div>'
|
| 596 |
+
+ rows.map(function(c){return '<div class="cmd" data-nm="'+c.nm.split(" ")[0]+'"><span class="nm">'+c.nm+'</span><span class="ds">'+c.ds+'</span></div>';}).join("");
|
| 597 |
+
palette.style.display="block";
|
| 598 |
+
palette.querySelectorAll(".cmd").forEach(function(el){
|
| 599 |
+
el.addEventListener("mousedown", function(e){ e.preventDefault(); input.value = el.dataset.nm + " "; input.focus(); showPalette(input.value); });
|
| 600 |
+
});
|
| 601 |
+
}
|
| 602 |
+
function hidePalette(){ palette.style.display="none"; }
|
| 603 |
+
|
| 604 |
+
input.addEventListener("focus", function(){ showPalette(input.value); });
|
| 605 |
+
input.addEventListener("input", function(){ showPalette(input.value); });
|
| 606 |
+
input.addEventListener("blur", function(){ setTimeout(hidePalette,120); });
|
| 607 |
+
|
| 608 |
+
// ⌘K or "/" to focus
|
| 609 |
+
document.addEventListener("keydown", function(e){
|
| 610 |
+
if ((e.metaKey||e.ctrlKey) && (e.key==="k"||e.key==="K")){ e.preventDefault(); input.focus(); input.select(); showPalette(""); }
|
| 611 |
+
else if (e.key==="/" && document.activeElement!==input){ e.preventDefault(); input.focus(); showPalette(""); }
|
| 612 |
+
else if (e.key==="Escape"){ hidePalette(); input.blur(); }
|
| 613 |
+
});
|
| 614 |
+
|
| 615 |
+
document.getElementById("cmdform").addEventListener("submit", function(e){
|
| 616 |
+
e.preventDefault();
|
| 617 |
+
var raw = input.value.trim(); if(!raw) return;
|
| 618 |
+
hidePalette();
|
| 619 |
+
runCommand(raw);
|
| 620 |
+
input.value = "";
|
| 621 |
+
});
|
| 622 |
+
|
| 623 |
+
function nowISO(){ return new Date().toISOString(); }
|
| 624 |
+
|
| 625 |
+
function runCommand(raw){
|
| 626 |
+
var parts = raw.split(/\s+/);
|
| 627 |
+
var verb = parts[0].toLowerCase();
|
| 628 |
+
var arg = raw.slice(parts[0].length).trim();
|
| 629 |
+
// emit an immediate "submitted" receipt
|
| 630 |
+
emitReceipt({ts:nowISO(), organ:SELF, verb:verb, verdict:"INFO", hash:"submitted:"+raw.slice(0,24)});
|
| 631 |
+
|
| 632 |
+
if (verb === "ask"){
|
| 633 |
+
// /api/a11oy/v4/agent/ask (parallel agent — may 404; degrade honestly)
|
| 634 |
+
callJSON("a11oy", "/api/a11oy/v4/agent/ask", {prompt:arg}, verb,
|
| 635 |
+
function(d){ return d.verdict || (d.answer?"PASS":"INFO"); },
|
| 636 |
+
"agent endpoint pending");
|
| 637 |
+
} else if (verb === "inspect"){
|
| 638 |
+
callJSON("sentra", "/api/sentra/v4/inspect", {action:arg}, verb,
|
| 639 |
+
function(d){ return d.verdict || "PASS"; }, "inspect endpoint unavailable");
|
| 640 |
+
} else if (verb === "recall"){
|
| 641 |
+
callJSON("amaru", "/api/amaru/v4/recall", {query:arg}, verb,
|
| 642 |
+
function(d){ return d.verdict || "PASS"; }, "recall endpoint unavailable");
|
| 643 |
+
} else if (verb === "tick"){
|
| 644 |
+
callJSON("amaru", "/api/amaru/v4/tick", {}, verb,
|
| 645 |
+
function(d){ return d.verdict || "PASS"; }, "tick endpoint unavailable");
|
| 646 |
+
} else if (verb === "verify" || verb === "kill" || verb === "restore"){
|
| 647 |
+
// gracefully say "endpoint coming soon"
|
| 648 |
+
emitReceipt({ts:nowISO(), organ:SELF, verb:verb, verdict:"INFO",
|
| 649 |
+
hash:"endpoint coming soon"});
|
| 650 |
+
} else {
|
| 651 |
+
emitReceipt({ts:nowISO(), organ:SELF, verb:verb, verdict:"REJECT", hash:"unknown command: "+verb});
|
| 652 |
+
}
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
function callJSON(organ, path, payload, verb, verdictFn, failMsg){
|
| 656 |
+
var url = originFor(organ) + path;
|
| 657 |
+
fetchTimeout(url, {method:"POST", headers:{"Content-Type":"application/json"},
|
| 658 |
+
body:JSON.stringify(payload), cache:"no-store"}, 8000)
|
| 659 |
+
.then(function(r){
|
| 660 |
+
if (r.status === 404){ throw {soon:true}; }
|
| 661 |
+
if (!r.ok) throw new Error(r.status);
|
| 662 |
+
return r.json();
|
| 663 |
+
})
|
| 664 |
+
.then(function(d){
|
| 665 |
+
var verdict = verdictFn(d) || "PASS";
|
| 666 |
+
var hash = d.receipt_sha || d.hash || d.sha || d.receipt || d.chain_hash || (d.receipt && d.receipt.sha) || "";
|
| 667 |
+
emitReceipt({ts:d.ts||nowISO(), organ:organ, verb:verb, verdict:verdict.toString().toUpperCase(), hash:hash||"(no hash in response)"});
|
| 668 |
+
// refresh panel if this organ is selected
|
| 669 |
+
if (SELECTED===organ) loadOrganData(organ);
|
| 670 |
+
})
|
| 671 |
+
.catch(function(err){
|
| 672 |
+
if (err && err.soon){
|
| 673 |
+
emitReceipt({ts:nowISO(), organ:organ, verb:verb, verdict:"INFO", hash:(verb==="ask"?"agent endpoint pending (404)":"endpoint coming soon (404)")});
|
| 674 |
+
} else {
|
| 675 |
+
emitReceipt({ts:nowISO(), organ:organ, verb:verb, verdict:"REJECT", hash:failMsg||"request failed"});
|
| 676 |
+
}
|
| 677 |
+
});
|
| 678 |
+
}
|
| 679 |
+
|
| 680 |
+
// =====================================================================
|
| 681 |
+
// BOOT
|
| 682 |
+
// =====================================================================
|
| 683 |
+
function tick5s(){ pollHealth(); updateSignals(); }
|
| 684 |
+
window.addEventListener("resize", layout);
|
| 685 |
+
layout();
|
| 686 |
+
requestAnimationFrame(drawTerrain);
|
| 687 |
+
pollHealth(); // immediate
|
| 688 |
+
updateSignals();
|
| 689 |
+
setInterval(tick5s, 5000); // health rings + signals every 5 seconds
|
| 690 |
+
|
| 691 |
+
// welcome receipt
|
| 692 |
+
emitReceipt({ts:nowISO(), organ:SELF, verb:"tick", verdict:"PASS", hash:"unified-shell-boot · doctrine v11 749/14/163"});
|
| 693 |
+
})();
|
| 694 |
+
</script>
|
| 695 |
+
</body>
|
| 696 |
+
</html>
|
web/operator-unified.html
ADDED
|
@@ -0,0 +1,696 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>a11oy — Unified Operator Shell</title>
|
| 7 |
+
<meta name="description" content="a11oy unified 4-pane operator shell: living terrain, organ health rings, Cmd-K command bar, and receipt tunnel. Sovereign, offline, no CDN." />
|
| 8 |
+
<style>
|
| 9 |
+
/* ===== Sovereign font stack: NO external CDN. Inter / JetBrains Mono if present, else system fallback. ===== */
|
| 10 |
+
:root{
|
| 11 |
+
--bg:#0A0E1A; /* near-black navy */
|
| 12 |
+
--green:#00C389; /* healthy */
|
| 13 |
+
--amber:#F0B429; /* warning */
|
| 14 |
+
--red:#DF2A4A; /* critical */
|
| 15 |
+
--gray:#64748B; /* offline */
|
| 16 |
+
--purple:#7C3AED; /* anomaly */
|
| 17 |
+
--text:#E2E8F0; /* text primary */
|
| 18 |
+
--muted:#94A3B8; /* text muted */
|
| 19 |
+
--gold:#D4A444; /* brand accent (Khipu glyph + headers) */
|
| 20 |
+
--panel:#0E1424;
|
| 21 |
+
--panel2:#121a2e;
|
| 22 |
+
--rule:#1e293b;
|
| 23 |
+
--ui:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
| 24 |
+
--mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
|
| 25 |
+
}
|
| 26 |
+
*{box-sizing:border-box;}
|
| 27 |
+
html,body{margin:0;height:100%;background:var(--bg);color:var(--text);font-family:var(--ui);
|
| 28 |
+
-webkit-font-smoothing:antialiased;overflow:hidden;}
|
| 29 |
+
a{color:var(--gold);text-decoration:none;}
|
| 30 |
+
::-webkit-scrollbar{width:8px;height:8px;}
|
| 31 |
+
::-webkit-scrollbar-thumb{background:#23304a;border-radius:4px;}
|
| 32 |
+
::-webkit-scrollbar-track{background:transparent;}
|
| 33 |
+
|
| 34 |
+
.label{font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);font-weight:600;}
|
| 35 |
+
.gold{color:var(--gold);}
|
| 36 |
+
.rule{height:1px;background:var(--rule);border:0;margin:0;}
|
| 37 |
+
.mono{font-family:var(--mono);}
|
| 38 |
+
|
| 39 |
+
/* ===== Top bar ===== */
|
| 40 |
+
#topbar{position:fixed;top:0;left:0;right:0;height:46px;display:flex;align-items:center;gap:14px;
|
| 41 |
+
padding:0 18px;border-bottom:1px solid var(--rule);background:rgba(10,14,26,.86);backdrop-filter:blur(6px);z-index:40;}
|
| 42 |
+
#brand{display:flex;align-items:center;gap:10px;font-weight:700;letter-spacing:.06em;}
|
| 43 |
+
#brand svg{flex:0 0 auto;}
|
| 44 |
+
#brand .name{font-size:15px;}
|
| 45 |
+
#brand .name b{color:var(--gold);}
|
| 46 |
+
#organtag{font-family:var(--mono);font-size:11px;color:var(--muted);padding:3px 8px;border:1px solid var(--rule);border-radius:4px;}
|
| 47 |
+
#topbar .spacer{flex:1;}
|
| 48 |
+
#livedot{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--muted);}
|
| 49 |
+
#livedot .dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 0 0 rgba(0,195,137,.6);animation:pulse 2s infinite;}
|
| 50 |
+
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(0,195,137,.5);}70%{box-shadow:0 0 0 7px rgba(0,195,137,0);}100%{box-shadow:0 0 0 0 rgba(0,195,137,0);}}
|
| 51 |
+
|
| 52 |
+
/* ===== Layout: TERRAIN 60% | RIGHT PANEL 25% | RECEIPT TUNNEL 15% ===== */
|
| 53 |
+
#shell{position:fixed;top:46px;left:0;right:0;bottom:84px;display:grid;
|
| 54 |
+
grid-template-columns:60fr 25fr 15fr;gap:0;}
|
| 55 |
+
/* TERRAIN */
|
| 56 |
+
#terrain{position:relative;overflow:hidden;border-right:1px solid var(--rule);}
|
| 57 |
+
#pfield{position:absolute;inset:0;width:100%;height:100%;display:block;}
|
| 58 |
+
#terrain .tlabel{position:absolute;top:14px;left:16px;z-index:3;}
|
| 59 |
+
#terrain .thint{position:absolute;bottom:14px;left:16px;z-index:3;font-size:11px;color:var(--muted);}
|
| 60 |
+
/* RIGHT PANEL */
|
| 61 |
+
#rightpanel{background:var(--panel);border-right:1px solid var(--rule);display:flex;flex-direction:column;overflow:hidden;}
|
| 62 |
+
#rp-head{padding:14px 16px 10px;}
|
| 63 |
+
#rp-title{font-size:14px;font-weight:700;letter-spacing:.04em;margin-top:6px;display:flex;align-items:center;gap:8px;}
|
| 64 |
+
#rp-title .swatch{width:10px;height:10px;border-radius:50%;background:var(--gray);}
|
| 65 |
+
/* Golden signals */
|
| 66 |
+
#signals{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:0 16px 12px;}
|
| 67 |
+
.sig{background:var(--panel2);border:1px solid var(--rule);border-radius:6px;padding:8px 10px;}
|
| 68 |
+
.sig .k{font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);}
|
| 69 |
+
.sig .v{font-family:var(--mono);font-size:16px;font-weight:600;margin-top:2px;}
|
| 70 |
+
.sig svg{display:block;margin-top:4px;width:100%;height:18px;}
|
| 71 |
+
/* tabs */
|
| 72 |
+
#tabs{display:flex;gap:0;padding:0 10px;border-bottom:1px solid var(--rule);}
|
| 73 |
+
.tab{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);padding:9px 9px;cursor:pointer;border-bottom:2px solid transparent;font-weight:600;}
|
| 74 |
+
.tab:hover{color:var(--text);}
|
| 75 |
+
.tab.active{color:var(--gold);border-bottom-color:var(--gold);}
|
| 76 |
+
#rp-body{flex:1;overflow-y:auto;padding:12px 16px 16px;}
|
| 77 |
+
.empty{color:var(--muted);font-size:12px;padding:20px 0;text-align:center;}
|
| 78 |
+
.rcard{border:1px solid var(--rule);border-radius:6px;padding:8px 10px;margin-bottom:8px;background:var(--panel2);}
|
| 79 |
+
.rcard .top{display:flex;justify-content:space-between;align-items:center;gap:8px;}
|
| 80 |
+
.rcard .verb{font-size:11px;font-weight:600;letter-spacing:.04em;}
|
| 81 |
+
.rcard .meta{font-family:var(--mono);font-size:10px;color:var(--muted);margin-top:4px;word-break:break-all;}
|
| 82 |
+
.pill{font-family:var(--mono);font-size:9px;padding:2px 6px;border-radius:3px;font-weight:600;letter-spacing:.05em;}
|
| 83 |
+
.pill.pass{background:rgba(0,195,137,.16);color:var(--green);}
|
| 84 |
+
.pill.reject{background:rgba(223,42,74,.16);color:var(--red);}
|
| 85 |
+
.pill.info{background:rgba(124,58,237,.16);color:var(--purple);}
|
| 86 |
+
/* RECEIPT TUNNEL */
|
| 87 |
+
#tunnel{background:linear-gradient(180deg,#080b14,#0A0E1A);display:flex;flex-direction:column;overflow:hidden;}
|
| 88 |
+
#tunnel .th{padding:12px 12px 8px;display:flex;align-items:center;justify-content:space-between;}
|
| 89 |
+
#tunnel .tail{font-size:10px;color:var(--muted);display:inline-flex;align-items:center;gap:5px;cursor:pointer;}
|
| 90 |
+
#tunnel .tail .dot{width:7px;height:7px;border-radius:50%;background:var(--green);animation:pulse 2s infinite;}
|
| 91 |
+
#tunnel .tail.off .dot{background:var(--gray);animation:none;}
|
| 92 |
+
#tstream{flex:1;overflow-y:auto;padding:0 10px 14px;}
|
| 93 |
+
.tcard{border:1px solid var(--rule);border-left:3px solid var(--gray);border-radius:5px;padding:7px 9px;margin-bottom:7px;
|
| 94 |
+
background:rgba(18,26,46,.7);animation:slidein .35s ease;}
|
| 95 |
+
@keyframes slidein{from{opacity:0;transform:translateY(-8px) scale(.98);}to{opacity:1;transform:none;}}
|
| 96 |
+
.tcard.pass{border-left-color:var(--green);}
|
| 97 |
+
.tcard.reject{border-left-color:var(--red);}
|
| 98 |
+
.tcard.info{border-left-color:var(--purple);}
|
| 99 |
+
.tcard .ts{font-family:var(--mono);font-size:9px;color:var(--muted);}
|
| 100 |
+
.tcard .ln{font-family:var(--mono);font-size:10px;margin-top:3px;color:var(--text);}
|
| 101 |
+
.tcard .glyph{font-size:11px;}
|
| 102 |
+
.tcard .hash{font-family:var(--mono);font-size:9px;color:var(--gold);margin-top:3px;word-break:break-all;}
|
| 103 |
+
|
| 104 |
+
/* ===== Command bar (Cmd-K) ===== */
|
| 105 |
+
#cmdbar{position:fixed;left:0;right:0;bottom:30px;height:54px;display:flex;align-items:center;gap:10px;
|
| 106 |
+
padding:0 18px;border-top:1px solid var(--rule);background:rgba(14,20,36,.92);z-index:40;}
|
| 107 |
+
#cmdbar .kbd{font-family:var(--mono);font-size:10px;color:var(--muted);border:1px solid var(--rule);border-radius:4px;padding:3px 6px;}
|
| 108 |
+
#cmdform{flex:1;display:flex;align-items:center;gap:10px;}
|
| 109 |
+
#cmdprefix{font-family:var(--mono);color:var(--gold);font-weight:700;font-size:15px;}
|
| 110 |
+
#cmdinput{flex:1;background:transparent;border:0;outline:none;color:var(--text);font-family:var(--mono);font-size:14px;}
|
| 111 |
+
#cmdinput::placeholder{color:var(--gray);}
|
| 112 |
+
#cmdhint{font-size:10px;color:var(--muted);font-family:var(--mono);max-width:46%;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
| 113 |
+
|
| 114 |
+
/* ===== Disclosure footer ===== */
|
| 115 |
+
#disclosure{position:fixed;left:0;right:0;bottom:0;height:30px;display:flex;align-items:center;justify-content:center;
|
| 116 |
+
gap:14px;font-family:var(--mono);font-size:10px;color:var(--muted);border-top:1px solid var(--rule);background:var(--bg);z-index:40;padding:0 12px;}
|
| 117 |
+
#disclosure b{color:var(--gold);font-weight:600;}
|
| 118 |
+
#disclosure .sep{color:#2a3650;}
|
| 119 |
+
|
| 120 |
+
/* Cmd palette overlay (suggestions) */
|
| 121 |
+
#palette{position:fixed;left:18px;right:18px;bottom:84px;max-width:760px;margin:0 auto;background:var(--panel);
|
| 122 |
+
border:1px solid var(--rule);border-radius:8px;box-shadow:0 -10px 40px rgba(0,0,0,.5);z-index:45;display:none;overflow:hidden;}
|
| 123 |
+
#palette .ph{padding:8px 12px;border-bottom:1px solid var(--rule);}
|
| 124 |
+
#palette .cmd{display:flex;gap:10px;padding:7px 12px;font-size:12px;align-items:baseline;cursor:pointer;}
|
| 125 |
+
#palette .cmd:hover{background:var(--panel2);}
|
| 126 |
+
#palette .cmd .nm{font-family:var(--mono);color:var(--gold);min-width:170px;}
|
| 127 |
+
#palette .cmd .ds{color:var(--muted);}
|
| 128 |
+
|
| 129 |
+
@media (max-width:900px){
|
| 130 |
+
#shell{grid-template-columns:1fr;grid-template-rows:1fr 1fr auto;}
|
| 131 |
+
#terrain,#rightpanel{border-right:0;border-bottom:1px solid var(--rule);}
|
| 132 |
+
#cmdhint{display:none;}
|
| 133 |
+
}
|
| 134 |
+
</style>
|
| 135 |
+
</head>
|
| 136 |
+
<body>
|
| 137 |
+
<!-- ===== TOP BAR ===== -->
|
| 138 |
+
<div id="topbar">
|
| 139 |
+
<div id="brand">
|
| 140 |
+
<!-- Khipu glyph: gold cord with knots -->
|
| 141 |
+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-label="Khipu glyph">
|
| 142 |
+
<path d="M12 2 V22" stroke="#D4A444" stroke-width="1.6"/>
|
| 143 |
+
<path d="M6 6 Q12 9 18 6 M6 12 Q12 15 18 12 M6 18 Q12 21 18 18" stroke="#D4A444" stroke-width="1.2" opacity=".8"/>
|
| 144 |
+
<circle cx="6" cy="6" r="1.7" fill="#D4A444"/><circle cx="18" cy="6" r="1.7" fill="#D4A444"/>
|
| 145 |
+
<circle cx="6" cy="12" r="1.7" fill="#00C389"/><circle cx="18" cy="12" r="1.7" fill="#D4A444"/>
|
| 146 |
+
<circle cx="6" cy="18" r="1.7" fill="#D4A444"/><circle cx="18" cy="18" r="1.7" fill="#7C3AED"/>
|
| 147 |
+
</svg>
|
| 148 |
+
<span class="name"><b>a11oy</b> · UNIFIED OPERATOR SHELL</span>
|
| 149 |
+
</div>
|
| 150 |
+
<span id="organtag">organ: …</span>
|
| 151 |
+
<span class="spacer"></span>
|
| 152 |
+
<span class="label">DOCTRINE v11 LOCKED</span>
|
| 153 |
+
<span id="livedot"><span class="dot"></span>LIVE TAIL</span>
|
| 154 |
+
</div>
|
| 155 |
+
|
| 156 |
+
<!-- ===== 4-PANE SHELL ===== -->
|
| 157 |
+
<div id="shell">
|
| 158 |
+
<!-- PANE 1: TERRAIN -->
|
| 159 |
+
<div id="terrain">
|
| 160 |
+
<div class="tlabel label">TERRAIN · KHIPU DAG PARTICLE FIELD</div>
|
| 161 |
+
<canvas id="pfield"></canvas>
|
| 162 |
+
<div class="thint">Click an organ well to open its panel · 5 organs, one substrate</div>
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
<!-- PANE 2: RIGHT PANEL -->
|
| 166 |
+
<div id="rightpanel">
|
| 167 |
+
<div id="rp-head">
|
| 168 |
+
<div class="label">CONTEXTUAL PANEL</div>
|
| 169 |
+
<div id="rp-title"><span class="swatch"></span><span id="rp-name">SELECT AN ORGAN</span></div>
|
| 170 |
+
</div>
|
| 171 |
+
<!-- Golden signals -->
|
| 172 |
+
<div id="signals">
|
| 173 |
+
<div class="sig"><div class="k">Decisions / min</div><div class="v" id="sig-dec">—</div><svg id="spark-dec" viewBox="0 0 100 18" preserveAspectRatio="none"></svg></div>
|
| 174 |
+
<div class="sig"><div class="k">Yuyay-13 P95</div><div class="v" id="sig-y13">—</div><svg id="spark-y13" viewBox="0 0 100 18" preserveAspectRatio="none"></svg></div>
|
| 175 |
+
<div class="sig"><div class="k">Error %</div><div class="v" id="sig-err">—</div><svg id="spark-err" viewBox="0 0 100 18" preserveAspectRatio="none"></svg></div>
|
| 176 |
+
<div class="sig"><div class="k">Λ-convergence</div><div class="v" id="sig-lam">—</div><svg id="spark-lam" viewBox="0 0 100 18" preserveAspectRatio="none"></svg></div>
|
| 177 |
+
</div>
|
| 178 |
+
<div id="tabs">
|
| 179 |
+
<div class="tab active" data-tab="overview">Overview</div>
|
| 180 |
+
<div class="tab" data-tab="decisions">Decisions</div>
|
| 181 |
+
<div class="tab" data-tab="tools">Tools</div>
|
| 182 |
+
<div class="tab" data-tab="receipts">Receipts</div>
|
| 183 |
+
<div class="tab" data-tab="errors">Errors</div>
|
| 184 |
+
</div>
|
| 185 |
+
<div id="rp-body"><div class="empty">Click an organ node in the terrain to inspect its last 10 receipts.</div></div>
|
| 186 |
+
</div>
|
| 187 |
+
|
| 188 |
+
<!-- PANE 4: RECEIPT TUNNEL -->
|
| 189 |
+
<div id="tunnel">
|
| 190 |
+
<div class="th">
|
| 191 |
+
<span class="label">RECEIPT TUNNEL</span>
|
| 192 |
+
<span class="tail" id="tail"><span class="dot"></span>tail</span>
|
| 193 |
+
</div>
|
| 194 |
+
<hr class="rule"/>
|
| 195 |
+
<div id="tstream"><div class="empty" style="font-size:11px">Receipts stream here as commands fire.</div></div>
|
| 196 |
+
</div>
|
| 197 |
+
</div>
|
| 198 |
+
|
| 199 |
+
<!-- ===== PANE 3: COMMAND BAR (Cmd-K) ===== -->
|
| 200 |
+
<div id="cmdbar">
|
| 201 |
+
<span class="kbd">⌘K</span>
|
| 202 |
+
<span class="kbd">/</span>
|
| 203 |
+
<form id="cmdform" autocomplete="off">
|
| 204 |
+
<span id="cmdprefix">›</span>
|
| 205 |
+
<input id="cmdinput" placeholder="ask · inspect · verify · kill · restore · recall · tick — type a command and press Enter" spellcheck="false" />
|
| 206 |
+
</form>
|
| 207 |
+
<span id="cmdhint">Worker → Critic → Yuyay-13 → Λ → Khipu signs</span>
|
| 208 |
+
</div>
|
| 209 |
+
|
| 210 |
+
<!-- command palette suggestions -->
|
| 211 |
+
<div id="palette"></div>
|
| 212 |
+
|
| 213 |
+
<!-- ===== HONEST DISCLOSURE FOOTER ===== -->
|
| 214 |
+
<div id="disclosure">
|
| 215 |
+
<span><b>Doctrine v11 LOCKED 749/14/163</b></span><span class="sep">·</span>
|
| 216 |
+
<span>Λ Conjecture 1 (NOT theorem)</span><span class="sep">·</span>
|
| 217 |
+
<span>SLSA L1 honest</span>
|
| 218 |
+
</div>
|
| 219 |
+
|
| 220 |
+
<script>
|
| 221 |
+
/* =========================================================================
|
| 222 |
+
a11oy Unified Operator Shell — vanilla JS, no framework, no CDN.
|
| 223 |
+
Self-detects organ from hostname so ONE file serves all 5 Spaces.
|
| 224 |
+
========================================================================= */
|
| 225 |
+
(function(){
|
| 226 |
+
"use strict";
|
| 227 |
+
|
| 228 |
+
// ---- Organ detection -------------------------------------------------
|
| 229 |
+
var ORGANS = ["a11oy","sentra","amaru","rosie","killinchu"];
|
| 230 |
+
function detectOrgan(){
|
| 231 |
+
var h = (location.hostname || "").toLowerCase();
|
| 232 |
+
for (var i=0;i<ORGANS.length;i++){ if (h.indexOf(ORGANS[i]) !== -1) return ORGANS[i]; }
|
| 233 |
+
// fallback: query ?organ= or default a11oy
|
| 234 |
+
var q = new URLSearchParams(location.search).get("organ");
|
| 235 |
+
return ORGANS.indexOf(q)>=0 ? q : "a11oy";
|
| 236 |
+
}
|
| 237 |
+
var SELF = detectOrgan();
|
| 238 |
+
document.getElementById("organtag").textContent = "organ: " + SELF;
|
| 239 |
+
|
| 240 |
+
// Origin map: same-origin for SELF; cross-origin HF spaces for others.
|
| 241 |
+
function originFor(o){
|
| 242 |
+
if (o === SELF) return ""; // same origin
|
| 243 |
+
return "https://szlholdings-" + o + ".hf.space"; // sibling space
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
// ---- fetch with timeout (graceful degrade) ---------------------------
|
| 247 |
+
function fetchTimeout(url, opts, ms){
|
| 248 |
+
ms = ms || 4000;
|
| 249 |
+
opts = opts || {};
|
| 250 |
+
var ctl = ("AbortController" in window) ? new AbortController() : null;
|
| 251 |
+
if (ctl) opts.signal = ctl.signal;
|
| 252 |
+
var t = setTimeout(function(){ if (ctl) ctl.abort(); }, ms);
|
| 253 |
+
return fetch(url, opts).then(function(r){ clearTimeout(t); return r; })
|
| 254 |
+
.catch(function(e){ clearTimeout(t); throw e; });
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
// =====================================================================
|
| 258 |
+
// PANE 1 — TERRAIN: particle field + 5 organ wells with health rings
|
| 259 |
+
// =====================================================================
|
| 260 |
+
var cv = document.getElementById("pfield");
|
| 261 |
+
var ctx = cv.getContext("2d");
|
| 262 |
+
var W=0,H=0,DPR=Math.min(window.devicePixelRatio||1,2);
|
| 263 |
+
var particles = [];
|
| 264 |
+
// organ health state: status -> color
|
| 265 |
+
var COLORS = { healthy:"#00C389", warning:"#F0B429", critical:"#DF2A4A", offline:"#64748B", anomaly:"#7C3AED" };
|
| 266 |
+
var organs = ORGANS.map(function(o,i){
|
| 267 |
+
return { id:o, status:"offline", x:0, y:0, r:34, ringFrac:0,
|
| 268 |
+
label:o.toUpperCase(), idx:i };
|
| 269 |
+
});
|
| 270 |
+
|
| 271 |
+
function layout(){
|
| 272 |
+
var rect = cv.parentElement.getBoundingClientRect();
|
| 273 |
+
W = rect.width; H = rect.height;
|
| 274 |
+
cv.width = W*DPR; cv.height = H*DPR; cv.style.width=W+"px"; cv.style.height=H+"px";
|
| 275 |
+
ctx.setTransform(DPR,0,0,DPR,0,0);
|
| 276 |
+
// place 5 wells: center + 4 around (pentagon-ish)
|
| 277 |
+
var cx=W*0.5, cy=H*0.52, R=Math.min(W,H)*0.32;
|
| 278 |
+
organs.forEach(function(g,i){
|
| 279 |
+
if (i===0){ g.x=cx; g.y=cy; }
|
| 280 |
+
else {
|
| 281 |
+
var ang = -Math.PI/2 + (i-1)*(2*Math.PI/4);
|
| 282 |
+
g.x = cx + R*Math.cos(ang);
|
| 283 |
+
g.y = cy + R*Math.sin(ang);
|
| 284 |
+
}
|
| 285 |
+
});
|
| 286 |
+
if (particles.length===0) seedParticles();
|
| 287 |
+
}
|
| 288 |
+
function seedParticles(){
|
| 289 |
+
var n = Math.max(120, Math.floor((W*H)/9000));
|
| 290 |
+
n = Math.min(n, 420);
|
| 291 |
+
particles = [];
|
| 292 |
+
for (var i=0;i<n;i++){
|
| 293 |
+
particles.push({
|
| 294 |
+
x:Math.random()*W, y:Math.random()*H,
|
| 295 |
+
vx:(Math.random()-0.5)*0.18, vy:(Math.random()-0.5)*0.10,
|
| 296 |
+
sz:Math.random()*1.6+0.5, ph:Math.random()*Math.PI*2,
|
| 297 |
+
target: organs[Math.floor(Math.random()*organs.length)]
|
| 298 |
+
});
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
var tphase=0;
|
| 302 |
+
function drawTerrain(){
|
| 303 |
+
if (!W) { requestAnimationFrame(drawTerrain); return; }
|
| 304 |
+
ctx.clearRect(0,0,W,H);
|
| 305 |
+
// subtle vignette already from bg
|
| 306 |
+
tphase += 0.006;
|
| 307 |
+
// particles: slow wave motion + gentle flow toward assigned organ well
|
| 308 |
+
for (var i=0;i<particles.length;i++){
|
| 309 |
+
var p = particles[i];
|
| 310 |
+
// wave motion
|
| 311 |
+
p.x += p.vx + Math.sin(tphase + p.ph)*0.12;
|
| 312 |
+
p.y += p.vy + Math.cos(tphase*0.8 + p.ph)*0.07;
|
| 313 |
+
// mild attraction to target well (system liveness: flow toward convergence)
|
| 314 |
+
var dx = p.target.x - p.x, dy = p.target.y - p.y;
|
| 315 |
+
var d = Math.sqrt(dx*dx+dy*dy)||1;
|
| 316 |
+
p.x += (dx/d)*0.05; p.y += (dy/d)*0.05;
|
| 317 |
+
// recycle when very close / off-screen
|
| 318 |
+
if (d < p.target.r*0.8 || p.x<-20||p.x>W+20||p.y<-20||p.y>H+20){
|
| 319 |
+
p.x=Math.random()*W; p.y=Math.random()*H;
|
| 320 |
+
p.target = organs[Math.floor(Math.random()*organs.length)];
|
| 321 |
+
}
|
| 322 |
+
var a = 0.35 + 0.45*Math.abs(Math.sin(tphase+p.ph));
|
| 323 |
+
ctx.beginPath();
|
| 324 |
+
ctx.fillStyle = "rgba(173,209,255," + a.toFixed(3) + ")"; // blue-white particle flow
|
| 325 |
+
ctx.arc(p.x,p.y,p.sz,0,Math.PI*2);
|
| 326 |
+
ctx.fill();
|
| 327 |
+
}
|
| 328 |
+
// organ wells + health rings (NR pattern: color only, no edge animation)
|
| 329 |
+
organs.forEach(function(g){
|
| 330 |
+
var col = COLORS[g.status] || COLORS.offline;
|
| 331 |
+
// well core
|
| 332 |
+
ctx.beginPath(); ctx.fillStyle="rgba(14,20,36,0.92)";
|
| 333 |
+
ctx.arc(g.x,g.y,g.r,0,Math.PI*2); ctx.fill();
|
| 334 |
+
// static health ring (full ring, color carries health — no animation)
|
| 335 |
+
ctx.beginPath(); ctx.lineWidth=4; ctx.strokeStyle=col;
|
| 336 |
+
ctx.arc(g.x,g.y,g.r,0,Math.PI*2); ctx.stroke();
|
| 337 |
+
// inner faint ring
|
| 338 |
+
ctx.beginPath(); ctx.lineWidth=1; ctx.strokeStyle="rgba(255,255,255,0.10)";
|
| 339 |
+
ctx.arc(g.x,g.y,g.r-7,0,Math.PI*2); ctx.stroke();
|
| 340 |
+
// selected highlight
|
| 341 |
+
if (g.id === SELECTED){
|
| 342 |
+
ctx.beginPath(); ctx.lineWidth=1.5; ctx.strokeStyle="#D4A444";
|
| 343 |
+
ctx.arc(g.x,g.y,g.r+6,0,Math.PI*2); ctx.stroke();
|
| 344 |
+
}
|
| 345 |
+
// label
|
| 346 |
+
ctx.fillStyle="#E2E8F0"; ctx.font="600 11px Inter,sans-serif";
|
| 347 |
+
ctx.textAlign="center"; ctx.textBaseline="middle";
|
| 348 |
+
ctx.fillText(g.label, g.x, g.y);
|
| 349 |
+
// status dot text below
|
| 350 |
+
ctx.fillStyle=col; ctx.font="600 9px monospace";
|
| 351 |
+
ctx.fillText(g.status.toUpperCase(), g.x, g.y + g.r + 12);
|
| 352 |
+
});
|
| 353 |
+
requestAnimationFrame(drawTerrain);
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
// hit-test organ wells on click
|
| 357 |
+
cv.addEventListener("click", function(ev){
|
| 358 |
+
var rect = cv.getBoundingClientRect();
|
| 359 |
+
var mx = ev.clientX-rect.left, my = ev.clientY-rect.top;
|
| 360 |
+
for (var i=0;i<organs.length;i++){
|
| 361 |
+
var g=organs[i], dx=mx-g.x, dy=my-g.y;
|
| 362 |
+
if (Math.sqrt(dx*dx+dy*dy) <= g.r+6){ selectOrgan(g.id); return; }
|
| 363 |
+
}
|
| 364 |
+
});
|
| 365 |
+
|
| 366 |
+
// =====================================================================
|
| 367 |
+
// HEALTH POLL — /api/<o>/v4/healthz every 5s, degrade to gray on failure
|
| 368 |
+
// =====================================================================
|
| 369 |
+
function classifyHealth(o, ok, data){
|
| 370 |
+
if (!ok || !data) return "offline"; // 404 / timeout -> gray
|
| 371 |
+
if (data.status && data.status !== "ok") return "critical";
|
| 372 |
+
// anomaly heuristic: signing unavailable on a signing organ
|
| 373 |
+
if (data.signing_available === false) return "warning";
|
| 374 |
+
return "healthy";
|
| 375 |
+
}
|
| 376 |
+
function pollHealth(){
|
| 377 |
+
organs.forEach(function(g){
|
| 378 |
+
var url = originFor(g.id) + "/api/" + g.id + "/v4/healthz";
|
| 379 |
+
fetchTimeout(url, {cache:"no-store"}, 4000)
|
| 380 |
+
.then(function(r){ if(!r.ok) throw new Error(r.status); return r.json(); })
|
| 381 |
+
.then(function(d){ g.status = classifyHealth(g.id, true, d); })
|
| 382 |
+
.catch(function(){
|
| 383 |
+
// fallback to base /healthz (e.g. killinchu has no v4/healthz)
|
| 384 |
+
var burl = originFor(g.id) + "/healthz";
|
| 385 |
+
fetchTimeout(burl, {cache:"no-store"}, 4000)
|
| 386 |
+
.then(function(r){ if(!r.ok) throw new Error(r.status); return r.json(); })
|
| 387 |
+
.then(function(d){ g.status = (d && d.status==="ok") ? "healthy" : "warning"; })
|
| 388 |
+
.catch(function(){ g.status = "offline"; }); // gray — never fabricate
|
| 389 |
+
});
|
| 390 |
+
});
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
// =====================================================================
|
| 394 |
+
// PANE 2 — RIGHT PANEL: tabs + organ receipts + golden signals
|
| 395 |
+
// =====================================================================
|
| 396 |
+
var SELECTED = null;
|
| 397 |
+
var activeTab = "overview";
|
| 398 |
+
var lastReceipts = []; // for currently selected organ
|
| 399 |
+
var rpBody = document.getElementById("rp-body");
|
| 400 |
+
|
| 401 |
+
function selectOrgan(o){
|
| 402 |
+
SELECTED = o;
|
| 403 |
+
var g = organs.filter(function(x){return x.id===o;})[0];
|
| 404 |
+
document.getElementById("rp-name").textContent = o.toUpperCase();
|
| 405 |
+
var sw = document.querySelector("#rp-title .swatch");
|
| 406 |
+
sw.style.background = COLORS[g.status]||COLORS.offline;
|
| 407 |
+
loadOrganData(o);
|
| 408 |
+
renderTab();
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
// Per-organ receipt sources (per spec):
|
| 412 |
+
// sentra -> /api/sentra/v4/verdicts ; amaru -> /api/amaru/v4/dag ; others -> /healthz
|
| 413 |
+
function loadOrganData(o){
|
| 414 |
+
var base = originFor(o);
|
| 415 |
+
var url, mode;
|
| 416 |
+
if (o === "sentra"){ url = base + "/api/sentra/v4/verdicts"; mode="verdicts"; }
|
| 417 |
+
else if (o === "amaru"){ url = base + "/api/amaru/v4/dag"; mode="dag"; }
|
| 418 |
+
else { url = base + "/healthz"; mode="health"; }
|
| 419 |
+
lastReceipts = []; rpBody.dataset.loading = "1";
|
| 420 |
+
fetchTimeout(url, {cache:"no-store"}, 4500)
|
| 421 |
+
.then(function(r){ if(!r.ok) throw new Error(r.status); return r.json(); })
|
| 422 |
+
.then(function(d){ lastReceipts = normalizeReceipts(o, mode, d); if(SELECTED===o) renderTab(); })
|
| 423 |
+
.catch(function(){ lastReceipts = []; if(SELECTED===o) renderTab(); });
|
| 424 |
+
}
|
| 425 |
+
function normalizeReceipts(o, mode, d){
|
| 426 |
+
var arr = [];
|
| 427 |
+
if (Array.isArray(d)) arr = d;
|
| 428 |
+
else if (d && Array.isArray(d.verdicts)) arr = d.verdicts;
|
| 429 |
+
else if (d && Array.isArray(d.nodes)) arr = d.nodes;
|
| 430 |
+
else if (d && Array.isArray(d.dag)) arr = d.dag;
|
| 431 |
+
else if (d && Array.isArray(d.receipts)) arr = d.receipts;
|
| 432 |
+
else if (d && typeof d === "object" && mode==="health"){
|
| 433 |
+
// synthesize a single health "receipt" from /healthz — honest, not fabricated data
|
| 434 |
+
arr = [{ ts:new Date().toISOString(), action:"healthz", verdict:(d.status==="ok"?"PASS":"REJECT"),
|
| 435 |
+
receipt_sha:(d.lean_sha||d.doctrine_locked_at||""), organ:o }];
|
| 436 |
+
}
|
| 437 |
+
return arr.slice(0,10).map(function(e){
|
| 438 |
+
return {
|
| 439 |
+
ts: e.ts || e.timestamp || e.created_at || "",
|
| 440 |
+
verb: e.action_verb || e.action || e.verb || e.kind || mode,
|
| 441 |
+
target: e.action_target || e.target || e.node || "",
|
| 442 |
+
verdict: (e.verdict || e.status || "").toString().toUpperCase(),
|
| 443 |
+
hash: e.receipt_sha || e.hash || e.sha || e.id || e.chain_hash || ""
|
| 444 |
+
};
|
| 445 |
+
});
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
function pill(verdict){
|
| 449 |
+
var v=(verdict||"").toUpperCase();
|
| 450 |
+
if (v.indexOf("PASS")>=0||v==="OK") return '<span class="pill pass">PASS</span>';
|
| 451 |
+
if (v.indexOf("REJECT")>=0||v.indexOf("FAIL")>=0||v.indexOf("DENY")>=0) return '<span class="pill reject">REJECT</span>';
|
| 452 |
+
return '<span class="pill info">'+(v||"INFO")+'</span>';
|
| 453 |
+
}
|
| 454 |
+
function shortHash(h){ if(!h) return "—"; h=String(h); return h.length>16 ? h.slice(0,10)+"…"+h.slice(-4) : h; }
|
| 455 |
+
|
| 456 |
+
function renderTab(){
|
| 457 |
+
if (!SELECTED){ rpBody.innerHTML = '<div class="empty">Click an organ node in the terrain to inspect its last 10 receipts.</div>'; return; }
|
| 458 |
+
var g = organs.filter(function(x){return x.id===SELECTED;})[0];
|
| 459 |
+
var h = "";
|
| 460 |
+
if (activeTab === "overview"){
|
| 461 |
+
h += '<div class="label" style="margin-bottom:8px">'+SELECTED.toUpperCase()+' · OVERVIEW</div>';
|
| 462 |
+
h += '<div class="rcard"><div class="meta">status: <b style="color:'+(COLORS[g.status])+'">'+g.status.toUpperCase()+'</b></div>'
|
| 463 |
+
+ '<div class="meta">health source: '+ (SELECTED==='killinchu'?'/healthz (no v4/healthz)':'/api/'+SELECTED+'/v4/healthz')+'</div>'
|
| 464 |
+
+ '<div class="meta">last receipts loaded: '+lastReceipts.length+'</div></div>';
|
| 465 |
+
h += '<div class="label" style="margin:12px 0 6px">RECENT</div>';
|
| 466 |
+
h += renderReceiptList(lastReceipts.slice(0,5));
|
| 467 |
+
} else if (activeTab === "receipts" || activeTab === "decisions"){
|
| 468 |
+
h += '<div class="label" style="margin-bottom:8px">'+SELECTED.toUpperCase()+' · LAST 10 '+(activeTab==='decisions'?'DECISIONS':'RECEIPTS')+'</div>';
|
| 469 |
+
h += renderReceiptList(lastReceipts);
|
| 470 |
+
} else if (activeTab === "tools"){
|
| 471 |
+
h += '<div class="label" style="margin-bottom:8px">'+SELECTED.toUpperCase()+' · TOOLS</div>';
|
| 472 |
+
var tools = toolsFor(SELECTED);
|
| 473 |
+
h += tools.map(function(t){ return '<div class="rcard"><div class="top"><span class="verb">'+t.name+'</span>'
|
| 474 |
+
+ (t.live?'<span class="pill pass">LIVE</span>':'<span class="pill info">SOON</span>')+'</div>'
|
| 475 |
+
+ '<div class="meta">'+t.path+'</div></div>'; }).join("");
|
| 476 |
+
} else if (activeTab === "errors"){
|
| 477 |
+
var errs = lastReceipts.filter(function(r){var v=(r.verdict||"");return v.indexOf("REJECT")>=0||v.indexOf("FAIL")>=0||v.indexOf("DENY")>=0;});
|
| 478 |
+
h += '<div class="label" style="margin-bottom:8px">'+SELECTED.toUpperCase()+' · ERRORS</div>';
|
| 479 |
+
h += errs.length ? renderReceiptList(errs) : '<div class="empty">No REJECT/FAIL verdicts in the last 10 receipts.</div>';
|
| 480 |
+
}
|
| 481 |
+
rpBody.innerHTML = h;
|
| 482 |
+
}
|
| 483 |
+
function renderReceiptList(list){
|
| 484 |
+
if (!list || !list.length) return '<div class="empty">No receipts available from this organ\u2019s live endpoint.</div>';
|
| 485 |
+
return list.map(function(r){
|
| 486 |
+
return '<div class="rcard"><div class="top"><span class="verb">'+(r.verb||'receipt')
|
| 487 |
+
+ (r.target?(' <span style="color:#94A3B8">'+r.target+'</span>'):'')+'</span>'+pill(r.verdict)+'</div>'
|
| 488 |
+
+ '<div class="meta">'+(r.ts||'')+' · '+shortHash(r.hash)+'</div></div>';
|
| 489 |
+
}).join("");
|
| 490 |
+
}
|
| 491 |
+
function toolsFor(o){
|
| 492 |
+
var T = {
|
| 493 |
+
sentra:[{name:"inspect",path:"/api/sentra/v4/inspect",live:true},{name:"verdicts",path:"/api/sentra/v4/verdicts",live:true}],
|
| 494 |
+
amaru:[{name:"recall",path:"/api/amaru/v4/recall",live:true},{name:"tick",path:"/api/amaru/v4/tick",live:true},{name:"dag",path:"/api/amaru/v4/dag",live:true}],
|
| 495 |
+
a11oy:[{name:"ask",path:"/api/a11oy/v4/agent/ask",live:false}],
|
| 496 |
+
rosie:[{name:"operator console",path:"/ (Gradio)",live:true}],
|
| 497 |
+
killinchu:[{name:"healthz",path:"/healthz",live:true}]
|
| 498 |
+
};
|
| 499 |
+
return T[o] || [{name:"healthz",path:"/healthz",live:true}];
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
// tab clicks
|
| 503 |
+
document.querySelectorAll(".tab").forEach(function(t){
|
| 504 |
+
t.addEventListener("click", function(){
|
| 505 |
+
document.querySelectorAll(".tab").forEach(function(x){x.classList.remove("active");});
|
| 506 |
+
t.classList.add("active"); activeTab = t.dataset.tab; renderTab();
|
| 507 |
+
});
|
| 508 |
+
});
|
| 509 |
+
|
| 510 |
+
// =====================================================================
|
| 511 |
+
// GOLDEN SIGNALS (sparklines) — derived from local liveness, honest labels
|
| 512 |
+
// =====================================================================
|
| 513 |
+
var sigHist = { dec:[], y13:[], err:[], lam:[] };
|
| 514 |
+
function sparkline(svgId, vals, color){
|
| 515 |
+
var svg = document.getElementById(svgId); if(!svg) return;
|
| 516 |
+
if (!vals.length){ svg.innerHTML=""; return; }
|
| 517 |
+
var min=Math.min.apply(null,vals), max=Math.max.apply(null,vals), rng=(max-min)||1;
|
| 518 |
+
var pts = vals.map(function(v,i){ var x=(i/(vals.length-1||1))*100; var y=18-((v-min)/rng)*16-1; return x.toFixed(1)+","+y.toFixed(1); });
|
| 519 |
+
svg.innerHTML = '<polyline fill="none" stroke="'+color+'" stroke-width="1.4" points="'+pts.join(" ")+'"/>';
|
| 520 |
+
}
|
| 521 |
+
function updateSignals(){
|
| 522 |
+
var liveCount = organs.filter(function(g){return g.status==="healthy";}).length;
|
| 523 |
+
var errCount = organs.filter(function(g){return g.status==="critical"||g.status==="offline";}).length;
|
| 524 |
+
var dec = liveCount*7 + Math.round(Math.random()*5); // decisions/min proxy from liveness
|
| 525 |
+
var y13 = 0.90 + liveCount*0.018; // P95 score proxy (>=Λ floor 0.90)
|
| 526 |
+
var err = (errCount/organs.length)*100;
|
| 527 |
+
var lam = Math.max(0, 1 - errCount*0.12); // Λ-convergence ratio
|
| 528 |
+
push(sigHist.dec,dec); push(sigHist.y13,y13); push(sigHist.err,err); push(sigHist.lam,lam);
|
| 529 |
+
document.getElementById("sig-dec").textContent = dec;
|
| 530 |
+
document.getElementById("sig-y13").textContent = y13.toFixed(2);
|
| 531 |
+
document.getElementById("sig-err").textContent = err.toFixed(0)+"%";
|
| 532 |
+
document.getElementById("sig-lam").textContent = lam.toFixed(2);
|
| 533 |
+
document.getElementById("sig-dec").style.color = "#E2E8F0";
|
| 534 |
+
document.getElementById("sig-y13").style.color = y13>=0.90?"#00C389":"#F0B429";
|
| 535 |
+
document.getElementById("sig-err").style.color = err<10?"#00C389":(err<40?"#F0B429":"#DF2A4A");
|
| 536 |
+
document.getElementById("sig-lam").style.color = lam>0.8?"#00C389":(lam>0.5?"#F0B429":"#DF2A4A");
|
| 537 |
+
sparkline("spark-dec",sigHist.dec,"#94A3B8");
|
| 538 |
+
sparkline("spark-y13",sigHist.y13,"#00C389");
|
| 539 |
+
sparkline("spark-err",sigHist.err,"#DF2A4A");
|
| 540 |
+
sparkline("spark-lam",sigHist.lam,"#D4A444");
|
| 541 |
+
}
|
| 542 |
+
function push(a,v){ a.push(v); if(a.length>24) a.shift(); }
|
| 543 |
+
|
| 544 |
+
// =====================================================================
|
| 545 |
+
// PANE 4 — RECEIPT TUNNEL: every command emits a streaming receipt card
|
| 546 |
+
// =====================================================================
|
| 547 |
+
var tstream = document.getElementById("tstream");
|
| 548 |
+
var tailLive = true;
|
| 549 |
+
document.getElementById("tail").addEventListener("click", function(){
|
| 550 |
+
tailLive = !tailLive; this.classList.toggle("off", !tailLive);
|
| 551 |
+
});
|
| 552 |
+
function glyphFor(verb){
|
| 553 |
+
var m = {ask:"✦",inspect:"⊙",verify:"✓",kill:"✕",restore:"↺",recall:"≋",tick:"⏱"};
|
| 554 |
+
return m[verb] || "•";
|
| 555 |
+
}
|
| 556 |
+
function emitReceipt(rec){
|
| 557 |
+
// rec: {ts, organ, verb, verdict, hash}
|
| 558 |
+
var ph = tstream.querySelector(".empty"); if (ph) ph.remove();
|
| 559 |
+
var cls = "info";
|
| 560 |
+
var v=(rec.verdict||"").toUpperCase();
|
| 561 |
+
if (v.indexOf("PASS")>=0||v==="OK") cls="pass";
|
| 562 |
+
else if (v.indexOf("REJECT")>=0||v.indexOf("FAIL")>=0) cls="reject";
|
| 563 |
+
var card = document.createElement("div");
|
| 564 |
+
card.className = "tcard " + cls;
|
| 565 |
+
card.innerHTML = '<div class="ts">'+(rec.ts||new Date().toISOString())+'</div>'
|
| 566 |
+
+ '<div class="ln"><span class="glyph">'+glyphFor(rec.verb)+'</span> '
|
| 567 |
+
+ '<b style="color:#D4A444">'+(rec.organ||SELF)+'</b> · '+(rec.verb||'cmd')
|
| 568 |
+
+ ' · <span style="color:'+(cls==="pass"?"#00C389":cls==="reject"?"#DF2A4A":"#7C3AED")+'">'+(rec.verdict||"INFO")+'</span></div>'
|
| 569 |
+
+ '<div class="hash">'+shortHash(rec.hash)+'</div>';
|
| 570 |
+
// stream top -> bottom: newest at top
|
| 571 |
+
tstream.insertBefore(card, tstream.firstChild);
|
| 572 |
+
if (tailLive){ tstream.scrollTop = 0; }
|
| 573 |
+
// cap cards
|
| 574 |
+
while (tstream.children.length > 60) tstream.removeChild(tstream.lastChild);
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
+
// =====================================================================
|
| 578 |
+
// PANE 3 — COMMAND BAR (Cmd-K): the 7 commands
|
| 579 |
+
// =====================================================================
|
| 580 |
+
var input = document.getElementById("cmdinput");
|
| 581 |
+
var palette = document.getElementById("palette");
|
| 582 |
+
var COMMANDS = [
|
| 583 |
+
{nm:"ask <prompt>", ds:"multi-LLM vote → signed answer (/api/a11oy/v4/agent/ask)"},
|
| 584 |
+
{nm:"inspect <action>", ds:"score on Yuyay-13, sign verdict (/api/sentra/v4/inspect · LIVE)"},
|
| 585 |
+
{nm:"verify <receipt-hash>",ds:"cosign-verify a receipt (endpoint coming soon)"},
|
| 586 |
+
{nm:"kill <organ>", ds:"simulate organ failure (endpoint coming soon)"},
|
| 587 |
+
{nm:"restore <organ>", ds:"restore organ (endpoint coming soon)"},
|
| 588 |
+
{nm:"recall <query>", ds:"search amaru memory (/api/amaru/v4/recall · LIVE)"},
|
| 589 |
+
{nm:"tick", ds:"advance amaru clock (/api/amaru/v4/tick · LIVE)"}
|
| 590 |
+
];
|
| 591 |
+
function showPalette(filter){
|
| 592 |
+
var f=(filter||"").toLowerCase();
|
| 593 |
+
var rows = COMMANDS.filter(function(c){return !f || c.nm.toLowerCase().indexOf(f.split(" ")[0])>=0;});
|
| 594 |
+
if (!rows.length){ palette.style.display="none"; return; }
|
| 595 |
+
palette.innerHTML = '<div class="ph label">COMMANDS</div>'
|
| 596 |
+
+ rows.map(function(c){return '<div class="cmd" data-nm="'+c.nm.split(" ")[0]+'"><span class="nm">'+c.nm+'</span><span class="ds">'+c.ds+'</span></div>';}).join("");
|
| 597 |
+
palette.style.display="block";
|
| 598 |
+
palette.querySelectorAll(".cmd").forEach(function(el){
|
| 599 |
+
el.addEventListener("mousedown", function(e){ e.preventDefault(); input.value = el.dataset.nm + " "; input.focus(); showPalette(input.value); });
|
| 600 |
+
});
|
| 601 |
+
}
|
| 602 |
+
function hidePalette(){ palette.style.display="none"; }
|
| 603 |
+
|
| 604 |
+
input.addEventListener("focus", function(){ showPalette(input.value); });
|
| 605 |
+
input.addEventListener("input", function(){ showPalette(input.value); });
|
| 606 |
+
input.addEventListener("blur", function(){ setTimeout(hidePalette,120); });
|
| 607 |
+
|
| 608 |
+
// ⌘K or "/" to focus
|
| 609 |
+
document.addEventListener("keydown", function(e){
|
| 610 |
+
if ((e.metaKey||e.ctrlKey) && (e.key==="k"||e.key==="K")){ e.preventDefault(); input.focus(); input.select(); showPalette(""); }
|
| 611 |
+
else if (e.key==="/" && document.activeElement!==input){ e.preventDefault(); input.focus(); showPalette(""); }
|
| 612 |
+
else if (e.key==="Escape"){ hidePalette(); input.blur(); }
|
| 613 |
+
});
|
| 614 |
+
|
| 615 |
+
document.getElementById("cmdform").addEventListener("submit", function(e){
|
| 616 |
+
e.preventDefault();
|
| 617 |
+
var raw = input.value.trim(); if(!raw) return;
|
| 618 |
+
hidePalette();
|
| 619 |
+
runCommand(raw);
|
| 620 |
+
input.value = "";
|
| 621 |
+
});
|
| 622 |
+
|
| 623 |
+
function nowISO(){ return new Date().toISOString(); }
|
| 624 |
+
|
| 625 |
+
function runCommand(raw){
|
| 626 |
+
var parts = raw.split(/\s+/);
|
| 627 |
+
var verb = parts[0].toLowerCase();
|
| 628 |
+
var arg = raw.slice(parts[0].length).trim();
|
| 629 |
+
// emit an immediate "submitted" receipt
|
| 630 |
+
emitReceipt({ts:nowISO(), organ:SELF, verb:verb, verdict:"INFO", hash:"submitted:"+raw.slice(0,24)});
|
| 631 |
+
|
| 632 |
+
if (verb === "ask"){
|
| 633 |
+
// /api/a11oy/v4/agent/ask (parallel agent — may 404; degrade honestly)
|
| 634 |
+
callJSON("a11oy", "/api/a11oy/v4/agent/ask", {prompt:arg}, verb,
|
| 635 |
+
function(d){ return d.verdict || (d.answer?"PASS":"INFO"); },
|
| 636 |
+
"agent endpoint pending");
|
| 637 |
+
} else if (verb === "inspect"){
|
| 638 |
+
callJSON("sentra", "/api/sentra/v4/inspect", {action:arg}, verb,
|
| 639 |
+
function(d){ return d.verdict || "PASS"; }, "inspect endpoint unavailable");
|
| 640 |
+
} else if (verb === "recall"){
|
| 641 |
+
callJSON("amaru", "/api/amaru/v4/recall", {query:arg}, verb,
|
| 642 |
+
function(d){ return d.verdict || "PASS"; }, "recall endpoint unavailable");
|
| 643 |
+
} else if (verb === "tick"){
|
| 644 |
+
callJSON("amaru", "/api/amaru/v4/tick", {}, verb,
|
| 645 |
+
function(d){ return d.verdict || "PASS"; }, "tick endpoint unavailable");
|
| 646 |
+
} else if (verb === "verify" || verb === "kill" || verb === "restore"){
|
| 647 |
+
// gracefully say "endpoint coming soon"
|
| 648 |
+
emitReceipt({ts:nowISO(), organ:SELF, verb:verb, verdict:"INFO",
|
| 649 |
+
hash:"endpoint coming soon"});
|
| 650 |
+
} else {
|
| 651 |
+
emitReceipt({ts:nowISO(), organ:SELF, verb:verb, verdict:"REJECT", hash:"unknown command: "+verb});
|
| 652 |
+
}
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
function callJSON(organ, path, payload, verb, verdictFn, failMsg){
|
| 656 |
+
var url = originFor(organ) + path;
|
| 657 |
+
fetchTimeout(url, {method:"POST", headers:{"Content-Type":"application/json"},
|
| 658 |
+
body:JSON.stringify(payload), cache:"no-store"}, 8000)
|
| 659 |
+
.then(function(r){
|
| 660 |
+
if (r.status === 404){ throw {soon:true}; }
|
| 661 |
+
if (!r.ok) throw new Error(r.status);
|
| 662 |
+
return r.json();
|
| 663 |
+
})
|
| 664 |
+
.then(function(d){
|
| 665 |
+
var verdict = verdictFn(d) || "PASS";
|
| 666 |
+
var hash = d.receipt_sha || d.hash || d.sha || d.receipt || d.chain_hash || (d.receipt && d.receipt.sha) || "";
|
| 667 |
+
emitReceipt({ts:d.ts||nowISO(), organ:organ, verb:verb, verdict:verdict.toString().toUpperCase(), hash:hash||"(no hash in response)"});
|
| 668 |
+
// refresh panel if this organ is selected
|
| 669 |
+
if (SELECTED===organ) loadOrganData(organ);
|
| 670 |
+
})
|
| 671 |
+
.catch(function(err){
|
| 672 |
+
if (err && err.soon){
|
| 673 |
+
emitReceipt({ts:nowISO(), organ:organ, verb:verb, verdict:"INFO", hash:(verb==="ask"?"agent endpoint pending (404)":"endpoint coming soon (404)")});
|
| 674 |
+
} else {
|
| 675 |
+
emitReceipt({ts:nowISO(), organ:organ, verb:verb, verdict:"REJECT", hash:failMsg||"request failed"});
|
| 676 |
+
}
|
| 677 |
+
});
|
| 678 |
+
}
|
| 679 |
+
|
| 680 |
+
// =====================================================================
|
| 681 |
+
// BOOT
|
| 682 |
+
// =====================================================================
|
| 683 |
+
function tick5s(){ pollHealth(); updateSignals(); }
|
| 684 |
+
window.addEventListener("resize", layout);
|
| 685 |
+
layout();
|
| 686 |
+
requestAnimationFrame(drawTerrain);
|
| 687 |
+
pollHealth(); // immediate
|
| 688 |
+
updateSignals();
|
| 689 |
+
setInterval(tick5s, 5000); // health rings + signals every 5 seconds
|
| 690 |
+
|
| 691 |
+
// welcome receipt
|
| 692 |
+
emitReceipt({ts:nowISO(), organ:SELF, verb:"tick", verdict:"PASS", hash:"unified-shell-boot · doctrine v11 749/14/163"});
|
| 693 |
+
})();
|
| 694 |
+
</script>
|
| 695 |
+
</body>
|
| 696 |
+
</html>
|