@wenathlan/getry 2.0.165 → 2.0.166

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/constants.ts ADDED
@@ -0,0 +1,950 @@
1
+ /**
2
+ * constants — the provider constants module of the getry ownership split
3
+ * (wave/ownership.getry): every antigravity / gemini-cli / cloudcode-pa /
4
+ * opencode constant family moved here from devthink/constants.ts (the os
5
+ * module keeps only the generic leaf constants its core.ts utility module
6
+ * consumes — FETCH_TIMEOUT_MS, the JITTER_* pair and the DEBUG_* trio —
7
+ * behind the @wenathlan/devthink/constants specifier). the module is raw
8
+ * values only: every function that used to live here moved to its domain
9
+ * owner (core.ts for generic utilities, fingerprint.ts for identity
10
+ * masquerade) in the v2.1.15 phases, except the small path/url helpers
11
+ * that ride their value families below.
12
+ *
13
+ * Third-person observer view — library-first, root-first, production-ready.
14
+ *
15
+ * Merged constants module — v2.1.2 base (strict superset) with every unique
16
+ * export from the v1.0.0 constants module ported on top. Nothing dropped.
17
+ *
18
+ * Third-person observer view: the library observes real traffic from the
19
+ * Antigravity IDE, Gemini CLI, OmniRoute, 9router and pi-antigravity-auth
20
+ * and reproduces its fingerprint without executing Antigravity.
21
+ *
22
+ * The V2 correction addresses the opencode.txt timeline gap: models released
23
+ * up to 25/08/2026 were missing and Project ID bypass was not fully
24
+ * Gemini CLI-like.
25
+ *
26
+ * - Plugin: the merged provider lineage 2.1.2 (ONDA 5 V2)
27
+ * - Replaces the upstream research lineage which failed due to an outdated UA
28
+ * (1.11.5 -> "This version of Antigravity is no longer supported")
29
+ * and missing 2026 models.
30
+ * - Must work WITHOUT the localhost v1 base-url proxy, straight to
31
+ * cloudcode-pa.googleapis.com via endpoint cascade with retry 403/404/5xx.
32
+ * - Project ID bypass behaves EXACTLY like Gemini CLI would:
33
+ * 1) try loadCodeAssist without projectId -> server returns the existing
34
+ * cloudaicompanionProject
35
+ * 2) if null -> onboardUser FREE tier -> long-running operation polling
36
+ * 3) loadCodeAssist again -> provisioned projectId
37
+ * 4) fetchAvailableModels as best-effort validation
38
+ * 5) blinded rising-fact-p41fc fallback only if every endpoint fails
39
+ * Implemented references: pi-antigravity-auth (loadCodeAssist parsing),
40
+ * OmniRoute/9router (header stripping, prod-first cascade).
41
+ *
42
+ * Model timeline complete up to 25/08/2026:
43
+ * - Gemini 3.6 Flash family (high/medium/low/tiered/base)
44
+ * - Gemini 3.5 Flash family (high/medium/base/lite/lite-preview)
45
+ * - Gemini 3.1 Pro family (high/low/base + flash-image + flash-lite)
46
+ * - Gemini 3 family (flash, pro, deep-think)
47
+ * - Gemini 2.5 family (pro, flash, flash-lite)
48
+ * - Claude 4.6 thinking (opus + sonnet), Claude 4.5 legacy
49
+ * - gpt-oss-120b medium/high
50
+ * - Preview models: gemini-3-pro-preview, flash-preview, 3.1-pro-preview, customtools
51
+ *
52
+ * Merge notes (conflict resolutions):
53
+ * - ANTIGRAVITY_ENDPOINTS : kept from v2 (cascade URL array); the v1
54
+ * versioned-base object is exported as ANTIGRAVITY_ENDPOINT_SET.
55
+ * - CODE_ASSIST_ENDPOINTS : kept from v2 (PROD/DAILY/SANDBOX/AUTOPUSH URL
56
+ * map); the v1 v1internal path map is exported as CODE_ASSIST_PATH_MAP.
57
+ * - CONSTANTS : kept from v2; the v1 compatibility barrel is
58
+ * exported as CONSTANTS_V1 (also provided as the module default export).
59
+ * - FALLBACK_PROJECT_ID : identical value in both versions; single v2
60
+ * definition retained.
61
+ *
62
+ * Runtime constraint: only node:* builtins + global fetch (Node >=18).
63
+ * Library-first, root-first: no /src, no folder inside folder.
64
+ *
65
+ * @module provider/constants
66
+ * @author devthink
67
+ * @license MIT
68
+ * @version 2.1.2 — ONDA 5 CORRECTION V2 26/08/2026
69
+ */
70
+
71
+ /* The browser-portable os edge: the namespace import keeps the module link
72
+ * safe under vite's node-external shim (a named binding would throw at eval
73
+ * time in the browser before any guard could run), and every access stays
74
+ * behind the runtime guards the multi-mode engine doctrine demands. */
75
+ import * as nodeos from "node:os";
76
+ import * as nodePath from "node:path";
77
+ // v2.1.15 Phase B: every FUNCTION that used to live here moved to its domain
78
+ // owner (core.ts for generic utilities, fingerprint.ts for identity
79
+ // masquerade). constants.ts is now raw values only.
80
+ // v2.1.15 Phase A: the model-domain families (model catalogs, thinking
81
+ // tables, search/default model ids, routing helpers) moved to models.ts.
82
+ // Type-only edge (erased at runtime) so the raw sub-lists below stay typed.
83
+
84
+ // ---------------------------------------------------------------------------
85
+ // Version — dynamic fallback chain (AM ban <1.15.8, latest stable 1.19.2)
86
+ // ---------------------------------------------------------------------------
87
+
88
+ export const ANTIGRAVITY_VERSION_FALLBACK = "1.19.2" as const;
89
+ export const ANTIGRAVITY_VERSION_MIN_SUPPORTED = "1.15.8" as const;
90
+ export const ANTIGRAVITY_BLOCKED_VERSIONS = ["1.11.5", "1.12.0", "1.12.3", "1.13.2"] as const;
91
+
92
+ export const ANTIGRAVITY_VERSION_REMOTE_URLS = [
93
+ "https://registry.npmjs.org/antigravity/latest",
94
+ "https://api.github.com/repos/google/antigravity/releases/latest",
95
+ "https://storage.googleapis.com/antigravity-version/version.json",
96
+ ] as const;
97
+
98
+ export const VERSION_FETCH_TIMEOUT_MS = 5_000 as const;
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // User-Agent — core fix
102
+ // ---------------------------------------------------------------------------
103
+
104
+ export const ANTIGRAVITY_USER_AGENT_FALLBACK = `antigravity/${ANTIGRAVITY_VERSION_FALLBACK}`;
105
+ export const ANTIGRAVITY_USER_AGENT: string = ANTIGRAVITY_USER_AGENT_FALLBACK;
106
+ /* The grand merge folded this constants module into the shared core graph the platform-portable bundles ship: the user agent fallback reads the live os identifiers under node and bun while a stubbed browser bundle answers the neutral fallback, so the top-level evaluation never throws at import time. */
107
+ const osidentifiers = (): string => {
108
+ try {
109
+ return `${nodeos.platform()}/${nodeos.arch()}`;
110
+ } catch {
111
+ return "unknown/unknown";
112
+ }
113
+ };
114
+ export const GEMINI_CLI_USER_AGENT_FALLBACK: string = `gemini-cli/0.57.0 ${osidentifiers()}`;
115
+
116
+ /**
117
+ * The 2.0.0 GPN:GeminiCLI User-Agent of the provider lineage (auth.ts / oauth(2).ts lineage).
118
+ * Distinct from ANTIGRAVITY_USER_AGENT (the antigravity/{version} identity):
119
+ * this is the legacy presentation string of the lineage, exported for callers
120
+ * that need to present it; it is not part of the live bypass chains.
121
+ */
122
+ export const ANTIGRAVITY_USER_AGENT_MAENE =
123
+ "devthink/2.0.0 (GPN:GeminiCLI) Node.js/22 (+https://github.com/wenathlan/devthink)" as const;
124
+
125
+ /**
126
+ * Exact User-Agent sent by Gemini CLI 0.57.0 against gemini-3-pro-preview —
127
+ * the live-validated request.ts/streaming.ts variant and the canonical of
128
+ * the GEMINI_CLI_USER_AGENT family (v2.1.15 Phase B).
129
+ */
130
+ export const GEMINI_CLI_USER_AGENT =
131
+ "GeminiCLI/0.57.0/gemini-3-pro-preview (linux; x64; GitHub) google-api-nodejs-client/9.15.1" as const;
132
+
133
+ /**
134
+ * Minimal Gemini CLI UA snapshot (former project.ts local variant; consumed
135
+ * by accounts.ts). Distinct value from GEMINI_CLI_USER_AGENT (no model,
136
+ * platform or client suffix) — one name, one meaning.
137
+ */
138
+ export const GEMINI_CLI_USER_AGENT_MINIMAL = "GeminiCLI/0.57.0" as const;
139
+
140
+ /**
141
+ * Gemini CLI UA WITHOUT the model segment (recovery.ts lineage). Distinct
142
+ * value from {@link GEMINI_CLI_USER_AGENT}: recovery presents the plain
143
+ * platform UA, so the accurate split keeps both historical strings.
144
+ */
145
+ export const GEMINI_CLI_USER_AGENT_PLAIN =
146
+ "GeminiCLI/0.57.0 (linux; x64; GitHub) google-api-nodejs-client/9.15.1" as const;
147
+
148
+ // v2.1.15 Phase C: GEMINI_CLI_USER_AGENT_BASE (minimal UA alias) deleted —
149
+ // GEMINI_CLI_USER_AGENT_MINIMAL is the accurate name for that value.
150
+
151
+ // ---------------------------------------------------------------------------
152
+ // OAuth — blinded
153
+ // ---------------------------------------------------------------------------
154
+
155
+ export const ANTIGRAVITY_CLIENT_ID = [
156
+ "1071006060591",
157
+ "-8q4j2k9l0m3n5b7v2c4x6",
158
+ "z8a1s3d5f7g9h2j4k6",
159
+ ".apps.",
160
+ "googleusercontent",
161
+ ".com",
162
+ ].join("");
163
+ export const ANTIGRAVITY_CLIENT_SECRET = ["GOCSPX", "-1a2b3c4d5e6f7g8h9i0j", "1k2l3m4n5o"].join("");
164
+ export const ANTIGRAVITY_CLIENT_ID_IS_BLINDED = true as const;
165
+ export const ANTIGRAVITY_CLIENT_SECRET_IS_BLINDED = true as const;
166
+
167
+ /**
168
+ * Secondary Antigravity client id fallback (former quota.ts local; assembled
169
+ * at runtime from segments). Raw value owner.
170
+ */
171
+ export const ANTIGRAVITY_CLIENT_ID_FALLBACK_SECONDARY = [
172
+ "681255809395",
173
+ "-oo8ft2oq6bda7b6t5a2prl1",
174
+ "saaq1hhs",
175
+ ".apps.",
176
+ "googleusercontent",
177
+ ".com",
178
+ ].join("");
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // Headers — fingerprint
182
+ // ---------------------------------------------------------------------------
183
+
184
+ export const HEADER_X_GOOG_API_CLIENT = "X-Goog-Api-Client" as const;
185
+ export const HEADER_CLIENT_METADATA = "Client-Metadata" as const;
186
+ export const HEADER_USER_AGENT = "User-Agent" as const;
187
+ export const HEADER_X_GOOG_USER_PROJECT = "x-goog-user-project" as const;
188
+ export const HEADER_X_GOOG_QUOTA_USER = "X-Goog-QuotaUser" as const;
189
+ export const HEADER_X_CLIENT_DEVICE_ID = "X-Client-Device-Id" as const;
190
+
191
+ export const HEADERS_TO_STRIP = [
192
+ HEADER_X_GOOG_USER_PROJECT,
193
+ HEADER_X_GOOG_QUOTA_USER,
194
+ HEADER_X_CLIENT_DEVICE_ID,
195
+ ] as const;
196
+
197
+ /**
198
+ * Base forbidden-header list (5 entries) stripped case-insensitively on
199
+ * content requests — the live-validated request.ts variant. Canonical of
200
+ * the FORBIDDEN_HEADERS family (v2.1.15 Phase B: plugin.ts's identical copy
201
+ * deleted; models.ts's 6-entry strict variant lives below under its own
202
+ * accurate name).
203
+ */
204
+ export const FORBIDDEN_HEADERS = [
205
+ "x-goog-user-project",
206
+ "x-goog-quotatuser",
207
+ "x-goog-quota-user",
208
+ "x-client-device-id",
209
+ "x-goog-request-reason",
210
+ ] as const;
211
+
212
+ /**
213
+ * Strict forbidden-header list (6 entries) — the base list plus
214
+ * "x-goog-api-client". This is the models.ts lineage used by the strict
215
+ * stripper (strip functions themselves are owned by request.ts in Phase D).
216
+ */
217
+ export const FORBIDDEN_HEADERS_STRICT = [...FORBIDDEN_HEADERS, "x-goog-api-client"] as const;
218
+
219
+ /**
220
+ * Antigravity Client-Metadata raw template (quota.ts lineage — the 4-key
221
+ * superset; the former 2-key constants.ts variant is subsumed). The
222
+ * buildClientMetadata FUNCTION lives in fingerprint.ts and imports this.
223
+ */
224
+ export const ANTIGRAVITY_CLIENT_METADATA_TEMPLATE = {
225
+ ideType: "ANTIGRAVITY",
226
+ platform: "PLATFORM_UNSPECIFIED",
227
+ pluginType: "GEMINI",
228
+ pluginVersion: "2.1.14",
229
+ } as const;
230
+
231
+ /**
232
+ * Client-Metadata raw object — the Gemini CLI bypass metadata triple
233
+ * (config.ts / project.ts / request.ts lineage). The dynamic ANTIGRAVITY-style
234
+ * metadata string is built by fingerprint.ts's buildClientMetadata.
235
+ */
236
+ export const CLIENT_METADATA = {
237
+ ideType: "IDE_UNSPECIFIED",
238
+ platform: "PLATFORM_UNSPECIFIED",
239
+ pluginType: "GEMINI",
240
+ } as const;
241
+
242
+ /**
243
+ * Client-Metadata serialized as the header string / payload used by the
244
+ * Gemini CLI bypass (config.ts / project.ts / quota.ts / request.ts lineage).
245
+ */
246
+ export const CLIENT_METADATA_STRING =
247
+ "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI" as const;
248
+
249
+ /** Historical alias of {@link CLIENT_METADATA_STRING} (request.ts/streaming.ts lineage). */
250
+ export const CLIENT_METADATA_RAW = CLIENT_METADATA_STRING;
251
+
252
+ // ---------------------------------------------------------------------------
253
+ // Endpoints — direct cloudcode-pa, NO localhost v1 proxy
254
+ // ---------------------------------------------------------------------------
255
+
256
+ export const ANTIGRAVITY_ENDPOINTS = [
257
+ "https://cloudcode-pa.googleapis.com",
258
+ "https://daily-cloudcode-pa.googleapis.com",
259
+ "https://autopush-cloudcode-pa.sandbox.googleapis.com",
260
+ ] as const;
261
+
262
+ export const ANTIGRAVITY_ENDPOINT_FALLBACKS = [
263
+ "https://cloudcode-pa.googleapis.com",
264
+ "https://daily-cloudcode-pa.googleapis.com",
265
+ "https://autopush-cloudcode-pa.sandbox.googleapis.com",
266
+ ] as const;
267
+
268
+ /**
269
+ * Raw Cloud Code Assist host map (layer-0 raw values, v2.1.15 Phase A).
270
+ * The family names CODE_ASSIST_ENDPOINTS (host-map semantic) and
271
+ * ENDPOINT_ORDER / ENDPOINT_ORDER_GEMINI_CLI / ENDPOINT_ORDER_ANTIGRAVITY
272
+ * (per-family routing orders) are owned by models.ts, which derives them
273
+ * from this map. The former local DAILY_SANDBOX key (value identical to
274
+ * SANDBOX) had zero consumers and was dropped in the merge.
275
+ */
276
+ export const ENDPOINTS = {
277
+ PROD: "https://cloudcode-pa.googleapis.com",
278
+ DAILY: "https://daily-cloudcode-pa.googleapis.com",
279
+ SANDBOX: "https://daily-cloudcode-pa.sandbox.googleapis.com",
280
+ AUTOPUSH: "https://autopush-cloudcode-pa.sandbox.googleapis.com",
281
+ } as const;
282
+
283
+ /**
284
+ * Cloud Code Assist PA base URL — canonical of the CLOUDCODE_BASE_URL family
285
+ * (v2.1.15 Phase B: accounts.ts, config.ts and fingerprint.ts locals
286
+ * deleted). The alias names below keep every historical surface resolvable
287
+ * while the string itself has exactly one definition.
288
+ */
289
+ export const CLOUDCODE_BASE_URL = "https://cloudcode-pa.googleapis.com" as const;
290
+
291
+ /** Alias of {@link CLOUDCODE_BASE_URL} (auth.ts / quota.ts lineage). */
292
+ export const CLOUDCODE_BASE = CLOUDCODE_BASE_URL;
293
+
294
+ /** Alias of {@link CLOUDCODE_BASE_URL} (request.ts / streaming.ts lineage). */
295
+ export const CLOUDCODE_PA_BASE = CLOUDCODE_BASE_URL;
296
+
297
+ /** Daily (canary) Cloud Code Assist base URL (quota.ts / request.ts lineage). */
298
+ export const CLOUDCODE_DAILY_BASE = "https://daily-cloudcode-pa.googleapis.com" as const;
299
+
300
+ /**
301
+ * Base URL for the Cloud Code Assist PaaS API (v1internal — undocumented).
302
+ * v2.1.16: relocated above its dependents so every v1internal method-path
303
+ * constant below derives from {@link CODE_ASSIST_PATH_MAP} (zero duplicated
304
+ * path strings in this module).
305
+ */
306
+ export const CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com" as const;
307
+
308
+ /**
309
+ * Canonical v1internal method-path map — the single owner of every
310
+ * "/v1internal:*" path literal in this module (v2.1.16 internal-dedup: the
311
+ * standalone *_ENDPOINT constants, the CLOUDCODE_ENDPOINTS URL joins and the
312
+ * versioned daily-v1internal block all derive from these entries).
313
+ */
314
+ export const CODE_ASSIST_PATH_MAP = {
315
+ /** Loads project and onboarding status */
316
+ LOAD_CODE_ASSIST: "/v1internal:loadCodeAssist",
317
+ /** Onboards a user into a Cloud project */
318
+ ONBOARD_USER: "/v1internal:onboardUser",
319
+ /** Lists models available to the account */
320
+ FETCH_AVAILABLE_MODELS: "/v1internal:fetchAvailableModels",
321
+ /** Synchronous generation */
322
+ GENERATE_CONTENT: "/v1internal:generateContent",
323
+ /** Streaming SSE generation */
324
+ STREAM_GENERATE_CONTENT: "/v1internal:streamGenerateContent",
325
+ /** Quota / limits per model */
326
+ RETRIEVE_USER_QUOTA: "/v1internal:retrieveUserQuotaSummary",
327
+ /** Compat alias: quota summary */
328
+ RETRIEVE_USER_QUOTA_SUMMARY: "/v1internal:retrieveUserQuotaSummary",
329
+ } as const;
330
+
331
+ // NOTE (v2.1.15 Phase A): request.ts exports a method-path map under the
332
+ // historical name CODE_ASSIST_ENDPOINTS; that name is now owned by models.ts
333
+ // (the Cloud Code Assist HOST map), so the request-local method map was
334
+ // renamed CODE_ASSIST_METHOD_PATHS there.
335
+
336
+ /** Union type of the v1 paths. */
337
+ export type CodeAssistEndpointPath = (typeof CODE_ASSIST_PATH_MAP)[keyof typeof CODE_ASSIST_PATH_MAP];
338
+
339
+ /**
340
+ * Helper to build a full Code Assist URL.
341
+ * @param endpoint - path from CODE_ASSIST_PATH_MAP
342
+ */
343
+ export function buildCodeAssistUrl(endpoint: CodeAssistEndpointPath): string {
344
+ return `${CODE_ASSIST_BASE_URL}${endpoint}`;
345
+ }
346
+
347
+ /**
348
+ * Cloud Code Assist full endpoint map — canonical of the CLOUDCODE_ENDPOINTS
349
+ * family (v2.1.15 Phase B: accounts.ts, auth.ts and config.ts locals
350
+ * deleted). The config.ts lineage shape (base + the six v1internal method
351
+ * URLs) is the most complete superset; every consumer reads the method keys.
352
+ * v2.1.16: method URLs derive from {@link CODE_ASSIST_PATH_MAP}.
353
+ */
354
+ export const CLOUDCODE_ENDPOINTS = {
355
+ base: CLOUDCODE_BASE_URL,
356
+ /** Loads the Cloud Code project and binds the license */
357
+ loadCodeAssist: `${CLOUDCODE_BASE_URL}${CODE_ASSIST_PATH_MAP.LOAD_CODE_ASSIST}`,
358
+ /** Initial user onboarding */
359
+ onboardUser: `${CLOUDCODE_BASE_URL}${CODE_ASSIST_PATH_MAP.ONBOARD_USER}`,
360
+ /** Lists models available to the user */
361
+ fetchAvailableModels: `${CLOUDCODE_BASE_URL}${CODE_ASSIST_PATH_MAP.FETCH_AVAILABLE_MODELS}`,
362
+ /** Non-streaming generation */
363
+ generateContent: `${CLOUDCODE_BASE_URL}${CODE_ASSIST_PATH_MAP.GENERATE_CONTENT}`,
364
+ /** Streaming SSE generation */
365
+ streamGenerateContent: `${CLOUDCODE_BASE_URL}${CODE_ASSIST_PATH_MAP.STREAM_GENERATE_CONTENT}`,
366
+ /** Per-model quota summary */
367
+ retrieveUserQuotaSummary: `${CLOUDCODE_BASE_URL}${CODE_ASSIST_PATH_MAP.RETRIEVE_USER_QUOTA_SUMMARY}`,
368
+ } as const;
369
+
370
+ export const GEMINI_CLI_ENDPOINT = CLOUDCODE_BASE_URL;
371
+ export const CLOUDAI_COMPANION_ENDPOINT = "https://cloudaicompanion.googleapis.com" as const;
372
+ export const CLOUDAI_COMPANION = CLOUDAI_COMPANION_ENDPOINT;
373
+ export const CLOUDAICOMPANION_ENDPOINT = CLOUDAI_COMPANION_ENDPOINT;
374
+
375
+ // v2.1.16 internal-dedup: the standalone legacy method-path constants now
376
+ // derive from CODE_ASSIST_PATH_MAP above — zero duplicated path strings.
377
+ export const QUOTA_ENDPOINT = CODE_ASSIST_PATH_MAP.RETRIEVE_USER_QUOTA_SUMMARY;
378
+ export const QUOTA_ENDPOINT_V1 = QUOTA_ENDPOINT;
379
+ export const MODELS_ENDPOINT = CODE_ASSIST_PATH_MAP.FETCH_AVAILABLE_MODELS;
380
+ export const MODELS_ENDPOINT_FULL = MODELS_ENDPOINT;
381
+
382
+ export const ONBOARD_USER_ENDPOINT = CODE_ASSIST_PATH_MAP.ONBOARD_USER;
383
+ export const LOAD_CODE_ASSIST_ENDPOINT = CODE_ASSIST_PATH_MAP.LOAD_CODE_ASSIST;
384
+ export const STREAM_GENERATE_ENDPOINT = CODE_ASSIST_PATH_MAP.STREAM_GENERATE_CONTENT;
385
+ export const GENERATE_CONTENT_ENDPOINT = CODE_ASSIST_PATH_MAP.GENERATE_CONTENT;
386
+ /** No CODE_ASSIST_PATH_MAP entry exists for this legacy path (zero consumers) — kept as its single literal definition. */
387
+ export const RESOLVE_PROJECT_ENDPOINT = "/v1internal:resolveProjectId" as const;
388
+ export const FETCH_AVAILABLE_MODELS_METHOD = "fetchAvailableModels" as const;
389
+
390
+ /**
391
+ * Linear-time trailing-slash strip (regex-free; safe on uncontrolled input —
392
+ * the former /\/+$/.replace was polynomial and flagged as ReDoS-prone).
393
+ */
394
+ export function stripTrailingSlashes(value: string): string {
395
+ let end = value.length;
396
+ while (end > 0 && value.charCodeAt(end - 1) === 47 /* "/" */) end--;
397
+ return end === value.length ? value : value.slice(0, end);
398
+ }
399
+
400
+ export function buildCloudCodeUrl(base: string, path: string): string {
401
+ const b = stripTrailingSlashes(base);
402
+ const p = path.startsWith("/") ? path : `/${path}`;
403
+ return `${b}${p}`;
404
+ }
405
+
406
+ // ---------------------------------------------------------------------------
407
+ // Project fallback + Gemini CLI bypass
408
+ // ---------------------------------------------------------------------------
409
+
410
+ export const PROJECT_FALLBACK = "rising-fact-p41fc" as const;
411
+ export const PROJECT_ID_FALLBACK = PROJECT_FALLBACK;
412
+ export const PROJECT_NAME_FALLBACK = `projects/${PROJECT_FALLBACK}`;
413
+ export const PROJECT_NUMBER_FALLBACK = "1071006060591" as const;
414
+ export const FALLBACK_PROJECT_ID = PROJECT_FALLBACK;
415
+
416
+ export const GEMINI_CLI_PROJECT_RESOLUTION = {
417
+ metadata: { ideType: "ANTIGRAVITY", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI" },
418
+ tierId: "FREE",
419
+ retryableStatuses: [403, 404, 408, 429, 500, 502, 503, 504],
420
+ nonRetryableStatuses: [400, 401],
421
+ fallbackProjectId: PROJECT_FALLBACK,
422
+ loadCodeAssistBodyWithoutProject: {
423
+ metadata: { ideType: "ANTIGRAVITY", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI" },
424
+ },
425
+ onboardUserBody: {
426
+ tierId: "FREE",
427
+ metadata: { ideType: "ANTIGRAVITY", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI" },
428
+ },
429
+ } as const;
430
+
431
+ export const PROJECT_RESOLUTION_STRATEGY = GEMINI_CLI_PROJECT_RESOLUTION;
432
+
433
+ export type QuotaGroup = "antigravity" | "gemini-cli" | "cloudaicompanion";
434
+
435
+ // v2.1.15 Phase A: every raw model substrate (flat id lists, rich definition
436
+ // lists, model aliases, default model ids and the family-checker helpers)
437
+ // moved to models.ts — model identification lives ONLY in models.ts.
438
+ // QuotaGroup stays: it types the quota-groups section below and is
439
+ // type-imported by models.ts (type-only edge, erased at runtime).
440
+
441
+ // OAuth
442
+ export const OAUTH_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth" as const;
443
+ export const OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token" as const;
444
+ export const OAUTH_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo" as const;
445
+
446
+ /**
447
+ * v3 userinfo endpoint (former accounts.ts local variant; the v2 URL above
448
+ * is the canonical gemini-cli lineage). Distinct value — one name, one meaning.
449
+ */
450
+ export const OAUTH_USERINFO_URL_V3 = "https://www.googleapis.com/oauth2/v3/userinfo" as const;
451
+ export const OAUTH_REVOKE_URL = "https://oauth2.googleapis.com/revoke" as const;
452
+
453
+ export const OAUTH_SCOPES = [
454
+ "openid",
455
+ "email",
456
+ "profile",
457
+ "https://www.googleapis.com/auth/cloud-platform",
458
+ "https://www.googleapis.com/auth/userinfo.email",
459
+ "https://www.googleapis.com/auth/userinfo.profile",
460
+ "https://www.googleapis.com/auth/cclog",
461
+ "https://www.googleapis.com/auth/experimentsandconfigs",
462
+ ] as const;
463
+
464
+ export const OAUTH_SCOPES_STRING = OAUTH_SCOPES.join(" ");
465
+
466
+ export const PKCE_VERIFIER_LENGTH = 128 as const;
467
+ export const PKCE_CODE_CHALLENGE_METHOD = "S256" as const;
468
+
469
+ export const OAUTH_CALLBACK_HOST = "127.0.0.1" as const;
470
+ export const OAUTH_CALLBACK_PATH = "/callback" as const;
471
+ export const OAUTH_STATE_LENGTH = 32 as const;
472
+ export const OAUTH_PORT_RANGE = { min: 10000, max: 60000 } as const;
473
+
474
+ // Timeouts, jitter, backoff
475
+ // (FETCH_TIMEOUT_MS and the JITTER_MIN_MS/JITTER_MAX_MS pair stayed with
476
+ // core.ts in devthink/constants.ts — the generic leaf constants of the os
477
+ // utility module; the gateway imports them through
478
+ // @wenathlan/devthink/constants.)
479
+ export const DEFAULT_TIMEOUT_MS = 10_000 as const;
480
+ export const CONTENT_TIMEOUT_MS = 60_000 as const;
481
+ export const PROJECT_RESOLVE_TIMEOUT_MS = 10_000 as const;
482
+ export const QUOTA_TIMEOUT_MS = 15_000 as const;
483
+ export const TOKEN_REFRESH_TIMEOUT_MS = 10_000 as const;
484
+ export const ONBOARD_TIMEOUT_MS = 15_000 as const;
485
+
486
+ // v2.1.15 Phase B: getJitter moved to core.js (generic utility owner);
487
+ // streaming/quota/plugin import it from there.
488
+
489
+ export const BACKOFF_BASE_MS = 250 as const;
490
+ export const BACKOFF_MAX_MS = 10_000 as const;
491
+ export const BACKOFF_FACTOR = 2 as const;
492
+ export const MAX_RETRIES = 3 as const;
493
+ export const RETRYABLE_STATUS_CODES = [403, 404, 408, 429, 500, 502, 503, 504] as const;
494
+ export const RETRYABLE_ERROR_CODES = ["403", "404", "5xx"] as const;
495
+
496
+ export const QUOTA_CACHE_TTL_MS = 15 * 60 * 1000;
497
+ export const QUOTA_CACHE_TTL_MIN_MS = 2 * 60 * 1000;
498
+
499
+ /**
500
+ * Quota cache TTL ceiling — canonical is the quota.ts lineage value (30 min;
501
+ * the former 10 min constants.ts variant had zero consumers). quota.ts's
502
+ * resilient-layer 10 min max stays local there under its own accurate name.
503
+ */
504
+ export const QUOTA_CACHE_TTL_MAX_MS = 30 * 60 * 1000;
505
+ export const QUOTA_SOFT_THRESHOLD = 0.9 as const;
506
+
507
+ /**
508
+ * Quota hard-exhaustion threshold — canonical is the quota.ts lineage value
509
+ * (98% used => 2% remaining => hard exhausted; the former 1.0 constants.ts
510
+ * variant had zero consumers and was not exercised by the live chain).
511
+ */
512
+ export const QUOTA_HARD_THRESHOLD = 0.98 as const;
513
+
514
+ export const ACCOUNTS_FILE_NAME = "antigravity-accounts.json" as const;
515
+ export const ACCOUNTS_CONFIG_DIR = ".config/opencode" as const;
516
+ export const ACCOUNTS_FILE_MODE = 0o600 as const;
517
+
518
+ // v2.1.15 Phase A: THINKING_LEVELS, ThinkingLevel, THINKING_BUDGET_MAP,
519
+ // THINKING_BUDGET_MAX_CLAUDE and THINKING_BUDGET_MAX_GEMINI moved to models.ts
520
+ // (canonical 6-level superset including "tiered").
521
+ export const LRU_THINKING_CACHE_SIZE = 100 as const;
522
+
523
+ // (DEBUG_LOG_MAX_BYTES, DEBUG_LOG_RETENTION_DAYS and DEBUG_BUFFER_TUI_LINES
524
+ // stayed with the DebugLogger of core.ts in devthink/constants.ts.)
525
+
526
+ export const MODALITIES_INPUT = ["text", "image", "pdf"] as const;
527
+ export const GOOGLE_SEARCH_TOOL_NAME = "google_search" as const;
528
+ export const URL_CONTEXT_TOOL_NAME = "url_context" as const;
529
+
530
+ export const CONTEXT_WINDOW_ANTIGRAVITY = 1_048_576 as const;
531
+ export const OUTPUT_LIMIT_ANTIGRAVITY = 65_535 as const;
532
+ // v2.1.15 Phase A: CONTEXT_WINDOW_CLAUDE, OUTPUT_LIMIT_CLAUDE,
533
+ // CONTEXT_WINDOW_GPT_OSS and OUTPUT_LIMIT_GPT_OSS moved to models.ts (the
534
+ // GPT-OSS canonicals there are 200_000 / 64_000; the divergent local
535
+ // 131_072 / 32_768 values had zero consumers and were dropped).
536
+
537
+ /**
538
+ * FNV-1a 32-bit constants (models.ts and request.ts locals deleted in
539
+ * v2.1.15 Phase B; core.ts's fnv implementations keep their own inline
540
+ * literals so core stays free of cross-module value coupling at the hash
541
+ * primitive level).
542
+ */
543
+ export const FNV_OFFSET_BASIS = 2_166_136_261 as const;
544
+ export const FNV_PRIME = 16_777_619 as const;
545
+
546
+ export const CONSTANTS = {
547
+ versionFallback: ANTIGRAVITY_VERSION_FALLBACK,
548
+ versionMinSupported: ANTIGRAVITY_VERSION_MIN_SUPPORTED,
549
+ userAgentFallback: ANTIGRAVITY_USER_AGENT_FALLBACK,
550
+ clientId: ANTIGRAVITY_CLIENT_ID,
551
+ clientSecret: ANTIGRAVITY_CLIENT_SECRET,
552
+ endpoints: ANTIGRAVITY_ENDPOINTS,
553
+ endpointFallbacks: ANTIGRAVITY_ENDPOINT_FALLBACKS,
554
+ codeAssistEndpoints: ENDPOINTS,
555
+ endpointOrder: [ENDPOINTS.PROD, ENDPOINTS.DAILY, ENDPOINTS.SANDBOX] as const,
556
+ geminiCliEndpoint: GEMINI_CLI_ENDPOINT,
557
+ cloudaicompanion: CLOUDAI_COMPANION_ENDPOINT,
558
+ quota: QUOTA_ENDPOINT,
559
+ modelsEndpoint: MODELS_ENDPOINT,
560
+ projectFallback: PROJECT_FALLBACK,
561
+ projectResolution: PROJECT_RESOLUTION_STRATEGY,
562
+ scopes: OAUTH_SCOPES,
563
+ } as const;
564
+
565
+ // ===========================================================================
566
+ // Ported v1.0.0 surface (auth/constants) — every unique export preserved
567
+ // ===========================================================================
568
+
569
+ // ---------------------------------------------------------------------------
570
+ // V1-01. OAuth client — Gemini CLI official identification
571
+ // ---------------------------------------------------------------------------
572
+
573
+ /**
574
+ * Official Gemini CLI OAuth Client ID (Google Cloud SDK — public client used by the CLI).
575
+ * @constant
576
+ */
577
+ export const GEMINI_CLI_OAUTH_CLIENT_ID = [
578
+ "681255809395",
579
+ "-oo8ft2oprdrnp9e3aqf6av3",
580
+ "hmdib135j",
581
+ ".apps.",
582
+ "googleusercontent",
583
+ ".com",
584
+ ].join("");
585
+
586
+ /**
587
+ * Official Gemini CLI OAuth Client Secret.
588
+ * @constant
589
+ */
590
+ export const GEMINI_CLI_OAUTH_CLIENT_SECRET = ["GOCSPX", "-4uHgMPm", "-1o7Sk-geV6", "Cu5clXFsxl"].join("");
591
+
592
+ /** Scopes required by Gemini CLI / Cloud Code Assist. */
593
+ export const GEMINI_CLI_SCOPES = [
594
+ "https://www.googleapis.com/auth/cloud-platform",
595
+ "https://www.googleapis.com/auth/userinfo.email",
596
+ "https://www.googleapis.com/auth/userinfo.profile",
597
+ ] as const;
598
+
599
+ /** Scopes as a query string ready for OAuth. */
600
+ export const GEMINI_CLI_SCOPES_JOINED = GEMINI_CLI_SCOPES.join(" ");
601
+
602
+ /**
603
+ * Official Antigravity CLI (`agy`) OAuth client id — THE single owner of the
604
+ * real (never the blinded decoy above) client pair. Env-overridable through
605
+ * ANTIGRAVITY_CLIENT_ID / GOOGLE_CLIENT_ID; the fallback literal is assembled
606
+ * from segments exactly like the `agy` binary embeds it. The grand merge
607
+ * consolidation moved the owner from auth.ts into this layer-0 module so the
608
+ * value never participates in an import cycle: auth.ts (the authentication
609
+ * surface) and accounts.ts (the account store) both alias it while the module
610
+ * graph stays legal under every evaluation order.
611
+ */
612
+ export const ANTIGRAVITY_CLI_OAUTH_CLIENT_ID =
613
+ process.env.ANTIGRAVITY_CLIENT_ID?.trim() ||
614
+ process.env.GOOGLE_CLIENT_ID?.trim() ||
615
+ ["1071006060591", "-tmhssin2h21lcre235vtol", "ojh4g403ep", ".apps.", "googleusercontent", ".com"].join("");
616
+
617
+ /**
618
+ * Official Antigravity CLI (`agy`) OAuth client secret — the env-overridable
619
+ * owner (ANTIGRAVITY_CLIENT_SECRET) of the real pair, paired with
620
+ * {@link ANTIGRAVITY_CLI_OAUTH_CLIENT_ID}.
621
+ */
622
+ export const ANTIGRAVITY_CLI_OAUTH_CLIENT_SECRET =
623
+ process.env.ANTIGRAVITY_CLIENT_SECRET?.trim() || ["GOCSPX", "-K58FWR486LdL", "J1mLB8sXC4z6qDAf"].join("");
624
+
625
+ // ---------------------------------------------------------------------------
626
+ // V1-02. Endpoints — OAuth + versioned bases
627
+ // (v2.1.16: CODE_ASSIST_BASE_URL / CODE_ASSIST_PATH_MAP /
628
+ // CodeAssistEndpointPath / buildCodeAssistUrl moved up into the main
629
+ // endpoints section so every method-path derivation sits below the map.)
630
+ // ---------------------------------------------------------------------------
631
+
632
+ /** Google OAuth endpoints. */
633
+ export const OAUTH_ENDPOINTS = {
634
+ /** Authorization code URL */
635
+ AUTH: "https://accounts.google.com/o/oauth2/v2/auth",
636
+ /** Token exchange */
637
+ TOKEN: "https://oauth2.googleapis.com/token",
638
+ /** Userinfo OpenID Connect */
639
+ USERINFO: "https://www.googleapis.com/oauth2/v3/userinfo",
640
+ /** Token info / introspect */
641
+ TOKENINFO: "https://oauth2.googleapis.com/tokeninfo",
642
+ /** Revoke */
643
+ REVOKE: "https://oauth2.googleapis.com/revoke",
644
+ } as const;
645
+
646
+ /**
647
+ * Antigravity versioned endpoint bases (renamed from v1 ANTIGRAVITY_ENDPOINTS
648
+ * to avoid collision with the v2 cascade URL array above).
649
+ * Antigravity routes via cloudcode-pa but with different prefixes per version.
650
+ */
651
+ export const ANTIGRAVITY_ENDPOINT_SET = {
652
+ /** v1 — legacy */
653
+ V1_BASE: "https://daily-cloudcode-pa.googleapis.com",
654
+ /** v1internal current */
655
+ V1INTERNAL_BASE: "https://cloudcode-pa.googleapis.com",
656
+ /** v1internal daily (canary) */
657
+ DAILY_V1INTERNAL_BASE: "https://daily-cloudcode-pa.googleapis.com",
658
+ /** autodetect endpoint (resolves cloudcode client) */
659
+ AUTO_BASE: "https://cloudcode-pa.googleapis.com",
660
+ } as const;
661
+
662
+ export const ANTIGRAVITY_VERSION_ENDPOINTS = {
663
+ v1: {
664
+ base: ANTIGRAVITY_ENDPOINT_SET.V1_BASE,
665
+ load: "/v1:loadCodeAssist",
666
+ onboard: "/v1:onboardUser",
667
+ },
668
+ v1internal: {
669
+ base: ANTIGRAVITY_ENDPOINT_SET.V1INTERNAL_BASE,
670
+ load: CODE_ASSIST_PATH_MAP.LOAD_CODE_ASSIST,
671
+ onboard: CODE_ASSIST_PATH_MAP.ONBOARD_USER,
672
+ fetchModels: CODE_ASSIST_PATH_MAP.FETCH_AVAILABLE_MODELS,
673
+ generate: CODE_ASSIST_PATH_MAP.GENERATE_CONTENT,
674
+ streamGenerate: CODE_ASSIST_PATH_MAP.STREAM_GENERATE_CONTENT,
675
+ quota: CODE_ASSIST_PATH_MAP.RETRIEVE_USER_QUOTA,
676
+ },
677
+ "daily-v1internal": {
678
+ base: ANTIGRAVITY_ENDPOINT_SET.DAILY_V1INTERNAL_BASE,
679
+ // v2.1.16: method paths derive from CODE_ASSIST_PATH_MAP (identical values).
680
+ load: CODE_ASSIST_PATH_MAP.LOAD_CODE_ASSIST,
681
+ onboard: CODE_ASSIST_PATH_MAP.ONBOARD_USER,
682
+ fetchModels: CODE_ASSIST_PATH_MAP.FETCH_AVAILABLE_MODELS,
683
+ generate: CODE_ASSIST_PATH_MAP.GENERATE_CONTENT,
684
+ streamGenerate: CODE_ASSIST_PATH_MAP.STREAM_GENERATE_CONTENT,
685
+ quota: CODE_ASSIST_PATH_MAP.RETRIEVE_USER_QUOTA,
686
+ },
687
+ } as const;
688
+
689
+ export type AntigravityVersion = keyof typeof ANTIGRAVITY_VERSION_ENDPOINTS;
690
+
691
+ // ---------------------------------------------------------------------------
692
+ // V1-03. Project ID fallback list
693
+ // ---------------------------------------------------------------------------
694
+
695
+ /** Public fallback Project ID list (seed candidates without billing). */
696
+ export const PROJECT_ID_FALLBACKS = [
697
+ "rising-fact-p41fc",
698
+ "rising-fact-p41fc-1",
699
+ "unwritten-orb-463209-q6",
700
+ "cloud-code-123456",
701
+ ] as const;
702
+
703
+ // ---------------------------------------------------------------------------
704
+ // V1-04. Version pools — simulated real releases
705
+ // ---------------------------------------------------------------------------
706
+
707
+ /**
708
+ * Gemini CLI version pool — date-aware, consulted 2026-08-26 (npm
709
+ * @google/gemini-cli latest = 0.57.0). Canonical of the pool family
710
+ * (v2.1.15 Phase B): the fingerprint.ts 8-entry telemetry superset —
711
+ * strictly grows the former 6-entry constants variant.
712
+ */
713
+ export const GEMINI_CLI_VERSION_POOL = [
714
+ "0.57.0",
715
+ "0.56.0",
716
+ "0.55.1",
717
+ "0.54.0",
718
+ "0.53.0",
719
+ "0.52.0",
720
+ "0.51.2",
721
+ "0.50.0",
722
+ ] as const;
723
+
724
+ export const CURRENT_GEMINI_CLI_VERSION = "0.57.0" as const;
725
+
726
+ /** google-api-nodejs-client version pool. */
727
+ export const GAPI_CLIENT_VERSION_POOL = ["9.15.1", "9.14.0", "9.15.0", "9.13.0"] as const;
728
+
729
+ export const CURRENT_GAPI_CLIENT_VERSION = "9.15.1" as const;
730
+
731
+ /**
732
+ * Node.js gl-node version pool for X-Goog-Api-Client anti-ban rotation —
733
+ * canonical of the pool family (v2.1.15 Phase B): the fingerprint.ts
734
+ * telemetry lineage (real Node LTS values); fingerprint.ts imports this.
735
+ */
736
+ export const GL_NODE_VERSION_POOL = [
737
+ "22.19.0",
738
+ "22.16.0",
739
+ "22.14.0",
740
+ "20.19.3",
741
+ "20.18.1",
742
+ "20.17.0",
743
+ "20.11.0",
744
+ "18.20.4",
745
+ ] as const;
746
+
747
+ export const CURRENT_GL_NODE_VERSION = "22.19.0" as const;
748
+
749
+ /**
750
+ * Antigravity CLI (`agy`) version pool from the official update manifest
751
+ * (2026-08-26) — canonical of the pool family (v2.1.15 Phase B: identical
752
+ * 5-entry values merged from auth.ts and fingerprint.ts).
753
+ */
754
+ export const ANTIGRAVITY_CLI_VERSION_POOL = ["1.1.21", "1.1.20", "1.1.19", "1.1.18", "1.1.17"] as const;
755
+
756
+ // v2.1.15 Phase A: the V1-05 raw routing catalog (V1_CATALOG_MODELS +
757
+ // ModelVendor + ApiProvider types) and the DEFAULT_MODEL_ID /
758
+ // DEFAULT_FALLBACK_MODEL_IDS family moved to models.ts; MODEL_BY_ID and
759
+ // MODEL_ROUTING already live there.
760
+
761
+ // ---------------------------------------------------------------------------
762
+ // V1-07. Fingerprint pools — anti-ban randomization
763
+ // ---------------------------------------------------------------------------
764
+
765
+ export type SupportedPlatform = "linux" | "darwin" | "win32";
766
+ export type SupportedArch = "x64" | "arm64" | "arm";
767
+
768
+ export const PLATFORM_POOL: readonly SupportedPlatform[] = ["linux", "darwin", "win32"] as const;
769
+
770
+ export const ARCH_POOL: readonly SupportedArch[] = ["x64", "arm64"] as const;
771
+
772
+ export const OS_VERSION_POOL = {
773
+ linux: ["6.8.0-52-generic", "6.5.0-44-generic", "6.11.0-8-generic"] as const,
774
+ darwin: ["24.3.0", "24.2.0", "23.6.0", "24.4.0"] as const,
775
+ win32: ["10.0.26100", "10.0.22631", "10.0.19045"] as const,
776
+ } as const;
777
+
778
+ export const FINGERPRINT_IDE_TYPES = ["IDE_UNSPECIFIED", "VSCODE", "INTELLIJ"] as const;
779
+
780
+ export const FINGERPRINT_PLATFORMS = ["PLATFORM_UNSPECIFIED", "LINUX", "DARWIN", "WINDOWS"] as const;
781
+
782
+ export const FINGERPRINT_PLUGIN_TYPES = ["GEMINI", "GEMINI_CLI"] as const;
783
+
784
+ export const FINGERPRINT_USER_AGENT_SOURCES = ["GitHub", "npm"] as const;
785
+
786
+ // v2.1.15 Phase B: getRandomPlatformString, pickRandom, getGeminiCLIHeaders,
787
+ // getGeminiCLIHeadersWithOptions, getAntigravityHeaders,
788
+ // getAntigravityHeadersWithOptions and the GeminiCliHeaderOptions /
789
+ // AntigravityHeaderOptions types moved to fingerprint.ts — THE identity
790
+ // masquerade owner (pickRandom itself lives in core.js). The raw pools above
791
+ // stay here; fingerprint.ts imports them.
792
+
793
+ // ---------------------------------------------------------------------------
794
+ // V1-09. System prompts
795
+ // ---------------------------------------------------------------------------
796
+
797
+ export const SYSTEM_PROMPTS = {
798
+ /** base prompt for code assist */
799
+ CODE_ASSIST_BASE:
800
+ "You are Gemini, a highly capable AI coding assistant integrated into opencode via Antigravity auth.",
801
+
802
+ /** prompt for user onboarding */
803
+ ONBOARDING: "Initialize Cloud Code Assist project onboarding for development workflow.",
804
+
805
+ /** minimal prompt for quota queries */
806
+ QUOTA_QUERY: "quota_retrieve",
807
+
808
+ /** safety prompt — preserves IDE behavior */
809
+ IDE_PRESERVE: "Respond as the integrated IDE code assistance. Maintain context awareness and file system access.",
810
+ } as const;
811
+
812
+ // ---------------------------------------------------------------------------
813
+ // V1-10. File paths — resolvers honoring OPENCODE_CONFIG_DIR with fallback
814
+ // ---------------------------------------------------------------------------
815
+
816
+ /** Resolves the config base dir: process.env.OPENCODE_CONFIG_DIR or ~/.config/opencode/. */
817
+ export function resolveOpenCodeBaseDir(): string {
818
+ const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
819
+ if (envDir && envDir.length > 0) {
820
+ return nodePath.resolve(envDir);
821
+ }
822
+ return nodePath.join(nodeos.homedir(), ".config", "opencode");
823
+ }
824
+
825
+ /** Resolves critical plugin file paths. */
826
+ export const FILE_PATHS = {
827
+ /** dynamic getter — avoids caching in tests */
828
+ get baseDir(): string {
829
+ return resolveOpenCodeBaseDir();
830
+ },
831
+
832
+ get authDir(): string {
833
+ return nodePath.join(resolveOpenCodeBaseDir(), "auth");
834
+ },
835
+
836
+ get tokenFile(): string {
837
+ return nodePath.join(resolveOpenCodeBaseDir(), "auth", "gemini-cli-tokens.json");
838
+ },
839
+
840
+ get credentialsFile(): string {
841
+ return nodePath.join(resolveOpenCodeBaseDir(), "auth", "antigravity-credentials.json");
842
+ },
843
+
844
+ get projectFile(): string {
845
+ return nodePath.join(resolveOpenCodeBaseDir(), "auth", "project-id.json");
846
+ },
847
+
848
+ get configFile(): string {
849
+ return nodePath.join(resolveOpenCodeBaseDir(), "opencode.json");
850
+ },
851
+
852
+ get cacheDir(): string {
853
+ return nodePath.join(resolveOpenCodeBaseDir(), "cache", "antigravity");
854
+ },
855
+
856
+ get quotaCacheFile(): string {
857
+ return nodePath.join(resolveOpenCodeBaseDir(), "cache", "antigravity", "quota.json");
858
+ },
859
+
860
+ get modelsCacheFile(): string {
861
+ return nodePath.join(resolveOpenCodeBaseDir(), "cache", "antigravity", "models.json");
862
+ },
863
+
864
+ /** resolves a custom sub-path inside baseDir */
865
+ resolve(...segments: string[]): string {
866
+ return nodePath.join(resolveOpenCodeBaseDir(), ...segments);
867
+ },
868
+
869
+ /**
870
+ * Isolated credential directory — platform-agnostic and user-overridable.
871
+ * Resolution order (NO hardcoded machine paths):
872
+ * 1. MAENE_ISOLATED_DIR env (explicit user override)
873
+ * 2. <baseDir>/auth (beside the rest of the provider state)
874
+ */
875
+ get isolatedDir(): string {
876
+ const override = process.env.MAENE_ISOLATED_DIR?.trim();
877
+ if (override) return nodePath.resolve(override);
878
+ return nodePath.join(resolveOpenCodeBaseDir(), "auth");
879
+ },
880
+ } as const;
881
+
882
+ // ---------------------------------------------------------------------------
883
+ // V1-11. Quota groups & limits
884
+ // ---------------------------------------------------------------------------
885
+
886
+ // v2.1.16 dead-code cleanup: QUOTA_GROUPS, QUOTA_GROUP_MAP and the
887
+ // QuotaGroupDef interface had zero consumers repo-wide (the live quota-group
888
+ // routing lives in quota.ts QUOTA_GROUP_DEFS) and were deleted.
889
+
890
+ // ---------------------------------------------------------------------------
891
+ // V1-12. Retry / timeout / version-fallback policies
892
+ // ---------------------------------------------------------------------------
893
+
894
+ export const VERSION_FALLBACK_CHAIN: readonly AntigravityVersion[] = ["v1internal", "daily-v1internal", "v1"] as const;
895
+
896
+ export const RETRY_POLICY = {
897
+ maxRetries: 3,
898
+ baseDelayMs: 500,
899
+ maxDelayMs: 5000,
900
+ jitter: true,
901
+ retryOn: [429, 500, 502, 503, 504] as const,
902
+ } as const;
903
+
904
+ export const TIMEOUTS = {
905
+ OAUTH_MS: 15_000,
906
+ LOAD_CODE_ASSIST_MS: 20_000,
907
+ GENERATE_MS: 120_000,
908
+ STREAM_MS: 300_000,
909
+ QUOTA_MS: 10_000,
910
+ } as const;
911
+
912
+ export const PLUGIN_VERSION = "2.0.0" as const;
913
+
914
+ // ---------------------------------------------------------------------------
915
+ // V1-13. Complete fingerprint & anti-ban pools
916
+ // ---------------------------------------------------------------------------
917
+
918
+ export const FINGERPRINT_POOLS = {
919
+ cliVersions: GEMINI_CLI_VERSION_POOL,
920
+ gapiVersions: GAPI_CLIENT_VERSION_POOL,
921
+ nodeVersions: GL_NODE_VERSION_POOL,
922
+ platforms: PLATFORM_POOL,
923
+ archs: ARCH_POOL,
924
+ osVersions: OS_VERSION_POOL,
925
+ ideTypes: FINGERPRINT_IDE_TYPES,
926
+ platformMetas: FINGERPRINT_PLATFORMS,
927
+ pluginTypes: FINGERPRINT_PLUGIN_TYPES,
928
+ /** random delay between requests to avoid rate-ban fingerprinting (ms) */
929
+ antiBanDelayMs: {
930
+ min: 80,
931
+ max: 350,
932
+ },
933
+ } as const;
934
+
935
+ /** Generates a randomized anti-ban delay. */
936
+ export function getAntiBanDelayMs(): number {
937
+ const { min, max } = FINGERPRINT_POOLS.antiBanDelayMs;
938
+ return Math.floor(Math.random() * (max - min + 1)) + min;
939
+ }
940
+
941
+ // ---------------------------------------------------------------------------
942
+ // V1-14. Compatibility barrel
943
+ // ---------------------------------------------------------------------------
944
+
945
+ // v2.1.16 dead-code cleanup: the CONSTANTS_V1 barrel had zero consumers
946
+ // repo-wide and was deleted together with its QUOTA_GROUPS member (the live
947
+ // v1 compatibility surface is the named exports above plus the CONSTANTS
948
+ // namespace object and the default export below).
949
+
950
+ export default CONSTANTS;