mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
feat(api): establish stable HTTP contract — uniform {success,data} envelope, status codes, /api/v1
Point 1 of the v1.0 lock-in: commit to a stable HTTP API (the cleanest, fullest form).
Core (centralized):
- Every JSON /api response now uses ONE envelope via a Fastify preSerialization hook (src/web/server.ts): success -> { success:true, data:<payload> }; error -> { success:false, error, errorCode } with a conventional HTTP status. Non-JSON routes (file-raw, tail-file SSE, download, screenshots, /q redirect, WS) are skipped.
- Error-code -> HTTP status is a single source of truth (httpStatusForErrorCode in src/types/api.ts): 400/401/404/409/422/429/500. Expanded ApiErrorCode (added UNAUTHORIZED, CONFLICT, RATE_LIMITED). Errors are no longer HTTP 200.
- Versioned alias: /api/v1/* rewrites to /api/* (rewriteApiV1Url), so external clients pin to a stable surface while the bundled UI keeps using /api/*.
- Handlers stripped of manual 'success:true' (50 across 14 route files) so they return bare payloads the hook wraps uniformly; fixed the mux DELETE {success:<bool>} envelope collision (-> {killed}).
Frontend (48 call sites across 10 files):
- _apiJson() auto-unwraps { success:true, data } -> data (null on error), so most bare-shape readers are transparent. Raw-fetch sites relocate payload reads under .data; success/res.ok/error checks unchanged.
Docs: new docs/api-reference.md (envelope, status table, error codes, /api/v1, SSE); versioning-policy.md flipped — the HTTP/SSE API is now part of the stable, SemVer-covered surface.
Verification: full unit/route suite green (2680 passed) incl. ~166 updated assertions across 24 test files; typecheck/lint/format/frontend-syntax clean; a headless-chromium smoke loaded the migrated UI and drove the panels with 0 console/page errors; /api/status and /api/v1/status confirmed returning the uniform envelope live.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
# HTTP API Reference
|
||||
|
||||
Codeman's HTTP API is a **stable contract** as of 1.0 — see
|
||||
[`versioning-policy.md`](versioning-policy.md) for the SemVer guarantee. This page
|
||||
defines the response envelope, status codes, error codes, versioning, and the SSE
|
||||
event channel.
|
||||
|
||||
## Versioning
|
||||
|
||||
- The stable, public surface is served under **`/api/v1/...`**. Pin external
|
||||
clients to this prefix.
|
||||
- The unversioned **`/api/...`** paths are a permanent alias of the current
|
||||
version (what the bundled web UI uses). They are kept working, but new external
|
||||
integrations should use `/api/v1`.
|
||||
- Breaking changes to the contract ship under a new prefix (`/api/v2`); `/api/v1`
|
||||
keeps its semantics. Additive changes (new endpoints, new optional fields, new
|
||||
error codes) are non-breaking and may appear in a minor release.
|
||||
- The implementation rewrites `/api/v1/*` → `/api/*` at the server level
|
||||
(`rewriteApiV1Url` in `src/web/server.ts`).
|
||||
|
||||
## Response envelope
|
||||
|
||||
Every JSON response uses one uniform envelope, applied centrally by a
|
||||
`preSerialization` hook (`src/web/server.ts`) — handlers return bare data and the
|
||||
hook wraps it:
|
||||
|
||||
**Success** — HTTP `2xx`:
|
||||
|
||||
```json
|
||||
{ "success": true, "data": <payload> }
|
||||
```
|
||||
|
||||
`data` is the endpoint's payload (object, array, or value). Endpoints with no
|
||||
payload return `{ "success": true, "data": {} }`.
|
||||
|
||||
**Error** — HTTP `4xx`/`5xx`:
|
||||
|
||||
```json
|
||||
{ "success": false, "error": "human-readable message", "errorCode": "NOT_FOUND" }
|
||||
```
|
||||
|
||||
`ApiResponse<T>` in `src/types/api.ts` is the canonical type.
|
||||
|
||||
> Non-JSON endpoints are exempt from the envelope: `GET /api/sessions/:id/file-raw`,
|
||||
> `GET /api/sessions/:id/tail-file` (SSE), `GET /api/download`,
|
||||
> `GET /api/screenshots/:name`, `GET /q/:code` (QR redirect), and the
|
||||
> `GET /ws/sessions/:id/terminal` WebSocket upgrade.
|
||||
|
||||
## Error codes → HTTP status
|
||||
|
||||
The single source of truth is `ErrorStatus` / `httpStatusForErrorCode()` in
|
||||
`src/types/api.ts`. Clients should branch on `errorCode` (stable) and may rely on
|
||||
the HTTP status.
|
||||
|
||||
| `errorCode` | HTTP | Meaning |
|
||||
|-------------|------|---------|
|
||||
| `INVALID_INPUT` | 400 | Malformed request / failed validation |
|
||||
| `UNAUTHORIZED` | 401 | Authentication required or failed |
|
||||
| `NOT_FOUND` | 404 | Resource does not exist |
|
||||
| `SESSION_BUSY` | 409 | Session is busy |
|
||||
| `CONFLICT` | 409 | Conflicts with current state (e.g. already running) |
|
||||
| `ALREADY_EXISTS` | 409 | Resource already exists |
|
||||
| `OPERATION_FAILED` | 422 | Well-formed but could not be completed |
|
||||
| `RATE_LIMITED` | 429 | Too many requests |
|
||||
| `INTERNAL_ERROR` | 500 | Unexpected server error |
|
||||
|
||||
Adding a new error code is non-breaking; removing or renaming one is a major change.
|
||||
|
||||
## Authentication
|
||||
|
||||
Optional HTTP Basic (`CODEMAN_USERNAME`/`CODEMAN_PASSWORD`) → opaque
|
||||
`codeman_session` cookie. When enabled, unauthenticated requests get
|
||||
`401 UNAUTHORIZED`; rate-limited requests get `429 RATE_LIMITED`. See
|
||||
[`security-architecture.md`](security-architecture.md).
|
||||
|
||||
## SSE event channel
|
||||
|
||||
`GET /api/events` is a Server-Sent Events stream (`text/event-stream`); each
|
||||
message is `event: <name>` + `data: <json>`. The event-name registry
|
||||
(`src/web/sse-events.ts`, mirrored in `src/web/public/constants.js`) is part of
|
||||
the stable contract — event names are not renamed without a major bump. An
|
||||
optional `?sessions=<id,...>` filter suppresses only the high-volume terminal
|
||||
stream; lifecycle/metadata events are delivered to all clients regardless.
|
||||
|
||||
## Consuming from JavaScript
|
||||
|
||||
The bundled frontend reads responses through `_apiJson()`
|
||||
(`src/web/public/api-client.js`), which unwraps `{success:true,data}` → `data` and
|
||||
returns `null` on a non-2xx / `{success:false}` response. External clients should
|
||||
do the same: check the HTTP status (or `body.success`), then read `body.data`.
|
||||
+18
-17
@@ -8,9 +8,9 @@ surfaces are covered by SemVer and which are explicitly not. It exists because
|
||||
"1.0" is a commitment to stability, and an undocumented public surface invites
|
||||
incompatible client assumptions we would then be pressured to keep.
|
||||
|
||||
> **Status:** draft for the 1.0 cut. The central decision below — that the HTTP/SSE
|
||||
> API is *not* SemVer-covered — should be confirmed by the maintainer before 1.0,
|
||||
> since it determines whether a number of in-flight cleanups are "breaking."
|
||||
> **Status:** finalized for the 1.0 cut. The HTTP/SSE API **is** part of the stable
|
||||
> surface — served under `/api/v1` with a uniform response envelope and
|
||||
> conventional HTTP status codes. See [`api-reference.md`](api-reference.md).
|
||||
|
||||
## What SemVer covers (the public, stable surface)
|
||||
|
||||
@@ -19,9 +19,10 @@ A **MAJOR** bump is required to break any of these after 1.0:
|
||||
1. **The CLI.** Command names, documented flags, and their behavior for
|
||||
`codeman <command>` (published to npm as `aicodeman`; invoked as `codeman`).
|
||||
This is the package's actual public entry point (`bin`).
|
||||
- *Note:* the npm package name vs. invoked command name (`aicodeman` vs
|
||||
`codeman`) is a known inconsistency to resolve **before** 1.0 — renaming
|
||||
either after 1.0 is itself a breaking change.
|
||||
- The package is published to npm as `aicodeman` and installs **both** the
|
||||
`aicodeman` and `codeman` commands (`bin` aliases); `codeman` is the
|
||||
canonical command used throughout the docs. Renaming either after 1.0 is a
|
||||
breaking change.
|
||||
2. **The published `xterm-zerolag-input` library**, but on **its own version
|
||||
line** — it is versioned and released independently of the Codeman app. Its
|
||||
1.0 status is a separate decision; the Codeman app reaching 1.0 does *not*
|
||||
@@ -31,26 +32,26 @@ A **MAJOR** bump is required to break any of these after 1.0:
|
||||
`CODEMAN_INSTANCE`, `CODEMAN_ALLOWED_HOSTS`, `CODEMAN_DATA_DIR`,
|
||||
`CODEMAN_TMUX_SOCKET`, and the `--host` / `--port` / `--https` CLI flags.
|
||||
Removing or changing the meaning of one of these is breaking.
|
||||
4. **The HTTP API and SSE event channel**, served under **`/api/v1`** with the
|
||||
uniform `{success:true,data}` / `{success:false,error,errorCode}` envelope and
|
||||
conventional HTTP status codes. Endpoint paths, the response envelope, error
|
||||
`errorCode` values, and SSE event names are stable — see
|
||||
[`api-reference.md`](api-reference.md). *Additive* changes (new endpoints, new
|
||||
optional fields, new error codes, new SSE events) are non-breaking; breaking
|
||||
changes ship under a new prefix (`/api/v2`). The unversioned `/api/...` alias
|
||||
is kept working for the bundled UI.
|
||||
|
||||
## What SemVer does NOT cover (internal surfaces — may change in any release)
|
||||
|
||||
These may change in a **MINOR** (or even PATCH) release without a MAJOR bump:
|
||||
|
||||
1. **The HTTP API and SSE event registry** (`/api/...`, the ~120 SSE event
|
||||
types). This is an **internal protocol between the server and its own bundled
|
||||
web frontend**, not a published client API. There is no OpenAPI spec, no
|
||||
published client, and no versioned `/api/v1` namespace. If you script against
|
||||
these endpoints, **pin to an exact Codeman version** — they can change between
|
||||
minors (response shapes, status codes, event names). Standardizing the error
|
||||
envelope and HTTP status codes is explicitly reserved as a non-breaking
|
||||
internal change under this policy.
|
||||
2. **The `~/.codeman/` state file formats** (`state.json`, `settings.json`,
|
||||
1. **The `~/.codeman/` state file formats** (`state.json`, `settings.json`,
|
||||
`mux-sessions.json`, etc.). We make a **best-effort** to migrate existing data
|
||||
forward (and have done so across renames), but the on-disk schema is not a
|
||||
stable contract — do not write tooling that depends on its exact shape.
|
||||
3. **Internal TypeScript modules.** The npm package is CLI-only; `import`ing it
|
||||
2. **Internal TypeScript modules.** The npm package is CLI-only; `import`ing it
|
||||
programmatically is not supported (there is no stable library entry point).
|
||||
4. **Experimental / opt-in features**, regardless of the app's version:
|
||||
3. **Experimental / opt-in features**, regardless of the app's version:
|
||||
Gesture Control (beta), Agent Teams
|
||||
(`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`), and anything labeled experimental
|
||||
in the UI or docs. These may change or be removed at any time.
|
||||
|
||||
Reference in New Issue
Block a user