@aryee337/aery-ai 0.2.28 → 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,137 @@
1
+ import type { AssistantMessage } from "../types";
2
+
3
+ /**
4
+ * Regex patterns to detect context overflow errors from different providers.
5
+ *
6
+ * These patterns match error messages returned when the input exceeds
7
+ * the model's context window.
8
+ *
9
+ * Provider-specific patterns (with example error messages):
10
+ *
11
+ * - Anthropic: "prompt is too long: 213462 tokens > 200000 maximum"
12
+ * - OpenAI: "Your input exceeds the context window of this model"
13
+ * - Google: "The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)"
14
+ * - xAI: "This model's maximum prompt length is 131072 but the request contains 537812 tokens"
15
+ * - Groq: "Please reduce the length of the messages or completion"
16
+ * - OpenRouter: "This endpoint's maximum context length is X tokens. However, you requested about Y tokens"
17
+ * - llama.cpp: "the request exceeds the available context size, try increasing it"
18
+ * - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
19
+ * - GitHub Copilot: "prompt token count of X exceeds the limit of Y"
20
+ * - MiniMax: "invalid params, context window exceeds limit"
21
+ * - Kimi For Coding: "Your request exceeded model token limit: X (requested: Y)"
22
+ * - Anthropic 413: "request_too_large" / "Request exceeds the maximum size" (payload too large)
23
+ * - HTTP 413 variants: "Payload Too Large" / "Request Entity Too Large"
24
+ * - z.ai / GLM: Returns finish_reason: "model_context_window_exceeded" mapped to error message
25
+ * - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow
26
+ * - Ollama: Silently truncates input - not detectable via error message
27
+ */
28
+ const OVERFLOW_PATTERNS = [
29
+ /prompt is too long/i, // Anthropic
30
+ /input is too long for requested model/i, // Amazon Bedrock
31
+ /exceeds the context window/i, // OpenAI (Completions & Responses API)
32
+ /input token count.*exceeds the maximum/i, // Google (Gemini)
33
+ /maximum prompt length is \d+/i, // xAI (Grok)
34
+ /reduce the length of the messages/i, // Groq
35
+ /maximum context length is \d+ tokens/i, // OpenRouter (all backends)
36
+ /exceeds the limit of \d+/i, // GitHub Copilot
37
+ /exceeds the available context size/i, // llama.cpp server
38
+ /requested tokens?.*exceed.*context (window|length|size)/i, // llama.cpp / OpenAI-compatible local servers
39
+ /context (window|length|size).*(exceeded|overflow|too small)/i, // Generic local server variants
40
+ /(prompt|input).*(too long|too large).*(context|n_ctx)/i, // llama.cpp phrasing variants
41
+ /requested tokens?.*(exceeds?|greater than).*(n_ctx|context)/i, // llama.cpp n_ctx variants
42
+ /greater than the context length/i, // LM Studio
43
+ /context window exceeds limit/i, // MiniMax
44
+ /exceeded model token limit/i, // Kimi For Coding
45
+ /context[_ ]length[_ ]exceeded/i, // Generic fallback
46
+ /too many tokens/i, // Generic fallback
47
+ /token limit exceeded/i, // Generic fallback
48
+ /request_too_large/i, // Anthropic 413 (request body too large)
49
+ /request exceeds the maximum size/i, // Anthropic 413 variant
50
+ /payload too large/i, // Generic HTTP 413 variant
51
+ /entity too large/i, // Generic HTTP 413 variant
52
+ /\b413\b.*\b(request|payload|entity)\b.*\btoo large\b/i, // "413 Request Entity Too Large" variants
53
+ /model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text
54
+ ];
55
+ /**
56
+ * Check if an assistant message represents a context overflow error.
57
+ *
58
+ * This handles two cases:
59
+ * 1. Error-based overflow: Most providers return stopReason "error" with a
60
+ * specific error message pattern.
61
+ * 2. Silent overflow: Some providers accept overflow requests and return
62
+ * successfully. For these, we check if usage.input exceeds the context window.
63
+ *
64
+ * ## Reliability by Provider
65
+ *
66
+ * **Reliable detection (returns error with detectable message):**
67
+ * - Anthropic: "prompt is too long: X tokens > Y maximum"
68
+ * - OpenAI (Completions & Responses): "exceeds the context window"
69
+ * - Google Gemini: "input token count exceeds the maximum"
70
+ * - xAI (Grok): "maximum prompt length is X but request contains Y"
71
+ * - Groq: "reduce the length of the messages"
72
+ * - Cerebras: 400/413 status code (no body)
73
+ * - Mistral: 400/413 status code (no body)
74
+ * - HTTP 413 payload/entity-too-large variants
75
+ * - OpenRouter (all backends): "maximum context length is X tokens"
76
+ * - llama.cpp: "exceeds the available context size"
77
+ * - LM Studio: "greater than the context length"
78
+ * - Kimi For Coding: "exceeded model token limit: X (requested: Y)"
79
+ * - Anthropic 413: "request_too_large" (request body exceeds size limit)
80
+ * - HTTP 413: "Payload Too Large" / "Request Entity Too Large"
81
+ *
82
+ * **Unreliable detection:**
83
+ * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),
84
+ * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.
85
+ * - Ollama: Silently truncates input without error. Cannot be detected via this function.
86
+ * The response will have usage.input < expected, but we don't know the expected value.
87
+ *
88
+ * ## Custom Providers
89
+ *
90
+ * If you've added custom models via settings.json, this function may not detect
91
+ * overflow errors from those providers. To add support:
92
+ *
93
+ * 1. Send a request that exceeds the model's context window
94
+ * 2. Check the errorMessage in the response
95
+ * 3. Create a regex pattern that matches the error
96
+ * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or
97
+ * check the errorMessage yourself before calling this function
98
+ *
99
+ * @param message - The assistant message to check
100
+ * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)
101
+ * @returns true if the message indicates a context overflow
102
+ */
103
+ export function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {
104
+ // Case 1: Check error message patterns
105
+ if (message.stopReason === "error" && message.errorMessage) {
106
+ // Check known patterns
107
+ if (OVERFLOW_PATTERNS.some(p => p.test(message.errorMessage!))) {
108
+ return true;
109
+ }
110
+
111
+ // Cerebras and Mistral return 400/413 with no body for context overflow.
112
+ // Proxy providers (e.g. api.synthetic.new) wrap upstream 400/413 no-body
113
+ // responses in a JSON envelope, so the status code phrase may appear
114
+ // anywhere in the message rather than at its start.
115
+ // Note: 429 is rate limiting (requests/tokens per time), NOT context overflow
116
+ if (/\b4(00|13)\s*(status code)?\s*\(no body\)/i.test(message.errorMessage)) {
117
+ return true;
118
+ }
119
+ }
120
+
121
+ // Case 2: Usage-based overflow (silent or provider-specific)
122
+ if (contextWindow) {
123
+ const inputTokens = message.usage.input + message.usage.cacheRead + message.usage.cacheWrite;
124
+ if (inputTokens > contextWindow) {
125
+ return true;
126
+ }
127
+ }
128
+
129
+ return false;
130
+ }
131
+
132
+ /**
133
+ * Get the overflow patterns for testing purposes.
134
+ */
135
+ export function getOverflowPatterns(): RegExp[] {
136
+ return [...OVERFLOW_PATTERNS];
137
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared `host:port` parser used by the auth-broker and auth-gateway boot
3
+ * paths. Centralized so the two servers can't drift on what they accept (the
4
+ * gateway used to silently allow empty hostnames; this fixes it).
5
+ */
6
+
7
+ export interface ParsedBind {
8
+ hostname: string;
9
+ port: number;
10
+ }
11
+
12
+ function parsePort(raw: string, bind: string): number {
13
+ if (!/^\d+$/.test(raw)) {
14
+ throw new Error(`Invalid bind '${bind}'; port must be an integer.`);
15
+ }
16
+ const port = Number.parseInt(raw, 10);
17
+ if (!Number.isFinite(port) || port < 0 || port > 65535) {
18
+ throw new Error(`Invalid bind '${bind}'; port out of range.`);
19
+ }
20
+ return port;
21
+ }
22
+
23
+ /**
24
+ * Parse a `host:port` (or bare `port`, which assumes loopback) string.
25
+ *
26
+ * Accepts:
27
+ * - `"4000"` → `127.0.0.1:4000`
28
+ * - `"0.0.0.0:4000"` → as written
29
+ * - `"[::1]:4000"` → as written (brackets retained, Bun handles them)
30
+ *
31
+ * Rejects:
32
+ * - empty input
33
+ * - empty hostname (`":4000"`)
34
+ * - non-integer / out-of-range port
35
+ */
36
+ export function parseBind(raw: string): ParsedBind {
37
+ const trimmed = raw.trim();
38
+ if (trimmed.length === 0) {
39
+ throw new Error("Invalid bind; expected 'host:port' or 'port'.");
40
+ }
41
+ if (/^\d+$/.test(trimmed)) {
42
+ return { hostname: "127.0.0.1", port: parsePort(trimmed, raw) };
43
+ }
44
+ const lastColon = trimmed.lastIndexOf(":");
45
+ if (lastColon < 0) {
46
+ throw new Error(`Invalid bind '${raw}'; expected 'host:port' or 'port'.`);
47
+ }
48
+ const hostPart = trimmed.slice(0, lastColon);
49
+ const portPart = trimmed.slice(lastColon + 1);
50
+ if (hostPart.length === 0) {
51
+ throw new Error(`Invalid bind '${raw}'; host must not be empty.`);
52
+ }
53
+ return { hostname: hostPart, port: parsePort(portPart, raw) };
54
+ }
@@ -0,0 +1,30 @@
1
+ import type { Api, Model, ProviderResponseMetadata, StreamOptions } from "../types";
2
+
3
+ export function normalizeProviderResponse(
4
+ response: Response,
5
+ requestId?: string | null,
6
+ metadata?: Record<string, unknown>,
7
+ ): ProviderResponseMetadata {
8
+ const headers: Record<string, string> = {};
9
+ response.headers.forEach((value, key) => {
10
+ headers[key.toLowerCase()] = value;
11
+ });
12
+ const providerResponse: ProviderResponseMetadata = {
13
+ status: response.status,
14
+ headers,
15
+ };
16
+ if (requestId !== undefined) providerResponse.requestId = requestId;
17
+ if (metadata !== undefined) providerResponse.metadata = metadata;
18
+ return providerResponse;
19
+ }
20
+
21
+ export async function notifyProviderResponse(
22
+ options: Pick<StreamOptions, "onResponse"> | undefined,
23
+ response: Response,
24
+ model?: Model<Api>,
25
+ requestId?: string | null,
26
+ metadata?: Record<string, unknown>,
27
+ ): Promise<void> {
28
+ if (!options?.onResponse) return;
29
+ await options.onResponse(normalizeProviderResponse(response, requestId, metadata), model);
30
+ }
@@ -0,0 +1,336 @@
1
+ import { Buffer } from "node:buffer";
2
+ import * as fs from "node:fs/promises";
3
+ import type { FetchImpl } from "../types";
4
+
5
+ const REQUEST_DEBUG_ENV = "PI_REQ_DEBUG";
6
+ const DEBUG_FETCH_MARKER = Symbol("aery.requestDebugFetch");
7
+ const textEncoder = new TextEncoder();
8
+ const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
9
+
10
+ let nextSessionId = 1;
11
+
12
+ type DebugFetch = FetchImpl & { [DEBUG_FETCH_MARKER]?: true };
13
+ type RequestBodyInit = NonNullable<RequestInit["body"]>;
14
+
15
+ type RequestDebugBody = { body: unknown } | { bodyText: string } | { bodyBase64: string } | { bodyUnavailable: string };
16
+
17
+ export type RequestDebugHeaders = Headers | Record<string, string | string[] | number | undefined | null> | undefined;
18
+
19
+ export interface RequestDebugPayload {
20
+ method: string;
21
+ url: string;
22
+ headers?: RequestDebugHeaders;
23
+ body?: unknown;
24
+ bodyText?: string;
25
+ bodyBase64?: string;
26
+ bodyUnavailable?: string;
27
+ protocol?: string;
28
+ }
29
+
30
+ export interface RequestDebugResponseLog {
31
+ write(chunk: Uint8Array | string): void;
32
+ close(): Promise<void>;
33
+ }
34
+
35
+ export interface RequestDebugSession {
36
+ readonly id: number;
37
+ readonly requestPath: string;
38
+ readonly responsePath: string;
39
+ openResponseLog(statusLine: string, headers?: RequestDebugHeaders): Promise<RequestDebugResponseLog>;
40
+ wrapResponse(response: Response): Promise<Response>;
41
+ }
42
+
43
+ export function isRequestDebugEnabled(): boolean {
44
+ return Bun.env[REQUEST_DEBUG_ENV] === "1";
45
+ }
46
+
47
+ export function wrapFetchForRequestDebug(fetchImpl: FetchImpl): FetchImpl {
48
+ if (!isRequestDebugEnabled()) return fetchImpl;
49
+ const maybeWrapped = fetchImpl as DebugFetch;
50
+ if (maybeWrapped[DEBUG_FETCH_MARKER]) return fetchImpl;
51
+
52
+ const wrapped = Object.assign(
53
+ async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
54
+ const session = await createFetchRequestDebugSession(input, init);
55
+ const response = await fetchImpl(input, init);
56
+ return session.wrapResponse(response);
57
+ },
58
+ fetchImpl.preconnect ? { preconnect: fetchImpl.preconnect } : {},
59
+ { [DEBUG_FETCH_MARKER]: true as const },
60
+ );
61
+ return wrapped;
62
+ }
63
+
64
+ export function withRequestDebugFetch<T extends { fetch?: FetchImpl } | undefined>(options: T): T {
65
+ if (!isRequestDebugEnabled()) return options;
66
+ const fetchImpl = options?.fetch ?? (globalThis.fetch as FetchImpl);
67
+ const wrapped = wrapFetchForRequestDebug(fetchImpl);
68
+ return { ...(options ?? {}), fetch: wrapped } as T;
69
+ }
70
+
71
+ export async function createRequestDebugSession(payload: RequestDebugPayload): Promise<RequestDebugSession> {
72
+ const { id, requestPath, responsePath, handle } = await reserveRequestDebugFile();
73
+ const requestDump: Record<string, unknown> = {
74
+ id,
75
+ protocol: payload.protocol ?? "http",
76
+ method: payload.method,
77
+ url: payload.url,
78
+ };
79
+ const headers = headersToRecord(payload.headers);
80
+ if (headers) requestDump.headers = headers;
81
+ if (payload.body !== undefined) requestDump.body = payload.body;
82
+ if (payload.bodyText !== undefined) requestDump.bodyText = payload.bodyText;
83
+ if (payload.bodyBase64 !== undefined) requestDump.bodyBase64 = payload.bodyBase64;
84
+ if (payload.bodyUnavailable !== undefined) requestDump.bodyUnavailable = payload.bodyUnavailable;
85
+
86
+ try {
87
+ await handle.writeFile(`${JSON.stringify(requestDump, null, 2)}\n`, "utf8");
88
+ } finally {
89
+ await handle.close();
90
+ }
91
+
92
+ return new FileRequestDebugSession(id, requestPath, responsePath);
93
+ }
94
+
95
+ async function createFetchRequestDebugSession(
96
+ input: string | URL | Request,
97
+ init: RequestInit | undefined,
98
+ ): Promise<RequestDebugSession> {
99
+ const headers = resolveRequestHeaders(input, init);
100
+ const body = await snapshotRequestBody(input, init, headers.get("content-type"));
101
+ return createRequestDebugSession({
102
+ method: resolveRequestMethod(input, init),
103
+ url: resolveRequestUrl(input),
104
+ headers,
105
+ ...body,
106
+ });
107
+ }
108
+
109
+ class FileRequestDebugSession implements RequestDebugSession {
110
+ readonly id: number;
111
+ readonly requestPath: string;
112
+ readonly responsePath: string;
113
+
114
+ constructor(id: number, requestPath: string, responsePath: string) {
115
+ this.id = id;
116
+ this.requestPath = requestPath;
117
+ this.responsePath = responsePath;
118
+ }
119
+
120
+ async openResponseLog(statusLine: string, headers?: RequestDebugHeaders): Promise<RequestDebugResponseLog> {
121
+ const handle = await fs.open(this.responsePath, "wx");
122
+ const headerBlock = formatResponseHeaderBlock(statusLine, headers);
123
+ await handle.write(textEncoder.encode(headerBlock));
124
+ return new FileRequestDebugResponseLog(handle);
125
+ }
126
+
127
+ async wrapResponse(response: Response): Promise<Response> {
128
+ const log = await this.openResponseLog(`HTTP ${response.status} ${response.statusText}`.trim(), response.headers);
129
+ if (!response.body) {
130
+ await log.close();
131
+ return response;
132
+ }
133
+
134
+ const reader = response.body.getReader();
135
+ const teed = new ReadableStream<Uint8Array>({
136
+ async pull(controller) {
137
+ try {
138
+ const { done, value } = await reader.read();
139
+ if (done) {
140
+ await log.close();
141
+ controller.close();
142
+ return;
143
+ }
144
+ log.write(value);
145
+ controller.enqueue(value);
146
+ } catch (error) {
147
+ await log.close().catch(() => undefined);
148
+ controller.error(error);
149
+ }
150
+ },
151
+ async cancel(reason) {
152
+ try {
153
+ await reader.cancel(reason);
154
+ } finally {
155
+ await log.close();
156
+ }
157
+ },
158
+ });
159
+
160
+ const wrapped = new Response(teed, {
161
+ status: response.status,
162
+ statusText: response.statusText,
163
+ headers: response.headers,
164
+ });
165
+ copyResponseMetadata(wrapped, response);
166
+ return wrapped;
167
+ }
168
+ }
169
+
170
+ class FileRequestDebugResponseLog implements RequestDebugResponseLog {
171
+ #handle: fs.FileHandle | undefined;
172
+ #pending: Promise<void> = Promise.resolve();
173
+
174
+ constructor(handle: fs.FileHandle) {
175
+ this.#handle = handle;
176
+ }
177
+
178
+ write(chunk: Uint8Array | string): void {
179
+ const handle = this.#handle;
180
+ if (!handle) return;
181
+ const bytes = typeof chunk === "string" ? textEncoder.encode(chunk) : chunk.slice();
182
+ this.#pending = this.#pending.then(async () => {
183
+ await handle.write(bytes);
184
+ });
185
+ }
186
+
187
+ async close(): Promise<void> {
188
+ const handle = this.#handle;
189
+ if (!handle) return;
190
+ this.#handle = undefined;
191
+ try {
192
+ await this.#pending;
193
+ } finally {
194
+ await handle.close();
195
+ }
196
+ }
197
+ }
198
+
199
+ function copyResponseMetadata(target: Response, source: Response): void {
200
+ const sourceUrl = source.url;
201
+ if (!sourceUrl) return;
202
+ try {
203
+ Object.defineProperty(target, "url", { value: sourceUrl, configurable: true });
204
+ } catch {
205
+ // Some runtimes may expose Response.url as non-configurable. The body
206
+ // capture remains correct; callers that need url already tolerate the
207
+ // platform default on other response wrappers in this package.
208
+ }
209
+ }
210
+
211
+ async function reserveRequestDebugFile(): Promise<{
212
+ id: number;
213
+ requestPath: string;
214
+ responsePath: string;
215
+ handle: fs.FileHandle;
216
+ }> {
217
+ for (;;) {
218
+ const id = nextSessionId++;
219
+ const requestPath = `rr-session-${id}.json`;
220
+ try {
221
+ const handle = await fs.open(requestPath, "wx");
222
+ return { id, requestPath, responsePath: `rr-session-${id}.res.log`, handle };
223
+ } catch (error) {
224
+ if (isFileExistsError(error)) continue;
225
+ throw error;
226
+ }
227
+ }
228
+ }
229
+
230
+ function resolveRequestMethod(input: string | URL | Request, init: RequestInit | undefined): string {
231
+ return (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
232
+ }
233
+
234
+ function resolveRequestUrl(input: string | URL | Request): string {
235
+ return input instanceof Request ? input.url : input.toString();
236
+ }
237
+
238
+ function resolveRequestHeaders(input: string | URL | Request, init: RequestInit | undefined): Headers {
239
+ if (init?.headers) return new Headers(init.headers);
240
+ return input instanceof Request ? new Headers(input.headers) : new Headers();
241
+ }
242
+
243
+ async function snapshotRequestBody(
244
+ input: string | URL | Request,
245
+ init: RequestInit | undefined,
246
+ contentType: string | null,
247
+ ): Promise<RequestDebugBody | undefined> {
248
+ if (init?.body !== undefined && init.body !== null) return snapshotBodyInit(init.body, contentType);
249
+ if (input instanceof Request && input.body) {
250
+ return snapshotBytes(new Uint8Array(await input.clone().arrayBuffer()), contentType);
251
+ }
252
+ return undefined;
253
+ }
254
+
255
+ async function snapshotBodyInit(body: RequestBodyInit, contentType: string | null): Promise<RequestDebugBody> {
256
+ if (typeof body === "string") return snapshotText(body, contentType);
257
+ if (body instanceof URLSearchParams) return { bodyText: body.toString() };
258
+ if (body instanceof FormData) return { bodyUnavailable: "FormData" };
259
+ if (body instanceof Blob) return snapshotBytes(new Uint8Array(await body.arrayBuffer()), body.type || contentType);
260
+ if (body instanceof ArrayBuffer) return snapshotBytes(new Uint8Array(body), contentType);
261
+ if (ArrayBuffer.isView(body)) {
262
+ return snapshotBytes(new Uint8Array(body.buffer, body.byteOffset, body.byteLength), contentType);
263
+ }
264
+ if (body instanceof ReadableStream) return { bodyUnavailable: "ReadableStream" };
265
+ return { bodyText: String(body) };
266
+ }
267
+
268
+ function snapshotBytes(bytes: Uint8Array, contentType: string | null): RequestDebugBody {
269
+ try {
270
+ return snapshotText(utf8Decoder.decode(bytes), contentType);
271
+ } catch {
272
+ return { bodyBase64: Buffer.from(bytes).toString("base64") };
273
+ }
274
+ }
275
+
276
+ function snapshotText(text: string, contentType: string | null): RequestDebugBody {
277
+ if (isJsonContentType(contentType) || looksLikeJson(text)) {
278
+ try {
279
+ return { body: JSON.parse(text) };
280
+ } catch {
281
+ // Fall through to bodyText: malformed JSON is still useful as raw text.
282
+ }
283
+ }
284
+ return { bodyText: text };
285
+ }
286
+
287
+ function isJsonContentType(contentType: string | null): boolean {
288
+ if (!contentType) return false;
289
+ const lower = contentType.toLowerCase();
290
+ return lower.includes("application/json") || lower.includes("+json");
291
+ }
292
+
293
+ function looksLikeJson(text: string): boolean {
294
+ const trimmed = text.trimStart();
295
+ return trimmed.startsWith("{") || trimmed.startsWith("[");
296
+ }
297
+
298
+ function formatResponseHeaderBlock(statusLine: string, headers?: RequestDebugHeaders): string {
299
+ const lines = [statusLine];
300
+ const record = headersToRecord(headers);
301
+ if (record) {
302
+ for (const name in record) {
303
+ const value = record[name];
304
+ if (Array.isArray(value)) {
305
+ for (const item of value) lines.push(`${name}: ${item}`);
306
+ } else {
307
+ lines.push(`${name}: ${value}`);
308
+ }
309
+ }
310
+ }
311
+ return `${lines.join("\r\n")}\r\n\r\n`;
312
+ }
313
+
314
+ function headersToRecord(headers: RequestDebugHeaders): Record<string, string | string[]> | undefined {
315
+ if (!headers) return undefined;
316
+ const record: Record<string, string | string[]> = {};
317
+ let hasHeaders = false;
318
+ if (headers instanceof Headers) {
319
+ headers.forEach((value, key) => {
320
+ hasHeaders = true;
321
+ record[key] = value;
322
+ });
323
+ } else {
324
+ for (const key in headers) {
325
+ const value = headers[key];
326
+ if (value === undefined || value === null) continue;
327
+ hasHeaders = true;
328
+ record[key] = Array.isArray(value) ? value.map(String) : String(value);
329
+ }
330
+ }
331
+ return hasHeaders ? record : undefined;
332
+ }
333
+
334
+ function isFileExistsError(error: unknown): boolean {
335
+ return typeof error === "object" && error !== null && (error as { code?: unknown }).code === "EEXIST";
336
+ }
@@ -0,0 +1,110 @@
1
+ export type HeadersLike = Headers | Record<string, string | undefined> | undefined | null;
2
+
3
+ const RETRY_AFTER_HINT = "retry-after-ms=";
4
+
5
+ export function formatErrorMessageWithRetryAfter(error: unknown, headers?: HeadersLike): string {
6
+ const message = error instanceof Error ? error.message : JSON.stringify(error);
7
+ if (message.includes(RETRY_AFTER_HINT)) {
8
+ return message;
9
+ }
10
+
11
+ const retryAfterMs = getRetryAfterMsFromHeaders(headers ?? getHeadersFromError(error));
12
+ if (retryAfterMs === undefined) {
13
+ return message;
14
+ }
15
+
16
+ return `${message} ${RETRY_AFTER_HINT}${retryAfterMs}`;
17
+ }
18
+
19
+ export function getRetryAfterMsFromHeaders(headers: HeadersLike): number | undefined {
20
+ if (!headers) return undefined;
21
+
22
+ const retryAfter = parseRetryAfterHeader(getHeaderValue(headers, "retry-after"));
23
+ const resetMs = parseResetHeader(getHeaderValue(headers, "x-ratelimit-reset-ms"), "ms");
24
+ const resetSeconds = parseResetHeader(getHeaderValue(headers, "x-ratelimit-reset"), "s");
25
+
26
+ const candidates = [retryAfter, resetMs, resetSeconds].filter((value): value is number => value !== undefined);
27
+ if (candidates.length === 0) return undefined;
28
+ return Math.max(...candidates);
29
+ }
30
+
31
+ function getHeadersFromError(error: unknown): HeadersLike {
32
+ if (!error || typeof error !== "object") return undefined;
33
+ const record = error as { headers?: unknown; response?: { headers?: unknown }; cause?: unknown };
34
+ const direct = extractHeaders(record.headers) ?? extractHeaders(record.response?.headers);
35
+ if (direct) return direct;
36
+ if (record.cause) return getHeadersFromError(record.cause);
37
+ return undefined;
38
+ }
39
+
40
+ function extractHeaders(value: unknown): HeadersLike {
41
+ if (!value) return undefined;
42
+ if (value instanceof Headers) return value;
43
+ if (typeof value === "object") return value as Record<string, string | undefined>;
44
+ return undefined;
45
+ }
46
+
47
+ function getHeaderValue(headers: Headers | Record<string, string | undefined>, name: string): string | undefined {
48
+ if (headers instanceof Headers) {
49
+ const value = headers.get(name);
50
+ return value ?? undefined;
51
+ }
52
+
53
+ const target = name.toLowerCase();
54
+ for (const [key, value] of Object.entries(headers)) {
55
+ if (key.toLowerCase() === target && typeof value === "string") {
56
+ return value;
57
+ }
58
+ }
59
+ return undefined;
60
+ }
61
+
62
+ function parseRetryAfterHeader(value: string | undefined): number | undefined {
63
+ if (!value) return undefined;
64
+ const trimmed = value.trim();
65
+ if (!trimmed) return undefined;
66
+
67
+ const numeric = Number(trimmed);
68
+ if (Number.isFinite(numeric)) {
69
+ if (numeric <= 0) return undefined;
70
+ return Math.ceil(numeric * 1000);
71
+ }
72
+
73
+ const dateMs = Date.parse(trimmed);
74
+ if (!Number.isNaN(dateMs)) {
75
+ const delay = dateMs - Date.now();
76
+ return delay > 0 ? Math.ceil(delay) : undefined;
77
+ }
78
+
79
+ return undefined;
80
+ }
81
+
82
+ function parseResetHeader(value: string | undefined, unit: "ms" | "s"): number | undefined {
83
+ if (!value) return undefined;
84
+ const numeric = Number(value);
85
+ if (!Number.isFinite(numeric) || numeric <= 0) return undefined;
86
+
87
+ const nowMs = Date.now();
88
+ let targetMs: number | undefined;
89
+
90
+ if (unit === "ms") {
91
+ if (numeric > 1e12) {
92
+ targetMs = numeric;
93
+ } else if (numeric > 1e9) {
94
+ targetMs = numeric * 1000;
95
+ } else {
96
+ return Math.ceil(numeric);
97
+ }
98
+ } else {
99
+ if (numeric > 1e12) {
100
+ targetMs = numeric;
101
+ } else if (numeric > 1e9) {
102
+ targetMs = numeric * 1000;
103
+ } else {
104
+ return Math.ceil(numeric * 1000);
105
+ }
106
+ }
107
+
108
+ if (targetMs <= nowMs) return undefined;
109
+ return Math.ceil(targetMs - nowMs);
110
+ }