@aryee337/aery-ai 0.2.27 → 0.2.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/CHANGELOG.md +2914 -0
  2. package/README.md +614 -813
  3. package/package.json +140 -105
  4. package/src/api-registry.ts +96 -0
  5. package/src/auth-broker/client.ts +358 -0
  6. package/src/auth-broker/index.ts +5 -0
  7. package/src/auth-broker/refresher.ts +117 -0
  8. package/src/auth-broker/remote-store.ts +623 -0
  9. package/src/auth-broker/server.ts +644 -0
  10. package/src/auth-broker/types.ts +127 -0
  11. package/src/auth-broker/wire-schemas.ts +200 -0
  12. package/src/auth-gateway/http.ts +194 -0
  13. package/src/auth-gateway/index.ts +3 -0
  14. package/src/auth-gateway/server.ts +818 -0
  15. package/src/auth-gateway/types.ts +143 -0
  16. package/src/auth-storage.ts +4422 -0
  17. package/src/index.ts +54 -0
  18. package/src/model-cache.ts +129 -0
  19. package/src/model-manager.ts +469 -0
  20. package/src/model-thinking.ts +782 -0
  21. package/src/models.json +83530 -0
  22. package/src/models.json.d.ts +9 -0
  23. package/src/models.ts +56 -0
  24. package/src/prompts/turn-aborted-guidance.md +4 -0
  25. package/src/provider-details.ts +90 -0
  26. package/src/provider-models/bundled-references.ts +38 -0
  27. package/src/provider-models/descriptors.ts +355 -0
  28. package/src/provider-models/google.ts +88 -0
  29. package/src/provider-models/index.ts +5 -0
  30. package/src/provider-models/ollama.ts +153 -0
  31. package/src/provider-models/openai-compat.ts +2817 -0
  32. package/src/provider-models/special.ts +67 -0
  33. package/src/providers/aery-native-client.ts +228 -0
  34. package/src/providers/aery-native-server.ts +212 -0
  35. package/src/providers/amazon-bedrock.ts +873 -0
  36. package/src/providers/anthropic-client.ts +318 -0
  37. package/src/providers/anthropic-messages-server-schema.ts +243 -0
  38. package/src/providers/anthropic-messages-server.ts +683 -0
  39. package/src/providers/anthropic-wire.ts +268 -0
  40. package/src/providers/anthropic.ts +3094 -0
  41. package/src/providers/aws-credentials.ts +501 -0
  42. package/src/providers/aws-eventstream.ts +185 -0
  43. package/src/providers/aws-sigv4.ts +218 -0
  44. package/src/providers/azure-openai-responses.ts +361 -0
  45. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  46. package/src/providers/cursor/proto/agent.proto +3526 -0
  47. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  48. package/src/providers/cursor/proto/buf.yaml +17 -0
  49. package/src/providers/cursor.ts +2621 -0
  50. package/src/providers/error-message.ts +21 -0
  51. package/src/providers/github-copilot-headers.ts +140 -0
  52. package/src/providers/gitlab-duo.ts +372 -0
  53. package/src/providers/google-auth.ts +252 -0
  54. package/src/providers/google-gemini-cli.ts +809 -0
  55. package/src/providers/google-gemini-headers.ts +41 -0
  56. package/src/providers/google-shared.ts +917 -0
  57. package/src/providers/google-types.ts +167 -0
  58. package/src/providers/google-vertex.ts +91 -0
  59. package/src/providers/google.ts +41 -0
  60. package/src/providers/grammar.ts +70 -0
  61. package/src/providers/kimi.ts +52 -0
  62. package/src/providers/mock.ts +496 -0
  63. package/src/providers/ollama.ts +644 -0
  64. package/src/providers/openai-anthropic-shim.ts +138 -0
  65. package/src/providers/openai-chat-server-schema.ts +252 -0
  66. package/src/providers/openai-chat-server.ts +647 -0
  67. package/src/providers/openai-codex/constants.ts +43 -0
  68. package/src/providers/openai-codex/request-transformer.ts +161 -0
  69. package/src/providers/openai-codex/response-handler.ts +81 -0
  70. package/src/providers/openai-codex-responses.ts +3018 -0
  71. package/src/providers/openai-completions-compat.ts +300 -0
  72. package/src/providers/openai-completions.ts +1979 -0
  73. package/src/providers/openai-responses-server-schema.ts +290 -0
  74. package/src/providers/openai-responses-server.ts +1183 -0
  75. package/src/providers/openai-responses-shared.ts +873 -0
  76. package/src/providers/openai-responses.ts +679 -0
  77. package/src/providers/register-builtins.ts +436 -0
  78. package/src/providers/synthetic.ts +50 -0
  79. package/src/providers/transform-messages.ts +382 -0
  80. package/src/providers/vision-guard.ts +31 -0
  81. package/src/providers/xai-responses.ts +82 -0
  82. package/src/rate-limit-utils.ts +84 -0
  83. package/src/stream.ts +1065 -0
  84. package/src/types.ts +944 -0
  85. package/src/usage/claude.ts +482 -0
  86. package/src/usage/gemini.ts +250 -0
  87. package/src/usage/github-copilot.ts +421 -0
  88. package/src/usage/google-antigravity.ts +201 -0
  89. package/src/usage/kimi.ts +271 -0
  90. package/src/usage/minimax-code.ts +31 -0
  91. package/src/usage/openai-codex.ts +503 -0
  92. package/src/usage/shared.ts +10 -0
  93. package/src/usage/zai.ts +247 -0
  94. package/src/usage.ts +185 -0
  95. package/src/utils/abort.ts +51 -0
  96. package/src/utils/abortable-iterator.ts +69 -0
  97. package/src/utils/anthropic-auth.ts +93 -0
  98. package/src/utils/discovery/antigravity.ts +261 -0
  99. package/src/utils/discovery/codex.ts +371 -0
  100. package/src/utils/discovery/cursor.ts +306 -0
  101. package/src/utils/discovery/gemini.ts +248 -0
  102. package/src/utils/discovery/index.ts +4 -0
  103. package/src/utils/discovery/openai-compatible.ts +224 -0
  104. package/src/utils/event-stream.ts +142 -0
  105. package/src/utils/fireworks-model-id.ts +30 -0
  106. package/src/utils/foundry.ts +8 -0
  107. package/src/utils/http-inspector.ts +176 -0
  108. package/src/utils/idle-iterator.ts +267 -0
  109. package/src/utils/json-parse.ts +182 -0
  110. package/src/utils/oauth/__tests__/xai-oauth.test.ts +107 -0
  111. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  112. package/src/utils/oauth/anthropic.ts +273 -0
  113. package/src/utils/oauth/api-key-login.ts +87 -0
  114. package/src/utils/oauth/api-key-validation.ts +92 -0
  115. package/src/utils/oauth/callback-server.ts +276 -0
  116. package/src/utils/oauth/cerebras.ts +16 -0
  117. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  118. package/src/utils/oauth/cursor.ts +157 -0
  119. package/src/utils/oauth/deepseek.ts +53 -0
  120. package/src/utils/oauth/firepass.ts +24 -0
  121. package/src/utils/oauth/fireworks.ts +15 -0
  122. package/src/utils/oauth/github-copilot.ts +362 -0
  123. package/src/utils/oauth/gitlab-duo.ts +123 -0
  124. package/src/utils/oauth/google-antigravity.ts +200 -0
  125. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  126. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  127. package/src/utils/oauth/huggingface.ts +62 -0
  128. package/src/utils/oauth/index.ts +484 -0
  129. package/src/utils/oauth/kagi.ts +47 -0
  130. package/src/utils/oauth/kilo.ts +87 -0
  131. package/src/utils/oauth/kimi.ts +254 -0
  132. package/src/utils/oauth/litellm.ts +47 -0
  133. package/src/utils/oauth/lm-studio.ts +38 -0
  134. package/src/utils/oauth/minimax-code.ts +78 -0
  135. package/src/utils/oauth/moonshot.ts +23 -0
  136. package/src/utils/oauth/nanogpt.ts +15 -0
  137. package/src/utils/oauth/nvidia.ts +70 -0
  138. package/src/utils/oauth/oauth.html +203 -0
  139. package/src/utils/oauth/ollama-cloud.ts +28 -0
  140. package/src/utils/oauth/ollama.ts +47 -0
  141. package/src/utils/oauth/openai-codex.ts +299 -0
  142. package/src/utils/oauth/opencode.ts +49 -0
  143. package/src/utils/oauth/openrouter.ts +20 -0
  144. package/src/utils/oauth/parallel.ts +46 -0
  145. package/src/utils/oauth/perplexity.ts +206 -0
  146. package/src/utils/oauth/pkce.ts +18 -0
  147. package/src/utils/oauth/qianfan.ts +58 -0
  148. package/src/utils/oauth/qwen-portal.ts +60 -0
  149. package/src/utils/oauth/synthetic.ts +15 -0
  150. package/src/utils/oauth/tavily.ts +46 -0
  151. package/src/utils/oauth/together.ts +16 -0
  152. package/src/utils/oauth/types.ts +99 -0
  153. package/src/utils/oauth/venice.ts +59 -0
  154. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  155. package/src/utils/oauth/vllm.ts +40 -0
  156. package/src/utils/oauth/wafer.ts +50 -0
  157. package/src/utils/oauth/xai-oauth.ts +342 -0
  158. package/src/utils/oauth/xiaomi.ts +139 -0
  159. package/src/utils/oauth/zai.ts +60 -0
  160. package/src/utils/oauth/zenmux.ts +15 -0
  161. package/src/utils/oauth/zhipu.ts +60 -0
  162. package/src/utils/overflow.ts +137 -0
  163. package/src/utils/parse-bind.ts +54 -0
  164. package/src/utils/provider-response.ts +30 -0
  165. package/src/utils/request-debug.ts +336 -0
  166. package/src/utils/retry-after.ts +110 -0
  167. package/src/utils/retry.ts +54 -0
  168. package/src/utils/schema/CONSTRAINTS.md +164 -0
  169. package/src/utils/schema/adapt.ts +36 -0
  170. package/src/utils/schema/compatibility.ts +435 -0
  171. package/src/utils/schema/dereference.ts +98 -0
  172. package/src/utils/schema/draft.ts +341 -0
  173. package/src/utils/schema/equality.ts +97 -0
  174. package/src/utils/schema/fields.ts +191 -0
  175. package/src/utils/schema/index.ts +13 -0
  176. package/src/utils/schema/json-schema-validator.ts +577 -0
  177. package/src/utils/schema/meta-validator.ts +167 -0
  178. package/src/utils/schema/normalize.ts +1588 -0
  179. package/src/utils/schema/spill.ts +43 -0
  180. package/src/utils/schema/stamps.ts +97 -0
  181. package/src/utils/schema/types.ts +10 -0
  182. package/src/utils/schema/wire.ts +293 -0
  183. package/src/utils/schema/zod-decontaminate.ts +331 -0
  184. package/src/utils/sdk-stream-timeout.ts +43 -0
  185. package/src/utils/sse-debug.ts +289 -0
  186. package/src/utils/stream-markup-healing.ts +612 -0
  187. package/src/utils/tool-choice.ts +99 -0
  188. package/src/utils/validation.ts +1024 -0
  189. package/src/utils.ts +166 -0
  190. package/dist/api-registry.d.ts +0 -20
  191. package/dist/api-registry.d.ts.map +0 -1
  192. package/dist/api-registry.js +0 -44
  193. package/dist/api-registry.js.map +0 -1
  194. package/dist/bedrock-provider.d.ts +0 -5
  195. package/dist/bedrock-provider.d.ts.map +0 -1
  196. package/dist/bedrock-provider.js +0 -6
  197. package/dist/bedrock-provider.js.map +0 -1
  198. package/dist/cli.d.ts +0 -3
  199. package/dist/cli.d.ts.map +0 -1
  200. package/dist/cli.js +0 -130
  201. package/dist/cli.js.map +0 -1
  202. package/dist/env-api-keys.d.ts +0 -18
  203. package/dist/env-api-keys.d.ts.map +0 -1
  204. package/dist/env-api-keys.js +0 -178
  205. package/dist/env-api-keys.js.map +0 -1
  206. package/dist/image-models.d.ts +0 -10
  207. package/dist/image-models.d.ts.map +0 -1
  208. package/dist/image-models.generated.d.ts +0 -440
  209. package/dist/image-models.generated.d.ts.map +0 -1
  210. package/dist/image-models.generated.js +0 -442
  211. package/dist/image-models.generated.js.map +0 -1
  212. package/dist/image-models.js +0 -23
  213. package/dist/image-models.js.map +0 -1
  214. package/dist/images-api-registry.d.ts +0 -14
  215. package/dist/images-api-registry.d.ts.map +0 -1
  216. package/dist/images-api-registry.js +0 -22
  217. package/dist/images-api-registry.js.map +0 -1
  218. package/dist/images.d.ts +0 -4
  219. package/dist/images.d.ts.map +0 -1
  220. package/dist/images.js +0 -14
  221. package/dist/images.js.map +0 -1
  222. package/dist/index.d.ts +0 -32
  223. package/dist/index.d.ts.map +0 -1
  224. package/dist/index.js +0 -20
  225. package/dist/index.js.map +0 -1
  226. package/dist/models.d.ts +0 -18
  227. package/dist/models.d.ts.map +0 -1
  228. package/dist/models.generated.d.ts +0 -17707
  229. package/dist/models.generated.d.ts.map +0 -1
  230. package/dist/models.generated.js +0 -16561
  231. package/dist/models.generated.js.map +0 -1
  232. package/dist/models.js +0 -71
  233. package/dist/models.js.map +0 -1
  234. package/dist/oauth.d.ts +0 -2
  235. package/dist/oauth.d.ts.map +0 -1
  236. package/dist/oauth.js +0 -2
  237. package/dist/oauth.js.map +0 -1
  238. package/dist/providers/aery-error-formatting.d.ts +0 -13
  239. package/dist/providers/aery-error-formatting.d.ts.map +0 -1
  240. package/dist/providers/aery-error-formatting.js +0 -112
  241. package/dist/providers/aery-error-formatting.js.map +0 -1
  242. package/dist/providers/amazon-bedrock.d.ts +0 -38
  243. package/dist/providers/amazon-bedrock.d.ts.map +0 -1
  244. package/dist/providers/amazon-bedrock.js +0 -763
  245. package/dist/providers/amazon-bedrock.js.map +0 -1
  246. package/dist/providers/anthropic.d.ts +0 -71
  247. package/dist/providers/anthropic.d.ts.map +0 -1
  248. package/dist/providers/anthropic.js +0 -949
  249. package/dist/providers/anthropic.js.map +0 -1
  250. package/dist/providers/azure-openai-responses.d.ts +0 -15
  251. package/dist/providers/azure-openai-responses.d.ts.map +0 -1
  252. package/dist/providers/azure-openai-responses.js +0 -225
  253. package/dist/providers/azure-openai-responses.js.map +0 -1
  254. package/dist/providers/cloudflare.d.ts +0 -13
  255. package/dist/providers/cloudflare.d.ts.map +0 -1
  256. package/dist/providers/cloudflare.js +0 -26
  257. package/dist/providers/cloudflare.js.map +0 -1
  258. package/dist/providers/faux.d.ts +0 -56
  259. package/dist/providers/faux.d.ts.map +0 -1
  260. package/dist/providers/faux.js +0 -368
  261. package/dist/providers/faux.js.map +0 -1
  262. package/dist/providers/github-copilot-headers.d.ts +0 -8
  263. package/dist/providers/github-copilot-headers.d.ts.map +0 -1
  264. package/dist/providers/github-copilot-headers.js +0 -29
  265. package/dist/providers/github-copilot-headers.js.map +0 -1
  266. package/dist/providers/google-gemini-cli.d.ts +0 -74
  267. package/dist/providers/google-gemini-cli.d.ts.map +0 -1
  268. package/dist/providers/google-gemini-cli.js +0 -779
  269. package/dist/providers/google-gemini-cli.js.map +0 -1
  270. package/dist/providers/google-shared.d.ts +0 -70
  271. package/dist/providers/google-shared.d.ts.map +0 -1
  272. package/dist/providers/google-shared.js +0 -329
  273. package/dist/providers/google-shared.js.map +0 -1
  274. package/dist/providers/google-vertex.d.ts +0 -15
  275. package/dist/providers/google-vertex.d.ts.map +0 -1
  276. package/dist/providers/google-vertex.js +0 -442
  277. package/dist/providers/google-vertex.js.map +0 -1
  278. package/dist/providers/google.d.ts +0 -13
  279. package/dist/providers/google.d.ts.map +0 -1
  280. package/dist/providers/google.js +0 -400
  281. package/dist/providers/google.js.map +0 -1
  282. package/dist/providers/images/openrouter.d.ts +0 -3
  283. package/dist/providers/images/openrouter.d.ts.map +0 -1
  284. package/dist/providers/images/openrouter.js +0 -129
  285. package/dist/providers/images/openrouter.js.map +0 -1
  286. package/dist/providers/images/register-builtins.d.ts +0 -4
  287. package/dist/providers/images/register-builtins.d.ts.map +0 -1
  288. package/dist/providers/images/register-builtins.js +0 -34
  289. package/dist/providers/images/register-builtins.js.map +0 -1
  290. package/dist/providers/mistral.d.ts +0 -25
  291. package/dist/providers/mistral.d.ts.map +0 -1
  292. package/dist/providers/mistral.js +0 -535
  293. package/dist/providers/mistral.js.map +0 -1
  294. package/dist/providers/openai-codex-responses.d.ts +0 -30
  295. package/dist/providers/openai-codex-responses.d.ts.map +0 -1
  296. package/dist/providers/openai-codex-responses.js +0 -1090
  297. package/dist/providers/openai-codex-responses.js.map +0 -1
  298. package/dist/providers/openai-completions.d.ts +0 -19
  299. package/dist/providers/openai-completions.d.ts.map +0 -1
  300. package/dist/providers/openai-completions.js +0 -950
  301. package/dist/providers/openai-completions.js.map +0 -1
  302. package/dist/providers/openai-prompt-cache.d.ts +0 -3
  303. package/dist/providers/openai-prompt-cache.d.ts.map +0 -1
  304. package/dist/providers/openai-prompt-cache.js +0 -10
  305. package/dist/providers/openai-prompt-cache.js.map +0 -1
  306. package/dist/providers/openai-responses-shared.d.ts +0 -18
  307. package/dist/providers/openai-responses-shared.d.ts.map +0 -1
  308. package/dist/providers/openai-responses-shared.js +0 -492
  309. package/dist/providers/openai-responses-shared.js.map +0 -1
  310. package/dist/providers/openai-responses.d.ts +0 -13
  311. package/dist/providers/openai-responses.d.ts.map +0 -1
  312. package/dist/providers/openai-responses.js +0 -237
  313. package/dist/providers/openai-responses.js.map +0 -1
  314. package/dist/providers/register-builtins.d.ts +0 -38
  315. package/dist/providers/register-builtins.d.ts.map +0 -1
  316. package/dist/providers/register-builtins.js +0 -278
  317. package/dist/providers/register-builtins.js.map +0 -1
  318. package/dist/providers/simple-options.d.ts +0 -8
  319. package/dist/providers/simple-options.d.ts.map +0 -1
  320. package/dist/providers/simple-options.js +0 -41
  321. package/dist/providers/simple-options.js.map +0 -1
  322. package/dist/providers/transform-messages.d.ts +0 -8
  323. package/dist/providers/transform-messages.d.ts.map +0 -1
  324. package/dist/providers/transform-messages.js +0 -184
  325. package/dist/providers/transform-messages.js.map +0 -1
  326. package/dist/session-resources.d.ts +0 -4
  327. package/dist/session-resources.d.ts.map +0 -1
  328. package/dist/session-resources.js +0 -22
  329. package/dist/session-resources.js.map +0 -1
  330. package/dist/stream.d.ts +0 -8
  331. package/dist/stream.d.ts.map +0 -1
  332. package/dist/stream.js +0 -27
  333. package/dist/stream.js.map +0 -1
  334. package/dist/types.d.ts +0 -498
  335. package/dist/types.d.ts.map +0 -1
  336. package/dist/types.js +0 -2
  337. package/dist/types.js.map +0 -1
  338. package/dist/utils/diagnostics.d.ts +0 -19
  339. package/dist/utils/diagnostics.d.ts.map +0 -1
  340. package/dist/utils/diagnostics.js +0 -25
  341. package/dist/utils/diagnostics.js.map +0 -1
  342. package/dist/utils/event-stream.d.ts +0 -21
  343. package/dist/utils/event-stream.d.ts.map +0 -1
  344. package/dist/utils/event-stream.js +0 -81
  345. package/dist/utils/event-stream.js.map +0 -1
  346. package/dist/utils/hash.d.ts +0 -3
  347. package/dist/utils/hash.d.ts.map +0 -1
  348. package/dist/utils/hash.js +0 -14
  349. package/dist/utils/hash.js.map +0 -1
  350. package/dist/utils/headers.d.ts +0 -2
  351. package/dist/utils/headers.d.ts.map +0 -1
  352. package/dist/utils/headers.js +0 -8
  353. package/dist/utils/headers.js.map +0 -1
  354. package/dist/utils/json-parse.d.ts +0 -16
  355. package/dist/utils/json-parse.d.ts.map +0 -1
  356. package/dist/utils/json-parse.js +0 -113
  357. package/dist/utils/json-parse.js.map +0 -1
  358. package/dist/utils/node-http-proxy.d.ts +0 -10
  359. package/dist/utils/node-http-proxy.d.ts.map +0 -1
  360. package/dist/utils/node-http-proxy.js +0 -97
  361. package/dist/utils/node-http-proxy.js.map +0 -1
  362. package/dist/utils/oauth/anthropic.d.ts +0 -25
  363. package/dist/utils/oauth/anthropic.d.ts.map +0 -1
  364. package/dist/utils/oauth/anthropic.js +0 -335
  365. package/dist/utils/oauth/anthropic.js.map +0 -1
  366. package/dist/utils/oauth/device-code.d.ts +0 -19
  367. package/dist/utils/oauth/device-code.d.ts.map +0 -1
  368. package/dist/utils/oauth/device-code.js +0 -55
  369. package/dist/utils/oauth/device-code.js.map +0 -1
  370. package/dist/utils/oauth/github-copilot.d.ts +0 -30
  371. package/dist/utils/oauth/github-copilot.d.ts.map +0 -1
  372. package/dist/utils/oauth/github-copilot.js +0 -268
  373. package/dist/utils/oauth/github-copilot.js.map +0 -1
  374. package/dist/utils/oauth/google-antigravity.d.ts +0 -26
  375. package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
  376. package/dist/utils/oauth/google-antigravity.js +0 -377
  377. package/dist/utils/oauth/google-antigravity.js.map +0 -1
  378. package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
  379. package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
  380. package/dist/utils/oauth/google-gemini-cli.js +0 -482
  381. package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
  382. package/dist/utils/oauth/index.d.ts +0 -63
  383. package/dist/utils/oauth/index.d.ts.map +0 -1
  384. package/dist/utils/oauth/index.js +0 -131
  385. package/dist/utils/oauth/index.js.map +0 -1
  386. package/dist/utils/oauth/oauth-page.d.ts +0 -3
  387. package/dist/utils/oauth/oauth-page.d.ts.map +0 -1
  388. package/dist/utils/oauth/oauth-page.js +0 -105
  389. package/dist/utils/oauth/oauth-page.js.map +0 -1
  390. package/dist/utils/oauth/openai-codex.d.ts +0 -34
  391. package/dist/utils/oauth/openai-codex.d.ts.map +0 -1
  392. package/dist/utils/oauth/openai-codex.js +0 -385
  393. package/dist/utils/oauth/openai-codex.js.map +0 -1
  394. package/dist/utils/oauth/pkce.d.ts +0 -13
  395. package/dist/utils/oauth/pkce.d.ts.map +0 -1
  396. package/dist/utils/oauth/pkce.js +0 -31
  397. package/dist/utils/oauth/pkce.js.map +0 -1
  398. package/dist/utils/oauth/types.d.ts +0 -64
  399. package/dist/utils/oauth/types.d.ts.map +0 -1
  400. package/dist/utils/oauth/types.js +0 -2
  401. package/dist/utils/oauth/types.js.map +0 -1
  402. package/dist/utils/overflow.d.ts +0 -56
  403. package/dist/utils/overflow.d.ts.map +0 -1
  404. package/dist/utils/overflow.js +0 -151
  405. package/dist/utils/overflow.js.map +0 -1
  406. package/dist/utils/sanitize-unicode.d.ts +0 -22
  407. package/dist/utils/sanitize-unicode.d.ts.map +0 -1
  408. package/dist/utils/sanitize-unicode.js +0 -26
  409. package/dist/utils/sanitize-unicode.js.map +0 -1
  410. package/dist/utils/typebox-helpers.d.ts +0 -17
  411. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  412. package/dist/utils/typebox-helpers.js +0 -21
  413. package/dist/utils/typebox-helpers.js.map +0 -1
  414. package/dist/utils/validation.d.ts +0 -18
  415. package/dist/utils/validation.d.ts.map +0 -1
  416. package/dist/utils/validation.js +0 -281
  417. package/dist/utils/validation.js.map +0 -1
@@ -0,0 +1,362 @@
1
+ /**
2
+ * GitHub Copilot OAuth flow (opencode OAuth app)
3
+ */
4
+ import { scheduler } from "node:timers/promises";
5
+ import { getBundledModels } from "../../models";
6
+ import type { OAuthCredentials } from "./types";
7
+
8
+ const CLIENT_ID = "Ov23li8tweQw6odWQebz";
9
+
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
+ const INITIAL_POLL_INTERVAL_MULTIPLIER = 1.2;
17
+ const SLOW_DOWN_POLL_INTERVAL_MULTIPLIER = 1.4;
18
+ type DeviceCodeResponse = {
19
+ device_code: string;
20
+ user_code: string;
21
+ verification_uri: string;
22
+ interval: number;
23
+ expires_in: number;
24
+ };
25
+
26
+ type DeviceTokenSuccessResponse = {
27
+ access_token: string;
28
+ token_type?: string;
29
+ scope?: string;
30
+ };
31
+
32
+ type DeviceTokenErrorResponse = {
33
+ error: string;
34
+ error_description?: string;
35
+ interval?: number;
36
+ };
37
+
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
+ function getUrls(domain: string): {
91
+ deviceCodeUrl: string;
92
+ accessTokenUrl: string;
93
+ } {
94
+ return {
95
+ deviceCodeUrl: `https://${domain}/login/device/code`,
96
+ accessTokenUrl: `https://${domain}/login/oauth/access_token`,
97
+ };
98
+ }
99
+
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);
111
+ if (!response.ok) {
112
+ const text = await response.text();
113
+ throw new Error(`${response.status} ${response.statusText}: ${text}`);
114
+ }
115
+ return response.json();
116
+ }
117
+
118
+ async function startDeviceFlow(domain: string): Promise<DeviceCodeResponse> {
119
+ 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,
126
+ },
127
+ body: JSON.stringify({
128
+ client_id: CLIENT_ID,
129
+ scope: "read:user",
130
+ }),
131
+ });
132
+
133
+ if (!data || typeof data !== "object") {
134
+ throw new Error("Invalid device code response");
135
+ }
136
+
137
+ const deviceCode = (data as Record<string, unknown>).device_code;
138
+ const userCode = (data as Record<string, unknown>).user_code;
139
+ const verificationUri = (data as Record<string, unknown>).verification_uri;
140
+ const interval = (data as Record<string, unknown>).interval;
141
+ const expiresIn = (data as Record<string, unknown>).expires_in;
142
+
143
+ if (
144
+ typeof deviceCode !== "string" ||
145
+ typeof userCode !== "string" ||
146
+ typeof verificationUri !== "string" ||
147
+ typeof interval !== "number" ||
148
+ typeof expiresIn !== "number"
149
+ ) {
150
+ throw new Error("Invalid device code response fields");
151
+ }
152
+
153
+ return {
154
+ device_code: deviceCode,
155
+ user_code: userCode,
156
+ verification_uri: verificationUri,
157
+ interval,
158
+ expires_in: expiresIn,
159
+ };
160
+ }
161
+
162
+ async function pollForGitHubAccessToken(
163
+ domain: string,
164
+ deviceCode: string,
165
+ intervalSeconds: number,
166
+ expiresIn: number,
167
+ signal?: AbortSignal,
168
+ pollIntervalFloorMs = 1000,
169
+ pollIntervalScaleMs = 1000,
170
+ ) {
171
+ const urls = getUrls(domain);
172
+ const deadline = Date.now() + expiresIn * 1000;
173
+ let intervalMs = Math.max(pollIntervalFloorMs, Math.floor(intervalSeconds * pollIntervalScaleMs));
174
+ let intervalMultiplier = INITIAL_POLL_INTERVAL_MULTIPLIER;
175
+ let slowDownResponses = 0;
176
+
177
+ while (Date.now() < deadline) {
178
+ if (signal?.aborted) {
179
+ throw new Error("Login cancelled");
180
+ }
181
+
182
+ const remainingMs = deadline - Date.now();
183
+ const waitMs = Math.min(Math.ceil(intervalMs * intervalMultiplier), remainingMs);
184
+ try {
185
+ await scheduler.wait(waitMs, { signal });
186
+ } catch {
187
+ throw new Error("Login cancelled");
188
+ }
189
+
190
+ const raw = await fetchJson(urls.accessTokenUrl, {
191
+ method: "POST",
192
+ headers: {
193
+ Accept: "application/json",
194
+ "Content-Type": "application/json",
195
+ ...OPENCODE_HEADERS,
196
+ },
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
+ });
203
+
204
+ if (raw && typeof raw === "object" && typeof (raw as DeviceTokenSuccessResponse).access_token === "string") {
205
+ return (raw as DeviceTokenSuccessResponse).access_token;
206
+ }
207
+
208
+ if (raw && typeof raw === "object" && typeof (raw as DeviceTokenErrorResponse).error === "string") {
209
+ const { error, error_description: description, interval } = raw as DeviceTokenErrorResponse;
210
+ if (error === "authorization_pending") {
211
+ continue;
212
+ }
213
+
214
+ if (error === "slow_down") {
215
+ slowDownResponses += 1;
216
+ intervalMs =
217
+ typeof interval === "number" && interval > 0
218
+ ? Math.max(pollIntervalFloorMs, interval * pollIntervalScaleMs)
219
+ : Math.max(pollIntervalFloorMs, intervalMs + 5 * pollIntervalScaleMs);
220
+ intervalMultiplier = SLOW_DOWN_POLL_INTERVAL_MULTIPLIER;
221
+ continue;
222
+ }
223
+
224
+ const descriptionSuffix = description ? `: ${description}` : "";
225
+ throw new Error(`Device flow failed: ${error}${descriptionSuffix}`);
226
+ }
227
+ }
228
+
229
+ if (slowDownResponses > 0) {
230
+ throw new Error(
231
+ "Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.",
232
+ );
233
+ }
234
+
235
+ throw new Error("Device flow timed out");
236
+ }
237
+
238
+ /** Far-future expiry (10 years). GitHub OAuth tokens are long-lived; no JWT exchange needed. */
239
+ const FAR_FUTURE_MS = Date.now() + 10 * 365.25 * 24 * 60 * 60 * 1000;
240
+
241
+ /**
242
+ * Refresh GitHub Copilot token.
243
+ * With the opencode OAuth flow, the GitHub token is used directly — no JWT exchange needed.
244
+ */
245
+ export function refreshGitHubCopilotToken(refreshToken: string, enterpriseDomain?: string): OAuthCredentials {
246
+ return {
247
+ refresh: refreshToken,
248
+ access: refreshToken,
249
+ expires: FAR_FUTURE_MS,
250
+ enterpriseUrl: enterpriseDomain,
251
+ };
252
+ }
253
+
254
+ /**
255
+ * Enable a model for the user's GitHub Copilot account.
256
+ * This is required for some models (like Claude, Grok) before they can be used.
257
+ */
258
+ async function enableGitHubCopilotModel(token: string, modelId: string, enterpriseDomain?: string): Promise<boolean> {
259
+ const baseUrl = getGitHubCopilotBaseUrl(enterpriseDomain);
260
+ const url = `${baseUrl}/models/${modelId}/policy`;
261
+
262
+ try {
263
+ const response = await fetch(url, {
264
+ method: "POST",
265
+ headers: {
266
+ "Content-Type": "application/json",
267
+ Authorization: `Bearer ${token}`,
268
+ ...OPENCODE_HEADERS,
269
+ "openai-intent": "chat-policy",
270
+ "x-interaction-type": "chat-policy",
271
+ },
272
+ body: JSON.stringify({ state: "enabled" }),
273
+ });
274
+ return response.ok;
275
+ } catch {
276
+ return false;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Enable all known GitHub Copilot models that may require policy acceptance.
282
+ * Called after successful login to ensure all models are available.
283
+ */
284
+ async function enableAllGitHubCopilotModels(
285
+ token: string,
286
+ enterpriseDomain?: string,
287
+ onProgress?: (model: string, success: boolean) => void,
288
+ ): Promise<void> {
289
+ const models = getBundledModels("github-copilot");
290
+ const BATCH_SIZE = 5;
291
+ for (let i = 0; i < models.length; i += BATCH_SIZE) {
292
+ const batch = models.slice(i, i + BATCH_SIZE);
293
+ await Promise.all(
294
+ batch.map(async model => {
295
+ const success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain);
296
+ onProgress?.(model.id, success);
297
+ }),
298
+ );
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Login with GitHub Copilot OAuth (device code flow)
304
+ *
305
+ * @param options.onAuth - Callback with URL and optional instructions (user code)
306
+ * @param options.onPrompt - Callback to prompt user for input
307
+ * @param options.onProgress - Optional progress callback
308
+ * @param options.signal - Optional AbortSignal for cancellation
309
+ */
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> {
318
+ const input = await options.onPrompt({
319
+ message: "GitHub Enterprise URL/domain (blank for github.com)",
320
+ placeholder: "company.ghe.com",
321
+ allowEmpty: true,
322
+ });
323
+
324
+ if (options.signal?.aborted) {
325
+ throw new Error("Login cancelled");
326
+ }
327
+
328
+ const trimmed = input.trim();
329
+ const normalizedDomain = normalizeDomain(input);
330
+ if (trimmed && !normalizedDomain) {
331
+ throw new Error("Invalid GitHub Enterprise URL/domain");
332
+ }
333
+ const enterpriseDomain = normalizeGitHubCopilotEnterpriseDomain(normalizedDomain ?? undefined);
334
+ const domain =
335
+ normalizedDomain && isPublicGitHubHost(normalizedDomain) ? "github.com" : (normalizedDomain ?? "github.com");
336
+
337
+ const device = await startDeviceFlow(domain);
338
+ options.onAuth(device.verification_uri, `Enter code: ${device.user_code}`);
339
+
340
+ const githubAccessToken = await pollForGitHubAccessToken(
341
+ domain,
342
+ device.device_code,
343
+ device.interval,
344
+ device.expires_in,
345
+ options.signal,
346
+ options.pollIntervalFloorMs,
347
+ options.pollIntervalScaleMs,
348
+ );
349
+
350
+ // With opencode OAuth, the GitHub token is used directly for all API requests
351
+ const credentials: OAuthCredentials = {
352
+ refresh: githubAccessToken,
353
+ access: githubAccessToken,
354
+ expires: FAR_FUTURE_MS,
355
+ enterpriseUrl: enterpriseDomain ?? undefined,
356
+ };
357
+
358
+ // Enable all models after successful login
359
+ options.onProgress?.("Enabling models...");
360
+ await enableAllGitHubCopilotModels(githubAccessToken, enterpriseDomain ?? undefined);
361
+ return credentials;
362
+ }
@@ -0,0 +1,123 @@
1
+ import { clearGitLabDuoDirectAccessCache } from "../../providers/gitlab-duo";
2
+ import { OAuthCallbackFlow } from "./callback-server";
3
+ import { generatePKCE } from "./pkce";
4
+ import type { OAuthCredentials, OAuthLoginCallbacks } from "./types";
5
+
6
+ const GITLAB_COM_URL = "https://gitlab.com";
7
+ const BUNDLED_CLIENT_ID = "da4edff2e6ebd2bc3208611e2768bc1c1dd7be791dc5ff26ca34ca9ee44f7d4b";
8
+ const OAUTH_SCOPES = ["api"];
9
+ const CALLBACK_PORT = 8080;
10
+ const CALLBACK_PATH = "/callback";
11
+
12
+ interface PKCEPair {
13
+ verifier: string;
14
+ challenge: string;
15
+ }
16
+
17
+ function mapTokenResponse(payload: {
18
+ access_token?: string;
19
+ refresh_token?: string;
20
+ expires_in?: number;
21
+ created_at?: number;
22
+ }): OAuthCredentials {
23
+ if (!payload.access_token || !payload.refresh_token || typeof payload.expires_in !== "number") {
24
+ throw new Error("GitLab OAuth token response missing required fields");
25
+ }
26
+
27
+ const createdAtMs =
28
+ typeof payload.created_at === "number" && Number.isFinite(payload.created_at)
29
+ ? payload.created_at * 1000
30
+ : Date.now();
31
+
32
+ return {
33
+ access: payload.access_token,
34
+ refresh: payload.refresh_token,
35
+ expires: createdAtMs + payload.expires_in * 1000 - 5 * 60 * 1000,
36
+ };
37
+ }
38
+
39
+ class GitLabDuoOAuthFlow extends OAuthCallbackFlow {
40
+ #pkce: PKCEPair;
41
+
42
+ constructor(ctrl: OAuthLoginCallbacks, pkce: PKCEPair) {
43
+ super(ctrl, CALLBACK_PORT, CALLBACK_PATH);
44
+ this.#pkce = pkce;
45
+ }
46
+
47
+ override async generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }> {
48
+ const authParams = new URLSearchParams({
49
+ client_id: BUNDLED_CLIENT_ID,
50
+ redirect_uri: redirectUri,
51
+ response_type: "code",
52
+ scope: OAUTH_SCOPES.join(" "),
53
+ code_challenge: this.#pkce.challenge,
54
+ code_challenge_method: "S256",
55
+ state,
56
+ });
57
+
58
+ return {
59
+ url: `${GITLAB_COM_URL}/oauth/authorize?${authParams.toString()}`,
60
+ instructions: "Complete GitLab login in browser. Authentication will finish automatically.",
61
+ };
62
+ }
63
+
64
+ override async exchangeToken(code: string, _state: string, redirectUri: string): Promise<OAuthCredentials> {
65
+ const response = await fetch(`${GITLAB_COM_URL}/oauth/token`, {
66
+ method: "POST",
67
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
68
+ body: new URLSearchParams({
69
+ client_id: BUNDLED_CLIENT_ID,
70
+ grant_type: "authorization_code",
71
+ code,
72
+ code_verifier: this.#pkce.verifier,
73
+ redirect_uri: redirectUri,
74
+ }).toString(),
75
+ });
76
+
77
+ if (!response.ok) {
78
+ throw new Error(`GitLab OAuth token exchange failed: ${response.status} ${await response.text()}`);
79
+ }
80
+
81
+ clearGitLabDuoDirectAccessCache();
82
+ return mapTokenResponse(
83
+ (await response.json()) as {
84
+ access_token?: string;
85
+ refresh_token?: string;
86
+ expires_in?: number;
87
+ created_at?: number;
88
+ },
89
+ );
90
+ }
91
+ }
92
+
93
+ export async function loginGitLabDuo(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
94
+ const pkce = await generatePKCE();
95
+ const flow = new GitLabDuoOAuthFlow(callbacks, pkce);
96
+ return flow.login();
97
+ }
98
+
99
+ export async function refreshGitLabDuoToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {
100
+ const response = await fetch(`${GITLAB_COM_URL}/oauth/token`, {
101
+ method: "POST",
102
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
103
+ body: new URLSearchParams({
104
+ client_id: BUNDLED_CLIENT_ID,
105
+ grant_type: "refresh_token",
106
+ refresh_token: credentials.refresh,
107
+ }).toString(),
108
+ });
109
+
110
+ if (!response.ok) {
111
+ throw new Error(`GitLab OAuth refresh failed: ${response.status} ${await response.text()}`);
112
+ }
113
+
114
+ clearGitLabDuoDirectAccessCache();
115
+ return mapTokenResponse(
116
+ (await response.json()) as {
117
+ access_token?: string;
118
+ refresh_token?: string;
119
+ expires_in?: number;
120
+ created_at?: number;
121
+ },
122
+ );
123
+ }
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Antigravity OAuth flow (Gemini 3, Claude, 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
+ }