rnxsim 0.1.313 → 0.1.315
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-CBU2WDKV.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-JJYYW2WH.js → agent-wrapper-UNB4R27L.js} +2 -2
- package/dist-cli/chunks/{app-fonts-IXRNQG6B.js → app-fonts-YMLRF3JD.js} +2 -2
- package/dist-cli/chunks/{assert-54T5SK5F.js → assert-5N6HUCL5.js} +2 -2
- package/dist-cli/chunks/{auth-FI5UDI45.js → auth-BHDR3A6G.js} +2 -2
- package/dist-cli/chunks/{beta-JV6UKADW.js → beta-LWUVCPYK.js} +2 -2
- package/dist-cli/chunks/camera-CNO6B2JZ.js +33 -0
- package/dist-cli/chunks/{chunk-ZMJD5GEC.js → chunk-25VCR44B.js} +1 -1
- package/dist-cli/chunks/{chunk-WUSWBCWA.js → chunk-26CSIR76.js} +8 -9
- package/dist-cli/chunks/{chunk-WINYQ44O.js → chunk-26WMWUH4.js} +1 -1
- package/dist-cli/chunks/{chunk-BTWORNNG.js → chunk-2JZ53D4A.js} +1 -1
- package/dist-cli/chunks/{chunk-DCEMHR2Y.js → chunk-2POODCTW.js} +2 -2
- package/dist-cli/chunks/{chunk-5TEF3ET3.js → chunk-4LTBK3ZN.js} +2 -2
- package/dist-cli/chunks/{chunk-NMF2ZMZQ.js → chunk-54MIRTFE.js} +4 -4
- package/dist-cli/chunks/{chunk-WEXDAC74.js → chunk-56H6EVO4.js} +2 -2
- package/dist-cli/chunks/{chunk-F5ZRSS3C.js → chunk-657SQG4D.js} +1 -1
- package/dist-cli/chunks/{chunk-IJ5CAZZC.js → chunk-6ZMRJPKI.js} +1 -1
- package/dist-cli/chunks/{chunk-W6K4EFPH.js → chunk-AOGGOC4V.js} +2 -2
- package/dist-cli/chunks/chunk-BPCDO7ST.js +15 -0
- package/dist-cli/chunks/{chunk-D4FFVGI5.js → chunk-C5O5CTXJ.js} +1 -1
- package/dist-cli/chunks/{chunk-RTN5C5RL.js → chunk-CMM64PPR.js} +1 -1
- package/dist-cli/chunks/{chunk-BBULZ7CG.js → chunk-D3M36D7O.js} +62 -87
- package/dist-cli/chunks/{chunk-5DHC6KHQ.js → chunk-DNCNFZ7V.js} +1 -1
- package/dist-cli/chunks/{chunk-YIFT42WN.js → chunk-FNTTIR2P.js} +2 -2
- package/dist-cli/chunks/chunk-H362IX2I.js +4 -0
- package/dist-cli/chunks/{chunk-IJO63TDP.js → chunk-J6HMTUZH.js} +2 -2
- package/dist-cli/chunks/{chunk-RSZWCKNT.js → chunk-JZ6VTZ5B.js} +3 -3
- package/dist-cli/chunks/{chunk-MJRLLB4R.js → chunk-KQSJVNFE.js} +4 -4
- package/dist-cli/chunks/chunk-L4JZKVCC.js +6 -0
- package/dist-cli/chunks/{chunk-VFCMSYZK.js → chunk-LP2BTEJ6.js} +2 -2
- package/dist-cli/chunks/{chunk-2YR5BGA5.js → chunk-LT6UC7I3.js} +2 -2
- package/dist-cli/chunks/{chunk-OVFJFXUD.js → chunk-MCKQH4AM.js} +2 -2
- package/dist-cli/chunks/{chunk-TZFFR3SD.js → chunk-MF7WUHQ3.js} +2 -2
- package/dist-cli/chunks/{chunk-46EUUFJ5.js → chunk-MSSY4EEZ.js} +1 -1
- package/dist-cli/chunks/{chunk-WMIIKMGK.js → chunk-N5ZCYP5F.js} +2 -2
- package/dist-cli/chunks/{chunk-3NV2NCNX.js → chunk-NCKAD3V7.js} +2 -2
- package/dist-cli/chunks/{chunk-VNQEB4L7.js → chunk-O2KRHVTU.js} +2 -2
- package/dist-cli/chunks/{chunk-OZSSI4WN.js → chunk-OF2HTH25.js} +2 -2
- package/dist-cli/chunks/{chunk-GGRX24GF.js → chunk-OXS2IWO2.js} +2 -2
- package/dist-cli/chunks/{chunk-WWZIXIRD.js → chunk-PVL7XSPN.js} +1 -1
- package/dist-cli/chunks/{chunk-DZS6WPUI.js → chunk-PVPAP3TV.js} +1 -1
- package/dist-cli/chunks/{chunk-5YJCOWCH.js → chunk-PXNAZYGV.js} +1 -1
- package/dist-cli/chunks/chunk-QR3HTFRB.js +5 -0
- package/dist-cli/chunks/{chunk-5TPRP5QT.js → chunk-QR7MWAWE.js} +1 -1
- package/dist-cli/chunks/chunk-RJIEZ2NY.js +4 -0
- package/dist-cli/chunks/{chunk-UC6U3MML.js → chunk-RVM4EZ4E.js} +2 -2
- package/dist-cli/chunks/{chunk-2D2UPBBR.js → chunk-SZKSFMWS.js} +1 -1
- package/dist-cli/chunks/{chunk-7GN3LVWB.js → chunk-TEL7G234.js} +2 -2
- package/dist-cli/chunks/{chunk-GASE6UBA.js → chunk-U2HNIHBY.js} +1 -1
- package/dist-cli/chunks/chunk-UROXJVY3.js +27 -0
- package/dist-cli/chunks/{chunk-HI5TFJWN.js → chunk-WM32MGTJ.js} +2 -2
- package/dist-cli/chunks/{chunk-VZXWHRUZ.js → chunk-WMZ7NJAO.js} +89 -133
- package/dist-cli/chunks/{chunk-XEVZYVIW.js → chunk-WUXCH6WN.js} +10 -9
- package/dist-cli/chunks/chunk-XOXGC3EH.js +9 -0
- package/dist-cli/chunks/chunk-XSSHENNY.js +4 -0
- package/dist-cli/chunks/{chunk-OHAZNXLK.js → chunk-XZ6O67AR.js} +1 -1
- package/dist-cli/chunks/{chunk-QKDWYITG.js → chunk-Y2MWQKOS.js} +3 -3
- package/dist-cli/chunks/{chunk-YDGQTMQL.js → chunk-YY24QI25.js} +1 -1
- package/dist-cli/chunks/{chunk-WF3T4SVI.js → chunk-ZWX6DDIS.js} +2 -2
- package/dist-cli/chunks/{cleanup-P27PA6JI.js → cleanup-F3NUJ3SL.js} +2 -2
- package/dist-cli/chunks/cli-version-XIW6O3FG.js +4 -0
- package/dist-cli/chunks/{compat-ZD65FED3.js → compat-AX2SEPTH.js} +2 -2
- package/dist-cli/chunks/{config-XMJRNM2A.js → config-4QQO5JVU.js} +2 -2
- package/dist-cli/chunks/{control-KMIQT3QP.js → control-OCKKSOWA.js} +2 -2
- package/dist-cli/chunks/daemon-N5VLMYKQ.js +4 -0
- package/dist-cli/chunks/{debug-PT4HOP7N.js → debug-T6MWVEVX.js} +5 -5
- package/dist-cli/chunks/{desktop-S3FG72AK.js → desktop-NCW7R7CB.js} +3 -3
- package/dist-cli/chunks/{detox-B3D4IFCN.js → detox-G6GITO2X.js} +2 -2
- package/dist-cli/chunks/{device-XBNDSB2R.js → device-7BTEJCTH.js} +2 -2
- package/dist-cli/chunks/{diagnose-HMQXJE5N.js → diagnose-SC5LACE3.js} +2 -2
- package/dist-cli/chunks/{disk-cleanup-BLCZ5BSZ.js → disk-cleanup-KK56QQZA.js} +2 -2
- package/dist-cli/chunks/drivers-NPGVBWMG.js +4 -0
- package/dist-cli/chunks/{film-BJGTBYZB.js → film-WGYEHGIA.js} +3 -3
- package/dist-cli/chunks/flow-KQBIBM3N.js +4 -0
- package/dist-cli/chunks/help-G7CAKI65.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-ANOYADHM.js → hidden-runtime-alias-CWTZWH3L.js} +2 -2
- package/dist-cli/chunks/home-paths-EBT4XHAS.js +4 -0
- package/dist-cli/chunks/inspect-CCPZXVDU.js +4 -0
- package/dist-cli/chunks/install-desktop-NZ3VGTQZ.js +4 -0
- package/dist-cli/chunks/{login-FJ737MWG.js → login-GSYOTGVQ.js} +4 -4
- package/dist-cli/chunks/{logout-ZCNMMHMY.js → logout-JPRIR7YK.js} +2 -2
- package/dist-cli/chunks/{maestro-SZTNKLDF.js → maestro-LXKDNO5M.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-DCFAIZ4H.js → maestro-generate-5ZIKOU53.js} +3 -3
- package/dist-cli/chunks/{mode-GRMQCRXR.js → mode-N3QHXCES.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-W36EWFFB.js → optional-demo-registry-XDH6X24N.js} +2 -2
- package/dist-cli/chunks/{perf-QYBAAUZG.js → perf-VOPL26FM.js} +2 -2
- package/dist-cli/chunks/{permissions-3QCQ6VF4.js → permissions-CZFKM4UD.js} +2 -2
- package/dist-cli/chunks/{record-QPPC2S4E.js → record-FODSOGMA.js} +3 -3
- package/dist-cli/chunks/{report-issue-7NMFP4HK.js → report-issue-FBPCZKOV.js} +2 -2
- package/dist-cli/chunks/reset-3H35GFO3.js +4 -0
- package/dist-cli/chunks/runtime-4OVFRXV5.js +4 -0
- package/dist-cli/chunks/{screenshot-command-67AECJFB.js → screenshot-command-7S3FYVO7.js} +7 -7
- package/dist-cli/chunks/{screenshot-layers-ASWBYPJL.js → screenshot-layers-CDYHPNC3.js} +3 -3
- package/dist-cli/chunks/{screenshots-capture-PXA3HFQK.js → screenshots-capture-EBIIY2YU.js} +2 -2
- package/dist-cli/chunks/serve-NELNSCZP.js +44 -0
- package/dist-cli/chunks/{setup-7DWPMRSB.js → setup-DF5ZDDIV.js} +2 -2
- package/dist-cli/chunks/{skills-S3Y22TUA.js → skills-E2QZF6WZ.js} +2 -2
- package/dist-cli/chunks/state-MBZDKZ7U.js +14 -0
- package/dist-cli/chunks/{storage-XUIMJWAJ.js → storage-J5BMNVXM.js} +6 -6
- package/dist-cli/chunks/store-U3DXC7HH.js +4 -0
- package/dist-cli/chunks/telemetry-XIPJVT5E.js +4 -0
- package/dist-cli/chunks/{timeline-TMPLQPSP.js → timeline-MC6N5BHY.js} +2 -2
- package/dist-cli/chunks/{upgrade-7HDSIM7K.js → upgrade-7UTU53DH.js} +2 -2
- package/dist-cli/chunks/upload-2M2KOTYI.js +4 -0
- package/dist-cli/chunks/version-VNACNYQK.js +6 -0
- package/dist-cli/chunks/{web-DG3WBYD3.js → web-MGJ2TUKR.js} +2 -2
- package/dist-cli/chunks/{what-happened-XFVUTZR7.js → what-happened-7DLKH3OD.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,453 @@
|
|
|
1
|
+
// sootsim maestro — drop-in replacement for the real `maestro` cli.
|
|
2
|
+
//
|
|
3
|
+
// users with an existing `.maestro/` directory can swap `maestro test`
|
|
4
|
+
// for `sootsim maestro test` and have their flows execute against
|
|
5
|
+
// sootsim's in-browser engine. the heavy lifting lives in `flow.ts` +
|
|
6
|
+
// `bridge-flow-runner.ts`; this file just re-maps the maestro argv shape
|
|
7
|
+
// onto the existing flow runner and handles directory iteration + init.
|
|
8
|
+
|
|
9
|
+
import * as fs from 'fs'
|
|
10
|
+
import * as path from 'path'
|
|
11
|
+
import yaml from 'yaml'
|
|
12
|
+
import { resolveCliAuth } from '../auth'
|
|
13
|
+
import { parseFlowFile } from '../flow-file'
|
|
14
|
+
import { registerRun, shouldRegisterRun, stepSummaryFromTrace } from '../run-registry'
|
|
15
|
+
import {
|
|
16
|
+
getLastFlowPreviewUploadResult,
|
|
17
|
+
getLastFlowTraceSteps,
|
|
18
|
+
hoistLeadingSimFlag,
|
|
19
|
+
runMaestroAuthoring,
|
|
20
|
+
runFlowPlayback,
|
|
21
|
+
} from './flow'
|
|
22
|
+
import { resolveDefaultUploadOrigin } from './upload'
|
|
23
|
+
|
|
24
|
+
export interface RunMaestroOptions {
|
|
25
|
+
port?: number
|
|
26
|
+
verbose?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const MAESTRO_DIRS = ['.maestro', 'maestro']
|
|
30
|
+
|
|
31
|
+
function printHelp() {
|
|
32
|
+
console.log(`
|
|
33
|
+
rnxsim maestro — author and run Maestro YAML flows against rnx
|
|
34
|
+
|
|
35
|
+
usage:
|
|
36
|
+
rnxsim maestro # discover .maestro/ or maestro/ in cwd, run all
|
|
37
|
+
rnxsim maestro test <flow-or-dir> # mirrors "maestro test"
|
|
38
|
+
rnxsim maestro test .maestro/ # every *.yaml / *.yml in a directory
|
|
39
|
+
rnxsim maestro generate "<goal>" # generate, run, and upload a Maestro flow
|
|
40
|
+
rnxsim maestro start|keep|end # author a flow from live CLI actions
|
|
41
|
+
rnxsim maestro validate <flow> # validate a Maestro YAML file
|
|
42
|
+
rnxsim maestro init # scaffold .maestro/login.yaml
|
|
43
|
+
rnxsim maestro --list-compat # print supported/unsupported verbs
|
|
44
|
+
|
|
45
|
+
options:
|
|
46
|
+
--env KEY=VALUE set env vars for \${KEY} interpolation (repeatable)
|
|
47
|
+
--continuous re-run the flow on file changes (alias for --watch)
|
|
48
|
+
--format <fmt> accepted for maestro cli compat (not used)
|
|
49
|
+
--new force a fresh browser window for this run
|
|
50
|
+
--headed force a fresh, VISIBLE window (implies --new,
|
|
51
|
+
overrides --headless) — watch the flow run live
|
|
52
|
+
--record record a webm while the flow runs
|
|
53
|
+
--preview record events+video, upload, and print /preview/<id>
|
|
54
|
+
--preview-open open the uploaded preview link after a successful run
|
|
55
|
+
--preview-origin <url> upload target for --preview
|
|
56
|
+
--preview-public-origin <url>
|
|
57
|
+
public link origin for --preview
|
|
58
|
+
--slow <ms> delay between steps for natural pacing
|
|
59
|
+
|
|
60
|
+
examples:
|
|
61
|
+
rnxsim maestro test .maestro/login.yaml
|
|
62
|
+
rnxsim maestro test .maestro/
|
|
63
|
+
rnxsim maestro generate "log in and verify the welcome screen"
|
|
64
|
+
rnxsim maestro start
|
|
65
|
+
rnxsim maestro end --output .maestro/login.yaml --validate
|
|
66
|
+
rnxsim maestro --env USERNAME=alice test .maestro/login.yaml
|
|
67
|
+
rnxsim maestro init
|
|
68
|
+
`)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function printCompatMatrix() {
|
|
72
|
+
console.log(`
|
|
73
|
+
rnxsim maestro — compatibility matrix
|
|
74
|
+
|
|
75
|
+
supported verbs:
|
|
76
|
+
launchApp, stopApp, clearState, clearKeychain (warn-only),
|
|
77
|
+
tapOn, tapAtCoords, longPressOn,
|
|
78
|
+
inputText, pressKey, dispatchKey, hideKeyboard, eraseText,
|
|
79
|
+
assertVisible, assertNotVisible, assertTreeContains,
|
|
80
|
+
waitFor, extendedWaitUntil, waitForAnimationToEnd,
|
|
81
|
+
scroll, scrollUntilVisible, scrollTo, swipe, pinch,
|
|
82
|
+
takeScreenshot, dumpTree, back,
|
|
83
|
+
repeat (times), runFlow (file / inline commands / env / when),
|
|
84
|
+
runScript (maestro JS: http, json(), output, env vars, console.log),
|
|
85
|
+
evalScript, copyTextFrom (\${maestro.copiedText}), openLink,
|
|
86
|
+
when: (visible / notVisible / platform / true),
|
|
87
|
+
optional:, label:, onFlowStart, onFlowComplete,
|
|
88
|
+
\${...} JS-expression interpolation at step execution time
|
|
89
|
+
(env vars as globals, output.*, maestro.*, \${EXPR || 'default'}).
|
|
90
|
+
|
|
91
|
+
workspace mode (directory runs):
|
|
92
|
+
config.yaml / config.yml discovery, flows: inclusion globs,
|
|
93
|
+
includeTags / excludeTags against flow frontmatter tags.
|
|
94
|
+
|
|
95
|
+
partial:
|
|
96
|
+
clearKeychain — warn-only, rnx has no keychain surface.
|
|
97
|
+
when.platform — rnx reports iOS.
|
|
98
|
+
openLink — dispatched into the app via the Linking emulation
|
|
99
|
+
(react-navigation deep links work); no OS-level Safari fallback.
|
|
100
|
+
launchApp.arguments — ignored; rnx has no native process.
|
|
101
|
+
launchApp.resetRuntime — sootsim-only extension for recorded benchmarks:
|
|
102
|
+
keep tenant storage/auth, but soft-reload the guest app inside launchApp.
|
|
103
|
+
takeScreenshot — maestro string form works unchanged; rnx also accepts
|
|
104
|
+
{ path, withFrame } for framed export.
|
|
105
|
+
env — rnx seeds the JS env from the full shell environment, not just
|
|
106
|
+
MAESTRO_* (so \${SOOTSIM_*} credentials resolve); a bare \${NAME} that is
|
|
107
|
+
undefined fails the step loudly instead of typing "undefined".
|
|
108
|
+
|
|
109
|
+
not yet implemented (will throw "unsupported maestro verb"):
|
|
110
|
+
travel, setLocation, setAirplaneMode, killApp, faker,
|
|
111
|
+
addMedia, startRecording, stopRecording (use --record flag),
|
|
112
|
+
repeat.while (only repeat.times is supported).
|
|
113
|
+
`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function looksLikeFlag(s: string): boolean {
|
|
117
|
+
return s.startsWith('-')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// extract --env KEY=VALUE pairs from argv and apply to process.env for the
|
|
121
|
+
// duration of the run. returns argv with the consumed flags removed so the
|
|
122
|
+
// rest can be forwarded to runFlowPlayback verbatim.
|
|
123
|
+
function applyEnvFlags(args: string[]): string[] {
|
|
124
|
+
const out: string[] = []
|
|
125
|
+
for (let i = 0; i < args.length; i++) {
|
|
126
|
+
if (args[i] === '--env' || args[i] === '-e') {
|
|
127
|
+
const kv = args[i + 1]
|
|
128
|
+
if (!kv || !kv.includes('=')) {
|
|
129
|
+
throw new Error(`--env expects KEY=VALUE (got ${JSON.stringify(kv ?? '')})`)
|
|
130
|
+
}
|
|
131
|
+
const eq = kv.indexOf('=')
|
|
132
|
+
const key = kv.slice(0, eq)
|
|
133
|
+
const value = kv.slice(eq + 1)
|
|
134
|
+
process.env[key] = value
|
|
135
|
+
i += 1
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
out.push(args[i])
|
|
139
|
+
}
|
|
140
|
+
return out
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Remove Maestro-only flags that the shared runner does not know.
|
|
144
|
+
// these are accepted so users can literally s/maestro/sootsim maestro/ in
|
|
145
|
+
// their scripts without edits.
|
|
146
|
+
function stripUnknownMaestroFlags(args: string[]): string[] {
|
|
147
|
+
const out: string[] = []
|
|
148
|
+
for (let i = 0; i < args.length; i++) {
|
|
149
|
+
const a = args[i]
|
|
150
|
+
if (a === '--format' || a === '--output' || a === '--device' || a === '-d') {
|
|
151
|
+
i += 1
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
if (a === '--continuous') {
|
|
155
|
+
// map to --watch for runFlowPlayback's existing watch path if one
|
|
156
|
+
// exists; today runFlowPlayback has no watch mode, so we drop the
|
|
157
|
+
// flag and log a warning rather than silently changing behavior.
|
|
158
|
+
console.warn(' warn: --continuous is not yet implemented in rnx — running once')
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
out.push(a)
|
|
162
|
+
}
|
|
163
|
+
return out
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function findDefaultMaestroTarget(cwd: string): string | null {
|
|
167
|
+
for (const dir of MAESTRO_DIRS) {
|
|
168
|
+
const abs = path.join(cwd, dir)
|
|
169
|
+
if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) return abs
|
|
170
|
+
}
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
interface MaestroWorkspaceConfig {
|
|
175
|
+
flows?: string[]
|
|
176
|
+
includeTags?: string[]
|
|
177
|
+
excludeTags?: string[]
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// workspace planning for directory targets — mirrors maestro's
|
|
181
|
+
// WorkspaceExecutionPlanner: discover config.yaml/config.yml in the
|
|
182
|
+
// directory, walk it recursively for *.yaml/*.yml flow files (config files
|
|
183
|
+
// excluded), keep the ones matching the config's `flows:` globs (default
|
|
184
|
+
// `*` = top level only), then filter by includeTags/excludeTags against
|
|
185
|
+
// each flow's frontmatter `tags:`.
|
|
186
|
+
export function collectFlowFiles(target: string): string[] {
|
|
187
|
+
const stat = fs.statSync(target)
|
|
188
|
+
if (stat.isFile()) return [target]
|
|
189
|
+
if (!stat.isDirectory()) {
|
|
190
|
+
throw new Error(`not a file or directory: ${target}`)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let config: MaestroWorkspaceConfig = {}
|
|
194
|
+
const configPath = ['config.yaml', 'config.yml']
|
|
195
|
+
.map((name) => path.join(target, name))
|
|
196
|
+
.find((p) => fs.existsSync(p))
|
|
197
|
+
if (configPath) {
|
|
198
|
+
// yaml.parse returns any; shape-check the fields we use below instead
|
|
199
|
+
// of trusting the file
|
|
200
|
+
const parsed = yaml.parse(fs.readFileSync(configPath, 'utf8'))
|
|
201
|
+
if (parsed && typeof parsed === 'object') {
|
|
202
|
+
config = parsed
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const globs =
|
|
207
|
+
Array.isArray(config.flows) && config.flows.length > 0 ? config.flows : ['*']
|
|
208
|
+
const isFlowFile = (rel: string) => {
|
|
209
|
+
const base = path.basename(rel)
|
|
210
|
+
if (base.startsWith('.')) return false
|
|
211
|
+
if (!base.endsWith('.yaml') && !base.endsWith('.yml')) return false
|
|
212
|
+
if (base === 'config.yaml' || base === 'config.yml') return false
|
|
213
|
+
return true
|
|
214
|
+
}
|
|
215
|
+
const matched = new Set<string>()
|
|
216
|
+
for (const glob of globs) {
|
|
217
|
+
for (const rel of fs.globSync(glob, { cwd: target })) {
|
|
218
|
+
if (!isFlowFile(rel)) continue
|
|
219
|
+
const abs = path.join(target, rel)
|
|
220
|
+
if (fs.existsSync(abs) && fs.statSync(abs).isFile()) matched.add(abs)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
let files = [...matched].sort()
|
|
224
|
+
|
|
225
|
+
const includeTags = Array.isArray(config.includeTags) ? config.includeTags : []
|
|
226
|
+
const excludeTags = Array.isArray(config.excludeTags) ? config.excludeTags : []
|
|
227
|
+
if (includeTags.length > 0 || excludeTags.length > 0) {
|
|
228
|
+
files = files.filter((file) => {
|
|
229
|
+
const { frontmatter } = parseFlowFile(fs.readFileSync(file, 'utf8'))
|
|
230
|
+
const tags: string[] = Array.isArray(frontmatter.tags) ? frontmatter.tags : []
|
|
231
|
+
if (includeTags.length > 0 && !tags.some((t) => includeTags.includes(t))) {
|
|
232
|
+
return false
|
|
233
|
+
}
|
|
234
|
+
if (tags.some((t) => excludeTags.includes(t))) return false
|
|
235
|
+
return true
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
return files
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function runInit(cwd: string): Promise<number> {
|
|
242
|
+
const dir = path.join(cwd, '.maestro')
|
|
243
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
244
|
+
const outPath = path.join(dir, 'login.yaml')
|
|
245
|
+
if (fs.existsSync(outPath)) {
|
|
246
|
+
console.error(` error: ${outPath} already exists`)
|
|
247
|
+
return 1
|
|
248
|
+
}
|
|
249
|
+
const starter = `# rnxsim maestro starter flow — drop-in compatible with the maestro cli.
|
|
250
|
+
# run: rnxsim maestro test .maestro/login.yaml
|
|
251
|
+
appId: com.example.app
|
|
252
|
+
---
|
|
253
|
+
- launchApp
|
|
254
|
+
- tapOn:
|
|
255
|
+
id: "email"
|
|
256
|
+
- inputText: "user@example.com"
|
|
257
|
+
- tapOn:
|
|
258
|
+
id: "password"
|
|
259
|
+
- inputText: "secret123"
|
|
260
|
+
- tapOn: "Sign in"
|
|
261
|
+
- assertVisible: "Welcome"
|
|
262
|
+
`
|
|
263
|
+
fs.writeFileSync(outPath, starter, 'utf8')
|
|
264
|
+
console.log(` + wrote ${outPath}`)
|
|
265
|
+
console.log(` next: rnxsim maestro test .maestro/login.yaml`)
|
|
266
|
+
return 0
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export async function runMaestro(
|
|
270
|
+
args: string[],
|
|
271
|
+
opts: RunMaestroOptions = {},
|
|
272
|
+
): Promise<number> {
|
|
273
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
274
|
+
printHelp()
|
|
275
|
+
return 0
|
|
276
|
+
}
|
|
277
|
+
if (args.includes('--list-compat')) {
|
|
278
|
+
printCompatMatrix()
|
|
279
|
+
return 0
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
let remaining: string[]
|
|
283
|
+
try {
|
|
284
|
+
remaining = applyEnvFlags(args)
|
|
285
|
+
} catch (err) {
|
|
286
|
+
console.error(` error: ${(err as Error).message}`)
|
|
287
|
+
return 1
|
|
288
|
+
}
|
|
289
|
+
// bin.ts prepends the global `--sim <id>` to argv. without hoisting it
|
|
290
|
+
// out, `remaining[0]` is `--sim` instead of the `test` subcommand, so
|
|
291
|
+
// maestro auto-discovered a default flow and forwarded `--sim`/`test`/
|
|
292
|
+
// the path as bogus flow args — the explicit sim was silently dropped
|
|
293
|
+
// (QA F21-4). hoist it to the tail so it still rides into forwardArgs →
|
|
294
|
+
// runFlowPlayback, where parseBridgeCliArgs sees simIdSource === 'flag'.
|
|
295
|
+
remaining = hoistLeadingSimFlag(remaining)
|
|
296
|
+
|
|
297
|
+
const cwd = process.cwd()
|
|
298
|
+
const sub = remaining[0]
|
|
299
|
+
|
|
300
|
+
if (sub === 'init') {
|
|
301
|
+
return runInit(cwd)
|
|
302
|
+
}
|
|
303
|
+
if (sub === 'generate') {
|
|
304
|
+
const { runMaestroGenerate } = await import('./maestro-generate')
|
|
305
|
+
return runMaestroGenerate(remaining.slice(1))
|
|
306
|
+
}
|
|
307
|
+
if (
|
|
308
|
+
sub === 'start' ||
|
|
309
|
+
sub === 'keep' ||
|
|
310
|
+
sub === 'good' ||
|
|
311
|
+
sub === 'end' ||
|
|
312
|
+
sub === 'validate'
|
|
313
|
+
) {
|
|
314
|
+
return runMaestroAuthoring(remaining)
|
|
315
|
+
}
|
|
316
|
+
remaining = stripUnknownMaestroFlags(remaining)
|
|
317
|
+
|
|
318
|
+
// resolve the target flow file or directory.
|
|
319
|
+
let target: string | null = null
|
|
320
|
+
let forwardArgs: string[]
|
|
321
|
+
|
|
322
|
+
if (sub === 'test') {
|
|
323
|
+
// `sootsim maestro test <flow-or-dir> [flags...]`
|
|
324
|
+
const positional = remaining.slice(1).find((a) => !looksLikeFlag(a))
|
|
325
|
+
if (positional) {
|
|
326
|
+
target = path.resolve(cwd, positional)
|
|
327
|
+
forwardArgs = remaining.slice(1).filter((a) => a !== positional)
|
|
328
|
+
} else {
|
|
329
|
+
target = findDefaultMaestroTarget(cwd)
|
|
330
|
+
forwardArgs = remaining.slice(1)
|
|
331
|
+
}
|
|
332
|
+
} else if (sub && !looksLikeFlag(sub)) {
|
|
333
|
+
// `sootsim maestro <flow-or-dir>` — tolerate the shorthand.
|
|
334
|
+
target = path.resolve(cwd, sub)
|
|
335
|
+
forwardArgs = remaining.slice(1)
|
|
336
|
+
} else {
|
|
337
|
+
// no positional — auto-discover.
|
|
338
|
+
target = findDefaultMaestroTarget(cwd)
|
|
339
|
+
forwardArgs = remaining
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!target) {
|
|
343
|
+
console.error(`
|
|
344
|
+
error: no maestro flows found.
|
|
345
|
+
expected one of: ${MAESTRO_DIRS.map((d) => `./${d}/`).join(', ')}
|
|
346
|
+
or pass a flow explicitly: rnxsim maestro test path/to/flow.yaml
|
|
347
|
+
or scaffold a starter flow: rnxsim maestro init
|
|
348
|
+
`)
|
|
349
|
+
return 1
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!fs.existsSync(target)) {
|
|
353
|
+
console.error(` error: ${target} not found`)
|
|
354
|
+
return 1
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let files: string[]
|
|
358
|
+
try {
|
|
359
|
+
files = collectFlowFiles(target)
|
|
360
|
+
} catch (err) {
|
|
361
|
+
console.error(` error: ${(err as Error).message}`)
|
|
362
|
+
return 1
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (files.length === 0) {
|
|
366
|
+
console.error(` error: no *.yaml or *.yml files found in ${target}`)
|
|
367
|
+
return 1
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
console.log(`
|
|
371
|
+
rnxsim maestro — ${files.length} flow${files.length === 1 ? '' : 's'}
|
|
372
|
+
root: ${path.relative(cwd, target) || '.'}
|
|
373
|
+
`)
|
|
374
|
+
|
|
375
|
+
// registration target for hosted run rows — resolved once per invocation.
|
|
376
|
+
const auth = resolveCliAuth()
|
|
377
|
+
const flagValue = (name: string) => {
|
|
378
|
+
const index = forwardArgs.indexOf(name)
|
|
379
|
+
return index >= 0 ? forwardArgs[index + 1] : undefined
|
|
380
|
+
}
|
|
381
|
+
// only `registerRun` below consumes this, and only for runs that register.
|
|
382
|
+
// resolving it up front probed https://contrast.localhost:3000 and blocked
|
|
383
|
+
// every flow — including ones that upload and register nothing — for the
|
|
384
|
+
// probe's full 2s timeout whenever no local Contrast stack was listening.
|
|
385
|
+
// resolve on first use instead, and keep the one probe per invocation.
|
|
386
|
+
let registerOriginPromise: Promise<string> | null = null
|
|
387
|
+
const registerOrigin = () =>
|
|
388
|
+
(registerOriginPromise ??= resolveDefaultUploadOrigin(
|
|
389
|
+
flagValue('--preview-origin') ?? flagValue('--origin'),
|
|
390
|
+
))
|
|
391
|
+
|
|
392
|
+
let worstExit = 0
|
|
393
|
+
const results: Array<{ file: string; exit: number }> = []
|
|
394
|
+
for (const file of files) {
|
|
395
|
+
console.log(`\n ▶ ${path.relative(cwd, file)}`)
|
|
396
|
+
const playbackArgs = [file, ...forwardArgs]
|
|
397
|
+
// `--port` is the BRIDGE port everywhere else in the CLI, so forward it as
|
|
398
|
+
// one. sending it on as `--url` made it an app target instead, which left
|
|
399
|
+
// the playback bridge on whichever world resolveBridgeWorld picks by
|
|
400
|
+
// default (a live Vite dev shell selected from the development registry).
|
|
401
|
+
// so `sootsim --port <installed daemon> maestro …` silently ran the flow
|
|
402
|
+
// against the dev engine, then failed trying to resolve a bundle at the
|
|
403
|
+
// bridge port. an explicit command-level `--port` still wins, since
|
|
404
|
+
// parseBridgeCliArgs takes the last occurrence.
|
|
405
|
+
if (opts.port) {
|
|
406
|
+
playbackArgs.push('--port', String(opts.port))
|
|
407
|
+
}
|
|
408
|
+
let exit = 0
|
|
409
|
+
const startedAt = Date.now()
|
|
410
|
+
try {
|
|
411
|
+
exit = await runFlowPlayback(playbackArgs)
|
|
412
|
+
} catch (err) {
|
|
413
|
+
console.error(` x ${(err as Error).message}`)
|
|
414
|
+
exit = 1
|
|
415
|
+
}
|
|
416
|
+
results.push({ file, exit })
|
|
417
|
+
if (exit !== 0 && worstExit === 0) worstExit = exit
|
|
418
|
+
|
|
419
|
+
const upload = getLastFlowPreviewUploadResult()
|
|
420
|
+
if (shouldRegisterRun({ uploadedShare: Boolean(upload), auth })) {
|
|
421
|
+
const traceSteps = getLastFlowTraceSteps()
|
|
422
|
+
const failedStep = traceSteps.find((step) => step.status === 'failure')
|
|
423
|
+
const run = await registerRun({
|
|
424
|
+
origin: await registerOrigin(),
|
|
425
|
+
kind: 'maestro',
|
|
426
|
+
name: path.relative(cwd, file),
|
|
427
|
+
status: exit === 0 ? 'passed' : 'failed',
|
|
428
|
+
failureMessage: exit === 0 ? null : (failedStep?.error ?? 'flow playback failed'),
|
|
429
|
+
previewShareId: upload?.shareId ?? null,
|
|
430
|
+
owner: flagValue('--owner') ?? null,
|
|
431
|
+
repo: flagValue('--repo') ?? null,
|
|
432
|
+
durationMs: Date.now() - startedAt,
|
|
433
|
+
...stepSummaryFromTrace(traceSteps),
|
|
434
|
+
auth,
|
|
435
|
+
})
|
|
436
|
+
if (run) {
|
|
437
|
+
console.log(` run: ${run.id}${run.traceUrl ? ` · replay: ${run.traceUrl}` : ''}`)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (files.length > 1) {
|
|
443
|
+
console.log(`\n maestro summary:`)
|
|
444
|
+
for (const r of results) {
|
|
445
|
+
const label = r.exit === 0 ? 'pass' : 'fail'
|
|
446
|
+
console.log(` ${label} ${path.relative(cwd, r.file)}`)
|
|
447
|
+
}
|
|
448
|
+
const passed = results.filter((r) => r.exit === 0).length
|
|
449
|
+
console.log(` ${passed}/${results.length} passed`)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return worstExit
|
|
453
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { runScreenshotMode } from './screenshot-mode'
|
|
2
|
+
import { runThreeMode } from './three-mode'
|
|
3
|
+
|
|
4
|
+
interface ModeOptions {
|
|
5
|
+
port?: number
|
|
6
|
+
verbose?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const VALUE_FLAGS = new Set(['--sim', '--port', '-p'])
|
|
10
|
+
const SUBCOMMANDS = new Set(['screenshot', 'three'])
|
|
11
|
+
|
|
12
|
+
function findSubcommand(args: string[]): { name: string; index: number } | null {
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
const arg = args[i]
|
|
15
|
+
if (arg.startsWith('-')) {
|
|
16
|
+
if (VALUE_FLAGS.has(arg)) i++
|
|
17
|
+
continue
|
|
18
|
+
}
|
|
19
|
+
return SUBCOMMANDS.has(arg) ? { name: arg, index: i } : null
|
|
20
|
+
}
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function withoutSubcommand(args: string[], index: number): string[] {
|
|
25
|
+
return [...args.slice(0, index), ...args.slice(index + 1)]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function printHelp(): void {
|
|
29
|
+
console.log(`
|
|
30
|
+
rnxsim mode — toggle live display modes
|
|
31
|
+
|
|
32
|
+
usage:
|
|
33
|
+
rnxsim mode screenshot [on|off|toggle]
|
|
34
|
+
rnxsim mode three [on|off|toggle]
|
|
35
|
+
rnxsim mode three configure [options]
|
|
36
|
+
`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function runMode(args: string[], opts: ModeOptions): Promise<void> {
|
|
40
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
41
|
+
printHelp()
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sub = findSubcommand(args)
|
|
46
|
+
if (!sub) {
|
|
47
|
+
printHelp()
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const rest = withoutSubcommand(args, sub.index)
|
|
52
|
+
if (sub.name === 'screenshot') {
|
|
53
|
+
await runScreenshotMode(rest, opts)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
await runThreeMode(rest, opts)
|
|
57
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// shared hint printed when the CLI has no bridge or no desktop companion.
|
|
2
|
+
|
|
3
|
+
import { findDesktopCompanion } from '../desktop-companion'
|
|
4
|
+
import type { BridgeSimInfo } from '../ws-bridge'
|
|
5
|
+
|
|
6
|
+
export function printMissingCompanionHint() {
|
|
7
|
+
console.error(' no rnxsim desktop companion found.')
|
|
8
|
+
console.error('')
|
|
9
|
+
console.error(' install the desktop app:')
|
|
10
|
+
console.error(' rnxsim desktop install')
|
|
11
|
+
console.error('')
|
|
12
|
+
console.error(' or run setup to install the background daemon:')
|
|
13
|
+
console.error(' rnxsim setup')
|
|
14
|
+
console.error('')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function printMissingBridgeHint(port: number) {
|
|
18
|
+
const companion = findDesktopCompanion()
|
|
19
|
+
console.log(` note: no rnx bridge detected on port ${port}`)
|
|
20
|
+
if (companion) {
|
|
21
|
+
console.log(` launch the installed companion with:`)
|
|
22
|
+
console.log(` rnxsim desktop`)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
console.log('')
|
|
26
|
+
console.log(' run `rnxsim setup` to install the background daemon,')
|
|
27
|
+
console.log(' or run `rnxsim serve` in another shell for a foreground bridge.')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function printMissingSimHint(port: number) {
|
|
31
|
+
console.error('')
|
|
32
|
+
console.error(` no sim is connected to the rnx bridge on port ${port}.`)
|
|
33
|
+
console.error('')
|
|
34
|
+
console.error(' start your app dev server, then open it in rnx:')
|
|
35
|
+
console.error(' npx expo start --localhost --port 8081')
|
|
36
|
+
console.error(' rnxsim open 8081')
|
|
37
|
+
console.error('')
|
|
38
|
+
console.error(' then inspect the live app:')
|
|
39
|
+
console.error(' rnxsim describe')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// printed when a command targeted a specific `--sim <id>` that the bridge
|
|
43
|
+
// does not know — distinct from printMissingSimHint, which assumes the bridge
|
|
44
|
+
// has no sims at all. when other sims *are* connected, "no sim is connected"
|
|
45
|
+
// is a flat-out lie; list the valid ids instead so the user can pick one.
|
|
46
|
+
export async function printUnknownSimHint(
|
|
47
|
+
bridge: { listSims: () => Promise<BridgeSimInfo[]> },
|
|
48
|
+
port: number,
|
|
49
|
+
requestedId: string,
|
|
50
|
+
) {
|
|
51
|
+
let sims: BridgeSimInfo[] = []
|
|
52
|
+
try {
|
|
53
|
+
sims = await bridge.listSims()
|
|
54
|
+
} catch {
|
|
55
|
+
// bridge unreachable while reporting — fall through to the generic hint
|
|
56
|
+
}
|
|
57
|
+
console.error('')
|
|
58
|
+
if (sims.length === 0) {
|
|
59
|
+
printMissingSimHint(port)
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
console.error(
|
|
63
|
+
` no sim with id "${requestedId}" is connected to the bridge on port ${port}.`,
|
|
64
|
+
)
|
|
65
|
+
console.error('')
|
|
66
|
+
console.error(' connected sims:')
|
|
67
|
+
for (const sim of sims) {
|
|
68
|
+
const flags = [
|
|
69
|
+
sim.isPrimary ? 'primary' : null,
|
|
70
|
+
sim.readyState,
|
|
71
|
+
sim.userVisible === false ? 'hidden' : sim.userVisible === true ? 'visible' : null,
|
|
72
|
+
sim.userFocused ? 'focused' : null,
|
|
73
|
+
]
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
.join(', ')
|
|
76
|
+
console.error(` ${sim.id} (${flags})`)
|
|
77
|
+
}
|
|
78
|
+
console.error('')
|
|
79
|
+
console.error(' pass a valid --sim id, or run `rnxsim list` to see all sessions.')
|
|
80
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { runCpuProfile } from './cpu-profile'
|
|
2
|
+
import { runInspect } from './inspect'
|
|
3
|
+
import { runReact } from './react'
|
|
4
|
+
|
|
5
|
+
interface PerfOptions {
|
|
6
|
+
port?: number
|
|
7
|
+
verbose?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const VALUE_FLAGS = new Set(['--sim', '--port', '-p'])
|
|
11
|
+
const SUBCOMMANDS = new Set(['cpu', 'react', 'shell', 'scroll'])
|
|
12
|
+
|
|
13
|
+
function findSubcommand(args: string[]): { name: string; index: number } | null {
|
|
14
|
+
for (let i = 0; i < args.length; i++) {
|
|
15
|
+
const arg = args[i]
|
|
16
|
+
if (arg.startsWith('-')) {
|
|
17
|
+
if (VALUE_FLAGS.has(arg)) i++
|
|
18
|
+
continue
|
|
19
|
+
}
|
|
20
|
+
return SUBCOMMANDS.has(arg) ? { name: arg, index: i } : null
|
|
21
|
+
}
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function withoutSubcommand(args: string[], index: number): string[] {
|
|
26
|
+
return [...args.slice(0, index), ...args.slice(index + 1)]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function printHelp(): void {
|
|
30
|
+
console.log(`
|
|
31
|
+
rnxsim perf — performance profiling tools
|
|
32
|
+
|
|
33
|
+
usage:
|
|
34
|
+
rnxsim perf cpu [options]
|
|
35
|
+
rnxsim perf react <subcommand> [options]
|
|
36
|
+
rnxsim perf shell <start|stop|transition> [options]
|
|
37
|
+
rnxsim perf scroll <start|stop> [options]
|
|
38
|
+
|
|
39
|
+
examples:
|
|
40
|
+
rnxsim perf cpu --duration 5 --output /tmp/trace.cpuprofile
|
|
41
|
+
rnxsim perf react summary --limit 10
|
|
42
|
+
rnxsim perf shell start
|
|
43
|
+
rnxsim perf shell stop --json
|
|
44
|
+
rnxsim perf scroll start
|
|
45
|
+
rnxsim perf scroll stop --json
|
|
46
|
+
`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function runPerf(args: string[], opts: PerfOptions): Promise<number> {
|
|
50
|
+
const sub = findSubcommand(args)
|
|
51
|
+
if (!sub) {
|
|
52
|
+
printHelp()
|
|
53
|
+
return args.includes('--help') || args.includes('-h') ? 0 : 1
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const rest = withoutSubcommand(args, sub.index)
|
|
57
|
+
if (sub.name === 'cpu') return runCpuProfile(rest, opts)
|
|
58
|
+
if (sub.name === 'react') return runReact(rest, opts)
|
|
59
|
+
|
|
60
|
+
const inspectArgs = rest.includes('--help') || rest.includes('-h') ? [] : rest
|
|
61
|
+
await runInspect(['debug', 'perf', ...inspectArgs], {
|
|
62
|
+
...opts,
|
|
63
|
+
internalPerfCommand: sub.name === 'scroll' ? 'scroll' : 'shell',
|
|
64
|
+
})
|
|
65
|
+
return typeof process.exitCode === 'number' ? process.exitCode : 0
|
|
66
|
+
}
|