From 25ea4a0cf3c8b09717240e8c3f5514123995e2bd Mon Sep 17 00:00:00 2001 From: xixingde <137176592+xixingde@users.noreply.github.com> Date: Thu, 14 May 2026 16:03:04 +0800 Subject: [PATCH] feat(auth): add remote API control config to gate third-party providers --- src/commands/provider.ts | 16 ++-- src/components/ConsoleOAuthFlow.tsx | 127 ++++++++++++++----------- src/entrypoints/init.ts | 18 ++++ src/services/apiControlConfig/index.ts | 106 +++++++++++++++++++++ 4 files changed, 200 insertions(+), 67 deletions(-) create mode 100644 src/services/apiControlConfig/index.ts diff --git a/src/commands/provider.ts b/src/commands/provider.ts index 6cbb42519..0ccccc3de 100644 --- a/src/commands/provider.ts +++ b/src/commands/provider.ts @@ -1,6 +1,7 @@ import type { Command } from '../commands.js' import type { LocalCommandCall } from '../types/command.js' import { getAPIProvider } from '../utils/model/providers.js' +import { isThirdPartyApiEnabled } from '../services/apiControlConfig/index.js' import { updateSettingsForSource } from '../utils/settings/settings.js' import { getSettings_DEPRECATED } from '../utils/settings/settings.js' import { applyConfigEnvironmentVariables } from '../utils/managedEnv.js' @@ -85,16 +86,11 @@ const call: LocalCommandCall = async (args, _context) => { } } - // Validate provider - const validProviders = [ - 'anthropic', - 'openai', - 'gemini', - 'grok', - 'bedrock', - 'vertex', - 'foundry', - ] + // Validate provider — dynamically filter based on remote API control config + const thirdPartyEnabled = isThirdPartyApiEnabled() + const validProviders = thirdPartyEnabled + ? ['anthropic', 'openai', 'gemini', 'grok', 'bedrock', 'vertex', 'foundry'] + : [] if (!validProviders.includes(arg)) { return { type: 'text', diff --git a/src/components/ConsoleOAuthFlow.tsx b/src/components/ConsoleOAuthFlow.tsx index 0d914873e..c608c9ecf 100644 --- a/src/components/ConsoleOAuthFlow.tsx +++ b/src/components/ConsoleOAuthFlow.tsx @@ -19,6 +19,7 @@ import { Spinner } from './Spinner.js' import TextInput from './TextInput.js' import { useSetAppState } from '../state/AppState.js' import { fi } from 'zod/v4/locales' +import { isThirdPartyApiEnabled } from '../services/apiControlConfig/index.js' type Props = { onDone(): void @@ -100,6 +101,13 @@ export function ConsoleOAuthFlow({ return { state: 'ready_to_start' } } if (forceLoginMethod === 'claudeai' || forceLoginMethod === 'console') { + // When third-party APIs are disabled by remote config, ignore + // forceLoginMethod and show the idle screen (CoStrict only). + // This prevents bypassing the isThirdPartyApiEnabled() gate + // through settings.forceLoginMethod. + if (!isThirdPartyApiEnabled()) { + return { state: 'idle' } + } return { state: 'ready_to_start' } } return { state: 'idle' } @@ -416,6 +424,7 @@ export function ConsoleOAuthFlow({ setOAuthStatus={setOAuthStatus} setLoginWithClaudeAi={setLoginWithClaudeAi} onDone={onDone} + setAppState={setAppState} /> @@ -437,6 +446,7 @@ type OAuthStatusMessageProps = { handleSubmitCode: (value: string, url: string) => void setOAuthStatus: (status: OAuthStatus) => void setLoginWithClaudeAi: (value: boolean) => void + setAppState: ReturnType } function OAuthStatusMessage({ @@ -454,10 +464,61 @@ function OAuthStatusMessage({ setOAuthStatus, setLoginWithClaudeAi, onDone, + setAppState, }: OAuthStatusMessageProps): React.ReactNode { - const setAppState = useSetAppState() switch (oauthStatus.state) { - case 'idle': + case 'idle': { + const thirdPartyEnabled = isThirdPartyApiEnabled() + const loginOptions: Array<{ label: React.ReactNode; value: string }> = [ + { + label: ( + + CoStrict ·{' '} + Sign in with CoStrict account + {'\n'} + + ), + value: 'costrict', + }, + ] + + if (thirdPartyEnabled) { + loginOptions.push( + { + label: ( + + Anthropic Compatible ·{' '} + Configure your own API endpoint + {'\n'} + + ), + value: 'custom_platform', + }, + { + label: ( + + OpenAI Compatible ·{' '} + + Ollama, DeepSeek, vLLM, One API, etc. + + {'\n'} + + ), + value: 'openai_chat_api', + }, + { + label: ( + + Gemini API ·{' '} + Google Gemini native REST/SSE + {'\n'} + + ), + value: 'gemini_api', + }, + ) + } + return ( @@ -470,50 +531,7 @@ function OAuthStatusMessage({