Commit graph

269 commits

Author SHA1 Message Date
A
a260dce642
test: add 133 tests for Webdock provider patterns and conventions (#1015)
Webdock was added in PR #1001 with zero dedicated test coverage.
This adds comprehensive tests validating:
- lib/common.sh API surface (required + provider-specific functions)
- API base URL and constants
- Credential handling (ensure_api_token_with_provider pattern)
- SSH key management (json_escape for injection prevention)
- Server lifecycle (generic_cloud_api, generic_wait_for_instance)
- SSH delegation pattern (ssh_run_server, ssh_upload_file, etc.)
- Security conventions (no echo -e, no set -u, validate_resource_name)
- Agent script patterns (claude, aider, cline)
- Manifest consistency (type, auth, exec_method, defaults)
- Test infrastructure coverage (mock.sh and record.sh entries)

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>
2026-02-13 14:11:47 -08:00
A
69d08e6b1d
fix: improve CLI UX with clearer credential status and help docs (#1012)
- Change 'auth: TOKEN' to 'needs TOKEN' with yellow highlight in spawn clouds
- Always show legend footer explaining ready/needs indicators
- Add --clear hint to spawn list footer
- Show --version/-v and --help/-h aliases in help text
- Document SPAWN_UNICODE=1 env var in help
- Include HTTP status code in update fetch errors
- Bump version to next patch

Fixes #1010

Agent: issue-fixer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 13:53:53 -08:00
A
bd28508fd8
test: add 40 tests for decomposed shared/common.sh helpers (#1009)
Tests cover the recently decomposed helper functions from PR #976
(cmdAgentInfo, generic_wait_for_instance) to ensure the refactored
helpers maintain correct behavior.

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 13:42:53 -08:00
A
d3919cafda
test: add 55 tests for agent info quick-start display (#1005)
Cover printAgentQuickStart (commands.ts) which has zero test coverage:
- Single-auth and multi-auth cloud credential display
- URL hint placement (only on first auth var)
- All/partial/no credentials detection ("ready to go" vs export lines)
- No-auth cloud (auth="none") handling
- Agent info header, install line, available clouds listing
- Credential prioritization in cloud ordering
- Grouped cloud type display and credential indicators
- Pure logic replica tests for quick-start computation

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>
2026-02-13 13:25:26 -08:00
A
ac56e5454a
fix: improve CLI UX with better error messages and help text (#1003)
- Show list-specific flags (-a, -c, --clear) in unknown flag error
- Add specific error for empty prompt files instead of generic validation
- Document SPAWN_UNICODE=1 env var in help text and troubleshooting
- Show filter/clear hints in interactive list picker

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-13 13:04:48 -08:00
A
583d2a63fc
test: add 38 tests validating test infrastructure stays in sync with manifest (#995)
Validates that test/mock.sh and test/record.sh stay in sync with
manifest.json. When a new cloud provider is added, CLAUDE.md mandates
updating both files with endpoint mappings, auth env vars, and API
dispatchers. These tests catch configuration drift automatically:

- ALL_RECORDABLE_CLOUDS completeness and no duplicates
- get_endpoints(), get_auth_env_var(), call_api() coverage parity
- _strip_api_base() URL patterns match fixture directories
- Fixture directories have required _env.sh and _metadata.json
- Auth env vars in record.sh match manifest auth fields
- Shell script conventions (shebang, set -eo pipefail, no echo -e)
- Test infrastructure conventions (NO_COLOR, cleanup traps, counters)

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>
2026-02-13 12:21:53 -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
fff84779dc
refactor: decompose cmdAgentInfo and generic_wait_for_instance into focused helpers (#976)
Extract printAgentQuickStart from cmdAgentInfo (63 -> 43 lines), paralleling
the existing printCloudQuickStart pattern. Extract _poll_instance_once and
_report_instance_timeout from generic_wait_for_instance (52 -> 20 lines),
eliminating duplicated elapsed/sleep/increment code in the polling loop.

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 11:48:07 -08:00
A
1d4e5b874c
fix: add defense-in-depth for SPAWN_HOME path validation and manifest JSON sanitization (#984)
- Validate SPAWN_HOME is an absolute path, reject relative paths to prevent
  unintended file writes (addresses #980)
- Resolve SPAWN_HOME to canonical form to collapse .. segments
- Strip __proto__, constructor, and prototype keys from parsed manifest JSON
  to prevent prototype pollution (addresses #979)
- Apply sanitization to all manifest ingestion paths (GitHub fetch, disk cache,
  local dev manifest)
- Add 12 tests covering path validation and JSON sanitization

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 11:37:10 -08:00
A
16b9132c7c
fix: add confirmation to history clear and improve UX details (#983)
- Add interactive confirmation prompt before clearing spawn history
  (spawn list --clear) to prevent accidental data loss
- Show total prompt length in dry-run preview when prompt exceeds 100
  characters, so users can verify the correct prompt was loaded
- Add "Rerun previous" suggestion to non-interactive terminal fallback
- Show "(shown first)" hint when clouds with credentials are detected
  in interactive picker, so users understand the sort order
- Add repository URL to spawn version output for discoverability

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-13 11:31:01 -08:00
A
3b4444f292
fix: show all auth vars in agent info quick start for multi-credential clouds (#975)
The `spawn <agent>` quick start section was only showing the first auth
env var when the best available cloud requires multiple credentials
(e.g., UpCloud with UPCLOUD_USERNAME + UPCLOUD_PASSWORD). This left
users confused about what other credentials they needed.

Now iterates over all auth vars, consistent with `spawn <cloud>` info.

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-13 11:18:28 -08:00
A
8c8511f9a2
test: add 67 tests for untested shared/common.sh helpers (#978)
Cover 8 functions that had zero test coverage:
- log_install_failed: actionable guidance for agent install failures (added across 126 scripts)
- ensure_jq: cross-platform jq installation
- get_cloud_init_userdata: cloud-init template structure and content
- _multi_creds_validate: credential validation with env var cleanup on failure
- check_python_available: dependency check with install guidance
- calculate_retry_backoff: exponential backoff with jitter bounds
- verify_agent_installed: agent command verification with diagnostic output
- _log_diagnostic: structured error reporting with causes and fixes
- _generate_csrf_state: CSRF token generation with multiple backends
- register_cleanup_trap / track_temp_file / cleanup_temp_files: temp file lifecycle

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 11:11:23 -08:00
A
37ae22ddb5
refactor: reduce complexity in Linode create_server and CLI dispatchCommand (#972)
Extract _linode_extract_error helper to deduplicate 3 inline Python
error-extraction blocks, and _linode_handle_create_error to reduce
create_server from 47 to 31 lines.

Extract dispatchListCommand, dispatchSubcommand, dispatchVerbAlias, and
dispatchSlashNotation from the 63-line dispatchCommand function, reducing
it to 15 lines with clear single-responsibility helpers.

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 10:58:40 -08:00
A
23351585b4
test: add 261 tests for cloud provider error guidance patterns (#973)
Validate that cloud provider lib/common.sh files include actionable
error guidance when operations fail (destroy_server, create_server,
auth). Tests cover dashboard URLs, billing warnings, structured
logging, API error extraction, URL format, timeout messages, and
auth credential references across all providers.

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>
2026-02-13 10:54:37 -08:00
A
118be14a23
test: add 70 tests for time formatting, auth parsing, and record helpers (#963)
Direct unit tests for exported functions in commands.ts that were
previously only exercised through replicas or integration paths:
formatRelativeTime, formatTimestamp, getImplementedAgents,
getImplementedClouds, parseAuthEnvVars, hasCloudCredentials,
resolveDisplayName, buildRecordLabel, and buildRecordHint.

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-13 10:18:38 -08:00
A
e163b38f09
test: add 44 tests for credential display functions in dry-run path (#961)
Adds unit tests for buildCredentialStatusLines, formatAuthVarLine, and
the credential section allSet detection in showDryRunPreview. These
functions had zero direct test coverage despite being in the critical
dry-run preview path. Tests cover:

- formatAuthVarLine: env var set/missing display, URL hints, indentation
- buildCredentialStatusLines: OPENROUTER_API_KEY always present, single
  and multi-var auth, URL hint placement, partial credentials, no-auth
  clouds, all-set scenarios
- Dry-run allSet detection: all creds set, partial, multi-var, none auth
- credentialHints allSet branch: the "appear to be set" path when all
  env vars are present but the error may be invalid/expired credentials
- credentialHints partial credentials: mixed set/missing env var states

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>
2026-02-13 09:51:49 -08:00
A
221ed67f2e
test: add 27 tests for clearHistory and cmdListClear (#955)
Add comprehensive test coverage for the previously untested clearHistory
(history.ts) and cmdListClear (commands.ts) functions invoked via
`spawn list --clear`.

Tests cover:
- Basic clearing: return value, file deletion, directory preservation
- Edge cases: corrupted JSON, non-array values, empty files, null
- Interaction: save-after-clear, filterHistory-after-clear, idempotency
- cmdListClear: log.info vs log.success output, singular/plural grammar,
  file deletion, corrupted file handling, large history counts

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 08:48:39 -08:00
A
68122a6f70
fix: remove duplicate ensure_jq and fix help text alias placement (#954)
- Remove duplicate ensure_jq() function in shared/common.sh (lines 2341-2372)
  that was accidentally left after extracting it to the shared lib in #946
- Move "Aliases: ls, history" onto the "spawn list" help line so it no longer
  appears to describe "spawn list --clear"

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 08:38:17 -08:00
A
0897f64f61
refactor: decompose Atlantic.Net and HOSTKEY create_server into focused helpers (#952)
- Atlantic.Net create_server (59 lines -> 30 lines):
  - Extract _atlanticnet_extract_error for API error message parsing
  - Extract _atlanticnet_check_create_error for error checking + diagnostics
  - Extract _atlanticnet_parse_instance_response for response parsing
  - Replace inline python3 with shared _extract_json_field helper
  - Reuse _atlanticnet_extract_error in atlanticnet_register_ssh_key

- HOSTKEY create_server (52 lines -> 24 lines):
  - Extract _hostkey_build_order_body for JSON body construction
  - Extract _hostkey_check_create_error for error checking + diagnostics
  - Extract _hostkey_parse_instance_response for response parsing

- Update atlanticnet-provider.test.ts to check extracted helpers

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 08:35:24 -08:00
A
ea39c8bf28
fix: prevent command injection in update-check reExecWithArgs (#951)
Replace execSync with execFileSync in reExecWithArgs() to prevent shell
metacharacter injection via binary path. execFileSync bypasses the shell
entirely, executing the binary directly with an argv array.

The performAutoUpdate() call retains execSync since it legitimately needs
a shell for piping (curl | bash).

Fixes #950

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 08:34:04 -08:00
A
904bebfb70
test: add 68 tests for run retry logic and display formatting helpers (#945)
Cover previously untested internal functions:
- formatCacheAge (index.ts): cache age to human-readable string conversion
- handleUserInterrupt (commands.ts): Ctrl+C detection in error messages
- runWithRetries (commands.ts): SSH failure retry logic with MAX_RETRIES
- printInfoHeader (commands.ts): agent/cloud info page header formatting
- printGroupedList (commands.ts): grouped display with type labels
- renderListTable (commands.ts): spawn history table output formatting

Includes boundary transition tests, edge cases, and integration scenarios.

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>
2026-02-13 07:24:49 -08:00
A
2e7b083f8f
fix: show cloud URL for missing credentials in dry-run and add spawn list --clear (#944)
Two UX improvements:

1. Dry-run credential status now shows the cloud provider's URL next to
   missing cloud-specific auth vars (e.g., HCLOUD_TOKEN), helping users
   find where to create their credentials. Previously only
   OPENROUTER_API_KEY showed a URL hint.

2. Added `spawn list --clear` command to let users clear their spawn
   history. Previously there was no way to reset the 100-entry history
   file without manually deleting ~/.spawn/history.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>
2026-02-13 07:16:14 -08:00
A
f806085bb6
fix: improve UX of error messages and status output (#938)
- Remove redundant "Warning:" prefix from API key format message (log_warn
  already conveys warning status)
- Fix incorrect `export VAR=token spawn ...` syntax in auth failure hint
  (export makes it persistent, inline env var syntax is correct)
- Replace attempt/retry jargon with elapsed time in SSH wait and instance
  polling messages (users care about time, not internal retry counts)
- Show instance IP in friendlier "ready (IP: x.x.x.x)" format
- Move HTTP status codes from error title to body in download failures
  (cleaner error headline, details still available)
- Simplify dry-run credential warning (remove confusing double-negative
  "without --dry-run")
- Remove redundant "Warning:" prefix from extra arguments message

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-13 06:45:04 -08:00
A
f696f57129
test: add 44 tests for extract_api_error_message and generic_wait_for_instance (#798)
These two critical shared/common.sh functions had zero test coverage despite
being used across 4+ and 9 cloud providers respectively (10+ call sites each).

extract_api_error_message tests cover:
- All JSON error field patterns (message, error, error.message, error_message, reason)
- Field priority ordering
- Fallback behavior for invalid JSON, empty input, unrecognized fields
- Real-world API response formats (Hetzner, DigitalOcean, Vultr, Contabo)
- Edge cases (special characters, unicode, arrays, null)

generic_wait_for_instance tests cover:
- Successful polling (first attempt and multi-attempt)
- IP extraction from flat and deeply nested JSON
- Timeout behavior when status never reaches target
- Continued polling when API returns errors or invalid JSON
- Polling when status matches but IP is empty
- Logging output (progress, success, timeout)

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 06:25:25 -08:00
A
c0cb32f9ce
test: add 97 tests for list command output helpers (#846)
* test: add 97 tests for list command output helpers

Cover buildRetryCommand (prompt truncation at 80 chars, quote escaping,
prompt-file fallback), resolveDisplayName (null manifest fallback),
buildRecordLabel/buildRecordHint (30-char hint truncation, picker
formatting), parseAuthEnvVars (multi-var parsing, validation),
hasCloudCredentials (multi-var auth, empty/unset vars),
getImplementedClouds/getImplementedAgents (manifest filtering),
isRetryableExitCode (SSH 255 detection), formatTimestamp (edge cases),
and getStatusDescription (404 special case).

Agent: test-engineer
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: import actual functions instead of duplicating them in tests

- Export formatTimestamp, buildRecordLabel, buildRecordHint from commands.ts
- Replace 11 duplicated function implementations with imports from commands.ts
- Add @clack/prompts mock (required when importing commands.ts)
- All 97 tests still pass against the real production code

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

* fix: resolve rebase conflicts and update tests for formatRelativeTime

Merged formatRelativeTime from main, exported formatTimestamp and
buildRecordHint, updated tests to use relative time assertions.

Agent: pr-maintainer
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 Haiku 4.5 <noreply@anthropic.com>
2026-02-13 06:22:18 -08:00
A
c833f4ed3e
fix: improve UX with macOS compat fix, clearer messages, and less alarming prompts (#934)
- Fix macOS compatibility bug in Atlantic.Net API signature: `base64 -w 0`
  fails on macOS (no `-w` flag); add fallback like other providers
- Replace misleading "Use 'csb' CLI dashboard" in CodeSandbox interactive
  session with link to the actual web terminal at codesandbox.io/dashboard
- Soften preflight credential check prompt from "will likely fail" to
  "will prompt you to authenticate" (scripts have built-in auth flows)
- Bump CLI version to 0.2.72

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-13 06:10:47 -08:00
A
75d7be29a4
fix: improve UX for stale manifest cache, list rerun hints, and version info (#805)
- Show warning when manifest is loaded from stale cache (offline fallback)
  so users know the data may be outdated
- Fix list footer rerun command: reuse buildRetryCommand instead of
  truncating prompts with "..." which produced broken copy-paste commands
- Show manifest cache age in "spawn version" output for troubleshooting
- Bump CLI version to 0.2.67

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 06:09:56 -08:00
A
4c1a344a7a
test: add 59 tests for JSON extraction helpers in shared/common.sh (#804)
Cover _extract_json_field and extract_api_error_message functions that
were recently extracted (PRs #673, #767) but had zero test coverage.
These are critical infrastructure used by Hetzner, DigitalOcean, Vultr,
and Contabo for API error parsing and by generic_wait_for_instance for
status polling.

Tests cover:
- _extract_json_field: basic extraction, nested fields, default values,
  complex Python expressions, real-world cloud provider patterns, edge cases
- extract_api_error_message: all standard error field patterns (message,
  error, error.message, error.error_message, reason), field priority order,
  fallback behavior, real-world cloud provider error formats, edge cases

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>
2026-02-13 06:07:50 -08:00
A
099d8020cc
test: add 343 cloud lib source chain verification tests (#935)
Verify that every cloud provider's lib/common.sh correctly sources
shared/common.sh and exposes required shared functions. Tests run
each cloud's lib in a real bash subprocess to catch source chain
breaks, syntax errors, and missing function definitions.

Coverage:
- Source chain integrity for all 36 cloud lib files
- Required shared function availability (logging, OAuth, API, SSH)
- json_escape behavior (quotes, newlines, backslashes, tabs)
- validate_api_token and validate_server_name security
- calculate_retry_backoff bounds
- extract_api_error_message parsing
- Cross-cloud consistency (SSH_OPTS, API helpers)
- bash -n syntax check on all lib files

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>
2026-02-13 06:02:06 -08:00
A
5948de15b8
fix: show 'ready to go' in quick start when all credentials are set (#866)
When all required credentials (OPENROUTER_API_KEY + cloud auth vars) are
already configured, the Quick start section in `spawn <agent>` and
`spawn <cloud>` now shows a concise "credentials detected -- ready to go"
message with just the launch command, instead of showing export instructions
the user doesn't need.

Previously, the `hasCreds` variable was computed but unused in both
`printCloudQuickStart` and `cmdAgentInfo`. This change puts it to use
to give users a clear signal when they're ready to launch.

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 05:58:22 -08:00
A
6589fd1f2f
refactor: extract helpers from performAutoUpdate in update-check.ts (#881)
Break down the 70-line performAutoUpdate function (depth-4 nesting, mixed
concerns) into focused helpers:

- shellQuote: reusable shell-quoting utility
- printUpdateBanner: boxed update notification formatting
- reExecWithArgs: binary re-exec with exit code forwarding
- performAutoUpdate: clean 22-line orchestrator

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 05:53:33 -08:00
A
ebc5a6cc2f
test: add 84 tests for interactive input validation helpers in shared/common.sh (#880)
Cover get_resource_name, get_validated_server_name, get_model_id_interactive,
interactive_pick, _display_and_select, and show_server_name_requirements --
all previously untested functions used by every agent/cloud script.

Tests exercise env-var bypass paths (critical for CI/non-interactive use),
validation rejection of injection attempts, boundary conditions, and menu
rendering output.

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>
2026-02-13 05:47:30 -08:00
A
6c762494e2
test: add Atlantic.Net and CodeSandbox provider pattern tests (268 tests) (#928)
Validates provider-specific patterns for the two most recently added clouds:
- Atlantic.Net: HMAC-SHA256 signing, query-param API, SSH delegation, dual-credential auth
- CodeSandbox: Node.js SDK exec, sandbox ID validation, env-var-based injection security
- Cross-provider contrast tests verifying SSH vs SDK architecture divergence
- Manifest consistency checks for both providers

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 05:47:01 -08:00
A
00f8913f20
fix: show credential readiness in spawn clouds and relative timestamps in spawn list (#910)
Two UX improvements:

1. `spawn clouds` now shows a green "ready" indicator next to clouds where
   credentials are already configured in the environment, making it immediately
   clear which providers the user can use without additional setup.

2. `spawn list` now shows relative timestamps ("5 min ago", "yesterday",
   "3d ago") instead of absolute dates, giving immediate temporal context.

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-13 05:20:04 -08:00
A
795a502efb
test: add comprehensive Atlantic.Net provider tests (165 tests) (#899)
Adds test coverage for the Atlantic.Net cloud provider (added in PR #883),
which had zero test coverage. Tests validate:

- lib/common.sh structure, API surface, and shell conventions
- HMAC-SHA256 signature auth flow correctness
- Security patterns (credential storage, URL encoding, config permissions)
- Credential management flow (env -> config -> prompt chain)
- SSH delegation pattern to shared helpers
- Server lifecycle functions (create, destroy, response parsing)
- Default parameter helpers and manifest consistency
- All 3 implemented agent scripts (claude, aider, openclaw)
- Agent-specific setup patterns and error handling
- API wrapper parameter handling
- README documentation

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 05:11:31 -08:00
A
74b9535457
test: add 85 tests for run-path credential display and validation functions (#918)
Tests prioritizeCloudsByCredentials (zero prior coverage), credential status
display logic, entity validation, key resolution, retry command building,
retryable exit code detection, and failure guidance for the critical
spawn <agent> <cloud> run path.

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 05:11:24 -08:00
A
2b9a812433
test: add CodeSandbox cloud provider pattern tests (202 tests) (#922)
Comprehensive test coverage for the CodeSandbox provider (merged in #857)
which previously had zero dedicated tests. Validates:

- Manifest integration (type, auth, exec_method, matrix entries)
- lib/common.sh API surface (13 required functions, no SSH leakage)
- SDK security: all 5 SDK functions pass user data via env vars
- Sandbox ID validation (regex, error handling, called by consumers)
- upload_file() security (path injection protection, base64 encoding)
- Authentication flow (ensure_api_token_with_provider delegation)
- create_server/destroy_server/list_servers SDK patterns
- Agent scripts follow standard provisioning flow (3 scripts)
- macOS bash 3.x compatibility (no echo -e, source <(), set -u)
- Node.js SDK code quality (try/catch, process.exit, process.env)
- No dangerous patterns (no eval, no unquoted expansions, no injection)

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-13 05:11:17 -08:00
A
7731306f37
test: add local cloud provider pattern tests (239 tests) (#911)
Adds comprehensive test coverage for the local cloud provider, which
runs agents directly on the user's machine without cloud provisioning.
Previously had zero dedicated tests despite 14 implemented agent scripts.

Tests cover:
- local/lib/common.sh API surface (no-op destroy, bash -c exec, cp uploads)
- All 14 local agent scripts follow local-specific patterns
- No SSH/SCP patterns leak into local scripts
- OpenRouter API key handling with OAuth fallback
- SPAWN_PROMPT handling for interactive/non-interactive modes
- Installation verification (command -v checks)
- Safety checks (no sudo, no rm -rf system dirs)
- Manifest consistency for local cloud entries

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>
2026-02-13 04:02:45 -08:00
A
7a441813fd
fix: detect slash notation and suggest correct syntax (#859)
When users type `spawn claude/hetzner` or `spawn hetzner/claude`,
the CLI now splits on the slash and forwards to the correct handler
with a helpful tip, instead of showing a confusing "invalid characters"
error from identifier 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-13 02:15:59 -08:00
A
fa5b4979e8
fix: upgrade SSH to StrictHostKeyChecking=accept-new (TOFU) and randomize temp paths (#849)
- Change SSH default from StrictHostKeyChecking=no to accept-new, which
  accepts host keys on first connection but rejects if they change later
  (Trust On First Use). This protects against MITM attacks on subsequent
  connections. Requires OpenSSH 7.6+ (released Oct 2017).
- Replace predictable $$-based temp file path in upload_config_file with
  $RANDOM to prevent symlink attacks on the remote server.

Addresses findings from issue #763.

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 02:11:47 -08:00
A
bfb125c028
test: add cloud lib API surface tests (#852)
Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 02:09:56 -08:00
A
ebdab346df
fix: warn about missing credentials before running spawn scripts (#851)
Previously, users would run `spawn claude hetzner` without HCLOUD_TOKEN
set, the CLI would download and start executing the script, and it would
fail mid-execution after potentially provisioning resources. Now the CLI
checks for missing credentials before running and warns the user upfront.

In interactive mode, shows a confirmation prompt so the user can abort
or continue. In non-interactive mode, shows a warning without blocking.

- Add preflightCredentialCheck() that inspects cloud auth env vars
- Call it in cmdRun before script execution
- 9 tests covering all credential states (all set, partial, missing,
  multi-var, CLI-based auth, none auth)
- Version bump to 0.2.69

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 01:52:41 -08:00
A
7b5f84141f
fix: show specific missing credentials in script failure messages (#813)
When a spawn script fails, the error message now checks which
required environment variables are actually set vs missing, instead
of generically saying "Missing or invalid credentials". This helps
users immediately see which credential they need to add.

- All set: "Credentials appear to be set (invalid or expired?)"
- Some missing: lists only the specific vars that are not set
- None set: lists all required vars

Version bump to 0.2.67.

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 01:45:01 -08:00
A
d9a18b49d3
fix: show credential-aware quick start in spawn <agent> and spawn <cloud> info (#817)
Prioritize clouds with detected credentials in spawn <agent> info pages.
Skip showing export instructions for env vars already set. Show credential
status in spawn <cloud> info header and available clouds list.

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 01:33:19 -08:00
A
2f671d8edf
test: add 66 tests for OAuth security functions in shared/common.sh (#814)
Cover previously untested security-critical OAuth functions:
- _generate_oauth_html: HTML generation for success/error pages
- _validate_oauth_server_args: port validation + CSRF state file
- _generate_oauth_server_script: Node.js server script generation
- cleanup_oauth_session: temp resource cleanup
- exchange_oauth_code: JSON injection prevention via json_escape
- execute_agent_non_interactive: prompt escaping with printf %q
- wait_for_oauth_code: timeout behavior
- _check_oauth_prerequisites: connectivity + runtime detection
- find_node_runtime: bun/node discovery

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>
2026-02-13 01:24:33 -08:00
A
1725fa79d4
test: add cloud lib security convention regression tests (69 tests) (#816)
Validates that all cloud provider lib/common.sh files follow security
conventions from the security audit. Tests cover SSH key encoding
(json_escape or python json.dumps), config file permissions, Python
code injection prevention, API body JSON safety, heredoc injection
prevention, shared/common.sh sourcing, and credential handling patterns.

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>
2026-02-13 01:23:20 -08:00
A
8446e785cf
test: add 88 tests for OAuth flow functions in shared/common.sh (#843)
The OAuth flow is the primary authentication mechanism for spawn users,
yet its component functions had zero test coverage. This adds tests for:

- validate_oauth_port: port range validation (boundary values, injection)
- _generate_csrf_state: CSRF token generation (entropy, uniqueness)
- _generate_oauth_html: success/error HTML page generation
- _generate_oauth_server_script: Node.js callback server (CSRF, ports)
- _validate_oauth_server_args: prerequisite validation (port, state, runtime)
- _init_oauth_session: temp directory and CSRF state file creation
- cleanup_oauth_session: PID and directory cleanup
- exchange_oauth_code: OAuth code-to-key exchange with json_escape security
- check_openrouter_connectivity: network reachability fallback chain
- Integration: session lifecycle and CSRF security properties

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>
2026-02-13 01:22:11 -08:00
A
6182348641
fix: show credential status in dry-run and specify missing env vars on failure (#841)
Two UX improvements:

1. `spawn <agent> <cloud> --dry-run` now shows a Credentials section that
   checks which env vars (OPENROUTER_API_KEY, cloud auth vars) are set vs
   missing, so users can verify readiness before a real run.

2. Script failure guidance (exit code 1 and default) now checks which
   specific env vars are unset instead of showing a generic "need X + Y"
   message, making it immediately clear what's missing.

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 01:20:21 -08:00
A
b1a576a52a
test: add 51 tests for _classify_api_result and _report_api_failure (#834)
These helpers were extracted from _cloud_api_retry_loop in PR #821 to
reduce cyclomatic complexity but had zero test coverage. They are
invoked on every cloud API call across all providers:

- _classify_api_result: Classifies curl/HTTP results into retry reasons
  (network error, rate limit 429, service unavailable 503) or empty
  (success/non-retryable error). Tests cover all branches including
  curl exit codes 1/6/7/28, HTTP 429/503, success codes 200/201/204,
  non-retryable errors 400-502, and edge cases.

- _report_api_failure: Generates user-facing error messages after
  retries are exhausted. Differentiates network vs HTTP errors,
  outputs API response body only for HTTP errors. Tests cover
  retry count display, response body handling, and special chars.

Also includes integration tests verifying the classify-then-report
pipeline and realistic cloud provider scenarios (Hetzner, DigitalOcean,
DNS failures, auth errors, validation errors).

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 01:19:31 -08:00
A
087a14c276
test: add agent env injection contract tests (128 tests) (#838)
Validates the critical contract that every implemented agent script
correctly injects the environment variables from manifest.json.
Catches silent breakage where an agent starts but cannot reach the
LLM API due to missing OPENROUTER_API_KEY or provider-specific vars.

Tests cover:
- OPENROUTER_API_KEY presence in all scripts
- Provider-specific env vars (ANTHROPIC_BASE_URL, OPENAI_BASE_URL, etc.)
- OpenRouter API key acquisition patterns (env check, OAuth, manual)
- Agent install and launch command references
- Cloud lib env injection infrastructure
- Base URL values pointing to openrouter.ai
- No hardcoded API keys (security)
- Full coverage statistics across all agents and clouds

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>
2026-02-13 01:19:14 -08:00