This repository provides two lightweight execution helpers for ComfyUI graphs:
- Shell Code – run arbitrary
/bin/bashscripts with STRING input and capture the stdout as both text and (optionally) a LIST of lines. - Python Code – run inline Python snippets that operate on a provided string and expose structured results, captured stdout, and error information.
custom_nodes/
comfy_code_nodes/
__init__.py # exports both node classes
shell_code_node.py # Shell Code node implementation
python_code_node.py # Python Code node implementation
requirements.txt # empty placeholder (no deps)
Clone this repository directly into ComfyUI/custom_nodes or install it via
ComfyUI Manager once published. During installation ComfyUI automatically
executes pip install -r requirements.txt for each custom node, so declaring
dependencies in that file is all that is required for normal setups.
If you run ComfyUI inside a Docker container you can manually trigger the same dependency install step with:
docker exec -it <comfy_container_name> bash -lc \
"cd /opt/ComfyUI/custom_nodes/code-nodes && pip install -r requirements.txt"Replace /opt/ComfyUI with the root of your ComfyUI checkout if it differs.
Running that command ensures the requirements resolve exactly the way ComfyUI
Manager would during a fresh install.
| Input | Type | Notes |
|---|---|---|
script |
STRING | Multiline bash script, default cat. |
stdin_text |
STRING | Text piped to stdin. |
split_lines |
BOOLEAN | Optional (default True). |
strip_empty |
BOOLEAN | Optional (default True). |
Outputs (stdout, stdout_lines, stderr, ok) where stdout_lines is marked as a
LIST output to allow wiring into other nodes.
| Input | Type | Notes |
|---|---|---|
script |
STRING | Python code executed with input_text in scope. |
input_text |
STRING | Text value available to the script. |
load_from_file |
BOOLEAN | Optional (default False). When enabled the script is loaded from disk and the inline editor becomes read-only. |
script_filename |
STRING | Optional (hidden unless load_from_file=True). Relative path (inside this extension directory) to the script that should be executed. |
split_lines |
BOOLEAN | Optional (default True). |
strip_empty |
BOOLEAN | Optional (default True). |
delimiter |
STRING | Optional custom delimiter (default ", "). Clear the field to rely solely on newline parsing. |
output_inner_delimiter |
STRING | Controls how nested lists are joined when auto-generating result_lines/result_lines_list (default ", "). |
Outputs (result, result_lines, result_lines_list, stdout, stderr, ok) where:
resultcomes from theresult(orresult_text) variable inside the script.result_linesis returned as a newline-delimited string, assembled from theresult_lineslist you manage inside the script. If you leave the list empty, the node auto-populates it either by splittingresult(whensplit_linesis enabled) or by formatting each element of a list/tuple result so that nested lists become comma-delimited strings (e.g.,['a', 'b'] → "a, b"). Treatresult_linesinside Python as a list; the node converts it to text when emitting.result_lines_listmirrorsresult_linesbut stays a Python list so you can wire the structured data elsewhere. Nested lists are formatted into strings usingoutput_delimiterbefore inclusion.stdoutcaptures anything printed by the script.stderrcontains the formatted traceback if an exception occurs.okisTruewhen the script executes without raising.
Inside the Python script you always get:
input1…input20(up to the configured slot count): each is either alist[str](whensplit_lines=True) or a raw string when line splitting is disabled. Every input also exposes_textand_linesvariants (e.g.,input3_text,input3_lines) so you can work with whichever format you prefer regardless of the checkbox state.inputs: ordered collection of all active inputs. Newinput*widgets appear automatically as you type, up to 20 total.inputs[n]mirrorsinput{n+1}and becomes alist[str]whenever either thedelimiterfield is populated orsplit_lines=True. Otherwise it remains a string.inputs_text/inputs_linesprovide string-or-list versions regardless of the toggle (newline splitting drives the_linesvariants).input_text/lines: convenient aliases forinput1_textandinput1_lines.script_path: absolute filesystem path of the file used whenload_from_fileis enabled, otherwise an empty string.delimiter: the currently configured delimiter string (blank when unused).output_inner_delimiter: the delimiter used when rendering nested list entries forresult_linesandresult_lines_list.result/result_lines: start empty; assign to them when you want to emit values. Inside your script, treatresult_linesas a list of strings. If you only populateresult, the node will automatically either convert each item of a list/tuple result into its own comma-delimited line or split the string output into lines (respecting thesplit_lines/strip_emptysettings). Before returning, the node converts the list to a newline-delimited string for theresult_linesoutput socket.
Additional input* widgets appear automatically as you fill in the last visible
field, up to twenty total, so you never have to manage an explicit “Input Count”.
Toggle Load code from file when you want to keep the main script in a real editor on disk. When the toggle is enabled:
- The script textbox becomes read-only so it mirrors whatever is on disk.
- The
script_filenamewidget (always visible) lets you type a path relative to thecode-nodesextension directory. For safety, the built-in saver only writes inside that directory. - The helper fetches the file over ComfyUI's
/extensions/...static server so the code preview inside the node keeps up with the file contents. Use the dedicated Reload File button (or the node's right-click menu → Reload Script Preview) after editing the file to pull in the latest version. - Need to push changes back to disk? Click Save File next to the reload button. If the target file already exists you'll get a unified diff in a confirmation dialog before the node overwrites it.
- During execution the node reads the same file directly from the filesystem so you can keep iterating in your own editor without copy/paste loops.
Both nodes are intentionally minimal wrappers over standard interpreters and do not provide sandboxing. Only run them on systems you control and never expose them to untrusted input.