This commit is contained in:
seb
2026-10-11 12:58:15 +02:00
parent 8262e35e53
commit 1149ffddcb
24 changed files with 575 additions and 261 deletions

View File

@@ -3,6 +3,7 @@ import path from 'path';
import jwt from 'jsonwebtoken';
import { defineTool, ToolError } from 'libllmorbiter';
import env from '../config/env.js';
import { TOOL_DESCRIPTIONS, TOOL_PARAMETER_DESCRIPTIONS } from './prompts.js';
import { createVfs, prepareVfs, syncVfsToDb } from './vfs.js';
import { runSandboxJob } from '../services/javascriptSandbox.js';
@@ -271,17 +272,7 @@ function formatSandboxError(code, report) {
export function createJavascriptExecuteTool({ googleAuthId }) {
return defineTool({
name: 'javascript_execute',
description:
'Execute JS in an isolated worker sandbox using either inline `code` or a VFS applet. Provide exactly one of `code` and `appletvfsfile`. ' +
'Applets are the preferred method when the first execution may throw: save a `.applet.js` file in the VFS so it can be inspected and further edited with `patch`, then call it by VFS path. ' +
'Applet files must start with `// @applet-input-schema {JSON Schema}` on line 1 and `// @applet-output-schema <columns>` on line 2. ' +
'The input schema describes the GUI form passed as `applet_input`. The output line declares the RESULT COLUMNS as typed CSV: `name:type` pairs separated by `;` (e.g. `id:integer;name:string;active:boolean`); a bare `name` defaults to string, and the allowed types are string, integer, number, boolean and date. ' +
'Applets return typed CSV, NOT JSON: export a function that receives `applet_input` and returns a row object, or an array of row objects, whose keys are the declared columns (e.g. `return [{ id: 1, name: "Kaffee" }]`). Only declared columns are emitted, in header order; missing keys become empty cells. The tool result then carries `result` as a `;`-separated CSV string with a header row, plus `columns` (name/type) and `rows` (data row count). Returning a single object yields one data row. ' +
'Inline code: assign `module.exports` to return value (sync value or Promise; wrap async in IIFE: `module.exports = (async () => { ... })()`). ' +
'REST API: `await fetch("/path")` (authenticated). ' +
'VFS: directly use global `fs.readFileSync`/`writeFileSync` for binary artifacts/buffers/images (`image/` is read-only; paths must be relative paths, root is not allowed). VFS can host a static website (HTML, CSS, JS, images, etc.). ' +
'Embed generated images/links in markdown: `![alt](relative/path)`. Internet links use `https://...` or `hostname.com`/`hostname.com/filename`; a pure relative file with no dotted hostname is always a VFS relative path. Use find_files/read_file/patch for text. ' +
'All APIs are pre-injected globals: `fs`/`vfs` (virtual filesystem), `sharp` (image processing), `ExcelJS`/`exceljs` (spreadsheet processing with VFS support for readFile/writeFile/writeBuffer/load), `PDFDocument`/`PDFKit`/`pdfkit` (PDF generation with VFS support for toFile/toBuffer/pipe), WebCrypto `crypto` (`subtle`, `randomUUID`), and standard web globals. Do NOT use `require()` or `import` (disabled; all necessary APIs are already in global scope).',
description: TOOL_DESCRIPTIONS.javascript_execute,
strict: false,
parameters: {
type: 'object',
@@ -289,28 +280,28 @@ export function createJavascriptExecuteTool({ googleAuthId }) {
properties: {
note: {
type: ['string', 'null'],
description: 'A note explaining what this code is doing (or null). This note will be displayed to the user while the code is executing.',
description: TOOL_PARAMETER_DESCRIPTIONS.javascript_execute.note,
},
code: {
type: 'string',
description: 'Optional inline JavaScript code. Provide exactly one of this and appletvfsfile. Assign result to `module.exports`. Async code must use IIFE. Do not use require() or import.',
description: TOOL_PARAMETER_DESCRIPTIONS.javascript_execute.code,
},
appletvfsfile: {
type: 'string',
description: 'Optional VFS-relative path to a .applet.js file. Provide exactly one of this and code. The file must begin with the two applet metadata lines (`@applet-input-schema` JSON Schema, then `@applet-output-schema` typed CSV columns like `id:integer;name:string`).',
description: TOOL_PARAMETER_DESCRIPTIONS.javascript_execute.appletvfsfile,
},
applet_input: {
type: 'object',
description: 'Input values passed to the exported function when running appletvfsfile. Omit to pass an empty object. The applet returns typed CSV rows mapped onto its `@applet-output-schema` columns.',
description: TOOL_PARAMETER_DESCRIPTIONS.javascript_execute.applet_input,
additionalProperties: true,
},
timeout: {
type: ['number', 'null'],
description: `Execution timeout in ms or null. Default ${DEFAULT_TIMEOUT_MS}, max ${MAX_TIMEOUT_MS}.`,
description: TOOL_PARAMETER_DESCRIPTIONS.javascript_execute.timeout,
},
max_output: {
type: ['number', 'null'],
description: `Max stdout chars or null. Default ${DEFAULT_MAX_OUTPUT}.`,
description: TOOL_PARAMETER_DESCRIPTIONS.javascript_execute.max_output,
},
},
},