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