refactor(google): merge gemini auth into google plugin
This commit is contained in:
parent
d56559bad7
commit
bc5054ce68
@ -176,16 +176,13 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no**
|
||||
- Compatibility: legacy OpenClaw config using `google/gemini-3.1-flash-preview` is normalized to `google/gemini-3-flash-preview`
|
||||
- CLI: `openclaw onboard --auth-choice gemini-api-key`
|
||||
|
||||
### Google Vertex, Antigravity, and Gemini CLI
|
||||
### Google Vertex and Gemini CLI
|
||||
|
||||
- Providers: `google-vertex`, `google-antigravity`, `google-gemini-cli`
|
||||
- Auth: Vertex uses gcloud ADC; 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`
|
||||
- Login: `openclaw models auth login --provider google-antigravity --set-default`
|
||||
- Gemini CLI OAuth is shipped as a bundled plugin (`google-gemini-cli-auth`, disabled by default).
|
||||
- Enable: `openclaw plugins enable google-gemini-cli-auth`
|
||||
- Providers: `google-vertex`, `google-gemini-cli`
|
||||
- Auth: Vertex uses gcloud ADC; Gemini CLI uses its OAuth flow
|
||||
- Caution: Gemini CLI OAuth in OpenClaw is an unofficial integration. 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.
|
||||
- Gemini CLI OAuth is shipped as part of the bundled `google` plugin.
|
||||
- Enable: `openclaw plugins enable google`
|
||||
- Login: `openclaw models auth login --provider google-gemini-cli --set-default`
|
||||
- Note: you do **not** paste a client id or secret into `openclaw.json`. The CLI login flow stores
|
||||
tokens in auth profiles on the gateway host.
|
||||
|
||||
@ -783,7 +783,7 @@ Gemini CLI uses a **plugin auth flow**, not a client id or secret in `openclaw.j
|
||||
|
||||
Steps:
|
||||
|
||||
1. Enable the plugin: `openclaw plugins enable google-gemini-cli-auth`
|
||||
1. Enable the plugin: `openclaw plugins enable google`
|
||||
2. Login: `openclaw models auth login --provider google-gemini-cli --set-default`
|
||||
|
||||
This stores OAuth tokens in auth profiles on the gateway host. Details: [Model providers](/concepts/model-providers).
|
||||
|
||||
@ -167,8 +167,7 @@ Important trust note:
|
||||
- Anthropic provider runtime — bundled as `anthropic` (enabled by default)
|
||||
- BytePlus provider catalog — bundled as `byteplus` (enabled by default)
|
||||
- Cloudflare AI Gateway provider catalog — bundled as `cloudflare-ai-gateway` (enabled by default)
|
||||
- Google Antigravity OAuth (provider auth) — bundled as `google-antigravity-auth` (disabled by default)
|
||||
- Gemini CLI OAuth (provider auth) — bundled as `google-gemini-cli-auth` (disabled by default)
|
||||
- Google web search + Gemini CLI OAuth — bundled as `google` (web search auto-loads it; provider auth stays opt-in)
|
||||
- GitHub Copilot provider runtime — bundled as `github-copilot` (enabled by default)
|
||||
- Hugging Face provider catalog — bundled as `huggingface` (enabled by default)
|
||||
- Kilo Gateway provider runtime — bundled as `kilocode` (enabled by default)
|
||||
@ -521,8 +520,7 @@ authoring plugins:
|
||||
`openclaw/plugin-sdk/acpx`, `openclaw/plugin-sdk/bluebubbles`,
|
||||
`openclaw/plugin-sdk/copilot-proxy`, `openclaw/plugin-sdk/device-pair`,
|
||||
`openclaw/plugin-sdk/diagnostics-otel`, `openclaw/plugin-sdk/diffs`,
|
||||
`openclaw/plugin-sdk/feishu`,
|
||||
`openclaw/plugin-sdk/google-gemini-cli-auth`, `openclaw/plugin-sdk/googlechat`,
|
||||
`openclaw/plugin-sdk/feishu`, `openclaw/plugin-sdk/googlechat`,
|
||||
`openclaw/plugin-sdk/irc`, `openclaw/plugin-sdk/llm-task`,
|
||||
`openclaw/plugin-sdk/lobster`, `openclaw/plugin-sdk/matrix`,
|
||||
`openclaw/plugin-sdk/mattermost`, `openclaw/plugin-sdk/memory-core`,
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
# Google Gemini CLI Auth (OpenClaw plugin)
|
||||
|
||||
OAuth provider plugin for **Gemini CLI** (Google Code Assist).
|
||||
|
||||
## Account safety caution
|
||||
|
||||
- This plugin is an unofficial integration and is not endorsed by Google.
|
||||
- Some users have reported account restrictions or suspensions after using third-party Gemini CLI and Antigravity OAuth clients.
|
||||
- Use caution, review the applicable Google terms, and avoid using a mission-critical account.
|
||||
|
||||
## Enable
|
||||
|
||||
Bundled plugins are disabled by default. Enable this one:
|
||||
|
||||
```bash
|
||||
openclaw plugins enable google-gemini-cli-auth
|
||||
```
|
||||
|
||||
Restart the Gateway after enabling.
|
||||
|
||||
## Authenticate
|
||||
|
||||
```bash
|
||||
openclaw models auth login --provider google-gemini-cli --set-default
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
Requires the Gemini CLI to be installed (credentials are extracted automatically):
|
||||
|
||||
```bash
|
||||
brew install gemini-cli
|
||||
# or: npm install -g @google/gemini-cli
|
||||
```
|
||||
|
||||
## Env vars (optional)
|
||||
|
||||
Override auto-detected credentials with:
|
||||
|
||||
- `OPENCLAW_GEMINI_OAUTH_CLIENT_ID` / `GEMINI_CLI_OAUTH_CLIENT_ID`
|
||||
- `OPENCLAW_GEMINI_OAUTH_CLIENT_SECRET` / `GEMINI_CLI_OAUTH_CLIENT_SECRET`
|
||||
@ -1,158 +0,0 @@
|
||||
import {
|
||||
buildOauthProviderAuthResult,
|
||||
emptyPluginConfigSchema,
|
||||
type ProviderFetchUsageSnapshotContext,
|
||||
type OpenClawPluginApi,
|
||||
type ProviderAuthContext,
|
||||
type ProviderResolveDynamicModelContext,
|
||||
type ProviderRuntimeModel,
|
||||
} from "openclaw/plugin-sdk/google-gemini-cli-auth";
|
||||
import { normalizeModelCompat } from "../../src/agents/model-compat.js";
|
||||
import { fetchGeminiUsage } from "../../src/infra/provider-usage.fetch.js";
|
||||
import { loginGeminiCliOAuth } from "./oauth.js";
|
||||
|
||||
const PROVIDER_ID = "google-gemini-cli";
|
||||
const PROVIDER_LABEL = "Gemini CLI OAuth";
|
||||
const DEFAULT_MODEL = "google-gemini-cli/gemini-3.1-pro-preview";
|
||||
const GEMINI_3_1_PRO_PREFIX = "gemini-3.1-pro";
|
||||
const GEMINI_3_1_FLASH_PREFIX = "gemini-3.1-flash";
|
||||
const GEMINI_3_1_PRO_TEMPLATE_IDS = ["gemini-3-pro-preview"] as const;
|
||||
const GEMINI_3_1_FLASH_TEMPLATE_IDS = ["gemini-3-flash-preview"] as const;
|
||||
const ENV_VARS = [
|
||||
"OPENCLAW_GEMINI_OAUTH_CLIENT_ID",
|
||||
"OPENCLAW_GEMINI_OAUTH_CLIENT_SECRET",
|
||||
"GEMINI_CLI_OAUTH_CLIENT_ID",
|
||||
"GEMINI_CLI_OAUTH_CLIENT_SECRET",
|
||||
];
|
||||
|
||||
function cloneFirstTemplateModel(params: {
|
||||
modelId: string;
|
||||
templateIds: readonly string[];
|
||||
ctx: ProviderResolveDynamicModelContext;
|
||||
}): ProviderRuntimeModel | undefined {
|
||||
const trimmedModelId = params.modelId.trim();
|
||||
for (const templateId of [...new Set(params.templateIds)].filter(Boolean)) {
|
||||
const template = params.ctx.modelRegistry.find(
|
||||
PROVIDER_ID,
|
||||
templateId,
|
||||
) as ProviderRuntimeModel | null;
|
||||
if (!template) {
|
||||
continue;
|
||||
}
|
||||
return normalizeModelCompat({
|
||||
...template,
|
||||
id: trimmedModelId,
|
||||
name: trimmedModelId,
|
||||
reasoning: true,
|
||||
} as ProviderRuntimeModel);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseGoogleUsageToken(apiKey: string): string {
|
||||
try {
|
||||
const parsed = JSON.parse(apiKey) as { token?: unknown };
|
||||
if (typeof parsed?.token === "string") {
|
||||
return parsed.token;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
async function fetchGeminiCliUsage(ctx: ProviderFetchUsageSnapshotContext) {
|
||||
return await fetchGeminiUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn, PROVIDER_ID);
|
||||
}
|
||||
|
||||
function resolveGeminiCliForwardCompatModel(
|
||||
ctx: ProviderResolveDynamicModelContext,
|
||||
): ProviderRuntimeModel | undefined {
|
||||
const trimmed = ctx.modelId.trim();
|
||||
const lower = trimmed.toLowerCase();
|
||||
|
||||
let templateIds: readonly string[];
|
||||
if (lower.startsWith(GEMINI_3_1_PRO_PREFIX)) {
|
||||
templateIds = GEMINI_3_1_PRO_TEMPLATE_IDS;
|
||||
} else if (lower.startsWith(GEMINI_3_1_FLASH_PREFIX)) {
|
||||
templateIds = GEMINI_3_1_FLASH_TEMPLATE_IDS;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return cloneFirstTemplateModel({
|
||||
modelId: trimmed,
|
||||
templateIds,
|
||||
ctx,
|
||||
});
|
||||
}
|
||||
|
||||
const geminiCliPlugin = {
|
||||
id: "google-gemini-cli-auth",
|
||||
name: "Google Gemini CLI Auth",
|
||||
description: "OAuth flow for Gemini CLI (Google Code Assist)",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: PROVIDER_LABEL,
|
||||
docsPath: "/providers/models",
|
||||
aliases: ["gemini-cli"],
|
||||
envVars: ENV_VARS,
|
||||
auth: [
|
||||
{
|
||||
id: "oauth",
|
||||
label: "Google OAuth",
|
||||
hint: "PKCE + localhost callback",
|
||||
kind: "oauth",
|
||||
run: async (ctx: ProviderAuthContext) => {
|
||||
const spin = ctx.prompter.progress("Starting Gemini CLI OAuth…");
|
||||
try {
|
||||
const result = await loginGeminiCliOAuth({
|
||||
isRemote: ctx.isRemote,
|
||||
openUrl: ctx.openUrl,
|
||||
log: (msg) => ctx.runtime.log(msg),
|
||||
note: ctx.prompter.note,
|
||||
prompt: async (message) => String(await ctx.prompter.text({ message })),
|
||||
progress: spin,
|
||||
});
|
||||
|
||||
spin.stop("Gemini CLI OAuth complete");
|
||||
return buildOauthProviderAuthResult({
|
||||
providerId: PROVIDER_ID,
|
||||
defaultModel: DEFAULT_MODEL,
|
||||
access: result.access,
|
||||
refresh: result.refresh,
|
||||
expires: result.expires,
|
||||
email: result.email,
|
||||
credentialExtra: { projectId: result.projectId },
|
||||
notes: ["If requests fail, set GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID."],
|
||||
});
|
||||
} catch (err) {
|
||||
spin.stop("Gemini CLI OAuth failed");
|
||||
await ctx.prompter.note(
|
||||
"Trouble with OAuth? Ensure your Google account has Gemini CLI access.",
|
||||
"OAuth help",
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
resolveDynamicModel: (ctx) => resolveGeminiCliForwardCompatModel(ctx),
|
||||
resolveUsageAuth: async (ctx) => {
|
||||
const auth = await ctx.resolveOAuthToken();
|
||||
if (!auth) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...auth,
|
||||
token: parseGoogleUsageToken(auth.token),
|
||||
};
|
||||
},
|
||||
fetchUsageSnapshot: async (ctx) => await fetchGeminiCliUsage(ctx),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default geminiCliPlugin;
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"id": "google-gemini-cli-auth",
|
||||
"providers": ["google-gemini-cli"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "@openclaw/google-gemini-cli-auth",
|
||||
"version": "2026.3.14",
|
||||
"private": true,
|
||||
"description": "OpenClaw Gemini CLI OAuth provider plugin",
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -4,24 +4,38 @@ import {
|
||||
createProviderUsageFetch,
|
||||
makeResponse,
|
||||
} from "../../src/test-utils/provider-usage-fetch.js";
|
||||
import geminiCliPlugin from "./index.js";
|
||||
import googlePlugin from "./index.js";
|
||||
|
||||
function registerProvider(): ProviderPlugin {
|
||||
function registerGooglePlugin(): {
|
||||
provider: ProviderPlugin;
|
||||
webSearchProviderRegistered: boolean;
|
||||
} {
|
||||
let provider: ProviderPlugin | undefined;
|
||||
geminiCliPlugin.register({
|
||||
let webSearchProviderRegistered = false;
|
||||
googlePlugin.register({
|
||||
registerProvider(nextProvider: ProviderPlugin) {
|
||||
provider = nextProvider;
|
||||
},
|
||||
registerWebSearchProvider() {
|
||||
webSearchProviderRegistered = true;
|
||||
},
|
||||
} as never);
|
||||
if (!provider) {
|
||||
throw new Error("provider registration missing");
|
||||
}
|
||||
return provider;
|
||||
return { provider, webSearchProviderRegistered };
|
||||
}
|
||||
|
||||
describe("google-gemini-cli-auth plugin", () => {
|
||||
describe("google plugin", () => {
|
||||
it("registers both Gemini CLI auth and Gemini web search", () => {
|
||||
const result = registerGooglePlugin();
|
||||
|
||||
expect(result.provider.id).toBe("google-gemini-cli");
|
||||
expect(result.webSearchProviderRegistered).toBe(true);
|
||||
});
|
||||
|
||||
it("owns gemini 3.1 forward-compat resolution", () => {
|
||||
const provider = registerProvider();
|
||||
const { provider } = registerGooglePlugin();
|
||||
const model = provider.resolveDynamicModel?.({
|
||||
provider: "google-gemini-cli",
|
||||
modelId: "gemini-3.1-pro-preview",
|
||||
@ -52,7 +66,7 @@ describe("google-gemini-cli-auth plugin", () => {
|
||||
});
|
||||
|
||||
it("owns usage-token parsing", async () => {
|
||||
const provider = registerProvider();
|
||||
const { provider } = registerGooglePlugin();
|
||||
await expect(
|
||||
provider.resolveUsageAuth?.({
|
||||
config: {} as never,
|
||||
@ -71,7 +85,7 @@ describe("google-gemini-cli-auth plugin", () => {
|
||||
});
|
||||
|
||||
it("owns usage snapshot fetching", async () => {
|
||||
const provider = registerProvider();
|
||||
const { provider } = registerGooglePlugin();
|
||||
const mockFetch = createProviderUsageFetch(async (url) => {
|
||||
if (url.includes("cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota")) {
|
||||
return makeResponse(200, {
|
||||
149
extensions/google/gemini-cli-provider.ts
Normal file
149
extensions/google/gemini-cli-provider.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { normalizeModelCompat } from "../../src/agents/model-compat.js";
|
||||
import { fetchGeminiUsage } from "../../src/infra/provider-usage.fetch.js";
|
||||
import { buildOauthProviderAuthResult } from "../../src/plugin-sdk/provider-auth-result.js";
|
||||
import type {
|
||||
OpenClawPluginApi,
|
||||
ProviderAuthContext,
|
||||
ProviderFetchUsageSnapshotContext,
|
||||
ProviderResolveDynamicModelContext,
|
||||
ProviderRuntimeModel,
|
||||
} from "../../src/plugins/types.js";
|
||||
import { loginGeminiCliOAuth } from "./oauth.js";
|
||||
|
||||
const PROVIDER_ID = "google-gemini-cli";
|
||||
const PROVIDER_LABEL = "Gemini CLI OAuth";
|
||||
const DEFAULT_MODEL = "google-gemini-cli/gemini-3.1-pro-preview";
|
||||
const GEMINI_3_1_PRO_PREFIX = "gemini-3.1-pro";
|
||||
const GEMINI_3_1_FLASH_PREFIX = "gemini-3.1-flash";
|
||||
const GEMINI_3_1_PRO_TEMPLATE_IDS = ["gemini-3-pro-preview"] as const;
|
||||
const GEMINI_3_1_FLASH_TEMPLATE_IDS = ["gemini-3-flash-preview"] as const;
|
||||
const ENV_VARS = [
|
||||
"OPENCLAW_GEMINI_OAUTH_CLIENT_ID",
|
||||
"OPENCLAW_GEMINI_OAUTH_CLIENT_SECRET",
|
||||
"GEMINI_CLI_OAUTH_CLIENT_ID",
|
||||
"GEMINI_CLI_OAUTH_CLIENT_SECRET",
|
||||
];
|
||||
|
||||
function cloneFirstTemplateModel(params: {
|
||||
modelId: string;
|
||||
templateIds: readonly string[];
|
||||
ctx: ProviderResolveDynamicModelContext;
|
||||
}): ProviderRuntimeModel | undefined {
|
||||
const trimmedModelId = params.modelId.trim();
|
||||
for (const templateId of [...new Set(params.templateIds)].filter(Boolean)) {
|
||||
const template = params.ctx.modelRegistry.find(
|
||||
PROVIDER_ID,
|
||||
templateId,
|
||||
) as ProviderRuntimeModel | null;
|
||||
if (!template) {
|
||||
continue;
|
||||
}
|
||||
return normalizeModelCompat({
|
||||
...template,
|
||||
id: trimmedModelId,
|
||||
name: trimmedModelId,
|
||||
reasoning: true,
|
||||
} as ProviderRuntimeModel);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseGoogleUsageToken(apiKey: string): string {
|
||||
try {
|
||||
const parsed = JSON.parse(apiKey) as { token?: unknown };
|
||||
if (typeof parsed?.token === "string") {
|
||||
return parsed.token;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
async function fetchGeminiCliUsage(ctx: ProviderFetchUsageSnapshotContext) {
|
||||
return await fetchGeminiUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn, PROVIDER_ID);
|
||||
}
|
||||
|
||||
function resolveGeminiCliForwardCompatModel(
|
||||
ctx: ProviderResolveDynamicModelContext,
|
||||
): ProviderRuntimeModel | undefined {
|
||||
const trimmed = ctx.modelId.trim();
|
||||
const lower = trimmed.toLowerCase();
|
||||
|
||||
let templateIds: readonly string[];
|
||||
if (lower.startsWith(GEMINI_3_1_PRO_PREFIX)) {
|
||||
templateIds = GEMINI_3_1_PRO_TEMPLATE_IDS;
|
||||
} else if (lower.startsWith(GEMINI_3_1_FLASH_PREFIX)) {
|
||||
templateIds = GEMINI_3_1_FLASH_TEMPLATE_IDS;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return cloneFirstTemplateModel({
|
||||
modelId: trimmed,
|
||||
templateIds,
|
||||
ctx,
|
||||
});
|
||||
}
|
||||
|
||||
export function registerGoogleGeminiCliProvider(api: OpenClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: PROVIDER_LABEL,
|
||||
docsPath: "/providers/models",
|
||||
aliases: ["gemini-cli"],
|
||||
envVars: ENV_VARS,
|
||||
auth: [
|
||||
{
|
||||
id: "oauth",
|
||||
label: "Google OAuth",
|
||||
hint: "PKCE + localhost callback",
|
||||
kind: "oauth",
|
||||
run: async (ctx: ProviderAuthContext) => {
|
||||
const spin = ctx.prompter.progress("Starting Gemini CLI OAuth…");
|
||||
try {
|
||||
const result = await loginGeminiCliOAuth({
|
||||
isRemote: ctx.isRemote,
|
||||
openUrl: ctx.openUrl,
|
||||
log: (msg) => ctx.runtime.log(msg),
|
||||
note: ctx.prompter.note,
|
||||
prompt: async (message) => String(await ctx.prompter.text({ message })),
|
||||
progress: spin,
|
||||
});
|
||||
|
||||
spin.stop("Gemini CLI OAuth complete");
|
||||
return buildOauthProviderAuthResult({
|
||||
providerId: PROVIDER_ID,
|
||||
defaultModel: DEFAULT_MODEL,
|
||||
access: result.access,
|
||||
refresh: result.refresh,
|
||||
expires: result.expires,
|
||||
email: result.email,
|
||||
credentialExtra: { projectId: result.projectId },
|
||||
notes: ["If requests fail, set GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID."],
|
||||
});
|
||||
} catch (err) {
|
||||
spin.stop("Gemini CLI OAuth failed");
|
||||
await ctx.prompter.note(
|
||||
"Trouble with OAuth? Ensure your Google account has Gemini CLI access.",
|
||||
"OAuth help",
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
resolveDynamicModel: (ctx) => resolveGeminiCliForwardCompatModel(ctx),
|
||||
resolveUsageAuth: async (ctx) => {
|
||||
const auth = await ctx.resolveOAuthToken();
|
||||
if (!auth) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...auth,
|
||||
token: parseGoogleUsageToken(auth.token),
|
||||
};
|
||||
},
|
||||
fetchUsageSnapshot: async (ctx) => await fetchGeminiCliUsage(ctx),
|
||||
});
|
||||
}
|
||||
@ -5,6 +5,7 @@ import {
|
||||
} from "../../src/agents/tools/web-search-plugin-factory.js";
|
||||
import { emptyPluginConfigSchema } from "../../src/plugins/config-schema.js";
|
||||
import type { OpenClawPluginApi } from "../../src/plugins/types.js";
|
||||
import { registerGoogleGeminiCliProvider } from "./gemini-cli-provider.js";
|
||||
|
||||
const googlePlugin = {
|
||||
id: "google",
|
||||
@ -12,6 +13,7 @@ const googlePlugin = {
|
||||
description: "Bundled Google plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
registerGoogleGeminiCliProvider(api);
|
||||
api.registerWebSearchProvider(
|
||||
createPluginBackedWebSearchProvider({
|
||||
id: "gemini",
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { join, parse } from "node:path";
|
||||
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/google-gemini-cli-auth", () => ({
|
||||
vi.mock("../../src/infra/wsl.js", () => ({
|
||||
isWSL2Sync: () => false,
|
||||
}));
|
||||
|
||||
vi.mock("../../src/infra/net/fetch-guard.js", () => ({
|
||||
fetchWithSsrFGuard: async (params: {
|
||||
url: string;
|
||||
init?: RequestInit;
|
||||
@ -2,7 +2,8 @@ import { createHash, randomBytes } from "node:crypto";
|
||||
import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
||||
import { createServer } from "node:http";
|
||||
import { delimiter, dirname, join } from "node:path";
|
||||
import { fetchWithSsrFGuard, isWSL2Sync } from "openclaw/plugin-sdk/google-gemini-cli-auth";
|
||||
import { fetchWithSsrFGuard } from "../../src/infra/net/fetch-guard.js";
|
||||
import { isWSL2Sync } from "../../src/infra/wsl.js";
|
||||
|
||||
const CLIENT_ID_KEYS = ["OPENCLAW_GEMINI_OAUTH_CLIENT_ID", "GEMINI_CLI_OAUTH_CLIENT_ID"];
|
||||
const CLIENT_SECRET_KEYS = [
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"id": "google",
|
||||
"providers": ["google-gemini-cli"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@ -108,10 +108,6 @@
|
||||
"types": "./dist/plugin-sdk/feishu.d.ts",
|
||||
"default": "./dist/plugin-sdk/feishu.js"
|
||||
},
|
||||
"./plugin-sdk/google-gemini-cli-auth": {
|
||||
"types": "./dist/plugin-sdk/google-gemini-cli-auth.d.ts",
|
||||
"default": "./dist/plugin-sdk/google-gemini-cli-auth.js"
|
||||
},
|
||||
"./plugin-sdk/googlechat": {
|
||||
"types": "./dist/plugin-sdk/googlechat.d.ts",
|
||||
"default": "./dist/plugin-sdk/googlechat.js"
|
||||
|
||||
@ -14,11 +14,6 @@ const allowedRawFetchCallsites = new Set([
|
||||
"extensions/feishu/src/streaming-card.ts:101",
|
||||
"extensions/feishu/src/streaming-card.ts:143",
|
||||
"extensions/feishu/src/streaming-card.ts:199",
|
||||
"extensions/google-gemini-cli-auth/oauth.ts:372",
|
||||
"extensions/google-gemini-cli-auth/oauth.ts:408",
|
||||
"extensions/google-gemini-cli-auth/oauth.ts:447",
|
||||
"extensions/google-gemini-cli-auth/oauth.ts:507",
|
||||
"extensions/google-gemini-cli-auth/oauth.ts:575",
|
||||
"extensions/googlechat/src/api.ts:22",
|
||||
"extensions/googlechat/src/api.ts:43",
|
||||
"extensions/googlechat/src/api.ts:63",
|
||||
|
||||
@ -59,7 +59,6 @@ const requiredSubpathEntries = [
|
||||
"diagnostics-otel",
|
||||
"diffs",
|
||||
"feishu",
|
||||
"google-gemini-cli-auth",
|
||||
"googlechat",
|
||||
"irc",
|
||||
"llm-task",
|
||||
|
||||
@ -57,8 +57,6 @@ const requiredPathGroups = [
|
||||
"dist/plugin-sdk/diffs.d.ts",
|
||||
"dist/plugin-sdk/feishu.js",
|
||||
"dist/plugin-sdk/feishu.d.ts",
|
||||
"dist/plugin-sdk/google-gemini-cli-auth.js",
|
||||
"dist/plugin-sdk/google-gemini-cli-auth.d.ts",
|
||||
"dist/plugin-sdk/googlechat.js",
|
||||
"dist/plugin-sdk/googlechat.d.ts",
|
||||
"dist/plugin-sdk/irc.js",
|
||||
|
||||
@ -25,7 +25,6 @@ const entrypoints = [
|
||||
"diagnostics-otel",
|
||||
"diffs",
|
||||
"feishu",
|
||||
"google-gemini-cli-auth",
|
||||
"googlechat",
|
||||
"irc",
|
||||
"llm-task",
|
||||
|
||||
@ -77,7 +77,7 @@ describe("applyAuthChoiceGoogleGeminiCli", () => {
|
||||
expect(result).toBe(expected);
|
||||
expect(mockedApplyAuthChoicePluginProvider).toHaveBeenCalledWith(params, {
|
||||
authChoice: "google-gemini-cli",
|
||||
pluginId: "google-gemini-cli-auth",
|
||||
pluginId: "google",
|
||||
providerId: "google-gemini-cli",
|
||||
methodId: "oauth",
|
||||
label: "Google Gemini CLI",
|
||||
|
||||
@ -29,7 +29,7 @@ export async function applyAuthChoiceGoogleGeminiCli(
|
||||
|
||||
return await applyAuthChoicePluginProvider(params, {
|
||||
authChoice: "google-gemini-cli",
|
||||
pluginId: "google-gemini-cli-auth",
|
||||
pluginId: "google",
|
||||
providerId: "google-gemini-cli",
|
||||
methodId: "oauth",
|
||||
label: "Google Gemini CLI",
|
||||
|
||||
@ -307,7 +307,7 @@ describe("applyPluginAutoEnable", () => {
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(result.config.plugins?.entries?.["google-gemini-cli-auth"]?.enabled).toBe(true);
|
||||
expect(result.config.plugins?.entries?.google?.enabled).toBe(true);
|
||||
});
|
||||
|
||||
it("auto-enables acpx plugin when ACP is configured", () => {
|
||||
|
||||
@ -28,7 +28,7 @@ export type PluginAutoEnableResult = {
|
||||
};
|
||||
|
||||
const PROVIDER_PLUGIN_IDS: Array<{ pluginId: string; providerId: string }> = [
|
||||
{ pluginId: "google-gemini-cli-auth", providerId: "google-gemini-cli" },
|
||||
{ pluginId: "google", providerId: "google-gemini-cli" },
|
||||
{ pluginId: "qwen-portal-auth", providerId: "qwen-portal" },
|
||||
{ pluginId: "copilot-proxy", providerId: "copilot-proxy" },
|
||||
{ pluginId: "minimax-portal-auth", providerId: "minimax-portal" },
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
// Narrow plugin-sdk surface for the bundled google-gemini-cli-auth plugin.
|
||||
// Keep this list additive and scoped to symbols used under extensions/google-gemini-cli-auth.
|
||||
|
||||
export { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
|
||||
export { isWSL2Sync } from "../infra/wsl.js";
|
||||
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||
export type {
|
||||
OpenClawPluginApi,
|
||||
ProviderAuthContext,
|
||||
ProviderFetchUsageSnapshotContext,
|
||||
ProviderResolveDynamicModelContext,
|
||||
ProviderRuntimeModel,
|
||||
} from "../plugins/types.js";
|
||||
export type { ProviderUsageSnapshot } from "../infra/provider-usage.types.js";
|
||||
export { buildOauthProviderAuthResult } from "./provider-auth-result.js";
|
||||
@ -25,7 +25,6 @@ const pluginSdkEntrypoints = [
|
||||
"diagnostics-otel",
|
||||
"diffs",
|
||||
"feishu",
|
||||
"google-gemini-cli-auth",
|
||||
"googlechat",
|
||||
"irc",
|
||||
"llm-task",
|
||||
|
||||
@ -18,10 +18,6 @@ const bundledExtensionSubpathLoaders = [
|
||||
{ id: "diagnostics-otel", load: () => import("openclaw/plugin-sdk/diagnostics-otel") },
|
||||
{ id: "diffs", load: () => import("openclaw/plugin-sdk/diffs") },
|
||||
{ id: "feishu", load: () => import("openclaw/plugin-sdk/feishu") },
|
||||
{
|
||||
id: "google-gemini-cli-auth",
|
||||
load: () => import("openclaw/plugin-sdk/google-gemini-cli-auth"),
|
||||
},
|
||||
{ id: "googlechat", load: () => import("openclaw/plugin-sdk/googlechat") },
|
||||
{ id: "irc", load: () => import("openclaw/plugin-sdk/irc") },
|
||||
{ id: "llm-task", load: () => import("openclaw/plugin-sdk/llm-task") },
|
||||
|
||||
@ -5,9 +5,9 @@ import { enablePluginInConfig } from "./enable.js";
|
||||
describe("enablePluginInConfig", () => {
|
||||
it("enables a plugin entry", () => {
|
||||
const cfg: OpenClawConfig = {};
|
||||
const result = enablePluginInConfig(cfg, "google-gemini-cli-auth");
|
||||
const result = enablePluginInConfig(cfg, "google");
|
||||
expect(result.enabled).toBe(true);
|
||||
expect(result.config.plugins?.entries?.["google-gemini-cli-auth"]?.enabled).toBe(true);
|
||||
expect(result.config.plugins?.entries?.google?.enabled).toBe(true);
|
||||
});
|
||||
|
||||
it("adds plugin to allowlist when allowlist is configured", () => {
|
||||
@ -16,18 +16,18 @@ describe("enablePluginInConfig", () => {
|
||||
allow: ["memory-core"],
|
||||
},
|
||||
};
|
||||
const result = enablePluginInConfig(cfg, "google-gemini-cli-auth");
|
||||
const result = enablePluginInConfig(cfg, "google");
|
||||
expect(result.enabled).toBe(true);
|
||||
expect(result.config.plugins?.allow).toEqual(["memory-core", "google-gemini-cli-auth"]);
|
||||
expect(result.config.plugins?.allow).toEqual(["memory-core", "google"]);
|
||||
});
|
||||
|
||||
it("refuses enable when plugin is denylisted", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
plugins: {
|
||||
deny: ["google-gemini-cli-auth"],
|
||||
deny: ["google"],
|
||||
},
|
||||
};
|
||||
const result = enablePluginInConfig(cfg, "google-gemini-cli-auth");
|
||||
const result = enablePluginInConfig(cfg, "google");
|
||||
expect(result.enabled).toBe(false);
|
||||
expect(result.reason).toBe("blocked by denylist");
|
||||
});
|
||||
|
||||
@ -10,7 +10,7 @@ const BUNDLED_PROVIDER_ALLOWLIST_COMPAT_PLUGIN_IDS = [
|
||||
"cloudflare-ai-gateway",
|
||||
"copilot-proxy",
|
||||
"github-copilot",
|
||||
"google-gemini-cli-auth",
|
||||
"google",
|
||||
"huggingface",
|
||||
"kilocode",
|
||||
"kimi-coding",
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
"src/plugin-sdk/diagnostics-otel.ts",
|
||||
"src/plugin-sdk/diffs.ts",
|
||||
"src/plugin-sdk/feishu.ts",
|
||||
"src/plugin-sdk/google-gemini-cli-auth.ts",
|
||||
"src/plugin-sdk/googlechat.ts",
|
||||
"src/plugin-sdk/irc.ts",
|
||||
"src/plugin-sdk/llm-task.ts",
|
||||
|
||||
@ -77,7 +77,6 @@ const pluginSdkEntrypoints = [
|
||||
"diagnostics-otel",
|
||||
"diffs",
|
||||
"feishu",
|
||||
"google-gemini-cli-auth",
|
||||
"googlechat",
|
||||
"irc",
|
||||
"llm-task",
|
||||
|
||||
@ -27,7 +27,6 @@ const pluginSdkSubpaths = [
|
||||
"diagnostics-otel",
|
||||
"diffs",
|
||||
"feishu",
|
||||
"google-gemini-cli-auth",
|
||||
"googlechat",
|
||||
"irc",
|
||||
"llm-task",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user