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
@@ -1,9 +1,10 @@
1
- import { existsSync, promises as fs } from "node:fs";
1
+ import { promises as fs } from "node:fs";
2
2
  import { basename, join } from "node:path";
3
3
  import { withRetry } from "./fs-retry.js";
4
4
  import { logWarn } from "./logger.js";
5
5
  import { getCodexMultiAuthDir } from "./runtime-paths.js";
6
6
  import { tempPathFor } from "./temp-path.js";
7
+ import { withFileTransactionLock } from "./storage/file-lock.js";
7
8
  import { isRecord } from "./utils.js";
8
9
  const QUOTA_CACHE_PATH = join(getCodexMultiAuthDir(), "quota-cache.json");
9
10
  const QUOTA_CACHE_LABEL = basename(QUOTA_CACHE_PATH);
@@ -127,29 +128,57 @@ export function getQuotaCachePath() {
127
128
  * will be empty if the on-disk file is absent, malformed, or could not be read.
128
129
  */
129
130
  export async function loadQuotaCache() {
130
- if (!existsSync(QUOTA_CACHE_PATH)) {
131
- return { byAccountId: {}, byEmail: {} };
132
- }
133
131
  try {
134
- const content = await readCacheFileWithRetry(QUOTA_CACHE_PATH);
135
- const parsed = JSON.parse(content);
136
- if (!isRecord(parsed)) {
137
- return { byAccountId: {}, byEmail: {} };
138
- }
139
- if (parsed.version !== 1) {
140
- logWarn(`Quota cache rejected due to version mismatch: ${String(parsed.version)}`);
141
- return { byAccountId: {}, byEmail: {} };
142
- }
143
- return {
144
- byAccountId: normalizeEntryMap(parsed.byAccountId),
145
- byEmail: normalizeEntryMap(parsed.byEmail),
146
- };
132
+ return await readQuotaCache();
147
133
  }
148
134
  catch (error) {
149
135
  logWarn(`Failed to load quota cache from ${QUOTA_CACHE_LABEL}: ${error instanceof Error ? error.message : String(error)}`);
150
136
  return { byAccountId: {}, byEmail: {} };
151
137
  }
152
138
  }
139
+ /** Only a missing file is empty for writers; read failures must never erase concurrent data. */
140
+ async function readQuotaCache() {
141
+ let content;
142
+ try {
143
+ content = await readCacheFileWithRetry(QUOTA_CACHE_PATH);
144
+ }
145
+ catch (error) {
146
+ if (error.code === "ENOENT")
147
+ return { byAccountId: {}, byEmail: {} };
148
+ throw error;
149
+ }
150
+ const parsed = JSON.parse(content);
151
+ if (!isRecord(parsed) || parsed.version !== 1) {
152
+ throw Error("Quota cache rejected due to version mismatch or invalid payload");
153
+ }
154
+ return {
155
+ byAccountId: normalizeEntryMap(parsed.byAccountId),
156
+ byEmail: normalizeEntryMap(parsed.byEmail),
157
+ ...(parsed.byWorkspace ? { byWorkspace: normalizeEntryMap(parsed.byWorkspace) } : {}),
158
+ };
159
+ }
160
+ /** Reapply this run's changed entries, preserving unrelated or newer concurrent observations. */
161
+ function mergeQuotaChanges(current, proposed, baseline) {
162
+ const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
163
+ for (const namespace of ["byAccountId", "byEmail", "byWorkspace"]) {
164
+ const before = baseline[namespace] ?? {}, next = proposed[namespace] ?? {};
165
+ for (const key of new Set([...Object.keys(before), ...Object.keys(next)])) {
166
+ if (same(before[key], next[key]))
167
+ continue;
168
+ const latest = current[namespace]?.[key], update = next[key];
169
+ if (update) {
170
+ if (!latest || update.updatedAt >= latest.updatedAt) {
171
+ current[namespace] ??= {};
172
+ current[namespace][key] = update;
173
+ }
174
+ }
175
+ else if (same(latest, before[key])) {
176
+ delete current[namespace]?.[key];
177
+ }
178
+ }
179
+ }
180
+ return current;
181
+ }
153
182
  /**
154
183
  * Persist the quota cache to the on-disk JSON file used by the multi-auth runtime.
155
184
  *
@@ -157,8 +186,9 @@ export async function loadQuotaCache() {
157
186
  * quota cache path. Failures are logged and do not throw, so callers should handle
158
187
  * eventual consistency or retry as needed.
159
188
  *
160
- * Concurrency: concurrent writers may race and overwrite each other; callers should
161
- * serialize writes if strong consistency is required.
189
+ * Concurrency: reload, apply changes since `baseline`, and atomically write under
190
+ * a cross-process lock. Pass the pre-edit snapshot for read–modify–write callers.
191
+ * Without a baseline, supplied entries are upserts; omitted entries are retained.
162
192
  *
163
193
  * Filesystem notes: Windows path length, permissions, or antivirus locks may cause
164
194
  * write failures; such errors are logged rather than thrown.
@@ -168,54 +198,56 @@ export async function loadQuotaCache() {
168
198
  *
169
199
  * @param data - The quota cache data (byAccountId and byEmail maps) to persist; callers
170
200
  * should pass normalized QuotaCacheData.
201
+ * @param baseline - The unmodified pre-edit snapshot; only changes since it are applied.
171
202
  */
172
- export async function saveQuotaCache(data) {
173
- const payload = {
174
- version: 1,
175
- byAccountId: data.byAccountId,
176
- byEmail: data.byEmail,
177
- };
203
+ export async function saveQuotaCache(data, baseline = { byAccountId: {}, byEmail: {} }) {
204
+ // Snapshot at invocation, not after waiting behind other writers.
205
+ const proposed = structuredClone(data), before = structuredClone(baseline);
178
206
  const writeTask = async () => {
179
207
  try {
180
- const cacheDir = getCodexMultiAuthDir();
181
- // The quota cache lives alongside other at-rest secrets, so keep the
182
- // directory owner-only on POSIX (mode is a no-op on win32 / ACL-based).
183
- await fs.mkdir(cacheDir, { recursive: true, mode: 0o700 });
184
- // mkdir's mode only applies to a freshly-created dir; an upgrade with a
185
- // pre-existing multi-auth dir keeps its old (possibly world-listable)
186
- // perms, so re-assert 0o700 on POSIX. Best-effort: a chmod failure must
187
- // not break the cache write (the 0o600 file below still protects data).
188
- if (process.platform !== "win32") {
189
- try {
190
- await fs.chmod(cacheDir, 0o700);
191
- }
192
- catch {
193
- // Best-effort hardening only.
194
- }
195
- }
196
- const tempPath = tempPathFor(QUOTA_CACHE_PATH);
197
- await fs.writeFile(tempPath, `${JSON.stringify(payload, null, 2)}\n`, {
198
- encoding: "utf8",
199
- mode: 0o600,
200
- });
201
- let renamed = false;
202
- try {
203
- await withRetry(() => fs.rename(tempPath, QUOTA_CACHE_PATH), {
204
- maxAttempts: 5,
205
- backoffMs: (attempt) => 10 * 2 ** (attempt - 1),
206
- });
207
- renamed = true;
208
- }
209
- finally {
210
- if (!renamed) {
208
+ await withFileTransactionLock(QUOTA_CACHE_PATH, async () => {
209
+ const merged = mergeQuotaChanges(await readQuotaCache(), proposed, before);
210
+ const payload = { version: 1, ...merged };
211
+ const cacheDir = getCodexMultiAuthDir();
212
+ // The quota cache lives alongside other at-rest secrets, so keep the
213
+ // directory owner-only on POSIX (mode is a no-op on win32 / ACL-based).
214
+ await fs.mkdir(cacheDir, { recursive: true, mode: 0o700 });
215
+ // mkdir's mode only applies to a freshly-created dir; an upgrade with a
216
+ // pre-existing multi-auth dir keeps its old (possibly world-listable)
217
+ // perms, so re-assert 0o700 on POSIX. Best-effort: a chmod failure must
218
+ // not break the cache write (the 0o600 file below still protects data).
219
+ if (process.platform !== "win32") {
211
220
  try {
212
- await fs.unlink(tempPath);
221
+ await fs.chmod(cacheDir, 0o700);
213
222
  }
214
223
  catch {
215
- // Best effort temp cleanup.
224
+ // Best-effort hardening only.
216
225
  }
217
226
  }
218
- }
227
+ const tempPath = tempPathFor(QUOTA_CACHE_PATH);
228
+ await fs.writeFile(tempPath, `${JSON.stringify(payload, null, 2)}\n`, {
229
+ encoding: "utf8",
230
+ mode: 0o600,
231
+ });
232
+ let renamed = false;
233
+ try {
234
+ await withRetry(() => fs.rename(tempPath, QUOTA_CACHE_PATH), {
235
+ maxAttempts: 5,
236
+ backoffMs: (attempt) => 10 * 2 ** (attempt - 1),
237
+ });
238
+ renamed = true;
239
+ }
240
+ finally {
241
+ if (!renamed) {
242
+ try {
243
+ await fs.unlink(tempPath);
244
+ }
245
+ catch {
246
+ // Best effort temp cleanup.
247
+ }
248
+ }
249
+ }
250
+ });
219
251
  }
220
252
  catch (error) {
221
253
  logWarn(`Failed to save quota cache to ${QUOTA_CACHE_LABEL}: ${error instanceof Error ? error.message : String(error)}`);
@@ -1,3 +1,4 @@
1
+ import { FirstUseProbeError } from "./runtime/subscription-first-use.js";
1
2
  /**
2
3
  * Human-readable note shown when a live probe fails solely because the account
3
4
  * lacks Codex entitlement (see {@link CodexUnavailableError}). Centralized so
@@ -25,6 +26,8 @@ export interface CodexQuotaWindow {
25
26
  resetAtMs?: number;
26
27
  }
27
28
  export interface CodexQuotaSnapshot {
29
+ primingCompleted?: boolean;
30
+ primingFailure?: FirstUseProbeError["reason"];
28
31
  status: number;
29
32
  planType?: string;
30
33
  activeLimit?: number;
@@ -57,6 +60,8 @@ export declare function formatQuotaResetAt(resetAtMs: number | undefined, nowMs?
57
60
  */
58
61
  export declare function formatQuotaSnapshotLine(snapshot: CodexQuotaSnapshot): string;
59
62
  export interface ProbeCodexQuotaOptions {
63
+ signal?: AbortSignal;
64
+ primeUnusedSubscription?: boolean;
60
65
  accountId: string;
61
66
  accessToken: string;
62
67
  model?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"quota-probe.d.ts","sourceRoot":"","sources":["../../lib/quota-probe.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,yCAAyC,CAAC;AAEnF;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,OAAO,EACd,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GACrC,MAAM,CAMR;AAED,MAAM,WAAW,gBAAgB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd;AA0MD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,KAAK,SAAa,GAChB,MAAM,GAAG,SAAS,CAqBpB;AAwBD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAa5E;AAED,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,sBAAsB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CA4G7B"}
1
+ {"version":3,"file":"quota-probe.d.ts","sourceRoot":"","sources":["../../lib/quota-probe.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,kBAAkB,EAAyD,MAAM,qCAAqC,CAAC;AAEhI;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,yCAAyC,CAAC;AAEnF;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,OAAO,EACd,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GACrC,MAAM,CAMR;AAED,MAAM,WAAW,gBAAgB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd;AAiND;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,KAAK,SAAa,GAChB,MAAM,GAAG,SAAS,CAqBpB;AAwBD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAa5E;AAED,MAAM,WAAW,sBAAsB;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,sBAAsB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CA0H7B"}
@@ -1,10 +1,11 @@
1
- import { CODEX_BASE_URL } from "./constants.js";
1
+ import { CODEX_BASE_URL, MAX_RATE_LIMIT_DELAY_MS } from "./constants.js";
2
2
  import { createCodexHeaders, getUnsupportedCodexModelInfo } from "./request/fetch-helpers.js";
3
3
  import { QUOTA_PROBE_MODEL_CHAIN, resolveProbeReasoningEffort, } from "./request/helpers/model-map.js";
4
4
  import { CodexUnavailableError, isCodexUnavailableError } from "./errors.js";
5
5
  import { getCodexInstructions } from "./prompts/codex.js";
6
6
  import { mutateRuntimeObservabilitySnapshot } from "./runtime/runtime-observability.js";
7
7
  import { isRecord } from "./utils.js";
8
+ import { FirstUseProbeError, finishSubscriptionFirstUse, needsSubscriptionFirstUse } from "./runtime/subscription-first-use.js";
8
9
  /**
9
10
  * Human-readable note shown when a live probe fails solely because the account
10
11
  * lacks Codex entitlement (see {@link CodexUnavailableError}). Centralized so
@@ -79,6 +80,12 @@ function parseFiniteIntHeader(headers, name) {
79
80
  * Security: does not emit or persist header values; callers must redact any sensitive tokens before storing or logging headers.
80
81
  */
81
82
  function parseResetAtMs(headers, prefix) {
83
+ const raw = parseUnboundedResetAtMs(headers, prefix);
84
+ // A bogus header (finite, but overflowing once scaled to ms) must not read as
85
+ // a window that never resets.
86
+ return raw === undefined || Number.isNaN(raw) ? undefined : Math.min(raw, Date.now() + MAX_RATE_LIMIT_DELAY_MS);
87
+ }
88
+ function parseUnboundedResetAtMs(headers, prefix) {
82
89
  const resetAfterSeconds = parseFiniteIntHeader(headers, `${prefix}-reset-after-seconds`);
83
90
  if (typeof resetAfterSeconds === "number" && resetAfterSeconds > 0) {
84
91
  return Date.now() + resetAfterSeconds * 1000;
@@ -324,9 +331,11 @@ export async function fetchCodexQuotaSnapshot(options) {
324
331
  let sawUnsupportedModel = false;
325
332
  let sawOtherFailure = false;
326
333
  for (const model of models) {
334
+ options.signal?.throwIfAborted();
327
335
  attemptedAnyModel = true;
328
336
  try {
329
337
  const instructions = await getCodexInstructions(model);
338
+ options.signal?.throwIfAborted();
330
339
  const probeBody = {
331
340
  model,
332
341
  stream: true,
@@ -362,7 +371,7 @@ export async function fetchCodexQuotaSnapshot(options) {
362
371
  method: "POST",
363
372
  headers,
364
373
  body: JSON.stringify(probeBody),
365
- signal: controller.signal,
374
+ signal: options.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal,
366
375
  });
367
376
  }
368
377
  finally {
@@ -370,6 +379,18 @@ export async function fetchCodexQuotaSnapshot(options) {
370
379
  }
371
380
  const snapshotBase = parseQuotaSnapshotBase(response.headers, response.status);
372
381
  if (snapshotBase) {
382
+ if (options.primeUnusedSubscription && needsSubscriptionFirstUse(snapshotBase, Date.now())) {
383
+ try {
384
+ await finishSubscriptionFirstUse(response, timeoutMs);
385
+ return { ...snapshotBase, model, primingCompleted: true };
386
+ }
387
+ catch (error) {
388
+ // Keep valid quota data without retrying an accepted inference.
389
+ if (error instanceof FirstUseProbeError)
390
+ return { ...snapshotBase, model, primingFailure: error.reason };
391
+ throw error;
392
+ }
393
+ }
373
394
  try {
374
395
  await response.body?.cancel();
375
396
  }
@@ -406,6 +427,9 @@ export async function fetchCodexQuotaSnapshot(options) {
406
427
  lastError = new Error("Codex response did not include quota headers");
407
428
  }
408
429
  catch (error) {
430
+ options.signal?.throwIfAborted();
431
+ if (error instanceof FirstUseProbeError)
432
+ throw error;
409
433
  sawOtherFailure = true;
410
434
  lastError = error instanceof Error ? error : new Error(String(error));
411
435
  }
@@ -1,6 +1,6 @@
1
1
  import type { QuotaCacheData, QuotaCacheEntry } from "./quota-cache.js";
2
2
  import type { AccountMetadataV3 } from "./storage.js";
3
- export type QuotaCacheAccountRef = Pick<AccountMetadataV3, "accountId" | "email">;
3
+ export type QuotaCacheAccountRef = Pick<AccountMetadataV3, "accountId" | "email" | "recordId" | "workspaces" | "currentWorkspaceIndex"> & Partial<Pick<AccountMetadataV3, "refreshToken" | "addedAt">>;
4
4
  export declare function normalizeQuotaAccountId(value: string | undefined): string | null;
5
5
  export declare function normalizeQuotaEmail(value: string | undefined): string | null;
6
6
  export declare function hasUniqueQuotaAccountId(accounts: readonly QuotaCacheAccountRef[], account: QuotaCacheAccountRef): boolean;
@@ -15,5 +15,10 @@ export declare function quotaUsedPercentIsExhausted(usedPercent: number | undefi
15
15
  export declare function isQuotaCacheEntryExhausted(entry: Pick<QuotaCacheEntry, "primary" | "secondary"> & {
16
16
  updatedAt?: number;
17
17
  } | null | undefined, now?: number): boolean;
18
- export declare function findQuotaCacheEntryForAccount(cache: QuotaCacheData | null | undefined, account: QuotaCacheAccountRef, accounts: readonly QuotaCacheAccountRef[], emailFallbackState?: ReadonlyMap<string, QuotaEmailFallbackState>): QuotaCacheEntry | null;
18
+ /** Cache identity includes the credential record, so shared organization IDs cannot collide. */
19
+ export declare function quotaWorkspaceKey(account: QuotaCacheAccountRef, workspaceId: string): string | undefined;
20
+ /** Display the selected workspace; callers routing another scope must pass that exact workspace. */
21
+ export declare function quotaWorkspaceId(account: QuotaCacheAccountRef): string | undefined;
22
+ /** Read exact-workspace quota, using legacy binding entries only for that binding. */
23
+ export declare function findQuotaCacheEntryForAccount(cache: QuotaCacheData | null | undefined, account: QuotaCacheAccountRef, accounts: readonly QuotaCacheAccountRef[], emailFallbackState?: ReadonlyMap<string, QuotaEmailFallbackState>, workspaceId?: string | undefined): QuotaCacheEntry | null;
19
24
  //# sourceMappingURL=quota-readiness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"quota-readiness.d.ts","sourceRoot":"","sources":["../../lib/quota-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAoB,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC;AAIlF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGhF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAG5E;AAED,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAQT;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAChC,CAAC;AAEF,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,SAAS,oBAAoB,EAAE,GACvC,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAiB9C;AAED,wBAAgB,yBAAyB,CACxC,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAChE,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAMT;AAED,wBAAgB,wBAAwB,CACvC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,MAAM,GAAG,SAAS,CAKpB;AAQD,wBAAgB,2BAA2B,CAC1C,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAMT;AA2BD,wBAAgB,0BAA0B,CACzC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,WAAW,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACjG,GAAG,SAAa,GACd,OAAO,CAQT;AAED,wBAAgB,6BAA6B,CAC5C,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,EACxC,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,kBAAkB,+CAAyC,GACzD,eAAe,GAAG,IAAI,CAWxB"}
1
+ {"version":3,"file":"quota-readiness.d.ts","sourceRoot":"","sources":["../../lib/quota-readiness.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAoB,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,YAAY,GAAG,uBAAuB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC;AAIvM,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGhF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAG5E;AAED,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAQT;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAChC,CAAC;AAEF,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,SAAS,oBAAoB,EAAE,GACvC,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAiB9C;AAED,wBAAgB,yBAAyB,CACxC,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAChE,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAMT;AAED,wBAAgB,wBAAwB,CACvC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,MAAM,GAAG,SAAS,CAKpB;AAQD,wBAAgB,2BAA2B,CAC1C,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAMT;AA2BD,wBAAgB,0BAA0B,CACzC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,WAAW,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACjG,GAAG,SAAa,GACd,OAAO,CAQT;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIxG;AAED,oGAAoG;AACpG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAIlF;AAED,sFAAsF;AACtF,wBAAgB,6BAA6B,CAC5C,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,EACxC,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,kBAAkB,+CAAyC,EAC3D,WAAW,qBAA4B,GACrC,eAAe,GAAG,IAAI,CAgBxB"}
@@ -1,3 +1,5 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolveAccountRecordId } from "./storage/record-identity.js";
1
3
  export function normalizeQuotaAccountId(value) {
2
4
  const trimmed = value?.trim();
3
5
  return trimmed && trimmed.length > 0 ? trimmed : null;
@@ -89,16 +91,35 @@ export function isQuotaCacheEntryExhausted(entry, now = Date.now()) {
89
91
  return (quotaWindowIsExhausted(entry?.primary, now, updatedAt) ||
90
92
  quotaWindowIsExhausted(entry?.secondary, now, updatedAt));
91
93
  }
92
- export function findQuotaCacheEntryForAccount(cache, account, accounts, emailFallbackState = buildQuotaEmailFallbackState(accounts)) {
94
+ /** Cache identity includes the credential record, so shared organization IDs cannot collide. */
95
+ export function quotaWorkspaceKey(account, workspaceId) {
96
+ const recordId = account.recordId?.trim() || (account.refreshToken !== undefined && account.addedAt !== undefined
97
+ ? resolveAccountRecordId({ ...account, refreshToken: account.refreshToken, addedAt: account.addedAt }) : undefined);
98
+ return recordId ? createHash("sha256").update(JSON.stringify(["oauth", recordId, workspaceId.trim()])).digest("hex") : undefined;
99
+ }
100
+ /** Display the selected workspace; callers routing another scope must pass that exact workspace. */
101
+ export function quotaWorkspaceId(account) {
102
+ return account.workspaces?.length
103
+ ? account.workspaces[account.currentWorkspaceIndex ?? 0]?.id.trim()
104
+ : account.accountId?.trim();
105
+ }
106
+ /** Read exact-workspace quota, using legacy binding entries only for that binding. */
107
+ export function findQuotaCacheEntryForAccount(cache, account, accounts, emailFallbackState = buildQuotaEmailFallbackState(accounts), workspaceId = quotaWorkspaceId(account)) {
93
108
  if (!cache)
94
109
  return null;
110
+ const key = workspaceId && quotaWorkspaceKey(account, workspaceId);
111
+ const scoped = key ? cache.byWorkspace?.[key] : undefined;
112
+ // Legacy entries describe only the stored binding, never a sibling workspace.
113
+ if (normalizeQuotaAccountId(workspaceId) !== normalizeQuotaAccountId(account.accountId))
114
+ return scoped ?? null;
95
115
  const accountId = normalizeQuotaAccountId(account.accountId);
116
+ const newer = (legacy) => scoped && (!legacy || scoped.updatedAt >= legacy.updatedAt) ? scoped : legacy ?? null;
96
117
  if (accountId && hasUniqueQuotaAccountId(accounts, account) && cache.byAccountId[accountId]) {
97
- return cache.byAccountId[accountId] ?? null;
118
+ return newer(cache.byAccountId[accountId]);
98
119
  }
99
120
  const email = normalizeQuotaEmail(account.email);
100
121
  if (email && hasSafeQuotaEmailFallback(emailFallbackState, account) && cache.byEmail[email]) {
101
- return cache.byEmail[email] ?? null;
122
+ return newer(cache.byEmail[email]);
102
123
  }
103
- return null;
124
+ return scoped ?? null;
104
125
  }
@@ -0,0 +1,5 @@
1
+ /** Internal transport marker: upstream timeouts may also use AbortError. */
2
+ export declare class ClientCancellationError extends Error {
3
+ constructor();
4
+ }
5
+ //# sourceMappingURL=client-cancellation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-cancellation.d.ts","sourceRoot":"","sources":["../../../lib/request/client-cancellation.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,qBAAa,uBAAwB,SAAQ,KAAK;;CAKjD"}
@@ -0,0 +1,7 @@
1
+ /** Internal transport marker: upstream timeouts may also use AbortError. */
2
+ export class ClientCancellationError extends Error {
3
+ constructor() {
4
+ super("Client cancelled request");
5
+ this.name = "AbortError";
6
+ }
7
+ }
@@ -0,0 +1,25 @@
1
+ export interface StreamCompletion {
2
+ success: boolean;
3
+ errorCode: string | null;
4
+ missingTerminal: boolean;
5
+ }
6
+ /** Protocol completion, separate from socket delivery. Never retain output or error messages. */
7
+ export declare class ResponseOutcome {
8
+ private readonly requireTerminal;
9
+ private terminal;
10
+ rejection: {
11
+ error: {
12
+ code?: string;
13
+ param?: string;
14
+ };
15
+ } | undefined;
16
+ constructor(requireTerminal: boolean);
17
+ observe(value: unknown): void;
18
+ /**
19
+ * `response.incomplete` (e.g. max_output_tokens) is a delivered response, not an
20
+ * account failure: it counts as success for health and affinity, and its terminal
21
+ * type survives only as the usage annotation in `errorCode`.
22
+ */
23
+ finish(): StreamCompletion;
24
+ }
25
+ //# sourceMappingURL=response-outcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-outcome.d.ts","sourceRoot":"","sources":["../../../lib/request/response-outcome.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;CACzB;AAED,iGAAiG;AACjG,qBAAa,eAAe;IAIf,OAAO,CAAC,QAAQ,CAAC,eAAe;IAH5C,OAAO,CAAC,QAAQ,CAAkE;IAClF,SAAS,EAAE;QAAE,KAAK,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,SAAS,CAAC;gBAEvC,eAAe,EAAE,OAAO;IAErD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAuB7B;;;;OAIG;IACH,MAAM,IAAI,gBAAgB;CAS1B"}
@@ -0,0 +1,49 @@
1
+ import { isRecord } from "../utils.js";
2
+ /** Protocol completion, separate from socket delivery. Never retain output or error messages. */
3
+ export class ResponseOutcome {
4
+ requireTerminal;
5
+ terminal;
6
+ rejection;
7
+ constructor(requireTerminal) {
8
+ this.requireTerminal = requireTerminal;
9
+ }
10
+ observe(value) {
11
+ if (!isRecord(value))
12
+ return;
13
+ let type = value.type ?? (value.object === "response" && typeof value.status === "string" ? `response.${value.status}` : undefined);
14
+ // SSE `response.done` carries its outcome in the nested status.
15
+ if (type === "response.done") {
16
+ const status = isRecord(value.response) ? value.response.status : undefined;
17
+ type = typeof status === "string" ? `response.${status}` : "response.completed";
18
+ }
19
+ if (type === "response.completed") {
20
+ if (!this.terminal)
21
+ this.terminal = "completed";
22
+ return;
23
+ }
24
+ const failure = type === "response.failed" || type === "error";
25
+ if (!failure && type !== "response.incomplete" && type !== "response.cancelled")
26
+ return;
27
+ this.terminal = failure ? "failed" : type === "response.incomplete" ? "incomplete" : "cancelled";
28
+ const response = isRecord(value.response) ? value.response : value;
29
+ const error = isRecord(response.error) ? response.error : type === "error" ? value : undefined;
30
+ if (error) {
31
+ const safe = (v) => typeof v === "string" && /^[A-Za-z0-9._-]{1,100}$/.test(v) ? v : undefined;
32
+ this.rejection = { error: { code: safe(error.code), param: safe(error.param) } };
33
+ }
34
+ }
35
+ /**
36
+ * `response.incomplete` (e.g. max_output_tokens) is a delivered response, not an
37
+ * account failure: it counts as success for health and affinity, and its terminal
38
+ * type survives only as the usage annotation in `errorCode`.
39
+ */
40
+ finish() {
41
+ const missingTerminal = this.requireTerminal && !this.terminal;
42
+ const success = !missingTerminal && (!this.terminal || this.terminal === "completed" || this.terminal === "incomplete");
43
+ return {
44
+ success,
45
+ missingTerminal,
46
+ errorCode: missingTerminal ? "upstream_missing_terminal" : this.terminal && this.terminal !== "completed" ? `upstream_response_${this.terminal}` : null,
47
+ };
48
+ }
49
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ServerResponse } from "node:http";
2
2
  import type { RuntimeRotationProxyStatus } from "../runtime/rotation-server-types.js";
3
+ import type { StreamCompletion } from "./response-outcome.js";
3
4
  export declare const HOP_BY_HOP_HEADERS: Set<string>;
4
5
  export declare function responseHeadersForClient(upstreamHeaders: Headers): Record<string, string>;
5
6
  export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, onTimeout: () => void, message: string): Promise<T>;
@@ -13,5 +14,5 @@ export declare function forwardStreamingResponse(upstream: Response, res: Server
13
14
  */
14
15
  onChunk?: (chunk: Uint8Array) => void,
15
16
  /** Additional response headers, applied after (so they can override) the forwarded upstream ones. */
16
- extraHeaders?: Record<string, string>): Promise<boolean>;
17
+ extraHeaders?: Record<string, string>, completion?: () => StreamCompletion): Promise<boolean>;
17
18
  //# sourceMappingURL=stream-failover-runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream-failover-runtime.d.ts","sourceRoot":"","sources":["../../../lib/request/stream-failover-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEtF,eAAO,MAAM,kBAAkB,aAW7B,CAAC;AAWH,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUzF;AAED,wBAAsB,WAAW,CAAC,CAAC,EAClC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,IAAI,EACrB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,CAAC,CAmBZ;AAED,wBAAsB,aAAa,CAClC,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,SAAc,GACpB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAoBD,wBAAsB,wBAAwB,CAC7C,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,0BAA0B,EAClC,aAAa,EAAE,MAAM,IAAI,EACzB,oBAAoB,EAAE,MAAM;AAC5B;;;;;GAKG;AACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI;AACrC,qGAAqG;AACrG,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,OAAO,CAAC,OAAO,CAAC,CAsDlB"}
1
+ {"version":3,"file":"stream-failover-runtime.d.ts","sourceRoot":"","sources":["../../../lib/request/stream-failover-runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,eAAO,MAAM,kBAAkB,aAW7B,CAAC;AAWH,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUzF;AAED,wBAAsB,WAAW,CAAC,CAAC,EAClC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,IAAI,EACrB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,CAAC,CAmBZ;AAED,wBAAsB,aAAa,CAClC,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,SAAc,GACpB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAoBD,wBAAsB,wBAAwB,CAC7C,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,0BAA0B,EAClC,aAAa,EAAE,MAAM,IAAI,EACzB,oBAAoB,EAAE,MAAM;AAC5B;;;;;GAKG;AACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI;AACrC,qGAAqG;AACrG,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,UAAU,CAAC,EAAE,MAAM,gBAAgB,GACjC,OAAO,CAAC,OAAO,CAAC,CAmElB"}
@@ -1,3 +1,4 @@
1
+ import { ClientCancellationError } from "./client-cancellation.js";
1
2
  export const HOP_BY_HOP_HEADERS = new Set([
2
3
  "connection",
3
4
  "content-length",
@@ -137,19 +138,34 @@ export async function forwardStreamingResponse(upstream, res, status, onStreamEr
137
138
  */
138
139
  onChunk,
139
140
  /** Additional response headers, applied after (so they can override) the forwarded upstream ones. */
140
- extraHeaders) {
141
+ extraHeaders, completion) {
141
142
  status.streamsStarted += 1;
142
143
  res.writeHead(upstream.status, {
143
144
  ...responseHeadersForClient(upstream.headers),
144
145
  ...extraHeaders,
145
146
  });
146
- if (!upstream.body) {
147
+ let clientDisconnected = false;
148
+ const finish = () => {
149
+ if (clientDisconnected || res.destroyed)
150
+ return false;
151
+ const result = completion?.();
152
+ // Already ended by a concurrent close path: never write after end.
153
+ if (res.writableEnded)
154
+ return false;
155
+ if (result && !result.success) {
156
+ status.lastError = result.errorCode;
157
+ if (result.missingTerminal)
158
+ res.write(Buffer.from(`data: ${JSON.stringify({ type: "error", error: { code: result.errorCode, message: "Upstream ended before completing the response." } })}\n\n`));
159
+ }
147
160
  res.end();
148
- return true;
149
- }
161
+ return result?.success ?? true;
162
+ };
163
+ if (!upstream.body)
164
+ return finish();
150
165
  const reader = upstream.body.getReader();
151
166
  res.on("close", () => {
152
167
  if (!res.writableEnded) {
168
+ clientDisconnected = true;
153
169
  void reader.cancel().catch(() => undefined);
154
170
  }
155
171
  });
@@ -163,10 +179,9 @@ extraHeaders) {
163
179
  if (value && value.byteLength > 0) {
164
180
  onChunk?.(value);
165
181
  // If the response is already finished (clean end by a concurrent
166
- // close-then-reader-cancel path), stop writing. Do NOT guard on
167
- // res.destroyed here: a socket-error-during-backpressure scenario sets
168
- // destroyed=true and then lets the next res.write() throw so the catch
169
- // block can record the error and fire onStreamError correctly.
182
+ // close-then-reader-cancel path), stop writing. A destroyed response
183
+ // is left to the next res.write(), which throws into the catch below;
184
+ // that path returns false without penalizing the upstream account.
170
185
  if (res.writableEnded)
171
186
  break;
172
187
  // Respect backpressure: when the client's socket buffer is full,
@@ -177,10 +192,14 @@ extraHeaders) {
177
192
  }
178
193
  }
179
194
  }
180
- res.end();
181
- return true;
195
+ return finish();
182
196
  }
183
197
  catch (error) {
198
+ if (clientDisconnected || res.destroyed || (error instanceof ClientCancellationError)) {
199
+ if (!res.destroyed)
200
+ res.destroy();
201
+ return false;
202
+ }
184
203
  status.lastError = error instanceof Error ? error.message : String(error);
185
204
  onStreamError();
186
205
  if (!res.destroyed) {
@@ -1 +1 @@
1
- {"version":3,"file":"account-check.d.ts","sourceRoot":"","sources":["../../../lib/runtime/account-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account-check.d.ts","sourceRoot":"","sources":["../../../lib/runtime/account-check.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,11 @@
1
1
  import { maskEmail } from "../logger.js";
2
2
  import { CODEX_UNAVAILABLE_PROBE_NOTE } from "../quota-probe.js";
3
3
  import { isCodexUnavailableError } from "../errors.js";
4
- import { bumpStorageAffinityGeneration, reconcilePinnedAccountIndex, } from "../storage.js";
4
+ import { cloneTrackedAccountStorage, bumpStorageAffinityGeneration, reconcilePinnedAccountIndex, } from "../storage.js";
5
5
  export async function runRuntimeAccountCheck(deepProbe, deps) {
6
6
  const loadedStorage = await deps.hydrateEmails(await deps.loadAccounts());
7
7
  const workingStorage = loadedStorage
8
- ? {
9
- ...loadedStorage,
10
- accounts: loadedStorage.accounts.map((account) => ({ ...account })),
11
- activeIndexByFamily: loadedStorage.activeIndexByFamily
12
- ? { ...loadedStorage.activeIndexByFamily }
13
- : {},
14
- }
8
+ ? cloneTrackedAccountStorage(loadedStorage)
15
9
  : deps.createEmptyStorage();
16
10
  if (workingStorage.accounts.length === 0) {
17
11
  deps.showLine("\nNo accounts to check.\n");