diff --git a/src/cliproxy/auth/provider-refreshers/index.ts b/src/cliproxy/auth/provider-refreshers/index.ts index 3e7c763c..07ed2bd2 100644 --- a/src/cliproxy/auth/provider-refreshers/index.ts +++ b/src/cliproxy/auth/provider-refreshers/index.ts @@ -26,6 +26,10 @@ export interface ProviderRefreshResult { delegated?: boolean; } +function assertNever(value: never): never { + throw new Error(`Unhandled token refresh ownership: ${String(value)}`); +} + /** * Check if a provider's token refresh is delegated to CLIProxy */ @@ -47,24 +51,22 @@ export async function refreshToken( return await refreshGeminiTokenWrapper(); } - if (isRefreshDelegated(provider)) { - // CLIProxyAPIPlus handles refresh for these providers automatically. - // No action needed from CCS — report success with delegated flag. - return { success: true, delegated: true }; - } - const ownership = getTokenRefreshOwnership(provider); - if (ownership === 'unsupported') { - return { - success: false, - error: `Token refresh not yet implemented for ${provider}`, - }; + switch (ownership) { + case 'cliproxy': + // CLIProxyAPIPlus handles refresh for these providers automatically. + // No action needed from CCS — report success with delegated flag. + return { success: true, delegated: true }; + case 'unsupported': + case 'ccs': + // Non-gemini CCS-owned refresh paths are not implemented yet. + return { + success: false, + error: `Token refresh not yet implemented for ${provider}`, + }; + default: + return assertNever(ownership); } - - return { - success: false, - error: `Unknown provider: ${provider}`, - }; } /** 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 751d7624..8e9bee7c 100644 --- a/tests/unit/cliproxy/backend-ui-default-ports-sync.test.ts +++ b/tests/unit/cliproxy/backend-ui-default-ports-sync.test.ts @@ -7,10 +7,22 @@ import { describe, expect, test } from 'bun:test'; import { CLIPROXY_DEFAULT_PORT as BACKEND_CLIPROXY_DEFAULT_PORT } from '../../../src/cliproxy/config/port-manager'; import { DEFAULT_CURSOR_PORT as BACKEND_CURSOR_DEFAULT_PORT } from '../../../src/cursor/cursor-models'; +import { + CLIPROXY_PROVIDER_IDS as BACKEND_CLIPROXY_PROVIDER_IDS, + getProvidersByOAuthFlow, +} from '../../../src/cliproxy/provider-capabilities'; import { CLIPROXY_DEFAULT_PORT as UI_CLIPROXY_DEFAULT_PORT, DEFAULT_CURSOR_PORT as UI_CURSOR_DEFAULT_PORT, } from '../../../ui/src/lib/default-ports'; +import { + CLIPROXY_PROVIDERS as UI_CLIPROXY_PROVIDERS, + DEVICE_CODE_PROVIDERS as UI_DEVICE_CODE_PROVIDERS, +} from '../../../ui/src/lib/provider-config'; + +function sorted(values: readonly string[]): string[] { + return [...values].sort((a, b) => a.localeCompare(b)); +} describe('Default Port Sync', () => { test('CLIProxy default port is synced between backend and UI', () => { @@ -20,4 +32,12 @@ describe('Default Port Sync', () => { test('Cursor default port is synced between backend and UI', () => { expect(UI_CURSOR_DEFAULT_PORT).toBe(BACKEND_CURSOR_DEFAULT_PORT); }); + + test('CLIProxy provider IDs are synced between backend and UI', () => { + expect(sorted(UI_CLIPROXY_PROVIDERS)).toEqual(sorted(BACKEND_CLIPROXY_PROVIDER_IDS)); + }); + + test('Device code providers are synced between backend and UI', () => { + expect(sorted(UI_DEVICE_CODE_PROVIDERS)).toEqual(sorted(getProvidersByOAuthFlow('device_code'))); + }); }); diff --git a/ui/src/hooks/use-cursor.ts b/ui/src/hooks/use-cursor.ts index e86d8782..2beca36f 100644 --- a/ui/src/hooks/use-cursor.ts +++ b/ui/src/hooks/use-cursor.ts @@ -8,8 +8,6 @@ import { useMemo } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { withApiBase } from '@/lib/api-client'; -export { DEFAULT_CURSOR_PORT } from '@/lib/default-ports'; - export interface CursorStatus { enabled: boolean; authenticated: boolean; diff --git a/ui/src/pages/cursor.tsx b/ui/src/pages/cursor.tsx index f66dd584..ec2da394 100644 --- a/ui/src/pages/cursor.tsx +++ b/ui/src/pages/cursor.tsx @@ -23,7 +23,8 @@ import { XCircle, } from 'lucide-react'; import { cn } from '@/lib/utils'; -import { DEFAULT_CURSOR_PORT, useCursor } from '@/hooks/use-cursor'; +import { useCursor } from '@/hooks/use-cursor'; +import { DEFAULT_CURSOR_PORT } from '@/lib/default-ports'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label';