fix(cli-tools): send the API key Codex actually reads

Codex only authenticates a custom model provider from env_key,
http_headers, env_http_headers or a token command — auth.json is
read solely by the built-in openai provider. Writing OPENAI_API_KEY
there left every request unauthenticated (401 Missing API key) while
clobbering an existing ChatGPT login.

Put the key in [model_providers.9router.http_headers] instead, and
drop the auth.json write. Also move the subagent model to the
agents.default_subagent_model scalar: agents.<role> now declares a
custom role and requires a description, so the old [agents.subagent]
table was discarded with a startup warning. DELETE still clears
auth.json to repair machines configured by the previous version.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
decoluaandClaude Fable 5 committed 2026-08-27 18:52:32 +07:00
1 parent e6f5724b4b
commit 9c45b27cd7
2 files changed
+14 -32

No files matched your search

+7 -19
View File
@@ -135,35 +135,22 @@ export async function POST(request) {
// Update or create 9router provider section (no api_key - Codex reads from auth.json)
// Ensure /v1 suffix is added only once
const normalizedBaseUrl = baseUrl.endsWith("/v1") ? baseUrl : `${baseUrl}/v1`;
// Custom providers ignore auth.json - the key must travel as a static header
setNestedSection(parsed, "model_providers.9router", {
name: "9Router",
base_url: normalizedBaseUrl,
wire_api: "responses",
http_headers: { Authorization: `Bearer ${apiKey}` },
});
// Add subagent configuration
const effectiveSubagentModel = subagentModel || model;
setNestedSection(parsed, "agents.subagent", {
model: effectiveSubagentModel,
});
// Subagent model is a scalar under [agents]; agents.<role> now means a custom role
deleteNestedSection(parsed, "agents.subagent");
setNestedSection(parsed, "agents.default_subagent_model", subagentModel || model);
// Write merged config
const configContent = stringifyTOML(parsed);
await fs.writeFile(configPath, configContent);
// Update auth.json with OPENAI_API_KEY (Codex reads this first)
const authPath = getCodexAuthPath();
let authData = {};
try {
const existingAuth = await fs.readFile(authPath, "utf-8");
authData = JSON.parse(existingAuth);
} catch { /* No existing auth */ }
// Force apikey mode (keep existing tokens untouched for ChatGPT login reuse)
authData.OPENAI_API_KEY = apiKey;
authData.auth_mode = "apikey";
await fs.writeFile(authPath, JSON.stringify(authData, null, 2));
return NextResponse.json({
success: true,
message: "Codex settings applied successfully!",
@@ -204,7 +191,8 @@ export async function DELETE() {
// Remove 9router provider section
deleteNestedSection(parsed, "model_providers.9router");
// Remove subagent configuration
// Remove subagent configuration (both the current key and the legacy role form)
deleteNestedSection(parsed, "agents.default_subagent_model");
deleteNestedSection(parsed, "agents.subagent");
// Write updated config