npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@spawnrun/cli

v0.1.0

Published

Safe-by-default autonomous Claude Code: OS-level isolation + a hard subscription-billing guarantee + a bounded PR-only agent loop. One install.

Readme

spawnrun 🔒 — autonomous Claude Code that can't burn the house down

Let Claude Code run unattended — overnight, in a loop, no human in the loop — without risking your machine, your data, or a surprise API bill. spawnrun wraps every run in an isolation sandbox with deny-by-default egress, hard-guarantees you stay on your Claude subscription (never the paid per-token API), and keeps output to a bounded, PR-only loop that never merges.

It doesn't reinvent anything — it's an opinionated, safe-by-default composition of tools that already exist (srt or Docker for isolation, Claude Code's headless mode, the ralph loop pattern), with the one guarantee none of them ships on its own: your subscription, never the API.

Highlights

  • 🔒 Isolation, not trust — every run is sandboxed (srt on macOS/Linux, or a Docker container) with a deny-by-default egress allowlist. MCP servers, hooks, and file tools can't reach anything you didn't allow.
  • 💸 Never a surprise API bill — refuses to run if ANTHROPIC_API_KEY is set, and pins subscription OAuth. You stay on Pro/Max.
  • 🌿 Bounded & PR-only — iteration/turn/error caps; works on its own branch; opens a PR; never merges to your base branch.
  • 🐳 Two isolation backends — host srt on your Mac, or a firewalled Docker container on Linux VMs (egress locked to Anthropic).
  • 🛰️ Runs anywhere — local, over SSH, or inside any container/microVM, with the same command.
  • 📦 Zero build, zero runtime deps — pure Node ESM. npx -y @spawnrun/cli and go.

⚠️ Early release (v0.1). The default srt backend depends on @anthropic-ai/sandbox-runtime, an experimental Anthropic project (the container backend doesn't need it). Treat isolation as protection against an overeager agent, not a malicious one — only run trusted repos unattended.

Why

Running claude --dangerously-skip-permissions unattended on your host is dangerous: there's no isolation boundary, MCP servers/hooks/file tools run unconstrained, and a stray ANTHROPIC_API_KEY silently bills the paid API. spawnrun closes all three holes:

  • Isolation — every run is wrapped in a sandbox with a deny-by-default network allowlist: either srt (macOS Seatbelt / Linux bubblewrap, default) or a throwaway Docker container (--isolation container). Nothing reaches the internet except the domains you allow.
  • Subscription billing, guaranteedspawnrun refuses to run if ANTHROPIC_API_KEY is set, and pins Claude Code to subscription OAuth (forceLoginMethod=claudeai). No accidental API spend.
  • Bounded, PR-only output — the loop caps iterations, turns, and consecutive errors; works on its own branch; opens a PR; and never merges to your base branch. A human always reviews.

Install

Requires Node ≥18 and (for --pr) the GitHub CLI gh.

# no install — just run it
npx -y @spawnrun/cli --help

# or install globally (the command is `spawnrun`)
npm install -g @spawnrun/cli

# or clone (zero build step)
git clone https://github.com/rcmiskin10/spawnrun && cd spawnrun && node src/cli.mjs --help

Then, depending on the isolation backend:

  • srt (default): Claude Code installed and logged in on a Pro/Max plan.
  • container: Docker installed, plus a subscription token in ~/.claude.env (see Isolation backends). Claude Code runs inside the image, so it need not be on the host.

Quickstart

spawnrun setup        # install srt + pin subscription billing (idempotent)
spawnrun doctor       # confirm auth/billing/isolation are safe (exit 0 = go)

spawnrun run "fix the failing tests in this repo"       # one bounded sandboxed pass

spawnrun init                                            # write .spawnrun.json (optional)
spawnrun loop "add pagination to the users API" --pr     # bounded PR-only autonomous loop

Commands

| Command | What it does | |---|---| | spawnrun setup | Prepare the chosen backend (install srt + pin forceLoginMethod=claudeai, or build the container image + check the token file), warn if ANTHROPIC_API_KEY is set. Idempotent. Honors --isolation. | | spawnrun doctor | Check billing/isolation/tooling for the chosen backend. Exit non-zero if anything is unsafe. Honors --isolation. | | spawnrun run "<task>" | One bounded, isolated, subscription-billed Claude Code pass. | | spawnrun loop "<goal>" | Bounded PR-only loop: fresh-context iterations until done, error cap, own branch, never merges. | | spawnrun schedule "<goal>" --cron "0 2 * * *" | Print (or --install) a cron entry that runs a bounded loop. Best on a cloud VM — laptop sleep suspends cron. | | spawnrun init | Drop a .spawnrun.json into the current repo. |

Configuration (.spawnrun.json)

Defaults are safe. Generate a starter file with spawnrun init, then override per-repo or with flags. Every key below has a matching flag where it makes sense (--max-iterations, --max-turns, --error-threshold, --pr, --allow-api-billing, --isolation, --container-image, --container-env-file, --target, --ssh-host, --remote-dir, --exec-template). See spawnrun --help.

Loop kill-criteria & output

| Key | Default | Meaning | |---|---|---| | maxIterations | 8 | hard cap on loop passes | | errorThreshold | 3 | consecutive failed passes before stopping | | maxTurns | 30 | per-pass Claude Code turn cap | | branchPrefix | spawnrun/ | loop branch namespace | | openPR | false | push + open a PR at loop end (never merges) | | doneSentinel | <SPAWNRUN_DONE> | token the agent prints when the goal is complete | | allowApiBilling | false | permit running with ANTHROPIC_API_KEY set (paid API) |

Isolation backend

| Key | Default | Meaning | |---|---|---| | isolation | "srt" | isolation backend: "srt" (host) or "container" (Docker) | | allowedDomains | Anthropic/npm/GitHub | srt: deny-by-default egress allowlist | | denyWrite | .env, ~/.ssh, ~/.aws | srt: paths the sandbox may never write | | denyRead | ~/.ssh, ~/.aws | srt: paths the sandbox may never read (secrets) | | containerImage | "spawnrun-arm" | container: image name (auto-built from assets/container) | | containerEnvFile | "~/.claude.env" | container: file holding CLAUDE_CODE_OAUTH_TOKEN | | containerAllowDomains | [] | container: extra egress hosts beyond Anthropic (e.g. registry.npmjs.org) |

Execution target (where run/loop execute — see Execution targets)

| Key | Default | Meaning | |---|---|---| | target | "local" | local | ssh | exec | | sshHost | null | target ssh: you@box (Tailscale name ok) | | remoteDir | null | target ssh: working dir on the remote | | execTemplate | null | target exec: command template containing {CMD} |

How the loop works

  1. Creates a fresh branch off your current HEAD (never touches it again).
  2. Each iteration: a clean-context sandboxed Claude Code pass reads SPAWNRUN_NOTES.md, does the next chunk, runs tests/build, appends progress, and prints the done-sentinel only when the whole goal is complete and verified.
  3. Commits after every successful iteration.
  4. Stops on: the sentinel, the consecutive-error threshold, or the max-iteration cap.
  5. With --pr: pushes and opens a PR. You merge.

Isolation backends — srt (default) or container

spawnrun isolates the agent two ways; pick with --isolation (or "isolation" in .spawnrun.json):

| Backend | Boundary | Best for | |---|---|---| | srt (default) | host OS-isolation (macOS Seatbelt / Linux bubblewrap) + egress allowlist | your Mac; quick local runs | | container | a throwaway Docker container with an in-container egress firewall (Anthropic-only) | Linux VMs, where srt-on-host is painful (e.g. Ubuntu 24.04's userns/AppArmor restrictions) |

Container mode runs claude inside the container as the boundary. The bundled image (assets/container/) bakes Claude Code in and firewalls egress to Anthropic-only via iptables; git push + PR run on the host, so the container needs no GitHub token and no GitHub/npm egress. Auth is your subscription, passed as a token file — never the paid API key:

# one-time: mint a subscription token (Pro/Max) and store it for the container
claude setup-token
printf 'CLAUDE_CODE_OAUTH_TOKEN=%s\n' "<paste-token>" > ~/.claude.env && chmod 600 ~/.claude.env

# then run/loop with container isolation (image auto-builds on first use)
spawnrun setup  --isolation container      # builds the image, checks the token file
spawnrun doctor --isolation container      # docker + image + token, exit 0 = go
spawnrun loop "add pagination" --pr --isolation container

Tunables: --container-image <name> (default spawnrun-arm), --container-env-file <path> (default ~/.claude.env), and containerAllowDomains in config to widen egress (e.g. add registry.npmjs.org for a task that installs packages). The CLAUDE_CODE_OAUTH_TOKEN is per-user — mint your own; spawnrun never ships, centralizes, or transmits it.

Why not the Agent SDK? Subscription OAuth tokens are only sanctioned with the real claude CLI, not the Agent SDK. spawnrun always drives the CLI. See the token/ToS notes in the design doc.

Claude Code skill

skills/safe-autonomous-run/ is a bundled Claude Code skill — copy it into your project's .claude/skills/ so an agent knows to reach for spawnrun (instead of a bare --dangerously-skip-permissions) whenever autonomous/overnight/loop work comes up.

Execution targets — swap where runs happen without changing commands

The same run/loop commands can execute in three places. Set target in .spawnrun.json or pass --target:

| Target | Where it runs | Config | |---|---|---| | local (default) | srt on this machine | — | | ssh | a remote box (e.g. your Hetzner/Tailscale VM) | --ssh-host you@box [--remote-dir /srv/app] | | exec | any VM/container, via a command template | --exec-template "<cmd with {CMD}>" |

# run on your always-on box (needs spawnrun + the repo already there)
spawnrun loop "add pagination" --pr --target ssh --ssh-host you@hetzner --remote-dir /srv/app

# run inside a throwaway container/microVM — no vendor SDK, just your CLI:
spawnrun run "fix tests" --target exec --exec-template "docker run --rm -v \$PWD:/w -w /w node:22 {CMD}"
spawnrun run "fix tests" --target exec --exec-template "e2b sandbox run -- {CMD}"

{CMD} is replaced with the on-target spawnrun … --target local invocation, so isolation + billing guarantees still apply inside the target. This is how you move from the Hetzner box to an ephemeral microVM (for untrusted code) by changing one config line — no code change.

Overnight runs on a Linux VM (Hetzner / Fly / DO)

A laptop sleeps and cron won't fire; a cheap always-on VM (~$5–12/mo) is the reliable place for overnight autonomous runs. On Linux the recommended backend is --isolation container — it sidesteps the srt-on-host friction (Ubuntu 24.04's unprivileged-userns/AppArmor block on bubblewrap) by making a Docker container the boundary:

# on the VM (e.g. ssh in over Tailscale — keep the box off the public internet)
curl -fsSL https://raw.githubusercontent.com/rcmiskin10/spawnrun/main/scripts/bootstrap-linux.sh | bash
claude setup-token           # mint a subscription token; store it (see "Isolation backends")
printf 'CLAUDE_CODE_OAUTH_TOKEN=%s\n' "<token>" > ~/.claude.env && chmod 600 ~/.claude.env
spawnrun setup  --isolation container && spawnrun doctor --isolation container
spawnrun schedule "<goal>" --cron "0 2 * * *" --pr --install   # PR-only, review in the morning

(Prefer host srt isolation instead? Drop --isolation container; the bootstrap installs bubblewrap and, on Ubuntu 24.04, lays down the bwrap AppArmor profile.)

Keep long-lived cloud creds off the box; use a fine-grained GitHub token scoped to the target repo. The box still needs outbound access to api.anthropic.com + GitHub (both in the egress allowlist); Tailscale secures access to the box, it doesn't gate that egress.

Bash fallback

No Node? scripts/claude-sandbox.sh mirrors spawnrun run for a single bounded pass.

License

MIT