codex-multi-auth 2.0.2 → 2.1.0

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 (121) hide show
  1. package/README.md +17 -5
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +91 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/account-policy.d.ts +22 -0
  6. package/dist/lib/account-policy.d.ts.map +1 -0
  7. package/dist/lib/account-policy.js +159 -0
  8. package/dist/lib/account-policy.js.map +1 -0
  9. package/dist/lib/accounts.d.ts.map +1 -1
  10. package/dist/lib/accounts.js +2 -1
  11. package/dist/lib/accounts.js.map +1 -1
  12. package/dist/lib/budget-guard.d.ts +39 -0
  13. package/dist/lib/budget-guard.d.ts.map +1 -0
  14. package/dist/lib/budget-guard.js +201 -0
  15. package/dist/lib/budget-guard.js.map +1 -0
  16. package/dist/lib/codex-manager/commands/account.d.ts +13 -0
  17. package/dist/lib/codex-manager/commands/account.d.ts.map +1 -0
  18. package/dist/lib/codex-manager/commands/account.js +156 -0
  19. package/dist/lib/codex-manager/commands/account.js.map +1 -0
  20. package/dist/lib/codex-manager/commands/bridge.d.ts +6 -0
  21. package/dist/lib/codex-manager/commands/bridge.d.ts.map +1 -0
  22. package/dist/lib/codex-manager/commands/bridge.js +100 -0
  23. package/dist/lib/codex-manager/commands/bridge.js.map +1 -0
  24. package/dist/lib/codex-manager/commands/budget.d.ts +12 -0
  25. package/dist/lib/codex-manager/commands/budget.d.ts.map +1 -0
  26. package/dist/lib/codex-manager/commands/budget.js +123 -0
  27. package/dist/lib/codex-manager/commands/budget.js.map +1 -0
  28. package/dist/lib/codex-manager/commands/integrations.d.ts +6 -0
  29. package/dist/lib/codex-manager/commands/integrations.d.ts.map +1 -0
  30. package/dist/lib/codex-manager/commands/integrations.js +78 -0
  31. package/dist/lib/codex-manager/commands/integrations.js.map +1 -0
  32. package/dist/lib/codex-manager/commands/models.d.ts +14 -0
  33. package/dist/lib/codex-manager/commands/models.d.ts.map +1 -0
  34. package/dist/lib/codex-manager/commands/models.js +74 -0
  35. package/dist/lib/codex-manager/commands/models.js.map +1 -0
  36. package/dist/lib/codex-manager/commands/monitor.d.ts +22 -0
  37. package/dist/lib/codex-manager/commands/monitor.d.ts.map +1 -0
  38. package/dist/lib/codex-manager/commands/monitor.js +94 -0
  39. package/dist/lib/codex-manager/commands/monitor.js.map +1 -0
  40. package/dist/lib/codex-manager/commands/rotation.d.ts +1 -0
  41. package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -1
  42. package/dist/lib/codex-manager/commands/rotation.js +293 -10
  43. package/dist/lib/codex-manager/commands/rotation.js.map +1 -1
  44. package/dist/lib/codex-manager/commands/usage.d.ts +13 -0
  45. package/dist/lib/codex-manager/commands/usage.d.ts.map +1 -0
  46. package/dist/lib/codex-manager/commands/usage.js +347 -0
  47. package/dist/lib/codex-manager/commands/usage.js.map +1 -0
  48. package/dist/lib/codex-manager/help.d.ts.map +1 -1
  49. package/dist/lib/codex-manager/help.js +8 -0
  50. package/dist/lib/codex-manager/help.js.map +1 -1
  51. package/dist/lib/codex-manager.d.ts.map +1 -1
  52. package/dist/lib/codex-manager.js +42 -2
  53. package/dist/lib/codex-manager.js.map +1 -1
  54. package/dist/lib/config.d.ts.map +1 -1
  55. package/dist/lib/config.js +2 -22
  56. package/dist/lib/config.js.map +1 -1
  57. package/dist/lib/constants.d.ts +1 -0
  58. package/dist/lib/constants.d.ts.map +1 -1
  59. package/dist/lib/constants.js +1 -0
  60. package/dist/lib/constants.js.map +1 -1
  61. package/dist/lib/env-parsing.d.ts +21 -0
  62. package/dist/lib/env-parsing.d.ts.map +1 -0
  63. package/dist/lib/env-parsing.js +34 -0
  64. package/dist/lib/env-parsing.js.map +1 -0
  65. package/dist/lib/index.d.ts +9 -0
  66. package/dist/lib/index.d.ts.map +1 -1
  67. package/dist/lib/index.js +9 -0
  68. package/dist/lib/index.js.map +1 -1
  69. package/dist/lib/integration-generators.d.ts +14 -0
  70. package/dist/lib/integration-generators.d.ts.map +1 -0
  71. package/dist/lib/integration-generators.js +93 -0
  72. package/dist/lib/integration-generators.js.map +1 -0
  73. package/dist/lib/local-bridge.d.ts +17 -0
  74. package/dist/lib/local-bridge.d.ts.map +1 -0
  75. package/dist/lib/local-bridge.js +249 -0
  76. package/dist/lib/local-bridge.js.map +1 -0
  77. package/dist/lib/local-client-tokens.d.ts +37 -0
  78. package/dist/lib/local-client-tokens.d.ts.map +1 -0
  79. package/dist/lib/local-client-tokens.js +200 -0
  80. package/dist/lib/local-client-tokens.js.map +1 -0
  81. package/dist/lib/model-capability-matrix.d.ts +38 -0
  82. package/dist/lib/model-capability-matrix.d.ts.map +1 -0
  83. package/dist/lib/model-capability-matrix.js +89 -0
  84. package/dist/lib/model-capability-matrix.js.map +1 -0
  85. package/dist/lib/policy/runtime-policy.d.ts +59 -0
  86. package/dist/lib/policy/runtime-policy.d.ts.map +1 -0
  87. package/dist/lib/policy/runtime-policy.js +156 -0
  88. package/dist/lib/policy/runtime-policy.js.map +1 -0
  89. package/dist/lib/proactive-refresh.js +4 -4
  90. package/dist/lib/proactive-refresh.js.map +1 -1
  91. package/dist/lib/refresh-lease.d.ts.map +1 -1
  92. package/dist/lib/refresh-lease.js +1 -10
  93. package/dist/lib/refresh-lease.js.map +1 -1
  94. package/dist/lib/routing-profiles.d.ts +36 -0
  95. package/dist/lib/routing-profiles.d.ts.map +1 -0
  96. package/dist/lib/routing-profiles.js +203 -0
  97. package/dist/lib/routing-profiles.js.map +1 -0
  98. package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
  99. package/dist/lib/runtime-rotation-proxy.js +96 -6
  100. package/dist/lib/runtime-rotation-proxy.js.map +1 -1
  101. package/dist/lib/usage/index.d.ts +5 -0
  102. package/dist/lib/usage/index.d.ts.map +1 -0
  103. package/dist/lib/usage/index.js +5 -0
  104. package/dist/lib/usage/index.js.map +1 -0
  105. package/dist/lib/usage/ledger.d.ts +12 -0
  106. package/dist/lib/usage/ledger.d.ts.map +1 -0
  107. package/dist/lib/usage/ledger.js +493 -0
  108. package/dist/lib/usage/ledger.js.map +1 -0
  109. package/dist/lib/usage/pricing.d.ts +11 -0
  110. package/dist/lib/usage/pricing.d.ts.map +1 -0
  111. package/dist/lib/usage/pricing.js +67 -0
  112. package/dist/lib/usage/pricing.js.map +1 -0
  113. package/dist/lib/usage/redaction.d.ts +10 -0
  114. package/dist/lib/usage/redaction.d.ts.map +1 -0
  115. package/dist/lib/usage/redaction.js +129 -0
  116. package/dist/lib/usage/redaction.js.map +1 -0
  117. package/dist/lib/usage/types.d.ts +89 -0
  118. package/dist/lib/usage/types.d.ts.map +1 -0
  119. package/dist/lib/usage/types.js +2 -0
  120. package/dist/lib/usage/types.js.map +1 -0
  121. package/package.json +1 -1
package/README.md CHANGED
@@ -22,8 +22,10 @@ Codex CLI-first multi-account OAuth manager for the official Codex CLI. The inst
22
22
  - Interactive dashboard for account actions and settings
23
23
  - Experimental settings tab for staged sync, backup, and refresh-guard controls
24
24
  - Forecast, report, fix, and doctor commands for operational safety
25
+ - Local usage ledger, budgets, account policy controls, routing profiles, and monitor views for local governance
25
26
  - Runtime counters, budget/cooldown state, and multi-auth probe visibility in `codex auth status` / `codex auth report`
26
27
  - Default-on runtime Responses proxy for live account rotation inside forwarded Codex CLI/app sessions
28
+ - Optional loopback-only local bridge for `/health`, `/v1/models`, and `/v1/responses`, protected by hashed local client tokens
27
29
  - Reversible packaged Codex app bind and user-level launcher routing helpers
28
30
  - Flagged account verification and restore flow
29
31
  - Session affinity and live account sync controls
@@ -173,6 +175,10 @@ For remote or headless shells, prefer `codex auth login --device-auth`.
173
175
  | `codex auth report --live --json` | How do I get the full machine-readable health report? |
174
176
  | `codex auth fix --live --model gpt-5.3-codex` | How do I run live repair probes with a chosen model? |
175
177
  | `codex auth why-selected --json` | Which account does the selector pick now, and why? |
178
+ | `codex auth usage --since 24h --by project` | What local usage has been recorded recently? |
179
+ | `codex auth monitor --json` | What is the combined usage, policy, quota, runtime, and project state? |
180
+ | `codex auth bridge token create --label local-client` | How do I create a local bridge bearer token? |
181
+ | `codex auth integrations --kind python` | How do I generate local bridge client snippets? |
176
182
  | `codex auth rotation status` | Is live runtime account rotation enabled for forwarded Codex sessions? |
177
183
 
178
184
  ### Reliability behavior
@@ -216,8 +222,13 @@ For remote or headless shells, prefer `codex auth login --device-auth`.
216
222
  | Settings | `~/.codex/multi-auth/settings.json` |
217
223
  | Accounts | `~/.codex/multi-auth/openai-codex-accounts.json` |
218
224
  | Flagged accounts | `~/.codex/multi-auth/openai-codex-flagged-accounts.json` |
219
- | Quota cache | `~/.codex/multi-auth/quota-cache.json` |
225
+ | Quota cache | `~/.codex/multi-auth/quota-cache.json` |
220
226
  | Runtime observability | `~/.codex/multi-auth/runtime-observability.json` |
227
+ | Usage ledger | `~/.codex/multi-auth/usage/usage-ledger.jsonl` |
228
+ | Account policies | `~/.codex/multi-auth/account-policies.json` |
229
+ | Routing profiles | `~/.codex/multi-auth/routing-profiles.json` |
230
+ | Budget guards | `~/.codex/multi-auth/budget-guards.json` |
231
+ | Local client tokens | `~/.codex/multi-auth/local-client-tokens.json` |
221
232
  | Runtime app helper status | `~/.codex/multi-auth/runtime-rotation-app-helper.json` |
222
233
  | Persistent app bind state/logs | `~/.codex/multi-auth/app-bind/` |
223
234
  | Logs | `~/.codex/multi-auth/logs/codex-plugin/` |
@@ -346,10 +357,11 @@ codex auth doctor --json
346
357
 
347
358
  ---
348
359
 
349
- ## Release Notes
350
-
351
- - Current stable: [docs/releases/v2.0.2.md](docs/releases/v2.0.2.md)
352
- - Previous stable: [docs/releases/v2.0.1.md](docs/releases/v2.0.1.md)
360
+ ## Release Notes
361
+
362
+ - Current stable: [docs/releases/v2.1.0.md](docs/releases/v2.1.0.md)
363
+ - Previous stable: [docs/releases/v2.0.2.md](docs/releases/v2.0.2.md)
364
+ - Earlier stable: [docs/releases/v2.0.1.md](docs/releases/v2.0.1.md)
353
365
  - Earlier stable: [docs/releases/v1.3.2.md](docs/releases/v1.3.2.md)
354
366
  - Stable archive: [docs/releases/v1.3.1.md](docs/releases/v1.3.1.md)
355
367
  - Full release archive: [docs/README.md#release-history](docs/README.md#release-history)
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,kBAAkB,CAAC;AAmV5D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAgoI/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,KAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,kBAAkB,CAAC;AA2V5D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAquI/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,KAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ import { EntitlementCache, resolveEntitlementAccountKey, } from "./lib/entitleme
37
37
  import { LiveAccountSync } from "./lib/live-account-sync.js";
38
38
  import { clearCorrelationId, initLogger, logDebug, logError, logInfo, logRequest, logWarn, setCorrelationId, } from "./lib/logger.js";
39
39
  import { PreemptiveQuotaScheduler, readQuotaSchedulerSnapshot, } from "./lib/preemptive-quota-scheduler.js";
40
+ import { createRuntimeUsageRecorder, evaluateRuntimePolicy, loadRuntimePolicyState, } from "./lib/policy/runtime-policy.js";
40
41
  import { getModelFamily, MODEL_FAMILIES, prewarmCodexInstructions, } from "./lib/prompts/codex.js";
41
42
  import { prewarmHostCodexPrompt } from "./lib/prompts/host-codex-prompt.js";
42
43
  import { fetchCodexQuotaSnapshot, formatQuotaSnapshotLine, } from "./lib/quota-probe.js";
@@ -543,6 +544,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
543
544
  * @returns Response from Codex API
544
545
  */
545
546
  async fetch(input, init) {
547
+ let usageRecorder = null;
548
+ let usageCompletion = null;
546
549
  try {
547
550
  if (cachedAccountManager &&
548
551
  cachedAccountManager !== accountManager) {
@@ -608,6 +611,59 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
608
611
  const requestTraceId = requestCorrelationId;
609
612
  runtimeMetrics.lastRequestAt = Date.now();
610
613
  syncRuntimeObservability(requestTraceId);
614
+ let runtimePolicyDecision = null;
615
+ try {
616
+ const runtimePolicyState = await loadRuntimePolicyState();
617
+ runtimePolicyDecision = await evaluateRuntimePolicy({
618
+ state: runtimePolicyState,
619
+ accounts: accountManager.getAccountsSnapshot(),
620
+ model: model ?? null,
621
+ capabilityPolicy: capabilityPolicyStore,
622
+ });
623
+ }
624
+ catch (policyError) {
625
+ logWarn("Runtime policy evaluation failed open.", {
626
+ error: policyError instanceof Error
627
+ ? policyError.message
628
+ : String(policyError),
629
+ });
630
+ }
631
+ usageRecorder = createRuntimeUsageRecorder({
632
+ source: "plugin-host",
633
+ operation: "responses",
634
+ model: model ?? null,
635
+ projectKey: runtimePolicyDecision?.projectKey ?? null,
636
+ requestId: requestTraceId,
637
+ startedAt: Date.now(),
638
+ });
639
+ usageCompletion = {
640
+ outcome: "failure",
641
+ statusCode: null,
642
+ errorCode: "plugin_host_request_failed",
643
+ };
644
+ if (runtimePolicyDecision && !runtimePolicyDecision.allowed) {
645
+ runtimeMetrics.failedRequests += 1;
646
+ runtimeMetrics.lastError = "Runtime policy blocked request";
647
+ syncRuntimeObservability(requestTraceId);
648
+ usageCompletion = {
649
+ outcome: "blocked",
650
+ statusCode: runtimePolicyDecision.statusCode,
651
+ errorCode: runtimePolicyDecision.errorCode,
652
+ };
653
+ return new Response(JSON.stringify({
654
+ error: {
655
+ message: "Runtime policy blocked this local request.",
656
+ code: runtimePolicyDecision.errorCode ??
657
+ "policy_blocked",
658
+ reasons: runtimePolicyDecision.reasons,
659
+ },
660
+ }), {
661
+ status: runtimePolicyDecision.statusCode,
662
+ headers: {
663
+ "content-type": "application/json; charset=utf-8",
664
+ },
665
+ });
666
+ }
611
667
  const abortSignal = requestInit?.signal ?? init?.signal ?? null;
612
668
  const sleep = createAbortableSleep(abortSignal);
613
669
  const poolCooldownRemainingMs = getPoolExhaustionCooldownRemaining();
@@ -616,6 +672,11 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
616
672
  runtimeMetrics.poolExhaustionFastFails += 1;
617
673
  runtimeMetrics.lastError = "Pool exhaustion cooldown active";
618
674
  syncRuntimeObservability(requestTraceId);
675
+ usageCompletion = {
676
+ outcome: "failure",
677
+ statusCode: 429,
678
+ errorCode: "pool_exhaustion_cooldown",
679
+ };
619
680
  return new Response(JSON.stringify({
620
681
  error: {
621
682
  message: `The account pool is cooling down after recent rate-limit exhaustion. Try again in ${formatWaitTime(poolCooldownRemainingMs)} or inspect \`codex auth status\`.`,
@@ -633,6 +694,11 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
633
694
  runtimeMetrics.serverBurstFastFails += 1;
634
695
  runtimeMetrics.lastError = "Server burst cooldown active";
635
696
  syncRuntimeObservability(requestTraceId);
697
+ usageCompletion = {
698
+ outcome: "failure",
699
+ statusCode: 503,
700
+ errorCode: "server_burst_cooldown",
701
+ };
636
702
  return new Response(JSON.stringify({
637
703
  error: {
638
704
  message: `Multiple accounts recently failed with upstream server errors. Try again in ${formatWaitTime(serverBurstCooldownRemainingMs)} or inspect \`codex auth report --json\`.`,
@@ -713,12 +779,20 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
713
779
  capabilityBoostByAccount[candidate.index] =
714
780
  capabilityPolicyStore.getBoost(accountKey, model ?? modelFamily);
715
781
  }
782
+ for (const [rawIndex, boost] of Object.entries(runtimePolicyDecision?.scoreBoostByAccount ?? {})) {
783
+ const index = Number(rawIndex);
784
+ if (!Number.isInteger(index))
785
+ continue;
786
+ capabilityBoostByAccount[index] =
787
+ (capabilityBoostByAccount[index] ?? 0) + boost;
788
+ }
716
789
  accountAttemptLoop: while (attempted.size < Math.max(1, accountCount)) {
717
790
  let account = null;
718
791
  if (!usedPreferredSessionAccount &&
719
792
  typeof preferredSessionAccountIndex === "number") {
720
793
  usedPreferredSessionAccount = true;
721
- if (accountManager.isAccountAvailableForFamily(preferredSessionAccountIndex, modelFamily, model)) {
794
+ if (accountManager.isAccountAvailableForFamily(preferredSessionAccountIndex, modelFamily, model) &&
795
+ !runtimePolicyDecision?.blockedAccountIndexes.has(preferredSessionAccountIndex)) {
722
796
  account = accountManager.getAccountByIndex(preferredSessionAccountIndex);
723
797
  if (account) {
724
798
  account.lastUsed = Date.now();
@@ -739,6 +813,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
739
813
  break;
740
814
  }
741
815
  attempted.add(account.index);
816
+ if (runtimePolicyDecision?.blockedAccountIndexes.has(account.index)) {
817
+ continue;
818
+ }
742
819
  // Log account selection for debugging rotation
743
820
  logDebug(`Using account ${account.index + 1}/${accountCount}: ${account.email ?? "unknown"} for ${modelFamily}`);
744
821
  let accountAuth = accountManager.toAuthDetails(account);
@@ -1587,6 +1664,16 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1587
1664
  }
1588
1665
  runtimeMetrics.successfulRequests++;
1589
1666
  runtimeMetrics.lastError = null;
1667
+ usageCompletion = {
1668
+ outcome: "success",
1669
+ statusCode: successResponse.status,
1670
+ errorCode: null,
1671
+ account: {
1672
+ index: successAccountForResponse.index,
1673
+ accountId: successAccountForResponse.accountId ?? null,
1674
+ email: successAccountForResponse.email ?? null,
1675
+ },
1676
+ };
1590
1677
  if (lastCodexCliActiveSyncIndex !==
1591
1678
  successAccountForResponse.index) {
1592
1679
  void accountManager.syncCodexCliActiveSelectionForIndex(successAccountForResponse.index);
@@ -1657,6 +1744,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1657
1744
  }
1658
1745
  }
1659
1746
  finally {
1747
+ if (usageRecorder && usageCompletion) {
1748
+ await usageRecorder.record(usageCompletion);
1749
+ }
1660
1750
  syncRuntimeObservability(null);
1661
1751
  clearCorrelationId();
1662
1752
  }