"use strict"; const BASE_URL = (process.env.MNOTE_UI_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, ""); const REQUEST_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_TIMEOUT_MS || 20_000); function assert(condition, message) { if (!condition) { throw new Error(message); } } async function fetchWithTimeout(path, init = {}) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); try { return await fetch(`${BASE_URL}${path}`, { ...init, signal: controller.signal, }); } finally { clearTimeout(timeout); } } async function readTextResponse(path, init) { const response = await fetchWithTimeout(path, init); const text = await response.text(); assert(response.ok, `${path} 请求失败: ${response.status} ${text.slice(0, 200)}`); return { response, text }; } function runtimeInputToolPlan() { return { kind: "tool", context: { deploymentId: null, projectId: null, workspaceId: "ws_task116", requestId: "req_task116", traceId: "trace_task116", actor: { actorType: "user", actorId: "task116-user", sessionId: null, }, source: { channel: "rust-web", client: "task116-smoke", }, tenantId: null, authToken: null, idempotencyKey: null, validateOnly: false, dryRun: false, }, tool: { tool: "docs_search", kind: "query", mode: "plan", argsJson: { query: "Rust Web islands" }, target: null, reason: "task116 owner smoke", refs: ["task-025"], }, data: null, }; } async function checkSearchShell() { const { response, text } = await readTextResponse("/search?workspaceId=ws_task116&q=Rust"); assert(response.headers.get("x-mnote-web-owner") === "mnote-web", "Search shell 缺少 mnote-web owner header"); assert(response.headers.get("x-mnote-web-shell") === "search", "Search shell 缺少 search shell header"); assert(text.includes("mnote.search_shell.v1"), "Search shell 缺少 contract schema"); assert(text.includes("react_search_palette"), "Search shell 缺少 search palette island"); return { owner: "mnote-web", shell: "search", island: "react_search_palette" }; } async function checkAiBridge() { const { response, text } = await readTextResponse("/api/hermes/bridge", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(runtimeInputToolPlan()), }); assert(response.headers.get("x-mnote-web-owner") === "mnote-web", "AI bridge 缺少 mnote-web owner header"); assert(response.headers.get("x-mnote-ai-bridge-owner") === "rust-web-hermes", "AI bridge 缺少 rust-web-hermes owner header"); const payload = JSON.parse(text); assert(payload?.contract?.schema === "mnote.ai_bridge.v1", "AI bridge 缺少 contract schema"); assert(payload?.contract?.toolEventOwner === "rust-web-hermes", "AI bridge tool event owner 未固定到 Hermes"); return { owner: "mnote-web", bridgeOwner: "rust-web-hermes", schema: payload.contract.schema }; } async function checkMindmapShell() { const { response, text } = await readTextResponse("/mindmap/doc_task116/mind_task116"); assert(response.headers.get("x-mnote-web-owner") === "mnote-web", "Mindmap shell 缺少 mnote-web owner header"); assert(response.headers.get("x-mnote-web-shell") === "mindmap", "Mindmap shell 缺少 mindmap shell header"); assert(text.includes("mnote.mindmap_shell.v1"), "Mindmap shell 缺少 contract schema"); assert(text.includes('data-react-island="mindmap_runtime"'), "Mindmap shell 缺少 mindmap runtime island"); return { owner: "mnote-web", shell: "mindmap", island: "mindmap_runtime" }; } async function main() { const search = await checkSearchShell(); const aiBridge = await checkAiBridge(); const mindmap = await checkMindmapShell(); console.log( JSON.stringify( { ok: true, baseUrl: BASE_URL, results: { search, aiBridge, mindmap, }, }, null, 2, ), ); } main().catch((error) => { console.error(error instanceof Error ? error.stack || error.message : String(error)); process.exit(1); });