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.
- package/dist/accounts-FH6AKZT7.js +27 -0
- package/dist/{auth-K5ETZYGY.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-24ILGZJ2.js → chunk-VLP22VAR.js} +263 -175
- package/dist/chunk-VLP22VAR.js.map +1 -0
- package/dist/cli/index.js +219 -29
- package/dist/cli/index.js.map +1 -1
- package/dist/sdk/index.d.ts +16 -5
- package/dist/sdk/index.js +409 -182
- package/dist/sdk/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-24ILGZJ2.js.map +0 -1
- /package/dist/{auth-K5ETZYGY.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 */
|
|
@@ -30,6 +45,163 @@ var OAUTH_CLIENT_SECRET = "d-FL95Q19q7MQmFpd7hHD0Ty";
|
|
|
30
45
|
var OAUTH_SCOPES = [
|
|
31
46
|
"https://www.googleapis.com/auth/cloud-platform"
|
|
32
47
|
].join(" ");
|
|
48
|
+
var OAUTH_REDIRECT_URI = "http://127.0.0.1:0/callback";
|
|
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
|
+
}
|
|
33
205
|
|
|
34
206
|
// src/sources/oauth/client.ts
|
|
35
207
|
var OAuthClient = class {
|
|
@@ -40,10 +212,10 @@ var OAuthClient = class {
|
|
|
40
212
|
/**
|
|
41
213
|
* Generate the OAuth authorization URL for user login
|
|
42
214
|
*/
|
|
43
|
-
getAuthUrl() {
|
|
215
|
+
getAuthUrl(redirectUri) {
|
|
44
216
|
const params = new URLSearchParams({
|
|
45
217
|
client_id: OAUTH_CLIENT_ID,
|
|
46
|
-
redirect_uri:
|
|
218
|
+
redirect_uri: redirectUri || OAUTH_REDIRECT_URI,
|
|
47
219
|
response_type: "code",
|
|
48
220
|
scope: OAUTH_SCOPES,
|
|
49
221
|
access_type: "offline",
|
|
@@ -54,12 +226,12 @@ var OAuthClient = class {
|
|
|
54
226
|
/**
|
|
55
227
|
* Exchange an authorization code for tokens
|
|
56
228
|
*/
|
|
57
|
-
async exchangeCode(code) {
|
|
229
|
+
async exchangeCode(code, redirectUri) {
|
|
58
230
|
const body = new URLSearchParams({
|
|
59
231
|
code,
|
|
60
232
|
client_id: OAUTH_CLIENT_ID,
|
|
61
233
|
client_secret: OAUTH_CLIENT_SECRET,
|
|
62
|
-
redirect_uri:
|
|
234
|
+
redirect_uri: redirectUri || OAUTH_REDIRECT_URI,
|
|
63
235
|
grant_type: "authorization_code"
|
|
64
236
|
});
|
|
65
237
|
const response = await this.postUrlEncoded("https://oauth2.googleapis.com/token", body);
|
|
@@ -91,6 +263,14 @@ var OAuthClient = class {
|
|
|
91
263
|
accessToken: data.access_token,
|
|
92
264
|
expiresAt: Date.now() + data.expires_in * 1e3
|
|
93
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
|
+
}
|
|
94
274
|
return this.tokens;
|
|
95
275
|
}
|
|
96
276
|
/**
|
|
@@ -136,6 +316,43 @@ var OAuthClient = class {
|
|
|
136
316
|
token
|
|
137
317
|
);
|
|
138
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
|
+
}
|
|
139
356
|
// ── HTTP Helpers ────────────────────────────────────────────────
|
|
140
357
|
authenticatedPost(url, body, token) {
|
|
141
358
|
return new Promise((resolve, reject) => {
|
|
@@ -158,6 +375,10 @@ var OAuthClient = class {
|
|
|
158
375
|
responseData += chunk;
|
|
159
376
|
});
|
|
160
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
|
+
}
|
|
161
382
|
try {
|
|
162
383
|
resolve(JSON.parse(responseData));
|
|
163
384
|
} catch {
|
|
@@ -194,6 +415,10 @@ var OAuthClient = class {
|
|
|
194
415
|
responseData += chunk;
|
|
195
416
|
});
|
|
196
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
|
+
}
|
|
197
422
|
try {
|
|
198
423
|
resolve(JSON.parse(responseData));
|
|
199
424
|
} catch {
|
|
@@ -212,169 +437,6 @@ var OAuthClient = class {
|
|
|
212
437
|
}
|
|
213
438
|
};
|
|
214
439
|
|
|
215
|
-
// src/sdk/accounts/storage.ts
|
|
216
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
217
|
-
import { join as join2 } from "path";
|
|
218
|
-
|
|
219
|
-
// src/core/config.ts
|
|
220
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
221
|
-
import { join } from "path";
|
|
222
|
-
import { homedir } from "os";
|
|
223
|
-
|
|
224
|
-
// src/core/logger.ts
|
|
225
|
-
var _verbose = false;
|
|
226
|
-
function setVerbose(verbose) {
|
|
227
|
-
_verbose = verbose;
|
|
228
|
-
}
|
|
229
|
-
function debug(module, msg, data) {
|
|
230
|
-
if (!_verbose) return;
|
|
231
|
-
const prefix = `[${module}]`;
|
|
232
|
-
if (data !== void 0) {
|
|
233
|
-
console.error(`${prefix} ${msg}`, data);
|
|
234
|
-
} else {
|
|
235
|
-
console.error(`${prefix} ${msg}`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// src/core/config.ts
|
|
240
|
-
var CONFIG_DIR_NAME = "when-does-my-quota-refresh";
|
|
241
|
-
var DEFAULT_CONFIG = {
|
|
242
|
-
defaultMode: "local",
|
|
243
|
-
cacheTtlMs: 5 * 60 * 1e3,
|
|
244
|
-
// 5 minutes
|
|
245
|
-
refreshIntervalMs: 5 * 60 * 1e3,
|
|
246
|
-
wakeupModels: ["claude-sonnet-4-5", "gemini-3-flash", "gemini-3-pro-low"],
|
|
247
|
-
wakeupAccounts: [],
|
|
248
|
-
notifications: true,
|
|
249
|
-
plugins: [],
|
|
250
|
-
daemon: {
|
|
251
|
-
pollIntervalMs: 60 * 1e3,
|
|
252
|
-
enabled: false
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
function getConfigDir() {
|
|
256
|
-
const home = homedir();
|
|
257
|
-
switch (process.platform) {
|
|
258
|
-
case "darwin":
|
|
259
|
-
return join(home, "Library", "Application Support", CONFIG_DIR_NAME);
|
|
260
|
-
case "win32":
|
|
261
|
-
return join(process.env.APPDATA || join(home, "AppData", "Roaming"), CONFIG_DIR_NAME);
|
|
262
|
-
default:
|
|
263
|
-
return join(home, ".config", CONFIG_DIR_NAME);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
function getConfigPath() {
|
|
267
|
-
return join(getConfigDir(), "config.json");
|
|
268
|
-
}
|
|
269
|
-
function getDataDir() {
|
|
270
|
-
return join(getConfigDir(), "data");
|
|
271
|
-
}
|
|
272
|
-
function ensureDirs() {
|
|
273
|
-
const configDir = getConfigDir();
|
|
274
|
-
const dataDir = getDataDir();
|
|
275
|
-
if (!existsSync(configDir)) mkdirSync(configDir, { recursive: true });
|
|
276
|
-
if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });
|
|
277
|
-
}
|
|
278
|
-
function loadConfig() {
|
|
279
|
-
try {
|
|
280
|
-
const path = getConfigPath();
|
|
281
|
-
if (!existsSync(path)) return DEFAULT_CONFIG;
|
|
282
|
-
const raw = readFileSync(path, "utf-8");
|
|
283
|
-
const parsed = JSON.parse(raw);
|
|
284
|
-
debug("config", `Loaded config from ${path}`);
|
|
285
|
-
return { ...DEFAULT_CONFIG, ...parsed };
|
|
286
|
-
} catch (err) {
|
|
287
|
-
debug("config", "Failed to load config, using defaults", err);
|
|
288
|
-
return DEFAULT_CONFIG;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
function saveConfig(config) {
|
|
292
|
-
ensureDirs();
|
|
293
|
-
const path = getConfigPath();
|
|
294
|
-
writeFileSync(path, JSON.stringify(config, null, 2));
|
|
295
|
-
debug("config", `Saved config to ${path}`);
|
|
296
|
-
}
|
|
297
|
-
function getConfigDirPath() {
|
|
298
|
-
return getConfigDir();
|
|
299
|
-
}
|
|
300
|
-
function getDataDirPath() {
|
|
301
|
-
return getDataDir();
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// src/sdk/accounts/storage.ts
|
|
305
|
-
var TOKENS_DIR = "accounts";
|
|
306
|
-
function getStorePath() {
|
|
307
|
-
return join2(getConfigDirPath(), TOKENS_DIR, "tokens.json");
|
|
308
|
-
}
|
|
309
|
-
function loadStore() {
|
|
310
|
-
const path = getStorePath();
|
|
311
|
-
if (!existsSync2(path)) return { accounts: [] };
|
|
312
|
-
try {
|
|
313
|
-
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
314
|
-
} catch {
|
|
315
|
-
return { accounts: [] };
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
function saveStore(store) {
|
|
319
|
-
ensureDirs();
|
|
320
|
-
writeFileSync2(getStorePath(), JSON.stringify(store, null, 2));
|
|
321
|
-
debug("account-storage", `Saved ${store.accounts.length} account(s)`);
|
|
322
|
-
}
|
|
323
|
-
function saveAccountTokens(email, tokens) {
|
|
324
|
-
const store = loadStore();
|
|
325
|
-
const existing = store.accounts.findIndex((a) => a.email === email);
|
|
326
|
-
const account = {
|
|
327
|
-
email,
|
|
328
|
-
tokens,
|
|
329
|
-
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
330
|
-
};
|
|
331
|
-
if (existing >= 0) {
|
|
332
|
-
store.accounts[existing] = account;
|
|
333
|
-
} else {
|
|
334
|
-
store.accounts.push(account);
|
|
335
|
-
}
|
|
336
|
-
if (store.accounts.length === 1) {
|
|
337
|
-
store.activeEmail = email;
|
|
338
|
-
}
|
|
339
|
-
saveStore(store);
|
|
340
|
-
}
|
|
341
|
-
function getActiveAccountTokens() {
|
|
342
|
-
const store = loadStore();
|
|
343
|
-
if (!store.activeEmail) return null;
|
|
344
|
-
const account = store.accounts.find((a) => a.email === store.activeEmail);
|
|
345
|
-
return account?.tokens || null;
|
|
346
|
-
}
|
|
347
|
-
function setActiveAccount(email) {
|
|
348
|
-
const store = loadStore();
|
|
349
|
-
if (!store.accounts.find((a) => a.email === email)) return false;
|
|
350
|
-
store.activeEmail = email;
|
|
351
|
-
saveStore(store);
|
|
352
|
-
return true;
|
|
353
|
-
}
|
|
354
|
-
function removeAccount(email) {
|
|
355
|
-
const store = loadStore();
|
|
356
|
-
const idx = store.accounts.findIndex((a) => a.email === email);
|
|
357
|
-
if (idx < 0) return false;
|
|
358
|
-
store.accounts.splice(idx, 1);
|
|
359
|
-
if (store.activeEmail === email) {
|
|
360
|
-
store.activeEmail = store.accounts[0]?.email;
|
|
361
|
-
}
|
|
362
|
-
saveStore(store);
|
|
363
|
-
return true;
|
|
364
|
-
}
|
|
365
|
-
function listAccounts() {
|
|
366
|
-
const store = loadStore();
|
|
367
|
-
return store.accounts.map((a) => ({
|
|
368
|
-
email: a.email,
|
|
369
|
-
isActive: a.email === store.activeEmail,
|
|
370
|
-
addedAt: a.addedAt
|
|
371
|
-
}));
|
|
372
|
-
}
|
|
373
|
-
function getAllAccountTokens() {
|
|
374
|
-
const store = loadStore();
|
|
375
|
-
return store.accounts.map((a) => a.tokens);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
440
|
// src/core/errors.ts
|
|
379
441
|
var SourceUnavailableError = class extends Error {
|
|
380
442
|
constructor(source, reason) {
|
|
@@ -395,14 +457,14 @@ import http from "http";
|
|
|
395
457
|
import { URL as URL2 } from "url";
|
|
396
458
|
async function loginAccount() {
|
|
397
459
|
const client = new OAuthClient();
|
|
398
|
-
const authUrl = client.getAuthUrl();
|
|
399
460
|
debug("auth", "Starting OAuth login flow");
|
|
461
|
+
let capturedRedirectUri = "http://127.0.0.1:0/callback";
|
|
400
462
|
const code = await new Promise((resolve, reject) => {
|
|
401
463
|
const server = http.createServer(async (req, res) => {
|
|
402
464
|
const url = new URL2(req.url || "/", `http://127.0.0.1`);
|
|
403
465
|
const code2 = url.searchParams.get("code");
|
|
404
466
|
if (code2) {
|
|
405
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
467
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
406
468
|
res.end(`
|
|
407
469
|
<html><body style="font-family: sans-serif; text-align: center; padding: 50px;">
|
|
408
470
|
<h1>\u2705 Login successful!</h1>
|
|
@@ -414,7 +476,7 @@ async function loginAccount() {
|
|
|
414
476
|
} else {
|
|
415
477
|
const error = url.searchParams.get("error");
|
|
416
478
|
if (error) {
|
|
417
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
479
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
418
480
|
res.end(`<h1>Login failed: ${error}</h1>`);
|
|
419
481
|
server.close();
|
|
420
482
|
reject(new AuthError(`OAuth error: ${error}`));
|
|
@@ -427,7 +489,9 @@ async function loginAccount() {
|
|
|
427
489
|
server.listen(0, "127.0.0.1", () => {
|
|
428
490
|
const addr = server.address();
|
|
429
491
|
if (typeof addr === "object" && addr) {
|
|
492
|
+
capturedRedirectUri = `http://127.0.0.1:${addr.port}/callback`;
|
|
430
493
|
debug("auth", `Callback server on port ${addr.port}`);
|
|
494
|
+
const authUrl = client.getAuthUrl(capturedRedirectUri);
|
|
431
495
|
open(authUrl).catch(() => {
|
|
432
496
|
console.log(`
|
|
433
497
|
Open this URL in your browser:
|
|
@@ -442,8 +506,19 @@ ${authUrl}
|
|
|
442
506
|
reject(new AuthError("Login timed out after 5 minutes"));
|
|
443
507
|
}, 5 * 60 * 1e3);
|
|
444
508
|
});
|
|
445
|
-
const tokens = await client.exchangeCode(code);
|
|
446
|
-
|
|
509
|
+
const tokens = await client.exchangeCode(code, capturedRedirectUri);
|
|
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
|
+
}
|
|
447
522
|
tokens.email = email;
|
|
448
523
|
saveAccountTokens(email, tokens);
|
|
449
524
|
setActiveAccount(email);
|
|
@@ -457,7 +532,18 @@ function getManualLoginUrl() {
|
|
|
457
532
|
async function completeManualLogin(code) {
|
|
458
533
|
const client = new OAuthClient();
|
|
459
534
|
const tokens = await client.exchangeCode(code);
|
|
460
|
-
|
|
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
|
+
}
|
|
461
547
|
tokens.email = email;
|
|
462
548
|
saveAccountTokens(email, tokens);
|
|
463
549
|
setActiveAccount(email);
|
|
@@ -468,19 +554,21 @@ export {
|
|
|
468
554
|
setVerbose,
|
|
469
555
|
debug,
|
|
470
556
|
SourceUnavailableError,
|
|
471
|
-
OAuthClient,
|
|
472
557
|
ensureDirs,
|
|
473
558
|
loadConfig,
|
|
474
559
|
saveConfig,
|
|
475
560
|
getConfigDirPath,
|
|
476
561
|
getDataDirPath,
|
|
562
|
+
saveAccountTokens,
|
|
563
|
+
getAccountTokens,
|
|
477
564
|
getActiveAccountTokens,
|
|
478
565
|
setActiveAccount,
|
|
479
566
|
removeAccount,
|
|
480
567
|
listAccounts,
|
|
481
568
|
getAllAccountTokens,
|
|
569
|
+
OAuthClient,
|
|
482
570
|
loginAccount,
|
|
483
571
|
getManualLoginUrl,
|
|
484
572
|
completeManualLogin
|
|
485
573
|
};
|
|
486
|
-
//# sourceMappingURL=chunk-
|
|
574
|
+
//# sourceMappingURL=chunk-VLP22VAR.js.map
|