@nodetool-ai/agents 0.7.0-rc.23 → 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 +35 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/analysis/audio-dsp.d.ts +223 -0
- package/dist/analysis/audio-dsp.d.ts.map +1 -0
- package/dist/analysis/audio-dsp.js +642 -0
- package/dist/analysis/audio-dsp.js.map +1 -0
- package/dist/analysis/media-decode.d.ts +85 -0
- package/dist/analysis/media-decode.d.ts.map +1 -0
- package/dist/analysis/media-decode.js +199 -0
- package/dist/analysis/media-decode.js.map +1 -0
- package/dist/analysis/mediabunny-runtime.d.ts +15 -0
- package/dist/analysis/mediabunny-runtime.d.ts.map +1 -0
- package/dist/analysis/mediabunny-runtime.js +30 -0
- package/dist/analysis/mediabunny-runtime.js.map +1 -0
- package/dist/analysis/video-frames.d.ts +120 -0
- package/dist/analysis/video-frames.d.ts.map +1 -0
- package/dist/analysis/video-frames.js +288 -0
- package/dist/analysis/video-frames.js.map +1 -0
- package/dist/apify/assets.d.ts +80 -0
- package/dist/apify/assets.d.ts.map +1 -0
- package/dist/apify/assets.js +192 -0
- package/dist/apify/assets.js.map +1 -0
- package/dist/apify/catalog.d.ts +44 -0
- package/dist/apify/catalog.d.ts.map +1 -0
- package/dist/apify/catalog.js +108 -0
- package/dist/apify/catalog.js.map +1 -0
- package/dist/apify/client.d.ts +137 -0
- package/dist/apify/client.d.ts.map +1 -0
- package/dist/apify/client.js +346 -0
- package/dist/apify/client.js.map +1 -0
- package/dist/apify/errors.d.ts +85 -0
- package/dist/apify/errors.d.ts.map +1 -0
- package/dist/apify/errors.js +90 -0
- package/dist/apify/errors.js.map +1 -0
- package/dist/apify/normalize.d.ts +131 -0
- package/dist/apify/normalize.d.ts.map +1 -0
- package/dist/apify/normalize.js +202 -0
- package/dist/apify/normalize.js.map +1 -0
- package/dist/apify/policy.d.ts +136 -0
- package/dist/apify/policy.d.ts.map +1 -0
- package/dist/apify/policy.js +223 -0
- package/dist/apify/policy.js.map +1 -0
- package/dist/apify/run.d.ts +86 -0
- package/dist/apify/run.d.ts.map +1 -0
- package/dist/apify/run.js +158 -0
- package/dist/apify/run.js.map +1 -0
- package/dist/app-build/author.d.ts +67 -0
- package/dist/app-build/author.d.ts.map +1 -0
- package/dist/app-build/author.js +231 -0
- package/dist/app-build/author.js.map +1 -0
- package/dist/app-build/bridge.d.ts +130 -0
- package/dist/app-build/bridge.d.ts.map +1 -0
- package/dist/app-build/bridge.js +647 -0
- package/dist/app-build/bridge.js.map +1 -0
- package/dist/app-build/build-service.d.ts +66 -0
- package/dist/app-build/build-service.d.ts.map +1 -0
- package/dist/app-build/build-service.js +249 -0
- package/dist/app-build/build-service.js.map +1 -0
- package/dist/app-build/build.d.ts +115 -0
- package/dist/app-build/build.d.ts.map +1 -0
- package/dist/app-build/build.js +901 -0
- package/dist/app-build/build.js.map +1 -0
- package/dist/app-build/interactions.d.ts +20 -0
- package/dist/app-build/interactions.d.ts.map +1 -0
- package/dist/app-build/interactions.js +121 -0
- package/dist/app-build/interactions.js.map +1 -0
- package/dist/app-build/judge.d.ts +113 -0
- package/dist/app-build/judge.d.ts.map +1 -0
- package/dist/app-build/judge.js +318 -0
- package/dist/app-build/judge.js.map +1 -0
- package/dist/app-build/markdown.d.ts +10 -0
- package/dist/app-build/markdown.d.ts.map +1 -0
- package/dist/app-build/markdown.js +65 -0
- package/dist/app-build/markdown.js.map +1 -0
- package/dist/app-build/spec.d.ts +228 -0
- package/dist/app-build/spec.d.ts.map +1 -0
- package/dist/app-build/spec.js +651 -0
- package/dist/app-build/spec.js.map +1 -0
- package/dist/app-build/tool-loop.d.ts +84 -0
- package/dist/app-build/tool-loop.d.ts.map +1 -0
- package/dist/app-build/tool-loop.js +113 -0
- package/dist/app-build/tool-loop.js.map +1 -0
- package/dist/app-build/types.d.ts +215 -0
- package/dist/app-build/types.d.ts.map +1 -0
- package/dist/app-build/types.js +28 -0
- package/dist/app-build/types.js.map +1 -0
- package/dist/author-graph.d.ts +91 -0
- package/dist/author-graph.d.ts.map +1 -0
- package/dist/author-graph.js +319 -0
- package/dist/author-graph.js.map +1 -0
- package/dist/background-subtasks.d.ts +79 -0
- package/dist/background-subtasks.d.ts.map +1 -0
- package/dist/background-subtasks.js +139 -0
- package/dist/background-subtasks.js.map +1 -0
- package/dist/blender-gate.d.ts +21 -0
- package/dist/blender-gate.d.ts.map +1 -0
- package/dist/blender-gate.js +27 -0
- package/dist/blender-gate.js.map +1 -0
- package/dist/browser-gate.d.ts +28 -0
- package/dist/browser-gate.d.ts.map +1 -0
- package/dist/browser-gate.js +34 -0
- package/dist/browser-gate.js.map +1 -0
- package/dist/capabilities/adapters.d.ts +36 -0
- package/dist/capabilities/adapters.d.ts.map +1 -0
- package/dist/capabilities/adapters.js +44 -0
- package/dist/capabilities/adapters.js.map +1 -0
- package/dist/capabilities/agents.d.ts +78 -0
- package/dist/capabilities/agents.d.ts.map +1 -0
- package/dist/capabilities/agents.js +366 -0
- package/dist/capabilities/agents.js.map +1 -0
- package/dist/capabilities/agents.specs.d.ts +27 -0
- package/dist/capabilities/agents.specs.d.ts.map +1 -0
- package/dist/capabilities/agents.specs.js +243 -0
- package/dist/capabilities/agents.specs.js.map +1 -0
- package/dist/capabilities/analysis.d.ts +93 -0
- package/dist/capabilities/analysis.d.ts.map +1 -0
- package/dist/capabilities/analysis.js +812 -0
- package/dist/capabilities/analysis.js.map +1 -0
- package/dist/capabilities/analysis.specs.d.ts +39 -0
- package/dist/capabilities/analysis.specs.d.ts.map +1 -0
- package/dist/capabilities/analysis.specs.js +217 -0
- package/dist/capabilities/analysis.specs.js.map +1 -0
- package/dist/capabilities/apify.d.ts +34 -0
- package/dist/capabilities/apify.d.ts.map +1 -0
- package/dist/capabilities/apify.js +449 -0
- package/dist/capabilities/apify.js.map +1 -0
- package/dist/capabilities/apify.specs.d.ts +28 -0
- package/dist/capabilities/apify.specs.d.ts.map +1 -0
- package/dist/capabilities/apify.specs.js +232 -0
- package/dist/capabilities/apify.specs.js.map +1 -0
- package/dist/capabilities/apps.d.ts +40 -0
- package/dist/capabilities/apps.d.ts.map +1 -0
- package/dist/capabilities/apps.js +413 -0
- package/dist/capabilities/apps.js.map +1 -0
- package/dist/capabilities/apps.specs.d.ts +23 -0
- package/dist/capabilities/apps.specs.d.ts.map +1 -0
- package/dist/capabilities/apps.specs.js +236 -0
- package/dist/capabilities/apps.specs.js.map +1 -0
- package/dist/capabilities/args.d.ts +65 -0
- package/dist/capabilities/args.d.ts.map +1 -0
- package/dist/capabilities/args.js +218 -0
- package/dist/capabilities/args.js.map +1 -0
- package/dist/capabilities/assets.d.ts +45 -0
- package/dist/capabilities/assets.d.ts.map +1 -0
- package/dist/capabilities/assets.js +952 -0
- package/dist/capabilities/assets.js.map +1 -0
- package/dist/capabilities/assets.specs.d.ts +54 -0
- package/dist/capabilities/assets.specs.d.ts.map +1 -0
- package/dist/capabilities/assets.specs.js +291 -0
- package/dist/capabilities/assets.specs.js.map +1 -0
- package/dist/capabilities/background-generation.d.ts +14 -0
- package/dist/capabilities/background-generation.d.ts.map +1 -0
- package/dist/capabilities/background-generation.js +29 -0
- package/dist/capabilities/background-generation.js.map +1 -0
- package/dist/capabilities/browser.d.ts +30 -0
- package/dist/capabilities/browser.d.ts.map +1 -0
- package/dist/capabilities/browser.js +250 -0
- package/dist/capabilities/browser.js.map +1 -0
- package/dist/capabilities/browser.specs.d.ts +37 -0
- package/dist/capabilities/browser.specs.d.ts.map +1 -0
- package/dist/capabilities/browser.specs.js +173 -0
- package/dist/capabilities/browser.specs.js.map +1 -0
- package/dist/capabilities/code-grading.d.ts +68 -0
- package/dist/capabilities/code-grading.d.ts.map +1 -0
- package/dist/capabilities/code-grading.js +75 -0
- package/dist/capabilities/code-grading.js.map +1 -0
- package/dist/capabilities/code.d.ts +56 -0
- package/dist/capabilities/code.d.ts.map +1 -0
- package/dist/capabilities/code.js +327 -0
- package/dist/capabilities/code.js.map +1 -0
- package/dist/capabilities/code.specs.d.ts +22 -0
- package/dist/capabilities/code.specs.d.ts.map +1 -0
- package/dist/capabilities/code.specs.js +175 -0
- package/dist/capabilities/code.specs.js.map +1 -0
- package/dist/capabilities/collections.d.ts +44 -0
- package/dist/capabilities/collections.d.ts.map +1 -0
- package/dist/capabilities/collections.js +432 -0
- package/dist/capabilities/collections.js.map +1 -0
- package/dist/capabilities/collections.specs.d.ts +25 -0
- package/dist/capabilities/collections.specs.d.ts.map +1 -0
- package/dist/capabilities/collections.specs.js +299 -0
- package/dist/capabilities/collections.specs.js.map +1 -0
- package/dist/capabilities/compositions.d.ts +57 -0
- package/dist/capabilities/compositions.d.ts.map +1 -0
- package/dist/capabilities/compositions.js +384 -0
- package/dist/capabilities/compositions.js.map +1 -0
- package/dist/capabilities/compositions.specs.d.ts +22 -0
- package/dist/capabilities/compositions.specs.d.ts.map +1 -0
- package/dist/capabilities/compositions.specs.js +126 -0
- package/dist/capabilities/compositions.specs.js.map +1 -0
- package/dist/capabilities/concurrency.d.ts +12 -0
- package/dist/capabilities/concurrency.d.ts.map +1 -0
- package/dist/capabilities/concurrency.js +30 -0
- package/dist/capabilities/concurrency.js.map +1 -0
- package/dist/capabilities/costs.d.ts +20 -0
- package/dist/capabilities/costs.d.ts.map +1 -0
- package/dist/capabilities/costs.js +40 -0
- package/dist/capabilities/costs.js.map +1 -0
- package/dist/capabilities/costs.specs.d.ts +12 -0
- package/dist/capabilities/costs.specs.d.ts.map +1 -0
- package/dist/capabilities/costs.specs.js +35 -0
- package/dist/capabilities/costs.specs.js.map +1 -0
- package/dist/capabilities/dispatcher.d.ts +57 -0
- package/dist/capabilities/dispatcher.d.ts.map +1 -0
- package/dist/capabilities/dispatcher.js +117 -0
- package/dist/capabilities/dispatcher.js.map +1 -0
- package/dist/capabilities/documents.d.ts +25 -0
- package/dist/capabilities/documents.d.ts.map +1 -0
- package/dist/capabilities/documents.js +278 -0
- package/dist/capabilities/documents.js.map +1 -0
- package/dist/capabilities/documents.specs.d.ts +18 -0
- package/dist/capabilities/documents.specs.d.ts.map +1 -0
- package/dist/capabilities/documents.specs.js +201 -0
- package/dist/capabilities/documents.specs.js.map +1 -0
- package/dist/capabilities/email.d.ts +23 -0
- package/dist/capabilities/email.d.ts.map +1 -0
- package/dist/capabilities/email.js +226 -0
- package/dist/capabilities/email.js.map +1 -0
- package/dist/capabilities/email.specs.d.ts +16 -0
- package/dist/capabilities/email.specs.d.ts.map +1 -0
- package/dist/capabilities/email.specs.js +102 -0
- package/dist/capabilities/email.specs.js.map +1 -0
- package/dist/capabilities/entities.d.ts +52 -0
- package/dist/capabilities/entities.d.ts.map +1 -0
- package/dist/capabilities/entities.js +478 -0
- package/dist/capabilities/entities.js.map +1 -0
- package/dist/capabilities/entities.specs.d.ts +25 -0
- package/dist/capabilities/entities.specs.d.ts.map +1 -0
- package/dist/capabilities/entities.specs.js +247 -0
- package/dist/capabilities/entities.specs.js.map +1 -0
- package/dist/capabilities/example-timelines.d.ts +3 -0
- package/dist/capabilities/example-timelines.d.ts.map +1 -0
- package/dist/capabilities/example-timelines.js +191 -0
- package/dist/capabilities/example-timelines.js.map +1 -0
- package/dist/capabilities/example-timelines.specs.d.ts +5 -0
- package/dist/capabilities/example-timelines.specs.d.ts.map +1 -0
- package/dist/capabilities/example-timelines.specs.js +53 -0
- package/dist/capabilities/example-timelines.specs.js.map +1 -0
- package/dist/capabilities/files.d.ts +53 -0
- package/dist/capabilities/files.d.ts.map +1 -0
- package/dist/capabilities/files.js +1172 -0
- package/dist/capabilities/files.js.map +1 -0
- package/dist/capabilities/files.specs.d.ts +28 -0
- package/dist/capabilities/files.specs.d.ts.map +1 -0
- package/dist/capabilities/files.specs.js +259 -0
- package/dist/capabilities/files.specs.js.map +1 -0
- package/dist/capabilities/flow.d.ts +93 -0
- package/dist/capabilities/flow.d.ts.map +1 -0
- package/dist/capabilities/flow.js +285 -0
- package/dist/capabilities/flow.js.map +1 -0
- package/dist/capabilities/flow.specs.d.ts +25 -0
- package/dist/capabilities/flow.specs.d.ts.map +1 -0
- package/dist/capabilities/flow.specs.js +114 -0
- package/dist/capabilities/flow.specs.js.map +1 -0
- package/dist/capabilities/game-asset-source.d.ts +31 -0
- package/dist/capabilities/game-asset-source.d.ts.map +1 -0
- package/dist/capabilities/game-asset-source.js +81 -0
- package/dist/capabilities/game-asset-source.js.map +1 -0
- package/dist/capabilities/game.d.ts +12 -0
- package/dist/capabilities/game.d.ts.map +1 -0
- package/dist/capabilities/game.js +1262 -0
- package/dist/capabilities/game.js.map +1 -0
- package/dist/capabilities/game.specs.d.ts +3 -0
- package/dist/capabilities/game.specs.d.ts.map +1 -0
- package/dist/capabilities/game.specs.js +96 -0
- package/dist/capabilities/game.specs.js.map +1 -0
- package/dist/capabilities/gate-from-context.d.ts +39 -0
- package/dist/capabilities/gate-from-context.d.ts.map +1 -0
- package/dist/capabilities/gate-from-context.js +97 -0
- package/dist/capabilities/gate-from-context.js.map +1 -0
- package/dist/capabilities/gate-tools.d.ts +18 -0
- package/dist/capabilities/gate-tools.d.ts.map +1 -0
- package/dist/capabilities/gate-tools.js +66 -0
- package/dist/capabilities/gate-tools.js.map +1 -0
- package/dist/capabilities/generations.d.ts +79 -0
- package/dist/capabilities/generations.d.ts.map +1 -0
- package/dist/capabilities/generations.js +382 -0
- package/dist/capabilities/generations.js.map +1 -0
- package/dist/capabilities/generations.specs.d.ts +62 -0
- package/dist/capabilities/generations.specs.d.ts.map +1 -0
- package/dist/capabilities/generations.specs.js +230 -0
- package/dist/capabilities/generations.specs.js.map +1 -0
- package/dist/capabilities/google.d.ts +50 -0
- package/dist/capabilities/google.d.ts.map +1 -0
- package/dist/capabilities/google.js +246 -0
- package/dist/capabilities/google.js.map +1 -0
- package/dist/capabilities/google.specs.d.ts +52 -0
- package/dist/capabilities/google.specs.d.ts.map +1 -0
- package/dist/capabilities/google.specs.js +391 -0
- package/dist/capabilities/google.specs.js.map +1 -0
- package/dist/capabilities/html-text.d.ts +28 -0
- package/dist/capabilities/html-text.d.ts.map +1 -0
- package/dist/capabilities/html-text.js +76 -0
- package/dist/capabilities/html-text.js.map +1 -0
- package/dist/capabilities/index.d.ts +19 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +14 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/invoke.d.ts +89 -0
- package/dist/capabilities/invoke.d.ts.map +1 -0
- package/dist/capabilities/invoke.js +214 -0
- package/dist/capabilities/invoke.js.map +1 -0
- package/dist/capabilities/jobs.d.ts +32 -0
- package/dist/capabilities/jobs.d.ts.map +1 -0
- package/dist/capabilities/jobs.js +123 -0
- package/dist/capabilities/jobs.js.map +1 -0
- package/dist/capabilities/jobs.specs.d.ts +20 -0
- package/dist/capabilities/jobs.specs.d.ts.map +1 -0
- package/dist/capabilities/jobs.specs.js +103 -0
- package/dist/capabilities/jobs.specs.js.map +1 -0
- package/dist/capabilities/js-scripts.d.ts +90 -0
- package/dist/capabilities/js-scripts.d.ts.map +1 -0
- package/dist/capabilities/js-scripts.js +700 -0
- package/dist/capabilities/js-scripts.js.map +1 -0
- package/dist/capabilities/js-scripts.specs.d.ts +50 -0
- package/dist/capabilities/js-scripts.specs.d.ts.map +1 -0
- package/dist/capabilities/js-scripts.specs.js +344 -0
- package/dist/capabilities/js-scripts.specs.js.map +1 -0
- package/dist/capabilities/lazy-tool.d.ts +42 -0
- package/dist/capabilities/lazy-tool.d.ts.map +1 -0
- package/dist/capabilities/lazy-tool.js +88 -0
- package/dist/capabilities/lazy-tool.js.map +1 -0
- package/dist/capabilities/local-browser.d.ts +24 -0
- package/dist/capabilities/local-browser.d.ts.map +1 -0
- package/dist/capabilities/local-browser.js +105 -0
- package/dist/capabilities/local-browser.js.map +1 -0
- package/dist/capabilities/media.d.ts +121 -0
- package/dist/capabilities/media.d.ts.map +1 -0
- package/dist/capabilities/media.js +2217 -0
- package/dist/capabilities/media.js.map +1 -0
- package/dist/capabilities/media.specs.d.ts +65 -0
- package/dist/capabilities/media.specs.d.ts.map +1 -0
- package/dist/capabilities/media.specs.js +779 -0
- package/dist/capabilities/media.specs.js.map +1 -0
- package/dist/capabilities/memory.d.ts +31 -0
- package/dist/capabilities/memory.d.ts.map +1 -0
- package/dist/capabilities/memory.js +403 -0
- package/dist/capabilities/memory.js.map +1 -0
- package/dist/capabilities/memory.specs.d.ts +55 -0
- package/dist/capabilities/memory.specs.d.ts.map +1 -0
- package/dist/capabilities/memory.specs.js +208 -0
- package/dist/capabilities/memory.specs.js.map +1 -0
- package/dist/capabilities/model3d.d.ts +29 -0
- package/dist/capabilities/model3d.d.ts.map +1 -0
- package/dist/capabilities/model3d.js +482 -0
- package/dist/capabilities/model3d.js.map +1 -0
- package/dist/capabilities/model3d.specs.d.ts +28 -0
- package/dist/capabilities/model3d.specs.d.ts.map +1 -0
- package/dist/capabilities/model3d.specs.js +305 -0
- package/dist/capabilities/model3d.specs.js.map +1 -0
- package/dist/capabilities/models.d.ts +138 -0
- package/dist/capabilities/models.d.ts.map +1 -0
- package/dist/capabilities/models.js +803 -0
- package/dist/capabilities/models.js.map +1 -0
- package/dist/capabilities/models.specs.d.ts +21 -0
- package/dist/capabilities/models.specs.d.ts.map +1 -0
- package/dist/capabilities/models.specs.js +141 -0
- package/dist/capabilities/models.specs.js.map +1 -0
- package/dist/capabilities/nodes.d.ts +26 -0
- package/dist/capabilities/nodes.d.ts.map +1 -0
- package/dist/capabilities/nodes.js +266 -0
- package/dist/capabilities/nodes.js.map +1 -0
- package/dist/capabilities/nodes.specs.d.ts +20 -0
- package/dist/capabilities/nodes.specs.d.ts.map +1 -0
- package/dist/capabilities/nodes.specs.js +106 -0
- package/dist/capabilities/nodes.specs.js.map +1 -0
- package/dist/capabilities/packs.d.ts +69 -0
- package/dist/capabilities/packs.d.ts.map +1 -0
- package/dist/capabilities/packs.js +246 -0
- package/dist/capabilities/packs.js.map +1 -0
- package/dist/capabilities/packs.specs.d.ts +27 -0
- package/dist/capabilities/packs.specs.d.ts.map +1 -0
- package/dist/capabilities/packs.specs.js +64 -0
- package/dist/capabilities/packs.specs.js.map +1 -0
- package/dist/capabilities/project-scope.d.ts +12 -0
- package/dist/capabilities/project-scope.d.ts.map +1 -0
- package/dist/capabilities/project-scope.js +19 -0
- package/dist/capabilities/project-scope.js.map +1 -0
- package/dist/capabilities/projects.d.ts +23 -0
- package/dist/capabilities/projects.d.ts.map +1 -0
- package/dist/capabilities/projects.js +206 -0
- package/dist/capabilities/projects.js.map +1 -0
- package/dist/capabilities/projects.specs.d.ts +23 -0
- package/dist/capabilities/projects.specs.d.ts.map +1 -0
- package/dist/capabilities/projects.specs.js +118 -0
- package/dist/capabilities/projects.specs.js.map +1 -0
- package/dist/capabilities/registry.d.ts +109 -0
- package/dist/capabilities/registry.d.ts.map +1 -0
- package/dist/capabilities/registry.js +445 -0
- package/dist/capabilities/registry.js.map +1 -0
- package/dist/capabilities/script-link.d.ts +22 -0
- package/dist/capabilities/script-link.d.ts.map +1 -0
- package/dist/capabilities/script-link.js +35 -0
- package/dist/capabilities/script-link.js.map +1 -0
- package/dist/capabilities/scripts.d.ts +43 -0
- package/dist/capabilities/scripts.d.ts.map +1 -0
- package/dist/capabilities/scripts.js +1789 -0
- package/dist/capabilities/scripts.js.map +1 -0
- package/dist/capabilities/scripts.specs.d.ts +40 -0
- package/dist/capabilities/scripts.specs.d.ts.map +1 -0
- package/dist/capabilities/scripts.specs.js +319 -0
- package/dist/capabilities/scripts.specs.js.map +1 -0
- package/dist/capabilities/serpapi.d.ts +42 -0
- package/dist/capabilities/serpapi.d.ts.map +1 -0
- package/dist/capabilities/serpapi.js +262 -0
- package/dist/capabilities/serpapi.js.map +1 -0
- package/dist/capabilities/serpapi.specs.d.ts +24 -0
- package/dist/capabilities/serpapi.specs.d.ts.map +1 -0
- package/dist/capabilities/serpapi.specs.js +174 -0
- package/dist/capabilities/serpapi.specs.js.map +1 -0
- package/dist/capabilities/settings.d.ts +48 -0
- package/dist/capabilities/settings.d.ts.map +1 -0
- package/dist/capabilities/settings.js +226 -0
- package/dist/capabilities/settings.js.map +1 -0
- package/dist/capabilities/settings.specs.d.ts +42 -0
- package/dist/capabilities/settings.specs.d.ts.map +1 -0
- package/dist/capabilities/settings.specs.js +126 -0
- package/dist/capabilities/settings.specs.js.map +1 -0
- package/dist/capabilities/shared.d.ts +35 -0
- package/dist/capabilities/shared.d.ts.map +1 -0
- package/dist/capabilities/shared.js +165 -0
- package/dist/capabilities/shared.js.map +1 -0
- package/dist/capabilities/shared.specs.d.ts +19 -0
- package/dist/capabilities/shared.specs.d.ts.map +1 -0
- package/dist/capabilities/shared.specs.js +134 -0
- package/dist/capabilities/shared.specs.js.map +1 -0
- package/dist/capabilities/sketches.d.ts +52 -0
- package/dist/capabilities/sketches.d.ts.map +1 -0
- package/dist/capabilities/sketches.js +1163 -0
- package/dist/capabilities/sketches.js.map +1 -0
- package/dist/capabilities/sketches.specs.d.ts +44 -0
- package/dist/capabilities/sketches.specs.d.ts.map +1 -0
- package/dist/capabilities/sketches.specs.js +392 -0
- package/dist/capabilities/sketches.specs.js.map +1 -0
- package/dist/capabilities/skills.d.ts +28 -0
- package/dist/capabilities/skills.d.ts.map +1 -0
- package/dist/capabilities/skills.js +269 -0
- package/dist/capabilities/skills.js.map +1 -0
- package/dist/capabilities/skills.specs.d.ts +17 -0
- package/dist/capabilities/skills.specs.d.ts.map +1 -0
- package/dist/capabilities/skills.specs.js +108 -0
- package/dist/capabilities/skills.specs.js.map +1 -0
- package/dist/capabilities/storyboards.d.ts +68 -0
- package/dist/capabilities/storyboards.d.ts.map +1 -0
- package/dist/capabilities/storyboards.js +2246 -0
- package/dist/capabilities/storyboards.js.map +1 -0
- package/dist/capabilities/storyboards.specs.d.ts +42 -0
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -0
- package/dist/capabilities/storyboards.specs.js +418 -0
- package/dist/capabilities/storyboards.specs.js.map +1 -0
- package/dist/capabilities/threads.d.ts +25 -0
- package/dist/capabilities/threads.d.ts.map +1 -0
- package/dist/capabilities/threads.js +240 -0
- package/dist/capabilities/threads.js.map +1 -0
- package/dist/capabilities/threads.specs.d.ts +26 -0
- package/dist/capabilities/threads.specs.d.ts.map +1 -0
- package/dist/capabilities/threads.specs.js +108 -0
- package/dist/capabilities/threads.specs.js.map +1 -0
- package/dist/capabilities/timeline-audio-bake.d.ts +114 -0
- package/dist/capabilities/timeline-audio-bake.d.ts.map +1 -0
- package/dist/capabilities/timeline-audio-bake.js +188 -0
- package/dist/capabilities/timeline-audio-bake.js.map +1 -0
- package/dist/capabilities/timeline-bake.d.ts +21 -0
- package/dist/capabilities/timeline-bake.d.ts.map +1 -0
- package/dist/capabilities/timeline-bake.js +118 -0
- package/dist/capabilities/timeline-bake.js.map +1 -0
- package/dist/capabilities/timeline-isolate-subject.d.ts +157 -0
- package/dist/capabilities/timeline-isolate-subject.d.ts.map +1 -0
- package/dist/capabilities/timeline-isolate-subject.js +293 -0
- package/dist/capabilities/timeline-isolate-subject.js.map +1 -0
- package/dist/capabilities/timeline-native-media.d.ts +4 -0
- package/dist/capabilities/timeline-native-media.d.ts.map +1 -0
- package/dist/capabilities/timeline-native-media.js +920 -0
- package/dist/capabilities/timeline-native-media.js.map +1 -0
- package/dist/capabilities/timeline-track-object.d.ts +83 -0
- package/dist/capabilities/timeline-track-object.d.ts.map +1 -0
- package/dist/capabilities/timeline-track-object.js +195 -0
- package/dist/capabilities/timeline-track-object.js.map +1 -0
- package/dist/capabilities/timelines.d.ts +190 -0
- package/dist/capabilities/timelines.d.ts.map +1 -0
- package/dist/capabilities/timelines.js +2326 -0
- package/dist/capabilities/timelines.js.map +1 -0
- package/dist/capabilities/timelines.specs.d.ts +134 -0
- package/dist/capabilities/timelines.specs.d.ts.map +1 -0
- package/dist/capabilities/timelines.specs.js +1362 -0
- package/dist/capabilities/timelines.specs.js.map +1 -0
- package/dist/capabilities/types.d.ts +197 -0
- package/dist/capabilities/types.d.ts.map +1 -0
- package/dist/capabilities/types.js +19 -0
- package/dist/capabilities/types.js.map +1 -0
- package/dist/capabilities/ui.d.ts +38 -0
- package/dist/capabilities/ui.d.ts.map +1 -0
- package/dist/capabilities/ui.js +128 -0
- package/dist/capabilities/ui.js.map +1 -0
- package/dist/capabilities/ui.specs.d.ts +22 -0
- package/dist/capabilities/ui.specs.d.ts.map +1 -0
- package/dist/capabilities/ui.specs.js +59 -0
- package/dist/capabilities/ui.specs.js.map +1 -0
- package/dist/capabilities/video-production-acceptance.d.ts +27 -0
- package/dist/capabilities/video-production-acceptance.d.ts.map +1 -0
- package/dist/capabilities/video-production-acceptance.js +41 -0
- package/dist/capabilities/video-production-acceptance.js.map +1 -0
- package/dist/capabilities/video-production.d.ts +179 -0
- package/dist/capabilities/video-production.d.ts.map +1 -0
- package/dist/capabilities/video-production.js +1492 -0
- package/dist/capabilities/video-production.js.map +1 -0
- package/dist/capabilities/video-production.specs.d.ts +13 -0
- package/dist/capabilities/video-production.specs.d.ts.map +1 -0
- package/dist/capabilities/video-production.specs.js +256 -0
- package/dist/capabilities/video-production.specs.js.map +1 -0
- package/dist/capabilities/web.d.ts +86 -0
- package/dist/capabilities/web.d.ts.map +1 -0
- package/dist/capabilities/web.js +850 -0
- package/dist/capabilities/web.js.map +1 -0
- package/dist/capabilities/web.specs.d.ts +33 -0
- package/dist/capabilities/web.specs.d.ts.map +1 -0
- package/dist/capabilities/web.specs.js +286 -0
- package/dist/capabilities/web.specs.js.map +1 -0
- package/dist/capabilities/workflows.d.ts +58 -0
- package/dist/capabilities/workflows.d.ts.map +1 -0
- package/dist/capabilities/workflows.js +1530 -0
- package/dist/capabilities/workflows.js.map +1 -0
- package/dist/capabilities/workflows.specs.d.ts +63 -0
- package/dist/capabilities/workflows.specs.d.ts.map +1 -0
- package/dist/capabilities/workflows.specs.js +852 -0
- package/dist/capabilities/workflows.specs.js.map +1 -0
- package/dist/code-gen/analyze.d.ts +26 -0
- package/dist/code-gen/analyze.d.ts.map +1 -0
- package/dist/code-gen/analyze.js +79 -0
- package/dist/code-gen/analyze.js.map +1 -0
- package/dist/code-gen/port-types.d.ts +58 -0
- package/dist/code-gen/port-types.d.ts.map +1 -0
- package/dist/code-gen/port-types.js +115 -0
- package/dist/code-gen/port-types.js.map +1 -0
- package/dist/code-gen/prompt.d.ts +37 -0
- package/dist/code-gen/prompt.d.ts.map +1 -0
- package/dist/code-gen/prompt.js +129 -0
- package/dist/code-gen/prompt.js.map +1 -0
- package/dist/code-gen/sandbox-manifest.d.ts +120 -0
- package/dist/code-gen/sandbox-manifest.d.ts.map +1 -0
- package/dist/code-gen/sandbox-manifest.js +1023 -0
- package/dist/code-gen/sandbox-manifest.js.map +1 -0
- package/dist/code-gen/sandbox-prompt.d.ts +26 -0
- package/dist/code-gen/sandbox-prompt.d.ts.map +1 -0
- package/dist/code-gen/sandbox-prompt.js +126 -0
- package/dist/code-gen/sandbox-prompt.js.map +1 -0
- package/dist/code-planner.d.ts +72 -0
- package/dist/code-planner.d.ts.map +1 -0
- package/dist/code-planner.js +271 -0
- package/dist/code-planner.js.map +1 -0
- package/dist/codeact/action-diagnostics.d.ts +35 -0
- package/dist/codeact/action-diagnostics.d.ts.map +1 -0
- package/dist/codeact/action-diagnostics.js +147 -0
- package/dist/codeact/action-diagnostics.js.map +1 -0
- package/dist/codeact/capability-modules.d.ts +66 -0
- package/dist/codeact/capability-modules.d.ts.map +1 -0
- package/dist/codeact/capability-modules.js +278 -0
- package/dist/codeact/capability-modules.js.map +1 -0
- package/dist/codeact/chat-codeact.d.ts +104 -0
- package/dist/codeact/chat-codeact.d.ts.map +1 -0
- package/dist/codeact/chat-codeact.js +424 -0
- package/dist/codeact/chat-codeact.js.map +1 -0
- package/dist/codeact/codeact-executor.d.ts +247 -0
- package/dist/codeact/codeact-executor.d.ts.map +1 -0
- package/dist/codeact/codeact-executor.js +1176 -0
- package/dist/codeact/codeact-executor.js.map +1 -0
- package/dist/codeact/compact-ids.d.ts +28 -0
- package/dist/codeact/compact-ids.d.ts.map +1 -0
- package/dist/codeact/compact-ids.js +77 -0
- package/dist/codeact/compact-ids.js.map +1 -0
- package/dist/codeact/execute-code-contract.d.ts +97 -0
- package/dist/codeact/execute-code-contract.d.ts.map +1 -0
- package/dist/codeact/execute-code-contract.js +225 -0
- package/dist/codeact/execute-code-contract.js.map +1 -0
- package/dist/codeact/fabric-package.d.ts +29 -0
- package/dist/codeact/fabric-package.d.ts.map +1 -0
- package/dist/codeact/fabric-package.js +62 -0
- package/dist/codeact/fabric-package.js.map +1 -0
- package/dist/codeact/flow-package.d.ts +31 -0
- package/dist/codeact/flow-package.d.ts.map +1 -0
- package/dist/codeact/flow-package.js +80 -0
- package/dist/codeact/flow-package.js.map +1 -0
- package/dist/codeact/graph-dsl-package.d.ts +42 -0
- package/dist/codeact/graph-dsl-package.d.ts.map +1 -0
- package/dist/codeact/graph-dsl-package.js +100 -0
- package/dist/codeact/graph-dsl-package.js.map +1 -0
- package/dist/codeact/graph-model.d.ts +30 -0
- package/dist/codeact/graph-model.d.ts.map +1 -0
- package/dist/codeact/graph-model.js +408 -0
- package/dist/codeact/graph-model.js.map +1 -0
- package/dist/codeact/mcp-guest-contract.d.ts +66 -0
- package/dist/codeact/mcp-guest-contract.d.ts.map +1 -0
- package/dist/codeact/mcp-guest-contract.js +108 -0
- package/dist/codeact/mcp-guest-contract.js.map +1 -0
- package/dist/codeact/nodetool-api.d.ts +60 -0
- package/dist/codeact/nodetool-api.d.ts.map +1 -0
- package/dist/codeact/nodetool-api.js +2056 -0
- package/dist/codeact/nodetool-api.js.map +1 -0
- package/dist/codeact/prompt.d.ts +88 -0
- package/dist/codeact/prompt.d.ts.map +1 -0
- package/dist/codeact/prompt.js +326 -0
- package/dist/codeact/prompt.js.map +1 -0
- package/dist/codeact/sandbox-package-docs.d.ts +48 -0
- package/dist/codeact/sandbox-package-docs.d.ts.map +1 -0
- package/dist/codeact/sandbox-package-docs.js +55 -0
- package/dist/codeact/sandbox-package-docs.js.map +1 -0
- package/dist/codeact/sandbox-package-listing.d.ts +81 -0
- package/dist/codeact/sandbox-package-listing.d.ts.map +1 -0
- package/dist/codeact/sandbox-package-listing.js +101 -0
- package/dist/codeact/sandbox-package-listing.js.map +1 -0
- package/dist/codeact/sandbox-packages.d.ts +72 -0
- package/dist/codeact/sandbox-packages.d.ts.map +1 -0
- package/dist/codeact/sandbox-packages.js +230 -0
- package/dist/codeact/sandbox-packages.js.map +1 -0
- package/dist/codeact/timeline-package.d.ts +5 -0
- package/dist/codeact/timeline-package.d.ts.map +1 -0
- package/dist/codeact/timeline-package.js +10 -0
- package/dist/codeact/timeline-package.js.map +1 -0
- package/dist/codeact/tool-api.d.ts +197 -0
- package/dist/codeact/tool-api.d.ts.map +1 -0
- package/dist/codeact/tool-api.js +445 -0
- package/dist/codeact/tool-api.js.map +1 -0
- package/dist/codeact/tools-prelude.d.ts +32 -0
- package/dist/codeact/tools-prelude.d.ts.map +1 -0
- package/dist/codeact/tools-prelude.js +65 -0
- package/dist/codeact/tools-prelude.js.map +1 -0
- package/dist/constants.d.ts +47 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +64 -2
- package/dist/constants.js.map +1 -1
- package/dist/custom-animation-bake.d.ts +57 -0
- package/dist/custom-animation-bake.d.ts.map +1 -0
- package/dist/custom-animation-bake.js +88 -0
- package/dist/custom-animation-bake.js.map +1 -0
- package/dist/dynamic-slots.d.ts +72 -0
- package/dist/dynamic-slots.d.ts.map +1 -0
- package/dist/dynamic-slots.js +0 -0
- package/dist/dynamic-slots.js.map +1 -0
- package/dist/evals/app-build-cases.d.ts +35 -0
- package/dist/evals/app-build-cases.d.ts.map +1 -0
- package/dist/evals/app-build-cases.js +722 -0
- package/dist/evals/app-build-cases.js.map +1 -0
- package/dist/evals/app-build-eval.d.ts +208 -0
- package/dist/evals/app-build-eval.d.ts.map +1 -0
- package/dist/evals/app-build-eval.js +518 -0
- package/dist/evals/app-build-eval.js.map +1 -0
- package/dist/evals/app-build-plan.d.ts +76 -0
- package/dist/evals/app-build-plan.d.ts.map +1 -0
- package/dist/evals/app-build-plan.js +140 -0
- package/dist/evals/app-build-plan.js.map +1 -0
- package/dist/evals/code-gen-cases.d.ts +13 -0
- package/dist/evals/code-gen-cases.d.ts.map +1 -0
- package/dist/evals/code-gen-cases.js +124 -0
- package/dist/evals/code-gen-cases.js.map +1 -0
- package/dist/evals/code-gen-eval.d.ts +109 -0
- package/dist/evals/code-gen-eval.d.ts.map +1 -0
- package/dist/evals/code-gen-eval.js +299 -0
- package/dist/evals/code-gen-eval.js.map +1 -0
- package/dist/evals/codeact-api-cases.d.ts +14 -0
- package/dist/evals/codeact-api-cases.d.ts.map +1 -0
- package/dist/evals/codeact-api-cases.js +29 -0
- package/dist/evals/codeact-api-cases.js.map +1 -0
- package/dist/evals/codeact-api-core.d.ts +23 -0
- package/dist/evals/codeact-api-core.d.ts.map +1 -0
- package/dist/evals/codeact-api-core.js +1411 -0
- package/dist/evals/codeact-api-core.js.map +1 -0
- package/dist/evals/codeact-api-surfaces.d.ts +25 -0
- package/dist/evals/codeact-api-surfaces.d.ts.map +1 -0
- package/dist/evals/codeact-api-surfaces.js +2191 -0
- package/dist/evals/codeact-api-surfaces.js.map +1 -0
- package/dist/evals/codeact-cases.d.ts +76 -0
- package/dist/evals/codeact-cases.d.ts.map +1 -0
- package/dist/evals/codeact-cases.js +151 -0
- package/dist/evals/codeact-cases.js.map +1 -0
- package/dist/evals/codeact-eval.d.ts +66 -0
- package/dist/evals/codeact-eval.d.ts.map +1 -0
- package/dist/evals/codeact-eval.js +250 -0
- package/dist/evals/codeact-eval.js.map +1 -0
- package/dist/evals/codeact-sandbox-pack-cases.d.ts +31 -0
- package/dist/evals/codeact-sandbox-pack-cases.d.ts.map +1 -0
- package/dist/evals/codeact-sandbox-pack-cases.js +219 -0
- package/dist/evals/codeact-sandbox-pack-cases.js.map +1 -0
- package/dist/evals/escalation-cases.d.ts +22 -0
- package/dist/evals/escalation-cases.d.ts.map +1 -0
- package/dist/evals/escalation-cases.js +343 -0
- package/dist/evals/escalation-cases.js.map +1 -0
- package/dist/evals/escalation.d.ts +92 -0
- package/dist/evals/escalation.d.ts.map +1 -0
- package/dist/evals/escalation.js +152 -0
- package/dist/evals/escalation.js.map +1 -0
- package/dist/evals/goal-judge.d.ts +51 -0
- package/dist/evals/goal-judge.d.ts.map +1 -0
- package/dist/evals/goal-judge.js +152 -0
- package/dist/evals/goal-judge.js.map +1 -0
- package/dist/evals/graph-e2e-cases.d.ts +21 -0
- package/dist/evals/graph-e2e-cases.d.ts.map +1 -0
- package/dist/evals/graph-e2e-cases.js +262 -0
- package/dist/evals/graph-e2e-cases.js.map +1 -0
- package/dist/evals/graph-e2e-eval.d.ts +202 -0
- package/dist/evals/graph-e2e-eval.d.ts.map +1 -0
- package/dist/evals/graph-e2e-eval.js +438 -0
- package/dist/evals/graph-e2e-eval.js.map +1 -0
- package/dist/evals/graph-planner-cases.d.ts +12 -0
- package/dist/evals/graph-planner-cases.d.ts.map +1 -0
- package/dist/evals/graph-planner-cases.js +256 -0
- package/dist/evals/graph-planner-cases.js.map +1 -0
- package/dist/evals/graph-planner-eval.d.ts +178 -0
- package/dist/evals/graph-planner-eval.d.ts.map +1 -0
- package/dist/evals/graph-planner-eval.js +495 -0
- package/dist/evals/graph-planner-eval.js.map +1 -0
- package/dist/evals/planner-tools.d.ts +21 -0
- package/dist/evals/planner-tools.d.ts.map +1 -0
- package/dist/evals/planner-tools.js +66 -0
- package/dist/evals/planner-tools.js.map +1 -0
- package/dist/evals/subtask-cases.d.ts +88 -0
- package/dist/evals/subtask-cases.d.ts.map +1 -0
- package/dist/evals/subtask-cases.js +322 -0
- package/dist/evals/subtask-cases.js.map +1 -0
- package/dist/evals/subtask-eval.d.ts +129 -0
- package/dist/evals/subtask-eval.d.ts.map +1 -0
- package/dist/evals/subtask-eval.js +397 -0
- package/dist/evals/subtask-eval.js.map +1 -0
- package/dist/evals/surfaces/app.d.ts +19 -0
- package/dist/evals/surfaces/app.d.ts.map +1 -0
- package/dist/evals/surfaces/app.js +341 -0
- package/dist/evals/surfaces/app.js.map +1 -0
- package/dist/evals/surfaces/creative-pipeline.d.ts +203 -0
- package/dist/evals/surfaces/creative-pipeline.d.ts.map +1 -0
- package/dist/evals/surfaces/creative-pipeline.js +909 -0
- package/dist/evals/surfaces/creative-pipeline.js.map +1 -0
- package/dist/evals/surfaces/game.d.ts +5 -0
- package/dist/evals/surfaces/game.d.ts.map +1 -0
- package/dist/evals/surfaces/game.js +42 -0
- package/dist/evals/surfaces/game.js.map +1 -0
- package/dist/evals/surfaces/js-script.d.ts +66 -0
- package/dist/evals/surfaces/js-script.d.ts.map +1 -0
- package/dist/evals/surfaces/js-script.js +407 -0
- package/dist/evals/surfaces/js-script.js.map +1 -0
- package/dist/evals/surfaces/memory.d.ts +34 -0
- package/dist/evals/surfaces/memory.d.ts.map +1 -0
- package/dist/evals/surfaces/memory.js +264 -0
- package/dist/evals/surfaces/memory.js.map +1 -0
- package/dist/evals/surfaces/model3d.d.ts +45 -0
- package/dist/evals/surfaces/model3d.d.ts.map +1 -0
- package/dist/evals/surfaces/model3d.js +346 -0
- package/dist/evals/surfaces/model3d.js.map +1 -0
- package/dist/evals/surfaces/script.d.ts +96 -0
- package/dist/evals/surfaces/script.d.ts.map +1 -0
- package/dist/evals/surfaces/script.js +709 -0
- package/dist/evals/surfaces/script.js.map +1 -0
- package/dist/evals/surfaces/sketch.d.ts +124 -0
- package/dist/evals/surfaces/sketch.d.ts.map +1 -0
- package/dist/evals/surfaces/sketch.js +1298 -0
- package/dist/evals/surfaces/sketch.js.map +1 -0
- package/dist/evals/surfaces/storyboard.d.ts +97 -0
- package/dist/evals/surfaces/storyboard.d.ts.map +1 -0
- package/dist/evals/surfaces/storyboard.js +693 -0
- package/dist/evals/surfaces/storyboard.js.map +1 -0
- package/dist/evals/surfaces/timeline.d.ts +416 -0
- package/dist/evals/surfaces/timeline.d.ts.map +1 -0
- package/dist/evals/surfaces/timeline.js +3267 -0
- package/dist/evals/surfaces/timeline.js.map +1 -0
- package/dist/evals/task-planner-cases.d.ts +57 -0
- package/dist/evals/task-planner-cases.d.ts.map +1 -0
- package/dist/evals/task-planner-cases.js +117 -0
- package/dist/evals/task-planner-cases.js.map +1 -0
- package/dist/evals/task-planner-eval.d.ts +101 -0
- package/dist/evals/task-planner-eval.d.ts.map +1 -0
- package/dist/evals/task-planner-eval.js +380 -0
- package/dist/evals/task-planner-eval.js.map +1 -0
- package/dist/evals/tool-loop-bridge.d.ts +57 -0
- package/dist/evals/tool-loop-bridge.d.ts.map +1 -0
- package/dist/evals/tool-loop-bridge.js +129 -0
- package/dist/evals/tool-loop-bridge.js.map +1 -0
- package/dist/evals/tool-loop-cases.d.ts +21 -0
- package/dist/evals/tool-loop-cases.d.ts.map +1 -0
- package/dist/evals/tool-loop-cases.js +401 -0
- package/dist/evals/tool-loop-cases.js.map +1 -0
- package/dist/evals/tool-loop-eval.d.ts +234 -0
- package/dist/evals/tool-loop-eval.d.ts.map +1 -0
- package/dist/evals/tool-loop-eval.js +373 -0
- package/dist/evals/tool-loop-eval.js.map +1 -0
- package/dist/evals/tool-loop-permission.d.ts +43 -0
- package/dist/evals/tool-loop-permission.d.ts.map +1 -0
- package/dist/evals/tool-loop-permission.js +76 -0
- package/dist/evals/tool-loop-permission.js.map +1 -0
- package/dist/game-examples.d.ts +447 -0
- package/dist/game-examples.d.ts.map +1 -0
- package/dist/game-examples.js +187 -0
- package/dist/game-examples.js.map +1 -0
- package/dist/graph-builder.d.ts +55 -3
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +159 -4
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-dsl-core.d.ts +25 -0
- package/dist/graph-dsl-core.d.ts.map +1 -0
- package/dist/graph-dsl-core.js +312 -0
- package/dist/graph-dsl-core.js.map +1 -0
- package/dist/graph-dsl.d.ts +30 -0
- package/dist/graph-dsl.d.ts.map +1 -0
- package/dist/graph-dsl.js +116 -0
- package/dist/graph-dsl.js.map +1 -0
- package/dist/host-binaries.d.ts +6 -0
- package/dist/host-binaries.d.ts.map +1 -0
- package/dist/host-binaries.js +6 -0
- package/dist/host-binaries.js.map +1 -0
- package/dist/host-binary-guard.d.ts +100 -0
- package/dist/host-binary-guard.d.ts.map +1 -0
- package/dist/host-binary-guard.js +205 -0
- package/dist/host-binary-guard.js.map +1 -0
- package/dist/host-modules/aws.d.ts +57 -0
- package/dist/host-modules/aws.d.ts.map +1 -0
- package/dist/host-modules/aws.js +309 -0
- package/dist/host-modules/aws.js.map +1 -0
- package/dist/host-modules/chrono.d.ts +15 -0
- package/dist/host-modules/chrono.d.ts.map +1 -0
- package/dist/host-modules/chrono.js +35 -0
- package/dist/host-modules/chrono.js.map +1 -0
- package/dist/host-modules/csv.d.ts +17 -0
- package/dist/host-modules/csv.d.ts.map +1 -0
- package/dist/host-modules/csv.js +64 -0
- package/dist/host-modules/csv.js.map +1 -0
- package/dist/host-modules/diff.d.ts +9 -0
- package/dist/host-modules/diff.d.ts.map +1 -0
- package/dist/host-modules/diff.js +26 -0
- package/dist/host-modules/diff.js.map +1 -0
- package/dist/host-modules/dispatcher.d.ts +41 -0
- package/dist/host-modules/dispatcher.d.ts.map +1 -0
- package/dist/host-modules/dispatcher.js +99 -0
- package/dist/host-modules/dispatcher.js.map +1 -0
- package/dist/host-modules/docx.d.ts +19 -0
- package/dist/host-modules/docx.d.ts.map +1 -0
- package/dist/host-modules/docx.js +120 -0
- package/dist/host-modules/docx.js.map +1 -0
- package/dist/host-modules/epub.d.ts +48 -0
- package/dist/host-modules/epub.d.ts.map +1 -0
- package/dist/host-modules/epub.js +100 -0
- package/dist/host-modules/epub.js.map +1 -0
- package/dist/host-modules/exif.d.ts +8 -0
- package/dist/host-modules/exif.d.ts.map +1 -0
- package/dist/host-modules/exif.js +19 -0
- package/dist/host-modules/exif.js.map +1 -0
- package/dist/host-modules/expr.d.ts +16 -0
- package/dist/host-modules/expr.d.ts.map +1 -0
- package/dist/host-modules/expr.js +18 -0
- package/dist/host-modules/expr.js.map +1 -0
- package/dist/host-modules/fabric.d.ts +46 -0
- package/dist/host-modules/fabric.d.ts.map +1 -0
- package/dist/host-modules/fabric.js +178 -0
- package/dist/host-modules/fabric.js.map +1 -0
- package/dist/host-modules/html.d.ts +51 -0
- package/dist/host-modules/html.d.ts.map +1 -0
- package/dist/host-modules/html.js +198 -0
- package/dist/host-modules/html.js.map +1 -0
- package/dist/host-modules/ics.d.ts +9 -0
- package/dist/host-modules/ics.d.ts.map +1 -0
- package/dist/host-modules/ics.js +35 -0
- package/dist/host-modules/ics.js.map +1 -0
- package/dist/host-modules/limits.d.ts +41 -0
- package/dist/host-modules/limits.d.ts.map +1 -0
- package/dist/host-modules/limits.js +76 -0
- package/dist/host-modules/limits.js.map +1 -0
- package/dist/host-modules/mammoth.d.ts +11 -0
- package/dist/host-modules/mammoth.d.ts.map +1 -0
- package/dist/host-modules/mammoth.js +29 -0
- package/dist/host-modules/mammoth.js.map +1 -0
- package/dist/host-modules/notion.d.ts +45 -0
- package/dist/host-modules/notion.d.ts.map +1 -0
- package/dist/host-modules/notion.js +128 -0
- package/dist/host-modules/notion.js.map +1 -0
- package/dist/host-modules/ocr.d.ts +25 -0
- package/dist/host-modules/ocr.d.ts.map +1 -0
- package/dist/host-modules/ocr.js +74 -0
- package/dist/host-modules/ocr.js.map +1 -0
- package/dist/host-modules/pdf.d.ts +21 -0
- package/dist/host-modules/pdf.d.ts.map +1 -0
- package/dist/host-modules/pdf.js +53 -0
- package/dist/host-modules/pdf.js.map +1 -0
- package/dist/host-modules/pdflib.d.ts +17 -0
- package/dist/host-modules/pdflib.d.ts.map +1 -0
- package/dist/host-modules/pdflib.js +138 -0
- package/dist/host-modules/pdflib.js.map +1 -0
- package/dist/host-modules/pptx.d.ts +19 -0
- package/dist/host-modules/pptx.d.ts.map +1 -0
- package/dist/host-modules/pptx.js +67 -0
- package/dist/host-modules/pptx.js.map +1 -0
- package/dist/host-modules/pptxgen.d.ts +13 -0
- package/dist/host-modules/pptxgen.d.ts.map +1 -0
- package/dist/host-modules/pptxgen.js +98 -0
- package/dist/host-modules/pptxgen.js.map +1 -0
- package/dist/host-modules/prepared-request.d.ts +41 -0
- package/dist/host-modules/prepared-request.d.ts.map +1 -0
- package/dist/host-modules/prepared-request.js +104 -0
- package/dist/host-modules/prepared-request.js.map +1 -0
- package/dist/host-modules/registry.d.ts +31 -0
- package/dist/host-modules/registry.d.ts.map +1 -0
- package/dist/host-modules/registry.js +193 -0
- package/dist/host-modules/registry.js.map +1 -0
- package/dist/host-modules/subtitle.d.ts +15 -0
- package/dist/host-modules/subtitle.d.ts.map +1 -0
- package/dist/host-modules/subtitle.js +45 -0
- package/dist/host-modules/subtitle.js.map +1 -0
- package/dist/host-modules/supabase.d.ts +42 -0
- package/dist/host-modules/supabase.d.ts.map +1 -0
- package/dist/host-modules/supabase.js +141 -0
- package/dist/host-modules/supabase.js.map +1 -0
- package/dist/host-modules/tfjs.d.ts +42 -0
- package/dist/host-modules/tfjs.d.ts.map +1 -0
- package/dist/host-modules/tfjs.js +186 -0
- package/dist/host-modules/tfjs.js.map +1 -0
- package/dist/host-modules/tokens.d.ts +17 -0
- package/dist/host-modules/tokens.d.ts.map +1 -0
- package/dist/host-modules/tokens.js +74 -0
- package/dist/host-modules/tokens.js.map +1 -0
- package/dist/host-modules/twilio.d.ts +34 -0
- package/dist/host-modules/twilio.d.ts.map +1 -0
- package/dist/host-modules/twilio.js +67 -0
- package/dist/host-modules/twilio.js.map +1 -0
- package/dist/host-modules/xlsx.d.ts +26 -0
- package/dist/host-modules/xlsx.d.ts.map +1 -0
- package/dist/host-modules/xlsx.js +323 -0
- package/dist/host-modules/xlsx.js.map +1 -0
- package/dist/host-modules/xml.d.ts +23 -0
- package/dist/host-modules/xml.d.ts.map +1 -0
- package/dist/host-modules/xml.js +38 -0
- package/dist/host-modules/xml.js.map +1 -0
- package/dist/host-modules/zip.d.ts +24 -0
- package/dist/host-modules/zip.d.ts.map +1 -0
- package/dist/host-modules/zip.js +70 -0
- package/dist/host-modules/zip.js.map +1 -0
- package/dist/index.d.ts +197 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +153 -60
- package/dist/index.js.map +1 -1
- package/dist/js-sandbox-worker/host.d.ts +136 -0
- package/dist/js-sandbox-worker/host.d.ts.map +1 -0
- package/dist/js-sandbox-worker/host.js +482 -0
- package/dist/js-sandbox-worker/host.js.map +1 -0
- package/dist/js-sandbox-worker/interpreter.d.ts +276 -0
- package/dist/js-sandbox-worker/interpreter.d.ts.map +1 -0
- package/dist/js-sandbox-worker/interpreter.js +1201 -0
- package/dist/js-sandbox-worker/interpreter.js.map +1 -0
- package/dist/js-sandbox-worker/protocol.d.ts +168 -0
- package/dist/js-sandbox-worker/protocol.d.ts.map +1 -0
- package/dist/js-sandbox-worker/protocol.js +217 -0
- package/dist/js-sandbox-worker/protocol.js.map +1 -0
- package/dist/js-sandbox-worker/worker-entry.d.ts +26 -0
- package/dist/js-sandbox-worker/worker-entry.d.ts.map +1 -0
- package/dist/js-sandbox-worker/worker-entry.js +307 -0
- package/dist/js-sandbox-worker/worker-entry.js.map +1 -0
- package/dist/js-sandbox.d.ts +455 -15
- package/dist/js-sandbox.d.ts.map +1 -1
- package/dist/js-sandbox.js +1961 -309
- package/dist/js-sandbox.js.map +1 -1
- package/dist/js-script-app-runner.d.ts +9 -0
- package/dist/js-script-app-runner.d.ts.map +1 -0
- package/dist/js-script-app-runner.js +49 -0
- package/dist/js-script-app-runner.js.map +1 -0
- package/dist/js-script-sandbox.d.ts +33 -0
- package/dist/js-script-sandbox.d.ts.map +1 -0
- package/dist/js-script-sandbox.js +82 -0
- package/dist/js-script-sandbox.js.map +1 -0
- package/dist/jtbd/friction.d.ts +26 -0
- package/dist/jtbd/friction.d.ts.map +1 -0
- package/dist/jtbd/friction.js +169 -0
- package/dist/jtbd/friction.js.map +1 -0
- package/dist/jtbd/index.d.ts +6 -0
- package/dist/jtbd/index.d.ts.map +1 -0
- package/dist/jtbd/index.js +6 -0
- package/dist/jtbd/index.js.map +1 -0
- package/dist/jtbd/optimize.d.ts +71 -0
- package/dist/jtbd/optimize.d.ts.map +1 -0
- package/dist/jtbd/optimize.js +176 -0
- package/dist/jtbd/optimize.js.map +1 -0
- package/dist/jtbd/registry.d.ts +29 -0
- package/dist/jtbd/registry.d.ts.map +1 -0
- package/dist/jtbd/registry.js +451 -0
- package/dist/jtbd/registry.js.map +1 -0
- package/dist/jtbd/run.d.ts +70 -0
- package/dist/jtbd/run.d.ts.map +1 -0
- package/dist/jtbd/run.js +177 -0
- package/dist/jtbd/run.js.map +1 -0
- package/dist/jtbd/types.d.ts +141 -0
- package/dist/jtbd/types.d.ts.map +1 -0
- package/dist/jtbd/types.js +25 -0
- package/dist/jtbd/types.js.map +1 -0
- package/dist/model-prompting-skills.d.ts +45 -0
- package/dist/model-prompting-skills.d.ts.map +1 -0
- package/dist/model-prompting-skills.js +116 -0
- package/dist/model-prompting-skills.js.map +1 -0
- package/dist/network-guard.d.ts +50 -0
- package/dist/network-guard.d.ts.map +1 -0
- package/dist/network-guard.js +106 -0
- package/dist/network-guard.js.map +1 -0
- package/dist/normalize-model-properties.d.ts +26 -0
- package/dist/normalize-model-properties.d.ts.map +1 -0
- package/dist/normalize-model-properties.js +58 -0
- package/dist/normalize-model-properties.js.map +1 -0
- package/dist/output-format.d.ts +5 -1
- package/dist/output-format.d.ts.map +1 -1
- package/dist/output-format.js.map +1 -1
- package/dist/parallel-task-executor.d.ts +68 -37
- package/dist/parallel-task-executor.d.ts.map +1 -1
- package/dist/parallel-task-executor.js +234 -203
- package/dist/parallel-task-executor.js.map +1 -1
- package/dist/prompts/background-subtask-prompt.d.ts +14 -0
- package/dist/prompts/background-subtask-prompt.d.ts.map +1 -0
- package/dist/prompts/background-subtask-prompt.js +64 -0
- package/dist/prompts/background-subtask-prompt.js.map +1 -0
- package/dist/prompts/product-knowledge.d.ts +3 -0
- package/dist/prompts/product-knowledge.d.ts.map +1 -0
- package/dist/prompts/product-knowledge.js +79 -0
- package/dist/prompts/product-knowledge.js.map +1 -0
- package/dist/prompts/read-only-search-prompt.d.ts +33 -0
- package/dist/prompts/read-only-search-prompt.d.ts.map +1 -0
- package/dist/prompts/read-only-search-prompt.js +109 -0
- package/dist/prompts/read-only-search-prompt.js.map +1 -0
- package/dist/prompts/sandbox-pack-catalog.d.ts +23 -0
- package/dist/prompts/sandbox-pack-catalog.d.ts.map +1 -0
- package/dist/prompts/sandbox-pack-catalog.js +40 -0
- package/dist/prompts/sandbox-pack-catalog.js.map +1 -0
- package/dist/prompts/security-monitor-prompt.d.ts +32 -0
- package/dist/prompts/security-monitor-prompt.d.ts.map +1 -0
- package/dist/prompts/security-monitor-prompt.js +99 -0
- package/dist/prompts/security-monitor-prompt.js.map +1 -0
- package/dist/prompts/workflow-authoring-knowledge.d.ts +77 -0
- package/dist/prompts/workflow-authoring-knowledge.d.ts.map +1 -0
- package/dist/prompts/workflow-authoring-knowledge.js +319 -0
- package/dist/prompts/workflow-authoring-knowledge.js.map +1 -0
- package/dist/run-policy.d.ts +25 -0
- package/dist/run-policy.d.ts.map +1 -0
- package/dist/run-policy.js +54 -0
- package/dist/run-policy.js.map +1 -0
- package/dist/sandbox-av-media.d.ts +33 -0
- package/dist/sandbox-av-media.d.ts.map +1 -0
- package/dist/sandbox-av-media.js +666 -0
- package/dist/sandbox-av-media.js.map +1 -0
- package/dist/sandbox-bootstrap-modules.d.ts +54 -0
- package/dist/sandbox-bootstrap-modules.d.ts.map +1 -0
- package/dist/sandbox-bootstrap-modules.js +104 -0
- package/dist/sandbox-bootstrap-modules.js.map +1 -0
- package/dist/sandbox-bytes.d.ts +43 -0
- package/dist/sandbox-bytes.d.ts.map +1 -0
- package/dist/sandbox-bytes.js +61 -0
- package/dist/sandbox-bytes.js.map +1 -0
- package/dist/sandbox-canvas-api.d.ts +30 -0
- package/dist/sandbox-canvas-api.d.ts.map +1 -0
- package/dist/sandbox-canvas-api.js +81 -0
- package/dist/sandbox-canvas-api.js.map +1 -0
- package/dist/sandbox-constants.d.ts +5 -0
- package/dist/sandbox-constants.d.ts.map +1 -0
- package/dist/sandbox-constants.js +13 -0
- package/dist/sandbox-constants.js.map +1 -0
- package/dist/sandbox-json-transport.d.ts +107 -0
- package/dist/sandbox-json-transport.d.ts.map +1 -0
- package/dist/sandbox-json-transport.js +324 -0
- package/dist/sandbox-json-transport.js.map +1 -0
- package/dist/sandbox-media-handle.d.ts +78 -0
- package/dist/sandbox-media-handle.d.ts.map +1 -0
- package/dist/sandbox-media-handle.js +114 -0
- package/dist/sandbox-media-handle.js.map +1 -0
- package/dist/sandbox-media-ref.d.ts +134 -0
- package/dist/sandbox-media-ref.d.ts.map +1 -0
- package/dist/sandbox-media-ref.js +442 -0
- package/dist/sandbox-media-ref.js.map +1 -0
- package/dist/sandbox-media.d.ts +177 -0
- package/dist/sandbox-media.d.ts.map +1 -0
- package/dist/sandbox-media.js +963 -0
- package/dist/sandbox-media.js.map +1 -0
- package/dist/sandbox-module-fixtures.d.ts +445 -0
- package/dist/sandbox-module-fixtures.d.ts.map +1 -0
- package/dist/sandbox-module-fixtures.js +214 -0
- package/dist/sandbox-module-fixtures.js.map +1 -0
- package/dist/sandbox-toolbelt.d.ts +13 -0
- package/dist/sandbox-toolbelt.d.ts.map +1 -0
- package/dist/sandbox-toolbelt.js +64 -0
- package/dist/sandbox-toolbelt.js.map +1 -0
- package/dist/security-monitor.d.ts +89 -0
- package/dist/security-monitor.d.ts.map +1 -0
- package/dist/security-monitor.js +307 -0
- package/dist/security-monitor.js.map +1 -0
- package/dist/serpapi/catalog.d.ts +101 -0
- package/dist/serpapi/catalog.d.ts.map +1 -0
- package/dist/serpapi/catalog.js +246 -0
- package/dist/serpapi/catalog.js.map +1 -0
- package/dist/serpapi/client.d.ts +81 -0
- package/dist/serpapi/client.d.ts.map +1 -0
- package/dist/serpapi/client.js +228 -0
- package/dist/serpapi/client.js.map +1 -0
- package/dist/serpapi/errors.d.ts +49 -0
- package/dist/serpapi/errors.d.ts.map +1 -0
- package/dist/serpapi/errors.js +64 -0
- package/dist/serpapi/errors.js.map +1 -0
- package/dist/serpapi/normalize.d.ts +58 -0
- package/dist/serpapi/normalize.d.ts.map +1 -0
- package/dist/serpapi/normalize.js +140 -0
- package/dist/serpapi/normalize.js.map +1 -0
- package/dist/skill-prompt.d.ts +27 -0
- package/dist/skill-prompt.d.ts.map +1 -0
- package/dist/skill-prompt.js +61 -0
- package/dist/skill-prompt.js.map +1 -0
- package/dist/step-executor.d.ts +90 -77
- package/dist/step-executor.d.ts.map +1 -1
- package/dist/step-executor.js +384 -690
- package/dist/step-executor.js.map +1 -1
- package/dist/subagent.d.ts +275 -0
- package/dist/subagent.d.ts.map +1 -0
- package/dist/subagent.js +444 -0
- package/dist/subagent.js.map +1 -0
- package/dist/supervisor/prompt.d.ts +12 -0
- package/dist/supervisor/prompt.d.ts.map +1 -0
- package/dist/supervisor/prompt.js +51 -0
- package/dist/supervisor/prompt.js.map +1 -0
- package/dist/supervisor/supervisor-agent.d.ts +72 -0
- package/dist/supervisor/supervisor-agent.d.ts.map +1 -0
- package/dist/supervisor/supervisor-agent.js +185 -0
- package/dist/supervisor/supervisor-agent.js.map +1 -0
- package/dist/supervisor/tools.d.ts +56 -0
- package/dist/supervisor/tools.d.ts.map +1 -0
- package/dist/supervisor/tools.js +83 -0
- package/dist/supervisor/tools.js.map +1 -0
- package/dist/supervisor/verdict-schema.d.ts +18 -0
- package/dist/supervisor/verdict-schema.d.ts.map +1 -0
- package/dist/supervisor/verdict-schema.js +58 -0
- package/dist/supervisor/verdict-schema.js.map +1 -0
- package/dist/system-skills.d.ts +68 -0
- package/dist/system-skills.d.ts.map +1 -0
- package/dist/system-skills.js +168 -0
- package/dist/system-skills.js.map +1 -0
- package/dist/task-executor.d.ts +90 -40
- package/dist/task-executor.d.ts.map +1 -1
- package/dist/task-executor.js +259 -282
- package/dist/task-executor.js.map +1 -1
- package/dist/task-planner.d.ts +18 -12
- package/dist/task-planner.d.ts.map +1 -1
- package/dist/task-planner.js +342 -328
- package/dist/task-planner.js.map +1 -1
- package/dist/timeline-preview/compare.d.ts +27 -0
- package/dist/timeline-preview/compare.d.ts.map +1 -0
- package/dist/timeline-preview/compare.js +71 -0
- package/dist/timeline-preview/compare.js.map +1 -0
- package/dist/timeline-preview/frames.d.ts +167 -0
- package/dist/timeline-preview/frames.d.ts.map +1 -0
- package/dist/timeline-preview/frames.js +646 -0
- package/dist/timeline-preview/frames.js.map +1 -0
- package/dist/timeline-preview/model3d.d.ts +67 -0
- package/dist/timeline-preview/model3d.d.ts.map +1 -0
- package/dist/timeline-preview/model3d.js +165 -0
- package/dist/timeline-preview/model3d.js.map +1 -0
- package/dist/timeline-preview/rasterize.d.ts +33 -0
- package/dist/timeline-preview/rasterize.d.ts.map +1 -0
- package/dist/timeline-preview/rasterize.js +83 -0
- package/dist/timeline-preview/rasterize.js.map +1 -0
- package/dist/timeline-preview/sheet.d.ts +38 -0
- package/dist/timeline-preview/sheet.d.ts.map +1 -0
- package/dist/timeline-preview/sheet.js +72 -0
- package/dist/timeline-preview/sheet.js.map +1 -0
- package/dist/tools/asset-persist.d.ts +29 -0
- package/dist/tools/asset-persist.d.ts.map +1 -0
- package/dist/tools/asset-persist.js +78 -0
- package/dist/tools/asset-persist.js.map +1 -0
- package/dist/tools/base-tool.d.ts +52 -5
- package/dist/tools/base-tool.d.ts.map +1 -1
- package/dist/tools/base-tool.js +180 -3
- package/dist/tools/base-tool.js.map +1 -1
- package/dist/tools/binary-output.d.ts +54 -0
- package/dist/tools/binary-output.d.ts.map +1 -0
- package/dist/tools/binary-output.js +127 -0
- package/dist/tools/binary-output.js.map +1 -0
- package/dist/tools/browser-tools.d.ts +5 -29
- package/dist/tools/browser-tools.d.ts.map +1 -1
- package/dist/tools/browser-tools.js +5 -205
- package/dist/tools/browser-tools.js.map +1 -1
- package/dist/tools/builtin-tools.d.ts +52 -0
- package/dist/tools/builtin-tools.d.ts.map +1 -0
- package/dist/tools/builtin-tools.js +276 -0
- package/dist/tools/builtin-tools.js.map +1 -0
- package/dist/tools/control-tool.d.ts +2 -5
- package/dist/tools/control-tool.d.ts.map +1 -1
- package/dist/tools/control-tool.js +18 -15
- package/dist/tools/control-tool.js.map +1 -1
- package/dist/tools/dataseo-tools.d.ts +34 -32
- package/dist/tools/dataseo-tools.d.ts.map +1 -1
- package/dist/tools/dataseo-tools.js +148 -233
- package/dist/tools/dataseo-tools.js.map +1 -1
- package/dist/tools/external-capability-tools.d.ts +36 -0
- package/dist/tools/external-capability-tools.d.ts.map +1 -0
- package/dist/tools/external-capability-tools.js +41 -0
- package/dist/tools/external-capability-tools.js.map +1 -0
- package/dist/tools/external-mcp-tools.d.ts +139 -0
- package/dist/tools/external-mcp-tools.d.ts.map +1 -0
- package/dist/tools/external-mcp-tools.js +552 -0
- package/dist/tools/external-mcp-tools.js.map +1 -0
- package/dist/tools/finish-step-tool.d.ts +1 -1
- package/dist/tools/finish-step-tool.d.ts.map +1 -1
- package/dist/tools/finish-step-tool.js +3 -3
- package/dist/tools/finish-step-tool.js.map +1 -1
- package/dist/tools/google-tools.d.ts +28 -39
- package/dist/tools/google-tools.d.ts.map +1 -1
- package/dist/tools/google-tools.js +51 -164
- package/dist/tools/google-tools.js.map +1 -1
- package/dist/tools/google-workspace-tools.d.ts +23 -0
- package/dist/tools/google-workspace-tools.d.ts.map +1 -0
- package/dist/tools/google-workspace-tools.js +26 -0
- package/dist/tools/google-workspace-tools.js.map +1 -0
- package/dist/tools/graph-validation-registry.d.ts +33 -0
- package/dist/tools/graph-validation-registry.d.ts.map +1 -0
- package/dist/tools/graph-validation-registry.js +54 -0
- package/dist/tools/graph-validation-registry.js.map +1 -0
- package/dist/tools/http-tools.d.ts +5 -53
- package/dist/tools/http-tools.d.ts.map +1 -1
- package/dist/tools/http-tools.js +5 -177
- package/dist/tools/http-tools.js.map +1 -1
- package/dist/tools/image-injection.d.ts +51 -0
- package/dist/tools/image-injection.d.ts.map +1 -0
- package/dist/tools/image-injection.js +85 -0
- package/dist/tools/image-injection.js.map +1 -0
- package/dist/tools/mcp-tool-support.d.ts +203 -0
- package/dist/tools/mcp-tool-support.d.ts.map +1 -0
- package/dist/tools/mcp-tool-support.js +536 -0
- package/dist/tools/mcp-tool-support.js.map +1 -0
- package/dist/tools/mcp-tools.d.ts +104 -390
- package/dist/tools/mcp-tools.d.ts.map +1 -1
- package/dist/tools/mcp-tools.js +128 -600
- package/dist/tools/mcp-tools.js.map +1 -1
- package/dist/tools/media-tools.d.ts +9 -272
- package/dist/tools/media-tools.d.ts.map +1 -1
- package/dist/tools/media-tools.js +11 -676
- package/dist/tools/media-tools.js.map +1 -1
- package/dist/tools/memory-tools.d.ts +36 -0
- package/dist/tools/memory-tools.d.ts.map +1 -0
- package/dist/tools/memory-tools.js +52 -0
- package/dist/tools/memory-tools.js.map +1 -0
- package/dist/tools/openai-tools.d.ts +28 -68
- package/dist/tools/openai-tools.d.ts.map +1 -1
- package/dist/tools/openai-tools.js +51 -181
- package/dist/tools/openai-tools.js.map +1 -1
- package/dist/tools/plan-builder-tools.d.ts +141 -7
- package/dist/tools/plan-builder-tools.d.ts.map +1 -1
- package/dist/tools/plan-builder-tools.js +159 -86
- package/dist/tools/plan-builder-tools.js.map +1 -1
- package/dist/tools/run-search-tool.d.ts +82 -0
- package/dist/tools/run-search-tool.d.ts.map +1 -0
- package/dist/tools/run-search-tool.js +121 -0
- package/dist/tools/run-search-tool.js.map +1 -0
- package/dist/tools/run-subtask-tool.d.ts +49 -0
- package/dist/tools/run-subtask-tool.d.ts.map +1 -0
- package/dist/tools/run-subtask-tool.js +88 -0
- package/dist/tools/run-subtask-tool.js.map +1 -0
- package/dist/tools/search-tools.d.ts +5 -36
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +5 -215
- package/dist/tools/search-tools.js.map +1 -1
- package/dist/tools/serp-providers/apify-provider.d.ts +18 -0
- package/dist/tools/serp-providers/apify-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/apify-provider.js +93 -0
- package/dist/tools/serp-providers/apify-provider.js.map +1 -0
- package/dist/tools/serp-providers/brave-provider.d.ts +34 -0
- package/dist/tools/serp-providers/brave-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/brave-provider.js +86 -0
- package/dist/tools/serp-providers/brave-provider.js.map +1 -0
- package/dist/tools/serp-providers/dataforseo-provider.d.ts +14 -1
- package/dist/tools/serp-providers/dataforseo-provider.d.ts.map +1 -1
- package/dist/tools/serp-providers/dataforseo-provider.js +0 -9
- package/dist/tools/serp-providers/dataforseo-provider.js.map +1 -1
- package/dist/tools/serp-providers/gemini-provider.d.ts +45 -0
- package/dist/tools/serp-providers/gemini-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/gemini-provider.js +148 -0
- package/dist/tools/serp-providers/gemini-provider.js.map +1 -0
- package/dist/tools/serp-providers/index.d.ts +61 -0
- package/dist/tools/serp-providers/index.d.ts.map +1 -1
- package/dist/tools/serp-providers/index.js +119 -3
- package/dist/tools/serp-providers/index.js.map +1 -1
- package/dist/tools/serp-providers/openai-provider.d.ts +58 -0
- package/dist/tools/serp-providers/openai-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/openai-provider.js +113 -0
- package/dist/tools/serp-providers/openai-provider.js.map +1 -0
- package/dist/tools/serp-providers/serpapi-provider.d.ts +9 -5
- package/dist/tools/serp-providers/serpapi-provider.d.ts.map +1 -1
- package/dist/tools/serp-providers/serpapi-provider.js +26 -44
- package/dist/tools/serp-providers/serpapi-provider.js.map +1 -1
- package/dist/tools/serp-tool-factory.d.ts +27 -0
- package/dist/tools/serp-tool-factory.d.ts.map +1 -0
- package/dist/tools/serp-tool-factory.js +56 -0
- package/dist/tools/serp-tool-factory.js.map +1 -0
- package/dist/tools/shared-tools.d.ts +33 -0
- package/dist/tools/shared-tools.d.ts.map +1 -0
- package/dist/tools/shared-tools.js +39 -0
- package/dist/tools/shared-tools.js.map +1 -0
- package/dist/tools/start-subtask-tool.d.ts +51 -0
- package/dist/tools/start-subtask-tool.d.ts.map +1 -0
- package/dist/tools/start-subtask-tool.js +189 -0
- package/dist/tools/start-subtask-tool.js.map +1 -0
- package/dist/tools/submit-code-tool.d.ts +159 -0
- package/dist/tools/submit-code-tool.d.ts.map +1 -0
- package/dist/tools/submit-code-tool.js +209 -0
- package/dist/tools/submit-code-tool.js.map +1 -0
- package/dist/tools/subtask-fields.d.ts +11 -0
- package/dist/tools/subtask-fields.d.ts.map +1 -0
- package/dist/tools/subtask-fields.js +11 -0
- package/dist/tools/subtask-fields.js.map +1 -0
- package/dist/tools/todo-tools.d.ts +15 -0
- package/dist/tools/todo-tools.d.ts.map +1 -0
- package/dist/tools/todo-tools.js +15 -0
- package/dist/tools/todo-tools.js.map +1 -0
- package/dist/tools/tool-permissions.d.ts +40 -0
- package/dist/tools/tool-permissions.d.ts.map +1 -0
- package/dist/tools/tool-permissions.js +52 -0
- package/dist/tools/tool-permissions.js.map +1 -0
- package/dist/tools/tool-search.d.ts +39 -0
- package/dist/tools/tool-search.d.ts.map +1 -0
- package/dist/tools/tool-search.js +108 -0
- package/dist/tools/tool-search.js.map +1 -0
- package/dist/tools/vector-tool-support.d.ts +15 -0
- package/dist/tools/vector-tool-support.d.ts.map +1 -0
- package/dist/tools/vector-tool-support.js +133 -0
- package/dist/tools/vector-tool-support.js.map +1 -0
- package/dist/tools/vector-tools.d.ts +13 -220
- package/dist/tools/vector-tools.d.ts.map +1 -1
- package/dist/tools/vector-tools.js +7 -578
- package/dist/tools/vector-tools.js.map +1 -1
- package/dist/tools/wait-subtasks-tool.d.ts +27 -0
- package/dist/tools/wait-subtasks-tool.d.ts.map +1 -0
- package/dist/tools/wait-subtasks-tool.js +66 -0
- package/dist/tools/wait-subtasks-tool.js.map +1 -0
- package/dist/tsconfig.tests.tmp.tsbuildinfo +1 -0
- package/dist/types.d.ts +16 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/dag-scheduler.d.ts +85 -0
- package/dist/utils/dag-scheduler.d.ts.map +1 -0
- package/dist/utils/dag-scheduler.js +180 -0
- package/dist/utils/dag-scheduler.js.map +1 -0
- package/dist/utils/json-parser.d.ts +32 -1
- package/dist/utils/json-parser.d.ts.map +1 -1
- package/dist/utils/json-parser.js +145 -10
- package/dist/utils/json-parser.js.map +1 -1
- package/dist/utils/json-schema-validate.d.ts +29 -0
- package/dist/utils/json-schema-validate.d.ts.map +1 -0
- package/dist/utils/json-schema-validate.js +228 -0
- package/dist/utils/json-schema-validate.js.map +1 -0
- package/dist/utils/link-abort.d.ts +13 -0
- package/dist/utils/link-abort.d.ts.map +1 -0
- package/dist/utils/link-abort.js +23 -0
- package/dist/utils/link-abort.js.map +1 -0
- package/dist/utils/merge-generators.d.ts +58 -0
- package/dist/utils/merge-generators.d.ts.map +1 -0
- package/dist/utils/merge-generators.js +148 -0
- package/dist/utils/merge-generators.js.map +1 -0
- package/dist/utils/remove-base64-images.d.ts +0 -5
- package/dist/utils/remove-base64-images.d.ts.map +1 -1
- package/dist/utils/remove-base64-images.js.map +1 -1
- package/dist/utils/step-failure.d.ts +20 -0
- package/dist/utils/step-failure.d.ts.map +1 -0
- package/dist/utils/step-failure.js +43 -0
- package/dist/utils/step-failure.js.map +1 -0
- package/dist/utils/think-tags.d.ts +16 -0
- package/dist/utils/think-tags.d.ts.map +1 -0
- package/dist/utils/think-tags.js +23 -0
- package/dist/utils/think-tags.js.map +1 -0
- package/dist/utils/type-guards.d.ts +26 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +44 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/dist/utils/ui-event-buffer.d.ts +18 -0
- package/dist/utils/ui-event-buffer.d.ts.map +1 -0
- package/dist/utils/ui-event-buffer.js +18 -0
- package/dist/utils/ui-event-buffer.js.map +1 -0
- package/dist/utils/wrap-generators-parallel.d.ts +0 -5
- package/dist/utils/wrap-generators-parallel.d.ts.map +1 -1
- package/dist/utils/wrap-generators-parallel.js +3 -6
- package/dist/utils/wrap-generators-parallel.js.map +1 -1
- package/dist/wasm-sandbox/host.d.ts +106 -0
- package/dist/wasm-sandbox/host.d.ts.map +1 -0
- package/dist/wasm-sandbox/host.js +409 -0
- package/dist/wasm-sandbox/host.js.map +1 -0
- package/dist/wasm-sandbox/workers.d.ts +43 -0
- package/dist/wasm-sandbox/workers.d.ts.map +1 -0
- package/dist/wasm-sandbox/workers.js +150 -0
- package/dist/wasm-sandbox/workers.js.map +1 -0
- package/dist/workspace-paths.d.ts +40 -0
- package/dist/workspace-paths.d.ts.map +1 -0
- package/dist/workspace-paths.js +102 -0
- package/dist/workspace-paths.js.map +1 -0
- package/dist/yt-dlp-gate.d.ts +19 -0
- package/dist/yt-dlp-gate.d.ts.map +1 -0
- package/dist/yt-dlp-gate.js +22 -0
- package/dist/yt-dlp-gate.js.map +1 -0
- package/package.json +112 -14
- package/dist/agent-executor.d.ts +0 -62
- package/dist/agent-executor.d.ts.map +0 -1
- package/dist/agent-executor.js +0 -324
- package/dist/agent-executor.js.map +0 -1
- package/dist/agent-step-executor.d.ts +0 -28
- package/dist/agent-step-executor.d.ts.map +0 -1
- package/dist/agent-step-executor.js +0 -99
- package/dist/agent-step-executor.js.map +0 -1
- package/dist/agent-workflow-runner.d.ts +0 -31
- package/dist/agent-workflow-runner.d.ts.map +0 -1
- package/dist/agent-workflow-runner.js +0 -124
- package/dist/agent-workflow-runner.js.map +0 -1
- package/dist/agent.d.ts +0 -115
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -587
- package/dist/agent.js.map +0 -1
- package/dist/base-agent.d.ts +0 -42
- package/dist/base-agent.d.ts.map +0 -1
- package/dist/base-agent.js +0 -29
- package/dist/base-agent.js.map +0 -1
- package/dist/graph-planner.d.ts +0 -56
- package/dist/graph-planner.d.ts.map +0 -1
- package/dist/graph-planner.js +0 -311
- package/dist/graph-planner.js.map +0 -1
- package/dist/multi-mode-agent.d.ts +0 -86
- package/dist/multi-mode-agent.d.ts.map +0 -1
- package/dist/multi-mode-agent.js +0 -476
- package/dist/multi-mode-agent.js.map +0 -1
- package/dist/prompts/graph-planner-prompt.d.ts +0 -48
- package/dist/prompts/graph-planner-prompt.d.ts.map +0 -1
- package/dist/prompts/graph-planner-prompt.js +0 -180
- package/dist/prompts/graph-planner-prompt.js.map +0 -1
- package/dist/reject-agentic-provider.d.ts +0 -21
- package/dist/reject-agentic-provider.d.ts.map +0 -1
- package/dist/reject-agentic-provider.js +0 -32
- package/dist/reject-agentic-provider.js.map +0 -1
- package/dist/simple-agent.d.ts +0 -29
- package/dist/simple-agent.d.ts.map +0 -1
- package/dist/simple-agent.js +0 -54
- package/dist/simple-agent.js.map +0 -1
- package/dist/sub-agent-planner.d.ts +0 -33
- package/dist/sub-agent-planner.d.ts.map +0 -1
- package/dist/sub-agent-planner.js +0 -203
- package/dist/sub-agent-planner.js.map +0 -1
- package/dist/team/db-task-board.d.ts +0 -54
- package/dist/team/db-task-board.d.ts.map +0 -1
- package/dist/team/db-task-board.js +0 -361
- package/dist/team/db-task-board.js.map +0 -1
- package/dist/team/edge-message-bus.d.ts +0 -51
- package/dist/team/edge-message-bus.d.ts.map +0 -1
- package/dist/team/edge-message-bus.js +0 -169
- package/dist/team/edge-message-bus.js.map +0 -1
- package/dist/team/edge-task-board.d.ts +0 -77
- package/dist/team/edge-task-board.d.ts.map +0 -1
- package/dist/team/edge-task-board.js +0 -140
- package/dist/team/edge-task-board.js.map +0 -1
- package/dist/team/index.d.ts +0 -16
- package/dist/team/index.d.ts.map +0 -1
- package/dist/team/index.js +0 -17
- package/dist/team/index.js.map +0 -1
- package/dist/team/message-bus.d.ts +0 -58
- package/dist/team/message-bus.d.ts.map +0 -1
- package/dist/team/message-bus.js +0 -151
- package/dist/team/message-bus.js.map +0 -1
- package/dist/team/task-board.d.ts +0 -106
- package/dist/team/task-board.d.ts.map +0 -1
- package/dist/team/task-board.js +0 -370
- package/dist/team/task-board.js.map +0 -1
- package/dist/team/team-executor.d.ts +0 -72
- package/dist/team/team-executor.d.ts.map +0 -1
- package/dist/team/team-executor.js +0 -466
- package/dist/team/team-executor.js.map +0 -1
- package/dist/team/team-tools.d.ts +0 -252
- package/dist/team/team-tools.d.ts.map +0 -1
- package/dist/team/team-tools.js +0 -412
- package/dist/team/team-tools.js.map +0 -1
- package/dist/team/types.d.ts +0 -198
- package/dist/team/types.d.ts.map +0 -1
- package/dist/team/types.js +0 -8
- package/dist/team/types.js.map +0 -1
- package/dist/tools/add-edge-tool.d.ts +0 -18
- package/dist/tools/add-edge-tool.d.ts.map +0 -1
- package/dist/tools/add-edge-tool.js +0 -92
- package/dist/tools/add-edge-tool.js.map +0 -1
- package/dist/tools/add-node-tool.d.ts +0 -22
- package/dist/tools/add-node-tool.d.ts.map +0 -1
- package/dist/tools/add-node-tool.js +0 -128
- package/dist/tools/add-node-tool.js.map +0 -1
- package/dist/tools/asset-tools.d.ts +0 -58
- package/dist/tools/asset-tools.d.ts.map +0 -1
- package/dist/tools/asset-tools.js +0 -191
- package/dist/tools/asset-tools.js.map +0 -1
- package/dist/tools/calculator-tool.d.ts +0 -24
- package/dist/tools/calculator-tool.d.ts.map +0 -1
- package/dist/tools/calculator-tool.js +0 -64
- package/dist/tools/calculator-tool.js.map +0 -1
- package/dist/tools/claude-code-tools.d.ts +0 -96
- package/dist/tools/claude-code-tools.d.ts.map +0 -1
- package/dist/tools/claude-code-tools.js +0 -463
- package/dist/tools/claude-code-tools.js.map +0 -1
- package/dist/tools/code-tools.d.ts +0 -26
- package/dist/tools/code-tools.d.ts.map +0 -1
- package/dist/tools/code-tools.js +0 -82
- package/dist/tools/code-tools.js.map +0 -1
- package/dist/tools/create-plan-tool.d.ts +0 -29
- package/dist/tools/create-plan-tool.d.ts.map +0 -1
- package/dist/tools/create-plan-tool.js +0 -355
- package/dist/tools/create-plan-tool.js.map +0 -1
- package/dist/tools/create-task-tool.d.ts +0 -28
- package/dist/tools/create-task-tool.d.ts.map +0 -1
- package/dist/tools/create-task-tool.js +0 -254
- package/dist/tools/create-task-tool.js.map +0 -1
- package/dist/tools/email-tools.d.ts +0 -76
- package/dist/tools/email-tools.d.ts.map +0 -1
- package/dist/tools/email-tools.js +0 -259
- package/dist/tools/email-tools.js.map +0 -1
- package/dist/tools/filesystem-tools.d.ts +0 -77
- package/dist/tools/filesystem-tools.d.ts.map +0 -1
- package/dist/tools/filesystem-tools.js +0 -334
- package/dist/tools/filesystem-tools.js.map +0 -1
- package/dist/tools/find-model-tool.d.ts +0 -25
- package/dist/tools/find-model-tool.d.ts.map +0 -1
- package/dist/tools/find-model-tool.js +0 -236
- package/dist/tools/find-model-tool.js.map +0 -1
- package/dist/tools/finish-graph-tool.d.ts +0 -23
- package/dist/tools/finish-graph-tool.d.ts.map +0 -1
- package/dist/tools/finish-graph-tool.js +0 -46
- package/dist/tools/finish-graph-tool.js.map +0 -1
- package/dist/tools/js-code-tool.d.ts +0 -30
- package/dist/tools/js-code-tool.d.ts.map +0 -1
- package/dist/tools/js-code-tool.js +0 -98
- package/dist/tools/js-code-tool.js.map +0 -1
- package/dist/tools/local-get-node-info-tool.d.ts +0 -19
- package/dist/tools/local-get-node-info-tool.d.ts.map +0 -1
- package/dist/tools/local-get-node-info-tool.js +0 -75
- package/dist/tools/local-get-node-info-tool.js.map +0 -1
- package/dist/tools/local-list-nodes-tool.d.ts +0 -19
- package/dist/tools/local-list-nodes-tool.d.ts.map +0 -1
- package/dist/tools/local-list-nodes-tool.js +0 -69
- package/dist/tools/local-list-nodes-tool.js.map +0 -1
- package/dist/tools/local-search-nodes-tool.d.ts +0 -21
- package/dist/tools/local-search-nodes-tool.d.ts.map +0 -1
- package/dist/tools/local-search-nodes-tool.js +0 -111
- package/dist/tools/local-search-nodes-tool.js.map +0 -1
- package/dist/tools/math-tools.d.ts +0 -156
- package/dist/tools/math-tools.d.ts.map +0 -1
- package/dist/tools/math-tools.js +0 -422
- package/dist/tools/math-tools.js.map +0 -1
- package/dist/tools/model-tools.d.ts +0 -25
- package/dist/tools/model-tools.d.ts.map +0 -1
- package/dist/tools/model-tools.js +0 -48
- package/dist/tools/model-tools.js.map +0 -1
- package/dist/tools/pdf-tools.d.ts +0 -152
- package/dist/tools/pdf-tools.d.ts.map +0 -1
- package/dist/tools/pdf-tools.js +0 -339
- package/dist/tools/pdf-tools.js.map +0 -1
- package/dist/tools/tool-registry.d.ts +0 -9
- package/dist/tools/tool-registry.d.ts.map +0 -1
- package/dist/tools/tool-registry.js +0 -17
- package/dist/tools/tool-registry.js.map +0 -1
- package/dist/tools/workspace-tools.d.ts +0 -58
- package/dist/tools/workspace-tools.d.ts.map +0 -1
- package/dist/tools/workspace-tools.js +0 -152
- package/dist/tools/workspace-tools.js.map +0 -1
package/dist/js-sandbox.js
CHANGED
|
@@ -1,27 +1,79 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared sandboxed JavaScript execution engine.
|
|
3
3
|
*
|
|
4
|
-
* Used by
|
|
4
|
+
* Used by the CodeAct executor (agent actions) and the CodeNode (workflow node).
|
|
5
5
|
* Runs user code in an isolated QuickJS WebAssembly context — the guest has its
|
|
6
6
|
* own heap inside the WASM instance, so there is a real memory/CPU boundary
|
|
7
7
|
* between host and guest (unlike Node's `node:vm`, which shares the V8 heap).
|
|
8
8
|
*
|
|
9
9
|
* The exposed surface is a small curated one: vanilla JavaScript plus a handful
|
|
10
|
-
* of bridge functions (`fetch`, `workspace`, `getSecret`, `
|
|
11
|
-
* `assetToSandbox`, `sandboxToAsset`, `console`
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
10
|
+
* of bridge functions (`fetch`, `workspace`, `getSecret`, `sleep`,
|
|
11
|
+
* `assetToSandbox`, `sandboxToAsset`, `crypto`, `console`, `progress`,
|
|
12
|
+
* `format`, `image`, `canvas`) and a few pure guest-side helpers
|
|
13
|
+
* (`toBase64`, `fromBase64`, `toHex`, `fromHex`,
|
|
14
|
+
* `parallelMap`, `createCanvas`). `crypto`
|
|
15
|
+
* covers `randomUUID`, `getRandomValues`, `digest`, and `hmac`
|
|
16
|
+
* (WebCrypto-backed); `workspace` covers text and binary reads/writes plus
|
|
17
|
+
* `stat`, `mkdir`, and `remove`; `progress` forwards a percentage and label to
|
|
18
|
+
* the host caller (rate-limited); `console.*` appends to the run's `logs`
|
|
19
|
+
* array and, when the caller set {@link RunSandboxOptions.onLog}, forwards
|
|
20
|
+
* each line live (capped); `format` exposes host `Intl` number, date,
|
|
21
|
+
* relative-time and list formatting, which QuickJS itself does not ship.
|
|
22
|
+
* Every quantitative limit (fetch calls, response body, output size,
|
|
23
|
+
* guest heap, stack, fetch timeout) is overridable per invocation through
|
|
24
|
+
* `RunSandboxOptions.limits`, clamped to hard ceilings.
|
|
25
|
+
*
|
|
26
|
+
* The guest imports nothing by default — without `RunSandboxOptions.modules`
|
|
27
|
+
* every specifier is denied by name, so user code cannot `import`/`require`
|
|
28
|
+
* anything. With it, the run's resolved sandbox packages and their intra-pack
|
|
29
|
+
* siblings are the only importable modules (see {@link createGuestModuleHost}),
|
|
30
|
+
* and dynamic `import()` stays denied.
|
|
31
|
+
*
|
|
32
|
+
* **Every library the sandbox offers is an importable module.** Some are guest
|
|
33
|
+
* modules the compiler admitted (js-yaml, date-fns); the rest are *host
|
|
34
|
+
* modules* — papaparse, cheerio, exceljs, fflate and friends — which run on the
|
|
35
|
+
* host behind a generated ESM facade over a per-run dispatcher
|
|
36
|
+
* (`host-modules/`), because they need Node builtins the guest lacks or carry a
|
|
37
|
+
* limit the guest could not enforce. Either way the guest writes `import`, and
|
|
38
|
+
* the pack has to be installed. The remaining globals — `fetch`,
|
|
39
|
+
* `workspace`, `getSecret`, the asset bridges, `format.*` (Intl), `crypto.*`
|
|
40
|
+
* (WebCrypto), `image.*`/`canvas.*` — are not libraries: they are the
|
|
41
|
+
* capabilities the node granted this run.
|
|
42
|
+
*
|
|
43
|
+
* The sandbox is fully asynchronous. Every host bridge call returns a real
|
|
44
|
+
* promise, and a bridge call starts its host-side work the moment it is
|
|
45
|
+
* invoked — not when it is awaited — so `Promise.all` (and `allSettled`,
|
|
46
|
+
* `race`, `any`) over bridge calls runs them in parallel on the host. Five
|
|
47
|
+
* fetches under `Promise.all` take one round trip, not five. `parallelMap`
|
|
48
|
+
* is the bounded form of that fan-out. `sleep` is the only timer: the
|
|
49
|
+
* timer globals (`setTimeout` and friends) are deleted inside the user-code
|
|
50
|
+
* module (see {@link wrapCode}), because timer callbacks would run outside
|
|
51
|
+
* the never-reject/abort-guard conventions every bridge follows.
|
|
18
52
|
*/
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
53
|
+
import { loadQuickJs } from "@sebastianwessel/quickjs";
|
|
54
|
+
import { SANDBOX_CAPABILITY_DISPATCH_GLOBAL, SANDBOX_WASM_DISPATCH_GLOBAL } from "@nodetool-ai/protocol";
|
|
55
|
+
import { createSandboxHostDispatcher } from "./host-modules/dispatcher.js";
|
|
56
|
+
import { EXPOSED_BRIDGE_NAMES, MAX_ENGINE_TIMEOUT_MS, NO_INPUT_STREAM_MESSAGE, registerTypedArraySerializers, runInterpreter, SANDBOX_CAPABILITY_BRIDGE_BINDING, SANDBOX_INPUT_TAKE_BINDING, SANDBOX_STREAM_OPEN_BINDING, SANDBOX_WASM_BRIDGE_BINDING } from "./js-sandbox-worker/interpreter.js";
|
|
57
|
+
import { SANDBOX_SERIALIZE_MAX_DEPTH, toGuestBytes, toGuestBytesDeep } from "./sandbox-bytes.js";
|
|
58
|
+
import { isBoolean, isFunction, isNonEmptyString, isNumber, isObjectLike, isRecord, isString } from "./utils/type-guards.js";
|
|
59
|
+
import { createSandboxMediaStore, isSandboxMediaHandle, MAX_RUN_MEDIA_BYTES } from "./sandbox-media-handle.js";
|
|
60
|
+
import { createMediaRefBridge, looksLikeMediaRef, mimeForRef, remapMediaRef, resolveRefBytes } from "./sandbox-media-ref.js";
|
|
61
|
+
import { createSandboxWasmDispatcher } from "./wasm-sandbox/host.js";
|
|
62
|
+
import { runInWorker } from "./js-sandbox-worker/host.js";
|
|
63
|
+
import { assertFetchUrlAllowed, assertResolvedHostAllowed } from "./network-guard.js";
|
|
64
|
+
import { GUEST_GLOBALS_JSON_BINDING, GUEST_GLOBALS_SIDECAR_BINDING, GUEST_MARSHAL_GLOBAL } from "./sandbox-json-transport.js";
|
|
65
|
+
import { MAX_RANDOM_BYTES } from "./sandbox-constants.js";
|
|
66
|
+
import { deriveBridgeShape, precheckCloneSafety, SANDBOX_DISPATCHER_BINDINGS } from "./js-sandbox-worker/protocol.js";
|
|
22
67
|
import * as quickJsVariantModule from "@jitl/quickjs-ng-wasmfile-release-sync";
|
|
68
|
+
/**
|
|
69
|
+
* The variant package is CJS with a `default` export, so `ns.default` is the
|
|
70
|
+
* variant at runtime.
|
|
71
|
+
*/
|
|
72
|
+
// SAFETY: TypeScript's CJS interop synthesizes `default` as the whole module
|
|
73
|
+
// object, contradicting the package's own `.d.ts`, which declares it as the
|
|
74
|
+
// `QuickJSSyncVariant` this reads.
|
|
23
75
|
const quickJsVariant = quickJsVariantModule.default;
|
|
24
|
-
import {
|
|
76
|
+
import { importNodeBuiltin } from "@nodetool-ai/config";
|
|
25
77
|
// ---------------------------------------------------------------------------
|
|
26
78
|
// Constants
|
|
27
79
|
// ---------------------------------------------------------------------------
|
|
@@ -34,44 +86,102 @@ export const MAX_RESPONSE_BODY_SIZE = 1_000_000;
|
|
|
34
86
|
export const GUEST_MEMORY_LIMIT = 64 * 1024 * 1024;
|
|
35
87
|
/** Guest stack cap — protects against deeply recursive code. */
|
|
36
88
|
export const GUEST_STACK_LIMIT = 512 * 1024;
|
|
89
|
+
/** Per-request wall-clock cap for the fetch bridge. */
|
|
90
|
+
export const FETCH_TIMEOUT_MS = 15_000;
|
|
91
|
+
/** Redirect hops the fetch bridge follows before giving up. */
|
|
92
|
+
export const MAX_FETCH_REDIRECTS = 5;
|
|
93
|
+
/** Locale used by the `format` bridge when the caller names none. */
|
|
94
|
+
export const DEFAULT_FORMAT_LOCALE = "en-US";
|
|
95
|
+
export { MAX_RANDOM_BYTES };
|
|
96
|
+
/** Progress reports forwarded to the host per run; further calls are dropped. */
|
|
97
|
+
export const MAX_PROGRESS_CALLS = 1000;
|
|
98
|
+
/** Console lines forwarded to {@link RunSandboxOptions.onLog} per run. */
|
|
99
|
+
export const MAX_CONSOLE_LOGS = 1000;
|
|
100
|
+
/** Longest console line forwarded to the host. A collected line is not truncated on its own. */
|
|
101
|
+
export const MAX_CONSOLE_LOG_CHARS = 4000;
|
|
102
|
+
/**
|
|
103
|
+
* Console lines the run's `logs` array collects. Forwarding was capped and
|
|
104
|
+
* collection was not: guest strings are not charged to the QuickJS heap, so a
|
|
105
|
+
* `console.log` in a hot loop grew host memory for the length of the action.
|
|
106
|
+
*/
|
|
107
|
+
export const MAX_COLLECTED_CONSOLE_LOGS = 5000;
|
|
108
|
+
/** Characters the run's `logs` array collects in total, across every line. */
|
|
109
|
+
export const MAX_COLLECTED_CONSOLE_CHARS = 512 * 1024;
|
|
110
|
+
/** The single line that closes a collected log the caps cut. */
|
|
111
|
+
export const CONSOLE_LOGS_CUT_NOTICE = `[console output cut: at most ${MAX_COLLECTED_CONSOLE_LOGS} lines / ` +
|
|
112
|
+
`${MAX_COLLECTED_CONSOLE_CHARS} characters are kept per run]`;
|
|
113
|
+
/**
|
|
114
|
+
* Emitted values a run may deliver. Unlike progress reports, emits are never
|
|
115
|
+
* dropped or rate-limited — every value reaches the host in call order — so the
|
|
116
|
+
* cap is enforced by throwing in the guest rather than by ignoring the call.
|
|
117
|
+
*/
|
|
118
|
+
export const MAX_EMIT_CALLS = 10_000;
|
|
119
|
+
/** Minimum gap between two forwarded progress reports. */
|
|
120
|
+
export const PROGRESS_MIN_INTERVAL_MS = 100;
|
|
121
|
+
/** Longest progress message forwarded to the host. */
|
|
122
|
+
export const MAX_PROGRESS_MESSAGE_CHARS = 500;
|
|
123
|
+
/**
|
|
124
|
+
* Headroom added to the engine's own wall-clock abort when a {@link SandboxClock}
|
|
125
|
+
* is in play. The clock only stops the *guest's* budget; this bounds how long a
|
|
126
|
+
* run may stay suspended in total, so a prompt nobody ever answers still ends.
|
|
127
|
+
*/
|
|
128
|
+
export const DEFAULT_SUSPEND_ALLOWANCE_MS = 30 * 60 * 1000;
|
|
129
|
+
/**
|
|
130
|
+
* Suspension allowance a run with an input stream gets by default.
|
|
131
|
+
*
|
|
132
|
+
* Effectively unbounded, deliberately: a streaming body legitimately lives as
|
|
133
|
+
* long as its upstream produces, and the run's own cancellation is the lifetime
|
|
134
|
+
* bound. A stream that never ends is the upstream's bug, surfaced by the
|
|
135
|
+
* runner — not a reason to kill a correct consumer. A caller that wants a
|
|
136
|
+
* shorter leash passes {@link RunSandboxOptions.suspendAllowanceMs}.
|
|
137
|
+
*/
|
|
138
|
+
export const INPUT_STREAM_SUSPEND_ALLOWANCE_MS = MAX_ENGINE_TIMEOUT_MS;
|
|
139
|
+
/**
|
|
140
|
+
* What the guest is told when it calls `stream` in a run that has no input
|
|
141
|
+
* stream behind it. Shared by the host bridge and the guest prelude so both
|
|
142
|
+
* paths say the same thing — the prelude lives with the interpreter, so the
|
|
143
|
+
* message does too.
|
|
144
|
+
*/
|
|
145
|
+
export { NO_INPUT_STREAM_MESSAGE } from "./js-sandbox-worker/interpreter.js";
|
|
146
|
+
/**
|
|
147
|
+
* Normalize a declared secret scope: a list of non-empty names, or `null` for
|
|
148
|
+
* an unscoped run. An empty declared list stays empty — it denies everything.
|
|
149
|
+
*/
|
|
150
|
+
function resolveSecretScope(scope) {
|
|
151
|
+
if (!Array.isArray(scope))
|
|
152
|
+
return null;
|
|
153
|
+
return scope
|
|
154
|
+
.filter((name) => typeof name === "string" && name !== "")
|
|
155
|
+
.map((name) => name.trim())
|
|
156
|
+
.filter((name) => name !== "");
|
|
157
|
+
}
|
|
158
|
+
function clampLimit(value, fallback, min, max) {
|
|
159
|
+
if (value == null || !Number.isFinite(value))
|
|
160
|
+
return fallback;
|
|
161
|
+
return Math.min(Math.max(Math.floor(value), min), max);
|
|
162
|
+
}
|
|
163
|
+
/** Apply defaults and ceilings to a caller-supplied {@link SandboxLimits}. */
|
|
164
|
+
export function resolveSandboxLimits(limits) {
|
|
165
|
+
return {
|
|
166
|
+
maxFetchCalls: clampLimit(limits?.maxFetchCalls, MAX_FETCH_CALLS, 0, 100),
|
|
167
|
+
maxResponseBodyBytes: clampLimit(limits?.maxResponseBodyBytes, MAX_RESPONSE_BODY_SIZE, 1024, 50 * 1024 * 1024),
|
|
168
|
+
maxOutputSize: clampLimit(limits?.maxOutputSize, MAX_OUTPUT_SIZE, 1024, 10 * 1024 * 1024),
|
|
169
|
+
memoryLimitBytes: clampLimit(limits?.memoryLimitBytes, GUEST_MEMORY_LIMIT, 1024 * 1024, 512 * 1024 * 1024),
|
|
170
|
+
runMediaBytes: clampLimit(limits?.runMediaBytes, MAX_RUN_MEDIA_BYTES, 1024 * 1024, 2 * 1024 * 1024 * 1024),
|
|
171
|
+
stackLimitBytes: clampLimit(limits?.stackLimitBytes, GUEST_STACK_LIMIT, 16 * 1024, 8 * 1024 * 1024),
|
|
172
|
+
fetchTimeoutMs: clampLimit(limits?.fetchTimeoutMs, FETCH_TIMEOUT_MS, 100, 120_000),
|
|
173
|
+
// Not clamped — these are capability switches, not magnitudes. Both
|
|
174
|
+
// resolve to the restrictive value unless the host explicitly opts out.
|
|
175
|
+
allowPrivateNetwork: limits?.allowPrivateNetwork === true,
|
|
176
|
+
userAgent: limits?.userAgent ?? "",
|
|
177
|
+
filesystemAccess: limits?.filesystemAccess === "host" ? "host" : "workspace",
|
|
178
|
+
secretScope: resolveSecretScope(limits?.secretScope)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
37
181
|
// ---------------------------------------------------------------------------
|
|
38
182
|
// Engine bootstrap — one WASM module shared by every invocation.
|
|
39
183
|
// ---------------------------------------------------------------------------
|
|
40
184
|
let enginePromise = null;
|
|
41
|
-
let serializersRegistered = false;
|
|
42
|
-
function registerTypedArraySerializers() {
|
|
43
|
-
if (serializersRegistered)
|
|
44
|
-
return;
|
|
45
|
-
serializersRegistered = true;
|
|
46
|
-
// Map every typed-array class to a native Uint8Array on the host side.
|
|
47
|
-
// The guest returns `new Uint8Array([...])` (or friends); without this,
|
|
48
|
-
// the library's generic object serializer produces a plain object with
|
|
49
|
-
// numeric keys, which downstream code (CodeNode's normalizeOutput) would
|
|
50
|
-
// miss when detecting binary values.
|
|
51
|
-
const typedArrayNames = [
|
|
52
|
-
"Uint8Array",
|
|
53
|
-
"Int8Array",
|
|
54
|
-
"Uint8ClampedArray",
|
|
55
|
-
"Int16Array",
|
|
56
|
-
"Uint16Array",
|
|
57
|
-
"Int32Array",
|
|
58
|
-
"Uint32Array",
|
|
59
|
-
"Float32Array",
|
|
60
|
-
"Float64Array"
|
|
61
|
-
];
|
|
62
|
-
for (const name of typedArrayNames) {
|
|
63
|
-
addSerializer(name, (ctx, handle) => {
|
|
64
|
-
const bufferHandle = ctx.getProp(handle, "buffer");
|
|
65
|
-
try {
|
|
66
|
-
const ab = ctx.getArrayBuffer(bufferHandle);
|
|
67
|
-
return Uint8Array.from(ab.value);
|
|
68
|
-
}
|
|
69
|
-
finally {
|
|
70
|
-
bufferHandle.dispose();
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
185
|
function getEngine() {
|
|
76
186
|
registerTypedArraySerializers();
|
|
77
187
|
if (!enginePromise) {
|
|
@@ -80,31 +190,82 @@ function getEngine() {
|
|
|
80
190
|
return enginePromise;
|
|
81
191
|
}
|
|
82
192
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
193
|
+
* The guest engine can fail in two ways that never reach `runInSandbox`'s own
|
|
194
|
+
* `await`, and both used to end the same way: the host process died, the tool
|
|
195
|
+
* call it was serving returned *nothing*, and the agent driving it saw a turn
|
|
196
|
+
* that simply stopped. A missing result is worse than any error — there is
|
|
197
|
+
* nothing to read and nothing to retry.
|
|
198
|
+
*
|
|
199
|
+
* 1. An Emscripten `abort()` (the `list_empty(&rt->gc_obj_list)` assertion in
|
|
200
|
+
* `JS_FreeRuntime`) surfaces as a `RuntimeError` from the WASM module.
|
|
201
|
+
* 2. A marshaling failure — most often guest OOM while a host return value is
|
|
202
|
+
* being written into the guest — throws inside a promise continuation the
|
|
203
|
+
* library created and never catches, so it lands as an unhandled rejection.
|
|
86
204
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* quickjs-ng when the runtime is freed. Routing failures through a resolved
|
|
91
|
-
* tagged value sidesteps the leak while preserving the guest-visible
|
|
92
|
-
* behaviour (the user still gets a thrown Error with name + message).
|
|
205
|
+
* `guardHostProcess` races the run against those escapes. A rejection that
|
|
206
|
+
* looks like the engine's fails the run; anything else is re-thrown on the next
|
|
207
|
+
* tick so a genuine host bug still crashes the way it should.
|
|
93
208
|
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
209
|
+
function isEngineFailure(error) {
|
|
210
|
+
const name = error?.constructor?.name ?? "";
|
|
211
|
+
const message = String(error?.message ?? error);
|
|
212
|
+
const stack = String(error?.stack ?? "");
|
|
213
|
+
return (name.startsWith("QuickJS") ||
|
|
214
|
+
message.includes("Assertion failed") ||
|
|
215
|
+
message.includes("gc_obj_list") ||
|
|
216
|
+
/\bAborted\(/.test(message) ||
|
|
217
|
+
stack.includes("quickjs"));
|
|
218
|
+
}
|
|
219
|
+
/** The agent-facing translation of an engine failure. */
|
|
220
|
+
export function describeEngineFailure(error) {
|
|
221
|
+
const message = String(error?.message ?? error);
|
|
222
|
+
if (/gc_obj_list|Assertion failed|\bAborted\(/.test(message)) {
|
|
223
|
+
return ("The JavaScript sandbox runtime aborted while cleaning up, so this " +
|
|
224
|
+
"action produced no result. It is usually triggered by moving a large " +
|
|
225
|
+
"amount of binary data across the sandbox boundary in one run. Retry " +
|
|
226
|
+
"with smaller pieces — process one image at a time, or hand large " +
|
|
227
|
+
"payloads between steps as assets instead of as bytes.");
|
|
228
|
+
}
|
|
229
|
+
if (/out of memory/i.test(message)) {
|
|
230
|
+
return ("The JavaScript sandbox ran out of guest memory, so this action " +
|
|
231
|
+
"produced no result. Retry with less data held live at once.");
|
|
232
|
+
}
|
|
233
|
+
return message;
|
|
234
|
+
}
|
|
235
|
+
async function guardHostProcess(run) {
|
|
236
|
+
// `process` is a Node global; the browser bundle (the in-process fallback
|
|
237
|
+
// path there) has none, and there is no host process to guard against an
|
|
238
|
+
// engine abort escaping as an unhandled rejection anyway.
|
|
239
|
+
const nodeProcess = globalThis.process;
|
|
240
|
+
if (!nodeProcess?.on || !nodeProcess?.off) {
|
|
241
|
+
return run;
|
|
242
|
+
}
|
|
243
|
+
let onRejection;
|
|
244
|
+
const escaped = new Promise((_resolve, reject) => {
|
|
245
|
+
onRejection = (reason) => {
|
|
246
|
+
if (isEngineFailure(reason)) {
|
|
247
|
+
reject(reason instanceof Error ? reason : new Error(String(reason)));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
// Not ours. Restore the default "unhandled rejection crashes the
|
|
251
|
+
// process" behaviour rather than silently swallowing someone's bug.
|
|
252
|
+
setImmediate(() => {
|
|
253
|
+
throw reason;
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
nodeProcess.on("unhandledRejection", onRejection);
|
|
257
|
+
});
|
|
258
|
+
try {
|
|
259
|
+
return await Promise.race([run, escaped]);
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
if (onRejection)
|
|
263
|
+
nodeProcess.off("unhandledRejection", onRejection);
|
|
264
|
+
// The loser of the race stays pending forever; make sure neither promise
|
|
265
|
+
// can later re-trigger the default handler.
|
|
266
|
+
void escaped.catch(() => { });
|
|
267
|
+
void run.then?.(() => { }, () => { });
|
|
268
|
+
}
|
|
108
269
|
}
|
|
109
270
|
// ---------------------------------------------------------------------------
|
|
110
271
|
// Helpers
|
|
@@ -114,12 +275,157 @@ export function truncate(text, max) {
|
|
|
114
275
|
return text;
|
|
115
276
|
return text.slice(0, max) + "\n...[truncated]";
|
|
116
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Load `node:fs/promises` via a bundler-hidden import so a browser/edge bundle
|
|
280
|
+
* never tries to resolve it. The `workspace.*` bridge is wired only when a
|
|
281
|
+
* `ProcessingContext` is present, and these loaders run only when guest code
|
|
282
|
+
* actually calls `workspace.read/write/list` — off-Node that path throws
|
|
283
|
+
* (`importNodeBuiltin` resolves `null`), which `neverReject` surfaces to the
|
|
284
|
+
* guest as a normal error.
|
|
285
|
+
*/
|
|
286
|
+
async function loadFsPromises() {
|
|
287
|
+
const fs = await importNodeBuiltin("node:fs/promises");
|
|
288
|
+
if (!fs)
|
|
289
|
+
throw new Error("workspace file access requires a Node.js runtime");
|
|
290
|
+
return fs;
|
|
291
|
+
}
|
|
292
|
+
async function loadNodePath() {
|
|
293
|
+
const nodePath = await importNodeBuiltin("node:path");
|
|
294
|
+
if (!nodePath) {
|
|
295
|
+
throw new Error("workspace file access requires a Node.js runtime");
|
|
296
|
+
}
|
|
297
|
+
return nodePath;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Verify a workspace path's real (symlink-resolved) location stays within the
|
|
301
|
+
* real workspace root before an fs operation. resolveWorkspacePath only checks
|
|
302
|
+
* containment lexically, so an in-workspace symlink pointing outside the root
|
|
303
|
+
* would otherwise be dereferenced (arbitrary host file read/write). For a write
|
|
304
|
+
* to a not-yet-existing file, the parent directory is checked instead. Throws
|
|
305
|
+
* on an escape.
|
|
306
|
+
*
|
|
307
|
+
* TOCTOU: the realpath check and the fs op that follows it are separate awaits,
|
|
308
|
+
* and guest bridge calls run concurrently (Promise.all), so an in-workspace
|
|
309
|
+
* symlink could be swapped to point outside between this check and the caller's
|
|
310
|
+
* read/write. Keeping this the last step before the op narrows the window but
|
|
311
|
+
* does not close it — a full fix needs fd-based ops (O_NOFOLLOW / openat), which
|
|
312
|
+
* node:fs/promises does not expose. Accepted as low-risk: it needs a local
|
|
313
|
+
* attacker racing a symlink swap inside the workspace, and both surfaces here
|
|
314
|
+
* (Code node, CodeAct actions) run first-party or already-trusted code.
|
|
315
|
+
*/
|
|
316
|
+
async function assertWorkspaceContained(context, fs, nodePath, fullPath, isWrite) {
|
|
317
|
+
const within = async (root, candidate) => {
|
|
318
|
+
try {
|
|
319
|
+
const realRoot = await fs.realpath(root);
|
|
320
|
+
const realCandidate = await fs.realpath(candidate);
|
|
321
|
+
if (realRoot === realCandidate)
|
|
322
|
+
return true;
|
|
323
|
+
const rel = nodePath.relative(realRoot, realCandidate);
|
|
324
|
+
return rel !== "" && !rel.startsWith("..") && !nodePath.isAbsolute(rel);
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
const root = context.resolveWorkspacePath(".");
|
|
331
|
+
if (await within(root, fullPath))
|
|
332
|
+
return;
|
|
333
|
+
if (isWrite) {
|
|
334
|
+
// Target may not exist yet — lstat detects a dangling symlink entry, and
|
|
335
|
+
// otherwise the nearest existing ancestor must be inside the workspace.
|
|
336
|
+
// Walking up covers writes and mkdirs that create several levels at once;
|
|
337
|
+
// path segments that don't exist yet cannot be symlinks, so nothing along
|
|
338
|
+
// the way can redirect the write.
|
|
339
|
+
try {
|
|
340
|
+
await fs.lstat(fullPath);
|
|
341
|
+
// Exists (real file or symlink) but failed containment → outside root.
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
let dir = nodePath.dirname(fullPath);
|
|
345
|
+
for (;;) {
|
|
346
|
+
let exists = true;
|
|
347
|
+
try {
|
|
348
|
+
await fs.lstat(dir);
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
exists = false;
|
|
352
|
+
}
|
|
353
|
+
if (exists) {
|
|
354
|
+
if (await within(root, dir))
|
|
355
|
+
return;
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
const parent = nodePath.dirname(dir);
|
|
359
|
+
if (parent === dir)
|
|
360
|
+
break;
|
|
361
|
+
dir = parent;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
throw new Error(`workspace path resolves outside the workspace: ${fullPath}`);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* The media engine behind the `image` and `canvas` bridges. Loaded on first
|
|
369
|
+
* use like the host modules, and for the same reasons — the canvas backend
|
|
370
|
+
* (Skia on Node) is heavy, and nothing that never draws should pay for it.
|
|
371
|
+
* Unlike those, the import is intra-package, so every bundler resolves it.
|
|
372
|
+
*/
|
|
373
|
+
async function loadSandboxMedia() {
|
|
374
|
+
return import("./sandbox-media.js");
|
|
375
|
+
}
|
|
376
|
+
/** Audio/video adapters are loaded only when a run uses those namespaces. */
|
|
377
|
+
async function loadSandboxAvMedia() {
|
|
378
|
+
return import("./sandbox-av-media.js");
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Read a fetch Response body into a Uint8Array bounded by `maxBytes`. Aborts the
|
|
382
|
+
* transfer via `controller` once the cap is exceeded so an oversized/fast
|
|
383
|
+
* response cannot buffer unbounded host memory.
|
|
384
|
+
*/
|
|
385
|
+
async function readBodyCapped(response, controller, maxBytes) {
|
|
386
|
+
if (!response.body) {
|
|
387
|
+
// No stream (e.g. empty body) — fall back, then clamp.
|
|
388
|
+
const buf = new Uint8Array(await response.arrayBuffer());
|
|
389
|
+
return buf.length > maxBytes ? buf.slice(0, maxBytes) : buf;
|
|
390
|
+
}
|
|
391
|
+
const reader = response.body.getReader();
|
|
392
|
+
const chunks = [];
|
|
393
|
+
let total = 0;
|
|
394
|
+
try {
|
|
395
|
+
for (;;) {
|
|
396
|
+
const { done, value } = await reader.read();
|
|
397
|
+
if (done)
|
|
398
|
+
break;
|
|
399
|
+
if (!value)
|
|
400
|
+
continue;
|
|
401
|
+
chunks.push(value);
|
|
402
|
+
total += value.byteLength;
|
|
403
|
+
if (total >= maxBytes) {
|
|
404
|
+
controller.abort();
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
// Aborted or network error mid-read — return what we have.
|
|
411
|
+
}
|
|
412
|
+
const out = new Uint8Array(Math.min(total, maxBytes));
|
|
413
|
+
let offset = 0;
|
|
414
|
+
for (const c of chunks) {
|
|
415
|
+
if (offset >= out.length)
|
|
416
|
+
break;
|
|
417
|
+
const take = Math.min(c.byteLength, out.length - offset);
|
|
418
|
+
out.set(c.subarray(0, take), offset);
|
|
419
|
+
offset += take;
|
|
420
|
+
}
|
|
421
|
+
return out;
|
|
422
|
+
}
|
|
117
423
|
function formatArg(arg) {
|
|
118
424
|
if (arg === null)
|
|
119
425
|
return "null";
|
|
120
426
|
if (arg === undefined)
|
|
121
427
|
return "undefined";
|
|
122
|
-
if (
|
|
428
|
+
if (isString(arg))
|
|
123
429
|
return arg;
|
|
124
430
|
try {
|
|
125
431
|
return JSON.stringify(arg, null, 2);
|
|
@@ -129,15 +435,20 @@ function formatArg(arg) {
|
|
|
129
435
|
}
|
|
130
436
|
}
|
|
131
437
|
function isTypedArray(value) {
|
|
132
|
-
if (!value
|
|
438
|
+
if (!isObjectLike(value))
|
|
133
439
|
return false;
|
|
134
440
|
const name = value.constructor?.name;
|
|
135
|
-
return name === "Uint8Array" ||
|
|
136
|
-
name === "
|
|
137
|
-
name === "
|
|
138
|
-
name === "
|
|
139
|
-
name === "
|
|
140
|
-
name === "
|
|
441
|
+
return (name === "Uint8Array" ||
|
|
442
|
+
name === "Buffer" ||
|
|
443
|
+
name === "Int8Array" ||
|
|
444
|
+
name === "Uint8ClampedArray" ||
|
|
445
|
+
name === "Int16Array" ||
|
|
446
|
+
name === "Uint16Array" ||
|
|
447
|
+
name === "Int32Array" ||
|
|
448
|
+
name === "Uint32Array" ||
|
|
449
|
+
name === "Float32Array" ||
|
|
450
|
+
name === "Float64Array" ||
|
|
451
|
+
name === "ArrayBuffer");
|
|
141
452
|
}
|
|
142
453
|
function toNativeUint8Array(value) {
|
|
143
454
|
const v = value;
|
|
@@ -147,54 +458,128 @@ function toNativeUint8Array(value) {
|
|
|
147
458
|
arr[i] = v[i] ?? 0;
|
|
148
459
|
return arr;
|
|
149
460
|
}
|
|
461
|
+
const DIGEST_ALGORITHMS = {
|
|
462
|
+
"SHA-1": "SHA-1",
|
|
463
|
+
SHA1: "SHA-1",
|
|
464
|
+
"SHA-256": "SHA-256",
|
|
465
|
+
SHA256: "SHA-256",
|
|
466
|
+
"SHA-384": "SHA-384",
|
|
467
|
+
SHA384: "SHA-384",
|
|
468
|
+
"SHA-512": "SHA-512",
|
|
469
|
+
SHA512: "SHA-512"
|
|
470
|
+
};
|
|
471
|
+
function normalizeDigestAlgorithm(algorithm) {
|
|
472
|
+
const key = String(algorithm ?? "")
|
|
473
|
+
.trim()
|
|
474
|
+
.toUpperCase();
|
|
475
|
+
const resolved = DIGEST_ALGORITHMS[key];
|
|
476
|
+
if (!resolved) {
|
|
477
|
+
throw new Error(`crypto: unsupported algorithm "${String(algorithm)}" (use SHA-1, SHA-256, SHA-384 or SHA-512)`);
|
|
478
|
+
}
|
|
479
|
+
return resolved;
|
|
480
|
+
}
|
|
481
|
+
/** Accept a guest string or byte-like value as host bytes. */
|
|
482
|
+
function coerceBytesInput(value, label) {
|
|
483
|
+
if (isString(value))
|
|
484
|
+
return new TextEncoder().encode(value);
|
|
485
|
+
if (value instanceof Uint8Array)
|
|
486
|
+
return Uint8Array.from(value);
|
|
487
|
+
if (isTypedArray(value))
|
|
488
|
+
return toNativeUint8Array(value);
|
|
489
|
+
if (Array.isArray(value))
|
|
490
|
+
return Uint8Array.from(value.map(Number));
|
|
491
|
+
if (isObjectLike(value) && isNumber(value.length)) {
|
|
492
|
+
return toNativeUint8Array(value);
|
|
493
|
+
}
|
|
494
|
+
throw new Error(`crypto: ${label} must be a string or Uint8Array`);
|
|
495
|
+
}
|
|
496
|
+
function webCryptoSubtle() {
|
|
497
|
+
const subtle = globalThis.crypto?.subtle;
|
|
498
|
+
if (!subtle) {
|
|
499
|
+
throw new Error("crypto: WebCrypto (crypto.subtle) is not available");
|
|
500
|
+
}
|
|
501
|
+
return subtle;
|
|
502
|
+
}
|
|
503
|
+
/** Depth ceiling for the binary-preserving walk — guards pathological nesting. */
|
|
504
|
+
const SERIALIZE_MAX_DEPTH = 32;
|
|
505
|
+
/**
|
|
506
|
+
* True if a typed array sits anywhere inside `value`. Cycle-safe: a revisited
|
|
507
|
+
* object reports false, which drops the value onto the JSON path, where
|
|
508
|
+
* `JSON.stringify` throws on the cycle and the caller falls back to `String` —
|
|
509
|
+
* the behavior cyclic values already had.
|
|
510
|
+
*/
|
|
511
|
+
function containsTypedArray(value, depth = 0, seen = new WeakSet()) {
|
|
512
|
+
if (isTypedArray(value))
|
|
513
|
+
return true;
|
|
514
|
+
if (!isObjectLike(value))
|
|
515
|
+
return false;
|
|
516
|
+
if (depth >= SERIALIZE_MAX_DEPTH)
|
|
517
|
+
return false;
|
|
518
|
+
const obj = value;
|
|
519
|
+
if (seen.has(obj))
|
|
520
|
+
return false;
|
|
521
|
+
seen.add(obj);
|
|
522
|
+
const values = Array.isArray(value)
|
|
523
|
+
? value
|
|
524
|
+
: Object.values(value);
|
|
525
|
+
return values.some((v) => containsTypedArray(v, depth + 1, seen));
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Rebuild `value`, converting every typed array at any depth to a native
|
|
529
|
+
* Uint8Array and leaving everything else structurally intact.
|
|
530
|
+
*/
|
|
531
|
+
function convertTypedArraysDeep(value, depth = 0, seen = new WeakSet()) {
|
|
532
|
+
if (isTypedArray(value))
|
|
533
|
+
return toNativeUint8Array(value);
|
|
534
|
+
if (!isObjectLike(value))
|
|
535
|
+
return value;
|
|
536
|
+
if (depth >= SERIALIZE_MAX_DEPTH)
|
|
537
|
+
return value;
|
|
538
|
+
const obj = value;
|
|
539
|
+
if (seen.has(obj))
|
|
540
|
+
return null;
|
|
541
|
+
seen.add(obj);
|
|
542
|
+
if (Array.isArray(value)) {
|
|
543
|
+
return value.map((v) => convertTypedArraysDeep(v, depth + 1, seen));
|
|
544
|
+
}
|
|
545
|
+
const out = {};
|
|
546
|
+
for (const [k, v] of Object.entries(value)) {
|
|
547
|
+
out[k] = convertTypedArraysDeep(v, depth + 1, seen);
|
|
548
|
+
}
|
|
549
|
+
return out;
|
|
550
|
+
}
|
|
150
551
|
/**
|
|
151
552
|
* Recursively serialize a value returned from the sandbox, converting typed
|
|
152
553
|
* arrays to native Uint8Array and enforcing output size limits.
|
|
153
554
|
*/
|
|
154
|
-
export function serializeResult(result) {
|
|
555
|
+
export function serializeResult(result, maxOutputSize = MAX_OUTPUT_SIZE) {
|
|
155
556
|
if (result === undefined)
|
|
156
557
|
return null;
|
|
157
558
|
if (result === null)
|
|
158
559
|
return null;
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (typeof result === "string" && result.length > MAX_OUTPUT_SIZE) {
|
|
163
|
-
return truncate(result, MAX_OUTPUT_SIZE);
|
|
560
|
+
if (isString(result) || isNumber(result) || isBoolean(result)) {
|
|
561
|
+
if (isString(result) && result.length > maxOutputSize) {
|
|
562
|
+
return truncate(result, maxOutputSize);
|
|
164
563
|
}
|
|
165
564
|
return result;
|
|
166
565
|
}
|
|
167
566
|
if (isTypedArray(result)) {
|
|
168
567
|
return toNativeUint8Array(result);
|
|
169
568
|
}
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
if (hasBinary) {
|
|
184
|
-
if (Array.isArray(result)) {
|
|
185
|
-
return result.map(v => isTypedArray(v) ? toNativeUint8Array(v) : v);
|
|
186
|
-
}
|
|
187
|
-
const obj = result;
|
|
188
|
-
const out = {};
|
|
189
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
190
|
-
out[k] = isTypedArray(v) ? toNativeUint8Array(v) : v;
|
|
191
|
-
}
|
|
192
|
-
return out;
|
|
569
|
+
if (isObjectLike(result)) {
|
|
570
|
+
// The scan and the rebuild are both deep. They used to look one level in,
|
|
571
|
+
// so a typed array nested any deeper fell through to the JSON path below —
|
|
572
|
+
// and `JSON.stringify(new Uint8Array([137, 80]))` is `{"0":137,"1":80}`,
|
|
573
|
+
// which is lossy and indistinguishable from a user's own integer map. The
|
|
574
|
+
// streaming path hit this every time: `genProcess` returns an array of
|
|
575
|
+
// yielded objects, so the bytes are always at depth 2.
|
|
576
|
+
if (containsTypedArray(result)) {
|
|
577
|
+
return convertTypedArraysDeep(result);
|
|
193
578
|
}
|
|
194
579
|
try {
|
|
195
580
|
const json = JSON.stringify(result);
|
|
196
|
-
if (json.length >
|
|
197
|
-
return truncate(json,
|
|
581
|
+
if (json.length > maxOutputSize) {
|
|
582
|
+
return truncate(json, maxOutputSize);
|
|
198
583
|
}
|
|
199
584
|
return JSON.parse(json);
|
|
200
585
|
}
|
|
@@ -233,50 +618,253 @@ export function cleanStack(stack) {
|
|
|
233
618
|
*
|
|
234
619
|
* @param context Optional ProcessingContext — when provided, enables
|
|
235
620
|
* `workspace.*` and `getSecret()` APIs.
|
|
621
|
+
* @param signal Optional external cancellation. Aborts in-flight host
|
|
622
|
+
* operations and makes `sleep` return immediately.
|
|
623
|
+
* @param limits Optional per-invocation limit overrides, clamped by
|
|
624
|
+
* {@link resolveSandboxLimits}.
|
|
625
|
+
* @param onProgress Optional sink for guest `progress()` calls. Without one the
|
|
626
|
+
* guest function is a no-op.
|
|
627
|
+
* @param onLog Optional sink for guest `console.*` calls. Without one the
|
|
628
|
+
* lines still land in {@link SandboxResult}'s `getLogs`.
|
|
629
|
+
* @param onEmit Optional sink for guest `emit()` calls. Without one the values
|
|
630
|
+
* are collected and handed back through `getEmitted`.
|
|
631
|
+
* @param streams Optional input bridges behind the guest `stream` global.
|
|
632
|
+
* Without an `onTakeInput` every `stream` call throws
|
|
633
|
+
* {@link NO_INPUT_STREAM_MESSAGE}.
|
|
634
|
+
* @param resolveMediaRef Optional host loader for media refs when this run
|
|
635
|
+
* has no ProcessingContext (chat). `image.*` uses it so a
|
|
636
|
+
* generation result can go straight into the next op.
|
|
236
637
|
*/
|
|
237
|
-
export function buildSandbox(context) {
|
|
638
|
+
export function buildSandbox(context, signal, limits, onProgress, onLog, onEmit, streams, resolveMediaRef, promoteMedia, resolveHost) {
|
|
238
639
|
const logs = [];
|
|
640
|
+
const resolvedLimits = resolveSandboxLimits(limits);
|
|
239
641
|
let fetchCount = 0;
|
|
642
|
+
let forwardedLogs = 0;
|
|
643
|
+
let collectedChars = 0;
|
|
644
|
+
let collectionCut = false;
|
|
645
|
+
// Bounded by count and by bytes; the cut is noted once, then nothing more
|
|
646
|
+
// is kept. A line that does not fit whole keeps the head that does.
|
|
647
|
+
const collectLog = (entry) => {
|
|
648
|
+
if (collectionCut)
|
|
649
|
+
return;
|
|
650
|
+
const remaining = MAX_COLLECTED_CONSOLE_CHARS - collectedChars;
|
|
651
|
+
if (logs.length < MAX_COLLECTED_CONSOLE_LOGS && entry.length <= remaining) {
|
|
652
|
+
logs.push(entry);
|
|
653
|
+
collectedChars += entry.length;
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (logs.length < MAX_COLLECTED_CONSOLE_LOGS && remaining > 0) {
|
|
657
|
+
logs.push(entry.slice(0, remaining));
|
|
658
|
+
}
|
|
659
|
+
collectionCut = true;
|
|
660
|
+
logs.push(CONSOLE_LOGS_CUT_NOTICE);
|
|
661
|
+
};
|
|
662
|
+
const pushConsole = (level, prefix, args) => {
|
|
663
|
+
const line = args.map(formatArg).join(" ");
|
|
664
|
+
collectLog(prefix + line);
|
|
665
|
+
if (!onLog || signal?.aborted)
|
|
666
|
+
return;
|
|
667
|
+
if (forwardedLogs >= MAX_CONSOLE_LOGS)
|
|
668
|
+
return;
|
|
669
|
+
forwardedLogs++;
|
|
670
|
+
const message = line.length > MAX_CONSOLE_LOG_CHARS
|
|
671
|
+
? line.slice(0, MAX_CONSOLE_LOG_CHARS)
|
|
672
|
+
: line;
|
|
673
|
+
try {
|
|
674
|
+
onLog(level, message);
|
|
675
|
+
}
|
|
676
|
+
catch {
|
|
677
|
+
// A failing host sink must not take the guest run down with it.
|
|
678
|
+
}
|
|
679
|
+
};
|
|
240
680
|
const console = {
|
|
241
681
|
log: (...args) => {
|
|
242
|
-
|
|
682
|
+
pushConsole("log", "", args);
|
|
243
683
|
},
|
|
244
684
|
warn: (...args) => {
|
|
245
|
-
|
|
685
|
+
pushConsole("warn", "[warn] ", args);
|
|
246
686
|
},
|
|
247
687
|
error: (...args) => {
|
|
248
|
-
|
|
688
|
+
pushConsole("error", "[error] ", args);
|
|
249
689
|
},
|
|
250
690
|
info: (...args) => {
|
|
251
|
-
|
|
691
|
+
pushConsole("info", "[info] ", args);
|
|
252
692
|
}
|
|
253
693
|
};
|
|
694
|
+
// The literal check above answers a hostname with "not an IP"; this one
|
|
695
|
+
// resolves it, so `metadata.example.com` → 169.254.169.254 is refused
|
|
696
|
+
// before a socket opens, on the first request and on every redirect hop.
|
|
697
|
+
// Node only: off Node the default resolver has no `dns` and answers
|
|
698
|
+
// nothing, and the browser's own network layer is the boundary there. One
|
|
699
|
+
// lookup per hostname per run — the guard is a layer, not the boundary (the
|
|
700
|
+
// socket resolves again), so re-resolving every call buys no rebinding
|
|
701
|
+
// protection and would cost a round trip per parallel fetch.
|
|
702
|
+
const onNodeHost = isString(globalThis.process?.versions?.node);
|
|
703
|
+
const resolvedHosts = new Map();
|
|
704
|
+
const assertHostResolvesAllowed = async (target) => {
|
|
705
|
+
if (!onNodeHost || resolvedLimits.allowPrivateNetwork)
|
|
706
|
+
return;
|
|
707
|
+
const host = new URL(target).hostname;
|
|
708
|
+
let pending = resolvedHosts.get(host);
|
|
709
|
+
if (pending === undefined) {
|
|
710
|
+
pending =
|
|
711
|
+
resolveHost === undefined
|
|
712
|
+
? assertResolvedHostAllowed(target, "fetch")
|
|
713
|
+
: assertResolvedHostAllowed(target, "fetch", resolveHost);
|
|
714
|
+
resolvedHosts.set(host, pending);
|
|
715
|
+
}
|
|
716
|
+
await pending;
|
|
717
|
+
};
|
|
254
718
|
const sandboxedFetch = async (url, options) => {
|
|
255
719
|
fetchCount++;
|
|
256
|
-
if (fetchCount >
|
|
257
|
-
throw new Error(`Fetch limit exceeded (max ${
|
|
720
|
+
if (fetchCount > resolvedLimits.maxFetchCalls) {
|
|
721
|
+
throw new Error(`Fetch limit exceeded (max ${resolvedLimits.maxFetchCalls} requests per execution)`);
|
|
258
722
|
}
|
|
259
|
-
if (
|
|
723
|
+
if (!isNonEmptyString(url)) {
|
|
260
724
|
throw new Error("fetch: url must be a non-empty string");
|
|
261
725
|
}
|
|
726
|
+
// SSRF guard: untrusted guest code must not reach loopback, link-local
|
|
727
|
+
// (incl. cloud metadata 169.254.169.254), or private ranges, nor non-http
|
|
728
|
+
// schemes. This is the literal check; the resolved-address check follows.
|
|
729
|
+
// DNS rebinding between the check and the socket is out of scope.
|
|
730
|
+
// The host can waive the address check for a node that replaces a trusted
|
|
731
|
+
// `lib.http` node; the scheme check is never waived.
|
|
732
|
+
assertFetchUrlAllowed(url, resolvedLimits.allowPrivateNetwork);
|
|
733
|
+
await assertHostResolvesAllowed(url);
|
|
262
734
|
const controller = new AbortController();
|
|
263
|
-
const timer = setTimeout(() => controller.abort(),
|
|
735
|
+
const timer = setTimeout(() => controller.abort(), resolvedLimits.fetchTimeoutMs);
|
|
736
|
+
// Cancelling the run kills an in-flight request rather than waiting out
|
|
737
|
+
// the per-request timeout.
|
|
738
|
+
const onExternalAbort = () => controller.abort();
|
|
739
|
+
if (signal?.aborted)
|
|
740
|
+
controller.abort();
|
|
741
|
+
else
|
|
742
|
+
signal?.addEventListener("abort", onExternalAbort, { once: true });
|
|
264
743
|
try {
|
|
265
744
|
const fetchOptions = {
|
|
266
745
|
method: options?.method ?? "GET",
|
|
267
746
|
signal: controller.signal
|
|
268
747
|
};
|
|
269
|
-
|
|
270
|
-
|
|
748
|
+
const requestHeaders = {};
|
|
749
|
+
// Host default first so a guest-set User-Agent still wins.
|
|
750
|
+
if (resolvedLimits.userAgent) {
|
|
751
|
+
requestHeaders["User-Agent"] = resolvedLimits.userAgent;
|
|
752
|
+
}
|
|
753
|
+
if (isObjectLike(options?.headers)) {
|
|
754
|
+
Object.assign(requestHeaders, options.headers);
|
|
755
|
+
}
|
|
756
|
+
if (Object.keys(requestHeaders).length > 0) {
|
|
757
|
+
fetchOptions.headers = requestHeaders;
|
|
271
758
|
}
|
|
272
759
|
if (options?.body !== undefined) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
760
|
+
const body = options.body;
|
|
761
|
+
if (isString(body)) {
|
|
762
|
+
fetchOptions.body = body;
|
|
763
|
+
}
|
|
764
|
+
else if (isTypedArray(body)) {
|
|
765
|
+
// Binary request body. A guest Uint8Array reaches the host as a
|
|
766
|
+
// native one via the typed-array serializers, but normalize anyway
|
|
767
|
+
// so a numeric-keyed object is sent as raw bytes, not as JSON.
|
|
768
|
+
// SAFETY: `BodyInit`'s view member is pinned to a plain
|
|
769
|
+
// ArrayBuffer; these bytes are copied out of the guest heap by
|
|
770
|
+
// `toNativeUint8Array`, never off a SharedArrayBuffer.
|
|
771
|
+
fetchOptions.body = toNativeUint8Array(body);
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
fetchOptions.body = JSON.stringify(body);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
// Follow redirects manually on Node so the SSRF guard runs on every hop's
|
|
778
|
+
// resolved Location, not just the initial URL — otherwise an allowed
|
|
779
|
+
// public URL that 302s to http://169.254.169.254/ or an internal host
|
|
780
|
+
// bypasses the guard entirely. In a browser redirect:"manual" yields an
|
|
781
|
+
// opaque response whose Location is unreadable, and cross-origin bodies
|
|
782
|
+
// are CORS-blocked anyway, so the browser keeps the single redirect:
|
|
783
|
+
// "follow" call with only the already-run initial-URL check.
|
|
784
|
+
const onNode = isString(globalThis.process?.versions?.node);
|
|
785
|
+
let response;
|
|
786
|
+
if (!onNode) {
|
|
787
|
+
response = await fetch(url, { ...fetchOptions, redirect: "follow" });
|
|
277
788
|
}
|
|
278
|
-
|
|
279
|
-
|
|
789
|
+
else {
|
|
790
|
+
const dropContentHeaders = (h) => {
|
|
791
|
+
const out = {};
|
|
792
|
+
for (const [k, v] of Object.entries(h)) {
|
|
793
|
+
const lk = k.toLowerCase();
|
|
794
|
+
if (lk === "content-length" || lk === "content-type")
|
|
795
|
+
continue;
|
|
796
|
+
out[k] = v;
|
|
797
|
+
}
|
|
798
|
+
return out;
|
|
799
|
+
};
|
|
800
|
+
// Credential headers must not cross to a different origin — this
|
|
801
|
+
// mirrors Node's native redirect:"follow", which strips them per the
|
|
802
|
+
// fetch spec. Guest code sets headers from getSecret(...), so a
|
|
803
|
+
// redirect to an attacker origin would otherwise exfiltrate them.
|
|
804
|
+
const dropSensitiveHeaders = (h) => {
|
|
805
|
+
const out = {};
|
|
806
|
+
for (const [k, v] of Object.entries(h)) {
|
|
807
|
+
const lk = k.toLowerCase();
|
|
808
|
+
if (lk === "authorization" ||
|
|
809
|
+
lk === "cookie" ||
|
|
810
|
+
lk === "proxy-authorization") {
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
out[k] = v;
|
|
814
|
+
}
|
|
815
|
+
return out;
|
|
816
|
+
};
|
|
817
|
+
let currentUrl = url;
|
|
818
|
+
let method = fetchOptions.method ?? "GET";
|
|
819
|
+
let body = fetchOptions.body;
|
|
820
|
+
let headers = { ...requestHeaders };
|
|
821
|
+
let hops = 0;
|
|
822
|
+
for (;;) {
|
|
823
|
+
response = await fetch(currentUrl, {
|
|
824
|
+
...fetchOptions,
|
|
825
|
+
method,
|
|
826
|
+
body,
|
|
827
|
+
headers: Object.keys(headers).length ? headers : undefined,
|
|
828
|
+
redirect: "manual"
|
|
829
|
+
});
|
|
830
|
+
const status = response.status;
|
|
831
|
+
if (status < 300 || status >= 400)
|
|
832
|
+
break;
|
|
833
|
+
const location = response.headers.get("location");
|
|
834
|
+
if (!location)
|
|
835
|
+
break; // 3xx without Location — treat as final.
|
|
836
|
+
if (hops++ >= MAX_FETCH_REDIRECTS) {
|
|
837
|
+
throw new Error("fetch: too many redirects");
|
|
838
|
+
}
|
|
839
|
+
const nextUrl = new URL(location, currentUrl).toString();
|
|
840
|
+
// Re-run the guard on the resolved target BEFORE the next request.
|
|
841
|
+
assertFetchUrlAllowed(nextUrl, resolvedLimits.allowPrivateNetwork);
|
|
842
|
+
await assertHostResolvesAllowed(nextUrl);
|
|
843
|
+
// Strip credential headers on a cross-origin hop (origin includes
|
|
844
|
+
// scheme, host and port); keep them on same-origin redirects.
|
|
845
|
+
if (new URL(nextUrl).origin !== new URL(currentUrl).origin) {
|
|
846
|
+
headers = dropSensitiveHeaders(headers);
|
|
847
|
+
}
|
|
848
|
+
// Method/body rewrite per the HTTP spec + browser behavior:
|
|
849
|
+
// 303 always → GET; 301/302 on a non-GET/HEAD → GET (what browsers
|
|
850
|
+
// do); 307/308 preserve method and body.
|
|
851
|
+
if (status === 303 ||
|
|
852
|
+
((status === 301 || status === 302) &&
|
|
853
|
+
method !== "GET" &&
|
|
854
|
+
method !== "HEAD")) {
|
|
855
|
+
method = "GET";
|
|
856
|
+
body = undefined;
|
|
857
|
+
headers = dropContentHeaders(headers);
|
|
858
|
+
}
|
|
859
|
+
// Free the redirect response's socket before the next hop.
|
|
860
|
+
await response.body?.cancel().catch(() => { });
|
|
861
|
+
currentUrl = nextUrl;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
// Read the body under a hard byte cap so a large/fast response can't OOM
|
|
865
|
+
// the shared host heap (the guest's 64MB WASM limit does not apply to
|
|
866
|
+
// these host-side bytes). Aborts the transfer once the cap is exceeded.
|
|
867
|
+
const rawBytes = await readBodyCapped(response, controller, resolvedLimits.maxResponseBodyBytes);
|
|
280
868
|
const ok = response.ok;
|
|
281
869
|
const status = response.status;
|
|
282
870
|
const statusText = response.statusText;
|
|
@@ -286,8 +874,9 @@ export function buildSandbox(context) {
|
|
|
286
874
|
if (cachedText === null) {
|
|
287
875
|
const decoded = new TextDecoder().decode(rawBytes);
|
|
288
876
|
cachedText =
|
|
289
|
-
decoded.length >
|
|
290
|
-
? decoded.slice(0,
|
|
877
|
+
decoded.length > resolvedLimits.maxResponseBodyBytes
|
|
878
|
+
? decoded.slice(0, resolvedLimits.maxResponseBodyBytes) +
|
|
879
|
+
"...[truncated]"
|
|
291
880
|
: decoded;
|
|
292
881
|
}
|
|
293
882
|
return cachedText;
|
|
@@ -299,6 +888,11 @@ export function buildSandbox(context) {
|
|
|
299
888
|
catch {
|
|
300
889
|
parsedJson = undefined;
|
|
301
890
|
}
|
|
891
|
+
// Plain data only — no methods. A function in a bridge result cannot
|
|
892
|
+
// cross the worker boundary (`postMessage` refuses it), so the guest
|
|
893
|
+
// prelude's fetch wrapper builds `text()`/`bytes()`/`arrayBuffer()` over
|
|
894
|
+
// these fields instead. The body is already fully buffered and capped
|
|
895
|
+
// here, so shipping it eagerly costs no extra read.
|
|
302
896
|
return {
|
|
303
897
|
ok,
|
|
304
898
|
status,
|
|
@@ -306,57 +900,342 @@ export function buildSandbox(context) {
|
|
|
306
900
|
headers,
|
|
307
901
|
body: getText(),
|
|
308
902
|
json: parsedJson,
|
|
309
|
-
|
|
310
|
-
arrayBuffer: async () => rawBytes.buffer.slice(rawBytes.byteOffset, rawBytes.byteOffset + rawBytes.byteLength),
|
|
311
|
-
bytes: async () => rawBytes
|
|
903
|
+
bodyBytes: toGuestBytes(rawBytes)
|
|
312
904
|
};
|
|
313
905
|
}
|
|
314
906
|
finally {
|
|
315
907
|
clearTimeout(timer);
|
|
908
|
+
signal?.removeEventListener("abort", onExternalAbort);
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
// WebCrypto exists in both Node >= 20 and browsers, so no node:crypto import
|
|
912
|
+
// is needed and the sandbox stays browser-safe. `getRandomValues` is the one
|
|
913
|
+
// synchronous member: it clamps instead of throwing, since a host throw does
|
|
914
|
+
// not go through the never-reject convention.
|
|
915
|
+
const sandboxCrypto = {
|
|
916
|
+
randomUUID: () => globalThis.crypto.randomUUID(),
|
|
917
|
+
getRandomValues: (length) => {
|
|
918
|
+
const requested = Number(length);
|
|
919
|
+
const size = Number.isFinite(requested)
|
|
920
|
+
? Math.min(Math.max(Math.floor(requested), 0), MAX_RANDOM_BYTES)
|
|
921
|
+
: 0;
|
|
922
|
+
const bytes = new Uint8Array(size);
|
|
923
|
+
if (size > 0)
|
|
924
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
925
|
+
return toGuestBytes(bytes);
|
|
926
|
+
},
|
|
927
|
+
digest: async (algorithm, data) => {
|
|
928
|
+
const algo = normalizeDigestAlgorithm(algorithm);
|
|
929
|
+
const bytes = coerceBytesInput(data, "data");
|
|
930
|
+
const digest = await webCryptoSubtle().digest(algo, bytes);
|
|
931
|
+
return toGuestBytes(new Uint8Array(digest));
|
|
932
|
+
},
|
|
933
|
+
hmac: async (algorithm, key, data) => {
|
|
934
|
+
const algo = normalizeDigestAlgorithm(algorithm);
|
|
935
|
+
const keyBytes = coerceBytesInput(key, "key");
|
|
936
|
+
const dataBytes = coerceBytesInput(data, "data");
|
|
937
|
+
const subtle = webCryptoSubtle();
|
|
938
|
+
const cryptoKey = await subtle.importKey("raw", keyBytes, { name: "HMAC", hash: algo }, false, ["sign"]);
|
|
939
|
+
const signature = await subtle.sign("HMAC", cryptoKey, dataBytes);
|
|
940
|
+
return toGuestBytes(new Uint8Array(signature));
|
|
316
941
|
}
|
|
317
942
|
};
|
|
318
|
-
|
|
943
|
+
/**
|
|
944
|
+
* The one place a secret leaves the host.
|
|
945
|
+
*
|
|
946
|
+
* The scope check sits here rather than in the guest prelude because a
|
|
947
|
+
* prelude is guest code: it can be shadowed, and a check the guest could
|
|
948
|
+
* remove is documentation, not a boundary. `nodetool.secrets.get` and the
|
|
949
|
+
* bare `getSecret` global both land here, so there is no second route.
|
|
950
|
+
*/
|
|
951
|
+
const secretScope = resolvedLimits.secretScope;
|
|
952
|
+
const assertSecretInScope = (name) => {
|
|
953
|
+
if (secretScope === null)
|
|
954
|
+
return;
|
|
955
|
+
if (secretScope.includes(name))
|
|
956
|
+
return;
|
|
957
|
+
throw new Error(secretScope.length === 0
|
|
958
|
+
? `getSecret("${name}"): this node declares no secrets, so it can read none`
|
|
959
|
+
: `getSecret("${name}"): this node may read only ${secretScope
|
|
960
|
+
.map((allowed) => `"${allowed}"`)
|
|
961
|
+
.join(", ")}`);
|
|
962
|
+
};
|
|
319
963
|
const getSecret = context
|
|
320
964
|
? async (name) => {
|
|
965
|
+
assertSecretInScope(name);
|
|
321
966
|
return (await context.getSecret(name)) ?? undefined;
|
|
322
967
|
}
|
|
323
|
-
: async (
|
|
968
|
+
: async (name) => {
|
|
969
|
+
assertSecretInScope(name);
|
|
324
970
|
return undefined;
|
|
325
971
|
};
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
throw new Error("
|
|
352
|
-
},
|
|
353
|
-
list: async (_path) => {
|
|
354
|
-
throw new Error("workspace.list is not available without a context");
|
|
972
|
+
/** What `workspace.stat` answers for a path that is not there. */
|
|
973
|
+
const MISSING_STAT = {
|
|
974
|
+
exists: false,
|
|
975
|
+
size: 0,
|
|
976
|
+
isDirectory: false,
|
|
977
|
+
isFile: false,
|
|
978
|
+
isSymlink: false,
|
|
979
|
+
modifiedMs: 0,
|
|
980
|
+
createdMs: 0,
|
|
981
|
+
accessedMs: 0
|
|
982
|
+
};
|
|
983
|
+
/**
|
|
984
|
+
* Resolve a guest path under the configured filesystem scope.
|
|
985
|
+
*
|
|
986
|
+
* `"workspace"` (the default) is the pre-existing behavior: resolve against
|
|
987
|
+
* the workspace root, then verify the symlink-resolved location is still
|
|
988
|
+
* inside it. `"host"` skips containment and expands `~`, matching what the
|
|
989
|
+
* `lib.os` nodes have always done. Host mode is reachable only when the host
|
|
990
|
+
* passes `filesystemAccess: "host"` — guest code cannot ask for it.
|
|
991
|
+
*/
|
|
992
|
+
const resolveGuestPath = async (ctx, path, isWrite) => {
|
|
993
|
+
const nodePath = await loadNodePath();
|
|
994
|
+
if (resolvedLimits.filesystemAccess === "host") {
|
|
995
|
+
const os = await importNodeBuiltin("node:os");
|
|
996
|
+
if (!os) {
|
|
997
|
+
throw new Error("host filesystem access requires a Node.js runtime");
|
|
355
998
|
}
|
|
356
|
-
|
|
999
|
+
const expanded = path === "~"
|
|
1000
|
+
? os.homedir()
|
|
1001
|
+
: path.startsWith("~/")
|
|
1002
|
+
? nodePath.join(os.homedir(), path.slice(2))
|
|
1003
|
+
: path;
|
|
1004
|
+
return nodePath.resolve(expanded);
|
|
1005
|
+
}
|
|
1006
|
+
const fullPath = ctx.resolveWorkspacePath(path);
|
|
1007
|
+
const fs = await loadFsPromises();
|
|
1008
|
+
await assertWorkspaceContained(ctx, fs, nodePath, fullPath, isWrite);
|
|
1009
|
+
return fullPath;
|
|
1010
|
+
};
|
|
1011
|
+
/**
|
|
1012
|
+
* The guest's `workspace.*` API.
|
|
1013
|
+
*
|
|
1014
|
+
* Three shapes, picked once: no context at all (every call explains itself),
|
|
1015
|
+
* `filesystemAccess: "host"` (the `lib.os` escape hatch — real paths, no
|
|
1016
|
+
* containment, host opt-in only), and the normal case, which goes through
|
|
1017
|
+
* `context.workspace` and therefore works the same whether the run's
|
|
1018
|
+
* workspace is a folder or a prefix in object storage.
|
|
1019
|
+
*/
|
|
1020
|
+
const hostFsWorkspace = (ctx) => ({
|
|
1021
|
+
read: async (path) => {
|
|
1022
|
+
const fs = await loadFsPromises();
|
|
1023
|
+
return fs.readFile(await resolveGuestPath(ctx, path, false), "utf-8");
|
|
1024
|
+
},
|
|
1025
|
+
write: async (path, content) => {
|
|
1026
|
+
const fs = await loadFsPromises();
|
|
1027
|
+
const nodePath = await loadNodePath();
|
|
1028
|
+
const fullPath = await resolveGuestPath(ctx, path, true);
|
|
1029
|
+
await fs.mkdir(nodePath.dirname(fullPath), { recursive: true });
|
|
1030
|
+
await fs.writeFile(fullPath, content, "utf-8");
|
|
1031
|
+
},
|
|
1032
|
+
list: async (path) => {
|
|
1033
|
+
const fs = await loadFsPromises();
|
|
1034
|
+
return fs.readdir(await resolveGuestPath(ctx, path, false));
|
|
1035
|
+
},
|
|
1036
|
+
readBytes: async (path) => {
|
|
1037
|
+
const fs = await loadFsPromises();
|
|
1038
|
+
const fullPath = await resolveGuestPath(ctx, path, false);
|
|
1039
|
+
return toGuestBytes(new Uint8Array(await fs.readFile(fullPath)));
|
|
1040
|
+
},
|
|
1041
|
+
writeBytes: async (path, data) => {
|
|
1042
|
+
const bytes = data instanceof Uint8Array ? data : toNativeUint8Array(data);
|
|
1043
|
+
const fs = await loadFsPromises();
|
|
1044
|
+
const nodePath = await loadNodePath();
|
|
1045
|
+
const fullPath = await resolveGuestPath(ctx, path, true);
|
|
1046
|
+
await fs.mkdir(nodePath.dirname(fullPath), { recursive: true });
|
|
1047
|
+
await fs.writeFile(fullPath, bytes);
|
|
1048
|
+
},
|
|
1049
|
+
stat: async (path) => {
|
|
1050
|
+
const fs = await loadFsPromises();
|
|
1051
|
+
const fullPath = await resolveGuestPath(ctx, path, true);
|
|
1052
|
+
let info;
|
|
1053
|
+
try {
|
|
1054
|
+
info = await fs.lstat(fullPath);
|
|
1055
|
+
}
|
|
1056
|
+
catch {
|
|
1057
|
+
return MISSING_STAT;
|
|
1058
|
+
}
|
|
1059
|
+
return {
|
|
1060
|
+
exists: true,
|
|
1061
|
+
size: info.size,
|
|
1062
|
+
isDirectory: info.isDirectory(),
|
|
1063
|
+
isFile: info.isFile(),
|
|
1064
|
+
isSymlink: info.isSymbolicLink(),
|
|
1065
|
+
modifiedMs: info.mtimeMs,
|
|
1066
|
+
createdMs: info.birthtimeMs,
|
|
1067
|
+
accessedMs: info.atimeMs
|
|
1068
|
+
};
|
|
1069
|
+
},
|
|
1070
|
+
root: async () => ctx.resolveWorkspacePath("."),
|
|
1071
|
+
copy: async (src, dest) => {
|
|
1072
|
+
const fs = await loadFsPromises();
|
|
1073
|
+
const nodePath = await loadNodePath();
|
|
1074
|
+
const fullSrc = await resolveGuestPath(ctx, src, false);
|
|
1075
|
+
const fullDest = await resolveGuestPath(ctx, dest, true);
|
|
1076
|
+
await fs.mkdir(nodePath.dirname(fullDest), { recursive: true });
|
|
1077
|
+
await fs.copyFile(fullSrc, fullDest);
|
|
1078
|
+
},
|
|
1079
|
+
move: async (src, dest) => {
|
|
1080
|
+
const fs = await loadFsPromises();
|
|
1081
|
+
const nodePath = await loadNodePath();
|
|
1082
|
+
const fullSrc = await resolveGuestPath(ctx, src, true);
|
|
1083
|
+
const fullDest = await resolveGuestPath(ctx, dest, true);
|
|
1084
|
+
await fs.mkdir(nodePath.dirname(fullDest), { recursive: true });
|
|
1085
|
+
await fs.rename(fullSrc, fullDest);
|
|
1086
|
+
},
|
|
1087
|
+
mkdir: async (path) => {
|
|
1088
|
+
const fs = await loadFsPromises();
|
|
1089
|
+
await fs.mkdir(await resolveGuestPath(ctx, path, true), {
|
|
1090
|
+
recursive: true
|
|
1091
|
+
});
|
|
1092
|
+
},
|
|
1093
|
+
remove: async (path) => {
|
|
1094
|
+
const fs = await loadFsPromises();
|
|
1095
|
+
const fullPath = await resolveGuestPath(ctx, path, true);
|
|
1096
|
+
const info = await fs.lstat(fullPath);
|
|
1097
|
+
if (info.isDirectory()) {
|
|
1098
|
+
// rmdir refuses a non-empty directory, which is the point.
|
|
1099
|
+
await fs.rmdir(fullPath);
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
await fs.rm(fullPath, { recursive: false });
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
const backedWorkspace = (ws) => ({
|
|
1107
|
+
read: async (path) => {
|
|
1108
|
+
const text = await ws.readText(path);
|
|
1109
|
+
if (text === null)
|
|
1110
|
+
throw new Error(`ENOENT: no such file: ${path}`);
|
|
1111
|
+
return text;
|
|
1112
|
+
},
|
|
1113
|
+
write: async (path, content) => {
|
|
1114
|
+
await ws.write(path, content);
|
|
1115
|
+
},
|
|
1116
|
+
// Names only, like readdir — the guest joins them onto the path it asked
|
|
1117
|
+
// about, which is how the pre-existing scripts use this.
|
|
1118
|
+
list: async (path) => (await ws.list(path)).map((entry) => entry.name),
|
|
1119
|
+
readBytes: async (path) => {
|
|
1120
|
+
const bytes = await ws.read(path);
|
|
1121
|
+
if (bytes === null)
|
|
1122
|
+
throw new Error(`ENOENT: no such file: ${path}`);
|
|
1123
|
+
return toGuestBytes(bytes);
|
|
1124
|
+
},
|
|
1125
|
+
writeBytes: async (path, data) => {
|
|
1126
|
+
const bytes = data instanceof Uint8Array ? data : toNativeUint8Array(data);
|
|
1127
|
+
await ws.write(path, bytes);
|
|
1128
|
+
},
|
|
1129
|
+
// A missing path is an answer, not a failure: guest code asking
|
|
1130
|
+
// "does this exist?" should not have to wrap the call in try/catch.
|
|
1131
|
+
stat: async (path) => {
|
|
1132
|
+
let info;
|
|
1133
|
+
try {
|
|
1134
|
+
info = await ws.stat(path);
|
|
1135
|
+
}
|
|
1136
|
+
catch {
|
|
1137
|
+
return MISSING_STAT;
|
|
1138
|
+
}
|
|
1139
|
+
if (!info)
|
|
1140
|
+
return MISSING_STAT;
|
|
1141
|
+
return {
|
|
1142
|
+
exists: true,
|
|
1143
|
+
size: info.size,
|
|
1144
|
+
isDirectory: info.isDirectory,
|
|
1145
|
+
isFile: !info.isDirectory,
|
|
1146
|
+
// Nothing reachable through a workspace is a symlink: an object store
|
|
1147
|
+
// cannot hold one, and the local backend refuses to follow one out of
|
|
1148
|
+
// the root.
|
|
1149
|
+
isSymlink: false,
|
|
1150
|
+
modifiedMs: info.modifiedAt,
|
|
1151
|
+
createdMs: info.modifiedAt,
|
|
1152
|
+
accessedMs: info.modifiedAt
|
|
1153
|
+
};
|
|
1154
|
+
},
|
|
1155
|
+
/**
|
|
1156
|
+
* The base for relative paths. A real directory when the workspace has
|
|
1157
|
+
* one; otherwise `/workspace`, which every workspace path accepts as its
|
|
1158
|
+
* own root, so a script that joins onto it keeps working.
|
|
1159
|
+
*/
|
|
1160
|
+
root: async () => ws.localDir ?? "/workspace",
|
|
1161
|
+
copy: async (src, dest) => {
|
|
1162
|
+
await ws.copy(src, dest);
|
|
1163
|
+
},
|
|
1164
|
+
move: async (src, dest) => {
|
|
1165
|
+
await ws.move(src, dest);
|
|
1166
|
+
},
|
|
1167
|
+
mkdir: async (path) => {
|
|
1168
|
+
await ws.mkdir(path);
|
|
1169
|
+
},
|
|
1170
|
+
remove: async (path) => {
|
|
1171
|
+
// One file or one empty directory — guest code cannot delete a whole
|
|
1172
|
+
// subtree in a single call, in any filesystem mode.
|
|
1173
|
+
const info = await ws.stat(path);
|
|
1174
|
+
if (info?.isDirectory) {
|
|
1175
|
+
const children = await ws.list(path);
|
|
1176
|
+
if (children.length > 0) {
|
|
1177
|
+
throw new Error(`ENOTEMPTY: directory not empty: ${path}`);
|
|
1178
|
+
}
|
|
1179
|
+
await ws.deleteAll(path);
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (!(await ws.delete(path))) {
|
|
1183
|
+
throw new Error(`ENOENT: no such file: ${path}`);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
const unavailable = (api) => async () => {
|
|
1188
|
+
throw new Error(`workspace.${api} is not available without a context`);
|
|
1189
|
+
};
|
|
1190
|
+
const workspace = !context
|
|
1191
|
+
? {
|
|
1192
|
+
read: unavailable("read"),
|
|
1193
|
+
write: unavailable("write"),
|
|
1194
|
+
list: unavailable("list"),
|
|
1195
|
+
readBytes: unavailable("readBytes"),
|
|
1196
|
+
writeBytes: unavailable("writeBytes"),
|
|
1197
|
+
stat: unavailable("stat"),
|
|
1198
|
+
root: unavailable("root"),
|
|
1199
|
+
copy: unavailable("copy"),
|
|
1200
|
+
move: unavailable("move"),
|
|
1201
|
+
mkdir: unavailable("mkdir"),
|
|
1202
|
+
remove: unavailable("remove")
|
|
1203
|
+
}
|
|
1204
|
+
: resolvedLimits.filesystemAccess === "host"
|
|
1205
|
+
? hostFsWorkspace(context)
|
|
1206
|
+
: context.workspace
|
|
1207
|
+
? backedWorkspace(context.workspace)
|
|
1208
|
+
: {
|
|
1209
|
+
read: unavailable("read"),
|
|
1210
|
+
write: unavailable("write"),
|
|
1211
|
+
list: unavailable("list"),
|
|
1212
|
+
readBytes: unavailable("readBytes"),
|
|
1213
|
+
writeBytes: unavailable("writeBytes"),
|
|
1214
|
+
stat: unavailable("stat"),
|
|
1215
|
+
root: unavailable("root"),
|
|
1216
|
+
copy: unavailable("copy"),
|
|
1217
|
+
move: unavailable("move"),
|
|
1218
|
+
mkdir: unavailable("mkdir"),
|
|
1219
|
+
remove: unavailable("remove")
|
|
1220
|
+
};
|
|
357
1221
|
const sleep = (ms) => {
|
|
358
1222
|
const capped = Math.min(ms, 5000);
|
|
359
|
-
|
|
1223
|
+
// Resolve immediately on cancellation: a sleeping script is the common
|
|
1224
|
+
// case for "still running after Stop", and waiting out the nap before the
|
|
1225
|
+
// next bridge call can reject it wastes up to 5s per sleep.
|
|
1226
|
+
return new Promise((resolve) => {
|
|
1227
|
+
if (signal?.aborted)
|
|
1228
|
+
return resolve();
|
|
1229
|
+
const timer = setTimeout(() => {
|
|
1230
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1231
|
+
resolve();
|
|
1232
|
+
}, capped);
|
|
1233
|
+
function onAbort() {
|
|
1234
|
+
clearTimeout(timer);
|
|
1235
|
+
resolve();
|
|
1236
|
+
}
|
|
1237
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1238
|
+
});
|
|
360
1239
|
};
|
|
361
1240
|
const assetToSandbox = context
|
|
362
1241
|
? async (assetId, path) => {
|
|
@@ -372,6 +1251,512 @@ export function buildSandbox(context) {
|
|
|
372
1251
|
: async (_path) => {
|
|
373
1252
|
throw new Error("sandboxToAsset is not available without a context");
|
|
374
1253
|
};
|
|
1254
|
+
// Fire-and-forget progress reporting. Synchronous like console.log: it never
|
|
1255
|
+
// throws, so it needs no never-reject wrapper. A guest that spams it cannot
|
|
1256
|
+
// flood the host — reports closer together than PROGRESS_MIN_INTERVAL_MS are
|
|
1257
|
+
// dropped, and the run has a lifetime cap.
|
|
1258
|
+
let progressCalls = 0;
|
|
1259
|
+
let lastProgressAt = 0;
|
|
1260
|
+
const progress = (percent, message) => {
|
|
1261
|
+
if (!onProgress || signal?.aborted)
|
|
1262
|
+
return;
|
|
1263
|
+
if (progressCalls >= MAX_PROGRESS_CALLS)
|
|
1264
|
+
return;
|
|
1265
|
+
const now = Date.now();
|
|
1266
|
+
if (progressCalls > 0 && now - lastProgressAt < PROGRESS_MIN_INTERVAL_MS) {
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
const raw = Number(percent);
|
|
1270
|
+
const clamped = Number.isFinite(raw) ? Math.min(Math.max(raw, 0), 100) : 0;
|
|
1271
|
+
const text = message === undefined || message === null
|
|
1272
|
+
? undefined
|
|
1273
|
+
: String(message).slice(0, MAX_PROGRESS_MESSAGE_CHARS);
|
|
1274
|
+
progressCalls++;
|
|
1275
|
+
lastProgressAt = now;
|
|
1276
|
+
try {
|
|
1277
|
+
onProgress(clamped, text);
|
|
1278
|
+
}
|
|
1279
|
+
catch {
|
|
1280
|
+
// A failing host sink must not take the guest run down with it.
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
// Output delivery. The opposite of `progress` in every respect: awaitable, so
|
|
1284
|
+
// the guest gets backpressure from a slow consumer, and never rate-limited or
|
|
1285
|
+
// dropped — an output value the host silently discards is data loss, not a
|
|
1286
|
+
// missed progress tick. Both throw into the guest rather than failing quietly.
|
|
1287
|
+
let emitCalls = 0;
|
|
1288
|
+
const emitted = [];
|
|
1289
|
+
const outputs = new Map();
|
|
1290
|
+
const handleName = (fn, name) => {
|
|
1291
|
+
if (!isString(name)) {
|
|
1292
|
+
throw new TypeError(`${fn}: name must be a string`);
|
|
1293
|
+
}
|
|
1294
|
+
return name;
|
|
1295
|
+
};
|
|
1296
|
+
const emit = async (name, value) => {
|
|
1297
|
+
const handle = handleName("emit", name);
|
|
1298
|
+
if (emitCalls >= MAX_EMIT_CALLS) {
|
|
1299
|
+
throw new Error(`emit: this run may emit at most ${MAX_EMIT_CALLS} values (MAX_EMIT_CALLS)`);
|
|
1300
|
+
}
|
|
1301
|
+
emitCalls++;
|
|
1302
|
+
const marshaled = serializeResult(value, resolvedLimits.maxOutputSize);
|
|
1303
|
+
if (onEmit) {
|
|
1304
|
+
// Awaited, so the guest's promise resolves only once the host has taken
|
|
1305
|
+
// the value — and a sink that throws unwinds the guest's `emit` call.
|
|
1306
|
+
await onEmit(handle, marshaled);
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
emitted.push({ name: handle, value: marshaled });
|
|
1310
|
+
};
|
|
1311
|
+
const output = async (name, value) => {
|
|
1312
|
+
const handle = handleName("output", name);
|
|
1313
|
+
if (outputs.has(handle)) {
|
|
1314
|
+
throw new Error(`output "${handle}" was already set`);
|
|
1315
|
+
}
|
|
1316
|
+
outputs.set(handle, serializeResult(value, resolvedLimits.maxOutputSize));
|
|
1317
|
+
};
|
|
1318
|
+
// Input delivery, the mirror of `emit`: the guest pulls one value at a time
|
|
1319
|
+
// and the host answers from the inbox it owns. Backpressure is free — an item
|
|
1320
|
+
// nobody asked for stays upstream.
|
|
1321
|
+
const takeInput = async (name) => {
|
|
1322
|
+
if (!streams?.onTakeInput) {
|
|
1323
|
+
throw new Error(NO_INPUT_STREAM_MESSAGE);
|
|
1324
|
+
}
|
|
1325
|
+
if (name !== null && !isString(name)) {
|
|
1326
|
+
throw new TypeError("stream: name must be a string");
|
|
1327
|
+
}
|
|
1328
|
+
// Waiting on upstream is not the guest executing, so it must not spend the
|
|
1329
|
+
// guest's timeout budget. The clock stops until the value (or EOS) lands.
|
|
1330
|
+
const resume = streams.clock?.suspend();
|
|
1331
|
+
try {
|
|
1332
|
+
const take = await streams.onTakeInput(name);
|
|
1333
|
+
if (take.done)
|
|
1334
|
+
return { done: true };
|
|
1335
|
+
return {
|
|
1336
|
+
done: false,
|
|
1337
|
+
handle: take.handle,
|
|
1338
|
+
value: toGuestBytesDeep(take.value)
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
finally {
|
|
1342
|
+
resume?.();
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
// Synchronous and never throwing, which is what lets the guest read it as a
|
|
1346
|
+
// plain boolean: an awaitable answer would make `if (stream.open(h))` true on
|
|
1347
|
+
// a pending promise. `null` is the one non-answer — this run has no input
|
|
1348
|
+
// stream at all — and the prelude turns it into the thrown error.
|
|
1349
|
+
const streamOpen = (name) => {
|
|
1350
|
+
if (!streams?.onTakeInput)
|
|
1351
|
+
return null;
|
|
1352
|
+
if (!isString(name) || !streams.onStreamOpen)
|
|
1353
|
+
return false;
|
|
1354
|
+
try {
|
|
1355
|
+
return streams.onStreamOpen(name) === true;
|
|
1356
|
+
}
|
|
1357
|
+
catch {
|
|
1358
|
+
// A host probe that throws answers "closed" rather than taking the run
|
|
1359
|
+
// down: the take itself is where a broken inbox must surface.
|
|
1360
|
+
return false;
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
// QuickJS ships no Intl, so locale-aware formatting has to come from the
|
|
1364
|
+
// host. Async + never-reject like the other bridges: a bad locale or option
|
|
1365
|
+
// surfaces in the guest as a thrown Error with Intl's own message.
|
|
1366
|
+
const format = {
|
|
1367
|
+
number: async (value, options) => {
|
|
1368
|
+
const num = Number(value);
|
|
1369
|
+
if (!Number.isFinite(num)) {
|
|
1370
|
+
throw new Error("format.number: value must be a finite number");
|
|
1371
|
+
}
|
|
1372
|
+
const opts = options ?? {};
|
|
1373
|
+
return new Intl.NumberFormat(opts.locale ?? DEFAULT_FORMAT_LOCALE, {
|
|
1374
|
+
style: opts.style,
|
|
1375
|
+
currency: opts.currency,
|
|
1376
|
+
minimumFractionDigits: opts.minimumFractionDigits,
|
|
1377
|
+
maximumFractionDigits: opts.maximumFractionDigits,
|
|
1378
|
+
useGrouping: opts.useGrouping
|
|
1379
|
+
}).format(num);
|
|
1380
|
+
},
|
|
1381
|
+
date: async (epochMs, options) => {
|
|
1382
|
+
const ms = Number(epochMs);
|
|
1383
|
+
if (!Number.isFinite(ms)) {
|
|
1384
|
+
throw new Error("format.date: epochMs must be a finite number of milliseconds");
|
|
1385
|
+
}
|
|
1386
|
+
const opts = options ?? {};
|
|
1387
|
+
const dateStyle = opts.dateStyle;
|
|
1388
|
+
const timeStyle = opts.timeStyle;
|
|
1389
|
+
return new Intl.DateTimeFormat(opts.locale ?? DEFAULT_FORMAT_LOCALE, {
|
|
1390
|
+
// Intl falls back to a date-only default when neither style is given.
|
|
1391
|
+
dateStyle: dateStyle ?? (timeStyle ? undefined : "medium"),
|
|
1392
|
+
timeStyle,
|
|
1393
|
+
timeZone: opts.timeZone
|
|
1394
|
+
}).format(new Date(ms));
|
|
1395
|
+
},
|
|
1396
|
+
relativeTime: async (value, unit, options) => {
|
|
1397
|
+
const num = Number(value);
|
|
1398
|
+
if (!Number.isFinite(num)) {
|
|
1399
|
+
throw new Error("format.relativeTime: value must be a finite number");
|
|
1400
|
+
}
|
|
1401
|
+
const opts = options ?? {};
|
|
1402
|
+
return new Intl.RelativeTimeFormat(opts.locale ?? DEFAULT_FORMAT_LOCALE).format(num, unit);
|
|
1403
|
+
},
|
|
1404
|
+
list: async (items, options) => {
|
|
1405
|
+
if (!Array.isArray(items)) {
|
|
1406
|
+
throw new Error("format.list: items must be an array of strings");
|
|
1407
|
+
}
|
|
1408
|
+
const opts = options ?? {};
|
|
1409
|
+
return new Intl.ListFormat(opts.locale ?? DEFAULT_FORMAT_LOCALE, { type: opts.type }).format(items.map((i) => String(i)));
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
// Bytes media transforms produced and the guest only carries around. Lives for the
|
|
1413
|
+
// run, then goes; nothing here is durable until something promotes it.
|
|
1414
|
+
const mediaStore = createSandboxMediaStore(resolvedLimits.runMediaBytes);
|
|
1415
|
+
let runAvMediaPromise;
|
|
1416
|
+
const loadRunAvMedia = () => {
|
|
1417
|
+
runAvMediaPromise ??= loadSandboxAvMedia().then((media) => ({
|
|
1418
|
+
audioOps: media.createAudioBridge(signal, resolvedLimits.runMediaBytes),
|
|
1419
|
+
videoOps: media.createVideoBridge(signal, resolvedLimits.runMediaBytes)
|
|
1420
|
+
}));
|
|
1421
|
+
return runAvMediaPromise;
|
|
1422
|
+
};
|
|
1423
|
+
// Media bridges. The engine (`sandbox-media.ts`) is imported on first use
|
|
1424
|
+
// like every other library-backed bridge, and picks its canvas backend from
|
|
1425
|
+
// the runtime — `@napi-rs/canvas` on Node, `OffscreenCanvas` in the browser
|
|
1426
|
+
// runner. Bytes come back tagged for the guest prelude to revive, so the
|
|
1427
|
+
// guest-visible API is `Uint8Array` in and `Uint8Array` out.
|
|
1428
|
+
const withGuestBytes = (fn) => {
|
|
1429
|
+
return async (...args) => toGuestBytesDeep(await fn(...args));
|
|
1430
|
+
};
|
|
1431
|
+
const mediaMember = (pick) => withGuestBytes(async (...args) => pick(await loadSandboxMedia())(...args));
|
|
1432
|
+
// `image.*` trades in handles, not bytes. On the way in, every handle and
|
|
1433
|
+
// every media ref buried anywhere in the arguments is replaced by the bytes
|
|
1434
|
+
// it names — resolved host-side, so an `asset://` a generator produced goes
|
|
1435
|
+
// straight into the next operation without being pulled through the guest.
|
|
1436
|
+
// On the way out, encoded bytes become a handle instead of a base64 payload.
|
|
1437
|
+
// A chain (resize → adjust → composite) therefore moves nothing across the
|
|
1438
|
+
// boundary but small objects.
|
|
1439
|
+
//
|
|
1440
|
+
// `info` and `decode` are the deliberate exceptions: they exist to tell the
|
|
1441
|
+
// guest what is *in* an image, so their results stay plain data. `decode`
|
|
1442
|
+
// still hands over real pixels, which is the one call that has to.
|
|
1443
|
+
const loadMediaRef = async (where, value, maxBytes = resolvedLimits.runMediaBytes) => {
|
|
1444
|
+
const ref = remapMediaRef(value);
|
|
1445
|
+
if (context) {
|
|
1446
|
+
return resolveRefBytes(where, ref, context, (path) => resolveGuestPath(context, path, false), maxBytes);
|
|
1447
|
+
}
|
|
1448
|
+
if (resolveMediaRef) {
|
|
1449
|
+
const bytes = await resolveMediaRef(where, ref);
|
|
1450
|
+
if (bytes.length > maxBytes) {
|
|
1451
|
+
throw new Error(`${where}: media input is ${bytes.length} bytes, over the ` +
|
|
1452
|
+
`${maxBytes} byte remaining run media limit`);
|
|
1453
|
+
}
|
|
1454
|
+
return bytes;
|
|
1455
|
+
}
|
|
1456
|
+
const label = ref.uri || ref.asset_id || "this media ref";
|
|
1457
|
+
throw new Error(`${where}: cannot resolve ${label} in this run. Pass a media ` +
|
|
1458
|
+
`handle or a generation result (its asset_uri).`);
|
|
1459
|
+
};
|
|
1460
|
+
const resolveMediaArgs = async (where, value,
|
|
1461
|
+
// `image.*` wants a ref flattened to bytes it can operate on. `media.*`
|
|
1462
|
+
// takes refs *as* refs — that is its whole job — so it resolves handles
|
|
1463
|
+
// only, or `media.bytes(assetRef)` would be handed bytes and no longer
|
|
1464
|
+
// have a ref to read.
|
|
1465
|
+
resolveRefs, depth = 0, expectedType, budget = { used: 0 }) => {
|
|
1466
|
+
if (depth >= SANDBOX_SERIALIZE_MAX_DEPTH)
|
|
1467
|
+
return value;
|
|
1468
|
+
const accountBytes = (bytes) => {
|
|
1469
|
+
if (bytes.byteLength > resolvedLimits.runMediaBytes - budget.used) {
|
|
1470
|
+
throw new Error(`${where}: aggregate media input exceeds the ` +
|
|
1471
|
+
`${resolvedLimits.runMediaBytes} byte run media limit`);
|
|
1472
|
+
}
|
|
1473
|
+
budget.used += bytes.byteLength;
|
|
1474
|
+
return bytes;
|
|
1475
|
+
};
|
|
1476
|
+
// Matched on the marker, not on store membership: a handle from an
|
|
1477
|
+
// earlier run must report *that*, rather than falling through to the ref
|
|
1478
|
+
// path and blaming the uri it happens to carry.
|
|
1479
|
+
if (isSandboxMediaHandle(value)) {
|
|
1480
|
+
const bytes = mediaStore.resolve(value, expectedType);
|
|
1481
|
+
if (!bytes)
|
|
1482
|
+
throw new Error(`${where}: media handle has no bytes`);
|
|
1483
|
+
return accountBytes(bytes);
|
|
1484
|
+
}
|
|
1485
|
+
if (value instanceof Uint8Array || ArrayBuffer.isView(value)) {
|
|
1486
|
+
return accountBytes(value);
|
|
1487
|
+
}
|
|
1488
|
+
if (Array.isArray(value)) {
|
|
1489
|
+
const resolved = [];
|
|
1490
|
+
for (const item of value) {
|
|
1491
|
+
const result = await resolveMediaArgs(where, item, resolveRefs, depth + 1, expectedType, budget);
|
|
1492
|
+
resolved.push(result);
|
|
1493
|
+
}
|
|
1494
|
+
return resolved;
|
|
1495
|
+
}
|
|
1496
|
+
if (resolveRefs && looksLikeMediaRef(value)) {
|
|
1497
|
+
if (expectedType && isObjectLike(value)) {
|
|
1498
|
+
const declared = value.type;
|
|
1499
|
+
if ((declared === "image" ||
|
|
1500
|
+
declared === "audio" ||
|
|
1501
|
+
declared === "video") &&
|
|
1502
|
+
declared !== expectedType) {
|
|
1503
|
+
throw new Error(`${where}: expected ${expectedType} media, but received ${declared}`);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
const remainingBytes = resolvedLimits.runMediaBytes - budget.used;
|
|
1507
|
+
return accountBytes(await loadMediaRef(where, value, remainingBytes));
|
|
1508
|
+
}
|
|
1509
|
+
if (isObjectLike(value)) {
|
|
1510
|
+
const record = value;
|
|
1511
|
+
const out = {};
|
|
1512
|
+
for (const [k, v] of Object.entries(record)) {
|
|
1513
|
+
out[k] = await resolveMediaArgs(where, v, resolveRefs, depth + 1, expectedType, budget);
|
|
1514
|
+
}
|
|
1515
|
+
return out;
|
|
1516
|
+
}
|
|
1517
|
+
return value;
|
|
1518
|
+
};
|
|
1519
|
+
const defaultMediaMime = (type) => type === "image"
|
|
1520
|
+
? "image/png"
|
|
1521
|
+
: type === "audio"
|
|
1522
|
+
? "audio/wav"
|
|
1523
|
+
: "video/mp4";
|
|
1524
|
+
const mediaMimeForValue = (value, expectedType) => {
|
|
1525
|
+
if (isSandboxMediaHandle(value)) {
|
|
1526
|
+
return (mediaStore.entry(value, expectedType)?.mimeType ??
|
|
1527
|
+
defaultMediaMime(expectedType));
|
|
1528
|
+
}
|
|
1529
|
+
const record = isRecord(value) ? value : undefined;
|
|
1530
|
+
const declared = record?.mimeType ?? record?.mime_type;
|
|
1531
|
+
const ref = remapMediaRef(value);
|
|
1532
|
+
if (isString(declared))
|
|
1533
|
+
ref.mimeType = declared;
|
|
1534
|
+
return mimeForRef(ref, defaultMediaMime(expectedType));
|
|
1535
|
+
};
|
|
1536
|
+
const sniffMediaMime = (type, bytes, fallback) => {
|
|
1537
|
+
const ascii = (start, length) => String.fromCharCode(...bytes.subarray(start, start + length));
|
|
1538
|
+
if (type === "image") {
|
|
1539
|
+
if (bytes.length >= 8 && ascii(1, 3) === "PNG")
|
|
1540
|
+
return "image/png";
|
|
1541
|
+
if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8)
|
|
1542
|
+
return "image/jpeg";
|
|
1543
|
+
if (bytes.length >= 6 && ascii(0, 3) === "GIF")
|
|
1544
|
+
return "image/gif";
|
|
1545
|
+
if (bytes.length >= 12 &&
|
|
1546
|
+
ascii(0, 4) === "RIFF" &&
|
|
1547
|
+
ascii(8, 4) === "WEBP")
|
|
1548
|
+
return "image/webp";
|
|
1549
|
+
return fallback;
|
|
1550
|
+
}
|
|
1551
|
+
if (type === "audio") {
|
|
1552
|
+
if (bytes.length >= 12 &&
|
|
1553
|
+
ascii(0, 4) === "RIFF" &&
|
|
1554
|
+
ascii(8, 4) === "WAVE")
|
|
1555
|
+
return "audio/wav";
|
|
1556
|
+
if (bytes.length >= 4 && ascii(0, 4) === "fLaC")
|
|
1557
|
+
return "audio/flac";
|
|
1558
|
+
if (bytes.length >= 4 && ascii(0, 4) === "OggS")
|
|
1559
|
+
return "audio/ogg";
|
|
1560
|
+
if ((bytes.length >= 3 && ascii(0, 3) === "ID3") ||
|
|
1561
|
+
(bytes.length >= 2 && bytes[0] === 0xff && (bytes[1] & 0xe0) === 0xe0))
|
|
1562
|
+
return "audio/mpeg";
|
|
1563
|
+
if (bytes.length >= 12 && ascii(4, 4) === "ftyp")
|
|
1564
|
+
return "audio/mp4";
|
|
1565
|
+
return fallback;
|
|
1566
|
+
}
|
|
1567
|
+
if (bytes.length >= 12 && ascii(0, 4) === "RIFF" && ascii(8, 4) === "AVI ")
|
|
1568
|
+
return "video/x-msvideo";
|
|
1569
|
+
if (bytes.length >= 4 && bytes[0] === 0x1a && bytes[1] === 0x45) {
|
|
1570
|
+
return ascii(0, Math.min(bytes.length, 64)).includes("webm")
|
|
1571
|
+
? "video/webm"
|
|
1572
|
+
: "video/x-matroska";
|
|
1573
|
+
}
|
|
1574
|
+
if (bytes.length >= 12 && ascii(4, 4) === "ftyp") {
|
|
1575
|
+
return ascii(8, 4) === "qt " ? "video/quicktime" : "video/mp4";
|
|
1576
|
+
}
|
|
1577
|
+
return fallback;
|
|
1578
|
+
};
|
|
1579
|
+
/** An `image.*` op: handles/refs in, a handle out. */
|
|
1580
|
+
const imageMember = (name, pick) => {
|
|
1581
|
+
const where = `image.${name}`;
|
|
1582
|
+
return async (...args) => {
|
|
1583
|
+
const resolved = (await resolveMediaArgs(where, args, true, 0, "image"));
|
|
1584
|
+
const result = await pick(await loadSandboxMedia())(...resolved);
|
|
1585
|
+
if (result instanceof Uint8Array) {
|
|
1586
|
+
const media = await loadSandboxMedia();
|
|
1587
|
+
const info = (await media.imageOps.info(result));
|
|
1588
|
+
const meta = {
|
|
1589
|
+
mimeType: info.format ? `image/${info.format}` : "image/png"
|
|
1590
|
+
};
|
|
1591
|
+
if (info.width !== undefined)
|
|
1592
|
+
meta.width = info.width;
|
|
1593
|
+
if (info.height !== undefined)
|
|
1594
|
+
meta.height = info.height;
|
|
1595
|
+
return mediaStore.put(result, meta);
|
|
1596
|
+
}
|
|
1597
|
+
return toGuestBytesDeep(result);
|
|
1598
|
+
};
|
|
1599
|
+
};
|
|
1600
|
+
const image = {
|
|
1601
|
+
/**
|
|
1602
|
+
* The one door from a handle back to real bytes, and deliberately the only
|
|
1603
|
+
* one. Every other op keeps the payload host-side, so a body that reaches
|
|
1604
|
+
* for this is saying it will read the bytes itself — the cost is explicit
|
|
1605
|
+
* at the call site instead of hidden in every hop. It needs no
|
|
1606
|
+
* ProcessingContext, so a Code node that really does parse an encoded image
|
|
1607
|
+
* works exactly as it did.
|
|
1608
|
+
*/
|
|
1609
|
+
bytes: async (value) => {
|
|
1610
|
+
const resolved = await resolveMediaArgs("image.bytes", value, true, 0, "image");
|
|
1611
|
+
const media = await loadSandboxMedia();
|
|
1612
|
+
return toGuestBytes(media.asImageBytes(resolved, "image.bytes"));
|
|
1613
|
+
},
|
|
1614
|
+
/**
|
|
1615
|
+
* Handle → durable asset. Bytes stay on the host; the guest gets a ref.
|
|
1616
|
+
*/
|
|
1617
|
+
toAsset: async (value, options) => {
|
|
1618
|
+
const sourceMime = mediaMimeForValue(value, "image");
|
|
1619
|
+
const resolved = await resolveMediaArgs("image.toAsset", value, true, 0, "image");
|
|
1620
|
+
const media = await loadSandboxMedia();
|
|
1621
|
+
const bytes = media.asImageBytes(resolved, "image.toAsset");
|
|
1622
|
+
const mimeType = sniffMediaMime("image", bytes, sourceMime);
|
|
1623
|
+
const assetOptions = { mimeType, ...options };
|
|
1624
|
+
if (promoteMedia) {
|
|
1625
|
+
return promoteMedia("image", bytes, assetOptions);
|
|
1626
|
+
}
|
|
1627
|
+
if (context) {
|
|
1628
|
+
return mediaRefBridge.toImage(bytes, assetOptions);
|
|
1629
|
+
}
|
|
1630
|
+
throw new Error("image.toAsset: cannot save an asset in this run. Use " +
|
|
1631
|
+
"nodetool.media.toImage from a chat action, or media.toImage " +
|
|
1632
|
+
"from a Code node.");
|
|
1633
|
+
},
|
|
1634
|
+
info: imageMember("info", (m) => m.imageOps.info),
|
|
1635
|
+
decode: imageMember("decode", (m) => m.imageOps.decode),
|
|
1636
|
+
stats: imageMember("stats", (m) => m.imageOps.stats),
|
|
1637
|
+
blank: imageMember("blank", (m) => m.imageOps.blank),
|
|
1638
|
+
pad: imageMember("pad", (m) => m.imageOps.pad),
|
|
1639
|
+
grid: imageMember("grid", (m) => m.imageOps.grid),
|
|
1640
|
+
resize: imageMember("resize", (m) => m.imageOps.resize),
|
|
1641
|
+
crop: imageMember("crop", (m) => m.imageOps.crop),
|
|
1642
|
+
rotate: imageMember("rotate", (m) => m.imageOps.rotate),
|
|
1643
|
+
flip: imageMember("flip", (m) => m.imageOps.flip),
|
|
1644
|
+
adjust: imageMember("adjust", (m) => m.imageOps.adjust),
|
|
1645
|
+
composite: imageMember("composite", (m) => m.imageOps.composite),
|
|
1646
|
+
convert: imageMember("convert", (m) => m.imageOps.convert)
|
|
1647
|
+
};
|
|
1648
|
+
const requireMediaBytes = (where, value) => {
|
|
1649
|
+
if (value instanceof Uint8Array)
|
|
1650
|
+
return value;
|
|
1651
|
+
if (ArrayBuffer.isView(value)) {
|
|
1652
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
1653
|
+
}
|
|
1654
|
+
throw new Error(`${where}: expected media bytes, a media handle, or a media ref`);
|
|
1655
|
+
};
|
|
1656
|
+
const avMember = (namespace, name, inputTypes, outputType, pick) => {
|
|
1657
|
+
const where = `${namespace}.${name}`;
|
|
1658
|
+
return async (...args) => {
|
|
1659
|
+
const budget = { used: 0 };
|
|
1660
|
+
const resolved = [];
|
|
1661
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1662
|
+
resolved.push(await resolveMediaArgs(where, args[index], true, 0, inputTypes[index], budget));
|
|
1663
|
+
}
|
|
1664
|
+
const result = await pick(await loadRunAvMedia())(...resolved);
|
|
1665
|
+
if (outputType && result instanceof Uint8Array) {
|
|
1666
|
+
const sourceType = inputTypes[0] ?? outputType;
|
|
1667
|
+
const sourceMime = mediaMimeForValue(args[0], sourceType);
|
|
1668
|
+
return mediaStore.put(result, {
|
|
1669
|
+
type: outputType,
|
|
1670
|
+
mimeType: sniffMediaMime(outputType, result, outputType === sourceType
|
|
1671
|
+
? sourceMime
|
|
1672
|
+
: defaultMediaMime(outputType))
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
return toGuestBytesDeep(result);
|
|
1676
|
+
};
|
|
1677
|
+
};
|
|
1678
|
+
const avBytes = (namespace) => async (value) => {
|
|
1679
|
+
const where = `${namespace}.bytes`;
|
|
1680
|
+
const resolved = await resolveMediaArgs(where, value, true, 0, namespace);
|
|
1681
|
+
return toGuestBytes(requireMediaBytes(where, resolved));
|
|
1682
|
+
};
|
|
1683
|
+
const avToAsset = (type) => async (value, options) => {
|
|
1684
|
+
const where = `${type}.toAsset`;
|
|
1685
|
+
const sourceMime = mediaMimeForValue(value, type);
|
|
1686
|
+
const resolved = await resolveMediaArgs(where, value, true, 0, type);
|
|
1687
|
+
const bytes = requireMediaBytes(where, resolved);
|
|
1688
|
+
const mimeType = sniffMediaMime(type, bytes, sourceMime);
|
|
1689
|
+
const assetOptions = { mimeType, ...options };
|
|
1690
|
+
if (promoteMedia) {
|
|
1691
|
+
return promoteMedia(type, bytes, assetOptions);
|
|
1692
|
+
}
|
|
1693
|
+
if (context) {
|
|
1694
|
+
return type === "audio"
|
|
1695
|
+
? mediaRefBridge.toAudio(bytes, assetOptions)
|
|
1696
|
+
: mediaRefBridge.toVideo(bytes, assetOptions);
|
|
1697
|
+
}
|
|
1698
|
+
throw new Error(`${where}: cannot save an asset in this run. Use media.to${type === "audio" ? "Audio" : "Video"} from a Code node.`);
|
|
1699
|
+
};
|
|
1700
|
+
const audio = {
|
|
1701
|
+
bytes: avBytes("audio"),
|
|
1702
|
+
toAsset: avToAsset("audio"),
|
|
1703
|
+
info: avMember("audio", "info", ["audio"], undefined, (m) => m.audioOps.info),
|
|
1704
|
+
normalize: avMember("audio", "normalize", ["audio"], "audio", (m) => m.audioOps.normalize),
|
|
1705
|
+
trim: avMember("audio", "trim", ["audio"], "audio", (m) => m.audioOps.trim),
|
|
1706
|
+
concat: avMember("audio", "concat", ["audio"], "audio", (m) => m.audioOps.concat),
|
|
1707
|
+
mix: avMember("audio", "mix", ["audio"], "audio", (m) => m.audioOps.mix),
|
|
1708
|
+
reverse: avMember("audio", "reverse", ["audio"], "audio", (m) => m.audioOps.reverse),
|
|
1709
|
+
fadeIn: avMember("audio", "fadeIn", ["audio"], "audio", (m) => m.audioOps.fadeIn),
|
|
1710
|
+
fadeOut: avMember("audio", "fadeOut", ["audio"], "audio", (m) => m.audioOps.fadeOut),
|
|
1711
|
+
repeat: avMember("audio", "repeat", ["audio"], "audio", (m) => m.audioOps.repeat)
|
|
1712
|
+
};
|
|
1713
|
+
const video = {
|
|
1714
|
+
bytes: avBytes("video"),
|
|
1715
|
+
toAsset: avToAsset("video"),
|
|
1716
|
+
info: avMember("video", "info", ["video"], undefined, (m) => m.videoOps.info),
|
|
1717
|
+
trim: avMember("video", "trim", ["video"], "video", (m) => m.videoOps.trim),
|
|
1718
|
+
resize: avMember("video", "resize", ["video"], "video", (m) => m.videoOps.resize),
|
|
1719
|
+
rotate: avMember("video", "rotate", ["video"], "video", (m) => m.videoOps.rotate),
|
|
1720
|
+
addAudio: avMember("video", "addAudio", ["video", "audio"], "video", (m) => m.videoOps.addAudio),
|
|
1721
|
+
extractAudio: avMember("video", "extractAudio", ["video"], "audio", (m) => m.videoOps.extractAudio),
|
|
1722
|
+
extractFrame: avMember("video", "extractFrame", ["video"], "image", (m) => m.videoOps.extractFrame)
|
|
1723
|
+
};
|
|
1724
|
+
const canvas = {
|
|
1725
|
+
render: imageMember("render", (m) => m.renderCanvas),
|
|
1726
|
+
measureText: mediaMember((m) => m.measureCanvasText)
|
|
1727
|
+
};
|
|
1728
|
+
// Media refs in and out. Unlike the transform namespaces, this one needs a
|
|
1729
|
+
// ProcessingContext — it resolves `asset://`, storage keys and package assets
|
|
1730
|
+
// through the host's own resolver — so without one every member throws.
|
|
1731
|
+
// `media.*` reads files, so it resolves paths through the same
|
|
1732
|
+
// `resolveGuestPath` `workspace.*` uses — one containment rule, one
|
|
1733
|
+
// `filesystemAccess` scope, no second scheme to keep in step.
|
|
1734
|
+
const mediaRefBridge = createMediaRefBridge(context, context
|
|
1735
|
+
? {
|
|
1736
|
+
resolvePath: (path) => resolveGuestPath(context, path, false)
|
|
1737
|
+
}
|
|
1738
|
+
: {});
|
|
1739
|
+
// A media handle is accepted wherever `media.*` takes a ref or bytes. The
|
|
1740
|
+
// last step of a chain promotes the result with the matching `media.to*`
|
|
1741
|
+
// call. Only this promotion writes to storage; intermediates never do. The
|
|
1742
|
+
// wrapped members share one signature, so they are held under that type and
|
|
1743
|
+
// only the assembled `media` object claims the bridge's shape.
|
|
1744
|
+
const mediaMembers = Object.fromEntries(Object.entries(mediaRefBridge).map(([name, fn]) => [
|
|
1745
|
+
name,
|
|
1746
|
+
async (...args) => fn(...(await resolveMediaArgs(`media.${name}`, args, false)))
|
|
1747
|
+
]));
|
|
1748
|
+
const promoteTypedHandle = (name, promote) => async (value, options) => {
|
|
1749
|
+
if (isSandboxMediaHandle(value)) {
|
|
1750
|
+
return promote(value, options);
|
|
1751
|
+
}
|
|
1752
|
+
return mediaMembers[name](value, options);
|
|
1753
|
+
};
|
|
1754
|
+
const media = {
|
|
1755
|
+
...mediaMembers,
|
|
1756
|
+
toImage: promoteTypedHandle("toImage", image.toAsset),
|
|
1757
|
+
toAudio: promoteTypedHandle("toAudio", audio.toAsset),
|
|
1758
|
+
toVideo: promoteTypedHandle("toVideo", video.toAsset)
|
|
1759
|
+
};
|
|
375
1760
|
const sandbox = {
|
|
376
1761
|
// Core JS globals are native in QuickJS; we still reflect them in the
|
|
377
1762
|
// descriptor so callers that inspect `sandbox.JSON` / `sandbox.Math`
|
|
@@ -412,33 +1797,81 @@ export function buildSandbox(context) {
|
|
|
412
1797
|
TextDecoder: globalThis.TextDecoder,
|
|
413
1798
|
URL: globalThis.URL,
|
|
414
1799
|
URLSearchParams: globalThis.URLSearchParams,
|
|
415
|
-
//
|
|
1800
|
+
// Timer globals blocked — use sleep() for delays and Promise.all /
|
|
1801
|
+
// parallelMap for concurrency. The engine re-installs host-backed timers
|
|
1802
|
+
// on every evaluation, so the real deletion happens in wrapCode; these
|
|
1803
|
+
// entries keep the manifest's blocked list truthful.
|
|
416
1804
|
setTimeout: undefined,
|
|
1805
|
+
clearTimeout: undefined,
|
|
417
1806
|
setInterval: undefined,
|
|
1807
|
+
clearInterval: undefined,
|
|
1808
|
+
setImmediate: undefined,
|
|
1809
|
+
clearImmediate: undefined,
|
|
418
1810
|
// Bridge functions — the only non-native surface the sandbox exposes.
|
|
419
1811
|
fetch: sandboxedFetch,
|
|
420
|
-
|
|
1812
|
+
crypto: sandboxCrypto,
|
|
421
1813
|
sleep,
|
|
422
1814
|
getSecret,
|
|
423
1815
|
workspace,
|
|
424
1816
|
assetToSandbox,
|
|
425
1817
|
sandboxToAsset,
|
|
426
|
-
|
|
1818
|
+
progress,
|
|
1819
|
+
emit,
|
|
1820
|
+
output,
|
|
1821
|
+
// Behind the `stream` global the prelude builds; never reachable by name,
|
|
1822
|
+
// which is why they are not in EXPOSED_BRIDGE_NAMES.
|
|
1823
|
+
[SANDBOX_INPUT_TAKE_BINDING]: takeInput,
|
|
1824
|
+
[SANDBOX_STREAM_OPEN_BINDING]: streamOpen,
|
|
1825
|
+
format,
|
|
1826
|
+
image,
|
|
1827
|
+
audio,
|
|
1828
|
+
video,
|
|
1829
|
+
canvas,
|
|
1830
|
+
media,
|
|
1831
|
+
__maxIter: MAX_LOOP_ITERATIONS,
|
|
1832
|
+
// The run's declared secret scope, so `nodetool.secrets.list()` can answer
|
|
1833
|
+
// without a host call. Reading it is not the check — `getSecret` is.
|
|
1834
|
+
__secretScope: secretScope === null ? null : [...secretScope]
|
|
1835
|
+
};
|
|
1836
|
+
return {
|
|
1837
|
+
sandbox,
|
|
1838
|
+
getLogs: () => logs,
|
|
1839
|
+
getOutputs: () => outputs.size > 0 ? Object.fromEntries(outputs) : undefined,
|
|
1840
|
+
getEmitted: () => (emitted.length > 0 ? [...emitted] : undefined)
|
|
427
1841
|
};
|
|
428
|
-
return { sandbox, getLogs: () => logs };
|
|
429
1842
|
}
|
|
430
1843
|
// ---------------------------------------------------------------------------
|
|
431
1844
|
// Code execution
|
|
432
1845
|
// ---------------------------------------------------------------------------
|
|
433
1846
|
/**
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
1847
|
+
* User-code wrapping and the guest module loader live with the interpreter:
|
|
1848
|
+
* the worker path evaluates the same entry module and resolves through the
|
|
1849
|
+
* same loader, and a second copy of either would be a second security policy.
|
|
437
1850
|
*/
|
|
438
|
-
export
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
1851
|
+
export { BLOCKED_TIMER_GLOBALS, buildEntryModule, createGuestModuleHost, DELETED_GUEST_GLOBALS, EXPOSED_BRIDGE_NAMES, SANDBOX_CAPABILITY_BRIDGE_BINDING, SANDBOX_HOST_BRIDGE_BINDING, SANDBOX_INPUT_TAKE_BINDING, SANDBOX_STREAM_OPEN_BINDING, SANDBOX_WASM_BRIDGE_BINDING, wrapCode } from "./js-sandbox-worker/interpreter.js";
|
|
1852
|
+
export function createSandboxClock() {
|
|
1853
|
+
let depth = 0;
|
|
1854
|
+
let openedAt = 0;
|
|
1855
|
+
let accumulated = 0;
|
|
1856
|
+
return {
|
|
1857
|
+
suspend() {
|
|
1858
|
+
if (depth === 0)
|
|
1859
|
+
openedAt = Date.now();
|
|
1860
|
+
depth++;
|
|
1861
|
+
let resumed = false;
|
|
1862
|
+
return () => {
|
|
1863
|
+
if (resumed)
|
|
1864
|
+
return;
|
|
1865
|
+
resumed = true;
|
|
1866
|
+
depth--;
|
|
1867
|
+
if (depth === 0)
|
|
1868
|
+
accumulated += Date.now() - openedAt;
|
|
1869
|
+
};
|
|
1870
|
+
},
|
|
1871
|
+
suspendedMs() {
|
|
1872
|
+
return depth > 0 ? accumulated + (Date.now() - openedAt) : accumulated;
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
442
1875
|
}
|
|
443
1876
|
const IDENTIFIER_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
444
1877
|
/** Overwrite the contents of `target` with the contents of `source` in place. */
|
|
@@ -451,47 +1884,77 @@ function replaceInPlace(target, source) {
|
|
|
451
1884
|
}
|
|
452
1885
|
return;
|
|
453
1886
|
}
|
|
454
|
-
if (target
|
|
1887
|
+
if (isObjectLike(target)) {
|
|
455
1888
|
const t = target;
|
|
456
1889
|
for (const key of Object.keys(t))
|
|
457
1890
|
delete t[key];
|
|
458
|
-
if (
|
|
1891
|
+
if (isRecord(source)) {
|
|
459
1892
|
const s = source;
|
|
460
1893
|
for (const [k, v] of Object.entries(s))
|
|
461
1894
|
t[k] = v;
|
|
462
1895
|
}
|
|
463
1896
|
}
|
|
464
1897
|
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Globals the guest prelude defines rather than the host marshals. All but
|
|
1900
|
+
* `stream` are pure — no host call behind them; `stream` is built here because
|
|
1901
|
+
* an async iterable cannot cross the bridge, so the prelude wraps the two
|
|
1902
|
+
* private input bindings into one.
|
|
1903
|
+
*/
|
|
1904
|
+
export const GUEST_HELPER_NAMES = [
|
|
1905
|
+
"toBase64",
|
|
1906
|
+
"fromBase64",
|
|
1907
|
+
"toHex",
|
|
1908
|
+
"fromHex",
|
|
1909
|
+
"parallelMap",
|
|
1910
|
+
"createCanvas",
|
|
1911
|
+
"stream"
|
|
1912
|
+
];
|
|
1913
|
+
/**
|
|
1914
|
+
* A dispatcher's entry point as a bare function, or nothing when the run
|
|
1915
|
+
* declared no such modules — which is what decides whether the guest gets the
|
|
1916
|
+
* binding at all.
|
|
1917
|
+
*/
|
|
1918
|
+
function dispatchCallOf(dispatcher) {
|
|
1919
|
+
if (dispatcher === undefined)
|
|
1920
|
+
return undefined;
|
|
1921
|
+
return async (moduleKey, exportName, args) => dispatcher.call(moduleKey, exportName, args);
|
|
1922
|
+
}
|
|
465
1923
|
/** Names that should never be reassigned via `globals` — core sandbox APIs. */
|
|
466
|
-
const RESERVED_SANDBOX_NAMES = new Set([
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
1924
|
+
export const RESERVED_SANDBOX_NAMES = new Set([
|
|
1925
|
+
...EXPOSED_BRIDGE_NAMES,
|
|
1926
|
+
...GUEST_HELPER_NAMES,
|
|
1927
|
+
SANDBOX_WASM_BRIDGE_BINDING,
|
|
1928
|
+
SANDBOX_WASM_DISPATCH_GLOBAL,
|
|
1929
|
+
SANDBOX_CAPABILITY_BRIDGE_BINDING,
|
|
1930
|
+
SANDBOX_CAPABILITY_DISPATCH_GLOBAL,
|
|
1931
|
+
SANDBOX_INPUT_TAKE_BINDING,
|
|
1932
|
+
SANDBOX_STREAM_OPEN_BINDING,
|
|
1933
|
+
GUEST_MARSHAL_GLOBAL,
|
|
1934
|
+
GUEST_GLOBALS_JSON_BINDING,
|
|
1935
|
+
GUEST_GLOBALS_SIDECAR_BINDING
|
|
476
1936
|
]);
|
|
477
1937
|
/**
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
* host versions creates thousands of handles that slow execution and leak on
|
|
482
|
-
* teardown.
|
|
1938
|
+
* Combine a guest error's name and message the way the report reads best:
|
|
1939
|
+
* include the name when it carries signal (`ExecutionTimeout`), drop it when
|
|
1940
|
+
* it is `Error` or already inside the message.
|
|
483
1941
|
*/
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
1942
|
+
function combineGuestError(name, message) {
|
|
1943
|
+
return name &&
|
|
1944
|
+
name !== "Error" &&
|
|
1945
|
+
!message.toLowerCase().includes(name.toLowerCase())
|
|
1946
|
+
? `${name}: ${message}`
|
|
1947
|
+
: message || name;
|
|
1948
|
+
}
|
|
1949
|
+
/** Warn once per process when the worker path silently falls back in-process. */
|
|
1950
|
+
let warnedWorkerFallback = false;
|
|
1951
|
+
function noteWorkerFallback(reason) {
|
|
1952
|
+
if (warnedWorkerFallback)
|
|
1953
|
+
return;
|
|
1954
|
+
warnedWorkerFallback = true;
|
|
1955
|
+
console.warn(`sandbox: running in-process (${reason}); a CPU-bound guest will block ` +
|
|
1956
|
+
"this thread until its timeout");
|
|
1957
|
+
}
|
|
495
1958
|
/**
|
|
496
1959
|
* Execute JavaScript code inside a QuickJS WebAssembly sandbox.
|
|
497
1960
|
*
|
|
@@ -499,11 +1962,57 @@ const EXPOSED_BRIDGE_NAMES = [
|
|
|
499
1962
|
* handler / memory limiter, so runaway user code can't exhaust host resources.
|
|
500
1963
|
*/
|
|
501
1964
|
export async function runInSandbox(options) {
|
|
502
|
-
const { code, context, timeoutMs = DEFAULT_TIMEOUT_MS, globals } = options;
|
|
1965
|
+
const { code, context, timeoutMs = DEFAULT_TIMEOUT_MS, globals, signal, limits, onProgress, onLog, onEmit, onTakeInput, onStreamOpen, clock, modules, capabilities, wasmPool, resolveMediaRef, promoteMedia, resolveHost } = options;
|
|
1966
|
+
const resolvedLimits = resolveSandboxLimits(limits);
|
|
1967
|
+
// A streaming run needs a clock whether or not the caller brought one: the
|
|
1968
|
+
// time it spends parked on upstream is not its own execution.
|
|
1969
|
+
const activeClock = clock ?? (onTakeInput ? createSandboxClock() : undefined);
|
|
1970
|
+
const suspendAllowanceMs = options.suspendAllowanceMs ??
|
|
1971
|
+
(onTakeInput
|
|
1972
|
+
? INPUT_STREAM_SUSPEND_ALLOWANCE_MS
|
|
1973
|
+
: DEFAULT_SUSPEND_ALLOWANCE_MS);
|
|
503
1974
|
if (!code.trim()) {
|
|
504
1975
|
return { success: false, error: "No code provided", logs: [] };
|
|
505
1976
|
}
|
|
506
|
-
|
|
1977
|
+
// The dispatcher is built first: it reads every declared WASM binary, so a
|
|
1978
|
+
// module the catalog could not honour is reported here rather than as a
|
|
1979
|
+
// broken facade the guest imports.
|
|
1980
|
+
let wasm;
|
|
1981
|
+
let hostModules;
|
|
1982
|
+
const wasmOptions = {};
|
|
1983
|
+
if (wasmPool !== undefined)
|
|
1984
|
+
wasmOptions.pool = wasmPool;
|
|
1985
|
+
if (signal !== undefined)
|
|
1986
|
+
wasmOptions.signal = signal;
|
|
1987
|
+
const hostOptions = {};
|
|
1988
|
+
if (signal !== undefined)
|
|
1989
|
+
hostOptions.signal = signal;
|
|
1990
|
+
try {
|
|
1991
|
+
wasm = modules
|
|
1992
|
+
? createSandboxWasmDispatcher(modules.modules, wasmOptions)
|
|
1993
|
+
: undefined;
|
|
1994
|
+
hostModules = modules
|
|
1995
|
+
? createSandboxHostDispatcher(modules.modules, hostOptions)
|
|
1996
|
+
: undefined;
|
|
1997
|
+
}
|
|
1998
|
+
catch (error) {
|
|
1999
|
+
return {
|
|
2000
|
+
success: false,
|
|
2001
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2002
|
+
logs: []
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
if (signal?.aborted) {
|
|
2006
|
+
return { success: false, error: "Execution cancelled", logs: [] };
|
|
2007
|
+
}
|
|
2008
|
+
const streams = {};
|
|
2009
|
+
if (onTakeInput !== undefined)
|
|
2010
|
+
streams.onTakeInput = onTakeInput;
|
|
2011
|
+
if (onStreamOpen !== undefined)
|
|
2012
|
+
streams.onStreamOpen = onStreamOpen;
|
|
2013
|
+
if (activeClock !== undefined)
|
|
2014
|
+
streams.clock = activeClock;
|
|
2015
|
+
const { sandbox, getLogs, getOutputs, getEmitted } = buildSandbox(context, signal, resolvedLimits, onProgress, onLog, onEmit, streams, resolveMediaRef, promoteMedia, resolveHost);
|
|
507
2016
|
// User-supplied globals (dynamic inputs from CodeNode etc.) layer on top of
|
|
508
2017
|
// the core surface, but must not clobber the bridge functions themselves.
|
|
509
2018
|
const userGlobals = {};
|
|
@@ -519,136 +2028,279 @@ export async function runInSandbox(options) {
|
|
|
519
2028
|
// Identify object-typed globals whose contents should be synced back to the
|
|
520
2029
|
// host after the guest runs. Primitives are passed by value and need no sync.
|
|
521
2030
|
const syncTargetNames = Object.entries(userGlobals)
|
|
522
|
-
.filter(([, v]) => v
|
|
2031
|
+
.filter(([, v]) => isObjectLike(v))
|
|
523
2032
|
.map(([k]) => k);
|
|
2033
|
+
// Write the extracted object-global snapshots back into the caller's own
|
|
2034
|
+
// objects. Both paths end here: node:vm shared the host heap, QuickJS does
|
|
2035
|
+
// not, and a worker makes the separation literal. CodeNode relies on this to
|
|
2036
|
+
// make its `state` object persist across invocations.
|
|
2037
|
+
const writeBackGlobals = (extracted) => {
|
|
2038
|
+
if (!extracted)
|
|
2039
|
+
return;
|
|
2040
|
+
for (const name of syncTargetNames) {
|
|
2041
|
+
const hostValue = userGlobals[name];
|
|
2042
|
+
const guestValue = extracted[name];
|
|
2043
|
+
if (isObjectLike(hostValue) && isObjectLike(guestValue)) {
|
|
2044
|
+
replaceInPlace(hostValue, guestValue);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
// Off the main thread whenever possible. A CPU-bound guest blocks whichever
|
|
2049
|
+
// thread runs it; on the server's main thread that freeze takes the whole
|
|
2050
|
+
// event loop — including the websocket `stop` frame that would have
|
|
2051
|
+
// cancelled the run — down with it. On a worker, abort is `terminate()`,
|
|
2052
|
+
// immediate for spinning and parked guests alike; the logs and emitted
|
|
2053
|
+
// values survive because they accumulate on this side of the port.
|
|
2054
|
+
//
|
|
2055
|
+
// Streaming runs stay in-process: the synchronous `stream.open` probe is
|
|
2056
|
+
// served from a worker-local mirror that must be seeded with the handle
|
|
2057
|
+
// names, which this signature does not carry. Those runs park on takes and
|
|
2058
|
+
// yield the thread constantly, so the freeze this path exists for cannot
|
|
2059
|
+
// build up there.
|
|
2060
|
+
const env = typeof globalThis.process?.env === "object"
|
|
2061
|
+
? globalThis.process.env
|
|
2062
|
+
: undefined;
|
|
2063
|
+
const workerRequired = env?.NODETOOL_SANDBOX_WORKER === "require";
|
|
2064
|
+
let workerUnavailable = null;
|
|
2065
|
+
// A chosen or designed fallback is quiet; an environmental one warns once.
|
|
2066
|
+
let fallbackByDesign = false;
|
|
2067
|
+
if (env?.NODETOOL_SANDBOX_INPROC === "1") {
|
|
2068
|
+
workerUnavailable = "NODETOOL_SANDBOX_INPROC=1";
|
|
2069
|
+
fallbackByDesign = true;
|
|
2070
|
+
}
|
|
2071
|
+
else if (onTakeInput !== undefined || onStreamOpen !== undefined) {
|
|
2072
|
+
workerUnavailable = "the run streams inputs";
|
|
2073
|
+
fallbackByDesign = true;
|
|
2074
|
+
}
|
|
2075
|
+
else {
|
|
2076
|
+
const safety = precheckCloneSafety(userGlobals);
|
|
2077
|
+
if (!safety.ok) {
|
|
2078
|
+
workerUnavailable = safety.reason;
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
if (workerUnavailable === null) {
|
|
2082
|
+
const dispatcherKinds = [];
|
|
2083
|
+
const dispatch = { ...sandbox };
|
|
2084
|
+
const bindDispatcher = (kind, call) => {
|
|
2085
|
+
if (call === undefined)
|
|
2086
|
+
return;
|
|
2087
|
+
dispatch[SANDBOX_DISPATCHER_BINDINGS[kind]] = call;
|
|
2088
|
+
dispatcherKinds.push(kind);
|
|
2089
|
+
};
|
|
2090
|
+
bindDispatcher("wasm", dispatchCallOf(wasm));
|
|
2091
|
+
bindDispatcher("host", dispatchCallOf(hostModules));
|
|
2092
|
+
bindDispatcher("capability", dispatchCallOf(capabilities));
|
|
2093
|
+
for (const [name, value] of Object.entries(userGlobals)) {
|
|
2094
|
+
if (isFunction(value))
|
|
2095
|
+
dispatch[name] = value;
|
|
2096
|
+
}
|
|
2097
|
+
const workerRun = {
|
|
2098
|
+
runId: globalThis.crypto.randomUUID(),
|
|
2099
|
+
code,
|
|
2100
|
+
timeoutMs,
|
|
2101
|
+
limits: resolvedLimits,
|
|
2102
|
+
suspendAllowanceMs,
|
|
2103
|
+
hasClock: activeClock !== undefined,
|
|
2104
|
+
engineTimeoutMs: activeClock !== undefined
|
|
2105
|
+
? Math.min(timeoutMs + suspendAllowanceMs, MAX_ENGINE_TIMEOUT_MS)
|
|
2106
|
+
: timeoutMs,
|
|
2107
|
+
streamOpenSeed: null,
|
|
2108
|
+
bridgeShape: deriveBridgeShape({
|
|
2109
|
+
bridges: sandbox,
|
|
2110
|
+
dispatchers: dispatcherKinds,
|
|
2111
|
+
globals: userGlobals
|
|
2112
|
+
})
|
|
2113
|
+
};
|
|
2114
|
+
if (modules !== undefined)
|
|
2115
|
+
workerRun.modules = modules;
|
|
2116
|
+
if (capabilities?.facades !== undefined) {
|
|
2117
|
+
workerRun.capabilityFacades = capabilities.facades;
|
|
2118
|
+
}
|
|
2119
|
+
const workerOptions = {
|
|
2120
|
+
run: workerRun,
|
|
2121
|
+
dispatch,
|
|
2122
|
+
// Console and progress already reach their accumulators through the
|
|
2123
|
+
// RPC'd bridges; the push channel exists for a worker that has nothing
|
|
2124
|
+
// else to say, which this integration does not use.
|
|
2125
|
+
onLog: () => { },
|
|
2126
|
+
onProgress: () => { }
|
|
2127
|
+
};
|
|
2128
|
+
if (signal !== undefined)
|
|
2129
|
+
workerOptions.signal = signal;
|
|
2130
|
+
if (activeClock !== undefined) {
|
|
2131
|
+
workerOptions.suspendedMs = () => activeClock.suspendedMs();
|
|
2132
|
+
}
|
|
2133
|
+
const message = await runInWorker(workerOptions);
|
|
2134
|
+
if (message === null) {
|
|
2135
|
+
workerUnavailable = "no sandbox worker could be started here";
|
|
2136
|
+
}
|
|
2137
|
+
else {
|
|
2138
|
+
writeBackGlobals(message.syncedGlobals);
|
|
2139
|
+
const logs = getLogs();
|
|
2140
|
+
// Cancellation wins over whatever the guest was doing when the interrupt
|
|
2141
|
+
// handler cut it — the in-process race has the same rule. The interrupted
|
|
2142
|
+
// guest's own error (`InternalError: interrupted`) is an implementation
|
|
2143
|
+
// detail, not a result.
|
|
2144
|
+
if (signal?.aborted === true) {
|
|
2145
|
+
return {
|
|
2146
|
+
success: false,
|
|
2147
|
+
error: "Execution cancelled",
|
|
2148
|
+
logs,
|
|
2149
|
+
emitted: getEmitted()
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
if (message.failure === "cancelled") {
|
|
2153
|
+
return {
|
|
2154
|
+
success: false,
|
|
2155
|
+
error: "Execution cancelled",
|
|
2156
|
+
logs,
|
|
2157
|
+
emitted: getEmitted()
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
if (message.failure === "worker") {
|
|
2161
|
+
return {
|
|
2162
|
+
success: false,
|
|
2163
|
+
error: describeEngineFailure(new Error(message.errorMessage ?? "the sandbox worker failed")),
|
|
2164
|
+
logs: logs.length > 0 ? logs : undefined,
|
|
2165
|
+
emitted: getEmitted()
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
if (!message.evalOk) {
|
|
2169
|
+
return {
|
|
2170
|
+
success: false,
|
|
2171
|
+
error: combineGuestError(message.errorName ?? "", message.errorMessage ?? ""),
|
|
2172
|
+
stack: message.errorStack
|
|
2173
|
+
? cleanStack(message.errorStack)
|
|
2174
|
+
: undefined,
|
|
2175
|
+
logs: logs.length > 0 ? logs : undefined,
|
|
2176
|
+
emitted: getEmitted()
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
return {
|
|
2180
|
+
success: true,
|
|
2181
|
+
result: serializeResult(message.data, resolvedLimits.maxOutputSize),
|
|
2182
|
+
logs: logs.length > 0 ? logs : undefined,
|
|
2183
|
+
outputs: getOutputs(),
|
|
2184
|
+
emitted: getEmitted()
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
if (workerRequired) {
|
|
2189
|
+
return {
|
|
2190
|
+
success: false,
|
|
2191
|
+
error: `the sandbox worker path is required (NODETOOL_SANDBOX_WORKER=require) but unavailable: ${workerUnavailable}`,
|
|
2192
|
+
logs: []
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
if (!fallbackByDesign) {
|
|
2196
|
+
noteWorkerFallback(workerUnavailable);
|
|
2197
|
+
}
|
|
524
2198
|
try {
|
|
525
2199
|
const { runSandboxed } = await getEngine();
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
//
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
};
|
|
569
|
-
globalThis.fetch = __wrap(globalThis.fetch);
|
|
570
|
-
globalThis.sleep = __wrap(globalThis.sleep);
|
|
571
|
-
globalThis.getSecret = __wrap(globalThis.getSecret);
|
|
572
|
-
globalThis.assetToSandbox = __wrap(globalThis.assetToSandbox);
|
|
573
|
-
globalThis.sandboxToAsset = __wrap(globalThis.sandboxToAsset);
|
|
574
|
-
const __ws = globalThis.workspace;
|
|
575
|
-
globalThis.workspace = {
|
|
576
|
-
read: __wrap(__ws.read),
|
|
577
|
-
write: __wrap(__ws.write),
|
|
578
|
-
list: __wrap(__ws.list)
|
|
579
|
-
};
|
|
580
|
-
delete globalThis.eval;
|
|
581
|
-
delete globalThis.Function;
|
|
582
|
-
export default true;`, "sandbox-init");
|
|
583
|
-
const userResult = await evalCode(wrapCode(code), "user-code");
|
|
584
|
-
// Sync mutable globals back to the host. node:vm shared the host heap,
|
|
585
|
-
// so `state.counter++` in user code mutated the caller's object directly.
|
|
586
|
-
// With QuickJS the guest heap is isolated, so after user code runs we
|
|
587
|
-
// extract the current values of the object-typed user globals and
|
|
588
|
-
// replace the contents of the host-side objects in place. CodeNode
|
|
589
|
-
// relies on this to make its `state` object persist across invocations.
|
|
590
|
-
if (userResult.ok && syncTargetNames.length > 0) {
|
|
591
|
-
const extractor = `export default {${syncTargetNames
|
|
592
|
-
.map((n) => `${n}: (typeof ${n} !== 'undefined' && ${n} !== null) ? ${n} : null`)
|
|
593
|
-
.join(", ")}};`;
|
|
594
|
-
const syncResp = await evalCode(extractor, "sandbox-sync");
|
|
595
|
-
if (syncResp.ok && syncResp.data && typeof syncResp.data === "object") {
|
|
596
|
-
const extracted = syncResp.data;
|
|
597
|
-
for (const name of syncTargetNames) {
|
|
598
|
-
const hostValue = userGlobals[name];
|
|
599
|
-
const guestValue = extracted[name];
|
|
600
|
-
if (hostValue !== null &&
|
|
601
|
-
typeof hostValue === "object" &&
|
|
602
|
-
guestValue !== null &&
|
|
603
|
-
typeof guestValue === "object") {
|
|
604
|
-
replaceInPlace(hostValue, guestValue);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
return userResult;
|
|
610
|
-
}, {
|
|
611
|
-
executionTimeout: timeoutMs,
|
|
612
|
-
memoryLimit: GUEST_MEMORY_LIMIT,
|
|
613
|
-
maxStackSize: GUEST_STACK_LIMIT
|
|
2200
|
+
// The dispatchers stay on this side of the boundary — the interpreter only
|
|
2201
|
+
// ever sees a call function, which is as true of a worker's RPC proxy as it
|
|
2202
|
+
// is of the real dispatcher here.
|
|
2203
|
+
const interpreterParams = {
|
|
2204
|
+
runSandboxed,
|
|
2205
|
+
code,
|
|
2206
|
+
sandbox,
|
|
2207
|
+
globals: userGlobals,
|
|
2208
|
+
syncTargetNames,
|
|
2209
|
+
timeoutMs,
|
|
2210
|
+
limits: resolvedLimits,
|
|
2211
|
+
suspendAllowanceMs,
|
|
2212
|
+
hasClock: activeClock !== undefined,
|
|
2213
|
+
wasmCall: dispatchCallOf(wasm),
|
|
2214
|
+
hostCall: dispatchCallOf(hostModules),
|
|
2215
|
+
capabilityCall: dispatchCallOf(capabilities),
|
|
2216
|
+
modules,
|
|
2217
|
+
capabilityFacades: capabilities?.facades
|
|
2218
|
+
};
|
|
2219
|
+
// The interpreter cannot hold either host object: neither an AbortSignal
|
|
2220
|
+
// nor a clock survives a thread boundary, so both arrive as a probe.
|
|
2221
|
+
if (signal !== undefined) {
|
|
2222
|
+
interpreterParams.isAborted = () => signal.aborted;
|
|
2223
|
+
}
|
|
2224
|
+
if (activeClock !== undefined) {
|
|
2225
|
+
interpreterParams.suspendedMs = () => activeClock.suspendedMs();
|
|
2226
|
+
}
|
|
2227
|
+
const sandboxRun = runInterpreter(interpreterParams).then((outcome) => {
|
|
2228
|
+
// The write-back runs as soon as the guest is done, even when
|
|
2229
|
+
// cancellation already won the race below — an orphaned run still owns
|
|
2230
|
+
// the answer it computed.
|
|
2231
|
+
writeBackGlobals(outcome.syncedGlobals);
|
|
2232
|
+
return outcome;
|
|
2233
|
+
});
|
|
2234
|
+
// Return as soon as cancellation lands. The bridges above make a guest that
|
|
2235
|
+
// awaits anything unwind almost immediately; this race additionally covers
|
|
2236
|
+
// a CPU-bound guest, which QuickJS gives us no way to interrupt early — the
|
|
2237
|
+
// orphaned run still winds down on its own execution timeout.
|
|
2238
|
+
const cancellation = new Promise((resolve) => {
|
|
2239
|
+
if (!signal)
|
|
2240
|
+
return;
|
|
2241
|
+
signal.addEventListener("abort", () => resolve(null), { once: true });
|
|
614
2242
|
});
|
|
2243
|
+
const raced = await guardHostProcess(Promise.race([sandboxRun, cancellation]));
|
|
2244
|
+
if (raced === null) {
|
|
2245
|
+
return {
|
|
2246
|
+
success: false,
|
|
2247
|
+
error: "Execution cancelled",
|
|
2248
|
+
logs: getLogs(),
|
|
2249
|
+
emitted: getEmitted()
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
const evalResponse = raced;
|
|
615
2253
|
const logs = getLogs();
|
|
616
2254
|
if (!evalResponse.ok) {
|
|
617
|
-
|
|
618
|
-
// useful signal (e.g. `ExecutionTimeout` for the library's wall-clock
|
|
619
|
-
// abort). `Error` is redundant, so omit it.
|
|
620
|
-
const name = evalResponse.error.name;
|
|
621
|
-
const message = evalResponse.error.message;
|
|
622
|
-
const combined = name && name !== "Error" && !message.toLowerCase().includes(name.toLowerCase())
|
|
623
|
-
? `${name}: ${message}`
|
|
624
|
-
: message || name;
|
|
2255
|
+
const combined = combineGuestError(evalResponse.error.name, evalResponse.error.message);
|
|
625
2256
|
return {
|
|
626
2257
|
success: false,
|
|
627
2258
|
error: combined,
|
|
628
2259
|
stack: evalResponse.error.stack
|
|
629
2260
|
? cleanStack(evalResponse.error.stack)
|
|
630
2261
|
: undefined,
|
|
631
|
-
logs: logs.length > 0 ? logs : undefined
|
|
2262
|
+
logs: logs.length > 0 ? logs : undefined,
|
|
2263
|
+
// Emitted values were delivered while the guest still ran, so they
|
|
2264
|
+
// stand. Recorded `output` finals do not: the run has no answer.
|
|
2265
|
+
emitted: getEmitted()
|
|
632
2266
|
};
|
|
633
2267
|
}
|
|
634
2268
|
return {
|
|
635
2269
|
success: true,
|
|
636
|
-
result: serializeResult(evalResponse.data),
|
|
637
|
-
logs: logs.length > 0 ? logs : undefined
|
|
2270
|
+
result: serializeResult(evalResponse.data, resolvedLimits.maxOutputSize),
|
|
2271
|
+
logs: logs.length > 0 ? logs : undefined,
|
|
2272
|
+
outputs: getOutputs(),
|
|
2273
|
+
emitted: getEmitted()
|
|
638
2274
|
};
|
|
639
2275
|
}
|
|
640
2276
|
catch (e) {
|
|
641
|
-
// Host-side failures (engine load error, marshaling bug,
|
|
642
|
-
// errors go through evalResponse.ok=false above.
|
|
2277
|
+
// Host-side failures (engine load error, marshaling bug, an aborted
|
|
2278
|
+
// runtime). Guest-side errors go through evalResponse.ok=false above.
|
|
2279
|
+
// An engine failure is reported in terms the caller can act on: the raw
|
|
2280
|
+
// Emscripten assertion text tells an agent nothing it can retry against.
|
|
643
2281
|
const logs = getLogs();
|
|
644
|
-
const errorMessage =
|
|
2282
|
+
const errorMessage = describeEngineFailure(e);
|
|
645
2283
|
const errorStack = e instanceof Error ? cleanStack(e.stack ?? "") : undefined;
|
|
646
2284
|
return {
|
|
647
2285
|
success: false,
|
|
648
2286
|
error: errorMessage,
|
|
649
2287
|
stack: errorStack,
|
|
650
|
-
logs: logs.length > 0 ? logs : undefined
|
|
2288
|
+
logs: logs.length > 0 ? logs : undefined,
|
|
2289
|
+
emitted: getEmitted()
|
|
651
2290
|
};
|
|
652
2291
|
}
|
|
653
2292
|
}
|
|
2293
|
+
// ---------------------------------------------------------------------------
|
|
2294
|
+
// Guest preludes for tool-bridged hosts
|
|
2295
|
+
// ---------------------------------------------------------------------------
|
|
2296
|
+
/**
|
|
2297
|
+
* Re-exported here because `./js-sandbox` is the one browser-safe subpath
|
|
2298
|
+
* export of this package: `nodetool.code.Code` (packages/code-nodes) is
|
|
2299
|
+
* bundled for the in-browser runner and must reach these prelude strings
|
|
2300
|
+
* without importing the package index, which drags the whole toolbelt —
|
|
2301
|
+
* native canvas, IMAP, execution — into a web build. Both modules are pure
|
|
2302
|
+
* strings over leaf imports.
|
|
2303
|
+
*/
|
|
2304
|
+
export { TOOLS_PRELUDE } from "./codeact/tools-prelude.js";
|
|
2305
|
+
export { NODETOOL_API_PRELUDE_FULL } from "./codeact/nodetool-api.js";
|
|
654
2306
|
//# sourceMappingURL=js-sandbox.js.map
|