@gajae-code/ai 0.1.1

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 (349) hide show
  1. package/CHANGELOG.md +2644 -0
  2. package/README.md +1181 -0
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth-broker/client.d.ts +66 -0
  5. package/dist/types/auth-broker/index.d.ts +5 -0
  6. package/dist/types/auth-broker/refresher.d.ts +25 -0
  7. package/dist/types/auth-broker/remote-store.d.ts +96 -0
  8. package/dist/types/auth-broker/server.d.ts +32 -0
  9. package/dist/types/auth-broker/types.d.ts +105 -0
  10. package/dist/types/auth-broker/wire-schemas.d.ts +412 -0
  11. package/dist/types/auth-gateway/http.d.ts +39 -0
  12. package/dist/types/auth-gateway/index.d.ts +3 -0
  13. package/dist/types/auth-gateway/server.d.ts +17 -0
  14. package/dist/types/auth-gateway/types.d.ts +115 -0
  15. package/dist/types/auth-storage.d.ts +641 -0
  16. package/dist/types/cli.d.ts +2 -0
  17. package/dist/types/index.d.ts +49 -0
  18. package/dist/types/model-cache.d.ts +17 -0
  19. package/dist/types/model-manager.d.ts +62 -0
  20. package/dist/types/model-thinking.d.ts +71 -0
  21. package/dist/types/models.d.ts +12 -0
  22. package/dist/types/provider-details.d.ts +24 -0
  23. package/dist/types/provider-models/bundled-references.d.ts +4 -0
  24. package/dist/types/provider-models/descriptors.d.ts +48 -0
  25. package/dist/types/provider-models/google.d.ts +20 -0
  26. package/dist/types/provider-models/index.d.ts +5 -0
  27. package/dist/types/provider-models/ollama.d.ts +7 -0
  28. package/dist/types/provider-models/openai-compat.d.ts +237 -0
  29. package/dist/types/provider-models/special.d.ts +16 -0
  30. package/dist/types/providers/amazon-bedrock.d.ts +36 -0
  31. package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
  32. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  33. package/dist/types/providers/anthropic.d.ts +188 -0
  34. package/dist/types/providers/aws-credentials.d.ts +43 -0
  35. package/dist/types/providers/aws-eventstream.d.ts +38 -0
  36. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  37. package/dist/types/providers/azure-openai-responses.d.ts +15 -0
  38. package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
  39. package/dist/types/providers/cursor.d.ts +42 -0
  40. package/dist/types/providers/error-message.d.ts +27 -0
  41. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  42. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  43. package/dist/types/providers/google-auth.d.ts +24 -0
  44. package/dist/types/providers/google-gemini-cli.d.ts +72 -0
  45. package/dist/types/providers/google-gemini-headers.d.ts +18 -0
  46. package/dist/types/providers/google-shared.d.ts +163 -0
  47. package/dist/types/providers/google-types.d.ts +138 -0
  48. package/dist/types/providers/google-vertex.d.ts +7 -0
  49. package/dist/types/providers/google.d.ts +4 -0
  50. package/dist/types/providers/grammar.d.ts +1 -0
  51. package/dist/types/providers/kimi.d.ts +27 -0
  52. package/dist/types/providers/mock.d.ts +175 -0
  53. package/dist/types/providers/ollama.d.ts +6 -0
  54. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  55. package/dist/types/providers/openai-chat-server-schema.d.ts +814 -0
  56. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  57. package/dist/types/providers/openai-codex/constants.d.ts +26 -0
  58. package/dist/types/providers/openai-codex/request-transformer.d.ts +49 -0
  59. package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
  60. package/dist/types/providers/openai-codex-responses.d.ts +67 -0
  61. package/dist/types/providers/openai-completions-compat.d.ts +25 -0
  62. package/dist/types/providers/openai-completions.d.ts +33 -0
  63. package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
  64. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  65. package/dist/types/providers/openai-responses-shared.d.ts +89 -0
  66. package/dist/types/providers/openai-responses.d.ts +32 -0
  67. package/dist/types/providers/pi-native-client.d.ts +13 -0
  68. package/dist/types/providers/pi-native-server.d.ts +68 -0
  69. package/dist/types/providers/register-builtins.d.ts +31 -0
  70. package/dist/types/providers/synthetic.d.ts +26 -0
  71. package/dist/types/providers/transform-messages.d.ts +12 -0
  72. package/dist/types/providers/vision-guard.d.ts +8 -0
  73. package/dist/types/rate-limit-utils.d.ts +19 -0
  74. package/dist/types/stream.d.ts +24 -0
  75. package/dist/types/types.d.ts +746 -0
  76. package/dist/types/usage/claude.d.ts +3 -0
  77. package/dist/types/usage/gemini.d.ts +2 -0
  78. package/dist/types/usage/github-copilot.d.ts +7 -0
  79. package/dist/types/usage/google-antigravity.d.ts +2 -0
  80. package/dist/types/usage/kimi.d.ts +2 -0
  81. package/dist/types/usage/minimax-code.d.ts +2 -0
  82. package/dist/types/usage/openai-codex.d.ts +3 -0
  83. package/dist/types/usage/shared.d.ts +1 -0
  84. package/dist/types/usage/zai.d.ts +2 -0
  85. package/dist/types/usage.d.ts +258 -0
  86. package/dist/types/utils/abort.d.ts +19 -0
  87. package/dist/types/utils/anthropic-auth.d.ts +31 -0
  88. package/dist/types/utils/discovery/antigravity.d.ts +61 -0
  89. package/dist/types/utils/discovery/codex.d.ts +38 -0
  90. package/dist/types/utils/discovery/cursor.d.ts +23 -0
  91. package/dist/types/utils/discovery/gemini.d.ts +25 -0
  92. package/dist/types/utils/discovery/index.d.ts +4 -0
  93. package/dist/types/utils/discovery/openai-compatible.d.ts +72 -0
  94. package/dist/types/utils/event-stream.d.ts +28 -0
  95. package/dist/types/utils/fireworks-model-id.d.ts +10 -0
  96. package/dist/types/utils/foundry.d.ts +1 -0
  97. package/dist/types/utils/h2-fetch.d.ts +22 -0
  98. package/dist/types/utils/http-inspector.d.ts +31 -0
  99. package/dist/types/utils/idle-iterator.d.ts +67 -0
  100. package/dist/types/utils/json-parse.d.ts +10 -0
  101. package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
  102. package/dist/types/utils/oauth/anthropic.d.ts +22 -0
  103. package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
  104. package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
  105. package/dist/types/utils/oauth/callback-server.d.ts +57 -0
  106. package/dist/types/utils/oauth/cerebras.d.ts +1 -0
  107. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
  108. package/dist/types/utils/oauth/cursor.d.ts +15 -0
  109. package/dist/types/utils/oauth/deepseek.d.ts +10 -0
  110. package/dist/types/utils/oauth/firepass.d.ts +1 -0
  111. package/dist/types/utils/oauth/fireworks.d.ts +1 -0
  112. package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
  113. package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
  114. package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
  115. package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
  116. package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
  117. package/dist/types/utils/oauth/huggingface.d.ts +19 -0
  118. package/dist/types/utils/oauth/index.d.ts +38 -0
  119. package/dist/types/utils/oauth/kagi.d.ts +17 -0
  120. package/dist/types/utils/oauth/kilo.d.ts +5 -0
  121. package/dist/types/utils/oauth/kimi.d.ts +21 -0
  122. package/dist/types/utils/oauth/litellm.d.ts +18 -0
  123. package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
  124. package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
  125. package/dist/types/utils/oauth/moonshot.d.ts +1 -0
  126. package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
  127. package/dist/types/utils/oauth/nvidia.d.ts +18 -0
  128. package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
  129. package/dist/types/utils/oauth/ollama.d.ts +18 -0
  130. package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
  131. package/dist/types/utils/oauth/opencode.d.ts +18 -0
  132. package/dist/types/utils/oauth/parallel.d.ts +17 -0
  133. package/dist/types/utils/oauth/perplexity.d.ts +9 -0
  134. package/dist/types/utils/oauth/pkce.d.ts +8 -0
  135. package/dist/types/utils/oauth/qianfan.d.ts +17 -0
  136. package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
  137. package/dist/types/utils/oauth/synthetic.d.ts +1 -0
  138. package/dist/types/utils/oauth/tavily.d.ts +17 -0
  139. package/dist/types/utils/oauth/together.d.ts +1 -0
  140. package/dist/types/utils/oauth/types.d.ts +44 -0
  141. package/dist/types/utils/oauth/venice.d.ts +18 -0
  142. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
  143. package/dist/types/utils/oauth/vllm.d.ts +16 -0
  144. package/dist/types/utils/oauth/xiaomi.d.ts +19 -0
  145. package/dist/types/utils/oauth/zai.d.ts +18 -0
  146. package/dist/types/utils/oauth/zenmux.d.ts +1 -0
  147. package/dist/types/utils/overflow.d.ts +54 -0
  148. package/dist/types/utils/parse-bind.d.ts +23 -0
  149. package/dist/types/utils/provider-response.d.ts +3 -0
  150. package/dist/types/utils/retry-after.d.ts +3 -0
  151. package/dist/types/utils/retry.d.ts +26 -0
  152. package/dist/types/utils/schema/adapt.d.ts +24 -0
  153. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  154. package/dist/types/utils/schema/dereference.d.ts +11 -0
  155. package/dist/types/utils/schema/draft.d.ts +10 -0
  156. package/dist/types/utils/schema/equality.d.ts +4 -0
  157. package/dist/types/utils/schema/fields.d.ts +49 -0
  158. package/dist/types/utils/schema/index.d.ts +13 -0
  159. package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
  160. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  161. package/dist/types/utils/schema/normalize.d.ts +93 -0
  162. package/dist/types/utils/schema/spill.d.ts +8 -0
  163. package/dist/types/utils/schema/stamps.d.ts +25 -0
  164. package/dist/types/utils/schema/types.d.ts +4 -0
  165. package/dist/types/utils/schema/wire.d.ts +54 -0
  166. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  167. package/dist/types/utils/sse-debug.d.ts +10 -0
  168. package/dist/types/utils/tool-call-healing.d.ts +71 -0
  169. package/dist/types/utils/tool-choice.d.ts +50 -0
  170. package/dist/types/utils/validation.d.ts +17 -0
  171. package/dist/types/utils.d.ts +28 -0
  172. package/package.json +146 -0
  173. package/src/api-registry.ts +96 -0
  174. package/src/auth-broker/client.ts +358 -0
  175. package/src/auth-broker/index.ts +5 -0
  176. package/src/auth-broker/refresher.ts +127 -0
  177. package/src/auth-broker/remote-store.ts +623 -0
  178. package/src/auth-broker/server.ts +644 -0
  179. package/src/auth-broker/types.ts +127 -0
  180. package/src/auth-broker/wire-schemas.ts +200 -0
  181. package/src/auth-gateway/http.ts +194 -0
  182. package/src/auth-gateway/index.ts +3 -0
  183. package/src/auth-gateway/server.ts +717 -0
  184. package/src/auth-gateway/types.ts +134 -0
  185. package/src/auth-storage.ts +4104 -0
  186. package/src/cli.ts +262 -0
  187. package/src/index.ts +54 -0
  188. package/src/model-cache.ts +129 -0
  189. package/src/model-manager.ts +450 -0
  190. package/src/model-thinking.ts +691 -0
  191. package/src/models.json +73853 -0
  192. package/src/models.json.d.ts +9 -0
  193. package/src/models.ts +56 -0
  194. package/src/prompts/turn-aborted-guidance.md +4 -0
  195. package/src/provider-details.ts +90 -0
  196. package/src/provider-models/bundled-references.ts +38 -0
  197. package/src/provider-models/descriptors.ts +308 -0
  198. package/src/provider-models/google.ts +91 -0
  199. package/src/provider-models/index.ts +5 -0
  200. package/src/provider-models/ollama.ts +153 -0
  201. package/src/provider-models/openai-compat.ts +2275 -0
  202. package/src/provider-models/special.ts +67 -0
  203. package/src/providers/amazon-bedrock.ts +849 -0
  204. package/src/providers/anthropic-messages-server-schema.ts +229 -0
  205. package/src/providers/anthropic-messages-server.ts +677 -0
  206. package/src/providers/anthropic.ts +2696 -0
  207. package/src/providers/aws-credentials.ts +501 -0
  208. package/src/providers/aws-eventstream.ts +185 -0
  209. package/src/providers/aws-sigv4.ts +218 -0
  210. package/src/providers/azure-openai-responses.ts +337 -0
  211. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  212. package/src/providers/cursor/proto/agent.proto +3526 -0
  213. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  214. package/src/providers/cursor/proto/buf.yaml +17 -0
  215. package/src/providers/cursor.ts +2561 -0
  216. package/src/providers/error-message.ts +21 -0
  217. package/src/providers/github-copilot-headers.ts +140 -0
  218. package/src/providers/gitlab-duo.ts +372 -0
  219. package/src/providers/google-auth.ts +252 -0
  220. package/src/providers/google-gemini-cli.ts +795 -0
  221. package/src/providers/google-gemini-headers.ts +41 -0
  222. package/src/providers/google-shared.ts +902 -0
  223. package/src/providers/google-types.ts +167 -0
  224. package/src/providers/google-vertex.ts +88 -0
  225. package/src/providers/google.ts +41 -0
  226. package/src/providers/grammar.ts +70 -0
  227. package/src/providers/kimi.ts +52 -0
  228. package/src/providers/mock.ts +500 -0
  229. package/src/providers/ollama.ts +544 -0
  230. package/src/providers/openai-anthropic-shim.ts +138 -0
  231. package/src/providers/openai-chat-server-schema.ts +243 -0
  232. package/src/providers/openai-chat-server.ts +628 -0
  233. package/src/providers/openai-codex/constants.ts +43 -0
  234. package/src/providers/openai-codex/request-transformer.ts +161 -0
  235. package/src/providers/openai-codex/response-handler.ts +81 -0
  236. package/src/providers/openai-codex-responses.ts +2598 -0
  237. package/src/providers/openai-completions-compat.ts +279 -0
  238. package/src/providers/openai-completions.ts +1853 -0
  239. package/src/providers/openai-responses-server-schema.ts +290 -0
  240. package/src/providers/openai-responses-server.ts +1183 -0
  241. package/src/providers/openai-responses-shared.ts +800 -0
  242. package/src/providers/openai-responses.ts +621 -0
  243. package/src/providers/pi-native-client.ts +228 -0
  244. package/src/providers/pi-native-server.ts +210 -0
  245. package/src/providers/register-builtins.ts +412 -0
  246. package/src/providers/synthetic.ts +50 -0
  247. package/src/providers/transform-messages.ts +309 -0
  248. package/src/providers/vision-guard.ts +31 -0
  249. package/src/rate-limit-utils.ts +84 -0
  250. package/src/stream.ts +895 -0
  251. package/src/types.ts +884 -0
  252. package/src/usage/claude.ts +431 -0
  253. package/src/usage/gemini.ts +250 -0
  254. package/src/usage/github-copilot.ts +421 -0
  255. package/src/usage/google-antigravity.ts +201 -0
  256. package/src/usage/kimi.ts +271 -0
  257. package/src/usage/minimax-code.ts +31 -0
  258. package/src/usage/openai-codex.ts +503 -0
  259. package/src/usage/shared.ts +10 -0
  260. package/src/usage/zai.ts +247 -0
  261. package/src/usage.ts +183 -0
  262. package/src/utils/abort.ts +51 -0
  263. package/src/utils/anthropic-auth.ts +87 -0
  264. package/src/utils/discovery/antigravity.ts +261 -0
  265. package/src/utils/discovery/codex.ts +371 -0
  266. package/src/utils/discovery/cursor.ts +306 -0
  267. package/src/utils/discovery/gemini.ts +248 -0
  268. package/src/utils/discovery/index.ts +4 -0
  269. package/src/utils/discovery/openai-compatible.ts +224 -0
  270. package/src/utils/event-stream.ts +142 -0
  271. package/src/utils/fireworks-model-id.ts +30 -0
  272. package/src/utils/foundry.ts +8 -0
  273. package/src/utils/h2-fetch.ts +60 -0
  274. package/src/utils/http-inspector.ts +176 -0
  275. package/src/utils/idle-iterator.ts +250 -0
  276. package/src/utils/json-parse.ts +148 -0
  277. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  278. package/src/utils/oauth/anthropic.ts +200 -0
  279. package/src/utils/oauth/api-key-login.ts +87 -0
  280. package/src/utils/oauth/api-key-validation.ts +92 -0
  281. package/src/utils/oauth/callback-server.ts +276 -0
  282. package/src/utils/oauth/cerebras.ts +16 -0
  283. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  284. package/src/utils/oauth/cursor.ts +157 -0
  285. package/src/utils/oauth/deepseek.ts +53 -0
  286. package/src/utils/oauth/firepass.ts +24 -0
  287. package/src/utils/oauth/fireworks.ts +15 -0
  288. package/src/utils/oauth/github-copilot.ts +362 -0
  289. package/src/utils/oauth/gitlab-duo.ts +123 -0
  290. package/src/utils/oauth/google-antigravity.ts +200 -0
  291. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  292. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  293. package/src/utils/oauth/huggingface.ts +62 -0
  294. package/src/utils/oauth/index.ts +444 -0
  295. package/src/utils/oauth/kagi.ts +47 -0
  296. package/src/utils/oauth/kilo.ts +87 -0
  297. package/src/utils/oauth/kimi.ts +254 -0
  298. package/src/utils/oauth/litellm.ts +47 -0
  299. package/src/utils/oauth/lm-studio.ts +38 -0
  300. package/src/utils/oauth/minimax-code.ts +78 -0
  301. package/src/utils/oauth/moonshot.ts +16 -0
  302. package/src/utils/oauth/nanogpt.ts +15 -0
  303. package/src/utils/oauth/nvidia.ts +70 -0
  304. package/src/utils/oauth/oauth.html +199 -0
  305. package/src/utils/oauth/ollama-cloud.ts +28 -0
  306. package/src/utils/oauth/ollama.ts +47 -0
  307. package/src/utils/oauth/openai-codex.ts +299 -0
  308. package/src/utils/oauth/opencode.ts +49 -0
  309. package/src/utils/oauth/parallel.ts +46 -0
  310. package/src/utils/oauth/perplexity.ts +206 -0
  311. package/src/utils/oauth/pkce.ts +18 -0
  312. package/src/utils/oauth/qianfan.ts +58 -0
  313. package/src/utils/oauth/qwen-portal.ts +60 -0
  314. package/src/utils/oauth/synthetic.ts +16 -0
  315. package/src/utils/oauth/tavily.ts +46 -0
  316. package/src/utils/oauth/together.ts +16 -0
  317. package/src/utils/oauth/types.ts +94 -0
  318. package/src/utils/oauth/venice.ts +59 -0
  319. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  320. package/src/utils/oauth/vllm.ts +40 -0
  321. package/src/utils/oauth/xiaomi.ts +137 -0
  322. package/src/utils/oauth/zai.ts +60 -0
  323. package/src/utils/oauth/zenmux.ts +15 -0
  324. package/src/utils/overflow.ts +137 -0
  325. package/src/utils/parse-bind.ts +54 -0
  326. package/src/utils/provider-response.ts +30 -0
  327. package/src/utils/retry-after.ts +110 -0
  328. package/src/utils/retry.ts +54 -0
  329. package/src/utils/schema/CONSTRAINTS.md +164 -0
  330. package/src/utils/schema/adapt.ts +36 -0
  331. package/src/utils/schema/compatibility.ts +435 -0
  332. package/src/utils/schema/dereference.ts +98 -0
  333. package/src/utils/schema/draft.ts +341 -0
  334. package/src/utils/schema/equality.ts +97 -0
  335. package/src/utils/schema/fields.ts +190 -0
  336. package/src/utils/schema/index.ts +13 -0
  337. package/src/utils/schema/json-schema-validator.ts +577 -0
  338. package/src/utils/schema/meta-validator.ts +167 -0
  339. package/src/utils/schema/normalize.ts +1588 -0
  340. package/src/utils/schema/spill.ts +43 -0
  341. package/src/utils/schema/stamps.ts +97 -0
  342. package/src/utils/schema/types.ts +11 -0
  343. package/src/utils/schema/wire.ts +213 -0
  344. package/src/utils/schema/zod-decontaminate.ts +331 -0
  345. package/src/utils/sse-debug.ts +289 -0
  346. package/src/utils/tool-call-healing.ts +271 -0
  347. package/src/utils/tool-choice.ts +99 -0
  348. package/src/utils/validation.ts +1019 -0
  349. package/src/utils.ts +166 -0
@@ -0,0 +1,503 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { CODEX_BASE_URL } from "../providers/openai-codex/constants";
3
+ import type {
4
+ CredentialRankingStrategy,
5
+ UsageAmount,
6
+ UsageFetchContext,
7
+ UsageFetchParams,
8
+ UsageLimit,
9
+ UsageProvider,
10
+ UsageReport,
11
+ UsageWindow,
12
+ } from "../usage";
13
+ import { isRecord } from "../utils";
14
+ import { toNumber } from "./shared";
15
+
16
+ const CODEX_USAGE_PATH = "wham/usage";
17
+ const JWT_AUTH_CLAIM = "https://api.openai.com/auth";
18
+ const JWT_PROFILE_CLAIM = "https://api.openai.com/profile";
19
+
20
+ interface CodexUsageWindowPayload {
21
+ used_percent?: number;
22
+ limit_window_seconds?: number;
23
+ reset_after_seconds?: number;
24
+ reset_at?: number;
25
+ }
26
+
27
+ interface CodexUsageRateLimitPayload {
28
+ allowed?: boolean;
29
+ limit_reached?: boolean;
30
+ primary_window?: CodexUsageWindowPayload | null;
31
+ secondary_window?: CodexUsageWindowPayload | null;
32
+ }
33
+
34
+ interface CodexUsageAdditionalRateLimitPayload {
35
+ limit_name?: string;
36
+ metered_feature?: string;
37
+ rate_limit?: CodexUsageRateLimitPayload | null;
38
+ }
39
+
40
+ interface CodexUsagePayload {
41
+ plan_type?: string;
42
+ rate_limit?: CodexUsageRateLimitPayload | null;
43
+ additional_rate_limits?: CodexUsageAdditionalRateLimitPayload[] | null;
44
+ }
45
+
46
+ interface ParsedUsageWindow {
47
+ usedPercent?: number;
48
+ limitWindowSeconds?: number;
49
+ resetAfterSeconds?: number;
50
+ resetAt?: number;
51
+ }
52
+
53
+ interface ParsedAdditionalUsage {
54
+ limitName?: string;
55
+ meteredFeature?: string;
56
+ allowed?: boolean;
57
+ limitReached?: boolean;
58
+ primary?: ParsedUsageWindow;
59
+ secondary?: ParsedUsageWindow;
60
+ }
61
+
62
+ interface ParsedUsage {
63
+ planType?: string;
64
+ allowed?: boolean;
65
+ limitReached?: boolean;
66
+ primary?: ParsedUsageWindow;
67
+ secondary?: ParsedUsageWindow;
68
+ additional: ParsedAdditionalUsage[];
69
+ raw: CodexUsagePayload;
70
+ }
71
+
72
+ interface JwtPayload {
73
+ [JWT_AUTH_CLAIM]?: {
74
+ chatgpt_account_id?: string;
75
+ };
76
+ [JWT_PROFILE_CLAIM]?: {
77
+ email?: string;
78
+ };
79
+ }
80
+
81
+ const toBoolean = (value: unknown): boolean | undefined => {
82
+ if (typeof value === "boolean") return value;
83
+ return undefined;
84
+ };
85
+
86
+ function base64UrlDecode(input: string): string {
87
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
88
+ const padLen = (4 - (base64.length % 4)) % 4;
89
+ const padded = base64 + "=".repeat(padLen);
90
+ return Buffer.from(padded, "base64").toString("utf8");
91
+ }
92
+
93
+ function parseJwt(token: string): JwtPayload | null {
94
+ const parts = token.split(".");
95
+ if (parts.length !== 3) return null;
96
+ try {
97
+ const payloadJson = base64UrlDecode(parts[1]);
98
+ return JSON.parse(payloadJson) as JwtPayload;
99
+ } catch {
100
+ return null;
101
+ }
102
+ }
103
+
104
+ function normalizeEmail(email: string | undefined): string | undefined {
105
+ if (!email) return undefined;
106
+ const normalized = email.trim().toLowerCase();
107
+ return normalized || undefined;
108
+ }
109
+
110
+ function extractAccountId(token: string | undefined): string | undefined {
111
+ if (!token) return undefined;
112
+ const payload = parseJwt(token);
113
+ return payload?.[JWT_AUTH_CLAIM]?.chatgpt_account_id ?? undefined;
114
+ }
115
+
116
+ function extractEmail(token: string | undefined): string | undefined {
117
+ if (!token) return undefined;
118
+ const payload = parseJwt(token);
119
+ return normalizeEmail(payload?.[JWT_PROFILE_CLAIM]?.email);
120
+ }
121
+
122
+ function parseUsageWindow(payload: unknown): ParsedUsageWindow | undefined {
123
+ if (!isRecord(payload)) return undefined;
124
+ const usedPercent = toNumber(payload.used_percent);
125
+ const limitWindowSeconds = toNumber(payload.limit_window_seconds);
126
+ const resetAfterSeconds = toNumber(payload.reset_after_seconds);
127
+ const resetAt = toNumber(payload.reset_at);
128
+ if (
129
+ usedPercent === undefined &&
130
+ limitWindowSeconds === undefined &&
131
+ resetAfterSeconds === undefined &&
132
+ resetAt === undefined
133
+ ) {
134
+ return undefined;
135
+ }
136
+ return {
137
+ usedPercent,
138
+ limitWindowSeconds,
139
+ resetAfterSeconds,
140
+ resetAt,
141
+ };
142
+ }
143
+
144
+ function parseAdditionalRateLimit(payload: unknown): ParsedAdditionalUsage | null {
145
+ if (!isRecord(payload)) return null;
146
+ const limitName = typeof payload.limit_name === "string" ? payload.limit_name : undefined;
147
+ const meteredFeature = typeof payload.metered_feature === "string" ? payload.metered_feature : undefined;
148
+ const rateLimit = isRecord(payload.rate_limit) ? payload.rate_limit : undefined;
149
+ if (!rateLimit) return null;
150
+ const primary = parseUsageWindow(rateLimit.primary_window);
151
+ const secondary = parseUsageWindow(rateLimit.secondary_window);
152
+ const allowed = toBoolean(rateLimit.allowed);
153
+ const limitReached = toBoolean(rateLimit.limit_reached);
154
+ if (!primary && !secondary && allowed === undefined && limitReached === undefined) return null;
155
+ return { limitName, meteredFeature, allowed, limitReached, primary, secondary };
156
+ }
157
+
158
+ function parseUsagePayload(payload: unknown): ParsedUsage | null {
159
+ if (!isRecord(payload)) return null;
160
+ const planType = typeof payload.plan_type === "string" ? payload.plan_type : undefined;
161
+ const rateLimit = isRecord(payload.rate_limit) ? payload.rate_limit : undefined;
162
+ const additionalRaw = Array.isArray(payload.additional_rate_limits) ? payload.additional_rate_limits : [];
163
+ const additional = additionalRaw
164
+ .map(parseAdditionalRateLimit)
165
+ .filter((value): value is ParsedAdditionalUsage => value !== null);
166
+ if (!rateLimit && additional.length === 0) return null;
167
+ const parsed: ParsedUsage = {
168
+ planType,
169
+ allowed: rateLimit ? toBoolean(rateLimit.allowed) : undefined,
170
+ limitReached: rateLimit ? toBoolean(rateLimit.limit_reached) : undefined,
171
+ primary: rateLimit ? parseUsageWindow(rateLimit.primary_window) : undefined,
172
+ secondary: rateLimit ? parseUsageWindow(rateLimit.secondary_window) : undefined,
173
+ additional,
174
+ raw: payload as CodexUsagePayload,
175
+ };
176
+ if (
177
+ !parsed.primary &&
178
+ !parsed.secondary &&
179
+ parsed.allowed === undefined &&
180
+ parsed.limitReached === undefined &&
181
+ parsed.additional.length === 0
182
+ ) {
183
+ return null;
184
+ }
185
+ return parsed;
186
+ }
187
+
188
+ function normalizeCodexBaseUrl(baseUrl?: string): string {
189
+ const fallback = CODEX_BASE_URL;
190
+ const trimmed = baseUrl?.trim() ? baseUrl.trim() : fallback;
191
+ const base = trimmed.replace(/\/+$/, "");
192
+ const lower = base.toLowerCase();
193
+ if (
194
+ (lower.startsWith("https://chatgpt.com") || lower.startsWith("https://chat.openai.com")) &&
195
+ !lower.includes("/backend-api")
196
+ ) {
197
+ return `${base}/backend-api`;
198
+ }
199
+ return base;
200
+ }
201
+
202
+ function buildCodexUsageUrl(baseUrl: string): string {
203
+ const normalized = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
204
+ return `${normalized}${CODEX_USAGE_PATH}`;
205
+ }
206
+
207
+ function formatWindowLabel(value: number, unit: "hour" | "day"): string {
208
+ const rounded = Math.round(value);
209
+ const suffix = rounded === 1 ? unit : `${unit}s`;
210
+ return `${rounded} ${suffix}`;
211
+ }
212
+
213
+ function buildWindowLabel(seconds: number): { id: string; label: string } {
214
+ const daySeconds = 86_400;
215
+ if (seconds >= daySeconds) {
216
+ const days = Math.round(seconds / daySeconds);
217
+ return { id: `${days}d`, label: formatWindowLabel(days, "day") };
218
+ }
219
+ const hours = Math.max(1, Math.round(seconds / 3600));
220
+ return { id: `${hours}h`, label: formatWindowLabel(hours, "hour") };
221
+ }
222
+
223
+ function resolveResetTime(window: ParsedUsageWindow, nowMs: number): number | undefined {
224
+ const resetAt = window.resetAt;
225
+ if (resetAt !== undefined) {
226
+ const resetAtMs = resetAt > 1_000_000_000_000 ? resetAt : resetAt * 1000;
227
+ if (Number.isFinite(resetAtMs)) return resetAtMs;
228
+ }
229
+ if (window.resetAfterSeconds !== undefined) {
230
+ return nowMs + window.resetAfterSeconds * 1000;
231
+ }
232
+ return undefined;
233
+ }
234
+
235
+ function buildUsageWindow(window: ParsedUsageWindow, key: string, nowMs: number): UsageWindow {
236
+ const resetsAt = resolveResetTime(window, nowMs);
237
+ if (window.limitWindowSeconds !== undefined) {
238
+ const { id, label } = buildWindowLabel(window.limitWindowSeconds);
239
+ const durationMs = window.limitWindowSeconds * 1000;
240
+ return { id, label, durationMs, ...(resetsAt !== undefined ? { resetsAt } : {}) };
241
+ }
242
+ const fallbackLabel = key === "primary" ? "Primary window" : "Secondary window";
243
+ return { id: key, label: fallbackLabel, ...(resetsAt !== undefined ? { resetsAt } : {}) };
244
+ }
245
+
246
+ function buildUsageAmount(window: ParsedUsageWindow): UsageAmount {
247
+ const usedPercent = window.usedPercent;
248
+ if (usedPercent === undefined) {
249
+ return { unit: "percent" };
250
+ }
251
+ const clamped = Math.min(Math.max(usedPercent, 0), 100);
252
+ const usedFraction = clamped / 100;
253
+ return {
254
+ used: clamped,
255
+ limit: 100,
256
+ remaining: Math.max(0, 100 - clamped),
257
+ usedFraction,
258
+ remainingFraction: Math.max(0, 1 - usedFraction),
259
+ unit: "percent",
260
+ };
261
+ }
262
+
263
+ function buildUsageStatus(usedFraction?: number, limitReached?: boolean): UsageLimit["status"] {
264
+ if (limitReached) return "exhausted";
265
+ if (usedFraction === undefined) return "unknown";
266
+ if (usedFraction >= 1) return "exhausted";
267
+ if (usedFraction >= 0.9) return "warning";
268
+ return "ok";
269
+ }
270
+
271
+ function buildUsageLimit(args: {
272
+ key: "primary" | "secondary";
273
+ window: ParsedUsageWindow;
274
+ accountId?: string;
275
+ planType?: string;
276
+ limitReached?: boolean;
277
+ nowMs: number;
278
+ }): UsageLimit {
279
+ const usageWindow = buildUsageWindow(args.window, args.key, args.nowMs);
280
+ const amount = buildUsageAmount(args.window);
281
+ return {
282
+ id: `openai-codex:${args.key}`,
283
+ label: usageWindow.label,
284
+ scope: {
285
+ provider: "openai-codex",
286
+ accountId: args.accountId,
287
+ tier: args.planType,
288
+ windowId: usageWindow.id,
289
+ shared: true,
290
+ },
291
+ window: usageWindow,
292
+ amount,
293
+ status: buildUsageStatus(amount.usedFraction, args.limitReached),
294
+ };
295
+ }
296
+ function additionalLimitSlug(args: { limitName?: string; meteredFeature?: string }): string {
297
+ const probe = `${args.limitName ?? ""} ${args.meteredFeature ?? ""}`.toLowerCase();
298
+ if (probe.includes("spark") || probe.includes("bengalfox")) return "spark";
299
+ const source = (args.meteredFeature ?? args.limitName ?? "extra").toLowerCase();
300
+ return (
301
+ source
302
+ .replace(/^codex[-_]/, "")
303
+ .replace(/[^a-z0-9]+/g, "-")
304
+ .replace(/^-+|-+$/g, "") || "extra"
305
+ );
306
+ }
307
+
308
+ function additionalDisplayName(slug: string, limitName?: string): string {
309
+ if (slug === "spark") return "Spark";
310
+ if (limitName) return limitName;
311
+ return slug.replace(
312
+ /(^|-)([a-z])/g,
313
+ (_match, sep: string, ch: string) => `${sep === "-" ? " " : ""}${ch.toUpperCase()}`,
314
+ );
315
+ }
316
+
317
+ function buildAdditionalUsageLimit(args: {
318
+ key: "primary" | "secondary";
319
+ slug: string;
320
+ displayName: string;
321
+ window: ParsedUsageWindow;
322
+ accountId?: string;
323
+ limitReached?: boolean;
324
+ limitName?: string;
325
+ meteredFeature?: string;
326
+ nowMs: number;
327
+ }): UsageLimit {
328
+ const usageWindow = buildUsageWindow(args.window, args.key, args.nowMs);
329
+ const amount = buildUsageAmount(args.window);
330
+ return {
331
+ id: `openai-codex:${args.slug}:${args.key}`,
332
+ label: `${usageWindow.label} (${args.displayName})`,
333
+ scope: {
334
+ provider: "openai-codex",
335
+ accountId: args.accountId,
336
+ tier: args.slug,
337
+ modelId: args.limitName,
338
+ windowId: usageWindow.id,
339
+ shared: true,
340
+ },
341
+ window: usageWindow,
342
+ amount,
343
+ status: buildUsageStatus(amount.usedFraction, args.limitReached),
344
+ };
345
+ }
346
+
347
+ export const openaiCodexUsageProvider: UsageProvider = {
348
+ id: "openai-codex",
349
+ supports(params: UsageFetchParams): boolean {
350
+ return params.provider === "openai-codex" && params.credential.type === "oauth";
351
+ },
352
+ async fetchUsage(params: UsageFetchParams, ctx: UsageFetchContext): Promise<UsageReport | null> {
353
+ if (params.provider !== "openai-codex") return null;
354
+ const { credential } = params;
355
+ if (credential.type !== "oauth") return null;
356
+
357
+ const accessToken = credential.accessToken;
358
+ if (!accessToken) return null;
359
+
360
+ const nowMs = Date.now();
361
+ if (credential.expiresAt !== undefined && credential.expiresAt <= nowMs) {
362
+ ctx.logger?.warn("Codex usage token expired", { provider: params.provider });
363
+ return null;
364
+ }
365
+
366
+ const baseUrl = normalizeCodexBaseUrl(params.baseUrl);
367
+ const accountId = credential.accountId ?? extractAccountId(accessToken);
368
+ const email = normalizeEmail(credential.email ?? extractEmail(accessToken));
369
+
370
+ const headers: Record<string, string> = {
371
+ Authorization: `Bearer ${accessToken}`,
372
+ "User-Agent": "OpenCode-Status-Plugin/1.0",
373
+ };
374
+ if (accountId) {
375
+ headers["ChatGPT-Account-Id"] = accountId;
376
+ }
377
+
378
+ const url = buildCodexUsageUrl(baseUrl);
379
+ let payload: unknown;
380
+ try {
381
+ const response = await ctx.fetch(url, { headers, signal: params.signal });
382
+ if (!response.ok) {
383
+ ctx.logger?.warn("Codex usage request failed", { status: response.status, provider: params.provider });
384
+ return null;
385
+ }
386
+ payload = await response.json();
387
+ } catch (error) {
388
+ ctx.logger?.warn("Codex usage request error", { provider: params.provider, error: String(error) });
389
+ return null;
390
+ }
391
+
392
+ const parsed = parseUsagePayload(payload);
393
+ const planType =
394
+ parsed?.planType ??
395
+ (isRecord(payload) && typeof payload.plan_type === "string" ? payload.plan_type : undefined);
396
+
397
+ const limits: UsageLimit[] = [];
398
+ if (parsed?.primary) {
399
+ limits.push(
400
+ buildUsageLimit({
401
+ key: "primary",
402
+ window: parsed.primary,
403
+ accountId,
404
+ planType,
405
+ limitReached: parsed.limitReached,
406
+ nowMs,
407
+ }),
408
+ );
409
+ }
410
+ if (parsed?.secondary) {
411
+ limits.push(
412
+ buildUsageLimit({
413
+ key: "secondary",
414
+ window: parsed.secondary,
415
+ accountId,
416
+ planType,
417
+ limitReached: parsed.limitReached,
418
+ nowMs,
419
+ }),
420
+ );
421
+ }
422
+ for (const extra of parsed?.additional ?? []) {
423
+ const slug = additionalLimitSlug({ limitName: extra.limitName, meteredFeature: extra.meteredFeature });
424
+ const displayName = additionalDisplayName(slug, extra.limitName);
425
+ if (extra.primary) {
426
+ limits.push(
427
+ buildAdditionalUsageLimit({
428
+ key: "primary",
429
+ slug,
430
+ displayName,
431
+ window: extra.primary,
432
+ accountId,
433
+ limitReached: extra.limitReached,
434
+ limitName: extra.limitName,
435
+ meteredFeature: extra.meteredFeature,
436
+ nowMs,
437
+ }),
438
+ );
439
+ }
440
+ if (extra.secondary) {
441
+ limits.push(
442
+ buildAdditionalUsageLimit({
443
+ key: "secondary",
444
+ slug,
445
+ displayName,
446
+ window: extra.secondary,
447
+ accountId,
448
+ limitReached: extra.limitReached,
449
+ limitName: extra.limitName,
450
+ meteredFeature: extra.meteredFeature,
451
+ nowMs,
452
+ }),
453
+ );
454
+ }
455
+ }
456
+
457
+ const report: UsageReport = {
458
+ provider: "openai-codex",
459
+ fetchedAt: nowMs,
460
+ limits,
461
+ metadata: {
462
+ planType,
463
+ allowed: parsed?.allowed,
464
+ limitReached: parsed?.limitReached,
465
+ email,
466
+ accountId,
467
+ },
468
+ raw: parsed?.raw ?? payload,
469
+ };
470
+
471
+ return report;
472
+ },
473
+ };
474
+
475
+ const FIVE_HOUR_MS = 5 * 60 * 60 * 1000;
476
+
477
+ export const codexRankingStrategy: CredentialRankingStrategy = {
478
+ findWindowLimits(report) {
479
+ const findLimit = (key: "primary" | "secondary"): UsageLimit | undefined => {
480
+ const direct = report.limits.find(l => l.id === `openai-codex:${key}`);
481
+ if (direct) return direct;
482
+ const byId = report.limits.find(l => l.id.toLowerCase().includes(key));
483
+ if (byId) return byId;
484
+ const windowId = key === "secondary" ? "7d" : "1h";
485
+ return report.limits.find(l => l.scope.windowId?.toLowerCase() === windowId);
486
+ };
487
+ return { primary: findLimit("primary"), secondary: findLimit("secondary") };
488
+ },
489
+ windowDefaults: { primaryMs: 60 * 60 * 1000, secondaryMs: 7 * 24 * 60 * 60 * 1000 },
490
+ hasPriorityBoost(primary) {
491
+ if (!primary) return false;
492
+ const windowId = primary.scope.windowId?.toLowerCase();
493
+ const durationMs = primary.window?.durationMs;
494
+ const isFiveHourWindow =
495
+ windowId === "5h" ||
496
+ (typeof durationMs === "number" &&
497
+ Number.isFinite(durationMs) &&
498
+ Math.abs(durationMs - FIVE_HOUR_MS) <= 60_000);
499
+ if (!isFiveHourWindow) return false;
500
+ const usedFraction = primary.amount.usedFraction;
501
+ return typeof usedFraction === "number" && Number.isFinite(usedFraction) && usedFraction === 0;
502
+ },
503
+ };
@@ -0,0 +1,10 @@
1
+ export const toNumber = (value: unknown): number | undefined => {
2
+ if (typeof value === "number" && Number.isFinite(value)) return value;
3
+ if (typeof value === "string") {
4
+ const trimmed = value.trim();
5
+ if (!trimmed) return undefined;
6
+ const parsed = Number(trimmed);
7
+ if (Number.isFinite(parsed)) return parsed;
8
+ }
9
+ return undefined;
10
+ };