// The human transport (lead decision 70): run // `packages/bus/src/human-cli.mjs ` as a child, write // `{business, verb, args}` on its stdin, read the JSON reply on its stdout, // or one bus error code on its stderr. The bus does the proof: the child // re-executes itself with a nonce and the broker checks the process and its // ancestry for agent markers. Nothing here carries a capability. import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import { CliError } from "./errors.mjs"; export const HUMAN_CLI = fileURLToPath(new URL("../../bus/src/human-cli.mjs", import.meta.url)); // The broker's markers (packages/bus/src/human.mjs). The broker checks the // whole ancestry; this check is only the clear early message for the case // it would refuse anyway. export const AGENT_MARKERS = Object.freeze([ "MOSAIC_BUS_CAP", "MOSAIC_RUN_ID", "MOSAIC_AGENT_RUN", "CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT", "CODEX_THREAD_ID", "PI_AGENT_DIR", ]); export function refuseInsideAgent(env = process.env) { const found = AGENT_MARKERS.filter((k) => env[k]); if (found.length > 0) { throw new CliError(`refused: this runs only from a human shell, outside any agent run (found ${found.join(", ")} in the environment)`, 3); } } // Exit codes by bus error code; anything else is invalid input (2). const EXIT = { "human-required": 3, unauthenticated: 3, "unknown-business": 3, "read-only": 3, "outcome-unknown": 1, "response-too-large": 1, "invalid-response": 1, }; export function busExit(code) { return EXIT[code] ?? 2; } // Returns a call(verb, args) for one business. `cli` and `spawn` exist for // the tests; the command line never sets them. export function humanTransport({ socket, business, env = process.env, cli = HUMAN_CLI, spawn = spawnSync, timeoutMs = 30000 }) { return async function call(verb, args = {}) { const proc = spawn(process.execPath, [cli, socket], { input: `${JSON.stringify({ business, verb, args })}\n`, encoding: "utf8", env, timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024, }); if (proc.error || proc.status === null) throw new CliError("outcome-unknown: the bus transport did not finish", 1); if (proc.status !== 0) { const code = String(proc.stderr ?? "").trim().split("\n").reverse().find((l) => /^[a-z-]{1,64}$/.test(l)) ?? "invalid-response"; const error = new CliError(code, busExit(code)); error.code = code; throw error; } try { return JSON.parse(proc.stdout); } catch { const error = new CliError("invalid-response", 1); error.code = "invalid-response"; throw error; } }; }