Files
mnote/scripts/task780-openhub-artifact-index-runtime-smoke.js
T

220 lines
7.2 KiB
JavaScript
Raw Normal View History

2026-06-26 20:01:02 +08:00
#!/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);
});