Commit graph

661 commits

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

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 07:46:56 -08:00
A
d47bbbd592 refactor: Automated improvements
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 15:27:49 +00:00
A
f95d2e4a97
test: add 114 security regression tests for upload_file across all clouds (#465)
Static analysis tests that verify every cloud's upload_file() function
uses safe patterns to prevent command injection. Tests cover:
- Path validation (single-quote, $, backtick rejection) or printf '%q' escaping
- Base64 content encoding before shell embedding
- printf '%s' for safe output (no echo with variable expansion)
- No eval on user-controlled input
- PR #453 regression tests for fly, northflank, daytona, e2b, koyeb
- Classification of all 30+ clouds into safe categories (ssh/scp/cp/exec-based)

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-11 07:26:28 -08:00
A
9d50d8101b
fix: improve user-facing messages and error text across CLI and shell scripts (#464)
- Cancel handling: use p.outro instead of red error text for user cancellation
- Exit code 130: warn that server may still be running instead of falsely claiming it isn't
- Download errors: hide internal URLs, show user-friendly "could not be found" message
- Compact list legend: use "not yet available" consistently instead of jargon "missing"
- Update messages: say "Run your spawn command again" instead of vague "Restart your command"
- API token errors: show friendly "special characters" message instead of listing forbidden chars
- OAuth fallback: explain this is normal on remote/SSH/headless environments
- Interactive picker: show what was entered and valid range on invalid selection
- Bump CLI version to 0.2.39

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-11 07:26:00 -08:00
A
3d274bf3d2
fix: escape shell commands and sanitize JSON to prevent injection (#463)
- Add printf %q command escaping to run_server/interactive_session in
  Koyeb, Render, Railway, and GitHub Codespaces (matching pattern used
  by E2B, Daytona, Northflank, Fly, and other providers)
- Use json_escape in exchange_oauth_code to prevent JSON injection via
  crafted OAuth codes in shared/common.sh
- Use json_escape in Fly.io _fly_create_app to prevent JSON injection
  via FLY_ORG env var, plus add validation for org slug format
- Pass Fly.io _fly_create_machine values via env vars instead of Python
  string interpolation to prevent code injection

Agent: security-auditor

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 07:20:41 -08:00
A
c1085f076a
refactor: reduce complexity in IONOS and Scaleway provider libraries (#462)
IONOS: Replace hand-rolled curl calls with generic_cloud_api_custom_auth,
eliminating the duplicated GET/DELETE vs POST branch and adding retry
logic that was previously missing.

Scaleway: Extract duplicated Python image-lookup into _scaleway_pick_ubuntu_image
helper and consolidate the two-pass search into a loop, reducing
get_ubuntu_image_id from 47 to 18 lines.

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-11 07:18:41 -08:00
A
a445ee142c
feat: Add Open Interpreter support for RamNode (#461)
Agent: gap-filler-ramnode-2

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 07:11:27 -08:00
A
54e32cec4f
feat: Add amazonq support for Netcup (#460)
Agent: gap-filler-netcup

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 07:00:23 -08:00
A
7faab3f88f
feat: Add goose support for Local Machine (#458)
Agent: gap-filler-local-2

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 07:00:19 -08:00
A
245a7b9634
refactor: reduce complexity in IONOS and UpCloud credential and provisioning functions (#457)
Extract focused helpers from the most complex functions in ionos/lib/common.sh
and upcloud/lib/common.sh to reduce line count and nesting depth.

IONOS (3 functions refactored):
- _ionos_create_boot_volume: 60 -> 24 lines (extracted _ionos_build_volume_body and _ionos_wait_for_volume)
- ensure_datacenter: 56 -> 18 lines (extracted _ionos_find_existing_datacenter and _ionos_create_datacenter)
- ensure_ionos_credentials: 47 -> 24 lines, max nesting 16 -> 8 (extracted _ionos_load_config_credentials and _ionos_prompt_credentials)

UpCloud (1 function refactored):
- ensure_upcloud_credentials: 57 -> 27 lines, max nesting 12 -> 8 (extracted _upcloud_load_config_credentials and _upcloud_prompt_credentials)

No behavior changes - pure refactoring with helper extraction and early returns.

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-11 06:57:02 -08:00
A
1316f9f609
fix: show runtime/platform in spawn version, clarify compact list legend (#456)
- `spawn version` now shows runtime (bun/node), version, platform,
  and architecture for easier bug reporting and diagnostics
- Compact list legend changed from confusing "N/N" to descriptive
  "green = all clouds  yellow = some missing"
- Bump CLI to v0.2.38

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-11 06:55:24 -08:00
A
b4580b4f8f
test: fix 6 broken tests and add coverage for exit code branches (#455)
The getScriptFailureGuidance function was updated (PRs #449, #450) to
add dedicated handlers for exit codes 130, 137, 255, and 2, but the
existing tests still expected these to fall through to the default case.

Fixed 4 broken assertions in script-failure-guidance.test.ts and 2 in
exec-script-errors.test.ts. Added 16 new tests covering the specific
guidance for each newly handled exit code (130=Ctrl+C, 137=OOM/killed,
255=SSH failure, 2=shell syntax error).

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-11 06:55:00 -08:00
A
8e5f9d46d4
refactor: reduce complexity in try_oauth_flow and ensure_ovh_authenticated (#454)
Extract helpers from the two longest functions in shared code:

- try_oauth_flow() (60 -> 37 lines): Extract _init_oauth_session() for
  temp dir + CSRF state setup, and _await_oauth_callback() for browser
  open + timeout handling
- ensure_ovh_authenticated() (67 -> 28 lines): Extract _ovh_prompt_credentials()
  for the interactive credential prompting, validation, and saving

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-11 06:28:54 -08:00
A
f1e8d946df
fix: secure upload_file functions against command injection in 5 clouds (#453)
Replace unsafe printf '%q'-escaped unquoted variables with validated
single-quoted embedding in upload_file() for fly, northflank, daytona,
e2b, and koyeb. The previous pattern used unquoted $escaped_content and
$escaped_path in command strings passed to bash -c or run_server, which
could allow command injection via crafted filenames.

The fix:
- Validates remote_path rejects unsafe chars (', $, `, newlines)
- Uses base64 content directly (alphanumeric + /+= is shell-safe)
- Single-quotes both content and path in the command string
- Uses printf '%s' instead of echo for safer output

Matches the pattern already used by render, modal, and railway.

Agent: security-auditor

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 06:28:45 -08:00
A
55fd4022e8
fix: improve error messages with actionable guidance for common failures (#452)
- Add signal exit code handling (130/Ctrl+C, 137/killed, 255/SSH failure, 2/syntax error)
- Replace vague "Cloud API retry logic exhausted" with attempt count and retry advice
- Add network troubleshooting hint to API network error after retries
- Clarify OAuth fallback prompt: explain why OAuth failed and what happens next
- Consolidate auth cancellation message with three clear recovery options

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 06:26:19 -08:00
A
f2c9af0d79
test: add 35 tests for getScriptFailureGuidance() exit code branches (#451)
Cover all exit code paths in getScriptFailureGuidance() which had zero
direct test coverage despite being recently modified (PRs #450, #449):
- Exit code 127: command not found guidance with tool listing
- Exit code 126: permission denied message
- Exit code 1: credential/API/provisioning failure hints
- Default case: generic troubleshooting for unknown exit codes
- null exit code: falls through to default
- Edge cases: 0, negative, large codes, signal codes (130, 137)
- Structure: return type validation, non-empty arrays, distinct output

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-11 06:25:54 -08:00
A
79cf25d210
fix: improve 404 error message in reportDownloadFailure() (#450)
The old message "This agent + cloud combination doesn't exist yet" was
misleading because by the time reportDownloadFailure runs, the agent/cloud
combination has already been validated in the manifest. A 404 means the
script file is missing from the server, not that the combination is invalid.

New message explains the script couldn't be found, suggests checking the
matrix, retrying, or reporting the issue. Also adds a hint about temporary
server issues for 500-level errors.

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 05:58:27 -08:00
A
4b76b3422c
refactor: reduce complexity in execScript and netcup pick functions (#449)
Extract error handling from execScript() into dedicated helpers
(reportDownloadError, reportScriptFailure, getScriptFailureGuidance),
reducing the function from 52 to 15 lines and making error guidance
directly testable.

Replace duplicated _pick_vps_product() and _pick_datacenter() in
netcup/lib/common.sh with calls to shared interactive_pick(),
eliminating ~60 lines of copy-pasted selection logic.

Net reduction: 42 lines (-98/+56).

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-11 05:56:48 -08:00
A
02ecd4123b
test: add 15 tests for cmdInteractive cancel and selection paths (#448)
cmdInteractive is the primary user entry point (bare `spawn` command) with
zero test coverage. Adds tests for: user cancels agent/cloud selection via
Ctrl+C, agent with no implemented clouds, happy path through selection to
script execution, intro/outro messaging, and run-directly hint.

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-11 05:55:27 -08:00
A
e9e5f1bfea
fix: improve error messages with actionable guidance (#447)
- validatePrompt max-length error now suggests --prompt-file alternative
- validateScriptContent shebang error explains likely download issue
- Compact list view now shows color legend (green=all, yellow=partial)
- Exit code 1 gets specific guidance (credentials, API, provisioning)
- cmdUpdate network error shows current version for context
- Bump CLI version to 0.2.37

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 05:20:31 -08:00
A
305a782fea
test: add 496 manifest type contract validation tests (#446)
Validates that every field in manifest.json conforms to TypeScript type
definitions (AgentDef, CloudDef) at runtime. Catches data quality issues
that truthiness-only checks miss:

- Required fields are correct types (string, not number/boolean/array)
- Agent env values are all strings with valid env var name keys
- Agent env references OPENROUTER_API_KEY (mandatory per CLAUDE.md)
- Optional fields (pre_launch, deps, config_files, interactive_prompts,
  dotenv, notes, defaults) have correct types when present
- dotenv has path (string) and values (Record<string, string>)
- interactive_prompts entries have prompt+default string fields
- config_files values are objects with path-like keys
- deps is an array of strings
- Cloud type values are lowercase
- Env var interpolation ${...} references valid env var names
- Launch commands don't contain dangerous shell metacharacters
- Display names are unique, agent keys don't collide with cloud keys
- Matrix covers all cloud/agent combinations exhaustively

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-11 05:20:13 -08:00
A
f79de27bc7
refactor: replace custom config loaders with shared helpers in 3 cloud libs (#445)
Migrate binarylane, northflank, and kamatera to use the shared
ensure_api_token_with_provider, _load_json_config_fields, and
_save_json_config helpers, removing ~120 lines of duplicated
token loading/saving/validation logic.

- binarylane: replace 50-line ensure_binarylane_token with
  ensure_api_token_with_provider + test_binarylane_token
- northflank: remove _load_northflank_config, _save_northflank_token,
  _northflank_login; consolidate into ensure_api_token_with_provider
  with test_northflank_token doing login + validation
- kamatera: replace inline python3 config loader with
  _load_json_config_fields, replace manual JSON save with
  _save_json_config

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-11 05:20:08 -08:00
A
c1e009a66e
refactor: replace custom config loaders with shared helpers in 3 cloud libs (#444)
Replace inline Python config loading (`_load_*_config`) and custom JSON
save functions (`_save_*_config`) in OVH, UpCloud, and Contabo with the
shared `_load_json_config_fields` and `_save_json_config` helpers.

Removes 85 lines of duplicated credential persistence code while
preserving identical behavior (env -> config file -> prompt -> save).

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-11 04:56:01 -08:00
B
e25bf29008 docs: Sync README matrix with manifest.json
Agent: team-lead
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 12:51:17 +00:00
A
88fa9e48e6
fix: prevent shell/Python injection in env var and credential handling (#443)
- binarylane/continue.sh: Replace unsafe inline echo with inject_env_vars_ssh
  to prevent command injection if OPENROUTER_API_KEY contains single quotes
- test/record.sh: Pass credential values via sys.argv instead of interpolating
  into Python string literals to prevent Python injection

Agent: security-auditor

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 04:50:34 -08:00
A
2bc95fdf15
test: add tests for validateImplementation branching (0, 1-3, >3 clouds) (#442)
Cover the untested code paths in validateImplementation (commands.ts
lines 233-256) that show different error messages based on how many
alternative clouds are available for an agent:
- 0 clouds: "no implemented cloud providers" + suggest "spawn list"
- 1-3 clouds: show all available clouds as examples
- >3 clouds: show first 3 examples + "Run spawn X to see all N options"
- Boundary: exactly 3 vs 4 clouds threshold
- Error message formatting (display names, singular/plural)

24 new tests, all passing (3865 total).

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-11 04:48:52 -08:00
A
d25cdd0da6
fix: use log_step for progress messages in netcup scripts (#441)
All netcup agent scripts were using log_warn (yellow) for routine
progress messages like "Installing...", "Setting up...", "Starting...".
These should use log_step (cyan) which was added specifically for
progress/status messages, reserving log_warn for actual warnings.

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 04:45:44 -08:00
A
10a40ca574
fix: add log_step for progress messages, fix misleading prompt error (#440)
- Add log_step() function (cyan) for status/progress messages
- Convert misused log_warn calls to log_step in shared/common.sh
  (14 instances: SSH key gen, agent verification, waiting, configuring)
- Convert representative cloud scripts: hetzner, digitalocean, sprite
- Fix misleading validatePrompt error that suggested --prompt-file as a
  workaround when it has the same validation

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 04:28:17 -08:00
A
14fc3ebbe6
feat: Add codex agent for Netcup (#437)
Agent: gap-filler-netcup-2

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 04:27:12 -08:00
A
e9e39f2032
feat: add netcup/gemini.sh implementation (#436)
Implements Gemini CLI on Netcup cloud infrastructure using the standard
pattern: provision VPS, install Gemini via npm, inject OpenRouter API
credentials, and launch interactive session.

Agent: gap-filler-netcup-5

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 04:27:08 -08:00
A
7ca3edc7ee
feat: Add Continue agent for Netcup (#439)
Agent: gap-filler-netcup-4

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 04:26:48 -08:00
A
e7a459a456
feat: Add nanoclaw agent for Netcup (#438)
Agent: gap-filler-netcup-1

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 04:26:43 -08:00
A
8ba9a14139
feat: Add Open Interpreter agent for Netcup (#435)
Agent: gap-filler-netcup-3

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 04:26:23 -08:00
A
88a5e9e844
refactor: extract shared config load/save helpers to reduce credential management complexity (#434)
Add _load_json_config_fields and _save_json_config to shared/common.sh,
replacing duplicated multi-python3-call patterns in IONOS (2 calls -> 1),
Netcup (3 calls -> 1 + inline python save -> helper), and RamNode
(3 calls -> 1 + inline python save -> helper).

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-11 04:25:22 -08:00
A
5722e13851
test: add 21 tests for execScript bash execution error handling (#433)
Cover the critical untested error paths in commands.ts when spawn scripts
fail with specific exit codes: 127 (command not found), 126 (permission
denied), 130 (Ctrl+C), and generic failures with troubleshooting advice.

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-11 04:24:09 -08:00
A
3e34133f54
test: fix non-TTY test expectations to match actual CLI behavior (#432)
Tests expected help output (USAGE/EXAMPLES) in non-TTY mode, but the CLI
actually shows a "No interactive terminal detected" hint with exit code 1.

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-11 04:09:53 -08:00
A
a1509ac8b3
fix: improve CLI error messages and cloud auth display (#431)
- Show URL hint only on the first auth var in multi-auth cloud quick-start
  (eliminates duplicate URLs for clouds like Contabo with 4 auth vars)
- Make script failure error messages exit-code-aware: show targeted guidance
  for command-not-found (127) and permission-denied (126) exit codes
- Consolidate generic failure hints into "Common causes" section

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-11 04:07:45 -08:00
A
4452f69144
refactor: extract helpers from create_server in binarylane, ramnode, upcloud (#430)
Extract response-handling and body-building helpers from the longest
create_server functions to improve readability and reduce per-function
complexity:

- binarylane: 69 -> 31 lines (extract _binarylane_build_server_body,
  _binarylane_handle_create_response)
- ramnode: 57 -> 40 lines (extract _ramnode_handle_create_response)
- upcloud: 56 -> 39 lines (extract _upcloud_handle_create_response)

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-11 04:06:32 -08:00
A
fdc5d5e58b
refactor: extract shared SSH helpers to eliminate ~410 lines of duplication (#429)
Add ssh_run_server, ssh_upload_file, ssh_interactive_session, and
ssh_verify_connectivity to shared/common.sh. These four functions
were copy-pasted identically across 21 cloud provider lib files,
differing only in SSH username (root vs ubuntu).

Providers now set SSH_USER and delegate to the shared helpers via
one-line wrappers, reducing each provider's lib by ~20 lines.

Agent: complexity-hunter

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 03:45:18 -08:00
A
81f1630424
test: Add 65 tests for untested shared/common.sh functions (#428)
Cover critical bash functions that had zero test coverage:
- validate_oauth_port: port validation boundary cases and injection
- generate_env_config: shell export generation and value escaping
- calculate_retry_backoff: exponential backoff with jitter range
- _update_retry_interval: indirect variable update via printf -v
- _parse_api_response: HTTP response code extraction
- _api_handle_transient_http_error: retry exhaustion messaging
- get_cloud_init_userdata: cloud-init YAML content validation

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-11 03:45:13 -08:00
A
5c4f830fea
fix: improve credential guidance in error messages and quick-start hints (#427)
- Show cloud provider URL alongside credential env vars in quick-start
  sections (both `spawn <agent>` and `spawn <cloud>` info views)
- Restructure script failure errors: separate credential issues from
  other causes, inline the `spawn <cloud>` hint next to cloud credentials
- Replace "Check cloud-specific READMEs" with actionable `spawn <cloud>`
  in help troubleshooting section
- Show concise 4-line guidance instead of full help dump when spawn is
  run without a TTY (e.g. piped or in CI)
- Add `spawn <agent> <cloud>` as primary action in `spawn list` footer

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 03:42:55 -08:00
A
bb4f0c29df
fix: improve credential guidance in error messages and quick-start hints (#426)
- Add OPENROUTER_API_KEY as first item in script failure error (most common cause)
- Replace external GitHub README link with actionable `spawn <cloud>` command in error output
- Fix help text: auth section now correctly says `spawn <cloud>` instead of `spawn <agent> <cloud>`
- Add inline URL hint next to OPENROUTER_API_KEY in quick-start sections
- Add cloud name label next to cloud auth vars in agent info quick-start

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 03:21:15 -08:00
A
ba5cdd8c3c
refactor: extract helpers from create_server() in hetzner and hostinger (#425)
Extract JSON body builders and error handlers from the two largest
remaining create_server() functions (hetzner 84->43, hostinger 76->44
lines), following the pattern established in PR #423.

Extracted helpers:
- hetzner: _hetzner_build_create_body(), _hetzner_check_create_error()
- hostinger: _hostinger_build_create_body(), _hostinger_handle_create_error()

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-11 03:19:54 -08:00
A
2f03f746ec
test: Add 54 tests for warnExtraArgs and dispatchCommand routing (#424)
Cover the critical untested paths from PR #422:
- warnExtraArgs: singular/plural warning, boundary cases
- dispatchCommand: IMMEDIATE_COMMANDS, SUBCOMMANDS, default handler routing
- SUBCOMMANDS --help flag redirect behavior
- showVersion output format
- End-to-end dispatch 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-11 03:19:02 -08:00
A
de19996360
refactor: extract helpers from create_server() in 4 cloud providers (#423)
Extract wait-for-IP polling loops and JSON body builders from the
largest create_server() functions (ramnode 105->59, netcup 95->50,
cherry 80->57, binarylane 92->70 lines), following the pattern
already established in ionos/lib/common.sh.

Extracted helpers:
- ramnode: _ramnode_build_server_body(), _ramnode_wait_for_ip()
- netcup: _netcup_build_create_body(), _netcup_wait_for_ip()
- cherry: _cherry_wait_for_ip()
- binarylane: _binarylane_wait_for_active()

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-11 02:59:31 -08:00
A
0181b73506
fix: warn on extra args and detect mismatched agent/cloud types (#422)
- Warn when extra positional arguments are silently ignored (e.g. "spawn
  claude sprite hetzner" now shows that "hetzner" was ignored)
- Detect when user passes two agents (e.g. "spawn claude aider") and
  explain that the second arg should be a cloud, not an agent
- Detect when user passes two clouds (e.g. "spawn hetzner sprite") and
  explain that the first arg should be an agent, not a cloud
- Add tests for both new behaviors

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-11 02:58:34 -08:00
A
df733d786b
test: Add 34 tests for --prompt-file error handling paths (#421)
Tests handlePromptFileError formatting for ENOENT, EACCES, EISDIR, and
generic error codes. Also tests --prompt-file success path with real
files, edge cases (spaces in paths, dots, deeply nested paths, /dev/null),
mutual exclusion with --prompt, and missing cloud argument errors.

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-11 02:58:14 -08:00
A
d890740155
refactor: reduce complexity by extracting helpers from main(), cmdCloudInfo(), and resolvePrompt() (#418)
- Extract main() (73 lines) into handleNoCommand(), showVersion(), dispatchCommand() + main() (20 lines)
- Extract cmdCloudInfo() (53 lines) into printCloudQuickStart(), printAgentList() + cmdCloudInfo() (26 lines)
- Extract resolvePrompt() error handling into handlePromptFileError() (37 lines from 50)
- Move IMMEDIATE_COMMANDS and SUBCOMMANDS to module-level constants

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-11 02:37:32 -08:00
A
246b72a22b
fix: Prevent Python/JSON injection in RamNode and Netcup providers (#420)
Use sys.argv and sys.stdin instead of shell variable interpolation
in Python strings to prevent code injection via credentials, SSH keys,
server names, and other user-controlled inputs.

RamNode fixes:
- _get_ramnode_token: credentials via sys.argv instead of string interpolation
- Config file read: use sys.argv[1] for file path (matches other providers)
- Config file save: use sys.argv for all values
- ramnode_check_ssh_key: key_name via sys.argv
- ramnode_register_ssh_key: public key via stdin, name via sys.argv
- create_server: all parameters via sys.argv

Netcup fixes:
- netcup_get_session: use python3+json.dumps instead of unquoted heredoc
- netcup_api: use python3+json.dumps for action parameter
- Config file read: use sys.argv[1] for file path
- Config file save: use python3+sys.argv instead of unquoted heredoc
- create_server: all parameters via sys.argv

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-11 02:36:03 -08:00
A
42655b6e24
test: Add 57 tests for findClosestKeyByNameOrKey fuzzy matching (#419)
The findClosestKeyByNameOrKey function (added in PR #414) had zero direct
test coverage. This function is used in validateAgent, validateCloud, and
showInfoOrError for typo suggestions. Tests cover key-based matching,
display name matching, priority between key vs name matches, multiple
key competition, edge cases, and integration with manifest-like data.
Also adds boundary tests for levenshtein, findClosestMatch threshold,
and resolveAgentKey/resolveCloudKey display name 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-11 02:36:00 -08:00