OpenProse
Reactor DevTools

Reference

The reactor-devtools bin, the read-only HTTP API, the frame shape the SPA consumes, and the importable library surface.

Reference

The reactor-devtools bin

reactor-devtools <state-dir> [--port <n>] [--host <h>] [--describe] [--json]
reactor-devtools --example <name> [--describe] [--json]            # replay a bundled fixture
reactor-devtools --example <name> --copy-to <dir> [--force]        # seed a sample ledger
Argument / optionDefaultEffect
<state-dir>(required unless --example)A saved Reactor state directory (receipts + compile/topology.json).
--example <name>--Replay a fixture shipped in the package by name -- no path needed, works after a global install from any cwd. Six fixtures ship: masked-relay, surprise-cost, agent-observatory, inbox-triage, monorepo-ci, research-tree. The keyless proof from the README is --example masked-relay; the "cost scales with surprise" thesis is --example surprise-cost.
--copy-to <dir>--Copy the bundled --example fixture into <dir> so you have a real state-dir on disk to inspect or replay. Refuses a non-empty / existing dir unless --force.
--force--Overwrite a non-empty / existing state-dir on --copy-to.
-p, --port <n>4555Port to listen on. Must be an integer.
--host <h>127.0.0.1Host to bind.
--describe--Print the headless run summary (per-node + per-frame dispositions, moved-facet diff, cost rollup, chain-verify) and exit. No server, no browser.
--json--With --describe, emit that summary as machine-readable JSON (the CI/agent surface). Only meaningful with --describe; refused otherwise.
-V, --version--Print the package version and exit.
-h, --help--Print usage.

Exit codes: 0 on --help, --version, a successful --describe, or a successful --copy-to; 1 when <state-dir> is missing (and no --example), when --port is not an integer, when --json is passed without --describe, when --copy-to hits a non-empty dir without --force, or on any startup error. The server runs until SIGINT / SIGTERM, then closes and exits 0.

If compile/topology.json is absent, the viewer falls back to a node-only set derived from the receipts' distinct node values (boxes, no edges).

HTTP API

The server serves the SPA plus a tiny read-only API:

RouteReturns
GET /api/stateThe full ReplaySnapshot (topology + frames + cost rollup). GET /api/snapshot is a kept alias. This is the only endpoint the SPA fetches -- the whole view is a pure function of this snapshot.
GET /api/node/:id?version=<v>A node's world-model at a version, via readVersion (version is a frame's atomicVersion). 400 if missing, 404 if no such node/version. This is a server/library endpoint for tools and integrators -- the shipped SPA does not call it (there is no node-click UI in v1); fetch it yourself, or use the readNodeWorldModel / versionForFrame library helpers.
GET /eventsAn SSE seam reserved for future live-attach -- idle (held open) in replay.

The frame shape

GET /api/state returns a ReplaySnapshot carrying frames: ReceiptFrame[] in append order (the scrubber index = frame.index). Each frame is a pure projection of one receipt:

interface ReceiptFrame {
  index: number;                 // scrubber position (append order)
  node: string;                  // which node to flash / dim / red
  status: "rendered" | "skipped" | "failed";
  wakeSource: "input" | "self" | "external";   // flash hue
  movedFacets: string[];         // facets that moved vs this node's prior receipt
  edgesToLight: { producer: string; subscriber: string; facet: string }[];
                                 // per-facet lanes to light -- only on rendered+moved
                                 // (skipped/failed light none); strict facet match
  wokenSubscribers: string[];    // DISTINCT downstreams woken -- diamond single-wake
  cost: { fresh: number; reused: number; surpriseCause: "input" | "self" | "external" };
  contentHash: string;           // this receipt's address (inspector chain key)
  atomicVersion: string;         // = fingerprints["@atomic"]; pass to /api/node?version=
}

edgesToLight and wokenSubscribers are derived server-side in buildSnapshot from the saved topology and the receipt's moved facets, reusing the SDK's own propagationTargets. So the diamond single-wake (a subscriber reached by >=2 moved facets of one producer fires exactly once) matches the live reconciler.

Library

The package is importable directly, so a benchmark front-end or a docs site can embed the renderer without pulling the CLI.

import { openStateDir, buildSnapshot, startDevToolsServer } from "@openprose/reactor-devtools";

// 1. Open a saved dir and build the SPA payload (a pure read of the SDK).
const opened = openStateDir("/path/to/state-dir");
const snapshot = buildSnapshot(opened);

// 2. Or just serve it.
const server = await startDevToolsServer({ stateDir: "/path/to/state-dir", port: 4555 });
console.log(server.url);     // -> http://127.0.0.1:4555/
// server.snapshot           // the ReplaySnapshot it is serving
await server.close();        // stop the server

Exported surface:

ExportWhat it is
openStateDir(dir, opts?)Open a state-dir → OpenedStateDir (ledger session + topology + world-models + labels + beats).
buildSnapshot(opened)Build the ReplaySnapshot the SPA consumes (topology + frames + cost rollup).
startDevToolsServer(opts)Boot the node:http server → DevToolsServer (url, snapshot, close()).
readTopology, openWorldModels, readNodeWorldModel, versionForFrame, verifyReceiptChainLower-level read helpers over the state-dir.
TypesOpenStateDirOptions, OpenedStateDir, ReplaySnapshot, ReceiptFrame, EdgeLight, NodeView, EdgeView, CostRollupView, NodeWorldModelView, WorldModelFileView, DevToolsServer, DevToolsServerOptions.

The ledger-shaping primitive itself -- createReplaySession -- lives in the SDK, re-exported from the curated . front door (@openprose/reactor), not here, so other tools can shape a trail without this package. See Front door and State dirs and replay.

Stack and dependencies

Version0.2.0
Binreactor-devtoolsdist/cli.js
Runtime dependency@openprose/reactor only
ServerNode built-in node:http (no web framework)
Front-endVanilla, no-build SVG SPA (no bundler, no graph library)
Recording (dev)Playwright (a dev dependency; repo tooling -- see Recording)

The package boundary is the point: the SDK stays zero-dep and headless; every opinionated UI choice is quarantined here, and even here it stays near-zero.

On this page