feat(lanes): badge codex lanes and document the lane tool

This commit is contained in:
2026-08-27 10:54:16 +07:00
parent 0d53603d9e
commit 24de68cfe1
5 changed files with 82 additions and 11 deletions
+26 -7
View File
@@ -135,7 +135,7 @@ POST /api/lanes/ensure
```
```json
{ "cwd": "/absolute/path/to/work", "title": "App package", "pipeline": "ship-feature" }
{ "cwd": "/absolute/path/to/work", "title": "App package", "pipeline": "ship-feature", "tool": "codex" }
```
`cwd` is optional — when missing or empty the server falls back to
@@ -147,6 +147,11 @@ optional and, like `title`, applies **only when a lane is actually created**
`POST /api/lanes/worktree` and `POST /api/lanes` take the same optional
field. An id no template defines returns `400 EBADPIPELINE`.
`tool` (`"claude"` or `"codex"`, default `"claude"`) is the CLI this lane's
runs will spawn. Immutable once the lane is created; an attempt to update it on
an existing lane returns `400 EBADTOOL`. The tool choice appears as an immutable
badge on the lane card (Codex lanes only).
Idempotent lookup by working directory — the Workspace page opens on a `cwd`,
not on a lane id. Returns the lane whose own `cwd` is that path, or the longest
path-boundary parent of it, as `{ "lane": {...}, "created": false }` with `200`.
@@ -194,10 +199,15 @@ existing `lane_update` message when the lane becomes `idle` or `failed`.
{
"sourceRepo": "/absolute/path/to/repository",
"title": "Criteria form",
"base": "main"
"base": "main",
"tool": "claude"
}
```
`tool` (`"claude"` or `"codex"`, default `"claude"`) is the CLI this lane's
runs will spawn. Immutable once the lane is created; an attempt to update it on
an existing lane returns `400 EBADTOOL`.
`sourceRepo` must exist, be absolute, and be a git repository. When `base` is
omitted, it defaults to the `LANE_BASE_BRANCH` env var, or `main` when that is
also unset. The feature branch is named `<LANE_BRANCH_PREFIX><slug>` (prefix
@@ -1571,23 +1581,32 @@ The `/api/run/*` namespace starts and manages `claude` subprocesses in tmux-back
```http
GET /api/run List all live runs (computed fresh from tmux state)
GET /api/run/tmux { available: bool } — whether tmux is on PATH
GET /api/run/binary { found, path } for the `claude` binary
GET /api/run/binary?tool= { found, path } for the `claude` or `codex` binary
GET /api/run/cwds Suggested cwds (dashboard, home, recent)
GET /api/run/history?limit=&laneId= Persisted run history; laneId narrows to one lane's runs
GET /api/run/files?cwd=&q= Fuzzy file search inside cwd for the @-file autocomplete
(skips node_modules, .git, dist, build, .next, .cache, coverage, vendor)
POST /api/run Start/attach run — Body: { laneId, initialPrompt?, ... }
POST /api/run Start/attach run — Body: { laneId, initialPrompt?, sandbox?, approval?, resumeLast?, ... }
GET /api/run/:id Run handle (returns live run state)
DELETE /api/run/:id Kill (SIGTERM → SIGKILL after 5 s)
```
**`POST /api/run` (start/attach):** Requires `laneId` (the lane this run belongs to). Creates or attaches an existing tmux session named `ccam-lane-<id>` in the lane's working directory. If that session already exists and its pane is idling at a shell prompt, the `claude` command line (including `--resume` and any initial prompt) is typed into that pane instead of being dropped; if the pane is running a program, the request adopts the session unchanged. Optionally accepts `initialPrompt` to immediately type/send into the session (if empty or omitted, the session is created/attached with no initial input). Returns `{ id, laneId, status, cwd, model, permissionMode, effort, resumeSessionId, sessionId, startedAt, promptPreview }` where `id` is the tmux session name. The dashboard self-heals a lane's `run_id`/`status` on every read if the tmux session has been killed externally.
**`GET /api/run/binary`:** Probes for the binary (`claude` or `codex`). Optional query parameter `?tool=codex` probes for Codex; omitted or `?tool=claude` probes for Claude. Returns `{ found: bool, path: string | null }`.
`permissionMode` (`acceptEdits` default, or `default` / `plan` / `bypassPermissions`) is the mode the pane starts in. The pane's argv also carries `--allow-dangerously-skip-permissions`, which only makes **bypass permissions** selectable in the pane's `shift+tab` cycle — it does not enable it. See [docs/LANES.md § start](LANES.md).
**`POST /api/run` (start/attach):** Requires `laneId` (the lane this run belongs to). Creates or attaches an existing tmux session named `ccam-lane-<id>` in the lane's working directory. If that session already exists and its pane is idling at a shell prompt, the appropriate command line (including `--resume` and any initial prompt) is typed into that pane instead of being dropped; if the pane is running a program, the request adopts the session unchanged. Optionally accepts `initialPrompt` to immediately type/send into the session (if empty or omitted, the session is created/attached with no initial input). Returns `{ id, laneId, status, cwd, model, permissionMode, effort, resumeSessionId, sessionId, startedAt, promptPreview }` where `id` is the tmux session name. The dashboard self-heals a lane's `run_id`/`status` on every read if the tmux session has been killed externally.
For **Claude lanes**, `permissionMode` (`acceptEdits` default, or `default` / `plan` / `bypassPermissions`) is the mode the pane starts in. The pane's argv also carries `--allow-dangerously-skip-permissions`, which only makes **bypass permissions** selectable in the pane's `shift+tab` cycle — it does not enable it.
For **Codex lanes**, three additional body fields control the spawn behavior:
- **`sandbox`** (boolean, default `false`) — whether to enable Codex's restricted filesystem/network mode (`-s` flag)
- **`approval`** (boolean, default `false`) — whether to prompt for confirmation before tool execution (`-a` flag)
- **`resumeLast`** (boolean, default `false`) — whether to resume the last Codex run (`--last` flag instead of starting new)
See [docs/LANES.md § Lane tool](LANES.md#lane-tool-claude-or-codex) for full details.
**PTY streaming:** Frames from the tmux pane are streamed to the client over `/ws-pty/:runId` as binary WebSocket frames (not JSON). The Workspace page's TerminalView component feeds these frames to xterm.js for live rendering. Simultaneously, `ccam lanes shell` can attach the same session via a real local terminal, staying in sync with the browser view.
Every history row carries `lane_id`: the lane whose run it belongs to. `GET /api/run/history?laneId=<n>` returns only that lane's runs. Spawned `claude` processes fire the dashboard's hooks like any other CLI session, so they show up in `/api/sessions`, the Dashboard's Analytics tab, and the Workflows page automatically.
Every history row carries `lane_id` (the lane whose run it belongs to) and `tool` (the CLI that was invoked: `"claude"` or `"codex"`). `GET /api/run/history?laneId=<n>` returns only that lane's runs. Spawned Claude processes fire the dashboard's hooks like any other CLI session, so they show up in `/api/sessions`, the Dashboard's Analytics tab, and the Workflows page automatically. Codex runs do not emit these hooks; they are recorded only in run history and `dashboard_runs`.
---