From c55b15f705d083e3dadcfb71494dcb0d6043e6c6 Mon Sep 17 00:00:00 2001 From: Tommaso Sciortino Date: Thu, 26 Jun 2025 08:27:20 -0700 Subject: [PATCH] Improve LoadCodeAssist error handling (#1645) --- docs/cli/authentication.md | 25 +------ packages/cli/src/gemini.tsx | 12 ++-- packages/cli/src/ui/hooks/useAuthCommand.ts | 12 +--- packages/core/src/code_assist/setup.ts | 72 ++++++++++++++------- 4 files changed, 61 insertions(+), 60 deletions(-) diff --git a/docs/cli/authentication.md b/docs/cli/authentication.md index 90553289..6c06b1b7 100644 --- a/docs/cli/authentication.md +++ b/docs/cli/authentication.md @@ -7,7 +7,7 @@ The Gemini CLI requires you to authenticate with Google's AI services. On initia - Use this option to log in with your google account. - During initial startup, Gemini CLI will direct you to a webpage for authentication. Once authenticated, your credentials will be cached locally so the web login can be skipped on subsequent runs. - Note that the web login must be done in a browser that can communicate with the machine Gemini CLI is being run from. (Specifically, the browser will be redirected to a localhost url that Gemini CLI will be listening on). - - Users may have to specify a GOOGLE_CLOUD_PROJECT if: + - Users may have to specify a GOOGLE_CLOUD_PROJECT if: 1. You have a Google Workspace account. Google Workspace is a paid service for businesses and organizations that provides a suite of productivity tools, including a custom email domain (e.g. your-name@your-company.com), enhanced security features, and administrative controls. These accounts are often managed by an employer or school. 2. You are a licensed Code Assist user. This can happen if you have previously purchased a Code Assist license or have acquired one through Google Developer Program. - If you fall into one of these categories, you must first configure a Google Cloud Project Id to use, [enable the Gemini for Cloud API](https://cloud.google.com/gemini/docs/discover/set-up-gemini#enable-api) and [configure access permissions](https://cloud.google.com/gemini/docs/discover/set-up-gemini#grant-iam). You can temporarily set the environment variable in your current shell session using the following command: @@ -34,28 +34,7 @@ The Gemini CLI requires you to authenticate with Google's AI services. On initia source ~/.bashrc ``` -3. **Login with Google (Gemini Code Assist for Workspace or licensed Code Assist users):** - - (For more information, see: https://developers.google.com/gemini-code-assist/resources/faqs#gcp-project-requirement) - - - Use this option if: - - 1. You have a Google Workspace account. Google Workspace is a paid service for businesses and organizations that provides a suite of productivity tools, including a custom email domain (e.g. your-name@your-company.com), enhanced security features, and administrative controls. These accounts are often managed by an employer or school. - 2. You are a licensed Code Assist user. This can happen if you have previously purchased a Code Assist license or have acquired one through Google Developer Program. - - - If you fall into one of these categories, you must first configure a Google Cloud Project Id to use, [enable the Gemini for Cloud API](https://cloud.google.com/gemini/docs/discover/set-up-gemini#enable-api) and [configure access permissions](https://cloud.google.com/gemini/docs/discover/set-up-gemini#grant-iam). You can temporarily set the environment variable in your current shell session using the following command: - ```bash - export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID" - ``` - - For repeated use, you can add the environment variable to your `.env` file (located in the project directory or user home directory) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file: - ```bash - echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc - source ~/.bashrc - ``` - - During startup, Gemini CLI will direct you to a webpage for authentication. Once authenticated, your credentials will be cached locally so the web login can be skipped on subsequent runs. - - Note that the web login must be done in a browser that can communicate with the machine Gemini CLI is being run from. (Specifically, the browser will be redirected to a localhost url that Gemini CLI will be listening on). - -4. **Vertex AI:** +3. **Vertex AI:** - If not using express mode: - Ensure you have a Google Cloud project and have enabled the Vertex AI API. - Set up Application Default Credentials (ADC), using the following command: diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx index 95c8d4f2..4a0014e1 100644 --- a/packages/cli/src/gemini.tsx +++ b/packages/cli/src/gemini.tsx @@ -141,12 +141,16 @@ export async function main() { if (sandboxConfig) { if (settings.merged.selectedAuthType) { // Validate authentication here because the sandbox will interfere with the Oauth2 web redirect. - const err = validateAuthMethod(settings.merged.selectedAuthType); - if (err) { - console.error(err); + try { + const err = validateAuthMethod(settings.merged.selectedAuthType); + if (err) { + throw new Error(err); + } + await config.refreshAuth(settings.merged.selectedAuthType); + } catch (err) { + console.error('Error authenticating:', err); process.exit(1); } - await config.refreshAuth(settings.merged.selectedAuthType); } await start_sandbox(sandboxConfig, memoryArgs); process.exit(0); diff --git a/packages/cli/src/ui/hooks/useAuthCommand.ts b/packages/cli/src/ui/hooks/useAuthCommand.ts index 283572d2..766ebe2b 100644 --- a/packages/cli/src/ui/hooks/useAuthCommand.ts +++ b/packages/cli/src/ui/hooks/useAuthCommand.ts @@ -46,17 +46,7 @@ export const useAuthCommand = ( config, ); } catch (e) { - let errorMessage = `Failed to login.\nMessage: ${getErrorMessage(e)}`; - if ( - settings.merged.selectedAuthType === - AuthType.LOGIN_WITH_GOOGLE_PERSONAL && - !process.env.GOOGLE_CLOUD_PROJECT - ) { - errorMessage = - 'Failed to login. Workspace accounts and licensed Code Assist users must configure' + - ` GOOGLE_CLOUD_PROJECT (see https://goo.gle/gemini-cli-auth-docs#workspace-gca).\nMessage: ${getErrorMessage(e)}`; - } - setAuthError(errorMessage); + setAuthError(`Failed to login. Message: ${getErrorMessage(e)}`); openAuthDialog(); } finally { setIsAuthenticating(false); diff --git a/packages/core/src/code_assist/setup.ts b/packages/core/src/code_assist/setup.ts index c49a5efa..f0ea60b3 100644 --- a/packages/core/src/code_assist/setup.ts +++ b/packages/core/src/code_assist/setup.ts @@ -4,17 +4,31 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ClientMetadata, OnboardUserRequest } from './types.js'; +import { + ClientMetadata, + GeminiUserTier, + LoadCodeAssistResponse, + OnboardUserRequest, + UserTierId, +} from './types.js'; import { CodeAssistServer } from './server.js'; import { OAuth2Client } from 'google-auth-library'; +export class ProjectIdRequiredError extends Error { + constructor() { + super( + 'This account requires setting the GOOGLE_CLOUD_PROJECT env var. See https://goo.gle/gemini-cli-auth-docs#workspace-gca', + ); + } +} + /** * * @param projectId the user's project id, if any * @returns the user's actual project id */ export async function setupUser(authClient: OAuth2Client): Promise { - const projectId = process.env.GOOGLE_CLOUD_PROJECT; + let projectId = process.env.GOOGLE_CLOUD_PROJECT; const caServer = new CodeAssistServer(authClient, projectId); const clientMetadata: ClientMetadata = { @@ -24,34 +38,48 @@ export async function setupUser(authClient: OAuth2Client): Promise { duetProject: projectId, }; - // TODO: Support Free Tier user without projectId. const loadRes = await caServer.loadCodeAssist({ cloudaicompanionProject: projectId, metadata: clientMetadata, }); - const onboardTier: string = - loadRes.allowedTiers?.find((tier) => tier.isDefault)?.id ?? 'legacy-tier'; + if (!projectId && loadRes.cloudaicompanionProject) { + projectId = loadRes.cloudaicompanionProject; + } + + const tier = getOnboardTier(loadRes); + if (tier.userDefinedCloudaicompanionProject && !projectId) { + throw new ProjectIdRequiredError(); + } const onboardReq: OnboardUserRequest = { - tierId: onboardTier, - cloudaicompanionProject: loadRes.cloudaicompanionProject || projectId || '', + tierId: tier.id, + cloudaicompanionProject: projectId, metadata: clientMetadata, }; - try { - // Poll onboardUser until long running operation is complete. - let lroRes = await caServer.onboardUser(onboardReq); - while (!lroRes.done) { - await new Promise((f) => setTimeout(f, 5000)); - lroRes = await caServer.onboardUser(onboardReq); - } - return lroRes.response?.cloudaicompanionProject?.id || ''; - } catch (e) { - console.log( - '\n\nError onboarding with Code Assist.\n' + - 'Google Workspace Account (e.g. your-name@your-company.com)' + - ' must specify a GOOGLE_CLOUD_PROJECT environment variable.\n\n', - ); - throw e; + + // Poll onboardUser until long running operation is complete. + let lroRes = await caServer.onboardUser(onboardReq); + while (!lroRes.done) { + await new Promise((f) => setTimeout(f, 5000)); + lroRes = await caServer.onboardUser(onboardReq); } + return lroRes.response?.cloudaicompanionProject?.id || ''; +} + +function getOnboardTier(res: LoadCodeAssistResponse): GeminiUserTier { + if (res.currentTier) { + return res.currentTier; + } + for (const tier of res.allowedTiers || []) { + if (tier.isDefault) { + return tier; + } + } + return { + name: '', + description: '', + id: UserTierId.LEGACY, + userDefinedCloudaicompanionProject: true, + }; }