fix: skip interactive session in headless mode (#2895)

* fix: skip interactive session in headless mode (#2892)

When SPAWN_HEADLESS=1, the orchestrator now exits with code 0 after
provisioning completes instead of attempting to launch the agent
interactively. This fixes Claude Code (and other agents) failing with
"Input must be provided through stdin or --prompt" when spawned via
`--headless --output json` without a prompt.

The VM is fully provisioned and ready — callers can SSH in or use
`spawn connect` to start the agent manually.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: clean up SPAWN_HEADLESS env in test afterEach to prevent leaks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
This commit is contained in:
A 2026-03-22 21:38:53 -07:00 committed by GitHub
parent 9280489ada
commit f1f2667cb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 5 deletions

View file

@ -531,18 +531,30 @@ async function postInstall(
logInfo(`Tip: ${agent.preLaunchMsg}`);
}
// Launch interactive session
// Launch agent
logInfo(`${agent.name} is ready`);
process.stderr.write("\n");
logInfo(`${cloud.cloudLabel} setup completed successfully!`);
process.stderr.write("\n");
logStep("Starting agent...");
prepareStdinForHandoff();
const launchCmd = agent.launchCmd();
saveLaunchCmd(launchCmd, spawnId);
// In headless mode, provisioning is done — skip the interactive session.
// The VM is healthy and the agent is installed; callers can SSH in or use `spawn connect`.
const isHeadless = process.env.SPAWN_HEADLESS === "1";
if (isHeadless) {
logInfo("Headless mode — provisioning complete. Skipping interactive session.");
if (tunnelHandle) {
tunnelHandle.stop();
}
process.exit(0);
}
logStep("Starting agent...");
prepareStdinForHandoff();
const sessionCmd = cloud.cloudName === "local" ? launchCmd : wrapWithRestartLoop(launchCmd);
// Auto-reconnect on connection drops. Ctrl+C (exit 0 or 130) exits immediately.