fix(zeroclaw): remove broken zeroclaw agent (repo 404) (#3107)

* fix(zeroclaw): remove broken zeroclaw agent (repo 404)

The zeroclaw-labs/zeroclaw GitHub repository returns 404 — all installs
fail. Remove zeroclaw entirely from the matrix: agent definition,
setup code, shell scripts, e2e tests, packer config, skill files,
and documentation.

Fixes #3102

Agent: code-health
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(zeroclaw): remove stale zeroclaw reference from discovery.md ARM agents list

Addresses security review on PR #3107 — the last remaining zeroclaw
reference in .claude/rules/discovery.md is now removed.

Agent: issue-fixer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(zeroclaw): remove remaining stale zeroclaw references from CI/packer

Remove zeroclaw from:
- .github/workflows/agent-tarballs.yml ARM build matrix
- .github/workflows/docker.yml agent matrix
- packer/digitalocean.pkr.hcl comment
- sh/e2e/e2e.sh comment

Addresses all 5 stale references flagged in security review of PR #3107.

Agent: issue-fixer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
A 2026-03-30 15:35:40 -07:00 committed by GitHub
parent 570caaba8d
commit 5e0144b645
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 22 additions and 603 deletions

View file

@ -62,7 +62,7 @@ Do NOT add agents speculatively. Only add one if there's **real community buzz**
Agents that ship compiled binaries (Rust, Go, etc.) need separate ARM (aarch64) tarball builds. npm-based agents are arch-independent and only need x86_64 builds. When adding a new agent:
- If it installs via `npm install -g` → x86_64 tarball only (Node handles arch)
- If it installs a pre-compiled binary (curl download, cargo install, go install) → add an ARM entry in `.github/workflows/agent-tarballs.yml` matrix `include` section
- Current native binary agents needing ARM: zeroclaw (Rust), opencode (Go), hermes, claude
- Current native binary agents needing ARM: opencode (Go), hermes, claude
To add: same steps as before (manifest.json entry, matrix entries, implement on 1+ cloud, README).

View file

@ -49,8 +49,6 @@ jobs:
# Native-binary agents need ARM builds too.
# npm-based agents (codex, openclaw, kilocode) are arch-independent — x86_64 only.
include:
- agent: zeroclaw
arch: arm64
- agent: opencode
arch: arm64
- agent: hermes

View file

@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
agent: [claude, codex, cursor, openclaw, opencode, kilocode, zeroclaw, hermes, junie]
agent: [claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

View file

@ -324,7 +324,6 @@ If an agent fails to install or launch on a cloud:
|---|---|---|---|---|---|---|
| [**Claude Code**](https://claude.ai) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [**OpenClaw**](https://github.com/openclaw/openclaw) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [**ZeroClaw**](https://github.com/zeroclaw-labs/zeroclaw) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [**Codex CLI**](https://github.com/openai/codex) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [**OpenCode**](https://github.com/sst/opencode) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [**Kilo Code**](https://github.com/Kilo-Org/kilocode) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |

View file

@ -7,10 +7,6 @@
"url": "https://openclaw.ai/apple-touch-icon.png",
"ext": "png"
},
"zeroclaw": {
"url": "https://avatars.githubusercontent.com/u/261820148?s=200&v=4",
"ext": "png"
},
"codex": {
"url": "https://avatars.githubusercontent.com/u/14957082?s=200&v=4",
"ext": "png"

View file

@ -89,52 +89,6 @@
"gateway"
]
},
"zeroclaw": {
"name": "ZeroClaw",
"description": "Fast, small, fully autonomous AI assistant infrastructure — deploy anywhere, swap anything",
"url": "https://github.com/zeroclaw-labs/zeroclaw",
"install": "curl -LsSf https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/a117be64fdaa31779204beadf2942c8aef57d0e5/scripts/bootstrap.sh | bash -s -- --install-rust --install-system-deps --prefer-prebuilt",
"launch": "zeroclaw agent",
"env": {
"OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}",
"ZEROCLAW_PROVIDER": "openrouter"
},
"config_files": {
"~/.zeroclaw/config.toml": {
"security": {
"autonomy": "full",
"supervised": false,
"allow_destructive": true
},
"shell": {
"policy": "allow_all"
}
}
},
"notes": "Rust-based agent framework built by Harvard/MIT/Sundai.Club communities. Natively supports OpenRouter via OPENROUTER_API_KEY + ZEROCLAW_PROVIDER=openrouter. Requires compilation from source (~5-10 min).",
"icon": "https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/assets/agents/zeroclaw.png",
"featured_cloud": [
"digitalocean",
"sprite"
],
"creator": "Sundai.Club",
"repo": "zeroclaw-labs/zeroclaw",
"license": "Apache-2.0",
"created": "2026-02",
"added": "2025-12",
"github_stars": 29095,
"stars_updated": "2026-03-29",
"language": "Rust",
"runtime": "binary",
"category": "cli",
"tagline": "Fast, small, fully autonomous AI infrastructure — deploy anywhere, swap anything",
"tags": [
"coding",
"terminal",
"rust",
"autonomous"
]
},
"codex": {
"name": "Codex CLI",
"description": "OpenAI's open-source coding agent",
@ -453,37 +407,31 @@
"matrix": {
"local/claude": "implemented",
"local/openclaw": "implemented",
"local/zeroclaw": "implemented",
"local/codex": "implemented",
"local/opencode": "implemented",
"local/kilocode": "implemented",
"hetzner/claude": "implemented",
"hetzner/openclaw": "implemented",
"hetzner/zeroclaw": "implemented",
"hetzner/codex": "implemented",
"hetzner/opencode": "implemented",
"hetzner/kilocode": "implemented",
"aws/claude": "implemented",
"aws/openclaw": "implemented",
"aws/zeroclaw": "implemented",
"aws/codex": "implemented",
"aws/opencode": "implemented",
"aws/kilocode": "implemented",
"digitalocean/claude": "implemented",
"digitalocean/openclaw": "implemented",
"digitalocean/zeroclaw": "implemented",
"digitalocean/codex": "implemented",
"digitalocean/opencode": "implemented",
"digitalocean/kilocode": "implemented",
"gcp/claude": "implemented",
"gcp/openclaw": "implemented",
"gcp/zeroclaw": "implemented",
"gcp/codex": "implemented",
"gcp/opencode": "implemented",
"gcp/kilocode": "implemented",
"sprite/claude": "implemented",
"sprite/openclaw": "implemented",
"sprite/zeroclaw": "implemented",
"sprite/codex": "implemented",
"sprite/opencode": "implemented",
"sprite/kilocode": "implemented",

View file

@ -191,12 +191,6 @@ describe("createCloudAgents", () => {
"ANTHROPIC_BASE_URL",
],
],
[
"zeroclaw",
[
"ZEROCLAW_PROVIDER=openrouter",
],
],
[
"hermes",
[
@ -228,11 +222,6 @@ describe("createCloudAgents", () => {
}
});
it("zeroclaw agent configure calls runServer", async () => {
await result.agents.zeroclaw.configure?.("sk-or-v1-test", undefined, new Set());
expect(runner.runServer).toHaveBeenCalled();
});
it("all agents have launchCmd returning non-empty string", () => {
for (const agent of Object.values(result.agents)) {
const cmd = agent.launchCmd();

View file

@ -189,7 +189,6 @@ describe("validateLaunchCmd", () => {
"source ~/.spawnrc 2>/dev/null; export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; openclaw tui",
"source ~/.spawnrc 2>/dev/null; source ~/.zshrc 2>/dev/null; opencode",
"source ~/.spawnrc 2>/dev/null; source ~/.zshrc 2>/dev/null; kilocode",
"export PATH=$HOME/.cargo/bin:$PATH; source ~/.cargo/env 2>/dev/null; source ~/.spawnrc 2>/dev/null; zeroclaw agent",
"source ~/.spawnrc 2>/dev/null; hermes",
"claude",
"aider",

View file

@ -22,10 +22,6 @@ describe("getSpawnSkillPath", () => {
"openclaw",
"~/.openclaw/skills/spawn/SKILL.md",
],
[
"zeroclaw",
"~/.zeroclaw/workspace/AGENTS.md",
],
[
"opencode",
"~/.config/opencode/AGENTS.md",
@ -67,7 +63,6 @@ describe("isAppendMode", () => {
"claude",
"codex",
"openclaw",
"zeroclaw",
"opencode",
"kilocode",
"junie",
@ -85,7 +80,6 @@ describe("getSkillContent", () => {
"claude",
"codex",
"openclaw",
"zeroclaw",
"opencode",
"kilocode",
"hermes",
@ -114,7 +108,6 @@ describe("getSkillContent", () => {
}
for (const agent of [
"zeroclaw",
"opencode",
"kilocode",
"junie",
@ -184,7 +177,6 @@ describe("injectSpawnSkill", () => {
"claude",
"codex",
"openclaw",
"zeroclaw",
"opencode",
"kilocode",
"hermes",

View file

@ -66,7 +66,6 @@ function defaultSshCommand(host: string, user: string, keyOpts: string[], cmd: s
const KNOWN_AGENTS = [
"claude",
"openclaw",
"zeroclaw",
"codex",
"opencode",
"kilocode",
@ -79,7 +78,7 @@ type KnownAgent = (typeof KNOWN_AGENTS)[number];
function detectAgent(host: string, user: string, keyOpts: string[], runCmd: SshCommandFn): string | null {
// First: check running processes
const psCmd =
"ps aux 2>/dev/null | grep -oE 'claude(-code)?|openclaw|zeroclaw|codex|opencode|kilocode|hermes|junie' | grep -v grep | head -1 || true";
"ps aux 2>/dev/null | grep -oE 'claude(-code)?|openclaw|codex|opencode|kilocode|hermes|junie' | grep -v grep | head -1 || true";
const psOut = runCmd(host, user, keyOpts, psCmd);
if (psOut) {
const match = KNOWN_AGENTS.find((b: KnownAgent) => psOut.includes(b));

View file

@ -35,7 +35,6 @@ const MARKETPLACE_IMAGES: Record<string, string> = {
openclaw: "openrouter-spawnopenclaw",
opencode: "openrouter-spawnopencode",
kilocode: "openrouter-spawnkilocode",
zeroclaw: "openrouter-spawnzeroclaw",
hermes: "openrouter-spawnhermes",
junie: "openrouter-spawnjunie",
};

View file

@ -406,7 +406,7 @@ export function validateLaunchCmd(cmd: string): void {
"Invalid launch command in history: invalid agent invocation\n\n" +
`Command: "${cmd}"\n` +
`Rejected segment: "${lastSegment}"\n\n` +
"The final segment must be a simple binary name (e.g., 'claude', 'zeroclaw agent').\n\n" +
"The final segment must be a simple binary name (e.g., 'claude', 'hermes').\n\n" +
"Your spawn history file may be corrupted or tampered with.\n" +
`To fix: run 'spawn list --clear' to reset history`,
);

View file

@ -592,51 +592,6 @@ export async function startGateway(runner: CloudRunner): Promise<void> {
logInfo("OpenClaw gateway started");
}
// ─── ZeroClaw Config ─────────────────────────────────────────────────────────
async function setupZeroclawConfig(runner: CloudRunner, _apiKey: string): Promise<void> {
logStep("Configuring ZeroClaw for autonomous operation...");
// Remove any pre-existing config (e.g. from Docker image extraction) before
// running onboard, which generates a fresh config with the correct API key.
await runner.runServer("rm -f ~/.zeroclaw/config.toml");
// Run onboard first to set up provider/key
await runner.runServer(
`source ~/.spawnrc 2>/dev/null; export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"; zeroclaw onboard --api-key "\${OPENROUTER_API_KEY}" --provider openrouter`,
);
// Patch autonomy settings in-place. `zeroclaw onboard` already generates
// [security] and [shell] sections — so we sed the values instead of
// appending duplicate sections.
const patchScript = [
"cd ~/.zeroclaw",
// Update existing security values (or append section if missing)
'if grep -q "^\\[security\\]" config.toml 2>/dev/null; then',
" sed -i 's/^autonomy = .*/autonomy = \"full\"/' config.toml",
" sed -i 's/^supervised = .*/supervised = false/' config.toml",
" sed -i 's/^allow_destructive = .*/allow_destructive = true/' config.toml",
"else",
" printf '\\n[security]\\nautonomy = \"full\"\\nsupervised = false\\nallow_destructive = true\\n' >> config.toml",
"fi",
// Update existing shell policy (or append section if missing)
'if grep -q "^\\[shell\\]" config.toml 2>/dev/null; then',
" sed -i 's/^policy = .*/policy = \"allow_all\"/' config.toml",
"else",
" printf '\\n[shell]\\npolicy = \"allow_all\"\\n' >> config.toml",
"fi",
// Force native runtime (no Docker) — zeroclaw auto-detects Docker and
// launches in a container otherwise, which hangs the interactive session.
'if grep -q "^\\[runtime\\]" config.toml 2>/dev/null; then',
" sed -i 's/^adapter = .*/adapter = \"native\"/' config.toml",
"else",
" printf '\\n[runtime]\\nadapter = \"native\"\\n' >> config.toml",
"fi",
].join("\n");
await runner.runServer(patchScript);
logInfo("ZeroClaw configured for autonomous operation");
}
// ─── OpenCode Install Command ────────────────────────────────────────────────
function openCodeInstallCmd(): string {
@ -894,10 +849,6 @@ export async function setupAutoUpdate(runner: CloudRunner, agentName: string, up
// ─── Default Agent Definitions ───────────────────────────────────────────────
// Last zeroclaw release that shipped Linux prebuilt binaries (v0.1.9a has none).
// Used for direct binary install to avoid a Rust source build timeout.
const ZEROCLAW_PREBUILT_TAG = "v0.1.7-beta.30";
function createAgents(runner: CloudRunner): Record<string, AgentConfig> {
return {
claude: {
@ -1011,50 +962,6 @@ function createAgents(runner: CloudRunner): Record<string, AgentConfig> {
"npm install -g ${_NPM_G_FLAGS:-} @kilocode/cli@latest",
},
zeroclaw: {
name: "ZeroClaw",
cloudInitTier: "minimal",
modelEnvVar: "ZEROCLAW_MODEL",
preProvision: detectGithubAuth,
install: async () => {
// Direct binary install from pinned release (v0.1.9a "latest" has no assets,
// causing the bootstrap --prefer-prebuilt path to 404-fail and fall back to
// a Rust source build that exceeds the 600s install timeout).
const directInstallCmd =
`_ZC_ARCH="$(uname -m)"; ` +
`if [ "$_ZC_ARCH" = "x86_64" ]; then _ZC_TARGET="x86_64-unknown-linux-gnu"; ` +
`elif [ "$_ZC_ARCH" = "aarch64" ] || [ "$_ZC_ARCH" = "arm64" ]; then _ZC_TARGET="aarch64-unknown-linux-gnu"; ` +
`else echo "Unsupported arch: $_ZC_ARCH" >&2; exit 1; fi; ` +
`_ZC_URL="https://github.com/zeroclaw-labs/zeroclaw/releases/download/${ZEROCLAW_PREBUILT_TAG}/zeroclaw-\${_ZC_TARGET}.tar.gz"; ` +
`_ZC_TMP="$(mktemp -d)"; ` +
`curl --proto '=https' -fsSL "$_ZC_URL" -o "$_ZC_TMP/zeroclaw.tar.gz" && ` +
`tar -xzf "$_ZC_TMP/zeroclaw.tar.gz" -C "$_ZC_TMP" && ` +
`{ mkdir -p "$HOME/.local/bin" && install -m 755 "$_ZC_TMP/zeroclaw" "$HOME/.local/bin/zeroclaw"; } && ` +
`rm -rf "$_ZC_TMP"`;
await installAgent(runner, "ZeroClaw", directInstallCmd, 120);
},
envVars: (apiKey) => [
`OPENROUTER_API_KEY=${apiKey}`,
"ZEROCLAW_PROVIDER=openrouter",
"ZEROCLAW_RUNTIME=native",
],
configure: (apiKey) => setupZeroclawConfig(runner, apiKey),
launchCmd: () =>
"export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH; source ~/.spawnrc 2>/dev/null; zeroclaw agent",
updateCmd:
'export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"; ' +
`_ZC_ARCH="$(uname -m)"; ` +
`if [ "$_ZC_ARCH" = "x86_64" ]; then _ZC_TARGET="x86_64-unknown-linux-gnu"; ` +
`elif [ "$_ZC_ARCH" = "aarch64" ] || [ "$_ZC_ARCH" = "arm64" ]; then _ZC_TARGET="aarch64-unknown-linux-gnu"; ` +
"else exit 1; fi; " +
`_ZC_URL="https://github.com/zeroclaw-labs/zeroclaw/releases/latest/download/zeroclaw-\${_ZC_TARGET}.tar.gz"; ` +
`_ZC_TMP="$(mktemp -d)"; ` +
`curl --proto '=https' -fsSL "$_ZC_URL" -o "$_ZC_TMP/zeroclaw.tar.gz" && ` +
`tar -xzf "$_ZC_TMP/zeroclaw.tar.gz" -C "$_ZC_TMP" && ` +
`install -m 755 "$_ZC_TMP/zeroclaw" "$HOME/.local/bin/zeroclaw" && ` +
`rm -rf "$_ZC_TMP"`,
},
hermes: {
name: "Hermes Agent",
cloudInitTier: "minimal",

View file

@ -285,7 +285,7 @@ export async function runOrchestration(
// Skip cloud-init for minimal-tier agents when using tarballs or snapshots.
// Ubuntu 24.04 base images already have curl + git, so minimal agents (claude,
// opencode, zeroclaw, hermes) don't need the cloud-init package install step.
// opencode, hermes) don't need the cloud-init package install step.
// This saves ~30-60s by just waiting for SSH instead of polling for cloud-init completion.
if (
cloud.cloudName !== "local" &&

View file

@ -30,7 +30,7 @@ You have the \`spawn\` CLI installed. Use it to provision cloud VMs with AI agen
spawn <agent> <cloud> --headless --output json --prompt "task description"
\`\`\`
**Agents:** claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie
**Agents:** claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie
**Clouds:** hetzner, digitalocean, aws, gcp, sprite
Returns JSON: \`{"status":"success","ip_address":"...","ssh_user":"root","server_id":"..."}\`
@ -74,7 +74,7 @@ const HERMES_SNIPPET = `
You have the \`spawn\` CLI for creating child cloud VMs with AI agents.
Use \`spawn <agent> <cloud> --headless --output json --prompt "task"\` to delegate work.
Available agents: claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie.
Available agents: claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie.
Cloud credentials are pre-configured. Run \`spawn list --json\` to see children.
\`--headless\` only provisions. To run a prompt on the child: \`ssh root@<ip> "bash -lc 'claude -p --dangerously-skip-permissions \\"prompt\\"'"\`. Always use \`bash -lc\` (binaries are in ~/.local/bin/).
`;
@ -104,11 +104,6 @@ const AGENT_SKILLS: Record<string, SkillConfig> = {
content: SKILL_FRONTMATTER + SKILL_BODY,
append: false,
},
zeroclaw: {
remotePath: "~/.zeroclaw/workspace/AGENTS.md",
content: SKILL_BODY,
append: false,
},
opencode: {
remotePath: "~/.config/opencode/AGENTS.md",
content: SKILL_BODY,

View file

@ -30,13 +30,6 @@
"mkdir -p ~/.npm-global/bin && npm install -g --prefix ~/.npm-global @kilocode/cli"
]
},
"zeroclaw": {
"tier": "minimal",
"install": [
"if [ ! -f /swapfile ]; then fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile; fi",
"curl -LsSf https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/a117be64fdaa31779204beadf2942c8aef57d0e5/scripts/bootstrap.sh | bash -s -- --install-rust --install-system-deps --prefer-prebuilt"
]
},
"hermes": {
"tier": "minimal",
"install": [

View file

@ -35,8 +35,8 @@ source "digitalocean" "spawn" {
api_token = var.digitalocean_access_token
image = "ubuntu-24-04-x64"
region = "sfo3"
# 2 GB RAM needed Claude's native installer and zeroclaw's Rust build
# get OOM-killed on s-1vcpu-1gb. Snapshots built here work on all sizes.
# 2 GB RAM needed Claude's native installer gets OOM-killed on
# s-1vcpu-1gb. Snapshots built here work on all sizes.
size = "s-2vcpu-2gb"
ssh_username = "root"

View file

@ -13,9 +13,9 @@ fi
# Validate agent name against allowed list to prevent injection
case "${AGENT_NAME}" in
openclaw|codex|kilocode|claude|opencode|zeroclaw|hermes|junie) ;;
openclaw|codex|kilocode|claude|opencode|hermes|junie) ;;
*)
printf 'Error: Invalid agent name: %s\nAllowed: openclaw, codex, kilocode, claude, opencode, zeroclaw, hermes, junie\n' "${AGENT_NAME}" >&2
printf 'Error: Invalid agent name: %s\nAllowed: openclaw, codex, kilocode, claude, opencode, hermes, junie\n' "${AGENT_NAME}" >&2
exit 1
;;
esac
@ -44,9 +44,6 @@ case "${AGENT_NAME}" in
opencode)
echo "/root/.opencode/" >> "${PATHS_FILE}"
;;
zeroclaw)
echo "/root/.cargo/bin/zeroclaw" >> "${PATHS_FILE}"
;;
hermes)
echo "/root/.local/bin/hermes" >> "${PATHS_FILE}"
echo "/root/.local/share/" >> "${PATHS_FILE}"

View file

@ -24,12 +24,6 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/aws/claude.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/aws/openclaw.sh)
```
#### ZeroClaw
```bash
bash <(curl -fsSL https://openrouter.ai/labs/spawn/aws/zeroclaw.sh)
```
#### Codex CLI
```bash

View file

@ -1,26 +0,0 @@
#!/bin/bash
set -eo pipefail
# Thin shim: ensures bun is available, runs bundled aws.js (local or from GitHub release)
_ensure_bun() {
if command -v bun &>/dev/null; then return 0; fi
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
export PATH="$HOME/.bun/bin:$PATH"
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
}
_ensure_bun
# SPAWN_CLI_DIR override — force local source (used by e2e tests)
if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/aws/main.ts" ]]; then
exec bun run "$SPAWN_CLI_DIR/packages/cli/src/aws/main.ts" zeroclaw "$@"
fi
# Remote — download and run compiled TypeScript bundle
AWS_JS=$(mktemp)
trap 'rm -f "$AWS_JS"' EXIT
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/aws-latest/aws.js" -o "$AWS_JS" \
|| { printf '\033[0;31mFailed to download aws.js\033[0m\n' >&2; exit 1; }
exec bun run "$AWS_JS" zeroclaw "$@"

View file

@ -16,12 +16,6 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/digitalocean/claude.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/digitalocean/openclaw.sh)
```
#### ZeroClaw
```bash
bash <(curl -fsSL https://openrouter.ai/labs/spawn/digitalocean/zeroclaw.sh)
```
#### Codex CLI
```bash

View file

@ -1,84 +0,0 @@
#!/bin/bash
set -eo pipefail
# Thin shim: ensures bun is available, runs bundled digitalocean.js (local or from GitHub release)
# Includes restart loop for SIGTERM recovery on DigitalOcean
_AGENT_NAME="zeroclaw"
_MAX_RETRIES=3
_ensure_bun() {
if command -v bun &>/dev/null; then return 0; fi
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
export PATH="$HOME/.bun/bin:$PATH"
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
}
# Run command in the foreground so bun gets full terminal access (raw mode,
# arrow keys for interactive prompts). The old pattern backgrounded the child
# with & + wait so a SIGTERM trap could forward the signal, but that removed
# bun from the foreground process group and broke @clack/prompts multiselect.
# Now SIGTERM is detected from exit code 143 (128 + 15) after the child exits.
_run_with_restart() {
# In headless mode (E2E / --headless), skip the restart loop entirely.
# Restarting in headless mode creates duplicate droplets, exhausting the
# account's droplet quota and causing all subsequent agents to fail.
if [ "${SPAWN_HEADLESS:-}" = "1" ]; then
"$@"
return $?
fi
local attempt=0
local backoff=2
while [ "$attempt" -lt "$_MAX_RETRIES" ]; do
attempt=$((attempt + 1))
"$@"
local exit_code=$?
# Normal exit
if [ "$exit_code" -eq 0 ]; then
return 0
fi
# SIGTERM (143) or SIGKILL (137) — attempt restart
if [ "$exit_code" -eq 143 ] || [ "$exit_code" -eq 137 ]; then
printf '\033[0;33m[spawn/%s] Agent process terminated (exit %s). The droplet is likely still running.\033[0m\n' \
"$_AGENT_NAME" "$exit_code" >&2
printf '\033[0;33m[spawn/%s] Check your DigitalOcean dashboard: https://cloud.digitalocean.com/droplets\033[0m\n' \
"$_AGENT_NAME" >&2
if [ "$attempt" -lt "$_MAX_RETRIES" ]; then
printf '\033[0;33m[spawn/%s] Restarting (attempt %s/%s, backoff %ss)...\033[0m\n' \
"$_AGENT_NAME" "$((attempt + 1))" "$_MAX_RETRIES" "$backoff" >&2
sleep "$backoff"
backoff=$((backoff * 2))
continue
else
printf '\033[0;31m[spawn/%s] Max restart attempts reached (%s). Giving up.\033[0m\n' \
"$_AGENT_NAME" "$_MAX_RETRIES" >&2
return "$exit_code"
fi
fi
# Other failure — exit with the original code
return "$exit_code"
done
}
_ensure_bun
# SPAWN_CLI_DIR override — force local source (used by e2e tests)
if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/digitalocean/main.ts" ]]; then
_run_with_restart bun run "$SPAWN_CLI_DIR/packages/cli/src/digitalocean/main.ts" "$_AGENT_NAME" "$@"
exit $?
fi
# Remote — download bundled digitalocean.js from GitHub release
DO_JS=$(mktemp)
trap 'rm -f "$DO_JS"' EXIT
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|| { printf '\033[0;31mFailed to download digitalocean.js\033[0m\n' >&2; exit 1; }
_run_with_restart bun run "$DO_JS" "$_AGENT_NAME" "$@"
exit $?

View file

@ -1,22 +0,0 @@
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive
# Base packages
RUN apt-get update -y && \
apt-get install -y --no-install-recommends \
curl git ca-certificates build-essential unzip && \
rm -rf /var/lib/apt/lists/*
# ZeroClaw — bootstrap script installs Rust + builds from source
RUN curl --proto '=https' -LsSf \
https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/a117be64fdaa31779204beadf2942c8aef57d0e5/scripts/bootstrap.sh \
| bash -s -- --install-rust --install-system-deps --prefer-prebuilt
# Ensure cargo bin is on PATH for all shells
RUN for rc in /root/.bashrc /root/.zshrc; do \
grep -q '.cargo/bin' "$rc" 2>/dev/null || \
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> "$rc"; \
done
CMD ["/bin/sleep", "inf"]

View file

@ -230,7 +230,7 @@ run_single_agent() {
# Per-agent timeout: run provision/verify/input_test in a subshell with a
# wall-clock timeout. This prevents any single step from hanging indefinitely
# and ensures a result file is always written (pass, fail, or timeout).
# Fixes #2714: sprite-zeroclaw and digitalocean-opencode stalling with no result.
# Fixes #2714: digitalocean-opencode stalling with no result.
# ---------------------------------------------------------------------------
local effective_agent_timeout
effective_agent_timeout=$(get_agent_timeout "${agent}")

View file

@ -5,7 +5,7 @@ set -eo pipefail
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
ALL_AGENTS="claude openclaw zeroclaw codex opencode kilocode hermes junie cursor"
ALL_AGENTS="claude openclaw codex opencode kilocode hermes junie cursor"
PROVISION_TIMEOUT="${PROVISION_TIMEOUT:-720}"
INSTALL_WAIT="${INSTALL_WAIT:-600}"
INPUT_TEST_TIMEOUT="${INPUT_TEST_TIMEOUT:-120}"

View file

@ -275,11 +275,6 @@ CLOUD_ENV
printf 'export OPENAI_BASE_URL=%q\n' "https://openrouter.ai/api/v1"
} >> "${env_tmp}"
;;
zeroclaw)
{
printf 'export ZEROCLAW_PROVIDER=%q\n' "openrouter"
} >> "${env_tmp}"
;;
hermes)
{
printf 'export OPENAI_BASE_URL=%q\n' "https://openrouter.ai/api/v1"
@ -393,10 +388,6 @@ _ensure_agent_binary() {
bin_name="codex"
install_cmd="mkdir -p ~/.npm-global && npm install -g --prefix ~/.npm-global @openai/codex"
;;
zeroclaw)
bin_name="zeroclaw"
install_cmd="curl -LsSf https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/a117be64fdaa31779204beadf2942c8aef57d0e5/scripts/bootstrap.sh | bash -s -- --install-rust --install-system-deps --prefer-prebuilt"
;;
opencode)
bin_name="opencode"
install_cmd="curl -fsSL https://opencode.ai/install | bash"

View file

@ -274,40 +274,6 @@ input_test_openclaw() {
return 1
}
input_test_zeroclaw() {
local app="$1"
_validate_timeout || return 1
log_step "Running input test for zeroclaw..."
# Base64-encode the prompt and stage it to a remote temp file.
# Use -m/--message for non-interactive single-message mode (not -p which is --provider).
local encoded_prompt
encoded_prompt=$(printf '%s' "${INPUT_TEST_PROMPT}" | base64 -w 0 2>/dev/null || printf '%s' "${INPUT_TEST_PROMPT}" | base64 | tr -d '\n')
_validate_base64 "${encoded_prompt}" || return 1
_stage_prompt_remotely "${app}" "${encoded_prompt}"
_stage_timeout_remotely "${app}" "${INPUT_TEST_TIMEOUT}"
local output
# The prompt and timeout are read from staged temp files — no interpolation in this command.
output=$(cloud_exec "${app}" "\
source ~/.spawnrc 2>/dev/null; source ~/.cargo/env 2>/dev/null; \
_TIMEOUT=\$(cat /tmp/.e2e-timeout); \
rm -rf /tmp/e2e-test && mkdir -p /tmp/e2e-test && cd /tmp/e2e-test && git init -q; \
PROMPT=\$(cat /tmp/.e2e-prompt | base64 -d); \
timeout \"\$_TIMEOUT\" zeroclaw agent -m \"\$PROMPT\"" 2>&1) || true
if printf '%s' "${output}" | grep -qx "${INPUT_TEST_MARKER}"; then
log_ok "zeroclaw input test — marker found in response"
return 0
else
log_err "zeroclaw input test — marker '${INPUT_TEST_MARKER}' not found in response"
log_err "Response (last 5 lines):"
printf '%s\n' "${output}" | tail -5 >&2
return 1
fi
}
input_test_opencode() {
log_warn "opencode is TUI-only — skipping input test"
return 0
@ -355,7 +321,6 @@ run_input_test() {
claude) input_test_claude "${app}" ;;
codex) input_test_codex "${app}" ;;
openclaw) input_test_openclaw "${app}" ;;
zeroclaw) input_test_zeroclaw "${app}" ;;
opencode) input_test_opencode ;;
kilocode) input_test_kilocode ;;
hermes) input_test_hermes ;;
@ -557,40 +522,6 @@ _openclaw_verify_gateway_resilience() {
fi
}
verify_zeroclaw() {
local app="$1"
local failures=0
# Binary check (may be in ~/.local/bin or ~/.cargo/bin depending on install method)
log_step "Checking zeroclaw binary..."
if cloud_exec "${app}" "export PATH=\$HOME/.local/bin:\$HOME/.cargo/bin:\$PATH; source ~/.cargo/env 2>/dev/null; command -v zeroclaw" >/dev/null 2>&1; then
log_ok "zeroclaw binary found"
else
log_err "zeroclaw binary not found"
failures=$((failures + 1))
fi
# Env check: ZEROCLAW_PROVIDER
log_step "Checking zeroclaw env (ZEROCLAW_PROVIDER)..."
if cloud_exec "${app}" "grep -q ZEROCLAW_PROVIDER ~/.spawnrc" >/dev/null 2>&1; then
log_ok "ZEROCLAW_PROVIDER present in .spawnrc"
else
log_err "ZEROCLAW_PROVIDER not found in .spawnrc"
failures=$((failures + 1))
fi
# Env check: provider is openrouter
log_step "Checking zeroclaw uses openrouter..."
if cloud_exec "${app}" "grep ZEROCLAW_PROVIDER ~/.spawnrc | grep -q openrouter" >/dev/null 2>&1; then
log_ok "ZEROCLAW_PROVIDER set to openrouter"
else
log_err "ZEROCLAW_PROVIDER not set to openrouter"
failures=$((failures + 1))
fi
return "${failures}"
}
verify_codex() {
local app="$1"
local failures=0
@ -810,7 +741,6 @@ verify_agent() {
case "${agent}" in
claude) verify_claude "${app}" || agent_failures=$? ;;
openclaw) verify_openclaw "${app}" || agent_failures=$? ;;
zeroclaw) verify_zeroclaw "${app}" || agent_failures=$? ;;
codex) verify_codex "${app}" || agent_failures=$? ;;
opencode) verify_opencode "${app}" || agent_failures=$? ;;
kilocode) verify_kilocode "${app}" || agent_failures=$? ;;

View file

@ -18,12 +18,6 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/gcp/claude.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/gcp/openclaw.sh)
```
#### ZeroClaw
```bash
bash <(curl -fsSL https://openrouter.ai/labs/spawn/gcp/zeroclaw.sh)
```
#### Codex CLI
```bash

View file

@ -1,27 +0,0 @@
#!/bin/bash
set -eo pipefail
# Thin shim: ensures bun is available, runs bundled gcp.js (local or from GitHub release)
_ensure_bun() {
if command -v bun &>/dev/null; then return 0; fi
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
export PATH="$HOME/.bun/bin:$PATH"
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
}
_ensure_bun
# SPAWN_CLI_DIR override — force local source (used by e2e tests)
if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/gcp/main.ts" ]]; then
exec bun run "$SPAWN_CLI_DIR/packages/cli/src/gcp/main.ts" zeroclaw "$@"
fi
# Remote — download bundled gcp.js from GitHub release
GCP_JS=$(mktemp)
trap 'rm -f "$GCP_JS"' EXIT
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/gcp-latest/gcp.js" -o "$GCP_JS" \
|| { printf '\033[0;31mFailed to download gcp.js\033[0m\n' >&2; exit 1; }
exec bun run "$GCP_JS" zeroclaw "$@"

View file

@ -16,12 +16,6 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/hetzner/claude.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/hetzner/openclaw.sh)
```
#### ZeroClaw
```bash
bash <(curl -fsSL https://openrouter.ai/labs/spawn/hetzner/zeroclaw.sh)
```
#### Codex CLI
```bash

View file

@ -1,22 +0,0 @@
#!/bin/bash
set -eo pipefail
_ensure_bun() {
if command -v bun &>/dev/null; then return 0; fi
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
export PATH="$HOME/.bun/bin:$PATH"
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
}
_ensure_bun
# SPAWN_CLI_DIR override — force local source (used by e2e tests)
if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/hetzner/main.ts" ]]; then
exec bun run "$SPAWN_CLI_DIR/packages/cli/src/hetzner/main.ts" zeroclaw "$@"
fi
HETZNER_JS=$(mktemp)
trap 'rm -f "$HETZNER_JS"' EXIT
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/hetzner-latest/hetzner.js" -o "$HETZNER_JS" \
|| { printf '\033[0;31mFailed to download hetzner.js\033[0m\n' >&2; exit 1; }
exec bun run "$HETZNER_JS" zeroclaw "$@"

View file

@ -11,7 +11,6 @@ If you have the [spawn CLI](https://github.com/OpenRouterTeam/spawn) installed:
```bash
spawn claude local
spawn openclaw local
spawn zeroclaw local
spawn codex local
spawn opencode local
spawn kilocode local
@ -25,7 +24,6 @@ Or run directly without the CLI:
```bash
bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/claude.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/openclaw.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/zeroclaw.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/codex.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/opencode.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/kilocode.sh)

View file

@ -1,27 +0,0 @@
#!/bin/bash
set -eo pipefail
# Thin shim: ensures bun is available, runs bundled local.js (local or from GitHub release)
_ensure_bun() {
if command -v bun &>/dev/null; then return 0; fi
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
export PATH="$HOME/.bun/bin:$PATH"
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
}
_ensure_bun
# SPAWN_CLI_DIR override — force local source (used by e2e tests)
if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/local/main.ts" ]]; then
exec bun run "$SPAWN_CLI_DIR/packages/cli/src/local/main.ts" zeroclaw "$@"
fi
# Remote — download bundled local.js from GitHub release
LOCAL_JS=$(mktemp)
trap 'rm -f "$LOCAL_JS"' EXIT
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/local-latest/local.js" -o "$LOCAL_JS" \
|| { printf '\033[0;31mFailed to download local.js\033[0m\n' >&2; exit 1; }
exec bun run "$LOCAL_JS" zeroclaw "$@"

View file

@ -16,12 +16,6 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/sprite/claude.sh)
bash <(curl -fsSL https://openrouter.ai/labs/spawn/sprite/openclaw.sh)
```
#### ZeroClaw
```bash
bash <(curl -fsSL https://openrouter.ai/labs/spawn/sprite/zeroclaw.sh)
```
#### Codex CLI
```bash

View file

@ -1,27 +0,0 @@
#!/bin/bash
set -eo pipefail
# Thin shim: ensures bun is available, runs bundled sprite.js (local or from GitHub release)
_ensure_bun() {
if command -v bun &>/dev/null; then return 0; fi
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
export PATH="$HOME/.bun/bin:$PATH"
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
}
_ensure_bun
# SPAWN_CLI_DIR override — force local source (used by e2e tests)
if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/sprite/main.ts" ]]; then
exec bun run "$SPAWN_CLI_DIR/packages/cli/src/sprite/main.ts" zeroclaw "$@"
fi
# Remote — download bundled sprite.js from GitHub release
SPRITE_JS=$(mktemp)
trap 'rm -f "$SPRITE_JS"' EXIT
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/sprite-latest/sprite.js" -o "$SPRITE_JS" \
|| { printf '\033[0;31mFailed to download sprite.js\033[0m\n' >&2; exit 1; }
exec bun run "$SPRITE_JS" zeroclaw "$@"

View file

@ -106,8 +106,8 @@ result=$(make_app_name "openclaw")
assert_match "make_app_name openclaw (aws)" '^e2e-aws-openclaw-[0-9]+$' "${result}"
ACTIVE_CLOUD="sprite"
result=$(make_app_name "zeroclaw")
assert_match "make_app_name zeroclaw (sprite)" '^e2e-sprite-zeroclaw-[0-9]+$' "${result}"
result=$(make_app_name "codex")
assert_match "make_app_name codex (sprite)" '^e2e-sprite-codex-[0-9]+$' "${result}"
# Reset
ACTIVE_CLOUD=""

View file

@ -14,7 +14,7 @@ You have the `spawn` CLI installed. Use it to provision cloud VMs with AI agents
spawn <agent> <cloud> --headless --output json --prompt "task description"
```
**Agents:** claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie
**Agents:** claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie
**Clouds:** hetzner, digitalocean, aws, gcp, sprite
Returns JSON: `{"status":"success","ip_address":"...","ssh_user":"root","server_id":"..."}`

View file

@ -14,7 +14,7 @@ You have the `spawn` CLI installed. Use it to provision cloud VMs with AI agents
spawn <agent> <cloud> --headless --output json --prompt "task description"
```
**Agents:** claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie
**Agents:** claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie
**Clouds:** hetzner, digitalocean, aws, gcp, sprite
Returns JSON: `{"status":"success","ip_address":"...","ssh_user":"root","server_id":"..."}`

View file

@ -4,6 +4,6 @@
You have the `spawn` CLI for creating child cloud VMs with AI agents.
Use `spawn <agent> <cloud> --headless --output json --prompt "task"` to delegate work.
Available agents: claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie.
Available agents: claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie.
Cloud credentials are pre-configured. Run `spawn list --json` to see children.
`--headless` only provisions. To run a prompt on the child: `ssh root@<ip> "bash -lc 'claude -p --dangerously-skip-permissions \"prompt\"'"`. Always use `bash -lc` (binaries are in ~/.local/bin/).

View file

@ -8,7 +8,7 @@ You have the `spawn` CLI installed. Use it to provision cloud VMs with AI agents
spawn <agent> <cloud> --headless --output json --prompt "task description"
```
**Agents:** claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie
**Agents:** claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie
**Clouds:** hetzner, digitalocean, aws, gcp, sprite
Returns JSON: `{"status":"success","ip_address":"...","ssh_user":"root","server_id":"..."}`

View file

@ -8,7 +8,7 @@ You have the `spawn` CLI installed. Use it to provision cloud VMs with AI agents
spawn <agent> <cloud> --headless --output json --prompt "task description"
```
**Agents:** claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie
**Agents:** claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie
**Clouds:** hetzner, digitalocean, aws, gcp, sprite
Returns JSON: `{"status":"success","ip_address":"...","ssh_user":"root","server_id":"..."}`

View file

@ -14,7 +14,7 @@ You have the `spawn` CLI installed. Use it to provision cloud VMs with AI agents
spawn <agent> <cloud> --headless --output json --prompt "task description"
```
**Agents:** claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie
**Agents:** claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie
**Clouds:** hetzner, digitalocean, aws, gcp, sprite
Returns JSON: `{"status":"success","ip_address":"...","ssh_user":"root","server_id":"..."}`

View file

@ -8,7 +8,7 @@ You have the `spawn` CLI installed. Use it to provision cloud VMs with AI agents
spawn <agent> <cloud> --headless --output json --prompt "task description"
```
**Agents:** claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie
**Agents:** claude, codex, cursor, openclaw, opencode, kilocode, hermes, junie
**Clouds:** hetzner, digitalocean, aws, gcp, sprite
Returns JSON: `{"status":"success","ip_address":"...","ssh_user":"root","server_id":"..."}`

View file

@ -1,46 +0,0 @@
# Spawn — Create Child VMs
You have the `spawn` CLI installed. Use it to provision cloud VMs with AI agents.
## Provision a Child VM
```bash
spawn <agent> <cloud> --headless --output json --prompt "task description"
```
**Agents:** claude, codex, cursor, openclaw, zeroclaw, opencode, kilocode, hermes, junie
**Clouds:** hetzner, digitalocean, aws, gcp, sprite
Returns JSON: `{"status":"success","ip_address":"...","ssh_user":"root","server_id":"..."}`
## Running Prompts on Child VMs
`--headless` only provisions — to run work on the child, SSH in with `bash -lc`:
```bash
# Claude Code (use -p, NOT --print or --headless)
ssh -o StrictHostKeyChecking=accept-new root@<ip> "bash -lc 'claude -p --dangerously-skip-permissions \"your prompt\"'"
# Codex CLI
ssh -o StrictHostKeyChecking=accept-new root@<ip> "bash -lc 'codex --quiet \"your prompt\"'"
```
### IMPORTANT — do NOT waste tokens rediscovering these:
- **Always use `bash -lc`** — agent binaries are in `~/.local/bin/` which is only on PATH in login shells
- **Claude uses `-p`** for non-interactive output (NOT `--print`, NOT `--headless`)
- **Add `--dangerously-skip-permissions`** to skip approval prompts on child VMs
- **Never try `which claude` or `find`** to locate binaries — they are always at `~/.local/bin/<agent>`
- **Never create non-root users** to work around permission issues — just use `-p`
## Managing Children
- `spawn list --json` — see running children
- `spawn delete --name <name> --yes` — tear down a child VM (headless)
- `spawn tree` — see the full spawn tree
## Context
- You are running inside a spawned VM (SPAWN_DEPTH is set)
- Cloud credentials are pre-configured — no auth prompts
- OpenRouter billing is shared with the parent