dsh-agy 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cli/index.mjs +4 -4
- package/lib/{exchange-BNNR9Cba.mjs → exchange-E1EN8XaA.mjs} +2 -2
- package/lib/{exchange-BNNR9Cba.mjs.map → exchange-E1EN8XaA.mjs.map} +1 -1
- package/lib/{import-D7455oFm.mjs → import-d_LaYzU0.mjs} +3 -3
- package/lib/{import-D7455oFm.mjs.map → import-d_LaYzU0.mjs.map} +1 -1
- package/lib/index.mjs +1 -1
- package/lib/{models-CfP-YGGN.mjs → models-ChWGlj9L.mjs} +2 -2
- package/lib/{models-CfP-YGGN.mjs.map → models-ChWGlj9L.mjs.map} +1 -1
- package/lib/{plugin-common-DPgWbKSr.mjs → plugin-common-izNX7bRt.mjs} +17 -4
- package/lib/plugin-common-izNX7bRt.mjs.map +1 -0
- package/lib/{session-Drzy1MBH.mjs → session-Bo6-av5Z.mjs} +31 -23
- package/lib/session-Bo6-av5Z.mjs.map +1 -0
- package/lib/{translate-CLzL4gLS.mjs → translate-ChUQAXjN.mjs} +57 -8
- package/lib/translate-ChUQAXjN.mjs.map +1 -0
- package/lib/web/plugin.mjs +6 -6
- package/package.json +1 -1
- package/lib/plugin-common-DPgWbKSr.mjs.map +0 -1
- package/lib/session-Drzy1MBH.mjs.map +0 -1
- package/lib/translate-CLzL4gLS.mjs.map +0 -1
package/lib/cli/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as loadMasterKey, d as resolveMasterKeyCodec, i as JsonAccountStore, o as createAesGcmCodec, s as deriveKey, t as AgySessionManager, u as resolveDshHome, y as AGY_DEFAULT_REDIRECT_URI } from "../session-
|
|
2
|
-
import { t as exchangeAntigravity } from "../exchange-
|
|
3
|
-
import { n as upsertImportedAccount, r as authorizeAntigravity, t as importManySources } from "../import-
|
|
1
|
+
import { c as loadMasterKey, d as resolveMasterKeyCodec, i as JsonAccountStore, o as createAesGcmCodec, s as deriveKey, t as AgySessionManager, u as resolveDshHome, y as AGY_DEFAULT_REDIRECT_URI } from "../session-Bo6-av5Z.mjs";
|
|
2
|
+
import { t as exchangeAntigravity } from "../exchange-E1EN8XaA.mjs";
|
|
3
|
+
import { n as upsertImportedAccount, r as authorizeAntigravity, t as importManySources } from "../import-d_LaYzU0.mjs";
|
|
4
4
|
import { r as encodeCredentialBlob } from "../blob-D1e7_uT1.mjs";
|
|
5
5
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
@@ -230,7 +230,7 @@ async function statusCommand() {
|
|
|
230
230
|
console.log(` ${marker} [${index}] ${account.email ?? "(no email)"} — ${state}${account.projectId ? ` (project: ${account.projectId})` : ""}`);
|
|
231
231
|
const session = await sessions.getSession().catch(() => void 0);
|
|
232
232
|
if (session && session.index === index) try {
|
|
233
|
-
const { fetchAvailableModels } = await import("../models-
|
|
233
|
+
const { fetchAvailableModels } = await import("../models-ChWGlj9L.mjs").then((n) => n.n);
|
|
234
234
|
const discovered = await fetchAvailableModels(session.auth.access, session.account.projectId);
|
|
235
235
|
const entries = Object.entries(discovered.models ?? {});
|
|
236
236
|
if (entries.length > 0) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { C as OAUTH_TOKEN_URL, D as getAgyBootstrapUserAgent, E as getAgyBootstrapClientMetadata, _ as AGY_CLIENT_ID, b as AGY_ENDPOINT_FALLBACKS, f as calculateTokenExpiry, v as AGY_CLIENT_SECRET, w as OAUTH_USERINFO_URL } from "./session-
|
|
2
|
+
import { C as OAUTH_TOKEN_URL, D as getAgyBootstrapUserAgent, E as getAgyBootstrapClientMetadata, _ as AGY_CLIENT_ID, b as AGY_ENDPOINT_FALLBACKS, f as calculateTokenExpiry, v as AGY_CLIENT_SECRET, w as OAUTH_USERINFO_URL } from "./session-Bo6-av5Z.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
//#region src/oauth/pkce.ts
|
|
5
5
|
/** Minimal PKCE (RFC 7636) with S256, hand-rolled on node:crypto (no extra dependency). */
|
|
@@ -237,4 +237,4 @@ async function exchangeAntigravity(code, state, redirectUri, expectedVerifier) {
|
|
|
237
237
|
//#endregion
|
|
238
238
|
export { generatePkcePair as i, exchange_exports as n, encodeState as r, exchangeAntigravity as t };
|
|
239
239
|
|
|
240
|
-
//# sourceMappingURL=exchange-
|
|
240
|
+
//# sourceMappingURL=exchange-E1EN8XaA.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exchange-BNNR9Cba.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 { TokenExchangeFailure, TokenExchangeResult } from '../types.ts'\nimport { calculateTokenExpiry } from './auth.ts'\nimport {\n AGY_CLIENT_ID,\n AGY_CLIENT_SECRET,\n AGY_ENDPOINT_FALLBACKS,\n OAUTH_TOKEN_URL,\n OAUTH_USERINFO_URL,\n getAgyBootstrapClientMetadata,\n getAgyBootstrapUserAgent,\n} from './constants.ts'\nimport { decodeState } from './pkce.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(url: string, options: RequestInit, timeoutMs = FETCH_TIMEOUT_MS): Promise<Response> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { ...options, signal: controller.signal })\n } finally {\n clearTimeout(timeout)\n }\n}\n\ninterface LoadCodeAssistData {\n cloudaicompanionProject?: unknown\n subscriptionInfo?: unknown\n}\n\n/** Build the metadata payload shared by loadCodeAssist and onboardUser.\n * Only `ideType` is sent: `platform`/`pluginType` values are rejected by the\n * backend's enum validation (verified live: INVALID_ARGUMENT on \"MACOS\"), and\n * the official clients send ideType alone (OmniRoute capture). */\nfunction bootstrapMetadata(): Record<string, string> {\n return { ideType: 'ANTIGRAVITY' }\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(accessToken: string): 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 'Client-Metadata': getAgyBootstrapClientMetadata(),\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 })\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 } = {},\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 'Client-Metadata': getAgyBootstrapClientMetadata(),\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 })\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)\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 } = {},\n): Promise<{ projectId: string; tierId: string }> {\n const discovered = await loadCodeAssist(accessToken)\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): Promise<string> {\n return (await loadCodeAssist(accessToken)).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): 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 tokenResponse = await fetch(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: AGY_CLIENT_ID,\n client_secret: AGY_CLIENT_SECRET,\n code,\n grant_type: 'authorization_code',\n redirect_uri: redirectUri,\n code_verifier: verifier,\n }),\n })\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 fetch(`${OAUTH_USERINFO_URL}?alt=json`, {\n headers: {\n Authorization: `Bearer ${tokenPayload.access_token}`,\n 'User-Agent': getAgyBootstrapUserAgent(),\n },\n })\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)).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 }\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-E1EN8XaA.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 { TokenExchangeFailure, TokenExchangeResult } from '../types.ts'\nimport { calculateTokenExpiry } from './auth.ts'\nimport {\n AGY_CLIENT_ID,\n AGY_CLIENT_SECRET,\n AGY_ENDPOINT_FALLBACKS,\n OAUTH_TOKEN_URL,\n OAUTH_USERINFO_URL,\n getAgyBootstrapClientMetadata,\n getAgyBootstrapUserAgent,\n} from './constants.ts'\nimport { decodeState } from './pkce.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(url: string, options: RequestInit, timeoutMs = FETCH_TIMEOUT_MS): Promise<Response> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { ...options, signal: controller.signal })\n } finally {\n clearTimeout(timeout)\n }\n}\n\ninterface LoadCodeAssistData {\n cloudaicompanionProject?: unknown\n subscriptionInfo?: unknown\n}\n\n/** Build the metadata payload shared by loadCodeAssist and onboardUser.\n * Only `ideType` is sent: `platform`/`pluginType` values are rejected by the\n * backend's enum validation (verified live: INVALID_ARGUMENT on \"MACOS\"), and\n * the official clients send ideType alone (OmniRoute capture). */\nfunction bootstrapMetadata(): Record<string, string> {\n return { ideType: 'ANTIGRAVITY' }\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(accessToken: string): 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 'Client-Metadata': getAgyBootstrapClientMetadata(),\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 })\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 } = {},\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 'Client-Metadata': getAgyBootstrapClientMetadata(),\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 })\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)\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 } = {},\n): Promise<{ projectId: string; tierId: string }> {\n const discovered = await loadCodeAssist(accessToken)\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): Promise<string> {\n return (await loadCodeAssist(accessToken)).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): 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 tokenResponse = await fetch(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: AGY_CLIENT_ID,\n client_secret: AGY_CLIENT_SECRET,\n code,\n grant_type: 'authorization_code',\n redirect_uri: redirectUri,\n code_verifier: verifier,\n }),\n })\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 fetch(`${OAUTH_USERINFO_URL}?alt=json`, {\n headers: {\n Authorization: `Bearer ${tokenPayload.access_token}`,\n 'User-Agent': getAgyBootstrapUserAgent(),\n },\n })\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)).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 }\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,5 +1,5 @@
|
|
|
1
|
-
import { D as getAgyBootstrapUserAgent, E as getAgyBootstrapClientMetadata, S as OAUTH_AUTHORIZE_URL, _ as AGY_CLIENT_ID, b as AGY_ENDPOINT_FALLBACKS, x as AGY_SCOPES } from "./session-
|
|
2
|
-
import { i as generatePkcePair, r as encodeState } from "./exchange-
|
|
1
|
+
import { D as getAgyBootstrapUserAgent, E as getAgyBootstrapClientMetadata, S as OAUTH_AUTHORIZE_URL, _ as AGY_CLIENT_ID, b as AGY_ENDPOINT_FALLBACKS, x as AGY_SCOPES } from "./session-Bo6-av5Z.mjs";
|
|
2
|
+
import { i as generatePkcePair, r as encodeState } from "./exchange-E1EN8XaA.mjs";
|
|
3
3
|
import { n as decodeCredentialBlob } from "./blob-D1e7_uT1.mjs";
|
|
4
4
|
//#region src/oauth/authorize.ts
|
|
5
5
|
/** Build the Antigravity OAuth authorization URL with PKCE and project metadata. */
|
|
@@ -201,4 +201,4 @@ async function upsertImportedAccount(store, enriched, options = {}) {
|
|
|
201
201
|
//#endregion
|
|
202
202
|
export { upsertImportedAccount as n, authorizeAntigravity as r, importManySources as t };
|
|
203
203
|
|
|
204
|
-
//# sourceMappingURL=import-
|
|
204
|
+
//# sourceMappingURL=import-d_LaYzU0.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-D7455oFm.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_CLIENT_ID,\n AGY_SCOPES,\n OAUTH_AUTHORIZE_URL,\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 url.searchParams.set('client_id', AGY_CLIENT_ID)\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 */\n\nimport { AGY_ENDPOINT_FALLBACKS, getAgyBootstrapClientMetadata, getAgyBootstrapUserAgent } from '../oauth/constants.ts'\nimport { decodeCredentialBlob } from '../oauth/blob.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}\n\nexport interface EnrichedAgyAuth extends ParsedAgyAuth {\n email: string | null\n projectId: string | null\n}\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 }\n}\n\n/** Best-effort enrichment: email (userinfo) + projectId (loadCodeAssist), time-boxed. */\nexport async function enrichWithAntigravityBackend(parsed: ParsedAgyAuth): 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 fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', {\n headers: { Authorization: `Bearer ${parsed.accessToken}` },\n signal: controller.signal,\n })\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 'Client-Metadata': getAgyBootstrapClientMetadata(),\n }\n for (const endpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const res = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ metadata: { ideType: 'ANTIGRAVITY' } }),\n signal: loadController.signal,\n })\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 { ...parsed, email, projectId }\n}\n\n/** Parse either a raw token document or a paste blob into an enriched account. */\nexport async function parseImportSource(source: unknown, kind: 'json' | 'blob'): 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 )\n }\n return enrichWithAntigravityBackend(parseAndValidateAgyToken(source))\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 } = {},\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 const enriched = await parseImportSource(item.source, item.kind)\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 } = {},\n): Promise<{ account: ManagedAccount; created: boolean }> {\n const resolvedEmail = options.email || enriched.email\n const refresh = `${enriched.refreshToken}|${enriched.projectId ?? ''}`\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 storage.accounts[existingIndex] = {\n ...existing,\n refresh,\n email: existing.email ?? resolvedEmail ?? undefined,\n addedAt: existing.addedAt,\n lastUsed: Date.now(),\n enabled: true,\n verificationRequired: false,\n verificationRequiredAt: undefined,\n verificationRequiredReason: undefined,\n }\n return { account: storage.accounts[existingIndex]!, created: false }\n }\n\n const account: ManagedAccount = {\n email: resolvedEmail ?? undefined,\n refresh,\n projectId: enriched.projectId ?? undefined,\n addedAt: Date.now(),\n lastUsed: Date.now(),\n enabled: true,\n }\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-d_LaYzU0.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_CLIENT_ID,\n AGY_SCOPES,\n OAUTH_AUTHORIZE_URL,\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 url.searchParams.set('client_id', AGY_CLIENT_ID)\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 */\n\nimport { AGY_ENDPOINT_FALLBACKS, getAgyBootstrapClientMetadata, getAgyBootstrapUserAgent } from '../oauth/constants.ts'\nimport { decodeCredentialBlob } from '../oauth/blob.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}\n\nexport interface EnrichedAgyAuth extends ParsedAgyAuth {\n email: string | null\n projectId: string | null\n}\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 }\n}\n\n/** Best-effort enrichment: email (userinfo) + projectId (loadCodeAssist), time-boxed. */\nexport async function enrichWithAntigravityBackend(parsed: ParsedAgyAuth): 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 fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', {\n headers: { Authorization: `Bearer ${parsed.accessToken}` },\n signal: controller.signal,\n })\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 'Client-Metadata': getAgyBootstrapClientMetadata(),\n }\n for (const endpoint of AGY_ENDPOINT_FALLBACKS) {\n try {\n const res = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ metadata: { ideType: 'ANTIGRAVITY' } }),\n signal: loadController.signal,\n })\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 { ...parsed, email, projectId }\n}\n\n/** Parse either a raw token document or a paste blob into an enriched account. */\nexport async function parseImportSource(source: unknown, kind: 'json' | 'blob'): 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 )\n }\n return enrichWithAntigravityBackend(parseAndValidateAgyToken(source))\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 } = {},\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 const enriched = await parseImportSource(item.source, item.kind)\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 } = {},\n): Promise<{ account: ManagedAccount; created: boolean }> {\n const resolvedEmail = options.email || enriched.email\n const refresh = `${enriched.refreshToken}|${enriched.projectId ?? ''}`\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 storage.accounts[existingIndex] = {\n ...existing,\n refresh,\n email: existing.email ?? resolvedEmail ?? undefined,\n addedAt: existing.addedAt,\n lastUsed: Date.now(),\n enabled: true,\n verificationRequired: false,\n verificationRequiredAt: undefined,\n verificationRequiredReason: undefined,\n }\n return { account: storage.accounts[existingIndex]!, created: false }\n }\n\n const account: ManagedAccount = {\n email: resolvedEmail ?? undefined,\n refresh,\n projectId: enriched.projectId ?? undefined,\n addedAt: Date.now(),\n lastUsed: Date.now(),\n enabled: true,\n }\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
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { D as getAgyBootstrapUserAgent, b as AGY_ENDPOINT_FALLBACKS } from "./session-
|
|
2
|
+
import { D as getAgyBootstrapUserAgent, b as AGY_ENDPOINT_FALLBACKS } from "./session-Bo6-av5Z.mjs";
|
|
3
3
|
//#region src/adapter/catalog.ts
|
|
4
4
|
const AGY_PUBLIC_MODELS = [
|
|
5
5
|
{
|
|
@@ -219,4 +219,4 @@ function resolveAgyModel(provider, model) {
|
|
|
219
219
|
//#endregion
|
|
220
220
|
export { models_exports as n, resolveAgyModel as r, listAgyModels as t };
|
|
221
221
|
|
|
222
|
-
//# sourceMappingURL=models-
|
|
222
|
+
//# sourceMappingURL=models-ChWGlj9L.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models-CfP-YGGN.mjs","names":[],"sources":["../src/adapter/catalog.ts","../src/adapter/models.ts"],"sourcesContent":["/**\n * Pinned agy model catalog (metadata only — dynamic discovery is primary).\n *\n * Adapted from OmniRoute's `AGY_PUBLIC_MODELS` (MIT, see NOTICE.md), which was\n * pinned from the live `v1internal:fetchAvailableModels` endpoint. The dynamic\n * endpoint supplies ids + quotaInfo; this catalog supplies the capability\n * metadata the endpoint omits (context length, output cap, reasoning/vision/\n * tool-calling). Tab-completion models are intentionally excluded.\n */\n\nexport interface CatalogModel {\n id: string\n name: string\n contextLength: number\n maxOutputTokens: number\n supportsReasoning?: boolean\n supportsVision?: boolean\n toolCalling?: boolean\n}\n\nexport const AGY_PUBLIC_MODELS: readonly CatalogModel[] = [\n { id: 'gemini-3.6-flash-high', name: 'Gemini 3.6 Flash (High)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.6-flash-medium', name: 'Gemini 3.6 Flash (Medium)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.6-flash-low', name: 'Gemini 3.6 Flash (Low)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'claude-opus-4-6-thinking', name: 'Claude Opus 4.6 (Thinking)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6 (Thinking)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-pro-agent', name: 'Gemini 3.1 Pro (High)', contextLength: 1048576, maxOutputTokens: 65535, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.1-pro-low', name: 'Gemini 3.1 Pro (Low)', contextLength: 1048576, maxOutputTokens: 65535, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3-flash-agent', name: 'Gemini 3.5 Flash (High)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.5-flash-low', name: 'Gemini 3.5 Flash (Medium)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.5-flash-extra-low', name: 'Gemini 3.5 Flash (Low)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.1-flash-lite', name: 'Gemini 3.1 Flash Lite', contextLength: 1048576, maxOutputTokens: 65535, toolCalling: true },\n { id: 'gemini-2.5-flash-thinking', name: 'Gemini 2.5 Flash Thinking', contextLength: 1048576, maxOutputTokens: 65535, supportsReasoning: true, toolCalling: true },\n { id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash', contextLength: 1048576, maxOutputTokens: 65535, toolCalling: true },\n { id: 'gemini-2.5-flash-lite', name: 'Gemini 2.5 Flash Lite', contextLength: 1048576, maxOutputTokens: 65535, toolCalling: true },\n { id: 'gpt-oss-120b-medium', name: 'GPT-OSS 120B (Medium)', contextLength: 131072, maxOutputTokens: 32768, supportsReasoning: true, toolCalling: true },\n]\n\nconst CATALOG_BY_ID = new Map(AGY_PUBLIC_MODELS.map((m) => [m.id, m]))\n\n/** Tab-completion models are discoverable but not chat-callable. */\nexport function isChatCallableModelId(modelId: string): boolean {\n return !modelId.startsWith('tab_')\n}\n\nexport function catalogModel(modelId: string): CatalogModel | undefined {\n return CATALOG_BY_ID.get(modelId)\n}\n","/**\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 type { LlmModelInfo, LlmResolvedModelInfo } from '@deepseek-ai/dsh-llm'\nimport { AGY_ENDPOINT_FALLBACKS, getAgyBootstrapUserAgent } from '../oauth/constants.ts'\nimport { AGY_PUBLIC_MODELS, catalogModel, isChatCallableModelId } from './catalog.ts'\n\nexport const AGY_PROVIDER = 'agy'\n\nexport interface DiscoveredModelEntry {\n quotaInfo?: {\n remainingFraction?: number\n resetTime?: string\n }\n displayName?: string\n modelName?: string\n}\n\nexport interface DiscoveredModels {\n models?: Record<string, DiscoveredModelEntry>\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 = fetch,\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/** Merge dynamic ids with catalog metadata; non-chat models and unknowns keep minimal info. */\nexport function mergeModelCatalog(dynamic: DiscoveredModels): LlmModelInfo[] {\n const entries: LlmModelInfo[] = []\n for (const [id, entry] of Object.entries(dynamic.models ?? {})) {\n if (!isChatCallableModelId(id)) continue\n const meta = catalogModel(id)\n entries.push({\n provider: AGY_PROVIDER,\n id,\n name: entry.displayName ?? meta?.name ?? entry.modelName ?? id,\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 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 = fetch,\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 return {\n provider,\n id: model,\n name: meta?.name ?? model,\n ...(meta ? { context: { contextWindow: meta.contextLength }, defaultMaxTokens: meta.maxOutputTokens } : {}),\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"models-ChWGlj9L.mjs","names":[],"sources":["../src/adapter/catalog.ts","../src/adapter/models.ts"],"sourcesContent":["/**\n * Pinned agy model catalog (metadata only — dynamic discovery is primary).\n *\n * Adapted from OmniRoute's `AGY_PUBLIC_MODELS` (MIT, see NOTICE.md), which was\n * pinned from the live `v1internal:fetchAvailableModels` endpoint. The dynamic\n * endpoint supplies ids + quotaInfo; this catalog supplies the capability\n * metadata the endpoint omits (context length, output cap, reasoning/vision/\n * tool-calling). Tab-completion models are intentionally excluded.\n */\n\nexport interface CatalogModel {\n id: string\n name: string\n contextLength: number\n maxOutputTokens: number\n supportsReasoning?: boolean\n supportsVision?: boolean\n toolCalling?: boolean\n}\n\nexport const AGY_PUBLIC_MODELS: readonly CatalogModel[] = [\n { id: 'gemini-3.6-flash-high', name: 'Gemini 3.6 Flash (High)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.6-flash-medium', name: 'Gemini 3.6 Flash (Medium)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.6-flash-low', name: 'Gemini 3.6 Flash (Low)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'claude-opus-4-6-thinking', name: 'Claude Opus 4.6 (Thinking)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6 (Thinking)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-pro-agent', name: 'Gemini 3.1 Pro (High)', contextLength: 1048576, maxOutputTokens: 65535, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.1-pro-low', name: 'Gemini 3.1 Pro (Low)', contextLength: 1048576, maxOutputTokens: 65535, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3-flash-agent', name: 'Gemini 3.5 Flash (High)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.5-flash-low', name: 'Gemini 3.5 Flash (Medium)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.5-flash-extra-low', name: 'Gemini 3.5 Flash (Low)', contextLength: 1048576, maxOutputTokens: 65536, supportsReasoning: true, supportsVision: true, toolCalling: true },\n { id: 'gemini-3.1-flash-lite', name: 'Gemini 3.1 Flash Lite', contextLength: 1048576, maxOutputTokens: 65535, toolCalling: true },\n { id: 'gemini-2.5-flash-thinking', name: 'Gemini 2.5 Flash Thinking', contextLength: 1048576, maxOutputTokens: 65535, supportsReasoning: true, toolCalling: true },\n { id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash', contextLength: 1048576, maxOutputTokens: 65535, toolCalling: true },\n { id: 'gemini-2.5-flash-lite', name: 'Gemini 2.5 Flash Lite', contextLength: 1048576, maxOutputTokens: 65535, toolCalling: true },\n { id: 'gpt-oss-120b-medium', name: 'GPT-OSS 120B (Medium)', contextLength: 131072, maxOutputTokens: 32768, supportsReasoning: true, toolCalling: true },\n]\n\nconst CATALOG_BY_ID = new Map(AGY_PUBLIC_MODELS.map((m) => [m.id, m]))\n\n/** Tab-completion models are discoverable but not chat-callable. */\nexport function isChatCallableModelId(modelId: string): boolean {\n return !modelId.startsWith('tab_')\n}\n\nexport function catalogModel(modelId: string): CatalogModel | undefined {\n return CATALOG_BY_ID.get(modelId)\n}\n","/**\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 type { LlmModelInfo, LlmResolvedModelInfo } from '@deepseek-ai/dsh-llm'\nimport { AGY_ENDPOINT_FALLBACKS, getAgyBootstrapUserAgent } from '../oauth/constants.ts'\nimport { AGY_PUBLIC_MODELS, catalogModel, isChatCallableModelId } from './catalog.ts'\n\nexport const AGY_PROVIDER = 'agy'\n\nexport interface DiscoveredModelEntry {\n quotaInfo?: {\n remainingFraction?: number\n resetTime?: string\n }\n displayName?: string\n modelName?: string\n}\n\nexport interface DiscoveredModels {\n models?: Record<string, DiscoveredModelEntry>\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 = fetch,\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/** Merge dynamic ids with catalog metadata; non-chat models and unknowns keep minimal info. */\nexport function mergeModelCatalog(dynamic: DiscoveredModels): LlmModelInfo[] {\n const entries: LlmModelInfo[] = []\n for (const [id, entry] of Object.entries(dynamic.models ?? {})) {\n if (!isChatCallableModelId(id)) continue\n const meta = catalogModel(id)\n entries.push({\n provider: AGY_PROVIDER,\n id,\n name: entry.displayName ?? meta?.name ?? entry.modelName ?? id,\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 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 = fetch,\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 return {\n provider,\n id: model,\n name: meta?.name ?? model,\n ...(meta ? { context: { contextWindow: meta.contextLength }, defaultMaxTokens: meta.maxOutputTokens } : {}),\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { T as fetchAgyFirstOk, a as MASTER_KEY_REF, c as loadMasterKey, d as resolveMasterKeyCodec, g as generateAntigravityRequestId, h as deriveAntigravitySessionId, i as JsonAccountStore, l as persistMasterKey, o as createAesGcmCodec, p as resolveAntigravityVersion, s as deriveKey, t as AgySessionManager, u as resolveDshHome } from "./session-
|
|
2
|
-
import { r as setThoughtSignature, t as toAgyRequestBody } from "./translate-
|
|
1
|
+
import { T as fetchAgyFirstOk, a as MASTER_KEY_REF, c as loadMasterKey, d as resolveMasterKeyCodec, g as generateAntigravityRequestId, h as deriveAntigravitySessionId, i as JsonAccountStore, l as persistMasterKey, o as createAesGcmCodec, p as resolveAntigravityVersion, s as deriveKey, t as AgySessionManager, u as resolveDshHome } from "./session-Bo6-av5Z.mjs";
|
|
2
|
+
import { r as setThoughtSignature, t as toAgyRequestBody } from "./translate-ChUQAXjN.mjs";
|
|
3
3
|
import { t as parseAgySse } from "./parse-CiEzD9d_.mjs";
|
|
4
|
-
import { r as resolveAgyModel, t as listAgyModels } from "./models-
|
|
4
|
+
import { r as resolveAgyModel, t as listAgyModels } from "./models-ChWGlj9L.mjs";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import { LlmAdapter, LlmError, ProviderRequestId, QUOTA_EXCEEDED_CODE, attributionHeaders } from "@deepseek-ai/dsh-llm";
|
|
7
7
|
//#region src/runtime/classify.ts
|
|
@@ -88,6 +88,19 @@ function classifyHttpError(status, headers, bodyText) {
|
|
|
88
88
|
retryAfterMs,
|
|
89
89
|
message: bodyText ? bodyText.slice(0, 200) : void 0
|
|
90
90
|
};
|
|
91
|
+
if (status === 400) {
|
|
92
|
+
const text = (bodyText ?? "").toLowerCase();
|
|
93
|
+
if (text.includes("context") && (text.includes("overflow") || text.includes("too long") || text.includes("exceeded")) || text.includes("model") && (text.includes("not found") || text.includes("unavailable") || text.includes("not supported"))) return {
|
|
94
|
+
kind: "transient",
|
|
95
|
+
status,
|
|
96
|
+
message: bodyText ? bodyText.slice(0, 200) : void 0
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
kind: "request-error",
|
|
100
|
+
status,
|
|
101
|
+
message: bodyText ? bodyText.slice(0, 200) : void 0
|
|
102
|
+
};
|
|
103
|
+
}
|
|
91
104
|
return {
|
|
92
105
|
kind: "transient",
|
|
93
106
|
status,
|
|
@@ -267,4 +280,4 @@ async function createAgyRuntime(ctx) {
|
|
|
267
280
|
//#endregion
|
|
268
281
|
export { createAgyRuntime as t };
|
|
269
282
|
|
|
270
|
-
//# sourceMappingURL=plugin-common-
|
|
283
|
+
//# sourceMappingURL=plugin-common-izNX7bRt.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-common-izNX7bRt.mjs","names":[],"sources":["../src/runtime/classify.ts","../src/adapter/adapter.ts","../src/plugin-common.ts"],"sourcesContent":["/**\n * Upstream failure classification: HTTP status + headers + body → FailureKind.\n * Conservative by design: only auth-failure may revoke an account; everything\n * else cools, rotates, or retries.\n */\n\nimport type { FailureKind } from '../types.ts'\n\nexport interface ClassifiedError {\n kind: FailureKind\n status?: number\n /** Server-provided retry delay (ms), when the response carried it. */\n retryAfterMs?: number\n /** Server-provided absolute reset time, when the response carried it. */\n resetTime?: string\n message?: string\n /** 429 sub-category, present when kind is rate-limit. */\n rateLimitCategory?: RateLimitCategory\n}\n\n/**\n * 429 sub-category (mirrors CLIProxyAPI/OmniRoute's classify429):\n * - soft_rate_limit: transient burst, retry immediately on the same account\n * - rate_limited: per-minute limit, short cooldown then retry (same account\n * when there is no other)\n * - quota_exhausted: daily/plan quota gone, long cooldown (24h)\n * - unknown: exponential backoff\n */\nexport type RateLimitCategory = 'soft_rate_limit' | 'rate_limited' | 'quota_exhausted' | 'unknown'\n\nconst QUOTA_EXHAUSTED_KEYWORDS = [\n 'quota_exhausted',\n 'quota exhausted',\n 'quota reached',\n 'enable overages',\n 'individual quota',\n]\n\n/** Classify a 429 body into the four upstream categories. */\nexport function classifyRateLimit(\n bodyText: string | undefined,\n retryAfterMs: number | undefined,\n): RateLimitCategory {\n const text = (bodyText ?? '').toLowerCase()\n if (QUOTA_EXHAUSTED_KEYWORDS.some((keyword) => text.includes(keyword))) {\n return 'quota_exhausted'\n }\n if (retryAfterMs !== undefined && retryAfterMs < 3000) {\n return 'soft_rate_limit'\n }\n if (retryAfterMs !== undefined) {\n return 'rate_limited'\n }\n // No explicit retry hint: \"quota\" wording → daily quota, otherwise unknown.\n return text.includes('quota') || text.includes('resource_exhausted') ? 'quota_exhausted' : 'unknown'\n}\n\nconst RATE_LIMIT_RESET_FIELDS = ['resetTime', 'reset_time', 'resetAt', 'quotaResetTime'] as const\n\nfunction extractResetTime(bodyText: string | undefined): string | undefined {\n if (!bodyText) return undefined\n try {\n const data = JSON.parse(bodyText) as Record<string, unknown>\n for (const field of RATE_LIMIT_RESET_FIELDS) {\n const value = data[field]\n if (typeof value === 'string' && value) return value\n if (typeof value === 'number' && Number.isFinite(value)) {\n // Unix ms when large, seconds when small (relative to now).\n return value > 1_000_000_000_000 ? new Date(value).toISOString() : new Date(Date.now() + value * 1000).toISOString()\n }\n }\n const quotaInfo = data.quotaInfo as Record<string, unknown> | undefined\n if (quotaInfo && typeof quotaInfo.resetTime === 'string') return quotaInfo.resetTime\n } catch {\n // not JSON — no reset info\n }\n return undefined\n}\n\nfunction parseRetryAfter(header: string | null): number | undefined {\n if (!header) return undefined\n const seconds = Number(header)\n if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000\n const date = Date.parse(header)\n if (!Number.isNaN(date)) return Math.max(0, date - Date.now())\n return undefined\n}\n\n/** Classify a completed HTTP response (non-2xx). */\nexport function classifyHttpError(\n status: number,\n headers: Headers,\n bodyText?: string,\n): ClassifiedError {\n const retryAfterMs = parseRetryAfter(headers.get('retry-after'))\n const resetTime = extractResetTime(bodyText)\n\n if (status === 429) {\n const category = classifyRateLimit(bodyText, retryAfterMs)\n return {\n kind: 'rate-limit',\n rateLimitCategory: category,\n status,\n retryAfterMs,\n resetTime,\n message: bodyText ? bodyText.slice(0, 200) : undefined,\n }\n }\n if (status === 401) {\n return { kind: 'auth-failure', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n if (status === 403) {\n // Google also reports quota walls as 403 RESOURCE_EXHAUSTED, and the\n // endpoint fallback chain ends on hosts answering 403 for \"no license\"\n // (not bad auth). Only treat a 403 as auth-failure when the body carries\n // no quota wording — a false revoke permanently disables the account.\n const category = classifyRateLimit(bodyText, undefined)\n if (category === 'quota_exhausted') {\n return {\n kind: 'rate-limit',\n rateLimitCategory: category,\n status,\n message: bodyText ? bodyText.slice(0, 200) : undefined,\n }\n }\n return { kind: 'auth-failure', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n if (status === 404) {\n // Model/route missing — nothing to rotate for; surface as transient request error.\n return { kind: 'transient', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n if (status >= 500) {\n return { kind: 'transient', status, retryAfterMs, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n if (status === 400) {\n // A 400 is a request-construction error (permanent — retrying sends the\n // same broken payload). Only capacity-style phrases are recoverable\n // (context overflow / model unavailable), mirroring OmniRoute's\n // classifyProviderError: everything else surfaces as a terminal\n // request-error so rotation does not waste attempts.\n const text = (bodyText ?? '').toLowerCase()\n const recoverable = (\n (text.includes('context') && (text.includes('overflow') || text.includes('too long') || text.includes('exceeded')))\n || (text.includes('model') && (text.includes('not found') || text.includes('unavailable') || text.includes('not supported')))\n )\n if (recoverable) {\n return { kind: 'transient', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n return { kind: 'request-error', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n // 400-range others (401 handled above): request-level errors, no rotation semantics.\n return { kind: 'transient', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n}\n\n/** Classify a fetch-level failure (DNS, refused, timeout, abort). */\nexport function classifyFetchError(error: unknown): ClassifiedError {\n const message = error instanceof Error ? error.message : String(error)\n if (error instanceof DOMException && error.name === 'AbortError') {\n return { kind: 'network-error', message }\n }\n return { kind: 'network-error', message }\n}\n\n/** Classify the result of a refresh attempt (shared by adapter + verify paths). */\nexport function classifyRefreshFailure(status: number, code?: string): ClassifiedError {\n if (code === 'invalid_grant') return { kind: 'auth-failure', status, message: code }\n if (status === 429) return { kind: 'rate-limit', status, message: code }\n if (status >= 500) return { kind: 'transient', status, message: code }\n return { kind: 'transient', status, message: code }\n}\n\nexport type { FailureKind }\n","/**\n * AgyAdapter: the DSH seam. A thin orchestrator over the deep modules —\n * account session resolution (shell-provided), request translation, SSE\n * parsing, failure classification, and rotation reporting. All wire details\n * live in translate.ts / parse.ts / models.ts.\n */\n\nimport {\n LlmAdapter,\n LlmError,\n ProviderRequestId,\n QUOTA_EXCEEDED_CODE,\n attributionHeaders,\n} from '@deepseek-ai/dsh-llm'\nimport type {\n GenerateOptions,\n LlmModelInfo,\n LlmProviderInfo,\n LlmResolvedModelInfo,\n StreamChunk,\n ToolSchema,\n} from '@deepseek-ai/dsh-llm'\nimport type { FailureKind, ManagedAccount, OAuthAuthDetails } from '../types.ts'\nimport type { RateLimitCategory } from '../runtime/classify.ts'\nimport { fetchAgyFirstOk } from '../oauth/constants.ts'\nimport { classifyFetchError, classifyHttpError } from '../runtime/classify.ts'\nimport { deriveAntigravitySessionId, generateAntigravityRequestId } from '../runtime/identity.ts'\nimport { setThoughtSignature } from '../runtime/signature-cache.ts'\nimport { toAgyRequestBody } from './translate.ts'\nimport { parseAgySse } from './parse.ts'\nimport { AGY_PROVIDER, listAgyModels, resolveAgyModel } from './models.ts'\n\nexport interface AgyAccountSession {\n auth: OAuthAuthDetails\n account: ManagedAccount\n index: number\n /** Fingerprint + randomized impersonation headers for this request. */\n impersonation: {\n 'User-Agent': string\n 'X-Goog-Api-Client': string\n 'Client-Metadata': string\n }\n}\n\nexport interface AgyAdapterOptions {\n /** Resolve the active account (shell refreshes expired tokens and switches on rotation). */\n getSession(): Promise<AgyAccountSession | undefined>\n /** Report a classified upstream failure so the shell can cool/rotate/revoke. */\n reportFailure(\n kind: FailureKind,\n session: AgyAccountSession,\n info?: { retryAfterMs?: number; status?: number; rateLimitCategory?: RateLimitCategory },\n ): Promise<void>\n /** Report a clean stream completion (resets the failure counter). */\n markSuccess?(session: AgyAccountSession): Promise<void>\n}\n\nconst UPSTREAM_ERROR_CODE = 'UPSTREAM'\n\n/** Build the impersonation headers for one request (per-request randomization applied by the shell). */\nexport function buildRequestHeaders(session: AgyAccountSession): Record<string, string> {\n return {\n authorization: `Bearer ${session.auth.access}`,\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'x-goog-request-id': generateAntigravityRequestId(),\n ...attributionHeaders(),\n ...session.impersonation,\n }\n}\n\nexport class AgyAdapter extends LlmAdapter {\n private readonly options: AgyAdapterOptions\n\n constructor(options: AgyAdapterOptions) {\n super()\n this.options = options\n }\n\n override providerInfo(_provider: string): LlmProviderInfo {\n return { id: AGY_PROVIDER, name: 'Antigravity (agy)' }\n }\n\n override async listModels(_provider: string): Promise<readonly LlmModelInfo[]> {\n const session = await this.options.getSession()\n return listAgyModels(session?.auth.access, session?.account.projectId)\n }\n\n override async resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo> {\n return resolveAgyModel(provider, model)\n }\n\n override async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {\n const session = await this.options.getSession()\n if (!session) {\n throw new LlmError(\n 'No agy account configured — run `dsh-agy login` to authenticate.',\n 'NO_CREDENTIAL',\n )\n }\n\n const body = toAgyRequestBody(options, {\n projectId: session.account.projectId,\n sessionId: deriveAntigravitySessionId(session.account.email) ?? undefined,\n })\n const headers = buildRequestHeaders(session)\n\n let response: Response\n try {\n response = await fetchAgyFirstOk('/v1internal:streamGenerateContent?alt=sse', {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: options.signal,\n })\n } catch (error) {\n const classified = classifyFetchError(error)\n await this.options.reportFailure(classified.kind, session)\n throw new LlmError(classified.message ?? 'agy fetch failed', 'TRANSPORT', { cause: error })\n }\n\n if (!response.ok) {\n const bodyText = await response.text().catch(() => undefined)\n const classified = classifyHttpError(response.status, response.headers, bodyText)\n await this.options.reportFailure(classified.kind, session, {\n retryAfterMs: classified.retryAfterMs,\n status: response.status,\n rateLimitCategory: classified.rateLimitCategory,\n })\n if (classified.kind === 'rate-limit') {\n // soft/rate limits are retryable by the harness (RATE_LIMIT + delay);\n // daily quota exhaustion is terminal (QUOTA, 24h cooldown already set).\n if (classified.rateLimitCategory === 'quota_exhausted') {\n throw new LlmError(\n `agy daily quota exhausted (${response.status}): ${classified.message ?? ''}`,\n QUOTA_EXCEEDED_CODE,\n )\n }\n throw new LlmError(\n `agy rate-limited (${response.status}): ${classified.message ?? ''}`,\n 'RATE_LIMIT',\n {\n providerRetryAfterMs: classified.retryAfterMs ?? undefined,\n requestId: ProviderRequestId(generateAntigravityRequestId()),\n },\n )\n }\n if (classified.kind === 'auth-failure') {\n throw new LlmError(\n `agy authentication failed (${response.status}) — run \\`dsh-agy login\\``,\n 'INVALID_CREDENTIAL',\n )\n }\n throw new LlmError(\n `agy upstream error (${response.status}): ${classified.message ?? ''}`,\n UPSTREAM_ERROR_CODE,\n )\n }\n\n if (!response.body) {\n throw new LlmError('agy stream returned no body', UPSTREAM_ERROR_CODE)\n }\n\n try {\n yield* parseAgySse(response.body, {\n signal: options.signal,\n onToolSignature: (toolCallId, signature) => {\n setThoughtSignature(toolCallId, signature)\n },\n })\n await this.options.markSuccess?.(session)\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n throw new LlmError('agy stream aborted', 'ABORTED', { cause: error })\n }\n await this.options.reportFailure('network-error', session)\n throw new LlmError(\n error instanceof Error ? error.message : 'agy stream parse failed',\n UPSTREAM_ERROR_CODE,\n { cause: error },\n )\n }\n }\n}\n\nexport type { ToolSchema }\n","/**\n * Shared runtime construction for the in-harness plugin entries: master-key\n * codec resolution (credentials seam first, credentials document fallback),\n * account store, session manager, and adapter. Used by the main plugin\n * (adapter registration) and the web plugin (route registration) so both\n * entries operate on the same store.\n */\n\nimport { randomBytes } from 'node:crypto'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { AgyAdapter } from './adapter/adapter.ts'\nimport { AGY_PROVIDER } from './adapter/models.ts'\nimport { resolveAntigravityVersion } from './runtime/version.ts'\nimport { AgySessionManager } from './session.ts'\nimport { JsonAccountStore } from './store/accounts.ts'\nimport type { AccountStore } from './store/accounts.ts'\nimport {\n MASTER_KEY_REF,\n createAesGcmCodec,\n deriveKey,\n loadMasterKey,\n persistMasterKey,\n resolveDshHome,\n resolveMasterKeyCodec,\n} from './store/keyring.ts'\nimport type { SecretCodec } from './store/keyring.ts'\n\nexport interface CredentialsSeam {\n resolve(ref: string): Promise<{ value: string } | undefined>\n set(ref: string, value: string): Promise<void>\n}\n\nfunction codecFrom(masterKey: string): SecretCodec {\n return createAesGcmCodec(deriveKey(masterKey))\n}\n\n/** Resolve or create the master key, preferring the credentials seam when available. */\nexport async function resolveCodec(ctx: Context): Promise<{ codec: SecretCodec; created: boolean }> {\n const dshHome = resolveDshHome()\n const credentials = ctx.get('credentials') as CredentialsSeam | undefined\n\n if (credentials) {\n const resolved = await credentials.resolve(MASTER_KEY_REF)\n if (resolved) return { codec: codecFrom(resolved.value), created: false }\n const fileKey = loadMasterKey(dshHome)\n if (fileKey) return { codec: codecFrom(fileKey), created: false }\n const fresh = randomBytes(32).toString('hex')\n try {\n await credentials.set(MASTER_KEY_REF, fresh)\n return { codec: codecFrom(fresh), created: true }\n } catch {\n // Read-only shadowing: persist to the credentials document directly.\n persistMasterKey(dshHome, fresh)\n return { codec: codecFrom(fresh), created: true }\n }\n }\n\n return resolveMasterKeyCodec(dshHome)\n}\n\n/** Build the store, session manager, and adapter for one plugin entry. */\nexport async function createAgyRuntime(ctx: Context): Promise<{\n store: AccountStore\n sessions: AgySessionManager\n adapter: AgyAdapter\n}> {\n // Warm the version cache (non-blocking) so fingerprint generation inside\n // the rate-limit path never waits on a cold feed.\n void resolveAntigravityVersion().catch(() => {})\n const { codec } = await resolveCodec(ctx)\n const dshHome = resolveDshHome()\n const store = new JsonAccountStore({ file: `${dshHome}/agy-accounts.json`, codec })\n const sessions = new AgySessionManager({ store })\n const adapter = new AgyAdapter({\n getSession: () => sessions.getSession(),\n reportFailure: (kind, session, info) => sessions.reportFailure(kind, session, info),\n markSuccess: (session) => sessions.markSuccess(session),\n })\n return { store, sessions, adapter }\n}\n\nexport { AGY_PROVIDER }\n"],"mappings":"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"}
|
|
@@ -89,9 +89,10 @@ const AGY_VERSION_FALLBACK = "1.18.3";
|
|
|
89
89
|
function getAgyBootstrapUserAgent(version = AGY_VERSION_FALLBACK) {
|
|
90
90
|
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`;
|
|
91
91
|
}
|
|
92
|
-
/** Client-Metadata payload for bootstrap calls
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
/** Client-Metadata payload for bootstrap calls — ideType only (backend enum
|
|
93
|
+
* validation rejects freely-added platform/pluginType; AGENTS.md invariant). */
|
|
94
|
+
function getAgyBootstrapClientMetadata() {
|
|
95
|
+
return "{\"ideType\":\"ANTIGRAVITY\"}";
|
|
95
96
|
}
|
|
96
97
|
//#endregion
|
|
97
98
|
//#region src/runtime/identity.ts
|
|
@@ -764,6 +765,7 @@ function decideRotation(kind, account, consecutiveFailures, retryAfterMs, catego
|
|
|
764
765
|
action: "cool",
|
|
765
766
|
backoffMs
|
|
766
767
|
};
|
|
768
|
+
case "request-error": return { action: "noop" };
|
|
767
769
|
case "transient": return {
|
|
768
770
|
action: "retry",
|
|
769
771
|
backoffMs
|
|
@@ -871,9 +873,6 @@ function getFingerprintData() {
|
|
|
871
873
|
function randomFrom(arr) {
|
|
872
874
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
873
875
|
}
|
|
874
|
-
function platformToDisplayName(platform) {
|
|
875
|
-
return platform.startsWith("windows") ? "WINDOWS" : "MACOS";
|
|
876
|
-
}
|
|
877
876
|
/** Generate a randomized device fingerprint representing one apparent device. */
|
|
878
877
|
function generateFingerprint(data = getFingerprintData(), version = randomFrom(data.versionPool)) {
|
|
879
878
|
const platform = randomFrom(data.platforms);
|
|
@@ -882,25 +881,16 @@ function generateFingerprint(data = getFingerprintData(), version = randomFrom(d
|
|
|
882
881
|
sessionToken: randomBytes(16).toString("hex"),
|
|
883
882
|
userAgent: `antigravity/${version} ${platform}`,
|
|
884
883
|
apiClient: randomFrom(data.sdkClients),
|
|
885
|
-
clientMetadata: {
|
|
886
|
-
ideType: randomFrom(data.ideTypes),
|
|
887
|
-
platform: platformToDisplayName(platform),
|
|
888
|
-
pluginType: randomFrom(data.pluginTypes)
|
|
889
|
-
},
|
|
884
|
+
clientMetadata: { ideType: randomFrom(data.ideTypes) },
|
|
890
885
|
createdAt: Date.now()
|
|
891
886
|
};
|
|
892
887
|
}
|
|
893
888
|
/** Per-request randomized headers (platform + SDK client pools). */
|
|
894
889
|
function getRandomizedHeaders(data = getFingerprintData(), version = randomFrom(data.versionPool)) {
|
|
895
|
-
const platform = randomFrom(data.platforms);
|
|
896
890
|
return {
|
|
897
|
-
"User-Agent": `antigravity/${version} ${
|
|
891
|
+
"User-Agent": `antigravity/${version} ${randomFrom(data.platforms)}`,
|
|
898
892
|
"X-Goog-Api-Client": randomFrom(data.sdkClients),
|
|
899
|
-
"Client-Metadata": JSON.stringify({
|
|
900
|
-
ideType: randomFrom(data.ideTypes),
|
|
901
|
-
platform: platformToDisplayName(platform),
|
|
902
|
-
pluginType: randomFrom(data.pluginTypes)
|
|
903
|
-
})
|
|
893
|
+
"Client-Metadata": JSON.stringify({ ideType: randomFrom(data.ideTypes) })
|
|
904
894
|
};
|
|
905
895
|
}
|
|
906
896
|
/** Rewrite the version inside a fingerprint UA; reports whether it changed. */
|
|
@@ -919,8 +909,6 @@ function recordFingerprintVersion(history, fingerprint, reason) {
|
|
|
919
909
|
reason
|
|
920
910
|
}].slice(-5);
|
|
921
911
|
}
|
|
922
|
-
//#endregion
|
|
923
|
-
//#region src/session.ts
|
|
924
912
|
/**
|
|
925
913
|
* Resolve the impersonation headers for one request from the account's
|
|
926
914
|
* persistent fingerprint (stable identity), falling back to full per-request
|
|
@@ -949,6 +937,14 @@ var AgySessionManager = class {
|
|
|
949
937
|
failureCounts = /* @__PURE__ */ new Map();
|
|
950
938
|
/** Accounts whose request-time project discovery already failed (no retry per request). */
|
|
951
939
|
projectRetryFailed = /* @__PURE__ */ new Set();
|
|
940
|
+
/**
|
|
941
|
+
* Session affinity (time-window approximation): DSH exposes no conversation
|
|
942
|
+
* id, so instead of pinning per session we reuse the last-used account while
|
|
943
|
+
* it is fresh. Keeps upstream prefix caching and sessionId continuity across
|
|
944
|
+
* the turns of one conversation (OmniRoute pins by session for the same
|
|
945
|
+
* reason). Cleared on rotate so a failure re-picks from activeIndex.
|
|
946
|
+
*/
|
|
947
|
+
lastUsed = null;
|
|
952
948
|
constructor(options) {
|
|
953
949
|
this.store = options.store;
|
|
954
950
|
this.onRotate = options.onRotate;
|
|
@@ -1001,6 +997,13 @@ var AgySessionManager = class {
|
|
|
1001
997
|
for (const account of storage.accounts) clearExpiredState(account, now);
|
|
1002
998
|
let resolved = resolveActiveAccount(storage);
|
|
1003
999
|
if (!resolved) return void 0;
|
|
1000
|
+
if (this.lastUsed && now - this.lastUsed.at < 6e5) {
|
|
1001
|
+
const last = storage.accounts[this.lastUsed.index];
|
|
1002
|
+
if (last && last.enabled !== false && !isCoolingDown(last, now)) resolved = {
|
|
1003
|
+
account: last,
|
|
1004
|
+
index: this.lastUsed.index
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1004
1007
|
if (isCoolingDown(resolved.account, now)) {
|
|
1005
1008
|
const nextIndex = pickNextAccountIndex(storage.accounts, resolved.index, now);
|
|
1006
1009
|
if (nextIndex !== resolved.index) resolved = {
|
|
@@ -1024,7 +1027,7 @@ var AgySessionManager = class {
|
|
|
1024
1027
|
if (!auth) return void 0;
|
|
1025
1028
|
const key = this.accountKey(picked.account);
|
|
1026
1029
|
if (!picked.account.projectId && !this.projectRetryFailed.has(key)) try {
|
|
1027
|
-
const { loadCodeAssist } = await import("./exchange-
|
|
1030
|
+
const { loadCodeAssist } = await import("./exchange-E1EN8XaA.mjs").then((n) => n.n);
|
|
1028
1031
|
const { projectId } = await loadCodeAssist(auth.access);
|
|
1029
1032
|
if (projectId) {
|
|
1030
1033
|
await this.store.mutate((s) => {
|
|
@@ -1044,6 +1047,10 @@ var AgySessionManager = class {
|
|
|
1044
1047
|
} catch {
|
|
1045
1048
|
this.projectRetryFailed.add(key);
|
|
1046
1049
|
}
|
|
1050
|
+
this.lastUsed = {
|
|
1051
|
+
index: picked.index,
|
|
1052
|
+
at: Date.now()
|
|
1053
|
+
};
|
|
1047
1054
|
return {
|
|
1048
1055
|
auth,
|
|
1049
1056
|
account: picked.account,
|
|
@@ -1086,6 +1093,7 @@ var AgySessionManager = class {
|
|
|
1086
1093
|
storage.activeIndex = nextIndex;
|
|
1087
1094
|
this.onRotate?.(session.index, nextIndex, kind);
|
|
1088
1095
|
}
|
|
1096
|
+
this.lastUsed = null;
|
|
1089
1097
|
}
|
|
1090
1098
|
await this.store.save(storage);
|
|
1091
1099
|
}
|
|
@@ -1106,7 +1114,7 @@ var AgySessionManager = class {
|
|
|
1106
1114
|
error: "No agy account configured — run `dsh-agy login` first."
|
|
1107
1115
|
};
|
|
1108
1116
|
try {
|
|
1109
|
-
const { toAgyRequestBody } = await import("./translate-
|
|
1117
|
+
const { toAgyRequestBody } = await import("./translate-ChUQAXjN.mjs").then((n) => n.n);
|
|
1110
1118
|
const { fetchAgyFirstOk } = await Promise.resolve().then(() => constants_exports);
|
|
1111
1119
|
const { parseAgySse } = await import("./parse-CiEzD9d_.mjs").then((n) => n.n);
|
|
1112
1120
|
const body = toAgyRequestBody({
|
|
@@ -1218,4 +1226,4 @@ var AgySessionManager = class {
|
|
|
1218
1226
|
//#endregion
|
|
1219
1227
|
export { OAUTH_TOKEN_URL as C, getAgyBootstrapUserAgent as D, getAgyBootstrapClientMetadata as E, OAUTH_AUTHORIZE_URL as S, fetchAgyFirstOk as T, AGY_CLIENT_ID as _, MASTER_KEY_REF as a, AGY_ENDPOINT_FALLBACKS as b, loadMasterKey as c, resolveMasterKeyCodec as d, calculateTokenExpiry as f, generateAntigravityRequestId as g, deriveAntigravitySessionId as h, JsonAccountStore as i, persistMasterKey as l, resolveAntigravityVersionBounded as m, generateFingerprint as n, createAesGcmCodec as o, resolveAntigravityVersion as p, recordFingerprintVersion as r, deriveKey as s, AgySessionManager as t, resolveDshHome as u, AGY_CLIENT_SECRET as v, OAUTH_USERINFO_URL as w, AGY_SCOPES as x, AGY_DEFAULT_REDIRECT_URI as y };
|
|
1220
1228
|
|
|
1221
|
-
//# sourceMappingURL=session-
|
|
1229
|
+
//# sourceMappingURL=session-Bo6-av5Z.mjs.map
|