codex-multi-auth 2.6.1 → 2.7.1

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.
Files changed (57) hide show
  1. package/.codex-plugin/plugin.json +9 -9
  2. package/README.md +16 -8
  3. package/dist/lib/cli.d.ts +2 -0
  4. package/dist/lib/cli.d.ts.map +1 -1
  5. package/dist/lib/codex-manager/formatters/quota-formatters.d.ts +20 -3
  6. package/dist/lib/codex-manager/formatters/quota-formatters.d.ts.map +1 -1
  7. package/dist/lib/codex-manager/formatters/quota-formatters.js +28 -13
  8. package/dist/lib/codex-manager/help.d.ts.map +1 -1
  9. package/dist/lib/codex-manager/help.js +18 -14
  10. package/dist/lib/codex-manager/login-menu-actions.d.ts.map +1 -1
  11. package/dist/lib/codex-manager/login-menu-actions.js +12 -1
  12. package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
  13. package/dist/lib/codex-manager/login-menu-data.js +19 -8
  14. package/dist/lib/codex-manager.d.ts.map +1 -1
  15. package/dist/lib/codex-manager.js +13 -0
  16. package/dist/lib/forecast.d.ts.map +1 -1
  17. package/dist/lib/forecast.js +30 -4
  18. package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
  19. package/dist/lib/policy/runtime-policy.js +12 -3
  20. package/dist/lib/quota-probe.d.ts +13 -0
  21. package/dist/lib/quota-probe.d.ts.map +1 -1
  22. package/dist/lib/quota-probe.js +6 -4
  23. package/dist/lib/quota-readiness.d.ts +1 -0
  24. package/dist/lib/quota-readiness.d.ts.map +1 -1
  25. package/dist/lib/quota-readiness.js +12 -2
  26. package/dist/lib/refresh-lease.d.ts +12 -0
  27. package/dist/lib/refresh-lease.d.ts.map +1 -1
  28. package/dist/lib/refresh-lease.js +24 -2
  29. package/dist/lib/refresh-queue.d.ts.map +1 -1
  30. package/dist/lib/refresh-queue.js +29 -3
  31. package/dist/lib/request/request-transformer.d.ts.map +1 -1
  32. package/dist/lib/request/request-transformer.js +13 -2
  33. package/dist/lib/request/response-handler.d.ts.map +1 -1
  34. package/dist/lib/request/response-handler.js +10 -1
  35. package/dist/lib/rotation.d.ts.map +1 -1
  36. package/dist/lib/rotation.js +8 -1
  37. package/dist/lib/runtime/account-check.d.ts +1 -1
  38. package/dist/lib/runtime/account-check.d.ts.map +1 -1
  39. package/dist/lib/runtime/account-check.js +8 -0
  40. package/dist/lib/runtime/rotation-storage-meta.d.ts.map +1 -1
  41. package/dist/lib/runtime/rotation-storage-meta.js +8 -2
  42. package/dist/lib/storage/account-port.d.ts +2 -0
  43. package/dist/lib/storage/account-port.d.ts.map +1 -1
  44. package/dist/lib/storage/account-port.js +1 -0
  45. package/dist/lib/storage/import-export.d.ts +1 -0
  46. package/dist/lib/storage/import-export.d.ts.map +1 -1
  47. package/dist/lib/storage/import-export.js +25 -0
  48. package/dist/lib/storage/project-migration.d.ts +1 -1
  49. package/dist/lib/storage/project-migration.d.ts.map +1 -1
  50. package/dist/lib/storage/project-migration.js +28 -2
  51. package/dist/lib/storage.d.ts +10 -0
  52. package/dist/lib/storage.d.ts.map +1 -1
  53. package/dist/lib/storage.js +16 -1
  54. package/dist/lib/ui/auth-menu-builder.d.ts +10 -0
  55. package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
  56. package/dist/lib/ui/auth-menu-builder.js +29 -4
  57. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
- {
2
- "name": "codex-multi-auth",
3
- "version": "2.6.1",
4
- "description": "Install and operate codex-multi-auth for the official @openai/codex CLI with multi-account OAuth rotation, switching, health checks, and recovery tools.",
5
- "interface": {
6
- "composerIcon": "./assets/codex-multi-auth-icon.svg"
7
- },
8
- "skills": "./skills/"
9
- }
1
+ {
2
+ "name": "codex-multi-auth",
3
+ "version": "2.7.1",
4
+ "description": "Install and operate codex-multi-auth for the official @openai/codex CLI with multi-account OAuth rotation, switching, health checks, and recovery tools.",
5
+ "interface": {
6
+ "composerIcon": "./assets/codex-multi-auth-icon.svg"
7
+ },
8
+ "skills": "./skills/"
9
+ }
package/README.md CHANGED
@@ -45,15 +45,16 @@ Use it when you need a local Codex CLI multi-account workflow with visible accou
45
45
 
46
46
  ## Current Architecture At A Glance
47
47
 
48
- `codex-multi-auth` now ships three distinct global binaries:
48
+ `codex-multi-auth` ships four global binaries:
49
49
 
50
50
  | Binary | Purpose |
51
51
  | --- | --- |
52
52
  | `codex-multi-auth` | Primary account manager; accepts bare auth subcommands such as `login`, `status`, `switch`, `forecast`, and `rotation status` |
53
53
  | `codex-multi-auth-codex` | Optional wrapper that handles `auth ...` locally and forwards every other command to the official Codex CLI |
54
+ | `mcodex` | Convenience launcher over `codex-multi-auth-codex` with optional `--monitor` and `--tmux` modes |
54
55
  | `codex-multi-auth-app-launcher` | Optional desktop launcher helper for supported user-level shortcuts and wrapper apps |
55
56
 
56
- The package does not publish a global `codex` binary. Keep `codex` owned by the official OpenAI install path and use `codex-multi-auth-codex ...` only when you intentionally want this package's forwarding wrapper.
57
+ The package does not publish a global `codex` binary. Keep `codex` owned by the official OpenAI install path. Use `codex-multi-auth-codex ...` or `mcodex ...` only when you intentionally want this package's forwarding wrapper.
57
58
 
58
59
  ---
59
60
 
@@ -170,15 +171,18 @@ For remote or headless shells, prefer `codex-multi-auth login --device-auth`.
170
171
  | Command | What it answers |
171
172
  | --- | --- |
172
173
  | `codex-multi-auth login` | How do I add or re-open the account menu? |
173
- | `codex-multi-auth status` | Is the wrapper active right now? |
174
- | `codex-multi-auth check` | Do my saved accounts look healthy? |
174
+ | `codex-multi-auth status` | What does my account pool, pin, and runtime metrics look like? (`list` is the same output) |
175
+ | `codex-multi-auth check` | Do live health probes against my saved accounts succeed? |
175
176
 
176
177
  ### Daily use
177
178
 
178
179
  | Command | What it answers |
179
180
  | --- | --- |
180
- | `codex-multi-auth list` | Which accounts are saved and which one is active? |
181
- | `codex-multi-auth switch <index>` | How do I move to a different saved account? |
181
+ | `codex-multi-auth list` | Alias of `status` — full account/runtime dump |
182
+ | `codex-multi-auth switch <index>` | How do I pin a different saved account for runtime routing? |
183
+ | `codex-multi-auth unpin` | How do I clear a manual pin and resume hybrid rotation? |
184
+ | `codex-multi-auth workspace <account> [workspace]` | How do I list or set an account's active workspace? |
185
+ | `codex-multi-auth best --live` | How do I switch to the forecast-best account (clears any pin)? |
182
186
  | `codex-multi-auth-codex --account <index\|email\|id>` | How do I force one account for a single wrapper session without changing my default? |
183
187
  | `codex-multi-auth forecast --live` | Which account looks best for the next session? |
184
188
 
@@ -273,8 +277,10 @@ Selected runtime/environment overrides:
273
277
  | Variable | Effect |
274
278
  | --- | --- |
275
279
  | `CODEX_MULTI_AUTH_DIR` | Override settings/accounts root |
276
- | `CODEX_MULTI_AUTH_CONFIG_PATH` | Alternate config file path |
280
+ | `CODEX_MULTI_AUTH_CONFIG_PATH` | Prefer this config file when it exists (also the save target when set) |
277
281
  | `CODEX_MODE=0/1` | Disable/enable Codex mode |
282
+ | `CODEX_MULTI_AUTH_FORCE_ACCOUNT=<index\|email\|id>` | Force one account for a single `codex-multi-auth-codex` run (ephemeral; requires rotation proxy) |
283
+ | `CODEX_MULTI_AUTH_BYPASS=1` | Skip multi-auth intercept and forward straight to official Codex |
278
284
  | `CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY=0/1` | Opt out/in of live Responses proxy rotation for forwarded Codex CLI/app sessions |
279
285
  | `CODEX_MULTI_AUTH_APP_ROTATION_IDLE_MS=<ms>` | Override automatic Codex app helper idle shutdown |
280
286
  | `CODEX_MULTI_AUTH_APP_BIND_INSTALL=0/1` | Opt out/in of packaged Codex app bind self-heal on first CLI run or rotation enable |
@@ -385,7 +391,9 @@ codex-multi-auth doctor --json
385
391
 
386
392
  ## Release Notes
387
393
 
388
- - Current stable: [docs/releases/v2.6.1.md](docs/releases/v2.6.1.md) — install via `npm i -g codex-multi-auth`
394
+ - Current stable: [docs/releases/v2.7.1.md](docs/releases/v2.7.1.md) — install via `npm i -g codex-multi-auth`
395
+ - Previous stable: [docs/releases/v2.7.0.md](docs/releases/v2.7.0.md)
396
+ - Previous stable: [docs/releases/v2.6.0.md](docs/releases/v2.6.0.md)
389
397
  - Previous stable: [docs/releases/v2.5.0.md](docs/releases/v2.5.0.md)
390
398
  - Previous stable: [docs/releases/v2.4.0.md](docs/releases/v2.4.0.md)
391
399
  - Previous stable: [docs/releases/v2.3.3.md](docs/releases/v2.3.3.md)
package/dist/lib/cli.d.ts CHANGED
@@ -24,6 +24,8 @@ export interface ExistingAccountInfo {
24
24
  quota5hResetAtMs?: number;
25
25
  quota7dLeftPercent?: number;
26
26
  quota7dResetAtMs?: number;
27
+ quotaPrimaryWindowMinutes?: number;
28
+ quotaSecondaryWindowMinutes?: number;
27
29
  quotaRateLimited?: boolean;
28
30
  quotaExhausted?: boolean;
29
31
  isCurrentAccount?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../lib/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../lib/cli.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,25 @@ import type { DashboardDisplaySettings } from "../../dashboard-settings.js";
2
2
  import type { QuotaCacheEntry } from "../../quota-cache.js";
3
3
  import { type CodexQuotaSnapshot, fetchCodexQuotaSnapshot } from "../../quota-probe.js";
4
4
  export declare function styleQuotaSummary(summary: string): string;
5
- export declare function formatQuotaSnapshotForDashboard(snapshot: Awaited<ReturnType<typeof fetchCodexQuotaSnapshot>>, settings: DashboardDisplaySettings): string;
5
+ /**
6
+ * Render the per-account health line printed by `codex-multi-auth check`.
7
+ *
8
+ * `check` is the only caller, and unlike the dashboard rows and account menu it
9
+ * prints one account per line with no width pressure, so it opts into the
10
+ * absolute reset clock for each quota window.
11
+ */
12
+ export declare function formatQuotaSnapshotForDashboard(snapshot: Awaited<ReturnType<typeof fetchCodexQuotaSnapshot>>, settings: DashboardDisplaySettings, now?: number): string;
6
13
  export declare function quotaCacheEntryToSnapshot(entry: QuotaCacheEntry): CodexQuotaSnapshot;
7
- export declare function formatCompactQuotaSnapshot(snapshot: CodexQuotaSnapshot, now?: number): string;
8
- export declare function formatAccountQuotaSummary(entry: QuotaCacheEntry, now?: number): string;
14
+ /**
15
+ * Options shared by the compact quota formatters.
16
+ *
17
+ * `showReset` is opt-in so that space-constrained surfaces (dashboard rows, the
18
+ * account menu, forecast lines) keep their existing single-line width, while
19
+ * `codex-multi-auth check` can append the absolute reset time.
20
+ */
21
+ export interface CompactQuotaFormatOptions {
22
+ showReset?: boolean;
23
+ }
24
+ export declare function formatCompactQuotaSnapshot(snapshot: CodexQuotaSnapshot, now?: number, options?: CompactQuotaFormatOptions): string;
25
+ export declare function formatAccountQuotaSummary(entry: QuotaCacheEntry, now?: number, options?: CompactQuotaFormatOptions): string;
9
26
  //# sourceMappingURL=quota-formatters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"quota-formatters.d.ts","sourceRoot":"","sources":["../../../../lib/codex-manager/formatters/quota-formatters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EACN,KAAK,kBAAkB,EACvB,uBAAuB,EAEvB,MAAM,sBAAsB,CAAC;AAY9B,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA8BzD;AAED,wBAAgB,+BAA+B,CAC9C,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,EAC7D,QAAQ,EAAE,wBAAwB,GAChC,MAAM,CAGR;AAED,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,eAAe,GACpB,kBAAkB,CAgBpB;AAyBD,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,SAAa,GACd,MAAM,CAuBR;AAED,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,eAAe,EACtB,GAAG,SAAa,GACd,MAAM,CAuBR"}
1
+ {"version":3,"file":"quota-formatters.d.ts","sourceRoot":"","sources":["../../../../lib/codex-manager/formatters/quota-formatters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EACN,KAAK,kBAAkB,EACvB,uBAAuB,EAGvB,MAAM,sBAAsB,CAAC;AAY9B,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmCzD;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC9C,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,EAC7D,QAAQ,EAAE,wBAAwB,EAClC,GAAG,SAAa,GACd,MAAM,CAGR;AAED,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,eAAe,GACpB,kBAAkB,CAgBpB;AAaD;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAqBD,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,SAAa,EAChB,OAAO,GAAE,yBAA8B,GACrC,MAAM,CA6BR;AAED,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,eAAe,EACtB,GAAG,SAAa,EAChB,OAAO,GAAE,yBAA8B,GACrC,MAAM,CA6BR"}
@@ -1,4 +1,4 @@
1
- import { formatQuotaSnapshotLine, } from "../../quota-probe.js";
1
+ import { formatQuotaResetAt, formatQuotaSnapshotLine, } from "../../quota-probe.js";
2
2
  import { isQuotaCacheEntryExhausted, quotaLeftPercentFromUsed, } from "../../quota-readiness.js";
3
3
  import { quotaToneFromLeftPercent } from "../../ui/format.js";
4
4
  import { collapseWhitespace, joinStyledSegments, stylePromptText, } from "./text-style.js";
@@ -16,7 +16,7 @@ export function styleQuotaSummary(summary) {
16
16
  if (/rate-limited/i.test(segment)) {
17
17
  return stylePromptText(segment, "danger");
18
18
  }
19
- const match = segment.match(/^([0-9a-zA-Z]+)\s+(\d{1,3})%$/);
19
+ const match = segment.match(/^([0-9a-zA-Z]+)\s+(\d{1,3})%(,\s*resets\s+\S.*)?$/);
20
20
  if (!match) {
21
21
  return stylePromptText(segment, "muted");
22
22
  }
@@ -26,14 +26,24 @@ export function styleQuotaSummary(summary) {
26
26
  return stylePromptText(segment, "muted");
27
27
  }
28
28
  const tone = quotaToneFromLeftPercent(leftPercent);
29
- return `${stylePromptText(windowLabel, "muted")} ${stylePromptText(`${leftPercent}%`, tone)}`;
29
+ const resetSuffix = match[3]
30
+ ? stylePromptText(match[3], "muted")
31
+ : "";
32
+ return `${stylePromptText(windowLabel, "muted")} ${stylePromptText(`${leftPercent}%`, tone)}${resetSuffix}`;
30
33
  });
31
34
  return joinStyledSegments(rendered);
32
35
  }
33
- export function formatQuotaSnapshotForDashboard(snapshot, settings) {
36
+ /**
37
+ * Render the per-account health line printed by `codex-multi-auth check`.
38
+ *
39
+ * `check` is the only caller, and unlike the dashboard rows and account menu it
40
+ * prints one account per line with no width pressure, so it opts into the
41
+ * absolute reset clock for each quota window.
42
+ */
43
+ export function formatQuotaSnapshotForDashboard(snapshot, settings, now = Date.now()) {
34
44
  if (!settings.showQuotaDetails)
35
45
  return "live session OK";
36
- return `live session OK (${formatCompactQuotaSnapshot(snapshot)})`;
46
+ return `live session OK (${formatCompactQuotaSnapshot(snapshot, now, { showReset: true })})`;
37
47
  }
38
48
  export function quotaCacheEntryToSnapshot(entry) {
39
49
  return {
@@ -62,18 +72,23 @@ function formatCompactQuotaWindowLabel(windowMinutes) {
62
72
  return `${windowMinutes / 60}h`;
63
73
  return `${windowMinutes}m`;
64
74
  }
65
- function formatCompactQuotaPart(windowMinutes, usedPercent) {
75
+ function formatCompactQuotaPart(windowMinutes, usedPercent, resetAtMs, options, now) {
66
76
  const label = formatCompactQuotaWindowLabel(windowMinutes);
67
77
  if (typeof usedPercent !== "number" || !Number.isFinite(usedPercent)) {
68
78
  return null;
69
79
  }
70
80
  const left = quotaLeftPercentFromUsed(usedPercent);
71
- return `${label} ${left}%`;
81
+ const part = `${label} ${left}%`;
82
+ if (!options.showReset)
83
+ return part;
84
+ // A missing or malformed reset timestamp must never drop the percentage.
85
+ const reset = formatQuotaResetAt(resetAtMs, now);
86
+ return reset ? `${part}, resets ${reset}` : part;
72
87
  }
73
- export function formatCompactQuotaSnapshot(snapshot, now = Date.now()) {
88
+ export function formatCompactQuotaSnapshot(snapshot, now = Date.now(), options = {}) {
74
89
  const parts = [
75
- formatCompactQuotaPart(snapshot.primary.windowMinutes, snapshot.primary.usedPercent),
76
- formatCompactQuotaPart(snapshot.secondary.windowMinutes, snapshot.secondary.usedPercent),
90
+ formatCompactQuotaPart(snapshot.primary.windowMinutes, snapshot.primary.usedPercent, snapshot.primary.resetAtMs, options, now),
91
+ formatCompactQuotaPart(snapshot.secondary.windowMinutes, snapshot.secondary.usedPercent, snapshot.secondary.resetAtMs, options, now),
77
92
  ].filter((value) => typeof value === "string" && value.length > 0);
78
93
  if (snapshot.status === 429) {
79
94
  parts.push("rate-limited");
@@ -86,10 +101,10 @@ export function formatCompactQuotaSnapshot(snapshot, now = Date.now()) {
86
101
  }
87
102
  return formatQuotaSnapshotLine(snapshot);
88
103
  }
89
- export function formatAccountQuotaSummary(entry, now = Date.now()) {
104
+ export function formatAccountQuotaSummary(entry, now = Date.now(), options = {}) {
90
105
  const parts = [
91
- formatCompactQuotaPart(entry.primary.windowMinutes, entry.primary.usedPercent),
92
- formatCompactQuotaPart(entry.secondary.windowMinutes, entry.secondary.usedPercent),
106
+ formatCompactQuotaPart(entry.primary.windowMinutes, entry.primary.usedPercent, entry.primary.resetAtMs, options, now),
107
+ formatCompactQuotaPart(entry.secondary.windowMinutes, entry.secondary.usedPercent, entry.secondary.resetAtMs, options, now),
93
108
  ].filter((value) => typeof value === "string" && value.length > 0);
94
109
  if (entry.status === 429) {
95
110
  parts.push("rate-limited");
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/help.ts"],"names":[],"mappings":"AAIA,wBAAgB,UAAU,IAAI,IAAI,CAkDjC;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAgEtE;AAED,UAAU,cAAc;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,cAAc,IAAI,IAAI,CAgBrC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,CAyD5D"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/help.ts"],"names":[],"mappings":"AAIA,wBAAgB,UAAU,IAAI,IAAI,CAsDjC;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAgEtE;AAED,UAAU,cAAc;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,cAAc,IAAI,IAAI,CAgBrC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,CAyD5D"}
@@ -6,46 +6,50 @@ export function printUsage() {
6
6
  "",
7
7
  "Start here:",
8
8
  " codex-multi-auth login [--device-auth|--manual|--no-browser] [--org <org_id>]",
9
- " codex-multi-auth status",
10
- " codex-multi-auth check",
9
+ " codex-multi-auth status [--json] (list is the same command)",
10
+ " codex-multi-auth check (always live-probes)",
11
11
  "",
12
12
  "Daily use:",
13
- " codex-multi-auth list",
13
+ " codex-multi-auth list [--json]",
14
14
  " codex-multi-auth switch <index> (pins the account for runtime routing)",
15
15
  " codex-multi-auth unpin (clears the manual pin set by switch)",
16
16
  " codex-multi-auth workspace <account> [workspace] (list or switch an account's workspaces)",
17
17
  " codex-multi-auth best [--live] [--json] [--model <model>] (clears any manual pin set by switch)",
18
- " codex-multi-auth forecast [--live] [--json] [--model <model>]",
19
- " codex-multi-auth account tag|untag|weight|pause|unpause|drain|undrain|note ...",
18
+ " codex-multi-auth forecast [--live] [--json] [--explain] [--model <model>] [--no-runtime-overlay]",
19
+ " codex-multi-auth account tag|untag|weight|pause|unpause|drain|undrain|note|policy list ...",
20
20
  "",
21
21
  "Repair:",
22
22
  " codex-multi-auth uninstall [--dry-run] [--json] [--clear-accounts]",
23
- " codex-multi-auth verify-flagged [--dry-run] [--json] [--no-restore]",
23
+ " codex-multi-auth verify-flagged [--dry-run|-n] [--json] [--no-restore]",
24
24
  " codex-multi-auth verify [--paths | --flagged | --all] [--json]",
25
- " codex-multi-auth fix [--dry-run] [--json] [--live] [--model <model>]",
26
- " codex-multi-auth doctor [--json] [--fix] [--dry-run]",
25
+ " codex-multi-auth fix [--dry-run|-n] [--json] [--live] [--model <model>]",
26
+ " codex-multi-auth doctor [--json] [--fix] [--dry-run|-n] (--dry-run requires --fix)",
27
27
  "",
28
28
  "Diagnostics:",
29
29
  " codex-multi-auth usage [--since <time|duration>] [--by <group>] [--json|--csv] [--out <path>]",
30
- " codex-multi-auth budget limit|check|list ...",
31
- " codex-multi-auth bridge token create|list|rotate|revoke",
30
+ " codex-multi-auth usage rotate [--if-larger-than-bytes <n>] [--json]",
31
+ " codex-multi-auth budget limit <key> --window <hour|day|week|month> [--requests N] [--tokens N] [--cost USD]",
32
+ " codex-multi-auth budget check|list [--json]",
33
+ " codex-multi-auth bridge token create|list|rotate|revoke [--json]",
32
34
  " codex-multi-auth integrations [--kind <name>] [--base-url <url>] [--model <model>] [--json]",
33
35
  " codex-multi-auth models [--json] [--model <model>]",
34
36
  " codex-multi-auth monitor [--json]",
35
- " codex-multi-auth rotation <enable|disable|status|bind-app|unbind-app>",
37
+ " codex-multi-auth rotation <enable|disable|status|bind-app|unbind-app|reset-runtime>",
36
38
  " codex-multi-auth rotation reset-rate-limits [--all | --account <idx>] [--dry-run] [--json]",
37
- " codex-multi-auth why-selected [--now | --last] [--json]",
39
+ " codex-multi-auth why-selected [--now|-n | --last|-l] [--json]",
38
40
  " codex-multi-auth history [list|show <id>] [--json] (all local sessions, ignores /resume provider filtering)",
39
41
  "",
40
42
  "Advanced:",
41
- " codex-multi-auth report [--live] [--json] [--explain] [--model <model>] [--out <path>]",
43
+ " codex-multi-auth report [--live] [--json] [--explain] [--model <model>] [--max-accounts <n>] [--max-probes <n>] [--cached-only] [--out <path>]",
42
44
  " codex-multi-auth config explain [--json]",
45
+ " codex-multi-auth config template|init-config [modern|legacy|minimal] [--write <path>] [--stdout]",
43
46
  " codex-multi-auth debug bundle [--json]",
44
- " codex-multi-auth features",
47
+ " codex-multi-auth features (partial built-in checklist; see docs/features.md for the full product map)",
45
48
  "",
46
49
  "Notes:",
47
50
  " - Uses ~/.codex/multi-auth/openai-codex-accounts.json",
48
51
  " - Syncs active account into Codex CLI auth state",
52
+ " - Default live probe model: " + DEFAULT_LIVE_PROBE_MODEL,
49
53
  " - See docs/reference/commands.md for the full command and flag matrix",
50
54
  ].join("\n"));
51
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"login-menu-actions.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/login-menu-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAEN,KAAK,gBAAgB,EAGrB,KAAK,kBAAkB,EAGvB,MAAM,eAAe,CAAC;AASvB,OAAO,EACN,KAAK,eAAe,EAKpB,MAAM,kBAAkB,CAAC;AAG1B;;;;;GAKG;AAEH,gBAAgB;AAChB,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE7D,gBAAgB;AAChB,wBAAsB,qBAAqB,CAC1C,YAAY,EAAE,kBAAkB,GAAG,IAAI,EACvC,sBAAsB,GAAE,MAAM,GAAG,IAAW,GAC1C,OAAO,CAAC,eAAe,CAAC,CA6D1B;AAED,gBAAgB;AAChB,wBAAsB,uBAAuB,CAC5C,YAAY,EAAE,kBAAkB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CA0C5B;AAED,gBAAgB;AAChB,wBAAsB,2BAA2B,CAChD,OAAO,EAAE,kBAAkB,EAAE,GAC3B,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAkCpC;AA2GD,gBAAgB;AAChB,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,GACrD,OAAO,CAAC,IAAI,CAAC,CA8Gf"}
1
+ {"version":3,"file":"login-menu-actions.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/login-menu-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAEN,KAAK,gBAAgB,EAIrB,KAAK,kBAAkB,EAGvB,MAAM,eAAe,CAAC;AASvB,OAAO,EACN,KAAK,eAAe,EAKpB,MAAM,kBAAkB,CAAC;AAG1B;;;;;GAKG;AAEH,gBAAgB;AAChB,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE7D,gBAAgB;AAChB,wBAAsB,qBAAqB,CAC1C,YAAY,EAAE,kBAAkB,GAAG,IAAI,EACvC,sBAAsB,GAAE,MAAM,GAAG,IAAW,GAC1C,OAAO,CAAC,eAAe,CAAC,CA6D1B;AAED,gBAAgB;AAChB,wBAAsB,uBAAuB,CAC5C,YAAY,EAAE,kBAAkB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CA0C5B;AAED,gBAAgB;AAChB,wBAAsB,2BAA2B,CAChD,OAAO,EAAE,kBAAkB,EAAE,GAC3B,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAkCpC;AA8GD,gBAAgB;AAChB,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,GACrD,OAAO,CAAC,IAAI,CAAC,CA0Hf"}
@@ -1,7 +1,7 @@
1
1
  import { stdin as input, stdout as output } from "node:process";
2
2
  import { sanitizeEmail } from "../accounts.js";
3
3
  import { MODEL_FAMILIES } from "../prompts/codex.js";
4
- import { findMatchingAccountIndex, loadAccounts, setStoragePath, withAccountStorageTransaction, } from "../storage.js";
4
+ import { findMatchingAccountIndex, loadAccounts, reconcilePinnedAccountIndex, setStoragePath, withAccountStorageTransaction, } from "../storage.js";
5
5
  import { UI_COPY } from "../ui/ui-copy.js";
6
6
  import { getUiRuntimeOptions } from "../ui/runtime.js";
7
7
  import { select } from "../ui/select.js";
@@ -179,6 +179,9 @@ function replaceManageActionStorage(target, source) {
179
179
  target.activeIndexByFamily = {
180
180
  ...source.activeIndexByFamily,
181
181
  };
182
+ // Keep the in-memory view's manual pin in sync with what was just persisted;
183
+ // the pin was re-resolved (or cleared) against the post-deletion account list.
184
+ target.pinnedAccountIndex = source.pinnedAccountIndex;
182
185
  }
183
186
  function resolveManageActionAccountIndex(storage, fallbackIndex, account) {
184
187
  if (account) {
@@ -236,8 +239,16 @@ export async function handleManageAction(storage, menuResult) {
236
239
  if (!matchesManageActionAccount(selectedAccount, nextAccount)) {
237
240
  return;
238
241
  }
242
+ // Capture the pinned account BEFORE the splice so the manual pin can be
243
+ // followed by identity afterwards. resetManageActionSelection only remaps
244
+ // activeIndex; without this the pin keeps its raw slot and silently points
245
+ // at a different account (or wedges the pool). See #474.
246
+ const pinnedAccount = typeof nextStorage.pinnedAccountIndex === "number"
247
+ ? nextStorage.accounts[nextStorage.pinnedAccountIndex]
248
+ : undefined;
239
249
  nextStorage.accounts.splice(nextIndex, 1);
240
250
  resetManageActionSelection(nextStorage, nextIndex);
251
+ nextStorage.pinnedAccountIndex = reconcilePinnedAccountIndex(pinnedAccount, nextStorage.accounts);
241
252
  await persist(nextStorage);
242
253
  replaceManageActionStorage(storage, nextStorage);
243
254
  deleted = true;
@@ -1 +1 @@
1
- {"version":3,"file":"login-menu-data.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/login-menu-data.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,EAEN,KAAK,wBAAwB,EAE7B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEN,KAAK,cAAc,EAGnB,MAAM,mBAAmB,CAAC;AAe3B,OAAO,EAKN,KAAK,8BAA8B,EACnC,MAAM,uCAAuC,CAAC;AAE/C,OAAO,KAAK,EAAqB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAczE;;;;;;GAMG;AAEH,eAAO,MAAM,iCAAiC,QAAa,CAAC;AA+E5D,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,MAAM,EAChB,GAAG,SAAa,GACd,MAAM,CAkBR;AAED,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACnD,OAAO,CAAC,cAAc,CAAC,CAiGzB;AA0JD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,cAAc,GAAG,IAAI,EACjC,eAAe,EAAE,wBAAwB,EACzC,cAAc,GAAE,8BAA8B,GAAG,IAAW,GAC1D,mBAAmB,EAAE,CA6EvB;AAED,wBAAsB,qCAAqC,CAC1D,OAAO,EAAE,gBAAgB,EACzB,GAAG,SAAa,GACd,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAgBhD;AAsBD,wBAAsB,oCAAoC,CACzD,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,OAAO,CAAC,CAyBlB"}
1
+ {"version":3,"file":"login-menu-data.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/login-menu-data.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,EAEN,KAAK,wBAAwB,EAE7B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEN,KAAK,cAAc,EAGnB,MAAM,mBAAmB,CAAC;AAe3B,OAAO,EAKN,KAAK,8BAA8B,EACnC,MAAM,uCAAuC,CAAC;AAE/C,OAAO,KAAK,EAAqB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAezE;;;;;;GAMG;AAEH,eAAO,MAAM,iCAAiC,QAAa,CAAC;AA+E5D,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,MAAM,EAChB,GAAG,SAAa,GACd,MAAM,CAkBR;AAED,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACnD,OAAO,CAAC,cAAc,CAAC,CAiGzB;AAoKD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,cAAc,GAAG,IAAI,EACjC,eAAe,EAAE,wBAAwB,EACzC,cAAc,GAAE,8BAA8B,GAAG,IAAW,GAC1D,mBAAmB,EAAE,CA+EvB;AAED,wBAAsB,qCAAqC,CAC1D,OAAO,EAAE,gBAAgB,EACzB,GAAG,SAAa,GACd,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAgBhD;AAsBD,wBAAsB,oCAAoC,CACzD,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,OAAO,CAAC,CAyBlB"}
@@ -9,6 +9,7 @@ import { resolveActiveIndex } from "../runtime/account-status.js";
9
9
  import { getAppBindStatus } from "../runtime/app-bind.js";
10
10
  import { isDisplayCurrentAccount, readAppRuntimeHelperAccountSignal, resolveAccountCurrentMarkers, resolveRuntimeCurrentAccount, } from "../runtime/runtime-current-account.js";
11
11
  import { loadPersistedRuntimeObservabilitySnapshot } from "../runtime/runtime-observability.js";
12
+ import { resolveQuotaWindowLabel } from "../ui/auth-menu-builder.js";
12
13
  import { hasUsableAccessToken } from "./account-credentials.js";
13
14
  import { formatAccountQuotaSummary, formatRateLimitEntry, } from "./formatters/index.js";
14
15
  import { cloneQuotaCacheData, DEFAULT_LIVE_PROBE_MODEL, getPersistedQuotaViewForAccount, updateQuotaCacheForAccount, } from "./quota-cache-helpers.js";
@@ -175,17 +176,25 @@ function parseLeftPercentFromQuotaSummary(summary, windowLabel) {
175
176
  return -1;
176
177
  return Math.max(0, Math.min(100, value));
177
178
  }
178
- function readQuotaLeftPercent(account, windowLabel) {
179
- const direct = windowLabel === "5h"
179
+ function readQuotaLeftPercent(account, window) {
180
+ const isPrimary = window === "primary";
181
+ const direct = isPrimary
180
182
  ? account.quota5hLeftPercent
181
183
  : account.quota7dLeftPercent;
182
184
  if (typeof direct === "number" && Number.isFinite(direct)) {
183
185
  return Math.max(0, Math.min(100, Math.round(direct)));
184
186
  }
185
- return parseLeftPercentFromQuotaSummary(account.quotaSummary, windowLabel);
187
+ // The summary string is labelled by DURATION (formatAccountQuotaSummary), so a
188
+ // Codex Business row reads "30d 18%, ...". Searching it for the positional
189
+ // "5h"/"7d" finds nothing, scores the account -1, and sinks it to the bottom of
190
+ // a ready-first sort. Resolve the label from the window's real duration.
191
+ const label = resolveQuotaWindowLabel(isPrimary
192
+ ? account.quotaPrimaryWindowMinutes
193
+ : account.quotaSecondaryWindowMinutes, isPrimary ? "5h" : "7d");
194
+ return parseLeftPercentFromQuotaSummary(account.quotaSummary, label);
186
195
  }
187
196
  function readQuotaFloorPercent(account) {
188
- return Math.min(readQuotaLeftPercent(account, "5h"), readQuotaLeftPercent(account, "7d"));
197
+ return Math.min(readQuotaLeftPercent(account, "primary"), readQuotaLeftPercent(account, "secondary"));
189
198
  }
190
199
  function accountStatusSortBucket(status) {
191
200
  switch (status) {
@@ -220,12 +229,12 @@ function compareReadyFirstAccounts(left, right) {
220
229
  const rightFloor = readQuotaFloorPercent(right);
221
230
  if (leftFloor !== rightFloor)
222
231
  return rightFloor - leftFloor;
223
- const left5h = readQuotaLeftPercent(left, "5h");
224
- const right5h = readQuotaLeftPercent(right, "5h");
232
+ const left5h = readQuotaLeftPercent(left, "primary");
233
+ const right5h = readQuotaLeftPercent(right, "primary");
225
234
  if (left5h !== right5h)
226
235
  return right5h - left5h;
227
- const left7d = readQuotaLeftPercent(left, "7d");
228
- const right7d = readQuotaLeftPercent(right, "7d");
236
+ const left7d = readQuotaLeftPercent(left, "secondary");
237
+ const right7d = readQuotaLeftPercent(right, "secondary");
229
238
  if (left7d !== right7d)
230
239
  return right7d - left7d;
231
240
  const leftLastUsed = left.lastUsed ?? 0;
@@ -291,6 +300,8 @@ export function toExistingAccountInfo(storage, quotaCache, displaySettings, runt
291
300
  quota5hResetAtMs: entry?.primary.resetAtMs,
292
301
  quota7dLeftPercent: quotaLeftPercentFromUsed(entry?.secondary.usedPercent),
293
302
  quota7dResetAtMs: entry?.secondary.resetAtMs,
303
+ quotaPrimaryWindowMinutes: entry?.primary.windowMinutes,
304
+ quotaSecondaryWindowMinutes: entry?.secondary.windowMinutes,
294
305
  quotaRateLimited: entry?.status === 429,
295
306
  quotaExhausted,
296
307
  isCurrentAccount,
@@ -1 +1 @@
1
- {"version":3,"file":"codex-manager.d.ts","sourceRoot":"","sources":["../../lib/codex-manager.ts"],"names":[],"mappings":"AAqFA,OAAO,EAEN,mBAAmB,EAMnB,sBAAsB,EAGtB,MAAM,qCAAqC,CAAC;AAiD7C,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,CAAC;AA+HvD,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,OAAO,CAAC,CAuGrE;AAwRD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C7E"}
1
+ {"version":3,"file":"codex-manager.d.ts","sourceRoot":"","sources":["../../lib/codex-manager.ts"],"names":[],"mappings":"AAqFA,OAAO,EAEN,mBAAmB,EAMnB,sBAAsB,EAGtB,MAAM,qCAAqC,CAAC;AAiD7C,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,CAAC;AA4IvD,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,OAAO,CAAC,CAuGrE;AAwRD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C7E"}
@@ -113,6 +113,19 @@ const IMPLEMENTED_FEATURES = [
113
113
  { id: 39, name: "Unified color/theme runtime (v2 UI)" },
114
114
  { id: 40, name: "OAuth browser-first flow with manual callback fallback" },
115
115
  { id: 41, name: "Auto-switch to best account command" },
116
+ { id: 42, name: "Device-code OAuth login (--device-auth)" },
117
+ { id: 43, name: "Workspace/org-bound login (--org)" },
118
+ { id: 44, name: "Manual pin, unpin, and affinity-generation invalidation" },
119
+ { id: 45, name: "Per-account workspace selection command" },
120
+ { id: 46, name: "Default-on runtime Responses rotation proxy" },
121
+ { id: 47, name: "Reversible packaged Codex app bind and launcher routing" },
122
+ { id: 48, name: "Local usage ledger, budgets, and account policies" },
123
+ { id: 49, name: "Routing profiles and model capability matrix" },
124
+ { id: 50, name: "Local bridge tokens and integration snippets" },
125
+ { id: 51, name: "Provider-agnostic local history listing" },
126
+ { id: 52, name: "Why-selected and monitor diagnostics" },
127
+ { id: 53, name: "Config explain, init-config templates, and debug bundle" },
128
+ { id: 54, name: "mcodex convenience launcher (monitor/tmux/forward)" },
116
129
  ];
117
130
  async function runForecast(args) {
118
131
  return runForecastCommand(args, {
@@ -1 +1 @@
1
- {"version":3,"file":"forecast.d.ts","sourceRoot":"","sources":["../../lib/forecast.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"forecast.d.ts","sourceRoot":"","sources":["../../lib/forecast.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { formatAccountLabel, formatWaitTime } from "./accounts.js";
2
- import { findQuotaCacheEntryForAccount, isQuotaCacheEntryExhausted, quotaLeftPercentFromUsed, } from "./quota-readiness.js";
2
+ import { findQuotaCacheEntryForAccount, isQuotaCacheEntryExhausted, quotaUsedPercentIsExhausted, } from "./quota-readiness.js";
3
3
  import { getRateLimitResetTimeForFamily } from "./runtime/account-status.js";
4
4
  function clampRisk(score) {
5
5
  if (!Number.isFinite(score))
@@ -49,8 +49,10 @@ function getLiveQuotaWaitMs(snapshot, now, onlyExhausted) {
49
49
  // resets ~7d out; folding that in would overstate the wait by orders of
50
50
  // magnitude and invert the account recommendation (stress audit H5). On a
51
51
  // 429 we honor every future window, since the upstream said "slow down now"
52
- // regardless of the usage gauge.
53
- if (onlyExhausted && quotaLeftPercentFromUsed(window?.usedPercent) !== 0) {
52
+ // regardless of the usage gauge. The test is the RAW usedPercent, never the
53
+ // rounded left-percent: 100 - 99.6 rounds to 0 left, which would bench a
54
+ // window that still has quota and falsely mark the account "delayed".
55
+ if (onlyExhausted && !quotaUsedPercentIsExhausted(window?.usedPercent)) {
54
56
  continue;
55
57
  }
56
58
  const resetAt = window?.resetAtMs;
@@ -135,8 +137,13 @@ export function evaluateForecastAccount(input) {
135
137
  }
136
138
  const quotaEntry = findQuotaCacheEntryForAccount(input.quotaCache, account, input.allAccounts ?? [account]);
137
139
  if (isQuotaCacheEntryExhausted(quotaEntry, now)) {
140
+ // Only a window that is genuinely at/over 100% used should contribute its
141
+ // reset time. Testing the ROUNDED left-percent instead would treat a
142
+ // 99.6%-used sibling as at-limit and fold its (possibly far-future) reset
143
+ // into the Math.max below, overstating the wait for an account whose
144
+ // actually-exhausted window recovers much sooner.
138
145
  const resetAts = [quotaEntry?.primary, quotaEntry?.secondary]
139
- .filter((window) => quotaLeftPercentFromUsed(window?.usedPercent) === 0 &&
146
+ .filter((window) => quotaUsedPercentIsExhausted(window?.usedPercent) &&
140
147
  typeof window?.resetAtMs === "number" &&
141
148
  Number.isFinite(window.resetAtMs) &&
142
149
  window.resetAtMs > now)
@@ -207,6 +214,25 @@ export function evaluateForecastAccount(input) {
207
214
  if (liveWait > 0 && availability === "ready") {
208
215
  availability = "delayed";
209
216
  }
217
+ // A live window at 100% used with NO resetAtMs has no known recovery time —
218
+ // the probe reports it fully consumed and cannot say when it refills. On a
219
+ // 200 probe getLiveQuotaWaitMs then yields 0 (no reset to wait on) and the
220
+ // 429 branch never fires, so without this the account would stay "ready" and
221
+ // be recommended as a healthy pick. Treat that case as exhausted and downgrade
222
+ // a "ready" account to "delayed". A window WITH a resetAtMs is only *delayed*,
223
+ // not exhausted: getLiveQuotaWaitMs already contributes its wait above, so a
224
+ // 429 with known reset times stays a recoverable "delayed" account rather than
225
+ // a blocked one. The raw-usedPercent test (not rounded left-percent) keeps
226
+ // 99.6% from being falsely benched.
227
+ const liveExhausted = (quotaUsedPercentIsExhausted(quota.primary.usedPercent) &&
228
+ typeof quota.primary.resetAtMs !== "number") ||
229
+ (quotaUsedPercentIsExhausted(quota.secondary.usedPercent) &&
230
+ typeof quota.secondary.resetAtMs !== "number");
231
+ if (liveExhausted) {
232
+ exhausted = true;
233
+ if (availability === "ready")
234
+ availability = "delayed";
235
+ }
210
236
  const primaryUsage = describeQuotaUsage("primary", quota.primary.usedPercent);
211
237
  if (primaryUsage)
212
238
  reasons.push(primaryUsage);
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-policy.d.ts","sourceRoot":"","sources":["../../../lib/policy/runtime-policy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime-policy.d.ts","sourceRoot":"","sources":["../../../lib/policy/runtime-policy.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { resolveEntitlementAccountKey } from "../entitlement-cache.js";
2
2
  import { getAccountPolicyKey, loadAccountPolicyStore, } from "../account-policy.js";
3
- import { evaluateBudgetGuard, getBudgetWindowStart, loadBudgetGuardStore, } from "../budget-guard.js";
3
+ import { evaluateBudgetGuard, getBudgetWindowStart, loadBudgetGuardStore, normalizeBudgetKey, } from "../budget-guard.js";
4
4
  import { resolveProjectRoutingProfile, } from "../routing-profiles.js";
5
5
  import { appendUsageLedgerRow, summarizeUsageLedger, } from "../usage/index.js";
6
6
  export async function loadRuntimePolicyState(startDir = process.cwd()) {
@@ -33,12 +33,21 @@ function intersects(left, right) {
33
33
  }
34
34
  async function evaluateBudgets(input) {
35
35
  const keys = new Set();
36
+ // `global` is already normalize-clean. Project/profile keys can carry uppercase
37
+ // or spaces, but budget-guard STORES limits only under normalizeBudgetKey (see
38
+ // upsertBudgetLimit and load-time normalizeStore). Look them up the same way, or
39
+ // a key like `project:MyApp` never matches its stored `project:myapp` and the
40
+ // budget is silently unenforced.
36
41
  keys.add("global");
37
42
  if (input.state.project.projectKey) {
38
- keys.add(`project:${input.state.project.projectKey}`);
43
+ const projectKey = normalizeBudgetKey(`project:${input.state.project.projectKey}`);
44
+ if (projectKey)
45
+ keys.add(projectKey);
39
46
  }
40
47
  if (input.state.project.profile?.budgetKey) {
41
- keys.add(input.state.project.profile.budgetKey);
48
+ const budgetKey = normalizeBudgetKey(input.state.project.profile.budgetKey);
49
+ if (budgetKey)
50
+ keys.add(budgetKey);
42
51
  }
43
52
  const evaluations = [];
44
53
  for (const key of keys) {
@@ -32,6 +32,19 @@ export interface CodexQuotaSnapshot {
32
32
  secondary: CodexQuotaWindow;
33
33
  model: string;
34
34
  }
35
+ /**
36
+ * Format a millisecond epoch timestamp into a concise human-readable reset time.
37
+ *
38
+ * Returns a 24-hour time "HH:MM" when the timestamp is today, otherwise "HH:MM on Mon DD".
39
+ *
40
+ * This function has no side effects, is safe for concurrent use, does not access the filesystem,
41
+ * and produces no sensitive token data.
42
+ *
43
+ * @param resetAtMs - Timestamp in milliseconds since epoch; if `undefined`, non-finite, or <= 0, the function returns `undefined`.
44
+ * @param nowMs - Reference timestamp used for the "is today" comparison; defaults to the current time.
45
+ * @returns The formatted reset time string, or `undefined` if `resetAtMs` is invalid or not provided.
46
+ */
47
+ export declare function formatQuotaResetAt(resetAtMs: number | undefined, nowMs?: number): string | undefined;
35
48
  /**
36
49
  * Produce a single-line human-readable summary of a Codex quota snapshot.
37
50
  *
@@ -1 +1 @@
1
- {"version":3,"file":"quota-probe.d.ts","sourceRoot":"","sources":["../../lib/quota-probe.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,yCAAyC,CAAC;AAEnF;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,OAAO,EACd,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GACrC,MAAM,CAMR;AAED,MAAM,WAAW,gBAAgB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd;AAiQD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAa5E;AAED,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,sBAAsB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CA4G7B"}
1
+ {"version":3,"file":"quota-probe.d.ts","sourceRoot":"","sources":["../../lib/quota-probe.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,yCAAyC,CAAC;AAEnF;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,OAAO,EACd,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GACrC,MAAM,CAMR;AAED,MAAM,WAAW,gBAAgB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd;AA0MD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,KAAK,SAAa,GAChB,MAAM,GAAG,SAAS,CAqBpB;AAwBD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAa5E;AAED,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,sBAAsB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CA4G7B"}