/** * dashboard-client/src/api/client.ts — typed fetch wrappers using A1 contracts. * * PREVENT-PI-004: every request targets a relative path (loopback-only — * the dashboard server is the same origin that serves this static bundle). * No absolute URLs, no external hosts. * * Uses the ENDPOINTS registry from A1 as the single source of truth for * paths + methods. Response types come from the api-contracts domain modules. */ import { ENDPOINTS } from "@contracts"; import type { SnapshotResponse, VersionResponse, IndexesSummaryResponse, IndexFallbackResponse, ReposResponse, SummaryResponse, DriftReportResponse, ServersResponse, GameStateResponse, GameStatePatch, GameScoreRow, GameScoresQuery, PerfResponse, PerfQuery, PerfSamplesResponse, AchievementRow, SessionsResponse, SessionTimeseriesResponse, TopicsResponse, TurnsResponse, ConversationTurnsResponse, RewindIntentsResponse, ForkResponse, PruneTurnsResponse, TopicMemoriesResponse, DbStatsResponse, SchemaHealthResponse, MaintenanceAction, MaintenanceActionResult, DebugBundleResponse, ProviderCacheResponse, CacheStripesResponse, SetupStatusResponse, SetupDetectResponse, SetupConfigureRequest, SetupConfigureResponse, EmbedderHealthResponse, RaptorTreeResponse, RaptorBuildHistoryResponse, SettingsResponse, SettingsUpdateRequest, SettingsResponsePost, RagMetricsResponse, WikiIndexResponse, WikiPageResponse, CurationResult, RenameTopicRequest, MergeTopicsRequest, SplitTopicRequest, TopicTimelineResponse, TopicEvolutionResponse, } from "@contracts"; // Shared HTTP helpers (ApiError/getJson/putJson/postJson/query) live in // client-http.ts (delegate-shell split keeps client.ts under 400 lines). import { getJson, putJson, postJson, query } from "./client-http.js"; // Newer endpoint groups (model thresholds, PC-C prefix-stability) live in // client-extra.ts; re-exported here so downstream imports are unchanged. export { fetchModelThresholds, putModelThreshold, deleteModelThreshold, fetchPrefixStability, } from "./client-extra.js"; // ─── Endpoint wrappers ────────────────────────────────────────────────────── export function fetchSnapshot(): Promise { return getJson(ENDPOINTS.snapshot.path); } export function fetchVersion(): Promise { return getJson(ENDPOINTS.version.path); } export function fetchIndex(): Promise< IndexesSummaryResponse | IndexFallbackResponse > { return getJson( ENDPOINTS.index.path, ); } export function fetchRepos(activeHours?: number): Promise { return getJson( `${ENDPOINTS.repos.path}${query({ active: activeHours ? `${activeHours}h` : undefined })}`, ); } export function fetchSummary(): Promise { return getJson(ENDPOINTS.summary.path); } export function fetchDrift(): Promise { return getJson(ENDPOINTS.drift.path); } export function fetchServers(): Promise { return getJson(ENDPOINTS.servers.path); } export function fetchGameState(): Promise { return getJson(ENDPOINTS.getGameState.path); } export function putGameState( patch: GameStatePatch, ): Promise { return putJson(ENDPOINTS.putGameState.path, patch); } export function fetchGameScores( params: GameScoresQuery = {}, ): Promise { return getJson( `${ENDPOINTS.gameScores.path}${query({ metric: params.metric, limit: params.limit })}`, ); } export function fetchPerf(params: PerfQuery = {}): Promise { return getJson( `${ENDPOINTS.perf.path}${query({ minutes: params.minutes })}`, ); } /** GET /api/perf/samples — raw perf samples for one kind (chart drill-down). */ export function fetchPerfSamples( kind: string, minutes?: number, ): Promise { return getJson( `${ENDPOINTS.perfSamples.path}${query({ kind, minutes })}`, ); } export function fetchAchievements(): Promise { return getJson(ENDPOINTS.achievements.path); } export function fetchSessions(): Promise { return getJson(ENDPOINTS.sessions.path); } export function fetchSessionTimeseries( minutes: number, ): Promise { return getJson( `${ENDPOINTS.sessionTimeseries.path}${query({ minutes })}`, ); } export function fetchTopics(): Promise { return getJson(ENDPOINTS.topics.path); } /** Lifetime provider prompt cache aggregates + $ savings estimate. */ export function fetchProviderCache(): Promise { return getJson(ENDPOINTS.providerCache.path); } /** Cache stripe distribution + health score (A3). */ export function fetchCacheStripes(): Promise { return getJson(ENDPOINTS.cacheStripes.path); } // ── S52: turn-by-turn memory tracking + recall + rewind ─────────────── export function fetchTurns(): Promise { return getJson(ENDPOINTS.turns.path); } export function fetchConversationTurns( conversationId: string, ): Promise { return getJson( `${ENDPOINTS.conversationTurns.path.replace(":convId", encodeURIComponent(conversationId))}`, ); } export function fetchTurnIntents(): Promise { return getJson(ENDPOINTS.turnIntents.path); } export function postTurnIntent( conversationId: string, targetTurnIndex: number, ): Promise { return postJson(ENDPOINTS.postTurnIntent.path, { conversationId, targetTurnIndex, }); } export function postFork( conversationId: string, turnIndex: number, ): Promise { return postJson(ENDPOINTS.fork.path, { conversationId, turnIndex, }); } export function postPruneTurns( maxTurnAgeMs: number, keepMinPerConversation = 50, ): Promise { return postJson(ENDPOINTS.pruneTurns.path, { maxTurnAgeMs, keepMinPerConversation, }); } export function fetchTopicMemories( topicId: string, ): Promise { return getJson( ENDPOINTS.topicMemories.path.replace( ":topicId", encodeURIComponent(topicId), ), ); } // ─── Maintenance (S49B) ───────────────────────────────────────────────── export function fetchDbStats(): Promise { return getJson(ENDPOINTS.maintenanceStats.path); } export function fetchSchemaHealth(): Promise { return getJson(ENDPOINTS.schemaHealth.path); } export function postMaintenanceAction( action: MaintenanceAction, ): Promise { return postJson( ENDPOINTS.maintenanceAction.path, action, ); } export function fetchDebugBundle(): Promise { return postJson("/api/maintenance/gather-debug", {}); } // ─── Setup wizard (P0b) ──────────────────────────────────────────────────── /** GET /api/setup-status — current embedder configuration. */ export function fetchSetupStatus(): Promise { return getJson(ENDPOINTS.setupStatus.path); } /** GET /api/setup-detect — detect available local embedder backends. */ export function fetchSetupDetect(): Promise { return getJson(ENDPOINTS.setupDetect.path); } /** POST /api/setup-configure — write embedder config to .mega-compact.env. */ export function configureEmbedder( body: SetupConfigureRequest, ): Promise { return postJson(ENDPOINTS.setupConfigure.path, body); } // ─── RAPTOR tree (Part B) ───────────────────────────────────────────── /** GET /api/raptor-tree — tree for a session (defaults to latest with nodes). */ export function fetchRaptorTree( sessionId?: string, ): Promise { return getJson( `${ENDPOINTS.raptorTree.path}${query({ sessionId })}`, ); } /** GET /api/raptor-build-history — build history for a session (coherence, depth, timeout). */ export function fetchRaptorBuildHistory( sessionId?: string, ): Promise { return getJson( `${ENDPOINTS.raptorBuildHistory.path}${query({ sessionId })}`, ); } /** GET /api/embedder-health — round-trip a test embed through the active embedder. */ export function fetchEmbedderHealth(): Promise { return getJson(ENDPOINTS.embedderHealth.path); } // ─── RAG Settings ───────────────────────────────────────────────────── /** GET /api/rag-settings — read all adjustable settings grouped by category. */ export function fetchSettings(): Promise { return getJson(ENDPOINTS.ragSettings.path); } /** POST /api/rag-settings — update a single setting (writes the env file). */ export function postSetting( body: SettingsUpdateRequest, ): Promise { return postJson(ENDPOINTS.ragSettingsUpdate.path, body); } /** @deprecated Use fetchSettings instead. */ export function fetchRagSettings(): Promise { return fetchSettings(); } /** @deprecated Use postSetting instead. */ export function postRagSettings( body: SettingsUpdateRequest, ): Promise { return postSetting(body); } /** GET /api/rag-metrics — HyDE + recall-quality telemetry aggregates (H2). */ export function fetchRagMetrics(): Promise { return getJson(ENDPOINTS.ragMetrics.path); } // ─── Wiki Revival (W3) ──────────────────────────────────────────────── /** GET /api/wiki/index — wiki landing (topics with resolved labels + badges). */ export function fetchWikiIndex(): Promise { return getJson(ENDPOINTS.wikiIndex.path); } /** GET /api/wiki/topic/:topicId — single topic page + provenance. */ export function fetchWikiTopic(topicId: string): Promise { return getJson( ENDPOINTS.wikiTopic.path.replace(":topicId", encodeURIComponent(topicId)), ); } /** PUT /api/wiki/topic/:topicId/label — rename a topic (user curation). */ export function renameTopic( topicId: string, label: string, ): Promise { return putJson( ENDPOINTS.renameTopic.path.replace(":topicId", encodeURIComponent(topicId)), { label } satisfies RenameTopicRequest, ); } /** POST /api/wiki/merge — merge source topic into target. */ export function mergeTopics( sourceTopicId: string, targetTopicId: string, ): Promise { return postJson(ENDPOINTS.mergeTopics.path, { sourceTopicId, targetTopicId, } satisfies MergeTopicsRequest); } /** POST /api/wiki/topic/:topicId/split — carve listed memories into a new topic. */ export function splitTopic( topicId: string, memoryIds: string[], ): Promise { return postJson( ENDPOINTS.splitTopic.path.replace(":topicId", encodeURIComponent(topicId)), { memoryIds } satisfies SplitTopicRequest, ); } /** GET /api/wiki/topic/:topicId/timeline — per-topic memory-addition buckets. */ export function fetchTopicTimeline( topicId: string, ): Promise { return getJson( ENDPOINTS.topicTimeline.path.replace( ":topicId", encodeURIComponent(topicId), ), ); } /** GET /api/wiki/evolution — global D3 topic-evolution graph feed. */ export function fetchTopicEvolution(): Promise { return getJson(ENDPOINTS.topicEvolution.path); }