feat(control-plane): add libSQL Turso backend
This commit is contained in:
@@ -4,17 +4,18 @@
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const { execFileSync } = require("node:child_process");
|
||||
const { chromium } = require("playwright");
|
||||
|
||||
const ROOT = process.env.MNOTE_REPO_ROOT || "/mnt/Data1T/mnote";
|
||||
const { BASE_URL, UI_TIMEOUT_MS } = require(path.join(ROOT, "scripts", "tree-shell-smoke-helpers"));
|
||||
const {
|
||||
setupWorkspaceAccess,
|
||||
seedAiRuntime,
|
||||
} = require(path.join(ROOT, "scripts", "lib", "control-plane-dev-seed"));
|
||||
const TASK = "task557-page-ai-run-resume-smoke";
|
||||
const OUTPUT_DIR = path.join(ROOT, "tmp", TASK);
|
||||
const RESULT_PATH = path.join(OUTPUT_DIR, "result.json");
|
||||
const SCREENSHOT_PATH = path.join(OUTPUT_DIR, "page-ai-run-resume.png");
|
||||
const CONTROL_PLANE_DB =
|
||||
process.env.MNOTE_CONTROL_PLANE_DB_PATH || "/mnt/Data1T/Mnote_data/control-plane/control-plane.db";
|
||||
const CHROME = process.env.MNOTE_PAGE_AI_CHROME || "/usr/bin/google-chrome";
|
||||
const RESUME_TIMEOUT_MS = Number(process.env.MNOTE_PAGE_AI_RESUME_TIMEOUT_MS || 90_000);
|
||||
const ACTOR_ID = "mnote-e2e";
|
||||
@@ -24,56 +25,42 @@ const ROOT_URI = `file://${ROOT_PATH}`;
|
||||
const OWNER_REL = "knowledge-rag-fixtures-7-57/PageAiRunResumeSmoke.md";
|
||||
const DOCUMENT_ID = `local-md:${OWNER_REL.replaceAll("/", "~2F")}`;
|
||||
|
||||
function sqlQuote(value) {
|
||||
return `'${String(value).replaceAll("'", "''")}'`;
|
||||
}
|
||||
|
||||
function sqliteExec(sql) {
|
||||
execFileSync("sqlite3", [CONTROL_PLANE_DB, sql], {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
}
|
||||
|
||||
function ensureFixture() {
|
||||
assert(fs.existsSync(CONTROL_PLANE_DB), `缺少 control-plane SQLite: ${CONTROL_PLANE_DB}`);
|
||||
const ownerPath = path.join(ROOT_PATH, OWNER_REL);
|
||||
fs.mkdirSync(path.dirname(ownerPath), { recursive: true });
|
||||
fs.writeFileSync(ownerPath, "# Page AI Run Resume Smoke\n\n用于验证 Page AI host run journal afterSeq 恢复。\n", "utf8");
|
||||
const now = new Date().toISOString();
|
||||
sqliteExec(`
|
||||
INSERT OR IGNORE INTO users (id, email, username, display_name, role, status, created_at, updated_at, revision)
|
||||
VALUES (${sqlQuote(ACTOR_ID)}, 'mnote.e2e@example.com', ${sqlQuote(ACTOR_ID)}, ${sqlQuote(ACTOR_ID)}, 'user', 'active', ${sqlQuote(now)}, ${sqlQuote(now)}, 1);
|
||||
INSERT OR REPLACE INTO workspaces (id, owner_user_id, name, kind, root_uri, root_path, source_kind, status, created_at, updated_at, revision)
|
||||
VALUES (${sqlQuote(WORKSPACE_ID)}, ${sqlQuote(ACTOR_ID)}, 'MNote E2E Space', 'personal', ${sqlQuote(ROOT_URI)}, ${sqlQuote(ROOT_PATH)}, 'local_folder', 'active', ${sqlQuote(now)}, ${sqlQuote(now)}, 1);
|
||||
INSERT OR REPLACE INTO directory_grants (id, user_id, workspace_id, root_uri, root_path, permission, recursive, capabilities_json, source, status, created_by, created_at, updated_at, revision)
|
||||
VALUES ('grant_task557_page_ai_run_resume', ${sqlQuote(ACTOR_ID)}, ${sqlQuote(WORKSPACE_ID)}, ${sqlQuote(ROOT_URI)}, ${sqlQuote(ROOT_PATH)}, 'write', 1, '["ai"]', 'smoke', 'active', ${sqlQuote(ACTOR_ID)}, ${sqlQuote(now)}, ${sqlQuote(now)}, 1);
|
||||
`);
|
||||
}
|
||||
|
||||
function seedRun() {
|
||||
async function seedRun(requestContext) {
|
||||
const stamp = Date.now();
|
||||
const runId = `run_task557_resume_${stamp}`;
|
||||
const sessionId = `mnote_task557_resume_${stamp}`;
|
||||
const now = new Date().toISOString();
|
||||
const runtimeJson = JSON.stringify({ runId, status: "running", lastEvent: "message.delta" });
|
||||
const payloadJson = JSON.stringify({
|
||||
requestId: `task557_resume_${stamp}`,
|
||||
agentId: "reasonix",
|
||||
message: "resume smoke user prompt",
|
||||
});
|
||||
sqliteExec(`
|
||||
INSERT OR REPLACE INTO ai_runtime_runs
|
||||
(id, user_id, workspace_id, document_id, session_id, run_id, title, profile, acp_runtime, trace_id, status, runtime_json, payload_json, deleted_at, created_at, updated_at, revision)
|
||||
VALUES
|
||||
(${sqlQuote(`arr_${stamp}`)}, ${sqlQuote(ACTOR_ID)}, ${sqlQuote(WORKSPACE_ID)}, ${sqlQuote(DOCUMENT_ID)}, ${sqlQuote(sessionId)}, ${sqlQuote(runId)}, 'Resume smoke', 'reasonix', 'reasonix', ${sqlQuote(`trace_task557_${stamp}`)}, 'running', ${sqlQuote(runtimeJson)}, ${sqlQuote(payloadJson)}, NULL, ${sqlQuote(now)}, ${sqlQuote(now)}, 1);
|
||||
DELETE FROM ai_runtime_events WHERE user_id=${sqlQuote(ACTOR_ID)} AND run_id=${sqlQuote(runId)};
|
||||
INSERT INTO ai_runtime_events
|
||||
(id, user_id, workspace_id, document_id, session_id, run_id, profile, acp_runtime, event_type, payload_json, created_at)
|
||||
VALUES
|
||||
(${sqlQuote(`are_${stamp}_1`)}, ${sqlQuote(ACTOR_ID)}, ${sqlQuote(WORKSPACE_ID)}, ${sqlQuote(DOCUMENT_ID)}, ${sqlQuote(sessionId)}, ${sqlQuote(runId)}, 'reasonix', 'reasonix', 'message.delta', '{"delta":"old-token"}', ${sqlQuote(now)}),
|
||||
(${sqlQuote(`are_${stamp}_2`)}, ${sqlQuote(ACTOR_ID)}, ${sqlQuote(WORKSPACE_ID)}, ${sqlQuote(DOCUMENT_ID)}, ${sqlQuote(sessionId)}, ${sqlQuote(runId)}, 'reasonix', 'reasonix', 'message.delta', '{"delta":"resumed-token"}', ${sqlQuote(now)});
|
||||
`);
|
||||
await seedAiRuntime(requestContext, BASE_URL, {
|
||||
id: `arr_${stamp}`,
|
||||
userId: ACTOR_ID,
|
||||
workspaceId: WORKSPACE_ID,
|
||||
documentId: DOCUMENT_ID,
|
||||
sessionId,
|
||||
runId,
|
||||
title: "Resume smoke",
|
||||
profile: "reasonix",
|
||||
acpRuntime: "reasonix",
|
||||
traceId: `trace_task557_${stamp}`,
|
||||
status: "running",
|
||||
runtimeJson: JSON.parse(runtimeJson),
|
||||
payloadJson: JSON.parse(payloadJson),
|
||||
events: [
|
||||
{ id: `are_${stamp}_1`, eventType: "message.delta", payloadJson: { delta: "old-token" } },
|
||||
{ id: `are_${stamp}_2`, eventType: "message.delta", payloadJson: { delta: "resumed-token" } },
|
||||
],
|
||||
timeoutMs: UI_TIMEOUT_MS,
|
||||
});
|
||||
return { runId, sessionId };
|
||||
}
|
||||
|
||||
@@ -98,7 +85,6 @@ async function signIn(context) {
|
||||
async function main() {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
ensureFixture();
|
||||
const seeded = seedRun();
|
||||
const browser = await chromium.launch({
|
||||
headless: process.env.MNOTE_PAGE_AI_VERIFY_HEADED !== "1",
|
||||
executablePath: fs.existsSync(CHROME) ? CHROME : undefined,
|
||||
@@ -125,6 +111,17 @@ async function main() {
|
||||
});
|
||||
try {
|
||||
await signIn(context);
|
||||
await setupWorkspaceAccess(context.request, BASE_URL, {
|
||||
actorId: ACTOR_ID,
|
||||
email: "mnote.e2e@example.com",
|
||||
workspaceId: WORKSPACE_ID,
|
||||
workspaceName: "MNote E2E Space",
|
||||
rootPath: ROOT_PATH,
|
||||
rootUri: ROOT_URI,
|
||||
capabilities: ["ai"],
|
||||
timeoutMs: UI_TIMEOUT_MS,
|
||||
});
|
||||
const seeded = await seedRun(context.request);
|
||||
await page.addInitScript(({ documentId, runId, sessionId }) => {
|
||||
window.localStorage.setItem(`hermes_page_ai_session:${documentId}:active-run`, JSON.stringify({
|
||||
schema: "mnote.page_ai_active_run_snapshot.v1",
|
||||
|
||||
Reference in New Issue
Block a user