codex-multi-auth 2.15.0 → 2.17.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 (272) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/README.md +22 -1
  3. package/config/README.md +2 -2
  4. package/config/codex-legacy.json +109 -184
  5. package/config/codex-modern.json +44 -114
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +10 -4
  8. package/dist/lib/account-policy.d.ts +4 -0
  9. package/dist/lib/account-policy.d.ts.map +1 -1
  10. package/dist/lib/account-policy.js +8 -0
  11. package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
  12. package/dist/lib/accounts/rate-limits.js +2 -0
  13. package/dist/lib/accounts.d.ts +21 -3
  14. package/dist/lib/accounts.d.ts.map +1 -1
  15. package/dist/lib/accounts.js +229 -17
  16. package/dist/lib/api-route-store.d.ts +24 -0
  17. package/dist/lib/api-route-store.d.ts.map +1 -0
  18. package/dist/lib/api-route-store.js +145 -0
  19. package/dist/lib/auth/account-access.d.ts +115 -0
  20. package/dist/lib/auth/account-access.d.ts.map +1 -0
  21. package/dist/lib/auth/account-access.js +214 -0
  22. package/dist/lib/auth/token-utils.d.ts +54 -1
  23. package/dist/lib/auth/token-utils.d.ts.map +1 -1
  24. package/dist/lib/auth/token-utils.js +92 -7
  25. package/dist/lib/cli.d.ts +1 -1
  26. package/dist/lib/cli.d.ts.map +1 -1
  27. package/dist/lib/cli.js +4 -0
  28. package/dist/lib/codex-cli/state.d.ts +7 -0
  29. package/dist/lib/codex-cli/state.d.ts.map +1 -1
  30. package/dist/lib/codex-cli/state.js +18 -0
  31. package/dist/lib/codex-cli/writer.d.ts.map +1 -1
  32. package/dist/lib/codex-cli/writer.js +60 -8
  33. package/dist/lib/codex-manager/account-manager-commands.d.ts.map +1 -1
  34. package/dist/lib/codex-manager/account-manager-commands.js +1 -0
  35. package/dist/lib/codex-manager/account-pool-write.d.ts +3 -1
  36. package/dist/lib/codex-manager/account-pool-write.d.ts.map +1 -1
  37. package/dist/lib/codex-manager/account-pool-write.js +4 -0
  38. package/dist/lib/codex-manager/api-login-menu.d.ts +14 -0
  39. package/dist/lib/codex-manager/api-login-menu.d.ts.map +1 -0
  40. package/dist/lib/codex-manager/api-login-menu.js +224 -0
  41. package/dist/lib/codex-manager/commands/account.d.ts.map +1 -1
  42. package/dist/lib/codex-manager/commands/account.js +27 -0
  43. package/dist/lib/codex-manager/commands/best.d.ts.map +1 -1
  44. package/dist/lib/codex-manager/commands/best.js +6 -2
  45. package/dist/lib/codex-manager/commands/check.d.ts +7 -1
  46. package/dist/lib/codex-manager/commands/check.d.ts.map +1 -1
  47. package/dist/lib/codex-manager/commands/check.js +24 -3
  48. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
  49. package/dist/lib/codex-manager/commands/forecast.js +5 -1
  50. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
  51. package/dist/lib/codex-manager/commands/report.js +5 -1
  52. package/dist/lib/codex-manager/commands/resets.d.ts +2 -0
  53. package/dist/lib/codex-manager/commands/resets.d.ts.map +1 -0
  54. package/dist/lib/codex-manager/commands/resets.js +67 -0
  55. package/dist/lib/codex-manager/commands/rotation.d.ts +2 -2
  56. package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -1
  57. package/dist/lib/codex-manager/commands/rotation.js +63 -12
  58. package/dist/lib/codex-manager/commands/status.d.ts +12 -1
  59. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
  60. package/dist/lib/codex-manager/commands/status.js +166 -22
  61. package/dist/lib/codex-manager/commands/uninstall.js +1 -1
  62. package/dist/lib/codex-manager/formatters/quota-formatters.d.ts.map +1 -1
  63. package/dist/lib/codex-manager/formatters/quota-formatters.js +3 -2
  64. package/dist/lib/codex-manager/health-check.d.ts +3 -0
  65. package/dist/lib/codex-manager/health-check.d.ts.map +1 -1
  66. package/dist/lib/codex-manager/health-check.js +56 -6
  67. package/dist/lib/codex-manager/help.d.ts.map +1 -1
  68. package/dist/lib/codex-manager/help.js +4 -2
  69. package/dist/lib/codex-manager/login-flow.d.ts.map +1 -1
  70. package/dist/lib/codex-manager/login-flow.js +69 -7
  71. package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
  72. package/dist/lib/codex-manager/login-menu-data.js +14 -5
  73. package/dist/lib/codex-manager/login-oauth.d.ts +3 -0
  74. package/dist/lib/codex-manager/login-oauth.d.ts.map +1 -1
  75. package/dist/lib/codex-manager/login-oauth.js +6 -1
  76. package/dist/lib/codex-manager/login-workspace-choice.d.ts +11 -0
  77. package/dist/lib/codex-manager/login-workspace-choice.d.ts.map +1 -0
  78. package/dist/lib/codex-manager/login-workspace-choice.js +37 -0
  79. package/dist/lib/codex-manager/persist-selected-account.d.ts.map +1 -1
  80. package/dist/lib/codex-manager/persist-selected-account.js +2 -1
  81. package/dist/lib/codex-manager/repair-commands.d.ts +13 -0
  82. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
  83. package/dist/lib/codex-manager/repair-commands.js +143 -8
  84. package/dist/lib/codex-manager.d.ts.map +1 -1
  85. package/dist/lib/codex-manager.js +25 -6
  86. package/dist/lib/concurrency.d.ts +3 -0
  87. package/dist/lib/concurrency.d.ts.map +1 -0
  88. package/dist/lib/concurrency.js +31 -0
  89. package/dist/lib/context-budget/model-context-windows.d.ts +1 -1
  90. package/dist/lib/context-budget/model-context-windows.d.ts.map +1 -1
  91. package/dist/lib/context-budget/model-context-windows.js +9 -10
  92. package/dist/lib/model-route-policy.d.ts +52 -0
  93. package/dist/lib/model-route-policy.d.ts.map +1 -0
  94. package/dist/lib/model-route-policy.js +159 -0
  95. package/dist/lib/policy/runtime-policy.d.ts +1 -0
  96. package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
  97. package/dist/lib/policy/runtime-policy.js +3 -0
  98. package/dist/lib/preemptive-quota-scheduler.d.ts +2 -0
  99. package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -1
  100. package/dist/lib/preemptive-quota-scheduler.js +4 -0
  101. package/dist/lib/prompts/codex.d.ts.map +1 -1
  102. package/dist/lib/prompts/codex.js +3 -1
  103. package/dist/lib/quota-probe.d.ts +5 -0
  104. package/dist/lib/quota-probe.d.ts.map +1 -1
  105. package/dist/lib/quota-probe.js +26 -2
  106. package/dist/lib/request/client-cancellation.d.ts +5 -0
  107. package/dist/lib/request/client-cancellation.d.ts.map +1 -0
  108. package/dist/lib/request/client-cancellation.js +7 -0
  109. package/dist/lib/request/error-classification.d.ts.map +1 -1
  110. package/dist/lib/request/error-classification.js +39 -25
  111. package/dist/lib/request/fetch-helpers.js +1 -1
  112. package/dist/lib/request/helpers/model-map.d.ts +12 -2
  113. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  114. package/dist/lib/request/helpers/model-map.js +174 -147
  115. package/dist/lib/request/response-outcome.d.ts +25 -0
  116. package/dist/lib/request/response-outcome.d.ts.map +1 -0
  117. package/dist/lib/request/response-outcome.js +49 -0
  118. package/dist/lib/request/stream-failover-runtime.d.ts +2 -1
  119. package/dist/lib/request/stream-failover-runtime.d.ts.map +1 -1
  120. package/dist/lib/request/stream-failover-runtime.js +29 -10
  121. package/dist/lib/runtime/account-check.d.ts.map +1 -1
  122. package/dist/lib/runtime/account-check.js +2 -8
  123. package/dist/lib/runtime/account-model-catalog.d.ts +64 -0
  124. package/dist/lib/runtime/account-model-catalog.d.ts.map +1 -0
  125. package/dist/lib/runtime/account-model-catalog.js +154 -0
  126. package/dist/lib/runtime/account-pool.d.ts +3 -1
  127. package/dist/lib/runtime/account-pool.d.ts.map +1 -1
  128. package/dist/lib/runtime/account-pool.js +4 -0
  129. package/dist/lib/runtime/account-reset-credits.d.ts +42 -0
  130. package/dist/lib/runtime/account-reset-credits.d.ts.map +1 -0
  131. package/dist/lib/runtime/account-reset-credits.js +82 -0
  132. package/dist/lib/runtime/api-model-capabilities.d.ts +30 -0
  133. package/dist/lib/runtime/api-model-capabilities.d.ts.map +1 -0
  134. package/dist/lib/runtime/api-model-capabilities.js +434 -0
  135. package/dist/lib/runtime/api-model-runtime.d.ts +42 -0
  136. package/dist/lib/runtime/api-model-runtime.d.ts.map +1 -0
  137. package/dist/lib/runtime/api-model-runtime.js +358 -0
  138. package/dist/lib/runtime/app-bind.d.ts +19 -1
  139. package/dist/lib/runtime/app-bind.d.ts.map +1 -1
  140. package/dist/lib/runtime/app-bind.js +79 -9
  141. package/dist/lib/runtime/automatic-account-checks.d.ts +31 -0
  142. package/dist/lib/runtime/automatic-account-checks.d.ts.map +1 -0
  143. package/dist/lib/runtime/automatic-account-checks.js +113 -0
  144. package/dist/lib/runtime/automatic-subscription-checks.d.ts +7 -0
  145. package/dist/lib/runtime/automatic-subscription-checks.d.ts.map +1 -0
  146. package/dist/lib/runtime/automatic-subscription-checks.js +57 -0
  147. package/dist/lib/runtime/catalog-capabilities.d.ts +8 -0
  148. package/dist/lib/runtime/catalog-capabilities.d.ts.map +1 -0
  149. package/dist/lib/runtime/catalog-capabilities.js +61 -0
  150. package/dist/lib/runtime/inference-activity.d.ts +15 -0
  151. package/dist/lib/runtime/inference-activity.d.ts.map +1 -0
  152. package/dist/lib/runtime/inference-activity.js +95 -0
  153. package/dist/lib/runtime/model-discovery-status.d.ts +100 -0
  154. package/dist/lib/runtime/model-discovery-status.d.ts.map +1 -0
  155. package/dist/lib/runtime/model-discovery-status.js +387 -0
  156. package/dist/lib/runtime/native-account-storage.d.ts +11 -0
  157. package/dist/lib/runtime/native-account-storage.d.ts.map +1 -0
  158. package/dist/lib/runtime/native-account-storage.js +50 -0
  159. package/dist/lib/runtime/native-account-sync.d.ts +19 -0
  160. package/dist/lib/runtime/native-account-sync.d.ts.map +1 -0
  161. package/dist/lib/runtime/native-account-sync.js +62 -0
  162. package/dist/lib/runtime/native-binding-lock.d.ts +3 -0
  163. package/dist/lib/runtime/native-binding-lock.d.ts.map +1 -0
  164. package/dist/lib/runtime/native-binding-lock.js +5 -0
  165. package/dist/lib/runtime/native-client-auth.d.ts +3 -0
  166. package/dist/lib/runtime/native-client-auth.d.ts.map +1 -0
  167. package/dist/lib/runtime/native-client-auth.js +42 -0
  168. package/dist/lib/runtime/native-provider-config.d.ts +4 -0
  169. package/dist/lib/runtime/native-provider-config.d.ts.map +1 -0
  170. package/dist/lib/runtime/native-provider-config.js +52 -0
  171. package/dist/lib/runtime/native-rate-limits.d.ts +18 -0
  172. package/dist/lib/runtime/native-rate-limits.d.ts.map +1 -0
  173. package/dist/lib/runtime/native-rate-limits.js +135 -0
  174. package/dist/lib/runtime/reset-credit-routing.d.ts +41 -0
  175. package/dist/lib/runtime/reset-credit-routing.d.ts.map +1 -0
  176. package/dist/lib/runtime/reset-credit-routing.js +66 -0
  177. package/dist/lib/runtime/reset-credits.d.ts +101 -0
  178. package/dist/lib/runtime/reset-credits.d.ts.map +1 -0
  179. package/dist/lib/runtime/reset-credits.js +162 -0
  180. package/dist/lib/runtime/response-output-history.d.ts +17 -0
  181. package/dist/lib/runtime/response-output-history.d.ts.map +1 -0
  182. package/dist/lib/runtime/response-output-history.js +85 -0
  183. package/dist/lib/runtime/responses-websocket.d.ts +31 -0
  184. package/dist/lib/runtime/responses-websocket.d.ts.map +1 -0
  185. package/dist/lib/runtime/responses-websocket.js +636 -0
  186. package/dist/lib/runtime/resume-picker.d.ts +41 -0
  187. package/dist/lib/runtime/resume-picker.d.ts.map +1 -0
  188. package/dist/lib/runtime/resume-picker.js +169 -0
  189. package/dist/lib/runtime/rotation-account-selection.d.ts +6 -0
  190. package/dist/lib/runtime/rotation-account-selection.d.ts.map +1 -1
  191. package/dist/lib/runtime/rotation-account-selection.js +63 -0
  192. package/dist/lib/runtime/rotation-proxy-state.d.ts +33 -0
  193. package/dist/lib/runtime/rotation-proxy-state.d.ts.map +1 -1
  194. package/dist/lib/runtime/rotation-server-types.d.ts +18 -0
  195. package/dist/lib/runtime/rotation-server-types.d.ts.map +1 -1
  196. package/dist/lib/runtime/runtime-capability-failures.d.ts +14 -0
  197. package/dist/lib/runtime/runtime-capability-failures.d.ts.map +1 -0
  198. package/dist/lib/runtime/runtime-capability-failures.js +76 -0
  199. package/dist/lib/runtime/runtime-observability.d.ts +7 -0
  200. package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
  201. package/dist/lib/runtime/runtime-observability.js +42 -2
  202. package/dist/lib/runtime/subscription-first-use.d.ts +10 -0
  203. package/dist/lib/runtime/subscription-first-use.d.ts.map +1 -0
  204. package/dist/lib/runtime/subscription-first-use.js +64 -0
  205. package/dist/lib/runtime/subscription-quota-event.d.ts +6 -0
  206. package/dist/lib/runtime/subscription-quota-event.d.ts.map +1 -0
  207. package/dist/lib/runtime/subscription-quota-event.js +31 -0
  208. package/dist/lib/runtime/subscription-quota-order.d.ts +17 -0
  209. package/dist/lib/runtime/subscription-quota-order.d.ts.map +1 -0
  210. package/dist/lib/runtime/subscription-quota-order.js +31 -0
  211. package/dist/lib/runtime/workspace-model-scopes.d.ts +15 -0
  212. package/dist/lib/runtime/workspace-model-scopes.d.ts.map +1 -0
  213. package/dist/lib/runtime/workspace-model-scopes.js +34 -0
  214. package/dist/lib/runtime-constants.d.ts +3 -0
  215. package/dist/lib/runtime-constants.d.ts.map +1 -1
  216. package/dist/lib/runtime-constants.js +5 -0
  217. package/dist/lib/runtime-paths.d.ts.map +1 -1
  218. package/dist/lib/runtime-paths.js +4 -1
  219. package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
  220. package/dist/lib/runtime-rotation-proxy.js +855 -46
  221. package/dist/lib/schemas.d.ts +20 -0
  222. package/dist/lib/schemas.d.ts.map +1 -1
  223. package/dist/lib/schemas.js +3 -0
  224. package/dist/lib/storage/credential-sidecars.d.ts +13 -0
  225. package/dist/lib/storage/credential-sidecars.d.ts.map +1 -0
  226. package/dist/lib/storage/credential-sidecars.js +52 -0
  227. package/dist/lib/storage/file-lock.d.ts +9 -0
  228. package/dist/lib/storage/file-lock.d.ts.map +1 -0
  229. package/dist/lib/storage/file-lock.js +186 -0
  230. package/dist/lib/storage/flagged-storage.d.ts.map +1 -1
  231. package/dist/lib/storage/flagged-storage.js +12 -0
  232. package/dist/lib/storage/path-state.d.ts +2 -0
  233. package/dist/lib/storage/path-state.d.ts.map +1 -1
  234. package/dist/lib/storage/path-state.js +9 -0
  235. package/dist/lib/storage/paths.js +4 -4
  236. package/dist/lib/storage/pending-auth.d.ts +17 -0
  237. package/dist/lib/storage/pending-auth.d.ts.map +1 -0
  238. package/dist/lib/storage/pending-auth.js +153 -0
  239. package/dist/lib/storage/public-types.d.ts +13 -0
  240. package/dist/lib/storage/public-types.d.ts.map +1 -1
  241. package/dist/lib/storage/snapshot-merge.d.ts +16 -0
  242. package/dist/lib/storage/snapshot-merge.d.ts.map +1 -0
  243. package/dist/lib/storage/snapshot-merge.js +206 -0
  244. package/dist/lib/storage/transactions.d.ts.map +1 -1
  245. package/dist/lib/storage/transactions.js +5 -4
  246. package/dist/lib/storage.d.ts +6 -0
  247. package/dist/lib/storage.d.ts.map +1 -1
  248. package/dist/lib/storage.js +77 -7
  249. package/dist/lib/ui/auth-menu-builder.d.ts +2 -0
  250. package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
  251. package/dist/lib/ui/auth-menu-builder.js +1 -0
  252. package/dist/lib/ui/auth-menu.d.ts.map +1 -1
  253. package/dist/lib/ui/auth-menu.js +1 -0
  254. package/dist/lib/ui/check-progress.d.ts +8 -0
  255. package/dist/lib/ui/check-progress.d.ts.map +1 -0
  256. package/dist/lib/ui/check-progress.js +30 -0
  257. package/dist/lib/ui/format.d.ts +3 -1
  258. package/dist/lib/ui/format.d.ts.map +1 -1
  259. package/dist/lib/ui/format.js +8 -0
  260. package/dist/lib/ui/ui-copy.d.ts +2 -2
  261. package/dist/lib/ui/ui-copy.js +2 -2
  262. package/dist/lib/usage/pricing.d.ts +19 -2
  263. package/dist/lib/usage/pricing.d.ts.map +1 -1
  264. package/dist/lib/usage/pricing.js +101 -40
  265. package/dist/lib/usage/usage-extraction.d.ts +3 -1
  266. package/dist/lib/usage/usage-extraction.d.ts.map +1 -1
  267. package/dist/lib/usage/usage-extraction.js +46 -9
  268. package/package.json +7 -4
  269. package/scripts/codex-app-router.js +7 -0
  270. package/scripts/codex-bin-resolver.js +40 -8
  271. package/scripts/codex-routing.js +1 -0
  272. package/scripts/codex.js +209 -126
@@ -144,8 +144,8 @@ export const UI_COPY = {
144
144
  fallback: {
145
145
  addAnotherTip: "Tip: Use private mode or sign out before adding another account.",
146
146
  addAnotherQuestion: (count) => `Add another account? (${count} added) (y/n): `,
147
- selectModePrompt: "(a) add, (c) check, (b) best, fi(x), (s) settings, (d) deep, (g) problem, (f) fresh, (q) back [a/c/b/x/s/d/g/f/q]: ",
148
- invalidModePrompt: "Use one of: a, c, b, x, s, d, g, f, q.",
147
+ selectModePrompt: "(api) API models, (a) add, (c) check, (b) best, fi(x), (s) settings, (d) deep, (g) problem, (f) fresh, (q) back [api/a/c/b/x/s/d/g/f/q]: ",
148
+ invalidModePrompt: "Use one of: api, a, c, b, x, s, d, g, f, q.",
149
149
  },
150
150
  };
151
151
  /**
@@ -12,14 +12,31 @@ export interface UsageModelPricing {
12
12
  * row under-counts it by half and lets a `maxCostUsd` cap overrun.
13
13
  *
14
14
  * A tier absent from this map is deliberately NOT approximated from the
15
- * standard rate. Only Astra's Fast multiplier is published; inventing one for
15
+ * standard rate. Only the GPT-6 Fast rates are published; inventing one for
16
16
  * the rest would move a budget's trip point on a guess, which is the failure
17
17
  * this file exists to avoid. `estimateUsageCostUsd` reports an unlisted tier
18
18
  * as unknown cost instead, so a budget fails closed the same way it does for
19
19
  * an unpriced model.
20
20
  */
21
21
  serviceTiers?: Partial<Record<UsageServiceTier, UsageModelPricing>>;
22
+ /**
23
+ * Rates once a response's input exceeds `SHORT_CONTEXT_MAX_INPUT_TOKENS`.
24
+ *
25
+ * OpenAI bills a GPT-6 request whose context crosses 272K at a separate,
26
+ * higher rate. A row that declares this block (every tier inside it too) is
27
+ * priced from it past the threshold. A row that declares it but whose
28
+ * resolved service tier does not reports unknown cost there instead of the
29
+ * cheaper short-context figure, for the same fail-closed reason as an
30
+ * unlisted tier. Rows with no long-context data at all are unchanged.
31
+ */
32
+ longContext?: UsageModelPricing;
22
33
  }
34
+ /**
35
+ * Largest input still billed at the short-context rate. OpenAI's GPT-6 model
36
+ * pages say "Prompts with more than 272K input tokens are priced at 2x input
37
+ * and cache rates", so exactly 272,000 is short and 272,001 is long.
38
+ */
39
+ export declare const SHORT_CONTEXT_MAX_INPUT_TOKENS = 272000;
23
40
  /**
24
41
  * Models the router can normalize to that have no published rate here yet.
25
42
  *
@@ -34,7 +51,7 @@ export interface UsageModelPricing {
34
51
  * `test/usage-pricing-coverage.test.ts` fails if a NEW routable model appears
35
52
  * in neither list.
36
53
  */
37
- export declare const UNPRICED_ROUTABLE_MODELS: readonly ["gpt-6-astra-aeon", "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.1", "gpt-5.2-pro", "gpt-5.4-mini", "gpt-5.4-nano", "gpt-5.4-pro", "gpt-5.5-pro", "gpt-5-mini", "gpt-5-nano"];
54
+ export declare const UNPRICED_ROUTABLE_MODELS: readonly ["gpt-6-astra-aeon", "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.5-pro"];
38
55
  export declare function getUsageModelPricing(model: string | null | undefined): UsageModelPricing | null;
39
56
  export declare function estimateUsageCostUsd(model: string | null | undefined, tokens: UsageTokenCounts): number | null;
40
57
  export declare function listUsageModelPricing(): Record<string, UsageModelPricing>;
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../../lib/usage/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,iBAAiB;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC,CAAC;CACpE;AAyFD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,0MAkB3B,CAAC;AAOX,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,iBAAiB,GAAG,IAAI,CAiB1B;AAmBD,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,MAAM,EAAE,gBAAgB,GACtB,MAAM,GAAG,IAAI,CA8Bf;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAEzE"}
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../../lib/usage/pricing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,iBAAiB;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACpE;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,SAAU,CAAC;AA8HtD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,qGAW3B,CAAC;AAOX,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,iBAAiB,GAAG,IAAI,CA0B1B;AAmCD,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,MAAM,EAAE,gBAAgB,GACtB,MAAM,GAAG,IAAI,CAsCf;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAEzE"}
@@ -1,3 +1,10 @@
1
+ import { RETIRED_MODEL_REPLACEMENTS } from "../request/helpers/model-map.js";
2
+ /**
3
+ * Largest input still billed at the short-context rate. OpenAI's GPT-6 model
4
+ * pages say "Prompts with more than 272K input tokens are priced at 2x input
5
+ * and cache rates", so exactly 272,000 is short and 272,001 is long.
6
+ */
7
+ export const SHORT_CONTEXT_MAX_INPUT_TOKENS = 272_000;
1
8
  const MODEL_PRICING = {
2
9
  // GPT-6 Astra, published at the 2026-09-03 launch: $10 / 1M input,
3
10
  // $50 / 1M output on the standard service tier, and $20 / $100 on Fast,
@@ -16,48 +23,85 @@ const MODEL_PRICING = {
16
23
  outputUsdPerMillion: 50,
17
24
  cachedInputUsdPerMillion: 1,
18
25
  reasoningUsdPerMillion: 50,
26
+ // Long-context rows from the same page (read 2026-09-23).
27
+ longContext: {
28
+ inputUsdPerMillion: 20,
29
+ outputUsdPerMillion: 75,
30
+ cachedInputUsdPerMillion: 2,
31
+ reasoningUsdPerMillion: 75,
32
+ },
19
33
  serviceTiers: {
20
34
  // Published at launch alongside the standard rate: Fast mode is up to
21
- // 2.5x the speed at 2x the price, $20 / $100 per 1M. This is the only
22
- // tier multiplier OpenAI has published for any model in this table,
23
- // which is why it is the only one listed anywhere in this file.
35
+ // 2.5x the speed at 2x the price, $20 / $100 per 1M. Only the GPT-6
36
+ // rows list a Fast tier, because only GPT-6 has a published Fast rate.
24
37
  priority: {
25
38
  inputUsdPerMillion: 20,
26
39
  outputUsdPerMillion: 100,
27
40
  cachedInputUsdPerMillion: 2,
28
41
  reasoningUsdPerMillion: 100,
42
+ longContext: {
43
+ inputUsdPerMillion: 40,
44
+ outputUsdPerMillion: 150,
45
+ cachedInputUsdPerMillion: 4,
46
+ reasoningUsdPerMillion: 150,
47
+ },
29
48
  },
30
49
  },
31
50
  },
32
- "gpt-5-codex": {
33
- inputUsdPerMillion: 1.25,
34
- outputUsdPerMillion: 10,
35
- cachedInputUsdPerMillion: 0.125,
36
- reasoningUsdPerMillion: 10,
37
- },
38
- "gpt-5.1-codex": {
39
- inputUsdPerMillion: 1.25,
40
- outputUsdPerMillion: 10,
41
- cachedInputUsdPerMillion: 0.125,
42
- reasoningUsdPerMillion: 10,
43
- },
44
- "gpt-5.2": {
45
- inputUsdPerMillion: 1.25,
46
- outputUsdPerMillion: 10,
47
- cachedInputUsdPerMillion: 0.125,
48
- reasoningUsdPerMillion: 10,
49
- },
50
- "gpt-5.3-codex": {
51
- inputUsdPerMillion: 1.25,
51
+ // GPT-6 Sol and Luna, from the OpenAI API pricing page (read 2026-09-23;
52
+ // short- and long-context rows). Both publish a Fast tier at exactly 2x,
53
+ // like Astra.
54
+ "gpt-6-sol": {
55
+ inputUsdPerMillion: 2,
52
56
  outputUsdPerMillion: 10,
53
- cachedInputUsdPerMillion: 0.125,
57
+ cachedInputUsdPerMillion: 0.2,
54
58
  reasoningUsdPerMillion: 10,
59
+ longContext: {
60
+ inputUsdPerMillion: 4,
61
+ outputUsdPerMillion: 15,
62
+ cachedInputUsdPerMillion: 0.4,
63
+ reasoningUsdPerMillion: 15,
64
+ },
65
+ serviceTiers: {
66
+ priority: {
67
+ inputUsdPerMillion: 4,
68
+ outputUsdPerMillion: 20,
69
+ cachedInputUsdPerMillion: 0.4,
70
+ reasoningUsdPerMillion: 20,
71
+ longContext: {
72
+ inputUsdPerMillion: 8,
73
+ outputUsdPerMillion: 30,
74
+ cachedInputUsdPerMillion: 0.8,
75
+ reasoningUsdPerMillion: 30,
76
+ },
77
+ },
78
+ },
55
79
  },
56
- "gpt-5.4": {
57
- inputUsdPerMillion: 2,
58
- outputUsdPerMillion: 12,
59
- cachedInputUsdPerMillion: 0.2,
60
- reasoningUsdPerMillion: 12,
80
+ "gpt-6-luna": {
81
+ inputUsdPerMillion: 0.1,
82
+ outputUsdPerMillion: 0.5,
83
+ cachedInputUsdPerMillion: 0.01,
84
+ reasoningUsdPerMillion: 0.5,
85
+ longContext: {
86
+ inputUsdPerMillion: 0.2,
87
+ outputUsdPerMillion: 0.75,
88
+ cachedInputUsdPerMillion: 0.02,
89
+ reasoningUsdPerMillion: 0.75,
90
+ },
91
+ serviceTiers: {
92
+ priority: {
93
+ inputUsdPerMillion: 0.2,
94
+ outputUsdPerMillion: 1,
95
+ cachedInputUsdPerMillion: 0.02,
96
+ reasoningUsdPerMillion: 1,
97
+ longContext: {
98
+ inputUsdPerMillion: 0.4,
99
+ outputUsdPerMillion: 1.5,
100
+ cachedInputUsdPerMillion: 0.04,
101
+ reasoningUsdPerMillion: 1.5,
102
+ },
103
+ },
104
+ },
61
105
  },
62
106
  "gpt-5.5": {
63
107
  inputUsdPerMillion: 2,
@@ -108,21 +152,14 @@ export const UNPRICED_ROUTABLE_MODELS = [
108
152
  "gpt-6-astra-aeon",
109
153
  "gpt-daybreak-blue-latest",
110
154
  "gpt-daybreak-red-latest",
111
- "gpt-5.1",
112
- "gpt-5.2-pro",
113
- "gpt-5.4-mini",
114
- "gpt-5.4-nano",
115
- "gpt-5.4-pro",
116
155
  "gpt-5.5-pro",
117
- "gpt-5-mini",
118
- "gpt-5-nano",
119
156
  ];
120
157
  function normalizeModelName(model) {
121
158
  const trimmed = model?.trim().toLowerCase();
122
159
  return trimmed && trimmed.length > 0 ? trimmed : null;
123
160
  }
124
161
  export function getUsageModelPricing(model) {
125
- const normalized = normalizeModelName(model);
162
+ const normalized = normalizeModelName(model?.replace(/^(api|zdr)\//, ""));
126
163
  if (!normalized) {
127
164
  return null;
128
165
  }
@@ -134,10 +171,19 @@ export function getUsageModelPricing(model) {
134
171
  // `NaN` instead of `null`. A NaN cost is worse than an unknown one, because
135
172
  // `NaN >= limit` is false, so it silently makes a `maxCostUsd` budget
136
173
  // unenforceable rather than failing closed the way an unpriced model does.
137
- if (!Object.hasOwn(MODEL_PRICING, normalized)) {
174
+ //
175
+ // A retired id is priced as the model it now runs on. The proxy records the
176
+ // raw client model string, so a new `gpt-5-codex` row is really a
177
+ // `gpt-5.6-sol` request; pricing it at the retired model's old rate would
178
+ // under-count a `maxCostUsd` budget. Rows already on disk are unaffected:
179
+ // the ledger stores `costUsd` when a row is written and never re-prices it.
180
+ const effective = !/^(api|zdr)\//.test(model ?? "") && Object.hasOwn(RETIRED_MODEL_REPLACEMENTS, normalized)
181
+ ? RETIRED_MODEL_REPLACEMENTS[normalized]
182
+ : normalized;
183
+ if (!effective || !Object.hasOwn(MODEL_PRICING, effective)) {
138
184
  return null;
139
185
  }
140
- return MODEL_PRICING[normalized] ?? null;
186
+ return MODEL_PRICING[effective] ?? null;
141
187
  }
142
188
  /**
143
189
  * Pick the rate that applies to a response's service tier.
@@ -152,6 +198,17 @@ function resolveServiceTierPricing(pricing, serviceTier) {
152
198
  }
153
199
  return pricing.serviceTiers?.[serviceTier] ?? null;
154
200
  }
201
+ function resolveContextLengthPricing(basePricing, tierPricing, inputTokens) {
202
+ if (inputTokens <= SHORT_CONTEXT_MAX_INPUT_TOKENS) {
203
+ return tierPricing;
204
+ }
205
+ if (tierPricing.longContext) {
206
+ return tierPricing.longContext;
207
+ }
208
+ // The model has long-context rates but not for this tier: unknown, never
209
+ // the cheaper short-context rate.
210
+ return basePricing.longContext ? null : tierPricing;
211
+ }
155
212
  export function estimateUsageCostUsd(model, tokens) {
156
213
  const basePricing = getUsageModelPricing(model);
157
214
  if (!basePricing) {
@@ -161,7 +218,11 @@ export function estimateUsageCostUsd(model, tokens) {
161
218
  // table has no rate for must report unknown cost, not a standard-tier
162
219
  // figure: under-counting is what lets a `maxCostUsd` cap overrun, and
163
220
  // `evaluateBudgetGuard` already knows how to fail closed on `null`.
164
- const pricing = resolveServiceTierPricing(basePricing, tokens.serviceTier);
221
+ const tierPricing = resolveServiceTierPricing(basePricing, tokens.serviceTier);
222
+ if (!tierPricing) {
223
+ return null;
224
+ }
225
+ const pricing = resolveContextLengthPricing(basePricing, tierPricing, tokens.inputTokens);
165
226
  if (!pricing) {
166
227
  return null;
167
228
  }
@@ -30,7 +30,7 @@ export interface UsageStreamScanner {
30
30
  /**
31
31
  * Observe a forwarded response body and recover its token counts.
32
32
  *
33
- * SSE bodies are scanned line by line so only the current partial line is
33
+ * SSE bodies are scanned frame by frame so only the current bounded event is
34
34
  * retained; a full response can be gigabytes of deltas and must never be
35
35
  * buffered. Non-SSE JSON bodies have no incremental structure to exploit, so
36
36
  * they are accumulated up to `MAX_BUFFERED_BYTES` and parsed at the end —
@@ -39,5 +39,7 @@ export interface UsageStreamScanner {
39
39
  */
40
40
  export declare function createUsageStreamScanner(options: {
41
41
  contentType?: string | null;
42
+ /** Observe parsed upstream events without reparsing the stream. */
43
+ onEvent?: (event: unknown) => void;
42
44
  }): UsageStreamScanner;
43
45
  //# sourceMappingURL=usage-extraction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage-extraction.d.ts","sourceRoot":"","sources":["../../../lib/usage/usage-extraction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+BrE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,OAAO,GACZ,gBAAgB,GAAG,IAAI,CA+BzB;AAoCD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAa/E;AAED,MAAM,WAAW,kBAAkB;IAClC,8CAA8C;IAC9C,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAClC,2EAA2E;IAC3E,MAAM,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;CACtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IACjD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,GAAG,kBAAkB,CA6ErB"}
1
+ {"version":3,"file":"usage-extraction.d.ts","sourceRoot":"","sources":["../../../lib/usage/usage-extraction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+BrE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,OAAO,GACZ,gBAAgB,GAAG,IAAI,CA+BzB;AAoCD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAa/E;AAED,MAAM,WAAW,kBAAkB;IAClC,8CAA8C;IAC9C,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAClC,2EAA2E;IAC3E,MAAM,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;CACtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IACjD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC,GAAG,kBAAkB,CAmGrB"}
@@ -124,7 +124,7 @@ export function extractResponsesUsage(payload) {
124
124
  /**
125
125
  * Observe a forwarded response body and recover its token counts.
126
126
  *
127
- * SSE bodies are scanned line by line so only the current partial line is
127
+ * SSE bodies are scanned frame by frame so only the current bounded event is
128
128
  * retained; a full response can be gigabytes of deltas and must never be
129
129
  * buffered. Non-SSE JSON bodies have no incremental structure to exploit, so
130
130
  * they are accumulated up to `MAX_BUFFERED_BYTES` and parsed at the end —
@@ -138,11 +138,20 @@ export function createUsageStreamScanner(options) {
138
138
  let bufferedBytes = 0;
139
139
  let overflowed = false;
140
140
  let latest = null;
141
- const consumeLine = (line) => {
142
- const trimmed = line.trim();
143
- if (!trimmed.startsWith("data:"))
141
+ let eventData = [];
142
+ let eventSize = 0;
143
+ let droppingEvent = false;
144
+ let droppingLine = false;
145
+ const consumeEvent = () => {
146
+ if (droppingEvent) {
147
+ droppingEvent = false;
148
+ eventData = [];
149
+ eventSize = 0;
144
150
  return;
145
- const payload = trimmed.slice("data:".length).trim();
151
+ }
152
+ const payload = eventData.join("\n");
153
+ eventData = [];
154
+ eventSize = 0;
146
155
  if (payload.length === 0 || payload === "[DONE]")
147
156
  return;
148
157
  let parsed;
@@ -152,6 +161,7 @@ export function createUsageStreamScanner(options) {
152
161
  catch {
153
162
  return;
154
163
  }
164
+ options.onEvent?.(parsed);
155
165
  // Prefer terminal events, which carry the authoritative totals, but fall
156
166
  // back to any event that reports usage so a stream that ends on a
157
167
  // non-standard terminal type still records something.
@@ -162,6 +172,24 @@ export function createUsageStreamScanner(options) {
162
172
  latest = counts;
163
173
  }
164
174
  };
175
+ const consumeLine = (raw) => {
176
+ const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
177
+ if (line.length === 0) {
178
+ consumeEvent();
179
+ return;
180
+ }
181
+ if (droppingEvent || !line.startsWith("data:"))
182
+ return;
183
+ const value = line.slice(5).replace(/^ /, "");
184
+ eventSize += value.length + 1;
185
+ if (eventSize > MAX_BUFFERED_BYTES) {
186
+ droppingEvent = true;
187
+ eventData = [];
188
+ eventSize = 0;
189
+ return;
190
+ }
191
+ eventData.push(value);
192
+ };
165
193
  return {
166
194
  push: (chunk) => {
167
195
  try {
@@ -178,14 +206,20 @@ export function createUsageStreamScanner(options) {
178
206
  }
179
207
  let newlineAt = pending.indexOf("\n");
180
208
  while (newlineAt !== -1) {
181
- consumeLine(pending.slice(0, newlineAt));
209
+ if (droppingLine)
210
+ droppingLine = false;
211
+ else
212
+ consumeLine(pending.slice(0, newlineAt));
182
213
  pending = pending.slice(newlineAt + 1);
183
214
  newlineAt = pending.indexOf("\n");
184
215
  }
185
216
  // A single unterminated line this long is not SSE any more; stop
186
217
  // retaining it rather than growing the buffer for the whole body.
187
- if (pending.length > MAX_BUFFERED_BYTES) {
188
- overflowed = true;
218
+ if (pending.length > MAX_BUFFERED_BYTES || droppingLine) {
219
+ droppingEvent = true;
220
+ droppingLine = true;
221
+ eventData = [];
222
+ eventSize = 0;
189
223
  pending = "";
190
224
  }
191
225
  }
@@ -202,13 +236,16 @@ export function createUsageStreamScanner(options) {
202
236
  if (pending.length > 0)
203
237
  consumeLine(pending);
204
238
  pending = "";
239
+ consumeEvent();
205
240
  return latest;
206
241
  }
207
242
  if (pending.trim().length === 0)
208
243
  return latest;
209
244
  const parsed = JSON.parse(pending);
210
245
  pending = "";
211
- return extractResponsesUsage(parsed) ?? latest;
246
+ options.onEvent?.(parsed);
247
+ latest = extractResponsesUsage(parsed) ?? latest;
248
+ return latest;
212
249
  }
213
250
  catch {
214
251
  return latest;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-multi-auth",
3
- "version": "2.15.0",
3
+ "version": "2.17.0",
4
4
  "description": "Codex CLI multi-account OAuth manager with account switching, health checks, runtime rotation, diagnostics, and recovery tools for @openai/codex",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -157,6 +157,7 @@
157
157
  },
158
158
  "devDependencies": {
159
159
  "@types/node": "^20.19.42",
160
+ "@types/ws": "8.18.1",
160
161
  "@typescript-eslint/eslint-plugin": "^8.56.0",
161
162
  "@typescript-eslint/parser": "^8.56.0",
162
163
  "@vitest/coverage-v8": "^4.1.8",
@@ -174,12 +175,13 @@
174
175
  "dependencies": {
175
176
  "@codex-ai/plugin": "file:vendor/codex-ai-plugin",
176
177
  "@codex-ai/sdk": "file:vendor/codex-ai-sdk",
177
- "hono": "4.12.33",
178
+ "hono": "4.13.9",
178
179
  "undici": "6.28.0",
180
+ "ws": "8.21.3",
179
181
  "zod": "4.4.3"
180
182
  },
181
183
  "overrides": {
182
- "hono": "4.12.33",
184
+ "hono": "4.13.9",
183
185
  "flatted": "3.4.2",
184
186
  "minimatch": "10.2.4",
185
187
  "picomatch": "4.0.4",
@@ -192,6 +194,7 @@
192
194
  "minimatch": "9.0.9"
193
195
  },
194
196
  "brace-expansion": "5.0.9",
195
- "postcss": "8.5.25"
197
+ "postcss": "8.5.25",
198
+ "nanoid": "3.3.18"
196
199
  }
197
200
  }
@@ -177,11 +177,16 @@ function createStatusPayload({
177
177
  baseUrl: proxyServer?.baseUrl ?? stateRecord?.baseUrl ?? null,
178
178
  totalRequests: proxyStatus.totalRequests ?? 0,
179
179
  upstreamRequests: proxyStatus.upstreamRequests ?? 0,
180
+ websocketConnections: proxyStatus.websocketConnections ?? 0,
181
+ websocketUpstreamRequests: proxyStatus.websocketUpstreamRequests ?? 0,
182
+ streamQuotaUpdates: proxyStatus.streamQuotaUpdates ?? 0,
183
+ lastStreamQuotaUpdateAt: proxyStatus.lastStreamQuotaUpdateAt ?? null,
180
184
  retries: proxyStatus.retries ?? 0,
181
185
  rotations: proxyStatus.rotations ?? 0,
182
186
  lastAccountIndex,
183
187
  lastAccountLabel,
184
188
  lastAccountId: proxyStatus.lastAccountId ?? null,
189
+ lastRequestedWorkspaceId: proxyStatus.lastRequestedWorkspaceId ?? null,
185
190
  lastAccountUpdatedAt: proxyStatus.lastAccountUpdatedAt ?? null,
186
191
  lastError: error ? (error instanceof Error ? error.message : String(error)) : proxyStatus.lastError ?? null,
187
192
  };
@@ -323,6 +328,8 @@ async function main() {
323
328
  try {
324
329
  const proxyModule = await import("../dist/lib/runtime-rotation-proxy.js");
325
330
  proxyServer = await proxyModule.startRuntimeRotationProxy({
331
+ nativeOpenai: stateRecord?.nativeOpenai === true,
332
+ catalogAccount: stateRecord?.catalogAccount,
326
333
  host,
327
334
  port,
328
335
  clientApiKey,
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, realpathSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
- import { basename, delimiter, dirname, extname, isAbsolute, join, relative } from "node:path";
4
+ import { basename, dirname, extname, isAbsolute, join, posix, relative, win32 } from "node:path";
5
5
  import process from "node:process";
6
6
  import { fileURLToPath } from "node:url";
7
7
 
@@ -55,6 +55,29 @@ function defaultResolvePackageBin(moduleUrl) {
55
55
  }
56
56
  }
57
57
 
58
+ // spawn() without a shell cannot run these on Windows: `.cmd`/`.bat` are
59
+ // EINVAL, `.ps1` is not an executable image, and an extensionless file
60
+ // (npm's `#!/bin/sh` shim) is ENOENT even when it is a real PE image. Going through cmd.exe instead would put every
61
+ // forwarded argument through cmd's quoting rules.
62
+ export function isWindowsShimPath(candidatePath) {
63
+ const extension = win32.extname(candidatePath).toLowerCase();
64
+ return extension === "" || extension === ".cmd" || extension === ".bat" || extension === ".ps1";
65
+ }
66
+
67
+ // npm's Windows shims sit in the prefix dir and run
68
+ // `<prefix>\node_modules\@openai\codex\bin\codex.js`.
69
+ export function resolveWindowsShimPackageEntry(shimPath, existsSyncImpl = existsSync) {
70
+ const entry = win32.join(
71
+ win32.dirname(shimPath),
72
+ "node_modules",
73
+ "@openai",
74
+ "codex",
75
+ "bin",
76
+ "codex.js",
77
+ );
78
+ return existsSyncImpl(entry) ? entry : null;
79
+ }
80
+
58
81
  function resolveWindowsCmdPath(env) {
59
82
  const comSpec = (env.ComSpec ?? env.COMSPEC ?? "").trim();
60
83
  if (comSpec.length > 0) return comSpec;
@@ -67,12 +90,12 @@ function resolveWindowsCmdPath(env) {
67
90
  return "cmd.exe";
68
91
  }
69
92
 
70
- export function splitPathEntries(pathValue) {
93
+ export function splitPathEntries(pathValue, platform = process.platform) {
71
94
  if (typeof pathValue !== "string" || pathValue.trim().length === 0) {
72
95
  return [];
73
96
  }
74
97
  return pathValue
75
- .split(delimiter)
98
+ .split(platform === "win32" ? ";" : ":")
76
99
  .map((entry) => entry.trim())
77
100
  .filter((entry) => entry.length > 0);
78
101
  }
@@ -85,7 +108,12 @@ function resolveCandidateExecutableNames(platform) {
85
108
  if (platform !== "win32") {
86
109
  return [resolvePathExecutableName(platform)];
87
110
  }
88
- return ["codex.exe", "codex"];
111
+ // npm on Windows puts `codex.cmd` plus an extensionless `#!/bin/sh` shim in
112
+ // the prefix dir and the package under `<prefix>\node_modules`. Neither shim
113
+ // can be spawned without a shell (`.cmd` is EINVAL, the sh script ENOENT),
114
+ // so launch the package entry the shims point at before falling back to an
115
+ // extensionless `codex`.
116
+ return ["codex.exe", win32.join("node_modules", "@openai", "codex", "bin", "codex.js"), "codex"];
89
117
  }
90
118
 
91
119
  function resolveCodexExecutableFromPath(
@@ -97,7 +125,7 @@ function resolveCodexExecutableFromPath(
97
125
  ) {
98
126
  for (const entry of pathEntries) {
99
127
  for (const executableName of resolveCandidateExecutableNames(platform)) {
100
- const candidate = join(entry, executableName);
128
+ const candidate = (platform === "win32" ? win32 : posix).join(entry, executableName);
101
129
  if (!existsSyncImpl(candidate)) {
102
130
  continue;
103
131
  }
@@ -134,7 +162,7 @@ function resolveCodexExecutableFromSystemPath(
134
162
  selfScriptPath,
135
163
  realpathSyncImpl,
136
164
  ) {
137
- const pathEntries = splitPathEntries(env.PATH ?? env.Path ?? "");
165
+ const pathEntries = splitPathEntries(env.PATH ?? env.Path ?? "", platform);
138
166
  const fromEnvPath = resolveCodexExecutableFromPath(
139
167
  pathEntries,
140
168
  platform,
@@ -207,8 +235,12 @@ export function resolveRealCodexBin(options = {}) {
207
235
 
208
236
  const override = (env.CODEX_MULTI_AUTH_REAL_CODEX_BIN ?? "").trim();
209
237
  if (override.length > 0) {
210
- if (existsSyncImpl(override)) return createResolvedCodexBin(override);
211
- return null;
238
+ if (!existsSyncImpl(override)) return null;
239
+ if (platform === "win32" && isWindowsShimPath(override)) {
240
+ const entry = resolveWindowsShimPackageEntry(override, existsSyncImpl);
241
+ return entry ? createResolvedCodexBin(entry) : null;
242
+ }
243
+ return createResolvedCodexBin(override);
212
244
  }
213
245
 
214
246
  const resolved = resolvePackageBin(moduleUrl);
@@ -10,6 +10,7 @@ const AUTH_SUBCOMMANDS = new Set([
10
10
  "check",
11
11
  "features",
12
12
  "usage",
13
+ "resets",
13
14
  "verify-flagged",
14
15
  "verify",
15
16
  "forecast",