Skip to content
Back to skills

Sandbox Flow

ASecurity

Call NodeTool nodes as typed async functions from sandbox code, one importable module per node namespace, with streaming and plain JavaScript control flow

  • 552 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 1, 2026
ai-agentsjavascriptgojavanode

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned September 1, 2026

npx -y skills add nodetool-ai/nodetool --skill sandbox-flow --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Sandbox Flow?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Sandbox Flow
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/nodetool-ai-sandbox-flow/badge)](https://www.skillsdirectory.com/skills/nodetool-ai-sandbox-flow)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: sandbox-flow
description: Call NodeTool nodes as typed async functions from sandbox code, one importable module per node namespace, with streaming and plain JavaScript control flow
---

# Calling nodes from the sandbox

Specifier: `@nodetool-ai/sandbox-flow`. Each node namespace is its own module —
`@nodetool-ai/sandbox-flow/nodetool.text`,
`@nodetool-ai/sandbox-flow/lib.audio`, and so on for all 69. Every node type is
a generated async function whose name and inputs come from the node's own
metadata, so a type this pack does not export has no import to resolve.

This is not the graph DSL. Nothing is built, saved, or scheduled: the call runs
the node and resolves to its outputs. `await` is the edge, a variable is the
wire, `Promise.all` is the fan-out, and `if`/`for`/`try` are themselves.

```js
import { concat } from "@nodetool-ai/sandbox-flow/nodetool.text";

const r = await concat({ a: "hello ", b: "world" });
return r.output;
```

## Two imports, both required

1. `import { … } from "@nodetool-ai/sandbox-flow/<namespace>";` — the nodes.
2. `import "@nodetool-ai/sandbox-nodetool/flow";` — the capability module.

The second one is not decoration. The host mounts a capability module by
reading the **body's** static imports, and this pack's guest code calls into
`@nodetool-ai/sandbox-nodetool/flow`. Without the body-side import the facade
is never mounted and the pack's own import of it is refused by name.

```js
import "@nodetool-ai/sandbox-nodetool/flow";
import { concat } from "@nodetool-ai/sandbox-flow/nodetool.text";
```

## Streaming

A node that streams its output carries a `.stream` member; a one-shot node does
not, so the surface tells you which is which. Awaiting the plain call on a
streaming node still works — it drains the node and returns the last value per
slot.

```js
import "@nodetool-ai/sandbox-nodetool/flow";
import { agent } from "@nodetool-ai/sandbox-flow/nodetool.agents";

let text = "";
for await (const chunk of agent.stream({ objective: "Summarize the file" })) {
  text += chunk.chunk ?? "";
  if (text.length > 2000) break; // closes the stream; the node stops
}
return text;
```

Breaking early releases the stream on the host. Read it to the end, or break —
both are fine; abandoning the loop object without either is what leaks.

A node written against the streaming-input contract streams `{slot, value}`
instead of partial outputs, one item per emission, and its inputs accept an
array wherever a single value goes:

```js
import { take } from "@nodetool-ai/sandbox-flow/nodetool.control";

const kept = [];
for await (const { slot, value } of take.stream({ input_item: [1, 2, 3], n: 2 })) {
  if (slot === "output") kept.push(value);
}
```

Arrays are the whole of streaming input in this version. An async iterable on
an input handle is not accepted.

## Errors

A node that fails rejects the call with its own error. There is no verdict
machinery and no per-node error stream — your `try`/`catch` is the supervisor,
and retry, fallback, and timeout are yours to write:

```js
import "@nodetool-ai/sandbox-nodetool/flow";
import { textToSpeech } from "@nodetool-ai/sandbox-flow/gemini.audio";

try {
  return await textToSpeech({ text: draft });
} catch (error) {
  return { error: String(error) };
}
```

## Fan-out

Concurrency is `Promise.all`, and it is real: the calls run at once.

```js
const summaries = await Promise.all(
  documents.map((document) => summarizer({ text: document }))
);
```

Nothing throttles this. A hundred items is a hundred concurrent model calls —
batch them yourself when that matters.

## The untyped root

`import { callNode } from "@nodetool-ai/sandbox-flow"` takes a node type as a
string and checks nothing until the host answers. Use it when the type is
decided at run time; otherwise import the namespace, where a wrong name is an
import error rather than a failed call.

## Gotchas

- **Inputs are values, not handles.** There is nothing to wire and no
  `.output()` — the call returns the outputs record.
- **No graph comes out of this.** Nothing opens in the editor, validates, or
  replays. When the artifact matters, build a graph with
  `@nodetool-ai/sandbox-dsl` instead.
- **A missing property is the node's default**, exactly as in a graph run.
- **Names follow the node class**: `nodetool.text.Concat` is `concat`,
  `nodetool.constant.Integer` is `integer`. Reserved words take a trailing
  underscore (`if_`).

Files in this skill

  • SKILL.md4.4 KB
  • package.json9.2 KB
  • sandbox/generated/gemini.audio.js330 B
  • sandbox/generated/gemini.image.js239 B
  • sandbox/generated/gemini.text.js331 B
  • sandbox/generated/gemini.video.js333 B
  • sandbox/generated/kie.dynamic_schema.js215 B
  • sandbox/generated/lib.apple.js1.9 KB
  • sandbox/generated/lib.audio.js1.6 KB
  • sandbox/generated/lib.browser.js223 B
  • sandbox/generated/lib.charts.js231 B
  • sandbox/generated/lib.comfy.js457 B
  • sandbox/generated/lib.grid.js232 B
  • sandbox/generated/lib.image.channel.js310 B
  • sandbox/generated/lib.image.color.js818 B
  • sandbox/generated/lib.image.color_grading.js1.2 KB
  • sandbox/generated/lib.image.draw.js1009 B
  • sandbox/generated/lib.image.effects.js607 B
  • sandbox/generated/lib.image.enhance.js763 B
  • sandbox/generated/lib.image.filter.js1.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…