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:
commit
1f68cd74e3
|
|
@ -32,7 +32,7 @@ export async function performLogout({ clearOnboarding = false }): Promise<void>
|
||||||
await deleteCoStrictCredentials();
|
await deleteCoStrictCredentials();
|
||||||
clearModelCache();
|
clearModelCache();
|
||||||
delete process.env.CLAUDE_CODE_USE_COSTRICT;
|
delete process.env.CLAUDE_CODE_USE_COSTRICT;
|
||||||
updateSettingsForSource('userSettings', { modelType: 'anthropic' as any });
|
updateSettingsForSource('userSettings', { modelType: undefined, model: undefined } as any);
|
||||||
|
|
||||||
await clearAuthRelatedCaches();
|
await clearAuthRelatedCaches();
|
||||||
saveGlobalConfig(current => {
|
saveGlobalConfig(current => {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ function ModelPickerWrapper({
|
||||||
const mainLoopModelForSession = useAppState(s => s.mainLoopModelForSession);
|
const mainLoopModelForSession = useAppState(s => s.mainLoopModelForSession);
|
||||||
const isFastMode = useAppState(s => s.fastMode);
|
const isFastMode = useAppState(s => s.fastMode);
|
||||||
const setAppState = useSetAppState();
|
const setAppState = useSetAppState();
|
||||||
|
const [pickerKey, setPickerKey] = React.useState(0);
|
||||||
|
|
||||||
function handleCancel(): void {
|
function handleCancel(): void {
|
||||||
logEvent('tengu_model_command_menu', {
|
logEvent('tengu_model_command_menu', {
|
||||||
|
|
@ -69,11 +70,9 @@ function ModelPickerWrapper({
|
||||||
const machineId = generateMachineId();
|
const machineId = generateMachineId();
|
||||||
const loginUrl = buildCoStrictLoginURL(baseUrl, state, machineId);
|
const loginUrl = buildCoStrictLoginURL(baseUrl, state, machineId);
|
||||||
|
|
||||||
// 打开浏览器
|
// 打开浏览器(不调用 onDone,保持 picker 显示)
|
||||||
await openBrowser(loginUrl);
|
await openBrowser(loginUrl);
|
||||||
|
|
||||||
onDone('Opening browser for CoStrict login...', { display: 'system' });
|
|
||||||
|
|
||||||
// 轮询等待登录完成
|
// 轮询等待登录完成
|
||||||
const tokens = await pollLoginToken(baseUrl, state, machineId);
|
const tokens = await pollLoginToken(baseUrl, state, machineId);
|
||||||
|
|
||||||
|
|
@ -96,22 +95,16 @@ function ModelPickerWrapper({
|
||||||
updateSettingsForSource('userSettings', { modelType: 'costrict' as any } as any);
|
updateSettingsForSource('userSettings', { modelType: 'costrict' as any } as any);
|
||||||
process.env.CLAUDE_CODE_USE_COSTRICT = '1';
|
process.env.CLAUDE_CODE_USE_COSTRICT = '1';
|
||||||
|
|
||||||
// 预取模型列表
|
// 预取并缓存模型列表
|
||||||
try {
|
try {
|
||||||
const { fetchCoStrictModels } = await import('../../costrict/provider/models.js');
|
const { fetchCoStrictModels } = await import('../../costrict/provider/models.js');
|
||||||
const models = await fetchCoStrictModels(baseUrl, tokens.access_token);
|
await fetchCoStrictModels(baseUrl, tokens.access_token);
|
||||||
if (models.length > 0) {
|
|
||||||
// 登录成功,刷新模型列表
|
|
||||||
onDone(`Successfully logged in to CoStrict! Found ${models.length} available models.`, {
|
|
||||||
display: 'system',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
// 预取失败
|
// 预取失败,picker 重载后会显示默认模型列表
|
||||||
}
|
}
|
||||||
|
|
||||||
onDone('Successfully logged in to CoStrict!', { display: 'system' });
|
// 登录成功后重载 ModelPicker,显示 CoStrict 模型选择界面
|
||||||
|
setPickerKey(k => k + 1);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
onDone(`Login failed: ${err.message || String(err)}`, { display: 'system' });
|
onDone(`Login failed: ${err.message || String(err)}`, { display: 'system' });
|
||||||
}
|
}
|
||||||
|
|
@ -166,6 +159,7 @@ function ModelPickerWrapper({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModelPicker
|
<ModelPicker
|
||||||
|
key={pickerKey}
|
||||||
initial={mainLoopModel}
|
initial={mainLoopModel}
|
||||||
sessionModel={mainLoopModelForSession}
|
sessionModel={mainLoopModelForSession}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,12 @@ import {
|
||||||
import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js'
|
import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js'
|
||||||
import { Box, Link, Newline, Text, useTheme } from '@anthropic/ink'
|
import { Box, Link, Newline, Text, useTheme } from '@anthropic/ink'
|
||||||
import { useKeybindings } from '../keybindings/useKeybinding.js'
|
import { useKeybindings } from '../keybindings/useKeybinding.js'
|
||||||
import { isAnthropicAuthEnabled } from '../utils/auth.js'
|
|
||||||
import { normalizeApiKeyForConfig } from '../utils/authPortable.js'
|
import { normalizeApiKeyForConfig } from '../utils/authPortable.js'
|
||||||
import { getCustomApiKeyStatus } from '../utils/config.js'
|
import { getCustomApiKeyStatus } from '../utils/config.js'
|
||||||
import { env } from '../utils/env.js'
|
import { env } from '../utils/env.js'
|
||||||
import { isRunningOnHomespace } from '../utils/envUtils.js'
|
import { isRunningOnHomespace } from '../utils/envUtils.js'
|
||||||
import { PreflightStep } from '../utils/preflightChecks.js'
|
|
||||||
import type { ThemeSetting } from '../utils/theme.js'
|
import type { ThemeSetting } from '../utils/theme.js'
|
||||||
import { ApproveApiKey } from './ApproveApiKey.js'
|
import { ApproveApiKey } from './ApproveApiKey.js'
|
||||||
import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js'
|
|
||||||
import { Select } from './CustomSelect/select.js'
|
import { Select } from './CustomSelect/select.js'
|
||||||
import { WelcomeV2 } from './LogoV2/WelcomeV2.js'
|
import { WelcomeV2 } from './LogoV2/WelcomeV2.js'
|
||||||
import { PressEnterToContinue } from './PressEnterToContinue.js'
|
import { PressEnterToContinue } from './PressEnterToContinue.js'
|
||||||
|
|
@ -26,9 +23,7 @@ import { ThemePicker } from './ThemePicker.js'
|
||||||
import { OrderedList } from './ui/OrderedList.js'
|
import { OrderedList } from './ui/OrderedList.js'
|
||||||
|
|
||||||
type StepId =
|
type StepId =
|
||||||
| 'preflight'
|
|
||||||
| 'theme'
|
| 'theme'
|
||||||
| 'oauth'
|
|
||||||
| 'api-key'
|
| 'api-key'
|
||||||
| 'security'
|
| 'security'
|
||||||
| 'terminal-setup'
|
| 'terminal-setup'
|
||||||
|
|
@ -44,15 +39,11 @@ type Props = {
|
||||||
|
|
||||||
export function Onboarding({ onDone }: Props): React.ReactNode {
|
export function Onboarding({ onDone }: Props): React.ReactNode {
|
||||||
const [currentStepIndex, setCurrentStepIndex] = useState(0)
|
const [currentStepIndex, setCurrentStepIndex] = useState(0)
|
||||||
const [skipOAuth, setSkipOAuth] = useState(false)
|
|
||||||
const [oauthEnabled] = useState(() => isAnthropicAuthEnabled())
|
|
||||||
const [theme, setTheme] = useTheme()
|
const [theme, setTheme] = useTheme()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('tengu_began_setup', {
|
logEvent('tengu_began_setup', {})
|
||||||
oauthEnabled,
|
}, [])
|
||||||
})
|
|
||||||
}, [oauthEnabled])
|
|
||||||
|
|
||||||
function goToNextStep() {
|
function goToNextStep() {
|
||||||
if (currentStepIndex < steps.length - 1) {
|
if (currentStepIndex < steps.length - 1) {
|
||||||
|
|
@ -60,7 +51,6 @@ export function Onboarding({ onDone }: Props): React.ReactNode {
|
||||||
setCurrentStepIndex(nextIndex)
|
setCurrentStepIndex(nextIndex)
|
||||||
|
|
||||||
logEvent('tengu_onboarding_step', {
|
logEvent('tengu_onboarding_step', {
|
||||||
oauthEnabled,
|
|
||||||
stepId: steps[nextIndex]
|
stepId: steps[nextIndex]
|
||||||
?.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
?.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||||
})
|
})
|
||||||
|
|
@ -123,8 +113,7 @@ export function Onboarding({ onDone }: Props): React.ReactNode {
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|
||||||
const preflightStep = <PreflightStep onSuccess={goToNextStep} />
|
// Create the steps array
|
||||||
// Create the steps array - determine which steps to include based on reAuth and oauthEnabled
|
|
||||||
const apiKeyNeedingApproval = useMemo(() => {
|
const apiKeyNeedingApproval = useMemo(() => {
|
||||||
// Add API key step if needed
|
// Add API key step if needed
|
||||||
// On homespace, ANTHROPIC_API_KEY is preserved in process.env for child
|
// 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) {
|
function handleApiKeyDone(_approved: boolean) {
|
||||||
if (approved) {
|
|
||||||
setSkipOAuth(true)
|
|
||||||
}
|
|
||||||
goToNextStep()
|
goToNextStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
const steps: OnboardingStep[] = []
|
const steps: OnboardingStep[] = []
|
||||||
if (oauthEnabled) {
|
|
||||||
steps.push({ id: 'preflight', component: preflightStep })
|
|
||||||
}
|
|
||||||
steps.push({ id: 'theme', component: themeStep })
|
steps.push({ id: 'theme', component: themeStep })
|
||||||
|
|
||||||
if (apiKeyNeedingApproval) {
|
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 })
|
steps.push({ id: 'security', component: securityStep })
|
||||||
|
|
||||||
if (shouldOfferTerminalSetup()) {
|
if (shouldOfferTerminalSetup()) {
|
||||||
|
|
@ -239,11 +211,11 @@ export function Onboarding({ onDone }: Props): React.ReactNode {
|
||||||
} else {
|
} else {
|
||||||
goToNextStep()
|
goToNextStep()
|
||||||
}
|
}
|
||||||
}, [currentStepIndex, steps.length, oauthEnabled, onDone])
|
}, [currentStepIndex, steps.length, onDone])
|
||||||
|
|
||||||
const handleTerminalSetupSkip = useCallback(() => {
|
const handleTerminalSetupSkip = useCallback(() => {
|
||||||
goToNextStep()
|
goToNextStep()
|
||||||
}, [currentStepIndex, steps.length, oauthEnabled, onDone])
|
}, [currentStepIndex, steps.length, onDone])
|
||||||
|
|
||||||
useKeybindings(
|
useKeybindings(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user