rnxsim 0.1.381 → 0.1.383

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 +20 -8
  82. package/dist-lib/bridge-contract-input.mjs +20 -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 +217 -0
  86. package/dist-lib/capture-contract.mjs +187 -0
  87. package/dist-lib/cli-constants.cjs +1 -1
  88. package/dist-lib/cloud-contract.cjs +41 -0
  89. package/dist-lib/cloud-contract.mjs +13 -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 +361 -75
  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 +436 -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 +25 -6
  123. package/src/bridge-contract.ts +35 -5
  124. package/src/capture-contract.ts +576 -0
  125. package/src/cloud-contract.ts +18 -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 +25 -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.383 | (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,234 @@ var init_cli_constants = __esm({
21416
21427
  }
21417
21428
  });
21418
21429
 
21430
+ // src/cloud-contract.ts
21431
+ var RNX_CLOUD_MAX_ARTIFACT_BYTES, RNX_CLOUD_DESIGN_MAX_STORAGE_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
+ RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
21437
+ }
21438
+ });
21439
+
21440
+ // cli/cloud-session.ts
21441
+ function isRecord3(value) {
21442
+ return value !== null && typeof value === "object" && !Array.isArray(value);
21443
+ }
21444
+ function normalizeCloudOrigin(source) {
21445
+ let parsed;
21446
+ try {
21447
+ parsed = new URL(source);
21448
+ } catch {
21449
+ throw new Error(`invalid RNX Cloud origin: ${source}`);
21450
+ }
21451
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
21452
+ throw new Error(`RNX Cloud origin must be an HTTP(S) origin: ${source}`);
21453
+ }
21454
+ if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) {
21455
+ throw new Error(`RNX Cloud origin requires HTTPS outside loopback: ${source}`);
21456
+ }
21457
+ return parsed.origin;
21458
+ }
21459
+ function createCloudSession(input) {
21460
+ const session = {
21461
+ version: CLOUD_SESSION_VERSION,
21462
+ simId: input.simId.trim(),
21463
+ apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
21464
+ claimId: input.claimId.trim(),
21465
+ token: input.token
21466
+ };
21467
+ if (!session.simId || !session.claimId || !session.token) {
21468
+ throw new Error("cloud session requires simulator, origin, claim, and token");
21469
+ }
21470
+ return session;
21471
+ }
21472
+ function parseCloudSession(encoded) {
21473
+ const value = encoded.trim();
21474
+ if (!value || value.length > MAX_ENCODED_SESSION_BYTES || !/^[A-Za-z0-9_-]+$/.test(value)) {
21475
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21476
+ }
21477
+ let parsed;
21478
+ try {
21479
+ const bytes = Buffer.from(value, "base64url");
21480
+ if (bytes.toString("base64url") !== value) {
21481
+ throw new Error("non-canonical cloud session");
21482
+ }
21483
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
21484
+ } catch {
21485
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21486
+ }
21487
+ if (!isRecord3(parsed) || parsed.version !== CLOUD_SESSION_VERSION) {
21488
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21489
+ }
21490
+ const simId = parsed.simId;
21491
+ const apiOrigin = parsed.apiOrigin;
21492
+ const claimId = parsed.claimId;
21493
+ const token = parsed.token;
21494
+ if (typeof simId !== "string" || typeof apiOrigin !== "string" || typeof claimId !== "string" || typeof token !== "string") {
21495
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21496
+ }
21497
+ try {
21498
+ return createCloudSession({ simId, apiOrigin, claimId, token });
21499
+ } catch {
21500
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
21501
+ }
21502
+ }
21503
+ function readCloudSession(environment = process.env) {
21504
+ const encoded = environment[RNX_CLOUD_SESSION_ENV];
21505
+ return encoded?.trim() ? parseCloudSession(encoded) : null;
21506
+ }
21507
+ 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;
21508
+ var init_cloud_session = __esm({
21509
+ "cli/cloud-session.ts"() {
21510
+ "use strict";
21511
+ init_backend_origin();
21512
+ CLOUD_SESSION_VERSION = 1;
21513
+ MAX_ENCODED_SESSION_BYTES = 32 * 1024;
21514
+ RNX_CLOUD_SESSION_ENV = "RNX_CLOUD_SESSION";
21515
+ RNX_SHELL_UPDATE_PREFIX = "__RNX_SHELL_UPDATE_V1__";
21516
+ RNX_SHELL_UPDATE_SET_PREFIX = `${RNX_SHELL_UPDATE_PREFIX}set:`;
21517
+ RNX_SHELL_UPDATE_UNSET = `${RNX_SHELL_UPDATE_PREFIX}unset`;
21518
+ }
21519
+ });
21520
+
21521
+ // cli/cloud-client.ts
21522
+ function isRecord4(value) {
21523
+ return value !== null && typeof value === "object" && !Array.isArray(value);
21524
+ }
21525
+ function responseError(status, text) {
21526
+ if (text) {
21527
+ try {
21528
+ const parsed = JSON.parse(text);
21529
+ const error = isRecord4(parsed) ? parsed.error : null;
21530
+ if (isRecord4(error) && typeof error.message === "string") {
21531
+ return new Error(`RNX Cloud request failed (${status}): ${error.message}`);
21532
+ }
21533
+ } catch {
21534
+ }
21535
+ }
21536
+ return new Error(
21537
+ `RNX Cloud request failed (${status})${text.trim() ? `: ${text.trim()}` : ""}`
21538
+ );
21539
+ }
21540
+ function resolveCloudSessionForParsed(parsed) {
21541
+ const session = readCloudSession();
21542
+ if (!session) return null;
21543
+ if (parsed.simIdSource === "flag" && parsed.simId !== session.simId) {
21544
+ throw new Error(
21545
+ `this shell is connected to RNX Cloud simulator ${session.simId}; ${parsed.simId ? `--sim ${parsed.simId}` : "the requested simulator"} is not available in this shell`
21546
+ );
21547
+ }
21548
+ return session;
21549
+ }
21550
+ function cloudCommand(command) {
21551
+ const clean = {};
21552
+ for (const [key, value] of Object.entries(command)) {
21553
+ if (key !== "id" && key !== "simId") clean[key] = value;
21554
+ }
21555
+ if (command.type === "query") {
21556
+ const query = command.query;
21557
+ if (!isRecord4(query) || query.kind !== "tree" && query.kind !== "find" && query.kind !== "count") {
21558
+ throw new Error("RNX Cloud supports only structured tree, node, and count queries");
21559
+ }
21560
+ return clean;
21561
+ }
21562
+ if (command.type === "waitFor") {
21563
+ const options = command.waitForOptions;
21564
+ const condition = isRecord4(options) ? options.condition : null;
21565
+ if (!isRecord4(condition) || condition.type !== "ready" && condition.type !== "present" && condition.type !== "absent") {
21566
+ throw new Error("RNX Cloud supports only ready and selector waits");
21567
+ }
21568
+ return clean;
21569
+ }
21570
+ if (command.type === "state") return clean;
21571
+ if (command.type === "capture" || command.type === "captureRegions") return clean;
21572
+ if (command.type === "setAppearance" || command.type === "openUrl") return clean;
21573
+ if (command.type === "screenshot" && command.crop === void 0 && command.captureFilter === void 0 && (command.layers === void 0 || command.layers === "tenant")) {
21574
+ return clean;
21575
+ }
21576
+ throw new Error(`RNX Cloud does not support bridge command type ${command.type}`);
21577
+ }
21578
+ function createCloudBridgeForParsed(parsed) {
21579
+ const session = resolveCloudSessionForParsed(parsed);
21580
+ return session ? new CloudBridge(session) : null;
21581
+ }
21582
+ var import_node_crypto5, DEFAULT_RNX_CLOUD_ORIGIN, CloudBridge;
21583
+ var init_cloud_client = __esm({
21584
+ "cli/cloud-client.ts"() {
21585
+ "use strict";
21586
+ import_node_crypto5 = require("node:crypto");
21587
+ init_cloud_contract();
21588
+ init_public_brand();
21589
+ init_cloud_session();
21590
+ DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin;
21591
+ CloudBridge = class {
21592
+ constructor(session) {
21593
+ this.session = session;
21594
+ }
21595
+ plane = "cloud";
21596
+ async send(command, options) {
21597
+ const body = {
21598
+ id: (0, import_node_crypto5.randomUUID)(),
21599
+ claimId: this.session.claimId,
21600
+ command: cloudCommand(command)
21601
+ };
21602
+ let response;
21603
+ try {
21604
+ response = await fetch(
21605
+ `${this.session.apiOrigin}/v1/sims/${encodeURIComponent(this.session.simId)}/commands`,
21606
+ {
21607
+ method: "POST",
21608
+ headers: {
21609
+ authorization: `Bearer ${this.session.token}`,
21610
+ "content-type": "application/json"
21611
+ },
21612
+ body: JSON.stringify(body),
21613
+ ...options?.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
21614
+ }
21615
+ );
21616
+ } catch (error) {
21617
+ throw new Error(
21618
+ `could not reach RNX Cloud at ${this.session.apiOrigin}: ${error instanceof Error ? error.message : String(error)}`
21619
+ );
21620
+ }
21621
+ const text = await response.text();
21622
+ if (!response.ok) throw responseError(response.status, text);
21623
+ let parsed;
21624
+ try {
21625
+ parsed = JSON.parse(text);
21626
+ } catch {
21627
+ throw new Error("RNX Cloud command returned unreadable JSON");
21628
+ }
21629
+ if (!isRecord4(parsed) || parsed.id !== body.id) {
21630
+ throw new Error("RNX Cloud command returned an invalid command receipt");
21631
+ }
21632
+ if (typeof parsed.error === "string" && parsed.error) throw new Error(parsed.error);
21633
+ if (!Object.hasOwn(parsed, "result")) {
21634
+ throw new Error("RNX Cloud command receipt has no result");
21635
+ }
21636
+ return parsed.result;
21637
+ }
21638
+ listSims() {
21639
+ return Promise.reject(new Error("RNX Cloud does not expose a local simulator list"));
21640
+ }
21641
+ focusSim() {
21642
+ return Promise.reject(
21643
+ new Error("RNX Cloud simulators have no browser window to focus")
21644
+ );
21645
+ }
21646
+ closeSim() {
21647
+ return Promise.reject(new Error("RNX Cloud close uses the simulator DELETE route"));
21648
+ }
21649
+ claim() {
21650
+ return Promise.reject(new Error("RNX Cloud claim uses the simulator claim route"));
21651
+ }
21652
+ close() {
21653
+ }
21654
+ };
21655
+ }
21656
+ });
21657
+
21419
21658
  // cli/parent-pid.ts
21420
21659
  function getParentPid(pid) {
21421
21660
  if (!cached3) cached3 = build2();
@@ -21575,6 +21814,36 @@ var init_parent_pid = __esm({
21575
21814
  }
21576
21815
  });
21577
21816
 
21817
+ // cli/secure-file.ts
21818
+ function writeSecureJsonAtomic(filepath, value) {
21819
+ (0, import_node_fs12.mkdirSync)((0, import_node_path13.dirname)(filepath), { recursive: true });
21820
+ const temporary = `${filepath}.tmp-${process.pid}-${(0, import_node_crypto6.randomUUID)()}`;
21821
+ let descriptor = null;
21822
+ try {
21823
+ descriptor = (0, import_node_fs12.openSync)(temporary, "wx", 384);
21824
+ (0, import_node_fs12.fchmodSync)(descriptor, 384);
21825
+ (0, import_node_fs12.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
21826
+ `);
21827
+ (0, import_node_fs12.fsyncSync)(descriptor);
21828
+ (0, import_node_fs12.closeSync)(descriptor);
21829
+ descriptor = null;
21830
+ (0, import_node_fs12.renameSync)(temporary, filepath);
21831
+ } catch (error) {
21832
+ if (descriptor !== null) (0, import_node_fs12.closeSync)(descriptor);
21833
+ (0, import_node_fs12.rmSync)(temporary, { force: true });
21834
+ throw error;
21835
+ }
21836
+ }
21837
+ var import_node_crypto6, import_node_fs12, import_node_path13;
21838
+ var init_secure_file = __esm({
21839
+ "cli/secure-file.ts"() {
21840
+ "use strict";
21841
+ import_node_crypto6 = require("node:crypto");
21842
+ import_node_fs12 = require("node:fs");
21843
+ import_node_path13 = require("node:path");
21844
+ }
21845
+ });
21846
+
21578
21847
  // cli/current-sim.ts
21579
21848
  function findTopAncestor(start, maxDepth = 20) {
21580
21849
  let prev = start;
@@ -21621,50 +21890,57 @@ function getCurrentSimPath() {
21621
21890
  const key = getCliIdentityKey();
21622
21891
  return (0, import_path3.join)((0, import_os.tmpdir)(), `rnx-current-sim-${key}.json`);
21623
21892
  }
21624
- function readCurrentSim() {
21893
+ function legacyCloudCredentialDirectory(filepath) {
21894
+ return (0, import_path3.join)((0, import_path3.dirname)(filepath), `${(0, import_path3.basename)(filepath)}.credentials`);
21895
+ }
21896
+ function readCurrentTarget() {
21625
21897
  const filepath = getCurrentSimPath();
21898
+ (0, import_fs4.rmSync)(legacyCloudCredentialDirectory(filepath), { recursive: true, force: true });
21626
21899
  if (!(0, import_fs4.existsSync)(filepath)) return null;
21627
21900
  try {
21628
21901
  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") {
21902
+ if (parsed === null || typeof parsed !== "object") {
21630
21903
  (0, import_fs4.rmSync)(filepath, { force: true });
21631
21904
  return null;
21632
21905
  }
21633
- return {
21906
+ const version = Reflect.get(parsed, "version");
21907
+ const plane = Reflect.get(parsed, "plane");
21908
+ const simId = Reflect.get(parsed, "simId");
21909
+ const updatedAt = Reflect.get(parsed, "updatedAt");
21910
+ if (version !== 1 && version !== CURRENT_SIM_VERSION || version === CURRENT_SIM_VERSION && plane !== "local" || typeof simId !== "string" || !simId.trim() || typeof updatedAt !== "string") {
21911
+ (0, import_fs4.rmSync)(filepath, { force: true });
21912
+ return null;
21913
+ }
21914
+ const target = {
21634
21915
  version: CURRENT_SIM_VERSION,
21635
- simId: parsed.simId.trim(),
21636
- updatedAt: parsed.updatedAt
21916
+ plane: "local",
21917
+ simId: simId.trim(),
21918
+ updatedAt
21637
21919
  };
21920
+ if (version === 1) writeSecureJsonAtomic(filepath, target);
21921
+ return target;
21638
21922
  } catch {
21639
21923
  (0, import_fs4.rmSync)(filepath, { force: true });
21640
21924
  return null;
21641
21925
  }
21642
21926
  }
21643
21927
  function readCurrentSimId() {
21644
- return readCurrentSim()?.simId || null;
21928
+ return readCurrentTarget()?.simId ?? null;
21645
21929
  }
21646
21930
  function saveCurrentSimId(simId) {
21647
21931
  const normalized = simId.trim();
21648
21932
  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
- );
21933
+ writeSecureJsonAtomic(getCurrentSimPath(), {
21934
+ version: CURRENT_SIM_VERSION,
21935
+ plane: "local",
21936
+ simId: normalized,
21937
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
21938
+ });
21663
21939
  }
21664
21940
  function clearCurrentSimId() {
21665
21941
  (0, import_fs4.rmSync)(getCurrentSimPath(), { force: true });
21666
21942
  }
21667
- var import_fs4, import_os, import_path3, CURRENT_SIM_VERSION, CURRENT_SIM_PATH_ENV, IDENTITY_ENV_VARS, IMMORTAL_HOST_COMM_RE;
21943
+ var import_fs4, import_os, import_path3, CURRENT_SIM_VERSION, CURRENT_SIM_PATH_ENV, IDENTITY_ENV_VARS, IMMORTAL_HOST_COMM_RE, readCurrentSim;
21668
21944
  var init_current_sim = __esm({
21669
21945
  "cli/current-sim.ts"() {
21670
21946
  "use strict";
@@ -21672,7 +21948,8 @@ var init_current_sim = __esm({
21672
21948
  import_os = require("os");
21673
21949
  import_path3 = require("path");
21674
21950
  init_parent_pid();
21675
- CURRENT_SIM_VERSION = 1;
21951
+ init_secure_file();
21952
+ CURRENT_SIM_VERSION = 2;
21676
21953
  CURRENT_SIM_PATH_ENV = "SOOTSIM_CLI_CURRENT_SIM_PATH";
21677
21954
  IDENTITY_ENV_VARS = [
21678
21955
  "RNX_CLI_IDENTITY",
@@ -21703,6 +21980,7 @@ var init_current_sim = __esm({
21703
21980
  // vscode integrated terminal
21704
21981
  ];
21705
21982
  IMMORTAL_HOST_COMM_RE = /^(?:tmux|screen)|^tm$/i;
21983
+ readCurrentSim = readCurrentTarget;
21706
21984
  }
21707
21985
  });
21708
21986
 
@@ -21736,17 +22014,17 @@ function liveDevBridgeLockfiles() {
21736
22014
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
21737
22015
  }
21738
22016
  function checkoutRoot(cwd) {
21739
- let current = import_node_path13.default.resolve(cwd);
22017
+ let current = import_node_path14.default.resolve(cwd);
21740
22018
  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);
22019
+ if (import_node_fs13.default.existsSync(import_node_path14.default.join(current, ".git"))) return current;
22020
+ const parent = import_node_path14.default.dirname(current);
21743
22021
  if (parent === current) return null;
21744
22022
  current = parent;
21745
22023
  }
21746
22024
  }
21747
22025
  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);
22026
+ const relative = import_node_path14.default.relative(parent, child);
22027
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path14.default.sep}`) && !import_node_path14.default.isAbsolute(relative);
21750
22028
  }
21751
22029
  function resolveDefaultDevBridgeLockfile() {
21752
22030
  const lockfiles = liveDevBridgeLockfiles();
@@ -21933,9 +22211,9 @@ function parseBridgeCliArgs(args, opts = {}) {
21933
22211
  }
21934
22212
  }
21935
22213
  if (!simId) {
21936
- const savedSimId = readCurrentSimId();
21937
- if (savedSimId) {
21938
- simId = savedSimId.trim() || void 0;
22214
+ const savedSim = readCurrentSim();
22215
+ if (savedSim) {
22216
+ simId = savedSim.simId.trim() || void 0;
21939
22217
  simIdSource = "saved";
21940
22218
  }
21941
22219
  }
@@ -22031,6 +22309,7 @@ function createBridge(wsPort, opts = {}) {
22031
22309
  }
22032
22310
  });
22033
22311
  return {
22312
+ plane: "local",
22034
22313
  async send(cmd, callOpts) {
22035
22314
  const effectiveTimeoutMs = callOpts?.timeoutMs ?? commandTimeoutMs;
22036
22315
  const sendOnce = async (defaultSimId) => {
@@ -22113,6 +22392,8 @@ function createBridge(wsPort, opts = {}) {
22113
22392
  };
22114
22393
  }
22115
22394
  function createBridgeFromParsed(parsed) {
22395
+ const cloudBridge = createCloudBridgeForParsed(parsed);
22396
+ if (cloudBridge) return cloudBridge;
22116
22397
  return createBridge(parsed.wsPort, {
22117
22398
  commandTimeoutMs: parsed.commandTimeoutMs,
22118
22399
  simId: parsed.simId,
@@ -22175,16 +22456,17 @@ async function callInBridge(bridge, path20, ...args) {
22175
22456
  async function callInBridgeWrite(bridge, path20, ...args) {
22176
22457
  return bridge.send({ type: "call", path: path20, args, acquireLock: true });
22177
22458
  }
22178
- var import_node_fs12, import_node_path13, import_ws3, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
22459
+ var import_node_fs13, import_node_path14, import_ws3, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
22179
22460
  var init_ws_bridge = __esm({
22180
22461
  "cli/ws-bridge.ts"() {
22181
22462
  "use strict";
22182
- import_node_fs12 = __toESM(require("node:fs"), 1);
22183
- import_node_path13 = __toESM(require("node:path"), 1);
22463
+ import_node_fs13 = __toESM(require("node:fs"), 1);
22464
+ import_node_path14 = __toESM(require("node:path"), 1);
22184
22465
  import_ws3 = require("ws");
22185
22466
  init_bridge_constants();
22186
22467
  init_cli_constants();
22187
22468
  init_home_paths();
22469
+ init_cloud_client();
22188
22470
  init_current_sim();
22189
22471
  RNX_PLACEMENT_ENV = "RNX_PLACEMENT";
22190
22472
  resolvedWorld = null;
@@ -22220,9 +22502,8 @@ function isAppDeepLinkTarget(target) {
22220
22502
  async function openAppUrl(bridge, target) {
22221
22503
  const normalized = normalizeAppUrlTarget(target);
22222
22504
  const result = await bridge.send({
22223
- type: "call",
22224
- path: "SootSim.bridges.test.openDeepLink",
22225
- args: [normalized]
22505
+ type: "openUrl",
22506
+ url: normalized
22226
22507
  });
22227
22508
  if (result !== null && typeof result === "object" && Reflect.get(result, "ok") === false) {
22228
22509
  const error = Reflect.get(result, "error");
@@ -22543,9 +22824,9 @@ var init_profiles = __esm({
22543
22824
  // cli/app-project.ts
22544
22825
  function firstNamedFile(dir, names) {
22545
22826
  for (const name2 of names) {
22546
- const candidate = import_node_path14.default.join(dir, name2);
22827
+ const candidate = import_node_path15.default.join(dir, name2);
22547
22828
  try {
22548
- if (import_node_fs13.default.statSync(candidate).isFile()) return candidate;
22829
+ if (import_node_fs14.default.statSync(candidate).isFile()) return candidate;
22549
22830
  } catch {
22550
22831
  }
22551
22832
  }
@@ -22558,24 +22839,24 @@ function findRNXAppConfig(dir) {
22558
22839
  return firstNamedFile(dir, RNX_APP_CONFIG_NAMES);
22559
22840
  }
22560
22841
  function resolveAppProject(projectRoot) {
22561
- const appDir = import_node_path14.default.resolve(projectRoot);
22842
+ const appDir = import_node_path15.default.resolve(projectRoot);
22562
22843
  try {
22563
- if (!import_node_fs13.default.statSync(appDir).isDirectory()) return null;
22844
+ if (!import_node_fs14.default.statSync(appDir).isDirectory()) return null;
22564
22845
  } catch {
22565
22846
  return null;
22566
22847
  }
22567
22848
  const configPath = findRNXAppConfig(appDir);
22568
- if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs13.default.existsSync(import_node_path14.default.join(appDir, "package.json"))) {
22849
+ if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs14.default.existsSync(import_node_path15.default.join(appDir, "package.json"))) {
22569
22850
  return null;
22570
22851
  }
22571
22852
  return { appDir, configPath };
22572
22853
  }
22573
- var import_node_fs13, import_node_path14, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
22854
+ var import_node_fs14, import_node_path15, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
22574
22855
  var init_app_project = __esm({
22575
22856
  "cli/app-project.ts"() {
22576
22857
  "use strict";
22577
- import_node_fs13 = __toESM(require("node:fs"), 1);
22578
- import_node_path14 = __toESM(require("node:path"), 1);
22858
+ import_node_fs14 = __toESM(require("node:fs"), 1);
22859
+ import_node_path15 = __toESM(require("node:path"), 1);
22579
22860
  EXPO_APP_CONFIG_NAMES = [
22580
22861
  "app.config.js",
22581
22862
  "app.config.ts",
@@ -22604,13 +22885,13 @@ function isRNXConfig(value) {
22604
22885
  }
22605
22886
  function resolveConfigReplacementFiles(config, configPath) {
22606
22887
  if (!config.modules) return config;
22607
- const configDir = import_node_path15.default.dirname(configPath);
22888
+ const configDir = import_node_path16.default.dirname(configPath);
22608
22889
  return {
22609
22890
  ...config,
22610
22891
  modules: Object.fromEntries(
22611
22892
  Object.entries(config.modules).map(([name2, resolution]) => [
22612
22893
  name2,
22613
- resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path15.default.resolve(configDir, resolution.file) } : resolution
22894
+ resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path16.default.resolve(configDir, resolution.file) } : resolution
22614
22895
  ])
22615
22896
  )
22616
22897
  };
@@ -22640,12 +22921,12 @@ async function resolveRNXAppConfig(startDir = process.cwd()) {
22640
22921
  const project = resolveAppProject(startDir);
22641
22922
  return { project, config: await loadRNXAppConfig(project) };
22642
22923
  }
22643
- var import_node_module, import_node_path15, import_jiti, require2;
22924
+ var import_node_module, import_node_path16, import_jiti, require2;
22644
22925
  var init_app_config = __esm({
22645
22926
  "cli/app-config.ts"() {
22646
22927
  "use strict";
22647
22928
  import_node_module = require("node:module");
22648
- import_node_path15 = __toESM(require("node:path"), 1);
22929
+ import_node_path16 = __toESM(require("node:path"), 1);
22649
22930
  init_src();
22650
22931
  import_jiti = require("jiti");
22651
22932
  init_public_brand();
@@ -22713,13 +22994,13 @@ function discoverNativeLinkedAppFonts(opts) {
22713
22994
  scannedFontDirectories++;
22714
22995
  let entries;
22715
22996
  try {
22716
- entries = import_node_fs14.default.readdirSync(current.dir, { withFileTypes: true });
22997
+ entries = import_node_fs15.default.readdirSync(current.dir, { withFileTypes: true });
22717
22998
  } catch {
22718
22999
  continue;
22719
23000
  }
22720
23001
  for (const entry of entries) {
22721
23002
  if (entry.isSymbolicLink()) continue;
22722
- const fullPath = import_node_path16.default.join(current.dir, entry.name);
23003
+ const fullPath = import_node_path17.default.join(current.dir, entry.name);
22723
23004
  if (entry.isDirectory()) {
22724
23005
  if (current.depth < 4 && pending.length < 64 && !entry.name.startsWith(".")) {
22725
23006
  pending.push({ dir: fullPath, depth: current.depth + 1 });
@@ -22727,15 +23008,15 @@ function discoverNativeLinkedAppFonts(opts) {
22727
23008
  continue;
22728
23009
  }
22729
23010
  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));
23011
+ const relative = import_node_path17.default.relative(root, fullPath).split(import_node_path17.default.sep).join("/");
23012
+ fontPaths.push(import_node_path17.default.posix.join(metroPathPrefix, relative));
22732
23013
  if (fontPaths.length >= 256) break;
22733
23014
  }
22734
23015
  }
22735
23016
  };
22736
23017
  try {
22737
23018
  const packageJson = JSON.parse(
22738
- import_node_fs14.default.readFileSync(import_node_path16.default.join(selectedAppDir, "package.json"), "utf8")
23019
+ import_node_fs15.default.readFileSync(import_node_path17.default.join(selectedAppDir, "package.json"), "utf8")
22739
23020
  );
22740
23021
  const dependencyNames = /* @__PURE__ */ new Set();
22741
23022
  for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
@@ -22750,24 +23031,24 @@ function discoverNativeLinkedAppFonts(opts) {
22750
23031
  const config = Reflect.get(packageJson, "reactNativeVectorIcons");
22751
23032
  const configuredFontDir = config && typeof config === "object" ? Reflect.get(config, "fontDir") : null;
22752
23033
  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("/"));
23034
+ const root = import_node_path17.default.resolve(selectedAppDir, configuredDir);
23035
+ const relativeRoot = import_node_path17.default.relative(selectedAppDir, root);
23036
+ if (!relativeRoot.startsWith("..") && !import_node_path17.default.isAbsolute(relativeRoot)) {
23037
+ collectFontFiles(root, relativeRoot.split(import_node_path17.default.sep).join("/"));
22757
23038
  }
22758
- const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path16.default.join(selectedAppDir, "package.json"));
23039
+ const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path17.default.join(selectedAppDir, "package.json"));
22759
23040
  for (const dependencyName of vectorIconDependencies) {
22760
23041
  try {
22761
23042
  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}`;
23043
+ const dependencyPath = dependencyName.split("/").join(import_node_path17.default.sep);
23044
+ const marker = `${import_node_path17.default.sep}node_modules${import_node_path17.default.sep}${dependencyPath}${import_node_path17.default.sep}`;
22764
23045
  const markerIndex = entryPath.lastIndexOf(marker);
22765
23046
  if (markerIndex === -1) continue;
22766
23047
  const packageRoot = entryPath.slice(0, markerIndex + marker.length - 1);
22767
23048
  const fontsDir = dependencyName === "react-native-vector-icons" ? "Fonts" : "fonts";
22768
23049
  collectFontFiles(
22769
- import_node_path16.default.join(packageRoot, fontsDir),
22770
- import_node_path16.default.posix.join("node_modules", dependencyName, fontsDir)
23050
+ import_node_path17.default.join(packageRoot, fontsDir),
23051
+ import_node_path17.default.posix.join("node_modules", dependencyName, fontsDir)
22771
23052
  );
22772
23053
  } catch {
22773
23054
  }
@@ -22776,37 +23057,37 @@ function discoverNativeLinkedAppFonts(opts) {
22776
23057
  } catch {
22777
23058
  }
22778
23059
  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"));
23060
+ const configPath = import_node_path17.default.join(selectedAppDir, "react-native.config.js");
23061
+ if (import_node_fs15.default.statSync(configPath).isFile()) {
23062
+ const appConfigRequire = (0, import_node_module2.createRequire)(import_node_path17.default.join(selectedAppDir, "package.json"));
22782
23063
  const rnConfig = appConfigRequire(configPath);
22783
23064
  const assets = rnConfig && typeof rnConfig === "object" ? Reflect.get(rnConfig, "assets") : null;
22784
23065
  if (Array.isArray(assets)) {
22785
23066
  for (const assetDir of assets) {
22786
23067
  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("/"));
23068
+ const root = import_node_path17.default.resolve(selectedAppDir, assetDir);
23069
+ const relativeRoot = import_node_path17.default.relative(selectedAppDir, root);
23070
+ if (relativeRoot.startsWith("..") || import_node_path17.default.isAbsolute(relativeRoot)) continue;
23071
+ collectFontFiles(root, relativeRoot.split(import_node_path17.default.sep).join("/"));
22791
23072
  }
22792
23073
  }
22793
23074
  }
22794
23075
  } catch {
22795
23076
  }
22796
23077
  if (platform === "ios") {
22797
- const iosDir = import_node_path16.default.join(selectedAppDir, "ios");
23078
+ const iosDir = import_node_path17.default.join(selectedAppDir, "ios");
22798
23079
  try {
22799
- const targetDirs = import_node_fs14.default.readdirSync(iosDir, { withFileTypes: true }).filter(
23080
+ const targetDirs = import_node_fs15.default.readdirSync(iosDir, { withFileTypes: true }).filter(
22800
23081
  (entry) => entry.isDirectory() && !entry.isSymbolicLink() && entry.name !== "Pods" && entry.name !== "build" && !entry.name.startsWith(".")
22801
23082
  ).slice(0, 64);
22802
- const plistPaths = [import_node_path16.default.join(iosDir, "Info.plist")];
23083
+ const plistPaths = [import_node_path17.default.join(iosDir, "Info.plist")];
22803
23084
  for (const entry of targetDirs) {
22804
- plistPaths.push(import_node_path16.default.join(iosDir, entry.name, "Info.plist"));
23085
+ plistPaths.push(import_node_path17.default.join(iosDir, entry.name, "Info.plist"));
22805
23086
  }
22806
23087
  for (const plistPath of plistPaths) {
22807
23088
  let parsed;
22808
23089
  try {
22809
- parsed = parsePlistSource(import_node_fs14.default.readFileSync(plistPath, "utf8"));
23090
+ parsed = parsePlistSource(import_node_fs15.default.readFileSync(plistPath, "utf8"));
22810
23091
  } catch {
22811
23092
  continue;
22812
23093
  }
@@ -22816,21 +23097,21 @@ function discoverNativeLinkedAppFonts(opts) {
22816
23097
  for (const declaredFont of declaredFonts) {
22817
23098
  if (typeof declaredFont !== "string") continue;
22818
23099
  const candidates = [
22819
- import_node_path16.default.resolve(import_node_path16.default.dirname(plistPath), declaredFont),
22820
- import_node_path16.default.resolve(iosDir, declaredFont)
23100
+ import_node_path17.default.resolve(import_node_path17.default.dirname(plistPath), declaredFont),
23101
+ import_node_path17.default.resolve(iosDir, declaredFont)
22821
23102
  ];
22822
23103
  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;
23104
+ const relative = import_node_path17.default.relative(selectedAppDir, candidate);
23105
+ if (relative.startsWith("..") || import_node_path17.default.isAbsolute(relative)) return false;
22825
23106
  try {
22826
- return import_node_fs14.default.statSync(candidate).isFile();
23107
+ return import_node_fs15.default.statSync(candidate).isFile();
22827
23108
  } catch {
22828
23109
  return false;
22829
23110
  }
22830
23111
  });
22831
23112
  if (!fontPath) continue;
22832
23113
  fontPaths.push(
22833
- import_node_path16.default.relative(selectedAppDir, fontPath).split(import_node_path16.default.sep).join("/")
23114
+ import_node_path17.default.relative(selectedAppDir, fontPath).split(import_node_path17.default.sep).join("/")
22834
23115
  );
22835
23116
  }
22836
23117
  }
@@ -22869,13 +23150,13 @@ async function resolveMetroProjectRoot(bundleUrl, opts) {
22869
23150
  function encodeAppFontsWire(specs) {
22870
23151
  return specs.map((spec) => `${encodeURIComponent(spec.url)}::${encodeURIComponent(spec.family)}`).join(",");
22871
23152
  }
22872
- var import_node_fs14, import_node_module2, import_node_path16, import_config_plugin_fonts, require3;
23153
+ var import_node_fs15, import_node_module2, import_node_path17, import_config_plugin_fonts, require3;
22873
23154
  var init_app_fonts = __esm({
22874
23155
  "cli/app-fonts.ts"() {
22875
23156
  "use strict";
22876
- import_node_fs14 = __toESM(require("node:fs"), 1);
23157
+ import_node_fs15 = __toESM(require("node:fs"), 1);
22877
23158
  import_node_module2 = require("node:module");
22878
- import_node_path16 = __toESM(require("node:path"), 1);
23159
+ import_node_path17 = __toESM(require("node:path"), 1);
22879
23160
  import_config_plugin_fonts = require("sootsim-engine/engine/config-plugin-fonts");
22880
23161
  init_dev_server_scanner();
22881
23162
  init_plist();
@@ -23953,7 +24234,7 @@ var init_browser_cache = __esm({
23953
24234
  });
23954
24235
 
23955
24236
  // cli/drivers/playwright-provisioning.ts
23956
- function isRecord3(value) {
24237
+ function isRecord5(value) {
23957
24238
  return typeof value === "object" && value !== null && !Array.isArray(value);
23958
24239
  }
23959
24240
  function resolvePlaywright() {
@@ -23966,20 +24247,20 @@ function resolvePlaywright() {
23966
24247
  try {
23967
24248
  const modulePath = requireFromRoot.resolve(spec);
23968
24249
  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;
24250
+ const parsed = JSON.parse((0, import_node_fs16.readFileSync)(packageJsonPath, "utf8"));
24251
+ if (!isRecord5(parsed) || typeof parsed.version !== "string") continue;
23971
24252
  const bin = parsed.bin;
23972
24253
  let binPath = null;
23973
24254
  if (typeof bin === "string") {
23974
24255
  binPath = bin;
23975
- } else if (isRecord3(bin)) {
24256
+ } else if (isRecord5(bin)) {
23976
24257
  const named = bin.playwright ?? bin["playwright-core"];
23977
24258
  const first = Object.values(bin).find((value) => typeof value === "string");
23978
24259
  binPath = typeof named === "string" ? named : typeof first === "string" ? first : null;
23979
24260
  }
23980
24261
  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;
24262
+ const cliPath = (0, import_node_path18.join)((0, import_node_path18.dirname)(packageJsonPath), binPath);
24263
+ if (!(0, import_node_fs16.existsSync)(cliPath)) continue;
23983
24264
  return {
23984
24265
  spec,
23985
24266
  modulePath,
@@ -24012,13 +24293,13 @@ function probeBrowser(resolved, env) {
24012
24293
  }
24013
24294
  try {
24014
24295
  const parsed = JSON.parse(probe3.stdout);
24015
- if (!isRecord3(parsed) || typeof parsed.executablePath !== "string" || typeof parsed.exists !== "boolean") {
24296
+ if (!isRecord5(parsed) || typeof parsed.executablePath !== "string" || typeof parsed.exists !== "boolean") {
24016
24297
  throw new Error("probe returned an invalid result");
24017
24298
  }
24018
24299
  return {
24019
24300
  ok: true,
24020
24301
  executablePath: parsed.executablePath,
24021
- exists: parsed.exists && (0, import_node_fs15.existsSync)(parsed.executablePath)
24302
+ exists: parsed.exists && (0, import_node_fs16.existsSync)(parsed.executablePath)
24022
24303
  };
24023
24304
  } catch (error) {
24024
24305
  return {
@@ -24057,14 +24338,14 @@ ${detail}` : ""}`
24057
24338
  }
24058
24339
  return { ok: true, executablePath: after.executablePath, installed: true };
24059
24340
  }
24060
- var import_node_child_process6, import_node_fs15, import_node_module4, import_node_path17, PLAYWRIGHT_BROWSER_PROBE;
24341
+ var import_node_child_process6, import_node_fs16, import_node_module4, import_node_path18, PLAYWRIGHT_BROWSER_PROBE;
24061
24342
  var init_playwright_provisioning = __esm({
24062
24343
  "cli/drivers/playwright-provisioning.ts"() {
24063
24344
  "use strict";
24064
24345
  import_node_child_process6 = require("node:child_process");
24065
- import_node_fs15 = require("node:fs");
24346
+ import_node_fs16 = require("node:fs");
24066
24347
  import_node_module4 = require("node:module");
24067
- import_node_path17 = require("node:path");
24348
+ import_node_path18 = require("node:path");
24068
24349
  PLAYWRIGHT_BROWSER_PROBE = `
24069
24350
  const { existsSync } = require('node:fs');
24070
24351
  const modulePath = process.env.SOOTSIM_PW_MODULE;
@@ -24474,7 +24755,7 @@ const cleanupProfile = () => {
24474
24755
  // we are spawned detached + unref'd so \`rnx open\` returns immediately,
24475
24756
  // which means nothing in the OS process tree ties Chrome's lifetime to the
24476
24757
  // 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
24758
+ // safety net is cleared, so absent an explicit \`rnx close\` (or the
24478
24759
  // daemon's idle reaper closing the sim socket) the host would hold Chrome
24479
24760
  // open forever after its owner exits. poll the owning session's pid and tear
24480
24761
  // down when it's gone. ESRCH (kill throws) = owner exited; EPERM = alive but
@@ -24765,10 +25046,10 @@ var init_run_rnx = __esm({
24765
25046
  });
24766
25047
 
24767
25048
  // cli/commands/inspect/env.ts
24768
- function isAgentEnv() {
24769
- if (process.env.RNX_AGENT === "0") return false;
25049
+ function isAgentEnv(env = process.env) {
25050
+ if (env.RNX_AGENT === "0") return false;
24770
25051
  for (const name2 of AGENT_ENV_VARS) {
24771
- const v = process.env[name2];
25052
+ const v = env[name2];
24772
25053
  if (v && v.trim() && v !== "0") return true;
24773
25054
  }
24774
25055
  return false;
@@ -24933,7 +25214,7 @@ async function activateVersion(version) {
24933
25214
  const notified = await tellDaemonRuntimeChanged(version);
24934
25215
  if (!notified) {
24935
25216
  console.log(
24936
- ` (no daemon running \u2014 next sootsim/electron launch will pick up ${version})`
25217
+ ` (no daemon running \u2014 next rnx/electron launch will pick up ${version})`
24937
25218
  );
24938
25219
  }
24939
25220
  }
@@ -25240,14 +25521,14 @@ function assertExplicitBridgeLock(preferredPort, explicitPort, lock) {
25240
25521
  }
25241
25522
  async function ensureRuntimeForOpen() {
25242
25523
  const active = readActiveRuntime();
25243
- if (active && import_node_fs16.default.existsSync(runtimeDir(active))) return;
25524
+ if (active && import_node_fs17.default.existsSync(runtimeDir(active))) return;
25244
25525
  console.error(" installing rnx engine runtime...");
25245
25526
  const { runRuntime: runRuntime2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
25246
25527
  await runRuntime2(["install"], {});
25247
25528
  }
25248
25529
  function spawnSootsim(args) {
25249
25530
  const entry = process.argv[1];
25250
- if (entry && import_node_fs16.default.existsSync(entry)) {
25531
+ if (entry && import_node_fs17.default.existsSync(entry)) {
25251
25532
  return (0, import_node_child_process7.spawn)(process.execPath, [entry, ...args], {
25252
25533
  detached: true,
25253
25534
  stdio: "ignore",
@@ -25394,10 +25675,10 @@ async function buildResolvedBundleOpenUrl(target, baseUrl, opts = {}) {
25394
25675
  }
25395
25676
  function normalizeReplacementPath(filePath) {
25396
25677
  if (filePath.startsWith("~/")) {
25397
- return import_node_path18.default.join(import_node_os3.default.homedir(), filePath.slice(2));
25678
+ return import_node_path19.default.join(import_node_os3.default.homedir(), filePath.slice(2));
25398
25679
  }
25399
- if (import_node_path18.default.isAbsolute(filePath)) return filePath;
25400
- return import_node_path18.default.resolve(process.cwd(), filePath);
25680
+ if (import_node_path19.default.isAbsolute(filePath)) return filePath;
25681
+ return import_node_path19.default.resolve(process.cwd(), filePath);
25401
25682
  }
25402
25683
  function parseOpenRuntimeConfig(args) {
25403
25684
  const modules = {};
@@ -25416,7 +25697,7 @@ function parseOpenRuntimeConfig(args) {
25416
25697
  }
25417
25698
  const moduleName = raw.slice(0, eqIndex).trim();
25418
25699
  const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim());
25419
- if (!import_node_fs16.default.existsSync(filePath)) {
25700
+ if (!import_node_fs17.default.existsSync(filePath)) {
25420
25701
  console.error(` rnx open: replacement file not found: ${filePath}`);
25421
25702
  rnxExit(1);
25422
25703
  }
@@ -25447,7 +25728,7 @@ async function resolveOpenAppProject(resolvedBundle) {
25447
25728
  return advertisedRoot ? resolveAppProject(advertisedRoot) : null;
25448
25729
  }
25449
25730
  async function ensureConfiguredRuntime(version) {
25450
- if (import_node_fs16.default.existsSync(import_node_path18.default.join(runtimeDir(version), "index.html"))) return;
25731
+ if (import_node_fs17.default.existsSync(import_node_path19.default.join(runtimeDir(version), "index.html"))) return;
25451
25732
  console.error(` installing configured engine runtime ${version}...`);
25452
25733
  const result = await rnxRuntime.install({
25453
25734
  version,
@@ -25807,7 +26088,7 @@ function playwrightHostPidForSim(sim) {
25807
26088
  function signalDriverLaunchConnected(result) {
25808
26089
  if (!result.connectAckFile) return false;
25809
26090
  try {
25810
- import_node_fs16.default.writeFileSync(
26091
+ import_node_fs17.default.writeFileSync(
25811
26092
  result.connectAckFile,
25812
26093
  `${JSON.stringify({ connectedAt: Date.now(), pid: result.pid ?? null })}
25813
26094
  `,
@@ -25821,19 +26102,19 @@ function signalDriverLaunchConnected(result) {
25821
26102
  function printDriverDiagnosticLogTail(result) {
25822
26103
  if (!result.diagnosticLogPath) return;
25823
26104
  try {
25824
- const detail = import_node_fs16.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
26105
+ const detail = import_node_fs17.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
25825
26106
  if (!detail) {
25826
- console.error(` ${import_node_path18.default.basename(result.diagnosticLogPath)} was empty`);
26107
+ console.error(` ${import_node_path19.default.basename(result.diagnosticLogPath)} was empty`);
25827
26108
  return;
25828
26109
  }
25829
26110
  const shown = detail.length > 4e3 ? `\u2026
25830
26111
  ${detail.slice(-4e3)}` : detail;
25831
- console.error(` ${import_node_path18.default.basename(result.diagnosticLogPath)}:`);
26112
+ console.error(` ${import_node_path19.default.basename(result.diagnosticLogPath)}:`);
25832
26113
  console.error(shown);
25833
26114
  } catch (err) {
25834
26115
  const message = err instanceof Error ? err.message : String(err);
25835
26116
  console.error(
25836
- ` failed to read ${import_node_path18.default.basename(result.diagnosticLogPath)}: ${message}`
26117
+ ` failed to read ${import_node_path19.default.basename(result.diagnosticLogPath)}: ${message}`
25837
26118
  );
25838
26119
  }
25839
26120
  }
@@ -26644,14 +26925,14 @@ async function runCloseCommand(args, opts = {}) {
26644
26925
  bridge.close();
26645
26926
  }
26646
26927
  }
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;
26928
+ 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
26929
  var init_control = __esm({
26649
26930
  "cli/commands/control.ts"() {
26650
26931
  "use strict";
26651
26932
  import_node_child_process7 = require("node:child_process");
26652
- import_node_fs16 = __toESM(require("node:fs"), 1);
26933
+ import_node_fs17 = __toESM(require("node:fs"), 1);
26653
26934
  import_node_os3 = __toESM(require("node:os"), 1);
26654
- import_node_path18 = __toESM(require("node:path"), 1);
26935
+ import_node_path19 = __toESM(require("node:path"), 1);
26655
26936
  import_settings = require("sootsim-engine/settings");
26656
26937
  import_ws5 = require("ws");
26657
26938
  init_dev_server_scanner();
@@ -27515,14 +27796,14 @@ process.stdin.on('end', async () => {
27515
27796
  function getBaseDir() {
27516
27797
  const explicit = process.env[SESSION_FILE_ENV];
27517
27798
  if (explicit?.trim()) {
27518
- return (0, import_node_path19.dirname)((0, import_node_path19.resolve)(explicit));
27799
+ return (0, import_node_path20.dirname)((0, import_node_path20.resolve)(explicit));
27519
27800
  }
27520
27801
  return electronUserDataDir();
27521
27802
  }
27522
27803
  function getSharedDesktopAuthFilePath() {
27523
27804
  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");
27805
+ if (explicit?.trim()) return (0, import_node_path20.resolve)(explicit);
27806
+ return (0, import_node_path20.join)(getBaseDir(), "desktop-auth.json");
27526
27807
  }
27527
27808
  function normalizeUser(input) {
27528
27809
  if (!input || typeof input !== "object") return null;
@@ -27569,26 +27850,26 @@ function normalizeSession(input) {
27569
27850
  }
27570
27851
  function readSharedDesktopAuthSession() {
27571
27852
  const filepath = getSharedDesktopAuthFilePath();
27572
- if (!(0, import_node_fs17.existsSync)(filepath)) return null;
27853
+ if (!(0, import_node_fs18.existsSync)(filepath)) return null;
27573
27854
  try {
27574
- const parsed = JSON.parse((0, import_node_fs17.readFileSync)(filepath, "utf8"));
27855
+ const parsed = JSON.parse((0, import_node_fs18.readFileSync)(filepath, "utf8"));
27575
27856
  const normalized = normalizeSession(parsed);
27576
27857
  if (!normalized) {
27577
- (0, import_node_fs17.rmSync)(filepath, { force: true });
27858
+ (0, import_node_fs18.rmSync)(filepath, { force: true });
27578
27859
  return null;
27579
27860
  }
27580
27861
  return normalized;
27581
27862
  } catch {
27582
- (0, import_node_fs17.rmSync)(filepath, { force: true });
27863
+ (0, import_node_fs18.rmSync)(filepath, { force: true });
27583
27864
  return null;
27584
27865
  }
27585
27866
  }
27586
- var import_node_fs17, import_node_path19, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
27867
+ var import_node_fs18, import_node_path20, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
27587
27868
  var init_shared_session = __esm({
27588
27869
  "src/auth/shared-session.ts"() {
27589
27870
  "use strict";
27590
- import_node_fs17 = require("node:fs");
27591
- import_node_path19 = require("node:path");
27871
+ import_node_fs18 = require("node:fs");
27872
+ import_node_path20 = require("node:path");
27592
27873
  init_home_paths();
27593
27874
  SESSION_VERSION = 1;
27594
27875
  SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
@@ -29176,7 +29457,7 @@ var init_bridge_flow_runner = __esm({
29176
29457
  await this.resetGuestAppData();
29177
29458
  await this.rearmCaptureAfterRuntimeReload();
29178
29459
  }
29179
- // maestro clearKeychain: there is no real keychain in sootsim's browser
29460
+ // maestro clearKeychain: there is no real keychain in rnx's browser
29180
29461
  // sandbox. log loudly so the author notices, then continue — matching the
29181
29462
  // "loud warning, not silent skip" policy in the plan.
29182
29463
  clearKeychain() {