fix: accept Google API keys for Gemini CLI

This commit is contained in:
Shakker
2026-06-15 19:23:42 +01:00
committed by Shakker
parent defaffbb93
commit eb92a0bf76
3 changed files with 81 additions and 6 deletions

View File

@@ -14,6 +14,7 @@ const GEMINI_MODEL_ALIASES: Record<string, string> = {
};
const GEMINI_CLI_DEFAULT_MODEL_REF = "google-gemini-cli/gemini-3-flash-preview";
const GEMINI_CLI_PROVIDER_ID = "google-gemini-cli";
const GOOGLE_PROVIDER_ID = "google";
const VERCEL_AI_GATEWAY_PROVIDER_ID = "vercel-ai-gateway";
const GEMINI_CLI_CREDENTIALS_FILENAME = "gemini-credentials.json";
const GEMINI_CLI_GCA_AUTH_ENV = [
@@ -68,7 +69,7 @@ type GeminiOAuthCredential = GeminiAuthProfileCredential & {
type GeminiApiKeyCredential = GeminiAuthProfileCredential & {
type: "api_key";
provider: typeof GEMINI_CLI_PROVIDER_ID;
provider: typeof GEMINI_CLI_PROVIDER_ID | typeof GOOGLE_PROVIDER_ID;
key: string;
};
@@ -154,7 +155,10 @@ function requireGeminiApiKeyCredential(
if (credential.type !== "api_key") {
return null;
}
if (credential.provider !== GEMINI_CLI_PROVIDER_ID) {
if (
credential.provider !== GEMINI_CLI_PROVIDER_ID &&
credential.provider !== GOOGLE_PROVIDER_ID
) {
throwUnsupportedGeminiCredential(credential);
}
@@ -166,7 +170,7 @@ function requireGeminiApiKeyCredential(
return {
...credential,
type: "api_key",
provider: GEMINI_CLI_PROVIDER_ID,
provider: credential.provider,
key,
};
}

View File

@@ -52,11 +52,11 @@ function buildGeminiApiKeyPrepareContext(workspaceDir: string): GeminiPrepareCon
agentDir,
provider: "google-gemini-cli",
modelId: "gemini-3.1-flash-lite",
authProfileId: "google-gemini-cli:api-key",
authProfileId: "google:api-key",
// Private bundled-runtime bridge, not public Plugin SDK surface.
authCredential: {
type: "api_key",
provider: "google-gemini-cli",
provider: "google",
key: "gemini-api-key",
email: "user@example.test",
},
@@ -141,7 +141,7 @@ describe("google gemini cli backend auth bridge", () => {
}
});
it("prepares selected Gemini API-key credentials and removes stale OAuth state for that profile home", async () => {
it("prepares selected canonical Google API-key credentials and removes stale OAuth state for that profile home", async () => {
const backend = buildGoogleGeminiCliBackend();
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-test-workspace-"));
let home: string | undefined;

View File

@@ -806,6 +806,77 @@ describe("CLI attempt execution", () => {
expect(firstRunCliAgentArg().authProfileId).toBe("google-gemini-cli:user@example.test");
});
it("forwards pinned canonical Google API-key profiles to Google models routed through Gemini CLI", async () => {
const sessionKey = "agent:main:direct:gemini-cli-google-api-key";
const sessionEntry: SessionEntry = {
sessionId: "openclaw-session-gemini-api-key",
updatedAt: Date.now(),
authProfileOverride: "google:api-key",
authProfileOverrideSource: "user",
};
const sessionStore: Record<string, SessionEntry> = { [sessionKey]: sessionEntry };
await fs.writeFile(storePath, JSON.stringify(sessionStore, null, 2), "utf-8");
saveAuthProfileStore(
{
version: 1,
profiles: {
"google:api-key": {
type: "api_key",
provider: "google",
key: "gemini-api-key",
},
},
},
tmpDir,
{ filterExternalAuthProfiles: false, syncExternalCli: false },
);
runCliAgentMock.mockResolvedValueOnce(makeCliResult("gemini cli api-key response"));
await runAgentAttempt({
providerOverride: "google",
originalProvider: "google",
modelOverride: "gemini-3.1-pro-preview",
cfg: {
agents: {
defaults: {
models: {
"google/gemini-3.1-pro-preview": {
agentRuntime: { id: "google-gemini-cli" },
},
},
},
},
} as OpenClawConfig,
sessionEntry,
sessionId: sessionEntry.sessionId,
sessionKey,
sessionAgentId: "main",
sessionFile: path.join(tmpDir, "session.jsonl"),
workspaceDir: tmpDir,
body: "continue",
isFallbackRetry: false,
resolvedThinkLevel: "medium",
timeoutMs: 1_000,
runId: "run-gemini-cli-google-api-key",
opts: {} as Parameters<typeof runAgentAttempt>[0]["opts"],
runContext: {} as Parameters<typeof runAgentAttempt>[0]["runContext"],
spawnedBy: undefined,
messageChannel: undefined,
skillsSnapshot: undefined,
resolvedVerboseLevel: undefined,
agentDir: tmpDir,
onAgentEvent: vi.fn(),
authProfileProvider: "google",
sessionStore,
storePath,
sessionHasHistory: false,
});
expect(runCliAgentMock).toHaveBeenCalledTimes(1);
expect(firstRunCliAgentArg().provider).toBe("google-gemini-cli");
expect(firstRunCliAgentArg().authProfileId).toBe("google:api-key");
});
it("persists CLI replies into the session transcript", async () => {
const sessionKey = "agent:main:subagent:cli-transcript";
const sessionFile = path.join(tmpDir, "session-cli-transcript.jsonl");