Run workflows headlessly
The browser app is for designing and testing. Two zero-dependency sibling packages re-run the same saved graph from code โ same noodle-graph.json, same NanoGPT API, your own key.
Design a workflow in the editor, hit ๐พ to download
noodle-graph.json, then load and re-run it from
Node.js or
Python.
Both packages are named nanoodle, ship a library and a CLI, have zero
runtime dependencies (Node โฅ 20 with built-in fetch; Python โฅ 3.9, stdlib
only), and implement the same contract โ same graphs, same semantics.
Every run() spends real money from your
NanoGPT balance โ NanoGPT
bills per generation. Loading, validating and inspecting workflows never calls the API,
and bad inputs (unknown node types, missing required inputs, missing API key) fail
before anything is spent. No telemetry, no analytics; the key is never logged.
JavaScript (Node โฅ 20)
npm install nanoodle # library + CLI
npx nanoodle --help # or run the CLI without installing
Library
import { Workflow } from "nanoodle";
const wf = await Workflow.load("noodle-graph.json"); // key from NANOGPT_API_KEY
const result = await wf.run({ Text: "a cozy ramen shop on a rainy night" });
await result.get("Image").save("ramen.png"); // media: MediaRef (url + bytes()/save())
console.log(result.costUsd, result.remainingBalance);
With the app's starter graph (text โ LLM prompt-writer โ image), that's the whole program.
CLI
Inspect first โ offline; shows inputs, outputs, and settings:
npx nanoodle inspect graph.json
Then run (calls NanoGPT and spends from your balance):
export NANOGPT_API_KEY=... # or --key K, or --env-file .env
npx nanoodle run graph.json --input Text="a cozy ramen shop" --out ./out
npx nanoodle run graph.json --input n2.system=@style.txt --set n3.size=1k --json
--env-file pathโ loadNANOGPT_API_KEYfrom a.env-style file (--keywins if both are set)--input k=@pathโ read a file (media as media;.txt/.md/.jsonas text)--out dirโ save media outputs to disk--jsonโ machine-readable result
Python (โฅ 3.9, stdlib only)
pip install nanoodle
export NANOGPT_API_KEY=... # nano-gpt.com API key (or OAuth access token)
Library
from nanoodle import Workflow
wf = Workflow.load("noodle-graph.json")
result = wf.run({"Text": "a cozy ramen shop on a rainy night"})
result["Image"].save("ramen.png") # media: MediaRef (url + bytes()/save())
print(result.cost_usd, result.remaining_balance)
CLI
Installed as nanoodle-py (and python -m nanoodle always
works):
nanoodle-py inspect graph.json
nanoodle-py run graph.json --input Text="a cozy ramen shop" --set n3.size=1k --out ./out
nanoodle-py run graph.json --input n2.system=@style.txt --json
nanoodle-py run graph.json --env-file .env --input Text="hello"
Inputs, outputs, settings
Both packages expose the workflow's interface before you run it โ
wf.inputs, wf.outputs, wf.settings. Input keys are
flexible (case-insensitive): the node's custom name ("Text"),
nodeId.field ("n2.system"), or the input's label. A workflow
with exactly one required input also accepts a bare value:
wf.run("hello"). Settings use nodeId.field keys
("n3.model").
Media inputs take a local file (mediaFromFile /
media_from_file), a hosted URL, or raw bytes (MIME sniffed). Media is sent
inline as base64 (NanoGPT has no upload endpoint); files over ~4.4 MB (~3.5 MB
for transcription) are refused locally with a clear error before any paid call.
What runs where
Pure-logic nodes (text, uploads, choice, join, comment) run locally; generation nodes
(llm, image, edit, inpaint, vision, video, lipsync, music, remix, tts, transcribe)
call NanoGPT. The 6 media-processing nodes (resize, vframes, combine, soundtrack, trim,
extractaudio) also run headlessly. They do their work on your machine and never spend.
Some of them need ffmpeg and ffprobe on PATH:
| node | JavaScript | Python |
|---|---|---|
resize | Pure JS for a PNG source; ffmpeg for any other format | ffmpeg |
trim | Pure JS for a PCM WAV source; ffmpeg for any other format | ffmpeg |
combine | Pure JS (lossless mp4 remux) when every clip is mp4 with matching stream parameters; ffmpeg otherwise | ffmpeg |
vframes | ffmpeg | ffmpeg |
soundtrack | ffmpeg | ffmpeg |
extractaudio | ffmpeg | ffmpeg |
ffmpeg is a soft dependency, not a package dependency. Install it with
apt install ffmpeg or brew install ffmpeg. If a node needs it
and it is absent, that node stops the run with a clear error that names it. The error
arrives when the node's turn comes, so paid nodes earlier in the graph have already
spent โ run a media-only graph once to prove your setup before you wire a paid node in
front of it.
Only an unknown node type is refused up front: the libraries warn at load and
throw at run() before any network call. That is how a graph saved before
2026-07-22 with the retired draw node behaves. The full node table is on the
format page.
No account: pay per run in Nano (x402)
Skip the API key entirely. With --pay (or a library
payment callback), each paid call returns an HTTP 402 Nano (XNO)
invoice โ scan/pay from a self-custody wallet; nanoodle waits for the deposit
and continues. The library never holds funds or keys.
# JavaScript
npx nanoodle run graph.json --input Text="hello" --pay
# Python
python -m nanoodle run graph.json --input Text="hello" --pay
Worked examples (QR / print invoice, wallet stub, bare chat) live in both packages: nanoodle-js/examples/x402 ยท nanoodle-py/examples/x402.
Use a workflow as an agent skill
A saved workflow plus a short SKILL.md playbook is a skill any coding
agent can run โ anything that reads markdown and runs shell. Recipe, template and a
working example (idea โ LLM prompt โ poster image) ship in both repos:
docs/agent-skills.md.