Files
LangBot/skills/scripts/e2e/reset-complex-agent-task.mjs
T
2026-08-01 09:23:41 +08:00

116 lines
4.0 KiB
JavaScript

#!/usr/bin/env node
import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { env } from "node:process";
import { spawnSync } from "node:child_process";
import {
apiJson,
boxWorkspaceNamespace,
ensureEvidence,
evidencePaths,
loadEnvFiles,
resetAndAuthLocalUser,
resolveWorkspaceUuid,
writeResult,
} from "./lib/langbot-e2e.mjs";
const DEFAULT_LOCAL_PASSWORD = "LangBotE2ELocalPass!2026";
await loadEnvFiles();
const paths = evidencePaths("reset-complex-agent-task");
await ensureEvidence(paths);
const scriptDir = dirname(fileURLToPath(import.meta.url));
const source = resolve(
scriptDir,
"../../skills/langbot-testing/fixtures/complex-agent-task/workspace",
);
const repo = env.LANGBOT_REPO || "";
let target = "";
const result = {
source: "setup_automation",
case_id: "reset-complex-agent-task",
run_id: paths.runId,
status: "fail",
reason: "",
target,
initial_test_exit_code: null,
evidence_collected: ["filesystem"],
};
try {
if (!repo) throw new Error("LANGBOT_REPO is required.");
const backendUrl = env.LANGBOT_BACKEND_URL || "";
const user = env.LANGBOT_E2E_LOGIN_USER || "";
const password = env.LANGBOT_E2E_LOGIN_PASSWORD || DEFAULT_LOCAL_PASSWORD;
if (!backendUrl) throw new Error("LANGBOT_BACKEND_URL is required.");
if (!user) throw new Error("LANGBOT_E2E_LOGIN_USER is required.");
const auth = await resetAndAuthLocalUser({ backendUrl, user, password });
const workspaceUuid = await resolveWorkspaceUuid(backendUrl, auth.token);
const bootstrap = await apiJson(backendUrl, "/api/v1/workspaces/bootstrap", {
token: auth.token,
skipWorkspace: true,
});
const workspaceAccess = (bootstrap.json.data?.workspaces || []).find(
(entry) => (entry.workspace?.uuid || entry.uuid) === workspaceUuid,
);
const instanceUuid = workspaceAccess?.workspace?.instance_uuid || workspaceAccess?.instance_uuid || "";
if (!instanceUuid) throw new Error("Workspace bootstrap did not include instance_uuid.");
const boxWorkspace = env.LANGBOT_BOX_WORKSPACE_HOST_PATH || resolve(
repo,
"data/box/default/tenants",
boxWorkspaceNamespace(instanceUuid, workspaceUuid),
);
target = resolve(boxWorkspace, "order-orchestrator");
result.target = target;
result.workspace_uuid = workspaceUuid;
await rm(target, { recursive: true, force: true });
await mkdir(dirname(target), { recursive: true });
await cp(source, target, { recursive: true });
const baseline = spawnSync(
"python3",
["-m", "unittest", "discover", "-s", "tests", "-v"],
{ cwd: target, encoding: "utf8", timeout: 60_000 },
);
result.initial_test_exit_code = baseline.status;
result.initial_failure_preview = `${baseline.stdout || ""}\n${baseline.stderr || ""}`.slice(0, 4000);
if (baseline.error) throw baseline.error;
if (baseline.status === 0) throw new Error("Complex task baseline unexpectedly passes; the fixture must start failing.");
await upsertEnvLocal(resolve("skills/.env.local"), {
LANGBOT_COMPLEX_AGENT_WORKSPACE: target,
});
result.status = "pass";
result.reason = "Complex task workspace reset and failing baseline confirmed.";
} catch (error) {
result.status = /required|ENOENT/.test(error.message) ? "env_issue" : "fail";
result.reason = error.message;
}
await writeResult(paths, result);
console.log(JSON.stringify(result, null, 2));
process.exit(result.status === "pass" ? 0 : result.status === "env_issue" ? 2 : 1);
async function upsertEnvLocal(path, updates) {
let content = "";
try {
content = await readFile(path, "utf8");
} catch (error) {
if (error.code !== "ENOENT") throw error;
}
const lines = content ? content.split(/\r?\n/) : [];
for (const [key, value] of Object.entries(updates)) {
const replacement = `${key}=${value}`;
const index = lines.findIndex((line) => line.startsWith(`${key}=`));
if (index >= 0) lines[index] = replacement;
else lines.push(replacement);
}
await writeFile(path, `${lines.filter(Boolean).join("\n")}\n`, "utf8");
}