Merge pull request #2668 from JohnKeating1997/feat/optimize-auth-intro

This commit is contained in:
pomelo 2026-03-27 17:38:17 +08:00 committed by GitHub
commit 4bacdea01e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 629 additions and 9 deletions

View file

@ -0,0 +1,24 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
export type AlibabaStandardRegion =
| 'cn-beijing'
| 'sg-singapore'
| 'us-virginia'
| 'cn-hongkong';
export const DASHSCOPE_STANDARD_API_KEY_ENV_KEY = 'DASHSCOPE_API_KEY';
export const ALIBABA_STANDARD_API_KEY_ENDPOINTS: Record<
AlibabaStandardRegion,
string
> = {
'cn-beijing': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
'sg-singapore': 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',
'us-virginia': 'https://dashscope-us.aliyuncs.com/compatible-mode/v1',
'cn-hongkong':
'https://cn-hongkong.dashscope.aliyuncs.com/compatible-mode/v1',
};

View file

@ -190,6 +190,8 @@ describe('AppContainer State Management', () => {
isAuthDialogOpen: false,
isAuthenticating: false,
handleAuthSelect: vi.fn(),
handleCodingPlanSubmit: vi.fn(),
handleAlibabaStandardSubmit: vi.fn(),
openAuthDialog: vi.fn(),
cancelAuthentication: vi.fn(),
});

View file

@ -457,6 +457,7 @@ export const AppContainer = (props: AppContainerProps) => {
qwenAuthState,
handleAuthSelect,
handleCodingPlanSubmit,
handleAlibabaStandardSubmit,
openAuthDialog,
cancelAuthentication,
} = useAuthCommand(settings, config, historyManager.addItem, refreshStatic);
@ -1691,6 +1692,7 @@ export const AppContainer = (props: AppContainerProps) => {
onAuthError,
cancelAuthentication,
handleCodingPlanSubmit,
handleAlibabaStandardSubmit,
handleEditorSelect,
exitEditorDialog,
closeSettingsDialog,
@ -1748,6 +1750,7 @@ export const AppContainer = (props: AppContainerProps) => {
onAuthError,
cancelAuthentication,
handleCodingPlanSubmit,
handleAlibabaStandardSubmit,
handleEditorSelect,
exitEditorDialog,
closeSettingsDialog,

View file

@ -32,6 +32,9 @@ const createMockUIActions = (overrides: Partial<UIActions> = {}): UIActions => {
// AuthDialog only uses handleAuthSelect
const baseActions = {
handleAuthSelect: vi.fn(),
handleCodingPlanSubmit: vi.fn(),
handleAlibabaStandardSubmit: vi.fn(),
onAuthError: vi.fn(),
handleRetryLastPrompt: vi.fn(),
} as Partial<UIActions>;
@ -555,4 +558,121 @@ describe('AuthDialog', () => {
expect(handleAuthSelect).toHaveBeenCalledWith(undefined);
unmount();
});
it('shows API Key subtype menu and opens custom info', async () => {
const settings: LoadedSettings = new LoadedSettings(
{
settings: { ui: { customThemes: {} }, mcpServers: {} },
originalSettings: { ui: { customThemes: {} }, mcpServers: {} },
path: '',
},
{
settings: {},
originalSettings: {},
path: '',
},
{
settings: {
security: { auth: { selectedType: undefined } },
ui: { customThemes: {} },
mcpServers: {},
},
originalSettings: {
security: { auth: { selectedType: undefined } },
ui: { customThemes: {} },
mcpServers: {},
},
path: '',
},
{
settings: { ui: { customThemes: {} }, mcpServers: {} },
originalSettings: { ui: { customThemes: {} }, mcpServers: {} },
path: '',
},
true,
new Set(),
);
const { stdin, lastFrame, unmount } = renderAuthDialog(settings);
await wait();
// Move from Qwen OAuth -> Coding Plan -> API Key, then enter
stdin.write('\u001B[B');
stdin.write('\u001B[B');
stdin.write('\r');
await wait();
expect(lastFrame()).toContain('Select API Key Type');
expect(lastFrame()).toContain('Alibaba Cloud ModelStudio Standard API Key');
expect(lastFrame()).toContain('Custom API Key');
// Move to Custom API Key and enter
stdin.write('\u001B[B');
stdin.write('\r');
await wait();
expect(lastFrame()).toContain('Custom Configuration');
unmount();
});
it('shows Alibaba Cloud ModelStudio Standard API Key region endpoint', async () => {
const settings: LoadedSettings = new LoadedSettings(
{
settings: { ui: { customThemes: {} }, mcpServers: {} },
originalSettings: { ui: { customThemes: {} }, mcpServers: {} },
path: '',
},
{
settings: {},
originalSettings: {},
path: '',
},
{
settings: {
security: { auth: { selectedType: undefined } },
ui: { customThemes: {} },
mcpServers: {},
},
originalSettings: {
security: { auth: { selectedType: undefined } },
ui: { customThemes: {} },
mcpServers: {},
},
path: '',
},
{
settings: { ui: { customThemes: {} }, mcpServers: {} },
originalSettings: { ui: { customThemes: {} }, mcpServers: {} },
path: '',
},
true,
new Set(),
);
const { stdin, lastFrame, unmount } = renderAuthDialog(settings, {}, {});
await wait();
// Main -> API Key
stdin.write('\u001B[B');
stdin.write('\u001B[B');
stdin.write('\r');
await wait();
// API Key type -> Alibaba Cloud ModelStudio Standard API Key (default)
stdin.write('\r');
await wait();
// Region -> Singapore
stdin.write('\u001B[B');
stdin.write('\r');
await wait();
expect(lastFrame()).toContain(
'Enter Alibaba Cloud ModelStudio Standard API Key',
);
expect(lastFrame()).toContain(
'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',
);
unmount();
});
});

View file

@ -13,6 +13,7 @@ import { theme } from '../semantic-colors.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { DescriptiveRadioButtonSelect } from '../components/shared/DescriptiveRadioButtonSelect.js';
import { ApiKeyInput } from '../components/ApiKeyInput.js';
import { TextInput } from '../components/shared/TextInput.js';
import { useUIState } from '../contexts/UIStateContext.js';
import { useUIActions } from '../contexts/UIActionsContext.js';
import { useConfig } from '../contexts/ConfigContext.js';
@ -21,6 +22,10 @@ import {
CodingPlanRegion,
isCodingPlanConfig,
} from '../../constants/codingPlan.js';
import {
ALIBABA_STANDARD_API_KEY_ENDPOINTS,
type AlibabaStandardRegion,
} from '../../constants/alibabaStandardApiKey.js';
const MODEL_PROVIDERS_DOCUMENTATION_URL =
'https://qwenlm.github.io/qwen-code-docs/en/users/configuration/model-providers/';
@ -39,15 +44,39 @@ function parseDefaultAuthType(
// Main menu option type
type MainOption = typeof AuthType.QWEN_OAUTH | 'CODING_PLAN' | 'API_KEY';
type ApiKeyOption = 'ALIBABA_STANDARD_API_KEY' | 'CUSTOM_API_KEY';
// View level for navigation
type ViewLevel = 'main' | 'region-select' | 'api-key-input' | 'custom-info';
type ViewLevel =
| 'main'
| 'region-select'
| 'api-key-input'
| 'api-key-type-select'
| 'alibaba-standard-region-select'
| 'alibaba-standard-api-key-input'
| 'alibaba-standard-model-id-input'
| 'custom-info';
const ALIBABA_STANDARD_MODEL_IDS_PLACEHOLDER = 'qwen3.5-plus,glm-5,kimi-k2.5';
const ALIBABA_STANDARD_API_DOCUMENTATION_URLS: Record<
AlibabaStandardRegion,
string
> = {
'cn-beijing': 'https://bailian.console.aliyun.com/cn-beijing?tab=api#/api',
'sg-singapore':
'https://modelstudio.console.alibabacloud.com/ap-southeast-1?tab=api#/api/?type=model&url=2712195',
'us-virginia':
'https://modelstudio.console.alibabacloud.com/us-east-1?tab=api#/api/?type=model&url=2712195',
'cn-hongkong':
'https://modelstudio.console.alibabacloud.com/cn-hongkong?tab=api#/api/?type=model&url=2712195',
};
export function AuthDialog(): React.JSX.Element {
const { pendingAuthType, authError } = useUIState();
const {
handleAuthSelect: onAuthSelect,
handleCodingPlanSubmit,
handleAlibabaStandardSubmit,
onAuthError,
} = useUIActions();
const config = useConfig();
@ -58,6 +87,18 @@ export function AuthDialog(): React.JSX.Element {
const [region, setRegion] = useState<CodingPlanRegion>(
CodingPlanRegion.CHINA,
);
const [alibabaStandardRegionIndex, setAlibabaStandardRegionIndex] =
useState<number>(0);
const [apiKeyTypeIndex, setApiKeyTypeIndex] = useState<number>(0);
const [alibabaStandardRegion, setAlibabaStandardRegion] =
useState<AlibabaStandardRegion>('cn-beijing');
const [alibabaStandardApiKey, setAlibabaStandardApiKey] = useState('');
const [alibabaStandardApiKeyError, setAlibabaStandardApiKeyError] = useState<
string | null
>(null);
const [alibabaStandardModelId, setAlibabaStandardModelId] = useState('');
const [alibabaStandardModelIdError, setAlibabaStandardModelIdError] =
useState<string | null>(null);
// Main authentication entries (flat three-option layout)
const mainItems = [
@ -124,21 +165,87 @@ export function AuthDialog(): React.JSX.Element {
},
];
const alibabaStandardRegionItems = [
{
key: 'cn-beijing',
title: t('China (Beijing)'),
label: t('China (Beijing)'),
description: (
<Text color={theme.text.secondary}>
Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS['cn-beijing']}
</Text>
),
value: 'cn-beijing' as AlibabaStandardRegion,
},
{
key: 'sg-singapore',
title: t('Singapore'),
label: t('Singapore'),
description: (
<Text color={theme.text.secondary}>
Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS['sg-singapore']}
</Text>
),
value: 'sg-singapore' as AlibabaStandardRegion,
},
{
key: 'us-virginia',
title: t('US (Virginia)'),
label: t('US (Virginia)'),
description: (
<Text color={theme.text.secondary}>
Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS['us-virginia']}
</Text>
),
value: 'us-virginia' as AlibabaStandardRegion,
},
{
key: 'cn-hongkong',
title: t('China (Hong Kong)'),
label: t('China (Hong Kong)'),
description: (
<Text color={theme.text.secondary}>
Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS['cn-hongkong']}
</Text>
),
value: 'cn-hongkong' as AlibabaStandardRegion,
},
];
const apiKeyTypeItems = [
{
key: 'ALIBABA_STANDARD_API_KEY',
title: t('Alibaba Cloud ModelStudio Standard API Key'),
label: t('Alibaba Cloud ModelStudio Standard API Key'),
description: t('Quick setup for Model Studio (China/International)'),
value: 'ALIBABA_STANDARD_API_KEY' as ApiKeyOption,
},
{
key: 'CUSTOM_API_KEY',
title: t('Custom API Key'),
label: t('Custom API Key'),
description: t(
'For other OpenAI / Anthropic / Gemini-compatible providers',
),
value: 'CUSTOM_API_KEY' as ApiKeyOption,
},
];
// Map an AuthType to the corresponding main menu option.
// QWEN_OAUTH maps directly; any other auth type maps to CODING_PLAN only
// if the current config actually uses a Coding Plan baseUrl+envKey,
// otherwise it maps to API_KEY.
// QWEN_OAUTH maps directly; USE_OPENAI maps to:
// - CODING_PLAN when current config matches coding plan
// - API_KEY for other OpenAI / Anthropic / Gemini-compatible configs
const contentGenConfig = config.getContentGeneratorConfig();
const isCurrentlyCodingPlan =
isCodingPlanConfig(
contentGenConfig?.baseUrl,
contentGenConfig?.apiKeyEnvKey,
) !== false;
const authTypeToMainOption = (authType: AuthType): MainOption => {
if (authType === AuthType.QWEN_OAUTH) return AuthType.QWEN_OAUTH;
if (authType === AuthType.USE_OPENAI && isCurrentlyCodingPlan)
if (authType === AuthType.USE_OPENAI && isCurrentlyCodingPlan) {
return 'CODING_PLAN';
}
return 'API_KEY';
};
@ -180,8 +287,7 @@ export function AuthDialog(): React.JSX.Element {
}
if (value === 'API_KEY') {
// Navigate directly to custom API key info
setViewLevel('custom-info');
setViewLevel('api-key-type-select');
return;
}
@ -189,6 +295,20 @@ export function AuthDialog(): React.JSX.Element {
await onAuthSelect(value);
};
const handleApiKeyTypeSelect = async (value: ApiKeyOption) => {
setErrorMessage(null);
onAuthError(null);
if (value === 'ALIBABA_STANDARD_API_KEY') {
setAlibabaStandardModelIdError(null);
setAlibabaStandardApiKeyError(null);
setViewLevel('alibaba-standard-region-select');
return;
}
setViewLevel('custom-info');
};
const handleRegionSelect = async (selectedRegion: CodingPlanRegion) => {
setErrorMessage(null);
onAuthError(null);
@ -196,6 +316,17 @@ export function AuthDialog(): React.JSX.Element {
setViewLevel('api-key-input');
};
const handleAlibabaStandardRegionSelect = async (
selectedRegion: AlibabaStandardRegion,
) => {
setErrorMessage(null);
onAuthError(null);
setAlibabaStandardApiKeyError(null);
setAlibabaStandardModelIdError(null);
setAlibabaStandardRegion(selectedRegion);
setViewLevel('alibaba-standard-api-key-input');
};
const handleApiKeyInputSubmit = async (apiKey: string) => {
setErrorMessage(null);
@ -208,14 +339,59 @@ export function AuthDialog(): React.JSX.Element {
await handleCodingPlanSubmit(apiKey, region);
};
const handleAlibabaStandardApiKeySubmit = () => {
const trimmedKey = alibabaStandardApiKey.trim();
if (!trimmedKey) {
setAlibabaStandardApiKeyError(t('API key cannot be empty.'));
return;
}
setAlibabaStandardApiKeyError(null);
if (!alibabaStandardModelId.trim()) {
setAlibabaStandardModelId(ALIBABA_STANDARD_MODEL_IDS_PLACEHOLDER);
}
setViewLevel('alibaba-standard-model-id-input');
};
const handleAlibabaStandardModelSubmit = () => {
const trimmedApiKey = alibabaStandardApiKey.trim();
const trimmedModelIds = alibabaStandardModelId.trim();
if (!trimmedApiKey) {
setAlibabaStandardApiKeyError(t('API key cannot be empty.'));
setViewLevel('alibaba-standard-api-key-input');
return;
}
if (!trimmedModelIds) {
setAlibabaStandardModelIdError(t('Model IDs cannot be empty.'));
return;
}
setAlibabaStandardModelIdError(null);
void handleAlibabaStandardSubmit(
trimmedApiKey,
alibabaStandardRegion,
trimmedModelIds,
);
};
const handleGoBack = () => {
setErrorMessage(null);
onAuthError(null);
if (viewLevel === 'region-select' || viewLevel === 'custom-info') {
if (viewLevel === 'region-select') {
setViewLevel('main');
} else if (viewLevel === 'api-key-input') {
setViewLevel('region-select');
} else if (viewLevel === 'api-key-type-select') {
setViewLevel('main');
} else if (viewLevel === 'custom-info') {
setViewLevel('api-key-type-select');
} else if (viewLevel === 'alibaba-standard-region-select') {
setViewLevel('api-key-type-select');
} else if (viewLevel === 'alibaba-standard-api-key-input') {
setViewLevel('alibaba-standard-region-select');
} else if (viewLevel === 'alibaba-standard-model-id-input') {
setViewLevel('alibaba-standard-api-key-input');
}
};
@ -232,6 +408,15 @@ export function AuthDialog(): React.JSX.Element {
handleGoBack();
return;
}
if (
viewLevel === 'api-key-type-select' ||
viewLevel === 'alibaba-standard-region-select' ||
viewLevel === 'alibaba-standard-api-key-input' ||
viewLevel === 'alibaba-standard-model-id-input'
) {
handleGoBack();
return;
}
// For main view, use existing logic
if (errorMessage) {
@ -304,6 +489,135 @@ export function AuthDialog(): React.JSX.Element {
</Box>
);
const renderApiKeyTypeSelectView = () => (
<>
<Box marginTop={1}>
<DescriptiveRadioButtonSelect
items={apiKeyTypeItems}
initialIndex={apiKeyTypeIndex}
onSelect={handleApiKeyTypeSelect}
onHighlight={(value) => {
const index = apiKeyTypeItems.findIndex(
(item) => item.value === value,
);
setApiKeyTypeIndex(index);
}}
itemGap={1}
/>
</Box>
<Box marginTop={1}>
<Text color={theme?.text?.secondary}>
{t('Enter to select, ↑↓ to navigate, Esc to go back')}
</Text>
</Box>
</>
);
const renderAlibabaStandardRegionSelectView = () => (
<>
<Box marginTop={1}>
<DescriptiveRadioButtonSelect
items={alibabaStandardRegionItems}
initialIndex={alibabaStandardRegionIndex}
onSelect={handleAlibabaStandardRegionSelect}
onHighlight={(value) => {
const index = alibabaStandardRegionItems.findIndex(
(item) => item.value === value,
);
setAlibabaStandardRegionIndex(index);
}}
itemGap={1}
/>
</Box>
<Box marginTop={1}>
<Text color={theme?.text?.secondary}>
{t('Enter to select, ↑↓ to navigate, Esc to go back')}
</Text>
</Box>
</>
);
const renderAlibabaStandardApiKeyInputView = () => (
<Box marginTop={1} flexDirection="column">
<Box marginTop={1}>
<Text color={theme.text.secondary}>
Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS[alibabaStandardRegion]}
</Text>
</Box>
<Box marginTop={1}>
<Text color={theme.text.secondary}>{t('Documentation')}:</Text>
</Box>
<Box marginTop={0}>
<Link
url={ALIBABA_STANDARD_API_DOCUMENTATION_URLS[alibabaStandardRegion]}
fallback={false}
>
<Text color={theme.text.link}>
{ALIBABA_STANDARD_API_DOCUMENTATION_URLS[alibabaStandardRegion]}
</Text>
</Link>
</Box>
<Box marginTop={1}>
<TextInput
value={alibabaStandardApiKey}
onChange={(value) => {
setAlibabaStandardApiKey(value);
if (alibabaStandardApiKeyError) {
setAlibabaStandardApiKeyError(null);
}
}}
onSubmit={handleAlibabaStandardApiKeySubmit}
placeholder="sk-..."
/>
</Box>
{alibabaStandardApiKeyError && (
<Box marginTop={1}>
<Text color={theme.status.error}>{alibabaStandardApiKeyError}</Text>
</Box>
)}
<Box marginTop={1}>
<Text color={theme.text.secondary}>
{t('Enter to submit, Esc to go back')}
</Text>
</Box>
</Box>
);
const renderAlibabaStandardModelIdInputView = () => (
<Box marginTop={1} flexDirection="column">
<Box marginTop={1}>
<Text color={theme.text.secondary}>
{t(
'You can enter multiple model IDs, separated by commas. Examples: qwen3.5-plus,glm-5,kimi-k2.5',
)}
</Text>
</Box>
<Box marginTop={1}>
<TextInput
value={alibabaStandardModelId}
onChange={(value) => {
setAlibabaStandardModelId(value);
if (alibabaStandardModelIdError) {
setAlibabaStandardModelIdError(null);
}
}}
onSubmit={handleAlibabaStandardModelSubmit}
placeholder={ALIBABA_STANDARD_MODEL_IDS_PLACEHOLDER}
/>
</Box>
{alibabaStandardModelIdError && (
<Box marginTop={1}>
<Text color={theme.status.error}>{alibabaStandardModelIdError}</Text>
</Box>
)}
<Box marginTop={1}>
<Text color={theme.text.secondary}>
{t('Enter to submit, Esc to go back')}
</Text>
</Box>
</Box>
);
// Render custom mode info
const renderCustomInfoView = () => (
<>
@ -336,8 +650,18 @@ export function AuthDialog(): React.JSX.Element {
return t('Select Region for Coding Plan');
case 'api-key-input':
return t('Enter Coding Plan API Key');
case 'api-key-type-select':
return t('Select API Key Type');
case 'custom-info':
return t('Custom Configuration');
case 'alibaba-standard-region-select':
return t(
'Select Region for Alibaba Cloud ModelStudio Standard API Key',
);
case 'alibaba-standard-api-key-input':
return t('Enter Alibaba Cloud ModelStudio Standard API Key');
case 'alibaba-standard-model-id-input':
return t('Enter Model IDs');
default:
return t('Select Authentication Method');
}
@ -356,6 +680,13 @@ export function AuthDialog(): React.JSX.Element {
{viewLevel === 'main' && renderMainView()}
{viewLevel === 'region-select' && renderRegionSelectView()}
{viewLevel === 'api-key-input' && renderApiKeyInputView()}
{viewLevel === 'api-key-type-select' && renderApiKeyTypeSelectView()}
{viewLevel === 'alibaba-standard-region-select' &&
renderAlibabaStandardRegionSelectView()}
{viewLevel === 'alibaba-standard-api-key-input' &&
renderAlibabaStandardApiKeyInputView()}
{viewLevel === 'alibaba-standard-model-id-input' &&
renderAlibabaStandardModelIdInputView()}
{viewLevel === 'custom-info' && renderCustomInfoView()}
{(authError || errorMessage) && (

View file

@ -36,6 +36,11 @@ import {
CODING_PLAN_ENV_KEY,
} from '../../constants/codingPlan.js';
import { backupSettingsFile } from '../../utils/settingsUtils.js';
import {
ALIBABA_STANDARD_API_KEY_ENDPOINTS,
DASHSCOPE_STANDARD_API_KEY_ENV_KEY,
type AlibabaStandardRegion,
} from '../../constants/alibabaStandardApiKey.js';
export type { QwenAuthState } from '../hooks/useQwenAuth.js';
@ -421,6 +426,134 @@ export const useAuthCommand = (
[settings, config, handleAuthFailure, addItem, onAuthChange],
);
/**
* Handle Alibaba Cloud standard API key flow.
* Persists key to env.DASHSCOPE_API_KEY and creates a modelProviders.openai entry.
*/
const handleAlibabaStandardSubmit = useCallback(
async (
apiKey: string,
region: AlibabaStandardRegion,
modelIdsInput: string,
) => {
try {
setIsAuthenticating(true);
setAuthError(null);
const trimmedApiKey = apiKey.trim();
const modelIds = modelIdsInput
.split(',')
.map((id) => id.trim())
.filter(
(id, index, array) => id.length > 0 && array.indexOf(id) === index,
);
if (!trimmedApiKey) {
throw new Error(t('API key cannot be empty.'));
}
if (modelIds.length === 0) {
throw new Error(t('Model IDs cannot be empty.'));
}
const baseUrl = ALIBABA_STANDARD_API_KEY_ENDPOINTS[region];
const persistScope = getPersistScopeForModelSelection(settings);
const settingsFile = settings.forScope(persistScope);
backupSettingsFile(settingsFile.path);
settings.setValue(
persistScope,
`env.${DASHSCOPE_STANDARD_API_KEY_ENV_KEY}`,
trimmedApiKey,
);
process.env[DASHSCOPE_STANDARD_API_KEY_ENV_KEY] = trimmedApiKey;
const newConfigs: ProviderModelConfig[] = modelIds.map((modelId) => ({
id: modelId,
name: `[ModelStudio Standard] ${modelId}`,
baseUrl,
envKey: DASHSCOPE_STANDARD_API_KEY_ENV_KEY,
}));
const existingConfigs =
(
settings.merged.modelProviders as ModelProvidersConfig | undefined
)?.[AuthType.USE_OPENAI] || [];
const nonAlibabaStandardConfigs = existingConfigs.filter(
(existing) =>
!(
existing.envKey === DASHSCOPE_STANDARD_API_KEY_ENV_KEY &&
typeof existing.baseUrl === 'string' &&
Object.values(ALIBABA_STANDARD_API_KEY_ENDPOINTS).includes(
existing.baseUrl,
)
),
);
const updatedConfigs = [...newConfigs, ...nonAlibabaStandardConfigs];
settings.setValue(
persistScope,
`modelProviders.${AuthType.USE_OPENAI}`,
updatedConfigs,
);
settings.setValue(
persistScope,
'security.auth.selectedType',
AuthType.USE_OPENAI,
);
settings.setValue(persistScope, 'model.name', modelIds[0]);
const updatedModelProviders: ModelProvidersConfig = {
...(settings.merged.modelProviders as
| ModelProvidersConfig
| undefined),
[AuthType.USE_OPENAI]: updatedConfigs,
};
config.reloadModelProvidersConfig(updatedModelProviders);
await config.refreshAuth(AuthType.USE_OPENAI);
setAuthError(null);
setAuthState(AuthState.Authenticated);
setPendingAuthType(undefined);
setIsAuthDialogOpen(false);
setIsAuthenticating(false);
onAuthChange?.();
addItem(
{
type: MessageType.INFO,
text: t(
'Alibaba Cloud ModelStudio Standard API Key successfully entered. Settings updated with env.DASHSCOPE_API_KEY and {{modelCount}} model(s).',
{ modelCount: String(modelIds.length) },
),
},
Date.now(),
);
addItem(
{
type: MessageType.INFO,
text: t(
'You can use /model to see new ModelStudio Standard models and switch between them.',
),
},
Date.now(),
);
const authEvent = new AuthEvent(
AuthType.USE_OPENAI,
'manual',
'success',
);
logAuth(config, authEvent);
} catch (error) {
handleAuthFailure(error);
}
},
[settings, config, handleAuthFailure, addItem, onAuthChange],
);
/**
/**
* We previously used a useEffect to trigger authentication automatically when
@ -472,6 +605,7 @@ export const useAuthCommand = (
qwenAuthState,
handleAuthSelect,
handleCodingPlanSubmit,
handleAlibabaStandardSubmit,
openAuthDialog,
cancelAuthentication,
};

View file

@ -16,6 +16,7 @@ import {
} from '@qwen-code/qwen-code-core';
import { type SettingScope } from '../../config/settings.js';
import { type CodingPlanRegion } from '../../constants/codingPlan.js';
import { type AlibabaStandardRegion } from '../../constants/alibabaStandardApiKey.js';
import type { AuthState } from '../types.js';
import { type ArenaDialogType } from '../hooks/useArenaCommand.js';
// OpenAICredentials type (previously imported from OpenAIKeyPrompt)
@ -45,6 +46,11 @@ export interface UIActions {
apiKey: string,
region?: CodingPlanRegion,
) => Promise<void>;
handleAlibabaStandardSubmit: (
apiKey: string,
region: AlibabaStandardRegion,
modelIdsInput: string,
) => Promise<void>;
setAuthState: (state: AuthState) => void;
onAuthError: (error: string | null) => void;
cancelAuthentication: () => void;