@eddyskywalker/dsh-chatgpt-subscription 0.1.37 → 0.1.38

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.
Files changed (46) hide show
  1. package/bin/antigravity-login.mjs +36 -0
  2. package/lib/client.js +1256 -53
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +1896 -20
  5. package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts +11 -0
  6. package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts.map +1 -0
  7. package/lib/types/client/antigravity/AntigravitySection.d.ts +10 -0
  8. package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -0
  9. package/lib/types/client/antigravity/locales.d.ts +298 -0
  10. package/lib/types/client/antigravity/locales.d.ts.map +1 -0
  11. package/lib/types/client/antigravity/styles.d.ts +3 -0
  12. package/lib/types/client/antigravity/styles.d.ts.map +1 -0
  13. package/lib/types/client/index.d.ts +1 -0
  14. package/lib/types/client/index.d.ts.map +1 -1
  15. package/lib/types/client/locales.d.ts +7 -7
  16. package/lib/types/client/mermaid/renderer.d.ts +3 -0
  17. package/lib/types/client/mermaid/renderer.d.ts.map +1 -0
  18. package/lib/types/client/mermaid/sanitize.d.ts +2 -0
  19. package/lib/types/client/mermaid/sanitize.d.ts.map +1 -0
  20. package/lib/types/client/mermaid/styles.d.ts +3 -0
  21. package/lib/types/client/mermaid/styles.d.ts.map +1 -0
  22. package/lib/types/host/antigravity/adapter.d.ts +15 -0
  23. package/lib/types/host/antigravity/adapter.d.ts.map +1 -0
  24. package/lib/types/host/antigravity/client.d.ts +21 -0
  25. package/lib/types/host/antigravity/client.d.ts.map +1 -0
  26. package/lib/types/host/antigravity/mapper.d.ts +28 -0
  27. package/lib/types/host/antigravity/mapper.d.ts.map +1 -0
  28. package/lib/types/host/antigravity/oauth.d.ts +38 -0
  29. package/lib/types/host/antigravity/oauth.d.ts.map +1 -0
  30. package/lib/types/host/antigravity/routes.d.ts +6 -0
  31. package/lib/types/host/antigravity/routes.d.ts.map +1 -0
  32. package/lib/types/host/antigravity/token-store.d.ts +60 -0
  33. package/lib/types/host/antigravity/token-store.d.ts.map +1 -0
  34. package/lib/types/host/antigravity/types.d.ts +46 -0
  35. package/lib/types/host/antigravity/types.d.ts.map +1 -0
  36. package/lib/types/host/common/idle-watchdog.d.ts +8 -0
  37. package/lib/types/host/common/idle-watchdog.d.ts.map +1 -0
  38. package/lib/types/host/responses-client.d.ts.map +1 -1
  39. package/lib/types/host/routes.d.ts.map +1 -1
  40. package/lib/types/index.d.ts +4 -0
  41. package/lib/types/index.d.ts.map +1 -1
  42. package/lib/types/shared/antigravity-contracts.d.ts +72 -0
  43. package/lib/types/shared/antigravity-contracts.d.ts.map +1 -0
  44. package/lib/types/shared/model-catalog.d.ts +7 -0
  45. package/lib/types/shared/model-catalog.d.ts.map +1 -1
  46. package/package.json +34 -29
package/lib/index.js CHANGED
@@ -1,18 +1,20 @@
1
+ import { createRequire } from "node:module";
1
2
  import * as LlmModule from "@deepseek-ai/dsh-llm";
2
3
  import { HarnessError, LlmAdapter, LlmError, ProviderRequestId, ReasoningEffortId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
3
4
  import { WebError } from "@deepseek-ai/dsh-web";
4
5
  import { createUserMessage } from "@deepseek-ai/dsh-llm/message";
5
6
  import { defineTool } from "@deepseek-ai/dsh-tools";
6
7
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
- import http from "node:http";
8
+ import http, { createServer } from "node:http";
8
9
  import { execSync, spawn } from "node:child_process";
9
10
  import { ProxyAgent, fetch as fetch$1 } from "undici";
10
11
  import * as SettingsModule from "@deepseek-ai/dsh-settings";
11
12
  import z from "@deepseek-ai/schemastery";
12
- import { constants } from "node:fs";
13
- import { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
14
- import { homedir } from "node:os";
15
- import { dirname, join } from "node:path";
13
+ import fs, { constants } from "node:fs";
14
+ import fsPromises, { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
15
+ import os, { homedir } from "node:os";
16
+ import path, { dirname, join } from "node:path";
17
+ import { URL as URL$1, URLSearchParams as URLSearchParams$1 } from "node:url";
16
18
  //#region src/compat.ts
17
19
  /**
18
20
  * Compatibility constants for the ChatGPT-backed Codex flow. The backend and
@@ -55,7 +57,8 @@ const CODEX_MODEL_CATALOG = [
55
57
  inputModalities: ["text", "image"],
56
58
  defaultReasoningEffort: "medium",
57
59
  reasoningProfile: "gpt-5.6",
58
- supportsReasoningSummary: true
60
+ supportsReasoningSummary: true,
61
+ fallbackModelId: "gpt-5.6-terra"
59
62
  },
60
63
  {
61
64
  id: "gpt-5.6-terra",
@@ -64,7 +67,8 @@ const CODEX_MODEL_CATALOG = [
64
67
  inputModalities: ["text", "image"],
65
68
  defaultReasoningEffort: "medium",
66
69
  reasoningProfile: "gpt-5.6",
67
- supportsReasoningSummary: true
70
+ supportsReasoningSummary: true,
71
+ fallbackModelId: "gpt-5.5"
68
72
  },
69
73
  {
70
74
  id: "gpt-5.6-luna",
@@ -73,7 +77,8 @@ const CODEX_MODEL_CATALOG = [
73
77
  inputModalities: ["text", "image"],
74
78
  defaultReasoningEffort: "medium",
75
79
  reasoningProfile: "gpt-5.6",
76
- supportsReasoningSummary: true
80
+ supportsReasoningSummary: true,
81
+ fallbackModelId: "gpt-5.5"
77
82
  },
78
83
  {
79
84
  id: "gpt-5.5",
@@ -91,7 +96,8 @@ const CODEX_MODEL_CATALOG = [
91
96
  inputModalities: ["text", "image"],
92
97
  defaultReasoningEffort: "none",
93
98
  reasoningProfile: "standard",
94
- supportsReasoningSummary: true
99
+ supportsReasoningSummary: true,
100
+ fallbackModelId: "gpt-5.4-mini"
95
101
  },
96
102
  {
97
103
  id: "gpt-5.4-mini",
@@ -149,14 +155,19 @@ function codexModelSupportsImageInput(model) {
149
155
  function codexModelSupportsReasoningSummary(model) {
150
156
  return resolveCodexCatalogEntry(model).supportsReasoningSummary;
151
157
  }
158
+ function resolveCodexFallbackModel(model) {
159
+ const entry = resolveCodexCatalogEntry(model);
160
+ if (!entry.fallbackModelId) return void 0;
161
+ return CODEX_MODEL_CATALOG.find((cand) => cand.id === entry.fallbackModelId);
162
+ }
152
163
  //#endregion
153
164
  //#region src/host/model-catalog.ts
154
- const PROVIDER_ID = CODEX_CHATGPT_PROVIDER_ID;
155
- const PROVIDER_NAME = "Codex(ChatGPT 订阅)";
165
+ const PROVIDER_ID$1 = CODEX_CHATGPT_PROVIDER_ID;
166
+ const PROVIDER_NAME$1 = "Codex(ChatGPT 订阅)";
156
167
  function listCodexModels(preferences) {
157
168
  const visible = new Set(preferences?.status().visibleModelIds ?? CODEX_MODEL_CATALOG.map((entry) => entry.id));
158
169
  return CODEX_MODEL_CATALOG.filter((entry) => visible.has(entry.id)).map((entry) => ({
159
- provider: PROVIDER_ID,
170
+ provider: PROVIDER_ID$1,
160
171
  id: entry.id,
161
172
  name: entry.name,
162
173
  inputModalities: [...entry.inputModalities]
@@ -168,7 +179,7 @@ function resolveCodexModel(model, preferences) {
168
179
  const configuredContextWindow = isConfigurableContextModelId(model) ? status?.contextWindowOverrides[model] : void 0;
169
180
  const efforts = reasoningEffortsForModel(model);
170
181
  return {
171
- provider: PROVIDER_ID,
182
+ provider: PROVIDER_ID$1,
172
183
  id: model,
173
184
  name: entry.id === model ? entry.name : model,
174
185
  inputModalities: [...entry.inputModalities],
@@ -213,7 +224,7 @@ var CodexChatGptAdapter = class extends LlmAdapter {
213
224
  providerInfo(provider) {
214
225
  return {
215
226
  id: provider,
216
- name: PROVIDER_NAME
227
+ name: PROVIDER_NAME$1
217
228
  };
218
229
  }
219
230
  providerRetryPolicy() {
@@ -1416,6 +1427,137 @@ function withWritable(value) {
1416
1427
  };
1417
1428
  }
1418
1429
  //#endregion
1430
+ //#region node_modules/@deepseek-ai/dsh-timeout/lib/index.js
1431
+ /**
1432
+ * Shared timeout arithmetic, signal fusion, and classification. The library
1433
+ * only notifies through abort signals; each capability still owns the mechanism
1434
+ * that stops its work and translates timeout reasons into public outcomes.
1435
+ * @module @deepseek-ai/dsh-timeout
1436
+ */
1437
+ /**
1438
+ * Internal abort reason carrying a capability-owned code and elapsed deadline.
1439
+ * Providers translate it through {@link timeoutOf} before returning to callers.
1440
+ */
1441
+ var TimeoutReason = class extends Error {
1442
+ code;
1443
+ timeoutMs;
1444
+ name = "TimeoutReason";
1445
+ /**
1446
+ * @param code Capability-owned timeout code (e.g. `BASH_TIMEOUT`).
1447
+ * @param timeoutMs The deadline that elapsed, in milliseconds.
1448
+ */
1449
+ constructor(code, timeoutMs) {
1450
+ super(`${code} after ${timeoutMs}ms`);
1451
+ this.code = code;
1452
+ this.timeoutMs = timeoutMs;
1453
+ }
1454
+ };
1455
+ /** Largest delay Node schedules without clamping it to one millisecond. */
1456
+ const MAX_TIMER_DELAY_MS = 2147483647;
1457
+ function assertTimerDelay(timeoutMs, name) {
1458
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) throw new Error(`${name} must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);
1459
+ }
1460
+ /**
1461
+ * Create a rearmable idle watchdog for an async iterator. The timer exists only
1462
+ * while {@link IdleWatchdog.next} is outstanding, so consumer think time does
1463
+ * not count as provider idle time. The returned signal is stable for the whole
1464
+ * call and only notifies; the iterator must observe it to terminate its work.
1465
+ *
1466
+ * @param upstream - caller cancellation fused into the stable signal.
1467
+ * @param timeoutMs - positive finite idle interval in milliseconds.
1468
+ * @param code - capability-owned code carried by the timeout reason.
1469
+ * @returns a stable signal, guarded next operation, and timer disposer.
1470
+ */
1471
+ function idleWatchdog(upstream, timeoutMs, code) {
1472
+ assertTimerDelay(timeoutMs, "idleWatchdog timeoutMs");
1473
+ const timeout = new AbortController();
1474
+ const signal = upstream === void 0 ? timeout.signal : AbortSignal.any([upstream, timeout.signal]);
1475
+ let timer;
1476
+ let outstanding = false;
1477
+ let disposed = false;
1478
+ const arm = () => {
1479
+ if (timer !== void 0) clearTimeout(timer);
1480
+ timer = setTimeout(() => {
1481
+ timeout.abort(new TimeoutReason(code, timeoutMs));
1482
+ }, timeoutMs);
1483
+ };
1484
+ return {
1485
+ signal,
1486
+ async next(iterator) {
1487
+ if (disposed) throw new Error("idleWatchdog is disposed");
1488
+ if (outstanding) throw new Error("idleWatchdog next is already outstanding");
1489
+ outstanding = true;
1490
+ arm();
1491
+ try {
1492
+ return await iterator.next();
1493
+ } finally {
1494
+ clearTimeout(timer);
1495
+ timer = void 0;
1496
+ outstanding = false;
1497
+ }
1498
+ },
1499
+ pulse() {
1500
+ if (disposed || !outstanding) return;
1501
+ arm();
1502
+ },
1503
+ [Symbol.dispose]() {
1504
+ if (disposed) return;
1505
+ disposed = true;
1506
+ if (timer !== void 0) clearTimeout(timer);
1507
+ timer = void 0;
1508
+ }
1509
+ };
1510
+ }
1511
+ /**
1512
+ * Recover a timeout reason from a reason-bearing object. Supplying `code`
1513
+ * distinguishes this deadline from a nested upstream deadline; a foreign code
1514
+ * follows the ordinary cancellation path.
1515
+ *
1516
+ * @param x An {@link AbortSignal} or any `{ reason }` carrier (e.g. a caught abort error).
1517
+ * @param code When provided, only a {@link TimeoutReason} with this exact `code` matches.
1518
+ * @returns The matching {@link TimeoutReason}, else `undefined`.
1519
+ */
1520
+ function timeoutOf(x, code) {
1521
+ const reason = x.reason;
1522
+ if (!(reason instanceof TimeoutReason)) return void 0;
1523
+ return code === void 0 || reason.code === code ? reason : void 0;
1524
+ }
1525
+ //#endregion
1526
+ //#region src/host/common/idle-watchdog.ts
1527
+ const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 3e5;
1528
+ const STREAM_IDLE_TIMEOUT_CODE$1 = "LLM_STREAM_IDLE_TIMEOUT";
1529
+ /**
1530
+ * 为异步流包裹可复位的空闲超时看门狗。
1531
+ * 当流式 chunk 产出之间的间隔超过指定阈值时,主动终止并抛出带有 TIMEOUT 的 LlmError。
1532
+ */
1533
+ async function* wrapStreamWithWatchdog(source, upstreamSignal, timeoutMs = DEFAULT_STREAM_IDLE_TIMEOUT_MS, timeoutCode = STREAM_IDLE_TIMEOUT_CODE$1, providerTag = "llm") {
1534
+ const consumer = new AbortController();
1535
+ const watchdog = idleWatchdog(upstreamSignal === void 0 ? consumer.signal : AbortSignal.any([upstreamSignal, consumer.signal]), timeoutMs, timeoutCode);
1536
+ const iterator = source(watchdog.signal)[Symbol.asyncIterator]();
1537
+ let exhausted = false;
1538
+ try {
1539
+ while (true) {
1540
+ const result = await watchdog.next(iterator);
1541
+ if (timeoutOf(watchdog.signal, timeoutCode) !== void 0) throw new LlmError(`${providerTag} stream idle timeout after ${timeoutMs}ms`, "TIMEOUT");
1542
+ if (result.done) {
1543
+ exhausted = true;
1544
+ return;
1545
+ }
1546
+ yield result.value;
1547
+ }
1548
+ } catch (error) {
1549
+ if (timeoutOf(watchdog.signal, timeoutCode) !== void 0) throw new LlmError(`${providerTag} stream idle timeout after ${timeoutMs}ms`, "TIMEOUT", { cause: error });
1550
+ if (upstreamSignal?.aborted) throw new LlmError(`${providerTag} request aborted by caller`, "ABORTED", { cause: error });
1551
+ throw error;
1552
+ } finally {
1553
+ consumer.abort(`${providerTag} stream consumer stopped`);
1554
+ if (!exhausted) try {
1555
+ await iterator.return?.(void 0);
1556
+ } catch {}
1557
+ watchdog[Symbol.dispose]();
1558
+ }
1559
+ }
1560
+ //#endregion
1419
1561
  //#region src/host/responses-mapper.ts
1420
1562
  function hiddenSandboxControlToolNames(options) {
1421
1563
  const retryTools = recentSandboxRetryToolNames(options.messages);
@@ -1854,11 +1996,33 @@ var ResponsesClient = class {
1854
1996
  }
1855
1997
  localRawImages;
1856
1998
  async *stream(options) {
1857
- const payload = await buildResponsesPayload(options, this.attachments, this.localRawImages, this.outputVerbosity(), this.fastMode());
1858
1999
  const hiddenSandboxControls = hiddenSandboxControlToolNames(options);
1859
2000
  const sessionId = stableSessionId(options.sessionId);
2001
+ let currentModel = options.model;
2002
+ let attemptOptions = options;
2003
+ let response;
2004
+ while (true) {
2005
+ const payload = await buildResponsesPayload(attemptOptions, this.attachments, this.localRawImages, this.outputVerbosity(), this.fastMode());
2006
+ try {
2007
+ response = await this.send(payload, sessionId, options.signal);
2008
+ break;
2009
+ } catch (error) {
2010
+ if (error instanceof LlmError && (error.code === "NOT_FOUND" || error.status === 404)) {
2011
+ const fallback = resolveCodexFallbackModel(currentModel);
2012
+ if (fallback && fallback.id !== currentModel) {
2013
+ currentModel = fallback.id;
2014
+ attemptOptions = {
2015
+ ...attemptOptions,
2016
+ model: fallback.id
2017
+ };
2018
+ continue;
2019
+ }
2020
+ }
2021
+ throw error;
2022
+ }
2023
+ }
1860
2024
  try {
1861
- yield* parseResponsesStream(await this.send(payload, sessionId, options.signal), options.signal, hiddenSandboxControls);
2025
+ yield* wrapStreamWithWatchdog((watchdogSignal) => parseResponsesStream(response, watchdogSignal, hiddenSandboxControls), options.signal, 3e5, "LLM_STREAM_IDLE_TIMEOUT", "Codex");
1862
2026
  } finally {
1863
2027
  this.onGenerationFinished();
1864
2028
  }
@@ -2203,6 +2367,7 @@ async function responseError(response) {
2203
2367
  ...response.status === 429 ? { providerRetryAfterMs: retryAfterMs(response.headers) } : {}
2204
2368
  };
2205
2369
  if (response.status === 401) return new LlmError("ChatGPT sign-in has expired. Sign in again.", "AUTH", options);
2370
+ if (response.status === 404) return new LlmError(`Codex model or resource not found (${response.status})${detail ? `: ${detail}` : "."}`, "NOT_FOUND", options);
2206
2371
  if (response.status === 429) return new LlmError("Codex rate limit reached.", "RATE_LIMIT", options);
2207
2372
  if (response.status >= 500) return new LlmError(`Codex service error (${response.status}).`, "SERVER_ERROR", options);
2208
2373
  return new LlmError(`Codex request failed (${response.status})${detail ? `: ${detail}` : "."}`, "PROVIDER_ERROR", options);
@@ -2651,6 +2816,20 @@ function registerRoutes(ctx, oauth, usage, preferences, proxyManager) {
2651
2816
  });
2652
2817
  return;
2653
2818
  }
2819
+ if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/mermaid.min.js`) {
2820
+ try {
2821
+ const mermaidPath = createRequire(import.meta.url).resolve("mermaid/dist/mermaid.min.js");
2822
+ response.writeHead(200, {
2823
+ "Content-Type": "application/javascript; charset=utf-8",
2824
+ "Cache-Control": "public, max-age=86400"
2825
+ });
2826
+ fs.createReadStream(mermaidPath).pipe(response);
2827
+ } catch {
2828
+ response.writeHead(404, { "Content-Type": "text/plain" });
2829
+ response.end("Not found");
2830
+ }
2831
+ return;
2832
+ }
2654
2833
  if (request.method !== "POST") {
2655
2834
  jsonError(response, 405, {
2656
2835
  code: "bad-request",
@@ -2854,7 +3033,7 @@ function field(value, name) {
2854
3033
  const candidate = value[name];
2855
3034
  return typeof candidate === "string" && candidate !== "" ? candidate : null;
2856
3035
  }
2857
- function isRecord(value) {
3036
+ function isRecord$1(value) {
2858
3037
  return typeof value === "object" && value !== null && !Array.isArray(value);
2859
3038
  }
2860
3039
  function readPreferencesUpdate(value, current) {
@@ -2880,7 +3059,7 @@ function readPreferencesUpdate(value, current) {
2880
3059
  patch.searchProvider = value.searchProvider;
2881
3060
  }
2882
3061
  if ("contextWindowOverrides" in value) {
2883
- if (!isRecord(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
3062
+ if (!isRecord$1(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
2884
3063
  const overrides = {};
2885
3064
  for (const [model, contextWindow] of Object.entries(value.contextWindowOverrides)) {
2886
3065
  if (!isConfigurableContextModelId(model)) throw new PreferenceError("Only GPT-5.6 context windows can be changed.");
@@ -3307,6 +3486,1691 @@ function currentConfig(entry) {
3307
3486
  return typeof config === "object" && config !== null && !Array.isArray(config) ? config : {};
3308
3487
  }
3309
3488
  //#endregion
3489
+ //#region src/host/antigravity/types.ts
3490
+ const PROVIDER_NAME = "Antigravity";
3491
+ const PROVIDER_ID = "antigravity";
3492
+ const STREAM_IDLE_TIMEOUT_MS = 3e5;
3493
+ const STREAM_IDLE_TIMEOUT_CODE = "LLM_STREAM_IDLE_TIMEOUT";
3494
+ const DISCOVERY_TIMEOUT_MS = 8e3;
3495
+ const PROJECT_CACHE_TTL_MS = 1800 * 1e3;
3496
+ const OAUTH_CALLBACK_TIMEOUT_MS = 300 * 1e3;
3497
+ const ENDPOINT_FALLBACKS = ["https://cloudcode-pa.googleapis.com", "https://daily-cloudcode-pa.sandbox.googleapis.com"];
3498
+ const REDIRECT_PATH = "/oauth-callback";
3499
+ const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
3500
+ const TOKEN_URL = "https://oauth2.googleapis.com/token";
3501
+ const SCOPES = [
3502
+ "https://www.googleapis.com/auth/aicode",
3503
+ "https://www.googleapis.com/auth/cloud-platform",
3504
+ "https://www.googleapis.com/auth/userinfo.email",
3505
+ "https://www.googleapis.com/auth/userinfo.profile",
3506
+ "https://www.googleapis.com/auth/cclog",
3507
+ "https://www.googleapis.com/auth/experimentsandconfigs"
3508
+ ];
3509
+ const DEFAULT_CLIENT_ID = Buffer.from("MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==", "base64").toString("utf8");
3510
+ const DEFAULT_CLIENT_SECRET = Buffer.from("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=", "base64").toString("utf8");
3511
+ const ANTIGRAVITY_SYSTEM_INSTRUCTION = "You are Antigravity, a powerful agentic AI coding assistant designed by Google DeepMind. You are pair programming with a user to solve coding tasks. Be concise, practical, and tool-aware.";
3512
+ const ANTIGRAVITY_NO_PREAMBLE_INSTRUCTION = "CRITICAL: NEVER output rule checks, formatting guidelines, constraint checklists, or thinking/personality preambles in the final response. Output only the final response.";
3513
+ const GEMINI_ROLE = {
3514
+ user: "user",
3515
+ model: "model"
3516
+ };
3517
+ const TOOL_CALLING_MODE = {
3518
+ none: "NONE",
3519
+ any: "ANY",
3520
+ auto: "AUTO",
3521
+ validated: "VALIDATED"
3522
+ };
3523
+ const ROUTING = {
3524
+ "gemini-3.8-flash": {
3525
+ off: "gemini-3.8-flash-tiered",
3526
+ routing: {
3527
+ minimal: "gemini-3.8-flash-tiered",
3528
+ low: "gemini-3.8-flash-tiered",
3529
+ medium: "gemini-3.8-flash-tiered",
3530
+ high: "gemini-3.8-flash-tiered",
3531
+ xhigh: "gemini-3.8-flash-tiered"
3532
+ },
3533
+ defaultRequestId: "gemini-3.8-flash-tiered",
3534
+ fallbackCandidates: ["gemini-3.7-flash-tiered", "gemini-3.6-flash-low"]
3535
+ },
3536
+ "claude-opus-4-6": {
3537
+ off: "claude-opus-4-6-thinking",
3538
+ routing: {
3539
+ minimal: "claude-opus-4-6-thinking",
3540
+ low: "claude-opus-4-6-thinking",
3541
+ medium: "claude-opus-4-6-thinking",
3542
+ high: "claude-opus-4-6-thinking",
3543
+ xhigh: "claude-opus-4-6-thinking"
3544
+ },
3545
+ defaultRequestId: "claude-opus-4-6-thinking",
3546
+ fallbackCandidates: ["claude-sonnet-4-6"]
3547
+ },
3548
+ "claude-sonnet-4-6": {
3549
+ off: "claude-sonnet-4-6",
3550
+ routing: {
3551
+ minimal: "claude-sonnet-4-6",
3552
+ low: "claude-sonnet-4-6",
3553
+ medium: "claude-sonnet-4-6",
3554
+ high: "claude-sonnet-4-6",
3555
+ xhigh: "claude-sonnet-4-6"
3556
+ },
3557
+ defaultRequestId: "claude-sonnet-4-6"
3558
+ },
3559
+ "gemini-3.7-flash": {
3560
+ off: "gemini-3.7-flash-tiered",
3561
+ routing: {
3562
+ minimal: "gemini-3.7-flash-tiered",
3563
+ low: "gemini-3.7-flash-tiered",
3564
+ medium: "gemini-3.7-flash-tiered",
3565
+ high: "gemini-3.7-flash-tiered",
3566
+ xhigh: "gemini-3.7-flash-tiered"
3567
+ },
3568
+ defaultRequestId: "gemini-3.7-flash-tiered",
3569
+ fallbackCandidates: ["gemini-3.6-flash-low"]
3570
+ },
3571
+ "gemini-3.6-flash": {
3572
+ off: "gemini-3.6-flash-low",
3573
+ routing: {
3574
+ minimal: "gemini-3.6-flash-low",
3575
+ low: "gemini-3.6-flash-low",
3576
+ medium: "gemini-3.6-flash-medium",
3577
+ high: "gemini-3.6-flash-high",
3578
+ xhigh: "gemini-3.6-flash-high"
3579
+ },
3580
+ defaultRequestId: "gemini-3.6-flash-high"
3581
+ },
3582
+ "gemini-3.5-flash": {
3583
+ off: "gemini-3.5-flash-extra-low",
3584
+ routing: {
3585
+ minimal: "gemini-3.5-flash-extra-low",
3586
+ low: "gemini-3.5-flash-low",
3587
+ medium: "gemini-3.5-flash-low",
3588
+ high: "gemini-3-flash-agent",
3589
+ xhigh: "gemini-3-flash-agent"
3590
+ },
3591
+ defaultRequestId: "gemini-3-flash-agent"
3592
+ },
3593
+ "gemini-3.1-pro": {
3594
+ off: "gemini-3.1-pro-low",
3595
+ routing: {
3596
+ minimal: "gemini-3.1-pro-low",
3597
+ low: "gemini-3.1-pro-low",
3598
+ medium: "gemini-pro-agent",
3599
+ high: "gemini-pro-agent",
3600
+ xhigh: "gemini-pro-agent"
3601
+ },
3602
+ defaultRequestId: "gemini-pro-agent"
3603
+ },
3604
+ "gemini-3.1-flash-image": {
3605
+ off: "gemini-3.1-flash-image",
3606
+ routing: {
3607
+ minimal: "gemini-3.1-flash-image",
3608
+ low: "gemini-3.1-flash-image",
3609
+ medium: "gemini-3.1-flash-image",
3610
+ high: "gemini-3.1-flash-image",
3611
+ xhigh: "gemini-3.1-flash-image"
3612
+ },
3613
+ defaultRequestId: "gemini-3.1-flash-image"
3614
+ },
3615
+ "gemini-3-flash": {
3616
+ off: "gemini-3-flash",
3617
+ routing: {
3618
+ minimal: "gemini-3-flash",
3619
+ low: "gemini-3-flash",
3620
+ medium: "gemini-3-flash",
3621
+ high: "gemini-3-flash",
3622
+ xhigh: "gemini-3-flash"
3623
+ },
3624
+ defaultRequestId: "gemini-3-flash"
3625
+ },
3626
+ "gemini-2.5-pro": {
3627
+ off: "gemini-2.5-pro",
3628
+ routing: {
3629
+ minimal: "gemini-2.5-pro",
3630
+ low: "gemini-2.5-pro",
3631
+ medium: "gemini-2.5-pro",
3632
+ high: "gemini-2.5-pro",
3633
+ xhigh: "gemini-2.5-pro"
3634
+ },
3635
+ defaultRequestId: "gemini-2.5-pro"
3636
+ },
3637
+ "gemini-2.5-flash": {
3638
+ off: "gemini-2.5-flash",
3639
+ routing: {
3640
+ minimal: "gemini-2.5-flash",
3641
+ low: "gemini-2.5-flash",
3642
+ medium: "gemini-2.5-flash",
3643
+ high: "gemini-2.5-flash",
3644
+ xhigh: "gemini-2.5-flash"
3645
+ },
3646
+ defaultRequestId: "gemini-2.5-flash"
3647
+ },
3648
+ "gpt-oss-120b": {
3649
+ off: "gpt-oss-120b-medium",
3650
+ routing: {
3651
+ minimal: "gpt-oss-120b-medium",
3652
+ low: "gpt-oss-120b-medium",
3653
+ medium: "gpt-oss-120b-medium",
3654
+ high: "gpt-oss-120b-medium",
3655
+ xhigh: "gpt-oss-120b-medium"
3656
+ },
3657
+ defaultRequestId: "gpt-oss-120b-medium"
3658
+ }
3659
+ };
3660
+ const RUNTIME_MAX_OUTPUT_TOKENS = {
3661
+ "gemini-3.8-flash": 65536,
3662
+ "gemini-3.8-flash-tiered": 65536,
3663
+ "gemini-3.7-flash": 65536,
3664
+ "gemini-3.7-flash-tiered": 65536,
3665
+ "gemini-3.7-flash-low": 65536,
3666
+ "gemini-3.7-flash-medium": 65536,
3667
+ "gemini-3.7-flash-high": 65536,
3668
+ "gemini-3.6-flash": 65536,
3669
+ "gemini-3.6-flash-low": 65536,
3670
+ "gemini-3.6-flash-medium": 65536,
3671
+ "gemini-3.6-flash-high": 65536,
3672
+ "gemini-3.5-flash": 65536,
3673
+ "gemini-3.5-flash-extra-low": 65536,
3674
+ "gemini-3.5-flash-low": 65536,
3675
+ "gemini-3-flash-agent": 65536,
3676
+ "gemini-3.1-pro": 65535,
3677
+ "gemini-3.1-pro-low": 65535,
3678
+ "gemini-3.1-pro-high": 65535,
3679
+ "gemini-pro-agent": 65535,
3680
+ "claude-opus-4-6": 64e3,
3681
+ "claude-opus-4-6-thinking": 64e3,
3682
+ "claude-sonnet-4-6": 64e3,
3683
+ "gpt-oss-120b": 32768,
3684
+ "gpt-oss-120b-medium": 32768
3685
+ };
3686
+ const MODELS = [
3687
+ {
3688
+ id: "gemini-3.8-flash",
3689
+ name: "Gemini 3.8 Flash",
3690
+ inputModalities: ["text", "image"],
3691
+ contextWindow: 1048576,
3692
+ maxTokens: 65536,
3693
+ reasoningEfforts: [
3694
+ "low",
3695
+ "medium",
3696
+ "high"
3697
+ ]
3698
+ },
3699
+ {
3700
+ id: "gemini-3.7-flash",
3701
+ name: "Gemini 3.7 Flash",
3702
+ inputModalities: ["text", "image"],
3703
+ contextWindow: 1048576,
3704
+ maxTokens: 65536,
3705
+ reasoningEfforts: [
3706
+ "low",
3707
+ "medium",
3708
+ "high"
3709
+ ]
3710
+ },
3711
+ {
3712
+ id: "gemini-3.6-flash",
3713
+ name: "Gemini 3.6 Flash",
3714
+ inputModalities: ["text", "image"],
3715
+ contextWindow: 1048576,
3716
+ maxTokens: 65536,
3717
+ reasoningEfforts: [
3718
+ "low",
3719
+ "medium",
3720
+ "high"
3721
+ ]
3722
+ },
3723
+ {
3724
+ id: "gemini-3.5-flash",
3725
+ name: "Gemini 3.5 Flash",
3726
+ inputModalities: ["text", "image"],
3727
+ contextWindow: 1048576,
3728
+ maxTokens: 65536,
3729
+ reasoningEfforts: [
3730
+ "low",
3731
+ "medium",
3732
+ "high"
3733
+ ]
3734
+ },
3735
+ {
3736
+ id: "gemini-3.1-pro",
3737
+ name: "Gemini 3.1 Pro",
3738
+ inputModalities: ["text", "image"],
3739
+ contextWindow: 1048576,
3740
+ maxTokens: 65535,
3741
+ reasoningEfforts: ["low", "high"]
3742
+ },
3743
+ {
3744
+ id: "gemini-3.1-flash-image",
3745
+ name: "Gemini 3.1 Flash Image",
3746
+ inputModalities: ["text", "image"],
3747
+ contextWindow: 1048576,
3748
+ maxTokens: 8192
3749
+ },
3750
+ {
3751
+ id: "gemini-3-flash",
3752
+ name: "Gemini 3 Flash",
3753
+ inputModalities: ["text", "image"],
3754
+ contextWindow: 1048576,
3755
+ maxTokens: 65536
3756
+ },
3757
+ {
3758
+ id: "gemini-2.5-pro",
3759
+ name: "Gemini 2.5 Pro",
3760
+ inputModalities: ["text", "image"],
3761
+ contextWindow: 1048576,
3762
+ maxTokens: 65535
3763
+ },
3764
+ {
3765
+ id: "gemini-2.5-flash",
3766
+ name: "Gemini 2.5 Flash",
3767
+ inputModalities: ["text", "image"],
3768
+ contextWindow: 1048576,
3769
+ maxTokens: 65536
3770
+ },
3771
+ {
3772
+ id: "claude-opus-4-6",
3773
+ name: "Claude Opus 4.6",
3774
+ inputModalities: ["text", "image"],
3775
+ contextWindow: 1048576,
3776
+ maxTokens: 64e3,
3777
+ reasoningEfforts: [
3778
+ "low",
3779
+ "medium",
3780
+ "high"
3781
+ ]
3782
+ },
3783
+ {
3784
+ id: "claude-sonnet-4-6",
3785
+ name: "Claude Sonnet 4.6",
3786
+ inputModalities: ["text", "image"],
3787
+ contextWindow: 1048576,
3788
+ maxTokens: 64e3,
3789
+ reasoningEfforts: [
3790
+ "low",
3791
+ "medium",
3792
+ "high"
3793
+ ]
3794
+ },
3795
+ {
3796
+ id: "gpt-oss-120b",
3797
+ name: "GPT-OSS 120B",
3798
+ inputModalities: ["text"],
3799
+ contextWindow: 262144,
3800
+ maxTokens: 32768,
3801
+ reasoningEfforts: [
3802
+ "low",
3803
+ "medium",
3804
+ "high"
3805
+ ]
3806
+ }
3807
+ ];
3808
+ //#endregion
3809
+ //#region src/host/antigravity/token-store.ts
3810
+ const ANTIGRAVITY_PREFERENCES_NAMESPACE = "dsh-antigravity";
3811
+ function registerAntigravityPreferenceStore(settings, fallbackStore = new FileModelSettingsStore()) {
3812
+ if (!settings) return {
3813
+ status: () => ({
3814
+ enabledModelIds: MODELS.map((m) => m.id),
3815
+ catalogModels: [],
3816
+ contextWindowOverrides: {},
3817
+ defaultReasoningEffort: null
3818
+ }),
3819
+ update: async (patch) => fallbackStore.updateSettings(patch)
3820
+ };
3821
+ const ns = SettingsModule.settingsNamespace ? SettingsModule.settingsNamespace(ANTIGRAVITY_PREFERENCES_NAMESPACE) : ANTIGRAVITY_PREFERENCES_NAMESPACE;
3822
+ const scope = settings.register.call(settings, ns, z.object({
3823
+ enabledModelIds: z.array(z.string()).default(MODELS.map((m) => m.id)),
3824
+ contextWindowOverrides: z.dict(z.number()).default({}),
3825
+ defaultReasoningEffort: z.union([
3826
+ z.const("low"),
3827
+ z.const("medium"),
3828
+ z.const("high"),
3829
+ z.const(null)
3830
+ ]).default(null)
3831
+ }));
3832
+ return {
3833
+ status: () => {
3834
+ const val = scope.get();
3835
+ return {
3836
+ enabledModelIds: val.enabledModelIds,
3837
+ catalogModels: [],
3838
+ contextWindowOverrides: val.contextWindowOverrides,
3839
+ defaultReasoningEffort: val.defaultReasoningEffort
3840
+ };
3841
+ },
3842
+ update: async (patch) => {
3843
+ const current = scope.get();
3844
+ const normalized = {
3845
+ enabledModelIds: patch.enabledModelIds ?? current.enabledModelIds,
3846
+ contextWindowOverrides: patch.contextWindowOverrides ? {
3847
+ ...current.contextWindowOverrides,
3848
+ ...patch.contextWindowOverrides
3849
+ } : current.contextWindowOverrides,
3850
+ defaultReasoningEffort: patch.defaultReasoningEffort !== void 0 ? patch.defaultReasoningEffort : current.defaultReasoningEffort
3851
+ };
3852
+ await scope.update(normalized);
3853
+ fallbackStore.updateSettings(patch).catch(() => void 0);
3854
+ return {
3855
+ ...normalized,
3856
+ catalogModels: []
3857
+ };
3858
+ }
3859
+ };
3860
+ }
3861
+ function dshHomeDir() {
3862
+ return process.env.DSH_HOME?.trim() || path.join(os.homedir(), ".dsh");
3863
+ }
3864
+ function credentialPath() {
3865
+ return path.join(dshHomeDir(), "storages", "antigravity-oauth.json");
3866
+ }
3867
+ function modelSettingsPath() {
3868
+ return path.join(dshHomeDir(), "storages", "antigravity-models.json");
3869
+ }
3870
+ var FileCredentialStore = class {
3871
+ filePath;
3872
+ constructor(filePath = credentialPath()) {
3873
+ this.filePath = filePath;
3874
+ }
3875
+ path() {
3876
+ return this.filePath;
3877
+ }
3878
+ async read() {
3879
+ try {
3880
+ const content = await fsPromises.readFile(this.filePath, "utf8");
3881
+ const parsed = JSON.parse(content);
3882
+ if (typeof parsed === "object" && parsed !== null && ("access_token" in parsed || "access" in parsed)) return parsed;
3883
+ return null;
3884
+ } catch {
3885
+ return null;
3886
+ }
3887
+ }
3888
+ async write(credentials) {
3889
+ await fsPromises.mkdir(path.dirname(this.filePath), { recursive: true });
3890
+ const tmp = `${this.filePath}.tmp.${Date.now()}`;
3891
+ await fsPromises.writeFile(tmp, JSON.stringify(credentials, null, 2), "utf8");
3892
+ await fsPromises.rename(tmp, this.filePath);
3893
+ }
3894
+ async delete() {
3895
+ try {
3896
+ await fsPromises.unlink(this.filePath);
3897
+ } catch (err) {
3898
+ if (err.code !== "ENOENT") throw err;
3899
+ }
3900
+ }
3901
+ };
3902
+ var FileModelSettingsStore = class {
3903
+ filePath;
3904
+ constructor(filePath = modelSettingsPath()) {
3905
+ this.filePath = filePath;
3906
+ }
3907
+ path() {
3908
+ return this.filePath;
3909
+ }
3910
+ async read() {
3911
+ try {
3912
+ const content = await fsPromises.readFile(this.filePath, "utf8");
3913
+ const parsed = JSON.parse(content);
3914
+ if (typeof parsed === "object" && parsed !== null) {
3915
+ const record = parsed;
3916
+ return {
3917
+ enabledModelIds: Array.isArray(record.enabledModelIds) ? record.enabledModelIds.filter((id) => typeof id === "string") : MODELS.map((m) => m.id),
3918
+ catalogModels: Array.isArray(record.catalogModels) ? record.catalogModels : [],
3919
+ contextWindowOverrides: typeof record.contextWindowOverrides === "object" && record.contextWindowOverrides !== null ? record.contextWindowOverrides : {},
3920
+ defaultReasoningEffort: record.defaultReasoningEffort === "low" || record.defaultReasoningEffort === "medium" || record.defaultReasoningEffort === "high" ? record.defaultReasoningEffort : null
3921
+ };
3922
+ }
3923
+ } catch {}
3924
+ return {
3925
+ enabledModelIds: MODELS.map((m) => m.id),
3926
+ catalogModels: [],
3927
+ contextWindowOverrides: {},
3928
+ defaultReasoningEffort: null
3929
+ };
3930
+ }
3931
+ async write(settings) {
3932
+ await fsPromises.mkdir(path.dirname(this.filePath), { recursive: true });
3933
+ const tmp = `${this.filePath}.tmp.${Date.now()}`;
3934
+ await fsPromises.writeFile(tmp, JSON.stringify(settings, null, 2), "utf8");
3935
+ await fsPromises.rename(tmp, this.filePath);
3936
+ }
3937
+ async updateSettings(patch) {
3938
+ const current = await this.read();
3939
+ const next = {
3940
+ ...current,
3941
+ ...patch.enabledModelIds !== void 0 ? { enabledModelIds: patch.enabledModelIds } : {},
3942
+ ...patch.contextWindowOverrides !== void 0 ? { contextWindowOverrides: {
3943
+ ...current.contextWindowOverrides || {},
3944
+ ...patch.contextWindowOverrides
3945
+ } } : {},
3946
+ ...patch.defaultReasoningEffort !== void 0 ? { defaultReasoningEffort: patch.defaultReasoningEffort } : {}
3947
+ };
3948
+ await this.write(next);
3949
+ return next;
3950
+ }
3951
+ async setEnabledModelIds(enabledModelIds) {
3952
+ return this.updateSettings({ enabledModelIds });
3953
+ }
3954
+ async setCatalogModels(catalogModels, options) {
3955
+ const current = await this.read();
3956
+ const next = {
3957
+ ...current,
3958
+ enabledModelIds: options?.enabledModelIds ?? current.enabledModelIds,
3959
+ catalogModels
3960
+ };
3961
+ await this.write(next);
3962
+ return next;
3963
+ }
3964
+ };
3965
+ //#endregion
3966
+ //#region src/host/antigravity/client.ts
3967
+ const projectCache = /* @__PURE__ */ new Map();
3968
+ let cachedQuota;
3969
+ const PLATFORM = process.platform === "darwin" ? "MACOS" : process.platform === "win32" ? "WINDOWS" : "LINUX";
3970
+ function defaultUserAgent() {
3971
+ return `antigravity/1.15.8 ${process.platform === "darwin" ? "darwin" : process.platform === "win32" ? "windows" : "linux"}/${process.arch === "x64" ? "amd64" : process.arch}`;
3972
+ }
3973
+ function antigravityHeaders(token) {
3974
+ return {
3975
+ Authorization: `Bearer ${token}`,
3976
+ "Content-Type": "application/json",
3977
+ Accept: "text/event-stream",
3978
+ "User-Agent": process.env.DSH_ANTIGRAVITY_USER_AGENT || defaultUserAgent(),
3979
+ "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
3980
+ "Client-Metadata": JSON.stringify({
3981
+ ideType: "ANTIGRAVITY",
3982
+ platform: PLATFORM,
3983
+ pluginType: "GEMINI"
3984
+ })
3985
+ };
3986
+ }
3987
+ function jsonHeaders(token) {
3988
+ return {
3989
+ ...antigravityHeaders(token),
3990
+ Accept: "application/json"
3991
+ };
3992
+ }
3993
+ function endpointCandidates() {
3994
+ const custom = process.env.DSH_ANTIGRAVITY_ENDPOINT?.trim();
3995
+ if (custom) return [custom];
3996
+ return ENDPOINT_FALLBACKS;
3997
+ }
3998
+ function extractProjectId(data) {
3999
+ if (typeof data !== "object" || data === null) return void 0;
4000
+ const record = data;
4001
+ const direct = record.antigravityProjectId ?? record.projectId ?? record.backendProjectId ?? record.userDefinedCloudaicompanionProject ?? record.cloudaicompanionProject ?? record.project;
4002
+ if (typeof direct === "string" && direct.length > 0) return direct;
4003
+ if (typeof direct === "object" && direct !== null && "id" in direct && typeof direct.id === "string") return direct.id;
4004
+ for (const key of [
4005
+ "projects",
4006
+ "projectIds",
4007
+ "cloudaicompanionProjects"
4008
+ ]) {
4009
+ const list = record[key];
4010
+ if (Array.isArray(list)) for (const item of list) {
4011
+ const nested = extractProjectId(item);
4012
+ if (nested) return nested;
4013
+ if (typeof item === "string" && item.length > 0) return item;
4014
+ }
4015
+ }
4016
+ }
4017
+ async function listCloudAICompanionProjects(token) {
4018
+ for (const endpoint of endpointCandidates()) try {
4019
+ const response = await fetch(`${endpoint}/v1internal:listCloudAICompanionProjects`, {
4020
+ method: "POST",
4021
+ headers: antigravityHeaders(token),
4022
+ body: JSON.stringify({}),
4023
+ signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
4024
+ });
4025
+ if (!response.ok) continue;
4026
+ return extractProjectId(await response.json());
4027
+ } catch {}
4028
+ }
4029
+ async function loadCodeAssist(token) {
4030
+ const cached = projectCache.get(token);
4031
+ if (cached && cached.expiresAt > Date.now()) return cached.projectId;
4032
+ const body = JSON.stringify({ metadata: {
4033
+ ideType: "ANTIGRAVITY",
4034
+ platform: "PLATFORM_UNSPECIFIED",
4035
+ pluginType: "GEMINI"
4036
+ } });
4037
+ for (const endpoint of endpointCandidates()) try {
4038
+ const response = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {
4039
+ method: "POST",
4040
+ headers: antigravityHeaders(token),
4041
+ body,
4042
+ signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
4043
+ });
4044
+ if (!response.ok) continue;
4045
+ const project = extractProjectId(await response.json());
4046
+ if (project) {
4047
+ projectCache.set(token, {
4048
+ projectId: project,
4049
+ expiresAt: Date.now() + PROJECT_CACHE_TTL_MS
4050
+ });
4051
+ return project;
4052
+ }
4053
+ const listProj = await listCloudAICompanionProjects(token);
4054
+ if (listProj) {
4055
+ projectCache.set(token, {
4056
+ projectId: listProj,
4057
+ expiresAt: Date.now() + PROJECT_CACHE_TTL_MS
4058
+ });
4059
+ return listProj;
4060
+ }
4061
+ } catch {}
4062
+ }
4063
+ async function postJson(path, token, body) {
4064
+ for (const endpoint of endpointCandidates()) try {
4065
+ const response = await fetch(`${endpoint}${path}`, {
4066
+ method: "POST",
4067
+ headers: jsonHeaders(token),
4068
+ body: JSON.stringify(body)
4069
+ });
4070
+ if (response.ok) return {
4071
+ endpoint,
4072
+ status: response.status,
4073
+ data: await response.json()
4074
+ };
4075
+ } catch {}
4076
+ throw new Error(`Failed to call Antigravity API ${path}`);
4077
+ }
4078
+ function parseQuotaSummary(data) {
4079
+ const summary = typeof data === "object" && data !== null ? data : {};
4080
+ const rawGroups = Array.isArray(summary.groups) ? summary.groups : [];
4081
+ const groups = [];
4082
+ for (const group of rawGroups) {
4083
+ if (typeof group !== "object" || group === null) continue;
4084
+ const groupRec = group;
4085
+ const buckets = [];
4086
+ const rawBuckets = Array.isArray(groupRec.buckets) ? groupRec.buckets : [];
4087
+ for (const bucket of rawBuckets) {
4088
+ if (typeof bucket !== "object" || bucket === null) continue;
4089
+ const bRec = bucket;
4090
+ const remaining = typeof bRec.remainingFraction === "number" ? Math.max(0, Math.min(1, bRec.remainingFraction)) : 0;
4091
+ buckets.push({
4092
+ bucketId: String(bRec.bucketId || bRec.displayName || "limit"),
4093
+ displayName: String(bRec.displayName || bRec.bucketId || "Limit"),
4094
+ window: typeof bRec.window === "string" ? bRec.window : void 0,
4095
+ resetTime: typeof bRec.resetTime === "string" ? bRec.resetTime : void 0,
4096
+ description: typeof bRec.description === "string" ? bRec.description : void 0,
4097
+ remainingFraction: remaining
4098
+ });
4099
+ }
4100
+ if (buckets.length > 0 || groupRec.displayName) groups.push({
4101
+ displayName: String(groupRec.displayName || "Quota group"),
4102
+ description: typeof groupRec.description === "string" ? groupRec.description : void 0,
4103
+ buckets
4104
+ });
4105
+ }
4106
+ return {
4107
+ groups,
4108
+ description: typeof summary.description === "string" ? summary.description : void 0
4109
+ };
4110
+ }
4111
+ function parseCatalogModels(data) {
4112
+ if (typeof data !== "object" || data === null) return [];
4113
+ const record = data;
4114
+ const rawModels = typeof record.models === "object" && record.models !== null ? record.models : {};
4115
+ const list = [];
4116
+ for (const [modelId, info] of Object.entries(rawModels)) {
4117
+ if (typeof info !== "object" || info === null) continue;
4118
+ const rec = info;
4119
+ if (rec.isInternal || modelId.startsWith("chat_")) continue;
4120
+ list.push({
4121
+ id: modelId,
4122
+ name: typeof rec.displayName === "string" ? rec.displayName : modelId,
4123
+ description: typeof rec.description === "string" ? rec.description : void 0
4124
+ });
4125
+ }
4126
+ return list;
4127
+ }
4128
+ async function fetchAccountQuota(store = new FileCredentialStore(), modelSettings) {
4129
+ const { token, projectId: credentialProjectId } = await ensureApiKey(store);
4130
+ const [assistResult, summaryResult] = await Promise.all([postJson("/v1internal:loadCodeAssist", token, { metadata: {
4131
+ ideType: "ANTIGRAVITY",
4132
+ platform: "PLATFORM_UNSPECIFIED",
4133
+ pluginType: "GEMINI"
4134
+ } }).catch(() => null), postJson("/v1internal:retrieveUserQuotaSummary", token, {}).catch(() => null)]);
4135
+ const discoveredProject = assistResult ? extractProjectId(assistResult.data) : void 0;
4136
+ const projectId = credentialProjectId || discoveredProject || "antigravity-default";
4137
+ const modelsData = (await postJson("/v1internal:fetchAvailableModels", token, { project: projectId }).catch(() => null))?.data;
4138
+ const { groups, description } = summaryResult ? parseQuotaSummary(summaryResult.data) : { groups: [] };
4139
+ const catalogModels = modelsData ? parseCatalogModels(modelsData) : [];
4140
+ const assistData = assistResult?.data || {};
4141
+ const currentTier = assistData.currentTier;
4142
+ const paidTier = assistData.paidTier;
4143
+ const planLabel = paidTier?.name || currentTier?.name || void 0;
4144
+ cachedQuota = {
4145
+ projectId,
4146
+ endpoint: summaryResult?.endpoint || "https://cloudcode-pa.googleapis.com",
4147
+ planLabel,
4148
+ productTier: currentTier,
4149
+ paidTier,
4150
+ groups,
4151
+ groupDescription: description,
4152
+ models: catalogModels.map((m) => ({
4153
+ modelId: m.id,
4154
+ displayName: m.name,
4155
+ description: m.description
4156
+ })),
4157
+ catalogModels,
4158
+ fetchedAt: Date.now()
4159
+ };
4160
+ if (modelSettings && catalogModels.length > 0) {
4161
+ const current = await modelSettings.read();
4162
+ const isFirstTime = current.catalogModels.length === 0 && current.enabledModelIds.length === 0;
4163
+ const catalogIds = new Set(catalogModels.map((m) => m.id));
4164
+ const mergedEnabled = isFirstTime ? catalogModels.map((m) => m.id) : current.enabledModelIds.filter((id) => catalogIds.has(id));
4165
+ await modelSettings.setCatalogModels(catalogModels, { enabledModelIds: mergedEnabled });
4166
+ }
4167
+ return cachedQuota;
4168
+ }
4169
+ function getCachedQuota() {
4170
+ return cachedQuota;
4171
+ }
4172
+ //#endregion
4173
+ //#region src/host/antigravity/oauth.ts
4174
+ let webLoginFlow = { status: "idle" };
4175
+ function antigravityEnv(namePart) {
4176
+ const full = `DSH_ANTIGRAVITY_${namePart}`;
4177
+ return process.env[full];
4178
+ }
4179
+ function callbackPort() {
4180
+ const configured = Number(antigravityEnv("CALLBACK_PORT"));
4181
+ if (Number.isInteger(configured) && configured > 0 && configured <= 65535) return configured;
4182
+ return 51121;
4183
+ }
4184
+ function resolveCallbackHost(raw = antigravityEnv("CALLBACK_HOST")) {
4185
+ const host = (raw || "localhost").trim().toLowerCase();
4186
+ if (host === "localhost" || host === "127.0.0.1" || host === "::1") return host;
4187
+ return "localhost";
4188
+ }
4189
+ function redirectUri() {
4190
+ return `http://${resolveCallbackHost()}:${callbackPort()}${"/oauth-callback".startsWith("/"), REDIRECT_PATH}`;
4191
+ }
4192
+ function clientId() {
4193
+ return antigravityEnv("CLIENT_ID")?.trim() || DEFAULT_CLIENT_ID;
4194
+ }
4195
+ function clientSecret() {
4196
+ return antigravityEnv("CLIENT_SECRET")?.trim() || DEFAULT_CLIENT_SECRET;
4197
+ }
4198
+ function base64Url(buffer) {
4199
+ return buffer.toString("base64url");
4200
+ }
4201
+ function generatePKCE() {
4202
+ const verifier = base64Url(randomBytes(32));
4203
+ return {
4204
+ verifier,
4205
+ challenge: base64Url(createHash("sha256").update(verifier).digest())
4206
+ };
4207
+ }
4208
+ function escapeHtml(text) {
4209
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
4210
+ }
4211
+ function sanitizeOAuthProviderError(text) {
4212
+ return escapeHtml(text.slice(0, 300).replace(/[\r\n\t]+/g, " "));
4213
+ }
4214
+ function openBrowser(url) {
4215
+ try {
4216
+ if (process.platform === "darwin") spawn("open", [url], {
4217
+ stdio: "ignore",
4218
+ detached: true
4219
+ });
4220
+ else if (process.platform === "win32") spawn("cmd", [
4221
+ "/c",
4222
+ "start",
4223
+ "",
4224
+ url
4225
+ ], {
4226
+ stdio: "ignore",
4227
+ detached: true
4228
+ });
4229
+ else spawn("xdg-open", [url], {
4230
+ stdio: "ignore",
4231
+ detached: true
4232
+ });
4233
+ } catch {}
4234
+ }
4235
+ async function getUserEmail(token) {
4236
+ try {
4237
+ const response = await fetch("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", { headers: { Authorization: `Bearer ${token}` } });
4238
+ if (!response.ok) return void 0;
4239
+ const data = await response.json();
4240
+ return typeof data.email === "string" ? data.email : void 0;
4241
+ } catch {
4242
+ return;
4243
+ }
4244
+ }
4245
+ function startCallbackServer(expectedState) {
4246
+ return new Promise((resolve, reject) => {
4247
+ let settled = false;
4248
+ let timeout;
4249
+ let resolveCode;
4250
+ let rejectCode;
4251
+ const codePromise = new Promise((res, rej) => {
4252
+ resolveCode = res;
4253
+ rejectCode = rej;
4254
+ });
4255
+ const finish = (fn) => {
4256
+ if (settled) return;
4257
+ settled = true;
4258
+ if (timeout) clearTimeout(timeout);
4259
+ fn();
4260
+ };
4261
+ const callbackUrl = redirectUri();
4262
+ const server = createServer((request, response) => {
4263
+ if (request.method !== "GET" && request.method !== "HEAD") {
4264
+ response.writeHead(405, { "Content-Type": "text/plain; charset=utf-8" });
4265
+ response.end("Method Not Allowed");
4266
+ return;
4267
+ }
4268
+ const url = new URL$1(request.url || "", callbackUrl);
4269
+ if (url.pathname !== "/oauth-callback") {
4270
+ response.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
4271
+ response.end("Antigravity OAuth callback route not found.");
4272
+ return;
4273
+ }
4274
+ const providerError = url.searchParams.get("error");
4275
+ const code = url.searchParams.get("code");
4276
+ const state = url.searchParams.get("state");
4277
+ if (providerError) {
4278
+ const safe = escapeHtml(providerError.slice(0, 200));
4279
+ response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
4280
+ response.end(`Antigravity authentication failed: ${safe}`);
4281
+ finish(() => rejectCode(/* @__PURE__ */ new Error(`OAuth error: ${providerError.slice(0, 200)}`)));
4282
+ return;
4283
+ }
4284
+ if (!code || !state) {
4285
+ response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
4286
+ response.end("Antigravity authentication failed: missing code or state.");
4287
+ finish(() => rejectCode(/* @__PURE__ */ new Error("Missing code or state in OAuth callback")));
4288
+ return;
4289
+ }
4290
+ if (state !== expectedState) {
4291
+ response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
4292
+ response.end("Antigravity authentication failed: invalid state.");
4293
+ finish(() => rejectCode(/* @__PURE__ */ new Error("OAuth state mismatch")));
4294
+ return;
4295
+ }
4296
+ response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
4297
+ response.end("Antigravity authentication complete. You can close this window and return to DSH.");
4298
+ finish(() => resolveCode({
4299
+ code,
4300
+ state
4301
+ }));
4302
+ });
4303
+ server.on("error", reject);
4304
+ server.listen(callbackPort(), resolveCallbackHost(), () => {
4305
+ timeout = setTimeout(() => {
4306
+ finish(() => rejectCode(/* @__PURE__ */ new Error("OAuth callback timed out waiting for browser login")));
4307
+ server.close();
4308
+ }, OAUTH_CALLBACK_TIMEOUT_MS);
4309
+ resolve({
4310
+ server,
4311
+ waitForCode: () => codePromise
4312
+ });
4313
+ });
4314
+ });
4315
+ }
4316
+ async function exchangeOAuthCode(code, verifier, callbackUrl) {
4317
+ const tokenResponse = await fetch(TOKEN_URL, {
4318
+ method: "POST",
4319
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
4320
+ body: new URLSearchParams$1({
4321
+ client_id: clientId(),
4322
+ client_secret: clientSecret(),
4323
+ code,
4324
+ grant_type: "authorization_code",
4325
+ redirect_uri: callbackUrl,
4326
+ code_verifier: verifier
4327
+ }).toString()
4328
+ });
4329
+ if (!tokenResponse.ok) throw new Error(`Token exchange failed: ${sanitizeOAuthProviderError(await tokenResponse.text())}`);
4330
+ const tokenData = await tokenResponse.json();
4331
+ const refreshToken = typeof tokenData.refresh_token === "string" ? tokenData.refresh_token : void 0;
4332
+ const accessToken = typeof tokenData.access_token === "string" ? tokenData.access_token : "";
4333
+ const expiresIn = typeof tokenData.expires_in === "number" ? tokenData.expires_in : 3600;
4334
+ if (!refreshToken) throw new Error("No refresh token received. Re-run login and allow offline access.");
4335
+ const [email, discoveredProject] = await Promise.all([getUserEmail(accessToken), loadCodeAssist(accessToken).catch(() => void 0)]);
4336
+ return {
4337
+ refresh: refreshToken,
4338
+ refresh_token: refreshToken,
4339
+ access: accessToken,
4340
+ access_token: accessToken,
4341
+ expires: Date.now() + expiresIn * 1e3 - 300 * 1e3,
4342
+ expires_at: Date.now() + expiresIn * 1e3 - 300 * 1e3,
4343
+ projectId: discoveredProject || void 0,
4344
+ email
4345
+ };
4346
+ }
4347
+ async function beginWebLogin(store) {
4348
+ if (webLoginFlow.status === "pending") return { ...webLoginFlow };
4349
+ const { verifier, challenge } = generatePKCE();
4350
+ const state = base64Url(randomBytes(32));
4351
+ const { server, waitForCode } = await startCallbackServer(state);
4352
+ const callbackUrl = redirectUri();
4353
+ webLoginFlow = {
4354
+ status: "pending",
4355
+ authUrl: `${AUTH_URL}?${new URLSearchParams$1({
4356
+ client_id: clientId(),
4357
+ response_type: "code",
4358
+ redirect_uri: callbackUrl,
4359
+ scope: SCOPES.join(" "),
4360
+ code_challenge: challenge,
4361
+ code_challenge_method: "S256",
4362
+ state,
4363
+ access_type: "offline",
4364
+ prompt: "consent"
4365
+ }).toString()}`,
4366
+ startedAt: Date.now(),
4367
+ error: void 0
4368
+ };
4369
+ (async () => {
4370
+ try {
4371
+ const { code, state: returnedState } = await waitForCode();
4372
+ if (returnedState !== state) throw new Error("OAuth state mismatch");
4373
+ const credentials = await exchangeOAuthCode(code, verifier, callbackUrl);
4374
+ await store.write(credentials);
4375
+ webLoginFlow.status = "complete";
4376
+ webLoginFlow.email = credentials.email;
4377
+ webLoginFlow.completedAt = Date.now();
4378
+ } catch (error) {
4379
+ webLoginFlow.status = "error";
4380
+ webLoginFlow.error = error instanceof Error ? error.message : String(error);
4381
+ webLoginFlow.completedAt = Date.now();
4382
+ } finally {
4383
+ server.close();
4384
+ }
4385
+ })();
4386
+ return { ...webLoginFlow };
4387
+ }
4388
+ function getWebLoginStatus() {
4389
+ return { ...webLoginFlow };
4390
+ }
4391
+ async function refreshAntigravityToken(credentials) {
4392
+ const refreshToken = credentials.refresh || credentials.refresh_token;
4393
+ if (!refreshToken) throw new Error("Missing Antigravity refresh token.");
4394
+ const response = await fetch(TOKEN_URL, {
4395
+ method: "POST",
4396
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
4397
+ body: new URLSearchParams$1({
4398
+ client_id: clientId(),
4399
+ client_secret: clientSecret(),
4400
+ refresh_token: refreshToken,
4401
+ grant_type: "refresh_token"
4402
+ }).toString()
4403
+ });
4404
+ if (!response.ok) throw new Error(`Token refresh failed: ${sanitizeOAuthProviderError(await response.text())}`);
4405
+ const data = await response.json();
4406
+ const accessToken = typeof data.access_token === "string" ? data.access_token : "";
4407
+ const expiresIn = typeof data.expires_in === "number" ? data.expires_in : 3600;
4408
+ const nextRefreshToken = typeof data.refresh_token === "string" ? data.refresh_token : refreshToken;
4409
+ return {
4410
+ ...credentials,
4411
+ refresh: nextRefreshToken,
4412
+ refresh_token: nextRefreshToken,
4413
+ access: accessToken,
4414
+ access_token: accessToken,
4415
+ expires: Date.now() + expiresIn * 1e3 - 300 * 1e3,
4416
+ expires_at: Date.now() + expiresIn * 1e3 - 300 * 1e3
4417
+ };
4418
+ }
4419
+ async function ensureApiKey(store) {
4420
+ let credentials = await store.read();
4421
+ if (!credentials) throw new Error("Not logged into Antigravity. Please log in from Settings > Antigravity.");
4422
+ const expires = credentials.expires || credentials.expires_at || 0;
4423
+ if (!(credentials.access || credentials.access_token) || expires <= Date.now() + 6e4) {
4424
+ credentials = await refreshAntigravityToken(credentials);
4425
+ await store.write(credentials);
4426
+ }
4427
+ return {
4428
+ token: credentials.access || credentials.access_token,
4429
+ projectId: credentials.projectId
4430
+ };
4431
+ }
4432
+ async function loginAndSave(store, signal, onUrl) {
4433
+ const { verifier, challenge } = generatePKCE();
4434
+ const state = base64Url(randomBytes(32));
4435
+ const { server, waitForCode } = await startCallbackServer(state);
4436
+ const callbackUrl = redirectUri();
4437
+ const authUrl = `${AUTH_URL}?${new URLSearchParams$1({
4438
+ client_id: clientId(),
4439
+ response_type: "code",
4440
+ redirect_uri: callbackUrl,
4441
+ scope: SCOPES.join(" "),
4442
+ code_challenge: challenge,
4443
+ code_challenge_method: "S256",
4444
+ state,
4445
+ access_type: "offline",
4446
+ prompt: "consent"
4447
+ }).toString()}`;
4448
+ try {
4449
+ if (onUrl) onUrl(authUrl);
4450
+ openBrowser(authUrl);
4451
+ if (signal?.aborted) throw new Error("OAuth login aborted");
4452
+ const { code, state: returnedState } = await waitForCode();
4453
+ if (returnedState !== state) throw new Error("OAuth state mismatch");
4454
+ const credentials = await exchangeOAuthCode(code, verifier, callbackUrl);
4455
+ await store.write(credentials);
4456
+ return credentials;
4457
+ } finally {
4458
+ server.close();
4459
+ }
4460
+ }
4461
+ //#endregion
4462
+ //#region src/host/antigravity/mapper.ts
4463
+ let toolCallCounter = 0;
4464
+ function sanitizeText(text) {
4465
+ return text.replace(/\0/g, "");
4466
+ }
4467
+ function isRecord(value) {
4468
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4469
+ }
4470
+ function asString(value) {
4471
+ return typeof value === "string" ? value : void 0;
4472
+ }
4473
+ function safeJsonParse(text) {
4474
+ try {
4475
+ return JSON.parse(text);
4476
+ } catch {
4477
+ return;
4478
+ }
4479
+ }
4480
+ function sanitizeToolCallId(id, fallbackName) {
4481
+ return String(id || "").replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64) || `${fallbackName || "tool"}_${Date.now()}_${++toolCallCounter}`;
4482
+ }
4483
+ function toolCallIdNeeded(modelId, runtimeModel) {
4484
+ return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-") || runtimeModel.startsWith("claude-") || runtimeModel.startsWith("gpt-oss-");
4485
+ }
4486
+ function parseArguments(raw) {
4487
+ if (isRecord(raw)) return raw;
4488
+ if (raw === void 0 || raw === null || raw === "") return {};
4489
+ const parsed = typeof raw === "string" ? safeJsonParse(raw) : raw;
4490
+ return isRecord(parsed) ? parsed : {};
4491
+ }
4492
+ function imageBlockToPart(block) {
4493
+ let data = asString(block.data) || asString(block.base64);
4494
+ const source = isRecord(block.source) ? block.source : void 0;
4495
+ if (!data && source) data = asString(source.data) || asString(source.base64);
4496
+ let mimeType = asString(block.mimeType) || asString(block.mediaType) || (source ? asString(source.mimeType) || asString(source.mediaType) : void 0) || "image/png";
4497
+ if (data?.startsWith("data:")) {
4498
+ const match = data.match(/^data:([^;,]+);base64,(.*)$/s);
4499
+ if (match) {
4500
+ mimeType = match[1] || mimeType;
4501
+ data = match[2] || "";
4502
+ }
4503
+ }
4504
+ return data ? { inlineData: {
4505
+ mimeType,
4506
+ data
4507
+ } } : void 0;
4508
+ }
4509
+ function contentToUserParts(content) {
4510
+ if (typeof content === "string") return [{ text: sanitizeText(content) }];
4511
+ if (!Array.isArray(content)) return [];
4512
+ const parts = [];
4513
+ for (const block of content) if (isRecord(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText(block.text) });
4514
+ else if (isRecord(block) && block.type === "image") {
4515
+ const img = imageBlockToPart(block);
4516
+ if (img) parts.push(img);
4517
+ }
4518
+ return parts;
4519
+ }
4520
+ function toolResultText(blocks) {
4521
+ if (!Array.isArray(blocks)) return "";
4522
+ return blocks.map((block) => {
4523
+ if (!isRecord(block)) return "";
4524
+ if (block.type === "text" && typeof block.text === "string") return sanitizeText(block.text);
4525
+ if (block.type === "tool-result") return toolResultText(block.content);
4526
+ return "";
4527
+ }).join("");
4528
+ }
4529
+ function replayBlockFor(message, index) {
4530
+ const source = message.source;
4531
+ if (!source || source.kind !== "model") return void 0;
4532
+ const state = source.replayState;
4533
+ if (!isRecord(state)) return void 0;
4534
+ const resp = isRecord(state.response) ? state.response : void 0;
4535
+ if (resp) {
4536
+ if (Array.isArray(resp.outputItems)) return resp.outputItems[index];
4537
+ if (Array.isArray(resp.blocks)) return resp.blocks[index];
4538
+ }
4539
+ if (Array.isArray(state.blocks)) return state.blocks[index];
4540
+ }
4541
+ function assistantParts(message, model, runtimeModel, toolNames) {
4542
+ const parts = [];
4543
+ if (!Array.isArray(message.content)) return parts;
4544
+ for (let index = 0; index < message.content.length; index++) {
4545
+ const block = message.content[index];
4546
+ if (!isRecord(block)) continue;
4547
+ const replay = replayBlockFor(message, index);
4548
+ if (block.type === "text" && String(block.text || "").trim()) parts.push({ text: sanitizeText(String(block.text)) });
4549
+ else if (block.type === "reasoning" && String(block.text || "").trim()) {
4550
+ const sig = asString(replay?.thinkingSignature) || asString(replay?.thought_signature) || asString(block.thought_signature);
4551
+ if (sig) parts.push({
4552
+ thought: true,
4553
+ text: sanitizeText(String(block.text)),
4554
+ thought_signature: sig,
4555
+ thoughtSignature: sig
4556
+ });
4557
+ else parts.push({ text: sanitizeText(String(block.text)) });
4558
+ } else if (block.type === "tool-call") {
4559
+ const toolId = String(block.id || "");
4560
+ const toolName = String(block.name || "");
4561
+ toolNames.set(toolId, toolName);
4562
+ const sig = asString(replay?.thought_signature) || asString(replay?.thoughtSignature) || asString(block.thought_signature) || asString(block.thoughtSignature) || "skip_thought_signature_validator";
4563
+ parts.push({
4564
+ functionCall: {
4565
+ name: toolName,
4566
+ args: parseArguments(block.arguments),
4567
+ ...toolCallIdNeeded(model.id, runtimeModel) ? { id: sanitizeToolCallId(toolId, toolName) } : {}
4568
+ },
4569
+ thought_signature: sig,
4570
+ thoughtSignature: sig
4571
+ });
4572
+ }
4573
+ }
4574
+ return parts;
4575
+ }
4576
+ function pushToolResult(contents, result, toolNames, model, runtimeModel) {
4577
+ const toolCallId = String(result.toolCallId || "");
4578
+ const toolName = toolNames.get(toolCallId) || "unknown";
4579
+ const responseText = toolResultText(result.content) || (result.isError ? "Tool failed" : "");
4580
+ const part = { functionResponse: {
4581
+ name: toolName,
4582
+ response: result.isError ? { error: responseText } : { output: responseText },
4583
+ ...toolCallIdNeeded(model.id, runtimeModel) ? { id: sanitizeToolCallId(toolCallId, toolName) } : {}
4584
+ } };
4585
+ const last = contents[contents.length - 1];
4586
+ if (last?.role === GEMINI_ROLE.user && last.parts.some((entry) => "functionResponse" in entry)) last.parts.push(part);
4587
+ else contents.push({
4588
+ role: GEMINI_ROLE.user,
4589
+ parts: [part]
4590
+ });
4591
+ }
4592
+ function convertMessages(options, model, runtimeModel) {
4593
+ const contents = [];
4594
+ const toolNames = /* @__PURE__ */ new Map();
4595
+ for (const message of options.messages) {
4596
+ const role = message.role || (message.source?.kind === "model" ? "assistant" : "user");
4597
+ if (role === "assistant" || message.source?.kind === "model") {
4598
+ const parts = assistantParts(message, model, runtimeModel, toolNames);
4599
+ if (parts.length) contents.push({
4600
+ role: GEMINI_ROLE.model,
4601
+ parts
4602
+ });
4603
+ continue;
4604
+ }
4605
+ const content = Array.isArray(message.content) ? message.content : [];
4606
+ const userParts = contentToUserParts(content.filter((b) => !isRecord(b) || b.type !== "tool-result"));
4607
+ if (role === "system") {
4608
+ if (userParts.length) contents.push({
4609
+ role: GEMINI_ROLE.user,
4610
+ parts: userParts
4611
+ });
4612
+ continue;
4613
+ }
4614
+ if (userParts.length) contents.push({
4615
+ role: GEMINI_ROLE.user,
4616
+ parts: userParts
4617
+ });
4618
+ for (const b of content) if (isRecord(b) && b.type === "tool-result") pushToolResult(contents, b, toolNames, model, runtimeModel);
4619
+ }
4620
+ return contents;
4621
+ }
4622
+ function stripMetaSchema(schema) {
4623
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return schema;
4624
+ const omit = /* @__PURE__ */ new Set([
4625
+ "$schema",
4626
+ "$id",
4627
+ "$anchor",
4628
+ "$dynamicAnchor",
4629
+ "$vocabulary",
4630
+ "$comment",
4631
+ "$defs",
4632
+ "definitions"
4633
+ ]);
4634
+ const out = {};
4635
+ for (const [k, v] of Object.entries(schema)) if (!omit.has(k)) out[k] = stripMetaSchema(v);
4636
+ return out;
4637
+ }
4638
+ function convertTools(tools) {
4639
+ if (!tools || tools.length === 0) return void 0;
4640
+ return [{ functionDeclarations: tools.map((tool) => ({
4641
+ name: tool.name,
4642
+ description: tool.description || "",
4643
+ parameters: stripMetaSchema(tool.parameters) || {
4644
+ type: "object",
4645
+ properties: {}
4646
+ }
4647
+ })) }];
4648
+ }
4649
+ function mapToolChoiceMode(toolChoice) {
4650
+ if (toolChoice === "none") return TOOL_CALLING_MODE.none;
4651
+ if (toolChoice === "any" || toolChoice === "required") return TOOL_CALLING_MODE.any;
4652
+ return TOOL_CALLING_MODE.auto;
4653
+ }
4654
+ function getMaxOutputTokens(modelId, runtimeModel) {
4655
+ return RUNTIME_MAX_OUTPUT_TOKENS[runtimeModel] || RUNTIME_MAX_OUTPUT_TOKENS[modelId] || 65536;
4656
+ }
4657
+ function buildRequest(options, model, projectId, runtimeModel, effort) {
4658
+ const request = {
4659
+ contents: convertMessages(options, model, runtimeModel),
4660
+ systemInstruction: {
4661
+ role: GEMINI_ROLE.user,
4662
+ parts: [
4663
+ { text: ANTIGRAVITY_SYSTEM_INSTRUCTION },
4664
+ { text: `Please ignore following [ignore]${ANTIGRAVITY_SYSTEM_INSTRUCTION}[/ignore]` },
4665
+ { text: ANTIGRAVITY_NO_PREAMBLE_INSTRUCTION },
4666
+ ...options.system ? [{ text: sanitizeText(options.system) }] : []
4667
+ ]
4668
+ }
4669
+ };
4670
+ const generationConfig = {};
4671
+ if (options.temperature !== void 0) generationConfig.temperature = options.temperature;
4672
+ if (runtimeModel === "gemini-3.8-flash-tiered" || runtimeModel === "gemini-3.7-flash-tiered") {
4673
+ const selected = effort || "off";
4674
+ generationConfig.thinkingConfig = { thinkingLevel: selected === "high" || selected === "xhigh" ? "HIGH" : selected === "medium" ? "MEDIUM" : "LOW" };
4675
+ }
4676
+ const maxAllowed = getMaxOutputTokens(model.id, runtimeModel);
4677
+ generationConfig.maxOutputTokens = options.maxTokens !== void 0 ? Math.min(options.maxTokens, maxAllowed) : maxAllowed;
4678
+ request.generationConfig = generationConfig;
4679
+ const toolChoice = options.toolChoice;
4680
+ const tools = convertTools(options.tools);
4681
+ if (tools) {
4682
+ request.tools = tools;
4683
+ if (toolChoice) request.toolConfig = { functionCallingConfig: { mode: mapToolChoiceMode(toolChoice) } };
4684
+ }
4685
+ if (options.sessionId) request.sessionId = String(options.sessionId);
4686
+ return {
4687
+ project: projectId,
4688
+ model: runtimeModel,
4689
+ request,
4690
+ requestType: "agent",
4691
+ userAgent: "antigravity",
4692
+ requestId: `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
4693
+ };
4694
+ }
4695
+ function createStreamState() {
4696
+ return {
4697
+ blocks: [],
4698
+ replayBlocks: [],
4699
+ currentBlock: null,
4700
+ hasContent: false,
4701
+ hasToolCall: false
4702
+ };
4703
+ }
4704
+ function processStreamLine(line, state) {
4705
+ if (!line.startsWith("data:")) return [];
4706
+ const json = line.slice(5).trim();
4707
+ if (!json || json === "[DONE]") return [];
4708
+ const chunk = safeJsonParse(json);
4709
+ if (!isRecord(chunk)) return [];
4710
+ const responseData = isRecord(chunk.response) ? chunk.response : chunk;
4711
+ const candidate = (Array.isArray(responseData.candidates) ? responseData.candidates : [])[0];
4712
+ const content = isRecord(candidate?.content) ? candidate.content : void 0;
4713
+ const parts = Array.isArray(content?.parts) ? content.parts : [];
4714
+ const out = [];
4715
+ const closeCurrentBlock = () => {
4716
+ if (!state.currentBlock) return;
4717
+ const index = state.blocks.length - 1;
4718
+ if (state.currentBlock.type === "text") out.push({
4719
+ type: "block-end",
4720
+ index,
4721
+ block: {
4722
+ type: "text",
4723
+ text: state.currentBlock.text
4724
+ }
4725
+ });
4726
+ else out.push({
4727
+ type: "block-end",
4728
+ index,
4729
+ block: {
4730
+ type: "reasoning",
4731
+ text: state.currentBlock.text
4732
+ }
4733
+ });
4734
+ state.currentBlock = null;
4735
+ };
4736
+ for (const part of parts) {
4737
+ if (!isRecord(part)) continue;
4738
+ if (part.text !== void 0 && typeof part.text === "string") {
4739
+ const isThinking = part.thought === true;
4740
+ const blockType = isThinking ? "reasoning" : "text";
4741
+ if (!state.currentBlock || state.currentBlock.type !== blockType) {
4742
+ closeCurrentBlock();
4743
+ state.currentBlock = {
4744
+ type: blockType,
4745
+ text: ""
4746
+ };
4747
+ const index = state.blocks.length;
4748
+ state.blocks.push({
4749
+ type: blockType,
4750
+ text: ""
4751
+ });
4752
+ state.replayBlocks.push({ type: blockType });
4753
+ out.push({
4754
+ type: "block-start",
4755
+ index,
4756
+ blockType
4757
+ });
4758
+ }
4759
+ const delta = sanitizeText(part.text);
4760
+ state.currentBlock.text += delta;
4761
+ state.hasContent = true;
4762
+ if (isThinking && part.thoughtSignature) {
4763
+ state.currentBlock.thinkingSignature = part.thoughtSignature;
4764
+ state.replayBlocks[state.blocks.length - 1].thinkingSignature = part.thoughtSignature;
4765
+ } else if (!isThinking && part.thoughtSignature) {
4766
+ state.currentBlock.textSignature = part.thoughtSignature;
4767
+ state.replayBlocks[state.blocks.length - 1].textSignature = part.thoughtSignature;
4768
+ }
4769
+ out.push({
4770
+ type: isThinking ? "reasoning-delta" : "text-delta",
4771
+ index: state.blocks.length - 1,
4772
+ text: delta
4773
+ });
4774
+ }
4775
+ if (isRecord(part.functionCall)) {
4776
+ closeCurrentBlock();
4777
+ const fc = part.functionCall;
4778
+ const toolName = asString(fc.name) || "";
4779
+ const toolId = sanitizeToolCallId(asString(fc.id) || "", toolName);
4780
+ const argsText = JSON.stringify(isRecord(fc.args) ? fc.args : {});
4781
+ const index = state.blocks.length;
4782
+ const sig = asString(part.thought_signature) || asString(part.thoughtSignature) || asString(part.thinkingSignature) || asString(fc.thought_signature) || asString(fc.thoughtSignature) || state.currentBlock?.thinkingSignature;
4783
+ const block = {
4784
+ type: "tool-call",
4785
+ id: toolId,
4786
+ name: toolName,
4787
+ arguments: argsText,
4788
+ ...sig ? {
4789
+ thought_signature: sig,
4790
+ thoughtSignature: sig
4791
+ } : {}
4792
+ };
4793
+ state.blocks.push(block);
4794
+ state.replayBlocks.push({
4795
+ type: "tool-call",
4796
+ ...sig ? {
4797
+ thought_signature: sig,
4798
+ thoughtSignature: sig
4799
+ } : {}
4800
+ });
4801
+ state.hasContent = true;
4802
+ state.hasToolCall = true;
4803
+ out.push({
4804
+ type: "block-start",
4805
+ index,
4806
+ blockType: "tool-call"
4807
+ });
4808
+ out.push({
4809
+ type: "tool-call-delta",
4810
+ index,
4811
+ id: toolId,
4812
+ name: toolName,
4813
+ argumentsDelta: argsText
4814
+ });
4815
+ out.push({
4816
+ type: "block-end",
4817
+ index,
4818
+ block
4819
+ });
4820
+ }
4821
+ }
4822
+ if (responseData.usageMetadata && isRecord(responseData.usageMetadata)) {
4823
+ const u = responseData.usageMetadata;
4824
+ const usage = {
4825
+ inputTokens: Math.max(0, (u.promptTokenCount || 0) - (u.cachedContentTokenCount || 0)),
4826
+ outputTokens: (u.candidatesTokenCount || 0) + (u.thoughtsTokenCount || 0),
4827
+ ...u.cachedContentTokenCount ? { cacheReadTokens: u.cachedContentTokenCount } : {}
4828
+ };
4829
+ out.push({
4830
+ type: "usage",
4831
+ usage
4832
+ });
4833
+ }
4834
+ const finishReason = candidate?.finishReason;
4835
+ if (finishReason) {
4836
+ closeCurrentBlock();
4837
+ const reason = state.hasToolCall ? { kind: "tool-calls" } : finishReason === "MAX_TOKENS" ? { kind: "max-tokens" } : { kind: "stop" };
4838
+ out.push({
4839
+ type: "finish",
4840
+ reason,
4841
+ replayState: { response: {
4842
+ outputItems: state.replayBlocks,
4843
+ blocks: state.replayBlocks
4844
+ } }
4845
+ });
4846
+ }
4847
+ return out;
4848
+ }
4849
+ function closeStream(state) {
4850
+ const out = [];
4851
+ if (state.currentBlock) {
4852
+ const index = state.blocks.length - 1;
4853
+ if (state.currentBlock.type === "text") out.push({
4854
+ type: "block-end",
4855
+ index,
4856
+ block: {
4857
+ type: "text",
4858
+ text: state.currentBlock.text
4859
+ }
4860
+ });
4861
+ else out.push({
4862
+ type: "block-end",
4863
+ index,
4864
+ block: {
4865
+ type: "reasoning",
4866
+ text: state.currentBlock.text
4867
+ }
4868
+ });
4869
+ state.currentBlock = null;
4870
+ }
4871
+ return out;
4872
+ }
4873
+ //#endregion
4874
+ //#region src/host/antigravity/adapter.ts
4875
+ var AntigravityAdapter = class extends LlmAdapter {
4876
+ store;
4877
+ modelSettings;
4878
+ preferences;
4879
+ constructor(store = new FileCredentialStore(), modelSettings = new FileModelSettingsStore(), preferences) {
4880
+ super();
4881
+ this.store = store;
4882
+ this.modelSettings = modelSettings;
4883
+ this.preferences = preferences;
4884
+ }
4885
+ providerInfo(provider) {
4886
+ return {
4887
+ id: provider,
4888
+ name: PROVIDER_NAME
4889
+ };
4890
+ }
4891
+ async listModels(provider) {
4892
+ const prov = provider || "antigravity";
4893
+ const settings = this.preferences ? this.preferences.status() : await this.modelSettings.read();
4894
+ const enabledSet = new Set(settings.enabledModelIds);
4895
+ const available = MODELS.filter((m) => enabledSet.has(m.id));
4896
+ const overrides = settings.contextWindowOverrides || {};
4897
+ return available.map((model) => ({
4898
+ provider: prov,
4899
+ id: model.id,
4900
+ name: model.name,
4901
+ inputModalities: model.inputModalities,
4902
+ context: { contextWindow: overrides[model.id] || model.contextWindow },
4903
+ defaultMaxTokens: model.maxTokens,
4904
+ ...model.reasoningEfforts ? { reasoningEfforts: model.reasoningEfforts } : {}
4905
+ }));
4906
+ }
4907
+ async resolveModel(provider, modelId, signal) {
4908
+ if (signal?.aborted) throw new LlmError("antigravity model resolution aborted", "ABORTED");
4909
+ const model = MODELS.find((m) => m.id === modelId) || {
4910
+ id: modelId,
4911
+ name: modelId,
4912
+ inputModalities: ["text", "image"],
4913
+ contextWindow: 128e3,
4914
+ maxTokens: 65536
4915
+ };
4916
+ const settings = this.preferences ? this.preferences.status() : await this.modelSettings.read();
4917
+ const overrides = settings.contextWindowOverrides || {};
4918
+ const efforts = model.reasoningEfforts || [
4919
+ "low",
4920
+ "medium",
4921
+ "high"
4922
+ ];
4923
+ const defaultEffort = settings.defaultReasoningEffort || "medium";
4924
+ return {
4925
+ provider,
4926
+ id: model.id,
4927
+ name: model.name,
4928
+ inputModalities: model.inputModalities,
4929
+ context: { contextWindow: overrides[model.id] || model.contextWindow },
4930
+ defaultMaxTokens: model.maxTokens,
4931
+ ...model.reasoningEfforts ? { reasoning: {
4932
+ efforts: efforts.map((effort) => ({
4933
+ id: ReasoningEffortId(effort),
4934
+ name: effort
4935
+ })),
4936
+ defaultEffort: ReasoningEffortId(defaultEffort)
4937
+ } } : {}
4938
+ };
4939
+ }
4940
+ async prepareCall(provider, model, signal) {
4941
+ return {
4942
+ model: await this.resolveModel(provider, model, signal),
4943
+ stream: (options) => this.stream(options)
4944
+ };
4945
+ }
4946
+ async *stream(options) {
4947
+ const model = MODELS.find((m) => m.id === options.model) || {
4948
+ id: options.model,
4949
+ name: options.model,
4950
+ inputModalities: ["text", "image"],
4951
+ contextWindow: 128e3,
4952
+ maxTokens: 65536
4953
+ };
4954
+ const settings = await this.modelSettings.read();
4955
+ const effectiveEffort = options.reasoningEffort || settings.defaultReasoningEffort || void 0;
4956
+ const effectiveOptions = effectiveEffort ? {
4957
+ ...options,
4958
+ reasoningEffort: effectiveEffort
4959
+ } : options;
4960
+ yield* wrapStreamWithWatchdog((watchdogSignal) => this.requestStream(effectiveOptions, model, watchdogSignal), options.signal, STREAM_IDLE_TIMEOUT_MS, STREAM_IDLE_TIMEOUT_CODE, "Antigravity");
4961
+ }
4962
+ async *requestStream(options, model, signal) {
4963
+ const { token, projectId: defaultProj } = await ensureApiKey(this.store);
4964
+ const projectId = defaultProj || "antigravity-default";
4965
+ const effort = String(options.reasoningEffort || "medium").toLowerCase();
4966
+ const routing = ROUTING[model.id];
4967
+ const initialRuntime = routing?.routing[effort] || routing?.defaultRequestId || model.id;
4968
+ const fallbackRuntime = routing?.off && routing.off !== initialRuntime ? routing.off : void 0;
4969
+ const candidates = [initialRuntime];
4970
+ if (fallbackRuntime && !candidates.includes(fallbackRuntime)) candidates.push(fallbackRuntime);
4971
+ if (routing?.fallbackCandidates) {
4972
+ for (const fc of routing.fallbackCandidates) if (!candidates.includes(fc)) candidates.push(fc);
4973
+ }
4974
+ let response;
4975
+ candidates[0];
4976
+ for (const runtimeModel of candidates) {
4977
+ const body = JSON.stringify(buildRequest(options, model, projectId, runtimeModel, effort));
4978
+ const headers = {
4979
+ ...antigravityHeaders(token),
4980
+ ...model.id.startsWith("claude-") ? { "anthropic-beta": "interleaved-thinking-2025-05-14" } : {}
4981
+ };
4982
+ for (const endpoint of endpointCandidates()) try {
4983
+ response = await fetch(`${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
4984
+ method: "POST",
4985
+ headers,
4986
+ body,
4987
+ signal
4988
+ });
4989
+ if (response.ok) break;
4990
+ if (response.status === 404) break;
4991
+ } catch (err) {
4992
+ if (signal.aborted) throw new LlmError("Antigravity request aborted", "ABORTED", { cause: err });
4993
+ }
4994
+ if (response && response.ok) break;
4995
+ }
4996
+ if (!response || !response.ok) {
4997
+ const status = response?.status ?? 500;
4998
+ const errText = await response?.text().catch(() => "");
4999
+ if (status === 429) throw new LlmError(`Antigravity 账号配额已耗尽或请求受限 (429 RESOURCE_EXHAUSTED)。请在插件设置页查看配额剩余百分比及重置倒计时。原始响应: ${errText || "No response"}`, "RATE_LIMIT", { status: 429 });
5000
+ throw new LlmError(`Antigravity API error (${status}): ${errText || "No response"}`, "PROVIDER_ERROR", { status });
5001
+ }
5002
+ if (!response.body) throw new LlmError("Antigravity returned empty response body", "PROVIDER_ERROR");
5003
+ const reader = response.body.getReader();
5004
+ const decoder = new TextDecoder();
5005
+ const state = createStreamState();
5006
+ let buffer = "";
5007
+ try {
5008
+ while (true) {
5009
+ const { done, value } = await reader.read();
5010
+ if (done) break;
5011
+ buffer += decoder.decode(value, { stream: true });
5012
+ const lines = buffer.split("\n");
5013
+ buffer = lines.pop() || "";
5014
+ for (const line of lines) {
5015
+ const trimmed = line.trim();
5016
+ if (!trimmed) continue;
5017
+ const chunks = processStreamLine(trimmed, state);
5018
+ for (const chunk of chunks) yield chunk;
5019
+ }
5020
+ }
5021
+ if (buffer.trim()) {
5022
+ const chunks = processStreamLine(buffer.trim(), state);
5023
+ for (const chunk of chunks) yield chunk;
5024
+ }
5025
+ for (const chunk of closeStream(state)) yield chunk;
5026
+ } finally {
5027
+ reader.cancel().catch(() => void 0);
5028
+ }
5029
+ }
5030
+ };
5031
+ //#endregion
5032
+ //#region src/host/antigravity/routes.ts
5033
+ function sendJson(response, status, body) {
5034
+ response.writeHead(status, { "Content-Type": "application/json" });
5035
+ response.end(JSON.stringify(body));
5036
+ }
5037
+ function sendMethodNotAllowed(response) {
5038
+ sendJson(response, 405, {
5039
+ ok: false,
5040
+ error: "Method Not Allowed"
5041
+ });
5042
+ }
5043
+ async function readRequestJson(request) {
5044
+ return new Promise((resolve, reject) => {
5045
+ let raw = "";
5046
+ request.on("data", (chunk) => {
5047
+ raw += String(chunk);
5048
+ if (raw.length > 64 * 1024) reject(/* @__PURE__ */ new Error("Request body too large"));
5049
+ });
5050
+ request.on("end", () => {
5051
+ try {
5052
+ resolve(raw ? JSON.parse(raw) : {});
5053
+ } catch (err) {
5054
+ reject(err);
5055
+ }
5056
+ });
5057
+ request.on("error", reject);
5058
+ });
5059
+ }
5060
+ async function getAntigravityWebStatus(store, modelSettings, preferences) {
5061
+ const credentials = await store.read();
5062
+ const settings = preferences ? preferences.status() : await modelSettings.read();
5063
+ const quota = getCachedQuota();
5064
+ const enabledSet = new Set(settings.enabledModelIds);
5065
+ const overrides = settings.contextWindowOverrides || {};
5066
+ const models = MODELS.map((m) => ({
5067
+ id: m.id,
5068
+ name: m.name,
5069
+ enabled: enabledSet.has(m.id),
5070
+ defaultContextWindow: m.contextWindow,
5071
+ contextWindow: overrides[m.id] || m.contextWindow,
5072
+ reasoningEfforts: m.reasoningEfforts
5073
+ }));
5074
+ return {
5075
+ authenticated: !!(credentials?.access || credentials?.access_token),
5076
+ email: credentials?.email,
5077
+ projectId: credentials?.projectId,
5078
+ hasCredentials: !!credentials,
5079
+ storagePath: store.path(),
5080
+ lastFetchedAt: quota?.fetchedAt,
5081
+ quota,
5082
+ models,
5083
+ contextWindowOverrides: overrides,
5084
+ defaultReasoningEffort: settings.defaultReasoningEffort || null
5085
+ };
5086
+ }
5087
+ function registerAntigravityRoutes(ctx, store, modelSettings, preferences) {
5088
+ return ctx.webServer.register({
5089
+ kind: "prefix",
5090
+ path: "/antigravity/api",
5091
+ handler: async (request, response) => {
5092
+ const path = new URL(request.url || "/", "http://dsh.local").pathname.replace(/^\/antigravity\/api\/?/, "");
5093
+ try {
5094
+ if (path === "status" || path === "") {
5095
+ if (request.method !== "GET") return sendMethodNotAllowed(response);
5096
+ return sendJson(response, 200, {
5097
+ ok: true,
5098
+ value: await getAntigravityWebStatus(store, modelSettings, preferences)
5099
+ });
5100
+ }
5101
+ if (path === "login") {
5102
+ if (request.method !== "POST") return sendMethodNotAllowed(response);
5103
+ return sendJson(response, 200, {
5104
+ ok: true,
5105
+ value: await beginWebLogin(store)
5106
+ });
5107
+ }
5108
+ if (path === "login/status") {
5109
+ if (request.method !== "GET") return sendMethodNotAllowed(response);
5110
+ return sendJson(response, 200, {
5111
+ ok: true,
5112
+ value: getWebLoginStatus()
5113
+ });
5114
+ }
5115
+ if (path === "quota") {
5116
+ if (request.method !== "GET" && request.method !== "POST") return sendMethodNotAllowed(response);
5117
+ const quota = await fetchAccountQuota(store, modelSettings);
5118
+ return sendJson(response, 200, {
5119
+ ok: true,
5120
+ value: {
5121
+ ...await getAntigravityWebStatus(store, modelSettings, preferences),
5122
+ quota
5123
+ }
5124
+ });
5125
+ }
5126
+ if (path === "settings") {
5127
+ if (request.method !== "POST") return sendMethodNotAllowed(response);
5128
+ const body = await readRequestJson(request);
5129
+ if (preferences) await preferences.update(body);
5130
+ else await modelSettings.updateSettings(body);
5131
+ return sendJson(response, 200, {
5132
+ ok: true,
5133
+ value: await getAntigravityWebStatus(store, modelSettings, preferences)
5134
+ });
5135
+ }
5136
+ if (path === "models") {
5137
+ if (request.method === "GET") return sendJson(response, 200, {
5138
+ ok: true,
5139
+ value: (await getAntigravityWebStatus(store, modelSettings, preferences)).models
5140
+ });
5141
+ if (request.method === "POST") {
5142
+ const body = await readRequestJson(request);
5143
+ if (Array.isArray(body.enabledModelIds) || body.contextWindowOverrides || body.defaultReasoningEffort !== void 0) if (preferences) await preferences.update(body);
5144
+ else await modelSettings.updateSettings(body);
5145
+ return sendJson(response, 200, {
5146
+ ok: true,
5147
+ value: await getAntigravityWebStatus(store, modelSettings, preferences)
5148
+ });
5149
+ }
5150
+ return sendMethodNotAllowed(response);
5151
+ }
5152
+ if (path === "logout") {
5153
+ if (request.method !== "POST") return sendMethodNotAllowed(response);
5154
+ await store.delete();
5155
+ return sendJson(response, 200, {
5156
+ ok: true,
5157
+ value: await getAntigravityWebStatus(store, modelSettings)
5158
+ });
5159
+ }
5160
+ return sendJson(response, 404, {
5161
+ ok: false,
5162
+ error: "not-found"
5163
+ });
5164
+ } catch (err) {
5165
+ return sendJson(response, 500, {
5166
+ ok: false,
5167
+ error: err instanceof Error ? err.message : String(err)
5168
+ });
5169
+ }
5170
+ }
5171
+ });
5172
+ }
5173
+ //#endregion
3310
5174
  //#region src/index.ts
3311
5175
  const inject = [
3312
5176
  "webServer",
@@ -3320,6 +5184,18 @@ const inject = [
3320
5184
  function apply(ctx) {
3321
5185
  const store = createPlatformTokenStore();
3322
5186
  const preferences = registerPreferenceStore(ctx.settings);
5187
+ const antigravityStore = new FileCredentialStore();
5188
+ const antigravityModelSettings = new FileModelSettingsStore();
5189
+ const antigravityPreferences = registerAntigravityPreferenceStore(ctx.settings, antigravityModelSettings);
5190
+ const antigravityAdapter = new AntigravityAdapter(antigravityStore, antigravityModelSettings, antigravityPreferences);
5191
+ ctx.effect(() => {
5192
+ const disposeAntigravityAdapter = ctx.llm.registerAdapter([PROVIDER_ID], antigravityAdapter);
5193
+ const disposeAntigravityRoutes = registerAntigravityRoutes(ctx, antigravityStore, antigravityModelSettings, antigravityPreferences);
5194
+ return () => {
5195
+ disposeAntigravityRoutes();
5196
+ disposeAntigravityAdapter();
5197
+ };
5198
+ }, "dsh-antigravity: adapter, routes, and lifecycle");
3323
5199
  ctx.effect(() => {
3324
5200
  const proxyManager = new ProxyManager({
3325
5201
  getPreferences: () => preferences.status(),
@@ -3345,7 +5221,7 @@ function apply(ctx) {
3345
5221
  });
3346
5222
  };
3347
5223
  const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager);
3348
- const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID], adapter);
5224
+ const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID$1], adapter);
3349
5225
  const disposeImageTool = ctx.tools.register(createCodexImageTool(oauth, ctx.attachments, { fetchFn: proxyFetch }));
3350
5226
  let disposeWebProviders = () => {};
3351
5227
  const registerWebProviders = () => {
@@ -3377,4 +5253,4 @@ function localWebServerBaseUrl(host, port) {
3377
5253
  return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
3378
5254
  }
3379
5255
  //#endregion
3380
- export { CodexChatGptAdapter, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, detectSystemProxy, inject, mapCodexUsage, parseCodexUsage, parseResponsesStream };
5256
+ export { AntigravityAdapter, CodexChatGptAdapter, FileCredentialStore, FileModelSettingsStore, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, beginWebLogin, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, credentialPath, detectSystemProxy, fetchAccountQuota, getCachedQuota, inject, loginAndSave, mapCodexUsage, modelSettingsPath, parseCodexUsage, parseResponsesStream, refreshAntigravityToken };