summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/components
diff options
context:
space:
mode:
authormatt korwel <[email protected]>2025-06-19 16:52:22 -0700
committerGitHub <[email protected]>2025-06-19 16:52:22 -0700
commit04518b52c0ddcd5ae1192763c55e472add218b3c (patch)
tree2587b0ccc5460e9e94eb8b715956cb713950f7c8 /packages/cli/src/ui/components
parentc48fcaa8c3fe8175718b1bbfc7770a958012173c (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.tsx41
-rw-r--r--packages/cli/src/ui/components/AuthDialog.tsx94
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>
+ );
+}