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

@tokenfactory/acc-runner

v0.37.1

Published

Agent Control Center local runner. Spawns Claude Code sessions assigned via ACC.

Readme

@tokenfactory/acc-runner

CLI runner that polls Agent Control Center for queued tasks and executes them via Claude Code.

Install

npm i -g @tokenfactory/acc-runner
# or
pnpm add -g @tokenfactory/acc-runner

Requires Node.js ≥ 20.6, plus these binaries on PATH:

  • claude — Claude Code CLI, signed in
  • gh — GitHub CLI, signed in (gh auth login)
  • git — with user.email configured
  • A local clone of the target repo (the repoPath, see below)

Run acc-runner doctor after install to verify everything resolves.

Configuration

The runner resolves its settings from three layers, highest precedence first:

  1. Environment variables (ACC_*)
  2. ~/.config/acc-runner/config.json (or $XDG_CONFIG_HOME/acc-runner/config.json)
  3. Built-in defaults — production endpoints baked into the CLI so a fresh npm i -g works out of the box

Production defaults are baked in; you only need to set anything if you want to point the runner at a preview deploy, change the repo clone location, or declare capabilities.

Environment variables

| Variable | Default | Notes | | ------------------------- | ---------------------------------------- | --------------------------------------------------------------------- | | ACC_PUBLIC_URL | production Vercel URL | Used for cost-pricing fetches and OAuth callback links. | | ACC_SUPABASE_URL | production project URL | Supabase project the runner talks to. | | ACC_SUPABASE_ANON_KEY | production anon key | Public; RLS does the gating. | | ACC_REPO_PATH | ~/work/TechArch | Local clone of the target repo. See "Repo path resolution" below. | | ACC_TARGET_REPO | tokenfactory-pvt-ltd/TechArch | Slug fallback. A task's own repo wins when present. | | ACC_INTEGRATION_BRANCH | acc/integration | Base branch for new PRs. A task's integration_branch wins. | | ACC_RUNNER_CAPABILITIES | (unset) | Comma-separated tags, e.g. code,docs. See "Capabilities" below. | | ACC_RUNNER_PROFILE | (unset) | Profile slug (developer, designer, tester, manager). | | ACC_RUNNER_TOKEN_MODE | keychain | env sources credentials from env vars instead of the OS keychain. See "Ephemeral runners" below. | | ACC_RUNNER_ACCESS_TOKEN | (unset) | Required when ACC_RUNNER_TOKEN_MODE=env. The Supabase JWT minted by the device-code exchange or autoscale provider. | | ACC_RUNNER_REFRESH_TOKEN| (unset) | Optional. Enables in-process token rotation in env mode; rotated successors stay in memory only. | | ACC_RUNNER_ID | minted acc-eph-<hex8> | Optional pre-assigned runner id (env mode). The acc-eph- prefix marks the ephemeral fleet — don't reuse it for desktop runners. Also keys the singleton lock and (when no ACC_RUNNER_CACHE_DIR) namespaces the cache dir, so distinct ids coexist on one host. | | ACC_RUNNER_CACHE_DIR | ~/.cache/acc-runner (or …/runners/<ACC_RUNNER_ID>) | Cache root for locks, worktrees, quarantine, and the singleton lock. When unset and ACC_RUNNER_ID is set, defaults to a per-identity subdir. Set explicitly to override. | | ACC_RUNNER_CONCURRENCY | 2 | Max tasks this runner processes simultaneously. Parsed with Number.parseInt, clamped to 1..50, falls back to 2 on non-numeric input. Each slot is a live Claude session plus a git worktree, so high values are only viable on API-key runners on high-resource hosts. On a Claude subscription they will 429. The serial integration-merge queue still caps end-to-end throughput, so real-world gains plateau around ~2–2.5× regardless of how high this is set. |

~/.config/acc-runner/config.json

For settings you don't want to repeat in your shell profile (especially when acc-runner watch runs under launchd and inherits a stripped environment), drop a JSON file at ~/.config/acc-runner/config.json. All fields are optional.

{
  "repo-path": "/Users/you/code/TechArch",
  "capabilities": ["code", "migration"],
  "runner-profile": "developer"
}

| Field | Notes | | ---------------- | -------------------------------------------------------------------------------------- | | repo-path | Local clone path. Overrides the ~/work/TechArch default; ACC_REPO_PATH overrides this. | | capabilities | Array of capability tags. Overridden by ACC_RUNNER_CAPABILITIES when set. | | runner-profile | Profile slug; overridden by ACC_RUNNER_PROFILE when set. |

The file is read at process startup. Missing file or malformed JSON falls back to the next layer silently — the runner never crashes on a config-file read error.

Repo path resolution

acc-runner picks the local repo clone in this order — the first non-empty value wins:

  1. The task row's repo_path_hint (set by the operator when assigning the task) — lets a single runner span repos.
  2. ACC_REPO_PATH environment variable.
  3. repo-path field in ~/.config/acc-runner/config.json.
  4. ~/work/TechArch — the built-in default.

Until v0.8.1, only steps 1, 2, and 4 were consulted: a repo-path in config.json was silently ignored, and a runner launched by launchd (which strips most environment variables) would default to ~/work/TechArch and fail every task pickup with spawn git ENOENT. Set either ACC_REPO_PATH or repo-path in config.json and verify with acc-runner doctor.

Capabilities

Tags declare what kinds of tasks this runner is willing to pick up. The server's task-router only dispatches a task to a runner whose capability set covers the task's required_capabilities. The canonical vocabulary is:

code, design, test, docs, migration, review, orchestration

An empty list (the default) matches any task with an empty required_capabilities — that's backward-compatible with pre-v0.6.4 behavior. Resolution order, highest first:

  1. ACC_RUNNER_CAPABILITIES (comma-separated)
  2. capabilities field in config.json
  3. The active runner profile's capabilities
  4. []

Runner profiles

A runner profile bundles capabilities and model defaults under a single name. Profiles ship inside the package at packages/acc-runner/runner-profiles/<name>.json. Available profiles:

  • developer — code + migration work (default fleet member)
  • designer — design work
  • tester — test authoring + review
  • manager — orchestration

Select a profile via ACC_RUNNER_PROFILE=designer or "runner-profile": "designer" in config.json. Unknown profile names abort CLI startup with a clear error rather than failing at task pickup. Env vars and config.json capabilities still override the profile's defaults when both are set.

Ephemeral runners (env-token mode)

Desktop runners read their session from the OS keychain (the default, ACC_RUNNER_TOKEN_MODE=keychain). Containerized / autoscaled runners have no keychain, so set ACC_RUNNER_TOKEN_MODE=env and source the session from environment variables instead:

export ACC_RUNNER_TOKEN_MODE=env
export ACC_RUNNER_ACCESS_TOKEN=<jwt-from-device-code-exchange>
export ACC_RUNNER_REFRESH_TOKEN=<refresh-token>   # optional; enables rotation
export ACC_RUNNER_ID=acc-eph-1a2b3c4d              # optional; minted if unset
acc-runner doctor    # validates the env-token config (see below)
acc-runner watch
  • ACC_RUNNER_ACCESS_TOKEN is required. user_id / email / expiry are decoded from the JWT — no extra env needed. A missing or undecodable token fails doctor and aborts watch at startup, not on the first RPC.
  • ACC_RUNNER_REFRESH_TOKEN is optional. With it, the runner rotates the access token in process memory (never written to disk). Without it, the runner simply exits when the access token expires — fine for an idle-TTL-bounded ephemeral runner. Provision a fresh access/refresh pair per container: replaying a previously-rotated refresh token trips the server's reuse detection and revokes the chain.
  • ACC_RUNNER_ID lets the orchestrator pre-assign a stable id. When unset, the runner mints one acc-eph-<hex8> per process; the acc-eph- prefix is the autoscale sweep's ephemeral-fleet discriminator.

acc-runner doctor adds an env-token config check in this mode: it confirms the access token is present and decodable, then reports the resolved runner id (and whether it came from ACC_RUNNER_ID or was minted), whether refresh is wired, and the access-token expiry. The check is a silent no-op for keychain runners.

Running multiple runners on one host

Each runner keeps its locks, per-task worktrees, quarantine sentinel, and single-instance lock under a cache root that defaults to ~/.cache/acc-runner. As of v1.02-A, giving each co-located runner a distinct ACC_RUNNER_ID is enough to run them concurrently on one host:

ACC_RUNNER_ID=acc-eph-aaaa0001 acc-runner watch &
ACC_RUNNER_ID=acc-eph-bbbb0002 acc-runner watch &

The single-instance lock is keyed by (repo path + runner id), so two distinct ids serving the same repo each acquire their own lock and coexist; only a true duplicate — same repo, same identity — is refused. With no explicit ACC_RUNNER_CACHE_DIR, each id also gets its own cache subdir (~/.cache/acc-runner/runners/<id>), so their locks, worktrees, and quarantine state never collide. You can still pin an explicit ACC_RUNNER_CACHE_DIR per runner if you want the cache somewhere specific:

ACC_RUNNER_ID=acc-eph-aaaa0001 ACC_RUNNER_CACHE_DIR=/var/run/acc/a acc-runner watch &
ACC_RUNNER_ID=acc-eph-bbbb0002 ACC_RUNNER_CACHE_DIR=/var/run/acc/b acc-runner watch &

Two runners with the same identity (or both with no ACC_RUNNER_ID) serving the same repo are still caught by the singleton lock. acc-runner doctor prints the resolved token mode and cache dir in its Runtime block, and names the holding identity if a duplicate is detected.

PATH under launchd

launchd resets PATH to /usr/bin:/bin:/usr/sbin:/sbin, which omits the Homebrew bin dirs where git and gh typically live. The runner prepends /opt/homebrew/{bin,sbin} and /usr/local/{bin,sbin} to its own PATH before spawning these binaries — no launchd plist edits required. If acc-runner doctor reports git or gh as missing under launchd, install them via Homebrew and re-run.

Quickstart

acc-runner login          # device-code OAuth in your browser
acc-runner doctor         # verify config + binaries are all green
acc-runner watch          # long-running; receives + executes tasks

Then, in ACC, assign a task to this machine and click Start. The runner will:

  1. Transition the task to running.
  2. git fetch && git checkout -B acc/T-XX-<slug> in the resolved repoPath.
  3. Pipe the rendered prompt into claude --print --dangerously-skip-permissions.
  4. Stream stdout/stderr to acc.task_events (visible in the History tab in ACC).
  5. git push and open a PR titled [T-XX] <task title> via gh.

Commands

acc-runner login

Device-code OAuth flow. Stores a Supabase JWT in your OS keychain (acc-runner service) and registers this machine in acc.runners with its capability tags.

acc-runner logout

Clears the keychain entry and marks this runner offline.

acc-runner watch

Subscribes to a Realtime broadcast channel scoped to your runner. Sends a heartbeat every 4s. Cancellation is honored via task_cancelled broadcasts (sends SIGTERM to the in-flight claude child).

acc-runner doctor

Runs every environment check (binaries, keychain access, env-token config, Supabase reachability, repo path, orphan worktrees) and prints a green/red list, preceded by the resolved Config and Runtime (token mode + cache dir) blocks. Exits 0 if all pass, 1 otherwise. A single check that throws is reported as one [FAIL] row rather than crashing the run. Suitable for CI and for diagnosing a silent failure mode after install.

acc-runner version

Prints the CLI version and the protocol version it speaks. The server exposes a minimum-protocol-version pin at /api/runner/min-version; the CLI refuses to start if it's behind.

Troubleshooting

spawn git ENOENT on every task pickuprepoPath resolves to a directory that doesn't exist. Set ACC_REPO_PATH or repo-path in config.json to the absolute path of your local clone, then re-run acc-runner doctor.

**✗ Not signed in. Run \acc-runner login` first.** — keychain entry was cleared or never created. Run acc-runner login`.

✗ keychain read/write (FAIL) — on Linux, install libsecret. On macOS, ensure your login keychain is unlocked. On Windows, run from a session with credential manager access.

✗ register_runner failed — the migration shipping acc.register_runner may not be applied to the deploy you're pointed at. Coordinate with the operator who applies migrations.

Task transitions to failed immediately — open the History tab in ACC. Common causes: git fetch permission denied, claude --print returned non-zero, or the rendered prompt lacked acceptance criteria.

Runner version rejected (426 Upgrade Required) — the server's MIN_VERSION was bumped past your installed CLI. Run npm i -g @tokenfactory/acc-runner@latest.

Runner quarantined after a single flaky run (v0.53+) — a lone instant-empty claude exit no longer benches the machine: the runner logs an env_broken_blip event and retries the spawn once. Only two consecutive instant-empty exits — or a definitive failure (missing dyld library, OOM, exit 127) — quarantine the runner. acc-runner doctor and acc-runner quarantine status print the cause, the consecutive count, and the acc-runner quarantine clear command to resume.

GitHub rate-limit awareness (v0.53)

src/github-client.ts is a budget-aware GitHub REST wrapper (ported from the server's T-52-4 client): per-resource quota tracking, one jittered backoff retry on a 429 / secondary-limit 403, and a per-task-execution soft call budget that fails clean as a retryable infra error (never quarantines).

Scope: the runner currently performs all of its GitHub work through the gh CLI — gh pr create/list (src/gh.ts), gh api conflict resolution (src/runtime/conflict-resolver.ts), gh pr view head-ref lookup (src/runtime/rework.ts), and gh pr view/gh pr diff for reviews (src/runtime/reviewer.ts). Those are separate processes with their own rate-limit handling and are not routed through github-client.ts — they remain uncovered by its budget/backoff. The client is the sanctioned path for any future direct REST call (fetch against api.github.com) the runner adds.

Conflict-resolution integrity guardrail (v0.57, T-57-2)

When a Claude/worktree task whose job is to resolve a merge conflict finishes, the runner does not trust the agent's self-reported gate results. Before the resolution is pushed, src/runtime/resolution-integrity.ts re-verifies it from the runner's own vantage point. The invariant — a conflict resolution is shippable only if all three hold:

  1. Zero conflict markers remain in any tracked file (<<<<<<< / ======= / >>>>>>>).
  2. pnpm typecheck passes (or the task's typecheck_cmd).
  3. No version regression: if a package.json "version" changed in the resolution it must move forward (semver-greater than the base ref, never backward) and CHANGELOG.md must carry a matching entry for the new version.

On ANY failure the resolution is rejected: the runner does not push and does not mark the task resolved. The task transitions to failed (so it is reworked, not merged broken), an append_task_event of kind resolution_integrity_failed records the offending assertion(s), and a conflict.integrity_rejected activity event surfaces it to the operator. A guardrail that itself errors fails closed (rejects). The check is a no-op for every non-conflict task — it is gated by isConflictResolutionTask.

This exists because v0.55–v0.56 agent resolutions repeatedly shipped broken merges that were still marked "resolved": stale-variable splices (task-runner.ts), duplicate declarations (health.ts), and a silently dropped version bump (#496 reverted package.json 0.14.0 → 0.13.0).

Security notes

  • The CLI never reads or writes your git, gh, or Anthropic credentials. It calls binaries that are already authenticated on your machine.
  • The Supabase JWT is short-lived and stored in your OS keychain via keytar, never on disk in plaintext.
  • Heartbeat interval is 4s — do not tighten this. Tighter intervals consume the Realtime budget.

Runner sandbox

acc-runner spawns claude --print --dangerously-skip-permissions so non-interactive task execution doesn't hang on per-edit approval prompts. The permission boundary is enforced by the surrounding context, not by claude itself:

  • cwd boundary: the spawn is bounded to the resolved repoPath. Claude cannot read or edit files outside this repo.
  • allowed/forbidden globs: the task's acc.tasks.allowed and acc.tasks.forbidden arrays are rendered into the prompt; claude is instructed which paths it may or may not touch.
  • operator-controlled install: an operator deliberately runs acc-runner watch on their own laptop. The runner is not a daemon and exits when the operator stops it.

License

Proprietary. Not for redistribution.