Commit graph

319 commits

Author SHA1 Message Date
A
58823bcc4f
fix(ux): show credential readiness in agents list, matrix, and preflight check (#1061)
- `spawn agents` now shows "N ready" indicator when clouds have credentials
- `spawn matrix` compact view adds a "Ready" column showing credential count
- Preflight credential check gives context-specific guidance: mentions OAuth
  browser flow when only OPENROUTER_API_KEY is missing, improving clarity

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-14 01:55:52 -05:00
A
04e92fe727
test: add 178 tests for credential display, auth parsing, and internal helpers (#1063)
Adds comprehensive test coverage for previously untested or weakly tested
areas in commands.ts and index.ts:

- formatCredStatusLine: newly exported function with zero prior tests
- formatAuthVarLine (replicated): private helper for quick-start display
- groupByType (replicated): private helper for clouds/agents grouping
- formatCacheAge (replicated): version display cache age formatting
- parseAuthEnvVars: 14 edge cases (CloudSigma format, short strings, hyphens)
- hasCloudCredentials: 8 edge cases (empty values, multiple vars, none auth)
- getImplementedClouds/getImplementedAgents: nonexistent/empty manifest cases
- getMissingClouds: all/none/empty scenarios
- calculateColumnWidth: boundary and empty array cases
- prioritizeCloudsByCredentials/buildAgentPickerHints: empty/orphan agents
- resolveDisplayName: null manifest, unknown keys
- buildRecordLabel/buildRecordHint: null manifest, long prompts
- formatRelativeTime/formatTimestamp: all time buckets + invalid input
- getErrorMessage: null, undefined, boolean, object-without-message
- levenshtein: empty strings, symmetry, insertions/deletions
- findClosestMatch/findClosestKeyByNameOrKey: empty candidates, case sensitivity
- resolveAgentKey/resolveCloudKey: display name resolution, empty string
- checkEntity: swapped kind detection (agent as cloud, cloud as agent)
- getStatusDescription: 404 vs other HTTP codes
- isRetryableExitCode: all exit codes including empty/signal messages
- buildRetryCommand: prompt length boundary (80 vs 81 chars), quote escaping
- credentialHints: all-set, missing, custom verb
- getSignalGuidance: all signals + dashboard URL
- getScriptFailureGuidance: all exit codes + null + dashboard

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-14 01:49:41 -05:00
A
1af9878d0a
test: add 56 tests for shared/key-request.sh credential loading (#1057)
Add comprehensive test coverage for shared/key-request.sh, which had
zero existing test coverage. Tests cover:

- get_cloud_env_vars: env var extraction from manifest (7 tests)
- _parse_cloud_auths: manifest parsing for auth specs (6 tests)
- _try_load_env_var: single var loading from JSON config (10 tests)
- _load_cloud_credentials: multi-var credential loading (5 tests)
- load_cloud_keys_from_config: full credential loader (8 tests)
- invalidate_cloud_key: config deletion with path traversal guard (11 tests)
- request_missing_cloud_keys: key server request behavior (4 tests)
- Integration: end-to-end key loading scenarios (5 tests)

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-14 00:54:00 -05:00
A
44918b64c4
refactor: extract helpers to reduce function complexity (#1055)
- Extract `readPromptFile` from `resolvePrompt` in index.ts (60 -> 40 lines),
  isolating prompt-file validation and reading into a standalone helper
- Extract `formatCredStatusLine` from `buildCredentialStatusLines` in
  commands.ts, replacing repetitive set/not-set formatting with a reusable
  helper
- Extract `_aliyun_validate_create_params` and `_aliyun_run_instances` from
  `create_server` in alibabacloud/lib/common.sh (69 -> 34 lines), separating
  validation, API call, and orchestration concerns

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-14 00:41:39 -05:00
A
9336998168
fix(ux): add post-session summary to 10 exec-based cloud providers (#1056)
Users on exec-based clouds (Fly, Render, Koyeb, Northflank, Railway,
Modal, Daytona, E2B, CodeSandbox, GitHub Codespaces) got no warning
when their session ended that their service was still running and
incurring charges. This adds:

- _show_exec_post_session_summary() in shared/common.sh for non-SSH
  providers that use CLI exec commands instead of direct SSH
- SPAWN_DASHBOARD_URL for all 10 exec-based clouds so users get
  actionable dashboard links
- Post-session summary calls in each cloud's interactive_session()
- 33 new tests covering the exec post-session summary feature

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-14 00:38:10 -05:00
A
90c610a21d
refactor: simplify signal/failure guidance and credential formatting in CLI (#1053)
Convert getSignalGuidance from switch statement to data-driven lookup
table (SIGNAL_GUIDANCE), separating signal metadata from rendering logic.
Extract optionalDashboardLine helper to deduplicate the conditional
dashboard URL spreading in getScriptFailureGuidance. Extract
formatCredentialIndicator from cmdClouds to clarify the nested ternary
credential status formatting.

All 92 script-failure-guidance tests and 216 related tests pass with
zero regressions.

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-14 00:33:50 -05:00
A
aca3ca0316
test: add 237 edge-case tests for core CLI modules (#1054)
Covers edge cases in manifest.ts, security.ts, commands.ts, index.ts,
and history.ts not addressed by existing tests or open PRs #1050/#1051.

Key areas tested:
- stripDangerousKeys: deep nesting, arrays, all-dangerous-keys objects
- validatePromptFilePath: path traversal combos, all sensitive path patterns
- validatePromptFileStats: boundary at exactly 1MB, empty files, non-files
- validateIdentifier: boundary at 64/65 chars, various invalid characters
- validateScriptContent: fork bomb, destructive ops, HTML error pages
- validatePrompt: backtick detection, boundary at 10KB, injection patterns
- checkEntity: swapped agent/cloud detection, typo correction
- cmdHelp: content verification (all subcommands, flags, env vars, sections)
- expandEqualsFlags: equals-in-value, empty value, mixed arg types
- history: combined filters, case-insensitive filters, corrupted files
- parseAuthEnvVars: multi-auth, short vars, non-env-var auth strings
- buildRetryCommand: quote escaping, long prompt threshold
- isRetryableExitCode: SSH exit 255 vs other codes
- getSignalGuidance/getScriptFailureGuidance: all signal/exit code branches
- hasCloudCredentials: multi-auth partial setup
- formatRelativeTime: all time ranges including future timestamps

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-14 00:26:38 -05:00
A
b1c2ad0a0d
test: add 159 edge-case tests for commands.ts exported helpers (#1051)
Comprehensive tests covering boundary conditions and interactions
between exported helpers in commands.ts:

- parseAuthEnvVars: unusual auth formats, multi-var, edge patterns
- hasCloudCredentials: env var presence/absence, multi-var auth
- resolveAgentKey/resolveCloudKey: display name resolution, case
  sensitivity, hyphenated keys, cross-kind rejection
- getImplementedClouds/getImplementedAgents: large manifest fixtures
- getMissingClouds: partial/full/zero implementation coverage
- prioritizeCloudsByCredentials: mixed credential states, multi-var
- buildAgentPickerHints: cloud counts, singular/plural, ready status
- formatRelativeTime: future dates, extreme dates, invalid input
- formatTimestamp: epoch, invalid, empty string
- buildRetryCommand: quote escaping, long prompt threshold, newlines
- isRetryableExitCode: all code paths including edge codes
- getErrorMessage: Error, plain object, primitives, null, undefined
- getSignalGuidance: all signal branches with/without dashboard URL
- getScriptFailureGuidance: all exit codes with auth/dashboard hints
- credentialHints: multi-var auth, partial/full/no credentials
- resolveDisplayName: null manifest, unknown keys
- buildRecordLabel/buildRecordHint: null manifest, prompt truncation
- levenshtein: symmetry, empty strings, transposition
- findClosestMatch: large candidate lists, empty list, case handling
- calculateColumnWidth: empty array, single char, varying lengths

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 23:37:22 -05:00
A
44b9a5bdff
fix(security): harden weak crypto fallbacks, key validation, and temp paths (#1039)
* fix(security): harden weak crypto fallbacks, key validation, and temp paths

- CSRF state generation: fail instead of using predictable date+$RANDOM
  fallback when openssl and /dev/urandom are unavailable (OAuth CSRF bypass)
- Kamatera password: fail instead of using predictable date-based password
  when no secure random source available
- key-server validKeyVal: enforce 8-512 char limits and ASCII-only check
  to block malformed/oversized values (Fixes #969)
- upload_config_file: use mktemp-derived randomness for remote temp paths
  instead of predictable $RANDOM (symlink attack on remote server)

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

* fix(test): update assertions for upload_config_file mktemp-derived paths

The upload_config_file function now uses mktemp-derived basenames
(spawn_config_tmp.XXX) instead of the original filename for remote temp
paths. Update test/run.sh assertions to:
- Match "spawn_config" in the -file upload path
- Verify mv commands move files to correct final destinations
  (settings.json, .claude.json)

Addresses reviewer feedback on PR #1039.

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 Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-13 21:43:37 -05:00
A
881067bf8b
test: add 50 tests for unified printQuickStart and buildDashboardHint helpers (#1042) (#1044)
Cover the unified printQuickStart function and extracted buildDashboardHint
helper from PR #1042. Tests verify:

- buildDashboardHint edge cases: empty string URL fallback, very long URLs,
  consistency across signal types, per-exit-code dashboard URL inclusion
- printQuickStart unified behavior via cmdCloudInfo: single-auth, no-agent,
  ready-to-go shortcut, non-parseable auth, none-auth
- printQuickStart unified behavior via cmdAgentInfo: credential-prioritized
  cloud ordering, no-implementations, single-cloud ready-to-go
- cmdCloudInfo agent list count display and metadata
- cmdAgentInfo cloud list count display and metadata

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 21:43:23 -05:00
A
dde41b1357
refactor(cli): unify quick-start printing and extract dashboard hint helper (#1042)
Merge printAgentQuickStart and printCloudQuickStart into a single
printQuickStart function, eliminating duplicated credential-checking and
auth-var-line printing logic. Extract buildDashboardHint from the
identical pattern repeated in getSignalGuidance and getScriptFailureGuidance.

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 20:54:13 -05:00
A
122b59e4da
test: add 99 tests for post-session summary and SPAWN_DASHBOARD_URL convention (#1040)
Cover the _show_post_session_summary function and updated
ssh_interactive_session integration from PR #1037. Tests verify:

- Summary warns user their server is still running with IP
- Dashboard URL shown when SPAWN_DASHBOARD_URL is set
- Generic message when no dashboard URL is available
- Reconnect command uses correct SSH_USER and IP
- SSH exit code preserved through the summary display
- All 25 SSH-based cloud providers set SPAWN_DASHBOARD_URL
- SPAWN_DASHBOARD_URL uses HTTPS and is defined before usage
- Detects custom interactive_session implementations missing summary
  (alibabacloud flagged as known gap)

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 20:46:40 -05:00
A
beceb69962
test: add 151 tests for key-server security-critical logic (#1036)
Add comprehensive test coverage for the key-server
(.claude/skills/setup-agent-team/key-server.ts), which previously had
zero tests despite containing security-critical logic:

- validKeyVal: API key validation (control chars, shell metacharacters,
  length limits) - 37 tests
- SAFE_PROVIDER_RE: path traversal prevention in provider names - 21 tests
- UUID_RE: batch ID format validation - 12 tests
- signHmac/verifyHmac: HMAC signing and verification for signed URLs - 17 tests
- isAuthed: timing-safe Bearer token auth - 9 tests
- rateCheck: rate limiting logic - 8 tests
- esc: HTML escaping for XSS prevention - 13 tests
- cleanup: data store batch expiry logic - 9 tests
- Key submission validation flow - 6 tests
- Route matching, security headers, backward compat - 19 tests

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 20:11:35 -05:00
A
8c052aceb8
test: add 239 tests for CloudSigma provider patterns and conventions (#1030)
Validates CloudSigma's unique architecture: region-based API URLs,
HTTP Basic Auth (email + password), drive cloning workflow, python3
JSON construction, SSRF-preventing region validation, and SSH with
'cloudsigma' user. Covers lib/common.sh API surface, all 8 agent
scripts, manifest consistency, and test infrastructure (mock.sh +
record.sh).

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 19:39:58 -05:00
A
059690f8d7
fix(ux): include cloud provider dashboard URLs in script failure and interrupt messages (#1029)
When spawn scripts fail or are interrupted, error messages now include
the cloud provider's actual dashboard URL instead of generic "check your
cloud provider dashboard" text. This helps users quickly navigate to
their provider to check server status, clean up orphaned resources, or
debug provisioning failures.

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 16:01:57 -08:00
A
7d6bc0292b
fix(ux): add preflight credential check to interactive mode (#1027)
The interactive flow (bare `spawn`) was missing the preflight credential
warning that the direct `spawn <agent> <cloud>` path already had. Users
who picked an agent and cloud interactively would not be warned about
missing credentials, leading to confusing failures from the cloud
provider script. Now both paths warn about missing credentials before
launching.

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 18:52:03 -05:00
A
b6a07e3c60
fix: prevent sensitive file exfiltration via --prompt-file flag (#1024)
Add path validation to --prompt-file to block reading sensitive files
(SSH keys, cloud credentials, .env files, etc.) whose contents would be
sent to remote agents. Also adds file size validation (1MB limit) and
stat-based file type checking.

Fixes #991

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 18:30:05 -05:00
A
b76f04cd78
fix(ux): show cloud count and credential readiness in interactive agent picker (#1025)
When users run `spawn` interactively, the agent picker now shows how many
clouds each agent supports and how many have credentials ready. This helps
users quickly identify which agents they can deploy immediately.

Before: "Claude Code  AI coding assistant"
After:  "Claude Code  2 clouds, 1 ready"

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 18:29:25 -05:00
A
46b760cf2b
test: add 364 tests for Oracle Cloud Infrastructure provider patterns (#1023)
Covers OCI CLI dependency management, VCN networking decomposition
(VCN -> IGW -> route -> security rules -> subnet), instance creation
with flex shape handling, cloud-init userdata, SSH delegation,
server destruction, availability domain handling, and all 15 agent
scripts following correct provisioning flow.

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 18:07:41 -05:00
A
aafe3d1ce4
fix: eliminate duplicate Loading manifest spinner in agent/cloud info (#1021)
When running `spawn claude` or `spawn hetzner`, the "Loading manifest..."
spinner appeared twice: once in showInfoOrError() and again in
cmdAgentInfo/cmdCloudInfo via validateAndGetEntity(). Pass the
pre-loaded manifest to avoid the redundant load and spinner flash.

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 18:07:08 -05:00
A
41afc76537
test: add 157 tests for Alibaba Cloud provider patterns and conventions (#1020)
Alibaba Cloud was added in commit 0d9307a with zero test coverage.
This adds comprehensive tests covering:
- lib/common.sh API surface (required + provider-specific functions)
- CLI installation and credential handling
- SSH key management (DescribeKeyPairs, ImportKeyPair)
- Server lifecycle (VPC, vSwitch, SecurityGroup, RunInstances)
- Network infrastructure setup (CIDR ranges, availability zones)
- Instance polling behavior
- Security conventions (input validation, safe JSON parsing, macOS compat)
- Agent script patterns (claude.sh, codex.sh, gemini.sh)
- OpenRouter env var injection via SSH
- Manifest consistency checks

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 18:00:55 -05:00
A
beec9ab8a3
fix: show signal names instead of 'code null' when scripts are killed (#1014)
When a spawn script is killed by a signal (SIGKILL, SIGTERM, SIGHUP, etc.),
Node.js returns exit code null. Previously this produced the confusing message
"Script exited with code null". Now detects the actual signal and shows
signal-specific guidance: OOM suggestions for SIGKILL, terminal reconnection
tips for SIGHUP, spot instance warnings for SIGTERM.

Fixes #1011

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 14:12:43 -08:00
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