Add fleet ops dashboard, Calibrate enforcement, and dead-code cleanup.

Ship live alerts, pool status, AI monitor, remote agent commands, build manager, and uninstall flow; wire Calibrate settings (WS ping, pool traffic log, retention limits) at runtime and exclude server/data from git.
This commit is contained in:
drjones
2026-05-27 09:16:04 -07:00
parent 9d223b8137
commit df81eb7744
75 changed files with 8891 additions and 966 deletions

View File

@@ -1,26 +1,35 @@
import { useEffect, useRef, useCallback, useState } from 'react';
import type { WSMessage, Agent, FleetStats, Share } from '../types';
import type { WSMessage, Agent, Share, FleetAlert, PoolStatus, AIActivityEntry } from '../types';
interface DashboardData {
interface DashboardInit {
agents: Agent[];
stats: FleetStats;
}
interface UseWebSocketReturn {
isConnected: boolean;
agents: Agent[];
stats: FleetStats | null;
recentShares: Share[];
fleetAlerts: FleetAlert[];
poolStatus: PoolStatus[];
aiActivity: AIActivityEntry[];
agentLogs: Record<string, string>;
}
export function useWebSocket(): UseWebSocketReturn {
const wsRef = useRef<WebSocket | null>(null);
const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const unmounted = useRef(false);
const [isConnected, setIsConnected] = useState(false);
const [agents, setAgents] = useState<Agent[]>([]);
const [stats, setStats] = useState<FleetStats | null>(null);
const [recentShares, setRecentShares] = useState<Share[]>([]);
const [fleetAlerts, setFleetAlerts] = useState<FleetAlert[]>([]);
const [poolStatus, setPoolStatus] = useState<PoolStatus[]>([]);
const [aiActivity, setAiActivity] = useState<AIActivityEntry[]>([]);
const [agentLogs, setAgentLogs] = useState<Record<string, string>>({});
const connect = useCallback(() => {
if (unmounted.current) return;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws/dashboard`;
@@ -28,13 +37,13 @@ export function useWebSocket(): UseWebSocketReturn {
wsRef.current = ws;
ws.onopen = () => {
setIsConnected(true);
if (!unmounted.current) setIsConnected(true);
};
ws.onclose = () => {
if (unmounted.current) return;
setIsConnected(false);
// Reconnect after 3 seconds
setTimeout(connect, 3000);
reconnectTimer.current = setTimeout(connect, 3000);
};
ws.onerror = () => {
@@ -47,9 +56,8 @@ export function useWebSocket(): UseWebSocketReturn {
switch (msg.type) {
case 'init': {
const data = msg.payload as DashboardData;
const data = msg.payload as DashboardInit;
if (data.agents) setAgents(data.agents);
if (data.stats) setStats(data.stats);
break;
}
case 'agent_online': {
@@ -102,6 +110,36 @@ export function useWebSocket(): UseWebSocketReturn {
setRecentShares((prev) => [share, ...prev].slice(0, 50));
break;
}
case 'fleet_alert': {
const alert = msg.payload as FleetAlert;
setFleetAlerts((prev) => [alert, ...prev].slice(0, 20));
break;
}
case 'pool_status': {
const pools = msg.payload as PoolStatus[];
if (Array.isArray(pools)) setPoolStatus(pools);
break;
}
case 'ai_activity': {
const entry = msg.payload as AIActivityEntry;
setAiActivity((prev) => {
const idx = prev.findIndex((a) => a.agent_id === entry.agent_id);
if (idx >= 0) {
const next = [...prev];
next[idx] = entry;
return next;
}
return [...prev, entry];
});
break;
}
case 'agent_log': {
const { agent_id, content } = msg.payload as { agent_id: string; content: string };
if (agent_id) {
setAgentLogs((prev) => ({ ...prev, [agent_id]: content }));
}
break;
}
}
} catch (err) {
console.error('Failed to parse WebSocket message:', err);
@@ -110,13 +148,20 @@ export function useWebSocket(): UseWebSocketReturn {
}, []);
useEffect(() => {
unmounted.current = false;
connect();
return () => {
if (wsRef.current) {
wsRef.current.close();
unmounted.current = true;
if (reconnectTimer.current) {
clearTimeout(reconnectTimer.current);
}
const ws = wsRef.current;
if (ws) {
ws.onclose = null;
ws.close();
}
};
}, [connect]);
return { isConnected, agents, stats, recentShares };
return { isConnected, agents, recentShares, fleetAlerts, poolStatus, aiActivity, agentLogs };
}