@solidrt/cli 0.0.48 → 0.0.50
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.
- package/AGENTS.md +34 -3
- package/package.json +8 -7
- package/scaffold/AGENTS.md +48 -4
- package/scaffold/package.json +5 -4
- package/server/main.ts +14 -8
- package/server/rebuild.ts +10 -2
- package/server/state.ts +1 -1
- package/server/tunnel.ts +5 -4
- package/src/args.ts +59 -24
- package/src/bundle-cli.ts +3 -2
- package/src/bundler.ts +127 -28
- package/src/commands/check.ts +6 -2
- package/src/commands/client.ts +16 -8
- package/src/commands/init.ts +59 -75
- package/src/commands/mcp.ts +143 -4
- package/src/commands/pack.ts +6 -2
- package/src/commands/server.ts +4 -1
- package/src/dev-dir.ts +25 -0
- package/src/dev-server.ts +46 -13
- package/src/pack-folder.ts +24 -3
- package/src/packer.ts +36 -3
- package/src/project.ts +9 -1
- package/src/prompt.ts +46 -72
- package/src/repl.ts +14 -6
- package/scaffold/templates/components/template.json +0 -4
- package/scaffold/templates/default/template.json +0 -4
package/AGENTS.md
CHANGED
|
@@ -24,7 +24,11 @@ bundled `flux` runtime, not on Bun. Invoke via `bunx srt <command>`.
|
|
|
24
24
|
separately (server distributes code; clients on other devices connect to it).
|
|
25
25
|
- `bunx srt run src/index.tsx --capture out.script.json` - records keydown/keyup
|
|
26
26
|
from every connected client into one script file (written on client
|
|
27
|
-
disconnect), for replaying later with `render --script`.
|
|
27
|
+
disconnect), for replaying later with `render --script`. The file is JSON
|
|
28
|
+
Lines and hand-authorable - the exact shape (`after` ms, `type`, `key`,
|
|
29
|
+
`device`) is in `docs/cli.md` under `--capture`. For probing app state
|
|
30
|
+
without a display, `-- <args...>` reaches the app as `flux:process` argv
|
|
31
|
+
(also `docs/cli.md`), which is often simpler than scripting input.
|
|
28
32
|
|
|
29
33
|
## Verifying without a display (headless / CI / agent box)
|
|
30
34
|
|
|
@@ -35,7 +39,8 @@ Two reliable checks that need no GUI:
|
|
|
35
39
|
renders offscreen via EGL and writes `frame-NNNNNN.png`. This actually
|
|
36
40
|
proves the app renders. Combine with `--fps`/`--duration` (defaults
|
|
37
41
|
1280x720, 60fps, 1s). No display needed: rendering uses SDL's offscreen
|
|
38
|
-
driver
|
|
42
|
+
driver, or alloy's own EGL pbuffer where that driver cannot go headless
|
|
43
|
+
(see the ANGLE gotcha below).
|
|
39
44
|
|
|
40
45
|
Also headless: the bundled flux runtime runs a plain `.js` file directly -
|
|
41
46
|
`node_modules/@solidrt/<platform>/flux script.js` (e.g.
|
|
@@ -49,12 +54,38 @@ behavior in isolation.
|
|
|
49
54
|
- `--size` is physical output pixels: layout runs at exactly that size
|
|
50
55
|
(display scale is pinned to 1), so frames are identical on every machine.
|
|
51
56
|
- Run from the project directory. There is no `bunx --cwd` flag.
|
|
57
|
+
- On ANGLE stacks (Windows, macOS) SDL's offscreen driver cannot go
|
|
58
|
+
headless (no EGL device enumeration), so `render` there builds its own
|
|
59
|
+
EGL pbuffer context behind SDL's dummy video driver instead; the log says
|
|
60
|
+
"using a headless EGL context". If that also fails it renders into a
|
|
61
|
+
hidden window, which needs an interactive desktop session (fails under a
|
|
62
|
+
service, in Session 0, or over SSH-only). Verified headless on Linux
|
|
63
|
+
(Wayland) and the pbuffer path on Windows from a desktop session; a
|
|
64
|
+
non-interactive Windows session and macOS are unverified.
|
|
65
|
+
|
|
66
|
+
## Sessions (parallel dev servers on one machine)
|
|
67
|
+
|
|
68
|
+
- `-s <N>` / `--session <N>` (default 0) picks the dev server: port
|
|
69
|
+
`34884 + N`. Valid on `run`, `server`, `client`, `mcp`. `srt run -s1` is a
|
|
70
|
+
second, fully independent dev setup; `srt client -s1 -c2` attaches another
|
|
71
|
+
client to it.
|
|
72
|
+
- `-c <N>` / `--client <N>` picks the client data tree, defaulting to the
|
|
73
|
+
session number. (`--compile` gave its short to `--client`.)
|
|
74
|
+
- Dev state lives in `~/.solidrt/`: `servers/<port>/` holds each server's tunnel
|
|
75
|
+
key and `live.json` (the registry record MCP resolution reads, removed at
|
|
76
|
+
exit), `clients/client<M>/` the client trees (srt passes
|
|
77
|
+
`--data-root ~/.solidrt/clients` to every locally spawned client).
|
|
78
|
+
- A server run serves the project it was started in; `load` outside the
|
|
79
|
+
project root is refused. Restart the server in another project to switch.
|
|
80
|
+
- `srt mcp` needs no port: each tool call resolves the server serving the
|
|
81
|
+
project the bridge runs in (registry match + probe); `-s`/`--port` pin it.
|
|
52
82
|
|
|
53
83
|
## Dev server proxies (when clients on other devices need your machine's data)
|
|
54
84
|
|
|
55
85
|
- `--proxy-http` - route `fetch` through the dev server; responses cached in
|
|
56
|
-
`.srt-data/http-cache.db` (delete the file to clear).
|
|
86
|
+
`.srt-data/http-cache.db` in the project root (delete the file to clear).
|
|
57
87
|
|
|
58
88
|
## REPL (opened by `run`/`server`)
|
|
59
89
|
|
|
60
90
|
`load <file>`, `reload [n]`, `stop [n]`, `list`, `!<cmd>`, `quit`/`exit`.
|
|
91
|
+
`load` is bound to the project root the server was started in.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.50",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Antoine van Wel",
|
|
6
6
|
"type": "module",
|
|
@@ -19,25 +19,26 @@
|
|
|
19
19
|
"@babel/core": "^7.0.0",
|
|
20
20
|
"@babel/plugin-syntax-jsx": "^7.0.0",
|
|
21
21
|
"@babel/preset-typescript": "^7.0.0",
|
|
22
|
+
"@clack/prompts": "^1.7.0",
|
|
22
23
|
"@jridgewell/remapping": "^2.3.0",
|
|
23
24
|
"@jridgewell/trace-mapping": "^0.3.25",
|
|
24
25
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
25
|
-
"babel-preset-solid": "2.0.0-
|
|
26
|
+
"babel-preset-solid": "2.0.0-rc.0",
|
|
26
27
|
"bonjour-service": "^1.4.0",
|
|
27
28
|
"qrcode-generator": "^2.0.4",
|
|
28
29
|
"zod": "^4.4.3"
|
|
29
30
|
},
|
|
30
31
|
"optionalDependencies": {
|
|
31
|
-
"@solidrt/darwin-arm64": "0.0.
|
|
32
|
-
"@solidrt/linux-arm64-gnu": "0.0.
|
|
33
|
-
"@solidrt/linux-x64-gnu": "0.0.
|
|
34
|
-
"@solidrt/win32-x64-msvc": "0.0.
|
|
32
|
+
"@solidrt/darwin-arm64": "0.0.50",
|
|
33
|
+
"@solidrt/linux-arm64-gnu": "0.0.50",
|
|
34
|
+
"@solidrt/linux-x64-gnu": "0.0.50",
|
|
35
|
+
"@solidrt/win32-x64-msvc": "0.0.50"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"typescript": "^7"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
|
-
"@solidrt/flux-types": "0.0.
|
|
41
|
+
"@solidrt/flux-types": "0.0.50",
|
|
41
42
|
"@types/babel__core": "^7.20.5",
|
|
42
43
|
"@types/bun": "latest"
|
|
43
44
|
}
|
package/scaffold/AGENTS.md
CHANGED
|
@@ -280,6 +280,46 @@ work stops being free" below is where it does not. Rules, in order of leverage:
|
|
|
280
280
|
snapshot instead of re-rasterizing. A window shader's output is invisible
|
|
281
281
|
to get_snapshot and every other MCP tool; `srt render` is the only way to
|
|
282
282
|
see it (Run / verify below).
|
|
283
|
+
7. `flux:wasm` is not the fast lane. It runs a pure interpreter (wasmi, no
|
|
284
|
+
JIT), so tight typed compute gains a small constant factor over the same
|
|
285
|
+
loop in JavaScript, nowhere near browser wasm speed, and every host call
|
|
286
|
+
costs marshalling. Use it to ship one compiled module across every target
|
|
287
|
+
without native binaries, not to speed up per-frame work; for that, rules
|
|
288
|
+
1-2 (move it to the GPU, cut property writes) are the leverage.
|
|
289
|
+
|
|
290
|
+
### Isolates: heavy work off the JS thread
|
|
291
|
+
|
|
292
|
+
A long synchronous computation (a big parse, a simulation step, a blocking
|
|
293
|
+
`flux:ffi`/`flux:wasm` call) freezes rendering and input for its duration.
|
|
294
|
+
Move it into an isolate module: a file whose first statement is the
|
|
295
|
+
`"use isolate"` directive runs in a second runtime on its own thread, and
|
|
296
|
+
main calls its exports as async functions.
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
// src/worker.ts
|
|
300
|
+
"use isolate"
|
|
301
|
+
export function crunch(data: Uint8Array): number { /* ... */ }
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
// src/index.tsx
|
|
306
|
+
import { isolate } from "flux:isolate"
|
|
307
|
+
import type * as Worker from "./worker"
|
|
308
|
+
let worker = isolate<typeof Worker>("worker") // id = path from src/, no extension
|
|
309
|
+
let n = await worker.crunch(bytes) // main keeps rendering meanwhile
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The bundler builds each such module as its own bundle and ships it with the
|
|
313
|
+
app (dev pushes and `srt pack` alike). Rules: main may only `import type`
|
|
314
|
+
from an isolate module (a value import is a build error); arguments and
|
|
315
|
+
results are copies (numbers, strings, byte buffers, arrays, plain objects -
|
|
316
|
+
no functions, no class instances); the child has the non-gui `flux:*`
|
|
317
|
+
modules only, so it never touches the render tree; module state persists
|
|
318
|
+
between calls and each `isolate()` call is its own instance. An
|
|
319
|
+
`async function*` export is a stream: `for await (let p of worker.progress())`
|
|
320
|
+
pulls one item per step (progress, ticks, a subscription), `break` ends it in
|
|
321
|
+
the isolate, and streams never block plain calls. Full contract:
|
|
322
|
+
node_modules/@solidrt/flux-types/modules/isolate.d.ts.
|
|
283
323
|
|
|
284
324
|
### Where GPU work stops being free
|
|
285
325
|
|
|
@@ -346,7 +386,8 @@ up to the frame period, because work outside the frame call is not in them.
|
|
|
346
386
|
(future launcher/window naming) with no storage meaning.
|
|
347
387
|
- `bunx srt pack src/index.tsx` builds a single-file executable;
|
|
348
388
|
`bunx srt pack --folder src/index.tsx` writes the flat app folder
|
|
349
|
-
(runner + manifest.json + bundle + assets
|
|
389
|
+
(runner + manifest.json + bundle + assets/, plus the runner's GL
|
|
390
|
+
libraries on Windows and macOS) to `dist/`.
|
|
350
391
|
|
|
351
392
|
## Run / verify
|
|
352
393
|
|
|
@@ -374,8 +415,11 @@ its tools over guessing at runtime state:
|
|
|
374
415
|
|
|
375
416
|
- list_clients: connected app clients, their platform and runtime
|
|
376
417
|
capabilities, plus the server's `entry` (the app source it serves) and
|
|
377
|
-
`projectDir` - check entry matches the app you think you are driving
|
|
378
|
-
|
|
418
|
+
`projectDir` - check entry matches the app you think you are driving. Each
|
|
419
|
+
tool call finds the dev server currently serving this project (by its
|
|
420
|
+
project root), so a server restarted on another port/session is followed
|
|
421
|
+
automatically; no server serving this project is an error, not a wrong
|
|
422
|
+
server.
|
|
379
423
|
Each client also lists `queries`, the dev-tool query kinds its runtime
|
|
380
424
|
answers - check it before planning verification against a mixed-version
|
|
381
425
|
fleet (no "input" = the client predates send_input)
|
|
@@ -394,7 +438,7 @@ its tools over guessing at runtime state:
|
|
|
394
438
|
re-fetch ids and restart cursors
|
|
395
439
|
- get_stats: fps, CPU/memory, frame phase timings, setProperty rate, plus
|
|
396
440
|
layout-activity counters for the last rebuild (nodes, measureCalls,
|
|
397
|
-
paraShapes, dirtiedNodes, cacheGets/cacheHits) - when layoutMs looks
|
|
441
|
+
paraShapes/wordHits, dirtiedNodes, cacheGets/cacheHits) - when layoutMs looks
|
|
398
442
|
wrong, these say whether the cost is text shaping, invalidation breadth,
|
|
399
443
|
or a defeated layout cache (healthy incremental rebuilds show a near-100%
|
|
400
444
|
cacheHits rate). reusedPerSec/skippedPerSec are the demand gate's visible
|
package/scaffold/package.json
CHANGED
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
"android": "srt client --android"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@solidrt/core": "0.0.
|
|
14
|
-
"@solidrt/components": "0.0.
|
|
13
|
+
"@solidrt/core": "0.0.50",
|
|
14
|
+
"@solidrt/components": "0.0.50",
|
|
15
|
+
"@solidrt/3d": "0.0.50"
|
|
15
16
|
},
|
|
16
17
|
"devDependencies": {
|
|
17
|
-
"@solidrt/cli": "0.0.
|
|
18
|
-
"@solidrt/flux-types": "0.0.
|
|
18
|
+
"@solidrt/cli": "0.0.50",
|
|
19
|
+
"@solidrt/flux-types": "0.0.50",
|
|
19
20
|
"typescript": "^7"
|
|
20
21
|
}
|
|
21
22
|
}
|
package/server/main.ts
CHANGED
|
@@ -74,16 +74,16 @@ async function handleInternal(req: FluxRequest, server: Server, path: string): P
|
|
|
74
74
|
|
|
75
75
|
switch (path) {
|
|
76
76
|
case "/__internal__/reload": {
|
|
77
|
-
// { message, clients?, latch?, sourceDir?,
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
// replaced on every reload
|
|
77
|
+
// { message, clients?, latch?, sourceDir?, map? }: send `message` (a
|
|
78
|
+
// full client-protocol message, built by srt) to the listed client ids,
|
|
79
|
+
// or to all when omitted. `latch` keeps it for late-joining clients
|
|
80
|
+
// (code reloads latch, one-shot bytecode loads do not); `sourceDir`
|
|
81
|
+
// moves the file-serving root (repl `load`; the project root - and with
|
|
82
|
+
// it the /assets/ root - is fixed for the life of the run); `map` is
|
|
83
|
+
// the bundle's sourcemap for log remapping, replaced on every reload
|
|
84
|
+
// (absent means none).
|
|
84
85
|
let body = await req.json()
|
|
85
86
|
if (typeof body.sourceDir === "string") state.sourceDir = body.sourceDir
|
|
86
|
-
if (typeof body.projectDir === "string") state.projectDir = body.projectDir
|
|
87
87
|
// Keep the rebuild entry in sync when `load` moves it, so a later MCP
|
|
88
88
|
// reload bundles the newly loaded file, not the launch-time one.
|
|
89
89
|
if (typeof body.entry === "string") state.config.entry = body.entry
|
|
@@ -211,6 +211,12 @@ serve({
|
|
|
211
211
|
if (path === "/assets" || path.startsWith("/assets/")) {
|
|
212
212
|
return handleFiles(req, path, state.projectDir)
|
|
213
213
|
}
|
|
214
|
+
// Isolate bundles are build outputs, not project files: srt (and the
|
|
215
|
+
// rebuild here) write them under .srt-data/isolates/, and the manifest
|
|
216
|
+
// lists them as isolates/<id>.js.
|
|
217
|
+
if (path.startsWith("/isolates/")) {
|
|
218
|
+
return handleFiles(req, path, config.cacheDir)
|
|
219
|
+
}
|
|
214
220
|
return handleFiles(req, path, state.sourceDir)
|
|
215
221
|
},
|
|
216
222
|
websocket: {
|
package/server/rebuild.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { command } from "flux:subprocess"
|
|
2
|
+
import { dir, file } from "flux:fs"
|
|
2
3
|
import { state } from "./state"
|
|
3
4
|
|
|
4
5
|
// Server-owned "rebuild and push": the single place the running app is rebuilt
|
|
@@ -45,13 +46,20 @@ export async function rebuildAndBroadcast(): Promise<string | null> {
|
|
|
45
46
|
return `Rebuild failed:\n${stderr.trim()}`
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
// bundle-cli writes one JSON object { code, map, manifest } to stdout.
|
|
49
|
-
let bundle: { code?: string; map?: string | null; manifest?: string }
|
|
49
|
+
// bundle-cli writes one JSON object { code, map, manifest, isolates } to stdout.
|
|
50
|
+
let bundle: { code?: string; map?: string | null; manifest?: string; isolates?: { id: string; code: string }[] }
|
|
50
51
|
try {
|
|
51
52
|
bundle = JSON.parse(typeof result.stdout === "string" ? result.stdout : "")
|
|
52
53
|
} catch {
|
|
53
54
|
return "Rebuild failed: unreadable bundler output"
|
|
54
55
|
}
|
|
56
|
+
// Isolate bundles are manifest assets clients fetch from our /isolates/
|
|
57
|
+
// route (served from cacheDir), so they must be on disk before the push.
|
|
58
|
+
for (let isolate of bundle.isolates ?? []) {
|
|
59
|
+
let path = `${config.cacheDir}/isolates/${isolate.id}.js`
|
|
60
|
+
await dir(path.slice(0, path.lastIndexOf("/"))).create()
|
|
61
|
+
await file(path).write(isolate.code)
|
|
62
|
+
}
|
|
55
63
|
state.currentMap = bundle.map ?? null
|
|
56
64
|
let text = JSON.stringify(buildReload(bundle.code ?? "", bundle.manifest))
|
|
57
65
|
state.currentReload = text
|
package/server/state.ts
CHANGED
|
@@ -29,7 +29,7 @@ export type Config = {
|
|
|
29
29
|
cache: boolean
|
|
30
30
|
/** Directory holding the proxy cache db (the project-local .srt-data). */
|
|
31
31
|
cacheDir: string
|
|
32
|
-
/** Directory holding .
|
|
32
|
+
/** Directory holding tunnel.key (the server's ~/.solidrt/servers/<port>/ folder). */
|
|
33
33
|
keyDir: string
|
|
34
34
|
/** Destination for captured key events, or unset when off. */
|
|
35
35
|
capture?: string
|
package/server/tunnel.ts
CHANGED
|
@@ -15,15 +15,16 @@ import { printQr } from "./qr"
|
|
|
15
15
|
// the handshake instead of desyncing.
|
|
16
16
|
export const TUNNEL_PROTOCOL = "solidrt-dev/0"
|
|
17
17
|
|
|
18
|
-
// The persisted identity file,
|
|
19
|
-
// tunnel
|
|
20
|
-
|
|
18
|
+
// The persisted identity file, in the server's ~/.solidrt/servers/<port>/ folder
|
|
19
|
+
// (keyDir): the tunnel identity belongs to the server, not to any project it
|
|
20
|
+
// serves. Delete it to rotate the identity (which invalidates any old ticket).
|
|
21
|
+
const KEY_FILE = "tunnel.key"
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Bind the tunnel endpoint and print its ticket (text + QR). The endpoint is
|
|
24
25
|
* kept stable across restarts so a paired client can re-dial the old ticket
|
|
25
26
|
* without re-scanning: the UDP port is pinned to the dev server's port, and the
|
|
26
|
-
* secret key is persisted in <keyDir
|
|
27
|
+
* secret key is persisted in <keyDir>/tunnel.key (generated on first
|
|
27
28
|
* run). Both are needed - a moving port or a fresh key each start would change
|
|
28
29
|
* the ticket. Stable across restarts on the same network only; a new machine IP
|
|
29
30
|
* still stales the ticket's addresses (that is the discovery/off-LAN story).
|
package/src/args.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseArgs } from "node:util"
|
|
2
2
|
import { resolve } from "node:path"
|
|
3
|
+
import { clientsRoot } from "./dev-dir"
|
|
3
4
|
|
|
4
5
|
// Everything after a bare "--" is the app's argument vector, kept out of
|
|
5
6
|
// parseArgs (which would fold it into positionals) and forwarded verbatim to
|
|
@@ -14,8 +15,9 @@ export let { values, positionals } = parseArgs({
|
|
|
14
15
|
options: {
|
|
15
16
|
dev: { type: "boolean", short: "d", default: false },
|
|
16
17
|
minify: { type: "boolean", short: "m", default: false },
|
|
17
|
-
compile: { type: "boolean",
|
|
18
|
+
compile: { type: "boolean", default: false },
|
|
18
19
|
flux: { type: "boolean", short: "f", default: false },
|
|
20
|
+
session: { type: "string", short: "s" },
|
|
19
21
|
folder: { type: "boolean", default: false },
|
|
20
22
|
stdout: { type: "boolean", default: false },
|
|
21
23
|
output: { type: "string", short: "o" },
|
|
@@ -28,33 +30,51 @@ export let { values, positionals } = parseArgs({
|
|
|
28
30
|
tunnel: { type: "boolean", default: false },
|
|
29
31
|
stats: { type: "boolean", default: false },
|
|
30
32
|
"data-root": { type: "string" },
|
|
31
|
-
client: { type: "string" },
|
|
33
|
+
client: { type: "string", short: "c" },
|
|
32
34
|
server: { type: "string" },
|
|
33
35
|
port: { type: "string" },
|
|
34
36
|
android: { type: "boolean", default: false },
|
|
35
37
|
device: { type: "string" },
|
|
36
|
-
|
|
38
|
+
with: { type: "string" },
|
|
37
39
|
},
|
|
38
40
|
allowPositionals: true,
|
|
39
41
|
})
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
43
|
+
export const DEFAULT_DEV_PORT = 0x8844
|
|
44
|
+
|
|
45
|
+
// The session number: -s/--session <N> selects the dev server (port
|
|
46
|
+
// DEFAULT_DEV_PORT + N, see dev-server.ts resolveDevPort) and the default
|
|
47
|
+
// client slot. Resolved at load, like the port.
|
|
48
|
+
function resolveSession(): number {
|
|
49
|
+
let raw = values.session
|
|
50
|
+
if (raw === undefined) return 0
|
|
51
|
+
let n = Number(raw)
|
|
52
|
+
if (!/^\d+$/.test(raw) || DEFAULT_DEV_PORT + n > 65535) {
|
|
53
|
+
console.error(
|
|
54
|
+
`Invalid --session value "${raw}": expected a non-negative integer with ${DEFAULT_DEV_PORT} + N at most 65535`,
|
|
55
|
+
)
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
return n
|
|
59
|
+
}
|
|
60
|
+
export let session = resolveSession()
|
|
61
|
+
|
|
62
|
+
// Storage flags for a locally spawned client. Dev client trees live in
|
|
63
|
+
// ~/.solidrt/clients/client<M>/, reached through --data-root so the client
|
|
64
|
+
// runtime keeps its single pref-path default rule (see lattice/src/storage.rs
|
|
65
|
+
// and okf/backlog/parallel-dev-servers.md); an explicit --data-root wins. The
|
|
66
|
+
// client slot defaults to the session number, so each session gets its own
|
|
67
|
+
// tree with a single flag. Roots are passed absolute because the client
|
|
68
|
+
// chdirs into its app sandbox at startup.
|
|
46
69
|
export function clientStorageArgs(): string[] {
|
|
47
|
-
let args: string[] = []
|
|
48
70
|
let root = values["data-root"]
|
|
49
|
-
|
|
50
|
-
let client = values.client
|
|
51
|
-
if (client
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
process.exit(1)
|
|
55
|
-
}
|
|
56
|
-
args.push("--client", client)
|
|
71
|
+
let args = ["--data-root", root ? resolve(root) : clientsRoot()]
|
|
72
|
+
let client = values.client ?? String(session)
|
|
73
|
+
if (!/^\d+$/.test(client)) {
|
|
74
|
+
console.error(`Invalid --client value "${client}": expected a non-negative integer`)
|
|
75
|
+
process.exit(1)
|
|
57
76
|
}
|
|
77
|
+
args.push("--client", client)
|
|
58
78
|
return args
|
|
59
79
|
}
|
|
60
80
|
|
|
@@ -111,6 +131,17 @@ export function validateArgs() {
|
|
|
111
131
|
if (values.port !== undefined && command !== "run" && command !== "server" && command !== "mcp") {
|
|
112
132
|
usage("srt <run|server|mcp> --port <N> (--port is only valid with the run, server and mcp commands)")
|
|
113
133
|
}
|
|
134
|
+
// --session selects a dev server (and the default client slot), so it is
|
|
135
|
+
// valid wherever a dev server is started, attached to, or resolved.
|
|
136
|
+
if (
|
|
137
|
+
values.session !== undefined &&
|
|
138
|
+
command !== "run" &&
|
|
139
|
+
command !== "server" &&
|
|
140
|
+
command !== "client" &&
|
|
141
|
+
command !== "mcp"
|
|
142
|
+
) {
|
|
143
|
+
usage("srt <run|server|client|mcp> -s <N> (--session is only valid with the run, server, client and mcp commands)")
|
|
144
|
+
}
|
|
114
145
|
}
|
|
115
146
|
|
|
116
147
|
export function printUsage() {
|
|
@@ -128,10 +159,11 @@ Commands:
|
|
|
128
159
|
mcp MCP server (stdio) exposing the running dev server to coding agents
|
|
129
160
|
|
|
130
161
|
init options:
|
|
131
|
-
|
|
162
|
+
--with <pkg,pkg> Extensions to include, e.g. @solidrt/components,@solidrt/3d (skips the picker)
|
|
132
163
|
|
|
133
164
|
run/server options:
|
|
134
|
-
|
|
165
|
+
-s, --session <N> Session number: dev server on port 34884+N, client slot N (default: 0)
|
|
166
|
+
--port <N> Dev server port (default: 34884 + session)
|
|
135
167
|
--proxy-http Route fetch calls through the dev server (HTTP cache enabled)
|
|
136
168
|
--capture <file> Record connected clients' key events to a script file
|
|
137
169
|
--tunnel Accept ticket-paired clients through the p2p tunnel
|
|
@@ -140,22 +172,25 @@ run/server options:
|
|
|
140
172
|
run/client options:
|
|
141
173
|
--size <WxH> Window size (default: 1280x720)
|
|
142
174
|
--stats Show the debug stats overlay (FPS, memory, frame timings)
|
|
143
|
-
--data-root <dir> Client data root (default:
|
|
144
|
-
|
|
175
|
+
--data-root <dir> Client data root (default: ~/.solidrt/clients)
|
|
176
|
+
-c, --client <N> Client number: its own data tree under the data root (default: the session)
|
|
145
177
|
|
|
146
178
|
client options:
|
|
147
|
-
|
|
179
|
+
-s, --session <N> Connect to this session's dev server on this machine
|
|
180
|
+
(127.0.0.1:34884+N); without it, start on the connect screen
|
|
181
|
+
--server <host[:port]> Connect to a dev server at this address (default port: 34884 + session)
|
|
148
182
|
--android Install and launch the client on a connected Android device
|
|
149
183
|
--device <serial> Target a specific adb device by serial or unique prefix
|
|
150
184
|
|
|
151
185
|
mcp options:
|
|
152
|
-
|
|
186
|
+
-s, --session <N> Attach to the dev server of this session (default: resolve by project)
|
|
187
|
+
--port <N> Port of the dev server to attach to (default: resolve by project)
|
|
153
188
|
|
|
154
189
|
bundle options:
|
|
155
190
|
-f, --flux Bundle for the bare Flux runtime, without SolidJS (entry must be .ts|.js)
|
|
156
191
|
-d, --dev Use development build of SolidJS (default: production)
|
|
157
192
|
-m, --minify Minify the output
|
|
158
|
-
|
|
193
|
+
--compile Compile to bytecode
|
|
159
194
|
-o, --output <name> Output filename
|
|
160
195
|
--stdout Write bundle to stdout
|
|
161
196
|
|
package/src/bundle-cli.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Standalone bundler entry, spawned by the dev server (a flux process) as a
|
|
2
2
|
// Bun subprocess to rebuild the app on an MCP-triggered reload. flux cannot call
|
|
3
3
|
// Bun.build, so the server shells out to this. Params arrive as one JSON
|
|
4
|
-
// argument; one JSON object { code, map } goes to stdout
|
|
5
|
-
// stderr. On a build failure it exits non-zero with an
|
|
4
|
+
// argument; one JSON object { code, map, manifest, isolates } goes to stdout
|
|
5
|
+
// and diagnostics to stderr. On a build failure it exits non-zero with an
|
|
6
|
+
// empty stdout.
|
|
6
7
|
|
|
7
8
|
import { bundleWith, type BundleOptions } from "./bundler"
|
|
8
9
|
|