Commit graph

2451 commits

Author SHA1 Message Date
A
05fbb2ebdc
fix(security): validate realpath result before LOG_DIR deletion in e2e.sh (#3225)
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 #3222

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-08 07:43:34 +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
0fe16d3ffc
fix(security): shell-quote package names in cloud-init scripts (#3220)
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
Apply shellQuote() to package names interpolated into startup scripts
across all four cloud providers (GCP, AWS, Hetzner, DigitalOcean).
Defense-in-depth against supply chain attacks where compromised package
lists could inject shell metacharacters into root cloud-init scripts.

Fixes #3216

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-07 15:35:44 +07:00
Ahmed Abushagur
aad03f3b1b
feat(security): add periodic security scan cron for VMs (#3214)
Installs a cron job (every 6h) that checks for SSH key anomalies,
failed login attempts (brute-force), suspicious software (attack tools,
crypto miners), unexpected processes, rogue cron entries, and unusual
listening ports. Findings are written to /var/log/spawn-security-alerts.log
and displayed as warnings when users reconnect via `spawn connect`.

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:29: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
00d5a8cd58
fix(spa): replace double JSON.parse with valibot validation in helpers.ts (#3210)
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 #3203

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-07 07:51:07 +07:00
Muhammad Hashmi
deb4b4f39e
fix(daytona): improve onboarding UX for new users (#3209)
* fix(daytona): open Daytona dashboard for new users, save default sandbox profile

* fix: remove duplicate print
2026-04-07 07:41:35 +07:00
A
2599ad9928
feat(growth): dedup against DB + shuffle subreddits/queries (#3212)
- Skip posts already in SPA's candidate DB (any status)
- Shuffle subreddits and queries each run for variety
- Added new subreddits: ClaudeAI, webdev, openai, CodingWithAI
- Removed LocalLLaMA (wrong audience for cloud/OpenRouter pitch)
- Added new queries: "AI coding assistant server", "run Claude Code
  remote", "coding agent VPS", "AI dev environment cheap"

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-04-06 16:48:10 -07:00
A
1e858503cb
fix(growth): robust json:candidate extraction (#3211)
The sed + tr approach grabbed invalid JSON when Claude's output had
multiple candidate-like blocks or mixed analysis text. Switch to bun
script that tries to JSON.parse each match, keeping the last valid one.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-04-06 16:46:03 -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
f251ed59ba
fix(security): harden e2e.sh against injection, symlink, and DoS (#3197)
Some checks are pending
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
- Sanitize cloud/agent names before building email HTML (#3189)
- Validate result values against allowlist (pass/fail/skip)
- Resolve symlinks and check ownership before rm -rf (#3194)
- Add upper bounds on cloud/agent list sizes (#3190)

Fixes #3189 #3194 #3190

Agent: test-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 06:16:38 -07:00
A
d42cbca525
feat(growth): decision log for learning (#3187)
Some checks are pending
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Adds decision logging to track approved/edited/skipped Reddit growth candidates. The log feeds back into the Claude prompt to improve future candidate selection based on past patterns.
2026-04-06 00:38:30 +00:00
A
0ece17d92e
fix(growth): handle multi-line json:candidate extraction (#3185)
The Claude output contains pretty-printed JSON spanning multiple lines.
`tail -1` only grabbed the last line ("}"). Use `tr -d '\n'` to join
all lines into a single JSON string before POSTing to SPA.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-04-05 14:34:18 -07:00
A
32fad1c389
feat(spa): add /reply endpoint for Reddit comment posting (#3186)
SPA now handles Reddit replies directly instead of proxying to an
external growth VM. The /reply route authenticates with Reddit OAuth
and posts comments using the configured credentials.

This makes the growth pipeline fully self-contained on a single VM:
fetch → score → Slack card → approve → Reddit reply.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:32:21 -07:00
A
16d0e75f49
feat(growth): batch Reddit fetching for faster growth cycles (#3184)
Splits the growth agent into two phases:
1. reddit-fetch.ts — parallel batch fetch of all Reddit posts (~30s)
2. Claude scoring — pure text analysis of pre-fetched data (~30s)

Previously Claude made 56+ sequential tool calls through the LLM loop,
taking 5-10 minutes. Now the full cycle completes in ~1-2 minutes.

Also fixes empty stdout issue by using stream-json output format and
extracting text content from the event stream.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 13:12:05 -07:00
A
aa98039f95
fix(e2e): validate LOG_DIR ownership before rm -rf in final_cleanup (#3183)
Some checks are pending
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
* fix(e2e): validate LOG_DIR ownership before rm -rf in final_cleanup

Adds _E2E_CREATED_LOG_DIR tracking to ensure cleanup only removes
directories created by this script instance, not attacker-controlled paths.

Fixes #3181

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

* fix(e2e): restore SAFE_TMP_ROOT prefix validation alongside ownership check

Defense-in-depth: keep both the path prefix check (SAFE_TMP_ROOT/spawn-e2e.*)
and the ownership check (_E2E_CREATED_LOG_DIR) as two independent layers.

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>
2026-04-05 19:56:55 +07:00
A
ade67a0c9c
fix(spa): default HTTP port 3100 → 8080 (#3179)
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
The trigger-server already uses 8080 as the standard port for HTTP
services in this repo. Aligns SPA with that convention.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 05:19:19 +00: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
564b5001a4
fix(hetzner): remove snapshot lookup — always boot from fresh ubuntu image (#3176)
Snapshots built on larger server types cause "image disk is bigger than
server type disk" errors on cx23. Remove findSpawnSnapshot and snapshot
logic from Hetzner provisioning so it always uses ubuntu-24.04.

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 17:56:33 -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
34c42c92d0
fix(ux): require --yes for spawn list --clear in non-interactive mode (#3165)
spawn list --clear silently cleared all history in non-interactive mode
(piped stdin, CI, SSH) without any confirmation. This is inconsistent
with spawn delete which requires --yes. Add the same guard so
destructive history clearing requires explicit opt-in when there is no
TTY to show a confirmation prompt.

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-03 18:39:34 -07:00
A
e26d65cd65
fix: add cursor to AGENT_SKILLS and add pi/cursor to spawn-skill tests (#3164)
cursor was missing from the AGENT_SKILLS map in spawn-skill.ts, causing
spawn skill injection to silently skip cursor VMs when --beta recursive
is active. pi was present in AGENT_SKILLS but missing from all test
arrays in spawn-skill.test.ts.

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-03 17:59:11 -07:00
A
a31a821e8a
fix(openclaw): wait for bootstrap completion before opening dashboard (#3170)
Poll `openclaw status --json` after onboarding until bootstrapPending
is false (up to 60s). Prevents the Control UI from opening into a
broken state where chat fails with "No session found" because the
initial session hasn't been created yet.

Fixes #3167

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-03 17:57:34 -07:00
A
7292ddef0e
fix(cursor): use real API key instead of dummy spawn-proxy value (#3169)
Cursor CLI validates CURSOR_API_KEY before connecting to the configured
endpoint. The dummy value "spawn-proxy" fails validation immediately,
causing an infinite restart loop. Use the actual OPENROUTER_API_KEY as
CURSOR_API_KEY so it passes Cursor's key format check.

Fixes #3166

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-03 17:54:38 -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
493cd1c7ba
feat: Reddit growth agent with Slack approval workflow (#3142)
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: add Reddit growth discovery agent

Adds an automated agent that scans Reddit for threads where Spawn
solves someone's problem, qualifies the poster, and surfaces the
best candidate to Slack for human review. Does not auto-reply.

- growth.sh: service script (same pattern as refactor.sh)
- growth-prompt.md: Claude prompt for Reddit scanning + Slack posting
- growth.yml: GitHub Actions workflow (daily trigger)
- start-growth.sh: gitignored template for VM secrets

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

* refactor: strip Slack/GH issue from growth agent, output to log only

Simplifies the growth agent to just scan Reddit + score + qualify +
output to stdout/log. Slack (via spa) and GH issue logging will be
wired up separately.

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

* fix: replace Pi agent icon with correct logo from shittycodingagent.ai

Previous icon was a wrong GitHub avatar (Korean characters). Now uses
the official Pi logo (pixelated P with dot) from the project website.

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

* Revert "fix: replace Pi agent icon with correct logo from shittycodingagent.ai"

This reverts commit 43098b2754.

* feat: wire Reddit growth agent to Slack approval via SPA

Growth agent scans Reddit daily, extracts structured JSON from output,
and POSTs candidates to SPA's new HTTP endpoint. SPA posts Block Kit
cards to #proj-spawn with Approve/Edit/Skip buttons. Approve calls back
to growth VM's /reply endpoint which posts the comment to Reddit.

- growth-prompt.md: add json:candidate output format
- growth.sh: extract JSON + POST to SPA_TRIGGER_URL
- reply.sh: new script for Reddit comment posting via OAuth
- trigger-server.ts: add POST /reply endpoint
- SPA helpers.ts: add candidates table + CRUD
- SPA main.ts: HTTP server, button handlers, edit modal
- spa.test.ts: candidate DB operation tests

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

* fix: address security review findings on growth agent

- chmod 0600 temp prompt file to prevent credential exposure
- Use stdin redirect instead of $(cat) for claude -p to avoid shell expansion
- Use curl --data-binary @- heredoc instead of -d to prevent command injection
- Move reply.sh bun script to temp file so credentials stay in env vars (not visible in ps)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.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>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
2026-04-02 23:34:36 -07: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
15df9dfae3
fix(security): array-based agent detection and GCP instance name validation (#3158)
* fix(security): array-based agent detection and GCP instance name validation

Replace shell string concatenation in detectAgent() with individual
`command -v` calls per agent, eliminating the compound shell command.
Add _gcp_validate_instance_name() to validate GCP instance names match
[a-z][a-z0-9-]*[a-z0-9] before passing to gcloud commands.

Fixes #3151
Fixes #3149

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

* fix: add instance name validation in _gcp_cleanup_stale()

Defense-in-depth: validate instance names from GCP API before passing
to gcloud delete, consistent with validation at other call sites.

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>
2026-04-03 11:24:33 +07:00
A
e157637ab8
fix(e2e): add pi to E2E agent coverage (#3156)
Fixes #3152

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-03 10:15:43 +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
44940ceb5b
fix: update Hermes agent icon to Nous Research logo (#3150)
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 Hermes agent site now uses the Nous Research logo instead of the
old snake icon. Update our bundled asset to match.

Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 12:23:53 -07:00
A
7d593df09f
test: add coverage for validateAgentName and validateLocalPath (#3148)
These security-critical validation functions in local/local.ts had zero
direct test coverage. Adds tests for valid inputs, empty strings,
shell metacharacters, path traversal, and uppercase rejection.

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 01:52:52 +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
e3278578ee
fix(e2e): skip GCP tests when billing is disabled (#3146)
Add a billing pre-check to _gcp_validate_env so the E2E orchestrator
skips GCP gracefully ("skipped — credentials not configured") instead
of failing every agent individually when billing is disabled.

Fixes #3091

Agent: test-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 19:26:42 +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
4cf5e34383
fix: replace Pi agent icon with correct logo from shittycodingagent.ai (#3143)
Previous icon was a wrong GitHub avatar (Korean characters). Now uses
the official Pi logo (pixelated P with dot) from the project website.

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-04-02 00:28:34 -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
1dc5e43095
test: add coverage for validateScriptTemplate, resolveDisplayName, groupByType (#3140)
These three exported pure functions had zero test coverage. validateScriptTemplate
is security-critical (prevents ${} interpolation injection in script templates).

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-01 19:27:54 +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
cd9d12d2c4
fix(qa): add shutdown timeout and e2e-tester responsiveness to prevent infinite loop (#3134)
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
When the e2e-tester finishes work and goes idle without responding to
shutdown_request, the team lead retries indefinitely burning the entire
85-min budget in a shutdown loop.

Three fixes:
1. e2e-tester protocol: add explicit instruction to respond to
   shutdown_request immediately after reporting results
2. Step 4 shutdown sequence: add 60s timeout — if a teammate doesn't
   respond, proceed with TeamDelete anyway
3. Fix stale timeout reference (25/29/30 → 75/83/85 min)

Fixes #3093

Agent: issue-fixer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-01 13:43:09 +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
9895d6e8cc
fix: correct README agent count (10→9, 60→54) (#3131)
The Pi agent PR (#3128) bumped from 8→9 agents but the README tagline
was incorrectly set to "10 agents / 60 combinations" instead of matching
the manifest's actual 9 agents / 54 implemented entries.

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-01 10:11:51 +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