feat: alive UI wave, galaxy presence, spread and fleet enhancements
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Dashboard ambient layer, comrade presence, Mission Deck and War Room, Emberwake supply chain, spread/docs publishing, fleet policy and modules API, CI docker mining, and refreshed USB pack.
This commit is contained in:
AetherForge
2026-06-04 22:36:17 -07:00
parent 1551bd5dad
commit a32860b0d9
154 changed files with 17383 additions and 601 deletions

View File

@@ -5,10 +5,12 @@ import type {
WSStatsUpdate,
WSCommandResult,
WSAgentLog,
WSPolicyAck,
} from '../types/ws';
import type { Agent, Share, FleetAlert, PoolStatus, AIActivityEntry, WSMessage } from '../types';
import { WebSocketContext } from './WebSocketContext';
import type { SeqCommandResult } from './WebSocketContext';
import type { SeqPolicyAck } from './WebSocketContext';
import { authHeaders, getStoredAuth } from '../api/auth';
/**
@@ -28,9 +30,17 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
const [aiActivity, setAiActivity] = useState<AIActivityEntry[]>([]);
const [agentLogs, setAgentLogs] = useState<Record<string, string>>({});
const [commandResults, setCommandResults] = useState<SeqCommandResult[]>([]);
const [policyAcks, setPolicyAcks] = useState<SeqPolicyAck[]>([]);
const [latestMessage, setLatestMessage] = useState<WSMessage | null>(null);
// Monotonic counter so consumers can detect new entries even after the ring buffer trims old ones
const cmdSeqRef = useRef(0);
const policyAckSeqRef = useRef(0);
const sendDashboardMessage = useCallback((type: string, payload: Record<string, unknown>) => {
const ws = wsRef.current;
if (!ws || ws.readyState !== WebSocket.OPEN) return;
ws.send(JSON.stringify({ type, payload }));
}, []);
const connect = useCallback(() => {
if (unmounted.current) return;
@@ -231,11 +241,35 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
}
break;
}
case 'policy_ack': {
const ack = msg.payload as WSPolicyAck;
policyAckSeqRef.current += 1;
setPolicyAcks((prev) => [...prev.slice(-49), { ...ack, _seq: policyAckSeqRef.current }]);
break;
}
case 'agent_log': {
const { agent_id, content } = msg.payload as WSAgentLog;
if (agent_id) setAgentLogs((prev) => ({ ...prev, [agent_id]: content }));
break;
}
case 'agent_capabilities': {
const { agent_id, capabilities } = msg.payload as {
agent_id: string;
capabilities: Agent['capabilities'];
};
if (!agent_id || !capabilities) break;
setAgents((prev) =>
prev.map((a) =>
a.id === agent_id
? {
...a,
capabilities: { ...a.capabilities, ...capabilities },
}
: a
)
);
break;
}
}
} catch (err) {
console.error('Failed to parse WebSocket message:', err);
@@ -263,7 +297,7 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
return (
<WebSocketContext.Provider value={{
isConnected, agents, recentShares, fleetAlerts, poolStatus,
aiActivity, agentLogs, commandResults, latestMessage,
aiActivity, agentLogs, commandResults, policyAcks, latestMessage, sendDashboardMessage,
}}>
{children}
</WebSocketContext.Provider>