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.
- package/dist/accounts-FH6AKZT7.js +27 -0
- package/dist/{auth-XYJBJA2Q.js → auth-GX4ZIG5O.js} +2 -2
- package/dist/auth-GX4ZIG5O.js.map +1 -0
- package/dist/chunk-H7BEO7IF.js +2 -0
- package/dist/chunk-H7BEO7IF.js.map +1 -0
- package/dist/{chunk-ST6OOHQI.js → chunk-VLP22VAR.js} +254 -169
- package/dist/chunk-VLP22VAR.js.map +1 -0
- package/dist/cli/index.js +218 -28
- package/dist/cli/index.js.map +1 -1
- package/dist/sdk/index.d.ts +4 -0
- package/dist/sdk/index.js +399 -175
- package/dist/sdk/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ST6OOHQI.js.map +0 -1
- /package/dist/{auth-XYJBJA2Q.js.map → accounts-FH6AKZT7.js.map} +0 -0
|
@@ -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-
|
|
6
|
+
} from "./chunk-VLP22VAR.js";
|
|
7
7
|
export {
|
|
8
8
|
completeManualLogin,
|
|
9
9
|
getManualLoginUrl,
|
|
10
10
|
loginAccount
|
|
11
11
|
};
|
|
12
|
-
//# sourceMappingURL=auth-
|
|
12
|
+
//# sourceMappingURL=auth-GX4ZIG5O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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-
|
|
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":";;;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"]}
|