@fudrouter/fsrouter 0.6.72 → 0.6.74

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 (314) hide show
  1. package/dist/lib/headroom/detect.js +97 -7
  2. package/dist/lib/headroom/process.js +136 -4
  3. package/dist/open-sse/config/appConstants.js +23 -79
  4. package/dist/open-sse/config/cliFingerprints.js +1 -1
  5. package/dist/open-sse/config/errorConfig.js +0 -11
  6. package/dist/open-sse/config/grokCli.js +10 -0
  7. package/dist/open-sse/config/kiroConstants.js +99 -46
  8. package/dist/open-sse/config/runtimeConfig.js +2 -0
  9. package/dist/open-sse/config/ttsModels.js +2 -0
  10. package/dist/open-sse/executors/antigravity.js +250 -164
  11. package/dist/open-sse/executors/base.js +16 -11
  12. package/dist/open-sse/executors/codex.js +269 -1108
  13. package/dist/open-sse/executors/commandCode.js +1 -1
  14. package/dist/open-sse/executors/commandcode.js +10 -4
  15. package/dist/open-sse/executors/cursor.js +96 -203
  16. package/dist/open-sse/executors/default.js +146 -244
  17. package/dist/open-sse/executors/github.js +128 -73
  18. package/dist/open-sse/executors/grok-cli.js +527 -94
  19. package/dist/open-sse/executors/grok-web.js +5 -7
  20. package/dist/open-sse/executors/index.js +12 -0
  21. package/dist/open-sse/executors/kiro.js +150 -61
  22. package/dist/open-sse/executors/opencode-go.js +12 -4
  23. package/dist/open-sse/executors/opencode.js +1 -1
  24. package/dist/open-sse/executors/perplexity-web.js +20 -96
  25. package/dist/open-sse/executors/qoder.js +28 -18
  26. package/dist/open-sse/executors/vertex.js +53 -7
  27. package/dist/open-sse/executors/xiaomi-tokenplan.js +6 -5
  28. package/dist/open-sse/handlers/chatCore/nonStreamingHandler.js +97 -85
  29. package/dist/open-sse/handlers/chatCore/requestDetail.js +32 -8
  30. package/dist/open-sse/handlers/chatCore/sseToJsonHandler.js +13 -7
  31. package/dist/open-sse/handlers/chatCore/streamingHandler.js +74 -25
  32. package/dist/open-sse/handlers/chatCore.js +35 -41
  33. package/dist/open-sse/handlers/embeddingProviders/index.js +1 -0
  34. package/dist/open-sse/handlers/embeddingProviders/openai.js +8 -16
  35. package/dist/open-sse/handlers/imageProviders/blackForestLabs.js +2 -21
  36. package/dist/open-sse/handlers/imageProviders/cloudflareAi.js +2 -30
  37. package/dist/open-sse/handlers/imageProviders/codex.js +2 -20
  38. package/dist/open-sse/handlers/imageProviders/comfyui.js +5 -18
  39. package/dist/open-sse/handlers/imageProviders/falAi.js +2 -22
  40. package/dist/open-sse/handlers/imageProviders/gemini.js +2 -16
  41. package/dist/open-sse/handlers/imageProviders/huggingface.js +2 -17
  42. package/dist/open-sse/handlers/imageProviders/index.js +3 -4
  43. package/dist/open-sse/handlers/imageProviders/leonardo.js +1 -1
  44. package/dist/open-sse/handlers/imageProviders/nanobanana.js +4 -23
  45. package/dist/open-sse/handlers/imageProviders/openai.js +15 -43
  46. package/dist/open-sse/handlers/imageProviders/runwayml.js +2 -26
  47. package/dist/open-sse/handlers/imageProviders/sdwebui.js +4 -20
  48. package/dist/open-sse/handlers/imageProviders/stabilityAi.js +2 -26
  49. package/dist/open-sse/handlers/imageProviders/weavy.js +1 -1
  50. package/dist/open-sse/handlers/search/chatSearch.js +60 -14
  51. package/dist/open-sse/handlers/ttsProviders/gemini.js +14 -3
  52. package/dist/open-sse/handlers/ttsProviders/index.js +4 -2
  53. package/dist/open-sse/handlers/ttsProviders/openai.js +4 -1
  54. package/dist/open-sse/handlers/ttsProviders/openrouter.js +7 -4
  55. package/dist/open-sse/handlers/videoCore.js +166 -0
  56. package/dist/open-sse/handlers/videoProviders/weavy.js +1 -1
  57. package/dist/open-sse/mcp-server/catalog.js +1 -1
  58. package/dist/open-sse/mcp-server/server.js +2 -2
  59. package/dist/open-sse/mcp-server/tools/advancedTools.js +2 -2
  60. package/dist/open-sse/mcp-server/tools/agentSkillTools.js +1 -1
  61. package/dist/open-sse/mcp-server/tools/compressionTools.js +4 -4
  62. package/dist/open-sse/mcp-server/tools/gamificationTools.js +10 -10
  63. package/dist/open-sse/mcp-server/tools/githubSkillTools.js +1 -1
  64. package/dist/open-sse/mcp-server/tools/memoryTools.js +3 -3
  65. package/dist/open-sse/mcp-server/tools/notionTools.js +2 -2
  66. package/dist/open-sse/mcp-server/tools/obsidianTools.js +2 -2
  67. package/dist/open-sse/mcp-server/tools/pickFastestModel.js +1 -1
  68. package/dist/open-sse/mcp-server/tools/pluginTools.js +4 -4
  69. package/dist/open-sse/mcp-server/tools/skillTools.js +2 -2
  70. package/dist/open-sse/providers/capabilities.js +24 -4
  71. package/dist/open-sse/providers/registry/alicode-intl.js +1 -1
  72. package/dist/open-sse/providers/registry/anthropic.js +1 -8
  73. package/dist/open-sse/providers/registry/codex.js +0 -9
  74. package/dist/open-sse/providers/registry/cohere.js +0 -2
  75. package/dist/open-sse/providers/registry/gemini.js +0 -2
  76. package/dist/open-sse/providers/registry/github.js +9 -6
  77. package/dist/open-sse/providers/registry/grok-cli.js +28 -18
  78. package/dist/open-sse/providers/registry/index.js +74 -80
  79. package/dist/open-sse/providers/registry/kiro.js +12 -0
  80. package/dist/open-sse/providers/registry/mistral.js +0 -3
  81. package/dist/open-sse/providers/registry/openai.js +0 -3
  82. package/dist/open-sse/providers/registry/perplexity.js +0 -2
  83. package/dist/open-sse/providers/registry/xai.js +5 -3
  84. package/dist/open-sse/rtk/autodetect.js +10 -2
  85. package/dist/open-sse/rtk/caveman.js +2 -93
  86. package/dist/open-sse/rtk/cavemanPrompts.js +34 -2
  87. package/dist/open-sse/rtk/constants.js +7 -2
  88. package/dist/open-sse/rtk/headroom.js +133 -0
  89. package/dist/open-sse/rtk/ponytail.js +1 -1
  90. package/dist/open-sse/rtk/registry.js +2 -0
  91. package/dist/open-sse/services/accountFallback.js +0 -108
  92. package/dist/open-sse/services/combo/quotaExhaustionCutoff.js +2 -2
  93. package/dist/open-sse/services/combo/quotaShareStrategy.js +1 -1
  94. package/dist/open-sse/services/combo/resolveAutoStrategy.js +1 -1
  95. package/dist/open-sse/services/combo/sessionStickiness.js +1 -1
  96. package/dist/open-sse/services/combo/targetSorters.js +2 -2
  97. package/dist/open-sse/services/combo/validateQuality.js +1 -1
  98. package/dist/open-sse/services/comboConfig.js +1 -1
  99. package/dist/open-sse/services/emergencyFallback.js +1 -1
  100. package/dist/open-sse/services/grokCliModels.js +127 -0
  101. package/dist/open-sse/services/model.js +7 -1
  102. package/dist/open-sse/services/provider.js +1 -1
  103. package/dist/open-sse/services/proxyAutoSelector.js +1 -1
  104. package/dist/open-sse/services/tokenRefresh/providers.js +3 -3
  105. package/dist/open-sse/services/usage/codex.js +148 -214
  106. package/dist/open-sse/services/usage/grok-cli.js +61 -7
  107. package/dist/open-sse/services/usage/misc.js +0 -19
  108. package/dist/open-sse/services/usage.js +0 -2
  109. package/dist/open-sse/translator/concerns/paramSupport.js +2 -2
  110. package/dist/open-sse/translator/concerns/thinkingUnified.js +6 -0
  111. package/dist/open-sse/translator/index.js +97 -59
  112. package/dist/open-sse/translator/request/claude-to-kiro.js +67 -40
  113. package/dist/open-sse/translator/request/openai-responses.js +1 -0
  114. package/dist/open-sse/translator/request/openai-to-commandcode.js +0 -4
  115. package/dist/open-sse/translator/request/openai-to-kiro.js +49 -20
  116. package/dist/open-sse/translator/response/commandcode-to-openai.js +0 -4
  117. package/dist/open-sse/utils/bypassHandler.js +18 -3
  118. package/dist/open-sse/utils/claudeCloaking.js +15 -5
  119. package/dist/open-sse/utils/cursorImages.js +1 -1
  120. package/dist/open-sse/utils/error.js +0 -17
  121. package/dist/open-sse/utils/keepaliveThreshold.js +4 -4
  122. package/dist/open-sse/utils/kiroSessionReplay.js +125 -0
  123. package/dist/open-sse/utils/noThinkingAlias.js +1 -1
  124. package/dist/open-sse/utils/proxyFetch.js +0 -1
  125. package/dist/open-sse/utils/reasoningContentInjector.js +4 -7
  126. package/dist/open-sse/utils/responsesStatePolicy.js +1 -1
  127. package/dist/open-sse/utils/responsesStreamHelpers.js +1 -0
  128. package/dist/open-sse/utils/sessionManager.js +49 -22
  129. package/dist/open-sse/utils/stream.js +54 -77
  130. package/dist/open-sse/utils/streamHandler.js +19 -15
  131. package/dist/open-sse/utils/streamHelpers.js +2 -17
  132. package/dist/open-sse/utils/usageTracking.js +103 -12
  133. package/dist/routes/headroom/extras/route.js +58 -0
  134. package/dist/routes/headroom/restart/route.js +33 -0
  135. package/open-sse/config/appConstants.js +23 -79
  136. package/open-sse/config/errorConfig.js +0 -11
  137. package/open-sse/config/grokCli.js +10 -0
  138. package/open-sse/config/kiroConstants.js +99 -46
  139. package/open-sse/config/runtimeConfig.js +2 -0
  140. package/open-sse/config/ttsModels.js +2 -0
  141. package/open-sse/executors/antigravity.js +250 -164
  142. package/open-sse/executors/base.js +16 -11
  143. package/open-sse/executors/codex.js +269 -1108
  144. package/open-sse/executors/commandcode.js +10 -4
  145. package/open-sse/executors/cursor.js +96 -203
  146. package/open-sse/executors/default.js +146 -244
  147. package/open-sse/executors/github.js +128 -73
  148. package/open-sse/executors/grok-cli.js +527 -94
  149. package/open-sse/executors/grok-web.js +5 -7
  150. package/open-sse/executors/index.js +12 -0
  151. package/open-sse/executors/kiro.js +150 -61
  152. package/open-sse/executors/opencode-go.js +12 -4
  153. package/open-sse/executors/opencode.js +1 -1
  154. package/open-sse/executors/perplexity-web.js +20 -96
  155. package/open-sse/executors/qoder.js +28 -18
  156. package/open-sse/executors/vertex.js +53 -7
  157. package/open-sse/executors/xiaomi-tokenplan.js +6 -5
  158. package/open-sse/handlers/chatCore/nonStreamingHandler.js +97 -85
  159. package/open-sse/handlers/chatCore/requestDetail.js +32 -8
  160. package/open-sse/handlers/chatCore/sseToJsonHandler.js +13 -7
  161. package/open-sse/handlers/chatCore/streamingHandler.js +74 -25
  162. package/open-sse/handlers/chatCore.js +35 -41
  163. package/open-sse/handlers/embeddingProviders/index.js +1 -0
  164. package/open-sse/handlers/embeddingProviders/openai.js +8 -16
  165. package/open-sse/handlers/imageProviders/blackForestLabs.js +2 -21
  166. package/open-sse/handlers/imageProviders/cloudflareAi.js +2 -30
  167. package/open-sse/handlers/imageProviders/codex.js +2 -20
  168. package/open-sse/handlers/imageProviders/comfyui.js +5 -18
  169. package/open-sse/handlers/imageProviders/falAi.js +2 -22
  170. package/open-sse/handlers/imageProviders/gemini.js +2 -16
  171. package/open-sse/handlers/imageProviders/huggingface.js +2 -17
  172. package/open-sse/handlers/imageProviders/index.js +3 -4
  173. package/open-sse/handlers/imageProviders/leonardo.js +1 -1
  174. package/open-sse/handlers/imageProviders/nanobanana.js +4 -23
  175. package/open-sse/handlers/imageProviders/openai.js +15 -43
  176. package/open-sse/handlers/imageProviders/runwayml.js +2 -26
  177. package/open-sse/handlers/imageProviders/sdwebui.js +4 -20
  178. package/open-sse/handlers/imageProviders/stabilityAi.js +2 -26
  179. package/open-sse/handlers/imageProviders/weavy.js +1 -1
  180. package/open-sse/handlers/search/chatSearch.js +60 -14
  181. package/open-sse/handlers/ttsProviders/gemini.js +14 -3
  182. package/open-sse/handlers/ttsProviders/index.js +4 -2
  183. package/open-sse/handlers/ttsProviders/openai.js +4 -1
  184. package/open-sse/handlers/ttsProviders/openrouter.js +7 -4
  185. package/open-sse/handlers/videoCore.js +166 -0
  186. package/open-sse/handlers/videoProviders/weavy.js +1 -1
  187. package/open-sse/mcp-server/audit.js +1 -1
  188. package/open-sse/mcp-server/catalog.js +2 -2
  189. package/open-sse/mcp-server/descriptionCompressor.js +1 -1
  190. package/open-sse/mcp-server/mcpCallerIdentity.js +1 -1
  191. package/open-sse/mcp-server/schemas/tools.js +1 -1
  192. package/open-sse/mcp-server/server.js +4 -4
  193. package/open-sse/mcp-server/tools/advancedTools.js +5 -5
  194. package/open-sse/mcp-server/tools/agentSkillTools.js +1 -1
  195. package/open-sse/mcp-server/tools/compressionTools.js +4 -4
  196. package/open-sse/mcp-server/tools/gamificationTools.js +10 -10
  197. package/open-sse/mcp-server/tools/githubSkillTools.js +1 -1
  198. package/open-sse/mcp-server/tools/memoryTools.js +3 -3
  199. package/open-sse/mcp-server/tools/notionTools.js +2 -2
  200. package/open-sse/mcp-server/tools/obsidianTools.js +2 -2
  201. package/open-sse/mcp-server/tools/pickFastestModel.js +3 -3
  202. package/open-sse/mcp-server/tools/pluginTools.js +4 -4
  203. package/open-sse/mcp-server/tools/skillTools.js +2 -2
  204. package/open-sse/providers/capabilities.js +24 -4
  205. package/open-sse/providers/registry/alicode-intl.js +1 -1
  206. package/open-sse/providers/registry/anthropic.js +1 -8
  207. package/open-sse/providers/registry/codex.js +0 -9
  208. package/open-sse/providers/registry/cohere.js +0 -2
  209. package/open-sse/providers/registry/gemini.js +0 -2
  210. package/open-sse/providers/registry/github.js +9 -6
  211. package/open-sse/providers/registry/grok-cli.js +28 -18
  212. package/open-sse/providers/registry/index.js +74 -80
  213. package/open-sse/providers/registry/kiro.js +12 -0
  214. package/open-sse/providers/registry/mistral.js +0 -3
  215. package/open-sse/providers/registry/openai.js +0 -3
  216. package/open-sse/providers/registry/perplexity.js +0 -2
  217. package/open-sse/providers/registry/xai.js +5 -3
  218. package/open-sse/rtk/autodetect.js +10 -2
  219. package/open-sse/rtk/caveman.js +2 -93
  220. package/open-sse/rtk/cavemanPrompts.js +34 -2
  221. package/open-sse/rtk/constants.js +7 -2
  222. package/open-sse/rtk/headroom.js +133 -0
  223. package/open-sse/rtk/ponytail.js +1 -1
  224. package/open-sse/rtk/registry.js +2 -0
  225. package/open-sse/services/accountFallback.js +0 -108
  226. package/open-sse/services/combo/quotaExhaustionCutoff.js +2 -2
  227. package/open-sse/services/combo/quotaShareStrategy.js +1 -1
  228. package/open-sse/services/combo/resolveAutoStrategy.js +1 -1
  229. package/open-sse/services/combo/sessionStickiness.js +1 -1
  230. package/open-sse/services/combo/shadowRouting.js +1 -1
  231. package/open-sse/services/combo/targetSorters.js +4 -4
  232. package/open-sse/services/combo/validateQuality.js +1 -1
  233. package/open-sse/services/evalRouting.js +1 -1
  234. package/open-sse/services/grokCliModels.js +127 -0
  235. package/open-sse/services/model.js +7 -1
  236. package/open-sse/services/modelCapabilities.js +1 -1
  237. package/open-sse/services/modelFamilyFallback.js +1 -1
  238. package/open-sse/services/provider.js +1 -1
  239. package/open-sse/services/providerCooldownTracker.js +1 -1
  240. package/open-sse/services/rateLimitManager.js +1 -1
  241. package/open-sse/services/tokenRefresh/providers.js +3 -3
  242. package/open-sse/services/usage/codex.js +148 -214
  243. package/open-sse/services/usage/grok-cli.js +61 -7
  244. package/open-sse/services/usage/misc.js +0 -19
  245. package/open-sse/services/usage.js +0 -2
  246. package/open-sse/translator/concerns/paramSupport.js +2 -2
  247. package/open-sse/translator/concerns/thinkingUnified.js +6 -0
  248. package/open-sse/translator/index.js +97 -59
  249. package/open-sse/translator/request/claude-to-kiro.js +67 -40
  250. package/open-sse/translator/request/openai-responses.js +1 -0
  251. package/open-sse/translator/request/openai-to-commandcode.js +0 -4
  252. package/open-sse/translator/request/openai-to-kiro.js +49 -20
  253. package/open-sse/translator/response/commandcode-to-openai.js +0 -4
  254. package/open-sse/utils/bypassHandler.js +18 -3
  255. package/open-sse/utils/claudeCloaking.js +15 -5
  256. package/open-sse/utils/error.js +0 -17
  257. package/open-sse/utils/kiroSessionReplay.js +125 -0
  258. package/open-sse/utils/logger.js +1 -1
  259. package/open-sse/utils/proxyFetch.js +0 -1
  260. package/open-sse/utils/reasoningContentInjector.js +4 -7
  261. package/open-sse/utils/responsesStreamHelpers.js +1 -0
  262. package/open-sse/utils/sessionManager.js +49 -22
  263. package/open-sse/utils/stream.js +54 -77
  264. package/open-sse/utils/streamHandler.js +19 -15
  265. package/open-sse/utils/streamHelpers.js +2 -17
  266. package/open-sse/utils/usageTracking.js +103 -12
  267. package/package.json +4 -7
  268. package/public/assets/EndpointPageClient.C2WMTYy0.js +6 -0
  269. package/public/assets/{Loading.Bj-WGl4k.js → Loading.BHPtQ9vj.js} +1 -1
  270. package/public/assets/{NoAuthProxyCard.qAv57b-0.js → NoAuthProxyCard.BDcXaWvM.js} +1 -1
  271. package/public/assets/index.Be3gbrBl.js +89 -0
  272. package/public/assets/index.COMgQZYj.css +1 -0
  273. package/public/assets/{page.CbkENBd5.js → page.-lpNgYAw.js} +1 -1
  274. package/public/assets/{page.CssmrzTT.js → page.11hpNKmq.js} +1 -1
  275. package/public/assets/{page.C72FTTJr.js → page.B-LxsnqU.js} +1 -1
  276. package/public/assets/{page.sOFOrxqq.js → page.B-dzeIUC.js} +1 -1
  277. package/public/assets/{page.CsxWmPoI.js → page.B-nBR75u.js} +1 -1
  278. package/public/assets/{page.Dx_GRRdT.js → page.B3QqvLep.js} +1 -1
  279. package/public/assets/{page.DPyOkPVo.js → page.B8xdkCWA.js} +1 -1
  280. package/public/assets/{page.DUI8kqOY.js → page.BFb7yUd8.js} +1 -1
  281. package/public/assets/page.BbPPdoas.js +1 -0
  282. package/public/assets/page.BdS20a1O.js +1 -0
  283. package/public/assets/{page.DibhF7f-.js → page.BfmI9KYe.js} +1 -1
  284. package/public/assets/{page.DX4Nno6M.js → page.BgGJovOg.js} +1 -1
  285. package/public/assets/{page.BnmTC5jG.js → page.C0gBvR4O.js} +1 -1
  286. package/public/assets/{page.D5gj98Oe.js → page.C6bYizL7.js} +1 -1
  287. package/public/assets/{page.vla5Mzki.js → page.C8sLEMa6.js} +1 -1
  288. package/public/assets/{page.CXd_R9yU.js → page.CCNsZBXp.js} +1 -1
  289. package/public/assets/{page.CQlVGoXg.js → page.CF_YSLwP.js} +1 -1
  290. package/public/assets/{page.BbO1pspY.js → page.CPTtRDxw.js} +1 -1
  291. package/public/assets/{page.D1oKew1j.js → page.CdMmlUEm.js} +1 -1
  292. package/public/assets/{page.DxMDTbSx.js → page.Cged1bOZ.js} +1 -1
  293. package/public/assets/{page.Cj5h09Zo.js → page.Ch647dVk.js} +1 -1
  294. package/public/assets/{page.DVIHRbxB.js → page.CpvXDkQF.js} +1 -1
  295. package/public/assets/{page.i4KDuWcM.js → page.CrBl7f8N.js} +1 -1
  296. package/public/assets/{page.BSLJ4tx5.js → page.CrP7niA2.js} +1 -1
  297. package/public/assets/{page.CnTop8Zt.js → page.CvUFZzXr.js} +1 -1
  298. package/public/assets/{page.O2ddJYoT.js → page.CyUqJPmx.js} +1 -1
  299. package/public/assets/{page.DBhu4Arl.js → page.D8Rp8fFR.js} +1 -1
  300. package/public/assets/{page.CPZav1_c.js → page.Df9qtrdG.js} +1 -1
  301. package/public/assets/{page.Qkkv8roc.js → page.DiL4Gwqe.js} +1 -1
  302. package/public/assets/{page.BdmJ3yY3.js → page.Dl1apZE2.js} +1 -1
  303. package/public/assets/{page.1qjqvrwX.js → page.HV0w1d5C.js} +1 -1
  304. package/public/assets/{page.ChX1NqmC.js → page.N1x4YL8_.js} +1 -1
  305. package/public/assets/{page.Dh5f0XJC.js → page.P2Ny5veR.js} +1 -1
  306. package/public/assets/{page.CSs3-gdt.js → page.fOOCr18J.js} +1 -1
  307. package/public/assets/{page.Ir7KvKcF.js → page.sUIPWIiF.js} +1 -1
  308. package/public/assets/{page.CzlQTyAL.js → page.tE7PpWYW.js} +1 -1
  309. package/public/assets/{page.DzZLqI5Q.js → page.uziaGZsT.js} +1 -1
  310. package/public/index.html +2 -2
  311. package/public/assets/EndpointPageClient.LyNNvZ4z.js +0 -6
  312. package/public/assets/index.BuPfZebu.css +0 -1
  313. package/public/assets/index.CShjRV_B.js +0 -89
  314. package/public/assets/page.DD_czjjP.js +0 -1
@@ -1,4 +1,4 @@
1
- import { listModelEvalRunsForRouting } from "../../dist/lib/db/evals.ts";
1
+ import { listModelEvalRunsForRouting } from "../../src/lib/db/evals.ts";
2
2
  import { parseModel } from "./model.ts";
3
3
  const DEFAULT_EVAL_ROUTING_CONFIG = {
4
4
  enabled: false,
@@ -0,0 +1,127 @@
1
+ import {
2
+ GROK_CLI_BASE_URL,
3
+ GROK_CLI_CLIENT_IDENTIFIER,
4
+ GROK_CLI_MODEL,
5
+ GROK_CLI_USER_AGENT,
6
+ GROK_CLI_VERSION,
7
+ } from "../config/grokCli.js";
8
+ import { refreshProviderCredentials } from "./oauthCredentialManager.js";
9
+ import { proxyAwareFetch } from "../utils/proxyFetch.js";
10
+
11
+ const MODELS_URL = `${GROK_CLI_BASE_URL}/models`;
12
+
13
+ function modelEntries(data) {
14
+ const value = Array.isArray(data) ? data : data?.data ?? data?.models ?? data?.results ?? [];
15
+ if (Array.isArray(value)) return value.map((item) => [null, item]);
16
+ if (value && typeof value === "object") return Object.entries(value);
17
+ return [];
18
+ }
19
+
20
+ export function parseGrokCliModels(data) {
21
+ const seen = new Set();
22
+ const models = [];
23
+
24
+ for (const [key, raw] of modelEntries(data)) {
25
+ const item = typeof raw === "string" ? { id: raw } : raw;
26
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
27
+ const id = String(
28
+ item.id ?? item.model_id ?? item.modelId ?? item.model ?? item.slug ?? key ?? item.name ?? "",
29
+ ).trim();
30
+ if (!id || seen.has(id)) continue;
31
+ seen.add(id);
32
+
33
+ const model = {
34
+ ...item,
35
+ id,
36
+ name: item.display_name ?? item.displayName ?? item.name ?? id,
37
+ };
38
+ const contextLength = Number(
39
+ item.context_length ?? item.contextLength ?? item.context_window ?? item.contextWindow,
40
+ );
41
+ const maxOutputTokens = Number(item.max_output_tokens ?? item.maxOutputTokens);
42
+ if (Number.isFinite(contextLength) && contextLength > 0) model.contextLength = contextLength;
43
+ if (Number.isFinite(maxOutputTokens) && maxOutputTokens > 0) {
44
+ model.maxOutputTokens = maxOutputTokens;
45
+ }
46
+ if (id === GROK_CLI_MODEL) {
47
+ model.contextLength ||= 500000;
48
+ model.maxOutputTokens ||= 64000;
49
+ }
50
+ models.push(model);
51
+ }
52
+
53
+ return models;
54
+ }
55
+
56
+ function buildHeaders(accessToken, providerSpecificData = {}) {
57
+ const headers = {
58
+ Authorization: `Bearer ${accessToken}`,
59
+ Accept: "application/json",
60
+ "User-Agent": GROK_CLI_USER_AGENT,
61
+ "x-xai-token-auth": "xai-grok-cli",
62
+ "x-grok-client-version": GROK_CLI_VERSION,
63
+ "x-grok-client-identifier": GROK_CLI_CLIENT_IDENTIFIER,
64
+ "x-grok-client-mode": "headless",
65
+ };
66
+ const email = providerSpecificData?.email;
67
+ const userId = providerSpecificData?.userId || providerSpecificData?.principalId;
68
+ if (email) headers["x-email"] = email;
69
+ if (userId) headers["x-userid"] = userId;
70
+ return headers;
71
+ }
72
+
73
+ export async function resolveGrokCliModels(credentials, options = {}) {
74
+ const {
75
+ fetchFn = proxyAwareFetch,
76
+ log = console,
77
+ proxyOptions = null,
78
+ onCredentialsRefreshed,
79
+ } = options;
80
+ let accessToken = credentials?.accessToken;
81
+ if (!accessToken) return { models: [], warning: "Grok CLI access token is missing." };
82
+
83
+ const request = (token) => fetchFn(
84
+ MODELS_URL,
85
+ {
86
+ method: "GET",
87
+ headers: buildHeaders(token, credentials?.providerSpecificData),
88
+ },
89
+ proxyOptions,
90
+ );
91
+
92
+ try {
93
+ let response = await request(accessToken);
94
+ if ((response.status === 401 || response.status === 403) && credentials?.refreshToken) {
95
+ const refreshed = await refreshProviderCredentials(
96
+ "grok-cli",
97
+ credentials,
98
+ log,
99
+ proxyOptions,
100
+ );
101
+ if (refreshed?.accessToken) {
102
+ accessToken = refreshed.accessToken;
103
+ try {
104
+ await onCredentialsRefreshed?.(refreshed);
105
+ } catch (error) {
106
+ log?.warn?.("Grok CLI credential persistence failed", error);
107
+ }
108
+ response = await request(accessToken);
109
+ }
110
+ }
111
+
112
+ if (!response.ok) {
113
+ const detail = await response.text().catch(() => "");
114
+ return {
115
+ models: [],
116
+ warning: `Grok CLI model discovery failed (${response.status})${detail ? `: ${detail.slice(0, 160)}` : ""}`,
117
+ };
118
+ }
119
+
120
+ const models = parseGrokCliModels(await response.json());
121
+ return models.length
122
+ ? { models }
123
+ : { models: [], warning: "Grok CLI returned no selectable models." };
124
+ } catch (error) {
125
+ return { models: [], warning: `Grok CLI model discovery failed: ${error.message}` };
126
+ }
127
+ }
@@ -17,6 +17,10 @@ for (const entry of REGISTRY) {
17
17
  for (const a of entry.aliases || []) ALIAS_TO_PROVIDER_ID[a] = entry.id;
18
18
  }
19
19
 
20
+ const BUILTIN_MODEL_ALIASES = {
21
+ "grok-build": "gcli/grok-build",
22
+ };
23
+
20
24
  /**
21
25
  * Resolve provider alias to provider ID
22
26
  */
@@ -104,7 +108,9 @@ export async function getModelInfoCore(modelStr, aliasesOrGetter) {
104
108
  : aliasesOrGetter;
105
109
 
106
110
  // Resolve alias
107
- const resolved = resolveModelAliasFromMap(parsed.model, aliases);
111
+ const resolved =
112
+ resolveModelAliasFromMap(parsed.model, aliases) ||
113
+ resolveModelAliasFromMap(parsed.model, BUILTIN_MODEL_ALIASES);
108
114
  if (resolved) {
109
115
  return resolved;
110
116
  }
@@ -2,4 +2,4 @@ export {
2
2
  getResolvedModelCapabilities,
3
3
  supportsReasoning,
4
4
  supportsToolCalling,
5
- } from "../../dist/lib/modelCapabilities.js";
5
+ } from "../../src/lib/modelCapabilities.js";
@@ -1,4 +1,4 @@
1
- import { getModelContextLimit } from "../../dist/lib/modelCapabilities";
1
+ import { getModelContextLimit } from "../../src/lib/modelCapabilities";
2
2
  import { parseModel } from "./model.js";
3
3
  import { CONTEXT_OVERFLOW_REGEX } from "./errorClassifier.js";
4
4
  import { getRegistryEntry } from "../config/providerRegistry.js";
@@ -105,7 +105,7 @@ export function detectFormat(body) {
105
105
  }
106
106
 
107
107
  // Get provider config (internal — no external runtime consumer)
108
- export function getProviderConfig(provider) {
108
+ function getProviderConfig(provider) {
109
109
  if (isOpenAICompatible(provider)) {
110
110
  const apiType = getOpenAICompatibleType(provider);
111
111
  return {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DEFAULT_RESILIENCE_SETTINGS
3
- } from "../../dist/lib/resilience/settings";
3
+ } from "../../src/lib/resilience/settings";
4
4
  const cooldownMap = /* @__PURE__ */ new Map();
5
5
  const DEFAULT_ENTRY_RETENTION_MS = 30 * 60 * 1e3;
6
6
  const CLEANUP_INTERVAL_MS = 60 * 1e3;
@@ -6,7 +6,7 @@ import { awaitProviderDefaultSlot } from "./providerDefaultRateLimit.js";
6
6
  import {
7
7
  DEFAULT_RESILIENCE_SETTINGS,
8
8
  resolveResilienceSettings
9
- } from "../../dist/lib/resilience/settings";
9
+ } from "../../src/lib/resilience/settings";
10
10
  import {
11
11
  STANDARD_HEADERS,
12
12
  ANTHROPIC_HEADERS,
@@ -2,7 +2,7 @@ import { PROVIDERS, PROVIDER_OAUTH } from "../../config/providers.js";
2
2
  import { OAUTH_ENDPOINTS, GITHUB_COPILOT } from "../../config/appConstants.js";
3
3
  import { proxyAwareFetch } from "../../utils/proxyFetch.js";
4
4
  import { dedupRefresh } from "./dedup.js";
5
- import { buildExternalIdpRefreshParams } from '../../../dist/lib/oauth/kiroExternalIdp.js';
5
+ import { buildExternalIdpRefreshParams } from '../../dist/lib/oauth/kiroExternalIdp.js';
6
6
 
7
7
  let _xaiServiceSingleton = null;
8
8
  export async function refreshXaiToken(refreshToken, log) {
@@ -10,7 +10,7 @@ export async function refreshXaiToken(refreshToken, log) {
10
10
  return dedupRefresh("xai", refreshToken, async () => {
11
11
  try {
12
12
  if (!_xaiServiceSingleton) {
13
- const mod = await import('../../../dist/lib/oauth/services/xai.js');
13
+ const mod = await import('../../dist/lib/oauth/services/xai.js');
14
14
  _xaiServiceSingleton = new mod.XaiService();
15
15
  }
16
16
  const tokens = await _xaiServiceSingleton.refreshAccessToken(refreshToken);
@@ -296,7 +296,7 @@ async function resolveKiroProfileArnPatch(providerSpecificData, accessToken, ref
296
296
  if (providerSpecificData?.profileArn) return {};
297
297
  let profileArn = refreshedArn?.trim?.() || null;
298
298
  if (!profileArn) {
299
- const { fetchKiroProfileArn } = await import('../../../dist/lib/oauth/providers.js');
299
+ const { fetchKiroProfileArn } = await import('../../dist/lib/oauth/providers.js');
300
300
  profileArn = await fetchKiroProfileArn(accessToken);
301
301
  }
302
302
  return profileArn ? { providerSpecificData: { profileArn } } : {};
@@ -1,267 +1,201 @@
1
1
  /**
2
- * Codex (OpenAI / ChatGPT backend) usage handler
3
- * Updated from OmniRoute — supports dual-window quota, code_review, spark, bankedResetCredits
2
+ * Codex (OpenAI) usage handler
4
3
  */
5
4
 
6
5
  import { proxyAwareFetch } from "../../utils/proxyFetch.js";
6
+ import { U, parseResetTime, toFiniteNumber } from "./shared.js";
7
7
 
8
- // Codex (OpenAI) API config — uses /wham/usage (newer endpoint)
8
+ // Codex (OpenAI) API config
9
9
  const CODEX_CONFIG = {
10
- usageUrl: "https://chatgpt.com/backend-api/wham/usage",
10
+ usageUrl: U("codex").url,
11
+ resetCreditsUrl: U("codex").resetCreditsUrl,
12
+ resetCreditsConsumeUrl: U("codex").resetCreditsConsumeUrl,
11
13
  };
12
14
 
13
- function getFieldValue(record, ...keys) {
14
- if (!record || typeof record !== "object") return null;
15
- for (const key of keys) {
16
- if (record[key] !== undefined && record[key] !== null) return record[key];
17
- }
18
- return null;
19
- }
20
-
21
- function toRecord(value) {
22
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
15
+ function toIsoDate(value) {
16
+ if (!value) return null;
17
+ const date = value instanceof Date
18
+ ? value
19
+ : new Date(typeof value === "number" && value < 1e12 ? value * 1000 : value);
20
+ const time = date.getTime();
21
+ return Number.isFinite(time) ? date.toISOString() : null;
23
22
  }
24
23
 
25
- function toNumber(value, fallback = 0) {
26
- if (typeof value === "number" && Number.isFinite(value)) return value;
27
- if (typeof value === "string" && value.trim().length > 0) {
28
- const parsed = Number(value);
29
- return Number.isFinite(parsed) ? parsed : fallback;
30
- }
31
- return fallback;
24
+ function getCodexAccountId(providerSpecificData) {
25
+ return providerSpecificData?.workspaceId || providerSpecificData?.accountId || providerSpecificData?.chatgptAccountId || null;
32
26
  }
33
27
 
34
- function parseResetTime(resetValue) {
35
- if (!resetValue) return null;
36
- try {
37
- let date = null;
38
- if (resetValue instanceof Date) {
39
- date = resetValue;
40
- } else if (typeof resetValue === "number") {
41
- date = new Date(resetValue < 1e12 ? resetValue * 1000 : resetValue);
42
- } else if (typeof resetValue === "string") {
43
- if (/^\d+$/.test(resetValue)) {
44
- const ts = Number(resetValue);
45
- date = new Date(ts < 1e12 ? ts * 1000 : ts);
46
- } else {
47
- date = new Date(resetValue);
48
- }
49
- }
50
- if (!date || date.getTime() <= 0) return null;
51
- return date.toISOString();
52
- } catch {
53
- return null;
54
- }
28
+ function getCodexRateLimitBody(snapshot) {
29
+ if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) return null;
30
+ return snapshot.rate_limit && typeof snapshot.rate_limit === "object"
31
+ ? snapshot.rate_limit
32
+ : snapshot;
55
33
  }
56
34
 
57
- function parseWindowReset(window) {
58
- const resetAt = toNumber(getFieldValue(window, "reset_at", "resetAt"), 0);
59
- const resetAfterSeconds = toNumber(getFieldValue(window, "reset_after_seconds", "resetAfterSeconds"), 0);
60
- if (resetAt > 0) return parseResetTime(resetAt * 1000);
61
- if (resetAfterSeconds > 0) return parseResetTime(Date.now() + resetAfterSeconds * 1000);
62
- return null;
63
- }
64
-
65
- function buildPercentageQuota(window, displayName) {
66
- const usedPercent = toNumber(getFieldValue(window, "used_percent", "usedPercent"), 0);
35
+ function formatCodexWindow(window) {
36
+ const used = Math.max(0, Math.min(100, toFiniteNumber(window?.used_percent ?? window?.percent_used, 0)));
67
37
  return {
68
- used: usedPercent,
38
+ used,
69
39
  total: 100,
70
- remaining: 100 - usedPercent,
71
- resetAt: parseWindowReset(window),
40
+ remaining: Math.max(0, 100 - used),
41
+ resetAt: parseResetTime(window?.reset_at ?? window?.resets_at ?? window?.resetAt ?? null),
72
42
  unlimited: false,
73
- ...(displayName ? { displayName } : {}),
74
43
  };
75
44
  }
76
45
 
77
- function isCodexReviewLimitDescriptor(...values) {
78
- return values.some((value) => {
79
- if (typeof value !== "string") return false;
80
- const normalized = value.trim().toLowerCase();
81
- if (!normalized) return false;
82
- return (
83
- normalized === "code_review" ||
84
- normalized === "codex_review" ||
85
- normalized === "review" ||
86
- normalized.includes("code_review") ||
87
- normalized.includes("codex_review") ||
88
- normalized.includes("code review")
89
- );
90
- });
91
- }
92
-
93
- function findCodexReviewRateLimit(data) {
94
- const additionalRateLimits = getFieldValue(data, "additional_rate_limits", "additionalRateLimits");
95
- if (!Array.isArray(additionalRateLimits)) return {};
96
- for (const entry of additionalRateLimits) {
97
- const rec = toRecord(entry);
98
- if (
99
- isCodexReviewLimitDescriptor(
100
- getFieldValue(rec, "limit_name", "limitName"),
101
- getFieldValue(rec, "metered_feature", "meteredFeature"),
102
- getFieldValue(rec, "limit_id", "limitId"),
103
- rec["id"], rec["name"]
104
- )
105
- ) {
106
- return toRecord(getFieldValue(rec, "rate_limit", "rateLimit"));
107
- }
108
- }
109
- return {};
110
- }
46
+ function appendCodexQuotaWindows(quotas, prefix, snapshot) {
47
+ const rateLimit = getCodexRateLimitBody(snapshot);
48
+ if (!rateLimit) return false;
111
49
 
112
- function isCodexSparkLimitDescriptor(...values) {
113
- return values.some((value) => {
114
- if (typeof value !== "string") return false;
115
- const normalized = value.trim().toLowerCase();
116
- return normalized === "spark" || normalized.includes("spark");
117
- });
118
- }
50
+ const primary = rateLimit.primary_window || rateLimit.primary || snapshot.primary_window || snapshot.primary;
51
+ const secondary = rateLimit.secondary_window || rateLimit.secondary || snapshot.secondary_window || snapshot.secondary;
52
+ let added = false;
119
53
 
120
- function findCodexSparkRateLimit(data) {
121
- const additionalRateLimits = getFieldValue(data, "additional_rate_limits", "additionalRateLimits");
122
- if (!Array.isArray(additionalRateLimits)) return {};
123
- for (const entry of additionalRateLimits) {
124
- const rec = toRecord(entry);
125
- if (
126
- isCodexSparkLimitDescriptor(
127
- getFieldValue(rec, "limit_name", "limitName"),
128
- getFieldValue(rec, "metered_feature", "meteredFeature"),
129
- getFieldValue(rec, "limit_id", "limitId"),
130
- rec["id"], rec["name"], rec["model"],
131
- getFieldValue(rec, "model_id", "modelId")
132
- )
133
- ) {
134
- return toRecord(getFieldValue(rec, "rate_limit", "rateLimit"));
135
- }
54
+ if (primary) {
55
+ quotas[prefix ? `${prefix}_session` : "session"] = formatCodexWindow(primary);
56
+ added = true;
57
+ }
58
+ if (secondary) {
59
+ quotas[prefix ? `${prefix}_weekly` : "weekly"] = formatCodexWindow(secondary);
60
+ added = true;
136
61
  }
137
- return {};
138
- }
139
-
140
- function parseBankedResetCredits(data) {
141
- const resetCredits = toRecord(getFieldValue(data, "rate_limit_reset_credits", "rateLimitResetCredits"));
142
- const availableCount = getFieldValue(resetCredits, "available_count", "availableCount");
143
- const count = toNumber(availableCount, NaN);
144
- return Number.isFinite(count) ? count : undefined;
145
- }
146
62
 
147
- function parseRateLimitReachedType(data) {
148
- const reachedType = getFieldValue(data, "rate_limit_reached_type", "rateLimitReachedType");
149
- if (typeof reachedType === "string" && reachedType.trim().length > 0) return reachedType.trim();
150
- const reachedTypeObj = toRecord(reachedType);
151
- const type = getFieldValue(reachedTypeObj, "type");
152
- return typeof type === "string" && type.trim().length > 0 ? type.trim() : undefined;
63
+ return added;
153
64
  }
154
65
 
155
- /**
156
- * Build Codex usage quotas from /wham/usage response.
157
- * Supports: session (5h), weekly (7d), code_review, code_review_weekly, spark, spark_weekly
158
- */
159
- function buildCodexUsageQuotas(dataValue) {
160
- const data = toRecord(dataValue);
161
- const rateLimit = toRecord(getFieldValue(data, "rate_limit", "rateLimit"));
162
- const quotas = {};
163
- const bankedResetCredits = parseBankedResetCredits(data);
164
- const rateLimitReachedType = parseRateLimitReachedType(data);
165
-
166
- // Primary window (5h session)
167
- const primaryWindow = toRecord(getFieldValue(rateLimit, "primary_window", "primaryWindow"));
168
- if (Object.keys(primaryWindow).length > 0) quotas.session = buildPercentageQuota(primaryWindow);
169
-
170
- // Secondary window (7d weekly)
171
- const secondaryWindow = toRecord(getFieldValue(rateLimit, "secondary_window", "secondaryWindow"));
172
- if (Object.keys(secondaryWindow).length > 0) quotas.weekly = buildPercentageQuota(secondaryWindow);
173
-
174
- // Code review rate limit (dedicated block or additional_rate_limits fallback)
175
- const dedicatedReviewRateLimit = toRecord(getFieldValue(data, "code_review_rate_limit", "codeReviewRateLimit"));
176
- const reviewRateLimit = Object.keys(dedicatedReviewRateLimit).length > 0
177
- ? dedicatedReviewRateLimit
178
- : findCodexReviewRateLimit(data);
179
-
180
- const codeReviewWindow = toRecord(getFieldValue(reviewRateLimit, "primary_window", "primaryWindow"));
181
- if (getFieldValue(codeReviewWindow, "used_percent", "usedPercent") !== null ||
182
- getFieldValue(codeReviewWindow, "remaining_count", "remainingCount") !== null) {
183
- quotas.code_review = buildPercentageQuota(codeReviewWindow);
66
+ function getCodexReviewRateLimit(data) {
67
+ if (data.code_review_rate_limit || data.review_rate_limit) {
68
+ return data.code_review_rate_limit || data.review_rate_limit;
184
69
  }
185
70
 
186
- const codeReviewSecondaryWindow = toRecord(getFieldValue(reviewRateLimit, "secondary_window", "secondaryWindow"));
187
- if (getFieldValue(codeReviewSecondaryWindow, "used_percent", "usedPercent") !== null ||
188
- getFieldValue(codeReviewSecondaryWindow, "remaining_count", "remainingCount") !== null) {
189
- quotas.code_review_weekly = buildPercentageQuota(codeReviewSecondaryWindow);
190
- }
191
-
192
- // Spark quota (additional_rate_limits)
193
- const sparkRateLimit = findCodexSparkRateLimit(data);
194
- const sparkPrimaryWindow = toRecord(getFieldValue(sparkRateLimit, "primary_window", "primaryWindow"));
195
- if (Object.keys(sparkPrimaryWindow).length > 0) {
196
- quotas.spark = buildPercentageQuota(sparkPrimaryWindow, "Spark");
197
- }
198
- const sparkSecondaryWindow = toRecord(getFieldValue(sparkRateLimit, "secondary_window", "secondaryWindow"));
199
- if (Object.keys(sparkSecondaryWindow).length > 0) {
200
- quotas.spark_weekly = buildPercentageQuota(sparkSecondaryWindow, "Spark Weekly");
71
+ const byLimitId = data.rate_limits_by_limit_id;
72
+ if (byLimitId && typeof byLimitId === "object" && !Array.isArray(byLimitId)) {
73
+ return byLimitId.code_review || byLimitId.codex_review || byLimitId.review || null;
201
74
  }
202
75
 
203
- return { rateLimit, quotas, bankedResetCredits, rateLimitReachedType };
76
+ const additional = Array.isArray(data.additional_rate_limits) ? data.additional_rate_limits : [];
77
+ return additional.find((entry) => {
78
+ const id = String(entry?.limit_name || entry?.metered_feature || entry?.id || "").toLowerCase();
79
+ return id === "code_review" || id === "codex_review" || id === "review" || id.includes("review");
80
+ }) || null;
204
81
  }
205
82
 
206
- /**
207
- * Codex (OpenAI) Usage - Fetch from ChatGPT /wham/usage API
208
- * Uses persisted workspaceId from OAuth for correct workspace binding.
209
- */
210
- export async function getCodexUsage(accessToken, providerSpecificData = {}) {
83
+ export async function getCodexUsage(accessToken, proxyOptions = null) {
211
84
  try {
212
- const accountId = typeof providerSpecificData?.workspaceId === "string"
213
- ? providerSpecificData.workspaceId
214
- : null;
215
-
216
- const headers = {
217
- Authorization: `Bearer ${accessToken}`,
218
- "Content-Type": "application/json",
219
- Accept: "application/json",
220
- };
221
- if (accountId) {
222
- headers["chatgpt-account-id"] = accountId;
223
- }
224
-
225
85
  const response = await proxyAwareFetch(CODEX_CONFIG.usageUrl, {
226
86
  method: "GET",
227
- headers,
228
- });
87
+ headers: {
88
+ "Authorization": `Bearer ${accessToken}`,
89
+ "Accept": "application/json",
90
+ },
91
+ }, proxyOptions);
229
92
 
230
93
  if (!response.ok) {
231
- if (response.status === 401 || response.status === 403) {
232
- return {
233
- message: "Codex token expired or access denied. Please re-authenticate the connection.",
234
- };
235
- }
236
- throw new Error(`Codex API error: ${response.status}`);
94
+ return { message: `Codex connected. Usage API temporarily unavailable (${response.status}).` };
237
95
  }
238
96
 
239
97
  const data = await response.json();
240
- const { rateLimit, quotas, bankedResetCredits, rateLimitReachedType } = buildCodexUsageQuotas(data);
98
+ const normalRateLimit = data.rate_limit || data.rate_limits || data.rate_limits_by_limit_id?.codex || {};
99
+ const reviewRateLimit = getCodexReviewRateLimit(data);
100
+ const availableResetCredits = Math.max(0, toFiniteNumber(data.rate_limit_reset_credits?.available_count, 0));
101
+ const quotas = {};
102
+
103
+ appendCodexQuotaWindows(quotas, "", normalRateLimit);
104
+ appendCodexQuotaWindows(quotas, "review", reviewRateLimit);
241
105
 
242
106
  return {
243
- plan: String(getFieldValue(data, "plan_type", "planType") || "unknown"),
244
- limitReached: Boolean(getFieldValue(rateLimit, "limit_reached", "limitReached")),
107
+ plan: data.plan_type || data.summary?.plan || "unknown",
108
+ limitReached: getCodexRateLimitBody(normalRateLimit)?.limit_reached || false,
109
+ reviewLimitReached: getCodexRateLimitBody(reviewRateLimit)?.limit_reached || false,
110
+ resetCredits: { availableCount: availableResetCredits },
245
111
  quotas,
246
- ...(bankedResetCredits !== undefined ? { bankedResetCredits } : {}),
247
- ...(rateLimitReachedType !== undefined ? { rateLimitReachedType } : {}),
248
112
  };
249
113
  } catch (error) {
250
- return { message: `Failed to fetch Codex usage: ${error.message}` };
114
+ throw new Error(`Failed to fetch Codex usage: ${error.message}`);
251
115
  }
252
116
  }
253
117
 
254
- // Stub: consume a banked reset credit (display-only, not implemented in AMRouter)
255
- export async function consumeCodexRateLimitResetCredit(accessToken, providerSpecificData = {}) {
256
- return { success: false, message: "Reset credit consumption not supported" };
257
- }
118
+ export async function getCodexRateLimitResetCredits(accessToken, proxyOptions = null, providerSpecificData = null) {
119
+ if (!accessToken) {
120
+ throw new Error("No Codex access token available. Please re-authorize the connection.");
121
+ }
122
+
123
+ const accountId = getCodexAccountId(providerSpecificData);
124
+ const headers = {
125
+ "Authorization": `Bearer ${accessToken}`,
126
+ "Accept": "application/json",
127
+ "OpenAI-Beta": "codex-1",
128
+ "originator": "codex_cli_rs",
129
+ };
130
+ if (accountId) headers["ChatGPT-Account-ID"] = accountId;
131
+
132
+ const response = await proxyAwareFetch(CODEX_CONFIG.resetCreditsUrl, {
133
+ method: "GET",
134
+ headers,
135
+ }, proxyOptions);
258
136
 
259
- // Stub: get banked reset credits
260
- export async function getCodexRateLimitResetCredits(accessToken, providerSpecificData = {}) {
137
+ let data = null;
261
138
  try {
262
- const result = await getCodexUsage(accessToken, providerSpecificData);
263
- return { credits: result.bankedResetCredits || 0 };
139
+ data = await response.json();
264
140
  } catch {
265
- return { credits: 0 };
141
+ data = null;
266
142
  }
143
+
144
+ if (!response.ok) {
145
+ const message = data?.message || data?.error || data?.detail || `Codex reset credits API unavailable (${response.status}).`;
146
+ throw new Error(message);
147
+ }
148
+
149
+ const credits = Array.isArray(data?.credits) ? data.credits : [];
150
+ return {
151
+ availableCount: Math.max(0, toFiniteNumber(data?.available_count ?? data?.availableCount, 0)),
152
+ credits: credits.map((credit) => ({
153
+ status: String(credit?.status || "unknown"),
154
+ grantedAt: toIsoDate(credit?.granted_at ?? credit?.grantedAt),
155
+ expiresAt: toIsoDate(credit?.expires_at ?? credit?.expiresAt),
156
+ })),
157
+ };
158
+ }
159
+
160
+ // Consume one Codex rate-limit reset credit (irreversible, spends 1 credit)
161
+ export async function consumeCodexRateLimitResetCredit(accessToken, redeemRequestId, proxyOptions = null) {
162
+ if (!accessToken) {
163
+ throw new Error("No Codex access token available. Please re-authorize the connection.");
164
+ }
165
+ if (!redeemRequestId || typeof redeemRequestId !== "string") {
166
+ throw new Error("A redeem request id is required to consume a Codex reset credit.");
167
+ }
168
+
169
+ let response;
170
+ let data = null;
171
+ try {
172
+ response = await proxyAwareFetch(CODEX_CONFIG.resetCreditsConsumeUrl, {
173
+ method: "POST",
174
+ headers: {
175
+ "Authorization": `Bearer ${accessToken}`,
176
+ "Accept": "application/json",
177
+ "Content-Type": "application/json",
178
+ },
179
+ body: JSON.stringify({ redeem_request_id: redeemRequestId }),
180
+ }, proxyOptions);
181
+
182
+ const text = await response.text();
183
+ data = text ? JSON.parse(text) : null;
184
+ } catch (error) {
185
+ throw new Error(`Failed to consume Codex reset credit: ${error.message}`);
186
+ }
187
+
188
+ const code = data?.code || null;
189
+ const windowsReset = toFiniteNumber(data?.windows_reset, 0);
190
+ const success = response.ok && (code === "reset" || windowsReset > 0);
191
+
192
+ return {
193
+ ok: success,
194
+ noCredit: response.ok && code === "no_credit",
195
+ status: response.status,
196
+ code,
197
+ windowsReset,
198
+ message: data?.message || null,
199
+ raw: data,
200
+ };
267
201
  }