codex-multi-auth 2.16.0 → 2.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/.codex-plugin/plugin.json +9 -9
  2. package/LICENSE +21 -21
  3. package/README.md +487 -463
  4. package/assets/codex-multi-auth-icon.svg +39 -39
  5. package/assets/readme-hero.svg +31 -31
  6. package/config/README.md +31 -31
  7. package/config/codex-legacy.json +1221 -1221
  8. package/config/codex-modern.json +423 -423
  9. package/config/minimal-codex.json +15 -15
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +8 -3
  12. package/dist/lib/account-policy.d.ts +4 -0
  13. package/dist/lib/account-policy.d.ts.map +1 -1
  14. package/dist/lib/account-policy.js +8 -0
  15. package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
  16. package/dist/lib/accounts/rate-limits.js +2 -0
  17. package/dist/lib/accounts.d.ts +15 -3
  18. package/dist/lib/accounts.d.ts.map +1 -1
  19. package/dist/lib/accounts.js +231 -30
  20. package/dist/lib/api-route-store.d.ts +24 -0
  21. package/dist/lib/api-route-store.d.ts.map +1 -0
  22. package/dist/lib/api-route-store.js +145 -0
  23. package/dist/lib/auth/account-access.d.ts +115 -0
  24. package/dist/lib/auth/account-access.d.ts.map +1 -0
  25. package/dist/lib/auth/account-access.js +214 -0
  26. package/dist/lib/auth/token-utils.d.ts +54 -1
  27. package/dist/lib/auth/token-utils.d.ts.map +1 -1
  28. package/dist/lib/auth/token-utils.js +92 -7
  29. package/dist/lib/cli.d.ts +1 -1
  30. package/dist/lib/cli.d.ts.map +1 -1
  31. package/dist/lib/cli.js +4 -0
  32. package/dist/lib/codex-cli/state.d.ts +7 -0
  33. package/dist/lib/codex-cli/state.d.ts.map +1 -1
  34. package/dist/lib/codex-cli/state.js +18 -0
  35. package/dist/lib/codex-cli/writer.d.ts.map +1 -1
  36. package/dist/lib/codex-cli/writer.js +60 -8
  37. package/dist/lib/codex-manager/account-manager-commands.d.ts.map +1 -1
  38. package/dist/lib/codex-manager/account-manager-commands.js +1 -0
  39. package/dist/lib/codex-manager/account-pool-write.d.ts +3 -1
  40. package/dist/lib/codex-manager/account-pool-write.d.ts.map +1 -1
  41. package/dist/lib/codex-manager/account-pool-write.js +4 -0
  42. package/dist/lib/codex-manager/api-login-menu.d.ts +14 -0
  43. package/dist/lib/codex-manager/api-login-menu.d.ts.map +1 -0
  44. package/dist/lib/codex-manager/api-login-menu.js +224 -0
  45. package/dist/lib/codex-manager/commands/account.d.ts.map +1 -1
  46. package/dist/lib/codex-manager/commands/account.js +27 -0
  47. package/dist/lib/codex-manager/commands/best.d.ts.map +1 -1
  48. package/dist/lib/codex-manager/commands/best.js +6 -2
  49. package/dist/lib/codex-manager/commands/check.d.ts +7 -1
  50. package/dist/lib/codex-manager/commands/check.d.ts.map +1 -1
  51. package/dist/lib/codex-manager/commands/check.js +24 -3
  52. package/dist/lib/codex-manager/commands/forecast.d.ts +1 -1
  53. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
  54. package/dist/lib/codex-manager/commands/forecast.js +7 -3
  55. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
  56. package/dist/lib/codex-manager/commands/report.js +5 -1
  57. package/dist/lib/codex-manager/commands/resets.d.ts +2 -0
  58. package/dist/lib/codex-manager/commands/resets.d.ts.map +1 -0
  59. package/dist/lib/codex-manager/commands/resets.js +67 -0
  60. package/dist/lib/codex-manager/commands/rotation.d.ts +2 -2
  61. package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -1
  62. package/dist/lib/codex-manager/commands/rotation.js +63 -12
  63. package/dist/lib/codex-manager/commands/status.d.ts +12 -1
  64. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
  65. package/dist/lib/codex-manager/commands/status.js +166 -22
  66. package/dist/lib/codex-manager/commands/uninstall.js +1 -1
  67. package/dist/lib/codex-manager/formatters/quota-formatters.d.ts.map +1 -1
  68. package/dist/lib/codex-manager/formatters/quota-formatters.js +3 -2
  69. package/dist/lib/codex-manager/health-check.d.ts +3 -0
  70. package/dist/lib/codex-manager/health-check.d.ts.map +1 -1
  71. package/dist/lib/codex-manager/health-check.js +57 -7
  72. package/dist/lib/codex-manager/help.d.ts.map +1 -1
  73. package/dist/lib/codex-manager/help.js +4 -2
  74. package/dist/lib/codex-manager/login-flow.d.ts.map +1 -1
  75. package/dist/lib/codex-manager/login-flow.js +69 -7
  76. package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
  77. package/dist/lib/codex-manager/login-menu-data.js +21 -8
  78. package/dist/lib/codex-manager/login-oauth.d.ts +3 -0
  79. package/dist/lib/codex-manager/login-oauth.d.ts.map +1 -1
  80. package/dist/lib/codex-manager/login-oauth.js +6 -1
  81. package/dist/lib/codex-manager/login-workspace-choice.d.ts +11 -0
  82. package/dist/lib/codex-manager/login-workspace-choice.d.ts.map +1 -0
  83. package/dist/lib/codex-manager/login-workspace-choice.js +37 -0
  84. package/dist/lib/codex-manager/persist-selected-account.d.ts.map +1 -1
  85. package/dist/lib/codex-manager/persist-selected-account.js +2 -1
  86. package/dist/lib/codex-manager/quota-cache-helpers.d.ts +7 -1
  87. package/dist/lib/codex-manager/quota-cache-helpers.d.ts.map +1 -1
  88. package/dist/lib/codex-manager/quota-cache-helpers.js +34 -19
  89. package/dist/lib/codex-manager/repair-commands.d.ts +13 -0
  90. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
  91. package/dist/lib/codex-manager/repair-commands.js +144 -9
  92. package/dist/lib/codex-manager.d.ts.map +1 -1
  93. package/dist/lib/codex-manager.js +25 -6
  94. package/dist/lib/concurrency.d.ts +3 -0
  95. package/dist/lib/concurrency.d.ts.map +1 -0
  96. package/dist/lib/concurrency.js +31 -0
  97. package/dist/lib/context-budget-response.js +8 -8
  98. package/dist/lib/context-overflow.js +10 -10
  99. package/dist/lib/model-route-policy.d.ts +52 -0
  100. package/dist/lib/model-route-policy.d.ts.map +1 -0
  101. package/dist/lib/model-route-policy.js +159 -0
  102. package/dist/lib/oauth-success.html +125 -125
  103. package/dist/lib/policy/runtime-policy.d.ts +1 -0
  104. package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
  105. package/dist/lib/policy/runtime-policy.js +3 -0
  106. package/dist/lib/preemptive-quota-scheduler.d.ts +2 -0
  107. package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -1
  108. package/dist/lib/preemptive-quota-scheduler.js +4 -0
  109. package/dist/lib/prompts/codex-host-bridge.js +138 -138
  110. package/dist/lib/prompts/codex.js +79 -79
  111. package/dist/lib/quota-cache.d.ts +7 -3
  112. package/dist/lib/quota-cache.d.ts.map +1 -1
  113. package/dist/lib/quota-cache.js +91 -59
  114. package/dist/lib/quota-probe.d.ts +5 -0
  115. package/dist/lib/quota-probe.d.ts.map +1 -1
  116. package/dist/lib/quota-probe.js +26 -2
  117. package/dist/lib/quota-readiness.d.ts +7 -2
  118. package/dist/lib/quota-readiness.d.ts.map +1 -1
  119. package/dist/lib/quota-readiness.js +25 -4
  120. package/dist/lib/request/client-cancellation.d.ts +5 -0
  121. package/dist/lib/request/client-cancellation.d.ts.map +1 -0
  122. package/dist/lib/request/client-cancellation.js +7 -0
  123. package/dist/lib/request/response-outcome.d.ts +25 -0
  124. package/dist/lib/request/response-outcome.d.ts.map +1 -0
  125. package/dist/lib/request/response-outcome.js +49 -0
  126. package/dist/lib/request/stream-failover-runtime.d.ts +2 -1
  127. package/dist/lib/request/stream-failover-runtime.d.ts.map +1 -1
  128. package/dist/lib/request/stream-failover-runtime.js +29 -10
  129. package/dist/lib/runtime/account-check.d.ts.map +1 -1
  130. package/dist/lib/runtime/account-check.js +2 -8
  131. package/dist/lib/runtime/account-model-catalog.d.ts +64 -0
  132. package/dist/lib/runtime/account-model-catalog.d.ts.map +1 -0
  133. package/dist/lib/runtime/account-model-catalog.js +154 -0
  134. package/dist/lib/runtime/account-pool.d.ts +3 -1
  135. package/dist/lib/runtime/account-pool.d.ts.map +1 -1
  136. package/dist/lib/runtime/account-pool.js +4 -0
  137. package/dist/lib/runtime/account-reset-credits.d.ts +42 -0
  138. package/dist/lib/runtime/account-reset-credits.d.ts.map +1 -0
  139. package/dist/lib/runtime/account-reset-credits.js +82 -0
  140. package/dist/lib/runtime/api-model-capabilities.d.ts +30 -0
  141. package/dist/lib/runtime/api-model-capabilities.d.ts.map +1 -0
  142. package/dist/lib/runtime/api-model-capabilities.js +434 -0
  143. package/dist/lib/runtime/api-model-runtime.d.ts +42 -0
  144. package/dist/lib/runtime/api-model-runtime.d.ts.map +1 -0
  145. package/dist/lib/runtime/api-model-runtime.js +358 -0
  146. package/dist/lib/runtime/app-bind.d.ts +19 -1
  147. package/dist/lib/runtime/app-bind.d.ts.map +1 -1
  148. package/dist/lib/runtime/app-bind.js +89 -11
  149. package/dist/lib/runtime/automatic-account-checks.d.ts +35 -0
  150. package/dist/lib/runtime/automatic-account-checks.d.ts.map +1 -0
  151. package/dist/lib/runtime/automatic-account-checks.js +140 -0
  152. package/dist/lib/runtime/automatic-subscription-checks.d.ts +7 -0
  153. package/dist/lib/runtime/automatic-subscription-checks.d.ts.map +1 -0
  154. package/dist/lib/runtime/automatic-subscription-checks.js +54 -0
  155. package/dist/lib/runtime/catalog-capabilities.d.ts +8 -0
  156. package/dist/lib/runtime/catalog-capabilities.d.ts.map +1 -0
  157. package/dist/lib/runtime/catalog-capabilities.js +61 -0
  158. package/dist/lib/runtime/inference-activity.d.ts +15 -0
  159. package/dist/lib/runtime/inference-activity.d.ts.map +1 -0
  160. package/dist/lib/runtime/inference-activity.js +95 -0
  161. package/dist/lib/runtime/model-discovery-status.d.ts +100 -0
  162. package/dist/lib/runtime/model-discovery-status.d.ts.map +1 -0
  163. package/dist/lib/runtime/model-discovery-status.js +387 -0
  164. package/dist/lib/runtime/native-account-storage.d.ts +11 -0
  165. package/dist/lib/runtime/native-account-storage.d.ts.map +1 -0
  166. package/dist/lib/runtime/native-account-storage.js +50 -0
  167. package/dist/lib/runtime/native-account-sync.d.ts +19 -0
  168. package/dist/lib/runtime/native-account-sync.d.ts.map +1 -0
  169. package/dist/lib/runtime/native-account-sync.js +62 -0
  170. package/dist/lib/runtime/native-binding-lock.d.ts +3 -0
  171. package/dist/lib/runtime/native-binding-lock.d.ts.map +1 -0
  172. package/dist/lib/runtime/native-binding-lock.js +5 -0
  173. package/dist/lib/runtime/native-client-auth.d.ts +3 -0
  174. package/dist/lib/runtime/native-client-auth.d.ts.map +1 -0
  175. package/dist/lib/runtime/native-client-auth.js +42 -0
  176. package/dist/lib/runtime/native-provider-config.d.ts +4 -0
  177. package/dist/lib/runtime/native-provider-config.d.ts.map +1 -0
  178. package/dist/lib/runtime/native-provider-config.js +52 -0
  179. package/dist/lib/runtime/native-rate-limits.d.ts +18 -0
  180. package/dist/lib/runtime/native-rate-limits.d.ts.map +1 -0
  181. package/dist/lib/runtime/native-rate-limits.js +135 -0
  182. package/dist/lib/runtime/reset-credit-routing.d.ts +41 -0
  183. package/dist/lib/runtime/reset-credit-routing.d.ts.map +1 -0
  184. package/dist/lib/runtime/reset-credit-routing.js +66 -0
  185. package/dist/lib/runtime/reset-credits.d.ts +101 -0
  186. package/dist/lib/runtime/reset-credits.d.ts.map +1 -0
  187. package/dist/lib/runtime/reset-credits.js +162 -0
  188. package/dist/lib/runtime/response-output-history.d.ts +17 -0
  189. package/dist/lib/runtime/response-output-history.d.ts.map +1 -0
  190. package/dist/lib/runtime/response-output-history.js +85 -0
  191. package/dist/lib/runtime/responses-websocket.d.ts +31 -0
  192. package/dist/lib/runtime/responses-websocket.d.ts.map +1 -0
  193. package/dist/lib/runtime/responses-websocket.js +636 -0
  194. package/dist/lib/runtime/resume-picker.d.ts +41 -0
  195. package/dist/lib/runtime/resume-picker.d.ts.map +1 -0
  196. package/dist/lib/runtime/resume-picker.js +169 -0
  197. package/dist/lib/runtime/rotation-account-selection.d.ts +6 -0
  198. package/dist/lib/runtime/rotation-account-selection.d.ts.map +1 -1
  199. package/dist/lib/runtime/rotation-account-selection.js +63 -0
  200. package/dist/lib/runtime/rotation-proxy-state.d.ts +33 -0
  201. package/dist/lib/runtime/rotation-proxy-state.d.ts.map +1 -1
  202. package/dist/lib/runtime/rotation-server-types.d.ts +18 -0
  203. package/dist/lib/runtime/rotation-server-types.d.ts.map +1 -1
  204. package/dist/lib/runtime/runtime-capability-failures.d.ts +14 -0
  205. package/dist/lib/runtime/runtime-capability-failures.d.ts.map +1 -0
  206. package/dist/lib/runtime/runtime-capability-failures.js +76 -0
  207. package/dist/lib/runtime/runtime-observability.d.ts +7 -0
  208. package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
  209. package/dist/lib/runtime/runtime-observability.js +42 -2
  210. package/dist/lib/runtime/subscription-first-use.d.ts +10 -0
  211. package/dist/lib/runtime/subscription-first-use.d.ts.map +1 -0
  212. package/dist/lib/runtime/subscription-first-use.js +64 -0
  213. package/dist/lib/runtime/subscription-quota-event.d.ts +6 -0
  214. package/dist/lib/runtime/subscription-quota-event.d.ts.map +1 -0
  215. package/dist/lib/runtime/subscription-quota-event.js +31 -0
  216. package/dist/lib/runtime/subscription-quota-order.d.ts +17 -0
  217. package/dist/lib/runtime/subscription-quota-order.d.ts.map +1 -0
  218. package/dist/lib/runtime/subscription-quota-order.js +31 -0
  219. package/dist/lib/runtime/workspace-model-scopes.d.ts +15 -0
  220. package/dist/lib/runtime/workspace-model-scopes.d.ts.map +1 -0
  221. package/dist/lib/runtime/workspace-model-scopes.js +34 -0
  222. package/dist/lib/runtime-constants.d.ts +3 -0
  223. package/dist/lib/runtime-constants.d.ts.map +1 -1
  224. package/dist/lib/runtime-constants.js +5 -0
  225. package/dist/lib/runtime-paths.d.ts.map +1 -1
  226. package/dist/lib/runtime-paths.js +4 -1
  227. package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
  228. package/dist/lib/runtime-rotation-proxy.js +852 -42
  229. package/dist/lib/schemas.d.ts +20 -0
  230. package/dist/lib/schemas.d.ts.map +1 -1
  231. package/dist/lib/schemas.js +3 -0
  232. package/dist/lib/storage/credential-sidecars.d.ts +13 -0
  233. package/dist/lib/storage/credential-sidecars.d.ts.map +1 -0
  234. package/dist/lib/storage/credential-sidecars.js +52 -0
  235. package/dist/lib/storage/file-lock.d.ts +9 -0
  236. package/dist/lib/storage/file-lock.d.ts.map +1 -0
  237. package/dist/lib/storage/file-lock.js +186 -0
  238. package/dist/lib/storage/flagged-storage.d.ts.map +1 -1
  239. package/dist/lib/storage/flagged-storage.js +12 -0
  240. package/dist/lib/storage/path-state.d.ts +2 -0
  241. package/dist/lib/storage/path-state.d.ts.map +1 -1
  242. package/dist/lib/storage/path-state.js +9 -0
  243. package/dist/lib/storage/paths.js +4 -4
  244. package/dist/lib/storage/pending-auth.d.ts +18 -0
  245. package/dist/lib/storage/pending-auth.d.ts.map +1 -0
  246. package/dist/lib/storage/pending-auth.js +155 -0
  247. package/dist/lib/storage/public-types.d.ts +13 -0
  248. package/dist/lib/storage/public-types.d.ts.map +1 -1
  249. package/dist/lib/storage/record-identity.d.ts +16 -0
  250. package/dist/lib/storage/record-identity.d.ts.map +1 -0
  251. package/dist/lib/storage/record-identity.js +16 -0
  252. package/dist/lib/storage/snapshot-merge.d.ts +16 -0
  253. package/dist/lib/storage/snapshot-merge.d.ts.map +1 -0
  254. package/dist/lib/storage/snapshot-merge.js +206 -0
  255. package/dist/lib/storage/transactions.d.ts.map +1 -1
  256. package/dist/lib/storage/transactions.js +5 -4
  257. package/dist/lib/storage.d.ts +6 -0
  258. package/dist/lib/storage.d.ts.map +1 -1
  259. package/dist/lib/storage.js +77 -7
  260. package/dist/lib/ui/auth-menu-builder.d.ts +2 -0
  261. package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
  262. package/dist/lib/ui/auth-menu-builder.js +1 -0
  263. package/dist/lib/ui/auth-menu.d.ts.map +1 -1
  264. package/dist/lib/ui/auth-menu.js +1 -0
  265. package/dist/lib/ui/check-progress.d.ts +8 -0
  266. package/dist/lib/ui/check-progress.d.ts.map +1 -0
  267. package/dist/lib/ui/check-progress.js +30 -0
  268. package/dist/lib/ui/format.d.ts +3 -1
  269. package/dist/lib/ui/format.d.ts.map +1 -1
  270. package/dist/lib/ui/format.js +8 -0
  271. package/dist/lib/ui/ui-copy.d.ts +2 -2
  272. package/dist/lib/ui/ui-copy.js +2 -2
  273. package/dist/lib/usage/pricing.js +2 -2
  274. package/dist/lib/usage/usage-extraction.d.ts +3 -1
  275. package/dist/lib/usage/usage-extraction.d.ts.map +1 -1
  276. package/dist/lib/usage/usage-extraction.js +46 -9
  277. package/node_modules/@codex-ai/plugin/dist/index.d.ts +2 -2
  278. package/node_modules/@codex-ai/plugin/dist/index.js +2 -2
  279. package/node_modules/@codex-ai/plugin/dist/tool.d.ts +42 -42
  280. package/node_modules/@codex-ai/plugin/dist/tool.js +29 -29
  281. package/node_modules/@codex-ai/plugin/package.json +9 -9
  282. package/node_modules/@codex-ai/sdk/dist/index.d.ts +4 -4
  283. package/node_modules/@codex-ai/sdk/dist/index.js +2 -2
  284. package/node_modules/@codex-ai/sdk/package.json +8 -8
  285. package/package.json +200 -197
  286. package/scripts/codex-app-launcher.js +653 -653
  287. package/scripts/codex-app-router.js +373 -366
  288. package/scripts/codex-bin-resolver.js +313 -281
  289. package/scripts/codex-multi-auth.js +59 -59
  290. package/scripts/codex-routing.js +62 -61
  291. package/scripts/codex.js +6642 -6564
  292. package/scripts/install-codex-auth-utils.js +213 -213
  293. package/scripts/mcodex.js +367 -367
  294. package/scripts/postinstall.js +114 -114
  295. package/scripts/preuninstall.js +265 -265
  296. package/vendor/codex-ai-plugin/dist/index.d.ts +2 -2
  297. package/vendor/codex-ai-plugin/dist/index.js +2 -2
  298. package/vendor/codex-ai-plugin/dist/tool.d.ts +42 -42
  299. package/vendor/codex-ai-plugin/dist/tool.js +29 -29
  300. package/vendor/codex-ai-plugin/package.json +9 -9
  301. package/vendor/codex-ai-sdk/dist/index.d.ts +4 -4
  302. package/vendor/codex-ai-sdk/dist/index.js +2 -2
  303. package/vendor/codex-ai-sdk/package.json +8 -8
@@ -0,0 +1,52 @@
1
+ import { tomlStringLiteral } from "./config-toml.js";
2
+ import { NATIVE_PROVIDER_BEGIN as BEGIN, hasNativeProviderConfig, } from "../runtime-constants.js";
3
+ export { hasNativeProviderConfig } from "../runtime-constants.js";
4
+ const END = "# codex-multi-auth native provider end";
5
+ const ROOT_KEYS = /^\s*(model_provider|openai_base_url)\s*=/;
6
+ function withoutNativeBlock(content) {
7
+ const start = content.indexOf(BEGIN);
8
+ if (start < 0)
9
+ return content;
10
+ const end = content.indexOf(END, start);
11
+ if (end < 0)
12
+ throw new Error("Incomplete native provider binding");
13
+ return (content.slice(0, start) +
14
+ content.slice(end + END.length).replace(/^\r?\n/, ""));
15
+ }
16
+ function rootLines(content, select) {
17
+ let root = true;
18
+ return content.split(/\r?\n/).filter((line) => {
19
+ if (/^\s*\[/.test(line))
20
+ root = false;
21
+ const match = root && ROOT_KEYS.test(line);
22
+ return select ? match : !match;
23
+ });
24
+ }
25
+ export function rewriteNativeProviderConfig(content, baseUrl) {
26
+ const url = new URL(baseUrl);
27
+ if (url.protocol !== "http:" ||
28
+ !["127.0.0.1", "[::1]"].includes(url.hostname) ||
29
+ !url.port ||
30
+ url.username ||
31
+ url.password ||
32
+ url.search ||
33
+ url.hash ||
34
+ url.pathname !== "/")
35
+ throw new Error("Native provider requires a loopback proxy origin");
36
+ const lines = rootLines(withoutNativeBlock(content), false);
37
+ return [
38
+ BEGIN,
39
+ 'model_provider = "openai"',
40
+ `openai_base_url = ${tomlStringLiteral(baseUrl)}`,
41
+ END,
42
+ ...lines,
43
+ ].join("\n");
44
+ }
45
+ export function restoreNativeProviderConfig(content, original) {
46
+ if (!hasNativeProviderConfig(content))
47
+ return content;
48
+ return [
49
+ ...rootLines(original, true),
50
+ ...rootLines(withoutNativeBlock(content), false),
51
+ ].join("\n");
52
+ }
@@ -0,0 +1,18 @@
1
+ import type { CodexCliMirror } from '../storage/public-types.js';
2
+ export interface NativeUsageAuth {
3
+ accessToken: string;
4
+ accountId: string;
5
+ expiresAt: number;
6
+ idToken?: string;
7
+ codexCliMirror?: CodexCliMirror;
8
+ }
9
+ type Method = 'account/rateLimits/read' | 'account/rateLimitResetCredit/consume';
10
+ /** One bounded native RPC session in an isolated home. No refresh token, API key,
11
+ * global configuration, or desktop auth changes are available to this process. */
12
+ export declare function nativeRateLimitsRpc(auth: NativeUsageAuth, method: Method, params: Record<string, unknown>, options?: {
13
+ command?: string[];
14
+ tempRoot?: string;
15
+ timeoutMs?: number;
16
+ }): Promise<unknown>;
17
+ export {};
18
+ //# sourceMappingURL=native-rate-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-rate-limits.d.ts","sourceRoot":"","sources":["../../../lib/runtime/native-rate-limits.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAIjE,MAAM,WAAW,eAAe;IAAE,WAAW,EAAC,MAAM,CAAC;IAAA,SAAS,EAAC,MAAM,CAAC;IAAA,SAAS,EAAC,MAAM,CAAC;IAAA,OAAO,CAAC,EAAC,MAAM,CAAC;IAAA,cAAc,CAAC,EAAC,cAAc,CAAA;CAAC;AAUtI,KAAK,MAAM,GAAC,yBAAyB,GAAC,sCAAsC,CAAC;AAU7E;kFACkF;AAClF,wBAAsB,mBAAmB,CAAC,IAAI,EAAC,eAAe,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,CAAC,MAAM,EAAC,OAAO,CAAC,EAAC,OAAO,GAAC;IAAC,OAAO,CAAC,EAAC,MAAM,EAAE,CAAC;IAAA,QAAQ,CAAC,EAAC,MAAM,CAAC;IAAA,SAAS,CAAC,EAAC,MAAM,CAAA;CAAI,GAAE,OAAO,CAAC,OAAO,CAAC,CAyC7L"}
@@ -0,0 +1,135 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, promises as fs } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { isAbsolute, join } from 'node:path';
5
+ import { codexCliAccountIdFor } from '../auth/token-utils.js';
6
+ import { getCodexMultiAuthDir } from '../runtime-paths.js';
7
+ import { isRecord } from '../utils.js';
8
+ /** Longer than any live session (15s timeout), so only crash leftovers are swept. */
9
+ const STALE_HOME_MS = 10 * 60_000;
10
+ async function sweepStaleHomes(root) {
11
+ const entries = await fs.readdir(root).catch(() => []);
12
+ await Promise.all(entries.filter(name => name.startsWith('reset-rpc-')).map(async (name) => {
13
+ const path = join(root, name);
14
+ try {
15
+ if (Date.now() - (await fs.stat(path)).mtimeMs > STALE_HOME_MS)
16
+ await fs.rm(path, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
17
+ }
18
+ catch { /* Another process owns or already removed it. */ }
19
+ }));
20
+ }
21
+ function nativeCommand() {
22
+ const override = process.env.CODEX_MULTI_AUTH_USAGE_CODEX_BIN?.trim();
23
+ if (override) {
24
+ if (!isAbsolute(override) || !existsSync(override))
25
+ throw Error('Native usage executable must be an existing absolute path');
26
+ return [override];
27
+ }
28
+ // Prefer the desktop backend whose protocol supplies the native usage UI.
29
+ const bundled = '/Applications/ChatGPT.app/Contents/Resources/codex';
30
+ if (process.platform === 'darwin' && existsSync(bundled))
31
+ return [bundled];
32
+ try {
33
+ return [process.execPath, createRequire(import.meta.url).resolve('@openai/codex/bin/codex.js')];
34
+ }
35
+ catch {
36
+ throw Error('Native usage backend unavailable; set CODEX_MULTI_AUTH_USAGE_CODEX_BIN to the native Codex executable');
37
+ }
38
+ }
39
+ /** One bounded native RPC session in an isolated home. No refresh token, API key,
40
+ * global configuration, or desktop auth changes are available to this process. */
41
+ export async function nativeRateLimitsRpc(auth, method, params, options = {}) {
42
+ if (!auth.accessToken || !auth.accountId || auth.expiresAt <= Date.now() + 30000)
43
+ throw Error('Native usage requires a fresh account access token');
44
+ const command = options.command ?? nativeCommand();
45
+ const executable = command[0];
46
+ if (!executable)
47
+ throw Error('Native usage backend unavailable');
48
+ // The home briefly holds an access token: keep it in the user-private multi-auth
49
+ // directory (chmod is a no-op on Windows %TEMP%) and sweep leftovers of hard kills.
50
+ const root = options.tempRoot ?? join(getCodexMultiAuthDir(), 'tmp');
51
+ await fs.mkdir(root, { recursive: true, mode: 0o700 });
52
+ await sweepStaleHomes(root);
53
+ const dir = await fs.mkdtemp(join(root, 'reset-rpc-'));
54
+ let stop = async () => { };
55
+ try {
56
+ await fs.chmod(dir, 0o700);
57
+ await fs.writeFile(join(dir, 'auth.json'), JSON.stringify({ auth_mode: 'chatgpt', OPENAI_API_KEY: null, tokens: { access_token: auth.accessToken, id_token: auth.idToken ?? auth.accessToken, refresh_token: '', account_id: codexCliAccountIdFor(auth, auth.accessToken, auth.idToken) }, last_refresh: new Date().toISOString() }), { mode: 0o600 });
58
+ await fs.writeFile(join(dir, 'config.toml'), 'cli_auth_credentials_store = "file"\n', { mode: 0o600 });
59
+ const env = { ...process.env, CODEX_HOME: dir };
60
+ // A native backend must not inherit API authentication or our own proxy route.
61
+ for (const key of ['NODE_OPTIONS', 'OPENAI_API_KEY', 'OPENAI_BASE_URL', 'CODEX_API_KEY'])
62
+ delete env[key];
63
+ const child = spawn(executable, [...command.slice(1), 'app-server'], { env, cwd: dir, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
64
+ const exited = new Promise(resolve => { child.once('exit', () => resolve()); child.once('error', () => resolve()); });
65
+ stop = async () => { if (child.exitCode === null && child.signalCode === null && child.pid) {
66
+ child.kill('SIGTERM');
67
+ const timer = setTimeout(() => { if (child.pid && child.exitCode === null && child.signalCode === null)
68
+ child.kill('SIGKILL'); }, 1000);
69
+ try {
70
+ await exited;
71
+ }
72
+ finally {
73
+ clearTimeout(timer);
74
+ }
75
+ } };
76
+ return await new Promise((resolve, reject) => {
77
+ let buffer = '';
78
+ let phase = 0;
79
+ let done = false;
80
+ const timer = setTimeout(() => fail('timed out'), options.timeoutMs ?? 15000);
81
+ const fail = (reason) => { if (done)
82
+ return; done = true; clearTimeout(timer); reject(Error('Native usage ' + reason)); };
83
+ const send = (value) => child.stdin.write(JSON.stringify(value) + '\n');
84
+ child.on('error', () => fail('backend failed to start'));
85
+ child.on('exit', () => fail('backend exited before replying'));
86
+ child.stdin.on('error', () => fail('backend pipe closed'));
87
+ child.stderr.resume();
88
+ child.stdout.setEncoding('utf8');
89
+ child.stdout.on('data', (data) => {
90
+ if (done)
91
+ return;
92
+ buffer += data;
93
+ if (Buffer.byteLength(buffer) > 1024 * 1024) {
94
+ fail('reply exceeded limit');
95
+ return;
96
+ }
97
+ let newline;
98
+ while ((newline = buffer.indexOf('\n')) >= 0) {
99
+ const line = buffer.slice(0, newline);
100
+ buffer = buffer.slice(newline + 1);
101
+ let reply;
102
+ try {
103
+ reply = JSON.parse(line);
104
+ }
105
+ catch {
106
+ fail('reply was invalid');
107
+ return;
108
+ }
109
+ if (!isRecord(reply) || reply.id !== phase + 1)
110
+ continue;
111
+ if (reply.error) {
112
+ fail('operation rejected by backend');
113
+ return;
114
+ }
115
+ if (phase === 0) {
116
+ phase = 1;
117
+ send({ method: 'initialized' });
118
+ send({ id: 2, method, params });
119
+ }
120
+ else {
121
+ done = true;
122
+ clearTimeout(timer);
123
+ resolve(reply.result);
124
+ return;
125
+ }
126
+ }
127
+ });
128
+ send({ id: 1, method: 'initialize', params: { clientInfo: { name: 'multi-auth-usage', version: '1.0' }, capabilities: { experimentalApi: true } } });
129
+ });
130
+ }
131
+ finally {
132
+ await stop();
133
+ await fs.rm(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
134
+ }
135
+ }
@@ -0,0 +1,41 @@
1
+ import { type AccountManager, type ManagedAccount } from '../accounts.js';
2
+ import type { ModelFamily } from '../prompts/codex.js';
3
+ import type { QuotaCacheEntry } from '../quota-cache.js';
4
+ import { type ResetCreditService } from './reset-credits.js';
5
+ import type { workspaceModelScopes } from './workspace-model-scopes.js';
6
+ type Scope = ReturnType<typeof workspaceModelScopes>[number];
7
+ export declare function recoverResetQuota(options: {
8
+ model: string;
9
+ family?: ModelFamily;
10
+ native: boolean;
11
+ pinned: boolean;
12
+ manager: AccountManager;
13
+ scopes: Map<number, Scope[]>;
14
+ priorityByAccount?: Record<number, number>;
15
+ preferredIndex?: number | null;
16
+ quotaForScope: (account: ManagedAccount, scope: Scope) => QuotaCacheEntry | null | undefined;
17
+ service: Pick<ResetCreditService, 'automatic'> & {
18
+ status: () => Promise<{
19
+ snapshots: Awaited<ReturnType<ResetCreditService['status']>>['snapshots'];
20
+ }>;
21
+ };
22
+ observations: Map<string, QuotaCacheEntry>;
23
+ clearQuotaScheduler: (account: ManagedAccount) => void;
24
+ now: () => number;
25
+ }): Promise<boolean>;
26
+ /** Apply a confirmed earned reset once per workspace/model observation. */
27
+ export declare function applyConfirmedReset(options: {
28
+ account: ManagedAccount;
29
+ scope: Scope;
30
+ model: string;
31
+ family: ModelFamily;
32
+ manager: AccountManager;
33
+ snapshot: Awaited<ReturnType<ResetCreditService['status']>>['snapshots'][string] | undefined;
34
+ lastRedemptionAt: number | undefined;
35
+ previous: QuotaCacheEntry | null | undefined;
36
+ observations: Map<string, QuotaCacheEntry>;
37
+ clearQuotaScheduler: (account: ManagedAccount) => void;
38
+ now: number;
39
+ }): boolean;
40
+ export {};
41
+ //# sourceMappingURL=reset-credit-routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset-credit-routing.d.ts","sourceRoot":"","sources":["../../../lib/runtime/reset-credit-routing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,KAAK,KAAK,GAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC3D,wBAAsB,iBAAiB,CAAC,OAAO,EAAC;IAC/C,KAAK,EAAC,MAAM,CAAC;IAAA,MAAM,CAAC,EAAC,WAAW,CAAC;IAAA,MAAM,EAAC,OAAO,CAAC;IAAA,MAAM,EAAC,OAAO,CAAC;IAAA,OAAO,EAAC,cAAc,CAAC;IACtF,MAAM,EAAC,GAAG,CAAC,MAAM,EAAC,KAAK,EAAE,CAAC,CAAC;IAAA,iBAAiB,CAAC,EAAC,MAAM,CAAC,MAAM,EAAC,MAAM,CAAC,CAAC;IAAA,cAAc,CAAC,EAAC,MAAM,GAAC,IAAI,CAAC;IAAA,aAAa,EAAC,CAAC,OAAO,EAAC,cAAc,EAAC,KAAK,EAAC,KAAK,KAAG,eAAe,GAAC,IAAI,GAAC,SAAS,CAAC;IACnL,OAAO,EAAC,IAAI,CAAC,kBAAkB,EAAC,WAAW,CAAC,GAAG;QAAC,MAAM,EAAC,MAAI,OAAO,CAAC;YAAC,SAAS,EAAC,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;SAAC,CAAC,CAAA;KAAC,CAAC;IAChJ,YAAY,EAAC,GAAG,CAAC,MAAM,EAAC,eAAe,CAAC,CAAC;IAAA,mBAAmB,EAAC,CAAC,OAAO,EAAC,cAAc,KAAG,IAAI,CAAC;IAAA,GAAG,EAAC,MAAI,MAAM,CAAC;CAC3G,GAAE,OAAO,CAAC,OAAO,CAAC,CAiClB;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,OAAO,EAAC;IAAC,OAAO,EAAC,cAAc,CAAC;IAAA,KAAK,EAAC,KAAK,CAAC;IAAA,KAAK,EAAC,MAAM,CAAC;IAAA,MAAM,EAAC,WAAW,CAAC;IAAA,OAAO,EAAC,cAAc,CAAC;IAAA,QAAQ,EAAC,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,GAAC,SAAS,CAAC;IAAA,gBAAgB,EAAC,MAAM,GAAC,SAAS,CAAC;IAAA,QAAQ,EAAC,eAAe,GAAC,IAAI,GAAC,SAAS,CAAC;IAAA,YAAY,EAAC,GAAG,CAAC,MAAM,EAAC,eAAe,CAAC,CAAC;IAAA,mBAAmB,EAAC,CAAC,OAAO,EAAC,cAAc,KAAG,IAAI,CAAC;IAAA,GAAG,EAAC,MAAM,CAAA;CAAC,GAAE,OAAO,CAW1Z"}
@@ -0,0 +1,66 @@
1
+ import { extractAccountId } from '../accounts.js';
2
+ import { parseModelRoute } from '../model-route-policy.js';
3
+ import { subscriptionQuotaPreference } from './subscription-quota-order.js';
4
+ import { resetSnapshotQuota } from './reset-credits.js';
5
+ export async function recoverResetQuota(options) {
6
+ if (!options.native || options.pinned || parseModelRoute(options.model).kind !== 'oauth')
7
+ return false;
8
+ const family = options.family ?? 'codex';
9
+ const candidates = [...options.scopes].flatMap(([index, scopes]) => { const account = options.manager.getAccountByIndex(index); return account && !account.authInvalidatedAt ? scopes.map(scope => ({ account, scope })) : []; });
10
+ if (!candidates.length)
11
+ return false;
12
+ // Any usable capacity (including the reserve), unknown quota, or non-quota
13
+ // failure prevents an automatic credit from being spent.
14
+ if (candidates.some(({ account, scope }) => {
15
+ if (account.enabled === false || account.authInvalidatedAt || !scope.routable)
16
+ return true;
17
+ const skip = options.manager.getAccountRuntimeSkipReason(account.index, family, options.model);
18
+ if (skip && !['rate-limited', 'cooling-down:rate-limit'].includes(skip))
19
+ return true;
20
+ const quota = subscriptionQuotaPreference(options.quotaForScope(account, scope), options.now());
21
+ return !quota.exhausted && !['rate-limited', 'cooling-down:rate-limit'].includes(skip ?? '');
22
+ }))
23
+ return false;
24
+ candidates.sort((a, b) => Number(b.account.index === options.preferredIndex) - Number(a.account.index === options.preferredIndex) || (options.priorityByAccount?.[a.account.index] ?? 1) - (options.priorityByAccount?.[b.account.index] ?? 1));
25
+ const targets = candidates.filter(({ scope }) => !scope.accountId.startsWith("org-")).map(({ scope }) => ({ key: scope.id, accountId: scope.accountId }));
26
+ await options.service.automatic(targets);
27
+ const state = await options.service.status();
28
+ let recovered = false;
29
+ for (const { account, scope } of candidates) {
30
+ const snapshot = state.snapshots[scope.id];
31
+ const previous = options.quotaForScope(account, scope);
32
+ if (!snapshot || snapshot.updatedAt < (previous?.updatedAt ?? 0) || options.now() - snapshot.updatedAt > 60000 || snapshot.updatedAt > options.now())
33
+ continue;
34
+ options.observations.set(JSON.stringify([scope.id, options.model]), resetSnapshotQuota(snapshot));
35
+ if (snapshot.ordinaryUsageAllowed !== true)
36
+ continue;
37
+ // Do not clear sibling workspace/model throttles or authentication failures.
38
+ if ((scope.bound || (account.accountId?.startsWith('org-') && scope.accountId === extractAccountId(account.access))) && account.lastRateLimitReason === 'quota') {
39
+ delete account.rateLimitResetTimes[family];
40
+ delete account.rateLimitResetTimes[`${family}:${options.model}`];
41
+ if (account.cooldownReason === 'rate-limit')
42
+ options.manager.clearAccountCooldown(account);
43
+ options.clearQuotaScheduler(account);
44
+ }
45
+ recovered = true;
46
+ }
47
+ if (recovered)
48
+ options.manager.saveToDiskDebounced();
49
+ return recovered;
50
+ }
51
+ /** Apply a confirmed earned reset once per workspace/model observation. */
52
+ export function applyConfirmedReset(options) {
53
+ const s = options.snapshot;
54
+ if (!s || options.lastRedemptionAt === undefined || s.updatedAt < options.lastRedemptionAt || s.ordinaryUsageAllowed !== true || s.updatedAt <= (options.previous?.updatedAt ?? 0) || options.now - s.updatedAt > 60000 || s.updatedAt > options.now)
55
+ return false;
56
+ options.observations.set(JSON.stringify([options.scope.id, options.model]), resetSnapshotQuota(s));
57
+ if ((options.scope.bound || (options.account.accountId?.startsWith('org-') && options.scope.accountId === extractAccountId(options.account.access))) && options.account.lastRateLimitReason === 'quota') {
58
+ delete options.account.rateLimitResetTimes[options.family];
59
+ delete options.account.rateLimitResetTimes[`${options.family}:${options.model}`];
60
+ if (options.account.cooldownReason === 'rate-limit')
61
+ options.manager.clearAccountCooldown(options.account);
62
+ options.clearQuotaScheduler(options.account);
63
+ options.manager.saveToDiskDebounced();
64
+ }
65
+ return true;
66
+ }
@@ -0,0 +1,101 @@
1
+ import { z } from 'zod';
2
+ import type { QuotaCacheEntry } from '../quota-cache.js';
3
+ export interface ResetTarget {
4
+ key: string;
5
+ accountId: string;
6
+ }
7
+ declare const snapshotSchema: z.ZodObject<{
8
+ updatedAt: z.ZodNumber;
9
+ availableCount: z.ZodNullable<z.ZodNumber>;
10
+ ordinaryUsageAllowed: z.ZodNullable<z.ZodBoolean>;
11
+ planType: z.ZodNullable<z.ZodString>;
12
+ primary: z.ZodObject<{
13
+ usedPercent: z.ZodOptional<z.ZodNumber>;
14
+ resetsAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
15
+ windowDurationMins: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
16
+ }, z.core.$strip>;
17
+ secondary: z.ZodObject<{
18
+ usedPercent: z.ZodOptional<z.ZodNumber>;
19
+ resetsAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
20
+ windowDurationMins: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
21
+ }, z.core.$strip>;
22
+ }, z.core.$strip>;
23
+ export type ResetSnapshot = z.infer<typeof snapshotSchema>;
24
+ declare const stateSchema: z.ZodObject<{
25
+ version: z.ZodLiteral<1>;
26
+ policy: z.ZodDefault<z.ZodEnum<{
27
+ manual: "manual";
28
+ "last-resort": "last-resort";
29
+ }>>;
30
+ snapshots: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
31
+ updatedAt: z.ZodNumber;
32
+ availableCount: z.ZodNullable<z.ZodNumber>;
33
+ ordinaryUsageAllowed: z.ZodNullable<z.ZodBoolean>;
34
+ planType: z.ZodNullable<z.ZodString>;
35
+ primary: z.ZodObject<{
36
+ usedPercent: z.ZodOptional<z.ZodNumber>;
37
+ resetsAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
38
+ windowDurationMins: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
39
+ }, z.core.$strip>;
40
+ secondary: z.ZodObject<{
41
+ usedPercent: z.ZodOptional<z.ZodNumber>;
42
+ resetsAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
43
+ windowDurationMins: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
44
+ }, z.core.$strip>;
45
+ }, z.core.$strip>>>;
46
+ pending: z.ZodOptional<z.ZodObject<{
47
+ key: z.ZodString;
48
+ idempotencyKey: z.ZodString;
49
+ }, z.core.$strip>>;
50
+ lastAutomaticCheckAt: z.ZodOptional<z.ZodNumber>;
51
+ lastRedemptionAt: z.ZodOptional<z.ZodNumber>;
52
+ lastRedemption: z.ZodOptional<z.ZodObject<{
53
+ key: z.ZodString;
54
+ at: z.ZodNumber;
55
+ outcome: z.ZodEnum<{
56
+ reset: "reset";
57
+ nothingToReset: "nothingToReset";
58
+ noCredit: "noCredit";
59
+ alreadyRedeemed: "alreadyRedeemed";
60
+ }>;
61
+ automatic: z.ZodBoolean;
62
+ }, z.core.$strip>>;
63
+ }, z.core.$strip>;
64
+ type State = z.infer<typeof stateSchema>;
65
+ declare const outcomeSchema: z.ZodObject<{
66
+ outcome: z.ZodEnum<{
67
+ reset: "reset";
68
+ nothingToReset: "nothingToReset";
69
+ noCredit: "noCredit";
70
+ alreadyRedeemed: "alreadyRedeemed";
71
+ }>;
72
+ }, z.core.$strip>;
73
+ export type ResetOutcome = z.infer<typeof outcomeSchema>['outcome'];
74
+ export declare function parseResetSnapshot(value: unknown, accountId: string, now: number): ResetSnapshot;
75
+ export declare function resetSnapshotQuota(snapshot: ResetSnapshot): QuotaCacheEntry;
76
+ export declare function isResetSubscription(snapshot: ResetSnapshot): boolean;
77
+ export interface ResetCreditIO {
78
+ read: (target: ResetTarget) => Promise<unknown>;
79
+ consume: (target: ResetTarget, idempotencyKey: string) => Promise<unknown>;
80
+ now?: () => number;
81
+ }
82
+ export declare class ResetCreditService {
83
+ private readonly path;
84
+ private readonly io;
85
+ constructor(path: string, io: ResetCreditIO);
86
+ private now;
87
+ status(): Promise<State>;
88
+ private save;
89
+ setPolicy(policy: State['policy']): Promise<void>;
90
+ private read;
91
+ refresh(targets: ResetTarget[]): Promise<Record<string, ResetSnapshot>>;
92
+ private consumeLocked;
93
+ redeem(target: ResetTarget): Promise<ResetOutcome>;
94
+ automatic(targets: ResetTarget[]): Promise<{
95
+ key: string;
96
+ outcome: ResetOutcome;
97
+ } | null>;
98
+ private automaticLocked;
99
+ }
100
+ export {};
101
+ //# sourceMappingURL=reset-credits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset-credits.d.ts","sourceRoot":"","sources":["../../../lib/runtime/reset-credits.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,MAAM,WAAW,WAAW;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE;AAE/D,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;iBAAiN,CAAC;AACtO,MAAM,MAAM,aAAa,GAAC,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACzD,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAud,CAAC;AACze,KAAK,KAAK,GAAC,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEvC,QAAA,MAAM,aAAa;;;;;;;iBAAoF,CAAC;AACxG,MAAM,MAAM,YAAY,GAAC,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC;AAClE,wBAAgB,kBAAkB,CAAC,KAAK,EAAC,OAAO,EAAC,SAAS,EAAC,MAAM,EAAC,GAAG,EAAC,MAAM,GAAE,aAAa,CAI1F;AACD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAC,aAAa,GAAE,eAAe,CAKzE;AACD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAC,aAAa,GAAE,OAAO,CAElE;AACD,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAC,CAAC,MAAM,EAAC,WAAW,KAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,EAAC,CAAC,MAAM,EAAC,WAAW,EAAC,cAAc,EAAC,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrE,GAAG,CAAC,EAAC,MAAI,MAAM,CAAC;CAChB;AAOD,qBAAa,kBAAkB;IAClB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAQ,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAA/B,IAAI,EAAC,MAAM,EAAkB,EAAE,EAAC,aAAa;IAC1E,OAAO,CAAC,GAAG;IACL,MAAM,IAAG,OAAO,CAAC,KAAK,CAAC;YAIf,IAAI;IASZ,SAAS,CAAC,MAAM,EAAC,KAAK,CAAC,QAAQ,CAAC;YACxB,IAAI;IACZ,OAAO,CAAC,OAAO,EAAC,WAAW,EAAE,GAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAC,aAAa,CAAC,CAAC;YAI5D,aAAa;IAUrB,MAAM,CAAC,MAAM,EAAC,WAAW,GAAE,OAAO,CAAC,YAAY,CAAC;IAQtD,SAAS,CAAC,OAAO,EAAC,WAAW,EAAE,GAAE,OAAO,CAAC;QAAC,GAAG,EAAC,MAAM,CAAC;QAAA,OAAO,EAAC,YAAY,CAAA;KAAC,GAAC,IAAI,CAAC;YAQlE,eAAe;CAiB7B"}
@@ -0,0 +1,162 @@
1
+ import { withRetry } from "../fs-retry.js";
2
+ import { resolve } from "node:path";
3
+ import { randomUUID } from 'node:crypto';
4
+ import { promises as fs } from 'node:fs';
5
+ import { z } from 'zod';
6
+ import { withFileTransactionLock } from '../storage/file-lock.js';
7
+ import { mapWithConcurrency } from '../concurrency.js';
8
+ import { MAX_RATE_LIMIT_DELAY_MS } from '../constants.js';
9
+ const windowSchema = z.object({ usedPercent: z.number().min(0).max(100).optional(), resetsAt: z.number().nonnegative().nullish(), windowDurationMins: z.number().nonnegative().nullish() });
10
+ const snapshotSchema = z.object({ updatedAt: z.number(), availableCount: z.number().int().nonnegative().nullable(), ordinaryUsageAllowed: z.boolean().nullable(), planType: z.string().nullable(), primary: windowSchema, secondary: windowSchema });
11
+ const stateSchema = z.object({ version: z.literal(1), policy: z.enum(['manual', 'last-resort']).default('manual'), snapshots: z.record(z.string(), snapshotSchema).default({}), pending: z.object({ key: z.string(), idempotencyKey: z.string().uuid() }).optional(), lastAutomaticCheckAt: z.number().optional(), lastRedemptionAt: z.number().optional(), lastRedemption: z.object({ key: z.string(), at: z.number(), outcome: z.enum(['reset', 'nothingToReset', 'noCredit', 'alreadyRedeemed']), automatic: z.boolean() }).optional() });
12
+ const readSchema = z.object({ accountId: z.string(), ordinaryUsageAllowed: z.boolean().nullish(), rateLimitResetCredits: z.object({ availableCount: z.number().int().nonnegative() }).nullish(), rateLimits: z.object({ planType: z.string().nullish(), primary: windowSchema.nullish(), secondary: windowSchema.nullish() }) });
13
+ const outcomeSchema = z.object({ outcome: z.enum(['reset', 'nothingToReset', 'noCredit', 'alreadyRedeemed']) });
14
+ export function parseResetSnapshot(value, accountId, now) {
15
+ const parsed = readSchema.parse(value);
16
+ if (parsed.accountId !== accountId)
17
+ throw Error('Reset credit workspace identity mismatch');
18
+ return { updatedAt: now, availableCount: parsed.rateLimitResetCredits?.availableCount ?? null, ordinaryUsageAllowed: parsed.ordinaryUsageAllowed ?? null, planType: parsed.rateLimits.planType ?? null, primary: parsed.rateLimits.primary ?? {}, secondary: parsed.rateLimits.secondary ?? {} };
19
+ }
20
+ export function resetSnapshotQuota(snapshot) {
21
+ // A bogus resetsAt must not read as a window that never resets.
22
+ const resetAtMs = (s) => typeof s === 'number' && Number.isFinite(s) ? Math.min(s * 1000, snapshot.updatedAt + MAX_RATE_LIMIT_DELAY_MS) : undefined;
23
+ const window = (w) => ({ usedPercent: w.usedPercent, resetAtMs: resetAtMs(w.resetsAt), windowMinutes: w.windowDurationMins ?? undefined });
24
+ return { updatedAt: snapshot.updatedAt, status: 200, model: 'usage-read', planType: snapshot.planType ?? undefined, primary: window(snapshot.primary), secondary: window(snapshot.secondary) };
25
+ }
26
+ export function isResetSubscription(snapshot) {
27
+ return ['plus', 'pro', 'prolite', 'team', 'business', 'self_serve_business_prolite', 'enterprise', 'edu', 'edu_plus', 'edu_pro'].includes(snapshot.planType ?? '');
28
+ }
29
+ /** No credentials or credit IDs are persisted. One global redemption lease covers
30
+ * all accounts: concurrent exhausted requests cannot independently spend credits. */
31
+ /** A stored time further ahead than this came from a clock that has since stepped back: treat it as expired. */
32
+ const FUTURE_SKEW_MS = 5 * 60000;
33
+ const within = (at, now, windowMs) => at !== undefined && at <= now + FUTURE_SKEW_MS && now - at < windowMs;
34
+ const automaticChecks = new Map();
35
+ export class ResetCreditService {
36
+ path;
37
+ io;
38
+ constructor(path, io) {
39
+ this.path = path;
40
+ this.io = io;
41
+ }
42
+ now() { return this.io.now?.() ?? Date.now(); }
43
+ async status() {
44
+ try {
45
+ const stat = await fs.lstat(this.path);
46
+ if (!stat.isFile() || stat.isSymbolicLink())
47
+ throw Error('Invalid reset-credit state file');
48
+ return stateSchema.parse(JSON.parse(await fs.readFile(this.path, 'utf8')));
49
+ }
50
+ catch (e) {
51
+ if (e.code === 'ENOENT')
52
+ return { version: 1, policy: 'manual', snapshots: {} };
53
+ throw e;
54
+ }
55
+ }
56
+ async save(state) {
57
+ const path = this.path + '.' + randomUUID() + '.tmp';
58
+ try {
59
+ const file = await fs.open(path, 'wx', 0o600);
60
+ try {
61
+ await file.writeFile(JSON.stringify(stateSchema.parse(state)) + '\n');
62
+ await file.sync();
63
+ }
64
+ finally {
65
+ await file.close();
66
+ }
67
+ await withRetry(() => fs.rename(path, this.path), { maxAttempts: 6, backoffMs: 25 });
68
+ }
69
+ finally {
70
+ await fs.rm(path, { force: true });
71
+ }
72
+ }
73
+ async setPolicy(policy) { await withFileTransactionLock(this.path, async () => { const s = await this.status(); s.policy = policy; await this.save(s); }); }
74
+ async read(target) { return parseResetSnapshot(await this.io.read(target), target.accountId, this.now()); }
75
+ async refresh(targets) {
76
+ const results = await mapWithConcurrency(targets, 3, async (t) => { try {
77
+ return { key: t.key, snapshot: await this.read(t) };
78
+ }
79
+ catch {
80
+ return null;
81
+ } });
82
+ return withFileTransactionLock(this.path, async () => { const state = await this.status(); const updated = {}; const now = this.now(); for (const row of results)
83
+ if (row) {
84
+ const old = state.snapshots[row.key];
85
+ if (!old || old.updatedAt <= row.snapshot.updatedAt || old.updatedAt > now + FUTURE_SKEW_MS)
86
+ state.snapshots[row.key] = row.snapshot;
87
+ updated[row.key] = row.snapshot;
88
+ } await this.save(state); return updated; });
89
+ }
90
+ async consumeLocked(state, target, automatic = false) {
91
+ if (state.pending && state.pending.key !== target.key)
92
+ throw Error('A reset redemption is pending for another account; retry that account explicitly first.');
93
+ state.pending ??= { key: target.key, idempotencyKey: randomUUID() };
94
+ state.lastRedemptionAt = this.now();
95
+ await this.save(state);
96
+ const result = outcomeSchema.parse(await this.io.consume(target, state.pending.idempotencyKey));
97
+ // Keep the same key on *any* ambiguous outcome, including a failed follow-up read.
98
+ state.snapshots[target.key] = await this.read(target);
99
+ state.lastRedemption = { key: target.key, at: this.now(), outcome: result.outcome, automatic };
100
+ delete state.pending;
101
+ await this.save(state);
102
+ return result.outcome;
103
+ }
104
+ async redeem(target) {
105
+ return withFileTransactionLock(this.path, async () => {
106
+ const state = await this.status();
107
+ if (!state.pending && within(state.lastRedemptionAt, this.now(), 10000))
108
+ throw Error('A reset was just redeemed; refresh usage before trying again.');
109
+ if (!state.pending) {
110
+ const snapshot = await this.read(target);
111
+ state.snapshots[target.key] = snapshot;
112
+ await this.save(state);
113
+ if (snapshot.availableCount === null)
114
+ throw Error('Reset-credit availability is unknown');
115
+ if (snapshot.availableCount === 0)
116
+ return 'noCredit';
117
+ }
118
+ return this.consumeLocked(state, target);
119
+ });
120
+ }
121
+ automatic(targets) {
122
+ const key = resolve(this.path), signature = JSON.stringify(targets), pending = automaticChecks.get(key);
123
+ if (pending)
124
+ return pending.targets === signature ? pending.promise : pending.promise.then(() => null);
125
+ if (automaticChecks.size >= 1000)
126
+ return Promise.resolve(null);
127
+ const promise = this.automaticLocked(targets).finally(() => { automaticChecks.delete(key); });
128
+ automaticChecks.set(key, { targets: signature, promise });
129
+ return promise;
130
+ }
131
+ async automaticLocked(targets) {
132
+ if (!targets.length || (await this.status()).policy !== 'last-resort')
133
+ return null;
134
+ return withFileTransactionLock(this.path, async () => {
135
+ const state = await this.status();
136
+ if (state.policy !== 'last-resort' || state.pending || within(state.lastAutomaticCheckAt, this.now(), 60000) || within(state.lastRedemptionAt, this.now(), 300000))
137
+ return null;
138
+ state.lastAutomaticCheckAt = this.now();
139
+ await this.save(state);
140
+ // Missing/failed reads prevent spending. A scheduled reset that has arrived
141
+ // is confirmed by ordinaryUsageAllowed, never inferred from a clock or %.
142
+ const reads = await mapWithConcurrency(targets, 3, async (t) => { try {
143
+ return await this.read(t);
144
+ }
145
+ catch {
146
+ return null;
147
+ } });
148
+ reads.forEach((s, i) => { const t = targets[i]; if (s && t)
149
+ state.snapshots[t.key] = s; });
150
+ await this.save(state);
151
+ if (reads.some(s => !s || !isResetSubscription(s) || s.ordinaryUsageAllowed !== false))
152
+ return null;
153
+ if (reads.some(s => s && [s.primary, s.secondary].some(w => w.usedPercent === 100 && typeof w.resetsAt === 'number' && w.resetsAt * 1000 <= this.now() + 60000)))
154
+ return null;
155
+ const index = reads.findIndex(s => s !== null && (s.availableCount ?? 0) > 0);
156
+ const target = targets[index];
157
+ if (!target)
158
+ return null;
159
+ return { key: target.key, outcome: await this.consumeLocked(state, target, true) };
160
+ }, { waitMs: 1000 });
161
+ }
162
+ }
@@ -0,0 +1,17 @@
1
+ type Item = Record<string, unknown>;
2
+ /** Preserve completed streamed items when the terminal response omits its output. */
3
+ export declare class ResponseOutputHistory {
4
+ private readonly maxBytes;
5
+ private readonly items;
6
+ private bytes;
7
+ private invalid;
8
+ constructor(maxBytes: number);
9
+ observe(event: Item): void;
10
+ finish(output: unknown): Item[] | undefined;
11
+ private invalidate;
12
+ private put;
13
+ }
14
+ /** A replay must carry each tool call before its corresponding result. */
15
+ export declare function hasOrphanToolResult(input: unknown): boolean;
16
+ export {};
17
+ //# sourceMappingURL=response-output-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-output-history.d.ts","sourceRoot":"","sources":["../../../lib/runtime/response-output-history.ts"],"names":[],"mappings":"AAEA,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpC,qFAAqF;AACrF,qBAAa,qBAAqB;IAIrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAHrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqE;IAC3F,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,OAAO,CAAS;gBACK,QAAQ,EAAE,MAAM;IAE7C,OAAO,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAY1B,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE,GAAG,SAAS;IAqB3C,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,GAAG;CAUX;AAED,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAY3D"}