#!/usr/bin/env node "use strict"; const fs = require("node:fs"); const path = require("node:path"); const repoRoot = path.resolve(__dirname, ".."); const openHubRoot = process.env.OPENHUB_RESEARCH_ROOT || "/tmp/mnote-openhub-research/OpenHub"; const files = { chatInput: path.join(openHubRoot, "smart-query-frontend/src/components/ChatInput.jsx"), embed: path.join(openHubRoot, "smart-query-frontend/src/mnoteEmbed.js"), api: path.join(openHubRoot, "smart-query-frontend/src/services/api.js"), queryModel: path.join(openHubRoot, "smart-query-backend/app/models/query.py"), queryApi: path.join(openHubRoot, "smart-query-backend/app/api/query.py"), stream: path.join(openHubRoot, "smart-query-backend/app/services/stream.py"), mnoteRoute: path.join(repoRoot, "rust/crates/mnote-web/src/routes/page_ai_openhub.rs"), }; function read(file) { return fs.readFileSync(file, "utf8"); } function assertCheck(failures, name, passed) { if (!passed) failures.push(name); } const chatInput = read(files.chatInput); const embed = read(files.embed); const api = read(files.api); const queryModel = read(files.queryModel); const queryApi = read(files.queryApi); const stream = read(files.stream); const mnoteRoute = read(files.mnoteRoute); const failures = []; assertCheck( failures, "OpenHub ChatInput natively owns MNote context toggles", chatInput.includes("requestMNoteActiveTabAddress") && chatInput.includes("data-mnote-openhub-current-tab-toggle") && chatInput.includes("data-mnote-openhub-current-folder-toggle") && chatInput.includes("mnoteContextMode === 'tab'") && chatInput.includes("mnoteContextMode === 'folder'") && chatInput.includes("handleSendWithMNoteContext") ); assertCheck( failures, "toggles live beside model quota controls and do not write textarea", chatInput.indexOf("model?.monthlyLimit") < chatInput.indexOf("data-mnote-openhub-current-tab-toggle") && chatInput.includes("handleSend(undefined, context ?") && !chatInput.includes("setQuestion(context.value") && !chatInput.includes("setQuestion(mnoteContext") ); assertCheck( failures, "OpenHub mnoteEmbed requests active tab/folder from MNote host", embed.includes("export const requestMNoteActiveTabAddress") && embed.includes("mnote:get-active-tab-address") && embed.includes("mnote:active-tab-address") && embed.includes("kind === 'folder' ? 'folder' : 'tab'") ); assertCheck( failures, "OpenHub request body carries hidden mnote_context", api.includes("mnoteContext = null") && api.includes("requestBody.mnote_context = mnoteContext") ); assertCheck( failures, "OpenHub backend accepts and forwards mnote_context", queryModel.includes("mnote_context: Optional[dict]") && queryApi.includes("mnote_context=request.mnote_context") && stream.includes("mnote_context: Optional[dict] = None") ); assertCheck( failures, "OpenHub stream prepends hidden current page/folder context before prompt", stream.includes("") && stream.includes("当前文件夹") && stream.includes("当前页面") && stream.includes("context_parts.append") && stream.includes("_build_mnote_context_block") && stream.includes("_mnote_context_from_scope") && stream.includes("Effective MNote context") && stream.includes("Sent prompt preview") ); assertCheck( failures, "MNote proxy no longer injects floating quick action overlay", !mnoteRoute.includes("data-mnote-openhub-ai-quick-actions") && !mnoteRoute.includes("mnote_openhub_bridge_markup") && !mnoteRoute.includes("insertAddress(") ); if (failures.length) { console.error("OpenHub native MNote context static smoke failed:"); for (const failure of failures) console.error(`- ${failure}`); process.exit(1); } console.log("OpenHub native MNote context static smoke passed.");