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
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.381 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.382 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1808,9 +1808,9 @@ var init_registry = __esm({
1808
1808
  coverage: 0.97,
1809
1809
  coverageSource: "estimated",
1810
1810
  capabilityCoverage: CAPABILITY_INVENTORY_PACKAGE_COVERAGE["react-native-reanimated"],
1811
- note: "core animations, gestures, scroll, layout transitions, keyboard, sensors, reduced motion, native view property reads, and direct static style snapshots",
1812
- working: "useSharedValue, useAnimatedStyle/Props/Reaction, useDerivedValue, useAnimatedScrollHandler, useScrollViewOffset, useFrameCallback, useAnimatedRef, useAnimatedKeyboard, useAnimatedSensor, useReducedMotion, entering/exiting/layout transitions, withTiming/Spring/Decay/Delay/Sequence/Repeat/Clamp, interpolate, interpolateColor, runOnJS/UI, measure, scrollTo, getViewProp (layout, opacity, zIndex, backgroundColor, boxShadow), direct setViewStyle snapshots, Easing, createAnimatedComponent, Animated.View/Text/Image/ScrollView/FlatList",
1813
- missing: "Reanimated 4 native CSS keyframes and transitions remain incomplete"
1811
+ note: "core animations, gestures, scroll, layout transitions, common CSS opacity and transform transitions and keyframes, keyboard, sensors, reduced motion, native view property reads, and direct static style snapshots",
1812
+ working: "useSharedValue, useAnimatedStyle/Props/Reaction, useDerivedValue, useAnimatedScrollHandler, useScrollViewOffset, useFrameCallback, useAnimatedRef, useAnimatedKeyboard, useAnimatedSensor, useReducedMotion, entering/exiting/layout transitions, common CSS opacity and transform transitions with continuous interruption and settled endpoints, opacity and transform keyframes with retained forwards fill, withTiming/Spring/Decay/Delay/Sequence/Repeat/Clamp, interpolate, interpolateColor, runOnJS/UI, measure, scrollTo, getViewProp (layout, opacity, zIndex, backgroundColor, boxShadow), direct setViewStyle snapshots, Easing, createAnimatedComponent, Animated.View/Text/Image/ScrollView/FlatList",
1813
+ missing: "Reanimated 4 CSS transitions and keyframes beyond opacity and transform remain incomplete"
1814
1814
  }
1815
1815
  ]
1816
1816
  },
@@ -1991,7 +1991,7 @@ var init_registry = __esm({
1991
1991
  capabilityCoverage: CAPABILITY_INVENTORY_PACKAGE_COVERAGE["react-native-keyboard-controller"],
1992
1992
  note: "native bindings stubbed (KeyboardControllerNative, KeyboardEvents, FocusedInputEvents, WindowDimensionsEvents, KeyboardControllerView + sibling native views); upstream pure-JS components, hooks, animated module, and KeyboardAvoidingView resolve from node_modules unchanged",
1993
1993
  working: "every upstream JS export runs through reanimated worklets; Android KeyboardGestureArea drives the existing keyboard owner through drag progress, velocity/distance settling, cancellation, focus retention, and cleanup; supported exports include KeyboardProvider, KeyboardAvoidingView, KeyboardStickyView, KeyboardAwareScrollView, KeyboardToolbar, KeyboardGestureArea, useReanimatedKeyboardAnimation, useKeyboardAnimation, useKeyboardHandler, useGenericKeyboardHandler, useKeyboardController, useKeyboardState, useKeyboardContext, useReanimatedFocusedInput, useFocusedInputHandler, useResizeMode, useWindowDimensions, KeyboardEvents, FocusedInputEvents, WindowDimensionsEvents resize events, KeyboardController (dismiss/setFocusTo/isVisible/state/preload/setInputMode/setDefaultMode), KeyboardControllerView, OverKeyboardView, KeyboardBackgroundView, KeyboardExtender, ClippingScrollView, KeyboardToolbarGroupView, and AndroidSoftInputModes",
1994
- missing: "preload is a noop; KeyboardGestureArea lacks its iOS textInputNativeID/offset effect; OverKeyboardView, KeyboardBackgroundView, and KeyboardExtender are passthrough views without their native platform effects"
1994
+ missing: "KeyboardGestureArea lacks its iOS textInputNativeID/offset effect; OverKeyboardView, KeyboardBackgroundView, and KeyboardExtender are passthrough views without their native platform effects"
1995
1995
  }
1996
1996
  ]
1997
1997
  },
@@ -3458,9 +3458,9 @@ var init_registry = __esm({
3458
3458
  {
3459
3459
  range: ">=13.0.0",
3460
3460
  coverage: 0.8,
3461
- note: 'native-seam: upstream expo-calendar JS runs from the bundle over an in-memory EventKit-shaped store seeded faithfully to a fresh iOS Simulator \u2014 a writable local "Calendar" on the "Default" local source plus the always-present read-only "Birthdays" calendar, so getCalendarsAsync/getDefaultCalendarAsync match what a real RN app sees on the conformance Simulator (no sootsim brand names leak into guest calendar pickers). create/read/update/delete then mutate that store like EKEventStore.',
3462
- working: 'reference-Simulator default calendar set (writable "Calendar" + read-only "Birthdays", faithful "Default" local source); real RN + sootsim validated event permission/create/read/update/range-query/attendee-read/delete smoke, upstream enums and JS validation, create/update/delete calendars, create/list/get/update/delete events with date-range filtering, attendee reads, create/list/get/update/delete reminders, open/edit dialog functions safely resolve, all permission functions',
3463
- missing: 'real EventKit / CalendarContract access (the in-memory store does not surface events the user actually owns), calendar sync across processes/reloads, native calendar UI dialogs (presentEvent/createEventDialog), recurrence rule expansion (RRULE), attendee response/availability semantics, exact per-iOS-version Simulator calendar set (the seeded "Calendar"/"Birthdays" pair is the stable baseline)'
3461
+ note: 'native-seam: upstream expo-calendar JS runs from the bundle over an in-memory EventKit-shaped store seeded faithfully to a fresh iOS Simulator \u2014 a writable local "Calendar" on the "Default" local source plus the always-present read-only "Birthdays" calendar, so getCalendarsAsync/getDefaultCalendarAsync match what a real RN app sees on the conformance Simulator (no sootsim brand names leak into guest calendar pickers). create/read/update/delete then mutate that store like EKEventStore. daily recurrence queries expand lazily inside the requested range and preserve local wall times across named-zone offset changes.',
3462
+ working: 'reference-Simulator default calendar set (writable "Calendar" + read-only "Birthdays", faithful "Default" local source); real RN + sootsim validated event permission/create/read/update/range-query/attendee-read/delete smoke; upstream enums and JS validation, create/update/delete calendars, create/list/get/update/delete events with date-range filtering; sootsim-validated bounded daily recurrence expansion with interval/count rules, inclusive overlap boundaries, named time zones, and whole-series update/delete; attendee reads, create/list/get/update/delete reminders, open/edit dialog functions safely resolve, all permission functions',
3463
+ missing: 'real EventKit / CalendarContract access (the in-memory store does not surface events the user actually owns), calendar sync across processes/reloads, native calendar UI dialogs (presentEvent/createEventDialog), weekly/monthly/yearly and advanced recurrence rule expansion, detached-instance and future-split mutation semantics, attendee response/availability semantics, exact per-iOS-version Simulator calendar set (the seeded "Calendar"/"Birthdays" pair is the stable baseline)'
3464
3464
  }
3465
3465
  ]
3466
3466
  },
@@ -14147,7 +14147,7 @@ function scanDepsWithData(pkg, data) {
14147
14147
  version,
14148
14148
  coverage: 0.3,
14149
14149
  status: "auto-stub",
14150
- note: "not yet verified; it may work through SootSim's generic native seam",
14150
+ note: "not yet verified; it may work through rnx's generic native seam",
14151
14151
  rnx
14152
14152
  });
14153
14153
  continue;
@@ -15689,6 +15689,7 @@ var init_cli_version = __esm({
15689
15689
  var home_paths_exports = {};
15690
15690
  __export(home_paths_exports, {
15691
15691
  ACTIVE_RUNTIME_FILE: () => ACTIVE_RUNTIME_FILE,
15692
+ CLI_UPDATE_FILE: () => CLI_UPDATE_FILE,
15692
15693
  CONFIG_FILE: () => CONFIG_FILE,
15693
15694
  DAEMON_HEARTBEAT_STALE_MS: () => DAEMON_HEARTBEAT_STALE_MS,
15694
15695
  DAEMON_LOCKFILE: () => DAEMON_LOCKFILE,
@@ -15699,6 +15700,7 @@ __export(home_paths_exports, {
15699
15700
  cacheDir: () => cacheDir,
15700
15701
  cameraFixturesDir: () => cameraFixturesDir,
15701
15702
  claimDaemonLockfile: () => claimDaemonLockfile,
15703
+ cliUpdateFilePath: () => cliUpdateFilePath,
15702
15704
  configFilePath: () => configFilePath,
15703
15705
  consumeRuntimeUpgradeNotice: () => consumeRuntimeUpgradeNotice,
15704
15706
  daemonAppBundlePath: () => daemonAppBundlePath,
@@ -15817,6 +15819,9 @@ function devBridgeLockfilePath(bridgePort) {
15817
15819
  function configFilePath() {
15818
15820
  return import_node_path4.default.join(rnxHomeDir(), CONFIG_FILE);
15819
15821
  }
15822
+ function cliUpdateFilePath() {
15823
+ return import_node_path4.default.join(rnxHomeDir(), CLI_UPDATE_FILE);
15824
+ }
15820
15825
  function readSharedConfig() {
15821
15826
  try {
15822
15827
  const raw = import_node_fs4.default.readFileSync(configFilePath(), "utf8");
@@ -16200,7 +16205,7 @@ function removeDevBridgeLockfile(owner) {
16200
16205
  } catch {
16201
16206
  }
16202
16207
  }
16203
- var import_node_crypto, import_node_fs4, import_node_os, import_node_path4, RNX_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, DEV_BRIDGES_DIR, CONFIG_FILE, DAEMON_HEARTBEAT_STALE_MS, CURRENT_ONBOARDING_VERSION, DAEMON_LOCKFILE_MAX_BYTES, DAEMON_CLAIM_STALE_MS;
16208
+ var import_node_crypto, import_node_fs4, import_node_os, import_node_path4, RNX_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, DEV_BRIDGES_DIR, CONFIG_FILE, CLI_UPDATE_FILE, DAEMON_HEARTBEAT_STALE_MS, CURRENT_ONBOARDING_VERSION, DAEMON_LOCKFILE_MAX_BYTES, DAEMON_CLAIM_STALE_MS;
16204
16209
  var init_home_paths = __esm({
16205
16210
  "src/home-paths.ts"() {
16206
16211
  "use strict";
@@ -16213,6 +16218,7 @@ var init_home_paths = __esm({
16213
16218
  DAEMON_LOCKFILE = "daemon.json";
16214
16219
  DEV_BRIDGES_DIR = "dev-bridges";
16215
16220
  CONFIG_FILE = "config.json";
16221
+ CLI_UPDATE_FILE = "cli-update.json";
16216
16222
  DAEMON_HEARTBEAT_STALE_MS = 3e4;
16217
16223
  CURRENT_ONBOARDING_VERSION = 1;
16218
16224
  DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
@@ -18560,7 +18566,7 @@ var init_open_url = __esm({
18560
18566
  }
18561
18567
  });
18562
18568
 
18563
- // ../contrast-harness/src/planeProtocol.ts
18569
+ // ../rnx-box/src/planeProtocol.ts
18564
18570
  function encodeBase64(bytes) {
18565
18571
  let binary = "";
18566
18572
  const CHUNK = 32768;
@@ -18635,47 +18641,47 @@ async function dispatchPlaneCommand(plane, type, msg, mutationOptions) {
18635
18641
  }
18636
18642
  var PLANE_FLAG_READ_ONLY, PLANE_FLAG_BINARY;
18637
18643
  var init_planeProtocol = __esm({
18638
- "../contrast-harness/src/planeProtocol.ts"() {
18644
+ "../rnx-box/src/planeProtocol.ts"() {
18639
18645
  "use strict";
18640
18646
  PLANE_FLAG_READ_ONLY = 1;
18641
18647
  PLANE_FLAG_BINARY = 2;
18642
18648
  }
18643
18649
  });
18644
18650
 
18645
- // ../contrast-harness/src/projectFileCache.ts
18651
+ // ../rnx-box/src/projectFileCache.ts
18646
18652
  var init_projectFileCache = __esm({
18647
- "../contrast-harness/src/projectFileCache.ts"() {
18653
+ "../rnx-box/src/projectFileCache.ts"() {
18648
18654
  "use strict";
18649
18655
  }
18650
18656
  });
18651
18657
 
18652
- // ../contrast-harness/src/planeClient.ts
18658
+ // ../rnx-box/src/planeClient.ts
18653
18659
  var init_planeClient = __esm({
18654
- "../contrast-harness/src/planeClient.ts"() {
18660
+ "../rnx-box/src/planeClient.ts"() {
18655
18661
  "use strict";
18656
18662
  init_planeProtocol();
18657
18663
  init_projectFileCache();
18658
18664
  }
18659
18665
  });
18660
18666
 
18661
- // ../contrast-harness/src/projectFilePlaneRpc.ts
18667
+ // ../rnx-box/src/projectFilePlaneRpc.ts
18662
18668
  var init_projectFilePlaneRpc = __esm({
18663
- "../contrast-harness/src/projectFilePlaneRpc.ts"() {
18669
+ "../rnx-box/src/projectFilePlaneRpc.ts"() {
18664
18670
  "use strict";
18665
18671
  init_planeProtocol();
18666
18672
  }
18667
18673
  });
18668
18674
 
18669
- // ../contrast-harness/src/planeTransport.browser.ts
18675
+ // ../rnx-box/src/planeTransport.browser.ts
18670
18676
  var init_planeTransport_browser = __esm({
18671
- "../contrast-harness/src/planeTransport.browser.ts"() {
18677
+ "../rnx-box/src/planeTransport.browser.ts"() {
18672
18678
  "use strict";
18673
18679
  }
18674
18680
  });
18675
18681
 
18676
- // ../contrast-harness/src/plane.ts
18682
+ // ../rnx-box/src/plane.ts
18677
18683
  var init_plane = __esm({
18678
- "../contrast-harness/src/plane.ts"() {
18684
+ "../rnx-box/src/plane.ts"() {
18679
18685
  "use strict";
18680
18686
  init_planeProtocol();
18681
18687
  init_planeClient();
@@ -18719,9 +18725,11 @@ var init_checkout_plane = __esm({
18719
18725
  // long a wedged git is waited on, never whether it is waited on, so proving
18720
18726
  // the kill does not have to cost the production deadline.
18721
18727
  listTimeoutMs;
18728
+ trackedOnly;
18722
18729
  constructor(root, options = {}) {
18723
18730
  this.root = import_node_path10.default.resolve(root);
18724
18731
  this.listTimeoutMs = options.listTimeoutMs ?? LIST_FILES_TIMEOUT_MS;
18732
+ this.trackedOnly = options.trackedOnly ?? false;
18725
18733
  }
18726
18734
  index() {
18727
18735
  return this.entries;
@@ -18729,7 +18737,8 @@ var init_checkout_plane = __esm({
18729
18737
  async refresh() {
18730
18738
  let stdout;
18731
18739
  try {
18732
- stdout = (0, import_node_child_process5.execFileSync)("git", ["ls-files", "-co", "--exclude-standard", "-z"], {
18740
+ const args = this.trackedOnly ? ["ls-files", "-z"] : ["ls-files", "-co", "--exclude-standard", "-z"];
18741
+ stdout = (0, import_node_child_process5.execFileSync)("git", args, {
18733
18742
  cwd: this.root,
18734
18743
  encoding: "utf8",
18735
18744
  maxBuffer: 64 * 1024 * 1024,
@@ -18749,7 +18758,7 @@ var init_checkout_plane = __esm({
18749
18758
  }
18750
18759
  if (stdout.length === 0) {
18751
18760
  throw new CheckoutRequiresGitError(
18752
- `git listed no files in ${this.root}. either git tracks nothing there and nothing untracked is visible to it, or this machine could not read the child process's output.`
18761
+ `git listed no ${this.trackedOnly ? "tracked " : ""}files in ${this.root}. either the checkout contains no eligible files, or this machine could not read the child process's output.`
18753
18762
  );
18754
18763
  }
18755
18764
  const next = /* @__PURE__ */ new Map();
@@ -19490,8 +19499,8 @@ var init_bridge_host = __esm({
19490
19499
  static USER_BOOT_LEASE_TTL_MS = 6e4;
19491
19500
  static SIM_RECONNECT_TTL_MS = 3e4;
19492
19501
  // abandoned-tab GC. the ws heartbeat only reaps sims whose socket went
19493
- // dead — but a background tab from a prior `sootsim open` / QA / test run
19494
- // keeps its socket alive (the page still pongs) forever, so `sootsim list`
19502
+ // dead — but a background tab from a prior `rnx open` / QA / test run
19503
+ // keeps its socket alive (the page still pongs) forever, so `rnx list`
19495
19504
  // accretes dozens of zombie sims that all time out on every command (QA
19496
19505
  // F21-3, carried F19-2). reap a sim only when it is provably nobody's:
19497
19506
  // not primary, not user-focused, no CLI attached, no active lease, and no
@@ -19499,7 +19508,7 @@ var init_bridge_host = __esm({
19499
19508
  // terminal 4001 code, so the client closes the abandoned window instead
19500
19509
  // of reconnecting the zombie straight back in.
19501
19510
  static SIM_IDLE_REAP_TTL_MS = 30 * 6e4;
19502
- // automation sims (spawned by the playwright driver: `sootsim open --driver
19511
+ // automation sims (spawned by the playwright driver: `rnx open --driver
19503
19512
  // playwright`, the factory/screenshot capture path — `meta.sootsimHostDriver
19504
19513
  // === 'playwright'`) are unattended one-shot browsers, never a human's tab. a
19505
19514
  // capture run opens one per slide/app/retry and the 30-min human/dev TTL lets
@@ -19529,7 +19538,7 @@ var init_bridge_host = __esm({
19529
19538
  // dropped, sim crashed) leave their server-side WebSocket sitting "open"
19530
19539
  // forever. ping every WS_HEARTBEAT_INTERVAL_MS; if the previous round's
19531
19540
  // ping was never answered, terminate(). that fires 'close' which runs the
19532
- // sim-cleanup path and stops `sootsim list` from showing 8 zombie
19541
+ // sim-cleanup path and stops `rnx list` from showing 8 zombie
19533
19542
  // sims that all time out on every command.
19534
19543
  wsHeartbeatTimer = null;
19535
19544
  wsIsAlive = /* @__PURE__ */ new WeakMap();
@@ -20733,7 +20742,7 @@ var init_bridge_host = __esm({
20733
20742
  this.sweepRestorableSims(now);
20734
20743
  this.reapIdleSims(now);
20735
20744
  }
20736
- // GC abandoned sims so `sootsim list` stays usable across long dev / QA /
20745
+ // GC abandoned sims so `rnx list` stays usable across long dev / QA /
20737
20746
  // test sessions (F21-3). conservative on purpose: a sim is only reaped
20738
20747
  // when every "someone cares about this" signal is absent.
20739
20748
  //
@@ -20749,7 +20758,7 @@ var init_bridge_host = __esm({
20749
20758
  // using is the primary and/or holds a fresh user-active lease; one that is
20750
20759
  // none of those and idle past the TTL is abandoned regardless of a stale
20751
20760
  // focus flag.
20752
- // automation sims are spawned by the playwright driver (`sootsim open
20761
+ // automation sims are spawned by the playwright driver (`rnx open
20753
20762
  // --driver playwright`, the factory/screenshot capture path), which tags them
20754
20763
  // with `meta.sootsimHostDriver === 'playwright'` via the host's addInitScript.
20755
20764
  // they are unattended one-shot browsers, never a human's tab, so they get a
@@ -21252,6 +21261,8 @@ var init_bridge_host = __esm({
21252
21261
  return "evaluated page state";
21253
21262
  case "screenshot":
21254
21263
  return "captured screenshot";
21264
+ case "capture":
21265
+ return "captured app screen";
21255
21266
  case "tap":
21256
21267
  return "sent tap event";
21257
21268
  case "keyboard":
@@ -21416,6 +21427,233 @@ var init_cli_constants = __esm({
21416
21427
  }
21417
21428
  });
21418
21429
 
21430
+ // src/cloud-contract.ts
21431
+ var RNX_CLOUD_MAX_ARTIFACT_BYTES;
21432
+ var init_cloud_contract = __esm({
21433
+ "src/cloud-contract.ts"() {
21434
+ "use strict";
21435
+ RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
21436
+ }
21437
+ });
21438
+
21439
+ // cli/cloud-session.ts
21440
+ function isRecord3(value) {
21441
+ return value !== null && typeof value === "object" && !Array.isArray(value);
21442
+ }
21443
+ function normalizeCloudOrigin(source) {
21444
+ let parsed;
21445
+ try {
21446
+ parsed = new URL(source);
21447
+ } catch {
21448
+ throw new Error(`invalid RNX Cloud origin: ${source}`);
21449
+ }
21450
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
21451
+ throw new Error(`RNX Cloud origin must be an HTTP(S) origin: ${source}`);
21452
+ }
21453
+ if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) {
21454
+ throw new Error(`RNX Cloud origin requires HTTPS outside loopback: ${source}`);
21455
+ }
21456
+ return parsed.origin;
21457
+ }
21458
+ function createCloudSession(input) {
21459
+ const session = {
21460
+ version: CLOUD_SESSION_VERSION,
21461
+ simId: input.simId.trim(),
21462
+ apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
21463
+ claimId: input.claimId.trim(),
21464
+ token: input.token
21465
+ };
21466
+ if (!session.simId || !session.claimId || !session.token) {
21467
+ throw new Error("cloud session requires simulator, origin, claim, and token");
21468
+ }
21469
+ return session;
21470
+ }
21471
+ function parseCloudSession(encoded) {
21472
+ const value = encoded.trim();
21473
+ if (!value || value.length > MAX_ENCODED_SESSION_BYTES || !/^[A-Za-z0-9_-]+$/.test(value)) {
21474
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21475
+ }
21476
+ let parsed;
21477
+ try {
21478
+ const bytes = Buffer.from(value, "base64url");
21479
+ if (bytes.toString("base64url") !== value) {
21480
+ throw new Error("non-canonical cloud session");
21481
+ }
21482
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
21483
+ } catch {
21484
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21485
+ }
21486
+ if (!isRecord3(parsed) || parsed.version !== CLOUD_SESSION_VERSION) {
21487
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21488
+ }
21489
+ const simId = parsed.simId;
21490
+ const apiOrigin = parsed.apiOrigin;
21491
+ const claimId = parsed.claimId;
21492
+ const token = parsed.token;
21493
+ if (typeof simId !== "string" || typeof apiOrigin !== "string" || typeof claimId !== "string" || typeof token !== "string") {
21494
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21495
+ }
21496
+ try {
21497
+ return createCloudSession({ simId, apiOrigin, claimId, token });
21498
+ } catch {
21499
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21500
+ }
21501
+ }
21502
+ function readCloudSession(environment = process.env) {
21503
+ const encoded = environment[RNX_CLOUD_SESSION_ENV];
21504
+ return encoded?.trim() ? parseCloudSession(encoded) : null;
21505
+ }
21506
+ var CLOUD_SESSION_VERSION, MAX_ENCODED_SESSION_BYTES, RNX_CLOUD_SESSION_ENV, RNX_SHELL_UPDATE_PREFIX, RNX_SHELL_UPDATE_SET_PREFIX, RNX_SHELL_UPDATE_UNSET;
21507
+ var init_cloud_session = __esm({
21508
+ "cli/cloud-session.ts"() {
21509
+ "use strict";
21510
+ init_backend_origin();
21511
+ CLOUD_SESSION_VERSION = 1;
21512
+ MAX_ENCODED_SESSION_BYTES = 32 * 1024;
21513
+ RNX_CLOUD_SESSION_ENV = "RNX_CLOUD_SESSION";
21514
+ RNX_SHELL_UPDATE_PREFIX = "__RNX_SHELL_UPDATE_V1__";
21515
+ RNX_SHELL_UPDATE_SET_PREFIX = `${RNX_SHELL_UPDATE_PREFIX}set:`;
21516
+ RNX_SHELL_UPDATE_UNSET = `${RNX_SHELL_UPDATE_PREFIX}unset`;
21517
+ }
21518
+ });
21519
+
21520
+ // cli/cloud-client.ts
21521
+ function isRecord4(value) {
21522
+ return value !== null && typeof value === "object" && !Array.isArray(value);
21523
+ }
21524
+ function responseError(status, text) {
21525
+ if (text) {
21526
+ try {
21527
+ const parsed = JSON.parse(text);
21528
+ const error = isRecord4(parsed) ? parsed.error : null;
21529
+ if (isRecord4(error) && typeof error.message === "string") {
21530
+ return new Error(`RNX Cloud request failed (${status}): ${error.message}`);
21531
+ }
21532
+ } catch {
21533
+ }
21534
+ }
21535
+ return new Error(
21536
+ `RNX Cloud request failed (${status})${text.trim() ? `: ${text.trim()}` : ""}`
21537
+ );
21538
+ }
21539
+ function resolveCloudSessionForParsed(parsed) {
21540
+ const session = readCloudSession();
21541
+ if (!session) return null;
21542
+ if (parsed.simIdSource === "flag" && parsed.simId !== session.simId) {
21543
+ throw new Error(
21544
+ `this shell is connected to RNX Cloud simulator ${session.simId}; ${parsed.simId ? `--sim ${parsed.simId}` : "the requested simulator"} is not available in this shell`
21545
+ );
21546
+ }
21547
+ return session;
21548
+ }
21549
+ function cloudCommand(command) {
21550
+ const clean = {};
21551
+ for (const [key, value] of Object.entries(command)) {
21552
+ if (key !== "id" && key !== "simId") clean[key] = value;
21553
+ }
21554
+ if (command.type === "query") {
21555
+ const query = command.query;
21556
+ if (!isRecord4(query) || query.kind !== "tree" && query.kind !== "find" && query.kind !== "count") {
21557
+ throw new Error("RNX Cloud supports only structured tree, node, and count queries");
21558
+ }
21559
+ return clean;
21560
+ }
21561
+ if (command.type === "waitFor") {
21562
+ const options = command.waitForOptions;
21563
+ const condition = isRecord4(options) ? options.condition : null;
21564
+ if (!isRecord4(condition) || condition.type !== "ready" && condition.type !== "present" && condition.type !== "absent") {
21565
+ throw new Error("RNX Cloud supports only ready and selector waits");
21566
+ }
21567
+ return clean;
21568
+ }
21569
+ if (command.type === "state") return clean;
21570
+ if (command.type === "capture" || command.type === "captureRegions") return clean;
21571
+ if (command.type === "setAppearance" || command.type === "openUrl") return clean;
21572
+ if (command.type === "screenshot" && command.crop === void 0 && command.captureFilter === void 0 && (command.layers === void 0 || command.layers === "tenant")) {
21573
+ return clean;
21574
+ }
21575
+ throw new Error(`RNX Cloud does not support bridge command type ${command.type}`);
21576
+ }
21577
+ function createCloudBridgeForParsed(parsed) {
21578
+ const session = resolveCloudSessionForParsed(parsed);
21579
+ return session ? new CloudBridge(session) : null;
21580
+ }
21581
+ var import_node_crypto5, DEFAULT_RNX_CLOUD_ORIGIN, CloudBridge;
21582
+ var init_cloud_client = __esm({
21583
+ "cli/cloud-client.ts"() {
21584
+ "use strict";
21585
+ import_node_crypto5 = require("node:crypto");
21586
+ init_cloud_contract();
21587
+ init_public_brand();
21588
+ init_cloud_session();
21589
+ DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin;
21590
+ CloudBridge = class {
21591
+ constructor(session) {
21592
+ this.session = session;
21593
+ }
21594
+ plane = "cloud";
21595
+ async send(command, options) {
21596
+ const body = {
21597
+ id: (0, import_node_crypto5.randomUUID)(),
21598
+ claimId: this.session.claimId,
21599
+ command: cloudCommand(command)
21600
+ };
21601
+ let response;
21602
+ try {
21603
+ response = await fetch(
21604
+ `${this.session.apiOrigin}/v1/sims/${encodeURIComponent(this.session.simId)}/commands`,
21605
+ {
21606
+ method: "POST",
21607
+ headers: {
21608
+ authorization: `Bearer ${this.session.token}`,
21609
+ "content-type": "application/json"
21610
+ },
21611
+ body: JSON.stringify(body),
21612
+ ...options?.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
21613
+ }
21614
+ );
21615
+ } catch (error) {
21616
+ throw new Error(
21617
+ `could not reach RNX Cloud at ${this.session.apiOrigin}: ${error instanceof Error ? error.message : String(error)}`
21618
+ );
21619
+ }
21620
+ const text = await response.text();
21621
+ if (!response.ok) throw responseError(response.status, text);
21622
+ let parsed;
21623
+ try {
21624
+ parsed = JSON.parse(text);
21625
+ } catch {
21626
+ throw new Error("RNX Cloud command returned unreadable JSON");
21627
+ }
21628
+ if (!isRecord4(parsed) || parsed.id !== body.id) {
21629
+ throw new Error("RNX Cloud command returned an invalid command receipt");
21630
+ }
21631
+ if (typeof parsed.error === "string" && parsed.error) throw new Error(parsed.error);
21632
+ if (!Object.hasOwn(parsed, "result")) {
21633
+ throw new Error("RNX Cloud command receipt has no result");
21634
+ }
21635
+ return parsed.result;
21636
+ }
21637
+ listSims() {
21638
+ return Promise.reject(new Error("RNX Cloud does not expose a local simulator list"));
21639
+ }
21640
+ focusSim() {
21641
+ return Promise.reject(
21642
+ new Error("RNX Cloud simulators have no browser window to focus")
21643
+ );
21644
+ }
21645
+ closeSim() {
21646
+ return Promise.reject(new Error("RNX Cloud close uses the simulator DELETE route"));
21647
+ }
21648
+ claim() {
21649
+ return Promise.reject(new Error("RNX Cloud claim uses the simulator claim route"));
21650
+ }
21651
+ close() {
21652
+ }
21653
+ };
21654
+ }
21655
+ });
21656
+
21419
21657
  // cli/parent-pid.ts
21420
21658
  function getParentPid(pid) {
21421
21659
  if (!cached3) cached3 = build2();
@@ -21575,6 +21813,36 @@ var init_parent_pid = __esm({
21575
21813
  }
21576
21814
  });
21577
21815
 
21816
+ // cli/secure-file.ts
21817
+ function writeSecureJsonAtomic(filepath, value) {
21818
+ (0, import_node_fs12.mkdirSync)((0, import_node_path13.dirname)(filepath), { recursive: true });
21819
+ const temporary = `${filepath}.tmp-${process.pid}-${(0, import_node_crypto6.randomUUID)()}`;
21820
+ let descriptor = null;
21821
+ try {
21822
+ descriptor = (0, import_node_fs12.openSync)(temporary, "wx", 384);
21823
+ (0, import_node_fs12.fchmodSync)(descriptor, 384);
21824
+ (0, import_node_fs12.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
21825
+ `);
21826
+ (0, import_node_fs12.fsyncSync)(descriptor);
21827
+ (0, import_node_fs12.closeSync)(descriptor);
21828
+ descriptor = null;
21829
+ (0, import_node_fs12.renameSync)(temporary, filepath);
21830
+ } catch (error) {
21831
+ if (descriptor !== null) (0, import_node_fs12.closeSync)(descriptor);
21832
+ (0, import_node_fs12.rmSync)(temporary, { force: true });
21833
+ throw error;
21834
+ }
21835
+ }
21836
+ var import_node_crypto6, import_node_fs12, import_node_path13;
21837
+ var init_secure_file = __esm({
21838
+ "cli/secure-file.ts"() {
21839
+ "use strict";
21840
+ import_node_crypto6 = require("node:crypto");
21841
+ import_node_fs12 = require("node:fs");
21842
+ import_node_path13 = require("node:path");
21843
+ }
21844
+ });
21845
+
21578
21846
  // cli/current-sim.ts
21579
21847
  function findTopAncestor(start, maxDepth = 20) {
21580
21848
  let prev = start;
@@ -21621,50 +21889,57 @@ function getCurrentSimPath() {
21621
21889
  const key = getCliIdentityKey();
21622
21890
  return (0, import_path3.join)((0, import_os.tmpdir)(), `rnx-current-sim-${key}.json`);
21623
21891
  }
21624
- function readCurrentSim() {
21892
+ function legacyCloudCredentialDirectory(filepath) {
21893
+ return (0, import_path3.join)((0, import_path3.dirname)(filepath), `${(0, import_path3.basename)(filepath)}.credentials`);
21894
+ }
21895
+ function readCurrentTarget() {
21625
21896
  const filepath = getCurrentSimPath();
21897
+ (0, import_fs4.rmSync)(legacyCloudCredentialDirectory(filepath), { recursive: true, force: true });
21626
21898
  if (!(0, import_fs4.existsSync)(filepath)) return null;
21627
21899
  try {
21628
21900
  const parsed = JSON.parse((0, import_fs4.readFileSync)(filepath, "utf8"));
21629
- if (parsed.version !== CURRENT_SIM_VERSION || typeof parsed.simId !== "string" || !parsed.simId.trim() || typeof parsed.updatedAt !== "string") {
21901
+ if (parsed === null || typeof parsed !== "object") {
21630
21902
  (0, import_fs4.rmSync)(filepath, { force: true });
21631
21903
  return null;
21632
21904
  }
21633
- return {
21905
+ const version = Reflect.get(parsed, "version");
21906
+ const plane = Reflect.get(parsed, "plane");
21907
+ const simId = Reflect.get(parsed, "simId");
21908
+ const updatedAt = Reflect.get(parsed, "updatedAt");
21909
+ if (version !== 1 && version !== CURRENT_SIM_VERSION || version === CURRENT_SIM_VERSION && plane !== "local" || typeof simId !== "string" || !simId.trim() || typeof updatedAt !== "string") {
21910
+ (0, import_fs4.rmSync)(filepath, { force: true });
21911
+ return null;
21912
+ }
21913
+ const target = {
21634
21914
  version: CURRENT_SIM_VERSION,
21635
- simId: parsed.simId.trim(),
21636
- updatedAt: parsed.updatedAt
21915
+ plane: "local",
21916
+ simId: simId.trim(),
21917
+ updatedAt
21637
21918
  };
21919
+ if (version === 1) writeSecureJsonAtomic(filepath, target);
21920
+ return target;
21638
21921
  } catch {
21639
21922
  (0, import_fs4.rmSync)(filepath, { force: true });
21640
21923
  return null;
21641
21924
  }
21642
21925
  }
21643
21926
  function readCurrentSimId() {
21644
- return readCurrentSim()?.simId || null;
21927
+ return readCurrentTarget()?.simId ?? null;
21645
21928
  }
21646
21929
  function saveCurrentSimId(simId) {
21647
21930
  const normalized = simId.trim();
21648
21931
  if (!normalized) return;
21649
- const filepath = getCurrentSimPath();
21650
- (0, import_fs4.mkdirSync)((0, import_path3.dirname)(filepath), { recursive: true });
21651
- (0, import_fs4.writeFileSync)(
21652
- filepath,
21653
- JSON.stringify(
21654
- {
21655
- version: CURRENT_SIM_VERSION,
21656
- simId: normalized,
21657
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
21658
- },
21659
- null,
21660
- 2
21661
- ) + "\n"
21662
- );
21932
+ writeSecureJsonAtomic(getCurrentSimPath(), {
21933
+ version: CURRENT_SIM_VERSION,
21934
+ plane: "local",
21935
+ simId: normalized,
21936
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
21937
+ });
21663
21938
  }
21664
21939
  function clearCurrentSimId() {
21665
21940
  (0, import_fs4.rmSync)(getCurrentSimPath(), { force: true });
21666
21941
  }
21667
- var import_fs4, import_os, import_path3, CURRENT_SIM_VERSION, CURRENT_SIM_PATH_ENV, IDENTITY_ENV_VARS, IMMORTAL_HOST_COMM_RE;
21942
+ var import_fs4, import_os, import_path3, CURRENT_SIM_VERSION, CURRENT_SIM_PATH_ENV, IDENTITY_ENV_VARS, IMMORTAL_HOST_COMM_RE, readCurrentSim;
21668
21943
  var init_current_sim = __esm({
21669
21944
  "cli/current-sim.ts"() {
21670
21945
  "use strict";
@@ -21672,7 +21947,8 @@ var init_current_sim = __esm({
21672
21947
  import_os = require("os");
21673
21948
  import_path3 = require("path");
21674
21949
  init_parent_pid();
21675
- CURRENT_SIM_VERSION = 1;
21950
+ init_secure_file();
21951
+ CURRENT_SIM_VERSION = 2;
21676
21952
  CURRENT_SIM_PATH_ENV = "SOOTSIM_CLI_CURRENT_SIM_PATH";
21677
21953
  IDENTITY_ENV_VARS = [
21678
21954
  "RNX_CLI_IDENTITY",
@@ -21703,6 +21979,7 @@ var init_current_sim = __esm({
21703
21979
  // vscode integrated terminal
21704
21980
  ];
21705
21981
  IMMORTAL_HOST_COMM_RE = /^(?:tmux|screen)|^tm$/i;
21982
+ readCurrentSim = readCurrentTarget;
21706
21983
  }
21707
21984
  });
21708
21985
 
@@ -21736,17 +22013,17 @@ function liveDevBridgeLockfiles() {
21736
22013
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
21737
22014
  }
21738
22015
  function checkoutRoot(cwd) {
21739
- let current = import_node_path13.default.resolve(cwd);
22016
+ let current = import_node_path14.default.resolve(cwd);
21740
22017
  while (true) {
21741
- if (import_node_fs12.default.existsSync(import_node_path13.default.join(current, ".git"))) return current;
21742
- const parent = import_node_path13.default.dirname(current);
22018
+ if (import_node_fs13.default.existsSync(import_node_path14.default.join(current, ".git"))) return current;
22019
+ const parent = import_node_path14.default.dirname(current);
21743
22020
  if (parent === current) return null;
21744
22021
  current = parent;
21745
22022
  }
21746
22023
  }
21747
22024
  function pathContains(parent, child) {
21748
- const relative = import_node_path13.default.relative(parent, child);
21749
- return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path13.default.sep}`) && !import_node_path13.default.isAbsolute(relative);
22025
+ const relative = import_node_path14.default.relative(parent, child);
22026
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path14.default.sep}`) && !import_node_path14.default.isAbsolute(relative);
21750
22027
  }
21751
22028
  function resolveDefaultDevBridgeLockfile() {
21752
22029
  const lockfiles = liveDevBridgeLockfiles();
@@ -21933,9 +22210,9 @@ function parseBridgeCliArgs(args, opts = {}) {
21933
22210
  }
21934
22211
  }
21935
22212
  if (!simId) {
21936
- const savedSimId = readCurrentSimId();
21937
- if (savedSimId) {
21938
- simId = savedSimId.trim() || void 0;
22213
+ const savedSim = readCurrentSim();
22214
+ if (savedSim) {
22215
+ simId = savedSim.simId.trim() || void 0;
21939
22216
  simIdSource = "saved";
21940
22217
  }
21941
22218
  }
@@ -22031,6 +22308,7 @@ function createBridge(wsPort, opts = {}) {
22031
22308
  }
22032
22309
  });
22033
22310
  return {
22311
+ plane: "local",
22034
22312
  async send(cmd, callOpts) {
22035
22313
  const effectiveTimeoutMs = callOpts?.timeoutMs ?? commandTimeoutMs;
22036
22314
  const sendOnce = async (defaultSimId) => {
@@ -22113,6 +22391,8 @@ function createBridge(wsPort, opts = {}) {
22113
22391
  };
22114
22392
  }
22115
22393
  function createBridgeFromParsed(parsed) {
22394
+ const cloudBridge = createCloudBridgeForParsed(parsed);
22395
+ if (cloudBridge) return cloudBridge;
22116
22396
  return createBridge(parsed.wsPort, {
22117
22397
  commandTimeoutMs: parsed.commandTimeoutMs,
22118
22398
  simId: parsed.simId,
@@ -22175,16 +22455,17 @@ async function callInBridge(bridge, path20, ...args) {
22175
22455
  async function callInBridgeWrite(bridge, path20, ...args) {
22176
22456
  return bridge.send({ type: "call", path: path20, args, acquireLock: true });
22177
22457
  }
22178
- var import_node_fs12, import_node_path13, import_ws3, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
22458
+ var import_node_fs13, import_node_path14, import_ws3, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
22179
22459
  var init_ws_bridge = __esm({
22180
22460
  "cli/ws-bridge.ts"() {
22181
22461
  "use strict";
22182
- import_node_fs12 = __toESM(require("node:fs"), 1);
22183
- import_node_path13 = __toESM(require("node:path"), 1);
22462
+ import_node_fs13 = __toESM(require("node:fs"), 1);
22463
+ import_node_path14 = __toESM(require("node:path"), 1);
22184
22464
  import_ws3 = require("ws");
22185
22465
  init_bridge_constants();
22186
22466
  init_cli_constants();
22187
22467
  init_home_paths();
22468
+ init_cloud_client();
22188
22469
  init_current_sim();
22189
22470
  RNX_PLACEMENT_ENV = "RNX_PLACEMENT";
22190
22471
  resolvedWorld = null;
@@ -22220,9 +22501,8 @@ function isAppDeepLinkTarget(target) {
22220
22501
  async function openAppUrl(bridge, target) {
22221
22502
  const normalized = normalizeAppUrlTarget(target);
22222
22503
  const result = await bridge.send({
22223
- type: "call",
22224
- path: "SootSim.bridges.test.openDeepLink",
22225
- args: [normalized]
22504
+ type: "openUrl",
22505
+ url: normalized
22226
22506
  });
22227
22507
  if (result !== null && typeof result === "object" && Reflect.get(result, "ok") === false) {
22228
22508
  const error = Reflect.get(result, "error");
@@ -22543,9 +22823,9 @@ var init_profiles = __esm({
22543
22823
  // cli/app-project.ts
22544
22824
  function firstNamedFile(dir, names) {
22545
22825
  for (const name2 of names) {
22546
- const candidate = import_node_path14.default.join(dir, name2);
22826
+ const candidate = import_node_path15.default.join(dir, name2);
22547
22827
  try {
22548
- if (import_node_fs13.default.statSync(candidate).isFile()) return candidate;
22828
+ if (import_node_fs14.default.statSync(candidate).isFile()) return candidate;
22549
22829
  } catch {
22550
22830
  }
22551
22831
  }
@@ -22558,24 +22838,24 @@ function findRNXAppConfig(dir) {
22558
22838
  return firstNamedFile(dir, RNX_APP_CONFIG_NAMES);
22559
22839
  }
22560
22840
  function resolveAppProject(projectRoot) {
22561
- const appDir = import_node_path14.default.resolve(projectRoot);
22841
+ const appDir = import_node_path15.default.resolve(projectRoot);
22562
22842
  try {
22563
- if (!import_node_fs13.default.statSync(appDir).isDirectory()) return null;
22843
+ if (!import_node_fs14.default.statSync(appDir).isDirectory()) return null;
22564
22844
  } catch {
22565
22845
  return null;
22566
22846
  }
22567
22847
  const configPath = findRNXAppConfig(appDir);
22568
- if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs13.default.existsSync(import_node_path14.default.join(appDir, "package.json"))) {
22848
+ if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs14.default.existsSync(import_node_path15.default.join(appDir, "package.json"))) {
22569
22849
  return null;
22570
22850
  }
22571
22851
  return { appDir, configPath };
22572
22852
  }
22573
- var import_node_fs13, import_node_path14, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
22853
+ var import_node_fs14, import_node_path15, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
22574
22854
  var init_app_project = __esm({
22575
22855
  "cli/app-project.ts"() {
22576
22856
  "use strict";
22577
- import_node_fs13 = __toESM(require("node:fs"), 1);
22578
- import_node_path14 = __toESM(require("node:path"), 1);
22857
+ import_node_fs14 = __toESM(require("node:fs"), 1);
22858
+ import_node_path15 = __toESM(require("node:path"), 1);
22579
22859
  EXPO_APP_CONFIG_NAMES = [
22580
22860
  "app.config.js",
22581
22861
  "app.config.ts",
@@ -22604,13 +22884,13 @@ function isRNXConfig(value) {
22604
22884
  }
22605
22885
  function resolveConfigReplacementFiles(config, configPath) {
22606
22886
  if (!config.modules) return config;
22607
- const configDir = import_node_path15.default.dirname(configPath);
22887
+ const configDir = import_node_path16.default.dirname(configPath);
22608
22888
  return {
22609
22889
  ...config,
22610
22890
  modules: Object.fromEntries(
22611
22891
  Object.entries(config.modules).map(([name2, resolution]) => [
22612
22892
  name2,
22613
- resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path15.default.resolve(configDir, resolution.file) } : resolution
22893
+ resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path16.default.resolve(configDir, resolution.file) } : resolution
22614
22894
  ])
22615
22895
  )
22616
22896
  };
@@ -22640,12 +22920,12 @@ async function resolveRNXAppConfig(startDir = process.cwd()) {
22640
22920
  const project = resolveAppProject(startDir);
22641
22921
  return { project, config: await loadRNXAppConfig(project) };
22642
22922
  }
22643
- var import_node_module, import_node_path15, import_jiti, require2;
22923
+ var import_node_module, import_node_path16, import_jiti, require2;
22644
22924
  var init_app_config = __esm({
22645
22925
  "cli/app-config.ts"() {
22646
22926
  "use strict";
22647
22927
  import_node_module = require("node:module");
22648
- import_node_path15 = __toESM(require("node:path"), 1);
22928
+ import_node_path16 = __toESM(require("node:path"), 1);
22649
22929
  init_src();
22650
22930
  import_jiti = require("jiti");
22651
22931
  init_public_brand();
@@ -22713,13 +22993,13 @@ function discoverNativeLinkedAppFonts(opts) {
22713
22993
  scannedFontDirectories++;
22714
22994
  let entries;
22715
22995
  try {
22716
- entries = import_node_fs14.default.readdirSync(current.dir, { withFileTypes: true });
22996
+ entries = import_node_fs15.default.readdirSync(current.dir, { withFileTypes: true });
22717
22997
  } catch {
22718
22998
  continue;
22719
22999
  }
22720
23000
  for (const entry of entries) {
22721
23001
  if (entry.isSymbolicLink()) continue;
22722
- const fullPath = import_node_path16.default.join(current.dir, entry.name);
23002
+ const fullPath = import_node_path17.default.join(current.dir, entry.name);
22723
23003
  if (entry.isDirectory()) {
22724
23004
  if (current.depth < 4 && pending.length < 64 && !entry.name.startsWith(".")) {
22725
23005
  pending.push({ dir: fullPath, depth: current.depth + 1 });
@@ -22727,15 +23007,15 @@ function discoverNativeLinkedAppFonts(opts) {
22727
23007
  continue;
22728
23008
  }
22729
23009
  if (!entry.isFile() || !/\.(ttf|otf|woff|woff2)$/i.test(entry.name)) continue;
22730
- const relative = import_node_path16.default.relative(root, fullPath).split(import_node_path16.default.sep).join("/");
22731
- fontPaths.push(import_node_path16.default.posix.join(metroPathPrefix, relative));
23010
+ const relative = import_node_path17.default.relative(root, fullPath).split(import_node_path17.default.sep).join("/");
23011
+ fontPaths.push(import_node_path17.default.posix.join(metroPathPrefix, relative));
22732
23012
  if (fontPaths.length >= 256) break;
22733
23013
  }
22734
23014
  }
22735
23015
  };
22736
23016
  try {
22737
23017
  const packageJson = JSON.parse(
22738
- import_node_fs14.default.readFileSync(import_node_path16.default.join(selectedAppDir, "package.json"), "utf8")
23018
+ import_node_fs15.default.readFileSync(import_node_path17.default.join(selectedAppDir, "package.json"), "utf8")
22739
23019
  );
22740
23020
  const dependencyNames = /* @__PURE__ */ new Set();
22741
23021
  for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
@@ -22750,24 +23030,24 @@ function discoverNativeLinkedAppFonts(opts) {
22750
23030
  const config = Reflect.get(packageJson, "reactNativeVectorIcons");
22751
23031
  const configuredFontDir = config && typeof config === "object" ? Reflect.get(config, "fontDir") : null;
22752
23032
  const configuredDir = typeof configuredFontDir === "string" ? configuredFontDir : "rnvi-fonts";
22753
- const root = import_node_path16.default.resolve(selectedAppDir, configuredDir);
22754
- const relativeRoot = import_node_path16.default.relative(selectedAppDir, root);
22755
- if (!relativeRoot.startsWith("..") && !import_node_path16.default.isAbsolute(relativeRoot)) {
22756
- collectFontFiles(root, relativeRoot.split(import_node_path16.default.sep).join("/"));
23033
+ const root = import_node_path17.default.resolve(selectedAppDir, configuredDir);
23034
+ const relativeRoot = import_node_path17.default.relative(selectedAppDir, root);
23035
+ if (!relativeRoot.startsWith("..") && !import_node_path17.default.isAbsolute(relativeRoot)) {
23036
+ collectFontFiles(root, relativeRoot.split(import_node_path17.default.sep).join("/"));
22757
23037
  }
22758
- const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path16.default.join(selectedAppDir, "package.json"));
23038
+ const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path17.default.join(selectedAppDir, "package.json"));
22759
23039
  for (const dependencyName of vectorIconDependencies) {
22760
23040
  try {
22761
23041
  const entryPath = vectorIconRequire.resolve(dependencyName);
22762
- const dependencyPath = dependencyName.split("/").join(import_node_path16.default.sep);
22763
- const marker = `${import_node_path16.default.sep}node_modules${import_node_path16.default.sep}${dependencyPath}${import_node_path16.default.sep}`;
23042
+ const dependencyPath = dependencyName.split("/").join(import_node_path17.default.sep);
23043
+ const marker = `${import_node_path17.default.sep}node_modules${import_node_path17.default.sep}${dependencyPath}${import_node_path17.default.sep}`;
22764
23044
  const markerIndex = entryPath.lastIndexOf(marker);
22765
23045
  if (markerIndex === -1) continue;
22766
23046
  const packageRoot = entryPath.slice(0, markerIndex + marker.length - 1);
22767
23047
  const fontsDir = dependencyName === "react-native-vector-icons" ? "Fonts" : "fonts";
22768
23048
  collectFontFiles(
22769
- import_node_path16.default.join(packageRoot, fontsDir),
22770
- import_node_path16.default.posix.join("node_modules", dependencyName, fontsDir)
23049
+ import_node_path17.default.join(packageRoot, fontsDir),
23050
+ import_node_path17.default.posix.join("node_modules", dependencyName, fontsDir)
22771
23051
  );
22772
23052
  } catch {
22773
23053
  }
@@ -22776,37 +23056,37 @@ function discoverNativeLinkedAppFonts(opts) {
22776
23056
  } catch {
22777
23057
  }
22778
23058
  try {
22779
- const configPath = import_node_path16.default.join(selectedAppDir, "react-native.config.js");
22780
- if (import_node_fs14.default.statSync(configPath).isFile()) {
22781
- const appConfigRequire = (0, import_node_module2.createRequire)(import_node_path16.default.join(selectedAppDir, "package.json"));
23059
+ const configPath = import_node_path17.default.join(selectedAppDir, "react-native.config.js");
23060
+ if (import_node_fs15.default.statSync(configPath).isFile()) {
23061
+ const appConfigRequire = (0, import_node_module2.createRequire)(import_node_path17.default.join(selectedAppDir, "package.json"));
22782
23062
  const rnConfig = appConfigRequire(configPath);
22783
23063
  const assets = rnConfig && typeof rnConfig === "object" ? Reflect.get(rnConfig, "assets") : null;
22784
23064
  if (Array.isArray(assets)) {
22785
23065
  for (const assetDir of assets) {
22786
23066
  if (typeof assetDir !== "string") continue;
22787
- const root = import_node_path16.default.resolve(selectedAppDir, assetDir);
22788
- const relativeRoot = import_node_path16.default.relative(selectedAppDir, root);
22789
- if (relativeRoot.startsWith("..") || import_node_path16.default.isAbsolute(relativeRoot)) continue;
22790
- collectFontFiles(root, relativeRoot.split(import_node_path16.default.sep).join("/"));
23067
+ const root = import_node_path17.default.resolve(selectedAppDir, assetDir);
23068
+ const relativeRoot = import_node_path17.default.relative(selectedAppDir, root);
23069
+ if (relativeRoot.startsWith("..") || import_node_path17.default.isAbsolute(relativeRoot)) continue;
23070
+ collectFontFiles(root, relativeRoot.split(import_node_path17.default.sep).join("/"));
22791
23071
  }
22792
23072
  }
22793
23073
  }
22794
23074
  } catch {
22795
23075
  }
22796
23076
  if (platform === "ios") {
22797
- const iosDir = import_node_path16.default.join(selectedAppDir, "ios");
23077
+ const iosDir = import_node_path17.default.join(selectedAppDir, "ios");
22798
23078
  try {
22799
- const targetDirs = import_node_fs14.default.readdirSync(iosDir, { withFileTypes: true }).filter(
23079
+ const targetDirs = import_node_fs15.default.readdirSync(iosDir, { withFileTypes: true }).filter(
22800
23080
  (entry) => entry.isDirectory() && !entry.isSymbolicLink() && entry.name !== "Pods" && entry.name !== "build" && !entry.name.startsWith(".")
22801
23081
  ).slice(0, 64);
22802
- const plistPaths = [import_node_path16.default.join(iosDir, "Info.plist")];
23082
+ const plistPaths = [import_node_path17.default.join(iosDir, "Info.plist")];
22803
23083
  for (const entry of targetDirs) {
22804
- plistPaths.push(import_node_path16.default.join(iosDir, entry.name, "Info.plist"));
23084
+ plistPaths.push(import_node_path17.default.join(iosDir, entry.name, "Info.plist"));
22805
23085
  }
22806
23086
  for (const plistPath of plistPaths) {
22807
23087
  let parsed;
22808
23088
  try {
22809
- parsed = parsePlistSource(import_node_fs14.default.readFileSync(plistPath, "utf8"));
23089
+ parsed = parsePlistSource(import_node_fs15.default.readFileSync(plistPath, "utf8"));
22810
23090
  } catch {
22811
23091
  continue;
22812
23092
  }
@@ -22816,21 +23096,21 @@ function discoverNativeLinkedAppFonts(opts) {
22816
23096
  for (const declaredFont of declaredFonts) {
22817
23097
  if (typeof declaredFont !== "string") continue;
22818
23098
  const candidates = [
22819
- import_node_path16.default.resolve(import_node_path16.default.dirname(plistPath), declaredFont),
22820
- import_node_path16.default.resolve(iosDir, declaredFont)
23099
+ import_node_path17.default.resolve(import_node_path17.default.dirname(plistPath), declaredFont),
23100
+ import_node_path17.default.resolve(iosDir, declaredFont)
22821
23101
  ];
22822
23102
  const fontPath = candidates.find((candidate) => {
22823
- const relative = import_node_path16.default.relative(selectedAppDir, candidate);
22824
- if (relative.startsWith("..") || import_node_path16.default.isAbsolute(relative)) return false;
23103
+ const relative = import_node_path17.default.relative(selectedAppDir, candidate);
23104
+ if (relative.startsWith("..") || import_node_path17.default.isAbsolute(relative)) return false;
22825
23105
  try {
22826
- return import_node_fs14.default.statSync(candidate).isFile();
23106
+ return import_node_fs15.default.statSync(candidate).isFile();
22827
23107
  } catch {
22828
23108
  return false;
22829
23109
  }
22830
23110
  });
22831
23111
  if (!fontPath) continue;
22832
23112
  fontPaths.push(
22833
- import_node_path16.default.relative(selectedAppDir, fontPath).split(import_node_path16.default.sep).join("/")
23113
+ import_node_path17.default.relative(selectedAppDir, fontPath).split(import_node_path17.default.sep).join("/")
22834
23114
  );
22835
23115
  }
22836
23116
  }
@@ -22869,13 +23149,13 @@ async function resolveMetroProjectRoot(bundleUrl, opts) {
22869
23149
  function encodeAppFontsWire(specs) {
22870
23150
  return specs.map((spec) => `${encodeURIComponent(spec.url)}::${encodeURIComponent(spec.family)}`).join(",");
22871
23151
  }
22872
- var import_node_fs14, import_node_module2, import_node_path16, import_config_plugin_fonts, require3;
23152
+ var import_node_fs15, import_node_module2, import_node_path17, import_config_plugin_fonts, require3;
22873
23153
  var init_app_fonts = __esm({
22874
23154
  "cli/app-fonts.ts"() {
22875
23155
  "use strict";
22876
- import_node_fs14 = __toESM(require("node:fs"), 1);
23156
+ import_node_fs15 = __toESM(require("node:fs"), 1);
22877
23157
  import_node_module2 = require("node:module");
22878
- import_node_path16 = __toESM(require("node:path"), 1);
23158
+ import_node_path17 = __toESM(require("node:path"), 1);
22879
23159
  import_config_plugin_fonts = require("sootsim-engine/engine/config-plugin-fonts");
22880
23160
  init_dev_server_scanner();
22881
23161
  init_plist();
@@ -23953,7 +24233,7 @@ var init_browser_cache = __esm({
23953
24233
  });
23954
24234
 
23955
24235
  // cli/drivers/playwright-provisioning.ts
23956
- function isRecord3(value) {
24236
+ function isRecord5(value) {
23957
24237
  return typeof value === "object" && value !== null && !Array.isArray(value);
23958
24238
  }
23959
24239
  function resolvePlaywright() {
@@ -23966,20 +24246,20 @@ function resolvePlaywright() {
23966
24246
  try {
23967
24247
  const modulePath = requireFromRoot.resolve(spec);
23968
24248
  const packageJsonPath = requireFromRoot.resolve(`${spec}/package.json`);
23969
- const parsed = JSON.parse((0, import_node_fs15.readFileSync)(packageJsonPath, "utf8"));
23970
- if (!isRecord3(parsed) || typeof parsed.version !== "string") continue;
24249
+ const parsed = JSON.parse((0, import_node_fs16.readFileSync)(packageJsonPath, "utf8"));
24250
+ if (!isRecord5(parsed) || typeof parsed.version !== "string") continue;
23971
24251
  const bin = parsed.bin;
23972
24252
  let binPath = null;
23973
24253
  if (typeof bin === "string") {
23974
24254
  binPath = bin;
23975
- } else if (isRecord3(bin)) {
24255
+ } else if (isRecord5(bin)) {
23976
24256
  const named = bin.playwright ?? bin["playwright-core"];
23977
24257
  const first = Object.values(bin).find((value) => typeof value === "string");
23978
24258
  binPath = typeof named === "string" ? named : typeof first === "string" ? first : null;
23979
24259
  }
23980
24260
  if (!binPath) continue;
23981
- const cliPath = (0, import_node_path17.join)((0, import_node_path17.dirname)(packageJsonPath), binPath);
23982
- if (!(0, import_node_fs15.existsSync)(cliPath)) continue;
24261
+ const cliPath = (0, import_node_path18.join)((0, import_node_path18.dirname)(packageJsonPath), binPath);
24262
+ if (!(0, import_node_fs16.existsSync)(cliPath)) continue;
23983
24263
  return {
23984
24264
  spec,
23985
24265
  modulePath,
@@ -24012,13 +24292,13 @@ function probeBrowser(resolved, env) {
24012
24292
  }
24013
24293
  try {
24014
24294
  const parsed = JSON.parse(probe3.stdout);
24015
- if (!isRecord3(parsed) || typeof parsed.executablePath !== "string" || typeof parsed.exists !== "boolean") {
24295
+ if (!isRecord5(parsed) || typeof parsed.executablePath !== "string" || typeof parsed.exists !== "boolean") {
24016
24296
  throw new Error("probe returned an invalid result");
24017
24297
  }
24018
24298
  return {
24019
24299
  ok: true,
24020
24300
  executablePath: parsed.executablePath,
24021
- exists: parsed.exists && (0, import_node_fs15.existsSync)(parsed.executablePath)
24301
+ exists: parsed.exists && (0, import_node_fs16.existsSync)(parsed.executablePath)
24022
24302
  };
24023
24303
  } catch (error) {
24024
24304
  return {
@@ -24057,14 +24337,14 @@ ${detail}` : ""}`
24057
24337
  }
24058
24338
  return { ok: true, executablePath: after.executablePath, installed: true };
24059
24339
  }
24060
- var import_node_child_process6, import_node_fs15, import_node_module4, import_node_path17, PLAYWRIGHT_BROWSER_PROBE;
24340
+ var import_node_child_process6, import_node_fs16, import_node_module4, import_node_path18, PLAYWRIGHT_BROWSER_PROBE;
24061
24341
  var init_playwright_provisioning = __esm({
24062
24342
  "cli/drivers/playwright-provisioning.ts"() {
24063
24343
  "use strict";
24064
24344
  import_node_child_process6 = require("node:child_process");
24065
- import_node_fs15 = require("node:fs");
24345
+ import_node_fs16 = require("node:fs");
24066
24346
  import_node_module4 = require("node:module");
24067
- import_node_path17 = require("node:path");
24347
+ import_node_path18 = require("node:path");
24068
24348
  PLAYWRIGHT_BROWSER_PROBE = `
24069
24349
  const { existsSync } = require('node:fs');
24070
24350
  const modulePath = process.env.SOOTSIM_PW_MODULE;
@@ -24474,7 +24754,7 @@ const cleanupProfile = () => {
24474
24754
  // we are spawned detached + unref'd so \`rnx open\` returns immediately,
24475
24755
  // which means nothing in the OS process tree ties Chrome's lifetime to the
24476
24756
  // session that asked for it. once the sim connects, the connect-timeout
24477
- // safety net is cleared, so absent an explicit \`sootsim close\` (or the
24757
+ // safety net is cleared, so absent an explicit \`rnx close\` (or the
24478
24758
  // daemon's idle reaper closing the sim socket) the host would hold Chrome
24479
24759
  // open forever after its owner exits. poll the owning session's pid and tear
24480
24760
  // down when it's gone. ESRCH (kill throws) = owner exited; EPERM = alive but
@@ -24765,10 +25045,10 @@ var init_run_rnx = __esm({
24765
25045
  });
24766
25046
 
24767
25047
  // cli/commands/inspect/env.ts
24768
- function isAgentEnv() {
24769
- if (process.env.RNX_AGENT === "0") return false;
25048
+ function isAgentEnv(env = process.env) {
25049
+ if (env.RNX_AGENT === "0") return false;
24770
25050
  for (const name2 of AGENT_ENV_VARS) {
24771
- const v = process.env[name2];
25051
+ const v = env[name2];
24772
25052
  if (v && v.trim() && v !== "0") return true;
24773
25053
  }
24774
25054
  return false;
@@ -24933,7 +25213,7 @@ async function activateVersion(version) {
24933
25213
  const notified = await tellDaemonRuntimeChanged(version);
24934
25214
  if (!notified) {
24935
25215
  console.log(
24936
- ` (no daemon running \u2014 next sootsim/electron launch will pick up ${version})`
25216
+ ` (no daemon running \u2014 next rnx/electron launch will pick up ${version})`
24937
25217
  );
24938
25218
  }
24939
25219
  }
@@ -25240,14 +25520,14 @@ function assertExplicitBridgeLock(preferredPort, explicitPort, lock) {
25240
25520
  }
25241
25521
  async function ensureRuntimeForOpen() {
25242
25522
  const active = readActiveRuntime();
25243
- if (active && import_node_fs16.default.existsSync(runtimeDir(active))) return;
25523
+ if (active && import_node_fs17.default.existsSync(runtimeDir(active))) return;
25244
25524
  console.error(" installing rnx engine runtime...");
25245
25525
  const { runRuntime: runRuntime2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
25246
25526
  await runRuntime2(["install"], {});
25247
25527
  }
25248
25528
  function spawnSootsim(args) {
25249
25529
  const entry = process.argv[1];
25250
- if (entry && import_node_fs16.default.existsSync(entry)) {
25530
+ if (entry && import_node_fs17.default.existsSync(entry)) {
25251
25531
  return (0, import_node_child_process7.spawn)(process.execPath, [entry, ...args], {
25252
25532
  detached: true,
25253
25533
  stdio: "ignore",
@@ -25394,10 +25674,10 @@ async function buildResolvedBundleOpenUrl(target, baseUrl, opts = {}) {
25394
25674
  }
25395
25675
  function normalizeReplacementPath(filePath) {
25396
25676
  if (filePath.startsWith("~/")) {
25397
- return import_node_path18.default.join(import_node_os3.default.homedir(), filePath.slice(2));
25677
+ return import_node_path19.default.join(import_node_os3.default.homedir(), filePath.slice(2));
25398
25678
  }
25399
- if (import_node_path18.default.isAbsolute(filePath)) return filePath;
25400
- return import_node_path18.default.resolve(process.cwd(), filePath);
25679
+ if (import_node_path19.default.isAbsolute(filePath)) return filePath;
25680
+ return import_node_path19.default.resolve(process.cwd(), filePath);
25401
25681
  }
25402
25682
  function parseOpenRuntimeConfig(args) {
25403
25683
  const modules = {};
@@ -25416,7 +25696,7 @@ function parseOpenRuntimeConfig(args) {
25416
25696
  }
25417
25697
  const moduleName = raw.slice(0, eqIndex).trim();
25418
25698
  const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim());
25419
- if (!import_node_fs16.default.existsSync(filePath)) {
25699
+ if (!import_node_fs17.default.existsSync(filePath)) {
25420
25700
  console.error(` rnx open: replacement file not found: ${filePath}`);
25421
25701
  rnxExit(1);
25422
25702
  }
@@ -25447,7 +25727,7 @@ async function resolveOpenAppProject(resolvedBundle) {
25447
25727
  return advertisedRoot ? resolveAppProject(advertisedRoot) : null;
25448
25728
  }
25449
25729
  async function ensureConfiguredRuntime(version) {
25450
- if (import_node_fs16.default.existsSync(import_node_path18.default.join(runtimeDir(version), "index.html"))) return;
25730
+ if (import_node_fs17.default.existsSync(import_node_path19.default.join(runtimeDir(version), "index.html"))) return;
25451
25731
  console.error(` installing configured engine runtime ${version}...`);
25452
25732
  const result = await rnxRuntime.install({
25453
25733
  version,
@@ -25807,7 +26087,7 @@ function playwrightHostPidForSim(sim) {
25807
26087
  function signalDriverLaunchConnected(result) {
25808
26088
  if (!result.connectAckFile) return false;
25809
26089
  try {
25810
- import_node_fs16.default.writeFileSync(
26090
+ import_node_fs17.default.writeFileSync(
25811
26091
  result.connectAckFile,
25812
26092
  `${JSON.stringify({ connectedAt: Date.now(), pid: result.pid ?? null })}
25813
26093
  `,
@@ -25821,19 +26101,19 @@ function signalDriverLaunchConnected(result) {
25821
26101
  function printDriverDiagnosticLogTail(result) {
25822
26102
  if (!result.diagnosticLogPath) return;
25823
26103
  try {
25824
- const detail = import_node_fs16.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
26104
+ const detail = import_node_fs17.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
25825
26105
  if (!detail) {
25826
- console.error(` ${import_node_path18.default.basename(result.diagnosticLogPath)} was empty`);
26106
+ console.error(` ${import_node_path19.default.basename(result.diagnosticLogPath)} was empty`);
25827
26107
  return;
25828
26108
  }
25829
26109
  const shown = detail.length > 4e3 ? `\u2026
25830
26110
  ${detail.slice(-4e3)}` : detail;
25831
- console.error(` ${import_node_path18.default.basename(result.diagnosticLogPath)}:`);
26111
+ console.error(` ${import_node_path19.default.basename(result.diagnosticLogPath)}:`);
25832
26112
  console.error(shown);
25833
26113
  } catch (err) {
25834
26114
  const message = err instanceof Error ? err.message : String(err);
25835
26115
  console.error(
25836
- ` failed to read ${import_node_path18.default.basename(result.diagnosticLogPath)}: ${message}`
26116
+ ` failed to read ${import_node_path19.default.basename(result.diagnosticLogPath)}: ${message}`
25837
26117
  );
25838
26118
  }
25839
26119
  }
@@ -26644,14 +26924,14 @@ async function runCloseCommand(args, opts = {}) {
26644
26924
  bridge.close();
26645
26925
  }
26646
26926
  }
26647
- var import_node_child_process7, import_node_fs16, import_node_os3, import_node_path18, import_settings, import_ws5, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS;
26927
+ var import_node_child_process7, import_node_fs17, import_node_os3, import_node_path19, import_settings, import_ws5, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS;
26648
26928
  var init_control = __esm({
26649
26929
  "cli/commands/control.ts"() {
26650
26930
  "use strict";
26651
26931
  import_node_child_process7 = require("node:child_process");
26652
- import_node_fs16 = __toESM(require("node:fs"), 1);
26932
+ import_node_fs17 = __toESM(require("node:fs"), 1);
26653
26933
  import_node_os3 = __toESM(require("node:os"), 1);
26654
- import_node_path18 = __toESM(require("node:path"), 1);
26934
+ import_node_path19 = __toESM(require("node:path"), 1);
26655
26935
  import_settings = require("sootsim-engine/settings");
26656
26936
  import_ws5 = require("ws");
26657
26937
  init_dev_server_scanner();
@@ -27515,14 +27795,14 @@ process.stdin.on('end', async () => {
27515
27795
  function getBaseDir() {
27516
27796
  const explicit = process.env[SESSION_FILE_ENV];
27517
27797
  if (explicit?.trim()) {
27518
- return (0, import_node_path19.dirname)((0, import_node_path19.resolve)(explicit));
27798
+ return (0, import_node_path20.dirname)((0, import_node_path20.resolve)(explicit));
27519
27799
  }
27520
27800
  return electronUserDataDir();
27521
27801
  }
27522
27802
  function getSharedDesktopAuthFilePath() {
27523
27803
  const explicit = process.env[SESSION_FILE_ENV];
27524
- if (explicit?.trim()) return (0, import_node_path19.resolve)(explicit);
27525
- return (0, import_node_path19.join)(getBaseDir(), "desktop-auth.json");
27804
+ if (explicit?.trim()) return (0, import_node_path20.resolve)(explicit);
27805
+ return (0, import_node_path20.join)(getBaseDir(), "desktop-auth.json");
27526
27806
  }
27527
27807
  function normalizeUser(input) {
27528
27808
  if (!input || typeof input !== "object") return null;
@@ -27569,26 +27849,26 @@ function normalizeSession(input) {
27569
27849
  }
27570
27850
  function readSharedDesktopAuthSession() {
27571
27851
  const filepath = getSharedDesktopAuthFilePath();
27572
- if (!(0, import_node_fs17.existsSync)(filepath)) return null;
27852
+ if (!(0, import_node_fs18.existsSync)(filepath)) return null;
27573
27853
  try {
27574
- const parsed = JSON.parse((0, import_node_fs17.readFileSync)(filepath, "utf8"));
27854
+ const parsed = JSON.parse((0, import_node_fs18.readFileSync)(filepath, "utf8"));
27575
27855
  const normalized = normalizeSession(parsed);
27576
27856
  if (!normalized) {
27577
- (0, import_node_fs17.rmSync)(filepath, { force: true });
27857
+ (0, import_node_fs18.rmSync)(filepath, { force: true });
27578
27858
  return null;
27579
27859
  }
27580
27860
  return normalized;
27581
27861
  } catch {
27582
- (0, import_node_fs17.rmSync)(filepath, { force: true });
27862
+ (0, import_node_fs18.rmSync)(filepath, { force: true });
27583
27863
  return null;
27584
27864
  }
27585
27865
  }
27586
- var import_node_fs17, import_node_path19, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
27866
+ var import_node_fs18, import_node_path20, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
27587
27867
  var init_shared_session = __esm({
27588
27868
  "src/auth/shared-session.ts"() {
27589
27869
  "use strict";
27590
- import_node_fs17 = require("node:fs");
27591
- import_node_path19 = require("node:path");
27870
+ import_node_fs18 = require("node:fs");
27871
+ import_node_path20 = require("node:path");
27592
27872
  init_home_paths();
27593
27873
  SESSION_VERSION = 1;
27594
27874
  SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
@@ -29176,7 +29456,7 @@ var init_bridge_flow_runner = __esm({
29176
29456
  await this.resetGuestAppData();
29177
29457
  await this.rearmCaptureAfterRuntimeReload();
29178
29458
  }
29179
- // maestro clearKeychain: there is no real keychain in sootsim's browser
29459
+ // maestro clearKeychain: there is no real keychain in rnx's browser
29180
29460
  // sandbox. log loudly so the author notices, then continue — matching the
29181
29461
  // "loud warning, not silent skip" policy in the plan.
29182
29462
  clearKeychain() {