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:
Tam Nhu Tran
2026-02-18 03:35:08 +07:00
parent 39593c161b
commit 7e527af777
7 changed files with 53 additions and 26 deletions
+1 -12
View File
@@ -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
+30 -4
View File
@@ -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,
+1 -1
View File
@@ -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'];
+1 -4
View File
@@ -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(
+2 -2
View File
@@ -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;
}