@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,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
+ };
@@ -0,0 +1,201 @@
1
+ import { getAntigravityUserAgent } from "../providers/google-gemini-headers";
2
+ import type {
3
+ UsageAmount,
4
+ UsageFetchContext,
5
+ UsageFetchParams,
6
+ UsageLimit,
7
+ UsageProvider,
8
+ UsageReport,
9
+ UsageStatus,
10
+ UsageWindow,
11
+ } from "../usage";
12
+
13
+ // (Refresh is the sole responsibility of AuthStorage; no provider-direct refresh here.)
14
+
15
+ interface AntigravityQuotaInfo {
16
+ remainingFraction?: number;
17
+ resetTime?: string;
18
+ tier?: string;
19
+ windowId?: string;
20
+ windowLabel?: string;
21
+ }
22
+
23
+ interface AntigravityModelInfo {
24
+ displayName?: string;
25
+ quotaInfo?: AntigravityQuotaInfo | AntigravityQuotaInfo[];
26
+ quotaInfos?: AntigravityQuotaInfo[];
27
+ quotaInfoByTier?: Record<string, AntigravityQuotaInfo | AntigravityQuotaInfo[]>;
28
+ }
29
+
30
+ interface AntigravityUsageResponse {
31
+ models: Record<string, AntigravityModelInfo>;
32
+ }
33
+
34
+ const DEFAULT_ENDPOINT = "https://daily-cloudcode-pa.googleapis.com";
35
+ const FETCH_AVAILABLE_MODELS_PATH = "/v1internal:fetchAvailableModels";
36
+
37
+ function clampFraction(value: number | undefined): number | undefined {
38
+ if (value === undefined || !Number.isFinite(value)) return undefined;
39
+ if (value < 0) return 0;
40
+ if (value > 1) return 1;
41
+ return value;
42
+ }
43
+
44
+ function getUsageStatus(remainingFraction: number | undefined): UsageStatus | undefined {
45
+ if (remainingFraction === undefined) return "unknown";
46
+ if (remainingFraction <= 0) return "exhausted";
47
+ if (remainingFraction <= 0.1) return "warning";
48
+ return "ok";
49
+ }
50
+
51
+ function parseWindow(info: AntigravityQuotaInfo): UsageWindow | undefined {
52
+ if (!info.resetTime) return undefined;
53
+ const resetAt = Date.parse(info.resetTime);
54
+ if (!Number.isFinite(resetAt)) return undefined;
55
+ return {
56
+ id: info.windowId ?? "default",
57
+ label: info.windowLabel ?? "Default",
58
+ resetsAt: resetAt,
59
+ };
60
+ }
61
+
62
+ function buildAmount(info: AntigravityQuotaInfo): UsageAmount {
63
+ const remainingFraction = clampFraction(info.remainingFraction);
64
+ const amount: UsageAmount = { unit: "percent" };
65
+ if (remainingFraction === undefined) return amount;
66
+ const usedFraction = clampFraction(1 - remainingFraction);
67
+ amount.remainingFraction = remainingFraction;
68
+ amount.usedFraction = usedFraction;
69
+ amount.remaining = remainingFraction * 100;
70
+ amount.used = usedFraction !== undefined ? usedFraction * 100 : undefined;
71
+ amount.limit = 100;
72
+ return amount;
73
+ }
74
+
75
+ function normalizeQuotaInfos(info: AntigravityModelInfo): AntigravityQuotaInfo[] {
76
+ const results: AntigravityQuotaInfo[] = [];
77
+ const addInfo = (value: AntigravityQuotaInfo, tier?: string) => {
78
+ results.push({ ...value, ...(tier ? { tier } : {}) });
79
+ };
80
+ const addArray = (values?: AntigravityQuotaInfo[]) => {
81
+ if (!values) return;
82
+ for (const value of values) addInfo(value);
83
+ };
84
+
85
+ if (Array.isArray(info.quotaInfo)) {
86
+ addArray(info.quotaInfo);
87
+ } else if (info.quotaInfo) {
88
+ addInfo(info.quotaInfo);
89
+ }
90
+ addArray(info.quotaInfos);
91
+
92
+ if (info.quotaInfoByTier) {
93
+ for (const [tier, value] of Object.entries(info.quotaInfoByTier)) {
94
+ if (Array.isArray(value)) {
95
+ for (const entry of value) addInfo(entry, tier);
96
+ } else if (value) {
97
+ addInfo(value, tier);
98
+ }
99
+ }
100
+ }
101
+
102
+ return results;
103
+ }
104
+
105
+ /**
106
+ * Return the OAuth access token to use against `/v1internal:*`. AuthStorage is
107
+ * the sole refresh authority (broker-aware, single-flighted, rotation-safe);
108
+ * an expired token short-circuits the probe rather than POSTing the broker
109
+ * sentinel back to Google.
110
+ */
111
+ function resolveAccessToken(params: UsageFetchParams): string | undefined {
112
+ const { credential } = params;
113
+ if (!credential.accessToken) return undefined;
114
+ if (credential.expiresAt !== undefined && credential.expiresAt <= Date.now()) {
115
+ return undefined;
116
+ }
117
+ return credential.accessToken;
118
+ }
119
+
120
+ async function fetchAntigravityUsage(params: UsageFetchParams, ctx: UsageFetchContext): Promise<UsageReport | null> {
121
+ const credential = params.credential;
122
+ if (!credential.projectId) return null;
123
+
124
+ const nowMs = Date.now();
125
+
126
+ const accessToken = resolveAccessToken(params);
127
+ if (!accessToken) return null;
128
+
129
+ const baseUrl = params.baseUrl?.replace(/\/+$/, "") || DEFAULT_ENDPOINT;
130
+ const url = `${baseUrl}${FETCH_AVAILABLE_MODELS_PATH}`;
131
+ const response = await ctx.fetch(url, {
132
+ method: "POST",
133
+ headers: {
134
+ Authorization: `Bearer ${accessToken}`,
135
+ "Content-Type": "application/json",
136
+ "User-Agent": getAntigravityUserAgent(),
137
+ },
138
+ body: JSON.stringify({ project: credential.projectId }),
139
+ signal: params.signal,
140
+ });
141
+
142
+ if (!response.ok) {
143
+ ctx.logger?.warn("Antigravity usage fetch failed", {
144
+ status: response.status,
145
+ statusText: response.statusText,
146
+ });
147
+ return null;
148
+ }
149
+
150
+ const data = (await response.json()) as AntigravityUsageResponse;
151
+ const limits: UsageLimit[] = [];
152
+ let earliestReset: number | undefined;
153
+
154
+ for (const [modelId, modelInfo] of Object.entries(data.models ?? {})) {
155
+ const quotaInfos = normalizeQuotaInfos(modelInfo);
156
+ for (const quotaInfo of quotaInfos) {
157
+ const amount = buildAmount(quotaInfo);
158
+ const window = parseWindow(quotaInfo);
159
+ if (window?.resetsAt) {
160
+ earliestReset = earliestReset ? Math.min(earliestReset, window.resetsAt) : window.resetsAt;
161
+ }
162
+ const labelBase = modelInfo.displayName || modelId;
163
+ const label = quotaInfo.tier ? `${labelBase} (${quotaInfo.tier})` : labelBase;
164
+ const windowId = window?.id ?? "default";
165
+ limits.push({
166
+ id: `${modelId}:${quotaInfo.tier ?? "default"}:${windowId}`,
167
+ label,
168
+ scope: {
169
+ provider: params.provider,
170
+ accountId: credential.accountId,
171
+ projectId: credential.projectId,
172
+ modelId,
173
+ tier: quotaInfo.tier,
174
+ windowId,
175
+ },
176
+ window,
177
+ amount,
178
+ status: getUsageStatus(amount.remainingFraction),
179
+ });
180
+ }
181
+ }
182
+
183
+ const report: UsageReport = {
184
+ provider: params.provider,
185
+ fetchedAt: nowMs,
186
+ limits,
187
+ metadata: {
188
+ endpoint: url,
189
+ projectId: credential.projectId,
190
+ },
191
+ raw: data,
192
+ };
193
+
194
+ return report;
195
+ }
196
+
197
+ export const antigravityUsageProvider: UsageProvider = {
198
+ id: "google-antigravity",
199
+ fetchUsage: fetchAntigravityUsage,
200
+ supports: params => params.provider === "google-antigravity",
201
+ };