Commands
cross.stream commands use Nushell expressions to define reusable
operations that can be called on-demand with arguments. Unlike handlers which
maintain state between invocations, or generators which run continuously,
commands are stateless and execute independently each time they are called.
Defining Commands
To create a command, append a definition string with the topic
<command-name>.define:
r#'{ # Required: Command closure run: {|frame| # frame.topic - always <command>.call # frame.hash - contains input content if present # frame.meta.args - contains call arguments let input = if ($frame.hash != null) { .cas $frame.hash } else { null } let n = $frame.meta.args.n 1..($n) | each {$"($in): ($input)"} }
# Optional: Module definitions modules: { "my-util": "export def format [x] { $\"formatted: ($x)\" }" }
# Optional: Control output frame behavior return_options: { suffix: ".output" # Output topic suffix (default: ".response") ttl: "last:1" # Keep only most recent frame }}'# | .append repeat.defineThe return_options field controls the suffix and TTL for the .response frame
produced by the command. TTL only applies to this .response frame—.error
events never expire.
The command definition requires:
run: A closure that receives the call frame and can return a pipeline of results
Upon successful definition, the command emits a <command>.ready frame indicating it’s ready to accept calls.
All values produced by the closure’s output pipeline are collected into a single
.response event automatically.
Calling Commands
Commands are called by appending to <command-name>.call with input content and
arguments:
# Call the repeat command with input and args"foo" | .append repeat.call --meta {args: {n: 3}}Lifecycle Events
Commands emit events to track their execution. See for all component suffixes.
| Event | Description |
|---|---|
<command>.ready | Command successfully defined and ready for calls |
<command>.response | Collected result of the command pipeline |
<command>.error | Error occurred during command execution |
All events include:
command_id: ID of the command definitionframe_id: ID of this specific invocation
Error Handling
If a command encounters an error during execution, it will:
- Emit a
<command>.errorframe with:- The error message
- Reference to both command_id and frame_id
- Stop processing the current invocation
Unlike generators, commands do not automatically restart on error - each invocation is independent.
Modules
Commands can use custom Nushell modules:
r#'{ run: {|frame| my-math double ($frame.meta.args.number) }
modules: { "my-math": "export def double [x] { $x * 2 }" }}'# | .append calculator.defineThis allows you to modularize your commands and reuse code across different commands.
Built-in Store Commands
When the run closure executes it can use several helper commands provided by
cross.stream:
.append– append a new frame. Metadata you provide is merged withcommand_idandframe_id..cat– read frames from the store..last– fetch the most recent frame for a topic..cas– read content from CAS by hash..get– retrieve a frame by ID..remove– delete a frame from the stream.
Key Differences
| Feature | Commands | Handlers | Generators |
|---|---|---|---|
| State | Stateless | Stateful between calls | Stateless |
| Execution | On-demand | Event-driven | Continuous |
| Results | Streamed immediately | Batched on completion | Streamed |
| Parallelism | Multiple parallel calls | Sequential processing | Single instance |
| Error Handling | Per-invocation | Unregisters handler | Auto-restarts |
| Modules | Supported | Supported | Not supported |