Watching Changes
Subscribe to workspace changes with durable cursors and update app views outside the model loop.
Use watch() when an app needs to react to workspace file changes outside the
model loop, such as refreshing a file tree, syncing generated outputs, or
showing a live artifact panel.
watch() uses the Runtime Engine durable event log, so configure a runtime
first:
import { config } from "@use-crux/core";
import { node } from "@use-crux/core/runtime";
config({ runtime: node() });
const handle = ws.watch("/outputs", {
recursive: true,
pollIntervalMs: 250,
});
const off = handle.on((event) => {
if (event.type === "rename") {
console.log(event.from, "->", event.path);
return;
}
console.log(event.type, event.path);
});
off();
handle.stop();Events have type, workspaceId, namespace, path, at, and cursor.
Rename events also include from:
type WorkspaceChangeEvent =
| {
type: "create" | "update" | "delete";
workspaceId: string;
namespace: string;
path: string;
at: number;
cursor: string;
}
| {
type: "rename";
workspaceId: string;
namespace: string;
from: string;
path: string;
at: number;
cursor: string;
};Scope
Omit the path to watch the whole workspace tree, or pass a specific path for
exact-path matching. Set recursive: true to include descendants. Rename events
match either the source path or destination path.
ws.watch(); // whole workspace
ws.watch("/outputs"); // exact path unless recursive is true
ws.watch("/outputs", { recursive: true }); // outputs treeResume With Cursors
Store handle.cursor after processing events and pass it back as { cursor }
to resume after the last delivered change:
const handle = ws.watch("/outputs", {
recursive: true,
cursor: savedCursor,
});
handle.on(async (event) => {
await updateFileTree(event);
await saveCursor(handle.cursor);
});Runtime Requirement
Without a configured runtime, direct workspace reads and writes still work, but
watch() throws RUNTIME_REQUIRED because delivery depends on durable event
cursors.
Pass onError to observe retryable namespace or event-read failures. The watch
stays alive and continues polling with backoff:
const handle = ws.watch("/outputs", {
recursive: true,
onError: (error) => {
console.warn("workspace watch retrying", error);
},
});Transaction staging writes are internal. Watchers see only committed live-namespace changes.
Devtools And Traces
Devtools include a Workspaces view that reconstructs a filesystem-like tree from workspace operations:
research
|- /workspace
| `- notes.md
`- /outputs
`- report.pdfOTel receives privacy-safe operation metadata only. Raw file contents and full
paths are not emitted to OTel. Workspace OTel spans use
crux.workspace.operation and crux.workspace.path_hash; devtools use the same
path hash as a stable file label when no local-only raw path is available.
Finalized artifacts expose bounded metadata such as status, artifact kind, MIME
type, size, and download ref.
The file inspector also has a Versions tab showing a file's revision timeline:
version number, operation, timestamp, and trace link. Each version is announced
by a single privacy-safe marker emitted when the version is recorded, so the
timeline counts one entry per content change even though an edit or undo
internally performs a nested write.
Project Index data-access facts preserve exact workspace operations, including
grep, watch, history, diff, undo, artifacts, rename, move,
copy, and finalize.