Commit graph

27 commits

Author SHA1 Message Date
A
edcdf78ba8
fix(e2e): correct env var names passed to AWS CLI in provision.sh (#1985)
The provision.sh was setting wrong env var names that the TypeScript CLI
does not read:
- AWS_LIGHTSAIL_INSTANCE_NAME → LIGHTSAIL_SERVER_NAME (read by aws.ts:getServerName)
- AWS_REGION → AWS_DEFAULT_REGION (read by aws.ts:authenticate/promptRegion)
- AWS_BUNDLE → LIGHTSAIL_BUNDLE (read by aws.ts:promptBundle)

Without the correct names, each provisioning run created an instance with a
random generated name instead of app_name, causing the post-provision
existence check to fail every time.

Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 01:47:41 -08:00
A
fe7cf1ba4b
refactor: Remove dead code and stale references (#1987)
Remove stale Fly.io references from shared shell scripts. Fly.io was
removed as a cloud provider (#1979) but comments referencing its
specific token format ("FlyV1 <macaroon>") and container behavior
remained in key-request.sh and github-auth.sh.

Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 04:19:53 -05:00
A
d8131c3df6
fix(hetzner): update deprecated server types to cx23/cpx22 gen (#1983)
* fix(hetzner): update deprecated cx22/cpx21 server types to cx23/cpx22

Hetzner deprecated the entire cx*2 and cpx*1 server lines on Jan 1, 2026.
New orders fail with "server type is deprecated". Updates to the current
gen3 CX and gen2 CPX lines (cx23, cx33, cx43, cx53, cpx22, cpx32).

Also shows the server type picker by default instead of requiring --custom,
so users can choose their instance size on every deploy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(zeroclaw): append autonomy config instead of overwriting onboard output

zeroclaw onboard generates a complete config with required fields like
default_temperature. Our setup was overwriting that with a partial config
missing required fields, causing a crash loop on startup. Now appends
the security/shell settings instead so onboard's fields are preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: fix biome formatting in agent-setup.ts

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

---------

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
2026-02-27 00:20:31 -08:00
A
7d83486077
refactor: Remove dead code and fix stale references in QA sweep (#1978)
- Replace local rec() helper in hetzner.ts with shared toRecord() from
  @openrouter/spawn-shared, eliminating a duplicate implementation that
  already existed in the shared package with equivalent behavior
- Fix stale comment in key-request.sh referencing non-existent qa.sh

Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
2026-02-27 00:10:16 -05:00
A
d04096a15b
feat!: remove Fly.io cloud provider support (#1979)
* feat!: remove Fly.io cloud provider support

Drop Fly.io as a supported cloud provider. Sprite (which uses Fly.io
infrastructure internally) is retained.

- Delete packages/cli/src/fly/ module, sh/fly/ scripts, fixtures/fly/
- Remove fly cloud entry and 6 fly matrix entries from manifest.json
- Remove fly imports, destroy cases, and connection handlers from commands.ts
- Remove fly-ssh sentinel from security.ts
- Port E2E test suite from Fly.io to AWS Lightsail (fly-e2e.sh → aws-e2e.sh)
- Update README (7 clouds, 42 combinations), CLAUDE.md, and skill prompts
- Clean up fly references in build config, gitignore, icon sources
- Bump CLI version to 0.11.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: restore Docker image build under sh/docker/

Move openclaw Dockerfile from sh/fly/docker/ to sh/docker/ and rename
workflow from fly-docker.yml to docker.yml with updated paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: fix extra blank lines in commands.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
2026-02-27 00:06:32 -05:00
A
f1c2b4d4e4
refactor: Remove dead code and stale references (#1976)
* refactor: remove dead offerGithubAuth exports from cloud agents.ts files

The per-cloud offerGithubAuth re-exports in each cloud's agents.ts were
never called from outside their own module. The actual GitHub auth
orchestration is handled by shared/orchestrate.ts which calls
offerGithubAuth from shared/agent-setup.ts directly.

Also update stale comment in sh/test/fixtures/_shared_agent_assertions.sh
that referenced mock.sh, a test harness file that no longer exists in
the repository.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: collapse multi-line imports to single-line per biome format

After removing offerGithubAuth exports, the remaining 2-import blocks
should be single-line. Also collapse fly/agents.ts 4-import block and
remove trailing blank line.

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

---------

Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
2026-02-26 22:04:33 -05:00
A
e2f40b3d8b
docs(digitalocean): document DO_REGION, DO_DROPLET_SIZE, and --custom flag (#1970)
Add Environment Variables section to sh/digitalocean/README.md with:
- DO_REGION: list of all 10 available regions with default (nyc3)
- DO_DROPLET_SIZE: list of all 6 available sizes with default (s-2vcpu-4gb)
- --custom flag: interactive region + size picker

Fixes #1968

Agent: issue-fixer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-26 16:35:47 -08:00
A
b6f021ecf2
fix(security): clarify base64+single-quote pattern in fly_ssh (#1937)
Fixes #1933. The comments incorrectly implied base64 encoding alone
prevents injection. Safety relies on the combination of base64 output
(no single quotes in alphabet) + single-quote wrapping. Made this
explicit in all 7 affected comments.

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-25 18:44:51 -05:00
A
b09295131a
fix(security): replace eval with pipe-to-sh in fly_ssh functions (#1928)
Eliminates nested quote eval pattern in favor of direct pipe to sh,
removing potential injection surface in fly_ssh and fly_ssh_long.
Fixes #1927

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 14:50:24 -05:00
A
f9c1568f9c
fix(security): use explicit exports in provision.sh subshell (#1926)
Replace inline env-var prefix pattern (VAR=value command) with explicit
export statements inside the subshell. While the inline prefix is
POSIX-compliant and not a real injection vector, explicit exports are
clearer about intent, eliminate the fragile backslash-continuation chain,
and prevent future copy-paste of the pattern into unsafe contexts.

Fixes #1924

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-25 12:54:58 -05:00
A
cac06b2706
fix(security): base64-encode INPUT_TEST_PROMPT in E2E verify.sh to prevent injection (#1923)
Fixes #1921

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 10:26:18 -05:00
A
638250e744
fix(security): use base64 encoding in fly_ssh to prevent command injection (#1918)
Replace single-quote escaping (which only handled ' but not other shell
metacharacters like $(), backticks, ;, ||, &&, |) with base64 encoding.
Base64 output contains only [A-Za-z0-9+/=] characters, completely
eliminating shell metacharacter injection risks regardless of command
content. Compatible with both GNU coreutils (Linux) and BSD (macOS).

Fixes #1912

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-25 03:25:31 -08:00
A
4994c28594
fix(security): harden shell scripts - fix sed portability, curl HTTPS enforcement, token expiry (#1917)
- MEDIUM: Validate flyctl auth status before empty FLY_API_TOKEN fallback
  in provision.sh (fail fast instead of silent failure)
- LOW: Fix sed -i portability in qa.sh (use sed -i.bak for macOS compat)
- LOW: Increase FLY_API_TOKEN expiry from 2h to 8h in common.sh
- LOW: Add --proto '=https' to all curl -L calls in digitalocean scripts
  (6 files) to prevent HTTP downgrade on redirects

Fixes #1913

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-25 03:23:32 -08:00
Ahmed Abushagur
338ae57f71
fix: replace @clack/prompts multiselect with /dev/tty picker for SSH keys (#1907)
* fix: run bun in foreground in DigitalOcean scripts to unfreeze interactive prompts

The _run_with_restart function backgrounded bun with `& + wait` so a SIGTERM
trap could forward the signal. But backgrounding removes bun from the terminal's
foreground process group, which prevents @clack/prompts multiselect from entering
raw mode — arrow keys print as raw escape sequences (^[[A^[[B) and the SSH key
selection prompt freezes.

Fix: run bun in the foreground and detect SIGTERM from exit code 143 (128+15)
instead of using a trap flag + PID tracking. This preserves the restart-on-signal
behavior while giving bun full terminal access for interactive prompts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: replace @clack/prompts multiselect with /dev/tty picker for SSH keys

When the CLI (parent bun) spawns bash → child bun for cloud scripts,
the parent's event loop keeps fd 0 registered and races with the child's
@clack/prompts for terminal input. This causes the SSH key multiselect
to render but freeze — arrow keys print as raw escape sequences.

Fix: add multiPickToTTY() in picker.ts that opens /dev/tty directly,
bypassing process.stdin entirely. Replace the @clack/prompts multiselect
in ssh-keys.ts with this new function. Also add process.stdin.unref()
to prepareStdinForHandoff() so the parent stops polling fd 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* perf: disable SSH compression for interactive sessions

Compression=yes adds per-keystroke CPU overhead that causes
noticeable input lag on normal connections. Only beneficial
on slow/high-latency links.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 23:54:54 -08:00
A
154112fb41
feat: add live input/output E2E verification for agents (#1886)
* feat: add live input/output E2E verification for agents

The E2E suite previously only verified static artifacts (binaries, config
files, env vars). An agent with a broken API key or crash-on-launch bug
would pass all checks. This adds an input test phase that sends a real
prompt to each agent and verifies the response contains a marker string.

- Add fly_ssh_long() with configurable timeout for long-running commands
- Add per-agent input test functions (claude -p, codex -q, openclaw -p,
  zeroclaw agent -p; opencode/kilocode skip as TUI-only)
- Add run_input_test() dispatcher with SKIP_INPUT_TEST env var support
- Add --skip-input-test CLI flag to fly-e2e.sh
- Chain input test after verify in run_single_agent() pipeline
- Add INPUT_TEST_TIMEOUT constant (default 120s, env-overridable)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: format p.text({ message }) to multi-line for biome

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:16:30 -05:00
A
80c044dbb3
fix: correct CLI entry point path in E2E provision script (#1887)
* fix: correct CLI entry point path in E2E provision script

The path resolution went up 2 levels (../../) from sh/e2e/lib/ which
landed in sh/ instead of the repo root. After the monorepo restructure,
packages/cli/src/index.ts is at the repo root — need 3 levels (../../..).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: format p.text({ message }) to multi-line for biome

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:16:07 -05:00
A
2a1ed4164e
fix(gcp): add network/subnet flags to fix custom VPC subnet mode (#1883)
GCP instance creation was failing with 'Invalid value for field
resource.networkInterfaces[0].subnetwork' when the project VPC uses
custom subnet mode. Add --network and --subnet flags defaulting to
'default', with GCP_NETWORK and GCP_SUBNET env var overrides for
custom VPC setups.

Fixes #1882

Agent: issue-fixer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-24 14:27:46 -05:00
A
7419ff8290
fix: use npm install for OpenClaw to fix Node module resolution (#1878)
OpenClaw runs under Node, but bun's flat node_modules layout breaks
Node's require() resolver when dynamically loading channel plugins.
Switching to npm install ensures standard node_modules layout.

Fixes #1875

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-24 04:51:09 -05:00
A
52b59bab17
fix: export PATH after bun install to fix current-shell availability (#1877)
When bun is freshly installed, rc file patching only takes effect
in new shells. Strengthen the post-install PATH export to include
both hard-coded $HOME/.bun/bin and $HOME/.local/bin alongside
$BUN_INSTALL/bin, so bun and spawn are available in the current
execution context immediately after install.

Fixes #1874

Agent: ux-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-24 04:51:00 -05:00
A
4e1467a521
fix: remove broken clone_cli() — go straight to pre-built binary (#1873)
* fix: remove broken clone_cli() — go straight to pre-built binary

The clone_cli() function (added before the monorepo migration, PR #1853)
only fetches top-level .ts files via the GitHub Contents API. Since the
monorepo reorganised source code into subdirectories (aws/, fly/, hetzner/,
shared/, etc.), clone_cli() silently downloads an incomplete source tree.
bun run build then always fails because cross-directory imports cannot
resolve, and the installer falls through to the pre-built binary anyway.

Every install was burning ~12 unnecessary GitHub API requests (rate-limited
at 60/hr for unauthenticated clients) and several seconds of wasted bun
install + failed build time.

Fix: remove clone_cli() entirely, replace build_and_install() with a
direct binary download. Behaviour is identical for all users (binary path
was already the universal outcome); installs are now faster and cheaper on
the API rate limit.

Agent: code-health
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: update install.sh tests for simplified binary-only installer

Remove tests for clone_cli() and source builds which were removed in
the parent commit. Add tests verifying the direct binary download
approach and asserting that the old clone/build code is gone.

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

* style: fix biome format errors in commands.ts and duplicate-detection test

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

---------

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 01:19:30 -08:00
A
58c91571f1
fix: make macOS compat linter blocking and add 6 missing rules (#1867)
The linter was running in CI with --warn-only, meaning it never
blocked anything — effectively vaporware. This removes --warn-only
to make it a real gate.

Also adds rules for bash 4.0+ features that were documented in
CLAUDE.md but not enforced:
- MC014: readarray/mapfile (bash 4.0+)
- MC015: coproc (bash 4.0+)
- MC016: &>> redirect (bash 4.0+)
- MC017: relative source paths (breaks curl|bash)
- MC018: wait -n (bash 4.3+)
- MC019: declare -g (bash 4.2+)

Excludes .claude/worktrees/ from scanning (temp copies, not
committed code).

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 03:50:12 -05:00
A
dad130b6d3
fix: add SIGTERM trap and restart loop to DigitalOcean agent scripts (#1863)
Agent processes on DigitalOcean droplets were dying silently on SIGTERM
with no logging or recovery. This adds:

- SIGTERM trap handler that logs signal, timestamp, and dashboard URL
- SIGHUP trap handler for terminal disconnection
- Restart loop with exponential backoff (up to 3 attempts) on SIGTERM
- Child process forwarding so bun receives the signal cleanly

Replaces bare `exec bun run` with a managed foreground process that
the shell can monitor and restart.

Fixes #1859

Agent: ux-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-24 02:51:14 -05:00
A
16cad3ebc9
fix: add error handling to _ensure_bun() in all Hetzner scripts (#1855)
All 6 Hetzner agent scripts silently ignored bun installation failures.
Now matches the robust pattern used by every other cloud (aws, fly, gcp,
digitalocean, daytona, local, sprite): prints status, exits on curl failure,
and verifies bun is available after install.

Fixes #1854

Agent: issue-fixer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-24 01:51:44 -05:00
A
65f6f1be32
feat: Bun workspace monorepo — packages/cli + packages/shared (#1853)
Restructure the repo as a Bun workspace monorepo:

- Move cli/ → packages/cli/
- Create packages/shared/ (@openrouter/spawn-shared) with type-guards and parse utilities
- Add root package.json with workspace configuration
- Update all CLI imports to use @openrouter/spawn-shared
- Deduplicate toRecord/toObjectArray helpers from 4 cloud modules
- Update SPA (slack-bot) to use shared package instead of local toObj()
- Update 48 agent shell scripts for new packages/cli/ path
- Update install.sh, install.ps1, e2e, and test scripts
- Update all GitHub workflows, .gitignore, pre-commit hooks
- Update CLAUDE.md, README.md, and skill prompt references
- Pin all dependency versions (no ^ ranges)
- Bump CLI version 0.9.1 → 0.10.0

All 1908 tests pass. Lint clean. All 8 cloud bundles build.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-23 22:07:05 -08:00
A
a96a396e79
fix: add Lightsail activation prerequisite to docs and error messages (#1850)
- Adds a Prerequisites section to sh/aws/README.md (updated path after
  shell scripts reorganization in #1843) with the Lightsail activation
  URL as the first step
- Surfaces the Lightsail activation URL in createInstance error hints
  for both CLI and REST paths so users get actionable guidance on failure
- Bumps CLI to 0.9.1

Fixes #1838

Agent: issue-fixer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-24 00:53:28 -05:00
A
c487ea215f
refactor: move test fixtures to root /fixtures directory (#1849)
* refactor: move test fixtures to root /fixtures directory

Moves test/fixtures/ → fixtures/ at the repo root for easier
discoverability. Updates all references in CLAUDE.md and QA
prompt files.

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

* refactor: move install.ps1 to sh/cli/ and fixtures to root

- Moves cli/install.ps1 → sh/cli/install.ps1 (consistent with install.sh)
- Moves test/fixtures/ → fixtures/ at the repo root
- Updates all references in README, CLAUDE.md, QA prompts, and the ps1 itself

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

---------

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-23 21:38:18 -08:00
A
b84adfb74e
refactor: move all shell scripts to /sh directory (#1843)
Reorganizes the project so all shell scripts live under a dedicated
/sh directory, enabling the OpenRouter rewrite URL to point at /sh/
instead of the repository root.

Moves:
- cli/install.sh → sh/cli/install.sh
- shared/*.sh → sh/shared/*.sh
- {cloud}/{agent}.sh → sh/{cloud}/{agent}.sh (48 scripts)
- {cloud}/README.md → sh/{cloud}/README.md
- e2e/*.sh → sh/e2e/*.sh
- test/macos-compat.sh → sh/test/macos-compat.sh
- test/fixtures/**/*.sh → sh/test/fixtures/**/*.sh

Updates all references:
- RAW_BASE path construction in commands.ts, update-check.ts
- GitHub auth URL in agent-setup.ts
- Self-referencing URLs in install.sh, github-auth.sh
- CI workflow paths in lint.yml, cli-release.yml
- Test file paths in install-script-validation, manifest-integrity
- Documentation in README.md, cli/README.md, CLAUDE.md
- QA scripts in .claude/skills/

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-23 21:14:54 -08:00