@nodetool-ai/websocket 0.7.0-rc.23 → 0.8.2

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 (1101) 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 +82 -14
  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/error-traces.d.ts +56 -0
  52. package/dist/error-traces.d.ts.map +1 -0
  53. package/dist/error-traces.js +179 -0
  54. package/dist/error-traces.js.map +1 -0
  55. package/dist/example-workflows.d.ts +20 -0
  56. package/dist/example-workflows.d.ts.map +1 -0
  57. package/dist/example-workflows.js +123 -0
  58. package/dist/example-workflows.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 +307 -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 +219 -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 +628 -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 +10 -3
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +10 -3
  94. package/dist/index.js.map +1 -1
  95. package/dist/interrupted-jobs.d.ts +14 -0
  96. package/dist/interrupted-jobs.d.ts.map +1 -0
  97. package/dist/interrupted-jobs.js +28 -0
  98. package/dist/interrupted-jobs.js.map +1 -0
  99. package/dist/job-control.d.ts +57 -0
  100. package/dist/job-control.d.ts.map +1 -0
  101. package/dist/job-control.js +134 -0
  102. package/dist/job-control.js.map +1 -0
  103. package/dist/job-queue.d.ts +47 -0
  104. package/dist/job-queue.d.ts.map +1 -0
  105. package/dist/job-queue.js +52 -0
  106. package/dist/job-queue.js.map +1 -0
  107. package/dist/job-run-registry.d.ts +176 -0
  108. package/dist/job-run-registry.d.ts.map +1 -0
  109. package/dist/job-run-registry.js +312 -0
  110. package/dist/job-run-registry.js.map +1 -0
  111. package/dist/lib/app-build-service.d.ts +16 -0
  112. package/dist/lib/app-build-service.d.ts.map +1 -0
  113. package/dist/lib/app-build-service.js +39 -0
  114. package/dist/lib/app-build-service.js.map +1 -0
  115. package/dist/lib/app-debug-service.d.ts +16 -0
  116. package/dist/lib/app-debug-service.d.ts.map +1 -0
  117. package/dist/lib/app-debug-service.js +48 -0
  118. package/dist/lib/app-debug-service.js.map +1 -0
  119. package/dist/lib/app-deployment-service.d.ts +82 -0
  120. package/dist/lib/app-deployment-service.d.ts.map +1 -0
  121. package/dist/lib/app-deployment-service.js +282 -0
  122. package/dist/lib/app-deployment-service.js.map +1 -0
  123. package/dist/lib/app-instances-service.d.ts +1284 -0
  124. package/dist/lib/app-instances-service.d.ts.map +1 -0
  125. package/dist/lib/app-instances-service.js +378 -0
  126. package/dist/lib/app-instances-service.js.map +1 -0
  127. package/dist/lib/app-run-cancellation.d.ts +4 -0
  128. package/dist/lib/app-run-cancellation.d.ts.map +1 -0
  129. package/dist/lib/app-run-cancellation.js +28 -0
  130. package/dist/lib/app-run-cancellation.js.map +1 -0
  131. package/dist/lib/app-run-server.d.ts +7 -0
  132. package/dist/lib/app-run-server.d.ts.map +1 -0
  133. package/dist/lib/app-run-server.js +6 -0
  134. package/dist/lib/app-run-server.js.map +1 -0
  135. package/dist/lib/app-session-scope.d.ts +91 -0
  136. package/dist/lib/app-session-scope.d.ts.map +1 -0
  137. package/dist/lib/app-session-scope.js +91 -0
  138. package/dist/lib/app-session-scope.js.map +1 -0
  139. package/dist/lib/applications-service.d.ts +354 -0
  140. package/dist/lib/applications-service.d.ts.map +1 -0
  141. package/dist/lib/applications-service.js +507 -0
  142. package/dist/lib/applications-service.js.map +1 -0
  143. package/dist/lib/asset-duration.d.ts +7 -0
  144. package/dist/lib/asset-duration.d.ts.map +1 -0
  145. package/dist/lib/asset-duration.js +24 -0
  146. package/dist/lib/asset-duration.js.map +1 -0
  147. package/dist/lib/asset-export.d.ts +34 -0
  148. package/dist/lib/asset-export.d.ts.map +1 -0
  149. package/dist/lib/asset-export.js +113 -0
  150. package/dist/lib/asset-export.js.map +1 -0
  151. package/dist/lib/asset-model-interface.d.ts +41 -0
  152. package/dist/lib/asset-model-interface.d.ts.map +1 -0
  153. package/dist/lib/asset-model-interface.js +74 -0
  154. package/dist/lib/asset-model-interface.js.map +1 -0
  155. package/dist/lib/asset-paths.d.ts +49 -1
  156. package/dist/lib/asset-paths.d.ts.map +1 -1
  157. package/dist/lib/asset-paths.js +127 -31
  158. package/dist/lib/asset-paths.js.map +1 -1
  159. package/dist/lib/asset-response.d.ts +12 -0
  160. package/dist/lib/asset-response.d.ts.map +1 -0
  161. package/dist/lib/asset-response.js +89 -0
  162. package/dist/lib/asset-response.js.map +1 -0
  163. package/dist/lib/audio-peaks.d.ts +23 -0
  164. package/dist/lib/audio-peaks.d.ts.map +1 -0
  165. package/dist/lib/audio-peaks.js +335 -0
  166. package/dist/lib/audio-peaks.js.map +1 -0
  167. package/dist/lib/bridge.d.ts +9 -1
  168. package/dist/lib/bridge.d.ts.map +1 -1
  169. package/dist/lib/bridge.js +27 -4
  170. package/dist/lib/bridge.js.map +1 -1
  171. package/dist/lib/compression.d.ts +3 -0
  172. package/dist/lib/compression.d.ts.map +1 -0
  173. package/dist/lib/compression.js +3 -0
  174. package/dist/lib/compression.js.map +1 -0
  175. package/dist/lib/document-model-interfaces.d.ts +30 -0
  176. package/dist/lib/document-model-interfaces.d.ts.map +1 -0
  177. package/dist/lib/document-model-interfaces.js +296 -0
  178. package/dist/lib/document-model-interfaces.js.map +1 -0
  179. package/dist/lib/env-config.d.ts +9 -0
  180. package/dist/lib/env-config.d.ts.map +1 -0
  181. package/dist/lib/env-config.js +18 -0
  182. package/dist/lib/env-config.js.map +1 -0
  183. package/dist/lib/example-apps.d.ts +52 -0
  184. package/dist/lib/example-apps.d.ts.map +1 -0
  185. package/dist/lib/example-apps.js +192 -0
  186. package/dist/lib/example-apps.js.map +1 -0
  187. package/dist/lib/example-games.d.ts +5 -0
  188. package/dist/lib/example-games.d.ts.map +1 -0
  189. package/dist/lib/example-games.js +13 -0
  190. package/dist/lib/example-games.js.map +1 -0
  191. package/dist/lib/example-recipes.d.ts +33 -0
  192. package/dist/lib/example-recipes.d.ts.map +1 -0
  193. package/dist/lib/example-recipes.js +213 -0
  194. package/dist/lib/example-recipes.js.map +1 -0
  195. package/dist/lib/example-storyboards.d.ts +40 -0
  196. package/dist/lib/example-storyboards.d.ts.map +1 -0
  197. package/dist/lib/example-storyboards.js +131 -0
  198. package/dist/lib/example-storyboards.js.map +1 -0
  199. package/dist/lib/example-thumbnail.d.ts +21 -0
  200. package/dist/lib/example-thumbnail.d.ts.map +1 -0
  201. package/dist/lib/example-thumbnail.js +50 -0
  202. package/dist/lib/example-thumbnail.js.map +1 -0
  203. package/dist/lib/example-timelines.d.ts +9 -0
  204. package/dist/lib/example-timelines.d.ts.map +1 -0
  205. package/dist/lib/example-timelines.js +25 -0
  206. package/dist/lib/example-timelines.js.map +1 -0
  207. package/dist/lib/extension-dist.d.ts +24 -0
  208. package/dist/lib/extension-dist.d.ts.map +1 -0
  209. package/dist/lib/extension-dist.js +67 -0
  210. package/dist/lib/extension-dist.js.map +1 -0
  211. package/dist/lib/external-asset-lookup.d.ts +30 -0
  212. package/dist/lib/external-asset-lookup.d.ts.map +1 -0
  213. package/dist/lib/external-asset-lookup.js +99 -0
  214. package/dist/lib/external-asset-lookup.js.map +1 -0
  215. package/dist/lib/external-assets.d.ts +13 -0
  216. package/dist/lib/external-assets.d.ts.map +1 -0
  217. package/dist/lib/external-assets.js +46 -0
  218. package/dist/lib/external-assets.js.map +1 -0
  219. package/dist/lib/fly-replay.d.ts +34 -0
  220. package/dist/lib/fly-replay.d.ts.map +1 -0
  221. package/dist/lib/fly-replay.js +111 -0
  222. package/dist/lib/fly-replay.js.map +1 -0
  223. package/dist/lib/http-rate-limit.d.ts +32 -0
  224. package/dist/lib/http-rate-limit.d.ts.map +1 -0
  225. package/dist/lib/http-rate-limit.js +47 -0
  226. package/dist/lib/http-rate-limit.js.map +1 -0
  227. package/dist/lib/http-tracing.d.ts +12 -0
  228. package/dist/lib/http-tracing.d.ts.map +1 -0
  229. package/dist/lib/http-tracing.js +81 -0
  230. package/dist/lib/http-tracing.js.map +1 -0
  231. package/dist/lib/instance-id.d.ts +25 -0
  232. package/dist/lib/instance-id.d.ts.map +1 -0
  233. package/dist/lib/instance-id.js +44 -0
  234. package/dist/lib/instance-id.js.map +1 -0
  235. package/dist/lib/link-codes.d.ts +82 -0
  236. package/dist/lib/link-codes.d.ts.map +1 -0
  237. package/dist/lib/link-codes.js +100 -0
  238. package/dist/lib/link-codes.js.map +1 -0
  239. package/dist/lib/local-asset-upload.d.ts +5 -0
  240. package/dist/lib/local-asset-upload.d.ts.map +1 -0
  241. package/dist/lib/local-asset-upload.js +69 -0
  242. package/dist/lib/local-asset-upload.js.map +1 -0
  243. package/dist/lib/local-file-access.d.ts +31 -0
  244. package/dist/lib/local-file-access.d.ts.map +1 -0
  245. package/dist/lib/local-file-access.js +169 -0
  246. package/dist/lib/local-file-access.js.map +1 -0
  247. package/dist/lib/localhost-trust.d.ts +59 -0
  248. package/dist/lib/localhost-trust.d.ts.map +1 -0
  249. package/dist/lib/localhost-trust.js +139 -0
  250. package/dist/lib/localhost-trust.js.map +1 -0
  251. package/dist/lib/mcp-mount.d.ts +25 -0
  252. package/dist/lib/mcp-mount.d.ts.map +1 -0
  253. package/dist/lib/mcp-mount.js +32 -0
  254. package/dist/lib/mcp-mount.js.map +1 -0
  255. package/dist/lib/media-size.d.ts +21 -0
  256. package/dist/lib/media-size.d.ts.map +1 -0
  257. package/dist/lib/media-size.js +48 -0
  258. package/dist/lib/media-size.js.map +1 -0
  259. package/dist/lib/media.d.ts +33 -0
  260. package/dist/lib/media.d.ts.map +1 -0
  261. package/dist/lib/media.js +110 -0
  262. package/dist/lib/media.js.map +1 -0
  263. package/dist/lib/package-asset-export.d.ts +83 -0
  264. package/dist/lib/package-asset-export.d.ts.map +1 -0
  265. package/dist/lib/package-asset-export.js +212 -0
  266. package/dist/lib/package-asset-export.js.map +1 -0
  267. package/dist/lib/package-assets.d.ts +41 -0
  268. package/dist/lib/package-assets.d.ts.map +1 -0
  269. package/dist/lib/package-assets.js +155 -0
  270. package/dist/lib/package-assets.js.map +1 -0
  271. package/dist/lib/project-delete.d.ts +11 -0
  272. package/dist/lib/project-delete.d.ts.map +1 -0
  273. package/dist/lib/project-delete.js +67 -0
  274. package/dist/lib/project-delete.js.map +1 -0
  275. package/dist/lib/project-document-copy.d.ts +31 -0
  276. package/dist/lib/project-document-copy.d.ts.map +1 -0
  277. package/dist/lib/project-document-copy.js +473 -0
  278. package/dist/lib/project-document-copy.js.map +1 -0
  279. package/dist/lib/public-app-assets.d.ts +8 -0
  280. package/dist/lib/public-app-assets.d.ts.map +1 -0
  281. package/dist/lib/public-app-assets.js +77 -0
  282. package/dist/lib/public-app-assets.js.map +1 -0
  283. package/dist/lib/public-routes.d.ts +102 -0
  284. package/dist/lib/public-routes.d.ts.map +1 -0
  285. package/dist/lib/public-routes.js +168 -0
  286. package/dist/lib/public-routes.js.map +1 -0
  287. package/dist/lib/public-workflow-share.d.ts +20 -0
  288. package/dist/lib/public-workflow-share.d.ts.map +1 -0
  289. package/dist/lib/public-workflow-share.js +45 -0
  290. package/dist/lib/public-workflow-share.js.map +1 -0
  291. package/dist/lib/run-trace-live-delivery.d.ts +25 -0
  292. package/dist/lib/run-trace-live-delivery.d.ts.map +1 -0
  293. package/dist/lib/run-trace-live-delivery.js +91 -0
  294. package/dist/lib/run-trace-live-delivery.js.map +1 -0
  295. package/dist/lib/runtime-detection.d.ts +17 -0
  296. package/dist/lib/runtime-detection.d.ts.map +1 -0
  297. package/dist/lib/runtime-detection.js +90 -0
  298. package/dist/lib/runtime-detection.js.map +1 -0
  299. package/dist/lib/storage-access.d.ts +21 -0
  300. package/dist/lib/storage-access.d.ts.map +1 -0
  301. package/dist/lib/storage-access.js +74 -0
  302. package/dist/lib/storage-access.js.map +1 -0
  303. package/dist/lib/storage.d.ts +9 -0
  304. package/dist/lib/storage.d.ts.map +1 -0
  305. package/dist/lib/storage.js +28 -0
  306. package/dist/lib/storage.js.map +1 -0
  307. package/dist/lib/storyboard-export.d.ts +64 -0
  308. package/dist/lib/storyboard-export.d.ts.map +1 -0
  309. package/dist/lib/storyboard-export.js +183 -0
  310. package/dist/lib/storyboard-export.js.map +1 -0
  311. package/dist/lib/style-presets.d.ts +34 -0
  312. package/dist/lib/style-presets.d.ts.map +1 -0
  313. package/dist/lib/style-presets.js +62 -0
  314. package/dist/lib/style-presets.js.map +1 -0
  315. package/dist/lib/temp-url-resolver.d.ts +35 -0
  316. package/dist/lib/temp-url-resolver.d.ts.map +1 -0
  317. package/dist/lib/temp-url-resolver.js +88 -0
  318. package/dist/lib/temp-url-resolver.js.map +1 -0
  319. package/dist/lib/test-ui-run-routes.d.ts +8 -0
  320. package/dist/lib/test-ui-run-routes.d.ts.map +1 -0
  321. package/dist/lib/test-ui-run-routes.js +42 -0
  322. package/dist/lib/test-ui-run-routes.js.map +1 -0
  323. package/dist/lib/thumbnail.d.ts +74 -0
  324. package/dist/lib/thumbnail.d.ts.map +1 -0
  325. package/dist/lib/thumbnail.js +305 -0
  326. package/dist/lib/thumbnail.js.map +1 -0
  327. package/dist/lib/timeline-bundle.d.ts +1821 -0
  328. package/dist/lib/timeline-bundle.d.ts.map +1 -0
  329. package/dist/lib/timeline-bundle.js +342 -0
  330. package/dist/lib/timeline-bundle.js.map +1 -0
  331. package/dist/lib/video-proxy.d.ts +114 -0
  332. package/dist/lib/video-proxy.d.ts.map +1 -0
  333. package/dist/lib/video-proxy.js +546 -0
  334. package/dist/lib/video-proxy.js.map +1 -0
  335. package/dist/lib/wire-values.d.ts +31 -0
  336. package/dist/lib/wire-values.d.ts.map +1 -0
  337. package/dist/lib/wire-values.js +49 -0
  338. package/dist/lib/wire-values.js.map +1 -0
  339. package/dist/lib/workflow-bundle.d.ts +102 -0
  340. package/dist/lib/workflow-bundle.d.ts.map +1 -0
  341. package/dist/lib/workflow-bundle.js +306 -0
  342. package/dist/lib/workflow-bundle.js.map +1 -0
  343. package/dist/lib/workflow-service.d.ts +82 -0
  344. package/dist/lib/workflow-service.d.ts.map +1 -0
  345. package/dist/lib/workflow-service.js +197 -0
  346. package/dist/lib/workflow-service.js.map +1 -0
  347. package/dist/lib/workflow-workspace.d.ts +20 -0
  348. package/dist/lib/workflow-workspace.d.ts.map +1 -0
  349. package/dist/lib/workflow-workspace.js +65 -0
  350. package/dist/lib/workflow-workspace.js.map +1 -0
  351. package/dist/lib/ws-connection-health.d.ts +10 -0
  352. package/dist/lib/ws-connection-health.d.ts.map +1 -0
  353. package/dist/lib/ws-connection-health.js +38 -0
  354. package/dist/lib/ws-connection-health.js.map +1 -0
  355. package/dist/lib/ws-rate-limit.d.ts +22 -0
  356. package/dist/lib/ws-rate-limit.d.ts.map +1 -0
  357. package/dist/lib/ws-rate-limit.js +45 -0
  358. package/dist/lib/ws-rate-limit.js.map +1 -0
  359. package/dist/lib/ws-upgrade.d.ts +40 -0
  360. package/dist/lib/ws-upgrade.d.ts.map +1 -0
  361. package/dist/lib/ws-upgrade.js +66 -0
  362. package/dist/lib/ws-upgrade.js.map +1 -0
  363. package/dist/lib/zip-stream.d.ts +46 -0
  364. package/dist/lib/zip-stream.d.ts.map +1 -0
  365. package/dist/lib/zip-stream.js +140 -0
  366. package/dist/lib/zip-stream.js.map +1 -0
  367. package/dist/mcp-agent-tools.d.ts +56 -0
  368. package/dist/mcp-agent-tools.d.ts.map +1 -0
  369. package/dist/mcp-agent-tools.js +1147 -0
  370. package/dist/mcp-agent-tools.js.map +1 -0
  371. package/dist/mcp-client-config.d.ts +64 -0
  372. package/dist/mcp-client-config.d.ts.map +1 -0
  373. package/dist/mcp-client-config.js +353 -0
  374. package/dist/mcp-client-config.js.map +1 -0
  375. package/dist/mcp-server.d.ts +56 -7
  376. package/dist/mcp-server.d.ts.map +1 -1
  377. package/dist/mcp-server.js +255 -762
  378. package/dist/mcp-server.js.map +1 -1
  379. package/dist/mcp-tool-deps.d.ts +20 -0
  380. package/dist/mcp-tool-deps.d.ts.map +1 -0
  381. package/dist/mcp-tool-deps.js +61 -0
  382. package/dist/mcp-tool-deps.js.map +1 -0
  383. package/dist/messagepack.d.ts +7 -0
  384. package/dist/messagepack.d.ts.map +1 -0
  385. package/dist/messagepack.js +19 -0
  386. package/dist/messagepack.js.map +1 -0
  387. package/dist/model-download-runtime.d.ts +9 -0
  388. package/dist/model-download-runtime.d.ts.map +1 -0
  389. package/dist/model-download-runtime.js +77 -0
  390. package/dist/model-download-runtime.js.map +1 -0
  391. package/dist/model-execution-availability.d.ts +14 -0
  392. package/dist/model-execution-availability.d.ts.map +1 -0
  393. package/dist/model-execution-availability.js +161 -0
  394. package/dist/model-execution-availability.js.map +1 -0
  395. package/dist/models-api.d.ts +61 -5
  396. package/dist/models-api.d.ts.map +1 -1
  397. package/dist/models-api.js +182 -689
  398. package/dist/models-api.js.map +1 -1
  399. package/dist/node-registry-setup.d.ts +84 -0
  400. package/dist/node-registry-setup.d.ts.map +1 -0
  401. package/dist/node-registry-setup.js +163 -0
  402. package/dist/node-registry-setup.js.map +1 -0
  403. package/dist/oauth/cimd.d.ts +28 -0
  404. package/dist/oauth/cimd.d.ts.map +1 -0
  405. package/dist/oauth/cimd.js +177 -0
  406. package/dist/oauth/cimd.js.map +1 -0
  407. package/dist/oauth/gate.d.ts +38 -0
  408. package/dist/oauth/gate.d.ts.map +1 -0
  409. package/dist/oauth/gate.js +77 -0
  410. package/dist/oauth/gate.js.map +1 -0
  411. package/dist/oauth/metadata.d.ts +35 -0
  412. package/dist/oauth/metadata.d.ts.map +1 -0
  413. package/dist/oauth/metadata.js +56 -0
  414. package/dist/oauth/metadata.js.map +1 -0
  415. package/dist/oauth/pending-store.d.ts +79 -0
  416. package/dist/oauth/pending-store.d.ts.map +1 -0
  417. package/dist/oauth/pending-store.js +133 -0
  418. package/dist/oauth/pending-store.js.map +1 -0
  419. package/dist/oauth/pkce.d.ts +16 -0
  420. package/dist/oauth/pkce.d.ts.map +1 -0
  421. package/dist/oauth/pkce.js +29 -0
  422. package/dist/oauth/pkce.js.map +1 -0
  423. package/dist/oauth/validate.d.ts +25 -0
  424. package/dist/oauth/validate.d.ts.map +1 -0
  425. package/dist/oauth/validate.js +62 -0
  426. package/dist/oauth/validate.js.map +1 -0
  427. package/dist/oauth/www-authenticate.d.ts +9 -0
  428. package/dist/oauth/www-authenticate.d.ts.map +1 -0
  429. package/dist/oauth/www-authenticate.js +33 -0
  430. package/dist/oauth/www-authenticate.js.map +1 -0
  431. package/dist/oauth-api.d.ts +19 -0
  432. package/dist/oauth-api.d.ts.map +1 -1
  433. package/dist/oauth-api.js +531 -12
  434. package/dist/oauth-api.js.map +1 -1
  435. package/dist/openai-api.d.ts.map +1 -1
  436. package/dist/openai-api.js +4 -9
  437. package/dist/openai-api.js.map +1 -1
  438. package/dist/pack-snapshot.d.ts +24 -0
  439. package/dist/pack-snapshot.d.ts.map +1 -0
  440. package/dist/pack-snapshot.js +35 -0
  441. package/dist/pack-snapshot.js.map +1 -0
  442. package/dist/plugins/websocket.d.ts +21 -4
  443. package/dist/plugins/websocket.d.ts.map +1 -1
  444. package/dist/plugins/websocket.js +106 -297
  445. package/dist/plugins/websocket.js.map +1 -1
  446. package/dist/python-bridge-gate.d.ts +28 -0
  447. package/dist/python-bridge-gate.d.ts.map +1 -0
  448. package/dist/python-bridge-gate.js +36 -0
  449. package/dist/python-bridge-gate.js.map +1 -0
  450. package/dist/resolve-media-urls.d.ts +17 -3
  451. package/dist/resolve-media-urls.d.ts.map +1 -1
  452. package/dist/resolve-media-urls.js +117 -31
  453. package/dist/resolve-media-urls.js.map +1 -1
  454. package/dist/resource-events.d.ts +28 -0
  455. package/dist/resource-events.d.ts.map +1 -0
  456. package/dist/resource-events.js +20 -0
  457. package/dist/resource-events.js.map +1 -0
  458. package/dist/routes/account.d.ts +100 -0
  459. package/dist/routes/account.d.ts.map +1 -0
  460. package/dist/routes/account.js +197 -0
  461. package/dist/routes/account.js.map +1 -0
  462. package/dist/routes/app-runs.d.ts +4 -0
  463. package/dist/routes/app-runs.d.ts.map +1 -0
  464. package/dist/routes/app-runs.js +173 -0
  465. package/dist/routes/app-runs.js.map +1 -0
  466. package/dist/routes/applications.d.ts +36 -0
  467. package/dist/routes/applications.d.ts.map +1 -0
  468. package/dist/routes/applications.js +183 -0
  469. package/dist/routes/applications.js.map +1 -0
  470. package/dist/routes/assets.d.ts +3 -3
  471. package/dist/routes/assets.d.ts.map +1 -1
  472. package/dist/routes/assets.js +78 -44
  473. package/dist/routes/assets.js.map +1 -1
  474. package/dist/routes/atlascloud-webhook.d.ts +93 -0
  475. package/dist/routes/atlascloud-webhook.d.ts.map +1 -0
  476. package/dist/routes/atlascloud-webhook.js +369 -0
  477. package/dist/routes/atlascloud-webhook.js.map +1 -0
  478. package/dist/routes/collections.d.ts.map +1 -1
  479. package/dist/routes/config.d.ts +26 -0
  480. package/dist/routes/config.d.ts.map +1 -0
  481. package/dist/routes/config.js +64 -0
  482. package/dist/routes/config.js.map +1 -0
  483. package/dist/routes/documents.d.ts +27 -0
  484. package/dist/routes/documents.d.ts.map +1 -0
  485. package/dist/routes/documents.js +155 -0
  486. package/dist/routes/documents.js.map +1 -0
  487. package/dist/routes/fal-credits.d.ts +4 -0
  488. package/dist/routes/fal-credits.d.ts.map +1 -0
  489. package/dist/routes/fal-credits.js +101 -0
  490. package/dist/routes/fal-credits.js.map +1 -0
  491. package/dist/routes/fal-pricing-estimate.d.ts +4 -0
  492. package/dist/routes/fal-pricing-estimate.d.ts.map +1 -0
  493. package/dist/routes/fal-pricing-estimate.js +114 -0
  494. package/dist/routes/fal-pricing-estimate.js.map +1 -0
  495. package/dist/routes/fal-pricing.d.ts +4 -0
  496. package/dist/routes/fal-pricing.d.ts.map +1 -0
  497. package/dist/routes/fal-pricing.js +93 -0
  498. package/dist/routes/fal-pricing.js.map +1 -0
  499. package/dist/routes/fal-webhook.d.ts +102 -0
  500. package/dist/routes/fal-webhook.d.ts.map +1 -0
  501. package/dist/routes/fal-webhook.js +379 -0
  502. package/dist/routes/fal-webhook.js.map +1 -0
  503. package/dist/routes/files.d.ts.map +1 -1
  504. package/dist/routes/files.js +4 -3
  505. package/dist/routes/files.js.map +1 -1
  506. package/dist/routes/health.d.ts +1 -0
  507. package/dist/routes/health.d.ts.map +1 -1
  508. package/dist/routes/health.js +41 -7
  509. package/dist/routes/health.js.map +1 -1
  510. package/dist/routes/integrations.d.ts +39 -0
  511. package/dist/routes/integrations.d.ts.map +1 -0
  512. package/dist/routes/integrations.js +211 -0
  513. package/dist/routes/integrations.js.map +1 -0
  514. package/dist/routes/js-scripts.d.ts +32 -0
  515. package/dist/routes/js-scripts.d.ts.map +1 -0
  516. package/dist/routes/js-scripts.js +498 -0
  517. package/dist/routes/js-scripts.js.map +1 -0
  518. package/dist/routes/kie-credits.d.ts +4 -0
  519. package/dist/routes/kie-credits.d.ts.map +1 -0
  520. package/dist/routes/kie-credits.js +93 -0
  521. package/dist/routes/kie-credits.js.map +1 -0
  522. package/dist/routes/kie-pricing.d.ts +4 -0
  523. package/dist/routes/kie-pricing.d.ts.map +1 -0
  524. package/dist/routes/kie-pricing.js +45 -0
  525. package/dist/routes/kie-pricing.js.map +1 -0
  526. package/dist/routes/kie-webhook.d.ts +4 -0
  527. package/dist/routes/kie-webhook.d.ts.map +1 -0
  528. package/dist/routes/kie-webhook.js +70 -0
  529. package/dist/routes/kie-webhook.js.map +1 -0
  530. package/dist/routes/nodes.d.ts.map +1 -1
  531. package/dist/routes/nodes.js +29 -51
  532. package/dist/routes/nodes.js.map +1 -1
  533. package/dist/routes/oauth-as.d.ts +30 -0
  534. package/dist/routes/oauth-as.d.ts.map +1 -0
  535. package/dist/routes/oauth-as.js +412 -0
  536. package/dist/routes/oauth-as.js.map +1 -0
  537. package/dist/routes/oauth.d.ts.map +1 -1
  538. package/dist/routes/openai.d.ts.map +1 -1
  539. package/dist/routes/public-apps.d.ts +27 -0
  540. package/dist/routes/public-apps.d.ts.map +1 -0
  541. package/dist/routes/public-apps.js +99 -0
  542. package/dist/routes/public-apps.js.map +1 -0
  543. package/dist/routes/public-shared-workflows.d.ts +16 -0
  544. package/dist/routes/public-shared-workflows.d.ts.map +1 -0
  545. package/dist/routes/public-shared-workflows.js +55 -0
  546. package/dist/routes/public-shared-workflows.js.map +1 -0
  547. package/dist/routes/run-spans.d.ts +4 -0
  548. package/dist/routes/run-spans.d.ts.map +1 -0
  549. package/dist/routes/run-spans.js +53 -0
  550. package/dist/routes/run-spans.js.map +1 -0
  551. package/dist/routes/sandbox-modules.d.ts +17 -0
  552. package/dist/routes/sandbox-modules.d.ts.map +1 -0
  553. package/dist/routes/sandbox-modules.js +85 -0
  554. package/dist/routes/sandbox-modules.js.map +1 -0
  555. package/dist/routes/sdk-v1.d.ts +24 -0
  556. package/dist/routes/sdk-v1.d.ts.map +1 -0
  557. package/dist/routes/sdk-v1.js +320 -0
  558. package/dist/routes/sdk-v1.js.map +1 -0
  559. package/dist/routes/storage.d.ts.map +1 -1
  560. package/dist/routes/storyboards.d.ts +16 -0
  561. package/dist/routes/storyboards.d.ts.map +1 -0
  562. package/dist/routes/storyboards.js +97 -0
  563. package/dist/routes/storyboards.js.map +1 -0
  564. package/dist/routes/timeline-animations.d.ts +23 -0
  565. package/dist/routes/timeline-animations.d.ts.map +1 -0
  566. package/dist/routes/timeline-animations.js +99 -0
  567. package/dist/routes/timeline-animations.js.map +1 -0
  568. package/dist/routes/timeline-audio-bake.d.ts +51 -0
  569. package/dist/routes/timeline-audio-bake.d.ts.map +1 -0
  570. package/dist/routes/timeline-audio-bake.js +110 -0
  571. package/dist/routes/timeline-audio-bake.js.map +1 -0
  572. package/dist/routes/timeline-fonts.d.ts +51 -0
  573. package/dist/routes/timeline-fonts.d.ts.map +1 -0
  574. package/dist/routes/timeline-fonts.js +195 -0
  575. package/dist/routes/timeline-fonts.js.map +1 -0
  576. package/dist/routes/timeline-isolate-subject.d.ts +55 -0
  577. package/dist/routes/timeline-isolate-subject.d.ts.map +1 -0
  578. package/dist/routes/timeline-isolate-subject.js +128 -0
  579. package/dist/routes/timeline-isolate-subject.js.map +1 -0
  580. package/dist/routes/timeline-track-object.d.ts +39 -0
  581. package/dist/routes/timeline-track-object.d.ts.map +1 -0
  582. package/dist/routes/timeline-track-object.js +97 -0
  583. package/dist/routes/timeline-track-object.js.map +1 -0
  584. package/dist/routes/timelines.d.ts +18 -0
  585. package/dist/routes/timelines.d.ts.map +1 -0
  586. package/dist/routes/timelines.js +191 -0
  587. package/dist/routes/timelines.js.map +1 -0
  588. package/dist/routes/workflows.d.ts +6 -9
  589. package/dist/routes/workflows.d.ts.map +1 -1
  590. package/dist/routes/workflows.js +100 -11
  591. package/dist/routes/workflows.js.map +1 -1
  592. package/dist/routes/workspace.d.ts.map +1 -1
  593. package/dist/run-supervisor.d.ts +30 -0
  594. package/dist/run-supervisor.d.ts.map +1 -0
  595. package/dist/run-supervisor.js +86 -0
  596. package/dist/run-supervisor.js.map +1 -0
  597. package/dist/sandbox-catalog.d.ts +28 -0
  598. package/dist/sandbox-catalog.d.ts.map +1 -0
  599. package/dist/sandbox-catalog.js +74 -0
  600. package/dist/sandbox-catalog.js.map +1 -0
  601. package/dist/screenshot-projects.d.ts +27 -0
  602. package/dist/screenshot-projects.d.ts.map +1 -0
  603. package/dist/screenshot-projects.js +880 -0
  604. package/dist/screenshot-projects.js.map +1 -0
  605. package/dist/screenshot-server.js +950 -47
  606. package/dist/screenshot-server.js.map +1 -1
  607. package/dist/sdk/sdk-cached-model-inventory.d.ts +21 -0
  608. package/dist/sdk/sdk-cached-model-inventory.d.ts.map +1 -0
  609. package/dist/sdk/sdk-cached-model-inventory.js +51 -0
  610. package/dist/sdk/sdk-cached-model-inventory.js.map +1 -0
  611. package/dist/sdk/sdk-capabilities-service.d.ts +32 -0
  612. package/dist/sdk/sdk-capabilities-service.d.ts.map +1 -0
  613. package/dist/sdk/sdk-capabilities-service.js +47 -0
  614. package/dist/sdk/sdk-capabilities-service.js.map +1 -0
  615. package/dist/sdk/sdk-execution-readiness-probe.d.ts +19 -0
  616. package/dist/sdk/sdk-execution-readiness-probe.d.ts.map +1 -0
  617. package/dist/sdk/sdk-execution-readiness-probe.js +90 -0
  618. package/dist/sdk/sdk-execution-readiness-probe.js.map +1 -0
  619. package/dist/sdk/sdk-execution-target-readiness.d.ts +15 -0
  620. package/dist/sdk/sdk-execution-target-readiness.d.ts.map +1 -0
  621. package/dist/sdk/sdk-execution-target-readiness.js +50 -0
  622. package/dist/sdk/sdk-execution-target-readiness.js.map +1 -0
  623. package/dist/sdk/sdk-feature-flags.d.ts +18 -0
  624. package/dist/sdk/sdk-feature-flags.d.ts.map +1 -0
  625. package/dist/sdk/sdk-feature-flags.js +30 -0
  626. package/dist/sdk/sdk-feature-flags.js.map +1 -0
  627. package/dist/sdk/sdk-huggingface-download-state.d.ts +15 -0
  628. package/dist/sdk/sdk-huggingface-download-state.d.ts.map +1 -0
  629. package/dist/sdk/sdk-huggingface-download-state.js +25 -0
  630. package/dist/sdk/sdk-huggingface-download-state.js.map +1 -0
  631. package/dist/sdk/sdk-live-runner-registry.d.ts +21 -0
  632. package/dist/sdk/sdk-live-runner-registry.d.ts.map +1 -0
  633. package/dist/sdk/sdk-live-runner-registry.js +26 -0
  634. package/dist/sdk/sdk-live-runner-registry.js.map +1 -0
  635. package/dist/sdk/sdk-model-catalog-service.d.ts +20 -0
  636. package/dist/sdk/sdk-model-catalog-service.d.ts.map +1 -0
  637. package/dist/sdk/sdk-model-catalog-service.js +219 -0
  638. package/dist/sdk/sdk-model-catalog-service.js.map +1 -0
  639. package/dist/sdk/sdk-model-download-service.d.ts +39 -0
  640. package/dist/sdk/sdk-model-download-service.d.ts.map +1 -0
  641. package/dist/sdk/sdk-model-download-service.js +277 -0
  642. package/dist/sdk/sdk-model-download-service.js.map +1 -0
  643. package/dist/sdk/sdk-model-requirement-probe.d.ts +30 -0
  644. package/dist/sdk/sdk-model-requirement-probe.d.ts.map +1 -0
  645. package/dist/sdk/sdk-model-requirement-probe.js +120 -0
  646. package/dist/sdk/sdk-model-requirement-probe.js.map +1 -0
  647. package/dist/sdk/sdk-node-package-requirement-probe.d.ts +20 -0
  648. package/dist/sdk/sdk-node-package-requirement-probe.d.ts.map +1 -0
  649. package/dist/sdk/sdk-node-package-requirement-probe.js +44 -0
  650. package/dist/sdk/sdk-node-package-requirement-probe.js.map +1 -0
  651. package/dist/sdk/sdk-node-type-inventory-service.d.ts +11 -0
  652. package/dist/sdk/sdk-node-type-inventory-service.d.ts.map +1 -0
  653. package/dist/sdk/sdk-node-type-inventory-service.js +36 -0
  654. package/dist/sdk/sdk-node-type-inventory-service.js.map +1 -0
  655. package/dist/sdk/sdk-preflight-orchestrator.d.ts +54 -0
  656. package/dist/sdk/sdk-preflight-orchestrator.d.ts.map +1 -0
  657. package/dist/sdk/sdk-preflight-orchestrator.js +63 -0
  658. package/dist/sdk/sdk-preflight-orchestrator.js.map +1 -0
  659. package/dist/sdk/sdk-preflight-requirement-resolver.d.ts +35 -0
  660. package/dist/sdk/sdk-preflight-requirement-resolver.d.ts.map +1 -0
  661. package/dist/sdk/sdk-preflight-requirement-resolver.js +67 -0
  662. package/dist/sdk/sdk-preflight-requirement-resolver.js.map +1 -0
  663. package/dist/sdk/sdk-preflight-service.d.ts +61 -0
  664. package/dist/sdk/sdk-preflight-service.d.ts.map +1 -0
  665. package/dist/sdk/sdk-preflight-service.js +104 -0
  666. package/dist/sdk/sdk-preflight-service.js.map +1 -0
  667. package/dist/sdk/sdk-preflight-workflow-source.d.ts +18 -0
  668. package/dist/sdk/sdk-preflight-workflow-source.d.ts.map +1 -0
  669. package/dist/sdk/sdk-preflight-workflow-source.js +42 -0
  670. package/dist/sdk/sdk-preflight-workflow-source.js.map +1 -0
  671. package/dist/sdk/sdk-route-policy.d.ts +3 -0
  672. package/dist/sdk/sdk-route-policy.d.ts.map +1 -0
  673. package/dist/sdk/sdk-route-policy.js +18 -0
  674. package/dist/sdk/sdk-route-policy.js.map +1 -0
  675. package/dist/sdk/sdk-runtime-capabilities-service.d.ts +60 -0
  676. package/dist/sdk/sdk-runtime-capabilities-service.d.ts.map +1 -0
  677. package/dist/sdk/sdk-runtime-capabilities-service.js +30 -0
  678. package/dist/sdk/sdk-runtime-capabilities-service.js.map +1 -0
  679. package/dist/sdk/sdk-runtime-requirement-probe.d.ts +13 -0
  680. package/dist/sdk/sdk-runtime-requirement-probe.d.ts.map +1 -0
  681. package/dist/sdk/sdk-runtime-requirement-probe.js +55 -0
  682. package/dist/sdk/sdk-runtime-requirement-probe.js.map +1 -0
  683. package/dist/sdk/sdk-static-preflight-service.d.ts +66 -0
  684. package/dist/sdk/sdk-static-preflight-service.d.ts.map +1 -0
  685. package/dist/sdk/sdk-static-preflight-service.js +492 -0
  686. package/dist/sdk/sdk-static-preflight-service.js.map +1 -0
  687. package/dist/sdk/sdk-temporary-asset-service.d.ts +18 -0
  688. package/dist/sdk/sdk-temporary-asset-service.d.ts.map +1 -0
  689. package/dist/sdk/sdk-temporary-asset-service.js +33 -0
  690. package/dist/sdk/sdk-temporary-asset-service.js.map +1 -0
  691. package/dist/sdk/sdk-temporary-asset-upload-http-handler.d.ts +9 -0
  692. package/dist/sdk/sdk-temporary-asset-upload-http-handler.d.ts.map +1 -0
  693. package/dist/sdk/sdk-temporary-asset-upload-http-handler.js +65 -0
  694. package/dist/sdk/sdk-temporary-asset-upload-http-handler.js.map +1 -0
  695. package/dist/sdk/sdk-v1-handler-map.d.ts +44 -0
  696. package/dist/sdk/sdk-v1-handler-map.d.ts.map +1 -0
  697. package/dist/sdk/sdk-v1-handler-map.js +37 -0
  698. package/dist/sdk/sdk-v1-handler-map.js.map +1 -0
  699. package/dist/sdk/sdk-v1-service-error.d.ts +21 -0
  700. package/dist/sdk/sdk-v1-service-error.d.ts.map +1 -0
  701. package/dist/sdk/sdk-v1-service-error.js +62 -0
  702. package/dist/sdk/sdk-v1-service-error.js.map +1 -0
  703. package/dist/sdk/sdk-v1-service.d.ts +74 -0
  704. package/dist/sdk/sdk-v1-service.d.ts.map +1 -0
  705. package/dist/sdk/sdk-v1-service.js +219 -0
  706. package/dist/sdk/sdk-v1-service.js.map +1 -0
  707. package/dist/sdk/sdk-worker-readiness-adapter.d.ts +22 -0
  708. package/dist/sdk/sdk-worker-readiness-adapter.d.ts.map +1 -0
  709. package/dist/sdk/sdk-worker-readiness-adapter.js +29 -0
  710. package/dist/sdk/sdk-worker-readiness-adapter.js.map +1 -0
  711. package/dist/server.js +1091 -206
  712. package/dist/server.js.map +1 -1
  713. package/dist/session/app-run-fold.d.ts +7 -0
  714. package/dist/session/app-run-fold.d.ts.map +1 -0
  715. package/dist/session/app-run-fold.js +45 -0
  716. package/dist/session/app-run-fold.js.map +1 -0
  717. package/dist/session/asset-autosave.d.ts +80 -0
  718. package/dist/session/asset-autosave.d.ts.map +1 -0
  719. package/dist/session/asset-autosave.js +456 -0
  720. package/dist/session/asset-autosave.js.map +1 -0
  721. package/dist/session/chat-compaction.d.ts +107 -0
  722. package/dist/session/chat-compaction.d.ts.map +1 -0
  723. package/dist/session/chat-compaction.js +191 -0
  724. package/dist/session/chat-compaction.js.map +1 -0
  725. package/dist/session/chat-history.d.ts +75 -0
  726. package/dist/session/chat-history.d.ts.map +1 -0
  727. package/dist/session/chat-history.js +194 -0
  728. package/dist/session/chat-history.js.map +1 -0
  729. package/dist/session/chat-prompt.d.ts +45 -0
  730. package/dist/session/chat-prompt.d.ts.map +1 -0
  731. package/dist/session/chat-prompt.js +502 -0
  732. package/dist/session/chat-prompt.js.map +1 -0
  733. package/dist/session/chat-turn.d.ts +337 -0
  734. package/dist/session/chat-turn.d.ts.map +1 -0
  735. package/dist/session/chat-turn.js +3612 -0
  736. package/dist/session/chat-turn.js.map +1 -0
  737. package/dist/session/client-session.d.ts +59 -0
  738. package/dist/session/client-session.d.ts.map +1 -0
  739. package/dist/session/client-session.js +2 -0
  740. package/dist/session/client-session.js.map +1 -0
  741. package/dist/session/commands.d.ts +133 -0
  742. package/dist/session/commands.d.ts.map +1 -0
  743. package/dist/session/commands.js +784 -0
  744. package/dist/session/commands.js.map +1 -0
  745. package/dist/session/inference.d.ts +218 -0
  746. package/dist/session/inference.d.ts.map +1 -0
  747. package/dist/session/inference.js +1143 -0
  748. package/dist/session/inference.js.map +1 -0
  749. package/dist/session/job-execution.d.ts +484 -0
  750. package/dist/session/job-execution.d.ts.map +1 -0
  751. package/dist/session/job-execution.js +2379 -0
  752. package/dist/session/job-execution.js.map +1 -0
  753. package/dist/session/media-generation.d.ts +94 -0
  754. package/dist/session/media-generation.d.ts.map +1 -0
  755. package/dist/session/media-generation.js +234 -0
  756. package/dist/session/media-generation.js.map +1 -0
  757. package/dist/session/model-interfaces.d.ts +28 -0
  758. package/dist/session/model-interfaces.d.ts.map +1 -0
  759. package/dist/session/model-interfaces.js +133 -0
  760. package/dist/session/model-interfaces.js.map +1 -0
  761. package/dist/session/sanitize.d.ts +7 -0
  762. package/dist/session/sanitize.d.ts.map +1 -0
  763. package/dist/session/sanitize.js +64 -0
  764. package/dist/session/sanitize.js.map +1 -0
  765. package/dist/settings-registry.d.ts +6 -15
  766. package/dist/settings-registry.d.ts.map +1 -1
  767. package/dist/settings-registry.js +6 -103
  768. package/dist/settings-registry.js.map +1 -1
  769. package/dist/storage-api.d.ts +20 -0
  770. package/dist/storage-api.d.ts.map +1 -1
  771. package/dist/storage-api.js +260 -77
  772. package/dist/storage-api.js.map +1 -1
  773. package/dist/storage-retention.d.ts +14 -0
  774. package/dist/storage-retention.d.ts.map +1 -0
  775. package/dist/storage-retention.js +95 -0
  776. package/dist/storage-retention.js.map +1 -0
  777. package/dist/system-stats.d.ts +52 -0
  778. package/dist/system-stats.d.ts.map +1 -0
  779. package/dist/system-stats.js +134 -0
  780. package/dist/system-stats.js.map +1 -0
  781. package/dist/test-ui-server.d.ts +46 -0
  782. package/dist/test-ui-server.d.ts.map +1 -1
  783. package/dist/test-ui-server.js +207 -42
  784. package/dist/test-ui-server.js.map +1 -1
  785. package/dist/timeline-generation-recovery.d.ts +4 -0
  786. package/dist/timeline-generation-recovery.d.ts.map +1 -0
  787. package/dist/timeline-generation-recovery.js +72 -0
  788. package/dist/timeline-generation-recovery.js.map +1 -0
  789. package/dist/triggers/boot.d.ts +73 -0
  790. package/dist/triggers/boot.d.ts.map +1 -0
  791. package/dist/triggers/boot.js +149 -0
  792. package/dist/triggers/boot.js.map +1 -0
  793. package/dist/triggers/dispatcher.d.ts +234 -0
  794. package/dist/triggers/dispatcher.d.ts.map +1 -0
  795. package/dist/triggers/dispatcher.js +525 -0
  796. package/dist/triggers/dispatcher.js.map +1 -0
  797. package/dist/triggers/file-watch.d.ts +90 -0
  798. package/dist/triggers/file-watch.d.ts.map +1 -0
  799. package/dist/triggers/file-watch.js +405 -0
  800. package/dist/triggers/file-watch.js.map +1 -0
  801. package/dist/triggers/registration-sync.d.ts +34 -0
  802. package/dist/triggers/registration-sync.d.ts.map +1 -0
  803. package/dist/triggers/registration-sync.js +162 -0
  804. package/dist/triggers/registration-sync.js.map +1 -0
  805. package/dist/triggers/schedule-timing.d.ts +52 -0
  806. package/dist/triggers/schedule-timing.d.ts.map +1 -0
  807. package/dist/triggers/schedule-timing.js +90 -0
  808. package/dist/triggers/schedule-timing.js.map +1 -0
  809. package/dist/triggers/scheduler.d.ts +55 -0
  810. package/dist/triggers/scheduler.d.ts.map +1 -0
  811. package/dist/triggers/scheduler.js +122 -0
  812. package/dist/triggers/scheduler.js.map +1 -0
  813. package/dist/triggers/settle.d.ts +66 -0
  814. package/dist/triggers/settle.d.ts.map +1 -0
  815. package/dist/triggers/settle.js +99 -0
  816. package/dist/triggers/settle.js.map +1 -0
  817. package/dist/triggers/stores.d.ts +38 -0
  818. package/dist/triggers/stores.d.ts.map +1 -0
  819. package/dist/triggers/stores.js +168 -0
  820. package/dist/triggers/stores.js.map +1 -0
  821. package/dist/triggers/webhook-route.d.ts +49 -0
  822. package/dist/triggers/webhook-route.d.ts.map +1 -0
  823. package/dist/triggers/webhook-route.js +148 -0
  824. package/dist/triggers/webhook-route.js.map +1 -0
  825. package/dist/trpc/context.d.ts +52 -5
  826. package/dist/trpc/context.d.ts.map +1 -1
  827. package/dist/trpc/context.js +4 -1
  828. package/dist/trpc/context.js.map +1 -1
  829. package/dist/trpc/error-formatter.d.ts.map +1 -1
  830. package/dist/trpc/error-formatter.js +23 -2
  831. package/dist/trpc/error-formatter.js.map +1 -1
  832. package/dist/trpc/error-logging.d.ts +24 -0
  833. package/dist/trpc/error-logging.d.ts.map +1 -0
  834. package/dist/trpc/error-logging.js +48 -0
  835. package/dist/trpc/error-logging.js.map +1 -0
  836. package/dist/trpc/index.d.ts +2 -2
  837. package/dist/trpc/index.d.ts.map +1 -1
  838. package/dist/trpc/index.js +0 -2
  839. package/dist/trpc/index.js.map +1 -1
  840. package/dist/trpc/middleware.d.ts +6 -3
  841. package/dist/trpc/middleware.d.ts.map +1 -1
  842. package/dist/trpc/router.d.ts +100 -2540
  843. package/dist/trpc/router.d.ts.map +1 -1
  844. package/dist/trpc/router.js +58 -6
  845. package/dist/trpc/router.js.map +1 -1
  846. package/dist/trpc/routers/agent-access.d.ts +149 -0
  847. package/dist/trpc/routers/agent-access.d.ts.map +1 -0
  848. package/dist/trpc/routers/agent-access.js +224 -0
  849. package/dist/trpc/routers/agent-access.js.map +1 -0
  850. package/dist/trpc/routers/app-instances.d.ts +2269 -0
  851. package/dist/trpc/routers/app-instances.d.ts.map +1 -0
  852. package/dist/trpc/routers/app-instances.js +52 -0
  853. package/dist/trpc/routers/app-instances.js.map +1 -0
  854. package/dist/trpc/routers/app-runs.d.ts +1092 -0
  855. package/dist/trpc/routers/app-runs.d.ts.map +1 -0
  856. package/dist/trpc/routers/app-runs.js +28 -0
  857. package/dist/trpc/routers/app-runs.js.map +1 -0
  858. package/dist/trpc/routers/applications.d.ts +2212 -0
  859. package/dist/trpc/routers/applications.d.ts.map +1 -0
  860. package/dist/trpc/routers/applications.js +221 -0
  861. package/dist/trpc/routers/applications.js.map +1 -0
  862. package/dist/trpc/routers/assets.d.ts +194 -42
  863. package/dist/trpc/routers/assets.d.ts.map +1 -1
  864. package/dist/trpc/routers/assets.js +503 -105
  865. package/dist/trpc/routers/assets.js.map +1 -1
  866. package/dist/trpc/routers/bug-reports.d.ts +33 -0
  867. package/dist/trpc/routers/bug-reports.d.ts.map +1 -0
  868. package/dist/trpc/routers/bug-reports.js +69 -0
  869. package/dist/trpc/routers/bug-reports.js.map +1 -0
  870. package/dist/trpc/routers/code-gen.d.ts +99 -0
  871. package/dist/trpc/routers/code-gen.d.ts.map +1 -0
  872. package/dist/trpc/routers/code-gen.js +164 -0
  873. package/dist/trpc/routers/code-gen.js.map +1 -0
  874. package/dist/trpc/routers/collections.d.ts +1 -26
  875. package/dist/trpc/routers/collections.d.ts.map +1 -1
  876. package/dist/trpc/routers/collections.js +145 -78
  877. package/dist/trpc/routers/collections.js.map +1 -1
  878. package/dist/trpc/routers/costs.d.ts +40 -96
  879. package/dist/trpc/routers/costs.d.ts.map +1 -1
  880. package/dist/trpc/routers/costs.js +14 -89
  881. package/dist/trpc/routers/costs.js.map +1 -1
  882. package/dist/trpc/routers/credits.d.ts +110 -0
  883. package/dist/trpc/routers/credits.d.ts.map +1 -0
  884. package/dist/trpc/routers/credits.js +69 -0
  885. package/dist/trpc/routers/credits.js.map +1 -0
  886. package/dist/trpc/routers/custom-providers.d.ts +88 -0
  887. package/dist/trpc/routers/custom-providers.d.ts.map +1 -0
  888. package/dist/trpc/routers/custom-providers.js +140 -0
  889. package/dist/trpc/routers/custom-providers.js.map +1 -0
  890. package/dist/trpc/routers/documents.d.ts +61 -0
  891. package/dist/trpc/routers/documents.d.ts.map +1 -0
  892. package/dist/trpc/routers/documents.js +29 -0
  893. package/dist/trpc/routers/documents.js.map +1 -0
  894. package/dist/trpc/routers/error-traces.d.ts +132 -0
  895. package/dist/trpc/routers/error-traces.d.ts.map +1 -0
  896. package/dist/trpc/routers/error-traces.js +118 -0
  897. package/dist/trpc/routers/error-traces.js.map +1 -0
  898. package/dist/trpc/routers/extension.d.ts +24 -0
  899. package/dist/trpc/routers/extension.d.ts.map +1 -0
  900. package/dist/trpc/routers/extension.js +28 -0
  901. package/dist/trpc/routers/extension.js.map +1 -0
  902. package/dist/trpc/routers/external-mcp.d.ts +108 -0
  903. package/dist/trpc/routers/external-mcp.d.ts.map +1 -0
  904. package/dist/trpc/routers/external-mcp.js +70 -0
  905. package/dist/trpc/routers/external-mcp.js.map +1 -0
  906. package/dist/trpc/routers/files.d.ts +9 -2
  907. package/dist/trpc/routers/files.d.ts.map +1 -1
  908. package/dist/trpc/routers/files.js +67 -37
  909. package/dist/trpc/routers/files.js.map +1 -1
  910. package/dist/trpc/routers/fonts.d.ts +30 -0
  911. package/dist/trpc/routers/fonts.d.ts.map +1 -0
  912. package/dist/trpc/routers/fonts.js +112 -0
  913. package/dist/trpc/routers/fonts.js.map +1 -0
  914. package/dist/trpc/routers/gameDraftHistory.d.ts +8 -0
  915. package/dist/trpc/routers/gameDraftHistory.d.ts.map +1 -0
  916. package/dist/trpc/routers/gameDraftHistory.js +40 -0
  917. package/dist/trpc/routers/gameDraftHistory.js.map +1 -0
  918. package/dist/trpc/routers/games.d.ts +26502 -0
  919. package/dist/trpc/routers/games.d.ts.map +1 -0
  920. package/dist/trpc/routers/games.js +674 -0
  921. package/dist/trpc/routers/games.js.map +1 -0
  922. package/dist/trpc/routers/integrations.d.ts +86 -0
  923. package/dist/trpc/routers/integrations.d.ts.map +1 -0
  924. package/dist/trpc/routers/integrations.js +129 -0
  925. package/dist/trpc/routers/integrations.js.map +1 -0
  926. package/dist/trpc/routers/jobs.d.ts +117 -21
  927. package/dist/trpc/routers/jobs.d.ts.map +1 -1
  928. package/dist/trpc/routers/jobs.js +191 -30
  929. package/dist/trpc/routers/jobs.js.map +1 -1
  930. package/dist/trpc/routers/js-scripts.d.ts +410 -0
  931. package/dist/trpc/routers/js-scripts.d.ts.map +1 -0
  932. package/dist/trpc/routers/js-scripts.js +303 -0
  933. package/dist/trpc/routers/js-scripts.js.map +1 -0
  934. package/dist/trpc/routers/mcp-config.d.ts +4 -2
  935. package/dist/trpc/routers/mcp-config.d.ts.map +1 -1
  936. package/dist/trpc/routers/mcp-config.js +46 -206
  937. package/dist/trpc/routers/mcp-config.js.map +1 -1
  938. package/dist/trpc/routers/memories.d.ts +79 -0
  939. package/dist/trpc/routers/memories.d.ts.map +1 -0
  940. package/dist/trpc/routers/memories.js +66 -0
  941. package/dist/trpc/routers/memories.js.map +1 -0
  942. package/dist/trpc/routers/messages.d.ts +4 -77
  943. package/dist/trpc/routers/messages.d.ts.map +1 -1
  944. package/dist/trpc/routers/messages.js +9 -58
  945. package/dist/trpc/routers/messages.js.map +1 -1
  946. package/dist/trpc/routers/models.d.ts +1544 -249
  947. package/dist/trpc/routers/models.d.ts.map +1 -1
  948. package/dist/trpc/routers/models.js +624 -318
  949. package/dist/trpc/routers/models.js.map +1 -1
  950. package/dist/trpc/routers/nodes.d.ts +32 -16
  951. package/dist/trpc/routers/nodes.d.ts.map +1 -1
  952. package/dist/trpc/routers/nodes.js +70 -31
  953. package/dist/trpc/routers/nodes.js.map +1 -1
  954. package/dist/trpc/routers/packs.d.ts +184 -0
  955. package/dist/trpc/routers/packs.d.ts.map +1 -0
  956. package/dist/trpc/routers/packs.js +259 -0
  957. package/dist/trpc/routers/packs.js.map +1 -0
  958. package/dist/trpc/routers/projects.d.ts +422 -0
  959. package/dist/trpc/routers/projects.d.ts.map +1 -0
  960. package/dist/trpc/routers/projects.js +373 -0
  961. package/dist/trpc/routers/projects.js.map +1 -0
  962. package/dist/trpc/routers/resources.d.ts +120 -0
  963. package/dist/trpc/routers/resources.d.ts.map +1 -0
  964. package/dist/trpc/routers/resources.js +212 -0
  965. package/dist/trpc/routers/resources.js.map +1 -0
  966. package/dist/trpc/routers/runs.d.ts +472 -0
  967. package/dist/trpc/routers/runs.d.ts.map +1 -0
  968. package/dist/trpc/routers/runs.js +40 -0
  969. package/dist/trpc/routers/runs.js.map +1 -0
  970. package/dist/trpc/routers/scripts.d.ts +1116 -0
  971. package/dist/trpc/routers/scripts.d.ts.map +1 -0
  972. package/dist/trpc/routers/scripts.js +131 -0
  973. package/dist/trpc/routers/scripts.js.map +1 -0
  974. package/dist/trpc/routers/segmentation.d.ts +62 -0
  975. package/dist/trpc/routers/segmentation.d.ts.map +1 -0
  976. package/dist/trpc/routers/segmentation.js +146 -0
  977. package/dist/trpc/routers/segmentation.js.map +1 -0
  978. package/dist/trpc/routers/settings.d.ts +180 -10
  979. package/dist/trpc/routers/settings.d.ts.map +1 -1
  980. package/dist/trpc/routers/settings.js +125 -33
  981. package/dist/trpc/routers/settings.js.map +1 -1
  982. package/dist/trpc/routers/sketch.d.ts +868 -0
  983. package/dist/trpc/routers/sketch.d.ts.map +1 -0
  984. package/dist/trpc/routers/sketch.js +617 -0
  985. package/dist/trpc/routers/sketch.js.map +1 -0
  986. package/dist/trpc/routers/skills.d.ts +76 -22
  987. package/dist/trpc/routers/skills.d.ts.map +1 -1
  988. package/dist/trpc/routers/skills.js +195 -170
  989. package/dist/trpc/routers/skills.js.map +1 -1
  990. package/dist/trpc/routers/storage.d.ts +3 -30
  991. package/dist/trpc/routers/storage.d.ts.map +1 -1
  992. package/dist/trpc/routers/storage.js +60 -117
  993. package/dist/trpc/routers/storage.js.map +1 -1
  994. package/dist/trpc/routers/storyboards.d.ts +6639 -0
  995. package/dist/trpc/routers/storyboards.d.ts.map +1 -0
  996. package/dist/trpc/routers/storyboards.js +156 -0
  997. package/dist/trpc/routers/storyboards.js.map +1 -0
  998. package/dist/trpc/routers/threads.d.ts +33 -21
  999. package/dist/trpc/routers/threads.d.ts.map +1 -1
  1000. package/dist/trpc/routers/threads.js +36 -30
  1001. package/dist/trpc/routers/threads.js.map +1 -1
  1002. package/dist/trpc/routers/timeline.d.ts +9085 -0
  1003. package/dist/trpc/routers/timeline.d.ts.map +1 -0
  1004. package/dist/trpc/routers/timeline.js +669 -0
  1005. package/dist/trpc/routers/timeline.js.map +1 -0
  1006. package/dist/trpc/routers/triggers.d.ts +46 -0
  1007. package/dist/trpc/routers/triggers.d.ts.map +1 -0
  1008. package/dist/trpc/routers/triggers.js +117 -0
  1009. package/dist/trpc/routers/triggers.js.map +1 -0
  1010. package/dist/trpc/routers/users.d.ts +1 -14
  1011. package/dist/trpc/routers/users.d.ts.map +1 -1
  1012. package/dist/trpc/routers/users.js +1 -12
  1013. package/dist/trpc/routers/users.js.map +1 -1
  1014. package/dist/trpc/routers/worker.d.ts +243 -0
  1015. package/dist/trpc/routers/worker.d.ts.map +1 -0
  1016. package/dist/trpc/routers/worker.js +221 -0
  1017. package/dist/trpc/routers/worker.js.map +1 -0
  1018. package/dist/trpc/routers/workflows.d.ts +428 -187
  1019. package/dist/trpc/routers/workflows.d.ts.map +1 -1
  1020. package/dist/trpc/routers/workflows.js +347 -345
  1021. package/dist/trpc/routers/workflows.js.map +1 -1
  1022. package/dist/trpc/routers/workspace.d.ts +59 -33
  1023. package/dist/trpc/routers/workspace.d.ts.map +1 -1
  1024. package/dist/trpc/routers/workspace.js +230 -93
  1025. package/dist/trpc/routers/workspace.js.map +1 -1
  1026. package/dist/trpc/sandbox-coverage.d.ts +64 -0
  1027. package/dist/trpc/sandbox-coverage.d.ts.map +1 -0
  1028. package/dist/trpc/sandbox-coverage.js +1139 -0
  1029. package/dist/trpc/sandbox-coverage.js.map +1 -0
  1030. package/dist/websocket-client-session.d.ts +322 -0
  1031. package/dist/websocket-client-session.d.ts.map +1 -0
  1032. package/dist/websocket-client-session.js +1267 -0
  1033. package/dist/websocket-client-session.js.map +1 -0
  1034. package/dist/workflow-interface-service.d.ts +87 -0
  1035. package/dist/workflow-interface-service.d.ts.map +1 -0
  1036. package/dist/workflow-interface-service.js +131 -0
  1037. package/dist/workflow-interface-service.js.map +1 -0
  1038. package/dist/workspace-api.d.ts.map +1 -1
  1039. package/dist/workspace-api.js +33 -23
  1040. package/dist/workspace-api.js.map +1 -1
  1041. package/dist/ws-adapter.d.ts +46 -3
  1042. package/dist/ws-adapter.d.ts.map +1 -1
  1043. package/dist/ws-adapter.js +164 -26
  1044. package/dist/ws-adapter.js.map +1 -1
  1045. package/package.json +97 -36
  1046. package/dist/agent/agent-runtime.d.ts +0 -47
  1047. package/dist/agent/agent-runtime.d.ts.map +0 -1
  1048. package/dist/agent/agent-runtime.js +0 -579
  1049. package/dist/agent/agent-runtime.js.map +0 -1
  1050. package/dist/agent/codex-agent.d.ts +0 -41
  1051. package/dist/agent/codex-agent.d.ts.map +0 -1
  1052. package/dist/agent/codex-agent.js +0 -334
  1053. package/dist/agent/codex-agent.js.map +0 -1
  1054. package/dist/agent/index.d.ts +0 -6
  1055. package/dist/agent/index.d.ts.map +0 -1
  1056. package/dist/agent/index.js +0 -4
  1057. package/dist/agent/index.js.map +0 -1
  1058. package/dist/agent/llm-agent.d.ts +0 -51
  1059. package/dist/agent/llm-agent.d.ts.map +0 -1
  1060. package/dist/agent/llm-agent.js +0 -526
  1061. package/dist/agent/llm-agent.js.map +0 -1
  1062. package/dist/agent/mcp-tool-server.d.ts +0 -33
  1063. package/dist/agent/mcp-tool-server.d.ts.map +0 -1
  1064. package/dist/agent/mcp-tool-server.js +0 -292
  1065. package/dist/agent/mcp-tool-server.js.map +0 -1
  1066. package/dist/agent/opencode-agent.d.ts +0 -32
  1067. package/dist/agent/opencode-agent.d.ts.map +0 -1
  1068. package/dist/agent/opencode-agent.js +0 -332
  1069. package/dist/agent/opencode-agent.js.map +0 -1
  1070. package/dist/agent/pi-agent.d.ts +0 -42
  1071. package/dist/agent/pi-agent.d.ts.map +0 -1
  1072. package/dist/agent/pi-agent.js +0 -429
  1073. package/dist/agent/pi-agent.js.map +0 -1
  1074. package/dist/agent/sdk-provider.d.ts +0 -46
  1075. package/dist/agent/sdk-provider.d.ts.map +0 -1
  1076. package/dist/agent/sdk-provider.js +0 -50
  1077. package/dist/agent/sdk-provider.js.map +0 -1
  1078. package/dist/agent/socket-route.d.ts +0 -11
  1079. package/dist/agent/socket-route.d.ts.map +0 -1
  1080. package/dist/agent/socket-route.js +0 -262
  1081. package/dist/agent/socket-route.js.map +0 -1
  1082. package/dist/agent/transport.d.ts +0 -39
  1083. package/dist/agent/transport.d.ts.map +0 -1
  1084. package/dist/agent/transport.js +0 -17
  1085. package/dist/agent/transport.js.map +0 -1
  1086. package/dist/agent/types.d.ts +0 -11
  1087. package/dist/agent/types.d.ts.map +0 -1
  1088. package/dist/agent/types.js +0 -10
  1089. package/dist/agent/types.js.map +0 -1
  1090. package/dist/routes/jobs.d.ts +0 -16
  1091. package/dist/routes/jobs.d.ts.map +0 -1
  1092. package/dist/routes/jobs.js +0 -25
  1093. package/dist/routes/jobs.js.map +0 -1
  1094. package/dist/trpc/routers/sandboxes.d.ts +0 -64
  1095. package/dist/trpc/routers/sandboxes.d.ts.map +0 -1
  1096. package/dist/trpc/routers/sandboxes.js +0 -327
  1097. package/dist/trpc/routers/sandboxes.js.map +0 -1
  1098. package/dist/unified-websocket-runner.d.ts +0 -280
  1099. package/dist/unified-websocket-runner.d.ts.map +0 -1
  1100. package/dist/unified-websocket-runner.js +0 -3840
  1101. package/dist/unified-websocket-runner.js.map +0 -1
package/dist/server.js CHANGED
@@ -6,49 +6,120 @@
6
6
  * An HTTP→HTTPS redirect server starts on port 80 when TLS is active.
7
7
  * Connect the CLI with: npm run chat -- --url ws://localhost:7777/ws
8
8
  */
9
+ import { ensureRunTraceTelemetry, startGenerationReconcileWorker, sweepInterruptedGenerations } from "@nodetool-ai/execution";
9
10
  import { join, resolve, dirname } from "node:path";
10
11
  import { fileURLToPath } from "node:url";
11
12
  import { existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
12
13
  import { spawnSync } from "node:child_process";
13
14
  import { createServer as createHttpServer } from "node:http";
14
15
  import crypto from "node:crypto";
15
- import { createLogger, configureLogging, getDefaultDbPath } from "@nodetool-ai/config";
16
- import { NodeRegistry } from "@nodetool-ai/node-sdk";
17
- import { registerBaseNodes } from "@nodetool-ai/base-nodes";
18
- import { registerElevenLabsNodes } from "@nodetool-ai/elevenlabs-nodes";
19
- import { registerTransformersJsNodes } from "@nodetool-ai/transformers-js-nodes";
16
+ import { createLogger, configureLogging, getDefaultDbPath, getPostgresDatabaseUrl, loadEnvironment } from "@nodetool-ai/config";
20
17
  import { registerTransformersJsProvider } from "@nodetool-ai/transformers-js-provider";
21
- import { registerFalNodes } from "@nodetool-ai/fal-nodes";
22
- import { registerKieNodes } from "@nodetool-ai/kie-nodes";
23
- import { registerReplicateNodes } from "@nodetool-ai/replicate-nodes";
24
- import { initTelemetry, setSecretResolver, PythonStdioBridge } from "@nodetool-ai/runtime";
25
- import { initMasterKey } from "@nodetool-ai/security";
26
- import { initDb, getSecret } from "@nodetool-ai/models";
27
- import { GoogleSearchTool, GoogleNewsTool, GoogleImagesTool, GoogleGroundedSearchTool, GoogleImageGenerationTool, OpenAIWebSearchTool, OpenAIImageGenerationTool, OpenAITextToSpeechTool, BrowserTool, ScreenshotTool, HttpRequestTool, CalculatorTool, RunCodeTool, StatisticsTool, GeometryTool, ConversionTool, SearchEmailTool, ArchiveEmailTool, AddLabelToEmailTool, DataForSEOSearchTool, DataForSEONewsTool, DataForSEOImagesTool } from "@nodetool-ai/agents";
28
- import { registerPythonProviders } from "./models-api.js";
18
+ import { registerWhisperCppProviders } from "@nodetool-ai/whisper-cpp";
19
+ import { setCodeNodeAgentsModule } from "@nodetool-ai/base-nodes";
20
+ import * as agentsModule from "@nodetool-ai/agents";
21
+ import { bootstrapNodeRegistry, mergePythonBridgeMetadata } from "./node-registry-setup.js";
22
+ import { corsOriginDelegate } from "./cors.js";
23
+ import { zipExtensionDist } from "./lib/extension-dist.js";
24
+ import { isPublicAuthExemptRoute } from "./lib/public-routes.js";
25
+ import { isWebSocketUpgrade, denyUnauthorized, denyDraining } from "./lib/ws-upgrade.js";
26
+ import { isDraining, startDrain } from "./drain.js";
27
+ import { chatTurnRegistry } from "./chat-turn-registry.js";
28
+ import { jobRunRegistry } from "./job-run-registry.js";
29
+ import { replayUpgradeToOwner } from "./lib/fly-replay.js";
30
+ import { startJobCancelPoller } from "./job-control.js";
31
+ import { resolveTrustLocalhost, isLoopbackAddress, parseTrustedProxies, parseTrustedLocalNetworks, isTrustedLocalAddress } from "./lib/localhost-trust.js";
32
+ import { registerHttpTracing } from "./lib/http-tracing.js";
33
+ import { shutdownTelemetry, createPythonBridge, WebsocketPythonBridge, SwappableBridge, logPythonWorkerStderr } from "@nodetool-ai/runtime";
34
+ import { deriveKey, getMasterKey, initMasterKey } from "@nodetool-ai/security";
35
+ import { setDefaultModelInterfaces } from "@nodetool-ai/runtime";
36
+ import { serverModelInterfaces } from "./websocket-client-session.js";
37
+ import { WorkerManager, attachWorkerActivityHeartbeat, startReaper } from "@nodetool-ai/compute";
38
+ import { getSecret, getRegisteredRunTrace, AccessToken, getWorkerProfile, initDb, initPostgresDb, isAccessToken, McpOauthClient, MCP_OAUTH_ACCESS_TOKEN_PREFIX, migrateSqliteDb, runSeeds } from "@nodetool-ai/models";
39
+ import { isMcpHttpEnabled, MCP_ENABLE_FLAG } from "./lib/mcp-mount.js";
40
+ import { authenticateMcpAccessToken, mcpBearerChallenge } from "./oauth/gate.js";
41
+ import { registerPythonProviders, relayWorkerDownload } from "./models-api.js";
42
+ import { createProviderRegistrar } from "./python-bridge-gate.js";
43
+ import { syncCustomProviderRegistry } from "./custom-providers.js";
44
+ import { runScheduledStorageCleanup } from "./storage-retention.js";
45
+ import { createGenerationRecoveryWorker } from "./generation-recovery.js";
46
+ import { sweepInterruptedJobs } from "./interrupted-jobs.js";
47
+ /** User id the auth middleware assigns in local (no-account) mode. */
48
+ const LOCAL_USER_ID = "1";
49
+ /** How long an MCP OAuth client nobody ever approved is kept before the
50
+ * periodic cleanup deletes it. */
51
+ const UNUSED_OAUTH_CLIENT_TTL_MS = 30 * 24 * 60 * 60 * 1000;
52
+ import { TRPC_MAX_BATCH_SIZE } from "@nodetool-ai/protocol";
29
53
  import { handleMcpHttpRequest } from "./mcp-server.js";
30
54
  import Fastify from "fastify";
31
55
  import fastifyWebSocket from "@fastify/websocket";
32
56
  import fastifyCors from "@fastify/cors";
57
+ import fastifyRateLimit from "@fastify/rate-limit";
33
58
  import fastifyStatic from "@fastify/static";
34
- import { encode } from "@msgpack/msgpack";
35
- import { SupabaseAuthProvider, LocalAuthProvider } from "@nodetool-ai/auth";
59
+ import { packWebSocketMessage } from "./messagepack.js";
60
+ import { SupabaseAuthProvider, LocalAuthProvider, DelegatedTokenProvider, isDelegatedToken, AppSessionTokenProvider, isAppSessionToken } from "@nodetool-ai/auth";
36
61
  import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify";
37
62
  import { appRouter } from "./trpc/router.js";
38
63
  import { createContextFactory } from "./trpc/context.js";
64
+ import { getHttpRateLimitConfig, rateLimitKey, isRateLimitExempt } from "./lib/http-rate-limit.js";
65
+ import { createTriggerWebhookRoute, startTriggerServices, triggersEnabled } from "./triggers/boot.js";
39
66
  import websocketPlugin from "./plugins/websocket.js";
40
- import healthRoute from "./routes/health.js";
67
+ import healthRoute, { getVersion } from "./routes/health.js";
68
+ import configRoute, { describeMissingAnonKey } from "./routes/config.js";
41
69
  import assetsRoutes from "./routes/assets.js";
70
+ import timelineFontRoutes from "./routes/timeline-fonts.js";
71
+ import sdkV1Routes from "./routes/sdk-v1.js";
42
72
  import workflowsRoutes from "./routes/workflows.js";
43
- import jobsRoutes from "./routes/jobs.js";
44
73
  import nodesRoutes from "./routes/nodes.js";
45
74
  import storageRoutes from "./routes/storage.js";
46
75
  import openaiRoutes from "./routes/openai.js";
47
76
  import oauthRoutes from "./routes/oauth.js";
77
+ import { oauthAsRoutes } from "./routes/oauth-as.js";
78
+ import { isSdkV1AuthenticationRequired, isSdkV1DiscoveryRequest } from "./sdk/sdk-route-policy.js";
79
+ import { createNodeToolSdkV1CapabilitiesProvider } from "./sdk/sdk-runtime-capabilities-service.js";
80
+ import { createSdkV1ModelDownloadService } from "./sdk/sdk-model-download-service.js";
81
+ import { getSdkV1ModelCatalog, SdkModelCatalogServiceError } from "./sdk/sdk-model-catalog-service.js";
82
+ import { unifiedModel } from "@nodetool-ai/protocol/api-schemas/models.js";
83
+ import { createNodeToolSdkV1PreflightService } from "./sdk/sdk-preflight-service.js";
84
+ import { createSdkV1ExecutionTargetReadiness } from "./sdk/sdk-execution-target-readiness.js";
85
+ import { SdkLiveRunnerRegistry } from "./sdk/sdk-live-runner-registry.js";
86
+ import { createSdkV1Service } from "./sdk/sdk-v1-service.js";
87
+ import { createSdkV1ImplementationBoundary } from "./sdk/sdk-v1-handler-map.js";
88
+ import { createSdkV1TemporaryAssetService } from "./sdk/sdk-temporary-asset-service.js";
89
+ import { getTempAdapter } from "./lib/storage.js";
90
+ import { FrontendRendererRegistry } from "./frontend-renderer-registry.js";
48
91
  import workspaceRoutes from "./routes/workspace.js";
92
+ import { initWorkspaceChangeEvents, initWorkspaceStorage } from "./lib/workflow-workspace.js";
49
93
  import filesRoutes from "./routes/files.js";
50
94
  import collectionsRoutes from "./routes/collections.js";
51
- import { agentSocketRoute, getAgentRuntime } from "./agent/index.js";
95
+ import applicationsRoutes from "./routes/applications.js";
96
+ import appRunsRoutes from "./routes/app-runs.js";
97
+ import runSpansRoutes from "./routes/run-spans.js";
98
+ import publicAppRoutes from "./routes/public-apps.js";
99
+ import publicSharedWorkflowRoutes from "./routes/public-shared-workflows.js";
100
+ import { appDeploymentsEnabled } from "./lib/app-deployment-service.js";
101
+ import accountRoutes from "./routes/account.js";
102
+ import jsScriptsRoutes from "./routes/js-scripts.js";
103
+ import timelineAnimationRoutes from "./routes/timeline-animations.js";
104
+ import timelineAudioBakeRoutes from "./routes/timeline-audio-bake.js";
105
+ import timelineIsolateSubjectRoutes from "./routes/timeline-isolate-subject.js";
106
+ import timelineTrackObjectRoutes from "./routes/timeline-track-object.js";
107
+ import storyboardsRoutes from "./routes/storyboards.js";
108
+ import timelinesRoutes from "./routes/timelines.js";
109
+ import documentsRoutes from "./routes/documents.js";
110
+ import sandboxModulesRoutes from "./routes/sandbox-modules.js";
111
+ import falCreditsRoute from "./routes/fal-credits.js";
112
+ import falPricingRoute from "./routes/fal-pricing.js";
113
+ import falPricingEstimateRoute from "./routes/fal-pricing-estimate.js";
114
+ import kieCreditsRoute from "./routes/kie-credits.js";
115
+ import kiePricingRoute from "./routes/kie-pricing.js";
116
+ import kieWebhookRoute from "./routes/kie-webhook.js";
117
+ import falWebhookRoute from "./routes/fal-webhook.js";
118
+ import atlasCloudWebhookRoute from "./routes/atlascloud-webhook.js";
119
+ import { createIntegrationRoutes } from "./routes/integrations.js";
120
+ import { isNonEmptyString, isString } from "./lib/wire-values.js";
121
+ import { logTrpcRequestError, traceTrpcRequestError } from "./trpc/error-logging.js";
122
+ import { captureError, startErrorTraceMaintenance } from "./error-traces.js";
52
123
  // @llamaindex/liteparse bundles a webpack pdf.js whose `isNodeJS` heuristic
53
124
  // resolves to false inside Electron utilityProcess (process.type === "utility"),
54
125
  // causing it to take browser-only code paths that reference the global `document`
@@ -57,8 +128,13 @@ import { agentSocketRoute, getAgentRuntime } from "./agent/index.js";
57
128
  // the heuristic back to Node mode so NodeBinaryDataFactory / fs-based asset
58
129
  // loading is selected. No-op outside Electron.
59
130
  {
60
- const proc = process;
61
- if (proc.versions?.electron && proc.type && proc.type !== "browser") {
131
+ // Electron adds these two to the Node `process`; on plain Node both reads
132
+ // come back undefined and the check below fails, as it should.
133
+ const electronVersion = process.versions["electron"];
134
+ // SAFETY: `type` is Electron's addition to the Node `process`, so it is
135
+ // declared optional here and reads undefined everywhere else.
136
+ const processType = process.type;
137
+ if (electronVersion && processType && processType !== "browser") {
62
138
  try {
63
139
  Object.defineProperty(process, "type", {
64
140
  value: "browser",
@@ -71,18 +147,27 @@ import { agentSocketRoute, getAgentRuntime } from "./agent/index.js";
71
147
  }
72
148
  }
73
149
  }
150
+ loadEnvironment(resolve(dirname(fileURLToPath(import.meta.url)), "../../.."));
74
151
  const log = createLogger("nodetool.websocket.server");
75
152
  // Apply log level from NODETOOL_LOG_LEVEL / LOG_LEVEL env vars (the module
76
153
  // initialises eagerly, but an explicit call here picks up any env mutations
77
154
  // made by the process launcher before this point).
78
155
  configureLogging();
79
- await initTelemetry();
156
+ await ensureRunTraceTelemetry();
80
157
  const startupT0 = performance.now();
158
+ /**
159
+ * When this process started. A generation or job row still in flight from
160
+ * before this moment belongs to a process that is gone, and the startup sweeps
161
+ * close it (docs/media-generation-tracking-design.md § 6.3).
162
+ */
163
+ const PROCESS_STARTED_AT = new Date().toISOString();
164
+ let stopGenerationReconcileWorker = null;
165
+ let stopErrorTraceMaintenance = null;
81
166
  function startupMs() {
82
167
  return `${(performance.now() - startupT0).toFixed(0)}ms`;
83
168
  }
84
169
  async function broadcastResourceChange(app, change) {
85
- const message = encode({
170
+ const message = packWebSocketMessage({
86
171
  type: "resource_change",
87
172
  ...change
88
173
  });
@@ -98,10 +183,7 @@ async function notifyPythonBridgeResourceChanges(app, pythonBridge) {
98
183
  resource_type: "metadata",
99
184
  resource: { id: "nodes", etag: String(Date.now()) }
100
185
  });
101
- const registered = await registerPythonProviders(pythonBridge);
102
- if (registered.length > 0) {
103
- log.info(`Registered Python providers: ${registered.join(", ")}`);
104
- }
186
+ const registered = await registerPythonProvidersOnce();
105
187
  const discoveredProviders = await pythonBridge.listProviders();
106
188
  const registeredProviders = new Set(registered);
107
189
  for (const provider of discoveredProviders) {
@@ -129,21 +211,119 @@ async function notifyPythonBridgeResourceChanges(app, pythonBridge) {
129
211
  // ---------------------------------------------------------------------------
130
212
  // Database setup
131
213
  // ---------------------------------------------------------------------------
132
- const dbPath = getDefaultDbPath();
214
+ function maskDatabaseUrl(url) {
215
+ try {
216
+ const parsed = new URL(url);
217
+ if (parsed.password) {
218
+ parsed.password = "***";
219
+ }
220
+ return parsed.toString();
221
+ }
222
+ catch {
223
+ return "(provided)";
224
+ }
225
+ }
133
226
  try {
134
- mkdirSync(dirname(dbPath), { recursive: true });
135
- initDb(dbPath);
136
- log.info(`Database ready [${startupMs()}]`, { path: dbPath });
137
- // Initialize master key from keychain before any secret access.
138
- // This must happen before setSecretResolver so that getMasterKey() (sync)
139
- // returns the keychain key rather than auto-generating a new one.
227
+ const postgresDatabaseUrl = getPostgresDatabaseUrl();
228
+ if (postgresDatabaseUrl) {
229
+ await initPostgresDb(postgresDatabaseUrl);
230
+ log.info(`PostgreSQL database ready [${startupMs()}]`, {
231
+ url: maskDatabaseUrl(postgresDatabaseUrl)
232
+ });
233
+ }
234
+ else {
235
+ const dbPath = getDefaultDbPath();
236
+ log.info(`Initializing SQLite database at: ${dbPath}`);
237
+ mkdirSync(dirname(dbPath), { recursive: true });
238
+ const appliedMigrations = await migrateSqliteDb(dbPath);
239
+ if (appliedMigrations.length > 0) {
240
+ log.info(`SQLite migrations applied [${startupMs()}]`, {
241
+ count: appliedMigrations.length,
242
+ versions: appliedMigrations
243
+ });
244
+ }
245
+ initDb(dbPath);
246
+ log.info(`SQLite database ready [${startupMs()}]`, { path: dbPath });
247
+ }
248
+ // Initialize master key from keychain before any secret access. Callers
249
+ // that need provider credentials bind their own `(key) => getSecret(key,
250
+ // userId)` closure when invoking provider APIs — there is no shared
251
+ // global resolver.
140
252
  await initMasterKey();
141
- setSecretResolver((key, userId) => getSecret(key, userId).then((v) => v ?? undefined));
253
+ // Bookkeeping for media generations: close the rows a restart orphaned, and
254
+ // keep refining estimates into billed amounts while the server runs.
255
+ void sweepInterruptedGenerations(PROCESS_STARTED_AT).catch((err) => {
256
+ log.warn("Interrupted-generation sweep failed", {
257
+ error: err instanceof Error ? err.message : String(err)
258
+ });
259
+ });
260
+ void sweepInterruptedJobs(PROCESS_STARTED_AT, {
261
+ sharedDatabase: Boolean(postgresDatabaseUrl)
262
+ }).catch((err) => {
263
+ log.warn("Interrupted-job sweep failed", {
264
+ error: err instanceof Error ? err.message : String(err)
265
+ });
266
+ });
267
+ stopGenerationReconcileWorker = startGenerationReconcileWorker({
268
+ resolveSecret: (key, userId) => getSecret(key, userId)
269
+ });
270
+ stopErrorTraceMaintenance = startErrorTraceMaintenance();
271
+ const runHistoryCleanup = () => {
272
+ void runScheduledStorageCleanup(LOCAL_USER_ID)
273
+ .then((result) => {
274
+ if (result && result.total > 0) {
275
+ log.info("Cleaned retained workflow and run history", result);
276
+ }
277
+ })
278
+ .catch((error) => {
279
+ log.warn("Automatic history cleanup failed", error instanceof Error ? error : new Error(String(error)));
280
+ });
281
+ // `/oauth/register` is anonymous and writes a durable row, so the rows it
282
+ // leaves behind need a sweep of their own: a client nobody ever approved
283
+ // is dead weight. One with any grant — active or revoked — is kept.
284
+ void McpOauthClient.gcUnused(UNUSED_OAUTH_CLIENT_TTL_MS)
285
+ .then((deleted) => {
286
+ if (deleted > 0) {
287
+ log.info("Deleted unapproved MCP OAuth clients", { deleted });
288
+ }
289
+ })
290
+ .catch((error) => {
291
+ log.warn("MCP OAuth client cleanup failed", error instanceof Error ? error : new Error(String(error)));
292
+ });
293
+ };
294
+ runHistoryCleanup();
295
+ const historyCleanupTimer = setInterval(runHistoryCleanup, 6 * 60 * 60 * 1000);
296
+ historyCleanupTimer.unref?.();
142
297
  }
143
298
  catch (err) {
144
299
  log.error("Database setup failed", err instanceof Error ? err : new Error(String(err)));
145
300
  process.exit(1);
146
301
  }
302
+ // Persistence for every context this process builds. Each entrance used to
303
+ // wire its own, and the ones that forgot produced runs that could not save an
304
+ // asset — the same workflow ran from the editor and threw over HTTP. A
305
+ // context that wires its own still wins.
306
+ setDefaultModelInterfaces(serverModelInterfaces());
307
+ // Seed built-in workflows — non-fatal; a seed failure should not prevent startup.
308
+ try {
309
+ await runSeeds();
310
+ log.info(`Seeds applied [${startupMs()}]`);
311
+ }
312
+ catch (err) {
313
+ log.warn("Seeds failed (non-fatal)", err instanceof Error ? err : new Error(String(err)));
314
+ }
315
+ // User-defined OpenAI-compatible providers for the local user, so a workflow
316
+ // run through a headless entrance reaches them without a settings request
317
+ // first. Each request re-syncs the caller's own catalog.
318
+ try {
319
+ const ids = await syncCustomProviderRegistry(LOCAL_USER_ID);
320
+ if (ids.length > 0) {
321
+ log.info(`Registered custom providers: ${ids.join(", ")}`);
322
+ }
323
+ }
324
+ catch (err) {
325
+ log.warn("Custom provider sync failed (non-fatal)", err instanceof Error ? err : new Error(String(err)));
326
+ }
147
327
  // ---------------------------------------------------------------------------
148
328
  // Python pip metadata root detection
149
329
  // ---------------------------------------------------------------------------
@@ -201,7 +381,7 @@ print(json.dumps(sorted(roots)))
201
381
  try {
202
382
  const roots = JSON.parse(proc.stdout.trim());
203
383
  if (Array.isArray(roots)) {
204
- return roots.filter((p) => typeof p === "string" && p.length > 0 && existsSync(p));
384
+ return roots.filter((p) => isNonEmptyString(p) && existsSync(p));
205
385
  }
206
386
  }
207
387
  catch {
@@ -239,28 +419,149 @@ log.info(`Metadata roots detected [${startupMs()}]`, {
239
419
  // ---------------------------------------------------------------------------
240
420
  // Node registry
241
421
  // ---------------------------------------------------------------------------
242
- const registry = new NodeRegistry();
243
- registry.loadPythonMetadata({ roots: metadataRoots, maxDepth: 8 });
244
- log.info(`Python metadata loaded [${startupMs()}]`);
245
- registerBaseNodes(registry);
246
- registerElevenLabsNodes(registry);
247
- registerTransformersJsNodes(registry);
248
- registerFalNodes(registry);
249
- registerKieNodes(registry);
250
- registerReplicateNodes(registry);
251
- registerTransformersJsProvider();
422
+ const registry = await bootstrapNodeRegistry({
423
+ metadataRoots,
424
+ log
425
+ });
252
426
  log.info(`Node registry ready [${startupMs()}]`);
427
+ // The Code node reaches the toolbelt and its `@nodetool-ai/sandbox-nodetool/*`
428
+ // imports through the agents package, which it resolves by bare specifier.
429
+ // That specifier resolves in a checkout and nowhere in the bundled backend —
430
+ // esbuild inlines the workspace packages into server.mjs — so hand it the copy
431
+ // this module already holds. Without it, a Code node body in the desktop app
432
+ // and in the Docker image runs with an empty `nodetool.capabilities()`.
433
+ setCodeNodeAgentsModule(agentsModule);
434
+ if (process.env["NODETOOL_ENV"] !== "production") {
435
+ registerTransformersJsProvider();
436
+ registerWhisperCppProviders();
437
+ }
253
438
  // ---------------------------------------------------------------------------
254
439
  // Python bridge
255
440
  // ---------------------------------------------------------------------------
256
- const pythonBridge = new PythonStdioBridge({
441
+ const localBridge = createPythonBridge({
257
442
  workerArgs: process.env["NODETOOL_WORKER_NAMESPACES"]
258
443
  ? ["--namespaces", process.env["NODETOOL_WORKER_NAMESPACES"]]
259
444
  : []
260
445
  });
446
+ // The ONE stable bridge reference handed to every consumer. It delegates to the
447
+ // local bridge by default; attaching a worker swaps in a dedicated WebSocket
448
+ // bridge to that worker (so ALL Python work — execution AND model management —
449
+ // targets the worker's GPU) and detach swaps back. Because the reference is
450
+ // stable and re-emits its target's events, consumers never re-read it and
451
+ // listeners are wired exactly once.
452
+ const pythonBridge = new SwappableBridge(localBridge);
453
+ /**
454
+ * Python-only providers (`huggingface-local`) exist only once the worker has
455
+ * listed them. A run that selects one must wait for this, not just for the
456
+ * connection: registration follows the connect, and a run that arrives in
457
+ * between is refused for an unknown provider.
458
+ */
459
+ const registerPythonProvidersOnce = createProviderRegistrar(async () => {
460
+ const registered = await registerPythonProviders(pythonBridge);
461
+ if (registered.length > 0) {
462
+ log.info(`Registered Python providers: ${registered.join(", ")}`);
463
+ }
464
+ return registered;
465
+ });
261
466
  let pythonBridgeReady = false;
467
+ // Worker bridge bookkeeping so it can be closed after swapping away. Only the
468
+ // remote WebSocket bridge is tracked here; the local stdio bridge is owned by
469
+ // `localBridge` above and never closed on swap.
470
+ let workerBridge = null;
471
+ const isLocalActive = () => pythonBridge.target === localBridge;
472
+ const getPythonBridgeReady = () => isLocalActive() ? pythonBridgeReady : pythonBridge.isConnected;
473
+ // ---------------------------------------------------------------------------
474
+ // Worker provisioning (GPU workers via @nodetool-ai/compute)
475
+ // ---------------------------------------------------------------------------
476
+ const workerManager = new WorkerManager();
477
+ /**
478
+ * Re-point the Python bridge for worker attach/detach by SWAPPING the
479
+ * SwappableBridge's target (a local stdio bridge cannot be re-pointed in place).
480
+ *
481
+ * On attach: build a dedicated WebSocket bridge to the worker, connect it
482
+ * (discover + status), swap it in as the target — so subsequent execution and
483
+ * model-management calls run on the worker — then close the previous worker
484
+ * bridge. Throws (failing the attach) if the worker can't be reached. On detach
485
+ * (`null`): swap back to the local bridge and dispose the worker bridge.
486
+ *
487
+ * `swap()` itself never connects or closes a bridge — lifecycle is owned here:
488
+ * connect before swapping in, close after swapping away.
489
+ */
490
+ async function repointPythonBridge(target) {
491
+ if (target) {
492
+ const next = new WebsocketPythonBridge({
493
+ wsUrl: target.wsUrl,
494
+ workerToken: target.token ?? undefined
495
+ });
496
+ try {
497
+ await next.connect();
498
+ }
499
+ catch (err) {
500
+ next.close();
501
+ throw err;
502
+ }
503
+ const previous = workerBridge;
504
+ workerBridge = next;
505
+ pythonBridge.swap(next);
506
+ if (previous)
507
+ previous.close();
508
+ log.info("Python bridge re-pointed to attached worker");
509
+ return;
510
+ }
511
+ const previous = workerBridge;
512
+ workerBridge = null;
513
+ pythonBridge.swap(localBridge);
514
+ if (previous)
515
+ previous.close();
516
+ log.info("Python bridge re-pointed back to the local worker");
517
+ }
518
+ /** Bound for the readiness probe: TCP/WS handshake + discover RPC. */
519
+ const WORKER_PROBE_TIMEOUT_MS = 8000;
520
+ /**
521
+ * Probe a `running` worker's readiness by opening a transient bridge to its
522
+ * `{wsUrl, token}` — the same handshake attach performs — then closing it. A
523
+ * resolved connect (WS open + `discover` answered) means the worker process is
524
+ * actually serving, not merely that the pod's port is mapped. Never throws:
525
+ * failures (still booting, unreachable) report `{ healthy: false }`.
526
+ */
527
+ async function probeWorkerHealth(target) {
528
+ if (!target.wsUrl) {
529
+ return { healthy: false, error: "Worker has no WebSocket URL yet" };
530
+ }
531
+ const probe = new WebsocketPythonBridge({
532
+ wsUrl: target.wsUrl,
533
+ workerToken: target.token ?? undefined,
534
+ // One-shot probe: no reconnect, bounded handshake.
535
+ autoRestart: false,
536
+ startupTimeoutMs: WORKER_PROBE_TIMEOUT_MS,
537
+ reconnectRpcTimeoutMs: WORKER_PROBE_TIMEOUT_MS
538
+ });
539
+ // A probe must never escalate a socket 'error' into a crash.
540
+ probe.on("error", () => { });
541
+ try {
542
+ await probe.connect();
543
+ return {
544
+ healthy: true,
545
+ protocolVersion: probe.supportsModelManagement() ? 2 : 1
546
+ };
547
+ }
548
+ catch (err) {
549
+ return {
550
+ healthy: false,
551
+ error: err instanceof Error ? err.message : String(err)
552
+ };
553
+ }
554
+ finally {
555
+ try {
556
+ probe.close();
557
+ }
558
+ catch {
559
+ /* best-effort cleanup */
560
+ }
561
+ }
562
+ }
262
563
  function logPythonBridgeDiagnostics(context) {
263
- const loadErrors = pythonBridge.getLoadErrors?.() ?? [];
564
+ const loadErrors = localBridge.getLoadErrors?.() ?? [];
264
565
  if (loadErrors.length === 0)
265
566
  return;
266
567
  log.warn(`Python bridge ${context} with ${loadErrors.length} load error(s)`);
@@ -273,64 +574,24 @@ function logPythonBridgeDiagnostics(context) {
273
574
  }
274
575
  pythonBridge.on("stderr", (msg) => {
275
576
  for (const line of msg.split("\n")) {
276
- if (line.trim())
277
- log.debug(`[python-worker] ${line}`);
577
+ logPythonWorkerStderr(line, log);
278
578
  }
279
579
  });
580
+ pythonBridge.on("error", (err) => {
581
+ log.error(`Python bridge protocol error: ${err.message}`);
582
+ pythonBridgeReady = false;
583
+ });
280
584
  pythonBridge.on("exit", (code) => {
281
585
  log.warn(`Python worker exited with code ${code}`);
282
586
  pythonBridgeReady = false;
283
587
  });
284
- // ---------------------------------------------------------------------------
285
- // Tool registry
286
- // ---------------------------------------------------------------------------
287
- const builtinToolClasses = [
288
- GoogleSearchTool,
289
- GoogleNewsTool,
290
- GoogleImagesTool,
291
- GoogleGroundedSearchTool,
292
- GoogleImageGenerationTool,
293
- OpenAIWebSearchTool,
294
- OpenAIImageGenerationTool,
295
- OpenAITextToSpeechTool,
296
- BrowserTool,
297
- ScreenshotTool,
298
- HttpRequestTool,
299
- CalculatorTool,
300
- RunCodeTool,
301
- StatisticsTool,
302
- GeometryTool,
303
- ConversionTool,
304
- SearchEmailTool,
305
- ArchiveEmailTool,
306
- AddLabelToEmailTool,
307
- DataForSEOSearchTool,
308
- DataForSEONewsTool,
309
- DataForSEOImagesTool
310
- ];
311
- // Lazy tool class map — defers instantiation until first access.
312
- let _toolClassMap = null;
313
- function getToolClassMap() {
314
- if (!_toolClassMap) {
315
- _toolClassMap = new Map();
316
- for (const cls of builtinToolClasses) {
317
- const instance = new cls();
318
- _toolClassMap.set(instance.name, cls);
319
- }
320
- log.info(`Tool class map built (${_toolClassMap.size} tools) [${startupMs()}]`);
321
- }
322
- return _toolClassMap;
323
- }
324
- // Expose as a getter-backed object so existing code using toolClassMap works unchanged.
325
- const toolClassMap = new Proxy(new Map(), {
326
- get(_target, prop, _receiver) {
327
- const map = getToolClassMap();
328
- const value = map[prop];
329
- if (typeof value === "function") {
330
- return value.bind(map);
331
- }
332
- return value;
333
- }
588
+ // Keep the attached worker's `last_activity_at` fresh so the cost guard's
589
+ // reaper measures idle time from real bridge traffic, not from creation. The
590
+ // throttling and the best-effort write live in @nodetool-ai/compute so the CLI
591
+ // gets the same behaviour; a local stdio bridge emits no "activity", so this is
592
+ // inert there.
593
+ attachWorkerActivityHeartbeat(pythonBridge, {
594
+ resolveInstanceId: async () => (await workerManager.getActiveWorker())?.id ?? null
334
595
  });
335
596
  // ---------------------------------------------------------------------------
336
597
  // TLS configuration
@@ -370,34 +631,116 @@ const port = Number(process.env["PORT"] ?? 7777);
370
631
  const isProduction = process.env["NODETOOL_ENV"] === "production";
371
632
  // In production, bind to all interfaces unless HOST is explicitly set
372
633
  const host = process.env["HOST"] ?? (isProduction ? "0.0.0.0" : "127.0.0.1");
634
+ // The `/mcp` streamable-HTTP mount. Off in production by default: it carries
635
+ // the full agent toolbelt for whichever user it binds, so a deployment opts in
636
+ // with NODETOOL_ENABLE_MCP=1 and authenticates the agent like any other client.
637
+ // The gate is shared with the settings UI, which reports it (see lib/mcp-mount).
638
+ const mcpHttpEnabled = isMcpHttpEnabled();
639
+ // The `WWW-Authenticate` challenge for an unauthenticated or invalid `/mcp`
640
+ // request (docs/mcp-oauth-design.md § "401 challenge") comes from
641
+ // `oauth/gate.ts` — the same gate the AS routes use, so the server never
642
+ // advertises a discovery flow the AS answers with 404. A server where the
643
+ // flow cannot complete answers the plain 401 it always has.
644
+ // Reverse proxies whose X-Forwarded-For header may be trusted to determine the
645
+ // real client IP. Comma-separated IPs/CIDRs (e.g. "127.0.0.1,10.0.0.0/8").
646
+ // When empty, X-Forwarded-For is NOT trusted: req.ip falls back to the socket
647
+ // peer address so clients cannot spoof their origin via headers.
648
+ const trustedProxies = parseTrustedProxies(process.env["NODETOOL_TRUSTED_PROXIES"]);
373
649
  // ---------------------------------------------------------------------------
374
650
  // Fastify app
375
651
  // ---------------------------------------------------------------------------
376
- const app = Fastify({
377
- ...(httpsOptions ? { https: httpsOptions } : {}),
378
- trustProxy: true,
652
+ const serverOptions = {
653
+ // Only trust X-Forwarded-For from explicitly configured proxies. With no
654
+ // proxies configured this is `false`, so req.ip is the unspoofable socket
655
+ // peer address.
656
+ trustProxy: trustedProxies.length > 0 ? trustedProxies : false,
379
657
  bodyLimit: 100 * 1024 * 1024, // 100 MB
380
658
  logger: false,
381
659
  // tRPC's httpBatchLink encodes all batched procedure names into a single
382
660
  // URL path segment joined with commas (e.g. `/trpc/foo,bar,baz`). Fastify's
383
- // default `maxParamLength` of 100 rejects larger batches with a 404 — bump
384
- // it so batches up to ~50 procedures route correctly.
661
+ // default `maxParamLength` of 100 rejects larger batches with a 404, and 2000
662
+ // was not enough either: one model-picker mount produced a ~2900 char path.
663
+ // The clients cap a batch at 20 procedures (`maxItems`); this is the backstop.
385
664
  routerOptions: {
386
665
  ignoreTrailingSlash: true,
387
- maxParamLength: 2000
666
+ maxParamLength: 8000
388
667
  },
389
668
  genReqId: (req) => {
390
669
  return req.headers["x-request-id"] || crypto.randomUUID();
391
670
  }
392
- });
671
+ };
672
+ const app = Fastify(httpsOptions ? { https: httpsOptions, ...serverOptions } : serverOptions);
673
+ // First hook, so every later hook and handler runs inside the request span.
393
674
  // ---------------------------------------------------------------------------
394
675
  // Request ID correlation
395
676
  // ---------------------------------------------------------------------------
396
677
  app.addHook("onRequest", async (request) => {
397
678
  request.log.info({ reqId: request.id, method: request.method, url: request.url }, "incoming request");
398
679
  });
680
+ // A REST handler that throws a 5xx leaves a redacted trace. 4xx errors are
681
+ // the caller's mistake and are not traced.
682
+ app.addHook("onError", async (request, _reply, error) => {
683
+ const status = error.statusCode ?? 500;
684
+ if (status < 500)
685
+ return;
686
+ captureError(error, {
687
+ source: "http",
688
+ userId: request.userId ?? null,
689
+ context: {
690
+ route: request.routeOptions.url ?? request.url.split("?")[0],
691
+ method: request.method,
692
+ http_status: status,
693
+ request_id: request.id
694
+ }
695
+ });
696
+ });
399
697
  app.addHook("onSend", async (request, reply) => {
400
698
  reply.header("X-Request-Id", request.id);
699
+ // Baseline security response headers. The web app's CSP lives in a `<meta>`
700
+ // tag, which browsers ignore for `frame-ancestors` — that directive is only
701
+ // honoured in a header, so clickjacking protection has to be set here.
702
+ //
703
+ // `/api/storage/*` is exempt: those bytes are designed to be embedded from
704
+ // other origins (MCP App iframes, the Electron renderer, external preview
705
+ // clients — see storage-api.ts), and the endpoint deliberately serves
706
+ // unknown extensions as `application/octet-stream` for `<img>`/`<video>` to
707
+ // sniff. Both `nosniff` and the frame headers would break that contract.
708
+ const path = request.url.split("?")[0] ?? "/";
709
+ if (!path.startsWith("/api/storage/")) {
710
+ reply.header("X-Content-Type-Options", "nosniff");
711
+ reply.header("X-Frame-Options", "SAMEORIGIN");
712
+ reply.header("Content-Security-Policy", "frame-ancestors 'self'");
713
+ }
714
+ reply.header("Referrer-Policy", "strict-origin-when-cross-origin");
715
+ });
716
+ // CORS must be registered before hooks that can short-circuit. Auth failures
717
+ // and rate-limit responses still need ACAO for first-party web deployments.
718
+ await app.register(fastifyCors, { origin: corsOriginDelegate });
719
+ // ---------------------------------------------------------------------------
720
+ // Per-IP HTTP rate limiting
721
+ // ---------------------------------------------------------------------------
722
+ // Registered before the auth hook so floods are rejected with 429 before any
723
+ // token verification work. Localhost is exempt; tune via NODETOOL_RATE_LIMIT_*.
724
+ const httpRateLimit = getHttpRateLimitConfig();
725
+ // Always register so every request path (including the auth hook) shares one
726
+ // limiter. When disabled via env, use a cap large enough to be inert in
727
+ // practice while keeping CodeQL/static setup able to see the plugin.
728
+ const httpRateLimitMax = httpRateLimit.enabled
729
+ ? httpRateLimit.max
730
+ : Number.MAX_SAFE_INTEGER;
731
+ await app.register(fastifyRateLimit, {
732
+ global: true,
733
+ max: httpRateLimitMax,
734
+ timeWindow: httpRateLimit.timeWindow,
735
+ keyGenerator: (req) => rateLimitKey(req, httpRateLimit.trustProxy),
736
+ allowList: (req) => isRateLimitExempt(rateLimitKey(req, httpRateLimit.trustProxy))
737
+ });
738
+ log.info(httpRateLimit.enabled
739
+ ? "Per-IP HTTP rate limiting enabled"
740
+ : "Per-IP HTTP rate limiting disabled (limiter registered with no practical cap)", {
741
+ max: httpRateLimitMax,
742
+ timeWindowMs: httpRateLimit.timeWindow,
743
+ trustProxy: httpRateLimit.trustProxy
401
744
  });
402
745
  // ---------------------------------------------------------------------------
403
746
  // Auth
@@ -412,63 +755,259 @@ const supabaseProvider = supabaseMode
412
755
  })
413
756
  : null;
414
757
  const localProvider = supabaseProvider ? null : new LocalAuthProvider();
758
+ // A server that enforces auth but never got an anon key serves a healthy
759
+ // /health and a well-formed /api/config while no user can log in. Say so at
760
+ // boot, where an operator reading the deploy log will see it.
761
+ const missingAnonKey = describeMissingAnonKey();
762
+ if (missingAnonKey) {
763
+ log.error(missingAnonKey);
764
+ }
765
+ // Auth is enforced whenever a remote identity provider is configured.
766
+ const enforceAuth = supabaseMode;
767
+ // Whether loopback connections may bypass auth as user "1". Explicit
768
+ // NODETOOL_TRUST_LOCALHOST wins; otherwise this defaults off when auth is
769
+ // enforced (so reverse-proxied/containerized deployments — where the proxy
770
+ // connects from loopback — don't silently disable auth) and on for local dev.
771
+ const trustLocalhost = resolveTrustLocalhost({
772
+ envValue: process.env["NODETOOL_TRUST_LOCALHOST"],
773
+ enforceAuth
774
+ });
775
+ if (enforceAuth && trustLocalhost) {
776
+ log.warn("NODETOOL_TRUST_LOCALHOST is enabled while auth is enforced: any " +
777
+ "connection from loopback (including a reverse proxy) bypasses " +
778
+ "authentication. Unset it unless the proxy is fully trusted.");
779
+ }
780
+ // Source networks trusted to run as the local user "1" without a token. Needed
781
+ // for containerized self-hosting: Docker NATs a published-port connection to
782
+ // the bridge gateway (e.g. 172.17.0.1), so it never arrives from loopback and
783
+ // the isLoopbackAddress bypass can't fire. Honored ONLY in Local mode — when
784
+ // auth is enforced a CIDR must never bypass a real login, so the list is empty.
785
+ const trustLocalNetworks = enforceAuth
786
+ ? []
787
+ : parseTrustedLocalNetworks(process.env["NODETOOL_TRUST_LOCAL_NETWORKS"]);
788
+ if (enforceAuth && process.env["NODETOOL_TRUST_LOCAL_NETWORKS"]) {
789
+ log.warn("NODETOOL_TRUST_LOCAL_NETWORKS is set while auth is enforced (Supabase " +
790
+ "mode); it is ignored so every request must present a valid token.");
791
+ }
792
+ else if (trustLocalNetworks.length > 0) {
793
+ log.warn(`Local mode: trusting connections from ${trustLocalNetworks.join(", ")} ` +
794
+ 'as user "1" without authentication. Only expose this instance to ' +
795
+ "networks you trust.");
796
+ }
797
+ // Messaging integrations (Telegram, later Discord). The service token is what
798
+ // enables the surface: without it there are no integration routes and no
799
+ // delegated tokens to verify.
800
+ const integrationServiceToken = process.env["NODETOOL_INTEGRATION_TOKEN"];
801
+ const integrationsEnabled = Boolean(integrationServiceToken && integrationServiceToken.length >= 16);
802
+ if (integrationServiceToken && !integrationsEnabled) {
803
+ log.warn("NODETOOL_INTEGRATION_TOKEN is shorter than 16 characters and was " +
804
+ "ignored; the /api/integrations routes are not registered.");
805
+ }
806
+ // Delegated tokens are signed with a key derived from the master key, so
807
+ // rotating the master key revokes every outstanding one. Derived lazily: a
808
+ // server that never mints one never reads the key.
809
+ let cachedDelegatedKey = null;
810
+ const delegatedSigningKey = () => (cachedDelegatedKey ??= deriveKey(getMasterKey(), "nodetool-delegated-token"));
811
+ const delegatedProvider = integrationsEnabled
812
+ ? new DelegatedTokenProvider(delegatedSigningKey)
813
+ : null;
814
+ // A deployed mini app's run sessions are signed with their own key, derived
815
+ // from the same master key under a different label. Rotating the master key
816
+ // invalidates every outstanding session; a distinct label keeps a session
817
+ // token from ever verifying as a delegated one, or the reverse.
818
+ let cachedAppSessionKey = null;
819
+ const appSessionSigningKey = () => (cachedAppSessionKey ??= deriveKey(getMasterKey(), "nodetool-app-session"));
820
+ // Public app deployments exist only in production; outside it the routes are
821
+ // still registered and every handler refuses, so the surface is decided by one
822
+ // predicate at request time rather than by boot order.
823
+ const appSessionProvider = new AppSessionTokenProvider(appSessionSigningKey);
415
824
  app.decorateRequest("userId", null);
825
+ app.decorateRequest("authToken", null);
826
+ app.decorateRequest("appSession", null);
827
+ // Global @fastify/rate-limit (registered above) runs before this hook on every
828
+ // request, including public auth exemptions handled by isPublicAuthExemptRoute.
829
+ // CodeQL js/missing-rate-limiting does not attribute a global Fastify plugin to
830
+ // a hook handler, so it would read this as unlimited. It ships in
831
+ // security-extended, which our default-setup code scanning does not run, so it
832
+ // never fires here. Nothing excludes it either: default setup reads no config
833
+ // file, so suppressing an alert means dismissing it in the Security UI.
416
834
  app.addHook("onRequest", async (req, reply) => {
417
835
  // Let CORS preflight through — the @fastify/cors plugin handles OPTIONS responses
418
836
  if (req.method === "OPTIONS")
419
837
  return;
420
- // Public routes — no auth required
838
+ // Public routes — no auth required (still rate-limited globally above).
421
839
  const pathname = req.url.split("?")[0];
422
- if (pathname === "/health" ||
423
- pathname === "/ready" ||
424
- pathname.startsWith("/api/oauth/") ||
425
- pathname === "/api/assets/packages" ||
426
- pathname.startsWith("/api/assets/packages/") ||
427
- pathname === "/api/nodes/metadata") {
840
+ if (isPublicAuthExemptRoute(pathname, req.method) ||
841
+ (isSdkV1DiscoveryRequest(pathname, req.method) &&
842
+ !isSdkV1AuthenticationRequired(process.env, enforceAuth))) {
428
843
  return;
429
844
  }
430
845
  // Static frontend assets don't require auth (served by fastifyStatic)
846
+ // `GET /mcp` is the MCP SSE stream, not a static asset — it must go through
847
+ // auth so the mount can bind the session's user.
431
848
  if (hasStaticApp &&
432
849
  req.method === "GET" &&
433
850
  !pathname.startsWith("/api") &&
434
851
  !pathname.startsWith("/ws") &&
435
852
  !pathname.startsWith("/v1") &&
436
- !pathname.startsWith("/trpc")) {
853
+ !pathname.startsWith("/trpc") &&
854
+ !pathname.startsWith("/mcp")) {
855
+ return;
856
+ }
857
+ // req.ip reflects X-Forwarded-For only when the request arrived through a
858
+ // configured trusted proxy (see NODETOOL_TRUSTED_PROXIES); otherwise it is
859
+ // the unspoofable socket peer address.
860
+ const clientIp = req.ip;
861
+ // Loopback connections bypass auth as user "1" only when explicitly trusted.
862
+ // Behind a reverse proxy/container/SSH tunnel the proxy itself connects from
863
+ // loopback, so this is gated by NODETOOL_TRUST_LOCALHOST (off by default when
864
+ // auth is enforced). Docker's bridge NAT means the peer is the gateway rather
865
+ // than loopback, so NODETOOL_TRUST_LOCAL_NETWORKS additionally trusts that
866
+ // source range (Local mode only).
867
+ if ((trustLocalhost && isLoopbackAddress(clientIp)) ||
868
+ isTrustedLocalAddress(clientIp, trustLocalNetworks)) {
869
+ req.userId = "1";
437
870
  return;
438
871
  }
439
872
  // Extract token from the appropriate source
440
- const isWs = req.headers["upgrade"]?.toLowerCase() === "websocket";
873
+ const isWs = isWebSocketUpgrade(req);
441
874
  const searchParams = new URLSearchParams(req.url.split("?")[1] ?? "");
442
875
  const provider = supabaseProvider ?? localProvider;
443
876
  const token = isWs
444
877
  ? provider.extractTokenFromWs(req.headers, searchParams)
445
878
  : provider.extractTokenFromHeaders(req.headers);
879
+ // MCP OAuth access tokens (`nta_`) authorize `/mcp` ONLY — the audience
880
+ // binding is structural, not claim-parsing (docs/mcp-oauth-design.md §
881
+ // "Token model and storage"). A valid token whose stored `resource` no
882
+ // longer matches this deployment's canonical `/mcp` URI (the server was
883
+ // rehosted since it was minted) or that is presented outside `/mcp` is
884
+ // denied with `invalid_token`, never silently ignored — falling through to
885
+ // the `ntk_`/session checks below would let an audience-scoped credential
886
+ // be probed against the rest of the API.
887
+ if (token && token.startsWith(MCP_OAUTH_ACCESS_TOKEN_PREFIX)) {
888
+ const decision = await authenticateMcpAccessToken(token, req.url.split("?")[0] ?? "/");
889
+ if (decision.ok) {
890
+ req.userId = decision.userId;
891
+ req.authToken = token;
892
+ return;
893
+ }
894
+ denyUnauthorized(req, reply, { error: "invalid_token" }, decision.challenge);
895
+ return;
896
+ }
897
+ // Access tokens (`ntk_`) are the credential a person mints in the app and
898
+ // pastes into an MCP client's config. They authenticate in every auth mode,
899
+ // including local: a self-hoster exposing /mcp beyond loopback should be
900
+ // able to hand out a revocable per-agent token rather than widen
901
+ // NODETOOL_TRUST_LOCAL_NETWORKS. A token that fails here falls through, so
902
+ // an expired or revoked one is refused by the mode's own rules.
903
+ if (token && isAccessToken(token)) {
904
+ const accessToken = await AccessToken.verify(token);
905
+ if (accessToken) {
906
+ req.userId = accessToken.user_id;
907
+ req.authToken = token;
908
+ void accessToken.touch().catch(() => {
909
+ // last_used_at is a convenience for the revoke UI; a failed write
910
+ // must not fail the request it was recording.
911
+ });
912
+ return;
913
+ }
914
+ }
915
+ // A deployed mini app's session token (`nda_`) authenticates a visitor who
916
+ // has the app's hidden URL. It carries the owner's user id *and* the one
917
+ // application it may act on, and its whole point is that it reaches nothing
918
+ // else — so the confinement is applied right here, at the door, rather than
919
+ // trusted to every handler downstream. `/ws` is the only path it opens: the
920
+ // run transport, and exactly that path — `/ws/download` is model downloads,
921
+ // which a visitor to somebody's app has no business opening. The public app routes
922
+ // need no token at all (they are auth-exempt above), and every other path —
923
+ // tRPC, the REST library, assets, settings — is refused, because the
924
+ // owner's account is not what the visitor was given a link to.
925
+ //
926
+ // A failed verification does *not* fall through to the providers below. An
927
+ // expired or tampered app session must be refused as itself; letting it
928
+ // reach the session check would turn a confined credential into a probe
929
+ // against the rest of the API.
930
+ if (token && isAppSessionToken(token)) {
931
+ const session = await appSessionProvider.verifyToken(token);
932
+ if (session.ok &&
933
+ session.applicationId &&
934
+ session.userId &&
935
+ appDeploymentsEnabled() &&
936
+ pathname === "/ws") {
937
+ req.userId = session.userId;
938
+ req.authToken = token;
939
+ req.appSession = {
940
+ applicationId: session.applicationId,
941
+ version: session.applicationVersion ?? 0
942
+ };
943
+ return;
944
+ }
945
+ denyUnauthorized(req, reply, { error: "Unauthorized" });
946
+ return;
947
+ }
948
+ // Delegated tokens (a messaging bridge acting as the linked user) are
949
+ // checked only when they announce themselves by prefix. Anything else —
950
+ // including a delegated token that is expired or tampered with — falls
951
+ // through to the configured provider, which rejects it.
952
+ if (delegatedProvider && token && isDelegatedToken(token)) {
953
+ const delegated = await delegatedProvider.verifyToken(token);
954
+ if (delegated.ok) {
955
+ req.userId = delegated.userId ?? null;
956
+ req.authToken = token;
957
+ return;
958
+ }
959
+ }
960
+ // /mcp gets a WWW-Authenticate challenge on every unauthenticated/invalid
961
+ // denial below it — but only when the OAuth flow can actually complete
962
+ // (see mcpBearerChallenge). Every other path is unaffected.
963
+ const challenge = pathname.startsWith("/mcp")
964
+ ? mcpBearerChallenge()
965
+ : undefined;
446
966
  if (supabaseMode) {
447
967
  if (!token) {
448
- reply.status(401).send({ error: "Unauthorized" });
968
+ denyUnauthorized(req, reply, { error: "Unauthorized" }, challenge);
449
969
  return;
450
970
  }
451
971
  const result = await supabaseProvider.verifyToken(token);
452
972
  if (!result.ok) {
453
- reply.status(401).send({ error: result.error ?? "Unauthorized" });
973
+ denyUnauthorized(req, reply, { error: result.error ?? "Unauthorized" }, challenge);
454
974
  return;
455
975
  }
456
976
  req.userId = result.userId ?? null;
977
+ req.authToken = token;
457
978
  return;
458
979
  }
459
- // Dev mode: localhost only
460
- // Use req.socket.remoteAddress rather than req.ip because trustProxy: true
461
- // makes req.ip reflect x-forwarded-for (spoofable).
462
- const remoteAddr = req.socket.remoteAddress ?? "";
463
- const isLocalhost = remoteAddr === "127.0.0.1" || remoteAddr === "::1";
464
- if (!isLocalhost) {
465
- reply.status(401).send({ error: "Remote access requires authentication" });
466
- return;
980
+ denyUnauthorized(req, reply, { error: "Remote access requires authentication" }, challenge);
981
+ });
982
+ registerHttpTracing(app, {
983
+ async authorizeTraceParent(request, parent) {
984
+ if (!request.userId || request.appSession) {
985
+ return false;
986
+ }
987
+ return Boolean(await getRegisteredRunTrace(request.userId, parent.traceId));
467
988
  }
468
- req.userId = "1";
469
989
  });
470
- // CORS
471
- await app.register(fastifyCors, { origin: true });
990
+ // Multi-instance only: a handshake asking to resume a run that another machine
991
+ // owns is answered with a fly-replay instruction instead of being accepted
992
+ // here, so the client reconnects onto the process holding the run's session.
993
+ // Registered after the auth hook because it scopes the lookup to req.userId.
994
+ // Inert (one env read, no DB) without NODETOOL_INSTANCE_ID / FLY_MACHINE_ID.
995
+ app.addHook("onRequest", async (req, reply) => {
996
+ await replayUpgradeToOwner(req, reply);
997
+ });
998
+ // A machine that is draining takes no new sockets: the handshake is answered
999
+ // with 503 rather than accepted, so the client's reconnect backoff carries it
1000
+ // to a machine that is staying. Its /health is already failing, so the proxy
1001
+ // normally routes elsewhere before this is reached; this covers the window
1002
+ // between the drain starting and the next health check.
1003
+ app.addHook("onRequest", async (req, reply) => {
1004
+ if (!isDraining() || !isWebSocketUpgrade(req))
1005
+ return;
1006
+ const path = req.url.split("?")[0] ?? "";
1007
+ if (path !== "/ws" && !path.startsWith("/ws/"))
1008
+ return;
1009
+ denyDraining(req, reply);
1010
+ });
472
1011
  // WebSocket support
473
1012
  await app.register(fastifyWebSocket);
474
1013
  // Parse all request bodies as raw Buffer — the Web API handlers do their own parsing
@@ -491,6 +1030,7 @@ app.addContentTypeParser("*", { parseAs: "buffer" }, (_req, body, done) => {
491
1030
  const _serverDir = dirname(fileURLToPath(import.meta.url));
492
1031
  const _envExamplesDir = process.env["NODETOOL_BASE_EXAMPLES_DIR"];
493
1032
  const _bundledExamplesDir = resolve(_serverDir, "examples", "nodetool-base");
1033
+ const _bundledAssetsDir = resolve(_serverDir, "assets", "nodetool-base");
494
1034
  const _monoExamplesDir = resolve(_serverDir, "..", "..", "..", "packages", "base-nodes", "nodetool", "examples", "nodetool-base");
495
1035
  const _examplesDirCandidates = [
496
1036
  _envExamplesDir && existsSync(_envExamplesDir) ? _envExamplesDir : null,
@@ -507,9 +1047,161 @@ else {
507
1047
  // ---------------------------------------------------------------------------
508
1048
  // API options for HTTP route handlers
509
1049
  // ---------------------------------------------------------------------------
510
- const apiOptions = { metadataRoots, registry };
1050
+ const sdkLiveRunnerRegistry = new SdkLiveRunnerRegistry();
1051
+ const frontendRendererRegistry = new FrontendRendererRegistry();
1052
+ const resolveExecutionTarget = createSdkV1ExecutionTargetReadiness({
1053
+ getActiveWorker: () => workerManager.getActiveWorker()
1054
+ });
1055
+ function listRegistryRecommendedModels() {
1056
+ const models = [];
1057
+ for (const metadata of registry.listMetadata()) {
1058
+ for (const candidate of metadata.recommended_models ?? []) {
1059
+ const parsed = unifiedModel.safeParse(candidate);
1060
+ if (parsed.success) {
1061
+ models.push({
1062
+ ...parsed.data,
1063
+ provider: parsed.data.provider == null ? undefined : parsed.data.provider
1064
+ });
1065
+ }
1066
+ }
1067
+ }
1068
+ return models;
1069
+ }
1070
+ const sdkModelDownloadService = createSdkV1ModelDownloadService({
1071
+ startWorkerDownload: (userId, request, operationId, onProgress) => relayWorkerDownload({
1072
+ send: (data) => onProgress(JSON.parse(data))
1073
+ }, pythonBridge, workerManager, {
1074
+ command: "start_download",
1075
+ repo_id: request.repo_id,
1076
+ path: request.path ?? null,
1077
+ allow_patterns: request.allow_patterns ?? null,
1078
+ ignore_patterns: request.ignore_patterns ?? null,
1079
+ model_type: request.model_type,
1080
+ scope: "worker"
1081
+ }, operationId, userId),
1082
+ cancelWorkerDownload: (operationId) => pythonBridge.cancelModelDownload(operationId)
1083
+ });
1084
+ const apiOptions = {
1085
+ metadataRoots,
1086
+ registry,
1087
+ getPythonBridgeReady,
1088
+ sdkV1Boundary: createSdkV1ImplementationBoundary(createSdkV1Service({
1089
+ getCapabilities: createNodeToolSdkV1CapabilitiesProvider({
1090
+ nodetoolVersion: getVersion(),
1091
+ registry,
1092
+ pythonBridge: () => (getPythonBridgeReady() ? "ready" : "starting"),
1093
+ profiles: {
1094
+ discovery: "available",
1095
+ execution: "available",
1096
+ preflight: "available",
1097
+ model_catalog: "available",
1098
+ model_download: "available",
1099
+ temporary_asset_upload: "available"
1100
+ },
1101
+ authModes: enforceAuth ? ["bearer"] : ["trusted_local"],
1102
+ assetUriSchemes: ["asset"],
1103
+ limits: {
1104
+ maxRpcBatch: 100,
1105
+ // The initial SDK profile deliberately prefers asset references for
1106
+ // media instead of promising an inline payload size.
1107
+ maxInlineBytes: 0,
1108
+ maxQueuedJobs: 0,
1109
+ maxJobEventReplay: 0,
1110
+ requestTimeoutSeconds: 30
1111
+ }
1112
+ }),
1113
+ preflightService: createNodeToolSdkV1PreflightService({
1114
+ registry,
1115
+ getPythonBridgeReady,
1116
+ getExecutionTargetReadiness: ({ request, principal }) => {
1117
+ const target = request.execution_target;
1118
+ if (target?.kind === "runner") {
1119
+ const ready = sdkLiveRunnerRegistry.has(target.runner_id, principal.userId);
1120
+ return {
1121
+ id: target.runner_id,
1122
+ name: "Live runner",
1123
+ ready,
1124
+ message: ready ? null : "Selected live runner is not available."
1125
+ };
1126
+ }
1127
+ return resolveExecutionTarget(target);
1128
+ },
1129
+ getExecutionCapacitySnapshot: ({ request, principal }) => {
1130
+ const target = request.execution_target;
1131
+ if (target?.kind !== "runner") {
1132
+ throw new Error("Execution capacity requires a selected live runner.");
1133
+ }
1134
+ return sdkLiveRunnerRegistry.getCapacity({
1135
+ runnerId: target.runner_id,
1136
+ userId: principal.userId,
1137
+ workflowId: request.workflow_id,
1138
+ concurrent: target.concurrent
1139
+ });
1140
+ }
1141
+ }),
1142
+ modelCatalogService: {
1143
+ list: (args) => getSdkV1ModelCatalog({
1144
+ ...args,
1145
+ recommendedModels: listRegistryRecommendedModels(),
1146
+ getWorkerModels: async () => {
1147
+ const activeWorker = await workerManager?.getActiveWorker();
1148
+ if (!activeWorker) {
1149
+ throw new SdkModelCatalogServiceError("No worker is attached to this server.");
1150
+ }
1151
+ if (!pythonBridge.supportsModelManagement()) {
1152
+ throw new SdkModelCatalogServiceError("The attached worker does not support model management.");
1153
+ }
1154
+ const models = [];
1155
+ for (const candidate of await pythonBridge.listCachedModels()) {
1156
+ const parsed = unifiedModel.safeParse(candidate);
1157
+ if (parsed.success)
1158
+ models.push(parsed.data);
1159
+ }
1160
+ return models;
1161
+ }
1162
+ })
1163
+ },
1164
+ modelDownloadService: sdkModelDownloadService,
1165
+ temporaryAssetService: createSdkV1TemporaryAssetService({
1166
+ getStorage: getTempAdapter
1167
+ })
1168
+ }))
1169
+ };
511
1170
  if (_resolvedExamplesDir) {
512
1171
  apiOptions.examplesDir = _resolvedExamplesDir;
1172
+ if (existsSync(_bundledAssetsDir)) {
1173
+ apiOptions.examplesAssetsFallbackDir = _bundledAssetsDir;
1174
+ }
1175
+ }
1176
+ // Constant package assets (`package://<pkg>/<file>`) are served read-only at
1177
+ // `/api/assets/packages/<pkg>/<file>` from `<root>/<pkg>/<file>`. The bundled
1178
+ // root sits next to server.mjs (no Python needed); the monorepo root is used
1179
+ // in dev/dist runs.
1180
+ const _bundledPackageAssetsRoot = resolve(_serverDir, "assets");
1181
+ const _monoPackageAssetsRoot = resolve(_serverDir, "..", "..", "..", "packages", "base-nodes", "nodetool", "assets");
1182
+ // The bundled typefaces (D8). Two layouts, checked rather than assumed: the
1183
+ // packaged backend stages them beside `server.mjs`, a checkout keeps them in
1184
+ // the timeline package's own `fonts/` directory. `registerBundledFonts` in
1185
+ // `@nodetool-ai/timeline/fonts/node` resolves the same two for the Node
1186
+ // rasterizers; this is the copy the HTTP route serves to the browser from.
1187
+ const _bundledFontsDir = [
1188
+ resolve(_serverDir, "fonts"),
1189
+ resolve(_serverDir, "..", "..", "..", "packages", "timeline", "fonts")
1190
+ ].find((dir) => existsSync(dir));
1191
+ if (_bundledFontsDir) {
1192
+ apiOptions.bundledFontsDir = _bundledFontsDir;
1193
+ }
1194
+ const _packageAssetsRoots = [
1195
+ existsSync(_bundledPackageAssetsRoot) ? _bundledPackageAssetsRoot : null,
1196
+ existsSync(_monoPackageAssetsRoot) ? _monoPackageAssetsRoot : null
1197
+ ].filter((d) => Boolean(d));
1198
+ if (_packageAssetsRoots.length > 0) {
1199
+ apiOptions.packageAssetsRoots = _packageAssetsRoots;
1200
+ // Let in-process workflow execution resolve `package://` refs straight from
1201
+ // disk instead of guessing the loopback port for an HTTP round-trip.
1202
+ if (!process.env["NODETOOL_PACKAGE_ASSETS_DIR"]) {
1203
+ process.env["NODETOOL_PACKAGE_ASSETS_DIR"] = _packageAssetsRoots[0];
1204
+ }
513
1205
  }
514
1206
  const staticFolder = process.env["STATIC_FOLDER"];
515
1207
  const hasStaticApp = Boolean(staticFolder && existsSync(staticFolder));
@@ -520,32 +1212,51 @@ const createContext = createContextFactory({
520
1212
  registry,
521
1213
  apiOptions,
522
1214
  pythonBridge,
523
- getPythonBridgeReady: () => pythonBridgeReady
1215
+ getPythonBridgeReady,
1216
+ workerManager,
1217
+ repointPythonBridge,
1218
+ probeWorkerHealth
524
1219
  });
525
1220
  await app.register(fastifyTRPCPlugin, {
526
1221
  prefix: "/trpc",
527
1222
  trpcOptions: {
528
1223
  router: appRouter,
529
1224
  createContext,
530
- onError({ path, error }) {
531
- log.error(`tRPC error on ${path}`, error instanceof Error ? error : new Error(String(error)));
532
- // Surface inner ZodError details (output validation failures) so callers
533
- // can diagnose the offending fields instead of seeing only "Output
534
- // validation failed".
535
- const cause = error.cause;
536
- if (cause && typeof cause === "object" && "issues" in cause) {
537
- log.error(`tRPC error cause on ${path}: ${JSON.stringify(cause.issues)}`);
538
- }
1225
+ // The web client forces all batches to POST (httpBatchLink
1226
+ // `methodOverride: "POST"`) so long batched inputs ride in the body instead
1227
+ // of the URL, which reverse proxies reject once the query string grows too
1228
+ // large (see web/src/trpc/client.ts and issues #3979/#3981). Without this
1229
+ // flag the server rejects POST-to-query with 405, leaving panels empty.
1230
+ allowMethodOverride: true,
1231
+ maxBatchSize: TRPC_MAX_BATCH_SIZE,
1232
+ onError({ path, error, req, ctx }) {
1233
+ logTrpcRequestError(log, {
1234
+ path,
1235
+ error,
1236
+ requestId: req.id
1237
+ });
1238
+ traceTrpcRequestError({
1239
+ path,
1240
+ error,
1241
+ requestId: req.id,
1242
+ userId: ctx?.userId
1243
+ });
539
1244
  }
540
1245
  }
541
1246
  });
542
1247
  await app.register(websocketPlugin, {
543
1248
  registry,
544
1249
  pythonBridge,
545
- getPythonBridgeReady: () => pythonBridgeReady,
1250
+ apiOptions,
1251
+ sdkLiveRunnerRegistry,
1252
+ frontendRendererRegistry,
1253
+ workerManager,
1254
+ getPythonBridgeReady,
546
1255
  ensurePythonBridge: async () => {
547
- if (pythonBridgeReady)
1256
+ if (getPythonBridgeReady()) {
1257
+ await registerPythonProvidersOnce();
548
1258
  return;
1259
+ }
549
1260
  log.info(`Lazily starting Python bridge [${startupMs()}]`);
550
1261
  try {
551
1262
  await pythonBridge.ensureConnected();
@@ -554,31 +1265,18 @@ await app.register(websocketPlugin, {
554
1265
  log.warn(`Python bridge lazy start failed [${startupMs()}]`, err instanceof Error ? err : new Error(String(err)));
555
1266
  throw err;
556
1267
  }
1268
+ // A worker bridge is connected eagerly on attach; readiness then derives
1269
+ // from its live connection state. Only the local bridge does the one-time
1270
+ // node-metadata registration below.
1271
+ if (!isLocalActive()) {
1272
+ log.info(`Worker Python bridge connected [${startupMs()}]`);
1273
+ return;
1274
+ }
557
1275
  pythonBridgeReady = true;
558
1276
  log.info(`Python bridge lazy start completed [${startupMs()}]`);
559
1277
  const meta = pythonBridge.getNodeMetadata();
560
- // Register Python bridge nodes — skip those already loaded from JSON metadata
561
- let bridgeOnly = 0;
562
- for (const nodeMeta of meta) {
563
- if (!nodeMeta.node_type)
564
- continue;
565
- if (registry.getMetadata(nodeMeta.node_type))
566
- continue;
567
- bridgeOnly++;
568
- registry.loadMetadata(nodeMeta.node_type, {
569
- ...nodeMeta,
570
- namespace: nodeMeta.node_type.split(".").slice(0, -1).join("."),
571
- layout: "default",
572
- recommended_models: [],
573
- basic_fields: [],
574
- required_settings: nodeMeta.required_settings ?? [],
575
- is_dynamic: nodeMeta.is_dynamic ?? false,
576
- is_streaming_output: nodeMeta.is_streaming_output ?? false,
577
- expose_as_tool: false,
578
- supports_dynamic_outputs: false
579
- });
580
- }
581
- log.info(`Python bridge connected [${startupMs()}] — ${meta.length} Python nodes (${bridgeOnly} bridge-only, ${meta.length - bridgeOnly} from JSON)`);
1278
+ const mergeResult = mergePythonBridgeMetadata(registry, meta);
1279
+ log.info(`Python bridge connected [${startupMs()}] — ${mergeResult.total} Python nodes (${mergeResult.bridgeOnly} bridge-only, ${mergeResult.alreadyKnown} already registered)`);
582
1280
  pythonBridge
583
1281
  .getWorkerStatus?.()
584
1282
  ?.then((status) => {
@@ -596,29 +1294,90 @@ await app.register(websocketPlugin, {
596
1294
  log.warn(`Failed to fetch Python bridge status [${startupMs()}]`, err instanceof Error ? err : new Error(String(err)));
597
1295
  });
598
1296
  logPythonBridgeDiagnostics("connected");
1297
+ // The run that asked for the bridge may select a Python-only provider.
1298
+ await registerPythonProvidersOnce();
599
1299
  notifyPythonBridgeResourceChanges(app, pythonBridge).catch((err) => {
600
1300
  log.warn("Failed to notify Python bridge resource changes", err instanceof Error ? err : new Error(String(err)));
601
1301
  });
602
- },
603
- toolClassMap
1302
+ }
604
1303
  });
1304
+ // One poller for the whole process, not one per connection: it re-reads this
1305
+ // instance's own running runs so a cancel written on another instance lands
1306
+ // here. The registry it consults is process-wide.
1307
+ const stopJobCancelPoller = startJobCancelPoller();
605
1308
  await app.register(healthRoute);
1309
+ await app.register(configRoute);
606
1310
  // All HTTP API routes receive apiOptions
607
1311
  const routeOpts = { apiOptions };
1312
+ await app.register(sdkV1Routes, routeOpts);
608
1313
  await app.register(assetsRoutes, routeOpts);
1314
+ await app.register(timelineFontRoutes, routeOpts);
609
1315
  await app.register(workflowsRoutes, routeOpts);
610
- await app.register(jobsRoutes, routeOpts);
611
1316
  await app.register(nodesRoutes, routeOpts);
612
1317
  await app.register(storageRoutes, routeOpts);
613
1318
  await app.register(openaiRoutes, routeOpts);
614
1319
  await app.register(oauthRoutes, routeOpts);
1320
+ // MCP OAuth 2.1 AS + well-known discovery routes (docs/mcp-oauth-design.md).
1321
+ // Self-contained: registers its own content-type parsers on its own
1322
+ // encapsulated context, so it is unaffected by the raw-buffer parser the
1323
+ // outer app installs above. 404s every route on its own when the flow is
1324
+ // unconfigured or NODETOOL_DISABLE_MCP_OAUTH=1 — nothing to gate here.
1325
+ await app.register(oauthAsRoutes);
615
1326
  await app.register(workspaceRoutes, routeOpts);
1327
+ // A cloud deployment keeps workspaces in the asset bucket rather than on the
1328
+ // machine's disk, which it would lose on every replacement. No-op locally.
1329
+ initWorkspaceStorage();
1330
+ // The Workspace Explorer has no other way to hear about a file a run wrote:
1331
+ // workspace files are not database rows.
1332
+ initWorkspaceChangeEvents();
616
1333
  await app.register(filesRoutes, routeOpts);
617
1334
  await app.register(collectionsRoutes, routeOpts);
618
- // MCP endpoints are only available in local/dev mode — not in production.
619
- // The configuration endpoints moved to the tRPC `mcpConfig` router; the
620
- // `/mcp` proxy below is a bare MCP over-HTTP transport and stays on REST.
621
- if (!isProduction) {
1335
+ await app.register(appRunsRoutes);
1336
+ await app.register(runSpansRoutes);
1337
+ await app.register(applicationsRoutes, routeOpts);
1338
+ await app.register(publicAppRoutes, { appSessionSigningKey });
1339
+ await app.register(publicSharedWorkflowRoutes);
1340
+ await app.register(jsScriptsRoutes, routeOpts);
1341
+ await app.register(timelineAnimationRoutes, routeOpts);
1342
+ await app.register(timelineAudioBakeRoutes, routeOpts);
1343
+ await app.register(timelineIsolateSubjectRoutes, routeOpts);
1344
+ await app.register(timelineTrackObjectRoutes, routeOpts);
1345
+ await app.register(storyboardsRoutes, routeOpts);
1346
+ await app.register(timelinesRoutes, routeOpts);
1347
+ // PDF/DOCX text extraction for script and screenplay imports. Stores
1348
+ // nothing and needs no apiOptions.
1349
+ await app.register(documentsRoutes);
1350
+ // Art. 20 export and Art. 17 erasure. The subject is always the caller:
1351
+ // no admin path, no subject id anywhere in the URL or body.
1352
+ await app.register(accountRoutes, routeOpts);
1353
+ await app.register(sandboxModulesRoutes);
1354
+ await app.register(falCreditsRoute);
1355
+ await app.register(falPricingRoute);
1356
+ await app.register(falPricingEstimateRoute);
1357
+ await app.register(kieCreditsRoute);
1358
+ await app.register(kiePricingRoute);
1359
+ await app.register(kieWebhookRoute);
1360
+ // fal callbacks authenticate with signed headers and commit to the durable
1361
+ // generation inbox before the route acknowledges them.
1362
+ await app.register(falWebhookRoute);
1363
+ // AtlasCloud callbacks authenticate with an Ed25519 signature over the raw
1364
+ // body and hand the finished prediction to the run waiting on it.
1365
+ await app.register(atlasCloudWebhookRoute);
1366
+ // Messaging-integration identity routes (`/api/integrations/:provider/*`).
1367
+ // The plugin registers nothing unless NODETOOL_INTEGRATION_TOKEN is set, so a
1368
+ // server without it answers 404 on every one of these paths.
1369
+ await app.register(createIntegrationRoutes({ signingKey: delegatedSigningKey, enforceAuth }));
1370
+ // Trigger webhook ingestion (`POST /api/webhooks/:token`). Registered here
1371
+ // because Fastify routes must exist before `app.listen`; the plugin reaches the
1372
+ // wakeup service and dispatcher started below through module accessors.
1373
+ if (triggersEnabled()) {
1374
+ await app.register(createTriggerWebhookRoute());
1375
+ }
1376
+ // The `/mcp` mount is a bare MCP over-HTTP transport (the configuration
1377
+ // endpoints moved to the tRPC `mcpConfig` router, which stays local-only). In
1378
+ // production it registers only with NODETOOL_ENABLE_MCP=1, and it binds the
1379
+ // user the auth hook resolved rather than the local single user.
1380
+ if (mcpHttpEnabled) {
622
1381
  app.all("/mcp", async (req, reply) => {
623
1382
  const protocol = httpsOptions ? "https" : "http";
624
1383
  const url = `${protocol}://${req.headers.host ?? `127.0.0.1:${port}`}${req.url}`;
@@ -629,7 +1388,7 @@ if (!isProduction) {
629
1388
  headers.append(key, item);
630
1389
  }
631
1390
  }
632
- else if (typeof value === "string") {
1391
+ else if (value != null) {
633
1392
  headers.set(key, value);
634
1393
  }
635
1394
  }
@@ -637,7 +1396,7 @@ if (!isProduction) {
637
1396
  ? undefined
638
1397
  : Buffer.isBuffer(req.body)
639
1398
  ? req.body
640
- : typeof req.body === "string"
1399
+ : isString(req.body)
641
1400
  ? req.body
642
1401
  : req.body == null
643
1402
  ? Buffer.alloc(0)
@@ -645,10 +1404,30 @@ if (!isProduction) {
645
1404
  const request = new Request(url, {
646
1405
  method: req.method,
647
1406
  headers,
648
- body: requestBody,
649
- duplex: "half"
1407
+ body: Buffer.isBuffer(requestBody)
1408
+ ? new Uint8Array(requestBody)
1409
+ : requestBody,
1410
+ // `duplex` is required by Node's fetch when streaming bodies but is
1411
+ // missing from some `@types/node`/`undici-types` versions of RequestInit.
1412
+ ...{ duplex: "half" }
1413
+ });
1414
+ // The scope is the user the auth hook resolved for this request. With no
1415
+ // user, no scope is passed and mcp-server.ts answers 401 at initialize
1416
+ // instead of handing out an anonymous full-access session.
1417
+ const response = await handleMcpHttpRequest(request, {
1418
+ metadataRoots,
1419
+ registry,
1420
+ examplesDir: apiOptions.examplesDir,
1421
+ frontendRendererRegistry,
1422
+ allowLocalFilePaths: !isProduction && isLoopbackAddress(req.ip),
1423
+ agentToolsScope: req.userId
1424
+ ? {
1425
+ userId: req.userId,
1426
+ source: isProduction ? "http-session" : "local-dev-http"
1427
+ }
1428
+ : undefined,
1429
+ unauthorizedChallenge: mcpBearerChallenge()
650
1430
  });
651
- const response = await handleMcpHttpRequest(request, { metadataRoots });
652
1431
  if (!response) {
653
1432
  reply.status(404).send({ error: "Not found" });
654
1433
  return;
@@ -661,7 +1440,9 @@ if (!isProduction) {
661
1440
  reply.send(payload);
662
1441
  });
663
1442
  }
664
- await app.register(agentSocketRoute);
1443
+ else {
1444
+ log.info(`MCP over HTTP (/mcp) disabled in production; set ${MCP_ENABLE_FLAG}=1 to enable`);
1445
+ }
665
1446
  log.info(`Routes registered [${startupMs()}]`);
666
1447
  if (hasStaticApp && staticFolder) {
667
1448
  log.info("Serving static app", { root: staticFolder });
@@ -676,6 +1457,20 @@ if (hasStaticApp && staticFolder) {
676
1457
  return reply.sendFile("index.html");
677
1458
  });
678
1459
  }
1460
+ // Download the built Chrome extension as a zip, for the install helper UI.
1461
+ app.get("/api/extension/download", async (_req, reply) => {
1462
+ try {
1463
+ const zip = await zipExtensionDist();
1464
+ return reply
1465
+ .header("Content-Type", "application/zip")
1466
+ .header("Content-Disposition", 'attachment; filename="nodetool-chrome-extension.zip"')
1467
+ .send(zip);
1468
+ }
1469
+ catch (err) {
1470
+ log.warn(`Extension download failed: ${err instanceof Error ? err.message : String(err)}`);
1471
+ return reply.status(404).send({ detail: "Extension build not found" });
1472
+ }
1473
+ });
679
1474
  // Log all 404s so we can identify missing routes
680
1475
  app.setNotFoundHandler((req, reply) => {
681
1476
  const pathname = req.url.split("?")[0] ?? "/";
@@ -685,8 +1480,14 @@ app.setNotFoundHandler((req, reply) => {
685
1480
  !pathname.startsWith("/health") &&
686
1481
  !pathname.startsWith("/ws") &&
687
1482
  !pathname.startsWith("/v1") &&
688
- !pathname.startsWith("/oauth") &&
1483
+ !pathname.startsWith("/.well-known/") &&
1484
+ // /oauth/consent is the one /oauth path that IS the SPA — the MCP OAuth
1485
+ // consent page (docs/mcp-oauth-design.md). Every other /oauth/* path is
1486
+ // an AS API route (authorize/token/register/revoke), and unmatched
1487
+ // requests there must 404 rather than get served index.html.
1488
+ (pathname === "/oauth/consent" || !pathname.startsWith("/oauth")) &&
689
1489
  !pathname.startsWith("/trpc") &&
1490
+ !pathname.startsWith("/mcp") &&
690
1491
  !pathname.includes(".")) {
691
1492
  return reply.sendFile("index.html");
692
1493
  }
@@ -730,18 +1531,112 @@ app.listen({ port, host }, (err) => {
730
1531
  log.info(`WebSocket endpoint: ${tlsEnabled ? "wss" : "ws"}://${host}:${port}/ws`);
731
1532
  });
732
1533
  // ---------------------------------------------------------------------------
1534
+ // Triggers — durable wakeup service, dispatcher, and ingestion adapters
1535
+ // ---------------------------------------------------------------------------
1536
+ //
1537
+ // Starting the dispatcher also drains the boot backlog: every unprocessed
1538
+ // `trigger_inputs` row a previous process life stored is dispatched now. Set
1539
+ // NODETOOL_DISABLE_TRIGGERS=1 to opt this process out.
1540
+ const triggerServices = startTriggerServices({ registry });
1541
+ // ---------------------------------------------------------------------------
1542
+ // Worker cost guard — reconcile orphaned GPU pods on boot, then reap on a loop
1543
+ // ---------------------------------------------------------------------------
1544
+ const WORKER_REAPER_INTERVAL_MS = 60_000;
1545
+ workerManager
1546
+ .reconcile()
1547
+ .then((summary) => {
1548
+ if (summary.liveCount > 0 || summary.orphans.length > 0) {
1549
+ log.info(`Worker reconcile: ${summary.liveCount} live, ` +
1550
+ `${summary.orphans.length} orphaned, ~$${summary.estimatedCostUsd.toFixed(2)}`);
1551
+ }
1552
+ })
1553
+ .catch((reconcileErr) => {
1554
+ log.warn("Worker reconcile failed at startup", reconcileErr instanceof Error
1555
+ ? reconcileErr
1556
+ : new Error(String(reconcileErr)));
1557
+ });
1558
+ const stopReaper = startReaper({
1559
+ manager: workerManager,
1560
+ getProfile: (name) => getWorkerProfile(name),
1561
+ now: () => Date.now()
1562
+ }, WORKER_REAPER_INTERVAL_MS);
1563
+ const generationRecoveryWorker = createGenerationRecoveryWorker();
1564
+ const generationRecoveryTimer = setInterval(() => {
1565
+ void generationRecoveryWorker.runOnce().catch((error) => {
1566
+ log.warn("Durable generation recovery failed", error instanceof Error ? error : new Error(String(error)));
1567
+ });
1568
+ }, 30_000);
1569
+ generationRecoveryTimer.unref?.();
1570
+ void generationRecoveryWorker.runOnce().catch((error) => {
1571
+ log.warn("Durable generation recovery failed at startup", error instanceof Error ? error : new Error(String(error)));
1572
+ });
1573
+ // ---------------------------------------------------------------------------
733
1574
  // Graceful shutdown — ensure child processes (Python worker, etc.) are killed
734
1575
  // ---------------------------------------------------------------------------
1576
+ /**
1577
+ * How long SIGTERM waits for aborted turns and runs to settle. Under Fly's
1578
+ * 300 s cap between the signal and the kill; the drain the deploy script runs
1579
+ * first is what actually waits for a long turn, so this is the fallback for a
1580
+ * restart nobody drained.
1581
+ */
1582
+ function shutdownGraceMs() {
1583
+ const raw = process.env["NODETOOL_SHUTDOWN_GRACE_MS"];
1584
+ if (!raw)
1585
+ return 240_000;
1586
+ const parsed = Number.parseInt(raw, 10);
1587
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : 240_000;
1588
+ }
1589
+ let shuttingDown = false;
735
1590
  async function shutdown(signal) {
1591
+ // The wait below can last minutes; a second signal in that window must not
1592
+ // run the teardown a second time on top of the first.
1593
+ if (shuttingDown) {
1594
+ log.info(`${signal} received again — shutdown already in progress`);
1595
+ return;
1596
+ }
1597
+ shuttingDown = true;
736
1598
  log.info(`${signal} received — shutting down`);
1599
+ // Take no new work first, so nothing starts during the wait below.
1600
+ startDrain();
1601
+ const abortedTurns = chatTurnRegistry.abortAll("shutdown");
1602
+ const cancelledJobs = jobRunRegistry.cancelAll();
1603
+ if (abortedTurns > 0 || cancelledJobs > 0) {
1604
+ log.info("Aborting in-flight work", {
1605
+ turns: abortedTurns,
1606
+ jobs: cancelledJobs
1607
+ });
1608
+ }
1609
+ const grace = shutdownGraceMs();
1610
+ const [turnsDrained, jobsDrained] = await Promise.all([
1611
+ chatTurnRegistry.drained(grace),
1612
+ jobRunRegistry.drained(grace)
1613
+ ]);
1614
+ if (!turnsDrained || !jobsDrained) {
1615
+ // The turn's own `finally` writes the stand-in tool-result rows, so a
1616
+ // turn still running here is one whose transcript stays incomplete.
1617
+ log.warn("Shutdown grace expired with work still running", {
1618
+ graceMs: grace,
1619
+ turns: chatTurnRegistry.runningCount(),
1620
+ jobs: jobRunRegistry.runningCount()
1621
+ });
1622
+ }
1623
+ // Spans are batched, so without this every span the last turns produced dies
1624
+ // with the process.
1625
+ await shutdownTelemetry();
737
1626
  log.info("Closing Python bridge");
1627
+ stopReaper();
1628
+ clearInterval(generationRecoveryTimer);
1629
+ stopJobCancelPoller();
1630
+ stopGenerationReconcileWorker?.();
1631
+ stopErrorTraceMaintenance?.();
738
1632
  try {
739
- getAgentRuntime().closeAllSessions();
1633
+ await triggerServices.stop();
740
1634
  }
741
- catch {
742
- // best-effort cleanup
1635
+ catch (err) {
1636
+ log.warn("Trigger services failed to stop cleanly", err instanceof Error ? err : new Error(String(err)));
743
1637
  }
744
- pythonBridge.close();
1638
+ localBridge.close();
1639
+ workerBridge?.close();
745
1640
  try {
746
1641
  await app.close();
747
1642
  }
@@ -752,12 +1647,20 @@ async function shutdown(signal) {
752
1647
  }
753
1648
  process.on("SIGINT", () => void shutdown("SIGINT"));
754
1649
  process.on("SIGTERM", () => void shutdown("SIGTERM"));
1650
+ // The deploy's drain signal: stop taking work and let the turns in flight
1651
+ // finish, without a deadline. `scripts/fly-rolling-deploy.sh` sends it, waits
1652
+ // for /health to report no turns and no jobs, and only then replaces the
1653
+ // machine — which is what keeps a 30-minute turn off Fly's 300 s SIGTERM cap.
1654
+ process.on("SIGUSR2", () => {
1655
+ log.info("SIGUSR2 received — draining");
1656
+ startDrain();
1657
+ });
755
1658
  // On Windows, Ctrl+C in some terminals fires SIGBREAK instead of SIGINT
756
1659
  if (process.platform === "win32") {
757
1660
  process.on("SIGBREAK", () => void shutdown("SIGBREAK"));
758
1661
  }
759
- // Start Python bridge eagerly if Python is installed.
760
- if (pythonBridge.hasPython()) {
1662
+ // Start Python bridge eagerly if a worker is available.
1663
+ if (pythonBridge.isAvailable()) {
761
1664
  log.info(`Starting Python bridge eagerly [${startupMs()}]`);
762
1665
  pythonBridge
763
1666
  .ensureConnected()
@@ -765,27 +1668,8 @@ if (pythonBridge.hasPython()) {
765
1668
  pythonBridgeReady = true;
766
1669
  log.info(`Python bridge eager start completed [${startupMs()}]`);
767
1670
  const meta = pythonBridge.getNodeMetadata();
768
- let bridgeOnly = 0;
769
- for (const nodeMeta of meta) {
770
- if (!nodeMeta.node_type)
771
- continue;
772
- if (registry.getMetadata(nodeMeta.node_type))
773
- continue;
774
- bridgeOnly++;
775
- registry.loadMetadata(nodeMeta.node_type, {
776
- ...nodeMeta,
777
- namespace: nodeMeta.node_type.split(".").slice(0, -1).join("."),
778
- layout: "default",
779
- recommended_models: [],
780
- basic_fields: [],
781
- required_settings: nodeMeta.required_settings ?? [],
782
- is_dynamic: nodeMeta.is_dynamic ?? false,
783
- is_streaming_output: nodeMeta.is_streaming_output ?? false,
784
- expose_as_tool: false,
785
- supports_dynamic_outputs: false
786
- });
787
- }
788
- log.info(`Python bridge connected [${startupMs()}] — ${meta.length} Python nodes (${bridgeOnly} bridge-only, ${meta.length - bridgeOnly} from JSON)`);
1671
+ const mergeResult = mergePythonBridgeMetadata(registry, meta);
1672
+ log.info(`Python bridge connected [${startupMs()}] — ${mergeResult.total} Python nodes (${mergeResult.bridgeOnly} bridge-only, ${mergeResult.alreadyKnown} already registered)`);
789
1673
  pythonBridge
790
1674
  .getWorkerStatus?.()
791
1675
  ?.then((status) => {
@@ -812,8 +1696,9 @@ if (pythonBridge.hasPython()) {
812
1696
  });
813
1697
  }
814
1698
  else {
815
- log.info(isProduction
816
- ? "Python bridge disabled in production — Python nodes will not be available"
1699
+ const pythonAllowedInProduction = process.env["NODETOOL_ALLOW_PYTHON_BRIDGE_IN_PRODUCTION"] === "1";
1700
+ log.info(isProduction && !pythonAllowedInProduction
1701
+ ? "Python bridge disabled in production — Python nodes will not be available; set NODETOOL_ALLOW_PYTHON_BRIDGE_IN_PRODUCTION=1 to enable (the image ships no Python worker: install nodetool-core and point NODETOOL_PYTHON at that interpreter)"
817
1702
  : "Python not found — Python nodes will not be available");
818
1703
  }
819
1704
  //# sourceMappingURL=server.js.map