learning_ai_common_plat/services/mcp-server/src/lib/nomgap-client.ts
saravanakumardb1 481760fba1 feat(mcp-server): fill DOMAIN_PRODUCTS.md tool gaps — +17 tools across 6 products
- mindlyst: +memory.get (single item with full TriageResult)
- lysnrai: +transcripts.get, +orgs.get
- jarvis: +agents.create, +marketplace.listPending/certify/suspend/feature
- chronomind: +timers.create, +households.list
- nomgap: +protocols.list, +bodyStages.list
- peakpulse: +sessions.get
2026-03-05 13:15:22 -08:00

145 lines
4.9 KiB
TypeScript

/**
* NomGap backend client — typed HTTP wrappers for the nomgap-backend (port 4013).
*
* Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service).
*/
import { config } from './config.js';
export interface NomGapClientOptions {
token?: string;
requestId?: string;
productId?: string;
}
async function nomgapFetch<T>(
path: string,
init: RequestInit,
opts: NomGapClientOptions
): Promise<T> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
...(opts.requestId ? { 'x-request-id': opts.requestId } : {}),
...(opts.productId ? { 'x-product-id': opts.productId } : { 'x-product-id': 'nomgap' }),
};
const res = await fetch(`${config.NOMGAP_BACKEND_URL}/api${path}`, {
...init,
headers: { ...((init.headers as Record<string, string>) ?? {}), ...headers },
signal: AbortSignal.timeout(15_000),
});
if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`nomgap-backend ${init.method ?? 'GET'} ${path}${res.status}: ${body}`);
}
return res.json() as Promise<T>;
}
// ── Fasting sessions ───────────────────────────────────────────────────────
export interface FastingSessionDoc {
id: string;
userId: string;
productId: string;
protocolId: string;
startedAt: string;
targetDurationMs: number;
status: 'active' | 'completed' | 'broken';
stages: unknown[];
moodCheckins: unknown[];
waterIntake: unknown[];
metrics: {
actualDurationMs: number;
completionRatio: number;
peakAutophagyConfidence: number;
totalPausedMs: number;
moodCheckinCount: number;
averageEnergy: number | null;
averageMood: number | null;
};
createdAt: string;
updatedAt: string;
}
export function nomgapFastingSessionsList(
params: { limit?: number; offset?: number; status?: string; from?: string; to?: string },
opts: NomGapClientOptions
): Promise<{ items: FastingSessionDoc[]; 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.status) qs.set('status', params.status);
if (params.from) qs.set('from', params.from);
if (params.to) qs.set('to', params.to);
const q = qs.toString();
return nomgapFetch(`/fasting/sessions${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function nomgapFastingGetStats(opts: NomGapClientOptions): Promise<Record<string, unknown>> {
return nomgapFetch('/fasting/stats', { method: 'GET' }, opts);
}
export function nomgapFastingGetWeeklyStats(
opts: NomGapClientOptions
): Promise<Record<string, unknown>> {
return nomgapFetch('/fasting/stats/weekly', { method: 'GET' }, opts);
}
// ── Protocols ─────────────────────────────────────────────────────────────
export function nomgapProtocolsList(
opts: NomGapClientOptions
): Promise<{ protocols: unknown[]; total: number }> {
return nomgapFetch('/fasting/protocols', { method: 'GET' }, opts);
}
// ── Body stages (public — no auth required) ────────────────────────────────
export function nomgapBodyStagesList(
opts: Pick<NomGapClientOptions, 'requestId'>
): Promise<{ stages: unknown[]; total: number }> {
return nomgapFetch('/fasting/stages', { method: 'GET' }, opts);
}
// ── Push triggers ──────────────────────────────────────────────────────────
export type PushTriggerType =
| 'streak_risk'
| 'fast_milestone'
| 'stage_transition'
| 'social_invite'
| 'weekly_digest'
| 'achievement_unlocked'
| 'refeeding_reminder';
export interface PushTriggerDoc {
id: string;
productId: string;
type: PushTriggerType;
userId: string;
variables: Record<string, string>;
status: 'pending' | 'sent' | 'skipped' | 'failed';
scheduledAt: string;
createdAt: string;
}
export function nomgapPushFire(
input: {
type: PushTriggerType;
userId: string;
variables?: Record<string, string>;
scheduledAt?: string;
},
opts: NomGapClientOptions
): Promise<PushTriggerDoc> {
return nomgapFetch('/push-triggers', { method: 'POST', body: JSON.stringify(input) }, opts);
}
export function nomgapPushGetStats(opts: NomGapClientOptions): Promise<Record<string, unknown>> {
return nomgapFetch('/push-triggers/stats', { method: 'GET' }, opts);
}
export function nomgapPushGetPending(opts: NomGapClientOptions): Promise<PushTriggerDoc[]> {
return nomgapFetch('/push-triggers/pending', { method: 'GET' }, opts);
}