/** * Browser half of the gateway API: same-origin JSON endpoints under * `/api/codex-plus/*`. Plain `fetch`; the host webserver's authority checks * gate the `/api` prefix, so a remote or untrusted origin cannot reach it. * * @module dsh-subagent-codex-plus/client/api */ import type { GatewayActionResponse, GatewayStateResponse, } from '../shared/types.ts' /** Browser-facing gateway API. */ export interface GatewayApi { state(sessionId: string): Promise attach(sessionId: string, threadId?: string): Promise detach(sessionId: string): Promise queueDelete(sessionId: string, id: string): Promise queueUpdate(sessionId: string, id: string, text: string): Promise queueReorder(sessionId: string, ids: readonly string[]): Promise steer(sessionId: string, text: string): Promise cancel(sessionId: string): Promise } const BASE = '/api/codex-plus' /** GET JSON (no body). */ async function getJson(path: string): Promise { const response = await fetch(path) if (!response.ok) { throw new Error(`${path} failed: ${response.status}`) } return (await response.json()) as T } /** POST JSON. */ async function postJson(path: string, body: unknown): Promise { const response = await fetch(path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body), }) if (!response.ok) { throw new Error(`${path} failed: ${response.status}`) } return (await response.json()) as T } /** The live host API instance (failures surface per call). */ export function makeGatewayApi(): GatewayApi { return { state: (sessionId) => getJson(`${BASE}/state?session=${encodeURIComponent(sessionId)}`), attach: (sessionId, threadId) => postJson(`${BASE}/attach`, { sessionId, ...threadId === undefined ? {} : { threadId } }), detach: (sessionId) => postJson(`${BASE}/detach`, { sessionId }), queueDelete: (sessionId, id) => postJson(`${BASE}/queue/delete`, { sessionId, id }), queueUpdate: (sessionId, id, text) => postJson(`${BASE}/queue/update`, { sessionId, id, text }), queueReorder: (sessionId, ids) => postJson(`${BASE}/queue/reorder`, { sessionId, ids }), steer: (sessionId, text) => postJson(`${BASE}/steer`, { sessionId, text }), cancel: (sessionId) => postJson(`${BASE}/cancel`, { sessionId }), } }