@stackmemoryai/stackmemory
Version:
Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, a
294 lines (292 loc) • 9.25 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { EventEmitter } from "events";
import { logger } from "../../../core/monitoring/logger.js";
class SwarmDashboard extends EventEmitter {
metrics = /* @__PURE__ */ new Map();
alerts = [];
monitoringInterval;
swarmCoordinator;
constructor(swarmCoordinator) {
super();
this.swarmCoordinator = swarmCoordinator;
this.setupDefaultAlerts();
}
/**
* Start real-time monitoring
*/
startMonitoring(intervalMs = 5e3) {
this.monitoringInterval = setInterval(() => {
this.collectMetrics();
this.checkAlerts();
}, intervalMs);
logger.info("Swarm monitoring dashboard started");
}
/**
* Stop monitoring
*/
stopMonitoring() {
if (this.monitoringInterval) {
clearInterval(this.monitoringInterval);
this.monitoringInterval = void 0;
}
logger.info("Swarm monitoring dashboard stopped");
}
/**
* Get current metrics for a swarm
*/
getSwarmMetrics(swarmId) {
return this.metrics.get(swarmId);
}
/**
* Get all active swarm metrics
*/
getAllMetrics() {
return Array.from(this.metrics.values());
}
/**
* Add custom alert rule
*/
addAlert(rule) {
this.alerts.push(rule);
logger.info(`Added alert rule: ${rule.id}`);
}
/**
* Generate real-time dashboard HTML
*/
generateDashboardHTML() {
const metrics = this.getAllMetrics();
return `
<!DOCTYPE html>
<html>
<head>
<title>Ralph Swarm Dashboard</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
.dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.metric { display: flex; justify-content: space-between; margin: 10px 0; }
.status-active { color: #28a745; }
.status-error { color: #dc3545; }
.status-idle { color: #6c757d; }
.alert-critical { color: #dc3545; font-weight: bold; }
.progress { background: #e9ecef; border-radius: 4px; height: 8px; }
.progress-bar { background: #007bff; height: 100%; border-radius: 4px; }
</style>
<script>
setTimeout(() => location.reload(), 5000); // Auto-refresh
</script>
</head>
<body>
<h1>\u{1F9BE} Ralph Swarm Dashboard</h1>
<div class="dashboard">
${metrics.map(
(swarm) => `
<div class="card">
<h3>Swarm ${swarm.swarmId.substring(0, 8)}</h3>
<div class="metric">
<span>Status:</span>
<span class="status-${swarm.status}">${swarm.status.toUpperCase()}</span>
</div>
<div class="metric">
<span>Agents:</span>
<span>${swarm.activeAgents}/${swarm.totalAgents}</span>
</div>
<div class="metric">
<span>Tasks:</span>
<span>${swarm.completedTasks}/${swarm.activeTasks + swarm.completedTasks}</span>
</div>
<div class="metric">
<span>Throughput:</span>
<span>${swarm.performance.throughput.toFixed(2)} tasks/min</span>
</div>
<div class="metric">
<span>Memory:</span>
<span>${swarm.resourceUsage.memoryMB} MB</span>
</div>
<div class="metric">
<span>Uptime:</span>
<span>${Math.round(swarm.performance.uptime / 1e3)}s</span>
</div>
<div style="margin-top: 15px;">
<h4>Agents:</h4>
${swarm.agents.map(
(agent) => `
<div class="metric">
<span>${agent.role}:</span>
<span class="status-${agent.status}">${agent.status}</span>
</div>
`
).join("")}
</div>
</div>
`
).join("")}
</div>
<div class="card" style="margin-top: 20px;">
<h3>\u{1F6A8} Active Alerts</h3>
<div id="alerts">${this.getActiveAlerts().map(
(alert) => `<div class="alert-${alert.severity}">${alert.message}</div>`
).join("")}</div>
</div>
</body>
</html>`;
}
/**
* Export metrics to JSON
*/
exportMetrics() {
return JSON.stringify(
{
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
swarms: this.getAllMetrics(),
alerts: this.getActiveAlerts()
},
null,
2
);
}
collectMetrics() {
const usage = this.swarmCoordinator.getResourceUsage();
const swarmState = this.swarmCoordinator.swarmState;
if (!swarmState || !swarmState.id) return;
const metrics = {
swarmId: swarmState.id,
status: swarmState.status,
totalAgents: usage.activeAgents,
activeAgents: usage.activeAgents,
completedTasks: swarmState.completedTaskCount || 0,
activeTasks: swarmState.activeTaskCount || 0,
averageTaskTime: 0,
// Calculate from agent metrics
resourceUsage: {
memoryMB: usage.memoryEstimate,
cpuPercent: this.estimateCpuUsage(),
diskMB: this.estimateDiskUsage()
},
performance: {
throughput: swarmState.performance?.throughput || 0,
efficiency: swarmState.performance?.efficiency || 0,
uptime: Date.now() - swarmState.startTime
},
agents: this.collectAgentMetrics()
};
this.metrics.set(swarmState.id, metrics);
this.emit("metricsUpdated", metrics);
}
collectAgentMetrics() {
const agents = this.swarmCoordinator.activeAgents;
if (!agents) return [];
return Array.from(agents.values()).map(
(agent) => ({
id: agent.id,
role: agent.role,
status: agent.status,
currentTask: agent.currentTask || void 0,
tasksCompleted: agent.performance?.tasksCompleted || 0,
averageTaskTime: agent.performance?.averageTaskTime || 0,
successRate: agent.performance?.successRate || 1,
lastActivity: agent.performance?.lastFreshStart || Date.now(),
resourceUsage: {
memoryMB: 50,
// Estimate per agent
iterations: agent.performance?.tasksCompleted || 0
}
})
);
}
checkAlerts() {
const metrics = this.getAllMetrics();
for (const swarmMetrics of metrics) {
for (const alert of this.alerts) {
if (this.evaluateAlertCondition(alert, swarmMetrics)) {
this.emit("alert", {
...alert,
swarmId: swarmMetrics.swarmId,
timestamp: Date.now(),
value: this.getMetricValue(alert.condition, swarmMetrics)
});
}
}
}
}
evaluateAlertCondition(alert, metrics) {
const value = this.getMetricValue(alert.condition, metrics);
switch (alert.type) {
case "performance":
return value < alert.threshold;
case "error":
return 1 - metrics.performance.efficiency > alert.threshold;
case "resource":
return metrics.resourceUsage.memoryMB > alert.threshold;
case "completion":
return metrics.performance.uptime > alert.threshold;
default:
return false;
}
}
getMetricValue(condition, metrics) {
if (condition.includes("throughput")) return metrics.performance.throughput;
if (condition.includes("efficiency")) return metrics.performance.efficiency;
if (condition.includes("memory")) return metrics.resourceUsage.memoryMB;
if (condition.includes("uptime")) return metrics.performance.uptime;
return 0;
}
getActiveAlerts() {
return [];
}
estimateCpuUsage() {
const activeAgents = this.collectAgentMetrics().filter(
(a) => a.status === "active"
).length;
return Math.min(activeAgents * 15, 100);
}
estimateDiskUsage() {
const usage = this.swarmCoordinator.getResourceUsage();
return usage.workingDirectories.length * 10;
}
setupDefaultAlerts() {
this.alerts = [
{
id: "low-throughput",
type: "performance",
condition: "throughput < 0.5",
threshold: 0.5,
message: "Low throughput detected: Less than 0.5 tasks per minute",
severity: "medium"
},
{
id: "high-memory",
type: "resource",
condition: "memory > 500",
threshold: 500,
message: "High memory usage detected: Over 500MB",
severity: "high"
},
{
id: "long-running",
type: "completion",
condition: "uptime > 1800000",
threshold: 18e5,
// 30 minutes
message: "Long running swarm detected: Over 30 minutes",
severity: "low"
},
{
id: "low-efficiency",
type: "error",
condition: "errorRate > 0.3",
threshold: 0.3,
message: "High error rate detected: Over 30% failure rate",
severity: "critical"
}
];
}
}
var swarm_dashboard_default = SwarmDashboard;
export {
SwarmDashboard,
swarm_dashboard_default as default
};