Commit graph

47 commits

Author SHA1 Message Date
A
b094accb93
fix: save connection info for all Sprite agents in cloud_provision (#1510)
5 of 6 Sprite agent scripts silently skipped saving connection info
for 'spawn list', because only sprite/claude.sh defined the
agent_save_connection hook. All other clouds save connection info in
their create_server() equivalent; move save_vm_connection into
cloud_provision() in sprite/lib/common.sh to match that pattern and
cover all agents uniformly. Remove now-redundant agent_save_connection
from sprite/claude.sh.

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-20 01:51:25 -05:00
L
d5690a8b11
feat: spawn name prompt + kebab resource naming across all clouds (#1507)
* feat: add spawn name prompt and project confirmation to GCP flow

Ask for spawn name upfront (before auth), derive kebab-case default for
VM naming, and confirm the current GCP project before using it.

New interaction order:
  1. Spawn name: "My Dev Box" → kebab "my-dev-box" exported as
     GCP_INSTANCE_NAME_KEBAB
  2. gcloud auth + project confirm: "Current project: X  Keep? [Y/n]"
     If no → project picker shown
  3. SSH key
  4. Machine type picker (existing)
  5. Zone picker (existing)
  6. Instance name prompt: "Instance name [my-dev-box]: "
     User can press Enter to accept or type a custom name

New functions:
  _to_kebab_case()         — lowercases, replaces non-alnum with hyphens
  _gcp_prompt_spawn_name() — prompts for display name, exports kebab default;
                             honours SPAWN_NAME env var set by CLI (--name flag)

Modified:
  _gcp_resolve_project()  — adds Y/n confirmation when project already set
  get_server_name()       — shows kebab default in prompt, accepts Enter
  cloud_authenticate()    — calls _gcp_prompt_spawn_name first

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

* feat: add spawn name prompt to all clouds via shared/common.sh

Move _to_kebab_case() and prompt_spawn_name() to shared/common.sh so all
clouds get upfront spawn name prompting and kebab-based resource naming.

shared/common.sh:
  + _to_kebab_case()    — "My Dev Box" → "my-dev-box"
  + prompt_spawn_name() — asks for display name, exports SPAWN_NAME_DISPLAY
                          and SPAWN_NAME_KEBAB; skips if already set;
                          honours SPAWN_NAME env var from CLI --name flag
  ~ get_resource_name() — replaces silent SPAWN_NAME fallback with a visible
                          prefilled default: "Enter server name [my-dev-box]: "

Per-cloud changes (cloud_authenticate gains prompt_spawn_name first):
  hetzner, fly, aws, daytona, digitalocean, sprite — one-line change each

gcp/lib/common.sh:
  - Remove _to_kebab_case()        (now in shared)
  - Remove _gcp_prompt_spawn_name() (now in shared as prompt_spawn_name)
  ~ cloud_authenticate: _gcp_prompt_spawn_name → prompt_spawn_name
  ~ get_server_name: simplified back to get_validated_server_name
    (shared get_resource_name now shows the kebab default in the prompt)

Result — every cloud shows this flow upfront:
  Spawn name (e.g. "My Dev Box"): My Claude Box
  ℹ Resource name: my-claude-box
  ...
  Enter server name [my-claude-box]: ⏎

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

* fix: use "Use project '...'?" instead of "Keep this project?" in GCP prompt

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

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-02-19 22:22:59 -08:00
A
c3d251100b
fix: inline temp file cleanup in setup_shell_environment to preserve EXIT trap (#1489)
Replace both the trap-clobbering `trap 'rm -f ...' EXIT` calls and the
inline `rm -f` approach with `track_temp_file()` from shared/common.sh.
This registers temp files with the centralized cleanup handler that is
already set up on EXIT/INT/TERM, so:
- Temp files are cleaned up even on interrupt (not just success path)
- The calling script's EXIT trap is never clobbered
- _sprite_retry wrappers are preserved for transient error recovery

Agent: pr-maintainer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 00:30:48 +00:00
Ahmed Abushagur
4d32923d5f
fix: add retry logic for transient Sprite API errors (#1487)
Sprite API calls intermittently fail with TLS handshake timeouts and
connection resets. Add _sprite_retry() wrapper that retries up to 3
times with 3s delay on transient errors.

Wrapped calls: sprite create, sprite exec (run_sprite), sprite exec
-file (upload_file_sprite, setup_shell_environment uploads).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 17:49:29 -05:00
Ahmed Abushagur
3b1f87e656
fix: pass -o org flag to all sprite CLI commands (#1479)
* fix: pass -o org flag to all sprite CLI commands

sprite create/exec/list/destroy fail with "authentication failed" when
the org isn't passed explicitly. Detect the selected org after login and
thread it through all sprite commands via _sprite_org_flags().

Also fix ensure_sprite_authenticated to fail loudly instead of
swallowing errors with || true.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: sprite scripts fail when zsh is not available

setup_shell_environment overwrites .bashrc with `exec zsh`, but sprites
don't have zsh installed. This breaks PATH and causes all agent launch
commands that source .zshrc to fail.

- Only switch to zsh if it's actually available on the sprite
- Replace `source ~/.zshrc` with explicit PATH in all sprite agent
  launch commands (openclaw, opencode, codex, kilocode)
- Fix start_openclaw_gateway to use explicit PATH instead of .zshrc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: openclaw not found on sprite — bashrc corruption from prior runs

On reused sprites, .bashrc still has `exec /usr/bin/zsh -l` from a prior
run. Sourcing it in the install command causes `&&` to short-circuit, so
`bun install -g openclaw` never runs.

- Clean up stale `exec zsh` lines from .bashrc at start of
  setup_shell_environment (fixes reused sprites)
- Use explicit PATH in openclaw install command instead of relying on
  .bashrc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use npm instead of bun for openclaw install on sprite

bun 1.3.9 on sprites fails with "connection closed" during dependency
resolution. Other sprite agents (codex, kilocode) already use npm
successfully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: openclaw install — npm+bun fallback, verify binary exists

Try npm first (more reliable on sprites), fall back to bun, then verify
the binary is actually in PATH before continuing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: persist npm global bin path to .spawnrc on sprites

npm installs openclaw successfully but its global bin dir isn't in the
sprite's default PATH. Detect the npm bin path after install, write it
to .spawnrc so gateway and launch commands (which source .spawnrc) find
the binary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 15:47:47 -05:00
A
f3ffb6caed
fix: broken error message in multi-creds validation, predictable temp path (#1442)
1. _multi_creds_validate referenced undefined help_url variable, causing
   empty "Get new credentials from: " error messages when OVH credential
   validation fails. Added help_url as parameter and pass it from caller.

2. _spawn_inject_env_vars (used by 130+ agent scripts via spawn_agent)
   uploaded credentials to static /tmp/env_config path. The older
   inject_env_vars_ssh/inject_env_vars_cb functions document this as a
   symlink attack vector and use randomized paths. Fixed to match.

3. Removed dead inject_env_vars_fly and inject_env_vars_sprite functions
   (all agent scripts now use spawn_agent -> _spawn_inject_env_vars).

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 07:51:28 -05:00
A
d2866b2976
ux: standardize destroy_server() wrapper for OVH and Sprite (#1345)
Adds destroy_server() wrapper functions to OVH and Sprite cloud libraries
to match the standardized function name used by 8 other clouds.

Before:
- OVH used destroy_ovh_instance()
- Sprite had no destroy function
- Cross-cloud scripts couldn't use a uniform destroy_server() call

After:
- OVH: destroy_server() wraps destroy_ovh_instance()
- Sprite: destroy_server() wraps "sprite destroy <name>" CLI command
- Cross-cloud scripts can now call destroy_server() uniformly

This fixes the blocker for PR #1217 which hardcodes destroy_server() calls
that would silently fail for OVH and Sprite users.

Fixes #1178

Agent: ux-engineer

Co-authored-by: spawn-bot <bot@openrouter.ai>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 20:24:40 -05:00
A
654352bed0
security: fix predictable temp file path in sprite upload_file_sprite (#1330)
Replace PID-based temp path with cryptographically random generation
to prevent symlink attacks on remote servers.

Severity: MEDIUM
Finding: sprite/lib/common.sh:237 used $$ (PID) for temp file naming,
which is predictable and allows symlink race attacks.

Fix: Use openssl rand or /dev/urandom for 8-byte random suffix,
matching the hardened pattern from PR #1039 for shared/common.sh.

Related: #763 (security batch tracking issue)

Agent: security-auditor

Co-authored-by: spawn-bot <bot@openrouter.ai>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 20:22:22 -05:00
A
ec81c74594
refactor: introduce cloud adapter + spawn_agent runner system (#1340)
Eliminate ~70% boilerplate across 149 agent scripts by introducing a
standard cloud_* adapter interface and spawn_agent orchestration runner.

Each cloud's lib/common.sh now exports 7 adapter functions (cloud_authenticate,
cloud_provision, cloud_wait_ready, cloud_run, cloud_upload, cloud_interactive,
cloud_label) that wrap cloud-specific operations behind a uniform interface.

Agent scripts define hooks (agent_install, agent_env_vars, agent_launch_cmd,
etc.) and call `spawn_agent "Agent Name"` — the runner handles the full
deployment flow: auth → provision → wait → install → API key → env → config → launch.

- shared/common.sh: add spawn_agent(), _fn_exists(), _spawn_inject_env_vars()
- 10 cloud lib/common.sh files: add cloud_* adapter functions
- 149 agent scripts: rewrite to hook pattern (~40-80 lines → ~20-35 lines)
- test/run.sh: update 2 sprite test patterns for new adapter paths
- Net reduction: ~4,300 lines (2,257 added, 6,563 removed)

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-16 16:25:44 -08:00
A
3030b1d036
fix: revert .profile writes, use explicit PATH in launch commands (#1260)
Stop writing env vars to ~/.profile and ~/.bash_profile — only write to
.bashrc and .zshrc. The .profile approach caused issues because login
shells source it inconsistently across distros, and creating .bash_profile
makes bash -l skip .profile entirely.

Replace `bash -lc claude` launch commands with explicit PATH export +
source pattern across all cloud providers. This ensures claude is found
regardless of shell initialization quirks.

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-16 00:43:49 -08:00
A
46e6f46008
fix: stop creating ~/.bash_profile — was destroying system PATH (#1258)
On Ubuntu/Debian, ~/.bash_profile doesn't exist by default. When bash
starts as a login shell (bash -l), it sources the FIRST file it finds
from: ~/.bash_profile, ~/.bash_login, ~/.profile. Since only ~/.profile
exists, that's what gets sourced — and ~/.profile sets up the standard
PATH (/usr/bin, /bin, etc.) and sources ~/.bashrc.

Our inject_env_vars_* functions and _finalize_claude_install were writing
to ~/.bash_profile and ~/.zprofile (either via touch+append or via
for-loop over all rc files). Creating ~/.bash_profile caused bash -l to
source it INSTEAD of ~/.profile, completely losing the standard PATH
setup. After deployment, even basic commands like `ls` would fail.

Fix: Only write to ~/.profile, ~/.bashrc, ~/.zshrc across all clouds
(shared, fly, sprite). These are the standard files that work correctly
on all Linux distros without breaking the shell initialization chain.

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-16 00:27:28 -08:00
A
99b21e2797
fix: write env config to all shell startup files including .bash_profile (#1251)
Root cause: bash -l sources the FIRST of ~/.bash_profile, ~/.bash_login,
~/.profile. If ~/.bash_profile exists (e.g. from cloud-init), ~/.profile
is never read and our claude PATH exports are invisible.

Additionally, .bashrc has a non-interactive guard that skips exports when
sourced from non-interactive shells like `ssh host "cmd"` or `bash -lc`.

Fix: write env config and PATH entries to ALL shell startup files:
~/.profile, ~/.bash_profile, ~/.bashrc, ~/.zshrc, ~/.zprofile.
This ensures both login and interactive shells on any platform find claude.

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-16 00:04:36 -08:00
A
dac4c62d6c
fix: try bun before npm for Claude Code install, fix PATH in launch (#1249)
Two fixes:
1. Swap fallback order from curl → npm → bun to curl → bun → npm.
   Bun is faster and typically pre-installed. Use `bun i -g`.

2. Fix "claude: command not found" at launch. The default .bashrc has
   a non-interactive guard (`case $- in *i*) ;; *) return;; esac`)
   that skips PATH exports when sourced from SSH command strings.
   Fix: write env config to ~/.profile (always sourced by login shells)
   in addition to .bashrc/.zshrc, and launch with `bash -lc claude`
   which starts a login shell that sources ~/.profile.

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-15 23:44:02 -08:00
L
fffb3591c4
feat: wire shared/github-auth.sh into all agent flows (#1216)
* feat: wire shared/github-auth.sh into all agent flows

Add offer_github_auth() to shared/common.sh and call it from the
inject_env_vars_* functions so all agent flows automatically offer
GitHub CLI setup after env var injection — no per-script changes needed.

Changes:
- shared/common.sh: add offer_github_auth() function, call it from
  inject_env_vars_ssh() and inject_env_vars_local()
- sprite/lib/common.sh: call offer_github_auth() from
  inject_env_vars_sprite()
- OVH is covered automatically (inject_env_vars_ovh delegates to
  inject_env_vars_ssh)

Behavior:
- Prompts "Set up GitHub CLI (gh) on this machine? (y/N):"
- Defaults to No (non-blocking for users who don't need it)
- Skippable via SPAWN_SKIP_GITHUB_AUTH=1 env var for CI/automation
- Uses safe_read for curl|bash compatibility
- Downloads and runs shared/github-auth.sh on the remote VM

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

* refactor: add shared agent setup helpers, deduplicate hetzner scripts (#1236)

Add 5 composable helper functions to shared/common.sh (install_agent,
verify_agent, get_or_prompt_api_key, inject_env_vars_cb, launch_session)
that use the same callback pattern as offer_github_auth and
setup_claude_code_config. Refactor all 15 hetzner agent scripts to use
them, reducing total line count from 868 to 579 (-33%).

Phase 1 of multi-phase rollout — remaining clouds to follow.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-15 23:00:53 -08:00
L
d8ac64863d
fix: inject env vars into both .bashrc and .zshrc, fix PATH across all clouds (#1213)
API keys and env vars were only written to .zshrc, so SSH sessions using
bash couldn't find credentials. Also fixes incorrect ~/.claude/local/bin
PATH (claude installs to ~/.local/bin) and syncs interactive_session PATH
with cloud-init PATH across all 9 clouds.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-15 17:30:40 -08:00
A
a0f6b335a4
fix: harden upload_file path validation with strict allowlist regex across 10 clouds (#993)
Replace fragile blocklist validation and printf '%q' escaping in upload_file()
with strict allowlist regex [a-zA-Z0-9/_.~-]+ across all non-SSH cloud providers.
For codesandbox, additionally migrate from shell command interpolation to SDK
filesystem API via environment variables, eliminating the injection surface entirely.

Affected clouds: codesandbox, daytona, e2b, fly, koyeb, modal, northflank,
railway, render, sprite

Fixes #989

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 12:20:40 -08:00
A
5a1037d92c
fix: replace ((var++)) with var=$((var + 1)) for macOS bash 3.x compat (#769)
((var++)) returns exit code 1 when the variable is 0 (falsy), which
causes set -e to terminate the script. Replace all instances with
the safe var=$((var + 1)) pattern in sprite/lib/common.sh and
test/run.sh.

Fixes #762

Agent: community-coordinator

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:45:51 -08:00
A
d9037fad32
fix: improve error messages and UX consistency across CLI and shell scripts (#466)
- Clarify download error messages: distinguish HTTP errors from network errors
  with specific status codes in the message
- Add actionable next steps to OAuth timeout: re-run command or set key manually
- Standardize error help labels to "How to fix:" across CLI and shell scripts
  (was inconsistently "What to do:", "Troubleshooting:", or missing)
- Add API method/endpoint context to retry failure messages so users know
  which API call failed
- Make verify_agent_installed error cases mutually exclusive: first for
  PATH/installation issues, second for runtime/dependency issues

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 07:46:56 -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
ca335aabdc
refactor: Extract helpers from start_oauth_server and ensure_sprite_installed (#325)
- start_oauth_server (68 -> 17 lines): Extract Node.js script generation
  into _generate_oauth_server_script helper
- ensure_sprite_installed (62 -> 49 lines): Extract duplicated version
  check-and-log pattern into _log_sprite_found helper

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-10 19:01:56 -08:00
Sprite
b2e2462f0d fix: Poll for sprite provisioning instead of blind sleep
ensure_sprite_exists() now polls `sprite list` until the sprite
appears (up to 30s) instead of a fixed sleep. This eliminates the
spurious "sprite not found" errors that appeared while the sprite
was still provisioning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 00:52:35 +00:00
A
ebb4c08306
fix: Remove printf %q escaping in run_sprite that broke command parsing (#92)
printf %q escapes spaces and shell metacharacters, turning "claude install"
into "claude\ install" — which bash -c interprets as a single command named
"claude install" (with literal space). This broke all multi-word commands
passed to run_sprite, including pipes, redirects, and && chains.

Since all callers pass trusted, hardcoded command strings (not user input),
the command string should be passed directly to bash -c for normal shell
parsing.

Fixes #88

Agent: team-lead

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:36:56 -08:00
A
bcceb700e1
fix: Improve sprite version display when version is unknown (#81)
When sprite version output doesn't match the expected format, the message
now omits the version rather than displaying "unknown". Also broadened the
version regex to match versions without 'v' prefix.

Fixes #79

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 08:14:08 -08:00
A
dfaf9370d2
fix: Improve sprite CLI detection on Termux (#76)
Check additional Termux-specific paths when detecting existing sprite
CLI installation, preventing unnecessary reinstalls.

Fixes #75

Agent: community-coordinator

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 03:55:46 -08:00
A
9852ef82f8
feat: Improve error messages and troubleshooting guidance (#71)
Enhance UX across CLI and cloud providers with actionable error messages:

1. Modal sandbox creation failures now provide:
   - Detailed error output from Python SDK
   - Common causes (auth expired, quota issues, network)
   - Step-by-step troubleshooting (re-auth, check quota, status page)

2. Modal CLI installation failures now explain:
   - Missing pip/pip3 (with installation commands)
   - Permission issues (suggest --user flag)
   - Manual installation steps

3. Sprite connectivity failures now include:
   - Commands to check sprite status and logs
   - Steps to recreate sprite
   - Support contact information

4. Sprite CLI installation now:
   - Catches installation failures with helpful error messages
   - Verifies installation succeeded before proceeding
   - Provides manual installation instructions

5. CLI script download failures improved:
   - Distinguish between 404 (doesn't exist) and other errors
   - Provide specific next steps for each scenario
   - Suggest checking matrix for implementation status

All error messages follow the pattern:
- What went wrong
- Why it might have happened (common causes)
- What to do next (actionable steps)

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 03:37:17 -08:00
A
f387428bca
fix: Check sprite version before reinstalling (#69)
- Modified ensure_sprite_installed() to check if sprite is already installed
- When sprite is found, log version and skip installation
- Prevents unnecessary reinstalls when sprite CLI is already present

Fixes #63

Agent: community-coordinator

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 03:34:57 -08:00
LAB
d76c8dba0f
Security: fix critical command injection vulnerabilities in container providers (#54)
* refactor: Simplify API call retry logic in generic_cloud_api

Extract duplicated retry handling into focused helper functions:
- handle_api_network_error(): Handles curl errors with retry logic
- handle_api_transient_error(): Handles 429/503 HTTP errors
- _call_cloud_api(): Internal curl wrapper separating concerns

Reduces cyclomatic complexity of generic_cloud_api from 9 to 3.
Lines reduced from 89 to 54 (40% reduction).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Security: fix critical command injection vulnerabilities in container providers

CRITICAL SECURITY FIX - Command injection vulnerabilities

Fixed command injection in bash -c calls across all container/sandbox providers.
These functions were passing commands directly to bash -c without proper escaping,
allowing potential remote code execution via crafted inputs.

Files fixed:
- sprite/lib/common.sh: run_sprite(), upload_file_sprite()
- e2b/lib/common.sh: run_server(), upload_file(), interactive_session()
- daytona/lib/common.sh: run_server(), upload_file(), interactive_session()
- railway/lib/common.sh: run_server(), upload_file(), interactive_session()

Fix: Use printf %q to properly escape all command arguments before passing to bash -c.
This prevents command injection while maintaining functionality.

Severity: CRITICAL (CVSS 9.8)
Impact: Remote code execution, full system compromise
Mitigation: Proper shell escaping using printf %q

All modified files pass bash -n syntax validation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 12:00:43 -08:00
Sprite
1da3dca516 refactor: complete trap cleanup in sprite provider - remove manual rm calls
Removed manual rm calls for temp files in sprite/lib/common.sh since
traps now handle cleanup automatically. Combined traps for both temp
files in configure_shell_sprite() function.

Impact: Prevents temp file leaks on crashes/interrupts
Risk: Low - only removing redundant cleanup calls

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 03:31:24 +00:00
Sprite
6244720c84 refactor: quote sleep delay variables for safety
Quote INSTANCE_STATUS_POLL_DELAY, SSH_RETRY_DELAY, and
SPRITE_CONNECTIVITY_POLL_DELAY to prevent potential word splitting
issues with unusual values.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:50:08 +00:00
Sprite
9137a3d1fe refactor: fix SC2155 in sprite provider - separate declare and assign
Separated local variable declaration from command substitution assignment
in upload_file_sprite function to avoid masking return values.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:48:27 +00:00
Sprite
63db82c590 refactor: extract sleep values to configurable constants
Extract hardcoded sleep values in wait loops to environment-configurable
constants at the top of provider lib/common.sh files:

- INSTANCE_STATUS_POLL_DELAY (default: 5s, Lambda: 10s) - for instance status checks
- SSH_RETRY_DELAY (default: 5s) - for SSH connection retries
- SPRITE_CONNECTIVITY_POLL_DELAY (default: 5s) - for sprite connectivity checks

This allows users to tune timeout behavior globally via environment variables
without modifying code.

Files modified:
- linode/lib/common.sh
- vultr/lib/common.sh
- aws-lightsail/lib/common.sh
- sprite/lib/common.sh
- digitalocean/lib/common.sh
- lambda/lib/common.sh

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:37:35 +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
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
8aeef42471 refactor: fix SC2088 tilde expansion in GCP scripts
- Replace "~/" with "$HOME/" for proper expansion
- Fix 4 SC2088 warnings in nanoclaw.sh, claude.sh, openclaw.sh
- Ensures paths resolve correctly in upload_file calls

Score: 15 (Impact: 5, Confidence: 9, Risk: 3)
2026-02-08 02:09:45 +00:00
Sprite
e068007174 refactor: add braces to variable references in sprite/lib/common.sh
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 01:48:27 +00:00
Sprite
0ad6680f1f refactor: extract duplicate get_server_name logic to shared function
- Add get_resource_name() to shared/common.sh
  - Generic function for env-var-or-prompt pattern
  - Uses indirect expansion ${!var} for dynamic env vars
  - Preserves exact behavior: env check → prompt → error

- Update 9 cloud providers to use shared function:
  - aws-lightsail: LIGHTSAIL_SERVER_NAME
  - digitalocean: DO_DROPLET_NAME (with validation)
  - gcp: GCP_INSTANCE_NAME
  - hetzner: HETZNER_SERVER_NAME (with validation)
  - linode: LINODE_SERVER_NAME (with validation)
  - sprite: SPRITE_NAME (with validation)
  - vultr: VULTR_SERVER_NAME (with validation)
  - e2b: E2B_SANDBOX_NAME
  - modal: MODAL_SANDBOX_NAME

- Reduces code duplication: ~120 lines → ~25 lines
- Maintains backward compatibility (env vars, prompts, errors unchanged)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 01:16:20 +00:00
L
591066cd53
Use ${VAR:-} for all optional env var checks (#28)
Protects against 'unbound variable' errors even if set -u is
re-enabled or inherited. Every [[ -n "$UPPER_VAR" ]] pattern now
uses [[ -n "${UPPER_VAR:-}" ]] to safely default to empty.

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 16:28:12 -08:00
L
4087deb14e
Drop nounset (set -u) flag — incompatible with env var checks (#27)
The autonomous refactoring added `set -euo pipefail` but the scripts
check optional env vars with `[[ -n "$VAR" ]]` which is a fatal error
under nounset when the var isn't set (e.g. SPRITE_NAME, OPENROUTER_API_KEY).

Fix: downgrade to `set -eo pipefail` across all 42 affected files.

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 16:22:04 -08:00
L
7e952d1310
Fix shared/common.sh loading for curl-piped execution (#26)
When scripts run via `bash <(curl ...)`, BASH_SOURCE resolves to
/dev/fd/N, making the relative path `../../shared/common.sh` fail.

Fix: add remote fallback — try local file first, fall back to
fetching shared/common.sh from GitHub via eval+curl.

Applied to all 5 refactored lib/common.sh files (sprite, hetzner,
digitalocean, vultr, linode).

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 16:16:51 -08:00
L
3fb2e77b03
Autonomous refactoring: 5 rounds, ~1,400 lines eliminated, production-ready
Five rounds of autonomous AI agent team refactoring with security fixes, code consolidation, and expanded test coverage.
2026-02-08 00:06:46 +00:00
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
Sprite
fa02572d8c Improve OAuth callback page with animated checkmark and auto-close
Add styled success page with CSS-animated checkmark, fade-in messaging,
and auto-close after 3 seconds with fallback text if browser blocks it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 06:16:56 +00:00
Sprite
d6e957b039 Fix OAuth server and browser opener for Termux environments
- Add nc_listen helper that detects busybox nc and uses -p flag accordingly
- Add termux-open-url support to open_browser
- Deduplicate inline browser opener in try_oauth_flow to use open_browser

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 05:52:01 +00:00
Sprite
302a9b7896 Fix verify_sprite_connectivity exiting script early after single failed check
Retry connectivity up to 6 attempts (30s) instead of trying once and
silently continuing, which caused the next sprite exec to fail under set -e.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 05:22:58 +00:00
Sprite
780e2de9d3 Fix TTY detection and log output for non-interactive environments
- safe_read(): Test /dev/tty is functional before using it (exists
  but fails in containers/VMs)
- Log functions: Write to stderr so they don't pollute stdout in
  command substitutions like $(get_sprite_name)
- ensure_sprite_exists(): Fix grep regex (use -E for ERE)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 05:05:12 +00:00
Sprite
10ada0e04d Fix non-interactive mode for curl | bash execution
The scripts were failing when run via curl | bash because they tried
to read from /dev/tty which doesn't exist in piped contexts.

## Changes

- Added safe_read() helper function that gracefully handles TTY absence
- Updated get_sprite_name() to support SPRITE_NAME env variable
- Updated all read commands to use safe_read()
- Added clear error messages for non-interactive usage
- Updated README with non-interactive mode documentation

## Usage

Interactive:
  curl URL | bash

Non-interactive:
  SPRITE_NAME=dev-mk1 curl URL | bash
  SPRITE_NAME=dev-mk1 OPENROUTER_API_KEY=sk-xxx curl URL | bash

## Fixes

- /dev/tty: No such device or address error
- Scripts now work in CI/CD and automated contexts
- OAuth fallback still works via OPENROUTER_API_KEY env var

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 04:48:48 +00:00
Sprite
640af6260c Refactor spawn scripts with shared library and OAuth fallback
Major refactoring to improve code maintainability and reliability:

## Shared Library Architecture
- Created sprite/lib/common.sh with reusable bash functions
- Reduced openclaw.sh from 258 to 93 lines (-64%)
- Reduced claude.sh from 272 to 101 lines (-63%)
- Eliminated ~330 lines of duplicate code

## OAuth Fallback Mechanism
- Added automatic fallback to manual API key entry
- Handles missing netcat (nc) gracefully
- Handles port conflicts and timeouts
- Validates API key format with override option
- Works in headless and minimal environments

## Dual Execution Support
- Local: bash sprite/openclaw.sh
- Remote: curl URL | bash
- Auto-detects context and sources library appropriately

## New Shared Functions
- Logging: log_info(), log_warn(), log_error()
- Sprite setup: ensure_sprite_installed/authenticated/exists()
- Environment: setup_shell_environment()
- OAuth: get_openrouter_api_key_oauth() with fallback
- Utilities: run_sprite(), verify_sprite_connectivity()

## Documentation
- REFACTORING.md - Architecture and benefits
- OAUTH_FALLBACK.md - Fallback mechanism guide
- CURL_BASH_SOLUTION.md - Execution mode details
- EXAMPLES.md - Usage scenarios
- CHANGELOG.md - Complete change history

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 04:06:11 +00:00