@nodetool-ai/websocket 0.7.0-rc.22 → 0.8.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 (1020) hide show
  1. package/README.md +50 -0
  2. package/dist/.nodetool-build-stamp +1 -0
  3. package/dist/agent/run-node-tool.d.ts +39 -0
  4. package/dist/agent/run-node-tool.d.ts.map +1 -0
  5. package/dist/agent/run-node-tool.js +55 -0
  6. package/dist/agent/run-node-tool.js.map +1 -0
  7. package/dist/chat-prediction-forwarder.d.ts +13 -0
  8. package/dist/chat-prediction-forwarder.d.ts.map +1 -0
  9. package/dist/chat-prediction-forwarder.js +22 -0
  10. package/dist/chat-prediction-forwarder.js.map +1 -0
  11. package/dist/chat-tool-call-repair.d.ts +45 -0
  12. package/dist/chat-tool-call-repair.d.ts.map +1 -0
  13. package/dist/chat-tool-call-repair.js +100 -0
  14. package/dist/chat-tool-call-repair.js.map +1 -0
  15. package/dist/chat-turn-registry.d.ts +169 -0
  16. package/dist/chat-turn-registry.d.ts.map +1 -0
  17. package/dist/chat-turn-registry.js +326 -0
  18. package/dist/chat-turn-registry.js.map +1 -0
  19. package/dist/collection-api.d.ts +2 -2
  20. package/dist/collection-api.d.ts.map +1 -1
  21. package/dist/collection-api.js +44 -13
  22. package/dist/collection-api.js.map +1 -1
  23. package/dist/configured-providers.d.ts +52 -0
  24. package/dist/configured-providers.d.ts.map +1 -0
  25. package/dist/configured-providers.js +85 -0
  26. package/dist/configured-providers.js.map +1 -0
  27. package/dist/cors.d.ts +22 -0
  28. package/dist/cors.d.ts.map +1 -0
  29. package/dist/cors.js +91 -0
  30. package/dist/cors.js.map +1 -0
  31. package/dist/credit-gate.d.ts +36 -0
  32. package/dist/credit-gate.d.ts.map +1 -0
  33. package/dist/credit-gate.js +76 -0
  34. package/dist/credit-gate.js.map +1 -0
  35. package/dist/custom-providers.d.ts +40 -0
  36. package/dist/custom-providers.d.ts.map +1 -0
  37. package/dist/custom-providers.js +134 -0
  38. package/dist/custom-providers.js.map +1 -0
  39. package/dist/debug-sessions.d.ts +10 -0
  40. package/dist/debug-sessions.d.ts.map +1 -0
  41. package/dist/debug-sessions.js +9 -0
  42. package/dist/debug-sessions.js.map +1 -0
  43. package/dist/drain.d.ts +34 -0
  44. package/dist/drain.d.ts.map +1 -0
  45. package/dist/drain.js +68 -0
  46. package/dist/drain.js.map +1 -0
  47. package/dist/e2e-server.d.ts +30 -0
  48. package/dist/e2e-server.d.ts.map +1 -0
  49. package/dist/e2e-server.js +111 -0
  50. package/dist/e2e-server.js.map +1 -0
  51. package/dist/example-workflows.d.ts +20 -0
  52. package/dist/example-workflows.d.ts.map +1 -0
  53. package/dist/example-workflows.js +123 -0
  54. package/dist/example-workflows.js.map +1 -0
  55. package/dist/extension-cdp-bridge.d.ts +85 -0
  56. package/dist/extension-cdp-bridge.d.ts.map +1 -0
  57. package/dist/extension-cdp-bridge.js +163 -0
  58. package/dist/extension-cdp-bridge.js.map +1 -0
  59. package/dist/external-mcp.d.ts +58 -0
  60. package/dist/external-mcp.d.ts.map +1 -0
  61. package/dist/external-mcp.js +288 -0
  62. package/dist/external-mcp.js.map +1 -0
  63. package/dist/fake-runtime.d.ts +114 -0
  64. package/dist/fake-runtime.d.ts.map +1 -0
  65. package/dist/fake-runtime.js +270 -0
  66. package/dist/fake-runtime.js.map +1 -0
  67. package/dist/file-api.d.ts +4 -7
  68. package/dist/file-api.d.ts.map +1 -1
  69. package/dist/file-api.js +122 -39
  70. package/dist/file-api.js.map +1 -1
  71. package/dist/frontend-renderer-registry.d.ts +75 -0
  72. package/dist/frontend-renderer-registry.d.ts.map +1 -0
  73. package/dist/frontend-renderer-registry.js +111 -0
  74. package/dist/frontend-renderer-registry.js.map +1 -0
  75. package/dist/generation-recovery.d.ts +13 -0
  76. package/dist/generation-recovery.d.ts.map +1 -0
  77. package/dist/generation-recovery.js +197 -0
  78. package/dist/generation-recovery.js.map +1 -0
  79. package/dist/headless-job-runner.d.ts +71 -0
  80. package/dist/headless-job-runner.d.ts.map +1 -0
  81. package/dist/headless-job-runner.js +198 -0
  82. package/dist/headless-job-runner.js.map +1 -0
  83. package/dist/http-api.d.ts +157 -24
  84. package/dist/http-api.d.ts.map +1 -1
  85. package/dist/http-api.js +613 -646
  86. package/dist/http-api.js.map +1 -1
  87. package/dist/http-body-schemas.d.ts +109 -0
  88. package/dist/http-body-schemas.d.ts.map +1 -0
  89. package/dist/http-body-schemas.js +146 -0
  90. package/dist/http-body-schemas.js.map +1 -0
  91. package/dist/index.d.ts +11 -3
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +11 -3
  94. package/dist/index.js.map +1 -1
  95. package/dist/job-control.d.ts +57 -0
  96. package/dist/job-control.d.ts.map +1 -0
  97. package/dist/job-control.js +134 -0
  98. package/dist/job-control.js.map +1 -0
  99. package/dist/job-queue.d.ts +47 -0
  100. package/dist/job-queue.d.ts.map +1 -0
  101. package/dist/job-queue.js +52 -0
  102. package/dist/job-queue.js.map +1 -0
  103. package/dist/job-run-registry.d.ts +176 -0
  104. package/dist/job-run-registry.d.ts.map +1 -0
  105. package/dist/job-run-registry.js +312 -0
  106. package/dist/job-run-registry.js.map +1 -0
  107. package/dist/lib/app-build-service.d.ts +16 -0
  108. package/dist/lib/app-build-service.d.ts.map +1 -0
  109. package/dist/lib/app-build-service.js +39 -0
  110. package/dist/lib/app-build-service.js.map +1 -0
  111. package/dist/lib/app-debug-service.d.ts +16 -0
  112. package/dist/lib/app-debug-service.d.ts.map +1 -0
  113. package/dist/lib/app-debug-service.js +48 -0
  114. package/dist/lib/app-debug-service.js.map +1 -0
  115. package/dist/lib/app-deployment-service.d.ts +82 -0
  116. package/dist/lib/app-deployment-service.d.ts.map +1 -0
  117. package/dist/lib/app-deployment-service.js +282 -0
  118. package/dist/lib/app-deployment-service.js.map +1 -0
  119. package/dist/lib/app-run-server.d.ts +7 -0
  120. package/dist/lib/app-run-server.d.ts.map +1 -0
  121. package/dist/lib/app-run-server.js +6 -0
  122. package/dist/lib/app-run-server.js.map +1 -0
  123. package/dist/lib/app-session-scope.d.ts +91 -0
  124. package/dist/lib/app-session-scope.d.ts.map +1 -0
  125. package/dist/lib/app-session-scope.js +91 -0
  126. package/dist/lib/app-session-scope.js.map +1 -0
  127. package/dist/lib/applications-service.d.ts +173 -0
  128. package/dist/lib/applications-service.d.ts.map +1 -0
  129. package/dist/lib/applications-service.js +491 -0
  130. package/dist/lib/applications-service.js.map +1 -0
  131. package/dist/lib/asset-duration.d.ts +7 -0
  132. package/dist/lib/asset-duration.d.ts.map +1 -0
  133. package/dist/lib/asset-duration.js +24 -0
  134. package/dist/lib/asset-duration.js.map +1 -0
  135. package/dist/lib/asset-export.d.ts +34 -0
  136. package/dist/lib/asset-export.d.ts.map +1 -0
  137. package/dist/lib/asset-export.js +113 -0
  138. package/dist/lib/asset-export.js.map +1 -0
  139. package/dist/lib/asset-model-interface.d.ts +41 -0
  140. package/dist/lib/asset-model-interface.d.ts.map +1 -0
  141. package/dist/lib/asset-model-interface.js +74 -0
  142. package/dist/lib/asset-model-interface.js.map +1 -0
  143. package/dist/lib/asset-paths.d.ts +52 -0
  144. package/dist/lib/asset-paths.d.ts.map +1 -1
  145. package/dist/lib/asset-paths.js +145 -1
  146. package/dist/lib/asset-paths.js.map +1 -1
  147. package/dist/lib/asset-response.d.ts +12 -0
  148. package/dist/lib/asset-response.d.ts.map +1 -0
  149. package/dist/lib/asset-response.js +88 -0
  150. package/dist/lib/asset-response.js.map +1 -0
  151. package/dist/lib/audio-peaks.d.ts +23 -0
  152. package/dist/lib/audio-peaks.d.ts.map +1 -0
  153. package/dist/lib/audio-peaks.js +331 -0
  154. package/dist/lib/audio-peaks.js.map +1 -0
  155. package/dist/lib/bridge.d.ts +9 -1
  156. package/dist/lib/bridge.d.ts.map +1 -1
  157. package/dist/lib/bridge.js +27 -4
  158. package/dist/lib/bridge.js.map +1 -1
  159. package/dist/lib/compression.d.ts +3 -0
  160. package/dist/lib/compression.d.ts.map +1 -0
  161. package/dist/lib/compression.js +3 -0
  162. package/dist/lib/compression.js.map +1 -0
  163. package/dist/lib/document-model-interfaces.d.ts +30 -0
  164. package/dist/lib/document-model-interfaces.d.ts.map +1 -0
  165. package/dist/lib/document-model-interfaces.js +295 -0
  166. package/dist/lib/document-model-interfaces.js.map +1 -0
  167. package/dist/lib/env-config.d.ts +9 -0
  168. package/dist/lib/env-config.d.ts.map +1 -0
  169. package/dist/lib/env-config.js +18 -0
  170. package/dist/lib/env-config.js.map +1 -0
  171. package/dist/lib/example-apps.d.ts +43 -0
  172. package/dist/lib/example-apps.d.ts.map +1 -0
  173. package/dist/lib/example-apps.js +145 -0
  174. package/dist/lib/example-apps.js.map +1 -0
  175. package/dist/lib/example-games.d.ts +5 -0
  176. package/dist/lib/example-games.d.ts.map +1 -0
  177. package/dist/lib/example-games.js +13 -0
  178. package/dist/lib/example-games.js.map +1 -0
  179. package/dist/lib/example-recipes.d.ts +33 -0
  180. package/dist/lib/example-recipes.d.ts.map +1 -0
  181. package/dist/lib/example-recipes.js +213 -0
  182. package/dist/lib/example-recipes.js.map +1 -0
  183. package/dist/lib/example-storyboards.d.ts +40 -0
  184. package/dist/lib/example-storyboards.d.ts.map +1 -0
  185. package/dist/lib/example-storyboards.js +131 -0
  186. package/dist/lib/example-storyboards.js.map +1 -0
  187. package/dist/lib/example-thumbnail.d.ts +21 -0
  188. package/dist/lib/example-thumbnail.d.ts.map +1 -0
  189. package/dist/lib/example-thumbnail.js +50 -0
  190. package/dist/lib/example-thumbnail.js.map +1 -0
  191. package/dist/lib/example-timelines.d.ts +9 -0
  192. package/dist/lib/example-timelines.d.ts.map +1 -0
  193. package/dist/lib/example-timelines.js +25 -0
  194. package/dist/lib/example-timelines.js.map +1 -0
  195. package/dist/lib/extension-dist.d.ts +24 -0
  196. package/dist/lib/extension-dist.d.ts.map +1 -0
  197. package/dist/lib/extension-dist.js +67 -0
  198. package/dist/lib/extension-dist.js.map +1 -0
  199. package/dist/lib/external-asset-lookup.d.ts +30 -0
  200. package/dist/lib/external-asset-lookup.d.ts.map +1 -0
  201. package/dist/lib/external-asset-lookup.js +99 -0
  202. package/dist/lib/external-asset-lookup.js.map +1 -0
  203. package/dist/lib/external-assets.d.ts +13 -0
  204. package/dist/lib/external-assets.d.ts.map +1 -0
  205. package/dist/lib/external-assets.js +46 -0
  206. package/dist/lib/external-assets.js.map +1 -0
  207. package/dist/lib/fly-replay.d.ts +34 -0
  208. package/dist/lib/fly-replay.d.ts.map +1 -0
  209. package/dist/lib/fly-replay.js +111 -0
  210. package/dist/lib/fly-replay.js.map +1 -0
  211. package/dist/lib/http-rate-limit.d.ts +32 -0
  212. package/dist/lib/http-rate-limit.d.ts.map +1 -0
  213. package/dist/lib/http-rate-limit.js +47 -0
  214. package/dist/lib/http-rate-limit.js.map +1 -0
  215. package/dist/lib/instance-id.d.ts +25 -0
  216. package/dist/lib/instance-id.d.ts.map +1 -0
  217. package/dist/lib/instance-id.js +44 -0
  218. package/dist/lib/instance-id.js.map +1 -0
  219. package/dist/lib/link-codes.d.ts +82 -0
  220. package/dist/lib/link-codes.d.ts.map +1 -0
  221. package/dist/lib/link-codes.js +100 -0
  222. package/dist/lib/link-codes.js.map +1 -0
  223. package/dist/lib/local-asset-upload.d.ts +5 -0
  224. package/dist/lib/local-asset-upload.d.ts.map +1 -0
  225. package/dist/lib/local-asset-upload.js +69 -0
  226. package/dist/lib/local-asset-upload.js.map +1 -0
  227. package/dist/lib/local-file-access.d.ts +31 -0
  228. package/dist/lib/local-file-access.d.ts.map +1 -0
  229. package/dist/lib/local-file-access.js +169 -0
  230. package/dist/lib/local-file-access.js.map +1 -0
  231. package/dist/lib/localhost-trust.d.ts +59 -0
  232. package/dist/lib/localhost-trust.d.ts.map +1 -0
  233. package/dist/lib/localhost-trust.js +139 -0
  234. package/dist/lib/localhost-trust.js.map +1 -0
  235. package/dist/lib/mcp-mount.d.ts +25 -0
  236. package/dist/lib/mcp-mount.d.ts.map +1 -0
  237. package/dist/lib/mcp-mount.js +32 -0
  238. package/dist/lib/mcp-mount.js.map +1 -0
  239. package/dist/lib/media-size.d.ts +21 -0
  240. package/dist/lib/media-size.d.ts.map +1 -0
  241. package/dist/lib/media-size.js +48 -0
  242. package/dist/lib/media-size.js.map +1 -0
  243. package/dist/lib/media.d.ts +38 -0
  244. package/dist/lib/media.d.ts.map +1 -0
  245. package/dist/lib/media.js +146 -0
  246. package/dist/lib/media.js.map +1 -0
  247. package/dist/lib/package-asset-export.d.ts +83 -0
  248. package/dist/lib/package-asset-export.d.ts.map +1 -0
  249. package/dist/lib/package-asset-export.js +212 -0
  250. package/dist/lib/package-asset-export.js.map +1 -0
  251. package/dist/lib/package-assets.d.ts +41 -0
  252. package/dist/lib/package-assets.d.ts.map +1 -0
  253. package/dist/lib/package-assets.js +155 -0
  254. package/dist/lib/package-assets.js.map +1 -0
  255. package/dist/lib/project-delete.d.ts +11 -0
  256. package/dist/lib/project-delete.d.ts.map +1 -0
  257. package/dist/lib/project-delete.js +67 -0
  258. package/dist/lib/project-delete.js.map +1 -0
  259. package/dist/lib/project-document-copy.d.ts +31 -0
  260. package/dist/lib/project-document-copy.d.ts.map +1 -0
  261. package/dist/lib/project-document-copy.js +473 -0
  262. package/dist/lib/project-document-copy.js.map +1 -0
  263. package/dist/lib/public-app-assets.d.ts +8 -0
  264. package/dist/lib/public-app-assets.d.ts.map +1 -0
  265. package/dist/lib/public-app-assets.js +77 -0
  266. package/dist/lib/public-app-assets.js.map +1 -0
  267. package/dist/lib/public-routes.d.ts +95 -0
  268. package/dist/lib/public-routes.d.ts.map +1 -0
  269. package/dist/lib/public-routes.js +154 -0
  270. package/dist/lib/public-routes.js.map +1 -0
  271. package/dist/lib/runtime-detection.d.ts +17 -0
  272. package/dist/lib/runtime-detection.d.ts.map +1 -0
  273. package/dist/lib/runtime-detection.js +90 -0
  274. package/dist/lib/runtime-detection.js.map +1 -0
  275. package/dist/lib/storage-access.d.ts +21 -0
  276. package/dist/lib/storage-access.d.ts.map +1 -0
  277. package/dist/lib/storage-access.js +74 -0
  278. package/dist/lib/storage-access.js.map +1 -0
  279. package/dist/lib/storage.d.ts +9 -0
  280. package/dist/lib/storage.d.ts.map +1 -0
  281. package/dist/lib/storage.js +28 -0
  282. package/dist/lib/storage.js.map +1 -0
  283. package/dist/lib/storyboard-export.d.ts +64 -0
  284. package/dist/lib/storyboard-export.d.ts.map +1 -0
  285. package/dist/lib/storyboard-export.js +183 -0
  286. package/dist/lib/storyboard-export.js.map +1 -0
  287. package/dist/lib/style-presets.d.ts +34 -0
  288. package/dist/lib/style-presets.d.ts.map +1 -0
  289. package/dist/lib/style-presets.js +62 -0
  290. package/dist/lib/style-presets.js.map +1 -0
  291. package/dist/lib/temp-url-resolver.d.ts +35 -0
  292. package/dist/lib/temp-url-resolver.d.ts.map +1 -0
  293. package/dist/lib/temp-url-resolver.js +88 -0
  294. package/dist/lib/temp-url-resolver.js.map +1 -0
  295. package/dist/lib/thumbnail.d.ts +74 -0
  296. package/dist/lib/thumbnail.d.ts.map +1 -0
  297. package/dist/lib/thumbnail.js +302 -0
  298. package/dist/lib/thumbnail.js.map +1 -0
  299. package/dist/lib/timeline-bundle.d.ts +1689 -0
  300. package/dist/lib/timeline-bundle.d.ts.map +1 -0
  301. package/dist/lib/timeline-bundle.js +330 -0
  302. package/dist/lib/timeline-bundle.js.map +1 -0
  303. package/dist/lib/video-proxy.d.ts +114 -0
  304. package/dist/lib/video-proxy.d.ts.map +1 -0
  305. package/dist/lib/video-proxy.js +545 -0
  306. package/dist/lib/video-proxy.js.map +1 -0
  307. package/dist/lib/wire-values.d.ts +31 -0
  308. package/dist/lib/wire-values.d.ts.map +1 -0
  309. package/dist/lib/wire-values.js +49 -0
  310. package/dist/lib/wire-values.js.map +1 -0
  311. package/dist/lib/workflow-bundle.d.ts +102 -0
  312. package/dist/lib/workflow-bundle.d.ts.map +1 -0
  313. package/dist/lib/workflow-bundle.js +306 -0
  314. package/dist/lib/workflow-bundle.js.map +1 -0
  315. package/dist/lib/workflow-service.d.ts +82 -0
  316. package/dist/lib/workflow-service.d.ts.map +1 -0
  317. package/dist/lib/workflow-service.js +194 -0
  318. package/dist/lib/workflow-service.js.map +1 -0
  319. package/dist/lib/workflow-workspace.d.ts +20 -0
  320. package/dist/lib/workflow-workspace.d.ts.map +1 -0
  321. package/dist/lib/workflow-workspace.js +65 -0
  322. package/dist/lib/workflow-workspace.js.map +1 -0
  323. package/dist/lib/ws-connection-health.d.ts +10 -0
  324. package/dist/lib/ws-connection-health.d.ts.map +1 -0
  325. package/dist/lib/ws-connection-health.js +38 -0
  326. package/dist/lib/ws-connection-health.js.map +1 -0
  327. package/dist/lib/ws-rate-limit.d.ts +22 -0
  328. package/dist/lib/ws-rate-limit.d.ts.map +1 -0
  329. package/dist/lib/ws-rate-limit.js +45 -0
  330. package/dist/lib/ws-rate-limit.js.map +1 -0
  331. package/dist/lib/ws-upgrade.d.ts +40 -0
  332. package/dist/lib/ws-upgrade.d.ts.map +1 -0
  333. package/dist/lib/ws-upgrade.js +66 -0
  334. package/dist/lib/ws-upgrade.js.map +1 -0
  335. package/dist/lib/zip-stream.d.ts +46 -0
  336. package/dist/lib/zip-stream.d.ts.map +1 -0
  337. package/dist/lib/zip-stream.js +140 -0
  338. package/dist/lib/zip-stream.js.map +1 -0
  339. package/dist/mcp-agent-tools.d.ts +56 -0
  340. package/dist/mcp-agent-tools.d.ts.map +1 -0
  341. package/dist/mcp-agent-tools.js +1216 -0
  342. package/dist/mcp-agent-tools.js.map +1 -0
  343. package/dist/mcp-server.d.ts +56 -7
  344. package/dist/mcp-server.d.ts.map +1 -1
  345. package/dist/mcp-server.js +255 -762
  346. package/dist/mcp-server.js.map +1 -1
  347. package/dist/mcp-tool-deps.d.ts +20 -0
  348. package/dist/mcp-tool-deps.d.ts.map +1 -0
  349. package/dist/mcp-tool-deps.js +61 -0
  350. package/dist/mcp-tool-deps.js.map +1 -0
  351. package/dist/messagepack.d.ts +7 -0
  352. package/dist/messagepack.d.ts.map +1 -0
  353. package/dist/messagepack.js +19 -0
  354. package/dist/messagepack.js.map +1 -0
  355. package/dist/model-download-runtime.d.ts +9 -0
  356. package/dist/model-download-runtime.d.ts.map +1 -0
  357. package/dist/model-download-runtime.js +77 -0
  358. package/dist/model-download-runtime.js.map +1 -0
  359. package/dist/model-execution-availability.d.ts +14 -0
  360. package/dist/model-execution-availability.d.ts.map +1 -0
  361. package/dist/model-execution-availability.js +161 -0
  362. package/dist/model-execution-availability.js.map +1 -0
  363. package/dist/models-api.d.ts +51 -5
  364. package/dist/models-api.d.ts.map +1 -1
  365. package/dist/models-api.js +160 -690
  366. package/dist/models-api.js.map +1 -1
  367. package/dist/node-registry-setup.d.ts +84 -0
  368. package/dist/node-registry-setup.d.ts.map +1 -0
  369. package/dist/node-registry-setup.js +256 -0
  370. package/dist/node-registry-setup.js.map +1 -0
  371. package/dist/oauth/cimd.d.ts +28 -0
  372. package/dist/oauth/cimd.d.ts.map +1 -0
  373. package/dist/oauth/cimd.js +177 -0
  374. package/dist/oauth/cimd.js.map +1 -0
  375. package/dist/oauth/gate.d.ts +38 -0
  376. package/dist/oauth/gate.d.ts.map +1 -0
  377. package/dist/oauth/gate.js +77 -0
  378. package/dist/oauth/gate.js.map +1 -0
  379. package/dist/oauth/metadata.d.ts +35 -0
  380. package/dist/oauth/metadata.d.ts.map +1 -0
  381. package/dist/oauth/metadata.js +56 -0
  382. package/dist/oauth/metadata.js.map +1 -0
  383. package/dist/oauth/pending-store.d.ts +79 -0
  384. package/dist/oauth/pending-store.d.ts.map +1 -0
  385. package/dist/oauth/pending-store.js +133 -0
  386. package/dist/oauth/pending-store.js.map +1 -0
  387. package/dist/oauth/pkce.d.ts +16 -0
  388. package/dist/oauth/pkce.d.ts.map +1 -0
  389. package/dist/oauth/pkce.js +29 -0
  390. package/dist/oauth/pkce.js.map +1 -0
  391. package/dist/oauth/validate.d.ts +25 -0
  392. package/dist/oauth/validate.d.ts.map +1 -0
  393. package/dist/oauth/validate.js +62 -0
  394. package/dist/oauth/validate.js.map +1 -0
  395. package/dist/oauth/www-authenticate.d.ts +9 -0
  396. package/dist/oauth/www-authenticate.d.ts.map +1 -0
  397. package/dist/oauth/www-authenticate.js +33 -0
  398. package/dist/oauth/www-authenticate.js.map +1 -0
  399. package/dist/oauth-api.d.ts +19 -0
  400. package/dist/oauth-api.d.ts.map +1 -1
  401. package/dist/oauth-api.js +531 -12
  402. package/dist/oauth-api.js.map +1 -1
  403. package/dist/openai-api.d.ts.map +1 -1
  404. package/dist/openai-api.js +4 -9
  405. package/dist/openai-api.js.map +1 -1
  406. package/dist/pack-snapshot.d.ts +24 -0
  407. package/dist/pack-snapshot.d.ts.map +1 -0
  408. package/dist/pack-snapshot.js +35 -0
  409. package/dist/pack-snapshot.js.map +1 -0
  410. package/dist/plugins/websocket.d.ts +21 -4
  411. package/dist/plugins/websocket.d.ts.map +1 -1
  412. package/dist/plugins/websocket.js +136 -290
  413. package/dist/plugins/websocket.js.map +1 -1
  414. package/dist/resolve-media-urls.d.ts +17 -3
  415. package/dist/resolve-media-urls.d.ts.map +1 -1
  416. package/dist/resolve-media-urls.js +117 -31
  417. package/dist/resolve-media-urls.js.map +1 -1
  418. package/dist/resource-events.d.ts +28 -0
  419. package/dist/resource-events.d.ts.map +1 -0
  420. package/dist/resource-events.js +20 -0
  421. package/dist/resource-events.js.map +1 -0
  422. package/dist/routes/account.d.ts +100 -0
  423. package/dist/routes/account.d.ts.map +1 -0
  424. package/dist/routes/account.js +197 -0
  425. package/dist/routes/account.js.map +1 -0
  426. package/dist/routes/applications.d.ts +36 -0
  427. package/dist/routes/applications.d.ts.map +1 -0
  428. package/dist/routes/applications.js +183 -0
  429. package/dist/routes/applications.js.map +1 -0
  430. package/dist/routes/assets.d.ts +3 -3
  431. package/dist/routes/assets.d.ts.map +1 -1
  432. package/dist/routes/assets.js +78 -44
  433. package/dist/routes/assets.js.map +1 -1
  434. package/dist/routes/atlascloud-webhook.d.ts +93 -0
  435. package/dist/routes/atlascloud-webhook.d.ts.map +1 -0
  436. package/dist/routes/atlascloud-webhook.js +369 -0
  437. package/dist/routes/atlascloud-webhook.js.map +1 -0
  438. package/dist/routes/collections.d.ts.map +1 -1
  439. package/dist/routes/config.d.ts +26 -0
  440. package/dist/routes/config.d.ts.map +1 -0
  441. package/dist/routes/config.js +64 -0
  442. package/dist/routes/config.js.map +1 -0
  443. package/dist/routes/documents.d.ts +27 -0
  444. package/dist/routes/documents.d.ts.map +1 -0
  445. package/dist/routes/documents.js +155 -0
  446. package/dist/routes/documents.js.map +1 -0
  447. package/dist/routes/fal-credits.d.ts +4 -0
  448. package/dist/routes/fal-credits.d.ts.map +1 -0
  449. package/dist/routes/fal-credits.js +101 -0
  450. package/dist/routes/fal-credits.js.map +1 -0
  451. package/dist/routes/fal-pricing-estimate.d.ts +4 -0
  452. package/dist/routes/fal-pricing-estimate.d.ts.map +1 -0
  453. package/dist/routes/fal-pricing-estimate.js +114 -0
  454. package/dist/routes/fal-pricing-estimate.js.map +1 -0
  455. package/dist/routes/fal-pricing.d.ts +4 -0
  456. package/dist/routes/fal-pricing.d.ts.map +1 -0
  457. package/dist/routes/fal-pricing.js +93 -0
  458. package/dist/routes/fal-pricing.js.map +1 -0
  459. package/dist/routes/fal-webhook.d.ts +102 -0
  460. package/dist/routes/fal-webhook.d.ts.map +1 -0
  461. package/dist/routes/fal-webhook.js +379 -0
  462. package/dist/routes/fal-webhook.js.map +1 -0
  463. package/dist/routes/files.d.ts.map +1 -1
  464. package/dist/routes/files.js +4 -3
  465. package/dist/routes/files.js.map +1 -1
  466. package/dist/routes/health.d.ts +1 -0
  467. package/dist/routes/health.d.ts.map +1 -1
  468. package/dist/routes/health.js +41 -7
  469. package/dist/routes/health.js.map +1 -1
  470. package/dist/routes/integrations.d.ts +39 -0
  471. package/dist/routes/integrations.d.ts.map +1 -0
  472. package/dist/routes/integrations.js +211 -0
  473. package/dist/routes/integrations.js.map +1 -0
  474. package/dist/routes/js-scripts.d.ts +26 -0
  475. package/dist/routes/js-scripts.d.ts.map +1 -0
  476. package/dist/routes/js-scripts.js +116 -0
  477. package/dist/routes/js-scripts.js.map +1 -0
  478. package/dist/routes/kie-credits.d.ts +4 -0
  479. package/dist/routes/kie-credits.d.ts.map +1 -0
  480. package/dist/routes/kie-credits.js +93 -0
  481. package/dist/routes/kie-credits.js.map +1 -0
  482. package/dist/routes/kie-pricing.d.ts +4 -0
  483. package/dist/routes/kie-pricing.d.ts.map +1 -0
  484. package/dist/routes/kie-pricing.js +45 -0
  485. package/dist/routes/kie-pricing.js.map +1 -0
  486. package/dist/routes/kie-webhook.d.ts +4 -0
  487. package/dist/routes/kie-webhook.d.ts.map +1 -0
  488. package/dist/routes/kie-webhook.js +70 -0
  489. package/dist/routes/kie-webhook.js.map +1 -0
  490. package/dist/routes/nodes.d.ts.map +1 -1
  491. package/dist/routes/nodes.js +6 -59
  492. package/dist/routes/nodes.js.map +1 -1
  493. package/dist/routes/oauth-as.d.ts +30 -0
  494. package/dist/routes/oauth-as.d.ts.map +1 -0
  495. package/dist/routes/oauth-as.js +412 -0
  496. package/dist/routes/oauth-as.js.map +1 -0
  497. package/dist/routes/oauth.d.ts.map +1 -1
  498. package/dist/routes/openai.d.ts.map +1 -1
  499. package/dist/routes/public-apps.d.ts +27 -0
  500. package/dist/routes/public-apps.d.ts.map +1 -0
  501. package/dist/routes/public-apps.js +99 -0
  502. package/dist/routes/public-apps.js.map +1 -0
  503. package/dist/routes/sandbox-modules.d.ts +17 -0
  504. package/dist/routes/sandbox-modules.d.ts.map +1 -0
  505. package/dist/routes/sandbox-modules.js +85 -0
  506. package/dist/routes/sandbox-modules.js.map +1 -0
  507. package/dist/routes/sdk-v1.d.ts +24 -0
  508. package/dist/routes/sdk-v1.d.ts.map +1 -0
  509. package/dist/routes/sdk-v1.js +320 -0
  510. package/dist/routes/sdk-v1.js.map +1 -0
  511. package/dist/routes/storage.d.ts.map +1 -1
  512. package/dist/routes/storyboards.d.ts +16 -0
  513. package/dist/routes/storyboards.d.ts.map +1 -0
  514. package/dist/routes/storyboards.js +97 -0
  515. package/dist/routes/storyboards.js.map +1 -0
  516. package/dist/routes/timeline-animations.d.ts +23 -0
  517. package/dist/routes/timeline-animations.d.ts.map +1 -0
  518. package/dist/routes/timeline-animations.js +99 -0
  519. package/dist/routes/timeline-animations.js.map +1 -0
  520. package/dist/routes/timeline-audio-bake.d.ts +51 -0
  521. package/dist/routes/timeline-audio-bake.d.ts.map +1 -0
  522. package/dist/routes/timeline-audio-bake.js +110 -0
  523. package/dist/routes/timeline-audio-bake.js.map +1 -0
  524. package/dist/routes/timeline-fonts.d.ts +29 -0
  525. package/dist/routes/timeline-fonts.d.ts.map +1 -0
  526. package/dist/routes/timeline-fonts.js +64 -0
  527. package/dist/routes/timeline-fonts.js.map +1 -0
  528. package/dist/routes/timeline-isolate-subject.d.ts +55 -0
  529. package/dist/routes/timeline-isolate-subject.d.ts.map +1 -0
  530. package/dist/routes/timeline-isolate-subject.js +128 -0
  531. package/dist/routes/timeline-isolate-subject.js.map +1 -0
  532. package/dist/routes/timeline-track-object.d.ts +39 -0
  533. package/dist/routes/timeline-track-object.d.ts.map +1 -0
  534. package/dist/routes/timeline-track-object.js +97 -0
  535. package/dist/routes/timeline-track-object.js.map +1 -0
  536. package/dist/routes/timelines.d.ts +18 -0
  537. package/dist/routes/timelines.d.ts.map +1 -0
  538. package/dist/routes/timelines.js +191 -0
  539. package/dist/routes/timelines.js.map +1 -0
  540. package/dist/routes/workflows.d.ts +6 -9
  541. package/dist/routes/workflows.d.ts.map +1 -1
  542. package/dist/routes/workflows.js +100 -11
  543. package/dist/routes/workflows.js.map +1 -1
  544. package/dist/routes/workspace.d.ts.map +1 -1
  545. package/dist/run-supervisor.d.ts +30 -0
  546. package/dist/run-supervisor.d.ts.map +1 -0
  547. package/dist/run-supervisor.js +86 -0
  548. package/dist/run-supervisor.js.map +1 -0
  549. package/dist/sandbox-catalog.d.ts +28 -0
  550. package/dist/sandbox-catalog.d.ts.map +1 -0
  551. package/dist/sandbox-catalog.js +74 -0
  552. package/dist/sandbox-catalog.js.map +1 -0
  553. package/dist/screenshot-projects.d.ts +27 -0
  554. package/dist/screenshot-projects.d.ts.map +1 -0
  555. package/dist/screenshot-projects.js +880 -0
  556. package/dist/screenshot-projects.js.map +1 -0
  557. package/dist/screenshot-server.js +947 -43
  558. package/dist/screenshot-server.js.map +1 -1
  559. package/dist/sdk/sdk-cached-model-inventory.d.ts +21 -0
  560. package/dist/sdk/sdk-cached-model-inventory.d.ts.map +1 -0
  561. package/dist/sdk/sdk-cached-model-inventory.js +51 -0
  562. package/dist/sdk/sdk-cached-model-inventory.js.map +1 -0
  563. package/dist/sdk/sdk-capabilities-service.d.ts +32 -0
  564. package/dist/sdk/sdk-capabilities-service.d.ts.map +1 -0
  565. package/dist/sdk/sdk-capabilities-service.js +47 -0
  566. package/dist/sdk/sdk-capabilities-service.js.map +1 -0
  567. package/dist/sdk/sdk-execution-readiness-probe.d.ts +19 -0
  568. package/dist/sdk/sdk-execution-readiness-probe.d.ts.map +1 -0
  569. package/dist/sdk/sdk-execution-readiness-probe.js +90 -0
  570. package/dist/sdk/sdk-execution-readiness-probe.js.map +1 -0
  571. package/dist/sdk/sdk-execution-target-readiness.d.ts +15 -0
  572. package/dist/sdk/sdk-execution-target-readiness.d.ts.map +1 -0
  573. package/dist/sdk/sdk-execution-target-readiness.js +50 -0
  574. package/dist/sdk/sdk-execution-target-readiness.js.map +1 -0
  575. package/dist/sdk/sdk-feature-flags.d.ts +18 -0
  576. package/dist/sdk/sdk-feature-flags.d.ts.map +1 -0
  577. package/dist/sdk/sdk-feature-flags.js +30 -0
  578. package/dist/sdk/sdk-feature-flags.js.map +1 -0
  579. package/dist/sdk/sdk-huggingface-download-state.d.ts +15 -0
  580. package/dist/sdk/sdk-huggingface-download-state.d.ts.map +1 -0
  581. package/dist/sdk/sdk-huggingface-download-state.js +25 -0
  582. package/dist/sdk/sdk-huggingface-download-state.js.map +1 -0
  583. package/dist/sdk/sdk-live-runner-registry.d.ts +21 -0
  584. package/dist/sdk/sdk-live-runner-registry.d.ts.map +1 -0
  585. package/dist/sdk/sdk-live-runner-registry.js +26 -0
  586. package/dist/sdk/sdk-live-runner-registry.js.map +1 -0
  587. package/dist/sdk/sdk-model-catalog-service.d.ts +20 -0
  588. package/dist/sdk/sdk-model-catalog-service.d.ts.map +1 -0
  589. package/dist/sdk/sdk-model-catalog-service.js +219 -0
  590. package/dist/sdk/sdk-model-catalog-service.js.map +1 -0
  591. package/dist/sdk/sdk-model-download-service.d.ts +39 -0
  592. package/dist/sdk/sdk-model-download-service.d.ts.map +1 -0
  593. package/dist/sdk/sdk-model-download-service.js +274 -0
  594. package/dist/sdk/sdk-model-download-service.js.map +1 -0
  595. package/dist/sdk/sdk-model-requirement-probe.d.ts +30 -0
  596. package/dist/sdk/sdk-model-requirement-probe.d.ts.map +1 -0
  597. package/dist/sdk/sdk-model-requirement-probe.js +120 -0
  598. package/dist/sdk/sdk-model-requirement-probe.js.map +1 -0
  599. package/dist/sdk/sdk-node-package-requirement-probe.d.ts +20 -0
  600. package/dist/sdk/sdk-node-package-requirement-probe.d.ts.map +1 -0
  601. package/dist/sdk/sdk-node-package-requirement-probe.js +44 -0
  602. package/dist/sdk/sdk-node-package-requirement-probe.js.map +1 -0
  603. package/dist/sdk/sdk-node-type-inventory-service.d.ts +11 -0
  604. package/dist/sdk/sdk-node-type-inventory-service.d.ts.map +1 -0
  605. package/dist/sdk/sdk-node-type-inventory-service.js +36 -0
  606. package/dist/sdk/sdk-node-type-inventory-service.js.map +1 -0
  607. package/dist/sdk/sdk-preflight-orchestrator.d.ts +54 -0
  608. package/dist/sdk/sdk-preflight-orchestrator.d.ts.map +1 -0
  609. package/dist/sdk/sdk-preflight-orchestrator.js +63 -0
  610. package/dist/sdk/sdk-preflight-orchestrator.js.map +1 -0
  611. package/dist/sdk/sdk-preflight-requirement-resolver.d.ts +35 -0
  612. package/dist/sdk/sdk-preflight-requirement-resolver.d.ts.map +1 -0
  613. package/dist/sdk/sdk-preflight-requirement-resolver.js +67 -0
  614. package/dist/sdk/sdk-preflight-requirement-resolver.js.map +1 -0
  615. package/dist/sdk/sdk-preflight-service.d.ts +61 -0
  616. package/dist/sdk/sdk-preflight-service.d.ts.map +1 -0
  617. package/dist/sdk/sdk-preflight-service.js +104 -0
  618. package/dist/sdk/sdk-preflight-service.js.map +1 -0
  619. package/dist/sdk/sdk-preflight-workflow-source.d.ts +18 -0
  620. package/dist/sdk/sdk-preflight-workflow-source.d.ts.map +1 -0
  621. package/dist/sdk/sdk-preflight-workflow-source.js +42 -0
  622. package/dist/sdk/sdk-preflight-workflow-source.js.map +1 -0
  623. package/dist/sdk/sdk-route-policy.d.ts +3 -0
  624. package/dist/sdk/sdk-route-policy.d.ts.map +1 -0
  625. package/dist/sdk/sdk-route-policy.js +18 -0
  626. package/dist/sdk/sdk-route-policy.js.map +1 -0
  627. package/dist/sdk/sdk-runtime-capabilities-service.d.ts +60 -0
  628. package/dist/sdk/sdk-runtime-capabilities-service.d.ts.map +1 -0
  629. package/dist/sdk/sdk-runtime-capabilities-service.js +30 -0
  630. package/dist/sdk/sdk-runtime-capabilities-service.js.map +1 -0
  631. package/dist/sdk/sdk-runtime-requirement-probe.d.ts +13 -0
  632. package/dist/sdk/sdk-runtime-requirement-probe.d.ts.map +1 -0
  633. package/dist/sdk/sdk-runtime-requirement-probe.js +55 -0
  634. package/dist/sdk/sdk-runtime-requirement-probe.js.map +1 -0
  635. package/dist/sdk/sdk-static-preflight-service.d.ts +66 -0
  636. package/dist/sdk/sdk-static-preflight-service.d.ts.map +1 -0
  637. package/dist/sdk/sdk-static-preflight-service.js +492 -0
  638. package/dist/sdk/sdk-static-preflight-service.js.map +1 -0
  639. package/dist/sdk/sdk-temporary-asset-service.d.ts +18 -0
  640. package/dist/sdk/sdk-temporary-asset-service.d.ts.map +1 -0
  641. package/dist/sdk/sdk-temporary-asset-service.js +33 -0
  642. package/dist/sdk/sdk-temporary-asset-service.js.map +1 -0
  643. package/dist/sdk/sdk-temporary-asset-upload-http-handler.d.ts +9 -0
  644. package/dist/sdk/sdk-temporary-asset-upload-http-handler.d.ts.map +1 -0
  645. package/dist/sdk/sdk-temporary-asset-upload-http-handler.js +65 -0
  646. package/dist/sdk/sdk-temporary-asset-upload-http-handler.js.map +1 -0
  647. package/dist/sdk/sdk-v1-handler-map.d.ts +44 -0
  648. package/dist/sdk/sdk-v1-handler-map.d.ts.map +1 -0
  649. package/dist/sdk/sdk-v1-handler-map.js +37 -0
  650. package/dist/sdk/sdk-v1-handler-map.js.map +1 -0
  651. package/dist/sdk/sdk-v1-service-error.d.ts +21 -0
  652. package/dist/sdk/sdk-v1-service-error.d.ts.map +1 -0
  653. package/dist/sdk/sdk-v1-service-error.js +62 -0
  654. package/dist/sdk/sdk-v1-service-error.js.map +1 -0
  655. package/dist/sdk/sdk-v1-service.d.ts +74 -0
  656. package/dist/sdk/sdk-v1-service.d.ts.map +1 -0
  657. package/dist/sdk/sdk-v1-service.js +219 -0
  658. package/dist/sdk/sdk-v1-service.js.map +1 -0
  659. package/dist/sdk/sdk-worker-readiness-adapter.d.ts +22 -0
  660. package/dist/sdk/sdk-worker-readiness-adapter.d.ts.map +1 -0
  661. package/dist/sdk/sdk-worker-readiness-adapter.js +29 -0
  662. package/dist/sdk/sdk-worker-readiness-adapter.js.map +1 -0
  663. package/dist/server.js +1019 -201
  664. package/dist/server.js.map +1 -1
  665. package/dist/session/asset-autosave.d.ts +80 -0
  666. package/dist/session/asset-autosave.d.ts.map +1 -0
  667. package/dist/session/asset-autosave.js +456 -0
  668. package/dist/session/asset-autosave.js.map +1 -0
  669. package/dist/session/chat-compaction.d.ts +107 -0
  670. package/dist/session/chat-compaction.d.ts.map +1 -0
  671. package/dist/session/chat-compaction.js +191 -0
  672. package/dist/session/chat-compaction.js.map +1 -0
  673. package/dist/session/chat-history.d.ts +75 -0
  674. package/dist/session/chat-history.d.ts.map +1 -0
  675. package/dist/session/chat-history.js +194 -0
  676. package/dist/session/chat-history.js.map +1 -0
  677. package/dist/session/chat-prompt.d.ts +45 -0
  678. package/dist/session/chat-prompt.d.ts.map +1 -0
  679. package/dist/session/chat-prompt.js +480 -0
  680. package/dist/session/chat-prompt.js.map +1 -0
  681. package/dist/session/chat-turn.d.ts +336 -0
  682. package/dist/session/chat-turn.d.ts.map +1 -0
  683. package/dist/session/chat-turn.js +3516 -0
  684. package/dist/session/chat-turn.js.map +1 -0
  685. package/dist/session/client-session.d.ts +59 -0
  686. package/dist/session/client-session.d.ts.map +1 -0
  687. package/dist/session/client-session.js +2 -0
  688. package/dist/session/client-session.js.map +1 -0
  689. package/dist/session/commands.d.ts +133 -0
  690. package/dist/session/commands.d.ts.map +1 -0
  691. package/dist/session/commands.js +778 -0
  692. package/dist/session/commands.js.map +1 -0
  693. package/dist/session/inference.d.ts +213 -0
  694. package/dist/session/inference.d.ts.map +1 -0
  695. package/dist/session/inference.js +1072 -0
  696. package/dist/session/inference.js.map +1 -0
  697. package/dist/session/job-execution.d.ts +477 -0
  698. package/dist/session/job-execution.d.ts.map +1 -0
  699. package/dist/session/job-execution.js +2089 -0
  700. package/dist/session/job-execution.js.map +1 -0
  701. package/dist/session/media-generation.d.ts +94 -0
  702. package/dist/session/media-generation.d.ts.map +1 -0
  703. package/dist/session/media-generation.js +234 -0
  704. package/dist/session/media-generation.js.map +1 -0
  705. package/dist/session/model-interfaces.d.ts +27 -0
  706. package/dist/session/model-interfaces.d.ts.map +1 -0
  707. package/dist/session/model-interfaces.js +132 -0
  708. package/dist/session/model-interfaces.js.map +1 -0
  709. package/dist/session/sanitize.d.ts +7 -0
  710. package/dist/session/sanitize.d.ts.map +1 -0
  711. package/dist/session/sanitize.js +64 -0
  712. package/dist/session/sanitize.js.map +1 -0
  713. package/dist/settings-registry.d.ts +6 -15
  714. package/dist/settings-registry.d.ts.map +1 -1
  715. package/dist/settings-registry.js +6 -103
  716. package/dist/settings-registry.js.map +1 -1
  717. package/dist/storage-api.d.ts +20 -0
  718. package/dist/storage-api.d.ts.map +1 -1
  719. package/dist/storage-api.js +260 -77
  720. package/dist/storage-api.js.map +1 -1
  721. package/dist/storage-retention.d.ts +9 -0
  722. package/dist/storage-retention.d.ts.map +1 -0
  723. package/dist/storage-retention.js +74 -0
  724. package/dist/storage-retention.js.map +1 -0
  725. package/dist/system-stats.d.ts +52 -0
  726. package/dist/system-stats.d.ts.map +1 -0
  727. package/dist/system-stats.js +134 -0
  728. package/dist/system-stats.js.map +1 -0
  729. package/dist/test-ui-server.d.ts +46 -0
  730. package/dist/test-ui-server.d.ts.map +1 -1
  731. package/dist/test-ui-server.js +188 -41
  732. package/dist/test-ui-server.js.map +1 -1
  733. package/dist/triggers/boot.d.ts +73 -0
  734. package/dist/triggers/boot.d.ts.map +1 -0
  735. package/dist/triggers/boot.js +149 -0
  736. package/dist/triggers/boot.js.map +1 -0
  737. package/dist/triggers/dispatcher.d.ts +234 -0
  738. package/dist/triggers/dispatcher.d.ts.map +1 -0
  739. package/dist/triggers/dispatcher.js +525 -0
  740. package/dist/triggers/dispatcher.js.map +1 -0
  741. package/dist/triggers/file-watch.d.ts +90 -0
  742. package/dist/triggers/file-watch.d.ts.map +1 -0
  743. package/dist/triggers/file-watch.js +405 -0
  744. package/dist/triggers/file-watch.js.map +1 -0
  745. package/dist/triggers/registration-sync.d.ts +34 -0
  746. package/dist/triggers/registration-sync.d.ts.map +1 -0
  747. package/dist/triggers/registration-sync.js +162 -0
  748. package/dist/triggers/registration-sync.js.map +1 -0
  749. package/dist/triggers/schedule-timing.d.ts +52 -0
  750. package/dist/triggers/schedule-timing.d.ts.map +1 -0
  751. package/dist/triggers/schedule-timing.js +90 -0
  752. package/dist/triggers/schedule-timing.js.map +1 -0
  753. package/dist/triggers/scheduler.d.ts +55 -0
  754. package/dist/triggers/scheduler.d.ts.map +1 -0
  755. package/dist/triggers/scheduler.js +122 -0
  756. package/dist/triggers/scheduler.js.map +1 -0
  757. package/dist/triggers/settle.d.ts +66 -0
  758. package/dist/triggers/settle.d.ts.map +1 -0
  759. package/dist/triggers/settle.js +99 -0
  760. package/dist/triggers/settle.js.map +1 -0
  761. package/dist/triggers/stores.d.ts +38 -0
  762. package/dist/triggers/stores.d.ts.map +1 -0
  763. package/dist/triggers/stores.js +168 -0
  764. package/dist/triggers/stores.js.map +1 -0
  765. package/dist/triggers/webhook-route.d.ts +49 -0
  766. package/dist/triggers/webhook-route.d.ts.map +1 -0
  767. package/dist/triggers/webhook-route.js +148 -0
  768. package/dist/triggers/webhook-route.js.map +1 -0
  769. package/dist/trpc/context.d.ts +52 -5
  770. package/dist/trpc/context.d.ts.map +1 -1
  771. package/dist/trpc/context.js +4 -1
  772. package/dist/trpc/context.js.map +1 -1
  773. package/dist/trpc/error-formatter.d.ts.map +1 -1
  774. package/dist/trpc/error-formatter.js +23 -2
  775. package/dist/trpc/error-formatter.js.map +1 -1
  776. package/dist/trpc/error-logging.d.ts +14 -0
  777. package/dist/trpc/error-logging.d.ts.map +1 -0
  778. package/dist/trpc/error-logging.js +27 -0
  779. package/dist/trpc/error-logging.js.map +1 -0
  780. package/dist/trpc/index.d.ts +2 -2
  781. package/dist/trpc/index.d.ts.map +1 -1
  782. package/dist/trpc/index.js +0 -2
  783. package/dist/trpc/index.js.map +1 -1
  784. package/dist/trpc/middleware.d.ts +6 -3
  785. package/dist/trpc/middleware.d.ts.map +1 -1
  786. package/dist/trpc/router.d.ts +29791 -1303
  787. package/dist/trpc/router.d.ts.map +1 -1
  788. package/dist/trpc/router.js +46 -3
  789. package/dist/trpc/router.js.map +1 -1
  790. package/dist/trpc/routers/agent-access.d.ts +149 -0
  791. package/dist/trpc/routers/agent-access.d.ts.map +1 -0
  792. package/dist/trpc/routers/agent-access.js +224 -0
  793. package/dist/trpc/routers/agent-access.js.map +1 -0
  794. package/dist/trpc/routers/applications.d.ts +1117 -0
  795. package/dist/trpc/routers/applications.d.ts.map +1 -0
  796. package/dist/trpc/routers/applications.js +218 -0
  797. package/dist/trpc/routers/applications.js.map +1 -0
  798. package/dist/trpc/routers/assets.d.ts +184 -42
  799. package/dist/trpc/routers/assets.d.ts.map +1 -1
  800. package/dist/trpc/routers/assets.js +499 -105
  801. package/dist/trpc/routers/assets.js.map +1 -1
  802. package/dist/trpc/routers/code-gen.d.ts +99 -0
  803. package/dist/trpc/routers/code-gen.d.ts.map +1 -0
  804. package/dist/trpc/routers/code-gen.js +164 -0
  805. package/dist/trpc/routers/code-gen.js.map +1 -0
  806. package/dist/trpc/routers/collections.d.ts +1 -26
  807. package/dist/trpc/routers/collections.d.ts.map +1 -1
  808. package/dist/trpc/routers/collections.js +145 -78
  809. package/dist/trpc/routers/collections.js.map +1 -1
  810. package/dist/trpc/routers/costs.d.ts +40 -96
  811. package/dist/trpc/routers/costs.d.ts.map +1 -1
  812. package/dist/trpc/routers/costs.js +14 -89
  813. package/dist/trpc/routers/costs.js.map +1 -1
  814. package/dist/trpc/routers/credits.d.ts +110 -0
  815. package/dist/trpc/routers/credits.d.ts.map +1 -0
  816. package/dist/trpc/routers/credits.js +69 -0
  817. package/dist/trpc/routers/credits.js.map +1 -0
  818. package/dist/trpc/routers/custom-providers.d.ts +88 -0
  819. package/dist/trpc/routers/custom-providers.d.ts.map +1 -0
  820. package/dist/trpc/routers/custom-providers.js +140 -0
  821. package/dist/trpc/routers/custom-providers.js.map +1 -0
  822. package/dist/trpc/routers/documents.d.ts +61 -0
  823. package/dist/trpc/routers/documents.d.ts.map +1 -0
  824. package/dist/trpc/routers/documents.js +23 -0
  825. package/dist/trpc/routers/documents.js.map +1 -0
  826. package/dist/trpc/routers/extension.d.ts +24 -0
  827. package/dist/trpc/routers/extension.d.ts.map +1 -0
  828. package/dist/trpc/routers/extension.js +28 -0
  829. package/dist/trpc/routers/extension.js.map +1 -0
  830. package/dist/trpc/routers/external-mcp.d.ts +108 -0
  831. package/dist/trpc/routers/external-mcp.d.ts.map +1 -0
  832. package/dist/trpc/routers/external-mcp.js +70 -0
  833. package/dist/trpc/routers/external-mcp.js.map +1 -0
  834. package/dist/trpc/routers/files.d.ts +9 -2
  835. package/dist/trpc/routers/files.d.ts.map +1 -1
  836. package/dist/trpc/routers/files.js +67 -37
  837. package/dist/trpc/routers/files.js.map +1 -1
  838. package/dist/trpc/routers/fonts.d.ts +30 -0
  839. package/dist/trpc/routers/fonts.d.ts.map +1 -0
  840. package/dist/trpc/routers/fonts.js +112 -0
  841. package/dist/trpc/routers/fonts.js.map +1 -0
  842. package/dist/trpc/routers/games.d.ts +7610 -0
  843. package/dist/trpc/routers/games.d.ts.map +1 -0
  844. package/dist/trpc/routers/games.js +456 -0
  845. package/dist/trpc/routers/games.js.map +1 -0
  846. package/dist/trpc/routers/integrations.d.ts +86 -0
  847. package/dist/trpc/routers/integrations.d.ts.map +1 -0
  848. package/dist/trpc/routers/integrations.js +129 -0
  849. package/dist/trpc/routers/integrations.js.map +1 -0
  850. package/dist/trpc/routers/jobs.d.ts +88 -27
  851. package/dist/trpc/routers/jobs.d.ts.map +1 -1
  852. package/dist/trpc/routers/jobs.js +113 -35
  853. package/dist/trpc/routers/jobs.js.map +1 -1
  854. package/dist/trpc/routers/js-scripts.d.ts +410 -0
  855. package/dist/trpc/routers/js-scripts.d.ts.map +1 -0
  856. package/dist/trpc/routers/js-scripts.js +303 -0
  857. package/dist/trpc/routers/js-scripts.js.map +1 -0
  858. package/dist/trpc/routers/mcp-config.d.ts +1 -1
  859. package/dist/trpc/routers/mcp-config.js +5 -3
  860. package/dist/trpc/routers/mcp-config.js.map +1 -1
  861. package/dist/trpc/routers/memories.d.ts +79 -0
  862. package/dist/trpc/routers/memories.d.ts.map +1 -0
  863. package/dist/trpc/routers/memories.js +66 -0
  864. package/dist/trpc/routers/memories.js.map +1 -0
  865. package/dist/trpc/routers/messages.d.ts +4 -77
  866. package/dist/trpc/routers/messages.d.ts.map +1 -1
  867. package/dist/trpc/routers/messages.js +9 -58
  868. package/dist/trpc/routers/messages.js.map +1 -1
  869. package/dist/trpc/routers/models.d.ts +1403 -238
  870. package/dist/trpc/routers/models.d.ts.map +1 -1
  871. package/dist/trpc/routers/models.js +532 -294
  872. package/dist/trpc/routers/models.js.map +1 -1
  873. package/dist/trpc/routers/nodes.d.ts +32 -16
  874. package/dist/trpc/routers/nodes.d.ts.map +1 -1
  875. package/dist/trpc/routers/nodes.js +70 -31
  876. package/dist/trpc/routers/nodes.js.map +1 -1
  877. package/dist/trpc/routers/packs.d.ts +184 -0
  878. package/dist/trpc/routers/packs.d.ts.map +1 -0
  879. package/dist/trpc/routers/packs.js +259 -0
  880. package/dist/trpc/routers/packs.js.map +1 -0
  881. package/dist/trpc/routers/projects.d.ts +422 -0
  882. package/dist/trpc/routers/projects.d.ts.map +1 -0
  883. package/dist/trpc/routers/projects.js +373 -0
  884. package/dist/trpc/routers/projects.js.map +1 -0
  885. package/dist/trpc/routers/resources.d.ts +120 -0
  886. package/dist/trpc/routers/resources.d.ts.map +1 -0
  887. package/dist/trpc/routers/resources.js +212 -0
  888. package/dist/trpc/routers/resources.js.map +1 -0
  889. package/dist/trpc/routers/scripts.d.ts +1116 -0
  890. package/dist/trpc/routers/scripts.d.ts.map +1 -0
  891. package/dist/trpc/routers/scripts.js +131 -0
  892. package/dist/trpc/routers/scripts.js.map +1 -0
  893. package/dist/trpc/routers/segmentation.d.ts +62 -0
  894. package/dist/trpc/routers/segmentation.d.ts.map +1 -0
  895. package/dist/trpc/routers/segmentation.js +146 -0
  896. package/dist/trpc/routers/segmentation.js.map +1 -0
  897. package/dist/trpc/routers/settings.d.ts +177 -10
  898. package/dist/trpc/routers/settings.d.ts.map +1 -1
  899. package/dist/trpc/routers/settings.js +123 -33
  900. package/dist/trpc/routers/settings.js.map +1 -1
  901. package/dist/trpc/routers/sketch.d.ts +854 -0
  902. package/dist/trpc/routers/sketch.d.ts.map +1 -0
  903. package/dist/trpc/routers/sketch.js +617 -0
  904. package/dist/trpc/routers/sketch.js.map +1 -0
  905. package/dist/trpc/routers/skills.d.ts +76 -22
  906. package/dist/trpc/routers/skills.d.ts.map +1 -1
  907. package/dist/trpc/routers/skills.js +195 -170
  908. package/dist/trpc/routers/skills.js.map +1 -1
  909. package/dist/trpc/routers/storage.d.ts +3 -30
  910. package/dist/trpc/routers/storage.d.ts.map +1 -1
  911. package/dist/trpc/routers/storage.js +60 -117
  912. package/dist/trpc/routers/storage.js.map +1 -1
  913. package/dist/trpc/routers/storyboards.d.ts +5907 -0
  914. package/dist/trpc/routers/storyboards.d.ts.map +1 -0
  915. package/dist/trpc/routers/storyboards.js +156 -0
  916. package/dist/trpc/routers/storyboards.js.map +1 -0
  917. package/dist/trpc/routers/threads.d.ts +33 -21
  918. package/dist/trpc/routers/threads.d.ts.map +1 -1
  919. package/dist/trpc/routers/threads.js +36 -30
  920. package/dist/trpc/routers/threads.js.map +1 -1
  921. package/dist/trpc/routers/timeline.d.ts +8516 -0
  922. package/dist/trpc/routers/timeline.d.ts.map +1 -0
  923. package/dist/trpc/routers/timeline.js +493 -0
  924. package/dist/trpc/routers/timeline.js.map +1 -0
  925. package/dist/trpc/routers/triggers.d.ts +46 -0
  926. package/dist/trpc/routers/triggers.d.ts.map +1 -0
  927. package/dist/trpc/routers/triggers.js +117 -0
  928. package/dist/trpc/routers/triggers.js.map +1 -0
  929. package/dist/trpc/routers/users.d.ts +1 -14
  930. package/dist/trpc/routers/users.d.ts.map +1 -1
  931. package/dist/trpc/routers/users.js +1 -12
  932. package/dist/trpc/routers/users.js.map +1 -1
  933. package/dist/trpc/routers/worker.d.ts +242 -0
  934. package/dist/trpc/routers/worker.d.ts.map +1 -0
  935. package/dist/trpc/routers/worker.js +220 -0
  936. package/dist/trpc/routers/worker.js.map +1 -0
  937. package/dist/trpc/routers/workflows.d.ts +385 -197
  938. package/dist/trpc/routers/workflows.d.ts.map +1 -1
  939. package/dist/trpc/routers/workflows.js +326 -345
  940. package/dist/trpc/routers/workflows.js.map +1 -1
  941. package/dist/trpc/routers/workspace.d.ts +59 -33
  942. package/dist/trpc/routers/workspace.d.ts.map +1 -1
  943. package/dist/trpc/routers/workspace.js +230 -93
  944. package/dist/trpc/routers/workspace.js.map +1 -1
  945. package/dist/trpc/sandbox-coverage.d.ts +64 -0
  946. package/dist/trpc/sandbox-coverage.d.ts.map +1 -0
  947. package/dist/trpc/sandbox-coverage.js +1049 -0
  948. package/dist/trpc/sandbox-coverage.js.map +1 -0
  949. package/dist/websocket-client-session.d.ts +320 -0
  950. package/dist/websocket-client-session.d.ts.map +1 -0
  951. package/dist/websocket-client-session.js +1253 -0
  952. package/dist/websocket-client-session.js.map +1 -0
  953. package/dist/workflow-interface-service.d.ts +87 -0
  954. package/dist/workflow-interface-service.d.ts.map +1 -0
  955. package/dist/workflow-interface-service.js +131 -0
  956. package/dist/workflow-interface-service.js.map +1 -0
  957. package/dist/workspace-api.d.ts.map +1 -1
  958. package/dist/workspace-api.js +33 -23
  959. package/dist/workspace-api.js.map +1 -1
  960. package/dist/ws-adapter.d.ts +46 -3
  961. package/dist/ws-adapter.d.ts.map +1 -1
  962. package/dist/ws-adapter.js +164 -26
  963. package/dist/ws-adapter.js.map +1 -1
  964. package/package.json +81 -35
  965. package/dist/agent/agent-runtime.d.ts +0 -47
  966. package/dist/agent/agent-runtime.d.ts.map +0 -1
  967. package/dist/agent/agent-runtime.js +0 -579
  968. package/dist/agent/agent-runtime.js.map +0 -1
  969. package/dist/agent/codex-agent.d.ts +0 -41
  970. package/dist/agent/codex-agent.d.ts.map +0 -1
  971. package/dist/agent/codex-agent.js +0 -334
  972. package/dist/agent/codex-agent.js.map +0 -1
  973. package/dist/agent/index.d.ts +0 -6
  974. package/dist/agent/index.d.ts.map +0 -1
  975. package/dist/agent/index.js +0 -4
  976. package/dist/agent/index.js.map +0 -1
  977. package/dist/agent/llm-agent.d.ts +0 -51
  978. package/dist/agent/llm-agent.d.ts.map +0 -1
  979. package/dist/agent/llm-agent.js +0 -526
  980. package/dist/agent/llm-agent.js.map +0 -1
  981. package/dist/agent/mcp-tool-server.d.ts +0 -33
  982. package/dist/agent/mcp-tool-server.d.ts.map +0 -1
  983. package/dist/agent/mcp-tool-server.js +0 -292
  984. package/dist/agent/mcp-tool-server.js.map +0 -1
  985. package/dist/agent/opencode-agent.d.ts +0 -32
  986. package/dist/agent/opencode-agent.d.ts.map +0 -1
  987. package/dist/agent/opencode-agent.js +0 -332
  988. package/dist/agent/opencode-agent.js.map +0 -1
  989. package/dist/agent/pi-agent.d.ts +0 -42
  990. package/dist/agent/pi-agent.d.ts.map +0 -1
  991. package/dist/agent/pi-agent.js +0 -429
  992. package/dist/agent/pi-agent.js.map +0 -1
  993. package/dist/agent/sdk-provider.d.ts +0 -46
  994. package/dist/agent/sdk-provider.d.ts.map +0 -1
  995. package/dist/agent/sdk-provider.js +0 -50
  996. package/dist/agent/sdk-provider.js.map +0 -1
  997. package/dist/agent/socket-route.d.ts +0 -11
  998. package/dist/agent/socket-route.d.ts.map +0 -1
  999. package/dist/agent/socket-route.js +0 -262
  1000. package/dist/agent/socket-route.js.map +0 -1
  1001. package/dist/agent/transport.d.ts +0 -39
  1002. package/dist/agent/transport.d.ts.map +0 -1
  1003. package/dist/agent/transport.js +0 -17
  1004. package/dist/agent/transport.js.map +0 -1
  1005. package/dist/agent/types.d.ts +0 -11
  1006. package/dist/agent/types.d.ts.map +0 -1
  1007. package/dist/agent/types.js +0 -10
  1008. package/dist/agent/types.js.map +0 -1
  1009. package/dist/routes/jobs.d.ts +0 -16
  1010. package/dist/routes/jobs.d.ts.map +0 -1
  1011. package/dist/routes/jobs.js +0 -25
  1012. package/dist/routes/jobs.js.map +0 -1
  1013. package/dist/trpc/routers/sandboxes.d.ts +0 -64
  1014. package/dist/trpc/routers/sandboxes.d.ts.map +0 -1
  1015. package/dist/trpc/routers/sandboxes.js +0 -327
  1016. package/dist/trpc/routers/sandboxes.js.map +0 -1
  1017. package/dist/unified-websocket-runner.d.ts +0 -280
  1018. package/dist/unified-websocket-runner.d.ts.map +0 -1
  1019. package/dist/unified-websocket-runner.js +0 -3840
  1020. package/dist/unified-websocket-runner.js.map +0 -1
package/dist/http-api.js CHANGED
@@ -1,30 +1,46 @@
1
1
  import { createServer } from "node:http";
2
2
  import { gzipSync } from "node:zlib";
3
- import { mkdir, writeFile, stat, readFile } from "node:fs/promises";
4
- import { readFileSync, readdirSync, existsSync } from "node:fs";
3
+ import { once } from "node:events";
4
+ import { readFileSync, readdirSync, existsSync, promises as fsp } from "node:fs";
5
5
  import nodePath from "node:path";
6
- import { createLogger, buildAssetUrl } from "@nodetool-ai/config";
6
+ import os from "node:os";
7
+ import { GZIP_THRESHOLD } from "./lib/compression.js";
8
+ import { withCacheBuster } from "./lib/example-thumbnail.js";
9
+ import { loadExampleGraph, deriveExampleAssetsDir } from "./example-workflows.js";
10
+ import { createLogger } from "@nodetool-ai/config";
7
11
  import { workflowToDsl } from "@nodetool-ai/dsl";
8
- import { Workflow, WorkflowVersion, Job, Asset } from "@nodetool-ai/models";
9
- import { loadPythonPackageMetadata, NodeRegistry } from "@nodetool-ai/node-sdk";
10
- import { registerBaseNodes } from "@nodetool-ai/base-nodes";
11
- import { registerElevenLabsNodes } from "@nodetool-ai/elevenlabs-nodes";
12
- import { registerTransformersJsNodes } from "@nodetool-ai/transformers-js-nodes";
13
- import { registerFalNodes } from "@nodetool-ai/fal-nodes";
14
- import { registerKieNodes } from "@nodetool-ai/kie-nodes";
15
- import { registerReplicateNodes } from "@nodetool-ai/replicate-nodes";
16
- import { PythonNodeExecutor, PythonStdioBridge } from "@nodetool-ai/runtime";
17
- import { WorkflowRunner } from "@nodetool-ai/kernel";
12
+ import { Workflow, WorkflowCollaborator, Asset, Project } from "@nodetool-ai/models";
13
+ import { loadPythonPackageMetadata } from "@nodetool-ai/node-sdk";
14
+ import { bootstrapNodeRegistry } from "./node-registry-setup.js";
15
+ import { PythonNodeExecutor, createPythonBridge, logPythonWorkerStderr } from "@nodetool-ai/runtime";
16
+ import { createAssetModelInterface, updateAssetBytesModelInterface } from "./lib/asset-model-interface.js";
17
+ import { probeAssetDurationSeconds } from "./lib/asset-duration.js";
18
+ import { verdictSchema } from "@nodetool-ai/protocol";
19
+ import { cancelDebugSession, debugSessions, peekDebugSession, runWorkflow, submitEscalationVerdict } from "@nodetool-ai/execution/service";
20
+ import { resolveWorkflowWorkspace } from "./lib/workflow-workspace.js";
18
21
  import { handleOpenAIRequest } from "./openai-api.js";
19
22
  import { handleOAuthRequest } from "./oauth-api.js";
20
23
  import { createStorageHandler } from "./storage-api.js";
21
24
  import { handleFileRequest } from "./file-api.js";
25
+ import { storeAssetWithThumbnail, storeAssetFileWithThumbnail, generateThumbnailForStoredAsset } from "./lib/thumbnail.js";
26
+ import { FileStorageAdapter } from "@nodetool-ai/storage";
27
+ import { getAssetAdapter, getTempAdapter } from "./lib/storage.js";
28
+ import { probeHasAudio, extractAudio, MediaToolingMissingError } from "./lib/media.js";
29
+ import { packWorkflowsBundle, importWorkflowBundle } from "./lib/workflow-bundle.js";
30
+ import { listExampleApps } from "./lib/example-apps.js";
31
+ import { createWorkflow, deleteWorkflow, updateWorkflow, WorkflowServiceError } from "./lib/workflow-service.js";
32
+ import { isNonEmptyString, isObjectLike, isRecord, isString } from "./lib/wire-values.js";
33
+ import { assetCreateBodySchema, escalationVerdictBodySchema, workflowRequestBodySchema, workflowRunBodySchema, workflowsExportBundleBodySchema } from "./http-body-schemas.js";
22
34
  const log = createLogger("nodetool.websocket.http");
23
35
  // ── Content type to file extension mapping ─────────────────────────
24
36
  // Asset filename + storage-path helpers live in `./lib/asset-paths.ts` so the
25
37
  // tRPC router can import them without dragging in the full http-api module
26
38
  // graph. Re-exported here for any remaining REST callers.
27
- import { getAssetFileName, getAssetStoragePath } from "./lib/asset-paths.js";
39
+ import { getAssetFileName, getAssetStorageKey, getAssetStoragePath, retrieveAssetBytes, localAssetPath, normalizeAssetContentType } from "./lib/asset-paths.js";
40
+ import { resolveAssetBytesForExport } from "./lib/asset-export.js";
41
+ import { toAssetResponse } from "./lib/asset-response.js";
42
+ import { scheduleVideoProxy } from "./lib/video-proxy.js";
43
+ export { toAssetResponse };
28
44
  export { getAssetFileName, getAssetStoragePath };
29
45
  // Lazily created storage handler — recreated if options change
30
46
  let _storageHandler = null;
@@ -37,23 +53,18 @@ function getStorageHandler(opts) {
37
53
  }
38
54
  return _storageHandler;
39
55
  }
40
- async function getWorkflowRuntimeEnvironment(options = {}) {
56
+ export async function getWorkflowRuntimeEnvironment(options = {}) {
41
57
  if (!workflowRuntimePromise) {
42
58
  workflowRuntimePromise = (async () => {
43
- const registry = options.registry ?? new NodeRegistry();
44
- if (!options.registry) {
45
- registry.loadPythonMetadata({
46
- roots: options.metadataRoots,
47
- maxDepth: options.metadataMaxDepth ?? 8
48
- });
49
- registerBaseNodes(registry);
50
- registerElevenLabsNodes(registry);
51
- registerTransformersJsNodes(registry);
52
- registerFalNodes(registry);
53
- registerKieNodes(registry);
54
- registerReplicateNodes(registry);
59
+ const bootstrapOptions = {
60
+ metadataMaxDepth: options.metadataMaxDepth ?? 8,
61
+ log
62
+ };
63
+ if (options.metadataRoots) {
64
+ bootstrapOptions.metadataRoots = options.metadataRoots;
55
65
  }
56
- const pythonBridge = new PythonStdioBridge({
66
+ const registry = options.registry ?? (await bootstrapNodeRegistry(bootstrapOptions));
67
+ const pythonBridge = createPythonBridge({
57
68
  workerArgs: process.env["NODETOOL_WORKER_NAMESPACES"]
58
69
  ? ["--namespaces", process.env["NODETOOL_WORKER_NAMESPACES"]]
59
70
  : []
@@ -70,10 +81,13 @@ async function getWorkflowRuntimeEnvironment(options = {}) {
70
81
  let pythonBridgeReady = false;
71
82
  pythonBridge.on("stderr", (msg) => {
72
83
  for (const line of msg.split("\n")) {
73
- if (line.trim())
74
- log.debug(`[python-worker] ${line}`);
84
+ logPythonWorkerStderr(line, log);
75
85
  }
76
86
  });
87
+ pythonBridge.on("error", (err) => {
88
+ log.error(`HTTP API Python bridge protocol error: ${err.message}`);
89
+ pythonBridgeReady = false;
90
+ });
77
91
  pythonBridge.on("exit", (code) => {
78
92
  log.warn(`HTTP API Python worker exited with code ${code}`);
79
93
  pythonBridgeReady = false;
@@ -118,7 +132,7 @@ async function getWorkflowRuntimeEnvironment(options = {}) {
118
132
  .getNodeMetadata()
119
133
  .find((n) => n.node_type === node.type);
120
134
  const props = (node.properties ?? node.data ?? {});
121
- return new PythonNodeExecutor(pythonBridge, node.type, props, Object.fromEntries((meta?.outputs ?? []).map((o) => [o.name, o.type.type])), meta?.required_settings ?? []);
135
+ return new PythonNodeExecutor(pythonBridge, node.type, props, Object.fromEntries((meta?.outputs ?? []).map((o) => [o.name, o.type.type])), meta?.required_settings ?? [], node.id, meta?.requires_vram_gb);
122
136
  }
123
137
  if (registry.getMetadata(node.type) && !registry.has(node.type)) {
124
138
  const stderrSummary = pythonBridge.getRecentStderrSummary?.() ?? null;
@@ -131,14 +145,30 @@ async function getWorkflowRuntimeEnvironment(options = {}) {
131
145
  }
132
146
  return registry.resolve(node);
133
147
  };
134
- return { registry, pythonBridge, ensurePythonBridge, resolveExecutor };
148
+ return {
149
+ registry,
150
+ pythonBridge,
151
+ ensurePythonBridge,
152
+ resolveExecutor,
153
+ // Asset persistence for the run's ProcessingContext — an Output node
154
+ // that stores an image needs it on every host, not just chat turns.
155
+ configureContext: (context) => {
156
+ context.setModelInterfaces({
157
+ createAsset: createAssetModelInterface,
158
+ updateAssetBytes: updateAssetBytesModelInterface
159
+ });
160
+ },
161
+ // The stores the run reads through. `asset://<id>` inputs — every
162
+ // uploaded image wired into a graph — live in the asset store; without
163
+ // it the reference resolves to nothing and the node runs on an empty
164
+ // input. Same pair the streaming WebSocket runner uses.
165
+ assetStorage: getAssetAdapter(),
166
+ storage: getTempAdapter()
167
+ };
135
168
  })();
136
169
  }
137
170
  return workflowRuntimePromise;
138
171
  }
139
- // Rate-limit tracking for autosave: maps workflow_id -> last autosave timestamp (ms)
140
- const lastAutosaveTime = new Map();
141
- const AUTOSAVE_RATE_LIMIT_MS = 30_000;
142
172
  function normalizePath(pathname) {
143
173
  if (pathname.length > 1 && pathname.endsWith("/")) {
144
174
  return pathname.slice(0, -1);
@@ -160,18 +190,39 @@ function errorResponse(status, detail) {
160
190
  export function getUserId(request, headerName) {
161
191
  return (request.headers.get(headerName) ?? request.headers.get("x-user-id") ?? "1");
162
192
  }
163
- async function parseJsonBody(request) {
193
+ /** The raw JSON body, or null when there is nothing readable to parse. */
194
+ async function readJsonBody(request) {
164
195
  const contentType = request.headers.get("content-type") ?? "";
165
196
  if (!contentType.toLowerCase().includes("application/json")) {
166
197
  return null;
167
198
  }
168
199
  try {
169
- return (await request.json());
200
+ return await request.json();
170
201
  }
171
202
  catch {
172
203
  return null;
173
204
  }
174
205
  }
206
+ /**
207
+ * Parse a request body once, at the route boundary.
208
+ *
209
+ * `null` means "no body the route can read": a non-JSON content type,
210
+ * unparseable JSON, or a falsy JSON value (`null`, `0`, `""`) — the cases
211
+ * every route already answered with its own 400 via `if (!body)`.
212
+ *
213
+ * A truthy non-object body (a number, a string, an array) parses as a record
214
+ * with every field absent, which is exactly what the routes saw when they read
215
+ * `.name` off a number, so their own error messages still fire.
216
+ */
217
+ function parseBodyValue(raw, schema) {
218
+ if (!raw) {
219
+ return null;
220
+ }
221
+ return schema.parse(isRecord(raw) ? raw : {});
222
+ }
223
+ async function parseBody(request, schema) {
224
+ return parseBodyValue(await readJsonBody(request), schema);
225
+ }
175
226
  export function toWorkflowResponse(workflow) {
176
227
  return {
177
228
  id: workflow.id,
@@ -192,9 +243,11 @@ export function toWorkflowResponse(workflow) {
192
243
  path: workflow.path,
193
244
  run_mode: workflow.run_mode,
194
245
  workspace_id: workflow.workspace_id,
246
+ project_id: workflow.project_id,
195
247
  required_providers: null,
196
248
  required_models: null,
197
249
  html_app: workflow.html_app,
250
+ app_doc: workflow.app_doc ?? null,
198
251
  etag: workflow.getEtag()
199
252
  };
200
253
  }
@@ -276,236 +329,89 @@ export function parseLimit(url, defaultLimit = 100) {
276
329
  return defaultLimit;
277
330
  return Math.min(parsed, 500);
278
331
  }
279
- async function createWorkflow(body, userId) {
280
- if (!body || typeof body.name !== "string") {
281
- throw new Error("Invalid workflow");
282
- }
283
- if (!body.graph ||
284
- !Array.isArray(body.graph.nodes) ||
285
- !Array.isArray(body.graph.edges)) {
286
- throw new Error("graph is required and must have nodes and edges arrays");
287
- }
288
- const graph = body.graph;
289
- return (await Workflow.create({
290
- user_id: userId,
291
- name: body.name,
292
- tool_name: body.tool_name ?? null,
293
- package_name: body.package_name ?? null,
294
- path: body.path ?? null,
295
- tags: body.tags ?? [],
296
- description: body.description ?? "",
297
- thumbnail: body.thumbnail ?? null,
298
- thumbnail_url: body.thumbnail_url ?? null,
299
- access: body.access === "public" ? "public" : "private",
300
- graph,
301
- settings: body.settings ?? null,
302
- run_mode: body.run_mode ?? "workflow",
303
- workspace_id: body.workspace_id ?? null,
304
- html_app: body.html_app ?? null
305
- }));
306
- }
307
- async function updateWorkflow(id, body, userId) {
308
- if (!body || typeof body.name !== "string") {
309
- throw new Error("Invalid workflow");
310
- }
311
- if (!body.graph ||
312
- !Array.isArray(body.graph.nodes) ||
313
- !Array.isArray(body.graph.edges)) {
314
- throw new Error("graph is required and must have nodes and edges arrays");
315
- }
316
- const graph = body.graph;
317
- const existing = (await Workflow.get(id));
318
- if (existing && existing.user_id !== userId) {
319
- throw new Error("Workflow not found");
320
- }
321
- if (existing) {
322
- existing.name = body.name;
323
- existing.tool_name = body.tool_name ?? null;
324
- existing.description = body.description ?? "";
325
- existing.tags = body.tags ?? [];
326
- existing.package_name = body.package_name ?? null;
327
- if (body.thumbnail !== undefined)
328
- existing.thumbnail = body.thumbnail;
329
- existing.access = body.access === "public" ? "public" : "private";
330
- existing.graph = graph;
331
- existing.settings = body.settings ?? null;
332
- if (body.run_mode !== undefined && body.run_mode !== null)
333
- existing.run_mode = body.run_mode;
334
- existing.workspace_id = body.workspace_id ?? null;
335
- existing.html_app = body.html_app ?? null;
336
- await existing.save();
337
- return existing;
338
- }
339
- // Upsert: create the workflow if it doesn't exist
340
- return (await Workflow.create({
341
- id,
342
- user_id: userId,
343
- name: body.name,
344
- tool_name: body.tool_name ?? null,
345
- package_name: body.package_name ?? null,
346
- path: body.path ?? null,
347
- tags: body.tags ?? [],
348
- description: body.description ?? "",
349
- thumbnail: body.thumbnail ?? null,
350
- thumbnail_url: body.thumbnail_url ?? null,
351
- access: body.access === "public" ? "public" : "private",
352
- graph: body.graph,
353
- settings: body.settings ?? null,
354
- run_mode: body.run_mode ?? "workflow",
355
- workspace_id: body.workspace_id ?? null,
356
- html_app: body.html_app ?? null
357
- }));
358
- }
359
- export async function handleWorkflowRun(request, workflowId, options = {}) {
332
+ export async function handleWorkflowRun(request, workflowId, options = {},
333
+ /** Return the full debug report (summary + verdict) instead of the run row. */
334
+ debug = false) {
360
335
  if (request.method !== "POST") {
361
336
  return errorResponse(405, "Method not allowed");
362
337
  }
363
338
  const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
364
- const workflow = await Workflow.find(userId, workflowId);
365
- if (!workflow) {
366
- return errorResponse(404, "Workflow not found");
367
- }
368
- const runMode = workflow.run_mode ?? "workflow";
369
- if (runMode !== "workflow") {
370
- return errorResponse(400, `Workflow run mode "${runMode}" is not supported by the standalone backend`);
371
- }
372
- const body = await parseJsonBody(request);
373
- const params = body?.params ?? {};
374
- const graph = workflow.getGraph();
375
- const runnableGraph = {
376
- nodes: graph.nodes.map((node) => {
377
- const record = node;
378
- return {
379
- ...record,
380
- id: String(record.id ?? ""),
381
- type: String(record.type ?? ""),
382
- properties: (record.properties ?? record.data ?? {})
383
- };
384
- }),
385
- edges: graph.edges.map((edge) => {
386
- const record = edge;
387
- return {
388
- ...record,
389
- id: typeof record.id === "string" || record.id == null
390
- ? record.id
391
- : String(record.id),
392
- source: String(record.source ?? ""),
393
- target: String(record.target ?? ""),
394
- sourceHandle: String(record.sourceHandle ?? ""),
395
- targetHandle: String(record.targetHandle ?? ""),
396
- edge_type: record.edge_type === "control" ? "control" : "data"
397
- };
398
- })
339
+ const body = await parseBody(request, workflowRunBodySchema);
340
+ const runOptions = {
341
+ workflowId,
342
+ userId,
343
+ debug,
344
+ // Lazy: a nonexistent workflow 404s before the runtime bootstraps — a
345
+ // cold-start bootstrap failure must not turn that into a 500.
346
+ environment: () => getWorkflowRuntimeEnvironment(options),
347
+ params: body?.params ?? {},
348
+ projectId: body?.project_id ?? null,
349
+ background: body?.background ?? false,
350
+ interactive: body?.interactive === true,
351
+ // The server's own import site, so a test that mocks it still governs.
352
+ resolveWorkspace: resolveWorkflowWorkspace
399
353
  };
400
- const runtime = await getWorkflowRuntimeEnvironment(options);
401
- const hasPythonNode = runnableGraph.nodes.some((node) => {
402
- const nodeType = typeof node.type === "string" ? node.type : "";
403
- return (nodeType !== "" &&
404
- Boolean(runtime.registry.getMetadata(nodeType)) &&
405
- !runtime.registry.has(nodeType));
406
- });
407
- if (hasPythonNode) {
408
- await runtime.ensurePythonBridge();
409
- }
410
- const job = await Job.create({
411
- workflow_id: workflowId,
412
- user_id: userId,
413
- status: "running",
414
- params,
415
- graph: runnableGraph
416
- });
417
- const runner = new WorkflowRunner(job.id, {
418
- resolveExecutor: (node) => runtime.resolveExecutor(node)
419
- });
420
- const result = await runner.run({ job_id: job.id, workflow_id: workflowId, params }, runnableGraph);
421
- if (result.status === "completed") {
422
- job.markCompleted();
354
+ if (body?.max_decisions !== undefined) {
355
+ runOptions.maxDecisions = body.max_decisions;
423
356
  }
424
- else if (result.status === "cancelled") {
425
- job.markCancelled();
357
+ if (body?.max_retries_per_node !== undefined) {
358
+ runOptions.maxRetriesPerNode = body.max_retries_per_node;
426
359
  }
427
- else {
428
- job.markFailed(result.error ?? "Workflow run failed");
360
+ if (body?.decision_timeout_ms !== undefined) {
361
+ runOptions.decisionTimeoutMs = body.decision_timeout_ms;
429
362
  }
430
- await job.save();
431
- return jsonResponse({
432
- job_id: job.id,
433
- workflow_id: workflowId,
434
- status: result.status,
435
- outputs: result.outputs,
436
- error: result.error ?? null,
437
- message_count: result.messages.length,
438
- background: body?.background ?? false
439
- });
440
- }
441
- export async function handleWorkflowAutosave(request, workflowId, options) {
442
- if (request.method !== "POST" && request.method !== "PUT") {
443
- return errorResponse(405, "Method not allowed");
363
+ const outcome = await runWorkflow(runOptions);
364
+ if (outcome.kind === "error") {
365
+ return errorResponse(outcome.status, outcome.detail);
444
366
  }
367
+ return jsonResponse(outcome.payload);
368
+ }
369
+ /**
370
+ * `/api/debug/sessions/:id[/verdict|/cancel]` — the caller's half of an
371
+ * interactive run. GET peeks at the session, `verdict` answers the parked
372
+ * escalation and waits for the next event, `cancel` cancels the run and
373
+ * returns the final report.
374
+ */
375
+ export async function handleDebugSessionRequest(request, sessionId, subPath, options = {}) {
445
376
  const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
446
- const workflow = (await Workflow.get(workflowId));
447
- if (!workflow)
448
- return errorResponse(404, "Workflow not found");
449
- if (workflow.user_id !== userId)
450
- return errorResponse(404, "Workflow not found");
451
- const body = await parseJsonBody(request);
452
- if (!body || !body.graph) {
453
- return errorResponse(400, "Invalid body: graph is required");
454
- }
455
- const graph = body.graph;
456
- const force = body?.force === true;
457
- const maxVersions = typeof body?.max_versions === "number" ? body.max_versions : 10;
458
- // Rate-limit: skip if last autosave < 30s ago and force is false
459
- if (!force) {
460
- const last = lastAutosaveTime.get(workflowId);
461
- if (last !== undefined && Date.now() - last < AUTOSAVE_RATE_LIMIT_MS) {
462
- return jsonResponse({
463
- version: null,
464
- message: "Autosave skipped (rate limited)",
465
- skipped: true
466
- });
377
+ // Session existence (scoped to the caller) is checked before anything else —
378
+ // a malformed verdict against a foreign or expired session answers 404, not
379
+ // 400, exactly as it did when this handler owned the registry.
380
+ if (!debugSessions.get(sessionId, userId)) {
381
+ return errorResponse(404, "Debug session not found");
382
+ }
383
+ const answer = (outcome) => outcome.kind === "error"
384
+ ? errorResponse(outcome.status, outcome.detail)
385
+ : jsonResponse(outcome.payload);
386
+ if (subPath === null) {
387
+ if (request.method !== "GET") {
388
+ return errorResponse(405, "Method not allowed");
467
389
  }
390
+ return answer(peekDebugSession(sessionId, userId));
468
391
  }
469
- workflow.graph = graph;
470
- if (body.name !== undefined)
471
- workflow.name = body.name;
472
- if (body.description !== undefined)
473
- workflow.description = body.description;
474
- if (body.access === "public" || body.access === "private")
475
- workflow.access = body.access;
476
- await workflow.save();
477
- lastAutosaveTime.set(workflowId, Date.now());
478
- // Create a version and prune old ones if WorkflowVersion table is available
479
- let version = null;
480
- try {
481
- const nextVer = await WorkflowVersion.nextVersion(workflowId);
482
- const wv = new WorkflowVersion({
483
- workflow_id: workflowId,
484
- user_id: userId,
485
- graph,
486
- version: nextVer,
487
- save_type: "autosave",
488
- name: workflow.name,
489
- description: workflow.description
490
- });
491
- await wv.save();
492
- version = {
493
- id: wv.id,
494
- version: wv.version,
495
- workflow_id: wv.workflow_id,
496
- save_type: wv.save_type,
497
- created_at: wv.created_at
498
- };
499
- await WorkflowVersion.pruneOldVersions(workflowId, maxVersions);
392
+ if (subPath === "verdict") {
393
+ if (request.method !== "POST") {
394
+ return errorResponse(405, "Method not allowed");
395
+ }
396
+ const body = await parseBody(request, escalationVerdictBodySchema);
397
+ if (!body || body.escalation_id === undefined) {
398
+ return errorResponse(400, "Body must carry escalation_id and verdict");
399
+ }
400
+ const parsed = verdictSchema.safeParse(body.verdict);
401
+ if (!parsed.success) {
402
+ return errorResponse(400, `Invalid verdict: ${parsed.error.issues
403
+ .map((issue) => issue.message)
404
+ .join("; ")}`);
405
+ }
406
+ return answer(await submitEscalationVerdict(sessionId, userId, body.escalation_id, parsed.data));
500
407
  }
501
- catch {
502
- // non-fatal — version table may not exist
408
+ if (subPath === "cancel") {
409
+ if (request.method !== "POST") {
410
+ return errorResponse(405, "Method not allowed");
411
+ }
412
+ return answer(await cancelDebugSession(sessionId, userId));
503
413
  }
504
- return jsonResponse({
505
- version,
506
- message: "Autosaved successfully",
507
- skipped: false
508
- });
414
+ return errorResponse(404, "Not found");
509
415
  }
510
416
  // ── Workflow tools ─────────────────────────────────────────────────────
511
417
  export async function handleWorkflowTools(request, options) {
@@ -528,13 +434,6 @@ export async function handleWorkflowTools(request, options) {
528
434
  // ── Workflow examples ──────────────────────────────────────────────────
529
435
  /** URL prefix for example workflow thumbnail images served by this API. */
530
436
  const EXAMPLES_THUMBNAILS_PREFIX = "/api/workflows/examples/thumbnails/";
531
- /**
532
- * Given an `examplesDir` (e.g. `.../nodetool/examples/nodetool-base`), return
533
- * the sibling assets directory (`.../nodetool/assets/nodetool-base`).
534
- */
535
- function deriveAssetsDir(examplesDir) {
536
- return nodePath.join(nodePath.dirname(nodePath.dirname(examplesDir)), "assets", nodePath.basename(examplesDir));
537
- }
538
437
  /**
539
438
  * Read example workflow metadata from a directory of JSON files.
540
439
  * Returns lightweight objects (no graph data) suitable for the /examples list.
@@ -544,11 +443,10 @@ function deriveAssetsDir(examplesDir) {
544
443
  * pointing to `/api/workflows/examples/thumbnails/<name>` is set so the
545
444
  * frontend can display the pre-generated JPG thumbnails.
546
445
  */
547
- function buildExamplesFromDir(examplesDir) {
446
+ function buildExamplesFromDir(examplesDir, examplesAssetsFallbackDir) {
548
447
  if (!existsSync(examplesDir))
549
448
  return [];
550
- // Derive the assets directory from the examples directory.
551
- const assetsDir = deriveAssetsDir(examplesDir);
449
+ const assetsDir = deriveExampleAssetsDir(examplesDir, examplesAssetsFallbackDir);
552
450
  const now = new Date().toISOString();
553
451
  const workflows = [];
554
452
  let files;
@@ -565,13 +463,16 @@ function buildExamplesFromDir(examplesDir) {
565
463
  try {
566
464
  const raw = readFileSync(nodePath.join(examplesDir, file), "utf8");
567
465
  const parsed = JSON.parse(raw);
568
- const name = typeof parsed.name === "string"
466
+ const name = isString(parsed.name)
569
467
  ? parsed.name
570
468
  : file.replace(/\.json$/i, "");
571
- // Point thumbnail_url to the served JPG when the file exists in assets.
469
+ // Point thumbnail_url to the served JPG when the file exists in
470
+ // assets. withCacheBuster() appends ?v=<md5-8> so the browser cache
471
+ // invalidates whenever the JPG is regenerated.
572
472
  const jpgFile = `${name}.jpg`;
573
- const thumbnailUrl = existsSync(nodePath.join(assetsDir, jpgFile))
574
- ? `${EXAMPLES_THUMBNAILS_PREFIX}${encodeURIComponent(jpgFile)}`
473
+ const jpgPath = nodePath.join(assetsDir, jpgFile);
474
+ const thumbnailUrl = existsSync(jpgPath)
475
+ ? withCacheBuster(`${EXAMPLES_THUMBNAILS_PREFIX}${encodeURIComponent(jpgFile)}`, jpgPath)
575
476
  : null;
576
477
  workflows.push({
577
478
  id: file,
@@ -580,23 +481,24 @@ function buildExamplesFromDir(examplesDir) {
580
481
  updated_at: now,
581
482
  name,
582
483
  tool_name: null,
583
- description: typeof parsed.description === "string" ? parsed.description : "",
584
- tags: Array.isArray(parsed.tags)
585
- ? parsed.tags.filter((t) => typeof t === "string")
586
- : [],
484
+ description: isString(parsed.description) ? parsed.description : "",
485
+ tags: Array.isArray(parsed.tags) ? parsed.tags.filter(isString) : [],
587
486
  thumbnail: thumbnailUrl ? jpgFile : null,
588
487
  thumbnail_url: thumbnailUrl,
589
488
  graph: { nodes: [], edges: [] },
590
489
  input_schema: null,
591
490
  output_schema: null,
592
491
  settings: null,
593
- package_name: typeof parsed.package_name === "string" ? parsed.package_name : null,
492
+ package_name: isString(parsed.package_name)
493
+ ? parsed.package_name
494
+ : null,
594
495
  path: null,
595
496
  run_mode: null,
596
497
  workspace_id: null,
597
498
  required_providers: null,
598
499
  required_models: null,
599
500
  html_app: null,
501
+ app_doc: isObjectLike(parsed.app_doc) ? parsed.app_doc : null,
600
502
  etag: null
601
503
  });
602
504
  }
@@ -606,10 +508,10 @@ function buildExamplesFromDir(examplesDir) {
606
508
  }
607
509
  return workflows;
608
510
  }
609
- function buildExampleWorkflows(options) {
511
+ export function buildExampleWorkflows(options) {
610
512
  // If a static examples directory is configured, use it directly — no Python needed.
611
513
  if (options.examplesDir) {
612
- return buildExamplesFromDir(options.examplesDir);
514
+ return buildExamplesFromDir(options.examplesDir, options.examplesAssetsFallbackDir);
613
515
  }
614
516
  const loaded = loadPythonPackageMetadata({
615
517
  roots: options.metadataRoots,
@@ -644,29 +546,13 @@ function buildExampleWorkflows(options) {
644
546
  required_providers: null,
645
547
  required_models: null,
646
548
  html_app: null,
549
+ app_doc: null,
647
550
  etag: null
648
551
  });
649
552
  }
650
553
  }
651
554
  return workflows;
652
555
  }
653
- function loadExampleGraph(packageName, exampleName, options) {
654
- const loaded = loadPythonPackageMetadata({
655
- roots: options.metadataRoots,
656
- maxDepth: options.metadataMaxDepth
657
- });
658
- const pkg = loaded.packages.find((p) => p.name === packageName);
659
- if (!pkg?.sourceFolder)
660
- return null;
661
- const examplePath = nodePath.join(pkg.sourceFolder, "nodetool", "examples", packageName, `${exampleName}.json`);
662
- try {
663
- const raw = readFileSync(examplePath, "utf8");
664
- return JSON.parse(raw);
665
- }
666
- catch {
667
- return null;
668
- }
669
- }
670
556
  export async function handleWorkflowExamples(request, options) {
671
557
  if (request.method !== "GET") {
672
558
  return errorResponse(405, "Method not allowed");
@@ -674,6 +560,27 @@ export async function handleWorkflowExamples(request, options) {
674
560
  const workflows = buildExampleWorkflows(options);
675
561
  return jsonResponse({ workflows, next: null });
676
562
  }
563
+ /**
564
+ * GET /api/workflows/examples/:package/:name — one example workflow, graph
565
+ * included. The `get_example_workflow` agent tool and the `nodetool` object
566
+ * model (`nodetool.workflows.example`) call this to feed graphs into
567
+ * `copyFrom`; the create-from-example flow stays on POST /api/workflows.
568
+ */
569
+ export async function handleWorkflowExampleByName(request, packageName, exampleName, options) {
570
+ if (request.method !== "GET") {
571
+ return errorResponse(405, "Method not allowed");
572
+ }
573
+ const example = loadExampleGraph(packageName, exampleName, {
574
+ examplesDir: options.examplesDir,
575
+ examplesAssetsFallbackDir: options.examplesAssetsFallbackDir,
576
+ metadataRoots: options.metadataRoots,
577
+ metadataMaxDepth: options.metadataMaxDepth
578
+ });
579
+ if (!example) {
580
+ return errorResponse(404, `Example '${exampleName}' not found in package '${packageName}'`);
581
+ }
582
+ return jsonResponse(example);
583
+ }
677
584
  export async function handleWorkflowExamplesSearch(request, options) {
678
585
  if (request.method !== "GET") {
679
586
  return errorResponse(405, "Method not allowed");
@@ -706,7 +613,7 @@ export async function handleWorkflowExamplesThumbnail(request, filename, options
706
613
  return errorResponse(404, "Examples not configured");
707
614
  }
708
615
  // Derive the assets directory from the examples directory.
709
- const assetsDir = deriveAssetsDir(options.examplesDir);
616
+ const assetsDir = deriveExampleAssetsDir(options.examplesDir, options.examplesAssetsFallbackDir);
710
617
  // Prevent path traversal — only allow a plain filename (no slashes).
711
618
  const safe = nodePath.basename(filename);
712
619
  const safeLower = safe.toLowerCase();
@@ -746,69 +653,6 @@ export async function handleWorkflowExamplesThumbnail(request, filename, options
746
653
  }
747
654
  });
748
655
  }
749
- // ── Workflow app page ──────────────────────────────────────────────────
750
- export async function handleWorkflowApp(request, workflowId, options) {
751
- if (request.method !== "GET") {
752
- return errorResponse(405, "Method not allowed");
753
- }
754
- const baseUrl = options.baseUrl ?? "http://127.0.0.1:7777";
755
- const html = `<!DOCTYPE html><html><head><title>Workflow App</title>
756
- <script>window.WORKFLOW_ID=${JSON.stringify(workflowId)};window.API_URL=${JSON.stringify(baseUrl)};</script>
757
- </head><body><div id="app"></div></body></html>`;
758
- return new Response(html, {
759
- status: 200,
760
- headers: { "content-type": "text/html" }
761
- });
762
- }
763
- // ── Workflow generate-name ─────────────────────────────────────────────
764
- /**
765
- * Generate a descriptive name for a workflow based on its node types.
766
- * The Python backend uses an LLM for this; the TS standalone mode derives
767
- * a name from the workflow graph's node types instead.
768
- *
769
- * Node types follow the pattern `<root>.<category>[.<subcategory>...]`,
770
- * e.g. `nodetool.text.Generate`. We collect unique category segments
771
- * (parts[1]) to form a human-readable label.
772
- */
773
- function deriveWorkflowName(workflow) {
774
- const graph = workflow.graph;
775
- const nodes = graph?.nodes ?? [];
776
- if (nodes.length === 0) {
777
- return workflow.name || "Untitled Workflow";
778
- }
779
- // Collect unique category segments from node types (second dotted segment).
780
- const categories = new Set();
781
- for (const n of nodes) {
782
- if (typeof n.type === "string") {
783
- const parts = n.type.split(".");
784
- // Require at least root.category format.
785
- if (parts.length >= 2 && parts[1]) {
786
- categories.add(parts[1]);
787
- }
788
- }
789
- }
790
- const segments = Array.from(categories).slice(0, 3);
791
- if (segments.length === 0) {
792
- return workflow.name || "Untitled Workflow";
793
- }
794
- const label = segments
795
- .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
796
- .join(" + ");
797
- return `${label} Workflow`;
798
- }
799
- export async function handleWorkflowGenerateName(request, workflowId, options) {
800
- if (request.method !== "POST") {
801
- return errorResponse(405, "Method not allowed");
802
- }
803
- const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
804
- const workflow = (await Workflow.get(workflowId));
805
- if (!workflow)
806
- return errorResponse(404, "Workflow not found");
807
- if (workflow.user_id !== userId)
808
- return errorResponse(404, "Workflow not found");
809
- const name = deriveWorkflowName(workflow);
810
- return jsonResponse({ name });
811
- }
812
656
  // ── Workflow DSL export (stub) ─────────────────────────────────────────
813
657
  export async function handleWorkflowDslExport(request, workflowId, options) {
814
658
  if (request.method !== "GET") {
@@ -841,107 +685,178 @@ export async function handleWorkflowDslExport(request, workflowId, options) {
841
685
  return errorResponse(400, message);
842
686
  }
843
687
  }
844
- // ── Workflow Gradio export (stub) ──────────────────────────────────────
845
- export async function handleWorkflowGradioExport(request, workflowId, options) {
846
- if (request.method !== "POST") {
847
- return errorResponse(405, "Method not allowed");
848
- }
849
- const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
688
+ // ── Workflow bundle export/import (.nodetool) ──────────────────────────
689
+ export { resolveAssetBytesForExport };
690
+ async function loadBundledWorkflow(workflowId, userId) {
850
691
  const workflow = (await Workflow.get(workflowId));
851
- if (!workflow)
852
- return errorResponse(404, "Workflow not found");
853
- if (workflow.user_id !== userId) {
854
- return errorResponse(404, "Workflow not found");
692
+ if (!workflow) {
693
+ return { error: errorResponse(404, `Workflow not found: ${workflowId}`) };
694
+ }
695
+ if (workflow.access !== "public" && workflow.user_id !== userId) {
696
+ return { error: errorResponse(404, `Workflow not found: ${workflowId}`) };
697
+ }
698
+ if (!workflow.graph) {
699
+ return { error: errorResponse(400, "Workflow has no graph to export") };
855
700
  }
856
- // Gradio export requires the Python Gradio library; return 501 in standalone TS mode.
857
- return errorResponse(501, "Workflow Gradio export is not available in standalone mode");
858
- }
859
- // ── Workflow versions ──────────────────────────────────────────────────
860
- function toVersionResponse(v) {
861
701
  return {
862
- id: v.id,
863
- workflow_id: v.workflow_id,
864
- user_id: v.user_id,
865
- name: v.name,
866
- description: v.description,
867
- graph: v.graph,
868
- version: v.version,
869
- save_type: v.save_type ?? "manual",
870
- autosave_metadata: v.autosave_metadata ?? null,
871
- created_at: v.created_at
702
+ id: workflow.id,
703
+ name: workflow.name,
704
+ description: workflow.description ?? "",
705
+ tags: workflow.tags ?? [],
706
+ run_mode: workflow.run_mode ?? null,
707
+ settings: workflow.settings ?? null,
708
+ graph: workflow.graph
872
709
  };
873
710
  }
874
- export async function handleWorkflowVersions(request, workflowId, options) {
875
- const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
876
- if (request.method === "POST") {
877
- const workflow = (await Workflow.get(workflowId));
878
- if (!workflow)
879
- return errorResponse(404, "Workflow not found");
880
- if (workflow.user_id !== userId)
881
- return errorResponse(404, "Workflow not found");
882
- const body = await parseJsonBody(request);
883
- const nextVer = await WorkflowVersion.nextVersion(workflowId);
884
- const version = (await WorkflowVersion.create({
885
- workflow_id: workflowId,
886
- user_id: userId,
887
- name: body?.name ?? null,
888
- description: body?.description ?? null,
889
- graph: workflow.graph,
890
- version: nextVer
891
- }));
892
- return jsonResponse(toVersionResponse(version));
711
+ function bundleResponse(bytes, name) {
712
+ const safe = name.replace(/[^A-Za-z0-9._-]+/g, "_") || "workflows";
713
+ // Copy into an ArrayBuffer-backed view so it satisfies BodyInit.
714
+ const body = new Uint8Array(bytes);
715
+ return new Response(body, {
716
+ status: 200,
717
+ headers: {
718
+ "content-type": "application/zip",
719
+ "content-length": String(bytes.byteLength),
720
+ "content-disposition": `attachment; filename="${safe}.nodetool"`,
721
+ "cache-control": "no-store"
722
+ }
723
+ });
724
+ }
725
+ /** GET /api/workflows/:id/export-bundle — single workflow as a .nodetool zip. */
726
+ export async function handleWorkflowExportBundle(request, workflowId, options) {
727
+ if (request.method !== "GET") {
728
+ return errorResponse(405, "Method not allowed");
893
729
  }
894
- if (request.method === "GET") {
895
- const url = new URL(request.url);
896
- const limit = parseLimit(url, 100);
897
- const versions = await WorkflowVersion.listForWorkflow(workflowId, {
898
- limit
899
- });
900
- return jsonResponse({ versions: versions.map(toVersionResponse) });
730
+ const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
731
+ const loaded = await loadBundledWorkflow(workflowId, userId);
732
+ if ("error" in loaded) {
733
+ return loaded.error;
901
734
  }
902
- return errorResponse(405, "Method not allowed");
735
+ const { bytes } = await packWorkflowsBundle({
736
+ workflows: [loaded],
737
+ fetchAssetBytes: resolveAssetBytesForExport
738
+ });
739
+ return bundleResponse(bytes, loaded.name);
903
740
  }
904
- export async function handleWorkflowVersionByNumber(request, workflowId, versionNumber, options) {
741
+ /** POST /api/workflows/export-bundle {workflow_ids} — multiple workflows. */
742
+ export async function handleWorkflowsExportBundle(request, options) {
743
+ if (request.method !== "POST") {
744
+ return errorResponse(405, "Method not allowed");
745
+ }
905
746
  const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
906
- if (request.method === "GET") {
907
- const version = await WorkflowVersion.findByVersion(workflowId, versionNumber);
908
- if (!version)
909
- return errorResponse(404, "Version not found");
910
- const workflow = (await Workflow.get(workflowId));
911
- if (!workflow)
912
- return errorResponse(404, "Workflow not found");
913
- if (workflow.user_id !== userId)
914
- return errorResponse(404, "Workflow not found");
915
- return jsonResponse(toVersionResponse(version));
747
+ const body = await parseBody(request, workflowsExportBundleBodySchema);
748
+ const ids = body?.workflow_ids ?? [];
749
+ if (ids.length === 0) {
750
+ return errorResponse(400, "workflow_ids (non-empty array) is required");
916
751
  }
917
- if (request.method === "POST") {
918
- // restore: copy version graph back to workflow
919
- const version = await WorkflowVersion.findByVersion(workflowId, versionNumber);
920
- if (!version)
921
- return errorResponse(404, "Version not found");
922
- const workflow = (await Workflow.get(workflowId));
923
- if (!workflow)
924
- return errorResponse(404, "Workflow not found");
925
- if (workflow.user_id !== userId)
926
- return errorResponse(404, "Workflow not found");
927
- workflow.graph = version.graph;
928
- await workflow.save();
929
- return jsonResponse(toWorkflowResponse(workflow));
752
+ const loadedResults = await Promise.all(ids.map((id) => loadBundledWorkflow(id, userId)));
753
+ const workflows = [];
754
+ for (const loaded of loadedResults) {
755
+ if ("error" in loaded) {
756
+ return loaded.error;
757
+ }
758
+ workflows.push(loaded);
930
759
  }
931
- return errorResponse(405, "Method not allowed");
760
+ const { bytes } = await packWorkflowsBundle({
761
+ workflows,
762
+ fetchAssetBytes: resolveAssetBytesForExport
763
+ });
764
+ const name = workflows.length === 1
765
+ ? workflows[0].name
766
+ : `${workflows.length}-workflows`;
767
+ return bundleResponse(bytes, name);
932
768
  }
933
- export async function handleWorkflowVersionDeleteById(request, _workflowId, versionId, options) {
934
- if (request.method !== "DELETE") {
769
+ /** POST /api/workflows/import-bundle — multipart .nodetool upload. */
770
+ export async function handleWorkflowImportBundle(request, options) {
771
+ if (request.method !== "POST") {
935
772
  return errorResponse(405, "Method not allowed");
936
773
  }
937
774
  const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
938
- const version = (await WorkflowVersion.get(versionId));
939
- if (!version)
940
- return errorResponse(404, "Version not found");
941
- if (version.user_id !== userId)
942
- return errorResponse(404, "Version not found");
943
- await version.delete();
944
- return new Response(null, { status: 204 });
775
+ let projectId = new URL(request.url).searchParams.get("project_id") ?? "default";
776
+ let zipBytes = null;
777
+ const contentType = request.headers.get("content-type") ?? "";
778
+ if (contentType.toLowerCase().includes("multipart/form-data")) {
779
+ try {
780
+ const fd = await request.formData();
781
+ const file = fd.get("file");
782
+ const formProjectId = fd.get("project_id");
783
+ if (typeof formProjectId === "string" && formProjectId) {
784
+ projectId = formProjectId;
785
+ }
786
+ if (file) {
787
+ zipBytes = new Uint8Array(await file.arrayBuffer());
788
+ }
789
+ }
790
+ catch {
791
+ return errorResponse(400, "Invalid multipart form data");
792
+ }
793
+ }
794
+ else {
795
+ const buf = await request.arrayBuffer();
796
+ if (buf.byteLength > 0) {
797
+ zipBytes = new Uint8Array(buf);
798
+ }
799
+ }
800
+ if (!zipBytes) {
801
+ return errorResponse(400, "A .nodetool bundle file is required");
802
+ }
803
+ if (projectId !== "default") {
804
+ try {
805
+ await Project.requireOwned(userId, projectId);
806
+ }
807
+ catch {
808
+ return errorResponse(400, "Project not found");
809
+ }
810
+ }
811
+ let result;
812
+ try {
813
+ result = await importWorkflowBundle(zipBytes, {
814
+ storeAsset: async ({ bytes, fileName, contentType: assetType }) => {
815
+ const asset = (await Asset.create({
816
+ user_id: userId,
817
+ name: fileName,
818
+ content_type: assetType,
819
+ parent_id: userId,
820
+ project_id: projectId,
821
+ size: bytes.byteLength
822
+ }));
823
+ const storedName = getAssetFileName(asset.id, asset.content_type);
824
+ await storeAssetWithThumbnail(asset.user_id, asset.id, storedName, bytes, asset.content_type);
825
+ return { uri: `asset://${storedName}`, assetId: asset.id };
826
+ }
827
+ });
828
+ }
829
+ catch (error) {
830
+ return errorResponse(400, `Invalid bundle: ${error instanceof Error ? error.message : String(error)}`);
831
+ }
832
+ const created = [];
833
+ try {
834
+ for (const wf of result.workflows) {
835
+ const workflow = await createWorkflow(userId, {
836
+ name: wf.name,
837
+ description: wf.description ?? "",
838
+ tags: wf.tags ?? [],
839
+ access: "private",
840
+ graph: wf.graph,
841
+ settings: wf.settings ?? null,
842
+ run_mode: wf.run_mode ?? "workflow"
843
+ });
844
+ created.push(toWorkflowResponse(workflow));
845
+ }
846
+ }
847
+ catch (error) {
848
+ // createWorkflow throws on a malformed graph (e.g. missing edges array),
849
+ // which unpackWorkflowBundle does not validate. Surface it as a 400 rather
850
+ // than an unhandled 500. (Earlier-created workflows in the loop remain —
851
+ // the import is not transactional; reported as a bad-bundle error.)
852
+ return errorResponse(400, `Invalid bundle: ${error instanceof Error ? error.message : String(error)}`);
853
+ }
854
+ return jsonResponse({
855
+ workflows: created,
856
+ imported: result.imported.length,
857
+ missing: result.missing,
858
+ checksum_mismatches: result.checksumMismatches
859
+ });
945
860
  }
946
861
  export async function handleWorkflowsRoot(request, options) {
947
862
  const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
@@ -949,11 +864,14 @@ export async function handleWorkflowsRoot(request, options) {
949
864
  if (request.method === "GET") {
950
865
  const limit = parseLimit(url, 100);
951
866
  const runMode = url.searchParams.get("run_mode")?.trim() ?? undefined;
952
- // cursor and columns params accepted for Python parity (cursor ignored in memory adapter)
953
- // columns param is Python-specific (column selection) — ignored here
867
+ const projectId = url.searchParams.get("project_id")?.trim() || undefined;
868
+ const startKey = url.searchParams.get("cursor")?.trim() || undefined;
869
+ // columns is Python-specific (column selection) and is ignored here.
954
870
  const [workflows, cursor] = await Workflow.paginate(userId, {
955
871
  limit,
956
- runMode
872
+ runMode,
873
+ projectId,
874
+ startKey
957
875
  });
958
876
  return jsonResponse({
959
877
  workflows: workflows.map((w) => toWorkflowResponse(w)),
@@ -961,24 +879,25 @@ export async function handleWorkflowsRoot(request, options) {
961
879
  });
962
880
  }
963
881
  if (request.method === "POST") {
964
- const body = await parseJsonBody(request);
882
+ const body = await parseBody(request, workflowRequestBodySchema);
965
883
  if (!body)
966
884
  return errorResponse(400, "Invalid JSON body");
885
+ const exampleName = url.searchParams.get("from_example_name")?.trim() || undefined;
967
886
  try {
968
- const fromPkg = url.searchParams.get("from_example_package")?.trim() ?? undefined;
969
- const fromName = url.searchParams.get("from_example_name")?.trim() ?? undefined;
970
- if (fromPkg &&
971
- fromName &&
972
- (!body.graph || body.graph.nodes?.length === 0)) {
973
- const example = loadExampleGraph(fromPkg, fromName, options);
974
- if (example?.graph) {
975
- body.graph = example.graph;
887
+ const workflow = await createWorkflow(userId, body, exampleName
888
+ ? {
889
+ packageName: url.searchParams.get("from_example_package")?.trim() ||
890
+ undefined,
891
+ exampleName,
892
+ apiOptions: options
976
893
  }
977
- }
978
- const workflow = await createWorkflow(body, userId);
894
+ : undefined);
979
895
  return jsonResponse(toWorkflowResponse(workflow));
980
896
  }
981
897
  catch (error) {
898
+ if (error instanceof WorkflowServiceError) {
899
+ return errorResponse(error.status, error.message);
900
+ }
982
901
  const message = error instanceof Error ? error.message : "Invalid workflow";
983
902
  return errorResponse(400, message);
984
903
  }
@@ -1013,42 +932,48 @@ export async function handleWorkflowById(request, workflowId, options) {
1013
932
  const workflow = (await Workflow.get(workflowId));
1014
933
  if (!workflow)
1015
934
  return errorResponse(404, "Workflow not found");
1016
- if (workflow.access !== "public" && workflow.user_id !== userId) {
935
+ const collaborator = workflow.user_id === userId
936
+ ? null
937
+ : await WorkflowCollaborator.findFor(workflowId, userId);
938
+ if (workflow.access !== "public" &&
939
+ workflow.user_id !== userId &&
940
+ !collaborator) {
1017
941
  return errorResponse(404, "Workflow not found");
1018
942
  }
1019
943
  return jsonResponse(toWorkflowResponse(workflow));
1020
944
  }
1021
945
  if (request.method === "PUT") {
1022
- const body = await parseJsonBody(request);
946
+ const body = await parseBody(request, workflowRequestBodySchema);
1023
947
  if (!body)
1024
948
  return errorResponse(400, "Invalid JSON body");
1025
949
  try {
1026
- const workflow = await updateWorkflow(workflowId, body, userId);
950
+ const workflow = await updateWorkflow(userId, workflowId, body);
1027
951
  return jsonResponse(toWorkflowResponse(workflow));
1028
952
  }
1029
953
  catch (error) {
954
+ if (error instanceof WorkflowServiceError) {
955
+ return errorResponse(error.status, error.message);
956
+ }
1030
957
  const message = error instanceof Error ? error.message : "Invalid workflow";
1031
- if (message === "Workflow not found")
1032
- return errorResponse(404, message);
1033
958
  return errorResponse(400, message);
1034
959
  }
1035
960
  }
1036
961
  if (request.method === "DELETE") {
1037
- const workflow = (await Workflow.get(workflowId));
1038
- if (!workflow)
1039
- return errorResponse(404, "Workflow not found");
1040
- if (workflow.user_id !== userId)
1041
- return errorResponse(404, "Workflow not found");
1042
- await workflow.delete();
962
+ try {
963
+ await deleteWorkflow(userId, workflowId);
964
+ }
965
+ catch (error) {
966
+ if (error instanceof WorkflowServiceError) {
967
+ return errorResponse(error.status, error.message);
968
+ }
969
+ throw error;
970
+ }
1043
971
  return new Response(null, { status: 204 });
1044
972
  }
1045
973
  return errorResponse(405, "Method not allowed");
1046
974
  }
1047
975
  // ── Job types & helpers ───────────────────────────────────────────
1048
- /**
1049
- * Full job response — still exported here because `mcp-server.ts` consumes it
1050
- * when serving job metadata over MCP. Consider relocating if MCP also migrates.
1051
- */
976
+ /** Full job response, shared by the REST routes that serve job metadata. */
1052
977
  export function toJobResponse(job) {
1053
978
  return {
1054
979
  id: job.id,
@@ -1056,94 +981,45 @@ export function toJobResponse(job) {
1056
981
  job_type: "workflow",
1057
982
  status: job.status,
1058
983
  workflow_id: job.workflow_id,
984
+ project_id: job.project_id,
1059
985
  started_at: job.started_at ?? null,
1060
986
  finished_at: job.finished_at ?? null,
1061
987
  error: job.error ?? null,
1062
- cost: null
1063
- };
1064
- }
1065
- // ── Trigger job stubs ─────────────────────────────────────────────
1066
- export async function handleTriggersRunning(request) {
1067
- if (request.method !== "GET") {
1068
- return errorResponse(405, "Method not allowed");
1069
- }
1070
- return jsonResponse({ workflows: [] });
1071
- }
1072
- export async function handleTriggerStart(request, _workflowId) {
1073
- if (request.method !== "POST") {
1074
- return errorResponse(405, "Method not allowed");
1075
- }
1076
- return errorResponse(501, "Trigger workflows not available in standalone mode");
1077
- }
1078
- export async function handleTriggerStop(request, _workflowId) {
1079
- if (request.method !== "POST") {
1080
- return errorResponse(405, "Method not allowed");
1081
- }
1082
- return errorResponse(501, "Trigger workflows not available in standalone mode");
1083
- }
1084
- // ── Nodes dummy ───────────────────────────────────────────────────
1085
- export async function handleNodesDummy(request) {
1086
- if (request.method !== "GET") {
1087
- return errorResponse(405, "Method not allowed");
1088
- }
1089
- return jsonResponse({
1090
- type: "asset",
1091
- uri: "",
1092
- asset_id: null,
1093
- data: null,
1094
- metadata: null
1095
- });
1096
- }
1097
- export function toAssetResponse(asset) {
1098
- const isFolder = asset.content_type === "folder";
1099
- const fileName = isFolder
1100
- ? null
1101
- : getAssetFileName(asset.id, asset.content_type);
1102
- const getUrl = fileName ? buildAssetUrl(fileName) : null;
1103
- const hasThumbnail = asset.content_type.startsWith("image/") ||
1104
- asset.content_type.startsWith("video/");
1105
- const updatedTs = asset.updated_at
1106
- ? Math.floor(new Date(asset.updated_at).getTime() / 1000)
1107
- : 0;
1108
- const thumbUrl = hasThumbnail
1109
- ? `/api/assets/${asset.id}/thumbnail?t=${updatedTs}`
1110
- : null;
1111
- return {
1112
- id: asset.id,
1113
- user_id: asset.user_id,
1114
- workflow_id: asset.workflow_id ?? null,
1115
- parent_id: asset.parent_id ?? null,
1116
- name: asset.name,
1117
- content_type: asset.content_type,
1118
- size: asset.size ?? null,
1119
- metadata: asset.metadata ?? null,
1120
- created_at: asset.created_at,
1121
- get_url: getUrl,
1122
- thumb_url: thumbUrl,
1123
- duration: asset.duration ?? null,
1124
- node_id: asset.node_id ?? null,
1125
- job_id: asset.job_id ?? null
988
+ cost: job.cost ?? null,
989
+ outputs: job.runOutputs()
1126
990
  };
1127
991
  }
1128
992
  /**
1129
993
  * Handle multipart file upload at POST /api/assets. JSON-only creation has
1130
994
  * moved to the tRPC `assets.create` procedure.
1131
995
  */
1132
- export async function handleAssetsRoot(request, options) {
996
+ export async function handleAssetsRoot(request, options, staged) {
1133
997
  const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
1134
998
  if (request.method === "POST") {
1135
999
  const contentType = request.headers.get("content-type") ?? "";
1136
1000
  let body = null;
1137
1001
  let fileBuffer = null;
1138
1002
  let fileSize = null;
1139
- if (contentType.toLowerCase().includes("multipart/form-data")) {
1003
+ if (staged) {
1004
+ body = parseBodyValue(staged.metadata, assetCreateBodySchema);
1005
+ if (staged.file) {
1006
+ body = {
1007
+ ...body,
1008
+ name: body?.name || staged.file.name,
1009
+ content_type: body?.content_type || staged.file.contentType,
1010
+ parent_id: body?.parent_id || userId
1011
+ };
1012
+ fileSize = staged.file.size;
1013
+ }
1014
+ }
1015
+ else if (contentType.toLowerCase().includes("multipart/form-data")) {
1140
1016
  try {
1141
1017
  const fd = await request.formData();
1142
1018
  const file = fd.get("file");
1143
1019
  // Frontend sends metadata as "json" field
1144
1020
  const assetJson = fd.get("json") ?? fd.get("asset");
1145
- if (assetJson && typeof assetJson === "string") {
1146
- body = JSON.parse(assetJson);
1021
+ if (isNonEmptyString(assetJson)) {
1022
+ body = parseBodyValue(JSON.parse(assetJson), assetCreateBodySchema);
1147
1023
  }
1148
1024
  if (file) {
1149
1025
  const arrayBuffer = await file.arrayBuffer();
@@ -1170,93 +1046,164 @@ export async function handleAssetsRoot(request, options) {
1170
1046
  }
1171
1047
  }
1172
1048
  else {
1173
- body = await parseJsonBody(request);
1049
+ body = await parseBody(request, assetCreateBodySchema);
1174
1050
  }
1175
1051
  if (!body ||
1176
- typeof body.name !== "string" ||
1177
- typeof body.content_type !== "string" ||
1178
- typeof body.parent_id !== "string") {
1052
+ body.name === undefined ||
1053
+ body.content_type === undefined ||
1054
+ body.parent_id === undefined) {
1179
1055
  return errorResponse(400, "Invalid JSON body: name, content_type, and parent_id are required");
1180
1056
  }
1181
1057
  const metadata = body.metadata ?? {};
1058
+ if (body.project_id && body.project_id !== "default") {
1059
+ try {
1060
+ await Project.requireOwned(userId, body.project_id);
1061
+ }
1062
+ catch {
1063
+ return errorResponse(400, "Project not found");
1064
+ }
1065
+ }
1066
+ const assetContentType = normalizeAssetContentType(body.content_type, body.name);
1067
+ const duration = staged?.file
1068
+ ? await probeAssetDurationSeconds(assetContentType, {
1069
+ path: staged.file.path
1070
+ })
1071
+ : fileBuffer
1072
+ ? await probeAssetDurationSeconds(assetContentType, {
1073
+ bytes: fileBuffer
1074
+ })
1075
+ : null;
1182
1076
  const asset = (await Asset.create({
1183
1077
  user_id: userId,
1184
1078
  name: body.name,
1185
- content_type: body.content_type,
1079
+ content_type: assetContentType,
1186
1080
  parent_id: body.parent_id,
1187
1081
  workflow_id: body.workflow_id ?? null,
1188
1082
  node_id: body.node_id ?? null,
1189
1083
  job_id: body.job_id ?? null,
1190
1084
  metadata: Object.keys(metadata).length > 0 ? metadata : (body.metadata ?? null),
1191
- size: fileSize ?? body.size ?? null
1085
+ project_id: body.project_id ?? "default",
1086
+ size: fileSize ?? body.size ?? null,
1087
+ duration
1192
1088
  }));
1193
- // Write file to storage directory so it's accessible via /api/storage/
1194
- if (fileBuffer) {
1195
- const storagePath = getAssetStoragePath(options.storage);
1089
+ if (fileBuffer || staged?.file) {
1196
1090
  const fileName = getAssetFileName(asset.id, asset.content_type);
1197
- const filePath = nodePath.join(storagePath, fileName);
1198
- await mkdir(storagePath, { recursive: true });
1199
- await writeFile(filePath, fileBuffer);
1091
+ log.info("asset upload (multipart)", {
1092
+ assetId: asset.id,
1093
+ fileName,
1094
+ contentType: asset.content_type,
1095
+ bytes: fileSize
1096
+ });
1097
+ try {
1098
+ if (staged?.file) {
1099
+ const adapter = getAssetAdapter();
1100
+ if (!(adapter instanceof FileStorageAdapter)) {
1101
+ throw new Error("Staged uploads require local storage");
1102
+ }
1103
+ await adapter.storeFile(getAssetStorageKey(asset.user_id, asset.id, asset.content_type), staged.file.path);
1104
+ // The stored file is local, so the thumbnail reads it in place at
1105
+ // any size.
1106
+ await generateThumbnailForStoredAsset(asset.user_id, asset.id, asset.content_type);
1107
+ }
1108
+ else if (fileBuffer) {
1109
+ await storeAssetWithThumbnail(asset.user_id, asset.id, fileName, new Uint8Array(fileBuffer), asset.content_type);
1110
+ }
1111
+ }
1112
+ catch (error) {
1113
+ // The row was created before the bytes were written. If the store
1114
+ // rejects (over the upload cap, or any S3/Supabase failure) drop the
1115
+ // row rather than leave it pointing at bytes that never landed.
1116
+ try {
1117
+ await asset.delete();
1118
+ }
1119
+ catch (deleteError) {
1120
+ log.warn("failed to delete asset row after upload failure", {
1121
+ assetId: asset.id,
1122
+ error: String(deleteError)
1123
+ });
1124
+ }
1125
+ throw error instanceof Error ? error : new Error(String(error));
1126
+ }
1127
+ scheduleVideoProxy(asset);
1200
1128
  }
1201
- return jsonResponse(toAssetResponse(asset));
1129
+ return jsonResponse(await toAssetResponse(asset));
1202
1130
  }
1203
1131
  return errorResponse(405, "Method not allowed");
1204
1132
  }
1205
- export async function handleAssetThumbnail(request, assetId, options) {
1206
- if (request.method === "POST") {
1207
- return errorResponse(501, "Thumbnail generation not available");
1208
- }
1209
- if (request.method !== "GET")
1210
- return errorResponse(405, "Method not allowed");
1133
+ /**
1134
+ * POST /api/assets/:id/extract-audio
1135
+ *
1136
+ * Extracts the audio track of a video asset into a new audio (WAV) asset and
1137
+ * returns it. If the video has no audio track, responds `{ has_audio: false }`
1138
+ * with no asset. Used by the timeline to create a linked, independently
1139
+ * editable audio clip when a video is imported.
1140
+ */
1141
+ export async function handleExtractAudio(request, options, assetId) {
1211
1142
  const userId = getUserId(request, options.userIdHeader ?? "x-user-id");
1212
- const asset = await Asset.find(userId, assetId);
1213
- if (!asset)
1143
+ const source = (await Asset.get(assetId));
1144
+ if (!source) {
1214
1145
  return errorResponse(404, "Asset not found");
1215
- if (!asset.hasThumbnail) {
1216
- return errorResponse(400, `Asset type '${asset.content_type}' does not support thumbnails`);
1217
1146
  }
1218
- const storagePath = getAssetStoragePath(options.storage);
1219
- // Try to serve a pre-generated thumbnail ({id}_thumb.jpg)
1220
- const thumbFileName = `${asset.id}_thumb.jpg`;
1221
- const thumbFilePath = nodePath.join(storagePath, thumbFileName);
1222
- try {
1223
- const thumbStat = await stat(thumbFilePath);
1224
- const data = await readFile(thumbFilePath);
1225
- return new Response(data, {
1226
- status: 200,
1227
- headers: {
1228
- "Content-Type": "image/jpeg",
1229
- "Content-Length": String(thumbStat.size),
1230
- "Cache-Control": "no-cache",
1231
- "Last-Modified": thumbStat.mtime.toUTCString()
1232
- }
1233
- });
1147
+ // Ownership guard: in multi-user mode an asset is only visible to its owner.
1148
+ if (source.user_id !== userId) {
1149
+ return errorResponse(404, "Asset not found");
1234
1150
  }
1235
- catch {
1236
- // No pre-generated thumbnail, fall through
1151
+ if (!source.content_type.startsWith("video/")) {
1152
+ return errorResponse(400, "Asset is not a video");
1237
1153
  }
1238
- // Fall back to serving the original file (for images)
1239
- if (asset.content_type.startsWith("image/")) {
1240
- const fileName = getAssetFileName(asset.id, asset.content_type);
1241
- const filePath = nodePath.join(storagePath, fileName);
1154
+ // ffmpeg reads a local file (managed or external) in place. Only a backend
1155
+ // with no local file has its bytes loaded and written to a temp input.
1156
+ const adapter = getAssetAdapter();
1157
+ const dir = await fsp.mkdtemp(nodePath.join(os.tmpdir(), "nodetool-extract-"));
1158
+ try {
1159
+ let inputPath = await localAssetPath(adapter, source.user_id, source.id, source.content_type);
1160
+ if (!inputPath) {
1161
+ const videoBytes = await retrieveAssetBytes(adapter, source.user_id, source.id, source.content_type);
1162
+ if (!videoBytes) {
1163
+ return errorResponse(404, "Asset bytes not found");
1164
+ }
1165
+ inputPath = nodePath.join(dir, "input");
1166
+ await fsp.writeFile(inputPath, videoBytes);
1167
+ }
1168
+ // The WAV stays on disk: it is stored from the file and thumbnailed from
1169
+ // it, never held in memory whole.
1170
+ const wavPath = nodePath.join(dir, "audio.wav");
1171
+ let durationMs;
1242
1172
  try {
1243
- const fileStat = await stat(filePath);
1244
- const data = await readFile(filePath);
1245
- return new Response(data, {
1246
- status: 200,
1247
- headers: {
1248
- "Content-Type": asset.content_type,
1249
- "Content-Length": String(fileStat.size),
1250
- "Cache-Control": "no-cache",
1251
- "Last-Modified": fileStat.mtime.toUTCString()
1252
- }
1253
- });
1173
+ if (!(await probeHasAudio(inputPath))) {
1174
+ return jsonResponse({ has_audio: false });
1175
+ }
1176
+ ({ durationMs } = await extractAudio(inputPath, wavPath));
1254
1177
  }
1255
- catch {
1256
- // File not found on disk
1178
+ catch (err) {
1179
+ if (err instanceof MediaToolingMissingError) {
1180
+ return errorResponse(503, "ffmpeg is required to extract audio from video. Install the ffmpeg runtime.");
1181
+ }
1182
+ throw err;
1257
1183
  }
1184
+ const { size } = await fsp.stat(wavPath);
1185
+ const audioAsset = (await Asset.create({
1186
+ user_id: userId,
1187
+ name: `${source.name} (audio)`,
1188
+ content_type: "audio/wav",
1189
+ parent_id: source.id,
1190
+ workflow_id: source.workflow_id ?? null,
1191
+ project_id: source.project_id,
1192
+ node_id: null,
1193
+ job_id: null,
1194
+ metadata: null,
1195
+ size,
1196
+ duration: durationMs != null ? durationMs / 1000 : null
1197
+ }));
1198
+ await storeAssetFileWithThumbnail(audioAsset.user_id, audioAsset.id, getAssetFileName(audioAsset.id, audioAsset.content_type), wavPath, audioAsset.content_type);
1199
+ return jsonResponse({
1200
+ has_audio: true,
1201
+ asset: await toAssetResponse(audioAsset)
1202
+ });
1203
+ }
1204
+ finally {
1205
+ await fsp.rm(dir, { recursive: true, force: true });
1258
1206
  }
1259
- return errorResponse(404, "No thumbnail available");
1260
1207
  }
1261
1208
  export async function handleApiRequest(request, options = {}) {
1262
1209
  const url = new URL(request.url);
@@ -1272,20 +1219,31 @@ export async function handleApiRequest(request, options = {}) {
1272
1219
  if (response)
1273
1220
  return response;
1274
1221
  }
1222
+ // Restored: the REST-to-tRPC migration on this branch dropped this route as a
1223
+ // side effect, with no tRPC replacement, and its suite (ws-phase2, T-WS-8)
1224
+ // still covers it. Whether to retire a public endpoint is a deliberate
1225
+ // decision, not a refactor's leftover. Note it never consulted storage —
1226
+ // `available` has always been unconditionally true.
1275
1227
  if (pathname === "/api/users/validate_username") {
1276
- if (request.method !== "GET")
1228
+ if (request.method !== "GET") {
1277
1229
  return errorResponse(405, "Method not allowed");
1230
+ }
1278
1231
  const url = new URL(request.url);
1279
1232
  const username = url.searchParams.get("username")?.trim() ?? null;
1280
- if (username === null)
1233
+ if (username === null) {
1281
1234
  return errorResponse(400, "username parameter is required");
1282
- if (!username)
1235
+ }
1236
+ if (!username) {
1283
1237
  return errorResponse(400, "username cannot be empty");
1238
+ }
1284
1239
  const valid = /^[a-zA-Z0-9_-]{3,32}$/.test(username);
1285
1240
  return jsonResponse({ valid, available: true });
1286
1241
  }
1287
- if (pathname === "/api/nodes/dummy") {
1288
- return handleNodesDummy(request);
1242
+ if (pathname === "/api/applications/examples") {
1243
+ if (request.method !== "GET") {
1244
+ return errorResponse(405, "Method not allowed");
1245
+ }
1246
+ return jsonResponse(listExampleApps(options));
1289
1247
  }
1290
1248
  if (pathname === "/api/nodes/metadata" || pathname === "/api/node/metadata") {
1291
1249
  return handleNodeMetadata(request, options);
@@ -1306,6 +1264,12 @@ export async function handleApiRequest(request, options = {}) {
1306
1264
  if (pathname === "/api/workflows/tools") {
1307
1265
  return handleWorkflowTools(request, options);
1308
1266
  }
1267
+ if (pathname === "/api/workflows/export-bundle") {
1268
+ return handleWorkflowsExportBundle(request, options);
1269
+ }
1270
+ if (pathname === "/api/workflows/import-bundle") {
1271
+ return handleWorkflowImportBundle(request, options);
1272
+ }
1309
1273
  if (pathname === "/api/workflows/examples") {
1310
1274
  return handleWorkflowExamples(request, options);
1311
1275
  }
@@ -1316,8 +1280,14 @@ export async function handleApiRequest(request, options = {}) {
1316
1280
  const filename = decodeURIComponent(pathname.slice("/api/workflows/examples/thumbnails/".length));
1317
1281
  return handleWorkflowExamplesThumbnail(request, filename, options);
1318
1282
  }
1283
+ // /api/workflows/examples/{package}/{name} — one example with its graph.
1319
1284
  if (pathname.startsWith("/api/workflows/examples/")) {
1320
- return errorResponse(404, "Examples not available in standalone mode");
1285
+ const rest = pathname.slice("/api/workflows/examples/".length);
1286
+ const slash = rest.indexOf("/");
1287
+ if (slash > 0) {
1288
+ return handleWorkflowExampleByName(request, decodeURIComponent(rest.slice(0, slash)), decodeURIComponent(rest.slice(slash + 1)), options);
1289
+ }
1290
+ return errorResponse(404, "Not found");
1321
1291
  }
1322
1292
  if (pathname === "/api/workflows/public") {
1323
1293
  return handlePublicWorkflows(request);
@@ -1328,6 +1298,14 @@ export async function handleApiRequest(request, options = {}) {
1328
1298
  return errorResponse(404, "Not found");
1329
1299
  return handlePublicWorkflowById(request, workflowId);
1330
1300
  }
1301
+ // ── Interactive debug sessions ─────────────────────────────────────
1302
+ // Pattern: /api/debug/sessions/{id}[/verdict|/cancel]
1303
+ {
1304
+ const debugSessionMatch = pathname.match(/^\/api\/debug\/sessions\/([^/]+)(?:\/([a-z]+))?$/);
1305
+ if (debugSessionMatch) {
1306
+ return handleDebugSessionRequest(request, decodeURIComponent(debugSessionMatch[1]), debugSessionMatch[2] ?? null, options);
1307
+ }
1308
+ }
1331
1309
  // ── Workflow sub-resource routes ───────────────────────────────────
1332
1310
  // Pattern: /api/workflows/{id}/...
1333
1311
  {
@@ -1338,41 +1316,14 @@ export async function handleApiRequest(request, options = {}) {
1338
1316
  if (subPath === "run") {
1339
1317
  return handleWorkflowRun(request, workflowId, options);
1340
1318
  }
1341
- if (subPath === "autosave") {
1342
- return handleWorkflowAutosave(request, workflowId, options);
1343
- }
1344
- if (subPath === "app") {
1345
- return handleWorkflowApp(request, workflowId, options);
1346
- }
1347
- if (subPath === "generate-name") {
1348
- return handleWorkflowGenerateName(request, workflowId, options);
1319
+ if (subPath === "debug") {
1320
+ return handleWorkflowRun(request, workflowId, options, true);
1349
1321
  }
1350
1322
  if (subPath === "dsl-export") {
1351
1323
  return handleWorkflowDslExport(request, workflowId, options);
1352
1324
  }
1353
- if (subPath === "gradio-export") {
1354
- return handleWorkflowGradioExport(request, workflowId, options);
1355
- }
1356
- if (subPath === "versions") {
1357
- return handleWorkflowVersions(request, workflowId, options);
1358
- }
1359
- // /api/workflows/{id}/versions/{version}/restore
1360
- const versionRestoreMatch = subPath.match(/^versions\/(\d+)\/restore$/);
1361
- if (versionRestoreMatch) {
1362
- const versionNum = Number.parseInt(versionRestoreMatch[1], 10);
1363
- return handleWorkflowVersionByNumber(request, workflowId, versionNum, options);
1364
- }
1365
- // /api/workflows/{id}/versions/{version} (GET by version number)
1366
- const versionNumMatch = subPath.match(/^versions\/(\d+)$/);
1367
- if (versionNumMatch) {
1368
- const versionNum = Number.parseInt(versionNumMatch[1], 10);
1369
- return handleWorkflowVersionByNumber(request, workflowId, versionNum, options);
1370
- }
1371
- // /api/workflows/{id}/versions/{version_id} (DELETE by id — version_id is not numeric)
1372
- const versionIdMatch = subPath.match(/^versions\/([^/]+)$/);
1373
- if (versionIdMatch) {
1374
- const versionId = decodeURIComponent(versionIdMatch[1]);
1375
- return handleWorkflowVersionDeleteById(request, workflowId, versionId, options);
1325
+ if (subPath === "export-bundle") {
1326
+ return handleWorkflowExportBundle(request, workflowId, options);
1376
1327
  }
1377
1328
  }
1378
1329
  }
@@ -1444,19 +1395,36 @@ export async function handleNodeHttpRequest(req, res, options = {}) {
1444
1395
  res.end();
1445
1396
  return;
1446
1397
  }
1447
- const bodyBuffer = Buffer.from(await response.arrayBuffer());
1448
- // Gzip-compress large JSON responses for performance (e.g. /api/nodes/metadata
1449
- // is ~5 MB uncompressed, ~550 KB compressed).
1450
- const GZIP_THRESHOLD = 256 * 1024;
1451
1398
  const acceptEncoding = req.headers["accept-encoding"] ?? "";
1452
- if (bodyBuffer.length > GZIP_THRESHOLD && acceptEncoding.includes("gzip")) {
1453
- const compressed = gzipSync(bodyBuffer);
1454
- res.setHeader("content-encoding", "gzip");
1455
- res.setHeader("content-length", compressed.length);
1456
- res.end(compressed);
1399
+ const contentType = response.headers.get("content-type") ?? "";
1400
+ const mayCompress = response.status !== 206 &&
1401
+ !response.headers.has("content-range") &&
1402
+ (contentType.includes("json") || contentType.startsWith("text/")) &&
1403
+ acceptEncoding.includes("gzip");
1404
+ if (mayCompress) {
1405
+ const bodyBuffer = Buffer.from(await response.arrayBuffer());
1406
+ if (bodyBuffer.length > GZIP_THRESHOLD) {
1407
+ const compressed = gzipSync(bodyBuffer);
1408
+ res.setHeader("content-encoding", "gzip");
1409
+ res.setHeader("content-length", compressed.length);
1410
+ res.end(compressed);
1411
+ }
1412
+ else {
1413
+ res.end(bodyBuffer);
1414
+ }
1457
1415
  return;
1458
1416
  }
1459
- res.end(bodyBuffer);
1417
+ const reader = response.body.getReader();
1418
+ while (true) {
1419
+ const { done, value } = await reader.read();
1420
+ if (done) {
1421
+ break;
1422
+ }
1423
+ if (!res.write(value)) {
1424
+ await once(res, "drain");
1425
+ }
1426
+ }
1427
+ res.end();
1460
1428
  }
1461
1429
  export function createHttpApiServer(options = {}) {
1462
1430
  return createServer((req, res) => {
@@ -1464,8 +1432,7 @@ export function createHttpApiServer(options = {}) {
1464
1432
  log.error("Request failed", error instanceof Error ? error : new Error(String(error)));
1465
1433
  res.statusCode = 500;
1466
1434
  res.setHeader("content-type", "application/json");
1467
- const detail = error instanceof Error ? error.message : String(error);
1468
- res.end(JSON.stringify({ detail }));
1435
+ res.end(JSON.stringify({ detail: "Internal server error" }));
1469
1436
  });
1470
1437
  });
1471
1438
  }