diff --git a/content/docs/guides/index.mdx b/content/docs/guides/index.mdx index 9f2dfc9..c6c9f8b 100644 --- a/content/docs/guides/index.mdx +++ b/content/docs/guides/index.mdx @@ -38,7 +38,7 @@ The catalog is being built out one guide at a time, prioritized by the questions | [Set up memory](/docs/guides/set-up-memory) | How do I enable memory and inspect what is stored? | | [Fix a failed install](/docs/guides/fix-a-failed-install) | My install broke — how do I diagnose and repair it? | | [Upgrade Coven](/docs/guides/upgrade-coven) | How do I upgrade safely without losing sessions? | -| Script the API | How do I call the daemon API from my own scripts? | +| [Script the API](/docs/guides/script-the-api) | How do I call the daemon API from my own scripts? | ## Start here today diff --git a/content/docs/guides/meta.json b/content/docs/guides/meta.json index 682abe9..702fe05 100644 --- a/content/docs/guides/meta.json +++ b/content/docs/guides/meta.json @@ -3,5 +3,5 @@ "description": "Step-by-Step How-Tos", "root": true, "icon": "LuCompass", - "pages": ["index", "install-and-first-run", "connect-a-harness", "set-up-the-daemon", "set-up-memory", "fix-a-failed-install", "upgrade-coven"] + "pages": ["index", "install-and-first-run", "connect-a-harness", "set-up-the-daemon", "set-up-memory", "fix-a-failed-install", "upgrade-coven", "script-the-api"] } diff --git a/content/docs/guides/script-the-api.mdx b/content/docs/guides/script-the-api.mdx new file mode 100644 index 0000000..b073f02 --- /dev/null +++ b/content/docs/guides/script-the-api.mdx @@ -0,0 +1,113 @@ +--- +title: "Script the API" +summary: "Call the daemon API from your own scripts: health handshake, listing and launching sessions, following events with a cursor, and handling structured errors." +description: "Step-by-step guide to scripting the Coven daemon socket API with curl: health, sessions, session launch, event cursors, and the error envelope." +read_when: + - You want your own script or tool to drive Coven instead of the CLI + - You need the minimal correct client loop: health, launch, events, errors +--- + +Everything the CLI and every other client does goes through the daemon's local HTTP API. This guide builds the minimal correct client loop with `curl`: handshake, list, launch, follow events, and branch on structured errors. The full contract is the [API reference](/docs/reference/api) and the [OpenAPI pages](/docs/openapi). + +## Prerequisites + +- The daemon running — see [Set up the daemon](/docs/guides/set-up-the-daemon). +- `curl`; `jq` helps but is optional. +- A project directory and one connected harness for the launch step — see [Connect a harness](/docs/guides/connect-a-harness). + +All examples use the Unix socket. Set the path once: + +```bash +SOCK="$HOME/.coven/coven.sock" +``` + +## Step 1 — Start with the health handshake + +Every client should begin every run here: + +```bash +curl --unix-socket "$SOCK" http://localhost/api/v1/health +``` + +**Expected output:** `"ok": true`, `"apiVersion": "coven.daemon.v1"`, and a capability map. Scripts should verify the `apiVersion` matches the contract they were written for and check `GET /api/v1/capabilities` before using optional features — see [versioning rules](/docs/reference/api#versioning). + +## Step 2 — List sessions + +```bash +curl --unix-socket "$SOCK" http://localhost/api/v1/sessions +``` + +**Expected output:** a JSON array of session records with snake_case fields (`id`, `project_root`, `harness`, `title`, `status`, `created_at`, …). An empty array just means no active, non-archived sessions. The record shape is documented under [session records](/docs/reference/api#session-records). + +## Step 3 — Launch a session + +`POST /api/v1/sessions` validates the project boundary before spawning anything: + +```bash +curl --unix-socket "$SOCK" http://localhost/api/v1/sessions \ + -H 'content-type: application/json' \ + -d '{ + "projectRoot": "/Users/you/code/your-repo", + "cwd": "/Users/you/code/your-repo", + "harness": "codex", + "prompt": "explain this repo in 5 bullets", + "title": "API launch smoke test" + }' +``` + +**Expected output:** `201` with the created session record — capture its `id` for the next step. `cwd` must resolve inside `projectRoot`, `harness` must be a supported harness id, and provider credentials are never part of the request. + +## Step 4 — Follow events with the cursor + +Poll the event page for your session, then resume from `nextCursor.afterSeq`: + +```bash +curl --unix-socket "$SOCK" \ + "http://localhost/api/v1/events?sessionId=&afterSeq=0&limit=100" +``` + +**Expected output:** an `events` array (each with `seq`, `kind`, `payload_json`), a `nextCursor`, and `hasMore`. The loop that never loses data: + +1. Request with your last `afterSeq` (start at `0`). +2. Process the returned events. +3. If `nextCursor` is non-null, persist `nextCursor.afterSeq` — it survives daemon restarts. `nextCursor` is `null` when the page is empty; keep your previous cursor. +4. Repeat while `hasMore` is `true`, then poll on your own interval. + +The daemon caps `limit` at 1000. `GET /api/v1/sessions//events` is the same read, path-scoped — see [events](/docs/reference/api#events). + +## Step 5 — Branch on the error envelope + +Force a failure to see the shape every error uses: + +```bash +curl --unix-socket "$SOCK" http://localhost/api/v1/sessions/does-not-exist +``` + +**Expected output:** + +```json +{ + "error": { + "code": "session_not_found", + "message": "Session was not found.", + "details": { "sessionId": "does-not-exist" } + } +} +``` + +Branch on `error.code`, never on message text. The stable codes (`not_found`, `invalid_request`, `session_not_found`, `session_not_live`, `project_root_violation`, `pty_spawn_failed`, `runtime_unavailable`, `internal_error`) and their HTTP statuses are tabulated in the [error envelope reference](/docs/reference/api#error-envelope). A robust script treats `runtime_unavailable` as retryable (a `503`: check `coven daemon status`, then retry), and `session_not_live` as "switch to replay, do not send input." + +## Troubleshooting + +- **`curl` cannot connect** — daemon down or wrong socket path for your `COVEN_HOME`; see [Set up the daemon](/docs/guides/set-up-the-daemon). +- **`invalid_request` on launch** — compare your body against Step 3's fields; an unknown harness id lands here too. +- **`project_root_violation`** — your `cwd` escapes `projectRoot`; see [`cwd` rejected](/docs/reference/troubleshooting#cwd-rejected). +- **Events stop arriving** — the session likely completed; check its `status` via `GET /api/v1/sessions/`. +- **Version mismatch after an upgrade** — see [API version rejected](/docs/reference/troubleshooting#api-version-rejected) and [Upgrade Coven](/docs/guides/upgrade-coven). + +## Related + +- [API reference](/docs/reference/api) +- [API architecture diagrams](/docs/reference/api-architecture) +- [OpenAPI endpoint pages](/docs/openapi) +- [Socket API overview](/docs/daemon/socket-api) diff --git a/scripts/check-guides-docs.mjs b/scripts/check-guides-docs.mjs index 5e1b80d..4b59338 100644 --- a/scripts/check-guides-docs.mjs +++ b/scripts/check-guides-docs.mjs @@ -13,6 +13,7 @@ const requiredPages = [ 'set-up-memory', 'fix-a-failed-install', 'upgrade-coven', + 'script-the-api', ]; const requiredMentions = [ @@ -53,6 +54,11 @@ const requiredMentions = [ 'coven --version', '/api/v1/capabilities', 'coven sessions --all --plain', + '/api/v1/sessions', + '/api/v1/events', + 'nextCursor', + 'error.code', + 'session_not_found', ]; function fail(message) { @@ -148,4 +154,8 @@ if (!readFileSync(join(guidesRoot, 'upgrade-coven.mdx'), 'utf8').includes('/docs fail('upgrade-coven guide must link to the recovery and upgrades reference.'); } +if (!readFileSync(join(guidesRoot, 'script-the-api.mdx'), 'utf8').includes('/docs/reference/api')) { + fail('script-the-api guide must link to the API reference.'); +} + console.log('Guides docs check passed.');