$ show OpenRouterLabs/spawn
Spawn is a multi-cloud VM provisioning CLI that deploys AI coding agents (Claude Code, Codex, OpenClaw, etc.) onto cloud VMs (Hetzner, DigitalOcean, AWS Lightsail, GCP, Daytona, Sprite) with a single command. It handles the full lifecycle: interactive agent/cloud selection via fuzzy matching, VM provisioning, SSH key injection, agent installation (npm binaries, pre-built tarballs, or Docker sidecars), environment configuration (OpenRouter API key, MCP servers via skill files), and interactive session handoff. The system supports headless mode, recursive spawning (agent launching sub-agents), auto-update systemd services, and a security scan cron. It is distributed as per-cloud JS bundles (Bun.build, minified) with curl|bash wrappers that download from GitHub Releases, and a Windows PowerShell installer. Architecturally, Spawn is a modular monolith: a shared orchestration pipeline (`shared/orchestrate.ts`) drives cloud-specific providers implementing a common `CloudRunner`/`CloudOrchestrator` interface (authenticate → promptSize → createServer → waitForReady → install → configure → launch → interactiveSession). Agents and clouds are defined in a `manifest.json` that validates compatibility via runtime type contracts. A shared `Result` monad (`@openrouter/spawn-shared`) enables functional error handling with retry semantics across SSH calls and cloud API interactions. The codebase is well-tested (~100+ test files) with fs-isolated sandboxes, mocked cloud prompts, and integration tests against real cloud providers via an E2E harness. The codebase demonstrates strong engineering practices: schema-validated input parsing (valibot), security-first validation (allowlists for every user-controlled input: instance names, IPs, SSH usernames, shell commands, remote paths), and atomic file operations with corruption recovery for history persistence. However, there is significant duplication across cloud providers (near-identical SSH execution patterns in each cloud module), large monolithic files (orchestrate.ts ~1000+ lines, agent-setup.ts ~2000 lines), and manual drift risk from hardcoded API slugs (DigitalOcean marketplace images, Hetzner server types, GCP machine types) and embedded OAuth client secrets (documented as acceptable public-client pattern).
The LLM identified 6 files worth highlighting for reuse:
Result monad (Ok/Err) with tryCatch, asyncTryCatch, tryCatchIf, and withRetry utilities. Isolates error handling from business logic, distinguishes retryable errors (network timeouts, SSH failures) from non-retryable ones. Extractable and reusable in any TypeScript project needing resilient error handling.
Centralized security validation using strict allowlist regex patterns for identifiers, IP addresses, SSH usernames, server IDs, launch commands, and shell scripts. Rejects null bytes, control characters, and path traversal. The `stripDangerousKeys` function prevents prototype pollution. Extractable as a standalone validation library.
Portable path resolution utilities (getSpawnDir, getCacheDir, getHistoryPath, getUserHome) with security checks: SPAWN_HOME must be absolute and within home directory, cache directories are validated and created with proper permissions. Extractable for any CLI tool needing secure path management.
JSON file-based history with schema versioning, atomic writes (tmp+rename), mkdir-based advisory file locking with stale PID recovery, and corruption recovery (automatic backup, archive recovery, per-record salvaging). Handles concurrent access and partial writes. Extractable as a general-purpose state persistence pattern.
SSH utility module with two-phase connection wait (TCP probe then SSH handshake), killWithTimeout (per-process timeout preventing hangs), pipe-draining to avoid buffer deadlocks, validateRemotePath for safe file paths, and consistent SSH option sets (SSH_BASE_OPTS, SSH_INTERACTIVE_OPTS). Extractable for any TypeScript project managing SSH connections.
Minimal, well-typed TypeScript type guard utilities (isString, isObject, isError) and valibot-based schema validation helpers (parseJsonWith, parseJsonObj). Used consistently across the entire codebase to replace unsafe casts with validated parsing. Extractable as a lightweight validation toolkit.