import { config } from "./config.js"; import type { AgentEvent, ConnectionStatus } from "./types.js"; type EventCallback = (event: AgentEvent) => void; function createWebSocketClient(url: string) { let connectionStatus: ConnectionStatus = $state("disconnected"); let ws: WebSocket | null = null; let reconnectDelay = 1000; let reconnectTimer: ReturnType | null = null; let manualDisconnect = false; const callbacks: EventCallback[] = []; function connect() { if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) { return; } manualDisconnect = false; connectionStatus = "connecting"; ws = new WebSocket(url); ws.onopen = () => { connectionStatus = "connected"; reconnectDelay = 1000; }; ws.onmessage = (event: MessageEvent) => { try { const data = JSON.parse(event.data as string) as AgentEvent; for (const cb of callbacks) { cb(data); } } catch { // ignore malformed messages } }; ws.onclose = () => { connectionStatus = "disconnected"; ws = null; if (!manualDisconnect) { scheduleReconnect(); } }; ws.onerror = () => { ws?.close(); }; } function scheduleReconnect() { if (reconnectTimer) clearTimeout(reconnectTimer); reconnectTimer = setTimeout(() => { reconnectTimer = null; connect(); }, reconnectDelay); reconnectDelay = Math.min(reconnectDelay * 2, 10000); } function disconnect() { manualDisconnect = true; if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } ws?.close(); ws = null; connectionStatus = "disconnected"; } function onEvent(callback: EventCallback) { callbacks.push(callback); return () => { const idx = callbacks.indexOf(callback); if (idx !== -1) callbacks.splice(idx, 1); }; } return { get connectionStatus() { return connectionStatus; }, connect, disconnect, onEvent, }; } export const wsClient = createWebSocketClient(config.wsUrl);