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