Skip to content

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:

Terminal window
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.define

The 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:

Terminal window
# 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.

EventDescription
<command>.readyCommand successfully defined and ready for calls
<command>.responseCollected result of the command pipeline
<command>.errorError occurred during command execution

All events include:

  • command_id: ID of the command definition
  • frame_id: ID of this specific invocation

Error Handling

If a command encounters an error during execution, it will:

  1. Emit a <command>.error frame with:
    • The error message
    • Reference to both command_id and frame_id
  2. 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:

Terminal window
r#'{
run: {|frame|
my-math double ($frame.meta.args.number)
}
modules: {
"my-math": "export def double [x] { $x * 2 }"
}
}'# | .append calculator.define

This 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 with command_id and frame_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

FeatureCommandsHandlersGenerators
StateStatelessStateful between callsStateless
ExecutionOn-demandEvent-drivenContinuous
ResultsStreamed immediatelyBatched on completionStreamed
ParallelismMultiple parallel callsSequential processingSingle instance
Error HandlingPer-invocationUnregisters handlerAuto-restarts
ModulesSupportedSupportedNot supported