@vib-rato/ai 0.16.0

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 (525) hide show
  1. package/CHANGELOG.md +3354 -0
  2. package/README.md +1194 -0
  3. package/dist/types/adapter-internals/provider-safety-stop.d.ts +45 -0
  4. package/dist/types/api-registry.d.ts +30 -0
  5. package/dist/types/auth-broker/client.d.ts +80 -0
  6. package/dist/types/auth-broker/index.d.ts +5 -0
  7. package/dist/types/auth-broker/redact.d.ts +7 -0
  8. package/dist/types/auth-broker/refresher.d.ts +25 -0
  9. package/dist/types/auth-broker/remote-store.d.ts +153 -0
  10. package/dist/types/auth-broker/server.d.ts +32 -0
  11. package/dist/types/auth-broker/types.d.ts +132 -0
  12. package/dist/types/auth-broker/wire-schemas.d.ts +555 -0
  13. package/dist/types/auth-gateway/http.d.ts +40 -0
  14. package/dist/types/auth-gateway/index.d.ts +3 -0
  15. package/dist/types/auth-gateway/server.d.ts +70 -0
  16. package/dist/types/auth-gateway/types.d.ts +129 -0
  17. package/dist/types/auth-storage.d.ts +1074 -0
  18. package/dist/types/cli.d.ts +2 -0
  19. package/dist/types/codex-tools.d.ts +4 -0
  20. package/dist/types/context-cap-policy.d.ts +68 -0
  21. package/dist/types/core.d.ts +35 -0
  22. package/dist/types/index.d.ts +55 -0
  23. package/dist/types/model-cache.d.ts +24 -0
  24. package/dist/types/model-manager.d.ts +77 -0
  25. package/dist/types/model-pricing.d.ts +3 -0
  26. package/dist/types/model-retirements.d.ts +6 -0
  27. package/dist/types/model-thinking.d.ts +100 -0
  28. package/dist/types/models.d.ts +21 -0
  29. package/dist/types/openai-completions-compat.d.ts +34 -0
  30. package/dist/types/provider-details.d.ts +24 -0
  31. package/dist/types/provider-models/bundled-references.d.ts +4 -0
  32. package/dist/types/provider-models/descriptors.d.ts +48 -0
  33. package/dist/types/provider-models/google.d.ts +20 -0
  34. package/dist/types/provider-models/index.d.ts +5 -0
  35. package/dist/types/provider-models/ollama.d.ts +7 -0
  36. package/dist/types/provider-models/openai-compat.d.ts +293 -0
  37. package/dist/types/provider-models/special.d.ts +29 -0
  38. package/dist/types/providers/amazon-bedrock.d.ts +60 -0
  39. package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
  40. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  41. package/dist/types/providers/anthropic.d.ts +280 -0
  42. package/dist/types/providers/aws-credential-config.d.ts +19 -0
  43. package/dist/types/providers/aws-credentials.d.ts +43 -0
  44. package/dist/types/providers/aws-eventstream.d.ts +38 -0
  45. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  46. package/dist/types/providers/azure-openai-responses.d.ts +22 -0
  47. package/dist/types/providers/composer-discipline.d.ts +32 -0
  48. package/dist/types/providers/cursor/client-version.d.ts +10 -0
  49. package/dist/types/providers/cursor/exec-modern.d.ts +98 -0
  50. package/dist/types/providers/cursor/gen/agent_pb.d.ts +16769 -0
  51. package/dist/types/providers/cursor-pi-args.d.ts +119 -0
  52. package/dist/types/providers/cursor.d.ts +72 -0
  53. package/dist/types/providers/dashscope-token-plan-headers.d.ts +57 -0
  54. package/dist/types/providers/error-message.d.ts +27 -0
  55. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  56. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  57. package/dist/types/providers/google-auth.d.ts +26 -0
  58. package/dist/types/providers/google-gemini-cli.d.ts +75 -0
  59. package/dist/types/providers/google-gemini-headers.d.ts +43 -0
  60. package/dist/types/providers/google-shared.d.ts +183 -0
  61. package/dist/types/providers/google-types.d.ts +138 -0
  62. package/dist/types/providers/google-vertex.d.ts +11 -0
  63. package/dist/types/providers/google.d.ts +4 -0
  64. package/dist/types/providers/grammar.d.ts +1 -0
  65. package/dist/types/providers/kimi.d.ts +27 -0
  66. package/dist/types/providers/kiro-api-key.d.ts +50 -0
  67. package/dist/types/providers/kiro-codewhisperer.d.ts +11 -0
  68. package/dist/types/providers/mock.d.ts +189 -0
  69. package/dist/types/providers/ollama.d.ts +41 -0
  70. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  71. package/dist/types/providers/openai-bounded-rate-limits.d.ts +3 -0
  72. package/dist/types/providers/openai-chat-server-schema.d.ts +1733 -0
  73. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  74. package/dist/types/providers/openai-codex/constants.d.ts +26 -0
  75. package/dist/types/providers/openai-codex/request-transformer.d.ts +50 -0
  76. package/dist/types/providers/openai-codex/response-handler.d.ts +18 -0
  77. package/dist/types/providers/openai-codex-responses.d.ts +71 -0
  78. package/dist/types/providers/openai-completions-compat.d.ts +6 -0
  79. package/dist/types/providers/openai-completions.d.ts +35 -0
  80. package/dist/types/providers/openai-opencodex-responses.d.ts +10 -0
  81. package/dist/types/providers/openai-request-transform.d.ts +4 -0
  82. package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
  83. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  84. package/dist/types/providers/openai-responses-shared.d.ts +106 -0
  85. package/dist/types/providers/openai-responses.d.ts +37 -0
  86. package/dist/types/providers/pi-native-client.d.ts +29 -0
  87. package/dist/types/providers/pi-native-server.d.ts +60 -0
  88. package/dist/types/providers/register-builtins.d.ts +59 -0
  89. package/dist/types/providers/synthetic.d.ts +26 -0
  90. package/dist/types/providers/transform-messages.d.ts +33 -0
  91. package/dist/types/providers/vision-guard.d.ts +8 -0
  92. package/dist/types/rate-limit-utils.d.ts +19 -0
  93. package/dist/types/stream.d.ts +45 -0
  94. package/dist/types/types.d.ts +1073 -0
  95. package/dist/types/usage/claude.d.ts +3 -0
  96. package/dist/types/usage/gemini.d.ts +2 -0
  97. package/dist/types/usage/github-copilot.d.ts +7 -0
  98. package/dist/types/usage/google-antigravity.d.ts +2 -0
  99. package/dist/types/usage/grok-cli.d.ts +17 -0
  100. package/dist/types/usage/kimi.d.ts +4 -0
  101. package/dist/types/usage/minimax-code.d.ts +2 -0
  102. package/dist/types/usage/openai-codex.d.ts +3 -0
  103. package/dist/types/usage/shared.d.ts +1 -0
  104. package/dist/types/usage/zai.d.ts +2 -0
  105. package/dist/types/usage.d.ts +264 -0
  106. package/dist/types/utils/abort.d.ts +19 -0
  107. package/dist/types/utils/anthropic-auth.d.ts +39 -0
  108. package/dist/types/utils/block-symbols.d.ts +6 -0
  109. package/dist/types/utils/discovery/antigravity.d.ts +67 -0
  110. package/dist/types/utils/discovery/codex.d.ts +38 -0
  111. package/dist/types/utils/discovery/cursor.d.ts +49 -0
  112. package/dist/types/utils/discovery/gemini.d.ts +25 -0
  113. package/dist/types/utils/discovery/index.d.ts +4 -0
  114. package/dist/types/utils/discovery/openai-compatible.d.ts +81 -0
  115. package/dist/types/utils/event-stream.d.ts +41 -0
  116. package/dist/types/utils/fallback-transport.d.ts +110 -0
  117. package/dist/types/utils/fireworks-model-id.d.ts +10 -0
  118. package/dist/types/utils/foundry.d.ts +11 -0
  119. package/dist/types/utils/h2-fetch.d.ts +22 -0
  120. package/dist/types/utils/http-inspector.d.ts +59 -0
  121. package/dist/types/utils/idle-iterator.d.ts +122 -0
  122. package/dist/types/utils/json-parse.d.ts +98 -0
  123. package/dist/types/utils/oauth/alibaba-token-plan.d.ts +19 -0
  124. package/dist/types/utils/oauth/anthropic.d.ts +41 -0
  125. package/dist/types/utils/oauth/api-key-login.d.ts +38 -0
  126. package/dist/types/utils/oauth/api-key-validation.d.ts +39 -0
  127. package/dist/types/utils/oauth/bizrouter.d.ts +1 -0
  128. package/dist/types/utils/oauth/callback-server.d.ts +80 -0
  129. package/dist/types/utils/oauth/cerebras.d.ts +1 -0
  130. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
  131. package/dist/types/utils/oauth/commandcode.d.ts +1 -0
  132. package/dist/types/utils/oauth/cursor.d.ts +15 -0
  133. package/dist/types/utils/oauth/deepinfra.d.ts +1 -0
  134. package/dist/types/utils/oauth/deepseek.d.ts +10 -0
  135. package/dist/types/utils/oauth/firepass.d.ts +1 -0
  136. package/dist/types/utils/oauth/fireworks.d.ts +1 -0
  137. package/dist/types/utils/oauth/fugu.d.ts +1 -0
  138. package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
  139. package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
  140. package/dist/types/utils/oauth/glm-zcode.d.ts +71 -0
  141. package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
  142. package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
  143. package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
  144. package/dist/types/utils/oauth/huggingface.d.ts +19 -0
  145. package/dist/types/utils/oauth/index.d.ts +39 -0
  146. package/dist/types/utils/oauth/kagi.d.ts +17 -0
  147. package/dist/types/utils/oauth/kilo.d.ts +5 -0
  148. package/dist/types/utils/oauth/kimi.d.ts +17 -0
  149. package/dist/types/utils/oauth/kiro.d.ts +71 -0
  150. package/dist/types/utils/oauth/litellm.d.ts +18 -0
  151. package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
  152. package/dist/types/utils/oauth/mara.d.ts +1 -0
  153. package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
  154. package/dist/types/utils/oauth/moonshot.d.ts +1 -0
  155. package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
  156. package/dist/types/utils/oauth/nvidia.d.ts +18 -0
  157. package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
  158. package/dist/types/utils/oauth/ollama.d.ts +18 -0
  159. package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
  160. package/dist/types/utils/oauth/opencode.d.ts +18 -0
  161. package/dist/types/utils/oauth/opengateway.d.ts +1 -0
  162. package/dist/types/utils/oauth/openrouter.d.ts +1 -0
  163. package/dist/types/utils/oauth/parallel.d.ts +17 -0
  164. package/dist/types/utils/oauth/perplexity.d.ts +4 -0
  165. package/dist/types/utils/oauth/pkce.d.ts +8 -0
  166. package/dist/types/utils/oauth/qianfan.d.ts +17 -0
  167. package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
  168. package/dist/types/utils/oauth/sglang.d.ts +16 -0
  169. package/dist/types/utils/oauth/synthetic.d.ts +1 -0
  170. package/dist/types/utils/oauth/tavily.d.ts +17 -0
  171. package/dist/types/utils/oauth/together.d.ts +1 -0
  172. package/dist/types/utils/oauth/types.d.ts +56 -0
  173. package/dist/types/utils/oauth/venice.d.ts +18 -0
  174. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
  175. package/dist/types/utils/oauth/vllm.d.ts +16 -0
  176. package/dist/types/utils/oauth/xai.d.ts +30 -0
  177. package/dist/types/utils/oauth/xiaomi.d.ts +25 -0
  178. package/dist/types/utils/oauth/zai.d.ts +18 -0
  179. package/dist/types/utils/oauth/zenmux.d.ts +1 -0
  180. package/dist/types/utils/overflow.d.ts +14 -0
  181. package/dist/types/utils/parse-bind.d.ts +26 -0
  182. package/dist/types/utils/provider-response.d.ts +6 -0
  183. package/dist/types/utils/provider-safety-stop.d.ts +8 -0
  184. package/dist/types/utils/proxy.d.ts +7 -0
  185. package/dist/types/utils/retry-after.d.ts +3 -0
  186. package/dist/types/utils/retry-budget.d.ts +1 -0
  187. package/dist/types/utils/retry.d.ts +29 -0
  188. package/dist/types/utils/schema/adapt.d.ts +24 -0
  189. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  190. package/dist/types/utils/schema/dereference.d.ts +11 -0
  191. package/dist/types/utils/schema/draft.d.ts +10 -0
  192. package/dist/types/utils/schema/equality.d.ts +4 -0
  193. package/dist/types/utils/schema/fields.d.ts +49 -0
  194. package/dist/types/utils/schema/index.d.ts +14 -0
  195. package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
  196. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  197. package/dist/types/utils/schema/normalize.d.ts +93 -0
  198. package/dist/types/utils/schema/root-combinator.d.ts +12 -0
  199. package/dist/types/utils/schema/spill.d.ts +8 -0
  200. package/dist/types/utils/schema/stamps.d.ts +25 -0
  201. package/dist/types/utils/schema/types.d.ts +4 -0
  202. package/dist/types/utils/schema/wire.d.ts +54 -0
  203. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  204. package/dist/types/utils/sse-debug.d.ts +10 -0
  205. package/dist/types/utils/tool-call-healing.d.ts +80 -0
  206. package/dist/types/utils/tool-choice-capability.d.ts +57 -0
  207. package/dist/types/utils/tool-choice.d.ts +50 -0
  208. package/dist/types/utils/validation.d.ts +17 -0
  209. package/dist/types/utils.d.ts +117 -0
  210. package/package.json +152 -0
  211. package/src/adapter-internals/provider-safety-stop.d.ts +45 -0
  212. package/src/adapter-internals/provider-safety-stop.ts +156 -0
  213. package/src/api-registry.d.ts +30 -0
  214. package/src/api-registry.ts +96 -0
  215. package/src/auth-broker/client.ts +444 -0
  216. package/src/auth-broker/index.ts +5 -0
  217. package/src/auth-broker/redact.ts +39 -0
  218. package/src/auth-broker/refresher.ts +130 -0
  219. package/src/auth-broker/remote-store.ts +1576 -0
  220. package/src/auth-broker/server.ts +764 -0
  221. package/src/auth-broker/types.ts +164 -0
  222. package/src/auth-broker/wire-schemas.ts +261 -0
  223. package/src/auth-gateway/http.ts +198 -0
  224. package/src/auth-gateway/index.ts +3 -0
  225. package/src/auth-gateway/server.ts +1315 -0
  226. package/src/auth-gateway/types.ts +160 -0
  227. package/src/auth-storage.ts +7312 -0
  228. package/src/cli.ts +269 -0
  229. package/src/codex-tools.d.ts +4 -0
  230. package/src/codex-tools.ts +24 -0
  231. package/src/context-cap-policy.d.ts +68 -0
  232. package/src/context-cap-policy.ts +123 -0
  233. package/src/core.ts +44 -0
  234. package/src/index.ts +61 -0
  235. package/src/model-cache.ts +236 -0
  236. package/src/model-manager.ts +744 -0
  237. package/src/model-pricing.d.ts +3 -0
  238. package/src/model-pricing.ts +68 -0
  239. package/src/model-retirements.d.ts +6 -0
  240. package/src/model-retirements.ts +19 -0
  241. package/src/model-thinking.d.ts +100 -0
  242. package/src/model-thinking.ts +1054 -0
  243. package/src/models.d.ts +21 -0
  244. package/src/models.json +94672 -0
  245. package/src/models.json.d.ts +9 -0
  246. package/src/models.ts +126 -0
  247. package/src/openai-completions-compat.d.ts +34 -0
  248. package/src/openai-completions-compat.ts +383 -0
  249. package/src/prompts/composer-bash-policy-recovery.md +1 -0
  250. package/src/prompts/cursor-composer-bash-policy-recovery.md +1 -0
  251. package/src/prompts/cursor-composer-edit-discipline.md +7 -0
  252. package/src/prompts/turn-aborted-guidance.md +4 -0
  253. package/src/provider-details.ts +90 -0
  254. package/src/provider-models/bundled-references.ts +38 -0
  255. package/src/provider-models/descriptors.ts +392 -0
  256. package/src/provider-models/google.ts +92 -0
  257. package/src/provider-models/index.ts +5 -0
  258. package/src/provider-models/ollama.ts +159 -0
  259. package/src/provider-models/openai-compat.ts +2920 -0
  260. package/src/provider-models/special.ts +185 -0
  261. package/src/providers/amazon-bedrock.d.ts +60 -0
  262. package/src/providers/amazon-bedrock.ts +939 -0
  263. package/src/providers/anthropic-messages-server-schema.ts +229 -0
  264. package/src/providers/anthropic-messages-server.ts +839 -0
  265. package/src/providers/anthropic.d.ts +280 -0
  266. package/src/providers/anthropic.ts +4421 -0
  267. package/src/providers/aws-credential-config.d.ts +19 -0
  268. package/src/providers/aws-credential-config.ts +179 -0
  269. package/src/providers/aws-credentials.d.ts +43 -0
  270. package/src/providers/aws-credentials.ts +457 -0
  271. package/src/providers/aws-eventstream.d.ts +38 -0
  272. package/src/providers/aws-eventstream.ts +185 -0
  273. package/src/providers/aws-sigv4.d.ts +55 -0
  274. package/src/providers/aws-sigv4.ts +218 -0
  275. package/src/providers/azure-openai-responses.d.ts +22 -0
  276. package/src/providers/azure-openai-responses.ts +447 -0
  277. package/src/providers/composer-discipline.d.ts +32 -0
  278. package/src/providers/composer-discipline.ts +95 -0
  279. package/src/providers/cursor/client-version.d.ts +10 -0
  280. package/src/providers/cursor/client-version.ts +10 -0
  281. package/src/providers/cursor/exec-modern.d.ts +98 -0
  282. package/src/providers/cursor/exec-modern.ts +497 -0
  283. package/src/providers/cursor/gen/agent_pb.d.ts +16769 -0
  284. package/src/providers/cursor/gen/agent_pb.ts +19780 -0
  285. package/src/providers/cursor/proto/agent.proto +4533 -0
  286. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  287. package/src/providers/cursor/proto/buf.yaml +17 -0
  288. package/src/providers/cursor-pi-args.d.ts +119 -0
  289. package/src/providers/cursor-pi-args.ts +187 -0
  290. package/src/providers/cursor.d.ts +72 -0
  291. package/src/providers/cursor.ts +3396 -0
  292. package/src/providers/dashscope-token-plan-headers.d.ts +57 -0
  293. package/src/providers/dashscope-token-plan-headers.ts +84 -0
  294. package/src/providers/error-message.d.ts +27 -0
  295. package/src/providers/error-message.ts +21 -0
  296. package/src/providers/github-copilot-headers.d.ts +40 -0
  297. package/src/providers/github-copilot-headers.ts +140 -0
  298. package/src/providers/gitlab-duo.d.ts +27 -0
  299. package/src/providers/gitlab-duo.ts +393 -0
  300. package/src/providers/google-auth.d.ts +26 -0
  301. package/src/providers/google-auth.ts +262 -0
  302. package/src/providers/google-gemini-cli.d.ts +75 -0
  303. package/src/providers/google-gemini-cli.ts +969 -0
  304. package/src/providers/google-gemini-headers.d.ts +43 -0
  305. package/src/providers/google-gemini-headers.ts +100 -0
  306. package/src/providers/google-shared.d.ts +183 -0
  307. package/src/providers/google-shared.ts +1105 -0
  308. package/src/providers/google-types.d.ts +138 -0
  309. package/src/providers/google-types.ts +167 -0
  310. package/src/providers/google-vertex.d.ts +11 -0
  311. package/src/providers/google-vertex.ts +124 -0
  312. package/src/providers/google.d.ts +4 -0
  313. package/src/providers/google.ts +41 -0
  314. package/src/providers/grammar.d.ts +1 -0
  315. package/src/providers/grammar.ts +70 -0
  316. package/src/providers/kimi.d.ts +27 -0
  317. package/src/providers/kimi.ts +52 -0
  318. package/src/providers/kiro-api-key.d.ts +50 -0
  319. package/src/providers/kiro-api-key.ts +786 -0
  320. package/src/providers/kiro-codewhisperer.d.ts +11 -0
  321. package/src/providers/kiro-codewhisperer.ts +600 -0
  322. package/src/providers/mock.ts +526 -0
  323. package/src/providers/ollama.d.ts +41 -0
  324. package/src/providers/ollama.ts +645 -0
  325. package/src/providers/openai-anthropic-shim.d.ts +31 -0
  326. package/src/providers/openai-anthropic-shim.ts +156 -0
  327. package/src/providers/openai-bounded-rate-limits.d.ts +3 -0
  328. package/src/providers/openai-bounded-rate-limits.ts +57 -0
  329. package/src/providers/openai-chat-server-schema.ts +254 -0
  330. package/src/providers/openai-chat-server.ts +724 -0
  331. package/src/providers/openai-codex/constants.d.ts +26 -0
  332. package/src/providers/openai-codex/constants.ts +43 -0
  333. package/src/providers/openai-codex/request-transformer.d.ts +50 -0
  334. package/src/providers/openai-codex/request-transformer.ts +219 -0
  335. package/src/providers/openai-codex/response-handler.d.ts +18 -0
  336. package/src/providers/openai-codex/response-handler.ts +111 -0
  337. package/src/providers/openai-codex-responses.d.ts +71 -0
  338. package/src/providers/openai-codex-responses.ts +3288 -0
  339. package/src/providers/openai-completions-compat.d.ts +6 -0
  340. package/src/providers/openai-completions-compat.ts +6 -0
  341. package/src/providers/openai-completions.d.ts +35 -0
  342. package/src/providers/openai-completions.ts +2294 -0
  343. package/src/providers/openai-opencodex-responses.ts +174 -0
  344. package/src/providers/openai-request-transform.d.ts +4 -0
  345. package/src/providers/openai-request-transform.ts +136 -0
  346. package/src/providers/openai-responses-server-schema.ts +290 -0
  347. package/src/providers/openai-responses-server.ts +1268 -0
  348. package/src/providers/openai-responses-shared.d.ts +106 -0
  349. package/src/providers/openai-responses-shared.ts +1253 -0
  350. package/src/providers/openai-responses.d.ts +37 -0
  351. package/src/providers/openai-responses.ts +989 -0
  352. package/src/providers/pi-native-client.d.ts +29 -0
  353. package/src/providers/pi-native-client.ts +243 -0
  354. package/src/providers/pi-native-server.ts +488 -0
  355. package/src/providers/register-builtins.d.ts +59 -0
  356. package/src/providers/register-builtins.ts +544 -0
  357. package/src/providers/synthetic.d.ts +26 -0
  358. package/src/providers/synthetic.ts +50 -0
  359. package/src/providers/transform-messages.d.ts +33 -0
  360. package/src/providers/transform-messages.ts +408 -0
  361. package/src/providers/vision-guard.d.ts +8 -0
  362. package/src/providers/vision-guard.ts +31 -0
  363. package/src/rate-limit-utils.d.ts +19 -0
  364. package/src/rate-limit-utils.ts +102 -0
  365. package/src/stream.d.ts +45 -0
  366. package/src/stream.ts +1306 -0
  367. package/src/types.d.ts +1073 -0
  368. package/src/types.ts +1305 -0
  369. package/src/usage/claude.ts +449 -0
  370. package/src/usage/gemini.ts +250 -0
  371. package/src/usage/github-copilot.ts +421 -0
  372. package/src/usage/google-antigravity.ts +201 -0
  373. package/src/usage/grok-cli.ts +259 -0
  374. package/src/usage/kimi.ts +285 -0
  375. package/src/usage/minimax-code.ts +31 -0
  376. package/src/usage/openai-codex.ts +503 -0
  377. package/src/usage/shared.ts +10 -0
  378. package/src/usage/zai.ts +247 -0
  379. package/src/usage.ts +190 -0
  380. package/src/utils/abort.d.ts +19 -0
  381. package/src/utils/abort.ts +51 -0
  382. package/src/utils/anthropic-auth.ts +95 -0
  383. package/src/utils/block-symbols.d.ts +6 -0
  384. package/src/utils/block-symbols.ts +11 -0
  385. package/src/utils/discovery/antigravity.ts +275 -0
  386. package/src/utils/discovery/codex.ts +362 -0
  387. package/src/utils/discovery/cursor.ts +388 -0
  388. package/src/utils/discovery/gemini.ts +248 -0
  389. package/src/utils/discovery/index.ts +4 -0
  390. package/src/utils/discovery/openai-compatible.ts +379 -0
  391. package/src/utils/event-stream.d.ts +41 -0
  392. package/src/utils/event-stream.ts +269 -0
  393. package/src/utils/fallback-transport.d.ts +110 -0
  394. package/src/utils/fallback-transport.ts +411 -0
  395. package/src/utils/fireworks-model-id.d.ts +10 -0
  396. package/src/utils/fireworks-model-id.ts +30 -0
  397. package/src/utils/foundry.d.ts +11 -0
  398. package/src/utils/foundry.ts +18 -0
  399. package/src/utils/h2-fetch.ts +60 -0
  400. package/src/utils/http-inspector.d.ts +59 -0
  401. package/src/utils/http-inspector.ts +380 -0
  402. package/src/utils/idle-iterator.d.ts +122 -0
  403. package/src/utils/idle-iterator.ts +410 -0
  404. package/src/utils/json-parse.d.ts +98 -0
  405. package/src/utils/json-parse.ts +607 -0
  406. package/src/utils/oauth/alibaba-token-plan.ts +60 -0
  407. package/src/utils/oauth/anthropic.ts +233 -0
  408. package/src/utils/oauth/api-key-login.ts +98 -0
  409. package/src/utils/oauth/api-key-validation.ts +344 -0
  410. package/src/utils/oauth/bizrouter.ts +15 -0
  411. package/src/utils/oauth/callback-server.d.ts +80 -0
  412. package/src/utils/oauth/callback-server.ts +359 -0
  413. package/src/utils/oauth/cerebras.ts +16 -0
  414. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  415. package/src/utils/oauth/commandcode.ts +17 -0
  416. package/src/utils/oauth/cursor.ts +157 -0
  417. package/src/utils/oauth/deepinfra.ts +15 -0
  418. package/src/utils/oauth/deepseek.ts +53 -0
  419. package/src/utils/oauth/firepass.ts +24 -0
  420. package/src/utils/oauth/fireworks.ts +15 -0
  421. package/src/utils/oauth/fugu.ts +15 -0
  422. package/src/utils/oauth/github-copilot.d.ts +38 -0
  423. package/src/utils/oauth/github-copilot.ts +362 -0
  424. package/src/utils/oauth/gitlab-duo.ts +123 -0
  425. package/src/utils/oauth/glm-zcode.d.ts +71 -0
  426. package/src/utils/oauth/glm-zcode.ts +433 -0
  427. package/src/utils/oauth/google-antigravity.ts +200 -0
  428. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  429. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  430. package/src/utils/oauth/huggingface.ts +62 -0
  431. package/src/utils/oauth/index.ts +558 -0
  432. package/src/utils/oauth/kagi.ts +47 -0
  433. package/src/utils/oauth/kilo.ts +87 -0
  434. package/src/utils/oauth/kimi.d.ts +17 -0
  435. package/src/utils/oauth/kimi.ts +275 -0
  436. package/src/utils/oauth/kiro.ts +448 -0
  437. package/src/utils/oauth/litellm.ts +47 -0
  438. package/src/utils/oauth/lm-studio.ts +38 -0
  439. package/src/utils/oauth/mara.ts +16 -0
  440. package/src/utils/oauth/minimax-code.ts +78 -0
  441. package/src/utils/oauth/moonshot.ts +16 -0
  442. package/src/utils/oauth/nanogpt.ts +15 -0
  443. package/src/utils/oauth/nvidia.ts +70 -0
  444. package/src/utils/oauth/oauth.html +199 -0
  445. package/src/utils/oauth/ollama-cloud.ts +28 -0
  446. package/src/utils/oauth/ollama.ts +47 -0
  447. package/src/utils/oauth/openai-codex.ts +299 -0
  448. package/src/utils/oauth/opencode.ts +49 -0
  449. package/src/utils/oauth/opengateway.ts +15 -0
  450. package/src/utils/oauth/openrouter.ts +16 -0
  451. package/src/utils/oauth/parallel.ts +46 -0
  452. package/src/utils/oauth/perplexity.ts +225 -0
  453. package/src/utils/oauth/pkce.ts +18 -0
  454. package/src/utils/oauth/qianfan.ts +58 -0
  455. package/src/utils/oauth/qwen-portal.ts +60 -0
  456. package/src/utils/oauth/sglang.ts +42 -0
  457. package/src/utils/oauth/synthetic.ts +15 -0
  458. package/src/utils/oauth/tavily.ts +46 -0
  459. package/src/utils/oauth/together.ts +16 -0
  460. package/src/utils/oauth/types.d.ts +56 -0
  461. package/src/utils/oauth/types.ts +122 -0
  462. package/src/utils/oauth/venice.ts +59 -0
  463. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  464. package/src/utils/oauth/vllm.ts +42 -0
  465. package/src/utils/oauth/xai.ts +246 -0
  466. package/src/utils/oauth/xiaomi.ts +199 -0
  467. package/src/utils/oauth/zai.ts +60 -0
  468. package/src/utils/oauth/zenmux.ts +15 -0
  469. package/src/utils/overflow.ts +275 -0
  470. package/src/utils/parse-bind.ts +81 -0
  471. package/src/utils/provider-response.d.ts +6 -0
  472. package/src/utils/provider-response.ts +30 -0
  473. package/src/utils/provider-safety-stop.ts +8 -0
  474. package/src/utils/proxy.d.ts +7 -0
  475. package/src/utils/proxy.ts +652 -0
  476. package/src/utils/retry-after.d.ts +3 -0
  477. package/src/utils/retry-after.ts +110 -0
  478. package/src/utils/retry-budget.d.ts +1 -0
  479. package/src/utils/retry-budget.ts +4 -0
  480. package/src/utils/retry.d.ts +29 -0
  481. package/src/utils/retry.ts +67 -0
  482. package/src/utils/schema/CONSTRAINTS.md +164 -0
  483. package/src/utils/schema/adapt.d.ts +24 -0
  484. package/src/utils/schema/adapt.ts +36 -0
  485. package/src/utils/schema/compatibility.d.ts +30 -0
  486. package/src/utils/schema/compatibility.ts +435 -0
  487. package/src/utils/schema/dereference.d.ts +11 -0
  488. package/src/utils/schema/dereference.ts +98 -0
  489. package/src/utils/schema/draft.d.ts +10 -0
  490. package/src/utils/schema/draft.ts +341 -0
  491. package/src/utils/schema/equality.d.ts +4 -0
  492. package/src/utils/schema/equality.ts +97 -0
  493. package/src/utils/schema/fields.d.ts +49 -0
  494. package/src/utils/schema/fields.ts +190 -0
  495. package/src/utils/schema/index.d.ts +14 -0
  496. package/src/utils/schema/index.ts +14 -0
  497. package/src/utils/schema/json-schema-validator.d.ts +12 -0
  498. package/src/utils/schema/json-schema-validator.ts +577 -0
  499. package/src/utils/schema/meta-validator.d.ts +2 -0
  500. package/src/utils/schema/meta-validator.ts +167 -0
  501. package/src/utils/schema/normalize.d.ts +93 -0
  502. package/src/utils/schema/normalize.ts +1588 -0
  503. package/src/utils/schema/root-combinator.d.ts +12 -0
  504. package/src/utils/schema/root-combinator.ts +143 -0
  505. package/src/utils/schema/spill.d.ts +8 -0
  506. package/src/utils/schema/spill.ts +43 -0
  507. package/src/utils/schema/stamps.d.ts +25 -0
  508. package/src/utils/schema/stamps.ts +97 -0
  509. package/src/utils/schema/types.d.ts +4 -0
  510. package/src/utils/schema/types.ts +11 -0
  511. package/src/utils/schema/wire.d.ts +54 -0
  512. package/src/utils/schema/wire.ts +213 -0
  513. package/src/utils/schema/zod-decontaminate.d.ts +31 -0
  514. package/src/utils/schema/zod-decontaminate.ts +331 -0
  515. package/src/utils/sse-debug.d.ts +10 -0
  516. package/src/utils/sse-debug.ts +289 -0
  517. package/src/utils/tool-call-healing.d.ts +80 -0
  518. package/src/utils/tool-call-healing.ts +298 -0
  519. package/src/utils/tool-choice-capability.d.ts +57 -0
  520. package/src/utils/tool-choice-capability.ts +633 -0
  521. package/src/utils/tool-choice.d.ts +50 -0
  522. package/src/utils/tool-choice.ts +99 -0
  523. package/src/utils/validation.ts +1080 -0
  524. package/src/utils.d.ts +117 -0
  525. package/src/utils.ts +523 -0
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Anthropic OAuth flow (Anthropic model Pro/Max)
3
+ */
4
+ import { OAuthCallbackFlow, type OAuthCallbackFlowOptions } from "./callback-server";
5
+ import { generatePKCE } from "./pkce";
6
+ import type { OAuthController, OAuthCredentials } from "./types";
7
+
8
+ const decode = (s: string) => atob(s);
9
+ const CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
10
+ const AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
11
+ const TOKEN_URL = "https://api.anthropic.com/v1/oauth/token";
12
+ const CALLBACK_PORT = 54545;
13
+ const CALLBACK_PATH = "/callback";
14
+ /**
15
+ * Redirect target for the paste-a-code login. Anthropic renders the
16
+ * authorization code on this page instead of redirecting into this machine, so
17
+ * a vib running over SSH, in a container, or on a headless box can be paired
18
+ * from a browser that has no route back to `localhost:54545`.
19
+ *
20
+ * Deliberately a hard-coded constant rather than an env/config override: this
21
+ * is where the authorization code is delivered, so making it injectable would
22
+ * turn any writable environment into an auth-code exfiltration channel.
23
+ */
24
+ export const ANTHROPIC_MANUAL_REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
25
+ const SCOPES = "org:create_api_key user:profile user:inference";
26
+
27
+ function formatErrorDetails(error: unknown): string {
28
+ if (error instanceof Error) {
29
+ const details: string[] = [`${error.name}: ${error.message}`];
30
+ const errorWithCode = error as Error & { code?: string; errno?: number | string; cause?: unknown };
31
+ if (errorWithCode.code) details.push(`code=${errorWithCode.code}`);
32
+ if (typeof errorWithCode.errno !== "undefined") details.push(`errno=${String(errorWithCode.errno)}`);
33
+ if (typeof error.cause !== "undefined") {
34
+ details.push(`cause=${formatErrorDetails(error.cause)}`);
35
+ }
36
+ if (error.stack) {
37
+ details.push(`stack=${error.stack}`);
38
+ }
39
+ return details.join("; ");
40
+ }
41
+ return String(error);
42
+ }
43
+
44
+ async function postJson(url: string, body: Record<string, string | number>): Promise<string> {
45
+ const response = await fetch(url, {
46
+ method: "POST",
47
+ headers: {
48
+ "Content-Type": "application/json",
49
+ Accept: "application/json",
50
+ },
51
+ body: JSON.stringify(body),
52
+ signal: AbortSignal.timeout(30_000),
53
+ });
54
+
55
+ const responseBody = await response.text();
56
+ if (!response.ok) {
57
+ throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
58
+ }
59
+ return responseBody;
60
+ }
61
+
62
+ /**
63
+ * Decoded shape of Anthropic's `/v1/oauth/token` response (both
64
+ * `authorization_code` exchange and `refresh_token` refresh return the same
65
+ * envelope). The `account` block is inlined alongside the tokens, so we can
66
+ * surface `accountId` / `email` on {@link OAuthCredentials} without a separate
67
+ * `/api/oauth/profile` round-trip.
68
+ */
69
+ interface AnthropicTokenResponse {
70
+ access_token: string;
71
+ refresh_token: string;
72
+ expires_in: number;
73
+ account?: { uuid?: string; email_address?: string };
74
+ }
75
+
76
+ function parseOAuthTokenResponse(responseBody: string, operation: string): AnthropicTokenResponse {
77
+ try {
78
+ return JSON.parse(responseBody) as AnthropicTokenResponse;
79
+ } catch (error) {
80
+ throw new Error(
81
+ `Anthropic ${operation} returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,
82
+ );
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Lift the OAuth response's `account: { uuid, email_address }` block onto
88
+ * {@link OAuthCredentials} so downstream identity propagation (e.g.
89
+ * `metadata.user_id.account_uuid`, usage tracking) works without a separate
90
+ * `/api/oauth/profile` round-trip. Returns `undefined` for either field when
91
+ * the response omits it or carries a non-string / empty value.
92
+ */
93
+ function extractAccountFromTokenResponse(data: AnthropicTokenResponse): {
94
+ accountId?: string;
95
+ email?: string;
96
+ } {
97
+ const accountUuid = data.account?.uuid;
98
+ const emailAddress = data.account?.email_address;
99
+ return {
100
+ accountId: typeof accountUuid === "string" && accountUuid.length > 0 ? accountUuid : undefined,
101
+ email: typeof emailAddress === "string" && emailAddress.length > 0 ? emailAddress : undefined,
102
+ };
103
+ }
104
+
105
+ export interface AnthropicOAuthFlowOptions {
106
+ /**
107
+ * Pair by pasting the code Anthropic displays instead of waiting on a local
108
+ * `localhost:54545` callback. Use when the browser completing the login has
109
+ * no network route back to the machine running vib.
110
+ */
111
+ manualCode?: boolean;
112
+ }
113
+
114
+ export class AnthropicOAuthFlow extends OAuthCallbackFlow {
115
+ #verifier: string = "";
116
+ #challenge: string = "";
117
+ readonly #manualCode: boolean;
118
+
119
+ constructor(ctrl: OAuthController, options: AnthropicOAuthFlowOptions = {}) {
120
+ const manualCode = options.manualCode === true;
121
+ const flowOptions: OAuthCallbackFlowOptions = {
122
+ preferredPort: CALLBACK_PORT,
123
+ callbackPath: CALLBACK_PATH,
124
+ redirectUri: manualCode ? ANTHROPIC_MANUAL_REDIRECT_URI : undefined,
125
+ skipCallbackServer: manualCode,
126
+ };
127
+ super(ctrl, flowOptions);
128
+ this.#manualCode = manualCode;
129
+ }
130
+
131
+ async generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }> {
132
+ const pkce = await generatePKCE();
133
+ this.#verifier = pkce.verifier;
134
+ this.#challenge = pkce.challenge;
135
+
136
+ const authParams = new URLSearchParams({
137
+ code: "true",
138
+ client_id: CLIENT_ID,
139
+ response_type: "code",
140
+ redirect_uri: redirectUri,
141
+ scope: SCOPES,
142
+ code_challenge: this.#challenge,
143
+ code_challenge_method: "S256",
144
+ state,
145
+ });
146
+ const url = `${AUTHORIZE_URL}?${authParams.toString()}`;
147
+
148
+ return {
149
+ url,
150
+ instructions: this.#manualCode
151
+ ? "Complete login in your browser. Anthropic will show an authorization code — paste it here."
152
+ : "Complete login in your browser. If the browser cannot reach this machine, paste the final redirect URL or authorization code when prompted. To pair by code instead, cancel this login and run /login anthropic --manual.",
153
+ };
154
+ }
155
+
156
+ async exchangeToken(code: string, state: string, redirectUri: string): Promise<OAuthCredentials> {
157
+ let exchangeCode = code;
158
+ let exchangeState = state;
159
+ const codeFragmentIndex = code.indexOf("#");
160
+ if (codeFragmentIndex >= 0) {
161
+ exchangeCode = code.slice(0, codeFragmentIndex);
162
+ const codeFragmentState = code.slice(codeFragmentIndex + 1);
163
+ if (codeFragmentState.length > 0) {
164
+ exchangeState = codeFragmentState;
165
+ }
166
+ }
167
+
168
+ let responseBody: string;
169
+ try {
170
+ responseBody = await postJson(TOKEN_URL, {
171
+ grant_type: "authorization_code",
172
+ client_id: CLIENT_ID,
173
+ code: exchangeCode,
174
+ state: exchangeState,
175
+ redirect_uri: redirectUri,
176
+ code_verifier: this.#verifier,
177
+ });
178
+ } catch (error) {
179
+ throw new Error(
180
+ `Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`,
181
+ );
182
+ }
183
+
184
+ const tokenData = parseOAuthTokenResponse(responseBody, "token exchange");
185
+ const { accountId, email } = extractAccountFromTokenResponse(tokenData);
186
+
187
+ return {
188
+ refresh: tokenData.refresh_token,
189
+ access: tokenData.access_token,
190
+ expires: Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000,
191
+ accountId,
192
+ email,
193
+ };
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Login with Anthropic OAuth
199
+ */
200
+ export async function loginAnthropic(
201
+ ctrl: OAuthController,
202
+ options: AnthropicOAuthFlowOptions = {},
203
+ ): Promise<OAuthCredentials> {
204
+ const flow = new AnthropicOAuthFlow(ctrl, options);
205
+ return flow.login();
206
+ }
207
+
208
+ /**
209
+ * Refresh Anthropic OAuth token
210
+ */
211
+ export async function refreshAnthropicToken(refreshToken: string): Promise<OAuthCredentials> {
212
+ let responseBody: string;
213
+ try {
214
+ responseBody = await postJson(TOKEN_URL, {
215
+ grant_type: "refresh_token",
216
+ client_id: CLIENT_ID,
217
+ refresh_token: refreshToken,
218
+ });
219
+ } catch (error) {
220
+ throw new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`);
221
+ }
222
+
223
+ const data = parseOAuthTokenResponse(responseBody, "token refresh");
224
+ const { accountId, email } = extractAccountFromTokenResponse(data);
225
+
226
+ return {
227
+ refresh: data.refresh_token || refreshToken,
228
+ access: data.access_token,
229
+ expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,
230
+ accountId,
231
+ email,
232
+ };
233
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Shared factory for API-key-paste "login" flows.
3
+ *
4
+ * Several providers (Cerebras, Synthetic, Moonshot, Together, NanoGPT, ZenMux,
5
+ * Command Code GOAT)
6
+ * don't actually implement OAuth — they just ask the user to paste an API key,
7
+ * optionally validate it, and return the trimmed key.
8
+ */
9
+
10
+ import { validateApiKeyAgainstModelsEndpoint, validateOpenAICompatibleApiKey } from "./api-key-validation";
11
+ import type { OAuthController } from "./types";
12
+
13
+ type ChatCompletionsValidation = {
14
+ kind: "chat-completions";
15
+ provider: string;
16
+ baseUrl: string;
17
+ model: string;
18
+ requireInferenceResponse?: boolean;
19
+ };
20
+
21
+ type ModelsEndpointValidation = {
22
+ kind: "models-endpoint";
23
+ provider: string;
24
+ modelsUrl: string;
25
+ };
26
+
27
+ export type ApiKeyLoginConfig = {
28
+ /** Display name used in error messages, e.g. "Cerebras", "NanoGPT". */
29
+ providerLabel: string;
30
+ /** URL opened in browser for the user to grab their key. */
31
+ authUrl: string;
32
+ /** Instructions shown with the onAuth callback. */
33
+ instructions: string;
34
+ /** Prompt message shown when asking for the key paste. */
35
+ promptMessage: string;
36
+ /** Placeholder string for the prompt (e.g. "sk-...", "csk-..."). */
37
+ placeholder: string;
38
+ /** Validation strategy, or `null` to skip validation. */
39
+ validation: ChatCompletionsValidation | ModelsEndpointValidation | null;
40
+ validationProgressMessage?: string;
41
+ };
42
+
43
+ export function createApiKeyLogin(config: ApiKeyLoginConfig): (options: OAuthController) => Promise<string> {
44
+ return async function login(options: OAuthController): Promise<string> {
45
+ if (options.signal?.aborted) throw new Error("Login cancelled");
46
+ if (!options.onPrompt) {
47
+ throw new Error(`${config.providerLabel} login requires onPrompt callback`);
48
+ }
49
+
50
+ options.onAuth?.({
51
+ url: config.authUrl,
52
+ instructions: config.instructions,
53
+ });
54
+
55
+ const apiKey = await options.onPrompt({
56
+ message: config.promptMessage,
57
+ placeholder: config.placeholder,
58
+ });
59
+
60
+ if (options.signal?.aborted) {
61
+ throw new Error("Login cancelled");
62
+ }
63
+
64
+ const trimmed = apiKey.trim();
65
+ if (!trimmed) {
66
+ throw new Error("API key is required");
67
+ }
68
+ if (/[\x00-\x1f\x7f-\x9f]/u.test(trimmed)) {
69
+ throw new Error("API key contains unsupported control characters");
70
+ }
71
+
72
+ if (config.validation) {
73
+ options.onProgress?.(config.validationProgressMessage ?? "Validating API key...");
74
+ if (config.validation.kind === "chat-completions") {
75
+ await validateOpenAICompatibleApiKey({
76
+ provider: config.validation.provider,
77
+ apiKey: trimmed,
78
+ baseUrl: config.validation.baseUrl,
79
+ model: config.validation.model,
80
+ requireInferenceResponse: config.validation.requireInferenceResponse,
81
+ signal: options.signal,
82
+ fetch: options.fetch,
83
+ });
84
+ } else {
85
+ await validateApiKeyAgainstModelsEndpoint({
86
+ provider: config.validation.provider,
87
+ apiKey: trimmed,
88
+ modelsUrl: config.validation.modelsUrl,
89
+ signal: options.signal,
90
+ fetch: options.fetch,
91
+ });
92
+ }
93
+ if (options.signal?.aborted) throw new Error("Login cancelled");
94
+ }
95
+
96
+ return trimmed;
97
+ };
98
+ }
@@ -0,0 +1,344 @@
1
+ import { sanitizeDisplayLine } from "@vib-rato/utils";
2
+
3
+ type OpenAICompatibleValidationOptions = {
4
+ provider: string;
5
+ apiKey: string;
6
+ baseUrl: string;
7
+ model: string;
8
+ signal?: AbortSignal;
9
+ fetch?: typeof globalThis.fetch;
10
+ requireInferenceResponse?: boolean;
11
+ timeoutMs?: number;
12
+ };
13
+
14
+ type ModelListValidationOptions = {
15
+ provider: string;
16
+ apiKey: string;
17
+ modelsUrl: string;
18
+ signal?: AbortSignal;
19
+ fetch?: typeof globalThis.fetch;
20
+ timeoutMs?: number;
21
+ };
22
+
23
+ const VALIDATION_TIMEOUT_MS = 15_000;
24
+
25
+ /** Most characters of an upstream body echoed into a validation error. */
26
+ const VALIDATION_DETAILS_LIMIT = 200;
27
+ const VALIDATION_BODY_LIMIT = 64 * 1024;
28
+
29
+ function redactSecrets(text: string, apiKey: string): string {
30
+ let safe = text.replace(/\u001b\[[0-?]*[ -/]*[@-~]/gu, "");
31
+ if (apiKey) {
32
+ const escaped = [...apiKey].map(char => char.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&"));
33
+ const interspersed = new RegExp(escaped.join("[\\s\\x00-\\x1f\\x7f-\\x9f]*"), "gu");
34
+ safe = safe.replace(interspersed, "[REDACTED]");
35
+ }
36
+ safe = sanitizeDisplayLine(safe);
37
+ safe = safe.replace(/[\x00-\x1f\x7f-\x9f]/gu, " ");
38
+ if (apiKey) safe = safe.replaceAll(apiKey, "[REDACTED]");
39
+ // Upstream errors sometimes echo credentials under a field name instead of
40
+ // returning the exact bearer value. Redact those values before retaining any
41
+ // bounded diagnostic snippet.
42
+ safe = safe
43
+ .replace(/(Bearer\s+)([^\s,}"']+)/giu, "$1[REDACTED]")
44
+ .replace(
45
+ /(["']?(?:authorization|proxy-authorization|api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|token|secret|password)["']?\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,}\]]+)/giu,
46
+ "$1[REDACTED]",
47
+ );
48
+ return safe;
49
+ }
50
+
51
+ function boundedDetails(text: string, apiKey: string): string {
52
+ const trimmed = redactSecrets(text, apiKey).trim();
53
+ return trimmed.length > VALIDATION_DETAILS_LIMIT ? `${trimmed.slice(0, VALIDATION_DETAILS_LIMIT)}…` : trimmed;
54
+ }
55
+
56
+ type BoundedBody = { text: string; truncated: boolean };
57
+
58
+ async function readBoundedBody(response: Response, signal: AbortSignal): Promise<BoundedBody> {
59
+ if (signal.aborted) throw new Error("Login cancelled");
60
+ const contentLength = Number(response.headers.get("content-length"));
61
+ if (Number.isFinite(contentLength) && contentLength > VALIDATION_BODY_LIMIT) {
62
+ await response.body?.cancel().catch(() => {});
63
+ return { text: "", truncated: true };
64
+ }
65
+ if (!response.body) return { text: "", truncated: false };
66
+ const reader = response.body.getReader();
67
+ const chunks: Uint8Array[] = [];
68
+ let total = 0;
69
+ let truncated = false;
70
+ try {
71
+ while (!truncated && total <= VALIDATION_BODY_LIMIT) {
72
+ if (signal.aborted) throw new Error("Login cancelled");
73
+ const { promise, resolve, reject } = Promise.withResolvers<{ done: boolean; value?: Uint8Array }>();
74
+ const onAbort = () => reject(new Error("Login cancelled"));
75
+ signal.addEventListener("abort", onAbort, { once: true });
76
+ reader
77
+ .read()
78
+ .then(value => resolve(value), reject)
79
+ .finally(() => signal.removeEventListener("abort", onAbort));
80
+ const { done, value } = await promise;
81
+ if (done) break;
82
+ if (value) {
83
+ const remaining = VALIDATION_BODY_LIMIT - total;
84
+ if (value.byteLength > remaining) truncated = true;
85
+ chunks.push(value.byteLength > remaining ? value.subarray(0, remaining) : value);
86
+ total += Math.min(value.byteLength, remaining);
87
+ }
88
+ }
89
+ } finally {
90
+ await reader.cancel().catch(() => {});
91
+ }
92
+ const bytes = new Uint8Array(Math.min(total, VALIDATION_BODY_LIMIT));
93
+ let offset = 0;
94
+ for (const chunk of chunks) {
95
+ const take = Math.min(chunk.byteLength, bytes.length - offset);
96
+ if (take <= 0) break;
97
+ bytes.set(chunk.subarray(0, take), offset);
98
+ offset += take;
99
+ }
100
+ return { text: new TextDecoder().decode(bytes), truncated };
101
+ }
102
+
103
+ function abortFailure(
104
+ provider: string,
105
+ apiKey: string,
106
+ callerSignal: AbortSignal | undefined,
107
+ timeoutSignal: AbortSignal,
108
+ ): Error | undefined {
109
+ if (callerSignal?.aborted) return new Error("Login cancelled");
110
+ if (timeoutSignal.aborted) return validationFailure(provider, apiKey, "validation request timed out");
111
+ }
112
+
113
+ function errorDetails(error: unknown, apiKey: string): string {
114
+ return boundedDetails(error instanceof Error ? error.message : String(error), apiKey);
115
+ }
116
+
117
+ function validationFailure(provider: string, apiKey: string, suffix: string): Error {
118
+ const details = boundedDetails(suffix, apiKey);
119
+ return new Error(
120
+ details ? `${provider} API key validation failed: ${details}` : `${provider} API key validation failed`,
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Validate an API key against an OpenAI-compatible chat completions endpoint.
126
+ *
127
+ * Performs a minimal request to verify credentials and endpoint access.
128
+ */
129
+ export async function validateOpenAICompatibleApiKey(options: OpenAICompatibleValidationOptions): Promise<void> {
130
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? VALIDATION_TIMEOUT_MS);
131
+ const signal = options.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal;
132
+ const fetchImpl = options.fetch ?? globalThis.fetch;
133
+ if (signal.aborted) throw new Error("Login cancelled");
134
+ if (/[\x00-\x1f\x7f-\x9f]/u.test(options.apiKey))
135
+ throw new Error(`${options.provider} API key contains unsupported control characters`);
136
+
137
+ let response: Response;
138
+ try {
139
+ response = await fetchImpl(`${options.baseUrl}/chat/completions`, {
140
+ method: "POST",
141
+ headers: {
142
+ "Content-Type": "application/json",
143
+ Authorization: `Bearer ${options.apiKey}`,
144
+ },
145
+ body: JSON.stringify({
146
+ model: options.model,
147
+ messages: [{ role: "user", content: "ping" }],
148
+ max_tokens: 1,
149
+ temperature: 0,
150
+ }),
151
+ signal,
152
+ });
153
+ } catch (error) {
154
+ const abortError = abortFailure(options.provider, options.apiKey, options.signal, timeoutSignal);
155
+ if (abortError) throw abortError;
156
+ throw validationFailure(
157
+ options.provider,
158
+ options.apiKey,
159
+ `request failed (${errorDetails(error, options.apiKey)})`,
160
+ );
161
+ }
162
+
163
+ if (response.ok) {
164
+ if (options.requireInferenceResponse) {
165
+ let body: string;
166
+ try {
167
+ const bounded = await readBoundedBody(response, signal);
168
+ if (bounded.truncated) throw new Error("inference probe response exceeded validation limit");
169
+ body = bounded.text;
170
+ } catch (error) {
171
+ const abortError = abortFailure(options.provider, options.apiKey, options.signal, timeoutSignal);
172
+ if (abortError) throw abortError;
173
+ if (error instanceof Error && error.message === "Login cancelled") throw error;
174
+ if (error instanceof Error && error.message === "inference probe response exceeded validation limit")
175
+ throw validationFailure(options.provider, options.apiKey, error.message);
176
+ throw validationFailure(
177
+ options.provider,
178
+ options.apiKey,
179
+ `the inference probe response could not be read (${errorDetails(error, options.apiKey)})`,
180
+ );
181
+ }
182
+ let parsed: unknown;
183
+ try {
184
+ parsed = JSON.parse(body);
185
+ } catch {
186
+ throw validationFailure(
187
+ options.provider,
188
+ options.apiKey,
189
+ `the inference probe returned a non-JSON response (${boundedDetails(body, options.apiKey)})`,
190
+ );
191
+ }
192
+ const choices =
193
+ typeof parsed === "object" && parsed !== null && "choices" in parsed
194
+ ? (parsed as { choices?: unknown }).choices
195
+ : undefined;
196
+ const firstChoice = Array.isArray(choices) ? choices[0] : undefined;
197
+ const message =
198
+ typeof firstChoice === "object" &&
199
+ firstChoice !== null &&
200
+ "message" in firstChoice &&
201
+ typeof (firstChoice as { message?: unknown }).message === "object" &&
202
+ !Array.isArray((firstChoice as { message?: unknown }).message)
203
+ ? (firstChoice as { message: Record<string, unknown> }).message
204
+ : undefined;
205
+ const content = message?.content;
206
+ if (!(typeof content === "string" && content.trim().length > 0)) {
207
+ throw validationFailure(options.provider, options.apiKey, "the inference probe returned no choices");
208
+ }
209
+ }
210
+ return;
211
+ }
212
+
213
+ let details = "";
214
+ try {
215
+ const bounded = await readBoundedBody(response, signal);
216
+ details = bounded.truncated
217
+ ? "response body exceeded validation limit"
218
+ : boundedDetails(bounded.text, options.apiKey);
219
+ } catch {
220
+ const abortError = abortFailure(options.provider, options.apiKey, options.signal, timeoutSignal);
221
+ if (abortError) throw abortError;
222
+ // ignore body parse errors, status is enough
223
+ }
224
+
225
+ const message = details
226
+ ? `${options.provider} API key validation failed (${response.status}): ${details}`
227
+ : `${options.provider} API key validation failed (${response.status})`;
228
+ throw new Error(message);
229
+ }
230
+ /**
231
+ * Whether a 200 body is a recognizable model list. OpenAI-compatible endpoints
232
+ * return `{"object":"list","data":[...]}`; some gateways answer with a bare
233
+ * array or `{"models":[...]}`. Anything else — including valid JSON without a
234
+ * list — is not evidence that the credential reached a models endpoint.
235
+ */
236
+ function isModelList(parsed: unknown): boolean {
237
+ const isModelArray = (value: unknown): boolean => {
238
+ if (!Array.isArray(value)) return false;
239
+ if (value.length === 0) return true;
240
+ return value.some(
241
+ item =>
242
+ typeof item === "object" &&
243
+ item !== null &&
244
+ typeof (item as { id?: unknown }).id === "string" &&
245
+ (item as { id: string }).id.trim().length > 0,
246
+ );
247
+ };
248
+ if (Array.isArray(parsed)) return isModelArray(parsed);
249
+ if (typeof parsed !== "object" || parsed === null) return false;
250
+ const record = parsed as { data?: unknown; models?: unknown };
251
+ return isModelArray(record.data) || isModelArray(record.models);
252
+ }
253
+
254
+ /**
255
+ * Validate a provider models endpoint's reachability and response shape.
256
+ *
257
+ * Useful for providers where access to specific models may vary by plan and
258
+ * should not block login; an available model list is not proof that an
259
+ * authenticated inference request will succeed for the supplied key.
260
+ *
261
+ * A 200 status alone is NOT accepted: a captive portal, misrouting proxy, or
262
+ * broken gateway can answer 200 with an HTML page or an empty JSON object.
263
+ * The body must parse as JSON and carry a recognizable model list before the
264
+ * endpoint is considered reachable. This catalog check is not proof that
265
+ * authenticated inference is entitled to use the supplied key.
266
+ */
267
+ export async function validateApiKeyAgainstModelsEndpoint(options: ModelListValidationOptions): Promise<void> {
268
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? VALIDATION_TIMEOUT_MS);
269
+ const signal = options.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal;
270
+ const fetchImpl = options.fetch ?? globalThis.fetch;
271
+ if (signal.aborted) throw new Error("Login cancelled");
272
+ if (/[\x00-\x1f\x7f-\x9f]/u.test(options.apiKey))
273
+ throw new Error(`${options.provider} API key contains unsupported control characters`);
274
+
275
+ let response: Response;
276
+ try {
277
+ response = await fetchImpl(options.modelsUrl, {
278
+ method: "GET",
279
+ headers: {
280
+ Authorization: `Bearer ${options.apiKey}`,
281
+ },
282
+ signal,
283
+ });
284
+ } catch (error) {
285
+ const abortError = abortFailure(options.provider, options.apiKey, options.signal, timeoutSignal);
286
+ if (abortError) throw abortError;
287
+ throw validationFailure(
288
+ options.provider,
289
+ options.apiKey,
290
+ `request failed (${errorDetails(error, options.apiKey)})`,
291
+ );
292
+ }
293
+
294
+ if (response.ok) {
295
+ let body: string;
296
+ try {
297
+ const bounded = await readBoundedBody(response, signal);
298
+ if (bounded.truncated) throw new Error("response body exceeded validation limit");
299
+ body = bounded.text;
300
+ } catch (error) {
301
+ const abortError = abortFailure(options.provider, options.apiKey, options.signal, timeoutSignal);
302
+ if (abortError) throw abortError;
303
+ if (error instanceof Error && error.message === "Login cancelled") throw error;
304
+ throw validationFailure(
305
+ options.provider,
306
+ options.apiKey,
307
+ `the models endpoint response body could not be read (${errorDetails(error, options.apiKey)})`,
308
+ );
309
+ }
310
+ let parsed: unknown;
311
+ try {
312
+ parsed = JSON.parse(body);
313
+ } catch {
314
+ throw new Error(
315
+ `${options.provider} API key validation failed: the models endpoint returned ${response.status} with a non-JSON body` +
316
+ `${body.trim() ? ` (${boundedDetails(body, options.apiKey)})` : ""}. Refusing to accept the key on status alone.`,
317
+ );
318
+ }
319
+ if (!isModelList(parsed)) {
320
+ throw new Error(
321
+ `${options.provider} API key validation failed: the models endpoint returned ${response.status} without a recognizable ` +
322
+ `model list. Refusing to accept the key on status alone.`,
323
+ );
324
+ }
325
+ return;
326
+ }
327
+
328
+ let details = "";
329
+ try {
330
+ const bounded = await readBoundedBody(response, signal);
331
+ details = bounded.truncated
332
+ ? "response body exceeded validation limit"
333
+ : boundedDetails(bounded.text, options.apiKey);
334
+ } catch {
335
+ const abortError = abortFailure(options.provider, options.apiKey, options.signal, timeoutSignal);
336
+ if (abortError) throw abortError;
337
+ // ignore body parse errors, status is enough
338
+ }
339
+
340
+ const message = details
341
+ ? `${options.provider} API key validation failed (${response.status}): ${details}`
342
+ : `${options.provider} API key validation failed (${response.status})`;
343
+ throw new Error(message);
344
+ }
@@ -0,0 +1,15 @@
1
+ /** BizRouter login flow (API key paste, validated via /v1/models). */
2
+ import { createApiKeyLogin } from "./api-key-login";
3
+
4
+ export const loginBizRouter = createApiKeyLogin({
5
+ providerLabel: "BizRouter",
6
+ authUrl: "https://bizrouter.ai/settings/keys",
7
+ instructions: "Create or copy your BizRouter API key",
8
+ promptMessage: "Paste your BizRouter API key",
9
+ placeholder: "sk-br-v1-...",
10
+ validation: {
11
+ kind: "models-endpoint",
12
+ provider: "BizRouter",
13
+ modelsUrl: "https://api.bizrouter.ai/v1/models",
14
+ },
15
+ });