mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 02:11:28 +00:00
refactor(cliproxy): resolve remaining review parity and cleanup nits
- export buildProviderMap from provider capabilities and reuse in auth-types - add provider descriptions to backend capabilities and sync display names - remove ui CLIPROXY_PORT alias and use CLIPROXY_DEFAULT_PORT directly - return stable string from getProviderDescription - extend backend/ui parity tests for display name and description values
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
import { CLIProxyProvider } from '../types';
|
||||
import type { AccountInfo } from '../account-manager';
|
||||
import {
|
||||
buildProviderMap,
|
||||
CLIPROXY_PROVIDER_IDS,
|
||||
getOAuthCallbackPort,
|
||||
getCLIProxyCallbackProviderName,
|
||||
@@ -15,18 +16,6 @@ import {
|
||||
getProviderTokenTypeValues,
|
||||
} from '../provider-capabilities';
|
||||
|
||||
function buildProviderMap<T>(
|
||||
valueFor: (provider: CLIProxyProvider) => T
|
||||
): Record<CLIProxyProvider, T> {
|
||||
return CLIPROXY_PROVIDER_IDS.reduce(
|
||||
(acc, provider) => {
|
||||
acc[provider] = valueFor(provider);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<CLIProxyProvider, T>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kiro authentication methods supported by CLIProxyAPIPlus.
|
||||
* - aws: AWS Builder ID via Device Code flow
|
||||
|
||||
@@ -5,6 +5,7 @@ export type TokenRefreshOwnership = 'ccs' | 'cliproxy' | 'unsupported';
|
||||
|
||||
export interface ProviderCapabilities {
|
||||
displayName: string;
|
||||
description: string;
|
||||
oauthFlow: OAuthFlowType;
|
||||
callbackPort: number | null;
|
||||
/** Provider name expected by CLIProxyAPI callback endpoint payload. */
|
||||
@@ -27,6 +28,7 @@ export interface ProviderCapabilities {
|
||||
export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilities> = {
|
||||
gemini: {
|
||||
displayName: 'Google Gemini',
|
||||
description: 'Gemini Pro/Flash models',
|
||||
oauthFlow: 'authorization_code',
|
||||
callbackPort: 8085,
|
||||
callbackProviderName: 'gemini',
|
||||
@@ -37,7 +39,8 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
||||
aliases: ['gemini-cli'],
|
||||
},
|
||||
codex: {
|
||||
displayName: 'Codex',
|
||||
displayName: 'OpenAI Codex',
|
||||
description: 'GPT-4 and codex models',
|
||||
oauthFlow: 'authorization_code',
|
||||
callbackPort: 1455,
|
||||
callbackProviderName: 'codex',
|
||||
@@ -48,7 +51,8 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
||||
aliases: [],
|
||||
},
|
||||
agy: {
|
||||
displayName: 'AntiGravity',
|
||||
displayName: 'Antigravity',
|
||||
description: 'Antigravity AI models',
|
||||
oauthFlow: 'authorization_code',
|
||||
callbackPort: 51121,
|
||||
callbackProviderName: 'antigravity',
|
||||
@@ -59,7 +63,8 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
||||
aliases: ['antigravity'],
|
||||
},
|
||||
qwen: {
|
||||
displayName: 'Qwen',
|
||||
displayName: 'Alibaba Qwen',
|
||||
description: 'Qwen Code models',
|
||||
oauthFlow: 'device_code',
|
||||
callbackPort: null,
|
||||
callbackProviderName: 'qwen',
|
||||
@@ -71,6 +76,7 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
||||
},
|
||||
iflow: {
|
||||
displayName: 'iFlow',
|
||||
description: 'iFlow AI models',
|
||||
oauthFlow: 'authorization_code',
|
||||
callbackPort: 11451,
|
||||
callbackProviderName: 'iflow',
|
||||
@@ -82,6 +88,7 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
||||
},
|
||||
kiro: {
|
||||
displayName: 'Kiro (AWS)',
|
||||
description: 'AWS CodeWhisperer models',
|
||||
oauthFlow: 'device_code',
|
||||
callbackPort: null,
|
||||
callbackProviderName: 'kiro',
|
||||
@@ -93,6 +100,7 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
||||
},
|
||||
ghcp: {
|
||||
displayName: 'GitHub Copilot (OAuth)',
|
||||
description: 'GitHub Copilot via OAuth',
|
||||
oauthFlow: 'device_code',
|
||||
callbackPort: null,
|
||||
callbackProviderName: 'copilot',
|
||||
@@ -103,7 +111,8 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
||||
aliases: ['github-copilot', 'copilot'],
|
||||
},
|
||||
claude: {
|
||||
displayName: 'Claude',
|
||||
displayName: 'Claude (Anthropic)',
|
||||
description: 'Claude Opus/Sonnet models',
|
||||
oauthFlow: 'authorization_code',
|
||||
callbackPort: 54545,
|
||||
callbackProviderName: 'anthropic',
|
||||
@@ -115,6 +124,7 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
|
||||
},
|
||||
kimi: {
|
||||
displayName: 'Kimi (Moonshot)',
|
||||
description: 'Moonshot AI K2/K2.5 models',
|
||||
oauthFlow: 'device_code',
|
||||
callbackPort: null,
|
||||
callbackProviderName: 'kimi',
|
||||
@@ -130,6 +140,18 @@ export const CLIPROXY_PROVIDER_IDS = Object.freeze(
|
||||
Object.keys(PROVIDER_CAPABILITIES) as CLIProxyProvider[]
|
||||
);
|
||||
|
||||
export function buildProviderMap<T>(
|
||||
valueFor: (provider: CLIProxyProvider) => T
|
||||
): Record<CLIProxyProvider, T> {
|
||||
return CLIPROXY_PROVIDER_IDS.reduce(
|
||||
(acc, provider) => {
|
||||
acc[provider] = valueFor(provider);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<CLIProxyProvider, T>
|
||||
);
|
||||
}
|
||||
|
||||
const PROVIDER_ID_SET = new Set(CLIPROXY_PROVIDER_IDS);
|
||||
|
||||
const PROVIDER_ALIAS_MAP: ReadonlyMap<string, CLIProxyProvider> = (() => {
|
||||
@@ -155,6 +177,10 @@ export function getProviderDisplayName(provider: CLIProxyProvider): string {
|
||||
return PROVIDER_CAPABILITIES[provider].displayName;
|
||||
}
|
||||
|
||||
export function getProviderDescription(provider: CLIProxyProvider): string {
|
||||
return PROVIDER_CAPABILITIES[provider].description;
|
||||
}
|
||||
|
||||
export function getProvidersByOAuthFlow(flowType: OAuthFlowType): CLIProxyProvider[] {
|
||||
return CLIPROXY_PROVIDER_IDS.filter(
|
||||
(provider) => PROVIDER_CAPABILITIES[provider].oauthFlow === flowType
|
||||
|
||||
@@ -9,6 +9,8 @@ import { CLIPROXY_DEFAULT_PORT as BACKEND_CLIPROXY_DEFAULT_PORT } from '../../..
|
||||
import { DEFAULT_CURSOR_PORT as BACKEND_CURSOR_DEFAULT_PORT } from '../../../src/cursor/cursor-models';
|
||||
import {
|
||||
CLIPROXY_PROVIDER_IDS as BACKEND_CLIPROXY_PROVIDER_IDS,
|
||||
getProviderDescription as getBackendProviderDescription,
|
||||
getProviderDisplayName as getBackendProviderDisplayName,
|
||||
getProvidersByOAuthFlow,
|
||||
} from '../../../src/cliproxy/provider-capabilities';
|
||||
import {
|
||||
@@ -18,6 +20,7 @@ import {
|
||||
import {
|
||||
CLIPROXY_PROVIDERS as UI_CLIPROXY_PROVIDERS,
|
||||
DEVICE_CODE_PROVIDERS as UI_DEVICE_CODE_PROVIDERS,
|
||||
PROVIDER_METADATA as UI_PROVIDER_METADATA,
|
||||
} from '../../../ui/src/lib/provider-config';
|
||||
|
||||
function sorted(values: readonly string[]): string[] {
|
||||
@@ -40,4 +43,16 @@ describe('Default Port Sync', () => {
|
||||
test('Device code providers are synced between backend and UI', () => {
|
||||
expect(sorted(UI_DEVICE_CODE_PROVIDERS)).toEqual(sorted(getProvidersByOAuthFlow('device_code')));
|
||||
});
|
||||
|
||||
test('Provider display names are synced between backend and UI', () => {
|
||||
for (const provider of BACKEND_CLIPROXY_PROVIDER_IDS) {
|
||||
expect(UI_PROVIDER_METADATA[provider].displayName).toBe(getBackendProviderDisplayName(provider));
|
||||
}
|
||||
});
|
||||
|
||||
test('Provider descriptions are synced between backend and UI', () => {
|
||||
for (const provider of BACKEND_CLIPROXY_PROVIDER_IDS) {
|
||||
expect(UI_PROVIDER_METADATA[provider].description).toBe(getBackendProviderDescription(provider));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
useCreatePreset,
|
||||
useDeletePreset,
|
||||
} from '@/hooks/use-cliproxy';
|
||||
import { CLIPROXY_PORT } from '@/lib/preset-utils';
|
||||
import { CLIPROXY_DEFAULT_PORT } from '@/lib/preset-utils';
|
||||
import { usePrivacy } from '@/contexts/privacy-context';
|
||||
import { useProviderEditor } from './use-provider-editor';
|
||||
import { CustomPresetDialog } from './custom-preset-dialog';
|
||||
@@ -117,7 +117,7 @@ export function ProviderEditor({
|
||||
const effectiveApiKey = authTokens?.apiKey?.value ?? 'ccs-internal-managed';
|
||||
|
||||
const handleApplyPreset = (updates: Record<string, string>) => {
|
||||
const effectivePort = port ?? CLIPROXY_PORT;
|
||||
const effectivePort = port ?? CLIPROXY_DEFAULT_PORT;
|
||||
updateEnvValues({
|
||||
ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`,
|
||||
ANTHROPIC_AUTH_TOKEN: effectiveApiKey,
|
||||
@@ -127,7 +127,7 @@ export function ProviderEditor({
|
||||
};
|
||||
|
||||
const handleCustomPresetApply = (values: ModelMappingValues, presetName?: string) => {
|
||||
const effectivePort = port ?? CLIPROXY_PORT;
|
||||
const effectivePort = port ?? CLIPROXY_DEFAULT_PORT;
|
||||
updateEnvValues({
|
||||
ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`,
|
||||
ANTHROPIC_AUTH_TOKEN: effectiveApiKey,
|
||||
|
||||
@@ -27,7 +27,7 @@ const WIZARD_PROVIDER_ORDER: CLIProxyProvider[] = [
|
||||
export const PROVIDERS: ProviderOption[] = WIZARD_PROVIDER_ORDER.map((id) => ({
|
||||
id,
|
||||
name: getProviderDisplayName(id),
|
||||
description: getProviderDescription(id) || '',
|
||||
description: getProviderDescription(id),
|
||||
}));
|
||||
|
||||
export const ALL_STEPS = ['provider', 'auth', 'variant', 'success'];
|
||||
|
||||
@@ -7,9 +7,6 @@ import { MODEL_CATALOGS } from './model-catalogs';
|
||||
import { CLIPROXY_DEFAULT_PORT } from './default-ports';
|
||||
export { CLIPROXY_DEFAULT_PORT } from './default-ports';
|
||||
|
||||
/** CLIProxy port - should match the backend configuration */
|
||||
export const CLIPROXY_PORT = CLIPROXY_DEFAULT_PORT;
|
||||
|
||||
/** Default fallback API key if fetch fails */
|
||||
const DEFAULT_API_KEY = 'ccs-internal-managed';
|
||||
|
||||
@@ -33,7 +30,7 @@ async function fetchEffectiveApiKey(): Promise<string> {
|
||||
* Uses the first model's presetMapping or falls back to using defaultModel for all tiers
|
||||
*
|
||||
* @param provider - The provider ID (e.g., 'gemini', 'codex', 'agy')
|
||||
* @param port - Optional custom port (defaults to CLIPROXY_PORT)
|
||||
* @param port - Optional custom port (defaults to CLIPROXY_DEFAULT_PORT)
|
||||
* @returns Object with success status and applied preset name
|
||||
*/
|
||||
export async function applyDefaultPreset(
|
||||
|
||||
@@ -115,9 +115,9 @@ export function getProviderDisplayName(provider: string): string {
|
||||
}
|
||||
|
||||
/** Map provider to user-facing short description */
|
||||
export function getProviderDescription(provider: string): string | undefined {
|
||||
export function getProviderDescription(provider: string): string {
|
||||
const normalized = provider.toLowerCase();
|
||||
if (!isValidProvider(normalized)) return undefined;
|
||||
if (!isValidProvider(normalized)) return '';
|
||||
return PROVIDER_METADATA[normalized].description;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user