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:
@@ -57,7 +57,7 @@ export default function CodexToolCard({ tool, isExpanded, onToggle, baseUrl, api
|
||||
if (modelMatch) setSelectedModel(modelMatch[1]);
|
||||
|
||||
// Parse subagent settings
|
||||
const subagentModelMatch = codexStatus.config.match(/\[agents\.subagent\]\s*\n\s*model\s*=\s*"([^"]+)"/m);
|
||||
const subagentModelMatch = codexStatus.config.match(/^default_subagent_model\s*=\s*"([^"]+)"/m);
|
||||
if (subagentModelMatch) setSubagentModel(subagentModelMatch[1]);
|
||||
}
|
||||
}, [codexStatus]);
|
||||
@@ -172,24 +172,18 @@ name = "9Router"
|
||||
base_url = "${getEffectiveBaseUrl()}"
|
||||
wire_api = "responses"
|
||||
|
||||
[agents.subagent]
|
||||
model = "${effectiveSubagentModel}"
|
||||
`;
|
||||
[model_providers.9router.http_headers]
|
||||
Authorization = "Bearer ${keyToUse}"
|
||||
|
||||
const authContent = JSON.stringify({
|
||||
auth_mode: "apikey",
|
||||
OPENAI_API_KEY: keyToUse
|
||||
}, null, 2);
|
||||
[agents]
|
||||
default_subagent_model = "${effectiveSubagentModel}"
|
||||
`;
|
||||
|
||||
return [
|
||||
{
|
||||
filename: "~/.codex/config.toml",
|
||||
content: configContent,
|
||||
},
|
||||
{
|
||||
filename: "~/.codex/auth.json",
|
||||
content: authContent,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -254,7 +248,7 @@ model = "${effectiveSubagentModel}"
|
||||
<p className="text-text-muted">After installation, run <code className="px-1 bg-black/5 dark:bg-white/5 rounded">codex</code> to verify.</p>
|
||||
<div className="pt-2 border-t border-border">
|
||||
<p className="text-text-muted text-xs">
|
||||
Codex uses <code className="px-1 bg-black/5 dark:bg-white/5 rounded">~/.codex/auth.json</code> with <code className="px-1 bg-black/5 dark:bg-white/5 rounded">OPENAI_API_KEY</code>.
|
||||
Codex reads custom providers from <code className="px-1 bg-black/5 dark:bg-white/5 rounded">~/.codex/config.toml</code>.
|
||||
Click "Apply" to auto-configure.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user