export type Fragment = { id: string; content: string; created_at: string; }; export type User = { id: string; label: string; role: "admin" | "member"; debug_sharing: boolean; }; export type Snapshot = { title: string | null; summary: string | null; maturity_ai: number; maturity_override: number | null; confidence: number | null; motion: string; position: string; tension: string; trajectory: string; possible_moves: string[]; change_kind: "analysis" | "manual_calibration" | "legacy_partial"; created_at: string; }; export type Idea = { id: string; title: string; summary: string; maturity_ai: number; maturity_override: number | null; maturity: number; is_overridden: boolean; confidence: number; motion: string; position: string; tension: string; trajectory: string; possible_moves: string[]; fragment_count: number; latest_fragment_at: string | null; updated_at: string; fragments?: Fragment[]; snapshots?: Snapshot[]; }; export type AdminOverview = { totals: { users: number; fragments: number; ideas: number; pending: number; audit_events: number; }; last_24h: { runs: number; successes: number; errors: number; success_rate: number; avg_duration_ms: number; input_tokens: number; output_tokens: number; reasoning_tokens: number; cached_tokens: number; model_rounds: number; tool_calls: number; }; runtime: { uptime_seconds: number; curator_configured: boolean; queue_depth: number; model: string; prompt_version: string; }; daily: Array<{ day: string; runs: number; successes: number; errors: number; reasoning_tokens: number; }>; recent_errors: Array<{ id: string; started_at: string; error_type: string; error_message: string; user_label: string; }>; audit_24h: { events: number; failed_logins: number; }; narrative: string; }; export type AgentRun = { id: string; user_id: string; fragment_id: string; status: "running" | "success" | "error"; prompt_version: string; model: string; thinking_effort: string; started_at: string; finished_at: string | null; duration_ms: number | null; attempt_count: number; model_rounds: number; tool_calls: number; search_calls: number; inspection_calls: number; input_tokens: number; output_tokens: number; reasoning_tokens: number; cached_tokens: number; error_type: string | null; error_message: string | null; user_label: string; user_role: string; debug_sharing: boolean; content_length: number; }; export type AgentEvent = { id: string; event_type: string; event_at: string; duration_ms: number | null; payload: Record; }; export type AgentRunDetail = { run: AgentRun & { content_sha256: string }; fragment: | { content_visible: true; content: string } | { content_visible: false; content_length: number; content_sha256: string }; events: AgentEvent[]; privacy: { content_visible: boolean; reasoning_content_stored: boolean; reason: string; }; }; export type AdminUser = User & { created_at: string; fragment_count: number; idea_count: number; }; export type AuditEvent = { id: string; event_type: string; created_at: string; user_label: string | null; payload: Record; }; type ApiOptions = RequestInit & { body?: string }; export class ApiError extends Error { status: number; constructor(status: number, message: string) { super(message); this.status = status; } } async function request(path: string, options: ApiOptions = {}): Promise { const response = await fetch(path, { ...options, credentials: "same-origin", headers: { "Content-Type": "application/json", "X-Note-Client": "xuxiang-web", ...(options.headers ?? {}), }, }); if (!response.ok) { let message = "请求没有完成"; try { const body = await response.json(); if (typeof body.detail === "string") message = body.detail; } catch { // Keep the calm fallback message. } throw new ApiError(response.status, message); } return response.json() as Promise; } export const api = { session: () => request<{ authenticated: boolean; user: User }>("/api/session"), login: (accessKey: string) => request<{ authenticated: boolean; user: User }>("/api/login", { method: "POST", body: JSON.stringify({ access_key: accessKey }), }), logout: () => request<{ authenticated: boolean }>("/api/logout", { method: "POST" }), fragments: () => request<{ items: Fragment[] }>("/api/fragments"), createFragment: (content: string) => request("/api/fragments", { method: "POST", body: JSON.stringify({ content }), }), ideas: () => request<{ items: Idea[] }>("/api/ideas"), idea: (id: string) => request(`/api/ideas/${id}`), updateMaturity: (id: string, maturity: number | null) => request(`/api/ideas/${id}/position`, { method: "PATCH", body: JSON.stringify({ maturity }), }), status: () => request<{ pending: number; curator_configured: boolean }>( "/api/system/status", ), updateDebugSharing: (enabled: boolean) => request("/api/account/debug-sharing", { method: "PATCH", body: JSON.stringify({ enabled }), }), adminOverview: () => request("/api/admin/overview"), adminUsers: () => request<{ items: AdminUser[] }>("/api/admin/users"), adminRuns: (limit = 80) => request<{ items: AgentRun[] }>(`/api/admin/runs?limit=${limit}`), adminRun: (id: string) => request(`/api/admin/runs/${id}`), retryAdminRun: (id: string) => request<{ queued: boolean }>(`/api/admin/runs/${id}/retry`, { method: "POST", }), adminAudit: (limit = 80) => request<{ items: AuditEvent[] }>(`/api/admin/audit?limit=${limit}`), };