// 统一的 API 端点常量,消除硬编码字符串 export const API = { config: '/api/config', restart: '/api/restart', health: '/health', mcpStatus: '/api/mcp/status', skills: '/api/skills', skillsToggle: '/api/skills/toggle', subagents: '/api/subagents', subagentsToggle: '/api/subagents/toggle', experts: '/api/experts', expertsToggle: '/api/experts/toggle', expertsCreate: '/api/experts/create', expertsUpdate: '/api/experts/update', expertsDelete: '/api/experts/delete', expertsSelected: '/api/experts/selected', expertsSelect: '/api/experts/select', } as const /** * 基础 fetch 封装:自动添加 JSON headers,解析响应。 * 返回 [data, error] 元组,不抛异常。 */ export async function apiFetch( endpoint: string, options?: { method?: string; body?: unknown; signal?: AbortSignal } ): Promise<[T | null, { status: number; message: string } | null]> { try { const resp = await fetch(endpoint, { method: options?.method ?? 'GET', headers: options?.body ? { 'Content-Type': 'application/json' } : undefined, body: options?.body ? JSON.stringify(options.body) : undefined, signal: options?.signal, }) const data = await resp.json().catch(() => null) if (!resp.ok) { return [null, { status: resp.status, message: data?.message || data?.error || `HTTP ${resp.status}` }] } return [data as T, null] } catch (e) { return [null, { status: 0, message: e instanceof Error ? e.message : 'Network error' }] } } /** * GET 请求,静默失败返回 null(用于列表加载等场景)。 */ export async function apiGetSilent(endpoint: string): Promise { try { const resp = await fetch(endpoint) if (!resp.ok) return null return await resp.json() as T } catch { return null } }