* docs: add spawn delete command to README Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: harden openclaw across all clouds — validation, reliability, performance Fixes multiple issues causing openclaw to break on most clouds: Bugs fixed: - Double-prefixed model ID (openrouter/openrouter/auto) in config generation - AWS gateway starting without env vars (missing .zshrc source) - DigitalOcean sourcing .spawnrc instead of .zshrc for gateway - Destructive rm -rf ~/.openclaw on re-runs (now mkdir -p) Validation added: - API key checked against OpenRouter /auth/key endpoint with re-prompt on failure - Model ID verified against OpenRouter model list with re-prompt loop - openrouter/auto and openrouter/free bypass model check Reliability improvements: - Standardized gateway launch with </dev/null & disown across all 9 clouds - Gateway log auto-displayed on startup timeout for diagnostics - 2GB swap added to cloud-init to prevent OOM on small VMs - Portable install timeout (10 min) with macOS gtimeout fallback Performance: - Reordered spawn_agent: OAuth runs while VM provisions (saves 30-60s) - Fly.io: bumped to 2GB RAM + 2 shared CPUs for openclaw - Fly.io: tries bun first (faster), falls back to npm Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: skip sudo in gh install when running as root (Fly.io containers) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review — skip validation in tests, quote escaped cmd, escape model_id - verify_openrouter_key and verify_openrouter_model skip network calls when SPAWN_SKIP_API_VALIDATION, BUN_ENV=test, or NODE_ENV=test is set - install_agent timeout wrapper now quotes the escaped command for defense in depth - model_id in openclaw JSON now uses json_escape() for consistency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove double-escaping in install_agent that broke shell operators install_agent() was wrapping commands with printf '%q' + bash -c before passing them to the run callback. But run callbacks (run_server, run_sprite, ssh_run_server) already handle escaping for remote transport. The double- escaping turned && || > | into literal characters, causing 'source' to treat the entire command as a single filename. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use local github-auth.sh instead of curling from main When running from a local checkout, base64-encode the local github-auth.sh and send it inline to the remote machine. This ensures fixes (like the sudo skip for root) take effect immediately without waiting for a merge to main. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle github-auth errors gracefully instead of terminating GitHub CLI setup is optional — failures should not abort the spawn session. Guard both run_callback calls in offer_github_auth with || log_warn so the script continues even if gh install fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use GOOGLE_GEMINI_BASE_URL to route Gemini CLI through OpenRouter Gemini CLI ignores OPENAI_BASE_URL — it uses GEMINI_API_KEY to talk directly to Google's API. The OpenRouter key is not a valid Google API key, so all requests fail with "API key not valid". Use GOOGLE_GEMINI_BASE_URL to redirect Gemini CLI to OpenRouter's endpoint. Fixes all 9 cloud gemini scripts + manifest.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: guard optional spawn_agent hooks so failures don't kill the session With set -eo pipefail, any unguarded failure terminates the script. Several optional operations in spawn_agent were unguarded: - agent_configure: config file uploads (agent works with defaults) - agent_save_connection: convenience JSON for spawn list - agent_pre_launch: gateway daemons, startup hooks - agent_pre_provision: pre-provision prompts - .spawnrc shell hooks: hooking env vars into .bashrc/.zshrc These now log warnings and continue instead of aborting. Critical steps (cloud_authenticate, agent_install, cloud_provision) still exit on failure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: audit and fix env vars, escaping, and error handling across all agents Audit findings from 3 parallel agents, fixes applied: **Env vars (4 agents fixed across 9 clouds each = 36 scripts):** - Amazon Q: remove fake OPENAI_* vars (Q uses AWS auth, can't use OpenRouter) - Cline: replace OPENAI_* env vars with `cline auth -p openrouter` command - Open Interpreter: drop OPENAI_* vars, use only OPENROUTER_API_KEY (native support via --model flag) - NanoClaw: add ANTHROPIC_BASE_URL to .env file (was missing, requests went to Anthropic directly) **Escaping:** - execute_agent_non_interactive: replace printf '%q' with single-quote wrapping to avoid double-escaping on Fly.io **Manifest updated** for amazonq, cline, interpreter entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use setsid to detach openclaw gateway daemon from SSH sessions The gateway daemon launch (`nohup openclaw gateway ... & disown`) hangs on all clouds because SSH/exec channels wait for child FDs to close. setsid creates a new session, fully detaching the daemon so the channel can close immediately. Falls back to nohup where setsid is unavailable. Consolidates the daemon launch into a shared start_openclaw_gateway() function used by all 9 cloud scripts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: configure npm global prefix for non-root clouds (AWS, GCP, OVH) AWS Lightsail, GCP, and OVH SSH as non-root users (ubuntu/login user), so `npm install -g` fails with EACCES on /usr/local/lib/node_modules/. Fix: configure npm prefix to ~/.npm-global during cloud-init/setup and add ~/.npm-global/bin to the SSH PATH prefix so agent install commands find globally-installed npm binaries without sudo. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove broken OpenRouter routing from Gemini CLI scripts Gemini CLI uses Google's native API format (/v1beta/models/:streamGenerateContent), not the OpenAI-compatible format (/v1/chat/completions). No base URL override can bridge this — the request formats are fundamentally incompatible. Same situation as Amazon Q (uses vendor-specific auth/API). Removed GEMINI_API_KEY and GOOGLE_GEMINI_BASE_URL from all 9 scripts + manifest. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: auto-install AWS CLI and gcloud SDK when missing Instead of printing manual install instructions and exiting, both CLIs now auto-install: - AWS: downloads official .pkg (macOS) or .zip (Linux) installer - GCP: uses brew cask on macOS, Google's tarball installer on Linux Falls back to manual instructions if auto-install fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: nanoclaw — install Docker on Linux, fix hardcoded /root/ path Two issues broke NanoClaw on all clouds: 1. .env upload hardcoded /root/nanoclaw/.env — fails on non-root clouds (AWS=ubuntu, GCP=user, OVH=ubuntu). Now uses upload_config_file with $HOME which expands on the remote side. 2. NanoClaw requires a container runtime. On Linux it uses Docker, but Docker was never installed. Added Docker install via get.docker.com to all cloud scripts (with sudo where SSH user is non-root). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address security review findings from PR #1463 - Reject symlinked github-auth.sh before base64-encoding (falls back to remote URL) - Hide API key from process list using curl -K - instead of -H in verify_openrouter_key Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: quote OPENROUTER_API_KEY in cline auth to prevent command injection Unquoted variable in `cline auth -p openrouter -k ${OPENROUTER_API_KEY}` allows shell metacharacters in the key to execute arbitrary commands on the remote server. Wrapping in escaped double quotes prevents expansion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .claude | ||
| .githooks | ||
| .github | ||
| aws | ||
| cli | ||
| daytona | ||
| digitalocean | ||
| fly | ||
| gcp | ||
| hetzner | ||
| local | ||
| ovh | ||
| shared | ||
| sprite | ||
| test | ||
| .gitignore | ||
| .shellcheckrc | ||
| CLAUDE.md | ||
| LICENSE | ||
| manifest.json | ||
| README.md | ||
Spawn
Launch any AI agent on any cloud with a single command. Coding agents, research agents, self-hosted AI tools — Spawn deploys them all. All models powered by OpenRouter. (ALPHA software, use at your own risk!)
15 agents. 10 clouds. 149 working combinations. Zero config.
Install
curl -fsSL https://openrouter.ai/labs/spawn/cli/install.sh | bash
Or install directly from GitHub:
curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/cli/install.sh | bash
Usage
spawn # Interactive picker
spawn <agent> <cloud> # Launch directly
spawn matrix # Show the full agent x cloud matrix
Examples
spawn # Interactive picker
spawn claude sprite # Claude Code on Sprite
spawn aider hetzner # Aider on Hetzner
spawn claude sprite --prompt "Fix bugs" # Non-interactive with prompt
spawn aider sprite -p "Add tests" # Short form
spawn claude # Show clouds available for Claude
spawn delete # Delete a running server
spawn delete -c hetzner # Delete a server on Hetzner
Commands
| Command | Description |
|---|---|
spawn |
Interactive agent + cloud picker |
spawn <agent> <cloud> |
Launch agent on cloud directly |
spawn <agent> <cloud> --dry-run |
Preview without provisioning |
spawn <agent> <cloud> -p "text" |
Non-interactive with prompt |
spawn <agent> <cloud> --prompt-file f.txt |
Prompt from file |
spawn <agent> <cloud> --debug |
Show all commands being executed |
spawn <agent> |
Show available clouds for an agent |
spawn <cloud> |
Show available agents for a cloud |
spawn matrix |
Full agent x cloud matrix |
spawn list |
Browse and rerun previous spawns |
spawn list <filter> |
Filter history by agent or cloud name |
spawn list -a <agent> |
Filter history by agent |
spawn list -c <cloud> |
Filter history by cloud |
spawn list --clear |
Clear all spawn history |
spawn last |
Instantly rerun the most recent spawn |
spawn agents |
List all agents with descriptions |
spawn clouds |
List all cloud providers |
spawn update |
Check for CLI updates |
spawn delete |
Interactively select and destroy a cloud server |
spawn delete -a <agent> |
Filter servers to delete by agent |
spawn delete -c <cloud> |
Filter servers to delete by cloud |
spawn help |
Show help message |
spawn version |
Show version |
Without the CLI
Every combination works as a one-liner — no install required:
bash <(curl -fsSL https://openrouter.ai/labs/spawn/{cloud}/{agent}.sh)
Non-Interactive Mode
Skip prompts by providing environment variables:
# OpenRouter API key (required for all agents)
export OPENROUTER_API_KEY=sk-or-v1-xxxxx
# Cloud-specific credentials (varies by provider)
# Note: Sprite uses `sprite login` for authentication
export HCLOUD_TOKEN=... # For Hetzner
export DO_API_TOKEN=... # For DigitalOcean
# Run non-interactively
spawn claude hetzner
You can also use inline environment variables:
OPENROUTER_API_KEY=sk-or-v1-xxxxx spawn claude sprite
Get your OpenRouter API key at: https://openrouter.ai/settings/keys
For cloud-specific auth, see each cloud's README in this repository.
Troubleshooting
Installation issues
If spawn fails to install, try these steps:
-
Check bun version: spawn requires bun >= 1.2.0
bun --version bun upgrade # if needed -
Manual installation: If auto-install fails, install bun first
curl -fsSL https://bun.sh/install | bash source ~/.bashrc # or ~/.zshrc for zsh curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/cli/install.sh | bash -
PATH issues: If
spawncommand not found after install# Add to your shell config (~/.bashrc or ~/.zshrc) export PATH="$HOME/.local/bin:$PATH"
Agent launch failures
If an agent fails to install or launch on a cloud:
-
Check credentials: Ensure cloud provider credentials are set
# Example for Hetzner export HCLOUD_TOKEN=your-token-here spawn claude hetzner -
Try a different cloud: Some clouds may have temporary issues
spawn <agent> # Interactive picker to choose another cloud -
Use --dry-run: Preview what spawn will do before provisioning
spawn claude hetzner --dry-run -
Check cloud status: Visit your cloud provider's status page
- Many failures are transient (network timeouts, package mirror issues)
- Retrying often succeeds
Getting help
- View command history:
spawn listshows all previous launches - Rerun last session:
spawn lastorspawn rerun - Check version:
spawn versionshows CLI version and cache status - Update spawn:
spawn updatechecks for the latest version - Report bugs: Open an issue at https://github.com/OpenRouterTeam/spawn/issues
Matrix
| Local Machine | Hetzner Cloud | OVHcloud | Fly.io | AWS Lightsail | Daytona | DigitalOcean | GCP Compute Engine | Sprite | |
|---|---|---|---|---|---|---|---|---|---|
| Claude Code | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| OpenClaw | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| NanoClaw | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Aider | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Goose | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Codex CLI | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Open Interpreter | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Gemini CLI | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Amazon Q CLI | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Cline | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| gptme | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| OpenCode | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Plandex | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Kilo Code | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Continue | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
How it works
Each cell in the matrix is a self-contained bash script that:
- Provisions a server on the cloud provider
- Installs the agent
- Injects your OpenRouter API key so every agent uses the same billing
- Drops you into an interactive session
Scripts work standalone (bash <(curl ...)) or through the CLI.
Development
git clone https://github.com/OpenRouterTeam/spawn.git
cd spawn
git config core.hooksPath .githooks
Structure
{cloud}/lib/common.sh # Cloud provider primitives (provision, SSH, cleanup)
{cloud}/{agent}.sh # Agent deployment script
shared/common.sh # Shared utilities (OAuth, logging, SSH helpers)
cli/ # TypeScript CLI (bun)
manifest.json # Source of truth for the matrix
Adding a new cloud
- Create
{cloud}/lib/common.shwith provisioning primitives - Add to
manifest.json - Implement agent scripts using the cloud's primitives
- See CLAUDE.md for full contributor guide
Adding a new agent
- Add to
manifest.json - Implement on 1+ cloud by adapting an existing agent script
- Must support OpenRouter via env var injection
Contributing
The easiest way to contribute is by testing and reporting issues. You don't need to write code.
Test a cloud provider
Pick any agent + cloud combination from the matrix and try it out:
spawn claude hetzner # or any combination
If something breaks, hangs, or behaves unexpectedly, open an issue using the bug report template. Include:
- The exact command you ran
- The cloud provider and agent
- What happened vs. what you expected
- Any error output
Request a cloud or agent
Want to see a specific cloud provider or agent supported? Use the dedicated templates:
Requests with real-world use cases get prioritized.
Report auth or credential issues
Cloud provider APIs change frequently. If you hit authentication failures, expired tokens, or permission errors on a provider that previously worked, please report it — these are high-priority fixes.
Code contributions
See CLAUDE.md for the full contributor guide covering shell script rules, testing, and the shared library pattern.