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/dist/main.cjs CHANGED
@@ -43,7 +43,7 @@ var timezone_js_default = /*#__PURE__*/ __webpack_require__.n(timezone_js_namesp
43
43
  const utc_js_namespaceObject = require("dayjs/plugin/utc.js");
44
44
  var utc_js_default = /*#__PURE__*/ __webpack_require__.n(utc_js_namespaceObject);
45
45
  var package_namespaceObject = {
46
- rE: "0.0.7"
46
+ rE: "0.0.9"
47
47
  };
48
48
  const external_node_crypto_namespaceObject = require("node:crypto");
49
49
  const promises_namespaceObject = require("node:fs/promises");
@@ -78,10 +78,44 @@ function isSupportedChatGPTAuthMode(authMode) {
78
78
  const normalized = normalizeAuthMode(authMode);
79
79
  return "chatgpt" === normalized || "chatgpt_auth_tokens" === normalized;
80
80
  }
81
+ function extractAuthClaim(payload) {
82
+ const value = payload["https://api.openai.com/auth"];
83
+ return isRecord(value) ? value : void 0;
84
+ }
85
+ function extractStringClaim(payload, key) {
86
+ const value = payload[key];
87
+ return "string" == typeof value && "" !== value.trim() ? value : void 0;
88
+ }
89
+ function extractSnapshotJwtPayloads(snapshot) {
90
+ const tokens = snapshot.tokens ?? {};
91
+ const payloads = [];
92
+ for (const tokenName of [
93
+ "id_token",
94
+ "access_token"
95
+ ]){
96
+ const token = tokens[tokenName];
97
+ if ("string" == typeof token && "" !== token.trim()) try {
98
+ payloads.push(decodeJwtPayload(token));
99
+ } catch {}
100
+ }
101
+ return payloads;
102
+ }
103
+ function getSnapshotUserId(snapshot) {
104
+ for (const payload of extractSnapshotJwtPayloads(snapshot)){
105
+ const authClaim = extractAuthClaim(payload);
106
+ const chatGPTUserId = authClaim?.chatgpt_user_id;
107
+ if ("string" == typeof chatGPTUserId && "" !== chatGPTUserId.trim()) return chatGPTUserId;
108
+ const userId = extractStringClaim(payload, "user_id");
109
+ if (userId) return userId;
110
+ }
111
+ }
81
112
  function fingerprintApiKey(apiKey) {
82
113
  return (0, external_node_crypto_namespaceObject.createHash)("sha256").update(apiKey).digest("hex").slice(0, 16);
83
114
  }
84
- function getSnapshotIdentity(snapshot) {
115
+ function composeIdentity(accountId, userId) {
116
+ return "string" == typeof userId && "" !== userId.trim() ? `${accountId}:${userId}` : accountId;
117
+ }
118
+ function getSnapshotAccountId(snapshot) {
85
119
  if (isApiKeyAuthMode(snapshot.auth_mode)) {
86
120
  const apiKey = snapshot.OPENAI_API_KEY;
87
121
  if ("string" != typeof apiKey || "" === apiKey.trim()) throw new Error('Field "OPENAI_API_KEY" must be a non-empty string for apikey auth.');
@@ -91,6 +125,12 @@ function getSnapshotIdentity(snapshot) {
91
125
  if ("string" != typeof accountId || "" === accountId.trim()) throw new Error('Field "tokens.account_id" must be a non-empty string.');
92
126
  return accountId;
93
127
  }
128
+ function getSnapshotIdentity(snapshot) {
129
+ return composeIdentity(getSnapshotAccountId(snapshot), isSupportedChatGPTAuthMode(snapshot.auth_mode) ? getSnapshotUserId(snapshot) : void 0);
130
+ }
131
+ function getMetaIdentity(meta) {
132
+ return composeIdentity(meta.account_id, meta.user_id);
133
+ }
94
134
  function defaultQuotaSnapshot() {
95
135
  return {
96
136
  status: "stale"
@@ -168,7 +208,8 @@ function createSnapshotMeta(name, snapshot, now, existingCreatedAt) {
168
208
  return {
169
209
  name,
170
210
  auth_mode: snapshot.auth_mode,
171
- account_id: getSnapshotIdentity(snapshot),
211
+ account_id: getSnapshotAccountId(snapshot),
212
+ user_id: isSupportedChatGPTAuthMode(snapshot.auth_mode) ? getSnapshotUserId(snapshot) : void 0,
172
213
  created_at: existingCreatedAt ?? timestamp,
173
214
  updated_at: timestamp,
174
215
  last_switched_at: null,
@@ -189,6 +230,7 @@ function parseSnapshotMeta(raw) {
189
230
  name: asNonEmptyString(parsed.name, "name"),
190
231
  auth_mode: asNonEmptyString(parsed.auth_mode, "auth_mode"),
191
232
  account_id: asNonEmptyString(parsed.account_id, "account_id"),
233
+ user_id: asOptionalString(parsed.user_id, "user_id"),
192
234
  created_at: asNonEmptyString(parsed.created_at, "created_at"),
193
235
  updated_at: asNonEmptyString(parsed.updated_at, "updated_at"),
194
236
  last_switched_at: lastSwitchedAt,
@@ -217,11 +259,11 @@ const USER_AGENT = "codexm/0.1";
217
259
  function quota_client_isRecord(value) {
218
260
  return "object" == typeof value && null !== value && !Array.isArray(value);
219
261
  }
220
- function extractAuthClaim(payload) {
262
+ function quota_client_extractAuthClaim(payload) {
221
263
  const value = payload["https://api.openai.com/auth"];
222
264
  return quota_client_isRecord(value) ? value : void 0;
223
265
  }
224
- function extractStringClaim(payload, key) {
266
+ function quota_client_extractStringClaim(payload, key) {
225
267
  const value = payload[key];
226
268
  return "string" == typeof value && "" !== value.trim() ? value : void 0;
227
269
  }
@@ -234,7 +276,7 @@ function parsePlanType(snapshot) {
234
276
  const token = tokens[tokenName];
235
277
  if ("string" == typeof token && "" !== token.trim()) try {
236
278
  const payload = decodeJwtPayload(token);
237
- const authClaim = extractAuthClaim(payload);
279
+ const authClaim = quota_client_extractAuthClaim(payload);
238
280
  const planType = authClaim?.chatgpt_plan_type;
239
281
  if ("string" == typeof planType && "" !== planType.trim()) return planType;
240
282
  } catch {}
@@ -258,7 +300,7 @@ function extractChatGPTAuth(snapshot) {
258
300
  const token = tokens[tokenName];
259
301
  if ("string" == typeof token && "" !== token.trim()) try {
260
302
  const payload = decodeJwtPayload(token);
261
- const authClaim = extractAuthClaim(payload);
303
+ const authClaim = quota_client_extractAuthClaim(payload);
262
304
  if (!accountId) {
263
305
  const maybeAccountId = authClaim?.chatgpt_account_id;
264
306
  if ("string" == typeof maybeAccountId && "" !== maybeAccountId.trim()) accountId = maybeAccountId;
@@ -267,8 +309,8 @@ function extractChatGPTAuth(snapshot) {
267
309
  const maybePlanType = authClaim?.chatgpt_plan_type;
268
310
  if ("string" == typeof maybePlanType && "" !== maybePlanType.trim()) planType = maybePlanType;
269
311
  }
270
- issuer ??= extractStringClaim(payload, "iss");
271
- clientId ??= extractStringClaim(payload, "client_id") ?? extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
312
+ issuer ??= quota_client_extractStringClaim(payload, "iss");
313
+ clientId ??= quota_client_extractStringClaim(payload, "client_id") ?? quota_client_extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
272
314
  } catch {}
273
315
  }
274
316
  if (!supported) return {
@@ -538,6 +580,13 @@ async function pathExists(path) {
538
580
  async function readJsonFile(path) {
539
581
  return (0, promises_namespaceObject.readFile)(path, "utf8");
540
582
  }
583
+ function canAutoMigrateLegacyChatGPTMeta(meta, snapshot) {
584
+ if (!isSupportedChatGPTAuthMode(meta.auth_mode) || !isSupportedChatGPTAuthMode(snapshot.auth_mode)) return false;
585
+ if ("string" == typeof meta.user_id && "" !== meta.user_id.trim()) return false;
586
+ const snapshotUserId = getSnapshotUserId(snapshot);
587
+ if (!snapshotUserId) return false;
588
+ return meta.account_id === getSnapshotAccountId(snapshot);
589
+ }
541
590
  async function detectRunningCodexProcesses() {
542
591
  try {
543
592
  const { stdout } = await execFile("ps", [
@@ -635,6 +684,8 @@ class AccountStore {
635
684
  return {
636
685
  name: account.name,
637
686
  account_id: account.account_id,
687
+ user_id: account.user_id ?? null,
688
+ identity: account.identity,
638
689
  plan_type: planType,
639
690
  credits_balance: account.quota.credits_balance ?? null,
640
691
  status: account.quota.status,
@@ -675,11 +726,21 @@ class AccountStore {
675
726
  readJsonFile(metaPath),
676
727
  readAuthSnapshotFile(authPath)
677
728
  ]);
678
- const meta = parseSnapshotMeta(rawMeta);
729
+ let meta = parseSnapshotMeta(rawMeta);
679
730
  if (meta.name !== name) throw new Error(`Account metadata name mismatch for "${name}".`);
680
- if (meta.account_id !== getSnapshotIdentity(snapshot)) throw new Error(`Account metadata account_id mismatch for "${name}".`);
731
+ const snapshotIdentity = getSnapshotIdentity(snapshot);
732
+ if (getMetaIdentity(meta) !== snapshotIdentity) if (canAutoMigrateLegacyChatGPTMeta(meta, snapshot)) {
733
+ meta = {
734
+ ...meta,
735
+ account_id: getSnapshotAccountId(snapshot),
736
+ user_id: getSnapshotUserId(snapshot)
737
+ };
738
+ await this.writeAccountMeta(name, meta);
739
+ } else throw new Error(`Account metadata account_id mismatch for "${name}".`);
740
+ if (getMetaIdentity(meta) !== snapshotIdentity) throw new Error(`Account metadata account_id mismatch for "${name}".`);
681
741
  return {
682
742
  ...meta,
743
+ identity: getMetaIdentity(meta),
683
744
  authPath,
684
745
  metaPath,
685
746
  configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
@@ -699,12 +760,12 @@ class AccountStore {
699
760
  warnings.push(`Account "${entry.name}" is invalid: ${error.message}`);
700
761
  }
701
762
  const counts = new Map();
702
- for (const account of accounts)counts.set(account.account_id, (counts.get(account.account_id) ?? 0) + 1);
763
+ for (const account of accounts)counts.set(account.identity, (counts.get(account.identity) ?? 0) + 1);
703
764
  accounts.sort((left, right)=>left.name.localeCompare(right.name));
704
765
  return {
705
766
  accounts: accounts.map((account)=>({
706
767
  ...account,
707
- duplicateAccountId: (counts.get(account.account_id) ?? 0) > 1
768
+ duplicateAccountId: (counts.get(account.identity) ?? 0) > 1
708
769
  })),
709
770
  warnings
710
771
  };
@@ -715,6 +776,8 @@ class AccountStore {
715
776
  exists: false,
716
777
  auth_mode: null,
717
778
  account_id: null,
779
+ user_id: null,
780
+ identity: null,
718
781
  matched_accounts: [],
719
782
  managed: false,
720
783
  duplicate_match: false,
@@ -722,11 +785,15 @@ class AccountStore {
722
785
  };
723
786
  const snapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
724
787
  const currentIdentity = getSnapshotIdentity(snapshot);
725
- const matchedAccounts = accounts.filter((account)=>account.account_id === currentIdentity).map((account)=>account.name);
788
+ const currentAccountId = getSnapshotAccountId(snapshot);
789
+ const currentUserId = getSnapshotUserId(snapshot) ?? null;
790
+ const matchedAccounts = accounts.filter((account)=>account.identity === currentIdentity).map((account)=>account.name);
726
791
  return {
727
792
  exists: true,
728
793
  auth_mode: snapshot.auth_mode,
729
- account_id: currentIdentity,
794
+ account_id: currentAccountId,
795
+ user_id: currentUserId,
796
+ identity: currentIdentity,
730
797
  matched_accounts: matchedAccounts,
731
798
  managed: matchedAccounts.length > 0,
732
799
  duplicate_match: matchedAccounts.length > 1,
@@ -744,9 +811,16 @@ class AccountStore {
744
811
  const authPath = this.accountAuthPath(name);
745
812
  const metaPath = this.accountMetaPath(name);
746
813
  const configPath = this.accountConfigPath(name);
814
+ const identity = getSnapshotIdentity(snapshot);
747
815
  const accountExists = await pathExists(accountDir);
748
816
  const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
749
817
  if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
818
+ const { accounts } = await this.listAccounts();
819
+ const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.identity === identity);
820
+ if (duplicateIdentityAccounts.length > 0) {
821
+ const joinedNames = duplicateIdentityAccounts.map((account)=>`"${account.name}"`).join(", ");
822
+ throw new Error(`Identity ${identity} is already managed by ${joinedNames}.`);
823
+ }
750
824
  this.validateConfigSnapshot(name, snapshot, rawConfig);
751
825
  await ensureDirectory(accountDir, DIRECTORY_MODE);
752
826
  await atomicWriteFile(authPath, `${rawSnapshot.trimEnd()}\n`);
@@ -817,7 +891,7 @@ class AccountStore {
817
891
  await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
818
892
  }
819
893
  const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
820
- if (getSnapshotIdentity(writtenSnapshot) !== account.account_id) throw new Error(`Switch verification failed for account "${name}".`);
894
+ if (getSnapshotIdentity(writtenSnapshot) !== account.identity) throw new Error(`Switch verification failed for account "${name}".`);
821
895
  const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
822
896
  meta.last_switched_at = new Date().toISOString();
823
897
  meta.updated_at = meta.last_switched_at;
@@ -861,7 +935,8 @@ class AccountStore {
861
935
  await this.syncCurrentAuthIfMatching(result.authSnapshot);
862
936
  }
863
937
  meta.auth_mode = result.authSnapshot.auth_mode;
864
- meta.account_id = getSnapshotIdentity(result.authSnapshot);
938
+ meta.account_id = getSnapshotAccountId(result.authSnapshot);
939
+ meta.user_id = getSnapshotUserId(result.authSnapshot);
865
940
  meta.updated_at = now.toISOString();
866
941
  meta.quota = result.quota;
867
942
  await this.writeAccountMeta(name, meta);
@@ -974,7 +1049,7 @@ class AccountStore {
974
1049
  if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
975
1050
  if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
976
1051
  if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
977
- if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.account_id} with another saved account.`);
1052
+ if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.identity} with another saved account.`);
978
1053
  }
979
1054
  let currentAuthPresent = false;
980
1055
  if (await pathExists(this.paths.currentAuthPath)) {
@@ -1042,12 +1117,10 @@ Usage:
1042
1117
  codexm list [name] [--json]
1043
1118
  codexm save <name> [--force] [--json]
1044
1119
  codexm update [--json]
1045
- codexm quota refresh [name] [--json]
1046
1120
  codexm switch <name> [--json]
1047
1121
  codexm switch --auto [--dry-run] [--json]
1048
1122
  codexm remove <name> [--yes] [--json]
1049
1123
  codexm rename <old> <new> [--json]
1050
- codexm doctor [--json]
1051
1124
 
1052
1125
  Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1053
1126
  `);
@@ -1057,7 +1130,7 @@ function describeCurrentStatus(status) {
1057
1130
  if (status.exists) {
1058
1131
  lines.push("Current auth: present");
1059
1132
  lines.push(`Auth mode: ${status.auth_mode}`);
1060
- lines.push(`Identity: ${maskAccountId(status.account_id ?? "")}`);
1133
+ lines.push(`Identity: ${maskAccountId(status.identity ?? "")}`);
1061
1134
  if (0 === status.matched_accounts.length) lines.push("Managed account: no (unmanaged)");
1062
1135
  else if (1 === status.matched_accounts.length) lines.push(`Managed account: ${status.matched_accounts[0]}`);
1063
1136
  else lines.push(`Managed account: multiple (${status.matched_accounts.join(", ")})`);
@@ -1065,16 +1138,6 @@ function describeCurrentStatus(status) {
1065
1138
  for (const warning of status.warnings)lines.push(`Warning: ${warning}`);
1066
1139
  return lines.join("\n");
1067
1140
  }
1068
- function describeDoctor(report) {
1069
- const lines = [
1070
- report.healthy ? "Doctor checks passed." : "Doctor checks found issues.",
1071
- `Saved accounts: ${report.account_count}`,
1072
- `Current auth present: ${report.current_auth_present ? "yes" : "no"}`
1073
- ];
1074
- for (const issue of report.issues)lines.push(`Issue: ${issue}`);
1075
- for (const warning of report.warnings)lines.push(`Warning: ${warning}`);
1076
- return lines.join("\n");
1077
- }
1078
1141
  function formatUsagePercent(window) {
1079
1142
  if (!window) return "-";
1080
1143
  return `${window.used_percent}%`;
@@ -1120,6 +1183,7 @@ function toAutoSwitchCandidate(account) {
1120
1183
  return {
1121
1184
  name: account.name,
1122
1185
  account_id: account.account_id,
1186
+ identity: account.identity,
1123
1187
  plan_type: account.plan_type,
1124
1188
  available: computeAvailability(account),
1125
1189
  refresh_status: "ok",
@@ -1152,7 +1216,7 @@ function rankAutoSwitchCandidates(accounts) {
1152
1216
  }
1153
1217
  function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1154
1218
  const lines = [
1155
- dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.account_id)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.account_id)}).`,
1219
+ dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.identity)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.identity)}).`,
1156
1220
  `Score: ${candidate.effective_score}`,
1157
1221
  `5H remaining: ${candidate.remain_5h}%`,
1158
1222
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1163,7 +1227,7 @@ function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1163
1227
  }
1164
1228
  function describeAutoSwitchNoop(candidate, warnings) {
1165
1229
  const lines = [
1166
- `Current account "${candidate.name}" (${maskAccountId(candidate.account_id)}) is already the best available account.`,
1230
+ `Current account "${candidate.name}" (${maskAccountId(candidate.identity)}) is already the best available account.`,
1167
1231
  `Score: ${candidate.effective_score}`,
1168
1232
  `5H remaining: ${candidate.remain_5h}%`,
1169
1233
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1175,7 +1239,7 @@ function describeQuotaAccounts(accounts, warnings) {
1175
1239
  if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
1176
1240
  const table = formatTable(accounts.map((account)=>({
1177
1241
  name: account.name,
1178
- account_id: maskAccountId(account.account_id),
1242
+ account_id: maskAccountId(account.identity),
1179
1243
  plan_type: account.plan_type ?? "-",
1180
1244
  available: computeAvailability(account) ?? "-",
1181
1245
  five_hour: formatUsagePercent(account.five_hour),
@@ -1300,11 +1364,13 @@ async function runCli(argv, options = {}) {
1300
1364
  account: {
1301
1365
  name: account.name,
1302
1366
  account_id: account.account_id,
1367
+ user_id: account.user_id ?? null,
1368
+ identity: account.identity,
1303
1369
  auth_mode: account.auth_mode
1304
1370
  }
1305
1371
  };
1306
1372
  if (json) writeJson(streams.stdout, payload);
1307
- else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.account_id)}).\n`);
1373
+ else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.identity)}).\n`);
1308
1374
  return 0;
1309
1375
  }
1310
1376
  case "update":
@@ -1326,6 +1392,8 @@ async function runCli(argv, options = {}) {
1326
1392
  account: {
1327
1393
  name: result.account.name,
1328
1394
  account_id: result.account.account_id,
1395
+ user_id: result.account.user_id ?? null,
1396
+ identity: result.account.identity,
1329
1397
  auth_mode: result.account.auth_mode
1330
1398
  },
1331
1399
  quota,
@@ -1333,23 +1401,11 @@ async function runCli(argv, options = {}) {
1333
1401
  };
1334
1402
  if (json) writeJson(streams.stdout, payload);
1335
1403
  else {
1336
- streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1404
+ streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1337
1405
  for (const warning of warnings)streams.stdout.write(`Warning: ${warning}\n`);
1338
1406
  }
1339
1407
  return 0;
1340
1408
  }
1341
- case "quota":
1342
- {
1343
- const quotaCommand = parsed.positionals[0];
1344
- if ("refresh" === quotaCommand) {
1345
- const targetName = parsed.positionals[1];
1346
- const result = await store.refreshAllQuotas(targetName);
1347
- if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
1348
- else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
1349
- return 0 === result.failures.length ? 0 : 1;
1350
- }
1351
- throw new Error("Usage: codexm quota refresh [name] [--json]");
1352
- }
1353
1409
  case "switch":
1354
1410
  {
1355
1411
  const auto = parsed.flags.has("--auto");
@@ -1388,7 +1444,8 @@ async function runCli(argv, options = {}) {
1388
1444
  reason: "already_current_best",
1389
1445
  account: {
1390
1446
  name: selected.name,
1391
- account_id: selected.account_id
1447
+ account_id: selected.account_id,
1448
+ identity: selected.identity
1392
1449
  },
1393
1450
  selected,
1394
1451
  candidates,
@@ -1408,6 +1465,8 @@ async function runCli(argv, options = {}) {
1408
1465
  account: {
1409
1466
  name: result.account.name,
1410
1467
  account_id: result.account.account_id,
1468
+ user_id: result.account.user_id ?? null,
1469
+ identity: result.account.identity,
1411
1470
  auth_mode: result.account.auth_mode
1412
1471
  },
1413
1472
  selected,
@@ -1437,6 +1496,8 @@ async function runCli(argv, options = {}) {
1437
1496
  account: {
1438
1497
  name: result.account.name,
1439
1498
  account_id: result.account.account_id,
1499
+ user_id: result.account.user_id ?? null,
1500
+ identity: result.account.identity,
1440
1501
  auth_mode: result.account.auth_mode
1441
1502
  },
1442
1503
  quota,
@@ -1445,7 +1506,7 @@ async function runCli(argv, options = {}) {
1445
1506
  };
1446
1507
  if (json) writeJson(streams.stdout, payload);
1447
1508
  else {
1448
- streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1509
+ streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1449
1510
  if (result.backup_path) streams.stdout.write(`Backup: ${result.backup_path}\n`);
1450
1511
  for (const warning of result.warnings)streams.stdout.write(`Warning: ${warning}\n`);
1451
1512
  }
@@ -1487,19 +1548,14 @@ async function runCli(argv, options = {}) {
1487
1548
  account: {
1488
1549
  name: account.name,
1489
1550
  account_id: account.account_id,
1551
+ user_id: account.user_id ?? null,
1552
+ identity: account.identity,
1490
1553
  auth_mode: account.auth_mode
1491
1554
  }
1492
1555
  });
1493
1556
  else streams.stdout.write(`Renamed "${oldName}" to "${newName}".\n`);
1494
1557
  return 0;
1495
1558
  }
1496
- case "doctor":
1497
- {
1498
- const report = await store.doctor();
1499
- if (json) writeJson(streams.stdout, report);
1500
- else streams.stdout.write(`${describeDoctor(report)}\n`);
1501
- return report.healthy ? 0 : 1;
1502
- }
1503
1559
  default:
1504
1560
  throw new Error(`Unknown command "${parsed.command}".`);
1505
1561
  }