The agent
thespider-agent is TheSpider's thin execution agent — a single Rust binary. It holds only a server URL, an auth token, and model_code → command bindings. All brain logic and all state live server-side; what reaches the client is one rendered prompt per claimed slice, by design.
Use the command surface
thespider-agent login # open <server>/activate, paste enrollment code (stdin), store token
thespider-agent logout # clear the stored token (keychain + file)
thespider-agent status # org/projects/entitlements, config sanity, token source
thespider-agent models # server catalog vs local [[model]] bindings, as a diff
thespider-agent run [flags] # claim slices, run models locally, upload results
thespider-agent gate --campaign <id> # poll the PR gate; exit 0 pass / 1 fail / 2 pending
thespider-agent daemon [flags] # persistent runner: poll → managed-clone → claim/run/submit
Enroll with login
login prints (and best-effort opens) <server>/activate, reads a short enrollment code from stdin, and POSTs /v1/enroll. It is identical in SaaS and on-prem — the /activate page is server-side, and enrollment codes are minted in the web UI or via bun run src/mint-enrollment-code.ts.
Run a campaign
thespider-agent run [--campaign <id>] [--once] [--models a,b] [--target-path <dir>] [--redact]
The loop:
GET /v1/me→ resolve org/project/entitlements.- Campaign:
--campaign <id>, else create afullcampaign from local git detect (branch/commit/remote_url/dirty; the client detects, the server records). - Claim a batch (sized to
concurrency), respecting the lease/timeout policy. For each lease: write the prompt to a temp file, substitute the model command, spawnsh -c, capture stdout/stderr/exit/duration, heartbeat the lease while the model runs, thenPOSTthe raw stdout echoingprompt_hash. - Response handling: accepted (
200), accepted-as-artifact / superseded (202), prompt_hash mismatch (409), too large (413). Network errors,429, and5xxretry with exponential backoff + jitter. --onceprocesses a single claim batch and stops (CI / one-shot). Without it,runloops until no claimable slices remain.
Redact on the client (opt-in)
The server secret scrubber is always on. --redact additionally, on the client, before upload:
- Masks secret-shaped substrings in raw stdout using the same pattern family — PEM private-key blocks, JWTs,
Authorization: Bearertokens, AWS access-key ids, GitHub/Slack/Google/OpenAI keys, and long high-entropy base64/hex runs — replacing them with«redacted». Ordinary prose, short identifiers, and file paths are preserved. - Truncates stdout to at most 1 MiB (stricter than the server's default artifact cap), appending a visible marker.
It never parses or reshapes findings semantics — it operates purely on the raw stdout string.
Run the persistent daemon (PR/CI pattern)
thespider-agent daemon [--interval <secs>] [--workdir <dir>] [--models a,b] [--redact] [--max-iterations <n>]
Run the daemon once on a team box or self-hosted CI runner holding a project-scoped token and local model creds; CI jobs then only poll the gate — no model credentials or model runtime in CI.
Loop:
GET /v1/me→ resolve the project.- Poll
GET /v1/projects/{id}/campaigns/claimable— campaigns with pending / expired-lease slices, newest first. These are created by the GitHub webhook (PR opened/synchronized → incremental campaign) or by a developer'srun. - For each workable campaign: managed clone — fetch and check out the campaign's exact commit into
<workdir>/<project>/<remote-hash>(a stable per-repo directory, reused across polls), detached — then claim/run/submit its slices exactly likerun, with{target_path}= the managed checkout. - Sleep a jittered interval (which backs off while idle) and poll again.
Handle daemon robustness
- Graceful shutdown on
SIGINT/SIGTERM— the in-flight campaign finishes; no new slices are claimed. - Per-campaign error isolation — a bad clone/checkout/run logs and the loop continues.
- A campaign with no
remote_url/commit(e.g. a CLI dirty-tree campaign) is skipped, not errored. --max-iterations <n>bounds the loop (E2E / one-shot CI).- The daemon prints a
daemon summary: iterations=… campaigns_worked=… leases=… accepted=… reports=… failed=…line.
Authenticate the managed clone
Git auth is git's own — the daemon shells out to git clone/git fetch:
- SSH remotes (
git@github.com:owner/repo.git) → a loaded ssh-agent key (or~/.ssh/configidentity) the daemon's user can use non-interactively. - HTTPS remotes → a token embedded in the remote URL (
https://x-access-token:<TOKEN>@github.com/owner/repo.git) or a configured git credential helper.
The repo_remote_url a project stores is what the webhook maps PRs to and what the daemon fetches — set it to a URL the runner can actually clone.