mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 16:57:46 +00:00
pre-release commit
This commit is contained in:
171
packages/cli/src/ui/components/AuthDialog.tsx
Normal file
171
packages/cli/src/ui/components/AuthDialog.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Text, useInput } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||
import { LoadedSettings, SettingScope } from '../../config/settings.js';
|
||||
import { AuthType } from '@qwen/qwen-code-core';
|
||||
import {
|
||||
validateAuthMethod,
|
||||
setOpenAIApiKey,
|
||||
setOpenAIBaseUrl,
|
||||
setOpenAIModel,
|
||||
} from '../../config/auth.js';
|
||||
import { OpenAIKeyPrompt } from './OpenAIKeyPrompt.js';
|
||||
|
||||
interface AuthDialogProps {
|
||||
onSelect: (authMethod: AuthType | undefined, scope: SettingScope) => void;
|
||||
settings: LoadedSettings;
|
||||
initialErrorMessage?: string | null;
|
||||
}
|
||||
|
||||
function parseDefaultAuthType(
|
||||
defaultAuthType: string | undefined,
|
||||
): AuthType | null {
|
||||
if (
|
||||
defaultAuthType &&
|
||||
Object.values(AuthType).includes(defaultAuthType as AuthType)
|
||||
) {
|
||||
return defaultAuthType as AuthType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function AuthDialog({
|
||||
onSelect,
|
||||
settings,
|
||||
initialErrorMessage,
|
||||
}: AuthDialogProps): React.JSX.Element {
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(
|
||||
initialErrorMessage || null,
|
||||
);
|
||||
const [showOpenAIKeyPrompt, setShowOpenAIKeyPrompt] = useState(false);
|
||||
const items = [{ label: 'OpenAI', value: AuthType.USE_OPENAI }];
|
||||
|
||||
const initialAuthIndex = items.findIndex((item) => {
|
||||
if (settings.merged.selectedAuthType) {
|
||||
return item.value === settings.merged.selectedAuthType;
|
||||
}
|
||||
|
||||
const defaultAuthType = parseDefaultAuthType(
|
||||
process.env.GEMINI_DEFAULT_AUTH_TYPE,
|
||||
);
|
||||
if (defaultAuthType) {
|
||||
return item.value === defaultAuthType;
|
||||
}
|
||||
|
||||
if (process.env.GEMINI_API_KEY) {
|
||||
return item.value === AuthType.USE_GEMINI;
|
||||
}
|
||||
|
||||
return item.value === AuthType.LOGIN_WITH_GOOGLE;
|
||||
});
|
||||
|
||||
const handleAuthSelect = (authMethod: AuthType) => {
|
||||
const error = validateAuthMethod(authMethod);
|
||||
if (error) {
|
||||
if (authMethod === AuthType.USE_OPENAI && !process.env.OPENAI_API_KEY) {
|
||||
setShowOpenAIKeyPrompt(true);
|
||||
setErrorMessage(null);
|
||||
} else {
|
||||
setErrorMessage(error);
|
||||
}
|
||||
} else {
|
||||
setErrorMessage(null);
|
||||
onSelect(authMethod, SettingScope.User);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenAIKeySubmit = (
|
||||
apiKey: string,
|
||||
baseUrl: string,
|
||||
model: string,
|
||||
) => {
|
||||
setOpenAIApiKey(apiKey);
|
||||
setOpenAIBaseUrl(baseUrl);
|
||||
setOpenAIModel(model);
|
||||
setShowOpenAIKeyPrompt(false);
|
||||
onSelect(AuthType.USE_OPENAI, SettingScope.User);
|
||||
};
|
||||
|
||||
const handleOpenAIKeyCancel = () => {
|
||||
setShowOpenAIKeyPrompt(false);
|
||||
setErrorMessage('OpenAI API key is required to use OpenAI authentication.');
|
||||
};
|
||||
|
||||
useInput((_input, key) => {
|
||||
// 当显示 OpenAIKeyPrompt 时,不处理输入事件
|
||||
if (showOpenAIKeyPrompt) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.escape) {
|
||||
// Prevent exit if there is an error message.
|
||||
// This means they user is not authenticated yet.
|
||||
if (errorMessage) {
|
||||
return;
|
||||
}
|
||||
if (settings.merged.selectedAuthType === undefined) {
|
||||
// Prevent exiting if no auth method is set
|
||||
setErrorMessage(
|
||||
'You must select an auth method to proceed. Press Ctrl+C twice to exit.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
onSelect(undefined, SettingScope.User);
|
||||
}
|
||||
});
|
||||
|
||||
if (showOpenAIKeyPrompt) {
|
||||
return (
|
||||
<OpenAIKeyPrompt
|
||||
onSubmit={handleOpenAIKeySubmit}
|
||||
onCancel={handleOpenAIKeyCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderColor={Colors.Gray}
|
||||
flexDirection="column"
|
||||
padding={1}
|
||||
width="100%"
|
||||
>
|
||||
<Text bold>Get started</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text>How would you like to authenticate for this project?</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<RadioButtonSelect
|
||||
items={items}
|
||||
initialIndex={initialAuthIndex}
|
||||
onSelect={handleAuthSelect}
|
||||
isFocused={true}
|
||||
/>
|
||||
</Box>
|
||||
{errorMessage && (
|
||||
<Box marginTop={1}>
|
||||
<Text color={Colors.AccentRed}>{errorMessage}</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box marginTop={1}>
|
||||
<Text color={Colors.AccentPurple}>(Use Enter to Set Auth)</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text>Terms of Services and Privacy Notice for Qwen Code</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text color={Colors.AccentBlue}>
|
||||
{'https://github.com/QwenLM/Qwen3-Coder/blob/main/README.md'}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user