fix(codex): add array coercion and typed arguments for CLI-backed routes

- Coerce mcp/plugins/marketplaces GET responses to arrays (CLI may emit
  object), degrading to empty list rather than crash on unexpected shape
- Add typed interfaces: CodexMcpAddArgs, CodexPluginActionArgs,
  CodexMarketplaceActionArgs with validated action literals
- Mirrors ccConfig's type safety across the namespace
This commit is contained in:
2026-09-08 20:27:52 +07:00
parent 2ae9b06652
commit 8021e569f2
+43 -9
View File
@@ -1700,25 +1700,37 @@ export const api = {
requestCodexBackupsHelper(params),
/**
* GET /api/codex-config/mcp - configured MCP servers from the codex CLI.
* Coerces the CLI's JSON output (which may be an object) to an array.
* @returns `{ mcp }` — {@link CodexMcpServer} items.
*/
mcp: () => request<{ mcp: CodexMcpServer[] }>("/codex-config/mcp"),
mcp: async () => {
const res = await request<{ mcp: unknown }>("/codex-config/mcp");
return { mcp: Array.isArray(res.mcp) ? res.mcp : [] };
},
/**
* GET /api/codex-config/plugins - installed plugins from the codex CLI.
* Coerces the CLI's JSON output (which may be an object) to an array.
* @returns `{ plugins }` — {@link CodexPlugin} items.
*/
plugins: () => request<{ plugins: CodexPlugin[] }>("/codex-config/plugins"),
plugins: async () => {
const res = await request<{ plugins: unknown }>("/codex-config/plugins");
return { plugins: Array.isArray(res.plugins) ? res.plugins : [] };
},
/**
* GET /api/codex-config/marketplaces - registered marketplaces from the codex CLI.
* Coerces the CLI's JSON output (which may be an object) to an array.
* @returns `{ marketplaces }` — {@link CodexMarketplace} items.
*/
marketplaces: () => request<{ marketplaces: CodexMarketplace[] }>("/codex-config/marketplaces"),
marketplaces: async () => {
const res = await request<{ marketplaces: unknown }>("/codex-config/marketplaces");
return { marketplaces: Array.isArray(res.marketplaces) ? res.marketplaces : [] };
},
/**
* POST /api/codex-config/mcp/add - add an MCP server via the `codex` CLI.
* @param args Arguments for `codex mcp add`.
* @param args {@link CodexMcpAddArgs} for `codex mcp add`.
* @returns `{ ok, stdout, stderr }` — success and CLI output.
*/
mcpAdd: (args: unknown) =>
mcpAdd: (args: CodexMcpAddArgs) =>
request<{ ok: true; stdout: string; stderr: string }>("/codex-config/mcp/add", {
method: "POST",
body: JSON.stringify(args),
@@ -1735,10 +1747,10 @@ export const api = {
}),
/**
* POST /api/codex-config/plugins/action - add or remove a plugin via the `codex` CLI.
* @param args Arguments with `spec` and `action` fields.
* @param args {@link CodexPluginActionArgs} with `spec` and `action` fields.
* @returns `{ ok, stdout, stderr }` — success and CLI output.
*/
pluginAction: (args: unknown) =>
pluginAction: (args: CodexPluginActionArgs) =>
request<{ ok: true; stdout: string; stderr: string }>("/codex-config/plugins/action", {
method: "POST",
body: JSON.stringify(args),
@@ -1755,10 +1767,10 @@ export const api = {
}),
/**
* POST /api/codex-config/marketplaces/action - update or remove a marketplace via the `codex` CLI.
* @param args Arguments with `name` and `action` fields.
* @param args {@link CodexMarketplaceActionArgs} with `name` and `action` fields.
* @returns `{ ok, stdout, stderr }` — success and CLI output.
*/
marketplaceAction: (args: unknown) =>
marketplaceAction: (args: CodexMarketplaceActionArgs) =>
request<{ ok: true; stdout: string; stderr: string }>("/codex-config/marketplaces/action", {
method: "POST",
body: JSON.stringify(args),
@@ -2994,6 +3006,28 @@ export interface CodexMarketplace {
[key: string]: unknown;
}
/** Arguments for POST /api/codex-config/mcp/add. */
export interface CodexMcpAddArgs {
name: string;
url?: string;
bearerTokenEnvVar?: string;
command?: string;
args?: string[];
env?: Record<string, string>;
}
/** Arguments for POST /api/codex-config/plugins/action. */
export interface CodexPluginActionArgs {
spec: string;
action: "add" | "remove";
}
/** Arguments for POST /api/codex-config/marketplaces/action. */
export interface CodexMarketplaceActionArgs {
name: string;
action: "remove";
}
// ─────────────────────────────────────────────────────────────────────────────
// Run types — request/response shapes for the Run page's `claude` process
// spawning/management. `RunMode`/`RunStatus`/`PermissionMode`/`EffortLevel`