diff --git a/CHANGELOG.md b/CHANGELOG.md index 6acb2fd82fb..64933056e4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai - Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898. - Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs. - Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy. +- Auth/Vertex AI: recognize `GOOGLE_CLOUD_API_KEY` for the `google-vertex` provider so `models auth` status, secret scrubbing, and env-var detection work without relying solely on gcloud ADC. (#45876) ### Fixes diff --git a/docs/concepts/model-providers.md b/docs/concepts/model-providers.md index 8793e3fe1d6..db79aebba0b 100644 --- a/docs/concepts/model-providers.md +++ b/docs/concepts/model-providers.md @@ -157,7 +157,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** ### Google Vertex, Antigravity, and Gemini CLI - Providers: `google-vertex`, `google-antigravity`, `google-gemini-cli` -- Auth: Vertex uses gcloud ADC; Antigravity/Gemini CLI use their respective auth flows +- Auth: Vertex supports `GOOGLE_CLOUD_API_KEY` (simplest) or gcloud ADC (`GOOGLE_CLOUD_PROJECT` + `GOOGLE_CLOUD_LOCATION` + `gcloud auth application-default login` or `GOOGLE_APPLICATION_CREDENTIALS`); Antigravity/Gemini CLI use their respective auth flows - Caution: Antigravity and Gemini CLI OAuth in OpenClaw are unofficial integrations. Some users have reported Google account restrictions after using third-party clients. Review Google terms and use a non-critical account if you choose to proceed. - Antigravity OAuth is shipped as a bundled plugin (`google-antigravity-auth`, disabled by default). - Enable: `openclaw plugins enable google-antigravity-auth` diff --git a/src/agents/model-auth-env-vars.ts b/src/agents/model-auth-env-vars.ts index c9cb9159138..f2d6c84d489 100644 --- a/src/agents/model-auth-env-vars.ts +++ b/src/agents/model-auth-env-vars.ts @@ -15,6 +15,7 @@ export const PROVIDER_ENV_API_KEY_CANDIDATES: Record = { huggingface: ["HUGGINGFACE_HUB_TOKEN", "HF_TOKEN"], openai: ["OPENAI_API_KEY"], google: ["GEMINI_API_KEY"], + "google-vertex": ["GOOGLE_CLOUD_API_KEY"], voyage: ["VOYAGE_API_KEY"], groq: ["GROQ_API_KEY"], deepgram: ["DEEPGRAM_API_KEY"], diff --git a/src/agents/model-auth.profiles.test.ts b/src/agents/model-auth.profiles.test.ts index a1fc511aaf8..0b07309342d 100644 --- a/src/agents/model-auth.profiles.test.ts +++ b/src/agents/model-auth.profiles.test.ts @@ -426,4 +426,34 @@ describe("getApiKeyForModel", () => { }, ); }); + + it("resolveEnvApiKey('google-vertex') returns GOOGLE_CLOUD_API_KEY when set", async () => { + await withEnvAsync( + { + GOOGLE_CLOUD_API_KEY: "AIzaSyTest1234567890", + }, + async () => { + const resolved = resolveEnvApiKey("google-vertex"); + expect(resolved?.apiKey).toBe("AIzaSyTest1234567890"); + expect(resolved?.source).toContain("GOOGLE_CLOUD_API_KEY"); + }, + ); + }); + + it("resolveEnvApiKey('google-vertex') does not return GOOGLE_CLOUD_API_KEY when the env var is unset", async () => { + await withEnvAsync( + { + GOOGLE_CLOUD_API_KEY: undefined, + }, + async () => { + const resolved = resolveEnvApiKey("google-vertex"); + // Without GOOGLE_CLOUD_API_KEY, the candidates check finds nothing. + // The ADC fallback may or may not return depending on local + // credential files, but the source must never reference the API key. + if (resolved) { + expect(resolved.source).not.toContain("GOOGLE_CLOUD_API_KEY"); + } + }, + ); + }); }); diff --git a/src/secrets/provider-env-vars.test.ts b/src/secrets/provider-env-vars.test.ts index 6e5b78f6643..af68202268b 100644 --- a/src/secrets/provider-env-vars.test.ts +++ b/src/secrets/provider-env-vars.test.ts @@ -17,6 +17,11 @@ describe("provider env vars", () => { expect(listKnownSecretEnvVarNames()).not.toContain("OPENCLAW_API_KEY"); }); + it("includes GOOGLE_CLOUD_API_KEY in both secret and auth env var lists", () => { + expect(listKnownSecretEnvVarNames()).toContain("GOOGLE_CLOUD_API_KEY"); + expect(listKnownProviderAuthEnvVarNames()).toContain("GOOGLE_CLOUD_API_KEY"); + }); + it("omits env keys case-insensitively", () => { const env = omitEnvKeysCaseInsensitive( { diff --git a/src/secrets/provider-env-vars.ts b/src/secrets/provider-env-vars.ts index 88900893376..e1a11862832 100644 --- a/src/secrets/provider-env-vars.ts +++ b/src/secrets/provider-env-vars.ts @@ -2,6 +2,7 @@ export const PROVIDER_ENV_VARS: Record = { openai: ["OPENAI_API_KEY"], anthropic: ["ANTHROPIC_API_KEY"], google: ["GEMINI_API_KEY"], + "google-vertex": ["GOOGLE_CLOUD_API_KEY"], minimax: ["MINIMAX_API_KEY"], "minimax-cn": ["MINIMAX_API_KEY"], moonshot: ["MOONSHOT_API_KEY"],