@krishivpb60/aether-ai-cli 1.3.4 → 1.3.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/HIGHLIGHTS.md +13 -0
- package/package.json +1 -1
- package/src/ai/telemetry.js +56 -3
- package/src/chat.js +2184 -1692
- package/src/cli.js +31 -0
- package/src/dashboard.js +112 -0
- package/src/telemetry-server.js +855 -0
- package/src/ui/dashboard.html +834 -0
- package/test/autopilot-debug.test.js +91 -0
- package/test/git-tui.test.js +94 -0
- package/test/telemetry.test.js +104 -0
|
@@ -0,0 +1,834 @@
|
|
|
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>Aether CLI // Telemetry Dashboard</title>
|
|
7
|
+
<!-- Cyberpunk fonts -->
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=Orbitron:wght@500;700;900&family=Share+Tech+Mono&display=swap" rel="stylesheet">
|
|
11
|
+
|
|
12
|
+
<style>
|
|
13
|
+
:root {
|
|
14
|
+
--bg-dark: #050811;
|
|
15
|
+
--bg-card: rgba(13, 24, 41, 0.85);
|
|
16
|
+
--bg-card-hover: rgba(20, 35, 60, 0.95);
|
|
17
|
+
--neon-cyan: #00f0ff;
|
|
18
|
+
--neon-magenta: #ff79c6;
|
|
19
|
+
--neon-green: #50fa7b;
|
|
20
|
+
--neon-orange: #ffb86c;
|
|
21
|
+
--neon-purple: #bd93f9;
|
|
22
|
+
--neon-red: #ff5555;
|
|
23
|
+
--text-main: #f8f8f2;
|
|
24
|
+
--text-muted: #6272a4;
|
|
25
|
+
--border-glow: rgba(0, 240, 255, 0.35);
|
|
26
|
+
--border-glow-magenta: rgba(255, 121, 198, 0.35);
|
|
27
|
+
--font-display: 'Orbitron', sans-serif;
|
|
28
|
+
--font-mono: 'Share Tech Mono', monospace;
|
|
29
|
+
--font-sans: 'Inter', sans-serif;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
* {
|
|
33
|
+
box-sizing: border-box;
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
body {
|
|
39
|
+
background-color: var(--bg-dark);
|
|
40
|
+
background-image:
|
|
41
|
+
radial-gradient(at 0% 0%, rgba(20, 30, 60, 0.5) 0px, transparent 50%),
|
|
42
|
+
radial-gradient(at 100% 100%, rgba(50, 15, 45, 0.4) 0px, transparent 50%),
|
|
43
|
+
linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%),
|
|
44
|
+
linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
|
|
45
|
+
background-size: 100% 100%, 100% 100%, 100% 4px, 6px 100%;
|
|
46
|
+
color: var(--text-main);
|
|
47
|
+
font-family: var(--font-sans);
|
|
48
|
+
min-height: 100vh;
|
|
49
|
+
overflow-x: hidden;
|
|
50
|
+
padding: 20px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Cyberpunk HUD frame */
|
|
54
|
+
.hud-frame {
|
|
55
|
+
border: 2px solid var(--neon-cyan);
|
|
56
|
+
box-shadow: 0 0 15px var(--border-glow);
|
|
57
|
+
border-radius: 8px;
|
|
58
|
+
padding: 20px;
|
|
59
|
+
position: relative;
|
|
60
|
+
background: rgba(5, 8, 17, 0.9);
|
|
61
|
+
backdrop-filter: blur(10px);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.hud-frame::before {
|
|
65
|
+
content: "SYSTEM LOG ACTIVE // TELEMETRY CORRELATION MESH";
|
|
66
|
+
position: absolute;
|
|
67
|
+
top: -12px;
|
|
68
|
+
left: 20px;
|
|
69
|
+
background: var(--bg-dark);
|
|
70
|
+
padding: 0 10px;
|
|
71
|
+
font-family: var(--font-display);
|
|
72
|
+
font-size: 0.75rem;
|
|
73
|
+
color: var(--neon-cyan);
|
|
74
|
+
letter-spacing: 2px;
|
|
75
|
+
font-weight: 700;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.hud-frame::after {
|
|
79
|
+
content: "AETHER CLI V1.3.6";
|
|
80
|
+
position: absolute;
|
|
81
|
+
bottom: -12px;
|
|
82
|
+
right: 20px;
|
|
83
|
+
background: var(--bg-dark);
|
|
84
|
+
padding: 0 10px;
|
|
85
|
+
font-family: var(--font-display);
|
|
86
|
+
font-size: 0.75rem;
|
|
87
|
+
color: var(--neon-magenta);
|
|
88
|
+
letter-spacing: 2px;
|
|
89
|
+
font-weight: 700;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
header {
|
|
93
|
+
display: flex;
|
|
94
|
+
justify-content: space-between;
|
|
95
|
+
align-items: center;
|
|
96
|
+
border-bottom: 1px solid rgba(0, 240, 255, 0.2);
|
|
97
|
+
padding-bottom: 15px;
|
|
98
|
+
margin-bottom: 20px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.title-area h1 {
|
|
102
|
+
font-family: var(--font-display);
|
|
103
|
+
font-size: 1.8rem;
|
|
104
|
+
text-transform: uppercase;
|
|
105
|
+
letter-spacing: 4px;
|
|
106
|
+
color: var(--text-main);
|
|
107
|
+
text-shadow: 0 0 10px rgba(0, 240, 255, 0.6);
|
|
108
|
+
display: flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
gap: 10px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.title-area h1 span {
|
|
114
|
+
color: var(--neon-magenta);
|
|
115
|
+
text-shadow: 0 0 10px rgba(255, 121, 198, 0.6);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.subtitle {
|
|
119
|
+
font-family: var(--font-mono);
|
|
120
|
+
font-size: 0.85rem;
|
|
121
|
+
color: var(--text-muted);
|
|
122
|
+
margin-top: 4px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.status-badge {
|
|
126
|
+
font-family: var(--font-mono);
|
|
127
|
+
font-size: 0.8rem;
|
|
128
|
+
background: rgba(80, 250, 123, 0.1);
|
|
129
|
+
border: 1px solid var(--neon-green);
|
|
130
|
+
color: var(--neon-green);
|
|
131
|
+
box-shadow: 0 0 8px rgba(80, 250, 123, 0.2);
|
|
132
|
+
padding: 6px 14px;
|
|
133
|
+
border-radius: 4px;
|
|
134
|
+
text-transform: uppercase;
|
|
135
|
+
letter-spacing: 1px;
|
|
136
|
+
display: flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
gap: 8px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.status-badge::before {
|
|
142
|
+
content: "";
|
|
143
|
+
display: inline-block;
|
|
144
|
+
width: 8px;
|
|
145
|
+
height: 8px;
|
|
146
|
+
background: var(--neon-green);
|
|
147
|
+
border-radius: 50%;
|
|
148
|
+
box-shadow: 0 0 8px var(--neon-green);
|
|
149
|
+
animation: blink 1.5s infinite;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@keyframes blink {
|
|
153
|
+
0%, 100% { opacity: 0.4; }
|
|
154
|
+
50% { opacity: 1; }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Grid Layout */
|
|
158
|
+
.grid-container {
|
|
159
|
+
display: grid;
|
|
160
|
+
grid-template-columns: 1fr 1fr;
|
|
161
|
+
gap: 20px;
|
|
162
|
+
margin-bottom: 20px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@media (max-width: 900px) {
|
|
166
|
+
.grid-container {
|
|
167
|
+
grid-template-columns: 1fr;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.card {
|
|
172
|
+
background: var(--bg-card);
|
|
173
|
+
border: 1px solid rgba(0, 240, 255, 0.15);
|
|
174
|
+
border-radius: 6px;
|
|
175
|
+
padding: 20px;
|
|
176
|
+
transition: all 0.3s ease;
|
|
177
|
+
position: relative;
|
|
178
|
+
overflow: hidden;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.card:hover {
|
|
182
|
+
background: var(--bg-card-hover);
|
|
183
|
+
border-color: var(--neon-cyan);
|
|
184
|
+
box-shadow: 0 0 10px rgba(0, 240, 255, 0.15);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.card.magenta-theme {
|
|
188
|
+
border-color: rgba(255, 121, 198, 0.15);
|
|
189
|
+
}
|
|
190
|
+
.card.magenta-theme:hover {
|
|
191
|
+
border-color: var(--neon-magenta);
|
|
192
|
+
box-shadow: 0 0 10px rgba(255, 121, 198, 0.15);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.card-title {
|
|
196
|
+
font-family: var(--font-display);
|
|
197
|
+
font-size: 0.95rem;
|
|
198
|
+
letter-spacing: 2px;
|
|
199
|
+
margin-bottom: 15px;
|
|
200
|
+
text-transform: uppercase;
|
|
201
|
+
display: flex;
|
|
202
|
+
justify-content: space-between;
|
|
203
|
+
align-items: center;
|
|
204
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
205
|
+
padding-bottom: 8px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.card-title.cyan { color: var(--neon-cyan); }
|
|
209
|
+
.card-title.magenta { color: var(--neon-magenta); }
|
|
210
|
+
.card-title.green { color: var(--neon-green); }
|
|
211
|
+
.card-title.orange { color: var(--neon-orange); }
|
|
212
|
+
|
|
213
|
+
/* Token Stats styling */
|
|
214
|
+
.token-metrics {
|
|
215
|
+
display: grid;
|
|
216
|
+
grid-template-columns: 1fr 1fr;
|
|
217
|
+
gap: 15px;
|
|
218
|
+
margin-bottom: 15px;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.metric-box {
|
|
222
|
+
background: rgba(0, 0, 0, 0.3);
|
|
223
|
+
border-radius: 4px;
|
|
224
|
+
padding: 12px;
|
|
225
|
+
border-left: 3px solid var(--neon-cyan);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.metric-box.completion {
|
|
229
|
+
border-left-color: var(--neon-magenta);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.metric-val {
|
|
233
|
+
font-family: var(--font-mono);
|
|
234
|
+
font-size: 1.6rem;
|
|
235
|
+
font-weight: bold;
|
|
236
|
+
margin-top: 4px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.metric-lbl {
|
|
240
|
+
font-size: 0.75rem;
|
|
241
|
+
color: var(--text-muted);
|
|
242
|
+
text-transform: uppercase;
|
|
243
|
+
letter-spacing: 1px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Live Progress bars */
|
|
247
|
+
.progress-bar-container {
|
|
248
|
+
margin-top: 10px;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.progress-info {
|
|
252
|
+
display: flex;
|
|
253
|
+
justify-content: space-between;
|
|
254
|
+
font-size: 0.75rem;
|
|
255
|
+
color: var(--text-muted);
|
|
256
|
+
margin-bottom: 4px;
|
|
257
|
+
font-family: var(--font-mono);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.progress-bg {
|
|
261
|
+
background: rgba(255, 255, 255, 0.05);
|
|
262
|
+
height: 8px;
|
|
263
|
+
border-radius: 4px;
|
|
264
|
+
overflow: hidden;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.progress-fill {
|
|
268
|
+
height: 100%;
|
|
269
|
+
width: 0%;
|
|
270
|
+
background: linear-gradient(90deg, var(--neon-cyan), var(--neon-magenta));
|
|
271
|
+
transition: width 0.8s ease;
|
|
272
|
+
box-shadow: 0 0 8px var(--neon-cyan);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Provider Mesh styling */
|
|
276
|
+
.mesh-grid {
|
|
277
|
+
display: grid;
|
|
278
|
+
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
|
|
279
|
+
gap: 10px;
|
|
280
|
+
max-height: 250px;
|
|
281
|
+
overflow-y: auto;
|
|
282
|
+
padding-right: 5px;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.mesh-node {
|
|
286
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
287
|
+
background: rgba(0, 0, 0, 0.25);
|
|
288
|
+
border-radius: 4px;
|
|
289
|
+
padding: 10px;
|
|
290
|
+
text-align: center;
|
|
291
|
+
transition: all 0.2s ease;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.mesh-node.active {
|
|
295
|
+
border-color: var(--neon-green);
|
|
296
|
+
background: rgba(80, 250, 123, 0.04);
|
|
297
|
+
box-shadow: inset 0 0 8px rgba(80, 250, 123, 0.05);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.mesh-node.inactive {
|
|
301
|
+
opacity: 0.6;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.node-name {
|
|
305
|
+
font-family: var(--font-display);
|
|
306
|
+
font-size: 0.75rem;
|
|
307
|
+
font-weight: bold;
|
|
308
|
+
text-transform: uppercase;
|
|
309
|
+
letter-spacing: 1px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.mesh-node.active .node-name {
|
|
313
|
+
color: var(--neon-green);
|
|
314
|
+
text-shadow: 0 0 6px rgba(80, 250, 123, 0.4);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.node-status {
|
|
318
|
+
font-family: var(--font-mono);
|
|
319
|
+
font-size: 0.65rem;
|
|
320
|
+
margin-top: 4px;
|
|
321
|
+
color: var(--text-muted);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.mesh-node.active .node-status {
|
|
325
|
+
color: var(--neon-cyan);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* SVG Chart */
|
|
329
|
+
.chart-container {
|
|
330
|
+
width: 100%;
|
|
331
|
+
height: 200px;
|
|
332
|
+
position: relative;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.chart-svg {
|
|
336
|
+
width: 100%;
|
|
337
|
+
height: 100%;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* History & Console */
|
|
341
|
+
.history-layout {
|
|
342
|
+
display: grid;
|
|
343
|
+
grid-template-columns: 280px 1fr;
|
|
344
|
+
gap: 20px;
|
|
345
|
+
margin-top: 20px;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@media (max-width: 800px) {
|
|
349
|
+
.history-layout {
|
|
350
|
+
grid-template-columns: 1fr;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.session-list {
|
|
355
|
+
max-height: 400px;
|
|
356
|
+
overflow-y: auto;
|
|
357
|
+
padding-right: 5px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.session-item {
|
|
361
|
+
padding: 12px;
|
|
362
|
+
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
363
|
+
background: rgba(0, 0, 0, 0.2);
|
|
364
|
+
border-radius: 4px;
|
|
365
|
+
margin-bottom: 8px;
|
|
366
|
+
cursor: pointer;
|
|
367
|
+
transition: all 0.2s ease;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.session-item:hover, .session-item.selected {
|
|
371
|
+
border-color: var(--neon-magenta);
|
|
372
|
+
background: rgba(255, 121, 198, 0.05);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.session-meta {
|
|
376
|
+
display: flex;
|
|
377
|
+
justify-content: space-between;
|
|
378
|
+
font-family: var(--font-mono);
|
|
379
|
+
font-size: 0.7rem;
|
|
380
|
+
color: var(--text-muted);
|
|
381
|
+
margin-bottom: 4px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.session-mode {
|
|
385
|
+
color: var(--neon-cyan);
|
|
386
|
+
text-transform: uppercase;
|
|
387
|
+
font-weight: bold;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.session-preview {
|
|
391
|
+
font-size: 0.8rem;
|
|
392
|
+
white-space: nowrap;
|
|
393
|
+
overflow: hidden;
|
|
394
|
+
text-overflow: ellipsis;
|
|
395
|
+
color: var(--text-main);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.chat-view {
|
|
399
|
+
border: 1px solid rgba(255, 121, 198, 0.15);
|
|
400
|
+
background: rgba(0, 0, 0, 0.3);
|
|
401
|
+
border-radius: 6px;
|
|
402
|
+
height: 400px;
|
|
403
|
+
display: flex;
|
|
404
|
+
flex-direction: column;
|
|
405
|
+
overflow: hidden;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.chat-header {
|
|
409
|
+
background: rgba(255, 121, 198, 0.08);
|
|
410
|
+
border-bottom: 1px solid rgba(255, 121, 198, 0.15);
|
|
411
|
+
padding: 10px 15px;
|
|
412
|
+
font-family: var(--font-display);
|
|
413
|
+
font-size: 0.8rem;
|
|
414
|
+
letter-spacing: 1px;
|
|
415
|
+
color: var(--neon-magenta);
|
|
416
|
+
display: flex;
|
|
417
|
+
justify-content: space-between;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.chat-body {
|
|
421
|
+
flex: 1;
|
|
422
|
+
padding: 15px;
|
|
423
|
+
overflow-y: auto;
|
|
424
|
+
display: flex;
|
|
425
|
+
flex-direction: column;
|
|
426
|
+
gap: 12px;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.message {
|
|
430
|
+
max-width: 85%;
|
|
431
|
+
padding: 10px 14px;
|
|
432
|
+
border-radius: 6px;
|
|
433
|
+
font-size: 0.85rem;
|
|
434
|
+
line-height: 1.4;
|
|
435
|
+
font-family: var(--font-sans);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.message.user {
|
|
439
|
+
align-self: flex-end;
|
|
440
|
+
background: rgba(0, 240, 255, 0.08);
|
|
441
|
+
border: 1px solid rgba(0, 240, 255, 0.25);
|
|
442
|
+
color: var(--text-main);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.message.assistant {
|
|
446
|
+
align-self: flex-start;
|
|
447
|
+
background: rgba(255, 121, 198, 0.06);
|
|
448
|
+
border: 1px solid rgba(255, 121, 198, 0.2);
|
|
449
|
+
color: var(--text-main);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.msg-header {
|
|
453
|
+
font-family: var(--font-mono);
|
|
454
|
+
font-size: 0.7rem;
|
|
455
|
+
color: var(--text-muted);
|
|
456
|
+
margin-bottom: 4px;
|
|
457
|
+
display: flex;
|
|
458
|
+
justify-content: space-between;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.msg-content {
|
|
462
|
+
white-space: pre-wrap;
|
|
463
|
+
word-break: break-word;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/* Scrollbar styling */
|
|
467
|
+
::-webkit-scrollbar {
|
|
468
|
+
width: 5px;
|
|
469
|
+
}
|
|
470
|
+
::-webkit-scrollbar-track {
|
|
471
|
+
background: rgba(0, 0, 0, 0.1);
|
|
472
|
+
}
|
|
473
|
+
::-webkit-scrollbar-thumb {
|
|
474
|
+
background: rgba(0, 240, 255, 0.2);
|
|
475
|
+
border-radius: 3px;
|
|
476
|
+
}
|
|
477
|
+
::-webkit-scrollbar-thumb:hover {
|
|
478
|
+
background: var(--neon-cyan);
|
|
479
|
+
}
|
|
480
|
+
</style>
|
|
481
|
+
</head>
|
|
482
|
+
<body>
|
|
483
|
+
|
|
484
|
+
<div class="hud-frame">
|
|
485
|
+
<header>
|
|
486
|
+
<div class="title-area">
|
|
487
|
+
<h1>AETHER <span>TELEMETRY</span></h1>
|
|
488
|
+
<div class="subtitle">// DECISION ENGINE DIAGNOSTICS & SYSTEM MESH STATUS</div>
|
|
489
|
+
</div>
|
|
490
|
+
<div class="status-badge" id="hud-status">CORRELATING MESH</div>
|
|
491
|
+
</header>
|
|
492
|
+
|
|
493
|
+
<div class="grid-container">
|
|
494
|
+
<!-- Card: Token & Session metrics -->
|
|
495
|
+
<div class="card">
|
|
496
|
+
<div class="card-title cyan">TOKEN METRICS</div>
|
|
497
|
+
<div class="token-metrics">
|
|
498
|
+
<div class="metric-box prompt">
|
|
499
|
+
<div class="metric-lbl">Prompt In</div>
|
|
500
|
+
<div class="metric-val" id="prompt-val">0</div>
|
|
501
|
+
</div>
|
|
502
|
+
<div class="metric-box completion">
|
|
503
|
+
<div class="metric-lbl">Completion Out</div>
|
|
504
|
+
<div class="metric-val" id="completion-val">0</div>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
|
|
508
|
+
<div class="progress-bar-container">
|
|
509
|
+
<div class="progress-info">
|
|
510
|
+
<span>Mesh Accumulator Load</span>
|
|
511
|
+
<span id="load-pct">0%</span>
|
|
512
|
+
</div>
|
|
513
|
+
<div class="progress-bg">
|
|
514
|
+
<div class="progress-fill" id="load-bar"></div>
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
</div>
|
|
518
|
+
|
|
519
|
+
<!-- Card: API Key Mesh -->
|
|
520
|
+
<div class="card magenta-theme">
|
|
521
|
+
<div class="card-title magenta">PROVIDER MESH MATRIX</div>
|
|
522
|
+
<div class="mesh-grid" id="provider-mesh">
|
|
523
|
+
<!-- Populated dynamically -->
|
|
524
|
+
</div>
|
|
525
|
+
</div>
|
|
526
|
+
</div>
|
|
527
|
+
|
|
528
|
+
<!-- Latency logs (Line Chart) -->
|
|
529
|
+
<div class="card" style="margin-bottom: 20px;">
|
|
530
|
+
<div class="card-title green">REQUEST LATENCY CHRONOLOGY</div>
|
|
531
|
+
<div class="chart-container">
|
|
532
|
+
<svg class="chart-svg" id="latency-chart" viewBox="0 0 800 200" preserveAspectRatio="none">
|
|
533
|
+
<!-- SVG lines/dots dynamically injected -->
|
|
534
|
+
</svg>
|
|
535
|
+
</div>
|
|
536
|
+
</div>
|
|
537
|
+
|
|
538
|
+
<!-- Past Sessions & Chat logs -->
|
|
539
|
+
<div class="card magenta-theme">
|
|
540
|
+
<div class="card-title magenta">SESSION HISTORY correlation MANAGER</div>
|
|
541
|
+
<div class="history-layout">
|
|
542
|
+
<div class="session-list" id="session-list">
|
|
543
|
+
<!-- Loaded dynamically -->
|
|
544
|
+
</div>
|
|
545
|
+
<div class="chat-view">
|
|
546
|
+
<div class="chat-header">
|
|
547
|
+
<span id="chat-header-session">No active correlate session</span>
|
|
548
|
+
<span id="chat-header-mode">-</span>
|
|
549
|
+
</div>
|
|
550
|
+
<div class="chat-body" id="chat-body">
|
|
551
|
+
<div style="color: var(--text-muted); text-align: center; margin-top: 150px; font-family: var(--font-mono);">
|
|
552
|
+
SELECT A CHAT SESSION ON THE LEFT TO BROADCAST LOGS
|
|
553
|
+
</div>
|
|
554
|
+
</div>
|
|
555
|
+
</div>
|
|
556
|
+
</div>
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
|
|
560
|
+
<script>
|
|
561
|
+
let activeSessionFilename = null;
|
|
562
|
+
let allSessions = [];
|
|
563
|
+
|
|
564
|
+
// Main fetch loop
|
|
565
|
+
async function updateTelemetry() {
|
|
566
|
+
try {
|
|
567
|
+
const res = await fetch("/api/telemetry");
|
|
568
|
+
if (!res.ok) throw new Error("Telemetry API down");
|
|
569
|
+
const data = await res.json();
|
|
570
|
+
|
|
571
|
+
document.getElementById("hud-status").innerText = "CONNECTED";
|
|
572
|
+
document.getElementById("hud-status").style.borderColor = "var(--neon-green)";
|
|
573
|
+
document.getElementById("hud-status").style.color = "var(--neon-green)";
|
|
574
|
+
|
|
575
|
+
updateTokenStats(data.tokenStats);
|
|
576
|
+
updateProviderMesh(data.meshStructure);
|
|
577
|
+
updateLatencyChart(data.latencyLogs);
|
|
578
|
+
updateSessions(data.sessions);
|
|
579
|
+
} catch (err) {
|
|
580
|
+
document.getElementById("hud-status").innerText = "DISCONNECTED";
|
|
581
|
+
document.getElementById("hud-status").style.borderColor = "var(--neon-red)";
|
|
582
|
+
document.getElementById("hud-status").style.color = "var(--neon-red)";
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function updateTokenStats(stats) {
|
|
587
|
+
const pVal = stats.promptTokens || 0;
|
|
588
|
+
const cVal = stats.completionTokens || 0;
|
|
589
|
+
const total = pVal + cVal;
|
|
590
|
+
|
|
591
|
+
document.getElementById("prompt-val").innerText = pVal.toLocaleString();
|
|
592
|
+
document.getElementById("completion-val").innerText = cVal.toLocaleString();
|
|
593
|
+
|
|
594
|
+
// Mesh load calculates load out of a nominal 50k window
|
|
595
|
+
const maxVal = 50000;
|
|
596
|
+
const pct = Math.min(100, Math.round((total / maxVal) * 100));
|
|
597
|
+
document.getElementById("load-pct").innerText = pct + "% (" + total.toLocaleString() + " / " + maxVal.toLocaleString() + ")";
|
|
598
|
+
document.getElementById("load-bar").style.width = pct + "%";
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function updateProviderMesh(mesh) {
|
|
602
|
+
const grid = document.getElementById("provider-mesh");
|
|
603
|
+
grid.innerHTML = "";
|
|
604
|
+
|
|
605
|
+
mesh.forEach(node => {
|
|
606
|
+
const div = document.createElement("div");
|
|
607
|
+
div.className = "mesh-node " + (node.configured ? "active" : "inactive");
|
|
608
|
+
|
|
609
|
+
const name = document.createElement("div");
|
|
610
|
+
name.className = "node-name";
|
|
611
|
+
name.innerText = node.name;
|
|
612
|
+
|
|
613
|
+
const status = document.createElement("div");
|
|
614
|
+
status.className = "node-status";
|
|
615
|
+
status.innerText = node.configured ? "ONLINE" : "OFFLINE";
|
|
616
|
+
|
|
617
|
+
div.appendChild(name);
|
|
618
|
+
div.appendChild(status);
|
|
619
|
+
grid.appendChild(div);
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function updateLatencyChart(logs) {
|
|
624
|
+
const svg = document.getElementById("latency-chart");
|
|
625
|
+
svg.innerHTML = "";
|
|
626
|
+
|
|
627
|
+
if (!logs || logs.length === 0) {
|
|
628
|
+
// Draw empty indicator
|
|
629
|
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
630
|
+
text.setAttribute("x", "400");
|
|
631
|
+
text.setAttribute("y", "100");
|
|
632
|
+
text.setAttribute("fill", "var(--text-muted)");
|
|
633
|
+
text.setAttribute("font-family", "var(--font-mono)");
|
|
634
|
+
text.setAttribute("text-anchor", "middle");
|
|
635
|
+
text.textContent = "Awaiting request latency tracking streams...";
|
|
636
|
+
svg.appendChild(text);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const width = 800;
|
|
641
|
+
const height = 200;
|
|
642
|
+
const padding = 30;
|
|
643
|
+
|
|
644
|
+
// Extract points
|
|
645
|
+
const points = logs.slice(-20); // last 20 queries
|
|
646
|
+
const maxLatency = Math.max(1000, ...points.map(p => p.latencyMs));
|
|
647
|
+
|
|
648
|
+
// Draw grid lines
|
|
649
|
+
for (let i = 1; i <= 3; i++) {
|
|
650
|
+
const y = padding + ((height - 2 * padding) / 4) * i;
|
|
651
|
+
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
652
|
+
line.setAttribute("x1", padding.toString());
|
|
653
|
+
line.setAttribute("y1", y.toString());
|
|
654
|
+
line.setAttribute("x2", (width - padding).toString());
|
|
655
|
+
line.setAttribute("y2", y.toString());
|
|
656
|
+
line.setAttribute("stroke", "rgba(80, 250, 123, 0.08)");
|
|
657
|
+
line.setAttribute("stroke-dasharray", "4 4");
|
|
658
|
+
svg.appendChild(line);
|
|
659
|
+
|
|
660
|
+
// Grid label
|
|
661
|
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
662
|
+
text.setAttribute("x", (padding - 5).toString());
|
|
663
|
+
text.setAttribute("y", (y + 4).toString());
|
|
664
|
+
text.setAttribute("fill", "var(--text-muted)");
|
|
665
|
+
text.setAttribute("font-family", "var(--font-mono)");
|
|
666
|
+
text.setAttribute("font-size", "9");
|
|
667
|
+
text.setAttribute("text-anchor", "end");
|
|
668
|
+
text.textContent = Math.round(maxLatency - (maxLatency / 4) * i) + "ms";
|
|
669
|
+
svg.appendChild(text);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Compute scales
|
|
673
|
+
const xRange = width - 2 * padding;
|
|
674
|
+
const yRange = height - 2 * padding;
|
|
675
|
+
const pointsCount = Math.max(2, points.length);
|
|
676
|
+
|
|
677
|
+
let polyPoints = [];
|
|
678
|
+
points.forEach((p, idx) => {
|
|
679
|
+
const x = padding + (xRange / (pointsCount - 1)) * idx;
|
|
680
|
+
const y = height - padding - (yRange * (p.latencyMs / maxLatency));
|
|
681
|
+
polyPoints.push({ x, y, ...p });
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
// Draw line path
|
|
685
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
686
|
+
let d = `M ${polyPoints[0].x} ${polyPoints[0].y}`;
|
|
687
|
+
for (let i = 1; i < polyPoints.length; i++) {
|
|
688
|
+
d += ` L ${polyPoints[i].x} ${polyPoints[i].y}`;
|
|
689
|
+
}
|
|
690
|
+
path.setAttribute("d", d);
|
|
691
|
+
path.setAttribute("fill", "none");
|
|
692
|
+
path.setAttribute("stroke", "var(--neon-green)");
|
|
693
|
+
path.setAttribute("stroke-width", "2");
|
|
694
|
+
svg.appendChild(path);
|
|
695
|
+
|
|
696
|
+
// Draw dots & interactive tooltips
|
|
697
|
+
polyPoints.forEach((pt) => {
|
|
698
|
+
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
699
|
+
circle.setAttribute("cx", pt.x.toString());
|
|
700
|
+
circle.setAttribute("cy", pt.y.toString());
|
|
701
|
+
circle.setAttribute("r", "4");
|
|
702
|
+
circle.setAttribute("fill", pt.success ? "var(--neon-green)" : "var(--neon-red)");
|
|
703
|
+
circle.setAttribute("stroke", "var(--bg-dark)");
|
|
704
|
+
circle.setAttribute("stroke-width", "1");
|
|
705
|
+
|
|
706
|
+
const title = document.createElementNS("http://www.w3.org/2000/svg", "title");
|
|
707
|
+
title.textContent = `Model: ${pt.model}\nLatency: ${pt.latencyMs}ms\nSuccess: ${pt.success}`;
|
|
708
|
+
circle.appendChild(title);
|
|
709
|
+
|
|
710
|
+
svg.appendChild(circle);
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function updateSessions(sessions) {
|
|
715
|
+
allSessions = sessions;
|
|
716
|
+
const list = document.getElementById("session-list");
|
|
717
|
+
|
|
718
|
+
// If none, clear and exit
|
|
719
|
+
if (!sessions || sessions.length === 0) {
|
|
720
|
+
list.innerHTML = `<div style="color: var(--text-muted); font-size: 0.8rem; text-align: center; margin-top: 40px; font-family: var(--font-mono);">No active correlators.</div>`;
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Preserve scroll position if reloading
|
|
725
|
+
const scrollTop = list.scrollTop;
|
|
726
|
+
list.innerHTML = "";
|
|
727
|
+
|
|
728
|
+
sessions.forEach(s => {
|
|
729
|
+
const div = document.createElement("div");
|
|
730
|
+
div.className = "session-item" + (activeSessionFilename === s.filename ? " selected" : "");
|
|
731
|
+
div.onclick = () => selectSession(s);
|
|
732
|
+
|
|
733
|
+
const meta = document.createElement("div");
|
|
734
|
+
meta.className = "session-meta";
|
|
735
|
+
|
|
736
|
+
const mode = document.createElement("span");
|
|
737
|
+
mode.className = "session-mode";
|
|
738
|
+
mode.innerText = s.mode;
|
|
739
|
+
|
|
740
|
+
const date = document.createElement("span");
|
|
741
|
+
const dObj = new Date(s.timestamp);
|
|
742
|
+
date.innerText = dObj.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
743
|
+
|
|
744
|
+
meta.appendChild(mode);
|
|
745
|
+
meta.appendChild(date);
|
|
746
|
+
|
|
747
|
+
const preview = document.createElement("div");
|
|
748
|
+
preview.className = "session-preview";
|
|
749
|
+
const firstUserMsg = s.messages.find(m => m.role === "user")?.content || "New interactive context";
|
|
750
|
+
preview.innerText = firstUserMsg;
|
|
751
|
+
|
|
752
|
+
div.appendChild(meta);
|
|
753
|
+
div.appendChild(preview);
|
|
754
|
+
list.appendChild(div);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
list.scrollTop = scrollTop;
|
|
758
|
+
|
|
759
|
+
// Auto-select first session if none is selected
|
|
760
|
+
if (!activeSessionFilename && sessions.length > 0) {
|
|
761
|
+
selectSession(sessions[0]);
|
|
762
|
+
} else if (activeSessionFilename) {
|
|
763
|
+
// Refresh the selected session's chat view
|
|
764
|
+
const currentSelected = sessions.find(s => s.filename === activeSessionFilename);
|
|
765
|
+
if (currentSelected) {
|
|
766
|
+
renderChatBody(currentSelected.messages);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function selectSession(session) {
|
|
772
|
+
activeSessionFilename = session.filename;
|
|
773
|
+
|
|
774
|
+
// Highlight selected session item in the sidebar
|
|
775
|
+
const items = document.querySelectorAll(".session-item");
|
|
776
|
+
items.forEach(el => el.classList.remove("selected"));
|
|
777
|
+
|
|
778
|
+
// We rebuild selection dynamically, so updateSessions will color it on next poll.
|
|
779
|
+
// But we set class manually right now for instant visual response:
|
|
780
|
+
const matchingItem = Array.from(items).find(el => el.querySelector(".session-preview").innerText === (session.messages.find(m => m.role === "user")?.content || "New interactive context"));
|
|
781
|
+
if (matchingItem) matchingItem.classList.add("selected");
|
|
782
|
+
|
|
783
|
+
document.getElementById("chat-header-session").innerText = "SESSION: " + session.filename.substring(0, 16) + "...";
|
|
784
|
+
document.getElementById("chat-header-mode").innerText = session.mode.toUpperCase();
|
|
785
|
+
|
|
786
|
+
renderChatBody(session.messages);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function renderChatBody(messages) {
|
|
790
|
+
const body = document.getElementById("chat-body");
|
|
791
|
+
body.innerHTML = "";
|
|
792
|
+
|
|
793
|
+
if (!messages || messages.length === 0) {
|
|
794
|
+
body.innerHTML = `<div style="color: var(--text-muted); text-align: center; margin-top: 150px; font-family: var(--font-mono);">EMPTY CORRELATION MESSAGES LOG</div>`;
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
messages.forEach(msg => {
|
|
799
|
+
const div = document.createElement("div");
|
|
800
|
+
div.className = "message " + (msg.role === "user" ? "user" : "assistant");
|
|
801
|
+
|
|
802
|
+
const header = document.createElement("div");
|
|
803
|
+
header.className = "msg-header";
|
|
804
|
+
|
|
805
|
+
const sender = document.createElement("span");
|
|
806
|
+
sender.style.fontWeight = "bold";
|
|
807
|
+
sender.style.color = msg.role === "user" ? "var(--neon-cyan)" : "var(--neon-magenta)";
|
|
808
|
+
sender.innerText = msg.role === "user" ? "❯ YOU" : "❯ AETHER";
|
|
809
|
+
|
|
810
|
+
const meta = document.createElement("span");
|
|
811
|
+
meta.innerText = msg.provider ? `${msg.provider} // ${msg.model || ""}` : "";
|
|
812
|
+
|
|
813
|
+
header.appendChild(sender);
|
|
814
|
+
header.appendChild(meta);
|
|
815
|
+
|
|
816
|
+
const content = document.createElement("div");
|
|
817
|
+
content.className = "msg-content";
|
|
818
|
+
content.innerText = msg.content;
|
|
819
|
+
|
|
820
|
+
div.appendChild(header);
|
|
821
|
+
div.appendChild(content);
|
|
822
|
+
body.appendChild(div);
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
// Scroll to bottom
|
|
826
|
+
body.scrollTop = body.scrollHeight;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Poll server every 1.5 seconds
|
|
830
|
+
updateTelemetry();
|
|
831
|
+
setInterval(updateTelemetry, 1500);
|
|
832
|
+
</script>
|
|
833
|
+
</body>
|
|
834
|
+
</html>
|