- Validate app_name at function entry (alphanumeric, dots, hyphens, underscores
only) before it's used in file paths or passed to cloud_exec
- Add trap-based cleanup for the temp file used during .spawnrc fallback creation
- Add security comments documenting the three-layer defense model: printf %q
quoting, base64 encoding, and stdin piping (no interpolation into command
strings)
The core vulnerability (env_b64 interpolated into the cloud_exec command string)
was already fixed in a prior commit that switched to stdin piping. This change
adds defense-in-depth and documentation.
Fixes#2437, #2441
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
install.sh: Replace color variable interpolation in printf format strings
with %b arguments to prevent format string injection (fixes#2443).
common.sh: Use %b for color escapes in logging functions. Document that
BASH_SOURCE and source usage in load_cloud_driver is intentional since
e2e scripts are filesystem-only, not curl|bash (fixes#2438).
Agent: ux-engineer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add defense-in-depth validation across all e2e cloud driver scripts:
- Validate IP addresses match IPv4 format before use in SSH commands
(aws, digitalocean, gcp, hetzner)
- Validate SSH username contains only safe characters (gcp)
- Validate resource IDs are numeric before interpolating into API URLs
(digitalocean droplet IDs, hetzner server IDs)
- URL-encode app name in Hetzner API query parameter to prevent
query parameter injection
- Validate numeric env vars (INPUT_TEST_TIMEOUT, PROVISION_TIMEOUT,
INSTALL_WAIT) that get interpolated into remote command strings
Fixes#2432, #2433, #2434, #2435, #2442
Agent: security-auditor
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
status.ts passed server_id from history directly into Hetzner/DO API
URLs without calling validateServerIdentifier(). Both delete.ts and
connect.ts validate first; status.ts was the only gap. A tampered
~/.spawn/history.json could craft a server_id with path traversal
characters (e.g. "../v2/account") causing the Bearer token to be
sent to an unintended API endpoint (SSRF via URL path manipulation).
Fix: call validateServerIdentifier() after extracting serverId,
returning "unknown" gracefully on failure.
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The validate-file.ts hook previously only blocked `set -u` when
`set -eo pipefail` was absent from the file. This allowed scripts
with both `set -eo pipefail` and `set -u` to pass validation,
contradicting the shell rules that unconditionally ban nounset.
Fix the regex to always reject `set -u` variants on actual set
invocation lines (not comments or strings), and update the error
message to recommend `${VAR:-}` instead.
Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
These path-utility tests were duplicated between history.test.ts and
paths.test.ts. Consolidate into paths.test.ts (the canonical location)
and move 4 unique test cases (dot-relative path, .. resolution, outside
home rejection, home-as-SPAWN_HOME) that only existed in history.test.ts.
Removes 64 lines of duplicate test code with zero coverage loss.
Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Without per-process timeouts, if the user's network drops during
cloud-init polling, the CLI hangs forever while billing continues.
Adds 30s kill timers to each polling SSH command (matching the
waitForSsh pattern in shared/ssh.ts) and 330s to DO's streaming SSH.
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds sprite-keep-running support so sprites stay alive during long
agent sessions instead of shutting down due to inactivity.
- Add installSpriteKeepAlive() to sprite/sprite.ts: downloads and
installs the sprite-keep-running script (~/.local/bin) on the sprite
during setup. Non-fatal: logs a warning if download fails so
deployment still proceeds.
- Modify interactiveSession() to wrap the session command in a temp
script (base64-encoded to handle multi-line restart loops) and exec
it via sprite-keep-running if available, with plain bash fallback.
- Call installSpriteKeepAlive() in sprite/main.ts createServer() step
after setupShellEnvironment(), applying to all Sprite agents.
- Add sprite-keep-alive.test.ts: 11 unit tests covering download URL,
install path, error resilience, session script structure, and
keep-alive wrapper inclusion.
Fixes#2424
Agent: issue-fixer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: set SPAWN_HOME in preload and add fs-sandbox guardrail test
The test preload now sets SPAWN_HOME to the sandbox directory by default,
so tests that call cmdRun/saveSpawnRecord without explicitly setting
SPAWN_HOME no longer write to the real ~/.spawn/history.json.
Add fs-sandbox.test.ts that verifies the sandbox is correctly configured
(HOME, SPAWN_HOME, XDG vars all point to temp). Update testing.md with
mandatory filesystem isolation rules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: add root bunfig.toml and fix biome formatting
Add root-level bunfig.toml with test preload so `bun test` works from
the repo root. Fix biome formatting in orchestrate.test.ts afterEach.
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>
Co-authored-by: Claude <claude@anthropic.com>
Move all filesystem path helpers (getUserHome, getSpawnDir, getHistoryPath,
getSpawnCloudConfigPath, getCacheDir, getCacheFile, getUpdateFailedPath,
getSshDir, getTmpDir) into a single shared/paths.ts module. This eliminates
scattered homedir()/process.env.HOME patterns across 8+ files and provides
a single import source for all path resolution.
- Create packages/cli/src/shared/paths.ts with 9 exported functions
- Update 17 source files to import from paths.ts
- Add re-exports in ui.ts and history.ts for backward compatibility
- Remove direct homedir() imports from gcp, sprite, local, ssh-keys, etc.
- Add comprehensive unit tests in paths.test.ts
- Bump CLI version to 0.15.34
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The manifest was updated to moonshotai/kimi-k2.5 but the code still
hardcoded openrouter/auto in both modelDefault and the configure
fallback.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
Bun's os.homedir() reads from getpwuid() and ignores runtime changes to
process.env.HOME. Named imports capture the native function binding, so
patching os.homedir on the default export doesn't propagate. This caused
all test files using homedir() to write .spawn-test-* dirs to the real
home directory instead of the preload sandbox.
Add getUserHome() helper to shared/ui.ts that prefers process.env.HOME,
replace all direct homedir() calls in production and test code.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add @commitlint/cli and @commitlint/config-conventional at repo root
- Configure commitlint with project-specific types (security, etc.)
- Set up Husky v9 with commit-msg hook running commitlint
- Add pre-commit hook running biome check on CLI source
Fixes#2406
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The "recovers from corrupted existing history file and creates backup"
test was a subset of the more thorough coverage in
history-corruption.test.ts. Removed the duplicate and its unused
readdirSync import.
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>
Consolidates duplicate server naming logic from 5 cloud modules into shared utilities in src/shared/ui.ts. No behavioral changes - purely structural refactor.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* security: escape pkill regex metacharacters in app_name
Fixes#2409 - escape regex metacharacters (., [, \, *, ^, $) in
app_name before using in pkill -f pattern to prevent unintended
process termination. Even though app_name is validated against a
safe character whitelist, . and - are regex metacharacters that
could match broader patterns than intended.
Note: #2410 (unquoted regex in bash conditional) was already fixed
by a prior commit that refactored the code to use sed instead of
[[ =~ BASH_REMATCH ]].
Agent: security-auditor
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: remove dead exec_long functions reintroduced from pre-#2407 code
Remove cloud_exec_long dispatcher and all _*_exec_long() functions
from common.sh and cloud driver files (aws, digitalocean, gcp,
hetzner, sprite). These were explicitly removed as dead code in
PR #2407 (commit c4ae1684) and must not be reintroduced.
Issue #2410 (unquoted regex in bash conditional) is already resolved:
the [[ =~ ]] pattern was previously replaced with case/sed parsing.
Fixes#2409Fixes#2410
Agent: pr-maintainer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The worktree path regex in pre-merge-check.ts used [^\s/]+ which only
matched a single path segment after /tmp/spawn-worktrees/. This blocked
PR merges from nested worktrees like refactor/fix/issue-N used by the
automated refactoring service.
Fix both the TypeScript regex ([^\s/]+ -> [^\s]+) and the inline bash
grep pattern in settings.json ([a-zA-Z0-9._-]+ -> [a-zA-Z0-9._/-]+).
Closes#2401
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The cloud_exec_long dispatcher in common.sh and all five cloud-specific
_exec_long implementations (aws, digitalocean, gcp, hetzner, sprite)
were defined but never called by any code in the e2e test suite.
Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Before creating symlinks in /usr/local/bin, verify that any existing
symlink points to a safe location ($HOME/.local/*, $HOME/.bun/*,
/usr/local/*, $HOME/.npm-global/*). If a symlink points to an
unexpected location, warn the user and skip to prevent malicious
symlink persistence through reinstalls.
Uses portable `readlink` (without -f) for macOS bash 3.2 compatibility.
Fixes#2402
Agent: security-auditor
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add logDebug() function gated on SPAWN_DEBUG=1 for surfacing error
details without cluttering normal output. Refactor 6 silent/overly-broad
catch blocks:
- agent-tarball.ts: split 70-line try into fetch+parse and remote exec
- update-check.ts: remove outer try, wrap only performAutoUpdate
- history.ts: add warnings to swallowed tryCatch results
- oauth.ts: warn when API key save fails
- orchestrate.ts: warn on checkAccountReady and preProvision failures
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: restore @openrouter/spawn-shared workspace package
Restore packages/shared/ as canonical location for parse.ts, result.ts,
and type-guards.ts. CLI shared files become thin re-exports, preserving
all existing import paths. SPA imports switch from fragile relative paths
to the workspace package.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: sort exports in shared package barrel to satisfy biome
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: sort SPA imports to satisfy biome organizeImports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Show a proactive warning before the OAuth/token entry flow when the user
has no saved DigitalOcean config and no DO_API_TOKEN env var. This prevents
new users from completing the full setup flow only to fail at provisioning
because their account has no payment method on file.
Warning is shown only once per first-time setup — returning users (who have
a saved token, even if expired or invalid) skip the reminder.
Closes#2395
Agent: issue-fixer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
Co-authored-by: A <258483684+la14-1@users.noreply.github.com>
After SSH reconnect, agent commands (openclaw, codex, kilocode, junie) were
not found because PATH was only written to ~/.bashrc, which is not sourced
by login shells. Login shells (used by SSH) source ~/.profile or
~/.bash_profile instead.
Changes:
- Write .spawnrc sourcing to ~/.profile and ~/.bash_profile in addition
to ~/.bashrc and ~/.zshrc (orchestrate.ts)
- Write npm-global PATH export to ~/.profile and ~/.bash_profile for all
npm-installed agents: OpenClaw, Codex, Kilo Code, Junie (agent-setup.ts)
- Write Claude Code PATH to ~/.profile and ~/.bash_profile (agent-setup.ts)
- Write OpenCode PATH to ~/.profile and ~/.bash_profile (agent-setup.ts)
- Extract NPM_GLOBAL_PATH_PERSIST constant to DRY up repeated shell snippets
- Fix e2e provision.sh to also write .spawnrc sourcing to login shell configs
- Bump CLI version to 0.15.32
Fixes#2394
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Reword preflight OpenRouter credential message to not imply it happens
immediately (cloud auth runs first in the orchestration pipeline)
- Clarify GitHub CLI setup messages to specify "remote server" instead of
leaving ambiguous "this machine" context for cloud users
Fixes#2396
Agent: ux-engineer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The orchestrate test suite called runOrchestration (which internally
calls saveSpawnRecord) without setting SPAWN_HOME to a temp directory.
Every test run wrote ~20 fake records into the user's real history,
eventually filling it with 100 connectionless "testagent" entries
and wiping all real spawn history.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: graceful recovery from corrupted history.json
- Atomic writes (write to .tmp, rename into place) to prevent corruption
- Backup corrupted files with .corrupt suffix before discarding
- Per-record salvaging: if some v1 records are malformed, keep the valid ones
- Archive recovery: when history.json is corrupted, try loading from archives
- Stderr warnings when corruption is detected or records are recovered
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: replace try/catch with Result tryCatch wrapper in history.ts
Add tryCatch() to shared/result.ts and use it throughout history.ts to
eliminate all 7 try/catch blocks. Errors are now handled via Result
pattern matching instead of exception control flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: A <258483684+la14-1@users.noreply.github.com>
When both server_id and server_name are missing from a connection record,
serverId falls back to "". Passing "" to fetchHetznerStatus/fetchDoStatus
constructs URLs like /v1/servers/ (list all), wasting rate-limit quota and
sending auth tokens to the wrong endpoint. Early-return "unknown" instead.
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The two-phase save architecture was fundamentally broken: saveVmConnection()
was called inside createServer() BEFORE saveSpawnRecord() created the record,
so the merge-by-spawnId silently failed every time — resulting in records
with no connection data and `spawn ls` showing nothing.
Replace with atomic single-save: createServer() now returns VMConnection,
and the orchestrator calls saveSpawnRecord() once with connection data
included. Removes saveVmConnection(), getConnectionPath(),
mergeLastConnection(), and last-connection.json entirely.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: A <258483684+la14-1@users.noreply.github.com>
The TTY key loop treated explicit user cancellation (ESC/Ctrl-C) the same
as a TTY failure — both called fallback() which renders a numbered-list
picker. Now the key loop distinguishes between the two: cancel() exits
cleanly, fallback() is only used when /dev/tty is unavailable.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 30+ individual it() blocks that each tested a single typo input
with data-driven loops using arrays of test cases. Same coverage, less
boilerplate. Reduces check-entity.test.ts from 401 to 330 lines.
Consolidated sections:
- non-existent entities: 5 tests -> 1 loop over 6 cases
- fuzzy match typos: 11 tests -> 2 loops over 6 cases each
- empty/boundary inputs: 8 tests -> 1 loop over 8 cases
- cross-kind fuzzy match: 6 tests -> 1 loop over 6 cases
- empty manifest: 2 near-identical tests -> 1 combined test
Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
DO default was s-2vcpu-4gb which isn't available in nyc3, causing 422
errors. Changed to s-2vcpu-2gb to match manifest.json. Also aligned
Hetzner default location from nbg1 to fsn1 to match manifest.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Merge security-edge-cases.test.ts and security-encoding.test.ts into
security.test.ts. Move stripDangerousKeys tests to manifest.test.ts
(where the function is defined). All 1447 tests pass, zero regressions.
-- qa/dedup-scanner
Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
- Remove unused multiPickToTTY function, MultiPickOption interface, and
MultiPickConfig interface from picker.ts (never called anywhere)
- Remove export keyword from 7 internal-only functions in commands/shared.ts
that are used within the file but never imported externally:
getEntityCollection, getEntityKeys, formatAuthVarLine,
hasCloudConfigCredentials, getCredentialGuidance,
checkAllCredentialsReady, printAuthVariableStatus
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>
On headless VMs there's no Chrome extension to attach to. Setting
defaultProfile to "openclaw" tells OpenClaw to launch and manage
the browser itself via CDP instead of waiting for an extension relay.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
`spawn status` silently ignored -a and -c flags, showing all servers
regardless. This is inconsistent with `spawn list` and `spawn delete`
which both support these filters.
- Update `cmdStatus` to accept `agentFilter`/`cloudFilter` options and
pass them to `filterHistory()`
- Update `dispatchStatusCommand` to parse filter flags using the shared
`parseListFilters` helper (same as list/delete)
- Document filter flags in help text for `spawn status`
- Bump version to 0.15.27
Fixes#2377
Agent: issue-fixer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The `_maxAttempts` parameter in both Hetzner and DigitalOcean's
`waitForCloudInit()` was silently ignored — loop bounds and early-exit
checks were hardcoded. Rename to `maxAttempts` and use it consistently,
matching the AWS/GCP implementations.
Fixes#2378
Agent: issue-fixer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove `|| true` from chmod call that restricts token file permissions.
If chmod fails, authentication now aborts with an error instead of
silently leaving ~/.config/gh/hosts.yml world-readable.
Fixes#2374
Agent: security-auditor
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Merge 9 test cases that called the same function with the same arguments
into adjacent tests, each checking a different assertion. Consolidated
them into single tests that verify all assertions in one call, removing
redundant setup/teardown overhead.
Files changed:
- commands-error-paths.test.ts: merge unknown agent/cloud and unimplemented combo tests
- commands-cloud-info.test.ts: merge unknown cloud error + suggestion tests
- commands-resolve-run.test.ts: merge many-clouds suggestion and no-clouds tests
- commands-name-suggestions.test.ts: merge display name suggestion + error tests
Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(security): base64-encode cmd in _sprite_exec to prevent injection
Applies base64 encoding to both _sprite_exec() and _sprite_exec_long()
so that shell metacharacters in the cmd parameter cannot break out of
context during remote execution on Sprite instances. The command is
base64-encoded locally and decoded on the remote side before execution.
Fixes#2369
Agent: security-auditor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* revert: restore stdin-piping approach per security review feedback
The base64 approach introduced ${_encoded} interpolation into shell context,
which is less secure than the existing stdin-piping approach on main.
Restores the original secure pattern: pipe cmd via stdin to avoid interpolation.
Agent: pr-maintainer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- wget not available on many cloud VMs, use curl instead
- Remove 2>/dev/null from dpkg/apt so install errors are visible
- Capture /usr/bin/google-chrome-stable in tarball (actual .deb binary name)
- Use curl in packer/agents.json tarball build too
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
* feat: show cloud prices as lead indicator, default OpenClaw to Kimi K2.5
- Add `price` field to all clouds in manifest.json
- Show price as lead indicator in cloud picker hints, cloud listings, cloud info, and dry-run preview
- Change OpenClaw default model from openrouter/auto to moonshotai/kimi-k2.5 (top used model by OpenClaw users)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add defensive guards for undefined cloud price in cached manifests
When users upgrade CLI but have cached manifests from before the price
field was added, c.price is undefined. Add ?? "" fallbacks and an
if-guard to prevent runtime crashes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: A <258483684+la14-1@users.noreply.github.com>
The findClosestMatch unit tests (distance matching, case insensitivity,
null for distant strings, closest-among-multiple) were duplicated between
commands-name-suggestions.test.ts and fuzzy-key-matching.test.ts. Remove
the redundant section from commands-name-suggestions.test.ts since
fuzzy-key-matching.test.ts is the dedicated unit test file for that
function. The integration tests via cmdRun/cmdAgentInfo/cmdCloudInfo
remain in commands-name-suggestions.test.ts.
-- qa/dedup-scanner
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>
* fix: use Google Chrome .deb instead of Playwright for OpenClaw browser
Snap Chromium on Ubuntu 24.04 fails because AppArmor confinement blocks
CDP control. OpenClaw's own docs recommend installing Google Chrome via
.deb package which bypasses snap entirely.
Also adds browser.noSandbox and browser.executablePath to the OpenClaw
config so the browser tool works out of the box on Linux VMs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove unnecessary confirmation prompt when OAuth fails
If OAuth didn't complete, the user obviously wants to paste a key.
The "Paste your API key manually? (Y/n)" prompt was pointless friction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove unnecessary "Continue anyway?" credential confirmation
If the user selected a cloud, they obviously want to continue.
The warning + setup guidance is sufficient — no need to block on a confirm.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: move Chrome install to configure step so it runs after tarball
The tarball path skips agent.install() entirely, so Chrome never got
installed. Moving it to configure() (setupOpenclawConfig) ensures it
always runs regardless of install method.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: bundle Google Chrome in openclaw tarball
Add Chrome .deb install to openclaw's tarball build so it ships
pre-installed. Capture /usr/bin/google-chrome and /opt/google/chrome/
in the tarball. Add dl.google.com to the workflow domain allowlist.
The configure() step still has a fallback install with idempotency
check (command -v google-chrome) for non-tarball installs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use openclaw config set for browser setup + correct binary name
- Use `google-chrome-stable` (actual .deb binary name) not `google-chrome`
- Set browser config via `openclaw config set` CLI (the supported way)
instead of writing JSON directly which wasn't being picked up
- Remove browser section from JSON config to avoid conflicts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>