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.js CHANGED
@@ -9,7 +9,7 @@ import { basename, dirname, join } from "node:path";
9
9
  import { execFile } from "node:child_process";
10
10
  import { promisify } from "node:util";
11
11
  var package_namespaceObject = {
12
- rE: "0.0.7"
12
+ rE: "0.0.9"
13
13
  };
14
14
  function isRecord(value) {
15
15
  return "object" == typeof value && null !== value && !Array.isArray(value);
@@ -42,10 +42,44 @@ function isSupportedChatGPTAuthMode(authMode) {
42
42
  const normalized = normalizeAuthMode(authMode);
43
43
  return "chatgpt" === normalized || "chatgpt_auth_tokens" === normalized;
44
44
  }
45
+ function extractAuthClaim(payload) {
46
+ const value = payload["https://api.openai.com/auth"];
47
+ return isRecord(value) ? value : void 0;
48
+ }
49
+ function extractStringClaim(payload, key) {
50
+ const value = payload[key];
51
+ return "string" == typeof value && "" !== value.trim() ? value : void 0;
52
+ }
53
+ function extractSnapshotJwtPayloads(snapshot) {
54
+ const tokens = snapshot.tokens ?? {};
55
+ const payloads = [];
56
+ for (const tokenName of [
57
+ "id_token",
58
+ "access_token"
59
+ ]){
60
+ const token = tokens[tokenName];
61
+ if ("string" == typeof token && "" !== token.trim()) try {
62
+ payloads.push(decodeJwtPayload(token));
63
+ } catch {}
64
+ }
65
+ return payloads;
66
+ }
67
+ function getSnapshotUserId(snapshot) {
68
+ for (const payload of extractSnapshotJwtPayloads(snapshot)){
69
+ const authClaim = extractAuthClaim(payload);
70
+ const chatGPTUserId = authClaim?.chatgpt_user_id;
71
+ if ("string" == typeof chatGPTUserId && "" !== chatGPTUserId.trim()) return chatGPTUserId;
72
+ const userId = extractStringClaim(payload, "user_id");
73
+ if (userId) return userId;
74
+ }
75
+ }
45
76
  function fingerprintApiKey(apiKey) {
46
77
  return createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
47
78
  }
48
- function getSnapshotIdentity(snapshot) {
79
+ function composeIdentity(accountId, userId) {
80
+ return "string" == typeof userId && "" !== userId.trim() ? `${accountId}:${userId}` : accountId;
81
+ }
82
+ function getSnapshotAccountId(snapshot) {
49
83
  if (isApiKeyAuthMode(snapshot.auth_mode)) {
50
84
  const apiKey = snapshot.OPENAI_API_KEY;
51
85
  if ("string" != typeof apiKey || "" === apiKey.trim()) throw new Error('Field "OPENAI_API_KEY" must be a non-empty string for apikey auth.');
@@ -55,6 +89,12 @@ function getSnapshotIdentity(snapshot) {
55
89
  if ("string" != typeof accountId || "" === accountId.trim()) throw new Error('Field "tokens.account_id" must be a non-empty string.');
56
90
  return accountId;
57
91
  }
92
+ function getSnapshotIdentity(snapshot) {
93
+ return composeIdentity(getSnapshotAccountId(snapshot), isSupportedChatGPTAuthMode(snapshot.auth_mode) ? getSnapshotUserId(snapshot) : void 0);
94
+ }
95
+ function getMetaIdentity(meta) {
96
+ return composeIdentity(meta.account_id, meta.user_id);
97
+ }
58
98
  function defaultQuotaSnapshot() {
59
99
  return {
60
100
  status: "stale"
@@ -132,7 +172,8 @@ function createSnapshotMeta(name, snapshot, now, existingCreatedAt) {
132
172
  return {
133
173
  name,
134
174
  auth_mode: snapshot.auth_mode,
135
- account_id: getSnapshotIdentity(snapshot),
175
+ account_id: getSnapshotAccountId(snapshot),
176
+ user_id: isSupportedChatGPTAuthMode(snapshot.auth_mode) ? getSnapshotUserId(snapshot) : void 0,
136
177
  created_at: existingCreatedAt ?? timestamp,
137
178
  updated_at: timestamp,
138
179
  last_switched_at: null,
@@ -153,6 +194,7 @@ function parseSnapshotMeta(raw) {
153
194
  name: asNonEmptyString(parsed.name, "name"),
154
195
  auth_mode: asNonEmptyString(parsed.auth_mode, "auth_mode"),
155
196
  account_id: asNonEmptyString(parsed.account_id, "account_id"),
197
+ user_id: asOptionalString(parsed.user_id, "user_id"),
156
198
  created_at: asNonEmptyString(parsed.created_at, "created_at"),
157
199
  updated_at: asNonEmptyString(parsed.updated_at, "updated_at"),
158
200
  last_switched_at: lastSwitchedAt,
@@ -177,11 +219,11 @@ const USER_AGENT = "codexm/0.1";
177
219
  function quota_client_isRecord(value) {
178
220
  return "object" == typeof value && null !== value && !Array.isArray(value);
179
221
  }
180
- function extractAuthClaim(payload) {
222
+ function quota_client_extractAuthClaim(payload) {
181
223
  const value = payload["https://api.openai.com/auth"];
182
224
  return quota_client_isRecord(value) ? value : void 0;
183
225
  }
184
- function extractStringClaim(payload, key) {
226
+ function quota_client_extractStringClaim(payload, key) {
185
227
  const value = payload[key];
186
228
  return "string" == typeof value && "" !== value.trim() ? value : void 0;
187
229
  }
@@ -194,7 +236,7 @@ function parsePlanType(snapshot) {
194
236
  const token = tokens[tokenName];
195
237
  if ("string" == typeof token && "" !== token.trim()) try {
196
238
  const payload = decodeJwtPayload(token);
197
- const authClaim = extractAuthClaim(payload);
239
+ const authClaim = quota_client_extractAuthClaim(payload);
198
240
  const planType = authClaim?.chatgpt_plan_type;
199
241
  if ("string" == typeof planType && "" !== planType.trim()) return planType;
200
242
  } catch {}
@@ -218,7 +260,7 @@ function extractChatGPTAuth(snapshot) {
218
260
  const token = tokens[tokenName];
219
261
  if ("string" == typeof token && "" !== token.trim()) try {
220
262
  const payload = decodeJwtPayload(token);
221
- const authClaim = extractAuthClaim(payload);
263
+ const authClaim = quota_client_extractAuthClaim(payload);
222
264
  if (!accountId) {
223
265
  const maybeAccountId = authClaim?.chatgpt_account_id;
224
266
  if ("string" == typeof maybeAccountId && "" !== maybeAccountId.trim()) accountId = maybeAccountId;
@@ -227,8 +269,8 @@ function extractChatGPTAuth(snapshot) {
227
269
  const maybePlanType = authClaim?.chatgpt_plan_type;
228
270
  if ("string" == typeof maybePlanType && "" !== maybePlanType.trim()) planType = maybePlanType;
229
271
  }
230
- issuer ??= extractStringClaim(payload, "iss");
231
- clientId ??= extractStringClaim(payload, "client_id") ?? extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
272
+ issuer ??= quota_client_extractStringClaim(payload, "iss");
273
+ clientId ??= quota_client_extractStringClaim(payload, "client_id") ?? quota_client_extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
232
274
  } catch {}
233
275
  }
234
276
  if (!supported) return {
@@ -498,6 +540,13 @@ async function pathExists(path) {
498
540
  async function readJsonFile(path) {
499
541
  return readFile(path, "utf8");
500
542
  }
543
+ function canAutoMigrateLegacyChatGPTMeta(meta, snapshot) {
544
+ if (!isSupportedChatGPTAuthMode(meta.auth_mode) || !isSupportedChatGPTAuthMode(snapshot.auth_mode)) return false;
545
+ if ("string" == typeof meta.user_id && "" !== meta.user_id.trim()) return false;
546
+ const snapshotUserId = getSnapshotUserId(snapshot);
547
+ if (!snapshotUserId) return false;
548
+ return meta.account_id === getSnapshotAccountId(snapshot);
549
+ }
501
550
  async function detectRunningCodexProcesses() {
502
551
  try {
503
552
  const { stdout } = await account_store_execFile("ps", [
@@ -595,6 +644,8 @@ class AccountStore {
595
644
  return {
596
645
  name: account.name,
597
646
  account_id: account.account_id,
647
+ user_id: account.user_id ?? null,
648
+ identity: account.identity,
598
649
  plan_type: planType,
599
650
  credits_balance: account.quota.credits_balance ?? null,
600
651
  status: account.quota.status,
@@ -635,11 +686,21 @@ class AccountStore {
635
686
  readJsonFile(metaPath),
636
687
  readAuthSnapshotFile(authPath)
637
688
  ]);
638
- const meta = parseSnapshotMeta(rawMeta);
689
+ let meta = parseSnapshotMeta(rawMeta);
639
690
  if (meta.name !== name) throw new Error(`Account metadata name mismatch for "${name}".`);
640
- if (meta.account_id !== getSnapshotIdentity(snapshot)) throw new Error(`Account metadata account_id mismatch for "${name}".`);
691
+ const snapshotIdentity = getSnapshotIdentity(snapshot);
692
+ if (getMetaIdentity(meta) !== snapshotIdentity) if (canAutoMigrateLegacyChatGPTMeta(meta, snapshot)) {
693
+ meta = {
694
+ ...meta,
695
+ account_id: getSnapshotAccountId(snapshot),
696
+ user_id: getSnapshotUserId(snapshot)
697
+ };
698
+ await this.writeAccountMeta(name, meta);
699
+ } else throw new Error(`Account metadata account_id mismatch for "${name}".`);
700
+ if (getMetaIdentity(meta) !== snapshotIdentity) throw new Error(`Account metadata account_id mismatch for "${name}".`);
641
701
  return {
642
702
  ...meta,
703
+ identity: getMetaIdentity(meta),
643
704
  authPath,
644
705
  metaPath,
645
706
  configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
@@ -659,12 +720,12 @@ class AccountStore {
659
720
  warnings.push(`Account "${entry.name}" is invalid: ${error.message}`);
660
721
  }
661
722
  const counts = new Map();
662
- for (const account of accounts)counts.set(account.account_id, (counts.get(account.account_id) ?? 0) + 1);
723
+ for (const account of accounts)counts.set(account.identity, (counts.get(account.identity) ?? 0) + 1);
663
724
  accounts.sort((left, right)=>left.name.localeCompare(right.name));
664
725
  return {
665
726
  accounts: accounts.map((account)=>({
666
727
  ...account,
667
- duplicateAccountId: (counts.get(account.account_id) ?? 0) > 1
728
+ duplicateAccountId: (counts.get(account.identity) ?? 0) > 1
668
729
  })),
669
730
  warnings
670
731
  };
@@ -675,6 +736,8 @@ class AccountStore {
675
736
  exists: false,
676
737
  auth_mode: null,
677
738
  account_id: null,
739
+ user_id: null,
740
+ identity: null,
678
741
  matched_accounts: [],
679
742
  managed: false,
680
743
  duplicate_match: false,
@@ -682,11 +745,15 @@ class AccountStore {
682
745
  };
683
746
  const snapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
684
747
  const currentIdentity = getSnapshotIdentity(snapshot);
685
- const matchedAccounts = accounts.filter((account)=>account.account_id === currentIdentity).map((account)=>account.name);
748
+ const currentAccountId = getSnapshotAccountId(snapshot);
749
+ const currentUserId = getSnapshotUserId(snapshot) ?? null;
750
+ const matchedAccounts = accounts.filter((account)=>account.identity === currentIdentity).map((account)=>account.name);
686
751
  return {
687
752
  exists: true,
688
753
  auth_mode: snapshot.auth_mode,
689
- account_id: currentIdentity,
754
+ account_id: currentAccountId,
755
+ user_id: currentUserId,
756
+ identity: currentIdentity,
690
757
  matched_accounts: matchedAccounts,
691
758
  managed: matchedAccounts.length > 0,
692
759
  duplicate_match: matchedAccounts.length > 1,
@@ -704,9 +771,16 @@ class AccountStore {
704
771
  const authPath = this.accountAuthPath(name);
705
772
  const metaPath = this.accountMetaPath(name);
706
773
  const configPath = this.accountConfigPath(name);
774
+ const identity = getSnapshotIdentity(snapshot);
707
775
  const accountExists = await pathExists(accountDir);
708
776
  const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
709
777
  if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
778
+ const { accounts } = await this.listAccounts();
779
+ const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.identity === identity);
780
+ if (duplicateIdentityAccounts.length > 0) {
781
+ const joinedNames = duplicateIdentityAccounts.map((account)=>`"${account.name}"`).join(", ");
782
+ throw new Error(`Identity ${identity} is already managed by ${joinedNames}.`);
783
+ }
710
784
  this.validateConfigSnapshot(name, snapshot, rawConfig);
711
785
  await ensureDirectory(accountDir, DIRECTORY_MODE);
712
786
  await atomicWriteFile(authPath, `${rawSnapshot.trimEnd()}\n`);
@@ -777,7 +851,7 @@ class AccountStore {
777
851
  await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
778
852
  }
779
853
  const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
780
- if (getSnapshotIdentity(writtenSnapshot) !== account.account_id) throw new Error(`Switch verification failed for account "${name}".`);
854
+ if (getSnapshotIdentity(writtenSnapshot) !== account.identity) throw new Error(`Switch verification failed for account "${name}".`);
781
855
  const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
782
856
  meta.last_switched_at = new Date().toISOString();
783
857
  meta.updated_at = meta.last_switched_at;
@@ -821,7 +895,8 @@ class AccountStore {
821
895
  await this.syncCurrentAuthIfMatching(result.authSnapshot);
822
896
  }
823
897
  meta.auth_mode = result.authSnapshot.auth_mode;
824
- meta.account_id = getSnapshotIdentity(result.authSnapshot);
898
+ meta.account_id = getSnapshotAccountId(result.authSnapshot);
899
+ meta.user_id = getSnapshotUserId(result.authSnapshot);
825
900
  meta.updated_at = now.toISOString();
826
901
  meta.quota = result.quota;
827
902
  await this.writeAccountMeta(name, meta);
@@ -934,7 +1009,7 @@ class AccountStore {
934
1009
  if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
935
1010
  if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
936
1011
  if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
937
- if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.account_id} with another saved account.`);
1012
+ if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.identity} with another saved account.`);
938
1013
  }
939
1014
  let currentAuthPresent = false;
940
1015
  if (await pathExists(this.paths.currentAuthPath)) {
@@ -1002,12 +1077,10 @@ Usage:
1002
1077
  codexm list [name] [--json]
1003
1078
  codexm save <name> [--force] [--json]
1004
1079
  codexm update [--json]
1005
- codexm quota refresh [name] [--json]
1006
1080
  codexm switch <name> [--json]
1007
1081
  codexm switch --auto [--dry-run] [--json]
1008
1082
  codexm remove <name> [--yes] [--json]
1009
1083
  codexm rename <old> <new> [--json]
1010
- codexm doctor [--json]
1011
1084
 
1012
1085
  Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1013
1086
  `);
@@ -1017,7 +1090,7 @@ function describeCurrentStatus(status) {
1017
1090
  if (status.exists) {
1018
1091
  lines.push("Current auth: present");
1019
1092
  lines.push(`Auth mode: ${status.auth_mode}`);
1020
- lines.push(`Identity: ${maskAccountId(status.account_id ?? "")}`);
1093
+ lines.push(`Identity: ${maskAccountId(status.identity ?? "")}`);
1021
1094
  if (0 === status.matched_accounts.length) lines.push("Managed account: no (unmanaged)");
1022
1095
  else if (1 === status.matched_accounts.length) lines.push(`Managed account: ${status.matched_accounts[0]}`);
1023
1096
  else lines.push(`Managed account: multiple (${status.matched_accounts.join(", ")})`);
@@ -1025,16 +1098,6 @@ function describeCurrentStatus(status) {
1025
1098
  for (const warning of status.warnings)lines.push(`Warning: ${warning}`);
1026
1099
  return lines.join("\n");
1027
1100
  }
1028
- function describeDoctor(report) {
1029
- const lines = [
1030
- report.healthy ? "Doctor checks passed." : "Doctor checks found issues.",
1031
- `Saved accounts: ${report.account_count}`,
1032
- `Current auth present: ${report.current_auth_present ? "yes" : "no"}`
1033
- ];
1034
- for (const issue of report.issues)lines.push(`Issue: ${issue}`);
1035
- for (const warning of report.warnings)lines.push(`Warning: ${warning}`);
1036
- return lines.join("\n");
1037
- }
1038
1101
  function formatUsagePercent(window) {
1039
1102
  if (!window) return "-";
1040
1103
  return `${window.used_percent}%`;
@@ -1080,6 +1143,7 @@ function toAutoSwitchCandidate(account) {
1080
1143
  return {
1081
1144
  name: account.name,
1082
1145
  account_id: account.account_id,
1146
+ identity: account.identity,
1083
1147
  plan_type: account.plan_type,
1084
1148
  available: computeAvailability(account),
1085
1149
  refresh_status: "ok",
@@ -1112,7 +1176,7 @@ function rankAutoSwitchCandidates(accounts) {
1112
1176
  }
1113
1177
  function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1114
1178
  const lines = [
1115
- dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.account_id)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.account_id)}).`,
1179
+ dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.identity)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.identity)}).`,
1116
1180
  `Score: ${candidate.effective_score}`,
1117
1181
  `5H remaining: ${candidate.remain_5h}%`,
1118
1182
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1123,7 +1187,7 @@ function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1123
1187
  }
1124
1188
  function describeAutoSwitchNoop(candidate, warnings) {
1125
1189
  const lines = [
1126
- `Current account "${candidate.name}" (${maskAccountId(candidate.account_id)}) is already the best available account.`,
1190
+ `Current account "${candidate.name}" (${maskAccountId(candidate.identity)}) is already the best available account.`,
1127
1191
  `Score: ${candidate.effective_score}`,
1128
1192
  `5H remaining: ${candidate.remain_5h}%`,
1129
1193
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1135,7 +1199,7 @@ function describeQuotaAccounts(accounts, warnings) {
1135
1199
  if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
1136
1200
  const table = formatTable(accounts.map((account)=>({
1137
1201
  name: account.name,
1138
- account_id: maskAccountId(account.account_id),
1202
+ account_id: maskAccountId(account.identity),
1139
1203
  plan_type: account.plan_type ?? "-",
1140
1204
  available: computeAvailability(account) ?? "-",
1141
1205
  five_hour: formatUsagePercent(account.five_hour),
@@ -1260,11 +1324,13 @@ async function runCli(argv, options = {}) {
1260
1324
  account: {
1261
1325
  name: account.name,
1262
1326
  account_id: account.account_id,
1327
+ user_id: account.user_id ?? null,
1328
+ identity: account.identity,
1263
1329
  auth_mode: account.auth_mode
1264
1330
  }
1265
1331
  };
1266
1332
  if (json) writeJson(streams.stdout, payload);
1267
- else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.account_id)}).\n`);
1333
+ else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.identity)}).\n`);
1268
1334
  return 0;
1269
1335
  }
1270
1336
  case "update":
@@ -1286,6 +1352,8 @@ async function runCli(argv, options = {}) {
1286
1352
  account: {
1287
1353
  name: result.account.name,
1288
1354
  account_id: result.account.account_id,
1355
+ user_id: result.account.user_id ?? null,
1356
+ identity: result.account.identity,
1289
1357
  auth_mode: result.account.auth_mode
1290
1358
  },
1291
1359
  quota,
@@ -1293,23 +1361,11 @@ async function runCli(argv, options = {}) {
1293
1361
  };
1294
1362
  if (json) writeJson(streams.stdout, payload);
1295
1363
  else {
1296
- streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1364
+ streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1297
1365
  for (const warning of warnings)streams.stdout.write(`Warning: ${warning}\n`);
1298
1366
  }
1299
1367
  return 0;
1300
1368
  }
1301
- case "quota":
1302
- {
1303
- const quotaCommand = parsed.positionals[0];
1304
- if ("refresh" === quotaCommand) {
1305
- const targetName = parsed.positionals[1];
1306
- const result = await store.refreshAllQuotas(targetName);
1307
- if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
1308
- else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
1309
- return 0 === result.failures.length ? 0 : 1;
1310
- }
1311
- throw new Error("Usage: codexm quota refresh [name] [--json]");
1312
- }
1313
1369
  case "switch":
1314
1370
  {
1315
1371
  const auto = parsed.flags.has("--auto");
@@ -1348,7 +1404,8 @@ async function runCli(argv, options = {}) {
1348
1404
  reason: "already_current_best",
1349
1405
  account: {
1350
1406
  name: selected.name,
1351
- account_id: selected.account_id
1407
+ account_id: selected.account_id,
1408
+ identity: selected.identity
1352
1409
  },
1353
1410
  selected,
1354
1411
  candidates,
@@ -1368,6 +1425,8 @@ async function runCli(argv, options = {}) {
1368
1425
  account: {
1369
1426
  name: result.account.name,
1370
1427
  account_id: result.account.account_id,
1428
+ user_id: result.account.user_id ?? null,
1429
+ identity: result.account.identity,
1371
1430
  auth_mode: result.account.auth_mode
1372
1431
  },
1373
1432
  selected,
@@ -1397,6 +1456,8 @@ async function runCli(argv, options = {}) {
1397
1456
  account: {
1398
1457
  name: result.account.name,
1399
1458
  account_id: result.account.account_id,
1459
+ user_id: result.account.user_id ?? null,
1460
+ identity: result.account.identity,
1400
1461
  auth_mode: result.account.auth_mode
1401
1462
  },
1402
1463
  quota,
@@ -1405,7 +1466,7 @@ async function runCli(argv, options = {}) {
1405
1466
  };
1406
1467
  if (json) writeJson(streams.stdout, payload);
1407
1468
  else {
1408
- streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1469
+ streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1409
1470
  if (result.backup_path) streams.stdout.write(`Backup: ${result.backup_path}\n`);
1410
1471
  for (const warning of result.warnings)streams.stdout.write(`Warning: ${warning}\n`);
1411
1472
  }
@@ -1447,19 +1508,14 @@ async function runCli(argv, options = {}) {
1447
1508
  account: {
1448
1509
  name: account.name,
1449
1510
  account_id: account.account_id,
1511
+ user_id: account.user_id ?? null,
1512
+ identity: account.identity,
1450
1513
  auth_mode: account.auth_mode
1451
1514
  }
1452
1515
  });
1453
1516
  else streams.stdout.write(`Renamed "${oldName}" to "${newName}".\n`);
1454
1517
  return 0;
1455
1518
  }
1456
- case "doctor":
1457
- {
1458
- const report = await store.doctor();
1459
- if (json) writeJson(streams.stdout, report);
1460
- else streams.stdout.write(`${describeDoctor(report)}\n`);
1461
- return report.healthy ? 0 : 1;
1462
- }
1463
1519
  default:
1464
1520
  throw new Error(`Unknown command "${parsed.command}".`);
1465
1521
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-team",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Manage multiple Codex ChatGPT auth snapshots and quota usage from the command line.",
5
5
  "license": "MIT",
6
6
  "type": "module",