diff --git a/src/cliproxy/auth/auth-types.ts b/src/cliproxy/auth/auth-types.ts index 90b086d8..92080547 100644 --- a/src/cliproxy/auth/auth-types.ts +++ b/src/cliproxy/auth/auth-types.ts @@ -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( - valueFor: (provider: CLIProxyProvider) => T -): Record { - return CLIPROXY_PROVIDER_IDS.reduce( - (acc, provider) => { - acc[provider] = valueFor(provider); - return acc; - }, - {} as Record - ); -} - /** * Kiro authentication methods supported by CLIProxyAPIPlus. * - aws: AWS Builder ID via Device Code flow diff --git a/src/cliproxy/provider-capabilities.ts b/src/cliproxy/provider-capabilities.ts index 5d575569..04049bcf 100644 --- a/src/cliproxy/provider-capabilities.ts +++ b/src/cliproxy/provider-capabilities.ts @@ -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 = { 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( + valueFor: (provider: CLIProxyProvider) => T +): Record { + return CLIPROXY_PROVIDER_IDS.reduce( + (acc, provider) => { + acc[provider] = valueFor(provider); + return acc; + }, + {} as Record + ); +} + const PROVIDER_ID_SET = new Set(CLIPROXY_PROVIDER_IDS); const PROVIDER_ALIAS_MAP: ReadonlyMap = (() => { @@ -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 diff --git a/tests/unit/cliproxy/backend-ui-default-ports-sync.test.ts b/tests/unit/cliproxy/backend-ui-default-ports-sync.test.ts index 8e9bee7c..c8fe0d1d 100644 --- a/tests/unit/cliproxy/backend-ui-default-ports-sync.test.ts +++ b/tests/unit/cliproxy/backend-ui-default-ports-sync.test.ts @@ -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)); + } + }); }); diff --git a/ui/src/components/cliproxy/provider-editor/index.tsx b/ui/src/components/cliproxy/provider-editor/index.tsx index 59658788..ab19c98a 100644 --- a/ui/src/components/cliproxy/provider-editor/index.tsx +++ b/ui/src/components/cliproxy/provider-editor/index.tsx @@ -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) => { - 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, diff --git a/ui/src/components/setup/wizard/constants.ts b/ui/src/components/setup/wizard/constants.ts index 9fba4e91..9f5014ed 100644 --- a/ui/src/components/setup/wizard/constants.ts +++ b/ui/src/components/setup/wizard/constants.ts @@ -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']; diff --git a/ui/src/lib/preset-utils.ts b/ui/src/lib/preset-utils.ts index bf2de02b..1121d5ab 100644 --- a/ui/src/lib/preset-utils.ts +++ b/ui/src/lib/preset-utils.ts @@ -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 { * 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( diff --git a/ui/src/lib/provider-config.ts b/ui/src/lib/provider-config.ts index eea4f08b..9a3fdc18 100644 --- a/ui/src/lib/provider-config.ts +++ b/ui/src/lib/provider-config.ts @@ -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; }