@aryee337/aery-ai 0.2.28 → 0.2.29

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 (417) hide show
  1. package/CHANGELOG.md +2914 -0
  2. package/README.md +614 -813
  3. package/package.json +140 -105
  4. package/src/api-registry.ts +96 -0
  5. package/src/auth-broker/client.ts +358 -0
  6. package/src/auth-broker/index.ts +5 -0
  7. package/src/auth-broker/refresher.ts +117 -0
  8. package/src/auth-broker/remote-store.ts +623 -0
  9. package/src/auth-broker/server.ts +644 -0
  10. package/src/auth-broker/types.ts +127 -0
  11. package/src/auth-broker/wire-schemas.ts +200 -0
  12. package/src/auth-gateway/http.ts +194 -0
  13. package/src/auth-gateway/index.ts +3 -0
  14. package/src/auth-gateway/server.ts +818 -0
  15. package/src/auth-gateway/types.ts +143 -0
  16. package/src/auth-storage.ts +4422 -0
  17. package/src/index.ts +54 -0
  18. package/src/model-cache.ts +129 -0
  19. package/src/model-manager.ts +469 -0
  20. package/src/model-thinking.ts +782 -0
  21. package/src/models.json +83530 -0
  22. package/src/models.json.d.ts +9 -0
  23. package/src/models.ts +56 -0
  24. package/src/prompts/turn-aborted-guidance.md +4 -0
  25. package/src/provider-details.ts +90 -0
  26. package/src/provider-models/bundled-references.ts +38 -0
  27. package/src/provider-models/descriptors.ts +355 -0
  28. package/src/provider-models/google.ts +88 -0
  29. package/src/provider-models/index.ts +5 -0
  30. package/src/provider-models/ollama.ts +153 -0
  31. package/src/provider-models/openai-compat.ts +2817 -0
  32. package/src/provider-models/special.ts +67 -0
  33. package/src/providers/aery-native-client.ts +228 -0
  34. package/src/providers/aery-native-server.ts +212 -0
  35. package/src/providers/amazon-bedrock.ts +873 -0
  36. package/src/providers/anthropic-client.ts +318 -0
  37. package/src/providers/anthropic-messages-server-schema.ts +243 -0
  38. package/src/providers/anthropic-messages-server.ts +683 -0
  39. package/src/providers/anthropic-wire.ts +268 -0
  40. package/src/providers/anthropic.ts +3094 -0
  41. package/src/providers/aws-credentials.ts +501 -0
  42. package/src/providers/aws-eventstream.ts +185 -0
  43. package/src/providers/aws-sigv4.ts +218 -0
  44. package/src/providers/azure-openai-responses.ts +361 -0
  45. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  46. package/src/providers/cursor/proto/agent.proto +3526 -0
  47. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  48. package/src/providers/cursor/proto/buf.yaml +17 -0
  49. package/src/providers/cursor.ts +2621 -0
  50. package/src/providers/error-message.ts +21 -0
  51. package/src/providers/github-copilot-headers.ts +140 -0
  52. package/src/providers/gitlab-duo.ts +372 -0
  53. package/src/providers/google-auth.ts +252 -0
  54. package/src/providers/google-gemini-cli.ts +809 -0
  55. package/src/providers/google-gemini-headers.ts +41 -0
  56. package/src/providers/google-shared.ts +917 -0
  57. package/src/providers/google-types.ts +167 -0
  58. package/src/providers/google-vertex.ts +91 -0
  59. package/src/providers/google.ts +41 -0
  60. package/src/providers/grammar.ts +70 -0
  61. package/src/providers/kimi.ts +52 -0
  62. package/src/providers/mock.ts +496 -0
  63. package/src/providers/ollama.ts +644 -0
  64. package/src/providers/openai-anthropic-shim.ts +138 -0
  65. package/src/providers/openai-chat-server-schema.ts +252 -0
  66. package/src/providers/openai-chat-server.ts +647 -0
  67. package/src/providers/openai-codex/constants.ts +43 -0
  68. package/src/providers/openai-codex/request-transformer.ts +161 -0
  69. package/src/providers/openai-codex/response-handler.ts +81 -0
  70. package/src/providers/openai-codex-responses.ts +3018 -0
  71. package/src/providers/openai-completions-compat.ts +300 -0
  72. package/src/providers/openai-completions.ts +1979 -0
  73. package/src/providers/openai-responses-server-schema.ts +290 -0
  74. package/src/providers/openai-responses-server.ts +1183 -0
  75. package/src/providers/openai-responses-shared.ts +873 -0
  76. package/src/providers/openai-responses.ts +679 -0
  77. package/src/providers/register-builtins.ts +436 -0
  78. package/src/providers/synthetic.ts +50 -0
  79. package/src/providers/transform-messages.ts +382 -0
  80. package/src/providers/vision-guard.ts +31 -0
  81. package/src/providers/xai-responses.ts +82 -0
  82. package/src/rate-limit-utils.ts +84 -0
  83. package/src/stream.ts +1065 -0
  84. package/src/types.ts +944 -0
  85. package/src/usage/claude.ts +482 -0
  86. package/src/usage/gemini.ts +250 -0
  87. package/src/usage/github-copilot.ts +421 -0
  88. package/src/usage/google-antigravity.ts +201 -0
  89. package/src/usage/kimi.ts +271 -0
  90. package/src/usage/minimax-code.ts +31 -0
  91. package/src/usage/openai-codex.ts +503 -0
  92. package/src/usage/shared.ts +10 -0
  93. package/src/usage/zai.ts +247 -0
  94. package/src/usage.ts +185 -0
  95. package/src/utils/abort.ts +51 -0
  96. package/src/utils/abortable-iterator.ts +69 -0
  97. package/src/utils/anthropic-auth.ts +93 -0
  98. package/src/utils/discovery/antigravity.ts +261 -0
  99. package/src/utils/discovery/codex.ts +371 -0
  100. package/src/utils/discovery/cursor.ts +306 -0
  101. package/src/utils/discovery/gemini.ts +248 -0
  102. package/src/utils/discovery/index.ts +4 -0
  103. package/src/utils/discovery/openai-compatible.ts +224 -0
  104. package/src/utils/event-stream.ts +142 -0
  105. package/src/utils/fireworks-model-id.ts +30 -0
  106. package/src/utils/foundry.ts +8 -0
  107. package/src/utils/http-inspector.ts +176 -0
  108. package/src/utils/idle-iterator.ts +267 -0
  109. package/src/utils/json-parse.ts +182 -0
  110. package/src/utils/oauth/__tests__/xai-oauth.test.ts +107 -0
  111. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  112. package/src/utils/oauth/anthropic.ts +273 -0
  113. package/src/utils/oauth/api-key-login.ts +87 -0
  114. package/src/utils/oauth/api-key-validation.ts +92 -0
  115. package/src/utils/oauth/callback-server.ts +276 -0
  116. package/src/utils/oauth/cerebras.ts +16 -0
  117. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  118. package/src/utils/oauth/cursor.ts +157 -0
  119. package/src/utils/oauth/deepseek.ts +53 -0
  120. package/src/utils/oauth/firepass.ts +24 -0
  121. package/src/utils/oauth/fireworks.ts +15 -0
  122. package/src/utils/oauth/github-copilot.ts +362 -0
  123. package/src/utils/oauth/gitlab-duo.ts +123 -0
  124. package/src/utils/oauth/google-antigravity.ts +200 -0
  125. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  126. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  127. package/src/utils/oauth/huggingface.ts +62 -0
  128. package/src/utils/oauth/index.ts +484 -0
  129. package/src/utils/oauth/kagi.ts +47 -0
  130. package/src/utils/oauth/kilo.ts +87 -0
  131. package/src/utils/oauth/kimi.ts +254 -0
  132. package/src/utils/oauth/litellm.ts +47 -0
  133. package/src/utils/oauth/lm-studio.ts +38 -0
  134. package/src/utils/oauth/minimax-code.ts +78 -0
  135. package/src/utils/oauth/moonshot.ts +23 -0
  136. package/src/utils/oauth/nanogpt.ts +15 -0
  137. package/src/utils/oauth/nvidia.ts +70 -0
  138. package/src/utils/oauth/oauth.html +203 -0
  139. package/src/utils/oauth/ollama-cloud.ts +28 -0
  140. package/src/utils/oauth/ollama.ts +47 -0
  141. package/src/utils/oauth/openai-codex.ts +299 -0
  142. package/src/utils/oauth/opencode.ts +49 -0
  143. package/src/utils/oauth/openrouter.ts +20 -0
  144. package/src/utils/oauth/parallel.ts +46 -0
  145. package/src/utils/oauth/perplexity.ts +206 -0
  146. package/src/utils/oauth/pkce.ts +18 -0
  147. package/src/utils/oauth/qianfan.ts +58 -0
  148. package/src/utils/oauth/qwen-portal.ts +60 -0
  149. package/src/utils/oauth/synthetic.ts +15 -0
  150. package/src/utils/oauth/tavily.ts +46 -0
  151. package/src/utils/oauth/together.ts +16 -0
  152. package/src/utils/oauth/types.ts +99 -0
  153. package/src/utils/oauth/venice.ts +59 -0
  154. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  155. package/src/utils/oauth/vllm.ts +40 -0
  156. package/src/utils/oauth/wafer.ts +50 -0
  157. package/src/utils/oauth/xai-oauth.ts +342 -0
  158. package/src/utils/oauth/xiaomi.ts +139 -0
  159. package/src/utils/oauth/zai.ts +60 -0
  160. package/src/utils/oauth/zenmux.ts +15 -0
  161. package/src/utils/oauth/zhipu.ts +60 -0
  162. package/src/utils/overflow.ts +137 -0
  163. package/src/utils/parse-bind.ts +54 -0
  164. package/src/utils/provider-response.ts +30 -0
  165. package/src/utils/request-debug.ts +336 -0
  166. package/src/utils/retry-after.ts +110 -0
  167. package/src/utils/retry.ts +54 -0
  168. package/src/utils/schema/CONSTRAINTS.md +164 -0
  169. package/src/utils/schema/adapt.ts +36 -0
  170. package/src/utils/schema/compatibility.ts +435 -0
  171. package/src/utils/schema/dereference.ts +98 -0
  172. package/src/utils/schema/draft.ts +341 -0
  173. package/src/utils/schema/equality.ts +97 -0
  174. package/src/utils/schema/fields.ts +191 -0
  175. package/src/utils/schema/index.ts +13 -0
  176. package/src/utils/schema/json-schema-validator.ts +577 -0
  177. package/src/utils/schema/meta-validator.ts +167 -0
  178. package/src/utils/schema/normalize.ts +1588 -0
  179. package/src/utils/schema/spill.ts +43 -0
  180. package/src/utils/schema/stamps.ts +97 -0
  181. package/src/utils/schema/types.ts +10 -0
  182. package/src/utils/schema/wire.ts +293 -0
  183. package/src/utils/schema/zod-decontaminate.ts +331 -0
  184. package/src/utils/sdk-stream-timeout.ts +43 -0
  185. package/src/utils/sse-debug.ts +289 -0
  186. package/src/utils/stream-markup-healing.ts +612 -0
  187. package/src/utils/tool-choice.ts +99 -0
  188. package/src/utils/validation.ts +1024 -0
  189. package/src/utils.ts +166 -0
  190. package/dist/api-registry.d.ts +0 -20
  191. package/dist/api-registry.d.ts.map +0 -1
  192. package/dist/api-registry.js +0 -44
  193. package/dist/api-registry.js.map +0 -1
  194. package/dist/bedrock-provider.d.ts +0 -5
  195. package/dist/bedrock-provider.d.ts.map +0 -1
  196. package/dist/bedrock-provider.js +0 -6
  197. package/dist/bedrock-provider.js.map +0 -1
  198. package/dist/cli.d.ts +0 -3
  199. package/dist/cli.d.ts.map +0 -1
  200. package/dist/cli.js +0 -130
  201. package/dist/cli.js.map +0 -1
  202. package/dist/env-api-keys.d.ts +0 -18
  203. package/dist/env-api-keys.d.ts.map +0 -1
  204. package/dist/env-api-keys.js +0 -178
  205. package/dist/env-api-keys.js.map +0 -1
  206. package/dist/image-models.d.ts +0 -10
  207. package/dist/image-models.d.ts.map +0 -1
  208. package/dist/image-models.generated.d.ts +0 -440
  209. package/dist/image-models.generated.d.ts.map +0 -1
  210. package/dist/image-models.generated.js +0 -442
  211. package/dist/image-models.generated.js.map +0 -1
  212. package/dist/image-models.js +0 -23
  213. package/dist/image-models.js.map +0 -1
  214. package/dist/images-api-registry.d.ts +0 -14
  215. package/dist/images-api-registry.d.ts.map +0 -1
  216. package/dist/images-api-registry.js +0 -22
  217. package/dist/images-api-registry.js.map +0 -1
  218. package/dist/images.d.ts +0 -4
  219. package/dist/images.d.ts.map +0 -1
  220. package/dist/images.js +0 -14
  221. package/dist/images.js.map +0 -1
  222. package/dist/index.d.ts +0 -32
  223. package/dist/index.d.ts.map +0 -1
  224. package/dist/index.js +0 -20
  225. package/dist/index.js.map +0 -1
  226. package/dist/models.d.ts +0 -18
  227. package/dist/models.d.ts.map +0 -1
  228. package/dist/models.generated.d.ts +0 -17707
  229. package/dist/models.generated.d.ts.map +0 -1
  230. package/dist/models.generated.js +0 -16561
  231. package/dist/models.generated.js.map +0 -1
  232. package/dist/models.js +0 -71
  233. package/dist/models.js.map +0 -1
  234. package/dist/oauth.d.ts +0 -2
  235. package/dist/oauth.d.ts.map +0 -1
  236. package/dist/oauth.js +0 -2
  237. package/dist/oauth.js.map +0 -1
  238. package/dist/providers/aery-error-formatting.d.ts +0 -13
  239. package/dist/providers/aery-error-formatting.d.ts.map +0 -1
  240. package/dist/providers/aery-error-formatting.js +0 -112
  241. package/dist/providers/aery-error-formatting.js.map +0 -1
  242. package/dist/providers/amazon-bedrock.d.ts +0 -38
  243. package/dist/providers/amazon-bedrock.d.ts.map +0 -1
  244. package/dist/providers/amazon-bedrock.js +0 -763
  245. package/dist/providers/amazon-bedrock.js.map +0 -1
  246. package/dist/providers/anthropic.d.ts +0 -71
  247. package/dist/providers/anthropic.d.ts.map +0 -1
  248. package/dist/providers/anthropic.js +0 -949
  249. package/dist/providers/anthropic.js.map +0 -1
  250. package/dist/providers/azure-openai-responses.d.ts +0 -15
  251. package/dist/providers/azure-openai-responses.d.ts.map +0 -1
  252. package/dist/providers/azure-openai-responses.js +0 -225
  253. package/dist/providers/azure-openai-responses.js.map +0 -1
  254. package/dist/providers/cloudflare.d.ts +0 -13
  255. package/dist/providers/cloudflare.d.ts.map +0 -1
  256. package/dist/providers/cloudflare.js +0 -26
  257. package/dist/providers/cloudflare.js.map +0 -1
  258. package/dist/providers/faux.d.ts +0 -56
  259. package/dist/providers/faux.d.ts.map +0 -1
  260. package/dist/providers/faux.js +0 -368
  261. package/dist/providers/faux.js.map +0 -1
  262. package/dist/providers/github-copilot-headers.d.ts +0 -8
  263. package/dist/providers/github-copilot-headers.d.ts.map +0 -1
  264. package/dist/providers/github-copilot-headers.js +0 -29
  265. package/dist/providers/github-copilot-headers.js.map +0 -1
  266. package/dist/providers/google-gemini-cli.d.ts +0 -74
  267. package/dist/providers/google-gemini-cli.d.ts.map +0 -1
  268. package/dist/providers/google-gemini-cli.js +0 -779
  269. package/dist/providers/google-gemini-cli.js.map +0 -1
  270. package/dist/providers/google-shared.d.ts +0 -70
  271. package/dist/providers/google-shared.d.ts.map +0 -1
  272. package/dist/providers/google-shared.js +0 -329
  273. package/dist/providers/google-shared.js.map +0 -1
  274. package/dist/providers/google-vertex.d.ts +0 -15
  275. package/dist/providers/google-vertex.d.ts.map +0 -1
  276. package/dist/providers/google-vertex.js +0 -442
  277. package/dist/providers/google-vertex.js.map +0 -1
  278. package/dist/providers/google.d.ts +0 -13
  279. package/dist/providers/google.d.ts.map +0 -1
  280. package/dist/providers/google.js +0 -400
  281. package/dist/providers/google.js.map +0 -1
  282. package/dist/providers/images/openrouter.d.ts +0 -3
  283. package/dist/providers/images/openrouter.d.ts.map +0 -1
  284. package/dist/providers/images/openrouter.js +0 -129
  285. package/dist/providers/images/openrouter.js.map +0 -1
  286. package/dist/providers/images/register-builtins.d.ts +0 -4
  287. package/dist/providers/images/register-builtins.d.ts.map +0 -1
  288. package/dist/providers/images/register-builtins.js +0 -34
  289. package/dist/providers/images/register-builtins.js.map +0 -1
  290. package/dist/providers/mistral.d.ts +0 -25
  291. package/dist/providers/mistral.d.ts.map +0 -1
  292. package/dist/providers/mistral.js +0 -535
  293. package/dist/providers/mistral.js.map +0 -1
  294. package/dist/providers/openai-codex-responses.d.ts +0 -30
  295. package/dist/providers/openai-codex-responses.d.ts.map +0 -1
  296. package/dist/providers/openai-codex-responses.js +0 -1090
  297. package/dist/providers/openai-codex-responses.js.map +0 -1
  298. package/dist/providers/openai-completions.d.ts +0 -19
  299. package/dist/providers/openai-completions.d.ts.map +0 -1
  300. package/dist/providers/openai-completions.js +0 -950
  301. package/dist/providers/openai-completions.js.map +0 -1
  302. package/dist/providers/openai-prompt-cache.d.ts +0 -3
  303. package/dist/providers/openai-prompt-cache.d.ts.map +0 -1
  304. package/dist/providers/openai-prompt-cache.js +0 -10
  305. package/dist/providers/openai-prompt-cache.js.map +0 -1
  306. package/dist/providers/openai-responses-shared.d.ts +0 -18
  307. package/dist/providers/openai-responses-shared.d.ts.map +0 -1
  308. package/dist/providers/openai-responses-shared.js +0 -492
  309. package/dist/providers/openai-responses-shared.js.map +0 -1
  310. package/dist/providers/openai-responses.d.ts +0 -13
  311. package/dist/providers/openai-responses.d.ts.map +0 -1
  312. package/dist/providers/openai-responses.js +0 -237
  313. package/dist/providers/openai-responses.js.map +0 -1
  314. package/dist/providers/register-builtins.d.ts +0 -38
  315. package/dist/providers/register-builtins.d.ts.map +0 -1
  316. package/dist/providers/register-builtins.js +0 -278
  317. package/dist/providers/register-builtins.js.map +0 -1
  318. package/dist/providers/simple-options.d.ts +0 -8
  319. package/dist/providers/simple-options.d.ts.map +0 -1
  320. package/dist/providers/simple-options.js +0 -41
  321. package/dist/providers/simple-options.js.map +0 -1
  322. package/dist/providers/transform-messages.d.ts +0 -8
  323. package/dist/providers/transform-messages.d.ts.map +0 -1
  324. package/dist/providers/transform-messages.js +0 -184
  325. package/dist/providers/transform-messages.js.map +0 -1
  326. package/dist/session-resources.d.ts +0 -4
  327. package/dist/session-resources.d.ts.map +0 -1
  328. package/dist/session-resources.js +0 -22
  329. package/dist/session-resources.js.map +0 -1
  330. package/dist/stream.d.ts +0 -8
  331. package/dist/stream.d.ts.map +0 -1
  332. package/dist/stream.js +0 -27
  333. package/dist/stream.js.map +0 -1
  334. package/dist/types.d.ts +0 -498
  335. package/dist/types.d.ts.map +0 -1
  336. package/dist/types.js +0 -2
  337. package/dist/types.js.map +0 -1
  338. package/dist/utils/diagnostics.d.ts +0 -19
  339. package/dist/utils/diagnostics.d.ts.map +0 -1
  340. package/dist/utils/diagnostics.js +0 -25
  341. package/dist/utils/diagnostics.js.map +0 -1
  342. package/dist/utils/event-stream.d.ts +0 -21
  343. package/dist/utils/event-stream.d.ts.map +0 -1
  344. package/dist/utils/event-stream.js +0 -81
  345. package/dist/utils/event-stream.js.map +0 -1
  346. package/dist/utils/hash.d.ts +0 -3
  347. package/dist/utils/hash.d.ts.map +0 -1
  348. package/dist/utils/hash.js +0 -14
  349. package/dist/utils/hash.js.map +0 -1
  350. package/dist/utils/headers.d.ts +0 -2
  351. package/dist/utils/headers.d.ts.map +0 -1
  352. package/dist/utils/headers.js +0 -8
  353. package/dist/utils/headers.js.map +0 -1
  354. package/dist/utils/json-parse.d.ts +0 -16
  355. package/dist/utils/json-parse.d.ts.map +0 -1
  356. package/dist/utils/json-parse.js +0 -113
  357. package/dist/utils/json-parse.js.map +0 -1
  358. package/dist/utils/node-http-proxy.d.ts +0 -10
  359. package/dist/utils/node-http-proxy.d.ts.map +0 -1
  360. package/dist/utils/node-http-proxy.js +0 -97
  361. package/dist/utils/node-http-proxy.js.map +0 -1
  362. package/dist/utils/oauth/anthropic.d.ts +0 -25
  363. package/dist/utils/oauth/anthropic.d.ts.map +0 -1
  364. package/dist/utils/oauth/anthropic.js +0 -335
  365. package/dist/utils/oauth/anthropic.js.map +0 -1
  366. package/dist/utils/oauth/device-code.d.ts +0 -19
  367. package/dist/utils/oauth/device-code.d.ts.map +0 -1
  368. package/dist/utils/oauth/device-code.js +0 -55
  369. package/dist/utils/oauth/device-code.js.map +0 -1
  370. package/dist/utils/oauth/github-copilot.d.ts +0 -30
  371. package/dist/utils/oauth/github-copilot.d.ts.map +0 -1
  372. package/dist/utils/oauth/github-copilot.js +0 -268
  373. package/dist/utils/oauth/github-copilot.js.map +0 -1
  374. package/dist/utils/oauth/google-antigravity.d.ts +0 -26
  375. package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
  376. package/dist/utils/oauth/google-antigravity.js +0 -377
  377. package/dist/utils/oauth/google-antigravity.js.map +0 -1
  378. package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
  379. package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
  380. package/dist/utils/oauth/google-gemini-cli.js +0 -482
  381. package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
  382. package/dist/utils/oauth/index.d.ts +0 -63
  383. package/dist/utils/oauth/index.d.ts.map +0 -1
  384. package/dist/utils/oauth/index.js +0 -131
  385. package/dist/utils/oauth/index.js.map +0 -1
  386. package/dist/utils/oauth/oauth-page.d.ts +0 -3
  387. package/dist/utils/oauth/oauth-page.d.ts.map +0 -1
  388. package/dist/utils/oauth/oauth-page.js +0 -105
  389. package/dist/utils/oauth/oauth-page.js.map +0 -1
  390. package/dist/utils/oauth/openai-codex.d.ts +0 -34
  391. package/dist/utils/oauth/openai-codex.d.ts.map +0 -1
  392. package/dist/utils/oauth/openai-codex.js +0 -385
  393. package/dist/utils/oauth/openai-codex.js.map +0 -1
  394. package/dist/utils/oauth/pkce.d.ts +0 -13
  395. package/dist/utils/oauth/pkce.d.ts.map +0 -1
  396. package/dist/utils/oauth/pkce.js +0 -31
  397. package/dist/utils/oauth/pkce.js.map +0 -1
  398. package/dist/utils/oauth/types.d.ts +0 -64
  399. package/dist/utils/oauth/types.d.ts.map +0 -1
  400. package/dist/utils/oauth/types.js +0 -2
  401. package/dist/utils/oauth/types.js.map +0 -1
  402. package/dist/utils/overflow.d.ts +0 -56
  403. package/dist/utils/overflow.d.ts.map +0 -1
  404. package/dist/utils/overflow.js +0 -151
  405. package/dist/utils/overflow.js.map +0 -1
  406. package/dist/utils/sanitize-unicode.d.ts +0 -22
  407. package/dist/utils/sanitize-unicode.d.ts.map +0 -1
  408. package/dist/utils/sanitize-unicode.js +0 -26
  409. package/dist/utils/sanitize-unicode.js.map +0 -1
  410. package/dist/utils/typebox-helpers.d.ts +0 -17
  411. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  412. package/dist/utils/typebox-helpers.js +0 -21
  413. package/dist/utils/typebox-helpers.js.map +0 -1
  414. package/dist/utils/validation.d.ts +0 -18
  415. package/dist/utils/validation.d.ts.map +0 -1
  416. package/dist/utils/validation.js +0 -281
  417. package/dist/utils/validation.js.map +0 -1
@@ -0,0 +1,250 @@
1
+ import { getGeminiCliHeaders } from "../providers/google-gemini-cli";
2
+ import type {
3
+ UsageAmount,
4
+ UsageFetchContext,
5
+ UsageFetchParams,
6
+ UsageLimit,
7
+ UsageProvider,
8
+ UsageReport,
9
+ UsageWindow,
10
+ } from "../usage";
11
+
12
+ // (Refresh is the sole responsibility of AuthStorage; no provider-direct refresh here.)
13
+
14
+ const DEFAULT_ENDPOINT = "https://cloudcode-pa.googleapis.com";
15
+
16
+ const GEMINI_TIER_MAP: Array<{ tier: string; models: string[] }> = [
17
+ {
18
+ tier: "3-Flash",
19
+ models: ["gemini-3-flash-preview", "gemini-3-flash"],
20
+ },
21
+ {
22
+ tier: "Flash",
23
+ models: ["gemini-2.5-flash", "gemini-2.5-flash-lite", "gemini-2.0-flash", "gemini-1.5-flash"],
24
+ },
25
+ {
26
+ tier: "Pro",
27
+ models: ["gemini-2.5-pro", "gemini-3-pro-preview", "gemini-3.1-pro-preview", "gemini-3-pro", "gemini-1.5-pro"],
28
+ },
29
+ ];
30
+
31
+ interface LoadCodeAssistResponse {
32
+ cloudaicompanionProject?: string | { id?: string };
33
+ currentTier?: { id?: string; name?: string };
34
+ }
35
+
36
+ interface RetrieveUserQuotaResponse {
37
+ buckets?: Array<{
38
+ modelId?: string;
39
+ remainingFraction?: number;
40
+ resetTime?: string;
41
+ }>;
42
+ }
43
+
44
+ function getProjectId(payload: LoadCodeAssistResponse | undefined): string | undefined {
45
+ if (!payload) return undefined;
46
+ if (typeof payload.cloudaicompanionProject === "string") {
47
+ return payload.cloudaicompanionProject;
48
+ }
49
+ if (payload.cloudaicompanionProject && typeof payload.cloudaicompanionProject === "object") {
50
+ return payload.cloudaicompanionProject.id;
51
+ }
52
+ return undefined;
53
+ }
54
+
55
+ function getModelTier(modelId: string): string | undefined {
56
+ for (const entry of GEMINI_TIER_MAP) {
57
+ if (entry.models.includes(modelId)) {
58
+ return entry.tier;
59
+ }
60
+ }
61
+ const normalized = modelId.toLowerCase();
62
+ if (normalized.includes("flash")) return "Flash";
63
+ if (normalized.includes("pro")) return "Pro";
64
+ return undefined;
65
+ }
66
+
67
+ function parseWindow(resetTime: string | undefined): UsageWindow {
68
+ if (!resetTime) {
69
+ return {
70
+ id: "quota",
71
+ label: "Quota window",
72
+ };
73
+ }
74
+ const resetsAt = Date.parse(resetTime);
75
+ if (Number.isNaN(resetsAt)) {
76
+ return {
77
+ id: "quota",
78
+ label: "Quota window",
79
+ };
80
+ }
81
+ return {
82
+ id: `reset-${resetsAt}`,
83
+ label: "Quota window",
84
+ resetsAt,
85
+ };
86
+ }
87
+
88
+ function buildAmount(remainingFraction: number | undefined): UsageAmount {
89
+ if (remainingFraction === undefined || !Number.isFinite(remainingFraction)) {
90
+ return { unit: "percent" };
91
+ }
92
+ const remaining = Math.min(Math.max(remainingFraction, 0), 1);
93
+ const used = Math.min(Math.max(1 - remaining, 0), 1);
94
+ return {
95
+ unit: "percent",
96
+ used: Math.round(used * 1000) / 10,
97
+ remaining: Math.round(remaining * 1000) / 10,
98
+ limit: 100,
99
+ usedFraction: used,
100
+ remainingFraction: remaining,
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Return the OAuth access token to use against `/v1internal:*`. AuthStorage is
106
+ * the sole refresh authority (broker-aware, single-flighted, rotation-safe);
107
+ * if the token landed here expired or near-expired, the next usage cycle will
108
+ * carry a freshly-refreshed credential. Returning `undefined` short-circuits
109
+ * the probe rather than POSTing a stale token to Google.
110
+ */
111
+ function resolveAccessToken(params: UsageFetchParams): string | undefined {
112
+ const { credential } = params;
113
+ if (credential.type !== "oauth") return undefined;
114
+ if (!credential.accessToken) return undefined;
115
+ if (credential.expiresAt !== undefined && credential.expiresAt <= Date.now()) {
116
+ return undefined;
117
+ }
118
+ return credential.accessToken;
119
+ }
120
+
121
+ async function loadCodeAssist(
122
+ params: UsageFetchParams,
123
+ ctx: UsageFetchContext,
124
+ accessToken: string,
125
+ baseUrl: string,
126
+ projectId?: string,
127
+ ): Promise<LoadCodeAssistResponse | undefined> {
128
+ const response = await ctx.fetch(`${baseUrl}/v1internal:loadCodeAssist`, {
129
+ method: "POST",
130
+ headers: {
131
+ Authorization: `Bearer ${accessToken}`,
132
+ "Content-Type": "application/json",
133
+ ...getGeminiCliHeaders(),
134
+ },
135
+ body: JSON.stringify({
136
+ ...(projectId ? { cloudaicompanionProject: projectId } : {}),
137
+ metadata: {
138
+ ideType: "IDE_UNSPECIFIED",
139
+ platform: "PLATFORM_UNSPECIFIED",
140
+ pluginType: "GEMINI",
141
+ },
142
+ }),
143
+ signal: params.signal,
144
+ });
145
+
146
+ if (!response.ok) {
147
+ const errorText = await response.text();
148
+ ctx.logger?.warn("Gemini CLI loadCodeAssist failed", {
149
+ status: response.status,
150
+ error: errorText,
151
+ });
152
+ return undefined;
153
+ }
154
+
155
+ return (await response.json()) as LoadCodeAssistResponse;
156
+ }
157
+
158
+ async function fetchQuota(
159
+ params: UsageFetchParams,
160
+ ctx: UsageFetchContext,
161
+ accessToken: string,
162
+ baseUrl: string,
163
+ projectId?: string,
164
+ ): Promise<RetrieveUserQuotaResponse | undefined> {
165
+ const response = await ctx.fetch(`${baseUrl}/v1internal:retrieveUserQuota`, {
166
+ method: "POST",
167
+ headers: {
168
+ Authorization: `Bearer ${accessToken}`,
169
+ "Content-Type": "application/json",
170
+ ...getGeminiCliHeaders(),
171
+ },
172
+ body: JSON.stringify(projectId ? { project: projectId } : {}),
173
+ signal: params.signal,
174
+ });
175
+
176
+ if (!response.ok) {
177
+ const errorText = await response.text();
178
+ ctx.logger?.warn("Gemini CLI retrieveUserQuota failed", {
179
+ status: response.status,
180
+ error: errorText,
181
+ });
182
+ return undefined;
183
+ }
184
+
185
+ return (await response.json()) as RetrieveUserQuotaResponse;
186
+ }
187
+
188
+ export const googleGeminiCliUsageProvider: UsageProvider = {
189
+ id: "google-gemini-cli",
190
+ supports: ({ credential }) => credential.type === "oauth" && !!credential.accessToken,
191
+ async fetchUsage(params, ctx) {
192
+ const { credential } = params;
193
+ if (credential.type !== "oauth") {
194
+ return null;
195
+ }
196
+ const accessToken = resolveAccessToken(params);
197
+ if (!accessToken) {
198
+ return null;
199
+ }
200
+
201
+ const baseUrl = (params.baseUrl?.trim() || DEFAULT_ENDPOINT).replace(/\/$/, "");
202
+
203
+ const loadResponse = await loadCodeAssist(params, ctx, accessToken, baseUrl, credential.projectId);
204
+ const projectId = credential.projectId ?? getProjectId(loadResponse);
205
+ const quotaResponse = await fetchQuota(params, ctx, accessToken, baseUrl, projectId);
206
+ if (!quotaResponse) {
207
+ return null;
208
+ }
209
+
210
+ const limits: UsageLimit[] = [];
211
+ const buckets = quotaResponse.buckets ?? [];
212
+
213
+ buckets.forEach((bucket, index) => {
214
+ const modelId = bucket.modelId;
215
+ const window = parseWindow(bucket.resetTime);
216
+ const amount = buildAmount(bucket.remainingFraction);
217
+ const tier = modelId ? getModelTier(modelId) : undefined;
218
+ const label = modelId ? `Gemini ${modelId}` : "Gemini quota";
219
+ const id = `${modelId ?? "unknown"}:${window?.id ?? index}`;
220
+
221
+ limits.push({
222
+ id,
223
+ label,
224
+ scope: {
225
+ provider: params.provider,
226
+ accountId: credential.accountId,
227
+ projectId,
228
+ modelId,
229
+ tier,
230
+ windowId: window?.id,
231
+ },
232
+ window,
233
+ amount,
234
+ });
235
+ });
236
+
237
+ const report: UsageReport = {
238
+ provider: params.provider,
239
+ fetchedAt: Date.now(),
240
+ limits,
241
+ metadata: {
242
+ currentTierId: loadResponse?.currentTier?.id,
243
+ currentTierName: loadResponse?.currentTier?.name,
244
+ },
245
+ raw: quotaResponse,
246
+ };
247
+
248
+ return report;
249
+ },
250
+ };
@@ -0,0 +1,421 @@
1
+ /**
2
+ * GitHub Copilot usage provider.
3
+ *
4
+ * Normalizes Copilot quota usage into the shared UsageReport schema.
5
+ */
6
+ import type {
7
+ UsageAmount,
8
+ UsageFetchContext,
9
+ UsageFetchParams,
10
+ UsageLimit,
11
+ UsageProvider,
12
+ UsageReport,
13
+ UsageStatus,
14
+ UsageWindow,
15
+ } from "../usage";
16
+ import { isRecord, toBoolean, toNumber } from "../utils";
17
+ import { OPENCODE_HEADERS } from "../utils/oauth/github-copilot";
18
+
19
+ type CopilotQuotaDetail = {
20
+ entitlement: number;
21
+ overage_count: number;
22
+ overage_permitted: boolean;
23
+ percent_remaining: number;
24
+ quota_id: string;
25
+ quota_remaining: number;
26
+ remaining: number;
27
+ unlimited: boolean;
28
+ };
29
+
30
+ type CopilotQuotaSnapshots = {
31
+ chat?: CopilotQuotaDetail;
32
+ completions?: CopilotQuotaDetail;
33
+ premium_interactions?: CopilotQuotaDetail;
34
+ };
35
+
36
+ type CopilotUsageResponse = {
37
+ copilot_plan: string;
38
+ quota_reset_date: string;
39
+ quota_snapshots: CopilotQuotaSnapshots;
40
+ };
41
+
42
+ type BillingUsageItem = {
43
+ product: string;
44
+ sku: string;
45
+ model?: string;
46
+ unitType: string;
47
+ grossQuantity: number;
48
+ netQuantity: number;
49
+ limit?: number;
50
+ };
51
+
52
+ type BillingUsageResponse = {
53
+ timePeriod: { year: number; month?: number };
54
+ user: string;
55
+ usageItems: BillingUsageItem[];
56
+ };
57
+
58
+ function resolveGitHubApiBaseUrl(params: UsageFetchParams): string {
59
+ const baseUrl = params.baseUrl?.replace(/\/$/, "");
60
+ if (baseUrl && !baseUrl.includes("githubcopilot.com")) return baseUrl;
61
+ const enterpriseUrl = params.credential.enterpriseUrl?.trim();
62
+ if (!enterpriseUrl) return "https://api.github.com";
63
+ if (enterpriseUrl.startsWith("http://") || enterpriseUrl.startsWith("https://")) {
64
+ return enterpriseUrl.replace(/\/$/, "");
65
+ }
66
+ if (enterpriseUrl.startsWith("api.")) {
67
+ return `https://${enterpriseUrl}`;
68
+ }
69
+ return `https://api.${enterpriseUrl}`;
70
+ }
71
+
72
+ function buildWindow(resetDate: string | undefined): UsageWindow | undefined {
73
+ if (!resetDate) return undefined;
74
+ const resetAt = Date.parse(resetDate);
75
+ if (!Number.isFinite(resetAt)) return undefined;
76
+ return {
77
+ id: "monthly",
78
+ label: "Monthly",
79
+ resetsAt: resetAt,
80
+ };
81
+ }
82
+
83
+ function buildAmount(used: number | undefined, limit: number | undefined, unit: UsageAmount["unit"]): UsageAmount {
84
+ const safeLimit = limit !== undefined && Number.isFinite(limit) ? limit : undefined;
85
+ const safeUsed = used !== undefined && Number.isFinite(used) ? used : undefined;
86
+ const remaining = safeLimit !== undefined && safeUsed !== undefined ? Math.max(0, safeLimit - safeUsed) : undefined;
87
+ const usedFraction =
88
+ safeLimit !== undefined && safeUsed !== undefined && safeLimit > 0 ? safeUsed / safeLimit : undefined;
89
+ const remainingFraction =
90
+ safeLimit !== undefined && remaining !== undefined && safeLimit > 0 ? remaining / safeLimit : undefined;
91
+ return {
92
+ used: safeUsed,
93
+ limit: safeLimit,
94
+ remaining,
95
+ usedFraction,
96
+ remainingFraction,
97
+ unit,
98
+ };
99
+ }
100
+
101
+ function deriveStatus(amount: UsageAmount, unlimited: boolean): UsageStatus {
102
+ if (unlimited) return "ok";
103
+ if (amount.remainingFraction === undefined) return "unknown";
104
+ if (amount.remainingFraction <= 0) return "exhausted";
105
+ if (amount.remainingFraction <= 0.1) return "warning";
106
+ return "ok";
107
+ }
108
+
109
+ function parseQuotaDetail(value: unknown): CopilotQuotaDetail | null {
110
+ if (!isRecord(value)) return null;
111
+ const entitlement = toNumber(value.entitlement);
112
+ const remaining = toNumber(value.remaining);
113
+ const percentRemaining = toNumber(value.percent_remaining);
114
+ const unlimited = toBoolean(value.unlimited);
115
+ if (
116
+ entitlement === undefined ||
117
+ remaining === undefined ||
118
+ percentRemaining === undefined ||
119
+ unlimited === undefined
120
+ ) {
121
+ return null;
122
+ }
123
+ const overageCount = toNumber(value.overage_count) ?? 0;
124
+ const overagePermitted = toBoolean(value.overage_permitted) ?? false;
125
+ const quotaId = typeof value.quota_id === "string" ? value.quota_id : "";
126
+ const quotaRemaining = toNumber(value.quota_remaining) ?? remaining;
127
+ return {
128
+ entitlement,
129
+ overage_count: overageCount,
130
+ overage_permitted: overagePermitted,
131
+ percent_remaining: percentRemaining,
132
+ quota_id: quotaId,
133
+ quota_remaining: quotaRemaining,
134
+ remaining,
135
+ unlimited,
136
+ };
137
+ }
138
+
139
+ async function fetchJson(ctx: UsageFetchContext, url: string, init: RequestInit): Promise<unknown> {
140
+ const response = await ctx.fetch(url, init);
141
+ if (!response.ok) {
142
+ const text = await response.text();
143
+ throw new Error(`${response.status} ${response.statusText}: ${text}`);
144
+ }
145
+ return response.json();
146
+ }
147
+
148
+ async function resolveGitHubUsername(
149
+ ctx: UsageFetchContext,
150
+ baseUrl: string,
151
+ token: string,
152
+ signal?: AbortSignal,
153
+ ): Promise<string | undefined> {
154
+ try {
155
+ const data = await fetchJson(ctx, `${baseUrl}/user`, {
156
+ headers: {
157
+ Accept: "application/vnd.github+json",
158
+ Authorization: `Bearer ${token}`,
159
+ "X-GitHub-Api-Version": "2022-11-28",
160
+ },
161
+ signal,
162
+ });
163
+ if (!isRecord(data)) return undefined;
164
+ return typeof data.login === "string" ? data.login : undefined;
165
+ } catch {
166
+ return undefined;
167
+ }
168
+ }
169
+
170
+ async function fetchInternalUsage(
171
+ ctx: UsageFetchContext,
172
+ githubApiBaseUrl: string,
173
+ token: string,
174
+ signal?: AbortSignal,
175
+ ): Promise<CopilotUsageResponse> {
176
+ const headers: Record<string, string> = {
177
+ "Content-Type": "application/json",
178
+ Accept: "application/json",
179
+ Authorization: `Bearer ${token}`,
180
+ ...OPENCODE_HEADERS,
181
+ };
182
+ const data = await fetchJson(ctx, `${githubApiBaseUrl}/copilot_internal/user`, { headers, signal });
183
+ if (!isRecord(data)) throw new Error("Invalid Copilot usage response");
184
+ return data as CopilotUsageResponse;
185
+ }
186
+
187
+ async function fetchBillingUsage(
188
+ ctx: UsageFetchContext,
189
+ baseUrl: string,
190
+ username: string,
191
+ token: string,
192
+ signal?: AbortSignal,
193
+ ): Promise<BillingUsageResponse> {
194
+ const data = await fetchJson(
195
+ ctx,
196
+ `${baseUrl}/users/${encodeURIComponent(username)}/settings/billing/premium_request/usage`,
197
+ {
198
+ headers: {
199
+ Accept: "application/vnd.github+json",
200
+ Authorization: `Bearer ${token}`,
201
+ "X-GitHub-Api-Version": "2022-11-28",
202
+ },
203
+ signal,
204
+ },
205
+ );
206
+
207
+ if (!isRecord(data)) throw new Error("Invalid Copilot billing usage response");
208
+ return data as BillingUsageResponse;
209
+ }
210
+
211
+ function buildLimitFromQuota(
212
+ key: string,
213
+ label: string,
214
+ quota: CopilotQuotaDetail,
215
+ plan: string,
216
+ window: UsageWindow | undefined,
217
+ accountId?: string,
218
+ ): UsageLimit {
219
+ const used = quota.unlimited ? undefined : Math.max(0, quota.entitlement - quota.remaining);
220
+ const limit = quota.unlimited ? undefined : quota.entitlement;
221
+ const amount = buildAmount(used, limit, "requests");
222
+ const notes: string[] = [];
223
+ if (quota.unlimited) notes.push("Unlimited");
224
+ if (quota.overage_count > 0) {
225
+ notes.push(`Overage requests: ${quota.overage_count}`);
226
+ }
227
+ return {
228
+ id: `copilot:${key}`,
229
+ label,
230
+ scope: {
231
+ provider: "github-copilot",
232
+ accountId,
233
+ tier: plan,
234
+ windowId: window?.id,
235
+ },
236
+ window,
237
+ amount,
238
+ status: deriveStatus(amount, quota.unlimited),
239
+ notes: notes.length > 0 ? notes : undefined,
240
+ };
241
+ }
242
+
243
+ function normalizeQuotaSnapshots(
244
+ data: CopilotUsageResponse,
245
+ accountId?: string,
246
+ ): { limits: UsageLimit[]; window?: UsageWindow } {
247
+ const window = buildWindow(data.quota_reset_date);
248
+ const snapshots = data.quota_snapshots ?? {};
249
+ const limits: UsageLimit[] = [];
250
+ const premium = parseQuotaDetail(snapshots.premium_interactions);
251
+ if (premium) {
252
+ limits.push(buildLimitFromQuota("premium", "Premium Requests", premium, data.copilot_plan, window, accountId));
253
+ }
254
+ const chat = parseQuotaDetail(snapshots.chat);
255
+ if (chat && !chat.unlimited) {
256
+ limits.push(buildLimitFromQuota("chat", "Chat Requests", chat, data.copilot_plan, window, accountId));
257
+ }
258
+ const completions = parseQuotaDetail(snapshots.completions);
259
+ if (completions && !completions.unlimited) {
260
+ limits.push(buildLimitFromQuota("completions", "Completions", completions, data.copilot_plan, window, accountId));
261
+ }
262
+ return { limits, window };
263
+ }
264
+
265
+ function normalizeBillingUsage(data: BillingUsageResponse): UsageLimit[] {
266
+ const limits: UsageLimit[] = [];
267
+ const periodLabel = data.timePeriod.month
268
+ ? `${data.timePeriod.year}-${String(data.timePeriod.month).padStart(2, "0")}`
269
+ : `${data.timePeriod.year}`;
270
+ const window: UsageWindow = {
271
+ id: "billing-period",
272
+ label: periodLabel,
273
+ };
274
+
275
+ const premiumItems = data.usageItems.filter(
276
+ item => item.sku === "Copilot Premium Request" || item.sku.includes("Premium"),
277
+ );
278
+ const totalUsed = premiumItems.reduce((sum, item) => sum + item.grossQuantity, 0);
279
+ const totalLimit = premiumItems.reduce((sum, item) => sum + (item.limit ?? 0), 0) || undefined;
280
+ const totalAmount = buildAmount(totalUsed, totalLimit, "requests");
281
+ limits.push({
282
+ id: "copilot:premium",
283
+ label: "Premium Requests",
284
+ scope: {
285
+ provider: "github-copilot",
286
+ accountId: data.user,
287
+ windowId: window.id,
288
+ },
289
+ window,
290
+ amount: totalAmount,
291
+ status: deriveStatus(totalAmount, false),
292
+ });
293
+
294
+ for (const item of data.usageItems) {
295
+ if (!item.model) continue;
296
+ if (item.grossQuantity <= 0) continue;
297
+ const amount = buildAmount(item.grossQuantity, item.limit, "requests");
298
+ limits.push({
299
+ id: `copilot:model:${item.model}`,
300
+ label: `Model ${item.model}`,
301
+ scope: {
302
+ provider: "github-copilot",
303
+ accountId: data.user,
304
+ modelId: item.model,
305
+ windowId: window.id,
306
+ },
307
+ window,
308
+ amount,
309
+ status: deriveStatus(amount, false),
310
+ });
311
+ }
312
+
313
+ return limits;
314
+ }
315
+
316
+ export const githubCopilotUsageProvider: UsageProvider = {
317
+ id: "github-copilot",
318
+ supports: ({ provider, credential }) => {
319
+ if (provider !== "github-copilot") return false;
320
+ if (credential.type === "oauth") {
321
+ return Boolean(credential.refreshToken || credential.accessToken);
322
+ }
323
+ return Boolean(credential.apiKey);
324
+ },
325
+ fetchUsage: async (params, ctx) => {
326
+ if (!githubCopilotUsageProvider.supports?.(params)) return null;
327
+
328
+ const githubApiBaseUrl = resolveGitHubApiBaseUrl(params);
329
+ let report: UsageReport | null = null;
330
+
331
+ if (params.credential.type === "api_key") {
332
+ let username: string | undefined;
333
+ const candidate =
334
+ params.credential.accountId || params.credential.metadata?.username || params.credential.metadata?.user;
335
+ if (typeof candidate === "string" && candidate.trim()) {
336
+ username = candidate.trim();
337
+ }
338
+ if (!username && params.credential.apiKey) {
339
+ username = await resolveGitHubUsername(ctx, githubApiBaseUrl, params.credential.apiKey, params.signal);
340
+ }
341
+ if (!username) {
342
+ ctx.logger?.warn("Copilot usage requires username for billing API", { provider: params.provider });
343
+ } else if (params.credential.apiKey) {
344
+ try {
345
+ const billing = await fetchBillingUsage(
346
+ ctx,
347
+ githubApiBaseUrl,
348
+ username,
349
+ params.credential.apiKey,
350
+ params.signal,
351
+ );
352
+ report = {
353
+ provider: "github-copilot",
354
+ fetchedAt: Date.now(),
355
+ limits: normalizeBillingUsage(billing),
356
+ metadata: {
357
+ accountId: billing.user,
358
+ account: billing.user,
359
+ period: billing.timePeriod,
360
+ },
361
+ };
362
+ } catch (error) {
363
+ ctx.logger?.warn("Copilot usage fetch failed", { error: String(error) });
364
+ }
365
+ }
366
+ if (!report && params.credential.apiKey) {
367
+ try {
368
+ const usage = await fetchInternalUsage(ctx, githubApiBaseUrl, params.credential.apiKey, params.signal);
369
+ const normalized = normalizeQuotaSnapshots(usage, username);
370
+ report = {
371
+ provider: "github-copilot",
372
+ fetchedAt: Date.now(),
373
+ limits: normalized.limits,
374
+ metadata: {
375
+ accountId: username,
376
+ plan: usage.copilot_plan,
377
+ quotaResetDate: usage.quota_reset_date,
378
+ },
379
+ raw: usage,
380
+ };
381
+ } catch (error) {
382
+ ctx.logger?.warn("Copilot usage fetch failed", { error: String(error) });
383
+ }
384
+ }
385
+ } else {
386
+ const { refreshToken, accessToken } = params.credential;
387
+ if (!refreshToken && !accessToken) return null;
388
+ const oauthToken = refreshToken || accessToken;
389
+ if (!oauthToken) return null;
390
+ const githubToken = refreshToken ?? accessToken;
391
+ if (!githubToken) return null;
392
+ try {
393
+ const usage = await fetchInternalUsage(ctx, githubApiBaseUrl, githubToken, params.signal);
394
+ let accountId = params.credential.accountId;
395
+ if (!accountId && refreshToken) {
396
+ accountId = await resolveGitHubUsername(ctx, githubApiBaseUrl, refreshToken, params.signal);
397
+ }
398
+ if (!accountId && accessToken) {
399
+ accountId = await resolveGitHubUsername(ctx, githubApiBaseUrl, accessToken, params.signal);
400
+ }
401
+ const normalized = normalizeQuotaSnapshots(usage, accountId);
402
+ report = {
403
+ provider: "github-copilot",
404
+ fetchedAt: Date.now(),
405
+ limits: normalized.limits,
406
+ metadata: {
407
+ accountId,
408
+ email: params.credential.email,
409
+ plan: usage.copilot_plan,
410
+ quotaResetDate: usage.quota_reset_date,
411
+ },
412
+ raw: usage,
413
+ };
414
+ } catch (error) {
415
+ ctx.logger?.warn("Copilot usage fetch failed", { error: String(error) });
416
+ }
417
+ }
418
+
419
+ return report;
420
+ },
421
+ };