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); });