rnxsim 0.1.381 → 0.1.382
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/README.md +19 -6
- package/cli/app-fonts.ts +1 -1
- package/cli/bin.ts +118 -26
- package/cli/bridge-diagnostics.ts +1 -1
- package/cli/bridge-flow-runner.ts +9 -9
- package/cli/browser-evals.ts +3 -3
- package/cli/cli-update.ts +261 -0
- package/cli/cloud-client.ts +407 -0
- package/cli/cloud-dispatch.ts +165 -0
- package/cli/cloud-session.ts +156 -0
- package/cli/command-contract.ts +2 -2
- package/cli/command-registry.ts +9 -0
- package/cli/commands/agent-wrapper.ts +4 -4
- package/cli/commands/agent.ts +3 -3
- package/cli/commands/box/checkout-plane.ts +16 -8
- package/cli/commands/box/rnx-command.ts +2 -2
- package/cli/commands/box/search-command.ts +2 -2
- package/cli/commands/box.ts +123 -70
- package/cli/commands/config.ts +3 -2
- package/cli/commands/control.ts +6 -6
- package/cli/commands/cpu-profile.ts +2 -2
- package/cli/commands/daemon.ts +7 -7
- package/cli/commands/debug.ts +2 -2
- package/cli/commands/detox.ts +7 -7
- package/cli/commands/diagnose.ts +1 -1
- package/cli/commands/electron.ts +1 -1
- package/cli/commands/film.ts +1 -1
- package/cli/commands/flow.ts +4 -4
- package/cli/commands/inspect/core.ts +58 -7
- package/cli/commands/inspect/env.ts +5 -3
- package/cli/commands/inspect/shared.ts +2 -1
- package/cli/commands/inspect/wait-event.ts +1 -1
- package/cli/commands/inspect/wait-ready.ts +15 -5
- package/cli/commands/inspect.ts +31 -9
- package/cli/commands/install-desktop.ts +2 -2
- package/cli/commands/maestro.ts +3 -3
- package/cli/commands/permissions.ts +1 -1
- package/cli/commands/platform.ts +91 -0
- package/cli/commands/record.ts +3 -3
- package/cli/commands/report-issue.ts +1 -1
- package/cli/commands/runtime.ts +1 -1
- package/cli/commands/screenshot-mode.ts +1 -1
- package/cli/commands/screenshot.ts +25 -0
- package/cli/commands/serve.ts +1 -1
- package/cli/commands/skills.ts +1 -1
- package/cli/commands/timeline.ts +1 -1
- package/cli/commands/upgrade.ts +8 -34
- package/cli/commands/upload.ts +3 -3
- package/cli/commands/version.ts +14 -3
- package/cli/commands/what-happened.ts +2 -2
- package/cli/current-sim.ts +43 -30
- package/cli/desktop-companion.ts +1 -1
- package/cli/drivers/electron.ts +1 -1
- package/cli/drivers/playwright.ts +3 -3
- package/cli/drivers/types.ts +4 -4
- package/cli/flow-file.ts +3 -3
- package/cli/help-core.ts +18 -11
- package/cli/hidden-runtime-alias.ts +1 -1
- package/cli/hints.ts +2 -2
- package/cli/maestro-js.ts +2 -2
- package/cli/outbound-endpoints.ts +40 -6
- package/cli/parse-args.ts +1 -1
- package/cli/registry.ts +1 -1
- package/cli/resolve-assets.ts +2 -2
- package/cli/secure-file.ts +31 -0
- package/cli/shell-init.ts +98 -0
- package/cli/ws-bridge.ts +18 -8
- package/detox/expectations.ts +1 -1
- package/detox/gestures.ts +3 -3
- package/detox/index.ts +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-input.cjs +15 -8
- package/dist-lib/bridge-contract-input.mjs +15 -8
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +107 -0
- package/dist-lib/capture-contract.mjs +80 -0
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +32 -0
- package/dist-lib/cloud-contract.mjs +7 -0
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +4 -4
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +9 -1
- package/dist-lib/host/bridge-host.cjs +15 -10
- 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/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +248 -73
- 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-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.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/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +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 +44 -6
- package/dist-lib/sdk.mjs +44 -6
- package/dist-lib/skills.cjs +435 -155
- package/dist-lib/vite.cjs +1 -1
- package/package.json +20 -4
- package/scripts/dev-server-scanner.ts +3 -3
- package/skills/contrast/SKILL.md +1 -1
- package/skills/rnx-perf/SKILL.md +68 -52
- package/src/app-url.ts +2 -3
- package/src/bridge-contract-input.ts +17 -6
- package/src/bridge-contract.ts +25 -4
- package/src/capture-contract.ts +423 -0
- package/src/cloud-contract.ts +15 -0
- package/src/home-paths.ts +9 -3
- package/src/host/agent-host.ts +1 -1
- package/src/host/bridge-host.ts +13 -11
- package/src/host/plane-host.ts +2 -2
- package/src/index.ts +21 -0
- package/src/native-dev-bundle-url.ts +2 -2
- package/src/screenshots/orchestrate.ts +1 -1
- package/src/screenshots/org-deck.ts +1 -1
- package/src/settings-bundle.ts +1 -1
- package/src/sim-client.ts +4 -0
- package/src/vite-plugin-one.ts +2 -2
- package/src/vite-plugin.ts +1 -1
- package/cli/commands/box/cloud-box.ts +0 -294
package/cli/commands/config.ts
CHANGED
|
@@ -9,8 +9,9 @@ usage:
|
|
|
9
9
|
rnx config telemetry [on|off|status]
|
|
10
10
|
|
|
11
11
|
environment:
|
|
12
|
-
RNX_HINTS=off
|
|
13
|
-
RNX_HINTS=always
|
|
12
|
+
RNX_HINTS=off suppress CLI hints
|
|
13
|
+
RNX_HINTS=always show every CLI hint every time
|
|
14
|
+
RNX_UPDATE_CHECK=off disable background CLI update checks and notices
|
|
14
15
|
`)
|
|
15
16
|
}
|
|
16
17
|
|
package/cli/commands/control.ts
CHANGED
|
@@ -339,7 +339,7 @@ function buildShellPathOpenUrl(target: string, baseUrl: string): string {
|
|
|
339
339
|
return url.toString()
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
//
|
|
342
|
+
// rnx share/preview/build links (e.g. https://rnxsim.com/preview/<id>,
|
|
343
343
|
// https://contrast.dev/build/<owner>/<repo>/<branch>) are full player pages
|
|
344
344
|
// served by the Contrast app — NOT Metro bundles and NOT driveable shell sims.
|
|
345
345
|
// passing one to `rnx open` used to fall through to the bundle resolver,
|
|
@@ -942,7 +942,7 @@ export function printConnectedSims(sims: BridgeSimInfo[], selectedId?: string) {
|
|
|
942
942
|
|
|
943
943
|
// classify which engine a sim is running so dev-vs-published is never ambiguous
|
|
944
944
|
// to agents or humans reading `rnx list`. dev shell serves local source on
|
|
945
|
-
// :5173; the Contrast app embeds
|
|
945
|
+
// :5173; the Contrast app embeds rnx on :3000; the bridge/daemon and the
|
|
946
946
|
// /__soot plugin serve the active runtime dir — which, inside a dev checkout, is
|
|
947
947
|
// the fresh dev-stack build (resolveDevCheckoutRuntimeRoot mode boundary), not a
|
|
948
948
|
// published runtime. keep this label honest so it never says "published" while
|
|
@@ -1126,9 +1126,9 @@ async function waitForProcessExit(pid: number, timeoutMs = 2_500): Promise<boole
|
|
|
1126
1126
|
|
|
1127
1127
|
// a sim record's host pid can outlive its process; after pid reuse the number
|
|
1128
1128
|
// may point at an unrelated process (real incident 2026-07-14: a stale record
|
|
1129
|
-
// made `
|
|
1129
|
+
// made `rnx close` SIGTERM the shared vite shell dev server, taking the
|
|
1130
1130
|
// whole dev stack down). only signal a pid whose argv proves it is still a
|
|
1131
|
-
//
|
|
1131
|
+
// rnx playwright host launcher.
|
|
1132
1132
|
function isPlaywrightHostProcess(pid: number): boolean {
|
|
1133
1133
|
// `ps eww` prints argv then the process ENVIRONMENT for same-user
|
|
1134
1134
|
// processes. the marker must live in the env SUFFIX (everything past the
|
|
@@ -1530,9 +1530,9 @@ export async function runOpenCommand(
|
|
|
1530
1530
|
}
|
|
1531
1531
|
|
|
1532
1532
|
// metro HMR mode. interactive opens default to hot=true so edits hot-apply
|
|
1533
|
-
// through
|
|
1533
|
+
// through rnx's HmrClient — expo CLI's createBundleUrlSearchParams
|
|
1534
1534
|
// hardcodes hot=false on every launchAsset URL (a leftover toggle real iOS
|
|
1535
|
-
// sims tolerate but
|
|
1535
|
+
// sims tolerate but rnx couldn't until now), so without this flip the
|
|
1536
1536
|
// bundle prelude doesn't include metro's HMR runtime + react-refresh and
|
|
1537
1537
|
// edits silently no-op. `--driver playwright` (agent / CI runs) defaults
|
|
1538
1538
|
// to hot=false for deterministic e2e. explicit `--hot` / `--no-hmr` win.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// rnx perf cpu — capture sampled CPU traces from the page and every worker.
|
|
2
2
|
//
|
|
3
3
|
// the JS Self-Profiler API (`new Profiler()`) cannot be constructed in a
|
|
4
|
-
// dedicated-worker scope, and
|
|
4
|
+
// dedicated-worker scope, and rnx runs the guest app in the tenant worker,
|
|
5
5
|
// so the old in-worker approach was unsupported (F28). this implementation
|
|
6
6
|
// attaches Chromium's `Profiler` CDP domain to the page and worker targets —
|
|
7
7
|
// which returns the .cpuprofile shape directly for every execution context.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// target, `Target.setAutoAttach` to discover the worker child sessions, and
|
|
11
11
|
// route `Profiler.*` to each worker via its sessionId (flatten mode).
|
|
12
12
|
//
|
|
13
|
-
// the user's normal
|
|
13
|
+
// the user's normal rnx tab runs in plain Chrome without a debug port.
|
|
14
14
|
// the easiest way to get a profilable sim is to open one with a CDP port —
|
|
15
15
|
// it stays driveable over the WS bridge, so you can scroll/navigate while
|
|
16
16
|
// profiling (the playwright driver passes --remote-debugging-port through):
|
package/cli/commands/daemon.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// rnx daemon — manage the persistent
|
|
1
|
+
// rnx daemon — manage the persistent rnx bridge daemon
|
|
2
2
|
//
|
|
3
3
|
// wraps `rnx serve` so it starts automatically on login and restarts if
|
|
4
|
-
// it crashes. the agent is tied to whichever `
|
|
5
|
-
// registration time, so `npm update -g
|
|
4
|
+
// it crashes. the agent is tied to whichever `rnxsim` binary resolves at
|
|
5
|
+
// registration time, so `npm update -g rnxsim` + `rnx daemon restart`
|
|
6
6
|
// picks up the upgrade without touching the plist/unit file.
|
|
7
7
|
//
|
|
8
8
|
// `daemonInstall` is shared by `rnx setup` and the explicit
|
|
@@ -104,10 +104,10 @@ function resolveSootsimInvocation(): SootsimInvocation {
|
|
|
104
104
|
// launchd / systemd run agents with a stripped PATH (basically just
|
|
105
105
|
// /usr/bin:/bin:/usr/sbin:/sbin), so a `#!/usr/bin/env node` shebang
|
|
106
106
|
// resolves nothing and the daemon never starts. spawn the absolute node
|
|
107
|
-
// binary directly with the
|
|
107
|
+
// binary directly with the rnx entry script as an argument.
|
|
108
108
|
//
|
|
109
109
|
// process.execPath is whichever node ran the install command, which is
|
|
110
|
-
// also the node the user expects
|
|
110
|
+
// also the node the user expects rnx to run on.
|
|
111
111
|
const npxInvocation = resolveNpxDaemonInvocation()
|
|
112
112
|
if (npxInvocation) return npxInvocation
|
|
113
113
|
const entry = resolveSootsimEntryScript()
|
|
@@ -433,7 +433,7 @@ export async function daemonInstall({ port, force }: { port: number; force: bool
|
|
|
433
433
|
|
|
434
434
|
// user-scope systemd units are killed when the user's last session ends.
|
|
435
435
|
// on most distros the daemon then disappears at logout — the unit is
|
|
436
|
-
// "active" until you log off, then silently dead, and `
|
|
436
|
+
// "active" until you log off, then silently dead, and `rnx` looks
|
|
437
437
|
// broken when you ssh back in. `loginctl enable-linger` keeps the user
|
|
438
438
|
// bus alive across sessions, which is what every other long-running
|
|
439
439
|
// user agent (gpg-agent, ssh-agent --systemd) also relies on.
|
|
@@ -513,7 +513,7 @@ export function teardownLegacyMacDaemonArtifacts(
|
|
|
513
513
|
* and its support files (mac .app bundle, log dir). does NOT touch user
|
|
514
514
|
* data under ~/.rnx. idempotent: each entry is checked before remove.
|
|
515
515
|
* exported for reuse by the npm preuninstall hook (scripts/preuninstall.cjs)
|
|
516
|
-
* via the bundled CLI so a `npm rm -g
|
|
516
|
+
* via the bundled CLI so a `npm rm -g rnxsim` tears down the daemon before
|
|
517
517
|
* the bin disappears. */
|
|
518
518
|
export function teardownDaemonService(): TeardownReport {
|
|
519
519
|
const removed: string[] = []
|
package/cli/commands/debug.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// rnx debug — drive __sootsimDebug.* from the terminal
|
|
2
2
|
//
|
|
3
|
-
// connects to a running
|
|
3
|
+
// connects to a running rnx instance via the WebSocket bridge and forwards
|
|
4
4
|
// debug channel management, tree snapshots, and on-demand inspectors without
|
|
5
5
|
// requiring a browser console.
|
|
6
6
|
//
|
|
@@ -78,7 +78,7 @@ subcommands:
|
|
|
78
78
|
scroll <id>, scroll-hit <x> <y>,
|
|
79
79
|
hit <x> <y>, gesture <x> <y>
|
|
80
80
|
|
|
81
|
-
js <code> execute javascript in the running app.
|
|
81
|
+
js <code> execute javascript in the running app. rnx
|
|
82
82
|
is the canonical state object — reach into its
|
|
83
83
|
bridges / state / chrome / debug slots directly.
|
|
84
84
|
e.g. SootSim.bridges.test.findByText("Sign in")
|
package/cli/commands/detox.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
//
|
|
1
|
+
// rnx detox — run existing detox suites against rnx
|
|
2
2
|
//
|
|
3
3
|
// the command is a thin orchestration layer over `npx jest`:
|
|
4
4
|
// 1. resolves a test dir (cwd e2e|test/e2e|detox, or the flag)
|
|
5
|
-
// 2. makes sure
|
|
5
|
+
// 2. makes sure an rnx shell is reachable (auto-launches one if not)
|
|
6
6
|
// 3. spawns jest with our preset baked in so `import ... from 'detox'`
|
|
7
|
-
// resolves to the
|
|
7
|
+
// resolves to the rnx driver
|
|
8
8
|
//
|
|
9
9
|
// it deliberately shells out to the user's own jest so their jest version,
|
|
10
10
|
// transforms, and setupFiles keep working. the only thing we force is the
|
|
@@ -129,7 +129,7 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
129
129
|
rnxExit(1)
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
// auto-launch: make sure
|
|
132
|
+
// auto-launch: make sure an rnx shell is reachable before running jest.
|
|
133
133
|
// Reuse the Maestro discovery path: scan local dev servers.
|
|
134
134
|
// and `rnx open` the first match.
|
|
135
135
|
if (!noLaunch) {
|
|
@@ -227,7 +227,7 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
227
227
|
|
|
228
228
|
// jest owns the browsers, and it only reaps them from its own signal and exit
|
|
229
229
|
// handlers. this process is the one a caller has a pid for, so a SIGTERM aimed
|
|
230
|
-
// at `
|
|
230
|
+
// at `rnx detox` lands here and jest never hears about it: it is a
|
|
231
231
|
// grandchild (this -> npx -> jest), so it keeps running with no parent and
|
|
232
232
|
// every chrome it launched is orphaned for good. forward the signal instead of
|
|
233
233
|
// exiting out from under it, and let its exit code end this process.
|
|
@@ -362,8 +362,8 @@ function initDetoxProject(): void {
|
|
|
362
362
|
writeFileSync(
|
|
363
363
|
configPath,
|
|
364
364
|
`// rnx detox jest config.
|
|
365
|
-
// extends the
|
|
366
|
-
// to the
|
|
365
|
+
// extends the rnx preset, which rewrites \`import ... from 'detox'\`
|
|
366
|
+
// to the rnx driver and keeps your jest transforms intact.
|
|
367
367
|
|
|
368
368
|
/** @type {import('@jest/types').Config.InitialOptions} */
|
|
369
369
|
module.exports = {
|
package/cli/commands/diagnose.ts
CHANGED
package/cli/commands/electron.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// rnx desktop — launch the installed desktop companion via the
|
|
2
2
|
// electron driver. the driver wraps findDesktopCompanion +
|
|
3
3
|
// launchDesktopCompanion, so this command is a thin CLI surface that
|
|
4
4
|
// parses options, prompts for install, and delegates everything else.
|
package/cli/commands/film.ts
CHANGED
|
@@ -148,7 +148,7 @@ examples:
|
|
|
148
148
|
)
|
|
149
149
|
if (!started) {
|
|
150
150
|
console.error(
|
|
151
|
-
' film: engine recording store did not start (is @
|
|
151
|
+
' film: engine recording store did not start (is @rnx/plugin-recording installed?)',
|
|
152
152
|
)
|
|
153
153
|
return 1
|
|
154
154
|
}
|
package/cli/commands/flow.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// shared Maestro YAML playback and live-authoring implementation.
|
|
2
|
-
// discovers
|
|
2
|
+
// discovers an rnx target when needed, then reuses the shared bridge action layer
|
|
3
3
|
|
|
4
4
|
import * as fs from 'fs'
|
|
5
5
|
import { createHash, randomUUID } from 'node:crypto'
|
|
@@ -82,7 +82,7 @@ export async function resolveAppFrontmatterTarget(
|
|
|
82
82
|
return { target: trimmed }
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// discover
|
|
85
|
+
// discover an rnx shell URL — prefer patched servers, fall back to any match.
|
|
86
86
|
// exported so `detox.ts` and `maestro.ts` can reuse the same discovery logic.
|
|
87
87
|
export async function discoverSootsimUrl(): Promise<string | null> {
|
|
88
88
|
const servers = await scanDevServers()
|
|
@@ -143,7 +143,7 @@ options:
|
|
|
143
143
|
--preview-origin <url>
|
|
144
144
|
upload target for --preview (default: auto)
|
|
145
145
|
--preview-public-origin <url>
|
|
146
|
-
public link origin for --preview (default:
|
|
146
|
+
public link origin for --preview (default: rnxsim.com
|
|
147
147
|
for prod uploads)
|
|
148
148
|
--billing-kind test_run
|
|
149
149
|
meter the chained preview upload as a test run
|
|
@@ -371,7 +371,7 @@ flow extension:
|
|
|
371
371
|
) {
|
|
372
372
|
const discovered = await discoverSootsimUrl()
|
|
373
373
|
if (!discovered) {
|
|
374
|
-
console.error(' error: no current sim and no
|
|
374
|
+
console.error(' error: no current sim and no rnx target found')
|
|
375
375
|
rnxExit(1)
|
|
376
376
|
}
|
|
377
377
|
openTarget = discovered
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// transport-agnostic
|
|
1
|
+
// transport-agnostic rnx inspect-verb kernels.
|
|
2
2
|
//
|
|
3
3
|
// each function here sends the smallest bridge command its verb needs and
|
|
4
4
|
// returns *structured data* — no arg parsing or stdout. the
|
|
@@ -29,7 +29,7 @@ import type {
|
|
|
29
29
|
SootSimTimelineQuery,
|
|
30
30
|
SootSimTimelineQueryResult,
|
|
31
31
|
SootSimTimelineSummary,
|
|
32
|
-
} from '@
|
|
32
|
+
} from '@rnx/globals'
|
|
33
33
|
|
|
34
34
|
// resolve a `--max-ms <n>` flag, tolerating the spellings agents and humans
|
|
35
35
|
// actually type (`--maxMs`, `--maxms`, `--max_ms`). a silently-ignored
|
|
@@ -48,8 +48,9 @@ export function resolveMaxMsFlag(args: string[], fallbackMs: number): number {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// the minimal bridge an inspect kernel needs: a JSON command channel into a
|
|
51
|
-
//
|
|
51
|
+
// rnx instance.
|
|
52
52
|
export interface InspectBridge {
|
|
53
|
+
readonly plane?: 'local' | 'cloud'
|
|
53
54
|
send(
|
|
54
55
|
cmd: { type: string; [k: string]: any },
|
|
55
56
|
opts?: { timeoutMs?: number },
|
|
@@ -285,7 +286,7 @@ export async function inspectDescribe(
|
|
|
285
286
|
// fields with `styling:true` / `--styling`.
|
|
286
287
|
//
|
|
287
288
|
// the SAME LayoutElement shape is produced by both preview surfaces:
|
|
288
|
-
// - native/
|
|
289
|
+
// - native/rnx — `LAYOUT_GET_NATIVE_EVAL` reads the engine node tree via
|
|
289
290
|
// __sootsimTest.listInspectable() (the source `describe` uses), where each
|
|
290
291
|
// SootSimInspectInfo already carries absolute layout and optional style /
|
|
291
292
|
// box-model data.
|
|
@@ -703,7 +704,7 @@ export function layoutGetNativeEval(opts: LayoutExtractionOptions = {}): string
|
|
|
703
704
|
export const LAYOUT_GET_NATIVE_EVAL = layoutGetNativeEval()
|
|
704
705
|
|
|
705
706
|
// `get-layout` (native) — bounding boxes for visible meaningful elements on the
|
|
706
|
-
//
|
|
707
|
+
// rnx/native preview. transport-agnostic: the CLI runs it over its WS
|
|
707
708
|
// bridge, the agent runs it over its in-process preview bridge.
|
|
708
709
|
export async function inspectGetLayout(
|
|
709
710
|
bridge: InspectBridge,
|
|
@@ -1128,7 +1129,10 @@ export async function inspectWaitSelector(
|
|
|
1128
1129
|
{
|
|
1129
1130
|
type: 'waitFor',
|
|
1130
1131
|
waitForOptions: {
|
|
1131
|
-
condition: {
|
|
1132
|
+
condition: {
|
|
1133
|
+
type: gone ? 'absent' : 'present',
|
|
1134
|
+
selector: { testID: testId },
|
|
1135
|
+
},
|
|
1132
1136
|
timeoutMs,
|
|
1133
1137
|
},
|
|
1134
1138
|
},
|
|
@@ -1352,6 +1356,51 @@ export interface LogFilterOptions {
|
|
|
1352
1356
|
// not installed yet, return an empty snapshot so validation tools don't turn a
|
|
1353
1357
|
// missing diagnostics buffer into the app failure being diagnosed.
|
|
1354
1358
|
export async function inspectLogs(bridge: InspectBridge): Promise<LogEntry[]> {
|
|
1359
|
+
if (bridge.plane === 'cloud') {
|
|
1360
|
+
const state: unknown = await bridge.send({
|
|
1361
|
+
type: 'state',
|
|
1362
|
+
stateOptions: {
|
|
1363
|
+
screenshot: false,
|
|
1364
|
+
tree: false,
|
|
1365
|
+
route: false,
|
|
1366
|
+
errors: false,
|
|
1367
|
+
logs: true,
|
|
1368
|
+
logLimit: 100,
|
|
1369
|
+
},
|
|
1370
|
+
})
|
|
1371
|
+
const recentLogs =
|
|
1372
|
+
state &&
|
|
1373
|
+
typeof state === 'object' &&
|
|
1374
|
+
Array.isArray(Reflect.get(state, 'recentLogs'))
|
|
1375
|
+
? Reflect.get(state, 'recentLogs')
|
|
1376
|
+
: []
|
|
1377
|
+
return recentLogs.flatMap((entry: unknown, index: number) => {
|
|
1378
|
+
if (!entry || typeof entry !== 'object') return []
|
|
1379
|
+
const text = Reflect.get(entry, 'text')
|
|
1380
|
+
const rawLevel = Reflect.get(entry, 'level')
|
|
1381
|
+
if (typeof text !== 'string') return []
|
|
1382
|
+
const level: LogLevel =
|
|
1383
|
+
rawLevel === 'log' ||
|
|
1384
|
+
rawLevel === 'info' ||
|
|
1385
|
+
rawLevel === 'warn' ||
|
|
1386
|
+
rawLevel === 'error' ||
|
|
1387
|
+
rawLevel === 'debug'
|
|
1388
|
+
? rawLevel
|
|
1389
|
+
: 'error'
|
|
1390
|
+
const at = Reflect.get(entry, 'at')
|
|
1391
|
+
const ts = typeof at === 'number' && Number.isFinite(at) ? at : 0
|
|
1392
|
+
return [
|
|
1393
|
+
{
|
|
1394
|
+
id: `cloud-${ts}-${index}`,
|
|
1395
|
+
source: 'rnx-cloud',
|
|
1396
|
+
level,
|
|
1397
|
+
ts,
|
|
1398
|
+
args: [text],
|
|
1399
|
+
stack: null,
|
|
1400
|
+
},
|
|
1401
|
+
]
|
|
1402
|
+
})
|
|
1403
|
+
}
|
|
1355
1404
|
const res = (await bridge.send({
|
|
1356
1405
|
type: 'evaluate',
|
|
1357
1406
|
code: `(() => {
|
|
@@ -1402,7 +1451,9 @@ export function filterLogEntries(
|
|
|
1402
1451
|
if (!opts.showInternal) {
|
|
1403
1452
|
out = out.filter((e) => {
|
|
1404
1453
|
const first = e.args[0]
|
|
1405
|
-
return !(
|
|
1454
|
+
return !(
|
|
1455
|
+
typeof first === 'string' && /^\[(?:rnx|sootsim)(?:\s[^\]]*)?\]/.test(first)
|
|
1456
|
+
)
|
|
1406
1457
|
})
|
|
1407
1458
|
}
|
|
1408
1459
|
if (opts.level) out = out.filter((e) => opts.level!.has(e.level))
|
|
@@ -13,10 +13,12 @@ const AGENT_ENV_VARS = [
|
|
|
13
13
|
'AIDER_MODEL',
|
|
14
14
|
]
|
|
15
15
|
|
|
16
|
-
export function isAgentEnv(
|
|
17
|
-
|
|
16
|
+
export function isAgentEnv(
|
|
17
|
+
env: Readonly<Record<string, string | undefined>> = process.env,
|
|
18
|
+
): boolean {
|
|
19
|
+
if (env.RNX_AGENT === '0') return false
|
|
18
20
|
for (const name of AGENT_ENV_VARS) {
|
|
19
|
-
const v =
|
|
21
|
+
const v = env[name]
|
|
20
22
|
if (v && v.trim() && v !== '0') return true
|
|
21
23
|
}
|
|
22
24
|
return false
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type WsBridge,
|
|
13
13
|
} from '../../ws-bridge'
|
|
14
14
|
import { getShellState, inspectWaitReady, isShellCommandUnavailable } from './core'
|
|
15
|
-
import type { SootSimScreenTransitionWaitResult } from '@
|
|
15
|
+
import type { SootSimScreenTransitionWaitResult } from '@rnx/globals'
|
|
16
16
|
|
|
17
17
|
// shell-state helpers live in the transport-agnostic kernel module so the CLI
|
|
18
18
|
// and the in-browser agent share one implementation. re-exported here so
|
|
@@ -93,6 +93,7 @@ export async function maybeWaitForStartedScreenTransitions(
|
|
|
93
93
|
bridge: WsBridge,
|
|
94
94
|
opts: { verbose?: boolean } = {},
|
|
95
95
|
) {
|
|
96
|
+
if (bridge.plane === 'cloud') return
|
|
96
97
|
try {
|
|
97
98
|
const result = await callTestBridge<SootSimScreenTransitionWaitResult>(
|
|
98
99
|
bridge,
|
|
@@ -33,13 +33,23 @@ export async function runWaitReadySubcommand(opts: {
|
|
|
33
33
|
errors,
|
|
34
34
|
bridgeError,
|
|
35
35
|
} = await inspectWaitReady(bridge, timeoutMs, {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
...(bridge.plane !== 'cloud'
|
|
37
|
+
? {
|
|
38
|
+
onProgress(status) {
|
|
39
|
+
console.error(
|
|
40
|
+
` still waiting after ${status.elapsedMs}ms — ${waitReadyReason(status)} (nodes: ${status.nodes}, targets: ${status.targets}, live surfaces: ${status.liveFrameActive ? status.liveFrameChannels : 0}, errors: ${status.errors})`,
|
|
41
|
+
)
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
: {}),
|
|
41
45
|
})
|
|
42
46
|
if (ready) {
|
|
47
|
+
if (bridge.plane === 'cloud') {
|
|
48
|
+
console.log(
|
|
49
|
+
` ready in ${elapsedMs}ms: ${nodes} nodes, ${targets} targets${liveFrameActive ? `, ${liveFrameChannels} live surfaces` : ''}`,
|
|
50
|
+
)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
43
53
|
const remainingMs = timeoutMs - elapsedMs
|
|
44
54
|
const idleBudgetMs = Math.max(100, Math.min(10_000, remainingMs))
|
|
45
55
|
const idle = await waitForSootsimIdle({
|
package/cli/commands/inspect.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
// connects to the
|
|
1
|
+
// rnx inspect — dump node tree, accessibility info, query nodes
|
|
2
|
+
// connects to the rnx WS bridge (no Playwright)
|
|
3
3
|
|
|
4
|
-
import { getCliInspectVerbMeta } from '@
|
|
5
|
-
import { renderCliCommandHelp, renderCliGroupHelp } from '@
|
|
4
|
+
import { getCliInspectVerbMeta } from '@rnx/skills/cli'
|
|
5
|
+
import { renderCliCommandHelp, renderCliGroupHelp } from '@rnx/skills/cli/renderers'
|
|
6
6
|
import { DEFAULT_SOOTSIM_BRIDGE_PORT } from '../../src/bridge-constants'
|
|
7
7
|
import { DEFAULT_SOOTSIM_SHELL_URL } from '../../src/cli-constants'
|
|
8
8
|
import { resetGuestAppStateEval } from '../app-state-reset'
|
|
@@ -105,7 +105,7 @@ import type {
|
|
|
105
105
|
SootSimRequestEntry,
|
|
106
106
|
SootSimScreenTransitionWaitResult,
|
|
107
107
|
SootSimScrollPerformanceTrace,
|
|
108
|
-
} from '@
|
|
108
|
+
} from '@rnx/globals'
|
|
109
109
|
|
|
110
110
|
interface InspectOptions {
|
|
111
111
|
port?: number
|
|
@@ -1112,7 +1112,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
1112
1112
|
` usage: ${inspectCommand(name)}${tail ? ` ${tail}` : ''}`
|
|
1113
1113
|
|
|
1114
1114
|
if (!subcommand || args.includes('--help') || args.includes('-h')) {
|
|
1115
|
-
// all help output comes from the registry in packages/
|
|
1115
|
+
// all help output comes from the registry in packages/rnx-skills
|
|
1116
1116
|
// so `rnx do --help`, `rnx get --help`, the website docs, and
|
|
1117
1117
|
// the Contrast skill markdown stay in lock-step. a drift check in
|
|
1118
1118
|
// test/sootsimCliRegistry.test.ts fails CI if a verb lands without a
|
|
@@ -1183,7 +1183,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
1183
1183
|
}
|
|
1184
1184
|
|
|
1185
1185
|
// `rnx list --drivers` — pure local registry view, no bridge needed.
|
|
1186
|
-
// short-circuit here so users can discover drivers even when no
|
|
1186
|
+
// short-circuit here so users can discover drivers even when no rnx
|
|
1187
1187
|
// session is running.
|
|
1188
1188
|
if (
|
|
1189
1189
|
subcommand === 'list' &&
|
|
@@ -1840,6 +1840,28 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
1840
1840
|
console.error(' resolves testID, id, then text — dumps full node info as JSON')
|
|
1841
1841
|
rnxExit(1)
|
|
1842
1842
|
}
|
|
1843
|
+
if (bridge.plane === 'cloud') {
|
|
1844
|
+
const byTestId = await inspectFind(bridge, { testId: matcher })
|
|
1845
|
+
const byText = byTestId?.result
|
|
1846
|
+
? null
|
|
1847
|
+
: await inspectFind(bridge, { text: matcher })
|
|
1848
|
+
const node = byTestId?.result ?? byText?.result ?? null
|
|
1849
|
+
console.log(
|
|
1850
|
+
JSON.stringify(
|
|
1851
|
+
node
|
|
1852
|
+
? {
|
|
1853
|
+
matcher,
|
|
1854
|
+
found: true,
|
|
1855
|
+
resolvedVia: byTestId?.result ? 'testID' : 'text',
|
|
1856
|
+
node,
|
|
1857
|
+
}
|
|
1858
|
+
: { matcher, found: false },
|
|
1859
|
+
null,
|
|
1860
|
+
2,
|
|
1861
|
+
),
|
|
1862
|
+
)
|
|
1863
|
+
break
|
|
1864
|
+
}
|
|
1843
1865
|
// resolve in order: testID → id → text. first hit wins, includes the
|
|
1844
1866
|
// resolved transform and a parent chain so agents can skip the usual
|
|
1845
1867
|
// __sootsimRoot walk.
|
|
@@ -3607,7 +3629,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
3607
3629
|
if (!code) {
|
|
3608
3630
|
console.error(inspectUsage('js', '<javascript>'))
|
|
3609
3631
|
console.error('')
|
|
3610
|
-
console.error(' runs the snippet in the engine realm.
|
|
3632
|
+
console.error(' runs the snippet in the engine realm. rnx is the')
|
|
3611
3633
|
console.error(' canonical state surface — reach into it directly.')
|
|
3612
3634
|
console.error('')
|
|
3613
3635
|
console.error(' examples:')
|
|
@@ -3679,7 +3701,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
3679
3701
|
}
|
|
3680
3702
|
|
|
3681
3703
|
case 'globals': {
|
|
3682
|
-
// show all available
|
|
3704
|
+
// show all available rnx globals and their methods
|
|
3683
3705
|
const info = await bridge.send({
|
|
3684
3706
|
type: 'evaluate',
|
|
3685
3707
|
code: `(async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
//
|
|
1
|
+
// rnx desktop install — download and install the optional desktop GUI
|
|
2
2
|
//
|
|
3
|
-
// the CLI + daemon are the canonical
|
|
3
|
+
// the CLI + daemon are the canonical rnx surface; this command is only
|
|
4
4
|
// for users who want the Electron-based GUI companion on top of that.
|
|
5
5
|
//
|
|
6
6
|
// fetches the right platform artifact from https://contrast.dev/api/electron-release/
|
package/cli/commands/maestro.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// users with an existing `.maestro/` directory can swap `maestro test`
|
|
4
4
|
// for `rnx maestro test` and have their flows execute against
|
|
5
|
-
//
|
|
5
|
+
// rnx's in-browser engine. the heavy lifting lives in `flow.ts` +
|
|
6
6
|
// `bridge-flow-runner.ts`; this file just re-maps the maestro argv shape
|
|
7
7
|
// onto the existing flow runner and handles directory iteration + init.
|
|
8
8
|
|
|
@@ -102,7 +102,7 @@ partial:
|
|
|
102
102
|
clearKeychain — warn-only, rnx has no keychain surface.
|
|
103
103
|
when.platform — rnx reports iOS for this runner.
|
|
104
104
|
openLink: app routing works; no OS-level Safari fallback.
|
|
105
|
-
launchApp.resetRuntime —
|
|
105
|
+
launchApp.resetRuntime — rnx-only extension for recorded benchmarks:
|
|
106
106
|
keep tenant storage/auth, but soft-reload the guest app inside launchApp.
|
|
107
107
|
takeScreenshot — maestro string form works unchanged; rnx also accepts
|
|
108
108
|
{ path, withFrame } for framed export.
|
|
@@ -402,7 +402,7 @@ export async function runMaestro(
|
|
|
402
402
|
// one. sending it on as `--url` made it an app target instead, which left
|
|
403
403
|
// the playback bridge on whichever world resolveBridgeWorld picks by
|
|
404
404
|
// default (a live Vite dev shell selected from the development registry).
|
|
405
|
-
// so `
|
|
405
|
+
// so `rnx --port <installed daemon> maestro …` silently ran the flow
|
|
406
406
|
// against the dev engine, then failed trying to resolve a bundle at the
|
|
407
407
|
// bridge port. an explicit command-level `--port` still wins, since
|
|
408
408
|
// parseBridgeCliArgs takes the last occurrence.
|