when-does-my-quota-refresh 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sources/oauth/client.ts","../src/core/logger.ts","../src/sources/cloud-code-api.ts","../src/sdk/accounts/storage.ts","../src/core/config.ts","../src/core/errors.ts","../src/sdk/accounts/auth.ts"],"sourcesContent":["/**\n * Google OAuth client for Cloud Code API\n *\n * Handles the OAuth2 flow for remote quota fetching.\n */\n\nimport https from 'https'\nimport { URL } from 'url'\nimport { debug } from '../../core/logger.js'\nimport type { OAuthTokenResponse, StoredTokens } from '../../sdk/types.js'\nimport {\n CLOUD_CODE_ENDPOINTS,\n CLOUD_CODE_DEFAULT_BODY,\n OAUTH_CLIENT_ID,\n OAUTH_CLIENT_SECRET,\n OAUTH_SCOPES,\n OAUTH_REDIRECT_URI,\n} from '../cloud-code-api.js'\nimport { saveAccountTokens } from '../../sdk/accounts/storage.js'\n\nexport class OAuthClient {\n private tokens: StoredTokens | null = null\n\n constructor(tokens?: StoredTokens) {\n this.tokens = tokens || null\n }\n\n /**\n * Generate the OAuth authorization URL for user login\n */\n getAuthUrl(redirectUri?: string): string {\n const params = new URLSearchParams({\n client_id: OAUTH_CLIENT_ID,\n redirect_uri: redirectUri || OAUTH_REDIRECT_URI,\n response_type: 'code',\n scope: OAUTH_SCOPES,\n access_type: 'offline',\n prompt: 'consent',\n })\n return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`\n }\n\n /**\n * Exchange an authorization code for tokens\n */\n async exchangeCode(code: string, redirectUri?: string): Promise<StoredTokens> {\n const body = new URLSearchParams({\n code,\n client_id: OAUTH_CLIENT_ID,\n client_secret: OAUTH_CLIENT_SECRET,\n redirect_uri: redirectUri || OAUTH_REDIRECT_URI,\n grant_type: 'authorization_code',\n })\n\n const response = await this.postUrlEncoded('https://oauth2.googleapis.com/token', body)\n const data = response as OAuthTokenResponse\n\n this.tokens = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || '',\n expiresAt: Date.now() + data.expires_in * 1000,\n }\n\n return this.tokens\n }\n\n /**\n * Refresh the access token\n */\n async refreshAccessToken(): Promise<StoredTokens> {\n if (!this.tokens?.refreshToken) {\n throw new Error('No refresh token available')\n }\n\n const body = new URLSearchParams({\n client_id: OAUTH_CLIENT_ID,\n client_secret: OAUTH_CLIENT_SECRET,\n refresh_token: this.tokens.refreshToken,\n grant_type: 'refresh_token',\n })\n\n const response = await this.postUrlEncoded('https://oauth2.googleapis.com/token', body)\n const data = response as OAuthTokenResponse\n\n this.tokens = {\n ...this.tokens,\n accessToken: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000,\n }\n\n // Persist refreshed token to disk so it survives restarts\n if (this.tokens.email) {\n try {\n saveAccountTokens(this.tokens.email, this.tokens)\n debug('oauth', `Persisted refreshed token for ${this.tokens.email}`)\n } catch (err) {\n debug('oauth', `Failed to persist refreshed token: ${err}`)\n }\n }\n\n return this.tokens\n }\n\n /**\n * Get a valid access token, refreshing if necessary\n */\n async getAccessToken(): Promise<string> {\n if (!this.tokens) throw new Error('Not authenticated')\n if (Date.now() >= this.tokens.expiresAt - 60000) {\n await this.refreshAccessToken()\n }\n return this.tokens.accessToken!\n }\n\n /**\n * Get stored tokens\n */\n getTokens(): StoredTokens | null {\n return this.tokens\n }\n\n /**\n * Make an authenticated POST request to the Cloud Code API\n */\n async fetchQuotaSummary(): Promise<unknown> {\n const token = await this.getAccessToken()\n return this.authenticatedPost(\n CLOUD_CODE_ENDPOINTS.retrieveUserQuotaSummary,\n CLOUD_CODE_DEFAULT_BODY,\n token\n )\n }\n\n async fetchUserStatus(): Promise<unknown> {\n const token = await this.getAccessToken()\n return this.authenticatedPost(\n CLOUD_CODE_ENDPOINTS.loadCodeAssist,\n CLOUD_CODE_DEFAULT_BODY,\n token\n )\n }\n\n async fetchAvailableModels(): Promise<unknown> {\n const token = await this.getAccessToken()\n return this.authenticatedPost(\n CLOUD_CODE_ENDPOINTS.fetchAvailableModels,\n CLOUD_CODE_DEFAULT_BODY,\n token\n )\n }\n\n /**\n * Fetch the authenticated user's profile (email, name, etc.)\n */\n async fetchUserInfo(): Promise<{ email?: string; name?: string }> {\n const token = await this.getAccessToken()\n return new Promise((resolve, reject) => {\n const options: https.RequestOptions = {\n hostname: 'www.googleapis.com',\n path: '/oauth2/v3/userinfo',\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n timeout: 10000,\n }\n\n const req = https.request(options, (res) => {\n let data = ''\n res.on('data', (chunk: Buffer) => { data += chunk })\n res.on('end', () => {\n try {\n const info = JSON.parse(data)\n resolve({ email: info.email, name: info.name })\n } catch {\n resolve({})\n }\n })\n })\n req.on('error', () => resolve({}))\n req.on('timeout', () => { req.destroy(); resolve({}) })\n req.end()\n })\n }\n\n // ── HTTP Helpers ────────────────────────────────────────────────\n\n private authenticatedPost(url: string, body: unknown, token: string): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const parsed = new URL(url)\n const data = JSON.stringify(body)\n const options: https.RequestOptions = {\n hostname: parsed.hostname,\n path: parsed.pathname,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n 'Content-Length': Buffer.byteLength(data),\n },\n timeout: 10000,\n }\n\n const req = https.request(options, (res) => {\n let responseData = ''\n res.on('data', (chunk: Buffer) => { responseData += chunk })\n res.on('end', () => {\n if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {\n reject(new Error(`HTTP ${res.statusCode}: ${responseData.slice(0, 200)}`))\n return\n }\n try {\n resolve(JSON.parse(responseData))\n } catch {\n reject(new Error(`Invalid JSON: ${responseData.slice(0, 200)}`))\n }\n })\n })\n req.on('error', reject)\n req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')) })\n req.write(data)\n req.end()\n })\n }\n\n private postUrlEncoded(url: string, body: URLSearchParams): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const parsed = new URL(url)\n const data = body.toString()\n const options: https.RequestOptions = {\n hostname: parsed.hostname,\n path: parsed.pathname,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Content-Length': Buffer.byteLength(data),\n },\n timeout: 10000,\n }\n\n const req = https.request(options, (res) => {\n let responseData = ''\n res.on('data', (chunk: Buffer) => { responseData += chunk })\n res.on('end', () => {\n if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {\n reject(new Error(`HTTP ${res.statusCode}: ${responseData.slice(0, 200)}`))\n return\n }\n try {\n resolve(JSON.parse(responseData))\n } catch {\n reject(new Error(`Invalid JSON: ${responseData.slice(0, 200)}`))\n }\n })\n })\n req.on('error', reject)\n req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')) })\n req.write(data)\n req.end()\n })\n }\n}\n","/**\n * Simple logger with debug flag\n */\n\nlet _verbose = false\n\nexport function setVerbose(verbose: boolean): void {\n _verbose = verbose\n}\n\nexport function debug(module: string, msg: string, data?: unknown): void {\n if (!_verbose) return\n const prefix = `[${module}]`\n if (data !== undefined) {\n console.error(`${prefix} ${msg}`, data)\n } else {\n console.error(`${prefix} ${msg}`)\n }\n}\n\nexport function info(msg: string): void {\n console.error(msg)\n}\n\nexport function warn(msg: string): void {\n console.error(`⚠ ${msg}`)\n}\n","/**\n * Google Cloud Code API endpoints for remote quota fetching\n *\n * Used as fallback when local sources are unavailable.\n * Requires OAuth authentication.\n */\n\nexport const CLOUD_CODE_ENDPOINTS = {\n /** Load code assist configuration */\n loadCodeAssist: 'https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist',\n\n /** Onboard a new user */\n onboardUser: 'https://cloudcode-pa.googleapis.com/v1internal:onboardUser',\n\n /** Fetch available models for the account */\n fetchAvailableModels: 'https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels',\n\n /** Fetch raw quota data */\n retrieveUserQuota: 'https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota',\n\n /** Fetch quota summary with weekly + session buckets */\n retrieveUserQuotaSummary: 'https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary',\n} as const\n\nexport const CLOUD_CODE_DEFAULT_BODY = {\n metadata: {\n ideName: 'antigravity',\n extensionName: 'antigravity',\n locale: 'en',\n ideVersion: 'unknown',\n },\n}\n\nexport const OAUTH_CLIENT_ID = '764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com'\nexport const OAUTH_CLIENT_SECRET = 'd-FL95Q19q7MQmFpd7hHD0Ty'\nexport const OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/cloud-platform',\n].join(' ')\nexport const OAUTH_REDIRECT_URI = 'http://127.0.0.1:0/callback'\n","/**\n * Token storage for account credentials\n *\n * Stores OAuth tokens locally in the config directory.\n * Tokens are stored as plain JSON (local machine only).\n */\n\nimport { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from 'fs'\nimport { join, dirname } from 'path'\nimport type { StoredTokens } from '../types.js'\nimport { getConfigDirPath, ensureDirs } from '../../core/config.js'\nimport { debug } from '../../core/logger.js'\n\nconst TOKENS_DIR = 'accounts'\n\ninterface TokenAccount {\n email: string\n tokens: StoredTokens\n addedAt: string\n}\n\ninterface TokenStore {\n accounts: TokenAccount[]\n activeEmail?: string\n}\n\nfunction getStorePath(): string {\n return join(getConfigDirPath(), TOKENS_DIR, 'tokens.json')\n}\n\nfunction loadStore(): TokenStore {\n const path = getStorePath()\n if (!existsSync(path)) return { accounts: [] }\n try {\n return JSON.parse(readFileSync(path, 'utf-8'))\n } catch {\n return { accounts: [] }\n }\n}\n\nfunction saveStore(store: TokenStore): void {\n ensureDirs()\n const storePath = getStorePath()\n mkdirSync(dirname(storePath), { recursive: true })\n writeFileSync(storePath, JSON.stringify(store, null, 2))\n debug('account-storage', `Saved ${store.accounts.length} account(s)`)\n}\n\n/**\n * Save tokens for an account\n */\nexport function saveAccountTokens(email: string, tokens: StoredTokens): void {\n const store = loadStore()\n const existing = store.accounts.findIndex(a => a.email === email)\n const account: TokenAccount = {\n email,\n tokens,\n addedAt: new Date().toISOString(),\n }\n\n if (existing >= 0) {\n store.accounts[existing] = account\n } else {\n store.accounts.push(account)\n }\n\n // Set as active if it's the only one\n if (store.accounts.length === 1) {\n store.activeEmail = email\n }\n\n saveStore(store)\n}\n\n/**\n * Get tokens for a specific account\n */\nexport function getAccountTokens(email: string): StoredTokens | null {\n const store = loadStore()\n const account = store.accounts.find(a => a.email === email)\n return account?.tokens || null\n}\n\n/**\n * Get the active account's tokens\n */\nexport function getActiveAccountTokens(): StoredTokens | null {\n const store = loadStore()\n if (!store.activeEmail) return null\n const account = store.accounts.find(a => a.email === store.activeEmail)\n return account?.tokens || null\n}\n\n/**\n * Set the active account\n */\nexport function setActiveAccount(email: string): boolean {\n const store = loadStore()\n if (!store.accounts.find(a => a.email === email)) return false\n store.activeEmail = email\n saveStore(store)\n return true\n}\n\n/**\n * Remove an account\n */\nexport function removeAccount(email: string): boolean {\n const store = loadStore()\n const idx = store.accounts.findIndex(a => a.email === email)\n if (idx < 0) return false\n store.accounts.splice(idx, 1)\n if (store.activeEmail === email) {\n store.activeEmail = store.accounts[0]?.email\n }\n saveStore(store)\n return true\n}\n\n/**\n * List all stored accounts\n */\nexport function listAccounts(): Array<{ email: string; isActive: boolean; addedAt: string }> {\n const store = loadStore()\n return store.accounts.map(a => ({\n email: a.email,\n isActive: a.email === store.activeEmail,\n addedAt: a.addedAt,\n }))\n}\n\n/**\n * Get all stored tokens (for multi-account fetch)\n */\nexport function getAllAccountTokens(): StoredTokens[] {\n const store = loadStore()\n return store.accounts.map(a => a.tokens)\n}\n","/**\n * Config file management — reads/writes app configuration\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs'\nimport { join } from 'path'\nimport { homedir } from 'os'\nimport type { AppConfig } from '../sdk/types.js'\nimport { debug } from './logger.js'\n\nconst CONFIG_DIR_NAME = 'when-does-my-quota-refresh'\n\nconst DEFAULT_CONFIG: AppConfig = {\n defaultMode: 'local',\n cacheTtlMs: 5 * 60 * 1000, // 5 minutes\n refreshIntervalMs: 5 * 60 * 1000,\n wakeupModels: ['claude-sonnet-4-5', 'gemini-3-flash', 'gemini-3-pro-low'],\n wakeupAccounts: [],\n wakeupMode: 'smart',\n wakeupIntervalHours: 6,\n wakeupDailyTimes: '9,17',\n notifications: true,\n plugins: [],\n daemon: {\n pollIntervalMs: 60 * 1000,\n enabled: false,\n },\n}\n\nfunction getConfigDir(): string {\n const home = homedir()\n switch (process.platform) {\n case 'darwin':\n return join(home, 'Library', 'Application Support', CONFIG_DIR_NAME)\n case 'win32':\n return join(process.env.APPDATA || join(home, 'AppData', 'Roaming'), CONFIG_DIR_NAME)\n default:\n return join(home, '.config', CONFIG_DIR_NAME)\n }\n}\n\nfunction getConfigPath(): string {\n return join(getConfigDir(), 'config.json')\n}\n\nfunction getDataDir(): string {\n return join(getConfigDir(), 'data')\n}\n\nexport function ensureDirs(): void {\n const configDir = getConfigDir()\n const dataDir = getDataDir()\n if (!existsSync(configDir)) mkdirSync(configDir, { recursive: true })\n if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true })\n}\n\nexport function loadConfig(): AppConfig {\n try {\n const path = getConfigPath()\n if (!existsSync(path)) return DEFAULT_CONFIG\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw)\n debug('config', `Loaded config from ${path}`)\n return { ...DEFAULT_CONFIG, ...parsed }\n } catch (err) {\n debug('config', 'Failed to load config, using defaults', err)\n return DEFAULT_CONFIG\n }\n}\n\nexport function saveConfig(config: AppConfig): void {\n ensureDirs()\n const path = getConfigPath()\n writeFileSync(path, JSON.stringify(config, null, 2))\n debug('config', `Saved config to ${path}`)\n}\n\nexport function getConfigDirPath(): string {\n return getConfigDir()\n}\n\nexport function getDataDirPath(): string {\n return getDataDir()\n}\n\nexport { DEFAULT_CONFIG }\n","/**\n * Custom error types for when-does-my-quota-refresh\n */\n\nexport class SourceUnavailableError extends Error {\n constructor(source: string, reason?: string) {\n super(`Source \"${source}\" unavailable${reason ? `: ${reason}` : ''}`)\n this.name = 'SourceUnavailableError'\n }\n}\n\nexport class AuthError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'AuthError'\n }\n}\n\nexport class QuotaFetchError extends Error {\n constructor(source: string, reason?: string) {\n super(`Failed to fetch quota from \"${source}\"${reason ? `: ${reason}` : ''}`)\n this.name = 'QuotaFetchError'\n }\n}\n\nexport class ProcessNotFoundError extends Error {\n constructor() {\n super('Antigravity language server process not found')\n this.name = 'ProcessNotFoundError'\n }\n}\n\nexport class PortDiscoveryError extends Error {\n constructor(pid: number, platform: string) {\n super(`Failed to discover ports for PID ${pid} on ${platform}`)\n this.name = 'PortDiscoveryError'\n }\n}\n","/**\n * OAuth authentication flow for Google accounts\n */\n\nimport { OAuthClient } from '../../sources/oauth/client.js'\nimport { saveAccountTokens, setActiveAccount } from './storage.js'\nimport { debug } from '../../core/logger.js'\nimport { AuthError } from '../../core/errors.js'\nimport open from 'open'\nimport http from 'http'\nimport { URL } from 'url'\n\n/**\n * Start an interactive OAuth login flow\n *\n * Opens the browser for Google login, then captures the redirect\n * to exchange the authorization code for tokens.\n */\nexport async function loginAccount(): Promise<string> {\n const client = new OAuthClient()\n\n debug('auth', 'Starting OAuth login flow')\n\n // Start a temporary local server to catch the redirect\n let capturedRedirectUri = 'http://127.0.0.1:0/callback'\n const code = await new Promise<string>((resolve, reject) => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url || '/', `http://127.0.0.1`)\n const code = url.searchParams.get('code')\n\n if (code) {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(`\n <html><body style=\"font-family: sans-serif; text-align: center; padding: 50px;\">\n <h1>✅ Login successful!</h1>\n <p>You can close this tab and return to the terminal.</p>\n </body></html>\n `)\n server.close()\n resolve(code)\n } else {\n const error = url.searchParams.get('error')\n if (error) {\n res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(`<h1>Login failed: ${error}</h1>`)\n server.close()\n reject(new AuthError(`OAuth error: ${error}`))\n } else {\n res.writeHead(404)\n res.end('Waiting for OAuth callback...')\n }\n }\n })\n\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address()\n if (typeof addr === 'object' && addr) {\n capturedRedirectUri = `http://127.0.0.1:${addr.port}/callback`\n debug('auth', `Callback server on port ${addr.port}`)\n\n // Build auth URL with the actual port\n const authUrl = client.getAuthUrl(capturedRedirectUri)\n\n // Open browser\n open(authUrl).catch(() => {\n console.log(`\\nOpen this URL in your browser:\\n\\n${authUrl}\\n`)\n })\n }\n })\n\n // Timeout after 5 minutes\n setTimeout(() => {\n server.close()\n reject(new AuthError('Login timed out after 5 minutes'))\n }, 5 * 60 * 1000)\n })\n\n // Exchange code for tokens (use same redirect URI as the auth URL)\n const tokens = await client.exchangeCode(code, capturedRedirectUri)\n\n // Fetch the user's email from Google\n let email = tokens.email\n if (!email) {\n try {\n const userInfo = await client.fetchUserInfo()\n email = userInfo.email\n } catch (err) {\n debug('auth', `Failed to fetch user info: ${err}`)\n }\n }\n if (!email) {\n throw new AuthError('Could not determine user email from Google account')\n }\n tokens.email = email\n\n // Store tokens\n saveAccountTokens(email, tokens)\n setActiveAccount(email)\n\n debug('auth', `Logged in as ${email}`)\n return email\n}\n\n/**\n * Perform a manual login (headless/SSH mode)\n * Returns the auth URL for the user to open manually\n */\nexport function getManualLoginUrl(): string {\n const client = new OAuthClient()\n return client.getAuthUrl()\n}\n\n/**\n * Complete manual login with an authorization code\n */\nexport async function completeManualLogin(code: string): Promise<string> {\n const client = new OAuthClient()\n const tokens = await client.exchangeCode(code)\n\n // Fetch the user's email from Google\n let email = tokens.email\n if (!email) {\n try {\n const userInfo = await client.fetchUserInfo()\n email = userInfo.email\n } catch (err) {\n debug('auth', `Failed to fetch user info: ${err}`)\n }\n }\n if (!email) {\n throw new AuthError('Could not determine user email from Google account')\n }\n tokens.email = email\n saveAccountTokens(email, tokens)\n setActiveAccount(email)\n return email\n}\n"],"mappings":";;;AAMA,OAAO,WAAW;AAClB,SAAS,WAAW;;;ACHpB,IAAI,WAAW;AAER,SAAS,WAAW,SAAwB;AACjD,aAAW;AACb;AAEO,SAAS,MAAM,QAAgB,KAAa,MAAsB;AACvE,MAAI,CAAC,SAAU;AACf,QAAM,SAAS,IAAI,MAAM;AACzB,MAAI,SAAS,QAAW;AACtB,YAAQ,MAAM,GAAG,MAAM,IAAI,GAAG,IAAI,IAAI;AAAA,EACxC,OAAO;AACL,YAAQ,MAAM,GAAG,MAAM,IAAI,GAAG,EAAE;AAAA,EAClC;AACF;;;ACXO,IAAM,uBAAuB;AAAA;AAAA,EAElC,gBAAgB;AAAA;AAAA,EAGhB,aAAa;AAAA;AAAA,EAGb,sBAAsB;AAAA;AAAA,EAGtB,mBAAmB;AAAA;AAAA,EAGnB,0BAA0B;AAC5B;AAEO,IAAM,0BAA0B;AAAA,EACrC,UAAU;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,YAAY;AAAA,EACd;AACF;AAEO,IAAM,kBAAkB;AACxB,IAAM,sBAAsB;AAC5B,IAAM,eAAe;AAAA,EAC1B;AACF,EAAE,KAAK,GAAG;AACH,IAAM,qBAAqB;;;AC/BlC,SAAS,gBAAAA,eAAc,iBAAAC,gBAAe,cAAAC,aAAwB,aAAAC,kBAAiB;AAC/E,SAAS,QAAAC,OAAM,eAAe;;;ACJ9B,SAAS,cAAc,eAAe,WAAW,kBAAkB;AACnE,SAAS,YAAY;AACrB,SAAS,eAAe;AAIxB,IAAM,kBAAkB;AAExB,IAAM,iBAA4B;AAAA,EAChC,aAAa;AAAA,EACb,YAAY,IAAI,KAAK;AAAA;AAAA,EACrB,mBAAmB,IAAI,KAAK;AAAA,EAC5B,cAAc,CAAC,qBAAqB,kBAAkB,kBAAkB;AAAA,EACxE,gBAAgB,CAAC;AAAA,EACjB,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,SAAS,CAAC;AAAA,EACV,QAAQ;AAAA,IACN,gBAAgB,KAAK;AAAA,IACrB,SAAS;AAAA,EACX;AACF;AAEA,SAAS,eAAuB;AAC9B,QAAM,OAAO,QAAQ;AACrB,UAAQ,QAAQ,UAAU;AAAA,IACxB,KAAK;AACH,aAAO,KAAK,MAAM,WAAW,uBAAuB,eAAe;AAAA,IACrE,KAAK;AACH,aAAO,KAAK,QAAQ,IAAI,WAAW,KAAK,MAAM,WAAW,SAAS,GAAG,eAAe;AAAA,IACtF;AACE,aAAO,KAAK,MAAM,WAAW,eAAe;AAAA,EAChD;AACF;AAEA,SAAS,gBAAwB;AAC/B,SAAO,KAAK,aAAa,GAAG,aAAa;AAC3C;AAEA,SAAS,aAAqB;AAC5B,SAAO,KAAK,aAAa,GAAG,MAAM;AACpC;AAEO,SAAS,aAAmB;AACjC,QAAM,YAAY,aAAa;AAC/B,QAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,WAAW,SAAS,EAAG,WAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACpE,MAAI,CAAC,WAAW,OAAO,EAAG,WAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAClE;AAEO,SAAS,aAAwB;AACtC,MAAI;AACF,UAAM,OAAO,cAAc;AAC3B,QAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,UAAM,MAAM,aAAa,MAAM,OAAO;AACtC,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAM,UAAU,sBAAsB,IAAI,EAAE;AAC5C,WAAO,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAAA,EACxC,SAAS,KAAK;AACZ,UAAM,UAAU,yCAAyC,GAAG;AAC5D,WAAO;AAAA,EACT;AACF;AAEO,SAAS,WAAW,QAAyB;AAClD,aAAW;AACX,QAAM,OAAO,cAAc;AAC3B,gBAAc,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AACnD,QAAM,UAAU,mBAAmB,IAAI,EAAE;AAC3C;AAEO,SAAS,mBAA2B;AACzC,SAAO,aAAa;AACtB;AAEO,SAAS,iBAAyB;AACvC,SAAO,WAAW;AACpB;;;ADtEA,IAAM,aAAa;AAanB,SAAS,eAAuB;AAC9B,SAAOC,MAAK,iBAAiB,GAAG,YAAY,aAAa;AAC3D;AAEA,SAAS,YAAwB;AAC/B,QAAM,OAAO,aAAa;AAC1B,MAAI,CAACC,YAAW,IAAI,EAAG,QAAO,EAAE,UAAU,CAAC,EAAE;AAC7C,MAAI;AACF,WAAO,KAAK,MAAMC,cAAa,MAAM,OAAO,CAAC;AAAA,EAC/C,QAAQ;AACN,WAAO,EAAE,UAAU,CAAC,EAAE;AAAA,EACxB;AACF;AAEA,SAAS,UAAU,OAAyB;AAC1C,aAAW;AACX,QAAM,YAAY,aAAa;AAC/B,EAAAC,WAAU,QAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AACjD,EAAAC,eAAc,WAAW,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AACvD,QAAM,mBAAmB,SAAS,MAAM,SAAS,MAAM,aAAa;AACtE;AAKO,SAAS,kBAAkB,OAAe,QAA4B;AAC3E,QAAM,QAAQ,UAAU;AACxB,QAAM,WAAW,MAAM,SAAS,UAAU,OAAK,EAAE,UAAU,KAAK;AAChE,QAAM,UAAwB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,EAClC;AAEA,MAAI,YAAY,GAAG;AACjB,UAAM,SAAS,QAAQ,IAAI;AAAA,EAC7B,OAAO;AACL,UAAM,SAAS,KAAK,OAAO;AAAA,EAC7B;AAGA,MAAI,MAAM,SAAS,WAAW,GAAG;AAC/B,UAAM,cAAc;AAAA,EACtB;AAEA,YAAU,KAAK;AACjB;AAKO,SAAS,iBAAiB,OAAoC;AACnE,QAAM,QAAQ,UAAU;AACxB,QAAM,UAAU,MAAM,SAAS,KAAK,OAAK,EAAE,UAAU,KAAK;AAC1D,SAAO,SAAS,UAAU;AAC5B;AAKO,SAAS,yBAA8C;AAC5D,QAAM,QAAQ,UAAU;AACxB,MAAI,CAAC,MAAM,YAAa,QAAO;AAC/B,QAAM,UAAU,MAAM,SAAS,KAAK,OAAK,EAAE,UAAU,MAAM,WAAW;AACtE,SAAO,SAAS,UAAU;AAC5B;AAKO,SAAS,iBAAiB,OAAwB;AACvD,QAAM,QAAQ,UAAU;AACxB,MAAI,CAAC,MAAM,SAAS,KAAK,OAAK,EAAE,UAAU,KAAK,EAAG,QAAO;AACzD,QAAM,cAAc;AACpB,YAAU,KAAK;AACf,SAAO;AACT;AAKO,SAAS,cAAc,OAAwB;AACpD,QAAM,QAAQ,UAAU;AACxB,QAAM,MAAM,MAAM,SAAS,UAAU,OAAK,EAAE,UAAU,KAAK;AAC3D,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,SAAS,OAAO,KAAK,CAAC;AAC5B,MAAI,MAAM,gBAAgB,OAAO;AAC/B,UAAM,cAAc,MAAM,SAAS,CAAC,GAAG;AAAA,EACzC;AACA,YAAU,KAAK;AACf,SAAO;AACT;AAKO,SAAS,eAA6E;AAC3F,QAAM,QAAQ,UAAU;AACxB,SAAO,MAAM,SAAS,IAAI,QAAM;AAAA,IAC9B,OAAO,EAAE;AAAA,IACT,UAAU,EAAE,UAAU,MAAM;AAAA,IAC5B,SAAS,EAAE;AAAA,EACb,EAAE;AACJ;AAKO,SAAS,sBAAsC;AACpD,QAAM,QAAQ,UAAU;AACxB,SAAO,MAAM,SAAS,IAAI,OAAK,EAAE,MAAM;AACzC;;;AHrHO,IAAM,cAAN,MAAkB;AAAA,EACf,SAA8B;AAAA,EAEtC,YAAY,QAAuB;AACjC,SAAK,SAAS,UAAU;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,aAA8B;AACvC,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,WAAW;AAAA,MACX,cAAc,eAAe;AAAA,MAC7B,eAAe;AAAA,MACf,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,IACV,CAAC;AACD,WAAO,gDAAgD,OAAO,SAAS,CAAC;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,MAAc,aAA6C;AAC5E,UAAM,OAAO,IAAI,gBAAgB;AAAA,MAC/B;AAAA,MACA,WAAW;AAAA,MACX,eAAe;AAAA,MACf,cAAc,eAAe;AAAA,MAC7B,YAAY;AAAA,IACd,CAAC;AAED,UAAM,WAAW,MAAM,KAAK,eAAe,uCAAuC,IAAI;AACtF,UAAM,OAAO;AAEb,SAAK,SAAS;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB,cAAc,KAAK,iBAAiB;AAAA,MACpC,WAAW,KAAK,IAAI,IAAI,KAAK,aAAa;AAAA,IAC5C;AAEA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBAA4C;AAChD,QAAI,CAAC,KAAK,QAAQ,cAAc;AAC9B,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,UAAM,OAAO,IAAI,gBAAgB;AAAA,MAC/B,WAAW;AAAA,MACX,eAAe;AAAA,MACf,eAAe,KAAK,OAAO;AAAA,MAC3B,YAAY;AAAA,IACd,CAAC;AAED,UAAM,WAAW,MAAM,KAAK,eAAe,uCAAuC,IAAI;AACtF,UAAM,OAAO;AAEb,SAAK,SAAS;AAAA,MACZ,GAAG,KAAK;AAAA,MACR,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK,IAAI,IAAI,KAAK,aAAa;AAAA,IAC5C;AAGA,QAAI,KAAK,OAAO,OAAO;AACrB,UAAI;AACF,0BAAkB,KAAK,OAAO,OAAO,KAAK,MAAM;AAChD,cAAM,SAAS,iCAAiC,KAAK,OAAO,KAAK,EAAE;AAAA,MACrE,SAAS,KAAK;AACZ,cAAM,SAAS,sCAAsC,GAAG,EAAE;AAAA,MAC5D;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAkC;AACtC,QAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,mBAAmB;AACrD,QAAI,KAAK,IAAI,KAAK,KAAK,OAAO,YAAY,KAAO;AAC/C,YAAM,KAAK,mBAAmB;AAAA,IAChC;AACA,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAiC;AAC/B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAsC;AAC1C,UAAM,QAAQ,MAAM,KAAK,eAAe;AACxC,WAAO,KAAK;AAAA,MACV,qBAAqB;AAAA,MACrB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,kBAAoC;AACxC,UAAM,QAAQ,MAAM,KAAK,eAAe;AACxC,WAAO,KAAK;AAAA,MACV,qBAAqB;AAAA,MACrB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,uBAAyC;AAC7C,UAAM,QAAQ,MAAM,KAAK,eAAe;AACxC,WAAO,KAAK;AAAA,MACV,qBAAqB;AAAA,MACrB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAA4D;AAChE,UAAM,QAAQ,MAAM,KAAK,eAAe;AACxC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAgC;AAAA,QACpC,UAAU;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,iBAAiB,UAAU,KAAK;AAAA,QAClC;AAAA,QACA,SAAS;AAAA,MACX;AAEA,YAAM,MAAM,MAAM,QAAQ,SAAS,CAAC,QAAQ;AAC1C,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAkB;AAAE,kBAAQ;AAAA,QAAM,CAAC;AACnD,YAAI,GAAG,OAAO,MAAM;AAClB,cAAI;AACF,kBAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,oBAAQ,EAAE,OAAO,KAAK,OAAO,MAAM,KAAK,KAAK,CAAC;AAAA,UAChD,QAAQ;AACN,oBAAQ,CAAC,CAAC;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AACD,UAAI,GAAG,SAAS,MAAM,QAAQ,CAAC,CAAC,CAAC;AACjC,UAAI,GAAG,WAAW,MAAM;AAAE,YAAI,QAAQ;AAAG,gBAAQ,CAAC,CAAC;AAAA,MAAE,CAAC;AACtD,UAAI,IAAI;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA,EAIQ,kBAAkB,KAAa,MAAe,OAAiC;AACrF,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,YAAM,OAAO,KAAK,UAAU,IAAI;AAChC,YAAM,UAAgC;AAAA,QACpC,UAAU,OAAO;AAAA,QACjB,MAAM,OAAO;AAAA,QACb,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB,UAAU,KAAK;AAAA,UAChC,kBAAkB,OAAO,WAAW,IAAI;AAAA,QAC1C;AAAA,QACA,SAAS;AAAA,MACX;AAEA,YAAM,MAAM,MAAM,QAAQ,SAAS,CAAC,QAAQ;AAC1C,YAAI,eAAe;AACnB,YAAI,GAAG,QAAQ,CAAC,UAAkB;AAAE,0BAAgB;AAAA,QAAM,CAAC;AAC3D,YAAI,GAAG,OAAO,MAAM;AAClB,cAAI,IAAI,eAAe,IAAI,aAAa,OAAO,IAAI,cAAc,MAAM;AACrE,mBAAO,IAAI,MAAM,QAAQ,IAAI,UAAU,KAAK,aAAa,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;AACzE;AAAA,UACF;AACA,cAAI;AACF,oBAAQ,KAAK,MAAM,YAAY,CAAC;AAAA,UAClC,QAAQ;AACN,mBAAO,IAAI,MAAM,iBAAiB,aAAa,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;AAAA,UACjE;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AACD,UAAI,GAAG,SAAS,MAAM;AACtB,UAAI,GAAG,WAAW,MAAM;AAAE,YAAI,QAAQ;AAAG,eAAO,IAAI,MAAM,SAAS,CAAC;AAAA,MAAE,CAAC;AACvE,UAAI,MAAM,IAAI;AACd,UAAI,IAAI;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEQ,eAAe,KAAa,MAAyC;AAC3E,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,YAAM,OAAO,KAAK,SAAS;AAC3B,YAAM,UAAgC;AAAA,QACpC,UAAU,OAAO;AAAA,QACjB,MAAM,OAAO;AAAA,QACb,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,QAC1C;AAAA,QACA,SAAS;AAAA,MACX;AAEA,YAAM,MAAM,MAAM,QAAQ,SAAS,CAAC,QAAQ;AAC1C,YAAI,eAAe;AACnB,YAAI,GAAG,QAAQ,CAAC,UAAkB;AAAE,0BAAgB;AAAA,QAAM,CAAC;AAC3D,YAAI,GAAG,OAAO,MAAM;AAClB,cAAI,IAAI,eAAe,IAAI,aAAa,OAAO,IAAI,cAAc,MAAM;AACrE,mBAAO,IAAI,MAAM,QAAQ,IAAI,UAAU,KAAK,aAAa,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;AACzE;AAAA,UACF;AACA,cAAI;AACF,oBAAQ,KAAK,MAAM,YAAY,CAAC;AAAA,UAClC,QAAQ;AACN,mBAAO,IAAI,MAAM,iBAAiB,aAAa,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;AAAA,UACjE;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AACD,UAAI,GAAG,SAAS,MAAM;AACtB,UAAI,GAAG,WAAW,MAAM;AAAE,YAAI,QAAQ;AAAG,eAAO,IAAI,MAAM,SAAS,CAAC;AAAA,MAAE,CAAC;AACvE,UAAI,MAAM,IAAI;AACd,UAAI,IAAI;AAAA,IACV,CAAC;AAAA,EACH;AACF;;;AKjQO,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAChD,YAAY,QAAgB,QAAiB;AAC3C,UAAM,WAAW,MAAM,gBAAgB,SAAS,KAAK,MAAM,KAAK,EAAE,EAAE;AACpE,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACRA,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,SAAS,OAAAC,YAAW;AAQpB,eAAsB,eAAgC;AACpD,QAAM,SAAS,IAAI,YAAY;AAE/B,QAAM,QAAQ,2BAA2B;AAGzC,MAAI,sBAAsB;AAC1B,QAAM,OAAO,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC1D,UAAM,SAAS,KAAK,aAAa,OAAO,KAAK,QAAQ;AACnD,YAAM,MAAM,IAAIA,KAAI,IAAI,OAAO,KAAK,kBAAkB;AACtD,YAAMC,QAAO,IAAI,aAAa,IAAI,MAAM;AAExC,UAAIA,OAAM;AACR,YAAI,UAAU,KAAK,EAAE,gBAAgB,2BAA2B,CAAC;AACjE,YAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,SAKP;AACD,eAAO,MAAM;AACb,gBAAQA,KAAI;AAAA,MACd,OAAO;AACL,cAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAC1C,YAAI,OAAO;AACT,cAAI,UAAU,KAAK,EAAE,gBAAgB,2BAA2B,CAAC;AACjE,cAAI,IAAI,qBAAqB,KAAK,OAAO;AACzC,iBAAO,MAAM;AACb,iBAAO,IAAI,UAAU,gBAAgB,KAAK,EAAE,CAAC;AAAA,QAC/C,OAAO;AACL,cAAI,UAAU,GAAG;AACjB,cAAI,IAAI,+BAA+B;AAAA,QACzC;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,OAAO,GAAG,aAAa,MAAM;AAClC,YAAM,OAAO,OAAO,QAAQ;AAC5B,UAAI,OAAO,SAAS,YAAY,MAAM;AACpC,8BAAsB,oBAAoB,KAAK,IAAI;AACnD,cAAM,QAAQ,2BAA2B,KAAK,IAAI,EAAE;AAGpD,cAAM,UAAU,OAAO,WAAW,mBAAmB;AAGrD,aAAK,OAAO,EAAE,MAAM,MAAM;AACxB,kBAAQ,IAAI;AAAA;AAAA;AAAA,EAAuC,OAAO;AAAA,CAAI;AAAA,QAChE,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,eAAW,MAAM;AACf,aAAO,MAAM;AACb,aAAO,IAAI,UAAU,iCAAiC,CAAC;AAAA,IACzD,GAAG,IAAI,KAAK,GAAI;AAAA,EAClB,CAAC;AAGD,QAAM,SAAS,MAAM,OAAO,aAAa,MAAM,mBAAmB;AAGlE,MAAI,QAAQ,OAAO;AACnB,MAAI,CAAC,OAAO;AACV,QAAI;AACF,YAAM,WAAW,MAAM,OAAO,cAAc;AAC5C,cAAQ,SAAS;AAAA,IACnB,SAAS,KAAK;AACZ,YAAM,QAAQ,8BAA8B,GAAG,EAAE;AAAA,IACnD;AAAA,EACF;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,UAAU,oDAAoD;AAAA,EAC1E;AACA,SAAO,QAAQ;AAGf,oBAAkB,OAAO,MAAM;AAC/B,mBAAiB,KAAK;AAEtB,QAAM,QAAQ,gBAAgB,KAAK,EAAE;AACrC,SAAO;AACT;AAMO,SAAS,oBAA4B;AAC1C,QAAM,SAAS,IAAI,YAAY;AAC/B,SAAO,OAAO,WAAW;AAC3B;AAKA,eAAsB,oBAAoB,MAA+B;AACvE,QAAM,SAAS,IAAI,YAAY;AAC/B,QAAM,SAAS,MAAM,OAAO,aAAa,IAAI;AAG7C,MAAI,QAAQ,OAAO;AACnB,MAAI,CAAC,OAAO;AACV,QAAI;AACF,YAAM,WAAW,MAAM,OAAO,cAAc;AAC5C,cAAQ,SAAS;AAAA,IACnB,SAAS,KAAK;AACZ,YAAM,QAAQ,8BAA8B,GAAG,EAAE;AAAA,IACnD;AAAA,EACF;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,UAAU,oDAAoD;AAAA,EAC1E;AACA,SAAO,QAAQ;AACf,oBAAkB,OAAO,MAAM;AAC/B,mBAAiB,KAAK;AACtB,SAAO;AACT;","names":["readFileSync","writeFileSync","existsSync","mkdirSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","URL","code"]}
package/dist/cli/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import "../chunk-H7BEO7IF.js";
2
3
  import {
3
4
  OAuthClient,
4
5
  SourceUnavailableError,
@@ -15,7 +16,7 @@ import {
15
16
  saveConfig,
16
17
  setActiveAccount,
17
18
  setVerbose
18
- } from "../chunk-24ILGZJ2.js";
19
+ } from "../chunk-VLP22VAR.js";
19
20
 
20
21
  // src/cli/index.ts
21
22
  import { Command } from "commander";
@@ -423,7 +424,7 @@ var ConnectClient = class {
423
424
  * Fetch model configs (last-resort fallback)
424
425
  */
425
426
  async fetchModelConfigs() {
426
- debug("connect-client", "Fetching GetCascadeModelConfigData");
427
+ debug("connect-client", "Fetching GetCommandModelConfigs");
427
428
  try {
428
429
  return await this.request("POST", ENDPOINTS.modelConfigs, {
429
430
  metadata: {
@@ -570,11 +571,11 @@ var ConnectClient = class {
570
571
  };
571
572
 
572
573
  // src/sources/local/parser.ts
573
- function parseQuotaSummary(summary, email) {
574
+ function parseQuotaSummary(summary, email, method = "local") {
574
575
  debug("parser", `Parsing quota summary: ${summary.groups.length} groups`);
575
576
  return {
576
577
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
577
- method: "local",
578
+ method,
578
579
  email,
579
580
  groups: summary.groups.map((g) => ({
580
581
  displayName: g.displayName,
@@ -589,11 +590,11 @@ function parseQuotaSummary(summary, email) {
589
590
  }))
590
591
  };
591
592
  }
592
- function parseUserStatus(userStatus) {
593
+ function parseUserStatus(userStatus, method = "local") {
593
594
  debug("parser", "Parsing GetUserStatus fallback");
594
595
  const snapshot = {
595
596
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
596
- method: "local",
597
+ method,
597
598
  email: userStatus.email,
598
599
  groups: []
599
600
  };
@@ -667,7 +668,7 @@ function isGeminiModel(modelId) {
667
668
  }
668
669
  function detectAutocomplete(modelId, label) {
669
670
  const text = `${modelId} ${label || ""}`.toLowerCase();
670
- return text.includes("autocomplete") || text.includes("gemini-2.5") || text.includes("gemini 2.5");
671
+ return text.includes("autocomplete");
671
672
  }
672
673
  function clampFraction(value) {
673
674
  if (value === void 0 || value === null) return 1;
@@ -841,10 +842,10 @@ var CliSource = class {
841
842
  const summary = await client.fetchQuotaSummary();
842
843
  if (summary && summary.groups.length > 0) {
843
844
  const email = (await client.fetchUserStatus())?.email;
844
- return parseQuotaSummary(summary, email);
845
+ return parseQuotaSummary(summary, email, "cli");
845
846
  }
846
847
  const userStatus = await client.fetchUserStatus();
847
- if (userStatus) return parseUserStatus(userStatus);
848
+ if (userStatus) return parseUserStatus(userStatus, "cli");
848
849
  } catch {
849
850
  }
850
851
  }
@@ -903,20 +904,25 @@ var OAuthSource = class {
903
904
  }
904
905
  async fetchQuota() {
905
906
  debug("oauth", "Fetching quota via Cloud Code API");
907
+ let email;
908
+ try {
909
+ const userInfo = await this.client.fetchUserInfo();
910
+ email = userInfo.email;
911
+ } catch {
912
+ debug("oauth", "Could not fetch user info for email");
913
+ }
906
914
  try {
907
915
  const summaryRaw = await this.client.fetchQuotaSummary();
908
916
  const summary = this.parseRemoteSummary(summaryRaw);
909
917
  if (summary && summary.groups.length > 0) {
910
- const statusRaw2 = await this.client.fetchUserStatus();
911
- const email = this.extractEmail(statusRaw2);
912
918
  debug("oauth", "Got quota summary via OAuth");
913
- return parseQuotaSummary(summary, email);
919
+ return parseQuotaSummary(summary, email, "oauth");
914
920
  }
915
921
  const statusRaw = await this.client.fetchUserStatus();
916
922
  const status = this.parseRemoteStatus(statusRaw);
917
923
  if (status) {
918
924
  debug("oauth", "Got user status via OAuth");
919
- return parseUserStatus(status);
925
+ return parseUserStatus(status, "oauth");
920
926
  }
921
927
  } catch (err) {
922
928
  debug("oauth", `Fetch failed: ${err}`);
@@ -950,13 +956,12 @@ var OAuthSource = class {
950
956
  */
951
957
  parseRemoteStatus(raw) {
952
958
  if (typeof raw !== "object" || raw === null) return null;
953
- return { raw };
954
- }
955
- extractEmail(raw) {
956
- if (typeof raw !== "object" || raw === null) return void 0;
957
959
  const data = raw;
958
- const userStatus = data.userStatus;
959
- return userStatus?.email || data.email;
960
+ const userStatus = data.userStatus || data;
961
+ return {
962
+ email: userStatus.email,
963
+ raw
964
+ };
960
965
  }
961
966
  };
962
967
 
@@ -1230,17 +1235,160 @@ function closeStore() {
1230
1235
  }
1231
1236
  }
1232
1237
 
1238
+ // src/plugins/builtin/notifications.ts
1239
+ var NotificationPlugin = class {
1240
+ name = "notifications";
1241
+ version = "1.0.0";
1242
+ notifier = null;
1243
+ notifiedLow = /* @__PURE__ */ new Set();
1244
+ async onInit(ctx) {
1245
+ try {
1246
+ const { default: notifier } = await import("node-notifier");
1247
+ this.notifier = notifier;
1248
+ debug("notifications", "Notification plugin initialized");
1249
+ } catch {
1250
+ debug("notifications", "node-notifier not available, notifications disabled");
1251
+ }
1252
+ }
1253
+ async onQuotaUpdate(snapshot, ctx) {
1254
+ for (const group of snapshot.groups) {
1255
+ for (const bucket of group.buckets) {
1256
+ if (bucket.remainingFraction > 0.8) {
1257
+ this.notifiedLow.delete(`${group.displayName}:${bucket.displayName}`);
1258
+ }
1259
+ }
1260
+ }
1261
+ }
1262
+ async onQuotaLow(group, bucket, remaining, ctx) {
1263
+ const key = `${group}:${bucket}`;
1264
+ if (this.notifiedLow.has(key)) return;
1265
+ const pct = Math.round(remaining * 100);
1266
+ const title = "when-does-my-quota-refresh";
1267
+ const message = `\u26A0\uFE0F Low quota: ${group} \u2014 ${bucket} at ${pct}%`;
1268
+ ctx.log(message);
1269
+ this.notify(title, message);
1270
+ this.notifiedLow.add(key);
1271
+ }
1272
+ async onReset(group, nextReset, ctx) {
1273
+ const title = "when-does-my-quota-refresh";
1274
+ const message = `\u2705 ${group} quota has reset!`;
1275
+ ctx.log(message);
1276
+ this.notify(title, message);
1277
+ }
1278
+ notify(title, message) {
1279
+ if (!this.notifier) return;
1280
+ try {
1281
+ this.notifier.notify({ title, message, timeout: 5 });
1282
+ } catch (err) {
1283
+ debug("notifications", `Notification failed: ${err}`);
1284
+ }
1285
+ }
1286
+ };
1287
+
1288
+ // src/plugins/index.ts
1289
+ var PluginRegistry = class {
1290
+ plugins = [];
1291
+ context;
1292
+ constructor(context) {
1293
+ this.context = {
1294
+ notify: context?.notify || (() => {
1295
+ }),
1296
+ log: context?.log || ((msg) => console.log(msg))
1297
+ };
1298
+ }
1299
+ /**
1300
+ * Register a plugin
1301
+ */
1302
+ async register(plugin) {
1303
+ this.plugins.push(plugin);
1304
+ if (plugin.onInit) {
1305
+ await plugin.onInit(this.context);
1306
+ }
1307
+ debug("plugins", `Registered: ${plugin.name}@${plugin.version}`);
1308
+ }
1309
+ /**
1310
+ * Load built-in plugins
1311
+ */
1312
+ async loadBuiltins() {
1313
+ await this.register(new NotificationPlugin());
1314
+ }
1315
+ /**
1316
+ * Notify all plugins of a quota update
1317
+ */
1318
+ async notifyQuotaUpdate(snapshot) {
1319
+ for (const plugin of this.plugins) {
1320
+ if (plugin.onQuotaUpdate) {
1321
+ try {
1322
+ await plugin.onQuotaUpdate(snapshot, this.context);
1323
+ } catch (err) {
1324
+ debug("plugins", `Error in ${plugin.name}.onQuotaUpdate: ${err}`);
1325
+ }
1326
+ }
1327
+ }
1328
+ for (const group of snapshot.groups) {
1329
+ for (const bucket of group.buckets) {
1330
+ if (bucket.remainingFraction < 0.15) {
1331
+ await this.notifyQuotaLow(group.displayName, bucket.displayName, bucket.remainingFraction);
1332
+ }
1333
+ }
1334
+ }
1335
+ }
1336
+ /**
1337
+ * Notify all plugins of low quota
1338
+ */
1339
+ async notifyQuotaLow(group, bucket, remaining) {
1340
+ for (const plugin of this.plugins) {
1341
+ if (plugin.onQuotaLow) {
1342
+ try {
1343
+ await plugin.onQuotaLow(group, bucket, remaining, this.context);
1344
+ } catch (err) {
1345
+ debug("plugins", `Error in ${plugin.name}.onQuotaLow: ${err}`);
1346
+ }
1347
+ }
1348
+ }
1349
+ }
1350
+ /**
1351
+ * Destroy all plugins
1352
+ */
1353
+ async destroy() {
1354
+ for (const plugin of this.plugins) {
1355
+ if (plugin.onDestroy) {
1356
+ try {
1357
+ await plugin.onDestroy();
1358
+ } catch (err) {
1359
+ debug("plugins", `Error in ${plugin.name}.onDestroy: ${err}`);
1360
+ }
1361
+ }
1362
+ }
1363
+ this.plugins = [];
1364
+ }
1365
+ /**
1366
+ * Get registered plugin names
1367
+ */
1368
+ getNames() {
1369
+ return this.plugins.map((p) => p.name);
1370
+ }
1371
+ };
1372
+
1233
1373
  // src/sdk/quota.ts
1234
1374
  var QuotaClient = class {
1235
1375
  registry;
1236
1376
  cache;
1377
+ plugins;
1237
1378
  verbose;
1238
1379
  constructor(options) {
1239
1380
  this.verbose = options?.verbose || false;
1240
- setVerbose(this.verbose);
1381
+ if (options?.verbose !== void 0) {
1382
+ setVerbose(this.verbose);
1383
+ }
1241
1384
  const config = loadConfig();
1242
1385
  this.registry = new SourceRegistry();
1243
1386
  this.cache = new QuotaCache(options?.cacheTtlMs || config.cacheTtlMs);
1387
+ this.plugins = new PluginRegistry();
1388
+ if (config.notifications) {
1389
+ this.plugins.loadBuiltins().catch(() => {
1390
+ });
1391
+ }
1244
1392
  this.registerOAuthSources();
1245
1393
  }
1246
1394
  /**
@@ -1251,11 +1399,19 @@ var QuotaClient = class {
1251
1399
  return this.fetchAllAccounts(options);
1252
1400
  }
1253
1401
  if (!options?.refresh) {
1402
+ const tokens = getAllAccountTokens();
1403
+ for (const token of tokens) {
1404
+ if (!token.email) continue;
1405
+ const cached = this.cache.get(token.email, options?.source || "auto");
1406
+ if (cached) return cached;
1407
+ }
1254
1408
  }
1255
1409
  const snapshot = await this.registry.fetchQuota(options?.source);
1256
- if (snapshot.email) {
1410
+ if (snapshot.email && snapshot.email !== "unknown") {
1257
1411
  this.cache.set(snapshot.email, snapshot.method, snapshot);
1258
1412
  storeSnapshot(snapshot);
1413
+ this.plugins.notifyQuotaUpdate(snapshot).catch(() => {
1414
+ });
1259
1415
  }
1260
1416
  return snapshot;
1261
1417
  }
@@ -1659,8 +1815,8 @@ function registerAccountsCommand(program2) {
1659
1815
  accounts.command("add").description("Login a new Google account").option("--manual", "Manual login for headless/SSH (get URL instead of browser)").action(async (opts) => {
1660
1816
  try {
1661
1817
  if (opts.manual) {
1662
- const { getManualLoginUrl: getManualLoginUrl2, completeManualLogin: completeManualLogin2 } = await import("../auth-K5ETZYGY.js");
1663
- const url = getManualLoginUrl2();
1818
+ const { getManualLoginUrl, completeManualLogin } = await import("../auth-GX4ZIG5O.js");
1819
+ const url = getManualLoginUrl();
1664
1820
  console.log(chalk6.bold("\nOpen this URL in your browser:\n"));
1665
1821
  console.log(chalk6.cyan(url));
1666
1822
  console.log(chalk6.dim("\nPaste the authorization code here:"));
@@ -1669,7 +1825,7 @@ function registerAccountsCommand(program2) {
1669
1825
  name: "code",
1670
1826
  message: "Authorization code:"
1671
1827
  }]);
1672
- const email = await completeManualLogin2(code);
1828
+ const email = await completeManualLogin(code);
1673
1829
  console.log(chalk6.green(`
1674
1830
  \u2713 Logged in as ${email}`));
1675
1831
  } else {
@@ -2086,7 +2242,15 @@ function registerWakeupCommand(program2) {
2086
2242
  default: true
2087
2243
  }
2088
2244
  ]);
2089
- config.wakeupAccounts = answers.allAccounts ? [] : [];
2245
+ config.wakeupMode = answers.mode;
2246
+ if (answers.intervalHours) config.wakeupIntervalHours = answers.intervalHours;
2247
+ if (answers.dailyTimes) config.wakeupDailyTimes = answers.dailyTimes;
2248
+ if (!answers.allAccounts) {
2249
+ const { listAccounts: listAccounts2 } = await import("../accounts-FH6AKZT7.js");
2250
+ config.wakeupAccounts = listAccounts2().map((a) => a.email);
2251
+ } else {
2252
+ config.wakeupAccounts = [];
2253
+ }
2090
2254
  saveConfig(config);
2091
2255
  console.log(chalk11.green("\n \u2713 Configuration saved"));
2092
2256
  console.log(chalk11.dim(` Run \`aq wakeup install\` to activate`));
@@ -2120,8 +2284,11 @@ function registerWakeupCommand(program2) {
2120
2284
  wakeup.command("status").description("Check wakeup status").action(() => {
2121
2285
  const config = loadConfig();
2122
2286
  console.log(chalk11.bold("\n Wakeup Status\n"));
2287
+ console.log(` Mode: ${config.wakeupMode}`);
2123
2288
  console.log(` Models: ${config.wakeupModels.join(", ")}`);
2124
2289
  console.log(` Accounts: ${config.wakeupAccounts.length > 0 ? config.wakeupAccounts.join(", ") : "all"}`);
2290
+ if (config.wakeupMode === "interval") console.log(` Interval: every ${config.wakeupIntervalHours}h`);
2291
+ if (config.wakeupMode === "daily") console.log(` Daily at: ${config.wakeupDailyTimes}`);
2125
2292
  if (isWindows()) {
2126
2293
  const installed = checkWindowsTask();
2127
2294
  console.log(` Scheduler: ${installed ? chalk11.green("Installed") : chalk11.dim("Not installed")}`);
@@ -2142,12 +2309,32 @@ function registerWakeupCommand(program2) {
2142
2309
  }
2143
2310
  });
2144
2311
  }
2312
+ function escapeShellArg(arg) {
2313
+ return arg.replace(/'/g, "'\\''");
2314
+ }
2145
2315
  function installCrontab() {
2146
2316
  const aqPath = process.argv[1];
2317
+ const safeAqPath = escapeShellArg(aqPath);
2147
2318
  const config = loadConfig();
2148
2319
  uninstallCrontab();
2149
- const intervalHours = 6;
2150
- const cronLine = `0 */${intervalHours} * * * "${aqPath}" quota --refresh --json > /dev/null 2>&1 ${CRON_MARKER}`;
2320
+ const intervalHours = config.wakeupIntervalHours || 6;
2321
+ let cronLine;
2322
+ if (config.wakeupMode === "daily" && config.wakeupDailyTimes) {
2323
+ const hours = config.wakeupDailyTimes.split(",").map((h) => parseInt(h.trim(), 10)).filter((h) => !isNaN(h));
2324
+ for (const hour of hours) {
2325
+ cronLine = `0 ${hour} * * * "${safeAqPath}" quota --refresh --json > /dev/null 2>&1 ${CRON_MARKER}`;
2326
+ try {
2327
+ const existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" }).trim();
2328
+ const newCron = existing ? `${existing}
2329
+ ${cronLine}` : cronLine;
2330
+ execSync(`echo '${newCron}' | crontab -`, { encoding: "utf-8" });
2331
+ } catch {
2332
+ execSync(`echo '${cronLine}' | crontab -`, { encoding: "utf-8" });
2333
+ }
2334
+ }
2335
+ return;
2336
+ }
2337
+ cronLine = `0 */${intervalHours} * * * "${safeAqPath}" quota --refresh --json > /dev/null 2>&1 ${CRON_MARKER}`;
2151
2338
  try {
2152
2339
  const existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" }).trim();
2153
2340
  const newCron = existing ? `${existing}
@@ -2161,7 +2348,8 @@ function uninstallCrontab() {
2161
2348
  try {
2162
2349
  const existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
2163
2350
  const filtered = existing.split("\n").filter((l) => !l.includes(CRON_MARKER)).join("\n").trim();
2164
- execSync(`echo '${filtered}' | crontab -`, { encoding: "utf-8" });
2351
+ execSync(`printf '%s
2352
+ ' '${filtered.replace(/'/g, "'\\''")}' | crontab -`, { encoding: "utf-8" });
2165
2353
  } catch {
2166
2354
  }
2167
2355
  }
@@ -2177,12 +2365,14 @@ function installWindowsTask() {
2177
2365
  const aqPath = process.argv[1];
2178
2366
  const xmlPath = join2(homedir(), "when-does-my-quota-refresh-wakeup.xml");
2179
2367
  uninstallWindowsTask();
2368
+ const config = loadConfig();
2369
+ const intervalHours = config.wakeupIntervalHours || 6;
2180
2370
  const xml = `<?xml version="1.0" encoding="UTF-16"?>
2181
2371
  <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
2182
2372
  <Triggers>
2183
2373
  <TimeTrigger>
2184
2374
  <Repetition>
2185
- <Interval>PT6H</Interval>
2375
+ <Interval>PT${intervalHours}H</Interval>
2186
2376
  </Repetition>
2187
2377
  <StartBoundary>2024-01-01T09:00:00</StartBoundary>
2188
2378
  </TimeTrigger>