Commit graph

69 commits

Author SHA1 Message Date
A
7609cf2d6f
refactor: reduce complexity in OAuth and Hetzner validation functions (#1132)
Extract helper functions to simplify complex control flow:
- try_oauth_flow: Extract _start_oauth_session_with_server helper to handle server startup phase, improving readability and testability
- _hetzner_resolve_server_type: Extract _hetzner_log_validation_error and _hetzner_log_type_change helpers to separate error handling logic from main flow

These changes reduce nesting levels and improve function cohesion while maintaining identical behavior.

Agent: complexity-hunter

Co-authored-by: spawn-refactor-bot <refactor@openrouter.ai>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-14 17:43:05 -05:00
A
f121b60d80
fix(ux): show post-session summary with server status and reconnect info (#1037)
After an interactive SSH session ends, users are now shown:
- A warning that their server is still running (and may incur charges)
- A link to the cloud provider's dashboard to manage/delete it
- The SSH command to reconnect

This prevents users from unknowingly leaving servers running after
exiting their agent session. Covers all 25 SSH-based cloud providers.

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>
2026-02-13 20:06:40 -05:00
A
f586e19790
fix(security): replace unquoted heredocs with printf to prevent shell expansion in API keys (#1031)
Unquoted `<< EOF` heredocs in nanoclaw .env file creation cause shell
expansion of the API key value. If an API key contains `$`, backticks,
or `\`, the value is silently corrupted or could trigger command
execution. Replace with `printf '%s'` which safely writes the value
without interpretation.

Also fix unquoted variable expansion in upload_config_file's mv command
and the github-codespaces/openclaw.sh config heredoc.

Fixes 34 scripts across all cloud providers.

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>
2026-02-13 19:41:10 -05:00
A
0f60a2b082
fix: add actionable guidance to agent installation failures across 126 scripts (#966)
Add log_install_failed helper to shared/common.sh that provides
structured troubleshooting for agent install failures: possible causes,
SSH debug command (when server IP available), manual install command,
and re-run suggestion. Also improve SSH key registration error message.

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>
2026-02-13 10:14:03 -08:00
A
5ebe3e5a13
fix: add actionable guidance to destroy_server failures and service timeouts (#959)
When server destruction fails, users are left with a bare error message and
no indication that they may still be billed for a running server. This adds
dashboard URLs and clear warnings to destroy_server errors across 9 clouds
(Hetzner, UpCloud, Contabo, Netcup, RamNode, Hostinger, HOSTKEY, OVH,
Latitude). Also improves error messages for Koyeb (app creation, service
deployment, deployment timeout, instance ID), GitHub Codespaces (creation
failure, readiness timeout), and E2B (sandbox creation failure).

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>
2026-02-13 09:38:58 -08:00
A
3c3c697ea5
fix: json_escape SSH key names and fix GCP metadata injection (#958)
SSH key registration in 11 cloud providers used unescaped key_name
directly in JSON request bodies. If the hostname (used to generate
key names) contained JSON-special characters like double-quotes, it
could break out of the JSON string and inject arbitrary JSON fields.

Fix: use json_escape for key_name in all providers, matching the
pattern already used by Scaleway.

Also fix GCP create_server which embedded the startup script inline
in --metadata with comma delimiters. Commas in the script could break
metadata parsing or inject additional metadata keys. Fix: use
--metadata-from-file for the startup script.

Affected providers: Hetzner, DigitalOcean, Vultr, BinaryLane,
Hostinger, Contabo, Cherry, HOSTKEY, Civo, Linode, Genesis Cloud, GCP.

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>
2026-02-13 09:03:35 -08:00
A
51d217add1
refactor: deduplicate _ensure_jq and decompose DO create_server (#943)
- Extract `ensure_jq()` from hetzner and hostkey into shared/common.sh,
  eliminating 64 lines of identical duplicated code
- Decompose DigitalOcean `create_server()` by extracting error handling
  into `_do_check_create_error()` helper, and using the shared
  `extract_api_error_message` instead of inline Python parsing
- Use shared `_extract_json_field` for droplet ID extraction

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>
2026-02-13 07:25:08 -08:00
A
de5a0c16de
refactor: extract helpers from Hetzner server type validation (#845)
Break down _validate_server_type_for_location (74 lines -> 29 lines) and
create_server (64 lines -> 43 lines) by extracting focused helpers:

- _hetzner_get_available_ids: fetch datacenter availability data
- _hetzner_find_fallback_type: search for compatible alternative types
- _hetzner_resolve_server_type: handle validation errors and fallback logging

Agent: complexity-hunter

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 02:14:30 -08:00
A
a96b310861
refactor: reduce complexity in Hetzner _validate_server_type_for_location (#831)
- Extract _hetzner_find_candidates helper to eliminate duplicated jq
  candidate-search logic (same-family and any-family searches were
  nearly identical 15-line blocks)
- Consolidate 3 separate jq calls for wanted_cpu/cores/memory into
  a single jq invocation
- Replace duplicated replacement-picking code with a loop over
  family strategies

Function reduced from 106 to ~72 lines (plus 17-line reusable helper).

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>
2026-02-12 23:52:17 -08:00
L
6633873ccc
refactor: replace Python with jq in Hetzner lib, fix /lab → /labs URLs (#827)
Hetzner lib: replace all Python JSON parsing with jq. Uses the
/datacenters API as the authoritative source for server type
availability (server_types.available), cross-referenced with
/server_types for specs and pricing. jq is auto-installed if missing.

URLs: update openrouter.ai/lab/spawn → openrouter.ai/labs/spawn
across all READMEs and CLI source.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 23:14:11 -08:00
L
720753c644
fix: validate Hetzner server type availability at selected location (#825)
When HETZNER_SERVER_TYPE and HETZNER_LOCATION are both pre-set (e.g. by
automated scripts), there was no validation that the type is actually
available at that location. ARM types like cax11 are only available in
EU datacenters, causing "unsupported location for server type" errors
when paired with US locations like ash.

Now create_server validates the type against the Hetzner /datacenters
API (server_types.available) before attempting creation. If incompatible,
it auto-selects the cheapest compatible alternative (same CPU family,
>= specs) and warns the user.

Uses jq for JSON parsing (auto-installed if missing) and the Hetzner
/datacenters + /server_types APIs for authoritative availability data.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 23:01:37 -08:00
A
fde0ed16b6
refactor: extract shared extract_api_error_message helper to reduce inline Python duplication (#767)
Replace 10 inline `python3 -c "import json,sys; d=json.loads(...)..."` one-liners
across vultr, hetzner, digitalocean, and contabo with calls to a new shared
`extract_api_error_message` helper in shared/common.sh. The helper tries common
JSON error field patterns (message, error, error.message, error.error_message,
reason) and falls back to a caller-specified default.

This pattern appears 35+ times across cloud libs; this PR converts the first 4
clouds as a proof of concept. Remaining clouds can adopt incrementally.

Net reduction: 10 lines per converted cloud (~3 lines saved per call site).

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>
2026-02-12 16:47:20 -08:00
A
4b0d25ca39
fix: prevent Python code injection via unescaped variables in inline Python (#771)
Use sys.argv to pass shell values to inline Python instead of direct
string interpolation, preventing single-quote injection attacks across
cloud lib common.sh files and test/record.sh.

Also fix eval injection in test/record.sh try_load_config() by replacing
eval of Python-generated export statements with safe tab-separated
parsing and direct variable assignment.

Fixes #759
Fixes #760

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>
2026-02-12 16:47:13 -08:00
A
6c7ced54dd
fix: replace log_warn with log_step/log_info for non-warning messages (#604)
Agent: ux-engineer

Many shell scripts misused log_warn (yellow) for normal progress/status
messages, making routine operations appear alarming. This fixes 59 files:

- Progress messages -> log_step (cyan): "Injecting environment variables...",
  "Attaching volume...", "Powering on instance...", "Retrieving server IP...",
  "Terminating sandbox/server...", "Creating datacenter...", "Importing SSH key...",
  "Deleting service/app...", "Modal not authenticated. Running setup..."
- Informational notices -> log_info (green): WhatsApp QR code authentication
  notices (30 nanoclaw scripts), codespace delete hints (14 scripts),
  "Appending environment variables to ~/.zshrc..." (6 local scripts),
  credential prompt hints, package update skipped, app reuse notices

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 03:24:30 -08:00
A
7c693db35b
refactor: extract check_ssh_key_by_fingerprint into shared helper (#552)
13 cloud providers had identical 5-line check_ssh_key functions that
fetch SSH keys from the provider API and grep for the fingerprint.
Extract this pattern into a shared check_ssh_key_by_fingerprint helper
in shared/common.sh, reducing each cloud's function to a single line.

Affected clouds: BinaryLane, Cherry, Civo, Contabo, DigitalOcean,
Genesis Cloud, Hetzner, Hostinger, Latitude, Linode, OVH, Scaleway,
Vultr.

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>
2026-02-11 16:12:07 -08:00
A
be5f9f1087
refactor: extract get_validated_server_name to eliminate 18 duplicate get_server_name functions (#535)
18 cloud lib/common.sh files had identical 7-line get_server_name()
functions (get_resource_name + validate_server_name + echo). Added a
shared get_validated_server_name helper to shared/common.sh and replaced
all duplicates with one-line delegations. Net -110 lines.

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>
2026-02-11 14:42:09 -08:00
A
0835b35a36
fix: use log_step (cyan) for progress messages instead of log_warn (yellow) (#534)
~1500 progress messages across 481 files were using log_warn (yellow)
for normal status updates like "Installing...", "Setting up...",
"Creating server...", etc. This made users think something was wrong
when everything was proceeding normally.

Changes:
- Replace log_warn with log_step for all progress/status messages
- Keep log_warn only for actual warnings (errors, remediation hints)
- Remove emoji from 3 sprite completion messages

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-11 14:37:43 -08:00
A
10a40ca574
fix: add log_step for progress messages, fix misleading prompt error (#440)
- Add log_step() function (cyan) for status/progress messages
- Convert misused log_warn calls to log_step in shared/common.sh
  (14 instances: SSH key gen, agent verification, waiting, configuring)
- Convert representative cloud scripts: hetzner, digitalocean, sprite
- Fix misleading validatePrompt error that suggested --prompt-file as a
  workaround when it has the same validation

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>
2026-02-11 04:28:17 -08:00
A
fdc5d5e58b
refactor: extract shared SSH helpers to eliminate ~410 lines of duplication (#429)
Add ssh_run_server, ssh_upload_file, ssh_interactive_session, and
ssh_verify_connectivity to shared/common.sh. These four functions
were copy-pasted identically across 21 cloud provider lib files,
differing only in SSH username (root vs ubuntu).

Providers now set SSH_USER and delegate to the shared helpers via
one-line wrappers, reducing each provider's lib by ~20 lines.

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>
2026-02-11 03:45:18 -08:00
A
ba5cdd8c3c
refactor: extract helpers from create_server() in hetzner and hostinger (#425)
Extract JSON body builders and error handlers from the two largest
remaining create_server() functions (hetzner 84->43, hostinger 76->44
lines), following the pattern established in PR #423.

Extracted helpers:
- hetzner: _hetzner_build_create_body(), _hetzner_check_create_error()
- hostinger: _hostinger_build_create_body(), _hostinger_handle_create_error()

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>
2026-02-11 03:19:54 -08:00
A
ccd7ff013a
refactor: reduce complexity by extracting shared interactive_pick() and using ensure_api_token_with_provider() (#411)
- Extract interactive_pick() to shared/common.sh: generic numbered-menu
  picker that replaces 4 duplicate _pick_location/_pick_server_type/_pick_plan
  functions across hetzner and hostinger (156 lines -> 71 lines)
- Replace ensure_fly_token() (53 lines) with ensure_api_token_with_provider()
  plus a flyctl CLI auth pre-check (17 lines)
- Replace ensure_render_api_key() (38 lines + _save_render_api_key 8 lines)
  with ensure_api_token_with_provider() (6 lines)

Net reduction: 156 lines removed across 5 files. No functionality changes.

Agent: complexity-hunter

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-11 01:42:22 -08:00
A
81bab47a74
fix: Escape API keys in continue.sh JSON configs to prevent injection (#374)
Replace vulnerable heredoc patterns across 27 continue.sh scripts with
setup_continue_config() helper that uses json_escape() + upload_config_file()
to safely handle API keys containing special characters like quotes or braces.

Also fix _save_token_to_config() in shared/common.sh which had the same
unescaped heredoc vulnerability for local token storage.

Relates to #104

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>
2026-02-11 00:13:19 -08:00
Ahmed Abushagur
8b9f9a0e5a
QA-Bot setup (#335)
* feat: testing

* feat: auto-fix dead apis

* fix: mock works

* feat: new fixtures

* fix: more clouds tested

* fix: dry run fix

* fix: civo valid size

* fix: civo result wait

* feat: fixtures

* feat: per cloud agent
2026-02-10 19:51:07 -08:00
A
571e17c559
feat: Add Continue AI coding assistant (#303)
Continue is an open-source AI coding assistant with 31K+ GitHub stars and strong HN buzz (298 points launch, 178 points v1.0). Features CLI TUI and headless modes, natively supports OpenRouter.

Evidence of community demand:
- GitHub: 31,335 stars (https://github.com/continuedev/continue)
- HN Launch: 298 points (July 2023)
- HN v1.0: 178 points (March 2025)
- YC-backed (S23)

Technical requirements met:
- Single command install: npm install -g @continuedev/cli
- Native OpenRouter support via config.json
- CLI modes: TUI (cn) and headless (cn -p)

Implemented on sprite and hetzner clouds.

Agent: agent-scout

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-10 16:49:31 -08:00
A
fc48279ecb
feat: Add kilocode scripts for hetzner, digitalocean, vultr, linode, lambda, aws-lightsail, gcp (#114)
Agent: gap-filler

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-09 19:46:21 -08:00
A
b0f924b511
fix: Prevent Python/shell injection via env vars and triple-quote strings (#102)
- Fix triple-quote injection in SSH keys (Scaleway, UpCloud), userdata
  (BinaryLane), init scripts (Civo, Kamatera), and GraphQL queries
  (RunPod) by passing data via stdin/json_escape instead of inline
  string interpolation
- Add input validation for all cloud provider env vars (region, type,
  plan, etc.) using validate_region_name/validate_resource_name to block
  shell metacharacters before they reach Python string interpolation
- Validate Modal image name as Python identifier to prevent code injection
- Validate numeric env vars (RAM, GPU count, disk size) across all providers

Affects: 19 cloud provider lib/common.sh files
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>
2026-02-09 10:22:39 -08:00
A
2915d7bca6
fix: Improve CLI error handling, fix bash compat, and update cloud READMEs (#90)
- Show clear error when --prompt/-p or --prompt-file is used without a
  value (previously silently ignored)
- Fix --prompt-file splice index bug when used after --prompt
- Replace echo -e with printf in fly/lib/common.sh for macOS bash 3.x
  compatibility
- Fix incorrect env var name in README (DIGITALOCEAN_TOKEN -> DO_API_TOKEN)
- Add missing agent entries (gptme, OpenCode, Plandex) to 11 cloud READMEs
- Add all 13 agents to Civo README (previously only had 3)

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>
2026-02-09 09:33:57 -08:00
A
1bd7b1bd07
feat: Add community-coordinator agent to refactor team (#64)
* ux: Improve error messages and user guidance across CLI and shell scripts

Enhanced error messages to be more actionable and user-friendly:

CLI improvements (commands.ts):
- Made validateNonEmptyString clearer: "is required but was not provided"
- Reordered troubleshooting steps to check matrix first (most common issue)
- Simplified 404 error message: "doesn't exist yet" vs "may not be implemented"
- Changed "Troubleshooting steps" to just "Troubleshooting" (less formal)

Shared library improvements (shared/common.sh):
- OAuth cancellation now explains why API key is needed and where to get it
- safe_read non-TTY error explains what non-interactive mode is with example
- get_resource_name error shows exact env var syntax needed
- Agent verification failures now list specific possible causes
- All improvements add context and next steps rather than just stating the problem

Hetzner library improvements (hetzner/lib/common.sh):
- Replaced technical "Remediation" with friendly "How to fix"
- Changed log_warn to log_error for error conditions (consistent severity)
- Added spacing for better readability of multi-line errors
- Made server creation errors more specific about account issues

All changes focus on helping users understand WHAT went wrong and HOW to fix it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: Replace issue-triager with community-coordinator agent

Replace the issue-triager agent in the refactor team with a
community-coordinator that actively engages with GitHub issues:
acknowledges reports, posts interim updates, delegates to relevant
teammates, and posts final resolutions — so reporters feel heard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-09 02:58:26 -08:00
L
0708ff1700
fix: Use robust OpenCode install method across all clouds (#48)
The upstream OpenCode installer pipes `curl -# -L | tar xz` which fails
in container exec environments (Sprite, E2B, Modal, Daytona) where the
binary stream gets corrupted through the exec layer, producing
"gzip: stdin: not in gzip format" errors.

Added opencode_install_cmd() to shared/common.sh that downloads the
binary to a file first, then extracts it. Updated all 17 opencode.sh
scripts to use this robust method instead of the upstream installer.

The previous fix (#44) only addressed Sprite with a hardcoded
linux-x86_64 architecture. This fix detects OS/arch dynamically and
applies to all cloud providers.

Fixes #42

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 23:02:18 -08:00
L
7f956b8d8e
Add Plandex coding agent with 14 cloud implementations (#36)
Plandex is an open source AI coding agent for complex tasks (15k+ GitHub
stars, multiple HN frontpage posts). It natively supports OpenRouter via
OPENROUTER_API_KEY environment variable and installs via a single curl
command. Go-based CLI with sandbox and version control for AI changes.

Implemented on all 14 clouds: sprite, hetzner, digitalocean, vultr,
linode, lambda, aws-lightsail, gcp, e2b, modal, fly, civo, scaleway,
daytona.

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 21:35:04 -08:00
Sprite
ea844c272b Add OpenCode agent (10.8k stars, 319pt HN post)
OpenCode is a Go-based AI coding agent with a terminal TUI (Bubble Tea).
It natively supports OpenRouter as a built-in provider via OPENROUTER_API_KEY.

- Add opencode agent to manifest.json with env config
- Implement sprite/opencode.sh for Sprite cloud
- Implement hetzner/opencode.sh for Hetzner Cloud
- Add matrix entries (implemented: sprite, hetzner; missing: rest)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-08 05:13:37 +00:00
Sprite
cdd0d4e93e refactor: Extract ENV_TEMP pattern to inject_env_vars_ssh (SSH providers)
Converted 34 scripts across 6 SSH-based providers to use inject_env_vars_ssh:
- digitalocean: amazonq, cline, gemini (3 scripts)
- hetzner: amazonq, cline, gemini (3 scripts)
- linode: amazonq, cline, gemini (3 scripts)
- vultr: amazonq, cline, gemini (3 scripts)
- aws-lightsail: all agents except openclaw (10 scripts)
- lambda: all agents except openclaw (10 scripts)

Plus aws-lightsail/openclaw and lambda/openclaw (2 scripts)

Each conversion replaces 11-15 lines of temp file management with a single
inject_env_vars_ssh call that handles creation, upload, sourcing, and cleanup.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 04:11:15 +00:00
L
b6ee6b6ab1
Add guardrails: CLAUDE.md rules, hooks, pre-commit validation (#33)
* feat: add gptme agent to spawn matrix

Add gptme (https://github.com/gptme/gptme) - a personal AI agent in the
terminal with tools for code editing, terminal commands, web browsing,
and more. Natively supports OpenRouter via OPENROUTER_API_KEY.

- Add gptme agent entry to manifest.json with OpenRouter env vars
- Implement sprite/gptme.sh deployment script
- Implement hetzner/gptme.sh deployment script
- Add "missing" matrix entries for remaining 8 clouds
- Update README.md with usage instructions for Sprite and Hetzner

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add Fly.io cloud provider with claude and aider agents

Add Fly.io as a new cloud provider using the Machines REST API for
provisioning and flyctl CLI for SSH access. Docker-based machines
with pay-per-second pricing.

- Create fly/lib/common.sh with Fly.io Machines API integration
- Implement fly/claude.sh for Claude Code deployment
- Implement fly/aider.sh for Aider deployment
- Update README.md with Fly.io usage instructions and env vars

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add gemini, amazonq, cline, gptme to Fly.io

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add openclaw, nanoclaw, goose, codex, interpreter to Fly.io

Implements 5 new agent scripts for the Fly.io cloud provider:
- fly/openclaw.sh: OpenClaw with gateway + TUI, model selection, config
- fly/nanoclaw.sh: NanoClaw WhatsApp agent with .env configuration
- fly/goose.sh: Block's Goose agent with OpenRouter provider
- fly/codex.sh: OpenAI Codex CLI with OpenRouter base URL override
- fly/interpreter.sh: Open Interpreter with OpenRouter base URL override

All scripts follow the Fly.io pattern (flyctl-based, no IP args for
run_server/interactive_session) and use upload_file for env injection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add gptme agent to 8 remaining clouds

Implement gptme agent scripts for digitalocean, vultr, linode, lambda,
aws-lightsail, gcp, e2b, and modal. Each script follows the exact
pattern of that cloud's existing aider.sh, adapted for gptme's install
and launch commands. Updates manifest.json matrix entries from "missing"
to "implemented".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add guardrails from insights: CLAUDE.md rules, hooks, pre-commit

Based on usage insights analysis:

CLAUDE.md:
- Shell script rules: curl|bash compat, macOS bash 3.x compat
- Autonomous loop rules: test after each iteration, never revert fixes
- Git workflow rules: always use feature branches

.claude/settings.json:
- PostToolUse hook validates .sh files on every Write/Edit:
  syntax check, no relative source, no echo -e, no set -u

.githooks/pre-commit:
- Blocks commits with: syntax errors, relative sources, echo -e,
  set -euo, references to deleted functions
- Install: git config core.hooksPath .githooks

README.md:
- Added developer setup section with hook installation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 20:02:19 -08:00
Sprite
6d4eca6d5d refactor: add SC2154 disable for API token variables
- hetzner/lib/common.sh:31 - HCLOUD_TOKEN set by caller
- digitalocean/lib/common.sh:36 - DO_API_TOKEN set by caller
- Silences false positive warnings for intentionally external variables
- These tokens are exported by ensure_*_token functions before use
2026-02-08 03:21:48 +00:00
Sprite
7a21be18fc fix: remove undefined ENV_TEMP from nanoclaw trap handlers
- 5 nanoclaw scripts only create DOTENV_TEMP but trap referenced both
- Causes trap to fail silently when trying to rm undefined variable
- Fix: Change trap from both temps to just DOTENV_TEMP
- Affected: digitalocean, hetzner, linode, sprite, vultr

Root cause: Automated trap addition in Round 19 incorrectly detected
these files as using both temp variables (DOTENV_TEMP contains substring
ENV_TEMP which caused false positive in grep check)
2026-02-08 03:20:36 +00:00
Sprite
91c466c008 refactor: add trap cleanup for temp files in agent scripts
- Add trap 'rm -f "${ENV_TEMP}"' EXIT after mktemp creation
- Scripts with DOTENV_TEMP get combined trap for both files
- Remove manual rm calls that are now redundant
- Prevents temp file leaks on early script exit (errors, signals)
- Affects 67 agent scripts across all providers

Impact: Prevents /tmp pollution in production deployments
Score: 90 (Impact: 9, Confidence: 10, Risk: 1)
2026-02-08 03:15:48 +00:00
Sprite
0f5a11b1c9 fix: repair broken Python error parsing in Hetzner and DigitalOcean (CRITICAL)
Fixed 2 critical syntax errors in API error handling:
- Hetzner line 160-161: Malformed error_msg assignment
- DigitalOcean line 172-173: Broken error_msg assignment

These were introduced during Round 15 refactoring and would crash
whenever users encountered API errors during server creation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 03:05:38 +00:00
Sprite
55ef42c82e refactor: add shellcheck disables for intentional SSH_OPTS word splitting
Add SC2086 disable comments to interactive_session() functions in
GCP, Hetzner, and DigitalOcean providers. SSH_OPTS is intentionally
unquoted to allow word splitting for multiple SSH options.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:56:08 +00:00
Sprite
f20568aea0 fix: repair broken Python multi-line assignments in 3 providers
Fixed malformed Python command assignments in:
- hetzner/lib/common.sh: Separated declaration and assignment for error_msg and userdata_json
- digitalocean/lib/common.sh: Fixed error_msg assignment split across lines
- vultr/lib/common.sh: Fixed saved_key and two error_msg assignments

Pattern applied: Separate `local var` declaration from `var=$(command)` assignment
to avoid bash syntax errors.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:47:13 +00:00
Sprite
f5d210aa19 refactor: add explicit timeout to wait_for_cloud_init calls
Add explicit timeout parameter (60 seconds) to all wait_for_cloud_init calls
across linode, vultr, digitalocean, hetzner, gcp, and aws-lightsail providers.

This makes timeout configuration more explicit and easier to tune per-provider
or per-agent in the future. The value of 60 matches the default in the
wait_for_cloud_init function implementations.

Modified 60 agent scripts (10 agents × 6 providers).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:38:19 +00:00
Sprite
cabdbc37ba refactor: add pipefail to error handling flags
Changed 65 agent scripts from `set -e` to `set -eo pipefail` to ensure
errors in piped commands are properly caught. This prevents silent
failures when commands like `curl | bash` fail in the middle.

Files updated across all cloud providers:
- aws-lightsail: 10 scripts
- digitalocean: 3 scripts
- e2b: 10 scripts
- gcp: 10 scripts
- hetzner: 3 scripts
- lambda: 10 scripts
- linode: 3 scripts
- modal: 10 scripts
- sprite: 3 scripts
- vultr: 3 scripts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:34:45 +00:00
Sprite
e0dfc9672a refactor: fix shellcheck directive placement in agent scripts
- Move shellcheck source directives before if statements
- Fix 90 SC1073/SC1123 parse errors
- Enables full shellcheck analysis of agent scripts

Score: 90 (Impact: 18, Confidence: 10, Risk: 2)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:32:08 +00:00
Sprite
0cab5827de refactor: consolidate OpenClaw config setup to shared helper
- Add setup_openclaw_config() to shared/common.sh
- Replace ~350 lines of duplicate config code across 10 files
- Uses callback pattern for provider-specific upload/run operations

Score: 14 (Impact: 7, Confidence: 8, Risk: 4)
2026-02-08 02:25:17 +00:00
Sprite
f9dd9a7bf5 refactor: consolidate Claude Code config setup to shared helper
- Add setup_claude_code_config() to shared/common.sh
- Replace ~400 lines of duplicate config code across 10 files
- Uses callback pattern for provider-specific upload/run operations
- Net reduction: 325 lines (81.2% reduction)

Score: 16 (Impact: 8, Confidence: 8, Risk: 4)
2026-02-08 02:22:06 +00:00
Sprite
eff8ded1bd refactor: add shellcheck source directives for provider variables
- Add shellcheck source comments to all agent scripts
- Tells shellcheck where provider-exported variables are defined
- Fix 132+ SC2154 warnings across all providers

Score: 30 (Impact: 6, Confidence: 10, Risk: 2)
2026-02-08 02:17:04 +00:00
Sprite
0f53c81052 refactor: fix SC2140 quoting in inject_env_vars calls
- Remove unnecessary inner quotes in environment variable values
- Fix 30+ SC2140 warnings across 28 provider scripts
- Pattern: "VAR=\"value\"" → "VAR=value"

Score: 35 (Impact: 7, Confidence: 10, Risk: 2)
2026-02-08 02:11:45 +00:00
Sprite
c7fe5ca9f2 refactor: fix SC2155 declaration issues in lib files
- Separate variable declaration from command assignment
- Prevents masking command exit codes
- Fix 7 SC2155 warnings in modal, sprite, and shared libs

Score: 30 (Impact: 6, Confidence: 10, Risk: 2)
2026-02-08 02:10:02 +00:00
Sprite
3b6c761904 refactor: add username parameter to generic_ssh_wait
- Add required username parameter to generic_ssh_wait()
- Update SSH command to use dynamic username instead of hardcoded "root"
- Update all existing callers to pass username explicitly
- Enables GCP and AWS Lightsail to adopt generic_ssh_wait in future

Score: 40 (Impact: 8, Confidence: 10, Risk: 2)
2026-02-08 01:58:48 +00:00
Sprite
f2afdea792 refactor: extract ensure_ssh_key duplication to shared library (~220 lines)
Eliminates duplicate SSH key registration logic across 5 cloud providers
(Hetzner, DigitalOcean, Vultr, Linode, Lambda) by introducing a generic
callback-based pattern in shared/common.sh.

Before: Each provider had ~45 lines of nearly identical code for:
- Generating SSH keys if missing
- Getting fingerprints
- Checking if key exists with provider
- Registering key if not exists
- Error handling

After: Providers implement 2 simple callbacks:
- check_callback: provider-specific API call to check if key exists
- register_callback: provider-specific API call to register key

The shared function handles:
- Key generation (via generate_ssh_key_if_missing)
- Fingerprint extraction (via get_ssh_fingerprint)
- Flow control and logging
- Callback orchestration

Changes:
- shared/common.sh: Added ensure_ssh_key_with_provider() function
- hetzner/lib/common.sh: Refactored to use callbacks
- digitalocean/lib/common.sh: Refactored to use callbacks
- vultr/lib/common.sh: Refactored to use callbacks
- linode/lib/common.sh: Refactored to use callbacks
- lambda/lib/common.sh: Refactored to use callbacks

Benefits:
- DRY: Eliminates ~220 lines of duplicate code
- Maintainability: Bug fixes in registration flow benefit all providers
- Consistency: All providers use identical registration logic
- Extensibility: New providers can reuse this pattern

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 01:33:06 +00:00
Sprite
a631faf4fe refactor: suppress SC2086 for intentional SSH_OPTS word splitting
SSH_OPTS contains multiple flags that must be word-split, so unquoted
usage is intentional. Added shellcheck directives to suppress false
positive warnings across all cloud provider common.sh files.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 01:29:56 +00:00