rnxsim 0.1.313 → 0.1.314
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/LICENSE +11 -10
- package/README.md +5 -0
- package/cli/app-config.ts +65 -0
- package/cli/app-fonts.ts +408 -0
- package/cli/app-project.ts +231 -0
- package/cli/app-splash.ts +185 -0
- package/cli/app-state-reset.ts +24 -0
- package/cli/auth.ts +155 -0
- package/cli/bin.ts +594 -0
- package/cli/bridge-diagnostics.ts +226 -0
- package/cli/bridge-flow-runner.ts +2830 -0
- package/cli/browser-evals.ts +96 -0
- package/cli/commands/agent-wrapper.ts +986 -0
- package/cli/commands/agent.ts +423 -0
- package/cli/commands/app-fonts.ts +98 -0
- package/cli/commands/assert.ts +541 -0
- package/cli/commands/auth.ts +59 -0
- package/cli/commands/camera.ts +266 -0
- package/cli/commands/cleanup.ts +169 -0
- package/cli/commands/compat.ts +87 -0
- package/cli/commands/config.ts +32 -0
- package/cli/commands/control.ts +2142 -0
- package/cli/commands/cpu-profile.ts +269 -0
- package/cli/commands/daemon-mac-app.ts +169 -0
- package/cli/commands/daemon.ts +874 -0
- package/cli/commands/debug.ts +719 -0
- package/cli/commands/desktop.ts +39 -0
- package/cli/commands/detect.ts +197 -0
- package/cli/commands/detox.ts +385 -0
- package/cli/commands/device.ts +133 -0
- package/cli/commands/diagnose.ts +589 -0
- package/cli/commands/electron.ts +95 -0
- package/cli/commands/film.ts +379 -0
- package/cli/commands/flow.ts +1124 -0
- package/cli/commands/inspect/actions.ts +622 -0
- package/cli/commands/inspect/core.ts +2405 -0
- package/cli/commands/inspect/count.ts +17 -0
- package/cli/commands/inspect/describe.ts +192 -0
- package/cli/commands/inspect/env.ts +23 -0
- package/cli/commands/inspect/find.ts +171 -0
- package/cli/commands/inspect/get-layout.ts +39 -0
- package/cli/commands/inspect/keyboard.ts +52 -0
- package/cli/commands/inspect/list.ts +58 -0
- package/cli/commands/inspect/memory.ts +215 -0
- package/cli/commands/inspect/redaction.ts +39 -0
- package/cli/commands/inspect/resolve-target.ts +82 -0
- package/cli/commands/inspect/screens.ts +78 -0
- package/cli/commands/inspect/settle.ts +22 -0
- package/cli/commands/inspect/settling.ts +158 -0
- package/cli/commands/inspect/shared.ts +353 -0
- package/cli/commands/inspect/sleep.ts +14 -0
- package/cli/commands/inspect/tree.ts +32 -0
- package/cli/commands/inspect/url.ts +17 -0
- package/cli/commands/inspect/wait-event.ts +210 -0
- package/cli/commands/inspect/wait-idle.ts +24 -0
- package/cli/commands/inspect/wait-ready.ts +74 -0
- package/cli/commands/inspect/wait-selector.ts +54 -0
- package/cli/commands/inspect/wait.ts +31 -0
- package/cli/commands/inspect.ts +4519 -0
- package/cli/commands/install-desktop.ts +351 -0
- package/cli/commands/login.ts +331 -0
- package/cli/commands/logout.ts +31 -0
- package/cli/commands/maestro-generate.ts +361 -0
- package/cli/commands/maestro.ts +453 -0
- package/cli/commands/mode.ts +57 -0
- package/cli/commands/no-bridge-hint.ts +80 -0
- package/cli/commands/perf.ts +66 -0
- package/cli/commands/permissions.ts +203 -0
- package/cli/commands/profile.ts +108 -0
- package/cli/commands/react.ts +353 -0
- package/cli/commands/record.ts +1434 -0
- package/cli/commands/report-issue.ts +305 -0
- package/cli/commands/reset.ts +85 -0
- package/cli/commands/runtime.ts +351 -0
- package/cli/commands/screenshot-command.ts +106 -0
- package/cli/commands/screenshot-layers.ts +143 -0
- package/cli/commands/screenshot-mode.ts +37 -0
- package/cli/commands/screenshot.ts +488 -0
- package/cli/commands/screenshots-capture.ts +607 -0
- package/cli/commands/screenshots.ts +127 -0
- package/cli/commands/serve.ts +168 -0
- package/cli/commands/setup.ts +545 -0
- package/cli/commands/shell-boolean-mode.ts +81 -0
- package/cli/commands/skills.ts +467 -0
- package/cli/commands/slides.ts +361 -0
- package/cli/commands/state.ts +87 -0
- package/cli/commands/storage.ts +58 -0
- package/cli/commands/telemetry.ts +54 -0
- package/cli/commands/three-mode.ts +763 -0
- package/cli/commands/timeline.ts +122 -0
- package/cli/commands/upgrade.ts +208 -0
- package/cli/commands/upload.ts +1225 -0
- package/cli/commands/version.ts +54 -0
- package/cli/commands/what-happened.ts +327 -0
- package/cli/current-sim.ts +204 -0
- package/cli/desktop-companion.ts +300 -0
- package/cli/drivers/electron.ts +70 -0
- package/cli/drivers/index.ts +20 -0
- package/cli/drivers/playwright-provisioning.ts +180 -0
- package/cli/drivers/playwright.ts +698 -0
- package/cli/drivers/registry.ts +65 -0
- package/cli/drivers/types.ts +102 -0
- package/cli/flow-file.ts +142 -0
- package/cli/flow-live-status.ts +120 -0
- package/cli/flow-session.ts +187 -0
- package/cli/help.ts +80 -0
- package/cli/hidden-runtime-alias.ts +19 -0
- package/cli/hints.ts +216 -0
- package/cli/inspect-notice-state.ts +114 -0
- package/cli/maestro-js.ts +334 -0
- package/cli/open-url.ts +8 -0
- package/cli/parent-pid.ts +204 -0
- package/cli/parse-args.ts +211 -0
- package/cli/prompt.ts +51 -0
- package/cli/recording-access.ts +107 -0
- package/cli/registry.ts +1 -0
- package/cli/resolve-assets.ts +63 -0
- package/cli/run-registry.ts +226 -0
- package/cli/runtime-notes.ts +66 -0
- package/cli/runtime-summary.ts +25 -0
- package/cli/setup-repository.ts +187 -0
- package/cli/telemetry.ts +187 -0
- package/cli/ws-bridge.ts +798 -0
- package/dist-cli/bin.js +5 -5
- package/dist-cli/chunks/{agent-XZ2KTPCU.js → agent-7YBDCYMA.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-JJYYW2WH.js → agent-wrapper-2GHFBHCR.js} +2 -2
- package/dist-cli/chunks/{app-fonts-IXRNQG6B.js → app-fonts-RSNVPQSU.js} +2 -2
- package/dist-cli/chunks/{assert-54T5SK5F.js → assert-XCMX3XJX.js} +2 -2
- package/dist-cli/chunks/{auth-FI5UDI45.js → auth-B442HRAX.js} +2 -2
- package/dist-cli/chunks/{beta-JV6UKADW.js → beta-XJ55JK3M.js} +2 -2
- package/dist-cli/chunks/camera-UGYSSLIK.js +33 -0
- package/dist-cli/chunks/{chunk-3NV2NCNX.js → chunk-277AEQZX.js} +2 -2
- package/dist-cli/chunks/{chunk-2YR5BGA5.js → chunk-2JNSK774.js} +2 -2
- package/dist-cli/chunks/{chunk-RSZWCKNT.js → chunk-32WOTSTR.js} +3 -3
- package/dist-cli/chunks/{chunk-WEXDAC74.js → chunk-3S753SNQ.js} +2 -2
- package/dist-cli/chunks/{chunk-IJO63TDP.js → chunk-46ZOLOYA.js} +2 -2
- package/dist-cli/chunks/{chunk-WUSWBCWA.js → chunk-5L7ELDQL.js} +8 -9
- package/dist-cli/chunks/{chunk-WF3T4SVI.js → chunk-7OOPFSQS.js} +2 -2
- package/dist-cli/chunks/{chunk-TZFFR3SD.js → chunk-7SV3RPRW.js} +2 -2
- package/dist-cli/chunks/{chunk-WWZIXIRD.js → chunk-7ZC35MOU.js} +1 -1
- package/dist-cli/chunks/chunk-AMG5E6CC.js +9 -0
- package/dist-cli/chunks/{chunk-WINYQ44O.js → chunk-APWNH3A4.js} +1 -1
- package/dist-cli/chunks/chunk-B57XUKY3.js +4 -0
- package/dist-cli/chunks/{chunk-YDGQTMQL.js → chunk-C2NL26TD.js} +1 -1
- package/dist-cli/chunks/{chunk-NMF2ZMZQ.js → chunk-D2FNUWAB.js} +4 -4
- package/dist-cli/chunks/{chunk-YIFT42WN.js → chunk-E5T4XSJ3.js} +2 -2
- package/dist-cli/chunks/{chunk-46EUUFJ5.js → chunk-EAC34EQS.js} +1 -1
- package/dist-cli/chunks/{chunk-OVFJFXUD.js → chunk-EG32ML36.js} +2 -2
- package/dist-cli/chunks/{chunk-5YJCOWCH.js → chunk-FXUAC6D5.js} +1 -1
- package/dist-cli/chunks/chunk-GQSL4USA.js +6 -0
- package/dist-cli/chunks/{chunk-VFCMSYZK.js → chunk-HAXW27SS.js} +2 -2
- package/dist-cli/chunks/{chunk-2D2UPBBR.js → chunk-IZAHPAN6.js} +1 -1
- package/dist-cli/chunks/{chunk-7GN3LVWB.js → chunk-J62KM5TB.js} +2 -2
- package/dist-cli/chunks/{chunk-BTWORNNG.js → chunk-JEMCD5E4.js} +1 -1
- package/dist-cli/chunks/{chunk-DCEMHR2Y.js → chunk-JZS3Q37N.js} +2 -2
- package/dist-cli/chunks/{chunk-D4FFVGI5.js → chunk-KR4JON7D.js} +1 -1
- package/dist-cli/chunks/chunk-KWCYKQIQ.js +4 -0
- package/dist-cli/chunks/{chunk-VNQEB4L7.js → chunk-MCWPL644.js} +2 -2
- package/dist-cli/chunks/chunk-MJYK3N2I.js +4 -0
- package/dist-cli/chunks/{chunk-5TEF3ET3.js → chunk-O6TRIZNS.js} +2 -2
- package/dist-cli/chunks/{chunk-QKDWYITG.js → chunk-P7XL2E73.js} +3 -3
- package/dist-cli/chunks/{chunk-BBULZ7CG.js → chunk-PFQTUKQ4.js} +62 -87
- package/dist-cli/chunks/{chunk-GGRX24GF.js → chunk-PG5RZCTN.js} +2 -2
- package/dist-cli/chunks/{chunk-W6K4EFPH.js → chunk-QGRI2Z4M.js} +2 -2
- package/dist-cli/chunks/{chunk-ZMJD5GEC.js → chunk-QLXXE7GE.js} +1 -1
- package/dist-cli/chunks/{chunk-VZXWHRUZ.js → chunk-QOJJJWJE.js} +89 -133
- package/dist-cli/chunks/{chunk-OZSSI4WN.js → chunk-RWZY5427.js} +2 -2
- package/dist-cli/chunks/{chunk-UC6U3MML.js → chunk-RZKU2K3J.js} +2 -2
- package/dist-cli/chunks/{chunk-IJ5CAZZC.js → chunk-SBV4IK4H.js} +1 -1
- package/dist-cli/chunks/{chunk-DZS6WPUI.js → chunk-SGMVFFMK.js} +1 -1
- package/dist-cli/chunks/{chunk-OHAZNXLK.js → chunk-TAX4UT2N.js} +1 -1
- package/dist-cli/chunks/{chunk-5DHC6KHQ.js → chunk-TUOFAWXT.js} +1 -1
- package/dist-cli/chunks/{chunk-GASE6UBA.js → chunk-UHZLOHGP.js} +1 -1
- package/dist-cli/chunks/{chunk-F5ZRSS3C.js → chunk-VC7V76U3.js} +1 -1
- package/dist-cli/chunks/{chunk-RTN5C5RL.js → chunk-VQVMLW4U.js} +1 -1
- package/dist-cli/chunks/{chunk-WMIIKMGK.js → chunk-VUKKYPZN.js} +2 -2
- package/dist-cli/chunks/{chunk-5TPRP5QT.js → chunk-WGGRJDRE.js} +1 -1
- package/dist-cli/chunks/chunk-X6H76EKP.js +15 -0
- package/dist-cli/chunks/chunk-XLZ5FNRT.js +27 -0
- package/dist-cli/chunks/{chunk-HI5TFJWN.js → chunk-XULEACM4.js} +2 -2
- package/dist-cli/chunks/{chunk-MJRLLB4R.js → chunk-YFSDM7AX.js} +4 -4
- package/dist-cli/chunks/chunk-YWI3UEVX.js +5 -0
- package/dist-cli/chunks/{chunk-XEVZYVIW.js → chunk-ZBSJO4NB.js} +10 -9
- package/dist-cli/chunks/{cleanup-P27PA6JI.js → cleanup-EYLGCA6Z.js} +2 -2
- package/dist-cli/chunks/cli-version-LL2UGIHE.js +4 -0
- package/dist-cli/chunks/{compat-ZD65FED3.js → compat-TLJYHB4E.js} +2 -2
- package/dist-cli/chunks/{config-XMJRNM2A.js → config-4JWUOEXK.js} +2 -2
- package/dist-cli/chunks/{control-KMIQT3QP.js → control-HPAOYF4N.js} +2 -2
- package/dist-cli/chunks/daemon-OYLASXLE.js +4 -0
- package/dist-cli/chunks/{debug-PT4HOP7N.js → debug-4BKXF6KI.js} +5 -5
- package/dist-cli/chunks/{desktop-S3FG72AK.js → desktop-ZQ6ZD2S6.js} +3 -3
- package/dist-cli/chunks/{detox-B3D4IFCN.js → detox-WNPASSS3.js} +2 -2
- package/dist-cli/chunks/{device-XBNDSB2R.js → device-BXLXVG3V.js} +2 -2
- package/dist-cli/chunks/{diagnose-HMQXJE5N.js → diagnose-3QX7W5RW.js} +2 -2
- package/dist-cli/chunks/{disk-cleanup-BLCZ5BSZ.js → disk-cleanup-KSWKI7WB.js} +2 -2
- package/dist-cli/chunks/drivers-EPIEFF7P.js +4 -0
- package/dist-cli/chunks/{film-BJGTBYZB.js → film-GDMR33VO.js} +3 -3
- package/dist-cli/chunks/flow-NKMPBYCJ.js +4 -0
- package/dist-cli/chunks/help-OGCBHOKA.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-ANOYADHM.js → hidden-runtime-alias-S2GTDX3T.js} +2 -2
- package/dist-cli/chunks/home-paths-QRCDLTTV.js +4 -0
- package/dist-cli/chunks/inspect-FUYMOZPY.js +4 -0
- package/dist-cli/chunks/install-desktop-FR6YKW7Y.js +4 -0
- package/dist-cli/chunks/{login-FJ737MWG.js → login-IJAPUZHI.js} +4 -4
- package/dist-cli/chunks/{logout-ZCNMMHMY.js → logout-QMXDFU2X.js} +2 -2
- package/dist-cli/chunks/{maestro-SZTNKLDF.js → maestro-ZXU3YCVX.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-DCFAIZ4H.js → maestro-generate-PYB5QY7K.js} +3 -3
- package/dist-cli/chunks/{mode-GRMQCRXR.js → mode-WGUCL5FZ.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-W36EWFFB.js → optional-demo-registry-WH2O6H36.js} +2 -2
- package/dist-cli/chunks/{perf-QYBAAUZG.js → perf-TOD3UFAH.js} +2 -2
- package/dist-cli/chunks/{permissions-3QCQ6VF4.js → permissions-I5BRJGTB.js} +2 -2
- package/dist-cli/chunks/{record-QPPC2S4E.js → record-ZYL2FYSK.js} +3 -3
- package/dist-cli/chunks/{report-issue-7NMFP4HK.js → report-issue-TAI6DYZO.js} +2 -2
- package/dist-cli/chunks/reset-7YGYKQPQ.js +4 -0
- package/dist-cli/chunks/runtime-B4JO6QRI.js +4 -0
- package/dist-cli/chunks/{screenshot-command-67AECJFB.js → screenshot-command-7ANLODZY.js} +7 -7
- package/dist-cli/chunks/{screenshot-layers-ASWBYPJL.js → screenshot-layers-A7FYXSVU.js} +3 -3
- package/dist-cli/chunks/{screenshots-capture-PXA3HFQK.js → screenshots-capture-WT2ZY6CB.js} +2 -2
- package/dist-cli/chunks/serve-TG5WKA4V.js +44 -0
- package/dist-cli/chunks/{setup-7DWPMRSB.js → setup-PCWLD22V.js} +2 -2
- package/dist-cli/chunks/{skills-S3Y22TUA.js → skills-27ZHWCQS.js} +2 -2
- package/dist-cli/chunks/state-ZQVY46ZO.js +14 -0
- package/dist-cli/chunks/{storage-XUIMJWAJ.js → storage-OYC57C4X.js} +6 -6
- package/dist-cli/chunks/store-32HSPZHI.js +4 -0
- package/dist-cli/chunks/telemetry-PBJR7XHR.js +4 -0
- package/dist-cli/chunks/{timeline-TMPLQPSP.js → timeline-RAJJFQT6.js} +2 -2
- package/dist-cli/chunks/{upgrade-7HDSIM7K.js → upgrade-OEFNZRIP.js} +2 -2
- package/dist-cli/chunks/upload-LQJITLKK.js +4 -0
- package/dist-cli/chunks/version-NIXTY6PL.js +6 -0
- package/dist-cli/chunks/{web-DG3WBYD3.js → web-XBUTBVGR.js} +2 -2
- package/dist-cli/chunks/{what-happened-XFVUTZR7.js → what-happened-YNHRFUDX.js} +3 -3
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract.cjs +20 -0
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +67 -28
- package/dist-lib/host/bridge-host.cjs +140 -12
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +2815 -40
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +2549 -2061
- package/dist-lib/sdk.mjs +2543 -2061
- package/dist-lib/skills.cjs +480 -280
- package/dist-lib/vite.cjs +1 -1
- package/package.json +8 -2
- package/src/bridge-constants.ts +3 -4
- package/src/bridge-contract.ts +251 -0
- package/src/connect.ts +83 -0
- package/src/disk-cleanup.ts +30 -0
- package/src/home-paths.ts +81 -38
- package/src/host/bridge-host.ts +134 -6
- package/src/index.ts +27 -1
- package/src/sdk.ts +8 -0
- package/src/sim-client.ts +660 -0
- package/dist-cli/chunks/camera-VL73YIKP.js +0 -22
- package/dist-cli/chunks/chunk-4NPPOV2N.js +0 -5
- package/dist-cli/chunks/chunk-FSUYIVJ6.js +0 -9
- package/dist-cli/chunks/chunk-G2WW6L2C.js +0 -23
- package/dist-cli/chunks/chunk-KTHV3RUS.js +0 -26
- package/dist-cli/chunks/chunk-LF2ZVT7O.js +0 -6
- package/dist-cli/chunks/chunk-NFK7T35W.js +0 -4
- package/dist-cli/chunks/chunk-TIVZIMMW.js +0 -4
- package/dist-cli/chunks/cli-version-WWLPBDQ7.js +0 -4
- package/dist-cli/chunks/daemon-G2ME7NLB.js +0 -4
- package/dist-cli/chunks/drivers-LDECZGP2.js +0 -4
- package/dist-cli/chunks/flow-UEQNVTU7.js +0 -4
- package/dist-cli/chunks/help-T5FYSVGB.js +0 -4
- package/dist-cli/chunks/home-paths-GT3LFNOR.js +0 -4
- package/dist-cli/chunks/inspect-ZA6XF5LD.js +0 -4
- package/dist-cli/chunks/install-desktop-TIMUDHPL.js +0 -4
- package/dist-cli/chunks/runtime-XOAXMSTU.js +0 -4
- package/dist-cli/chunks/serve-BI2NBAXG.js +0 -44
- package/dist-cli/chunks/store-JTHEJLAZ.js +0 -4
- package/dist-cli/chunks/telemetry-ZYJGD2DB.js +0 -4
- package/dist-cli/chunks/upload-GMSZPWM6.js +0 -4
- package/dist-cli/chunks/version-HOCHZ37L.js +0 -6
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
// sootsim film — record a flow on the flat canvas, then re-play that
|
|
2
|
+
// recording on the 3d device stage under a camera cinematic and capture the
|
|
3
|
+
// stage as video.
|
|
4
|
+
//
|
|
5
|
+
// running a flow inside live 3d mode is too slow to look good (the guest
|
|
6
|
+
// app, the shell, and the WebGL stage all compete for the same cores), so
|
|
7
|
+
// filming is two-phase: the flow records at full speed on the flat canvas
|
|
8
|
+
// via the engine recording store, then the 3d recording editor plays the
|
|
9
|
+
// webm back on the phone screen while only the stage renders. the stage
|
|
10
|
+
// canvas is captured with MediaRecorder (WebGL content only — the editor's
|
|
11
|
+
// DOM dock never appears in the capture).
|
|
12
|
+
|
|
13
|
+
import { spawnSync } from 'node:child_process'
|
|
14
|
+
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
15
|
+
import { homedir } from 'node:os'
|
|
16
|
+
import { dirname, extname, join, resolve } from 'node:path'
|
|
17
|
+
import {
|
|
18
|
+
callInBridgeWrite,
|
|
19
|
+
createBridgeFromParsed,
|
|
20
|
+
parseBridgeCliArgs,
|
|
21
|
+
type WsBridge,
|
|
22
|
+
} from '../ws-bridge'
|
|
23
|
+
import { buildConfigureOptions, waitForThreeModeRuntimeReadyOnBridge } from './three-mode'
|
|
24
|
+
|
|
25
|
+
interface FilmOptions {
|
|
26
|
+
port?: number
|
|
27
|
+
verbose?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
|
31
|
+
|
|
32
|
+
// flags consumed by film itself; everything else that buildConfigureOptions
|
|
33
|
+
// understands (--background, --colorway, --env-*, --room, --focus*, ...)
|
|
34
|
+
// passes through to the stage configure call.
|
|
35
|
+
const FILM_VALUE_FLAGS = ['--out', '--fps', '--script', '--slow']
|
|
36
|
+
const FILM_BOOLEAN_FLAGS = ['--keep-three']
|
|
37
|
+
|
|
38
|
+
const CONFIGURE_VALUE_FLAGS = [
|
|
39
|
+
'--background',
|
|
40
|
+
'--gradient',
|
|
41
|
+
'--gradient-angle',
|
|
42
|
+
'--colorway',
|
|
43
|
+
'--environment',
|
|
44
|
+
'--env-shape',
|
|
45
|
+
'--env-spread',
|
|
46
|
+
'--env-size',
|
|
47
|
+
'--env-color',
|
|
48
|
+
'--env-glow',
|
|
49
|
+
'--env-plastic',
|
|
50
|
+
'--room',
|
|
51
|
+
'--focus',
|
|
52
|
+
'--focus-amount',
|
|
53
|
+
'--focus-range',
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
const MAX_FILM_MS = 120_000
|
|
57
|
+
const STAGE_BITRATE = 16_000_000
|
|
58
|
+
|
|
59
|
+
function flagValue(args: string[], flag: string): string | undefined {
|
|
60
|
+
const i = args.indexOf(flag)
|
|
61
|
+
return i >= 0 ? args[i + 1] : undefined
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function evalInPage<T>(bridge: WsBridge, code: string, timeoutMs = 30_000) {
|
|
65
|
+
return (await bridge.send({ type: 'evaluate', code }, { timeoutMs })) as T
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function runFilm(args: string[], opts: FilmOptions): Promise<number> {
|
|
69
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
70
|
+
console.log(`
|
|
71
|
+
rnxsim film — record a flow flat, replay it on the 3d stage, save a clip
|
|
72
|
+
|
|
73
|
+
usage:
|
|
74
|
+
rnxsim film <flow.yaml> [options]
|
|
75
|
+
|
|
76
|
+
how it works:
|
|
77
|
+
1. the flow runs at full speed on the flat canvas while the engine
|
|
78
|
+
recording store captures the device screen
|
|
79
|
+
2. 3d mode + the recording editor come up and play that capture back on
|
|
80
|
+
the phone screen while a camera script animates
|
|
81
|
+
3. the WebGL stage canvas is recorded and saved
|
|
82
|
+
|
|
83
|
+
options:
|
|
84
|
+
--out <path> output file (.webm, or .mp4 when ffmpeg is
|
|
85
|
+
installed). default: ~/Desktop/sootsim-films/<flow>.webm
|
|
86
|
+
--script <id> camera script to animate during playback
|
|
87
|
+
(default: hero-arc)
|
|
88
|
+
--fps <n> stage capture framerate (default: 30)
|
|
89
|
+
--slow <ms> per-step delay forwarded to the flow runner
|
|
90
|
+
--keep-three leave 3d mode + the recording editor on afterwards
|
|
91
|
+
--sim <id> target a specific connected sim
|
|
92
|
+
|
|
93
|
+
stage decor (same flags as \`rnxsim mode three configure\`):
|
|
94
|
+
--background <id> · --gradient <stops> · --gradient-angle <deg>
|
|
95
|
+
--colorway <id> · --room on|off · --focus on|off · --focus-amount <id>
|
|
96
|
+
--focus-range <id> · --environment on|off · --env-shape/spread/size/color
|
|
97
|
+
--env-glow on|off · --env-plastic on|off
|
|
98
|
+
|
|
99
|
+
examples:
|
|
100
|
+
rnxsim film flows/feed-scroll.yaml
|
|
101
|
+
rnxsim film flows/swap.yaml --script hero-arc --background gradient-tide --out ~/Desktop/swap-3d.mp4
|
|
102
|
+
rnxsim film flows/login.yaml --colorway deep-blue --environment on --env-shape pane --room on
|
|
103
|
+
`)
|
|
104
|
+
return 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const flowPath = args.find((a) => !a.startsWith('--') && /\.ya?ml$/.test(a))
|
|
108
|
+
if (!flowPath || !existsSync(resolve(flowPath))) {
|
|
109
|
+
console.error(` film: flow file not found: ${flowPath ?? '(none given)'}`)
|
|
110
|
+
return 1
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const fps = Number.parseInt(flagValue(args, '--fps') ?? '30', 10)
|
|
114
|
+
const scriptId = flagValue(args, '--script') ?? 'hero-arc'
|
|
115
|
+
const keepThree = args.includes('--keep-three')
|
|
116
|
+
const flowName = flowPath.replace(/^.*\//, '').replace(/\.ya?ml$/, '')
|
|
117
|
+
const outArg = flagValue(args, '--out')
|
|
118
|
+
const outPath = resolve(
|
|
119
|
+
outArg ?? join(homedir(), 'Desktop', 'sootsim-films', `${flowName}.webm`),
|
|
120
|
+
)
|
|
121
|
+
const wantsMp4 = extname(outPath).toLowerCase() === '.mp4'
|
|
122
|
+
if (wantsMp4 && spawnSync('ffmpeg', ['-version']).status !== 0) {
|
|
123
|
+
console.error(' film: --out ends in .mp4 but ffmpeg is not installed')
|
|
124
|
+
console.error(' install ffmpeg or use a .webm output path')
|
|
125
|
+
return 1
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const parsed = parseBridgeCliArgs(args, {
|
|
129
|
+
port: opts.port,
|
|
130
|
+
stripValueFlags: [...FILM_VALUE_FLAGS, ...CONFIGURE_VALUE_FLAGS],
|
|
131
|
+
stripBooleanFlags: FILM_BOOLEAN_FLAGS,
|
|
132
|
+
})
|
|
133
|
+
const bridge = createBridgeFromParsed({ ...parsed, commandTimeoutMs: 60_000 })
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// remember the shell state we mutate so a plain run is side-effect free
|
|
137
|
+
const before = await evalInPage<{ threeMode?: boolean; editor?: boolean }>(
|
|
138
|
+
bridge,
|
|
139
|
+
`(() => { const s = window.SootSim?.bridges?.settings?.get?.() || {}
|
|
140
|
+
return { threeMode: s.threeMode === true, editor: s.threeRecordingEditor === true } })()`,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
// -- phase 1: record the flow on the flat canvas ----------------------
|
|
144
|
+
console.log(` film: recording flow ${flowName} on the flat canvas`)
|
|
145
|
+
const started = await evalInPage<boolean>(
|
|
146
|
+
bridge,
|
|
147
|
+
`(async () => !!(await window.SootSim?.bridges?.startRecording?.('video', undefined, { skipEntitlement: true })))()`,
|
|
148
|
+
)
|
|
149
|
+
if (!started) {
|
|
150
|
+
console.error(
|
|
151
|
+
' film: engine recording store did not start (is @sootsim/plugin-recording installed?)',
|
|
152
|
+
)
|
|
153
|
+
return 1
|
|
154
|
+
}
|
|
155
|
+
const t0 = Date.now()
|
|
156
|
+
await sleep(500)
|
|
157
|
+
|
|
158
|
+
const { runFlowPlayback } = await import('./flow')
|
|
159
|
+
const playbackArgs = [flowPath]
|
|
160
|
+
const simFlag = flagValue(args, '--sim')
|
|
161
|
+
if (simFlag) playbackArgs.push('--sim', simFlag)
|
|
162
|
+
const slowFlag = flagValue(args, '--slow')
|
|
163
|
+
if (slowFlag) playbackArgs.push('--slow', slowFlag)
|
|
164
|
+
const flowCode = await runFlowPlayback(playbackArgs)
|
|
165
|
+
await sleep(400)
|
|
166
|
+
const durationMs = Math.min(Date.now() - t0, MAX_FILM_MS)
|
|
167
|
+
await evalInPage(bridge, `window.SootSim?.bridges?.stopRecording?.()`)
|
|
168
|
+
if (flowCode !== 0) {
|
|
169
|
+
console.error(` film: flow failed (exit ${flowCode}) — not filming a broken take`)
|
|
170
|
+
return flowCode
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let hasBlob = false
|
|
174
|
+
for (let i = 0; i < 50; i++) {
|
|
175
|
+
await sleep(400)
|
|
176
|
+
const st = await evalInPage<{ state?: string; hasBlob?: boolean }>(
|
|
177
|
+
bridge,
|
|
178
|
+
`(() => { const s = window.SootSim?.bridges?.getRecordingState?.()
|
|
179
|
+
return { state: s?.state, hasBlob: !!s?.lastVideoBlob } })()`,
|
|
180
|
+
)
|
|
181
|
+
if (st?.state === 'idle' && st.hasBlob) {
|
|
182
|
+
hasBlob = true
|
|
183
|
+
break
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (!hasBlob) {
|
|
187
|
+
console.error(' film: flow recording never produced a video blob')
|
|
188
|
+
return 1
|
|
189
|
+
}
|
|
190
|
+
console.log(` film: flow captured (${(durationMs / 1000).toFixed(1)}s)`)
|
|
191
|
+
|
|
192
|
+
// -- phase 2: bring up the 3d stage + recording editor -----------------
|
|
193
|
+
await evalInPage(
|
|
194
|
+
bridge,
|
|
195
|
+
`window.SootSim?.bridges?.settings?.set?.('threeMode', true)`,
|
|
196
|
+
)
|
|
197
|
+
await waitForThreeModeRuntimeReadyOnBridge(bridge)
|
|
198
|
+
await sleep(1400)
|
|
199
|
+
await evalInPage(
|
|
200
|
+
bridge,
|
|
201
|
+
`window.SootSim?.bridges?.settings?.set?.('threeRecordingEditor', true)`,
|
|
202
|
+
)
|
|
203
|
+
let replaySeeded = false
|
|
204
|
+
for (let i = 0; i < 60; i++) {
|
|
205
|
+
await sleep(250)
|
|
206
|
+
const seeded = await evalInPage<boolean>(
|
|
207
|
+
bridge,
|
|
208
|
+
`(() => { const rs = window.SootSim?.replayStore
|
|
209
|
+
return rs?.value?.preview?.surface === 'video' && !!rs?.value?.preview?.flowVideoUrl })()`,
|
|
210
|
+
)
|
|
211
|
+
if (seeded) {
|
|
212
|
+
replaySeeded = true
|
|
213
|
+
break
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!replaySeeded) {
|
|
217
|
+
console.error(' film: recording editor did not seed the replay store')
|
|
218
|
+
return 1
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const configure = buildConfigureOptions(args)
|
|
222
|
+
delete configure.script
|
|
223
|
+
configure.resetPose = true
|
|
224
|
+
await callInBridgeWrite(bridge, 'SootSim.bridges.threeMode.configure', configure)
|
|
225
|
+
await sleep(2400)
|
|
226
|
+
|
|
227
|
+
// -- phase 3: capture the stage while the replay + camera script run ---
|
|
228
|
+
console.log(` film: filming the 3d stage (script: ${scriptId})`)
|
|
229
|
+
const rec = await evalInPage<{ ok: boolean; error?: string }>(
|
|
230
|
+
bridge,
|
|
231
|
+
`(() => {
|
|
232
|
+
const c = window.SootSim?.bridges?.threeMode?.getCanvas?.()
|
|
233
|
+
if (!c || !c.captureStream) return { ok: false, error: 'no stage canvas' }
|
|
234
|
+
let mime = 'video/webm;codecs=vp9'
|
|
235
|
+
if (!('MediaRecorder' in window) || !MediaRecorder.isTypeSupported(mime)) mime = 'video/webm'
|
|
236
|
+
const stream = c.captureStream(${fps})
|
|
237
|
+
const mr = new MediaRecorder(stream, { mimeType: mime, videoBitsPerSecond: ${STAGE_BITRATE} })
|
|
238
|
+
const st = { mr, chunks: [], b64: null }
|
|
239
|
+
mr.ondataavailable = (e) => { if (e.data && e.data.size) st.chunks.push(e.data) }
|
|
240
|
+
mr.onstop = () => {
|
|
241
|
+
const blob = new Blob(st.chunks, { type: 'video/webm' })
|
|
242
|
+
const fr = new FileReader()
|
|
243
|
+
fr.onload = () => { st.b64 = String(fr.result).split(',')[1] || '' }
|
|
244
|
+
fr.readAsDataURL(blob)
|
|
245
|
+
}
|
|
246
|
+
window.__sootsimFilm = st
|
|
247
|
+
mr.start(200)
|
|
248
|
+
return { ok: true }
|
|
249
|
+
})()`,
|
|
250
|
+
)
|
|
251
|
+
if (!rec?.ok) {
|
|
252
|
+
console.error(` film: stage capture failed: ${rec?.error ?? 'unknown'}`)
|
|
253
|
+
return 1
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await evalInPage(
|
|
257
|
+
bridge,
|
|
258
|
+
`((dur) => {
|
|
259
|
+
const rs = window.SootSim?.replayStore
|
|
260
|
+
if (!rs) return false
|
|
261
|
+
const probed = rs.value?.playback?.durationMs
|
|
262
|
+
const total = probed && probed > 500 ? probed : dur
|
|
263
|
+
const start = performance.now()
|
|
264
|
+
const tick = () => {
|
|
265
|
+
const t = performance.now() - start
|
|
266
|
+
rs.emit({ ...rs.value, playback: { ...rs.value.playback, playing: true, scrubbing: false, currentTimeMs: Math.min(t, total) } })
|
|
267
|
+
if (t < total) requestAnimationFrame(tick)
|
|
268
|
+
}
|
|
269
|
+
requestAnimationFrame(tick)
|
|
270
|
+
return true
|
|
271
|
+
})(${durationMs})`,
|
|
272
|
+
)
|
|
273
|
+
await callInBridgeWrite(bridge, 'SootSim.bridges.threeMode.configure', {
|
|
274
|
+
script: { id: scriptId, progress: 1, animate: true, slow: true },
|
|
275
|
+
})
|
|
276
|
+
await sleep(durationMs + 700)
|
|
277
|
+
|
|
278
|
+
await evalInPage(
|
|
279
|
+
bridge,
|
|
280
|
+
`(() => { const s = window.__sootsimFilm; if (s?.mr?.state === 'recording') s.mr.stop(); return true })()`,
|
|
281
|
+
)
|
|
282
|
+
let b64Size = 0
|
|
283
|
+
for (let i = 0; i < 60; i++) {
|
|
284
|
+
await sleep(500)
|
|
285
|
+
const probe = await evalInPage<{ ready: boolean; size: number }>(
|
|
286
|
+
bridge,
|
|
287
|
+
`(() => { const s = window.__sootsimFilm; return { ready: !!(s && s.b64), size: s && s.b64 ? s.b64.length : 0 } })()`,
|
|
288
|
+
)
|
|
289
|
+
if (probe?.ready && probe.size > 0) {
|
|
290
|
+
b64Size = probe.size
|
|
291
|
+
break
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (!b64Size) {
|
|
295
|
+
console.error(' film: stage recording never finished encoding')
|
|
296
|
+
return 1
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// stream the base64 back in chunks (bridge messages cap out on very
|
|
300
|
+
// large single payloads)
|
|
301
|
+
const CHUNK = 2_400_000
|
|
302
|
+
let b64 = ''
|
|
303
|
+
for (let offset = 0; offset < b64Size; offset += CHUNK) {
|
|
304
|
+
const part = await evalInPage<{ data: string }>(
|
|
305
|
+
bridge,
|
|
306
|
+
`(() => ({ data: window.__sootsimFilm.b64.substr(${offset}, ${CHUNK}) }))()`,
|
|
307
|
+
)
|
|
308
|
+
b64 += part.data
|
|
309
|
+
}
|
|
310
|
+
await evalInPage(
|
|
311
|
+
bridge,
|
|
312
|
+
`(() => { try { delete window.__sootsimFilm } catch {} return true })()`,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
const webmBytes = Buffer.from(b64, 'base64')
|
|
316
|
+
mkdirSync(dirname(outPath), { recursive: true })
|
|
317
|
+
if (wantsMp4) {
|
|
318
|
+
const tmpWebm = `${outPath}.tmp.webm`
|
|
319
|
+
writeFileSync(tmpWebm, webmBytes)
|
|
320
|
+
const ff = spawnSync(
|
|
321
|
+
'ffmpeg',
|
|
322
|
+
[
|
|
323
|
+
'-y',
|
|
324
|
+
'-i',
|
|
325
|
+
tmpWebm,
|
|
326
|
+
'-c:v',
|
|
327
|
+
'libx264',
|
|
328
|
+
// libx264 + yuv420p requires even dimensions; the stage canvas is
|
|
329
|
+
// whatever the window layout produced (e.g. 750x987)
|
|
330
|
+
'-vf',
|
|
331
|
+
'scale=trunc(iw/2)*2:trunc(ih/2)*2',
|
|
332
|
+
'-pix_fmt',
|
|
333
|
+
'yuv420p',
|
|
334
|
+
'-movflags',
|
|
335
|
+
'+faststart',
|
|
336
|
+
outPath,
|
|
337
|
+
],
|
|
338
|
+
{ stdio: ['ignore', 'ignore', 'pipe'] },
|
|
339
|
+
)
|
|
340
|
+
if (ff.status !== 0) {
|
|
341
|
+
// keep the source webm so the capture isn't lost
|
|
342
|
+
const tail = String(ff.stderr ?? '')
|
|
343
|
+
.split('\n')
|
|
344
|
+
.filter(Boolean)
|
|
345
|
+
.slice(-4)
|
|
346
|
+
.join('\n ')
|
|
347
|
+
console.error(` film: ffmpeg transcode failed:\n ${tail}`)
|
|
348
|
+
console.error(` film: source kept at ${tmpWebm}`)
|
|
349
|
+
return 1
|
|
350
|
+
}
|
|
351
|
+
unlinkSync(tmpWebm)
|
|
352
|
+
} else {
|
|
353
|
+
writeFileSync(outPath, webmBytes)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// -- restore shell state ----------------------------------------------
|
|
357
|
+
if (!keepThree) {
|
|
358
|
+
if (!before?.editor) {
|
|
359
|
+
await evalInPage(
|
|
360
|
+
bridge,
|
|
361
|
+
`window.SootSim?.bridges?.settings?.set?.('threeRecordingEditor', false)`,
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
if (!before?.threeMode) {
|
|
365
|
+
await evalInPage(
|
|
366
|
+
bridge,
|
|
367
|
+
`window.SootSim?.bridges?.settings?.set?.('threeMode', false)`,
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
console.log(
|
|
373
|
+
` film: saved ${outPath} (${(webmBytes.length / 1e6).toFixed(1)}MB source)`,
|
|
374
|
+
)
|
|
375
|
+
return 0
|
|
376
|
+
} finally {
|
|
377
|
+
bridge.close()
|
|
378
|
+
}
|
|
379
|
+
}
|