when-does-my-quota-refresh 1.1.0 → 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,27 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-H7BEO7IF.js";
3
+ import {
4
+ completeManualLogin,
5
+ getAccountTokens,
6
+ getActiveAccountTokens,
7
+ getAllAccountTokens,
8
+ getManualLoginUrl,
9
+ listAccounts,
10
+ loginAccount,
11
+ removeAccount,
12
+ saveAccountTokens,
13
+ setActiveAccount
14
+ } from "./chunk-VLP22VAR.js";
15
+ export {
16
+ completeManualLogin,
17
+ getAccountTokens,
18
+ getActiveAccountTokens,
19
+ getAllAccountTokens,
20
+ getManualLoginUrl,
21
+ listAccounts,
22
+ loginAccount,
23
+ removeAccount,
24
+ saveAccountTokens,
25
+ setActiveAccount
26
+ };
27
+ //# sourceMappingURL=accounts-FH6AKZT7.js.map
@@ -3,10 +3,10 @@ import {
3
3
  completeManualLogin,
4
4
  getManualLoginUrl,
5
5
  loginAccount
6
- } from "./chunk-ST6OOHQI.js";
6
+ } from "./chunk-VLP22VAR.js";
7
7
  export {
8
8
  completeManualLogin,
9
9
  getManualLoginUrl,
10
10
  loginAccount
11
11
  };
12
- //# sourceMappingURL=auth-XYJBJA2Q.js.map
12
+ //# sourceMappingURL=auth-GX4ZIG5O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ //# sourceMappingURL=chunk-H7BEO7IF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -4,6 +4,21 @@
4
4
  import https from "https";
5
5
  import { URL } from "url";
6
6
 
7
+ // src/core/logger.ts
8
+ var _verbose = false;
9
+ function setVerbose(verbose) {
10
+ _verbose = verbose;
11
+ }
12
+ function debug(module, msg, data) {
13
+ if (!_verbose) return;
14
+ const prefix = `[${module}]`;
15
+ if (data !== void 0) {
16
+ console.error(`${prefix} ${msg}`, data);
17
+ } else {
18
+ console.error(`${prefix} ${msg}`);
19
+ }
20
+ }
21
+
7
22
  // src/sources/cloud-code-api.ts
8
23
  var CLOUD_CODE_ENDPOINTS = {
9
24
  /** Load code assist configuration */
@@ -32,6 +47,162 @@ var OAUTH_SCOPES = [
32
47
  ].join(" ");
33
48
  var OAUTH_REDIRECT_URI = "http://127.0.0.1:0/callback";
34
49
 
50
+ // src/sdk/accounts/storage.ts
51
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
52
+ import { join as join2, dirname } from "path";
53
+
54
+ // src/core/config.ts
55
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
56
+ import { join } from "path";
57
+ import { homedir } from "os";
58
+ var CONFIG_DIR_NAME = "when-does-my-quota-refresh";
59
+ var DEFAULT_CONFIG = {
60
+ defaultMode: "local",
61
+ cacheTtlMs: 5 * 60 * 1e3,
62
+ // 5 minutes
63
+ refreshIntervalMs: 5 * 60 * 1e3,
64
+ wakeupModels: ["claude-sonnet-4-5", "gemini-3-flash", "gemini-3-pro-low"],
65
+ wakeupAccounts: [],
66
+ wakeupMode: "smart",
67
+ wakeupIntervalHours: 6,
68
+ wakeupDailyTimes: "9,17",
69
+ notifications: true,
70
+ plugins: [],
71
+ daemon: {
72
+ pollIntervalMs: 60 * 1e3,
73
+ enabled: false
74
+ }
75
+ };
76
+ function getConfigDir() {
77
+ const home = homedir();
78
+ switch (process.platform) {
79
+ case "darwin":
80
+ return join(home, "Library", "Application Support", CONFIG_DIR_NAME);
81
+ case "win32":
82
+ return join(process.env.APPDATA || join(home, "AppData", "Roaming"), CONFIG_DIR_NAME);
83
+ default:
84
+ return join(home, ".config", CONFIG_DIR_NAME);
85
+ }
86
+ }
87
+ function getConfigPath() {
88
+ return join(getConfigDir(), "config.json");
89
+ }
90
+ function getDataDir() {
91
+ return join(getConfigDir(), "data");
92
+ }
93
+ function ensureDirs() {
94
+ const configDir = getConfigDir();
95
+ const dataDir = getDataDir();
96
+ if (!existsSync(configDir)) mkdirSync(configDir, { recursive: true });
97
+ if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });
98
+ }
99
+ function loadConfig() {
100
+ try {
101
+ const path = getConfigPath();
102
+ if (!existsSync(path)) return DEFAULT_CONFIG;
103
+ const raw = readFileSync(path, "utf-8");
104
+ const parsed = JSON.parse(raw);
105
+ debug("config", `Loaded config from ${path}`);
106
+ return { ...DEFAULT_CONFIG, ...parsed };
107
+ } catch (err) {
108
+ debug("config", "Failed to load config, using defaults", err);
109
+ return DEFAULT_CONFIG;
110
+ }
111
+ }
112
+ function saveConfig(config) {
113
+ ensureDirs();
114
+ const path = getConfigPath();
115
+ writeFileSync(path, JSON.stringify(config, null, 2));
116
+ debug("config", `Saved config to ${path}`);
117
+ }
118
+ function getConfigDirPath() {
119
+ return getConfigDir();
120
+ }
121
+ function getDataDirPath() {
122
+ return getDataDir();
123
+ }
124
+
125
+ // src/sdk/accounts/storage.ts
126
+ var TOKENS_DIR = "accounts";
127
+ function getStorePath() {
128
+ return join2(getConfigDirPath(), TOKENS_DIR, "tokens.json");
129
+ }
130
+ function loadStore() {
131
+ const path = getStorePath();
132
+ if (!existsSync2(path)) return { accounts: [] };
133
+ try {
134
+ return JSON.parse(readFileSync2(path, "utf-8"));
135
+ } catch {
136
+ return { accounts: [] };
137
+ }
138
+ }
139
+ function saveStore(store) {
140
+ ensureDirs();
141
+ const storePath = getStorePath();
142
+ mkdirSync2(dirname(storePath), { recursive: true });
143
+ writeFileSync2(storePath, JSON.stringify(store, null, 2));
144
+ debug("account-storage", `Saved ${store.accounts.length} account(s)`);
145
+ }
146
+ function saveAccountTokens(email, tokens) {
147
+ const store = loadStore();
148
+ const existing = store.accounts.findIndex((a) => a.email === email);
149
+ const account = {
150
+ email,
151
+ tokens,
152
+ addedAt: (/* @__PURE__ */ new Date()).toISOString()
153
+ };
154
+ if (existing >= 0) {
155
+ store.accounts[existing] = account;
156
+ } else {
157
+ store.accounts.push(account);
158
+ }
159
+ if (store.accounts.length === 1) {
160
+ store.activeEmail = email;
161
+ }
162
+ saveStore(store);
163
+ }
164
+ function getAccountTokens(email) {
165
+ const store = loadStore();
166
+ const account = store.accounts.find((a) => a.email === email);
167
+ return account?.tokens || null;
168
+ }
169
+ function getActiveAccountTokens() {
170
+ const store = loadStore();
171
+ if (!store.activeEmail) return null;
172
+ const account = store.accounts.find((a) => a.email === store.activeEmail);
173
+ return account?.tokens || null;
174
+ }
175
+ function setActiveAccount(email) {
176
+ const store = loadStore();
177
+ if (!store.accounts.find((a) => a.email === email)) return false;
178
+ store.activeEmail = email;
179
+ saveStore(store);
180
+ return true;
181
+ }
182
+ function removeAccount(email) {
183
+ const store = loadStore();
184
+ const idx = store.accounts.findIndex((a) => a.email === email);
185
+ if (idx < 0) return false;
186
+ store.accounts.splice(idx, 1);
187
+ if (store.activeEmail === email) {
188
+ store.activeEmail = store.accounts[0]?.email;
189
+ }
190
+ saveStore(store);
191
+ return true;
192
+ }
193
+ function listAccounts() {
194
+ const store = loadStore();
195
+ return store.accounts.map((a) => ({
196
+ email: a.email,
197
+ isActive: a.email === store.activeEmail,
198
+ addedAt: a.addedAt
199
+ }));
200
+ }
201
+ function getAllAccountTokens() {
202
+ const store = loadStore();
203
+ return store.accounts.map((a) => a.tokens);
204
+ }
205
+
35
206
  // src/sources/oauth/client.ts
36
207
  var OAuthClient = class {
37
208
  tokens = null;
@@ -92,6 +263,14 @@ var OAuthClient = class {
92
263
  accessToken: data.access_token,
93
264
  expiresAt: Date.now() + data.expires_in * 1e3
94
265
  };
266
+ if (this.tokens.email) {
267
+ try {
268
+ saveAccountTokens(this.tokens.email, this.tokens);
269
+ debug("oauth", `Persisted refreshed token for ${this.tokens.email}`);
270
+ } catch (err) {
271
+ debug("oauth", `Failed to persist refreshed token: ${err}`);
272
+ }
273
+ }
95
274
  return this.tokens;
96
275
  }
97
276
  /**
@@ -137,6 +316,43 @@ var OAuthClient = class {
137
316
  token
138
317
  );
139
318
  }
319
+ /**
320
+ * Fetch the authenticated user's profile (email, name, etc.)
321
+ */
322
+ async fetchUserInfo() {
323
+ const token = await this.getAccessToken();
324
+ return new Promise((resolve, reject) => {
325
+ const options = {
326
+ hostname: "www.googleapis.com",
327
+ path: "/oauth2/v3/userinfo",
328
+ method: "GET",
329
+ headers: {
330
+ "Authorization": `Bearer ${token}`
331
+ },
332
+ timeout: 1e4
333
+ };
334
+ const req = https.request(options, (res) => {
335
+ let data = "";
336
+ res.on("data", (chunk) => {
337
+ data += chunk;
338
+ });
339
+ res.on("end", () => {
340
+ try {
341
+ const info = JSON.parse(data);
342
+ resolve({ email: info.email, name: info.name });
343
+ } catch {
344
+ resolve({});
345
+ }
346
+ });
347
+ });
348
+ req.on("error", () => resolve({}));
349
+ req.on("timeout", () => {
350
+ req.destroy();
351
+ resolve({});
352
+ });
353
+ req.end();
354
+ });
355
+ }
140
356
  // ── HTTP Helpers ────────────────────────────────────────────────
141
357
  authenticatedPost(url, body, token) {
142
358
  return new Promise((resolve, reject) => {
@@ -159,6 +375,10 @@ var OAuthClient = class {
159
375
  responseData += chunk;
160
376
  });
161
377
  res.on("end", () => {
378
+ if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
379
+ reject(new Error(`HTTP ${res.statusCode}: ${responseData.slice(0, 200)}`));
380
+ return;
381
+ }
162
382
  try {
163
383
  resolve(JSON.parse(responseData));
164
384
  } catch {
@@ -195,6 +415,10 @@ var OAuthClient = class {
195
415
  responseData += chunk;
196
416
  });
197
417
  res.on("end", () => {
418
+ if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
419
+ reject(new Error(`HTTP ${res.statusCode}: ${responseData.slice(0, 200)}`));
420
+ return;
421
+ }
198
422
  try {
199
423
  resolve(JSON.parse(responseData));
200
424
  } catch {
@@ -213,169 +437,6 @@ var OAuthClient = class {
213
437
  }
214
438
  };
215
439
 
216
- // src/sdk/accounts/storage.ts
217
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
218
- import { join as join2 } from "path";
219
-
220
- // src/core/config.ts
221
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
222
- import { join } from "path";
223
- import { homedir } from "os";
224
-
225
- // src/core/logger.ts
226
- var _verbose = false;
227
- function setVerbose(verbose) {
228
- _verbose = verbose;
229
- }
230
- function debug(module, msg, data) {
231
- if (!_verbose) return;
232
- const prefix = `[${module}]`;
233
- if (data !== void 0) {
234
- console.error(`${prefix} ${msg}`, data);
235
- } else {
236
- console.error(`${prefix} ${msg}`);
237
- }
238
- }
239
-
240
- // src/core/config.ts
241
- var CONFIG_DIR_NAME = "when-does-my-quota-refresh";
242
- var DEFAULT_CONFIG = {
243
- defaultMode: "local",
244
- cacheTtlMs: 5 * 60 * 1e3,
245
- // 5 minutes
246
- refreshIntervalMs: 5 * 60 * 1e3,
247
- wakeupModels: ["claude-sonnet-4-5", "gemini-3-flash", "gemini-3-pro-low"],
248
- wakeupAccounts: [],
249
- notifications: true,
250
- plugins: [],
251
- daemon: {
252
- pollIntervalMs: 60 * 1e3,
253
- enabled: false
254
- }
255
- };
256
- function getConfigDir() {
257
- const home = homedir();
258
- switch (process.platform) {
259
- case "darwin":
260
- return join(home, "Library", "Application Support", CONFIG_DIR_NAME);
261
- case "win32":
262
- return join(process.env.APPDATA || join(home, "AppData", "Roaming"), CONFIG_DIR_NAME);
263
- default:
264
- return join(home, ".config", CONFIG_DIR_NAME);
265
- }
266
- }
267
- function getConfigPath() {
268
- return join(getConfigDir(), "config.json");
269
- }
270
- function getDataDir() {
271
- return join(getConfigDir(), "data");
272
- }
273
- function ensureDirs() {
274
- const configDir = getConfigDir();
275
- const dataDir = getDataDir();
276
- if (!existsSync(configDir)) mkdirSync(configDir, { recursive: true });
277
- if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });
278
- }
279
- function loadConfig() {
280
- try {
281
- const path = getConfigPath();
282
- if (!existsSync(path)) return DEFAULT_CONFIG;
283
- const raw = readFileSync(path, "utf-8");
284
- const parsed = JSON.parse(raw);
285
- debug("config", `Loaded config from ${path}`);
286
- return { ...DEFAULT_CONFIG, ...parsed };
287
- } catch (err) {
288
- debug("config", "Failed to load config, using defaults", err);
289
- return DEFAULT_CONFIG;
290
- }
291
- }
292
- function saveConfig(config) {
293
- ensureDirs();
294
- const path = getConfigPath();
295
- writeFileSync(path, JSON.stringify(config, null, 2));
296
- debug("config", `Saved config to ${path}`);
297
- }
298
- function getConfigDirPath() {
299
- return getConfigDir();
300
- }
301
- function getDataDirPath() {
302
- return getDataDir();
303
- }
304
-
305
- // src/sdk/accounts/storage.ts
306
- var TOKENS_DIR = "accounts";
307
- function getStorePath() {
308
- return join2(getConfigDirPath(), TOKENS_DIR, "tokens.json");
309
- }
310
- function loadStore() {
311
- const path = getStorePath();
312
- if (!existsSync2(path)) return { accounts: [] };
313
- try {
314
- return JSON.parse(readFileSync2(path, "utf-8"));
315
- } catch {
316
- return { accounts: [] };
317
- }
318
- }
319
- function saveStore(store) {
320
- ensureDirs();
321
- writeFileSync2(getStorePath(), JSON.stringify(store, null, 2));
322
- debug("account-storage", `Saved ${store.accounts.length} account(s)`);
323
- }
324
- function saveAccountTokens(email, tokens) {
325
- const store = loadStore();
326
- const existing = store.accounts.findIndex((a) => a.email === email);
327
- const account = {
328
- email,
329
- tokens,
330
- addedAt: (/* @__PURE__ */ new Date()).toISOString()
331
- };
332
- if (existing >= 0) {
333
- store.accounts[existing] = account;
334
- } else {
335
- store.accounts.push(account);
336
- }
337
- if (store.accounts.length === 1) {
338
- store.activeEmail = email;
339
- }
340
- saveStore(store);
341
- }
342
- function getActiveAccountTokens() {
343
- const store = loadStore();
344
- if (!store.activeEmail) return null;
345
- const account = store.accounts.find((a) => a.email === store.activeEmail);
346
- return account?.tokens || null;
347
- }
348
- function setActiveAccount(email) {
349
- const store = loadStore();
350
- if (!store.accounts.find((a) => a.email === email)) return false;
351
- store.activeEmail = email;
352
- saveStore(store);
353
- return true;
354
- }
355
- function removeAccount(email) {
356
- const store = loadStore();
357
- const idx = store.accounts.findIndex((a) => a.email === email);
358
- if (idx < 0) return false;
359
- store.accounts.splice(idx, 1);
360
- if (store.activeEmail === email) {
361
- store.activeEmail = store.accounts[0]?.email;
362
- }
363
- saveStore(store);
364
- return true;
365
- }
366
- function listAccounts() {
367
- const store = loadStore();
368
- return store.accounts.map((a) => ({
369
- email: a.email,
370
- isActive: a.email === store.activeEmail,
371
- addedAt: a.addedAt
372
- }));
373
- }
374
- function getAllAccountTokens() {
375
- const store = loadStore();
376
- return store.accounts.map((a) => a.tokens);
377
- }
378
-
379
440
  // src/core/errors.ts
380
441
  var SourceUnavailableError = class extends Error {
381
442
  constructor(source, reason) {
@@ -403,7 +464,7 @@ async function loginAccount() {
403
464
  const url = new URL2(req.url || "/", `http://127.0.0.1`);
404
465
  const code2 = url.searchParams.get("code");
405
466
  if (code2) {
406
- res.writeHead(200, { "Content-Type": "text/html" });
467
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
407
468
  res.end(`
408
469
  <html><body style="font-family: sans-serif; text-align: center; padding: 50px;">
409
470
  <h1>\u2705 Login successful!</h1>
@@ -415,7 +476,7 @@ async function loginAccount() {
415
476
  } else {
416
477
  const error = url.searchParams.get("error");
417
478
  if (error) {
418
- res.writeHead(400, { "Content-Type": "text/html" });
479
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
419
480
  res.end(`<h1>Login failed: ${error}</h1>`);
420
481
  server.close();
421
482
  reject(new AuthError(`OAuth error: ${error}`));
@@ -446,7 +507,18 @@ ${authUrl}
446
507
  }, 5 * 60 * 1e3);
447
508
  });
448
509
  const tokens = await client.exchangeCode(code, capturedRedirectUri);
449
- const email = tokens.email || "unknown@gmail.com";
510
+ let email = tokens.email;
511
+ if (!email) {
512
+ try {
513
+ const userInfo = await client.fetchUserInfo();
514
+ email = userInfo.email;
515
+ } catch (err) {
516
+ debug("auth", `Failed to fetch user info: ${err}`);
517
+ }
518
+ }
519
+ if (!email) {
520
+ throw new AuthError("Could not determine user email from Google account");
521
+ }
450
522
  tokens.email = email;
451
523
  saveAccountTokens(email, tokens);
452
524
  setActiveAccount(email);
@@ -460,7 +532,18 @@ function getManualLoginUrl() {
460
532
  async function completeManualLogin(code) {
461
533
  const client = new OAuthClient();
462
534
  const tokens = await client.exchangeCode(code);
463
- const email = tokens.email || "unknown@gmail.com";
535
+ let email = tokens.email;
536
+ if (!email) {
537
+ try {
538
+ const userInfo = await client.fetchUserInfo();
539
+ email = userInfo.email;
540
+ } catch (err) {
541
+ debug("auth", `Failed to fetch user info: ${err}`);
542
+ }
543
+ }
544
+ if (!email) {
545
+ throw new AuthError("Could not determine user email from Google account");
546
+ }
464
547
  tokens.email = email;
465
548
  saveAccountTokens(email, tokens);
466
549
  setActiveAccount(email);
@@ -471,19 +554,21 @@ export {
471
554
  setVerbose,
472
555
  debug,
473
556
  SourceUnavailableError,
474
- OAuthClient,
475
557
  ensureDirs,
476
558
  loadConfig,
477
559
  saveConfig,
478
560
  getConfigDirPath,
479
561
  getDataDirPath,
562
+ saveAccountTokens,
563
+ getAccountTokens,
480
564
  getActiveAccountTokens,
481
565
  setActiveAccount,
482
566
  removeAccount,
483
567
  listAccounts,
484
568
  getAllAccountTokens,
569
+ OAuthClient,
485
570
  loginAccount,
486
571
  getManualLoginUrl,
487
572
  completeManualLogin
488
573
  };
489
- //# sourceMappingURL=chunk-ST6OOHQI.js.map
574
+ //# sourceMappingURL=chunk-VLP22VAR.js.map
@@ -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":"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","names":["readFileSync","writeFileSync","existsSync","mkdirSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","URL","code"]}