@juspay/neurolink 9.64.0 → 9.65.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +18 -17
  3. package/dist/adapters/providerImageAdapter.js +29 -1
  4. package/dist/adapters/replicate/auth.d.ts +19 -0
  5. package/dist/adapters/replicate/auth.js +32 -0
  6. package/dist/adapters/replicate/predictionLifecycle.d.ts +46 -0
  7. package/dist/adapters/replicate/predictionLifecycle.js +283 -0
  8. package/dist/adapters/video/klingVideoHandler.d.ts +37 -0
  9. package/dist/adapters/video/klingVideoHandler.js +305 -0
  10. package/dist/adapters/video/replicateVideoHandler.d.ts +29 -0
  11. package/dist/adapters/video/replicateVideoHandler.js +157 -0
  12. package/dist/adapters/video/runwayVideoHandler.d.ts +32 -0
  13. package/dist/adapters/video/runwayVideoHandler.js +316 -0
  14. package/dist/adapters/video/vertexVideoHandler.d.ts +19 -1
  15. package/dist/adapters/video/vertexVideoHandler.js +33 -9
  16. package/dist/agent/directTools.js +11 -3
  17. package/dist/autoresearch/runner.js +8 -2
  18. package/dist/avatar/index.d.ts +13 -0
  19. package/dist/avatar/index.js +13 -0
  20. package/dist/avatar/providers/DIDAvatar.d.ts +49 -0
  21. package/dist/avatar/providers/DIDAvatar.js +501 -0
  22. package/dist/avatar/providers/HeyGenAvatar.d.ts +30 -0
  23. package/dist/avatar/providers/HeyGenAvatar.js +337 -0
  24. package/dist/avatar/providers/ReplicateAvatar.d.ts +36 -0
  25. package/dist/avatar/providers/ReplicateAvatar.js +267 -0
  26. package/dist/browser/neurolink.min.js +624 -601
  27. package/dist/cli/commands/mcp.js +29 -0
  28. package/dist/cli/commands/proxy.js +24 -5
  29. package/dist/cli/factories/commandFactory.d.ts +11 -1
  30. package/dist/cli/factories/commandFactory.js +291 -38
  31. package/dist/constants/contextWindows.js +101 -0
  32. package/dist/constants/enums.d.ts +273 -2
  33. package/dist/constants/enums.js +290 -1
  34. package/dist/constants/videoErrors.d.ts +4 -0
  35. package/dist/constants/videoErrors.js +4 -0
  36. package/dist/core/baseProvider.d.ts +22 -2
  37. package/dist/core/baseProvider.js +217 -11
  38. package/dist/core/constants.d.ts +12 -0
  39. package/dist/core/constants.js +72 -1
  40. package/dist/evaluation/index.d.ts +2 -0
  41. package/dist/evaluation/index.js +4 -0
  42. package/dist/factories/providerFactory.js +7 -1
  43. package/dist/factories/providerRegistry.js +202 -5
  44. package/dist/features/ppt/contentPlanner.js +42 -14
  45. package/dist/index.d.ts +9 -1
  46. package/dist/index.js +16 -1
  47. package/dist/lib/adapters/providerImageAdapter.js +29 -1
  48. package/dist/lib/adapters/replicate/auth.d.ts +19 -0
  49. package/dist/lib/adapters/replicate/auth.js +33 -0
  50. package/dist/lib/adapters/replicate/predictionLifecycle.d.ts +46 -0
  51. package/dist/lib/adapters/replicate/predictionLifecycle.js +284 -0
  52. package/dist/lib/adapters/video/klingVideoHandler.d.ts +37 -0
  53. package/dist/lib/adapters/video/klingVideoHandler.js +306 -0
  54. package/dist/lib/adapters/video/replicateVideoHandler.d.ts +29 -0
  55. package/dist/lib/adapters/video/replicateVideoHandler.js +158 -0
  56. package/dist/lib/adapters/video/runwayVideoHandler.d.ts +32 -0
  57. package/dist/lib/adapters/video/runwayVideoHandler.js +317 -0
  58. package/dist/lib/adapters/video/vertexVideoHandler.d.ts +19 -1
  59. package/dist/lib/adapters/video/vertexVideoHandler.js +33 -9
  60. package/dist/lib/agent/directTools.js +11 -3
  61. package/dist/lib/autoresearch/runner.js +8 -2
  62. package/dist/lib/avatar/index.d.ts +13 -0
  63. package/dist/lib/avatar/index.js +14 -0
  64. package/dist/lib/avatar/providers/DIDAvatar.d.ts +49 -0
  65. package/dist/lib/avatar/providers/DIDAvatar.js +502 -0
  66. package/dist/lib/avatar/providers/HeyGenAvatar.d.ts +30 -0
  67. package/dist/lib/avatar/providers/HeyGenAvatar.js +338 -0
  68. package/dist/lib/avatar/providers/ReplicateAvatar.d.ts +36 -0
  69. package/dist/lib/avatar/providers/ReplicateAvatar.js +268 -0
  70. package/dist/lib/constants/contextWindows.js +101 -0
  71. package/dist/lib/constants/enums.d.ts +273 -2
  72. package/dist/lib/constants/enums.js +290 -1
  73. package/dist/lib/constants/videoErrors.d.ts +4 -0
  74. package/dist/lib/constants/videoErrors.js +4 -0
  75. package/dist/lib/core/baseProvider.d.ts +22 -2
  76. package/dist/lib/core/baseProvider.js +217 -11
  77. package/dist/lib/core/constants.d.ts +12 -0
  78. package/dist/lib/core/constants.js +72 -1
  79. package/dist/lib/evaluation/index.d.ts +2 -0
  80. package/dist/lib/evaluation/index.js +4 -0
  81. package/dist/lib/factories/providerFactory.js +7 -1
  82. package/dist/lib/factories/providerRegistry.js +202 -5
  83. package/dist/lib/features/ppt/contentPlanner.js +42 -14
  84. package/dist/lib/index.d.ts +9 -1
  85. package/dist/lib/index.js +16 -1
  86. package/dist/lib/middleware/builtin/lifecycle.js +39 -9
  87. package/dist/lib/music/index.d.ts +13 -0
  88. package/dist/lib/music/index.js +14 -0
  89. package/dist/lib/music/providers/BeatovenMusic.d.ts +31 -0
  90. package/dist/lib/music/providers/BeatovenMusic.js +334 -0
  91. package/dist/lib/music/providers/ElevenLabsMusic.d.ts +30 -0
  92. package/dist/lib/music/providers/ElevenLabsMusic.js +169 -0
  93. package/dist/lib/music/providers/LyriaMusic.d.ts +29 -0
  94. package/dist/lib/music/providers/LyriaMusic.js +173 -0
  95. package/dist/lib/music/providers/ReplicateMusic.d.ts +31 -0
  96. package/dist/lib/music/providers/ReplicateMusic.js +262 -0
  97. package/dist/lib/neurolink.d.ts +30 -0
  98. package/dist/lib/neurolink.js +323 -77
  99. package/dist/lib/providers/amazonBedrock.d.ts +10 -0
  100. package/dist/lib/providers/amazonBedrock.js +94 -39
  101. package/dist/lib/providers/anthropic.js +55 -7
  102. package/dist/lib/providers/anthropicBaseProvider.js +1 -1
  103. package/dist/lib/providers/azureOpenai.js +66 -17
  104. package/dist/lib/providers/cloudflare.d.ts +35 -0
  105. package/dist/lib/providers/cloudflare.js +174 -0
  106. package/dist/lib/providers/cohere.d.ts +52 -0
  107. package/dist/lib/providers/cohere.js +253 -0
  108. package/dist/lib/providers/deepseek.js +72 -17
  109. package/dist/lib/providers/fireworks.d.ts +33 -0
  110. package/dist/lib/providers/fireworks.js +164 -0
  111. package/dist/lib/providers/googleAiStudio.js +126 -10
  112. package/dist/lib/providers/googleNativeGemini3.d.ts +26 -6
  113. package/dist/lib/providers/googleNativeGemini3.js +276 -29
  114. package/dist/lib/providers/googleVertex.js +639 -181
  115. package/dist/lib/providers/groq.d.ts +33 -0
  116. package/dist/lib/providers/groq.js +181 -0
  117. package/dist/lib/providers/huggingFace.js +9 -8
  118. package/dist/lib/providers/ideogram.d.ts +34 -0
  119. package/dist/lib/providers/ideogram.js +184 -0
  120. package/dist/lib/providers/index.d.ts +13 -0
  121. package/dist/lib/providers/index.js +13 -0
  122. package/dist/lib/providers/jina.d.ts +59 -0
  123. package/dist/lib/providers/jina.js +218 -0
  124. package/dist/lib/providers/llamaCpp.js +14 -46
  125. package/dist/lib/providers/lmStudio.js +14 -47
  126. package/dist/lib/providers/mistral.js +7 -7
  127. package/dist/lib/providers/nvidiaNim.js +160 -19
  128. package/dist/lib/providers/ollama.js +7 -7
  129. package/dist/lib/providers/openAI.d.ts +22 -1
  130. package/dist/lib/providers/openAI.js +181 -0
  131. package/dist/lib/providers/openRouter.js +35 -23
  132. package/dist/lib/providers/openaiCompatible.js +9 -8
  133. package/dist/lib/providers/perplexity.d.ts +33 -0
  134. package/dist/lib/providers/perplexity.js +179 -0
  135. package/dist/lib/providers/recraft.d.ts +34 -0
  136. package/dist/lib/providers/recraft.js +197 -0
  137. package/dist/lib/providers/replicate.d.ts +75 -0
  138. package/dist/lib/providers/replicate.js +403 -0
  139. package/dist/lib/providers/stability.d.ts +37 -0
  140. package/dist/lib/providers/stability.js +191 -0
  141. package/dist/lib/providers/togetherAi.d.ts +33 -0
  142. package/dist/lib/providers/togetherAi.js +176 -0
  143. package/dist/lib/providers/voyage.d.ts +47 -0
  144. package/dist/lib/providers/voyage.js +177 -0
  145. package/dist/lib/providers/xai.d.ts +33 -0
  146. package/dist/lib/providers/xai.js +172 -0
  147. package/dist/lib/telemetry/index.d.ts +1 -1
  148. package/dist/lib/telemetry/index.js +1 -1
  149. package/dist/lib/telemetry/tracers.d.ts +19 -0
  150. package/dist/lib/telemetry/tracers.js +19 -0
  151. package/dist/lib/telemetry/withSpan.d.ts +35 -0
  152. package/dist/lib/telemetry/withSpan.js +103 -0
  153. package/dist/lib/types/avatar.d.ts +143 -0
  154. package/dist/lib/types/avatar.js +20 -0
  155. package/dist/lib/types/cli.d.ts +6 -0
  156. package/dist/lib/types/conversation.d.ts +16 -0
  157. package/dist/lib/types/generate.d.ts +62 -5
  158. package/dist/lib/types/index.d.ts +5 -0
  159. package/dist/lib/types/index.js +7 -0
  160. package/dist/lib/types/middleware.d.ts +27 -0
  161. package/dist/lib/types/multimodal.d.ts +35 -2
  162. package/dist/lib/types/music.d.ts +165 -0
  163. package/dist/lib/types/music.js +21 -0
  164. package/dist/lib/types/providers.d.ts +144 -1
  165. package/dist/lib/types/replicate.d.ts +67 -0
  166. package/dist/lib/types/replicate.js +10 -0
  167. package/dist/lib/types/safeFetch.d.ts +15 -0
  168. package/dist/lib/types/safeFetch.js +7 -0
  169. package/dist/lib/types/stream.d.ts +2 -1
  170. package/dist/lib/types/tools.d.ts +13 -0
  171. package/dist/lib/types/video.d.ts +89 -0
  172. package/dist/lib/types/video.js +15 -0
  173. package/dist/lib/utils/avatarProcessor.d.ts +68 -0
  174. package/dist/lib/utils/avatarProcessor.js +172 -0
  175. package/dist/lib/utils/cloneOptions.d.ts +36 -0
  176. package/dist/lib/utils/cloneOptions.js +62 -0
  177. package/dist/lib/utils/lifecycleCallbacks.d.ts +51 -8
  178. package/dist/lib/utils/lifecycleCallbacks.js +82 -26
  179. package/dist/lib/utils/lifecycleTimeout.d.ts +25 -0
  180. package/dist/lib/utils/lifecycleTimeout.js +39 -0
  181. package/dist/lib/utils/logSanitize.d.ts +49 -0
  182. package/dist/lib/utils/logSanitize.js +170 -0
  183. package/dist/lib/utils/loggingFetch.d.ts +29 -0
  184. package/dist/lib/utils/loggingFetch.js +60 -0
  185. package/dist/lib/utils/messageBuilder.js +43 -25
  186. package/dist/lib/utils/modelChoices.js +236 -3
  187. package/dist/lib/utils/musicProcessor.d.ts +67 -0
  188. package/dist/lib/utils/musicProcessor.js +189 -0
  189. package/dist/lib/utils/optionsConversion.js +3 -2
  190. package/dist/lib/utils/parameterValidation.js +14 -4
  191. package/dist/lib/utils/pricing.js +193 -0
  192. package/dist/lib/utils/providerConfig.d.ts +55 -0
  193. package/dist/lib/utils/providerConfig.js +224 -0
  194. package/dist/lib/utils/safeFetch.d.ts +26 -0
  195. package/dist/lib/utils/safeFetch.js +83 -0
  196. package/dist/lib/utils/sizeGuard.d.ts +34 -0
  197. package/dist/lib/utils/sizeGuard.js +45 -0
  198. package/dist/lib/utils/ssrfGuard.d.ts +52 -0
  199. package/dist/lib/utils/ssrfGuard.js +411 -0
  200. package/dist/lib/utils/videoProcessor.d.ts +60 -0
  201. package/dist/lib/utils/videoProcessor.js +201 -0
  202. package/dist/lib/voice/providers/FishAudioTTS.d.ts +27 -0
  203. package/dist/lib/voice/providers/FishAudioTTS.js +183 -0
  204. package/dist/lib/workflow/core/ensembleExecutor.js +26 -9
  205. package/dist/middleware/builtin/lifecycle.js +39 -9
  206. package/dist/music/index.d.ts +13 -0
  207. package/dist/music/index.js +13 -0
  208. package/dist/music/providers/BeatovenMusic.d.ts +31 -0
  209. package/dist/music/providers/BeatovenMusic.js +333 -0
  210. package/dist/music/providers/ElevenLabsMusic.d.ts +30 -0
  211. package/dist/music/providers/ElevenLabsMusic.js +168 -0
  212. package/dist/music/providers/LyriaMusic.d.ts +29 -0
  213. package/dist/music/providers/LyriaMusic.js +172 -0
  214. package/dist/music/providers/ReplicateMusic.d.ts +31 -0
  215. package/dist/music/providers/ReplicateMusic.js +261 -0
  216. package/dist/neurolink.d.ts +30 -0
  217. package/dist/neurolink.js +323 -77
  218. package/dist/providers/amazonBedrock.d.ts +10 -0
  219. package/dist/providers/amazonBedrock.js +94 -39
  220. package/dist/providers/anthropic.js +55 -7
  221. package/dist/providers/anthropicBaseProvider.js +1 -1
  222. package/dist/providers/azureOpenai.js +66 -17
  223. package/dist/providers/cloudflare.d.ts +35 -0
  224. package/dist/providers/cloudflare.js +173 -0
  225. package/dist/providers/cohere.d.ts +52 -0
  226. package/dist/providers/cohere.js +252 -0
  227. package/dist/providers/deepseek.js +72 -17
  228. package/dist/providers/fireworks.d.ts +33 -0
  229. package/dist/providers/fireworks.js +163 -0
  230. package/dist/providers/googleAiStudio.js +126 -10
  231. package/dist/providers/googleNativeGemini3.d.ts +26 -6
  232. package/dist/providers/googleNativeGemini3.js +276 -29
  233. package/dist/providers/googleVertex.js +639 -181
  234. package/dist/providers/groq.d.ts +33 -0
  235. package/dist/providers/groq.js +180 -0
  236. package/dist/providers/huggingFace.js +9 -8
  237. package/dist/providers/ideogram.d.ts +34 -0
  238. package/dist/providers/ideogram.js +183 -0
  239. package/dist/providers/index.d.ts +13 -0
  240. package/dist/providers/index.js +13 -0
  241. package/dist/providers/jina.d.ts +59 -0
  242. package/dist/providers/jina.js +217 -0
  243. package/dist/providers/llamaCpp.js +14 -46
  244. package/dist/providers/lmStudio.js +14 -47
  245. package/dist/providers/mistral.js +7 -7
  246. package/dist/providers/nvidiaNim.js +160 -19
  247. package/dist/providers/ollama.js +7 -7
  248. package/dist/providers/openAI.d.ts +22 -1
  249. package/dist/providers/openAI.js +181 -0
  250. package/dist/providers/openRouter.js +35 -23
  251. package/dist/providers/openaiCompatible.js +9 -8
  252. package/dist/providers/perplexity.d.ts +33 -0
  253. package/dist/providers/perplexity.js +178 -0
  254. package/dist/providers/recraft.d.ts +34 -0
  255. package/dist/providers/recraft.js +196 -0
  256. package/dist/providers/replicate.d.ts +75 -0
  257. package/dist/providers/replicate.js +402 -0
  258. package/dist/providers/stability.d.ts +37 -0
  259. package/dist/providers/stability.js +190 -0
  260. package/dist/providers/togetherAi.d.ts +33 -0
  261. package/dist/providers/togetherAi.js +175 -0
  262. package/dist/providers/voyage.d.ts +47 -0
  263. package/dist/providers/voyage.js +176 -0
  264. package/dist/providers/xai.d.ts +33 -0
  265. package/dist/providers/xai.js +171 -0
  266. package/dist/telemetry/index.d.ts +1 -1
  267. package/dist/telemetry/index.js +1 -1
  268. package/dist/telemetry/tracers.d.ts +19 -0
  269. package/dist/telemetry/tracers.js +19 -0
  270. package/dist/telemetry/withSpan.d.ts +35 -0
  271. package/dist/telemetry/withSpan.js +103 -0
  272. package/dist/types/avatar.d.ts +143 -0
  273. package/dist/types/avatar.js +19 -0
  274. package/dist/types/cli.d.ts +6 -0
  275. package/dist/types/conversation.d.ts +16 -0
  276. package/dist/types/generate.d.ts +62 -5
  277. package/dist/types/index.d.ts +5 -0
  278. package/dist/types/index.js +7 -0
  279. package/dist/types/middleware.d.ts +27 -0
  280. package/dist/types/multimodal.d.ts +35 -2
  281. package/dist/types/music.d.ts +165 -0
  282. package/dist/types/music.js +20 -0
  283. package/dist/types/providers.d.ts +144 -1
  284. package/dist/types/replicate.d.ts +67 -0
  285. package/dist/types/replicate.js +9 -0
  286. package/dist/types/safeFetch.d.ts +15 -0
  287. package/dist/types/safeFetch.js +6 -0
  288. package/dist/types/stream.d.ts +2 -1
  289. package/dist/types/tools.d.ts +13 -0
  290. package/dist/types/video.d.ts +89 -0
  291. package/dist/types/video.js +14 -0
  292. package/dist/utils/avatarProcessor.d.ts +68 -0
  293. package/dist/utils/avatarProcessor.js +171 -0
  294. package/dist/utils/cloneOptions.d.ts +36 -0
  295. package/dist/utils/cloneOptions.js +61 -0
  296. package/dist/utils/lifecycleCallbacks.d.ts +51 -8
  297. package/dist/utils/lifecycleCallbacks.js +82 -26
  298. package/dist/utils/lifecycleTimeout.d.ts +25 -0
  299. package/dist/utils/lifecycleTimeout.js +38 -0
  300. package/dist/utils/logSanitize.d.ts +49 -0
  301. package/dist/utils/logSanitize.js +169 -0
  302. package/dist/utils/loggingFetch.d.ts +29 -0
  303. package/dist/utils/loggingFetch.js +59 -0
  304. package/dist/utils/messageBuilder.js +43 -25
  305. package/dist/utils/modelChoices.js +236 -3
  306. package/dist/utils/musicProcessor.d.ts +67 -0
  307. package/dist/utils/musicProcessor.js +188 -0
  308. package/dist/utils/optionsConversion.js +3 -2
  309. package/dist/utils/parameterValidation.js +14 -4
  310. package/dist/utils/pricing.js +193 -0
  311. package/dist/utils/providerConfig.d.ts +55 -0
  312. package/dist/utils/providerConfig.js +224 -0
  313. package/dist/utils/safeFetch.d.ts +26 -0
  314. package/dist/utils/safeFetch.js +82 -0
  315. package/dist/utils/sizeGuard.d.ts +34 -0
  316. package/dist/utils/sizeGuard.js +44 -0
  317. package/dist/utils/ssrfGuard.d.ts +52 -0
  318. package/dist/utils/ssrfGuard.js +410 -0
  319. package/dist/utils/videoProcessor.d.ts +60 -0
  320. package/dist/utils/videoProcessor.js +200 -0
  321. package/dist/voice/providers/FishAudioTTS.d.ts +27 -0
  322. package/dist/voice/providers/FishAudioTTS.js +182 -0
  323. package/dist/workflow/core/ensembleExecutor.js +26 -9
  324. package/package.json +32 -5
@@ -0,0 +1,52 @@
1
+ /**
2
+ * SSRF Guard — Safe URL Validation Utility
3
+ *
4
+ * Prevents Server-Side Request Forgery by:
5
+ * 1. Enforcing HTTPS-only (no plain HTTP).
6
+ * 2. Normalising encoded IPv4 forms (octal, hex, decimal integer, IPv4-mapped IPv6)
7
+ * to canonical dotted-decimal before rangechecking.
8
+ * 3. Resolving the hostname for **both** A and AAAA families and rejecting
9
+ * requests to RFC 1918 private ranges, loopback, link-local, CGNAT,
10
+ * IPv6 link-local/ULA, and cloud metadata endpoints
11
+ * (AWS / GCP / Azure / Alibaba).
12
+ * 4. Re-throwing on DNS failure rather than silently allowing the request.
13
+ *
14
+ * **DNS rebinding residual race:** `assertSafeUrl` validates the IP at the
15
+ * moment of the lookup. If the resolver returns a public IP here and a private
16
+ * IP at the actual `fetch()` call, the guard is bypassed. To eliminate the
17
+ * race, use the companion `safeDownload` helper in `safeFetch.ts` which pins
18
+ * the resolved IP onto the request via an undici Agent dispatcher.
19
+ *
20
+ * Usage:
21
+ * await assertSafeUrl(url);
22
+ * // ... or, for actual downloads: ...
23
+ * await safeDownload(url, { maxBytes, label });
24
+ *
25
+ * @module utils/ssrfGuard
26
+ */
27
+ /**
28
+ * Assert that `url` is safe to fetch server-side.
29
+ *
30
+ * @throws {Error} when the URL is non-HTTPS, parses as a blocked IP literal,
31
+ * or resolves (A or AAAA) to a blocked IP. **Also throws on DNS lookup
32
+ * failure** (the previous behaviour of silently allowing was a bypass —
33
+ * an attacker-controlled resolver could force NXDOMAIN here and a private
34
+ * IP at the actual fetch).
35
+ */
36
+ export declare function assertSafeUrl(url: string): Promise<void>;
37
+ /**
38
+ * Validate `url` and return the resolved IP that should be used for the
39
+ * actual fetch (companion to `safeFetch.ts:safeDownload`).
40
+ *
41
+ * For IP-literal hosts, returns the normalised IP and family. For hostnames,
42
+ * returns the first acceptable IP from the resolver. Same throw semantics as
43
+ * {@link assertSafeUrl}.
44
+ *
45
+ * This is the canonical entry point for binary downloads where DNS-rebinding
46
+ * pinning matters — see `safeFetch.ts`.
47
+ */
48
+ export declare function validateAndResolveUrl(url: string): Promise<{
49
+ url: string;
50
+ ip: string;
51
+ family: 4 | 6;
52
+ }>;
@@ -0,0 +1,410 @@
1
+ /**
2
+ * SSRF Guard — Safe URL Validation Utility
3
+ *
4
+ * Prevents Server-Side Request Forgery by:
5
+ * 1. Enforcing HTTPS-only (no plain HTTP).
6
+ * 2. Normalising encoded IPv4 forms (octal, hex, decimal integer, IPv4-mapped IPv6)
7
+ * to canonical dotted-decimal before rangechecking.
8
+ * 3. Resolving the hostname for **both** A and AAAA families and rejecting
9
+ * requests to RFC 1918 private ranges, loopback, link-local, CGNAT,
10
+ * IPv6 link-local/ULA, and cloud metadata endpoints
11
+ * (AWS / GCP / Azure / Alibaba).
12
+ * 4. Re-throwing on DNS failure rather than silently allowing the request.
13
+ *
14
+ * **DNS rebinding residual race:** `assertSafeUrl` validates the IP at the
15
+ * moment of the lookup. If the resolver returns a public IP here and a private
16
+ * IP at the actual `fetch()` call, the guard is bypassed. To eliminate the
17
+ * race, use the companion `safeDownload` helper in `safeFetch.ts` which pins
18
+ * the resolved IP onto the request via an undici Agent dispatcher.
19
+ *
20
+ * Usage:
21
+ * await assertSafeUrl(url);
22
+ * // ... or, for actual downloads: ...
23
+ * await safeDownload(url, { maxBytes, label });
24
+ *
25
+ * @module utils/ssrfGuard
26
+ */
27
+ import { lookup } from "node:dns/promises";
28
+ import { isIP } from "node:net";
29
+ /**
30
+ * Blocked IPv4 CIDRs.
31
+ *
32
+ * Each entry is a `[network, prefix]` pair. Membership is computed by
33
+ * bitwise comparison of the 32-bit address vs the masked network.
34
+ */
35
+ const BLOCKED_V4_CIDRS = [
36
+ ["0.0.0.0", 8], // "this network"
37
+ ["10.0.0.0", 8], // RFC 1918
38
+ ["100.64.0.0", 10], // CGNAT (RFC 6598)
39
+ ["127.0.0.0", 8], // loopback
40
+ ["169.254.0.0", 16], // link-local (AWS/GCP/Azure metadata + APIPA)
41
+ ["172.16.0.0", 12], // RFC 1918
42
+ ["192.0.0.0", 24], // protocol assignments
43
+ ["192.168.0.0", 16], // RFC 1918
44
+ ["198.18.0.0", 15], // benchmarking
45
+ ["100.100.100.200", 32], // Alibaba Cloud metadata (NOT in 100.64/10 CGNAT)
46
+ ["224.0.0.0", 4], // multicast
47
+ ["240.0.0.0", 4], // reserved
48
+ ];
49
+ /**
50
+ * Blocked IPv6 prefixes.
51
+ *
52
+ * Compared by lowercase prefix match on the expanded address form.
53
+ * (`expandIPv6` normalizes `::1` to `0000:0000:...:0001` for unambiguous
54
+ * prefix matching.)
55
+ */
56
+ const BLOCKED_V6_PREFIXES = [
57
+ "0000:0000:0000:0000:0000:0000:0000:0000", // :: (unspecified)
58
+ "0000:0000:0000:0000:0000:0000:0000:0001", // ::1 (loopback)
59
+ "fc", // fc00::/7 unique-local (covers fc and fd prefixes)
60
+ "fd", // fd00::/8
61
+ "fe8", // fe80::/10 link-local (covers fe8/fe9/fea/feb)
62
+ "fe9",
63
+ "fea",
64
+ "feb",
65
+ ];
66
+ function parseOctet(s) {
67
+ if (s.length === 0) {
68
+ return null;
69
+ }
70
+ if (/^0x[0-9a-f]+$/i.test(s)) {
71
+ return parseInt(s.slice(2), 16);
72
+ }
73
+ // Plain "0" is valid decimal zero; leading-zero forms (`0177`) are octal
74
+ if (s.length > 1 && s.startsWith("0") && /^0[0-7]+$/.test(s)) {
75
+ return parseInt(s.slice(1), 8);
76
+ }
77
+ if (/^\d+$/.test(s)) {
78
+ return parseInt(s, 10);
79
+ }
80
+ return null;
81
+ }
82
+ /**
83
+ * Normalize any IPv4-like host string to canonical dotted-decimal form, or
84
+ * return `null` if it's not parseable as IPv4.
85
+ *
86
+ * Handles:
87
+ * - 127.0.0.1 (canonical)
88
+ * - 0177.0.0.1 (octal octets)
89
+ * - 0x7f.0.0.1 (hex octets)
90
+ * - 0x7f000001 (hex integer)
91
+ * - 2130706433 (decimal integer)
92
+ * - 0177.0.0.1 (mixed encodings)
93
+ */
94
+ function normalizeIPv4(host) {
95
+ if (host.length === 0) {
96
+ return null;
97
+ }
98
+ const parts = host.split(".");
99
+ if (parts.length === 4) {
100
+ const octets = parts.map(parseOctet);
101
+ if (octets.some((o) => o === null || o < 0 || o > 255)) {
102
+ return null;
103
+ }
104
+ return octets.join(".");
105
+ }
106
+ // Single integer form: 2130706433 or 0x7f000001
107
+ if (parts.length === 1) {
108
+ let n;
109
+ if (/^0x[0-9a-f]+$/i.test(host)) {
110
+ n = parseInt(host.slice(2), 16);
111
+ }
112
+ else if (/^\d+$/.test(host)) {
113
+ n = parseInt(host, 10);
114
+ }
115
+ else {
116
+ return null;
117
+ }
118
+ if (Number.isNaN(n) || n < 0 || n > 0xffffffff) {
119
+ return null;
120
+ }
121
+ return [
122
+ (n >>> 24) & 0xff,
123
+ (n >>> 16) & 0xff,
124
+ (n >>> 8) & 0xff,
125
+ n & 0xff,
126
+ ].join(".");
127
+ }
128
+ return null;
129
+ }
130
+ /**
131
+ * Expand a compressed IPv6 address (`::1`) to full 8-group form
132
+ * (`0000:0000:0000:0000:0000:0000:0000:0001`) for unambiguous prefix matching.
133
+ *
134
+ * Returns the expanded lowercased string, or `null` if `host` isn't IPv6.
135
+ */
136
+ function expandIPv6(host) {
137
+ if (isIP(host) !== 6) {
138
+ return null;
139
+ }
140
+ // Handle IPv4-mapped IPv6: ::ffff:127.0.0.1 → expand the IPv4 part to two groups
141
+ const v4MappedMatch = host.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
142
+ let groups;
143
+ if (v4MappedMatch) {
144
+ const v4 = normalizeIPv4(v4MappedMatch[1]);
145
+ if (!v4) {
146
+ return null;
147
+ }
148
+ const v4Octets = v4.split(".").map((n) => parseInt(n, 10));
149
+ const high = ((v4Octets[0] << 8) | v4Octets[1]).toString(16);
150
+ const low = ((v4Octets[2] << 8) | v4Octets[3]).toString(16);
151
+ groups = ["0", "0", "0", "0", "0", "ffff", high, low];
152
+ }
153
+ else {
154
+ const [head, tail = ""] = host.split("::");
155
+ const headParts = head ? head.split(":") : [];
156
+ const tailParts = tail ? tail.split(":") : [];
157
+ const missing = 8 - headParts.length - tailParts.length;
158
+ if (missing < 0) {
159
+ return null;
160
+ }
161
+ groups = [...headParts, ...Array(missing).fill("0"), ...tailParts];
162
+ }
163
+ if (groups.length !== 8) {
164
+ return null;
165
+ }
166
+ return groups.map((g) => g.toLowerCase().padStart(4, "0")).join(":");
167
+ }
168
+ /**
169
+ * If `host` is an IPv4-mapped IPv6 address, return the embedded IPv4 in
170
+ * canonical dotted-decimal form, or `null` otherwise.
171
+ *
172
+ * Handles both forms:
173
+ * - dotted-decimal IPv4 part: `::ffff:127.0.0.1`
174
+ * - hex-encoded IPv4 part: `::ffff:7f00:1` / `::ffff:7f00:0001`
175
+ *
176
+ * Node's `URL` parser normalises bracketed `::ffff:127.0.0.1` to
177
+ * `[::ffff:7f00:1]`, so the hex form is the one we actually receive after
178
+ * `URL.hostname` + bracket stripping. Both paths must be covered.
179
+ */
180
+ function extractIPv4FromMapped(host) {
181
+ // Form 1: `::ffff:127.0.0.1`
182
+ const dottedMatch = host.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
183
+ if (dottedMatch) {
184
+ return normalizeIPv4(dottedMatch[1]);
185
+ }
186
+ // Form 2: `::ffff:7f00:1` (two hex groups, optionally zero-padded)
187
+ const hexMatch = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);
188
+ if (hexMatch) {
189
+ const high = parseInt(hexMatch[1], 16);
190
+ const low = parseInt(hexMatch[2], 16);
191
+ if (Number.isNaN(high) ||
192
+ Number.isNaN(low) ||
193
+ high > 0xffff ||
194
+ low > 0xffff) {
195
+ return null;
196
+ }
197
+ return [
198
+ (high >> 8) & 0xff,
199
+ high & 0xff,
200
+ (low >> 8) & 0xff,
201
+ low & 0xff,
202
+ ].join(".");
203
+ }
204
+ return null;
205
+ }
206
+ function ipv4ToInt(ip) {
207
+ const [a, b, c, d] = ip.split(".").map((n) => parseInt(n, 10));
208
+ return ((a << 24) | (b << 16) | (c << 8) | d) >>> 0;
209
+ }
210
+ function isBlockedIPv4(ip) {
211
+ const ipInt = ipv4ToInt(ip);
212
+ for (const [network, prefix] of BLOCKED_V4_CIDRS) {
213
+ const netInt = ipv4ToInt(network);
214
+ const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
215
+ if ((ipInt & mask) === (netInt & mask)) {
216
+ return true;
217
+ }
218
+ }
219
+ return false;
220
+ }
221
+ function isBlockedIPv6(expanded) {
222
+ return BLOCKED_V6_PREFIXES.some((prefix) => {
223
+ if (prefix.length === 39) {
224
+ // full-form exact match
225
+ return expanded === prefix;
226
+ }
227
+ return expanded.startsWith(prefix);
228
+ });
229
+ }
230
+ /**
231
+ * Strip the IPv6 brackets that `URL.hostname` returns for IPv6 hosts
232
+ * (Node behaviour varies — sometimes `[::1]`, sometimes `::1`).
233
+ */
234
+ function stripBrackets(host) {
235
+ if (host.startsWith("[") && host.endsWith("]")) {
236
+ return host.slice(1, -1);
237
+ }
238
+ return host;
239
+ }
240
+ /**
241
+ * Internal check: given a host string (already bracket-stripped, lowercased),
242
+ * return a reject reason or null if safe.
243
+ *
244
+ * Detects IP literals via every encoded form. Does NOT do DNS — that's the
245
+ * caller's job.
246
+ */
247
+ function checkHostLiteral(host) {
248
+ // IPv4 (including encoded forms)
249
+ const v4 = normalizeIPv4(host);
250
+ if (v4) {
251
+ if (isBlockedIPv4(v4)) {
252
+ return `IPv4 ${host} → ${v4} is in a blocked range`;
253
+ }
254
+ return null;
255
+ }
256
+ // IPv6 (including IPv4-mapped)
257
+ if (host.includes(":")) {
258
+ // First, check IPv4-mapped: convert and re-check via v4 path
259
+ const v4FromMapped = extractIPv4FromMapped(host);
260
+ if (v4FromMapped) {
261
+ if (isBlockedIPv4(v4FromMapped)) {
262
+ return `IPv4-mapped IPv6 ${host} → ${v4FromMapped} is in a blocked range`;
263
+ }
264
+ return null;
265
+ }
266
+ const expanded = expandIPv6(host);
267
+ if (!expanded) {
268
+ return `IPv6 ${host} could not be parsed`;
269
+ }
270
+ if (isBlockedIPv6(expanded)) {
271
+ return `IPv6 ${host} is in a blocked range`;
272
+ }
273
+ return null;
274
+ }
275
+ // Not an IP literal — caller should fall through to DNS resolution
276
+ return "not-an-ip";
277
+ }
278
+ /**
279
+ * Assert that `url` is safe to fetch server-side.
280
+ *
281
+ * @throws {Error} when the URL is non-HTTPS, parses as a blocked IP literal,
282
+ * or resolves (A or AAAA) to a blocked IP. **Also throws on DNS lookup
283
+ * failure** (the previous behaviour of silently allowing was a bypass —
284
+ * an attacker-controlled resolver could force NXDOMAIN here and a private
285
+ * IP at the actual fetch).
286
+ */
287
+ export async function assertSafeUrl(url) {
288
+ let parsed;
289
+ try {
290
+ parsed = new URL(url);
291
+ }
292
+ catch {
293
+ throw new Error(`Invalid URL: "${url}"`);
294
+ }
295
+ if (parsed.protocol !== "https:") {
296
+ throw new Error(`Only HTTPS URLs are permitted; got "${parsed.protocol}//" in "${url}"`);
297
+ }
298
+ const host = stripBrackets(parsed.hostname).toLowerCase();
299
+ // First, try as an IP literal (covers encoded forms + IPv4-mapped IPv6).
300
+ const literalCheck = checkHostLiteral(host);
301
+ if (literalCheck === null) {
302
+ return; // routable IP literal — safe
303
+ }
304
+ if (literalCheck !== "not-an-ip") {
305
+ throw new Error(`URL "${url}" rejected: ${literalCheck}`);
306
+ }
307
+ // Hostname — resolve BOTH A and AAAA. Reject if either family yields a
308
+ // blocked address (closes off the "publish AAAA public, A private" attack).
309
+ const [a, aaaa] = await Promise.allSettled([
310
+ lookup(host, { family: 4, all: true }),
311
+ lookup(host, { family: 6, all: true }),
312
+ ]);
313
+ const v4Addresses = [];
314
+ const v6Addresses = [];
315
+ let hadAnySuccess = false;
316
+ if (a.status === "fulfilled") {
317
+ hadAnySuccess = true;
318
+ for (const entry of a.value) {
319
+ v4Addresses.push(entry.address);
320
+ }
321
+ }
322
+ if (aaaa.status === "fulfilled") {
323
+ hadAnySuccess = true;
324
+ for (const entry of aaaa.value) {
325
+ v6Addresses.push(entry.address);
326
+ }
327
+ }
328
+ if (!hadAnySuccess) {
329
+ // BOTH lookups failed — the host doesn't resolve at all. Re-throw with
330
+ // a clear message rather than silently allowing the fetch (the prior
331
+ // behaviour, which is the DNS-rebinding bypass).
332
+ const aErr = a.status === "rejected"
333
+ ? a.reason instanceof Error
334
+ ? a.reason.message
335
+ : String(a.reason)
336
+ : "ok";
337
+ const aaaaErr = aaaa.status === "rejected"
338
+ ? aaaa.reason instanceof Error
339
+ ? aaaa.reason.message
340
+ : String(aaaa.reason)
341
+ : "ok";
342
+ throw new Error(`URL "${url}" rejected: hostname ${host} could not be resolved (A: ${aErr}; AAAA: ${aaaaErr})`);
343
+ }
344
+ for (const addr of v4Addresses) {
345
+ if (isBlockedIPv4(addr)) {
346
+ throw new Error(`URL "${url}" rejected: hostname ${host} resolves to ${addr} (IPv4 in blocked range)`);
347
+ }
348
+ }
349
+ for (const addr of v6Addresses) {
350
+ // Re-use the literal check pipeline for IPv6 so IPv4-mapped resolved
351
+ // addresses are caught.
352
+ const reason = checkHostLiteral(addr.toLowerCase());
353
+ if (reason && reason !== "not-an-ip") {
354
+ throw new Error(`URL "${url}" rejected: hostname ${host} resolves to ${addr} (IPv6 ${reason})`);
355
+ }
356
+ }
357
+ }
358
+ /**
359
+ * Validate `url` and return the resolved IP that should be used for the
360
+ * actual fetch (companion to `safeFetch.ts:safeDownload`).
361
+ *
362
+ * For IP-literal hosts, returns the normalised IP and family. For hostnames,
363
+ * returns the first acceptable IP from the resolver. Same throw semantics as
364
+ * {@link assertSafeUrl}.
365
+ *
366
+ * This is the canonical entry point for binary downloads where DNS-rebinding
367
+ * pinning matters — see `safeFetch.ts`.
368
+ */
369
+ export async function validateAndResolveUrl(url) {
370
+ let parsed;
371
+ try {
372
+ parsed = new URL(url);
373
+ }
374
+ catch {
375
+ throw new Error(`Invalid URL: "${url}"`);
376
+ }
377
+ if (parsed.protocol !== "https:") {
378
+ throw new Error(`Only HTTPS URLs are permitted; got "${parsed.protocol}//" in "${url}"`);
379
+ }
380
+ const host = stripBrackets(parsed.hostname).toLowerCase();
381
+ // IP literal — normalise + check, return canonical form
382
+ const v4 = normalizeIPv4(host);
383
+ if (v4) {
384
+ if (isBlockedIPv4(v4)) {
385
+ throw new Error(`URL "${url}" rejected: IPv4 ${host} → ${v4} is in a blocked range`);
386
+ }
387
+ return { url, ip: v4, family: 4 };
388
+ }
389
+ if (host.includes(":")) {
390
+ const v4FromMapped = extractIPv4FromMapped(host);
391
+ if (v4FromMapped) {
392
+ if (isBlockedIPv4(v4FromMapped)) {
393
+ throw new Error(`URL "${url}" rejected: IPv4-mapped IPv6 ${host} → ${v4FromMapped} is in a blocked range`);
394
+ }
395
+ return { url, ip: v4FromMapped, family: 4 };
396
+ }
397
+ const expanded = expandIPv6(host);
398
+ if (!expanded) {
399
+ throw new Error(`URL "${url}" rejected: IPv6 ${host} could not be parsed`);
400
+ }
401
+ if (isBlockedIPv6(expanded)) {
402
+ throw new Error(`URL "${url}" rejected: IPv6 ${host} is in a blocked range`);
403
+ }
404
+ return { url, ip: host, family: 6 };
405
+ }
406
+ // Hostname — resolve and pick a safe address
407
+ await assertSafeUrl(url);
408
+ const result = await lookup(host);
409
+ return { url, ip: result.address, family: result.family };
410
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Video Generation Processing Utility
3
+ *
4
+ * Central registry + dispatch for video-generation handlers across
5
+ * providers (Vertex Veo, Kling, Runway, Replicate-hosted models, etc.).
6
+ *
7
+ * Mirrors the static-handler-registry pattern established by
8
+ * `TTSProcessor` (`utils/ttsProcessor.ts`) and `STTProcessor`
9
+ * (`utils/sttProcessor.ts`).
10
+ *
11
+ * @module utils/videoProcessor
12
+ */
13
+ import { VIDEO_ERROR_CODES } from "../constants/videoErrors.js";
14
+ import type { VideoGenerationResult, VideoHandler, VideoOutputOptions, VideoTransitionOptions } from "../types/index.js";
15
+ import { VideoError } from "../adapters/video/vertexVideoHandler.js";
16
+ export { VideoError, VIDEO_ERROR_CODES };
17
+ /**
18
+ * Static processor managing the video handler registry.
19
+ *
20
+ * Handlers register themselves during `ProviderRegistry._doRegister()`
21
+ * via `VideoProcessor.registerHandler(name, instance)`. Lookups are
22
+ * O(1) on a normalised lower-case provider key.
23
+ */
24
+ export declare class VideoProcessor {
25
+ private static readonly handlers;
26
+ /**
27
+ * Register a video handler for a specific provider.
28
+ */
29
+ static registerHandler(providerName: string, handler: VideoHandler): void;
30
+ /**
31
+ * Check if a provider has a registered video handler.
32
+ */
33
+ static supports(providerName: string): boolean;
34
+ /**
35
+ * List the names of all registered providers.
36
+ */
37
+ static listProviders(): string[];
38
+ private static getHandler;
39
+ private static buildSpanAttributes;
40
+ /**
41
+ * Generate a single video clip via the registered handler.
42
+ *
43
+ * @param provider - Registered provider name (e.g. "vertex", "kling")
44
+ * @param image - Source image buffer
45
+ * @param prompt - Text prompt describing the desired motion / content
46
+ * @param options - Resolution / length / aspect-ratio / audio options
47
+ * @param region - Optional region override (Vertex location, etc.)
48
+ * @throws VideoError on registry miss, handler-not-configured, or
49
+ * generation failure
50
+ */
51
+ static generate(provider: string, image: Buffer, prompt: string, options: VideoOutputOptions, region?: string): Promise<VideoGenerationResult>;
52
+ /**
53
+ * Generate a transition clip via the registered handler (Director Mode).
54
+ *
55
+ * Providers without first-and-last-frame interpolation surface a typed
56
+ * `TRANSITION_NOT_SUPPORTED` error here; callers should fall back to
57
+ * generating a regular clip with a transition prompt.
58
+ */
59
+ static generateTransition(provider: string, firstFrame: Buffer, lastFrame: Buffer, prompt: string, options?: VideoTransitionOptions, region?: string): Promise<Buffer>;
60
+ }