mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-11 03:13:12 +00:00
refactor(cliproxy): DRY provider lists into single source of truth
- unify CLIPROXY_SUPPORTED_PROVIDERS to import from CLIPROXY_PROVIDER_IDS - replace 4 inline union types with CLIProxyProvider import - replace hardcoded provider arrays in migration-manager and proxy-routes - remove duplicate PROVIDER_DISPLAY_NAMES, use getProviderDisplayName() - sync test now imports from ui/src/lib/provider-config instead of hardcoded array Adding a new CLIProxy provider no longer requires updating 14+ hardcoded lists.
This commit is contained in:
1 parent
905b35eea5
commit
94b03c7f75
6 files changed
+20
-50
No files matched your search
@@ -6,6 +6,7 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { CLIProxyProvider, ProviderConfig } from '../types';
|
import { CLIProxyProvider, ProviderConfig } from '../types';
|
||||||
|
import { getProviderDisplayName } from '../provider-capabilities';
|
||||||
import { getModelMappingFromConfig } from '../base-config-loader';
|
import { getModelMappingFromConfig } from '../base-config-loader';
|
||||||
import { loadOrCreateUnifiedConfig } from '../../config/unified-config-loader';
|
import { loadOrCreateUnifiedConfig } from '../../config/unified-config-loader';
|
||||||
import { getEffectiveApiKey, getEffectiveManagementSecret } from '../auth-token-manager';
|
import { getEffectiveApiKey, getEffectiveManagementSecret } from '../auth-token-manager';
|
||||||
@@ -46,34 +47,17 @@ const DEFAULT_ANTIGRAVITY_ALIASES: Array<{ name: string; alias: string; fork?: b
|
|||||||
{ name: 'claude-opus-4-6-thinking', alias: 'gemini-claude-opus-4-6-thinking', fork: true },
|
{ name: 'claude-opus-4-6-thinking', alias: 'gemini-claude-opus-4-6-thinking', fork: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Provider display names (static metadata) */
|
|
||||||
const PROVIDER_DISPLAY_NAMES: Record<CLIProxyProvider, string> = {
|
|
||||||
gemini: 'Gemini',
|
|
||||||
codex: 'Codex',
|
|
||||||
agy: 'Antigravity',
|
|
||||||
qwen: 'Qwen Code',
|
|
||||||
iflow: 'iFlow',
|
|
||||||
kiro: 'Kiro (AWS)',
|
|
||||||
ghcp: 'GitHub Copilot (OAuth)',
|
|
||||||
claude: 'Claude (Anthropic)',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get provider configuration
|
* Get provider configuration
|
||||||
* Model mappings are loaded from config/base-{provider}.settings.json
|
* Model mappings are loaded from config/base-{provider}.settings.json
|
||||||
*/
|
*/
|
||||||
export function getProviderConfig(provider: CLIProxyProvider): ProviderConfig {
|
export function getProviderConfig(provider: CLIProxyProvider): ProviderConfig {
|
||||||
const displayName = PROVIDER_DISPLAY_NAMES[provider];
|
|
||||||
if (!displayName) {
|
|
||||||
throw new Error(`Unknown provider: ${provider}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load models from base config file
|
// Load models from base config file
|
||||||
const models = getModelMappingFromConfig(provider);
|
const models = getModelMappingFromConfig(provider);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: provider,
|
name: provider,
|
||||||
displayName,
|
displayName: getProviderDisplayName(provider),
|
||||||
models,
|
models,
|
||||||
requiresOAuth: true, // All CLIProxy providers require OAuth
|
requiresOAuth: true, // All CLIProxy providers require OAuth
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { getCcsDir } from '../utils/config-manager';
|
|||||||
import { expandPath } from '../utils/helpers';
|
import { expandPath } from '../utils/helpers';
|
||||||
import type { ProfileConfig, AccountConfig, CLIProxyVariantConfig } from './unified-config-types';
|
import type { ProfileConfig, AccountConfig, CLIProxyVariantConfig } from './unified-config-types';
|
||||||
import { createEmptyUnifiedConfig } from './unified-config-types';
|
import { createEmptyUnifiedConfig } from './unified-config-types';
|
||||||
|
import { CLIPROXY_PROVIDER_IDS } from '../cliproxy/provider-capabilities';
|
||||||
import { saveUnifiedConfig, hasUnifiedConfig, loadUnifiedConfig } from './unified-config-loader';
|
import { saveUnifiedConfig, hasUnifiedConfig, loadUnifiedConfig } from './unified-config-loader';
|
||||||
import { infoBox, warn } from '../utils/ui';
|
import { infoBox, warn } from '../utils/ui';
|
||||||
|
|
||||||
@@ -203,7 +204,7 @@ export async function migrate(dryRun = false): Promise<MigrationResult> {
|
|||||||
// 6b. Migrate built-in CLIProxy OAuth profile settings (gemini, codex, agy, qwen, iflow)
|
// 6b. Migrate built-in CLIProxy OAuth profile settings (gemini, codex, agy, qwen, iflow)
|
||||||
// Keep settings in *.settings.json files - only record reference in config.yaml
|
// Keep settings in *.settings.json files - only record reference in config.yaml
|
||||||
// This matches Claude's ~/.claude/settings.json pattern for user familiarity
|
// This matches Claude's ~/.claude/settings.json pattern for user familiarity
|
||||||
const builtInProviders = ['gemini', 'codex', 'agy', 'qwen', 'iflow'];
|
const builtInProviders = [...CLIPROXY_PROVIDER_IDS];
|
||||||
for (const provider of builtInProviders) {
|
for (const provider of builtInProviders) {
|
||||||
const settingsFile = `${provider}.settings.json`;
|
const settingsFile = `${provider}.settings.json`;
|
||||||
const settingsPath = path.join(ccsDir, settingsFile);
|
const settingsPath = path.join(ccsDir, settingsFile);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { TargetType } from '../targets/target-adapter';
|
import type { TargetType } from '../targets/target-adapter';
|
||||||
|
import type { CLIProxyProvider } from '../cliproxy/types';
|
||||||
|
import { CLIPROXY_PROVIDER_IDS } from '../cliproxy/provider-capabilities';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unified config version.
|
* Unified config version.
|
||||||
@@ -25,18 +27,9 @@ export const UNIFIED_CONFIG_VERSION = 8;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Supported CLIProxy providers.
|
* Supported CLIProxy providers.
|
||||||
* Includes all OAuth-based providers supported by CLIProxyAPI.
|
* Derived from CLIPROXY_PROVIDER_IDS — single source of truth in provider-capabilities.ts.
|
||||||
*/
|
*/
|
||||||
export const CLIPROXY_SUPPORTED_PROVIDERS = [
|
export const CLIPROXY_SUPPORTED_PROVIDERS = CLIPROXY_PROVIDER_IDS;
|
||||||
'gemini',
|
|
||||||
'codex',
|
|
||||||
'agy',
|
|
||||||
'qwen',
|
|
||||||
'iflow',
|
|
||||||
'kiro',
|
|
||||||
'ghcp',
|
|
||||||
'claude',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account configuration (formerly in profiles.json).
|
* Account configuration (formerly in profiles.json).
|
||||||
@@ -80,7 +73,7 @@ export type OAuthAccounts = Record<string, string>;
|
|||||||
*/
|
*/
|
||||||
export interface CLIProxyVariantConfig {
|
export interface CLIProxyVariantConfig {
|
||||||
/** Base provider to use */
|
/** Base provider to use */
|
||||||
provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp' | 'claude';
|
provider: CLIProxyProvider;
|
||||||
/** Account nickname (references oauth_accounts) */
|
/** Account nickname (references oauth_accounts) */
|
||||||
account?: string;
|
account?: string;
|
||||||
/** Path to settings file (e.g., "~/.ccs/gemini-custom.settings.json") */
|
/** Path to settings file (e.g., "~/.ccs/gemini-custom.settings.json") */
|
||||||
@@ -98,14 +91,14 @@ export interface CLIProxyVariantConfig {
|
|||||||
*/
|
*/
|
||||||
export interface CompositeTierConfig {
|
export interface CompositeTierConfig {
|
||||||
/** Provider for this tier */
|
/** Provider for this tier */
|
||||||
provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp' | 'claude';
|
provider: CLIProxyProvider;
|
||||||
/** Model ID to use for this tier */
|
/** Model ID to use for this tier */
|
||||||
model: string;
|
model: string;
|
||||||
/** Account nickname (optional, references oauth_accounts) */
|
/** Account nickname (optional, references oauth_accounts) */
|
||||||
account?: string;
|
account?: string;
|
||||||
/** Fallback provider+model if primary fails */
|
/** Fallback provider+model if primary fails */
|
||||||
fallback?: {
|
fallback?: {
|
||||||
provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp' | 'claude';
|
provider: CLIProxyProvider;
|
||||||
model: string;
|
model: string;
|
||||||
account?: string;
|
account?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-1
@@ -3,6 +3,8 @@
|
|||||||
* Source: ~/.ccs/config.json
|
* Source: ~/.ccs/config.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { CLIProxyProvider } from '../cliproxy/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profile configuration mapping
|
* Profile configuration mapping
|
||||||
* Maps profile names to settings.json paths
|
* Maps profile names to settings.json paths
|
||||||
@@ -18,7 +20,7 @@ export interface ProfilesConfig {
|
|||||||
*/
|
*/
|
||||||
export interface CLIProxyVariantConfig {
|
export interface CLIProxyVariantConfig {
|
||||||
/** CLIProxy provider to use */
|
/** CLIProxy provider to use */
|
||||||
provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp' | 'claude';
|
provider: CLIProxyProvider;
|
||||||
/** Path to settings.json with custom model configuration (optional) */
|
/** Path to settings.json with custom model configuration (optional) */
|
||||||
settings?: string;
|
settings?: string;
|
||||||
/** Account identifier for multi-account support (optional, defaults to 'default') */
|
/** Account identifier for multi-account support (optional, defaults to 'default') */
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
DEFAULT_CLIPROXY_SERVER_CONFIG,
|
DEFAULT_CLIPROXY_SERVER_CONFIG,
|
||||||
CliproxyServerConfig,
|
CliproxyServerConfig,
|
||||||
} from '../../config/unified-config-types';
|
} from '../../config/unified-config-types';
|
||||||
|
import { CLIPROXY_PROVIDER_IDS } from '../../cliproxy/provider-capabilities';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ router.put('/backend', async (req: Request, res: Response) => {
|
|||||||
config.cliproxy = {
|
config.cliproxy = {
|
||||||
backend,
|
backend,
|
||||||
oauth_accounts: {},
|
oauth_accounts: {},
|
||||||
providers: ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp'],
|
providers: [...CLIPROXY_PROVIDER_IDS],
|
||||||
variants: {},
|
variants: {},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,34 +7,23 @@
|
|||||||
|
|
||||||
import { describe, expect, test } from 'bun:test';
|
import { describe, expect, test } from 'bun:test';
|
||||||
import { CLIPROXY_PROFILES } from '../../../src/auth/profile-detector';
|
import { CLIPROXY_PROFILES } from '../../../src/auth/profile-detector';
|
||||||
|
import { CLIPROXY_PROVIDERS } from '../../../ui/src/lib/provider-config';
|
||||||
// UI providers (must manually sync - this test validates the sync)
|
|
||||||
const UI_CLIPROXY_PROVIDERS = [
|
|
||||||
'gemini',
|
|
||||||
'codex',
|
|
||||||
'agy',
|
|
||||||
'qwen',
|
|
||||||
'iflow',
|
|
||||||
'kiro',
|
|
||||||
'ghcp',
|
|
||||||
'claude',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
describe('Provider Sync', () => {
|
describe('Provider Sync', () => {
|
||||||
test('backend CLIPROXY_PROFILES matches UI CLIPROXY_PROVIDERS', () => {
|
test('backend CLIPROXY_PROFILES matches UI CLIPROXY_PROVIDERS', () => {
|
||||||
const backend = [...CLIPROXY_PROFILES].sort();
|
const backend = [...CLIPROXY_PROFILES].sort();
|
||||||
const ui = [...UI_CLIPROXY_PROVIDERS].sort();
|
const ui = [...CLIPROXY_PROVIDERS].sort();
|
||||||
|
|
||||||
expect(backend).toEqual(ui);
|
expect(backend).toEqual(ui);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('both arrays have same length', () => {
|
test('both arrays have same length', () => {
|
||||||
expect(CLIPROXY_PROFILES.length).toBe(UI_CLIPROXY_PROVIDERS.length);
|
expect(CLIPROXY_PROFILES.length).toBe(CLIPROXY_PROVIDERS.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('UI array contains all backend providers', () => {
|
test('UI array contains all backend providers', () => {
|
||||||
for (const provider of CLIPROXY_PROFILES) {
|
for (const provider of CLIPROXY_PROFILES) {
|
||||||
expect(UI_CLIPROXY_PROVIDERS).toContain(provider);
|
expect(CLIPROXY_PROVIDERS).toContain(provider);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user