dsh-agy 0.2.9 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +1 -0
  2. package/lib/{accounts-Um8N9I56.mjs → accounts-hsgwm7J9.mjs} +260 -233
  3. package/lib/accounts-hsgwm7J9.mjs.map +1 -0
  4. package/lib/auth-DPPVFwio.mjs +32 -0
  5. package/lib/auth-DPPVFwio.mjs.map +1 -0
  6. package/lib/cli/index.d.mts.map +1 -1
  7. package/lib/cli/index.mjs +8 -7
  8. package/lib/cli/index.mjs.map +1 -1
  9. package/lib/constants-BFKYjOmh.mjs +173 -0
  10. package/lib/constants-BFKYjOmh.mjs.map +1 -0
  11. package/lib/{exchange-Bdql4kf3.mjs → exchange-dQm2mjJ5.mjs} +15 -14
  12. package/lib/exchange-dQm2mjJ5.mjs.map +1 -0
  13. package/lib/identity-DsftHTJC.mjs +35 -0
  14. package/lib/identity-DsftHTJC.mjs.map +1 -0
  15. package/lib/{import-BkqCMX7m.mjs → import-D7R0GpRr.mjs} +14 -12
  16. package/lib/import-D7R0GpRr.mjs.map +1 -0
  17. package/lib/index.mjs +2 -2
  18. package/lib/{models-F-1cWO0f.mjs → models-IfOGIOS9.mjs} +3 -3
  19. package/lib/{models-F-1cWO0f.mjs.map → models-IfOGIOS9.mjs.map} +1 -1
  20. package/lib/{plugin-common-Di4n-1eR.mjs → plugin-common-Dk772zkm.mjs} +16 -129
  21. package/lib/plugin-common-Dk772zkm.mjs.map +1 -0
  22. package/lib/{proxy-DQPIwUov.mjs → proxy-DLvX8yxv.mjs} +63 -11
  23. package/lib/proxy-DLvX8yxv.mjs.map +1 -0
  24. package/lib/{translate-CE9tNXiV.mjs → translate-D0JHjwC-.mjs} +2 -2
  25. package/lib/{translate-CE9tNXiV.mjs.map → translate-D0JHjwC-.mjs.map} +1 -1
  26. package/lib/web/plugin.mjs +9 -9
  27. package/lib/web/plugin.mjs.map +1 -1
  28. package/package.json +3 -2
  29. package/lib/accounts-Um8N9I56.mjs.map +0 -1
  30. package/lib/exchange-Bdql4kf3.mjs.map +0 -1
  31. package/lib/import-BkqCMX7m.mjs.map +0 -1
  32. package/lib/plugin-common-Di4n-1eR.mjs.map +0 -1
  33. package/lib/proxy-DQPIwUov.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/callback-server.ts","../../src/cli/index.ts"],"sourcesContent":["/** Loopback OAuth callback server for the standalone CLI login flow. */\n\nimport { createServer } from 'node:http'\nimport type { Server } from 'node:http'\n\nexport interface CallbackResult {\n code: string\n state: string\n url: string\n}\n\nexport interface CallbackHandle {\n result: Promise<CallbackResult>\n /** Resolves once the loopback listener is bound; rejects on bind errors. */\n ready: Promise<void>\n close(): Promise<void>\n}\n\nconst SUCCESS_HTML = `<!doctype html><html><head><meta charset=\"utf-8\"><title>Login successful</title></head>\n<body style=\"font-family:system-ui;text-align:center;padding:3rem\">\n<h2>✓ Authentication successful</h2>\n<p>You can close this tab and return to the terminal.</p>\n</body></html>`\n\n/** Listen on 127.0.0.1:<port>/oauth-callback and resolve with the code+state. */\nexport function startCallbackServer(options: { port?: number; timeoutMs?: number } = {}): CallbackHandle {\n const port = options.port ?? 51121\n const timeoutMs = options.timeoutMs ?? 300_000\n\n let resolveResult: (value: CallbackResult) => void\n let rejectResult: (reason: Error) => void\n const result = new Promise<CallbackResult>((resolve, reject) => {\n resolveResult = resolve\n rejectResult = reject\n })\n\n let resolveReady: () => void\n let rejectReady: (reason: Error) => void\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve\n rejectReady = reject\n })\n\n const server: Server = createServer((req, res) => {\n const url = new URL(req.url ?? '/', 'http://localhost')\n if (url.pathname === '/oauth-callback') {\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n if (!code || !state) {\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end('<h2>Missing code or state</h2>')\n return\n }\n res.writeHead(200, { 'Content-Type': 'text/html', connection: 'close' })\n res.end(SUCCESS_HTML)\n resolveResult({ code, state, url: url.toString() })\n // Give the browser a moment to render before the server closes.\n setTimeout(() => server.close(), 1500)\n return\n }\n res.writeHead(404)\n res.end('Not found')\n })\n\n server.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EADDRINUSE') {\n rejectResult(new Error(`Port ${port} is already in use — pass --port to pick another.`))\n } else {\n rejectResult(error)\n }\n rejectReady(error)\n })\n\n server.listen(port, '127.0.0.1', () => {\n resolveReady()\n })\n\n const timeout = setTimeout(() => {\n rejectResult(new Error('Authentication timed out — no callback received.'))\n server.close()\n }, timeoutMs)\n\n const handle: CallbackHandle = {\n result,\n ready,\n async close() {\n clearTimeout(timeout)\n if (server.listening) {\n // The success page's keep-alive connection would otherwise hold\n // server.close() (and the CLI process) open indefinitely.\n server.closeAllConnections()\n await new Promise<void>((resolve) => server.close(() => resolve()))\n }\n },\n }\n return handle\n}\n\n/** Open a URL in the system browser (best effort). */\nexport async function openBrowser(url: string): Promise<boolean> {\n const { spawn } = await import('node:child_process')\n const platform = process.platform\n const command = platform === 'darwin' ? ['open', url]\n : platform === 'win32' ? ['cmd', '/c', 'start', '', url]\n : ['xdg-open', url]\n return new Promise<boolean>((resolve) => {\n const child = spawn(command[0]!, command.slice(1), { stdio: 'ignore' })\n child.on('error', () => resolve(false))\n child.on('spawn', () => resolve(true))\n })\n}\n","/**\n * dsh-agy CLI: login / status / import / verify / logout.\n * Standalone bin — the dsh launcher has no plugin subcommand slot, and the\n * remote paste-blob flow must run on machines without a harness.\n */\n\nimport { Command } from 'commander'\nimport { readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createInterface } from 'node:readline/promises'\nimport { stdin as input, stdout as output } from 'node:process'\nimport { authorizeAntigravity } from '../oauth/authorize.ts'\nimport { exchangeAntigravity } from '../oauth/exchange.ts'\nimport { encodeCredentialBlob } from '../oauth/blob.ts'\nimport { AGY_DEFAULT_REDIRECT_URI } from '../oauth/constants.ts'\nimport { createAesGcmCodec, deriveKey, loadMasterKey, resolveDshHome, resolveMasterKeyCodec } from '../store/keyring.ts'\nimport type { SecretCodec } from '../store/keyring.ts'\nimport { JsonAccountStore, maskProxyUrl } from '../store/accounts.ts'\nimport { AgySessionManager } from '../session.ts'\nimport { isAgyDisabled } from '../runtime/risk.ts'\nimport { startCallbackServer, openBrowser } from './callback-server.ts'\nimport { importManySources, upsertImportedAccount } from './import.ts'\nimport { isProxyReachable, normalizeProxyUrl } from '../proxy.ts'\n\n/** Package version, read from the shipped package.json — never hard-coded twice. */\nconst { version: PACKAGE_VERSION } = JSON.parse(\n readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),\n) as { version: string }\n\nfunction createStore(options: { readOnly?: boolean } = {}): JsonAccountStore {\n const dshHome = resolveDshHome()\n let codec: SecretCodec\n if (options.readOnly) {\n // Read-only commands must never create the master key or the credentials\n // document: they would fail for no reason on a read-only HOME and write\n // files nobody asked for anywhere else.\n const masterKey = loadMasterKey(dshHome)\n if (!masterKey) {\n throw new Error('No agy account store found — run `dsh-agy login` first.')\n }\n codec = createAesGcmCodec(deriveKey(masterKey))\n } else {\n codec = resolveMasterKeyCodec(dshHome).codec\n }\n return new JsonAccountStore({ file: `${dshHome}/agy-accounts.json`, codec })\n}\n\n/** Read-only store with a friendly error when no credentials exist yet. */\nfunction createReadOnlyStoreOrExit(): JsonAccountStore {\n try {\n return createStore({ readOnly: true })\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n}\n\nfunction resolveProxyOption(raw: string | undefined): string | undefined {\n if (raw === undefined) return undefined\n if (!raw.trim()) return ''\n try {\n return normalizeProxyUrl(raw)\n } catch (error) {\n console.error(`Invalid proxy URL: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n}\n\nasync function ask(question: string): Promise<string> {\n const rl = createInterface({ input, output })\n try {\n return (await rl.question(question)).trim()\n } finally {\n rl.close()\n }\n}\n\n/** Remote/SSH sessions cannot reach a local browser; auto-select headless paste. */\nfunction isRemoteSession(): boolean {\n return !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY)\n}\n\nasync function loginCommand(options: { headless: boolean; blob: boolean; port: number; project?: string; timeout?: string; proxy?: string }) {\n const proxyInput = resolveProxyOption(options.proxy)\n // proxy \"\" sentinel means clear; normalizeProxyUrl already handled\n const normalizedProxy = proxyInput === '' ? '' : proxyInput\n const store = createStore()\n const redirectUri = `http://localhost:${options.port}/oauth-callback`\n if (!options.headless && isRemoteSession()) {\n console.log('(SSH session detected — using headless paste flow; pass --headless explicitly to force)')\n options.headless = true\n }\n\n let callback: Awaited<ReturnType<typeof startCallbackServer>> | undefined\n if (!options.headless) {\n // Bind the loopback listener BEFORE the URL is shown: a local process\n // squatting on the fixed port could otherwise capture code+state (the\n // state carries the PKCE verifier) and steal the refresh token.\n callback = startCallbackServer({ port: options.port, timeoutMs: Number(options.timeout) || 300_000 })\n try {\n await callback.ready\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n }\n\n const { url, verifier } = await authorizeAntigravity(redirectUri, options.project ?? '')\n console.log(`\\nOpen this URL in a browser to authorize:\\n\\n ${url}\\n`)\n\n let code: string\n let state: string\n\n if (options.headless) {\n const pasted = await ask('After approving, paste the full redirected URL here: ')\n const parsed = new URL(pasted)\n code = parsed.searchParams.get('code') ?? ''\n state = parsed.searchParams.get('state') ?? ''\n if (!code || !state) {\n console.error('Error: pasted URL is missing code or state.')\n process.exit(1)\n }\n } else {\n const opened = await openBrowser(url)\n if (!opened) console.log('(Could not open a browser automatically — open the URL manually.)')\n let callbackResult: { code: string; state: string; url: string }\n try {\n callbackResult = await callback!.result\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n code = callbackResult.code\n state = callbackResult.state\n }\n\n // Bind the exchange to the verifier we issued: a state from any other login\n // (pasted from another session, or fabricated) must be rejected.\n const result = await exchangeAntigravity(code, state, redirectUri, verifier)\n if (result.type === 'failed') {\n console.error(`Login failed: ${result.error}`)\n process.exit(1)\n }\n\n if (options.blob) {\n if (normalizedProxy !== undefined) {\n console.log('(Note: --proxy is ignored with --blob; proxy is only stored with the account)')\n }\n const blob = encodeCredentialBlob('agy', {\n access_token: result.access,\n refresh_token: result.refresh.split('|')[0],\n expires_in: Math.max(0, Math.round((result.expires - Date.now()) / 1000)),\n })\n console.log(`\\nPaste this blob into the remote dashboard/CLI:\\n\\n${blob}\\n`)\n } else {\n const proxyForUpsert = normalizedProxy !== undefined ? normalizedProxy : undefined\n const { account, created } = await upsertImportedAccount(store, {\n accessToken: result.access,\n refreshToken: result.refresh.split('|')[0]!,\n tokenType: 'Bearer',\n expiresAt: new Date(result.expires).toISOString(),\n authMethod: 'oauth',\n email: result.email ?? null,\n projectId: result.projectId || null,\n clientId: result.clientId || null,\n }, { overwriteExisting: true, ...(proxyForUpsert !== undefined ? { proxy: proxyForUpsert } : {}) })\n const masked = maskProxyUrl(account.proxy)\n console.log(`${created ? 'Added' : 'Updated'} account: ${account.email ?? '(no email)'} (project: ${result.projectId || 'default'})${masked ? ` proxy: ${masked}` : ''}`)\n }\n\n await callback?.close()\n}\n\nasync function statusCommand() {\n const store = createReadOnlyStoreOrExit()\n const storage = await store.load()\n if (storage.accounts.length === 0) {\n console.log('No agy accounts. Run `dsh-agy login` first.')\n return\n }\n console.log(`\\n${storage.accounts.length} account(s), active index ${storage.activeIndex}:\\n`)\n const sessions = new AgySessionManager({ store })\n for (const [index, account] of storage.accounts.entries()) {\n const marker = index === storage.activeIndex ? '★' : ' '\n const state = account.enabled === false ? 'disabled'\n : account.verificationRequired ? 'verification-required'\n : account.coolingDownUntil && account.coolingDownUntil > Date.now() ? 'cooling'\n : 'active'\n const maskedProxy = maskProxyUrl(account.proxy)\n console.log(` ${marker} [${index}] ${account.email ?? '(no email)'} — ${state}${account.projectId ? ` (project: ${account.projectId})` : ''}${maskedProxy ? ` proxy: ${maskedProxy}` : ''}`)\n\n // Best-effort quota summary via fetchAvailableModels (fresh access token).\n const session = await sessions.getSession().catch(() => undefined)\n if (session && session.index === index) {\n try {\n const { fetchAvailableModels } = await import('../adapter/models.ts')\n const discovered = await fetchAvailableModels(session.auth.access, session.account.projectId)\n const entries = Object.entries(discovered.models ?? {})\n if (entries.length > 0) {\n const withQuota = entries\n .map(([id, entry]) => ({ id, ...entry.quotaInfo }))\n .filter((e) => typeof e.remainingFraction === 'number')\n .sort((a, b) => (a.remainingFraction ?? 0) - (b.remainingFraction ?? 0))\n if (withQuota.length > 0) {\n const lowest = withQuota[0]!\n console.log(` models: ${entries.length}, lowest quota: ${Math.round((lowest.remainingFraction ?? 0) * 100)}% (${lowest.id})${lowest.resetTime ? `, resets ${new Date(lowest.resetTime).toISOString()}` : ''}`)\n } else {\n console.log(` models: ${entries.length} (no per-model quota reported)`)\n }\n }\n } catch (error) {\n console.log(` quota: unavailable (${error instanceof Error ? error.message : String(error)})`)\n }\n }\n }\n}\n\nasync function importCommand(options: { blob: boolean; files?: string[]; email?: string; overwrite: boolean; proxy?: string }) {\n const store = createStore()\n const proxyForUpsert = resolveProxyOption(options.proxy)\n let items: Array<{ source: unknown; kind: 'json' | 'blob' }>\n if (options.files && options.files.length > 0) {\n items = options.files.map((file) => {\n const raw = readFileSync(file, 'utf8')\n return { source: options.blob ? raw : (JSON.parse(raw) as unknown), kind: options.blob ? 'blob' : 'json' }\n })\n } else {\n const pasted = await ask('Paste the agy token JSON (or blob with --blob): ')\n items = [{ source: pasted, kind: options.blob ? 'blob' : 'json' }]\n }\n const importOpts: { email?: string; overwriteExisting?: boolean; proxy?: string } = {\n email: options.email,\n overwriteExisting: options.overwrite,\n }\n if (proxyForUpsert !== undefined) importOpts.proxy = proxyForUpsert\n const result = await importManySources(items, store, importOpts)\n console.log(`Imported ${result.imported}, replaced ${result.replaced}${result.errors.length > 0 ? `, ${result.errors.length} failed` : ''}`)\n for (const error of result.errors) console.log(` ! ${error}`)\n}\n\nasync function exportCommand(options: { index?: string; out?: string }) {\n const store = createReadOnlyStoreOrExit()\n const sessions = new AgySessionManager({ store })\n const storage = await store.load()\n const indices = options.index !== undefined\n ? [Number(options.index)]\n : storage.accounts.map((_, i) => i)\n\n let exported = 0\n for (const index of indices) {\n const account = storage.accounts[index]\n if (!account) {\n console.log(`[${index}] not found`)\n continue\n }\n const result = await sessions.exportBlob(index)\n if (!result.blob) {\n console.log(`[${index}] ${account.email ?? ''} — FAILED: ${result.error}`)\n continue\n }\n if (options.out) {\n const file = join(options.out, `dsh-agy-${index}.blob`)\n writeFileSync(file, result.blob + '\\n')\n console.log(`[${index}] ${account.email ?? ''} — wrote ${file}`)\n } else {\n console.log(result.blob)\n }\n exported++\n }\n if (!options.out) console.log(`\\n${exported} blob(s) exported — one line each, paste into a remote import`)\n}\n\nasync function verifyCommand(options: { index?: string }) {\n const store = createReadOnlyStoreOrExit()\n const sessions = new AgySessionManager({ store })\n const storage = await store.load()\n const indices = options.index !== undefined\n ? [Number(options.index)]\n : storage.accounts.map((_, i) => i)\n\n for (const index of indices) {\n const account = storage.accounts[index]\n if (!account) {\n console.log(`[${index}] not found`)\n continue\n }\n const result = await sessions.verifyAccount(index)\n if (result.ok) {\n console.log(`[${index}] ${account.email ?? ''} — OK${result.email && result.email !== account.email ? ` (userinfo: ${result.email})` : ''}`)\n } else {\n console.log(`[${index}] ${account.email ?? ''} — FAILED: ${result.error}`)\n }\n }\n}\n\nasync function healthCommand(options: { interval?: string; index?: string[] }) {\n const store = createReadOnlyStoreOrExit()\n const sessions = new AgySessionManager({\n store,\n onHealthReport: (results) => {\n for (const result of results) {\n if (result.ok) console.log(`[${result.index}] ${result.email ?? ''} — OK`)\n else console.log(`[${result.index}] ${result.email ?? ''} — FAILED: ${result.error}`)\n }\n },\n })\n const indices = options.index?.map((value) => Number(value))\n\n if (options.interval) {\n const intervalMs = Number(options.interval)\n if (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n console.error('Error: --interval must be a positive number of milliseconds.')\n process.exit(1)\n }\n console.log(`Health probe every ${intervalMs}ms (Ctrl+C to stop).`)\n sessions.startHealthProbe(intervalMs, { unref: false })\n await new Promise<void>(() => {}) // keep the loop alive\n return\n }\n\n const results = await sessions.checkAccounts(indices)\n if (results.length === 0) console.log('No enabled accounts — run `dsh-agy login` first.')\n}\n\nasync function logoutCommand(options: { index?: string; email?: string }) {\n const store = createReadOnlyStoreOrExit()\n await store.mutate((storage) => {\n const index = options.index !== undefined\n ? Number(options.index)\n : options.email\n ? storage.accounts.findIndex((a) => a.email?.toLowerCase() === options.email!.toLowerCase())\n : storage.activeIndex\n if (index < 0 || index >= storage.accounts.length) {\n throw new Error(`account not found (index ${index})`)\n }\n const [removed] = storage.accounts.splice(index, 1)\n if (storage.activeIndex >= storage.accounts.length) storage.activeIndex = 0\n console.log(`Removed account: ${removed?.email ?? `[${index}]`}`)\n })\n}\n\nasync function proxySetCommand(options: { index: string; proxy: string }) {\n const idx = Number(options.index)\n if (!Number.isInteger(idx) || idx < 0) {\n console.error('Invalid --index (must be >= 0)')\n process.exit(1)\n }\n const raw = options.proxy ?? ''\n if (!raw.trim()) {\n console.error('Missing --proxy value')\n process.exit(1)\n }\n let normalized: string\n try {\n normalized = normalizeProxyUrl(raw)\n } catch (error) {\n console.error(`Invalid proxy URL: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n const store = createStore()\n await store.mutate((storage) => {\n const account = storage.accounts[idx]\n if (!account) throw new Error(`account not found (index ${idx})`)\n account.proxy = normalized\n })\n console.log(`[${idx}] proxy set to ${maskProxyUrl(normalized)}`)\n}\n\nasync function proxyClearCommand(options: { index: string }) {\n const idx = Number(options.index)\n if (!Number.isInteger(idx) || idx < 0) {\n console.error('Invalid --index (must be >= 0)')\n process.exit(1)\n }\n const store = createStore()\n await store.mutate((storage) => {\n const account = storage.accounts[idx]\n if (!account) throw new Error(`account not found (index ${idx})`)\n delete (account as { proxy?: string }).proxy\n })\n console.log(`[${idx}] proxy cleared`)\n}\n\nasync function proxyTestCommand(options: { index: string }) {\n const idx = Number(options.index)\n if (!Number.isInteger(idx) || idx < 0) {\n console.error('Invalid --index (must be >= 0)')\n process.exit(1)\n }\n const store = createReadOnlyStoreOrExit()\n const storage = await store.load()\n const account = storage.accounts[idx]\n if (!account) {\n console.error(`account not found (index ${idx})`)\n process.exit(1)\n }\n if (!account.proxy) {\n console.log(`[${idx}] no proxy configured`)\n return\n }\n const masked = maskProxyUrl(account.proxy)\n const reachable = await isProxyReachable(account.proxy)\n if (reachable) console.log(`[${idx}] ${masked} — ok`)\n else console.log(`[${idx}] ${masked} — proxy_unreachable`)\n}\n\nasync function proxyListCommand() {\n const store = createReadOnlyStoreOrExit()\n const storage = await store.load()\n if (storage.accounts.length === 0) {\n console.log('No agy accounts.')\n return\n }\n for (const [index, account] of storage.accounts.entries()) {\n const masked = maskProxyUrl(account.proxy)\n console.log(` [${index}] ${account.email ?? '(no email)'} — ${masked ?? '(no proxy)'}`)\n }\n}\n\nexport function createProgram(): Command {\n const program = new Command()\n program\n .name('dsh-agy')\n .description('Google Antigravity (agy) account management for DeepSeek Harness')\n .version(PACKAGE_VERSION)\n\n program.hook('preAction', () => {\n if (isAgyDisabled()) {\n console.error('dsh-agy is disabled (DSH_AGY_DISABLE=1).')\n process.exit(1)\n }\n })\n\n program\n .command('login')\n .description('OAuth login (browser, or headless paste)')\n .option('--headless', 'print the URL and wait for a pasted redirect URL', false)\n .option('--blob', 'print a paste-credential blob instead of storing the account', false)\n .option('--port <n>', 'loopback callback port', '51121')\n .option('--project <id>', 'bind the login to a specific project')\n .option('--timeout <ms>', 'callback timeout', '300000')\n .option('--proxy <url>', 'per-account proxy URL (http/https/socks5); empty to clear')\n .action(async (options) => {\n await loginCommand({ ...options, timeout: options.timeout })\n })\n\n program\n .command('status')\n .description('List accounts and their health')\n .action(async () => {\n await statusCommand()\n })\n\n program\n .command('import')\n .description('Import agy token files or paste credentials')\n .argument('[files...]', 'paths to agy auth.json token files (multiple allowed)')\n .option('--blob', 'the pasted value is a credential blob', false)\n .option('--email <email>', 'account email (skips userinfo verification)')\n .option('--overwrite', 'replace an existing account with the same email', false)\n .option('--proxy <url>', 'per-account proxy URL (http/https/socks5); empty to clear')\n .action(async (files: string[] | undefined, options: { blob: boolean; email?: string; overwrite: boolean; proxy?: string }) => {\n await importCommand({ ...options, files })\n })\n\n program\n .command('export')\n .description('Export account credentials as paste blobs')\n .option('--index <n>', 'export one account by index; default all')\n .option('--out <dir>', 'write one dsh-agy-<index>.blob file per account into this directory (default: print to stdout)')\n .action(async (options: { index?: string; out?: string }) => {\n await exportCommand(options)\n })\n\n program\n .command('verify')\n .description('Verify account credentials (refresh + userinfo)')\n .option('--index <n>', 'verify one account by index; default all')\n .action(async (options: { index?: string }) => {\n await verifyCommand(options)\n })\n\n program\n .command('health')\n .description('Check every enabled account (refresh + userinfo), optionally on an interval')\n .option('--interval <ms>', 'repeat on an interval instead of once')\n .option('--index <n...>', 'check only these accounts')\n .action(async (options: { interval?: string; index?: string[] }) => {\n await healthCommand(options)\n })\n\n program\n .command('logout')\n .description('Remove an account')\n .option('--index <n>', 'account index (default: active)')\n .option('--email <email>', 'account email')\n .action(async (options: { index?: string; email?: string }) => {\n await logoutCommand(options)\n })\n\n const proxy = program.command('proxy').description('Manage per-account proxies')\n proxy\n .command('set')\n .description('Set proxy for an account')\n .requiredOption('--index <n>', 'account index')\n .requiredOption('--proxy <url>', 'proxy URL (http/https/socks5)')\n .action(async (options: { index: string; proxy: string }) => {\n await proxySetCommand(options)\n })\n proxy\n .command('clear')\n .description('Clear proxy for an account')\n .requiredOption('--index <n>', 'account index')\n .action(async (options: { index: string }) => {\n await proxyClearCommand(options)\n })\n proxy\n .command('test')\n .description('Test proxy reachability for an account (TCP 2s fast-fail)')\n .requiredOption('--index <n>', 'account index')\n .action(async (options: { index: string }) => {\n await proxyTestCommand(options)\n })\n proxy\n .command('list')\n .description('List accounts with masked proxies')\n .action(async () => {\n await proxyListCommand()\n })\n\n return program\n}\n\nexport { AGY_DEFAULT_REDIRECT_URI }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/callback-server.ts","../../src/cli/index.ts"],"sourcesContent":["/** Loopback OAuth callback server for the standalone CLI login flow. */\n\nimport { createServer } from 'node:http'\nimport type { Server } from 'node:http'\n\nexport interface CallbackResult {\n code: string\n state: string\n url: string\n}\n\nexport interface CallbackHandle {\n result: Promise<CallbackResult>\n /** Resolves once the loopback listener is bound; rejects on bind errors. */\n ready: Promise<void>\n close(): Promise<void>\n}\n\nconst SUCCESS_HTML = `<!doctype html><html><head><meta charset=\"utf-8\"><title>Login successful</title></head>\n<body style=\"font-family:system-ui;text-align:center;padding:3rem\">\n<h2>✓ Authentication successful</h2>\n<p>You can close this tab and return to the terminal.</p>\n</body></html>`\n\n/** Listen on 127.0.0.1:<port>/oauth-callback and resolve with the code+state. */\nexport function startCallbackServer(options: { port?: number; timeoutMs?: number } = {}): CallbackHandle {\n const port = options.port ?? 51121\n const timeoutMs = options.timeoutMs ?? 300_000\n\n let resolveResult: (value: CallbackResult) => void\n let rejectResult: (reason: Error) => void\n const result = new Promise<CallbackResult>((resolve, reject) => {\n resolveResult = resolve\n rejectResult = reject\n })\n\n let resolveReady: () => void\n let rejectReady: (reason: Error) => void\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve\n rejectReady = reject\n })\n\n const server: Server = createServer((req, res) => {\n const url = new URL(req.url ?? '/', 'http://localhost')\n if (url.pathname === '/oauth-callback') {\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n if (!code || !state) {\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end('<h2>Missing code or state</h2>')\n return\n }\n res.writeHead(200, { 'Content-Type': 'text/html', connection: 'close' })\n res.end(SUCCESS_HTML)\n resolveResult({ code, state, url: url.toString() })\n // Give the browser a moment to render before the server closes.\n setTimeout(() => server.close(), 1500)\n return\n }\n res.writeHead(404)\n res.end('Not found')\n })\n\n server.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EADDRINUSE') {\n rejectResult(new Error(`Port ${port} is already in use — pass --port to pick another.`))\n } else {\n rejectResult(error)\n }\n rejectReady(error)\n })\n\n server.listen(port, '127.0.0.1', () => {\n resolveReady()\n })\n\n const timeout = setTimeout(() => {\n rejectResult(new Error('Authentication timed out — no callback received.'))\n server.close()\n }, timeoutMs)\n\n const handle: CallbackHandle = {\n result,\n ready,\n async close() {\n clearTimeout(timeout)\n if (server.listening) {\n // The success page's keep-alive connection would otherwise hold\n // server.close() (and the CLI process) open indefinitely.\n server.closeAllConnections()\n await new Promise<void>((resolve) => server.close(() => resolve()))\n }\n },\n }\n return handle\n}\n\n/** Open a URL in the system browser (best effort). */\nexport async function openBrowser(url: string): Promise<boolean> {\n const { spawn } = await import('node:child_process')\n const platform = process.platform\n const command = platform === 'darwin' ? ['open', url]\n : platform === 'win32' ? ['cmd', '/c', 'start', '', url]\n : ['xdg-open', url]\n return new Promise<boolean>((resolve) => {\n const child = spawn(command[0]!, command.slice(1), { stdio: 'ignore' })\n child.on('error', () => resolve(false))\n child.on('spawn', () => resolve(true))\n })\n}\n","/**\n * dsh-agy CLI: login / status / import / verify / logout.\n * Standalone bin — the dsh launcher has no plugin subcommand slot, and the\n * remote paste-blob flow must run on machines without a harness.\n */\n\nimport { Command } from 'commander'\nimport { readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createInterface } from 'node:readline/promises'\nimport { stdin as input, stdout as output } from 'node:process'\nimport { authorizeAntigravity } from '../oauth/authorize.ts'\nimport { exchangeAntigravity } from '../oauth/exchange.ts'\nimport { encodeCredentialBlob } from '../oauth/blob.ts'\nimport { AGY_DEFAULT_REDIRECT_URI } from '../oauth/constants.ts'\nimport { createAesGcmCodec, deriveKey, loadMasterKey, resolveDshHome, resolveMasterKeyCodec } from '../store/keyring.ts'\nimport type { SecretCodec } from '../store/keyring.ts'\nimport { JsonAccountStore, maskProxyUrl } from '../store/accounts.ts'\nimport { AgySessionManager } from '../session.ts'\nimport { isAgyDisabled } from '../runtime/risk.ts'\nimport { startCallbackServer, openBrowser } from './callback-server.ts'\nimport { importManySources, upsertImportedAccount } from './import.ts'\nimport { accountFetch, isProxyReachable, normalizeProxyUrl } from '../proxy.ts'\n\n/** Package version, read from the shipped package.json — never hard-coded twice. */\nconst { version: PACKAGE_VERSION } = JSON.parse(\n readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),\n) as { version: string }\n\nfunction createStore(options: { readOnly?: boolean } = {}): JsonAccountStore {\n const dshHome = resolveDshHome()\n let codec: SecretCodec\n if (options.readOnly) {\n // Read-only commands must never create the master key or the credentials\n // document: they would fail for no reason on a read-only HOME and write\n // files nobody asked for anywhere else.\n const masterKey = loadMasterKey(dshHome)\n if (!masterKey) {\n throw new Error('No agy account store found — run `dsh-agy login` first.')\n }\n codec = createAesGcmCodec(deriveKey(masterKey))\n } else {\n codec = resolveMasterKeyCodec(dshHome).codec\n }\n return new JsonAccountStore({ file: `${dshHome}/agy-accounts.json`, codec })\n}\n\n/** Read-only store with a friendly error when no credentials exist yet. */\nfunction createReadOnlyStoreOrExit(): JsonAccountStore {\n try {\n return createStore({ readOnly: true })\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n}\n\nfunction resolveProxyOption(raw: string | undefined): string | undefined {\n if (raw === undefined) return undefined\n if (!raw.trim()) return ''\n try {\n return normalizeProxyUrl(raw)\n } catch (error) {\n console.error(`Invalid proxy URL: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n}\n\nasync function ask(question: string): Promise<string> {\n const rl = createInterface({ input, output })\n try {\n return (await rl.question(question)).trim()\n } finally {\n rl.close()\n }\n}\n\n/** Remote/SSH sessions cannot reach a local browser; auto-select headless paste. */\nfunction isRemoteSession(): boolean {\n return !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY)\n}\n\nasync function loginCommand(options: { headless: boolean; blob: boolean; port: number; project?: string; timeout?: string; proxy?: string }) {\n const proxyInput = resolveProxyOption(options.proxy)\n // proxy \"\" sentinel means clear; normalizeProxyUrl already handled\n const normalizedProxy = proxyInput === '' ? '' : proxyInput\n const store = createStore()\n const redirectUri = `http://localhost:${options.port}/oauth-callback`\n if (!options.headless && isRemoteSession()) {\n console.log('(SSH session detected — using headless paste flow; pass --headless explicitly to force)')\n options.headless = true\n }\n\n let callback: Awaited<ReturnType<typeof startCallbackServer>> | undefined\n if (!options.headless) {\n // Bind the loopback listener BEFORE the URL is shown: a local process\n // squatting on the fixed port could otherwise capture code+state (the\n // state carries the PKCE verifier) and steal the refresh token.\n callback = startCallbackServer({ port: options.port, timeoutMs: Number(options.timeout) || 300_000 })\n try {\n await callback.ready\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n }\n\n const { url, verifier } = await authorizeAntigravity(redirectUri, options.project ?? '')\n console.log(`\\nOpen this URL in a browser to authorize:\\n\\n ${url}\\n`)\n\n let code: string\n let state: string\n\n if (options.headless) {\n const pasted = await ask('After approving, paste the full redirected URL here: ')\n const parsed = new URL(pasted)\n code = parsed.searchParams.get('code') ?? ''\n state = parsed.searchParams.get('state') ?? ''\n if (!code || !state) {\n console.error('Error: pasted URL is missing code or state.')\n process.exit(1)\n }\n } else {\n const opened = await openBrowser(url)\n if (!opened) console.log('(Could not open a browser automatically — open the URL manually.)')\n let callbackResult: { code: string; state: string; url: string }\n try {\n callbackResult = await callback!.result\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n code = callbackResult.code\n state = callbackResult.state\n }\n\n // Bind the exchange to the verifier we issued: a state from any other login\n // (pasted from another session, or fabricated) must be rejected.\n // Route the exchange over the proxy being bound: it targets Google (never the\n // loopback callback, which is forced direct), and leaving it unproxied would\n // leak the host's real IP at exactly the moment the user asked to isolate it.\n const result = await exchangeAntigravity(code, state, redirectUri, verifier, {\n ...(normalizedProxy ? { proxyUrl: normalizedProxy } : {}),\n })\n if (result.type === 'failed') {\n console.error(`Login failed: ${result.error}`)\n process.exit(1)\n }\n\n if (options.blob) {\n if (normalizedProxy !== undefined) {\n console.log('(Note: --proxy is ignored with --blob; proxy is only stored with the account)')\n }\n const blob = encodeCredentialBlob('agy', {\n access_token: result.access,\n refresh_token: result.refresh.split('|')[0],\n expires_in: Math.max(0, Math.round((result.expires - Date.now()) / 1000)),\n })\n console.log(`\\nPaste this blob into the remote dashboard/CLI:\\n\\n${blob}\\n`)\n } else {\n const proxyForUpsert = normalizedProxy !== undefined ? normalizedProxy : undefined\n const { account, created } = await upsertImportedAccount(store, {\n accessToken: result.access,\n refreshToken: result.refresh.split('|')[0]!,\n tokenType: 'Bearer',\n expiresAt: new Date(result.expires).toISOString(),\n authMethod: 'oauth',\n email: result.email ?? null,\n projectId: result.projectId || null,\n clientId: result.clientId || null,\n }, { overwriteExisting: true, ...(proxyForUpsert !== undefined ? { proxy: proxyForUpsert } : {}) })\n const masked = maskProxyUrl(account.proxy)\n console.log(`${created ? 'Added' : 'Updated'} account: ${account.email ?? '(no email)'} (project: ${result.projectId || 'default'})${masked ? ` proxy: ${masked}` : ''}`)\n }\n\n await callback?.close()\n}\n\nasync function statusCommand() {\n const store = createReadOnlyStoreOrExit()\n const storage = await store.load()\n if (storage.accounts.length === 0) {\n console.log('No agy accounts. Run `dsh-agy login` first.')\n return\n }\n console.log(`\\n${storage.accounts.length} account(s), active index ${storage.activeIndex}:\\n`)\n const sessions = new AgySessionManager({ store })\n for (const [index, account] of storage.accounts.entries()) {\n const marker = index === storage.activeIndex ? '★' : ' '\n const state = account.enabled === false ? 'disabled'\n : account.verificationRequired ? 'verification-required'\n : account.coolingDownUntil && account.coolingDownUntil > Date.now() ? 'cooling'\n : 'active'\n const maskedProxy = maskProxyUrl(account.proxy)\n console.log(` ${marker} [${index}] ${account.email ?? '(no email)'} — ${state}${account.projectId ? ` (project: ${account.projectId})` : ''}${maskedProxy ? ` proxy: ${maskedProxy}` : ''}`)\n\n // Best-effort quota summary via fetchAvailableModels (fresh access token).\n const session = await sessions.getSession().catch(() => undefined)\n if (session && session.index === index) {\n try {\n const { fetchAvailableModels } = await import('../adapter/models.ts')\n // Account-scoped: route through the account's proxy so a status check\n // never reveals the host's real IP for a proxied account.\n const discovered = await fetchAvailableModels(\n session.auth.access,\n session.account.projectId,\n accountFetch({ proxyUrl: session.account.proxy }),\n )\n const entries = Object.entries(discovered.models ?? {})\n if (entries.length > 0) {\n const withQuota = entries\n .map(([id, entry]) => ({ id, ...entry.quotaInfo }))\n .filter((e) => typeof e.remainingFraction === 'number')\n .sort((a, b) => (a.remainingFraction ?? 0) - (b.remainingFraction ?? 0))\n if (withQuota.length > 0) {\n const lowest = withQuota[0]!\n console.log(` models: ${entries.length}, lowest quota: ${Math.round((lowest.remainingFraction ?? 0) * 100)}% (${lowest.id})${lowest.resetTime ? `, resets ${new Date(lowest.resetTime).toISOString()}` : ''}`)\n } else {\n console.log(` models: ${entries.length} (no per-model quota reported)`)\n }\n }\n } catch (error) {\n console.log(` quota: unavailable (${error instanceof Error ? error.message : String(error)})`)\n }\n }\n }\n}\n\nasync function importCommand(options: { blob: boolean; files?: string[]; email?: string; overwrite: boolean; proxy?: string }) {\n const store = createStore()\n const proxyForUpsert = resolveProxyOption(options.proxy)\n let items: Array<{ source: unknown; kind: 'json' | 'blob' }>\n if (options.files && options.files.length > 0) {\n items = options.files.map((file) => {\n const raw = readFileSync(file, 'utf8')\n return { source: options.blob ? raw : (JSON.parse(raw) as unknown), kind: options.blob ? 'blob' : 'json' }\n })\n } else {\n const pasted = await ask('Paste the agy token JSON (or blob with --blob): ')\n items = [{ source: pasted, kind: options.blob ? 'blob' : 'json' }]\n }\n const importOpts: { email?: string; overwriteExisting?: boolean; proxy?: string } = {\n email: options.email,\n overwriteExisting: options.overwrite,\n }\n if (proxyForUpsert !== undefined) importOpts.proxy = proxyForUpsert\n const result = await importManySources(items, store, importOpts)\n console.log(`Imported ${result.imported}, replaced ${result.replaced}${result.errors.length > 0 ? `, ${result.errors.length} failed` : ''}`)\n for (const error of result.errors) console.log(` ! ${error}`)\n}\n\nasync function exportCommand(options: { index?: string; out?: string }) {\n const store = createReadOnlyStoreOrExit()\n const sessions = new AgySessionManager({ store })\n const storage = await store.load()\n const indices = options.index !== undefined\n ? [Number(options.index)]\n : storage.accounts.map((_, i) => i)\n\n let exported = 0\n for (const index of indices) {\n const account = storage.accounts[index]\n if (!account) {\n console.log(`[${index}] not found`)\n continue\n }\n const result = await sessions.exportBlob(index)\n if (!result.blob) {\n console.log(`[${index}] ${account.email ?? ''} — FAILED: ${result.error}`)\n continue\n }\n if (options.out) {\n const file = join(options.out, `dsh-agy-${index}.blob`)\n writeFileSync(file, result.blob + '\\n')\n console.log(`[${index}] ${account.email ?? ''} — wrote ${file}`)\n } else {\n console.log(result.blob)\n }\n exported++\n }\n if (!options.out) console.log(`\\n${exported} blob(s) exported — one line each, paste into a remote import`)\n}\n\nasync function verifyCommand(options: { index?: string }) {\n const store = createReadOnlyStoreOrExit()\n const sessions = new AgySessionManager({ store })\n const storage = await store.load()\n const indices = options.index !== undefined\n ? [Number(options.index)]\n : storage.accounts.map((_, i) => i)\n\n for (const index of indices) {\n const account = storage.accounts[index]\n if (!account) {\n console.log(`[${index}] not found`)\n continue\n }\n const result = await sessions.verifyAccount(index)\n if (result.ok) {\n console.log(`[${index}] ${account.email ?? ''} — OK${result.email && result.email !== account.email ? ` (userinfo: ${result.email})` : ''}`)\n } else {\n console.log(`[${index}] ${account.email ?? ''} — FAILED: ${result.error}`)\n }\n }\n}\n\nasync function healthCommand(options: { interval?: string; index?: string[] }) {\n const store = createReadOnlyStoreOrExit()\n const sessions = new AgySessionManager({\n store,\n onHealthReport: (results) => {\n for (const result of results) {\n if (result.ok) console.log(`[${result.index}] ${result.email ?? ''} — OK`)\n else console.log(`[${result.index}] ${result.email ?? ''} — FAILED: ${result.error}`)\n }\n },\n })\n const indices = options.index?.map((value) => Number(value))\n\n if (options.interval) {\n const intervalMs = Number(options.interval)\n if (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n console.error('Error: --interval must be a positive number of milliseconds.')\n process.exit(1)\n }\n console.log(`Health probe every ${intervalMs}ms (Ctrl+C to stop).`)\n sessions.startHealthProbe(intervalMs, { unref: false })\n await new Promise<void>(() => {}) // keep the loop alive\n return\n }\n\n const results = await sessions.checkAccounts(indices)\n if (results.length === 0) console.log('No enabled accounts — run `dsh-agy login` first.')\n}\n\nasync function logoutCommand(options: { index?: string; email?: string }) {\n const store = createReadOnlyStoreOrExit()\n await store.mutate((storage) => {\n const index = options.index !== undefined\n ? Number(options.index)\n : options.email\n ? storage.accounts.findIndex((a) => a.email?.toLowerCase() === options.email!.toLowerCase())\n : storage.activeIndex\n if (index < 0 || index >= storage.accounts.length) {\n throw new Error(`account not found (index ${index})`)\n }\n const [removed] = storage.accounts.splice(index, 1)\n if (storage.activeIndex >= storage.accounts.length) storage.activeIndex = 0\n console.log(`Removed account: ${removed?.email ?? `[${index}]`}`)\n })\n}\n\nasync function proxySetCommand(options: { index: string; proxy: string }) {\n const idx = Number(options.index)\n if (!Number.isInteger(idx) || idx < 0) {\n console.error('Invalid --index (must be >= 0)')\n process.exit(1)\n }\n const raw = options.proxy ?? ''\n if (!raw.trim()) {\n console.error('Missing --proxy value')\n process.exit(1)\n }\n let normalized: string\n try {\n normalized = normalizeProxyUrl(raw)\n } catch (error) {\n console.error(`Invalid proxy URL: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n const store = createStore()\n await store.mutate((storage) => {\n const account = storage.accounts[idx]\n if (!account) throw new Error(`account not found (index ${idx})`)\n account.proxy = normalized\n })\n console.log(`[${idx}] proxy set to ${maskProxyUrl(normalized)}`)\n}\n\nasync function proxyClearCommand(options: { index: string }) {\n const idx = Number(options.index)\n if (!Number.isInteger(idx) || idx < 0) {\n console.error('Invalid --index (must be >= 0)')\n process.exit(1)\n }\n const store = createStore()\n await store.mutate((storage) => {\n const account = storage.accounts[idx]\n if (!account) throw new Error(`account not found (index ${idx})`)\n delete (account as { proxy?: string }).proxy\n })\n console.log(`[${idx}] proxy cleared`)\n}\n\nasync function proxyTestCommand(options: { index: string }) {\n const idx = Number(options.index)\n if (!Number.isInteger(idx) || idx < 0) {\n console.error('Invalid --index (must be >= 0)')\n process.exit(1)\n }\n const store = createReadOnlyStoreOrExit()\n const storage = await store.load()\n const account = storage.accounts[idx]\n if (!account) {\n console.error(`account not found (index ${idx})`)\n process.exit(1)\n }\n if (!account.proxy) {\n console.log(`[${idx}] no proxy configured`)\n return\n }\n const masked = maskProxyUrl(account.proxy)\n const reachable = await isProxyReachable(account.proxy)\n if (reachable) console.log(`[${idx}] ${masked} — ok`)\n else console.log(`[${idx}] ${masked} — proxy_unreachable`)\n}\n\nasync function proxyListCommand() {\n const store = createReadOnlyStoreOrExit()\n const storage = await store.load()\n if (storage.accounts.length === 0) {\n console.log('No agy accounts.')\n return\n }\n for (const [index, account] of storage.accounts.entries()) {\n const masked = maskProxyUrl(account.proxy)\n console.log(` [${index}] ${account.email ?? '(no email)'} — ${masked ?? '(no proxy)'}`)\n }\n}\n\nexport function createProgram(): Command {\n const program = new Command()\n program\n .name('dsh-agy')\n .description('Google Antigravity (agy) account management for DeepSeek Harness')\n .version(PACKAGE_VERSION)\n\n program.hook('preAction', () => {\n if (isAgyDisabled()) {\n console.error('dsh-agy is disabled (DSH_AGY_DISABLE=1).')\n process.exit(1)\n }\n })\n\n program\n .command('login')\n .description('OAuth login (browser, or headless paste)')\n .option('--headless', 'print the URL and wait for a pasted redirect URL', false)\n .option('--blob', 'print a paste-credential blob instead of storing the account', false)\n .option('--port <n>', 'loopback callback port', '51121')\n .option('--project <id>', 'bind the login to a specific project')\n .option('--timeout <ms>', 'callback timeout', '300000')\n .option('--proxy <url>', 'per-account proxy URL (http/https/socks5); empty to clear')\n .action(async (options) => {\n await loginCommand({ ...options, timeout: options.timeout })\n })\n\n program\n .command('status')\n .description('List accounts and their health')\n .action(async () => {\n await statusCommand()\n })\n\n program\n .command('import')\n .description('Import agy token files or paste credentials')\n .argument('[files...]', 'paths to agy auth.json token files (multiple allowed)')\n .option('--blob', 'the pasted value is a credential blob', false)\n .option('--email <email>', 'account email (skips userinfo verification)')\n .option('--overwrite', 'replace an existing account with the same email', false)\n .option('--proxy <url>', 'per-account proxy URL (http/https/socks5); empty to clear')\n .action(async (files: string[] | undefined, options: { blob: boolean; email?: string; overwrite: boolean; proxy?: string }) => {\n await importCommand({ ...options, files })\n })\n\n program\n .command('export')\n .description('Export account credentials as paste blobs')\n .option('--index <n>', 'export one account by index; default all')\n .option('--out <dir>', 'write one dsh-agy-<index>.blob file per account into this directory (default: print to stdout)')\n .action(async (options: { index?: string; out?: string }) => {\n await exportCommand(options)\n })\n\n program\n .command('verify')\n .description('Verify account credentials (refresh + userinfo)')\n .option('--index <n>', 'verify one account by index; default all')\n .action(async (options: { index?: string }) => {\n await verifyCommand(options)\n })\n\n program\n .command('health')\n .description('Check every enabled account (refresh + userinfo), optionally on an interval')\n .option('--interval <ms>', 'repeat on an interval instead of once')\n .option('--index <n...>', 'check only these accounts')\n .action(async (options: { interval?: string; index?: string[] }) => {\n await healthCommand(options)\n })\n\n program\n .command('logout')\n .description('Remove an account')\n .option('--index <n>', 'account index (default: active)')\n .option('--email <email>', 'account email')\n .action(async (options: { index?: string; email?: string }) => {\n await logoutCommand(options)\n })\n\n const proxy = program.command('proxy').description('Manage per-account proxies')\n proxy\n .command('set')\n .description('Set proxy for an account')\n .requiredOption('--index <n>', 'account index')\n .requiredOption('--proxy <url>', 'proxy URL (http/https/socks5)')\n .action(async (options: { index: string; proxy: string }) => {\n await proxySetCommand(options)\n })\n proxy\n .command('clear')\n .description('Clear proxy for an account')\n .requiredOption('--index <n>', 'account index')\n .action(async (options: { index: string }) => {\n await proxyClearCommand(options)\n })\n proxy\n .command('test')\n .description('Test proxy reachability for an account (TCP 2s fast-fail)')\n .requiredOption('--index <n>', 'account index')\n .action(async (options: { index: string }) => {\n await proxyTestCommand(options)\n })\n proxy\n .command('list')\n .description('List accounts with masked proxies')\n .action(async () => {\n await proxyListCommand()\n })\n\n return program\n}\n\nexport { AGY_DEFAULT_REDIRECT_URI }\n"],"mappings":";;;;;;;;;;;;;;AAkBA,MAAM,eAAe;;;;;;AAOrB,SAAgB,oBAAoB,UAAiD,CAAC,GAAmB;CACvG,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,YAAY,QAAQ,aAAa;CAEvC,IAAI;CACJ,IAAI;CACJ,MAAM,SAAS,IAAI,SAAyB,SAAS,WAAW;EAC9D,gBAAgB;EAChB,eAAe;CACjB,CAAC;CAED,IAAI;CACJ,IAAI;CACJ,MAAM,QAAQ,IAAI,SAAe,SAAS,WAAW;EACnD,eAAe;EACf,cAAc;CAChB,CAAC;CAED,MAAM,SAAiB,cAAc,KAAK,QAAQ;EAChD,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB;EACtD,IAAI,IAAI,aAAa,mBAAmB;GACtC,MAAM,OAAO,IAAI,aAAa,IAAI,MAAM;GACxC,MAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;GAC1C,IAAI,CAAC,QAAQ,CAAC,OAAO;IACnB,IAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;IAClD,IAAI,IAAI,gCAAgC;IACxC;GACF;GACA,IAAI,UAAU,KAAK;IAAE,gBAAgB;IAAa,YAAY;GAAQ,CAAC;GACvE,IAAI,IAAI,YAAY;GACpB,cAAc;IAAE;IAAM;IAAO,KAAK,IAAI,SAAS;GAAE,CAAC;GAElD,iBAAiB,OAAO,MAAM,GAAG,IAAI;GACrC;EACF;EACA,IAAI,UAAU,GAAG;EACjB,IAAI,IAAI,WAAW;CACrB,CAAC;CAED,OAAO,GAAG,UAAU,UAAiC;EACnD,IAAI,MAAM,SAAS,cACjB,6BAAa,IAAI,MAAM,QAAQ,KAAK,kDAAkD,CAAC;OAEvF,aAAa,KAAK;EAEpB,YAAY,KAAK;CACnB,CAAC;CAED,OAAO,OAAO,MAAM,mBAAmB;EACrC,aAAa;CACf,CAAC;CAED,MAAM,UAAU,iBAAiB;EAC/B,6BAAa,IAAI,MAAM,kDAAkD,CAAC;EAC1E,OAAO,MAAM;CACf,GAAG,SAAS;CAeZ,OAAO;EAZL;EACA;EACA,MAAM,QAAQ;GACZ,aAAa,OAAO;GACpB,IAAI,OAAO,WAAW;IAGpB,OAAO,oBAAoB;IAC3B,MAAM,IAAI,SAAe,YAAY,OAAO,YAAY,QAAQ,CAAC,CAAC;GACpE;EACF;CAEU;AACd;;AAGA,eAAsB,YAAY,KAA+B;CAC/D,MAAM,EAAE,UAAU,MAAM,OAAO;CAC/B,MAAM,WAAW,QAAQ;CACzB,MAAM,UAAU,aAAa,WAAW,CAAC,QAAQ,GAAG,IAChD,aAAa,UAAU;EAAC;EAAO;EAAM;EAAS;EAAI;CAAG,IACrD,CAAC,YAAY,GAAG;CACpB,OAAO,IAAI,SAAkB,YAAY;EACvC,MAAM,QAAQ,MAAM,QAAQ,IAAK,QAAQ,MAAM,CAAC,GAAG,EAAE,OAAO,SAAS,CAAC;EACtE,MAAM,GAAG,eAAe,QAAQ,KAAK,CAAC;EACtC,MAAM,GAAG,eAAe,QAAQ,IAAI,CAAC;CACvC,CAAC;AACH;;;;;;;;;ACrFA,MAAM,EAAE,SAAS,oBAAoB,KAAK,MACxC,aAAa,IAAI,IAAI,sBAAsB,YAAY,GAAG,GAAG,MAAM,CACrE;AAEA,SAAS,YAAY,UAAkC,CAAC,GAAqB;CAC3E,MAAM,UAAU,eAAe;CAC/B,IAAI;CACJ,IAAI,QAAQ,UAAU;EAIpB,MAAM,YAAY,cAAc,OAAO;EACvC,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,yDAAyD;EAE3E,QAAQ,kBAAkB,UAAU,SAAS,CAAC;CAChD,OACE,QAAQ,sBAAsB,OAAO,CAAC,CAAC;CAEzC,OAAO,IAAI,iBAAiB;EAAE,MAAM,GAAG,QAAQ;EAAqB;CAAM,CAAC;AAC7E;;AAGA,SAAS,4BAA8C;CACrD,IAAI;EACF,OAAO,YAAY,EAAE,UAAU,KAAK,CAAC;CACvC,SAAS,OAAO;EACd,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EACpE,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,SAAS,mBAAmB,KAA6C;CACvE,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAA;CAC9B,IAAI,CAAC,IAAI,KAAK,GAAG,OAAO;CACxB,IAAI;EACF,OAAO,kBAAkB,GAAG;CAC9B,SAAS,OAAO;EACd,QAAQ,MAAM,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;EAC5F,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,eAAe,IAAI,UAAmC;CACpD,MAAM,KAAK,gBAAgB;EAAE,OAAA;EAAO,QAAA;CAAO,CAAC;CAC5C,IAAI;EACF,QAAQ,MAAM,GAAG,SAAS,QAAQ,EAAA,CAAG,KAAK;CAC5C,UAAU;EACR,GAAG,MAAM;CACX;AACF;;AAGA,SAAS,kBAA2B;CAClC,OAAO,CAAC,EAAE,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,cAAc,QAAQ,IAAI;AAChF;AAEA,eAAe,aAAa,SAAiH;CAC3I,MAAM,aAAa,mBAAmB,QAAQ,KAAK;CAEnD,MAAM,kBAAkB,eAAe,KAAK,KAAK;CACjD,MAAM,QAAQ,YAAY;CAC1B,MAAM,cAAc,oBAAoB,QAAQ,KAAK;CACrD,IAAI,CAAC,QAAQ,YAAY,gBAAgB,GAAG;EAC1C,QAAQ,IAAI,yFAAyF;EACrG,QAAQ,WAAW;CACrB;CAEA,IAAI;CACJ,IAAI,CAAC,QAAQ,UAAU;EAIrB,WAAW,oBAAoB;GAAE,MAAM,QAAQ;GAAM,WAAW,OAAO,QAAQ,OAAO,KAAK;EAAQ,CAAC;EACpG,IAAI;GACF,MAAM,SAAS;EACjB,SAAS,OAAO;GACd,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;GACpE,QAAQ,KAAK,CAAC;EAChB;CACF;CAEA,MAAM,EAAE,KAAK,aAAa,MAAM,qBAAqB,aAAa,QAAQ,WAAW,EAAE;CACvF,QAAQ,IAAI,mDAAmD,IAAI,GAAG;CAEtE,IAAI;CACJ,IAAI;CAEJ,IAAI,QAAQ,UAAU;EACpB,MAAM,SAAS,MAAM,IAAI,uDAAuD;EAChF,MAAM,SAAS,IAAI,IAAI,MAAM;EAC7B,OAAO,OAAO,aAAa,IAAI,MAAM,KAAK;EAC1C,QAAQ,OAAO,aAAa,IAAI,OAAO,KAAK;EAC5C,IAAI,CAAC,QAAQ,CAAC,OAAO;GACnB,QAAQ,MAAM,6CAA6C;GAC3D,QAAQ,KAAK,CAAC;EAChB;CACF,OAAO;EAEL,IAAI,CAAC,MADgB,YAAY,GAAG,GACvB,QAAQ,IAAI,mEAAmE;EAC5F,IAAI;EACJ,IAAI;GACF,iBAAiB,MAAM,SAAU;EACnC,SAAS,OAAO;GACd,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;GACpE,QAAQ,KAAK,CAAC;EAChB;EACA,OAAO,eAAe;EACtB,QAAQ,eAAe;CACzB;CAOA,MAAM,SAAS,MAAM,oBAAoB,MAAM,OAAO,aAAa,UAAU,EAC3E,GAAI,kBAAkB,EAAE,UAAU,gBAAgB,IAAI,CAAC,EACzD,CAAC;CACD,IAAI,OAAO,SAAS,UAAU;EAC5B,QAAQ,MAAM,iBAAiB,OAAO,OAAO;EAC7C,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,QAAQ,MAAM;EAChB,IAAI,oBAAoB,KAAA,GACtB,QAAQ,IAAI,+EAA+E;EAE7F,MAAM,OAAO,qBAAqB,OAAO;GACvC,cAAc,OAAO;GACrB,eAAe,OAAO,QAAQ,MAAM,GAAG,CAAC,CAAC;GACzC,YAAY,KAAK,IAAI,GAAG,KAAK,OAAO,OAAO,UAAU,KAAK,IAAI,KAAK,GAAI,CAAC;EAC1E,CAAC;EACD,QAAQ,IAAI,uDAAuD,KAAK,GAAG;CAC7E,OAAO;EACL,MAAM,iBAAiB,oBAAoB,KAAA,IAAY,kBAAkB,KAAA;EACzE,MAAM,EAAE,SAAS,YAAY,MAAM,sBAAsB,OAAO;GAC9D,aAAa,OAAO;GACpB,cAAc,OAAO,QAAQ,MAAM,GAAG,CAAC,CAAC;GACxC,WAAW;GACX,WAAW,IAAI,KAAK,OAAO,OAAO,CAAC,CAAC,YAAY;GAChD,YAAY;GACZ,OAAO,OAAO,SAAS;GACvB,WAAW,OAAO,aAAa;GAC/B,UAAU,OAAO,YAAY;EAC/B,GAAG;GAAE,mBAAmB;GAAM,GAAI,mBAAmB,KAAA,IAAY,EAAE,OAAO,eAAe,IAAI,CAAC;EAAG,CAAC;EAClG,MAAM,SAAS,aAAa,QAAQ,KAAK;EACzC,QAAQ,IAAI,GAAG,UAAU,UAAU,UAAU,YAAY,QAAQ,SAAS,aAAa,aAAa,OAAO,aAAa,UAAU,GAAG,SAAS,WAAW,WAAW,IAAI;CAC1K;CAEA,MAAM,UAAU,MAAM;AACxB;AAEA,eAAe,gBAAgB;CAC7B,MAAM,QAAQ,0BAA0B;CACxC,MAAM,UAAU,MAAM,MAAM,KAAK;CACjC,IAAI,QAAQ,SAAS,WAAW,GAAG;EACjC,QAAQ,IAAI,6CAA6C;EACzD;CACF;CACA,QAAQ,IAAI,KAAK,QAAQ,SAAS,OAAO,4BAA4B,QAAQ,YAAY,IAAI;CAC7F,MAAM,WAAW,IAAI,kBAAkB,EAAE,MAAM,CAAC;CAChD,KAAK,MAAM,CAAC,OAAO,YAAY,QAAQ,SAAS,QAAQ,GAAG;EACzD,MAAM,SAAS,UAAU,QAAQ,cAAc,MAAM;EACrD,MAAM,QAAQ,QAAQ,YAAY,QAAQ,aACtC,QAAQ,uBAAuB,0BAC/B,QAAQ,oBAAoB,QAAQ,mBAAmB,KAAK,IAAI,IAAI,YACpE;EACJ,MAAM,cAAc,aAAa,QAAQ,KAAK;EAC9C,QAAQ,IAAI,IAAI,OAAO,IAAI,MAAM,IAAI,QAAQ,SAAS,aAAa,KAAK,QAAQ,QAAQ,YAAY,cAAc,QAAQ,UAAU,KAAK,KAAK,cAAc,WAAW,gBAAgB,IAAI;EAG3L,MAAM,UAAU,MAAM,SAAS,WAAW,CAAC,CAAC,YAAY,KAAA,CAAS;EACjE,IAAI,WAAW,QAAQ,UAAU,OAC/B,IAAI;GACF,MAAM,EAAE,yBAAyB,MAAM,OAAO,yBAAuB,CAAA,MAAA,MAAA,EAAA,CAAA;GAGrE,MAAM,aAAa,MAAM,qBACvB,QAAQ,KAAK,QACb,QAAQ,QAAQ,WAChB,aAAa,EAAE,UAAU,QAAQ,QAAQ,MAAM,CAAC,CAClD;GACA,MAAM,UAAU,OAAO,QAAQ,WAAW,UAAU,CAAC,CAAC;GACtD,IAAI,QAAQ,SAAS,GAAG;IACtB,MAAM,YAAY,QACf,KAAK,CAAC,IAAI,YAAY;KAAE;KAAI,GAAG,MAAM;IAAU,EAAE,CAAC,CAClD,QAAQ,MAAM,OAAO,EAAE,sBAAsB,QAAQ,CAAC,CACtD,MAAM,GAAG,OAAO,EAAE,qBAAqB,MAAM,EAAE,qBAAqB,EAAE;IACzE,IAAI,UAAU,SAAS,GAAG;KACxB,MAAM,SAAS,UAAU;KACzB,QAAQ,IAAI,kBAAkB,QAAQ,OAAO,kBAAkB,KAAK,OAAO,OAAO,qBAAqB,KAAK,GAAG,EAAE,KAAK,OAAO,GAAG,GAAG,OAAO,YAAY,YAAY,IAAI,KAAK,OAAO,SAAS,CAAC,CAAC,YAAY,MAAM,IAAI;IACrN,OACE,QAAQ,IAAI,kBAAkB,QAAQ,OAAO,+BAA+B;GAEhF;EACF,SAAS,OAAO;GACd,QAAQ,IAAI,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,EAAE;EACrG;CAEJ;AACF;AAEA,eAAe,cAAc,SAAkG;CAC7H,MAAM,QAAQ,YAAY;CAC1B,MAAM,iBAAiB,mBAAmB,QAAQ,KAAK;CACvD,IAAI;CACJ,IAAI,QAAQ,SAAS,QAAQ,MAAM,SAAS,GAC1C,QAAQ,QAAQ,MAAM,KAAK,SAAS;EAClC,MAAM,MAAM,aAAa,MAAM,MAAM;EACrC,OAAO;GAAE,QAAQ,QAAQ,OAAO,MAAO,KAAK,MAAM,GAAG;GAAe,MAAM,QAAQ,OAAO,SAAS;EAAO;CAC3G,CAAC;MAGD,QAAQ,CAAC;EAAE,QAAQ,MADE,IAAI,kDAAkD;EAChD,MAAM,QAAQ,OAAO,SAAS;CAAO,CAAC;CAEnE,MAAM,aAA8E;EAClF,OAAO,QAAQ;EACf,mBAAmB,QAAQ;CAC7B;CACA,IAAI,mBAAmB,KAAA,GAAW,WAAW,QAAQ;CACrD,MAAM,SAAS,MAAM,kBAAkB,OAAO,OAAO,UAAU;CAC/D,QAAQ,IAAI,YAAY,OAAO,SAAS,aAAa,OAAO,WAAW,OAAO,OAAO,SAAS,IAAI,KAAK,OAAO,OAAO,OAAO,WAAW,IAAI;CAC3I,KAAK,MAAM,SAAS,OAAO,QAAQ,QAAQ,IAAI,OAAO,OAAO;AAC/D;AAEA,eAAe,cAAc,SAA2C;CACtE,MAAM,QAAQ,0BAA0B;CACxC,MAAM,WAAW,IAAI,kBAAkB,EAAE,MAAM,CAAC;CAChD,MAAM,UAAU,MAAM,MAAM,KAAK;CACjC,MAAM,UAAU,QAAQ,UAAU,KAAA,IAC9B,CAAC,OAAO,QAAQ,KAAK,CAAC,IACtB,QAAQ,SAAS,KAAK,GAAG,MAAM,CAAC;CAEpC,IAAI,WAAW;CACf,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,UAAU,QAAQ,SAAS;EACjC,IAAI,CAAC,SAAS;GACZ,QAAQ,IAAI,IAAI,MAAM,YAAY;GAClC;EACF;EACA,MAAM,SAAS,MAAM,SAAS,WAAW,KAAK;EAC9C,IAAI,CAAC,OAAO,MAAM;GAChB,QAAQ,IAAI,IAAI,MAAM,IAAI,QAAQ,SAAS,GAAG,aAAa,OAAO,OAAO;GACzE;EACF;EACA,IAAI,QAAQ,KAAK;GACf,MAAM,OAAO,KAAK,QAAQ,KAAK,WAAW,MAAM,MAAM;GACtD,cAAc,MAAM,OAAO,OAAO,IAAI;GACtC,QAAQ,IAAI,IAAI,MAAM,IAAI,QAAQ,SAAS,GAAG,WAAW,MAAM;EACjE,OACE,QAAQ,IAAI,OAAO,IAAI;EAEzB;CACF;CACA,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,SAAS,8DAA8D;AAC5G;AAEA,eAAe,cAAc,SAA6B;CACxD,MAAM,QAAQ,0BAA0B;CACxC,MAAM,WAAW,IAAI,kBAAkB,EAAE,MAAM,CAAC;CAChD,MAAM,UAAU,MAAM,MAAM,KAAK;CACjC,MAAM,UAAU,QAAQ,UAAU,KAAA,IAC9B,CAAC,OAAO,QAAQ,KAAK,CAAC,IACtB,QAAQ,SAAS,KAAK,GAAG,MAAM,CAAC;CAEpC,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,UAAU,QAAQ,SAAS;EACjC,IAAI,CAAC,SAAS;GACZ,QAAQ,IAAI,IAAI,MAAM,YAAY;GAClC;EACF;EACA,MAAM,SAAS,MAAM,SAAS,cAAc,KAAK;EACjD,IAAI,OAAO,IACT,QAAQ,IAAI,IAAI,MAAM,IAAI,QAAQ,SAAS,GAAG,OAAO,OAAO,SAAS,OAAO,UAAU,QAAQ,QAAQ,eAAe,OAAO,MAAM,KAAK,IAAI;OAE3I,QAAQ,IAAI,IAAI,MAAM,IAAI,QAAQ,SAAS,GAAG,aAAa,OAAO,OAAO;CAE7E;AACF;AAEA,eAAe,cAAc,SAAkD;CAC7E,MAAM,QAAQ,0BAA0B;CACxC,MAAM,WAAW,IAAI,kBAAkB;EACrC;EACA,iBAAiB,YAAY;GAC3B,KAAK,MAAM,UAAU,SACnB,IAAI,OAAO,IAAI,QAAQ,IAAI,IAAI,OAAO,MAAM,IAAI,OAAO,SAAS,GAAG,MAAM;QACpE,QAAQ,IAAI,IAAI,OAAO,MAAM,IAAI,OAAO,SAAS,GAAG,aAAa,OAAO,OAAO;EAExF;CACF,CAAC;CACD,MAAM,UAAU,QAAQ,OAAO,KAAK,UAAU,OAAO,KAAK,CAAC;CAE3D,IAAI,QAAQ,UAAU;EACpB,MAAM,aAAa,OAAO,QAAQ,QAAQ;EAC1C,IAAI,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,GAAG;GACnD,QAAQ,MAAM,8DAA8D;GAC5E,QAAQ,KAAK,CAAC;EAChB;EACA,QAAQ,IAAI,sBAAsB,WAAW,qBAAqB;EAClE,SAAS,iBAAiB,YAAY,EAAE,OAAO,MAAM,CAAC;EACtD,MAAM,IAAI,cAAoB,CAAC,CAAC;EAChC;CACF;CAGA,KAAI,MADkB,SAAS,cAAc,OAAO,EAAA,CACxC,WAAW,GAAG,QAAQ,IAAI,kDAAkD;AAC1F;AAEA,eAAe,cAAc,SAA6C;CAExE,MADc,0BACJ,CAAC,CAAC,QAAQ,YAAY;EAC9B,MAAM,QAAQ,QAAQ,UAAU,KAAA,IAC5B,OAAO,QAAQ,KAAK,IACpB,QAAQ,QACN,QAAQ,SAAS,WAAW,MAAM,EAAE,OAAO,YAAY,MAAM,QAAQ,MAAO,YAAY,CAAC,IACzF,QAAQ;EACd,IAAI,QAAQ,KAAK,SAAS,QAAQ,SAAS,QACzC,MAAM,IAAI,MAAM,4BAA4B,MAAM,EAAE;EAEtD,MAAM,CAAC,WAAW,QAAQ,SAAS,OAAO,OAAO,CAAC;EAClD,IAAI,QAAQ,eAAe,QAAQ,SAAS,QAAQ,QAAQ,cAAc;EAC1E,QAAQ,IAAI,oBAAoB,SAAS,SAAS,IAAI,MAAM,IAAI;CAClE,CAAC;AACH;AAEA,eAAe,gBAAgB,SAA2C;CACxE,MAAM,MAAM,OAAO,QAAQ,KAAK;CAChC,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,GAAG;EACrC,QAAQ,MAAM,gCAAgC;EAC9C,QAAQ,KAAK,CAAC;CAChB;CACA,MAAM,MAAM,QAAQ,SAAS;CAC7B,IAAI,CAAC,IAAI,KAAK,GAAG;EACf,QAAQ,MAAM,uBAAuB;EACrC,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI;CACJ,IAAI;EACF,aAAa,kBAAkB,GAAG;CACpC,SAAS,OAAO;EACd,QAAQ,MAAM,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;EAC5F,QAAQ,KAAK,CAAC;CAChB;CAEA,MADc,YACJ,CAAC,CAAC,QAAQ,YAAY;EAC9B,MAAM,UAAU,QAAQ,SAAS;EACjC,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,4BAA4B,IAAI,EAAE;EAChE,QAAQ,QAAQ;CAClB,CAAC;CACD,QAAQ,IAAI,IAAI,IAAI,iBAAiB,aAAa,UAAU,GAAG;AACjE;AAEA,eAAe,kBAAkB,SAA4B;CAC3D,MAAM,MAAM,OAAO,QAAQ,KAAK;CAChC,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,GAAG;EACrC,QAAQ,MAAM,gCAAgC;EAC9C,QAAQ,KAAK,CAAC;CAChB;CAEA,MADc,YACJ,CAAC,CAAC,QAAQ,YAAY;EAC9B,MAAM,UAAU,QAAQ,SAAS;EACjC,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,4BAA4B,IAAI,EAAE;EAChE,OAAQ,QAA+B;CACzC,CAAC;CACD,QAAQ,IAAI,IAAI,IAAI,gBAAgB;AACtC;AAEA,eAAe,iBAAiB,SAA4B;CAC1D,MAAM,MAAM,OAAO,QAAQ,KAAK;CAChC,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,GAAG;EACrC,QAAQ,MAAM,gCAAgC;EAC9C,QAAQ,KAAK,CAAC;CAChB;CAGA,MAAM,WAAU,MAFF,0BACY,CAAC,CAAC,KAAK,EAAA,CACT,SAAS;CACjC,IAAI,CAAC,SAAS;EACZ,QAAQ,MAAM,4BAA4B,IAAI,EAAE;EAChD,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI,CAAC,QAAQ,OAAO;EAClB,QAAQ,IAAI,IAAI,IAAI,sBAAsB;EAC1C;CACF;CACA,MAAM,SAAS,aAAa,QAAQ,KAAK;CAEzC,IAAI,MADoB,iBAAiB,QAAQ,KAAK,GACvC,QAAQ,IAAI,IAAI,IAAI,IAAI,OAAO,MAAM;MAC/C,QAAQ,IAAI,IAAI,IAAI,IAAI,OAAO,qBAAqB;AAC3D;AAEA,eAAe,mBAAmB;CAEhC,MAAM,UAAU,MADF,0BACY,CAAC,CAAC,KAAK;CACjC,IAAI,QAAQ,SAAS,WAAW,GAAG;EACjC,QAAQ,IAAI,kBAAkB;EAC9B;CACF;CACA,KAAK,MAAM,CAAC,OAAO,YAAY,QAAQ,SAAS,QAAQ,GAAG;EACzD,MAAM,SAAS,aAAa,QAAQ,KAAK;EACzC,QAAQ,IAAI,KAAK,MAAM,IAAI,QAAQ,SAAS,aAAa,KAAK,UAAU,cAAc;CACxF;AACF;AAEA,SAAgB,gBAAyB;CACvC,MAAM,UAAU,IAAI,QAAQ;CAC5B,QACG,KAAK,SAAS,CAAC,CACf,YAAY,kEAAkE,CAAC,CAC/E,QAAQ,eAAe;CAE1B,QAAQ,KAAK,mBAAmB;EAC9B,IAAI,cAAc,GAAG;GACnB,QAAQ,MAAM,0CAA0C;GACxD,QAAQ,KAAK,CAAC;EAChB;CACF,CAAC;CAED,QACG,QAAQ,OAAO,CAAC,CAChB,YAAY,0CAA0C,CAAC,CACvD,OAAO,cAAc,oDAAoD,KAAK,CAAC,CAC/E,OAAO,UAAU,gEAAgE,KAAK,CAAC,CACvF,OAAO,cAAc,0BAA0B,OAAO,CAAC,CACvD,OAAO,kBAAkB,sCAAsC,CAAC,CAChE,OAAO,kBAAkB,oBAAoB,QAAQ,CAAC,CACtD,OAAO,iBAAiB,2DAA2D,CAAC,CACpF,OAAO,OAAO,YAAY;EACzB,MAAM,aAAa;GAAE,GAAG;GAAS,SAAS,QAAQ;EAAQ,CAAC;CAC7D,CAAC;CAEH,QACG,QAAQ,QAAQ,CAAC,CACjB,YAAY,gCAAgC,CAAC,CAC7C,OAAO,YAAY;EAClB,MAAM,cAAc;CACtB,CAAC;CAEH,QACG,QAAQ,QAAQ,CAAC,CACjB,YAAY,6CAA6C,CAAC,CAC1D,SAAS,cAAc,uDAAuD,CAAC,CAC/E,OAAO,UAAU,yCAAyC,KAAK,CAAC,CAChE,OAAO,mBAAmB,6CAA6C,CAAC,CACxE,OAAO,eAAe,mDAAmD,KAAK,CAAC,CAC/E,OAAO,iBAAiB,2DAA2D,CAAC,CACpF,OAAO,OAAO,OAA6B,YAAmF;EAC7H,MAAM,cAAc;GAAE,GAAG;GAAS;EAAM,CAAC;CAC3C,CAAC;CAEH,QACG,QAAQ,QAAQ,CAAC,CACjB,YAAY,2CAA2C,CAAC,CACxD,OAAO,eAAe,0CAA0C,CAAC,CACjE,OAAO,eAAe,gGAAgG,CAAC,CACvH,OAAO,OAAO,YAA8C;EAC3D,MAAM,cAAc,OAAO;CAC7B,CAAC;CAEH,QACG,QAAQ,QAAQ,CAAC,CACjB,YAAY,iDAAiD,CAAC,CAC9D,OAAO,eAAe,0CAA0C,CAAC,CACjE,OAAO,OAAO,YAAgC;EAC7C,MAAM,cAAc,OAAO;CAC7B,CAAC;CAEH,QACG,QAAQ,QAAQ,CAAC,CACjB,YAAY,6EAA6E,CAAC,CAC1F,OAAO,mBAAmB,uCAAuC,CAAC,CAClE,OAAO,kBAAkB,2BAA2B,CAAC,CACrD,OAAO,OAAO,YAAqD;EAClE,MAAM,cAAc,OAAO;CAC7B,CAAC;CAEH,QACG,QAAQ,QAAQ,CAAC,CACjB,YAAY,mBAAmB,CAAC,CAChC,OAAO,eAAe,iCAAiC,CAAC,CACxD,OAAO,mBAAmB,eAAe,CAAC,CAC1C,OAAO,OAAO,YAAgD;EAC7D,MAAM,cAAc,OAAO;CAC7B,CAAC;CAEH,MAAM,QAAQ,QAAQ,QAAQ,OAAO,CAAC,CAAC,YAAY,4BAA4B;CAC/E,MACG,QAAQ,KAAK,CAAC,CACd,YAAY,0BAA0B,CAAC,CACvC,eAAe,eAAe,eAAe,CAAC,CAC9C,eAAe,iBAAiB,+BAA+B,CAAC,CAChE,OAAO,OAAO,YAA8C;EAC3D,MAAM,gBAAgB,OAAO;CAC/B,CAAC;CACH,MACG,QAAQ,OAAO,CAAC,CAChB,YAAY,4BAA4B,CAAC,CACzC,eAAe,eAAe,eAAe,CAAC,CAC9C,OAAO,OAAO,YAA+B;EAC5C,MAAM,kBAAkB,OAAO;CACjC,CAAC;CACH,MACG,QAAQ,MAAM,CAAC,CACf,YAAY,2DAA2D,CAAC,CACxE,eAAe,eAAe,eAAe,CAAC,CAC9C,OAAO,OAAO,YAA+B;EAC5C,MAAM,iBAAiB,OAAO;CAChC,CAAC;CACH,MACG,QAAQ,MAAM,CAAC,CACf,YAAY,mCAAmC,CAAC,CAChD,OAAO,YAAY;EAClB,MAAM,iBAAiB;CACzB,CAAC;CAEH,OAAO;AACT"}
@@ -0,0 +1,173 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { a as proxiedFetch } from "./proxy-DLvX8yxv.mjs";
3
+ //#region src/types.ts
4
+ /** Whether a request ran through an explicit per-account proxy. */
5
+ function isProxyRouted(routing) {
6
+ return Boolean(routing?.proxyUrl);
7
+ }
8
+ /**
9
+ * Host-independent authentication error. The adapter maps `kind` to the DSH
10
+ * error protocol without coupling the session or CLI layers to dsh-llm.
11
+ */
12
+ var AgyAuthError = class extends Error {
13
+ kind;
14
+ constructor(kind, message, options) {
15
+ super(message, options);
16
+ this.name = "AgyAuthError";
17
+ this.kind = kind;
18
+ }
19
+ };
20
+ /**
21
+ * Enabled accounts exist, but every candidate is temporarily blocked. Kept
22
+ * independent of dsh-llm so CLI and web entry points do not gain a host import.
23
+ */
24
+ var AgyPoolBlockedError = class extends Error {
25
+ kind;
26
+ blockedUntil;
27
+ constructor(kind, blockedUntil) {
28
+ super(kind === "quota-exhausted" ? "All agy accounts have exhausted quota for the requested model family." : "All agy accounts are temporarily blocked for the requested model family.");
29
+ this.name = "AgyPoolBlockedError";
30
+ this.kind = kind;
31
+ this.blockedUntil = blockedUntil;
32
+ }
33
+ };
34
+ //#endregion
35
+ //#region src/oauth/constants.ts
36
+ /**
37
+ * Antigravity (agy) OAuth and API constants.
38
+ *
39
+ * The client id/secret below are the public Google consumer-OAuth credentials
40
+ * shipped inside the Antigravity desktop product and its `agy` CLI; they are
41
+ * embedded in many public tools (see NOTICE.md). They are not secrets owned by
42
+ * this project.
43
+ */
44
+ var constants_exports = /* @__PURE__ */ __exportAll({
45
+ AGY_CLIENT_ID: () => AGY_CLIENT_ID,
46
+ AGY_CLIENT_SECRET: () => AGY_CLIENT_SECRET,
47
+ AGY_DEFAULT_REDIRECT_URI: () => AGY_DEFAULT_REDIRECT_URI,
48
+ AGY_ENDPOINT_AUTOPUSH: () => AGY_ENDPOINT_AUTOPUSH,
49
+ AGY_ENDPOINT_DAILY: () => AGY_ENDPOINT_DAILY,
50
+ AGY_ENDPOINT_DAILY_SANDBOX: () => AGY_ENDPOINT_DAILY_SANDBOX,
51
+ AGY_ENDPOINT_FALLBACKS: () => AGY_ENDPOINT_FALLBACKS,
52
+ AGY_ENDPOINT_PROD: () => AGY_ENDPOINT_PROD,
53
+ AGY_ENDPOINT_SKIP_STATUSES: () => AGY_ENDPOINT_SKIP_STATUSES,
54
+ AGY_SCOPES: () => AGY_SCOPES,
55
+ AGY_VERSION_FALLBACK: () => AGY_VERSION_FALLBACK,
56
+ OAUTH_AUTHORIZE_URL: () => OAUTH_AUTHORIZE_URL,
57
+ OAUTH_TOKEN_URL: () => OAUTH_TOKEN_URL,
58
+ OAUTH_USERINFO_URL: () => OAUTH_USERINFO_URL,
59
+ fetchAgyFirstOk: () => fetchAgyFirstOk,
60
+ getAgyBootstrapClientMetadata: () => getAgyBootstrapClientMetadata,
61
+ getAgyBootstrapUserAgent: () => getAgyBootstrapUserAgent,
62
+ resolveAgyClientCredentials: () => resolveAgyClientCredentials
63
+ });
64
+ const AGY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
65
+ const AGY_CLIENT_SECRET = "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";
66
+ /**
67
+ * Effective OAuth client credentials: AGY_CLIENT_ID / AGY_CLIENT_SECRET env
68
+ * overrides win when set (BYO OAuth app escape hatch, mirrors pi-antigravity);
69
+ * otherwise the embedded public Antigravity credentials are used.
70
+ */
71
+ function resolveAgyClientCredentials(overrideClientId) {
72
+ if (overrideClientId) {
73
+ if (overrideClientId === "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com") return {
74
+ clientId: AGY_CLIENT_ID,
75
+ clientSecret: AGY_CLIENT_SECRET
76
+ };
77
+ return {
78
+ clientId: overrideClientId,
79
+ clientSecret: process.env.AGY_CLIENT_SECRET || "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"
80
+ };
81
+ }
82
+ return {
83
+ clientId: process.env.AGY_CLIENT_ID || "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com",
84
+ clientSecret: process.env.AGY_CLIENT_SECRET || "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"
85
+ };
86
+ }
87
+ /** Required scopes. `openid` must NOT be added: it routes Google into the hanging
88
+ * `firstparty/nativeapp` consent for this client (verified by OmniRoute). */
89
+ const AGY_SCOPES = [
90
+ "https://www.googleapis.com/auth/cloud-platform",
91
+ "https://www.googleapis.com/auth/userinfo.email",
92
+ "https://www.googleapis.com/auth/userinfo.profile",
93
+ "https://www.googleapis.com/auth/cclog",
94
+ "https://www.googleapis.com/auth/experimentsandconfigs"
95
+ ];
96
+ const OAUTH_AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth";
97
+ const OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
98
+ const OAUTH_USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo";
99
+ /** Default loopback callback used by the standalone CLI listener (fixed port, like opencode). */
100
+ const AGY_DEFAULT_REDIRECT_URI = "http://localhost:51121/oauth-callback";
101
+ /**
102
+ * Antigravity API endpoints. The daily runtime host (no .sandbox suffix) is the
103
+ * live endpoint for consumer OAuth accounts — cloudcode-pa.googleapis.com
104
+ * answers RESOURCE_EXHAUSTED for them (verified by live probe), while the
105
+ * daily host answers 200. Order matters: first reachable non-429/403/503 wins.
106
+ */
107
+ const AGY_ENDPOINT_DAILY = "https://daily-cloudcode-pa.googleapis.com";
108
+ const AGY_ENDPOINT_PROD = "https://cloudcode-pa.googleapis.com";
109
+ const AGY_ENDPOINT_DAILY_SANDBOX = "https://daily-cloudcode-pa.sandbox.googleapis.com";
110
+ const AGY_ENDPOINT_AUTOPUSH = "https://autopush-cloudcode-pa.sandbox.googleapis.com";
111
+ /** Runtime/bootstrap endpoint fallback order (daily first, mirroring OmniRoute). */
112
+ const AGY_ENDPOINT_FALLBACKS = [
113
+ AGY_ENDPOINT_DAILY,
114
+ AGY_ENDPOINT_PROD,
115
+ AGY_ENDPOINT_DAILY_SANDBOX,
116
+ AGY_ENDPOINT_AUTOPUSH
117
+ ];
118
+ /**
119
+ * Statuses that mean "this endpoint is not usable for this attempt"; skip to the
120
+ * next one in the chain. 429/403 = rate/quota/entitlement wall, 503 = capacity
121
+ * rejection (e.g. "No capacity available for model ..."). A 503 skipped here
122
+ * still reaches the caller when every endpoint fails, where the classifier marks
123
+ * it transient and the adapter surfaces it as a retryable SERVER error.
124
+ */
125
+ const AGY_ENDPOINT_SKIP_STATUSES = /* @__PURE__ */ new Set([
126
+ 429,
127
+ 403,
128
+ 503
129
+ ]);
130
+ /**
131
+ * Try each runtime endpoint in order, skipping unusable ones (429/403/503/network).
132
+ * Returns the first other response (2xx or a real error like 400/401); when
133
+ * every endpoint is unusable, returns the last skipped response so the caller's
134
+ * classifier can still produce a meaningful error (a returned 503 becomes a
135
+ * retryable SERVER failure rather than being swallowed here).
136
+ *
137
+ * When every endpoint fails at the network level, the LAST error is rethrown so
138
+ * its cause survives (a generic "all endpoints failed" hid DNS/TLS/timeout).
139
+ */
140
+ async function fetchAgyFirstOk(urlPath, init, fetchImpl = proxiedFetch, options = {}) {
141
+ let lastSkipped = null;
142
+ let lastNetworkError;
143
+ for (const baseEndpoint of AGY_ENDPOINT_FALLBACKS) try {
144
+ const response = await fetchImpl(`${baseEndpoint}${urlPath}`, init);
145
+ if (!AGY_ENDPOINT_SKIP_STATUSES.has(response.status)) return response;
146
+ lastSkipped = response;
147
+ } catch (error) {
148
+ lastNetworkError = error;
149
+ if (isProxyRouted(options)) {
150
+ const { isProxyUnreachableError } = await import("./proxy-DLvX8yxv.mjs").then((n) => n.s);
151
+ if (isProxyUnreachableError(error)) throw error;
152
+ }
153
+ }
154
+ if (lastSkipped) return lastSkipped;
155
+ if (lastNetworkError !== void 0) throw lastNetworkError;
156
+ throw new Error("all agy endpoints failed");
157
+ }
158
+ /** Default Antigravity client version used in User-Agent strings; overridden by the
159
+ * runtime version fetcher (see runtime/fingerprint.ts). */
160
+ const AGY_VERSION_FALLBACK = "1.18.3";
161
+ /** Electron-style UA used for bootstrap calls (loadCodeAssist/onboardUser). */
162
+ function getAgyBootstrapUserAgent(version = AGY_VERSION_FALLBACK) {
163
+ 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`;
164
+ }
165
+ /** Client-Metadata payload for bootstrap calls — ideType only (backend enum
166
+ * validation rejects freely-added platform/pluginType; AGENTS.md invariant). */
167
+ function getAgyBootstrapClientMetadata() {
168
+ return "{\"ideType\":\"ANTIGRAVITY\"}";
169
+ }
170
+ //#endregion
171
+ export { AGY_VERSION_FALLBACK as a, OAUTH_USERINFO_URL as c, getAgyBootstrapClientMetadata as d, getAgyBootstrapUserAgent as f, isProxyRouted as g, AgyPoolBlockedError as h, AGY_SCOPES as i, constants_exports as l, AgyAuthError as m, AGY_DEFAULT_REDIRECT_URI as n, OAUTH_AUTHORIZE_URL as o, resolveAgyClientCredentials as p, AGY_ENDPOINT_FALLBACKS as r, OAUTH_TOKEN_URL as s, AGY_CLIENT_ID as t, fetchAgyFirstOk as u };
172
+
173
+ //# sourceMappingURL=constants-BFKYjOmh.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants-BFKYjOmh.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 * Client-Metadata must only transmit ideType: the backend's enum validation\n * rejects freely-added platform/pluginType fields (AGENTS.md invariant).\n */\nexport interface ClientMetadata {\n ideType: 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/** Per-account quota cache keyed by model id. */\nexport interface CachedQuota {\n remainingFraction?: number\n resetTime?: string\n modelCount?: 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 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 /** 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 /** 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\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 | 'network-error'\n | 'project-error'\n | 'request-error'\n | 'transient'\n | 'proxy-unreachable'\n\n/** Rotation state machine decision for one failed attempt. */\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/** Default Antigravity client version used in User-Agent strings; overridden by the\n * runtime version fetcher (see runtime/fingerprint.ts). */\nexport const AGY_VERSION_FALLBACK = '1.18.3'\n\n/** Electron-style UA used for bootstrap calls (loadCodeAssist/onboardUser). */\nexport function getAgyBootstrapUserAgent(version = AGY_VERSION_FALLBACK): 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/** Client-Metadata payload for bootstrap calls — ideType only (backend enum\n * validation rejects freely-added platform/pluginType; AGENTS.md invariant). */\nexport function getAgyBootstrapClientMetadata(): string {\n return '{\"ideType\":\"ANTIGRAVITY\"}'\n}\n"],"mappings":";;;;AAgIA,SAAgB,cAAc,SAA8C;CAC1E,OAAO,QAAQ,SAAS,QAAQ;AAClC;;;;;AAsDA,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxMA,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;;;AAIA,MAAa,uBAAuB;;AAGpC,SAAgB,yBAAyB,UAAU,sBAA8B;CAC/E,OAAO,gGAAgG,QAAQ;AACjH;;;AAIA,SAAgB,gCAAwC;CACtD,OAAO;AACT"}
@@ -1,6 +1,7 @@
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, O as resolveAgyClientCredentials, b as AGY_ENDPOINT_FALLBACKS, m as calculateTokenExpiry, w as OAUTH_USERINFO_URL } from "./accounts-Um8N9I56.mjs";
3
- import { i as proxiedFetch } from "./proxy-DQPIwUov.mjs";
2
+ import { c as OAUTH_USERINFO_URL, d as getAgyBootstrapClientMetadata, f as getAgyBootstrapUserAgent, p as resolveAgyClientCredentials, r as AGY_ENDPOINT_FALLBACKS, s as OAUTH_TOKEN_URL } from "./constants-BFKYjOmh.mjs";
3
+ import { a as proxiedFetch } from "./proxy-DLvX8yxv.mjs";
4
+ import { n as calculateTokenExpiry } from "./auth-DPPVFwio.mjs";
4
5
  import { createHash, randomBytes } from "node:crypto";
5
6
  //#region src/oauth/pkce.ts
6
7
  /** Minimal PKCE (RFC 7636) with S256, hand-rolled on node:crypto (no extra dependency). */
@@ -36,14 +37,14 @@ var exchange_exports = /* @__PURE__ */ __exportAll({
36
37
  onboardAndDiscoverProject: () => onboardAndDiscoverProject
37
38
  });
38
39
  const FETCH_TIMEOUT_MS = 1e4;
39
- async function fetchWithTimeout(url, options, timeoutMs = FETCH_TIMEOUT_MS) {
40
+ async function fetchWithTimeout(url, options, timeoutMs = FETCH_TIMEOUT_MS, proxyUrl) {
40
41
  const controller = new AbortController();
41
42
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
42
43
  try {
43
44
  return await proxiedFetch(url, {
44
45
  ...options,
45
46
  signal: controller.signal
46
- });
47
+ }, { proxyUrl });
47
48
  } finally {
48
49
  clearTimeout(timeout);
49
50
  }
@@ -89,7 +90,7 @@ function extractOnboardTierId(subscriptionInfo) {
89
90
  return "legacy-tier";
90
91
  }
91
92
  /** Resolve project id + tier id via loadCodeAssist across fallback endpoints. */
92
- async function loadCodeAssist(accessToken) {
93
+ async function loadCodeAssist(accessToken, options = {}) {
93
94
  const errors = [];
94
95
  const loadHeaders = {
95
96
  Authorization: `Bearer ${accessToken}`,
@@ -102,7 +103,7 @@ async function loadCodeAssist(accessToken) {
102
103
  method: "POST",
103
104
  headers: loadHeaders,
104
105
  body: JSON.stringify({ metadata: bootstrapMetadata() })
105
- });
106
+ }, FETCH_TIMEOUT_MS, options.proxyUrl);
106
107
  if (!response.ok) {
107
108
  const message = await response.text().catch(() => "");
108
109
  errors.push(`loadCodeAssist ${response.status} at ${baseEndpoint}${message ? `: ${message}` : ""}`);
@@ -148,10 +149,10 @@ async function onboardAndDiscoverProject(accessToken, tierId, options = {}) {
148
149
  tier_id: tierId,
149
150
  metadata
150
151
  })
151
- });
152
+ }, FETCH_TIMEOUT_MS, options.proxyUrl);
152
153
  if (!response.ok) continue;
153
154
  if ((await response.json()).done === true) {
154
- const discovered = await loadCodeAssist(accessToken);
155
+ const discovered = await loadCodeAssist(accessToken, { proxyUrl: options.proxyUrl });
155
156
  if (discovered.projectId) return discovered;
156
157
  }
157
158
  }
@@ -165,7 +166,7 @@ async function onboardAndDiscoverProject(accessToken, tierId, options = {}) {
165
166
  }
166
167
  /** Full bootstrap for a fresh account: discover the project, onboarding if needed. */
167
168
  async function bootstrapAccount(accessToken, options = {}) {
168
- const discovered = await loadCodeAssist(accessToken);
169
+ const discovered = await loadCodeAssist(accessToken, options);
169
170
  if (discovered.projectId) return discovered;
170
171
  return onboardAndDiscoverProject(accessToken, discovered.tierId, options);
171
172
  }
@@ -176,7 +177,7 @@ async function bootstrapAccount(accessToken, options = {}) {
176
177
  * @param state - the `state` query parameter (encodes verifier + projectId).
177
178
  * @param redirectUri - must match the authorize() redirectUri.
178
179
  */
179
- async function exchangeAntigravity(code, state, redirectUri, expectedVerifier) {
180
+ async function exchangeAntigravity(code, state, redirectUri, expectedVerifier, routing = {}) {
180
181
  try {
181
182
  const { verifier, projectId } = decodeState(state);
182
183
  if (!verifier) return {
@@ -204,7 +205,7 @@ async function exchangeAntigravity(code, state, redirectUri, expectedVerifier) {
204
205
  redirect_uri: redirectUri,
205
206
  code_verifier: verifier
206
207
  })
207
- });
208
+ }, routing);
208
209
  if (!tokenResponse.ok) return {
209
210
  type: "failed",
210
211
  error: await tokenResponse.text()
@@ -213,14 +214,14 @@ async function exchangeAntigravity(code, state, redirectUri, expectedVerifier) {
213
214
  const userInfoResponse = await proxiedFetch(`${OAUTH_USERINFO_URL}?alt=json`, { headers: {
214
215
  Authorization: `Bearer ${tokenPayload.access_token}`,
215
216
  "User-Agent": getAgyBootstrapUserAgent()
216
- } });
217
+ } }, routing);
217
218
  const userInfo = userInfoResponse.ok ? await userInfoResponse.json() : {};
218
219
  const refreshToken = tokenPayload.refresh_token;
219
220
  if (!refreshToken) return {
220
221
  type: "failed",
221
222
  error: "Missing refresh token in response"
222
223
  };
223
- const effectiveProjectId = projectId || (await bootstrapAccount(tokenPayload.access_token)).projectId;
224
+ const effectiveProjectId = projectId || (await bootstrapAccount(tokenPayload.access_token, routing)).projectId;
224
225
  return {
225
226
  type: "success",
226
227
  refresh: `${refreshToken}|${effectiveProjectId || ""}`,
@@ -240,4 +241,4 @@ async function exchangeAntigravity(code, state, redirectUri, expectedVerifier) {
240
241
  //#endregion
241
242
  export { generatePkcePair as i, exchange_exports as n, encodeState as r, exchangeAntigravity as t };
242
243
 
243
- //# sourceMappingURL=exchange-Bdql4kf3.mjs.map
244
+ //# sourceMappingURL=exchange-dQm2mjJ5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exchange-dQm2mjJ5.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 OAUTH_TOKEN_URL,\n OAUTH_USERINFO_URL,\n getAgyBootstrapClientMetadata,\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/** 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(\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 '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 }, 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 '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 }, 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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"}
@@ -0,0 +1,35 @@
1
+ import { randomBytes } from "node:crypto";
2
+ //#region src/runtime/identity.ts
3
+ /**
4
+ * Antigravity request/session identity generation (mirrors the active
5
+ * OmniRoute implementation; the archived reference stopped maintaining these).
6
+ */
7
+ /** One request id per upstream call: `agent/<epoch>/<8 hex>`. */
8
+ function generateAntigravityRequestId() {
9
+ return `agent/${Date.now()}/${randomBytes(4).toString("hex")}`;
10
+ }
11
+ const FNV_OFFSET_I64 = -3750763044362895579n;
12
+ const FNV_PRIME_I64 = 1099511628211n;
13
+ /** 64-bit FNV-1a hash of a string (stable across processes). */
14
+ function fnv1a64(input) {
15
+ let hash = FNV_OFFSET_I64;
16
+ for (let i = 0; i < input.length; i++) {
17
+ hash ^= BigInt(input.charCodeAt(i));
18
+ hash = BigInt.asIntN(64, hash * FNV_PRIME_I64);
19
+ }
20
+ return hash;
21
+ }
22
+ /**
23
+ * Stable per-account session id: same account always derives the same id, so
24
+ * multi-turn context caching keys consistently; unknown accounts get a fresh
25
+ * random id.
26
+ */
27
+ function deriveAntigravitySessionId(accountKey) {
28
+ if (!accountKey || accountKey.trim().length === 0) return null;
29
+ const hash = fnv1a64(accountKey.trim());
30
+ return `-${((hash < 0n ? -hash : hash) % 9000000000000000000n).toString()}`;
31
+ }
32
+ //#endregion
33
+ export { generateAntigravityRequestId as n, deriveAntigravitySessionId as t };
34
+
35
+ //# sourceMappingURL=identity-DsftHTJC.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-DsftHTJC.mjs","names":[],"sources":["../src/runtime/identity.ts"],"sourcesContent":["/**\n * Antigravity request/session identity generation (mirrors the active\n * OmniRoute implementation; the archived reference stopped maintaining these).\n */\n\nimport { randomBytes } from 'node:crypto'\n\n/** One request id per upstream call: `agent/<epoch>/<8 hex>`. */\nexport function generateAntigravityRequestId(): string {\n return `agent/${Date.now()}/${randomBytes(4).toString('hex')}`\n}\n\n/**\n * Uniformly random session id in `-{0..9e18}` via rejection sampling (avoids\n * modulo bias). The negative numeric shape matches what the backend issues.\n */\nexport function generateAntigravitySessionId(): string {\n const max = 18_446_744_073_709_551_615n // 2^64 - 1\n const target = 9_000_000_000_000_000_000n\n const limit = max - (max % target)\n let value: bigint\n do {\n value = randomBytes(8).readBigUInt64BE()\n } while (value >= limit)\n return `-${(value % target).toString()}`\n}\n\nconst FNV_OFFSET_I64 = -3_750_763_044_362_895_579n\nconst FNV_PRIME_I64 = 1_099_511_628_211n\n\n/** 64-bit FNV-1a hash of a string (stable across processes). */\nexport function fnv1a64(input: string): bigint {\n let hash = FNV_OFFSET_I64\n for (let i = 0; i < input.length; i++) {\n hash ^= BigInt(input.charCodeAt(i))\n hash = BigInt.asIntN(64, hash * FNV_PRIME_I64)\n }\n return hash\n}\n\n/**\n * Stable per-account session id: same account always derives the same id, so\n * multi-turn context caching keys consistently; unknown accounts get a fresh\n * random id.\n */\nexport function deriveAntigravitySessionId(accountKey: string | null | undefined): string | null {\n if (!accountKey || accountKey.trim().length === 0) return null\n const hash = fnv1a64(accountKey.trim())\n // Fold into the positive 9e18 range the backend accepts.\n const target = 9_000_000_000_000_000_000n\n const folded = hash < 0n ? -hash : hash\n return `-${(folded % target).toString()}`\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,+BAAuC;CACrD,OAAO,SAAS,KAAK,IAAI,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK;AAC7D;AAiBA,MAAM,iBAAiB,CAAC;AACxB,MAAM,gBAAgB;;AAGtB,SAAgB,QAAQ,OAAuB;CAC7C,IAAI,OAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,QAAQ,OAAO,MAAM,WAAW,CAAC,CAAC;EAClC,OAAO,OAAO,OAAO,IAAI,OAAO,aAAa;CAC/C;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,2BAA2B,YAAsD;CAC/F,IAAI,CAAC,cAAc,WAAW,KAAK,CAAC,CAAC,WAAW,GAAG,OAAO;CAC1D,MAAM,OAAO,QAAQ,WAAW,KAAK,CAAC;CAItC,OAAO,MADQ,OAAO,KAAK,CAAC,OAAO,QACd,qBAAA,CAAQ,SAAS;AACxC"}