diff --git a/extensions/google-antigravity-auth/index.ts b/extensions/google-antigravity-auth/index.ts
index f349ada6a..56c583a06 100644
--- a/extensions/google-antigravity-auth/index.ts
+++ b/extensions/google-antigravity-auth/index.ts
@@ -2,6 +2,7 @@ import { createHash, randomBytes } from "node:crypto";
import { readFileSync } from "node:fs";
import { createServer } from "node:http";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
+import { ProxyAgent } from "undici";
// OAuth constants - decoded from pi-ai's base64 encoded values to stay in sync
const decode = (s: string) => Buffer.from(s, "base64").toString();
@@ -28,6 +29,37 @@ const CODE_ASSIST_ENDPOINTS = [
"https://daily-cloudcode-pa.sandbox.googleapis.com",
];
+const PROXY_ENV_KEYS = [
+ "CLAWDBOT_HTTPS_PROXY",
+ "CLAWDBOT_HTTP_PROXY",
+ "HTTPS_PROXY",
+ "HTTP_PROXY",
+ "https_proxy",
+ "http_proxy",
+];
+
+function resolveProxyUrl(): string | undefined {
+ for (const key of PROXY_ENV_KEYS) {
+ const value = process.env[key]?.trim();
+ if (value) return value;
+ }
+ return undefined;
+}
+
+const proxyUrl = resolveProxyUrl();
+const proxyAgent = proxyUrl
+ ? new ProxyAgent({
+ uri: proxyUrl,
+ requestTls: { maxVersion: "TLSv1.2" },
+ })
+ : undefined;
+
+function fetchWithProxy(input: RequestInfo | URL, init?: RequestInit) {
+ if (!proxyAgent) return fetch(input, init);
+ const nextInit = init ? { ...init, dispatcher: proxyAgent } : { dispatcher: proxyAgent };
+ return fetch(input, nextInit);
+}
+
const RESPONSE_PAGE = `
@@ -178,7 +210,7 @@ async function exchangeCode(params: {
code: string;
verifier: string;
}): Promise<{ access: string; refresh: string; expires: number }> {
- const response = await fetch(TOKEN_URL, {
+ const response = await fetchWithProxy(TOKEN_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
@@ -215,7 +247,7 @@ async function exchangeCode(params: {
async function fetchUserEmail(accessToken: string): Promise {
try {
- const response = await fetch("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", {
+ const response = await fetchWithProxy("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!response.ok) return undefined;
@@ -241,7 +273,7 @@ async function fetchProjectId(accessToken: string): Promise {
for (const endpoint of CODE_ASSIST_ENDPOINTS) {
try {
- const response = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {
+ const response = await fetchWithProxy(`${endpoint}/v1internal:loadCodeAssist`, {
method: "POST",
headers,
body: JSON.stringify({
diff --git a/extensions/google-gemini-cli-auth/oauth.ts b/extensions/google-gemini-cli-auth/oauth.ts
index 405b94641..05af34497 100644
--- a/extensions/google-gemini-cli-auth/oauth.ts
+++ b/extensions/google-gemini-cli-auth/oauth.ts
@@ -2,6 +2,7 @@ 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 { ProxyAgent } from "undici";
const CLIENT_ID_KEYS = ["CLAWDBOT_GEMINI_OAUTH_CLIENT_ID", "GEMINI_CLI_OAUTH_CLIENT_ID"];
const CLIENT_SECRET_KEYS = [
@@ -23,6 +24,37 @@ const TIER_FREE = "free-tier";
const TIER_LEGACY = "legacy-tier";
const TIER_STANDARD = "standard-tier";
+const PROXY_ENV_KEYS = [
+ "CLAWDBOT_HTTPS_PROXY",
+ "CLAWDBOT_HTTP_PROXY",
+ "HTTPS_PROXY",
+ "HTTP_PROXY",
+ "https_proxy",
+ "http_proxy",
+];
+
+function resolveProxyUrl(): string | undefined {
+ for (const key of PROXY_ENV_KEYS) {
+ const value = process.env[key]?.trim();
+ if (value) return value;
+ }
+ return undefined;
+}
+
+const proxyUrl = resolveProxyUrl();
+const proxyAgent = proxyUrl
+ ? new ProxyAgent({
+ uri: proxyUrl,
+ requestTls: { maxVersion: "TLSv1.2" },
+ })
+ : undefined;
+
+function fetchWithProxy(input: RequestInfo | URL, init?: RequestInit) {
+ if (!proxyAgent) return fetch(input, init);
+ const nextInit = init ? { ...init, dispatcher: proxyAgent } : { dispatcher: proxyAgent };
+ return fetch(input, nextInit);
+}
+
export type GeminiCliOAuthCredentials = {
access: string;
refresh: string;
@@ -312,7 +344,7 @@ async function exchangeCodeForTokens(code: string, verifier: string): Promise {
try {
- const response = await fetch(USERINFO_URL, {
+ const response = await fetchWithProxy(USERINFO_URL, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (response.ok) {
@@ -387,7 +419,7 @@ async function discoverProject(accessToken: string): Promise {
} = {};
try {
- const response = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`, {
+ const response = await fetchWithProxy(`${CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`, {
method: "POST",
headers,
body: JSON.stringify(loadBody),
@@ -441,7 +473,7 @@ async function discoverProject(accessToken: string): Promise {
(onboardBody.metadata as Record).duetProject = envProject;
}
- const onboardResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:onboardUser`, {
+ const onboardResponse = await fetchWithProxy(`${CODE_ASSIST_ENDPOINT}/v1internal:onboardUser`, {
method: "POST",
headers,
body: JSON.stringify(onboardBody),
@@ -495,7 +527,7 @@ async function pollOperation(
): Promise<{ done?: boolean; response?: { cloudaicompanionProject?: { id?: string } } }> {
for (let attempt = 0; attempt < 24; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 5000));
- const response = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal/${operationName}`, {
+ const response = await fetchWithProxy(`${CODE_ASSIST_ENDPOINT}/v1internal/${operationName}`, {
headers,
});
if (!response.ok) continue;