Implements Goose agent on Render with:
- Direct install via Block's download_cli.sh script
- Native OpenRouter support via GOOSE_PROVIDER env var
- Simple environment configuration
- Interactive session launch
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement railway/cline.sh following the standard Railway pattern:
- Install cline via npm
- Inject OPENAI_API_KEY and OPENAI_BASE_URL for OpenRouter compatibility
- Launch with 'cline' command
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create render/gemini.sh with Render CLI provisioning
- Install Gemini CLI via npm on Render service
- Inject OpenRouter credentials via OPENAI_BASE_URL and GEMINI_API_KEY
- Update manifest.json matrix entry to "implemented"
- Update render/README.md with Gemini usage instructions
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements NanoClaw agent on Render with:
- Node.js and tsx installation
- Clone and build nanoclaw from GitHub
- OpenRouter integration via .env file
- WhatsApp QR code authentication flow
- Interactive dev mode session
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create render/interpreter.sh with Render CLI provisioning
- Install Open Interpreter via pip on Render service
- Inject OpenRouter credentials via OPENAI_BASE_URL override
- Update manifest.json matrix entry to "implemented"
- Update render/README.md with interpreter usage instructions
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement railway/amazonq.sh following the standard Railway pattern:
- Install Amazon Q CLI via AWS installer script
- Inject OPENAI_API_KEY and OPENAI_BASE_URL for OpenRouter compatibility
- Launch with 'q chat' command
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements OpenClaw agent on Render with:
- Bun installation for openclaw
- Multi-channel gateway in background
- Interactive TUI session
- OpenRouter integration with model selection
- Config file generation with API key and model
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Validate SPAWN_ISSUE is a positive integer in both trigger-server.ts
and refactor.sh to prevent command injection via crafted issue params
- Use Python json.dumps for Render _render_create_service JSON body
instead of string interpolation (prevents JSON injection)
- Remove erroneous "api_key" 6th argument in Hyperstack generic_cloud_api
call that was being interpreted as max_retries, breaking all API calls
Agent: security-auditor
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement railway/gemini.sh following the standard Railway pattern:
- Install @google/gemini-cli via npm
- Inject GEMINI_API_KEY, OPENAI_API_KEY, and OPENAI_BASE_URL
- Launch with OpenRouter compatibility
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Tests the actual CLI entry point through subprocess execution:
- help/version command routing (help, --help, -h, version, --version, -v, -V)
- Subcommand --help routing (list --help, agents --help, clouds --help, etc.)
- ls alias routing
- Non-TTY mode behavior (shows help when no args)
- handleError formatting for invalid identifiers
- extractFlagValue in actual CLI (--prompt/--prompt-file missing values)
- --prompt and --prompt-file mutual exclusion
- --prompt-file with nonexistent file
- Prompt-only-without-cloud error messaging
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, `spawn hetzne` (typo of cloud name "hetzner") would show
"Unknown agent: hetzne" with no useful suggestion, because it only
searched agent names for typo matches. Now when a single argument
matches neither an agent nor a cloud, the error searches both pools
and shows "Did you mean hetzner (cloud)?" — guiding the user to the
right command.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix railway/gptme.sh calling nonexistent inject_env_vars_railway (should be inject_env_vars)
- Railway README: add missing NanoClaw and gptme agent entries
- Northflank README: add missing NanoClaw, Goose, Codex CLI, Open Interpreter, Gemini CLI agent entries and Environment Variables table
- Hyperstack README: restructure to match standard format with Agents section, use openrouter.ai/lab/spawn URLs instead of raw GitHub URLs, add 6 missing agents (Amazon Q, Cline, gptme, OpenCode, Plandex, Kilo Code), add Environment Variables table and Non-Interactive Mode section
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Railway's inject_env_vars passed user-controlled values (e.g. OPENROUTER_API_KEY)
through bash -c without proper escaping, allowing shell injection. Replace with
the safe file-based pattern used by other providers (write to temp file, upload,
append to .bashrc).
Also add remote_path validation to Railway and Modal upload_file functions to
prevent single-quote breakout injection, matching the pattern already used by
Koyeb. Fix gptme.sh reference to non-existent inject_env_vars_railway function.
Agent: security-auditor
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement Render cloud integration with CLI and API support:
- render/lib/common.sh with provider primitives (auth, provision, SSH, upload)
- render/claude.sh for Claude Code deployment
- render/aider.sh for Aider deployment
- Updated manifest.json with Render cloud and 14 matrix entries
- Created README.md with usage documentation
Render offers a developer-first platform with free tier, Docker support,
and SSH access via render CLI. All scripts support OpenRouter integration.
Agent: cloud-scout-1
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
- Fix railway/gptme.sh calling nonexistent inject_env_vars_railway (should be inject_env_vars)
- Fix northflank claude/openclaw/aider using inject_env_vars_local (only writes .zshrc)
instead of inject_env_vars_northflank (writes both .bashrc and .zshrc)
- Update Railway README to list NanoClaw and gptme agents
- Update Northflank README to list all 8 implemented agents and add env var table
- Mark railway/gptme as implemented in manifest.json
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cmdCloudInfo was the only major command function with zero test coverage.
Tests cover happy paths, cloud notes display, empty agents state, error
paths (invalid identifiers, unknown clouds), and typo suggestions.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Railway: Missing base64 -w0 caused newline injection; unescaped remote_path
in single quotes allowed single-quote breakout command injection. Now uses
base64 -w0 with macOS fallback, printf '%q' for path escaping, and routes
through run_server instead of direct railway run bash -c.
Modal: Remote path was embedded in single quotes without escaping, allowing
single-quote breakout. Now uses printf '%q' for safe path escaping.
Koyeb: Used fragile deny-list validation for remote_path (rejecting specific
characters) and base64 without -w0 flag. Replaced with printf '%q' escaping
and added base64 -w0 with macOS fallback.
Agent: security-auditor
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The timeout wrapper was killing cycles prematurely. The trigger
server's RUN_TIMEOUT_MS (75 min) is the safety net if something
truly hangs — no need for a second timeout layer in the script.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract helper functions from the two largest create_server functions:
Linode (99 lines -> 30-line orchestrator):
- _linode_fetch_ssh_keys: fetch authorized SSH public keys
- _linode_build_create_payload: build userdata, root password, request body
- _linode_wait_for_active: poll until instance is running
GenesisCloud (92 lines -> 28-line orchestrator):
- _genesis_fetch_ssh_key_ids: fetch SSH key IDs
- _genesis_build_create_payload: build userdata and request body
- _genesis_wait_for_active: poll until instance is active
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bun build command was failing on proot-distro ubuntu because it couldn't
resolve node_modules dependencies. Added --packages bundle flag to explicitly
bundle all dependencies into the output file.
Fixes#209
Agent: issue-responder
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
json_escape() returns a fully-quoted JSON string (e.g. "value") via
Python's json.dumps(). Callers using printf templates were wrapping
the result in additional quotes ("%s"), producing invalid JSON like
""value"". Remove the redundant quotes from all printf format strings
so json_escape's quotes are used directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the broken keep-alive ping loop with a fundamentally better
approach: the trigger server now streams the script's stdout/stderr
back as the HTTP response body in chunks. The GH Action holds the
curl connection open for the entire cycle duration (~90 min timeout).
This works because Sprite keeps VMs alive while "actively servicing
HTTP requests." A single long-lived streaming response satisfies
this naturally — no synthetic pings needed.
Key changes:
trigger-server.ts:
- /trigger now returns a streaming text/plain Response
- stdout/stderr piped through ReadableStream with chunked output
- 30s heartbeat lines injected during silent periods
- Client disconnect handled gracefully (process keeps running)
- X-Accel-Buffering: no header to prevent proxy buffering
discovery.yml / refactor.yml:
- curl -sSN --fail-with-body streams output in real-time
- timeout-minutes: 90 to hold the connection for full cycles
- Error responses (429/409/401) still print body and exit cleanly
discovery.sh / refactor.sh:
- Removed all keep-alive logic (start_keepalive/stop_keepalive)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Localhost pings (curl http://localhost:8080/health) bypass the Sprite
proxy entirely and don't register as "actively servicing HTTP requests."
Per Sprite lifecycle rules, VMs pause when there's no inbound HTTP
through the proxy and no detachable session output — so the old
keep-alive was doing nothing.
Now both discovery.sh and refactor.sh resolve the Sprite's public URL
via `sprite-env info` and ping that instead. The request routes through
the Sprite proxy, which counts as real activity and prevents pause.
Also adds keep-alive to discovery.sh (previously had none at all).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The discovery loop was missing stale PR resolution — unlike refactor.sh,
it relied entirely on the Branch Cleaner teammate to handle open PRs
during the cycle. If the cycle timed out or the teammate failed, PRs
would leak across cycles indefinitely.
Now the team lead checks for open provider PRs at three points:
1. Pre-cycle: merge or close stale PRs (>2h old) before launching agents
2. Shutdown: explicit sweep for provider-related PRs before exiting
3. Between cycles: catch anything missed by the shutdown sequence
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>