@eddyskywalker/dsh-chatgpt-subscription 0.2.10 → 0.2.12

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 (37) hide show
  1. package/CHANGELOG.md +24 -24
  2. package/LICENSE +21 -21
  3. package/README.md +169 -169
  4. package/bin/antigravity-login.mjs +15 -4
  5. package/cordis.patch.yml +6 -6
  6. package/lib/client.js +110 -12
  7. package/lib/client.js.map +1 -1
  8. package/lib/index.js +253 -93
  9. package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts.map +1 -1
  10. package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -1
  11. package/lib/types/client/locales.d.ts +65 -1
  12. package/lib/types/client/locales.d.ts.map +1 -1
  13. package/lib/types/compat.d.ts +11 -0
  14. package/lib/types/compat.d.ts.map +1 -1
  15. package/lib/types/host/antigravity/adapter.d.ts +1 -0
  16. package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
  17. package/lib/types/host/antigravity/client.d.ts +23 -1
  18. package/lib/types/host/antigravity/client.d.ts.map +1 -1
  19. package/lib/types/host/antigravity/mapper.d.ts +1 -0
  20. package/lib/types/host/antigravity/mapper.d.ts.map +1 -1
  21. package/lib/types/host/antigravity/oauth.d.ts +9 -3
  22. package/lib/types/host/antigravity/oauth.d.ts.map +1 -1
  23. package/lib/types/host/antigravity/routes.d.ts.map +1 -1
  24. package/lib/types/host/antigravity/types.d.ts +6 -0
  25. package/lib/types/host/antigravity/types.d.ts.map +1 -1
  26. package/lib/types/host/model-catalog.d.ts.map +1 -1
  27. package/lib/types/host/preferences.d.ts.map +1 -1
  28. package/lib/types/host/proxy-manager.d.ts.map +1 -1
  29. package/lib/types/host/responses-mapper.d.ts +1 -0
  30. package/lib/types/host/responses-mapper.d.ts.map +1 -1
  31. package/lib/types/index.d.ts +1 -1
  32. package/lib/types/index.d.ts.map +1 -1
  33. package/lib/types/shared/contracts.d.ts +9 -0
  34. package/lib/types/shared/contracts.d.ts.map +1 -1
  35. package/lib/types/shared/preferences.d.ts +2 -0
  36. package/lib/types/shared/preferences.d.ts.map +1 -1
  37. package/package.json +134 -134
package/lib/index.js CHANGED
@@ -7,13 +7,13 @@ import { defineTool } from "@deepseek-ai/dsh-tools";
7
7
  import { createHash, randomBytes, randomUUID } from "node:crypto";
8
8
  import http, { createServer } from "node:http";
9
9
  import { execSync, spawn } from "node:child_process";
10
+ import fs, { constants } from "node:fs";
11
+ import path, { dirname, join } from "node:path";
12
+ import os, { homedir } from "node:os";
10
13
  import { ProxyAgent, fetch as fetch$1 } from "undici";
11
14
  import * as SettingsModule from "@deepseek-ai/dsh-settings";
12
15
  import z from "@deepseek-ai/schemastery";
13
- import fs, { constants } from "node:fs";
14
16
  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
17
  import { isDeepStrictEqual } from "node:util";
18
18
  import { URL as URL$1, URLSearchParams as URLSearchParams$1 } from "node:url";
19
19
  //#region src/compat.ts
@@ -211,6 +211,7 @@ function resolveCodexModel(model, preferences) {
211
211
  inputModalities: [...entry.inputModalities],
212
212
  context: { contextWindow: configuredContextWindow ?? entry.contextWindow },
213
213
  defaultMaxTokens: 32768,
214
+ systemPromptUpdate: "in-history",
214
215
  reasoning: {
215
216
  efforts: efforts.map((effort) => ({
216
217
  id: ReasoningEffortId(effort),
@@ -1244,8 +1245,16 @@ function parseMacOsScutilProxy(stdout) {
1244
1245
  }
1245
1246
  function parseEnvProxy(env = process.env) {
1246
1247
  const proxy = env.HTTPS_PROXY || env.https_proxy || env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
1247
- if (!proxy || !proxy.trim()) return null;
1248
- return normalizeProxyUrl(proxy);
1248
+ if (proxy && proxy.trim()) return normalizeProxyUrl(proxy);
1249
+ try {
1250
+ const dshHome = env.DSH_HOME || path.join(os.homedir(), ".dsh");
1251
+ const envFile = path.join(dshHome, ".env");
1252
+ if (fs.existsSync(envFile)) {
1253
+ const match = fs.readFileSync(envFile, "utf8").match(/^(?:export\s+)?(?:HTTPS_PROXY|https_proxy|HTTP_PROXY|http_proxy|ALL_PROXY|all_proxy)\s*=\s*["']?([^"'\r\n]+)["']?/m);
1254
+ if (match && match[1]?.trim()) return normalizeProxyUrl(match[1].trim());
1255
+ }
1256
+ } catch {}
1257
+ return null;
1249
1258
  }
1250
1259
  function detectSystemProxy(platform = process.platform, env = process.env) {
1251
1260
  try {
@@ -1354,6 +1363,7 @@ const DEFAULT_PREFERENCES = {
1354
1363
  },
1355
1364
  subagentContextWindow: null,
1356
1365
  subagentMaxDepth: null,
1366
+ teamModelRules: [],
1357
1367
  proxyMode: "auto",
1358
1368
  customProxyUrl: null
1359
1369
  };
@@ -1400,6 +1410,13 @@ function registerPreferenceStore(settings) {
1400
1410
  }).default(DEFAULT_PREFERENCES.contextWindowOverrides),
1401
1411
  subagentContextWindow: z.union([z.number().step(1).min(1), z.const(null)]).default(DEFAULT_PREFERENCES.subagentContextWindow),
1402
1412
  subagentMaxDepth: z.union([z.number().step(1).min(0).max(3), z.const(null)]).default(DEFAULT_PREFERENCES.subagentMaxDepth),
1413
+ teamModelRules: z.array(z.object({
1414
+ id: z.string(),
1415
+ pattern: z.string(),
1416
+ model: z.string(),
1417
+ reasoningEffort: z.union([z.string(), z.const(null)]).default(null),
1418
+ description: z.union([z.string(), z.const(null)]).default(null)
1419
+ })).default(DEFAULT_PREFERENCES.teamModelRules),
1403
1420
  proxyMode: z.union([
1404
1421
  z.const("auto"),
1405
1422
  z.const("custom"),
@@ -1442,6 +1459,7 @@ var SettingsPreferenceStore = class {
1442
1459
  };
1443
1460
  if (patch.subagentContextWindow !== void 0) normalized.subagentContextWindow = patch.subagentContextWindow;
1444
1461
  if (patch.subagentMaxDepth !== void 0) normalized.subagentMaxDepth = patch.subagentMaxDepth;
1462
+ if (patch.teamModelRules !== void 0) normalized.teamModelRules = Array.isArray(patch.teamModelRules) ? patch.teamModelRules.filter((r) => r && typeof r.pattern === "string" && typeof r.model === "string") : [];
1445
1463
  if (patch.proxyMode !== void 0) {
1446
1464
  if (!isProxyMode(patch.proxyMode)) throw new PreferenceError("Unsupported proxy mode preference.");
1447
1465
  normalized.proxyMode = patch.proxyMode;
@@ -1875,6 +1893,14 @@ async function mapContent(message, attachments, signal, localRawImages, localIma
1875
1893
  type: "input_image",
1876
1894
  image_url: await imageDataUrl(block.attachment, attachments, signal)
1877
1895
  });
1896
+ } else if (block.type === "file") {
1897
+ const file = block.attachment ?? {};
1898
+ const fileText = `[File attachment: ${typeof file.name === "string" ? file.name : typeof file.filename === "string" ? file.filename : "unnamed"}${typeof file.byteSize === "number" ? ` (${file.byteSize} bytes)` : ""}${typeof file.savedPath === "string" ? ` path: ${file.savedPath}` : ""}]`;
1899
+ if (message.role === "user") pushInputText(result, fileText);
1900
+ else result.push({
1901
+ type: "output_text",
1902
+ text: fileText
1903
+ });
1878
1904
  }
1879
1905
  return result;
1880
1906
  }
@@ -2005,6 +2031,10 @@ function blocksToText(blocks) {
2005
2031
  return blocks.map((block) => {
2006
2032
  if (block.type === "text" || block.type === "reasoning") return block.text;
2007
2033
  if (block.type === "image") return `[image: ${block.attachment.name ?? block.attachment.attachmentId}]`;
2034
+ if (block.type === "file") {
2035
+ const file = block.attachment ?? {};
2036
+ return `[file: ${typeof file.name === "string" ? file.name : typeof file.filename === "string" ? file.filename : "unnamed"}${typeof file.byteSize === "number" ? ` (${file.byteSize} bytes)` : ""}${typeof file.savedPath === "string" ? ` path: ${file.savedPath}` : ""}]`;
2037
+ }
2008
2038
  if (block.type === "tool-result") return blocksToText(block.content);
2009
2039
  return "";
2010
2040
  }).filter(Boolean).join("\n");
@@ -3130,6 +3160,19 @@ function readPreferencesUpdate(value, current) {
3130
3160
  if (value.subagentMaxDepth !== null && (!Number.isSafeInteger(value.subagentMaxDepth) || value.subagentMaxDepth < 0 || value.subagentMaxDepth > 3)) throw new PreferenceError(`subagentMaxDepth must be null or an integer from 0 to 3.`);
3131
3161
  patch.subagentMaxDepth = value.subagentMaxDepth;
3132
3162
  }
3163
+ if ("teamModelRules" in value) {
3164
+ if (!Array.isArray(value.teamModelRules)) throw new PreferenceError("teamModelRules must be an array.");
3165
+ patch.teamModelRules = value.teamModelRules.map((rule) => {
3166
+ if (!isRecord$2(rule) || typeof rule.pattern !== "string" || typeof rule.model !== "string") throw new PreferenceError("Each teamModelRule must have a string pattern and model.");
3167
+ return {
3168
+ id: typeof rule.id === "string" && rule.id ? rule.id : `rule_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
3169
+ pattern: rule.pattern.trim(),
3170
+ model: rule.model.trim(),
3171
+ reasoningEffort: typeof rule.reasoningEffort === "string" ? rule.reasoningEffort : null,
3172
+ description: typeof rule.description === "string" ? rule.description.trim() : void 0
3173
+ };
3174
+ });
3175
+ }
3133
3176
  if ("proxyMode" in value) {
3134
3177
  if (value.proxyMode !== "auto" && value.proxyMode !== "custom" && value.proxyMode !== "direct") throw new PreferenceError("proxyMode must be auto, custom, or direct.");
3135
3178
  patch.proxyMode = value.proxyMode;
@@ -3568,9 +3611,11 @@ const PROVIDER_ID = "antigravity";
3568
3611
  const STREAM_IDLE_TIMEOUT_MS = 3e5;
3569
3612
  const STREAM_IDLE_TIMEOUT_CODE = "LLM_STREAM_IDLE_TIMEOUT";
3570
3613
  const DISCOVERY_TIMEOUT_MS = 8e3;
3571
- const PROJECT_CACHE_TTL_MS = 1800 * 1e3;
3572
3614
  const OAUTH_CALLBACK_TIMEOUT_MS = 300 * 1e3;
3573
3615
  const ENDPOINT_FALLBACKS = ["https://daily-cloudcode-pa.googleapis.com", "https://cloudcode-pa.googleapis.com"];
3616
+ const FREE_TIER_ID = "free-tier";
3617
+ const ONBOARD_TIMEOUT_MS = 3e4;
3618
+ const ONBOARD_POLL_INTERVAL_MS = 1e3;
3574
3619
  const REDIRECT_PATH = "/oauth-callback";
3575
3620
  const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
3576
3621
  const TOKEN_URL = "https://oauth2.googleapis.com/token";
@@ -4202,13 +4247,13 @@ var FileModelSettingsStore = class {
4202
4247
  return next;
4203
4248
  }
4204
4249
  };
4205
- //#endregion
4206
- //#region src/host/antigravity/client.ts
4207
- const projectCache = /* @__PURE__ */ new Map();
4208
4250
  let cachedQuota;
4251
+ let quotaFetchInFlight = null;
4209
4252
  const PLATFORM = process.platform === "darwin" ? "MACOS" : process.platform === "win32" ? "WINDOWS" : "LINUX";
4210
4253
  function defaultUserAgent() {
4211
- return `antigravity/1.15.8 ${process.platform === "darwin" ? "darwin" : process.platform === "win32" ? "windows" : "linux"}/${process.arch === "x64" ? "amd64" : process.arch}`;
4254
+ const version = process.env.DSH_ANTIGRAVITY_VERSION || "2.8.0";
4255
+ const cl = process.env.DSH_ANTIGRAVITY_CL || "963137146";
4256
+ return `antigravity/hub/${version} (aidev_client; os_type=${process.env.DSH_ANTIGRAVITY_OS || "darwin"}; arch=${process.env.DSH_ANTIGRAVITY_ARCH || "arm64"}; cl=${cl})`;
4212
4257
  }
4213
4258
  function antigravityHeaders(token) {
4214
4259
  return {
@@ -4254,50 +4299,97 @@ function extractProjectId(data) {
4254
4299
  }
4255
4300
  }
4256
4301
  }
4257
- async function listCloudAICompanionProjects(token, fetchFn = fetch) {
4302
+ async function loadCodeAssistDetail(token, fetchFn = fetch, signal) {
4303
+ const metadata = {
4304
+ ideType: "ANTIGRAVITY",
4305
+ platform: "PLATFORM_UNSPECIFIED",
4306
+ pluginType: "GEMINI"
4307
+ };
4258
4308
  for (const endpoint of endpointCandidates()) try {
4259
- const response = await fetchFn(`${endpoint}/v1internal:listCloudAICompanionProjects`, {
4309
+ const response = await fetchFn(`${endpoint}/v1internal:loadCodeAssist`, {
4260
4310
  method: "POST",
4261
- headers: antigravityHeaders(token),
4262
- body: JSON.stringify({}),
4263
- signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
4311
+ headers: jsonHeaders(token),
4312
+ body: JSON.stringify({ metadata }),
4313
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)]) : AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
4264
4314
  });
4265
4315
  if (!response.ok) continue;
4266
- return extractProjectId(await response.json());
4316
+ const data = await response.json();
4317
+ const projectId = extractProjectId(data);
4318
+ const allowedTiers = Array.isArray(data.allowedTiers) ? data.allowedTiers : [];
4319
+ const ineligibleTiers = Array.isArray(data.ineligibleTiers) ? data.ineligibleTiers : [];
4320
+ return {
4321
+ currentTier: data.currentTier || null,
4322
+ paidTier: data.paidTier || null,
4323
+ allowedTiers,
4324
+ ineligibleTiers,
4325
+ projectId,
4326
+ raw: data
4327
+ };
4267
4328
  } catch {}
4268
4329
  }
4269
- async function loadCodeAssist(token, fetchFn = fetch) {
4270
- const cached = projectCache.get(token);
4271
- if (cached && cached.expiresAt > Date.now()) return cached.projectId;
4272
- const body = JSON.stringify({ metadata: {
4273
- ideType: "ANTIGRAVITY",
4274
- platform: "PLATFORM_UNSPECIFIED",
4275
- pluginType: "GEMINI"
4276
- } });
4330
+ async function onboardUser(token, fetchFn = fetch, signal) {
4331
+ const deadline = Date.now() + ONBOARD_TIMEOUT_MS;
4332
+ const body = JSON.stringify({
4333
+ tierId: FREE_TIER_ID,
4334
+ metadata: { ideType: "ANTIGRAVITY" }
4335
+ });
4277
4336
  for (const endpoint of endpointCandidates()) try {
4278
- const response = await fetchFn(`${endpoint}/v1internal:loadCodeAssist`, {
4337
+ const remainingTime = Math.max(1e3, deadline - Date.now());
4338
+ const timeoutSignal = AbortSignal.timeout(remainingTime);
4339
+ const callSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
4340
+ const response = await fetchFn(`${endpoint}/v1internal:onboardUser`, {
4279
4341
  method: "POST",
4280
- headers: antigravityHeaders(token),
4342
+ headers: jsonHeaders(token),
4281
4343
  body,
4282
- signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
4344
+ signal: callSignal
4283
4345
  });
4284
- if (!response.ok) continue;
4285
- const project = extractProjectId(await response.json());
4286
- if (project) {
4287
- projectCache.set(token, {
4288
- projectId: project,
4289
- expiresAt: Date.now() + PROJECT_CACHE_TTL_MS
4290
- });
4291
- return project;
4346
+ if (!response.ok) {
4347
+ const errorText = await response.text().catch(() => "");
4348
+ throw new Error(`onboardUser failed: ${response.status} ${response.statusText}: ${errorText}`);
4292
4349
  }
4293
- const listProj = await listCloudAICompanionProjects(token, fetchFn);
4294
- if (listProj) {
4295
- projectCache.set(token, {
4296
- projectId: listProj,
4297
- expiresAt: Date.now() + PROJECT_CACHE_TTL_MS
4350
+ let operation = await response.json();
4351
+ while (true) {
4352
+ if (operation.done === true) {
4353
+ if (operation.error) {
4354
+ const msg = operation.error.message || `Error code ${operation.error.code}`;
4355
+ throw new Error(`OnboardUser operation failed: ${msg}`);
4356
+ }
4357
+ return;
4358
+ }
4359
+ const waitMs = Math.min(ONBOARD_POLL_INTERVAL_MS, Math.max(100, deadline - Date.now()));
4360
+ if (Date.now() >= deadline) throw new Error(`onboardUser timed out after ${ONBOARD_TIMEOUT_MS}ms`);
4361
+ await new Promise((r) => setTimeout(r, waitMs));
4362
+ if (signal?.aborted) throw new Error("OAuth login cancelled");
4363
+ const operationName = operation.name || "";
4364
+ if (!operationName) throw new Error("onboardUser returned an operation without a name");
4365
+ const pollTime = Math.max(1e3, deadline - Date.now());
4366
+ const pollTimeoutSignal = AbortSignal.timeout(pollTime);
4367
+ const pollSignal = signal ? AbortSignal.any([signal, pollTimeoutSignal]) : pollTimeoutSignal;
4368
+ const pollResp = await fetchFn(`${endpoint}/v1internal/${operationName}`, {
4369
+ method: "GET",
4370
+ headers: jsonHeaders(token),
4371
+ signal: pollSignal
4298
4372
  });
4299
- return listProj;
4373
+ if (!pollResp.ok) {
4374
+ const pollErr = await pollResp.text().catch(() => "");
4375
+ throw new Error(`onboardUser operation poll failed: ${pollResp.status}: ${pollErr}`);
4376
+ }
4377
+ operation = await pollResp.json();
4300
4378
  }
4379
+ } catch (err) {
4380
+ if (Date.now() >= deadline || signal?.aborted) throw err;
4381
+ }
4382
+ }
4383
+ async function listCloudAICompanionProjects(token, fetchFn = fetch) {
4384
+ for (const endpoint of endpointCandidates()) try {
4385
+ const response = await fetchFn(`${endpoint}/v1internal:listCloudAICompanionProjects`, {
4386
+ method: "POST",
4387
+ headers: antigravityHeaders(token),
4388
+ body: JSON.stringify({}),
4389
+ signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
4390
+ });
4391
+ if (!response.ok) continue;
4392
+ return extractProjectId(await response.json());
4301
4393
  } catch {}
4302
4394
  }
4303
4395
  async function postJson(path, token, body, fetchFn = fetch) {
@@ -4365,50 +4457,63 @@ function parseCatalogModels(data) {
4365
4457
  }
4366
4458
  return list;
4367
4459
  }
4368
- async function fetchAccountQuota(store = new FileCredentialStore(), modelSettings, fetchFn = fetch) {
4369
- const { token, projectId: credentialProjectId } = await ensureApiKey(store, fetchFn);
4370
- const [assistResult, summaryResult] = await Promise.all([postJson("/v1internal:loadCodeAssist", token, { metadata: {
4371
- ideType: "ANTIGRAVITY",
4372
- platform: "PLATFORM_UNSPECIFIED",
4373
- pluginType: "GEMINI"
4374
- } }, fetchFn).catch(() => null), postJson("/v1internal:retrieveUserQuotaSummary", token, {}, fetchFn).catch(() => null)]);
4375
- const discoveredProject = assistResult ? extractProjectId(assistResult.data) : void 0;
4376
- const projectId = credentialProjectId || discoveredProject || "antigravity-default";
4377
- const modelsData = (await postJson("/v1internal:fetchAvailableModels", token, { project: projectId }, fetchFn).catch(() => null))?.data;
4378
- const { groups, description } = summaryResult ? parseQuotaSummary(summaryResult.data) : { groups: [] };
4379
- const catalogModels = modelsData ? parseCatalogModels(modelsData) : [];
4380
- const assistData = assistResult?.data || {};
4381
- const currentTier = assistData.currentTier;
4382
- const paidTier = assistData.paidTier;
4383
- const planLabel = paidTier?.name || currentTier?.name || void 0;
4384
- cachedQuota = {
4385
- projectId,
4386
- endpoint: summaryResult?.endpoint || ENDPOINT_FALLBACKS[0],
4387
- planLabel,
4388
- productTier: currentTier,
4389
- paidTier,
4390
- groups,
4391
- groupDescription: description,
4392
- models: catalogModels.map((m) => ({
4393
- modelId: m.id,
4394
- displayName: m.name,
4395
- description: m.description
4396
- })),
4397
- catalogModels,
4398
- fetchedAt: Date.now()
4399
- };
4400
- if (modelSettings && catalogModels.length > 0) {
4401
- const current = await modelSettings.read();
4402
- const isFirstTime = current.catalogModels.length === 0 && current.enabledModelIds.length === 0;
4403
- const catalogIds = new Set(catalogModels.map((m) => m.id));
4404
- const mergedEnabled = isFirstTime ? catalogModels.map((m) => m.id) : current.enabledModelIds.filter((id) => catalogIds.has(id));
4405
- await modelSettings.setCatalogModels(catalogModels, { enabledModelIds: mergedEnabled });
4406
- }
4407
- return cachedQuota;
4460
+ async function fetchAccountQuota(store = new FileCredentialStore(), modelSettings, fetchFn = fetch, force = false) {
4461
+ if (!force && cachedQuota && Date.now() - (cachedQuota.fetchedAt || 0) < 12e4) return cachedQuota;
4462
+ if (quotaFetchInFlight) return quotaFetchInFlight;
4463
+ quotaFetchInFlight = (async () => {
4464
+ try {
4465
+ const { token, projectId: credentialProjectId } = await ensureApiKey(store, fetchFn);
4466
+ const [assistResult, summaryResult] = await Promise.all([postJson("/v1internal:loadCodeAssist", token, { metadata: {
4467
+ ideType: "ANTIGRAVITY",
4468
+ platform: "PLATFORM_UNSPECIFIED",
4469
+ pluginType: "GEMINI"
4470
+ } }, fetchFn).catch(() => null), postJson("/v1internal:retrieveUserQuotaSummary", token, {}, fetchFn).catch(() => null)]);
4471
+ const discoveredProject = assistResult ? extractProjectId(assistResult.data) : void 0;
4472
+ const projectId = credentialProjectId || discoveredProject || "antigravity-default";
4473
+ const modelsData = (await postJson("/v1internal:fetchAvailableModels", token, { project: projectId }, fetchFn).catch(() => null))?.data;
4474
+ const { groups, description } = summaryResult ? parseQuotaSummary(summaryResult.data) : { groups: [] };
4475
+ const catalogModels = modelsData ? parseCatalogModels(modelsData) : [];
4476
+ const assistData = assistResult?.data || {};
4477
+ const currentTier = assistData.currentTier;
4478
+ const paidTier = assistData.paidTier;
4479
+ const planLabel = paidTier?.name || currentTier?.name || void 0;
4480
+ cachedQuota = {
4481
+ projectId,
4482
+ endpoint: summaryResult?.endpoint || ENDPOINT_FALLBACKS[0],
4483
+ planLabel,
4484
+ productTier: currentTier,
4485
+ paidTier,
4486
+ groups,
4487
+ groupDescription: description,
4488
+ models: catalogModels.map((m) => ({
4489
+ modelId: m.id,
4490
+ displayName: m.name,
4491
+ description: m.description
4492
+ })),
4493
+ catalogModels,
4494
+ fetchedAt: Date.now()
4495
+ };
4496
+ if (modelSettings && catalogModels.length > 0) {
4497
+ const current = await modelSettings.read();
4498
+ const isFirstTime = current.catalogModels.length === 0 && current.enabledModelIds.length === 0;
4499
+ const catalogIds = new Set(catalogModels.map((m) => m.id));
4500
+ const mergedEnabled = isFirstTime ? catalogModels.map((m) => m.id) : current.enabledModelIds.filter((id) => catalogIds.has(id));
4501
+ await modelSettings.setCatalogModels(catalogModels, { enabledModelIds: mergedEnabled });
4502
+ }
4503
+ return cachedQuota;
4504
+ } finally {
4505
+ quotaFetchInFlight = null;
4506
+ }
4507
+ })();
4508
+ return quotaFetchInFlight;
4408
4509
  }
4409
4510
  function getCachedQuota() {
4410
4511
  return cachedQuota;
4411
4512
  }
4513
+ function clearCachedQuota() {
4514
+ cachedQuota = void 0;
4515
+ quotaFetchInFlight = null;
4516
+ }
4412
4517
  //#endregion
4413
4518
  //#region src/host/antigravity/oauth.ts
4414
4519
  let webLoginFlow = { status: "idle" };
@@ -4553,7 +4658,36 @@ function startCallbackServer(expectedState) {
4553
4658
  });
4554
4659
  });
4555
4660
  }
4556
- async function exchangeOAuthCode(code, verifier, callbackUrl, fetchFn = fetch) {
4661
+ function extractGoogleValidationUrl(text) {
4662
+ const match = /https:\/\/[^\s"'><]+/i.exec(text);
4663
+ return match ? match[0] : void 0;
4664
+ }
4665
+ function assertFreeTierEligible(payload) {
4666
+ if (payload.allowedTiers?.some((tier) => tier.id === "free-tier") === true) return;
4667
+ const ineligibility = payload.ineligibleTiers?.find((c) => c.tierId === FREE_TIER_ID);
4668
+ if (!ineligibility?.reasonMessage) return;
4669
+ const validation = ineligibility.validationUrl ? `\nValidation URL: ${ineligibility.validationUrl}` : "";
4670
+ const err = /* @__PURE__ */ new Error(`${ineligibility.reasonMessage}${validation}`);
4671
+ if (ineligibility.validationUrl) err.validationUrl = ineligibility.validationUrl;
4672
+ throw err;
4673
+ }
4674
+ async function discoverAntigravityProject(token, fetchFn = fetch, signal, onProgress) {
4675
+ onProgress?.("正在检查 Cloud Code Assist 账号状态...");
4676
+ const initial = await loadCodeAssistDetail(token, fetchFn, signal);
4677
+ if (!initial) throw new Error("无法连接到 Cloud Code Assist 服务,请检查网络连接");
4678
+ assertFreeTierEligible(initial);
4679
+ if (initial.allowedTiers?.some((tier) => tier.id === "free-tier") === true && !initial.currentTier) {
4680
+ onProgress?.("正在为新账号开通 Antigravity 免费额度...");
4681
+ await onboardUser(token, fetchFn, signal);
4682
+ onProgress?.("正在获取专属项目 (Project ID)...");
4683
+ const refreshed = await loadCodeAssistDetail(token, fetchFn, signal);
4684
+ if (refreshed?.projectId) return refreshed.projectId;
4685
+ } else if (initial.projectId) return initial.projectId;
4686
+ const fallback = await listCloudAICompanionProjects(token, fetchFn);
4687
+ if (fallback) return fallback;
4688
+ return initial.projectId;
4689
+ }
4690
+ async function exchangeOAuthCode(code, verifier, callbackUrl, fetchFn = fetch, signal, onProgress) {
4557
4691
  const tokenResponse = await fetchFn(TOKEN_URL, {
4558
4692
  method: "POST",
4559
4693
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -4564,7 +4698,8 @@ async function exchangeOAuthCode(code, verifier, callbackUrl, fetchFn = fetch) {
4564
4698
  grant_type: "authorization_code",
4565
4699
  redirect_uri: callbackUrl,
4566
4700
  code_verifier: verifier
4567
- }).toString()
4701
+ }).toString(),
4702
+ signal
4568
4703
  });
4569
4704
  if (!tokenResponse.ok) throw new Error(`Token exchange failed: ${sanitizeOAuthProviderError(await tokenResponse.text())}`);
4570
4705
  const tokenData = await tokenResponse.json();
@@ -4572,7 +4707,7 @@ async function exchangeOAuthCode(code, verifier, callbackUrl, fetchFn = fetch) {
4572
4707
  const accessToken = typeof tokenData.access_token === "string" ? tokenData.access_token : "";
4573
4708
  const expiresIn = typeof tokenData.expires_in === "number" ? tokenData.expires_in : 3600;
4574
4709
  if (!refreshToken) throw new Error("No refresh token received. Re-run login and allow offline access.");
4575
- const [email, discoveredProject] = await Promise.all([getUserEmail(accessToken, fetchFn), loadCodeAssist(accessToken, fetchFn).catch(() => void 0)]);
4710
+ const [email, discoveredProject] = await Promise.all([getUserEmail(accessToken, fetchFn), discoverAntigravityProject(accessToken, fetchFn, signal, onProgress)]);
4576
4711
  return {
4577
4712
  refresh: refreshToken,
4578
4713
  refresh_token: refreshToken,
@@ -4584,7 +4719,7 @@ async function exchangeOAuthCode(code, verifier, callbackUrl, fetchFn = fetch) {
4584
4719
  email
4585
4720
  };
4586
4721
  }
4587
- async function beginWebLogin(store, fetchFn = fetch) {
4722
+ async function beginWebLogin(store, fetchFn = fetch, signal) {
4588
4723
  if (webLoginFlow.status === "pending") return { ...webLoginFlow };
4589
4724
  const { verifier, challenge } = generatePKCE();
4590
4725
  const state = base64Url(randomBytes(32));
@@ -4604,20 +4739,28 @@ async function beginWebLogin(store, fetchFn = fetch) {
4604
4739
  prompt: "consent"
4605
4740
  }).toString()}`,
4606
4741
  startedAt: Date.now(),
4607
- error: void 0
4742
+ progress: "等待浏览器授权...",
4743
+ error: void 0,
4744
+ validationUrl: void 0
4608
4745
  };
4609
4746
  (async () => {
4610
4747
  try {
4611
4748
  const { code, state: returnedState } = await waitForCode();
4612
4749
  if (returnedState !== state) throw new Error("OAuth state mismatch");
4613
- const credentials = await exchangeOAuthCode(code, verifier, callbackUrl, fetchFn);
4750
+ const credentials = await exchangeOAuthCode(code, verifier, callbackUrl, fetchFn, signal, (stage) => {
4751
+ webLoginFlow.progress = stage;
4752
+ });
4614
4753
  await store.write(credentials);
4615
4754
  webLoginFlow.status = "complete";
4616
4755
  webLoginFlow.email = credentials.email;
4617
4756
  webLoginFlow.completedAt = Date.now();
4757
+ webLoginFlow.progress = "授权成功";
4618
4758
  } catch (error) {
4619
4759
  webLoginFlow.status = "error";
4620
- webLoginFlow.error = error instanceof Error ? error.message : String(error);
4760
+ const errText = error instanceof Error ? error.message : String(error);
4761
+ webLoginFlow.error = errText;
4762
+ const validationUrl = error?.validationUrl || extractGoogleValidationUrl(errText);
4763
+ if (validationUrl) webLoginFlow.validationUrl = validationUrl;
4621
4764
  webLoginFlow.completedAt = Date.now();
4622
4765
  } finally {
4623
4766
  server.close();
@@ -4653,7 +4796,8 @@ async function refreshAntigravityToken(credentials, fetchFn = fetch) {
4653
4796
  access: accessToken,
4654
4797
  access_token: accessToken,
4655
4798
  expires: Date.now() + expiresIn * 1e3 - 300 * 1e3,
4656
- expires_at: Date.now() + expiresIn * 1e3 - 300 * 1e3
4799
+ expires_at: Date.now() + expiresIn * 1e3 - 300 * 1e3,
4800
+ projectId: credentials.projectId
4657
4801
  };
4658
4802
  }
4659
4803
  async function ensureApiKey(store, fetchFn = fetch) {
@@ -4669,7 +4813,7 @@ async function ensureApiKey(store, fetchFn = fetch) {
4669
4813
  projectId: credentials.projectId
4670
4814
  };
4671
4815
  }
4672
- async function loginAndSave(store, signal, onUrl, fetchFn = fetch) {
4816
+ async function loginAndSave(store, signal, onUrl, fetchFn = fetch, onProgress) {
4673
4817
  const { verifier, challenge } = generatePKCE();
4674
4818
  const state = base64Url(randomBytes(32));
4675
4819
  const { server, waitForCode } = await startCallbackServer(state);
@@ -4691,7 +4835,7 @@ async function loginAndSave(store, signal, onUrl, fetchFn = fetch) {
4691
4835
  if (signal?.aborted) throw new Error("OAuth login aborted");
4692
4836
  const { code, state: returnedState } = await waitForCode();
4693
4837
  if (returnedState !== state) throw new Error("OAuth state mismatch");
4694
- const credentials = await exchangeOAuthCode(code, verifier, callbackUrl, fetchFn);
4838
+ const credentials = await exchangeOAuthCode(code, verifier, callbackUrl, fetchFn, signal, onProgress);
4695
4839
  await store.write(credentials);
4696
4840
  return credentials;
4697
4841
  } finally {
@@ -4839,6 +4983,12 @@ function contentToUserParts(content) {
4839
4983
  else if (isRecord(block) && block.type === "image") {
4840
4984
  const img = imageBlockToPart(block);
4841
4985
  if (img) parts.push(img);
4986
+ } else if (isRecord(block) && block.type === "file") {
4987
+ const file = isRecord(block.attachment) ? block.attachment : block;
4988
+ const name = asString(file.name) || asString(file.filename) || "unnamed";
4989
+ const size = typeof file.byteSize === "number" ? ` (${file.byteSize} bytes)` : "";
4990
+ const savedPath = asString(file.savedPath) ? ` path: ${asString(file.savedPath)}` : "";
4991
+ parts.push({ text: sanitizeText(`[File attachment: ${name}${size}${savedPath}]`) });
4842
4992
  }
4843
4993
  return parts;
4844
4994
  }
@@ -4847,6 +4997,10 @@ function toolResultText(blocks) {
4847
4997
  return blocks.map((block) => {
4848
4998
  if (!isRecord(block)) return "";
4849
4999
  if (block.type === "text" && typeof block.text === "string") return sanitizeText(block.text);
5000
+ if (block.type === "file") {
5001
+ const file = isRecord(block.attachment) ? block.attachment : block;
5002
+ return sanitizeText(`[file: ${asString(file.name) || asString(file.filename) || "unnamed"}${typeof file.byteSize === "number" ? ` (${file.byteSize} bytes)` : ""}]`);
5003
+ }
4850
5004
  if (block.type === "tool-result") return toolResultText(block.content);
4851
5005
  return "";
4852
5006
  }).join("");
@@ -5315,6 +5469,7 @@ var AntigravityAdapter = class extends LlmAdapter {
5315
5469
  inputModalities: model.inputModalities,
5316
5470
  context: { contextWindow: overrides[model.id] || model.contextWindow },
5317
5471
  defaultMaxTokens: model.maxTokens,
5472
+ systemPromptUpdate: "in-history",
5318
5473
  ...model.reasoningEfforts ? { reasoning: {
5319
5474
  efforts: efforts.map((effort) => ({
5320
5475
  id: ReasoningEffortId(effort),
@@ -5482,6 +5637,10 @@ function registerAntigravityRoutes(ctx, store, modelSettings, preferences, fetch
5482
5637
  try {
5483
5638
  if (path === "status" || path === "") {
5484
5639
  if (request.method !== "GET") return sendMethodNotAllowed(response);
5640
+ const credentials = await store.read();
5641
+ const authenticated = !!(credentials?.access || credentials?.access_token);
5642
+ const cached = getCachedQuota();
5643
+ if (authenticated && (!cached || Date.now() - (cached.fetchedAt || 0) > 12e4)) await fetchAccountQuota(store, modelSettings, fetchFn).catch(() => void 0);
5485
5644
  return sendJson(response, 200, {
5486
5645
  ok: true,
5487
5646
  value: await getAntigravityWebStatus(store, modelSettings, preferences)
@@ -5503,7 +5662,7 @@ function registerAntigravityRoutes(ctx, store, modelSettings, preferences, fetch
5503
5662
  }
5504
5663
  if (path === "quota") {
5505
5664
  if (request.method !== "GET" && request.method !== "POST") return sendMethodNotAllowed(response);
5506
- const quota = await fetchAccountQuota(store, modelSettings, fetchFn);
5665
+ const quota = await fetchAccountQuota(store, modelSettings, fetchFn, true);
5507
5666
  return sendJson(response, 200, {
5508
5667
  ok: true,
5509
5668
  value: {
@@ -5541,6 +5700,7 @@ function registerAntigravityRoutes(ctx, store, modelSettings, preferences, fetch
5541
5700
  if (path === "logout") {
5542
5701
  if (request.method !== "POST") return sendMethodNotAllowed(response);
5543
5702
  await store.delete();
5703
+ clearCachedQuota();
5544
5704
  return sendJson(response, 200, {
5545
5705
  ok: true,
5546
5706
  value: await getAntigravityWebStatus(store, modelSettings)
@@ -5628,4 +5788,4 @@ function localWebServerBaseUrl(host, port) {
5628
5788
  return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
5629
5789
  }
5630
5790
  //#endregion
5631
- 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 };
5791
+ export { AntigravityAdapter, CodexChatGptAdapter, FileCredentialStore, FileModelSettingsStore, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, beginWebLogin, clearCachedQuota, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, credentialPath, detectSystemProxy, fetchAccountQuota, getCachedQuota, inject, loginAndSave, mapCodexUsage, modelSettingsPath, parseCodexUsage, parseResponsesStream, refreshAntigravityToken };
@@ -1 +1 @@
1
- {"version":3,"file":"AntigravityComposerQuota.d.ts","sourceRoot":"","sources":["../../../../src/client/antigravity/AntigravityComposerQuota.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAmB7C,KAAK,KAAK,GAAG,YAAY,CAAC,0BAA0B,CAAC,GACnD,WAAW,CAAC,OAAO,cAAc,CAAC,GAAG;IACnC,SAAS,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;IAC7C,kBAAkB,EAAE,MAAM,IAAI,CAAA;CAC/B,CAAA;AAqCH,wBAAgB,wBAAwB,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAgF3G"}
1
+ {"version":3,"file":"AntigravityComposerQuota.d.ts","sourceRoot":"","sources":["../../../../src/client/antigravity/AntigravityComposerQuota.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAmB7C,KAAK,KAAK,GAAG,YAAY,CAAC,0BAA0B,CAAC,GACnD,WAAW,CAAC,OAAO,cAAc,CAAC,GAAG;IACnC,SAAS,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;IAC7C,kBAAkB,EAAE,MAAM,IAAI,CAAA;CAC/B,CAAA;AAqCH,wBAAgB,wBAAwB,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAyF3G"}
@@ -1 +1 @@
1
- {"version":3,"file":"AntigravitySection.d.ts","sourceRoot":"","sources":["../../../../src/client/antigravity/AntigravitySection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAM/D,UAAU,KAAK;IACb,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;CAChC;AAiBD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD;AAuBD,wBAAgB,kBAAkB,CAAC,EAAE,aAAa,EAAE,kBAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,YAAY,CAuanG"}
1
+ {"version":3,"file":"AntigravitySection.d.ts","sourceRoot":"","sources":["../../../../src/client/antigravity/AntigravitySection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAM/D,UAAU,KAAK;IACb,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;CAChC;AAiBD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD;AAuBD,wBAAgB,kBAAkB,CAAC,EAAE,aAAa,EAAE,kBAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,YAAY,CAmdnG"}