Extract _get_multi_cred_spec, _load_multi_config_from_file, and
_save_multi_config_to_file helpers to eliminate duplicated per-cloud
config blocks in try_load_config, save_config, has_credentials,
prompt_credentials, and list_clouds.
The cloud-to-credential mapping (OVH, UpCloud, Kamatera, AtlanticNet,
CloudSigma) is now defined once in _get_multi_cred_spec and consumed
by all five functions, making it trivial to add new multi-credential
clouds.
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>
Implements Webdock cloud provider with full API integration:
- webdock/lib/common.sh with REST API primitives
- claude.sh, cline.sh, aider.sh agent scripts
- Test coverage in test/record.sh and test/mock.sh
- manifest.json updated with cloud entry and matrix
- README.md with usage documentation
Webdock offers affordable European VPS (€2.15/month starting) with
full REST API, SSH access, and developer-friendly features.
Agent: cloud-scout-1
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Both mock.sh and record.sh now run each cloud's tests/recordings
concurrently as background jobs instead of sequentially.
Results are aggregated after all clouds finish.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: strip ANSI colors before grepping test summary
The mock test output uses ANSI escape codes for colored ✓/✗/━━━
characters, so the grep in the Post summary step couldn't match
them. Strip colors with sed first.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use NO_COLOR standard instead of sed to strip ANSI codes
mock.sh now respects the NO_COLOR env var (https://no-color.org/).
CI sets NO_COLOR=1 so grep matches ✓/✗/━━━ cleanly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add CloudSigma cloud provider
Add CloudSigma as a new cloud provider with API-first architecture:
- Create cloudsigma/lib/common.sh with HTTP Basic Auth support
- Implement cloudsigma/claude.sh and cloudsigma/aider.sh agent scripts
- Add CloudSigma to manifest.json (38th cloud provider)
- Add matrix entries for all 15 agents (2 implemented, 13 missing)
- Update test/record.sh with CloudSigma endpoints and auth handling
- Update test/mock.sh with URL-stripping for CloudSigma API
- Add cloudsigma/README.md with usage documentation
CloudSigma features:
- API v2.0 with HTTP Basic Auth (email:password)
- Regions: ZRH (Zurich), WDC (Washington DC), LVS (Las Vegas)
- Granular resource control (CPU/RAM/Disk independently configurable)
- Ubuntu 24.04 cloned from public library drives
- SSH access via cloudsigma user
- Pay-as-you-go pricing starting at ~$14/month
Agent: cloud-scout
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: address security review comments for CloudSigma provider
- [CRITICAL] Fix command injection in credential saving: use sys.argv
instead of raw shell interpolation in Python strings
- [CRITICAL] Fix shell injection in create_cloudsigma_drive: pass name
and size via sys.argv instead of inline interpolation
- [CRITICAL] Fix shell injection in SSH key fingerprint lookups: pass
fingerprint via sys.argv
- [HIGH] Replace hardcoded VNC password with random generation via
openssl rand -hex 8
- [MEDIUM] Fix config file path injection: pass via sys.argv
Agent: pr-maintainer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add HOSTKEY (https://hostkey.com/) as a new cloud provider to the spawn
matrix. HOSTKEY offers affordable VPS hosting starting from €1/month with
hourly billing, making it suitable for running AI agents that use remote
API inference.
Changes:
- Created hostkey/lib/common.sh with HOSTKEY API wrappers
- Implemented hostkey/claude.sh (Claude Code agent)
- Implemented hostkey/openclaw.sh (OpenClaw agent)
- Added HOSTKEY to manifest.json clouds section
- Added matrix entries for all 15 agents (2 implemented, 13 missing)
- Updated test/record.sh with HOSTKEY test infrastructure
- Updated test/mock.sh with HOSTKEY URL handling
- Created hostkey/README.md with usage instructions
Data centers: Amsterdam, Frankfurt, Helsinki, Reykjavik, Istanbul, New York
Agent: cloud-scout
Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add Atlantic.Net Cloud as a new cloud provider with REST API support.
Starting at $4-8/mo for budget VPS instances with SSH access.
Implementation:
- Created atlanticnet/lib/common.sh with HMAC-SHA256 API auth
- Implemented 3 agent scripts: claude.sh, aider.sh, openclaw.sh
- Updated manifest.json with cloud entry and 15 matrix entries
- Added test coverage in test/record.sh and test/mock.sh
- Created atlanticnet/README.md with usage docs
API authentication uses timestamp + random GUID signed with private key.
Defaults: G2.2GB plan, ubuntu-24.04_64bit image, USEAST2 location.
Agent: cloud-scout-1
Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Reduces setup_env_for_cloud (84 lines -> 8 lines) and assert_cloud_api_calls
(32 lines -> 9 lines) in test/mock.sh by moving cloud-specific data into
per-cloud _env.sh and _api_assertions.sh files in test/fixtures/.
Adding a new cloud's test config now only requires creating two small files
in the fixtures directory instead of editing case branches in mock.sh.
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>
Add CodeSandbox as a new sandbox cloud provider for running AI agents.
CodeSandbox features:
- Firecracker microVMs with ~2 second start times
- SDK/CLI-based exec (no SSH)
- Free tier: 40 hours/month on Build plan
- Secure isolated environments
Implementation:
- Created codesandbox/lib/common.sh with SDK wrapper functions
- Implemented 3 agent scripts: claude, aider, openclaw
- Added CodeSandbox to manifest.json clouds
- Created matrix entries (3 implemented, 12 missing)
- Updated test/record.sh to list as non-recordable CLI cloud
- Added codesandbox/README.md with usage instructions
The implementation follows the existing pattern from e2b and modal,
using Node.js SDK (@codesandbox/sdk) for sandbox lifecycle management.
Agent: cloud-scout
Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The 5 per-cloud live recording functions (_live_hetzner, _live_digitalocean,
_live_vultr, _live_linode, _live_civo) each duplicated 50-65 lines of
identical create->save->extract-id->delete->save logic. Extract a generic
_live_create_delete_cycle helper that handles the shared flow, with per-cloud
body builder functions providing only the cloud-specific parts.
Reduces test/record.sh by 112 lines (1016 -> 904) while preserving all
behavior including cloud-specific delete delays and empty-response fallbacks.
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>
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#759Fixes#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>
((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>
- Replace hardcoded 4-cloud script list in run_shellcheck with dynamic
discovery that covers all 21 clouds automatically
- Convert 3 inline JSON templates (setup_claude_code_config,
setup_openclaw_config, setup_continue_config) from single-line printf
to readable heredocs while preserving json_escape security
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Break down the 150-line run_test() function into focused helpers:
- run_script_with_timeout(): script execution with env vars and timeout
- show_failure_output(): display last 20 lines on failure
- assert_error_scenario(): handle error scenario assertions
- assert_cloud_api_calls(): cloud-specific API call assertions
- record_test_result(): write pass/fail to RESULTS_FILE
run_test() is now 57 lines (62% reduction), each helper is under 35 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>
The mock curl heredoc script was a monolithic 287-line function with
inline arg parsing, error injection, URL routing, body validation,
fixture lookup, and state tracking all in one flow.
Extract 10 focused helper functions within the heredoc:
- _parse_args: curl argument parsing
- _maybe_inject_error: MOCK_ERROR_SCENARIO handling
- _handle_special_urls: install scripts, OpenRouter, spawn repo
- _strip_api_base: URL-to-endpoint mapping for 14 cloud APIs
- _check_fields / _validate_body: POST body validation
- _try_fixture: fixture file lookup
- _synthetic_active_response: cloud-specific GET-by-ID responses
- _respond_get / _respond_post: METHOD-based response routing
- _track_state: creation/deletion state tracking
The main logic is now a 26-line sequence of named function calls,
making the mock's control flow immediately readable.
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>
- Extract _create_logging_mock and _create_silent_mock from setup_mock_agents
(test/mock.sh) to eliminate repetitive mock creation patterns
- Extract _record_ensure_credentials, _record_endpoint, and
_record_write_metadata from record_cloud (test/record.sh) to separate
credential checking, API recording, and metadata writing concerns
Pure refactoring — no behavior changes.
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>
- binarylane/continue.sh: Replace unsafe inline echo with inject_env_vars_ssh
to prevent command injection if OPENROUTER_API_KEY contains single quotes
- test/record.sh: Pass credential values via sys.argv instead of interpolating
into Python string literals to prevent Python injection
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>
Adds a "local" cloud provider that installs and runs agents directly on the
user's machine without any cloud provisioning. This is useful for local
development and testing.
- local/lib/common.sh: Cloud lib with local execution functions
- local/claude.sh: Claude Code agent script
- local/openclaw.sh: OpenClaw agent script
- local/nanoclaw.sh: NanoClaw agent script
- manifest.json: Added local cloud + matrix entries
- test/: Updated record.sh and mock.sh for local cloud support
Fixes#378
Agent: issue-fixer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Security:
- Fix command injection in modal/lib/common.sh (run_server, upload_file, interactive_session)
- Fix command injection in fly/lib/common.sh (run_server, upload_file, interactive_session)
- All container providers now use printf '%q' for proper shell escaping
Complexity:
- Extract _api_should_retry_on_error() helper in shared/common.sh (-19 lines)
- Refactor scaleway_api and upcloud_api to use shared retry helper (-24 lines)
- Extract _save_fly_token() helper in fly/lib/common.sh (-11 lines)
- Extract validateAndGetAgent() in commands.ts, reducing cmdRun/cmdAgentInfo duplication
- Refactor cmdList column width calculation to use calculateColumnWidth()
UX:
- Add actionable next steps to error messages in shared/common.sh
- Improve CLI bash fallback error messages with guidance (spawn.sh)
- Add OAuth progress indicator during browser authentication wait
- Show invalid model ID value and link to openrouter.ai/models
- Add troubleshooting steps for agent installation failures
Tests:
- Update test assertions in test/run.sh to match refactored patterns
- All tests passing: 74 TypeScript + 75 bash = 149 total, 0 failures
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root causes:
- `clear` command fails with exit 1 when TERM is not set (test env has
no terminal), crashing the script due to set -e. Guard with || true.
- Test patterns for Claude settings/state uploads used old temp file
naming convention (/tmp/claude_settings, /tmp/claude_global) that no
longer matches the paths generated by upload_config_file +
upload_file_sprite (/tmp/*settings.json, /tmp/*.claude.json).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove tests for deleted nc_listen and create_oauth_response_html
functions. Replace echo -e with printf for macOS bash 3.x compat.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added default '*) ' case to handle agents without specific assertions,
resolving SC2249 info warning and improving code clarity.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed trap commands from double quotes to single quotes so variables
expand at trap execution time instead of definition time. This prevents
security issues where variables could be tampered with between trap
definition and execution.
Fixed 3 instances:
- cli/install.sh (2 instances): trap 'rm -rf "$tmpdir"' EXIT
- test/run.sh (1 instance): trap 'cleanup' EXIT
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Add NanoClaw spawn script
NanoClaw is a lightweight WhatsApp-based Claude AI assistant that runs
agents in isolated containers. This script sets up a sprite with
nanoclaw pre-configured: clones the repo, installs dependencies,
configures the API key, and launches in dev mode for WhatsApp QR auth.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* 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>
* Add test harness for spawn scripts
Mocks the sprite CLI and runs each script end-to-end verifying:
- common.sh sources correctly and all functions resolve
- Log functions write to stderr (not stdout)
- Env var flow (SPRITE_NAME, OPENROUTER_API_KEY)
- Sprite commands called in correct order
- Temp files created and cleaned up
- Each script reaches its final interactive launch
Usage: bash test/run.sh
42 tests, all passing.
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>