/** * Mock 拦截层入口。 * * 由环境变量 NEXT_PUBLIC_ENABLE_MOCK 开关:为 "true" 时,apiRequest 命中下列路由表 * 即返回 Mock 数据,不再发起真实请求;未命中的端点放行至真实请求(并打印 warn)。 * SSE 流的 Mock 见 lib/sse-client.ts。接入真实数据后,关闭开关即可,无需改组件。 */ import type { CreateAgentRequest, CreateCharacterRequest, CreateGoalRequest, CreateImportRequest, CreateMemoryRequest, CreatePerceptionChannelRequest, CreatePlanRequest, CreateSafetyPolicyRequest, CreateSessionRequest, CreateToolRequest, CreateWorldEntityRequest, BatchReviewRequest, CreateMcpServerRequest, CreateSkillRequest, CreateWorldRelationRequest, UpdateGenerationPreferencesRequest, UpdateMemoryRequest, UpdateModelPolicyRequest, UpdateProfileRequest, UpdateSessionRequest, } from "@/types/api"; import * as data from "./data"; /** 是否启用 Mock(NEXT_PUBLIC_ 前缀使其在服务端与浏览器均可用) */ export const USE_MOCK = process.env.NEXT_PUBLIC_ENABLE_MOCK === "true"; /** 模拟网络延迟,让加载/过渡更接近真实 */ export const MOCK_LATENCY_MS = 180; /** mock 内存态:当前激活人物 id,支持切换后 getActiveCharacter 正确返回 */ let _mockActiveCharacterId = data.MOCK_CHARACTER_ID; /** mock 内存态:纪念日列表,支持 CRUD */ const _annivList: Array> = []; export const mockDelay = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); export interface MockResult { status: number; data: unknown; } const ok = (body: unknown, status = 200): MockResult => ({ status, data: body, }); function paginate(items: T[]) { return { items, page: 1, page_size: items.length, total: items.length, }; } /** * 路由匹配:命中返回 MockResult,未命中返回 undefined。 * @param method HTTP 方法 * @param path 不含 /api/v1 前缀的路径(如 /characters/active) * @param query 查询参数 * @param json 请求体 */ export function resolveMock( method: string, path: string, query?: Record, json?: unknown, ): MockResult | undefined { const m = method.toUpperCase(); // —— 健康检查 —— if (m === "GET" && path === "/health") return ok(data.health); // —— 百工:能力目录 / 授契 / 请命 / 行动账 —— if (m === "GET" && path === "/capabilities") { return ok([ { id: "cap_mock_001", name: "builtin.echo", description: "回显输入(无副作用演示能力)", executor_type: "builtin", executor_ref: "echo", risk_dims: { capability: "L0", data: "L0", consequence: "L0", context: "L0", }, data_scope: "", destinations: "", source: "builtin", version: "1", consent_mode: "allow", enabled: true, mandated: true, }, { id: "cap_mock_002", name: "qiankun.shell", description: "在乾坤沙箱中执行命令(隔离执行,HMAC 授契)", executor_type: "qiankun", executor_ref: "", risk_dims: { capability: "L2", data: "L1", consequence: "L3", context: "L1", }, data_scope: "沙箱工作目录", destinations: "本机沙箱进程", source: "builtin", version: "1", consent_mode: "ask", enabled: true, mandated: false, }, ]); } if (m === "GET" && path === "/mandates") { return ok([ { id: "mdt_mock_001", capability_id: "cap_mock_001", character_id: null, kind: "charter", scope: {}, budget_total: 20, budget_used: 3, status: "active", granted_at: "2026-08-27T10:00:00Z", expires_at: null, }, ]); } if (m === "POST" && path === "/mandates") { return ok({ id: "mdt_mock_002", status: "active" }, 201); } if (m === "DELETE" && path.startsWith("/mandates/")) { return ok(null, 204); } if (m === "GET" && path === "/petitions") { return ok([ { id: "confirm_mock_001", capability: "qiankun.shell", risk_level: "L3", reason: "请命:高风险动作需当次确认", brief: { who: "character:character_mock", what: "在乾坤沙箱中执行命令", capability: "qiankun.shell", executor: "qiankun", destinations: "本机沙箱进程", consequences: "风险等级 L3", alternatives: "拒绝后可调整参数或改用低风险能力", }, run_id: "run_mock_001", character_id: "character_mock", created_at: "2026-08-27T10:05:00Z", expires_at: "2026-08-27T10:15:00Z", }, ]); } if ( m === "POST" && path.startsWith("/petitions/") && path.endsWith("/approve") ) { return ok({ status: "resolved", approved: true }); } if ( m === "POST" && path.startsWith("/petitions/") && path.endsWith("/deny") ) { return ok({ status: "cancelled", approved: false }); } if (m === "GET" && path === "/tool-calls") { return ok([ { id: "tc_mock_001", capability_id: "cap_mock_001", run_id: "run_mock_001", character_id: "character_mock", trace_id: "trace_mock_001", decision: "allow", mandate_id: "mdt_mock_001", status: "ok", error_code: null, input_digest: '{"text":"琉璃之镜"}', output_digest: "[echo] 琉璃之镜", evidence: { kind: "observation", capability: "builtin.echo", epistemic: "observation_not_fact", }, duration_ms: 3, created_at: "2026-08-27T10:06:00Z", }, { id: "tc_mock_002", capability_id: "cap_mock_002", run_id: null, character_id: null, trace_id: null, decision: "denied_unmandated", mandate_id: null, status: "rejected", error_code: "HJ-1303", input_digest: '{"command":"ls"}', output_digest: "", evidence: { kind: "observation", capability: "qiankun.shell" }, duration_ms: 0, created_at: "2026-08-27T10:07:00Z", }, ]); } // —— 神识阁:BYOK 密钥(脱敏)—— if (m === "GET" && path === "/me/byok-keys") { return ok({ keys: [ { id: "byok_mock_001", name: "主力 DeepSeek", type: "openai_compatible", base_url: "https://api.deepseek.com", model: "deepseek-v4-flash", api_key_mask: "sk-****", }, ], }); } if (m === "POST" && path === "/me/byok-keys") { const body = (json ?? {}) as Record; return ok( { id: "byok_mock_002", name: String(body.name ?? ""), type: String(body.type ?? ""), base_url: String(body.base_url ?? ""), model: String(body.model ?? ""), api_key_mask: "sk-****", }, 201, ); } if (m === "DELETE" && path.startsWith("/me/byok-keys/")) { return ok(null, 204); } // —— 神识阁:用量 —— if (m === "GET" && path === "/me/usage") { return ok({ rows: [ { user_id: "user_mock", model: "deepseek-v4-flash", endpoint: "deepseek", requests: 12, ok: 11, prompt_tokens: 8400, completion_tokens: 3100, }, ], quota: { budget: 1_000_000, used: 11_500, reserved: 0 }, }); } // —— 设置:生成参数偏好(GET / PATCH /settings/preferences)—— if (m === "GET" && path === "/settings/preferences") { return ok(data.generationPreferences); } if (m === "PATCH" && path === "/settings/preferences") { return ok( data.updateGenerationPreferences( (json ?? {}) as UpdateGenerationPreferencesRequest, ), ); } // —— 司命台 · 九枢健康度聚合(API §19.1)—— if (m === "GET" && path === "/admin/pivots/health") { return ok(data.pivotHealthReport); } // —— 司命台 · 朱批待办(API §19.2)—— if (m === "GET" && path === "/admin/safety/pending") { return ok(paginate(data.pendingApprovals)); } // —— 安全审计(GET /safety/audit-events,按人物/枢过滤)—— if (m === "GET" && path === "/safety/audit-events") { const pivot = query?.pivot as string | undefined; const items = pivot ? data.auditTimeline.filter((e) => e.pivot === pivot) : data.auditTimeline; return ok(paginate(items)); } // —— 安全策略(§16;开放集合:增删改,随工具增长;后端返回全量 list 非分页)—— if (m === "GET" && path === "/safety/policies") { return ok(data.safetyPolicies); } if (m === "POST" && path === "/safety/policies") { return ok(data.createPolicy((json ?? {}) as CreateSafetyPolicyRequest)); } { const mt = path.match(/^\/safety\/policies\/([^/]+)$/); if (mt && m === "PATCH") { const body = (json ?? {}) as Record; const found = data.safetyPolicies.find((p) => p.tool === mt[1]); // 乐观:新增策略不在初始列表,find 不到时合成 base 再合并 const base = found ?? data.createPolicy({ tool: mt[1], risk_level: "L2", permission_policy: "ask", }); return ok({ ...base, ...body, tool: mt[1] }); } if (mt && m === "DELETE") return ok(undefined, 204); } // —— 司命台 · 起居注审计(API §19.3),支持 pivot 过滤 —— if (m === "GET" && path === "/admin/audit/timeline") { const pivot = query?.pivot as string | undefined; const items = pivot ? data.auditTimeline.filter((e) => e.pivot === pivot) : data.auditTimeline; return ok(paginate(items)); } // —— 司命台 · 后续阶段治理端点(API §19.4 预留)—— // 太一枢 · 目标层级(开放集合:增删改) if (m === "GET" && path === "/admin/goals") { const characterId = query?.character_id as string | undefined; return ok( characterId ? data.goals.filter((g) => g.character_id === characterId) : data.goals, ); } if (m === "POST" && path === "/admin/goals") { return ok(data.createGoal((json ?? {}) as CreateGoalRequest)); } { const mt = path.match(/^\/admin\/goals\/([^/]+)$/); if (mt && m === "PATCH") { const found = data.goals.find((g) => g.id === mt[1]); const body = (json ?? {}) as Record; // 乐观:新增项不在初始列表,find 不到时合成 base 再合并,保证可继续编辑 const base = found ?? data.createGoal({ title: String(body.title ?? "未命名目标"), level: "task", }); return ok({ ...base, ...body, id: mt[1], updated_at: new Date().toISOString(), }); } if (mt && m === "DELETE") return ok(undefined, 204); } // 烛照枢 · 感知通道授权(开放集合:增删改;默认六类,可自定义感知源) if (m === "GET" && path === "/admin/perception/channels") return ok(data.perceptionChannels); if (m === "POST" && path === "/admin/perception/channels") { return ok( data.createPerceptionChannel( (json ?? {}) as CreatePerceptionChannelRequest, ), ); } { const mt = path.match(/^\/admin\/perception\/channels\/([^/]+)$/); if (mt && m === "PATCH") { const found = data.perceptionChannels.find((c) => c.channel === mt[1]); const body = (json ?? {}) as Record; const base = found ?? data.createPerceptionChannel({ channel: mt[1], name: String(body.name ?? mt[1]), description: "自定义感知源", }); return ok({ ...base, ...body, channel: mt[1] }); } if (mt && m === "DELETE") return ok(undefined, 204); } // 河图枢 · 世界实体(开放集合:增删改) if (m === "GET" && path === "/admin/world/entities") return ok(data.worldEntities); if (m === "POST" && path === "/admin/world/entities") { return ok(data.createWorldEntity((json ?? {}) as CreateWorldEntityRequest)); } { const mt = path.match(/^\/admin\/world\/entities\/([^/]+)$/); if (mt && m === "PATCH") { const found = data.worldEntities.find((e) => e.id === mt[1]); const body = (json ?? {}) as Record; const base = found ?? data.createWorldEntity({ type: "concept", name: String(body.name ?? "未命名实体"), }); return ok({ ...base, ...body, id: mt[1], updated_at: new Date().toISOString(), }); } if (mt && m === "DELETE") return ok(undefined, 204); } // 洛书枢 · 计划(开放集合:增删改) if (m === "GET" && path === "/admin/plans") return ok(data.plans); if (m === "POST" && path === "/admin/plans") { return ok(data.createPlan((json ?? {}) as CreatePlanRequest)); } { const mt = path.match(/^\/admin\/plans\/([^/]+)$/); if (mt && m === "PATCH") { const found = data.plans.find((p) => p.id === mt[1]); const body = (json ?? {}) as Record; const base = found ?? data.createPlan({ title: String(body.title ?? "未命名计划") }); return ok({ ...base, ...body, id: mt[1], updated_at: new Date().toISOString(), }); } if (mt && m === "DELETE") return ok(undefined, 204); } // 天工阁枢 · 角色在岗(开放集合:增删改;默认九角色,可自定义新角色) if (m === "GET" && path === "/admin/agents/roster") return ok(data.agentRoster); if (m === "POST" && path === "/admin/agents/roster") { return ok(data.createAgent((json ?? {}) as CreateAgentRequest)); } { const mt = path.match(/^\/admin\/agents\/roster\/([^/]+)$/); if (mt && m === "PATCH") { const found = data.agentRoster.find((a) => a.role === mt[1]); const body = (json ?? {}) as Record; const base = found ?? data.createAgent({ role: mt[1], name: String(body.name ?? mt[1]), responsibility: "自定义角色", }); return ok({ ...base, ...body, role: mt[1] }); } if (mt && m === "DELETE") return ok(undefined, 204); } // —— 司命台 · 链路追踪 GET /admin/traces(§19.5)—— if (m === "GET" && path === "/admin/traces") { return ok({ items: [], total: 0 }); } { const mt = path.match(/^\/admin\/traces\/([^/]+)$/); if (mt && m === "GET") { return ok({ trace_id: mt[1], events: [], audit_entries: [], idempotency_entries: [], session_ids: [], character_ids: [], }); } } // —— 司命台 · 时间事件 GET /admin/time-events(§19.5)—— if (m === "GET" && path === "/admin/time-events") { return ok({ items: [], total: 0, page: 1, page_size: 50 }); } // —— 司命台 · 租户管理(管理员用)—— if (m === "GET" && path === "/admin/tenants/overview") { return ok([]); } { const mt = path.match(/^\/admin\/tenants\/([^/]+)\/characters$/); if (mt && m === "GET") return ok([]); } { const mt = path.match(/^\/admin\/tenants\/([^/]+)\/memories$/); if (mt && m === "GET") return ok([]); } // —— 用户管理(§19.6)—— if (m === "GET" && path === "/admin/users") { return ok([]); } if (m === "POST" && path === "/admin/users") { return ok({ id: `user-${Date.now()}`, username: (json as Record)?.username ?? "new_user", email: "", is_active: true, is_admin: false, created_at: new Date().toISOString(), }); } { const mt = path.match(/^\/admin\/users\/([^/]+)$/); if (mt && m === "PATCH") { return ok({ id: mt[1], ...(json as Record) }); } } // —— 工具(§15;开放集合:增删改,器用治理)—— if (m === "GET" && path === "/tools") { return ok(data.tools); } if (m === "POST" && path === "/tools") { return ok(data.createTool((json ?? {}) as CreateToolRequest)); } // 工具调用详情(GET /tools/calls/{tool_call_id}) { const mt = path.match(/^\/tools\/calls\/([^/]+)$/); if (mt && m === "GET") { return ok({ id: mt[1], tool: "unknown", status: "completed", started_at: new Date().toISOString(), completed_at: new Date().toISOString(), }); } } // 工具调用(POST /tools/{tool_name}/invoke,非流式) { const mt = path.match(/^\/tools\/([^/]+)\/invoke$/); if (mt && m === "POST") { return ok({ status: "completed", result: { ok: true, exit_code: 0, stdout: "", stderr: "", duration_ms: 100, timeout: false, truncated: false, }, trace_id: `trace-${mt[1]}-${Date.now()}`, }); } } { const mt = path.match(/^\/tools\/([^/]+)$/); if (mt && m === "PATCH") { const body = (json ?? {}) as Record; const found = data.tools.find((t) => t.name === mt[1]); const base = found ?? data.createTool({ name: mt[1], description: "自定义工具", risk_level: "L2", permission_policy: "ask", side_effect: false, audit_required: false, }); return ok({ ...base, ...body, name: mt[1] }); } if (mt && m === "DELETE") return ok(undefined, 204); } { const mt = path.match(/^\/safety\/confirmations\/([^/]+)\/(approve|deny)$/); if (mt && m === "POST") { return mt[2] === "approve" ? ok({ status: "completed", result: { ok: true, exit_code: 0, stdout: "", stderr: "", duration_ms: 120, timeout: false, truncated: false, trace_id: `trace-${mt[1]}`, }, }) : ok({ status: "cancelled", confirmation_id: mt[1] }); } } // —— 人物 —— if (m === "GET" && path === "/characters/active") { const found = data.characters.find((c) => c.id === _mockActiveCharacterId); return ok(found ?? data.activeCharacter); } if (m === "GET" && path === "/characters") { return ok(paginate(data.characterList)); } if (m === "GET" && path === "/characters/templates") { return ok(data.characterTemplates); } { const mt = path.match(/^\/characters\/([^/]+)$/); if (mt && m === "GET") { const found = data.characters.find((c) => c.id === mt[1]); return ok(found ?? data.activeCharacter); } } { const mt = path.match(/^\/characters\/([^/]+)\/activate$/); if (mt && m === "POST") { _mockActiveCharacterId = mt[1]; return ok({ active_character_id: mt[1], status: "active" }); } } if (m === "POST" && path === "/characters") { const created = data.createCharacter( (json ?? {}) as CreateCharacterRequest, ); // 入库:加入 mock 列表,使后续查询可访问 data.addCharacterToList(created); // 模板创建的人物自动设为激活,与前端的创建后跳转表现一致 if ((json as CreateCharacterRequest)?.template_key) { created.status = "active"; // 同步更新列表项的状态 const item = data.characterList.find((c) => c.id === created.id); if (item) item.status = "active"; _mockActiveCharacterId = created.id; } return ok(created); } { const mt = path.match(/^\/characters\/([^/]+)\/profile$/); if (mt && m === "PATCH") { return ok( data.updateCharacterProfile( mt[1], (json ?? {}) as UpdateProfileRequest, ), ); } } { const mt = path.match(/^\/characters\/([^/]+)\/model-policy$/); if (mt && m === "PATCH") { return ok( data.updateCharacterModelPolicy( mt[1], (json ?? {}) as UpdateModelPolicyRequest, ), ); } } { const mt = path.match(/^\/characters\/([^/]+)\/(pause|resume|archive)$/); if (mt && m === "POST") { const found = data.characters.find((c) => c.id === mt[1]); const base = found ?? data.activeCharacter; const status = mt[2] === "pause" ? "paused" : mt[2] === "resume" ? "active" : "archived"; return ok({ ...base, id: mt[1], status, updated_at: new Date().toISOString(), }); } } // —— 记忆 —— if (m === "GET" && path === "/memory") { return ok(paginate(data.memories)); } if (m === "POST" && path === "/memory") { return ok(data.createMemory(json as CreateMemoryRequest)); } { const mt = path.match(/^\/memory\/([^/]+)$/); if (mt && m === "PATCH") { return ok(data.updateMemory(mt[1], json as UpdateMemoryRequest)); } if (mt && m === "DELETE") { return ok(undefined, 204); } } // —— 人格 —— if (m === "GET" && path === "/personality/current") { return ok(data.personality); } if (m === "GET" && path === "/personality/versions") { return ok(paginate(data.personalityVersions)); } if (m === "POST" && path === "/personality/propose-update") { const body = (json ?? {}) as { reason?: string; fields?: Record; }; return ok({ proposal_id: `personality_proposal_${Date.now()}`, character_id: data.MOCK_CHARACTER_ID, status: "pending", reason: body.reason ?? "", fields: body.fields ?? {}, created_at: new Date().toISOString(), }); } { const mt = path.match( /^\/personality\/proposals\/([^/]+)\/(approve|reject)$/, ); if (mt && m === "POST") { const approved = mt[2] === "approve"; return ok({ proposal_id: mt[1], character_id: data.MOCK_CHARACTER_ID, status: approved ? "approved" : "rejected", reason: "", fields: {}, resulting_version_id: approved ? `pv-jingwu-00${data.personalityVersions.length + 1}` : null, reviewed_at: new Date().toISOString(), created_at: new Date().toISOString(), }); } } { const mt = path.match(/^\/personality\/versions\/([^/]+)\/rollback$/); if (mt && m === "POST") { const found = data.personalityVersions.find((v) => v.id === mt[1]); return ok(found ?? data.personality); } } // —— 对话 —— if (m === "GET" && path === "/chat/sessions") { const characterId = query?.character_id as string | undefined; const status = query?.status as string | undefined; return ok( paginate( data.sessions.filter( (s) => s.status !== "deleted" && (!characterId || s.character_id === characterId) && (!status || s.status === status), ), ), ); } if (m === "POST" && path === "/chat/sessions") { return ok(data.createSession((json ?? {}) as CreateSessionRequest)); } { const mt = path.match(/^\/chat\/sessions\/([^/]+)$/); if (mt && m === "PATCH") { const updated = data.updateSession( mt[1], (json ?? {}) as UpdateSessionRequest, ); return ok( updated ?? { session_id: mt[1], character_id: data.MOCK_CHARACTER_ID, status: "active", created_at: new Date().toISOString(), }, ); } if (mt && m === "DELETE") { data.deleteSession(mt[1]); return ok(undefined, 204); } } { const mt = path.match(/^\/chat\/sessions\/([^/]+)\/messages$/); if (mt && m === "POST") { return ok(data.sendMessage(mt[1])); } if (mt && m === "GET") { // mock 不自动注入预设历史——进入/新建会话均为空,消息由本次对话在内存持有。 return ok(paginate([])); } } { const mt = path.match( /^\/chat\/sessions\/([^/]+)\/messages\/([^/]+)\/version$/, ); if (mt && m === "PATCH") { const body = (json ?? {}) as { version_idx?: number }; return ok({ message_id: mt[2], active_version_idx: body.version_idx ?? 0, versions: [], }); } } // —— 运行 —— // 列表(GET /runs,支持分页参数) if (m === "GET" && path === "/runs") { return ok(paginate([data.getRun("run-mock-001")])); } { const mt = path.match(/^\/runs\/([^/]+)$/); if (mt && m === "GET") return ok(data.getRun(mt[1])); } { const mt = path.match(/^\/runs\/([^/]+)\/cancel$/); if (mt && m === "POST") return ok({ id: mt[1], status: "cancelled" }); } { const mt = path.match(/^\/runs\/([^/]+)\/events$/); if (mt && m === "GET") return ok(data.listRunEvents(mt[1])); } // —— 世界模型(独立 /world 域:实体 + 关系;实体编辑/删除仍走 admin)—— if (m === "GET" && path === "/world/entities") { return ok(paginate(data.worldEntities)); } if (m === "POST" && path === "/world/entities") { return ok(data.createWorldEntity((json ?? {}) as CreateWorldEntityRequest)); } { const mt = path.match(/^\/world\/entities\/([^/]+)$/); if (mt && m === "GET") { const found = data.worldEntities.find((e) => e.id === mt[1]); return ok(found ?? data.worldEntities[0]); } } if (m === "GET" && path === "/world/relations") { return ok(paginate(data.mockWorldRelations)); } if (m === "POST" && path === "/world/relations") { return ok( data.createWorldRelation((json ?? {}) as CreateWorldRelationRequest), ); } { const mt = path.match(/^\/world\/relations\/([^/]+)$/); if (mt && m === "DELETE") return ok(undefined, 204); } // —— 候选审核(跨来源统一入口:记忆候选 / 世界实体候选)—— { const mt = path.match(/^\/characters\/([^/]+)\/memory-candidates$/); if (mt && m === "GET") { const source = query?.source as string | undefined; const items = source ? data.mockMemoryCandidates.filter((c) => source === "cycle" ? c.cycle_id : c.import_job_id, ) : data.mockMemoryCandidates; return ok(paginate(items)); } } { const mt = path.match(/^\/memory-candidates\/([^/]+)\/(approve|reject)$/); if (mt && m === "POST") { return ok({ id: mt[1], status: mt[2] === "approve" ? "approved" : "rejected", }); { const mt = path.match(/^\/world\/relations\/([^/]+)$/); if (mt && m === "DELETE") return ok(undefined, 204); } } } { const mt = path.match(/^\/characters\/([^/]+)\/world\/entity-proposals$/); if (mt && m === "GET") return ok(paginate(data.mockWorldEntityProposals)); } { const mt = path.match(/^\/world\/entities\/([^/]+)\/(approve|reject)$/); if (mt && m === "POST") { return ok({ id: mt[1], status: mt[2] === "approve" ? "confirmed" : "rejected", }); } } // —— 资料导入流水线(§10,挂在 /characters/{id}/imports 下)—— { const mt = path.match(/^\/characters\/([^/]+)\/imports$/); if (mt && m === "POST") { return ok(data.createImportJob((json ?? {}) as CreateImportRequest)); } if (mt && m === "GET") return ok(paginate([data.mockImportJob])); } { const mt = path.match(/^\/characters\/([^/]+)\/imports\/([^/]+)$/); if (mt && m === "GET") return ok(data.mockImportJob); } { const mt = path.match(/^\/characters\/([^/]+)\/imports\/([^/]+)\/items$/); if (mt && m === "GET") return ok(paginate(data.mockImportItems)); } { const mt = path.match( /^\/characters\/([^/]+)\/imports\/([^/]+)\/profile-candidates$/, ); if (mt && m === "POST") return ok({ status: "ok" }); if (mt && m === "GET") return ok(paginate(data.mockProfileCandidates)); } { const mt = path.match( /^\/characters\/([^/]+)\/imports\/([^/]+)\/memory-candidates$/, ); if (mt && m === "POST") return ok({ status: "ok" }); if (mt && m === "GET") return ok(paginate(data.mockMemoryCandidates)); } { const mt = path.match( /^\/characters\/([^/]+)\/imports\/([^/]+)\/personality-proposals$/, ); if (mt && m === "POST") return ok({ status: "ok" }); } { const mt = path.match( /^\/characters\/([^/]+)\/profile-candidates\/batch-review$/, ); if (mt && m === "POST") { const body = (json ?? {}) as BatchReviewRequest; const approve = body.decisions?.filter((d) => d.decision === "approve").length ?? 0; const reject = body.decisions?.filter((d) => d.decision === "reject").length ?? 0; return ok({ applied: approve, rejected: reject, skipped: 0 }); } } { const mt = path.match( /^\/characters\/([^/]+)\/memory-candidates\/batch-review$/, ); if (mt && m === "POST") { const body = (json ?? {}) as BatchReviewRequest; const approve = body.decisions?.filter((d) => d.decision === "approve").length ?? 0; const reject = body.decisions?.filter((d) => d.decision === "reject").length ?? 0; return ok({ applied: approve, rejected: reject, skipped: 0 }); } } // —— Skill 能力包(百工枢,/tools/skills)—— if (m === "GET" && path === "/tools/skills") { const status = query?.status as string | undefined; const items = status ? data.mockSkills.filter((s) => s.status === status) : data.mockSkills; return ok(items); } if (m === "POST" && path === "/tools/skills") { return ok(data.createSkillData((json ?? {}) as CreateSkillRequest)); } { const mt = path.match(/^\/tools\/skills\/([^/]+)$/); if (mt && m === "PATCH") { const body = (json ?? {}) as Record; const found = data.mockSkills.find((s) => s.id === mt[1]); const base = found ?? data.createSkillData({ name: String(body.name ?? mt[1]) }); return ok({ ...base, ...body, id: mt[1], updated_at: new Date().toISOString(), }); } if (mt && m === "DELETE") return ok(undefined, 204); } // —— MCP server(百工枢,/tools/mcp-servers)—— if (m === "GET" && path === "/tools/mcp-servers") { const status = query?.status as string | undefined; const items = status ? data.mockMcpServers.filter((s) => s.status === status) : data.mockMcpServers; return ok(items); } if (m === "POST" && path === "/tools/mcp-servers") { return ok(data.createMcpServerData((json ?? {}) as CreateMcpServerRequest)); } { const mt = path.match(/^\/tools\/mcp-servers\/([^/]+)$/); if (mt && m === "DELETE") return ok(undefined, 204); if (mt && m === "PATCH") { const found = data.mockMcpServers.find((s) => s.id === mt[1]); const body = (json ?? {}) as Record; const base = found ?? data.createMcpServerData({ name: String(body.name ?? mt[1]) }); return ok({ ...base, ...body, id: mt[1] }); } } { const mt = path.match(/^\/tools\/mcp-servers\/([^/]+)\/status$/); if (mt && m === "GET") { return ok({ server_id: mt[1], process: "stopped", pid: null, uptime_seconds: null, }); } } { const mt = path.match( /^\/tools\/mcp-servers\/([^/]+)\/(start|stop|restart|refresh)$/, ); if (mt && m === "POST") { if (mt[2] === "start") { return ok({ server_id: mt[1], process: "running", pid: 12345, uptime_seconds: 0, }); } if (mt[2] === "stop") { return ok({ server_id: mt[1], process: "stopped", pid: null, uptime_seconds: null, }); } if (mt[2] === "restart") { return ok({ server_id: mt[1], process: "running", pid: 12346, uptime_seconds: 0, }); } if (mt[2] === "refresh") { return ok( data.mockMcpServers.find((s) => s.id === mt[1]) ?? data.mockMcpServers[0], ); } } } // —— 统一能力列举(GET /tools/capabilities,builtin/mcp/skill 三源)—— if (m === "GET" && path === "/tools/capabilities") { return ok(data.mockCapabilities); } // —— 辰正时态(/time/*)—— if (m === "GET" && path === "/time/snapshot") { const now = new Date(); const h = now.getHours(); const dayPeriod = h < 5 ? "凌晨" : h < 8 ? "清晨" : h < 12 ? "上午" : h < 14 ? "正午" : h < 18 ? "下午" : h < 21 ? "傍晚" : "深夜"; const shichenIdx = Math.floor(((h + 1) % 24) / 2); const shichenNames = [ "zi", "chou", "yin", "mao", "chen", "si", "wu", "wei", "shen", "you", "xu", "hai", ]; return ok({ snapshot: { now_utc: now.toISOString(), local_time: now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }), timezone: "Asia/Shanghai", local_hour: h, shichen: shichenNames[shichenIdx], day_period: dayPeriod, weekday: now.getDay(), is_weekend: now.getDay() === 0 || now.getDay() === 6, solar_date: now.toISOString().slice(0, 10), lunar: { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate(), leap: false, text: "腊月十五", }, season: h >= 3 && h < 6 ? "春" : h >= 6 && h < 9 ? "夏" : h >= 9 && h < 12 ? "秋" : "冬", solar_term: null, festivals: [], acquaintance: { total_days: 365, natural_language: "相识一载" }, offline: { seconds: 120, natural_language: "刚刚还在" }, next_milestone: { days_until: 30, name: "相识周年", date: now.toISOString().slice(0, 10), }, active_milestones: [], home_tz_diff_minutes: 0, location: { city: "上海", country: "中国", timezone: "Asia/Shanghai" }, }, factors: { festival_joy: 0.3, late_night_fragility: dayPeriod === "深夜" || dayPeriod === "凌晨" ? 0.7 : 0.2, reunion_intensity: 0.1, weekend_relax: now.getDay() === 0 || now.getDay() === 6 ? 0.6 : 0.2, season_resonance: 0.5, same_period_resonance: 0.4, care_suggest: dayPeriod === "深夜" ? 0.8 : 0.3, care_kind: dayPeriod === "深夜" ? "jetlag" : null, }, context_text: dayPeriod === "深夜" ? "夜深了,万籁俱寂。这是思绪最容易沉降的时刻——适合温缓的承接,不宜急于追问。" : "白日的喧嚣渐远,此刻宜静不宜动。让话语如湖面般展开,不催不赶。", }); } if (m === "GET" && path === "/time/anniversaries") { return ok(_annivList); } if (m === "POST" && path === "/time/anniversaries") { const body = (json ?? {}) as Record; const anniv = { id: `anniv-${Date.now()}`, ...body, recurs_yearly: true, source: body.source ?? "manual", }; _annivList.push(anniv); return ok(anniv); } { const mt = path.match(/^\/time\/anniversaries\/([^/]+)$/); if (mt && m === "GET") { const found = _annivList.find((a) => a.id === mt[1]); return ok( found ?? { id: mt[1], name: "纪念日", calendar: "solar", date: "2025-01-01", recurs_yearly: true, kind: "anniversary", subject_type: "relationship", source: "manual", }, ); } if (mt && m === "PATCH") { const idx = _annivList.findIndex((a) => a.id === mt[1]); const body = (json ?? {}) as Record; if (idx >= 0) { _annivList[idx] = { ..._annivList[idx], ...body, id: mt[1] }; return ok(_annivList[idx]); } return ok({ id: mt[1], ...body, recurs_yearly: true }); } if (mt && m === "DELETE") { const idx = _annivList.findIndex((a) => a.id === mt[1]); if (idx >= 0) _annivList.splice(idx, 1); return ok(undefined, 204); } } if (m === "GET" && path === "/time/acquaintance") { return ok({ total_days: 365, natural_language: "相识一载,如镜映月" }); } if (m === "GET" && path === "/time/config") { return ok({ user_timezone: "Asia/Shanghai", home_timezone: "Asia/Shanghai", default_city: "上海", offline_active_minutes: 5, offline_short_absence_minutes: 30, offline_long_absence_minutes: 180, offline_reunion_impact_minutes: 15, care_enabled: true, jetlag_threshold_minutes: 120, }); } return undefined; }