dsh-agy 0.3.1 → 0.3.2

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.
@@ -1,6 +1,42 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { o as proxiedFetch } from "./proxy-DfaL73mL.mjs";
3
3
  //#region src/types.ts
4
+ /**
5
+ * The upstream `window` tokens we understand: their ORDER by duration, and which
6
+ * of them `CachedQuota` has a field for.
7
+ *
8
+ * ONE table for a vocabulary that used to live in two places giving two different
9
+ * answers: `adapter/quota-summary.ts` sorted by its own token table while
10
+ * `runtime/quota.ts` classified windows with `includes()` checks. The
11
+ * disagreement was not academic — `daily` existed in one and not the other, so a
12
+ * daily group parsed and display-sorted correctly, then contributed an EMPTY
13
+ * record that replaced a real measurement.
14
+ *
15
+ * Keeping both facts in one row is deliberate: a token cannot be rankable in one
16
+ * consumer and unknown to the other, because there is only one place to add it.
17
+ * A missing `kind` means the cache has nowhere to put that reading, which is what
18
+ * stops the empty-record overwrite.
19
+ *
20
+ * Declared in this leaf rather than in either consumer because BOTH need it:
21
+ * `runtime/` must not reach into `adapter/`, and the persisted `QuotaWindow` is
22
+ * already defined here.
23
+ *
24
+ * `rank` is explicit rather than inferred from token length so it states the
25
+ * intended duration ordering: `daily` (5 chars) would otherwise sort before
26
+ * `weekly` (6) for the wrong reason.
27
+ */
28
+ const QUOTA_WINDOWS = {
29
+ "5h": {
30
+ rank: 0,
31
+ kind: "rolling"
32
+ },
33
+ daily: { rank: 1 },
34
+ weekly: {
35
+ rank: 2,
36
+ kind: "weekly"
37
+ },
38
+ monthly: { rank: 3 }
39
+ };
4
40
  /** Whether a request ran through an explicit per-account proxy. */
5
41
  function isProxyRouted(routing) {
6
42
  return Boolean(routing?.proxyUrl);
@@ -233,6 +269,6 @@ function antigravityUserAgent(version = currentAgyVersion(), platform = "darwin/
233
269
  */
234
270
  const AGY_IDE_TYPE = "ANTIGRAVITY";
235
271
  //#endregion
236
- export { setResolvedAgyVersion as _, AGY_PLATFORM_ENUM as a, isProxyRouted as b, OAUTH_AUTHORIZE_URL as c, antigravityUserAgent as d, constants_exports as f, resolveAgyClientCredentials as g, getAgyBootstrapUserAgent as h, AGY_IDE_TYPE as i, OAUTH_TOKEN_URL as l, fetchAgyFirstOk as m, AGY_DEFAULT_REDIRECT_URI as n, AGY_SCOPES as o, currentAgyVersion as p, AGY_ENDPOINT_FALLBACKS as r, AGY_VERSION_FALLBACK as s, AGY_CLIENT_ID as t, OAUTH_USERINFO_URL as u, AgyAuthError as v, AgyPoolBlockedError as y };
272
+ export { setResolvedAgyVersion as _, AGY_PLATFORM_ENUM as a, QUOTA_WINDOWS as b, OAUTH_AUTHORIZE_URL as c, antigravityUserAgent as d, constants_exports as f, resolveAgyClientCredentials as g, getAgyBootstrapUserAgent as h, AGY_IDE_TYPE as i, OAUTH_TOKEN_URL as l, fetchAgyFirstOk as m, AGY_DEFAULT_REDIRECT_URI as n, AGY_SCOPES as o, currentAgyVersion as p, AGY_ENDPOINT_FALLBACKS as r, AGY_VERSION_FALLBACK as s, AGY_CLIENT_ID as t, OAUTH_USERINFO_URL as u, AgyAuthError as v, isProxyRouted as x, AgyPoolBlockedError as y };
237
273
 
238
- //# sourceMappingURL=constants-Db4tmyfb.mjs.map
274
+ //# sourceMappingURL=constants-BpyRirvk.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants-BpyRirvk.mjs","names":[],"sources":["../src/types.ts","../src/oauth/constants.ts"],"sourcesContent":["/** Shared domain types for dsh-agy. */\n\n/**\n * Device fingerprint persisted per account (rate-limit mitigation).\n *\n * Mirrors `google.internal.cloud.code.v1internal.ClientMetadata`, read out of the\n * installed official CLI's own descriptor (`docs/official-identity.json`). The\n * message has eight fields; only the three below are populated, because the rest\n * (`pluginVersion`, `updateChannel`, `duetProject`, `pluginType`, `ideName`) have\n * no captured value and a wrong value is a worse anomaly than an absent one.\n *\n * This is the BODY message (`metadata`), not a header. Nothing here is sent as a\n * `Client-Metadata` header: neither official binary contains that header name.\n */\nexport interface ClientMetadata {\n ideType: string\n /** Client version for the claimed product line (the CLI's, not the IDE's). */\n ideVersion?: string\n /**\n * `ClientMetadata.platform` enum NAME (`DARWIN_ARM64`), which is a DIFFERENT\n * vocabulary from the UA's `darwin/arm64` token — both exist, and conflating\n * them is how `\"MACOS\"` came to be sent and rejected.\n */\n platform?: string\n}\n\nexport interface Fingerprint {\n deviceId: string\n sessionToken: string\n userAgent: string\n apiClient: string\n clientMetadata: ClientMetadata\n createdAt: number\n}\n\nexport interface FingerprintVersion {\n fingerprint: Fingerprint\n timestamp: number\n reason: 'initial' | 'regenerated' | 'restored'\n}\n\nexport type CooldownReason =\n | 'auth-failure'\n | 'network-error'\n | 'project-error'\n | 'quota-exhausted'\n | 'validation-required'\n\n/**\n * Per-account quota cache keyed by model FAMILY (`google` / `anthropic` /\n * `openai`, or `unknown`) — never by model id. See `familyKeyOf` in\n * `runtime/quota.ts`.\n *\n * The keying is load-bearing for the fields below: a family has exactly ONE\n * record, so a family-scoped reading stored here cannot disagree with a\n * per-model copy of itself, and there is no second key to keep in sync.\n */\nexport interface CachedQuota {\n /** 0..1 left in the family's rolling 5-hour window. */\n remainingFraction?: number\n /** When the 5-hour window refills (RFC3339). */\n resetTime?: string\n /**\n * 0..1 left in the family's 7-day window, when `retrieveUserQuotaSummary`\n * reported one.\n *\n * A genuinely SEPARATE window, not a second view of `remainingFraction`: the\n * 5-hour bucket refills four times a day while the weekly budget only drains,\n * so an account can be comfortable on one and exhausted on the other.\n *\n * It always travels with its own `weeklyResetTime`, and that timestamp is what\n * bounds the value's life: once it passes, every consumer ignores the reading\n * (`isFamilyDrained`, `parseFutureResetMs`, `rankPoolCandidates`), so a weekly\n * value carried forward across a failed probe cannot outlive its window.\n */\n weeklyFraction?: number\n /** When the 7-day window refills (RFC3339). */\n weeklyResetTime?: string\n /** How many models the per-model probe contributed to this family's reading. */\n modelCount?: number\n}\n\n/**\n * The upstream `window` tokens we understand: their ORDER by duration, and which\n * of them `CachedQuota` has a field for.\n *\n * ONE table for a vocabulary that used to live in two places giving two different\n * answers: `adapter/quota-summary.ts` sorted by its own token table while\n * `runtime/quota.ts` classified windows with `includes()` checks. The\n * disagreement was not academic — `daily` existed in one and not the other, so a\n * daily group parsed and display-sorted correctly, then contributed an EMPTY\n * record that replaced a real measurement.\n *\n * Keeping both facts in one row is deliberate: a token cannot be rankable in one\n * consumer and unknown to the other, because there is only one place to add it.\n * A missing `kind` means the cache has nowhere to put that reading, which is what\n * stops the empty-record overwrite.\n *\n * Declared in this leaf rather than in either consumer because BOTH need it:\n * `runtime/` must not reach into `adapter/`, and the persisted `QuotaWindow` is\n * already defined here.\n *\n * `rank` is explicit rather than inferred from token length so it states the\n * intended duration ordering: `daily` (5 chars) would otherwise sort before\n * `weekly` (6) for the wrong reason.\n */\nexport const QUOTA_WINDOWS: Record<string, { rank: number, kind?: 'rolling' | 'weekly' }> = {\n '5h': { rank: 0, kind: 'rolling' },\n daily: { rank: 1 },\n weekly: { rank: 2, kind: 'weekly' },\n monthly: { rank: 3 },\n}\n\n/**\n * One window of one `QuotaGroup`, as `retrieveUserQuotaSummary` reports it.\n *\n * Declared here (the dependency-free leaf) because it is PERSISTED on the\n * account; the parser that produces it lives in `adapter/quota-summary.ts`, which\n * imports this module rather than the reverse.\n */\nexport interface QuotaWindow {\n /** Upstream's own bucket id, e.g. `gemini-5h`, `3p-weekly`. Kept verbatim. */\n bucketId: string\n /** Upstream's window token: `5h` or `weekly` today. */\n window: string\n /** 0..1, or null when upstream omitted the fraction (unknown, not empty). */\n remainingFraction: number | null\n /** RFC3339 reset moment, or null when upstream omitted it. */\n resetTime: string | null\n}\n\n/** One group of models sharing a 5-hour and a weekly window. */\nexport interface QuotaGroup {\n /** Upstream's group label, e.g. `Gemini Models`. */\n name: string\n windows: QuotaWindow[]\n}\n\n/**\n * The grouped 5-hour / weekly windows, cached per account.\n *\n * Deliberately SEPARATE from `cachedQuota`: that map is per-FAMILY and feeds the\n * rotation/ranking path (`familyQuotaFor`, `isFamilyDrained`), while this is\n * per-GROUP and display-only. Merging them would put two different shapes under\n * one key and let a display refresh influence scheduling.\n */\nexport interface CachedLimits {\n groups: QuotaGroup[]\n /** When this snapshot was taken (Unix ms). */\n updatedAt: number\n}\n\n/** One account in the pool. `refresh` is the packed `refreshToken|projectId|managedProjectId` string. */\nexport interface ManagedAccount {\n id?: string\n email?: string\n refresh: string\n projectId?: string\n managedProjectId?: string\n clientId?: string\n addedAt: number\n lastUsed: number\n enabled?: boolean\n rateLimitResetTimes?: Record<string, number>\n coolingDownUntil?: number\n cooldownReason?: CooldownReason\n /**\n * When the CURRENT cooldown began (Unix ms).\n *\n * Separate from `coolingDownUntil`, which is its END: the duration is a\n * backoff computed from the consecutive-failure count, so the start cannot be\n * recovered from the end. Persisted because the reason is only useful with an\n * age attached — \"network-error\" alone cannot distinguish a blip from seconds\n * ago from one that has been sitting there for days. Cleared together with\n * `cooldownReason` when the window expires.\n */\n cooldownSetAt?: number\n verificationRequired?: boolean\n verificationRequiredAt?: number\n verificationRequiredReason?: string\n verificationUrl?: string\n fingerprint?: Fingerprint\n fingerprintHistory?: FingerprintVersion[]\n cachedQuota?: Record<string, CachedQuota>\n cachedQuotaUpdatedAt?: number\n /** Grouped 5h/weekly windows, display-only (see `CachedLimits`). */\n cachedLimits?: CachedLimits\n /** Per-account proxy URL (e.g. http://user:pass@host:8080 or socks5://host:1080). Undefined = follow env. */\n proxy?: string\n}\n\nexport interface AccountStorageV1 {\n version: 1\n accounts: Array<{\n email?: string\n refreshToken: string\n projectId?: string\n managedProjectId?: string\n addedAt: number\n lastUsed: number\n isRateLimited?: boolean\n rateLimitResetTime?: number\n lastSwitchReason?: 'rate-limit' | 'initial' | 'rotation'\n }>\n activeIndex: number\n}\n\nexport interface AccountStorageV2 {\n version: 2\n accounts: Array<{\n email?: string\n refreshToken: string\n projectId?: string\n managedProjectId?: string\n addedAt: number\n lastUsed: number\n lastSwitchReason?: 'rate-limit' | 'initial' | 'rotation'\n rateLimitResetTimes?: Record<string, number>\n }>\n activeIndex: number\n}\n\nexport interface AccountStorageV3 {\n version: 3\n accounts: ManagedAccount[]\n activeIndex: number\n}\n\nexport interface AccountStorageV4 {\n version: 4\n accounts: ManagedAccount[]\n activeIndex: number\n}\n\nexport type AccountStorage = AccountStorageV1 | AccountStorageV2 | AccountStorageV3 | AccountStorageV4\n\n/**\n * How one account-scoped request is routed — and therefore how its transport\n * failures must be read. `proxyUrl` is the single source of truth: fail-closed\n * applies exactly when it is set, so routing and failure classification can\n * never disagree (splitting them into two arguments let them drift).\n */\nexport interface AccountRouting {\n /** Per-account proxy; unset means the env/direct route. */\n proxyUrl?: string\n /**\n * Generation stream: long model silences are normal, so the per-gap body\n * inactivity timer must be disabled (AGENTS.md \"Proxy Routing\").\n */\n streaming?: boolean\n}\n\n/** Whether a request ran through an explicit per-account proxy. */\nexport function isProxyRouted(routing: AccountRouting | undefined): boolean {\n return Boolean(routing?.proxyUrl)\n}\n\n/** Parsed halves of the packed refresh string. */\nexport interface RefreshParts {\n refreshToken?: string\n projectId?: string\n managedProjectId?: string\n}\n\n/** OAuth token view of an account used by the refresh path. */\nexport interface OAuthAuthDetails {\n access: string\n expires: number\n refresh: string\n}\n\n/** Result of the OAuth token exchange. */\nexport interface TokenExchangeSuccess {\n type: 'success'\n refresh: string\n access: string\n expires: number\n email?: string\n projectId: string\n tier?: string\n clientId?: string\n}\n\nexport interface TokenExchangeFailure {\n type: 'failed'\n error: string\n}\n\nexport type TokenExchangeResult = TokenExchangeSuccess | TokenExchangeFailure\n\nexport interface AgyAccountSession {\n auth: OAuthAuthDetails\n account: ManagedAccount\n index: number\n /**\n * Impersonation headers for this request.\n *\n * `clientMetadata` rides alongside rather than inside: it is a BODY message\n * (`metadata` on the control-plane calls), because the `Client-Metadata` header\n * this used to be is present in neither official binary.\n */\n impersonation: {\n 'User-Agent': string\n 'X-Goog-Api-Client': string\n clientMetadata: ClientMetadata\n }\n}\n\n/** Authentication failure while resolving an account session. */\nexport type AgyAuthErrorKind = 'transport' | 'rate-limit' | 'invalid-credential'\n\n/**\n * Host-independent authentication error. The adapter maps `kind` to the DSH\n * error protocol without coupling the session or CLI layers to dsh-llm.\n */\nexport class AgyAuthError extends Error {\n readonly kind: AgyAuthErrorKind\n\n constructor(kind: AgyAuthErrorKind, message: string, options?: ErrorOptions) {\n super(message, options)\n this.name = 'AgyAuthError'\n this.kind = kind\n }\n}\n\n/** Why an enabled account pool cannot currently serve one model family. */\nexport type PoolBlockedKind = 'retryable' | 'quota-exhausted'\n\n/**\n * Enabled accounts exist, but every candidate is temporarily blocked. Kept\n * independent of dsh-llm so CLI and web entry points do not gain a host import.\n */\nexport class AgyPoolBlockedError extends Error {\n readonly kind: PoolBlockedKind\n readonly blockedUntil: number\n\n constructor(kind: PoolBlockedKind, blockedUntil: number) {\n super(kind === 'quota-exhausted'\n ? 'All agy accounts have exhausted quota for the requested model family.'\n : 'All agy accounts are temporarily blocked for the requested model family.')\n this.name = 'AgyPoolBlockedError'\n this.kind = kind\n this.blockedUntil = blockedUntil\n }\n}\n\n/** Classified upstream failure kinds consumed by the rotation state machine. */\nexport type FailureKind =\n | 'rate-limit'\n | 'auth-failure'\n /**\n * Upstream asked for account verification (`VALIDATION_REQUIRED`) rather than\n * rejecting the credential. Deliberately distinct from `auth-failure` because\n * it is RECOVERABLE: the account is temporarily walled, not dead, and the user\n * can act on the returned URL. Treating it as `auth-failure` permanently\n * disabled a healthy account on a signal that meant \"come back after\n * verifying\", with no automatic way back.\n */\n | 'verification-required'\n | 'network-error'\n | 'project-error'\n | 'request-error'\n | 'transient'\n | 'proxy-unreachable'\n\n/**\n * Rotation state machine decision for one failed attempt.\n *\n * `backoffMs` is **advisory**, and for `retry`/`rotate` no caller consumes it.\n * It is not the mechanism that paces the pool: the account-level `cool` paths\n * write it into `coolingDownUntil` themselves, `rotate` blocks the failed\n * account through `rateLimitResetTimes`, and the delay before the retry of a\n * single request belongs to DSH's retry policy (`providerRetryAfterMs`, else its\n * own exponential `localDelay`). Do NOT \"wire it up\" by feeding it into\n * `providerRetryAfterMs`: a tier above DSH's `maxDelayMs` makes the normal retry\n * mode give up entirely, turning a recoverable 5xx into a failed turn.\n */\nexport type RotationAction =\n | { action: 'retry'; backoffMs: number }\n | { action: 'cool'; backoffMs: number }\n | { action: 'rotate'; backoffMs: number }\n | { action: 'revoke' }\n /** Permanent request-construction error: no state change, surface as-is. */\n | { action: 'noop' }\n","/**\n * Antigravity (agy) OAuth and API constants.\n *\n * The client id/secret below are the public Google consumer-OAuth credentials\n * shipped inside the Antigravity desktop product and its `agy` CLI; they are\n * embedded in many public tools (see NOTICE.md). They are not secrets owned by\n * this project.\n */\n\nimport { proxiedFetch } from '../proxy.ts'\nimport { isProxyRouted } from '../types.ts'\nimport type { AccountRouting } from '../types.ts'\n\nexport const AGY_CLIENT_ID =\n '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com'\n\nexport const AGY_CLIENT_SECRET = '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'\n\n/**\n * Effective OAuth client credentials: AGY_CLIENT_ID / AGY_CLIENT_SECRET env\n * overrides win when set (BYO OAuth app escape hatch, mirrors pi-antigravity);\n * otherwise the embedded public Antigravity credentials are used.\n */\nexport function resolveAgyClientCredentials(overrideClientId?: string): { clientId: string; clientSecret: string } {\n if (overrideClientId) {\n if (overrideClientId === AGY_CLIENT_ID) {\n return { clientId: AGY_CLIENT_ID, clientSecret: AGY_CLIENT_SECRET }\n }\n return {\n clientId: overrideClientId,\n clientSecret: process.env.AGY_CLIENT_SECRET || AGY_CLIENT_SECRET,\n }\n }\n return {\n clientId: process.env.AGY_CLIENT_ID || AGY_CLIENT_ID,\n clientSecret: process.env.AGY_CLIENT_SECRET || AGY_CLIENT_SECRET,\n }\n}\n\n/** Required scopes. `openid` must NOT be added: it routes Google into the hanging\n * `firstparty/nativeapp` consent for this client (verified by OmniRoute). */\nexport const AGY_SCOPES: readonly string[] = [\n 'https://www.googleapis.com/auth/cloud-platform',\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'https://www.googleapis.com/auth/cclog',\n 'https://www.googleapis.com/auth/experimentsandconfigs',\n]\n\nexport const OAUTH_AUTHORIZE_URL = 'https://accounts.google.com/o/oauth2/v2/auth'\nexport const OAUTH_TOKEN_URL = 'https://oauth2.googleapis.com/token'\nexport const OAUTH_USERINFO_URL = 'https://www.googleapis.com/oauth2/v1/userinfo'\n\n/** Default loopback callback used by the standalone CLI listener (fixed port, like opencode). */\nexport const AGY_DEFAULT_REDIRECT_URI = 'http://localhost:51121/oauth-callback'\n\n/**\n * Antigravity API endpoints. The daily runtime host (no .sandbox suffix) is the\n * live endpoint for consumer OAuth accounts — cloudcode-pa.googleapis.com\n * answers RESOURCE_EXHAUSTED for them (verified by live probe), while the\n * daily host answers 200. Order matters: first reachable non-429/403/503 wins.\n */\nexport const AGY_ENDPOINT_DAILY = 'https://daily-cloudcode-pa.googleapis.com'\nexport const AGY_ENDPOINT_PROD = 'https://cloudcode-pa.googleapis.com'\nexport const AGY_ENDPOINT_DAILY_SANDBOX = 'https://daily-cloudcode-pa.sandbox.googleapis.com'\nexport const AGY_ENDPOINT_AUTOPUSH = 'https://autopush-cloudcode-pa.sandbox.googleapis.com'\n\n/** Runtime/bootstrap endpoint fallback order (daily first, mirroring OmniRoute). */\nexport const AGY_ENDPOINT_FALLBACKS: readonly string[] = [\n AGY_ENDPOINT_DAILY,\n AGY_ENDPOINT_PROD,\n AGY_ENDPOINT_DAILY_SANDBOX,\n AGY_ENDPOINT_AUTOPUSH,\n]\n\n/**\n * Statuses that mean \"this endpoint is not usable for this attempt\"; skip to the\n * next one in the chain. 429/403 = rate/quota/entitlement wall, 503 = capacity\n * rejection (e.g. \"No capacity available for model ...\"). A 503 skipped here\n * still reaches the caller when every endpoint fails, where the classifier marks\n * it transient and the adapter surfaces it as a retryable SERVER error.\n */\nexport const AGY_ENDPOINT_SKIP_STATUSES = new Set([429, 403, 503])\n\n/** Routing of the requests being tried (see {@link AccountRouting}). */\nexport type AgyFallbackOptions = Pick<AccountRouting, 'proxyUrl'>\n/**\n * Try each runtime endpoint in order, skipping unusable ones (429/403/503/network).\n * Returns the first other response (2xx or a real error like 400/401); when\n * every endpoint is unusable, returns the last skipped response so the caller's\n * classifier can still produce a meaningful error (a returned 503 becomes a\n * retryable SERVER failure rather than being swallowed here).\n *\n * When every endpoint fails at the network level, the LAST error is rethrown so\n * its cause survives (a generic \"all endpoints failed\" hid DNS/TLS/timeout).\n */\nexport async function fetchAgyFirstOk(\n urlPath: string,\n init: RequestInit,\n fetchImpl: typeof fetch = proxiedFetch,\n options: AgyFallbackOptions = {},\n): Promise<Response> {\n let lastSkipped: Response | null = null\n let lastNetworkError: unknown\n for (const baseEndpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const response = await fetchImpl(`${baseEndpoint}${urlPath}`, init)\n if (!AGY_ENDPOINT_SKIP_STATUSES.has(response.status)) return response\n lastSkipped = response\n } catch (error) {\n lastNetworkError = error\n // Fail-closed only while an explicit per-account proxy is in effect: the\n // request must not be retried on another endpoint or go direct.\n if (isProxyRouted(options)) {\n const { isProxyUnreachableError } = await import('../proxy.ts')\n if (isProxyUnreachableError(error)) throw error\n }\n // network error — try the next endpoint\n }\n }\n if (lastSkipped) return lastSkipped\n if (lastNetworkError !== undefined) throw lastNetworkError\n throw new Error('all agy endpoints failed')\n}\n\n/**\n * Pinned Antigravity version, used only until the runtime resolver has published\n * a fresh one (see {@link setResolvedAgyVersion}).\n *\n * A stale version string is the most detectable fingerprint anomaly, so this\n * value is a cold-start floor, not the version anything should normally send.\n * It tracks the release feed's newest entry; refresh it when the feed moves.\n *\n * NAMESPACE: this is the **Antigravity CLI** line (`google-antigravity/antigravity-cli`),\n * not the IDE or hub line, because that is the product this client claims to be\n * (`docs/official-identity.json`). The three lines are separate version\n * namespaces — IDE `2.0.0`, hub `2.15.1`, CLI `1.2.9` — so comparing them\n * numerically is meaningless and mixing them advertises a version that does not\n * exist for the product we name.\n */\nexport const AGY_VERSION_FALLBACK = '1.2.9'\n\n/**\n * `ClientMetadata.platform` value: the proto enum NAME, as protobuf-JSON emits it.\n *\n * The official enumeration is `PLATFORM_UNSPECIFIED | DARWIN_AMD64 | DARWIN_ARM64\n * | LINUX_AMD64 | LINUX_ARM64 | WINDOWS_AMD64` (read from the installed official\n * CLI's own descriptor). The earlier `\"MACOS\"` that this backend rejected with\n * `INVALID_ARGUMENT` was an invalid *value*, not a forbidden field — a\n * distinction that mattered, because it had been read as \"send `ideType` only\".\n */\nexport const AGY_PLATFORM_ENUM = 'DARWIN_ARM64'\n\n/**\n * Newest Antigravity version the runtime resolver has observed, or undefined\n * before its first success.\n *\n * Deliberately lives in this module — the dependency leaf — rather than in\n * `runtime/version.ts`. `oauth/` must not import `runtime/` (AGENTS.md), and the\n * edge already runs the other way: `runtime/version.ts` imports THIS file. So the\n * resolver publishes here and every User-Agent builder reads here.\n *\n * This shape is the fix for a real defect: the version used to be a function\n * parameter, and all six bootstrap call sites silently took the default, pinning\n * every control-plane request to the stale fallback while the data plane used the\n * resolved version — one process presenting two different clients. Reading a\n * shared value makes that drift unrepresentable rather than merely discouraged.\n */\nlet resolvedAgyVersion: string | undefined\n\n/** Publish a freshly resolved version for every User-Agent builder. */\nexport function setResolvedAgyVersion(version: string | undefined): void {\n resolvedAgyVersion = version\n}\n\n/** Version a User-Agent should advertise: the resolved one, else the pinned floor. */\nexport function currentAgyVersion(): string {\n return resolvedAgyVersion ?? AGY_VERSION_FALLBACK\n}\n\n/** Electron-style UA used for bootstrap calls (loadCodeAssist/onboardUser). */\nexport function getAgyBootstrapUserAgent(version = currentAgyVersion()): string {\n return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Antigravity/${version} Chrome/138.0.7204.235 Electron/37.3.1 Safari/537.36`\n}\n\n/**\n * Short-form client User-Agent: `antigravity/<version> <platform>`.\n *\n * Used where the full Electron string would be inappropriate but the request\n * must still not self-identify as this tool (e.g. the version feeds, which the\n * official client ecosystem also polls).\n */\nexport function antigravityUserAgent(version = currentAgyVersion(), platform = 'darwin/arm64'): string {\n return `antigravity/${version} ${platform}`\n}\n\n/**\n * `ClientMetadata.ideType` value.\n *\n * `ANTIGRAVITY` is retained rather than switched to the enumeration's newer\n * `GEMINI_CLI`: the CLI's own `IdeType` is injected through its auth provider and\n * has not been captured, so the established working value is kept and the\n * alternative recorded (`docs/official-identity.json`) rather than guessed at.\n */\nexport const AGY_IDE_TYPE = 'ANTIGRAVITY'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA0GA,MAAa,gBAA+E;CAC1F,MAAM;EAAE,MAAM;EAAG,MAAM;CAAU;CACjC,OAAO,EAAE,MAAM,EAAE;CACjB,QAAQ;EAAE,MAAM;EAAG,MAAM;CAAS;CAClC,SAAS,EAAE,MAAM,EAAE;AACrB;;AA8IA,SAAgB,cAAc,SAA8C;CAC1E,OAAO,QAAQ,SAAS,QAAQ;AAClC;;;;;AA4DA,IAAa,eAAb,cAAkC,MAAM;CACtC;CAEA,YAAY,MAAwB,SAAiB,SAAwB;EAC3E,MAAM,SAAS,OAAO;EACtB,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF;;;;;AASA,IAAa,sBAAb,cAAyC,MAAM;CAC7C;CACA;CAEA,YAAY,MAAuB,cAAsB;EACvD,MAAM,SAAS,oBACX,0EACA,0EAA0E;EAC9E,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,eAAe;CACtB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3UA,MAAa,gBACX;AAEF,MAAa,oBAAoB;;;;;;AAOjC,SAAgB,4BAA4B,kBAAuE;CACjH,IAAI,kBAAkB;EACpB,IAAI,qBAAA,6EACF,OAAO;GAAE,UAAU;GAAe,cAAc;EAAkB;EAEpE,OAAO;GACL,UAAU;GACV,cAAc,QAAQ,IAAI,qBAAA;EAC5B;CACF;CACA,OAAO;EACL,UAAU,QAAQ,IAAI,iBAAA;EACtB,cAAc,QAAQ,IAAI,qBAAA;CAC5B;AACF;;;AAIA,MAAa,aAAgC;CAC3C;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,sBAAsB;AACnC,MAAa,kBAAkB;AAC/B,MAAa,qBAAqB;;AAGlC,MAAa,2BAA2B;;;;;;;AAQxC,MAAa,qBAAqB;AAClC,MAAa,oBAAoB;AACjC,MAAa,6BAA6B;AAC1C,MAAa,wBAAwB;;AAGrC,MAAa,yBAA4C;CACvD;CACA;CACA;CACA;AACF;;;;;;;;AASA,MAAa,6CAA6B,IAAI,IAAI;CAAC;CAAK;CAAK;AAAG,CAAC;;;;;;;;;;;AAcjE,eAAsB,gBACpB,SACA,MACA,YAA0B,cAC1B,UAA8B,CAAC,GACZ;CACnB,IAAI,cAA+B;CACnC,IAAI;CACJ,KAAK,MAAM,gBAAgB,wBACzB,IAAI;EACF,MAAM,WAAW,MAAM,UAAU,GAAG,eAAe,WAAW,IAAI;EAClE,IAAI,CAAC,2BAA2B,IAAI,SAAS,MAAM,GAAG,OAAO;EAC7D,cAAc;CAChB,SAAS,OAAO;EACd,mBAAmB;EAGnB,IAAI,cAAc,OAAO,GAAG;GAC1B,MAAM,EAAE,4BAA4B,MAAM,OAAO,uBAAc,CAAA,MAAA,MAAA,EAAA,CAAA;GAC/D,IAAI,wBAAwB,KAAK,GAAG,MAAM;EAC5C;CAEF;CAEF,IAAI,aAAa,OAAO;CACxB,IAAI,qBAAqB,KAAA,GAAW,MAAM;CAC1C,MAAM,IAAI,MAAM,0BAA0B;AAC5C;;;;;;;;;;;;;;;;AAiBA,MAAa,uBAAuB;;;;;;;;;;AAWpC,MAAa,oBAAoB;;;;;;;;;;;;;;;;AAiBjC,IAAI;;AAGJ,SAAgB,sBAAsB,SAAmC;CACvE,qBAAqB;AACvB;;AAGA,SAAgB,oBAA4B;CAC1C,OAAO,sBAAA;AACT;;AAGA,SAAgB,yBAAyB,UAAU,kBAAkB,GAAW;CAC9E,OAAO,gGAAgG,QAAQ;AACjH;;;;;;;;AASA,SAAgB,qBAAqB,UAAU,kBAAkB,GAAG,WAAW,gBAAwB;CACrG,OAAO,eAAe,QAAQ,GAAG;AACnC;;;;;;;;;AAUA,MAAa,eAAe"}
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { a as AGY_PLATFORM_ENUM, g as resolveAgyClientCredentials, h as getAgyBootstrapUserAgent, i as AGY_IDE_TYPE, l as OAUTH_TOKEN_URL, p as currentAgyVersion, r as AGY_ENDPOINT_FALLBACKS, u as OAUTH_USERINFO_URL } from "./constants-Db4tmyfb.mjs";
2
+ import { a as AGY_PLATFORM_ENUM, g as resolveAgyClientCredentials, h as getAgyBootstrapUserAgent, i as AGY_IDE_TYPE, l as OAUTH_TOKEN_URL, p as currentAgyVersion, r as AGY_ENDPOINT_FALLBACKS, u as OAUTH_USERINFO_URL } from "./constants-BpyRirvk.mjs";
3
3
  import { o as proxiedFetch } from "./proxy-DfaL73mL.mjs";
4
4
  import { n as calculateTokenExpiry } from "./auth-DPPVFwio.mjs";
5
5
  import { createHash, randomBytes } from "node:crypto";
@@ -256,4 +256,4 @@ async function exchangeAntigravity(code, state, redirectUri, expectedVerifier, r
256
256
  //#endregion
257
257
  export { generatePkcePair as i, exchange_exports as n, encodeState as r, exchangeAntigravity as t };
258
258
 
259
- //# sourceMappingURL=exchange-yZPIZumk.mjs.map
259
+ //# sourceMappingURL=exchange-LzEyxve1.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"exchange-yZPIZumk.mjs","names":["record"],"sources":["../src/oauth/pkce.ts","../src/oauth/exchange.ts"],"sourcesContent":["/** Minimal PKCE (RFC 7636) with S256, hand-rolled on node:crypto (no extra dependency). */\n\nimport { createHash, randomBytes } from 'node:crypto'\n\nfunction base64url(input: Buffer): string {\n return input.toString('base64url')\n}\n\n/** Generate a PKCE verifier (43-char URL-safe random) and its S256 challenge. */\nexport function generatePkcePair(): { verifier: string; challenge: string } {\n const verifier = base64url(randomBytes(32))\n const challenge = base64url(createHash('sha256').update(verifier).digest())\n return { verifier, challenge }\n}\n\n/** Encode an object into a URL-safe base64 string (used for the OAuth state). */\nexport function encodeState(payload: unknown): string {\n return Buffer.from(JSON.stringify(payload), 'utf8').toString('base64url')\n}\n\n/** Decode a URL-safe base64 OAuth state back into its structured representation. */\nexport function decodeState<T>(state: string): T {\n const normalized = state.replace(/-/g, '+').replace(/_/g, '/')\n const padded = normalized.padEnd(\n normalized.length + (((4 - (normalized.length % 4)) % 4)),\n '=',\n )\n const json = Buffer.from(padded, 'base64').toString('utf8')\n return JSON.parse(json) as T\n}\n","/**\n * Exchange an authorization code for agy tokens, then resolve identity and\n * project via the Code Assist backend (userinfo + loadCodeAssist, multi-endpoint\n * fallback, best-effort).\n */\n\nimport type { AccountRouting, TokenExchangeFailure, TokenExchangeResult } from '../types.ts'\nimport { calculateTokenExpiry } from './auth.ts'\nimport {\n AGY_ENDPOINT_FALLBACKS,\n AGY_IDE_TYPE,\n AGY_PLATFORM_ENUM,\n OAUTH_TOKEN_URL,\n OAUTH_USERINFO_URL,\n currentAgyVersion,\n getAgyBootstrapUserAgent,\n resolveAgyClientCredentials,\n} from './constants.ts'\nimport { decodeState } from './pkce.ts'\nimport { proxiedFetch } from '../proxy.ts'\n\nconst FETCH_TIMEOUT_MS = 10_000\n\ninterface OAuthState {\n verifier: string\n projectId?: string\n}\n\ninterface TokenPayload {\n access_token: string\n expires_in?: number\n refresh_token?: string\n}\n\ninterface UserInfo {\n email?: string\n}\n\nasync function fetchWithTimeout(\n url: string,\n options: RequestInit,\n timeoutMs = FETCH_TIMEOUT_MS,\n proxyUrl?: string,\n): Promise<Response> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await proxiedFetch(url, { ...options, signal: controller.signal }, { proxyUrl })\n } finally {\n clearTimeout(timeout)\n }\n}\n\n/** Bootstrap calls take the same routing as any other account-scoped request. */\nexport type AgyBootstrapOptions = AccountRouting\n\ninterface LoadCodeAssistData {\n cloudaicompanionProject?: unknown\n subscriptionInfo?: unknown\n}\n\n/**\n * Build the metadata payload shared by loadCodeAssist and onboardUser.\n *\n * This is the `ClientMetadata` BODY message (`docs/official-identity.json`), and\n * it replaces the `Client-Metadata` header this client used to send in addition:\n * neither official binary contains that header name, while `LoadCodeAssistRequest.metadata`\n * and `OnboardUserRequest.metadata` are both typed\n * `google.internal.cloud.code.v1internal.ClientMetadata` in the official\n * descriptor.\n *\n * The old \"send `ideType` only\" rule was a misreading of a real measurement: the\n * live `INVALID_ARGUMENT` was on the VALUE `\"MACOS\"`, which is not in the\n * `Platform` enum (`DARWIN_AMD64 | DARWIN_ARM64 | LINUX_AMD64 | LINUX_ARM64 |\n * WINDOWS_AMD64`). A bad enum value is not a forbidden field. `AGY_PLATFORM_ENUM`\n * carries a valid one.\n */\nexport function bootstrapMetadata(): Record<string, string> {\n return {\n ideType: AGY_IDE_TYPE,\n ideVersion: currentAgyVersion(),\n platform: AGY_PLATFORM_ENUM,\n }\n}\n\nfunction extractProjectId(data: LoadCodeAssistData): string {\n const project = data.cloudaicompanionProject\n if (typeof project === 'string' && project) return project\n const record = project as { id?: string } | undefined\n return record && typeof record.id === 'string' && record.id ? record.id : ''\n}\n\n/**\n * Extract the subscription tier id used for onboardUser, mirroring OmniRoute's\n * codeAssistSubscription: paid → current → default allowed → legacy-tier.\n */\nexport function extractOnboardTierId(subscriptionInfo: unknown): string {\n const subscription = (subscriptionInfo ?? {}) as Record<string, unknown>\n const tierOf = (value: unknown, field: 'name' | 'id'): string | null => {\n const record = value as Record<string, unknown> | undefined\n const picked = record?.[field]\n return typeof picked === 'string' && picked.trim() ? picked.trim() : null\n }\n\n const paidId = tierOf(subscription.paidTier, 'id')\n if (paidId) return paidId\n\n const ineligible = Array.isArray(subscription.ineligibleTiers) && subscription.ineligibleTiers.length > 0\n if (!ineligible) {\n const currentId = tierOf(subscription.currentTier, 'id')\n if (currentId) return currentId\n }\n\n if (Array.isArray(subscription.allowedTiers)) {\n for (const tierValue of subscription.allowedTiers) {\n const tier = tierValue as Record<string, unknown>\n if (tier.isDefault) {\n const defaultId = tierOf(tier, 'id')\n if (defaultId) return defaultId\n }\n }\n }\n\n const currentId = tierOf(subscription.currentTier, 'id')\n if (currentId) return currentId\n return 'legacy-tier'\n}\n\n/** Resolve project id + tier id via loadCodeAssist across fallback endpoints. */\nexport async function loadCodeAssist(\n accessToken: string,\n options: AgyBootstrapOptions = {},\n): Promise<{ projectId: string; tierId: string }> {\n const errors: string[] = []\n const loadHeaders: Record<string, string> = {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': getAgyBootstrapUserAgent(),\n }\n\n for (const baseEndpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const url = `${baseEndpoint}/v1internal:loadCodeAssist`\n const response = await fetchWithTimeout(url, {\n method: 'POST',\n headers: loadHeaders,\n body: JSON.stringify({ metadata: bootstrapMetadata() }),\n }, FETCH_TIMEOUT_MS, options.proxyUrl)\n\n if (!response.ok) {\n const message = await response.text().catch(() => '')\n errors.push(`loadCodeAssist ${response.status} at ${baseEndpoint}${message ? `: ${message}` : ''}`)\n continue\n }\n\n const data = (await response.json()) as LoadCodeAssistData\n const projectId = extractProjectId(data)\n if (projectId) {\n return { projectId, tierId: extractOnboardTierId(data.subscriptionInfo) }\n }\n errors.push(`loadCodeAssist missing project id at ${baseEndpoint}`)\n } catch (error) {\n errors.push(\n `loadCodeAssist error at ${baseEndpoint}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n\n return { projectId: '', tierId: 'legacy-tier' }\n}\n\n/**\n * Onboard a new account (no Cloud Code project yet): POST onboardUser with the\n * tier id, then retry loadCodeAssist. Mirrors OmniRoute's postExchange flow\n * (bounded inline onboarding, 10 attempts × 5s).\n */\nexport async function onboardAndDiscoverProject(\n accessToken: string,\n tierId: string,\n options: { maxAttempts?: number; retryDelayMs?: number } & AgyBootstrapOptions = {},\n): Promise<{ projectId: string; tierId: string }> {\n // Bounded onboarding: 3 attempts with jittered delay (3-7s). Fixed-rhythm\n // long retry loops read as scripted automation to the upstream (OmniRoute\n // #9939 ban-safety hardening capped 10x5s to 3x3-7s).\n const maxAttempts = options.maxAttempts ?? 3\n const retryDelayMs = options.retryDelayMs ?? 3000 + Math.floor(Math.random() * 4000)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': getAgyBootstrapUserAgent(),\n }\n const metadata = bootstrapMetadata()\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n for (const baseEndpoint of AGY_ENDPOINT_FALLBACKS) {\n const response = await fetchWithTimeout(`${baseEndpoint}/v1internal:onboardUser`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ tier_id: tierId, metadata }),\n }, FETCH_TIMEOUT_MS, options.proxyUrl)\n if (!response.ok) continue\n const result = (await response.json()) as { done?: boolean }\n if (result.done === true) {\n const discovered = await loadCodeAssist(accessToken, { proxyUrl: options.proxyUrl })\n if (discovered.projectId) return discovered\n }\n }\n } catch {\n // transient — retry\n }\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs))\n }\n\n return { projectId: '', tierId }\n}\n\n/** Full bootstrap for a fresh account: discover the project, onboarding if needed. */\nexport async function bootstrapAccount(\n accessToken: string,\n options: { maxAttempts?: number; retryDelayMs?: number } & AgyBootstrapOptions = {},\n): Promise<{ projectId: string; tierId: string }> {\n const discovered = await loadCodeAssist(accessToken, options)\n if (discovered.projectId) return discovered\n return onboardAndDiscoverProject(accessToken, discovered.tierId, options)\n}\n\n/** Resolve the account's Cloud Code project id via loadCodeAssist across fallback endpoints. */\nexport async function fetchProjectID(accessToken: string, options: AgyBootstrapOptions = {}): Promise<string> {\n return (await loadCodeAssist(accessToken, options)).projectId\n}\n\n/**\n * Exchange the authorization code (from the loopback callback) for access/refresh\n * tokens, resolve the email, and discover the project id.\n * @param code - the `code` query parameter from the redirect.\n * @param state - the `state` query parameter (encodes verifier + projectId).\n * @param redirectUri - must match the authorize() redirectUri.\n */\nexport async function exchangeAntigravity(\n code: string,\n state: string,\n redirectUri: string,\n expectedVerifier?: string,\n routing: AccountRouting = {},\n): Promise<TokenExchangeResult> {\n try {\n const { verifier, projectId } = decodeState<OAuthState>(state)\n if (!verifier) {\n return { type: 'failed', error: 'Missing PKCE verifier in state' }\n }\n // The caller issued this authorization URL (the CLI holds the verifier;\n // the web entry stores it per state). A state from a different login — or\n // one fabricated by an attacker — must not be exchangeable into the store.\n if (expectedVerifier && verifier !== expectedVerifier) {\n return { type: 'failed', error: 'State does not match the issued authorization' }\n }\n\n const startTime = Date.now()\n const { clientId, clientSecret } = resolveAgyClientCredentials()\n const tokenResponse = await proxiedFetch(OAUTH_TOKEN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n Accept: '*/*',\n 'User-Agent': getAgyBootstrapUserAgent(),\n },\n body: new URLSearchParams({\n client_id: clientId,\n client_secret: clientSecret,\n code,\n grant_type: 'authorization_code',\n redirect_uri: redirectUri,\n code_verifier: verifier,\n }),\n }, routing)\n\n if (!tokenResponse.ok) {\n const errorText = await tokenResponse.text()\n return { type: 'failed', error: errorText }\n }\n\n const tokenPayload = (await tokenResponse.json()) as TokenPayload\n\n const userInfoResponse = await proxiedFetch(`${OAUTH_USERINFO_URL}?alt=json`, {\n headers: {\n Authorization: `Bearer ${tokenPayload.access_token}`,\n 'User-Agent': getAgyBootstrapUserAgent(),\n },\n }, routing)\n const userInfo: UserInfo = userInfoResponse.ok\n ? ((await userInfoResponse.json()) as UserInfo)\n : {}\n\n const refreshToken = tokenPayload.refresh_token\n if (!refreshToken) {\n return { type: 'failed', error: 'Missing refresh token in response' }\n }\n\n const effectiveProjectId = projectId || (await bootstrapAccount(tokenPayload.access_token, routing)).projectId\n\n return {\n type: 'success',\n refresh: `${refreshToken}|${effectiveProjectId || ''}`,\n access: tokenPayload.access_token,\n expires: calculateTokenExpiry(startTime, tokenPayload.expires_in),\n email: userInfo.email,\n projectId: effectiveProjectId || '',\n clientId,\n }\n } catch (error) {\n const failure: TokenExchangeFailure = {\n type: 'failed',\n error: error instanceof Error ? error.message : 'Unknown error',\n }\n return failure\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"exchange-LzEyxve1.mjs","names":["record"],"sources":["../src/oauth/pkce.ts","../src/oauth/exchange.ts"],"sourcesContent":["/** Minimal PKCE (RFC 7636) with S256, hand-rolled on node:crypto (no extra dependency). */\n\nimport { createHash, randomBytes } from 'node:crypto'\n\nfunction base64url(input: Buffer): string {\n return input.toString('base64url')\n}\n\n/** Generate a PKCE verifier (43-char URL-safe random) and its S256 challenge. */\nexport function generatePkcePair(): { verifier: string; challenge: string } {\n const verifier = base64url(randomBytes(32))\n const challenge = base64url(createHash('sha256').update(verifier).digest())\n return { verifier, challenge }\n}\n\n/** Encode an object into a URL-safe base64 string (used for the OAuth state). */\nexport function encodeState(payload: unknown): string {\n return Buffer.from(JSON.stringify(payload), 'utf8').toString('base64url')\n}\n\n/** Decode a URL-safe base64 OAuth state back into its structured representation. */\nexport function decodeState<T>(state: string): T {\n const normalized = state.replace(/-/g, '+').replace(/_/g, '/')\n const padded = normalized.padEnd(\n normalized.length + (((4 - (normalized.length % 4)) % 4)),\n '=',\n )\n const json = Buffer.from(padded, 'base64').toString('utf8')\n return JSON.parse(json) as T\n}\n","/**\n * Exchange an authorization code for agy tokens, then resolve identity and\n * project via the Code Assist backend (userinfo + loadCodeAssist, multi-endpoint\n * fallback, best-effort).\n */\n\nimport type { AccountRouting, TokenExchangeFailure, TokenExchangeResult } from '../types.ts'\nimport { calculateTokenExpiry } from './auth.ts'\nimport {\n AGY_ENDPOINT_FALLBACKS,\n AGY_IDE_TYPE,\n AGY_PLATFORM_ENUM,\n OAUTH_TOKEN_URL,\n OAUTH_USERINFO_URL,\n currentAgyVersion,\n getAgyBootstrapUserAgent,\n resolveAgyClientCredentials,\n} from './constants.ts'\nimport { decodeState } from './pkce.ts'\nimport { proxiedFetch } from '../proxy.ts'\n\nconst FETCH_TIMEOUT_MS = 10_000\n\ninterface OAuthState {\n verifier: string\n projectId?: string\n}\n\ninterface TokenPayload {\n access_token: string\n expires_in?: number\n refresh_token?: string\n}\n\ninterface UserInfo {\n email?: string\n}\n\nasync function fetchWithTimeout(\n url: string,\n options: RequestInit,\n timeoutMs = FETCH_TIMEOUT_MS,\n proxyUrl?: string,\n): Promise<Response> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await proxiedFetch(url, { ...options, signal: controller.signal }, { proxyUrl })\n } finally {\n clearTimeout(timeout)\n }\n}\n\n/** Bootstrap calls take the same routing as any other account-scoped request. */\nexport type AgyBootstrapOptions = AccountRouting\n\ninterface LoadCodeAssistData {\n cloudaicompanionProject?: unknown\n subscriptionInfo?: unknown\n}\n\n/**\n * Build the metadata payload shared by loadCodeAssist and onboardUser.\n *\n * This is the `ClientMetadata` BODY message (`docs/official-identity.json`), and\n * it replaces the `Client-Metadata` header this client used to send in addition:\n * neither official binary contains that header name, while `LoadCodeAssistRequest.metadata`\n * and `OnboardUserRequest.metadata` are both typed\n * `google.internal.cloud.code.v1internal.ClientMetadata` in the official\n * descriptor.\n *\n * The old \"send `ideType` only\" rule was a misreading of a real measurement: the\n * live `INVALID_ARGUMENT` was on the VALUE `\"MACOS\"`, which is not in the\n * `Platform` enum (`DARWIN_AMD64 | DARWIN_ARM64 | LINUX_AMD64 | LINUX_ARM64 |\n * WINDOWS_AMD64`). A bad enum value is not a forbidden field. `AGY_PLATFORM_ENUM`\n * carries a valid one.\n */\nexport function bootstrapMetadata(): Record<string, string> {\n return {\n ideType: AGY_IDE_TYPE,\n ideVersion: currentAgyVersion(),\n platform: AGY_PLATFORM_ENUM,\n }\n}\n\nfunction extractProjectId(data: LoadCodeAssistData): string {\n const project = data.cloudaicompanionProject\n if (typeof project === 'string' && project) return project\n const record = project as { id?: string } | undefined\n return record && typeof record.id === 'string' && record.id ? record.id : ''\n}\n\n/**\n * Extract the subscription tier id used for onboardUser, mirroring OmniRoute's\n * codeAssistSubscription: paid → current → default allowed → legacy-tier.\n */\nexport function extractOnboardTierId(subscriptionInfo: unknown): string {\n const subscription = (subscriptionInfo ?? {}) as Record<string, unknown>\n const tierOf = (value: unknown, field: 'name' | 'id'): string | null => {\n const record = value as Record<string, unknown> | undefined\n const picked = record?.[field]\n return typeof picked === 'string' && picked.trim() ? picked.trim() : null\n }\n\n const paidId = tierOf(subscription.paidTier, 'id')\n if (paidId) return paidId\n\n const ineligible = Array.isArray(subscription.ineligibleTiers) && subscription.ineligibleTiers.length > 0\n if (!ineligible) {\n const currentId = tierOf(subscription.currentTier, 'id')\n if (currentId) return currentId\n }\n\n if (Array.isArray(subscription.allowedTiers)) {\n for (const tierValue of subscription.allowedTiers) {\n const tier = tierValue as Record<string, unknown>\n if (tier.isDefault) {\n const defaultId = tierOf(tier, 'id')\n if (defaultId) return defaultId\n }\n }\n }\n\n const currentId = tierOf(subscription.currentTier, 'id')\n if (currentId) return currentId\n return 'legacy-tier'\n}\n\n/** Resolve project id + tier id via loadCodeAssist across fallback endpoints. */\nexport async function loadCodeAssist(\n accessToken: string,\n options: AgyBootstrapOptions = {},\n): Promise<{ projectId: string; tierId: string }> {\n const errors: string[] = []\n const loadHeaders: Record<string, string> = {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': getAgyBootstrapUserAgent(),\n }\n\n for (const baseEndpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const url = `${baseEndpoint}/v1internal:loadCodeAssist`\n const response = await fetchWithTimeout(url, {\n method: 'POST',\n headers: loadHeaders,\n body: JSON.stringify({ metadata: bootstrapMetadata() }),\n }, FETCH_TIMEOUT_MS, options.proxyUrl)\n\n if (!response.ok) {\n const message = await response.text().catch(() => '')\n errors.push(`loadCodeAssist ${response.status} at ${baseEndpoint}${message ? `: ${message}` : ''}`)\n continue\n }\n\n const data = (await response.json()) as LoadCodeAssistData\n const projectId = extractProjectId(data)\n if (projectId) {\n return { projectId, tierId: extractOnboardTierId(data.subscriptionInfo) }\n }\n errors.push(`loadCodeAssist missing project id at ${baseEndpoint}`)\n } catch (error) {\n errors.push(\n `loadCodeAssist error at ${baseEndpoint}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n\n return { projectId: '', tierId: 'legacy-tier' }\n}\n\n/**\n * Onboard a new account (no Cloud Code project yet): POST onboardUser with the\n * tier id, then retry loadCodeAssist. Mirrors OmniRoute's postExchange flow\n * (bounded inline onboarding, 10 attempts × 5s).\n */\nexport async function onboardAndDiscoverProject(\n accessToken: string,\n tierId: string,\n options: { maxAttempts?: number; retryDelayMs?: number } & AgyBootstrapOptions = {},\n): Promise<{ projectId: string; tierId: string }> {\n // Bounded onboarding: 3 attempts with jittered delay (3-7s). Fixed-rhythm\n // long retry loops read as scripted automation to the upstream (OmniRoute\n // #9939 ban-safety hardening capped 10x5s to 3x3-7s).\n const maxAttempts = options.maxAttempts ?? 3\n const retryDelayMs = options.retryDelayMs ?? 3000 + Math.floor(Math.random() * 4000)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': getAgyBootstrapUserAgent(),\n }\n const metadata = bootstrapMetadata()\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n for (const baseEndpoint of AGY_ENDPOINT_FALLBACKS) {\n const response = await fetchWithTimeout(`${baseEndpoint}/v1internal:onboardUser`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ tier_id: tierId, metadata }),\n }, FETCH_TIMEOUT_MS, options.proxyUrl)\n if (!response.ok) continue\n const result = (await response.json()) as { done?: boolean }\n if (result.done === true) {\n const discovered = await loadCodeAssist(accessToken, { proxyUrl: options.proxyUrl })\n if (discovered.projectId) return discovered\n }\n }\n } catch {\n // transient — retry\n }\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs))\n }\n\n return { projectId: '', tierId }\n}\n\n/** Full bootstrap for a fresh account: discover the project, onboarding if needed. */\nexport async function bootstrapAccount(\n accessToken: string,\n options: { maxAttempts?: number; retryDelayMs?: number } & AgyBootstrapOptions = {},\n): Promise<{ projectId: string; tierId: string }> {\n const discovered = await loadCodeAssist(accessToken, options)\n if (discovered.projectId) return discovered\n return onboardAndDiscoverProject(accessToken, discovered.tierId, options)\n}\n\n/** Resolve the account's Cloud Code project id via loadCodeAssist across fallback endpoints. */\nexport async function fetchProjectID(accessToken: string, options: AgyBootstrapOptions = {}): Promise<string> {\n return (await loadCodeAssist(accessToken, options)).projectId\n}\n\n/**\n * Exchange the authorization code (from the loopback callback) for access/refresh\n * tokens, resolve the email, and discover the project id.\n * @param code - the `code` query parameter from the redirect.\n * @param state - the `state` query parameter (encodes verifier + projectId).\n * @param redirectUri - must match the authorize() redirectUri.\n */\nexport async function exchangeAntigravity(\n code: string,\n state: string,\n redirectUri: string,\n expectedVerifier?: string,\n routing: AccountRouting = {},\n): Promise<TokenExchangeResult> {\n try {\n const { verifier, projectId } = decodeState<OAuthState>(state)\n if (!verifier) {\n return { type: 'failed', error: 'Missing PKCE verifier in state' }\n }\n // The caller issued this authorization URL (the CLI holds the verifier;\n // the web entry stores it per state). A state from a different login — or\n // one fabricated by an attacker — must not be exchangeable into the store.\n if (expectedVerifier && verifier !== expectedVerifier) {\n return { type: 'failed', error: 'State does not match the issued authorization' }\n }\n\n const startTime = Date.now()\n const { clientId, clientSecret } = resolveAgyClientCredentials()\n const tokenResponse = await proxiedFetch(OAUTH_TOKEN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n Accept: '*/*',\n 'User-Agent': getAgyBootstrapUserAgent(),\n },\n body: new URLSearchParams({\n client_id: clientId,\n client_secret: clientSecret,\n code,\n grant_type: 'authorization_code',\n redirect_uri: redirectUri,\n code_verifier: verifier,\n }),\n }, routing)\n\n if (!tokenResponse.ok) {\n const errorText = await tokenResponse.text()\n return { type: 'failed', error: errorText }\n }\n\n const tokenPayload = (await tokenResponse.json()) as TokenPayload\n\n const userInfoResponse = await proxiedFetch(`${OAUTH_USERINFO_URL}?alt=json`, {\n headers: {\n Authorization: `Bearer ${tokenPayload.access_token}`,\n 'User-Agent': getAgyBootstrapUserAgent(),\n },\n }, routing)\n const userInfo: UserInfo = userInfoResponse.ok\n ? ((await userInfoResponse.json()) as UserInfo)\n : {}\n\n const refreshToken = tokenPayload.refresh_token\n if (!refreshToken) {\n return { type: 'failed', error: 'Missing refresh token in response' }\n }\n\n const effectiveProjectId = projectId || (await bootstrapAccount(tokenPayload.access_token, routing)).projectId\n\n return {\n type: 'success',\n refresh: `${refreshToken}|${effectiveProjectId || ''}`,\n access: tokenPayload.access_token,\n expires: calculateTokenExpiry(startTime, tokenPayload.expires_in),\n email: userInfo.email,\n projectId: effectiveProjectId || '',\n clientId,\n }\n } catch (error) {\n const failure: TokenExchangeFailure = {\n type: 'failed',\n error: error instanceof Error ? error.message : 'Unknown error',\n }\n return failure\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { a as AGY_PLATFORM_ENUM, c as OAUTH_AUTHORIZE_URL, g as resolveAgyClientCredentials, h as getAgyBootstrapUserAgent, i as AGY_IDE_TYPE, o as AGY_SCOPES, p as currentAgyVersion, r as AGY_ENDPOINT_FALLBACKS } from "./constants-Db4tmyfb.mjs";
1
+ import { a as AGY_PLATFORM_ENUM, c as OAUTH_AUTHORIZE_URL, g as resolveAgyClientCredentials, h as getAgyBootstrapUserAgent, i as AGY_IDE_TYPE, o as AGY_SCOPES, p as currentAgyVersion, r as AGY_ENDPOINT_FALLBACKS } from "./constants-BpyRirvk.mjs";
2
2
  import { i as normalizeProxyUrl, o as proxiedFetch } from "./proxy-DfaL73mL.mjs";
3
- import { i as generatePkcePair, r as encodeState } from "./exchange-yZPIZumk.mjs";
3
+ import { i as generatePkcePair, r as encodeState } from "./exchange-LzEyxve1.mjs";
4
4
  import { n as decodeCredentialBlob } from "./blob-D1e7_uT1.mjs";
5
5
  import { randomUUID } from "node:crypto";
6
6
  //#region src/oauth/authorize.ts
@@ -229,4 +229,4 @@ async function upsertImportedAccount(store, enriched, options = {}) {
229
229
  //#endregion
230
230
  export { upsertImportedAccount as n, authorizeAntigravity as r, importManySources as t };
231
231
 
232
- //# sourceMappingURL=import-BaFULQnH.mjs.map
232
+ //# sourceMappingURL=import-CotRHNDk.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"import-BaFULQnH.mjs","names":[],"sources":["../src/oauth/authorize.ts","../src/cli/import.ts"],"sourcesContent":["/** Build the Antigravity OAuth authorization URL with PKCE and project metadata. */\n\nimport {\n AGY_SCOPES,\n OAUTH_AUTHORIZE_URL,\n resolveAgyClientCredentials,\n} from './constants.ts'\nimport { encodeState, generatePkcePair } from './pkce.ts'\n\nexport interface AntigravityAuthorization {\n url: string\n verifier: string\n projectId: string\n /** Raw state payload embedded in the URL (for server-side binding). */\n state: string\n}\n\n/**\n * Build the Google OAuth authorization URL for the Antigravity client.\n * The `state` payload carries the PKCE verifier and an optional project id so a\n * headless callback can be pasted back verbatim.\n * @param redirectUri - loopback callback URI (harness webServer route or CLI listener).\n * @param projectId - optional Antigravity project id to bind this login to.\n */\nexport async function authorizeAntigravity(\n redirectUri: string,\n projectId = '',\n): Promise<AntigravityAuthorization> {\n const { verifier, challenge } = generatePkcePair()\n\n const url = new URL(OAUTH_AUTHORIZE_URL)\n const { clientId } = resolveAgyClientCredentials()\n url.searchParams.set('client_id', clientId)\n url.searchParams.set('response_type', 'code')\n url.searchParams.set('redirect_uri', redirectUri)\n url.searchParams.set('scope', AGY_SCOPES.join(' '))\n url.searchParams.set('code_challenge', challenge)\n url.searchParams.set('code_challenge_method', 'S256')\n const state = encodeState({ verifier, projectId: projectId || '' })\n url.searchParams.set('state', state)\n url.searchParams.set('access_type', 'offline')\n url.searchParams.set('prompt', 'consent')\n\n return { url: url.toString(), verifier, projectId: projectId || '', state }\n}\n","/**\n * Import agy credentials: parse the `agy` CLI auth.json token file or a\n * paste credential blob, enrich with the Code Assist backend (email/project),\n * and persist into the account store. Adapted from OmniRoute's agyAuthImport\n * (MIT, see NOTICE.md).\n */\nimport { randomUUID } from 'node:crypto'\nimport {\n AGY_ENDPOINT_FALLBACKS,\n AGY_IDE_TYPE,\n AGY_PLATFORM_ENUM,\n currentAgyVersion,\n getAgyBootstrapUserAgent,\n} from '../oauth/constants.ts'\nimport { decodeCredentialBlob } from '../oauth/blob.ts'\nimport { normalizeProxyUrl, proxiedFetch } from '../proxy.ts'\nimport type { AccountStore } from '../store/accounts.ts'\nimport type { ManagedAccount } from '../types.ts'\n\nexport class AgyAuthFileError extends Error {\n status: number\n code: string\n\n constructor(message: string, status = 400, code = 'invalid_request') {\n super(message)\n this.name = 'AgyAuthFileError'\n this.status = status\n this.code = code\n }\n}\n\nexport interface ParsedAgyAuth {\n accessToken: string\n refreshToken: string\n tokenType: string\n expiresAt: string | null\n authMethod: string | null\n clientId?: string | null\n}\n\nexport interface EnrichedAgyAuth extends ParsedAgyAuth {\n email: string | null\n projectId: string | null\n clientId?: string | null\n}\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {}\n}\n\nfunction toNonEmptyString(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed ? trimmed : null\n}\n\n/**\n * Parse the agy CLI token file: tokens nest under `.token` with an ISO\n * `expiry` string and no id_token; a flat top-level shape is accepted too.\n */\nexport function parseAndValidateAgyToken(raw: unknown): ParsedAgyAuth {\n const doc = toRecord(raw)\n const token = toRecord(doc.token ?? doc)\n\n const accessToken = toNonEmptyString(token.access_token)\n const refreshToken = toNonEmptyString(token.refresh_token)\n if (!accessToken) throw new AgyAuthFileError('access_token is missing or empty in the agy token file', 400, 'missing_access_token')\n if (!refreshToken) throw new AgyAuthFileError('refresh_token is missing or empty in the agy token file', 400, 'missing_refresh_token')\n\n let expiresAt: string | null = null\n const isoExpiry = toNonEmptyString(token.expiry) ?? toNonEmptyString(token.expires_at)\n if (isoExpiry) {\n const ms = new Date(isoExpiry).getTime()\n expiresAt = Number.isNaN(ms) ? null : new Date(ms).toISOString()\n } else if (typeof token.expiry_date === 'number' && Number.isFinite(token.expiry_date)) {\n expiresAt = new Date(token.expiry_date).toISOString()\n }\n\n return {\n accessToken,\n refreshToken,\n tokenType: toNonEmptyString(token.token_type) ?? 'Bearer',\n expiresAt,\n authMethod: toNonEmptyString(doc.auth_method) ?? toNonEmptyString(token.auth_method),\n clientId: toNonEmptyString(token.client_id) ?? toNonEmptyString(doc.client_id) ?? toNonEmptyString(token.clientId) ?? toNonEmptyString(doc.clientId),\n }\n}\n\n/** Best-effort enrichment: email (userinfo) + projectId (loadCodeAssist), time-boxed.\n * `proxyUrl` is the account proxy being imported: enrichment talks to Google as\n * this account, so it must take the same egress route the account will use. */\nexport async function enrichWithAntigravityBackend(\n parsed: ParsedAgyAuth,\n options: { proxyUrl?: string } = {},\n): Promise<EnrichedAgyAuth> {\n let email: string | null = null\n let projectId: string | null = null\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), 8000)\n try {\n const res = await proxiedFetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', {\n headers: { Authorization: `Bearer ${parsed.accessToken}` },\n signal: controller.signal,\n }, { proxyUrl: options.proxyUrl })\n if (res.ok) {\n email = toNonEmptyString(toRecord(await res.json()).email)\n }\n } catch {\n // best effort — email stays null\n } finally {\n clearTimeout(timer)\n }\n\n const loadController = new AbortController()\n const loadTimer = setTimeout(() => loadController.abort(), 8000)\n try {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${parsed.accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': getAgyBootstrapUserAgent(),\n }\n // The identity rides in the BODY `metadata` message, matching\n // `LoadCodeAssistRequest.metadata` in the official descriptor; there is no\n // `Client-Metadata` header (neither official binary contains that name).\n const loadBody = JSON.stringify({\n metadata: {\n ideType: AGY_IDE_TYPE,\n ideVersion: currentAgyVersion(),\n platform: AGY_PLATFORM_ENUM,\n },\n })\n for (const endpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const res = await proxiedFetch(`${endpoint}/v1internal:loadCodeAssist`, {\n method: 'POST',\n headers,\n body: loadBody,\n signal: loadController.signal,\n }, { proxyUrl: options.proxyUrl })\n if (!res.ok) continue\n const data = toRecord(await res.json())\n const project = data.cloudaicompanionProject\n projectId = (typeof project === 'string' ? toNonEmptyString(project) : null)\n ?? toNonEmptyString(toRecord(project).id)\n if (projectId) break\n } catch {\n // try next endpoint\n }\n }\n } catch {\n // best effort — projectId stays null\n } finally {\n clearTimeout(loadTimer)\n }\n\n return {\n ...parsed,\n email,\n projectId,\n clientId: parsed.clientId ?? null,\n }\n}\n\n/** Parse either a raw token document or a paste blob into an enriched account. */\nexport async function parseImportSource(\n source: unknown,\n kind: 'json' | 'blob',\n options: { proxyUrl?: string } = {},\n): Promise<EnrichedAgyAuth> {\n if (kind === 'blob') {\n if (typeof source !== 'string') throw new AgyAuthFileError('blob must be a string', 400, 'invalid_blob')\n const blob = decodeCredentialBlob(source)\n return enrichWithAntigravityBackend(\n parseAndValidateAgyToken({ token: blob.tokens, auth_method: 'paste-blob' }),\n options,\n )\n }\n return enrichWithAntigravityBackend(parseAndValidateAgyToken(source), options)\n}\n\n/** Batch-import many sources (CLI multi-file / web multi-line paste). Each item\n * is independent: a failure is collected, the rest still import. */\nexport async function importManySources(\n items: Array<{ source: unknown; kind: 'json' | 'blob' }>,\n store: AccountStore,\n options: { email?: string; overwriteExisting?: boolean; proxy?: string } = {},\n): Promise<{ imported: number; replaced: number; errors: string[] }> {\n const result = { imported: 0, replaced: 0, errors: [] as string[] }\n for (const item of items) {\n try {\n // Enrich over the proxy this account is being imported with, so the\n // verification calls leave from the same egress as the account itself.\n const enriched = await parseImportSource(item.source, item.kind, {\n ...(options.proxy ? { proxyUrl: normalizeProxyUrl(options.proxy) } : {}),\n })\n const { created } = await upsertImportedAccount(store, enriched, options)\n if (created) result.imported++\n else result.replaced++\n } catch (error) {\n result.errors.push(error instanceof Error ? error.message : String(error))\n }\n }\n return result\n}\n\n/** Upsert the account into the store, deduping by email. */\nexport async function upsertImportedAccount(\n store: AccountStore,\n enriched: EnrichedAgyAuth,\n options: { email?: string; overwriteExisting?: boolean; proxy?: string } = {},\n): Promise<{ account: ManagedAccount; created: boolean }> {\n const resolvedEmail = options.email || enriched.email\n const refresh = `${enriched.refreshToken}|${enriched.projectId ?? ''}`\n // Normalize proxy if explicitly provided (empty -> clear -> undefined)\n let normalizedProxy: string | undefined\n let proxyProvided = options.proxy !== undefined\n if (proxyProvided) {\n const raw = options.proxy ?? ''\n if (!raw.trim()) normalizedProxy = undefined\n else normalizedProxy = normalizeProxyUrl(raw)\n }\n\n return store.mutate((storage) => {\n const existingIndex = resolvedEmail\n ? storage.accounts.findIndex((a) => a.email?.toLowerCase() === resolvedEmail.toLowerCase())\n : -1\n\n if (existingIndex !== -1) {\n if (!options.overwriteExisting) {\n throw new AgyAuthFileError(\n 'An agy account for this email already exists. Pass --overwrite to replace it.',\n 409,\n 'duplicate_account',\n )\n }\n const existing = storage.accounts[existingIndex]!\n const updated: ManagedAccount = {\n ...existing,\n id: existing.id || randomUUID(),\n refresh,\n email: existing.email ?? resolvedEmail ?? undefined,\n clientId: enriched.clientId ? enriched.clientId : existing.clientId,\n addedAt: existing.addedAt,\n lastUsed: Date.now(),\n enabled: true,\n verificationRequired: false,\n verificationRequiredAt: undefined,\n verificationRequiredReason: undefined,\n }\n if (proxyProvided) {\n if (normalizedProxy) updated.proxy = normalizedProxy\n else delete (updated as { proxy?: string }).proxy\n }\n storage.accounts[existingIndex] = updated\n return { account: storage.accounts[existingIndex]!, created: false }\n }\n\n const account: ManagedAccount = {\n id: randomUUID(),\n email: resolvedEmail ?? undefined,\n refresh,\n projectId: enriched.projectId ?? undefined,\n clientId: enriched.clientId ?? undefined,\n addedAt: Date.now(),\n lastUsed: Date.now(),\n enabled: true,\n ...(normalizedProxy ? { proxy: normalizedProxy } : {}),\n }\n // When proxy was explicitly cleared (empty string) for new account, just omit proxy field\n storage.accounts.push(account)\n if (storage.activeIndex >= storage.accounts.length - 1 && storage.accounts.length === 1) {\n storage.activeIndex = 0\n }\n return { account, created: true }\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"import-CotRHNDk.mjs","names":[],"sources":["../src/oauth/authorize.ts","../src/cli/import.ts"],"sourcesContent":["/** Build the Antigravity OAuth authorization URL with PKCE and project metadata. */\n\nimport {\n AGY_SCOPES,\n OAUTH_AUTHORIZE_URL,\n resolveAgyClientCredentials,\n} from './constants.ts'\nimport { encodeState, generatePkcePair } from './pkce.ts'\n\nexport interface AntigravityAuthorization {\n url: string\n verifier: string\n projectId: string\n /** Raw state payload embedded in the URL (for server-side binding). */\n state: string\n}\n\n/**\n * Build the Google OAuth authorization URL for the Antigravity client.\n * The `state` payload carries the PKCE verifier and an optional project id so a\n * headless callback can be pasted back verbatim.\n * @param redirectUri - loopback callback URI (harness webServer route or CLI listener).\n * @param projectId - optional Antigravity project id to bind this login to.\n */\nexport async function authorizeAntigravity(\n redirectUri: string,\n projectId = '',\n): Promise<AntigravityAuthorization> {\n const { verifier, challenge } = generatePkcePair()\n\n const url = new URL(OAUTH_AUTHORIZE_URL)\n const { clientId } = resolveAgyClientCredentials()\n url.searchParams.set('client_id', clientId)\n url.searchParams.set('response_type', 'code')\n url.searchParams.set('redirect_uri', redirectUri)\n url.searchParams.set('scope', AGY_SCOPES.join(' '))\n url.searchParams.set('code_challenge', challenge)\n url.searchParams.set('code_challenge_method', 'S256')\n const state = encodeState({ verifier, projectId: projectId || '' })\n url.searchParams.set('state', state)\n url.searchParams.set('access_type', 'offline')\n url.searchParams.set('prompt', 'consent')\n\n return { url: url.toString(), verifier, projectId: projectId || '', state }\n}\n","/**\n * Import agy credentials: parse the `agy` CLI auth.json token file or a\n * paste credential blob, enrich with the Code Assist backend (email/project),\n * and persist into the account store. Adapted from OmniRoute's agyAuthImport\n * (MIT, see NOTICE.md).\n */\nimport { randomUUID } from 'node:crypto'\nimport {\n AGY_ENDPOINT_FALLBACKS,\n AGY_IDE_TYPE,\n AGY_PLATFORM_ENUM,\n currentAgyVersion,\n getAgyBootstrapUserAgent,\n} from '../oauth/constants.ts'\nimport { decodeCredentialBlob } from '../oauth/blob.ts'\nimport { normalizeProxyUrl, proxiedFetch } from '../proxy.ts'\nimport type { AccountStore } from '../store/accounts.ts'\nimport type { ManagedAccount } from '../types.ts'\n\nexport class AgyAuthFileError extends Error {\n status: number\n code: string\n\n constructor(message: string, status = 400, code = 'invalid_request') {\n super(message)\n this.name = 'AgyAuthFileError'\n this.status = status\n this.code = code\n }\n}\n\nexport interface ParsedAgyAuth {\n accessToken: string\n refreshToken: string\n tokenType: string\n expiresAt: string | null\n authMethod: string | null\n clientId?: string | null\n}\n\nexport interface EnrichedAgyAuth extends ParsedAgyAuth {\n email: string | null\n projectId: string | null\n clientId?: string | null\n}\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {}\n}\n\nfunction toNonEmptyString(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed ? trimmed : null\n}\n\n/**\n * Parse the agy CLI token file: tokens nest under `.token` with an ISO\n * `expiry` string and no id_token; a flat top-level shape is accepted too.\n */\nexport function parseAndValidateAgyToken(raw: unknown): ParsedAgyAuth {\n const doc = toRecord(raw)\n const token = toRecord(doc.token ?? doc)\n\n const accessToken = toNonEmptyString(token.access_token)\n const refreshToken = toNonEmptyString(token.refresh_token)\n if (!accessToken) throw new AgyAuthFileError('access_token is missing or empty in the agy token file', 400, 'missing_access_token')\n if (!refreshToken) throw new AgyAuthFileError('refresh_token is missing or empty in the agy token file', 400, 'missing_refresh_token')\n\n let expiresAt: string | null = null\n const isoExpiry = toNonEmptyString(token.expiry) ?? toNonEmptyString(token.expires_at)\n if (isoExpiry) {\n const ms = new Date(isoExpiry).getTime()\n expiresAt = Number.isNaN(ms) ? null : new Date(ms).toISOString()\n } else if (typeof token.expiry_date === 'number' && Number.isFinite(token.expiry_date)) {\n expiresAt = new Date(token.expiry_date).toISOString()\n }\n\n return {\n accessToken,\n refreshToken,\n tokenType: toNonEmptyString(token.token_type) ?? 'Bearer',\n expiresAt,\n authMethod: toNonEmptyString(doc.auth_method) ?? toNonEmptyString(token.auth_method),\n clientId: toNonEmptyString(token.client_id) ?? toNonEmptyString(doc.client_id) ?? toNonEmptyString(token.clientId) ?? toNonEmptyString(doc.clientId),\n }\n}\n\n/** Best-effort enrichment: email (userinfo) + projectId (loadCodeAssist), time-boxed.\n * `proxyUrl` is the account proxy being imported: enrichment talks to Google as\n * this account, so it must take the same egress route the account will use. */\nexport async function enrichWithAntigravityBackend(\n parsed: ParsedAgyAuth,\n options: { proxyUrl?: string } = {},\n): Promise<EnrichedAgyAuth> {\n let email: string | null = null\n let projectId: string | null = null\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), 8000)\n try {\n const res = await proxiedFetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', {\n headers: { Authorization: `Bearer ${parsed.accessToken}` },\n signal: controller.signal,\n }, { proxyUrl: options.proxyUrl })\n if (res.ok) {\n email = toNonEmptyString(toRecord(await res.json()).email)\n }\n } catch {\n // best effort — email stays null\n } finally {\n clearTimeout(timer)\n }\n\n const loadController = new AbortController()\n const loadTimer = setTimeout(() => loadController.abort(), 8000)\n try {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${parsed.accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': getAgyBootstrapUserAgent(),\n }\n // The identity rides in the BODY `metadata` message, matching\n // `LoadCodeAssistRequest.metadata` in the official descriptor; there is no\n // `Client-Metadata` header (neither official binary contains that name).\n const loadBody = JSON.stringify({\n metadata: {\n ideType: AGY_IDE_TYPE,\n ideVersion: currentAgyVersion(),\n platform: AGY_PLATFORM_ENUM,\n },\n })\n for (const endpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const res = await proxiedFetch(`${endpoint}/v1internal:loadCodeAssist`, {\n method: 'POST',\n headers,\n body: loadBody,\n signal: loadController.signal,\n }, { proxyUrl: options.proxyUrl })\n if (!res.ok) continue\n const data = toRecord(await res.json())\n const project = data.cloudaicompanionProject\n projectId = (typeof project === 'string' ? toNonEmptyString(project) : null)\n ?? toNonEmptyString(toRecord(project).id)\n if (projectId) break\n } catch {\n // try next endpoint\n }\n }\n } catch {\n // best effort — projectId stays null\n } finally {\n clearTimeout(loadTimer)\n }\n\n return {\n ...parsed,\n email,\n projectId,\n clientId: parsed.clientId ?? null,\n }\n}\n\n/** Parse either a raw token document or a paste blob into an enriched account. */\nexport async function parseImportSource(\n source: unknown,\n kind: 'json' | 'blob',\n options: { proxyUrl?: string } = {},\n): Promise<EnrichedAgyAuth> {\n if (kind === 'blob') {\n if (typeof source !== 'string') throw new AgyAuthFileError('blob must be a string', 400, 'invalid_blob')\n const blob = decodeCredentialBlob(source)\n return enrichWithAntigravityBackend(\n parseAndValidateAgyToken({ token: blob.tokens, auth_method: 'paste-blob' }),\n options,\n )\n }\n return enrichWithAntigravityBackend(parseAndValidateAgyToken(source), options)\n}\n\n/** Batch-import many sources (CLI multi-file / web multi-line paste). Each item\n * is independent: a failure is collected, the rest still import. */\nexport async function importManySources(\n items: Array<{ source: unknown; kind: 'json' | 'blob' }>,\n store: AccountStore,\n options: { email?: string; overwriteExisting?: boolean; proxy?: string } = {},\n): Promise<{ imported: number; replaced: number; errors: string[] }> {\n const result = { imported: 0, replaced: 0, errors: [] as string[] }\n for (const item of items) {\n try {\n // Enrich over the proxy this account is being imported with, so the\n // verification calls leave from the same egress as the account itself.\n const enriched = await parseImportSource(item.source, item.kind, {\n ...(options.proxy ? { proxyUrl: normalizeProxyUrl(options.proxy) } : {}),\n })\n const { created } = await upsertImportedAccount(store, enriched, options)\n if (created) result.imported++\n else result.replaced++\n } catch (error) {\n result.errors.push(error instanceof Error ? error.message : String(error))\n }\n }\n return result\n}\n\n/** Upsert the account into the store, deduping by email. */\nexport async function upsertImportedAccount(\n store: AccountStore,\n enriched: EnrichedAgyAuth,\n options: { email?: string; overwriteExisting?: boolean; proxy?: string } = {},\n): Promise<{ account: ManagedAccount; created: boolean }> {\n const resolvedEmail = options.email || enriched.email\n const refresh = `${enriched.refreshToken}|${enriched.projectId ?? ''}`\n // Normalize proxy if explicitly provided (empty -> clear -> undefined)\n let normalizedProxy: string | undefined\n let proxyProvided = options.proxy !== undefined\n if (proxyProvided) {\n const raw = options.proxy ?? ''\n if (!raw.trim()) normalizedProxy = undefined\n else normalizedProxy = normalizeProxyUrl(raw)\n }\n\n return store.mutate((storage) => {\n const existingIndex = resolvedEmail\n ? storage.accounts.findIndex((a) => a.email?.toLowerCase() === resolvedEmail.toLowerCase())\n : -1\n\n if (existingIndex !== -1) {\n if (!options.overwriteExisting) {\n throw new AgyAuthFileError(\n 'An agy account for this email already exists. Pass --overwrite to replace it.',\n 409,\n 'duplicate_account',\n )\n }\n const existing = storage.accounts[existingIndex]!\n const updated: ManagedAccount = {\n ...existing,\n id: existing.id || randomUUID(),\n refresh,\n email: existing.email ?? resolvedEmail ?? undefined,\n clientId: enriched.clientId ? enriched.clientId : existing.clientId,\n addedAt: existing.addedAt,\n lastUsed: Date.now(),\n enabled: true,\n verificationRequired: false,\n verificationRequiredAt: undefined,\n verificationRequiredReason: undefined,\n }\n if (proxyProvided) {\n if (normalizedProxy) updated.proxy = normalizedProxy\n else delete (updated as { proxy?: string }).proxy\n }\n storage.accounts[existingIndex] = updated\n return { account: storage.accounts[existingIndex]!, created: false }\n }\n\n const account: ManagedAccount = {\n id: randomUUID(),\n email: resolvedEmail ?? undefined,\n refresh,\n projectId: enriched.projectId ?? undefined,\n clientId: enriched.clientId ?? undefined,\n addedAt: Date.now(),\n lastUsed: Date.now(),\n enabled: true,\n ...(normalizedProxy ? { proxy: normalizedProxy } : {}),\n }\n // When proxy was explicitly cleared (empty string) for new account, just omit proxy field\n storage.accounts.push(account)\n if (storage.activeIndex >= storage.accounts.length - 1 && storage.accounts.length === 1) {\n storage.activeIndex = 0\n }\n return { account, created: true }\n })\n}\n"],"mappings":"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"}
package/lib/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { i as isAgyDisabled } from "./accounts-CkaHhb5H.mjs";
2
- import { t as createAgyRuntime } from "./plugin-common-B1MG00JT.mjs";
1
+ import { i as isAgyDisabled } from "./accounts-CKWybAfc.mjs";
2
+ import { t as createAgyRuntime } from "./plugin-common-B5dyfnMH.mjs";
3
3
  //#region src/index.ts
4
4
  const name = "dsh-agy";
5
5
  const inject = ["llm"];
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { h as getAgyBootstrapUserAgent, r as AGY_ENDPOINT_FALLBACKS } from "./constants-Db4tmyfb.mjs";
2
+ import { h as getAgyBootstrapUserAgent, r as AGY_ENDPOINT_FALLBACKS } from "./constants-BpyRirvk.mjs";
3
3
  import { o as proxiedFetch } from "./proxy-DfaL73mL.mjs";
4
4
  import { i as isLevelThinkingModel, n as catalogModel, r as isChatCallableModelId, t as AGY_PUBLIC_MODELS } from "./catalog-BgdDVJEB.mjs";
5
5
  import { ReasoningEffortId } from "@deepseek-ai/dsh-llm";
@@ -217,4 +217,4 @@ function resolveAgyModel(provider, model) {
217
217
  //#endregion
218
218
  export { resolveAgyModel as i, listAgyModels as n, models_exports as r, catalogModelList as t };
219
219
 
220
- //# sourceMappingURL=models-BGuwl50d.mjs.map
220
+ //# sourceMappingURL=models-Qn0-sh6X.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"models-BGuwl50d.mjs","names":[],"sources":["../src/adapter/models.ts"],"sourcesContent":["/**\n * Model discovery: dynamic `v1internal:fetchAvailableModels` as the primary\n * source (fresh ids + per-model quotaInfo), the pinned catalog merged in for\n * capability metadata, and catalog fallback when the endpoint is unreachable.\n */\n\nimport { ReasoningEffortId, type LlmModelInfo, type LlmModelReasoningInfo, type LlmResolvedModelInfo, type ModelModality } from '@deepseek-ai/dsh-llm'\nimport { AGY_ENDPOINT_FALLBACKS, getAgyBootstrapUserAgent } from '../oauth/constants.ts'\nimport { proxiedFetch } from '../proxy.ts'\nimport { AGY_PUBLIC_MODELS, catalogModel, isChatCallableModelId, isLevelThinkingModel } from './catalog.ts'\n\nexport const AGY_PROVIDER = 'agy'\n\n/**\n * Selectable thinking levels for a tiered model. Deliberately NO `defaultEffort`.\n *\n * `defaultEffort` is not a display hint: the harness reads it as\n * `effective = requested ?? reasoning.defaultEffort`, and it also gates the\n * selector's own \"provider default\" entry\n * (`...reasoning.defaultEffort === void 0 ? [providerDefault] : []`). Setting it\n * therefore did two things at once — it forced an effort onto every request, so\n * `translate.ts` emitted `thinkingConfig` even when the user never chose a level,\n * and it REMOVED the only option that expresses \"let the model decide\".\n *\n * Leaving it unset restores that option (host label \"Default\"): choosing it\n * sends no `reasoningEffort`, `translate.ts` omits `thinkingConfig` entirely, and\n * the upstream runs its own adaptive budget — measured on this channel as\n * `thinkingBudget: -1` + `minThinkingBudget: 32` for every `*-tiered` model.\n * The three explicit levels remain selectable alongside it.\n */\nconst LEVEL_REASONING: LlmModelReasoningInfo = Object.freeze({\n efforts: Object.freeze([\n { id: ReasoningEffortId('low'), name: 'Low' },\n { id: ReasoningEffortId('medium'), name: 'Medium' },\n { id: ReasoningEffortId('high'), name: 'High' },\n ] as const),\n} as const)\n\n/**\n * Input modalities per model. Image support follows the catalog's own\n * `supportsVision` metadata for known models (gpt-oss-120b-medium is text-only\n * there); unknown dynamic ids default to vision-capable — the upstream schema\n * accepts inlineData across the board, and a wrong guess surfaces as a clear\n * upstream 400 instead of a silent drop.\n */\nconst AGY_INPUT_MODALITIES = ['text', 'image'] as const\nconst AGY_TEXT_ONLY_MODALITIES = ['text'] as const\n\nfunction inputModalitiesFor(meta: { supportsVision?: boolean } | undefined): ModelModality[] {\n return [...(meta ? meta.supportsVision === true : true) ? AGY_INPUT_MODALITIES : AGY_TEXT_ONLY_MODALITIES]\n}\n\nexport interface DiscoveredModelEntry {\n quotaInfo?: {\n remainingFraction?: number\n resetTime?: string\n }\n displayName?: string\n modelName?: string\n}\n\nexport interface DeprecatedModelInfo {\n newModelId?: string\n oldModelEnum?: string\n newModelEnum?: string\n}\n\nexport interface DiscoveredModels {\n models?: Record<string, DiscoveredModelEntry>\n /**\n * Role assignments the endpoint returns alongside `models`. Upstream uses\n * them to decide which id drives which IDE feature; only the ones that mean\n * \"this is not an agent chat model\" are consumed here, plus the three\n * positive signals below.\n */\n tabModelIds?: string[]\n imageGenerationModelIds?: string[]\n audioTranscriptionModelIds?: string[]\n /** Keyed by the RETIRED id; an object, not an array. */\n deprecatedModelIds?: Record<string, DeprecatedModelInfo>\n defaultAgentModelId?: string\n agentModelSorts?: { displayName?: string; groups?: { modelIds?: string[] }[] }[]\n /** Selectable-thinking families, e.g. `{flash: ['gemini-3.8-flash-tiered']}`. */\n tieredModelIds?: Record<string, string[]>\n}\n\n/** Role keys whose members are not agent chat models. */\nconst NON_CHAT_ROLE_KEYS = ['tabModelIds', 'imageGenerationModelIds', 'audioTranscriptionModelIds'] as const\n\n/** The payload arrives via an unvalidated cast, so every shape is re-checked. */\nfunction stringsFrom(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value.filter((item): item is string => typeof item === 'string' && item.length > 0)\n}\n\n/**\n * Ids the payload itself advertises: the default agent model, anything in an\n * `agentModelSorts` group, and every tiered family member. These are never\n * hidden. If upstream both recommends an id and files it under a non-chat\n * role, the recommendation is the more recent statement of intent, and this\n * keeps a labelling mistake from removing a working model.\n */\nfunction advertisedModelIds(dynamic: DiscoveredModels): Set<string> {\n const advertised = new Set<string>()\n if (typeof dynamic.defaultAgentModelId === 'string' && dynamic.defaultAgentModelId.length > 0) {\n advertised.add(dynamic.defaultAgentModelId)\n }\n for (const sort of Array.isArray(dynamic.agentModelSorts) ? dynamic.agentModelSorts : []) {\n for (const group of Array.isArray(sort?.groups) ? sort.groups : []) {\n for (const id of stringsFrom(group?.modelIds)) advertised.add(id)\n }\n }\n const tiered = dynamic.tieredModelIds\n if (tiered && typeof tiered === 'object' && !Array.isArray(tiered)) {\n for (const family of Object.values(tiered)) {\n for (const id of stringsFrom(family)) advertised.add(id)\n }\n }\n return advertised\n}\n\n/**\n * Discovered ids that should not appear in a chat model list, according to the\n * payload's own role assignments.\n *\n * This is the judgement `isChatCallableModelId` already makes with the `tab_`\n * prefix, but taken from upstream's list instead of a name guess. That matters:\n * `tabModelIds` members are not required to start with `tab_` (live accounts\n * return ids such as `chat_20706`), so those ids would otherwise reach the\n * model list, where they render as a raw id with no metadata.\n *\n * Only roles that mean \"different output modality\" or \"superseded\" are\n * consumed. The utility roles (`commandModelIds`, `mqueryModelIds`,\n * `webSearchModelIds`, `commitMessageModelIds`) name ordinary chat models doing\n * a side job - on a live account all three of the latter name\n * `gemini-3.1-flash-lite`, which is a pinned catalog model.\n *\n * A deprecated id is hidden only when its `newModelId` is present in `models`,\n * chat-callable, and not role-hidden; otherwise hiding it would remove the only\n * route to that capability on an account whose tier does not carry the\n * replacement. Replacement availability is judged against role hiding alone and\n * never against other deprecations, so a chain A -> B -> C resolves the same\n * way whatever order the payload happens to serialize its keys in.\n *\n * Not the complete hiding rule: the `tab_` prefix check stays in\n * `mergeModelCatalog`, because the catalog-only fallback has no payload.\n */\nfunction hiddenDiscoveredIds(dynamic: DiscoveredModels): Set<string> {\n const models = dynamic.models ?? {}\n const advertised = advertisedModelIds(dynamic)\n const hidden = new Set<string>()\n for (const key of NON_CHAT_ROLE_KEYS) {\n for (const id of stringsFrom(dynamic[key])) {\n if (!advertised.has(id)) hidden.add(id)\n }\n }\n const roleHidden = new Set(hidden)\n const deprecated = dynamic.deprecatedModelIds\n if (deprecated && typeof deprecated === 'object' && !Array.isArray(deprecated)) {\n for (const [retiredId, info] of Object.entries(deprecated)) {\n if (advertised.has(retiredId)) continue\n const replacement = info?.newModelId\n if (typeof replacement !== 'string' || replacement.length === 0 || replacement === retiredId) continue\n if (!Object.hasOwn(models, replacement)) continue\n if (!isChatCallableModelId(replacement) || roleHidden.has(replacement)) continue\n hidden.add(retiredId)\n }\n }\n return hidden\n}\n\n/** Fetch the account's available models from the first reachable endpoint. */\nexport async function fetchAvailableModels(\n accessToken: string,\n projectId?: string,\n fetchImpl: typeof fetch = proxiedFetch,\n): Promise<DiscoveredModels> {\n let lastError: unknown = null\n const body = projectId ? { project: projectId } : {}\n for (const baseEndpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const response = await fetchImpl(`${baseEndpoint}/v1internal:fetchAvailableModels`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': getAgyBootstrapUserAgent(),\n },\n body: JSON.stringify(body),\n })\n if (response.ok) {\n return (await response.json()) as DiscoveredModels\n }\n lastError = new Error(`fetchAvailableModels ${response.status} at ${baseEndpoint}`)\n } catch (error) {\n lastError = error\n }\n }\n throw lastError instanceof Error ? lastError : new Error('fetchAvailableModels: all endpoints failed')\n}\n\n/**\n * The discovered ids a chat session may actually use: upstream's `models` minus\n * the `tab_`/role/deprecated set (see `hiddenDiscoveredIds`).\n *\n * Exported so surfaces other than the selector (the quota panel) present the\n * same list. `mergeModelCatalog` consumes it too, so the two cannot drift.\n */\nexport function chatCallableDiscoveredIds(dynamic: DiscoveredModels): string[] {\n const hidden = hiddenDiscoveredIds(dynamic)\n return Object.keys(dynamic.models ?? {}).filter((id) => isChatCallableModelId(id) && !hidden.has(id))\n}\n\n/** Merge dynamic ids with catalog metadata; non-chat and superseded ids are dropped, unknown ids keep minimal info. */\nexport function mergeModelCatalog(dynamic: DiscoveredModels): LlmModelInfo[] {\n const entries: LlmModelInfo[] = []\n const hidden = hiddenDiscoveredIds(dynamic)\n for (const [id, entry] of Object.entries(dynamic.models ?? {})) {\n if (!isChatCallableModelId(id) || hidden.has(id)) continue\n const meta = catalogModel(id)\n const rawDisplayName = entry.displayName && entry.displayName !== id ? entry.displayName : undefined\n const displayName = rawDisplayName ?? meta?.name ?? entry.displayName ?? entry.modelName ?? id\n entries.push({\n provider: AGY_PROVIDER,\n id,\n name: displayName,\n inputModalities: inputModalitiesFor(meta),\n ...(meta ? { context: { contextWindow: meta.contextLength } } : {}),\n })\n }\n return entries\n}\n\n/** Catalog-only model list used when the endpoint is unreachable. */\nexport function catalogModelList(): LlmModelInfo[] {\n return AGY_PUBLIC_MODELS.map((model) => ({\n provider: AGY_PROVIDER,\n id: model.id,\n name: model.name,\n inputModalities: inputModalitiesFor(model),\n context: { contextWindow: model.contextLength },\n }))\n}\n\n/** Adapter-facing listing: dynamic first, catalog fallback. */\nexport async function listAgyModels(\n accessToken: string | undefined,\n projectId: string | undefined,\n fetchImpl: typeof fetch = proxiedFetch,\n): Promise<readonly LlmModelInfo[]> {\n if (!accessToken) return catalogModelList()\n try {\n const dynamic = await fetchAvailableModels(accessToken, projectId, fetchImpl)\n const merged = mergeModelCatalog(dynamic)\n return merged.length > 0 ? merged : catalogModelList()\n } catch {\n return catalogModelList()\n }\n}\n\n/** Resolve one exact model's metadata (catalog-backed; dynamic ids pass through). */\nexport function resolveAgyModel(provider: string, model: string): LlmResolvedModelInfo {\n const meta = catalogModel(model)\n if (isLevelThinkingModel(model)) {\n return {\n provider,\n id: model,\n name: meta?.name ?? model,\n inputModalities: inputModalitiesFor(meta),\n context: { contextWindow: meta?.contextLength ?? 1048576 },\n defaultMaxTokens: meta?.maxOutputTokens ?? 65536,\n // Return a shallow copy so callers cannot mutate the frozen singleton.\n reasoning: { ...LEVEL_REASONING, efforts: [...LEVEL_REASONING.efforts] },\n }\n }\n return {\n provider,\n id: model,\n name: meta?.name ?? model,\n inputModalities: inputModalitiesFor(meta),\n ...(meta ? { context: { contextWindow: meta.contextLength }, defaultMaxTokens: meta.maxOutputTokens } : {}),\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"models-Qn0-sh6X.mjs","names":[],"sources":["../src/adapter/models.ts"],"sourcesContent":["/**\n * Model discovery: dynamic `v1internal:fetchAvailableModels` as the primary\n * source (fresh ids + per-model quotaInfo), the pinned catalog merged in for\n * capability metadata, and catalog fallback when the endpoint is unreachable.\n */\n\nimport { ReasoningEffortId, type LlmModelInfo, type LlmModelReasoningInfo, type LlmResolvedModelInfo, type ModelModality } from '@deepseek-ai/dsh-llm'\nimport { AGY_ENDPOINT_FALLBACKS, getAgyBootstrapUserAgent } from '../oauth/constants.ts'\nimport { proxiedFetch } from '../proxy.ts'\nimport { AGY_PUBLIC_MODELS, catalogModel, isChatCallableModelId, isLevelThinkingModel } from './catalog.ts'\n\nexport const AGY_PROVIDER = 'agy'\n\n/**\n * Selectable thinking levels for a tiered model. Deliberately NO `defaultEffort`.\n *\n * `defaultEffort` is not a display hint: the harness reads it as\n * `effective = requested ?? reasoning.defaultEffort`, and it also gates the\n * selector's own \"provider default\" entry\n * (`...reasoning.defaultEffort === void 0 ? [providerDefault] : []`). Setting it\n * therefore did two things at once — it forced an effort onto every request, so\n * `translate.ts` emitted `thinkingConfig` even when the user never chose a level,\n * and it REMOVED the only option that expresses \"let the model decide\".\n *\n * Leaving it unset restores that option (host label \"Default\"): choosing it\n * sends no `reasoningEffort`, `translate.ts` omits `thinkingConfig` entirely, and\n * the upstream runs its own adaptive budget — measured on this channel as\n * `thinkingBudget: -1` + `minThinkingBudget: 32` for every `*-tiered` model.\n * The three explicit levels remain selectable alongside it.\n */\nconst LEVEL_REASONING: LlmModelReasoningInfo = Object.freeze({\n efforts: Object.freeze([\n { id: ReasoningEffortId('low'), name: 'Low' },\n { id: ReasoningEffortId('medium'), name: 'Medium' },\n { id: ReasoningEffortId('high'), name: 'High' },\n ] as const),\n} as const)\n\n/**\n * Input modalities per model. Image support follows the catalog's own\n * `supportsVision` metadata for known models (gpt-oss-120b-medium is text-only\n * there); unknown dynamic ids default to vision-capable — the upstream schema\n * accepts inlineData across the board, and a wrong guess surfaces as a clear\n * upstream 400 instead of a silent drop.\n */\nconst AGY_INPUT_MODALITIES = ['text', 'image'] as const\nconst AGY_TEXT_ONLY_MODALITIES = ['text'] as const\n\nfunction inputModalitiesFor(meta: { supportsVision?: boolean } | undefined): ModelModality[] {\n return [...(meta ? meta.supportsVision === true : true) ? AGY_INPUT_MODALITIES : AGY_TEXT_ONLY_MODALITIES]\n}\n\nexport interface DiscoveredModelEntry {\n quotaInfo?: {\n remainingFraction?: number\n resetTime?: string\n }\n displayName?: string\n modelName?: string\n}\n\nexport interface DeprecatedModelInfo {\n newModelId?: string\n oldModelEnum?: string\n newModelEnum?: string\n}\n\nexport interface DiscoveredModels {\n models?: Record<string, DiscoveredModelEntry>\n /**\n * Role assignments the endpoint returns alongside `models`. Upstream uses\n * them to decide which id drives which IDE feature; only the ones that mean\n * \"this is not an agent chat model\" are consumed here, plus the three\n * positive signals below.\n */\n tabModelIds?: string[]\n imageGenerationModelIds?: string[]\n audioTranscriptionModelIds?: string[]\n /** Keyed by the RETIRED id; an object, not an array. */\n deprecatedModelIds?: Record<string, DeprecatedModelInfo>\n defaultAgentModelId?: string\n agentModelSorts?: { displayName?: string; groups?: { modelIds?: string[] }[] }[]\n /** Selectable-thinking families, e.g. `{flash: ['gemini-3.8-flash-tiered']}`. */\n tieredModelIds?: Record<string, string[]>\n}\n\n/** Role keys whose members are not agent chat models. */\nconst NON_CHAT_ROLE_KEYS = ['tabModelIds', 'imageGenerationModelIds', 'audioTranscriptionModelIds'] as const\n\n/** The payload arrives via an unvalidated cast, so every shape is re-checked. */\nfunction stringsFrom(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value.filter((item): item is string => typeof item === 'string' && item.length > 0)\n}\n\n/**\n * Ids the payload itself advertises: the default agent model, anything in an\n * `agentModelSorts` group, and every tiered family member. These are never\n * hidden. If upstream both recommends an id and files it under a non-chat\n * role, the recommendation is the more recent statement of intent, and this\n * keeps a labelling mistake from removing a working model.\n */\nfunction advertisedModelIds(dynamic: DiscoveredModels): Set<string> {\n const advertised = new Set<string>()\n if (typeof dynamic.defaultAgentModelId === 'string' && dynamic.defaultAgentModelId.length > 0) {\n advertised.add(dynamic.defaultAgentModelId)\n }\n for (const sort of Array.isArray(dynamic.agentModelSorts) ? dynamic.agentModelSorts : []) {\n for (const group of Array.isArray(sort?.groups) ? sort.groups : []) {\n for (const id of stringsFrom(group?.modelIds)) advertised.add(id)\n }\n }\n const tiered = dynamic.tieredModelIds\n if (tiered && typeof tiered === 'object' && !Array.isArray(tiered)) {\n for (const family of Object.values(tiered)) {\n for (const id of stringsFrom(family)) advertised.add(id)\n }\n }\n return advertised\n}\n\n/**\n * Discovered ids that should not appear in a chat model list, according to the\n * payload's own role assignments.\n *\n * This is the judgement `isChatCallableModelId` already makes with the `tab_`\n * prefix, but taken from upstream's list instead of a name guess. That matters:\n * `tabModelIds` members are not required to start with `tab_` (live accounts\n * return ids such as `chat_20706`), so those ids would otherwise reach the\n * model list, where they render as a raw id with no metadata.\n *\n * Only roles that mean \"different output modality\" or \"superseded\" are\n * consumed. The utility roles (`commandModelIds`, `mqueryModelIds`,\n * `webSearchModelIds`, `commitMessageModelIds`) name ordinary chat models doing\n * a side job - on a live account all three of the latter name\n * `gemini-3.1-flash-lite`, which is a pinned catalog model.\n *\n * A deprecated id is hidden only when its `newModelId` is present in `models`,\n * chat-callable, and not role-hidden; otherwise hiding it would remove the only\n * route to that capability on an account whose tier does not carry the\n * replacement. Replacement availability is judged against role hiding alone and\n * never against other deprecations, so a chain A -> B -> C resolves the same\n * way whatever order the payload happens to serialize its keys in.\n *\n * Not the complete hiding rule: the `tab_` prefix check stays in\n * `mergeModelCatalog`, because the catalog-only fallback has no payload.\n */\nfunction hiddenDiscoveredIds(dynamic: DiscoveredModels): Set<string> {\n const models = dynamic.models ?? {}\n const advertised = advertisedModelIds(dynamic)\n const hidden = new Set<string>()\n for (const key of NON_CHAT_ROLE_KEYS) {\n for (const id of stringsFrom(dynamic[key])) {\n if (!advertised.has(id)) hidden.add(id)\n }\n }\n const roleHidden = new Set(hidden)\n const deprecated = dynamic.deprecatedModelIds\n if (deprecated && typeof deprecated === 'object' && !Array.isArray(deprecated)) {\n for (const [retiredId, info] of Object.entries(deprecated)) {\n if (advertised.has(retiredId)) continue\n const replacement = info?.newModelId\n if (typeof replacement !== 'string' || replacement.length === 0 || replacement === retiredId) continue\n if (!Object.hasOwn(models, replacement)) continue\n if (!isChatCallableModelId(replacement) || roleHidden.has(replacement)) continue\n hidden.add(retiredId)\n }\n }\n return hidden\n}\n\n/** Fetch the account's available models from the first reachable endpoint. */\nexport async function fetchAvailableModels(\n accessToken: string,\n projectId?: string,\n fetchImpl: typeof fetch = proxiedFetch,\n): Promise<DiscoveredModels> {\n let lastError: unknown = null\n const body = projectId ? { project: projectId } : {}\n for (const baseEndpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const response = await fetchImpl(`${baseEndpoint}/v1internal:fetchAvailableModels`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': getAgyBootstrapUserAgent(),\n },\n body: JSON.stringify(body),\n })\n if (response.ok) {\n return (await response.json()) as DiscoveredModels\n }\n lastError = new Error(`fetchAvailableModels ${response.status} at ${baseEndpoint}`)\n } catch (error) {\n lastError = error\n }\n }\n throw lastError instanceof Error ? lastError : new Error('fetchAvailableModels: all endpoints failed')\n}\n\n/**\n * The discovered ids a chat session may actually use: upstream's `models` minus\n * the `tab_`/role/deprecated set (see `hiddenDiscoveredIds`).\n *\n * Exported so surfaces other than the selector (the quota panel) present the\n * same list. `mergeModelCatalog` consumes it too, so the two cannot drift.\n */\nexport function chatCallableDiscoveredIds(dynamic: DiscoveredModels): string[] {\n const hidden = hiddenDiscoveredIds(dynamic)\n return Object.keys(dynamic.models ?? {}).filter((id) => isChatCallableModelId(id) && !hidden.has(id))\n}\n\n/** Merge dynamic ids with catalog metadata; non-chat and superseded ids are dropped, unknown ids keep minimal info. */\nexport function mergeModelCatalog(dynamic: DiscoveredModels): LlmModelInfo[] {\n const entries: LlmModelInfo[] = []\n const hidden = hiddenDiscoveredIds(dynamic)\n for (const [id, entry] of Object.entries(dynamic.models ?? {})) {\n if (!isChatCallableModelId(id) || hidden.has(id)) continue\n const meta = catalogModel(id)\n const rawDisplayName = entry.displayName && entry.displayName !== id ? entry.displayName : undefined\n const displayName = rawDisplayName ?? meta?.name ?? entry.displayName ?? entry.modelName ?? id\n entries.push({\n provider: AGY_PROVIDER,\n id,\n name: displayName,\n inputModalities: inputModalitiesFor(meta),\n ...(meta ? { context: { contextWindow: meta.contextLength } } : {}),\n })\n }\n return entries\n}\n\n/** Catalog-only model list used when the endpoint is unreachable. */\nexport function catalogModelList(): LlmModelInfo[] {\n return AGY_PUBLIC_MODELS.map((model) => ({\n provider: AGY_PROVIDER,\n id: model.id,\n name: model.name,\n inputModalities: inputModalitiesFor(model),\n context: { contextWindow: model.contextLength },\n }))\n}\n\n/** Adapter-facing listing: dynamic first, catalog fallback. */\nexport async function listAgyModels(\n accessToken: string | undefined,\n projectId: string | undefined,\n fetchImpl: typeof fetch = proxiedFetch,\n): Promise<readonly LlmModelInfo[]> {\n if (!accessToken) return catalogModelList()\n try {\n const dynamic = await fetchAvailableModels(accessToken, projectId, fetchImpl)\n const merged = mergeModelCatalog(dynamic)\n return merged.length > 0 ? merged : catalogModelList()\n } catch {\n return catalogModelList()\n }\n}\n\n/** Resolve one exact model's metadata (catalog-backed; dynamic ids pass through). */\nexport function resolveAgyModel(provider: string, model: string): LlmResolvedModelInfo {\n const meta = catalogModel(model)\n if (isLevelThinkingModel(model)) {\n return {\n provider,\n id: model,\n name: meta?.name ?? model,\n inputModalities: inputModalitiesFor(meta),\n context: { contextWindow: meta?.contextLength ?? 1048576 },\n defaultMaxTokens: meta?.maxOutputTokens ?? 65536,\n // Return a shallow copy so callers cannot mutate the frozen singleton.\n reasoning: { ...LEVEL_REASONING, efforts: [...LEVEL_REASONING.efforts] },\n }\n }\n return {\n provider,\n id: model,\n name: meta?.name ?? model,\n inputModalities: inputModalitiesFor(meta),\n ...(meta ? { context: { contextWindow: meta.contextLength }, defaultMaxTokens: meta.maxOutputTokens } : {}),\n }\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import { m as fetchAgyFirstOk, v as AgyAuthError, y as AgyPoolBlockedError } from "./constants-Db4tmyfb.mjs";
1
+ import { m as fetchAgyFirstOk, v as AgyAuthError, y as AgyPoolBlockedError } from "./constants-BpyRirvk.mjs";
2
2
  import { a as probeFetch, o as proxiedFetch, t as accountFetch } from "./proxy-DfaL73mL.mjs";
3
- import { _ as resolveMasterKeyCodec, b as describeFetchError, d as MASTER_KEY_REF, f as createAesGcmCodec, g as resolveDshHome, h as persistMasterKey, m as loadMasterKey, p as deriveKey, r as AgySessionManager, s as resolveAntigravityVersion, t as JsonAccountStore, u as pickProbeProxyUrl, v as classifyFetchError, x as isSessionAccumulationOverflow, y as classifyHttpError } from "./accounts-CkaHhb5H.mjs";
3
+ import { _ as resolveMasterKeyCodec, b as describeFetchError, d as MASTER_KEY_REF, f as createAesGcmCodec, g as resolveDshHome, h as persistMasterKey, m as loadMasterKey, p as deriveKey, r as AgySessionManager, s as resolveAntigravityVersion, t as JsonAccountStore, u as pickProbeProxyUrl, v as classifyFetchError, x as isSessionAccumulationOverflow, y as classifyHttpError } from "./accounts-CKWybAfc.mjs";
4
4
  import { i as generateAntigravityRequestId, n as currentSessionGeneration, r as deriveAntigravitySessionId, t as bumpSessionGeneration } from "./identity-Ci8EGc1F.mjs";
5
5
  import { i as setThoughtSignature, r as resolveMultimodalFiles, t as toAgyRequestBody } from "./translate-BQu8HLvB.mjs";
6
6
  import { t as parseAgySse } from "./parse-Bs4rPkHA.mjs";
7
- import { i as resolveAgyModel, n as listAgyModels, t as catalogModelList } from "./models-BGuwl50d.mjs";
7
+ import { i as resolveAgyModel, n as listAgyModels, t as catalogModelList } from "./models-Qn0-sh6X.mjs";
8
8
  import { randomBytes } from "node:crypto";
9
9
  import { LlmAdapter, LlmError, ProviderRequestId, QUOTA_EXCEEDED_CODE } from "@deepseek-ai/dsh-llm";
10
10
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
@@ -1310,4 +1310,4 @@ async function createAgyRuntime(ctx) {
1310
1310
  //#endregion
1311
1311
  export { THINKING_BUDGET_MAX as a, CLAUDE_BUDGET_MIN as i, foldWindowBreakdown as n, CLAUDE_BUDGET_MAX as r, createAgyRuntime as t };
1312
1312
 
1313
- //# sourceMappingURL=plugin-common-B1MG00JT.mjs.map
1313
+ //# sourceMappingURL=plugin-common-B5dyfnMH.mjs.map