pi-provider-antigravity 0.16.0 → 0.18.0

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.
package/dist/index.js CHANGED
@@ -1,77 +1,25 @@
1
1
  // src/auth.ts
2
2
  import { createHash, randomBytes } from "node:crypto";
3
3
 
4
- // src/protocol.ts
5
- var DEFAULT_ENDPOINT = "https://daily-cloudcode-pa.googleapis.com";
6
- var DEFAULT_USER_AGENT = "antigravity/cli/1.2.7 (aidev_client; os_type=linux; arch=amd64; cl=984112147; auth_method=consumer)";
4
+ // src/constants.ts
7
5
  var PROVIDER_ID = "antigravity";
8
- var AntigravityHttpError = class extends Error {
9
- status;
10
- path;
11
- errorBody;
12
- constructor(status, path2, errorBody) {
13
- super(`Antigravity request to ${path2} failed (${status}): ${errorBody}`);
14
- this.name = "AntigravityHttpError";
15
- this.status = status;
16
- this.path = path2;
17
- this.errorBody = errorBody;
18
- }
19
- };
20
- function resolveToken(auth) {
21
- if (typeof auth === "string") return auth;
22
- if (auth && typeof auth.token === "string") return auth.token;
23
- throw new Error("Invalid auth parameter: missing token string");
24
- }
25
- async function postAntigravityRaw({
26
- auth,
27
- path: path2,
28
- endpoint = DEFAULT_ENDPOINT,
29
- headers,
30
- body,
31
- signal
32
- }) {
33
- const token = resolveToken(auth);
34
- return fetch(`${endpoint}/${path2}`, {
35
- method: "POST",
36
- headers: { ...buildAntigravityHeaders(token), ...headers },
37
- body: JSON.stringify(body),
38
- signal
39
- });
40
- }
41
- async function postAntigravityJson(params) {
42
- const res = await postAntigravityRaw(params);
43
- if (!res.ok) {
44
- const errText = await res.text();
45
- throw new AntigravityHttpError(res.status, params.path, errText);
46
- }
47
- return await res.json();
48
- }
49
- async function postAntigravityStream(params) {
50
- const res = await postAntigravityRaw(params);
51
- if (!res.ok) {
52
- const errText = await res.text();
53
- throw new AntigravityHttpError(res.status, params.path, errText);
54
- }
55
- if (!res.body) {
56
- throw new Error(`No response stream received from Antigravity for ${params.path}.`);
57
- }
58
- return { response: res, stream: res.body };
59
- }
60
- function buildAntigravityHeaders(token, userAgent = DEFAULT_USER_AGENT) {
61
- return {
62
- Host: "daily-cloudcode-pa.googleapis.com",
63
- "User-Agent": userAgent,
64
- "Content-Type": "application/json",
65
- Authorization: `Bearer ${token}`
66
- };
67
- }
6
+ var PROVIDER_NAME = "Antigravity";
7
+ var DEFAULT_HOST = "daily-cloudcode-pa.googleapis.com";
8
+ var DEFAULT_ENDPOINT = `https://${DEFAULT_HOST}`;
9
+ var DEFAULT_USER_AGENT = "antigravity/cli/1.2.11 (aidev_client; os_type=linux; arch=amd64; cl=987915746; auth_method=consumer)";
10
+ var FALLBACK_PROJECT_ID = "aicode-consumers";
68
11
 
69
12
  // src/auth.ts
13
+ var AntigravityAuthError = class extends Error {
14
+ constructor(message = NOT_LOGGED_IN) {
15
+ super(message);
16
+ this.name = "AntigravityAuthError";
17
+ }
18
+ };
70
19
  var REDIRECT_URI = "https://antigravity.google/oauth-callback";
71
20
  var AUTH_URL = "https://accounts.google.com/o/oauth2/auth";
72
21
  var TOKEN_URL = "https://oauth2.googleapis.com/token";
73
22
  var OAUTH_CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
74
- var FALLBACK_PROJECT_ID = "aicode-consumers";
75
23
  var SCOPES = [
76
24
  "https://www.googleapis.com/auth/cloud-platform",
77
25
  "https://www.googleapis.com/auth/userinfo.email",
@@ -112,14 +60,22 @@ function generatePKCE() {
112
60
  }
113
61
  async function fetchProjectId(token, signal) {
114
62
  try {
115
- const data = await postAntigravityJson({
116
- auth: token,
117
- path: "v1internal:loadCodeAssist",
118
- body: { metadata: { ideType: "ANTIGRAVITY" } },
63
+ const res = await fetch(`${DEFAULT_ENDPOINT}/v1internal:loadCodeAssist`, {
64
+ method: "POST",
65
+ headers: {
66
+ Host: DEFAULT_HOST,
67
+ "User-Agent": DEFAULT_USER_AGENT,
68
+ "Content-Type": "application/json",
69
+ Authorization: `Bearer ${token}`
70
+ },
71
+ body: JSON.stringify({ metadata: { ideType: "ANTIGRAVITY" } }),
119
72
  signal
120
73
  });
121
- if (data.cloudaicompanionProject) {
122
- return data.cloudaicompanionProject;
74
+ if (res.ok) {
75
+ const data = await res.json();
76
+ if (data.cloudaicompanionProject) {
77
+ return data.cloudaicompanionProject;
78
+ }
123
79
  }
124
80
  } catch (err) {
125
81
  if (signal?.aborted) throw err;
@@ -262,7 +218,7 @@ var NOT_LOGGED_IN = "Not logged in. Run /login antigravity first.";
262
218
  async function requireCredentials(source, errorMessage = NOT_LOGGED_IN) {
263
219
  const creds = await resolveCredentials(source);
264
220
  if (!creds) {
265
- throw new Error(errorMessage);
221
+ throw new AntigravityAuthError(errorMessage);
266
222
  }
267
223
  return creds;
268
224
  }
@@ -277,20 +233,158 @@ function parseStoredCredentials(rawApiKey) {
277
233
  return { token: rawApiKey, projectId: FALLBACK_PROJECT_ID };
278
234
  }
279
235
 
236
+ // src/protocol.ts
237
+ var AntigravityHttpError = class extends Error {
238
+ status;
239
+ path;
240
+ errorBody;
241
+ constructor(status, path2, errorBody) {
242
+ super(`Antigravity request to ${path2} failed (${status}): ${errorBody}`);
243
+ this.name = "AntigravityHttpError";
244
+ this.status = status;
245
+ this.path = path2;
246
+ this.errorBody = errorBody;
247
+ }
248
+ };
249
+ function resolveToken(auth) {
250
+ if (typeof auth === "string") return auth;
251
+ if (auth && typeof auth.token === "string") return auth.token;
252
+ throw new Error("Invalid auth parameter: missing token string");
253
+ }
254
+ async function postAntigravityRaw({
255
+ auth,
256
+ path: path2,
257
+ endpoint = DEFAULT_ENDPOINT,
258
+ headers,
259
+ body,
260
+ signal
261
+ }) {
262
+ const token = resolveToken(auth);
263
+ return fetch(`${endpoint}/${path2}`, {
264
+ method: "POST",
265
+ headers: { ...buildAntigravityHeaders(token), ...headers },
266
+ body: JSON.stringify(body),
267
+ signal
268
+ });
269
+ }
270
+ async function postAntigravityJson(params) {
271
+ const res = await postAntigravityRaw(params);
272
+ if (!res.ok) {
273
+ const errText = await res.text();
274
+ throw new AntigravityHttpError(res.status, params.path, errText);
275
+ }
276
+ return await res.json();
277
+ }
278
+ async function postAntigravityStream(params) {
279
+ const res = await postAntigravityRaw(params);
280
+ if (!res.ok) {
281
+ const errText = await res.text();
282
+ throw new AntigravityHttpError(res.status, params.path, errText);
283
+ }
284
+ if (!res.body) {
285
+ throw new Error(`No response stream received from Antigravity for ${params.path}.`);
286
+ }
287
+ return { response: res, stream: res.body };
288
+ }
289
+ function buildAntigravityHeaders(token, userAgent = DEFAULT_USER_AGENT) {
290
+ return {
291
+ Host: DEFAULT_HOST,
292
+ "User-Agent": userAgent,
293
+ "Content-Type": "application/json",
294
+ Authorization: `Bearer ${token}`
295
+ };
296
+ }
297
+ function createAntigravityClient(options) {
298
+ const endpoint = options?.endpoint || DEFAULT_ENDPOINT;
299
+ const userAgent = options?.userAgent || DEFAULT_USER_AGENT;
300
+ return {
301
+ endpoint,
302
+ userAgent,
303
+ async fetchAvailableModels(source, signal) {
304
+ const creds = await requireCredentials(source);
305
+ return postAntigravityJson({
306
+ auth: creds.token,
307
+ endpoint,
308
+ path: "v1internal:fetchAvailableModels",
309
+ body: { project: creds.projectId },
310
+ signal
311
+ });
312
+ },
313
+ async retrieveQuotaSummary(source, signal) {
314
+ const creds = await resolveCredentials(source);
315
+ if (!creds) return void 0;
316
+ return postAntigravityJson({
317
+ auth: creds.token,
318
+ endpoint,
319
+ path: "v1internal:retrieveUserQuotaSummary",
320
+ body: { project: creds.projectId },
321
+ signal
322
+ });
323
+ },
324
+ async streamGenerateContent(params) {
325
+ const creds = await requireCredentials(params.source);
326
+ return postAntigravityStream({
327
+ auth: creds.token,
328
+ endpoint: params.endpoint || endpoint,
329
+ path: "v1internal:streamGenerateContent?alt=sse",
330
+ headers: params.headers,
331
+ body: params.body,
332
+ signal: params.signal
333
+ });
334
+ },
335
+ async postJson(source, path2, body = {}, signal, headers) {
336
+ const creds = await requireCredentials(source);
337
+ const effectiveBody = body && typeof body === "object" && !Array.isArray(body) ? { project: creds.projectId, ...body } : body;
338
+ return postAntigravityJson({
339
+ auth: creds.token,
340
+ endpoint,
341
+ path: path2,
342
+ body: effectiveBody,
343
+ headers,
344
+ signal
345
+ });
346
+ }
347
+ };
348
+ }
349
+ var defaultAntigravityClient = createAntigravityClient();
350
+
280
351
  // src/model-identity.ts
281
- function classifyModelFamily(modelId) {
352
+ function getModelProfile(modelId) {
282
353
  const bare = ((modelId || "").split("/").pop() || "").toLowerCase();
283
- if (bare.startsWith("gemini-")) return "gemini";
284
- if (bare.startsWith("claude-")) return "claude";
285
- if (bare.startsWith("gpt-")) return "gpt";
286
- return "unknown";
287
- }
288
- function isCompatibleFamily(msgModel, targetModelId) {
289
- if (!msgModel || !targetModelId) return true;
290
- if (msgModel === targetModelId) return true;
291
- const msgFamily = classifyModelFamily(msgModel);
292
- if (msgFamily !== "unknown" && msgFamily === classifyModelFamily(targetModelId)) return true;
293
- return false;
354
+ let family = "unknown";
355
+ if (bare.startsWith("gemini-")) family = "gemini";
356
+ else if (bare.startsWith("claude-")) family = "claude";
357
+ else if (bare.startsWith("gpt-")) family = "gpt";
358
+ const isGemini = family === "gemini";
359
+ const isClaude = family === "claude";
360
+ const isGpt = family === "gpt";
361
+ const isNonGemini = family !== "gemini";
362
+ const quotaPoolKind = isClaude || isGpt ? "3p" : "gemini";
363
+ const replaysReasoningHistory = family !== "gpt";
364
+ const isFlash = bare.includes("flash");
365
+ const defaultContextWindow = isFlash ? 1048576 : isClaude ? 25e4 : isGpt ? 128e3 : 1048576;
366
+ const defaultMaxOutputTokens = isClaude ? 64e3 : isGpt ? 32768 : 65536;
367
+ const promptCache = isClaude ? { short: 300, long: 3600 } : { short: 300 };
368
+ return {
369
+ id: modelId,
370
+ family,
371
+ isGemini,
372
+ isClaude,
373
+ isGpt,
374
+ isNonGemini,
375
+ quotaPoolKind,
376
+ replaysReasoningHistory,
377
+ defaultContextWindow,
378
+ defaultMaxOutputTokens,
379
+ promptCache,
380
+ isReplayCompatible(candidateModel) {
381
+ if (!candidateModel || !modelId) return true;
382
+ if (candidateModel === modelId) return true;
383
+ const other = getModelProfile(candidateModel);
384
+ if (other.family !== "unknown" && other.family === family) return true;
385
+ return false;
386
+ }
387
+ };
294
388
  }
295
389
 
296
390
  // src/model-catalog.ts
@@ -432,13 +526,8 @@ function parseAvailableModels(data) {
432
526
  ...Object.keys(deprecated).length > 0 ? { deprecated } : {}
433
527
  };
434
528
  }
435
- async function fetchAvailableModelsCatalog(token, projectId, signal) {
436
- const json = await postAntigravityJson({
437
- auth: token,
438
- path: "v1internal:fetchAvailableModels",
439
- body: { project: projectId },
440
- signal
441
- });
529
+ async function fetchAvailableModelsCatalog(source, signal, client = defaultAntigravityClient) {
530
+ const json = await client.fetchAvailableModels(source, signal);
442
531
  return parseAvailableModels(json);
443
532
  }
444
533
  function toPersistedSnapshot(snapshot) {
@@ -509,13 +598,13 @@ function resolveModelPlan(publicModelId, effort, snapshot) {
509
598
  `Unknown model "${runtimeModelId}". Run /antigravity refresh, then pick a current model.`
510
599
  );
511
600
  }
512
- const family = classifyModelFamily(runtimeModelId);
601
+ const profile = getModelProfile(runtimeModelId);
513
602
  return {
514
603
  runtimeModelId,
515
604
  modelEnum,
516
605
  thinkingConfig: resolveThinkingConfig(runtimeModelId, snapshot),
517
- isNonGemini: family !== "gemini",
518
- isClaude: family === "claude"
606
+ isNonGemini: profile.isNonGemini,
607
+ isClaude: profile.isClaude
519
608
  };
520
609
  }
521
610
  function estimateModelCost(_baseId) {
@@ -544,13 +633,9 @@ function synthesizeDynamicModel(baseId, items) {
544
633
  ...hasVariant(tierSpellings("medium")) ? {} : { medium: null },
545
634
  ...hasVariant(tierSpellings("high")) || items.some((it) => it.id === baseId) ? {} : { high: null }
546
635
  };
547
- const family = classifyModelFamily(baseId);
548
- const isFlash = baseId.includes("flash");
549
- const isClaude = family === "claude";
550
- const isGpt = family === "gpt";
551
- const defaultContext = isFlash ? 1048576 : isClaude ? 25e4 : isGpt ? 128e3 : 1048576;
552
- const defaultMaxOutput = isClaude ? 64e3 : isGpt ? 32768 : 65536;
553
- const promptCache = isClaude ? { short: 300, long: 3600 } : { short: 300 };
636
+ const profile = getModelProfile(baseId);
637
+ const defaultContext = profile.defaultContextWindow;
638
+ const defaultMaxOutput = profile.defaultMaxOutputTokens;
554
639
  return {
555
640
  id: baseId,
556
641
  name: formatModelDisplayName(baseId, repItem?.displayName),
@@ -561,7 +646,7 @@ function synthesizeDynamicModel(baseId, items) {
561
646
  thinkingLevelMap,
562
647
  input: repItem?.supportsImages ? ["text", "image"] : ["text"],
563
648
  cost: estimateModelCost(baseId),
564
- promptCache,
649
+ promptCache: profile.promptCache,
565
650
  contextWindow: repItem?.maxTokens || defaultContext,
566
651
  maxTokens: repItem?.maxOutputTokens || defaultMaxOutput
567
652
  };
@@ -639,7 +724,8 @@ function formatModelsList(catalog) {
639
724
  }
640
725
  return lines.join("\n");
641
726
  }
642
- function createModelCatalog() {
727
+ function createModelCatalog(deps) {
728
+ const client = deps?.client ?? defaultAntigravityClient;
643
729
  let generation = { snapshot: EMPTY_SNAPSHOT(), version: 0 };
644
730
  const record = (catalogOrRaw) => {
645
731
  let catalog;
@@ -678,9 +764,7 @@ function createModelCatalog() {
678
764
  const storedModels = () => [...context.stored?.models ?? []];
679
765
  if (!context.allowNetwork) return storedModels();
680
766
  try {
681
- const creds = await resolveCredentials(context);
682
- if (!creds) return storedModels();
683
- const catalog = await fetchAvailableModelsCatalog(creds, creds.projectId, context.signal);
767
+ const catalog = await fetchAvailableModelsCatalog(context, context.signal, client);
684
768
  record(catalog);
685
769
  const dynamicModels = buildDynamicPublicModels(catalog);
686
770
  if (context.publish) {
@@ -1030,8 +1114,8 @@ function isGeminiQuotaGroup(group) {
1030
1114
  }
1031
1115
  function selectQuotaGroup(groups, modelId) {
1032
1116
  if (groups.length === 0) return void 0;
1033
- const family = classifyModelFamily(modelId);
1034
- if (family === "claude" || family === "gpt") {
1117
+ const profile = getModelProfile(modelId);
1118
+ if (profile.quotaPoolKind === "3p") {
1035
1119
  const found = groups.find((g) => !isGeminiQuotaGroup(g));
1036
1120
  if (found) return found;
1037
1121
  }
@@ -1237,17 +1321,21 @@ var QuotaStatusCoordinator = class {
1237
1321
  lastPairs;
1238
1322
  store;
1239
1323
  fetchSummaryOverride;
1324
+ client;
1240
1325
  nowFn;
1241
1326
  constructor(depsOrStoreOrFile) {
1242
1327
  if (typeof depsOrStoreOrFile === "string" || !depsOrStoreOrFile) {
1243
1328
  this.store = fileQuotaStatusStore(depsOrStoreOrFile || defaultConfigFile());
1329
+ this.client = defaultAntigravityClient;
1244
1330
  this.nowFn = Date.now;
1245
1331
  } else if ("loadMode" in depsOrStoreOrFile) {
1246
1332
  this.store = depsOrStoreOrFile;
1333
+ this.client = defaultAntigravityClient;
1247
1334
  this.nowFn = Date.now;
1248
1335
  } else {
1249
1336
  this.store = depsOrStoreOrFile.store ?? fileQuotaStatusStore(depsOrStoreOrFile.configFile || defaultConfigFile());
1250
1337
  this.fetchSummaryOverride = depsOrStoreOrFile.fetchQuotaSummary;
1338
+ this.client = depsOrStoreOrFile.client ?? defaultAntigravityClient;
1251
1339
  this.nowFn = depsOrStoreOrFile.now ?? Date.now;
1252
1340
  }
1253
1341
  this.weeklyTo5hRatio = DEFAULT_WEEKLY_TO_5H_RATIO;
@@ -1260,6 +1348,26 @@ var QuotaStatusCoordinator = class {
1260
1348
  mode() {
1261
1349
  return this.store.loadMode();
1262
1350
  }
1351
+ bind(pi) {
1352
+ if (typeof pi?.on !== "function") return;
1353
+ pi.on("session_start", async (_event, ctx) => {
1354
+ await this.refreshAndPaint(ctx);
1355
+ });
1356
+ pi.on("model_select", async (event, ctx) => {
1357
+ const modelCtx = {
1358
+ ui: ctx.ui,
1359
+ modelRegistry: ctx.modelRegistry,
1360
+ model: event.model || ctx.model
1361
+ };
1362
+ await this.refreshAndPaint(modelCtx);
1363
+ });
1364
+ pi.on("agent_settled", async (_event, ctx) => {
1365
+ await this.refreshAndPaint(ctx);
1366
+ });
1367
+ }
1368
+ formatUsage(ctx) {
1369
+ return this.inspectUsage(ctx);
1370
+ }
1263
1371
  footerFor(modelId, mode = "smart") {
1264
1372
  return FOOTER_MODES[mode]?.render(this.summary, modelId, this.weeklyTo5hRatio, void 0, this.nowFn).plain;
1265
1373
  }
@@ -1328,18 +1436,13 @@ var QuotaStatusCoordinator = class {
1328
1436
  }
1329
1437
  async fetch(ctx, signal) {
1330
1438
  try {
1331
- const creds = await resolveCredentials(ctx);
1332
- if (!creds) return void 0;
1333
1439
  let rawSummary;
1334
1440
  if (this.fetchSummaryOverride) {
1441
+ const creds = await resolveCredentials(ctx);
1442
+ if (!creds) return void 0;
1335
1443
  rawSummary = await this.fetchSummaryOverride(creds.token, signal);
1336
1444
  } else {
1337
- rawSummary = await postAntigravityJson({
1338
- auth: creds,
1339
- path: "v1internal:retrieveUserQuotaSummary",
1340
- body: { project: creds.projectId },
1341
- signal
1342
- });
1445
+ rawSummary = await this.client.retrieveQuotaSummary(ctx, signal);
1343
1446
  }
1344
1447
  if (!rawSummary) return void 0;
1345
1448
  const summary = rawSummary.groups ? parseQuotaSummary(rawSummary) : rawSummary;
@@ -1516,8 +1619,9 @@ function convertTools(tools) {
1516
1619
  function translateTurnTrace(conversation, runtimeModelId) {
1517
1620
  const contents = [];
1518
1621
  const messages = conversation || [];
1519
- const isGeminiRequest = classifyModelFamily(runtimeModelId) === "gemini";
1520
- const isGptRequest = classifyModelFamily(runtimeModelId) === "gpt";
1622
+ const runtimeProfile = getModelProfile(runtimeModelId);
1623
+ const isGeminiRequest = runtimeProfile.isGemini;
1624
+ const isGptRequest = runtimeProfile.isGpt;
1521
1625
  for (const msg of messages) {
1522
1626
  if (msg.role === "user") {
1523
1627
  const parts = [];
@@ -1545,7 +1649,7 @@ function translateTurnTrace(conversation, runtimeModelId) {
1545
1649
  continue;
1546
1650
  }
1547
1651
  const isSameProvider = !msg.provider || msg.provider === PROVIDER_ID;
1548
- const isSameModel = isCompatibleFamily(msg.model, runtimeModelId);
1652
+ const isSameModel = runtimeProfile.isReplayCompatible(msg.model);
1549
1653
  const isSameProviderAndModel = isSameProvider && isSameModel;
1550
1654
  const parts = [];
1551
1655
  let pendingThinkingSig;
@@ -1984,7 +2088,7 @@ async function consumeAntigravityStream(streamBody, output, stream, model, signa
1984
2088
  reader.releaseLock();
1985
2089
  }
1986
2090
  }
1987
- function streamAntigravity(model, context, options, catalog) {
2091
+ function streamAntigravity(model, context, options, catalog, client = defaultAntigravityClient) {
1988
2092
  const stream = createAssistantMessageEventStream();
1989
2093
  (async () => {
1990
2094
  const output = {
@@ -2038,10 +2142,9 @@ function streamAntigravity(model, context, options, catalog) {
2038
2142
  if (typeof v === "string") mergedHeaders[k] = v;
2039
2143
  }
2040
2144
  }
2041
- const { response, stream: streamBody } = await postAntigravityStream({
2042
- auth: token,
2145
+ const { response, stream: streamBody } = await client.streamGenerateContent({
2146
+ source: options,
2043
2147
  endpoint,
2044
- path: "v1internal:streamGenerateContent?alt=sse",
2045
2148
  headers: mergedHeaders,
2046
2149
  body: requestBody,
2047
2150
  signal: options?.signal
@@ -2140,7 +2243,7 @@ ${sourceLines.join("\n")}`;
2140
2243
  completedAt
2141
2244
  };
2142
2245
  }
2143
- async function performWebSearch(creds, options) {
2246
+ async function performWebSearch(source, options, client = defaultAntigravityClient) {
2144
2247
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
2145
2248
  let effectiveQuery = options.query.trim();
2146
2249
  if (options.domain && options.domain.trim()) {
@@ -2150,7 +2253,6 @@ async function performWebSearch(creds, options) {
2150
2253
  }
2151
2254
  }
2152
2255
  const reqBody = {
2153
- project: creds.projectId || FALLBACK_PROJECT_ID,
2154
2256
  request: {
2155
2257
  contents: [
2156
2258
  {
@@ -2181,12 +2283,12 @@ async function performWebSearch(creds, options) {
2181
2283
  userAgent: "antigravity",
2182
2284
  requestType: "web_search"
2183
2285
  };
2184
- const resp = await postAntigravityJson({
2185
- auth: creds.token,
2186
- path: "v1internal:generateContent",
2187
- body: reqBody,
2188
- signal: options.signal
2189
- });
2286
+ const resp = await client.postJson(
2287
+ source,
2288
+ "v1internal:generateContent",
2289
+ reqBody,
2290
+ options.signal
2291
+ );
2190
2292
  const completedAt = (/* @__PURE__ */ new Date()).toISOString();
2191
2293
  const candidate = resp.response?.candidates?.[0];
2192
2294
  const rawText = candidate?.content?.parts?.filter((p) => typeof p.text === "string").map((p) => p.text).join("") || "";
@@ -2220,15 +2322,8 @@ function registerWebSearchTool(pi) {
2220
2322
  )
2221
2323
  }),
2222
2324
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
2223
- const creds = await resolveCredentials(ctx);
2224
- if (!creds) {
2225
- return {
2226
- content: [{ type: "text", text: `Error: ${NOT_LOGGED_IN}` }],
2227
- details: { error: "not_logged_in" }
2228
- };
2229
- }
2230
2325
  try {
2231
- const result = await performWebSearch(creds, {
2326
+ const result = await performWebSearch(ctx, {
2232
2327
  query: params.query,
2233
2328
  domain: params.domain,
2234
2329
  signal
@@ -2241,6 +2336,12 @@ function registerWebSearchTool(pi) {
2241
2336
  }
2242
2337
  };
2243
2338
  } catch (err) {
2339
+ if (err instanceof AntigravityAuthError) {
2340
+ return {
2341
+ content: [{ type: "text", text: `Error: ${NOT_LOGGED_IN}` }],
2342
+ details: { error: "not_logged_in" }
2343
+ };
2344
+ }
2244
2345
  return {
2245
2346
  content: [{ type: "text", text: `Search failed: ${err.message}` }],
2246
2347
  details: { error: err.message }
@@ -2250,11 +2351,6 @@ function registerWebSearchTool(pi) {
2250
2351
  });
2251
2352
  }
2252
2353
  async function executeWebSearchCommand(ctx, query) {
2253
- const creds = await resolveCredentials(ctx);
2254
- if (!creds) {
2255
- notifyUser(ctx, NOT_LOGGED_IN, "warning");
2256
- return;
2257
- }
2258
2354
  const trimmedQuery = (query || "").trim();
2259
2355
  if (!trimmedQuery) {
2260
2356
  notifyUser(ctx, "Usage: /antigravity websearch <query>", "warning");
@@ -2262,12 +2358,16 @@ async function executeWebSearchCommand(ctx, query) {
2262
2358
  }
2263
2359
  try {
2264
2360
  if (ctx.hasUI) ctx.ui.notify(`Searching: "${trimmedQuery}"\u2026`, "info");
2265
- const result = await performWebSearch(creds, {
2361
+ const result = await performWebSearch(ctx, {
2266
2362
  query: trimmedQuery,
2267
2363
  signal: ctx.signal
2268
2364
  });
2269
2365
  notifyUser(ctx, result.formattedOutput);
2270
2366
  } catch (err) {
2367
+ if (err instanceof AntigravityAuthError) {
2368
+ notifyUser(ctx, NOT_LOGGED_IN, "warning");
2369
+ return;
2370
+ }
2271
2371
  notifyUser(ctx, `Search failed: ${err.message}`, "error");
2272
2372
  }
2273
2373
  }
@@ -2386,7 +2486,7 @@ async function runUsageSubcommand(ctx, quotaStatus) {
2386
2486
  }
2387
2487
  try {
2388
2488
  if (ctx.hasUI) ctx.ui.notify("Fetching quota summary\u2026", "info");
2389
- const text = await quotaStatus.inspectUsage(ctx);
2489
+ const text = await quotaStatus.formatUsage(ctx);
2390
2490
  emitOutput(ctx, text);
2391
2491
  } catch (err) {
2392
2492
  emitOutput(ctx, `Failed to fetch usage: ${err.message}`, "error");
@@ -2417,24 +2517,36 @@ function runLoginSubcommand(ctx) {
2417
2517
  emitOutput(ctx, "Please run /login antigravity to authenticate.");
2418
2518
  }
2419
2519
  }
2420
- async function runAntigravitySubcommand(args, ctx, quotaStatus, catalog) {
2421
- const raw = (args || "").trim();
2422
- const parts = raw.split(/\s+/).filter(Boolean);
2423
- const sub = parseAntigravitySubcommand(parts[0] || "");
2424
- const subArgs = raw.slice(parts[0]?.length || 0).trim();
2425
- const cmd = SUBCOMMANDS.find((c) => c.name === sub);
2426
- if (cmd) {
2427
- await cmd.run({ ctx, quotaStatus, catalog, subArgs });
2428
- return;
2429
- }
2430
- emitOutput(ctx, USAGE_TEXT);
2520
+ function createAntigravityCommands(deps) {
2521
+ return {
2522
+ async handle(args, ctx) {
2523
+ const raw = (args || "").trim();
2524
+ const parts = raw.split(/\s+/).filter(Boolean);
2525
+ const sub = parseAntigravitySubcommand(parts[0] || "");
2526
+ const subArgs = raw.slice(parts[0]?.length || 0).trim();
2527
+ const cmd = SUBCOMMANDS.find((c) => c.name === sub);
2528
+ if (cmd) {
2529
+ await cmd.run({
2530
+ ctx,
2531
+ quotaStatus: deps.quotaStatus,
2532
+ catalog: deps.catalog,
2533
+ subArgs
2534
+ });
2535
+ return;
2536
+ }
2537
+ emitOutput(ctx, USAGE_TEXT);
2538
+ },
2539
+ complete(prefix) {
2540
+ return completeSubcommands(prefix);
2541
+ }
2542
+ };
2431
2543
  }
2432
2544
 
2433
2545
  // src/index.ts
2434
- var PROVIDER_NAME = "Antigravity";
2435
2546
  function index_default(pi) {
2436
2547
  const quotaStatus = createQuotaStatus();
2437
2548
  const catalog = createModelCatalog();
2549
+ const commands = createAntigravityCommands({ quotaStatus, catalog });
2438
2550
  pi.registerProvider(PROVIDER_ID, {
2439
2551
  name: PROVIDER_NAME,
2440
2552
  baseUrl: DEFAULT_ENDPOINT,
@@ -2453,26 +2565,13 @@ function index_default(pi) {
2453
2565
  });
2454
2566
  pi.registerCommand("antigravity", {
2455
2567
  description: "Antigravity quota, models and settings",
2456
- getArgumentCompletions: (prefix) => completeSubcommands(prefix),
2568
+ getArgumentCompletions: (prefix) => commands.complete(prefix),
2457
2569
  handler: async (args, ctx) => {
2458
- await runAntigravitySubcommand(args, ctx, quotaStatus, catalog);
2570
+ await commands.handle(args, ctx);
2459
2571
  }
2460
2572
  });
2461
2573
  registerWebSearchTool(pi);
2462
- pi.on("session_start", async (_event, ctx) => {
2463
- await quotaStatus.refreshAndPaint(ctx);
2464
- });
2465
- pi.on("model_select", async (event, ctx) => {
2466
- const modelCtx = {
2467
- ui: ctx.ui,
2468
- modelRegistry: ctx.modelRegistry,
2469
- model: event.model || ctx.model
2470
- };
2471
- await quotaStatus.refreshAndPaint(modelCtx);
2472
- });
2473
- pi.on("agent_settled", async (_event, ctx) => {
2474
- await quotaStatus.refreshAndPaint(ctx);
2475
- });
2574
+ quotaStatus.bind(pi);
2476
2575
  }
2477
2576
  export {
2478
2577
  PROVIDER_ID,