From ff9dbb30614263b1f0b0751f1277728b636ef236 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 26 Feb 2026 14:06:04 +0700 Subject: [PATCH] fix(cliproxy): normalize deprecated antigravity Claude aliases --- src/cliproxy/config/generator.ts | 29 +++++++++++----- tests/unit/cliproxy/config-generator.test.js | 36 +++++++++++++++++++- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/cliproxy/config/generator.ts b/src/cliproxy/config/generator.ts index 27e649d4..0b88f6f0 100644 --- a/src/cliproxy/config/generator.ts +++ b/src/cliproxy/config/generator.ts @@ -31,8 +31,9 @@ export const CCS_CONTROL_PANEL_SECRET = 'ccs'; * v7: Added fork:true for Claude model aliases (keep both upstream and alias names) * v8: Added Gemini 3.1 preview aliases for provider routing compatibility * v9: Added resilient alias compatibility expansion and cache-assisted alias enrichment + * v10: Migrated deprecated gemini-claude-* aliases to upstream claude-* aliases */ -export const CLIPROXY_CONFIG_VERSION = 9; +export const CLIPROXY_CONFIG_VERSION = 10; interface OAuthModelAliasEntry { name: string; @@ -41,6 +42,8 @@ interface OAuthModelAliasEntry { } const GEMINI_MINOR_COMPAT_RANGE = [1, 2, 3, 4, 5, 6, 7, 8, 9] as const; +const DEPRECATED_ANTIGRAVITY_ALIAS_PREFIX = 'gemini-claude-'; +const UPSTREAM_CLAUDE_ALIAS_PREFIX = 'claude-'; /** * Default Antigravity oauth-model-alias entries. @@ -54,12 +57,12 @@ const DEFAULT_ANTIGRAVITY_ALIASES: OAuthModelAliasEntry[] = [ { name: 'gemini-3-pro-high', alias: 'gemini-3.1-pro-preview' }, { name: 'gemini-3-pro-high', alias: 'gemini-3.1-pro-preview-customtools' }, { name: 'gemini-3-flash', alias: 'gemini-3-flash-preview' }, - { name: 'claude-sonnet-4-6', alias: 'gemini-claude-sonnet-4-6', fork: true }, - { name: 'claude-sonnet-4-6-thinking', alias: 'gemini-claude-sonnet-4-6-thinking', fork: true }, - { name: 'claude-sonnet-4-5', alias: 'gemini-claude-sonnet-4-5', fork: true }, - { name: 'claude-sonnet-4-5-thinking', alias: 'gemini-claude-sonnet-4-5-thinking', fork: true }, - { name: 'claude-opus-4-5-thinking', alias: 'gemini-claude-opus-4-5-thinking', fork: true }, - { name: 'claude-opus-4-6-thinking', alias: 'gemini-claude-opus-4-6-thinking', fork: true }, + { name: 'claude-sonnet-4-6', alias: 'claude-sonnet-4-6', fork: true }, + { name: 'claude-sonnet-4-6-thinking', alias: 'claude-sonnet-4-6-thinking', fork: true }, + { name: 'claude-sonnet-4-5', alias: 'claude-sonnet-4-5', fork: true }, + { name: 'claude-sonnet-4-5-thinking', alias: 'claude-sonnet-4-5-thinking', fork: true }, + { name: 'claude-opus-4-5-thinking', alias: 'claude-opus-4-5-thinking', fork: true }, + { name: 'claude-opus-4-6-thinking', alias: 'claude-opus-4-6-thinking', fork: true }, ]; /** @@ -101,6 +104,16 @@ function sanitizeYamlScalar(rawValue: string): string { return trimmed; } +function normalizeAntigravityAlias(rawAlias: string): string { + const normalized = sanitizeYamlScalar(rawAlias); + if (normalized.toLowerCase().startsWith(DEPRECATED_ANTIGRAVITY_ALIAS_PREFIX)) { + return ( + UPSTREAM_CLAUDE_ALIAS_PREFIX + normalized.slice(DEPRECATED_ANTIGRAVITY_ALIAS_PREFIX.length) + ); + } + return normalized; +} + function addAliasEntry( entries: OAuthModelAliasEntry[], indexByKey: Map, @@ -108,7 +121,7 @@ function addAliasEntry( ): void { const normalized: OAuthModelAliasEntry = { name: sanitizeYamlScalar(entry.name), - alias: sanitizeYamlScalar(entry.alias), + alias: normalizeAntigravityAlias(entry.alias), fork: entry.fork || undefined, }; if (!normalized.name || !normalized.alias) return; diff --git a/tests/unit/cliproxy/config-generator.test.js b/tests/unit/cliproxy/config-generator.test.js index 772d7a9b..d32269f1 100644 --- a/tests/unit/cliproxy/config-generator.test.js +++ b/tests/unit/cliproxy/config-generator.test.js @@ -587,11 +587,15 @@ auth-dir: "${cliproxyDir.replace(/\\/g, '/')}/auth" assert(config.includes('claude-sonnet-4-5'), 'Should include Claude sonnet model'); assert(config.includes('claude-sonnet-4-6'), 'Should include Claude Sonnet 4.6 model'); assert(config.includes('fork: true'), 'Should include fork: true for Claude aliases'); + assert( + !config.includes('alias: gemini-claude-'), + 'Should not emit deprecated gemini-claude aliases' + ); // Verify fork: true appears after each Claude alias entry const lines = config.split('\n'); for (let i = 0; i < lines.length; i++) { - if (lines[i].includes('alias: gemini-claude-')) { + if (lines[i].includes('alias: claude-')) { assert( lines[i + 1] && lines[i + 1].trim() === 'fork: true', `fork: true should follow Claude alias at line ${i}: ${lines[i]}` @@ -766,5 +770,35 @@ oauth-model-alias: } } }); + + it('normalizes deprecated gemini-claude aliases during regeneration', () => { + const cliproxyDir = path.join(testDir, '.ccs', 'cliproxy'); + fs.mkdirSync(cliproxyDir, { recursive: true }); + + const initialConfig = `# CLIProxyAPI config generated by CCS v9 +port: 8317 +api-keys: + - "ccs-internal-managed" +auth-dir: "${cliproxyDir.replace(/\\/g, '/')}/auth" +oauth-model-alias: + antigravity: + - name: claude-sonnet-4-6-thinking + alias: gemini-claude-sonnet-4-6-thinking + fork: true +`; + fs.writeFileSync(path.join(cliproxyDir, 'config.yaml'), initialConfig); + + regenerateConfig(); + + const newConfig = fs.readFileSync(path.join(cliproxyDir, 'config.yaml'), 'utf-8'); + assert( + newConfig.includes('alias: claude-sonnet-4-6-thinking'), + 'Should include normalized upstream Claude alias' + ); + assert( + !newConfig.includes('alias: gemini-claude-sonnet-4-6-thinking'), + 'Should remove deprecated gemini-claude alias after regeneration' + ); + }); }); });