feat(control-plane): add libSQL Turso backend

This commit is contained in:
Agent Board
2026-07-03 13:24:20 +08:00
parent ee6612028d
commit a75b3d11f9
44 changed files with 11070 additions and 666 deletions
+215
View File
@@ -0,0 +1,215 @@
"use strict";
const assert = require("node:assert/strict");
async function postDevSeed(requestContext, baseUrl, seeds, timeoutMs) {
const response = await requestContext.fetch(`${baseUrl.replace(/\/+$/, "")}/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;
}
assert(
response.ok(),
`/api/dev/seed 失败: ${response.status()} ${typeof payload === "string" ? payload : JSON.stringify(payload)}`,
);
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",
userId: actorId,
email: options.email || `${actorId}@example.com`,
username: options.username || actorId,
displayName: options.displayName || actorId,
role: options.role || "user",
workspaceId: options.workspaceId,
workspaceName: options.workspaceName || "MNote Smoke Workspace",
rootUri,
rootPath,
sourceKind: options.sourceKind || "local_folder",
permission: options.permission || "write",
capabilities: options.capabilities || ["ai"],
grantSource: options.grantSource || "smoke",
grantCreatedBy: options.grantCreatedBy || actorId,
},
],
options.timeoutMs,
);
}
async function seedAiRuntime(requestContext, baseUrl, options) {
return postDevSeed(
requestContext,
baseUrl,
[
{
kind: "seedAiRuntime",
id: options.id,
userId: options.userId,
workspaceId: options.workspaceId,
documentId: options.documentId,
sessionId: options.sessionId,
runId: options.runId,
title: options.title,
profile: options.profile || "reasonix",
acpRuntime: options.acpRuntime || options.profile || "reasonix",
traceId: options.traceId,
status: options.status || "running",
runtimeJson: options.runtimeJson || {},
payloadJson: options.payloadJson || {},
events: options.events || [],
},
],
options.timeoutMs,
);
}
async function clearRuntimeEvents(requestContext, baseUrl, options) {
return postDevSeed(
requestContext,
baseUrl,
[
{
kind: "clearRuntimeEvents",
userId: options.userId,
runId: options.runId,
},
],
options.timeoutMs,
);
}
async function getAiRuntimeRun(requestContext, baseUrl, options) {
const payload = await postDevSeed(
requestContext,
baseUrl,
[
{
kind: "getAiRuntimeRun",
userId: options.userId,
runId: 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",
userId: options.userId,
workspaceId: options.workspaceId,
documentId: options.documentId,
sessionId: 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",
userId: options.userId,
runId: options.runId,
limit: options.limit,
eventType: 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",
userId: options.userId,
runId: options.runId,
eventType: 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",
userId: options.userId,
workspaceId: options.workspaceId,
mnoteSessionId: 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",
userId: options.userId,
workspaceId: options.workspaceId,
mnoteSessionId: options.mnoteSessionId,
limit: options.limit,
},
],
options.timeoutMs,
);
return payload.results && payload.results[0] ? payload.results[0].bindings || [] : [];
}
module.exports = {
postDevSeed,
setupWorkspaceAccess,
seedAiRuntime,
clearRuntimeEvents,
getAiRuntimeRun,
listAiRuntimeRuns,
listAiRuntimeEvents,
countAiRuntimeEvents,
findExternalConversationBinding,
listExternalConversationBindings,
};
+44
View File
@@ -0,0 +1,44 @@
"use strict";
const path = require("node:path");
function buildControlPlaneTestEnv(dataRoot, baseEnv = process.env) {
const backend = String(baseEnv.MNOTE_SMOKE_CONTROL_PLANE_BACKEND || "libsql-local").trim();
if (!backend) {
throw new Error("MNOTE_SMOKE_CONTROL_PLANE_BACKEND 不能为空");
}
const env = {
MNOTE_CONTROL_PLANE_BACKEND: backend,
};
if (backend === "sqlite") {
env.MNOTE_CONTROL_PLANE_DB_PATH =
baseEnv.MNOTE_SMOKE_CONTROL_PLANE_DB_PATH || path.join(dataRoot, "control-plane.sqlite3");
return env;
}
if (backend === "libsql-local" || backend === "turso-local" || backend === "turso") {
env.MNOTE_TURSO_LOCAL_PATH =
baseEnv.MNOTE_SMOKE_TURSO_LOCAL_PATH || path.join(dataRoot, "control-plane-libsql.db");
return env;
}
if (backend === "turso-local-replica" || backend === "turso-remote-replica") {
env.MNOTE_TURSO_LOCAL_REPLICA_PATH =
baseEnv.MNOTE_SMOKE_TURSO_LOCAL_REPLICA_PATH
|| baseEnv.MNOTE_TURSO_LOCAL_REPLICA_PATH
|| path.join(dataRoot, "control-plane-replica.db");
return env;
}
if (backend === "turso-remote" || backend === "turso-synced") {
return env;
}
throw new Error(`Unsupported MNOTE_SMOKE_CONTROL_PLANE_BACKEND: ${backend}`);
}
module.exports = {
buildControlPlaneTestEnv,
};