// API layer — talks to the orchestra backend (Fastify). // Base resolves automatically; override with window.ORC_API_BASE if needed. const API_BASE = (() => { if (window.ORC_API_BASE) return window.ORC_API_BASE; const h = location.hostname; if (h.endsWith('zenixbot.com')) return 'https://api.zenixbot.com'; return 'http://localhost:3777'; // local dev default })(); async function req(path, opts = {}) { const hasBody = opts.body !== undefined; const res = await fetch(`${API_BASE}${path}`, { ...opts, headers: hasBody ? { 'content-type': 'application/json', ...opts.headers } : opts.headers, body: hasBody ? JSON.stringify(opts.body) : undefined, }); const text = await res.text(); const data = text ? JSON.parse(text) : null; if (!res.ok) { const err = new Error(data?.error || `HTTP ${res.status}`); err.status = res.status; err.data = data; throw err; } return data; } window.ORC_API = { base: API_BASE, health: () => req('/api/health'), getBoard: () => req('/api/board'), getTask: (id) => req(`/api/tasks/${id}`), createTask: (body) => req('/api/tasks', { method: 'POST', body }), transition: (id, to) => req(`/api/tasks/${id}/transition`, { method: 'POST', body: { to } }), approve: (id) => req(`/api/tasks/${id}/approve`, { method: 'POST' }), reject: (id) => req(`/api/tasks/${id}/reject`, { method: 'POST' }), getLogs: (id) => req(`/api/tasks/${id}/logs`), getConnections: () => req('/api/connections'), loginConnection: (id) => req(`/api/connections/${id}/login`, { method: 'POST' }), // SSE live stream of {type:'task'|'log', ...}. Returns the EventSource. stream: (onEvent) => { const es = new EventSource(`${API_BASE}/api/stream`); es.onmessage = (e) => { try { onEvent(JSON.parse(e.data)); } catch { /* ignore */ } }; return es; }, };