clawmoney 0.13.0 → 0.13.2
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.
|
@@ -68,6 +68,7 @@ interface AntigravityAccountsFile {
|
|
|
68
68
|
version: 1;
|
|
69
69
|
accounts: AntigravityAccount[];
|
|
70
70
|
}
|
|
71
|
+
export declare function configureAntigravityDispatcher(): void;
|
|
71
72
|
export declare function ensureClawmoneyDir(): void;
|
|
72
73
|
export declare function loadAccounts(): AntigravityAccountsFile;
|
|
73
74
|
export declare function saveAccounts(file: AntigravityAccountsFile): void;
|
|
@@ -29,7 +29,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
|
29
29
|
import { join } from "node:path";
|
|
30
30
|
import { homedir } from "node:os";
|
|
31
31
|
import { randomUUID } from "node:crypto";
|
|
32
|
-
import { ProxyAgent
|
|
32
|
+
import { ProxyAgent } from "undici";
|
|
33
33
|
import { relayLogger as logger } from "../logger.js";
|
|
34
34
|
import { RateGuard, RateGuardBudgetExceededError, RateGuardCooldownError, } from "./rate-guard.js";
|
|
35
35
|
import { calculateCost } from "../pricing.js";
|
|
@@ -83,24 +83,73 @@ const ANTIGRAVITY_VERSION = "1.21.9";
|
|
|
83
83
|
const CLAWMONEY_DIR = join(homedir(), ".clawmoney");
|
|
84
84
|
const ACCOUNTS_FILE = join(CLAWMONEY_DIR, "antigravity-accounts.json");
|
|
85
85
|
// ── Proxy ──
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
//
|
|
87
|
+
// We build our own ProxyAgent instead of relying on setGlobalDispatcher,
|
|
88
|
+
// then pass it explicitly to each fetch() call. This is more reliable than
|
|
89
|
+
// the global-dispatcher approach (no cross-module timing issues with Node's
|
|
90
|
+
// built-in fetch) and makes errors surface with a real cause chain.
|
|
91
|
+
//
|
|
92
|
+
// Also exported so the `antigravity login` command can trigger the same
|
|
93
|
+
// setup before it hits oauth2.googleapis.com / userinfo / loadCodeAssist —
|
|
94
|
+
// providers behind the GFW were seeing "fetch failed" at the token-exchange
|
|
95
|
+
// step before we honored HTTPS_PROXY here.
|
|
96
|
+
let cachedProxyAgent = null;
|
|
97
|
+
let proxyResolved = false;
|
|
98
|
+
function getProxyAgent() {
|
|
99
|
+
if (proxyResolved)
|
|
100
|
+
return cachedProxyAgent;
|
|
101
|
+
proxyResolved = true;
|
|
91
102
|
const url = process.env.HTTPS_PROXY ||
|
|
92
103
|
process.env.https_proxy ||
|
|
93
104
|
process.env.HTTP_PROXY ||
|
|
94
105
|
process.env.http_proxy;
|
|
95
106
|
if (!url)
|
|
96
|
-
return;
|
|
107
|
+
return null;
|
|
97
108
|
if (!/^https?:\/\//.test(url)) {
|
|
98
109
|
logger.warn(`[antigravity-api] ignoring non-HTTP proxy ${url} (SOCKS not supported)`);
|
|
99
|
-
return;
|
|
110
|
+
return null;
|
|
100
111
|
}
|
|
101
|
-
|
|
112
|
+
cachedProxyAgent = new ProxyAgent(url);
|
|
102
113
|
logger.info(`[antigravity-api] upstream proxy ${url}`);
|
|
114
|
+
return cachedProxyAgent;
|
|
103
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* fetch wrapper that: (1) auto-applies the configured ProxyAgent when
|
|
118
|
+
* HTTPS_PROXY is set, and (2) unwraps undici's TypeError("fetch failed")
|
|
119
|
+
* to surface the real underlying error code — without this, users on the
|
|
120
|
+
* GFW side see opaque "fetch failed" messages and can't tell whether it's
|
|
121
|
+
* a DNS failure, cert error, proxy refusal, or timeout.
|
|
122
|
+
*/
|
|
123
|
+
async function fetchWithProxy(url, init = {}) {
|
|
124
|
+
const agent = getProxyAgent();
|
|
125
|
+
const opts = { ...init };
|
|
126
|
+
if (agent)
|
|
127
|
+
opts.dispatcher = agent;
|
|
128
|
+
try {
|
|
129
|
+
// Node's built-in fetch accepts `dispatcher` through the undici
|
|
130
|
+
// extension, but the DOM `RequestInit` type doesn't expose it. We cast
|
|
131
|
+
// via a local type to keep strict TS happy.
|
|
132
|
+
return await fetch(url, opts);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
// undici wraps the real network error in TypeError("fetch failed")
|
|
136
|
+
// with the real cause on err.cause. Surface it so "ECONNREFUSED"
|
|
137
|
+
// / "ETIMEDOUT" / "CERT_HAS_EXPIRED" is visible in logs.
|
|
138
|
+
const cause = err.cause;
|
|
139
|
+
if (cause) {
|
|
140
|
+
const code = cause.code;
|
|
141
|
+
const message = cause.message;
|
|
142
|
+
throw new Error(`fetch ${url} failed: ${code ?? ""} ${message ?? String(cause)}`.trim());
|
|
143
|
+
}
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Legacy name kept for call sites that haven't been migrated. Also acts as
|
|
148
|
+
// the public preflight hook for the daemon's `preflightAntigravityApi`.
|
|
149
|
+
export function configureAntigravityDispatcher() {
|
|
150
|
+
getProxyAgent();
|
|
151
|
+
}
|
|
152
|
+
const configureDispatcher = configureAntigravityDispatcher;
|
|
104
153
|
// ── Account storage ──
|
|
105
154
|
export function ensureClawmoneyDir() {
|
|
106
155
|
mkdirSync(CLAWMONEY_DIR, { recursive: true });
|
|
@@ -157,7 +206,7 @@ async function refreshUpstreamToken(refreshToken) {
|
|
|
157
206
|
client_id: ANTIGRAVITY_CLIENT_ID,
|
|
158
207
|
client_secret: ANTIGRAVITY_CLIENT_SECRET,
|
|
159
208
|
});
|
|
160
|
-
const resp = await
|
|
209
|
+
const resp = await fetchWithProxy(OAUTH_TOKEN_URL, {
|
|
161
210
|
method: "POST",
|
|
162
211
|
headers: {
|
|
163
212
|
"content-type": "application/x-www-form-urlencoded",
|
|
@@ -223,6 +272,7 @@ async function getFreshAccount() {
|
|
|
223
272
|
* sub2api and opencode-antigravity-auth behavior.
|
|
224
273
|
*/
|
|
225
274
|
export async function resolveAntigravityProjectId(accessToken) {
|
|
275
|
+
configureDispatcher();
|
|
226
276
|
const body = JSON.stringify({
|
|
227
277
|
metadata: {
|
|
228
278
|
ideType: "ANTIGRAVITY",
|
|
@@ -233,11 +283,7 @@ export async function resolveAntigravityProjectId(accessToken) {
|
|
|
233
283
|
const headers = antigravityHeaders(accessToken);
|
|
234
284
|
for (const baseEndpoint of ANTIGRAVITY_ENDPOINTS) {
|
|
235
285
|
try {
|
|
236
|
-
const resp = await
|
|
237
|
-
method: "POST",
|
|
238
|
-
headers,
|
|
239
|
-
body,
|
|
240
|
-
});
|
|
286
|
+
const resp = await fetchWithProxy(`${baseEndpoint}/v1internal:loadCodeAssist`, { method: "POST", headers, body });
|
|
241
287
|
if (!resp.ok)
|
|
242
288
|
continue;
|
|
243
289
|
const data = (await resp.json());
|
|
@@ -375,7 +421,7 @@ async function doCallAntigravityApi(opts) {
|
|
|
375
421
|
const url = `${baseEndpoint}${GENERATE_PATH}`;
|
|
376
422
|
let resp;
|
|
377
423
|
try {
|
|
378
|
-
resp = await
|
|
424
|
+
resp = await fetchWithProxy(url, {
|
|
379
425
|
method: "POST",
|
|
380
426
|
headers: antigravityHeaders(creds.access_token),
|
|
381
427
|
body: bodyJson,
|
|
@@ -511,7 +557,7 @@ export async function storeNewAntigravityAccount(input) {
|
|
|
511
557
|
*/
|
|
512
558
|
export async function exchangeAntigravityAuthCode(input) {
|
|
513
559
|
const start = Date.now();
|
|
514
|
-
const resp = await
|
|
560
|
+
const resp = await fetchWithProxy(OAUTH_TOKEN_URL, {
|
|
515
561
|
method: "POST",
|
|
516
562
|
headers: {
|
|
517
563
|
"content-type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
@@ -548,7 +594,7 @@ export async function exchangeAntigravityAuthCode(input) {
|
|
|
548
594
|
*/
|
|
549
595
|
export async function fetchAntigravityUserEmail(accessToken) {
|
|
550
596
|
try {
|
|
551
|
-
const resp = await
|
|
597
|
+
const resp = await fetchWithProxy(OAUTH_USERINFO_URL, {
|
|
552
598
|
headers: { authorization: `Bearer ${accessToken}` },
|
|
553
599
|
});
|
|
554
600
|
if (!resp.ok)
|