codex-team 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cli.cjs +145 -37
- package/dist/main.cjs +145 -37
- package/dist/main.js +145 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,8 @@ Use `--json` on query and mutation commands when you need machine-readable outpu
|
|
|
36
36
|
4. Switch between saved accounts with `codexm switch <name>` or let the tool choose with `codexm switch --auto`.
|
|
37
37
|
5. Refresh and inspect quota usage with `codexm list`.
|
|
38
38
|
|
|
39
|
+
For ChatGPT auth snapshots, `codex-team` can save and switch different users under the same ChatGPT account/workspace as separate managed entries when the local login tokens distinguish them.
|
|
40
|
+
|
|
39
41
|
## Development
|
|
40
42
|
|
|
41
43
|
```bash
|
package/dist/cli.cjs
CHANGED
|
@@ -13,7 +13,7 @@ var __webpack_modules__ = {
|
|
|
13
13
|
const utc_js_namespaceObject = require("dayjs/plugin/utc.js");
|
|
14
14
|
var utc_js_default = /*#__PURE__*/ __webpack_require__.n(utc_js_namespaceObject);
|
|
15
15
|
var package_namespaceObject = {
|
|
16
|
-
rE: "0.0.
|
|
16
|
+
rE: "0.0.10"
|
|
17
17
|
};
|
|
18
18
|
const external_node_crypto_namespaceObject = require("node:crypto");
|
|
19
19
|
const promises_namespaceObject = require("node:fs/promises");
|
|
@@ -48,10 +48,44 @@ var __webpack_modules__ = {
|
|
|
48
48
|
const normalized = normalizeAuthMode(authMode);
|
|
49
49
|
return "chatgpt" === normalized || "chatgpt_auth_tokens" === normalized;
|
|
50
50
|
}
|
|
51
|
+
function extractAuthClaim(payload) {
|
|
52
|
+
const value = payload["https://api.openai.com/auth"];
|
|
53
|
+
return isRecord(value) ? value : void 0;
|
|
54
|
+
}
|
|
55
|
+
function extractStringClaim(payload, key) {
|
|
56
|
+
const value = payload[key];
|
|
57
|
+
return "string" == typeof value && "" !== value.trim() ? value : void 0;
|
|
58
|
+
}
|
|
59
|
+
function extractSnapshotJwtPayloads(snapshot) {
|
|
60
|
+
const tokens = snapshot.tokens ?? {};
|
|
61
|
+
const payloads = [];
|
|
62
|
+
for (const tokenName of [
|
|
63
|
+
"id_token",
|
|
64
|
+
"access_token"
|
|
65
|
+
]){
|
|
66
|
+
const token = tokens[tokenName];
|
|
67
|
+
if ("string" == typeof token && "" !== token.trim()) try {
|
|
68
|
+
payloads.push(decodeJwtPayload(token));
|
|
69
|
+
} catch {}
|
|
70
|
+
}
|
|
71
|
+
return payloads;
|
|
72
|
+
}
|
|
73
|
+
function getSnapshotUserId(snapshot) {
|
|
74
|
+
for (const payload of extractSnapshotJwtPayloads(snapshot)){
|
|
75
|
+
const authClaim = extractAuthClaim(payload);
|
|
76
|
+
const chatGPTUserId = authClaim?.chatgpt_user_id;
|
|
77
|
+
if ("string" == typeof chatGPTUserId && "" !== chatGPTUserId.trim()) return chatGPTUserId;
|
|
78
|
+
const userId = extractStringClaim(payload, "user_id");
|
|
79
|
+
if (userId) return userId;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
51
82
|
function fingerprintApiKey(apiKey) {
|
|
52
83
|
return (0, external_node_crypto_namespaceObject.createHash)("sha256").update(apiKey).digest("hex").slice(0, 16);
|
|
53
84
|
}
|
|
54
|
-
function
|
|
85
|
+
function composeIdentity(accountId, userId) {
|
|
86
|
+
return "string" == typeof userId && "" !== userId.trim() ? `${accountId}:${userId}` : accountId;
|
|
87
|
+
}
|
|
88
|
+
function getSnapshotAccountId(snapshot) {
|
|
55
89
|
if (isApiKeyAuthMode(snapshot.auth_mode)) {
|
|
56
90
|
const apiKey = snapshot.OPENAI_API_KEY;
|
|
57
91
|
if ("string" != typeof apiKey || "" === apiKey.trim()) throw new Error('Field "OPENAI_API_KEY" must be a non-empty string for apikey auth.');
|
|
@@ -61,6 +95,12 @@ var __webpack_modules__ = {
|
|
|
61
95
|
if ("string" != typeof accountId || "" === accountId.trim()) throw new Error('Field "tokens.account_id" must be a non-empty string.');
|
|
62
96
|
return accountId;
|
|
63
97
|
}
|
|
98
|
+
function getSnapshotIdentity(snapshot) {
|
|
99
|
+
return composeIdentity(getSnapshotAccountId(snapshot), isSupportedChatGPTAuthMode(snapshot.auth_mode) ? getSnapshotUserId(snapshot) : void 0);
|
|
100
|
+
}
|
|
101
|
+
function getMetaIdentity(meta) {
|
|
102
|
+
return composeIdentity(meta.account_id, meta.user_id);
|
|
103
|
+
}
|
|
64
104
|
function defaultQuotaSnapshot() {
|
|
65
105
|
return {
|
|
66
106
|
status: "stale"
|
|
@@ -138,7 +178,8 @@ var __webpack_modules__ = {
|
|
|
138
178
|
return {
|
|
139
179
|
name,
|
|
140
180
|
auth_mode: snapshot.auth_mode,
|
|
141
|
-
account_id:
|
|
181
|
+
account_id: getSnapshotAccountId(snapshot),
|
|
182
|
+
user_id: isSupportedChatGPTAuthMode(snapshot.auth_mode) ? getSnapshotUserId(snapshot) : void 0,
|
|
142
183
|
created_at: existingCreatedAt ?? timestamp,
|
|
143
184
|
updated_at: timestamp,
|
|
144
185
|
last_switched_at: null,
|
|
@@ -159,6 +200,7 @@ var __webpack_modules__ = {
|
|
|
159
200
|
name: asNonEmptyString(parsed.name, "name"),
|
|
160
201
|
auth_mode: asNonEmptyString(parsed.auth_mode, "auth_mode"),
|
|
161
202
|
account_id: asNonEmptyString(parsed.account_id, "account_id"),
|
|
203
|
+
user_id: asOptionalString(parsed.user_id, "user_id"),
|
|
162
204
|
created_at: asNonEmptyString(parsed.created_at, "created_at"),
|
|
163
205
|
updated_at: asNonEmptyString(parsed.updated_at, "updated_at"),
|
|
164
206
|
last_switched_at: lastSwitchedAt,
|
|
@@ -184,14 +226,17 @@ var __webpack_modules__ = {
|
|
|
184
226
|
const external_node_util_namespaceObject = require("node:util");
|
|
185
227
|
const DEFAULT_CHATGPT_BASE_URL = "https://chatgpt.com";
|
|
186
228
|
const USER_AGENT = "codexm/0.1";
|
|
229
|
+
const USAGE_FETCH_ATTEMPTS = 3;
|
|
230
|
+
const USAGE_FETCH_RETRY_DELAY_MS = 250;
|
|
231
|
+
const USAGE_FETCH_TIMEOUT_MS = 15000;
|
|
187
232
|
function quota_client_isRecord(value) {
|
|
188
233
|
return "object" == typeof value && null !== value && !Array.isArray(value);
|
|
189
234
|
}
|
|
190
|
-
function
|
|
235
|
+
function quota_client_extractAuthClaim(payload) {
|
|
191
236
|
const value = payload["https://api.openai.com/auth"];
|
|
192
237
|
return quota_client_isRecord(value) ? value : void 0;
|
|
193
238
|
}
|
|
194
|
-
function
|
|
239
|
+
function quota_client_extractStringClaim(payload, key) {
|
|
195
240
|
const value = payload[key];
|
|
196
241
|
return "string" == typeof value && "" !== value.trim() ? value : void 0;
|
|
197
242
|
}
|
|
@@ -204,7 +249,7 @@ var __webpack_modules__ = {
|
|
|
204
249
|
const token = tokens[tokenName];
|
|
205
250
|
if ("string" == typeof token && "" !== token.trim()) try {
|
|
206
251
|
const payload = decodeJwtPayload(token);
|
|
207
|
-
const authClaim =
|
|
252
|
+
const authClaim = quota_client_extractAuthClaim(payload);
|
|
208
253
|
const planType = authClaim?.chatgpt_plan_type;
|
|
209
254
|
if ("string" == typeof planType && "" !== planType.trim()) return planType;
|
|
210
255
|
} catch {}
|
|
@@ -228,7 +273,7 @@ var __webpack_modules__ = {
|
|
|
228
273
|
const token = tokens[tokenName];
|
|
229
274
|
if ("string" == typeof token && "" !== token.trim()) try {
|
|
230
275
|
const payload = decodeJwtPayload(token);
|
|
231
|
-
const authClaim =
|
|
276
|
+
const authClaim = quota_client_extractAuthClaim(payload);
|
|
232
277
|
if (!accountId) {
|
|
233
278
|
const maybeAccountId = authClaim?.chatgpt_account_id;
|
|
234
279
|
if ("string" == typeof maybeAccountId && "" !== maybeAccountId.trim()) accountId = maybeAccountId;
|
|
@@ -237,8 +282,8 @@ var __webpack_modules__ = {
|
|
|
237
282
|
const maybePlanType = authClaim?.chatgpt_plan_type;
|
|
238
283
|
if ("string" == typeof maybePlanType && "" !== maybePlanType.trim()) planType = maybePlanType;
|
|
239
284
|
}
|
|
240
|
-
issuer ??=
|
|
241
|
-
clientId ??=
|
|
285
|
+
issuer ??= quota_client_extractStringClaim(payload, "iss");
|
|
286
|
+
clientId ??= quota_client_extractStringClaim(payload, "client_id") ?? quota_client_extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
|
|
242
287
|
} catch {}
|
|
243
288
|
}
|
|
244
289
|
if (!supported) return {
|
|
@@ -281,8 +326,6 @@ var __webpack_modules__ = {
|
|
|
281
326
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/u, "");
|
|
282
327
|
const candidates = [
|
|
283
328
|
`${normalizedBaseUrl}/backend-api/wham/usage`,
|
|
284
|
-
`${normalizedBaseUrl}/wham/usage`,
|
|
285
|
-
`${normalizedBaseUrl}/api/codex/usage`,
|
|
286
329
|
"https://chatgpt.com/backend-api/wham/usage"
|
|
287
330
|
];
|
|
288
331
|
return [
|
|
@@ -292,6 +335,19 @@ var __webpack_modules__ = {
|
|
|
292
335
|
function normalizeFetchError(error) {
|
|
293
336
|
return error instanceof Error ? error.message : String(error);
|
|
294
337
|
}
|
|
338
|
+
async function delay(milliseconds) {
|
|
339
|
+
await new Promise((resolve)=>setTimeout(resolve, milliseconds));
|
|
340
|
+
}
|
|
341
|
+
function isTransientUsageStatus(status) {
|
|
342
|
+
return 408 === status || 429 === status || status >= 500 && status <= 599;
|
|
343
|
+
}
|
|
344
|
+
function isLastAttempt(attempt) {
|
|
345
|
+
return attempt >= USAGE_FETCH_ATTEMPTS;
|
|
346
|
+
}
|
|
347
|
+
function formatUsageAttemptError(url, attempt, message) {
|
|
348
|
+
const retryContext = USAGE_FETCH_ATTEMPTS > 1 ? ` attempt ${attempt}/${USAGE_FETCH_ATTEMPTS}` : "";
|
|
349
|
+
return `${url}${retryContext} -> ${message}`;
|
|
350
|
+
}
|
|
295
351
|
function shouldRetryWithTokenRefresh(message) {
|
|
296
352
|
const normalized = message.toLowerCase();
|
|
297
353
|
return normalized.includes("401") || normalized.includes("403") || normalized.includes("unauthorized") || normalized.includes("invalid_token") || normalized.includes("deactivated_workspace");
|
|
@@ -347,7 +403,9 @@ var __webpack_modules__ = {
|
|
|
347
403
|
const urls = await resolveUsageUrls(options.homeDir);
|
|
348
404
|
const now = (options.now ?? new Date()).toISOString();
|
|
349
405
|
const errors = [];
|
|
350
|
-
for (const url of urls){
|
|
406
|
+
for (const url of urls)for(let attempt = 1; attempt <= USAGE_FETCH_ATTEMPTS; attempt += 1){
|
|
407
|
+
const abortController = new AbortController();
|
|
408
|
+
const timeout = setTimeout(()=>abortController.abort(), USAGE_FETCH_TIMEOUT_MS);
|
|
351
409
|
let response;
|
|
352
410
|
try {
|
|
353
411
|
response = await fetchImpl(url, {
|
|
@@ -357,23 +415,35 @@ var __webpack_modules__ = {
|
|
|
357
415
|
"ChatGPT-Account-Id": extracted.accountId,
|
|
358
416
|
Accept: "application/json",
|
|
359
417
|
"User-Agent": USER_AGENT
|
|
360
|
-
}
|
|
418
|
+
},
|
|
419
|
+
signal: abortController.signal
|
|
361
420
|
});
|
|
362
421
|
} catch (error) {
|
|
363
|
-
|
|
364
|
-
|
|
422
|
+
clearTimeout(timeout);
|
|
423
|
+
const message = abortController.signal.aborted ? `timed out after ${USAGE_FETCH_TIMEOUT_MS}ms` : normalizeFetchError(error);
|
|
424
|
+
errors.push(formatUsageAttemptError(url, attempt, message));
|
|
425
|
+
if (!isLastAttempt(attempt)) {
|
|
426
|
+
await delay(USAGE_FETCH_RETRY_DELAY_MS * attempt);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
break;
|
|
365
430
|
}
|
|
431
|
+
clearTimeout(timeout);
|
|
366
432
|
if (!response.ok) {
|
|
367
433
|
const body = await response.text();
|
|
368
|
-
errors.push(
|
|
369
|
-
|
|
434
|
+
errors.push(formatUsageAttemptError(url, attempt, `${response.status}: ${body.slice(0, 140).replace(/\s+/gu, " ").trim()}`));
|
|
435
|
+
if (isTransientUsageStatus(response.status) && !isLastAttempt(attempt)) {
|
|
436
|
+
await delay(USAGE_FETCH_RETRY_DELAY_MS * attempt);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
break;
|
|
370
440
|
}
|
|
371
441
|
let payload;
|
|
372
442
|
try {
|
|
373
443
|
payload = await response.json();
|
|
374
444
|
} catch (error) {
|
|
375
|
-
errors.push(
|
|
376
|
-
|
|
445
|
+
errors.push(formatUsageAttemptError(url, attempt, `failed to parse JSON: ${normalizeFetchError(error)}`));
|
|
446
|
+
break;
|
|
377
447
|
}
|
|
378
448
|
return mapUsagePayload(payload, extracted.planType, now);
|
|
379
449
|
}
|
|
@@ -508,6 +578,13 @@ var __webpack_modules__ = {
|
|
|
508
578
|
async function readJsonFile(path) {
|
|
509
579
|
return (0, promises_namespaceObject.readFile)(path, "utf8");
|
|
510
580
|
}
|
|
581
|
+
function canAutoMigrateLegacyChatGPTMeta(meta, snapshot) {
|
|
582
|
+
if (!isSupportedChatGPTAuthMode(meta.auth_mode) || !isSupportedChatGPTAuthMode(snapshot.auth_mode)) return false;
|
|
583
|
+
if ("string" == typeof meta.user_id && "" !== meta.user_id.trim()) return false;
|
|
584
|
+
const snapshotUserId = getSnapshotUserId(snapshot);
|
|
585
|
+
if (!snapshotUserId) return false;
|
|
586
|
+
return meta.account_id === getSnapshotAccountId(snapshot);
|
|
587
|
+
}
|
|
511
588
|
async function detectRunningCodexProcesses() {
|
|
512
589
|
try {
|
|
513
590
|
const { stdout } = await execFile("ps", [
|
|
@@ -605,6 +682,8 @@ var __webpack_modules__ = {
|
|
|
605
682
|
return {
|
|
606
683
|
name: account.name,
|
|
607
684
|
account_id: account.account_id,
|
|
685
|
+
user_id: account.user_id ?? null,
|
|
686
|
+
identity: account.identity,
|
|
608
687
|
plan_type: planType,
|
|
609
688
|
credits_balance: account.quota.credits_balance ?? null,
|
|
610
689
|
status: account.quota.status,
|
|
@@ -645,11 +724,21 @@ var __webpack_modules__ = {
|
|
|
645
724
|
readJsonFile(metaPath),
|
|
646
725
|
readAuthSnapshotFile(authPath)
|
|
647
726
|
]);
|
|
648
|
-
|
|
727
|
+
let meta = parseSnapshotMeta(rawMeta);
|
|
649
728
|
if (meta.name !== name) throw new Error(`Account metadata name mismatch for "${name}".`);
|
|
650
|
-
|
|
729
|
+
const snapshotIdentity = getSnapshotIdentity(snapshot);
|
|
730
|
+
if (getMetaIdentity(meta) !== snapshotIdentity) if (canAutoMigrateLegacyChatGPTMeta(meta, snapshot)) {
|
|
731
|
+
meta = {
|
|
732
|
+
...meta,
|
|
733
|
+
account_id: getSnapshotAccountId(snapshot),
|
|
734
|
+
user_id: getSnapshotUserId(snapshot)
|
|
735
|
+
};
|
|
736
|
+
await this.writeAccountMeta(name, meta);
|
|
737
|
+
} else throw new Error(`Account metadata account_id mismatch for "${name}".`);
|
|
738
|
+
if (getMetaIdentity(meta) !== snapshotIdentity) throw new Error(`Account metadata account_id mismatch for "${name}".`);
|
|
651
739
|
return {
|
|
652
740
|
...meta,
|
|
741
|
+
identity: getMetaIdentity(meta),
|
|
653
742
|
authPath,
|
|
654
743
|
metaPath,
|
|
655
744
|
configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
|
|
@@ -669,12 +758,12 @@ var __webpack_modules__ = {
|
|
|
669
758
|
warnings.push(`Account "${entry.name}" is invalid: ${error.message}`);
|
|
670
759
|
}
|
|
671
760
|
const counts = new Map();
|
|
672
|
-
for (const account of accounts)counts.set(account.
|
|
761
|
+
for (const account of accounts)counts.set(account.identity, (counts.get(account.identity) ?? 0) + 1);
|
|
673
762
|
accounts.sort((left, right)=>left.name.localeCompare(right.name));
|
|
674
763
|
return {
|
|
675
764
|
accounts: accounts.map((account)=>({
|
|
676
765
|
...account,
|
|
677
|
-
duplicateAccountId: (counts.get(account.
|
|
766
|
+
duplicateAccountId: (counts.get(account.identity) ?? 0) > 1
|
|
678
767
|
})),
|
|
679
768
|
warnings
|
|
680
769
|
};
|
|
@@ -685,6 +774,8 @@ var __webpack_modules__ = {
|
|
|
685
774
|
exists: false,
|
|
686
775
|
auth_mode: null,
|
|
687
776
|
account_id: null,
|
|
777
|
+
user_id: null,
|
|
778
|
+
identity: null,
|
|
688
779
|
matched_accounts: [],
|
|
689
780
|
managed: false,
|
|
690
781
|
duplicate_match: false,
|
|
@@ -692,11 +783,15 @@ var __webpack_modules__ = {
|
|
|
692
783
|
};
|
|
693
784
|
const snapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
|
|
694
785
|
const currentIdentity = getSnapshotIdentity(snapshot);
|
|
695
|
-
const
|
|
786
|
+
const currentAccountId = getSnapshotAccountId(snapshot);
|
|
787
|
+
const currentUserId = getSnapshotUserId(snapshot) ?? null;
|
|
788
|
+
const matchedAccounts = accounts.filter((account)=>account.identity === currentIdentity).map((account)=>account.name);
|
|
696
789
|
return {
|
|
697
790
|
exists: true,
|
|
698
791
|
auth_mode: snapshot.auth_mode,
|
|
699
|
-
account_id:
|
|
792
|
+
account_id: currentAccountId,
|
|
793
|
+
user_id: currentUserId,
|
|
794
|
+
identity: currentIdentity,
|
|
700
795
|
matched_accounts: matchedAccounts,
|
|
701
796
|
managed: matchedAccounts.length > 0,
|
|
702
797
|
duplicate_match: matchedAccounts.length > 1,
|
|
@@ -719,7 +814,7 @@ var __webpack_modules__ = {
|
|
|
719
814
|
const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
|
|
720
815
|
if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
|
|
721
816
|
const { accounts } = await this.listAccounts();
|
|
722
|
-
const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.
|
|
817
|
+
const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.identity === identity);
|
|
723
818
|
if (duplicateIdentityAccounts.length > 0) {
|
|
724
819
|
const joinedNames = duplicateIdentityAccounts.map((account)=>`"${account.name}"`).join(", ");
|
|
725
820
|
throw new Error(`Identity ${identity} is already managed by ${joinedNames}.`);
|
|
@@ -794,7 +889,7 @@ var __webpack_modules__ = {
|
|
|
794
889
|
await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
|
|
795
890
|
}
|
|
796
891
|
const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
|
|
797
|
-
if (getSnapshotIdentity(writtenSnapshot) !== account.
|
|
892
|
+
if (getSnapshotIdentity(writtenSnapshot) !== account.identity) throw new Error(`Switch verification failed for account "${name}".`);
|
|
798
893
|
const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
|
|
799
894
|
meta.last_switched_at = new Date().toISOString();
|
|
800
895
|
meta.updated_at = meta.last_switched_at;
|
|
@@ -838,7 +933,8 @@ var __webpack_modules__ = {
|
|
|
838
933
|
await this.syncCurrentAuthIfMatching(result.authSnapshot);
|
|
839
934
|
}
|
|
840
935
|
meta.auth_mode = result.authSnapshot.auth_mode;
|
|
841
|
-
meta.account_id =
|
|
936
|
+
meta.account_id = getSnapshotAccountId(result.authSnapshot);
|
|
937
|
+
meta.user_id = getSnapshotUserId(result.authSnapshot);
|
|
842
938
|
meta.updated_at = now.toISOString();
|
|
843
939
|
meta.quota = result.quota;
|
|
844
940
|
await this.writeAccountMeta(name, meta);
|
|
@@ -951,7 +1047,7 @@ var __webpack_modules__ = {
|
|
|
951
1047
|
if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
|
|
952
1048
|
if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
|
|
953
1049
|
if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
|
|
954
|
-
if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.
|
|
1050
|
+
if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.identity} with another saved account.`);
|
|
955
1051
|
}
|
|
956
1052
|
let currentAuthPresent = false;
|
|
957
1053
|
if (await pathExists(this.paths.currentAuthPath)) {
|
|
@@ -1032,7 +1128,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1032
1128
|
if (status.exists) {
|
|
1033
1129
|
lines.push("Current auth: present");
|
|
1034
1130
|
lines.push(`Auth mode: ${status.auth_mode}`);
|
|
1035
|
-
lines.push(`Identity: ${maskAccountId(status.
|
|
1131
|
+
lines.push(`Identity: ${maskAccountId(status.identity ?? "")}`);
|
|
1036
1132
|
if (0 === status.matched_accounts.length) lines.push("Managed account: no (unmanaged)");
|
|
1037
1133
|
else if (1 === status.matched_accounts.length) lines.push(`Managed account: ${status.matched_accounts[0]}`);
|
|
1038
1134
|
else lines.push(`Managed account: multiple (${status.matched_accounts.join(", ")})`);
|
|
@@ -1085,6 +1181,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1085
1181
|
return {
|
|
1086
1182
|
name: account.name,
|
|
1087
1183
|
account_id: account.account_id,
|
|
1184
|
+
identity: account.identity,
|
|
1088
1185
|
plan_type: account.plan_type,
|
|
1089
1186
|
available: computeAvailability(account),
|
|
1090
1187
|
refresh_status: "ok",
|
|
@@ -1117,7 +1214,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1117
1214
|
}
|
|
1118
1215
|
function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
|
|
1119
1216
|
const lines = [
|
|
1120
|
-
dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.
|
|
1217
|
+
dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.identity)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.identity)}).`,
|
|
1121
1218
|
`Score: ${candidate.effective_score}`,
|
|
1122
1219
|
`5H remaining: ${candidate.remain_5h}%`,
|
|
1123
1220
|
`1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
|
|
@@ -1128,7 +1225,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1128
1225
|
}
|
|
1129
1226
|
function describeAutoSwitchNoop(candidate, warnings) {
|
|
1130
1227
|
const lines = [
|
|
1131
|
-
`Current account "${candidate.name}" (${maskAccountId(candidate.
|
|
1228
|
+
`Current account "${candidate.name}" (${maskAccountId(candidate.identity)}) is already the best available account.`,
|
|
1132
1229
|
`Score: ${candidate.effective_score}`,
|
|
1133
1230
|
`5H remaining: ${candidate.remain_5h}%`,
|
|
1134
1231
|
`1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
|
|
@@ -1140,7 +1237,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1140
1237
|
if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
|
|
1141
1238
|
const table = formatTable(accounts.map((account)=>({
|
|
1142
1239
|
name: account.name,
|
|
1143
|
-
account_id: maskAccountId(account.
|
|
1240
|
+
account_id: maskAccountId(account.identity),
|
|
1144
1241
|
plan_type: account.plan_type ?? "-",
|
|
1145
1242
|
available: computeAvailability(account) ?? "-",
|
|
1146
1243
|
five_hour: formatUsagePercent(account.five_hour),
|
|
@@ -1265,11 +1362,13 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1265
1362
|
account: {
|
|
1266
1363
|
name: account.name,
|
|
1267
1364
|
account_id: account.account_id,
|
|
1365
|
+
user_id: account.user_id ?? null,
|
|
1366
|
+
identity: account.identity,
|
|
1268
1367
|
auth_mode: account.auth_mode
|
|
1269
1368
|
}
|
|
1270
1369
|
};
|
|
1271
1370
|
if (json) writeJson(streams.stdout, payload);
|
|
1272
|
-
else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.
|
|
1371
|
+
else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.identity)}).\n`);
|
|
1273
1372
|
return 0;
|
|
1274
1373
|
}
|
|
1275
1374
|
case "update":
|
|
@@ -1291,6 +1390,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1291
1390
|
account: {
|
|
1292
1391
|
name: result.account.name,
|
|
1293
1392
|
account_id: result.account.account_id,
|
|
1393
|
+
user_id: result.account.user_id ?? null,
|
|
1394
|
+
identity: result.account.identity,
|
|
1294
1395
|
auth_mode: result.account.auth_mode
|
|
1295
1396
|
},
|
|
1296
1397
|
quota,
|
|
@@ -1298,7 +1399,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1298
1399
|
};
|
|
1299
1400
|
if (json) writeJson(streams.stdout, payload);
|
|
1300
1401
|
else {
|
|
1301
|
-
streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.
|
|
1402
|
+
streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
|
|
1302
1403
|
for (const warning of warnings)streams.stdout.write(`Warning: ${warning}\n`);
|
|
1303
1404
|
}
|
|
1304
1405
|
return 0;
|
|
@@ -1341,7 +1442,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1341
1442
|
reason: "already_current_best",
|
|
1342
1443
|
account: {
|
|
1343
1444
|
name: selected.name,
|
|
1344
|
-
account_id: selected.account_id
|
|
1445
|
+
account_id: selected.account_id,
|
|
1446
|
+
identity: selected.identity
|
|
1345
1447
|
},
|
|
1346
1448
|
selected,
|
|
1347
1449
|
candidates,
|
|
@@ -1361,6 +1463,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1361
1463
|
account: {
|
|
1362
1464
|
name: result.account.name,
|
|
1363
1465
|
account_id: result.account.account_id,
|
|
1466
|
+
user_id: result.account.user_id ?? null,
|
|
1467
|
+
identity: result.account.identity,
|
|
1364
1468
|
auth_mode: result.account.auth_mode
|
|
1365
1469
|
},
|
|
1366
1470
|
selected,
|
|
@@ -1390,6 +1494,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1390
1494
|
account: {
|
|
1391
1495
|
name: result.account.name,
|
|
1392
1496
|
account_id: result.account.account_id,
|
|
1497
|
+
user_id: result.account.user_id ?? null,
|
|
1498
|
+
identity: result.account.identity,
|
|
1393
1499
|
auth_mode: result.account.auth_mode
|
|
1394
1500
|
},
|
|
1395
1501
|
quota,
|
|
@@ -1398,7 +1504,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1398
1504
|
};
|
|
1399
1505
|
if (json) writeJson(streams.stdout, payload);
|
|
1400
1506
|
else {
|
|
1401
|
-
streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.
|
|
1507
|
+
streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
|
|
1402
1508
|
if (result.backup_path) streams.stdout.write(`Backup: ${result.backup_path}\n`);
|
|
1403
1509
|
for (const warning of result.warnings)streams.stdout.write(`Warning: ${warning}\n`);
|
|
1404
1510
|
}
|
|
@@ -1440,6 +1546,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1440
1546
|
account: {
|
|
1441
1547
|
name: account.name,
|
|
1442
1548
|
account_id: account.account_id,
|
|
1549
|
+
user_id: account.user_id ?? null,
|
|
1550
|
+
identity: account.identity,
|
|
1443
1551
|
auth_mode: account.auth_mode
|
|
1444
1552
|
}
|
|
1445
1553
|
});
|