fix(codex-config): close whole-branch review defects in the Codex panel

Plugins/marketplaces GET routes were silently empty because codex-cli.js
never unwrapped the CLI's {installed,available}/{marketplaces} objects
into arrays; backup writes bypassed the symlink-escape guard entirely;
readFileSafe was stricter than every other Codex reader, showing a
symlinked skill/rule in the list and then refusing to open it;
marketplaceAdd only accepted http(s) URLs, rejecting owner/repo sources
the CLI itself takes. Also: docs drift, a dead i18n key, and a scope-cast
hazard in the shared Editor/ConfirmDelete modals.

Fixes, one per finding: unwrap installed/marketplaces in codex-cli.js so
every list route keeps returning a bare list; guard the backup path in
codex-mutate.js with the same assertNotSymlinkEscape the write path
already used (Claude's cc-mutate.js has no such guard on its primary
write path either, so left untouched there); relax readFileSafe's
containment to lexical-only, matching the other four Codex readers,
while leaving the write side's realpath check untouched; reuse
cc-plugin-cli's assertSource for marketplaceAdd instead of a third
validator (ssh remains out of scope, no credential story for it yet).

Server: 1557/1557 (was 1548). Client: tsc -b clean, 469/469 tests.
This commit is contained in:
2026-09-09 01:17:06 +07:00
parent fef3e640d0
commit acff059e92
16 changed files with 359 additions and 108 deletions
+12 -8
View File
@@ -9249,17 +9249,19 @@ paths:
tags:
- CodexConfig
summary: List installed plugins
description: Reads live from `codex plugin list --json`.
description: Reads live from `codex plugin list --json` and unwraps its `{installed, available}` object down to the `installed` array — the route's `plugins` field is always a list.
operationId: codexConfigGetPlugins
responses:
'200':
description: Plugins as reported by the CLI.
description: Installed plugins as reported by the CLI.
content:
application/json:
schema:
type: object
properties:
plugins: {}
plugins:
type: array
items: {}
'422':
description: '`ECLIEXIT` — the `codex` CLI exited non-zero. stdout/stderr are preserved.'
content:
@@ -9312,7 +9314,7 @@ paths:
tags:
- CodexConfig
summary: List registered marketplaces
description: Reads live from `codex plugin marketplace list --json`.
description: Reads live from `codex plugin marketplace list --json` and unwraps its `{marketplaces}` object down to a bare array.
operationId: codexConfigGetMarketplaces
responses:
'200':
@@ -9322,7 +9324,9 @@ paths:
schema:
type: object
properties:
marketplaces: {}
marketplaces:
type: array
items: {}
'422':
description: '`ECLIEXIT` — the `codex` CLI exited non-zero. stdout/stderr are preserved.'
content:
@@ -9582,7 +9586,7 @@ paths:
tags:
- CodexConfig
summary: Register a marketplace
description: Runs `codex plugin marketplace add <url>`.
description: Runs `codex plugin marketplace add <source>`. `source` is an owner/repo shorthand, an http(s) git URL, or an absolute local path — the same set `claude plugin marketplace add` accepts; ssh remotes are not.
operationId: codexConfigPostMarketplacesAdd
requestBody:
required: true
@@ -9596,7 +9600,7 @@ paths:
url:
type: string
example:
url: https://example.com/marketplace.json
url: openai/codex
responses:
'200':
description: Marketplace added.
@@ -9612,7 +9616,7 @@ paths:
example:
error:
code: EBADARG
message: marketplace url must be a valid http(s) URL
message: source must be owner/repo, an http(s) URL, or an absolute path
'422':
description: '`ECLIEXIT` — the `codex` CLI exited non-zero. stdout/stderr are preserved.'
content: