npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bytespost-canvas

v0.4.5

Published

Local CLI + MCP server for the bytespost canvas — drive a running canvas tab from the terminal or any AI agent.

Readme

bytespost-canvas

Local CLI + MCP server for the bytespost canvas — drive a running canvas tab from your terminal or any AI agent (Claude Code, Cursor, etc.).

What it is

bytespost-canvas runs a tiny localhost daemon (HTTP + WebSocket hub) that bridges your terminal / AI agent and a canvas tab open in your browser. The first daemon-dependent canvas command starts it in the background automatically. Once the canvas tab is connected:

  • The CLI sends commands (draw a rectangle, capture a screenshot, query the document, set selection, …).
  • The daemon relays them over WebSocket to the canvas page.
  • The page executes them in the live editor and streams back results (node IDs, PNG bytes, JSON document state).

It also exposes an MCP (Model Context Protocol) stdio server so AI agents that speak MCP can talk to your canvas with no glue code.

Install

# Global install — gives you the `canvas` binary
npm install -g bytespost-canvas

# Or run without installing
npx bytespost-canvas --help

Three binaries ship, all aliasing the same CLI: canvas, canvas-bridge, canvasd.

Quick start

# 1. Run a command. If needed, Canvas starts the daemon in the background.
canvas status --json

# 2. Open app.bytespost.com. The dashboard connects as a control-only tab, so
#    project create/open works before an editor exists. A concrete Design,
#    Slides, or 3D editor upgrades the same tab with canvas operations.

# 3. From any other terminal, run commands
canvas ls                              # list connected tabs (with node counts)
canvas watch                           # live dashboard of open canvases (r=refresh, q=quit)
canvas screenshot -o canvas.png        # capture the active tab
canvas draw rect --x 100 --y 100 --w 200 --h 120 --fill "#7c3aed"
canvas document nodes --page active    # list only the active page's nodes
canvas node move <id> --position front # bring a sibling to the top/front
canvas select set <id>                 # set selection

# Create and install Canvas Plugin v2 modules
canvas plugin create ./my-plugin --id @acme/my-plugin
canvas plugin install https://plugins.example.com/my-plugin.mjs --tab tab_abc123 --trust

# Discover and call tools registered by the current editor and its plugins
canvas tool list --json
canvas tool show building_create_wall --json
canvas tool call building_create_wall '{"startX":0,"startY":0,"endX":480,"endY":0}' --json

# Core capabilities that used to exist only inside the editor AI are now in
# the same catalog and therefore work through CLI, MCP, and WebMCP.
canvas tool call canvas_generate_grid '{"x":80,"y":80,"rows":8,"cols":12,"cell":"dot","gap":24}' --json
canvas tool call canvas_generate_isometric_stack '{"x":640,"y":180,"layers":[{"label":"UI","kind":"outline"},{"label":"Data","kind":"gradient","fill":"#7C3AED","fill2":"#38BDF8"}]}' --json
canvas tool call canvas_render_mermaid '{"text":"flowchart LR\nA[Observe]-->B[Act]-->C[Verify]","diagramId":"agent-loop"}' --json
canvas tool call canvas_create_connector '{"fromId":"node-a","toId":"node-b","routingType":"orthogonal","endArrow":"arrow"}' --json
canvas tool call canvas_apply_animation_preset '{"nodeId":"hero","preset":"slide-in-left","duration":0.7}' --json

# High-level composition: one semantic circle, not dozens of line segments
canvas draw circle --cx 320 --cy 240 --radius 90 --stroke "#111827"
canvas draw path --d "M0 0 C80 0 80 120 160 120" --stroke "#7c3aed"
canvas draw model --primitive torus --x 0 --y 1 --z 0 --size 2,2,.5
canvas draw model --file keyboard.glb --x 0 --y 0 --scale 1,1,1
cat 133-nodes.json | canvas draw - --json
canvas export glb -o scene.glb
canvas export svg -o active-page.svg

# Projects, pages, and exact micro-adjustments
canvas project create --name "Bike study" --type general-3d
canvas page create --name "Rear drivetrain"
canvas node transform <id> --by 12,0,0 --rotate 0,0,15
canvas camera preset hero
canvas lighting set studio

# Computer-use-style semantic loop
canvas observe --page active --json
canvas act '[{"type":"node.move","target":{"id":"heart"},"move":{"position":"front"}}]' --observe --json
canvas verify '[{"type":"order","front":{"id":"heart"},"back":{"id":"web"}}]' --json
canvas verify '[{"type":"layout","container":{"id":"artboard"},"allowOverlap":false,"minFontSize":12}]' --json

# Ask for the best AI prompt demo instead of guessing primitive commands
canvas helper "AI 系统架构图" --json

# Browse prompt demos, then give one complete prompt to an AI agent.
# These commands never inspect or change the canvas.
canvas demo list --json
canvas demo prompt architecture-ai-pipeline
canvas demo prompt architecture-ai-pipeline --tab tab_abc123 --target cli --json
canvas demo prompt architecture-ai-pipeline --target all --json

# Browse the extensible catalog (`morse` is a typo-tolerant alias)
canvas demo more --json
canvas demo morse --json

canvas start and canvas serve remain available when you intentionally want the daemon in the foreground for logs or debugging. Set CANVAS_BRIDGE_AUTO_SERVE=0 to disable automatic background startup.

Every daemon-backed CLI request and MCP tool checks the local daemon's package and protocol versions. An older lifecycle-capable daemon stops admitting new work, waits for outstanding operations, preserves unexpired operation receipts, and restarts from the invoking CLI installation. Commands resume only after the original tab IDs reconnect. A newer daemon is never downgraded by a stale CLI; update that CLI installation instead. Disabling auto-start still enforces version compatibility. Help, version, generation and offline headless commands do not start a daemon.

Pre-0.4.1 daemons cannot drain operations. Idle legacy daemons can be migrated after verifying the owned local process; connected legacy tabs require a one-time CANVAS_BRIDGE_LEGACY_RESTART=1 migration after preserving pending work. No document reload is required. DAEMON_BUSY and DAEMON_RECONNECT_PENDING mean the command was not dispatched; keep the editor open and resolve the reported condition before retrying. A restart is not a durable backup of documents.

AI prompt demos

canvas demo is a read-only teaching surface, not a template executor. A demo contains a user brief, agent boundaries, success criteria, recovery rules, and a complete capabilities → observe → revision-guarded act → verify example. The AI must inspect the actual document, adapt the recipe, execute the canvas commands itself, and verify the resulting revision.

# Human-readable prompt with a complete, runnable shell example
canvas demo prompt architecture-ai-pipeline --tab tab_abc123

# Machine-readable prompt packet for an AI client
canvas demo prompt architecture-ai-pipeline --target all --json

# `show` is a read-only compatibility alias
canvas demo show architecture-ai-pipeline --json

The former canvas demo run command has been removed because it bypassed the AI's observation and planning. Legacy invocations fail with DEMO_EXECUTION_REMOVED, mutatedCanvas:false, and a demo prompt replacement; they never contact the daemon, even with --dry-run.

Use canvas node move <id> --parent <frameId> --index <n> to reparent and place a layer, or --root to return it to its current page root. Structural parent/order changes are intentionally separate from node update, whose shallow patch cannot replace id, type, or children.

node update accepts type-aware friendly aliases while remaining a non-structural patch. For example, TEXT accepts fontFamily, fontStyle, lineHeight, alignment and decoration fields; PATH accepts pathData; 2D vectors accept fill, strokeColor, and strokeWidth; MODEL accepts materialColor (or fill as a compatibility alias), metalness, roughness, modelTranslation, modelRotation, and modelScale. Run canvas node update --help for complete copyable examples.

Observe / act / verify

The primary agent interface is a canvas-specific state/action loop:

  1. observe returns a content-derived document revision plus semantic nodes, stable IDs, parent/index/paint paths, effective visibility, world bounds, selection, and optional renderer PNG. Use --page active to remove other-page noise and pass --since <revision> for a page-filtered diff.
  2. act accepts a typed action array and an optional --expected-revision. All document actions are calculated against a private snapshot and committed once, so a bad selector, illegal parent, cycle, cross-page move, or stale revision leaves the live document unchanged.
  3. verify checks postconditions such as existence, parent, containment, visibility, paint order, text/container overflow, overlap policy, and minimum font size against one document revision. Viewport and selection changes do not invalidate that revision.

Actions include create/update/transform/move/delete/duplicate/group/ungroup, page lifecycle, selection, and viewport operations. Selectors can use a stable ID or a semantic query by type, exact/partial name, parent, page, selection, and effective visibility. Queries expect one node by default and reject ambiguity; opt into expect: "many" explicitly.

canvas node update and the corresponding node.update action remain non-structural property patches. Both accept the same documented, type-aware allowlist and normalize it into exact IR fields. The aggregate style, textStyle, and material objects are explicit advanced escape hatches; arbitrary raw IR keys are not accepted. Unknown keys and structural id, type, children, parentId, and pageId fields fail validation without changing the document. Use node.move, node.group, and node.ungroup for hierarchy and z-order.

The optional visual observation and screenshot never include DOM/Web overlays. For 2D they use renderer-page export; for 3D canvas screenshot captures the currently presented camera viewport.

canvas <command> --help prints command-specific flags. canvas --json returns stable machine-readable JSON for any command — pipe straight into jq.

Every mutation supports --dry-run; it returns the exact HTTP operation without contacting the daemon or changing the canvas. Commands default to a 25 second timeout and one retry with the same operation ID, so a late browser reply can be retrieved without executing the mutation twice. Use --timeout-ms, --retries, and --idempotency-key to override those bounds.

MCP mode (for Claude Code / Cursor / any MCP client)

canvas mcp     # speaks MCP over stdio

Register it in your AI agent's MCP config. Sample for Claude Code:

// ~/.claude/config.json
{
  "mcpServers": {
    "canvas": {
      "command": "canvas",
      "args": ["mcp"]
    }
  }
}

The agent sees the semantic control plane and typed helpers (canvas_observe, canvas_act, canvas_verify, canvas_draw, canvas_document_get, canvas_viewport_screenshot, canvas_export_glb, …) and can compose them autonomously. It also sees canvas_tool_list / canvas_tool_call. Plugin tools declared by the browser's protocol-neutral registry are dynamically projected into MCP by their own names and original JSON Schemas; if more than one tab exposes the same name, use canvas_tool_call with an explicit tab.

Commands

| Command | What it does | |---|---| | serve / start | Run the daemon (foreground). | | mcp | Run an MCP stdio server. | | status / health | Daemon + connected-tab health. | | ls | List open canvas tabs (with node counts). | | watch | Live TUI dashboard of open canvases; r refresh, q quit. | | observe | Read revisioned semantic state and optional diff/renderer image. | | act | Execute an atomic typed action batch with an optional revision guard. | | verify | Check semantic postconditions without mutation. | | capabilities | List canonical operations, actions, selectors, and limits. | | tool list / show / call | Discover or invoke live core/plugin tools registered by the editor. | | plugin create | Scaffold a self-contained Canvas Plugin v2 ESM project. | | plugin install | Register and enable a reviewed ESM plugin URL in a selected Canvas tab; requires --trust. | | helper [intent] | Recommend a category and stable read-only AI prompt demo. | | demo list / prompt / show | Browse demos or generate complete CLI/MCP prompts; never operate the canvas directly. | | demo more / demo morse / morse | Browse the extensible demo catalog beyond the featured eight. | | tab info | Show tab metadata. | | document get | Dump the full document IR. | | document nodes | List node IDs, optionally filtered by --page active|<id>. | | project create / open | Create any supported canvas type or open an existing project. | | page list / create / use / rename / delete | Manage pages inside the active project. | | draw | Atomically create nodes from inline JSON, a file, or stdin (-). | | draw rect / frame / text / line / circle / ellipse / path / polygon / star / model | Semantic editable shapes, 3D primitives, or GLB/GLTF/FBX models. | | export glb / svg | Export the live 3D scene or serialize editable 2D nodes through existing exporters. | | node get / update / transform / move / delete | Read, micro-adjust, reparent, or remove a node. | | fiber inspect | Name the real bundles, curves and control points inside one fiberBundle MODEL, paged. | | select set / clear | Drive the selection. | | viewport screenshot / screenshot | PNG bytes of the active tab. | | viewport fit / zoom | Pan/zoom the camera. | | scene / camera / lighting | Switch 2D/3D view and control 3D presentation. | | science render | Compile scientific DSL into editable semantic nodes. | | html write | Turn one HTML/CSS composition into editable canvas layers. | | command | Advanced escape hatch for versioned bridge commands. | | building | Building-3D plugin shortcuts (wall / slab / window / door / …). |

Editing inside a fiber-bundle MODEL

A fiber bundle is one node. Its curves and control points are addressable inside it — they are not child layers, and a network of 78,000 segments will never become 78,000 nodes. So the ids an edit uses have to be asked for:

# 1. what is in there, and which structure it is
canvas fiber inspect "$model" --tab "$tab" --json
#    -> { structureKey: "1:3", counts: {...}, bundles: [{ bundleId: "cord:L:0", curveCount: 12, ... }] }

# 2. the curves of one bundle: point counts, parents and dependents
canvas fiber inspect "$model" --tab "$tab" --level curve --bundle cord:L:0 --limit 20 --json

# 3. the control points of one curve: real ids, rest positions, authored offsets
canvas fiber inspect "$model" --tab "$tab" --level point --curve 'cord:L:0#2' --offset 10 --limit 5 --json
#    -> points: [{ pointId: "cord:L:0#2.12", position: [...], offset: null, animatedPosition: [...] }]

# 4. move one point, BOUND to the structure those ids came from
canvas node update "$model" --tab "$tab" --json --patch \
  '{"fiberStructureKey":"1:3","fiberPointOffsets":{"cord:L:0#2.12":[20,0,0]}}'

Three things worth knowing before you write an offset:

  • Space. position, offset and bounds are all in the bundle's own object space, the space fiberPointOffsets are written in. The MODEL's own transform is on top of that; a world-space number would land somewhere else.
  • Rest vs. what you see. position is the edited REST pose, which is what an offset is applied to. When motionAmount > 0 the shader displaces it every frame, and animatedPosition is where that same point is at the current clock. Neither is "wrong"; they answer different questions.
  • fiberStructureKey is the point of the whole flow. Ids are derived from the seed, so cord:L:0#2.12 names one strand of this network and a different strand of the next one. Pass the key you read back with the edit: if the seed changed in between, the whole patch is refused rather than applied to whatever now carries that number. Omitting it keeps the older unguarded behaviour, which is only safe for a caller holding the live node (the editor's own inspector).
  • A seed change and an internal-id write cannot travel together. {"fiberSeed": 7, "fiberPointOffsets": {...}} is refused: the ids can only have been read from the network the seed is about to replace, and nobody can have read the new one yet. Change the seed, then read the ids again. A seed change may still carry fiberClearOverrides or null values — discarding what belonged to the old structure names no new id.
  • status is not decoration. A query answers ok, or stale when the built bundle and the document name different structures (a reseed the renderer has not caught up with — read again; the CLI exits 1), or unavailable when no bundle is built for that node. Ids are never returned under a structure key they do not belong to.

fiberCurveColors, fiberPointOffsets and fiberClearOverrides go through the same canvas node update mapper and the same editor history as every other patch, so Undo/Redo and reopening behave the way they do everywhere else.

canvas fiber inspect is carried by the generic /v1/command tool-call envelope, invoking the canonical canvas_fiber_inspect tool — the same one the page publishes to MCP, WebMCP and the in-app assistant. It therefore needs no daemon restart: a running daemon relays it like any other command. (The page does have to be new enough to publish the tool; if it is not, the call comes back as an unknown tool rather than as an empty answer.) The daemon also exposes GET /v1/fiber/:nodeId for callers that would rather use a route, but nothing in the CLI depends on it.

Verification and headless mode

verify exits with 0 for passed checks, 1 for failed checks, and 2 for invalid or incomplete evidence. HTTP success alone never passes verification.

canvas verify layout --tab <tab> --no-overlap --output audit.json
canvas verify layout --tab <tab> --no-overlap --screenshot --artifacts layout-001
canvas verify screenshot --tab <tab> --baseline approved.png --artifacts visual-001
canvas verify screenshot --image run/viewport.png --baseline approved.png --artifacts visual-002
canvas browser install --background --json
canvas browser status --json
canvas verify layout --headless --document document.json --no-overlap \
  --app-dir apps/webapp/dist --artifacts artifacts/run-001
canvas headless run workflow.json --app-dir apps/webapp/dist --output artifacts/run-002

Headless mode downloads an open-source Chromium Headless Shell separately (about 93 MiB on the tested macOS ARM host). Use Node 24; installation requires Node >=22.12. A compatible Canvas Web build containing headless.html is required and is not bundled in the CLI package. No pre-opened browser or daemon is needed with --app-dir.

Runs preserve the snapshot, revisioned checks, renderer diagnostics, resource hashes, a bounded credential-redacted console.jsonl, and screenshot in a new artifact directory. Offline actions may include deletion because they operate only on the isolated snapshot; live destructive commands still require --confirm. They do not use user cookies or write cloud documents. Screenshot capture is not a visual-correctness verdict. --require-rendered reports incomplete until render-consistency evidence exists.

Screenshot verification saves actual/baseline/difference PNGs and changed-pixel bounds. Without a baseline it saves a capture for review and exits 2; identical pixels do not prove the baseline itself is correct. Layout checks report recursive overlaps, text/container overflow and opaque rectangle occlusion with node IDs and covered model-box ratios. Transparent/clipped/masked content is not certified as opaque coverage. Live screenshot attachment rejects document/page changes and records that renderer exports exclude DOM overlays.

Layout reports default to a summary: complete issue counts by code/severity and up to 20 examples per list. Use canvas verify layout --details --tab <tab> (or the typed check details: "full") for all issues. Exit status uses the complete audit, including errors outside the example window. This bounds the reply; the audit still computes the complete report internally.

PATH bounds use the renderer's line/Bezier/arc geometry transformed into document world coordinates, shared with node observation and fit bounds. Unknown or malformed paths report unsupported coverage rather than a zero box at the origin. Intersecting path envelopes alone are not proof of painted overlap. Conservative stroke-envelope ambiguity also reports unsupported coverage. allowOverlap: true skips the general overlap checks; connector attachment/crossing checks remain separate. It does not certify overlapping artwork as visually correct.

Use headless capture <checks.json> --tab <tab> --output workflow.json to capture a stable live snapshot. Synchronization is explicit with headless sync <report.json> --tab <tab> --confirm: only passing, exact-ID update/transform/move batches are replayed, guarded by the original revision and verified again. External plugins and terminal connections are disabled in this isolated execution surface.

See the workflow and evidence contract for canvas types, asset origins and regression-driven improvement.

Performance diagnostics

canvas perf is the Canvas CLI performance recorder, not the operating system's perf utility. Use sample for a point-in-time snapshot, run for a bounded recording, and suite for repeatable generated workloads. Every form can write its complete JSON result with -o.

canvas perf sample --tab <tab> -o /tmp/canvas-sample.json
canvas perf run --tab <tab> --duration-ms 5000 -o /tmp/canvas-recording.json
canvas perf delete --tab <tab> -o /tmp/canvas-delete.json
canvas perf suite --tab <tab> --profile scenario -o /tmp/canvas-scenario.json

perf delete is the shortest regression command for deletion flicker. It creates a temporary editable neighborhood, deletes one vector, verifies nearby nodes still exist, and rejects any whole-scene rebuild or empty-document frame. Generated nodes are cleaned up unless --keep is supplied.

Editable 2.5D diagrams

canvas diagram isometric --example platform --output platform.json --json
canvas diagram isometric --example dataflow --output dataflow.json --json
canvas diagram isometric scene.json --output custom.json --json

The compiler projects 3D coordinates into native editable PATH/TEXT nodes inside one FRAME. It supports exploded planes, tiled grids, boxes, wireframe hourglass surfaces, native cubic arrows, polylines, polygons and screen-positioned labels. platform and dataflow are bundled examples; no network, browser or daemon is required to create them. Output paths must be new; --dry-run validates without writing.

This is orthographic 2.5D illustration, with explicit painter order, not live 3D rotation, hidden-surface removal, or bound connector routing. Keep semantic architecture connections in diagram apply (D2). A compatible headless Web build can render these documents for visual review; geometry checks alone do not establish reference-image fidelity.

See the scene schema and runnable capture example.

Configuration

  • Daemon port: CANVAS_BRIDGE_PORT (default 7777).
  • All commands accept --tab <id> when multiple canvas tabs are connected; with exactly one, it's optional.

Architecture (one sentence)

your CLI ──HTTP──> daemon ──WebSocket──> canvas tab in browser
                       │
                       └── MCP stdio (for AI agents)

License

MIT

Repair detached diagram edges

canvas connector reconcile --tab <tab> --page <page> --json previews repairs without writing. Inspect candidate IDs and before/after audits, then repeat with --confirm --revision <beforeRevision> to commit atomically. The command requires a Web editor that advertises connector.reconcile; older editors reject it. Only detached bound LINEs are re-routed through the canonical document-coordinate writer. Healthy/manual/free paths are retained. Unresolved attachments, label occlusion and unrelated crossings remain in the audit; this is not a rendered-visibility or clean-layout claim. Headless workflows can use {type:"connector.reconcile",pageId:"page-1",dryRun:false} on an isolated document and save the resulting document and screenshot before any explicit sync.

Progressive atomic batches and compact reads (local changes, not yet released)

act prints its operation ID immediately, then polls daemon progress automatically on stderr. Stdout remains a single final JSON receipt. The shared control plane prepares changes privately and yields between actions after approximately 8 ms of preparation work. It commits the document once; it does not save partial chunks or create a separate undo entry for each action. An edit, page switch, or selection change while preparing rejects the batch before commit. A single large action, connector refresh, revision hashing, and final observation can still exceed that budget; this is not a hard frame-time guarantee.

canvas act actions.json --tab "$tab" --idempotency-key regroup-001 --json
canvas act status regroup-001 --tab "$tab" --json
canvas node get "$model" --tab "$tab" --fields id,transform,source --json
canvas node get "$model" --tab "$tab" --include-source --json

Status is daemon-local and scoped to the tab and operation ID. In-flight IDs remain reserved. Completed outcomes are retained for five minutes and remain readable if the tab disconnects; full results are retained within the byte budget below. outcome_unknown or OPERATION_UNKNOWN never proves a write was not committed. Do not blindly retry with a new ID. Progress is informational; complete means the control-plane operation finished, not that GPU presentation or cloud persistence was verified. Unplanned daemon exits lose these records; a lifecycle version switch preserves unexpired settled receipts.

Daemon 0.4.1 bounds retained operations to 1024 and concurrent waiters per operation to 16. OPERATION_CAPACITY rejects a new command before sending it; OPERATION_WAITERS_FULL rejects only the additional waiter. Query the existing ID instead of issuing unknown writes under new IDs. Pending operations are not silently evicted; completed records expire after five minutes. Waiter timeouts release their timers and callbacks while preserving the operation ID. Retained UTF-8 result/progress buffers share a 16 MiB budget; one result is limited to 4 MiB and one progress update to 64 KiB. This bounds retained payload bytes, not transient transport/parsing memory or total process RSS. Terminal outcomes replace progress. Oversized progress is reported as progress: null with progressOmitted: true; a later small update may replace it.

The original connected caller still receives the complete reply. If it cannot be retained, status preserves completed/failed plus retention (byteLength, sha256, retained=false, reason), and result is null. A retry with the same ID returns RESULT_NOT_RETAINED without sending another command. Query status and inspect document state before any new write; a missing result is not a failed mutation. Expiry releases payload capacity. These records remain memory-only; daemon restart recovery is not implemented. The default relay deadline is 10 seconds; an explicit timeout remains clamped to 100–300000 ms. Only the socket that received a command may settle its receipt.

Normal node reads replace embedded data URIs with MIME/encoded-length descriptors, including nested assets. Use --include-source explicitly to retrieve the complete source; --fields selects existing top-level fields. The saved document is unchanged. The browser projects before sending, and the daemon also projects old frontend replies. This means an old frontend can still send large data over its WebSocket, even though the CLI response is compact.

Imported GLB/FBX models no longer get a default uniform material. Explicit appearance options still request a uniform replacement, which can remove per-mesh textures. For an existing uniform override, restore the source materials without deleting its settings with canvas node update "$model" --tab "$tab" --patch '{"material":{"enabled":false}}'. Re-enable with enabled:true. This does not retroactively migrate cloud documents. Primitive model defaults remain unchanged.

Cooperative work inside large actions

Creation is prepared in batches of 32 specifications; multi-target update, transform, move, delete and duplicate actions also check the cooperative time budget between nodes. Progress can include completedItems and totalItems for the current action. Completed-action counts still describe the original request, so a single action can report item progress while that count is zero.

All preparation stays private until the atomic commit. Yielding does not create partial documents or extra undo entries. A concurrent document, page or selection change aborts preparation with REVISION_CONFLICT. Individual geometry/engine operations, connector refresh, revision/snapshot and commit remain synchronous: the 8 ms checkpoint budget is a scheduling target, not a worst-frame guarantee.

Reproducible cloud reports

canvas report create --title "Problem" --description "Reproduction details" --demo demo.json --json submits an explicit reproduction bundle. canvas report list, canvas report get <id> --output <new-directory> and maintainer-only canvas report status <id> --status resolved complete the retrieval/triage loop. Use --dry-run before upload and a stable --id for retries. Requires a Canvas API bearer session via CANVAS_API_TOKEN or --token-file, not the local bridge token. No daemon/tab is required. See canvas report --help and the Reports contract. Deployment of the Reports API and D1 migration is a separate prerequisite.

Renderer diagnostics in headless workflows

The proposed requireRendererDiagnostics headless route is a local candidate pending Review; the B2 diagnostics route remains a local unreleased candidate pending review and actual target support.

Science documentation remains bounded: legacy v1 halo and v2 shared SourceObject/CurveFlow numeric paths are distinct; constant emissive beams and dynamic heads are limited profiles. Volume, arbitrary dynamic beam appearance, and 10k aggregate performance are not advertised. Repo-local scene document/package generation does not publish or authenticate an online document.