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.
Files changed (138) hide show
  1. package/README.md +19 -6
  2. package/cli/app-fonts.ts +1 -1
  3. package/cli/bin.ts +118 -26
  4. package/cli/bridge-diagnostics.ts +1 -1
  5. package/cli/bridge-flow-runner.ts +9 -9
  6. package/cli/browser-evals.ts +3 -3
  7. package/cli/cli-update.ts +261 -0
  8. package/cli/cloud-client.ts +407 -0
  9. package/cli/cloud-dispatch.ts +165 -0
  10. package/cli/cloud-session.ts +156 -0
  11. package/cli/command-contract.ts +2 -2
  12. package/cli/command-registry.ts +9 -0
  13. package/cli/commands/agent-wrapper.ts +4 -4
  14. package/cli/commands/agent.ts +3 -3
  15. package/cli/commands/box/checkout-plane.ts +16 -8
  16. package/cli/commands/box/rnx-command.ts +2 -2
  17. package/cli/commands/box/search-command.ts +2 -2
  18. package/cli/commands/box.ts +123 -70
  19. package/cli/commands/config.ts +3 -2
  20. package/cli/commands/control.ts +6 -6
  21. package/cli/commands/cpu-profile.ts +2 -2
  22. package/cli/commands/daemon.ts +7 -7
  23. package/cli/commands/debug.ts +2 -2
  24. package/cli/commands/detox.ts +7 -7
  25. package/cli/commands/diagnose.ts +1 -1
  26. package/cli/commands/electron.ts +1 -1
  27. package/cli/commands/film.ts +1 -1
  28. package/cli/commands/flow.ts +4 -4
  29. package/cli/commands/inspect/core.ts +58 -7
  30. package/cli/commands/inspect/env.ts +5 -3
  31. package/cli/commands/inspect/shared.ts +2 -1
  32. package/cli/commands/inspect/wait-event.ts +1 -1
  33. package/cli/commands/inspect/wait-ready.ts +15 -5
  34. package/cli/commands/inspect.ts +31 -9
  35. package/cli/commands/install-desktop.ts +2 -2
  36. package/cli/commands/maestro.ts +3 -3
  37. package/cli/commands/permissions.ts +1 -1
  38. package/cli/commands/platform.ts +91 -0
  39. package/cli/commands/record.ts +3 -3
  40. package/cli/commands/report-issue.ts +1 -1
  41. package/cli/commands/runtime.ts +1 -1
  42. package/cli/commands/screenshot-mode.ts +1 -1
  43. package/cli/commands/screenshot.ts +25 -0
  44. package/cli/commands/serve.ts +1 -1
  45. package/cli/commands/skills.ts +1 -1
  46. package/cli/commands/timeline.ts +1 -1
  47. package/cli/commands/upgrade.ts +8 -34
  48. package/cli/commands/upload.ts +3 -3
  49. package/cli/commands/version.ts +14 -3
  50. package/cli/commands/what-happened.ts +2 -2
  51. package/cli/current-sim.ts +43 -30
  52. package/cli/desktop-companion.ts +1 -1
  53. package/cli/drivers/electron.ts +1 -1
  54. package/cli/drivers/playwright.ts +3 -3
  55. package/cli/drivers/types.ts +4 -4
  56. package/cli/flow-file.ts +3 -3
  57. package/cli/help-core.ts +18 -11
  58. package/cli/hidden-runtime-alias.ts +1 -1
  59. package/cli/hints.ts +2 -2
  60. package/cli/maestro-js.ts +2 -2
  61. package/cli/outbound-endpoints.ts +40 -6
  62. package/cli/parse-args.ts +1 -1
  63. package/cli/registry.ts +1 -1
  64. package/cli/resolve-assets.ts +2 -2
  65. package/cli/secure-file.ts +31 -0
  66. package/cli/shell-init.ts +98 -0
  67. package/cli/ws-bridge.ts +18 -8
  68. package/detox/expectations.ts +1 -1
  69. package/detox/gestures.ts +3 -3
  70. package/detox/index.ts +3 -3
  71. package/dist-lib/agent-daemon-client.cjs +1 -1
  72. package/dist-lib/agent-events.cjs +1 -1
  73. package/dist-lib/agent-identity.cjs +1 -1
  74. package/dist-lib/agent-sessions.cjs +1 -1
  75. package/dist-lib/attached-projects.cjs +1 -1
  76. package/dist-lib/auth/shared-session.cjs +1 -1
  77. package/dist-lib/backend-origin.cjs +1 -1
  78. package/dist-lib/beta.cjs +1 -1
  79. package/dist-lib/beta.mjs +1 -1
  80. package/dist-lib/bridge-constants.cjs +1 -1
  81. package/dist-lib/bridge-contract-input.cjs +15 -8
  82. package/dist-lib/bridge-contract-input.mjs +15 -8
  83. package/dist-lib/bridge-contract.cjs +1 -1
  84. package/dist-lib/bridge-contract.mjs +1 -1
  85. package/dist-lib/capture-contract.cjs +107 -0
  86. package/dist-lib/capture-contract.mjs +80 -0
  87. package/dist-lib/cli-constants.cjs +1 -1
  88. package/dist-lib/cloud-contract.cjs +32 -0
  89. package/dist-lib/cloud-contract.mjs +7 -0
  90. package/dist-lib/config.cjs +1 -1
  91. package/dist-lib/detox/index.cjs +4 -4
  92. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  93. package/dist-lib/home-paths.cjs +9 -1
  94. package/dist-lib/host/bridge-host.cjs +15 -10
  95. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  96. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  97. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  98. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  99. package/dist-lib/host/websocket-proxy.cjs +1 -1
  100. package/dist-lib/index.cjs +248 -73
  101. package/dist-lib/jump-to-source-babel.cjs +1 -1
  102. package/dist-lib/menu.cjs +1 -1
  103. package/dist-lib/menu.mjs +1 -1
  104. package/dist-lib/metro-production-bundle.cjs +1 -1
  105. package/dist-lib/metro-production-bundle.mjs +1 -1
  106. package/dist-lib/metro.cjs +1 -1
  107. package/dist-lib/profiles.cjs +1 -1
  108. package/dist-lib/public-brand.cjs +1 -1
  109. package/dist-lib/react-native-host-modules.cjs +1 -1
  110. package/dist-lib/react-native-host-modules.mjs +1 -1
  111. package/dist-lib/render-mode.cjs +1 -1
  112. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  113. package/dist-lib/sdk.cjs +44 -6
  114. package/dist-lib/sdk.mjs +44 -6
  115. package/dist-lib/skills.cjs +435 -155
  116. package/dist-lib/vite.cjs +1 -1
  117. package/package.json +20 -4
  118. package/scripts/dev-server-scanner.ts +3 -3
  119. package/skills/contrast/SKILL.md +1 -1
  120. package/skills/rnx-perf/SKILL.md +68 -52
  121. package/src/app-url.ts +2 -3
  122. package/src/bridge-contract-input.ts +17 -6
  123. package/src/bridge-contract.ts +25 -4
  124. package/src/capture-contract.ts +423 -0
  125. package/src/cloud-contract.ts +15 -0
  126. package/src/home-paths.ts +9 -3
  127. package/src/host/agent-host.ts +1 -1
  128. package/src/host/bridge-host.ts +13 -11
  129. package/src/host/plane-host.ts +2 -2
  130. package/src/index.ts +21 -0
  131. package/src/native-dev-bundle-url.ts +2 -2
  132. package/src/screenshots/orchestrate.ts +1 -1
  133. package/src/screenshots/org-deck.ts +1 -1
  134. package/src/settings-bundle.ts +1 -1
  135. package/src/sim-client.ts +4 -0
  136. package/src/vite-plugin-one.ts +2 -2
  137. package/src/vite-plugin.ts +1 -1
  138. package/cli/commands/box/cloud-box.ts +0 -294
@@ -9,8 +9,9 @@ usage:
9
9
  rnx config telemetry [on|off|status]
10
10
 
11
11
  environment:
12
- RNX_HINTS=off suppress CLI hints
13
- RNX_HINTS=always show every CLI hint every time
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
 
@@ -339,7 +339,7 @@ function buildShellPathOpenUrl(target: string, baseUrl: string): string {
339
339
  return url.toString()
340
340
  }
341
341
 
342
- // SootSim share/preview/build links (e.g. https://sootsim.com/preview/<id>,
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 sootsim on :3000; the bridge/daemon and the
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 `sootsim close` SIGTERM the shared vite shell dev server, taking the
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
- // sootsim playwright host launcher.
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 sootsim's HmrClient — expo CLI's createBundleUrlSearchParams
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 sootsim couldn't until now), so without this flip the
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 sootsim runs the guest app in the tenant worker,
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 sootsim tab runs in plain Chrome without a debug port.
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):
@@ -1,8 +1,8 @@
1
- // rnx daemon — manage the persistent sootsim bridge daemon
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 `sootsim` binary resolves at
5
- // registration time, so `npm update -g sootsim` + `rnx daemon restart`
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 sootsim entry script as an argument.
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 sootsim to run on.
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 `sootsim` looks
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 sootsim` tears down the daemon before
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[] = []
@@ -1,6 +1,6 @@
1
1
  // rnx debug — drive __sootsimDebug.* from the terminal
2
2
  //
3
- // connects to a running sootsim instance via the WebSocket bridge and forwards
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. SootSim
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")
@@ -1,10 +1,10 @@
1
- // sootsim detox — run existing detox suites against sootsim
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 a sootsim shell is reachable (auto-launches one if not)
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 sootsim driver
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 a sootsim shell is reachable before running jest.
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 `sootsim detox` lands here and jest never hears about it: it is a
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 sootsim preset, which rewrites \`import ... from 'detox'\`
366
- // to the sootsim driver and keeps your jest transforms intact.
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 = {
@@ -32,7 +32,7 @@ import type {
32
32
  SootSimTimelineEvent,
33
33
  SootSimTimelineQuery,
34
34
  SootSimTimelineQueryResult,
35
- } from '@sootsim/globals'
35
+ } from '@rnx/globals'
36
36
 
37
37
  interface DiagnoseOptions {
38
38
  port?: number
@@ -1,4 +1,4 @@
1
- // sootsim desktop — launch the installed desktop companion via the
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.
@@ -148,7 +148,7 @@ examples:
148
148
  )
149
149
  if (!started) {
150
150
  console.error(
151
- ' film: engine recording store did not start (is @sootsim/plugin-recording installed?)',
151
+ ' film: engine recording store did not start (is @rnx/plugin-recording installed?)',
152
152
  )
153
153
  return 1
154
154
  }
@@ -1,5 +1,5 @@
1
1
  // shared Maestro YAML playback and live-authoring implementation.
2
- // discovers a sootsim target when needed, then reuses the shared bridge action layer
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 a sootsim shell URL — prefer patched servers, fall back to any match.
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: sootsim.com
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 sootsim target found')
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 sootsim inspect-verb kernels.
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 '@sootsim/globals'
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
- // sootsim instance.
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/sootsim — `LAYOUT_GET_NATIVE_EVAL` reads the engine node tree via
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
- // sootsim/native preview. transport-agnostic: the CLI runs it over its WS
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: { type: gone ? 'absent' : 'present', testID: testId },
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 !(typeof first === 'string' && first.startsWith('[rnx]'))
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(): boolean {
17
- if (process.env.RNX_AGENT === '0') return false
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 = process.env[name]
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 '@sootsim/globals'
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,
@@ -26,7 +26,7 @@ import type {
26
26
  SootSimTimelineEvent,
27
27
  SootSimTimelineQuery,
28
28
  SootSimTimelineQueryResult,
29
- } from '@sootsim/globals'
29
+ } from '@rnx/globals'
30
30
 
31
31
  interface WaitEventOptions {
32
32
  bridge: WsBridge
@@ -33,13 +33,23 @@ export async function runWaitReadySubcommand(opts: {
33
33
  errors,
34
34
  bridgeError,
35
35
  } = await inspectWaitReady(bridge, timeoutMs, {
36
- onProgress(status) {
37
- console.error(
38
- ` still waiting after ${status.elapsedMs}ms — ${waitReadyReason(status)} (nodes: ${status.nodes}, targets: ${status.targets}, live surfaces: ${status.liveFrameActive ? status.liveFrameChannels : 0}, errors: ${status.errors})`,
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({
@@ -1,8 +1,8 @@
1
- // sootsim inspect — dump node tree, accessibility info, query nodes
2
- // connects to the sootsim WS bridge (no Playwright)
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 '@sootsim/skills/cli'
5
- import { renderCliCommandHelp, renderCliGroupHelp } from '@sootsim/skills/cli/renderers'
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 '@sootsim/globals'
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/sootsim-skills
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 sootsim
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. SootSim is the')
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 sootsim globals and their methods
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
- // sootsim desktop install — download and install the optional desktop GUI
1
+ // rnx desktop install — download and install the optional desktop GUI
2
2
  //
3
- // the CLI + daemon are the canonical sootsim surface; this command is only
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/
@@ -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
- // sootsim's in-browser engine. the heavy lifting lives in `flow.ts` +
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 — sootsim-only extension for recorded benchmarks:
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 `sootsim --port <installed daemon> maestro …` silently ran the flow
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.
@@ -10,7 +10,7 @@ import type {
10
10
  SystemPermissionKind,
11
11
  SystemPermissionMutation,
12
12
  SystemPermissionStatus,
13
- } from '@sootsim/globals'
13
+ } from '@rnx/globals'
14
14
 
15
15
  interface PermissionsOptions {
16
16
  port?: number