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.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.8"
46
+ rE: "0.0.10"
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,
@@ -214,14 +256,17 @@ const external_node_child_process_namespaceObject = require("node:child_process"
214
256
  const external_node_util_namespaceObject = require("node:util");
215
257
  const DEFAULT_CHATGPT_BASE_URL = "https://chatgpt.com";
216
258
  const USER_AGENT = "codexm/0.1";
259
+ const USAGE_FETCH_ATTEMPTS = 3;
260
+ const USAGE_FETCH_RETRY_DELAY_MS = 250;
261
+ const USAGE_FETCH_TIMEOUT_MS = 15000;
217
262
  function quota_client_isRecord(value) {
218
263
  return "object" == typeof value && null !== value && !Array.isArray(value);
219
264
  }
220
- function extractAuthClaim(payload) {
265
+ function quota_client_extractAuthClaim(payload) {
221
266
  const value = payload["https://api.openai.com/auth"];
222
267
  return quota_client_isRecord(value) ? value : void 0;
223
268
  }
224
- function extractStringClaim(payload, key) {
269
+ function quota_client_extractStringClaim(payload, key) {
225
270
  const value = payload[key];
226
271
  return "string" == typeof value && "" !== value.trim() ? value : void 0;
227
272
  }
@@ -234,7 +279,7 @@ function parsePlanType(snapshot) {
234
279
  const token = tokens[tokenName];
235
280
  if ("string" == typeof token && "" !== token.trim()) try {
236
281
  const payload = decodeJwtPayload(token);
237
- const authClaim = extractAuthClaim(payload);
282
+ const authClaim = quota_client_extractAuthClaim(payload);
238
283
  const planType = authClaim?.chatgpt_plan_type;
239
284
  if ("string" == typeof planType && "" !== planType.trim()) return planType;
240
285
  } catch {}
@@ -258,7 +303,7 @@ function extractChatGPTAuth(snapshot) {
258
303
  const token = tokens[tokenName];
259
304
  if ("string" == typeof token && "" !== token.trim()) try {
260
305
  const payload = decodeJwtPayload(token);
261
- const authClaim = extractAuthClaim(payload);
306
+ const authClaim = quota_client_extractAuthClaim(payload);
262
307
  if (!accountId) {
263
308
  const maybeAccountId = authClaim?.chatgpt_account_id;
264
309
  if ("string" == typeof maybeAccountId && "" !== maybeAccountId.trim()) accountId = maybeAccountId;
@@ -267,8 +312,8 @@ function extractChatGPTAuth(snapshot) {
267
312
  const maybePlanType = authClaim?.chatgpt_plan_type;
268
313
  if ("string" == typeof maybePlanType && "" !== maybePlanType.trim()) planType = maybePlanType;
269
314
  }
270
- issuer ??= extractStringClaim(payload, "iss");
271
- clientId ??= extractStringClaim(payload, "client_id") ?? extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
315
+ issuer ??= quota_client_extractStringClaim(payload, "iss");
316
+ clientId ??= quota_client_extractStringClaim(payload, "client_id") ?? quota_client_extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
272
317
  } catch {}
273
318
  }
274
319
  if (!supported) return {
@@ -311,8 +356,6 @@ async function resolveUsageUrls(homeDir) {
311
356
  const normalizedBaseUrl = baseUrl.replace(/\/+$/u, "");
312
357
  const candidates = [
313
358
  `${normalizedBaseUrl}/backend-api/wham/usage`,
314
- `${normalizedBaseUrl}/wham/usage`,
315
- `${normalizedBaseUrl}/api/codex/usage`,
316
359
  "https://chatgpt.com/backend-api/wham/usage"
317
360
  ];
318
361
  return [
@@ -322,6 +365,19 @@ async function resolveUsageUrls(homeDir) {
322
365
  function normalizeFetchError(error) {
323
366
  return error instanceof Error ? error.message : String(error);
324
367
  }
368
+ async function delay(milliseconds) {
369
+ await new Promise((resolve)=>setTimeout(resolve, milliseconds));
370
+ }
371
+ function isTransientUsageStatus(status) {
372
+ return 408 === status || 429 === status || status >= 500 && status <= 599;
373
+ }
374
+ function isLastAttempt(attempt) {
375
+ return attempt >= USAGE_FETCH_ATTEMPTS;
376
+ }
377
+ function formatUsageAttemptError(url, attempt, message) {
378
+ const retryContext = USAGE_FETCH_ATTEMPTS > 1 ? ` attempt ${attempt}/${USAGE_FETCH_ATTEMPTS}` : "";
379
+ return `${url}${retryContext} -> ${message}`;
380
+ }
325
381
  function shouldRetryWithTokenRefresh(message) {
326
382
  const normalized = message.toLowerCase();
327
383
  return normalized.includes("401") || normalized.includes("403") || normalized.includes("unauthorized") || normalized.includes("invalid_token") || normalized.includes("deactivated_workspace");
@@ -377,7 +433,9 @@ async function requestUsage(snapshot, options) {
377
433
  const urls = await resolveUsageUrls(options.homeDir);
378
434
  const now = (options.now ?? new Date()).toISOString();
379
435
  const errors = [];
380
- for (const url of urls){
436
+ for (const url of urls)for(let attempt = 1; attempt <= USAGE_FETCH_ATTEMPTS; attempt += 1){
437
+ const abortController = new AbortController();
438
+ const timeout = setTimeout(()=>abortController.abort(), USAGE_FETCH_TIMEOUT_MS);
381
439
  let response;
382
440
  try {
383
441
  response = await fetchImpl(url, {
@@ -387,23 +445,35 @@ async function requestUsage(snapshot, options) {
387
445
  "ChatGPT-Account-Id": extracted.accountId,
388
446
  Accept: "application/json",
389
447
  "User-Agent": USER_AGENT
390
- }
448
+ },
449
+ signal: abortController.signal
391
450
  });
392
451
  } catch (error) {
393
- errors.push(`${url} -> ${normalizeFetchError(error)}`);
394
- continue;
452
+ clearTimeout(timeout);
453
+ const message = abortController.signal.aborted ? `timed out after ${USAGE_FETCH_TIMEOUT_MS}ms` : normalizeFetchError(error);
454
+ errors.push(formatUsageAttemptError(url, attempt, message));
455
+ if (!isLastAttempt(attempt)) {
456
+ await delay(USAGE_FETCH_RETRY_DELAY_MS * attempt);
457
+ continue;
458
+ }
459
+ break;
395
460
  }
461
+ clearTimeout(timeout);
396
462
  if (!response.ok) {
397
463
  const body = await response.text();
398
- errors.push(`${url} -> ${response.status}: ${body.slice(0, 140).replace(/\s+/gu, " ").trim()}`);
399
- continue;
464
+ errors.push(formatUsageAttemptError(url, attempt, `${response.status}: ${body.slice(0, 140).replace(/\s+/gu, " ").trim()}`));
465
+ if (isTransientUsageStatus(response.status) && !isLastAttempt(attempt)) {
466
+ await delay(USAGE_FETCH_RETRY_DELAY_MS * attempt);
467
+ continue;
468
+ }
469
+ break;
400
470
  }
401
471
  let payload;
402
472
  try {
403
473
  payload = await response.json();
404
474
  } catch (error) {
405
- errors.push(`${url} -> failed to parse JSON: ${normalizeFetchError(error)}`);
406
- continue;
475
+ errors.push(formatUsageAttemptError(url, attempt, `failed to parse JSON: ${normalizeFetchError(error)}`));
476
+ break;
407
477
  }
408
478
  return mapUsagePayload(payload, extracted.planType, now);
409
479
  }
@@ -538,6 +608,13 @@ async function pathExists(path) {
538
608
  async function readJsonFile(path) {
539
609
  return (0, promises_namespaceObject.readFile)(path, "utf8");
540
610
  }
611
+ function canAutoMigrateLegacyChatGPTMeta(meta, snapshot) {
612
+ if (!isSupportedChatGPTAuthMode(meta.auth_mode) || !isSupportedChatGPTAuthMode(snapshot.auth_mode)) return false;
613
+ if ("string" == typeof meta.user_id && "" !== meta.user_id.trim()) return false;
614
+ const snapshotUserId = getSnapshotUserId(snapshot);
615
+ if (!snapshotUserId) return false;
616
+ return meta.account_id === getSnapshotAccountId(snapshot);
617
+ }
541
618
  async function detectRunningCodexProcesses() {
542
619
  try {
543
620
  const { stdout } = await execFile("ps", [
@@ -635,6 +712,8 @@ class AccountStore {
635
712
  return {
636
713
  name: account.name,
637
714
  account_id: account.account_id,
715
+ user_id: account.user_id ?? null,
716
+ identity: account.identity,
638
717
  plan_type: planType,
639
718
  credits_balance: account.quota.credits_balance ?? null,
640
719
  status: account.quota.status,
@@ -675,11 +754,21 @@ class AccountStore {
675
754
  readJsonFile(metaPath),
676
755
  readAuthSnapshotFile(authPath)
677
756
  ]);
678
- const meta = parseSnapshotMeta(rawMeta);
757
+ let meta = parseSnapshotMeta(rawMeta);
679
758
  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}".`);
759
+ const snapshotIdentity = getSnapshotIdentity(snapshot);
760
+ if (getMetaIdentity(meta) !== snapshotIdentity) if (canAutoMigrateLegacyChatGPTMeta(meta, snapshot)) {
761
+ meta = {
762
+ ...meta,
763
+ account_id: getSnapshotAccountId(snapshot),
764
+ user_id: getSnapshotUserId(snapshot)
765
+ };
766
+ await this.writeAccountMeta(name, meta);
767
+ } else throw new Error(`Account metadata account_id mismatch for "${name}".`);
768
+ if (getMetaIdentity(meta) !== snapshotIdentity) throw new Error(`Account metadata account_id mismatch for "${name}".`);
681
769
  return {
682
770
  ...meta,
771
+ identity: getMetaIdentity(meta),
683
772
  authPath,
684
773
  metaPath,
685
774
  configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
@@ -699,12 +788,12 @@ class AccountStore {
699
788
  warnings.push(`Account "${entry.name}" is invalid: ${error.message}`);
700
789
  }
701
790
  const counts = new Map();
702
- for (const account of accounts)counts.set(account.account_id, (counts.get(account.account_id) ?? 0) + 1);
791
+ for (const account of accounts)counts.set(account.identity, (counts.get(account.identity) ?? 0) + 1);
703
792
  accounts.sort((left, right)=>left.name.localeCompare(right.name));
704
793
  return {
705
794
  accounts: accounts.map((account)=>({
706
795
  ...account,
707
- duplicateAccountId: (counts.get(account.account_id) ?? 0) > 1
796
+ duplicateAccountId: (counts.get(account.identity) ?? 0) > 1
708
797
  })),
709
798
  warnings
710
799
  };
@@ -715,6 +804,8 @@ class AccountStore {
715
804
  exists: false,
716
805
  auth_mode: null,
717
806
  account_id: null,
807
+ user_id: null,
808
+ identity: null,
718
809
  matched_accounts: [],
719
810
  managed: false,
720
811
  duplicate_match: false,
@@ -722,11 +813,15 @@ class AccountStore {
722
813
  };
723
814
  const snapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
724
815
  const currentIdentity = getSnapshotIdentity(snapshot);
725
- const matchedAccounts = accounts.filter((account)=>account.account_id === currentIdentity).map((account)=>account.name);
816
+ const currentAccountId = getSnapshotAccountId(snapshot);
817
+ const currentUserId = getSnapshotUserId(snapshot) ?? null;
818
+ const matchedAccounts = accounts.filter((account)=>account.identity === currentIdentity).map((account)=>account.name);
726
819
  return {
727
820
  exists: true,
728
821
  auth_mode: snapshot.auth_mode,
729
- account_id: currentIdentity,
822
+ account_id: currentAccountId,
823
+ user_id: currentUserId,
824
+ identity: currentIdentity,
730
825
  matched_accounts: matchedAccounts,
731
826
  managed: matchedAccounts.length > 0,
732
827
  duplicate_match: matchedAccounts.length > 1,
@@ -749,7 +844,7 @@ class AccountStore {
749
844
  const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
750
845
  if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
751
846
  const { accounts } = await this.listAccounts();
752
- const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.account_id === identity);
847
+ const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.identity === identity);
753
848
  if (duplicateIdentityAccounts.length > 0) {
754
849
  const joinedNames = duplicateIdentityAccounts.map((account)=>`"${account.name}"`).join(", ");
755
850
  throw new Error(`Identity ${identity} is already managed by ${joinedNames}.`);
@@ -824,7 +919,7 @@ class AccountStore {
824
919
  await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
825
920
  }
826
921
  const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
827
- if (getSnapshotIdentity(writtenSnapshot) !== account.account_id) throw new Error(`Switch verification failed for account "${name}".`);
922
+ if (getSnapshotIdentity(writtenSnapshot) !== account.identity) throw new Error(`Switch verification failed for account "${name}".`);
828
923
  const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
829
924
  meta.last_switched_at = new Date().toISOString();
830
925
  meta.updated_at = meta.last_switched_at;
@@ -868,7 +963,8 @@ class AccountStore {
868
963
  await this.syncCurrentAuthIfMatching(result.authSnapshot);
869
964
  }
870
965
  meta.auth_mode = result.authSnapshot.auth_mode;
871
- meta.account_id = getSnapshotIdentity(result.authSnapshot);
966
+ meta.account_id = getSnapshotAccountId(result.authSnapshot);
967
+ meta.user_id = getSnapshotUserId(result.authSnapshot);
872
968
  meta.updated_at = now.toISOString();
873
969
  meta.quota = result.quota;
874
970
  await this.writeAccountMeta(name, meta);
@@ -981,7 +1077,7 @@ class AccountStore {
981
1077
  if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
982
1078
  if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
983
1079
  if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
984
- if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.account_id} with another saved account.`);
1080
+ if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.identity} with another saved account.`);
985
1081
  }
986
1082
  let currentAuthPresent = false;
987
1083
  if (await pathExists(this.paths.currentAuthPath)) {
@@ -1062,7 +1158,7 @@ function describeCurrentStatus(status) {
1062
1158
  if (status.exists) {
1063
1159
  lines.push("Current auth: present");
1064
1160
  lines.push(`Auth mode: ${status.auth_mode}`);
1065
- lines.push(`Identity: ${maskAccountId(status.account_id ?? "")}`);
1161
+ lines.push(`Identity: ${maskAccountId(status.identity ?? "")}`);
1066
1162
  if (0 === status.matched_accounts.length) lines.push("Managed account: no (unmanaged)");
1067
1163
  else if (1 === status.matched_accounts.length) lines.push(`Managed account: ${status.matched_accounts[0]}`);
1068
1164
  else lines.push(`Managed account: multiple (${status.matched_accounts.join(", ")})`);
@@ -1115,6 +1211,7 @@ function toAutoSwitchCandidate(account) {
1115
1211
  return {
1116
1212
  name: account.name,
1117
1213
  account_id: account.account_id,
1214
+ identity: account.identity,
1118
1215
  plan_type: account.plan_type,
1119
1216
  available: computeAvailability(account),
1120
1217
  refresh_status: "ok",
@@ -1147,7 +1244,7 @@ function rankAutoSwitchCandidates(accounts) {
1147
1244
  }
1148
1245
  function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1149
1246
  const lines = [
1150
- dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.account_id)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.account_id)}).`,
1247
+ dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.identity)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.identity)}).`,
1151
1248
  `Score: ${candidate.effective_score}`,
1152
1249
  `5H remaining: ${candidate.remain_5h}%`,
1153
1250
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1158,7 +1255,7 @@ function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1158
1255
  }
1159
1256
  function describeAutoSwitchNoop(candidate, warnings) {
1160
1257
  const lines = [
1161
- `Current account "${candidate.name}" (${maskAccountId(candidate.account_id)}) is already the best available account.`,
1258
+ `Current account "${candidate.name}" (${maskAccountId(candidate.identity)}) is already the best available account.`,
1162
1259
  `Score: ${candidate.effective_score}`,
1163
1260
  `5H remaining: ${candidate.remain_5h}%`,
1164
1261
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1170,7 +1267,7 @@ function describeQuotaAccounts(accounts, warnings) {
1170
1267
  if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
1171
1268
  const table = formatTable(accounts.map((account)=>({
1172
1269
  name: account.name,
1173
- account_id: maskAccountId(account.account_id),
1270
+ account_id: maskAccountId(account.identity),
1174
1271
  plan_type: account.plan_type ?? "-",
1175
1272
  available: computeAvailability(account) ?? "-",
1176
1273
  five_hour: formatUsagePercent(account.five_hour),
@@ -1295,11 +1392,13 @@ async function runCli(argv, options = {}) {
1295
1392
  account: {
1296
1393
  name: account.name,
1297
1394
  account_id: account.account_id,
1395
+ user_id: account.user_id ?? null,
1396
+ identity: account.identity,
1298
1397
  auth_mode: account.auth_mode
1299
1398
  }
1300
1399
  };
1301
1400
  if (json) writeJson(streams.stdout, payload);
1302
- else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.account_id)}).\n`);
1401
+ else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.identity)}).\n`);
1303
1402
  return 0;
1304
1403
  }
1305
1404
  case "update":
@@ -1321,6 +1420,8 @@ async function runCli(argv, options = {}) {
1321
1420
  account: {
1322
1421
  name: result.account.name,
1323
1422
  account_id: result.account.account_id,
1423
+ user_id: result.account.user_id ?? null,
1424
+ identity: result.account.identity,
1324
1425
  auth_mode: result.account.auth_mode
1325
1426
  },
1326
1427
  quota,
@@ -1328,7 +1429,7 @@ async function runCli(argv, options = {}) {
1328
1429
  };
1329
1430
  if (json) writeJson(streams.stdout, payload);
1330
1431
  else {
1331
- streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1432
+ streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1332
1433
  for (const warning of warnings)streams.stdout.write(`Warning: ${warning}\n`);
1333
1434
  }
1334
1435
  return 0;
@@ -1371,7 +1472,8 @@ async function runCli(argv, options = {}) {
1371
1472
  reason: "already_current_best",
1372
1473
  account: {
1373
1474
  name: selected.name,
1374
- account_id: selected.account_id
1475
+ account_id: selected.account_id,
1476
+ identity: selected.identity
1375
1477
  },
1376
1478
  selected,
1377
1479
  candidates,
@@ -1391,6 +1493,8 @@ async function runCli(argv, options = {}) {
1391
1493
  account: {
1392
1494
  name: result.account.name,
1393
1495
  account_id: result.account.account_id,
1496
+ user_id: result.account.user_id ?? null,
1497
+ identity: result.account.identity,
1394
1498
  auth_mode: result.account.auth_mode
1395
1499
  },
1396
1500
  selected,
@@ -1420,6 +1524,8 @@ async function runCli(argv, options = {}) {
1420
1524
  account: {
1421
1525
  name: result.account.name,
1422
1526
  account_id: result.account.account_id,
1527
+ user_id: result.account.user_id ?? null,
1528
+ identity: result.account.identity,
1423
1529
  auth_mode: result.account.auth_mode
1424
1530
  },
1425
1531
  quota,
@@ -1428,7 +1534,7 @@ async function runCli(argv, options = {}) {
1428
1534
  };
1429
1535
  if (json) writeJson(streams.stdout, payload);
1430
1536
  else {
1431
- streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1537
+ streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1432
1538
  if (result.backup_path) streams.stdout.write(`Backup: ${result.backup_path}\n`);
1433
1539
  for (const warning of result.warnings)streams.stdout.write(`Warning: ${warning}\n`);
1434
1540
  }
@@ -1470,6 +1576,8 @@ async function runCli(argv, options = {}) {
1470
1576
  account: {
1471
1577
  name: account.name,
1472
1578
  account_id: account.account_id,
1579
+ user_id: account.user_id ?? null,
1580
+ identity: account.identity,
1473
1581
  auth_mode: account.auth_mode
1474
1582
  }
1475
1583
  });