168 lines
4.8 KiB
JavaScript
168 lines
4.8 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
import { DatabaseSync } from "node:sqlite";
|
|
|
|
let runtimeRoot = "";
|
|
let discoverCodexProjectCandidates;
|
|
let discoverCodexProjectCandidatesInWorker;
|
|
|
|
async function setup() {
|
|
if (runtimeRoot) return;
|
|
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-local-agent-discovery-worker-"));
|
|
({
|
|
discoverCodexProjectCandidates,
|
|
discoverCodexProjectCandidatesInWorker,
|
|
} = await import("../local-agent/codex-session-discovery.mjs"));
|
|
}
|
|
|
|
test.after(async () => {
|
|
if (runtimeRoot) {
|
|
await rm(runtimeRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("discoverCodexProjectCandidatesInWorker returns the same folder/thread candidates as sync discovery", async () => {
|
|
await setup();
|
|
|
|
const codexRoot = path.join(runtimeRoot, ".codex");
|
|
const now = new Date("2026-03-31T12:00:00+08:00");
|
|
await mkdir(codexRoot, { recursive: true });
|
|
const stateDbPath = path.join(codexRoot, "state_5.sqlite");
|
|
const logsDbPath = path.join(codexRoot, "logs_1.sqlite");
|
|
const sessionIndexPath = path.join(codexRoot, "session_index.jsonl");
|
|
const globalStatePath = path.join(codexRoot, ".codex-global-state.json");
|
|
|
|
const stateDb = new DatabaseSync(stateDbPath);
|
|
stateDb.exec(`
|
|
CREATE TABLE threads (
|
|
id TEXT PRIMARY KEY,
|
|
rollout_path TEXT NOT NULL,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL,
|
|
source TEXT NOT NULL,
|
|
model_provider TEXT NOT NULL,
|
|
cwd TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
sandbox_policy TEXT NOT NULL,
|
|
approval_mode TEXT NOT NULL,
|
|
tokens_used INTEGER NOT NULL DEFAULT 0,
|
|
has_user_event INTEGER NOT NULL DEFAULT 0,
|
|
archived INTEGER NOT NULL DEFAULT 0,
|
|
archived_at INTEGER,
|
|
git_sha TEXT,
|
|
git_branch TEXT,
|
|
git_origin_url TEXT,
|
|
cli_version TEXT NOT NULL DEFAULT '',
|
|
first_user_message TEXT NOT NULL DEFAULT '',
|
|
agent_nickname TEXT,
|
|
agent_role TEXT,
|
|
memory_mode TEXT NOT NULL DEFAULT 'enabled',
|
|
model TEXT,
|
|
reasoning_effort TEXT,
|
|
agent_path TEXT
|
|
);
|
|
`);
|
|
const insertThread = stateDb.prepare(`
|
|
INSERT INTO threads (
|
|
id, rollout_path, created_at, updated_at, source, model_provider, cwd, title,
|
|
sandbox_policy, approval_mode, tokens_used, has_user_event, archived,
|
|
cli_version, first_user_message, agent_nickname, agent_role, memory_mode, model, reasoning_effort
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?, '0.118.0', '', ?, ?, 'enabled', 'gpt-5.4', 'medium')
|
|
`);
|
|
insertThread.run(
|
|
"019d-worker-boss-main",
|
|
path.join(codexRoot, "sessions/2026/03/31/rollout-boss-main.jsonl"),
|
|
1774929600,
|
|
1774929612,
|
|
"desktop",
|
|
"openai",
|
|
"/Users/kris/code/boss",
|
|
"Boss 主线程",
|
|
"workspace-write",
|
|
"never",
|
|
0,
|
|
null,
|
|
null,
|
|
);
|
|
insertThread.run(
|
|
"019d-worker-meiyesaas",
|
|
path.join(codexRoot, "sessions/2026/03/31/rollout-meiyesaas.jsonl"),
|
|
1774929630,
|
|
1774929644,
|
|
"desktop",
|
|
"openai",
|
|
"/Users/kris/code/meiyesaas",
|
|
"美业 SaaS",
|
|
"workspace-write",
|
|
"never",
|
|
0,
|
|
null,
|
|
null,
|
|
);
|
|
stateDb.close();
|
|
|
|
const logsDb = new DatabaseSync(logsDbPath);
|
|
logsDb.exec(`
|
|
CREATE TABLE logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
ts INTEGER NOT NULL,
|
|
ts_nanos INTEGER NOT NULL,
|
|
level TEXT NOT NULL,
|
|
target TEXT NOT NULL,
|
|
feedback_log_body TEXT,
|
|
module_path TEXT,
|
|
file TEXT,
|
|
line INTEGER,
|
|
thread_id TEXT,
|
|
process_uuid TEXT,
|
|
estimated_bytes INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
`);
|
|
const insertLog = logsDb.prepare(`
|
|
INSERT INTO logs (ts, ts_nanos, level, target, thread_id, estimated_bytes)
|
|
VALUES (?, 0, 'info', 'codex', ?, 0)
|
|
`);
|
|
insertLog.run(1774929612, "019d-worker-boss-main");
|
|
insertLog.run(1774929644, "019d-worker-meiyesaas");
|
|
logsDb.close();
|
|
|
|
await writeFile(
|
|
sessionIndexPath,
|
|
[
|
|
JSON.stringify({
|
|
id: "019d-worker-boss-main",
|
|
thread_name: "Boss 主线程",
|
|
updated_at: "2026-03-31T04:00:12.000000Z",
|
|
}),
|
|
JSON.stringify({
|
|
id: "019d-worker-meiyesaas",
|
|
thread_name: "美业 SaaS 主线程",
|
|
updated_at: "2026-03-31T04:00:44.000000Z",
|
|
}),
|
|
].join("\n") + "\n",
|
|
"utf8",
|
|
);
|
|
await writeFile(
|
|
globalStatePath,
|
|
JSON.stringify({ "thread-workspace-root-hints": {} }, null, 2),
|
|
"utf8",
|
|
);
|
|
|
|
const options = {
|
|
stateDbPath,
|
|
logsDbPath,
|
|
sessionIndexPath,
|
|
globalStatePath,
|
|
lookbackHours: 24,
|
|
now,
|
|
};
|
|
|
|
const syncResult = await discoverCodexProjectCandidates(options);
|
|
const workerResult = await discoverCodexProjectCandidatesInWorker(options);
|
|
|
|
assert.deepEqual(workerResult, syncResult);
|
|
});
|