import { useState, useCallback } from 'react'; const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || (import.meta.env.DEV ? 'http://localhost:8000' : ''); export default function useOperatorSSE() { const [toolCalls, setToolCalls] = useState([]); const [projectStatuses, setProjectStatuses] = useState({}); const [briefText, setBriefText] = useState(null); const [isScanning, setIsScanning] = useState(false); const [isLive, setIsLive] = useState(false); const startScan = useCallback(() => { setToolCalls([]); setProjectStatuses({}); setBriefText(null); setIsScanning(true); setIsLive(false); const source = new EventSource(`${BACKEND_URL}/api/run`); source.addEventListener('mode', (e) => { const data = JSON.parse(e.data); setIsLive(data.live); }); source.addEventListener('tool_call', (e) => { const data = JSON.parse(e.data); setToolCalls(prev => [...prev, { tool: data.tool, project: data.project, status: 'running' }]); }); source.addEventListener('tool_result', (e) => { const data = JSON.parse(e.data); setToolCalls(prev => { const updated = [...prev]; const idx = updated.findLastIndex(t => t.tool === data.tool && t.project === data.project); if (idx >= 0) updated[idx] = { ...updated[idx], status: 'done' }; return updated; }); }); source.addEventListener('initiative', (e) => { const data = JSON.parse(e.data); setProjectStatuses(prev => ({ ...prev, [data.project]: data })); }); source.addEventListener('brief', (e) => { const data = JSON.parse(e.data); setBriefText(data.text); setIsScanning(false); source.close(); }); source.onerror = () => { setIsScanning(false); source.close(); }; }, []); const sendChat = useCallback(async (message) => { const res = await fetch(`${BACKEND_URL}/api/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message }), }); const reader = res.body.getReader(); const decoder = new TextDecoder(); let replyText = ''; let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.startsWith('data:') || line.startsWith('data: ')) { try { const data = JSON.parse(line.replace(/^data:\s*/, '')); if (data.type === 'chat_reply') { replyText = data.text; } } catch { /* skip */ } } } } return replyText; }, []); return { toolCalls, projectStatuses, briefText, isScanning, isLive, startScan, sendChat }; }