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/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.8"
12
+ rE: "0.0.10"
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,
@@ -174,14 +216,17 @@ function decodeJwtPayload(token) {
174
216
  }
175
217
  const DEFAULT_CHATGPT_BASE_URL = "https://chatgpt.com";
176
218
  const USER_AGENT = "codexm/0.1";
219
+ const USAGE_FETCH_ATTEMPTS = 3;
220
+ const USAGE_FETCH_RETRY_DELAY_MS = 250;
221
+ const USAGE_FETCH_TIMEOUT_MS = 15000;
177
222
  function quota_client_isRecord(value) {
178
223
  return "object" == typeof value && null !== value && !Array.isArray(value);
179
224
  }
180
- function extractAuthClaim(payload) {
225
+ function quota_client_extractAuthClaim(payload) {
181
226
  const value = payload["https://api.openai.com/auth"];
182
227
  return quota_client_isRecord(value) ? value : void 0;
183
228
  }
184
- function extractStringClaim(payload, key) {
229
+ function quota_client_extractStringClaim(payload, key) {
185
230
  const value = payload[key];
186
231
  return "string" == typeof value && "" !== value.trim() ? value : void 0;
187
232
  }
@@ -194,7 +239,7 @@ function parsePlanType(snapshot) {
194
239
  const token = tokens[tokenName];
195
240
  if ("string" == typeof token && "" !== token.trim()) try {
196
241
  const payload = decodeJwtPayload(token);
197
- const authClaim = extractAuthClaim(payload);
242
+ const authClaim = quota_client_extractAuthClaim(payload);
198
243
  const planType = authClaim?.chatgpt_plan_type;
199
244
  if ("string" == typeof planType && "" !== planType.trim()) return planType;
200
245
  } catch {}
@@ -218,7 +263,7 @@ function extractChatGPTAuth(snapshot) {
218
263
  const token = tokens[tokenName];
219
264
  if ("string" == typeof token && "" !== token.trim()) try {
220
265
  const payload = decodeJwtPayload(token);
221
- const authClaim = extractAuthClaim(payload);
266
+ const authClaim = quota_client_extractAuthClaim(payload);
222
267
  if (!accountId) {
223
268
  const maybeAccountId = authClaim?.chatgpt_account_id;
224
269
  if ("string" == typeof maybeAccountId && "" !== maybeAccountId.trim()) accountId = maybeAccountId;
@@ -227,8 +272,8 @@ function extractChatGPTAuth(snapshot) {
227
272
  const maybePlanType = authClaim?.chatgpt_plan_type;
228
273
  if ("string" == typeof maybePlanType && "" !== maybePlanType.trim()) planType = maybePlanType;
229
274
  }
230
- issuer ??= extractStringClaim(payload, "iss");
231
- clientId ??= extractStringClaim(payload, "client_id") ?? extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
275
+ issuer ??= quota_client_extractStringClaim(payload, "iss");
276
+ clientId ??= quota_client_extractStringClaim(payload, "client_id") ?? quota_client_extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
232
277
  } catch {}
233
278
  }
234
279
  if (!supported) return {
@@ -271,8 +316,6 @@ async function resolveUsageUrls(homeDir) {
271
316
  const normalizedBaseUrl = baseUrl.replace(/\/+$/u, "");
272
317
  const candidates = [
273
318
  `${normalizedBaseUrl}/backend-api/wham/usage`,
274
- `${normalizedBaseUrl}/wham/usage`,
275
- `${normalizedBaseUrl}/api/codex/usage`,
276
319
  "https://chatgpt.com/backend-api/wham/usage"
277
320
  ];
278
321
  return [
@@ -282,6 +325,19 @@ async function resolveUsageUrls(homeDir) {
282
325
  function normalizeFetchError(error) {
283
326
  return error instanceof Error ? error.message : String(error);
284
327
  }
328
+ async function delay(milliseconds) {
329
+ await new Promise((resolve)=>setTimeout(resolve, milliseconds));
330
+ }
331
+ function isTransientUsageStatus(status) {
332
+ return 408 === status || 429 === status || status >= 500 && status <= 599;
333
+ }
334
+ function isLastAttempt(attempt) {
335
+ return attempt >= USAGE_FETCH_ATTEMPTS;
336
+ }
337
+ function formatUsageAttemptError(url, attempt, message) {
338
+ const retryContext = USAGE_FETCH_ATTEMPTS > 1 ? ` attempt ${attempt}/${USAGE_FETCH_ATTEMPTS}` : "";
339
+ return `${url}${retryContext} -> ${message}`;
340
+ }
285
341
  function shouldRetryWithTokenRefresh(message) {
286
342
  const normalized = message.toLowerCase();
287
343
  return normalized.includes("401") || normalized.includes("403") || normalized.includes("unauthorized") || normalized.includes("invalid_token") || normalized.includes("deactivated_workspace");
@@ -337,7 +393,9 @@ async function requestUsage(snapshot, options) {
337
393
  const urls = await resolveUsageUrls(options.homeDir);
338
394
  const now = (options.now ?? new Date()).toISOString();
339
395
  const errors = [];
340
- for (const url of urls){
396
+ for (const url of urls)for(let attempt = 1; attempt <= USAGE_FETCH_ATTEMPTS; attempt += 1){
397
+ const abortController = new AbortController();
398
+ const timeout = setTimeout(()=>abortController.abort(), USAGE_FETCH_TIMEOUT_MS);
341
399
  let response;
342
400
  try {
343
401
  response = await fetchImpl(url, {
@@ -347,23 +405,35 @@ async function requestUsage(snapshot, options) {
347
405
  "ChatGPT-Account-Id": extracted.accountId,
348
406
  Accept: "application/json",
349
407
  "User-Agent": USER_AGENT
350
- }
408
+ },
409
+ signal: abortController.signal
351
410
  });
352
411
  } catch (error) {
353
- errors.push(`${url} -> ${normalizeFetchError(error)}`);
354
- continue;
412
+ clearTimeout(timeout);
413
+ const message = abortController.signal.aborted ? `timed out after ${USAGE_FETCH_TIMEOUT_MS}ms` : normalizeFetchError(error);
414
+ errors.push(formatUsageAttemptError(url, attempt, message));
415
+ if (!isLastAttempt(attempt)) {
416
+ await delay(USAGE_FETCH_RETRY_DELAY_MS * attempt);
417
+ continue;
418
+ }
419
+ break;
355
420
  }
421
+ clearTimeout(timeout);
356
422
  if (!response.ok) {
357
423
  const body = await response.text();
358
- errors.push(`${url} -> ${response.status}: ${body.slice(0, 140).replace(/\s+/gu, " ").trim()}`);
359
- continue;
424
+ errors.push(formatUsageAttemptError(url, attempt, `${response.status}: ${body.slice(0, 140).replace(/\s+/gu, " ").trim()}`));
425
+ if (isTransientUsageStatus(response.status) && !isLastAttempt(attempt)) {
426
+ await delay(USAGE_FETCH_RETRY_DELAY_MS * attempt);
427
+ continue;
428
+ }
429
+ break;
360
430
  }
361
431
  let payload;
362
432
  try {
363
433
  payload = await response.json();
364
434
  } catch (error) {
365
- errors.push(`${url} -> failed to parse JSON: ${normalizeFetchError(error)}`);
366
- continue;
435
+ errors.push(formatUsageAttemptError(url, attempt, `failed to parse JSON: ${normalizeFetchError(error)}`));
436
+ break;
367
437
  }
368
438
  return mapUsagePayload(payload, extracted.planType, now);
369
439
  }
@@ -498,6 +568,13 @@ async function pathExists(path) {
498
568
  async function readJsonFile(path) {
499
569
  return readFile(path, "utf8");
500
570
  }
571
+ function canAutoMigrateLegacyChatGPTMeta(meta, snapshot) {
572
+ if (!isSupportedChatGPTAuthMode(meta.auth_mode) || !isSupportedChatGPTAuthMode(snapshot.auth_mode)) return false;
573
+ if ("string" == typeof meta.user_id && "" !== meta.user_id.trim()) return false;
574
+ const snapshotUserId = getSnapshotUserId(snapshot);
575
+ if (!snapshotUserId) return false;
576
+ return meta.account_id === getSnapshotAccountId(snapshot);
577
+ }
501
578
  async function detectRunningCodexProcesses() {
502
579
  try {
503
580
  const { stdout } = await account_store_execFile("ps", [
@@ -595,6 +672,8 @@ class AccountStore {
595
672
  return {
596
673
  name: account.name,
597
674
  account_id: account.account_id,
675
+ user_id: account.user_id ?? null,
676
+ identity: account.identity,
598
677
  plan_type: planType,
599
678
  credits_balance: account.quota.credits_balance ?? null,
600
679
  status: account.quota.status,
@@ -635,11 +714,21 @@ class AccountStore {
635
714
  readJsonFile(metaPath),
636
715
  readAuthSnapshotFile(authPath)
637
716
  ]);
638
- const meta = parseSnapshotMeta(rawMeta);
717
+ let meta = parseSnapshotMeta(rawMeta);
639
718
  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}".`);
719
+ const snapshotIdentity = getSnapshotIdentity(snapshot);
720
+ if (getMetaIdentity(meta) !== snapshotIdentity) if (canAutoMigrateLegacyChatGPTMeta(meta, snapshot)) {
721
+ meta = {
722
+ ...meta,
723
+ account_id: getSnapshotAccountId(snapshot),
724
+ user_id: getSnapshotUserId(snapshot)
725
+ };
726
+ await this.writeAccountMeta(name, meta);
727
+ } else throw new Error(`Account metadata account_id mismatch for "${name}".`);
728
+ if (getMetaIdentity(meta) !== snapshotIdentity) throw new Error(`Account metadata account_id mismatch for "${name}".`);
641
729
  return {
642
730
  ...meta,
731
+ identity: getMetaIdentity(meta),
643
732
  authPath,
644
733
  metaPath,
645
734
  configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
@@ -659,12 +748,12 @@ class AccountStore {
659
748
  warnings.push(`Account "${entry.name}" is invalid: ${error.message}`);
660
749
  }
661
750
  const counts = new Map();
662
- for (const account of accounts)counts.set(account.account_id, (counts.get(account.account_id) ?? 0) + 1);
751
+ for (const account of accounts)counts.set(account.identity, (counts.get(account.identity) ?? 0) + 1);
663
752
  accounts.sort((left, right)=>left.name.localeCompare(right.name));
664
753
  return {
665
754
  accounts: accounts.map((account)=>({
666
755
  ...account,
667
- duplicateAccountId: (counts.get(account.account_id) ?? 0) > 1
756
+ duplicateAccountId: (counts.get(account.identity) ?? 0) > 1
668
757
  })),
669
758
  warnings
670
759
  };
@@ -675,6 +764,8 @@ class AccountStore {
675
764
  exists: false,
676
765
  auth_mode: null,
677
766
  account_id: null,
767
+ user_id: null,
768
+ identity: null,
678
769
  matched_accounts: [],
679
770
  managed: false,
680
771
  duplicate_match: false,
@@ -682,11 +773,15 @@ class AccountStore {
682
773
  };
683
774
  const snapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
684
775
  const currentIdentity = getSnapshotIdentity(snapshot);
685
- const matchedAccounts = accounts.filter((account)=>account.account_id === currentIdentity).map((account)=>account.name);
776
+ const currentAccountId = getSnapshotAccountId(snapshot);
777
+ const currentUserId = getSnapshotUserId(snapshot) ?? null;
778
+ const matchedAccounts = accounts.filter((account)=>account.identity === currentIdentity).map((account)=>account.name);
686
779
  return {
687
780
  exists: true,
688
781
  auth_mode: snapshot.auth_mode,
689
- account_id: currentIdentity,
782
+ account_id: currentAccountId,
783
+ user_id: currentUserId,
784
+ identity: currentIdentity,
690
785
  matched_accounts: matchedAccounts,
691
786
  managed: matchedAccounts.length > 0,
692
787
  duplicate_match: matchedAccounts.length > 1,
@@ -709,7 +804,7 @@ class AccountStore {
709
804
  const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
710
805
  if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
711
806
  const { accounts } = await this.listAccounts();
712
- const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.account_id === identity);
807
+ const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.identity === identity);
713
808
  if (duplicateIdentityAccounts.length > 0) {
714
809
  const joinedNames = duplicateIdentityAccounts.map((account)=>`"${account.name}"`).join(", ");
715
810
  throw new Error(`Identity ${identity} is already managed by ${joinedNames}.`);
@@ -784,7 +879,7 @@ class AccountStore {
784
879
  await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
785
880
  }
786
881
  const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
787
- if (getSnapshotIdentity(writtenSnapshot) !== account.account_id) throw new Error(`Switch verification failed for account "${name}".`);
882
+ if (getSnapshotIdentity(writtenSnapshot) !== account.identity) throw new Error(`Switch verification failed for account "${name}".`);
788
883
  const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
789
884
  meta.last_switched_at = new Date().toISOString();
790
885
  meta.updated_at = meta.last_switched_at;
@@ -828,7 +923,8 @@ class AccountStore {
828
923
  await this.syncCurrentAuthIfMatching(result.authSnapshot);
829
924
  }
830
925
  meta.auth_mode = result.authSnapshot.auth_mode;
831
- meta.account_id = getSnapshotIdentity(result.authSnapshot);
926
+ meta.account_id = getSnapshotAccountId(result.authSnapshot);
927
+ meta.user_id = getSnapshotUserId(result.authSnapshot);
832
928
  meta.updated_at = now.toISOString();
833
929
  meta.quota = result.quota;
834
930
  await this.writeAccountMeta(name, meta);
@@ -941,7 +1037,7 @@ class AccountStore {
941
1037
  if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
942
1038
  if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
943
1039
  if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
944
- if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.account_id} with another saved account.`);
1040
+ if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.identity} with another saved account.`);
945
1041
  }
946
1042
  let currentAuthPresent = false;
947
1043
  if (await pathExists(this.paths.currentAuthPath)) {
@@ -1022,7 +1118,7 @@ function describeCurrentStatus(status) {
1022
1118
  if (status.exists) {
1023
1119
  lines.push("Current auth: present");
1024
1120
  lines.push(`Auth mode: ${status.auth_mode}`);
1025
- lines.push(`Identity: ${maskAccountId(status.account_id ?? "")}`);
1121
+ lines.push(`Identity: ${maskAccountId(status.identity ?? "")}`);
1026
1122
  if (0 === status.matched_accounts.length) lines.push("Managed account: no (unmanaged)");
1027
1123
  else if (1 === status.matched_accounts.length) lines.push(`Managed account: ${status.matched_accounts[0]}`);
1028
1124
  else lines.push(`Managed account: multiple (${status.matched_accounts.join(", ")})`);
@@ -1075,6 +1171,7 @@ function toAutoSwitchCandidate(account) {
1075
1171
  return {
1076
1172
  name: account.name,
1077
1173
  account_id: account.account_id,
1174
+ identity: account.identity,
1078
1175
  plan_type: account.plan_type,
1079
1176
  available: computeAvailability(account),
1080
1177
  refresh_status: "ok",
@@ -1107,7 +1204,7 @@ function rankAutoSwitchCandidates(accounts) {
1107
1204
  }
1108
1205
  function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1109
1206
  const lines = [
1110
- dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.account_id)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.account_id)}).`,
1207
+ dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.identity)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.identity)}).`,
1111
1208
  `Score: ${candidate.effective_score}`,
1112
1209
  `5H remaining: ${candidate.remain_5h}%`,
1113
1210
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1118,7 +1215,7 @@ function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1118
1215
  }
1119
1216
  function describeAutoSwitchNoop(candidate, warnings) {
1120
1217
  const lines = [
1121
- `Current account "${candidate.name}" (${maskAccountId(candidate.account_id)}) is already the best available account.`,
1218
+ `Current account "${candidate.name}" (${maskAccountId(candidate.identity)}) is already the best available account.`,
1122
1219
  `Score: ${candidate.effective_score}`,
1123
1220
  `5H remaining: ${candidate.remain_5h}%`,
1124
1221
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1130,7 +1227,7 @@ function describeQuotaAccounts(accounts, warnings) {
1130
1227
  if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
1131
1228
  const table = formatTable(accounts.map((account)=>({
1132
1229
  name: account.name,
1133
- account_id: maskAccountId(account.account_id),
1230
+ account_id: maskAccountId(account.identity),
1134
1231
  plan_type: account.plan_type ?? "-",
1135
1232
  available: computeAvailability(account) ?? "-",
1136
1233
  five_hour: formatUsagePercent(account.five_hour),
@@ -1255,11 +1352,13 @@ async function runCli(argv, options = {}) {
1255
1352
  account: {
1256
1353
  name: account.name,
1257
1354
  account_id: account.account_id,
1355
+ user_id: account.user_id ?? null,
1356
+ identity: account.identity,
1258
1357
  auth_mode: account.auth_mode
1259
1358
  }
1260
1359
  };
1261
1360
  if (json) writeJson(streams.stdout, payload);
1262
- else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.account_id)}).\n`);
1361
+ else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.identity)}).\n`);
1263
1362
  return 0;
1264
1363
  }
1265
1364
  case "update":
@@ -1281,6 +1380,8 @@ async function runCli(argv, options = {}) {
1281
1380
  account: {
1282
1381
  name: result.account.name,
1283
1382
  account_id: result.account.account_id,
1383
+ user_id: result.account.user_id ?? null,
1384
+ identity: result.account.identity,
1284
1385
  auth_mode: result.account.auth_mode
1285
1386
  },
1286
1387
  quota,
@@ -1288,7 +1389,7 @@ async function runCli(argv, options = {}) {
1288
1389
  };
1289
1390
  if (json) writeJson(streams.stdout, payload);
1290
1391
  else {
1291
- streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1392
+ streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1292
1393
  for (const warning of warnings)streams.stdout.write(`Warning: ${warning}\n`);
1293
1394
  }
1294
1395
  return 0;
@@ -1331,7 +1432,8 @@ async function runCli(argv, options = {}) {
1331
1432
  reason: "already_current_best",
1332
1433
  account: {
1333
1434
  name: selected.name,
1334
- account_id: selected.account_id
1435
+ account_id: selected.account_id,
1436
+ identity: selected.identity
1335
1437
  },
1336
1438
  selected,
1337
1439
  candidates,
@@ -1351,6 +1453,8 @@ async function runCli(argv, options = {}) {
1351
1453
  account: {
1352
1454
  name: result.account.name,
1353
1455
  account_id: result.account.account_id,
1456
+ user_id: result.account.user_id ?? null,
1457
+ identity: result.account.identity,
1354
1458
  auth_mode: result.account.auth_mode
1355
1459
  },
1356
1460
  selected,
@@ -1380,6 +1484,8 @@ async function runCli(argv, options = {}) {
1380
1484
  account: {
1381
1485
  name: result.account.name,
1382
1486
  account_id: result.account.account_id,
1487
+ user_id: result.account.user_id ?? null,
1488
+ identity: result.account.identity,
1383
1489
  auth_mode: result.account.auth_mode
1384
1490
  },
1385
1491
  quota,
@@ -1388,7 +1494,7 @@ async function runCli(argv, options = {}) {
1388
1494
  };
1389
1495
  if (json) writeJson(streams.stdout, payload);
1390
1496
  else {
1391
- streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1497
+ streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1392
1498
  if (result.backup_path) streams.stdout.write(`Backup: ${result.backup_path}\n`);
1393
1499
  for (const warning of result.warnings)streams.stdout.write(`Warning: ${warning}\n`);
1394
1500
  }
@@ -1430,6 +1536,8 @@ async function runCli(argv, options = {}) {
1430
1536
  account: {
1431
1537
  name: account.name,
1432
1538
  account_id: account.account_id,
1539
+ user_id: account.user_id ?? null,
1540
+ identity: account.identity,
1433
1541
  auth_mode: account.auth_mode
1434
1542
  }
1435
1543
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-team",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "Manage multiple Codex ChatGPT auth snapshots and quota usage from the command line.",
5
5
  "license": "MIT",
6
6
  "type": "module",