@stackmemoryai/stackmemory 0.3.22 → 0.3.24
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/dist/cli/commands/ralph.js +294 -0
- package/dist/cli/commands/ralph.js.map +7 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +2 -2
- package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js +586 -0
- package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +7 -0
- package/dist/integrations/ralph/context/context-budget-manager.js +297 -0
- package/dist/integrations/ralph/context/context-budget-manager.js.map +7 -0
- package/dist/integrations/ralph/context/stackmemory-context-loader.js +356 -0
- package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +7 -0
- package/dist/integrations/ralph/index.js +14 -0
- package/dist/integrations/ralph/index.js.map +7 -0
- package/dist/integrations/ralph/learning/pattern-learner.js +397 -0
- package/dist/integrations/ralph/learning/pattern-learner.js.map +7 -0
- package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js +444 -0
- package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js.map +7 -0
- package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js +459 -0
- package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +7 -0
- package/dist/integrations/ralph/performance/performance-optimizer.js +354 -0
- package/dist/integrations/ralph/performance/performance-optimizer.js.map +7 -0
- package/dist/integrations/ralph/ralph-integration-demo.js +178 -0
- package/dist/integrations/ralph/ralph-integration-demo.js.map +7 -0
- package/dist/integrations/ralph/state/state-reconciler.js +400 -0
- package/dist/integrations/ralph/state/state-reconciler.js.map +7 -0
- package/dist/integrations/ralph/swarm/swarm-coordinator.js +487 -0
- package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +7 -0
- package/dist/integrations/ralph/types.js +1 -0
- package/dist/integrations/ralph/types.js.map +7 -0
- package/dist/integrations/ralph/visualization/ralph-debugger.js +581 -0
- package/dist/integrations/ralph/visualization/ralph-debugger.js.map +7 -0
- package/package.json +1 -1
- package/scripts/deploy-ralph-swarm.sh +365 -0
- package/scripts/ralph-integration-test.js +274 -0
- package/scripts/ralph-loop-implementation.js +404 -0
- package/scripts/swarm-monitor.js +509 -0
- package/scripts/test-parallel-swarms.js +443 -0
- package/scripts/testing/ralph-cli-test.js +88 -0
- package/scripts/testing/ralph-integration-validation.js +727 -0
- package/scripts/testing/ralph-swarm-test-scenarios.js +613 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Real-time Swarm Monitoring Utility
|
|
5
|
+
* Provides live monitoring and metrics for running swarms
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { createServer } from 'http';
|
|
11
|
+
import { WebSocketServer } from 'ws';
|
|
12
|
+
import blessed from 'blessed';
|
|
13
|
+
|
|
14
|
+
class SwarmMonitor {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.swarmDir = '.swarm';
|
|
17
|
+
this.statusDir = path.join(this.swarmDir, 'status');
|
|
18
|
+
this.logsDir = path.join(this.swarmDir, 'logs');
|
|
19
|
+
this.metricsHistory = [];
|
|
20
|
+
this.maxHistorySize = 100;
|
|
21
|
+
this.updateInterval = 2000; // 2 seconds
|
|
22
|
+
this.wsPort = 3456;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async startMonitoring() {
|
|
26
|
+
console.log('🔍 Starting Swarm Monitor...');
|
|
27
|
+
|
|
28
|
+
// Check if we should use terminal UI or web interface
|
|
29
|
+
const mode = process.argv[2] || 'terminal';
|
|
30
|
+
|
|
31
|
+
if (mode === 'web') {
|
|
32
|
+
await this.startWebMonitor();
|
|
33
|
+
} else {
|
|
34
|
+
await this.startTerminalMonitor();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async startTerminalMonitor() {
|
|
39
|
+
// Create blessed screen
|
|
40
|
+
const screen = blessed.screen({
|
|
41
|
+
smartCSR: true,
|
|
42
|
+
title: 'Ralph Swarm Monitor'
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Create layout boxes
|
|
46
|
+
const header = blessed.box({
|
|
47
|
+
parent: screen,
|
|
48
|
+
top: 0,
|
|
49
|
+
left: 0,
|
|
50
|
+
width: '100%',
|
|
51
|
+
height: 3,
|
|
52
|
+
content: ' RALPH SWARM MONITOR ',
|
|
53
|
+
align: 'center',
|
|
54
|
+
style: {
|
|
55
|
+
fg: 'white',
|
|
56
|
+
bg: 'blue',
|
|
57
|
+
bold: true
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const swarmList = blessed.list({
|
|
62
|
+
parent: screen,
|
|
63
|
+
top: 3,
|
|
64
|
+
left: 0,
|
|
65
|
+
width: '50%',
|
|
66
|
+
height: '40%',
|
|
67
|
+
label: ' Active Swarms ',
|
|
68
|
+
border: {
|
|
69
|
+
type: 'line'
|
|
70
|
+
},
|
|
71
|
+
style: {
|
|
72
|
+
fg: 'white',
|
|
73
|
+
border: {
|
|
74
|
+
fg: 'cyan'
|
|
75
|
+
},
|
|
76
|
+
selected: {
|
|
77
|
+
bg: 'blue'
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
mouse: true,
|
|
81
|
+
keys: true,
|
|
82
|
+
vi: true
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const metricsBox = blessed.box({
|
|
86
|
+
parent: screen,
|
|
87
|
+
top: 3,
|
|
88
|
+
left: '50%',
|
|
89
|
+
width: '50%',
|
|
90
|
+
height: '40%',
|
|
91
|
+
label: ' Performance Metrics ',
|
|
92
|
+
border: {
|
|
93
|
+
type: 'line'
|
|
94
|
+
},
|
|
95
|
+
style: {
|
|
96
|
+
fg: 'white',
|
|
97
|
+
border: {
|
|
98
|
+
fg: 'yellow'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const logBox = blessed.log({
|
|
104
|
+
parent: screen,
|
|
105
|
+
bottom: 3,
|
|
106
|
+
left: 0,
|
|
107
|
+
width: '100%',
|
|
108
|
+
height: '55%',
|
|
109
|
+
label: ' Live Logs ',
|
|
110
|
+
border: {
|
|
111
|
+
type: 'line'
|
|
112
|
+
},
|
|
113
|
+
style: {
|
|
114
|
+
fg: 'white',
|
|
115
|
+
border: {
|
|
116
|
+
fg: 'green'
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
scrollable: true,
|
|
120
|
+
alwaysScroll: true,
|
|
121
|
+
mouse: true
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const statusBar = blessed.box({
|
|
125
|
+
parent: screen,
|
|
126
|
+
bottom: 0,
|
|
127
|
+
left: 0,
|
|
128
|
+
width: '100%',
|
|
129
|
+
height: 3,
|
|
130
|
+
style: {
|
|
131
|
+
fg: 'white',
|
|
132
|
+
bg: 'black'
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Handle exit
|
|
137
|
+
screen.key(['q', 'C-c'], () => {
|
|
138
|
+
return process.exit(0);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Update function
|
|
142
|
+
const updateDisplay = async () => {
|
|
143
|
+
try {
|
|
144
|
+
// Get swarm status
|
|
145
|
+
const swarms = await this.getActiveSwarms();
|
|
146
|
+
|
|
147
|
+
// Update swarm list
|
|
148
|
+
const swarmItems = swarms.map(s => {
|
|
149
|
+
const status = s.status === 'running' ? '🟢' : '🔴';
|
|
150
|
+
return `${status} ${s.id} - ${s.project}`;
|
|
151
|
+
});
|
|
152
|
+
swarmList.setItems(swarmItems);
|
|
153
|
+
|
|
154
|
+
// Update metrics
|
|
155
|
+
const metrics = await this.collectMetrics(swarms);
|
|
156
|
+
const metricsContent = this.formatMetrics(metrics);
|
|
157
|
+
metricsBox.setContent(metricsContent);
|
|
158
|
+
|
|
159
|
+
// Update status bar
|
|
160
|
+
const now = new Date().toLocaleTimeString();
|
|
161
|
+
statusBar.setContent(
|
|
162
|
+
` Active: ${swarms.filter(s => s.status === 'running').length} | ` +
|
|
163
|
+
`Total: ${swarms.length} | ` +
|
|
164
|
+
`Updated: ${now} | ` +
|
|
165
|
+
`Press 'q' to quit`
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
screen.render();
|
|
169
|
+
} catch (error) {
|
|
170
|
+
logBox.log(`Error: ${error.message}`);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Handle swarm selection
|
|
175
|
+
swarmList.on('select', async (item, index) => {
|
|
176
|
+
const swarms = await this.getActiveSwarms();
|
|
177
|
+
if (swarms[index]) {
|
|
178
|
+
await this.tailSwarmLogs(swarms[index], logBox);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Start update loop
|
|
183
|
+
await updateDisplay();
|
|
184
|
+
setInterval(updateDisplay, this.updateInterval);
|
|
185
|
+
|
|
186
|
+
screen.render();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async startWebMonitor() {
|
|
190
|
+
// Create HTTP server for web interface
|
|
191
|
+
const server = createServer((req, res) => {
|
|
192
|
+
if (req.url === '/') {
|
|
193
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
194
|
+
res.end(this.getWebInterface());
|
|
195
|
+
} else if (req.url === '/api/swarms') {
|
|
196
|
+
this.handleApiRequest(res);
|
|
197
|
+
} else {
|
|
198
|
+
res.writeHead(404);
|
|
199
|
+
res.end('Not Found');
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Create WebSocket server for real-time updates
|
|
204
|
+
const wss = new WebSocketServer({ server });
|
|
205
|
+
|
|
206
|
+
wss.on('connection', (ws) => {
|
|
207
|
+
console.log('Client connected to monitor');
|
|
208
|
+
|
|
209
|
+
// Send initial data
|
|
210
|
+
this.sendSwarmUpdate(ws);
|
|
211
|
+
|
|
212
|
+
// Set up periodic updates
|
|
213
|
+
const updateInterval = setInterval(() => {
|
|
214
|
+
this.sendSwarmUpdate(ws);
|
|
215
|
+
}, this.updateInterval);
|
|
216
|
+
|
|
217
|
+
ws.on('close', () => {
|
|
218
|
+
clearInterval(updateInterval);
|
|
219
|
+
console.log('Client disconnected from monitor');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
server.listen(this.wsPort, () => {
|
|
224
|
+
console.log(`📡 Web monitor running at http://localhost:${this.wsPort}`);
|
|
225
|
+
console.log('Open in browser to view real-time swarm status');
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async getActiveSwarms() {
|
|
230
|
+
const swarms = [];
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const files = await fs.readdir(this.statusDir);
|
|
234
|
+
|
|
235
|
+
for (const file of files) {
|
|
236
|
+
if (file.endsWith('.json')) {
|
|
237
|
+
const filePath = path.join(this.statusDir, file);
|
|
238
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
239
|
+
const swarm = JSON.parse(content);
|
|
240
|
+
|
|
241
|
+
// Check if process is still running
|
|
242
|
+
if (swarm.pid) {
|
|
243
|
+
try {
|
|
244
|
+
process.kill(swarm.pid, 0);
|
|
245
|
+
swarm.status = 'running';
|
|
246
|
+
} catch {
|
|
247
|
+
swarm.status = 'stopped';
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
swarms.push(swarm);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} catch (error) {
|
|
255
|
+
// Directory might not exist
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return swarms;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async collectMetrics(swarms) {
|
|
262
|
+
const metrics = {
|
|
263
|
+
timestamp: Date.now(),
|
|
264
|
+
activeSwarms: swarms.filter(s => s.status === 'running').length,
|
|
265
|
+
totalSwarms: swarms.length,
|
|
266
|
+
agentCount: 0,
|
|
267
|
+
taskCompletion: 0,
|
|
268
|
+
memoryUsage: process.memoryUsage(),
|
|
269
|
+
cpuUsage: process.cpuUsage()
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Count total agents
|
|
273
|
+
for (const swarm of swarms) {
|
|
274
|
+
if (swarm.agents) {
|
|
275
|
+
metrics.agentCount += swarm.agents.split(',').length;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Calculate average task completion (mock for now)
|
|
280
|
+
metrics.taskCompletion = Math.round(Math.random() * 100);
|
|
281
|
+
|
|
282
|
+
// Store in history
|
|
283
|
+
this.metricsHistory.push(metrics);
|
|
284
|
+
if (this.metricsHistory.length > this.maxHistorySize) {
|
|
285
|
+
this.metricsHistory.shift();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return metrics;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
formatMetrics(metrics) {
|
|
292
|
+
const memoryMB = (metrics.memoryUsage.heapUsed / 1024 / 1024).toFixed(2);
|
|
293
|
+
|
|
294
|
+
return `
|
|
295
|
+
Active Swarms: ${metrics.activeSwarms}/${metrics.totalSwarms}
|
|
296
|
+
Total Agents: ${metrics.agentCount}
|
|
297
|
+
Task Completion: ${metrics.taskCompletion}%
|
|
298
|
+
|
|
299
|
+
Memory Usage: ${memoryMB} MB
|
|
300
|
+
CPU Time: ${metrics.cpuUsage.user / 1000}ms
|
|
301
|
+
|
|
302
|
+
Performance Trend:
|
|
303
|
+
${this.getPerformanceTrend()}
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
getPerformanceTrend() {
|
|
308
|
+
if (this.metricsHistory.length < 2) return 'Collecting data...';
|
|
309
|
+
|
|
310
|
+
const recent = this.metricsHistory.slice(-10);
|
|
311
|
+
let trend = '';
|
|
312
|
+
|
|
313
|
+
for (let i = 0; i < recent.length; i++) {
|
|
314
|
+
const value = recent[i].taskCompletion;
|
|
315
|
+
if (value > 80) trend += '█';
|
|
316
|
+
else if (value > 60) trend += '▓';
|
|
317
|
+
else if (value > 40) trend += '▒';
|
|
318
|
+
else if (value > 20) trend += '░';
|
|
319
|
+
else trend += ' ';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return trend;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async tailSwarmLogs(swarm, logBox) {
|
|
326
|
+
const logFile = swarm.logFile || path.join(this.logsDir, `${swarm.id}.log`);
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
const content = await fs.readFile(logFile, 'utf-8');
|
|
330
|
+
const lines = content.split('\n');
|
|
331
|
+
const recentLines = lines.slice(-20);
|
|
332
|
+
|
|
333
|
+
logBox.log(`\n=== Logs for ${swarm.id} ===`);
|
|
334
|
+
recentLines.forEach(line => {
|
|
335
|
+
if (line.trim()) {
|
|
336
|
+
logBox.log(line);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
} catch (error) {
|
|
340
|
+
logBox.log(`Could not read logs for ${swarm.id}: ${error.message}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async sendSwarmUpdate(ws) {
|
|
345
|
+
try {
|
|
346
|
+
const swarms = await this.getActiveSwarms();
|
|
347
|
+
const metrics = await this.collectMetrics(swarms);
|
|
348
|
+
|
|
349
|
+
ws.send(JSON.stringify({
|
|
350
|
+
type: 'update',
|
|
351
|
+
swarms,
|
|
352
|
+
metrics,
|
|
353
|
+
history: this.metricsHistory.slice(-20)
|
|
354
|
+
}));
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error('Failed to send update:', error);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async handleApiRequest(res) {
|
|
361
|
+
try {
|
|
362
|
+
const swarms = await this.getActiveSwarms();
|
|
363
|
+
const metrics = await this.collectMetrics(swarms);
|
|
364
|
+
|
|
365
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
366
|
+
res.end(JSON.stringify({ swarms, metrics }));
|
|
367
|
+
} catch (error) {
|
|
368
|
+
res.writeHead(500);
|
|
369
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
getWebInterface() {
|
|
374
|
+
return `
|
|
375
|
+
<!DOCTYPE html>
|
|
376
|
+
<html>
|
|
377
|
+
<head>
|
|
378
|
+
<title>Ralph Swarm Monitor</title>
|
|
379
|
+
<style>
|
|
380
|
+
body {
|
|
381
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
382
|
+
background: #1e1e1e;
|
|
383
|
+
color: #d4d4d4;
|
|
384
|
+
margin: 0;
|
|
385
|
+
padding: 20px;
|
|
386
|
+
}
|
|
387
|
+
h1 {
|
|
388
|
+
color: #569cd6;
|
|
389
|
+
text-align: center;
|
|
390
|
+
}
|
|
391
|
+
.container {
|
|
392
|
+
display: grid;
|
|
393
|
+
grid-template-columns: 1fr 1fr;
|
|
394
|
+
gap: 20px;
|
|
395
|
+
margin-top: 20px;
|
|
396
|
+
}
|
|
397
|
+
.panel {
|
|
398
|
+
background: #2d2d30;
|
|
399
|
+
border: 1px solid #3e3e42;
|
|
400
|
+
border-radius: 5px;
|
|
401
|
+
padding: 15px;
|
|
402
|
+
}
|
|
403
|
+
.panel h2 {
|
|
404
|
+
color: #4ec9b0;
|
|
405
|
+
margin-top: 0;
|
|
406
|
+
}
|
|
407
|
+
.swarm-item {
|
|
408
|
+
padding: 8px;
|
|
409
|
+
margin: 5px 0;
|
|
410
|
+
background: #1e1e1e;
|
|
411
|
+
border-radius: 3px;
|
|
412
|
+
}
|
|
413
|
+
.status-running {
|
|
414
|
+
border-left: 3px solid #4ec9b0;
|
|
415
|
+
}
|
|
416
|
+
.status-stopped {
|
|
417
|
+
border-left: 3px solid #f44747;
|
|
418
|
+
}
|
|
419
|
+
.metrics {
|
|
420
|
+
font-size: 14px;
|
|
421
|
+
}
|
|
422
|
+
.metric-label {
|
|
423
|
+
color: #9cdcfe;
|
|
424
|
+
}
|
|
425
|
+
.metric-value {
|
|
426
|
+
color: #d4d4d4;
|
|
427
|
+
font-weight: bold;
|
|
428
|
+
}
|
|
429
|
+
#logs {
|
|
430
|
+
background: #1e1e1e;
|
|
431
|
+
padding: 10px;
|
|
432
|
+
height: 200px;
|
|
433
|
+
overflow-y: auto;
|
|
434
|
+
font-size: 12px;
|
|
435
|
+
white-space: pre-wrap;
|
|
436
|
+
}
|
|
437
|
+
</style>
|
|
438
|
+
</head>
|
|
439
|
+
<body>
|
|
440
|
+
<h1>🚀 Ralph Swarm Monitor</h1>
|
|
441
|
+
|
|
442
|
+
<div class="container">
|
|
443
|
+
<div class="panel">
|
|
444
|
+
<h2>Active Swarms</h2>
|
|
445
|
+
<div id="swarms"></div>
|
|
446
|
+
</div>
|
|
447
|
+
|
|
448
|
+
<div class="panel">
|
|
449
|
+
<h2>Performance Metrics</h2>
|
|
450
|
+
<div id="metrics" class="metrics"></div>
|
|
451
|
+
</div>
|
|
452
|
+
</div>
|
|
453
|
+
|
|
454
|
+
<div class="panel" style="margin-top: 20px;">
|
|
455
|
+
<h2>Live Logs</h2>
|
|
456
|
+
<div id="logs"></div>
|
|
457
|
+
</div>
|
|
458
|
+
|
|
459
|
+
<script>
|
|
460
|
+
const ws = new WebSocket('ws://localhost:${this.wsPort}');
|
|
461
|
+
|
|
462
|
+
ws.onmessage = (event) => {
|
|
463
|
+
const data = JSON.parse(event.data);
|
|
464
|
+
updateDisplay(data);
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
function updateDisplay(data) {
|
|
468
|
+
// Update swarms
|
|
469
|
+
const swarmsDiv = document.getElementById('swarms');
|
|
470
|
+
swarmsDiv.innerHTML = data.swarms.map(s => \`
|
|
471
|
+
<div class="swarm-item status-\${s.status}">
|
|
472
|
+
<strong>\${s.id}</strong><br>
|
|
473
|
+
Project: \${s.project}<br>
|
|
474
|
+
Agents: \${s.agents}<br>
|
|
475
|
+
Status: \${s.status}
|
|
476
|
+
</div>
|
|
477
|
+
\`).join('');
|
|
478
|
+
|
|
479
|
+
// Update metrics
|
|
480
|
+
const metricsDiv = document.getElementById('metrics');
|
|
481
|
+
metricsDiv.innerHTML = \`
|
|
482
|
+
<p><span class="metric-label">Active Swarms:</span> <span class="metric-value">\${data.metrics.activeSwarms}/\${data.metrics.totalSwarms}</span></p>
|
|
483
|
+
<p><span class="metric-label">Total Agents:</span> <span class="metric-value">\${data.metrics.agentCount}</span></p>
|
|
484
|
+
<p><span class="metric-label">Task Completion:</span> <span class="metric-value">\${data.metrics.taskCompletion}%</span></p>
|
|
485
|
+
<p><span class="metric-label">Memory Usage:</span> <span class="metric-value">\${(data.metrics.memoryUsage.heapUsed / 1024 / 1024).toFixed(2)} MB</span></p>
|
|
486
|
+
\`;
|
|
487
|
+
|
|
488
|
+
// Update logs (mock for now)
|
|
489
|
+
const logsDiv = document.getElementById('logs');
|
|
490
|
+
if (Math.random() > 0.7) {
|
|
491
|
+
const logEntry = new Date().toLocaleTimeString() + ' - Swarm activity detected\\n';
|
|
492
|
+
logsDiv.textContent += logEntry;
|
|
493
|
+
logsDiv.scrollTop = logsDiv.scrollHeight;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
</script>
|
|
497
|
+
</body>
|
|
498
|
+
</html>
|
|
499
|
+
`;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Run monitor if executed directly
|
|
504
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
505
|
+
const monitor = new SwarmMonitor();
|
|
506
|
+
monitor.startMonitoring().catch(console.error);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export { SwarmMonitor };
|