ifURI examples
← all examples

32 — YAML task scenarios + a live event stream from the node

Three things in one example:

  1. Describe computer tasks as YAML scenarios and run them against a urirun node over

the mesh — login to a site, drive the desktop, write an office document, audit a box.

  1. Receive the node's logs/errors live, as URIs — the runner subscribes to the

node's SSE event stream (GET /events) and prints each run/error event the node emits *while* the steps dispatch, so you see both sides in real time.

  1. Generate scenarios from natural languagenl_scenario.py turns an NL goal into

a YAML scenario of URIs (LLM, constrained to the node's live action space), runs it, captures both directions of the flow, and writes a Markdown report.

NL → YAML scenario → run → Markdown (both directions)

./nl_scenario.sh "sprawdź zdrowie maszyny, wypisz 5 procesów i zapisz notatkę 'audyt OK'"
NODE_URL=http://192.168.188.201:8765 ./nl_scenario.sh "otwórz https://example.com i zrób zrzut ekranu"

It (1) asks the LLM for [{uri, payload, why}] using only the node's /routes, (2) saves the YAML to scenarios/gen-<slug>.yaml, (3) dispatches each step (host→node) while a background SSE subscriber collects the node's events (node→host), (4) reads the node's own log back, and (5) writes generated/<slug>.md — goal, the generated YAML, a Host → Node dispatch table, a Node → Host live-events table, and the node-side log. See the committed generated/*.md for real runs against lenovo. The model + key come from examples/.env; with no key it falls back to a deterministic generator.

 HOST                                           NODE
 ────                                           ────
 run_scenarios.py  ──POST /run {uri,payload}──►  executes a step
        │   (one per YAML step)                       │ emits an event
        ▼                                             ▼
   ◄─────────────── GET /events (SSE) ───────────  run://…  /  error://local/E-…
   prints  "░ node-event: run  him://lab/… ok"   (logs + errors as URIs, immediately)

Run

./run_scenarios.sh                                   # every scenarios/*.yaml
./run_scenarios.sh scenarios/web-login.yaml          # one scenario
NODE_URL=http://192.168.188.201:8765 ./run_scenarios.sh scenarios/system-audit.yaml

A live run against a mock office node (4 scenarios) interleaves dispatch + node events:

  [1] him://lab/keyboard/command/type-text
      -> ok: {"typed": "jan@firma.pl", ...}
      ░ node-event: run   him://lab/keyboard/command/type-text ok
  [5] browser://lab/page/command/screenshot
      -> FAIL: "HTTP 400"
      ░ node-event: error error://local/E-1ebc9205/query/info  INVALID_ARGUMENT
== 4 scenario(s): 15/18 steps ok; 21 live node-events received ==

system-audit.yaml works on any node (even the default 7-route one), so it runs against the live remote node too; the others need the office surface (him/kvm/ browser/urioffice) — see example 31.

Scenario format

name: web-login
description: ...
steps:
  - uri: browser://{host}/page/command/open    # {host}/{monitor}/{session} are resolved
    payload: {url: "https://example.com/login"}
    why: open the login page
  - uri: him://{host}/keyboard/command/type-text
    payload: {text: "$ref:0.title"}            # thread an earlier step's output

percent-encoded %7B…%7D that /routes emits).

The live event channel (GET /events, SSE)

Any urirun node now exposes a one-way node→host Server-Sent Events stream. Each dispatched route publishes a run event, and every failure an error event whose uri is the error://local/E-… address — so the controller receives logs and errors as URIs the instant they happen, without polling /errors.

urirun host watch lab                  # or: urirun host watch http://NODE_IP:8765
curl -N http://NODE_IP:8765/events     # raw SSE
run    him://lab/keyboard/command/type-text  ok
run    browser://lab/page/command/screenshot  FAIL  INVALID_ARGUMENT
error  error://local/E-1ebc9205/query/info  INVALID_ARGUMENT

Fan out events to MQTT (many subscribers / a UI)

For more than one consumer — a dashboard, several operators, automations — republish the node's events to an MQTT broker. urirun host watch does it inline:

urirun host watch lab --mqtt-broker localhost:1883            # node events -> MQTT
urirun host watch lab --mqtt-broker localhost:1883 --scheme error   # only errors

# any number of subscribers then consume, wildcarding by node or kind:
mosquitto_sub -t 'urirun/events/lab/#'        # everything from node 'lab'
mosquitto_sub -t 'urirun/events/+/error/#'    # every node's errors

Topic layout: urirun/events/<node>/<event>/<uri-scheme> (e.g. urirun/events/lab/run/him, urirun/events/lab/error/error). Needs paho-mqtt on the watcher (pip install paho-mqtt) and a reachable broker. The same publish point can feed the urirun-connector-mqtt device bridge or any MQTT-native dashboard.

See NOTES-bidirectional.md for why SSE (not WebSocket) and whether a fuller two-way channel needs a package refactor.

Files

gen-*.yaml (NL-generated).

Test

python3 -m pytest test_scenarios.py -q     # or: python3 test_scenarios.py

Files

.gitignoreNOTES-bidirectional.mdREADME.mdaggregate_report.pyhost_node_matrix.pynl_scenario.pynl_scenario.shreversibility_check.pyrun_scenarios.pyrun_scenarios.shrun_scenarios.sh.tmptest_host_node_matrix.pytest_reversibility.pytest_scenarios.py.benchmarks/scenarios/

View on GitHub →