@prometheus-ai/ai 0.5.4 → 0.5.8

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 (377) hide show
  1. package/dist/types/auth-broker/remote-store.d.ts +2 -1
  2. package/dist/types/auth-broker/wire-schemas.d.ts +4 -1
  3. package/dist/types/auth-gateway/server.d.ts +19 -0
  4. package/dist/types/auth-gateway/types.d.ts +9 -3
  5. package/dist/types/auth-retry.d.ts +119 -0
  6. package/dist/types/auth-storage.d.ts +217 -8
  7. package/dist/types/errors.d.ts +24 -0
  8. package/dist/types/index.d.ts +5 -9
  9. package/dist/types/provider-details.d.ts +1 -1
  10. package/dist/types/providers/amazon-bedrock.d.ts +12 -6
  11. package/dist/types/providers/anthropic-client.d.ts +10 -3
  12. package/dist/types/providers/anthropic-messages-server-schema.d.ts +2 -2
  13. package/dist/types/providers/anthropic-messages-server.d.ts +3 -3
  14. package/dist/types/providers/anthropic-wire.d.ts +3 -3
  15. package/dist/types/providers/anthropic.d.ts +41 -34
  16. package/dist/types/providers/aws-credentials.d.ts +8 -0
  17. package/dist/types/providers/azure-openai-responses.d.ts +1 -0
  18. package/dist/types/providers/google-gemini-cli.d.ts +22 -1
  19. package/dist/types/providers/google-shared.d.ts +22 -0
  20. package/dist/types/providers/google-types.d.ts +13 -1
  21. package/dist/types/providers/mock.d.ts +8 -3
  22. package/dist/types/providers/ollama.d.ts +6 -0
  23. package/dist/types/providers/openai-chat-server-schema.d.ts +6 -3
  24. package/dist/types/providers/openai-chat-server.d.ts +3 -3
  25. package/dist/types/providers/openai-chat-wire.d.ts +644 -0
  26. package/dist/types/providers/openai-codex/request-transformer.d.ts +8 -0
  27. package/dist/types/providers/openai-codex/response-handler.d.ts +9 -0
  28. package/dist/types/providers/openai-codex-responses.d.ts +31 -2
  29. package/dist/types/providers/openai-completions-compat.d.ts +2 -25
  30. package/dist/types/providers/openai-completions.d.ts +2 -10
  31. package/dist/types/providers/openai-responses-server-schema.d.ts +4 -4
  32. package/dist/types/providers/openai-responses-server.d.ts +2 -2
  33. package/dist/types/providers/openai-responses-shared.d.ts +49 -9
  34. package/dist/types/providers/openai-responses-wire.d.ts +6065 -0
  35. package/dist/types/providers/openai-responses.d.ts +13 -4
  36. package/dist/types/providers/prometheus-native-client.d.ts +9 -0
  37. package/dist/types/providers/prometheus-native-server.d.ts +4 -3
  38. package/dist/types/providers/transform-messages.d.ts +1 -2
  39. package/dist/types/rate-limit-utils.d.ts +3 -2
  40. package/dist/types/registry/aimlapi.d.ts +4 -0
  41. package/dist/types/registry/alibaba-coding-plan.d.ts +7 -0
  42. package/dist/types/registry/amazon-bedrock.d.ts +5 -0
  43. package/dist/types/registry/anthropic.d.ts +10 -0
  44. package/dist/types/{utils/oauth → registry}/api-key-login.d.ts +8 -2
  45. package/dist/types/{utils/oauth → registry}/api-key-validation.d.ts +15 -0
  46. package/dist/types/registry/cerebras.d.ts +7 -0
  47. package/dist/types/registry/cloudflare-ai-gateway.d.ts +13 -0
  48. package/dist/types/registry/cursor.d.ts +7 -0
  49. package/dist/types/registry/deepseek.d.ts +8 -0
  50. package/dist/types/registry/derived.d.ts +5 -0
  51. package/dist/types/registry/firepass.d.ts +16 -0
  52. package/dist/types/registry/fireworks.d.ts +7 -0
  53. package/dist/types/registry/github-copilot.d.ts +7 -0
  54. package/dist/types/registry/gitlab-duo.d.ts +9 -0
  55. package/dist/types/registry/google-antigravity.d.ts +9 -0
  56. package/dist/types/registry/google-gemini-cli.d.ts +9 -0
  57. package/dist/types/registry/google-vertex.d.ts +5 -0
  58. package/dist/types/registry/google.d.ts +4 -0
  59. package/dist/types/registry/groq.d.ts +4 -0
  60. package/dist/types/registry/huggingface.d.ts +7 -0
  61. package/dist/types/registry/index.d.ts +4 -0
  62. package/dist/types/registry/kagi.d.ts +14 -0
  63. package/dist/types/registry/kilo.d.ts +7 -0
  64. package/dist/types/registry/kimi-code.d.ts +7 -0
  65. package/dist/types/registry/litellm.d.ts +13 -0
  66. package/dist/types/registry/lm-studio.d.ts +8 -0
  67. package/dist/types/registry/minimax-code-cn.d.ts +6 -0
  68. package/dist/types/registry/minimax-code.d.ts +6 -0
  69. package/dist/types/registry/minimax.d.ts +4 -0
  70. package/dist/types/registry/mistral.d.ts +4 -0
  71. package/dist/types/registry/moonshot.d.ts +7 -0
  72. package/dist/types/registry/nanogpt.d.ts +7 -0
  73. package/dist/types/registry/nvidia.d.ts +7 -0
  74. package/dist/types/registry/oauth/__tests__/xai-oauth.test.d.ts +1 -0
  75. package/dist/types/{utils → registry}/oauth/anthropic.d.ts +2 -1
  76. package/dist/types/{utils → registry}/oauth/github-copilot.d.ts +15 -23
  77. package/dist/types/{utils → registry}/oauth/index.d.ts +1 -0
  78. package/dist/types/{utils → registry}/oauth/minimax-code.d.ts +5 -5
  79. package/dist/types/{utils → registry}/oauth/types.d.ts +6 -1
  80. package/dist/types/{utils → registry}/oauth/xai-oauth.d.ts +2 -1
  81. package/dist/types/registry/ollama-cloud.d.ts +7 -0
  82. package/dist/types/registry/ollama.d.ts +12 -0
  83. package/dist/types/registry/openai-codex-device.d.ts +8 -0
  84. package/dist/types/registry/openai-codex.d.ts +9 -0
  85. package/dist/types/registry/openai.d.ts +4 -0
  86. package/dist/types/registry/opencode-go.d.ts +6 -0
  87. package/dist/types/registry/opencode-zen.d.ts +6 -0
  88. package/dist/types/registry/openrouter.d.ts +13 -0
  89. package/dist/types/registry/parallel.d.ts +14 -0
  90. package/dist/types/registry/perplexity.d.ts +7 -0
  91. package/dist/types/registry/qianfan.d.ts +7 -0
  92. package/dist/types/registry/qwen-portal.d.ts +7 -0
  93. package/dist/types/registry/registry.d.ts +272 -0
  94. package/dist/types/registry/synthetic.d.ts +6 -0
  95. package/dist/types/registry/tavily.d.ts +14 -0
  96. package/dist/types/registry/together.d.ts +6 -0
  97. package/dist/types/registry/types.d.ts +51 -0
  98. package/dist/types/registry/venice.d.ts +13 -0
  99. package/dist/types/registry/vercel-ai-gateway.d.ts +7 -0
  100. package/dist/types/registry/vllm.d.ts +7 -0
  101. package/dist/types/registry/wafer-pass.d.ts +6 -0
  102. package/dist/types/registry/wafer-serverless.d.ts +6 -0
  103. package/dist/types/registry/xai-oauth.d.ts +7 -0
  104. package/dist/types/registry/xai.d.ts +4 -0
  105. package/dist/types/registry/xiaomi-token-plan-ams.d.ts +6 -0
  106. package/dist/types/registry/xiaomi-token-plan-cn.d.ts +6 -0
  107. package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +6 -0
  108. package/dist/types/registry/xiaomi.d.ts +6 -0
  109. package/dist/types/registry/zai.d.ts +7 -0
  110. package/dist/types/registry/zenmux.d.ts +7 -0
  111. package/dist/types/registry/zhipu-coding-plan.d.ts +7 -0
  112. package/dist/types/stream.d.ts +9 -1
  113. package/dist/types/types.d.ts +56 -295
  114. package/dist/types/usage/google-antigravity.d.ts +15 -1
  115. package/dist/types/usage/openai-codex-reset.d.ts +79 -0
  116. package/dist/types/usage/openai-codex.d.ts +1 -0
  117. package/dist/types/usage.d.ts +77 -4
  118. package/dist/types/utils/abort.d.ts +6 -0
  119. package/dist/types/utils/event-stream.d.ts +2 -0
  120. package/dist/types/utils/http-inspector.d.ts +0 -1
  121. package/dist/types/utils/idle-iterator.d.ts +35 -0
  122. package/dist/types/utils/openai-http.d.ts +58 -0
  123. package/dist/types/utils/request-debug.d.ts +3 -0
  124. package/dist/types/utils/retry-after.d.ts +1 -0
  125. package/dist/types/utils/schema/fields.d.ts +5 -0
  126. package/dist/types/utils/schema/json-schema-validator.d.ts +8 -0
  127. package/dist/types/utils/schema/stamps.d.ts +7 -15
  128. package/dist/types/utils/sse-debug.d.ts +0 -5
  129. package/dist/types/utils/stream-markup-healing.d.ts +2 -0
  130. package/dist/types/utils.d.ts +1 -5
  131. package/package.json +17 -29
  132. package/src/auth-broker/remote-store.ts +10 -1
  133. package/src/auth-broker/snapshot-cache.ts +1 -1
  134. package/src/auth-broker/wire-schemas.ts +1 -1
  135. package/src/auth-gateway/http.ts +1 -1
  136. package/src/auth-gateway/server.ts +95 -30
  137. package/src/auth-gateway/types.ts +10 -2
  138. package/src/auth-retry.ts +238 -0
  139. package/src/auth-storage.ts +935 -430
  140. package/src/errors.ts +32 -0
  141. package/src/index.ts +9 -14
  142. package/src/provider-details.ts +1 -1
  143. package/src/providers/__tests__/google-auth.test.ts +144 -0
  144. package/src/providers/amazon-bedrock.ts +70 -40
  145. package/src/providers/anthropic-client.ts +15 -13
  146. package/src/providers/anthropic-messages-server-schema.ts +17 -7
  147. package/src/providers/anthropic-messages-server.ts +88 -20
  148. package/src/providers/anthropic-wire.ts +4 -3
  149. package/src/providers/anthropic.ts +1234 -621
  150. package/src/providers/aws-credentials.ts +47 -5
  151. package/src/providers/aws-eventstream.ts +5 -0
  152. package/src/providers/azure-openai-responses.ts +117 -67
  153. package/src/providers/cursor.ts +30 -30
  154. package/src/providers/github-copilot-headers.ts +1 -1
  155. package/src/providers/gitlab-duo.ts +36 -29
  156. package/src/providers/google-auth.ts +71 -8
  157. package/src/providers/google-gemini-cli.ts +118 -22
  158. package/src/providers/google-shared.ts +163 -43
  159. package/src/providers/google-types.ts +10 -1
  160. package/src/providers/kimi.ts +1 -1
  161. package/src/providers/mock.ts +11 -3
  162. package/src/providers/ollama.ts +64 -7
  163. package/src/providers/openai-anthropic-shim.ts +17 -8
  164. package/src/providers/openai-chat-server-schema.ts +9 -3
  165. package/src/providers/openai-chat-server.ts +82 -16
  166. package/src/providers/openai-chat-wire.ts +847 -0
  167. package/src/providers/openai-codex/request-transformer.ts +129 -34
  168. package/src/providers/openai-codex/response-handler.ts +22 -1
  169. package/src/providers/openai-codex-responses.ts +699 -247
  170. package/src/providers/openai-completions-compat.ts +8 -308
  171. package/src/providers/openai-completions.ts +416 -267
  172. package/src/providers/openai-responses-server-schema.ts +15 -9
  173. package/src/providers/openai-responses-server.ts +162 -114
  174. package/src/providers/openai-responses-shared.ts +320 -82
  175. package/src/providers/openai-responses-wire.ts +6391 -0
  176. package/src/providers/openai-responses.ts +382 -176
  177. package/src/providers/prometheus-native-client.ts +27 -11
  178. package/src/providers/prometheus-native-server.ts +44 -17
  179. package/src/providers/transform-messages.ts +311 -120
  180. package/src/providers/vision-guard.ts +5 -3
  181. package/src/rate-limit-utils.ts +13 -3
  182. package/src/registry/aimlapi.ts +6 -0
  183. package/src/{utils/oauth → registry}/alibaba-coding-plan.ts +8 -18
  184. package/src/registry/amazon-bedrock.ts +22 -0
  185. package/src/registry/anthropic.ts +26 -0
  186. package/src/{utils/oauth → registry}/api-key-login.ts +25 -3
  187. package/src/{utils/oauth → registry}/api-key-validation.ts +62 -2
  188. package/src/{utils/oauth → registry}/cerebras.ts +8 -1
  189. package/src/{utils/oauth → registry}/cloudflare-ai-gateway.ts +8 -12
  190. package/src/registry/cursor.ts +20 -0
  191. package/src/{utils/oauth → registry}/deepseek.ts +9 -17
  192. package/src/registry/derived.ts +9 -0
  193. package/src/{utils/oauth → registry}/firepass.ts +10 -2
  194. package/src/{utils/oauth → registry}/fireworks.ts +8 -1
  195. package/src/registry/github-copilot.ts +22 -0
  196. package/src/registry/gitlab-duo.ts +19 -0
  197. package/src/registry/google-antigravity.ts +21 -0
  198. package/src/registry/google-gemini-cli.ts +21 -0
  199. package/src/registry/google-vertex.ts +38 -0
  200. package/src/registry/google.ts +6 -0
  201. package/src/registry/groq.ts +6 -0
  202. package/src/{utils/oauth → registry}/huggingface.ts +8 -19
  203. package/src/registry/index.ts +4 -0
  204. package/src/{utils/oauth → registry}/kagi.ts +9 -11
  205. package/src/{utils/oauth → registry}/kilo.ts +11 -6
  206. package/src/registry/kimi-code.ts +17 -0
  207. package/src/{utils/oauth → registry}/litellm.ts +8 -12
  208. package/src/{utils/oauth → registry}/lm-studio.ts +9 -17
  209. package/src/registry/minimax-code-cn.ts +12 -0
  210. package/src/registry/minimax-code.ts +12 -0
  211. package/src/registry/minimax.ts +6 -0
  212. package/src/registry/mistral.ts +6 -0
  213. package/src/{utils/oauth → registry}/moonshot.ts +8 -9
  214. package/src/{utils/oauth → registry}/nanogpt.ts +8 -1
  215. package/src/{utils/oauth → registry}/nvidia.ts +8 -18
  216. package/src/{utils → registry}/oauth/__tests__/xai-oauth.test.ts +4 -7
  217. package/src/{utils → registry}/oauth/anthropic.ts +38 -17
  218. package/src/{utils → registry}/oauth/github-copilot.ts +79 -115
  219. package/src/registry/oauth/gitlab-duo.ts +198 -0
  220. package/src/{utils → registry}/oauth/google-antigravity.ts +1 -4
  221. package/src/{utils → registry}/oauth/google-gemini-cli.ts +1 -4
  222. package/src/registry/oauth/index.ts +164 -0
  223. package/src/{utils → registry}/oauth/minimax-code.ts +16 -14
  224. package/src/{utils → registry}/oauth/types.ts +7 -51
  225. package/src/{utils → registry}/oauth/wafer.ts +1 -1
  226. package/src/{utils → registry}/oauth/xai-oauth.ts +16 -8
  227. package/src/{utils → registry}/oauth/xiaomi.ts +9 -4
  228. package/src/{utils/oauth → registry}/ollama-cloud.ts +8 -1
  229. package/src/{utils/oauth → registry}/ollama.ts +8 -13
  230. package/src/registry/openai-codex-device.ts +18 -0
  231. package/src/registry/openai-codex.ts +19 -0
  232. package/src/registry/openai.ts +6 -0
  233. package/src/registry/opencode-go.ts +12 -0
  234. package/src/registry/opencode-zen.ts +12 -0
  235. package/src/{utils/oauth → registry}/openrouter.ts +10 -2
  236. package/src/{utils/oauth → registry}/parallel.ts +9 -11
  237. package/src/registry/perplexity.ts +13 -0
  238. package/src/{utils/oauth → registry}/qianfan.ts +8 -17
  239. package/src/{utils/oauth → registry}/qwen-portal.ts +8 -19
  240. package/src/registry/registry.ts +149 -0
  241. package/src/{utils/oauth → registry}/synthetic.ts +7 -1
  242. package/src/{utils/oauth → registry}/tavily.ts +10 -12
  243. package/src/{utils/oauth → registry}/together.ts +7 -1
  244. package/src/registry/types.ts +56 -0
  245. package/src/{utils/oauth → registry}/venice.ts +8 -12
  246. package/src/{utils/oauth → registry}/vercel-ai-gateway.ts +8 -18
  247. package/src/{utils/oauth → registry}/vllm.ts +9 -16
  248. package/src/registry/wafer-pass.ts +12 -0
  249. package/src/registry/wafer-serverless.ts +12 -0
  250. package/src/registry/xai-oauth.ts +17 -0
  251. package/src/registry/xai.ts +6 -0
  252. package/src/registry/xiaomi-token-plan-ams.ts +12 -0
  253. package/src/registry/xiaomi-token-plan-cn.ts +12 -0
  254. package/src/registry/xiaomi-token-plan-sgp.ts +12 -0
  255. package/src/registry/xiaomi.ts +12 -0
  256. package/src/{utils/oauth → registry}/zai.ts +10 -22
  257. package/src/{utils/oauth → registry}/zenmux.ts +8 -1
  258. package/src/{utils/oauth/zhipu.ts → registry/zhipu-coding-plan.ts} +9 -21
  259. package/src/stream.ts +229 -199
  260. package/src/types.ts +63 -384
  261. package/src/usage/claude.ts +4 -2
  262. package/src/usage/github-copilot.ts +4 -2
  263. package/src/usage/google-antigravity.ts +196 -28
  264. package/src/usage/kimi.ts +1 -1
  265. package/src/usage/minimax-code.ts +5 -6
  266. package/src/usage/openai-codex-reset.ts +174 -0
  267. package/src/usage/openai-codex.ts +19 -2
  268. package/src/usage/zai.ts +2 -1
  269. package/src/usage.ts +93 -4
  270. package/src/utils/abort.ts +14 -0
  271. package/src/utils/event-stream.ts +17 -0
  272. package/src/utils/http-inspector.ts +4 -12
  273. package/src/utils/idle-iterator.ts +250 -79
  274. package/src/utils/openai-http.ts +157 -0
  275. package/src/utils/request-debug.ts +67 -19
  276. package/src/utils/retry-after.ts +1 -1
  277. package/src/utils/retry.ts +23 -2
  278. package/src/utils/schema/CONSTRAINTS.md +4 -2
  279. package/src/utils/schema/fields.ts +16 -0
  280. package/src/utils/schema/json-schema-validator.ts +19 -1
  281. package/src/utils/schema/normalize.ts +80 -8
  282. package/src/utils/schema/stamps.ts +22 -10
  283. package/src/utils/schema/wire.ts +2 -2
  284. package/src/utils/sse-debug.ts +0 -271
  285. package/src/utils/stream-markup-healing.ts +50 -8
  286. package/src/utils/validation.ts +49 -13
  287. package/src/utils.ts +2 -26
  288. package/dist/types/model-cache.d.ts +0 -17
  289. package/dist/types/model-manager.d.ts +0 -64
  290. package/dist/types/model-thinking.d.ts +0 -100
  291. package/dist/types/models.d.ts +0 -12
  292. package/dist/types/provider-models/bundled-references.d.ts +0 -4
  293. package/dist/types/provider-models/descriptors.d.ts +0 -50
  294. package/dist/types/provider-models/google.d.ts +0 -24
  295. package/dist/types/provider-models/index.d.ts +0 -5
  296. package/dist/types/provider-models/ollama.d.ts +0 -7
  297. package/dist/types/provider-models/openai-compat.d.ts +0 -323
  298. package/dist/types/provider-models/special.d.ts +0 -16
  299. package/dist/types/utils/discovery/antigravity.d.ts +0 -61
  300. package/dist/types/utils/discovery/codex.d.ts +0 -38
  301. package/dist/types/utils/discovery/cursor.d.ts +0 -23
  302. package/dist/types/utils/discovery/gemini.d.ts +0 -25
  303. package/dist/types/utils/discovery/index.d.ts +0 -4
  304. package/dist/types/utils/discovery/openai-compatible.d.ts +0 -72
  305. package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +0 -18
  306. package/dist/types/utils/oauth/cerebras.d.ts +0 -1
  307. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +0 -18
  308. package/dist/types/utils/oauth/deepseek.d.ts +0 -10
  309. package/dist/types/utils/oauth/firepass.d.ts +0 -1
  310. package/dist/types/utils/oauth/fireworks.d.ts +0 -1
  311. package/dist/types/utils/oauth/huggingface.d.ts +0 -19
  312. package/dist/types/utils/oauth/kagi.d.ts +0 -17
  313. package/dist/types/utils/oauth/kilo.d.ts +0 -5
  314. package/dist/types/utils/oauth/litellm.d.ts +0 -18
  315. package/dist/types/utils/oauth/lm-studio.d.ts +0 -17
  316. package/dist/types/utils/oauth/moonshot.d.ts +0 -1
  317. package/dist/types/utils/oauth/nanogpt.d.ts +0 -1
  318. package/dist/types/utils/oauth/nvidia.d.ts +0 -18
  319. package/dist/types/utils/oauth/ollama-cloud.d.ts +0 -2
  320. package/dist/types/utils/oauth/ollama.d.ts +0 -18
  321. package/dist/types/utils/oauth/openrouter.d.ts +0 -1
  322. package/dist/types/utils/oauth/parallel.d.ts +0 -17
  323. package/dist/types/utils/oauth/qianfan.d.ts +0 -17
  324. package/dist/types/utils/oauth/qwen-portal.d.ts +0 -19
  325. package/dist/types/utils/oauth/synthetic.d.ts +0 -1
  326. package/dist/types/utils/oauth/tavily.d.ts +0 -17
  327. package/dist/types/utils/oauth/together.d.ts +0 -1
  328. package/dist/types/utils/oauth/venice.d.ts +0 -18
  329. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +0 -18
  330. package/dist/types/utils/oauth/vllm.d.ts +0 -16
  331. package/dist/types/utils/oauth/zai.d.ts +0 -18
  332. package/dist/types/utils/oauth/zenmux.d.ts +0 -1
  333. package/dist/types/utils/oauth/zhipu.d.ts +0 -18
  334. package/src/model-cache.ts +0 -129
  335. package/src/model-manager.ts +0 -469
  336. package/src/model-thinking.ts +0 -756
  337. package/src/models.json +0 -60287
  338. package/src/models.json.d.ts +0 -9
  339. package/src/models.ts +0 -56
  340. package/src/provider-models/bundled-references.ts +0 -38
  341. package/src/provider-models/descriptors.ts +0 -364
  342. package/src/provider-models/google.ts +0 -88
  343. package/src/provider-models/index.ts +0 -5
  344. package/src/provider-models/ollama.ts +0 -153
  345. package/src/provider-models/openai-compat.ts +0 -2904
  346. package/src/provider-models/special.ts +0 -67
  347. package/src/utils/discovery/antigravity.ts +0 -261
  348. package/src/utils/discovery/codex.ts +0 -371
  349. package/src/utils/discovery/cursor.ts +0 -306
  350. package/src/utils/discovery/gemini.ts +0 -248
  351. package/src/utils/discovery/index.ts +0 -4
  352. package/src/utils/discovery/openai-compatible.ts +0 -224
  353. package/src/utils/oauth/gitlab-duo.ts +0 -123
  354. package/src/utils/oauth/index.ts +0 -502
  355. /package/dist/types/{utils/oauth/__tests__/xai-oauth.test.d.ts → providers/__tests__/google-auth.test.d.ts} +0 -0
  356. /package/dist/types/{utils → registry}/oauth/callback-server.d.ts +0 -0
  357. /package/dist/types/{utils → registry}/oauth/cursor.d.ts +0 -0
  358. /package/dist/types/{utils → registry}/oauth/gitlab-duo.d.ts +0 -0
  359. /package/dist/types/{utils → registry}/oauth/google-antigravity.d.ts +0 -0
  360. /package/dist/types/{utils → registry}/oauth/google-gemini-cli.d.ts +0 -0
  361. /package/dist/types/{utils → registry}/oauth/google-oauth-shared.d.ts +0 -0
  362. /package/dist/types/{utils → registry}/oauth/kimi.d.ts +0 -0
  363. /package/dist/types/{utils → registry}/oauth/openai-codex.d.ts +0 -0
  364. /package/dist/types/{utils → registry}/oauth/opencode.d.ts +0 -0
  365. /package/dist/types/{utils → registry}/oauth/perplexity.d.ts +0 -0
  366. /package/dist/types/{utils → registry}/oauth/pkce.d.ts +0 -0
  367. /package/dist/types/{utils → registry}/oauth/wafer.d.ts +0 -0
  368. /package/dist/types/{utils → registry}/oauth/xiaomi.d.ts +0 -0
  369. /package/src/{utils → registry}/oauth/callback-server.ts +0 -0
  370. /package/src/{utils → registry}/oauth/cursor.ts +0 -0
  371. /package/src/{utils → registry}/oauth/google-oauth-shared.ts +0 -0
  372. /package/src/{utils → registry}/oauth/kimi.ts +0 -0
  373. /package/src/{utils → registry}/oauth/oauth.html +0 -0
  374. /package/src/{utils → registry}/oauth/openai-codex.ts +0 -0
  375. /package/src/{utils → registry}/oauth/opencode.ts +0 -0
  376. /package/src/{utils → registry}/oauth/perplexity.ts +0 -0
  377. /package/src/{utils → registry}/oauth/pkce.ts +0 -0
@@ -2,19 +2,32 @@
2
2
  * GitHub Copilot OAuth flow (opencode OAuth app)
3
3
  */
4
4
  import { scheduler } from "node:timers/promises";
5
- import { getBundledModels } from "../../models";
5
+ import { getBundledModels } from "@prometheus-ai/catalog/models";
6
+ import {
7
+ COPILOT_API_HEADERS,
8
+ getGitHubCopilotBaseUrl,
9
+ isPublicGitHubHost,
10
+ normalizeDomain,
11
+ normalizeGitHubCopilotEnterpriseDomain,
12
+ OPENCODE_HEADERS,
13
+ } from "@prometheus-ai/catalog/wire/github-copilot";
14
+ import type { FetchImpl } from "../../types";
6
15
  import type { OAuthCredentials } from "./types";
7
16
 
8
17
  const CLIENT_ID = "Ov23li8tweQw6odWQebz";
9
18
 
10
- export const COPILOT_USER_AGENT = "opencode/1.3.15" as const;
11
-
12
- export const OPENCODE_HEADERS = {
13
- "User-Agent": COPILOT_USER_AGENT,
14
- } as const;
15
-
16
19
  const INITIAL_POLL_INTERVAL_MULTIPLIER = 1.2;
17
20
  const SLOW_DOWN_POLL_INTERVAL_MULTIPLIER = 1.4;
21
+
22
+ type GitHubCopilotLoginOptions = {
23
+ onAuth: (url: string, instructions?: string) => void;
24
+ onPrompt: (prompt: { message: string; placeholder?: string; allowEmpty?: boolean }) => Promise<string>;
25
+ onProgress?: (message: string) => void;
26
+ signal?: AbortSignal;
27
+ pollIntervalFloorMs?: number;
28
+ pollIntervalScaleMs?: number;
29
+ fetch?: FetchImpl;
30
+ };
18
31
  type DeviceCodeResponse = {
19
32
  device_code: string;
20
33
  user_code: string;
@@ -35,58 +48,6 @@ type DeviceTokenErrorResponse = {
35
48
  interval?: number;
36
49
  };
37
50
 
38
- type GitHubCopilotApiKeyPayload = {
39
- token?: unknown;
40
- enterpriseUrl?: unknown;
41
- };
42
-
43
- export type ParsedGitHubCopilotApiKey = {
44
- accessToken: string;
45
- enterpriseUrl?: string;
46
- };
47
-
48
- const PUBLIC_GITHUB_HOSTS = new Set(["api.github.com", "github.com", "www.github.com"]);
49
-
50
- function isPublicGitHubHost(host: string): boolean {
51
- return PUBLIC_GITHUB_HOSTS.has(host.trim().toLowerCase());
52
- }
53
-
54
- export function normalizeGitHubCopilotEnterpriseDomain(input: string | undefined): string | undefined {
55
- const trimmed = input?.trim();
56
- if (!trimmed) return undefined;
57
- const normalized = normalizeDomain(trimmed) ?? trimmed.toLowerCase();
58
- if (!normalized || isPublicGitHubHost(normalized)) return undefined;
59
- return normalized;
60
- }
61
-
62
- export function parseGitHubCopilotApiKey(apiKeyRaw: string): ParsedGitHubCopilotApiKey {
63
- try {
64
- const parsed = JSON.parse(apiKeyRaw) as GitHubCopilotApiKeyPayload;
65
- if (typeof parsed.token === "string") {
66
- return {
67
- accessToken: parsed.token,
68
- enterpriseUrl:
69
- typeof parsed.enterpriseUrl === "string"
70
- ? normalizeGitHubCopilotEnterpriseDomain(parsed.enterpriseUrl)
71
- : undefined,
72
- };
73
- }
74
- } catch {}
75
-
76
- return { accessToken: apiKeyRaw };
77
- }
78
-
79
- export function normalizeDomain(input: string): string | null {
80
- const trimmed = input.trim();
81
- if (!trimmed) return null;
82
- try {
83
- const url = trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`);
84
- return url.hostname;
85
- } catch {
86
- return null;
87
- }
88
- }
89
-
90
51
  function getUrls(domain: string): {
91
52
  deviceCodeUrl: string;
92
53
  accessTokenUrl: string;
@@ -97,17 +58,8 @@ function getUrls(domain: string): {
97
58
  };
98
59
  }
99
60
 
100
- export function getGitHubCopilotBaseUrl(enterpriseDomain?: string): string {
101
- const normalizedEnterpriseDomain = normalizeGitHubCopilotEnterpriseDomain(enterpriseDomain);
102
- if (!normalizedEnterpriseDomain) return "https://api.githubcopilot.com";
103
- const host = normalizedEnterpriseDomain.startsWith("copilot-api.")
104
- ? normalizedEnterpriseDomain
105
- : `copilot-api.${normalizedEnterpriseDomain}`;
106
- return `https://${host}`;
107
- }
108
-
109
- async function fetchJson(url: string, init: RequestInit): Promise<unknown> {
110
- const response = await fetch(url, init);
61
+ async function fetchJson(url: string, init: RequestInit, fetchImpl: FetchImpl): Promise<unknown> {
62
+ const response = await fetchImpl(url, init);
111
63
  if (!response.ok) {
112
64
  const text = await response.text();
113
65
  throw new Error(`${response.status} ${response.statusText}: ${text}`);
@@ -115,20 +67,24 @@ async function fetchJson(url: string, init: RequestInit): Promise<unknown> {
115
67
  return response.json();
116
68
  }
117
69
 
118
- async function startDeviceFlow(domain: string): Promise<DeviceCodeResponse> {
70
+ async function startDeviceFlow(domain: string, fetchImpl: FetchImpl): Promise<DeviceCodeResponse> {
119
71
  const urls = getUrls(domain);
120
- const data = await fetchJson(urls.deviceCodeUrl, {
121
- method: "POST",
122
- headers: {
123
- Accept: "application/json",
124
- "Content-Type": "application/json",
125
- ...OPENCODE_HEADERS,
72
+ const data = await fetchJson(
73
+ urls.deviceCodeUrl,
74
+ {
75
+ method: "POST",
76
+ headers: {
77
+ Accept: "application/json",
78
+ "Content-Type": "application/json",
79
+ ...OPENCODE_HEADERS,
80
+ },
81
+ body: JSON.stringify({
82
+ client_id: CLIENT_ID,
83
+ scope: "read:user",
84
+ }),
126
85
  },
127
- body: JSON.stringify({
128
- client_id: CLIENT_ID,
129
- scope: "read:user",
130
- }),
131
- });
86
+ fetchImpl,
87
+ );
132
88
 
133
89
  if (!data || typeof data !== "object") {
134
90
  throw new Error("Invalid device code response");
@@ -164,7 +120,8 @@ async function pollForGitHubAccessToken(
164
120
  deviceCode: string,
165
121
  intervalSeconds: number,
166
122
  expiresIn: number,
167
- signal?: AbortSignal,
123
+ signal: AbortSignal | undefined,
124
+ fetchImpl: FetchImpl,
168
125
  pollIntervalFloorMs = 1000,
169
126
  pollIntervalScaleMs = 1000,
170
127
  ) {
@@ -187,19 +144,23 @@ async function pollForGitHubAccessToken(
187
144
  throw new Error("Login cancelled");
188
145
  }
189
146
 
190
- const raw = await fetchJson(urls.accessTokenUrl, {
191
- method: "POST",
192
- headers: {
193
- Accept: "application/json",
194
- "Content-Type": "application/json",
195
- ...OPENCODE_HEADERS,
147
+ const raw = await fetchJson(
148
+ urls.accessTokenUrl,
149
+ {
150
+ method: "POST",
151
+ headers: {
152
+ Accept: "application/json",
153
+ "Content-Type": "application/json",
154
+ ...OPENCODE_HEADERS,
155
+ },
156
+ body: JSON.stringify({
157
+ client_id: CLIENT_ID,
158
+ device_code: deviceCode,
159
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
160
+ }),
196
161
  },
197
- body: JSON.stringify({
198
- client_id: CLIENT_ID,
199
- device_code: deviceCode,
200
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
201
- }),
202
- });
162
+ fetchImpl,
163
+ );
203
164
 
204
165
  if (raw && typeof raw === "object" && typeof (raw as DeviceTokenSuccessResponse).access_token === "string") {
205
166
  return (raw as DeviceTokenSuccessResponse).access_token;
@@ -255,17 +216,22 @@ export function refreshGitHubCopilotToken(refreshToken: string, enterpriseDomain
255
216
  * Enable a model for the user's GitHub Copilot account.
256
217
  * This is required for some models (like Claude, Grok) before they can be used.
257
218
  */
258
- async function enableGitHubCopilotModel(token: string, modelId: string, enterpriseDomain?: string): Promise<boolean> {
219
+ async function enableGitHubCopilotModel(
220
+ token: string,
221
+ modelId: string,
222
+ fetchImpl: FetchImpl,
223
+ enterpriseDomain?: string,
224
+ ): Promise<boolean> {
259
225
  const baseUrl = getGitHubCopilotBaseUrl(enterpriseDomain);
260
226
  const url = `${baseUrl}/models/${modelId}/policy`;
261
227
 
262
228
  try {
263
- const response = await fetch(url, {
229
+ const response = await fetchImpl(url, {
264
230
  method: "POST",
265
231
  headers: {
266
232
  "Content-Type": "application/json",
267
233
  Authorization: `Bearer ${token}`,
268
- ...OPENCODE_HEADERS,
234
+ ...COPILOT_API_HEADERS,
269
235
  "openai-intent": "chat-policy",
270
236
  "x-interaction-type": "chat-policy",
271
237
  },
@@ -283,17 +249,20 @@ async function enableGitHubCopilotModel(token: string, modelId: string, enterpri
283
249
  */
284
250
  async function enableAllGitHubCopilotModels(
285
251
  token: string,
286
- enterpriseDomain?: string,
252
+ enterpriseDomain: string | undefined,
253
+ fetchImpl: FetchImpl,
287
254
  onProgress?: (model: string, success: boolean) => void,
288
255
  ): Promise<void> {
289
- const models = getBundledModels("github-copilot");
256
+ // Synthesized catalog variants (Copilot long-context `-1m` entries) share
257
+ // the upstream model id; enable each wire id exactly once.
258
+ const wireModelIds = [...new Set(getBundledModels("github-copilot").map(model => model.requestModelId ?? model.id))];
290
259
  const BATCH_SIZE = 5;
291
- for (let i = 0; i < models.length; i += BATCH_SIZE) {
292
- const batch = models.slice(i, i + BATCH_SIZE);
260
+ for (let i = 0; i < wireModelIds.length; i += BATCH_SIZE) {
261
+ const batch = wireModelIds.slice(i, i + BATCH_SIZE);
293
262
  await Promise.all(
294
- batch.map(async model => {
295
- const success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain);
296
- onProgress?.(model.id, success);
263
+ batch.map(async modelId => {
264
+ const success = await enableGitHubCopilotModel(token, modelId, fetchImpl, enterpriseDomain);
265
+ onProgress?.(modelId, success);
297
266
  }),
298
267
  );
299
268
  }
@@ -307,14 +276,8 @@ async function enableAllGitHubCopilotModels(
307
276
  * @param options.onProgress - Optional progress callback
308
277
  * @param options.signal - Optional AbortSignal for cancellation
309
278
  */
310
- export async function loginGitHubCopilot(options: {
311
- onAuth: (url: string, instructions?: string) => void;
312
- onPrompt: (prompt: { message: string; placeholder?: string; allowEmpty?: boolean }) => Promise<string>;
313
- onProgress?: (message: string) => void;
314
- signal?: AbortSignal;
315
- pollIntervalFloorMs?: number;
316
- pollIntervalScaleMs?: number;
317
- }): Promise<OAuthCredentials> {
279
+ export async function loginGitHubCopilot(options: GitHubCopilotLoginOptions): Promise<OAuthCredentials> {
280
+ const fetchImpl = options.fetch ?? fetch;
318
281
  const input = await options.onPrompt({
319
282
  message: "GitHub Enterprise URL/domain (blank for github.com)",
320
283
  placeholder: "company.ghe.com",
@@ -334,7 +297,7 @@ export async function loginGitHubCopilot(options: {
334
297
  const domain =
335
298
  normalizedDomain && isPublicGitHubHost(normalizedDomain) ? "github.com" : (normalizedDomain ?? "github.com");
336
299
 
337
- const device = await startDeviceFlow(domain);
300
+ const device = await startDeviceFlow(domain, fetchImpl);
338
301
  options.onAuth(device.verification_uri, `Enter code: ${device.user_code}`);
339
302
 
340
303
  const githubAccessToken = await pollForGitHubAccessToken(
@@ -343,6 +306,7 @@ export async function loginGitHubCopilot(options: {
343
306
  device.interval,
344
307
  device.expires_in,
345
308
  options.signal,
309
+ fetchImpl,
346
310
  options.pollIntervalFloorMs,
347
311
  options.pollIntervalScaleMs,
348
312
  );
@@ -357,6 +321,6 @@ export async function loginGitHubCopilot(options: {
357
321
 
358
322
  // Enable all models after successful login
359
323
  options.onProgress?.("Enabling models...");
360
- await enableAllGitHubCopilotModels(githubAccessToken, enterpriseDomain ?? undefined);
324
+ await enableAllGitHubCopilotModels(githubAccessToken, enterpriseDomain ?? undefined, fetchImpl);
361
325
  return credentials;
362
326
  }
@@ -0,0 +1,198 @@
1
+ import { clearGitLabDuoDirectAccessCache } from "../../providers/gitlab-duo";
2
+ import type { FetchImpl } from "../../types";
3
+ import { OAuthCallbackFlow, type OAuthCallbackFlowOptions } from "./callback-server";
4
+ import { generatePKCE } from "./pkce";
5
+ import type { OAuthCredentials, OAuthLoginCallbacks } from "./types";
6
+
7
+ const GITLAB_COM_URL = "https://gitlab.com";
8
+ /**
9
+ * Default OAuth client id baked into the bundled GitLab Duo login flow. GitLab
10
+ * authorize requests are rejected outright (`The redirect URI included is not
11
+ * valid`) whenever this client id's registered redirect URI list drifts from
12
+ * `http://localhost:8080/callback`. Users hitting that case can either:
13
+ *
14
+ * - register their own GitLab OAuth application and override the bundled
15
+ * credentials with `GITLAB_CLIENT_ID` + `GITLAB_REDIRECT_URI`, or
16
+ * - skip OAuth entirely and supply a Personal Access Token via `GITLAB_TOKEN`.
17
+ *
18
+ * @see https://github.com/uttamtrivedi/prometheus/issues/2424
19
+ */
20
+ const DEFAULT_CLIENT_ID = "da4edff2e6ebd2bc3208611e2768bc1c1dd7be791dc5ff26ca34ca9ee44f7d4b";
21
+ const OAUTH_SCOPES = ["api"];
22
+ const DEFAULT_CALLBACK_PORT = 8080;
23
+ const DEFAULT_CALLBACK_PATH = "/callback";
24
+ const DEFAULT_CALLBACK_HOSTNAME = "localhost";
25
+
26
+ interface PKCEPair {
27
+ verifier: string;
28
+ challenge: string;
29
+ }
30
+
31
+ /**
32
+ * Resolve the OAuth client id, preferring `GITLAB_CLIENT_ID` when set so users
33
+ * with their own GitLab OAuth application can bypass the bundled credentials.
34
+ */
35
+ function resolveClientId(): string {
36
+ const env = process.env.GITLAB_CLIENT_ID?.trim();
37
+ return env && env.length > 0 ? env : DEFAULT_CLIENT_ID;
38
+ }
39
+
40
+ /**
41
+ * Resolve callback-server options from `GITLAB_REDIRECT_URI`. When set, the
42
+ * exact string is advertised to GitLab (strict matching), random-port fallback
43
+ * is disabled, and HTTP loopback URIs bind the listener to the URI's host/port
44
+ * so the browser callback lands on us. HTTPS loopback URIs are rejected because
45
+ * the local callback server is plaintext HTTP. Non-loopback URIs bind a random
46
+ * local port — only the paste-code path can complete in that case.
47
+ */
48
+ function resolveCallbackOptions(): OAuthCallbackFlowOptions {
49
+ const raw = process.env.GITLAB_REDIRECT_URI?.trim();
50
+ if (!raw) {
51
+ return {
52
+ preferredPort: DEFAULT_CALLBACK_PORT,
53
+ callbackPath: DEFAULT_CALLBACK_PATH,
54
+ callbackHostname: DEFAULT_CALLBACK_HOSTNAME,
55
+ };
56
+ }
57
+
58
+ let parsed: URL;
59
+ try {
60
+ parsed = new URL(raw);
61
+ } catch {
62
+ throw new Error(`Invalid GITLAB_REDIRECT_URI: ${raw}`);
63
+ }
64
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
65
+ throw new Error(`GITLAB_REDIRECT_URI must use http:// or https://, got: ${raw}`);
66
+ }
67
+
68
+ const isLoopback = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]";
69
+ if (isLoopback && parsed.protocol !== "http:") {
70
+ throw new Error(`GITLAB_REDIRECT_URI loopback callbacks must use http://, got: ${raw}`);
71
+ }
72
+
73
+ const port = parsed.port ? Number.parseInt(parsed.port, 10) : parsed.protocol === "https:" ? 443 : 80;
74
+
75
+ return {
76
+ preferredPort: isLoopback ? port : 0,
77
+ callbackPath: parsed.pathname || DEFAULT_CALLBACK_PATH,
78
+ callbackHostname: isLoopback ? parsed.hostname : DEFAULT_CALLBACK_HOSTNAME,
79
+ redirectUri: raw,
80
+ };
81
+ }
82
+
83
+ function mapTokenResponse(payload: {
84
+ access_token?: string;
85
+ refresh_token?: string;
86
+ expires_in?: number;
87
+ created_at?: number;
88
+ }): OAuthCredentials {
89
+ if (!payload.access_token || !payload.refresh_token || typeof payload.expires_in !== "number") {
90
+ throw new Error("GitLab OAuth token response missing required fields");
91
+ }
92
+
93
+ const createdAtMs =
94
+ typeof payload.created_at === "number" && Number.isFinite(payload.created_at)
95
+ ? payload.created_at * 1000
96
+ : Date.now();
97
+
98
+ return {
99
+ access: payload.access_token,
100
+ refresh: payload.refresh_token,
101
+ expires: createdAtMs + payload.expires_in * 1000 - 5 * 60 * 1000,
102
+ };
103
+ }
104
+
105
+ class GitLabDuoOAuthFlow extends OAuthCallbackFlow {
106
+ #pkce: PKCEPair;
107
+ #clientId: string;
108
+ #fetch: FetchImpl;
109
+
110
+ constructor(ctrl: OAuthLoginCallbacks, pkce: PKCEPair, clientId: string, options: OAuthCallbackFlowOptions) {
111
+ super(ctrl, options);
112
+ this.#pkce = pkce;
113
+ this.#clientId = clientId;
114
+ this.#fetch = ctrl.fetch ?? fetch;
115
+ }
116
+
117
+ override async generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }> {
118
+ const authParams = new URLSearchParams({
119
+ client_id: this.#clientId,
120
+ redirect_uri: redirectUri,
121
+ response_type: "code",
122
+ scope: OAUTH_SCOPES.join(" "),
123
+ code_challenge: this.#pkce.challenge,
124
+ code_challenge_method: "S256",
125
+ state,
126
+ });
127
+
128
+ return {
129
+ url: `${GITLAB_COM_URL}/oauth/authorize?${authParams.toString()}`,
130
+ instructions:
131
+ 'Complete GitLab login in browser. If GitLab responds with "The redirect URI included is not valid", ' +
132
+ "register your own GitLab OAuth application and set GITLAB_CLIENT_ID + GITLAB_REDIRECT_URI, or use a " +
133
+ "Personal Access Token via GITLAB_TOKEN.",
134
+ };
135
+ }
136
+
137
+ override async exchangeToken(code: string, _state: string, redirectUri: string): Promise<OAuthCredentials> {
138
+ const response = await this.#fetch(`${GITLAB_COM_URL}/oauth/token`, {
139
+ method: "POST",
140
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
141
+ body: new URLSearchParams({
142
+ client_id: this.#clientId,
143
+ grant_type: "authorization_code",
144
+ code,
145
+ code_verifier: this.#pkce.verifier,
146
+ redirect_uri: redirectUri,
147
+ }).toString(),
148
+ });
149
+
150
+ if (!response.ok) {
151
+ throw new Error(`GitLab OAuth token exchange failed: ${response.status} ${await response.text()}`);
152
+ }
153
+
154
+ clearGitLabDuoDirectAccessCache();
155
+ return mapTokenResponse(
156
+ (await response.json()) as {
157
+ access_token?: string;
158
+ refresh_token?: string;
159
+ expires_in?: number;
160
+ created_at?: number;
161
+ },
162
+ );
163
+ }
164
+ }
165
+
166
+ export async function loginGitLabDuo(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
167
+ const pkce = await generatePKCE();
168
+ const clientId = resolveClientId();
169
+ const options = resolveCallbackOptions();
170
+ const flow = new GitLabDuoOAuthFlow(callbacks, pkce, clientId, options);
171
+ return flow.login();
172
+ }
173
+
174
+ export async function refreshGitLabDuoToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {
175
+ const response = await fetch(`${GITLAB_COM_URL}/oauth/token`, {
176
+ method: "POST",
177
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
178
+ body: new URLSearchParams({
179
+ client_id: resolveClientId(),
180
+ grant_type: "refresh_token",
181
+ refresh_token: credentials.refresh,
182
+ }).toString(),
183
+ });
184
+
185
+ if (!response.ok) {
186
+ throw new Error(`GitLab OAuth refresh failed: ${response.status} ${await response.text()}`);
187
+ }
188
+
189
+ clearGitLabDuoDirectAccessCache();
190
+ return mapTokenResponse(
191
+ (await response.json()) as {
192
+ access_token?: string;
193
+ refresh_token?: string;
194
+ expires_in?: number;
195
+ created_at?: number;
196
+ },
197
+ );
198
+ }
@@ -2,14 +2,11 @@
2
2
  * Antigravity OAuth flow (Gemini 3, Claude, GPT-OSS via Google Cloud)
3
3
  * Uses different OAuth credentials than google-gemini-cli for access to additional models.
4
4
  */
5
- import { getAntigravityUserAgent } from "../../providers/google-gemini-headers";
5
+ import { getAntigravityUserAgent } from "@prometheus-ai/catalog/wire/gemini-headers";
6
6
  import { runGoogleOAuthLogin } from "./google-oauth-shared";
7
7
  import type { OAuthController, OAuthCredentials } from "./types";
8
8
 
9
9
  const decode = (s: string) => atob(s);
10
- // Native-app OAuth credentials identify the public Google/Antigravity client.
11
- // They are not user OAuth tokens, and installed apps cannot keep client
12
- // credentials confidential. See docs/secrets.md for the release hygiene note.
13
10
  const CLIENT_ID = decode(
14
11
  "MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==",
15
12
  );
@@ -3,15 +3,12 @@
3
3
  * Standard Gemini models only (gemini-2.0-flash, gemini-2.5-*)
4
4
  */
5
5
 
6
+ import { getGeminiCliHeaders } from "@prometheus-ai/catalog/wire/gemini-headers";
6
7
  import { $env } from "@prometheus-ai/utils";
7
- import { getGeminiCliHeaders } from "../../providers/google-gemini-headers";
8
8
  import { runGoogleOAuthLogin } from "./google-oauth-shared";
9
9
  import type { OAuthController, OAuthCredentials } from "./types";
10
10
 
11
11
  const decode = (s: string) => atob(s);
12
- // Native-app OAuth credentials identify the public Google/Gemini CLI client.
13
- // They are not user OAuth tokens, and installed apps cannot keep client
14
- // credentials confidential. See docs/secrets.md for the release hygiene note.
15
12
  const CLIENT_ID = decode(
16
13
  "NjgxMjU1ODA5Mzk1LW9vOGZ0Mm9wcmRybnA5ZTNhcWY2YXYzaG1kaWIxMzVqLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29t",
17
14
  );
@@ -0,0 +1,164 @@
1
+ // ============================================================================
2
+ // High-level API
3
+ // ============================================================================
4
+
5
+ import { getProviderDefinition, PROVIDER_REGISTRY } from "../registry";
6
+ import type {
7
+ OAuthCredentials,
8
+ OAuthProvider,
9
+ OAuthProviderId,
10
+ OAuthProviderInfo,
11
+ OAuthProviderInterface,
12
+ } from "./types";
13
+
14
+ export type * from "./types";
15
+
16
+ const builtInOAuthProviders: OAuthProviderInfo[] = PROVIDER_REGISTRY.filter(
17
+ provider => provider.login && provider.showInLoginList !== false,
18
+ ).map(provider => ({
19
+ id: provider.id,
20
+ name: provider.name,
21
+ available: provider.available ?? true,
22
+ }));
23
+
24
+ const customOAuthProviders = new Map<string, OAuthProviderInterface>();
25
+
26
+ /**
27
+ * Register a custom OAuth provider.
28
+ */
29
+ export function registerOAuthProvider(provider: OAuthProviderInterface): void {
30
+ customOAuthProviders.set(provider.id, provider);
31
+ }
32
+
33
+ /**
34
+ * Get a custom OAuth provider by ID.
35
+ */
36
+ export function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {
37
+ return customOAuthProviders.get(id);
38
+ }
39
+
40
+ /**
41
+ * Remove all custom OAuth providers registered by a source.
42
+ */
43
+ export function unregisterOAuthProviders(sourceId: string): void {
44
+ for (const [id, provider] of customOAuthProviders.entries()) {
45
+ if (provider.sourceId === sourceId) {
46
+ customOAuthProviders.delete(id);
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Refresh token for any OAuth provider.
53
+ * Saves the new credentials and returns the new access token.
54
+ */
55
+ export async function refreshOAuthToken(
56
+ provider: OAuthProvider,
57
+ credentials: OAuthCredentials,
58
+ ): Promise<OAuthCredentials> {
59
+ if (!credentials) {
60
+ throw new Error(`No OAuth credentials found for ${provider}`);
61
+ }
62
+ const def = getProviderDefinition(provider);
63
+ if (!def?.login) {
64
+ throw new Error(`Unknown OAuth provider: ${provider}`);
65
+ }
66
+ // Providers without a real refresher (static bearer tokens / API keys that
67
+ // don't expire) return the credentials unchanged.
68
+ return def.refreshToken ? def.refreshToken(credentials) : credentials;
69
+ }
70
+ function getPerplexityJwtExpiryMs(token: string): number | undefined {
71
+ const parts = token.split(".");
72
+ if (parts.length !== 3) return undefined;
73
+ const payload = parts[1];
74
+ if (!payload) return undefined;
75
+ try {
76
+ const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf8")) as { exp?: unknown };
77
+ if (typeof decoded.exp !== "number" || !Number.isFinite(decoded.exp)) return undefined;
78
+ return decoded.exp * 1000 - 5 * 60_000;
79
+ } catch {
80
+ return undefined;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Build API-key bytes for a provider from an already-fresh OAuth credential.
86
+ *
87
+ * Refresh is owned by AuthStorage. This helper deliberately refuses expired
88
+ * credentials so it cannot POST broker redaction sentinels to upstream token
89
+ * endpoints as a side channel.
90
+ *
91
+ * For providers that need credential metadata at request time, returns
92
+ * JSON-encoded credentials plus expiry metadata for diagnostics/edge guards.
93
+ * @returns API key string, or null if no credentials
94
+ * @throws Error if the credential is expired and must be refreshed upstream
95
+ */
96
+ export async function getOAuthApiKey(
97
+ provider: OAuthProvider,
98
+ credentials: Record<string, OAuthCredentials>,
99
+ ): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null> {
100
+ let creds = credentials[provider];
101
+ if (!creds) {
102
+ return null;
103
+ }
104
+
105
+ if (provider === "perplexity") {
106
+ // Perplexity JWTs usually omit `exp` (server-side sessions). Trust the JWT
107
+ // claim when present; otherwise treat the credential as non-expiring rather
108
+ // than honoring a stale stored `expires` (older logins wrote loginTime+1h).
109
+ const NEVER_EXPIRES = 8.64e15;
110
+ const normalizedExpires =
111
+ creds.expires > 0 && creds.expires < 10_000_000_000 ? creds.expires * 1000 : creds.expires;
112
+ const jwtExpiry = getPerplexityJwtExpiryMs(creds.access);
113
+ const expires = jwtExpiry ?? Math.max(normalizedExpires, NEVER_EXPIRES);
114
+ if (expires !== creds.expires) {
115
+ creds = { ...creds, expires };
116
+ }
117
+ }
118
+ // Refresh is the sole responsibility of `AuthStorage` (which calls
119
+ // `refreshOAuthToken` directly with broker-aware single-flighting). If we
120
+ // reach here with an expired credential, the outer pipeline failed to
121
+ // refresh before this call OR the refresh slot is the broker sentinel —
122
+ // either way, posting the credential to a provider endpoint would only
123
+ // trigger a `__remote__`-against-real-provider failure that gets classified
124
+ // as `invalid_grant` and disables the row. Refuse loudly instead.
125
+ if (Date.now() >= creds.expires) {
126
+ if (provider === "perplexity") {
127
+ const jwtExpiry = getPerplexityJwtExpiryMs(creds.access);
128
+ if (jwtExpiry && Date.now() < jwtExpiry) {
129
+ const fallbackCredentials = { ...creds, expires: jwtExpiry };
130
+ return { newCredentials: fallbackCredentials, apiKey: fallbackCredentials.access };
131
+ }
132
+ }
133
+ throw new Error(
134
+ `OAuth credential for ${provider} is expired and must be refreshed via AuthStorage before getOAuthApiKey is called`,
135
+ );
136
+ }
137
+ // For providers that need request-time credential metadata, return JSON.
138
+ const needsStructuredApiKey =
139
+ provider === "github-copilot" || provider === "google-gemini-cli" || provider === "google-antigravity";
140
+ const apiKey = needsStructuredApiKey
141
+ ? JSON.stringify({
142
+ token: creds.access,
143
+ enterpriseUrl: creds.enterpriseUrl,
144
+ projectId: creds.projectId,
145
+ refreshToken: creds.refresh,
146
+ expiresAt: creds.expires,
147
+ email: creds.email,
148
+ accountId: creds.accountId,
149
+ })
150
+ : creds.access;
151
+ return { newCredentials: creds, apiKey };
152
+ }
153
+
154
+ /**
155
+ * Get list of OAuth providers.
156
+ */
157
+ export function getOAuthProviders(): OAuthProviderInfo[] {
158
+ const customProviders = Array.from(customOAuthProviders.values(), provider => ({
159
+ id: provider.id,
160
+ name: provider.name,
161
+ available: true,
162
+ }));
163
+ return [...builtInOAuthProviders, ...customProviders];
164
+ }