-
-
Notifications
You must be signed in to change notification settings - Fork 78
feat(gemini): Add manual project_id input when auto-discovery fails #152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # Manual Google Cloud Code Assist Setup for Gemini CLI | ||
|
|
||
| > **⚠️ PLACEHOLDER - To Be Completed by Mirrowel** | ||
| > | ||
| > This document will contain step-by-step instructions for manually enabling the Code Assist API in Google Cloud Console. | ||
|
|
||
| ## Overview | ||
|
|
||
| Some Gemini accounts require manual creation of a Google Cloud Console project with the Code Assist API enabled. This guide will walk you through the process. | ||
|
|
||
|
Comment on lines
+1
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This documentation file is incomplete — every section contains a "[bold]Please follow the manual setup instructions:[/bold]\n"
"[cyan]docs/gemini-cli-manual-code-assist-setup.md[/cyan]\n\n"
"After following the instructions, enter your Project ID below."A user whose auto-discovery fails will be directed to this file for help and find it completely empty. Either the document should be completed before merging, or the prompt should reference the |
||
| ## Prerequisites | ||
|
|
||
| - A Google account with Gemini CLI access | ||
| - Access to Google Cloud Console | ||
|
|
||
| ## Steps | ||
|
|
||
| ### Step 1: Create a Google Cloud Project | ||
|
|
||
| [Instructions to be added...] | ||
|
|
||
| ### Step 2: Enable the Code Assist API | ||
|
|
||
| [Instructions to be added...] | ||
|
|
||
| ### Step 3: Configure API Permissions | ||
|
|
||
| [Instructions to be added...] | ||
|
|
||
| ### Step 4: Obtain Your Project ID | ||
|
|
||
| [Instructions to be added...] | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Common Issues | ||
|
|
||
| [To be added...] | ||
|
|
||
| ### Error Messages | ||
|
|
||
| [To be added...] | ||
|
|
||
| ## Additional Resources | ||
|
|
||
| - [Google Cloud Console](https://console.cloud.google.com/) | ||
| - [Gemini CLI Documentation](https://goo.gle/gemini-cli-auth-docs) | ||
|
|
||
| --- | ||
|
|
||
| **Note:** This document is a placeholder. Please check back later for complete instructions. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,15 @@ | |
| # Service Usage API for checking enabled APIs | ||
| SERVICE_USAGE_API = "https://serviceusage.googleapis.com/v1" | ||
|
|
||
| # Known Gemini CLI tiers for manual selection | ||
| KNOWN_GEMINI_TIERS = [ | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| "free-tier", | ||
| "legacy-tier", | ||
| "standard", | ||
| "premium", | ||
| "enterprise", | ||
| ] | ||
|
|
||
| lib_logger = logging.getLogger("rotator_library") | ||
|
|
||
| # Headers for Gemini CLI auth/discovery calls (loadCodeAssist, onboardUser, etc.) | ||
|
|
@@ -785,14 +794,173 @@ async def _discover_project_id( | |
| except httpx.RequestError as e: | ||
| lib_logger.error(f"Network error while listing GCP projects: {e}") | ||
|
|
||
| # Auto-discovery failed - prompt for manual input (interactive mode only) | ||
| lib_logger.info( | ||
| "Auto-discovery failed. Checking if manual input is available..." | ||
| ) | ||
|
|
||
| # Try to get project_id and tier from user input | ||
| try: | ||
| manual_result = await self._prompt_for_manual_setup() | ||
| if manual_result and manual_result.get("project_id"): | ||
| project_id = manual_result["project_id"] | ||
| tier = manual_result.get("tier") | ||
|
|
||
| lib_logger.info( | ||
| f"Using manually provided project ID: {project_id}, tier: {tier}" | ||
| ) | ||
|
|
||
| # Cache the values | ||
| self.project_id_cache[credential_path] = project_id | ||
| if tier: | ||
| self.project_tier_cache[credential_path] = tier | ||
|
|
||
| # Persist to credential file | ||
| await self._persist_project_metadata(credential_path, project_id, tier) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If Consider adding explicit warning logging if persistence fails. |
||
|
|
||
| return project_id | ||
| except Exception as e: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The try-except block catches all |
||
| lib_logger.debug(f"Manual input not available or failed: {e}") | ||
|
|
||
| # If we get here, manual input was not available or failed | ||
| raise ValueError( | ||
| "Could not auto-discover Gemini project ID. Possible causes:\n" | ||
| " 1. The cloudaicompanion.googleapis.com API is not enabled (enable it in Google Cloud Console)\n" | ||
| " 2. No active GCP projects exist for this account (create one in Google Cloud Console)\n" | ||
| " 3. Account lacks necessary permissions\n" | ||
| "To manually specify a project, set GEMINI_CLI_PROJECT_ID in your .env file." | ||
| "To manually specify a project, set GEMINI_CLI_PROJECT_ID in your .env file or provide it during interactive setup." | ||
| ) | ||
|
|
||
| async def _prompt_for_manual_setup(self) -> Optional[Dict[str, str]]: | ||
| """ | ||
| Prompt user for manual project_id and tier selection. | ||
|
|
||
| This is called when auto-discovery fails during interactive credential setup. | ||
| Only works in interactive mode (stdin available). | ||
|
|
||
| Returns: | ||
| Dict with 'project_id' and optionally 'tier', or None if not available | ||
| """ | ||
| import sys | ||
|
|
||
| # Check if we're in interactive mode | ||
| if not sys.stdin.isatty(): | ||
| lib_logger.debug("Not in interactive mode, skipping manual prompt") | ||
| return None | ||
|
|
||
| # Import Rich for console output (deferred import to avoid circular dependency) | ||
| try: | ||
| from rich.console import Console | ||
| from rich.prompt import Prompt, Confirm | ||
| from rich.panel import Panel | ||
| from rich.text import Text | ||
| except ImportError: | ||
| lib_logger.debug("Rich not available for manual prompt") | ||
| return None | ||
|
|
||
| console = Console() | ||
|
|
||
| # Display instructions | ||
| console.print( | ||
| Panel( | ||
| Text.from_markup( | ||
| "[bold yellow]Automatic project discovery failed.[/bold yellow]\n\n" | ||
| "This usually means:\n" | ||
| "1. The Cloud AI Companion API is not enabled in your Google Cloud project\n" | ||
| "2. You need to manually create a project\n\n" | ||
| "[bold]Please follow the manual setup instructions:[/bold]\n" | ||
| "[cyan]docs/gemini-cli-manual-code-assist-setup.md[/cyan]\n\n" | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This hard-coded relative path assumes the user is running from the repository root. Consider using a path relative to the module using |
||
| "After following the instructions, enter your Project ID below." | ||
| ), | ||
| title="Manual Project Setup Required", | ||
| style="yellow", | ||
| ) | ||
| ) | ||
|
|
||
| # Prompt for project_id | ||
| project_id = Prompt.ask( | ||
| "[bold]Enter your Google Cloud Project ID[/bold] [dim](or press Enter to skip)[/dim]", | ||
| default="", | ||
| ) | ||
|
|
||
| if not project_id.strip(): | ||
| console.print( | ||
| "[dim]Skipping manual setup. You can set GEMINI_CLI_PROJECT_ID environment variable later.[/dim]" | ||
| ) | ||
| return None | ||
|
|
||
| project_id = project_id.strip() | ||
|
|
||
| # Prompt for tier selection | ||
| console.print("\n[bold]Select your tier:[/bold]") | ||
| console.print("[dim]If unsure, select 'free-tier' or 'legacy-tier'[/dim]\n") | ||
|
|
||
| for i, tier in enumerate(KNOWN_GEMINI_TIERS, 1): | ||
| console.print(f" {i}. {tier}") | ||
| console.print(f" {len(KNOWN_GEMINI_TIERS) + 1}. Other (unknown)") | ||
|
|
||
| tier_choice = Prompt.ask( | ||
| "\n[bold]Enter tier number[/bold]", | ||
| default="1", | ||
| ) | ||
|
|
||
| try: | ||
| choice_idx = int(tier_choice) - 1 | ||
| if 0 <= choice_idx < len(KNOWN_GEMINI_TIERS): | ||
| selected_tier = KNOWN_GEMINI_TIERS[choice_idx] | ||
| else: | ||
| selected_tier = "unknown" | ||
| except ValueError: | ||
| selected_tier = "unknown" | ||
|
|
||
| console.print( | ||
| Panel( | ||
| f"Project ID: [cyan]{project_id}[/cyan]\n" | ||
| f"Tier: [green]{selected_tier}[/green]", | ||
| style="green", | ||
| title="Manual Setup Complete", | ||
| ) | ||
| ) | ||
|
|
||
| return {"project_id": project_id, "tier": selected_tier} | ||
|
|
||
| async def _persist_project_metadata( | ||
| self, credential_path: str, project_id: str, tier: Optional[str] | ||
| ): | ||
|
Comment on lines
+927
to
+929
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
await self._persist_project_metadata(
credential_path,
project_id,
discovered_tier,
discovered_tier_full, # ← 4th arg, no matching parameter
)This will raise async def _persist_project_metadata(
self, credential_path: str, project_id: str, tier: Optional[str], tier_full: Optional[str] = None
):
...
if tier_full:
creds["_proxy_metadata"]["tier_full"] = tier_full |
||
| """Persists project ID and tier to the credential file for faster future startups.""" | ||
| # Skip persistence for env:// paths (environment-based credentials) | ||
| credential_index = self._parse_env_credential_path(credential_path) | ||
| if credential_index is not None: | ||
| lib_logger.debug( | ||
| f"Skipping project metadata persistence for env:// credential path: {credential_path}" | ||
| ) | ||
| return | ||
|
|
||
| try: | ||
| # Load current credentials | ||
| with open(credential_path, "r") as f: | ||
| creds = json.load(f) | ||
|
|
||
| # Update metadata | ||
| if "_proxy_metadata" not in creds: | ||
| creds["_proxy_metadata"] = {} | ||
|
|
||
| creds["_proxy_metadata"]["project_id"] = project_id | ||
| if tier: | ||
| creds["_proxy_metadata"]["tier"] = tier | ||
|
|
||
| # Save back using the existing save method (handles atomic writes and permissions) | ||
| await self._save_credentials(credential_path, creds) | ||
|
|
||
| lib_logger.debug( | ||
| f"Persisted project_id and tier to credential file: {credential_path}" | ||
| ) | ||
| except Exception as e: | ||
| lib_logger.warning( | ||
| f"Failed to persist project metadata to credential file: {e}" | ||
| ) | ||
| # Non-fatal - just means slower startup next time | ||
|
|
||
| # ========================================================================= | ||
| # CREDENTIAL MANAGEMENT OVERRIDES | ||
| # ========================================================================= | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This documentation file is marked as a placeholder with incomplete steps. However, the code in
gemini_auth_base.py:872directs users to read this file during manual setup.Consider completing the documentation before merging, or remove this file from the PR and add it in a follow-up once the steps are documented.