NAME
zeughaus-nodes - node types, pins and settings of every zeughaus plugin
DESCRIPTION
A node is an instance of a node type, and every node type comes from a plugin. A type is named by its type id, <plugin>.<name> (transform.sin, db.table), and the catalog entry behind it carries a display name, a category, pins, settings and whether the type is a container. The editor places nodes from this catalog; the runner instantiates the same types and executes them. The editor never executes a node.
The catalog below lists the plugins in the order the runner registers them (Runner::new); the editor registers the same plugins in the same order. Plugins marked native only (capture, db, record, llm) are left out of the browser editor build; the runner always has them. job is registered in the editor detached: a job node is placed and configured there and runs only in a runner.
Each entry shows the pins of a freshly placed node. Where pins follow the wiring or a setting, the notes say so.
Pins
An input pin is either a trigger or a sample pin. A trigger pin is an event: the node acts on what arrives there. A sample pin is state: it is read whenever the node runs, for whatever reason. The editor draws a trigger pin as a square and a sample pin as a circle, and a wire into a trigger pin as a moving dash, into a sample pin as a solid line. An output pin carries one value; an output whose last run produced no value is drawn dim.
Dirty propagation is uniform: when a node emits, everything downstream of it reruns. A node that must act only on its own event asks whether its trigger pin received a value since it last ran, which is how db.insert writes one row per event and not one per pass.
A pin is coloured by its type, from the theme's ANSI slots:
| type | colour |
|---|---|
Float | green |
Str | yellow |
Bool | blue |
Int | cyan |
Any | bright white |
| everything else | white |
A field pin belongs to neither side. db.table declares one per field; a wire between two field pins is a relation between two tables, carries no value and is kept out of execution, so two tables that reference each other are not a cycle.
Settings
Settings are the fields in a node's body and the one way to configure a node from the editor. The kind of a setting only chooses the widget: Text is one line, Multiline a taller field, Title the node's name drawn as its heading, and Fields a row editor over name:type lines with a type choice per row.
The value is always text. The editor stores it in the node's parameters and sends it 400 ms after the last keystroke; the runner hands it to the node, and the node parses it. A node that cannot use a value refuses it with a reason: the runner reports the refusal, every editor draws the reason under the field, and the node keeps its last accepted value.
Some parameters are derived by the editor and never typed: db_path from the enclosing db.database, columns of a db.insert from the wired table, relations from the wires between field pins, and renamed_from after a table's title changed. The runner applies them like any other parameter.
Derived pins
A node may reshape its pins. The variadic nodes (flow.all, the ml merges, record.writer) grow an input when the last one is wired, so there is always one free. db.table has one field pin per row of its columns setting, and db.insert one input per column of the table wired into it. Editor and runner both recompute the pins after every connection change and after a setting that shapes them.
Containers
A container (graph.sub, db.database) holds other nodes and has no pins of its own. The editor gives it one input per graph.input child and one output per graph.output child, named by that child's name setting and typed Any. A wire drawn onto a container's pin is stored against the boundary node behind it, so the store and the runner see one flat graph of real nodes; only the editor knows about nesting. A container's open button shows its contents in a tab of their own.
Sources and clocks
A node runs when something upstream of it changes. A source has nothing upstream, so it needs a clock: a node may declare an interval at which the host runs it on its own. flow.timer and record.player declare one. capture.screen does not: without a flow.timer wired to its trigger it produces one frame and stops.
Presses
flow.button and job.run also act on a press, delivered by the runner as the fire parameter. In the editor, a Button node's body is its Trigger button, and a press works from any window. Outside the editor, zeughaus-runner trigger presses any node by id, optionally with a payload; see zeughaus-runner(1).
Async work and errors
A node whose work would block a pass hands it off as async work: llm.chat (the HTTP request), job.run (the wait for the process) and capture.screen (the first Wayland capture). The node is pending until the result arrives, and its downstream is held back until then. A node that fails does not fail the pass: its message is drawn on the node, its downstream is held back, and everything unrelated runs.
PIN TYPES
A pin type is a runtime value, not a compile-time label, so a node can derive types from data (a table's fields). The variants:
Any- The wildcard. Connects to anything and is never coerced: the type is whatever flows through (
flow.hold, the graph boundaries,transform.display). Bool,Int,Float,Str- The scalars, one to one with the Rust types
bool,i64,f64andString. Narrower numeric types are not pin types; a node converts where it emits. List<T>,Option<T>- Composites of another type, built at runtime. No node in the current catalog declares one.
Record- A named type with ordered fields, built at runtime. No node in the current catalog declares one.
- Opaque types
- A plugin's own Rust type, matched by name:
image(a frame; shared bycaptureandrecord),KerasModel(ml),Conversation(llm),db.table(db).
An output may connect to an input when the two types are equal, when either side is Any, or when a converter joins them. The one built-in converter is Int -> Float; no plugin registers another, and there is none from Float to Int. Editor and runner build the same converter registry from the same plugins, so what may connect and what is coerced agree. The editor refuses a wire that nothing converts and says why, for example nothing converts db.table into int. Two field pins connect only when their types are equal.
TRANSFORM
Crate zeughaus-transform. Constants, arithmetic, trigonometry, comparison, logic and string operations over scalar pins, and a display sink.
transform.const_f64Const (f64) ConstEmits the number typed into its
valuesetting.out value Float
settings value Text, default
0
Text that does not parse as a number is refused ("is not a number"); the node keeps emitting its last accepted value.
transform.const_boolConst (bool) ConstEmits the boolean typed into its
valuesetting.out value Bool
settings value Text, default
false
Accepts
true,1,false,0; an empty field reads as false. Anything else is refused ("is not true or false").transform.const_stringConst (String) ConstEmits the text of its
valuesetting exactly as typed, untrimmed.out value Str
settings value Text
transform.addAdd MathEmits
a + b.in a Float (trigger)b Float (trigger)out result Float
An unwired input reads as 0.
transform.subtractSubtract MathEmits
a - b.in a Float (trigger)b Float (trigger)out result Float
An unwired input reads as 0.
transform.multiplyMultiply MathEmits
a * b.in a Float (trigger)b Float (trigger)out result Float
An unwired input reads as 0.
transform.divideDivide MathEmits
a / b.in a Float (trigger)b Float (trigger)out result Float
adefaults to 0 andbto 1. Division by zero emits infinity.transform.negateNegate MathEmits
-input.in input Float (trigger)out result Float
An unwired input reads as 0.
transform.absAbs MathEmits the absolute value of
input.in input Float (trigger)out result Float
transform.clampClamp MathEmits
inputlimited to the range [min,max].in input Float (trigger)min Floatmax Floatout result Float
An unwired bound is open:
mindefaults to negative infinity,maxto infinity.transform.moduloModulo MathEmits the remainder
a % b, with the sign ofa.in a Float (trigger)b Float (trigger)out result Float
bdefaults to 1. A zero divisor emits NaN.transform.powerPower MathEmits
baseraised toexp.in base Float (trigger)exp Float (trigger)out result Float
basedefaults to 0,expto 1.transform.minMin MathEmits the smaller of
aandb.in a Float (trigger)b Float (trigger)out result Float
transform.maxMax MathEmits the larger of
aandb.in a Float (trigger)b Float (trigger)out result Float
transform.lerpLerp MathLinear interpolation: emits
a + (b - a) * t.in a Float (trigger)b Float (trigger)t Floatout result Float
Unwired inputs default to
a= 0,b= 1,t= 0.5.transform.sqrtSqrt MathEmits the square root of
input; a negative input gives NaN.in input Float (trigger)out result Float
transform.floorFloor MathRounds
inputdown to the nearest integer value.in input Float (trigger)out result Float
transform.ceilCeil MathRounds
inputup to the nearest integer value.in input Float (trigger)out result Float
transform.roundRound MathRounds
inputto the nearest integer value, halves away from zero.in input Float (trigger)out result Float
transform.log2Log2 MathEmits the base-2 logarithm of
input.in input Float (trigger)out result Float
transform.lnLn MathEmits the natural logarithm of
input.in input Float (trigger)out result Float
transform.sinSin TrigEmits the sine of
input, in radians.in input Float (trigger)out result Float
transform.cosCos TrigEmits the cosine of
input, in radians.in input Float (trigger)out result Float
transform.tanTan TrigEmits the tangent of
input, in radians.in input Float (trigger)out result Float
transform.greater_thanGreater Than LogicEmits whether
a > b.in a Float (trigger)b Float (trigger)out result Bool
An unwired input reads as 0.
transform.equalEqual LogicEmits whether
aandbdiffer by less thanepsilon.in a Float (trigger)b Float (trigger)epsilon Floatout result Bool
epsilondefaults to 1e-10.transform.selectSelect LogicEmits
true_valwhenconditionis true, otherwisefalse_val.in condition Bool (trigger)true_val Floatfalse_val Floatout result Float
An unwired
conditionreads as false; unwired values read as 0.transform.notNot LogicEmits the negation of
input.in input Bool (trigger)out result Bool
transform.andAnd LogicEmits
a && b.in a Bool (trigger)b Bool (trigger)out result Bool
An unwired input reads as false.
transform.orOr LogicEmits
a || b.in a Bool (trigger)b Bool (trigger)out result Bool
An unwired input reads as false.
transform.map_rangeMap Range UtilityRemaps
valuefrom [in_min,in_max] onto [out_min,out_max].in value Float (trigger)in_min Floatin_max Floatout_min Floatout_max Floatout result Float
The ranges default to 0..1 on both sides. The result is not clamped. An empty input range emits
out_min.transform.to_stringTo String StringFormats a value of any type as the text the editor shows for it.
in input Any (trigger)out result Str
A plugin type with no text form formats as its type name in angle brackets, e.g.
<KerasModel>. An unwired input emits the empty string.transform.concatConcat StringJoins
aandb, withsepbetween them when it is not empty.in a Str (trigger)b Str (trigger)sep Strout result Str
transform.string_lenString Length StringEmits the length of
inputin bytes, as a Float.in input Str (trigger)out result Float
transform.displayDisplay OutputShows the last value it received in its body.
in input Any (trigger)
Has no outputs. The one node whose body shows data: an image arriving here is drawn from the runner's sample feed.
ML
Crate zeughaus-ml. Keras layers as nodes: a KerasModel value is extended one step per node, branches merge, and the result is exported as functional-API Python.
ml.inputInput MLStarts a model branch with
layers.Input(...).in model KerasModelout out KerasModel
settings shape Text, default
(28, 28, 1)
shapeis emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.exportrequires every branch of a model to start with this node.ml.denseDense MLAppends a
layers.Dense(...)step to the incoming model.in model KerasModelout out KerasModel
settings units Text, default
64
activation Text, defaultrelu
activationis emitted as a quoted Python string;unitsis emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.conv2dConv2D MLAppends a
layers.Conv2D(...)step to the incoming model.in model KerasModelout out KerasModel
settings filters Text, default
32
kernel_size Text, default(3, 3)
strides Textpadding Textactivation Text, defaultrelu
padding,activationare emitted as a quoted Python string;filters,kernel_size,stridesare emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.conv1dConv1D MLAppends a
layers.Conv1D(...)step to the incoming model.in model KerasModelout out KerasModel
settings filters Text, default
32
kernel_size Text, default3
activation Text, defaultrelu
activationis emitted as a quoted Python string;filters,kernel_sizeare emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.maxpool2dMaxPooling2D MLAppends a
layers.MaxPooling2D(...)step to the incoming model.in model KerasModelout out KerasModel
settings pool_size Text, default
(2, 2)
pool_sizeis emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.avgpool2dAveragePooling2D MLAppends a
layers.AveragePooling2D(...)step to the incoming model.in model KerasModelout out KerasModel
settings pool_size Text, default
(2, 2)
pool_sizeis emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.global_avgpool2dGlobalAveragePooling2D MLAppends a
layers.GlobalAveragePooling2D(...)step to the incoming model.in model KerasModelout out KerasModel
No parameters.
ml.flattenFlatten MLAppends a
layers.Flatten(...)step to the incoming model.in model KerasModelout out KerasModel
No parameters.
ml.dropoutDropout MLAppends a
layers.Dropout(...)step to the incoming model.in model KerasModelout out KerasModel
settings rate Text, default
0.5
rateis emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.batchnormBatchNormalization MLAppends a
layers.BatchNormalization(...)step to the incoming model.in model KerasModelout out KerasModel
No parameters.
ml.layernormLayerNormalization MLAppends a
layers.LayerNormalization(...)step to the incoming model.in model KerasModelout out KerasModel
No parameters.
ml.activationActivation MLAppends a
layers.Activation(...)step to the incoming model.in model KerasModelout out KerasModel
settings activation Text, default
relu
activationis emitted as a quoted Python string. A blank setting is left out of the call, so Keras applies its own default.ml.lstmLSTM MLAppends a
layers.LSTM(...)step to the incoming model.in model KerasModelout out KerasModel
settings units Text, default
64
return_sequences Textunits,return_sequencesare emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.gruGRU MLAppends a
layers.GRU(...)step to the incoming model.in model KerasModelout out KerasModel
settings units Text, default
64
return_sequences Textunits,return_sequencesare emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.embeddingEmbedding MLAppends a
layers.Embedding(...)step to the incoming model.in model KerasModelout out KerasModel
settings input_dim Text, default
10000
output_dim Text, default128
input_dim,output_dimare emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.reshapeReshape MLAppends a
layers.Reshape(...)step to the incoming model.in model KerasModelout out KerasModel
settings target_shape Text, default
(28, 28, 1)
target_shapeis emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.concatenateConcatenate ML MergeJoins the incoming branches into one model with
layers.Concatenate()([...]).in a KerasModelb KerasModelout out KerasModel
settings axis Text
Variadic: inputs
a,b, ... grow as they are wired, always one spare after the highest wired input, at least 2 and at most 26 (atoz). Fails with "Concatenate needs at least two connected model inputs" when fewer than two non-empty models arrive.axisis emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.addAdd ML MergeJoins the incoming branches into one model with
layers.Add()([...]).in a KerasModelb KerasModelout out KerasModel
Variadic: inputs
a,b, ... grow as they are wired, always one spare after the highest wired input, at least 2 and at most 26 (atoz). Fails with "Add needs at least two connected model inputs" when fewer than two non-empty models arrive.ml.subtractSubtract ML MergeJoins the incoming branches into one model with
layers.Subtract()([...]).in a KerasModelb KerasModelout out KerasModel
Variadic: inputs
a,b, ... grow as they are wired, always one spare after the highest wired input, at least 2 and at most 26 (atoz). Fails with "Subtract needs at least two connected model inputs" when fewer than two non-empty models arrive.ml.multiplyMultiply ML MergeJoins the incoming branches into one model with
layers.Multiply()([...]).in a KerasModelb KerasModelout out KerasModel
Variadic: inputs
a,b, ... grow as they are wired, always one spare after the highest wired input, at least 2 and at most 26 (atoz). Fails with "Multiply needs at least two connected model inputs" when fewer than two non-empty models arrive.ml.averageAverage ML MergeJoins the incoming branches into one model with
layers.Average()([...]).in a KerasModelb KerasModelout out KerasModel
Variadic: inputs
a,b, ... grow as they are wired, always one spare after the highest wired input, at least 2 and at most 26 (atoz). Fails with "Average needs at least two connected model inputs" when fewer than two non-empty models arrive.ml.maximumMaximum ML MergeJoins the incoming branches into one model with
layers.Maximum()([...]).in a KerasModelb KerasModelout out KerasModel
Variadic: inputs
a,b, ... grow as they are wired, always one spare after the highest wired input, at least 2 and at most 26 (atoz). Fails with "Maximum needs at least two connected model inputs" when fewer than two non-empty models arrive.ml.minimumMinimum ML MergeJoins the incoming branches into one model with
layers.Minimum()([...]).in a KerasModelb KerasModelout out KerasModel
Variadic: inputs
a,b, ... grow as they are wired, always one spare after the highest wired input, at least 2 and at most 26 (atoz). Fails with "Minimum needs at least two connected model inputs" when fewer than two non-empty models arrive.ml.dotDot ML MergeJoins the incoming branches into one model with
layers.Dot()([...]).in a KerasModelb KerasModelout out KerasModel
settings axes Text, default
-1
Variadic: inputs
a,b, ... grow as they are wired, always one spare after the highest wired input, at least 2 and at most 26 (atoz). Fails with "Dot needs at least two connected model inputs" when fewer than two non-empty models arrive.axesis emitted verbatim. A blank setting is left out of the call, so Keras applies its own default.ml.compileCompile MLAttaches a training configuration that
ml.exportrenders asmodel.compile(...).in model KerasModelout out KerasModel
settings optimizer Text, default
adam
loss Text, defaultcategorical_crossentropy
metrics Text, defaultaccuracy
metricsis a comma-separated list and becomes a Python list of strings. A blank setting is left out of the call.ml.exportExport Code MLRenders the incoming model as a runnable Keras functional-API Python program.
in model KerasModel (trigger)out code Str
The program imports keras, assigns one variable per step, builds
keras.Model(inputs, outputs), compiles when a Compile step is present and callsmodel.summary(). Fails on an empty model and when a branch does not start with an Input layer.
FLOW
Crate zeughaus-flow. Primitives between events and state: latch, manual press, clock and fan-in.
flow.holdHold FlowLatches the most recent event of any type and keeps emitting it as state.
in in Any (trigger)out out Any
The event-to-state adapter. Emits nothing before the first value arrives.
Emits one
trueevent per press.out out Bool
A press arrives as the
fireparameter: the Trigger button in the node body, from any editor, orzeughaus-runner trigger. Between pressesoutcarries no value, so downstream trigger pins stay quiet.flow.timerTimer FlowThe clock: emits
tick = trueevery 1/hzseconds.out tick Bool
settings hz Text, default
30
Declares its interval through
tick_interval; the host schedules it. A rate that is not a finite number is refused and the previous rate stays; a finite rate is clamped to 0.01..240.flow.allAll FlowFan-in: fires
outonce every wired input has fired since it last fired.in a Any (trigger)b Any (trigger)out out Bool
Variadic: inputs
a,b, ... grow as they are wired, one spare after the highest wired input, up to 26. Unwired inputs are not waited for, and unwiring an input forgets what it fired. With nothing wired it never fires.
GRAPH
Crate zeughaus-graph. Subgraphs: a container node and the boundary nodes that give it pins.
graph.subSubgraph Graph containerA container that holds other nodes.
Declares no pins: the editor synthesizes one input per
graph.inputchild and one output pergraph.outputchild, named by that child'snamesetting, all typedAny. A wire on such a pin is stored against the boundary node, so the executor sees one flat graph. Theopenbutton shows the contents in a tab of their own.graph.inputInput GraphA subgraph's input boundary: passes what arrives on
intoout.in in Any (trigger)out out Any
settings name Text, default
in
namenames the container pin it contributes. It is trimmed; an empty name keeps the previous one. Emits nothing while the outside is unwired.graph.outputOutput GraphA subgraph's output boundary: passes what arrives on
intoout.in in Any (trigger)out out Any
settings name Text, default
out
namenames the container pin it contributes. It is trimmed; an empty name keeps the previous one.
CAPTURE
Crate zeughaus-capture, native only: the runner executes it, the browser editor cannot place it. Screen capture as an image source.
capture.screenScreen Capture CaptureCaptures the primary screen once per execution.
in trigger Any (trigger)out frame imagewidth Floatheight Floatframe_size Floatcaptured Boolerror Str
A source: wire a
flow.timertotriggerfor a live feed. On success it emitsframe,width,height,frame_sizeandcaptured = true; on failure onlycaptured = falseanderror. On Wayland it uses the xdg-desktop-portal ScreenCast stream (the first capture is async work and may show a consent dialog, later ones sample the running stream) and falls back to the Screenshot portal; on X11, Windows and macOS it usesscrap.
DB
Crate zeughaus-db, native only: the runner executes it, the browser editor cannot place it. SQLite databases drawn as subgraphs: tables are schemas, wires between fields are foreign keys.
db.databaseDatabase Database containerA container whose
pathsetting names the SQLite file every node inside it works on.settings path Text, default
zeughaus.sqlite
Executes nothing. The editor passes
pathto each child as the deriveddb_pathparameter; adb.*node outside a database fails with "not inside a database". Its pins come fromgraph.input/graph.outputchildren, as forgraph.sub. Connections are shared per file, with foreign keys enforced and WAL journaling.db.tableTable DatabaseA table schema: creates the table and keeps the file in step with the declared fields.
out table db.tableddl Strfields id Intvalue Float
settings name Title, default
table
columns Fields, defaultid:int value:float
columnsis onename:typerow per field, typesint,float,str,bool; each row is a field pin of that type (the fields listed are the defaults). A wire between two field pins of equal type is a relation and becomes a FOREIGN KEY; the end whose field isidis referenced.id:intbecomes INTEGER PRIMARY KEY. On each run it renames the table after a title change, renames a renamed field, adds new fields and drops removed ones; a changed column type is refused. A malformed row, a duplicate field or an empty name is refused.ddlis the CREATE TABLE statement.db.insertInsert DatabaseWrites one row into the wired table each time
insertfires.in table db.tableinsert Any (trigger)out id Intcount Int
Derived pins: one Sample input per column of the wired table, typed like the column, except
id, which SQLite assigns. The editor derives the column list from the wireddb.table.idis the last inserted rowid,countthe rows this node has written. A pass without aninsertevent writes nothing.db.queryQuery DatabaseSelects rows from the wired table when
runfires.in table db.tablerun Any (trigger)out rows Strcount Int
settings where Textorder Text, default
id DESC
limit Text, default100
whereandorderare raw SQL fragments.limitmust be a positive row count, is capped at 10000, and anything else is refused.rowsis a JSON array of objects, one per row; the last result stays on the pins between runs.db.sqlSQL DatabaseRuns the SQL statement in its
sqlsetting whenrunfires.in run Any (trigger)out rows Strcount Int
settings sql Multiline
A statement starting with SELECT, WITH, PRAGMA or EXPLAIN returns its rows as JSON on
rows; any other statement reports the number of changed rows oncount. Fails on an empty statement.
RECORD
Crate zeughaus-record, native only: the runner executes it, the browser editor cannot place it. Recorder and player: frames and values as PNG files plus a JSON-lines index on disk.
record.writerRecorder RecordWrites every frame it is triggered with, and the values wired beside it, to a recording directory.
in frame image (trigger)v0 Anyout count Intsession Strpath Str
settings dir Text, default
recordings
session TextWrites
<dir>/<session>/<seq:06>.pngplus one line per frame inindex.jsonl(seq,ts,file,width,height,values). An emptysessionis named by the first frame, in unix seconds. Derived pins: value inputsv0,v1, ... always one more than are wired. Changingdirorsessionstarts a new recording. A pass without a new frame writes nothing.record.playerPlayer RecordReplays a recording, one frame per tick, in write order.
out frame imageseq Intts Floatvalues Str
settings dir Text, default
recordings
session Texthz Text, default10
loop Text, defaulttrue
A clocked source: runs every 1/
hzseconds on its own.hzis clamped to 0.1..120; text that is not a number keeps the previous rate.loopacceptstrue,1,false,0and refuses anything else; without it playback ends after the last frame and the pins go empty.valuesis the JSON object the recorder wrote. Fails whensessionis empty or its index cannot be read.
LLM
Crate zeughaus-llm, native only: the runner executes it, the browser editor cannot place it. LLM conversations against LM Studio's OpenAI-compatible endpoint: each node extends a Conversation value by one message.
llm.systemSystem Message LLMAppends a system message to the incoming conversation, or starts one.
in conv Conversationout out Conversation
settings text Multiline, default
You are a helpful assistant.
llm.userUser Message LLMAppends a user message to the incoming conversation, or starts one.
in conv Conversationtext Strout out Conversation
settings text Multiline
A wired
textinput wins over thetextsetting.llm.chatChat (LM Studio) LLMSends the conversation to an OpenAI-compatible endpoint and appends the reply.
in conv Conversation (trigger)out out Conversationreply Strtok_per_s Float
settings base_url Text, default
http://localhost:1234/v1
model TextAsync work: the request runs off the pass and its outputs arrive when the reply does. An empty
modeluses the first model the endpoint lists; an emptybase_urluses the default. Fails on an empty conversation.llm.last_replyLast Reply LLMEmits the content of the conversation's last message as text.
in conv Conversationout text Str
llm.mergeMerge LLMConcatenates conversation
aand thenbinto one.in a Conversationb Conversationout out Conversation
JOB
Crate zeughaus-job. Jobs: processes with a beginning and an end, run in runner-owned terminals with a log per run. Registered detached in the editor.
job.runJob JobRuns a process in a terminal the runner owns, on a
runevent or a press.in run Any (trigger)cwd Strout ok Boolfailed Intdir Str
settings command Textenv Textcwd Textartifacts Textkeep_on_failure Text, default
true
command,envandartifactsare split like shell words (quotes group, nothing expands, no shell runs); an unbalanced quote, anenvword without=or akeep_on_failureother than true or false is refused. A wiredcwdwins over the setting. A press (zeughaus-runner trigger, delivered as thefireparameter) starts a run like an event onrun; its payload text, or a string arriving onrun, reaches the program asZEUGHAUS_PAYLOAD. The program also seesZEUGHAUS_RUN_DIR. The wait is async work:okfires on exit 0,failedcarries the exit code (-1 for a signal or kill),diris the run directory. One run at a time; refuses while a run is live, while the runner is held, and in a process that does not execute. The editor registers this plugin detached: the node is drawn and configured there and runs only in a runner.
SEE ALSO
zeughaus-architecture(7), zeughaus-runner(1), zeughaus(1), zeughaus-keys(7), zeughaus-glossary(7)