ultra-dex 2.2.1 → 3.1.0
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 +84 -128
- package/assets/agents/00-AGENT_INDEX.md +1 -1
- package/assets/docs/LAUNCH-POSTS.md +1 -1
- package/assets/docs/QUICK-REFERENCE.md +9 -4
- package/assets/docs/VISION-V2.md +1 -1
- package/assets/hooks/pre-commit +98 -0
- package/assets/saas-plan/04-Imp-Template.md +1 -1
- package/bin/ultra-dex.js +95 -4
- package/lib/commands/advanced.js +471 -0
- package/lib/commands/agent-builder.js +226 -0
- package/lib/commands/agents.js +99 -42
- package/lib/commands/auto-implement.js +68 -0
- package/lib/commands/build.js +73 -187
- package/lib/commands/ci-monitor.js +84 -0
- package/lib/commands/config.js +207 -0
- package/lib/commands/dashboard.js +770 -0
- package/lib/commands/diff.js +233 -0
- package/lib/commands/doctor.js +397 -0
- package/lib/commands/export.js +408 -0
- package/lib/commands/fix.js +96 -0
- package/lib/commands/generate.js +96 -72
- package/lib/commands/hooks.js +251 -76
- package/lib/commands/init.js +53 -1
- package/lib/commands/memory.js +80 -0
- package/lib/commands/plan.js +82 -0
- package/lib/commands/review.js +34 -5
- package/lib/commands/run.js +233 -0
- package/lib/commands/serve.js +177 -146
- package/lib/commands/state.js +354 -0
- package/lib/commands/swarm.js +284 -0
- package/lib/commands/sync.js +82 -23
- package/lib/commands/team.js +275 -0
- package/lib/commands/upgrade.js +190 -0
- package/lib/commands/validate.js +34 -0
- package/lib/commands/verify.js +81 -0
- package/lib/commands/watch.js +79 -0
- package/lib/mcp/graph.js +92 -0
- package/lib/mcp/memory.js +95 -0
- package/lib/mcp/resources.js +152 -0
- package/lib/mcp/server.js +34 -0
- package/lib/mcp/tools.js +481 -0
- package/lib/mcp/websocket.js +117 -0
- package/lib/providers/index.js +49 -4
- package/lib/providers/ollama.js +136 -0
- package/lib/providers/router.js +63 -0
- package/lib/quality/scanner.js +128 -0
- package/lib/swarm/coordinator.js +97 -0
- package/lib/swarm/index.js +598 -0
- package/lib/swarm/protocol.js +677 -0
- package/lib/swarm/tiers.js +485 -0
- package/lib/templates/custom-agent.md +10 -0
- package/lib/utils/files.js +14 -0
- package/lib/utils/graph.js +108 -0
- package/package.json +22 -13
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ultra-dex dashboard command
|
|
3
|
+
* Local web dashboard for monitoring Ultra-Dex projects (GOD MODE)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import http from 'http';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
10
|
+
import { execSync, spawn } from 'child_process';
|
|
11
|
+
import { loadState } from './plan.js';
|
|
12
|
+
import { buildGraph } from '../utils/graph.js';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
|
|
15
|
+
// Global clients for SSE
|
|
16
|
+
const clients = new Set();
|
|
17
|
+
|
|
18
|
+
// Action history for timeline
|
|
19
|
+
const actionHistory = [];
|
|
20
|
+
const MAX_HISTORY = 50;
|
|
21
|
+
|
|
22
|
+
function addAction(type, message, agent = null) {
|
|
23
|
+
actionHistory.unshift({
|
|
24
|
+
timestamp: new Date().toISOString(),
|
|
25
|
+
type,
|
|
26
|
+
message,
|
|
27
|
+
agent
|
|
28
|
+
});
|
|
29
|
+
if (actionHistory.length > MAX_HISTORY) actionHistory.pop();
|
|
30
|
+
sendToClients({ type: 'action', action: actionHistory[0] });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function sendToClients(data) {
|
|
34
|
+
const payload = `data: ${JSON.stringify(data)}\n\n`;
|
|
35
|
+
clients.forEach(client => client.res.write(payload));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function getGitInfo() {
|
|
39
|
+
try {
|
|
40
|
+
const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
41
|
+
const lastCommit = execSync('git log -1 --format="%h %s" 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
42
|
+
const status = execSync('git status --porcelain 2>/dev/null', { encoding: 'utf8' });
|
|
43
|
+
const changedFiles = status.split('\n').filter(l => l.trim()).length;
|
|
44
|
+
return { branch, lastCommit, changedFiles };
|
|
45
|
+
} catch {
|
|
46
|
+
return { branch: 'unknown', lastCommit: 'N/A', changedFiles: 0 };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function generateDashboardHTML(state, gitInfo, graphSummary) {
|
|
51
|
+
const phasesHTML = state.phases.map(phase => {
|
|
52
|
+
const statusClass = phase.status;
|
|
53
|
+
const progress = (phase.steps.filter(s => s.status === 'completed').length / phase.steps.length) * 100;
|
|
54
|
+
|
|
55
|
+
return `
|
|
56
|
+
<div class="card phase-card ${statusClass}">
|
|
57
|
+
<div class="phase-header">
|
|
58
|
+
<h3>${phase.name}</h3>
|
|
59
|
+
<span class="status-badge">${phase.status.replace('_', ' ')}</span>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="progress-mini"><div class="fill" style="width: ${progress}%"></div></div>
|
|
62
|
+
<ul class="steps">
|
|
63
|
+
${phase.steps.map(step => `
|
|
64
|
+
<li class="${step.status}">
|
|
65
|
+
<span class="dot"></span>
|
|
66
|
+
${step.task}
|
|
67
|
+
</li>
|
|
68
|
+
`).join('')}
|
|
69
|
+
</ul>
|
|
70
|
+
</div>
|
|
71
|
+
`;
|
|
72
|
+
}).join('');
|
|
73
|
+
|
|
74
|
+
const agentsHTML = state.agents.registry.map(agent => `
|
|
75
|
+
<div class="agent-card" id="agent-${agent}">
|
|
76
|
+
<div class="agent-header">
|
|
77
|
+
<span class="agent-name">@${agent}</span>
|
|
78
|
+
<span class="agent-status status-idle">IDLE</span>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="agent-activity">Waiting for tasks...</div>
|
|
81
|
+
</div>
|
|
82
|
+
`).join('');
|
|
83
|
+
|
|
84
|
+
// Calculate alignment score dynamically
|
|
85
|
+
const totalSteps = state.phases.reduce((sum, p) => sum + p.steps.length, 0);
|
|
86
|
+
const completedSteps = state.phases.reduce((sum, p) => sum + p.steps.filter(s => s.status === 'completed').length, 0);
|
|
87
|
+
const alignmentScore = totalSteps > 0 ? Math.round((completedSteps / totalSteps) * 100) : 0;
|
|
88
|
+
const scoreColor = alignmentScore >= 80 ? 'var(--success)' : alignmentScore >= 50 ? 'var(--warning)' : 'var(--danger)';
|
|
89
|
+
|
|
90
|
+
return `<!DOCTYPE html>
|
|
91
|
+
<html lang="en">
|
|
92
|
+
<head>
|
|
93
|
+
<meta charset="UTF-8">
|
|
94
|
+
<title>ULTRA-DEX KERNEL • ${state.project.name}</title>
|
|
95
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
96
|
+
<style>
|
|
97
|
+
:root {
|
|
98
|
+
--bg: #09090b;
|
|
99
|
+
--card: #18181b;
|
|
100
|
+
--accent: #06b6d4;
|
|
101
|
+
--text: #fafafa;
|
|
102
|
+
--text-dim: #a1a1aa;
|
|
103
|
+
--success: #22c55e;
|
|
104
|
+
--warning: #eab308;
|
|
105
|
+
--pending: #3f3f46;
|
|
106
|
+
--danger: #ef4444;
|
|
107
|
+
}
|
|
108
|
+
.light-theme {
|
|
109
|
+
--bg: #f8fafc;
|
|
110
|
+
--card: #ffffff;
|
|
111
|
+
--text: #0f172a;
|
|
112
|
+
--text-dim: #64748b;
|
|
113
|
+
--pending: #e2e8f0;
|
|
114
|
+
}
|
|
115
|
+
* { margin:0; padding:0; box-sizing:border-box; }
|
|
116
|
+
body {
|
|
117
|
+
background: var(--bg);
|
|
118
|
+
color: var(--text);
|
|
119
|
+
font-family: 'Inter', system-ui, sans-serif;
|
|
120
|
+
padding: 2rem;
|
|
121
|
+
line-height: 1.5;
|
|
122
|
+
transition: background 0.3s, color 0.3s;
|
|
123
|
+
}
|
|
124
|
+
.header { margin-bottom: 2rem; border-left: 4px solid var(--accent); padding-left: 1.5rem; display: flex; justify-content: space-between; align-items: end; }
|
|
125
|
+
.header h1 { font-size: 2.5rem; letter-spacing: -0.05em; text-transform: uppercase; }
|
|
126
|
+
.header p { color: var(--text-dim); font-family: monospace; }
|
|
127
|
+
.header-controls { display: flex; gap: 0.5rem; align-items: center; }
|
|
128
|
+
|
|
129
|
+
/* Toolbar */
|
|
130
|
+
.toolbar {
|
|
131
|
+
display: flex;
|
|
132
|
+
gap: 0.5rem;
|
|
133
|
+
margin-bottom: 1.5rem;
|
|
134
|
+
flex-wrap: wrap;
|
|
135
|
+
padding: 1rem;
|
|
136
|
+
background: var(--card);
|
|
137
|
+
border-radius: 0.5rem;
|
|
138
|
+
border: 1px solid #27272a;
|
|
139
|
+
}
|
|
140
|
+
.toolbar button { padding: 0.5rem 1rem; font-size: 0.75rem; }
|
|
141
|
+
.toolbar button.secondary { background: #27272a; color: var(--text); }
|
|
142
|
+
.toolbar button.secondary:hover { background: #3f3f46; }
|
|
143
|
+
.toolbar .spacer { flex: 1; }
|
|
144
|
+
|
|
145
|
+
/* Theme Toggle */
|
|
146
|
+
.theme-toggle {
|
|
147
|
+
background: #27272a;
|
|
148
|
+
color: var(--text);
|
|
149
|
+
border: none;
|
|
150
|
+
padding: 0.5rem 0.75rem;
|
|
151
|
+
border-radius: 0.5rem;
|
|
152
|
+
cursor: pointer;
|
|
153
|
+
font-size: 1rem;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.dashboard-grid { display: grid; grid-template-columns: 350px 1fr 300px; gap: 1.5rem; }
|
|
157
|
+
|
|
158
|
+
.card {
|
|
159
|
+
background: var(--card);
|
|
160
|
+
border: 1px solid #27272a;
|
|
161
|
+
border-radius: 0.75rem;
|
|
162
|
+
padding: 1.5rem;
|
|
163
|
+
position: relative;
|
|
164
|
+
overflow: hidden;
|
|
165
|
+
margin-bottom: 1rem;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.phase-card.completed { border-color: var(--success); }
|
|
169
|
+
.phase-card.in_progress { border-color: var(--accent); }
|
|
170
|
+
|
|
171
|
+
.phase-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; }
|
|
172
|
+
.status-badge { font-size: 0.65rem; text-transform: uppercase; background: #27272a; padding: 2px 8px; border-radius: 4px; color: var(--text-dim); }
|
|
173
|
+
|
|
174
|
+
.progress-mini { height: 4px; background: #27272a; border-radius: 2px; margin-bottom: 1.5rem; }
|
|
175
|
+
.progress-mini .fill { height: 100%; background: var(--accent); border-radius: 2px; transition: width 0.3s; }
|
|
176
|
+
|
|
177
|
+
.steps { list-style: none; }
|
|
178
|
+
.steps li { font-size: 0.85rem; color: var(--text-dim); margin-bottom: 0.5rem; display: flex; align-items: center; }
|
|
179
|
+
.steps li.completed { color: var(--text); }
|
|
180
|
+
.steps li.completed .dot { background: var(--success); box-shadow: 0 0 8px var(--success); }
|
|
181
|
+
.steps li .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--pending); margin-right: 12px; }
|
|
182
|
+
|
|
183
|
+
/* Agent Panel */
|
|
184
|
+
.agent-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
|
|
185
|
+
.agent-card { background: #202022; padding: 0.75rem; border-radius: 0.5rem; border: 1px solid #333; transition: border-color 0.3s; }
|
|
186
|
+
.agent-card.active { border-color: var(--accent); }
|
|
187
|
+
.agent-header { display: flex; justify-content: space-between; margin-bottom: 0.5rem; }
|
|
188
|
+
.agent-name { font-family: monospace; font-size: 0.8rem; color: var(--accent); }
|
|
189
|
+
.agent-status { font-size: 0.6rem; padding: 2px 6px; border-radius: 4px; text-transform: uppercase; }
|
|
190
|
+
.status-idle { background: #333; color: #888; }
|
|
191
|
+
.status-working { background: rgba(6, 182, 212, 0.2); color: var(--accent); animation: pulse 2s infinite; }
|
|
192
|
+
.agent-activity { font-size: 0.7rem; color: #888; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
193
|
+
.agent-last-action { font-size: 0.65rem; color: #555; margin-top: 0.25rem; }
|
|
194
|
+
|
|
195
|
+
/* Timeline */
|
|
196
|
+
.timeline { max-height: 300px; overflow-y: auto; font-family: monospace; font-size: 0.8rem; }
|
|
197
|
+
.log-entry { margin-bottom: 0.5rem; border-left: 2px solid #333; padding-left: 0.5rem; color: #888; }
|
|
198
|
+
.log-entry.info { border-color: var(--accent); color: #ccc; }
|
|
199
|
+
.log-entry.success { border-color: var(--success); color: var(--success); }
|
|
200
|
+
.log-entry.error { border-color: var(--danger); color: var(--danger); }
|
|
201
|
+
.log-entry .time { color: #555; margin-right: 0.5rem; }
|
|
202
|
+
|
|
203
|
+
/* Recent Actions Timeline */
|
|
204
|
+
.actions-timeline { max-height: 200px; overflow-y: auto; }
|
|
205
|
+
.action-item {
|
|
206
|
+
display: flex;
|
|
207
|
+
align-items: center;
|
|
208
|
+
gap: 0.75rem;
|
|
209
|
+
padding: 0.5rem 0;
|
|
210
|
+
border-bottom: 1px solid #27272a;
|
|
211
|
+
font-size: 0.8rem;
|
|
212
|
+
}
|
|
213
|
+
.action-icon { font-size: 1rem; }
|
|
214
|
+
.action-details { flex: 1; }
|
|
215
|
+
.action-message { color: var(--text); }
|
|
216
|
+
.action-time { color: var(--text-dim); font-size: 0.7rem; }
|
|
217
|
+
|
|
218
|
+
/* Chart */
|
|
219
|
+
.chart-container { position: relative; height: 200px; width: 100%; }
|
|
220
|
+
|
|
221
|
+
.control-panel {
|
|
222
|
+
padding: 1.5rem;
|
|
223
|
+
background: rgba(6, 182, 212, 0.05);
|
|
224
|
+
border: 1px solid var(--accent);
|
|
225
|
+
border-radius: 0.75rem;
|
|
226
|
+
margin-bottom: 1.5rem;
|
|
227
|
+
}
|
|
228
|
+
.input-group { display: flex; gap: 1rem; margin-top: 1rem; }
|
|
229
|
+
input[type="text"] {
|
|
230
|
+
flex: 1;
|
|
231
|
+
background: #000;
|
|
232
|
+
border: 1px solid #333;
|
|
233
|
+
padding: 0.75rem 1rem;
|
|
234
|
+
color: #fff;
|
|
235
|
+
border-radius: 0.5rem;
|
|
236
|
+
font-family: monospace;
|
|
237
|
+
}
|
|
238
|
+
.light-theme input[type="text"] { background: #f1f5f9; color: #0f172a; border-color: #e2e8f0; }
|
|
239
|
+
button {
|
|
240
|
+
background: var(--accent);
|
|
241
|
+
color: #000;
|
|
242
|
+
border: none;
|
|
243
|
+
padding: 0 1.5rem;
|
|
244
|
+
border-radius: 0.5rem;
|
|
245
|
+
font-weight: bold;
|
|
246
|
+
cursor: pointer;
|
|
247
|
+
text-transform: uppercase;
|
|
248
|
+
font-size: 0.8rem;
|
|
249
|
+
}
|
|
250
|
+
button:hover { opacity: 0.9; }
|
|
251
|
+
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
252
|
+
|
|
253
|
+
@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } }
|
|
254
|
+
|
|
255
|
+
/* Toast Notifications */
|
|
256
|
+
.toast {
|
|
257
|
+
position: fixed;
|
|
258
|
+
bottom: 2rem;
|
|
259
|
+
right: 2rem;
|
|
260
|
+
background: var(--card);
|
|
261
|
+
border: 1px solid var(--accent);
|
|
262
|
+
padding: 1rem 1.5rem;
|
|
263
|
+
border-radius: 0.5rem;
|
|
264
|
+
display: none;
|
|
265
|
+
animation: slideIn 0.3s;
|
|
266
|
+
}
|
|
267
|
+
.toast.show { display: block; }
|
|
268
|
+
@keyframes slideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }
|
|
269
|
+
</style>
|
|
270
|
+
</head>
|
|
271
|
+
<body>
|
|
272
|
+
<div class="header">
|
|
273
|
+
<div>
|
|
274
|
+
<h1>${state.project.name} <small style="font-size: 0.4em; vertical-align: middle; color: var(--accent)">GOD MODE</small></h1>
|
|
275
|
+
<p>KERNEL v${state.project.version} • LOCALHOST:${gitInfo.branch} • <span id="clock">${new Date().toLocaleTimeString()}</span></p>
|
|
276
|
+
</div>
|
|
277
|
+
<div class="header-controls">
|
|
278
|
+
<button class="theme-toggle" onclick="toggleTheme()" title="Toggle Theme">🌓</button>
|
|
279
|
+
<div style="text-align: right; margin-left: 1rem;">
|
|
280
|
+
<div id="score-display" style="font-size: 2rem; font-weight: bold; color: ${scoreColor}">${alignmentScore}%</div>
|
|
281
|
+
<div style="font-size: 0.8rem; color: #666">ALIGNMENT SCORE</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
<!-- Quick Action Toolbar -->
|
|
287
|
+
<div class="toolbar">
|
|
288
|
+
<button onclick="runAction('generate')" title="Generate code from plan">⚡ Generate</button>
|
|
289
|
+
<button onclick="runAction('build')" title="Build the project">🔨 Build</button>
|
|
290
|
+
<button onclick="runAction('review')" title="Run code review">🔍 Review</button>
|
|
291
|
+
<button onclick="runAction('validate')" title="Validate alignment">✅ Validate</button>
|
|
292
|
+
<button onclick="runAction('diff')" title="Check plan vs code diff">📊 Diff</button>
|
|
293
|
+
<span class="spacer"></span>
|
|
294
|
+
<button class="secondary" onclick="exportReport()" title="Export HTML report">📄 Export Report</button>
|
|
295
|
+
<button class="secondary" onclick="refreshDashboard()" title="Refresh data">🔄 Refresh</button>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div class="dashboard-grid">
|
|
299
|
+
<!-- LEFT: PHASES -->
|
|
300
|
+
<div class="col-phases">
|
|
301
|
+
<h3 style="color: #666; margin-bottom: 1rem; font-size: 0.8rem">IMPLEMENTATION PLAN</h3>
|
|
302
|
+
${phasesHTML}
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<!-- CENTER: MAIN -->
|
|
306
|
+
<div class="col-main">
|
|
307
|
+
<div class="control-panel">
|
|
308
|
+
<h3>🚀 SWARM COMMAND CENTER</h3>
|
|
309
|
+
<div class="input-group">
|
|
310
|
+
<input type="text" id="swarm-input" placeholder="Enter objective (e.g., 'Build user profile page')..." />
|
|
311
|
+
<button id="swarm-btn" onclick="startSwarm()">DEPLOY AGENTS</button>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
|
|
315
|
+
<div class="card">
|
|
316
|
+
<h3 style="margin-bottom: 1rem">ALIGNMENT VELOCITY</h3>
|
|
317
|
+
<div class="chart-container">
|
|
318
|
+
<canvas id="alignmentChart"></canvas>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
<div class="card">
|
|
323
|
+
<h3 style="margin-bottom: 1rem">RECENT ACTIONS</h3>
|
|
324
|
+
<div class="actions-timeline" id="actions-container">
|
|
325
|
+
<div class="action-item">
|
|
326
|
+
<span class="action-icon">🚀</span>
|
|
327
|
+
<div class="action-details">
|
|
328
|
+
<div class="action-message">Dashboard started</div>
|
|
329
|
+
<div class="action-time">${new Date().toLocaleTimeString()}</div>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
<div class="card">
|
|
336
|
+
<h3 style="margin-bottom: 1rem">LIVE SYSTEM LOGS</h3>
|
|
337
|
+
<div class="timeline" id="log-container">
|
|
338
|
+
<div class="log-entry info"><span class="time">${new Date().toLocaleTimeString()}</span> System initialized.</div>
|
|
339
|
+
<div class="log-entry info"><span class="time">${new Date().toLocaleTimeString()}</span> Neural link established.</div>
|
|
340
|
+
<div class="log-entry"><span class="time">${new Date().toLocaleTimeString()}</span> Waiting for agent activity...</div>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<!-- RIGHT: AGENTS & MEMORY -->
|
|
346
|
+
<div class="col-agents">
|
|
347
|
+
<h3 style="color: #666; margin-bottom: 1rem; font-size: 0.8rem">ACTIVE AGENTS</h3>
|
|
348
|
+
<div class="agent-grid">
|
|
349
|
+
${agentsHTML}
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<div class="card" style="margin-top: 1rem">
|
|
353
|
+
<h3 style="margin-bottom: 1rem">🧠 MEMORY BANK</h3>
|
|
354
|
+
<div id="memory-bank" class="timeline" style="max-height: 200px">
|
|
355
|
+
<div class="log-entry">Waiting for neural data...</div>
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<div class="card" style="margin-top: 1rem; font-family: monospace; font-size: 0.8rem">
|
|
360
|
+
<h3 style="margin-bottom: 0.5rem">SYSTEM STATUS</h3>
|
|
361
|
+
<div style="color: var(--success)">> git: ${gitInfo.changedFiles > 0 ? gitInfo.changedFiles + ' changes' : 'clean'}</div>
|
|
362
|
+
<div style="color: var(--accent)">> graph: ${graphSummary ? graphSummary.nodes + ' nodes' : 'scanning...'}</div>
|
|
363
|
+
<div style="color: #666">> uptime: <span id="uptime">0s</span></div>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
|
|
368
|
+
<!-- Toast Notification -->
|
|
369
|
+
<div class="toast" id="toast"></div>
|
|
370
|
+
|
|
371
|
+
<script>
|
|
372
|
+
// Fetch initial memory
|
|
373
|
+
async function loadMemory() {
|
|
374
|
+
try {
|
|
375
|
+
const res = await fetch('/api/memory');
|
|
376
|
+
const memories = await res.json();
|
|
377
|
+
const container = document.getElementById('memory-bank');
|
|
378
|
+
if (memories.length === 0) {
|
|
379
|
+
container.innerHTML = '<div class="log-entry">Memory is empty.</div>';
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
container.innerHTML = memories.map(m => '<div class="log-entry info">' +
|
|
383
|
+
'<span class="time">' + new Date(m.timestamp).toLocaleDateString() + '</span>' +
|
|
384
|
+
m.text +
|
|
385
|
+
'</div>').join('');
|
|
386
|
+
} catch (e) {
|
|
387
|
+
console.error('Failed to load memory');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
loadMemory();
|
|
391
|
+
|
|
392
|
+
// Initialize Chart
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
const ctx = document.getElementById('alignmentChart').getContext('2d');
|
|
396
|
+
const chart = new Chart(ctx, {
|
|
397
|
+
type: 'line',
|
|
398
|
+
data: {
|
|
399
|
+
labels: ['1h ago', '45m ago', '30m ago', '15m ago', 'Now'],
|
|
400
|
+
datasets: [{
|
|
401
|
+
label: 'Alignment Score',
|
|
402
|
+
data: [65, 68, 72, 85, 92],
|
|
403
|
+
borderColor: '#06b6d4',
|
|
404
|
+
tension: 0.4,
|
|
405
|
+
fill: true,
|
|
406
|
+
backgroundColor: 'rgba(6, 182, 212, 0.1)'
|
|
407
|
+
}]
|
|
408
|
+
},
|
|
409
|
+
options: {
|
|
410
|
+
responsive: true,
|
|
411
|
+
maintainAspectRatio: false,
|
|
412
|
+
plugins: { legend: { display: false } },
|
|
413
|
+
scales: {
|
|
414
|
+
y: { min: 0, max: 100, grid: { color: '#333' } },
|
|
415
|
+
x: { grid: { display: false } }
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Real-time Sync Logic (SSE)
|
|
421
|
+
const evtSource = new EventSource("/events");
|
|
422
|
+
evtSource.onmessage = (event) => {
|
|
423
|
+
const data = JSON.parse(event.data);
|
|
424
|
+
|
|
425
|
+
if (data.type === 'log') {
|
|
426
|
+
addLog(data.message, data.level);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (data.type === 'agent_status') {
|
|
430
|
+
updateAgent(data.agent, data.status, data.activity);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (data.type === 'score') {
|
|
434
|
+
// Update chart
|
|
435
|
+
chart.data.datasets[0].data.shift();
|
|
436
|
+
chart.data.datasets[0].data.push(data.score);
|
|
437
|
+
chart.update();
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
function addLog(msg, level = 'info') {
|
|
442
|
+
const container = document.getElementById('log-container');
|
|
443
|
+
const div = document.createElement('div');
|
|
444
|
+
div.className = \`log-entry \${level}\`;
|
|
445
|
+
div.innerHTML = \`<span class="time">\${new Date().toLocaleTimeString()}</span> \${msg}\`;
|
|
446
|
+
container.prepend(div);
|
|
447
|
+
// Keep max 50 entries
|
|
448
|
+
while (container.children.length > 50) container.lastChild.remove();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function updateAgent(name, status, activity) {
|
|
452
|
+
const card = document.getElementById(\`agent-\${name}\`);
|
|
453
|
+
if (card) {
|
|
454
|
+
const statusEl = card.querySelector('.agent-status');
|
|
455
|
+
const activityEl = card.querySelector('.agent-activity');
|
|
456
|
+
|
|
457
|
+
statusEl.className = \`agent-status status-\${status}\`;
|
|
458
|
+
statusEl.innerText = status;
|
|
459
|
+
activityEl.innerText = activity;
|
|
460
|
+
|
|
461
|
+
// Highlight active agent
|
|
462
|
+
card.classList.toggle('active', status === 'working');
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function addAction(icon, message) {
|
|
467
|
+
const container = document.getElementById('actions-container');
|
|
468
|
+
const div = document.createElement('div');
|
|
469
|
+
div.className = 'action-item';
|
|
470
|
+
div.innerHTML = \`
|
|
471
|
+
<span class="action-icon">\${icon}</span>
|
|
472
|
+
<div class="action-details">
|
|
473
|
+
<div class="action-message">\${message}</div>
|
|
474
|
+
<div class="action-time">\${new Date().toLocaleTimeString()}</div>
|
|
475
|
+
</div>
|
|
476
|
+
\`;
|
|
477
|
+
container.prepend(div);
|
|
478
|
+
while (container.children.length > 20) container.lastChild.remove();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function showToast(message, duration = 3000) {
|
|
482
|
+
const toast = document.getElementById('toast');
|
|
483
|
+
toast.innerText = message;
|
|
484
|
+
toast.classList.add('show');
|
|
485
|
+
setTimeout(() => toast.classList.remove('show'), duration);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Theme Toggle
|
|
489
|
+
function toggleTheme() {
|
|
490
|
+
document.body.classList.toggle('light-theme');
|
|
491
|
+
const isLight = document.body.classList.contains('light-theme');
|
|
492
|
+
localStorage.setItem('theme', isLight ? 'light' : 'dark');
|
|
493
|
+
showToast(isLight ? '☀️ Light mode' : '🌙 Dark mode');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Load saved theme
|
|
497
|
+
if (localStorage.getItem('theme') === 'light') {
|
|
498
|
+
document.body.classList.add('light-theme');
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Quick Actions
|
|
502
|
+
async function runAction(action) {
|
|
503
|
+
const icons = { generate: '⚡', build: '🔨', review: '🔍', validate: '✅', diff: '📊' };
|
|
504
|
+
addLog(\`Running \${action}...\`, 'info');
|
|
505
|
+
addAction(icons[action] || '▶️', \`Started \${action}\`);
|
|
506
|
+
|
|
507
|
+
try {
|
|
508
|
+
const res = await fetch(\`/api/action/\${action}\`, { method: 'POST' });
|
|
509
|
+
const data = await res.json();
|
|
510
|
+
|
|
511
|
+
if (data.success) {
|
|
512
|
+
addLog(\`\${action} completed successfully\`, 'success');
|
|
513
|
+
addAction('✅', \`\${action} completed\`);
|
|
514
|
+
showToast(\`✅ \${action} completed\`);
|
|
515
|
+
} else {
|
|
516
|
+
addLog(\`\${action} failed: \${data.error}\`, 'error');
|
|
517
|
+
showToast(\`❌ \${action} failed\`);
|
|
518
|
+
}
|
|
519
|
+
} catch (e) {
|
|
520
|
+
addLog(\`Failed to run \${action}\`, 'error');
|
|
521
|
+
showToast(\`❌ Connection error\`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function exportReport() {
|
|
526
|
+
addLog('Generating report...', 'info');
|
|
527
|
+
try {
|
|
528
|
+
const res = await fetch('/api/export');
|
|
529
|
+
const blob = await res.blob();
|
|
530
|
+
const url = URL.createObjectURL(blob);
|
|
531
|
+
const a = document.createElement('a');
|
|
532
|
+
a.href = url;
|
|
533
|
+
a.download = 'ultra-dex-report.html';
|
|
534
|
+
a.click();
|
|
535
|
+
URL.revokeObjectURL(url);
|
|
536
|
+
addLog('Report exported', 'success');
|
|
537
|
+
showToast('📄 Report downloaded');
|
|
538
|
+
} catch (e) {
|
|
539
|
+
addLog('Export failed', 'error');
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function refreshDashboard() {
|
|
544
|
+
location.reload();
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
async function startSwarm() {
|
|
548
|
+
const input = document.getElementById('swarm-input');
|
|
549
|
+
const btn = document.getElementById('swarm-btn');
|
|
550
|
+
const objective = input.value.trim();
|
|
551
|
+
|
|
552
|
+
if (!objective) return;
|
|
553
|
+
|
|
554
|
+
btn.disabled = true;
|
|
555
|
+
btn.innerText = "DEPLOYING...";
|
|
556
|
+
addLog(\`Initiating Swarm: \${objective}\`, 'info');
|
|
557
|
+
addAction('🐝', \`Swarm: \${objective}\`);
|
|
558
|
+
|
|
559
|
+
try {
|
|
560
|
+
const res = await fetch('/api/swarm', {
|
|
561
|
+
method: 'POST',
|
|
562
|
+
headers: { 'Content-Type': 'application/json' },
|
|
563
|
+
body: JSON.stringify({ feature: objective })
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
const data = await res.json();
|
|
567
|
+
|
|
568
|
+
if (data.success) {
|
|
569
|
+
addLog("Swarm processes started successfully", "success");
|
|
570
|
+
showToast('🐝 Swarm deployed!');
|
|
571
|
+
input.value = "";
|
|
572
|
+
} else {
|
|
573
|
+
addLog(\`Error: \${data.error}\`, "error");
|
|
574
|
+
}
|
|
575
|
+
} catch (e) {
|
|
576
|
+
addLog("Connection Failed", "error");
|
|
577
|
+
} finally {
|
|
578
|
+
btn.disabled = false;
|
|
579
|
+
btn.innerText = "DEPLOY AGENTS";
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Clock & Uptime
|
|
584
|
+
const startTime = Date.now();
|
|
585
|
+
setInterval(() => {
|
|
586
|
+
document.getElementById('clock').innerText = new Date().toLocaleTimeString();
|
|
587
|
+
const uptime = Math.floor((Date.now() - startTime) / 1000);
|
|
588
|
+
const mins = Math.floor(uptime / 60);
|
|
589
|
+
const secs = uptime % 60;
|
|
590
|
+
document.getElementById('uptime').innerText = mins > 0 ? \`\${mins}m \${secs}s\` : \`\${secs}s\`;
|
|
591
|
+
}, 1000);
|
|
592
|
+
|
|
593
|
+
// Handle action events from SSE
|
|
594
|
+
evtSource.addEventListener('message', (event) => {
|
|
595
|
+
const data = JSON.parse(event.data);
|
|
596
|
+
if (data.type === 'action') {
|
|
597
|
+
const icons = { swarm: '🐝', generate: '⚡', build: '🔨', review: '🔍', validate: '✅' };
|
|
598
|
+
addAction(icons[data.action?.type] || '▶️', data.action?.message || 'Action completed');
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
</script>
|
|
602
|
+
</body>
|
|
603
|
+
</html>`;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export function registerDashboardCommand(program) {
|
|
607
|
+
program
|
|
608
|
+
.command('dashboard')
|
|
609
|
+
.description('Start the Ultra-Dex JARVIS Dashboard')
|
|
610
|
+
.option('-p, --port <port>', 'Port to listen on', '3002')
|
|
611
|
+
.action(async (options) => {
|
|
612
|
+
const port = parseInt(options.port);
|
|
613
|
+
console.log(chalk.bold.cyan('\n🖥️ Starting God Mode Dashboard...'));
|
|
614
|
+
|
|
615
|
+
console.log(chalk.gray('Initializing Neural Link (Graph Scan)...'));
|
|
616
|
+
let graphSummary = null;
|
|
617
|
+
try {
|
|
618
|
+
const graph = await buildGraph();
|
|
619
|
+
graphSummary = { nodes: graph.nodes.length, edges: graph.edges.length };
|
|
620
|
+
console.log(chalk.green(`✅ Neural Link Established: ${graph.nodes.length} nodes mapped.`));
|
|
621
|
+
} catch (e) {
|
|
622
|
+
console.log(chalk.yellow('⚠️ Neural Link Warning: ' + e.message));
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const server = http.createServer(async (req, res) => {
|
|
626
|
+
// Handle SSE
|
|
627
|
+
if (req.url === '/events') {
|
|
628
|
+
res.writeHead(200, {
|
|
629
|
+
'Content-Type': 'text/event-stream',
|
|
630
|
+
'Cache-Control': 'no-cache',
|
|
631
|
+
'Connection': 'keep-alive'
|
|
632
|
+
});
|
|
633
|
+
const client = { res };
|
|
634
|
+
clients.add(client);
|
|
635
|
+
|
|
636
|
+
// Send initial ping
|
|
637
|
+
res.write(`data: ${JSON.stringify({ type: 'log', message: 'Connected to Ultra-Dex Kernel' })}\n\n`);
|
|
638
|
+
|
|
639
|
+
req.on('close', () => clients.delete(client));
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Handle API: Swarm Trigger
|
|
644
|
+
if (req.url === '/api/swarm' && req.method === 'POST') {
|
|
645
|
+
let body = '';
|
|
646
|
+
req.on('data', chunk => body += chunk.toString());
|
|
647
|
+
req.on('end', async () => {
|
|
648
|
+
try {
|
|
649
|
+
const { feature } = JSON.parse(body);
|
|
650
|
+
console.log(chalk.magenta(`\n⚡ Dashboard Trigger: Starting Swarm for "${feature}"...`));
|
|
651
|
+
|
|
652
|
+
// Add to action history
|
|
653
|
+
addAction('swarm', `Swarm started: ${feature}`, 'planner');
|
|
654
|
+
|
|
655
|
+
// Simulate agent activity
|
|
656
|
+
sendToClients({ type: 'log', message: `Swarm triggered: ${feature}`, level: 'info' });
|
|
657
|
+
sendToClients({ type: 'agent_status', agent: 'planner', status: 'working', activity: 'Analyzing requirements...' });
|
|
658
|
+
|
|
659
|
+
// Use npx to spawn autonomous process detached
|
|
660
|
+
const child = spawn('npx', ['ultra-dex', 'auto-implement', feature], {
|
|
661
|
+
stdio: 'inherit',
|
|
662
|
+
shell: true,
|
|
663
|
+
detached: true
|
|
664
|
+
});
|
|
665
|
+
child.unref();
|
|
666
|
+
|
|
667
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
668
|
+
res.end(JSON.stringify({ success: true, message: 'Swarm initiated' }));
|
|
669
|
+
} catch (e) {
|
|
670
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
671
|
+
res.end(JSON.stringify({ success: false, error: e.message }));
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Handle API: Quick Actions
|
|
678
|
+
if (req.url.startsWith('/api/action/') && req.method === 'POST') {
|
|
679
|
+
const action = req.url.replace('/api/action/', '');
|
|
680
|
+
console.log(chalk.cyan(`\n▶️ Running action: ${action}`));
|
|
681
|
+
|
|
682
|
+
try {
|
|
683
|
+
const actionCommands = {
|
|
684
|
+
generate: ['npx', ['ultra-dex', 'generate']],
|
|
685
|
+
build: ['npx', ['ultra-dex', 'build']],
|
|
686
|
+
review: ['npx', ['ultra-dex', 'review']],
|
|
687
|
+
validate: ['npx', ['ultra-dex', 'validate']],
|
|
688
|
+
diff: ['npx', ['ultra-dex', 'diff', '--json']]
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
if (!actionCommands[action]) {
|
|
692
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
693
|
+
res.end(JSON.stringify({ success: false, error: 'Unknown action' }));
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
addAction(action, `Started ${action}`, null);
|
|
698
|
+
sendToClients({ type: 'log', message: `Running ${action}...`, level: 'info' });
|
|
699
|
+
|
|
700
|
+
const [cmd, args] = actionCommands[action];
|
|
701
|
+
const result = execSync(`${cmd} ${args.join(' ')}`, {
|
|
702
|
+
encoding: 'utf-8',
|
|
703
|
+
timeout: 60000,
|
|
704
|
+
maxBuffer: 1024 * 1024
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
sendToClients({ type: 'log', message: `${action} completed`, level: 'success' });
|
|
708
|
+
|
|
709
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
710
|
+
res.end(JSON.stringify({ success: true, output: result.slice(0, 1000) }));
|
|
711
|
+
} catch (e) {
|
|
712
|
+
sendToClients({ type: 'log', message: `${action} failed: ${e.message}`, level: 'error' });
|
|
713
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
714
|
+
res.end(JSON.stringify({ success: false, error: e.message }));
|
|
715
|
+
}
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Handle API: Export Report
|
|
720
|
+
if (req.url === '/api/export') {
|
|
721
|
+
try {
|
|
722
|
+
const state = await loadState();
|
|
723
|
+
const gitInfo = await getGitInfo();
|
|
724
|
+
const html = generateDashboardHTML(state, gitInfo, graphSummary);
|
|
725
|
+
|
|
726
|
+
res.writeHead(200, {
|
|
727
|
+
'Content-Type': 'text/html',
|
|
728
|
+
'Content-Disposition': 'attachment; filename="ultra-dex-report.html"'
|
|
729
|
+
});
|
|
730
|
+
res.end(html);
|
|
731
|
+
} catch (e) {
|
|
732
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
733
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
734
|
+
}
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Handle API: Get actions history
|
|
739
|
+
if (req.url === '/api/actions') {
|
|
740
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
741
|
+
res.end(JSON.stringify(actionHistory));
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Handle API: Get memory bank
|
|
746
|
+
if (req.url === '/api/memory') {
|
|
747
|
+
try {
|
|
748
|
+
const { ultraMemory } = await import('../mcp/memory.js');
|
|
749
|
+
const items = await ultraMemory.getAll();
|
|
750
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
751
|
+
res.end(JSON.stringify(items));
|
|
752
|
+
} catch (e) {
|
|
753
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
754
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
755
|
+
}
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const state = await loadState();
|
|
760
|
+
const gitInfo = await getGitInfo();
|
|
761
|
+
const html = generateDashboardHTML(state, gitInfo, graphSummary);
|
|
762
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
763
|
+
res.end(html);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
server.listen(port, () => {
|
|
767
|
+
console.log(chalk.green(`✅ Dashboard active at http://localhost:${port}`));
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
}
|