/** * ChronoMind backend client — typed HTTP wrappers for the chronomind-backend (port 4011). * * Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service). */ import { config } from './config.js'; export interface ChronoMindClientOptions { token?: string; requestId?: string; } async function chronomindFetch( path: string, init: RequestInit, opts: ChronoMindClientOptions ): Promise { const headers: Record = { 'Content-Type': 'application/json', ...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}), ...(opts.requestId ? { 'x-request-id': opts.requestId } : {}), }; const res = await fetch(`${config.CHRONOMIND_BACKEND_URL}${path}`, { ...init, headers: { ...((init.headers as Record) ?? {}), ...headers }, signal: AbortSignal.timeout(15_000), }); if (!res.ok) { const body = await res.text().catch(() => ''); throw new Error(`chronomind-backend ${init.method ?? 'GET'} ${path} → ${res.status}: ${body}`); } return res.json() as Promise; } // ── Timers ───────────────────────────────────────────────────────────────── export interface TimerDoc { id: string; userId: string; productId: string; label: string; description?: string; type: 'alarm' | 'countdown' | 'pomodoro' | 'event'; state: 'idle' | 'active' | 'paused' | 'warning' | 'fired' | 'completed'; urgency: 'critical' | 'important' | 'standard' | 'gentle' | 'passive'; duration?: number; targetTime?: string; category?: string; createdAt: string; lastSyncedAt: string; syncVersion: number; } export function chronomindTimersList( params: { limit?: number; offset?: number; type?: string; state?: string }, opts: ChronoMindClientOptions ): Promise<{ items: TimerDoc[]; total: number }> { const qs = new URLSearchParams(); if (params.limit !== undefined) qs.set('limit', String(params.limit)); if (params.offset !== undefined) qs.set('offset', String(params.offset)); if (params.type) qs.set('type', params.type); if (params.state) qs.set('state', params.state); const q = qs.toString(); return chronomindFetch(`/timers${q ? `?${q}` : ''}`, { method: 'GET' }, opts); } export function chronomindSyncStatus(opts: ChronoMindClientOptions): Promise<{ userId: string; productId: string; totalTimers: number; active: number; pending: number; unsyncedCount: number; lastSyncedAt: string | null; generatedAt: string; }> { return chronomindFetch('/timers/sync-status', { method: 'GET' }, opts); } // ── Routines ─────────────────────────────────────────────────────────────── export interface RoutineDoc { id: string; userId: string; productId: string; name: string; description?: string; steps: unknown[]; totalDurationMinutes: number; status: 'idle' | 'running' | 'paused' | 'completed'; isTemplate: boolean; category?: string; createdAt: string; lastSyncedAt: string; syncVersion: number; } export function chronomindRoutinesList( params: { limit?: number; offset?: number; isTemplate?: boolean }, opts: ChronoMindClientOptions ): Promise<{ items: RoutineDoc[]; total: number }> { const qs = new URLSearchParams(); if (params.limit !== undefined) qs.set('limit', String(params.limit)); if (params.offset !== undefined) qs.set('offset', String(params.offset)); if (params.isTemplate !== undefined) qs.set('isTemplate', String(params.isTemplate)); const q = qs.toString(); return chronomindFetch(`/routines${q ? `?${q}` : ''}`, { method: 'GET' }, opts); } export function chronomindTimerDelete( timerId: string, opts: ChronoMindClientOptions ): Promise<{ success: boolean }> { return chronomindFetch(`/timers/${timerId}`, { method: 'DELETE' }, opts); }