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/README.md CHANGED
@@ -36,6 +36,8 @@ Use `--json` on query and mutation commands when you need machine-readable outpu
36
36
  4. Switch between saved accounts with `codexm switch <name>` or let the tool choose with `codexm switch --auto`.
37
37
  5. Refresh and inspect quota usage with `codexm list`.
38
38
 
39
+ For ChatGPT auth snapshots, `codex-team` can save and switch different users under the same ChatGPT account/workspace as separate managed entries when the local login tokens distinguish them.
40
+
39
41
  ## Development
40
42
 
41
43
  ```bash
package/dist/cli.cjs CHANGED
@@ -13,7 +13,7 @@ var __webpack_modules__ = {
13
13
  const utc_js_namespaceObject = require("dayjs/plugin/utc.js");
14
14
  var utc_js_default = /*#__PURE__*/ __webpack_require__.n(utc_js_namespaceObject);
15
15
  var package_namespaceObject = {
16
- rE: "0.0.8"
16
+ rE: "0.0.10"
17
17
  };
18
18
  const external_node_crypto_namespaceObject = require("node:crypto");
19
19
  const promises_namespaceObject = require("node:fs/promises");
@@ -48,10 +48,44 @@ var __webpack_modules__ = {
48
48
  const normalized = normalizeAuthMode(authMode);
49
49
  return "chatgpt" === normalized || "chatgpt_auth_tokens" === normalized;
50
50
  }
51
+ function extractAuthClaim(payload) {
52
+ const value = payload["https://api.openai.com/auth"];
53
+ return isRecord(value) ? value : void 0;
54
+ }
55
+ function extractStringClaim(payload, key) {
56
+ const value = payload[key];
57
+ return "string" == typeof value && "" !== value.trim() ? value : void 0;
58
+ }
59
+ function extractSnapshotJwtPayloads(snapshot) {
60
+ const tokens = snapshot.tokens ?? {};
61
+ const payloads = [];
62
+ for (const tokenName of [
63
+ "id_token",
64
+ "access_token"
65
+ ]){
66
+ const token = tokens[tokenName];
67
+ if ("string" == typeof token && "" !== token.trim()) try {
68
+ payloads.push(decodeJwtPayload(token));
69
+ } catch {}
70
+ }
71
+ return payloads;
72
+ }
73
+ function getSnapshotUserId(snapshot) {
74
+ for (const payload of extractSnapshotJwtPayloads(snapshot)){
75
+ const authClaim = extractAuthClaim(payload);
76
+ const chatGPTUserId = authClaim?.chatgpt_user_id;
77
+ if ("string" == typeof chatGPTUserId && "" !== chatGPTUserId.trim()) return chatGPTUserId;
78
+ const userId = extractStringClaim(payload, "user_id");
79
+ if (userId) return userId;
80
+ }
81
+ }
51
82
  function fingerprintApiKey(apiKey) {
52
83
  return (0, external_node_crypto_namespaceObject.createHash)("sha256").update(apiKey).digest("hex").slice(0, 16);
53
84
  }
54
- function getSnapshotIdentity(snapshot) {
85
+ function composeIdentity(accountId, userId) {
86
+ return "string" == typeof userId && "" !== userId.trim() ? `${accountId}:${userId}` : accountId;
87
+ }
88
+ function getSnapshotAccountId(snapshot) {
55
89
  if (isApiKeyAuthMode(snapshot.auth_mode)) {
56
90
  const apiKey = snapshot.OPENAI_API_KEY;
57
91
  if ("string" != typeof apiKey || "" === apiKey.trim()) throw new Error('Field "OPENAI_API_KEY" must be a non-empty string for apikey auth.');
@@ -61,6 +95,12 @@ var __webpack_modules__ = {
61
95
  if ("string" != typeof accountId || "" === accountId.trim()) throw new Error('Field "tokens.account_id" must be a non-empty string.');
62
96
  return accountId;
63
97
  }
98
+ function getSnapshotIdentity(snapshot) {
99
+ return composeIdentity(getSnapshotAccountId(snapshot), isSupportedChatGPTAuthMode(snapshot.auth_mode) ? getSnapshotUserId(snapshot) : void 0);
100
+ }
101
+ function getMetaIdentity(meta) {
102
+ return composeIdentity(meta.account_id, meta.user_id);
103
+ }
64
104
  function defaultQuotaSnapshot() {
65
105
  return {
66
106
  status: "stale"
@@ -138,7 +178,8 @@ var __webpack_modules__ = {
138
178
  return {
139
179
  name,
140
180
  auth_mode: snapshot.auth_mode,
141
- account_id: getSnapshotIdentity(snapshot),
181
+ account_id: getSnapshotAccountId(snapshot),
182
+ user_id: isSupportedChatGPTAuthMode(snapshot.auth_mode) ? getSnapshotUserId(snapshot) : void 0,
142
183
  created_at: existingCreatedAt ?? timestamp,
143
184
  updated_at: timestamp,
144
185
  last_switched_at: null,
@@ -159,6 +200,7 @@ var __webpack_modules__ = {
159
200
  name: asNonEmptyString(parsed.name, "name"),
160
201
  auth_mode: asNonEmptyString(parsed.auth_mode, "auth_mode"),
161
202
  account_id: asNonEmptyString(parsed.account_id, "account_id"),
203
+ user_id: asOptionalString(parsed.user_id, "user_id"),
162
204
  created_at: asNonEmptyString(parsed.created_at, "created_at"),
163
205
  updated_at: asNonEmptyString(parsed.updated_at, "updated_at"),
164
206
  last_switched_at: lastSwitchedAt,
@@ -184,14 +226,17 @@ var __webpack_modules__ = {
184
226
  const external_node_util_namespaceObject = require("node:util");
185
227
  const DEFAULT_CHATGPT_BASE_URL = "https://chatgpt.com";
186
228
  const USER_AGENT = "codexm/0.1";
229
+ const USAGE_FETCH_ATTEMPTS = 3;
230
+ const USAGE_FETCH_RETRY_DELAY_MS = 250;
231
+ const USAGE_FETCH_TIMEOUT_MS = 15000;
187
232
  function quota_client_isRecord(value) {
188
233
  return "object" == typeof value && null !== value && !Array.isArray(value);
189
234
  }
190
- function extractAuthClaim(payload) {
235
+ function quota_client_extractAuthClaim(payload) {
191
236
  const value = payload["https://api.openai.com/auth"];
192
237
  return quota_client_isRecord(value) ? value : void 0;
193
238
  }
194
- function extractStringClaim(payload, key) {
239
+ function quota_client_extractStringClaim(payload, key) {
195
240
  const value = payload[key];
196
241
  return "string" == typeof value && "" !== value.trim() ? value : void 0;
197
242
  }
@@ -204,7 +249,7 @@ var __webpack_modules__ = {
204
249
  const token = tokens[tokenName];
205
250
  if ("string" == typeof token && "" !== token.trim()) try {
206
251
  const payload = decodeJwtPayload(token);
207
- const authClaim = extractAuthClaim(payload);
252
+ const authClaim = quota_client_extractAuthClaim(payload);
208
253
  const planType = authClaim?.chatgpt_plan_type;
209
254
  if ("string" == typeof planType && "" !== planType.trim()) return planType;
210
255
  } catch {}
@@ -228,7 +273,7 @@ var __webpack_modules__ = {
228
273
  const token = tokens[tokenName];
229
274
  if ("string" == typeof token && "" !== token.trim()) try {
230
275
  const payload = decodeJwtPayload(token);
231
- const authClaim = extractAuthClaim(payload);
276
+ const authClaim = quota_client_extractAuthClaim(payload);
232
277
  if (!accountId) {
233
278
  const maybeAccountId = authClaim?.chatgpt_account_id;
234
279
  if ("string" == typeof maybeAccountId && "" !== maybeAccountId.trim()) accountId = maybeAccountId;
@@ -237,8 +282,8 @@ var __webpack_modules__ = {
237
282
  const maybePlanType = authClaim?.chatgpt_plan_type;
238
283
  if ("string" == typeof maybePlanType && "" !== maybePlanType.trim()) planType = maybePlanType;
239
284
  }
240
- issuer ??= extractStringClaim(payload, "iss");
241
- clientId ??= extractStringClaim(payload, "client_id") ?? extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
285
+ issuer ??= quota_client_extractStringClaim(payload, "iss");
286
+ clientId ??= quota_client_extractStringClaim(payload, "client_id") ?? quota_client_extractStringClaim(payload, "azp") ?? ("string" == typeof payload.aud ? payload.aud : void 0);
242
287
  } catch {}
243
288
  }
244
289
  if (!supported) return {
@@ -281,8 +326,6 @@ var __webpack_modules__ = {
281
326
  const normalizedBaseUrl = baseUrl.replace(/\/+$/u, "");
282
327
  const candidates = [
283
328
  `${normalizedBaseUrl}/backend-api/wham/usage`,
284
- `${normalizedBaseUrl}/wham/usage`,
285
- `${normalizedBaseUrl}/api/codex/usage`,
286
329
  "https://chatgpt.com/backend-api/wham/usage"
287
330
  ];
288
331
  return [
@@ -292,6 +335,19 @@ var __webpack_modules__ = {
292
335
  function normalizeFetchError(error) {
293
336
  return error instanceof Error ? error.message : String(error);
294
337
  }
338
+ async function delay(milliseconds) {
339
+ await new Promise((resolve)=>setTimeout(resolve, milliseconds));
340
+ }
341
+ function isTransientUsageStatus(status) {
342
+ return 408 === status || 429 === status || status >= 500 && status <= 599;
343
+ }
344
+ function isLastAttempt(attempt) {
345
+ return attempt >= USAGE_FETCH_ATTEMPTS;
346
+ }
347
+ function formatUsageAttemptError(url, attempt, message) {
348
+ const retryContext = USAGE_FETCH_ATTEMPTS > 1 ? ` attempt ${attempt}/${USAGE_FETCH_ATTEMPTS}` : "";
349
+ return `${url}${retryContext} -> ${message}`;
350
+ }
295
351
  function shouldRetryWithTokenRefresh(message) {
296
352
  const normalized = message.toLowerCase();
297
353
  return normalized.includes("401") || normalized.includes("403") || normalized.includes("unauthorized") || normalized.includes("invalid_token") || normalized.includes("deactivated_workspace");
@@ -347,7 +403,9 @@ var __webpack_modules__ = {
347
403
  const urls = await resolveUsageUrls(options.homeDir);
348
404
  const now = (options.now ?? new Date()).toISOString();
349
405
  const errors = [];
350
- for (const url of urls){
406
+ for (const url of urls)for(let attempt = 1; attempt <= USAGE_FETCH_ATTEMPTS; attempt += 1){
407
+ const abortController = new AbortController();
408
+ const timeout = setTimeout(()=>abortController.abort(), USAGE_FETCH_TIMEOUT_MS);
351
409
  let response;
352
410
  try {
353
411
  response = await fetchImpl(url, {
@@ -357,23 +415,35 @@ var __webpack_modules__ = {
357
415
  "ChatGPT-Account-Id": extracted.accountId,
358
416
  Accept: "application/json",
359
417
  "User-Agent": USER_AGENT
360
- }
418
+ },
419
+ signal: abortController.signal
361
420
  });
362
421
  } catch (error) {
363
- errors.push(`${url} -> ${normalizeFetchError(error)}`);
364
- continue;
422
+ clearTimeout(timeout);
423
+ const message = abortController.signal.aborted ? `timed out after ${USAGE_FETCH_TIMEOUT_MS}ms` : normalizeFetchError(error);
424
+ errors.push(formatUsageAttemptError(url, attempt, message));
425
+ if (!isLastAttempt(attempt)) {
426
+ await delay(USAGE_FETCH_RETRY_DELAY_MS * attempt);
427
+ continue;
428
+ }
429
+ break;
365
430
  }
431
+ clearTimeout(timeout);
366
432
  if (!response.ok) {
367
433
  const body = await response.text();
368
- errors.push(`${url} -> ${response.status}: ${body.slice(0, 140).replace(/\s+/gu, " ").trim()}`);
369
- continue;
434
+ errors.push(formatUsageAttemptError(url, attempt, `${response.status}: ${body.slice(0, 140).replace(/\s+/gu, " ").trim()}`));
435
+ if (isTransientUsageStatus(response.status) && !isLastAttempt(attempt)) {
436
+ await delay(USAGE_FETCH_RETRY_DELAY_MS * attempt);
437
+ continue;
438
+ }
439
+ break;
370
440
  }
371
441
  let payload;
372
442
  try {
373
443
  payload = await response.json();
374
444
  } catch (error) {
375
- errors.push(`${url} -> failed to parse JSON: ${normalizeFetchError(error)}`);
376
- continue;
445
+ errors.push(formatUsageAttemptError(url, attempt, `failed to parse JSON: ${normalizeFetchError(error)}`));
446
+ break;
377
447
  }
378
448
  return mapUsagePayload(payload, extracted.planType, now);
379
449
  }
@@ -508,6 +578,13 @@ var __webpack_modules__ = {
508
578
  async function readJsonFile(path) {
509
579
  return (0, promises_namespaceObject.readFile)(path, "utf8");
510
580
  }
581
+ function canAutoMigrateLegacyChatGPTMeta(meta, snapshot) {
582
+ if (!isSupportedChatGPTAuthMode(meta.auth_mode) || !isSupportedChatGPTAuthMode(snapshot.auth_mode)) return false;
583
+ if ("string" == typeof meta.user_id && "" !== meta.user_id.trim()) return false;
584
+ const snapshotUserId = getSnapshotUserId(snapshot);
585
+ if (!snapshotUserId) return false;
586
+ return meta.account_id === getSnapshotAccountId(snapshot);
587
+ }
511
588
  async function detectRunningCodexProcesses() {
512
589
  try {
513
590
  const { stdout } = await execFile("ps", [
@@ -605,6 +682,8 @@ var __webpack_modules__ = {
605
682
  return {
606
683
  name: account.name,
607
684
  account_id: account.account_id,
685
+ user_id: account.user_id ?? null,
686
+ identity: account.identity,
608
687
  plan_type: planType,
609
688
  credits_balance: account.quota.credits_balance ?? null,
610
689
  status: account.quota.status,
@@ -645,11 +724,21 @@ var __webpack_modules__ = {
645
724
  readJsonFile(metaPath),
646
725
  readAuthSnapshotFile(authPath)
647
726
  ]);
648
- const meta = parseSnapshotMeta(rawMeta);
727
+ let meta = parseSnapshotMeta(rawMeta);
649
728
  if (meta.name !== name) throw new Error(`Account metadata name mismatch for "${name}".`);
650
- if (meta.account_id !== getSnapshotIdentity(snapshot)) throw new Error(`Account metadata account_id mismatch for "${name}".`);
729
+ const snapshotIdentity = getSnapshotIdentity(snapshot);
730
+ if (getMetaIdentity(meta) !== snapshotIdentity) if (canAutoMigrateLegacyChatGPTMeta(meta, snapshot)) {
731
+ meta = {
732
+ ...meta,
733
+ account_id: getSnapshotAccountId(snapshot),
734
+ user_id: getSnapshotUserId(snapshot)
735
+ };
736
+ await this.writeAccountMeta(name, meta);
737
+ } else throw new Error(`Account metadata account_id mismatch for "${name}".`);
738
+ if (getMetaIdentity(meta) !== snapshotIdentity) throw new Error(`Account metadata account_id mismatch for "${name}".`);
651
739
  return {
652
740
  ...meta,
741
+ identity: getMetaIdentity(meta),
653
742
  authPath,
654
743
  metaPath,
655
744
  configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
@@ -669,12 +758,12 @@ var __webpack_modules__ = {
669
758
  warnings.push(`Account "${entry.name}" is invalid: ${error.message}`);
670
759
  }
671
760
  const counts = new Map();
672
- for (const account of accounts)counts.set(account.account_id, (counts.get(account.account_id) ?? 0) + 1);
761
+ for (const account of accounts)counts.set(account.identity, (counts.get(account.identity) ?? 0) + 1);
673
762
  accounts.sort((left, right)=>left.name.localeCompare(right.name));
674
763
  return {
675
764
  accounts: accounts.map((account)=>({
676
765
  ...account,
677
- duplicateAccountId: (counts.get(account.account_id) ?? 0) > 1
766
+ duplicateAccountId: (counts.get(account.identity) ?? 0) > 1
678
767
  })),
679
768
  warnings
680
769
  };
@@ -685,6 +774,8 @@ var __webpack_modules__ = {
685
774
  exists: false,
686
775
  auth_mode: null,
687
776
  account_id: null,
777
+ user_id: null,
778
+ identity: null,
688
779
  matched_accounts: [],
689
780
  managed: false,
690
781
  duplicate_match: false,
@@ -692,11 +783,15 @@ var __webpack_modules__ = {
692
783
  };
693
784
  const snapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
694
785
  const currentIdentity = getSnapshotIdentity(snapshot);
695
- const matchedAccounts = accounts.filter((account)=>account.account_id === currentIdentity).map((account)=>account.name);
786
+ const currentAccountId = getSnapshotAccountId(snapshot);
787
+ const currentUserId = getSnapshotUserId(snapshot) ?? null;
788
+ const matchedAccounts = accounts.filter((account)=>account.identity === currentIdentity).map((account)=>account.name);
696
789
  return {
697
790
  exists: true,
698
791
  auth_mode: snapshot.auth_mode,
699
- account_id: currentIdentity,
792
+ account_id: currentAccountId,
793
+ user_id: currentUserId,
794
+ identity: currentIdentity,
700
795
  matched_accounts: matchedAccounts,
701
796
  managed: matchedAccounts.length > 0,
702
797
  duplicate_match: matchedAccounts.length > 1,
@@ -719,7 +814,7 @@ var __webpack_modules__ = {
719
814
  const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
720
815
  if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
721
816
  const { accounts } = await this.listAccounts();
722
- const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.account_id === identity);
817
+ const duplicateIdentityAccounts = accounts.filter((account)=>account.name !== name && account.identity === identity);
723
818
  if (duplicateIdentityAccounts.length > 0) {
724
819
  const joinedNames = duplicateIdentityAccounts.map((account)=>`"${account.name}"`).join(", ");
725
820
  throw new Error(`Identity ${identity} is already managed by ${joinedNames}.`);
@@ -794,7 +889,7 @@ var __webpack_modules__ = {
794
889
  await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
795
890
  }
796
891
  const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
797
- if (getSnapshotIdentity(writtenSnapshot) !== account.account_id) throw new Error(`Switch verification failed for account "${name}".`);
892
+ if (getSnapshotIdentity(writtenSnapshot) !== account.identity) throw new Error(`Switch verification failed for account "${name}".`);
798
893
  const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
799
894
  meta.last_switched_at = new Date().toISOString();
800
895
  meta.updated_at = meta.last_switched_at;
@@ -838,7 +933,8 @@ var __webpack_modules__ = {
838
933
  await this.syncCurrentAuthIfMatching(result.authSnapshot);
839
934
  }
840
935
  meta.auth_mode = result.authSnapshot.auth_mode;
841
- meta.account_id = getSnapshotIdentity(result.authSnapshot);
936
+ meta.account_id = getSnapshotAccountId(result.authSnapshot);
937
+ meta.user_id = getSnapshotUserId(result.authSnapshot);
842
938
  meta.updated_at = now.toISOString();
843
939
  meta.quota = result.quota;
844
940
  await this.writeAccountMeta(name, meta);
@@ -951,7 +1047,7 @@ var __webpack_modules__ = {
951
1047
  if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
952
1048
  if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
953
1049
  if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
954
- if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.account_id} with another saved account.`);
1050
+ if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.identity} with another saved account.`);
955
1051
  }
956
1052
  let currentAuthPresent = false;
957
1053
  if (await pathExists(this.paths.currentAuthPath)) {
@@ -1032,7 +1128,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1032
1128
  if (status.exists) {
1033
1129
  lines.push("Current auth: present");
1034
1130
  lines.push(`Auth mode: ${status.auth_mode}`);
1035
- lines.push(`Identity: ${maskAccountId(status.account_id ?? "")}`);
1131
+ lines.push(`Identity: ${maskAccountId(status.identity ?? "")}`);
1036
1132
  if (0 === status.matched_accounts.length) lines.push("Managed account: no (unmanaged)");
1037
1133
  else if (1 === status.matched_accounts.length) lines.push(`Managed account: ${status.matched_accounts[0]}`);
1038
1134
  else lines.push(`Managed account: multiple (${status.matched_accounts.join(", ")})`);
@@ -1085,6 +1181,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1085
1181
  return {
1086
1182
  name: account.name,
1087
1183
  account_id: account.account_id,
1184
+ identity: account.identity,
1088
1185
  plan_type: account.plan_type,
1089
1186
  available: computeAvailability(account),
1090
1187
  refresh_status: "ok",
@@ -1117,7 +1214,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1117
1214
  }
1118
1215
  function describeAutoSwitchSelection(candidate, dryRun, backupPath, warnings) {
1119
1216
  const lines = [
1120
- dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.account_id)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.account_id)}).`,
1217
+ dryRun ? `Best account: "${candidate.name}" (${maskAccountId(candidate.identity)}).` : `Auto-switched to "${candidate.name}" (${maskAccountId(candidate.identity)}).`,
1121
1218
  `Score: ${candidate.effective_score}`,
1122
1219
  `5H remaining: ${candidate.remain_5h}%`,
1123
1220
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1128,7 +1225,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1128
1225
  }
1129
1226
  function describeAutoSwitchNoop(candidate, warnings) {
1130
1227
  const lines = [
1131
- `Current account "${candidate.name}" (${maskAccountId(candidate.account_id)}) is already the best available account.`,
1228
+ `Current account "${candidate.name}" (${maskAccountId(candidate.identity)}) is already the best available account.`,
1132
1229
  `Score: ${candidate.effective_score}`,
1133
1230
  `5H remaining: ${candidate.remain_5h}%`,
1134
1231
  `1W remaining (5H-equivalent): ${candidate.remain_1w_eq_5h}%`
@@ -1140,7 +1237,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1140
1237
  if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
1141
1238
  const table = formatTable(accounts.map((account)=>({
1142
1239
  name: account.name,
1143
- account_id: maskAccountId(account.account_id),
1240
+ account_id: maskAccountId(account.identity),
1144
1241
  plan_type: account.plan_type ?? "-",
1145
1242
  available: computeAvailability(account) ?? "-",
1146
1243
  five_hour: formatUsagePercent(account.five_hour),
@@ -1265,11 +1362,13 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1265
1362
  account: {
1266
1363
  name: account.name,
1267
1364
  account_id: account.account_id,
1365
+ user_id: account.user_id ?? null,
1366
+ identity: account.identity,
1268
1367
  auth_mode: account.auth_mode
1269
1368
  }
1270
1369
  };
1271
1370
  if (json) writeJson(streams.stdout, payload);
1272
- else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.account_id)}).\n`);
1371
+ else streams.stdout.write(`Saved account "${account.name}" (${maskAccountId(account.identity)}).\n`);
1273
1372
  return 0;
1274
1373
  }
1275
1374
  case "update":
@@ -1291,6 +1390,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1291
1390
  account: {
1292
1391
  name: result.account.name,
1293
1392
  account_id: result.account.account_id,
1393
+ user_id: result.account.user_id ?? null,
1394
+ identity: result.account.identity,
1294
1395
  auth_mode: result.account.auth_mode
1295
1396
  },
1296
1397
  quota,
@@ -1298,7 +1399,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1298
1399
  };
1299
1400
  if (json) writeJson(streams.stdout, payload);
1300
1401
  else {
1301
- streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1402
+ streams.stdout.write(`Updated managed account "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1302
1403
  for (const warning of warnings)streams.stdout.write(`Warning: ${warning}\n`);
1303
1404
  }
1304
1405
  return 0;
@@ -1341,7 +1442,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1341
1442
  reason: "already_current_best",
1342
1443
  account: {
1343
1444
  name: selected.name,
1344
- account_id: selected.account_id
1445
+ account_id: selected.account_id,
1446
+ identity: selected.identity
1345
1447
  },
1346
1448
  selected,
1347
1449
  candidates,
@@ -1361,6 +1463,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1361
1463
  account: {
1362
1464
  name: result.account.name,
1363
1465
  account_id: result.account.account_id,
1466
+ user_id: result.account.user_id ?? null,
1467
+ identity: result.account.identity,
1364
1468
  auth_mode: result.account.auth_mode
1365
1469
  },
1366
1470
  selected,
@@ -1390,6 +1494,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1390
1494
  account: {
1391
1495
  name: result.account.name,
1392
1496
  account_id: result.account.account_id,
1497
+ user_id: result.account.user_id ?? null,
1498
+ identity: result.account.identity,
1393
1499
  auth_mode: result.account.auth_mode
1394
1500
  },
1395
1501
  quota,
@@ -1398,7 +1504,7 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1398
1504
  };
1399
1505
  if (json) writeJson(streams.stdout, payload);
1400
1506
  else {
1401
- streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.account_id)}).\n`);
1507
+ streams.stdout.write(`Switched to "${result.account.name}" (${maskAccountId(result.account.identity)}).\n`);
1402
1508
  if (result.backup_path) streams.stdout.write(`Backup: ${result.backup_path}\n`);
1403
1509
  for (const warning of result.warnings)streams.stdout.write(`Warning: ${warning}\n`);
1404
1510
  }
@@ -1440,6 +1546,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1440
1546
  account: {
1441
1547
  name: account.name,
1442
1548
  account_id: account.account_id,
1549
+ user_id: account.user_id ?? null,
1550
+ identity: account.identity,
1443
1551
  auth_mode: account.auth_mode
1444
1552
  }
1445
1553
  });