Improve error messages in shared utilities and cloud providers that
previously showed bare "Failed to..." messages without telling users
how to fix the problem.
Shared (shared/common.sh):
- generate_ssh_key_if_missing: handle ssh-keygen/mkdir failures with
disk space and permission guidance
- get_ssh_fingerprint: detect missing/corrupt public key files with
regeneration instructions
- generic_ssh_wait: structured "How to fix" with manual SSH test command
and firewall check
- _report_api_failure: add DNS/firewall/proxy guidance for network errors
- ensure_jq: platform-specific install commands for unknown package
managers, hash rehash hint after install
- get_openrouter_api_key_manual: structured guidance after 3 failed
attempts
Cloud providers:
- Contabo: actionable guidance for OAuth token failures
- Exoscale: guidance for credential validation and CLI download failures
- Netcup: network connectivity hint for API connection failure
- Scaleway: structured guidance for project ID lookup failure
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add log_install_failed helper to shared/common.sh that provides
structured troubleshooting for agent install failures: possible causes,
SSH debug command (when server IP available), manual install command,
and re-run suggestion. Also improve SSH key registration error message.
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>
Improve error messages in cloud provider lib/common.sh files to include
specific troubleshooting steps, dashboard URLs, and environment variable
hints instead of bare "Failed" messages.
Providers improved: Netcup, IONOS, CloudSigma, Northflank, UpCloud,
Fly.io, RamNode, OVH, Civo, Scaleway.
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 server destruction fails, users are left with a bare error message and
no indication that they may still be billed for a running server. This adds
dashboard URLs and clear warnings to destroy_server errors across 9 clouds
(Hetzner, UpCloud, Contabo, Netcup, RamNode, Hostinger, HOSTKEY, OVH,
Latitude). Also improves error messages for Koyeb (app creation, service
deployment, deployment timeout, instance ID), GitHub Codespaces (creation
failure, readiness timeout), and E2B (sandbox creation failure).
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>
Hetzner lib: replace all Python JSON parsing with jq. Uses the
/datacenters API as the authoritative source for server type
availability (server_types.available), cross-referenced with
/server_types for specs and pricing. jq is auto-installed if missing.
URLs: update openrouter.ai/lab/spawn → openrouter.ai/labs/spawn
across all READMEs and CLI source.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: implement netcup/kilocode integration
Add Kilo Code support on Netcup VPS provider. This fills a missing matrix entry by combining Netcup cloud primitives with Kilo Code agent setup.
Changes:
- netcup/kilocode.sh: New script using Netcup API provisioning + Kilo Code CLI
- netcup/README.md: Added Kilo Code to available agents list
- manifest.json: Updated netcup/kilocode from "missing" to "implemented"
Agent: gap-filler-1
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: address review findings in netcup/kilocode.sh
- Add missing upload_file/run_server args to inject_env_vars_ssh (HIGH -
prevented credential leak where API key was passed as upload_func)
- Add wait_for_cloud_init call after verify_server_connectivity (MEDIUM)
- Add shellcheck source directive and SC2154 disable
- Add server info to completion message
Agent: pr-maintainer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Sprite <noreply@sprites.dev>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Consistently use log_step for progress/status messages ("Waiting for...",
"Fetching...", "Creating...") and reserve log_info for success/completion
messages. This gives users a clear visual distinction between operations
that are still running (cyan) vs operations that have completed (green).
Also adds periodic progress updates to silent polling loops in ramnode,
cherry, and netcup IP wait functions so users see activity during long waits.
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>
Implements Plandex on Netcup VPS using netcup/lib/common.sh primitives.
Updates manifest.json to mark netcup/plandex as implemented.
Updates netcup/README.md with Plandex usage instructions.
Agent: gap-filler-netcup
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
18 cloud lib/common.sh files had identical 7-line get_server_name()
functions (get_resource_name + validate_server_name + echo). Added a
shared get_validated_server_name helper to shared/common.sh and replaced
all duplicates with one-line delegations. Net -110 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>
~1500 progress messages across 481 files were using log_warn (yellow)
for normal status updates like "Installing...", "Setting up...",
"Creating server...", etc. This made users think something was wrong
when everything was proceeding normally.
Changes:
- Replace log_warn with log_step for all progress/status messages
- Keep log_warn only for actual warnings (errors, remediation hints)
- Remove emoji from 3 sprite completion messages
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Replace log_warn (yellow) with log_step (cyan) for progress messages
like "Installing...", "Setting up environment variables...", and
"Starting..." in recently-added scripts. Yellow warnings should be
reserved for actual warnings, not normal progress output.
Also fix bare `clear` calls to use `clear 2>/dev/null || true` for
robustness on minimal terminals, and improve the misleading
"Appending environment variables to ~/.zshrc..." message in local
scripts to the standard "Setting up environment variables..." phrasing.
Files: local/gptme.sh, local/aider.sh, ramnode/openclaw.sh,
ramnode/gptme.sh, netcup/gptme.sh
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>
Implements gptme agent on Netcup VPS using netcup lib primitives.
Agent: gap-filler-netcup
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add a generic ensure_multi_credentials() helper to shared/common.sh that
handles the env-var/config-file/prompt/test/save flow for providers needing
multiple credentials. This eliminates ~270 lines of duplicated logic across
contabo, netcup, ramnode, ionos, and upcloud, replacing it with single
function calls.
Each provider's ensure_*_credentials() function is now 3-8 lines instead
of 30-65 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>
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>
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>
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>
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>
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>
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>
- Uses netcup lib primitives for server creation
- Installs bun and openclaw via bun global install
- OpenRouter injection via env vars
- Tested with bash -n
Agent: gap-filler-1
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add Netcup as a new cloud provider - a German budget VPS provider
with REST API support starting at €3.86/mo.
Changes:
- Created netcup/lib/common.sh with session-based REST API primitives
- Added Netcup to manifest.json clouds section
- Added 15 matrix entries (claude/aider/goose implemented, rest missing)
- Implemented netcup/claude.sh, netcup/aider.sh, netcup/goose.sh
- Created netcup/README.md with usage documentation
Netcup uses session-based authentication requiring:
- NETCUP_CUSTOMER_NUMBER
- NETCUP_API_KEY
- NETCUP_API_PASSWORD
API launched Oct 2025, replaces legacy SOAP service (deprecated May 2026).
Agent: cloud-scout-2
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>