feat(codex): create and customize Codex config from /settings

Adds the Codex config panel (skills/agents/rules/memory/config editing,
mcp/plugins/marketplaces add-remove via the codex CLI) as the last piece
of parity with the Claude Code settings tab, plus the codex_config_changed
WebSocket type the client was missing, and docs for the new
/api/codex-config surface.
This commit is contained in:
2026-09-08 21:09:28 +07:00
parent f6973d24d6
commit 8015730adf
9 changed files with 1856 additions and 6 deletions
+31
View File
@@ -1619,6 +1619,37 @@ The dashboard **cannot log an account in** — only `claude auth login` mints to
Backup paths look like `<root>/cc-config-backups/<type>/<base>.<ISO>.bak[.dir]` — outside the directories Claude Code scans, so a deleted skill cannot resurface as a backup-named one. The Backups modal in the UI auto-builds `mv` restore commands.
### Codex Config Explorer
The `/api/codex-config/*` namespace powers the **Codex** tab of Settings (`/settings?tool=codex`) — the Codex CLI counterpart of the Claude Config Explorer above. Codex has a narrower scope union than Claude Code: `"user"` or `"project"` only, no `"project-local"` and no per-project file-based memory. Reads for skills/agents/rules/memory/config are pure file reads (`server/lib/codex-discovery.js`); MCP servers, plugins, and marketplaces have no on-disk reader and are read *and* mutated entirely through the `codex` CLI (`server/lib/codex-cli.js`), because the CLI owns `config.toml`'s schema.
```http
GET /api/codex-config/overview
GET /api/codex-config/skills?scope=user|project|all
GET /api/codex-config/agents?scope=user|project|all
GET /api/codex-config/rules?scope=user|project|all
GET /api/codex-config/memory
GET /api/codex-config/config
GET /api/codex-config/file?path=<absolute-path>
PUT /api/codex-config/file Body: { scope, type, name?, content }
DELETE /api/codex-config/file Body: { scope, type, name? }
GET /api/codex-config/backups[?scope=&type=]
GET /api/codex-config/mcp
GET /api/codex-config/plugins
GET /api/codex-config/marketplaces
POST /api/codex-config/mcp/add Body: { name, url, bearerTokenEnvVar? } | { name, command, args?, env? }
POST /api/codex-config/mcp/remove Body: { name }
POST /api/codex-config/plugins/action Body: { spec, action: add|remove }
POST /api/codex-config/marketplaces/add Body: { url }
POST /api/codex-config/marketplaces/action Body: { name, action: remove }
```
`type` is one of `skills`, `agents`, `rules`, `memory`, `config`. `name` is required for `skills`/`agents`/`rules` and forbidden for `memory`/`config` (each is a single fixed file per scope — `AGENTS.md` and `config.toml` respectively). `PUT`/`DELETE` always back the previous content up first (`server/lib/codex-mutate.js`) and always resolve `project` scope against the **dashboard server's own cwd** — unlike the read routes, there is no `?cwd=` override on a write, for the same reason `cc-config.js`'s `PATCH /settings` refuses one. Mutation errors: `EBADTYPE`/`EBADSCOPE`/`EBADNAME`/`EBADCONTENT`/`EOUTOFROOT` (400), `ETOOLARGE` (413), `ENOTFOUND` (404). Every successful write/delete broadcasts `codex_config_changed`.
`GET /mcp`, `/plugins`, `/marketplaces` proxy `codex mcp list --json` / `codex plugin list --json` / `codex marketplace list --json`; a fresh install with none of a kind returns `{ mcp: [] }` etc. rather than a parse error, and the client further coerces a non-array response to `[]` defensively. The add/remove routes shell out to `codex …` with an explicit argv array (never a shell string); a non-zero CLI exit is reported as **422 `ECLIEXIT`** with `stdout`/`stderr` preserved, never a 500. `POST /mcp/add` takes either `{ name, url, bearerTokenEnvVar? }` (HTTP transport) or `{ name, command, args?, env? }` (stdio transport) — never both, mirroring `codex mcp add`'s own mutually-exclusive flags. Other CLI errors: `EBADARG` (400), `ECLINOTFOUND` (503 — the `codex` binary isn't on `PATH`), `ETIMEOUT` (504).
Because `CodexMcpServer`/`CodexPlugin`/`CodexMarketplace` are opaque JSON blobs from the CLI (`[key: string]: unknown`) rather than a shape the dashboard controls, the Codex tab renders them as generic key/value cards instead of the richly-typed plugin/MCP cards the Claude Code tab shows.
### Run Claude via tmux+PTY
The `/api/run/*` namespace starts and manages `claude` subprocesses in tmux-backed pseudoterminals, one session per lane. The Workspace page renders an interactive xterm.js terminal attached to the session over `/ws-pty/:runId` (binary PTY frames, not JSON). Every route enforces a same-origin / loopback-Origin guard; browser requests must come from `localhost`, `127.0.0.1`, `::1`, or `0.0.0.0`. CLI / curl requests with no `Origin` header pass through. When `DASHBOARD_TOKEN` is set, a valid token is also required here (like the rest of `/api/*` — see [Authentication](#authentication)). **`tmux` must be installed on the dashboard server** (same operational requirement as better-sqlite3's native module).