`run <scriptId>` auto-detects whether the web server is already reachable (a quick /api/healthz check): - If it is, the run goes through the existing POST /api/scripts/:id/runs endpoint (token-authenticated, same as any other API client) and the CLI subscribes over /ws/runs exactly like a browser tab - so the run shows up live in Run History and any open browser watching it, with zero server-side changes, since the broadcast path has no idea a run was triggered by a click vs a CLI invocation. - If nothing's reachable, it calls startRun() directly in its own process (after its own migrateOnBoot/reconcileOrphanedRuns, so a from-scratch .triggershell/ works standalone) and streams output by listening on the same in-process runEvents emitter a WS client would otherwise be fed from - read-log-then-listen, the same ordering ws/server.ts's subscribe() already uses, so a fast script finishing before the listener attaches still gets its output printed. Both modes support --var name=value (repeatable; repeat a name for multiselect), --no-wait, and Ctrl-C cancellation through the same mechanism the web UI's Cancel button uses (a WS cancel message remotely, cancelRun() directly locally). `scripts list`/`scripts show` are local-only, no network - same direct-config-read pattern as `validate`/`doctor`. Extracts defaultValuesForScript() out of dynamic-form.tsx into src/lib/config/defaults.ts so the CLI's --var handling and the web form fill in a script's configured defaults identically instead of duplicating that logic. Verified live end-to-end: a CLI-triggered remote run was observed streaming to both the triggering CLI process and an independent WS client (simulating a browser tab) simultaneously; local-mode Ctrl-C confirmed to actually kill the spawned child process, not just the CLI; token, wrong-token, and TRIGGERSHELL_API_TOKEN auth paths all verified against a running auth-enabled server. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3.3 KiB
API Reference
Base URL: http://<server.host>:<server.port> (default http://127.0.0.1:4173).
When auth.enabled: true, every endpoint below except /api/healthz, /api/auth/login, and
/api/auth/session requires either a valid session cookie or an Authorization: Bearer <token>
header using a token from triggershell users add-token.
triggershell run <scriptId> is a first-party client of this exact REST + WS contract (see
Running scripts from the CLI) - nothing below is
CLI-specific.
Auth
POST /api/auth/login
curl -c cookies.txt -X POST http://127.0.0.1:4173/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"..."}'
200 {"user": {"username": "admin"}} on success (sets the session cookie), 401 on bad
credentials, 429 if rate-limited.
POST /api/auth/logout
204 — clears the session.
GET /api/auth/session
200 {"authRequired": bool, "authenticated": bool, "user": {"username": string} | null}
Scripts
GET /api/scripts
200 {"scripts": [{"id", "name", "description"}]}
GET /api/scripts/:scriptId
Full schema for one script, including resolved UI control per variable and secret defaults
stripped:
curl -b cookies.txt http://127.0.0.1:4173/api/scripts/deploy-service
404 if not found.
POST /api/scripts/:scriptId/runs
Starts a run.
curl -b cookies.txt -X POST http://127.0.0.1:4173/api/scripts/deploy-service/runs \
-H "Content-Type: application/json" \
-d '{"variables": {"environment": "staging", "replicas": 2, "dryRun": true}}'
201 {"runId": string, "status": "queued"} on success. 400 {"error", "fieldErrors"} if the
variables fail validation (the same Zod schema the UI form uses). 404 if the script doesn't exist.
Runs
GET /api/runs
Query params: scriptId, status (one of queued|running|succeeded|failed|cancelled|timed_out|interrupted),
limit (default 50, max 200), cursor (offset, from the previous page's nextCursor).
200 {"runs": [...], "nextCursor": string | null}
GET /api/runs/:runId
The full run record (status, variables with secrets redacted, resolved command, timestamps, exit
code). 404 if not found.
POST /api/runs/:runId/cancel
202 {"status": "cancelling"}. 409 if the run already finished, or if it isn't tracked by this
server process (e.g. after a restart — see "orphaned runs" in docs/ARCHITECTURE.md).
GET /api/runs/:runId/logs
Plain-text log output. Query params: tail=<N> (last N lines only), download=1 (sets
Content-Disposition: attachment).
WebSocket: /ws/runs
Auth: session cookie, or ?token=<api-token> for non-browser clients (the upgrade request has no
Authorization header support, since it's a plain HTTP upgrade).
Client → server messages:
{ "type": "subscribe", "runId": "..." }
{ "type": "unsubscribe", "runId": "..." }
{ "type": "cancel", "runId": "..." }
Server → client messages:
{ "type": "output", "runId": "...", "stream": "stdout" | "stderr", "chunk": "...", "seq": 0, "ts": 0 }
{ "type": "status", "runId": "...", "status": "running", "exitCode": null, "ts": 0 }
{ "type": "error", "runId": "...", "message": "..." }
A client only receives messages for runs it has subscribed to.