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 committed 2026-02-18 03:35:08 +07:00
1 parent 39593c161b
commit 7e527af777
7 files changed
+53 -26

No files matched your search

+1 -12
View File
@@ -7,6 +7,7 @@
import { CLIProxyProvider } from '../types'; import { CLIProxyProvider } from '../types';
import type { AccountInfo } from '../account-manager'; import type { AccountInfo } from '../account-manager';
import { import {
buildProviderMap,
CLIPROXY_PROVIDER_IDS, CLIPROXY_PROVIDER_IDS,
getOAuthCallbackPort, getOAuthCallbackPort,
getCLIProxyCallbackProviderName, getCLIProxyCallbackProviderName,
@@ -15,18 +16,6 @@ import {
getProviderTokenTypeValues, getProviderTokenTypeValues,
} from '../provider-capabilities'; } 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. * Kiro authentication methods supported by CLIProxyAPIPlus.
* - aws: AWS Builder ID via Device Code flow * - 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 { export interface ProviderCapabilities {
displayName: string; displayName: string;
description: string;
oauthFlow: OAuthFlowType; oauthFlow: OAuthFlowType;
callbackPort: number | null; callbackPort: number | null;
/** Provider name expected by CLIProxyAPI callback endpoint payload. */ /** Provider name expected by CLIProxyAPI callback endpoint payload. */
@@ -27,6 +28,7 @@ export interface ProviderCapabilities {
export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilities> = { export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilities> = {
gemini: { gemini: {
displayName: 'Google Gemini', displayName: 'Google Gemini',
description: 'Gemini Pro/Flash models',
oauthFlow: 'authorization_code', oauthFlow: 'authorization_code',
callbackPort: 8085, callbackPort: 8085,
callbackProviderName: 'gemini', callbackProviderName: 'gemini',
@@ -37,7 +39,8 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
aliases: ['gemini-cli'], aliases: ['gemini-cli'],
}, },
codex: { codex: {
displayName: 'Codex', displayName: 'OpenAI Codex',
description: 'GPT-4 and codex models',
oauthFlow: 'authorization_code', oauthFlow: 'authorization_code',
callbackPort: 1455, callbackPort: 1455,
callbackProviderName: 'codex', callbackProviderName: 'codex',
@@ -48,7 +51,8 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
aliases: [], aliases: [],
}, },
agy: { agy: {
displayName: 'AntiGravity', displayName: 'Antigravity',
description: 'Antigravity AI models',
oauthFlow: 'authorization_code', oauthFlow: 'authorization_code',
callbackPort: 51121, callbackPort: 51121,
callbackProviderName: 'antigravity', callbackProviderName: 'antigravity',
@@ -59,7 +63,8 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
aliases: ['antigravity'], aliases: ['antigravity'],
}, },
qwen: { qwen: {
displayName: 'Qwen', displayName: 'Alibaba Qwen',
description: 'Qwen Code models',
oauthFlow: 'device_code', oauthFlow: 'device_code',
callbackPort: null, callbackPort: null,
callbackProviderName: 'qwen', callbackProviderName: 'qwen',
@@ -71,6 +76,7 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
}, },
iflow: { iflow: {
displayName: 'iFlow', displayName: 'iFlow',
description: 'iFlow AI models',
oauthFlow: 'authorization_code', oauthFlow: 'authorization_code',
callbackPort: 11451, callbackPort: 11451,
callbackProviderName: 'iflow', callbackProviderName: 'iflow',
@@ -82,6 +88,7 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
}, },
kiro: { kiro: {
displayName: 'Kiro (AWS)', displayName: 'Kiro (AWS)',
description: 'AWS CodeWhisperer models',
oauthFlow: 'device_code', oauthFlow: 'device_code',
callbackPort: null, callbackPort: null,
callbackProviderName: 'kiro', callbackProviderName: 'kiro',
@@ -93,6 +100,7 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
}, },
ghcp: { ghcp: {
displayName: 'GitHub Copilot (OAuth)', displayName: 'GitHub Copilot (OAuth)',
description: 'GitHub Copilot via OAuth',
oauthFlow: 'device_code', oauthFlow: 'device_code',
callbackPort: null, callbackPort: null,
callbackProviderName: 'copilot', callbackProviderName: 'copilot',
@@ -103,7 +111,8 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
aliases: ['github-copilot', 'copilot'], aliases: ['github-copilot', 'copilot'],
}, },
claude: { claude: {
displayName: 'Claude', displayName: 'Claude (Anthropic)',
description: 'Claude Opus/Sonnet models',
oauthFlow: 'authorization_code', oauthFlow: 'authorization_code',
callbackPort: 54545, callbackPort: 54545,
callbackProviderName: 'anthropic', callbackProviderName: 'anthropic',
@@ -115,6 +124,7 @@ export const PROVIDER_CAPABILITIES: Record<CLIProxyProvider, ProviderCapabilitie
}, },
kimi: { kimi: {
displayName: 'Kimi (Moonshot)', displayName: 'Kimi (Moonshot)',
description: 'Moonshot AI K2/K2.5 models',
oauthFlow: 'device_code', oauthFlow: 'device_code',
callbackPort: null, callbackPort: null,
callbackProviderName: 'kimi', callbackProviderName: 'kimi',
@@ -130,6 +140,18 @@ export const CLIPROXY_PROVIDER_IDS = Object.freeze(
Object.keys(PROVIDER_CAPABILITIES) as CLIProxyProvider[] 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_ID_SET = new Set(CLIPROXY_PROVIDER_IDS);
const PROVIDER_ALIAS_MAP: ReadonlyMap<string, CLIProxyProvider> = (() => { const PROVIDER_ALIAS_MAP: ReadonlyMap<string, CLIProxyProvider> = (() => {
@@ -155,6 +177,10 @@ export function getProviderDisplayName(provider: CLIProxyProvider): string {
return PROVIDER_CAPABILITIES[provider].displayName; return PROVIDER_CAPABILITIES[provider].displayName;
} }
export function getProviderDescription(provider: CLIProxyProvider): string {
return PROVIDER_CAPABILITIES[provider].description;
}
export function getProvidersByOAuthFlow(flowType: OAuthFlowType): CLIProxyProvider[] { export function getProvidersByOAuthFlow(flowType: OAuthFlowType): CLIProxyProvider[] {
return CLIPROXY_PROVIDER_IDS.filter( return CLIPROXY_PROVIDER_IDS.filter(
(provider) => PROVIDER_CAPABILITIES[provider].oauthFlow === flowType (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 { DEFAULT_CURSOR_PORT as BACKEND_CURSOR_DEFAULT_PORT } from '../../../src/cursor/cursor-models';
import { import {
CLIPROXY_PROVIDER_IDS as BACKEND_CLIPROXY_PROVIDER_IDS, CLIPROXY_PROVIDER_IDS as BACKEND_CLIPROXY_PROVIDER_IDS,
getProviderDescription as getBackendProviderDescription,
getProviderDisplayName as getBackendProviderDisplayName,
getProvidersByOAuthFlow, getProvidersByOAuthFlow,
} from '../../../src/cliproxy/provider-capabilities'; } from '../../../src/cliproxy/provider-capabilities';
import { import {
@@ -18,6 +20,7 @@ import {
import { import {
CLIPROXY_PROVIDERS as UI_CLIPROXY_PROVIDERS, CLIPROXY_PROVIDERS as UI_CLIPROXY_PROVIDERS,
DEVICE_CODE_PROVIDERS as UI_DEVICE_CODE_PROVIDERS, DEVICE_CODE_PROVIDERS as UI_DEVICE_CODE_PROVIDERS,
PROVIDER_METADATA as UI_PROVIDER_METADATA,
} from '../../../ui/src/lib/provider-config'; } from '../../../ui/src/lib/provider-config';
function sorted(values: readonly string[]): string[] { function sorted(values: readonly string[]): string[] {
@@ -40,4 +43,16 @@ describe('Default Port Sync', () => {
test('Device code providers are synced between backend and UI', () => { test('Device code providers are synced between backend and UI', () => {
expect(sorted(UI_DEVICE_CODE_PROVIDERS)).toEqual(sorted(getProvidersByOAuthFlow('device_code'))); 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, useCreatePreset,
useDeletePreset, useDeletePreset,
} from '@/hooks/use-cliproxy'; } 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 { usePrivacy } from '@/contexts/privacy-context';
import { useProviderEditor } from './use-provider-editor'; import { useProviderEditor } from './use-provider-editor';
import { CustomPresetDialog } from './custom-preset-dialog'; import { CustomPresetDialog } from './custom-preset-dialog';
@@ -117,7 +117,7 @@ export function ProviderEditor({
const effectiveApiKey = authTokens?.apiKey?.value ?? 'ccs-internal-managed'; const effectiveApiKey = authTokens?.apiKey?.value ?? 'ccs-internal-managed';
const handleApplyPreset = (updates: Record<string, string>) => { const handleApplyPreset = (updates: Record<string, string>) => {
const effectivePort = port ?? CLIPROXY_PORT; const effectivePort = port ?? CLIPROXY_DEFAULT_PORT;
updateEnvValues({ updateEnvValues({
ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`, ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`,
ANTHROPIC_AUTH_TOKEN: effectiveApiKey, ANTHROPIC_AUTH_TOKEN: effectiveApiKey,
@@ -127,7 +127,7 @@ export function ProviderEditor({
}; };
const handleCustomPresetApply = (values: ModelMappingValues, presetName?: string) => { const handleCustomPresetApply = (values: ModelMappingValues, presetName?: string) => {
const effectivePort = port ?? CLIPROXY_PORT; const effectivePort = port ?? CLIPROXY_DEFAULT_PORT;
updateEnvValues({ updateEnvValues({
ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`, ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`,
ANTHROPIC_AUTH_TOKEN: effectiveApiKey, 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) => ({ export const PROVIDERS: ProviderOption[] = WIZARD_PROVIDER_ORDER.map((id) => ({
id, id,
name: getProviderDisplayName(id), name: getProviderDisplayName(id),
description: getProviderDescription(id) || '', description: getProviderDescription(id),
})); }));
export const ALL_STEPS = ['provider', 'auth', 'variant', 'success']; 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'; import { CLIPROXY_DEFAULT_PORT } from './default-ports';
export { 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 */ /** Default fallback API key if fetch fails */
const DEFAULT_API_KEY = 'ccs-internal-managed'; 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 * 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 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 * @returns Object with success status and applied preset name
*/ */
export async function applyDefaultPreset( 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 */ /** Map provider to user-facing short description */
export function getProviderDescription(provider: string): string | undefined { export function getProviderDescription(provider: string): string {
const normalized = provider.toLowerCase(); const normalized = provider.toLowerCase();
if (!isValidProvider(normalized)) return undefined; if (!isValidProvider(normalized)) return '';
return PROVIDER_METADATA[normalized].description; return PROVIDER_METADATA[normalized].description;
} }