Files

77 lines
4.1 KiB
JavaScript

// D2 driver: one managed coder session on the scratch root, real modules from the land tree.
// Usage: node d2-drive.mjs <pi|claude-code> <turn|noturn>. Prints no credential value.
import { readFileSync, existsSync, copyFileSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
const LAND = "/mnt/storage/scratch/sage-r64/land";
const D2 = "/mnt/storage/scratch/sage-r64/d2";
const [harness, mode] = process.argv.slice(2);
const { Client } = await import(`${LAND}/packages/bus/src/client.mjs`);
const { readSessions, SESSION_CREDENTIALS } = await import(`${LAND}/packages/seat/src/session.mjs`);
const { bootConfig, loadSystem } = await import(`${LAND}/packages/cli/src/config.mjs`);
const { startHost, startTimeOf } = await import(`${LAND}/packages/cli/src/host.mjs`);
const { createLauncher } = await import(`${LAND}/packages/cli/src/launcher.mjs`);
const { AGENT_MARKERS } = await import(`${LAND}/packages/cli/src/transport.mjs`);
const env = { ...process.env, MOSAIC_CONFIG: join(D2, "config", "config.json"), MOSAIC_ROLES_DIR: join(LAND, "roles"), MOSAIC_CLAUDE_BIN: join(homedir(), ".local/bin/claude") };
for (const k of AGENT_MARKERS) delete env[k];
delete env.NODE_TEST_CONTEXT;
for (const k of SESSION_CREDENTIALS.flat()) delete env[k];
// The host's session credential file holds one name, CLAUDE_CODE_OAUTH_TOKEN. Loaded, never printed.
for (const line of readFileSync(join(homedir(), ".config/mosaic-dev/secrets/claude-session.env"), "utf8").split("\n")) {
const m = line.match(/^\s*(?:export\s+)?([A-Z_][A-Z0-9_]*)=(.*)$/);
if (m) env[m[1]] = m[2].replace(/^(['"])(.*)\1$/, "$2");
}
console.log("credential names loaded:", SESSION_CREDENTIALS.flat().filter((k) => env[k]).join(","));
const business = "mosaic-stack";
const logs = [];
const log = (l) => { logs.push(l); console.log("log:", l); };
const system = loadSystem({ env });
const boot = bootConfig({ system, businessId: business, env });
const host = await startHost({ boot, business, log });
const launcher = createLauncher({ host, system, env, sessionDefaults: { pollInterval: 100 }, log });
let code = 0;
try {
await launcher.listen();
const bind = async (role, run) => {
const b = await host.bindLaunch({ business, role, run, harness: "claude-code", pid: process.pid, startTime: startTimeOf(process.pid) });
const c = new Client({ path: host.path, cap: b.cap });
await c.call("role.claim");
return { b, c };
};
const chief = await bind("chief", `chief-d2-${harness}`);
const cto = await bind("cto", `cto-d2-${harness}`);
const launched = await launcher.fromSession(chief.b.cap, "coder");
console.log("launch:", JSON.stringify(launched));
if (!launched.ok) throw new Error("launch refused");
const run = launched.result.run;
const runDir = readSessions(boot.dataRoot ?? system.dataRoot).find((s) => s.run === run)?.runDir
?? join(system.dataRoot, "launches", business, run);
const manifest = join(runDir, "bundle", "manifest.json");
for (let i = 0; i < 100 && !existsSync(manifest); i++) await new Promise((r) => setTimeout(r, 100));
copyFileSync(manifest, join(D2, `manifest-${harness}.json`));
copyFileSync(join(runDir, "bundle", "prompt.md"), join(D2, `prompt-${harness}.md`));
console.log("runDir:", runDir);
if (mode === "turn") {
const sent = await cto.c.call("message.send", { to: "coder", class: "REQUEST", body: "D2 check for row 64. Reply in one line: list the skill names in your Skills section, and nothing else. Don't use any tool." });
const end = Date.now() + 240000;
let reply;
while (!reply && Date.now() < end) {
reply = (await cto.c.call("message.receive")).find((m) => m.in_reply_to === sent.id);
if (!reply) await new Promise((r) => setTimeout(r, 1000));
}
console.log("reply:", reply ? JSON.stringify({ from_role: reply.from_role, from_run: reply.from_run, body: reply.body.slice(-600) }) : "NONE in 240 s");
if (!reply) code = 3;
} else {
await new Promise((r) => setTimeout(r, 3000));
}
} catch (e) {
console.log("error:", e.message);
code = 2;
} finally {
await launcher.close();
await host.close(0);
}
process.exit(code);