/** * MindLyst backend client — typed HTTP wrappers for the mindlyst-backend (port 4014). * * Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service). * Admin callers may pass x-admin-user-id to operate on behalf of any user. */ import { config } from './config.js'; export interface MindlystClientOptions { token?: string; requestId?: string; /** When set (admin only) — overrides the JWT sub used as userId in backend routes */ adminUserId?: string; } // ── Shared fetch helper ──────────────────────────────────────────────────── async function mindlystFetch( path: string, init: RequestInit, opts: MindlystClientOptions ): Promise { const headers: Record = { 'Content-Type': 'application/json', ...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}), ...(opts.requestId ? { 'x-request-id': opts.requestId } : {}), ...(opts.adminUserId ? { 'x-admin-user-id': opts.adminUserId } : {}), }; const res = await fetch(`${config.MINDLYST_BACKEND_URL}/api${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(`mindlyst-backend ${init.method ?? 'GET'} ${path} → ${res.status}: ${body}`); } return res.json() as Promise; } // ── Memory items ─────────────────────────────────────────────────────────── export interface MemoryItemDoc { id: string; productId: string; userId: string; sourceType: string; captureSurface: string; rawContent: string; triageResult: { contentType: string; summary?: string; urgencyScore: number; emotionScore: number; confidenceScore: number; suggestedBrainId: string; entities: string[]; suggestedActions: string[]; }; brainIds: string[]; actedOn: boolean; actedOnAt: string | null; nudgeCount: number; reminderAt?: string; isSensitive: boolean; createdAt: string; updatedAt: string; } export function mindlystMemoryList( params: { brainId?: string; filter?: 'forgotten' | 'completed_today'; limit?: number; offset?: number; productId?: string; }, opts: MindlystClientOptions ): Promise<{ items: MemoryItemDoc[]; limit: number; offset: number }> { const qs = new URLSearchParams(); if (params.brainId) qs.set('brainId', params.brainId); if (params.filter) qs.set('filter', params.filter); if (params.limit !== undefined) qs.set('limit', String(params.limit)); if (params.offset !== undefined) qs.set('offset', String(params.offset)); if (params.productId) qs.set('productId', params.productId); const q = qs.toString(); return mindlystFetch(`/memory-items${q ? `?${q}` : ''}`, { method: 'GET' }, opts); } export function mindlystMemoryRetriage( itemId: string, opts: MindlystClientOptions ): Promise { return mindlystFetch(`/memory-items/${itemId}/retriage`, { method: 'POST' }, opts); } export function mindlystMemoryPatch( itemId: string, action: 'mark_done' | 'mark_undone' | 'increment_nudge' | 'set_reminder', reminderAt: string | undefined, opts: MindlystClientOptions ): Promise { return mindlystFetch( `/memory-items/${itemId}`, { method: 'PATCH', body: JSON.stringify({ action, ...(reminderAt ? { reminderAt } : {}) }) }, opts ); } export function mindlystMemoryReassign( itemId: string, newBrainId: string, opts: MindlystClientOptions ): Promise { return mindlystFetch( `/memory-items/${itemId}/reassign`, { method: 'PUT', body: JSON.stringify({ newBrainId }) }, opts ); } // ── Brains ───────────────────────────────────────────────────────────────── export interface BrainDoc { id: string; userId: string; productId: string; name: string; rolePrompt?: string; tone?: string; colorFrom?: string; colorTo?: string; createdAt: string; updatedAt: string | null; } export function mindlystBrainsList( params: { limit?: number; offset?: number }, opts: MindlystClientOptions ): Promise<{ items: BrainDoc[]; total: number; limit: number; offset: 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)); const q = qs.toString(); return mindlystFetch(`/brains${q ? `?${q}` : ''}`, { method: 'GET' }, opts); } // ── Daily briefs ─────────────────────────────────────────────────────────── export interface DailyBriefDoc { id: string; userId: string; productId: string; date: string; greeting?: string; priorityItems: unknown[]; brainSummaries: Record; streakMessage?: string; motivationalQuote?: string; createdAt: string; } export function mindlystBriefsGetToday(opts: MindlystClientOptions): Promise { return mindlystFetch('/daily-briefs/today', { method: 'GET' }, opts); } export function mindlystBriefsList( params: { limit?: number; offset?: number }, opts: MindlystClientOptions ): Promise<{ items: DailyBriefDoc[]; 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)); const q = qs.toString(); return mindlystFetch(`/daily-briefs${q ? `?${q}` : ''}`, { method: 'GET' }, opts); } // ── Streaks ──────────────────────────────────────────────────────────────── export interface StreakDoc { id: string; userId: string; productId: string; currentStreak: number; longestStreak: number; lastActiveDate: string; streakFreezeAvailable: boolean; totalActiveDays: number; createdAt: string; updatedAt: string; } export function mindlystStreaksGet(opts: MindlystClientOptions): Promise { return mindlystFetch('/streaks', { method: 'GET' }, opts); } // ── Reflections ──────────────────────────────────────────────────────────── export interface ReflectionDoc { id: string; userId: string; productId: string; weekStartDate: string; themes: string[]; highlights: string[]; challenges: string[]; summary?: string; createdAt: string; } export function mindlystReflectionsList( params: { limit?: number; offset?: number }, opts: MindlystClientOptions ): Promise<{ items: ReflectionDoc[]; 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)); const q = qs.toString(); return mindlystFetch(`/reflections${q ? `?${q}` : ''}`, { method: 'GET' }, opts); }