#!/usr/bin/env node "use strict"; const fs = require("node:fs"); const path = require("node:path"); const vm = require("node:vm"); const { TextDecoder } = require("node:util"); const repoRoot = path.resolve(__dirname, ".."); const openHubRoot = process.env.OPENHUB_RESEARCH_ROOT || "/tmp/mnote-openhub-research/OpenHub"; const embedPath = path.join(openHubRoot, "smart-query-frontend/src/mnoteEmbed.js"); const smartQueryPath = path.join(openHubRoot, "smart-query-frontend/src/pages/SmartQueryPage.jsx"); const diffViewerPath = path.join(openHubRoot, "smart-query-frontend/src/components/DiffViewer.jsx"); const designPath = path.join( repoRoot, "design/07-ai/process/7-68-openhub-weknora-mnote-deep-fusion-checklist-v1.md" ); const forbiddenFulltextKeys = [ "message", "messages", "messageContent", "conversation", "conversationMessages", "assistantMessage", "userMessage", "content", "text", "transcript", ]; function read(filePath) { return fs.readFileSync(filePath, "utf8"); } function assertCheck(failures, name, passed, details = undefined) { if (!passed) failures.push({ name, details }); } function base64UrlJson(value) { return Buffer.from(JSON.stringify(value), "utf8") .toString("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/g, ""); } function hasForbiddenKey(value) { if (!value || typeof value !== "object") return false; if (Array.isArray(value)) return value.some(hasForbiddenKey); return Object.entries(value).some(([key, entry]) => ( forbiddenFulltextKeys.includes(key) || hasForbiddenKey(entry) )); } async function probeEmbedRuntime(embedSource) { const mnoteScope = { workspaceScope: { rootUri: "file:///tmp/mnote-artifact-root", workspaceId: "ws-task780", pageResourceId: "page-task780", }, }; const calls = []; const sandbox = { console, TextDecoder, URLSearchParams, Uint8Array, window: { location: { pathname: "/page-ai/openhub/ai", search: `?scope=session-task780&mnoteScope=${base64UrlJson(mnoteScope)}`, origin: "http://127.0.0.1:3000", }, parent: {}, atob: (value) => Buffer.from(value, "base64").toString("binary"), }, fetch: async (url, options = {}) => { calls.push({ url, options }); return { ok: true, status: 200, json: async () => ({ ok: true }), }; }, module: { exports: {} }, exports: {}, }; const transformed = embedSource .replace(/import\.meta\.env\.VITE_API_BASE_URL/g, "undefined") .replace(/\bexport const /g, "const "); vm.runInNewContext( `${transformed}\nmodule.exports = { getMNoteArtifactIndexContext, postMNoteArtifactIndex };`, sandbox, { filename: embedPath } ); const result = await sandbox.module.exports.postMNoteArtifactIndex({ openhubSessionId: "ses-task780", kind: "changed_file", providerId: "opencode_tool_event", path: "/tmp/mnote-artifact-root/page.md", citationPayload: { schema: "openhub.changed_file_locator.v1", rootRelativePath: "page.md", diffAvailable: false, }, }); const body = calls[0] ? JSON.parse(calls[0].options.body) : null; return { result, calls, body }; } async function main() { const failures = []; const embed = read(embedPath); const smartQuery = read(smartQueryPath); const diffViewer = read(diffViewerPath); const design = read(designPath); assertCheck( failures, "mnoteEmbed posts artifact index to MNote root API", embed.includes("postMNoteArtifactIndex") && embed.includes("getMNoteArtifactIndexContext") && embed.includes("fetch('/api/page-ai/openhub/artifact-index'") && embed.includes("credentials: 'include'") ); assertCheck( failures, "artifact index payload is limited to locator fields", ["openhubSessionId", "kind", "providerId", "path", "citationPayload", "rootUri", "workspaceId", "pageResourceId"] .every((field) => embed.includes(field)) ); assertCheck( failures, "SmartQueryPage indexes changed files after diff metadata is loaded", smartQuery.includes("indexChangedFiles(sessionId, files)") && smartQuery.includes("kind: 'changed_file'") && smartQuery.includes("schema: 'openhub.changed_file_locator.v1'") && smartQuery.includes("providerId: file?.source || 'opencode_tool_event'") ); assertCheck( failures, "SmartQueryPage indexes WeKnora citation locator payloads", smartQuery.includes("indexCitationArtifacts") && smartQuery.includes("kind: 'citation'") && smartQuery.includes("schema: 'openhub.weknora_citation_locator.v1'") && smartQuery.includes("citation?.sourceRootRelativePath") ); assertCheck( failures, "citation payload sanitizer strips fulltext-like fields recursively", forbiddenFulltextKeys.every((key) => smartQuery.includes(`'${key}'`)) && smartQuery.includes("sanitizeCitationPayload(entry)") && smartQuery.includes(".filter(([key]) => !forbiddenKeys.has(key))") ); assertCheck( failures, "changed file bridge remains connected to MNote open-file event", diffViewer.includes("postMNoteOpenFile") && smartQuery.includes("data-mnote-openhub-changed-file") && smartQuery.includes("handleOpenChangedFile") ); assertCheck( failures, "design checklist records task780 runtime artifact index bridge", design.includes("task780-openhub-artifact-index-runtime-smoke.js") && design.includes("changed_file / citation 轻量 artifact index") ); let runtimeProbe = null; try { runtimeProbe = await probeEmbedRuntime(embed); assertCheck( failures, "runtime request uses MNote artifact-index endpoint", runtimeProbe.calls.length === 1 && runtimeProbe.calls[0].url === "/api/page-ai/openhub/artifact-index" && runtimeProbe.calls[0].options.method === "POST", runtimeProbe ); assertCheck( failures, "runtime request body contains required locator fields", runtimeProbe.body && runtimeProbe.body.openhubSessionId === "ses-task780" && runtimeProbe.body.kind === "changed_file" && runtimeProbe.body.providerId === "opencode_tool_event" && runtimeProbe.body.path === "/tmp/mnote-artifact-root/page.md" && runtimeProbe.body.rootUri === "file:///tmp/mnote-artifact-root" && runtimeProbe.body.workspaceId === "ws-task780" && runtimeProbe.body.pageResourceId === "page-task780", runtimeProbe.body ); assertCheck( failures, "runtime request body does not contain OpenHub message fulltext fields", runtimeProbe.body && !hasForbiddenKey(runtimeProbe.body), runtimeProbe.body ); } catch (error) { runtimeProbe = { error: error instanceof Error ? error.stack || error.message : String(error) }; assertCheck(failures, "runtime request shape probe executes", false, runtimeProbe); } const result = { ok: failures.length === 0, task: "task780-openhub-artifact-index-runtime-smoke", runtimeProbe, failures, }; console.log(JSON.stringify(result, null, 2)); if (!result.ok) process.exit(1); } main().catch((error) => { console.error(error instanceof Error ? error.stack || error.message : String(error)); process.exit(1); });