@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.
- package/CHANGELOG.md +24 -24
- package/LICENSE +21 -21
- package/README.md +169 -169
- package/bin/antigravity-login.mjs +15 -4
- package/cordis.patch.yml +6 -6
- package/lib/client.js +110 -12
- package/lib/client.js.map +1 -1
- package/lib/index.js +253 -93
- package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts.map +1 -1
- package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +65 -1
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/compat.d.ts +11 -0
- package/lib/types/compat.d.ts.map +1 -1
- package/lib/types/host/antigravity/adapter.d.ts +1 -0
- package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
- package/lib/types/host/antigravity/client.d.ts +23 -1
- package/lib/types/host/antigravity/client.d.ts.map +1 -1
- package/lib/types/host/antigravity/mapper.d.ts +1 -0
- package/lib/types/host/antigravity/mapper.d.ts.map +1 -1
- package/lib/types/host/antigravity/oauth.d.ts +9 -3
- package/lib/types/host/antigravity/oauth.d.ts.map +1 -1
- package/lib/types/host/antigravity/routes.d.ts.map +1 -1
- package/lib/types/host/antigravity/types.d.ts +6 -0
- package/lib/types/host/antigravity/types.d.ts.map +1 -1
- package/lib/types/host/model-catalog.d.ts.map +1 -1
- package/lib/types/host/preferences.d.ts.map +1 -1
- package/lib/types/host/proxy-manager.d.ts.map +1 -1
- package/lib/types/host/responses-mapper.d.ts +1 -0
- package/lib/types/host/responses-mapper.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/contracts.d.ts +9 -0
- package/lib/types/shared/contracts.d.ts.map +1 -1
- package/lib/types/shared/preferences.d.ts +2 -0
- package/lib/types/shared/preferences.d.ts.map +1 -1
- 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 (
|
|
1248
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
4309
|
+
const response = await fetchFn(`${endpoint}/v1internal:loadCodeAssist`, {
|
|
4260
4310
|
method: "POST",
|
|
4261
|
-
headers:
|
|
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
|
-
|
|
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
|
|
4270
|
-
const
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
ideType: "ANTIGRAVITY"
|
|
4274
|
-
|
|
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
|
|
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:
|
|
4342
|
+
headers: jsonHeaders(token),
|
|
4281
4343
|
body,
|
|
4282
|
-
signal:
|
|
4344
|
+
signal: callSignal
|
|
4283
4345
|
});
|
|
4284
|
-
if (!response.ok)
|
|
4285
|
-
|
|
4286
|
-
|
|
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
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
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
|
-
|
|
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),
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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"}
|