@solidrt/cli 0.0.51 → 0.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/AGENTS.md +68 -38
  2. package/README.md +86 -15
  3. package/agents/assets.md +19 -5
  4. package/agents/debugging.md +173 -17
  5. package/dist/console.srtapp +73916 -51
  6. package/dist/demos/3d/assets/icon.svg +23 -0
  7. package/dist/demos/3d/package.json +9 -0
  8. package/dist/demos/3d/the-third-dimension/the-third-dimension.srt.js +7528 -0
  9. package/dist/demos/components/assets/icon.png +0 -0
  10. package/dist/demos/components/assets/icon.svg +23 -0
  11. package/dist/demos/components/gallery/gallery.srt.js +14087 -0
  12. package/dist/demos/components/package.json +9 -0
  13. package/dist/server.js +3936 -0
  14. package/package.json +10 -11
  15. package/src/android/docs.md +21 -0
  16. package/src/android/main.ts +286 -0
  17. package/src/{bundler.ts → bundle/bundler.ts} +70 -58
  18. package/src/bundle/docs.md +12 -0
  19. package/src/{commands/bundle.ts → bundle/main.ts} +67 -25
  20. package/src/{untyped-deps.d.ts → bundle/untyped-deps.d.ts} +3 -3
  21. package/src/check/docs.md +10 -0
  22. package/src/check/main.ts +85 -0
  23. package/src/{commands/check.ts → check/typecheck.ts} +11 -41
  24. package/src/client/docs.md +9 -0
  25. package/src/client/main.ts +33 -0
  26. package/src/console/docs.md +14 -0
  27. package/src/console/main.ts +21 -0
  28. package/src/demo/docs.md +33 -0
  29. package/src/demo/main.ts +69 -0
  30. package/src/init/docs.md +11 -0
  31. package/src/{commands/init.ts → init/main.ts} +21 -15
  32. package/src/init/scaffold/AGENTS.md +100 -0
  33. package/src/init/scaffold/package.json +23 -0
  34. package/{scaffold → src/init/scaffold}/templates/components/index.tsx +2 -3
  35. package/{scaffold → src/init/scaffold}/templates/default/index.tsx +2 -3
  36. package/src/lib/args.ts +194 -0
  37. package/src/{artifacts.ts → lib/artifacts.ts} +41 -1
  38. package/src/{dev-dir.ts → lib/dev-dir.ts} +10 -8
  39. package/src/{fonts.ts → lib/fonts.ts} +12 -26
  40. package/src/lib/mode.ts +77 -0
  41. package/src/{project.ts → lib/project.ts} +109 -61
  42. package/src/lib/registry.ts +120 -0
  43. package/src/lib/server-bundle.ts +24 -0
  44. package/src/lib/usage.ts +117 -0
  45. package/src/lib/util.ts +36 -0
  46. package/src/main.ts +109 -31
  47. package/src/mcp/docs.md +22 -0
  48. package/src/mcp/main.ts +719 -0
  49. package/src/pack/docs.md +18 -0
  50. package/src/{pack-folder.ts → pack/layout.ts} +13 -22
  51. package/src/{commands/pack.ts → pack/main.ts} +28 -17
  52. package/src/{packer.ts → pack/trailer.ts} +32 -42
  53. package/src/render/docs.md +19 -0
  54. package/src/{commands/render.ts → render/main.ts} +17 -15
  55. package/src/server/args.ts +126 -0
  56. package/src/server/binaries.ts +47 -0
  57. package/src/server/config.ts +54 -0
  58. package/{server → src/server}/control.ts +239 -81
  59. package/src/server/docs.md +51 -0
  60. package/src/server/line-editor.ts +200 -0
  61. package/src/server/main.ts +476 -0
  62. package/src/server/mode.ts +92 -0
  63. package/src/server/rebuild.ts +90 -0
  64. package/src/server/registry.ts +138 -0
  65. package/src/server/repl.ts +223 -0
  66. package/src/server/state.ts +54 -0
  67. package/{server → src/server}/tsconfig.json +1 -1
  68. package/{server → src/server}/tunnel.ts +6 -6
  69. package/src/server/watcher.ts +121 -0
  70. package/src/tool/main.ts +70 -0
  71. package/src/types/bundle.d.ts +24 -0
  72. package/src/types/control.d.ts +90 -0
  73. package/src/types/registry.d.ts +16 -0
  74. package/scaffold/AGENTS.md +0 -185
  75. package/scaffold/package.json +0 -22
  76. package/scaffold/templates/components/icon.tsx +0 -48
  77. package/scaffold/templates/default/icon.tsx +0 -48
  78. package/server/main.ts +0 -308
  79. package/server/rebuild.ts +0 -76
  80. package/server/state.ts +0 -94
  81. package/src/args.ts +0 -210
  82. package/src/bundle-cli.ts +0 -13
  83. package/src/commands/client.ts +0 -34
  84. package/src/commands/mcp.ts +0 -617
  85. package/src/commands/server.ts +0 -73
  86. package/src/dev-android.ts +0 -176
  87. package/src/dev-client.ts +0 -29
  88. package/src/dev-server.ts +0 -302
  89. package/src/repl.ts +0 -249
  90. package/src/util.ts +0 -122
  91. package/src/watcher.ts +0 -73
  92. /package/src/{prompt.ts → init/prompt.ts} +0 -0
  93. /package/{scaffold → src/init/scaffold}/gitignore +0 -0
  94. /package/{scaffold → src/init/scaffold}/icon.svg +0 -0
  95. /package/{scaffold → src/init/scaffold}/mcp.json +0 -0
  96. /package/{scaffold → src/init/scaffold}/tsconfig.json +0 -0
  97. /package/{server → src/server}/cache.ts +0 -0
  98. /package/{server → src/server}/proxy.ts +0 -0
  99. /package/{server → src/server}/qr.ts +0 -0
  100. /package/{server → src/server}/remap.ts +0 -0
package/AGENTS.md CHANGED
@@ -1,20 +1,29 @@
1
1
  # @solidrt/cli - agent notes
2
2
 
3
- Dense, self-contained facts for running and verifying a SolidRT app. Full docs
4
- live in docs/ (and the website). For the authoring model (elements, props,
5
- reactivity), see @solidrt/core (its AGENTS.md).
3
+ Dense, self-contained facts for running and verifying a SolidRT app. The
4
+ prose lives in README.md and src/<command>/docs.md (also the website). For
5
+ the authoring model (elements, props, reactivity), see @solidrt/core (its
6
+ AGENTS.md).
6
7
 
7
8
  `srt` is the dev tool. Bun is a dev prerequisite only; SolidRT apps run on the
8
9
  bundled `flux` runtime, not on Bun. Invoke via `bunx srt <command>`.
9
10
 
10
- Two companion files carry the depth this one leaves out; read the one that
11
- matches the work before starting it:
12
- - agents/debugging.md - driving a running app over MCP, and the debugging
13
- lessons that cost real time. Read before investigating a bug or verifying
14
- a change against the live app.
15
- - agents/assets.md - the assets/ folder, inlined imports, fonts, and the
16
- `solidrt` package.json key. Read before adding an asset or font, or
17
- building for distribution.
11
+ The dev loop against a running app is pause_watch -> edit -> reload ->
12
+ resume_watch -> get_logs -> get_snapshot, with mute_user_input while you
13
+ measure or test and unmute_user_input after; agents/debugging.md has the
14
+ why of each hold. `reload` surfaces build errors but not type errors:
15
+ `bunx srt check` is for those.
16
+
17
+ agents/ carries the depth this one leaves out; read the one that matches
18
+ the work before starting it:
19
+ - agents/debugging.md - driving a running app over MCP, pointing an agent
20
+ client at the `srt mcp` server, what the dev server serves and its control
21
+ API without MCP (a shell, a CI step), and the debugging lessons that cost
22
+ real time. Read before investigating a bug, verifying a change against the
23
+ live app, or scripting against a server.
24
+ - agents/assets.md - the assets/ folder, inlined imports and the bundle
25
+ output; fonts, the `solidrt` package.json identity key, and distribution
26
+ builds. Read before adding an asset or a font, or building to distribute.
18
27
 
19
28
  ## Commands
20
29
 
@@ -22,19 +31,34 @@ matches the work before starting it:
22
31
  folder: package.json, tsconfig.json, AGENTS.md, a starter src/index.tsx, an
23
32
  empty assets/ (everything in it ships with the app), then installs deps. Greenfield shortcut (no install needed first):
24
33
  `bun create solidrt <dir>`.
25
- - `bunx srt run src/index.tsx` - dev server + a local client window, watches and
26
- hot-reloads. NEEDS A DISPLAY (opens a GUI window). Not usable headless.
27
- - `bunx srt bundle src/index.tsx` - bundle into `dist/bundle/` (or
34
+ - `bunx srt run` - dev server + a local client window, from the project root
35
+ (entry `solidrt.entry` in package.json, default src/index.tsx); `bunx srt run
36
+ <file>` serves a single file outside a project. NEEDS A DISPLAY (opens a GUI
37
+ window). Not usable headless. Reloads on save (the bundle's inputs and
38
+ `assets/`); an agent pauses that with `pause_watch` and pushes its edits
39
+ with the MCP `reload` tool.
40
+ - `bunx srt tool` - list the build-time tools the installed `@solidrt/*`
41
+ packages ship (`<package>/tools/<name>.ts`, named `<package>/<name>`);
42
+ `bunx srt tool <package>/<name> [arguments]` runs one under bun in the
43
+ project, everything after the name passed through as the tool's own
44
+ arguments (each tool prints its own usage on `--help`). What a tool does
45
+ is the package's business (e.g. `3d/model` bakes a glTF into a model
46
+ file); srt only finds and runs them.
47
+ - `bunx srt check [file|dir]` - build in memory and typecheck, no output
48
+ and no reload. With a folder it covers every entry under it (src/index.tsx
49
+ and examples/*), so `bunx srt check .` answers "did I break any example"
50
+ in one call. The dev server runs it once at startup without gating on it.
51
+ - `bunx srt bundle` - bundle the project into `dist/bundle/` (or
28
52
  `--output <dir>`): `<name>.srt.js` plus the app's isolate modules as
29
53
  `isolates/<id>.js`. With `--compile`, bytecode (`.srt.bin`/`.bin`)
30
54
  instead. Move the dir, not the bare file - a bundle loaded without its
31
55
  isolates/ dir loses them (`--stdout` cannot carry them at all).
32
56
  `--minify`, `--dev` also available.
33
- - `bunx srt render src/index.tsx [flags]` - render OFFSCREEN to PNG frames,
57
+ - `bunx srt render [flags]` - render the project OFFSCREEN to PNG frames,
34
58
  optionally replaying a `--script` file recorded via `--capture`.
35
59
  - `bunx srt server [file]` / `bunx srt client` - the two halves of `run`
36
60
  separately (server distributes code; clients on other devices connect to it).
37
- - `bunx srt run src/index.tsx --capture out.script.json` - records keydown/keyup
61
+ - `bunx srt run --capture out.script.json` - records keydown/keyup
38
62
  from every connected client into one script file (written on client
39
63
  disconnect), for replaying later with `render --script`. The file is JSON
40
64
  Lines and hand-authorable: one object per line,
@@ -47,8 +71,8 @@ matches the work before starting it:
47
71
 
48
72
  Two reliable checks that need no GUI:
49
73
 
50
- 1. `bunx srt bundle src/index.tsx` - exit 0 means the app compiles. Fast.
51
- 2. `bunx srt render src/index.tsx --size 480x640 --duration 1 --fps 2` -
74
+ 1. `bunx srt bundle` - exit 0 means the app compiles. Fast.
75
+ 2. `bunx srt render --size 480x640 --duration 1 --fps 2` -
52
76
  renders offscreen via EGL and writes `frame-NNNNNN.png`. This actually
53
77
  proves the app renders. Combine with `--fps`/`--duration` (defaults
54
78
  1280x720, 60fps, 1s). No display needed: rendering uses SDL's offscreen
@@ -76,29 +100,35 @@ behavior in isolation.
76
100
  (Wayland) and the pbuffer path on Windows from a desktop session; a
77
101
  non-interactive Windows session and macOS are unverified.
78
102
 
79
- ## Sessions (parallel dev servers on one machine)
80
-
81
- - `-s <N>` / `--session <N>` (default 0) picks the dev server: port
82
- `34884 + N`. Valid on `run`, `server`, `client`, `mcp`. `srt run -s1` is a
83
- second, fully independent dev setup; `srt client -s1 -c2` attaches another
84
- client to it.
85
- - `-c <N>` / `--client <N>` picks the client data tree, defaulting to the
86
- session number. (`--compile` gave its short to `--client`.)
87
- - Dev state lives in `~/.solidrt/`: `servers/<port>/` holds each server's tunnel
88
- key and `live.json` (the registry record MCP resolution reads, removed at
89
- exit), `clients/client<M>/` the client trees (srt passes
90
- `--data-root ~/.solidrt/clients` to every locally spawned client).
91
- - A server run serves the project it was started in; `load` outside the
92
- project root is refused. Restart the server in another project to switch.
93
- - `srt mcp` needs no port: each tool call resolves the server serving the
94
- project the bridge runs in (registry match + probe); `-s`/`--port` pin it.
103
+ ## Servers (what is served, and finding it again)
104
+
105
+ - A dev server serves a project (started in its root: the cwd must hold the
106
+ package.json) or a single file (`srt run <file>` outside a project). Both
107
+ in one place is ambiguous, so `srt run <file>` in a project root needs
108
+ `--project` (the project, with this entry) or `--file` (the file alone).
109
+ Nothing searches upward for a package.json.
110
+ - One server per project or file. The server binds the port it had last
111
+ time, else the first free one from 34884 up, and prints it; `--port <N>`
112
+ pins it. Loopback only
113
+ unless `--lan`, which is what phones and other devices (and `srt android`
114
+ on a real device) need; `--tunnel` works without it.
115
+ - `-c <N>` / `--client <N>` picks the client data tree (default 0). Storage
116
+ is per app inside a tree, so two projects share client 0; only two clients
117
+ of the same app need distinct slots.
118
+ - Dev state lives in `~/.solidrt/`: `servers/<key hash>/` holds each server's
119
+ `live.json` (the registry record, written by the server and removed at
120
+ exit), its remembered `port` and tunnel key; `clients/client<M>/` the
121
+ client trees (srt passes `--data-root ~/.solidrt/clients` to every locally
122
+ spawned client). A record left behind by a crash is pruned when the next
123
+ server starts, and `srt client`, `srt mcp` and `srt android` confirm a
124
+ record against the server (its control API names the key it serves)
125
+ before using it.
126
+ - `srt client` and `srt mcp` need no port: run from the project root (or the
127
+ directory of a served file) they resolve the server from the registry;
128
+ `--port` pins one.
95
129
 
96
130
  ## Dev server proxies (when clients on other devices need your machine's data)
97
131
 
98
132
  - `--proxy-http` - route `fetch` through the dev server; responses cached in
99
133
  `.srt-data/http-cache.db` in the project root (delete the file to clear).
100
134
 
101
- ## REPL (opened by `run`/`server`)
102
-
103
- `load <file>`, `reload [n]`, `stop [n]`, `list`, `!<cmd>`, `quit`/`exit`.
104
- `load` is bound to the project root the server was started in.
package/README.md CHANGED
@@ -1,26 +1,97 @@
1
1
  # @solidrt/cli
2
2
 
3
- Developer tooling for SolidRT. Provides a development environment for `@solidrt/core` applications.
3
+ Developer tooling for SolidRT: `srt`, one command-line tool covering the
4
+ whole cycle of a `@solidrt/core` application: scaffold, develop, inspect,
5
+ ship. A GUI inspector, itself a SolidRT app, is in progress alongside it.
4
6
 
5
- > LLM agents: see [AGENTS.md](./AGENTS.md) for a dense, self-contained quickstart.
7
+ > LLM agents: `AGENTS.md` in this package is a dense, self-contained quickstart.
8
+
9
+ Bun is a dev prerequisite only; apps run on the bundled `flux` runtime.
10
+ Invoke via `bunx srt <command>`. A scaffolded project wires the common
11
+ commands into scripts, so day-to-day work is `bun run dev`, `bun run
12
+ android` (and `server`, `client`, `pack` for the rest).
6
13
 
7
14
  ## Commands
8
15
 
16
+ | Command | |
17
+ | --- | --- |
18
+ | [`srt init <dir>`](src/init/docs.md) | scaffold a new project into a new (empty) folder |
19
+ | [`srt run [file]`](src/server/docs.md) | dev server + local client window |
20
+ | [`srt server [file]`](src/server/docs.md) | dev server only |
21
+ | [`srt client`](src/client/docs.md) | client only, attached to the project's dev server |
22
+ | [`srt android`](src/android/docs.md) | install and launch the client on a connected Android device |
23
+ | [`srt demo [n]`](src/demo/docs.md) | list the demos the CLI ships, or run one |
24
+ | [`srt console`](src/console/docs.md) | start the dev console: the dev servers on this machine and their clients |
25
+ | [`srt check [file]`](src/check/docs.md) | build and typecheck, writing nothing |
26
+ | [`srt bundle [file]`](src/bundle/docs.md) | transpile to JS or bytecode (dist/bundle/) |
27
+ | [`srt render [file]`](src/render/docs.md) | render frames offscreen, optionally replaying a script |
28
+ | [`srt pack [file]`](src/pack/docs.md) | bundle + compile to a standalone executable (experimental) |
29
+ | [`srt mcp`](src/mcp/docs.md) | MCP server (stdio) exposing the running dev server to agents |
30
+
31
+ `srt --help` lists every command and option; `srt --version` prints the
32
+ version. Run from the project root to work on the project (its entry is
33
+ `solidrt.entry` in package.json, default `src/index.tsx`); pass a file to
34
+ work on that file on its own.
35
+
36
+ ## Develop
37
+
38
+ ```sh
39
+ srt run
40
+ ```
41
+
42
+ Starts the dev server and a local client window against it. The server
43
+ pushes the bundle to every connected client, so one server can drive a
44
+ desktop window and a phone at the same time; edits reach them on an
45
+ explicit reload (the MCP `reload` tool) or on save.
46
+
47
+ Split them when you need to:
48
+
49
+ ```sh
50
+ srt server # server only
51
+ srt client # client only, the project's server (from its root)
52
+ srt client --server 192.168.1.5:34884 # client only, pointed at that address
53
+ srt android # install and launch on a connected device
54
+ ```
55
+
56
+ ## Inspect
57
+
58
+ ```sh
59
+ srt mcp
60
+ ```
61
+
62
+ Exposes the running app to a coding agent: logs, stats, the live render
63
+ tree, screenshots, GPU resources, input injection, a virtual-time transport,
64
+ reload, and the app's own debug commands. A scaffolded project ships an
65
+ `.mcp.json`, so Claude Code attaches with no setup.
66
+
67
+ ## Record and replay
68
+
69
+ ```sh
70
+ srt run --capture session.json
71
+ srt render --script session.json --fps 60 --duration 5
72
+ ```
73
+
74
+ `--capture` records key events from connected clients to a script; `render`
75
+ replays it headlessly and writes frames, which makes bug reports
76
+ reproducible and turns an interaction into a video.
77
+
78
+ ## Ship
79
+
9
80
  ```sh
10
- bunx srt run [file] # start dev server + client, optionally load a file
11
- bunx srt run --server [file] # start dev server only
12
- bunx srt run --client # start dev client only
81
+ srt check . # build and typecheck every entry, no build output
82
+ srt bundle # transpile to JS or bytecode
83
+ srt pack # standalone executable (experimental)
13
84
  ```
14
85
 
15
- ## REPL
86
+ `srt bundle --flux` and `srt pack --flux` target the bare Flux runtime
87
+ instead of a SolidRT app, for scripts and servers with no UI.
16
88
 
17
- Running `bunx srt run` opens an interactive REPL. The loaded file is watched for changes and automatically pushed to connected clients.
89
+ ## Layout
18
90
 
19
- | Command | Description |
20
- | --------------- | ------------------------------------------------ |
21
- | `load <file>` | load and push a `.tsx`, `.srt.js`, or `.srt.bin` |
22
- | `reload [n]` | rebuild and push to all clients, or client `n` |
23
- | `stop [n]` | stop all clients, or client `n` |
24
- | `list` | list connected clients |
25
- | `!<cmd>` | run a shell command |
26
- | `quit` / `exit` | exit the dev server |
91
+ One folder per command under `src/`, named for what it does; the first line
92
+ of each says its runtime. `src/server/` is the dev server, a flux script
93
+ (`bun`-free at runtime, its own tsconfig); every other command runs on bun.
94
+ `src/lib/` is what the bun commands share, `src/types/` the type-only
95
+ contracts between the two runtimes. Each command folder carries its
96
+ `docs.md` (this site); the depth agents need lives in `agents/` at the
97
+ package root, as in the other packages.
package/agents/assets.md CHANGED
@@ -1,7 +1,9 @@
1
- # Assets and app identity
1
+ # Assets, fonts, identity and distribution
2
2
 
3
- Read this before adding an asset, a font, or preparing a build for
4
- distribution.
3
+ Read this before adding an asset or a font, setting the app's identity, or
4
+ preparing a build for distribution.
5
+
6
+ ## Assets and inlined imports
5
7
 
6
8
  - Everything under `assets/` ships with the app: the folder is collected
7
9
  wholesale into each build's version manifest (no bundler analysis, no
@@ -16,6 +18,17 @@ distribution.
16
18
  `.frag`) are declared as text modules out of the box, so they typecheck
17
19
  without setup. Inlining trades update granularity for zero I/O - keep big or
18
20
  streamable files (audio, images) in `assets/`.
21
+ - `bunx srt bundle` writes `dist/bundle/` (or `--output <dir>`):
22
+ `<name>.srt.js` plus the app's isolate modules as `isolates/<id>.js`; with
23
+ `--compile`, bytecode (`.srt.bin`/`.bin`) instead. Move the dir, not the
24
+ bare file - a bundle loaded without its isolates/ dir loses them
25
+ (`--stdout` cannot carry them at all). Isolates (`"use isolate"` modules)
26
+ exist in projects only, not for a file served on its own.
27
+ - `bunx srt bundle --json` is the dev server's rebuild contract: one JSON
28
+ object (code, sourcemap, manifest, isolates) on stdout. Not for humans.
29
+
30
+ ## Fonts, identity and distribution
31
+
19
32
  - Custom fonts go in `assets/fonts/` and are declared in the `solidrt.fonts`
20
33
  map in package.json (alias -> file path; role aliases `sans`/`serif`/`mono`
21
34
  replace the built-in defaults, `false` drops one, other keys add fonts
@@ -26,7 +39,8 @@ distribution.
26
39
  folder, defaults from the package name in dev, and `srt pack` warns
27
40
  while defaulted. `org` and `displayName` are optional display metadata
28
41
  (future launcher/window naming) with no storage meaning.
29
- - `bunx srt pack src/index.tsx` builds a single-file executable;
30
- `bunx srt pack --folder src/index.tsx` writes the flat app folder
42
+ - `bunx srt pack` builds a single-file executable (the runner with the
43
+ bytecode, manifest, assets and fonts appended as a trailer);
44
+ `bunx srt pack --folder` writes the flat app folder
31
45
  (runner + manifest.json + bundle + assets/, plus the runner's GL
32
46
  libraries on Windows and macOS) to `dist/pack/`.
@@ -1,30 +1,46 @@
1
1
  # Debugging a running app
2
2
 
3
3
  Read this before investigating a bug in a running app, or before driving the
4
- app over MCP to verify a change.
4
+ app to verify a change: over MCP, or through the same control API from a
5
+ shell or a CI step ("The control API without MCP" below).
5
6
 
6
7
  ## Driving the app over MCP
7
8
 
8
- The project ships an MCP server (`.mcp.json`, `srt mcp`) that talks to the dev
9
- server `bunx srt run` starts. Each tool documents itself in full - read the
10
- tool description rather than guessing at its arguments. What the individual
11
- descriptions cannot tell you:
9
+ The project ships an MCP server (`srt mcp`) that talks to the dev server
10
+ `bunx srt run` starts. If your client lists no `solidrt` tools, it has not
11
+ been pointed at the server yet - see "Wiring up an agent client" below. Each
12
+ tool documents itself in full - read the tool description rather than guessing
13
+ at its arguments. What the individual descriptions cannot tell you:
12
14
 
13
15
  - If `list_clients` is empty, no app is running: ask the user to start
14
- `bunx srt run src/index.tsx` rather than starting a second one yourself.
15
- The bridge dials the dev server's default port (34884), so if the user
16
- started it with `--port N`, `.mcp.json` needs the same flag:
17
- `"args": [..., "mcp", "--port", "N"]`.
16
+ `bunx srt run` rather than starting a second one yourself.
17
+ The bridge needs no port: it resolves the server currently serving this
18
+ project, whatever `--port` it was started with, and re-resolves when
19
+ that server goes away or a different project's server takes its port.
20
+ Passing the flag to `srt mcp` pins the bridge to that one server instead.
18
21
  - Several clients may be attached at once (desktop window, phone, tablet)
19
22
  with different sizes, display scales and safe areas. `reload` pushes to all
20
- of them, but `call_debug` / `send_input` / `get_snapshot` / log cursors are
21
- per client, and interactive state does NOT sync - a flow driven on one
22
- client leaves the others sitting on the initial screen, which reads as a
23
- crash to a human holding that device. So: when driving state via
24
- `call_debug`, send the same call to every client (or say which client you
25
- are using); and before calling a visual change done, snapshot each distinct
26
- form factor at least once - a layout that fits one window can clip or
27
- overflow another.
23
+ of them, but `call_debug` / `send_input` / `get_snapshot` are per client
24
+ (`get_logs` takes a `client` filter), and interactive state does NOT sync
25
+ - a flow driven on one client leaves the others sitting on the initial
26
+ screen, which reads as a crash to a human holding that device. So: when
27
+ driving state via `call_debug`, send the same call to every client (or say
28
+ which client you are using); and before calling a visual change done,
29
+ snapshot each distinct form factor at least once - a layout that fits one
30
+ window can clip or overflow another.
31
+ - Measuring or testing? `mute_user_input` first: it mutes the user's own
32
+ input on every client (a stray click or keypress mid-measurement corrupts
33
+ the result); `send_input` still works. `unmute_user_input` the moment you
34
+ are done, or whenever you need the human to press something: they see an
35
+ unresponsive client meanwhile. The bridge unmutes when it exits, the
36
+ server when it stops, but neither is a reason to leave a mute on.
37
+ - Editing? `pause_watch` first: the server reloads on save, and a
38
+ half-finished save would land on the user's screens as a build error or
39
+ a broken app. Edit, `reload`, then `resume_watch`. The two holds are
40
+ separate on purpose: the mute keeps the human out while you measure, the
41
+ pause keeps your own saves out while you edit, and a human editing next
42
+ to you keeps auto-reload unless you hold it. The bridge resumes when it
43
+ exits, but do not rely on that.
28
44
  - A `shader` on `<window>` runs on the finished frame past the point every
29
45
  capture reads: `get_snapshot` returns the UNSHADED content (window node
30
46
  included), `get_texture` has no id for the shaded layer, and
@@ -40,6 +56,146 @@ descriptions cannot tell you:
40
56
  ~/.claude/settings.json to cover every solidrt project). This is the user's
41
57
  call to make, once, in their own tooling.
42
58
 
59
+ ## Wiring up an agent client
60
+
61
+ `.mcp.json` in the project root is Claude Code's convention, and a scaffolded
62
+ app ships one. MCP standardizes the protocol, not how a client discovers
63
+ servers, so every other client reads its own file. The entry is the same
64
+ everywhere, in the client's syntax:
65
+
66
+ ```json
67
+ { "command": "bun", "args": ["node_modules/@solidrt/cli/bin/srt", "mcp"] }
68
+ ```
69
+
70
+ Where it goes (these locations move between client releases; check the
71
+ client's own docs if it does not match):
72
+
73
+ - Claude Code: `.mcp.json` in the project root, under `mcpServers`. Claude
74
+ Code asks once, per project, before it will launch a server from this
75
+ file; until that is approved the `solidrt` tools are absent.
76
+ - Cursor: `.cursor/mcp.json`, under `mcpServers`
77
+ - VS Code / Copilot: `.vscode/mcp.json`, under `servers`
78
+ - Gemini CLI: `.gemini/settings.json`, under `mcpServers`
79
+ - Codex CLI: `~/.codex/config.toml`, under `[mcp_servers.solidrt]`
80
+
81
+ Several of these also ship a command that writes the entry for you
82
+ (`claude mcp add`, `codex mcp add`); prefer it over hand-editing.
83
+
84
+ The one constraint: the bridge must run with the project as its working
85
+ directory. It finds the dev server through the nearest package.json above
86
+ its cwd, and the `args` path is relative to the project root. A per-project
87
+ file gives that for free. A user-level entry works too, but only while the
88
+ client itself was launched from the project root (or the client supports a
89
+ `cwd` field and it is set); launched from elsewhere, every tool call answers
90
+ "No dev server for ..." while one is running.
91
+
92
+ ## The dev server
93
+
94
+ What the dev server (`srt run` / `srt server`) is and what it serves.
95
+
96
+ - A dev server serves a project (started in its root: the cwd must hold the
97
+ package.json) or a single file (`srt run <file>` outside a project). Both
98
+ in one place is ambiguous, so `srt run <file>` in a project root needs
99
+ `--project` (the project, with this entry) or `--file` (the file alone).
100
+ Nothing searches upward for a package.json.
101
+ - One server per project or file. The server binds the port it had last
102
+ time, else the first free one from 34884 up, and prints it; `--port <N>`
103
+ pins it. Loopback only unless `--lan`.
104
+ - Dev state lives in `~/.solidrt/`: `servers/<key hash>/` holds each server's
105
+ `live.json` (the registry record, written by the server and removed at
106
+ exit), its remembered `port` and tunnel key; `clients/client<N>/` the
107
+ client trees. `srt client` and `srt mcp` resolve the server from the
108
+ registry by the project (or served file) at their cwd; `--port` pins one.
109
+ A record whose process died is pruned when the next server starts, and
110
+ resolution confirms a record against the server before using it.
111
+ - The server is one flux process, complete on its own:
112
+ `flux dist/server.js [file] [--project|--file] [--port N] [--lan]
113
+ [--proxy-http] [--capture f] [--tunnel] [--stats] [--client N ...] [-- args]`
114
+ (src/server/args.ts). It finds the platform binaries, bun and srt through
115
+ `SRT_PLATFORM_DIR`, `SRT_BUN` and `SRT_CLI`, which `srt` sets; started by
116
+ hand in a checkout it needs only `SRT_HOME`.
117
+ - Reload-on-save watches the bundle's inputs (every file the running
118
+ bundle was built from, dependencies included) and the `assets/` tree, not
119
+ a directory: a file the app does not import never triggers a rebuild.
120
+ While the last build failed, the source tree is watched as a whole until
121
+ a build succeeds. `POST /__control__/watch?active=false` pauses it (the
122
+ MCP `pause_watch` tool) while an agent edits; `POST /__control__/reload`
123
+ (the `reload` tool) is the agent's push.
124
+
125
+ ## The control API without MCP
126
+
127
+ Every MCP tool is a thin wrapper over the dev server's HTTP control API, so
128
+ a shell script, a CI step, or an agent with no MCP bridge can drive the app
129
+ the same way. Base: `http://127.0.0.1:<port>/__control__/`, where `<port>`
130
+ is the one `srt run` printed at startup (also in the server's
131
+ `~/.solidrt/servers/*/live.json` record). GET unless noted;
132
+ every endpoint answers JSON and an error is `{ "error": "..." }` with a
133
+ 4xx/5xx status, and every response carries `x-solidrt-project` (the key
134
+ served) and `x-solidrt-generation` (the server run) headers, so a caller can
135
+ confirm it reached the server it meant and notice a restart. Endpoints that
136
+ talk to a client take `?client=<id>` (from `/clients`); it may be omitted
137
+ when exactly one client is connected.
138
+
139
+ - `/clients` - `{ generation, key, mode, entry, projectDir, userInputMuted,
140
+ watchPaused, clients: [{ id, ... }] }`. Check `key` (the project root or
141
+ single file served) is the app you mean.
142
+ - `/logs?since=<seq>&level=<lvl>&contains=<text>&wait=<ms>&client=<id>` -
143
+ `{ entries: [{ seq, at, client, level, text, repeats? }], latest,
144
+ generation }`. Pass the previous `latest` as `since` to read only new
145
+ output; `client` keeps one client's entries (all clients without); a
146
+ changed `generation` means the server restarted and cursors and client
147
+ ids are stale.
148
+ - `/tree?query=<text>&root=<id>&depth=<n>&props=true` - `{ limit, matches:
149
+ [{ id, kind, path, x, y, width, height }] }` for a query, the nested tree
150
+ otherwise. Node ids are per client and change on reload; re-query after
151
+ `/reload`.
152
+ - `/snapshot?node=<id>` - `{ width, height, pngBase64 }`, display-scaled;
153
+ add `&format=raw` for `rgbaBase64` (RGBA8 bytes, no decoder needed for
154
+ pixel assertions), `&x=&y=&width=&height=` (all four) to crop,
155
+ `&scale=<1-8>`. Snapshot the smallest node that shows the change, not the
156
+ window root.
157
+ - `/texture?id=<textureId>` - same shape and options as `/snapshot`, at the
158
+ texture's native size (a scene or shader target behind a `<texture>` leaf).
159
+ - `/gpu?label=<text>` - the GPU resource inventory; `label` keeps only the
160
+ resources created with exactly that label (ids change on reload, labels
161
+ do not).
162
+ - `/buffer?id=<bufferId>&offset=<n>&length=<n>&as=<f32|u16|u8>` - vertex
163
+ buffer contents (default f32; reads cap at 64 KiB).
164
+ - `/stats?window=<ms>` - the performance statistics. POST
165
+ `/stats?active=true|false` switches the on-screen stats overlay instead
166
+ (the `set_stats_overlay` tool): one client with `&client=<id>`, every
167
+ client (and the setting new clients join with) without; `/clients`
168
+ reports each client's `stats`. `frames` counts the frames actually
169
+ rebuilt in the window, `fps` the refresh rate presented at: when motion
170
+ looks wrong and `fps` looks fine, `frames` is the number to read - a
171
+ picture that only changes 26 times a second shows 26 there, and the
172
+ stutter is the app's update cadence, not the engine's.
173
+ - `/debug` - the app's registered debug commands; POST
174
+ `/debug?name=<cmd>` with a JSON body as its args to call one.
175
+ - POST `/input` with `{ "events": [...] }` - synthetic input through the
176
+ real pipeline, same event shape as the `send_input` tool (tap real
177
+ coordinates read from `/tree`).
178
+ - POST `/clock?scale=<x>` (0 pauses) / `?step=<n>` frames while paused;
179
+ `{ scale, pendingSteps }` back. `/clients` reports each client's `timeScale`,
180
+ reset to 1 by every push.
181
+ - POST `/reload` - rebuild and push to every client; `{ ok, clients }` or
182
+ the build error.
183
+ - POST `/load` with `{ "entry": "<path>" }` - switch the entry and push it;
184
+ `{ ok, entry, clients }` or the build error. Relative paths resolve
185
+ against the project root (file mode: the served file's directory). A
186
+ project server only loads files inside its project.
187
+ - POST `/mute?active=true|false` - mute/unmute the user's own input on
188
+ every client, gamepads included (synthetic `/input` still goes through;
189
+ resize and close cannot be muted). `{ ok, active, clients }`. The mute
190
+ lifts when the server stops; unmute yourself when done.
191
+ - POST `/watch?active=true|false` - resume/pause reload-on-save (`active`
192
+ is whether it watches). `{ ok, active }`. Paused, saves push nothing
193
+ until `/reload`; changes made meanwhile are not replayed on resume. The
194
+ pause lifts when the server stops; resume yourself when done.
195
+
196
+ The loop is the same as over MCP: `/reload`, then `/logs?since=`, then
197
+ `/tree` for coordinates and `/snapshot` of the smallest relevant node.
198
+
43
199
  ## Lessons that cost real time
44
200
 
45
201
  - console.log + get_logs is your primary probe into runtime state. For state