From 598454c931267082ba80c4ef426d0ef2c0370f55 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Fri, 19 Dec 2025 09:44:14 -0500 Subject: [PATCH] fix(cliproxy): include BASE_URL and AUTH_TOKEN when applying presets Presets now always include ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN when applied to provider settings. This ensures CLIProxy routing works correctly for Gemini, Codex, and Agy providers. Also fixes settings save failing for new profiles by changing PUT /api/settings/:profile to upsert behavior (create if not exists). --- src/web-server/routes.ts | 45 +++++++++++-------- .../components/cliproxy/provider-editor.tsx | 9 ++++ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/web-server/routes.ts b/src/web-server/routes.ts index 5bc1bcea..a7cf9175 100644 --- a/src/web-server/routes.ts +++ b/src/web-server/routes.ts @@ -781,6 +781,7 @@ apiRoutes.get('/settings/:profile/raw', (req: Request, res: Response): void => { /** * PUT /api/settings/:profile - Update settings with conflict detection and backup + * Creates the settings file if it doesn't exist (upsert behavior) */ apiRoutes.put('/settings/:profile', (req: Request, res: Response): void => { const { profile } = req.params; @@ -788,29 +789,36 @@ apiRoutes.put('/settings/:profile', (req: Request, res: Response): void => { const ccsDir = getCcsDir(); const settingsPath = path.join(ccsDir, `${profile}.settings.json`); - if (!fs.existsSync(settingsPath)) { - res.status(404).json({ error: 'Settings not found' }); - return; + const fileExists = fs.existsSync(settingsPath); + + // Only check conflict if file exists and expectedMtime was provided + if (fileExists && expectedMtime) { + const stat = fs.statSync(settingsPath); + if (stat.mtime.getTime() !== expectedMtime) { + res.status(409).json({ + error: 'File modified externally', + currentMtime: stat.mtime.getTime(), + }); + return; + } } - // Conflict detection - const stat = fs.statSync(settingsPath); - if (expectedMtime && stat.mtime.getTime() !== expectedMtime) { - res.status(409).json({ - error: 'File modified externally', - currentMtime: stat.mtime.getTime(), - }); - return; + // Create backup only if file exists + let backupPath: string | undefined; + if (fileExists) { + const backupDir = path.join(ccsDir, 'backups'); + if (!fs.existsSync(backupDir)) { + fs.mkdirSync(backupDir, { recursive: true }); + } + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + backupPath = path.join(backupDir, `${profile}.${timestamp}.settings.json`); + fs.copyFileSync(settingsPath, backupPath); } - // Create backup - const backupDir = path.join(ccsDir, 'backups'); - if (!fs.existsSync(backupDir)) { - fs.mkdirSync(backupDir, { recursive: true }); + // Ensure directory exists for new files + if (!fileExists) { + fs.mkdirSync(path.dirname(settingsPath), { recursive: true }); } - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - const backupPath = path.join(backupDir, `${profile}.${timestamp}.settings.json`); - fs.copyFileSync(settingsPath, backupPath); // Write new settings atomically const tempPath = settingsPath + '.tmp'; @@ -822,6 +830,7 @@ apiRoutes.put('/settings/:profile', (req: Request, res: Response): void => { profile, mtime: newStat.mtime.getTime(), backupPath, + created: !fileExists, }); }); diff --git a/ui/src/components/cliproxy/provider-editor.tsx b/ui/src/components/cliproxy/provider-editor.tsx index 0fd9632e..a1c637e8 100644 --- a/ui/src/components/cliproxy/provider-editor.tsx +++ b/ui/src/components/cliproxy/provider-editor.tsx @@ -296,7 +296,10 @@ export function ProviderEditor({ sonnet: model.id, haiku: model.id, }; + // Always include BASE_URL and AUTH_TOKEN for CLIProxy providers updateEnvValues({ + ANTHROPIC_BASE_URL: `http://127.0.0.1:8317/api/provider/${provider}`, + ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed', ANTHROPIC_MODEL: mapping.default, ANTHROPIC_DEFAULT_OPUS_MODEL: mapping.opus, ANTHROPIC_DEFAULT_SONNET_MODEL: mapping.sonnet, @@ -318,7 +321,10 @@ export function ProviderEditor({ size="sm" className="text-xs h-7 gap-1 pr-6" onClick={() => { + // Always include BASE_URL and AUTH_TOKEN for CLIProxy providers updateEnvValues({ + ANTHROPIC_BASE_URL: `http://127.0.0.1:8317/api/provider/${provider}`, + ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed', ANTHROPIC_MODEL: preset.default, ANTHROPIC_DEFAULT_OPUS_MODEL: preset.opus, ANTHROPIC_DEFAULT_SONNET_MODEL: preset.sonnet, @@ -655,7 +661,10 @@ export function ProviderEditor({ haiku: haikuModel || '', }} onApply={(values, presetName) => { + // Always include BASE_URL and AUTH_TOKEN for CLIProxy providers updateEnvValues({ + ANTHROPIC_BASE_URL: `http://127.0.0.1:8317/api/provider/${provider}`, + ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed', ANTHROPIC_MODEL: values.default, ANTHROPIC_DEFAULT_OPUS_MODEL: values.opus, ANTHROPIC_DEFAULT_SONNET_MODEL: values.sonnet,