A JS script is a saved JavaScript document. It has a body, declared input and output ports, the secret names it may read, a timeout, and saved test cases. Unlike the body of a Code node, which lives inside one workflow, a script is a standalone document you can reuse, run on its own, and test.
Scripts run in the server’s QuickJS sandbox, never in the browser. For what the sandbox allows, its limits, and its imports, see JavaScript Sandbox.
Create a script
- Open JS Scripts in the left sidebar.
- Select the New JS script button (+). NodeTool creates “Untitled JS script” and opens it as a workspace tab.
The sidebar panel lists your scripts, newest first, and Search JS scripts… filters them by name or description. Select a script to open it in a tab.
The editor
The script tab has four regions.
- Editor. The script body in Monaco. Every keystroke autosaves. Undo and redo work as in other documents.
- Run console. Below the editor, with Run and Test buttons and the result of the last run.
- Script tab. On the right, the settings panel described below.
- Assistant tab. On the right, next to Script. See Assistant.
On a phone these become four panes, Code, Run, Script, and Assistant, with one visible at a time.
Writing the body
The host wraps the body in an async function, so write top-level statements only.
- Do not write
exportor wrap the body in a function.inputsis already in scope. - Read an input as
inputs.<name>. - Send a result out with
await output(name, value)orawait emit(name, value). - Do not return outputs. A body that returns its outputs fails validation.
- Import any installed sandbox pack or
@nodetool-ai/sandbox-nodetool/<namespace>directly.
await output("total", inputs.numbers.reduce((a, n) => a + n, 0));
Media handles are valid only for the current run. Persist them before output, for example with await image.toAsset(frame). A body that reads its inputs with stream runs once and pulls items as they arrive. For the full model, see the nodetool-js-scripting skill and JavaScript Sandbox.
Declare inputs and outputs
In the Script tab, the Inputs and Outputs lists declare the script’s ports. Select Add to add a row, then enter a name and choose a type. Port names must be valid identifiers, because the body reads them as inputs.<name>.
The available types are str, int, float, bool, list, dict, image, audio, video, document, and any.
The ports are the only source for the run dialog, the test cases, and the validation checks. The same panel holds:
| Field | What it does |
|---|---|
| Description | What the script does. Agents read it to decide whether to call the script. |
| Show in node menu | Adds the script to My Nodes so it drops onto a graph like any node. Category sets the group, and defaults to My Nodes. |
| Timeout (seconds, max 120) | How long a run may take. The default is 30. |
Secrets
Under Secrets, list the names of the secrets the body may read, such as OPENAI_API_KEY. The body reads one with nodetool.secrets.get.
The list is a ceiling, not a grant. A run gets only the names that appear both in this list and in what the calling context allows, so declaring a name never widens access. The validator warns when a declared secret is missing on the current install.
Run a script
Select Run in the console. The Run script dialog asks for one value per declared input, using the control that matches the port type. The list, dict, and any types take JSON text. A value that does not parse is passed as a string. A script with no inputs says “This script declares no inputs. Run it as it is.”
If the body reads inputs with stream, each field instead takes a JSON array of items to stage, for example [1, 2, 3].
The console then shows Last run with its status and duration, plus Logs, Streamed values from emit, and Outputs. Failures show the error in a banner.
Runs always use the saved script. If the document has unsaved changes, the console says “Runs use the saved script”, and the run and test tools flush the live document first.
Tests
A test case has a name, the inputs to run with, and the expected result. It can compare the final outputs per port (expect) and the ordered sequence of emit calls (expectedStreamed). A case for a body that uses stream stages items with inputStreams.
The editor has no form for writing cases. Ask the assistant to add them, or edit them through the CLI or the agent tools described below. The Test button reads “No tests” until a case exists, then “Test (N)”. It runs the cases one at a time and reports “N passed, M failed”. Each failing case lists the output name with its expected and actual values.
Assistant
The Assistant tab (“JS Script Assistant”) edits the document directly. Its changes autosave, with no Apply step. Ask it to write or change the script, add an error output, or write a test for the empty case.
Its tools:
| Tool | What it does |
|---|---|
ui_jsscript_get_state |
Reads the name, document, validation issues, and the last run and test results |
ui_jsscript_set_code |
Replaces the body |
ui_jsscript_set_ports |
Replaces input and output ports |
ui_jsscript_set_meta |
Sets name, description, secrets, and timeout |
ui_jsscript_set_tests |
Replaces the saved test cases |
ui_jsscript_run |
Runs the saved script with given inputs |
ui_jsscript_test |
Runs and grades the saved cases. Fails when there are none. |
Outside the editor, agents manage scripts with list_js_scripts, get_js_script, save_js_script, validate_js_script, run_js_script, test_js_script, edit_js_script, and delete_js_script, plus version tools for listing, reading, creating, restoring, and deleting versions.
Use a script in a workflow
Two paths exist.
- Node menu. Turn on Show in node menu in the Script tab. The script appears under the category you set in the node menu.
- Code node. The Code node has a Script property that links it to a script version. Linking copies that version’s code, secrets, and timeout into the node and pins the version. Editing the script later does not change the workflow. Use Update to latest to re-copy and re-pin.
Command line
nodetool jsscript works on a script JSON file or a saved script id. Full options are in CLI reference.
| Command | What it does |
|---|---|
nodetool jsscript validate <script_id_or_file> |
Checks syntax, imports, undefined names, undeclared inputs.* reads, unreachable outputs, port names, and tests. --warnings-as-errors fails on warnings. |
nodetool jsscript run <script_id_or_file> |
Runs the body once. Pass --inputs '{"a":1}' or, for a body that uses stream, --input-streams '{"nums":[1,2,3]}'. |
nodetool jsscript test <script_id_or_file> |
Runs and grades the saved cases. Exits non-zero if any case fails. |
nodetool jsscript debug <script_id_or_file> |
Replays scripted ui_jsscript_* steps against the headless bridge and validates the result. |
nodetool jsscript versions |
Lists, creates, and restores versions of a script. |
nodetool jsscript validate script.json
nodetool jsscript run script.json --inputs '{"numbers":[1,2,3]}'
nodetool jsscript test script.json