diff options
| author | matt korwel <[email protected]> | 2025-06-19 16:52:22 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-06-19 16:52:22 -0700 |
| commit | 04518b52c0ddcd5ae1192763c55e472add218b3c (patch) | |
| tree | 2587b0ccc5460e9e94eb8b715956cb713950f7c8 /packages/cli/src/ui/components | |
| parent | c48fcaa8c3fe8175718b1bbfc7770a958012173c (diff) | |
Auth First Run (#1207)
Co-authored-by: Tommaso Sciortino <[email protected]>
Co-authored-by: N. Taylor Mullen <[email protected]>
Diffstat (limited to 'packages/cli/src/ui/components')
| -rw-r--r-- | packages/cli/src/ui/components/AuthDialog.test.tsx | 41 | ||||
| -rw-r--r-- | packages/cli/src/ui/components/AuthDialog.tsx | 94 |
2 files changed, 135 insertions, 0 deletions
diff --git a/packages/cli/src/ui/components/AuthDialog.test.tsx b/packages/cli/src/ui/components/AuthDialog.test.tsx new file mode 100644 index 00000000..a5f46d93 --- /dev/null +++ b/packages/cli/src/ui/components/AuthDialog.test.tsx @@ -0,0 +1,41 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { render } from 'ink-testing-library'; +import { AuthDialog } from './AuthDialog.js'; +import { LoadedSettings } from '../../config/settings.js'; +import { AuthType } from '@gemini-cli/core'; + +describe('AuthDialog', () => { + it('should show an error if the initial auth type is invalid', () => { + const settings: LoadedSettings = new LoadedSettings( + { + settings: { + selectedAuthType: AuthType.USE_GEMINI, + }, + path: '', + }, + { + settings: {}, + path: '', + }, + [], + ); + + const { lastFrame } = render( + <AuthDialog + onSelect={() => {}} + onHighlight={() => {}} + settings={settings} + initialErrorMessage="GEMINI_API_KEY environment variable not found" + />, + ); + + expect(lastFrame()).toContain( + 'GEMINI_API_KEY environment variable not found', + ); + }); +}); diff --git a/packages/cli/src/ui/components/AuthDialog.tsx b/packages/cli/src/ui/components/AuthDialog.tsx new file mode 100644 index 00000000..b16529cf --- /dev/null +++ b/packages/cli/src/ui/components/AuthDialog.tsx @@ -0,0 +1,94 @@ +/** + * @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 '@gemini-cli/core'; +import { validateAuthMethod } from '../../config/auth.js'; + +interface AuthDialogProps { + onSelect: (authMethod: string | undefined, scope: SettingScope) => void; + onHighlight: (authMethod: string | undefined) => void; + settings: LoadedSettings; + initialErrorMessage?: string | null; +} + +export function AuthDialog({ + onSelect, + onHighlight, + settings, + initialErrorMessage, +}: AuthDialogProps): React.JSX.Element { + const [errorMessage, setErrorMessage] = useState<string | null>( + initialErrorMessage || null, + ); + const authItems = [ + { + label: 'Login with Google Personal Account', + value: AuthType.LOGIN_WITH_GOOGLE_PERSONAL, + }, + { label: 'Gemini API Key', value: AuthType.USE_GEMINI }, + { + label: 'Login with GCP Project and Google Work Account', + value: AuthType.LOGIN_WITH_GOOGLE_ENTERPRISE, + }, + { label: 'Vertex AI', value: AuthType.USE_VERTEX_AI }, + ]; + + let initialAuthIndex = authItems.findIndex( + (item) => item.value === settings.merged.selectedAuthType, + ); + + if (initialAuthIndex === -1) { + initialAuthIndex = 0; + } + + const handleAuthSelect = (authMethod: string) => { + const error = validateAuthMethod(authMethod); + if (error) { + setErrorMessage(error); + } else { + setErrorMessage(null); + onSelect(authMethod, SettingScope.User); + } + }; + + useInput((_input, key) => { + if (key.escape) { + onSelect(undefined, SettingScope.User); + } + }); + + return ( + <Box + borderStyle="round" + borderColor={Colors.Gray} + flexDirection="column" + padding={1} + width="100%" + > + <Text bold>Select Auth Method</Text> + <RadioButtonSelect + items={authItems} + initialIndex={initialAuthIndex} + onSelect={handleAuthSelect} + onHighlight={onHighlight} + isFocused={true} + /> + {errorMessage && ( + <Box marginTop={1}> + <Text color={Colors.AccentRed}>{errorMessage}</Text> + </Box> + )} + <Box marginTop={1}> + <Text color={Colors.Gray}>(Use Enter to select)</Text> + </Box> + </Box> + ); +} |
