codex-team 0.0.7 → 0.0.9
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 +3 -3
- package/dist/cli.cjs +113 -57
- package/dist/main.cjs +113 -57
- package/dist/main.js +113 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,8 +24,6 @@ codexm switch <name>
|
|
|
24
24
|
codexm switch --auto --dry-run
|
|
25
25
|
codexm remove <name> --yes
|
|
26
26
|
codexm rename <old> <new>
|
|
27
|
-
codexm quota refresh [name]
|
|
28
|
-
codexm doctor
|
|
29
27
|
```
|
|
30
28
|
|
|
31
29
|
Use `--json` on query and mutation commands when you need machine-readable output.
|
|
@@ -36,7 +34,9 @@ Use `--json` on query and mutation commands when you need machine-readable outpu
|
|
|
36
34
|
2. Save the current auth snapshot with `codexm save <name>`.
|
|
37
35
|
3. Repeat for other accounts.
|
|
38
36
|
4. Switch between saved accounts with `codexm switch <name>` or let the tool choose with `codexm switch --auto`.
|
|
39
|
-
5. Refresh and inspect quota usage with `codexm list
|
|
37
|
+
5. Refresh and inspect quota usage with `codexm list`.
|
|
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
40
|
|
|
41
41
|
## Development
|
|
42
42
|
|
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.9"
|
|
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,
|
|
@@ -187,11 +229,11 @@ var __webpack_modules__ = {
|
|
|
187
229
|
function quota_client_isRecord(value) {
|
|
188
230
|
return "object" == typeof value && null !== value && !Array.isArray(value);
|
|
189
231
|
}
|
|
190
|
-
function
|
|
232
|
+
function quota_client_extractAuthClaim(payload) {
|
|
191
233
|
const value = payload["https://api.openai.com/auth"];
|
|
192
234
|
return quota_client_isRecord(value) ? value : void 0;
|
|
193
235
|
}
|
|
194
|
-
function
|
|
236
|
+
function quota_client_extractStringClaim(payload, key) {
|
|
195
237
|
const value = payload[key];
|
|
196
238
|
return "string" == typeof value && "" !== value.trim() ? value : void 0;
|
|
197
239
|
}
|
|
@@ -204,7 +246,7 @@ var __webpack_modules__ = {
|
|
|
204
246
|
const token = tokens[tokenName];
|
|
205
247
|
if ("string" == typeof token && "" !== token.trim()) try {
|
|
206
248
|
const payload = decodeJwtPayload(token);
|
|
207
|
-
const authClaim =
|
|
249
|
+
const authClaim = quota_client_extractAuthClaim(payload);
|
|
208
250
|
const planType = authClaim?.chatgpt_plan_type;
|
|
209
251
|
if ("string" == typeof planType && "" !== planType.trim()) return planType;
|
|
210
252
|
} catch {}
|
|
@@ -228,7 +270,7 @@ var __webpack_modules__ = {
|
|
|
228
270
|
const token = tokens[tokenName];
|
|
229
271
|
if ("string" == typeof token && "" !== token.trim()) try {
|
|
230
272
|
const payload = decodeJwtPayload(token);
|
|
231
|
-
const authClaim =
|
|
273
|
+
const authClaim = quota_client_extractAuthClaim(payload);
|
|
232
274
|
if (!accountId) {
|
|
233
275
|
const maybeAccountId = authClaim?.chatgpt_account_id;
|
|
234
276
|
if ("string" == typeof maybeAccountId && "" !== maybeAccountId.trim()) accountId = maybeAccountId;
|
|
@@ -237,8 +279,8 @@ var __webpack_modules__ = {
|
|
|
237
279
|
const maybePlanType = authClaim?.chatgpt_plan_type;
|
|
238
280
|
if ("string" == typeof maybePlanType && "" !== maybePlanType.trim()) planType = maybePlanType;
|
|
239
281
|
}
|
|
240
|
-
issuer ??=
|
|
241
|
-
clientId ??=
|
|
282
|
+
issuer ??= quota_client_extractStringClaim(payload, "iss");
|
|
283
|
+
clientId ??= quota_client_extractStringClaim(payload, "client_id") ?? quota_client_extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
|
|
242
284
|
} catch {}
|
|
243
285
|
}
|
|
244
286
|
if (!supported) return {
|
|
@@ -508,6 +550,13 @@ var __webpack_modules__ = {
|
|
|
508
550
|
async function readJsonFile(path) {
|
|
509
551
|
return (0, promises_namespaceObject.readFile)(path, "utf8");
|
|
510
552
|
}
|
|
553
|
+
function canAutoMigrateLegacyChatGPTMeta(meta, snapshot) {
|
|
554
|
+
if (!isSupportedChatGPTAuthMode(meta.auth_mode) || !isSupportedChatGPTAuthMode(snapshot.auth_mode)) return false;
|
|
555
|
+
if ("string" == typeof meta.user_id && "" !== meta.user_id.trim()) return false;
|
|
556
|
+
const snapshotUserId = getSnapshotUserId(snapshot);
|
|
557
|
+
if (!snapshotUserId) return false;
|
|
558
|
+
return meta.account_id === getSnapshotAccountId(snapshot);
|
|
559
|
+
}
|
|
511
560
|
async function detectRunningCodexProcesses() {
|
|
512
561
|
try {
|
|
513
562
|
const { stdout } = await execFile("ps", [
|
|
@@ -605,6 +654,8 @@ var __webpack_modules__ = {
|
|
|
605
654
|
return {
|
|
606
655
|
name: account.name,
|
|
607
656
|
account_id: account.account_id,
|
|
657
|
+
user_id: account.user_id ?? null,
|
|
658
|
+
identity: account.identity,
|
|
608
659
|
plan_type: planType,
|
|
609
660
|
credits_balance: account.quota.credits_balance ?? null,
|
|
610
661
|
status: account.quota.status,
|
|
@@ -645,11 +696,21 @@ var __webpack_modules__ = {
|
|
|
645
696
|
readJsonFile(metaPath),
|
|
646
697
|
readAuthSnapshotFile(authPath)
|
|
647
698
|
]);
|
|
648
|
-
|
|
699
|
+
let meta = parseSnapshotMeta(rawMeta);
|
|
649
700
|
if (meta.name !== name) throw new Error(`Account metadata name mismatch for "${name}".`);
|
|
650
|
-
|
|
701
|
+
const snapshotIdentity = getSnapshotIdentity(snapshot);
|
|
702
|
+
if (getMetaIdentity(meta) !== snapshotIdentity) if (canAutoMigrateLegacyChatGPTMeta(meta, snapshot)) {
|
|
703
|
+
meta = {
|
|
704
|
+
...meta,
|
|
705
|
+
account_id: getSnapshotAccountId(snapshot),
|
|
706
|
+
user_id: getSnapshotUserId(snapshot)
|
|
707
|
+
};
|
|
708
|
+
await this.writeAccountMeta(name, meta);
|
|
709
|
+
} else throw new Error(`Account metadata account_id mismatch for "${name}".`);
|
|
710
|
+
if (getMetaIdentity(meta) !== snapshotIdentity) throw new Error(`Account metadata account_id mismatch for "${name}".`);
|
|
651
711
|
return {
|
|
652
712
|
...meta,
|
|
713
|
+
identity: getMetaIdentity(meta),
|
|
653
714
|
authPath,
|
|
654
715
|
metaPath,
|
|
655
716
|
configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
|
|
@@ -669,12 +730,12 @@ var __webpack_modules__ = {
|
|
|
669
730
|
warnings.push(`Account "${entry.name}" is invalid: ${error.message}`);
|
|
670
731
|
}
|
|
671
732
|
const counts = new Map();
|
|
672
|
-
for (const account of accounts)counts.set(account.
|
|
733
|
+
for (const account of accounts)counts.set(account.identity, (counts.get(account.identity) ?? 0) + 1);
|
|
673
734
|
accounts.sort((left, right)=>left.name.localeCompare(right.name));
|
|
674
735
|
return {
|
|
675
736
|
accounts: accounts.map((account)=>({
|
|
676
737
|
...account,
|
|
677
|
-
duplicateAccountId: (counts.get(account.
|
|
738
|
+
duplicateAccountId: (counts.get(account.identity) ?? 0) > 1
|
|
678
739
|
})),
|
|
679
740
|
warnings
|
|
680
741
|
};
|
|
@@ -685,6 +746,8 @@ var __webpack_modules__ = {
|
|
|
685
746
|
exists: false,
|
|
686
747
|
auth_mode: null,
|
|
687
748
|
account_id: null,
|
|
749
|
+
user_id: null,
|
|
750
|
+
identity: null,
|
|
688
751
|
matched_accounts: [],
|
|
689
752
|
managed: false,
|
|
690
753
|
duplicate_match: false,
|
|
@@ -692,11 +755,15 @@ var __webpack_modules__ = {
|
|
|
692
755
|
};
|
|
693
756
|
const snapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
|
|
694
757
|
const currentIdentity = getSnapshotIdentity(snapshot);
|
|
695
|
-
const
|
|
758
|
+
const currentAccountId = getSnapshotAccountId(snapshot);
|
|
759
|
+
const currentUserId = getSnapshotUserId(snapshot) ?? null;
|
|
760
|
+
const matchedAccounts = accounts.filter((account)=>account.identity === currentIdentity).map((account)=>account.name);
|
|
696
761
|
return {
|
|
697
762
|
exists: true,
|
|
698
763
|
auth_mode: snapshot.auth_mode,
|
|
699
|
-
account_id:
|
|
764
|
+
account_id: currentAccountId,
|
|
765
|
+
user_id: currentUserId,
|
|
766
|
+
identity: currentIdentity,
|
|
700
767
|
matched_accounts: matchedAccounts,
|
|
701
768
|
managed: matchedAccounts.length > 0,
|
|
702
769
|
duplicate_match: matchedAccounts.length > 1,
|
|
@@ -714,9 +781,16 @@ var __webpack_modules__ = {
|
|
|
714
781
|
const authPath = this.accountAuthPath(name);
|
|
715
782
|
const metaPath = this.accountMetaPath(name);
|
|
716
783
|
const configPath = this.accountConfigPath(name);
|
|
784
|
+
const identity = getSnapshotIdentity(snapshot);
|
|
717
785
|
const accountExists = await pathExists(accountDir);
|
|
718
786
|
const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
|
|
719
787
|
if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
|
|
788
|
+
const { accounts } = await this.listAccounts();
|
|
789
|
+
const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.identity === identity);
|
|
790
|
+
if (duplicateIdentityAccounts.length > 0) {
|
|
791
|
+
const joinedNames = duplicateIdentityAccounts.map((account)=>`"${account.name}"`).join(", ");
|
|
792
|
+
throw new Error(`Identity ${identity} is already managed by ${joinedNames}.`);
|
|
793
|
+
}
|
|
720
794
|
this.validateConfigSnapshot(name, snapshot, rawConfig);
|
|
721
795
|
await ensureDirectory(accountDir, DIRECTORY_MODE);
|
|
722
796
|
await atomicWriteFile(authPath, `${rawSnapshot.trimEnd()}\n`);
|
|
@@ -787,7 +861,7 @@ var __webpack_modules__ = {
|
|
|
787
861
|
await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
|
|
788
862
|
}
|
|
789
863
|
const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
|
|
790
|
-
if (getSnapshotIdentity(writtenSnapshot) !== account.
|
|
864
|
+
if (getSnapshotIdentity(writtenSnapshot) !== account.identity) throw new Error(`Switch verification failed for account "${name}".`);
|
|
791
865
|
const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
|
|
792
866
|
meta.last_switched_at = new Date().toISOString();
|
|
793
867
|
meta.updated_at = meta.last_switched_at;
|
|
@@ -831,7 +905,8 @@ var __webpack_modules__ = {
|
|
|
831
905
|
await this.syncCurrentAuthIfMatching(result.authSnapshot);
|
|
832
906
|
}
|
|
833
907
|
meta.auth_mode = result.authSnapshot.auth_mode;
|
|
834
|
-
meta.account_id =
|
|
908
|
+
meta.account_id = getSnapshotAccountId(result.authSnapshot);
|
|
909
|
+
meta.user_id = getSnapshotUserId(result.authSnapshot);
|
|
835
910
|
meta.updated_at = now.toISOString();
|
|
836
911
|
meta.quota = result.quota;
|
|
837
912
|
await this.writeAccountMeta(name, meta);
|
|
@@ -944,7 +1019,7 @@ var __webpack_modules__ = {
|
|
|
944
1019
|
if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
|
|
945
1020
|
if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
|
|
946
1021
|
if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
|
|
947
|
-
if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.
|
|
1022
|
+
if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.identity} with another saved account.`);
|
|
948
1023
|
}
|
|
949
1024
|
let currentAuthPresent = false;
|
|
950
1025
|
if (await pathExists(this.paths.currentAuthPath)) {
|
|
@@ -1012,12 +1087,10 @@ Usage:
|
|
|
1012
1087
|
codexm list [name] [--json]
|
|
1013
1088
|
codexm save <name> [--force] [--json]
|
|
1014
1089
|
codexm update [--json]
|
|
1015
|
-
codexm quota refresh [name] [--json]
|
|
1016
1090
|
codexm switch <name> [--json]
|
|
1017
1091
|
codexm switch --auto [--dry-run] [--json]
|
|
1018
1092
|
codexm remove <name> [--yes] [--json]
|
|
1019
1093
|
codexm rename <old> <new> [--json]
|
|
1020
|
-
codexm doctor [--json]
|
|
1021
1094
|
|
|
1022
1095
|
Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
1023
1096
|
`);
|
|
@@ -1027,7 +1100,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1027
1100
|
if (status.exists) {
|
|
1028
1101
|
lines.push("Current auth: present");
|
|
1029
1102
|
lines.push(`Auth mode: ${status.auth_mode}`);
|
|
1030
|
-
lines.push(`Identity: ${maskAccountId(status.
|
|
1103
|
+
lines.push(`Identity: ${maskAccountId(status.identity ?? "")}`);
|
|
1031
1104
|
if (0 === status.matched_accounts.length) lines.push("Managed account: no (unmanaged)");
|
|
1032
1105
|
else if (1 === status.matched_accounts.length) lines.push(`Managed account: ${status.matched_accounts[0]}`);
|
|
1033
1106
|
else lines.push(`Managed account: multiple (${status.matched_accounts.join(", ")})`);
|
|
@@ -1035,16 +1108,6 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1035
1108
|
for (const warning of status.warnings)lines.push(`Warning: ${warning}`);
|
|
1036
1109
|
return lines.join("\n");
|
|
1037
1110
|
}
|
|
1038
|
-
function describeDoctor(report) {
|
|
1039
|
-
const lines = [
|
|
1040
|
-
report.healthy ? "Doctor checks passed." : "Doctor checks found issues.",
|
|
1041
|
-
`Saved accounts: ${report.account_count}`,
|
|
1042
|
-
`Current auth present: ${report.current_auth_present ? "yes" : "no"}`
|
|
1043
|
-
];
|
|
1044
|
-
for (const issue of report.issues)lines.push(`Issue: ${issue}`);
|
|
1045
|
-
for (const warning of report.warnings)lines.push(`Warning: ${warning}`);
|
|
1046
|
-
return lines.join("\n");
|
|
1047
|
-
}
|
|
1048
1111
|
function formatUsagePercent(window) {
|
|
1049
1112
|
if (!window) return "-";
|
|
1050
1113
|
return `${window.used_percent}%`;
|
|
@@ -1090,6 +1153,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1090
1153
|
return {
|
|
1091
1154
|
name: account.name,
|
|
1092
1155
|
account_id: account.account_id,
|
|
1156
|
+
identity: account.identity,
|
|
1093
1157
|
plan_type: account.plan_type,
|
|
1094
1158
|
available: computeAvailability(account),
|
|
1095
1159
|
refresh_status: "ok",
|
|
@@ -1122,7 +1186,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1122
1186
|
}
|
|
1123
1187
|
function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
|
|
1124
1188
|
const lines = [
|
|
1125
|
-
dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.
|
|
1189
|
+
dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.identity)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.identity)}).`,
|
|
1126
1190
|
`Score: ${candidate.effective_score}`,
|
|
1127
1191
|
`5H remaining: ${candidate.remain_5h}%`,
|
|
1128
1192
|
`1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
|
|
@@ -1133,7 +1197,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1133
1197
|
}
|
|
1134
1198
|
function describeAutoSwitchNoop(candidate, warnings) {
|
|
1135
1199
|
const lines = [
|
|
1136
|
-
`Current account "${candidate.name}" (${maskAccountId(candidate.
|
|
1200
|
+
`Current account "${candidate.name}" (${maskAccountId(candidate.identity)}) is already the best available account.`,
|
|
1137
1201
|
`Score: ${candidate.effective_score}`,
|
|
1138
1202
|
`5H remaining: ${candidate.remain_5h}%`,
|
|
1139
1203
|
`1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
|
|
@@ -1145,7 +1209,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1145
1209
|
if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
|
|
1146
1210
|
const table = formatTable(accounts.map((account)=>({
|
|
1147
1211
|
name: account.name,
|
|
1148
|
-
account_id: maskAccountId(account.
|
|
1212
|
+
account_id: maskAccountId(account.identity),
|
|
1149
1213
|
plan_type: account.plan_type ?? "-",
|
|
1150
1214
|
available: computeAvailability(account) ?? "-",
|
|
1151
1215
|
five_hour: formatUsagePercent(account.five_hour),
|
|
@@ -1270,11 +1334,13 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1270
1334
|
account: {
|
|
1271
1335
|
name: account.name,
|
|
1272
1336
|
account_id: account.account_id,
|
|
1337
|
+
user_id: account.user_id ?? null,
|
|
1338
|
+
identity: account.identity,
|
|
1273
1339
|
auth_mode: account.auth_mode
|
|
1274
1340
|
}
|
|
1275
1341
|
};
|
|
1276
1342
|
if (json) writeJson(streams.stdout, payload);
|
|
1277
|
-
else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.
|
|
1343
|
+
else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.identity)}).\n`);
|
|
1278
1344
|
return 0;
|
|
1279
1345
|
}
|
|
1280
1346
|
case "update":
|
|
@@ -1296,6 +1362,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1296
1362
|
account: {
|
|
1297
1363
|
name: result.account.name,
|
|
1298
1364
|
account_id: result.account.account_id,
|
|
1365
|
+
user_id: result.account.user_id ?? null,
|
|
1366
|
+
identity: result.account.identity,
|
|
1299
1367
|
auth_mode: result.account.auth_mode
|
|
1300
1368
|
},
|
|
1301
1369
|
quota,
|
|
@@ -1303,23 +1371,11 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1303
1371
|
};
|
|
1304
1372
|
if (json) writeJson(streams.stdout, payload);
|
|
1305
1373
|
else {
|
|
1306
|
-
streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.
|
|
1374
|
+
streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
|
|
1307
1375
|
for (const warning of warnings)streams.stdout.write(`Warning: ${warning}\n`);
|
|
1308
1376
|
}
|
|
1309
1377
|
return 0;
|
|
1310
1378
|
}
|
|
1311
|
-
case "quota":
|
|
1312
|
-
{
|
|
1313
|
-
const quotaCommand = parsed.positionals[0];
|
|
1314
|
-
if ("refresh" === quotaCommand) {
|
|
1315
|
-
const targetName = parsed.positionals[1];
|
|
1316
|
-
const result = await store.refreshAllQuotas(targetName);
|
|
1317
|
-
if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
|
|
1318
|
-
else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
|
|
1319
|
-
return 0 === result.failures.length ? 0 : 1;
|
|
1320
|
-
}
|
|
1321
|
-
throw new Error("Usage: codexm quota refresh [name] [--json]");
|
|
1322
|
-
}
|
|
1323
1379
|
case "switch":
|
|
1324
1380
|
{
|
|
1325
1381
|
const auto = parsed.flags.has("--auto");
|
|
@@ -1358,7 +1414,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1358
1414
|
reason: "already_current_best",
|
|
1359
1415
|
account: {
|
|
1360
1416
|
name: selected.name,
|
|
1361
|
-
account_id: selected.account_id
|
|
1417
|
+
account_id: selected.account_id,
|
|
1418
|
+
identity: selected.identity
|
|
1362
1419
|
},
|
|
1363
1420
|
selected,
|
|
1364
1421
|
candidates,
|
|
@@ -1378,6 +1435,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1378
1435
|
account: {
|
|
1379
1436
|
name: result.account.name,
|
|
1380
1437
|
account_id: result.account.account_id,
|
|
1438
|
+
user_id: result.account.user_id ?? null,
|
|
1439
|
+
identity: result.account.identity,
|
|
1381
1440
|
auth_mode: result.account.auth_mode
|
|
1382
1441
|
},
|
|
1383
1442
|
selected,
|
|
@@ -1407,6 +1466,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1407
1466
|
account: {
|
|
1408
1467
|
name: result.account.name,
|
|
1409
1468
|
account_id: result.account.account_id,
|
|
1469
|
+
user_id: result.account.user_id ?? null,
|
|
1470
|
+
identity: result.account.identity,
|
|
1410
1471
|
auth_mode: result.account.auth_mode
|
|
1411
1472
|
},
|
|
1412
1473
|
quota,
|
|
@@ -1415,7 +1476,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1415
1476
|
};
|
|
1416
1477
|
if (json) writeJson(streams.stdout, payload);
|
|
1417
1478
|
else {
|
|
1418
|
-
streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.
|
|
1479
|
+
streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
|
|
1419
1480
|
if (result.backup_path) streams.stdout.write(`Backup: ${result.backup_path}\n`);
|
|
1420
1481
|
for (const warning of result.warnings)streams.stdout.write(`Warning: ${warning}\n`);
|
|
1421
1482
|
}
|
|
@@ -1457,19 +1518,14 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
|
|
|
1457
1518
|
account: {
|
|
1458
1519
|
name: account.name,
|
|
1459
1520
|
account_id: account.account_id,
|
|
1521
|
+
user_id: account.user_id ?? null,
|
|
1522
|
+
identity: account.identity,
|
|
1460
1523
|
auth_mode: account.auth_mode
|
|
1461
1524
|
}
|
|
1462
1525
|
});
|
|
1463
1526
|
else streams.stdout.write(`Renamed "${oldName}" to "${newName}".\n`);
|
|
1464
1527
|
return 0;
|
|
1465
1528
|
}
|
|
1466
|
-
case "doctor":
|
|
1467
|
-
{
|
|
1468
|
-
const report = await store.doctor();
|
|
1469
|
-
if (json) writeJson(streams.stdout, report);
|
|
1470
|
-
else streams.stdout.write(`${describeDoctor(report)}\n`);
|
|
1471
|
-
return report.healthy ? 0 : 1;
|
|
1472
|
-
}
|
|
1473
1529
|
default:
|
|
1474
1530
|
throw new Error(`Unknown command "${parsed.command}".`);
|
|
1475
1531
|
}
|