@asm-agent/ai 0.9.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"env-api-keys.d.ts","sourceRoot":"","sources":["../src/env-api-keys.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAqIhD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;AAC3E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;AASpE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC;AAC1E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AA4CnE,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAiBnD","sourcesContent":["import type { existsSync, readFileSync } from \"node:fs\";\nimport type { homedir } from \"node:os\";\nimport type { join } from \"node:path\";\nimport type { KnownProvider } from \"./types.js\";\n\n// NEVER convert to top-level runtime imports - breaks browser/Vite builds\nlet _existsSync: typeof existsSync | null = null;\nlet _readFileSync: typeof readFileSync | null = null;\nlet _homedir: typeof homedir | null = null;\nlet _join: typeof join | null = null;\n\ntype DynamicImport = (specifier: string) => Promise<unknown>;\n\nconst dynamicImport: DynamicImport = (specifier) => import(specifier);\nconst NODE_FS_SPECIFIER = \"node:\" + \"fs\";\nconst NODE_OS_SPECIFIER = \"node:\" + \"os\";\nconst NODE_PATH_SPECIFIER = \"node:\" + \"path\";\n\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\tdynamicImport(NODE_FS_SPECIFIER).then((m) => {\n\t\t_existsSync = (m as { existsSync: typeof existsSync }).existsSync;\n\t\t_readFileSync = (m as { readFileSync: typeof readFileSync }).readFileSync;\n\t});\n\tdynamicImport(NODE_OS_SPECIFIER).then((m) => {\n\t\t_homedir = (m as { homedir: typeof homedir }).homedir;\n\t});\n\tdynamicImport(NODE_PATH_SPECIFIER).then((m) => {\n\t\t_join = (m as { join: typeof join }).join;\n\t});\n}\n\nlet _procEnvCache: Map<string, string> | null = null;\n\n/**\n * Fallback for https://github.com/oven-sh/bun/issues/27802\n * Bun compiled binaries have an empty `process.env` inside sandbox\n * environments on Linux. We can recover the env from `/proc/self/environ`.\n */\nfunction getProcEnv(key: string): string | undefined {\n\tif (typeof process === \"undefined\" || !process.versions?.bun) return undefined;\n\n\t// If process.env already has entries, the bug is not triggered.\n\tif (Object.keys(process.env).length > 0) return undefined;\n\n\tif (_procEnvCache === null) {\n\t\t_procEnvCache = new Map();\n\t\ttry {\n\t\t\tconst { readFileSync: readProcEnvFile } = require(\"node:fs\") as { readFileSync: typeof readFileSync };\n\t\t\tconst data = readProcEnvFile(\"/proc/self/environ\", \"utf-8\");\n\t\t\tfor (const entry of data.split(\"\\0\")) {\n\t\t\t\tconst idx = entry.indexOf(\"=\");\n\t\t\t\tif (idx > 0) {\n\t\t\t\t\t_procEnvCache.set(entry.slice(0, idx), entry.slice(idx + 1));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// /proc/self/environ may not be readable.\n\t\t}\n\t}\n\n\treturn _procEnvCache.get(key);\n}\n\nlet cachedVertexAdcCredentialsExists: boolean | null = null;\n\nfunction hasVertexAdcCredentials(): boolean {\n\tif (cachedVertexAdcCredentialsExists === null) {\n\t\t// If node modules haven't loaded yet (async import race at startup),\n\t\t// return false WITHOUT caching so the next call retries once they're ready.\n\t\t// Only cache false permanently in a browser environment where fs is never available.\n\t\tif (!_existsSync || !_homedir || !_join) {\n\t\t\tconst isNode = typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun);\n\t\t\tif (!isNode) {\n\t\t\t\t// Definitively in a browser — safe to cache false permanently\n\t\t\t\tcachedVertexAdcCredentialsExists = false;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tconst gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS || getProcEnv(\"GOOGLE_APPLICATION_CREDENTIALS\");\n\t\tif (gacPath) {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(gacPath);\n\t\t} else {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(\n\t\t\t\t_join(_homedir(), \".config\", \"gcloud\", \"application_default_credentials.json\"),\n\t\t\t);\n\t\t}\n\t}\n\treturn cachedVertexAdcCredentialsExists;\n}\n\nfunction getApiKeyEnvVars(provider: string): readonly string[] | undefined {\n\tif (provider === \"github-copilot\") {\n\t\treturn [\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"];\n\t}\n\n\t// ANTHROPIC_OAUTH_TOKEN takes precedence over ANTHROPIC_API_KEY\n\tif (provider === \"anthropic\") {\n\t\treturn [\"ANTHROPIC_OAUTH_TOKEN\", \"ANTHROPIC_API_KEY\"];\n\t}\n\n\tconst envMap: Record<string, string> = {\n\t\topenai: \"OPENAI_API_KEY\",\n\t\t\"azure-openai-responses\": \"AZURE_OPENAI_API_KEY\",\n\t\t\"prime-inference\": \"PRIME_API_KEY\",\n\t\tdeepseek: \"DEEPSEEK_API_KEY\",\n\t\tgoogle: \"GEMINI_API_KEY\",\n\t\t\"google-vertex\": \"GOOGLE_CLOUD_API_KEY\",\n\t\tgroq: \"GROQ_API_KEY\",\n\t\tcerebras: \"CEREBRAS_API_KEY\",\n\t\txai: \"XAI_API_KEY\",\n\t\topenrouter: \"OPENROUTER_API_KEY\",\n\t\t\"vercel-ai-gateway\": \"AI_GATEWAY_API_KEY\",\n\t\tzai: \"ZAI_API_KEY\",\n\t\tmistral: \"MISTRAL_API_KEY\",\n\t\tminimax: \"MINIMAX_API_KEY\",\n\t\t\"minimax-cn\": \"MINIMAX_CN_API_KEY\",\n\t\tmoonshotai: \"MOONSHOT_API_KEY\",\n\t\t\"moonshotai-cn\": \"MOONSHOT_API_KEY\",\n\t\thuggingface: \"HF_TOKEN\",\n\t\tfireworks: \"FIREWORKS_API_KEY\",\n\t\topencode: \"OPENCODE_API_KEY\",\n\t\t\"opencode-go\": \"OPENCODE_API_KEY\",\n\t\t\"kimi-coding\": \"KIMI_API_KEY\",\n\t\t\"cloudflare-workers-ai\": \"CLOUDFLARE_API_KEY\",\n\t\t\"cloudflare-ai-gateway\": \"CLOUDFLARE_API_KEY\",\n\t\txiaomi: \"XIAOMI_API_KEY\",\n\t\t\"xiaomi-token-plan-cn\": \"XIAOMI_TOKEN_PLAN_CN_API_KEY\",\n\t\t\"xiaomi-token-plan-ams\": \"XIAOMI_TOKEN_PLAN_AMS_API_KEY\",\n\t\t\"xiaomi-token-plan-sgp\": \"XIAOMI_TOKEN_PLAN_SGP_API_KEY\",\n\t};\n\n\tconst envVar = envMap[provider];\n\treturn envVar ? [envVar] : undefined;\n}\n\n/**\n * Find configured environment variables that can provide an API key for a provider.\n *\n * This only reports actual API key variables. It intentionally excludes ambient\n * credential sources such as AWS profiles, AWS IAM credentials, and Google\n * Application Default Credentials.\n */\nexport function findEnvKeys(provider: KnownProvider): string[] | undefined;\nexport function findEnvKeys(provider: string): string[] | undefined;\nexport function findEnvKeys(provider: string): string[] | undefined {\n\tconst envVars = getApiKeyEnvVars(provider);\n\tif (!envVars) return undefined;\n\n\tconst found = envVars.filter((envVar) => !!process.env[envVar] || !!getProcEnv(envVar));\n\treturn found.length > 0 ? found : undefined;\n}\n\n/**\n * Get API key for provider from known environment variables, e.g. OPENAI_API_KEY.\n *\n * Will not return API keys for providers that require OAuth tokens.\n */\nexport function getEnvApiKey(provider: KnownProvider): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined {\n\tconst envKeys = findEnvKeys(provider);\n\tif (envKeys?.[0]) {\n\t\treturn process.env[envKeys[0]] || getProcEnv(envKeys[0]);\n\t}\n\n\tif (provider === \"google-vertex\") {\n\t\tconst hasCredentials = hasVertexAdcCredentials();\n\t\tconst hasProject = !!(\n\t\t\tprocess.env.GOOGLE_CLOUD_PROJECT ||\n\t\t\tprocess.env.GCLOUD_PROJECT ||\n\t\t\tgetProcEnv(\"GOOGLE_CLOUD_PROJECT\") ||\n\t\t\tgetProcEnv(\"GCLOUD_PROJECT\")\n\t\t);\n\t\tconst hasLocation = !!(process.env.GOOGLE_CLOUD_LOCATION || getProcEnv(\"GOOGLE_CLOUD_LOCATION\"));\n\n\t\tif (hasCredentials && hasProject && hasLocation) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\tif (provider === \"amazon-bedrock\") {\n\t\tif (\n\t\t\tprocess.env.AWS_PROFILE ||\n\t\t\t(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ||\n\t\t\tprocess.env.AWS_BEARER_TOKEN_BEDROCK ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_FULL_URI ||\n\t\t\tprocess.env.AWS_WEB_IDENTITY_TOKEN_FILE ||\n\t\t\tgetProcEnv(\"AWS_PROFILE\") ||\n\t\t\t(getProcEnv(\"AWS_ACCESS_KEY_ID\") && getProcEnv(\"AWS_SECRET_ACCESS_KEY\")) ||\n\t\t\tgetProcEnv(\"AWS_BEARER_TOKEN_BEDROCK\") ||\n\t\t\tgetProcEnv(\"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\") ||\n\t\t\tgetProcEnv(\"AWS_CONTAINER_CREDENTIALS_FULL_URI\") ||\n\t\t\tgetProcEnv(\"AWS_WEB_IDENTITY_TOKEN_FILE\")\n\t\t) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport function getPrimeTeamId(): string | undefined {\n\tconst fromEnv = process.env.PRIME_TEAM_ID || getProcEnv(\"PRIME_TEAM_ID\");\n\tif (fromEnv?.trim()) return fromEnv.trim();\n\n\tif (!_existsSync || !_readFileSync || !_homedir || !_join) return undefined;\n\tconst configPath = _join(_homedir(), \".prime\", \"config.json\");\n\tif (!_existsSync(configPath)) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(_readFileSync(configPath, \"utf-8\")) as unknown;\n\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\tconst teamId = (parsed as Record<string, unknown>).team_id;\n\t\t\tif (typeof teamId === \"string\" && teamId.trim()) return teamId.trim();\n\t\t}\n\t} catch {\n\t\t// Treat unreadable or malformed config as no configured team.\n\t}\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"env-api-keys.d.ts","sourceRoot":"","sources":["../src/env-api-keys.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAsIhD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;AAC3E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;AASpE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC;AAC1E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AA4CnE,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAiBnD","sourcesContent":["import type { existsSync, readFileSync } from \"node:fs\";\nimport type { homedir } from \"node:os\";\nimport type { join } from \"node:path\";\nimport type { KnownProvider } from \"./types.js\";\n\n// NEVER convert to top-level runtime imports - breaks browser/Vite builds\nlet _existsSync: typeof existsSync | null = null;\nlet _readFileSync: typeof readFileSync | null = null;\nlet _homedir: typeof homedir | null = null;\nlet _join: typeof join | null = null;\n\ntype DynamicImport = (specifier: string) => Promise<unknown>;\n\nconst dynamicImport: DynamicImport = (specifier) => import(specifier);\nconst NODE_FS_SPECIFIER = \"node:\" + \"fs\";\nconst NODE_OS_SPECIFIER = \"node:\" + \"os\";\nconst NODE_PATH_SPECIFIER = \"node:\" + \"path\";\n\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\tdynamicImport(NODE_FS_SPECIFIER).then((m) => {\n\t\t_existsSync = (m as { existsSync: typeof existsSync }).existsSync;\n\t\t_readFileSync = (m as { readFileSync: typeof readFileSync }).readFileSync;\n\t});\n\tdynamicImport(NODE_OS_SPECIFIER).then((m) => {\n\t\t_homedir = (m as { homedir: typeof homedir }).homedir;\n\t});\n\tdynamicImport(NODE_PATH_SPECIFIER).then((m) => {\n\t\t_join = (m as { join: typeof join }).join;\n\t});\n}\n\nlet _procEnvCache: Map<string, string> | null = null;\n\n/**\n * Fallback for https://github.com/oven-sh/bun/issues/27802\n * Bun compiled binaries have an empty `process.env` inside sandbox\n * environments on Linux. We can recover the env from `/proc/self/environ`.\n */\nfunction getProcEnv(key: string): string | undefined {\n\tif (typeof process === \"undefined\" || !process.versions?.bun) return undefined;\n\n\t// If process.env already has entries, the bug is not triggered.\n\tif (Object.keys(process.env).length > 0) return undefined;\n\n\tif (_procEnvCache === null) {\n\t\t_procEnvCache = new Map();\n\t\ttry {\n\t\t\tconst { readFileSync: readProcEnvFile } = require(\"node:fs\") as { readFileSync: typeof readFileSync };\n\t\t\tconst data = readProcEnvFile(\"/proc/self/environ\", \"utf-8\");\n\t\t\tfor (const entry of data.split(\"\\0\")) {\n\t\t\t\tconst idx = entry.indexOf(\"=\");\n\t\t\t\tif (idx > 0) {\n\t\t\t\t\t_procEnvCache.set(entry.slice(0, idx), entry.slice(idx + 1));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// /proc/self/environ may not be readable.\n\t\t}\n\t}\n\n\treturn _procEnvCache.get(key);\n}\n\nlet cachedVertexAdcCredentialsExists: boolean | null = null;\n\nfunction hasVertexAdcCredentials(): boolean {\n\tif (cachedVertexAdcCredentialsExists === null) {\n\t\t// If node modules haven't loaded yet (async import race at startup),\n\t\t// return false WITHOUT caching so the next call retries once they're ready.\n\t\t// Only cache false permanently in a browser environment where fs is never available.\n\t\tif (!_existsSync || !_homedir || !_join) {\n\t\t\tconst isNode = typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun);\n\t\t\tif (!isNode) {\n\t\t\t\t// Definitively in a browser — safe to cache false permanently\n\t\t\t\tcachedVertexAdcCredentialsExists = false;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tconst gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS || getProcEnv(\"GOOGLE_APPLICATION_CREDENTIALS\");\n\t\tif (gacPath) {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(gacPath);\n\t\t} else {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(\n\t\t\t\t_join(_homedir(), \".config\", \"gcloud\", \"application_default_credentials.json\"),\n\t\t\t);\n\t\t}\n\t}\n\treturn cachedVertexAdcCredentialsExists;\n}\n\nfunction getApiKeyEnvVars(provider: string): readonly string[] | undefined {\n\tif (provider === \"github-copilot\") {\n\t\treturn [\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"];\n\t}\n\n\t// ANTHROPIC_OAUTH_TOKEN takes precedence over ANTHROPIC_API_KEY\n\tif (provider === \"anthropic\") {\n\t\treturn [\"ANTHROPIC_OAUTH_TOKEN\", \"ANTHROPIC_API_KEY\"];\n\t}\n\n\tconst envMap: Record<string, string> = {\n\t\topenai: \"OPENAI_API_KEY\",\n\t\t\"azure-openai-responses\": \"AZURE_OPENAI_API_KEY\",\n\t\t\"prime-inference\": \"PRIME_API_KEY\",\n\t\tdeepseek: \"DEEPSEEK_API_KEY\",\n\t\tgoogle: \"GEMINI_API_KEY\",\n\t\tgemini: \"GEMINI_API_KEY\",\n\t\t\"google-vertex\": \"GOOGLE_CLOUD_API_KEY\",\n\t\tgroq: \"GROQ_API_KEY\",\n\t\tcerebras: \"CEREBRAS_API_KEY\",\n\t\txai: \"XAI_API_KEY\",\n\t\topenrouter: \"OPENROUTER_API_KEY\",\n\t\t\"vercel-ai-gateway\": \"AI_GATEWAY_API_KEY\",\n\t\tzai: \"ZAI_API_KEY\",\n\t\tmistral: \"MISTRAL_API_KEY\",\n\t\tminimax: \"MINIMAX_API_KEY\",\n\t\t\"minimax-cn\": \"MINIMAX_CN_API_KEY\",\n\t\tmoonshotai: \"MOONSHOT_API_KEY\",\n\t\t\"moonshotai-cn\": \"MOONSHOT_API_KEY\",\n\t\thuggingface: \"HF_TOKEN\",\n\t\tfireworks: \"FIREWORKS_API_KEY\",\n\t\topencode: \"OPENCODE_API_KEY\",\n\t\t\"opencode-go\": \"OPENCODE_API_KEY\",\n\t\t\"kimi-coding\": \"KIMI_API_KEY\",\n\t\t\"cloudflare-workers-ai\": \"CLOUDFLARE_API_KEY\",\n\t\t\"cloudflare-ai-gateway\": \"CLOUDFLARE_API_KEY\",\n\t\txiaomi: \"XIAOMI_API_KEY\",\n\t\t\"xiaomi-token-plan-cn\": \"XIAOMI_TOKEN_PLAN_CN_API_KEY\",\n\t\t\"xiaomi-token-plan-ams\": \"XIAOMI_TOKEN_PLAN_AMS_API_KEY\",\n\t\t\"xiaomi-token-plan-sgp\": \"XIAOMI_TOKEN_PLAN_SGP_API_KEY\",\n\t};\n\n\tconst envVar = envMap[provider];\n\treturn envVar ? [envVar] : undefined;\n}\n\n/**\n * Find configured environment variables that can provide an API key for a provider.\n *\n * This only reports actual API key variables. It intentionally excludes ambient\n * credential sources such as AWS profiles, AWS IAM credentials, and Google\n * Application Default Credentials.\n */\nexport function findEnvKeys(provider: KnownProvider): string[] | undefined;\nexport function findEnvKeys(provider: string): string[] | undefined;\nexport function findEnvKeys(provider: string): string[] | undefined {\n\tconst envVars = getApiKeyEnvVars(provider);\n\tif (!envVars) return undefined;\n\n\tconst found = envVars.filter((envVar) => !!process.env[envVar] || !!getProcEnv(envVar));\n\treturn found.length > 0 ? found : undefined;\n}\n\n/**\n * Get API key for provider from known environment variables, e.g. OPENAI_API_KEY.\n *\n * Will not return API keys for providers that require OAuth tokens.\n */\nexport function getEnvApiKey(provider: KnownProvider): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined {\n\tconst envKeys = findEnvKeys(provider);\n\tif (envKeys?.[0]) {\n\t\treturn process.env[envKeys[0]] || getProcEnv(envKeys[0]);\n\t}\n\n\tif (provider === \"google-vertex\") {\n\t\tconst hasCredentials = hasVertexAdcCredentials();\n\t\tconst hasProject = !!(\n\t\t\tprocess.env.GOOGLE_CLOUD_PROJECT ||\n\t\t\tprocess.env.GCLOUD_PROJECT ||\n\t\t\tgetProcEnv(\"GOOGLE_CLOUD_PROJECT\") ||\n\t\t\tgetProcEnv(\"GCLOUD_PROJECT\")\n\t\t);\n\t\tconst hasLocation = !!(process.env.GOOGLE_CLOUD_LOCATION || getProcEnv(\"GOOGLE_CLOUD_LOCATION\"));\n\n\t\tif (hasCredentials && hasProject && hasLocation) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\tif (provider === \"amazon-bedrock\") {\n\t\tif (\n\t\t\tprocess.env.AWS_PROFILE ||\n\t\t\t(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ||\n\t\t\tprocess.env.AWS_BEARER_TOKEN_BEDROCK ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_FULL_URI ||\n\t\t\tprocess.env.AWS_WEB_IDENTITY_TOKEN_FILE ||\n\t\t\tgetProcEnv(\"AWS_PROFILE\") ||\n\t\t\t(getProcEnv(\"AWS_ACCESS_KEY_ID\") && getProcEnv(\"AWS_SECRET_ACCESS_KEY\")) ||\n\t\t\tgetProcEnv(\"AWS_BEARER_TOKEN_BEDROCK\") ||\n\t\t\tgetProcEnv(\"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\") ||\n\t\t\tgetProcEnv(\"AWS_CONTAINER_CREDENTIALS_FULL_URI\") ||\n\t\t\tgetProcEnv(\"AWS_WEB_IDENTITY_TOKEN_FILE\")\n\t\t) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport function getPrimeTeamId(): string | undefined {\n\tconst fromEnv = process.env.PRIME_TEAM_ID || getProcEnv(\"PRIME_TEAM_ID\");\n\tif (fromEnv?.trim()) return fromEnv.trim();\n\n\tif (!_existsSync || !_readFileSync || !_homedir || !_join) return undefined;\n\tconst configPath = _join(_homedir(), \".prime\", \"config.json\");\n\tif (!_existsSync(configPath)) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(_readFileSync(configPath, \"utf-8\")) as unknown;\n\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\tconst teamId = (parsed as Record<string, unknown>).team_id;\n\t\t\tif (typeof teamId === \"string\" && teamId.trim()) return teamId.trim();\n\t\t}\n\t} catch {\n\t\t// Treat unreadable or malformed config as no configured team.\n\t}\n\treturn undefined;\n}\n"]}
@@ -87,6 +87,7 @@ function getApiKeyEnvVars(provider) {
87
87
  "prime-inference": "PRIME_API_KEY",
88
88
  deepseek: "DEEPSEEK_API_KEY",
89
89
  google: "GEMINI_API_KEY",
90
+ gemini: "GEMINI_API_KEY",
90
91
  "google-vertex": "GOOGLE_CLOUD_API_KEY",
91
92
  groq: "GROQ_API_KEY",
92
93
  cerebras: "CEREBRAS_API_KEY",
@@ -1 +1 @@
1
- {"version":3,"file":"env-api-keys.js","sourceRoot":"","sources":["../src/env-api-keys.ts"],"names":[],"mappings":"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","sourcesContent":["import type { existsSync, readFileSync } from \"node:fs\";\nimport type { homedir } from \"node:os\";\nimport type { join } from \"node:path\";\nimport type { KnownProvider } from \"./types.js\";\n\n// NEVER convert to top-level runtime imports - breaks browser/Vite builds\nlet _existsSync: typeof existsSync | null = null;\nlet _readFileSync: typeof readFileSync | null = null;\nlet _homedir: typeof homedir | null = null;\nlet _join: typeof join | null = null;\n\ntype DynamicImport = (specifier: string) => Promise<unknown>;\n\nconst dynamicImport: DynamicImport = (specifier) => import(specifier);\nconst NODE_FS_SPECIFIER = \"node:\" + \"fs\";\nconst NODE_OS_SPECIFIER = \"node:\" + \"os\";\nconst NODE_PATH_SPECIFIER = \"node:\" + \"path\";\n\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\tdynamicImport(NODE_FS_SPECIFIER).then((m) => {\n\t\t_existsSync = (m as { existsSync: typeof existsSync }).existsSync;\n\t\t_readFileSync = (m as { readFileSync: typeof readFileSync }).readFileSync;\n\t});\n\tdynamicImport(NODE_OS_SPECIFIER).then((m) => {\n\t\t_homedir = (m as { homedir: typeof homedir }).homedir;\n\t});\n\tdynamicImport(NODE_PATH_SPECIFIER).then((m) => {\n\t\t_join = (m as { join: typeof join }).join;\n\t});\n}\n\nlet _procEnvCache: Map<string, string> | null = null;\n\n/**\n * Fallback for https://github.com/oven-sh/bun/issues/27802\n * Bun compiled binaries have an empty `process.env` inside sandbox\n * environments on Linux. We can recover the env from `/proc/self/environ`.\n */\nfunction getProcEnv(key: string): string | undefined {\n\tif (typeof process === \"undefined\" || !process.versions?.bun) return undefined;\n\n\t// If process.env already has entries, the bug is not triggered.\n\tif (Object.keys(process.env).length > 0) return undefined;\n\n\tif (_procEnvCache === null) {\n\t\t_procEnvCache = new Map();\n\t\ttry {\n\t\t\tconst { readFileSync: readProcEnvFile } = require(\"node:fs\") as { readFileSync: typeof readFileSync };\n\t\t\tconst data = readProcEnvFile(\"/proc/self/environ\", \"utf-8\");\n\t\t\tfor (const entry of data.split(\"\\0\")) {\n\t\t\t\tconst idx = entry.indexOf(\"=\");\n\t\t\t\tif (idx > 0) {\n\t\t\t\t\t_procEnvCache.set(entry.slice(0, idx), entry.slice(idx + 1));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// /proc/self/environ may not be readable.\n\t\t}\n\t}\n\n\treturn _procEnvCache.get(key);\n}\n\nlet cachedVertexAdcCredentialsExists: boolean | null = null;\n\nfunction hasVertexAdcCredentials(): boolean {\n\tif (cachedVertexAdcCredentialsExists === null) {\n\t\t// If node modules haven't loaded yet (async import race at startup),\n\t\t// return false WITHOUT caching so the next call retries once they're ready.\n\t\t// Only cache false permanently in a browser environment where fs is never available.\n\t\tif (!_existsSync || !_homedir || !_join) {\n\t\t\tconst isNode = typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun);\n\t\t\tif (!isNode) {\n\t\t\t\t// Definitively in a browser — safe to cache false permanently\n\t\t\t\tcachedVertexAdcCredentialsExists = false;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tconst gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS || getProcEnv(\"GOOGLE_APPLICATION_CREDENTIALS\");\n\t\tif (gacPath) {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(gacPath);\n\t\t} else {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(\n\t\t\t\t_join(_homedir(), \".config\", \"gcloud\", \"application_default_credentials.json\"),\n\t\t\t);\n\t\t}\n\t}\n\treturn cachedVertexAdcCredentialsExists;\n}\n\nfunction getApiKeyEnvVars(provider: string): readonly string[] | undefined {\n\tif (provider === \"github-copilot\") {\n\t\treturn [\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"];\n\t}\n\n\t// ANTHROPIC_OAUTH_TOKEN takes precedence over ANTHROPIC_API_KEY\n\tif (provider === \"anthropic\") {\n\t\treturn [\"ANTHROPIC_OAUTH_TOKEN\", \"ANTHROPIC_API_KEY\"];\n\t}\n\n\tconst envMap: Record<string, string> = {\n\t\topenai: \"OPENAI_API_KEY\",\n\t\t\"azure-openai-responses\": \"AZURE_OPENAI_API_KEY\",\n\t\t\"prime-inference\": \"PRIME_API_KEY\",\n\t\tdeepseek: \"DEEPSEEK_API_KEY\",\n\t\tgoogle: \"GEMINI_API_KEY\",\n\t\t\"google-vertex\": \"GOOGLE_CLOUD_API_KEY\",\n\t\tgroq: \"GROQ_API_KEY\",\n\t\tcerebras: \"CEREBRAS_API_KEY\",\n\t\txai: \"XAI_API_KEY\",\n\t\topenrouter: \"OPENROUTER_API_KEY\",\n\t\t\"vercel-ai-gateway\": \"AI_GATEWAY_API_KEY\",\n\t\tzai: \"ZAI_API_KEY\",\n\t\tmistral: \"MISTRAL_API_KEY\",\n\t\tminimax: \"MINIMAX_API_KEY\",\n\t\t\"minimax-cn\": \"MINIMAX_CN_API_KEY\",\n\t\tmoonshotai: \"MOONSHOT_API_KEY\",\n\t\t\"moonshotai-cn\": \"MOONSHOT_API_KEY\",\n\t\thuggingface: \"HF_TOKEN\",\n\t\tfireworks: \"FIREWORKS_API_KEY\",\n\t\topencode: \"OPENCODE_API_KEY\",\n\t\t\"opencode-go\": \"OPENCODE_API_KEY\",\n\t\t\"kimi-coding\": \"KIMI_API_KEY\",\n\t\t\"cloudflare-workers-ai\": \"CLOUDFLARE_API_KEY\",\n\t\t\"cloudflare-ai-gateway\": \"CLOUDFLARE_API_KEY\",\n\t\txiaomi: \"XIAOMI_API_KEY\",\n\t\t\"xiaomi-token-plan-cn\": \"XIAOMI_TOKEN_PLAN_CN_API_KEY\",\n\t\t\"xiaomi-token-plan-ams\": \"XIAOMI_TOKEN_PLAN_AMS_API_KEY\",\n\t\t\"xiaomi-token-plan-sgp\": \"XIAOMI_TOKEN_PLAN_SGP_API_KEY\",\n\t};\n\n\tconst envVar = envMap[provider];\n\treturn envVar ? [envVar] : undefined;\n}\n\n/**\n * Find configured environment variables that can provide an API key for a provider.\n *\n * This only reports actual API key variables. It intentionally excludes ambient\n * credential sources such as AWS profiles, AWS IAM credentials, and Google\n * Application Default Credentials.\n */\nexport function findEnvKeys(provider: KnownProvider): string[] | undefined;\nexport function findEnvKeys(provider: string): string[] | undefined;\nexport function findEnvKeys(provider: string): string[] | undefined {\n\tconst envVars = getApiKeyEnvVars(provider);\n\tif (!envVars) return undefined;\n\n\tconst found = envVars.filter((envVar) => !!process.env[envVar] || !!getProcEnv(envVar));\n\treturn found.length > 0 ? found : undefined;\n}\n\n/**\n * Get API key for provider from known environment variables, e.g. OPENAI_API_KEY.\n *\n * Will not return API keys for providers that require OAuth tokens.\n */\nexport function getEnvApiKey(provider: KnownProvider): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined {\n\tconst envKeys = findEnvKeys(provider);\n\tif (envKeys?.[0]) {\n\t\treturn process.env[envKeys[0]] || getProcEnv(envKeys[0]);\n\t}\n\n\tif (provider === \"google-vertex\") {\n\t\tconst hasCredentials = hasVertexAdcCredentials();\n\t\tconst hasProject = !!(\n\t\t\tprocess.env.GOOGLE_CLOUD_PROJECT ||\n\t\t\tprocess.env.GCLOUD_PROJECT ||\n\t\t\tgetProcEnv(\"GOOGLE_CLOUD_PROJECT\") ||\n\t\t\tgetProcEnv(\"GCLOUD_PROJECT\")\n\t\t);\n\t\tconst hasLocation = !!(process.env.GOOGLE_CLOUD_LOCATION || getProcEnv(\"GOOGLE_CLOUD_LOCATION\"));\n\n\t\tif (hasCredentials && hasProject && hasLocation) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\tif (provider === \"amazon-bedrock\") {\n\t\tif (\n\t\t\tprocess.env.AWS_PROFILE ||\n\t\t\t(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ||\n\t\t\tprocess.env.AWS_BEARER_TOKEN_BEDROCK ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_FULL_URI ||\n\t\t\tprocess.env.AWS_WEB_IDENTITY_TOKEN_FILE ||\n\t\t\tgetProcEnv(\"AWS_PROFILE\") ||\n\t\t\t(getProcEnv(\"AWS_ACCESS_KEY_ID\") && getProcEnv(\"AWS_SECRET_ACCESS_KEY\")) ||\n\t\t\tgetProcEnv(\"AWS_BEARER_TOKEN_BEDROCK\") ||\n\t\t\tgetProcEnv(\"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\") ||\n\t\t\tgetProcEnv(\"AWS_CONTAINER_CREDENTIALS_FULL_URI\") ||\n\t\t\tgetProcEnv(\"AWS_WEB_IDENTITY_TOKEN_FILE\")\n\t\t) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport function getPrimeTeamId(): string | undefined {\n\tconst fromEnv = process.env.PRIME_TEAM_ID || getProcEnv(\"PRIME_TEAM_ID\");\n\tif (fromEnv?.trim()) return fromEnv.trim();\n\n\tif (!_existsSync || !_readFileSync || !_homedir || !_join) return undefined;\n\tconst configPath = _join(_homedir(), \".prime\", \"config.json\");\n\tif (!_existsSync(configPath)) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(_readFileSync(configPath, \"utf-8\")) as unknown;\n\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\tconst teamId = (parsed as Record<string, unknown>).team_id;\n\t\t\tif (typeof teamId === \"string\" && teamId.trim()) return teamId.trim();\n\t\t}\n\t} catch {\n\t\t// Treat unreadable or malformed config as no configured team.\n\t}\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"env-api-keys.js","sourceRoot":"","sources":["../src/env-api-keys.ts"],"names":[],"mappings":"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","sourcesContent":["import type { existsSync, readFileSync } from \"node:fs\";\nimport type { homedir } from \"node:os\";\nimport type { join } from \"node:path\";\nimport type { KnownProvider } from \"./types.js\";\n\n// NEVER convert to top-level runtime imports - breaks browser/Vite builds\nlet _existsSync: typeof existsSync | null = null;\nlet _readFileSync: typeof readFileSync | null = null;\nlet _homedir: typeof homedir | null = null;\nlet _join: typeof join | null = null;\n\ntype DynamicImport = (specifier: string) => Promise<unknown>;\n\nconst dynamicImport: DynamicImport = (specifier) => import(specifier);\nconst NODE_FS_SPECIFIER = \"node:\" + \"fs\";\nconst NODE_OS_SPECIFIER = \"node:\" + \"os\";\nconst NODE_PATH_SPECIFIER = \"node:\" + \"path\";\n\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\tdynamicImport(NODE_FS_SPECIFIER).then((m) => {\n\t\t_existsSync = (m as { existsSync: typeof existsSync }).existsSync;\n\t\t_readFileSync = (m as { readFileSync: typeof readFileSync }).readFileSync;\n\t});\n\tdynamicImport(NODE_OS_SPECIFIER).then((m) => {\n\t\t_homedir = (m as { homedir: typeof homedir }).homedir;\n\t});\n\tdynamicImport(NODE_PATH_SPECIFIER).then((m) => {\n\t\t_join = (m as { join: typeof join }).join;\n\t});\n}\n\nlet _procEnvCache: Map<string, string> | null = null;\n\n/**\n * Fallback for https://github.com/oven-sh/bun/issues/27802\n * Bun compiled binaries have an empty `process.env` inside sandbox\n * environments on Linux. We can recover the env from `/proc/self/environ`.\n */\nfunction getProcEnv(key: string): string | undefined {\n\tif (typeof process === \"undefined\" || !process.versions?.bun) return undefined;\n\n\t// If process.env already has entries, the bug is not triggered.\n\tif (Object.keys(process.env).length > 0) return undefined;\n\n\tif (_procEnvCache === null) {\n\t\t_procEnvCache = new Map();\n\t\ttry {\n\t\t\tconst { readFileSync: readProcEnvFile } = require(\"node:fs\") as { readFileSync: typeof readFileSync };\n\t\t\tconst data = readProcEnvFile(\"/proc/self/environ\", \"utf-8\");\n\t\t\tfor (const entry of data.split(\"\\0\")) {\n\t\t\t\tconst idx = entry.indexOf(\"=\");\n\t\t\t\tif (idx > 0) {\n\t\t\t\t\t_procEnvCache.set(entry.slice(0, idx), entry.slice(idx + 1));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// /proc/self/environ may not be readable.\n\t\t}\n\t}\n\n\treturn _procEnvCache.get(key);\n}\n\nlet cachedVertexAdcCredentialsExists: boolean | null = null;\n\nfunction hasVertexAdcCredentials(): boolean {\n\tif (cachedVertexAdcCredentialsExists === null) {\n\t\t// If node modules haven't loaded yet (async import race at startup),\n\t\t// return false WITHOUT caching so the next call retries once they're ready.\n\t\t// Only cache false permanently in a browser environment where fs is never available.\n\t\tif (!_existsSync || !_homedir || !_join) {\n\t\t\tconst isNode = typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun);\n\t\t\tif (!isNode) {\n\t\t\t\t// Definitively in a browser — safe to cache false permanently\n\t\t\t\tcachedVertexAdcCredentialsExists = false;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tconst gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS || getProcEnv(\"GOOGLE_APPLICATION_CREDENTIALS\");\n\t\tif (gacPath) {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(gacPath);\n\t\t} else {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(\n\t\t\t\t_join(_homedir(), \".config\", \"gcloud\", \"application_default_credentials.json\"),\n\t\t\t);\n\t\t}\n\t}\n\treturn cachedVertexAdcCredentialsExists;\n}\n\nfunction getApiKeyEnvVars(provider: string): readonly string[] | undefined {\n\tif (provider === \"github-copilot\") {\n\t\treturn [\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"];\n\t}\n\n\t// ANTHROPIC_OAUTH_TOKEN takes precedence over ANTHROPIC_API_KEY\n\tif (provider === \"anthropic\") {\n\t\treturn [\"ANTHROPIC_OAUTH_TOKEN\", \"ANTHROPIC_API_KEY\"];\n\t}\n\n\tconst envMap: Record<string, string> = {\n\t\topenai: \"OPENAI_API_KEY\",\n\t\t\"azure-openai-responses\": \"AZURE_OPENAI_API_KEY\",\n\t\t\"prime-inference\": \"PRIME_API_KEY\",\n\t\tdeepseek: \"DEEPSEEK_API_KEY\",\n\t\tgoogle: \"GEMINI_API_KEY\",\n\t\tgemini: \"GEMINI_API_KEY\",\n\t\t\"google-vertex\": \"GOOGLE_CLOUD_API_KEY\",\n\t\tgroq: \"GROQ_API_KEY\",\n\t\tcerebras: \"CEREBRAS_API_KEY\",\n\t\txai: \"XAI_API_KEY\",\n\t\topenrouter: \"OPENROUTER_API_KEY\",\n\t\t\"vercel-ai-gateway\": \"AI_GATEWAY_API_KEY\",\n\t\tzai: \"ZAI_API_KEY\",\n\t\tmistral: \"MISTRAL_API_KEY\",\n\t\tminimax: \"MINIMAX_API_KEY\",\n\t\t\"minimax-cn\": \"MINIMAX_CN_API_KEY\",\n\t\tmoonshotai: \"MOONSHOT_API_KEY\",\n\t\t\"moonshotai-cn\": \"MOONSHOT_API_KEY\",\n\t\thuggingface: \"HF_TOKEN\",\n\t\tfireworks: \"FIREWORKS_API_KEY\",\n\t\topencode: \"OPENCODE_API_KEY\",\n\t\t\"opencode-go\": \"OPENCODE_API_KEY\",\n\t\t\"kimi-coding\": \"KIMI_API_KEY\",\n\t\t\"cloudflare-workers-ai\": \"CLOUDFLARE_API_KEY\",\n\t\t\"cloudflare-ai-gateway\": \"CLOUDFLARE_API_KEY\",\n\t\txiaomi: \"XIAOMI_API_KEY\",\n\t\t\"xiaomi-token-plan-cn\": \"XIAOMI_TOKEN_PLAN_CN_API_KEY\",\n\t\t\"xiaomi-token-plan-ams\": \"XIAOMI_TOKEN_PLAN_AMS_API_KEY\",\n\t\t\"xiaomi-token-plan-sgp\": \"XIAOMI_TOKEN_PLAN_SGP_API_KEY\",\n\t};\n\n\tconst envVar = envMap[provider];\n\treturn envVar ? [envVar] : undefined;\n}\n\n/**\n * Find configured environment variables that can provide an API key for a provider.\n *\n * This only reports actual API key variables. It intentionally excludes ambient\n * credential sources such as AWS profiles, AWS IAM credentials, and Google\n * Application Default Credentials.\n */\nexport function findEnvKeys(provider: KnownProvider): string[] | undefined;\nexport function findEnvKeys(provider: string): string[] | undefined;\nexport function findEnvKeys(provider: string): string[] | undefined {\n\tconst envVars = getApiKeyEnvVars(provider);\n\tif (!envVars) return undefined;\n\n\tconst found = envVars.filter((envVar) => !!process.env[envVar] || !!getProcEnv(envVar));\n\treturn found.length > 0 ? found : undefined;\n}\n\n/**\n * Get API key for provider from known environment variables, e.g. OPENAI_API_KEY.\n *\n * Will not return API keys for providers that require OAuth tokens.\n */\nexport function getEnvApiKey(provider: KnownProvider): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined {\n\tconst envKeys = findEnvKeys(provider);\n\tif (envKeys?.[0]) {\n\t\treturn process.env[envKeys[0]] || getProcEnv(envKeys[0]);\n\t}\n\n\tif (provider === \"google-vertex\") {\n\t\tconst hasCredentials = hasVertexAdcCredentials();\n\t\tconst hasProject = !!(\n\t\t\tprocess.env.GOOGLE_CLOUD_PROJECT ||\n\t\t\tprocess.env.GCLOUD_PROJECT ||\n\t\t\tgetProcEnv(\"GOOGLE_CLOUD_PROJECT\") ||\n\t\t\tgetProcEnv(\"GCLOUD_PROJECT\")\n\t\t);\n\t\tconst hasLocation = !!(process.env.GOOGLE_CLOUD_LOCATION || getProcEnv(\"GOOGLE_CLOUD_LOCATION\"));\n\n\t\tif (hasCredentials && hasProject && hasLocation) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\tif (provider === \"amazon-bedrock\") {\n\t\tif (\n\t\t\tprocess.env.AWS_PROFILE ||\n\t\t\t(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ||\n\t\t\tprocess.env.AWS_BEARER_TOKEN_BEDROCK ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_FULL_URI ||\n\t\t\tprocess.env.AWS_WEB_IDENTITY_TOKEN_FILE ||\n\t\t\tgetProcEnv(\"AWS_PROFILE\") ||\n\t\t\t(getProcEnv(\"AWS_ACCESS_KEY_ID\") && getProcEnv(\"AWS_SECRET_ACCESS_KEY\")) ||\n\t\t\tgetProcEnv(\"AWS_BEARER_TOKEN_BEDROCK\") ||\n\t\t\tgetProcEnv(\"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\") ||\n\t\t\tgetProcEnv(\"AWS_CONTAINER_CREDENTIALS_FULL_URI\") ||\n\t\t\tgetProcEnv(\"AWS_WEB_IDENTITY_TOKEN_FILE\")\n\t\t) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport function getPrimeTeamId(): string | undefined {\n\tconst fromEnv = process.env.PRIME_TEAM_ID || getProcEnv(\"PRIME_TEAM_ID\");\n\tif (fromEnv?.trim()) return fromEnv.trim();\n\n\tif (!_existsSync || !_readFileSync || !_homedir || !_join) return undefined;\n\tconst configPath = _join(_homedir(), \".prime\", \"config.json\");\n\tif (!_existsSync(configPath)) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(_readFileSync(configPath, \"utf-8\")) as unknown;\n\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\tconst teamId = (parsed as Record<string, unknown>).team_id;\n\t\t\tif (typeof teamId === \"string\" && teamId.trim()) return teamId.trim();\n\t\t}\n\t} catch {\n\t\t// Treat unreadable or malformed config as no configured team.\n\t}\n\treturn undefined;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAKX,mBAAmB,EACnB,cAAc,EACd,aAAa,EAKb,MAAM,aAAa,CAAC;AAQrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAY9D,MAAM,WAAW,aAAc,SAAQ,aAAa;IACnD,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IACrC,QAAQ,CAAC,EAAE;QACV,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,mBAAmB,CAAC;KAC5B,CAAC;CACF;AAID,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,sBAAsB,EAAE,aAAa,CAuO9E,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,sBAAsB,EAAE,mBAAmB,CAoC1F,CAAC","sourcesContent":["import {\n\ttype GenerateContentConfig,\n\ttype GenerateContentParameters,\n\tGoogleGenAI,\n\ttype ThinkingConfig,\n} from \"@google/genai\";\nimport { getEnvApiKey } from \"../env-api-keys.js\";\nimport { calculateCost, clampThinkingLevel } from \"../models.js\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n\tTextContent,\n\tThinkingContent,\n\tThinkingLevel,\n\tToolCall,\n} from \"../types.js\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.js\";\nimport { sanitizeSurrogates } from \"../utils/sanitize-unicode.js\";\nimport {\n\tformatStreamFailureMessage,\n\trecordStreamFailure,\n\tstreamFailureFromStopReason,\n} from \"../utils/stream-failure.js\";\nimport type { GoogleThinkingLevel } from \"./google-shared.js\";\nimport {\n\tconvertMessages,\n\tconvertTools,\n\tgetGoogleThinkingBudget,\n\tisThinkingPart,\n\tmapStopReason,\n\tmapToolChoice,\n\tretainThoughtSignature,\n} from \"./google-shared.js\";\nimport { buildBaseOptions } from \"./simple-options.js\";\n\nexport interface GoogleOptions extends StreamOptions {\n\ttoolChoice?: \"auto\" | \"none\" | \"any\";\n\tthinking?: {\n\t\tenabled: boolean;\n\t\tbudgetTokens?: number; // -1 for dynamic, 0 to disable\n\t\tlevel?: GoogleThinkingLevel;\n\t};\n}\n\nlet toolCallCounter = 0;\n\nexport const streamGoogle: StreamFunction<\"google-generative-ai\", GoogleOptions> = (\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions?: GoogleOptions,\n): AssistantMessageEventStream => {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: \"google-generative-ai\" as Api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"stop\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey || getEnvApiKey(model.provider) || \"\";\n\t\t\tconst client = createClient(model, apiKey, options?.headers);\n\t\t\tlet params = buildParams(model, context, options);\n\t\t\tconst nextParams = await options?.onPayload?.(params, model);\n\t\t\tif (nextParams !== undefined) {\n\t\t\t\tparams = nextParams as GenerateContentParameters;\n\t\t\t}\n\t\t\tconst googleStream = await client.models.generateContentStream(params);\n\n\t\t\tstream.push({ type: \"start\", partial: output });\n\t\t\tlet currentBlock: TextContent | ThinkingContent | null = null;\n\t\t\tconst blocks = output.content;\n\t\t\tconst blockIndex = () => blocks.length - 1;\n\t\t\tfor await (const chunk of googleStream) {\n\t\t\t\t// @google/genai documents GenerateContentResponse.responseId as an output-only field\n\t\t\t\t// used to identify each response. Keep the first non-empty one from the stream.\n\t\t\t\toutput.responseId ||= chunk.responseId;\n\t\t\t\tconst candidate = chunk.candidates?.[0];\n\t\t\t\tif (candidate?.content?.parts) {\n\t\t\t\t\tfor (const part of candidate.content.parts) {\n\t\t\t\t\t\tif (part.text !== undefined) {\n\t\t\t\t\t\t\tconst isThinking = isThinkingPart(part);\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!currentBlock ||\n\t\t\t\t\t\t\t\t(isThinking && currentBlock.type !== \"thinking\") ||\n\t\t\t\t\t\t\t\t(!isThinking && currentBlock.type !== \"text\")\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tif (currentBlock) {\n\t\t\t\t\t\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: blocks.length - 1,\n\t\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (isThinking) {\n\t\t\t\t\t\t\t\t\tcurrentBlock = { type: \"thinking\", thinking: \"\", thinkingSignature: undefined };\n\t\t\t\t\t\t\t\t\toutput.content.push(currentBlock);\n\t\t\t\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcurrentBlock = { type: \"text\", text: \"\" };\n\t\t\t\t\t\t\t\t\toutput.content.push(currentBlock);\n\t\t\t\t\t\t\t\t\tstream.push({ type: \"text_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (currentBlock.type === \"thinking\") {\n\t\t\t\t\t\t\t\tcurrentBlock.thinking += part.text;\n\t\t\t\t\t\t\t\tcurrentBlock.thinkingSignature = retainThoughtSignature(\n\t\t\t\t\t\t\t\t\tcurrentBlock.thinkingSignature,\n\t\t\t\t\t\t\t\t\tpart.thoughtSignature,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\tdelta: part.text,\n\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcurrentBlock.text += part.text;\n\t\t\t\t\t\t\t\tcurrentBlock.textSignature = retainThoughtSignature(\n\t\t\t\t\t\t\t\t\tcurrentBlock.textSignature,\n\t\t\t\t\t\t\t\t\tpart.thoughtSignature,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\tdelta: part.text,\n\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (part.functionCall) {\n\t\t\t\t\t\t\tif (currentBlock) {\n\t\t\t\t\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcurrentBlock = null;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst providedId = part.functionCall.id;\n\t\t\t\t\t\t\tconst needsNewId =\n\t\t\t\t\t\t\t\t!providedId || output.content.some((b) => b.type === \"toolCall\" && b.id === providedId);\n\t\t\t\t\t\t\tconst toolCallId = needsNewId\n\t\t\t\t\t\t\t\t? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}`\n\t\t\t\t\t\t\t\t: providedId;\n\n\t\t\t\t\t\t\tconst toolCall: ToolCall = {\n\t\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\t\tid: toolCallId,\n\t\t\t\t\t\t\t\tname: part.functionCall.name || \"\",\n\t\t\t\t\t\t\t\targuments: (part.functionCall.args as Record<string, any>) ?? {},\n\t\t\t\t\t\t\t\t...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\toutput.content.push(toolCall);\n\t\t\t\t\t\t\tstream.push({ type: \"toolcall_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\tdelta: JSON.stringify(toolCall.arguments),\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstream.push({ type: \"toolcall_end\", contentIndex: blockIndex(), toolCall, partial: output });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (candidate?.finishReason) {\n\t\t\t\t\toutput.stopReason = mapStopReason(candidate.finishReason);\n\t\t\t\t\tif (output.content.some((b) => b.type === \"toolCall\")) {\n\t\t\t\t\t\toutput.stopReason = \"toolUse\";\n\t\t\t\t\t}\n\t\t\t\t\tif (output.stopReason === \"error\") {\n\t\t\t\t\t\toutput.stopReasonRaw = candidate.finishReason;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (chunk.usageMetadata) {\n\t\t\t\t\toutput.usage = {\n\t\t\t\t\t\tinput:\n\t\t\t\t\t\t\t(chunk.usageMetadata.promptTokenCount || 0) - (chunk.usageMetadata.cachedContentTokenCount || 0),\n\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t(chunk.usageMetadata.candidatesTokenCount || 0) + (chunk.usageMetadata.thoughtsTokenCount || 0),\n\t\t\t\t\t\tcacheRead: chunk.usageMetadata.cachedContentTokenCount || 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: chunk.usageMetadata.totalTokenCount || 0,\n\t\t\t\t\t\tcost: {\n\t\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\ttotal: 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tcalculateCost(model, output.usage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentBlock) {\n\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tif (output.stopReason === \"aborted\" || output.stopReason === \"error\") {\n\t\t\t\tthrow streamFailureFromStopReason(output.stopReasonRaw);\n\t\t\t}\n\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason, message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\t// Remove internal index property used during streaming\n\t\t\tfor (const block of output.content) {\n\t\t\t\tif (\"index\" in block) {\n\t\t\t\t\tdelete (block as { index?: number }).index;\n\t\t\t\t}\n\t\t\t}\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = formatStreamFailureMessage(error);\n\t\t\trecordStreamFailure(model, output, error);\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n};\n\nexport const streamSimpleGoogle: StreamFunction<\"google-generative-ai\", SimpleStreamOptions> = (\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n\tconst apiKey = options?.apiKey || getEnvApiKey(model.provider);\n\tif (!apiKey) {\n\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t}\n\n\tconst base = buildBaseOptions(model, options, apiKey);\n\tif (!options?.reasoning || options.reasoning === \"off\") {\n\t\treturn streamGoogle(model, context, { ...base, thinking: { enabled: false } } satisfies GoogleOptions);\n\t}\n\n\tconst clampedReasoning = clampThinkingLevel(model, options.reasoning);\n\tconst effort = (clampedReasoning === \"off\" ? \"high\" : clampedReasoning) as ClampedThinkingLevel;\n\tconst googleModel = model as Model<\"google-generative-ai\">;\n\n\tif (isGemini3ProModel(googleModel) || isGemini3FlashModel(googleModel) || isGemma4Model(googleModel)) {\n\t\treturn streamGoogle(model, context, {\n\t\t\t...base,\n\t\t\tthinking: {\n\t\t\t\tenabled: true,\n\t\t\t\tlevel: getThinkingLevel(effort, googleModel),\n\t\t\t},\n\t\t} satisfies GoogleOptions);\n\t}\n\n\treturn streamGoogle(model, context, {\n\t\t...base,\n\t\tthinking: {\n\t\t\tenabled: true,\n\t\t\tbudgetTokens: getGoogleThinkingBudget(googleModel.id, effort, options.thinkingBudgets),\n\t\t},\n\t} satisfies GoogleOptions);\n};\n\nfunction createClient(\n\tmodel: Model<\"google-generative-ai\">,\n\tapiKey?: string,\n\toptionsHeaders?: Record<string, string>,\n): GoogleGenAI {\n\tconst httpOptions: { baseUrl?: string; apiVersion?: string; headers?: Record<string, string> } = {};\n\tif (model.baseUrl) {\n\t\thttpOptions.baseUrl = model.baseUrl;\n\t\thttpOptions.apiVersion = \"\"; // baseUrl already includes version path, don't append\n\t}\n\tif (model.headers || optionsHeaders) {\n\t\thttpOptions.headers = { ...model.headers, ...optionsHeaders };\n\t}\n\n\treturn new GoogleGenAI({\n\t\tapiKey,\n\t\thttpOptions: Object.keys(httpOptions).length > 0 ? httpOptions : undefined,\n\t});\n}\n\nfunction buildParams(\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions: GoogleOptions = {},\n): GenerateContentParameters {\n\tconst contents = convertMessages(model, context);\n\n\tconst generationConfig: GenerateContentConfig = {};\n\tif (options.temperature !== undefined) {\n\t\tgenerationConfig.temperature = options.temperature;\n\t}\n\tif (options.maxTokens !== undefined) {\n\t\tgenerationConfig.maxOutputTokens = options.maxTokens;\n\t}\n\n\tconst config: GenerateContentConfig = {\n\t\t...(Object.keys(generationConfig).length > 0 && generationConfig),\n\t\t...(context.systemPrompt && { systemInstruction: sanitizeSurrogates(context.systemPrompt) }),\n\t\t...(context.tools && context.tools.length > 0 && { tools: convertTools(context.tools) }),\n\t};\n\n\tif (context.tools && context.tools.length > 0 && options.toolChoice) {\n\t\tconfig.toolConfig = {\n\t\t\tfunctionCallingConfig: {\n\t\t\t\tmode: mapToolChoice(options.toolChoice),\n\t\t\t},\n\t\t};\n\t} else {\n\t\tconfig.toolConfig = undefined;\n\t}\n\n\tif (options.thinking?.enabled && model.reasoning) {\n\t\tconst thinkingConfig: ThinkingConfig = { includeThoughts: true };\n\t\tif (options.thinking.level !== undefined) {\n\t\t\t// Cast to any since our GoogleThinkingLevel mirrors Google's ThinkingLevel enum values\n\t\t\tthinkingConfig.thinkingLevel = options.thinking.level as any;\n\t\t} else if (options.thinking.budgetTokens !== undefined) {\n\t\t\tthinkingConfig.thinkingBudget = options.thinking.budgetTokens;\n\t\t}\n\t\tconfig.thinkingConfig = thinkingConfig;\n\t} else if (model.reasoning && options.thinking && !options.thinking.enabled) {\n\t\tconfig.thinkingConfig = getDisabledThinkingConfig(model);\n\t}\n\n\tif (options.signal) {\n\t\tif (options.signal.aborted) {\n\t\t\tthrow new Error(\"Request aborted\");\n\t\t}\n\t\tconfig.abortSignal = options.signal;\n\t}\n\n\tconst params: GenerateContentParameters = {\n\t\tmodel: model.id,\n\t\tcontents,\n\t\tconfig,\n\t};\n\n\treturn params;\n}\n\ntype ClampedThinkingLevel = Exclude<ThinkingLevel, \"xhigh\" | \"max\">;\n\nfunction isGemma4Model(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemma-?4/.test(model.id.toLowerCase());\n}\n\nfunction isGemini3ProModel(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemini-3(?:\\.\\d+)?-pro/.test(model.id.toLowerCase());\n}\n\nfunction isGemini3FlashModel(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemini-3(?:\\.\\d+)?-flash/.test(model.id.toLowerCase());\n}\n\nfunction getDisabledThinkingConfig(model: Model<\"google-generative-ai\">): ThinkingConfig {\n\t// Google docs: Gemini 3.1 Pro cannot disable thinking, and Gemini 3 Flash / Flash-Lite\n\t// do not support full thinking-off either. For Gemini 3 models, use the lowest supported\n\t// thinkingLevel without includeThoughts so hidden thinking remains invisible to pi.\n\tif (isGemini3ProModel(model)) {\n\t\treturn { thinkingLevel: \"LOW\" as any };\n\t}\n\tif (isGemini3FlashModel(model)) {\n\t\treturn { thinkingLevel: \"MINIMAL\" as any };\n\t}\n\tif (isGemma4Model(model)) {\n\t\treturn { thinkingLevel: \"MINIMAL\" as any };\n\t}\n\n\t// Gemini 2.x supports disabling via thinkingBudget = 0.\n\treturn { thinkingBudget: 0 };\n}\n\nfunction getThinkingLevel(effort: ClampedThinkingLevel, model: Model<\"google-generative-ai\">): GoogleThinkingLevel {\n\tif (isGemini3ProModel(model)) {\n\t\tswitch (effort) {\n\t\t\tcase \"minimal\":\n\t\t\tcase \"low\":\n\t\t\t\treturn \"LOW\";\n\t\t\tcase \"medium\":\n\t\t\tcase \"high\":\n\t\t\t\treturn \"HIGH\";\n\t\t}\n\t}\n\tif (isGemma4Model(model)) {\n\t\tswitch (effort) {\n\t\t\tcase \"minimal\":\n\t\t\tcase \"low\":\n\t\t\t\treturn \"MINIMAL\";\n\t\t\tcase \"medium\":\n\t\t\tcase \"high\":\n\t\t\t\treturn \"HIGH\";\n\t\t}\n\t}\n\tswitch (effort) {\n\t\tcase \"minimal\":\n\t\t\treturn \"MINIMAL\";\n\t\tcase \"low\":\n\t\t\treturn \"LOW\";\n\t\tcase \"medium\":\n\t\t\treturn \"MEDIUM\";\n\t\tcase \"high\":\n\t\t\treturn \"HIGH\";\n\t}\n}\n"]}
1
+ {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAKX,mBAAmB,EACnB,cAAc,EACd,aAAa,EAKb,MAAM,aAAa,CAAC;AAQrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAY9D,MAAM,WAAW,aAAc,SAAQ,aAAa;IACnD,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IACrC,QAAQ,CAAC,EAAE;QACV,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,mBAAmB,CAAC;KAC5B,CAAC;CACF;AAID,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,sBAAsB,EAAE,aAAa,CAuO9E,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,sBAAsB,EAAE,mBAAmB,CAoC1F,CAAC","sourcesContent":["import {\n\ttype GenerateContentConfig,\n\ttype GenerateContentParameters,\n\tGoogleGenAI,\n\ttype ThinkingConfig,\n} from \"@google/genai\";\nimport { getEnvApiKey } from \"../env-api-keys.js\";\nimport { calculateCost, clampThinkingLevel } from \"../models.js\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n\tTextContent,\n\tThinkingContent,\n\tThinkingLevel,\n\tToolCall,\n} from \"../types.js\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.js\";\nimport { sanitizeSurrogates } from \"../utils/sanitize-unicode.js\";\nimport {\n\tformatStreamFailureMessage,\n\trecordStreamFailure,\n\tstreamFailureFromStopReason,\n} from \"../utils/stream-failure.js\";\nimport type { GoogleThinkingLevel } from \"./google-shared.js\";\nimport {\n\tconvertMessages,\n\tconvertTools,\n\tgetGoogleThinkingBudget,\n\tisThinkingPart,\n\tmapStopReason,\n\tmapToolChoice,\n\tretainThoughtSignature,\n} from \"./google-shared.js\";\nimport { buildBaseOptions } from \"./simple-options.js\";\n\nexport interface GoogleOptions extends StreamOptions {\n\ttoolChoice?: \"auto\" | \"none\" | \"any\";\n\tthinking?: {\n\t\tenabled: boolean;\n\t\tbudgetTokens?: number; // -1 for dynamic, 0 to disable\n\t\tlevel?: GoogleThinkingLevel;\n\t};\n}\n\nlet toolCallCounter = 0;\n\nexport const streamGoogle: StreamFunction<\"google-generative-ai\", GoogleOptions> = (\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions?: GoogleOptions,\n): AssistantMessageEventStream => {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: \"google-generative-ai\" as Api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"stop\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey || getEnvApiKey(model.provider) || \"\";\n\t\t\tconst client = createClient(model, apiKey, options?.headers);\n\t\t\tlet params = buildParams(model, context, options);\n\t\t\tconst nextParams = await options?.onPayload?.(params, model);\n\t\t\tif (nextParams !== undefined) {\n\t\t\t\tparams = nextParams as GenerateContentParameters;\n\t\t\t}\n\t\t\tconst googleStream = await client.models.generateContentStream(params);\n\n\t\t\tstream.push({ type: \"start\", partial: output });\n\t\t\tlet currentBlock: TextContent | ThinkingContent | null = null;\n\t\t\tconst blocks = output.content;\n\t\t\tconst blockIndex = () => blocks.length - 1;\n\t\t\tfor await (const chunk of googleStream) {\n\t\t\t\t// @google/genai documents GenerateContentResponse.responseId as an output-only field\n\t\t\t\t// used to identify each response. Keep the first non-empty one from the stream.\n\t\t\t\toutput.responseId ||= chunk.responseId;\n\t\t\t\tconst candidate = chunk.candidates?.[0];\n\t\t\t\tif (candidate?.content?.parts) {\n\t\t\t\t\tfor (const part of candidate.content.parts) {\n\t\t\t\t\t\tif (part.text !== undefined) {\n\t\t\t\t\t\t\tconst isThinking = isThinkingPart(part);\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!currentBlock ||\n\t\t\t\t\t\t\t\t(isThinking && currentBlock.type !== \"thinking\") ||\n\t\t\t\t\t\t\t\t(!isThinking && currentBlock.type !== \"text\")\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tif (currentBlock) {\n\t\t\t\t\t\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: blocks.length - 1,\n\t\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (isThinking) {\n\t\t\t\t\t\t\t\t\tcurrentBlock = { type: \"thinking\", thinking: \"\", thinkingSignature: undefined };\n\t\t\t\t\t\t\t\t\toutput.content.push(currentBlock);\n\t\t\t\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcurrentBlock = { type: \"text\", text: \"\" };\n\t\t\t\t\t\t\t\t\toutput.content.push(currentBlock);\n\t\t\t\t\t\t\t\t\tstream.push({ type: \"text_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (currentBlock.type === \"thinking\") {\n\t\t\t\t\t\t\t\tcurrentBlock.thinking += part.text;\n\t\t\t\t\t\t\t\tcurrentBlock.thinkingSignature = retainThoughtSignature(\n\t\t\t\t\t\t\t\t\tcurrentBlock.thinkingSignature,\n\t\t\t\t\t\t\t\t\tpart.thoughtSignature,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\tdelta: part.text,\n\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcurrentBlock.text += part.text;\n\t\t\t\t\t\t\t\tcurrentBlock.textSignature = retainThoughtSignature(\n\t\t\t\t\t\t\t\t\tcurrentBlock.textSignature,\n\t\t\t\t\t\t\t\t\tpart.thoughtSignature,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\tdelta: part.text,\n\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (part.functionCall) {\n\t\t\t\t\t\t\tif (currentBlock) {\n\t\t\t\t\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcurrentBlock = null;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst providedId = part.functionCall.id;\n\t\t\t\t\t\t\tconst needsNewId =\n\t\t\t\t\t\t\t\t!providedId || output.content.some((b) => b.type === \"toolCall\" && b.id === providedId);\n\t\t\t\t\t\t\tconst toolCallId = needsNewId\n\t\t\t\t\t\t\t\t? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}`\n\t\t\t\t\t\t\t\t: providedId;\n\n\t\t\t\t\t\t\tconst toolCall: ToolCall = {\n\t\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\t\tid: toolCallId,\n\t\t\t\t\t\t\t\tname: part.functionCall.name || \"\",\n\t\t\t\t\t\t\t\targuments: (part.functionCall.args as Record<string, any>) ?? {},\n\t\t\t\t\t\t\t\t...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\toutput.content.push(toolCall);\n\t\t\t\t\t\t\tstream.push({ type: \"toolcall_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\tdelta: JSON.stringify(toolCall.arguments),\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstream.push({ type: \"toolcall_end\", contentIndex: blockIndex(), toolCall, partial: output });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (candidate?.finishReason) {\n\t\t\t\t\toutput.stopReason = mapStopReason(candidate.finishReason);\n\t\t\t\t\tif (output.content.some((b) => b.type === \"toolCall\")) {\n\t\t\t\t\t\toutput.stopReason = \"toolUse\";\n\t\t\t\t\t}\n\t\t\t\t\tif (output.stopReason === \"error\") {\n\t\t\t\t\t\toutput.stopReasonRaw = candidate.finishReason;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (chunk.usageMetadata) {\n\t\t\t\t\toutput.usage = {\n\t\t\t\t\t\tinput:\n\t\t\t\t\t\t\t(chunk.usageMetadata.promptTokenCount || 0) - (chunk.usageMetadata.cachedContentTokenCount || 0),\n\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t(chunk.usageMetadata.candidatesTokenCount || 0) + (chunk.usageMetadata.thoughtsTokenCount || 0),\n\t\t\t\t\t\tcacheRead: chunk.usageMetadata.cachedContentTokenCount || 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: chunk.usageMetadata.totalTokenCount || 0,\n\t\t\t\t\t\tcost: {\n\t\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\ttotal: 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tcalculateCost(model, output.usage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentBlock) {\n\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tif (output.stopReason === \"aborted\" || output.stopReason === \"error\") {\n\t\t\t\tthrow streamFailureFromStopReason(output.stopReasonRaw);\n\t\t\t}\n\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason, message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\t// Remove internal index property used during streaming\n\t\t\tfor (const block of output.content) {\n\t\t\t\tif (\"index\" in block) {\n\t\t\t\t\tdelete (block as { index?: number }).index;\n\t\t\t\t}\n\t\t\t}\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = formatStreamFailureMessage(error);\n\t\t\trecordStreamFailure(model, output, error);\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n};\n\nexport const streamSimpleGoogle: StreamFunction<\"google-generative-ai\", SimpleStreamOptions> = (\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n\tconst apiKey = options?.apiKey || getEnvApiKey(model.provider);\n\tif (!apiKey) {\n\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t}\n\n\tconst base = buildBaseOptions(model, options, apiKey);\n\tif (!options?.reasoning || options.reasoning === \"off\") {\n\t\treturn streamGoogle(model, context, { ...base, thinking: { enabled: false } } satisfies GoogleOptions);\n\t}\n\n\tconst clampedReasoning = clampThinkingLevel(model, options.reasoning);\n\tconst effort = (clampedReasoning === \"off\" ? \"high\" : clampedReasoning) as ClampedThinkingLevel;\n\tconst googleModel = model as Model<\"google-generative-ai\">;\n\n\tif (isGemini3ProModel(googleModel) || isGemini3FlashModel(googleModel) || isGemma4Model(googleModel)) {\n\t\treturn streamGoogle(model, context, {\n\t\t\t...base,\n\t\t\tthinking: {\n\t\t\t\tenabled: true,\n\t\t\t\tlevel: getThinkingLevel(effort, googleModel),\n\t\t\t},\n\t\t} satisfies GoogleOptions);\n\t}\n\n\treturn streamGoogle(model, context, {\n\t\t...base,\n\t\tthinking: {\n\t\t\tenabled: true,\n\t\t\tbudgetTokens: getGoogleThinkingBudget(googleModel.id, effort, options.thinkingBudgets),\n\t\t},\n\t} satisfies GoogleOptions);\n};\n\nfunction createClient(\n\tmodel: Model<\"google-generative-ai\">,\n\tapiKey?: string,\n\toptionsHeaders?: Record<string, string>,\n): GoogleGenAI {\n\tconst httpOptions: { baseUrl?: string; apiVersion?: string; headers?: Record<string, string> } = {};\n\tif (model.baseUrl) {\n\t\thttpOptions.baseUrl = model.baseUrl;\n\t\thttpOptions.apiVersion = \"\"; // baseUrl already includes version path, don't append\n\t}\n\tconst headers: Record<string, string> = { ...model.headers, ...optionsHeaders };\n\tconst isOAuthToken = apiKey && (apiKey.startsWith(\"ya29.\") || apiKey.startsWith(\"Bearer \"));\n\tif (isOAuthToken) {\n\t\theaders.Authorization = apiKey.startsWith(\"Bearer \") ? apiKey : `Bearer ${apiKey}`;\n\t\thttpOptions.headers = headers;\n\t\treturn new GoogleGenAI({\n\t\t\thttpOptions: Object.keys(httpOptions).length > 0 ? httpOptions : undefined,\n\t\t});\n\t}\n\tif (Object.keys(headers).length > 0) {\n\t\thttpOptions.headers = headers;\n\t}\n\n\treturn new GoogleGenAI({\n\t\tapiKey,\n\t\thttpOptions: Object.keys(httpOptions).length > 0 ? httpOptions : undefined,\n\t});\n}\n\nfunction buildParams(\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions: GoogleOptions = {},\n): GenerateContentParameters {\n\tconst contents = convertMessages(model, context);\n\n\tconst generationConfig: GenerateContentConfig = {};\n\tif (options.temperature !== undefined) {\n\t\tgenerationConfig.temperature = options.temperature;\n\t}\n\tif (options.maxTokens !== undefined) {\n\t\tgenerationConfig.maxOutputTokens = options.maxTokens;\n\t}\n\n\tconst config: GenerateContentConfig = {\n\t\t...(Object.keys(generationConfig).length > 0 && generationConfig),\n\t\t...(context.systemPrompt && { systemInstruction: sanitizeSurrogates(context.systemPrompt) }),\n\t\t...(context.tools && context.tools.length > 0 && { tools: convertTools(context.tools) }),\n\t};\n\n\tif (context.tools && context.tools.length > 0 && options.toolChoice) {\n\t\tconfig.toolConfig = {\n\t\t\tfunctionCallingConfig: {\n\t\t\t\tmode: mapToolChoice(options.toolChoice),\n\t\t\t},\n\t\t};\n\t} else {\n\t\tconfig.toolConfig = undefined;\n\t}\n\n\tif (options.thinking?.enabled && model.reasoning) {\n\t\tconst thinkingConfig: ThinkingConfig = { includeThoughts: true };\n\t\tif (options.thinking.level !== undefined) {\n\t\t\t// Cast to any since our GoogleThinkingLevel mirrors Google's ThinkingLevel enum values\n\t\t\tthinkingConfig.thinkingLevel = options.thinking.level as any;\n\t\t} else if (options.thinking.budgetTokens !== undefined) {\n\t\t\tthinkingConfig.thinkingBudget = options.thinking.budgetTokens;\n\t\t}\n\t\tconfig.thinkingConfig = thinkingConfig;\n\t} else if (model.reasoning && options.thinking && !options.thinking.enabled) {\n\t\tconfig.thinkingConfig = getDisabledThinkingConfig(model);\n\t}\n\n\tif (options.signal) {\n\t\tif (options.signal.aborted) {\n\t\t\tthrow new Error(\"Request aborted\");\n\t\t}\n\t\tconfig.abortSignal = options.signal;\n\t}\n\n\tconst params: GenerateContentParameters = {\n\t\tmodel: model.id,\n\t\tcontents,\n\t\tconfig,\n\t};\n\n\treturn params;\n}\n\ntype ClampedThinkingLevel = Exclude<ThinkingLevel, \"xhigh\" | \"max\">;\n\nfunction isGemma4Model(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemma-?4/.test(model.id.toLowerCase());\n}\n\nfunction isGemini3ProModel(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemini-3(?:\\.\\d+)?-pro/.test(model.id.toLowerCase());\n}\n\nfunction isGemini3FlashModel(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemini-3(?:\\.\\d+)?-flash/.test(model.id.toLowerCase());\n}\n\nfunction getDisabledThinkingConfig(model: Model<\"google-generative-ai\">): ThinkingConfig {\n\t// Google docs: Gemini 3.1 Pro cannot disable thinking, and Gemini 3 Flash / Flash-Lite\n\t// do not support full thinking-off either. For Gemini 3 models, use the lowest supported\n\t// thinkingLevel without includeThoughts so hidden thinking remains invisible to pi.\n\tif (isGemini3ProModel(model)) {\n\t\treturn { thinkingLevel: \"LOW\" as any };\n\t}\n\tif (isGemini3FlashModel(model)) {\n\t\treturn { thinkingLevel: \"MINIMAL\" as any };\n\t}\n\tif (isGemma4Model(model)) {\n\t\treturn { thinkingLevel: \"MINIMAL\" as any };\n\t}\n\n\t// Gemini 2.x supports disabling via thinkingBudget = 0.\n\treturn { thinkingBudget: 0 };\n}\n\nfunction getThinkingLevel(effort: ClampedThinkingLevel, model: Model<\"google-generative-ai\">): GoogleThinkingLevel {\n\tif (isGemini3ProModel(model)) {\n\t\tswitch (effort) {\n\t\t\tcase \"minimal\":\n\t\t\tcase \"low\":\n\t\t\t\treturn \"LOW\";\n\t\t\tcase \"medium\":\n\t\t\tcase \"high\":\n\t\t\t\treturn \"HIGH\";\n\t\t}\n\t}\n\tif (isGemma4Model(model)) {\n\t\tswitch (effort) {\n\t\t\tcase \"minimal\":\n\t\t\tcase \"low\":\n\t\t\t\treturn \"MINIMAL\";\n\t\t\tcase \"medium\":\n\t\t\tcase \"high\":\n\t\t\t\treturn \"HIGH\";\n\t\t}\n\t}\n\tswitch (effort) {\n\t\tcase \"minimal\":\n\t\t\treturn \"MINIMAL\";\n\t\tcase \"low\":\n\t\t\treturn \"LOW\";\n\t\tcase \"medium\":\n\t\t\treturn \"MEDIUM\";\n\t\tcase \"high\":\n\t\t\treturn \"HIGH\";\n\t}\n}\n"]}
@@ -251,8 +251,17 @@ function createClient(model, apiKey, optionsHeaders) {
251
251
  httpOptions.baseUrl = model.baseUrl;
252
252
  httpOptions.apiVersion = ""; // baseUrl already includes version path, don't append
253
253
  }
254
- if (model.headers || optionsHeaders) {
255
- httpOptions.headers = { ...model.headers, ...optionsHeaders };
254
+ const headers = { ...model.headers, ...optionsHeaders };
255
+ const isOAuthToken = apiKey && (apiKey.startsWith("ya29.") || apiKey.startsWith("Bearer "));
256
+ if (isOAuthToken) {
257
+ headers.Authorization = apiKey.startsWith("Bearer ") ? apiKey : `Bearer ${apiKey}`;
258
+ httpOptions.headers = headers;
259
+ return new GoogleGenAI({
260
+ httpOptions: Object.keys(httpOptions).length > 0 ? httpOptions : undefined,
261
+ });
262
+ }
263
+ if (Object.keys(headers).length > 0) {
264
+ httpOptions.headers = headers;
256
265
  }
257
266
  return new GoogleGenAI({
258
267
  apiKey,
@@ -1 +1 @@
1
- {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype GenerateContentConfig,\n\ttype GenerateContentParameters,\n\tGoogleGenAI,\n\ttype ThinkingConfig,\n} from \"@google/genai\";\nimport { getEnvApiKey } from \"../env-api-keys.js\";\nimport { calculateCost, clampThinkingLevel } from \"../models.js\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n\tTextContent,\n\tThinkingContent,\n\tThinkingLevel,\n\tToolCall,\n} from \"../types.js\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.js\";\nimport { sanitizeSurrogates } from \"../utils/sanitize-unicode.js\";\nimport {\n\tformatStreamFailureMessage,\n\trecordStreamFailure,\n\tstreamFailureFromStopReason,\n} from \"../utils/stream-failure.js\";\nimport type { GoogleThinkingLevel } from \"./google-shared.js\";\nimport {\n\tconvertMessages,\n\tconvertTools,\n\tgetGoogleThinkingBudget,\n\tisThinkingPart,\n\tmapStopReason,\n\tmapToolChoice,\n\tretainThoughtSignature,\n} from \"./google-shared.js\";\nimport { buildBaseOptions } from \"./simple-options.js\";\n\nexport interface GoogleOptions extends StreamOptions {\n\ttoolChoice?: \"auto\" | \"none\" | \"any\";\n\tthinking?: {\n\t\tenabled: boolean;\n\t\tbudgetTokens?: number; // -1 for dynamic, 0 to disable\n\t\tlevel?: GoogleThinkingLevel;\n\t};\n}\n\nlet toolCallCounter = 0;\n\nexport const streamGoogle: StreamFunction<\"google-generative-ai\", GoogleOptions> = (\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions?: GoogleOptions,\n): AssistantMessageEventStream => {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: \"google-generative-ai\" as Api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"stop\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey || getEnvApiKey(model.provider) || \"\";\n\t\t\tconst client = createClient(model, apiKey, options?.headers);\n\t\t\tlet params = buildParams(model, context, options);\n\t\t\tconst nextParams = await options?.onPayload?.(params, model);\n\t\t\tif (nextParams !== undefined) {\n\t\t\t\tparams = nextParams as GenerateContentParameters;\n\t\t\t}\n\t\t\tconst googleStream = await client.models.generateContentStream(params);\n\n\t\t\tstream.push({ type: \"start\", partial: output });\n\t\t\tlet currentBlock: TextContent | ThinkingContent | null = null;\n\t\t\tconst blocks = output.content;\n\t\t\tconst blockIndex = () => blocks.length - 1;\n\t\t\tfor await (const chunk of googleStream) {\n\t\t\t\t// @google/genai documents GenerateContentResponse.responseId as an output-only field\n\t\t\t\t// used to identify each response. Keep the first non-empty one from the stream.\n\t\t\t\toutput.responseId ||= chunk.responseId;\n\t\t\t\tconst candidate = chunk.candidates?.[0];\n\t\t\t\tif (candidate?.content?.parts) {\n\t\t\t\t\tfor (const part of candidate.content.parts) {\n\t\t\t\t\t\tif (part.text !== undefined) {\n\t\t\t\t\t\t\tconst isThinking = isThinkingPart(part);\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!currentBlock ||\n\t\t\t\t\t\t\t\t(isThinking && currentBlock.type !== \"thinking\") ||\n\t\t\t\t\t\t\t\t(!isThinking && currentBlock.type !== \"text\")\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tif (currentBlock) {\n\t\t\t\t\t\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: blocks.length - 1,\n\t\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (isThinking) {\n\t\t\t\t\t\t\t\t\tcurrentBlock = { type: \"thinking\", thinking: \"\", thinkingSignature: undefined };\n\t\t\t\t\t\t\t\t\toutput.content.push(currentBlock);\n\t\t\t\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcurrentBlock = { type: \"text\", text: \"\" };\n\t\t\t\t\t\t\t\t\toutput.content.push(currentBlock);\n\t\t\t\t\t\t\t\t\tstream.push({ type: \"text_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (currentBlock.type === \"thinking\") {\n\t\t\t\t\t\t\t\tcurrentBlock.thinking += part.text;\n\t\t\t\t\t\t\t\tcurrentBlock.thinkingSignature = retainThoughtSignature(\n\t\t\t\t\t\t\t\t\tcurrentBlock.thinkingSignature,\n\t\t\t\t\t\t\t\t\tpart.thoughtSignature,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\tdelta: part.text,\n\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcurrentBlock.text += part.text;\n\t\t\t\t\t\t\t\tcurrentBlock.textSignature = retainThoughtSignature(\n\t\t\t\t\t\t\t\t\tcurrentBlock.textSignature,\n\t\t\t\t\t\t\t\t\tpart.thoughtSignature,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\tdelta: part.text,\n\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (part.functionCall) {\n\t\t\t\t\t\t\tif (currentBlock) {\n\t\t\t\t\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcurrentBlock = null;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst providedId = part.functionCall.id;\n\t\t\t\t\t\t\tconst needsNewId =\n\t\t\t\t\t\t\t\t!providedId || output.content.some((b) => b.type === \"toolCall\" && b.id === providedId);\n\t\t\t\t\t\t\tconst toolCallId = needsNewId\n\t\t\t\t\t\t\t\t? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}`\n\t\t\t\t\t\t\t\t: providedId;\n\n\t\t\t\t\t\t\tconst toolCall: ToolCall = {\n\t\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\t\tid: toolCallId,\n\t\t\t\t\t\t\t\tname: part.functionCall.name || \"\",\n\t\t\t\t\t\t\t\targuments: (part.functionCall.args as Record<string, any>) ?? {},\n\t\t\t\t\t\t\t\t...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\toutput.content.push(toolCall);\n\t\t\t\t\t\t\tstream.push({ type: \"toolcall_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\tdelta: JSON.stringify(toolCall.arguments),\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstream.push({ type: \"toolcall_end\", contentIndex: blockIndex(), toolCall, partial: output });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (candidate?.finishReason) {\n\t\t\t\t\toutput.stopReason = mapStopReason(candidate.finishReason);\n\t\t\t\t\tif (output.content.some((b) => b.type === \"toolCall\")) {\n\t\t\t\t\t\toutput.stopReason = \"toolUse\";\n\t\t\t\t\t}\n\t\t\t\t\tif (output.stopReason === \"error\") {\n\t\t\t\t\t\toutput.stopReasonRaw = candidate.finishReason;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (chunk.usageMetadata) {\n\t\t\t\t\toutput.usage = {\n\t\t\t\t\t\tinput:\n\t\t\t\t\t\t\t(chunk.usageMetadata.promptTokenCount || 0) - (chunk.usageMetadata.cachedContentTokenCount || 0),\n\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t(chunk.usageMetadata.candidatesTokenCount || 0) + (chunk.usageMetadata.thoughtsTokenCount || 0),\n\t\t\t\t\t\tcacheRead: chunk.usageMetadata.cachedContentTokenCount || 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: chunk.usageMetadata.totalTokenCount || 0,\n\t\t\t\t\t\tcost: {\n\t\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\ttotal: 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tcalculateCost(model, output.usage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentBlock) {\n\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tif (output.stopReason === \"aborted\" || output.stopReason === \"error\") {\n\t\t\t\tthrow streamFailureFromStopReason(output.stopReasonRaw);\n\t\t\t}\n\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason, message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\t// Remove internal index property used during streaming\n\t\t\tfor (const block of output.content) {\n\t\t\t\tif (\"index\" in block) {\n\t\t\t\t\tdelete (block as { index?: number }).index;\n\t\t\t\t}\n\t\t\t}\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = formatStreamFailureMessage(error);\n\t\t\trecordStreamFailure(model, output, error);\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n};\n\nexport const streamSimpleGoogle: StreamFunction<\"google-generative-ai\", SimpleStreamOptions> = (\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n\tconst apiKey = options?.apiKey || getEnvApiKey(model.provider);\n\tif (!apiKey) {\n\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t}\n\n\tconst base = buildBaseOptions(model, options, apiKey);\n\tif (!options?.reasoning || options.reasoning === \"off\") {\n\t\treturn streamGoogle(model, context, { ...base, thinking: { enabled: false } } satisfies GoogleOptions);\n\t}\n\n\tconst clampedReasoning = clampThinkingLevel(model, options.reasoning);\n\tconst effort = (clampedReasoning === \"off\" ? \"high\" : clampedReasoning) as ClampedThinkingLevel;\n\tconst googleModel = model as Model<\"google-generative-ai\">;\n\n\tif (isGemini3ProModel(googleModel) || isGemini3FlashModel(googleModel) || isGemma4Model(googleModel)) {\n\t\treturn streamGoogle(model, context, {\n\t\t\t...base,\n\t\t\tthinking: {\n\t\t\t\tenabled: true,\n\t\t\t\tlevel: getThinkingLevel(effort, googleModel),\n\t\t\t},\n\t\t} satisfies GoogleOptions);\n\t}\n\n\treturn streamGoogle(model, context, {\n\t\t...base,\n\t\tthinking: {\n\t\t\tenabled: true,\n\t\t\tbudgetTokens: getGoogleThinkingBudget(googleModel.id, effort, options.thinkingBudgets),\n\t\t},\n\t} satisfies GoogleOptions);\n};\n\nfunction createClient(\n\tmodel: Model<\"google-generative-ai\">,\n\tapiKey?: string,\n\toptionsHeaders?: Record<string, string>,\n): GoogleGenAI {\n\tconst httpOptions: { baseUrl?: string; apiVersion?: string; headers?: Record<string, string> } = {};\n\tif (model.baseUrl) {\n\t\thttpOptions.baseUrl = model.baseUrl;\n\t\thttpOptions.apiVersion = \"\"; // baseUrl already includes version path, don't append\n\t}\n\tif (model.headers || optionsHeaders) {\n\t\thttpOptions.headers = { ...model.headers, ...optionsHeaders };\n\t}\n\n\treturn new GoogleGenAI({\n\t\tapiKey,\n\t\thttpOptions: Object.keys(httpOptions).length > 0 ? httpOptions : undefined,\n\t});\n}\n\nfunction buildParams(\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions: GoogleOptions = {},\n): GenerateContentParameters {\n\tconst contents = convertMessages(model, context);\n\n\tconst generationConfig: GenerateContentConfig = {};\n\tif (options.temperature !== undefined) {\n\t\tgenerationConfig.temperature = options.temperature;\n\t}\n\tif (options.maxTokens !== undefined) {\n\t\tgenerationConfig.maxOutputTokens = options.maxTokens;\n\t}\n\n\tconst config: GenerateContentConfig = {\n\t\t...(Object.keys(generationConfig).length > 0 && generationConfig),\n\t\t...(context.systemPrompt && { systemInstruction: sanitizeSurrogates(context.systemPrompt) }),\n\t\t...(context.tools && context.tools.length > 0 && { tools: convertTools(context.tools) }),\n\t};\n\n\tif (context.tools && context.tools.length > 0 && options.toolChoice) {\n\t\tconfig.toolConfig = {\n\t\t\tfunctionCallingConfig: {\n\t\t\t\tmode: mapToolChoice(options.toolChoice),\n\t\t\t},\n\t\t};\n\t} else {\n\t\tconfig.toolConfig = undefined;\n\t}\n\n\tif (options.thinking?.enabled && model.reasoning) {\n\t\tconst thinkingConfig: ThinkingConfig = { includeThoughts: true };\n\t\tif (options.thinking.level !== undefined) {\n\t\t\t// Cast to any since our GoogleThinkingLevel mirrors Google's ThinkingLevel enum values\n\t\t\tthinkingConfig.thinkingLevel = options.thinking.level as any;\n\t\t} else if (options.thinking.budgetTokens !== undefined) {\n\t\t\tthinkingConfig.thinkingBudget = options.thinking.budgetTokens;\n\t\t}\n\t\tconfig.thinkingConfig = thinkingConfig;\n\t} else if (model.reasoning && options.thinking && !options.thinking.enabled) {\n\t\tconfig.thinkingConfig = getDisabledThinkingConfig(model);\n\t}\n\n\tif (options.signal) {\n\t\tif (options.signal.aborted) {\n\t\t\tthrow new Error(\"Request aborted\");\n\t\t}\n\t\tconfig.abortSignal = options.signal;\n\t}\n\n\tconst params: GenerateContentParameters = {\n\t\tmodel: model.id,\n\t\tcontents,\n\t\tconfig,\n\t};\n\n\treturn params;\n}\n\ntype ClampedThinkingLevel = Exclude<ThinkingLevel, \"xhigh\" | \"max\">;\n\nfunction isGemma4Model(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemma-?4/.test(model.id.toLowerCase());\n}\n\nfunction isGemini3ProModel(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemini-3(?:\\.\\d+)?-pro/.test(model.id.toLowerCase());\n}\n\nfunction isGemini3FlashModel(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemini-3(?:\\.\\d+)?-flash/.test(model.id.toLowerCase());\n}\n\nfunction getDisabledThinkingConfig(model: Model<\"google-generative-ai\">): ThinkingConfig {\n\t// Google docs: Gemini 3.1 Pro cannot disable thinking, and Gemini 3 Flash / Flash-Lite\n\t// do not support full thinking-off either. For Gemini 3 models, use the lowest supported\n\t// thinkingLevel without includeThoughts so hidden thinking remains invisible to pi.\n\tif (isGemini3ProModel(model)) {\n\t\treturn { thinkingLevel: \"LOW\" as any };\n\t}\n\tif (isGemini3FlashModel(model)) {\n\t\treturn { thinkingLevel: \"MINIMAL\" as any };\n\t}\n\tif (isGemma4Model(model)) {\n\t\treturn { thinkingLevel: \"MINIMAL\" as any };\n\t}\n\n\t// Gemini 2.x supports disabling via thinkingBudget = 0.\n\treturn { thinkingBudget: 0 };\n}\n\nfunction getThinkingLevel(effort: ClampedThinkingLevel, model: Model<\"google-generative-ai\">): GoogleThinkingLevel {\n\tif (isGemini3ProModel(model)) {\n\t\tswitch (effort) {\n\t\t\tcase \"minimal\":\n\t\t\tcase \"low\":\n\t\t\t\treturn \"LOW\";\n\t\t\tcase \"medium\":\n\t\t\tcase \"high\":\n\t\t\t\treturn \"HIGH\";\n\t\t}\n\t}\n\tif (isGemma4Model(model)) {\n\t\tswitch (effort) {\n\t\t\tcase \"minimal\":\n\t\t\tcase \"low\":\n\t\t\t\treturn \"MINIMAL\";\n\t\t\tcase \"medium\":\n\t\t\tcase \"high\":\n\t\t\t\treturn \"HIGH\";\n\t\t}\n\t}\n\tswitch (effort) {\n\t\tcase \"minimal\":\n\t\t\treturn \"MINIMAL\";\n\t\tcase \"low\":\n\t\t\treturn \"LOW\";\n\t\tcase \"medium\":\n\t\t\treturn \"MEDIUM\";\n\t\tcase \"high\":\n\t\t\treturn \"HIGH\";\n\t}\n}\n"]}
1
+ {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype GenerateContentConfig,\n\ttype GenerateContentParameters,\n\tGoogleGenAI,\n\ttype ThinkingConfig,\n} from \"@google/genai\";\nimport { getEnvApiKey } from \"../env-api-keys.js\";\nimport { calculateCost, clampThinkingLevel } from \"../models.js\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n\tTextContent,\n\tThinkingContent,\n\tThinkingLevel,\n\tToolCall,\n} from \"../types.js\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.js\";\nimport { sanitizeSurrogates } from \"../utils/sanitize-unicode.js\";\nimport {\n\tformatStreamFailureMessage,\n\trecordStreamFailure,\n\tstreamFailureFromStopReason,\n} from \"../utils/stream-failure.js\";\nimport type { GoogleThinkingLevel } from \"./google-shared.js\";\nimport {\n\tconvertMessages,\n\tconvertTools,\n\tgetGoogleThinkingBudget,\n\tisThinkingPart,\n\tmapStopReason,\n\tmapToolChoice,\n\tretainThoughtSignature,\n} from \"./google-shared.js\";\nimport { buildBaseOptions } from \"./simple-options.js\";\n\nexport interface GoogleOptions extends StreamOptions {\n\ttoolChoice?: \"auto\" | \"none\" | \"any\";\n\tthinking?: {\n\t\tenabled: boolean;\n\t\tbudgetTokens?: number; // -1 for dynamic, 0 to disable\n\t\tlevel?: GoogleThinkingLevel;\n\t};\n}\n\nlet toolCallCounter = 0;\n\nexport const streamGoogle: StreamFunction<\"google-generative-ai\", GoogleOptions> = (\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions?: GoogleOptions,\n): AssistantMessageEventStream => {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: \"google-generative-ai\" as Api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"stop\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey || getEnvApiKey(model.provider) || \"\";\n\t\t\tconst client = createClient(model, apiKey, options?.headers);\n\t\t\tlet params = buildParams(model, context, options);\n\t\t\tconst nextParams = await options?.onPayload?.(params, model);\n\t\t\tif (nextParams !== undefined) {\n\t\t\t\tparams = nextParams as GenerateContentParameters;\n\t\t\t}\n\t\t\tconst googleStream = await client.models.generateContentStream(params);\n\n\t\t\tstream.push({ type: \"start\", partial: output });\n\t\t\tlet currentBlock: TextContent | ThinkingContent | null = null;\n\t\t\tconst blocks = output.content;\n\t\t\tconst blockIndex = () => blocks.length - 1;\n\t\t\tfor await (const chunk of googleStream) {\n\t\t\t\t// @google/genai documents GenerateContentResponse.responseId as an output-only field\n\t\t\t\t// used to identify each response. Keep the first non-empty one from the stream.\n\t\t\t\toutput.responseId ||= chunk.responseId;\n\t\t\t\tconst candidate = chunk.candidates?.[0];\n\t\t\t\tif (candidate?.content?.parts) {\n\t\t\t\t\tfor (const part of candidate.content.parts) {\n\t\t\t\t\t\tif (part.text !== undefined) {\n\t\t\t\t\t\t\tconst isThinking = isThinkingPart(part);\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!currentBlock ||\n\t\t\t\t\t\t\t\t(isThinking && currentBlock.type !== \"thinking\") ||\n\t\t\t\t\t\t\t\t(!isThinking && currentBlock.type !== \"text\")\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tif (currentBlock) {\n\t\t\t\t\t\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: blocks.length - 1,\n\t\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (isThinking) {\n\t\t\t\t\t\t\t\t\tcurrentBlock = { type: \"thinking\", thinking: \"\", thinkingSignature: undefined };\n\t\t\t\t\t\t\t\t\toutput.content.push(currentBlock);\n\t\t\t\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcurrentBlock = { type: \"text\", text: \"\" };\n\t\t\t\t\t\t\t\t\toutput.content.push(currentBlock);\n\t\t\t\t\t\t\t\t\tstream.push({ type: \"text_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (currentBlock.type === \"thinking\") {\n\t\t\t\t\t\t\t\tcurrentBlock.thinking += part.text;\n\t\t\t\t\t\t\t\tcurrentBlock.thinkingSignature = retainThoughtSignature(\n\t\t\t\t\t\t\t\t\tcurrentBlock.thinkingSignature,\n\t\t\t\t\t\t\t\t\tpart.thoughtSignature,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\tdelta: part.text,\n\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcurrentBlock.text += part.text;\n\t\t\t\t\t\t\t\tcurrentBlock.textSignature = retainThoughtSignature(\n\t\t\t\t\t\t\t\t\tcurrentBlock.textSignature,\n\t\t\t\t\t\t\t\t\tpart.thoughtSignature,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\tdelta: part.text,\n\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (part.functionCall) {\n\t\t\t\t\t\t\tif (currentBlock) {\n\t\t\t\t\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcurrentBlock = null;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst providedId = part.functionCall.id;\n\t\t\t\t\t\t\tconst needsNewId =\n\t\t\t\t\t\t\t\t!providedId || output.content.some((b) => b.type === \"toolCall\" && b.id === providedId);\n\t\t\t\t\t\t\tconst toolCallId = needsNewId\n\t\t\t\t\t\t\t\t? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}`\n\t\t\t\t\t\t\t\t: providedId;\n\n\t\t\t\t\t\t\tconst toolCall: ToolCall = {\n\t\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\t\tid: toolCallId,\n\t\t\t\t\t\t\t\tname: part.functionCall.name || \"\",\n\t\t\t\t\t\t\t\targuments: (part.functionCall.args as Record<string, any>) ?? {},\n\t\t\t\t\t\t\t\t...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\toutput.content.push(toolCall);\n\t\t\t\t\t\t\tstream.push({ type: \"toolcall_start\", contentIndex: blockIndex(), partial: output });\n\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\t\t\tdelta: JSON.stringify(toolCall.arguments),\n\t\t\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstream.push({ type: \"toolcall_end\", contentIndex: blockIndex(), toolCall, partial: output });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (candidate?.finishReason) {\n\t\t\t\t\toutput.stopReason = mapStopReason(candidate.finishReason);\n\t\t\t\t\tif (output.content.some((b) => b.type === \"toolCall\")) {\n\t\t\t\t\t\toutput.stopReason = \"toolUse\";\n\t\t\t\t\t}\n\t\t\t\t\tif (output.stopReason === \"error\") {\n\t\t\t\t\t\toutput.stopReasonRaw = candidate.finishReason;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (chunk.usageMetadata) {\n\t\t\t\t\toutput.usage = {\n\t\t\t\t\t\tinput:\n\t\t\t\t\t\t\t(chunk.usageMetadata.promptTokenCount || 0) - (chunk.usageMetadata.cachedContentTokenCount || 0),\n\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t(chunk.usageMetadata.candidatesTokenCount || 0) + (chunk.usageMetadata.thoughtsTokenCount || 0),\n\t\t\t\t\t\tcacheRead: chunk.usageMetadata.cachedContentTokenCount || 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: chunk.usageMetadata.totalTokenCount || 0,\n\t\t\t\t\t\tcost: {\n\t\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\ttotal: 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tcalculateCost(model, output.usage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentBlock) {\n\t\t\t\tif (currentBlock.type === \"text\") {\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\tcontent: currentBlock.text,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\tcontentIndex: blockIndex(),\n\t\t\t\t\t\tcontent: currentBlock.thinking,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tif (output.stopReason === \"aborted\" || output.stopReason === \"error\") {\n\t\t\t\tthrow streamFailureFromStopReason(output.stopReasonRaw);\n\t\t\t}\n\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason, message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\t// Remove internal index property used during streaming\n\t\t\tfor (const block of output.content) {\n\t\t\t\tif (\"index\" in block) {\n\t\t\t\t\tdelete (block as { index?: number }).index;\n\t\t\t\t}\n\t\t\t}\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = formatStreamFailureMessage(error);\n\t\t\trecordStreamFailure(model, output, error);\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n};\n\nexport const streamSimpleGoogle: StreamFunction<\"google-generative-ai\", SimpleStreamOptions> = (\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n\tconst apiKey = options?.apiKey || getEnvApiKey(model.provider);\n\tif (!apiKey) {\n\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t}\n\n\tconst base = buildBaseOptions(model, options, apiKey);\n\tif (!options?.reasoning || options.reasoning === \"off\") {\n\t\treturn streamGoogle(model, context, { ...base, thinking: { enabled: false } } satisfies GoogleOptions);\n\t}\n\n\tconst clampedReasoning = clampThinkingLevel(model, options.reasoning);\n\tconst effort = (clampedReasoning === \"off\" ? \"high\" : clampedReasoning) as ClampedThinkingLevel;\n\tconst googleModel = model as Model<\"google-generative-ai\">;\n\n\tif (isGemini3ProModel(googleModel) || isGemini3FlashModel(googleModel) || isGemma4Model(googleModel)) {\n\t\treturn streamGoogle(model, context, {\n\t\t\t...base,\n\t\t\tthinking: {\n\t\t\t\tenabled: true,\n\t\t\t\tlevel: getThinkingLevel(effort, googleModel),\n\t\t\t},\n\t\t} satisfies GoogleOptions);\n\t}\n\n\treturn streamGoogle(model, context, {\n\t\t...base,\n\t\tthinking: {\n\t\t\tenabled: true,\n\t\t\tbudgetTokens: getGoogleThinkingBudget(googleModel.id, effort, options.thinkingBudgets),\n\t\t},\n\t} satisfies GoogleOptions);\n};\n\nfunction createClient(\n\tmodel: Model<\"google-generative-ai\">,\n\tapiKey?: string,\n\toptionsHeaders?: Record<string, string>,\n): GoogleGenAI {\n\tconst httpOptions: { baseUrl?: string; apiVersion?: string; headers?: Record<string, string> } = {};\n\tif (model.baseUrl) {\n\t\thttpOptions.baseUrl = model.baseUrl;\n\t\thttpOptions.apiVersion = \"\"; // baseUrl already includes version path, don't append\n\t}\n\tconst headers: Record<string, string> = { ...model.headers, ...optionsHeaders };\n\tconst isOAuthToken = apiKey && (apiKey.startsWith(\"ya29.\") || apiKey.startsWith(\"Bearer \"));\n\tif (isOAuthToken) {\n\t\theaders.Authorization = apiKey.startsWith(\"Bearer \") ? apiKey : `Bearer ${apiKey}`;\n\t\thttpOptions.headers = headers;\n\t\treturn new GoogleGenAI({\n\t\t\thttpOptions: Object.keys(httpOptions).length > 0 ? httpOptions : undefined,\n\t\t});\n\t}\n\tif (Object.keys(headers).length > 0) {\n\t\thttpOptions.headers = headers;\n\t}\n\n\treturn new GoogleGenAI({\n\t\tapiKey,\n\t\thttpOptions: Object.keys(httpOptions).length > 0 ? httpOptions : undefined,\n\t});\n}\n\nfunction buildParams(\n\tmodel: Model<\"google-generative-ai\">,\n\tcontext: Context,\n\toptions: GoogleOptions = {},\n): GenerateContentParameters {\n\tconst contents = convertMessages(model, context);\n\n\tconst generationConfig: GenerateContentConfig = {};\n\tif (options.temperature !== undefined) {\n\t\tgenerationConfig.temperature = options.temperature;\n\t}\n\tif (options.maxTokens !== undefined) {\n\t\tgenerationConfig.maxOutputTokens = options.maxTokens;\n\t}\n\n\tconst config: GenerateContentConfig = {\n\t\t...(Object.keys(generationConfig).length > 0 && generationConfig),\n\t\t...(context.systemPrompt && { systemInstruction: sanitizeSurrogates(context.systemPrompt) }),\n\t\t...(context.tools && context.tools.length > 0 && { tools: convertTools(context.tools) }),\n\t};\n\n\tif (context.tools && context.tools.length > 0 && options.toolChoice) {\n\t\tconfig.toolConfig = {\n\t\t\tfunctionCallingConfig: {\n\t\t\t\tmode: mapToolChoice(options.toolChoice),\n\t\t\t},\n\t\t};\n\t} else {\n\t\tconfig.toolConfig = undefined;\n\t}\n\n\tif (options.thinking?.enabled && model.reasoning) {\n\t\tconst thinkingConfig: ThinkingConfig = { includeThoughts: true };\n\t\tif (options.thinking.level !== undefined) {\n\t\t\t// Cast to any since our GoogleThinkingLevel mirrors Google's ThinkingLevel enum values\n\t\t\tthinkingConfig.thinkingLevel = options.thinking.level as any;\n\t\t} else if (options.thinking.budgetTokens !== undefined) {\n\t\t\tthinkingConfig.thinkingBudget = options.thinking.budgetTokens;\n\t\t}\n\t\tconfig.thinkingConfig = thinkingConfig;\n\t} else if (model.reasoning && options.thinking && !options.thinking.enabled) {\n\t\tconfig.thinkingConfig = getDisabledThinkingConfig(model);\n\t}\n\n\tif (options.signal) {\n\t\tif (options.signal.aborted) {\n\t\t\tthrow new Error(\"Request aborted\");\n\t\t}\n\t\tconfig.abortSignal = options.signal;\n\t}\n\n\tconst params: GenerateContentParameters = {\n\t\tmodel: model.id,\n\t\tcontents,\n\t\tconfig,\n\t};\n\n\treturn params;\n}\n\ntype ClampedThinkingLevel = Exclude<ThinkingLevel, \"xhigh\" | \"max\">;\n\nfunction isGemma4Model(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemma-?4/.test(model.id.toLowerCase());\n}\n\nfunction isGemini3ProModel(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemini-3(?:\\.\\d+)?-pro/.test(model.id.toLowerCase());\n}\n\nfunction isGemini3FlashModel(model: Model<\"google-generative-ai\">): boolean {\n\treturn /gemini-3(?:\\.\\d+)?-flash/.test(model.id.toLowerCase());\n}\n\nfunction getDisabledThinkingConfig(model: Model<\"google-generative-ai\">): ThinkingConfig {\n\t// Google docs: Gemini 3.1 Pro cannot disable thinking, and Gemini 3 Flash / Flash-Lite\n\t// do not support full thinking-off either. For Gemini 3 models, use the lowest supported\n\t// thinkingLevel without includeThoughts so hidden thinking remains invisible to pi.\n\tif (isGemini3ProModel(model)) {\n\t\treturn { thinkingLevel: \"LOW\" as any };\n\t}\n\tif (isGemini3FlashModel(model)) {\n\t\treturn { thinkingLevel: \"MINIMAL\" as any };\n\t}\n\tif (isGemma4Model(model)) {\n\t\treturn { thinkingLevel: \"MINIMAL\" as any };\n\t}\n\n\t// Gemini 2.x supports disabling via thinkingBudget = 0.\n\treturn { thinkingBudget: 0 };\n}\n\nfunction getThinkingLevel(effort: ClampedThinkingLevel, model: Model<\"google-generative-ai\">): GoogleThinkingLevel {\n\tif (isGemini3ProModel(model)) {\n\t\tswitch (effort) {\n\t\t\tcase \"minimal\":\n\t\t\tcase \"low\":\n\t\t\t\treturn \"LOW\";\n\t\t\tcase \"medium\":\n\t\t\tcase \"high\":\n\t\t\t\treturn \"HIGH\";\n\t\t}\n\t}\n\tif (isGemma4Model(model)) {\n\t\tswitch (effort) {\n\t\t\tcase \"minimal\":\n\t\t\tcase \"low\":\n\t\t\t\treturn \"MINIMAL\";\n\t\t\tcase \"medium\":\n\t\t\tcase \"high\":\n\t\t\t\treturn \"HIGH\";\n\t\t}\n\t}\n\tswitch (effort) {\n\t\tcase \"minimal\":\n\t\t\treturn \"MINIMAL\";\n\t\tcase \"low\":\n\t\t\treturn \"LOW\";\n\t\tcase \"medium\":\n\t\t\treturn \"MEDIUM\";\n\t\tcase \"high\":\n\t\t\treturn \"HIGH\";\n\t}\n}\n"]}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Google Gemini OAuth flow
3
+ *
4
+ * NOTE: This module uses Node.js http.createServer for the OAuth callback server.
5
+ * It is only intended for CLI use, not browser environments.
6
+ */
7
+ import type { OAuthCredentials, OAuthPrompt, OAuthProviderInterface } from "./types.js";
8
+ export declare function parseAuthorizationInput(input: string): {
9
+ code?: string;
10
+ state?: string;
11
+ apiKey?: string;
12
+ };
13
+ /**
14
+ * Login with Google Gemini OAuth (authorization code + PKCE)
15
+ */
16
+ export declare function loginGemini(options: {
17
+ onAuth: (info: {
18
+ url: string;
19
+ instructions?: string;
20
+ }) => void;
21
+ onPrompt: (prompt: OAuthPrompt) => Promise<string>;
22
+ onProgress?: (message: string) => void;
23
+ onManualCodeInput?: () => Promise<string>;
24
+ }): Promise<OAuthCredentials>;
25
+ /**
26
+ * Refresh Google Gemini OAuth token
27
+ */
28
+ export declare function refreshGeminiToken(refreshToken: string): Promise<OAuthCredentials>;
29
+ export declare const geminiOAuthProvider: OAuthProviderInterface;
30
+ export declare const googleOAuthProvider: OAuthProviderInterface;
31
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/gemini.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAuB,WAAW,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAqD7G,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAgCzG;AA0JD;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAC1C,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/D,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmI5B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuCxF;AAED,eAAO,MAAM,mBAAmB,EAAE,sBAwBjC,CAAC;AAEF,eAAO,MAAM,mBAAmB,wBAAsB,CAAC","sourcesContent":["/**\n * Google Gemini OAuth flow\n *\n * NOTE: This module uses Node.js http.createServer for the OAuth callback server.\n * It is only intended for CLI use, not browser environments.\n */\n\nimport type { Server } from \"node:http\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.js\";\nimport { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthPrompt, OAuthProviderInterface } from \"./types.js\";\n\ntype CallbackServerInfo = {\n\tserver: Server;\n\tredirectUri: string;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string; state: string } | null>;\n};\n\ntype NodeApis = {\n\tcreateServer: typeof import(\"node:http\").createServer;\n};\n\nlet nodeApis: NodeApis | null = null;\nlet nodeApisPromise: Promise<NodeApis> | null = null;\n\nconst DEFAULT_CLIENT_ID = \"681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com\";\nconst AUTHORIZE_URL = \"https://accounts.google.com/o/oauth2/v2/auth\";\nconst TOKEN_URL = \"https://oauth2.googleapis.com/token\";\nconst CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || \"127.0.0.1\";\nconst CALLBACK_PORT = Number(process.env.GEMINI_OAUTH_CALLBACK_PORT || process.env.GOOGLE_OAUTH_CALLBACK_PORT || 53694);\nconst CALLBACK_PATH = \"/oauth2callback\";\nconst REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;\nconst SCOPES = [\n\t\"https://www.googleapis.com/auth/generative-language\",\n\t\"https://www.googleapis.com/auth/cloud-platform\",\n\t\"openid\",\n\t\"https://www.googleapis.com/auth/userinfo.email\",\n\t\"https://www.googleapis.com/auth/userinfo.profile\",\n].join(\" \");\n\nfunction getClientId(): string {\n\treturn process.env.GEMINI_OAUTH_CLIENT_ID || process.env.GOOGLE_OAUTH_CLIENT_ID || DEFAULT_CLIENT_ID;\n}\n\nfunction getClientSecret(): string | undefined {\n\treturn process.env.GEMINI_OAUTH_CLIENT_SECRET || process.env.GOOGLE_OAUTH_CLIENT_SECRET || undefined;\n}\n\nasync function getNodeApis(): Promise<NodeApis> {\n\tif (nodeApis) return nodeApis;\n\tif (!nodeApisPromise) {\n\t\tif (typeof process === \"undefined\" || (!process.versions?.node && !process.versions?.bun)) {\n\t\t\tthrow new Error(\"Gemini OAuth is only available in Node.js environments\");\n\t\t}\n\t\tnodeApisPromise = import(\"node:http\").then((httpModule) => ({\n\t\t\tcreateServer: httpModule.createServer,\n\t\t}));\n\t}\n\tnodeApis = await nodeApisPromise;\n\treturn nodeApis;\n}\n\nexport function parseAuthorizationInput(input: string): { code?: string; state?: string; apiKey?: string } {\n\tconst value = input.trim();\n\tif (!value) return {};\n\n\tif (value.startsWith(\"AIza\") || (value.startsWith(\"ya29.\") && !value.includes(\"=\") && !value.includes(\"&\"))) {\n\t\treturn { apiKey: value };\n\t}\n\n\ttry {\n\t\tconst url = new URL(value);\n\t\treturn {\n\t\t\tcode: url.searchParams.get(\"code\") ?? undefined,\n\t\t\tstate: url.searchParams.get(\"state\") ?? undefined,\n\t\t};\n\t} catch {\n\t\t// not a URL\n\t}\n\n\tif (value.includes(\"#\")) {\n\t\tconst [code, state] = value.split(\"#\", 2);\n\t\treturn { code, state };\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\tconst params = new URLSearchParams(value);\n\t\treturn {\n\t\t\tcode: params.get(\"code\") ?? undefined,\n\t\t\tstate: params.get(\"state\") ?? undefined,\n\t\t};\n\t}\n\n\treturn { code: value };\n}\n\nfunction formatErrorDetails(error: unknown): string {\n\tif (error instanceof Error) {\n\t\tconst details: string[] = [`${error.name}: ${error.message}`];\n\t\tconst errorWithCode = error as Error & { code?: string; errno?: number | string; cause?: unknown };\n\t\tif (errorWithCode.code) details.push(`code=${errorWithCode.code}`);\n\t\tif (typeof errorWithCode.errno !== \"undefined\") details.push(`errno=${String(errorWithCode.errno)}`);\n\t\tif (typeof error.cause !== \"undefined\") {\n\t\t\tdetails.push(`cause=${formatErrorDetails(error.cause)}`);\n\t\t}\n\t\tif (error.stack) {\n\t\t\tdetails.push(`stack=${error.stack}`);\n\t\t}\n\t\treturn details.join(\"; \");\n\t}\n\treturn String(error);\n}\n\nasync function startCallbackServer(expectedState: string): Promise<CallbackServerInfo> {\n\tconst { createServer } = await getNodeApis();\n\n\treturn new Promise((resolve, reject) => {\n\t\tlet settleWait: ((value: { code: string; state: string } | null) => void) | undefined;\n\t\tconst waitForCodePromise = new Promise<{ code: string; state: string } | null>((resolveWait) => {\n\t\t\tlet settled = false;\n\t\t\tsettleWait = (value) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolveWait(value);\n\t\t\t};\n\t\t});\n\n\t\tconst server = createServer((req, res) => {\n\t\t\ttry {\n\t\t\t\tconst url = new URL(req.url || \"\", \"http://localhost\");\n\t\t\t\tif (url.pathname !== CALLBACK_PATH && url.pathname !== \"/callback\") {\n\t\t\t\t\tres.writeHead(404, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\t\tres.end(oauthErrorHtml(\"Callback route not found.\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst code = url.searchParams.get(\"code\");\n\t\t\t\tconst state = url.searchParams.get(\"state\");\n\t\t\t\tconst error = url.searchParams.get(\"error\");\n\n\t\t\t\tif (error) {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\t\tres.end(oauthErrorHtml(\"Google Gemini authentication did not complete.\", `Error: ${error}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!code || !state) {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\t\tres.end(oauthErrorHtml(\"Missing code or state parameter.\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (state !== expectedState) {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\t\tres.end(oauthErrorHtml(\"State mismatch.\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tres.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\tres.end(oauthSuccessHtml(\"Google Gemini authentication completed. You can close this window.\"));\n\t\t\t\tsettleWait?.({ code, state });\n\t\t\t} catch {\n\t\t\t\tres.writeHead(500, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n\t\t\t\tres.end(\"Internal error\");\n\t\t\t}\n\t\t});\n\n\t\tserver.on(\"error\", (err) => {\n\t\t\treject(err);\n\t\t});\n\n\t\tserver.listen(CALLBACK_PORT, CALLBACK_HOST, () => {\n\t\t\tresolve({\n\t\t\t\tserver,\n\t\t\t\tredirectUri: REDIRECT_URI,\n\t\t\t\tcancelWait: () => {\n\t\t\t\t\tsettleWait?.(null);\n\t\t\t\t},\n\t\t\t\twaitForCode: () => waitForCodePromise,\n\t\t\t});\n\t\t});\n\t});\n}\n\nasync function postForm(url: string, params: Record<string, string>): Promise<string> {\n\tconst body = new URLSearchParams(params);\n\tconst response = await fetch(url, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: body.toString(),\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\n\tconst responseBody = await response.text();\n\n\tif (!response.ok) {\n\t\tthrow new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);\n\t}\n\n\treturn responseBody;\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n): Promise<OAuthCredentials> {\n\tconst clientId = getClientId();\n\tconst clientSecret = getClientSecret();\n\tconst payload: Record<string, string> = {\n\t\tgrant_type: \"authorization_code\",\n\t\tclient_id: clientId,\n\t\tcode,\n\t\tredirect_uri: redirectUri,\n\t\tcode_verifier: verifier,\n\t};\n\tif (clientSecret) {\n\t\tpayload.client_secret = clientSecret;\n\t}\n\n\tlet responseBody: string;\n\ttry {\n\t\tresponseBody = await postForm(TOKEN_URL, payload);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\tlet tokenData: { access_token: string; refresh_token?: string; expires_in?: number };\n\ttry {\n\t\ttokenData = JSON.parse(responseBody) as { access_token: string; refresh_token?: string; expires_in?: number };\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\treturn {\n\t\taccess: tokenData.access_token,\n\t\trefresh: tokenData.refresh_token || \"\",\n\t\texpires: Date.now() + (tokenData.expires_in ?? 3600) * 1000 - 5 * 60 * 1000,\n\t};\n}\n\n/**\n * Login with Google Gemini OAuth (authorization code + PKCE)\n */\nexport async function loginGemini(options: {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonPrompt: (prompt: OAuthPrompt) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tonManualCodeInput?: () => Promise<string>;\n}): Promise<OAuthCredentials> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst server = await startCallbackServer(verifier);\n\n\tlet code: string | undefined;\n\tlet state: string | undefined;\n\tlet manualApiKey: string | undefined;\n\tlet redirectUriForExchange = REDIRECT_URI;\n\n\ttry {\n\t\tconst clientId = getClientId();\n\t\tconst authParams = new URLSearchParams({\n\t\t\tclient_id: clientId,\n\t\t\tresponse_type: \"code\",\n\t\t\tredirect_uri: REDIRECT_URI,\n\t\t\tscope: SCOPES,\n\t\t\tcode_challenge: challenge,\n\t\t\tcode_challenge_method: \"S256\",\n\t\t\tstate: verifier,\n\t\t\taccess_type: \"offline\",\n\t\t\tprompt: \"consent\",\n\t\t});\n\n\t\toptions.onAuth({\n\t\t\turl: `${AUTHORIZE_URL}?${authParams.toString()}`,\n\t\t\tinstructions:\n\t\t\t\t\"Complete login in your browser. If the browser is on another machine, paste the final redirect URL or an API key here.\",\n\t\t});\n\n\t\tif (options.onManualCodeInput) {\n\t\t\tlet manualInput: string | undefined;\n\t\t\tlet manualError: Error | undefined;\n\t\t\tconst manualPromise = options\n\t\t\t\t.onManualCodeInput()\n\t\t\t\t.then((input) => {\n\t\t\t\t\tmanualInput = input;\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tmanualError = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t});\n\n\t\t\tconst result = await server.waitForCode();\n\n\t\t\tif (manualError) {\n\t\t\t\tthrow manualError;\n\t\t\t}\n\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t\tstate = result.state;\n\t\t\t\tredirectUriForExchange = REDIRECT_URI;\n\t\t\t} else if (manualInput) {\n\t\t\t\tconst parsed = parseAuthorizationInput(manualInput);\n\t\t\t\tif (parsed.apiKey) {\n\t\t\t\t\tmanualApiKey = parsed.apiKey;\n\t\t\t\t} else {\n\t\t\t\t\tif (parsed.state && parsed.state !== verifier) {\n\t\t\t\t\t\tthrow new Error(\"OAuth state mismatch\");\n\t\t\t\t\t}\n\t\t\t\t\tcode = parsed.code;\n\t\t\t\t\tstate = parsed.state ?? verifier;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!code && !manualApiKey) {\n\t\t\t\tawait manualPromise;\n\t\t\t\tif (manualError) {\n\t\t\t\t\tthrow manualError;\n\t\t\t\t}\n\t\t\t\tif (manualInput) {\n\t\t\t\t\tconst parsed = parseAuthorizationInput(manualInput);\n\t\t\t\t\tif (parsed.apiKey) {\n\t\t\t\t\t\tmanualApiKey = parsed.apiKey;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (parsed.state && parsed.state !== verifier) {\n\t\t\t\t\t\t\tthrow new Error(\"OAuth state mismatch\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcode = parsed.code;\n\t\t\t\t\t\tstate = parsed.state ?? verifier;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst result = await server.waitForCode();\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t\tstate = result.state;\n\t\t\t\tredirectUriForExchange = REDIRECT_URI;\n\t\t\t}\n\t\t}\n\n\t\tif (!code && !manualApiKey) {\n\t\t\tconst input = await options.onPrompt({\n\t\t\t\tmessage: \"Paste the authorization code, redirect URL, or Gemini API key:\",\n\t\t\t\tplaceholder: REDIRECT_URI,\n\t\t\t});\n\t\t\tconst parsed = parseAuthorizationInput(input);\n\t\t\tif (parsed.apiKey) {\n\t\t\t\tmanualApiKey = parsed.apiKey;\n\t\t\t} else {\n\t\t\t\tif (parsed.state && parsed.state !== verifier) {\n\t\t\t\t\tthrow new Error(\"OAuth state mismatch\");\n\t\t\t\t}\n\t\t\t\tcode = parsed.code;\n\t\t\t\tstate = parsed.state ?? verifier;\n\t\t\t}\n\t\t}\n\n\t\tif (manualApiKey) {\n\t\t\treturn {\n\t\t\t\taccess: manualApiKey,\n\t\t\t\trefresh: \"\",\n\t\t\t\texpires: Date.now() + 365 * 24 * 60 * 60 * 1000,\n\t\t\t};\n\t\t}\n\n\t\tif (!code) {\n\t\t\tthrow new Error(\"Missing authorization code\");\n\t\t}\n\n\t\tif (!state) {\n\t\t\tthrow new Error(\"Missing OAuth state\");\n\t\t}\n\n\t\toptions.onProgress?.(\"Exchanging authorization code for tokens...\");\n\t\treturn exchangeAuthorizationCode(code, verifier, redirectUriForExchange);\n\t} finally {\n\t\tserver.server.close();\n\t}\n}\n\n/**\n * Refresh Google Gemini OAuth token\n */\nexport async function refreshGeminiToken(refreshToken: string): Promise<OAuthCredentials> {\n\tif (!refreshToken) {\n\t\tthrow new Error(\"No refresh token available for Google Gemini\");\n\t}\n\n\tconst clientId = getClientId();\n\tconst clientSecret = getClientSecret();\n\tconst payload: Record<string, string> = {\n\t\tgrant_type: \"refresh_token\",\n\t\tclient_id: clientId,\n\t\trefresh_token: refreshToken,\n\t};\n\tif (clientSecret) {\n\t\tpayload.client_secret = clientSecret;\n\t}\n\n\tlet responseBody: string;\n\ttry {\n\t\tresponseBody = await postForm(TOKEN_URL, payload);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Google Gemini token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\tlet data: { access_token: string; refresh_token?: string; expires_in?: number };\n\ttry {\n\t\tdata = JSON.parse(responseBody) as { access_token: string; refresh_token?: string; expires_in?: number };\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Google Gemini token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\treturn {\n\t\taccess: data.access_token,\n\t\trefresh: data.refresh_token || refreshToken,\n\t\texpires: Date.now() + (data.expires_in ?? 3600) * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nexport const geminiOAuthProvider: OAuthProviderInterface = {\n\tid: \"google\",\n\tname: \"Google Gemini\",\n\tusesCallbackServer: true,\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginGemini({\n\t\t\tonAuth: callbacks.onAuth,\n\t\t\tonPrompt: callbacks.onPrompt,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tonManualCodeInput: callbacks.onManualCodeInput,\n\t\t});\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\tif (!credentials.refresh) {\n\t\t\treturn credentials;\n\t\t}\n\t\treturn refreshGeminiToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n\nexport const googleOAuthProvider = geminiOAuthProvider;\n"]}
@@ -0,0 +1,393 @@
1
+ /**
2
+ * Google Gemini OAuth flow
3
+ *
4
+ * NOTE: This module uses Node.js http.createServer for the OAuth callback server.
5
+ * It is only intended for CLI use, not browser environments.
6
+ */
7
+ import { oauthErrorHtml, oauthSuccessHtml } from "./oauth-page.js";
8
+ import { generatePKCE } from "./pkce.js";
9
+ let nodeApis = null;
10
+ let nodeApisPromise = null;
11
+ const DEFAULT_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com";
12
+ const AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth";
13
+ const TOKEN_URL = "https://oauth2.googleapis.com/token";
14
+ const CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
15
+ const CALLBACK_PORT = Number(process.env.GEMINI_OAUTH_CALLBACK_PORT || process.env.GOOGLE_OAUTH_CALLBACK_PORT || 53694);
16
+ const CALLBACK_PATH = "/oauth2callback";
17
+ const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
18
+ const SCOPES = [
19
+ "https://www.googleapis.com/auth/generative-language",
20
+ "https://www.googleapis.com/auth/cloud-platform",
21
+ "openid",
22
+ "https://www.googleapis.com/auth/userinfo.email",
23
+ "https://www.googleapis.com/auth/userinfo.profile",
24
+ ].join(" ");
25
+ function getClientId() {
26
+ return process.env.GEMINI_OAUTH_CLIENT_ID || process.env.GOOGLE_OAUTH_CLIENT_ID || DEFAULT_CLIENT_ID;
27
+ }
28
+ function getClientSecret() {
29
+ return process.env.GEMINI_OAUTH_CLIENT_SECRET || process.env.GOOGLE_OAUTH_CLIENT_SECRET || undefined;
30
+ }
31
+ async function getNodeApis() {
32
+ if (nodeApis)
33
+ return nodeApis;
34
+ if (!nodeApisPromise) {
35
+ if (typeof process === "undefined" || (!process.versions?.node && !process.versions?.bun)) {
36
+ throw new Error("Gemini OAuth is only available in Node.js environments");
37
+ }
38
+ nodeApisPromise = import("node:http").then((httpModule) => ({
39
+ createServer: httpModule.createServer,
40
+ }));
41
+ }
42
+ nodeApis = await nodeApisPromise;
43
+ return nodeApis;
44
+ }
45
+ export function parseAuthorizationInput(input) {
46
+ const value = input.trim();
47
+ if (!value)
48
+ return {};
49
+ if (value.startsWith("AIza") || (value.startsWith("ya29.") && !value.includes("=") && !value.includes("&"))) {
50
+ return { apiKey: value };
51
+ }
52
+ try {
53
+ const url = new URL(value);
54
+ return {
55
+ code: url.searchParams.get("code") ?? undefined,
56
+ state: url.searchParams.get("state") ?? undefined,
57
+ };
58
+ }
59
+ catch {
60
+ // not a URL
61
+ }
62
+ if (value.includes("#")) {
63
+ const [code, state] = value.split("#", 2);
64
+ return { code, state };
65
+ }
66
+ if (value.includes("code=")) {
67
+ const params = new URLSearchParams(value);
68
+ return {
69
+ code: params.get("code") ?? undefined,
70
+ state: params.get("state") ?? undefined,
71
+ };
72
+ }
73
+ return { code: value };
74
+ }
75
+ function formatErrorDetails(error) {
76
+ if (error instanceof Error) {
77
+ const details = [`${error.name}: ${error.message}`];
78
+ const errorWithCode = error;
79
+ if (errorWithCode.code)
80
+ details.push(`code=${errorWithCode.code}`);
81
+ if (typeof errorWithCode.errno !== "undefined")
82
+ details.push(`errno=${String(errorWithCode.errno)}`);
83
+ if (typeof error.cause !== "undefined") {
84
+ details.push(`cause=${formatErrorDetails(error.cause)}`);
85
+ }
86
+ if (error.stack) {
87
+ details.push(`stack=${error.stack}`);
88
+ }
89
+ return details.join("; ");
90
+ }
91
+ return String(error);
92
+ }
93
+ async function startCallbackServer(expectedState) {
94
+ const { createServer } = await getNodeApis();
95
+ return new Promise((resolve, reject) => {
96
+ let settleWait;
97
+ const waitForCodePromise = new Promise((resolveWait) => {
98
+ let settled = false;
99
+ settleWait = (value) => {
100
+ if (settled)
101
+ return;
102
+ settled = true;
103
+ resolveWait(value);
104
+ };
105
+ });
106
+ const server = createServer((req, res) => {
107
+ try {
108
+ const url = new URL(req.url || "", "http://localhost");
109
+ if (url.pathname !== CALLBACK_PATH && url.pathname !== "/callback") {
110
+ res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
111
+ res.end(oauthErrorHtml("Callback route not found."));
112
+ return;
113
+ }
114
+ const code = url.searchParams.get("code");
115
+ const state = url.searchParams.get("state");
116
+ const error = url.searchParams.get("error");
117
+ if (error) {
118
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
119
+ res.end(oauthErrorHtml("Google Gemini authentication did not complete.", `Error: ${error}`));
120
+ return;
121
+ }
122
+ if (!code || !state) {
123
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
124
+ res.end(oauthErrorHtml("Missing code or state parameter."));
125
+ return;
126
+ }
127
+ if (state !== expectedState) {
128
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
129
+ res.end(oauthErrorHtml("State mismatch."));
130
+ return;
131
+ }
132
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
133
+ res.end(oauthSuccessHtml("Google Gemini authentication completed. You can close this window."));
134
+ settleWait?.({ code, state });
135
+ }
136
+ catch {
137
+ res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
138
+ res.end("Internal error");
139
+ }
140
+ });
141
+ server.on("error", (err) => {
142
+ reject(err);
143
+ });
144
+ server.listen(CALLBACK_PORT, CALLBACK_HOST, () => {
145
+ resolve({
146
+ server,
147
+ redirectUri: REDIRECT_URI,
148
+ cancelWait: () => {
149
+ settleWait?.(null);
150
+ },
151
+ waitForCode: () => waitForCodePromise,
152
+ });
153
+ });
154
+ });
155
+ }
156
+ async function postForm(url, params) {
157
+ const body = new URLSearchParams(params);
158
+ const response = await fetch(url, {
159
+ method: "POST",
160
+ headers: {
161
+ "Content-Type": "application/x-www-form-urlencoded",
162
+ Accept: "application/json",
163
+ },
164
+ body: body.toString(),
165
+ signal: AbortSignal.timeout(30_000),
166
+ });
167
+ const responseBody = await response.text();
168
+ if (!response.ok) {
169
+ throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
170
+ }
171
+ return responseBody;
172
+ }
173
+ async function exchangeAuthorizationCode(code, verifier, redirectUri) {
174
+ const clientId = getClientId();
175
+ const clientSecret = getClientSecret();
176
+ const payload = {
177
+ grant_type: "authorization_code",
178
+ client_id: clientId,
179
+ code,
180
+ redirect_uri: redirectUri,
181
+ code_verifier: verifier,
182
+ };
183
+ if (clientSecret) {
184
+ payload.client_secret = clientSecret;
185
+ }
186
+ let responseBody;
187
+ try {
188
+ responseBody = await postForm(TOKEN_URL, payload);
189
+ }
190
+ catch (error) {
191
+ throw new Error(`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; details=${formatErrorDetails(error)}`);
192
+ }
193
+ let tokenData;
194
+ try {
195
+ tokenData = JSON.parse(responseBody);
196
+ }
197
+ catch (error) {
198
+ throw new Error(`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`);
199
+ }
200
+ return {
201
+ access: tokenData.access_token,
202
+ refresh: tokenData.refresh_token || "",
203
+ expires: Date.now() + (tokenData.expires_in ?? 3600) * 1000 - 5 * 60 * 1000,
204
+ };
205
+ }
206
+ /**
207
+ * Login with Google Gemini OAuth (authorization code + PKCE)
208
+ */
209
+ export async function loginGemini(options) {
210
+ const { verifier, challenge } = await generatePKCE();
211
+ const server = await startCallbackServer(verifier);
212
+ let code;
213
+ let state;
214
+ let manualApiKey;
215
+ let redirectUriForExchange = REDIRECT_URI;
216
+ try {
217
+ const clientId = getClientId();
218
+ const authParams = new URLSearchParams({
219
+ client_id: clientId,
220
+ response_type: "code",
221
+ redirect_uri: REDIRECT_URI,
222
+ scope: SCOPES,
223
+ code_challenge: challenge,
224
+ code_challenge_method: "S256",
225
+ state: verifier,
226
+ access_type: "offline",
227
+ prompt: "consent",
228
+ });
229
+ options.onAuth({
230
+ url: `${AUTHORIZE_URL}?${authParams.toString()}`,
231
+ instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL or an API key here.",
232
+ });
233
+ if (options.onManualCodeInput) {
234
+ let manualInput;
235
+ let manualError;
236
+ const manualPromise = options
237
+ .onManualCodeInput()
238
+ .then((input) => {
239
+ manualInput = input;
240
+ server.cancelWait();
241
+ })
242
+ .catch((err) => {
243
+ manualError = err instanceof Error ? err : new Error(String(err));
244
+ server.cancelWait();
245
+ });
246
+ const result = await server.waitForCode();
247
+ if (manualError) {
248
+ throw manualError;
249
+ }
250
+ if (result?.code) {
251
+ code = result.code;
252
+ state = result.state;
253
+ redirectUriForExchange = REDIRECT_URI;
254
+ }
255
+ else if (manualInput) {
256
+ const parsed = parseAuthorizationInput(manualInput);
257
+ if (parsed.apiKey) {
258
+ manualApiKey = parsed.apiKey;
259
+ }
260
+ else {
261
+ if (parsed.state && parsed.state !== verifier) {
262
+ throw new Error("OAuth state mismatch");
263
+ }
264
+ code = parsed.code;
265
+ state = parsed.state ?? verifier;
266
+ }
267
+ }
268
+ if (!code && !manualApiKey) {
269
+ await manualPromise;
270
+ if (manualError) {
271
+ throw manualError;
272
+ }
273
+ if (manualInput) {
274
+ const parsed = parseAuthorizationInput(manualInput);
275
+ if (parsed.apiKey) {
276
+ manualApiKey = parsed.apiKey;
277
+ }
278
+ else {
279
+ if (parsed.state && parsed.state !== verifier) {
280
+ throw new Error("OAuth state mismatch");
281
+ }
282
+ code = parsed.code;
283
+ state = parsed.state ?? verifier;
284
+ }
285
+ }
286
+ }
287
+ }
288
+ else {
289
+ const result = await server.waitForCode();
290
+ if (result?.code) {
291
+ code = result.code;
292
+ state = result.state;
293
+ redirectUriForExchange = REDIRECT_URI;
294
+ }
295
+ }
296
+ if (!code && !manualApiKey) {
297
+ const input = await options.onPrompt({
298
+ message: "Paste the authorization code, redirect URL, or Gemini API key:",
299
+ placeholder: REDIRECT_URI,
300
+ });
301
+ const parsed = parseAuthorizationInput(input);
302
+ if (parsed.apiKey) {
303
+ manualApiKey = parsed.apiKey;
304
+ }
305
+ else {
306
+ if (parsed.state && parsed.state !== verifier) {
307
+ throw new Error("OAuth state mismatch");
308
+ }
309
+ code = parsed.code;
310
+ state = parsed.state ?? verifier;
311
+ }
312
+ }
313
+ if (manualApiKey) {
314
+ return {
315
+ access: manualApiKey,
316
+ refresh: "",
317
+ expires: Date.now() + 365 * 24 * 60 * 60 * 1000,
318
+ };
319
+ }
320
+ if (!code) {
321
+ throw new Error("Missing authorization code");
322
+ }
323
+ if (!state) {
324
+ throw new Error("Missing OAuth state");
325
+ }
326
+ options.onProgress?.("Exchanging authorization code for tokens...");
327
+ return exchangeAuthorizationCode(code, verifier, redirectUriForExchange);
328
+ }
329
+ finally {
330
+ server.server.close();
331
+ }
332
+ }
333
+ /**
334
+ * Refresh Google Gemini OAuth token
335
+ */
336
+ export async function refreshGeminiToken(refreshToken) {
337
+ if (!refreshToken) {
338
+ throw new Error("No refresh token available for Google Gemini");
339
+ }
340
+ const clientId = getClientId();
341
+ const clientSecret = getClientSecret();
342
+ const payload = {
343
+ grant_type: "refresh_token",
344
+ client_id: clientId,
345
+ refresh_token: refreshToken,
346
+ };
347
+ if (clientSecret) {
348
+ payload.client_secret = clientSecret;
349
+ }
350
+ let responseBody;
351
+ try {
352
+ responseBody = await postForm(TOKEN_URL, payload);
353
+ }
354
+ catch (error) {
355
+ throw new Error(`Google Gemini token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`);
356
+ }
357
+ let data;
358
+ try {
359
+ data = JSON.parse(responseBody);
360
+ }
361
+ catch (error) {
362
+ throw new Error(`Google Gemini token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`);
363
+ }
364
+ return {
365
+ access: data.access_token,
366
+ refresh: data.refresh_token || refreshToken,
367
+ expires: Date.now() + (data.expires_in ?? 3600) * 1000 - 5 * 60 * 1000,
368
+ };
369
+ }
370
+ export const geminiOAuthProvider = {
371
+ id: "google",
372
+ name: "Google Gemini",
373
+ usesCallbackServer: true,
374
+ async login(callbacks) {
375
+ return loginGemini({
376
+ onAuth: callbacks.onAuth,
377
+ onPrompt: callbacks.onPrompt,
378
+ onProgress: callbacks.onProgress,
379
+ onManualCodeInput: callbacks.onManualCodeInput,
380
+ });
381
+ },
382
+ async refreshToken(credentials) {
383
+ if (!credentials.refresh) {
384
+ return credentials;
385
+ }
386
+ return refreshGeminiToken(credentials.refresh);
387
+ },
388
+ getApiKey(credentials) {
389
+ return credentials.access;
390
+ },
391
+ };
392
+ export const googleOAuthProvider = geminiOAuthProvider;
393
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../src/utils/oauth/gemini.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Google Gemini OAuth flow\n *\n * NOTE: This module uses Node.js http.createServer for the OAuth callback server.\n * It is only intended for CLI use, not browser environments.\n */\n\nimport type { Server } from \"node:http\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.js\";\nimport { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthPrompt, OAuthProviderInterface } from \"./types.js\";\n\ntype CallbackServerInfo = {\n\tserver: Server;\n\tredirectUri: string;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string; state: string } | null>;\n};\n\ntype NodeApis = {\n\tcreateServer: typeof import(\"node:http\").createServer;\n};\n\nlet nodeApis: NodeApis | null = null;\nlet nodeApisPromise: Promise<NodeApis> | null = null;\n\nconst DEFAULT_CLIENT_ID = \"681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com\";\nconst AUTHORIZE_URL = \"https://accounts.google.com/o/oauth2/v2/auth\";\nconst TOKEN_URL = \"https://oauth2.googleapis.com/token\";\nconst CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || \"127.0.0.1\";\nconst CALLBACK_PORT = Number(process.env.GEMINI_OAUTH_CALLBACK_PORT || process.env.GOOGLE_OAUTH_CALLBACK_PORT || 53694);\nconst CALLBACK_PATH = \"/oauth2callback\";\nconst REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;\nconst SCOPES = [\n\t\"https://www.googleapis.com/auth/generative-language\",\n\t\"https://www.googleapis.com/auth/cloud-platform\",\n\t\"openid\",\n\t\"https://www.googleapis.com/auth/userinfo.email\",\n\t\"https://www.googleapis.com/auth/userinfo.profile\",\n].join(\" \");\n\nfunction getClientId(): string {\n\treturn process.env.GEMINI_OAUTH_CLIENT_ID || process.env.GOOGLE_OAUTH_CLIENT_ID || DEFAULT_CLIENT_ID;\n}\n\nfunction getClientSecret(): string | undefined {\n\treturn process.env.GEMINI_OAUTH_CLIENT_SECRET || process.env.GOOGLE_OAUTH_CLIENT_SECRET || undefined;\n}\n\nasync function getNodeApis(): Promise<NodeApis> {\n\tif (nodeApis) return nodeApis;\n\tif (!nodeApisPromise) {\n\t\tif (typeof process === \"undefined\" || (!process.versions?.node && !process.versions?.bun)) {\n\t\t\tthrow new Error(\"Gemini OAuth is only available in Node.js environments\");\n\t\t}\n\t\tnodeApisPromise = import(\"node:http\").then((httpModule) => ({\n\t\t\tcreateServer: httpModule.createServer,\n\t\t}));\n\t}\n\tnodeApis = await nodeApisPromise;\n\treturn nodeApis;\n}\n\nexport function parseAuthorizationInput(input: string): { code?: string; state?: string; apiKey?: string } {\n\tconst value = input.trim();\n\tif (!value) return {};\n\n\tif (value.startsWith(\"AIza\") || (value.startsWith(\"ya29.\") && !value.includes(\"=\") && !value.includes(\"&\"))) {\n\t\treturn { apiKey: value };\n\t}\n\n\ttry {\n\t\tconst url = new URL(value);\n\t\treturn {\n\t\t\tcode: url.searchParams.get(\"code\") ?? undefined,\n\t\t\tstate: url.searchParams.get(\"state\") ?? undefined,\n\t\t};\n\t} catch {\n\t\t// not a URL\n\t}\n\n\tif (value.includes(\"#\")) {\n\t\tconst [code, state] = value.split(\"#\", 2);\n\t\treturn { code, state };\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\tconst params = new URLSearchParams(value);\n\t\treturn {\n\t\t\tcode: params.get(\"code\") ?? undefined,\n\t\t\tstate: params.get(\"state\") ?? undefined,\n\t\t};\n\t}\n\n\treturn { code: value };\n}\n\nfunction formatErrorDetails(error: unknown): string {\n\tif (error instanceof Error) {\n\t\tconst details: string[] = [`${error.name}: ${error.message}`];\n\t\tconst errorWithCode = error as Error & { code?: string; errno?: number | string; cause?: unknown };\n\t\tif (errorWithCode.code) details.push(`code=${errorWithCode.code}`);\n\t\tif (typeof errorWithCode.errno !== \"undefined\") details.push(`errno=${String(errorWithCode.errno)}`);\n\t\tif (typeof error.cause !== \"undefined\") {\n\t\t\tdetails.push(`cause=${formatErrorDetails(error.cause)}`);\n\t\t}\n\t\tif (error.stack) {\n\t\t\tdetails.push(`stack=${error.stack}`);\n\t\t}\n\t\treturn details.join(\"; \");\n\t}\n\treturn String(error);\n}\n\nasync function startCallbackServer(expectedState: string): Promise<CallbackServerInfo> {\n\tconst { createServer } = await getNodeApis();\n\n\treturn new Promise((resolve, reject) => {\n\t\tlet settleWait: ((value: { code: string; state: string } | null) => void) | undefined;\n\t\tconst waitForCodePromise = new Promise<{ code: string; state: string } | null>((resolveWait) => {\n\t\t\tlet settled = false;\n\t\t\tsettleWait = (value) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolveWait(value);\n\t\t\t};\n\t\t});\n\n\t\tconst server = createServer((req, res) => {\n\t\t\ttry {\n\t\t\t\tconst url = new URL(req.url || \"\", \"http://localhost\");\n\t\t\t\tif (url.pathname !== CALLBACK_PATH && url.pathname !== \"/callback\") {\n\t\t\t\t\tres.writeHead(404, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\t\tres.end(oauthErrorHtml(\"Callback route not found.\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst code = url.searchParams.get(\"code\");\n\t\t\t\tconst state = url.searchParams.get(\"state\");\n\t\t\t\tconst error = url.searchParams.get(\"error\");\n\n\t\t\t\tif (error) {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\t\tres.end(oauthErrorHtml(\"Google Gemini authentication did not complete.\", `Error: ${error}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!code || !state) {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\t\tres.end(oauthErrorHtml(\"Missing code or state parameter.\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (state !== expectedState) {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\t\tres.end(oauthErrorHtml(\"State mismatch.\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tres.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n\t\t\t\tres.end(oauthSuccessHtml(\"Google Gemini authentication completed. You can close this window.\"));\n\t\t\t\tsettleWait?.({ code, state });\n\t\t\t} catch {\n\t\t\t\tres.writeHead(500, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n\t\t\t\tres.end(\"Internal error\");\n\t\t\t}\n\t\t});\n\n\t\tserver.on(\"error\", (err) => {\n\t\t\treject(err);\n\t\t});\n\n\t\tserver.listen(CALLBACK_PORT, CALLBACK_HOST, () => {\n\t\t\tresolve({\n\t\t\t\tserver,\n\t\t\t\tredirectUri: REDIRECT_URI,\n\t\t\t\tcancelWait: () => {\n\t\t\t\t\tsettleWait?.(null);\n\t\t\t\t},\n\t\t\t\twaitForCode: () => waitForCodePromise,\n\t\t\t});\n\t\t});\n\t});\n}\n\nasync function postForm(url: string, params: Record<string, string>): Promise<string> {\n\tconst body = new URLSearchParams(params);\n\tconst response = await fetch(url, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: body.toString(),\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\n\tconst responseBody = await response.text();\n\n\tif (!response.ok) {\n\t\tthrow new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);\n\t}\n\n\treturn responseBody;\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n): Promise<OAuthCredentials> {\n\tconst clientId = getClientId();\n\tconst clientSecret = getClientSecret();\n\tconst payload: Record<string, string> = {\n\t\tgrant_type: \"authorization_code\",\n\t\tclient_id: clientId,\n\t\tcode,\n\t\tredirect_uri: redirectUri,\n\t\tcode_verifier: verifier,\n\t};\n\tif (clientSecret) {\n\t\tpayload.client_secret = clientSecret;\n\t}\n\n\tlet responseBody: string;\n\ttry {\n\t\tresponseBody = await postForm(TOKEN_URL, payload);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\tlet tokenData: { access_token: string; refresh_token?: string; expires_in?: number };\n\ttry {\n\t\ttokenData = JSON.parse(responseBody) as { access_token: string; refresh_token?: string; expires_in?: number };\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\treturn {\n\t\taccess: tokenData.access_token,\n\t\trefresh: tokenData.refresh_token || \"\",\n\t\texpires: Date.now() + (tokenData.expires_in ?? 3600) * 1000 - 5 * 60 * 1000,\n\t};\n}\n\n/**\n * Login with Google Gemini OAuth (authorization code + PKCE)\n */\nexport async function loginGemini(options: {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonPrompt: (prompt: OAuthPrompt) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tonManualCodeInput?: () => Promise<string>;\n}): Promise<OAuthCredentials> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst server = await startCallbackServer(verifier);\n\n\tlet code: string | undefined;\n\tlet state: string | undefined;\n\tlet manualApiKey: string | undefined;\n\tlet redirectUriForExchange = REDIRECT_URI;\n\n\ttry {\n\t\tconst clientId = getClientId();\n\t\tconst authParams = new URLSearchParams({\n\t\t\tclient_id: clientId,\n\t\t\tresponse_type: \"code\",\n\t\t\tredirect_uri: REDIRECT_URI,\n\t\t\tscope: SCOPES,\n\t\t\tcode_challenge: challenge,\n\t\t\tcode_challenge_method: \"S256\",\n\t\t\tstate: verifier,\n\t\t\taccess_type: \"offline\",\n\t\t\tprompt: \"consent\",\n\t\t});\n\n\t\toptions.onAuth({\n\t\t\turl: `${AUTHORIZE_URL}?${authParams.toString()}`,\n\t\t\tinstructions:\n\t\t\t\t\"Complete login in your browser. If the browser is on another machine, paste the final redirect URL or an API key here.\",\n\t\t});\n\n\t\tif (options.onManualCodeInput) {\n\t\t\tlet manualInput: string | undefined;\n\t\t\tlet manualError: Error | undefined;\n\t\t\tconst manualPromise = options\n\t\t\t\t.onManualCodeInput()\n\t\t\t\t.then((input) => {\n\t\t\t\t\tmanualInput = input;\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tmanualError = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t});\n\n\t\t\tconst result = await server.waitForCode();\n\n\t\t\tif (manualError) {\n\t\t\t\tthrow manualError;\n\t\t\t}\n\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t\tstate = result.state;\n\t\t\t\tredirectUriForExchange = REDIRECT_URI;\n\t\t\t} else if (manualInput) {\n\t\t\t\tconst parsed = parseAuthorizationInput(manualInput);\n\t\t\t\tif (parsed.apiKey) {\n\t\t\t\t\tmanualApiKey = parsed.apiKey;\n\t\t\t\t} else {\n\t\t\t\t\tif (parsed.state && parsed.state !== verifier) {\n\t\t\t\t\t\tthrow new Error(\"OAuth state mismatch\");\n\t\t\t\t\t}\n\t\t\t\t\tcode = parsed.code;\n\t\t\t\t\tstate = parsed.state ?? verifier;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!code && !manualApiKey) {\n\t\t\t\tawait manualPromise;\n\t\t\t\tif (manualError) {\n\t\t\t\t\tthrow manualError;\n\t\t\t\t}\n\t\t\t\tif (manualInput) {\n\t\t\t\t\tconst parsed = parseAuthorizationInput(manualInput);\n\t\t\t\t\tif (parsed.apiKey) {\n\t\t\t\t\t\tmanualApiKey = parsed.apiKey;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (parsed.state && parsed.state !== verifier) {\n\t\t\t\t\t\t\tthrow new Error(\"OAuth state mismatch\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcode = parsed.code;\n\t\t\t\t\t\tstate = parsed.state ?? verifier;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst result = await server.waitForCode();\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t\tstate = result.state;\n\t\t\t\tredirectUriForExchange = REDIRECT_URI;\n\t\t\t}\n\t\t}\n\n\t\tif (!code && !manualApiKey) {\n\t\t\tconst input = await options.onPrompt({\n\t\t\t\tmessage: \"Paste the authorization code, redirect URL, or Gemini API key:\",\n\t\t\t\tplaceholder: REDIRECT_URI,\n\t\t\t});\n\t\t\tconst parsed = parseAuthorizationInput(input);\n\t\t\tif (parsed.apiKey) {\n\t\t\t\tmanualApiKey = parsed.apiKey;\n\t\t\t} else {\n\t\t\t\tif (parsed.state && parsed.state !== verifier) {\n\t\t\t\t\tthrow new Error(\"OAuth state mismatch\");\n\t\t\t\t}\n\t\t\t\tcode = parsed.code;\n\t\t\t\tstate = parsed.state ?? verifier;\n\t\t\t}\n\t\t}\n\n\t\tif (manualApiKey) {\n\t\t\treturn {\n\t\t\t\taccess: manualApiKey,\n\t\t\t\trefresh: \"\",\n\t\t\t\texpires: Date.now() + 365 * 24 * 60 * 60 * 1000,\n\t\t\t};\n\t\t}\n\n\t\tif (!code) {\n\t\t\tthrow new Error(\"Missing authorization code\");\n\t\t}\n\n\t\tif (!state) {\n\t\t\tthrow new Error(\"Missing OAuth state\");\n\t\t}\n\n\t\toptions.onProgress?.(\"Exchanging authorization code for tokens...\");\n\t\treturn exchangeAuthorizationCode(code, verifier, redirectUriForExchange);\n\t} finally {\n\t\tserver.server.close();\n\t}\n}\n\n/**\n * Refresh Google Gemini OAuth token\n */\nexport async function refreshGeminiToken(refreshToken: string): Promise<OAuthCredentials> {\n\tif (!refreshToken) {\n\t\tthrow new Error(\"No refresh token available for Google Gemini\");\n\t}\n\n\tconst clientId = getClientId();\n\tconst clientSecret = getClientSecret();\n\tconst payload: Record<string, string> = {\n\t\tgrant_type: \"refresh_token\",\n\t\tclient_id: clientId,\n\t\trefresh_token: refreshToken,\n\t};\n\tif (clientSecret) {\n\t\tpayload.client_secret = clientSecret;\n\t}\n\n\tlet responseBody: string;\n\ttry {\n\t\tresponseBody = await postForm(TOKEN_URL, payload);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Google Gemini token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\tlet data: { access_token: string; refresh_token?: string; expires_in?: number };\n\ttry {\n\t\tdata = JSON.parse(responseBody) as { access_token: string; refresh_token?: string; expires_in?: number };\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Google Gemini token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\treturn {\n\t\taccess: data.access_token,\n\t\trefresh: data.refresh_token || refreshToken,\n\t\texpires: Date.now() + (data.expires_in ?? 3600) * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nexport const geminiOAuthProvider: OAuthProviderInterface = {\n\tid: \"google\",\n\tname: \"Google Gemini\",\n\tusesCallbackServer: true,\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginGemini({\n\t\t\tonAuth: callbacks.onAuth,\n\t\t\tonPrompt: callbacks.onPrompt,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tonManualCodeInput: callbacks.onManualCodeInput,\n\t\t});\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\tif (!credentials.refresh) {\n\t\t\treturn credentials;\n\t\t}\n\t\treturn refreshGeminiToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n\nexport const googleOAuthProvider = geminiOAuthProvider;\n"]}
@@ -7,6 +7,7 @@
7
7
  * - GitHub Copilot
8
8
  */
9
9
  export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
10
+ export { geminiOAuthProvider, googleOAuthProvider, loginGemini, refreshGeminiToken, } from "./gemini.js";
10
11
  export { getGitHubCopilotBaseUrl, githubCopilotOAuthProvider, loginGitHubCopilot, normalizeDomain, refreshGitHubCopilotToken, } from "./github-copilot.js";
11
12
  export { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from "./openai-codex.js";
12
13
  export * from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EACN,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,eAAe,EACf,yBAAyB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAExG,cAAc,YAAY,CAAC;AAK3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAY/G,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,eAAe,GAAG,sBAAsB,GAAG,SAAS,CAExF;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAOxD;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED,wBAAgB,iBAAiB,IAAI,sBAAsB,EAAE,CAE5D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,iBAAiB,EAAE,CAM9D;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,gBAAgB,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC3C,OAAO,CAAC;IAAE,cAAc,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAqBtE","sourcesContent":["/**\n * OAuth credential management for AI providers.\n *\n * This module handles login, token refresh, and credential storage\n * for OAuth-based providers:\n * - Anthropic (Claude Pro/Max)\n * - GitHub Copilot\n */\n\nexport { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from \"./anthropic.js\";\nexport {\n\tgetGitHubCopilotBaseUrl,\n\tgithubCopilotOAuthProvider,\n\tloginGitHubCopilot,\n\tnormalizeDomain,\n\trefreshGitHubCopilotToken,\n} from \"./github-copilot.js\";\nexport { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from \"./openai-codex.js\";\n\nexport * from \"./types.js\";\n\nimport { anthropicOAuthProvider } from \"./anthropic.js\";\nimport { githubCopilotOAuthProvider } from \"./github-copilot.js\";\nimport { openaiCodexOAuthProvider } from \"./openai-codex.js\";\nimport type { OAuthCredentials, OAuthProviderId, OAuthProviderInfo, OAuthProviderInterface } from \"./types.js\";\n\nconst BUILT_IN_OAUTH_PROVIDERS: OAuthProviderInterface[] = [\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n];\n\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>(\n\tBUILT_IN_OAUTH_PROVIDERS.map((provider) => [provider.id, provider]),\n);\n\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n\treturn oauthProviderRegistry.get(id);\n}\n\nexport function registerOAuthProvider(provider: OAuthProviderInterface): void {\n\toauthProviderRegistry.set(provider.id, provider);\n}\n\n/**\n * Unregister an OAuth provider.\n *\n * If the provider is built-in, restores the built-in implementation.\n * Custom providers are removed completely.\n */\nexport function unregisterOAuthProvider(id: string): void {\n\tconst builtInProvider = BUILT_IN_OAUTH_PROVIDERS.find((provider) => provider.id === id);\n\tif (builtInProvider) {\n\t\toauthProviderRegistry.set(id, builtInProvider);\n\t\treturn;\n\t}\n\toauthProviderRegistry.delete(id);\n}\n\nexport function resetOAuthProviders(): void {\n\toauthProviderRegistry.clear();\n\tfor (const provider of BUILT_IN_OAUTH_PROVIDERS) {\n\t\toauthProviderRegistry.set(provider.id, provider);\n\t}\n}\n\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n\treturn Array.from(oauthProviderRegistry.values());\n}\n\n/**\n * @deprecated Use getOAuthProviders() which returns OAuthProviderInterface[]\n */\nexport function getOAuthProviderInfoList(): OAuthProviderInfo[] {\n\treturn getOAuthProviders().map((p) => ({\n\t\tid: p.id,\n\t\tname: p.name,\n\t\tavailable: true,\n\t}));\n}\n\n/**\n * Refresh token for any OAuth provider.\n * @deprecated Use getOAuthProvider(id).refreshToken() instead\n */\nexport async function refreshOAuthToken(\n\tproviderId: OAuthProviderId,\n\tcredentials: OAuthCredentials,\n): Promise<OAuthCredentials> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\treturn provider.refreshToken(credentials);\n}\n\n/**\n * Get API key for a provider from OAuth credentials.\n * Automatically refreshes expired tokens.\n *\n * @returns API key string and updated credentials, or null if no credentials\n * @throws Error if refresh fails\n */\nexport async function getOAuthApiKey(\n\tproviderId: OAuthProviderId,\n\tcredentials: Record<string, OAuthCredentials>,\n): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\n\tlet creds = credentials[providerId];\n\tif (!creds) {\n\t\treturn null;\n\t}\n\n\tif (Date.now() >= creds.expires) {\n\t\ttry {\n\t\t\tcreds = await provider.refreshToken(creds);\n\t\t} catch (_error) {\n\t\t\tthrow new Error(`Failed to refresh OAuth token for ${providerId}`);\n\t\t}\n\t}\n\n\tconst apiKey = provider.getApiKey(creds);\n\treturn { newCredentials: creds, apiKey };\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,kBAAkB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,eAAe,EACf,yBAAyB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAExG,cAAc,YAAY,CAAC;AAM3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAa/G,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,eAAe,GAAG,sBAAsB,GAAG,SAAS,CAExF;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAOxD;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED,wBAAgB,iBAAiB,IAAI,sBAAsB,EAAE,CAE5D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,iBAAiB,EAAE,CAM9D;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,gBAAgB,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC3C,OAAO,CAAC;IAAE,cAAc,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAqBtE","sourcesContent":["/**\n * OAuth credential management for AI providers.\n *\n * This module handles login, token refresh, and credential storage\n * for OAuth-based providers:\n * - Anthropic (Claude Pro/Max)\n * - GitHub Copilot\n */\n\nexport { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from \"./anthropic.js\";\nexport {\n\tgeminiOAuthProvider,\n\tgoogleOAuthProvider,\n\tloginGemini,\n\trefreshGeminiToken,\n} from \"./gemini.js\";\nexport {\n\tgetGitHubCopilotBaseUrl,\n\tgithubCopilotOAuthProvider,\n\tloginGitHubCopilot,\n\tnormalizeDomain,\n\trefreshGitHubCopilotToken,\n} from \"./github-copilot.js\";\nexport { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from \"./openai-codex.js\";\n\nexport * from \"./types.js\";\n\nimport { anthropicOAuthProvider } from \"./anthropic.js\";\nimport { geminiOAuthProvider } from \"./gemini.js\";\nimport { githubCopilotOAuthProvider } from \"./github-copilot.js\";\nimport { openaiCodexOAuthProvider } from \"./openai-codex.js\";\nimport type { OAuthCredentials, OAuthProviderId, OAuthProviderInfo, OAuthProviderInterface } from \"./types.js\";\n\nconst BUILT_IN_OAUTH_PROVIDERS: OAuthProviderInterface[] = [\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\tgeminiOAuthProvider,\n\topenaiCodexOAuthProvider,\n];\n\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>(\n\tBUILT_IN_OAUTH_PROVIDERS.map((provider) => [provider.id, provider]),\n);\n\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n\treturn oauthProviderRegistry.get(id) ?? (id === \"gemini\" ? oauthProviderRegistry.get(\"google\") : undefined);\n}\n\nexport function registerOAuthProvider(provider: OAuthProviderInterface): void {\n\toauthProviderRegistry.set(provider.id, provider);\n}\n\n/**\n * Unregister an OAuth provider.\n *\n * If the provider is built-in, restores the built-in implementation.\n * Custom providers are removed completely.\n */\nexport function unregisterOAuthProvider(id: string): void {\n\tconst builtInProvider = BUILT_IN_OAUTH_PROVIDERS.find((provider) => provider.id === id);\n\tif (builtInProvider) {\n\t\toauthProviderRegistry.set(id, builtInProvider);\n\t\treturn;\n\t}\n\toauthProviderRegistry.delete(id);\n}\n\nexport function resetOAuthProviders(): void {\n\toauthProviderRegistry.clear();\n\tfor (const provider of BUILT_IN_OAUTH_PROVIDERS) {\n\t\toauthProviderRegistry.set(provider.id, provider);\n\t}\n}\n\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n\treturn Array.from(oauthProviderRegistry.values());\n}\n\n/**\n * @deprecated Use getOAuthProviders() which returns OAuthProviderInterface[]\n */\nexport function getOAuthProviderInfoList(): OAuthProviderInfo[] {\n\treturn getOAuthProviders().map((p) => ({\n\t\tid: p.id,\n\t\tname: p.name,\n\t\tavailable: true,\n\t}));\n}\n\n/**\n * Refresh token for any OAuth provider.\n * @deprecated Use getOAuthProvider(id).refreshToken() instead\n */\nexport async function refreshOAuthToken(\n\tproviderId: OAuthProviderId,\n\tcredentials: OAuthCredentials,\n): Promise<OAuthCredentials> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\treturn provider.refreshToken(credentials);\n}\n\n/**\n * Get API key for a provider from OAuth credentials.\n * Automatically refreshes expired tokens.\n *\n * @returns API key string and updated credentials, or null if no credentials\n * @throws Error if refresh fails\n */\nexport async function getOAuthApiKey(\n\tproviderId: OAuthProviderId,\n\tcredentials: Record<string, OAuthCredentials>,\n): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\n\tlet creds = credentials[providerId];\n\tif (!creds) {\n\t\treturn null;\n\t}\n\n\tif (Date.now() >= creds.expires) {\n\t\ttry {\n\t\t\tcreds = await provider.refreshToken(creds);\n\t\t} catch (_error) {\n\t\t\tthrow new Error(`Failed to refresh OAuth token for ${providerId}`);\n\t\t}\n\t}\n\n\tconst apiKey = provider.getApiKey(creds);\n\treturn { newCredentials: creds, apiKey };\n}\n"]}
@@ -7,20 +7,23 @@
7
7
  * - GitHub Copilot
8
8
  */
9
9
  export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
10
+ export { geminiOAuthProvider, googleOAuthProvider, loginGemini, refreshGeminiToken, } from "./gemini.js";
10
11
  export { getGitHubCopilotBaseUrl, githubCopilotOAuthProvider, loginGitHubCopilot, normalizeDomain, refreshGitHubCopilotToken, } from "./github-copilot.js";
11
12
  export { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from "./openai-codex.js";
12
13
  export * from "./types.js";
13
14
  import { anthropicOAuthProvider } from "./anthropic.js";
15
+ import { geminiOAuthProvider } from "./gemini.js";
14
16
  import { githubCopilotOAuthProvider } from "./github-copilot.js";
15
17
  import { openaiCodexOAuthProvider } from "./openai-codex.js";
16
18
  const BUILT_IN_OAUTH_PROVIDERS = [
17
19
  anthropicOAuthProvider,
18
20
  githubCopilotOAuthProvider,
21
+ geminiOAuthProvider,
19
22
  openaiCodexOAuthProvider,
20
23
  ];
21
24
  const oauthProviderRegistry = new Map(BUILT_IN_OAUTH_PROVIDERS.map((provider) => [provider.id, provider]));
22
25
  export function getOAuthProvider(id) {
23
- return oauthProviderRegistry.get(id);
26
+ return oauthProviderRegistry.get(id) ?? (id === "gemini" ? oauthProviderRegistry.get("google") : undefined);
24
27
  }
25
28
  export function registerOAuthProvider(provider) {
26
29
  oauthProviderRegistry.set(provider.id, provider);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OAuth credential management for AI providers.\n *\n * This module handles login, token refresh, and credential storage\n * for OAuth-based providers:\n * - Anthropic (Claude Pro/Max)\n * - GitHub Copilot\n */\n\nexport { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from \"./anthropic.js\";\nexport {\n\tgetGitHubCopilotBaseUrl,\n\tgithubCopilotOAuthProvider,\n\tloginGitHubCopilot,\n\tnormalizeDomain,\n\trefreshGitHubCopilotToken,\n} from \"./github-copilot.js\";\nexport { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from \"./openai-codex.js\";\n\nexport * from \"./types.js\";\n\nimport { anthropicOAuthProvider } from \"./anthropic.js\";\nimport { githubCopilotOAuthProvider } from \"./github-copilot.js\";\nimport { openaiCodexOAuthProvider } from \"./openai-codex.js\";\nimport type { OAuthCredentials, OAuthProviderId, OAuthProviderInfo, OAuthProviderInterface } from \"./types.js\";\n\nconst BUILT_IN_OAUTH_PROVIDERS: OAuthProviderInterface[] = [\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n];\n\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>(\n\tBUILT_IN_OAUTH_PROVIDERS.map((provider) => [provider.id, provider]),\n);\n\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n\treturn oauthProviderRegistry.get(id);\n}\n\nexport function registerOAuthProvider(provider: OAuthProviderInterface): void {\n\toauthProviderRegistry.set(provider.id, provider);\n}\n\n/**\n * Unregister an OAuth provider.\n *\n * If the provider is built-in, restores the built-in implementation.\n * Custom providers are removed completely.\n */\nexport function unregisterOAuthProvider(id: string): void {\n\tconst builtInProvider = BUILT_IN_OAUTH_PROVIDERS.find((provider) => provider.id === id);\n\tif (builtInProvider) {\n\t\toauthProviderRegistry.set(id, builtInProvider);\n\t\treturn;\n\t}\n\toauthProviderRegistry.delete(id);\n}\n\nexport function resetOAuthProviders(): void {\n\toauthProviderRegistry.clear();\n\tfor (const provider of BUILT_IN_OAUTH_PROVIDERS) {\n\t\toauthProviderRegistry.set(provider.id, provider);\n\t}\n}\n\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n\treturn Array.from(oauthProviderRegistry.values());\n}\n\n/**\n * @deprecated Use getOAuthProviders() which returns OAuthProviderInterface[]\n */\nexport function getOAuthProviderInfoList(): OAuthProviderInfo[] {\n\treturn getOAuthProviders().map((p) => ({\n\t\tid: p.id,\n\t\tname: p.name,\n\t\tavailable: true,\n\t}));\n}\n\n/**\n * Refresh token for any OAuth provider.\n * @deprecated Use getOAuthProvider(id).refreshToken() instead\n */\nexport async function refreshOAuthToken(\n\tproviderId: OAuthProviderId,\n\tcredentials: OAuthCredentials,\n): Promise<OAuthCredentials> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\treturn provider.refreshToken(credentials);\n}\n\n/**\n * Get API key for a provider from OAuth credentials.\n * Automatically refreshes expired tokens.\n *\n * @returns API key string and updated credentials, or null if no credentials\n * @throws Error if refresh fails\n */\nexport async function getOAuthApiKey(\n\tproviderId: OAuthProviderId,\n\tcredentials: Record<string, OAuthCredentials>,\n): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\n\tlet creds = credentials[providerId];\n\tif (!creds) {\n\t\treturn null;\n\t}\n\n\tif (Date.now() >= creds.expires) {\n\t\ttry {\n\t\t\tcreds = await provider.refreshToken(creds);\n\t\t} catch (_error) {\n\t\t\tthrow new Error(`Failed to refresh OAuth token for ${providerId}`);\n\t\t}\n\t}\n\n\tconst apiKey = provider.getApiKey(creds);\n\treturn { newCredentials: creds, apiKey };\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OAuth credential management for AI providers.\n *\n * This module handles login, token refresh, and credential storage\n * for OAuth-based providers:\n * - Anthropic (Claude Pro/Max)\n * - GitHub Copilot\n */\n\nexport { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from \"./anthropic.js\";\nexport {\n\tgeminiOAuthProvider,\n\tgoogleOAuthProvider,\n\tloginGemini,\n\trefreshGeminiToken,\n} from \"./gemini.js\";\nexport {\n\tgetGitHubCopilotBaseUrl,\n\tgithubCopilotOAuthProvider,\n\tloginGitHubCopilot,\n\tnormalizeDomain,\n\trefreshGitHubCopilotToken,\n} from \"./github-copilot.js\";\nexport { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from \"./openai-codex.js\";\n\nexport * from \"./types.js\";\n\nimport { anthropicOAuthProvider } from \"./anthropic.js\";\nimport { geminiOAuthProvider } from \"./gemini.js\";\nimport { githubCopilotOAuthProvider } from \"./github-copilot.js\";\nimport { openaiCodexOAuthProvider } from \"./openai-codex.js\";\nimport type { OAuthCredentials, OAuthProviderId, OAuthProviderInfo, OAuthProviderInterface } from \"./types.js\";\n\nconst BUILT_IN_OAUTH_PROVIDERS: OAuthProviderInterface[] = [\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\tgeminiOAuthProvider,\n\topenaiCodexOAuthProvider,\n];\n\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>(\n\tBUILT_IN_OAUTH_PROVIDERS.map((provider) => [provider.id, provider]),\n);\n\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n\treturn oauthProviderRegistry.get(id) ?? (id === \"gemini\" ? oauthProviderRegistry.get(\"google\") : undefined);\n}\n\nexport function registerOAuthProvider(provider: OAuthProviderInterface): void {\n\toauthProviderRegistry.set(provider.id, provider);\n}\n\n/**\n * Unregister an OAuth provider.\n *\n * If the provider is built-in, restores the built-in implementation.\n * Custom providers are removed completely.\n */\nexport function unregisterOAuthProvider(id: string): void {\n\tconst builtInProvider = BUILT_IN_OAUTH_PROVIDERS.find((provider) => provider.id === id);\n\tif (builtInProvider) {\n\t\toauthProviderRegistry.set(id, builtInProvider);\n\t\treturn;\n\t}\n\toauthProviderRegistry.delete(id);\n}\n\nexport function resetOAuthProviders(): void {\n\toauthProviderRegistry.clear();\n\tfor (const provider of BUILT_IN_OAUTH_PROVIDERS) {\n\t\toauthProviderRegistry.set(provider.id, provider);\n\t}\n}\n\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n\treturn Array.from(oauthProviderRegistry.values());\n}\n\n/**\n * @deprecated Use getOAuthProviders() which returns OAuthProviderInterface[]\n */\nexport function getOAuthProviderInfoList(): OAuthProviderInfo[] {\n\treturn getOAuthProviders().map((p) => ({\n\t\tid: p.id,\n\t\tname: p.name,\n\t\tavailable: true,\n\t}));\n}\n\n/**\n * Refresh token for any OAuth provider.\n * @deprecated Use getOAuthProvider(id).refreshToken() instead\n */\nexport async function refreshOAuthToken(\n\tproviderId: OAuthProviderId,\n\tcredentials: OAuthCredentials,\n): Promise<OAuthCredentials> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\treturn provider.refreshToken(credentials);\n}\n\n/**\n * Get API key for a provider from OAuth credentials.\n * Automatically refreshes expired tokens.\n *\n * @returns API key string and updated credentials, or null if no credentials\n * @throws Error if refresh fails\n */\nexport async function getOAuthApiKey(\n\tproviderId: OAuthProviderId,\n\tcredentials: Record<string, OAuthCredentials>,\n): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\n\tlet creds = credentials[providerId];\n\tif (!creds) {\n\t\treturn null;\n\t}\n\n\tif (Date.now() >= creds.expires) {\n\t\ttry {\n\t\t\tcreds = await provider.refreshToken(creds);\n\t\t} catch (_error) {\n\t\t\tthrow new Error(`Failed to refresh OAuth token for ${providerId}`);\n\t\t}\n\t}\n\n\tconst apiKey = provider.getApiKey(creds);\n\treturn { newCredentials: creds, apiKey };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asm-agent/ai",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Unified LLM API with automatic model discovery and provider configuration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",