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
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user