spawn/CLAUDE.md
L 6ac59e6bb3
Fix OAuth server for macOS bash 3.x (#24)
Three issues broke the OAuth callback server on macOS:

1. echo -e doesn't work in bash 3.x — \r\n appears as literal text
   in the HTTP response, browser gets malformed headers.
   Fix: pre-write response with printf to a file before the subshell.

2. local variables inside ( ... ) & subshell — undefined behavior in
   bash 3.x since subshells aren't function scope.
   Fix: use plain variables in subshells.

3. ((elapsed++)) when elapsed=0 evaluates to falsy — set -e kills
   the script on the first iteration of the timeout loop.
   Fix: use elapsed=$((elapsed + 1)) instead.

Also simplified nc_listen detection to only check for BusyBox
(the -p flag check could misfire on macOS nc).

Applied to all 10 lib/common.sh files.

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 14:21:47 -08:00

167 lines
7.3 KiB
Markdown

# Spawn
Spawn is a matrix of **agents x clouds**. Every script provisions a cloud server, installs an agent, injects OpenRouter credentials, and drops the user into an interactive session.
## The Matrix
`manifest.json` is the source of truth. It tracks:
- **agents** — coding agents / AI tools (Claude Code, OpenClaw, NanoClaw, ...)
- **clouds** — cloud providers to run them on (Sprite, Hetzner, ...)
- **matrix** — which `cloud/agent` combinations are `"implemented"` vs `"missing"`
## How to Improve Spawn
When run via `./improve.sh`, your job is to pick ONE of these tasks and execute it:
### 1. Fill a missing matrix entry
Look at `manifest.json``matrix` for any `"missing"` entry. To implement it:
- Find the **cloud's** `lib/common.sh` — it has all the provider-specific primitives (create server, run command, upload file, interactive session)
- Find the **agent's** existing script on another cloud — it shows the install steps, config files, env vars, and launch command
- Combine them: use the cloud's primitives to execute the agent's setup steps
- The script goes at `{cloud}/{agent}.sh`
**Pattern for every script:**
```
1. Source {cloud}/lib/common.sh (local or remote fallback)
2. Authenticate with cloud provider
3. Provision server/VM
4. Wait for readiness
5. Install the agent
6. Get OpenRouter API key (env var or OAuth)
7. Inject env vars into shell config
8. Write agent-specific config files
9. Launch interactive session
```
**OpenRouter injection is mandatory.** Every agent script MUST:
- Set `OPENROUTER_API_KEY` in the shell environment
- Set provider-specific env vars (e.g., `ANTHROPIC_BASE_URL=https://openrouter.ai/api`)
- These come from the agent's `env` field in `manifest.json`
### 2. Add a new agent
Research coding agents, AI CLI tools, or AI-powered dev tools. To add one:
1. Add an entry to `manifest.json``agents` with: name, description, url, install command, launch command, and env vars needed for OpenRouter
2. Add `"missing"` entries to the matrix for every existing cloud
3. Implement the script for at least one cloud
4. Update `README.md`
**Where to find new agents:**
- GitHub trending in AI/coding categories
- OpenRouter's ecosystem
- HuggingFace agent frameworks
- CLI tools that accept `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` (these work with OpenRouter via base URL override)
### 3. Add a new cloud provider
Research cloud providers with API-based provisioning. To add one:
1. Create `{cloud}/lib/common.sh` with the provider's primitives:
- Auth/token management (env var → config file → prompt)
- Server creation (API call or CLI)
- SSH/exec connectivity
- File upload
- Interactive session
- Server destruction
2. Add an entry to `manifest.json``clouds`
3. Add `"missing"` entries to the matrix for every existing agent
4. Implement at least one agent script
5. Update `README.md`
**Good candidate clouds** have:
- REST API or simple CLI for provisioning
- SSH access to the created server
- Cloud-init or similar userdata support
- Pay-per-hour pricing (so users can destroy after use)
### 4. Extend tests
`test/run.sh` contains the test harness. When adding a new cloud or agent:
- Add mock functions for the cloud's CLI/API calls
- Add per-script assertions matching the agent's setup steps
- Run `bash test/run.sh` to verify
## File Structure Convention
```
spawn/
cli/
spawn.sh # Main CLI binary (interactive picker, matrix viewer, launcher)
install.sh # One-liner installer (downloads spawn.sh to ~/.local/bin)
{cloud}/
lib/common.sh # Cloud-specific shared functions
{agent}.sh # One script per agent
manifest.json # The matrix (source of truth)
improve.sh # Run this to trigger one improvement cycle
test/run.sh # Test harness
README.md # User-facing docs
```
## CLI (`cli/`)
The `spawn` CLI is a pure-bash binary that provides a unified entry point for the matrix.
- **`cli/spawn.sh`** — Main binary. Fetches manifest.json from GitHub (cached for 1hr at `~/.cache/spawn/`), parses it with `jq` or `python3` fallback, and provides: interactive picker (`spawn`), direct launch (`spawn <agent> <cloud>`), agent info (`spawn <agent>`), matrix table (`spawn list`), and self-update (`spawn update`). Installed to `~/.local/bin/spawn`.
- **`cli/install.sh`** — One-liner installer. Downloads `spawn.sh` to `~/.local/bin/spawn`, sets executable bit, and prints PATH instructions if needed. Override install dir with `SPAWN_INSTALL_DIR` env var.
## Script Conventions
- `#!/bin/bash` + `set -e`
- Source `lib/common.sh` with local-first, remote-fallback pattern
- Use `OPENROUTER_API_KEY` env var to skip OAuth when set
- All env vars documented in README.md under the relevant section
- Remote fallback URL: `https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/{path}`
- Scripts must be runnable via: `bash <(curl -fsSL https://openrouter.ai/lab/spawn/{cloud}/{agent}.sh)`
## After Each Change
1. Update `manifest.json` matrix status to `"implemented"`
2. Update `README.md` with usage instructions
3. Run `bash -n {file}` to syntax-check your scripts
4. Commit with a descriptive message
## Agent Team Roles
When running as part of an agent team (`./improve.sh`), teammates are assigned specific roles:
### Gap Filler
You're assigned a specific `{cloud}/{agent}` entry to implement. Steps:
1. Read `{cloud}/lib/common.sh` — understand the cloud's primitives
2. Read an existing `{agent}.sh` on another cloud — understand the install steps
3. Write `{cloud}/{agent}.sh` combining the two
4. Update `manifest.json` matrix entry to `"implemented"`
5. Add usage entry to `README.md` under the cloud's section
6. `bash -n` syntax check
7. Commit your changes only (don't touch other teammates' files)
### Agent Scout
Research and add ONE new AI coding agent. Requirements:
- Must be installable via a single command (`npm install -g`, `pip install`, `curl | bash`, etc.)
- Must accept API keys via environment variables (`OPENAI_API_KEY`, `OPENROUTER_API_KEY`, `ANTHROPIC_API_KEY`, etc.)
- OpenRouter compatibility: either native `OPENROUTER_API_KEY` support, or `OPENAI_BASE_URL=https://openrouter.ai/api/v1` override
- Add to `manifest.json``agents` with full metadata including `env` field
- Add `"missing"` entries in the matrix for ALL existing clouds
- Implement on at least 2 clouds to prove the pattern
- Update `README.md`
### Cloud Scout
Research and add ONE new cloud provider. Requirements:
- REST API or CLI for provisioning VMs/instances
- SSH access to created servers
- Cloud-init, userdata, or startup-script support
- Pay-per-hour pricing
- Create `{cloud}/lib/common.sh` with ALL primitives (see existing clouds for the pattern)
- Add to `manifest.json``clouds`
- Add `"missing"` entries for ALL existing agents
- Implement at least 2 agents to prove the lib works
- Update `README.md`
### Coordination Rules
- **Never edit the same file as another teammate** — coordinate via the shared task list
- **manifest.json conflicts**: only ONE teammate should update manifest.json at a time. If you're a Gap Filler, update just your entry. If you're a Scout, add your block and your matrix entries.
- **README.md**: append your section, don't rewrite others' sections
- **Commit early**: commit your work as soon as it's done so other teammates can see it
- **Self-claim**: when you finish your assigned task, check the task list for the next unblocked item