This commit is contained in:
seb
2026-10-06 03:30:31 +02:00
parent 0369b4e819
commit d5e58e76e1
15 changed files with 103 additions and 196 deletions

View File

@@ -122,29 +122,11 @@ export function createJavascriptExecuteTool({ googleAuthId }) {
return defineTool({
name: 'javascript_execute',
description:
'Run JavaScript in a sandboxed Node environment and return the result. ' +
'Runs in an isolated worker thread with a hard wall-clock timeout. ' +
'Return a value by assigning `module.exports` (sync value or a promise). ' +
'For async code: `module.exports = (async () => { ... })()`. ' +
'console.* is captured and returned as stdout. ' +
'To query the Dashback REST API from JavaScript: call `await fetch("/route")` (e.g. `const res = await fetch("/article/2"); const data = await res.json();`). ' +
'Relative paths automatically resolve against Dashback and are authenticated with your user credentials. ' +
'A virtual filesystem (fs / vfs) is available to store and retrieve artifacts (files, images, JSON data, buffers) across runs (e.g. `fs.writeFileSync("artifact.json", data)`). ' +
'IMAGES WRITTEN TO THE VFS ARE DISPLAYED INLINE IN THE CHAT. After writing an image into the virtual filesystem (e.g. `fs.writeFileSync("artifacts/example.png", buf)`), ' +
'reference the exact path you wrote with standard markdown `![title](artifacts/example.png)` and it renders as an image inline. ' +
'Note the `image/` folder is reserved for database images and is read-only; write generated images anywhere else. ' +
'Always embed images you generate this way so the user sees them. Other file types do not render inline. ' +
'For TEXT use the dedicated find_files / read_file / patch tools instead of hand-rolling fs calls — they validate paths and persist to the database. ' +
'Use this sandbox for BINARY work and computation: images, PDFs, buffers, charts, encoding. ' +
'To find or inspect images, use the search_images and analyze_image tools — they are not available inside the sandbox. ' +
'The `sharp` image library is a pre-injected GLOBAL. ' +
'Also global: `validateLabelObjects(objects)` and `validateLabelObject(object)` from react-canvas-label-editor. ' +
'CRYPTO: only the WebCrypto global `crypto` is available. Node\'s `crypto` module is NOT installed, so ' +
'`require("crypto")` will fail. Available: `crypto.randomUUID()`, `crypto.getRandomValues(typedArray)`, ' +
'and the async `crypto.subtle` API (digest/hash, sign/verify, encrypt/decrypt, importKey/exportKey, deriveBits). ' +
'`crypto.subtle` is async, so use it inside an async IIFE: ' +
'`module.exports = (async () => (await crypto.subtle.digest("SHA-256", data)).byteLength)();`. ' +
'Typed arrays work correctly across the sandbox boundary (verified against Node\'s real digests).',
'Execute JS in isolated Node worker sandbox. Assign `module.exports` to return value (sync value or Promise; no bare top-level await — wrap async in IIFE: `module.exports = (async () => { ... })()`). ' +
'REST API: `await fetch("/path")` (authenticated). ' +
'VFS: `fs.readFileSync`/`writeFileSync` for binary artifacts/buffers/images (`image/` is read-only). ' +
'Embed generated images in markdown: `![alt](path)`. Use find_files/read_file/patch for text. ' +
'Globals: `fs`/`vfs`, `sharp`, WebCrypto `crypto` (`subtle`, `randomUUID`), web standard globals. Node built-ins unavailable (`require` fails).',
strict: false,
parameters: {
type: 'object',
@@ -153,25 +135,15 @@ export function createJavascriptExecuteTool({ googleAuthId }) {
properties: {
code: {
type: 'string',
description:
'JavaScript source to run in a CommonJS wrapper. Set `module.exports` to return a value; ' +
'for async code assign the PROMISE, e.g. `module.exports = (async () => { ... })()`. ' +
'To query Dashback REST API: `const res = await fetch("/article/2"); const data = await res.json();`. ' +
'To persist BINARY artifacts between runs: use `fs.writeFileSync("name.ext", buffer)` and `fs.readFileSync("name.ext")`. ' +
'For text files prefer the find_files / read_file / patch tools. ' +
'Bare top-level `await` is a syntax error here — always wrap it in that async IIFE. ' +
'Globals available: `fetch`, `console`, `fs`/`vfs`, `sharp`, `performance`, `URL`, `URLSearchParams`, `Headers`, ' +
'`Request`, `Response`, `FormData`, `Blob`, `File`, `TextEncoder`, `TextDecoder`, `AbortController`, ' +
'`AbortSignal`, `setTimeout`, `clearTimeout`, and WebCrypto `crypto` (`randomUUID`, `getRandomValues`, `subtle`). ' +
'No Node built-ins are installable — `require("crypto")`, `require("fs")`, `require("path")` etc. all fail.',
description: 'CommonJS JS code. Assign result to `module.exports`. Async code must use IIFE.',
},
timeout: {
type: 'number',
description: `Hard wall-clock limit in ms. Default ${DEFAULT_TIMEOUT_MS}, max ${MAX_TIMEOUT_MS}.`,
description: `Execution timeout in ms. Default ${DEFAULT_TIMEOUT_MS}, max ${MAX_TIMEOUT_MS}.`,
},
max_output: {
type: 'number',
description: `Max chars of captured console output to return. Default ${DEFAULT_MAX_OUTPUT}.`,
description: `Max stdout chars. Default ${DEFAULT_MAX_OUTPUT}.`,
},
},
},