"use strict"; const assert = require("node:assert/strict"); async function postDevSeed(requestContext, baseUrl, seeds, timeoutMs) { const normalizedBaseUrl = baseUrl.replace(/\/+$/, ""); const response = await requestContext.fetch(`${normalizedBaseUrl}/api/dev/seed`, { method: "POST", headers: { "content-type": "application/json" }, data: { seeds }, timeout: timeoutMs, }); const text = await response.text(); let payload = null; try { payload = text ? JSON.parse(text) : null; } catch { payload = text; } if (!response.ok()) { const details = typeof payload === "string" ? payload : JSON.stringify(payload); if (response.status() === 403 && payload && payload.code === "dev_seed_disabled") { throw new Error( [ `/api/dev/seed 未启用(base=${normalizedBaseUrl})。`, "依赖 seed 的 smoke 必须使用 `npm run dev:hot` 启动;dev:hot 默认开启 MNOTE_WEB_ALLOW_DEV_FIXTURES=1。", "若复用 desktop:hot,请使用 `MNOTE_WEB_ALLOW_DEV_FIXTURES=1 npm run desktop:hot`。", "修改 scripts/dev-hot.js 或启动环境后必须重启 dev:hot 主进程,cargo-watch 不会刷新 Node 启动环境。", ].join(" "), ); } assert.fail(`/api/dev/seed 失败: ${response.status()} ${details}`); } return payload; } async function setupWorkspaceAccess(requestContext, baseUrl, options) { const actorId = options.actorId; const rootPath = options.rootPath || options.root; const rootUri = options.rootUri || `file://${rootPath}`; return postDevSeed( requestContext, baseUrl, [ { kind: "setupWorkspace", user_id: actorId, email: options.email || `${actorId}@example.com`, username: options.username || actorId, display_name: options.displayName || actorId, role: options.role || "user", workspace_id: options.workspaceId, workspace_name: options.workspaceName || "MNote Smoke Workspace", root_uri: rootUri, root_path: rootPath, source_kind: options.sourceKind || "local_folder", permission: options.permission || "write", capabilities: options.capabilities || ["ai"], grant_source: options.grantSource || "smoke", grant_created_by: options.grantCreatedBy || actorId, }, ], options.timeoutMs, ); } async function seedAiRuntime(requestContext, baseUrl, options) { return postDevSeed( requestContext, baseUrl, [ { kind: "seedAiRuntime", id: options.id, user_id: options.userId, workspace_id: options.workspaceId, document_id: options.documentId, session_id: options.sessionId, run_id: options.runId, title: options.title, profile: options.profile || "reasonix", acp_runtime: options.acpRuntime || options.profile || "reasonix", trace_id: options.traceId, status: options.status || "running", runtime_json: options.runtimeJson || {}, payload_json: options.payloadJson || {}, events: (options.events || []).map((event) => ({ id: event.id, eventType: event.eventType || event.event_type, payloadJson: event.payloadJson || event.payload_json || {}, })), }, ], options.timeoutMs, ); } async function seedAiPolicy(requestContext, baseUrl, options) { return postDevSeed( requestContext, baseUrl, [ { kind: "seedAiPolicy", id: options.id, user_id: options.userId, workspace_id: options.workspaceId, allowed_roots_json: options.allowedRootsJson || [], model_policy_json: options.modelPolicyJson || {}, quota_json: options.quotaJson || {}, }, ], options.timeoutMs, ); } async function clearRuntimeEvents(requestContext, baseUrl, options) { return postDevSeed( requestContext, baseUrl, [ { kind: "clearRuntimeEvents", user_id: options.userId, run_id: options.runId, }, ], options.timeoutMs, ); } async function getAiRuntimeRun(requestContext, baseUrl, options) { const payload = await postDevSeed( requestContext, baseUrl, [ { kind: "getAiRuntimeRun", user_id: options.userId, run_id: options.runId, }, ], options.timeoutMs, ); return payload.results && payload.results[0] ? payload.results[0].run : null; } async function listAiRuntimeRuns(requestContext, baseUrl, options) { const payload = await postDevSeed( requestContext, baseUrl, [ { kind: "listAiRuntimeRuns", user_id: options.userId, workspace_id: options.workspaceId, document_id: options.documentId, session_id: options.sessionId, limit: options.limit, }, ], options.timeoutMs, ); return payload.results && payload.results[0] ? payload.results[0].runs || [] : []; } async function listAiRuntimeEvents(requestContext, baseUrl, options) { const payload = await postDevSeed( requestContext, baseUrl, [ { kind: "listAiRuntimeEvents", user_id: options.userId, run_id: options.runId, limit: options.limit, event_type: options.eventType, }, ], options.timeoutMs, ); return payload.results && payload.results[0] ? payload.results[0].events || [] : []; } async function countAiRuntimeEvents(requestContext, baseUrl, options) { const payload = await postDevSeed( requestContext, baseUrl, [ { kind: "countAiRuntimeEvents", user_id: options.userId, run_id: options.runId, event_type: options.eventType, }, ], options.timeoutMs, ); return Number(payload.results && payload.results[0] ? payload.results[0].count : 0); } async function findExternalConversationBinding(requestContext, baseUrl, options) { const payload = await postDevSeed( requestContext, baseUrl, [ { kind: "findExternalConversationBinding", user_id: options.userId, workspace_id: options.workspaceId, mnote_session_id: options.mnoteSessionId, provider: options.provider, }, ], options.timeoutMs, ); return payload.results && payload.results[0] ? payload.results[0].binding : null; } async function listExternalConversationBindings(requestContext, baseUrl, options) { const payload = await postDevSeed( requestContext, baseUrl, [ { kind: "listExternalConversationBindings", user_id: options.userId, workspace_id: options.workspaceId, mnote_session_id: options.mnoteSessionId, limit: options.limit, }, ], options.timeoutMs, ); return payload.results && payload.results[0] ? payload.results[0].bindings || [] : []; } module.exports = { postDevSeed, setupWorkspaceAccess, seedAiRuntime, seedAiPolicy, clearRuntimeEvents, getAiRuntimeRun, listAiRuntimeRuns, listAiRuntimeEvents, countAiRuntimeEvents, findExternalConversationBinding, listExternalConversationBindings, };