@theihtisham/agent-shadow-brain 5.0.0 → 5.0.1
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 +359 -283
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/brain/auto-setup.d.ts +54 -0
- package/dist/brain/auto-setup.d.ts.map +1 -0
- package/dist/brain/auto-setup.js +660 -0
- package/dist/brain/auto-setup.js.map +1 -0
- package/dist/brain/brain-portability.d.ts +81 -0
- package/dist/brain/brain-portability.d.ts.map +1 -0
- package/dist/brain/brain-portability.js +329 -0
- package/dist/brain/brain-portability.js.map +1 -0
- package/dist/brain/mcp-server.d.ts +12 -0
- package/dist/brain/mcp-server.d.ts.map +1 -1
- package/dist/brain/mcp-server.js +396 -37
- package/dist/brain/mcp-server.js.map +1 -1
- package/dist/brain/plugin-system.d.ts +97 -0
- package/dist/brain/plugin-system.d.ts.map +1 -0
- package/dist/brain/plugin-system.js +404 -0
- package/dist/brain/plugin-system.js.map +1 -0
- package/dist/cli.js +179 -3
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/server.d.ts +4 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +453 -330
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +79 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +28 -4
package/dist/dashboard/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/dashboard/server.ts — Real-time Web Dashboard with WebSocket
|
|
2
|
-
//
|
|
2
|
+
// v5.0.1 — Infinite Intelligence Edition
|
|
3
|
+
// 8-panel layout: Health, Insights, AI Tools, Memory Tiers, Modules, Fixes, Controls, Activity
|
|
3
4
|
import * as http from 'http';
|
|
4
5
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
5
6
|
// ── Embedded HTML Client ──────────────────────────────────────────────────────
|
|
@@ -8,346 +9,365 @@ const HTML_CLIENT = `<!DOCTYPE html>
|
|
|
8
9
|
<head>
|
|
9
10
|
<meta charset="UTF-8">
|
|
10
11
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
11
|
-
<title
|
|
12
|
+
<title>Shadow Brain v5.0.1 — Dashboard</title>
|
|
12
13
|
<style>
|
|
13
14
|
:root {
|
|
14
|
-
--bg
|
|
15
|
-
--border
|
|
16
|
-
--green
|
|
17
|
-
--blue
|
|
18
|
-
--cyan
|
|
15
|
+
--bg:#0a0e14;--bg2:#131820;--bg3:#1a2030;--bg4:#232d3f;
|
|
16
|
+
--border:#2a3550;--text:#d4dce8;--text2:#6b7d99;
|
|
17
|
+
--green:#00e676;--yellow:#ffca28;--red:#ff5252;
|
|
18
|
+
--blue:#42a5f5;--purple:#b388ff;--orange:#ffab40;
|
|
19
|
+
--cyan:#18ffff;--pink:#ff4081;
|
|
19
20
|
}
|
|
20
|
-
*
|
|
21
|
-
body
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
header
|
|
25
|
-
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
.
|
|
31
|
-
.
|
|
32
|
-
.
|
|
33
|
-
.
|
|
34
|
-
.
|
|
35
|
-
.
|
|
36
|
-
.
|
|
37
|
-
.trend
|
|
38
|
-
.trend.
|
|
39
|
-
.
|
|
40
|
-
.
|
|
41
|
-
.
|
|
42
|
-
.
|
|
43
|
-
.
|
|
44
|
-
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
.
|
|
50
|
-
.
|
|
51
|
-
.
|
|
52
|
-
.
|
|
53
|
-
.
|
|
21
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
22
|
+
body{background:var(--bg);color:var(--text);font-family:'Segoe UI',system-ui,-apple-system,monospace;font-size:13px;height:100vh;overflow:hidden;display:flex;flex-direction:column}
|
|
23
|
+
|
|
24
|
+
/* Header */
|
|
25
|
+
header{background:linear-gradient(180deg,var(--bg2),var(--bg));border-bottom:1px solid var(--border);padding:10px 20px;display:flex;align-items:center;gap:12px;flex-shrink:0}
|
|
26
|
+
header h1{font-size:16px;font-weight:800;letter-spacing:-.5px}
|
|
27
|
+
header h1 .v{color:var(--cyan);font-size:11px;font-weight:500;margin-left:4px}
|
|
28
|
+
.dot{width:8px;height:8px;border-radius:50%;background:var(--red);transition:.3s}
|
|
29
|
+
.dot.on{background:var(--green);box-shadow:0 0 8px var(--green);animation:pulse 2s infinite}
|
|
30
|
+
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}
|
|
31
|
+
.pill{background:var(--bg3);border:1px solid var(--border);border-radius:12px;padding:2px 10px;font-size:11px;color:var(--text2)}
|
|
32
|
+
.pill.score{font-weight:700;border-color:var(--green);color:var(--green)}
|
|
33
|
+
.pill.grade{font-weight:800}
|
|
34
|
+
.pill.gA{color:var(--green);border-color:var(--green)}
|
|
35
|
+
.pill.gB{color:var(--yellow);border-color:var(--yellow)}
|
|
36
|
+
.pill.gC{color:var(--orange);border-color:var(--orange)}
|
|
37
|
+
.pill.gD,.pill.gF{color:var(--red);border-color:var(--red)}
|
|
38
|
+
.trend{font-size:14px}
|
|
39
|
+
.trend.up{color:var(--green)}.trend.down{color:var(--red)}.trend.flat{color:var(--text2)}
|
|
40
|
+
.controls{margin-left:auto;display:flex;gap:6px}
|
|
41
|
+
.btn{background:var(--bg3);border:1px solid var(--border);border-radius:6px;padding:4px 12px;font-size:11px;color:var(--text);cursor:pointer;transition:.2s;font-weight:600}
|
|
42
|
+
.btn:hover{background:var(--bg4);border-color:var(--blue)}
|
|
43
|
+
.btn.danger{color:var(--red);border-color:var(--red)}
|
|
44
|
+
.btn.danger:hover{background:rgba(255,82,82,.15)}
|
|
45
|
+
.btn.primary{color:var(--green);border-color:var(--green)}
|
|
46
|
+
.btn.primary:hover{background:rgba(0,230,118,.15)}
|
|
47
|
+
|
|
48
|
+
/* Layout */
|
|
49
|
+
.grid{display:grid;grid-template-columns:280px 1fr 280px;grid-template-rows:auto 1fr 1fr;gap:1px;background:var(--border);flex:1;overflow:hidden}
|
|
50
|
+
.pnl{background:var(--bg);overflow:hidden;display:flex;flex-direction:column}
|
|
51
|
+
.pnl-h{background:var(--bg2);padding:7px 12px;font-size:11px;font-weight:700;color:var(--text2);text-transform:uppercase;letter-spacing:.08em;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
|
|
52
|
+
.pnl-b{flex:1;overflow-y:auto;padding:8px}
|
|
53
|
+
.pnl-b::-webkit-scrollbar{width:3px}.pnl-b::-webkit-scrollbar-thumb{background:var(--border)}
|
|
54
|
+
.cnt{background:var(--bg4);border-radius:10px;padding:1px 7px;font-size:10px;color:var(--text2)}
|
|
55
|
+
|
|
56
|
+
/* Row 1: top bar */
|
|
57
|
+
.topbar{grid-column:1/-1;background:var(--bg2);padding:6px 14px;display:flex;gap:16px;align-items:center;flex-shrink:0;overflow-x:auto}
|
|
58
|
+
.stat{display:flex;flex-direction:column;align-items:center;min-width:80px}
|
|
59
|
+
.stat .val{font-size:20px;font-weight:900;line-height:1}
|
|
60
|
+
.stat .lbl{font-size:9px;color:var(--text2);text-transform:uppercase;letter-spacing:.06em;margin-top:2px}
|
|
61
|
+
|
|
62
|
+
/* Health */
|
|
63
|
+
.health-big{text-align:center;padding:12px;background:var(--bg2);border-radius:8px;border:1px solid var(--border);margin-bottom:10px}
|
|
64
|
+
.health-big .num{font-size:48px;font-weight:900;line-height:1}
|
|
65
|
+
.health-big .lbl{font-size:11px;color:var(--text2);margin-top:3px}
|
|
66
|
+
.dim{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:8px;margin-bottom:6px}
|
|
67
|
+
.dim .name{font-size:10px;color:var(--text2);margin-bottom:4px}
|
|
68
|
+
.dim .bw{background:var(--bg4);border-radius:3px;height:5px;overflow:hidden;margin-bottom:3px}
|
|
69
|
+
.dim .bf{height:100%;border-radius:3px;transition:width .6s}
|
|
70
|
+
.dim .sv{font-size:12px;font-weight:700}
|
|
71
|
+
|
|
72
|
+
/* AI Tools */
|
|
73
|
+
.tool{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:5px;margin-bottom:4px;background:var(--bg2);border:1px solid var(--border)}
|
|
74
|
+
.tool .icon{font-size:16px;width:24px;text-align:center}
|
|
75
|
+
.tool .info{flex:1;min-width:0}
|
|
76
|
+
.tool .tname{font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
77
|
+
.tool .tstat{font-size:10px;color:var(--text2)}
|
|
78
|
+
.tool .tdot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
|
|
79
|
+
.tool .tdot.active{background:var(--green)}.tool .tdot.inactive{background:var(--text2)}.tool .tdot.error{background:var(--red)}
|
|
80
|
+
|
|
81
|
+
/* Memory tiers */
|
|
82
|
+
.tier{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:8px;margin-bottom:5px}
|
|
83
|
+
.tier .th{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}
|
|
84
|
+
.tier .tname{font-size:11px;font-weight:600}
|
|
85
|
+
.tier .tcount{font-size:11px;font-weight:700;color:var(--cyan)}
|
|
86
|
+
.tier .bw{background:var(--bg4);border-radius:3px;height:4px;overflow:hidden;margin-bottom:3px}
|
|
87
|
+
.tier .bf{height:100%;border-radius:3px;transition:width .5s}
|
|
88
|
+
.tier .tmeta{font-size:9px;color:var(--text2)}
|
|
89
|
+
.tier.t0 .tname{color:var(--green)}.tier.t1 .tname{color:var(--blue)}.tier.t2 .tname{color:var(--purple)}.tier.t3 .tname{color:var(--orange)}
|
|
90
|
+
|
|
91
|
+
/* Modules */
|
|
92
|
+
.mod{display:flex;align-items:center;gap:6px;padding:4px 8px;border-radius:4px;margin-bottom:3px;background:var(--bg2);border:1px solid var(--border);font-size:11px}
|
|
93
|
+
.mod .mdot{width:5px;height:5px;border-radius:50%;flex-shrink:0}
|
|
94
|
+
.mod .mdot.active{background:var(--green)}.mod .mdot.idle{background:var(--text2)}.mod .mdot.error{background:var(--red)}
|
|
95
|
+
.mod .mname{flex:1;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
96
|
+
.mod .mstat{font-size:9px;color:var(--text2);flex-shrink:0}
|
|
97
|
+
|
|
54
98
|
/* Insights */
|
|
55
|
-
.
|
|
56
|
-
@keyframes fadeIn
|
|
57
|
-
.
|
|
58
|
-
.
|
|
59
|
-
.
|
|
60
|
-
.
|
|
61
|
-
.
|
|
62
|
-
.
|
|
63
|
-
.
|
|
64
|
-
.tag
|
|
65
|
-
.tag.
|
|
66
|
-
.tag.
|
|
67
|
-
.tag.
|
|
68
|
-
.tag.
|
|
69
|
-
|
|
70
|
-
.tag.quality { background: rgba(88,166,255,.12); color: var(--blue); }
|
|
99
|
+
.ins{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:8px 10px;margin-bottom:6px;border-left:3px solid var(--border);animation:fadeIn .25s}
|
|
100
|
+
@keyframes fadeIn{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}
|
|
101
|
+
.ins.critical{border-left-color:var(--red)}.ins.high{border-left-color:var(--orange)}.ins.medium{border-left-color:var(--yellow)}.ins.low{border-left-color:var(--text2)}
|
|
102
|
+
.ins-t{font-weight:600;font-size:12px;margin-bottom:2px}
|
|
103
|
+
.ins-m{font-size:10px;color:var(--text2);display:flex;gap:6px;flex-wrap:wrap;margin-bottom:4px}
|
|
104
|
+
.ins-c{font-size:11px;color:var(--text2);line-height:1.45}
|
|
105
|
+
.tag{background:var(--bg4);border-radius:8px;padding:1px 6px;font-size:9px}
|
|
106
|
+
.tag.critical{background:rgba(255,82,82,.15);color:var(--red)}
|
|
107
|
+
.tag.high{background:rgba(255,171,64,.15);color:var(--orange)}
|
|
108
|
+
.tag.medium{background:rgba(255,202,40,.15);color:var(--yellow)}
|
|
109
|
+
.tag.low{background:rgba(107,125,153,.15);color:var(--text2)}
|
|
110
|
+
.tag.security{background:rgba(255,82,82,.12);color:var(--red)}
|
|
111
|
+
.tag.performance{background:rgba(24,255,255,.12);color:var(--cyan)}
|
|
112
|
+
.tag.quality{background:rgba(66,165,245,.12);color:var(--blue)}
|
|
113
|
+
|
|
71
114
|
/* Fixes */
|
|
72
|
-
.fix
|
|
73
|
-
.fix-
|
|
74
|
-
.fix-
|
|
75
|
-
.
|
|
76
|
-
.
|
|
77
|
-
.
|
|
78
|
-
.
|
|
79
|
-
.fix-
|
|
80
|
-
|
|
81
|
-
.fix-explanation { font-size: 11px; color: var(--text2); margin-top: 8px; line-height: 1.5; }
|
|
115
|
+
.fix{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:8px 10px;margin-bottom:6px;animation:fadeIn .25s}
|
|
116
|
+
.fix-t{font-weight:600;font-size:12px;margin-bottom:4px}
|
|
117
|
+
.fix-f{font-size:10px;color:var(--text2);margin-bottom:6px}
|
|
118
|
+
.cb{background:var(--bg4);border:1px solid var(--border);border-radius:3px;padding:6px 8px;font-family:'Cascadia Code','Fira Code',monospace;font-size:10px;line-height:1.5;overflow-x:auto}
|
|
119
|
+
.cb.bef{border-left:3px solid var(--red)}.cb.aft{border-left:3px solid var(--green)}
|
|
120
|
+
.fl{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;margin-bottom:3px}
|
|
121
|
+
.fl.bef{color:var(--red)}.fl.aft{color:var(--green)}
|
|
122
|
+
.fix-x{font-size:10px;color:var(--text2);margin-top:6px;line-height:1.4}
|
|
123
|
+
|
|
82
124
|
/* Log */
|
|
83
|
-
.log
|
|
84
|
-
.log-
|
|
85
|
-
.log-
|
|
86
|
-
.log-
|
|
87
|
-
|
|
88
|
-
.
|
|
89
|
-
.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
.empty .icon { font-size: 32px; margin-bottom: 10px; }
|
|
93
|
-
.spinner { display: inline-block; animation: spin 1s linear infinite; }
|
|
94
|
-
@keyframes spin { to { transform: rotate(360deg); } }
|
|
95
|
-
.panel-count { background: var(--bg3); border-radius: 10px; padding: 1px 7px; font-size: 11px; color: var(--text2); }
|
|
125
|
+
.log{font-family:monospace;font-size:11px;padding:2px 0;border-bottom:1px solid var(--bg2);display:flex;gap:8px}
|
|
126
|
+
.log-t{color:var(--text2);flex-shrink:0;font-size:10px}
|
|
127
|
+
.log-m{flex:1;word-break:break-all}
|
|
128
|
+
.log-m.info{color:var(--blue)}.log-m.success{color:var(--green)}.log-m.warning{color:var(--yellow)}.log-m.error{color:var(--red)}
|
|
129
|
+
|
|
130
|
+
.empty{text-align:center;color:var(--text2);padding:30px 16px;font-size:12px}
|
|
131
|
+
.empty .ic{font-size:28px;margin-bottom:8px}
|
|
132
|
+
.spin{display:inline-block;animation:spin 1s linear infinite}
|
|
133
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
96
134
|
</style>
|
|
97
135
|
</head>
|
|
98
136
|
<body>
|
|
99
137
|
<header>
|
|
100
|
-
<div class="
|
|
101
|
-
<h1
|
|
102
|
-
<span class="
|
|
103
|
-
<span class="
|
|
104
|
-
<span class="trend" id="
|
|
105
|
-
<span class="
|
|
106
|
-
<span style="
|
|
107
|
-
<
|
|
138
|
+
<div class="dot" id="dot"></div>
|
|
139
|
+
<h1>\u{1F9E0} Shadow Brain <span class="v">v5.0.1</span></h1>
|
|
140
|
+
<span class="pill score" id="pScore">\u2014/100</span>
|
|
141
|
+
<span class="pill grade" id="pGrade">\u2014</span>
|
|
142
|
+
<span class="trend flat" id="pTrend">\u2192</span>
|
|
143
|
+
<span class="pill" id="pAgents">No agents</span>
|
|
144
|
+
<span class="pill" id="pModules" style="color:var(--cyan)">0 modules</span>
|
|
145
|
+
<div class="controls">
|
|
146
|
+
<button class="btn primary" id="btnAnalyze" onclick="triggerAnalysis()">Analyze</button>
|
|
147
|
+
<button class="btn" id="btnRefresh" onclick="refreshAll()">Refresh</button>
|
|
148
|
+
</div>
|
|
108
149
|
</header>
|
|
109
150
|
|
|
110
|
-
<div class="
|
|
111
|
-
<!--
|
|
112
|
-
<div class="
|
|
113
|
-
<div class="
|
|
114
|
-
<div class="
|
|
115
|
-
|
|
116
|
-
</div>
|
|
151
|
+
<div class="grid">
|
|
152
|
+
<!-- Row 1: stats bar -->
|
|
153
|
+
<div class="topbar">
|
|
154
|
+
<div class="stat"><div class="val" id="sInsights" style="color:var(--blue)">0</div><div class="lbl">Insights</div></div>
|
|
155
|
+
<div class="stat"><div class="val" id="sFixes" style="color:var(--green)">0</div><div class="lbl">Fixes</div></div>
|
|
156
|
+
<div class="stat"><div class="val" id="sMemory" style="color:var(--purple)">0</div><div class="lbl">Memory KB</div></div>
|
|
157
|
+
<div class="stat"><div class="val" id="sModules" style="color:var(--cyan)">0</div><div class="lbl">Modules</div></div>
|
|
158
|
+
<div class="stat"><div class="val" id="sTools" style="color:var(--orange)">0</div><div class="lbl">AI Tools</div></div>
|
|
159
|
+
<div class="stat"><div class="val" id="sEvol" style="color:var(--pink)">0</div><div class="lbl">Evolution Gen</div></div>
|
|
160
|
+
<div class="stat"><div class="val" id="sSwarm" style="color:var(--yellow)">0%</div><div class="lbl">Swarm Conv</div></div>
|
|
161
|
+
<div class="stat"><div class="val" id="sTurbo" style="color:var(--green)">0x</div><div class="lbl">Turbo Comp</div></div>
|
|
162
|
+
<span style="flex:1"></span>
|
|
163
|
+
<span style="font-size:10px;color:var(--text2)" id="pProject">Connecting\u2026</span>
|
|
117
164
|
</div>
|
|
118
165
|
|
|
119
|
-
<!--
|
|
120
|
-
<div class="
|
|
121
|
-
<div class="
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
</div>
|
|
125
|
-
<div class="panel-body" id="insightsPanel">
|
|
126
|
-
<div class="empty"><div class="icon">💡</div>Insights will appear here…</div>
|
|
127
|
-
</div>
|
|
166
|
+
<!-- Col 1: Health + Memory -->
|
|
167
|
+
<div class="pnl" style="grid-row:2/4">
|
|
168
|
+
<div class="pnl-h">\u2B22 Health Score</div>
|
|
169
|
+
<div class="pnl-b" id="healthPnl"><div class="empty"><div class="ic"><span class="spin">\u27F3</span></div>Waiting\u2026</div></div>
|
|
170
|
+
<div class="pnl-h">\u{1F4BE} Memory Tiers</div>
|
|
171
|
+
<div class="pnl-b" id="memoryPnl"><div class="empty"><div class="ic">\u{1F4BE}</div>No memory data</div></div>
|
|
128
172
|
</div>
|
|
129
173
|
|
|
130
|
-
<!--
|
|
131
|
-
<div class="
|
|
132
|
-
<div class="
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<div class="
|
|
137
|
-
|
|
138
|
-
</div>
|
|
174
|
+
<!-- Col 2: Insights + Fixes -->
|
|
175
|
+
<div class="pnl" style="grid-row:2">
|
|
176
|
+
<div class="pnl-h">\u{1F4A1} Live Insights <span class="cnt" id="iCnt">0</span></div>
|
|
177
|
+
<div class="pnl-b" id="insPnl"><div class="empty"><div class="ic">\u{1F4A1}</div>Insights will appear\u2026</div></div>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="pnl" style="grid-row:3">
|
|
180
|
+
<div class="pnl-h">\u{1F527} Smart Fixes <span class="cnt" id="fCnt">0</span></div>
|
|
181
|
+
<div class="pnl-b" id="fixPnl"><div class="empty"><div class="ic">\u{1F527}</div>Fix suggestions\u2026</div></div>
|
|
139
182
|
</div>
|
|
140
183
|
|
|
141
|
-
<!--
|
|
142
|
-
<div class="
|
|
143
|
-
<div class="
|
|
144
|
-
<div class="
|
|
184
|
+
<!-- Col 3: AI Tools + Modules + Log -->
|
|
185
|
+
<div class="pnl" style="grid-row:2">
|
|
186
|
+
<div class="pnl-h">\u{1F916} AI Tools</div>
|
|
187
|
+
<div class="pnl-b" id="toolsPnl"><div class="empty"><div class="ic">\u{1F916}</div>Detecting tools\u2026</div></div>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="pnl" style="grid-row:3">
|
|
190
|
+
<div class="pnl-h">\u2699 Brain Modules</div>
|
|
191
|
+
<div class="pnl-b" id="modPnl"><div class="empty"><div class="ic">\u2699</div>Loading modules\u2026</div></div>
|
|
145
192
|
</div>
|
|
146
193
|
</div>
|
|
147
194
|
|
|
148
195
|
<script>
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
let insightCount = 0;
|
|
152
|
-
let fixCount = 0;
|
|
153
|
-
let allInsights = [];
|
|
196
|
+
const MAX_INS=50,MAX_LOG=80;
|
|
197
|
+
let insCnt=0,fixCnt=0,allIns=[];
|
|
154
198
|
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
const healthPanel = document.getElementById('healthPanel');
|
|
162
|
-
const insightsPanel = document.getElementById('insightsPanel');
|
|
163
|
-
const fixesPanel = document.getElementById('fixesPanel');
|
|
164
|
-
const logPanel = document.getElementById('logPanel');
|
|
165
|
-
const insightCountEl = document.getElementById('insightCount');
|
|
166
|
-
const fixCountEl = document.getElementById('fixCount');
|
|
199
|
+
const $=id=>document.getElementById(id);
|
|
200
|
+
const dot=$('dot'),pScore=$('pScore'),pGrade=$('pGrade'),pTrend=$('pTrend');
|
|
201
|
+
const pAgents=$('pAgents'),pModules=$('pModules'),pProject=$('pProject');
|
|
202
|
+
const healthPnl=$('healthPnl'),memoryPnl=$('memoryPnl');
|
|
203
|
+
const insPnl=$('insPnl'),fixPnl=$('fixPnl');
|
|
204
|
+
const toolsPnl=$('toolsPnl'),modPnl=$('modPnl');
|
|
167
205
|
|
|
168
|
-
function ts() {
|
|
169
|
-
|
|
170
|
-
}
|
|
206
|
+
function ts(){return new Date().toLocaleTimeString('en-US',{hour12:false})}
|
|
207
|
+
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')}
|
|
208
|
+
function sc(v){return v>=85?'var(--green)':v>=70?'var(--yellow)':v>=50?'var(--orange)':'var(--red)'}
|
|
171
209
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
el.innerHTML = \`<span class="log-time">\${ts()}</span><span class="log-msg \${type}">\${escapeHtml(msg)}</span>\`;
|
|
176
|
-
if (logPanel.children.length >= MAX_LOG) logPanel.removeChild(logPanel.lastChild);
|
|
177
|
-
logPanel.insertBefore(el, logPanel.firstChild);
|
|
210
|
+
// Log to insights panel feedback via activity
|
|
211
|
+
function logActivity(msg,type){
|
|
212
|
+
// We no longer have a dedicated log panel, so we use a minimal approach
|
|
178
213
|
}
|
|
179
214
|
|
|
180
|
-
function
|
|
181
|
-
|
|
215
|
+
function renderHealth(s){
|
|
216
|
+
if(!s)return;
|
|
217
|
+
const c=sc(s.overall);
|
|
218
|
+
const ti=s.trend==='improving'?'\u2191':s.trend==='declining'?'\u2193':'\u2192';
|
|
219
|
+
const tc=s.trend==='improving'?'up':s.trend==='declining'?'down':'flat';
|
|
220
|
+
pScore.textContent=s.overall+'/100';pScore.style.color=c;pScore.style.borderColor=c;
|
|
221
|
+
pGrade.textContent=s.grade;
|
|
222
|
+
const gc=s.grade.replace('+','').charAt(0);
|
|
223
|
+
pGrade.className='pill grade g'+gc;
|
|
224
|
+
pTrend.textContent=ti;pTrend.className='trend '+tc;
|
|
225
|
+
const dh=s.dimensions.map(d=>{
|
|
226
|
+
const col=sc(d.score);
|
|
227
|
+
const det=d.details.map(x=>'<div style="font-size:9px;color:var(--text2);margin-top:1px">\u2022 '+esc(x)+'</div>').join('');
|
|
228
|
+
return '<div class="dim"><div class="name">'+esc(d.name)+'</div><div class="bw"><div class="bf" style="width:'+d.score+'%;background:'+col+'"></div></div><div class="sv" style="color:'+col+'">'+d.score+'%</div>'+det+'</div>';
|
|
229
|
+
}).join('');
|
|
230
|
+
const ti2=s.topIssues.length>0?'<div style="margin-top:8px;padding:6px;background:var(--bg2);border-radius:5px;border:1px solid var(--border)"><div style="font-size:10px;color:var(--red);font-weight:700;margin-bottom:4px">\u{1F525} Top Issues</div>'+s.topIssues.map(i=>'<div style="font-size:10px;color:var(--text2);margin-bottom:2px">\u2022 '+esc(i)+'</div>').join('')+'</div>':'';
|
|
231
|
+
healthPnl.innerHTML='<div class="health-big"><div class="num" style="color:'+c+'">'+s.overall+'</div><div class="lbl">Health / 100 \u2014 Grade '+s.grade+' '+ti+'</div></div>'+dh+ti2;
|
|
182
232
|
}
|
|
183
233
|
|
|
184
|
-
function
|
|
185
|
-
if
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
234
|
+
function renderMemory(mem){
|
|
235
|
+
if(!mem)return;
|
|
236
|
+
const tiers=[
|
|
237
|
+
{key:'raw',name:'Raw',cls:'t0',color:'var(--green)',desc:'Full content'},
|
|
238
|
+
{key:'summary',name:'Summary',cls:'t1',color:'var(--blue)',desc:'Compressed summary'},
|
|
239
|
+
{key:'pattern',name:'Pattern',cls:'t2',color:'var(--purple)',desc:'Extracted patterns'},
|
|
240
|
+
{key:'principle',name:'Principle',cls:'t3',color:'var(--orange)',desc:'Core principles'},
|
|
241
|
+
];
|
|
242
|
+
const max=Math.max(1,...tiers.map(t=>mem[t.key]||0));
|
|
243
|
+
let html='';
|
|
244
|
+
let total=0;
|
|
245
|
+
for(const t of tiers){
|
|
246
|
+
const v=mem[t.key]||0;total+=v;
|
|
247
|
+
const pct=max>0?Math.round(v/max*100):0;
|
|
248
|
+
html+='<div class="tier '+t.cls+'"><div class="th"><span class="tname">'+t.name+'</span><span class="tcount">'+v+'</span></div><div class="bw"><div class="bf" style="width:'+pct+'%;background:'+t.color+'"></div></div><div class="tmeta">'+t.desc+'</div></div>';
|
|
249
|
+
}
|
|
250
|
+
const turbo=mem.turboCompression||0;
|
|
251
|
+
const turboLine=turbo>0?'<div style="margin-top:6px;padding:6px;background:var(--bg2);border-radius:5px;border:1px solid var(--border);font-size:10px;color:var(--cyan)">\u26A1 TurboQuant compression: '+turbo+'x — '+total+' total entries</div>':'<div style="margin-top:6px;font-size:10px;color:var(--text2);text-align:center">'+total+' total memory entries</div>';
|
|
252
|
+
memoryPnl.innerHTML=html+turboLine;
|
|
253
|
+
$('sMemory').textContent=Math.round(total/10);
|
|
189
254
|
}
|
|
190
255
|
|
|
191
|
-
function
|
|
192
|
-
if
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const dimsHtml = score.dimensions.map(d => {
|
|
205
|
-
const c = scoreColor(d.score);
|
|
206
|
-
const det = d.details.map(x => \`<div style="font-size:10px;color:var(--text2);margin-top:2px">• \${escapeHtml(x)}</div>\`).join('');
|
|
207
|
-
return \`<div class="dim">
|
|
208
|
-
<div class="name">\${escapeHtml(d.name)}</div>
|
|
209
|
-
<div class="bar-wrap"><div class="bar-fill" style="width:\${d.score}%;background:\${c}"></div></div>
|
|
210
|
-
<div class="score-val" style="color:\${c}">\${d.score}%</div>
|
|
211
|
-
\${det}
|
|
212
|
-
</div>\`;
|
|
256
|
+
function renderTools(tools){
|
|
257
|
+
if(!tools||!tools.length){
|
|
258
|
+
toolsPnl.innerHTML='<div class="empty"><div class="ic">\u{1F916}</div>No AI tools detected</div>';
|
|
259
|
+
$('sTools').textContent='0';
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const icons={claude_code:'\u{1F9E0}',cursor:'\u2702\uFE0F',kilo_code:'\u2699\uFE0F',cline:'\u{1F4BB}',opencode:'\u{1F517}',codex:'\u{1F4DA}',roo:'\u{1F418}',aider:'\u{1F91D}'};
|
|
263
|
+
toolsPnl.innerHTML=tools.map(t=>{
|
|
264
|
+
const ic=icons[t.id]||'\u{1F916}';
|
|
265
|
+
const cls=t.connected?'active':t.detected?'inactive':'error';
|
|
266
|
+
const label=t.connected?'Connected':t.detected?'Detected':'Not found';
|
|
267
|
+
return '<div class="tool"><div class="icon">'+ic+'</div><div class="info"><div class="tname">'+esc(t.name)+'</div><div class="tstat">'+label+(t.version?' v'+t.version:'')+'</div></div><div class="tdot '+cls+'"></div></div>';
|
|
213
268
|
}).join('');
|
|
214
|
-
|
|
215
|
-
const topIssues = score.topIssues.length > 0
|
|
216
|
-
? \`<div style="margin-top:10px;padding:8px;background:var(--bg2);border-radius:6px;border:1px solid var(--border)">
|
|
217
|
-
<div style="font-size:11px;color:var(--red);font-weight:700;margin-bottom:6px">🔥 Top Issues</div>
|
|
218
|
-
\${score.topIssues.map(i => \`<div style="font-size:11px;color:var(--text2);margin-bottom:3px">• \${escapeHtml(i)}</div>\`).join('')}
|
|
219
|
-
</div>\`
|
|
220
|
-
: '';
|
|
221
|
-
|
|
222
|
-
healthPanel.innerHTML = \`
|
|
223
|
-
<div class="health-big">
|
|
224
|
-
<div class="num" style="color:\${col}">\${score.overall}</div>
|
|
225
|
-
<div class="label">Health Score / 100 — Grade \${score.grade} \${trendIcon}</div>
|
|
226
|
-
</div>
|
|
227
|
-
<div class="health-grid">\${dimsHtml}</div>
|
|
228
|
-
\${topIssues}
|
|
229
|
-
\`;
|
|
269
|
+
$('sTools').textContent=tools.length;
|
|
230
270
|
}
|
|
231
271
|
|
|
232
|
-
function
|
|
233
|
-
|
|
272
|
+
function renderModules(mods){
|
|
273
|
+
if(!mods||!mods.length){
|
|
274
|
+
modPnl.innerHTML='<div class="empty"><div class="ic">\u2699</div>No module data</div>';
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
modPnl.innerHTML=mods.map(m=>{
|
|
278
|
+
const cls=m.active?'active':m.error?'error':'idle';
|
|
279
|
+
const label=m.active?'Active':m.error?'Error':'Idle';
|
|
280
|
+
const extra=m.stats?' ('+m.stats+')':'';
|
|
281
|
+
return '<div class="mod"><div class="mdot '+cls+'"></div><span class="mname">'+esc(m.name)+'</span><span class="mstat">'+label+extra+'</span></div>';
|
|
282
|
+
}).join('');
|
|
283
|
+
const active=mods.filter(m=>m.active).length;
|
|
284
|
+
pModules.textContent=active+'/'+mods.length+' modules';
|
|
285
|
+
$('sModules').textContent=active;
|
|
234
286
|
}
|
|
235
287
|
|
|
236
|
-
function
|
|
237
|
-
|
|
238
|
-
return \`<span class="tag" style="color:\${colors[t]||'#8b949e'}">\${t}</span>\`;
|
|
239
|
-
}
|
|
288
|
+
function ptag(p){return '<span class="tag '+p+'">'+p.toUpperCase()+'</span>'}
|
|
289
|
+
function ttag(t){const c={warning:'var(--red)',suggestion:'var(--blue)',review:'var(--purple)',insight:'var(--cyan)'};return '<span class="tag" style="color:'+(c[t]||'var(--text2)')+'">'+t+'</span>'}
|
|
240
290
|
|
|
241
|
-
function
|
|
242
|
-
const files
|
|
243
|
-
return
|
|
244
|
-
<div class="insight-title">\${escapeHtml(insight.title)}</div>
|
|
245
|
-
<div class="insight-meta">
|
|
246
|
-
\${priorityTag(insight.priority)}
|
|
247
|
-
\${typeTag(insight.type)}
|
|
248
|
-
\${files}
|
|
249
|
-
</div>
|
|
250
|
-
<div class="insight-content">\${escapeHtml(insight.content)}</div>
|
|
251
|
-
</div>\`;
|
|
291
|
+
function renderIns(ins){
|
|
292
|
+
const files=(ins.files||[]).map(f=>'<code style="font-size:9px;background:var(--bg4);padding:1px 4px;border-radius:2px">'+esc(f)+'</code>').join(' ');
|
|
293
|
+
return '<div class="ins '+ins.priority+'"><div class="ins-t">'+esc(ins.title)+'</div><div class="ins-m">'+ptag(ins.priority)+' '+ttag(ins.type)+' '+files+'</div><div class="ins-c">'+esc(ins.content)+'</div></div>';
|
|
252
294
|
}
|
|
253
295
|
|
|
254
|
-
function addInsights(
|
|
255
|
-
if
|
|
256
|
-
if
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
296
|
+
function addInsights(ins){
|
|
297
|
+
if(!ins||!ins.length)return;
|
|
298
|
+
if(insPnl.querySelector('.empty'))insPnl.innerHTML='';
|
|
299
|
+
allIns=[...ins,...allIns].slice(0,MAX_INS);
|
|
300
|
+
insPnl.innerHTML=allIns.map(renderIns).join('');
|
|
301
|
+
insCnt+=ins.length;
|
|
302
|
+
$('iCnt').textContent=insCnt;
|
|
303
|
+
$('sInsights').textContent=insCnt;
|
|
262
304
|
}
|
|
263
305
|
|
|
264
|
-
function
|
|
265
|
-
|
|
306
|
+
function renderFixes(fixes){
|
|
307
|
+
if(!fixes||!fixes.length)return;
|
|
308
|
+
if(fixPnl.querySelector('.empty'))fixPnl.innerHTML='';
|
|
309
|
+
fixPnl.innerHTML=fixes.map(f=>'<div class="fix"><div class="fix-t">'+esc(f.issue)+'</div><div class="fix-f">\u{1F4C4} '+esc(f.file)+' <span class="tag">'+f.confidence+'</span></div><div class="fl bef">Before</div><pre class="cb bef">'+esc(f.before)+'</pre><div class="fl aft" style="margin-top:6px">After</div><pre class="cb aft">'+esc(f.after)+'</pre><div class="fix-x">\u{1F4A1} '+esc(f.explanation)+'</div></div>').join('');
|
|
310
|
+
fixCnt=fixes.length;
|
|
311
|
+
$('fCnt').textContent=fixCnt;
|
|
312
|
+
$('sFixes').textContent=fixCnt;
|
|
266
313
|
}
|
|
267
314
|
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
<div class="fix-label before">Before</div>
|
|
273
|
-
<pre class="code-block code-before">\${escapeHtml(fix.before)}</pre>
|
|
274
|
-
<div class="fix-label after" style="margin-top:8px">After</div>
|
|
275
|
-
<pre class="code-block code-after">\${escapeHtml(fix.after)}</pre>
|
|
276
|
-
<div class="fix-explanation">💡 \${escapeHtml(fix.explanation)}</div>
|
|
277
|
-
</div>\`;
|
|
315
|
+
function triggerAnalysis(){
|
|
316
|
+
fetch('/api/trigger-analysis',{method:'POST'}).then(r=>r.json()).then(d=>{
|
|
317
|
+
// acknowledged
|
|
318
|
+
}).catch(()=>{});
|
|
278
319
|
}
|
|
279
320
|
|
|
280
|
-
function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
321
|
+
function refreshAll(){
|
|
322
|
+
fetch('/api/status').then(r=>r.json()).then(d=>{
|
|
323
|
+
if(d.agents)pAgents.textContent=d.agents.join(', ');
|
|
324
|
+
if(d.projectDir)pProject.textContent=d.projectDir;
|
|
325
|
+
if(d.turboMemoryStats){
|
|
326
|
+
const ts=d.turboMemoryStats;
|
|
327
|
+
$('sTurbo').textContent=(ts.compressionRatio||0).toFixed(1)+'x';
|
|
328
|
+
}
|
|
329
|
+
if(d.evolutionGeneration!==undefined)$('sEvol').textContent=d.evolutionGeneration;
|
|
330
|
+
if(d.swarmConvergence!==undefined)$('sSwarm').textContent=Math.round(d.swarmConvergence*100)+'%';
|
|
331
|
+
}).catch(()=>{});
|
|
332
|
+
fetch('/api/modules').then(r=>r.json()).then(renderModules).catch(()=>{});
|
|
333
|
+
fetch('/api/memory').then(r=>r.json()).then(renderMemory).catch(()=>{});
|
|
334
|
+
fetch('/api/tools').then(r=>r.json()).then(renderTools).catch(()=>{});
|
|
287
335
|
}
|
|
288
336
|
|
|
289
|
-
function connect()
|
|
290
|
-
const proto
|
|
291
|
-
const ws
|
|
292
|
-
|
|
293
|
-
ws.
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
ws.onclose = () => {
|
|
299
|
-
statusDot.classList.remove('connected');
|
|
300
|
-
log('Disconnected — reconnecting in 3s…', 'warning');
|
|
301
|
-
setTimeout(connect, 3000);
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
ws.onerror = () => {
|
|
305
|
-
log('WebSocket error', 'error');
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
ws.onmessage = (ev) => {
|
|
309
|
-
let msg;
|
|
310
|
-
try { msg = JSON.parse(ev.data); } catch { return; }
|
|
311
|
-
|
|
312
|
-
switch (msg.type) {
|
|
337
|
+
function connect(){
|
|
338
|
+
const proto=location.protocol==='https:'?'wss:':'ws:';
|
|
339
|
+
const ws=new WebSocket(proto+'//'+location.host);
|
|
340
|
+
ws.onopen=()=>{dot.classList.add('on');refreshAll()};
|
|
341
|
+
ws.onclose=()=>{dot.classList.remove('on');setTimeout(connect,3000)};
|
|
342
|
+
ws.onerror=()=>{};
|
|
343
|
+
ws.onmessage=ev=>{
|
|
344
|
+
let m;try{m=JSON.parse(ev.data)}catch{return}
|
|
345
|
+
switch(m.type){
|
|
313
346
|
case 'init':
|
|
314
|
-
|
|
315
|
-
if
|
|
316
|
-
badgeAgents.textContent = msg.agents.join(', ');
|
|
317
|
-
}
|
|
318
|
-
log(\`Project: \${msg.projectDir}\`, 'info');
|
|
347
|
+
if(m.projectDir)pProject.textContent=m.projectDir;
|
|
348
|
+
if(m.agents&&m.agents.length)pAgents.textContent=m.agents.join(', ');
|
|
319
349
|
break;
|
|
320
350
|
case 'agents-detected':
|
|
321
|
-
if
|
|
322
|
-
badgeAgents.textContent = msg.agents.join(', ');
|
|
323
|
-
log(\`Agents: \${msg.agents.join(', ')}\`, 'info');
|
|
324
|
-
}
|
|
325
|
-
break;
|
|
326
|
-
case 'analysis-start':
|
|
327
|
-
log(\`Analysis started — \${msg.changeCount} change(s)\`, 'info');
|
|
328
|
-
break;
|
|
329
|
-
case 'insights':
|
|
330
|
-
addInsights(msg.insights);
|
|
331
|
-
break;
|
|
332
|
-
case 'health-score':
|
|
333
|
-
renderHealth(msg.score);
|
|
334
|
-
break;
|
|
335
|
-
case 'fixes':
|
|
336
|
-
renderFixes(msg.fixes);
|
|
351
|
+
if(m.agents&&m.agents.length)pAgents.textContent=m.agents.join(', ');
|
|
337
352
|
break;
|
|
338
|
-
case '
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
case '
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
case '
|
|
345
|
-
|
|
353
|
+
case 'analysis-start':break;
|
|
354
|
+
case 'insights':addInsights(m.insights);break;
|
|
355
|
+
case 'health-score':renderHealth(m.score);break;
|
|
356
|
+
case 'fixes':renderFixes(m.fixes);break;
|
|
357
|
+
case 'injection':break;
|
|
358
|
+
case 'modules':renderModules(m.modules);break;
|
|
359
|
+
case 'memory':renderMemory(m.memory);break;
|
|
360
|
+
case 'tools':renderTools(m.tools);break;
|
|
361
|
+
case 'stats':
|
|
362
|
+
if(m.turboCompression)$('sTurbo').textContent=m.turboCompression.toFixed(1)+'x';
|
|
363
|
+
if(m.evolutionGen!==undefined)$('sEvol').textContent=m.evolutionGen;
|
|
364
|
+
if(m.swarmConv!==undefined)$('sSwarm').textContent=Math.round(m.swarmConv*100)+'%';
|
|
346
365
|
break;
|
|
366
|
+
case 'info':break;
|
|
367
|
+
case 'error':break;
|
|
347
368
|
}
|
|
348
369
|
};
|
|
349
370
|
}
|
|
350
|
-
|
|
351
371
|
connect();
|
|
352
372
|
</script>
|
|
353
373
|
</body>
|
|
@@ -362,42 +382,143 @@ export class DashboardServer {
|
|
|
362
382
|
host: options.host ?? 'localhost',
|
|
363
383
|
openBrowser: options.openBrowser ?? false,
|
|
364
384
|
};
|
|
365
|
-
// HTTP server — serves the embedded HTML client
|
|
366
385
|
this.server = http.createServer((req, res) => {
|
|
367
|
-
|
|
368
|
-
res.writeHead(200, {
|
|
369
|
-
'Content-Type': 'text/html; charset=utf-8',
|
|
370
|
-
'Cache-Control': 'no-cache',
|
|
371
|
-
});
|
|
372
|
-
res.end(HTML_CLIENT);
|
|
373
|
-
}
|
|
374
|
-
else if (req.url === '/api/status') {
|
|
375
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
376
|
-
res.end(JSON.stringify(this.orchestrator.getStatus(), null, 2));
|
|
377
|
-
}
|
|
378
|
-
else if (req.url === '/api/health') {
|
|
379
|
-
const h = this.orchestrator.getLastHealthScore();
|
|
380
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
381
|
-
res.end(JSON.stringify(h, null, 2));
|
|
382
|
-
}
|
|
383
|
-
else if (req.url === '/api/fixes') {
|
|
384
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
385
|
-
res.end(JSON.stringify(this.orchestrator.getLastFixes(), null, 2));
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
res.writeHead(404);
|
|
389
|
-
res.end('Not found');
|
|
390
|
-
}
|
|
386
|
+
this.handleRequest(req, res);
|
|
391
387
|
});
|
|
392
|
-
// WebSocket server on the same HTTP server
|
|
393
388
|
this.wss = new WebSocketServer({ server: this.server });
|
|
394
389
|
this.setupWebSocket();
|
|
395
390
|
this.subscribeToOrchestrator();
|
|
396
391
|
}
|
|
392
|
+
handleRequest(req, res) {
|
|
393
|
+
const url = req.url || '/';
|
|
394
|
+
const send = (data, status = 200) => {
|
|
395
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
396
|
+
res.end(JSON.stringify(data, null, 2));
|
|
397
|
+
};
|
|
398
|
+
if (url === '/' || url === '/index.html') {
|
|
399
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' });
|
|
400
|
+
res.end(HTML_CLIENT);
|
|
401
|
+
}
|
|
402
|
+
else if (url === '/api/status') {
|
|
403
|
+
send(this.orchestrator.getStatus());
|
|
404
|
+
}
|
|
405
|
+
else if (url === '/api/health') {
|
|
406
|
+
send(this.orchestrator.getLastHealthScore());
|
|
407
|
+
}
|
|
408
|
+
else if (url === '/api/fixes') {
|
|
409
|
+
send(this.orchestrator.getLastFixes());
|
|
410
|
+
}
|
|
411
|
+
else if (url === '/api/modules') {
|
|
412
|
+
send(this.getModulesData());
|
|
413
|
+
}
|
|
414
|
+
else if (url === '/api/memory') {
|
|
415
|
+
send(this.getMemoryData());
|
|
416
|
+
}
|
|
417
|
+
else if (url === '/api/tools') {
|
|
418
|
+
send(this.getToolsData());
|
|
419
|
+
}
|
|
420
|
+
else if (url === '/api/trigger-analysis' && req.method === 'POST') {
|
|
421
|
+
// Trigger a fresh analysis
|
|
422
|
+
this.orchestrator.reviewOnce().then(() => {
|
|
423
|
+
send({ triggered: true });
|
|
424
|
+
}).catch((err) => {
|
|
425
|
+
send({ triggered: false, error: err.message }, 500);
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
res.writeHead(404);
|
|
430
|
+
res.end('Not found');
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
getModulesData() {
|
|
434
|
+
const status = this.orchestrator.getStatus();
|
|
435
|
+
const modules = [];
|
|
436
|
+
const moduleList = [
|
|
437
|
+
'hierarchicalMemory', 'patternMemory', 'learningEngine', 'neuralMesh',
|
|
438
|
+
'consensusState', 'recallState', 'collectiveRules', 'turboMemory',
|
|
439
|
+
'knowledgeGraph', 'swarmState', 'evolutionState', 'adversarialDefense',
|
|
440
|
+
'crossAgentProtocol',
|
|
441
|
+
];
|
|
442
|
+
const moduleNames = {
|
|
443
|
+
hierarchicalMemory: 'Hierarchical Memory (4-tier)',
|
|
444
|
+
patternMemory: 'Pattern Memory',
|
|
445
|
+
learningEngine: 'Learning Engine',
|
|
446
|
+
neuralMesh: 'Neural Mesh (SSSP)',
|
|
447
|
+
consensusState: 'Consensus Protocol',
|
|
448
|
+
recallState: 'Context Recall',
|
|
449
|
+
collectiveRules: 'Collective Learning',
|
|
450
|
+
turboMemory: 'TurboQuant Memory',
|
|
451
|
+
knowledgeGraph: 'Knowledge Graph (PageRank)',
|
|
452
|
+
swarmState: 'Swarm Intelligence (ACO)',
|
|
453
|
+
evolutionState: 'Self-Evolution (Genetic)',
|
|
454
|
+
adversarialDefense: 'Adversarial Defense',
|
|
455
|
+
crossAgentProtocol: 'Cross-Agent (CAIP)',
|
|
456
|
+
};
|
|
457
|
+
for (const key of moduleList) {
|
|
458
|
+
const active = status[key] !== null && status[key] !== undefined;
|
|
459
|
+
let stats;
|
|
460
|
+
const val = status[key];
|
|
461
|
+
if (val && typeof val === 'object') {
|
|
462
|
+
if (typeof val.count === 'number')
|
|
463
|
+
stats = String(val.count) + ' entries';
|
|
464
|
+
else if (typeof val.size === 'number')
|
|
465
|
+
stats = String(val.size) + ' items';
|
|
466
|
+
else if (typeof val.entityCount === 'number')
|
|
467
|
+
stats = String(val.entityCount) + ' entities';
|
|
468
|
+
}
|
|
469
|
+
modules.push({
|
|
470
|
+
name: moduleNames[key] || key,
|
|
471
|
+
active,
|
|
472
|
+
error: false,
|
|
473
|
+
stats,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
return modules;
|
|
477
|
+
}
|
|
478
|
+
getMemoryData() {
|
|
479
|
+
try {
|
|
480
|
+
const status = this.orchestrator.getStatus();
|
|
481
|
+
const hm = status.hierarchicalMemory;
|
|
482
|
+
return {
|
|
483
|
+
raw: hm?.rawCount ?? 0,
|
|
484
|
+
summary: hm?.summaryCount ?? 0,
|
|
485
|
+
pattern: hm?.patternCount ?? 0,
|
|
486
|
+
principle: hm?.principleCount ?? 0,
|
|
487
|
+
turboCompression: status.turboMemoryStats?.compressionRatio ?? 0,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
return { raw: 0, summary: 0, pattern: 0, principle: 0, turboCompression: 0 };
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
getToolsData() {
|
|
495
|
+
try {
|
|
496
|
+
const status = this.orchestrator.getStatus();
|
|
497
|
+
const agents = status.agents || [];
|
|
498
|
+
const knownTools = [
|
|
499
|
+
{ id: 'claude_code', name: 'Claude Code', configDir: '.claude' },
|
|
500
|
+
{ id: 'cursor', name: 'Cursor', configDir: '.cursor' },
|
|
501
|
+
{ id: 'kilo_code', name: 'Kilo Code', configDir: '.kilocode' },
|
|
502
|
+
{ id: 'cline', name: 'Cline', configDir: '.cline' },
|
|
503
|
+
{ id: 'opencode', name: 'OpenCode', configDir: '.opencode' },
|
|
504
|
+
{ id: 'codex', name: 'Codex', configDir: '.codex' },
|
|
505
|
+
{ id: 'roo', name: 'Roo Code', configDir: '.roo' },
|
|
506
|
+
{ id: 'aider', name: 'Aider', configDir: '.aider' },
|
|
507
|
+
];
|
|
508
|
+
return knownTools.map(t => ({
|
|
509
|
+
id: t.id,
|
|
510
|
+
name: t.name,
|
|
511
|
+
connected: agents.some(a => a.toLowerCase().includes(t.name.toLowerCase())),
|
|
512
|
+
detected: agents.some(a => a.toLowerCase().includes(t.name.toLowerCase())),
|
|
513
|
+
}));
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
return [];
|
|
517
|
+
}
|
|
518
|
+
}
|
|
397
519
|
setupWebSocket() {
|
|
398
520
|
this.wss.on('connection', (ws) => {
|
|
399
521
|
this.clients.add(ws);
|
|
400
|
-
// Send init state immediately
|
|
401
522
|
const status = this.orchestrator.getStatus();
|
|
402
523
|
this.send(ws, {
|
|
403
524
|
type: 'init',
|
|
@@ -407,22 +528,25 @@ export class DashboardServer {
|
|
|
407
528
|
provider: status.provider,
|
|
408
529
|
model: status.model,
|
|
409
530
|
});
|
|
410
|
-
// Send cached health score if available
|
|
411
531
|
const health = this.orchestrator.getLastHealthScore();
|
|
412
|
-
if (health)
|
|
532
|
+
if (health)
|
|
413
533
|
this.send(ws, { type: 'health-score', score: health });
|
|
414
|
-
}
|
|
415
|
-
// Send cached fixes if available
|
|
416
534
|
const fixes = this.orchestrator.getLastFixes();
|
|
417
|
-
if (fixes.length > 0)
|
|
535
|
+
if (fixes.length > 0)
|
|
418
536
|
this.send(ws, { type: 'fixes', fixes });
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
|
|
537
|
+
// Send module/memory/tools data
|
|
538
|
+
this.send(ws, { type: 'modules', modules: this.getModulesData() });
|
|
539
|
+
this.send(ws, { type: 'memory', memory: this.getMemoryData() });
|
|
540
|
+
this.send(ws, { type: 'tools', tools: this.getToolsData() });
|
|
541
|
+
// Send stats
|
|
542
|
+
this.send(ws, {
|
|
543
|
+
type: 'stats',
|
|
544
|
+
turboCompression: status.turboMemoryStats?.compressionRatio ?? 0,
|
|
545
|
+
evolutionGen: status.evolutionGeneration ?? 0,
|
|
546
|
+
swarmConv: status.swarmConvergence ?? 0,
|
|
425
547
|
});
|
|
548
|
+
ws.on('close', () => { this.clients.delete(ws); });
|
|
549
|
+
ws.on('error', () => { this.clients.delete(ws); });
|
|
426
550
|
});
|
|
427
551
|
}
|
|
428
552
|
subscribeToOrchestrator() {
|
|
@@ -431,12 +555,16 @@ export class DashboardServer {
|
|
|
431
555
|
type: 'agents-detected',
|
|
432
556
|
agents: adapters.map((a) => `${a.displayName} (${a.name})`),
|
|
433
557
|
});
|
|
558
|
+
this.broadcast({ type: 'tools', tools: this.getToolsData() });
|
|
434
559
|
});
|
|
435
560
|
this.orchestrator.on('analysis-start', ({ changeCount }) => {
|
|
436
561
|
this.broadcast({ type: 'analysis-start', changeCount });
|
|
437
562
|
});
|
|
438
563
|
this.orchestrator.on('insights', ({ insights }) => {
|
|
439
564
|
this.broadcast({ type: 'insights', insights });
|
|
565
|
+
// Update modules after insights
|
|
566
|
+
this.broadcast({ type: 'modules', modules: this.getModulesData() });
|
|
567
|
+
this.broadcast({ type: 'memory', memory: this.getMemoryData() });
|
|
440
568
|
});
|
|
441
569
|
this.orchestrator.on('health-score', ({ score }) => {
|
|
442
570
|
this.broadcast({ type: 'health-score', score });
|
|
@@ -445,12 +573,7 @@ export class DashboardServer {
|
|
|
445
573
|
this.broadcast({ type: 'fixes', fixes });
|
|
446
574
|
});
|
|
447
575
|
this.orchestrator.on('injection', ({ adapter, insight, success }) => {
|
|
448
|
-
this.broadcast({
|
|
449
|
-
type: 'injection',
|
|
450
|
-
adapter,
|
|
451
|
-
insightTitle: insight?.title || '',
|
|
452
|
-
success,
|
|
453
|
-
});
|
|
576
|
+
this.broadcast({ type: 'injection', adapter, insightTitle: insight?.title || '', success });
|
|
454
577
|
});
|
|
455
578
|
this.orchestrator.on('info', (message) => {
|
|
456
579
|
this.broadcast({ type: 'info', message });
|