openalerts 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +253 -0
- package/dist/channels/console.d.ts +6 -0
- package/dist/channels/console.d.ts.map +1 -0
- package/dist/channels/console.js +10 -0
- package/dist/channels/console.js.map +1 -0
- package/dist/channels/telegram.d.ts +12 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +28 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/webhook.d.ts +8 -0
- package/dist/channels/webhook.d.ts.map +1 -0
- package/dist/channels/webhook.js +15 -0
- package/dist/channels/webhook.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +234 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +51 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/core/alert-channel.d.ts +16 -0
- package/dist/core/alert-channel.d.ts.map +1 -0
- package/dist/core/alert-channel.js +23 -0
- package/dist/core/alert-channel.js.map +1 -0
- package/dist/core/bounded-map.d.ts +52 -0
- package/dist/core/bounded-map.d.ts.map +1 -0
- package/dist/core/bounded-map.js +129 -0
- package/dist/core/bounded-map.js.map +1 -0
- package/dist/core/engine.d.ts +36 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +162 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/evaluator.d.ts +19 -0
- package/dist/core/evaluator.d.ts.map +1 -0
- package/dist/core/evaluator.js +168 -0
- package/dist/core/evaluator.js.map +1 -0
- package/dist/core/event-bus.d.ts +18 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +32 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/formatter.d.ts +15 -0
- package/dist/core/formatter.d.ts.map +1 -0
- package/dist/core/formatter.js +125 -0
- package/dist/core/formatter.js.map +1 -0
- package/dist/core/rules.d.ts +3 -0
- package/dist/core/rules.d.ts.map +1 -0
- package/dist/core/rules.js +410 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/store.d.ts +9 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/store.js +72 -0
- package/dist/core/store.js.map +1 -0
- package/dist/core/types.d.ts +158 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +18 -0
- package/dist/core/types.js.map +1 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +31 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/queries.d.ts +157 -0
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +221 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/db/schema.d.ts +5 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +177 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/readers/openclaw.d.ts +11 -0
- package/dist/readers/openclaw.d.ts.map +1 -0
- package/dist/readers/openclaw.js +267 -0
- package/dist/readers/openclaw.js.map +1 -0
- package/dist/server/dashboard.d.ts +2 -0
- package/dist/server/dashboard.d.ts.map +1 -0
- package/dist/server/dashboard.js +765 -0
- package/dist/server/dashboard.js.map +1 -0
- package/dist/server/index.d.ts +10 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +28 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/routes.d.ts +6 -0
- package/dist/server/routes.d.ts.map +1 -0
- package/dist/server/routes.js +146 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/server/sse.d.ts +21 -0
- package/dist/server/sse.d.ts.map +1 -0
- package/dist/server/sse.js +53 -0
- package/dist/server/sse.js.map +1 -0
- package/dist/watchers/files.d.ts +19 -0
- package/dist/watchers/files.d.ts.map +1 -0
- package/dist/watchers/files.js +105 -0
- package/dist/watchers/files.js.map +1 -0
- package/dist/watchers/gateway-adapter.d.ts +18 -0
- package/dist/watchers/gateway-adapter.d.ts.map +1 -0
- package/dist/watchers/gateway-adapter.js +273 -0
- package/dist/watchers/gateway-adapter.js.map +1 -0
- package/dist/watchers/gateway.d.ts +27 -0
- package/dist/watchers/gateway.d.ts.map +1 -0
- package/dist/watchers/gateway.js +131 -0
- package/dist/watchers/gateway.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
// Dashboard HTML — backtick chars inside JS are written as \x60 to avoid TS template literal conflicts
|
|
2
|
+
const CSS = `
|
|
3
|
+
:root {
|
|
4
|
+
--bg:#0d0f17;--surface:#13151f;--surface2:#1c1f2e;--border:#252839;
|
|
5
|
+
--text:#e2e8f0;--muted:#4a5568;--green:#22c55e;--yellow:#f59e0b;
|
|
6
|
+
--red:#ef4444;--blue:#3b82f6;--critical:#dc2626;--error:#ef4444;
|
|
7
|
+
--warn:#f59e0b;--info:#3b82f6;--font:'SF Mono','Fira Code','Consolas',monospace;
|
|
8
|
+
}
|
|
9
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
10
|
+
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5}
|
|
11
|
+
header{background:var(--surface);border-bottom:1px solid var(--border);padding:10px 20px;display:flex;align-items:center;gap:12px;position:sticky;top:0;z-index:10}
|
|
12
|
+
header h1{font-size:15px;font-weight:600;letter-spacing:-.2px;display:flex;align-items:center}
|
|
13
|
+
.dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:7px;flex-shrink:0}
|
|
14
|
+
.dot.live{background:var(--green);animation:pulse 2s infinite}
|
|
15
|
+
.dot.dead{background:var(--red)}
|
|
16
|
+
.dot.warn{background:var(--yellow);animation:pulse 2s infinite}
|
|
17
|
+
.hdr-stat{color:var(--muted);font-size:12px}
|
|
18
|
+
.hdr-stat b{color:var(--text);font-weight:500}
|
|
19
|
+
.badge{padding:2px 7px;border-radius:2px;font-size:11px;font-weight:600;letter-spacing:.5px}
|
|
20
|
+
.badge-green{background:rgba(34,197,94,.12);color:var(--green)}
|
|
21
|
+
.badge-red{background:rgba(239,68,68,.12);color:var(--red)}
|
|
22
|
+
.badge-yellow{background:rgba(245,158,11,.12);color:var(--yellow)}
|
|
23
|
+
.badge-blue{background:rgba(59,130,246,.12);color:var(--blue)}
|
|
24
|
+
.badge-muted{background:var(--surface2);color:var(--muted)}
|
|
25
|
+
#conn-status{margin-left:auto;font-size:11px}
|
|
26
|
+
nav{display:flex;gap:0;padding:0 16px;background:var(--surface);border-bottom:1px solid var(--border);overflow-x:auto}
|
|
27
|
+
nav button{background:none;border:none;border-bottom:2px solid transparent;color:var(--muted);cursor:pointer;padding:8px 14px;font-size:12px;font-weight:500;white-space:nowrap;transition:color .1s,border-color .1s}
|
|
28
|
+
nav button:hover{color:var(--text)}
|
|
29
|
+
nav button.active{color:var(--text);border-bottom-color:var(--blue)}
|
|
30
|
+
main{padding:0}
|
|
31
|
+
.tab{display:none} .tab.active{display:block}
|
|
32
|
+
.grid{display:grid;gap:0;border-top:1px solid var(--border);border-left:1px solid var(--border)} .grid-2{grid-template-columns:1fr 1fr} .grid-3{grid-template-columns:1fr 1fr 1fr}
|
|
33
|
+
@media(max-width:900px){.grid-2,.grid-3{grid-template-columns:1fr}}
|
|
34
|
+
.card{background:var(--surface);border-right:1px solid var(--border);border-bottom:1px solid var(--border);padding:16px 20px}
|
|
35
|
+
.card h2{font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.8px;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid var(--border)}
|
|
36
|
+
.stat-row{display:flex;justify-content:space-between;align-items:center;padding:6px 0;border-bottom:1px solid var(--border);font-size:13px}
|
|
37
|
+
.stat-row:last-child{border-bottom:none}
|
|
38
|
+
.stat-val{font-family:var(--font);color:var(--text);font-weight:600}
|
|
39
|
+
.empty{color:var(--muted);font-size:12px;text-align:center;padding:32px;font-family:var(--font)}
|
|
40
|
+
.alert-item{padding:10px 14px;border-left:2px solid;background:var(--surface2);margin-bottom:1px}
|
|
41
|
+
.alert-item.critical{border-color:var(--critical)} .alert-item.error{border-color:var(--error)}
|
|
42
|
+
.alert-item.warn{border-color:var(--warn)} .alert-item.info{border-color:var(--info)}
|
|
43
|
+
.alert-title{font-weight:600;font-size:13px}
|
|
44
|
+
.alert-detail{font-size:12px;color:var(--muted);margin-top:2px}
|
|
45
|
+
.alert-meta{font-size:11px;color:var(--muted);margin-top:4px;font-family:var(--font)}
|
|
46
|
+
.agent-card{border-left:2px solid var(--blue);padding-left:12px;margin-bottom:12px}
|
|
47
|
+
.agent-name{font-size:15px;font-weight:700}
|
|
48
|
+
.doc-preview{background:var(--bg);border:1px solid var(--border);padding:12px;font-size:12px;font-family:var(--font);white-space:pre-wrap;word-break:break-word;max-height:200px;overflow-y:auto;color:var(--muted);line-height:1.6;margin-top:8px}
|
|
49
|
+
.cron-item{padding:10px 14px;border-bottom:1px solid var(--border);background:var(--surface2)}
|
|
50
|
+
.cron-item:last-child{border-bottom:none}
|
|
51
|
+
.cron-item.error{border-left:2px solid var(--red)} .cron-item.success{border-left:2px solid var(--green)}
|
|
52
|
+
.cron-name{font-weight:600;font-size:13px}
|
|
53
|
+
.cron-meta{font-size:12px;color:var(--muted);margin-top:2px;font-family:var(--font)}
|
|
54
|
+
.cron-error{font-size:11px;color:var(--red);margin-top:4px;font-family:var(--font);word-break:break-all}
|
|
55
|
+
.session-item{padding:8px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px}
|
|
56
|
+
.session-item:last-child{border-bottom:none}
|
|
57
|
+
.session-key{font-family:var(--font);font-size:12px;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
58
|
+
.session-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}
|
|
59
|
+
.session-dot.active{background:var(--green);box-shadow:0 0 4px var(--green)} .session-dot.idle{background:var(--muted)}
|
|
60
|
+
.diag-item{font-size:12px;font-family:var(--font);padding:4px 10px;border-bottom:1px solid var(--border);color:var(--muted)}
|
|
61
|
+
.diag-item:last-child{border-bottom:none}
|
|
62
|
+
.diag-ts{color:var(--blue);margin-right:8px} .diag-type{color:var(--text)}
|
|
63
|
+
.delivery-item{padding:8px 14px;border-bottom:1px solid var(--border);font-size:12px}
|
|
64
|
+
.delivery-item:last-child{border-bottom:none}
|
|
65
|
+
.delivery-item.pending{border-left:2px solid var(--yellow)} .delivery-item.failed{border-left:2px solid var(--red)}
|
|
66
|
+
.hb-row{display:flex;align-items:center;gap:8px;padding:4px 0;font-size:12px}
|
|
67
|
+
.pulse{width:7px;height:7px;border-radius:50%;animation:pulse 2s infinite}
|
|
68
|
+
.pulse.ok{background:var(--green);box-shadow:0 0 0 0 rgba(34,197,94,.7)} .pulse.err{background:var(--red);animation:none}
|
|
69
|
+
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(34,197,94,.7)}70%{box-shadow:0 0 0 6px rgba(34,197,94,0)}100%{box-shadow:0 0 0 0 rgba(34,197,94,0)}}
|
|
70
|
+
.scrollable{max-height:400px;overflow-y:auto}
|
|
71
|
+
.section{margin-bottom:0}
|
|
72
|
+
.ts{color:var(--muted);font-size:11px}
|
|
73
|
+
/* ── Overview two-panel layout ── */
|
|
74
|
+
.ov-layout{display:flex;height:calc(100vh - 88px);overflow:hidden}
|
|
75
|
+
.ov-panel{display:flex;flex-direction:column;overflow:hidden;flex:1;min-width:0}
|
|
76
|
+
.ov-right{width:300px;flex-shrink:0;display:flex;flex-direction:column;border-left:1px solid var(--border)}
|
|
77
|
+
.panel-hdr{background:var(--surface);padding:6px 12px;font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.8px;border-bottom:1px solid var(--border);flex-shrink:0;display:flex;align-items:center;justify-content:space-between}
|
|
78
|
+
.panel-scroll{flex:1;overflow-y:auto;min-height:0}
|
|
79
|
+
.panel-scroll::-webkit-scrollbar{width:5px}
|
|
80
|
+
.panel-scroll::-webkit-scrollbar-thumb{background:var(--border)}
|
|
81
|
+
.ov-stat-row{display:flex;justify-content:space-between;align-items:center;padding:5px 12px;border-bottom:1px solid var(--border);font-size:12px}
|
|
82
|
+
.ov-stat-row:last-child{border-bottom:none}
|
|
83
|
+
/* ── Overview panel content indent ── */
|
|
84
|
+
.ov-panel .panel-scroll{padding-left:10px}
|
|
85
|
+
/* ── Log-style activity feed ── */
|
|
86
|
+
.log-row{display:flex;align-items:baseline;gap:6px;padding:3px 0;border-bottom:1px solid rgba(37,40,57,.6);font-size:12px;font-family:var(--font);line-height:1.4}
|
|
87
|
+
.log-row:last-child{border-bottom:none}
|
|
88
|
+
.log-ts{color:var(--blue);flex-shrink:0;font-size:11px;min-width:60px}
|
|
89
|
+
.log-sub{flex-shrink:0;width:56px;text-align:right;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.3px;color:var(--muted)}
|
|
90
|
+
.log-msg{flex:1;color:var(--text);word-break:break-word;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
91
|
+
.log-sk{font-size:10px;color:var(--muted);flex-shrink:0;max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
92
|
+
.log-ok .log-sub{color:var(--green)} .log-ok .log-msg{color:var(--muted);font-size:11px}
|
|
93
|
+
.log-err .log-sub{color:var(--red)} .log-err .log-msg{color:var(--red)}
|
|
94
|
+
.log-warn .log-sub{color:var(--yellow)} .log-warn .log-msg{color:var(--yellow)}
|
|
95
|
+
.log-info .log-sub{color:var(--blue)} .log-info .log-msg{color:var(--text)}
|
|
96
|
+
.log-user .log-sub{color:#a78bfa} .log-user .log-msg{color:var(--text);font-style:italic}
|
|
97
|
+
.log-tool .log-sub{color:#a78bfa} .log-tool .log-msg{color:var(--text)}
|
|
98
|
+
.log-dim .log-msg,.log-dim .log-sub{color:var(--muted)}
|
|
99
|
+
/* ── Heartbeat debug row ── */
|
|
100
|
+
.hbd-row{display:flex;align-items:center;gap:8px;padding:3px 12px;border-bottom:1px solid rgba(37,40,57,.6);font-size:12px;font-family:var(--font)}
|
|
101
|
+
.hbd-row:last-child{border-bottom:none}
|
|
102
|
+
.hbd-ts{color:var(--blue);font-size:11px;flex-shrink:0;min-width:60px}
|
|
103
|
+
.hbd-body{flex:1;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
|
104
|
+
.hbd-ok{color:var(--green);font-weight:600} .hbd-err{color:var(--red);font-weight:600}
|
|
105
|
+
.hbd-kv{color:var(--muted);font-size:11px} .hbd-kv span{color:var(--text)}
|
|
106
|
+
.hbd-ago{color:var(--muted);font-size:10px;flex-shrink:0}
|
|
107
|
+
/* ── Gateway gate overlay ── */
|
|
108
|
+
#gw-gate{position:fixed;inset:0;z-index:100;background:var(--bg);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;transition:opacity .4s}
|
|
109
|
+
#gw-gate.hidden{opacity:0;pointer-events:none}
|
|
110
|
+
.gate-spinner{width:36px;height:36px;border:2px solid var(--border);border-top-color:var(--blue);border-radius:50%;animation:spin 1s linear infinite}
|
|
111
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
112
|
+
.gate-title{font-size:16px;font-weight:600;color:var(--text)}
|
|
113
|
+
.gate-sub{font-size:12px;color:var(--muted);text-align:center;max-width:340px;line-height:1.7}
|
|
114
|
+
.gate-url{font-family:var(--font);font-size:12px;color:var(--blue);background:var(--surface);padding:3px 10px;border:1px solid var(--border)}
|
|
115
|
+
.gate-dot{width:6px;height:6px;border-radius:50%;background:var(--yellow);animation:pulse 1.4s infinite;display:inline-block;margin-right:6px}
|
|
116
|
+
.gate-skip{margin-top:8px;font-size:12px;color:var(--muted);cursor:pointer;text-decoration:underline;opacity:0;transition:opacity .4s}
|
|
117
|
+
.gate-skip:hover{color:var(--text)}
|
|
118
|
+
.gate-skip.visible{opacity:1}
|
|
119
|
+
/* ── Live Monitor ── */
|
|
120
|
+
#tab-live{margin:0}
|
|
121
|
+
.live-layout{display:flex;height:calc(100vh - 88px);min-height:500px;overflow:hidden}
|
|
122
|
+
.live-sidebar{width:220px;flex-shrink:0;overflow-y:auto;border-right:1px solid var(--border);padding:8px 0;background:var(--surface);display:flex;flex-direction:column;gap:0}
|
|
123
|
+
.live-sidebar-hdr{font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.8px;padding:6px 12px 8px;flex-shrink:0;border-bottom:1px solid var(--border);margin-bottom:4px}
|
|
124
|
+
.live-main{flex:1;overflow-y:auto;padding:8px 0}
|
|
125
|
+
.live-sess-item{padding:6px 12px;cursor:pointer;display:flex;align-items:center;gap:8px;border-left:2px solid transparent;transition:background .1s}
|
|
126
|
+
.live-sess-item:hover{background:var(--surface2)}
|
|
127
|
+
.live-sess-item.lsel{background:var(--surface2);border-left-color:var(--blue)}
|
|
128
|
+
.live-sess-key{font-family:var(--font);font-size:11px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text)}
|
|
129
|
+
.live-sess-meta{font-size:10px;color:var(--muted);margin-top:1px}
|
|
130
|
+
.live-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;background:var(--muted)}
|
|
131
|
+
.live-dot.ok{background:var(--green);box-shadow:0 0 4px var(--green)}
|
|
132
|
+
.live-dot.think{background:var(--yellow);box-shadow:0 0 4px var(--yellow);animation:pulse 1.2s infinite}
|
|
133
|
+
.run-card{background:var(--surface);border:1px solid var(--border);margin-bottom:1px;overflow:hidden}
|
|
134
|
+
.run-header{padding:7px 12px;background:var(--surface2);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:6px;font-size:12px;flex-wrap:wrap}
|
|
135
|
+
.run-skey{font-family:var(--font);font-size:11px;color:var(--blue);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
136
|
+
.run-steps{padding:4px 12px 8px}
|
|
137
|
+
.step-item{display:flex;align-items:flex-start;gap:8px;padding:5px 0;font-size:12px;border-bottom:1px solid rgba(37,40,57,.6)}
|
|
138
|
+
.step-item:last-child{border-bottom:none}
|
|
139
|
+
.step-ic{width:16px;text-align:center;flex-shrink:0;font-size:12px;color:var(--muted);margin-top:2px}
|
|
140
|
+
.step-body{flex:1;min-width:0}
|
|
141
|
+
.step-lbl{font-weight:600;color:var(--text)}
|
|
142
|
+
.step-txt{color:var(--muted);font-size:11px;font-family:var(--font);margin-top:2px;white-space:pre-wrap;word-break:break-word;max-height:80px;overflow:hidden}
|
|
143
|
+
.step-ts{font-size:10px;color:var(--muted);flex-shrink:0;font-family:var(--font);margin-top:2px}
|
|
144
|
+
.s-user .step-lbl{color:var(--text)} .s-user .step-ic{color:#a78bfa}
|
|
145
|
+
.s-start .step-ic{color:var(--green)}
|
|
146
|
+
.s-done .step-lbl{color:var(--green)} .s-done .step-ic{color:var(--green)}
|
|
147
|
+
.s-err .step-lbl{color:var(--red)} .s-err .step-ic{color:var(--red)}
|
|
148
|
+
.s-tool .step-ic{color:var(--blue)} .s-tool .step-lbl{color:var(--blue)}
|
|
149
|
+
.s-stream .step-lbl{color:var(--muted)}
|
|
150
|
+
.live-empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--muted);font-size:12px;text-align:center;line-height:2;font-family:var(--font)}
|
|
151
|
+
`;
|
|
152
|
+
const BODY = `
|
|
153
|
+
<div id="gw-gate">
|
|
154
|
+
<div class="gate-spinner"></div>
|
|
155
|
+
<div class="gate-title">Connecting to OpenClaw…</div>
|
|
156
|
+
<div class="gate-sub">
|
|
157
|
+
<span class="gate-dot"></span>Waiting for the OpenClaw gateway at<br>
|
|
158
|
+
<span class="gate-url" id="gate-url">ws://127.0.0.1:18789</span>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="gate-sub" style="font-size:12px">Make sure OpenClaw is running: <code style="font-family:var(--font)">openclaw gateway run</code></div>
|
|
161
|
+
<div class="gate-skip" id="gate-skip" onclick="dismissGate()">Continue without gateway</div>
|
|
162
|
+
</div>
|
|
163
|
+
<header>
|
|
164
|
+
<h1><span class="dot dead" id="sDot"></span>OpenAlerts</h1>
|
|
165
|
+
<span class="hdr-stat" id="hdr-conn">connecting…</span>
|
|
166
|
+
<span class="hdr-stat">sessions: <b id="sess-val">--</b></span>
|
|
167
|
+
<span class="hdr-stat">queue: <b id="queue-val">0</b></span>
|
|
168
|
+
<span class="hdr-stat">tools: <b id="hdr-tools">0</b></span>
|
|
169
|
+
<span class="hdr-stat">err: <b id="hdr-errs">0</b></span>
|
|
170
|
+
<span id="conn-status" class="badge badge-muted" style="margin-left:auto">connecting…</span>
|
|
171
|
+
</header>
|
|
172
|
+
<nav>
|
|
173
|
+
<button class="active" onclick="showTab('overview',this)">Overview</button>
|
|
174
|
+
<button onclick="showTab('agents',this)">Workspaces</button>
|
|
175
|
+
<button onclick="showTab('alerts',this)">Alerts <span id="alert-count-badge"></span></button>
|
|
176
|
+
<button onclick="showTab('sessions',this)">Sessions</button>
|
|
177
|
+
<button onclick="showTab('live',this)">▶ Live Monitor</button>
|
|
178
|
+
<button onclick="showTab('cron',this)">Cron Jobs</button>
|
|
179
|
+
<button onclick="showTab('diagnostics',this)">Diagnostics</button>
|
|
180
|
+
<button onclick="showTab('delivery',this)">Delivery Queue</button>
|
|
181
|
+
</nav>
|
|
182
|
+
<main>
|
|
183
|
+
<div id="tab-overview" class="tab active">
|
|
184
|
+
<div class="ov-layout">
|
|
185
|
+
<div class="ov-panel">
|
|
186
|
+
<div class="panel-hdr"><span>Live Activity</span><span id="ov-ev-cnt" style="font-weight:400">0</span></div>
|
|
187
|
+
<div class="panel-scroll" id="live-diag"><div class="empty">Waiting for events…</div></div>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="ov-right">
|
|
190
|
+
<div class="panel-hdr">Recent Alerts</div>
|
|
191
|
+
<div class="panel-scroll" style="max-height:220px" id="overview-alerts"><div class="empty">No alerts</div></div>
|
|
192
|
+
<div class="panel-hdr" style="border-top:1px solid var(--border)">24h Stats</div>
|
|
193
|
+
<div class="ov-stat-row"><span>Agent runs</span><span class="stat-val" id="s-agent-starts">0</span></div>
|
|
194
|
+
<div class="ov-stat-row"><span>Tool calls</span><span class="stat-val" id="s-tool-calls">0</span></div>
|
|
195
|
+
<div class="ov-stat-row"><span>Errors</span><span class="stat-val" id="s-errors">0</span></div>
|
|
196
|
+
<div class="ov-stat-row"><span>Tokens</span><span class="stat-val" id="s-tokens">0</span></div>
|
|
197
|
+
<div class="ov-stat-row" style="border-bottom:none"><span>Cost (24h)</span><span class="stat-val" id="s-cost">$0.00</span></div>
|
|
198
|
+
<div class="panel-hdr" style="border-top:1px solid var(--border)">Gateway Health</div>
|
|
199
|
+
<div class="panel-scroll" id="hb-log"><div class="empty">No heartbeats yet</div></div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
<div id="tab-agents" class="tab"><div class="grid grid-2" id="agents-grid"><div class="empty">Loading…</div></div></div>
|
|
204
|
+
<div id="tab-alerts" class="tab"><div id="alerts-list"></div></div>
|
|
205
|
+
<div id="tab-sessions" class="tab"><div id="sessions-list"></div></div>
|
|
206
|
+
<div id="tab-live" class="tab">
|
|
207
|
+
<div class="live-layout">
|
|
208
|
+
<div class="live-sidebar">
|
|
209
|
+
<div class="live-sidebar-hdr">Sessions</div>
|
|
210
|
+
<div id="live-sess-list"></div>
|
|
211
|
+
</div>
|
|
212
|
+
<div class="live-main" id="live-runs">
|
|
213
|
+
<div class="live-empty">No activity yet — waiting for agent events…<br><span style="font-size:11px;color:var(--muted)">Events appear here as Claude runs tools and calls the LLM</span></div>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
<div id="tab-cron" class="tab"><div id="cron-list"></div></div>
|
|
218
|
+
<div id="tab-diagnostics" class="tab"><div id="diag-list"></div></div>
|
|
219
|
+
<div id="tab-delivery" class="tab"><div id="delivery-list"></div></div>
|
|
220
|
+
</main>
|
|
221
|
+
`;
|
|
222
|
+
// JS uses only single/double quotes — no backtick literals inside, safe to embed in template
|
|
223
|
+
const SCRIPT = `
|
|
224
|
+
/* ── Gateway gate ─────────────────────────────────────────────────────────── */
|
|
225
|
+
var gateOpen = true;
|
|
226
|
+
function dismissGate(connected){
|
|
227
|
+
if(!gateOpen) return;
|
|
228
|
+
gateOpen = false;
|
|
229
|
+
var g = document.getElementById('gw-gate');
|
|
230
|
+
if(g){ g.classList.add('hidden'); setTimeout(function(){ g.style.display='none'; }, 450); }
|
|
231
|
+
var dot=document.getElementById('sDot');
|
|
232
|
+
var hc=document.getElementById('hdr-conn');
|
|
233
|
+
if(connected){
|
|
234
|
+
if(dot) dot.className='dot live';
|
|
235
|
+
if(hc){hc.style.color='';hc.textContent='connected';}
|
|
236
|
+
} else {
|
|
237
|
+
if(dot) dot.className='dot warn';
|
|
238
|
+
if(hc){hc.style.color='var(--yellow)';hc.textContent='gateway offline';}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function checkGateway(){
|
|
242
|
+
fetch('/api/engine').then(function(r){ return r.json(); }).then(function(d){
|
|
243
|
+
if(d.gatewayConnected) dismissGate(true);
|
|
244
|
+
}).catch(function(){});
|
|
245
|
+
}
|
|
246
|
+
/* Check immediately on load, then every 3s until connected */
|
|
247
|
+
checkGateway();
|
|
248
|
+
var gateTimer = setInterval(function(){
|
|
249
|
+
if(!gateOpen){ clearInterval(gateTimer); return; }
|
|
250
|
+
checkGateway();
|
|
251
|
+
}, 3000);
|
|
252
|
+
/* Show skip button after 3s, auto-dismiss after 10s */
|
|
253
|
+
setTimeout(function(){
|
|
254
|
+
var sk=document.getElementById('gate-skip');
|
|
255
|
+
if(sk && gateOpen) sk.classList.add('visible');
|
|
256
|
+
}, 3000);
|
|
257
|
+
setTimeout(function(){
|
|
258
|
+
if(gateOpen) dismissGate();
|
|
259
|
+
}, 10000);
|
|
260
|
+
|
|
261
|
+
function showTab(name,btn){
|
|
262
|
+
document.querySelectorAll('.tab').forEach(function(t){t.classList.remove('active')});
|
|
263
|
+
document.querySelectorAll('nav button').forEach(function(b){b.classList.remove('active')});
|
|
264
|
+
document.getElementById('tab-'+name).classList.add('active');
|
|
265
|
+
if(btn) btn.classList.add('active');
|
|
266
|
+
if(name==='live') renderLiveTab();
|
|
267
|
+
}
|
|
268
|
+
function fmtTs(ms){
|
|
269
|
+
if(!ms) return '--';
|
|
270
|
+
return new Date(ms).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit'});
|
|
271
|
+
}
|
|
272
|
+
function fmtAgo(ms){
|
|
273
|
+
if(!ms) return '--';
|
|
274
|
+
var diff=Date.now()-ms;
|
|
275
|
+
if(diff<60000) return Math.round(diff/1000)+'s ago';
|
|
276
|
+
if(diff<3600000) return Math.round(diff/60000)+'m ago';
|
|
277
|
+
return Math.round(diff/3600000)+'h ago';
|
|
278
|
+
}
|
|
279
|
+
function fmtDur(ms){
|
|
280
|
+
if(!ms) return '';
|
|
281
|
+
if(ms<1000) return ms+'ms';
|
|
282
|
+
return (ms/1000).toFixed(1)+'s';
|
|
283
|
+
}
|
|
284
|
+
function esc(s){return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')}
|
|
285
|
+
function svr(sev){var m={critical:'badge-red',error:'badge-red',warn:'badge-yellow',warning:'badge-yellow',info:'badge-blue'};return '<span class="badge '+(m[sev]||'badge-muted')+'">'+esc(sev)+'</span>'}
|
|
286
|
+
var state={agents:[],cronJobs:[],sessions:[],recentAlerts:[],recentDiagnostics:[],recentHeartbeats:[],pendingDeliveries:[],recentActions:[]};
|
|
287
|
+
var engineState={running:false,stats:{}};
|
|
288
|
+
var liveActivityLog=[]; // mixed diagnostic + action events for overview feed, newest first
|
|
289
|
+
/* helpers for activity log */
|
|
290
|
+
function actSub(type){
|
|
291
|
+
if(!type) return 'sys';
|
|
292
|
+
var t=String(type);
|
|
293
|
+
if(t==='user_message') return 'user';
|
|
294
|
+
if(t==='tool_call'||t==='tool_result') return 'tool';
|
|
295
|
+
if(t==='start'||t==='complete'||t==='streaming') return 'agent';
|
|
296
|
+
if(t==='error'||t==='aborted') return 'agent';
|
|
297
|
+
if(t==='exec') return 'exec';
|
|
298
|
+
var prefix=t.split('.')[0];
|
|
299
|
+
if(prefix==='llm') return 'llm';
|
|
300
|
+
if(prefix==='tool') return 'tool';
|
|
301
|
+
if(prefix==='agent') return 'agent';
|
|
302
|
+
if(prefix==='infra') return 'infra';
|
|
303
|
+
if(prefix==='session') return 'sess';
|
|
304
|
+
return 'sys';
|
|
305
|
+
}
|
|
306
|
+
function actCls(type){
|
|
307
|
+
if(!type) return 'log-dim';
|
|
308
|
+
var t=String(type).toLowerCase();
|
|
309
|
+
if(t.indexOf('error')>=0||t.indexOf('fail')>=0) return 'log-err';
|
|
310
|
+
if(t.indexOf('warn')>=0) return 'log-warn';
|
|
311
|
+
if(t==='user_message') return 'log-user';
|
|
312
|
+
if(t==='tool_call'||t==='tool.call') return 'log-tool';
|
|
313
|
+
if(t==='tool_result') return 'log-dim';
|
|
314
|
+
if(t==='streaming') return 'log-dim';
|
|
315
|
+
if(t==='complete'||t.indexOf('agent.end')>=0) return 'log-ok';
|
|
316
|
+
if(t==='start'||t.indexOf('agent.start')>=0) return 'log-info';
|
|
317
|
+
if(t.indexOf('llm')>=0) return 'log-info';
|
|
318
|
+
if(t==='infra.heartbeat'||t==='infra.heartbeat:success') return 'log-dim';
|
|
319
|
+
return '';
|
|
320
|
+
}
|
|
321
|
+
function actMsg(type, summary, toolName, content){
|
|
322
|
+
/* diagnostic events have summary like "agent.start" or "tool.call" or "infra.heartbeat:success" */
|
|
323
|
+
if(summary){
|
|
324
|
+
var s=String(summary);
|
|
325
|
+
if(s==='infra.heartbeat:success'||s==='infra.heartbeat') return 'heartbeat ok';
|
|
326
|
+
if(s==='infra.heartbeat:error') return 'heartbeat FAIL';
|
|
327
|
+
var ci=s.indexOf(':');
|
|
328
|
+
if(ci>0){var after=s.substring(ci+1); if(after) return after;}
|
|
329
|
+
return s;
|
|
330
|
+
}
|
|
331
|
+
/* action events */
|
|
332
|
+
if(type==='user_message') return 'user message';
|
|
333
|
+
if(type==='start') return 'agent started';
|
|
334
|
+
if(type==='streaming') return 'thinking\u2026';
|
|
335
|
+
if(type==='tool_call') return (toolName?'tool: '+toolName:'tool call');
|
|
336
|
+
if(type==='tool_result') return 'tool result received';
|
|
337
|
+
if(type==='complete') return 'response complete'+(content?' \u2014 '+content.substring(0,50):'');
|
|
338
|
+
if(type==='error') return 'error'+(content?' \u2014 '+content.substring(0,60):'');
|
|
339
|
+
if(type==='aborted') return 'aborted';
|
|
340
|
+
if(type==='exec') return 'exec'+(toolName?' '+toolName:'');
|
|
341
|
+
return type||'event';
|
|
342
|
+
}
|
|
343
|
+
function pushActivity(entry){
|
|
344
|
+
liveActivityLog.unshift(entry);
|
|
345
|
+
if(liveActivityLog.length>80) liveActivityLog.pop();
|
|
346
|
+
var cnt=document.getElementById('ov-ev-cnt');
|
|
347
|
+
if(cnt) cnt.textContent=String(liveActivityLog.length);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* ── Live Monitor state ────────────────────────────────────────────────────── */
|
|
351
|
+
var liveRuns={}; // runId -> run object
|
|
352
|
+
var runOrder=[]; // runIds newest-first
|
|
353
|
+
var filterKey=null; // currently selected sessionKey (null = all)
|
|
354
|
+
var liveSessions={}; // sessionKey -> {status, lastActivity, agentId}
|
|
355
|
+
|
|
356
|
+
function shortKey(k){
|
|
357
|
+
if(!k) return '?';
|
|
358
|
+
var parts=String(k).split(':');
|
|
359
|
+
var id=parts[parts.length-1]||k;
|
|
360
|
+
return id.length>14 ? id.substring(0,14)+'\u2026' : id;
|
|
361
|
+
}
|
|
362
|
+
function safeKey(k){ return String(k||'').replace(/['"]/g,''); }
|
|
363
|
+
|
|
364
|
+
function stepCls(type){
|
|
365
|
+
if(type==='user_message') return 's-user';
|
|
366
|
+
if(type==='complete') return 's-done';
|
|
367
|
+
if(type==='error'||type==='aborted') return 's-err';
|
|
368
|
+
if(type==='tool_call') return 's-tool';
|
|
369
|
+
if(type==='tool_result') return 's-tool';
|
|
370
|
+
if(type==='streaming') return 's-stream';
|
|
371
|
+
if(type==='start') return 's-start';
|
|
372
|
+
return '';
|
|
373
|
+
}
|
|
374
|
+
function stepIc(type){
|
|
375
|
+
if(type==='user_message') return '\u{1F4AC}';
|
|
376
|
+
if(type==='start') return '\u25b6';
|
|
377
|
+
if(type==='streaming') return '\u22ef';
|
|
378
|
+
if(type==='tool_call') return '\u26a1';
|
|
379
|
+
if(type==='tool_result') return '\u21a9';
|
|
380
|
+
if(type==='complete') return '\u2713';
|
|
381
|
+
if(type==='error') return '\u2717';
|
|
382
|
+
if(type==='aborted') return '\u2298';
|
|
383
|
+
if(type==='exec') return '$';
|
|
384
|
+
return '\u2022';
|
|
385
|
+
}
|
|
386
|
+
function stepLbl(type,eventType,toolName,platform){
|
|
387
|
+
if(type==='user_message'){
|
|
388
|
+
var src=platform?(' <span class="badge badge-muted" style="font-size:9px">'+esc(platform)+'</span>'):'';
|
|
389
|
+
return 'User message'+src;
|
|
390
|
+
}
|
|
391
|
+
if(type==='start') return 'Agent start';
|
|
392
|
+
if(type==='streaming') return 'Thinking\u2026';
|
|
393
|
+
if(type==='tool_call') return 'Tool: '+(toolName||eventType||'?');
|
|
394
|
+
if(type==='tool_result') return 'Tool result';
|
|
395
|
+
if(type==='complete'){
|
|
396
|
+
return eventType==='exec' ? 'Exec done' : 'Response complete';
|
|
397
|
+
}
|
|
398
|
+
if(type==='error') return 'Error';
|
|
399
|
+
if(type==='aborted') return 'Aborted';
|
|
400
|
+
if(type==='exec') return 'Exec: '+(toolName||'?');
|
|
401
|
+
return type;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function handleLiveAction(a){
|
|
405
|
+
var runId=a.runId;
|
|
406
|
+
if(!runId) return;
|
|
407
|
+
var sessionKey=a.sessionKey||'';
|
|
408
|
+
|
|
409
|
+
// Update session tracker
|
|
410
|
+
if(sessionKey){
|
|
411
|
+
if(!liveSessions[sessionKey]) liveSessions[sessionKey]={status:'active',lastActivity:a.ts,agentId:''};
|
|
412
|
+
liveSessions[sessionKey].lastActivity=a.ts;
|
|
413
|
+
if(a.type==='start'||a.type==='streaming') liveSessions[sessionKey].status='thinking';
|
|
414
|
+
else if(a.type==='complete'||a.type==='error'||a.type==='aborted') liveSessions[sessionKey].status='active';
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Create run if new
|
|
418
|
+
if(!liveRuns[runId]){
|
|
419
|
+
liveRuns[runId]={runId:runId,sessionKey:sessionKey,steps:[],startTs:a.ts,endTs:null,toolCount:0,tokIn:0,tokOut:0};
|
|
420
|
+
runOrder.unshift(runId);
|
|
421
|
+
if(runOrder.length>30){ var old=runOrder.pop(); delete liveRuns[old]; }
|
|
422
|
+
}
|
|
423
|
+
var run=liveRuns[runId];
|
|
424
|
+
if(!run.sessionKey && sessionKey) run.sessionKey=sessionKey;
|
|
425
|
+
|
|
426
|
+
// Coalesce consecutive streaming steps
|
|
427
|
+
if(a.type==='streaming'){
|
|
428
|
+
var last=run.steps[run.steps.length-1];
|
|
429
|
+
if(last && last.type==='streaming'){
|
|
430
|
+
if(a.content) last.content=(last.content||'')+a.content;
|
|
431
|
+
last.ts=a.ts;
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if(a.type==='tool_call') run.toolCount++;
|
|
437
|
+
if(a.inputTokens) run.tokIn+=a.inputTokens;
|
|
438
|
+
if(a.outputTokens) run.tokOut+=a.outputTokens;
|
|
439
|
+
if(a.type==='complete'||a.type==='error'||a.type==='aborted') run.endTs=a.ts;
|
|
440
|
+
|
|
441
|
+
run.steps.push({
|
|
442
|
+
type:a.type, eventType:a.eventType||'', toolName:a.toolName||'',
|
|
443
|
+
content:a.content||'', ts:a.ts, durationMs:a.durationMs||0,
|
|
444
|
+
inputTokens:a.inputTokens||0, outputTokens:a.outputTokens||0,
|
|
445
|
+
platform:a.platform||''
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function handleLiveHealth(h){
|
|
450
|
+
var sessions=Array.isArray(h.sessions)?h.sessions:[];
|
|
451
|
+
var now=h.ts||Date.now();
|
|
452
|
+
sessions.forEach(function(s){
|
|
453
|
+
var k=s.key||s.sessionKey;
|
|
454
|
+
if(!k) return;
|
|
455
|
+
if(!liveSessions[k]) liveSessions[k]={status:s.status||'active',lastActivity:s.lastActivityAt||now,agentId:s.agentId||''};
|
|
456
|
+
else{
|
|
457
|
+
if(s.lastActivityAt) liveSessions[k].lastActivity=s.lastActivityAt;
|
|
458
|
+
if(s.agentId) liveSessions[k].agentId=s.agentId;
|
|
459
|
+
if(s.status) liveSessions[k].status=s.status;
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
// Also update gateway status indicator on overview tab
|
|
463
|
+
var gwEl=document.getElementById('gw-val');
|
|
464
|
+
if(gwEl) gwEl.textContent='connected';
|
|
465
|
+
var sessEl=document.getElementById('sess-val');
|
|
466
|
+
if(sessEl) sessEl.textContent=String(h.activeSessions||sessions.length||0);
|
|
467
|
+
var qEl=document.getElementById('queue-val');
|
|
468
|
+
if(qEl) qEl.textContent=String(h.queueDepth||0);
|
|
469
|
+
var hbEl=document.getElementById('hb-val');
|
|
470
|
+
if(hbEl) hbEl.textContent=fmtTs(now);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function handleLiveExec(ex){
|
|
474
|
+
var runId=ex.runId;
|
|
475
|
+
if(!runId) return;
|
|
476
|
+
if(!liveRuns[runId]){
|
|
477
|
+
liveRuns[runId]={runId:runId,sessionKey:ex.sessionId||'',steps:[],startTs:ex.ts||Date.now(),endTs:null,toolCount:0,tokIn:0,tokOut:0};
|
|
478
|
+
runOrder.unshift(runId);
|
|
479
|
+
if(runOrder.length>30){ var o2=runOrder.pop(); delete liveRuns[o2]; }
|
|
480
|
+
}
|
|
481
|
+
var r2=liveRuns[runId];
|
|
482
|
+
if(ex.type==='started'){
|
|
483
|
+
r2.steps.push({type:'exec',eventType:'exec',toolName:(ex.command||'').substring(0,40),content:'pid '+ex.pid,ts:ex.ts||Date.now(),durationMs:0,inputTokens:0,outputTokens:0});
|
|
484
|
+
} else if(ex.type==='completed'){
|
|
485
|
+
var ok=ex.exitCode===0;
|
|
486
|
+
r2.steps.push({type:ok?'complete':'error',eventType:'exec',toolName:'exec',content:'exit '+ex.exitCode+(ex.durationMs?' ('+fmtDur(ex.durationMs)+')':''),ts:ex.ts||Date.now(),durationMs:ex.durationMs||0,inputTokens:0,outputTokens:0});
|
|
487
|
+
if(!ok) r2.endTs=ex.ts||Date.now();
|
|
488
|
+
} else if(ex.type==='output' && ex.output){
|
|
489
|
+
// Coalesce exec output into last exec step
|
|
490
|
+
var ls=r2.steps[r2.steps.length-1];
|
|
491
|
+
if(ls && ls.type==='exec') ls.content=(ls.content||'')+'\\n'+(ex.output||'').substring(0,100);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function renderRunCard(runId){
|
|
496
|
+
var run=liveRuns[runId];
|
|
497
|
+
if(!run) return '';
|
|
498
|
+
if(filterKey && run.sessionKey !== filterKey) return '';
|
|
499
|
+
|
|
500
|
+
var dur=run.endTs ? fmtDur(run.endTs-run.startTs) : '';
|
|
501
|
+
var toks=run.tokIn+run.tokOut;
|
|
502
|
+
|
|
503
|
+
var hdr='<div class="run-header">'
|
|
504
|
+
+'<span class="run-skey">'+esc(shortKey(run.sessionKey))+'</span>'
|
|
505
|
+
+(run.toolCount?'<span class="badge badge-blue">'+run.toolCount+' tools</span>':'')
|
|
506
|
+
+(toks?'<span class="badge badge-muted">'+toks+' tok</span>':'')
|
|
507
|
+
+'<span class="badge badge-muted">'+fmtTs(run.startTs)+'</span>'
|
|
508
|
+
+(run.endTs?'<span class="badge badge-muted">'+dur+'</span>':'<span class="badge badge-green">live</span>')
|
|
509
|
+
+'</div>';
|
|
510
|
+
|
|
511
|
+
var steps=run.steps.map(function(s){
|
|
512
|
+
var cls=stepCls(s.type);
|
|
513
|
+
var txt=s.content?s.content.substring(0,250):'';
|
|
514
|
+
return '<div class="step-item '+cls+'">'
|
|
515
|
+
+'<div class="step-ic">'+stepIc(s.type)+'</div>'
|
|
516
|
+
+'<div class="step-body">'
|
|
517
|
+
+'<div class="step-lbl">'+stepLbl(s.type,s.eventType,s.toolName,s.platform)
|
|
518
|
+
+(s.durationMs?' <span style="color:var(--muted);font-weight:400">('+fmtDur(s.durationMs)+')</span>':'')
|
|
519
|
+
+(s.inputTokens?' <span style="color:var(--muted);font-weight:400;font-size:10px">'+s.inputTokens+'+'+s.outputTokens+' tok</span>':'')
|
|
520
|
+
+'</div>'
|
|
521
|
+
+(txt?'<div class="step-txt">'+esc(txt)+'</div>':'')
|
|
522
|
+
+'</div>'
|
|
523
|
+
+'<div class="step-ts">'+fmtTs(s.ts)+'</div>'
|
|
524
|
+
+'</div>';
|
|
525
|
+
}).join('');
|
|
526
|
+
|
|
527
|
+
return '<div class="run-card">'+hdr+'<div class="run-steps">'+(steps||'<div class="empty" style="padding:8px">Starting\u2026</div>')+'</div></div>';
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function renderLiveSessions(){
|
|
531
|
+
var el=document.getElementById('live-sess-list');
|
|
532
|
+
if(!el) return;
|
|
533
|
+
var keys=Object.keys(liveSessions).sort(function(a,b){return (liveSessions[b].lastActivity||0)-(liveSessions[a].lastActivity||0);});
|
|
534
|
+
var html='<div class="live-sess-item '+(filterKey?'':'lsel')+'" onclick="filterLive(null)">'
|
|
535
|
+
+'<div class="live-dot ok"></div>'
|
|
536
|
+
+'<div><div class="live-sess-key">All sessions</div>'
|
|
537
|
+
+'<div class="live-sess-meta">'+runOrder.length+' runs buffered</div></div>'
|
|
538
|
+
+'</div>';
|
|
539
|
+
html+=keys.map(function(k){
|
|
540
|
+
var s=liveSessions[k];
|
|
541
|
+
var dotCls=s.status==='thinking'?'think':(s.status==='active'?'ok':'');
|
|
542
|
+
var sk=safeKey(k);
|
|
543
|
+
return '<div class="live-sess-item '+(filterKey===k?'lsel':'')+'" onclick="filterLive(\\''+sk+'\\')">'+
|
|
544
|
+
'<div class="live-dot '+dotCls+'"></div>'
|
|
545
|
+
+'<div style="flex:1;min-width:0">'
|
|
546
|
+
+'<div class="live-sess-key">'+esc(shortKey(k))+'</div>'
|
|
547
|
+
+'<div class="live-sess-meta">'+(s.status==='thinking'?'\u22ef thinking':'')+(s.status==='active'?fmtAgo(s.lastActivity):'')+'</div>'
|
|
548
|
+
+'</div>'
|
|
549
|
+
+'</div>';
|
|
550
|
+
}).join('');
|
|
551
|
+
el.innerHTML=html;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function renderLiveTab(){
|
|
555
|
+
renderLiveSessions();
|
|
556
|
+
var el=document.getElementById('live-runs');
|
|
557
|
+
if(!el) return;
|
|
558
|
+
var cards=runOrder.map(renderRunCard).filter(function(c){return !!c;});
|
|
559
|
+
if(!cards.length){
|
|
560
|
+
el.innerHTML='<div class="live-empty">No activity yet — waiting for agent events…<br><span style="font-size:11px;color:var(--muted)">Events appear here as Claude runs tools and calls the LLM</span></div>';
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
el.innerHTML=cards.join('');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function filterLive(key){
|
|
567
|
+
filterKey=key;
|
|
568
|
+
renderLiveTab();
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/* ── Standard render functions ─────────────────────────────────────────────── */
|
|
572
|
+
function renderOverview(){
|
|
573
|
+
var al=state.recentAlerts||[];
|
|
574
|
+
var cb=document.getElementById('alert-count-badge');
|
|
575
|
+
if(cb) cb.innerHTML=al.length?'<span class="badge badge-red">'+al.length+'</span>':'';
|
|
576
|
+
var oa=document.getElementById('overview-alerts');
|
|
577
|
+
if(oa){
|
|
578
|
+
if(!al.length){oa.innerHTML='<div class="empty">No alerts</div>';}
|
|
579
|
+
else{oa.innerHTML=al.slice(0,5).map(function(a){return '<div class="alert-item '+esc(a.severity)+'"><div class="alert-title">'+esc(a.title)+'</div><div class="alert-meta">'+esc(a.rule_id)+' · '+fmtTs(a.ts)+'</div></div>';}).join('');}
|
|
580
|
+
}
|
|
581
|
+
var hbs=state.recentHeartbeats||[];
|
|
582
|
+
var hbEl=document.getElementById('hb-log');
|
|
583
|
+
if(hbEl){
|
|
584
|
+
if(!hbs.length){hbEl.innerHTML='<div class="empty">No heartbeats yet</div>';}
|
|
585
|
+
else{hbEl.innerHTML=hbs.slice(0,30).map(function(h){
|
|
586
|
+
var ok=!!h.gateway_connected;
|
|
587
|
+
return '<div class="hbd-row">'
|
|
588
|
+
+'<span class="hbd-ts">'+fmtTs(h.ts)+'</span>'
|
|
589
|
+
+'<div class="pulse '+(ok?'ok':'err')+'" style="flex-shrink:0"></div>'
|
|
590
|
+
+'<div class="hbd-body">'
|
|
591
|
+
+(ok?'<span class="hbd-ok">connected</span>':'<span class="hbd-err">disconnected</span>')
|
|
592
|
+
+(h.active_sessions!=null?'<span class="hbd-kv">sessions=<span>'+h.active_sessions+'</span></span>':'')
|
|
593
|
+
+'<span class="hbd-kv">queue=<span'+(h.queue_depth?'>'+h.queue_depth+' ▶':' style="color:var(--muted)">0')+'</span></span>'
|
|
594
|
+
+'</div>'
|
|
595
|
+
+'<span class="hbd-ago">'+fmtAgo(h.ts)+'</span>'
|
|
596
|
+
+'</div>';
|
|
597
|
+
}).join('');}
|
|
598
|
+
}
|
|
599
|
+
/* live activity log — mix of diagnostic + action events */
|
|
600
|
+
var ld=document.getElementById('live-diag');
|
|
601
|
+
if(ld){
|
|
602
|
+
/* seed from state.recentDiagnostics on first state push if liveActivityLog is empty */
|
|
603
|
+
var src = liveActivityLog.length ? liveActivityLog : (state.recentDiagnostics||[]).map(function(d){
|
|
604
|
+
return {ts:d.ts,sub:actSub(d.event_type),cls:actCls(d.event_type||d.summary||''),msg:actMsg(null,d.summary||d.event_type,null,null),sk:d.session_key||''};
|
|
605
|
+
});
|
|
606
|
+
if(!src.length){ld.innerHTML='<div class="empty">No events yet</div>';}
|
|
607
|
+
else{ld.innerHTML=src.slice(0,40).map(function(e){
|
|
608
|
+
return '<div class="log-row '+esc(e.cls)+'">'
|
|
609
|
+
+'<span class="log-ts">'+fmtTs(e.ts)+'</span>'
|
|
610
|
+
+'<span class="log-sub">'+esc(e.sub)+'</span>'
|
|
611
|
+
+'<span class="log-msg">'+esc(e.msg)+'</span>'
|
|
612
|
+
+(e.sk?'<span class="log-sk">'+esc(shortKey(e.sk))+'</span>':'')
|
|
613
|
+
+'</div>';
|
|
614
|
+
}).join('');}
|
|
615
|
+
}
|
|
616
|
+
var s=engineState.stats||{};
|
|
617
|
+
function set(id,v){var e=document.getElementById(id);if(e)e.textContent=v;}
|
|
618
|
+
set('s-agent-starts',s.agentStarts||0);
|
|
619
|
+
set('s-tool-calls',s.toolCalls||0);
|
|
620
|
+
set('s-errors',(s.agentErrors||0)+(s.toolErrors||0));
|
|
621
|
+
set('s-tokens',(s.totalTokens||0).toLocaleString());
|
|
622
|
+
set('s-cost','$'+(s.totalCostUsd||0).toFixed(4));
|
|
623
|
+
set('hdr-tools',s.toolCalls||0);
|
|
624
|
+
set('hdr-errs',(s.agentErrors||0)+(s.toolErrors||0));
|
|
625
|
+
}
|
|
626
|
+
function renderAgents(){
|
|
627
|
+
var agents=state.agents||[];
|
|
628
|
+
var grid=document.getElementById('agents-grid');
|
|
629
|
+
if(!grid) return;
|
|
630
|
+
if(!agents.length){grid.innerHTML='<div class="empty">No agents found</div>';return;}
|
|
631
|
+
grid.innerHTML=agents.map(function(a){
|
|
632
|
+
var docs='';
|
|
633
|
+
var pairs=[['SOUL',a.soul_md],['HEARTBEAT',a.heartbeat_md],['MEMORY',a.memory_md],['USER',a.user_md]];
|
|
634
|
+
pairs.forEach(function(p){
|
|
635
|
+
if(p[1]) docs+='<div style="margin-bottom:8px"><div style="font-size:11px;font-weight:600;color:var(--muted);margin-bottom:4px">'+p[0]+'</div><div class="doc-preview">'+esc(p[1]).substring(0,600)+(p[1].length>600?'…':'')+'</div></div>';
|
|
636
|
+
});
|
|
637
|
+
if(!docs) docs='<div class="empty">No docs loaded</div>';
|
|
638
|
+
return '<div class="card"><div class="agent-card"><span style="font-size:24px;margin-right:8px">'+esc(a.emoji||'X')+'</span><span class="agent-name">'+esc(a.name||a.agent_id)+'</span><span class="badge badge-muted" style="margin-left:8px">'+esc(a.agent_id)+'</span></div><div style="margin-top:12px">'+docs+'</div></div>';
|
|
639
|
+
}).join('');
|
|
640
|
+
}
|
|
641
|
+
function renderAlerts(){
|
|
642
|
+
var al=state.recentAlerts||[];
|
|
643
|
+
var el=document.getElementById('alerts-list');
|
|
644
|
+
if(!el) return;
|
|
645
|
+
if(!al.length){el.innerHTML='<div class="empty">No alerts recorded</div>';return;}
|
|
646
|
+
el.innerHTML=al.map(function(a){return '<div class="alert-item '+esc(a.severity)+'"><div class="alert-title">'+svr(a.severity)+' '+esc(a.title)+'</div><div class="alert-detail">'+esc(a.detail||'')+'</div><div class="alert-meta">'+esc(a.rule_id)+' · '+esc(a.fingerprint)+' · '+fmtTs(a.ts)+'</div></div>';}).join('');
|
|
647
|
+
}
|
|
648
|
+
function renderSessions(){
|
|
649
|
+
var ss=state.sessions||[];
|
|
650
|
+
var el=document.getElementById('sessions-list');
|
|
651
|
+
if(!el) return;
|
|
652
|
+
if(!ss.length){el.innerHTML='<div class="empty">No sessions</div>';return;}
|
|
653
|
+
el.innerHTML=ss.map(function(s){return '<div class="session-item"><div class="session-dot '+(s.status==='active'?'active':'idle')+'"></div><span class="session-key">'+esc(s.session_key)+'</span>'+(s.agent_id?'<span class="badge badge-blue">'+esc(s.agent_id)+'</span>':'')+(s.recipient?'<span class="badge badge-muted">'+esc(s.recipient)+'</span>':'')+(s.total_cost_usd?'<span class="badge badge-yellow">$'+Number(s.total_cost_usd).toFixed(4)+'</span>':'')+'<span class="ts">'+fmtAgo(s.last_activity_at)+'</span></div>';}).join('');
|
|
654
|
+
}
|
|
655
|
+
function renderCron(){
|
|
656
|
+
var jobs=state.cronJobs||[];
|
|
657
|
+
var el=document.getElementById('cron-list');
|
|
658
|
+
if(!el) return;
|
|
659
|
+
if(!jobs.length){el.innerHTML='<div class="card"><div class="empty">No cron jobs</div></div>';return;}
|
|
660
|
+
el.innerHTML=jobs.map(function(j){return '<div class="cron-item '+(j.last_status==='error'?'error':(j.last_status==='success'?'success':''))+'"><div class="cron-name">'+esc(j.name||j.id)+'</div>'+(j.description?'<div class="cron-meta">'+esc(j.description.substring(0,80))+'</div>':'')+'<div class="cron-meta">'+(j.schedule_expr?esc(j.schedule_expr)+' '+(j.schedule_tz||''):'')+' · agent: '+esc(j.agent_id||'?')+' · status: <strong style="color:'+(j.last_status==='error'?'var(--red)':'var(--green)')+'">'+esc(j.last_status||'unknown')+'</strong>'+(j.consecutive_errors?' · <span style="color:var(--red)">'+j.consecutive_errors+' errors</span>':'')+'</div><div class="cron-meta">Last: '+fmtAgo(j.last_run_at)+' · Next: '+fmtTs(j.next_run_at)+'</div>'+(j.last_error?'<div class="cron-error">'+esc(j.last_error.substring(0,200))+'</div>':'')+'</div>';}).join('');
|
|
661
|
+
}
|
|
662
|
+
function renderDiagnostics(){
|
|
663
|
+
var diags=state.recentDiagnostics||[];
|
|
664
|
+
var el=document.getElementById('diag-list');
|
|
665
|
+
if(!el) return;
|
|
666
|
+
if(!diags.length){el.innerHTML='<div class="empty">No diagnostics yet</div>';return;}
|
|
667
|
+
el.innerHTML=diags.map(function(d){return '<div class="diag-item"><span class="diag-ts">'+fmtTs(d.ts)+'</span><span class="diag-type">'+esc(d.event_type)+'</span>'+(d.channel?'<span style="color:#94a3b8;margin-left:6px">@'+esc(d.channel)+'</span>':'')+(d.session_key?'<span style="color:#64748b;margin-left:6px">'+esc(d.session_key)+'</span>':'')+'</div>';}).join('');
|
|
668
|
+
}
|
|
669
|
+
function renderDelivery(){
|
|
670
|
+
var items=state.pendingDeliveries||[];
|
|
671
|
+
var el=document.getElementById('delivery-list');
|
|
672
|
+
if(!el) return;
|
|
673
|
+
if(!items.length){el.innerHTML='<div class="empty">Queue is empty</div>';return;}
|
|
674
|
+
el.innerHTML=items.map(function(d){return '<div class="delivery-item '+(d.status||'pending')+'"><strong>'+esc(d.channel||'?')+'</strong> → '+esc(d.to_address||'?')+(d.retry_count?'<span class="badge badge-yellow">retry '+d.retry_count+'</span>':'')+'<div style="font-size:11px;color:var(--muted);margin-top:2px">'+esc((d.text||'').substring(0,100))+'</div>'+(d.last_error?'<div style="font-size:11px;color:var(--red);margin-top:2px">'+esc(d.last_error.substring(0,100))+'</div>':'')+'</div>';}).join('');
|
|
675
|
+
}
|
|
676
|
+
function renderAll(){renderOverview();renderAgents();renderAlerts();renderSessions();renderCron();renderDiagnostics();renderDelivery();}
|
|
677
|
+
function pollEngine(){
|
|
678
|
+
fetch('/api/engine').then(function(r){return r.json();}).then(function(d){engineState=d;renderOverview();}).catch(function(){});
|
|
679
|
+
}
|
|
680
|
+
setInterval(pollEngine,30000);
|
|
681
|
+
pollEngine();
|
|
682
|
+
|
|
683
|
+
/* ── SSE connection ─────────────────────────────────────────────────────────── */
|
|
684
|
+
function connect(){
|
|
685
|
+
var es=new EventSource('/events');
|
|
686
|
+
var conn=document.getElementById('conn-status');
|
|
687
|
+
es.addEventListener('state',function(e){
|
|
688
|
+
state=JSON.parse(e.data);renderAll();
|
|
689
|
+
if(conn){conn.className='badge badge-green';conn.textContent='live';}
|
|
690
|
+
});
|
|
691
|
+
es.addEventListener('openalerts',function(e){
|
|
692
|
+
var a=JSON.parse(e.data);
|
|
693
|
+
state.recentAlerts=[a].concat(state.recentAlerts).slice(0,50);
|
|
694
|
+
renderAlerts();renderOverview();
|
|
695
|
+
var btn=document.querySelectorAll('nav button')[2];
|
|
696
|
+
if(btn){btn.style.background='rgba(239,68,68,.2)';setTimeout(function(){btn.style.background='';},2000);}
|
|
697
|
+
});
|
|
698
|
+
es.addEventListener('diagnostic',function(e){
|
|
699
|
+
var d=JSON.parse(e.data);
|
|
700
|
+
state.recentDiagnostics=[d].concat(state.recentDiagnostics).slice(0,100);
|
|
701
|
+
pushActivity({ts:d.ts,sub:actSub(d.event_type),cls:actCls(d.event_type||d.summary||''),msg:actMsg(null,d.summary||d.event_type,null,null),sk:d.session_key||''});
|
|
702
|
+
renderDiagnostics();renderOverview();
|
|
703
|
+
});
|
|
704
|
+
es.addEventListener('action',function(e){
|
|
705
|
+
var a=JSON.parse(e.data);
|
|
706
|
+
handleLiveAction(a);
|
|
707
|
+
/* Skip streaming steps in overview feed to avoid noise — only meaningful events */
|
|
708
|
+
if(a.type!=='streaming'&&a.type!=='tool_result'){
|
|
709
|
+
pushActivity({ts:a.ts,sub:actSub(a.type),cls:actCls(a.type),msg:actMsg(a.type,null,a.toolName,a.content),sk:a.sessionKey||''});
|
|
710
|
+
var ovTab=document.getElementById('tab-overview');
|
|
711
|
+
if(ovTab&&ovTab.classList.contains('active')) renderOverview();
|
|
712
|
+
}
|
|
713
|
+
/* Only re-render live tab if visible */
|
|
714
|
+
var liveTab=document.getElementById('tab-live');
|
|
715
|
+
if(liveTab&&liveTab.classList.contains('active')) renderLiveTab();
|
|
716
|
+
});
|
|
717
|
+
es.addEventListener('health',function(e){
|
|
718
|
+
var h=JSON.parse(e.data);
|
|
719
|
+
dismissGate(true);
|
|
720
|
+
handleLiveHealth(h);
|
|
721
|
+
/* add a heartbeat row to the activity feed & heartbeat log */
|
|
722
|
+
var hb={ts:h.ts||Date.now(),status:'ok',gateway_connected:1,queue_depth:h.queueDepth||0,active_sessions:h.activeSessions||0};
|
|
723
|
+
state.recentHeartbeats=[hb].concat(state.recentHeartbeats).slice(0,50);
|
|
724
|
+
pushActivity({ts:hb.ts,sub:'infra',cls:'log-dim',msg:'heartbeat ok sessions='+hb.active_sessions+' queue='+hb.queue_depth,sk:''});
|
|
725
|
+
var ovTab=document.getElementById('tab-overview');
|
|
726
|
+
if(ovTab&&ovTab.classList.contains('active')) renderOverview();
|
|
727
|
+
var liveTab=document.getElementById('tab-live');
|
|
728
|
+
if(liveTab&&liveTab.classList.contains('active')) renderLiveSessions();
|
|
729
|
+
});
|
|
730
|
+
es.addEventListener('exec',function(e){
|
|
731
|
+
var ex=JSON.parse(e.data);
|
|
732
|
+
handleLiveExec(ex);
|
|
733
|
+
var liveTab=document.getElementById('tab-live');
|
|
734
|
+
if(liveTab && liveTab.classList.contains('active')) renderLiveTab();
|
|
735
|
+
});
|
|
736
|
+
es.onopen=function(){
|
|
737
|
+
if(conn){conn.className='badge badge-green';conn.textContent='connected';}
|
|
738
|
+
var dot=document.getElementById('sDot');if(dot) dot.className='dot live';
|
|
739
|
+
var hc=document.getElementById('hdr-conn');if(hc){hc.style.color='';hc.textContent='connected';}
|
|
740
|
+
};
|
|
741
|
+
es.onerror=function(){
|
|
742
|
+
if(conn){conn.className='badge badge-red';conn.textContent='disconnected';}
|
|
743
|
+
var dot=document.getElementById('sDot');if(dot) dot.className='dot dead';
|
|
744
|
+
var hc=document.getElementById('hdr-conn');if(hc){hc.style.color='var(--red)';hc.textContent='disconnected';}
|
|
745
|
+
setTimeout(connect,3000);es.close();
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
connect();
|
|
749
|
+
`;
|
|
750
|
+
export function getDashboardHtml() {
|
|
751
|
+
return `<!DOCTYPE html>
|
|
752
|
+
<html lang="en">
|
|
753
|
+
<head>
|
|
754
|
+
<meta charset="UTF-8">
|
|
755
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
756
|
+
<title>OpenAlerts</title>
|
|
757
|
+
<style>${CSS}</style>
|
|
758
|
+
</head>
|
|
759
|
+
<body>
|
|
760
|
+
${BODY}
|
|
761
|
+
<script>${SCRIPT}<\/script>
|
|
762
|
+
</body>
|
|
763
|
+
</html>`;
|
|
764
|
+
}
|
|
765
|
+
//# sourceMappingURL=dashboard.js.map
|