Merge pull request #14 from Askhz/refactor/first-login

refactor: remove OAuth/preflight from onboarding and fix model picker reload after CoStrict login
This commit is contained in:
geroge 2026-04-10 09:48:50 +08:00 committed by GitHub
commit 1f68cd74e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 15 additions and 49 deletions

View File

@ -32,7 +32,7 @@ export async function performLogout({ clearOnboarding = false }): Promise<void>
await deleteCoStrictCredentials();
clearModelCache();
delete process.env.CLAUDE_CODE_USE_COSTRICT;
updateSettingsForSource('userSettings', { modelType: 'anthropic' as any });
updateSettingsForSource('userSettings', { modelType: undefined, model: undefined } as any);
await clearAuthRelatedCaches();
saveGlobalConfig(current => {

View File

@ -47,6 +47,7 @@ function ModelPickerWrapper({
const mainLoopModelForSession = useAppState(s => s.mainLoopModelForSession);
const isFastMode = useAppState(s => s.fastMode);
const setAppState = useSetAppState();
const [pickerKey, setPickerKey] = React.useState(0);
function handleCancel(): void {
logEvent('tengu_model_command_menu', {
@ -69,11 +70,9 @@ function ModelPickerWrapper({
const machineId = generateMachineId();
const loginUrl = buildCoStrictLoginURL(baseUrl, state, machineId);
// 打开浏览器
// 打开浏览器(不调用 onDone保持 picker 显示)
await openBrowser(loginUrl);
onDone('Opening browser for CoStrict login...', { display: 'system' });
// 轮询等待登录完成
const tokens = await pollLoginToken(baseUrl, state, machineId);
@ -96,22 +95,16 @@ function ModelPickerWrapper({
updateSettingsForSource('userSettings', { modelType: 'costrict' as any } as any);
process.env.CLAUDE_CODE_USE_COSTRICT = '1';
// 预取模型列表
// 预取并缓存模型列表
try {
const { fetchCoStrictModels } = await import('../../costrict/provider/models.js');
const models = await fetchCoStrictModels(baseUrl, tokens.access_token);
if (models.length > 0) {
// 登录成功,刷新模型列表
onDone(`Successfully logged in to CoStrict! Found ${models.length} available models.`, {
display: 'system',
});
return;
}
await fetchCoStrictModels(baseUrl, tokens.access_token);
} catch {
// 预取失败
// 预取失败picker 重载后会显示默认模型列表
}
onDone('Successfully logged in to CoStrict!', { display: 'system' });
// 登录成功后重载 ModelPicker显示 CoStrict 模型选择界面
setPickerKey(k => k + 1);
} catch (err: any) {
onDone(`Login failed: ${err.message || String(err)}`, { display: 'system' });
}
@ -166,6 +159,7 @@ function ModelPickerWrapper({
return (
<ModelPicker
key={pickerKey}
initial={mainLoopModel}
sessionModel={mainLoopModelForSession}
onSelect={handleSelect}

View File

@ -10,15 +10,12 @@ import {
import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js'
import { Box, Link, Newline, Text, useTheme } from '@anthropic/ink'
import { useKeybindings } from '../keybindings/useKeybinding.js'
import { isAnthropicAuthEnabled } from '../utils/auth.js'
import { normalizeApiKeyForConfig } from '../utils/authPortable.js'
import { getCustomApiKeyStatus } from '../utils/config.js'
import { env } from '../utils/env.js'
import { isRunningOnHomespace } from '../utils/envUtils.js'
import { PreflightStep } from '../utils/preflightChecks.js'
import type { ThemeSetting } from '../utils/theme.js'
import { ApproveApiKey } from './ApproveApiKey.js'
import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js'
import { Select } from './CustomSelect/select.js'
import { WelcomeV2 } from './LogoV2/WelcomeV2.js'
import { PressEnterToContinue } from './PressEnterToContinue.js'
@ -26,9 +23,7 @@ import { ThemePicker } from './ThemePicker.js'
import { OrderedList } from './ui/OrderedList.js'
type StepId =
| 'preflight'
| 'theme'
| 'oauth'
| 'api-key'
| 'security'
| 'terminal-setup'
@ -44,15 +39,11 @@ type Props = {
export function Onboarding({ onDone }: Props): React.ReactNode {
const [currentStepIndex, setCurrentStepIndex] = useState(0)
const [skipOAuth, setSkipOAuth] = useState(false)
const [oauthEnabled] = useState(() => isAnthropicAuthEnabled())
const [theme, setTheme] = useTheme()
useEffect(() => {
logEvent('tengu_began_setup', {
oauthEnabled,
})
}, [oauthEnabled])
logEvent('tengu_began_setup', {})
}, [])
function goToNextStep() {
if (currentStepIndex < steps.length - 1) {
@ -60,7 +51,6 @@ export function Onboarding({ onDone }: Props): React.ReactNode {
setCurrentStepIndex(nextIndex)
logEvent('tengu_onboarding_step', {
oauthEnabled,
stepId: steps[nextIndex]
?.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
})
@ -123,8 +113,7 @@ export function Onboarding({ onDone }: Props): React.ReactNode {
</Box>
)
const preflightStep = <PreflightStep onSuccess={goToNextStep} />
// Create the steps array - determine which steps to include based on reAuth and oauthEnabled
// Create the steps array
const apiKeyNeedingApproval = useMemo(() => {
// Add API key step if needed
// On homespace, ANTHROPIC_API_KEY is preserved in process.env for child
@ -140,17 +129,11 @@ export function Onboarding({ onDone }: Props): React.ReactNode {
}
}, [])
function handleApiKeyDone(approved: boolean) {
if (approved) {
setSkipOAuth(true)
}
function handleApiKeyDone(_approved: boolean) {
goToNextStep()
}
const steps: OnboardingStep[] = []
if (oauthEnabled) {
steps.push({ id: 'preflight', component: preflightStep })
}
steps.push({ id: 'theme', component: themeStep })
if (apiKeyNeedingApproval) {
@ -165,17 +148,6 @@ export function Onboarding({ onDone }: Props): React.ReactNode {
})
}
if (oauthEnabled) {
steps.push({
id: 'oauth',
component: (
<SkippableStep skip={skipOAuth} onSkip={goToNextStep}>
<ConsoleOAuthFlow onDone={goToNextStep} />
</SkippableStep>
),
})
}
steps.push({ id: 'security', component: securityStep })
if (shouldOfferTerminalSetup()) {
@ -239,11 +211,11 @@ export function Onboarding({ onDone }: Props): React.ReactNode {
} else {
goToNextStep()
}
}, [currentStepIndex, steps.length, oauthEnabled, onDone])
}, [currentStepIndex, steps.length, onDone])
const handleTerminalSetupSkip = useCallback(() => {
goToNextStep()
}, [currentStepIndex, steps.length, oauthEnabled, onDone])
}, [currentStepIndex, steps.length, onDone])
useKeybindings(
{