diff --git a/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js b/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js
index 3e706996..f5aa88d7 100644
--- a/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js
+++ b/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js
@@ -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}"
After installation, run codex to verify.
- Codex uses ~/.codex/auth.json with OPENAI_API_KEY.
+ Codex reads custom providers from ~/.codex/config.toml.
Click "Apply" to auto-configure.
diff --git a/src/app/api/cli-tools/codex-settings/route.js b/src/app/api/cli-tools/codex-settings/route.js
index ff20c575..1a6d016f 100644
--- a/src/app/api/cli-tools/codex-settings/route.js
+++ b/src/app/api/cli-tools/codex-settings/route.js
@@ -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. 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