Build a NodeTool workflow graph in the sandbox from generated node wrappers, one importable module per node namespace
Scanned 9/1/2026
Install to Claude Code
npx -y skills add nodetool-ai/nodetool --skill sandbox-dsl --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Sandbox Dsl?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/nodetool-ai-sandbox-dsl)More formats (shields.io, HTML) on the badges page.
---
name: sandbox-dsl
description: Build a NodeTool workflow graph in the sandbox from generated node wrappers, one importable module per node namespace
---
# The workflow DSL in the sandbox
Specifier: `@nodetool-ai/sandbox-dsl`. The root exports `workflow()` and every
namespace under a short name. Each namespace is also its own module:
`@nodetool-ai/sandbox-dsl/nodetool.image`, `@nodetool-ai/sandbox-dsl/lib.audio`,
and so on for all 71.
Every node type is a generated function whose name and inputs come from the
node's own metadata. A type this pack does not export does not exist, and the
import fails before the program runs — which is the difference from building a
graph out of type strings.
## Build a graph
```js
import { workflow } from "@nodetool-ai/sandbox-dsl";
import { stringInput } from "@nodetool-ai/sandbox-dsl/nodetool.input";
import { resize } from "@nodetool-ai/sandbox-dsl/nodetool.image";
import { output } from "@nodetool-ai/sandbox-dsl/nodetool.output";
const prompt = stringInput({ name: "prompt", value: "a fox in snow" });
const smaller = resize({ width: 256, height: 256 });
return workflow(output({ name: "image", value: smaller.output() }));
```
`workflow()` returns `{ nodes, edges }` in the kernel shape — nodes carry
`{id, type, properties}`, edges carry `{id, source, sourceHandle, target,
targetHandle}`. Hand that straight to `validate_workflow` or `create_workflow`.
## Check it, save it, run it
The graph is data until something checks it. Validate first — it costs nothing
and catches a missing property, a dangling edge, or a model nobody selected
before a run spends money on the half of the graph that does work:
```js
import { validate_workflow, create_workflow, run_workflow, debug_workflow }
from "@nodetool-ai/sandbox-nodetool/workflows";
const graph = workflow(output({ name: "image", value: smaller.output() }));
const check = await validate_workflow({ graph });
if (!check.ok) throw new Error(check.issues.map((i) => i.message).join("; "));
```
`validate_workflow` answers `{ok, counts, issues}` — `ok` is false only when
the graph has errors, and each issue carries `{severity, code, message}`.
Warnings do not set `ok` false; read them off `issues`.
```js
const saved = await create_workflow({ name: "Thumbnailer", graph });
const run = await run_workflow({
workflow_id: saved.id,
params: { prompt: "a fox in snow" }
});
```
`run_workflow` answers `{status, outputs}`. When a run fails and the graph looks
right, `debug_workflow({workflow_id, params})` runs it again and answers one
report: `{workflow_id, run, job, workflow}`. `run` carries
`{status, outputs, error, verdict}` — `outputs` is keyed by output name and
each name holds an array of emitted values (`run.outputs.image[0]`). `job`
carries status, cost and logs; `verdict.headline` and `verdict.issues` say
which node failed and why.
Every model property must be selected before you save: assign a `find_model`
result's `ref` to the node's `model`. A graph saved with unselected models is
refused by `create_workflow`, because nothing stamps models in at run time.
Where the session mounts no capability modules, the same three verbs are
`nodetool.workflows.validate/create/run/debug`. Both forms reach one
implementation past one permission gate.
## Wiring
A node function returns a reference. `ref.output()` is the default output slot;
`ref.output("mask")` names one. Pass a handle as a property value and the edge
is wired for you:
```js
const wired = resize({ image: source.output(), width: 512 });
```
A `list[...]` input takes an array of handles — one edge per element, and the
sources run in parallel:
```js
const strip = combineImageGrid({
tiles: [a.output(), b.output(), c.output()],
columns: 3
});
```
Every element must be a handle; mixing wired outputs and literal values in one
array throws. A handle buried inside an object value throws too — a connection
is only made from a handle assigned directly to an input.
A node with several outputs has no default, so `output()` without a slot throws
and names the slots it has. A slot the node does not have throws the same way.
## Ids
Ids are assigned from the node type: `resize`, `resize_2`, `string_input`. They
are stable for a given program, so a later edit can name one — but the generated
wrappers take inputs and nothing else, so a program cannot choose an id.
## Everything in one import
```js
import * as dsl from "@nodetool-ai/sandbox-dsl";
const smaller = dsl.image.resize({ width: 256, height: 256 });
return dsl.workflow(dsl.output.output({ name: "image", value: smaller.output() }));
```
The `nodetool.*` namespaces drop the prefix (`dsl.image`, `dsl.input`,
`dsl.text`); the rest keep it in camel case (`dsl.libAudio`, `dsl.openaiImage`).
Importing one namespace module is cheaper than the root, which pulls all 71.
## Gotchas
- **`workflow()` lives only at the root.** A program that imports namespace
subpaths still declares `@nodetool-ai/sandbox-dsl` for the builder.
- **A handle is not text.** `` `use ${node.output()}` `` throws rather than
writing `[object Object]` into a property and wiring no edge.
- **Only reachable nodes ship.** `workflow(terminal)` walks back from its
terminals; a node nothing wires to is dropped. Pass every terminal you want.
- **One graph per call.** `workflow()` clears the registry, so a handle from an
earlier call is spent and using it throws.
- **This builds a graph; it does not run one.** The pack is pure computation —
no models are called, no assets resolve, no node executes. Run the graph
through the workflow tools once it validates.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!