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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
- 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>
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>
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>
- 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>
- 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>
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>
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>
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>
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>
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>
- 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>
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>
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>
- 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>
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>
* 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>
- 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>
- 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>
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>
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>
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>
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>
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>
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>
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>