@solidrt/cli 0.0.22 → 0.0.24
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 +7 -3
- package/package.json +5 -5
- package/scaffold/AGENTS.md +1 -1
- package/scaffold/package.json +4 -4
- package/scaffold/templates/minimal/index.tsx +11 -0
- package/src/args.ts +8 -4
- package/src/commands/render.ts +18 -0
- package/src/commands/server.ts +6 -0
- package/src/dev-server.ts +20 -2
- package/src/main.ts +4 -4
- package/src/util.ts +12 -1
- package/src/commands/record.ts +0 -16
package/AGENTS.md
CHANGED
|
@@ -18,21 +18,25 @@ bundled `flux` runtime, not on Bun. Invoke via `bunx srt <command>`.
|
|
|
18
18
|
- `bunx srt bundle src/index.tsx` - transpile to `<file>.srt.js`. With
|
|
19
19
|
`--compile`, emits `.srt.bin` bytecode. `--minify`, `--dev`, `--stdout`,
|
|
20
20
|
`--output` also available.
|
|
21
|
-
- `bunx srt
|
|
21
|
+
- `bunx srt render src/index.tsx [flags]` - render OFFSCREEN to PNG frames,
|
|
22
|
+
optionally replaying a `--script` file recorded via `--capture`.
|
|
22
23
|
- `bunx srt server [file]` / `bunx srt client` - the two halves of `run`
|
|
23
24
|
separately (server distributes code; clients on other devices connect to it).
|
|
25
|
+
- `bunx srt run src/index.tsx --capture out.script.json` - records keydown/keyup
|
|
26
|
+
from every connected client into one script file (written on client
|
|
27
|
+
disconnect), for replaying later with `render --script`.
|
|
24
28
|
|
|
25
29
|
## Verifying without a display (headless / CI / agent box)
|
|
26
30
|
|
|
27
31
|
Two reliable checks that need no GUI:
|
|
28
32
|
|
|
29
33
|
1. `bunx srt bundle src/index.tsx` - exit 0 means the app compiles. Fast.
|
|
30
|
-
2. `bunx srt
|
|
34
|
+
2. `bunx srt render src/index.tsx --size 480x640 --duration 1 --fps 2` -
|
|
31
35
|
renders offscreen via EGL/wgpu and writes `frame-NNNNNN.png`. This actually
|
|
32
36
|
proves the app renders. Combine with `--fps`/`--duration` (defaults
|
|
33
37
|
1280x720, 60fps, 1s).
|
|
34
38
|
|
|
35
|
-
`
|
|
39
|
+
`render` gotchas:
|
|
36
40
|
- Frames are written to the RUNTIME's working dir (`~/.local/share/SolidRT/go/`),
|
|
37
41
|
NOT the directory you ran the command from. Look there for the PNGs.
|
|
38
42
|
- The recording includes a debug overlay (FPS/REQ/MiB/CPU) in a corner.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Antoine van Wel",
|
|
6
6
|
"type": "module",
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"qrcode-generator": "^2.0.4"
|
|
23
23
|
},
|
|
24
24
|
"optionalDependencies": {
|
|
25
|
-
"@solidrt/darwin-arm64": "0.0.
|
|
26
|
-
"@solidrt/linux-x64-gnu": "0.0.
|
|
27
|
-
"@solidrt/win32-x64-msvc": "0.0.
|
|
25
|
+
"@solidrt/darwin-arm64": "0.0.24",
|
|
26
|
+
"@solidrt/linux-x64-gnu": "0.0.24",
|
|
27
|
+
"@solidrt/win32-x64-msvc": "0.0.24"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@solidrt/core": "0.0.
|
|
30
|
+
"@solidrt/core": "0.0.24",
|
|
31
31
|
"typescript": "^6"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
package/scaffold/AGENTS.md
CHANGED
|
@@ -81,6 +81,6 @@ Authoritative references ship inside the installed packages - read them:
|
|
|
81
81
|
|
|
82
82
|
- bunx srt run src/index.tsx - dev server + window (needs a display)
|
|
83
83
|
- bunx srt bundle src/index.tsx - exit 0 means it compiles
|
|
84
|
-
- bunx srt
|
|
84
|
+
- bunx srt render src/index.tsx --size 480x640 --duration 1 --fps 2 - headless
|
|
85
85
|
render to PNG frames (proves it renders; see the cli AGENTS.md for where the
|
|
86
86
|
frames land)
|
package/scaffold/package.json
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"android": "srt client --android"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@solidrt/core": "0.0.
|
|
13
|
-
"@solidrt/components": "0.0.
|
|
12
|
+
"@solidrt/core": "0.0.24",
|
|
13
|
+
"@solidrt/components": "0.0.24"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"@solidrt/cli": "0.0.
|
|
17
|
-
"@solidrt/flux-types": "0.0.
|
|
16
|
+
"@solidrt/cli": "0.0.24",
|
|
17
|
+
"@solidrt/flux-types": "0.0.24",
|
|
18
18
|
"typescript": "^6"
|
|
19
19
|
}
|
|
20
20
|
}
|
package/src/args.ts
CHANGED
|
@@ -13,6 +13,8 @@ export let { values, positionals } = parseArgs({
|
|
|
13
13
|
fps: { type: "string" },
|
|
14
14
|
duration: { type: "string" },
|
|
15
15
|
size: { type: "string" },
|
|
16
|
+
script: { type: "string" },
|
|
17
|
+
capture: { type: "string" },
|
|
16
18
|
stats: { type: "boolean", default: false },
|
|
17
19
|
android: { type: "boolean", default: false },
|
|
18
20
|
device: { type: "string" },
|
|
@@ -43,8 +45,8 @@ export function validateArgs() {
|
|
|
43
45
|
usage("srt bundle [options] <entry.[tsx|jsx|ts|js|srt.js|srt.bin]>")
|
|
44
46
|
}
|
|
45
47
|
break
|
|
46
|
-
case "
|
|
47
|
-
if (!source || !isTsx) usage("srt
|
|
48
|
+
case "render":
|
|
49
|
+
if (!source || !isTsx) usage("srt render <entry.[tsx|jsx]>")
|
|
48
50
|
break
|
|
49
51
|
case "pack":
|
|
50
52
|
if (values.flux) {
|
|
@@ -71,7 +73,7 @@ Commands:
|
|
|
71
73
|
server [file] Start dev server only
|
|
72
74
|
client Start solidrt-go client only
|
|
73
75
|
bundle <file> Transpile TS/JS/TSX/JSX to JS or bytecode
|
|
74
|
-
|
|
76
|
+
render <file.tsx|jsx> Replay a script (optional) and render frames for video generation
|
|
75
77
|
pack <file> Bundle + compile to a standalone executable (experimental)
|
|
76
78
|
|
|
77
79
|
init options:
|
|
@@ -80,6 +82,7 @@ init options:
|
|
|
80
82
|
run/server options:
|
|
81
83
|
--proxy-files Route file/dir access through the dev server
|
|
82
84
|
--proxy-http Route fetch calls through the dev server (HTTP cache enabled)
|
|
85
|
+
--capture <file> Record connected clients' key events to a script file
|
|
83
86
|
|
|
84
87
|
run/client options:
|
|
85
88
|
--size <WxH> Window size (default: 1280x720)
|
|
@@ -102,7 +105,8 @@ pack options:
|
|
|
102
105
|
-m, --minify Minify the output
|
|
103
106
|
-o, --output <name> Output filename
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
render options:
|
|
109
|
+
--script <file> Script file to replay (default: no scripted input)
|
|
106
110
|
--fps <N> Frames per second (default: 60)
|
|
107
111
|
--duration <N> Duration in seconds (default: 1)
|
|
108
112
|
--size <WxH> Frame size (default: 1280x720)`)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { source, values } from "../args"
|
|
2
|
+
import { requireBinary, run } from "../util"
|
|
3
|
+
import { bundleTo } from "../bundler"
|
|
4
|
+
import { resolve } from "path"
|
|
5
|
+
|
|
6
|
+
export async function runRenderCommand() {
|
|
7
|
+
let jsOutfile = source!.replace(/\.[jt]sx$/, "") + ".srt.js"
|
|
8
|
+
await bundleTo(jsOutfile)
|
|
9
|
+
let runner = requireBinary("solidrt-go")
|
|
10
|
+
let playbackArgs = ["--playback"]
|
|
11
|
+
if (values.fps) playbackArgs.push("--fps", values.fps)
|
|
12
|
+
if (values.duration) playbackArgs.push("--duration", values.duration)
|
|
13
|
+
if (values.size) playbackArgs.push("--size", values.size)
|
|
14
|
+
if (values.script) playbackArgs.push("--script", resolve(values.script))
|
|
15
|
+
playbackArgs.push(resolve(jsOutfile))
|
|
16
|
+
let exit = await run(runner, playbackArgs)
|
|
17
|
+
process.exit(exit)
|
|
18
|
+
}
|
package/src/commands/server.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { startRepl } from "../repl"
|
|
|
7
7
|
import { startWatcher } from "../watcher"
|
|
8
8
|
import * as cache from "../cache"
|
|
9
9
|
import { resolve, dirname } from "path"
|
|
10
|
+
import { writeFileSync } from "node:fs"
|
|
10
11
|
|
|
11
12
|
// Brings up the dev server (HTTP/WS + initial bundle + repl + watcher). The
|
|
12
13
|
// `run` command spawns a local client on top of this from main.ts.
|
|
@@ -15,6 +16,11 @@ export async function runServerCommand() {
|
|
|
15
16
|
state.source = source
|
|
16
17
|
state.sourceDir = source ? dirname(resolve(source)) : process.cwd()
|
|
17
18
|
state.stats = values.stats
|
|
19
|
+
state.capture = values.capture ? resolve(values.capture) : undefined
|
|
20
|
+
state.captureStartMs = Date.now()
|
|
21
|
+
// Start each capture from an empty file: appendFileSync (dev-server.ts)
|
|
22
|
+
// would otherwise tack onto whatever a previous run left behind.
|
|
23
|
+
if (state.capture) writeFileSync(state.capture, "")
|
|
18
24
|
|
|
19
25
|
if (values["proxy-http"]) {
|
|
20
26
|
cache.initCache({ dir: process.cwd() })
|
package/src/dev-server.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve } from "path"
|
|
2
2
|
import { stat as fsStat, readdir } from "node:fs/promises"
|
|
3
|
+
import { appendFileSync } from "node:fs"
|
|
3
4
|
import { networkInterfaces } from "node:os"
|
|
4
5
|
import { Bonjour } from "bonjour-service"
|
|
5
6
|
import qrcode from "qrcode-generator"
|
|
@@ -172,11 +173,14 @@ export function startServer() {
|
|
|
172
173
|
},
|
|
173
174
|
websocket: {
|
|
174
175
|
open(ws) {
|
|
175
|
-
|
|
176
|
+
let id = state.nextClientId++
|
|
177
|
+
state.clients.set(ws, { platform: "unknown", version: "unknown", id })
|
|
176
178
|
print(`[cli] Client connected ${ws.remoteAddress}`)
|
|
177
179
|
// Advertise our real LAN address so clients dialed over the adb loopback
|
|
178
180
|
// can show/remember the directly reachable address (see connection.rs).
|
|
179
|
-
ws.send(
|
|
181
|
+
ws.send(
|
|
182
|
+
JSON.stringify({ type: "welcome", address: state.serverUrl, stats: state.stats, capture: !!state.capture }),
|
|
183
|
+
)
|
|
180
184
|
if (state.currentCode) {
|
|
181
185
|
ws.send(buildReload({ code: state.currentCode }))
|
|
182
186
|
}
|
|
@@ -195,11 +199,25 @@ export function startServer() {
|
|
|
195
199
|
try {
|
|
196
200
|
let data = JSON.parse(typeof msg === "string" ? msg : Buffer.from(msg).toString())
|
|
197
201
|
if (data.type === "info") {
|
|
202
|
+
let existing = state.clients.get(ws)
|
|
198
203
|
state.clients.set(ws, {
|
|
199
204
|
platform: data.platform ?? "unknown",
|
|
200
205
|
version: data.version ?? "unknown",
|
|
206
|
+
id: existing?.id ?? state.nextClientId++,
|
|
201
207
|
})
|
|
202
208
|
print(`[cli] Client info ${ws.remoteAddress} ${data.platform} (${data.version})`)
|
|
209
|
+
} else if (data.type === "capture" && state.capture) {
|
|
210
|
+
let device = state.clients.get(ws)?.id ?? -1
|
|
211
|
+
// Milliseconds, integer: Date.now() is already integer ms, so the
|
|
212
|
+
// delta needs no rounding.
|
|
213
|
+
let at = Date.now() - state.captureStartMs
|
|
214
|
+
let after = at - state.captureLastAt
|
|
215
|
+
state.captureLastAt = at
|
|
216
|
+
// JSON Lines: one event object per line, streamed to disk as it
|
|
217
|
+
// arrives rather than buffered - no in-memory growth for a long
|
|
218
|
+
// capture, and the file is always complete on disk mid-session.
|
|
219
|
+
let line = JSON.stringify({ after, type: data.kind, key: data.key, device }) + "\n"
|
|
220
|
+
appendFileSync(state.capture, line)
|
|
203
221
|
}
|
|
204
222
|
} catch {}
|
|
205
223
|
},
|
package/src/main.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { values, command, validateArgs, printUsage } from "./args"
|
|
|
4
4
|
import { runInitCommand } from "./commands/init"
|
|
5
5
|
import { runBundleCommand } from "./commands/bundle"
|
|
6
6
|
import { runPackCommand } from "./commands/pack"
|
|
7
|
-
import {
|
|
7
|
+
import { runRenderCommand } from "./commands/render"
|
|
8
8
|
import { runServerCommand } from "./commands/server"
|
|
9
9
|
import { runClientCommand } from "./commands/client"
|
|
10
10
|
import { spawnClient } from "./dev-client"
|
|
@@ -22,7 +22,7 @@ validateArgs()
|
|
|
22
22
|
// the auto-activation by setting NODE_ENV=production. Since that is read at startup,
|
|
23
23
|
// we re-exec rather than mutate process.env. Assumes srt runs via bun (argv is
|
|
24
24
|
// [bun, script, ...]); would need rework if ever shipped as a compiled binary.
|
|
25
|
-
let isProdBuild = (command === "bundle" || command === "
|
|
25
|
+
let isProdBuild = (command === "bundle" || command === "render" || command === "pack") && !values.dev
|
|
26
26
|
if (isProdBuild && process.env.NODE_ENV !== "production") {
|
|
27
27
|
let proc = Bun.spawnSync({
|
|
28
28
|
cmd: [process.execPath, ...process.argv.slice(1)],
|
|
@@ -42,8 +42,8 @@ if (command === "init") {
|
|
|
42
42
|
await runBundleCommand()
|
|
43
43
|
} else if (command === "pack") {
|
|
44
44
|
await runPackCommand()
|
|
45
|
-
} else if (command === "
|
|
46
|
-
await
|
|
45
|
+
} else if (command === "render") {
|
|
46
|
+
await runRenderCommand()
|
|
47
47
|
} else if (command === "client") {
|
|
48
48
|
await runClientCommand()
|
|
49
49
|
} else if (command === "server") {
|
package/src/util.ts
CHANGED
|
@@ -6,7 +6,8 @@ import type { Server as BunServer } from "bun"
|
|
|
6
6
|
import type { Bonjour } from "bonjour-service"
|
|
7
7
|
|
|
8
8
|
export let state = {
|
|
9
|
-
clients: new Map<any, { platform: string; version: string }>(),
|
|
9
|
+
clients: new Map<any, { platform: string; version: string; id: number }>(),
|
|
10
|
+
nextClientId: 0,
|
|
10
11
|
currentCode: null as string | null,
|
|
11
12
|
source: undefined as string | undefined,
|
|
12
13
|
sourceDir: process.cwd(),
|
|
@@ -16,6 +17,16 @@ export let state = {
|
|
|
16
17
|
rl: null as ReadlineInterface | null,
|
|
17
18
|
bonjour: null as Bonjour | null,
|
|
18
19
|
stats: false,
|
|
20
|
+
// --capture <file>: destination for captured key events, or undefined when
|
|
21
|
+
// off. Clients only report kind/key; the server stamps `after` itself (one
|
|
22
|
+
// shared clock from captureStartMs, integer milliseconds) so events from
|
|
23
|
+
// several connected clients merge into one coherent timeline, tagged by
|
|
24
|
+
// `device` (see dev-server.ts) so they can be told apart. Streamed to disk
|
|
25
|
+
// as JSON Lines (one event object per line) as each arrives - see
|
|
26
|
+
// dev-server.ts's "capture" message handling.
|
|
27
|
+
capture: undefined as string | undefined,
|
|
28
|
+
captureStartMs: 0,
|
|
29
|
+
captureLastAt: 0, // ms, same clock as captureStartMs
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
// Build target per binary, for the "not found" hint. Run from the repo root.
|
package/src/commands/record.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { source, values } from "../args"
|
|
2
|
-
import { requireBinary, run } from "../util"
|
|
3
|
-
import { bundleTo } from "../bundler"
|
|
4
|
-
import { resolve } from "path"
|
|
5
|
-
|
|
6
|
-
export async function runRecordCommand() {
|
|
7
|
-
let jsOutfile = source!.replace(/\.[jt]sx$/, "") + ".srt.js"
|
|
8
|
-
await bundleTo(jsOutfile)
|
|
9
|
-
let runner = requireBinary("solidrt-go")
|
|
10
|
-
let recordArgs = ["--record", resolve(jsOutfile)]
|
|
11
|
-
if (values.fps) recordArgs.push("--fps", values.fps)
|
|
12
|
-
if (values.duration) recordArgs.push("--duration", values.duration)
|
|
13
|
-
if (values.size) recordArgs.push("--size", values.size)
|
|
14
|
-
let exit = await run(runner, recordArgs)
|
|
15
|
-
process.exit(exit)
|
|
16
|
-
}
|