Commit graph

324 commits

Author SHA1 Message Date
A
57174a0f15
feat(agent): add T3 Code agent (web GUI for Claude/Codex) (#3322)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
All CI green. Rebased from #3321, added Daytona support, resolved conflicts. Security reviewed: no injection vectors — all env var values come from hardcoded config, shell scripts follow existing patterns.
2026-04-18 01:14:37 -07:00
Ahmed Abushagur
dc4fb59f67
fix(openclaw): batch config set calls into single exec (#3319)
Merges 4 separate runner.runServer() calls (model, sandbox, browser,
channel stubs) into one exec with commands chained by `;`. On Sprite
(container-exec, not persistent SSH), many sequential execs exhaust the
connection and cause "connection closed" / "context deadline exceeded"
on later steps like gateway startup.

Before: 4 execs → 14 "Config overwrite" log lines → flaky connection
After:  1 exec  → same config result → stable connection for gateway

Individual commands use `;` not `&&` so a failure in one (e.g. browser
path not found) doesn't skip the rest — these are all non-fatal prefs.

Bumps 1.0.15 -> 1.0.16.
2026-04-18 00:56:37 -07:00
A
513d3448d4
fix(ux): correct reconnect command suggestion from "spawn connect" to "spawn last" (#3311)
Some checks failed
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
CLI Release / Build and release CLI (push) Has been cancelled
"spawn connect" is not a valid top-level CLI command — users following
this guidance after SSH reconnect failure would see "Unknown agent or
cloud: connect". Replace with "spawn last" which correctly reconnects
to the most recent spawn.

Agent: ux-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-16 15:39:36 +07:00
A
84331173fd
fix(link): add pi and cursor to agent auto-detection (#3309)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
KNOWN_AGENTS was missing pi and cursor, so `spawn link` could not
auto-detect these agents on remote servers. Also adds a binary-name
mapping for cursor (whose CLI binary is `agent`).

Bump CLI to 1.0.14.

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-16 07:44:20 +07:00
Ahmed Abushagur
a179fdbbab
fix(telemetry): opt-in default + picker funnel events (#3308)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Two bugs from the #3305 rollout:

1. Test pollution: orchestrate.test.ts imports runOrchestration directly
   and never calls initTelemetry, but _enabled defaulted to true in the
   module so captureEvent happily fired real events at PostHog tagged
   agent=testagent. The onboarding funnel filled up with CI fixture data.

2. Funnel started too late: funnel_* events fired inside runOrchestration,
   which is only called AFTER the interactive picker completes. Users who
   bail at the agent/cloud/setup-options/name prompts were invisible —
   yet that's exactly where real drop-off happens.

Fix 1 — telemetry.ts:
  - Default _enabled = false. Nothing fires until initTelemetry is
    explicitly called. Production (index.ts) calls it; tests that need
    telemetry (telemetry.test.ts) call it with BUN_ENV/NODE_ENV cleared.
  - Belt-and-suspenders: initTelemetry now short-circuits when
    BUN_ENV === "test" || NODE_ENV === "test", so even if future code
    calls it from a test context, events stay local.

Fix 2 — picker instrumentation:
  New events fired before runOrchestration in every entry path:

    spawn_launched         { mode: interactive | agent_interactive | direct | headless }
    menu_shown / menu_selected / menu_cancelled   (only when user has prior spawns)
    agent_picker_shown
    agent_selected         { agent }     — also sets telemetry context
    cloud_picker_shown
    cloud_selected         { cloud }     — also sets telemetry context
    preflight_passed
    setup_options_shown
    setup_options_selected { step_count }
    name_prompt_shown
    name_entered
    picker_completed

  Wired into:
    commands/interactive.ts  cmdInteractive + cmdAgentInteractive
    commands/run.ts          cmdRun (direct `spawn <agent> <cloud>`)
                             cmdRunHeadless (only spawn_launched)

  runOrchestration's existing funnel_* events continue to fire unchanged.
  The final funnel in PostHog:
    spawn_launched → agent_selected → cloud_selected → preflight_passed
    → setup_options_selected → name_entered → picker_completed
    → funnel_started → funnel_cloud_authed → funnel_credentials_ready
    → funnel_vm_ready → funnel_install_completed → funnel_configure_completed
    → funnel_prelaunch_completed → funnel_handoff

Tests:
- telemetry.test.ts: 2 new env-guard tests (BUN_ENV, NODE_ENV), plus
  updated beforeEach to clear both env vars so existing tests still
  exercise initTelemetry.
- Full suite: 2131/2131 pass, biome 0 errors.

Bumps 1.0.12 -> 1.0.13 (patch — auto-propagates under #3296 policy).
2026-04-15 15:43:30 +07:00
A
d1d51fb06d
fix(security): guarantee temp file cleanup in performAutoUpdate (#3307)
Restructure temp file write-execute-cleanup in performAutoUpdate so
cleanup is unconditionally reached after tryCatch captures any exec
error. Previously, the Windows and Unix paths each had separate
tryCatch+cleanup+rethrow sequences that could diverge under future
edits. Now a single tryCatch wraps the platform-branching exec, with
cleanup always running before any error is re-thrown.

Fixes #3306

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 12:48:12 +07:00
A
4de37274e4
fix(cli): bump version to 1.0.11 for security fix in #3301 (#3304)
PR #3301 modified packages/cli/src/shared/agent-setup.ts (GitHub token
temp file security fix) but did not bump the CLI version. Without this
bump, users on auto-update won't receive the security fix.

Agent: team-lead

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 07:44:38 +07:00
A
352c55c068
fix(update-check): validate install script content before execution (#3302)
Add pre-execution validation of downloaded install scripts to catch
corrupted or truncated downloads. Checks minimum size threshold and
expected shebang/header for the platform. Documents current HTTPS-only
security posture and absence of checksum infrastructure.

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-14 20:41:38 +07:00
Ahmed Abushagur
655a909955
fix(update-check): auto-install patch bumps without SPAWN_AUTO_UPDATE (#3296)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
auto-install to same-major.minor bumps. The intent was "give users control
over feature updates" but the effect was "nobody installs security patches"
because the default became notice-only for everything.

This decouples the two ideas and aligns the policy with semver intent:

  - PATCH bumps (1.0.5 -> 1.0.7, same major.minor): auto-install always,
    no opt-in needed. Patches are reserved for bug fixes and security
    hardening. Blast radius is bounded by semver: no behavior changes,
    no new features, no breaking changes.

  - MINOR / MAJOR bumps (1.0.x -> 1.1.0, 1.x.x -> 2.0.0): respect
    SPAWN_AUTO_UPDATE=1 as opt-in. These can contain behavior changes
    and users should decide when to move to them.

  - SPAWN_NO_AUTO_UPDATE=1: new explicit opt-out for CI environments
    or pinned installs that need a fully static CLI.

Caveat — the one-time hurdle: users currently on 1.0.6 won't get 1.0.7
automatically, because they're still running 1.0.6's update-check.ts
which honors the old opt-in gate. Once they reach 1.0.7 via spawn update
(or by setting SPAWN_AUTO_UPDATE=1), every future patch will propagate
automatically and the fleet becomes self-healing on security.

Tests:
- 5 new tests lock in the policy (patch auto without env, minor notice
  without env, minor auto with env, major notice without env, explicit
  opt-out suppresses patch)
- All 21 update-check tests pass (16 existing + 5 new)
- 2109/2109 total suite

Bumps 1.0.6 -> 1.0.7.
2026-04-14 10:38:08 +00:00
Ahmed Abushagur
c6287b9194
feat(cli): hermes web dashboard tunnel support (#3295)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
* feat(cli): hermes web dashboard tunnel support

Hermes Agent v0.9.0 ships a local web dashboard (hermes dashboard, default
127.0.0.1:9119) for config / session / skill / gateway management. This wires
Hermes into spawn's existing SSH-tunnel infrastructure so `spawn run hermes`
auto-exposes the dashboard to the user's local browser.

- agent-setup.ts: new startHermesDashboard() helper — session-scoped
  background launch via setsid/nohup with a port-ready wait loop. No systemd
  (unlike OpenClaw's gateway) because the dashboard only needs to live for
  the duration of the spawn session. Falls back gracefully if hermes isn't
  in PATH or the dashboard fails to come up.
- Wire preLaunch, preLaunchMsg, and tunnel { remotePort: 9119 } into the
  hermes AgentConfig. Mirrors the OpenClaw tunnel pattern at
  orchestrate.ts:628 — startSshTunnel + openBrowser happen automatically.
- manifest.json: update hermes notes to mention the dashboard.
- hermes-dashboard.test.ts: 7 new unit tests verifying the deploy script
  calls `hermes dashboard --port 9119 --host 127.0.0.1 --no-open`, checks
  all three port-probe fallbacks (ss / /dev/tcp / nc), uses setsid+nohup,
  waits for the port, and does NOT install a systemd unit.
- Bump cli version 1.0.6 -> 1.0.7.

Closes #3293

* chore: bump cli to 1.0.8 to leave 1.0.7 for #3296

---------

Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
2026-04-14 08:43:27 +07:00
A
ace5aa94d1
fix(security): pipe install script via temp file instead of bash -c to prevent command injection (#3292)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Fixes #3291

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-13 15:55:24 +07:00
A
439e5a1446
fix: resolve TypeScript type errors in update-check.test.ts (#3284)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Replace `mock()` + `spyOn().mockImplementation(mockFn)` pattern with
direct `spyOn().mockImplementation(() => ...)` to fix fetch mock type
mismatches. Make execFileSync mocks return Buffer.from("") instead of
void. Add explicit type annotations for callback parameters.

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-13 07:40:59 +07:00
A
14155cb7f8
fix(security): validate remotePath in injectInstructionSkill to prevent shell injection (#3276)
Add validateRemotePath() and shellQuote() to instruction_path handling
in skills.ts, matching the pattern used by uploadConfigFile(). Previously,
remotePath from manifest.json was interpolated directly into shell commands
without validation, allowing path traversal and shell injection via a
malicious instruction_path field.

Closes #3275

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 17:50:05 -07:00
A
187595283e
fix: resolve 4 production TypeScript type errors (#3266)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
- local.ts: spread ReadonlyArray into mutable array for Bun.spawn
- run.ts: capture optional fields in local vars for proper narrowing
- delete.ts: filter SpawnRecordSchema output for required id field

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-11 17:16:47 +07:00
A
500ef53cb7
fix: replace plan_mode_required with message-based approval in refactor team (#3257)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
* fix: replace plan_mode_required with message-based approval in refactor team

Agents spawned with plan_mode_required in non-interactive (-p) mode hang
indefinitely waiting for human UI approval that never arrives. While blocked
in the plan approval loop, they cannot process shutdown_request messages,
which prevents TeamDelete from completing cleanly.

This is the third occurrence of the same bug: #3244 (security-auditor),
#3249 (code-health), #3256 (security-auditor again).

Fix: proactive teammates now use message-based plan approval instead of
plan_mode_required. They send their plan proposal to the team lead via
SendMessage, wait up to 3 minutes for an "Approved" reply, and proceed
only if approved. This is fully compatible with non-interactive mode.

Fixes #3256

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

* fix: correct version bump to 1.0.2 and restore stdin sanitization placeholder

Address security review on PR #3257:
- Fix version: downgrade from 1.0.1→1.0.0 was wrong, correct to 1.0.2
- Note: sanitizeStdinInput() restoration requires additional review

Agent: team-lead
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>
2026-04-11 03:10:00 +00:00
Ahmed Abushagur
eaf49446f8
feat: --beta skills — pre-install MCP servers and skills on VMs (#3258)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
CLI plumbing for the skills feature. The skills catalog in manifest.json
is populated by the discovery scout (#3252), not manually curated.

Flow:
1. User runs `spawn claude hetzner --beta skills`
2. Skills picker shows available skills for that agent (from manifest.json)
3. User selects skills, enters required env vars (GITHUB_TOKEN, etc.)
4. During provisioning, skills are installed on the VM:
   - MCP servers → merged into agent's config (settings.json, mcp.json)
   - Instruction skills → SKILL.md written to agent's skills directory
   - Prerequisites → apt packages, Chrome, etc. installed first
5. Env vars appended to .spawnrc for MCP server runtime access

Headless: SPAWN_SELECTED_SKILLS=github-mcp,context7 spawn claude hetzner

Supports: Claude Code, Cursor (native MCP config), all other agents
(generic mcp.json fallback).

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:02:16 -07:00
Ahmed Abushagur
317227bd41
feat: v1.0.0 golden release — auto-update now opt-in (#3254)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Two changes to update behavior:

1. Auto-update is now opt-in via SPAWN_AUTO_UPDATE=1 (default: notify only)
2. Even with auto-update on, only patch versions install automatically
   (e.g. 1.0.0 → 1.0.5 yes, 1.0.0 → 1.1.0 no)

This pins users to a stable major.minor — bug fixes flow automatically
but new features require an explicit `spawn update`.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:38:01 +00:00
Ahmed Abushagur
561be1cef9
fix: extract tarballs directly to $HOME on non-root VMs (#3253)
Tarballs are built with /root/ paths. On non-root VMs (Sprite), the old
approach extracted to /root/ with sudo, then mirrored files to $HOME/.
This failed on Sprite which doesn't have sudo.

New approach: use tar --transform to remap /root/ → $HOME/ during
extraction. No sudo needed, no mirror step. Falls back to sudo extract
for clouds with passwordless sudo (AWS, GCP).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 13:45:16 +07:00
A
88c1f37d7e
fix(security): add upper bound to base64 scrub regex to prevent ReDoS (#3251)
Fixes #3250

The unbounded quantifier {40,} with word boundary \b caused exponential
backtracking on long non-matching strings. Adding {40,100} upper bound
and removing \b prevents catastrophic backtracking.

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-10 10:16:34 +07:00
Ahmed Abushagur
3aa34f21d3
feat(telemetry): use PostHog Error Tracking with $exception events (#3245)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
* feat(telemetry): use PostHog Error Tracking with $exception events

Errors now send $exception events with $exception_list, parsed stack
frames, and mechanism metadata — shows up in PostHog Error Tracking
tab with auto-grouping, occurrence counts, and assignee support.
Warnings stay as custom cli_warning events in Activity.

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

* fix: remove stderr monkey-patch, restore explicit capture calls

Remove process.stderr.write interception (recursion risk, fragile ANSI
matching, noise capture). Restore captureError/captureWarning in
logError/logWarn/handleError for clean, intentional telemetry.

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: spawn-bot <spawn-bot@openrouter.ai>
2026-04-09 00:52:57 -07:00
Ahmed Abushagur
2b99be70d1
fix(telemetry): move distinct_id into properties for PostHog batch API (#3243)
PostHog's /batch/ endpoint requires distinct_id inside each event's
properties object, not at the event level. Events were silently dropped.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:43:13 -07:00
Ahmed Abushagur
656b0da975
feat: add PostHog telemetry for CLI errors and warnings (#3242)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Sends CLI errors, warnings, and crashes to PostHog for observability.
Strictly error/warning events — no command tracking or session events.

All messages are scrubbed before sending:
- API keys (sk-or-v1-*, sk-ant-*, key-*)
- GitHub tokens (ghp_*, github_pat_*)
- Bearer tokens
- Email addresses
- IP addresses
- Long tokens (60+ char alphanumeric)
- Base64 blobs (40+ chars)
- Home directory paths (/Users/name → ~/[USER])

Default on. Disable with SPAWN_TELEMETRY=0.
Fire-and-forget with 5s timeout — never blocks the CLI.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:02:39 -07:00
Ahmed Abushagur
3c77825e6b
fix(openclaw): always set model after onboard to prevent wrong default (#3236)
`openclaw onboard --non-interactive` now defaults to arcee/trinity-large-thinking
instead of using the OpenRouter provider. Always run `openclaw config set
agents.defaults.model.primary` after onboard to ensure openrouter/auto is set.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:45:44 -07:00
Ahmed Abushagur
ad9da53210
feat(security): behavioral miner detection + spawn status security column (#3227)
Adds two behavioral crypto miner checks to the security scan:
- Flag non-agent processes using >80% CPU (catches renamed miners)
- Detect outbound connections to known mining pool ports (3333, 4444, etc.)

Adds a Security column to `spawn status` that shows clean/alerts/—
for each running server, with detailed alert summary after the table.
JSON output includes security and security_alerts fields.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 07:40:14 +07:00
A
52550dbdca
fix(security): replace eval-style interpolation with env var in allowOpenClawPreviewOrigin (#3217)
Pass the preview origin via SPAWN_PREVIEW_ORIGIN env var instead of
interpolating it into the Node.js inline script, preventing potential
command injection if a malicious preview URL were returned by the API.

Fixes #3215

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-06 23:09:45 -07:00
A
714c29c5a6
feat(gcp): add gh CLI to VM startup script (#3208)
Install GitHub CLI (gh) via the official APT repository in the GCP
cloud-init startup script, so it's available before SSH is reported
as ready. This eliminates the race condition where consumers start
using the VM immediately after JSON output but before spawn's
post-provision SSH setup finishes installing gh.

Fixes #3206

Agent: ux-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-07 00:51:15 +07:00
A
606e521f33
fix: complete VM recovery rewrite for spawn fix command (#3178)
* fix: complete VM recovery rewrite for spawn fix command

Fixes #3173

Rewrites spawn fix to use CloudRunner interface for full VM recovery
instead of a flat bash script piped over SSH. Now runs the same
install(), configure(), preLaunch() functions as initial provisioning.

- Added generic SSH CloudRunner (ssh-runner.ts) reusable by other commands
- Exported injectEnvVarsToRunner() from orchestrate.ts for shared use
- Fixed command injection vulnerability via validateIdentifier(binaryName)
- Updated dependency injection: runScript → makeRunner (CloudRunner)
- Updated tests to use CloudRunner-based DI pattern

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

* test(ssh-runner): add coverage for validation paths

Tests cover the early-exit branches in makeSshRunner methods
(runServer invalid command, uploadFile/downloadFile path traversal)
that throw before any subprocess is spawned.

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

---------

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 11:27:47 +07:00
A
df06bc85af
feat: headless promptCmd, link in cloud picker, default headless steps (#3177) 2026-04-05 01:39:39 +00:00
Muhammad Hashmi
a60d238dfc
fix(daytona): set per-sandbox user/org defaults (#3175)
* feat(daytona): re-add Daytona cloud provider

* fix(daytona): tighten live provider behavior

* fix(daytona): harden reconnect and dashboard flows

* fix(daytona): use platform sandbox defaults

* fix(daytona): add user and org defaults

* fix(ux): stop echoing shell script on startup

---------

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-04-04 18:08:40 -07:00
Ahmed Abushagur
7797906241
fix(openclaw): remove blocking telegram pairing prompt (#3171)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
* fix(openclaw): fix telegram bot not responding to messages

The switch to `openclaw config set` calls in #2655 created malformed
nested config structures — the bot token and dmPolicy weren't read
properly by openclaw, so the bot never started polling for messages.
The `groups` block was also dropped entirely.

Fix: write the complete telegram channel object atomically via a bun
script that reads the existing config, deep-merges the full telegram
block, and writes it back — matching the original atomic JSON approach.

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

* fix(security): pass telegram config via env var instead of JS interpolation

Prevents JavaScript code injection via attacker-controlled bot token by
passing the telegramConfig JSON through a shell-quoted environment variable
(TELEGRAM_CONFIG) and parsing it with JSON.parse(process.env.TELEGRAM_CONFIG)
inside the bun script, instead of interpolating it directly into JS source.

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

* test: add test for atomic telegram config write

Verifies that openclaw telegram config uses a bun merge script (atomic
write) instead of individual `openclaw config set` calls, and that the
full config object (botToken, dmPolicy, groupPolicy, groups) is included.

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

---------

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
2026-04-04 17:02:43 -07:00
A
78ebce9af8
fix: add pi agent and daytona cloud to embedded skill lists (#3172)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
The SKILL_BODY and HERMES_SNIPPET in spawn-skill.ts listed available
agents and clouds but were not updated when pi (#3156) and daytona
(#3168) were added. Agents spawned via the skill system could not
delegate work to Pi or provision on Daytona.

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 14:52:05 +07:00
A
8e9af23c63
refactor: extract recordSpawn() helper to deduplicate spawn record construction (#3163)
The same 12-line saveSpawnRecord block was duplicated 3 times in
runOrchestration() (fast-mode boot, fast-mode retry, sequential path).
A bug fixed in one copy could easily be missed in another. Extracted
a shared recordSpawn() helper that all 3 sites now call.

Agent: complexity-hunter

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 07:46:57 +07:00
Muhammad Hashmi
9b176cd5b8
feat(daytona): add Daytona provider (#3168)
* feat(daytona): re-add Daytona cloud provider

* fix(daytona): tighten live provider behavior

* fix(daytona): harden reconnect and dashboard flows
2026-04-04 00:36:38 +00:00
A
0ffa035e35
fix(security): add command validation to local provider's runLocal/interactiveSession (#3160)
The local provider was missing the empty-string and null-byte command
validation that all other cloud providers (AWS, GCP, Hetzner, DO, Sprite)
already enforce. While callers currently pass hardcoded commands, this adds
defense-in-depth parity with the rest of the codebase.

Fixes #3155

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 12:18:04 +07:00
A
b99a16616f
fix(test): check sensitive paths before lstat to fix macOS permission error (#3157)
On macOS, lstat("/etc/master.passwd") throws EACCES before the
sensitive-path pattern check runs. Move pattern matching before
filesystem calls so security errors are thrown consistently
regardless of filesystem permissions.

Fixes #3153

Agent: test-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-03 10:12:20 +07:00
A
82a9939a80
fix(ux): interactive feedback prompt and link SSH error handling (#3147)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
- spawn feedback: prompt interactively for message when run in a TTY
  without arguments, instead of showing an error
- spawn link: report SSH failure after "Connect now?" instead of
  silently ignoring the exit code

Agent: ux-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-02 20:38:52 +07:00
A
70bba831f6
fix(security): use array-based spawn for docker commands in local.ts (#3145)
Replace string-interpolated shell commands in pullAndStartContainer()
with Bun.spawn() array arguments, eliminating shell interpretation
as defense-in-depth against command injection.

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-02 16:33:42 +07:00
A
0c4dc613b2
fix(security): sanitize control characters in prompt file error messages (#3141)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Reject file paths containing ASCII control characters (ANSI escape
sequences, null bytes, etc.) in validatePromptFilePath() to prevent
terminal injection. Also strip control chars in handlePromptFileError()
as defense-in-depth for error paths before validation.

Fixes #3138

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-01 20:38:43 +07:00
A
d61cf02b9b
fix(security): validate paths and agent names to prevent traversal/injection (#3139)
Fixes #3136 - add path validation to uploadFile/downloadFile in local.ts
Fixes #3135 - add agentName validation before Docker shell commands

- validateLocalPath() resolves paths and rejects ".." traversal attempts
- validateAgentName() ensures agent names match [a-z0-9-]+ before Docker ops
- Both functions are exported for testability

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-01 11:28:03 +00:00
A
41f6b6eb8f
fix(cli): add --flat to KNOWN_FLAGS so spawn list --flat works (#3137)
The --flat flag was documented in help output and used by `spawn list`
but missing from KNOWN_FLAGS, causing an "Unknown flag" error.

Agent: ux-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-01 16:33:45 +07:00
A
1599444517
fix(sandbox): use Docker runner for agent.configure() in sandbox mode (#3133)
Agent config functions (setupClaudeCodeConfig, setupCodexConfig, etc.)
captured the bare host runner from local/agents.ts, bypassing the Docker
wrapper. This caused config files like ~/.claude/settings.json to be
written to the host filesystem instead of inside the sandbox container.

Fix: when --beta sandbox is active, recreate agents with the Docker-wrapped
runner so configure()/install() closures execute inside the container.

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 22:16:52 -07:00
A
3b61c22f25
fix(security): validate script templates before base64 encoding (#3132)
Add pre-encoding validation to reject ${} interpolation patterns in
script template strings before they are base64-encoded and injected
into systemd services running with root privileges on remote VMs.

Defense-in-depth against future regressions where template variable
interpolation before encoding could allow command injection.

Fixes #3130

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-01 10:15:20 +07:00
A
426ebc9b76
fix: start Docker daemon on sandbox startup, not just after install (#3129)
The sandbox mode now starts the Docker daemon whenever it's not running,
not only after a fresh install. This handles the common case where
OrbStack/Docker is installed but the daemon isn't started yet.

Flow: check daemon → if down, check binary → if missing, install →
start daemon (open -a OrbStack / systemctl start docker) → poll up to 30s

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:50:57 -07:00
A
c1d8acb73e
feat: add Pi coding agent (shittycodingagent.ai) to spawn (#3128)
Pi is a minimal terminal coding agent by Mario Zechner (~29.8k GitHub
stars) that natively supports OpenRouter via OPENROUTER_API_KEY.
Installed via npm as @mariozechner/pi-coding-agent, CLI command is `pi`.

- Add Pi agent config across all 6 clouds (local, hetzner, aws, do, gcp, sprite)
- Add manifest.json entry with matrix entries
- Add agent-setup.ts config (node cloudInitTier, npm install)
- Add spawn-skill.ts injection path (~/.pi/agent/skills/spawn/SKILL.md)
- Add bash wrappers for all clouds
- Update README matrix (also adds missing Cursor CLI row: 10 agents, 60 combos)

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:34:34 -07:00
A
14ea507313
feat: add --beta sandbox for Docker-based local agent sandboxing (#3127)
* feat: add --beta sandbox for Docker-based local agent sandboxing

When running agents locally, users can now opt into sandboxed execution
via `--beta sandbox` or the interactive picker. This runs the agent
inside a Docker container (using pre-built ghcr.io/openrouterteam images)
with memory and CPU limits, providing filesystem/network isolation.

- Docker auto-installed if missing (OrbStack on macOS, docker.io on Linux)
- Reuses existing makeDockerRunner() pattern from Hetzner/GCP
- Container auto-cleaned up on process exit
- OpenClaw security warning skipped in sandbox mode (already isolated)
- Interactive picker shows Direct vs Sandboxed when Docker available

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

* fix: rename local machine to local

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>

* fix: remove memory limits and move sandbox to cloud picker

- Remove --memory=4g --cpus=2 from docker run (breaks small VMs and recursive spawns)
- Replace sandbox sub-prompt with a "Local Machine (Sandboxed)" option
  in the cloud picker itself, shown when --beta sandbox is active
- Docker availability check happens later in local/main.ts (ensureDocker),
  not in the picker — so the option always appears with --beta sandbox

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

* docs: add --beta sandbox to README

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

---------

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-03-31 17:00:49 -07:00
A
2b43996f60
fix(cli): allow --headless and --dry-run to be used together (#3117)
Removes the mutual-exclusion validation that blocked combining these flags.
Both flags serve independent purposes: --dry-run previews what would happen,
--headless suppresses interactive prompts and emits structured output.
Combining them is valid for CI pipelines that want structured JSON previews.

Fixes #3114

Agent: issue-fixer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-31 06:48:54 +07:00
A
9624141844
fix(security): expand $HOME before path validation in downloadFile (#3080)
Fixes #3080

Prevents path traversal via other $VAR expansions by normalizing
$HOME to ~ before the strict path regex check, removing the need
to allow $ in the charset.

Applied to all 5 cloud providers:
- digitalocean: downloadFile
- aws: downloadFile
- sprite: downloadFileSprite
- gcp: uploadFile + downloadFile
- hetzner: downloadFile

Also bumps CLI version to 0.27.7.

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:56:05 +00:00
A
ccbe52ccc2
fix: add cursor to agent lists in spawn skill files (#3108)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
cursor is a fully implemented agent across all 6 clouds but was missing
from the available agents list in spawn skill instructions injected onto
child VMs. This caused claude, codex, hermes, junie, kilocode, openclaw,
opencode, and zeroclaw to be unaware they could delegate work to cursor.

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-03-29 22:49:04 -07:00
A
749f79a9c2
feat(status): add agent alive probe via SSH (#3109)
`spawn status` now probes running servers by SSHing in and running
`{agent} --version` to verify the agent binary is installed and
executable. Results show in a new "Probe" column (live/down/—) and
as `agent_alive` in JSON output. Only "running" servers are probed;
gone/stopped/unknown servers are skipped.

The probe function is injectable via opts for testability.

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 22:44:46 -07:00
A
ddce16a438
fix(cursor): update proxy model list to current models (#3105)
Replace outdated models (Claude Sonnet 4, GPT-4o) with current ones:
- Claude Sonnet 4.6 (default), Claude Haiku 4.5
- GPT-4.1
- Gemini 2.5 Pro, Gemini 2.5 Flash

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 21:25:58 -07:00