Spaces:
Paused
feat(auth): enable Gemini CLI stateless authentication via environment variables
Browse filesImplement support for loading Gemini CLI credentials directly from environment variables (e.g., GEMINI_CLI_ACCESS_TOKEN) for stateless hosting environments.
The credential management tool (`credential_tool.py`) now includes a new option to export existing file-based Gemini OAuth credentials into a standard .env file format, simplifying setup for platforms without persistent storage.
- Provider logic is updated to prioritize loading credentials from environment variables over local files.
- Prevents saving newly refreshed access tokens back to the file system when the credentials originated from environment variables.
- Updates README documentation detailing the new tool option and required environment variables.
- Adds `time` import and `Accept` header for robustness.
|
@@ -119,6 +119,25 @@ For many providers, **no configuration is necessary**. The proxy automatically d
|
|
| 119 |
|
| 120 |
You only need to create a `.env` file to set your `PROXY_API_KEY` and to override or add credentials if the automatic discovery doesn't suit your needs.
|
| 121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
**Example `.env` configuration:**
|
| 123 |
```env
|
| 124 |
# A secret key for your proxy server to authenticate requests.
|
|
@@ -137,6 +156,16 @@ OPENROUTER_API_KEY_1="YOUR_OPENROUTER_API_KEY_1"
|
|
| 137 |
# You can override this by specifying a path to your credential file.
|
| 138 |
GEMINI_CLI_OAUTH_1="/path/to/your/specific/gemini_creds.json"
|
| 139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
# --- Dual Authentication Support ---
|
| 141 |
# Some providers (qwen_code, iflow) support BOTH OAuth and direct API keys.
|
| 142 |
# You can use either method, or mix both for credential rotation:
|
|
|
|
| 119 |
|
| 120 |
You only need to create a `.env` file to set your `PROXY_API_KEY` and to override or add credentials if the automatic discovery doesn't suit your needs.
|
| 121 |
|
| 122 |
+
#### Interactive Credential Management Tool
|
| 123 |
+
|
| 124 |
+
For easier credential management, you can use the interactive credential tool:
|
| 125 |
+
|
| 126 |
+
```bash
|
| 127 |
+
python -m rotator_library.credential_tool
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
This tool provides:
|
| 131 |
+
1. **Add OAuth Credential** - Interactive OAuth flow for Gemini CLI, Qwen Code, and iFlow
|
| 132 |
+
2. **Add API Key** - Add API keys for any LiteLLM-supported provider
|
| 133 |
+
3. **Export Gemini CLI to .env** - NEW! Export OAuth credentials to environment variables for stateless deployments
|
| 134 |
+
|
| 135 |
+
**For Stateless Hosting (Railway, Render, Vercel, etc.):**
|
| 136 |
+
- Use option 3 to export your Gemini CLI credentials to `.env` format
|
| 137 |
+
- The generated file contains all necessary environment variables
|
| 138 |
+
- Simply paste these into your hosting platform's environment settings
|
| 139 |
+
- No file persistence required - credentials load automatically from environment variables
|
| 140 |
+
|
| 141 |
**Example `.env` configuration:**
|
| 142 |
```env
|
| 143 |
# A secret key for your proxy server to authenticate requests.
|
|
|
|
| 156 |
# You can override this by specifying a path to your credential file.
|
| 157 |
GEMINI_CLI_OAUTH_1="/path/to/your/specific/gemini_creds.json"
|
| 158 |
|
| 159 |
+
# --- Gemini CLI: Stateless Deployment Support ---
|
| 160 |
+
# For hosts without file persistence (Railway, Render, etc.), you can provide
|
| 161 |
+
# Gemini CLI credentials directly via environment variables:
|
| 162 |
+
GEMINI_CLI_ACCESS_TOKEN="ya29.your-access-token"
|
| 163 |
+
GEMINI_CLI_REFRESH_TOKEN="1//your-refresh-token"
|
| 164 |
+
GEMINI_CLI_EXPIRY_DATE="1234567890000"
|
| 165 |
+
GEMINI_CLI_EMAIL="your-email@gmail.com"
|
| 166 |
+
# Optional: GEMINI_CLI_PROJECT_ID, GEMINI_CLI_CLIENT_ID, etc.
|
| 167 |
+
# See IMPLEMENTATION_SUMMARY.md for full list of supported variables
|
| 168 |
+
|
| 169 |
# --- Dual Authentication Support ---
|
| 170 |
# Some providers (qwen_code, iflow) support BOTH OAuth and direct API keys.
|
| 171 |
# You can use either method, or mix both for credential rotation:
|
|
@@ -3,6 +3,7 @@
|
|
| 3 |
import asyncio
|
| 4 |
import json
|
| 5 |
import re
|
|
|
|
| 6 |
from pathlib import Path
|
| 7 |
from dotenv import set_key, get_key
|
| 8 |
|
|
@@ -220,6 +221,102 @@ async def setup_new_credential(provider_name: str):
|
|
| 220 |
console.print(Panel(f"An error occurred during setup for {provider_name}: {e}", style="bold red", title="Error"))
|
| 221 |
|
| 222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
async def main():
|
| 224 |
"""
|
| 225 |
An interactive CLI tool to add new credentials.
|
|
@@ -229,20 +326,20 @@ async def main():
|
|
| 229 |
|
| 230 |
while True:
|
| 231 |
console.print(Panel(
|
| 232 |
-
Text.from_markup("1. Add OAuth Credential\n2. Add API Key"),
|
| 233 |
title="Choose credential type",
|
| 234 |
style="bold blue"
|
| 235 |
))
|
| 236 |
-
|
| 237 |
setup_type = Prompt.ask(
|
| 238 |
Text.from_markup("[bold]Please select an option or type [red]'q'[/red] to quit[/bold]"),
|
| 239 |
-
choices=["1", "2", "q"],
|
| 240 |
show_choices=False
|
| 241 |
)
|
| 242 |
|
| 243 |
if setup_type.lower() == 'q':
|
| 244 |
break
|
| 245 |
-
|
| 246 |
if setup_type == "1":
|
| 247 |
available_providers = get_available_providers()
|
| 248 |
oauth_friendly_names = {
|
|
@@ -282,6 +379,9 @@ async def main():
|
|
| 282 |
elif setup_type == "2":
|
| 283 |
await setup_api_key()
|
| 284 |
|
|
|
|
|
|
|
|
|
|
| 285 |
console.print("\n" + "="*50 + "\n")
|
| 286 |
|
| 287 |
def run_credential_tool():
|
|
|
|
| 3 |
import asyncio
|
| 4 |
import json
|
| 5 |
import re
|
| 6 |
+
import time
|
| 7 |
from pathlib import Path
|
| 8 |
from dotenv import set_key, get_key
|
| 9 |
|
|
|
|
| 221 |
console.print(Panel(f"An error occurred during setup for {provider_name}: {e}", style="bold red", title="Error"))
|
| 222 |
|
| 223 |
|
| 224 |
+
async def export_gemini_cli_to_env():
|
| 225 |
+
"""
|
| 226 |
+
Export a Gemini CLI credential JSON file to .env format.
|
| 227 |
+
Generates one .env file per credential.
|
| 228 |
+
"""
|
| 229 |
+
console.print(Panel("[bold cyan]Export Gemini CLI Credential to .env[/bold cyan]", expand=False))
|
| 230 |
+
|
| 231 |
+
# Find all gemini_cli credentials
|
| 232 |
+
gemini_cli_files = list(OAUTH_BASE_DIR.glob("gemini_cli_oauth_*.json"))
|
| 233 |
+
|
| 234 |
+
if not gemini_cli_files:
|
| 235 |
+
console.print(Panel("No Gemini CLI credentials found. Please add one first using 'Add OAuth Credential'.",
|
| 236 |
+
style="bold red", title="No Credentials"))
|
| 237 |
+
return
|
| 238 |
+
|
| 239 |
+
# Display available credentials
|
| 240 |
+
cred_text = Text()
|
| 241 |
+
for i, cred_file in enumerate(gemini_cli_files):
|
| 242 |
+
try:
|
| 243 |
+
with open(cred_file, 'r') as f:
|
| 244 |
+
creds = json.load(f)
|
| 245 |
+
email = creds.get("_proxy_metadata", {}).get("email", "unknown")
|
| 246 |
+
cred_text.append(f" {i + 1}. {cred_file.name} ({email})\n")
|
| 247 |
+
except Exception as e:
|
| 248 |
+
cred_text.append(f" {i + 1}. {cred_file.name} (error reading: {e})\n")
|
| 249 |
+
|
| 250 |
+
console.print(Panel(cred_text, title="Available Gemini CLI Credentials", style="bold blue"))
|
| 251 |
+
|
| 252 |
+
choice = Prompt.ask(
|
| 253 |
+
Text.from_markup("[bold]Please select a credential to export or type [red]'b'[/red] to go back[/bold]"),
|
| 254 |
+
choices=[str(i + 1) for i in range(len(gemini_cli_files))] + ["b"],
|
| 255 |
+
show_choices=False
|
| 256 |
+
)
|
| 257 |
+
|
| 258 |
+
if choice.lower() == 'b':
|
| 259 |
+
return
|
| 260 |
+
|
| 261 |
+
try:
|
| 262 |
+
choice_index = int(choice) - 1
|
| 263 |
+
if 0 <= choice_index < len(gemini_cli_files):
|
| 264 |
+
cred_file = gemini_cli_files[choice_index]
|
| 265 |
+
|
| 266 |
+
# Load the credential
|
| 267 |
+
with open(cred_file, 'r') as f:
|
| 268 |
+
creds = json.load(f)
|
| 269 |
+
|
| 270 |
+
# Extract metadata
|
| 271 |
+
email = creds.get("_proxy_metadata", {}).get("email", "unknown")
|
| 272 |
+
project_id = creds.get("_proxy_metadata", {}).get("project_id", "")
|
| 273 |
+
|
| 274 |
+
# Generate .env file name
|
| 275 |
+
safe_email = email.replace("@", "_at_").replace(".", "_")
|
| 276 |
+
env_filename = f"gemini_cli_{safe_email}.env"
|
| 277 |
+
env_filepath = OAUTH_BASE_DIR / env_filename
|
| 278 |
+
|
| 279 |
+
# Build .env content
|
| 280 |
+
env_lines = [
|
| 281 |
+
f"# Gemini CLI Credential for: {email}",
|
| 282 |
+
f"# Generated from: {cred_file.name}",
|
| 283 |
+
f"# Generated at: {time.strftime('%Y-%m-%d %H:%M:%S')}",
|
| 284 |
+
"",
|
| 285 |
+
f"GEMINI_CLI_ACCESS_TOKEN={creds.get('access_token', '')}",
|
| 286 |
+
f"GEMINI_CLI_REFRESH_TOKEN={creds.get('refresh_token', '')}",
|
| 287 |
+
f"GEMINI_CLI_EXPIRY_DATE={creds.get('expiry_date', 0)}",
|
| 288 |
+
f"GEMINI_CLI_CLIENT_ID={creds.get('client_id', '')}",
|
| 289 |
+
f"GEMINI_CLI_CLIENT_SECRET={creds.get('client_secret', '')}",
|
| 290 |
+
f"GEMINI_CLI_TOKEN_URI={creds.get('token_uri', 'https://oauth2.googleapis.com/token')}",
|
| 291 |
+
f"GEMINI_CLI_UNIVERSE_DOMAIN={creds.get('universe_domain', 'googleapis.com')}",
|
| 292 |
+
f"GEMINI_CLI_EMAIL={email}",
|
| 293 |
+
]
|
| 294 |
+
|
| 295 |
+
# Add project_id if present
|
| 296 |
+
if project_id:
|
| 297 |
+
env_lines.append(f"GEMINI_CLI_PROJECT_ID={project_id}")
|
| 298 |
+
|
| 299 |
+
# Write to .env file
|
| 300 |
+
with open(env_filepath, 'w') as f:
|
| 301 |
+
f.write('\n'.join(env_lines))
|
| 302 |
+
|
| 303 |
+
success_text = Text.from_markup(
|
| 304 |
+
f"Successfully exported credential to [bold yellow]'{env_filepath}'[/bold yellow]\n\n"
|
| 305 |
+
f"To use this credential:\n"
|
| 306 |
+
f"1. Copy [bold yellow]{env_filepath.name}[/bold yellow] to your deployment environment\n"
|
| 307 |
+
f"2. Load the variables: [bold cyan]export $(cat {env_filepath.name} | grep -v '^#' | xargs)[/bold cyan]\n"
|
| 308 |
+
f"3. Or source it: [bold cyan]source {env_filepath.name}[/bold cyan]\n"
|
| 309 |
+
f"4. The Gemini CLI provider will automatically use these environment variables"
|
| 310 |
+
)
|
| 311 |
+
console.print(Panel(success_text, style="bold green", title="Success"))
|
| 312 |
+
else:
|
| 313 |
+
console.print("[bold red]Invalid choice. Please try again.[/bold red]")
|
| 314 |
+
except ValueError:
|
| 315 |
+
console.print("[bold red]Invalid input. Please enter a number or 'b'.[/bold red]")
|
| 316 |
+
except Exception as e:
|
| 317 |
+
console.print(Panel(f"An error occurred during export: {e}", style="bold red", title="Error"))
|
| 318 |
+
|
| 319 |
+
|
| 320 |
async def main():
|
| 321 |
"""
|
| 322 |
An interactive CLI tool to add new credentials.
|
|
|
|
| 326 |
|
| 327 |
while True:
|
| 328 |
console.print(Panel(
|
| 329 |
+
Text.from_markup("1. Add OAuth Credential\n2. Add API Key\n3. Export Gemini CLI credential to .env"),
|
| 330 |
title="Choose credential type",
|
| 331 |
style="bold blue"
|
| 332 |
))
|
| 333 |
+
|
| 334 |
setup_type = Prompt.ask(
|
| 335 |
Text.from_markup("[bold]Please select an option or type [red]'q'[/red] to quit[/bold]"),
|
| 336 |
+
choices=["1", "2", "3", "q"],
|
| 337 |
show_choices=False
|
| 338 |
)
|
| 339 |
|
| 340 |
if setup_type.lower() == 'q':
|
| 341 |
break
|
| 342 |
+
|
| 343 |
if setup_type == "1":
|
| 344 |
available_providers = get_available_providers()
|
| 345 |
oauth_friendly_names = {
|
|
|
|
| 379 |
elif setup_type == "2":
|
| 380 |
await setup_api_key()
|
| 381 |
|
| 382 |
+
elif setup_type == "3":
|
| 383 |
+
await export_gemini_cli_to_env()
|
| 384 |
+
|
| 385 |
console.print("\n" + "="*50 + "\n")
|
| 386 |
|
| 387 |
def run_credential_tool():
|
|
@@ -1,7 +1,8 @@
|
|
| 1 |
# src/rotator_library/providers/gemini_auth_base.py
|
| 2 |
|
|
|
|
| 3 |
import webbrowser
|
| 4 |
-
from typing import Union
|
| 5 |
import json
|
| 6 |
import time
|
| 7 |
import asyncio
|
|
@@ -29,13 +30,80 @@ class GeminiAuthBase:
|
|
| 29 |
self._credentials_cache: Dict[str, Dict[str, Any]] = {}
|
| 30 |
self._refresh_locks: Dict[str, asyncio.Lock] = {}
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
async def _load_credentials(self, path: str) -> Dict[str, Any]:
|
| 33 |
if path in self._credentials_cache:
|
| 34 |
return self._credentials_cache[path]
|
| 35 |
-
|
| 36 |
async with self._get_lock(path):
|
| 37 |
if path in self._credentials_cache:
|
| 38 |
return self._credentials_cache[path]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
try:
|
| 40 |
lib_logger.debug(f"Loading Gemini credentials from file: {path}")
|
| 41 |
with open(path, 'r') as f:
|
|
@@ -52,6 +120,12 @@ class GeminiAuthBase:
|
|
| 52 |
|
| 53 |
async def _save_credentials(self, path: str, creds: Dict[str, Any]):
|
| 54 |
self._credentials_cache[path] = creds
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
try:
|
| 56 |
with open(path, 'w') as f:
|
| 57 |
json.dump(creds, f, indent=2)
|
|
|
|
| 1 |
# src/rotator_library/providers/gemini_auth_base.py
|
| 2 |
|
| 3 |
+
import os
|
| 4 |
import webbrowser
|
| 5 |
+
from typing import Union, Optional
|
| 6 |
import json
|
| 7 |
import time
|
| 8 |
import asyncio
|
|
|
|
| 30 |
self._credentials_cache: Dict[str, Dict[str, Any]] = {}
|
| 31 |
self._refresh_locks: Dict[str, asyncio.Lock] = {}
|
| 32 |
|
| 33 |
+
def _load_from_env(self) -> Optional[Dict[str, Any]]:
|
| 34 |
+
"""
|
| 35 |
+
Load OAuth credentials from environment variables for stateless deployments.
|
| 36 |
+
|
| 37 |
+
Expected environment variables:
|
| 38 |
+
- GEMINI_CLI_ACCESS_TOKEN (required)
|
| 39 |
+
- GEMINI_CLI_REFRESH_TOKEN (required)
|
| 40 |
+
- GEMINI_CLI_EXPIRY_DATE (optional, defaults to 0)
|
| 41 |
+
- GEMINI_CLI_CLIENT_ID (optional, uses default)
|
| 42 |
+
- GEMINI_CLI_CLIENT_SECRET (optional, uses default)
|
| 43 |
+
- GEMINI_CLI_TOKEN_URI (optional, uses default)
|
| 44 |
+
- GEMINI_CLI_UNIVERSE_DOMAIN (optional, defaults to googleapis.com)
|
| 45 |
+
- GEMINI_CLI_EMAIL (optional, defaults to "env-user")
|
| 46 |
+
- GEMINI_CLI_PROJECT_ID (optional)
|
| 47 |
+
|
| 48 |
+
Returns:
|
| 49 |
+
Dict with credential structure if env vars present, None otherwise
|
| 50 |
+
"""
|
| 51 |
+
access_token = os.getenv("GEMINI_CLI_ACCESS_TOKEN")
|
| 52 |
+
refresh_token = os.getenv("GEMINI_CLI_REFRESH_TOKEN")
|
| 53 |
+
|
| 54 |
+
# Both access and refresh tokens are required
|
| 55 |
+
if not (access_token and refresh_token):
|
| 56 |
+
return None
|
| 57 |
+
|
| 58 |
+
lib_logger.debug("Loading Gemini CLI credentials from environment variables")
|
| 59 |
+
|
| 60 |
+
# Parse expiry_date as float, default to 0 if not present
|
| 61 |
+
expiry_str = os.getenv("GEMINI_CLI_EXPIRY_DATE", "0")
|
| 62 |
+
try:
|
| 63 |
+
expiry_date = float(expiry_str)
|
| 64 |
+
except ValueError:
|
| 65 |
+
lib_logger.warning(f"Invalid GEMINI_CLI_EXPIRY_DATE value: {expiry_str}, using 0")
|
| 66 |
+
expiry_date = 0
|
| 67 |
+
|
| 68 |
+
creds = {
|
| 69 |
+
"access_token": access_token,
|
| 70 |
+
"refresh_token": refresh_token,
|
| 71 |
+
"expiry_date": expiry_date,
|
| 72 |
+
"client_id": os.getenv("GEMINI_CLI_CLIENT_ID", CLIENT_ID),
|
| 73 |
+
"client_secret": os.getenv("GEMINI_CLI_CLIENT_SECRET", CLIENT_SECRET),
|
| 74 |
+
"token_uri": os.getenv("GEMINI_CLI_TOKEN_URI", TOKEN_URI),
|
| 75 |
+
"universe_domain": os.getenv("GEMINI_CLI_UNIVERSE_DOMAIN", "googleapis.com"),
|
| 76 |
+
"_proxy_metadata": {
|
| 77 |
+
"email": os.getenv("GEMINI_CLI_EMAIL", "env-user"),
|
| 78 |
+
"last_check_timestamp": time.time(),
|
| 79 |
+
"loaded_from_env": True # Flag to indicate env-based credentials
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
# Add project_id if provided
|
| 84 |
+
project_id = os.getenv("GEMINI_CLI_PROJECT_ID")
|
| 85 |
+
if project_id:
|
| 86 |
+
creds["_proxy_metadata"]["project_id"] = project_id
|
| 87 |
+
|
| 88 |
+
return creds
|
| 89 |
+
|
| 90 |
async def _load_credentials(self, path: str) -> Dict[str, Any]:
|
| 91 |
if path in self._credentials_cache:
|
| 92 |
return self._credentials_cache[path]
|
| 93 |
+
|
| 94 |
async with self._get_lock(path):
|
| 95 |
if path in self._credentials_cache:
|
| 96 |
return self._credentials_cache[path]
|
| 97 |
+
|
| 98 |
+
# First, try loading from environment variables
|
| 99 |
+
env_creds = self._load_from_env()
|
| 100 |
+
if env_creds:
|
| 101 |
+
lib_logger.info("Using Gemini CLI credentials from environment variables")
|
| 102 |
+
# Cache env-based credentials using the path as key
|
| 103 |
+
self._credentials_cache[path] = env_creds
|
| 104 |
+
return env_creds
|
| 105 |
+
|
| 106 |
+
# Fall back to file-based loading
|
| 107 |
try:
|
| 108 |
lib_logger.debug(f"Loading Gemini credentials from file: {path}")
|
| 109 |
with open(path, 'r') as f:
|
|
|
|
| 120 |
|
| 121 |
async def _save_credentials(self, path: str, creds: Dict[str, Any]):
|
| 122 |
self._credentials_cache[path] = creds
|
| 123 |
+
|
| 124 |
+
# Don't save to file if credentials were loaded from environment
|
| 125 |
+
if creds.get("_proxy_metadata", {}).get("loaded_from_env"):
|
| 126 |
+
lib_logger.debug("Credentials loaded from env, skipping file save")
|
| 127 |
+
return
|
| 128 |
+
|
| 129 |
try:
|
| 130 |
with open(path, 'w') as f:
|
| 131 |
json.dump(creds, f, indent=2)
|
|
@@ -596,6 +596,7 @@ class GeminiCliProvider(GeminiAuthBase, ProviderInterface):
|
|
| 596 |
"User-Agent": "google-api-nodejs-client/9.15.1",
|
| 597 |
"X-Goog-Api-Client": "gl-node/22.17.0",
|
| 598 |
"Client-Metadata": "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI",
|
|
|
|
| 599 |
})
|
| 600 |
try:
|
| 601 |
async with client.stream("POST", url, headers=final_headers, json=request_payload, params={"alt": "sse"}, timeout=600) as response:
|
|
|
|
| 596 |
"User-Agent": "google-api-nodejs-client/9.15.1",
|
| 597 |
"X-Goog-Api-Client": "gl-node/22.17.0",
|
| 598 |
"Client-Metadata": "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI",
|
| 599 |
+
"Accept": "application/json",
|
| 600 |
})
|
| 601 |
try:
|
| 602 |
async with client.stream("POST", url, headers=final_headers, json=request_payload, params={"alt": "sse"}, timeout=600) as response:
|