codex-team 0.0.1 → 0.0.2

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
@@ -16,14 +16,13 @@ After install, use the `codexm` command.
16
16
 
17
17
  ```bash
18
18
  codexm current
19
- codexm list
19
+ codexm list [name]
20
20
  codexm save <name>
21
21
  codexm update
22
22
  codexm switch <name>
23
23
  codexm remove <name> --yes
24
24
  codexm rename <old> <new>
25
25
  codexm quota refresh [name]
26
- codexm quota list
27
26
  codexm doctor
28
27
  ```
29
28
 
@@ -35,7 +34,7 @@ Use `--json` on query and mutation commands when you need machine-readable outpu
35
34
  2. Save the current auth snapshot with `codexm save <name>`.
36
35
  3. Repeat for other accounts.
37
36
  4. Switch between saved accounts with `codexm switch <name>`.
38
- 5. Refresh and inspect quota usage with `codexm quota refresh` and `codexm quota list`.
37
+ 5. Refresh and inspect quota usage with `codexm list` or `codexm quota refresh`.
39
38
 
40
39
  ## Development
41
40
 
package/dist/cli.cjs CHANGED
@@ -409,6 +409,7 @@ var __webpack_modules__ = {
409
409
  const FILE_MODE = 384;
410
410
  const SCHEMA_VERSION = 1;
411
411
  const ACCOUNT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
412
+ const QUOTA_REFRESH_CONCURRENCY = 3;
412
413
  function defaultPaths(homeDir = (0, external_node_os_namespaceObject.homedir)()) {
413
414
  const codexDir = (0, external_node_path_namespaceObject.join)(homeDir, ".codex");
414
415
  const codexTeamDir = (0, external_node_path_namespaceObject.join)(homeDir, ".codex-team");
@@ -759,17 +760,36 @@ var __webpack_modules__ = {
759
760
  const { accounts } = await this.listAccounts();
760
761
  const targets = targetName ? accounts.filter((account)=>account.name === targetName) : accounts;
761
762
  if (targetName && 0 === targets.length) throw new Error(`Account "${targetName}" does not exist.`);
763
+ const results = new Array(targets.length);
764
+ let nextIndex = 0;
765
+ const workerCount = Math.min(QUOTA_REFRESH_CONCURRENCY, targets.length);
766
+ await Promise.all(Array.from({
767
+ length: workerCount
768
+ }, async ()=>{
769
+ while(true){
770
+ const index = nextIndex;
771
+ nextIndex += 1;
772
+ if (index >= targets.length) return;
773
+ const account = targets[index];
774
+ try {
775
+ const refreshed = await this.refreshQuotaForAccount(account.name);
776
+ results[index] = {
777
+ success: await this.quotaSummaryForAccount(refreshed.account)
778
+ };
779
+ } catch (error) {
780
+ results[index] = {
781
+ failure: {
782
+ name: account.name,
783
+ error: error.message
784
+ }
785
+ };
786
+ }
787
+ }
788
+ }));
762
789
  const successes = [];
763
790
  const failures = [];
764
- for (const account of targets)try {
765
- const refreshed = await this.refreshQuotaForAccount(account.name);
766
- successes.push(await this.quotaSummaryForAccount(refreshed.account));
767
- } catch (error) {
768
- failures.push({
769
- name: account.name,
770
- error: error.message
771
- });
772
- }
791
+ for (const result of results)if (result) if ("success" in result) successes.push(result.success);
792
+ else failures.push(result.failure);
773
793
  return {
774
794
  successes,
775
795
  failures
@@ -885,11 +905,10 @@ var __webpack_modules__ = {
885
905
 
886
906
  Usage:
887
907
  codexm current [--json]
888
- codexm list [--json]
908
+ codexm list [name] [--json]
889
909
  codexm save <name> [--force] [--json]
890
910
  codexm update [--json]
891
911
  codexm quota refresh [name] [--json]
892
- codexm quota list [--json]
893
912
  codexm switch <name> [--json]
894
913
  codexm remove <name> [--yes] [--json]
895
914
  codexm rename <old> <new> [--json]
@@ -911,47 +930,6 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
911
930
  for (const warning of status.warnings)lines.push(`Warning: ${warning}`);
912
931
  return lines.join("\n");
913
932
  }
914
- function describeAccounts(accounts, warnings) {
915
- if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
916
- const table = formatTable(accounts.map((account)=>({
917
- name: account.name,
918
- account_id: maskAccountId(account.account_id),
919
- auth_mode: account.auth_mode,
920
- saved: account.created_at,
921
- switched: account.last_switched_at ?? "-",
922
- flags: account.duplicateAccountId ? "duplicate-account-id" : "-"
923
- })), [
924
- {
925
- key: "name",
926
- label: "NAME"
927
- },
928
- {
929
- key: "account_id",
930
- label: "ACCOUNT ID"
931
- },
932
- {
933
- key: "auth_mode",
934
- label: "AUTH MODE"
935
- },
936
- {
937
- key: "saved",
938
- label: "SAVED AT"
939
- },
940
- {
941
- key: "switched",
942
- label: "LAST SWITCHED"
943
- },
944
- {
945
- key: "flags",
946
- label: "FLAGS"
947
- }
948
- ]);
949
- const lines = [
950
- table
951
- ];
952
- for (const warning of warnings)lines.push(`Warning: ${warning}`);
953
- return lines.join("\n");
954
- }
955
933
  function describeDoctor(report) {
956
934
  const lines = [
957
935
  report.healthy ? "Doctor checks passed." : "Doctor checks found issues.",
@@ -970,17 +948,43 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
970
948
  if (!window?.reset_at) return "-";
971
949
  return external_dayjs_default().utc(window.reset_at).tz(external_dayjs_default().tz.guess()).format("MM-DD HH:mm");
972
950
  }
951
+ function computeAvailability(account) {
952
+ if ("ok" !== account.status) return null;
953
+ const usedPercents = [
954
+ account.five_hour?.used_percent,
955
+ account.one_week?.used_percent
956
+ ].filter((value)=>"number" == typeof value);
957
+ if (0 === usedPercents.length) return null;
958
+ if (usedPercents.some((value)=>value >= 100)) return "unavailable";
959
+ if (usedPercents.some((value)=>100 - value < 10)) return "almost unavailable";
960
+ return "available";
961
+ }
962
+ function toCliQuotaSummary(account) {
963
+ const { status, ...rest } = account;
964
+ return {
965
+ ...rest,
966
+ available: computeAvailability(account),
967
+ refresh_status: status
968
+ };
969
+ }
970
+ function toCliQuotaRefreshResult(result) {
971
+ return {
972
+ successes: result.successes.map(toCliQuotaSummary),
973
+ failures: result.failures
974
+ };
975
+ }
973
976
  function describeQuotaAccounts(accounts, warnings) {
974
977
  if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
975
978
  const table = formatTable(accounts.map((account)=>({
976
979
  name: account.name,
977
980
  account_id: maskAccountId(account.account_id),
978
981
  plan_type: account.plan_type ?? "-",
982
+ available: computeAvailability(account) ?? "-",
979
983
  five_hour: formatUsagePercent(account.five_hour),
980
984
  five_hour_reset: formatResetAt(account.five_hour),
981
985
  one_week: formatUsagePercent(account.one_week),
982
986
  one_week_reset: formatResetAt(account.one_week),
983
- status: account.status
987
+ refresh_status: account.status
984
988
  })), [
985
989
  {
986
990
  key: "name",
@@ -994,6 +998,10 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
994
998
  key: "plan_type",
995
999
  label: "PLAN TYPE"
996
1000
  },
1001
+ {
1002
+ key: "available",
1003
+ label: "AVAILABLE"
1004
+ },
997
1005
  {
998
1006
  key: "five_hour",
999
1007
  label: "5H USED"
@@ -1011,8 +1019,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1011
1019
  label: "1W RESET AT"
1012
1020
  },
1013
1021
  {
1014
- key: "status",
1015
- label: "STATUS"
1022
+ key: "refresh_status",
1023
+ label: "REFRESH STATUS"
1016
1024
  }
1017
1025
  ]);
1018
1026
  const lines = [
@@ -1068,10 +1076,11 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1068
1076
  }
1069
1077
  case "list":
1070
1078
  {
1071
- const result = await store.listAccounts();
1072
- if (json) writeJson(streams.stdout, result);
1073
- else streams.stdout.write(`${describeAccounts(result.accounts, result.warnings)}\n`);
1074
- return 0;
1079
+ const targetName = parsed.positionals[0];
1080
+ const result = await store.refreshAllQuotas(targetName);
1081
+ if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
1082
+ else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
1083
+ return 0 === result.failures.length ? 0 : 1;
1075
1084
  }
1076
1085
  case "save":
1077
1086
  {
@@ -1099,7 +1108,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1099
1108
  try {
1100
1109
  const quotaResult = await store.refreshQuotaForAccount(result.account.name);
1101
1110
  const quotaList = await store.listQuotaSummaries();
1102
- quota = quotaList.accounts.find((account)=>account.name === quotaResult.account.name) ?? null;
1111
+ const matched = quotaList.accounts.find((account)=>account.name === quotaResult.account.name) ?? null;
1112
+ quota = matched ? toCliQuotaSummary(matched) : null;
1103
1113
  } catch (error) {
1104
1114
  warnings.push(error.message);
1105
1115
  }
@@ -1124,20 +1134,14 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1124
1134
  case "quota":
1125
1135
  {
1126
1136
  const quotaCommand = parsed.positionals[0];
1127
- if ("list" === quotaCommand) {
1128
- const result = await store.listQuotaSummaries();
1129
- if (json) writeJson(streams.stdout, result);
1130
- else streams.stdout.write(`${describeQuotaAccounts(result.accounts, result.warnings)}\n`);
1131
- return 0;
1132
- }
1133
1137
  if ("refresh" === quotaCommand) {
1134
1138
  const targetName = parsed.positionals[1];
1135
1139
  const result = await store.refreshAllQuotas(targetName);
1136
- if (json) writeJson(streams.stdout, result);
1140
+ if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
1137
1141
  else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
1138
1142
  return 0 === result.failures.length ? 0 : 1;
1139
1143
  }
1140
- throw new Error("Usage: codexm quota <refresh [name] | list> [--json]");
1144
+ throw new Error("Usage: codexm quota refresh [name] [--json]");
1141
1145
  }
1142
1146
  case "switch":
1143
1147
  {
@@ -1148,7 +1152,8 @@ Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1148
1152
  try {
1149
1153
  await store.refreshQuotaForAccount(result.account.name);
1150
1154
  const quotaList = await store.listQuotaSummaries();
1151
- quota = quotaList.accounts.find((account)=>account.name === result.account.name) ?? null;
1155
+ const matched = quotaList.accounts.find((account)=>account.name === result.account.name) ?? null;
1156
+ quota = matched ? toCliQuotaSummary(matched) : null;
1152
1157
  } catch (error) {
1153
1158
  result.warnings.push(error.message);
1154
1159
  }
package/dist/main.cjs CHANGED
@@ -439,6 +439,7 @@ const DIRECTORY_MODE = 448;
439
439
  const FILE_MODE = 384;
440
440
  const SCHEMA_VERSION = 1;
441
441
  const ACCOUNT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
442
+ const QUOTA_REFRESH_CONCURRENCY = 3;
442
443
  function defaultPaths(homeDir = (0, external_node_os_namespaceObject.homedir)()) {
443
444
  const codexDir = (0, external_node_path_namespaceObject.join)(homeDir, ".codex");
444
445
  const codexTeamDir = (0, external_node_path_namespaceObject.join)(homeDir, ".codex-team");
@@ -789,17 +790,36 @@ class AccountStore {
789
790
  const { accounts } = await this.listAccounts();
790
791
  const targets = targetName ? accounts.filter((account)=>account.name === targetName) : accounts;
791
792
  if (targetName && 0 === targets.length) throw new Error(`Account "${targetName}" does not exist.`);
793
+ const results = new Array(targets.length);
794
+ let nextIndex = 0;
795
+ const workerCount = Math.min(QUOTA_REFRESH_CONCURRENCY, targets.length);
796
+ await Promise.all(Array.from({
797
+ length: workerCount
798
+ }, async ()=>{
799
+ while(true){
800
+ const index = nextIndex;
801
+ nextIndex += 1;
802
+ if (index >= targets.length) return;
803
+ const account = targets[index];
804
+ try {
805
+ const refreshed = await this.refreshQuotaForAccount(account.name);
806
+ results[index] = {
807
+ success: await this.quotaSummaryForAccount(refreshed.account)
808
+ };
809
+ } catch (error) {
810
+ results[index] = {
811
+ failure: {
812
+ name: account.name,
813
+ error: error.message
814
+ }
815
+ };
816
+ }
817
+ }
818
+ }));
792
819
  const successes = [];
793
820
  const failures = [];
794
- for (const account of targets)try {
795
- const refreshed = await this.refreshQuotaForAccount(account.name);
796
- successes.push(await this.quotaSummaryForAccount(refreshed.account));
797
- } catch (error) {
798
- failures.push({
799
- name: account.name,
800
- error: error.message
801
- });
802
- }
821
+ for (const result of results)if (result) if ("success" in result) successes.push(result.success);
822
+ else failures.push(result.failure);
803
823
  return {
804
824
  successes,
805
825
  failures
@@ -915,11 +935,10 @@ function printHelp(stream) {
915
935
 
916
936
  Usage:
917
937
  codexm current [--json]
918
- codexm list [--json]
938
+ codexm list [name] [--json]
919
939
  codexm save <name> [--force] [--json]
920
940
  codexm update [--json]
921
941
  codexm quota refresh [name] [--json]
922
- codexm quota list [--json]
923
942
  codexm switch <name> [--json]
924
943
  codexm remove <name> [--yes] [--json]
925
944
  codexm rename <old> <new> [--json]
@@ -941,47 +960,6 @@ function describeCurrentStatus(status) {
941
960
  for (const warning of status.warnings)lines.push(`Warning: ${warning}`);
942
961
  return lines.join("\n");
943
962
  }
944
- function describeAccounts(accounts, warnings) {
945
- if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
946
- const table = formatTable(accounts.map((account)=>({
947
- name: account.name,
948
- account_id: maskAccountId(account.account_id),
949
- auth_mode: account.auth_mode,
950
- saved: account.created_at,
951
- switched: account.last_switched_at ?? "-",
952
- flags: account.duplicateAccountId ? "duplicate-account-id" : "-"
953
- })), [
954
- {
955
- key: "name",
956
- label: "NAME"
957
- },
958
- {
959
- key: "account_id",
960
- label: "ACCOUNT ID"
961
- },
962
- {
963
- key: "auth_mode",
964
- label: "AUTH MODE"
965
- },
966
- {
967
- key: "saved",
968
- label: "SAVED AT"
969
- },
970
- {
971
- key: "switched",
972
- label: "LAST SWITCHED"
973
- },
974
- {
975
- key: "flags",
976
- label: "FLAGS"
977
- }
978
- ]);
979
- const lines = [
980
- table
981
- ];
982
- for (const warning of warnings)lines.push(`Warning: ${warning}`);
983
- return lines.join("\n");
984
- }
985
963
  function describeDoctor(report) {
986
964
  const lines = [
987
965
  report.healthy ? "Doctor checks passed." : "Doctor checks found issues.",
@@ -1000,17 +978,43 @@ function formatResetAt(window) {
1000
978
  if (!window?.reset_at) return "-";
1001
979
  return external_dayjs_default().utc(window.reset_at).tz(external_dayjs_default().tz.guess()).format("MM-DD HH:mm");
1002
980
  }
981
+ function computeAvailability(account) {
982
+ if ("ok" !== account.status) return null;
983
+ const usedPercents = [
984
+ account.five_hour?.used_percent,
985
+ account.one_week?.used_percent
986
+ ].filter((value)=>"number" == typeof value);
987
+ if (0 === usedPercents.length) return null;
988
+ if (usedPercents.some((value)=>value >= 100)) return "unavailable";
989
+ if (usedPercents.some((value)=>100 - value < 10)) return "almost unavailable";
990
+ return "available";
991
+ }
992
+ function toCliQuotaSummary(account) {
993
+ const { status, ...rest } = account;
994
+ return {
995
+ ...rest,
996
+ available: computeAvailability(account),
997
+ refresh_status: status
998
+ };
999
+ }
1000
+ function toCliQuotaRefreshResult(result) {
1001
+ return {
1002
+ successes: result.successes.map(toCliQuotaSummary),
1003
+ failures: result.failures
1004
+ };
1005
+ }
1003
1006
  function describeQuotaAccounts(accounts, warnings) {
1004
1007
  if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
1005
1008
  const table = formatTable(accounts.map((account)=>({
1006
1009
  name: account.name,
1007
1010
  account_id: maskAccountId(account.account_id),
1008
1011
  plan_type: account.plan_type ?? "-",
1012
+ available: computeAvailability(account) ?? "-",
1009
1013
  five_hour: formatUsagePercent(account.five_hour),
1010
1014
  five_hour_reset: formatResetAt(account.five_hour),
1011
1015
  one_week: formatUsagePercent(account.one_week),
1012
1016
  one_week_reset: formatResetAt(account.one_week),
1013
- status: account.status
1017
+ refresh_status: account.status
1014
1018
  })), [
1015
1019
  {
1016
1020
  key: "name",
@@ -1024,6 +1028,10 @@ function describeQuotaAccounts(accounts, warnings) {
1024
1028
  key: "plan_type",
1025
1029
  label: "PLAN TYPE"
1026
1030
  },
1031
+ {
1032
+ key: "available",
1033
+ label: "AVAILABLE"
1034
+ },
1027
1035
  {
1028
1036
  key: "five_hour",
1029
1037
  label: "5H USED"
@@ -1041,8 +1049,8 @@ function describeQuotaAccounts(accounts, warnings) {
1041
1049
  label: "1W RESET AT"
1042
1050
  },
1043
1051
  {
1044
- key: "status",
1045
- label: "STATUS"
1052
+ key: "refresh_status",
1053
+ label: "REFRESH STATUS"
1046
1054
  }
1047
1055
  ]);
1048
1056
  const lines = [
@@ -1098,10 +1106,11 @@ async function runCli(argv, options = {}) {
1098
1106
  }
1099
1107
  case "list":
1100
1108
  {
1101
- const result = await store.listAccounts();
1102
- if (json) writeJson(streams.stdout, result);
1103
- else streams.stdout.write(`${describeAccounts(result.accounts, result.warnings)}\n`);
1104
- return 0;
1109
+ const targetName = parsed.positionals[0];
1110
+ const result = await store.refreshAllQuotas(targetName);
1111
+ if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
1112
+ else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
1113
+ return 0 === result.failures.length ? 0 : 1;
1105
1114
  }
1106
1115
  case "save":
1107
1116
  {
@@ -1129,7 +1138,8 @@ async function runCli(argv, options = {}) {
1129
1138
  try {
1130
1139
  const quotaResult = await store.refreshQuotaForAccount(result.account.name);
1131
1140
  const quotaList = await store.listQuotaSummaries();
1132
- quota = quotaList.accounts.find((account)=>account.name === quotaResult.account.name) ?? null;
1141
+ const matched = quotaList.accounts.find((account)=>account.name === quotaResult.account.name) ?? null;
1142
+ quota = matched ? toCliQuotaSummary(matched) : null;
1133
1143
  } catch (error) {
1134
1144
  warnings.push(error.message);
1135
1145
  }
@@ -1154,20 +1164,14 @@ async function runCli(argv, options = {}) {
1154
1164
  case "quota":
1155
1165
  {
1156
1166
  const quotaCommand = parsed.positionals[0];
1157
- if ("list" === quotaCommand) {
1158
- const result = await store.listQuotaSummaries();
1159
- if (json) writeJson(streams.stdout, result);
1160
- else streams.stdout.write(`${describeQuotaAccounts(result.accounts, result.warnings)}\n`);
1161
- return 0;
1162
- }
1163
1167
  if ("refresh" === quotaCommand) {
1164
1168
  const targetName = parsed.positionals[1];
1165
1169
  const result = await store.refreshAllQuotas(targetName);
1166
- if (json) writeJson(streams.stdout, result);
1170
+ if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
1167
1171
  else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
1168
1172
  return 0 === result.failures.length ? 0 : 1;
1169
1173
  }
1170
- throw new Error("Usage: codexm quota <refresh [name] | list> [--json]");
1174
+ throw new Error("Usage: codexm quota refresh [name] [--json]");
1171
1175
  }
1172
1176
  case "switch":
1173
1177
  {
@@ -1178,7 +1182,8 @@ async function runCli(argv, options = {}) {
1178
1182
  try {
1179
1183
  await store.refreshQuotaForAccount(result.account.name);
1180
1184
  const quotaList = await store.listQuotaSummaries();
1181
- quota = quotaList.accounts.find((account)=>account.name === result.account.name) ?? null;
1185
+ const matched = quotaList.accounts.find((account)=>account.name === result.account.name) ?? null;
1186
+ quota = matched ? toCliQuotaSummary(matched) : null;
1182
1187
  } catch (error) {
1183
1188
  result.warnings.push(error.message);
1184
1189
  }
package/dist/main.js CHANGED
@@ -399,6 +399,7 @@ const DIRECTORY_MODE = 448;
399
399
  const FILE_MODE = 384;
400
400
  const SCHEMA_VERSION = 1;
401
401
  const ACCOUNT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
402
+ const QUOTA_REFRESH_CONCURRENCY = 3;
402
403
  function defaultPaths(homeDir = homedir()) {
403
404
  const codexDir = join(homeDir, ".codex");
404
405
  const codexTeamDir = join(homeDir, ".codex-team");
@@ -749,17 +750,36 @@ class AccountStore {
749
750
  const { accounts } = await this.listAccounts();
750
751
  const targets = targetName ? accounts.filter((account)=>account.name === targetName) : accounts;
751
752
  if (targetName && 0 === targets.length) throw new Error(`Account "${targetName}" does not exist.`);
753
+ const results = new Array(targets.length);
754
+ let nextIndex = 0;
755
+ const workerCount = Math.min(QUOTA_REFRESH_CONCURRENCY, targets.length);
756
+ await Promise.all(Array.from({
757
+ length: workerCount
758
+ }, async ()=>{
759
+ while(true){
760
+ const index = nextIndex;
761
+ nextIndex += 1;
762
+ if (index >= targets.length) return;
763
+ const account = targets[index];
764
+ try {
765
+ const refreshed = await this.refreshQuotaForAccount(account.name);
766
+ results[index] = {
767
+ success: await this.quotaSummaryForAccount(refreshed.account)
768
+ };
769
+ } catch (error) {
770
+ results[index] = {
771
+ failure: {
772
+ name: account.name,
773
+ error: error.message
774
+ }
775
+ };
776
+ }
777
+ }
778
+ }));
752
779
  const successes = [];
753
780
  const failures = [];
754
- for (const account of targets)try {
755
- const refreshed = await this.refreshQuotaForAccount(account.name);
756
- successes.push(await this.quotaSummaryForAccount(refreshed.account));
757
- } catch (error) {
758
- failures.push({
759
- name: account.name,
760
- error: error.message
761
- });
762
- }
781
+ for (const result of results)if (result) if ("success" in result) successes.push(result.success);
782
+ else failures.push(result.failure);
763
783
  return {
764
784
  successes,
765
785
  failures
@@ -875,11 +895,10 @@ function printHelp(stream) {
875
895
 
876
896
  Usage:
877
897
  codexm current [--json]
878
- codexm list [--json]
898
+ codexm list [name] [--json]
879
899
  codexm save <name> [--force] [--json]
880
900
  codexm update [--json]
881
901
  codexm quota refresh [name] [--json]
882
- codexm quota list [--json]
883
902
  codexm switch <name> [--json]
884
903
  codexm remove <name> [--yes] [--json]
885
904
  codexm rename <old> <new> [--json]
@@ -901,47 +920,6 @@ function describeCurrentStatus(status) {
901
920
  for (const warning of status.warnings)lines.push(`Warning: ${warning}`);
902
921
  return lines.join("\n");
903
922
  }
904
- function describeAccounts(accounts, warnings) {
905
- if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
906
- const table = formatTable(accounts.map((account)=>({
907
- name: account.name,
908
- account_id: maskAccountId(account.account_id),
909
- auth_mode: account.auth_mode,
910
- saved: account.created_at,
911
- switched: account.last_switched_at ?? "-",
912
- flags: account.duplicateAccountId ? "duplicate-account-id" : "-"
913
- })), [
914
- {
915
- key: "name",
916
- label: "NAME"
917
- },
918
- {
919
- key: "account_id",
920
- label: "ACCOUNT ID"
921
- },
922
- {
923
- key: "auth_mode",
924
- label: "AUTH MODE"
925
- },
926
- {
927
- key: "saved",
928
- label: "SAVED AT"
929
- },
930
- {
931
- key: "switched",
932
- label: "LAST SWITCHED"
933
- },
934
- {
935
- key: "flags",
936
- label: "FLAGS"
937
- }
938
- ]);
939
- const lines = [
940
- table
941
- ];
942
- for (const warning of warnings)lines.push(`Warning: ${warning}`);
943
- return lines.join("\n");
944
- }
945
923
  function describeDoctor(report) {
946
924
  const lines = [
947
925
  report.healthy ? "Doctor checks passed." : "Doctor checks found issues.",
@@ -960,17 +938,43 @@ function formatResetAt(window) {
960
938
  if (!window?.reset_at) return "-";
961
939
  return dayjs.utc(window.reset_at).tz(dayjs.tz.guess()).format("MM-DD HH:mm");
962
940
  }
941
+ function computeAvailability(account) {
942
+ if ("ok" !== account.status) return null;
943
+ const usedPercents = [
944
+ account.five_hour?.used_percent,
945
+ account.one_week?.used_percent
946
+ ].filter((value)=>"number" == typeof value);
947
+ if (0 === usedPercents.length) return null;
948
+ if (usedPercents.some((value)=>value >= 100)) return "unavailable";
949
+ if (usedPercents.some((value)=>100 - value < 10)) return "almost unavailable";
950
+ return "available";
951
+ }
952
+ function toCliQuotaSummary(account) {
953
+ const { status, ...rest } = account;
954
+ return {
955
+ ...rest,
956
+ available: computeAvailability(account),
957
+ refresh_status: status
958
+ };
959
+ }
960
+ function toCliQuotaRefreshResult(result) {
961
+ return {
962
+ successes: result.successes.map(toCliQuotaSummary),
963
+ failures: result.failures
964
+ };
965
+ }
963
966
  function describeQuotaAccounts(accounts, warnings) {
964
967
  if (0 === accounts.length) return 0 === warnings.length ? "No saved accounts." : warnings.map((warning)=>`Warning: ${warning}`).join("\n");
965
968
  const table = formatTable(accounts.map((account)=>({
966
969
  name: account.name,
967
970
  account_id: maskAccountId(account.account_id),
968
971
  plan_type: account.plan_type ?? "-",
972
+ available: computeAvailability(account) ?? "-",
969
973
  five_hour: formatUsagePercent(account.five_hour),
970
974
  five_hour_reset: formatResetAt(account.five_hour),
971
975
  one_week: formatUsagePercent(account.one_week),
972
976
  one_week_reset: formatResetAt(account.one_week),
973
- status: account.status
977
+ refresh_status: account.status
974
978
  })), [
975
979
  {
976
980
  key: "name",
@@ -984,6 +988,10 @@ function describeQuotaAccounts(accounts, warnings) {
984
988
  key: "plan_type",
985
989
  label: "PLAN TYPE"
986
990
  },
991
+ {
992
+ key: "available",
993
+ label: "AVAILABLE"
994
+ },
987
995
  {
988
996
  key: "five_hour",
989
997
  label: "5H USED"
@@ -1001,8 +1009,8 @@ function describeQuotaAccounts(accounts, warnings) {
1001
1009
  label: "1W RESET AT"
1002
1010
  },
1003
1011
  {
1004
- key: "status",
1005
- label: "STATUS"
1012
+ key: "refresh_status",
1013
+ label: "REFRESH STATUS"
1006
1014
  }
1007
1015
  ]);
1008
1016
  const lines = [
@@ -1058,10 +1066,11 @@ async function runCli(argv, options = {}) {
1058
1066
  }
1059
1067
  case "list":
1060
1068
  {
1061
- const result = await store.listAccounts();
1062
- if (json) writeJson(streams.stdout, result);
1063
- else streams.stdout.write(`${describeAccounts(result.accounts, result.warnings)}\n`);
1064
- return 0;
1069
+ const targetName = parsed.positionals[0];
1070
+ const result = await store.refreshAllQuotas(targetName);
1071
+ if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
1072
+ else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
1073
+ return 0 === result.failures.length ? 0 : 1;
1065
1074
  }
1066
1075
  case "save":
1067
1076
  {
@@ -1089,7 +1098,8 @@ async function runCli(argv, options = {}) {
1089
1098
  try {
1090
1099
  const quotaResult = await store.refreshQuotaForAccount(result.account.name);
1091
1100
  const quotaList = await store.listQuotaSummaries();
1092
- quota = quotaList.accounts.find((account)=>account.name === quotaResult.account.name) ?? null;
1101
+ const matched = quotaList.accounts.find((account)=>account.name === quotaResult.account.name) ?? null;
1102
+ quota = matched ? toCliQuotaSummary(matched) : null;
1093
1103
  } catch (error) {
1094
1104
  warnings.push(error.message);
1095
1105
  }
@@ -1114,20 +1124,14 @@ async function runCli(argv, options = {}) {
1114
1124
  case "quota":
1115
1125
  {
1116
1126
  const quotaCommand = parsed.positionals[0];
1117
- if ("list" === quotaCommand) {
1118
- const result = await store.listQuotaSummaries();
1119
- if (json) writeJson(streams.stdout, result);
1120
- else streams.stdout.write(`${describeQuotaAccounts(result.accounts, result.warnings)}\n`);
1121
- return 0;
1122
- }
1123
1127
  if ("refresh" === quotaCommand) {
1124
1128
  const targetName = parsed.positionals[1];
1125
1129
  const result = await store.refreshAllQuotas(targetName);
1126
- if (json) writeJson(streams.stdout, result);
1130
+ if (json) writeJson(streams.stdout, toCliQuotaRefreshResult(result));
1127
1131
  else streams.stdout.write(`${describeQuotaRefresh(result)}\n`);
1128
1132
  return 0 === result.failures.length ? 0 : 1;
1129
1133
  }
1130
- throw new Error("Usage: codexm quota <refresh [name] | list> [--json]");
1134
+ throw new Error("Usage: codexm quota refresh [name] [--json]");
1131
1135
  }
1132
1136
  case "switch":
1133
1137
  {
@@ -1138,7 +1142,8 @@ async function runCli(argv, options = {}) {
1138
1142
  try {
1139
1143
  await store.refreshQuotaForAccount(result.account.name);
1140
1144
  const quotaList = await store.listQuotaSummaries();
1141
- quota = quotaList.accounts.find((account)=>account.name === result.account.name) ?? null;
1145
+ const matched = quotaList.accounts.find((account)=>account.name === result.account.name) ?? null;
1146
+ quota = matched ? toCliQuotaSummary(matched) : null;
1142
1147
  } catch (error) {
1143
1148
  result.warnings.push(error.message);
1144
1149
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-team",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Manage multiple Codex ChatGPT auth snapshots and quota usage from the command line.",
5
5
  "license": "MIT",
6
6
  "type": "module",