#!/usr/bin/env node /** * Cassette Replay Worker * * Spawned as a detached subprocess by CassetteProcessManager instead of the real * agent CLI. Reads the cassette recording from CW_CASSETTE_DATA env var, replays * the JSONL output to stdout (which spawnDetached redirects to the output file), * writes signal.json relative to the process cwd, and exits. * * This is a plain .mjs file (no TypeScript) so it can be spawned with bare `node` * without any build step or tsx dependency. */ import { mkdirSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; const data = process.env.CW_CASSETTE_DATA; if (!data) { process.stderr.write('[replay-worker] CW_CASSETTE_DATA env var not set\n'); process.exit(1); } let recording; try { recording = JSON.parse(data); } catch (err) { process.stderr.write(`[replay-worker] failed to parse CW_CASSETTE_DATA: ${err.message}\n`); process.exit(1); } const { jsonlLines = [], signalJson = null, exitCode = 0 } = recording; // Write JSONL lines to stdout. // spawnDetached redirects stdout to the output file via open()+fd redirection, // so writing to process.stdout here is equivalent to writing to the output file. for (const line of jsonlLines) { process.stdout.write(line + '\n'); } // Write signal.json to the expected location relative to cwd. // The agent's cwd is set by spawnDetached to the agent working directory. if (signalJson) { const signalDir = join(process.cwd(), '.cw', 'output'); mkdirSync(signalDir, { recursive: true }); writeFileSync(join(signalDir, 'signal.json'), JSON.stringify(signalJson, null, 2), 'utf-8'); } process.exit(exitCode);