@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,200 @@
1
+ /**
2
+ * Antigravity OAuth flow (Gemini 3, Anthropic model, GPT-OSS via Google Cloud)
3
+ * Uses different OAuth credentials than google-gemini-cli for access to additional models.
4
+ */
5
+ import { getAntigravityUserAgent } from "../../providers/google-gemini-headers";
6
+ import { runGoogleOAuthLogin } from "./google-oauth-shared";
7
+ import type { OAuthController, OAuthCredentials } from "./types";
8
+
9
+ const decode = (s: string) => atob(s);
10
+ const CLIENT_ID = decode(
11
+ "MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==",
12
+ );
13
+ const CLIENT_SECRET = decode("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=");
14
+ const CALLBACK_PORT = 51121;
15
+ const CALLBACK_PATH = "/oauth-callback";
16
+
17
+ const SCOPES = [
18
+ "https://www.googleapis.com/auth/cloud-platform",
19
+ "https://www.googleapis.com/auth/userinfo.email",
20
+ "https://www.googleapis.com/auth/userinfo.profile",
21
+ "https://www.googleapis.com/auth/cclog",
22
+ "https://www.googleapis.com/auth/experimentsandconfigs",
23
+ ];
24
+
25
+ const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
26
+ const TOKEN_URL = "https://oauth2.googleapis.com/token";
27
+ const CLOUD_CODE_ENDPOINT = "https://cloudcode-pa.googleapis.com";
28
+ const TIER_LEGACY = "legacy-tier";
29
+ const PROJECT_ONBOARD_MAX_ATTEMPTS = 5;
30
+ const PROJECT_ONBOARD_INTERVAL_MS = 2000;
31
+
32
+ interface LoadCodeAssistPayload {
33
+ cloudaicompanionProject?: string | { id?: string };
34
+ currentTier?: { id?: string };
35
+ allowedTiers?: Array<{ id?: string; isDefault?: boolean }>;
36
+ }
37
+
38
+ interface LongRunningOperationResponse {
39
+ done?: boolean;
40
+ response?: {
41
+ cloudaicompanionProject?: string | { id?: string };
42
+ };
43
+ }
44
+
45
+ export const ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA = Object.freeze({
46
+ ideType: "ANTIGRAVITY",
47
+ platform: "PLATFORM_UNSPECIFIED",
48
+ pluginType: "GEMINI",
49
+ });
50
+
51
+ function readProjectId(value: string | { id?: string } | undefined): string | undefined {
52
+ if (typeof value === "string" && value.length > 0) {
53
+ return value;
54
+ }
55
+ if (value && typeof value === "object" && typeof value.id === "string" && value.id.length > 0) {
56
+ return value.id;
57
+ }
58
+ return undefined;
59
+ }
60
+
61
+ function getDefaultTierId(allowedTiers?: Array<{ id?: string; isDefault?: boolean }>): string {
62
+ if (!allowedTiers || allowedTiers.length === 0) {
63
+ return TIER_LEGACY;
64
+ }
65
+ const defaultTier = allowedTiers.find(tier => tier.isDefault && typeof tier.id === "string" && tier.id.length > 0);
66
+ if (defaultTier?.id) {
67
+ return defaultTier.id;
68
+ }
69
+ return TIER_LEGACY;
70
+ }
71
+
72
+ async function onboardProjectWithRetries(
73
+ endpoint: string,
74
+ headers: Record<string, string>,
75
+ onboardBody: { tierId: string; metadata: typeof ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA },
76
+ onProgress?: (message: string) => void,
77
+ ): Promise<string> {
78
+ for (let attempt = 1; attempt <= PROJECT_ONBOARD_MAX_ATTEMPTS; attempt += 1) {
79
+ if (attempt > 1) {
80
+ onProgress?.(`Waiting for project provisioning (attempt ${attempt}/${PROJECT_ONBOARD_MAX_ATTEMPTS})...`);
81
+ await Bun.sleep(PROJECT_ONBOARD_INTERVAL_MS);
82
+ }
83
+
84
+ const onboardResponse = await fetch(`${endpoint}/v1internal:onboardUser`, {
85
+ method: "POST",
86
+ headers,
87
+ body: JSON.stringify(onboardBody),
88
+ });
89
+
90
+ if (!onboardResponse.ok) {
91
+ const errorText = await onboardResponse.text();
92
+ throw new Error(`onboardUser failed: ${onboardResponse.status} ${onboardResponse.statusText}: ${errorText}`);
93
+ }
94
+
95
+ const operation = (await onboardResponse.json()) as LongRunningOperationResponse;
96
+ if (!operation.done) {
97
+ continue;
98
+ }
99
+
100
+ const projectId = readProjectId(operation.response?.cloudaicompanionProject);
101
+ if (projectId) {
102
+ return projectId;
103
+ }
104
+ }
105
+
106
+ throw new Error(
107
+ `onboardUser did not return a provisioned project id after ${PROJECT_ONBOARD_MAX_ATTEMPTS} attempts`,
108
+ );
109
+ }
110
+
111
+ async function discoverProject(accessToken: string, onProgress?: (message: string) => void): Promise<string> {
112
+ const headers = {
113
+ Authorization: `Bearer ${accessToken}`,
114
+ "Content-Type": "application/json",
115
+ "User-Agent": getAntigravityUserAgent(),
116
+ };
117
+
118
+ onProgress?.("Checking for existing project...");
119
+ const endpoint = CLOUD_CODE_ENDPOINT;
120
+ try {
121
+ const loadResponse = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {
122
+ method: "POST",
123
+ headers,
124
+ body: JSON.stringify({
125
+ metadata: ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA,
126
+ }),
127
+ });
128
+
129
+ if (!loadResponse.ok) {
130
+ const errorText = await loadResponse.text();
131
+ throw new Error(`loadCodeAssist failed: ${loadResponse.status} ${loadResponse.statusText}: ${errorText}`);
132
+ }
133
+
134
+ const loadPayload = (await loadResponse.json()) as LoadCodeAssistPayload;
135
+ const existingProject = readProjectId(loadPayload.cloudaicompanionProject);
136
+ if (existingProject) {
137
+ return existingProject;
138
+ }
139
+
140
+ const tierId = getDefaultTierId(loadPayload.allowedTiers);
141
+ onProgress?.("Provisioning project...");
142
+ const onboardBody = {
143
+ tierId,
144
+ metadata: ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA,
145
+ };
146
+ const provisionedProject = await onboardProjectWithRetries(endpoint, headers, onboardBody, onProgress);
147
+ return provisionedProject;
148
+ } catch (error) {
149
+ throw new Error(
150
+ `Could not discover or provision an Antigravity project. ${error instanceof Error ? error.message : String(error)}`,
151
+ );
152
+ }
153
+ }
154
+
155
+ export async function loginAntigravity(ctrl: OAuthController): Promise<OAuthCredentials> {
156
+ return runGoogleOAuthLogin(ctrl, {
157
+ clientId: CLIENT_ID,
158
+ clientSecret: CLIENT_SECRET,
159
+ authUrl: AUTH_URL,
160
+ tokenUrl: TOKEN_URL,
161
+ scopes: SCOPES,
162
+ callbackPort: CALLBACK_PORT,
163
+ callbackPath: CALLBACK_PATH,
164
+ discoverProject,
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Refresh Antigravity token
170
+ */
171
+ export async function refreshAntigravityToken(refreshToken: string, projectId: string): Promise<OAuthCredentials> {
172
+ const response = await fetch(TOKEN_URL, {
173
+ method: "POST",
174
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
175
+ body: new URLSearchParams({
176
+ client_id: CLIENT_ID,
177
+ client_secret: CLIENT_SECRET,
178
+ refresh_token: refreshToken,
179
+ grant_type: "refresh_token",
180
+ }),
181
+ });
182
+
183
+ if (!response.ok) {
184
+ const error = await response.text();
185
+ throw new Error(`Antigravity token refresh failed: ${error}`);
186
+ }
187
+
188
+ const data = (await response.json()) as {
189
+ access_token: string;
190
+ expires_in: number;
191
+ refresh_token?: string;
192
+ };
193
+
194
+ return {
195
+ refresh: data.refresh_token || refreshToken,
196
+ access: data.access_token,
197
+ expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,
198
+ projectId,
199
+ };
200
+ }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Gemini CLI OAuth flow (Google Cloud Code Assist)
3
+ * Standard Gemini models only (gemini-2.0-flash, gemini-2.5-*)
4
+ */
5
+
6
+ import { $env } from "@gajae-code/utils";
7
+ import { getGeminiCliHeaders } from "../../providers/google-gemini-headers";
8
+ import { runGoogleOAuthLogin } from "./google-oauth-shared";
9
+ import type { OAuthController, OAuthCredentials } from "./types";
10
+
11
+ const decode = (s: string) => atob(s);
12
+ const CLIENT_ID = decode(
13
+ "NjgxMjU1ODA5Mzk1LW9vOGZ0Mm9wcmRybnA5ZTNhcWY2YXYzaG1kaWIxMzVqLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29t",
14
+ );
15
+ const CLIENT_SECRET = decode("R09DU1BYLTR1SGdNUG0tMW83U2stZ2VWNkN1NWNsWEZzeGw=");
16
+ const CALLBACK_PORT = 8085;
17
+ const CALLBACK_PATH = "/oauth2callback";
18
+ const SCOPES = [
19
+ "https://www.googleapis.com/auth/cloud-platform",
20
+ "https://www.googleapis.com/auth/userinfo.email",
21
+ "https://www.googleapis.com/auth/userinfo.profile",
22
+ ];
23
+ const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
24
+ const TOKEN_URL = "https://oauth2.googleapis.com/token";
25
+ const CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
26
+
27
+ interface LoadCodeAssistPayload {
28
+ cloudaicompanionProject?: string;
29
+ currentTier?: { id?: string };
30
+ allowedTiers?: Array<{ id?: string; isDefault?: boolean }>;
31
+ }
32
+
33
+ interface LongRunningOperationResponse {
34
+ name?: string;
35
+ done?: boolean;
36
+ response?: {
37
+ cloudaicompanionProject?: { id?: string };
38
+ };
39
+ }
40
+
41
+ const TIER_FREE = "free-tier";
42
+ const TIER_LEGACY = "legacy-tier";
43
+ const TIER_STANDARD = "standard-tier";
44
+
45
+ interface GoogleRpcErrorResponse {
46
+ error?: {
47
+ details?: Array<{ reason?: string }>;
48
+ };
49
+ }
50
+
51
+ function getDefaultTier(allowedTiers?: Array<{ id?: string; isDefault?: boolean }>): { id?: string } {
52
+ if (!allowedTiers || allowedTiers.length === 0) return { id: TIER_LEGACY };
53
+ const defaultTier = allowedTiers.find(t => t.isDefault);
54
+ return defaultTier ?? { id: TIER_LEGACY };
55
+ }
56
+
57
+ function isVpcScAffectedUser(payload: unknown): boolean {
58
+ if (!payload || typeof payload !== "object") return false;
59
+ if (!("error" in payload)) return false;
60
+ const error = (payload as GoogleRpcErrorResponse).error;
61
+ if (!error?.details || !Array.isArray(error.details)) return false;
62
+ return error.details.some(detail => detail.reason === "SECURITY_POLICY_VIOLATED");
63
+ }
64
+
65
+ async function pollOperation(
66
+ operationName: string,
67
+ headers: Record<string, string>,
68
+ onProgress?: (message: string) => void,
69
+ ): Promise<LongRunningOperationResponse> {
70
+ let attempt = 0;
71
+ while (true) {
72
+ if (attempt > 0) {
73
+ onProgress?.(`Waiting for project provisioning (attempt ${attempt + 1})...`);
74
+ await Bun.sleep(5000);
75
+ }
76
+
77
+ const response = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal/${operationName}`, {
78
+ method: "GET",
79
+ headers,
80
+ });
81
+
82
+ if (!response.ok) {
83
+ throw new Error(`Failed to poll operation: ${response.status} ${response.statusText}`);
84
+ }
85
+
86
+ const data = (await response.json()) as LongRunningOperationResponse;
87
+ if (data.done) {
88
+ return data;
89
+ }
90
+
91
+ attempt += 1;
92
+ }
93
+ }
94
+
95
+ async function discoverProject(accessToken: string, onProgress?: (message: string) => void): Promise<string> {
96
+ const envProjectId = $env.GOOGLE_CLOUD_PROJECT || $env.GOOGLE_CLOUD_PROJECT_ID;
97
+
98
+ const headers = {
99
+ Authorization: `Bearer ${accessToken}`,
100
+ "Content-Type": "application/json",
101
+ ...getGeminiCliHeaders(),
102
+ };
103
+
104
+ onProgress?.("Checking for existing Cloud Code Assist project...");
105
+ const loadResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`, {
106
+ method: "POST",
107
+ headers,
108
+ body: JSON.stringify({
109
+ cloudaicompanionProject: envProjectId,
110
+ metadata: {
111
+ ideType: "IDE_UNSPECIFIED",
112
+ platform: "PLATFORM_UNSPECIFIED",
113
+ pluginType: "GEMINI",
114
+ duetProject: envProjectId,
115
+ },
116
+ }),
117
+ });
118
+
119
+ let data: LoadCodeAssistPayload;
120
+
121
+ if (!loadResponse.ok) {
122
+ let errorPayload: unknown;
123
+ try {
124
+ errorPayload = await loadResponse.clone().json();
125
+ } catch {
126
+ errorPayload = undefined;
127
+ }
128
+
129
+ if (isVpcScAffectedUser(errorPayload)) {
130
+ data = { currentTier: { id: TIER_STANDARD } };
131
+ } else {
132
+ const errorText = await loadResponse.text();
133
+ throw new Error(`loadCodeAssist failed: ${loadResponse.status} ${loadResponse.statusText}: ${errorText}`);
134
+ }
135
+ } else {
136
+ data = (await loadResponse.json()) as LoadCodeAssistPayload;
137
+ }
138
+
139
+ if (data.currentTier) {
140
+ if (data.cloudaicompanionProject) {
141
+ return data.cloudaicompanionProject;
142
+ }
143
+ if (envProjectId) {
144
+ return envProjectId;
145
+ }
146
+ throw new Error(
147
+ "This account requires setting the GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID environment variable. " +
148
+ "See https://goo.gle/gemini-cli-auth-docs#workspace-gca",
149
+ );
150
+ }
151
+
152
+ const tier = getDefaultTier(data.allowedTiers);
153
+ const tierId = tier?.id ?? TIER_FREE;
154
+
155
+ if (tierId !== TIER_FREE && !envProjectId) {
156
+ throw new Error(
157
+ "This account requires setting the GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID environment variable. " +
158
+ "See https://goo.gle/gemini-cli-auth-docs#workspace-gca",
159
+ );
160
+ }
161
+
162
+ onProgress?.("Provisioning Cloud Code Assist project (this may take a moment)...");
163
+
164
+ const onboardBody: Record<string, unknown> = {
165
+ tierId,
166
+ metadata: {
167
+ ideType: "IDE_UNSPECIFIED",
168
+ platform: "PLATFORM_UNSPECIFIED",
169
+ pluginType: "GEMINI",
170
+ },
171
+ };
172
+
173
+ if (tierId !== TIER_FREE && envProjectId) {
174
+ onboardBody.cloudaicompanionProject = envProjectId;
175
+ (onboardBody.metadata as Record<string, unknown>).duetProject = envProjectId;
176
+ }
177
+
178
+ const onboardResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:onboardUser`, {
179
+ method: "POST",
180
+ headers,
181
+ body: JSON.stringify(onboardBody),
182
+ });
183
+
184
+ if (!onboardResponse.ok) {
185
+ const errorText = await onboardResponse.text();
186
+ throw new Error(`onboardUser failed: ${onboardResponse.status} ${onboardResponse.statusText}: ${errorText}`);
187
+ }
188
+
189
+ let lroData = (await onboardResponse.json()) as LongRunningOperationResponse;
190
+
191
+ if (!lroData.done && lroData.name) {
192
+ lroData = await pollOperation(lroData.name, headers, onProgress);
193
+ }
194
+
195
+ const projectId = lroData.response?.cloudaicompanionProject?.id;
196
+ if (projectId) {
197
+ return projectId;
198
+ }
199
+
200
+ if (envProjectId) {
201
+ return envProjectId;
202
+ }
203
+
204
+ throw new Error(
205
+ "Could not discover or provision a Google Cloud project. " +
206
+ "Try setting the GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID environment variable. " +
207
+ "See https://goo.gle/gemini-cli-auth-docs#workspace-gca",
208
+ );
209
+ }
210
+
211
+ export async function loginGeminiCli(ctrl: OAuthController): Promise<OAuthCredentials> {
212
+ return runGoogleOAuthLogin(ctrl, {
213
+ clientId: CLIENT_ID,
214
+ clientSecret: CLIENT_SECRET,
215
+ authUrl: AUTH_URL,
216
+ tokenUrl: TOKEN_URL,
217
+ scopes: SCOPES,
218
+ callbackPort: CALLBACK_PORT,
219
+ callbackPath: CALLBACK_PATH,
220
+ discoverProject,
221
+ });
222
+ }
223
+
224
+ /**
225
+ * Refresh Google Cloud Code Assist token
226
+ */
227
+ export async function refreshGoogleCloudToken(refreshToken: string, projectId: string): Promise<OAuthCredentials> {
228
+ const response = await fetch(TOKEN_URL, {
229
+ method: "POST",
230
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
231
+ body: new URLSearchParams({
232
+ client_id: CLIENT_ID,
233
+ client_secret: CLIENT_SECRET,
234
+ refresh_token: refreshToken,
235
+ grant_type: "refresh_token",
236
+ }),
237
+ });
238
+
239
+ if (!response.ok) {
240
+ const error = await response.text();
241
+ throw new Error(`Google Cloud token refresh failed: ${error}`);
242
+ }
243
+
244
+ const data = (await response.json()) as {
245
+ access_token: string;
246
+ expires_in: number;
247
+ refresh_token?: string;
248
+ };
249
+
250
+ return {
251
+ refresh: data.refresh_token || refreshToken,
252
+ access: data.access_token,
253
+ expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,
254
+ projectId,
255
+ };
256
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Shared OAuth flow for Google-style providers (Gemini CLI, Antigravity).
3
+ *
4
+ * Both providers use the same authorization-code flow shape; only the client
5
+ * credentials, scopes, endpoint constants, and project-discovery logic differ.
6
+ */
7
+ import { OAuthCallbackFlow } from "./callback-server";
8
+ import type { OAuthController, OAuthCredentials } from "./types";
9
+
10
+ export interface GoogleOAuthFlowConfig {
11
+ clientId: string;
12
+ clientSecret: string;
13
+ authUrl: string;
14
+ tokenUrl: string;
15
+ scopes: string[];
16
+ callbackPort: number;
17
+ callbackPath: string;
18
+ discoverProject: (accessToken: string, onProgress?: (message: string) => void) => Promise<string>;
19
+ }
20
+
21
+ async function getUserEmail(accessToken: string): Promise<string | undefined> {
22
+ try {
23
+ const response = await fetch("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", {
24
+ headers: { Authorization: `Bearer ${accessToken}` },
25
+ });
26
+
27
+ if (response.ok) {
28
+ const data = (await response.json()) as { email?: string };
29
+ return data.email;
30
+ }
31
+ } catch {
32
+ // Ignore errors, email is optional
33
+ }
34
+ return undefined;
35
+ }
36
+
37
+ export class GoogleOAuthFlow extends OAuthCallbackFlow {
38
+ private readonly config: GoogleOAuthFlowConfig;
39
+
40
+ constructor(ctrl: OAuthController, config: GoogleOAuthFlowConfig) {
41
+ super(ctrl, config.callbackPort, config.callbackPath);
42
+ this.config = config;
43
+ }
44
+
45
+ async generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }> {
46
+ const authParams = new URLSearchParams({
47
+ client_id: this.config.clientId,
48
+ response_type: "code",
49
+ redirect_uri: redirectUri,
50
+ scope: this.config.scopes.join(" "),
51
+ state,
52
+ access_type: "offline",
53
+ prompt: "consent",
54
+ });
55
+
56
+ const url = `${this.config.authUrl}?${authParams.toString()}`;
57
+ return { url, instructions: "Complete the sign-in in your browser." };
58
+ }
59
+
60
+ async exchangeToken(code: string, _state: string, redirectUri: string): Promise<OAuthCredentials> {
61
+ this.ctrl.onProgress?.("Exchanging authorization code for tokens...");
62
+
63
+ const tokenResponse = await fetch(this.config.tokenUrl, {
64
+ method: "POST",
65
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
66
+ body: new URLSearchParams({
67
+ client_id: this.config.clientId,
68
+ client_secret: this.config.clientSecret,
69
+ code,
70
+ grant_type: "authorization_code",
71
+ redirect_uri: redirectUri,
72
+ }),
73
+ });
74
+
75
+ if (!tokenResponse.ok) {
76
+ const error = await tokenResponse.text();
77
+ throw new Error(`Token exchange failed: ${error}`);
78
+ }
79
+
80
+ const tokenData = (await tokenResponse.json()) as {
81
+ access_token: string;
82
+ refresh_token: string;
83
+ expires_in: number;
84
+ };
85
+
86
+ if (!tokenData.refresh_token) {
87
+ throw new Error("No refresh token received. Please try again.");
88
+ }
89
+
90
+ this.ctrl.onProgress?.("Getting user info...");
91
+ const email = await getUserEmail(tokenData.access_token);
92
+ const projectId = await this.config.discoverProject(tokenData.access_token, this.ctrl.onProgress);
93
+
94
+ return {
95
+ refresh: tokenData.refresh_token,
96
+ access: tokenData.access_token,
97
+ expires: Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000,
98
+ projectId,
99
+ email,
100
+ };
101
+ }
102
+ }
103
+
104
+ export async function runGoogleOAuthLogin(
105
+ ctrl: OAuthController,
106
+ config: GoogleOAuthFlowConfig,
107
+ ): Promise<OAuthCredentials> {
108
+ const flow = new GoogleOAuthFlow(ctrl, config);
109
+ return flow.login();
110
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Hugging Face Inference login flow.
3
+ *
4
+ * Hugging Face Inference Providers expose an OpenAI-compatible endpoint via
5
+ * https://router.huggingface.co/v1.
6
+ *
7
+ * This is an API key flow:
8
+ * 1. Open browser to Hugging Face token settings
9
+ * 2. User creates/copies a token with Inference Providers permission
10
+ * 3. User pastes the token into the CLI
11
+ */
12
+
13
+ import { validateOpenAICompatibleApiKey } from "./api-key-validation";
14
+ import type { OAuthController } from "./types";
15
+
16
+ const AUTH_URL =
17
+ "https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained";
18
+ const API_BASE_URL = "https://router.huggingface.co/v1";
19
+ const VALIDATION_MODEL = "openai/gpt-oss-120b";
20
+
21
+ /**
22
+ * Login to Hugging Face Inference Providers.
23
+ *
24
+ * Opens browser to token settings, prompts user to paste their API key.
25
+ * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
26
+ */
27
+ export async function loginHuggingface(options: OAuthController): Promise<string> {
28
+ if (!options.onPrompt) {
29
+ throw new Error("Hugging Face login requires onPrompt callback");
30
+ }
31
+
32
+ options.onAuth?.({
33
+ url: AUTH_URL,
34
+ instructions:
35
+ "Create/copy a token with Make calls to Inference Providers permission (usable as HUGGINGFACE_HUB_TOKEN or HF_TOKEN)",
36
+ });
37
+
38
+ const apiKey = await options.onPrompt({
39
+ message: "Paste your Hugging Face token (HUGGINGFACE_HUB_TOKEN / HF_TOKEN)",
40
+ placeholder: "hf_...",
41
+ });
42
+
43
+ if (options.signal?.aborted) {
44
+ throw new Error("Login cancelled");
45
+ }
46
+
47
+ const trimmed = apiKey.trim();
48
+ if (!trimmed) {
49
+ throw new Error("API key is required");
50
+ }
51
+
52
+ options.onProgress?.("Validating API key...");
53
+ await validateOpenAICompatibleApiKey({
54
+ provider: "Hugging Face",
55
+ apiKey: trimmed,
56
+ baseUrl: API_BASE_URL,
57
+ model: VALIDATION_MODEL,
58
+ signal: options.signal,
59
+ });
60
+
61
+ return trimmed;
62
+ }