The script node runs a small piece of code you write as a step in your pipeline. It reads the media item and the outputs of any nodes upstream of it, and produces whatever outputs you declare — a text, a number, a list of texts, a block of JSON, a set of timeframes, or images.
It exists for the gap between "one of the built-in nodes already does this" and "we need a new node built and released". Turning a transcript into chapter marks, thresholding a quality metric into a tag, reshaping a language model’s answer into structured fields — each is a few lines of code, and each would otherwise be a development project.
Kind |
|
Applies to |
Any asset |
Inputs |
The media item, plus the outputs of any upstream nodes you connect |
Output keys |
Whatever you declare — see Declaring outputs |
Languages |
JavaScript |
Requirements |
None. The script runs inside the Cortex worker; no external service is involved |
Persists to |
|
Writing a script
The script runs once per media item. Six values are available to it:
| Name | What it gives you |
|---|---|
|
The current item: |
|
Outputs of upstream nodes: |
|
The constants you set in the node’s Parameters field, so one script can be reused with different settings |
|
How you produce results — |
|
|
|
|
A complete example — turning a transcript into chapter marks:
const re = new RegExp(params.pattern, 'i');
const transcript = JSON.parse(upstream['whisper']['whisper_result']);
const frames = transcript.segments
.filter(s => re.test(s.text))
.map(s => ({ startMs: Math.round(s.start * 1000),
endMs: Math.round(s.end * 1000),
label: s.text.trim() }));
if (frames.length === 0) {
ctx.skip('no chapter markers in transcript');
}
out.timeframes('chapter_frames', frames);
out.list('chapter_titles', frames.map(f => f.label));
out.integer('chapter_count', frames.length);
Declaring outputs
Outputs are declared in the node’s Outputs field, not discovered from the script. This is what lets the pipeline editor draw the node’s output connectors so you can wire the next node to them before the script has ever run.
[
{ "key": "chapter_frames", "type": "TIMEFRAMES" },
{ "key": "chapter_titles", "type": "TEXT_LIST" },
{ "key": "chapter_count", "type": "INTEGER" }
]
| Type | The script provides | Stored as |
|---|---|---|
|
a string |
a field in the node’s JSON result |
|
a number |
a field in the node’s JSON result |
|
|
a field in the node’s JSON result |
|
an object |
a field in the node’s JSON result |
|
an array of strings |
a field in the node’s JSON result |
|
an array of |
a real timeline on the asset |
|
image bytes, base64, or a path |
an image file in the worker’s cache |
|
a file path |
a field in the node’s JSON result |
A single item can produce several multi-valued outputs at once — that is how one transcript becomes a whole timeline of chapter marks plus a list of titles plus a count, in one pass.
Writing to a key you have not declared stops the node with an error naming the key. That is deliberate: a typo that was silently dropped would leave your pipeline claiming a connection carries data that never arrives.
Timeframes are stored as a real timeline against the asset, so they can be queried and displayed
rather than sitting in an opaque blob. Each timeframe output is filed under a segment type —
CHAPTER (the default), SCENE, SILENCE or SHOT. Set it with "segmentType": "SCENE" alongside
the key and type. If one node declares two timeframe outputs, give them different segment types.
Images are written to the worker that ran the script, and the output carries their file paths. Because those paths are local to that worker, keep the nodes that consume them in the same affinity group.
Configuration
| Option | Meaning |
|---|---|
|
The script language. |
|
The script body, run once per media item |
|
The declared outputs — see Declaring outputs |
|
Constants handed to the script as |
|
|
|
Whether the script runs with the worker’s full privileges (default) — see Safety and performance |
|
Extra capabilities, off by default |
|
How long one item may take before the script is stopped (default 10 s) |
|
A second guard against a script that loops forever |
|
Cap on the size of a single item’s results (default 1 MB) |
|
Cap on |
Required Inputs is what makes a script safe to hang off an optional branch: if an OCR node only
runs for images, a script downstream of it skips videos quietly instead of reporting a failure for
every one.
Safety and performance
Anyone who can edit a pipeline can run code on a worker. A script node is not a sandboxed plugin by default — treat permission to edit pipelines as equivalent to permission to run code, and grant it accordingly.
Three controls are available:
-
Trusted off. Turning off Trusted denies the script access to the host system: no Java classes, no files, no network, no threads. Use it for scripts that only need to reshape data.
-
Limits. The timeout and statement limit always apply, in both modes. A script that loops forever is stopped and the item recorded as failed; it cannot occupy a worker indefinitely.
-
Turning the node off entirely. Adding
scriptto a worker’s node blacklist means that worker will refuse script work. If no worker accepts it, pipelines containing a script node are rejected when you try to run them, with a clear message — see Cortex Configuration.
On performance: scripts are interpreted, which is ample for shaping data and wrong for heavy per-pixel or per-frame computation. If you find yourself writing a tight loop over image data, that is the signal to build a proper node instead.
Use cases
-
Derive a timeline — turn a transcript, a caption set or a detection list into chapter marks.
-
Classify on a threshold — convert a quality metric or a sentiment score into a simple label.
-
Reshape model output — pull structured fields out of a language model’s free-text answer.
-
Bridge two nodes — rename, filter or combine upstream outputs so the next node gets what it expects.
-
Prototype — try out a processing idea in a live pipeline before committing to building a node for it.