Skip to content

Actions

cross.stream actions use Nushell expressions to define reusable operations that can be called on-demand with arguments. Unlike actors which maintain state between invocations, or services which run continuously, actions are stateless and execute independently each time they are called.

Defining Actions

To create an action, append a definition string with the topic <action-name>.define:

Terminal window
r#'{
# Required: Action closure
run: {|frame|
# frame.topic - always <action>.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: 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 action. TTL only applies to this .response frame---.error events never expire.

The action definition requires:

  • run: A closure that receives the call frame and can return a pipeline of results

Upon successful definition, the action emits a <action>.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 Actions

Actions are called by appending to <action-name>.call with input content and arguments:

Terminal window
# Call the repeat action with input and args
"foo" | .append repeat.call --meta {args: {n: 3}}

Lifecycle Events

Actions emit events to track their execution. See for all processor suffixes.

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

All events include:

  • action_id: ID of the action definition
  • frame_id: ID of this specific invocation

Error Handling

If an action encounters an error during execution, it will:

  1. Emit a <action>.error frame with:
    • The error message
    • Reference to both action_id and frame_id
  2. Stop processing the current invocation

Unlike services, actions do not automatically restart on error - each invocation is independent.

Modules

Actions can use modules registered via *.nu topics. An action sees the modules as they existed when it was defined. See for details.

Terminal window
r#'{
run: {|frame|
use xs/my-math
my-math double ($frame.meta.args.number)
}
}'# | .append calculator.define

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 action_id and frame_id.
  • .cat — read frames from the store.
  • .last — fetch the most recent frame(s), optionally filtered by topic.
  • .cas — read content from CAS by hash.
  • .get — retrieve a frame by ID.
  • .remove — delete a frame from the stream.

Key Differences

FeatureActionsActorsServices
StateStatelessStateful between callsStateless
ExecutionOn-demandEvent-drivenContinuous
ResultsCollected into responseBatched on completionStreamed
ParallelismMultiple parallel callsSequential processingSingle instance
Error HandlingPer-invocationUnregisters actorAuto-restarts
ModulesSupportedSupportedSupported