/** * JarvisJr backend client — typed HTTP wrappers for the jarvisjr-backend (port 4012). * * Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service). */ import { config } from './config.js'; export interface JarvisClientOptions { token?: string; requestId?: string; } // ── Shared fetch helper ──────────────────────────────────────────────────── async function jarvisFetch( path: string, init: RequestInit, opts: JarvisClientOptions ): 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.JARVISJR_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(`jarvisjr-backend ${init.method ?? 'GET'} ${path} → ${res.status}: ${body}`); } return res.json() as Promise; } // ── Agents ───────────────────────────────────────────────────────────────── export interface JarvisAgentDoc { id: string; userId: string; productId: string; name: string; role: string; systemPrompt: string; voiceId?: string; coachingFramework?: string; accentColor?: string; welcomeMessage?: string; sessionLength?: number; difficultyLevel?: string; language?: string; privacyLevel?: string; checkInSchedule?: string; isTemplate?: boolean; templateSource?: string; totalSessions: number; lastSessionAt: string | null; createdAt: string; updatedAt: string; } export interface AgentCreateInput { name: string; role: string; systemPrompt: string; voiceId?: string; coachingFramework?: string; accentColor?: string; welcomeMessage?: string; sessionLength?: number; difficultyLevel?: 'beginner' | 'intermediate' | 'advanced'; language?: string; privacyLevel?: 'standard' | 'strict'; } export function jarvisAgentCreate( input: AgentCreateInput, opts: JarvisClientOptions ): Promise { return jarvisFetch('/jarvis/agents', { method: 'POST', body: JSON.stringify(input) }, opts); } export function jarvisAgentsList( params: { limit?: number; offset?: number }, opts: JarvisClientOptions ): Promise<{ agents: JarvisAgentDoc[]; 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 jarvisFetch(`/jarvis/agents${q ? `?${q}` : ''}`, { method: 'GET' }, opts); } export function jarvisAgentDuplicate( agentId: string, opts: JarvisClientOptions ): Promise { return jarvisFetch(`/jarvis/agents/${agentId}/duplicate`, { method: 'POST' }, opts); } // ── Sessions ─────────────────────────────────────────────────────────────── export interface JarvisSessionDoc { id: string; userId: string; productId: string; agentId: string; mode: string; status: 'active' | 'completed'; summary?: string; coachingNotes?: string[]; skillMetrics?: Record; duration?: number; messageCount: number; createdAt: string; completedAt: string | null; } export interface JarvisSessionStats { totalSessions: number; totalDurationMinutes: number; currentStreak: number; longestStreak: number; perAgent: Record; } export function jarvisSessionsList( params: { limit?: number; offset?: number; agentId?: string }, opts: JarvisClientOptions ): Promise<{ sessions: JarvisSessionDoc[]; 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.agentId) qs.set('agentId', params.agentId); const q = qs.toString(); return jarvisFetch(`/jarvis/sessions${q ? `?${q}` : ''}`, { method: 'GET' }, opts); } export function jarvisSessionsGetStats(opts: JarvisClientOptions): Promise { return jarvisFetch('/jarvis/sessions/stats', { method: 'GET' }, opts); } // ── Memory ───────────────────────────────────────────────────────────────── export interface JarvisMemoryDoc { id: string; agentId: string; userId: string; productId: string; sessionId?: string; type: 'skill_note' | 'preference' | 'goal' | 'context' | 'exercise'; content: string; importance: number; tags?: string[]; createdAt: string; expiresAt?: string; } export function jarvisMemoryList( agentId: string, params: { type?: string; minImportance?: number; limit?: number; offset?: number; }, opts: JarvisClientOptions ): Promise<{ memories: JarvisMemoryDoc[]; total: number }> { const qs = new URLSearchParams(); if (params.type) qs.set('type', params.type); if (params.minImportance !== undefined) qs.set('minImportance', String(params.minImportance)); 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 jarvisFetch( `/jarvis/agents/${agentId}/memory${q ? `?${q}` : ''}`, { method: 'GET' }, opts ); } // ── Marketplace admin ───────────────────────────────────────────────────── export function jarvisMarketplaceListPending( opts: JarvisClientOptions ): Promise<{ listings: unknown[]; total: number }> { return jarvisFetch('/marketplace/admin/pending', { method: 'GET' }, opts); } export function jarvisMarketplaceCertify( listingId: string, decision: { decision: 'approved' | 'rejected'; notes?: string }, opts: JarvisClientOptions ): Promise { const action = decision.decision === 'approved' ? 'approve' : 'reject'; return jarvisFetch( `/marketplace/admin/${listingId}/${action}`, { method: 'POST', body: JSON.stringify(decision) }, opts ); } export function jarvisMarketplaceSuspend( listingId: string, reason: string, opts: JarvisClientOptions ): Promise { return jarvisFetch( `/marketplace/admin/${listingId}/suspend`, { method: 'POST', body: JSON.stringify({ reason }) }, opts ); } export function jarvisMarketplaceFeature( listingId: string, featured: boolean, opts: JarvisClientOptions ): Promise { return jarvisFetch( `/marketplace/admin/${listingId}/feature`, { method: 'POST', body: JSON.stringify({ featured }) }, opts ); } export function jarvisMemoryPrune( agentId: string, opts: JarvisClientOptions ): Promise<{ pruned: number }> { return jarvisFetch(`/jarvis/agents/${agentId}/memory/prune`, { method: 'POST' }, opts); } export function jarvisMemoryGetContext( agentId: string, limit: number | undefined, opts: JarvisClientOptions ): Promise<{ memories: JarvisMemoryDoc[]; count: number }> { const qs = limit !== undefined ? `?limit=${limit}` : ''; return jarvisFetch(`/jarvis/agents/${agentId}/memory/context${qs}`, { method: 'GET' }, opts); }