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).
This commit is contained in:
kaitranntt committed 2025-12-19 09:44:14 -05:00
1 parent 68eab562ce
commit 598454c931
2 files changed
+36 -18

No files matched your search

+27 -18
View File
@@ -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 * 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 => { apiRoutes.put('/settings/:profile', (req: Request, res: Response): void => {
const { profile } = req.params; const { profile } = req.params;
@@ -788,29 +789,36 @@ apiRoutes.put('/settings/:profile', (req: Request, res: Response): void => {
const ccsDir = getCcsDir(); const ccsDir = getCcsDir();
const settingsPath = path.join(ccsDir, `${profile}.settings.json`); const settingsPath = path.join(ccsDir, `${profile}.settings.json`);
if (!fs.existsSync(settingsPath)) { const fileExists = fs.existsSync(settingsPath);
res.status(404).json({ error: 'Settings not found' });
return; // 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 // Create backup only if file exists
const stat = fs.statSync(settingsPath); let backupPath: string | undefined;
if (expectedMtime && stat.mtime.getTime() !== expectedMtime) { if (fileExists) {
res.status(409).json({ const backupDir = path.join(ccsDir, 'backups');
error: 'File modified externally', if (!fs.existsSync(backupDir)) {
currentMtime: stat.mtime.getTime(), fs.mkdirSync(backupDir, { recursive: true });
}); }
return; const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
backupPath = path.join(backupDir, `${profile}.${timestamp}.settings.json`);
fs.copyFileSync(settingsPath, backupPath);
} }
// Create backup // Ensure directory exists for new files
const backupDir = path.join(ccsDir, 'backups'); if (!fileExists) {
if (!fs.existsSync(backupDir)) { fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
fs.mkdirSync(backupDir, { 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 // Write new settings atomically
const tempPath = settingsPath + '.tmp'; const tempPath = settingsPath + '.tmp';
@@ -822,6 +830,7 @@ apiRoutes.put('/settings/:profile', (req: Request, res: Response): void => {
profile, profile,
mtime: newStat.mtime.getTime(), mtime: newStat.mtime.getTime(),
backupPath, backupPath,
created: !fileExists,
}); });
}); });
@@ -296,7 +296,10 @@ export function ProviderEditor({
sonnet: model.id, sonnet: model.id,
haiku: model.id, haiku: model.id,
}; };
// Always include BASE_URL and AUTH_TOKEN for CLIProxy providers
updateEnvValues({ updateEnvValues({
ANTHROPIC_BASE_URL: `http://127.0.0.1:8317/api/provider/${provider}`,
ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed',
ANTHROPIC_MODEL: mapping.default, ANTHROPIC_MODEL: mapping.default,
ANTHROPIC_DEFAULT_OPUS_MODEL: mapping.opus, ANTHROPIC_DEFAULT_OPUS_MODEL: mapping.opus,
ANTHROPIC_DEFAULT_SONNET_MODEL: mapping.sonnet, ANTHROPIC_DEFAULT_SONNET_MODEL: mapping.sonnet,
@@ -318,7 +321,10 @@ export function ProviderEditor({
size="sm" size="sm"
className="text-xs h-7 gap-1 pr-6" className="text-xs h-7 gap-1 pr-6"
onClick={() => { onClick={() => {
// Always include BASE_URL and AUTH_TOKEN for CLIProxy providers
updateEnvValues({ updateEnvValues({
ANTHROPIC_BASE_URL: `http://127.0.0.1:8317/api/provider/${provider}`,
ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed',
ANTHROPIC_MODEL: preset.default, ANTHROPIC_MODEL: preset.default,
ANTHROPIC_DEFAULT_OPUS_MODEL: preset.opus, ANTHROPIC_DEFAULT_OPUS_MODEL: preset.opus,
ANTHROPIC_DEFAULT_SONNET_MODEL: preset.sonnet, ANTHROPIC_DEFAULT_SONNET_MODEL: preset.sonnet,
@@ -655,7 +661,10 @@ export function ProviderEditor({
haiku: haikuModel || '', haiku: haikuModel || '',
}} }}
onApply={(values, presetName) => { onApply={(values, presetName) => {
// Always include BASE_URL and AUTH_TOKEN for CLIProxy providers
updateEnvValues({ updateEnvValues({
ANTHROPIC_BASE_URL: `http://127.0.0.1:8317/api/provider/${provider}`,
ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed',
ANTHROPIC_MODEL: values.default, ANTHROPIC_MODEL: values.default,
ANTHROPIC_DEFAULT_OPUS_MODEL: values.opus, ANTHROPIC_DEFAULT_OPUS_MODEL: values.opus,
ANTHROPIC_DEFAULT_SONNET_MODEL: values.sonnet, ANTHROPIC_DEFAULT_SONNET_MODEL: values.sonnet,