rnxsim 0.1.343 → 0.1.345

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 (209) hide show
  1. package/README.md +59 -31
  2. package/cli/app-fonts.ts +8 -28
  3. package/cli/app-project.ts +12 -184
  4. package/cli/app-splash.ts +3 -17
  5. package/cli/bin.ts +15 -28
  6. package/cli/commands/control.ts +20 -30
  7. package/cli/commands/daemon.ts +19 -44
  8. package/cli/commands/flow.ts +0 -27
  9. package/cli/commands/install-desktop.ts +2 -16
  10. package/cli/commands/login.ts +3 -43
  11. package/cli/commands/logout.ts +0 -7
  12. package/cli/commands/serve.ts +2 -32
  13. package/cli/commands/setup.ts +20 -194
  14. package/cli/commands/telemetry.ts +7 -5
  15. package/cli/commands/upload.ts +1 -48
  16. package/cli/commands/welcome.ts +64 -0
  17. package/cli/outbound-endpoints.ts +586 -0
  18. package/cli/privacy.ts +53 -0
  19. package/cli/runtime-notes.ts +2 -2
  20. package/cli/telemetry.ts +46 -149
  21. package/dist-cli/bin.js +6 -6
  22. package/dist-cli/chunks/{agent-RFD32ACU.js → agent-52G55XTO.js} +2 -2
  23. package/dist-cli/chunks/{agent-wrapper-2YLW2HQK.js → agent-wrapper-BOZM43ON.js} +2 -2
  24. package/dist-cli/chunks/{app-fonts-6OATDF3Y.js → app-fonts-A2L6V5EZ.js} +2 -2
  25. package/dist-cli/chunks/{assert-PSQICFOY.js → assert-CJKIP4LN.js} +2 -2
  26. package/dist-cli/chunks/{auth-XWLFKZT7.js → auth-VHHN3TOB.js} +2 -2
  27. package/dist-cli/chunks/{beta-I6WTVM6C.js → beta-EBDIGV3E.js} +2 -2
  28. package/dist-cli/chunks/{camera-45HKTLWG.js → camera-5F4KKCQ3.js} +2 -2
  29. package/dist-cli/chunks/chunk-3DBTS4MC.js +15 -0
  30. package/dist-cli/chunks/chunk-3N7M5HMJ.js +6 -0
  31. package/dist-cli/chunks/{chunk-RWYA2USN.js → chunk-4BIPA7MD.js} +1 -1
  32. package/dist-cli/chunks/chunk-4OKV7T5D.js +4 -0
  33. package/dist-cli/chunks/{chunk-T4T6DDKM.js → chunk-4ZILDTZX.js} +2 -2
  34. package/dist-cli/chunks/{chunk-R7X74I3I.js → chunk-5GXRRUOA.js} +26 -26
  35. package/dist-cli/chunks/chunk-63ASUHOV.js +4 -0
  36. package/dist-cli/chunks/{chunk-DASP2EKF.js → chunk-7GO2DSYQ.js} +1 -1
  37. package/dist-cli/chunks/{chunk-HJXIBZ35.js → chunk-7LGINKM6.js} +2 -2
  38. package/dist-cli/chunks/{chunk-2IPMH24A.js → chunk-7UIIAHVU.js} +2 -2
  39. package/dist-cli/chunks/{chunk-ES5BHPTS.js → chunk-7V4KPZ7O.js} +2 -2
  40. package/dist-cli/chunks/{chunk-FCHRGEHA.js → chunk-7YMQRA6L.js} +2 -2
  41. package/dist-cli/chunks/{chunk-DC7TS573.js → chunk-A2Q76LJV.js} +2 -2
  42. package/dist-cli/chunks/{chunk-SAUCA7SN.js → chunk-BCQV63BW.js} +45 -45
  43. package/dist-cli/chunks/{chunk-3FYJ7U3C.js → chunk-BFTDL2GX.js} +17 -17
  44. package/dist-cli/chunks/chunk-BHKSCNDC.js +4 -0
  45. package/dist-cli/chunks/{chunk-QPTJNLPT.js → chunk-DGTV3KOW.js} +1 -1
  46. package/dist-cli/chunks/chunk-DHM3JDCK.js +4 -0
  47. package/dist-cli/chunks/{chunk-TQRVLK64.js → chunk-DPP2M4AJ.js} +1 -1
  48. package/dist-cli/chunks/chunk-E2RGSCHT.js +4 -0
  49. package/dist-cli/chunks/{chunk-TQ5OPG33.js → chunk-EPLVIDPC.js} +1 -1
  50. package/dist-cli/chunks/{chunk-P3IDK7FR.js → chunk-GKGXULJK.js} +2 -2
  51. package/dist-cli/chunks/{chunk-34KDMQ6U.js → chunk-HK7YKH6N.js} +2 -2
  52. package/dist-cli/chunks/{chunk-BJUNXCPO.js → chunk-HXE344V3.js} +1 -1
  53. package/dist-cli/chunks/{chunk-HJHFC5ZZ.js → chunk-JC33TOAF.js} +1 -1
  54. package/dist-cli/chunks/{chunk-G3B2RZ47.js → chunk-KA4PJNTE.js} +2 -2
  55. package/dist-cli/chunks/{chunk-VC4TDPJQ.js → chunk-KH777UAO.js} +3 -3
  56. package/dist-cli/chunks/{chunk-4PFMDBLB.js → chunk-KRZPFXVN.js} +1 -1
  57. package/dist-cli/chunks/{chunk-6SLM6URN.js → chunk-KZFJNDH3.js} +1 -1
  58. package/dist-cli/chunks/{chunk-KLMWLQAO.js → chunk-LAQGSSTT.js} +1 -1
  59. package/dist-cli/chunks/chunk-LRM2XCEQ.js +11 -0
  60. package/dist-cli/chunks/{chunk-YKKVDKLE.js → chunk-NUPRHZQW.js} +2 -2
  61. package/dist-cli/chunks/{chunk-AKV2DGKM.js → chunk-OPHT4DUO.js} +1 -1
  62. package/dist-cli/chunks/{chunk-KON6XG4Y.js → chunk-P2CGGMU2.js} +2 -2
  63. package/dist-cli/chunks/{chunk-5FB4EA33.js → chunk-PLB6UVVX.js} +2 -2
  64. package/dist-cli/chunks/{chunk-FRRHCLG2.js → chunk-PLUWN2V5.js} +1 -1
  65. package/dist-cli/chunks/{chunk-WDA4QR6U.js → chunk-QNBBKRCA.js} +2 -2
  66. package/dist-cli/chunks/{chunk-G7S6T5OX.js → chunk-QNRWPAOA.js} +2 -2
  67. package/dist-cli/chunks/{chunk-2EVBZ37G.js → chunk-R7ZNRGQN.js} +1 -1
  68. package/dist-cli/chunks/{chunk-TMJGPUWJ.js → chunk-RB4VFUNM.js} +3 -3
  69. package/dist-cli/chunks/chunk-RT3DXTDP.js +25 -0
  70. package/dist-cli/chunks/{chunk-OYLIWHDB.js → chunk-RTDHX6CW.js} +2 -2
  71. package/dist-cli/chunks/{chunk-YLFDA4WW.js → chunk-T7SBFG5I.js} +2 -2
  72. package/dist-cli/chunks/{chunk-W7EXE22C.js → chunk-TEU5NGUA.js} +2 -2
  73. package/dist-cli/chunks/chunk-TMRD5MHL.js +99 -0
  74. package/dist-cli/chunks/{chunk-SU7RE3PV.js → chunk-TOF5TA5X.js} +11 -11
  75. package/dist-cli/chunks/{chunk-6IVEGYWV.js → chunk-U5YHXQHH.js} +2 -2
  76. package/dist-cli/chunks/{chunk-BA3HRFJ5.js → chunk-UVOEHIGP.js} +1 -1
  77. package/dist-cli/chunks/{chunk-SW4DV2TY.js → chunk-VLBN3KRC.js} +1 -1
  78. package/dist-cli/chunks/{chunk-7WKV6EKX.js → chunk-WPPGU5IK.js} +2 -2
  79. package/dist-cli/chunks/{chunk-JOE3J46E.js → chunk-WU72KL6Z.js} +2 -2
  80. package/dist-cli/chunks/chunk-XJBFJGNT.js +4 -0
  81. package/dist-cli/chunks/{chunk-3CMKNWRL.js → chunk-XXZLK4BY.js} +1 -1
  82. package/dist-cli/chunks/{chunk-BKMPV6N4.js → chunk-YVSUFVVB.js} +1 -1
  83. package/dist-cli/chunks/{chunk-RO3WVQPB.js → chunk-ZMTZ5G5H.js} +2 -2
  84. package/dist-cli/chunks/{cleanup-OU4VKFC3.js → cleanup-MAVZJXVW.js} +2 -2
  85. package/dist-cli/chunks/cli-version-2B4KRNML.js +4 -0
  86. package/dist-cli/chunks/{compat-GAEKP67T.js → compat-7AURLGCS.js} +2 -2
  87. package/dist-cli/chunks/config-TN3Y4J5N.js +13 -0
  88. package/dist-cli/chunks/{control-VQJURMFD.js → control-EV2ELU5R.js} +2 -2
  89. package/dist-cli/chunks/daemon-KKDG5MYR.js +4 -0
  90. package/dist-cli/chunks/{debug-F5N2LO4T.js → debug-JICITUB6.js} +3 -3
  91. package/dist-cli/chunks/{desktop-I4ILT4BS.js → desktop-4F5HGLLX.js} +3 -3
  92. package/dist-cli/chunks/{detox-2HMPYSP4.js → detox-ZC7NHLJI.js} +2 -2
  93. package/dist-cli/chunks/{device-5LWLOB5R.js → device-V57GHRXX.js} +2 -2
  94. package/dist-cli/chunks/{diagnose-5S4IQPAP.js → diagnose-FF4IWYXC.js} +2 -2
  95. package/dist-cli/chunks/{disk-cleanup-CJSZVJQX.js → disk-cleanup-T277L6ZO.js} +2 -2
  96. package/dist-cli/chunks/drivers-LCX7S76X.js +4 -0
  97. package/dist-cli/chunks/{film-HCPU2TDB.js → film-G7ZAPWB7.js} +3 -3
  98. package/dist-cli/chunks/flow-ONZGGFGD.js +4 -0
  99. package/dist-cli/chunks/help-DROVDXJM.js +4 -0
  100. package/dist-cli/chunks/{hidden-runtime-alias-YYUEZQPR.js → hidden-runtime-alias-VSJNNSNI.js} +2 -2
  101. package/dist-cli/chunks/{home-paths-3FXISM5I.js → home-paths-75HP3HKA.js} +2 -2
  102. package/dist-cli/chunks/inspect-MDA7ESGQ.js +4 -0
  103. package/dist-cli/chunks/install-desktop-EIVZHIFA.js +4 -0
  104. package/dist-cli/chunks/login-ZSIHGHUK.js +26 -0
  105. package/dist-cli/chunks/logout-Q4DXMI2C.js +4 -0
  106. package/dist-cli/chunks/{maestro-F5KK4RC4.js → maestro-LE5LCRBL.js} +3 -3
  107. package/dist-cli/chunks/{maestro-generate-6SUU3Y5I.js → maestro-generate-D3ZIJCRL.js} +3 -3
  108. package/dist-cli/chunks/{mode-CSWGOJLI.js → mode-BWX6MDPJ.js} +2 -2
  109. package/dist-cli/chunks/{optional-demo-registry-Y6VJ4SVV.js → optional-demo-registry-6CVASL3S.js} +2 -2
  110. package/dist-cli/chunks/{perf-NRWWAJL4.js → perf-NXQZISN6.js} +2 -2
  111. package/dist-cli/chunks/{permissions-OD62A3DJ.js → permissions-5PD67RRO.js} +2 -2
  112. package/dist-cli/chunks/privacy-H5KNRNIZ.js +4 -0
  113. package/dist-cli/chunks/{record-JZT2GM3G.js → record-YJF4KXMR.js} +3 -3
  114. package/dist-cli/chunks/{report-issue-ZWSNHOPB.js → report-issue-XOL5FQBG.js} +2 -2
  115. package/dist-cli/chunks/reset-5LZN5P5B.js +4 -0
  116. package/dist-cli/chunks/runtime-U36VWBH3.js +4 -0
  117. package/dist-cli/chunks/{screenshot-command-ZGJR7ICY.js → screenshot-command-VRPJXWFS.js} +3 -3
  118. package/dist-cli/chunks/{screenshot-layers-5AQKOJZK.js → screenshot-layers-QP4WA2F5.js} +2 -2
  119. package/dist-cli/chunks/{screenshots-capture-CPTWK5Y3.js → screenshots-capture-O2IBAVSP.js} +2 -2
  120. package/dist-cli/chunks/serve-WC6RZNJL.js +118 -0
  121. package/dist-cli/chunks/setup-A6BIG3KL.js +30 -0
  122. package/dist-cli/chunks/{skills-NVWRBMEO.js → skills-A7YC6RL5.js} +2 -2
  123. package/dist-cli/chunks/{state-QGLTKN6K.js → state-IJI5YROJ.js} +2 -2
  124. package/dist-cli/chunks/{storage-OZMEZ4AV.js → storage-Z2DBO2PQ.js} +4 -4
  125. package/dist-cli/chunks/store-BYTCKNIC.js +4 -0
  126. package/dist-cli/chunks/telemetry-JLHFMD7N.js +4 -0
  127. package/dist-cli/chunks/{timeline-EXLDREY3.js → timeline-N7V2M6QA.js} +2 -2
  128. package/dist-cli/chunks/{upgrade-4VWXQO6I.js → upgrade-P2NH3BOR.js} +2 -2
  129. package/dist-cli/chunks/upload-Z2WX5DCD.js +4 -0
  130. package/dist-cli/chunks/{version-WZM6XCD2.js → version-OUHCSCUS.js} +2 -2
  131. package/dist-cli/chunks/{web-ZYPEUVWL.js → web-6NRTF3UV.js} +2 -2
  132. package/dist-cli/chunks/welcome-6RS7PEJW.js +5 -0
  133. package/dist-cli/chunks/{what-happened-KF2HLWKW.js → what-happened-FUEL3BEF.js} +2 -2
  134. package/dist-lib/agent-daemon-client.cjs +1 -1
  135. package/dist-lib/agent-events.cjs +1 -1
  136. package/dist-lib/agent-identity.cjs +1 -1
  137. package/dist-lib/agent-sessions.cjs +1 -1
  138. package/dist-lib/attached-projects.cjs +1 -1
  139. package/dist-lib/auth/shared-session.cjs +1 -1
  140. package/dist-lib/backend-origin.cjs +1 -1
  141. package/dist-lib/beta.cjs +1 -1
  142. package/dist-lib/beta.mjs +1 -1
  143. package/dist-lib/bridge-constants.cjs +1 -1
  144. package/dist-lib/bridge-contract.cjs +1 -1
  145. package/dist-lib/cli-constants.cjs +1 -1
  146. package/dist-lib/config.cjs +1 -1
  147. package/dist-lib/detox/index.cjs +1 -1
  148. package/dist-lib/dev-bundle-resolution.cjs +18 -4
  149. package/dist-lib/home-paths.cjs +145 -11
  150. package/dist-lib/host/bridge-host.cjs +236 -31
  151. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  152. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  153. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  154. package/dist-lib/host/replacement-module-handler.cjs +71 -4
  155. package/dist-lib/host/websocket-proxy.cjs +1 -1
  156. package/dist-lib/index.cjs +337 -37
  157. package/dist-lib/jump-to-source-babel.cjs +1 -1
  158. package/dist-lib/menu.cjs +1 -1
  159. package/dist-lib/menu.mjs +1 -1
  160. package/dist-lib/metro-production-bundle.cjs +1 -1
  161. package/dist-lib/metro-production-bundle.mjs +1 -1
  162. package/dist-lib/metro.cjs +484 -14
  163. package/dist-lib/profiles.cjs +1 -1
  164. package/dist-lib/public-brand.cjs +1 -1
  165. package/dist-lib/react-native-host-modules.cjs +1 -1
  166. package/dist-lib/react-native-host-modules.mjs +1 -1
  167. package/dist-lib/render-mode.cjs +1 -1
  168. package/dist-lib/scripts/dev-server-scanner.cjs +25 -2
  169. package/dist-lib/sdk.cjs +1 -1
  170. package/dist-lib/sdk.mjs +1 -1
  171. package/dist-lib/skills.cjs +384 -385
  172. package/dist-lib/vite.cjs +193 -37
  173. package/package.json +2 -1
  174. package/scripts/dev-server-scanner.ts +12 -5
  175. package/skills/rnx-test/SKILL.md +4 -0
  176. package/src/dev-bundle-resolution.ts +13 -6
  177. package/src/dev-server-open.ts +197 -0
  178. package/src/home-paths.ts +168 -15
  179. package/src/host/bridge-host.ts +2 -2
  180. package/src/host/replacement-module-handler.ts +68 -2
  181. package/src/metro-plugin.ts +83 -5
  182. package/src/native-dev-bundle-url.ts +22 -0
  183. package/src/vite-plugin-one.ts +42 -28
  184. package/dist-cli/chunks/chunk-44LRQEU3.js +0 -4
  185. package/dist-cli/chunks/chunk-4RKTGL63.js +0 -9
  186. package/dist-cli/chunks/chunk-5SCIDXJD.js +0 -25
  187. package/dist-cli/chunks/chunk-CUVY4A2A.js +0 -4
  188. package/dist-cli/chunks/chunk-DPJGB7MP.js +0 -101
  189. package/dist-cli/chunks/chunk-HD6P3W5R.js +0 -4
  190. package/dist-cli/chunks/chunk-HFNESSHG.js +0 -15
  191. package/dist-cli/chunks/chunk-I7HHBFSD.js +0 -4
  192. package/dist-cli/chunks/chunk-LV2G34IX.js +0 -4
  193. package/dist-cli/chunks/cli-version-REQOM22G.js +0 -4
  194. package/dist-cli/chunks/config-QTFXJKD6.js +0 -13
  195. package/dist-cli/chunks/daemon-ETA375WE.js +0 -4
  196. package/dist-cli/chunks/drivers-R4YB2XAF.js +0 -4
  197. package/dist-cli/chunks/flow-3VQL6XZT.js +0 -4
  198. package/dist-cli/chunks/help-2RYCKZ2G.js +0 -4
  199. package/dist-cli/chunks/inspect-XDSX5D6D.js +0 -4
  200. package/dist-cli/chunks/install-desktop-OKVRLL35.js +0 -4
  201. package/dist-cli/chunks/login-4VCBPAY2.js +0 -28
  202. package/dist-cli/chunks/logout-WSL5JS5R.js +0 -4
  203. package/dist-cli/chunks/reset-3C52YQCV.js +0 -4
  204. package/dist-cli/chunks/runtime-4V2RBUOR.js +0 -4
  205. package/dist-cli/chunks/serve-ERAZQGCJ.js +0 -118
  206. package/dist-cli/chunks/setup-UG6PLYIL.js +0 -32
  207. package/dist-cli/chunks/store-VPBQKKWA.js +0 -4
  208. package/dist-cli/chunks/telemetry-FRW7OCYZ.js +0 -4
  209. package/dist-cli/chunks/upload-C6676NST.js +0 -4
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.343 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.345 | (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;
@@ -9524,6 +9524,20 @@ var init_config = __esm({
9524
9524
  }
9525
9525
  });
9526
9526
 
9527
+ // src/backend-origin.ts
9528
+ function normalizeHostname(hostname) {
9529
+ return hostname.replace(/^\[|\]$/g, "").toLowerCase();
9530
+ }
9531
+ function isLoopbackHost(hostname) {
9532
+ const normalized = normalizeHostname(hostname);
9533
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
9534
+ }
9535
+ var init_backend_origin = __esm({
9536
+ "src/backend-origin.ts"() {
9537
+ "use strict";
9538
+ }
9539
+ });
9540
+
9527
9541
  // src/native-dev-bundle-url.ts
9528
9542
  function isAbsoluteHttpUrl(url) {
9529
9543
  return /^https?:\/\//i.test(url);
@@ -9531,6 +9545,19 @@ function isAbsoluteHttpUrl(url) {
9531
9545
  function isNativeDevBundlePath(pathname) {
9532
9546
  return pathname.endsWith(".bundle");
9533
9547
  }
9548
+ function alignLoopbackBundleUrlWithProbe(bundleUrl, probeBaseUrl) {
9549
+ try {
9550
+ const bundle = new URL(bundleUrl);
9551
+ const probe3 = new URL(probeBaseUrl);
9552
+ if (!isLoopbackHost(bundle.hostname) || !isLoopbackHost(probe3.hostname)) {
9553
+ return bundleUrl;
9554
+ }
9555
+ bundle.hostname = probe3.hostname;
9556
+ return bundle.toString();
9557
+ } catch {
9558
+ return bundleUrl;
9559
+ }
9560
+ }
9534
9561
  function applyPreviewProdBundle(parsed) {
9535
9562
  if (!process.env.SOOTSIM_PREVIEW_PROD_BUNDLE) return;
9536
9563
  parsed.searchParams.set("dev", "false");
@@ -9570,6 +9597,7 @@ var METRO_BUNDLE_PATH;
9570
9597
  var init_native_dev_bundle_url = __esm({
9571
9598
  "src/native-dev-bundle-url.ts"() {
9572
9599
  "use strict";
9600
+ init_backend_origin();
9573
9601
  METRO_BUNDLE_PATH = "/index.bundle?platform=ios&dev=true&hot=true&minify=false";
9574
9602
  }
9575
9603
  });
@@ -9678,9 +9706,9 @@ function safeChildPlistName(value) {
9678
9706
  name: file.toLowerCase().endsWith(".plist") ? file : `${file}.plist`
9679
9707
  };
9680
9708
  }
9681
- async function readableSettingsBundle(path19) {
9709
+ async function readableSettingsBundle(path18) {
9682
9710
  try {
9683
- const root = await (0, import_promises.stat)((0, import_node_path.join)(path19, "Root.plist"));
9711
+ const root = await (0, import_promises.stat)((0, import_node_path.join)(path18, "Root.plist"));
9684
9712
  return root.isFile() && root.size <= MAX_PLIST_BYTES;
9685
9713
  } catch {
9686
9714
  return false;
@@ -9725,11 +9753,11 @@ async function settingsBundleFingerprint(bundlePath) {
9725
9753
  return null;
9726
9754
  }
9727
9755
  }
9728
- async function parsePlistRecord(path19) {
9756
+ async function parsePlistRecord(path18) {
9729
9757
  try {
9730
- const metadata = await (0, import_promises.stat)(path19);
9758
+ const metadata = await (0, import_promises.stat)(path18);
9731
9759
  if (!metadata.isFile() || metadata.size > MAX_PLIST_BYTES) return null;
9732
- const source = await (0, import_promises.readFile)(path19, "utf8");
9760
+ const source = await (0, import_promises.readFile)(path18, "utf8");
9733
9761
  const parsed = parsePlistSource(source);
9734
9762
  return isRecord2(parsed) ? parsed : null;
9735
9763
  } catch {
@@ -10008,10 +10036,10 @@ function tcpPing(port, timeout = TCP_GATE_MS) {
10008
10036
  sock.connect(port, "localhost");
10009
10037
  });
10010
10038
  }
10011
- function httpGet(port, path19, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
10039
+ function httpGet(port, path18, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
10012
10040
  return new Promise((resolve7) => {
10013
10041
  const req = import_http.default.request(
10014
- { hostname: "localhost", port, path: path19, method, timeout, headers },
10042
+ { hostname: "localhost", port, path: path18, method, timeout, headers },
10015
10043
  (res) => {
10016
10044
  let body = "";
10017
10045
  res.on("data", (c) => body += c.toString());
@@ -10203,7 +10231,8 @@ function applyManifest(result, manifestRes, buildIconProxyUrl) {
10203
10231
  if (client.name) result.projectName = client.name;
10204
10232
  if (client.ios?.bundleIdentifier) result.bundleId = client.ios.bundleIdentifier;
10205
10233
  if (result.framework === "metro" && client.sdkVersion) result.framework = "expo";
10206
- const launchUrl2 = manifest?.launchAsset?.url;
10234
+ const rawLaunchUrl = manifest?.launchAsset?.url;
10235
+ const launchUrl2 = typeof rawLaunchUrl === "string" ? alignLoopbackBundleUrlWithProbe(rawLaunchUrl, `http://localhost:${result.port}`) : rawLaunchUrl;
10207
10236
  if (launchUrl2 && !result.patched && !isDirectOneBundleUrl(result.bundleUrl)) {
10208
10237
  result.bundleUrl = withRuntimeConfig(result.port, launchUrl2, result.cwd);
10209
10238
  }
@@ -10724,8 +10753,8 @@ function readPrivacyPreferences() {
10724
10753
  const productAnalytics = settings?.productAnalytics;
10725
10754
  const crashReports = settings?.crashReports;
10726
10755
  return {
10727
- productAnalytics: typeof productAnalytics === "boolean" ? productAnalytics : true,
10728
- crashReports: typeof crashReports === "boolean" ? crashReports : true,
10756
+ productAnalytics: typeof productAnalytics === "boolean" ? productAnalytics : false,
10757
+ crashReports: typeof crashReports === "boolean" ? crashReports : false,
10729
10758
  configured: typeof productAnalytics === "boolean" && typeof crashReports === "boolean"
10730
10759
  };
10731
10760
  }
@@ -10894,10 +10923,30 @@ function readLiveRuntimeVersions() {
10894
10923
  }
10895
10924
  function writeDaemonLockfile(data) {
10896
10925
  ensureSootsimHome();
10897
- const tmp = `${daemonLockfilePath()}.tmp`;
10926
+ const lockfilePath = daemonLockfilePath();
10927
+ const existing = readDaemonLockfile();
10928
+ if (!existing) {
10929
+ if (createDaemonLockfileExclusive(data)) return;
10930
+ throw new Error("another rnx daemon claimed the lockfile before this write");
10931
+ }
10932
+ if (!sameDaemonOwner(existing, data)) {
10933
+ throw new Error("refusing to overwrite another rnx daemon lockfile");
10934
+ }
10935
+ const tmp = `${lockfilePath}.${data.pid}.${(0, import_node_crypto.randomUUID)()}.tmp`;
10898
10936
  import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
10899
10937
  `, "utf8");
10900
- import_node_fs3.default.renameSync(tmp, daemonLockfilePath());
10938
+ try {
10939
+ const current = readDaemonLockfile();
10940
+ if (!current || !sameDaemonOwner(current, data)) {
10941
+ throw new Error("rnx daemon lock ownership changed during heartbeat");
10942
+ }
10943
+ import_node_fs3.default.renameSync(tmp, lockfilePath);
10944
+ } finally {
10945
+ try {
10946
+ import_node_fs3.default.unlinkSync(tmp);
10947
+ } catch {
10948
+ }
10949
+ }
10901
10950
  }
10902
10951
  function writeDevBridgeLockfile(data) {
10903
10952
  import_node_fs3.default.mkdirSync(devBridgesDir(), { recursive: true });
@@ -10907,17 +10956,130 @@ function writeDevBridgeLockfile(data) {
10907
10956
  `, "utf8");
10908
10957
  import_node_fs3.default.renameSync(tmp, lockfilePath);
10909
10958
  }
10959
+ function sameDaemonOwner(left, right) {
10960
+ return left.pid === right.pid && left.startedAt === right.startedAt;
10961
+ }
10962
+ function isAlreadyExists(error) {
10963
+ return error instanceof Error && "code" in error && Reflect.get(error, "code") === "EEXIST";
10964
+ }
10965
+ function createDaemonLockfileExclusive(data) {
10966
+ const lockfilePath = daemonLockfilePath();
10967
+ const tmp = `${lockfilePath}.${data.pid}.${(0, import_node_crypto.randomUUID)()}.claim`;
10968
+ import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
10969
+ `, "utf8");
10970
+ try {
10971
+ import_node_fs3.default.linkSync(tmp, lockfilePath);
10972
+ return true;
10973
+ } catch (error) {
10974
+ if (isAlreadyExists(error)) return false;
10975
+ throw error;
10976
+ } finally {
10977
+ try {
10978
+ import_node_fs3.default.unlinkSync(tmp);
10979
+ } catch {
10980
+ }
10981
+ }
10982
+ }
10983
+ function daemonClaimPath() {
10984
+ return `${daemonLockfilePath()}.claim`;
10985
+ }
10986
+ function parseDaemonClaim(raw) {
10987
+ try {
10988
+ const value = JSON.parse(raw);
10989
+ if (value !== null && typeof value === "object" && typeof Reflect.get(value, "pid") === "number" && typeof Reflect.get(value, "startedAt") === "number" && typeof Reflect.get(value, "createdAt") === "number") {
10990
+ return {
10991
+ pid: Reflect.get(value, "pid"),
10992
+ startedAt: Reflect.get(value, "startedAt"),
10993
+ createdAt: Reflect.get(value, "createdAt")
10994
+ };
10995
+ }
10996
+ } catch {
10997
+ }
10998
+ return null;
10999
+ }
11000
+ function readDaemonClaim() {
11001
+ try {
11002
+ return parseDaemonClaim(import_node_fs3.default.readFileSync(daemonClaimPath(), "utf8"));
11003
+ } catch {
11004
+ return null;
11005
+ }
11006
+ }
11007
+ function isDaemonClaimFresh(claim, now = Date.now()) {
11008
+ if (now - claim.createdAt > DAEMON_CLAIM_STALE_MS) return false;
11009
+ try {
11010
+ process.kill(claim.pid, 0);
11011
+ return true;
11012
+ } catch {
11013
+ return false;
11014
+ }
11015
+ }
11016
+ function acquireDaemonClaim(data) {
11017
+ const claim = {
11018
+ pid: data.pid,
11019
+ startedAt: data.startedAt,
11020
+ createdAt: Date.now()
11021
+ };
11022
+ const serialized = `${JSON.stringify(claim)}
11023
+ `;
11024
+ for (let attempt = 0; attempt < 8; attempt++) {
11025
+ try {
11026
+ import_node_fs3.default.writeFileSync(daemonClaimPath(), serialized, { encoding: "utf8", flag: "wx" });
11027
+ return true;
11028
+ } catch (error) {
11029
+ if (!isAlreadyExists(error)) throw error;
11030
+ }
11031
+ const existing = readDaemonClaim();
11032
+ if (existing && sameDaemonOwner(existing, claim)) return true;
11033
+ if (existing && isDaemonClaimFresh(existing)) return false;
11034
+ let before;
11035
+ try {
11036
+ before = import_node_fs3.default.readFileSync(daemonClaimPath(), "utf8");
11037
+ const latest = parseDaemonClaim(before);
11038
+ if (latest && isDaemonClaimFresh(latest)) return false;
11039
+ if (import_node_fs3.default.readFileSync(daemonClaimPath(), "utf8") !== before) continue;
11040
+ import_node_fs3.default.unlinkSync(daemonClaimPath());
11041
+ } catch {
11042
+ }
11043
+ }
11044
+ return false;
11045
+ }
11046
+ function releaseDaemonClaim(owner) {
11047
+ try {
11048
+ const claim = readDaemonClaim();
11049
+ if (!claim || !sameDaemonOwner(claim, owner)) return;
11050
+ import_node_fs3.default.unlinkSync(daemonClaimPath());
11051
+ } catch {
11052
+ }
11053
+ }
10910
11054
  function claimDaemonLockfile(data) {
10911
11055
  ensureSootsimHome();
10912
- const existing = readDaemonLockfile();
10913
- if (existing && isDaemonLockfileFresh(existing) && existing.pid !== data.pid) {
10914
- return false;
11056
+ if (!acquireDaemonClaim(data)) return false;
11057
+ try {
11058
+ const existing = readDaemonLockfile();
11059
+ if (existing && sameDaemonOwner(existing, data)) {
11060
+ writeDaemonLockfile(data);
11061
+ return true;
11062
+ }
11063
+ if (existing && isDaemonLockfileFresh(existing)) return false;
11064
+ if (existing) {
11065
+ const latest = readDaemonLockfile();
11066
+ if (!latest || !sameDaemonOwner(latest, existing)) return false;
11067
+ if (isDaemonLockfileFresh(latest)) return false;
11068
+ try {
11069
+ import_node_fs3.default.unlinkSync(daemonLockfilePath());
11070
+ } catch {
11071
+ return false;
11072
+ }
11073
+ }
11074
+ return createDaemonLockfileExclusive(data);
11075
+ } finally {
11076
+ releaseDaemonClaim(data);
10915
11077
  }
10916
- writeDaemonLockfile(data);
10917
- return true;
10918
11078
  }
10919
- function removeDaemonLockfile() {
11079
+ function removeDaemonLockfile(owner) {
10920
11080
  try {
11081
+ const lockfile = readDaemonLockfile();
11082
+ if (!lockfile || !sameDaemonOwner(lockfile, owner)) return;
10921
11083
  import_node_fs3.default.unlinkSync(daemonLockfilePath());
10922
11084
  } catch {
10923
11085
  }
@@ -10931,7 +11093,7 @@ function removeDevBridgeLockfile(owner) {
10931
11093
  } catch {
10932
11094
  }
10933
11095
  }
10934
- var import_node_crypto, import_node_fs3, import_node_os, import_node_path4, SOOTSIM_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, DEV_BRIDGES_DIR, CONFIG_FILE, DAEMON_HEARTBEAT_STALE_MS, DAEMON_LOCKFILE_MAX_BYTES;
11096
+ var import_node_crypto, import_node_fs3, import_node_os, import_node_path4, SOOTSIM_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, DEV_BRIDGES_DIR, CONFIG_FILE, DAEMON_HEARTBEAT_STALE_MS, DAEMON_LOCKFILE_MAX_BYTES, DAEMON_CLAIM_STALE_MS;
10935
11097
  var init_home_paths = __esm({
10936
11098
  "src/home-paths.ts"() {
10937
11099
  "use strict";
@@ -10946,6 +11108,7 @@ var init_home_paths = __esm({
10946
11108
  CONFIG_FILE = "config.json";
10947
11109
  DAEMON_HEARTBEAT_STALE_MS = 3e4;
10948
11110
  DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
11111
+ DAEMON_CLAIM_STALE_MS = 3e4;
10949
11112
  }
10950
11113
  });
10951
11114
 
@@ -12870,20 +13033,6 @@ var init_agent_host = __esm({
12870
13033
  }
12871
13034
  });
12872
13035
 
12873
- // src/backend-origin.ts
12874
- function normalizeHostname(hostname) {
12875
- return hostname.replace(/^\[|\]$/g, "").toLowerCase();
12876
- }
12877
- function isLoopbackHost(hostname) {
12878
- const normalized = normalizeHostname(hostname);
12879
- return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
12880
- }
12881
- var init_backend_origin = __esm({
12882
- "src/backend-origin.ts"() {
12883
- "use strict";
12884
- }
12885
- });
12886
-
12887
13036
  // src/host/fetch-proxy-overrides.ts
12888
13037
  function headerOverridesFor(hostname) {
12889
13038
  const host = hostname.toLowerCase();
@@ -13367,9 +13516,18 @@ export const Appearance = rn.Appearance
13367
13516
  `;
13368
13517
  }
13369
13518
  function sendJavaScript(req, res, source) {
13519
+ const origin = req.headers.origin;
13520
+ let allowedOrigin;
13521
+ if (origin) {
13522
+ try {
13523
+ if (isLoopbackHost(new URL(origin).hostname)) allowedOrigin = origin;
13524
+ } catch {
13525
+ }
13526
+ }
13370
13527
  res.writeHead(200, {
13371
13528
  "Content-Type": "application/javascript; charset=utf-8",
13372
- "Cache-Control": "no-cache"
13529
+ "Cache-Control": "no-cache",
13530
+ ...allowedOrigin ? { "Access-Control-Allow-Origin": allowedOrigin, Vary: "Origin" } : {}
13373
13531
  });
13374
13532
  res.end(req.method === "HEAD" ? void 0 : source);
13375
13533
  }
@@ -13390,6 +13548,23 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
13390
13548
  } catch {
13391
13549
  return false;
13392
13550
  }
13551
+ if (options.requireLoopbackOrigin) {
13552
+ const origin = req.headers.origin;
13553
+ const remoteAddress = req.socket.remoteAddress?.replace(/^::ffff:/, "");
13554
+ let allowed = Boolean(remoteAddress && isLoopbackHost(remoteAddress));
13555
+ if (allowed && origin) {
13556
+ try {
13557
+ allowed = isLoopbackHost(new URL(origin).hostname);
13558
+ } catch {
13559
+ allowed = false;
13560
+ }
13561
+ }
13562
+ if (!allowed) {
13563
+ res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
13564
+ res.end("replacement modules are available only to loopback origins");
13565
+ return true;
13566
+ }
13567
+ }
13393
13568
  if (url.pathname === `${REPLACEMENT_RUNTIME_PREFIX}/react`) {
13394
13569
  sendJavaScript(req, res, reactRuntimeSource(options.reactBridgeUrl));
13395
13570
  return true;
@@ -13399,6 +13574,11 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
13399
13574
  return true;
13400
13575
  }
13401
13576
  if (url.pathname !== REPLACEMENT_MODULE_ROUTE) return false;
13577
+ if (options.requestToken && url.searchParams.get("token") !== options.requestToken) {
13578
+ res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
13579
+ res.end("replacement module request token is invalid");
13580
+ return true;
13581
+ }
13402
13582
  const targetPath = url.searchParams.get("path");
13403
13583
  if (!targetPath) {
13404
13584
  res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
@@ -13411,17 +13591,42 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
13411
13591
  res.end("replacement files must be .js, .jsx, .ts, or .tsx");
13412
13592
  return true;
13413
13593
  }
13594
+ if (!import_node_path10.default.isAbsolute(targetPath)) {
13595
+ res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
13596
+ res.end("replacement file path must be absolute");
13597
+ return true;
13598
+ }
13599
+ let resolvedProjectRoot;
13600
+ if (options.projectRoot) {
13601
+ const lexicalProjectRoot = import_node_path10.default.resolve(options.projectRoot);
13602
+ resolvedProjectRoot = await import_promises5.default.realpath(options.projectRoot);
13603
+ const lexicalTarget = import_node_path10.default.relative(lexicalProjectRoot, targetPath);
13604
+ if (lexicalTarget.startsWith("..") || import_node_path10.default.isAbsolute(lexicalTarget)) {
13605
+ res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
13606
+ res.end("replacement file must be inside the project root");
13607
+ return true;
13608
+ }
13609
+ }
13610
+ let resolvedTargetPath;
13414
13611
  try {
13415
- await import_promises5.default.access(targetPath);
13612
+ resolvedTargetPath = await import_promises5.default.realpath(targetPath);
13416
13613
  } catch (error) {
13417
13614
  const message = error instanceof Error ? error.message : String(error);
13418
13615
  res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
13419
13616
  res.end(`replacement file not found: ${message}`);
13420
13617
  return true;
13421
13618
  }
13619
+ if (resolvedProjectRoot) {
13620
+ const relativeTarget = import_node_path10.default.relative(resolvedProjectRoot, resolvedTargetPath);
13621
+ if (relativeTarget.startsWith("..") || import_node_path10.default.isAbsolute(relativeTarget)) {
13622
+ res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
13623
+ res.end("replacement file must be inside the project root");
13624
+ return true;
13625
+ }
13626
+ }
13422
13627
  try {
13423
13628
  const result = await (0, import_esbuild.build)({
13424
- entryPoints: [targetPath],
13629
+ entryPoints: [resolvedTargetPath],
13425
13630
  bundle: true,
13426
13631
  write: false,
13427
13632
  format: "esm",
@@ -13460,6 +13665,7 @@ var init_replacement_module_handler = __esm({
13460
13665
  import_promises5 = __toESM(require("node:fs/promises"), 1);
13461
13666
  import_node_path10 = __toESM(require("node:path"), 1);
13462
13667
  import_esbuild = require("esbuild");
13668
+ init_backend_origin();
13463
13669
  REPLACEMENT_MODULE_ROUTE = "/__sootsim-replacement-module";
13464
13670
  REPLACEMENT_RUNTIME_PREFIX = "/__sootsim-replacement-runtime";
13465
13671
  }
@@ -14726,7 +14932,7 @@ var init_bridge_host = __esm({
14726
14932
  removeLockfile() {
14727
14933
  if (!this.shouldWriteLockfile) return;
14728
14934
  try {
14729
- removeDaemonLockfile();
14935
+ removeDaemonLockfile({ pid: process.pid, startedAt: this.startedAt });
14730
14936
  } catch {
14731
14937
  }
14732
14938
  }
@@ -15285,7 +15491,7 @@ var init_bridge_host = __esm({
15285
15491
  }
15286
15492
  if (this.shouldWriteLockfile) {
15287
15493
  try {
15288
- removeDaemonLockfile();
15494
+ removeDaemonLockfile({ pid: process.pid, startedAt: this.startedAt });
15289
15495
  } catch {
15290
15496
  }
15291
15497
  }
@@ -16487,11 +16693,11 @@ async function evalInBridge(bridge, code, opts = {}) {
16487
16693
  if (opts.acquireLock) payload.acquireLock = true;
16488
16694
  return bridge.send(payload);
16489
16695
  }
16490
- async function callInBridge(bridge, path19, ...args) {
16491
- return bridge.send({ type: "call", path: path19, args });
16696
+ async function callInBridge(bridge, path18, ...args) {
16697
+ return bridge.send({ type: "call", path: path18, args });
16492
16698
  }
16493
- async function callInBridgeWrite(bridge, path19, ...args) {
16494
- return bridge.send({ type: "call", path: path19, args, acquireLock: true });
16699
+ async function callInBridgeWrite(bridge, path18, ...args) {
16700
+ return bridge.send({ type: "call", path: path18, args, acquireLock: true });
16495
16701
  }
16496
16702
  var import_node_fs9, import_node_path11, import_ws3, resolvedWorld, BridgeSimLockedError;
16497
16703
  var init_ws_bridge = __esm({
@@ -16553,8 +16759,8 @@ function getDefaultPortForProtocol(protocol) {
16553
16759
  return protocol === "https:" ? 443 : 80;
16554
16760
  }
16555
16761
  function isBaseServerUrl(url) {
16556
- const path19 = url.pathname || "/";
16557
- return (path19 === "/" || path19 === "") && !url.search && !url.hash;
16762
+ const path18 = url.pathname || "/";
16763
+ return (path18 === "/" || path18 === "") && !url.search && !url.hash;
16558
16764
  }
16559
16765
  async function resolvePortViaServerScan(port) {
16560
16766
  try {
@@ -16570,8 +16776,8 @@ async function resolvePortViaServerScan(port) {
16570
16776
  if (!match) return null;
16571
16777
  return {
16572
16778
  // normalize exactly like the manifest-probe branch below — the scan
16573
- // returns metro's raw launchAsset url (lazy=true for code-split one/vxrn
16574
- // apps), but sootsim loads ONE self-contained bundle into the tenant
16779
+ // returns metro's manifest launchAsset url (lazy=true for code-split
16780
+ // one/vxrn apps), but sootsim loads ONE self-contained bundle into the tenant
16575
16781
  // worker, so a deferred React.lazy chunk throws "Requiring unknown module
16576
16782
  // <id>". normalizeNativeDevBundleUrl forces lazy=false + strips bytecode.
16577
16783
  bundleUrl: normalizeNativeDevBundleUrl(match.bundleUrl),
@@ -16599,7 +16805,8 @@ async function probeBaseUrlBundle(baseUrl, port) {
16599
16805
  if (manifestRes.ok) {
16600
16806
  const manifest = await manifestRes.json();
16601
16807
  const client = manifest?.extra?.expoClient || manifest?.extra || {};
16602
- const launchUrl2 = typeof manifest?.launchAsset?.url === "string" ? manifest.launchAsset.url : void 0;
16808
+ const rawLaunchUrl = typeof manifest?.launchAsset?.url === "string" ? manifest.launchAsset.url : void 0;
16809
+ const launchUrl2 = rawLaunchUrl ? alignLoopbackBundleUrlWithProbe(rawLaunchUrl, normalizedBaseUrl) : void 0;
16603
16810
  if (launchUrl2 || client.name) {
16604
16811
  return {
16605
16812
  bundleUrl: normalizeNativeDevBundleUrl(
@@ -16788,150 +16995,20 @@ function findExpoAppConfig(dir) {
16788
16995
  function findSootSimAppConfig(dir) {
16789
16996
  return firstNamedFile(dir, SOOTSIM_APP_CONFIG_NAMES);
16790
16997
  }
16791
- function readWorkspacePatterns(rootDir) {
16998
+ function resolveAppProject(projectRoot) {
16999
+ const appDir = import_node_path12.default.resolve(projectRoot);
16792
17000
  try {
16793
- const value = JSON.parse(import_node_fs10.default.readFileSync(import_node_path12.default.join(rootDir, "package.json"), "utf8"));
16794
- if (!value || typeof value !== "object") return [];
16795
- const workspaces = Reflect.get(value, "workspaces");
16796
- if (Array.isArray(workspaces)) {
16797
- return workspaces.filter((entry) => typeof entry === "string");
16798
- }
16799
- if (!workspaces || typeof workspaces !== "object") return [];
16800
- const packages = Reflect.get(workspaces, "packages");
16801
- return Array.isArray(packages) ? packages.filter((entry) => typeof entry === "string") : [];
17001
+ if (!import_node_fs10.default.statSync(appDir).isDirectory()) return null;
16802
17002
  } catch {
16803
- return [];
16804
- }
16805
- }
16806
- function workspaceDirectories(rootDir) {
16807
- const directories = /* @__PURE__ */ new Set();
16808
- let expanded = 0;
16809
- let complete = true;
16810
- for (const pattern of readWorkspacePatterns(rootDir)) {
16811
- if (expanded >= MAX_SCANNED_DIRECTORIES) {
16812
- complete = false;
16813
- break;
16814
- }
16815
- if (!pattern || import_node_path12.default.isAbsolute(pattern)) continue;
16816
- const segments = pattern.split(/[\\/]+/).filter(Boolean);
16817
- if (segments.length === 0 || segments.length > MAX_SCAN_DEPTH) continue;
16818
- if (segments.some((segment) => segment === ".." || segment === "**")) continue;
16819
- let current = [rootDir];
16820
- for (const segment of segments) {
16821
- const next = [];
16822
- for (const parent of current) {
16823
- if (expanded >= MAX_SCANNED_DIRECTORIES) {
16824
- complete = false;
16825
- break;
16826
- }
16827
- expanded++;
16828
- if (segment === "*") {
16829
- let entries;
16830
- try {
16831
- entries = import_node_fs10.default.readdirSync(parent, { withFileTypes: true });
16832
- } catch {
16833
- continue;
16834
- }
16835
- for (const entry of entries) {
16836
- if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
16837
- if (entry.name.startsWith(".") || SKIP_DIR_NAMES.has(entry.name)) continue;
16838
- next.push(import_node_path12.default.join(parent, entry.name));
16839
- }
16840
- } else if (!segment.includes("*")) {
16841
- const candidate = import_node_path12.default.join(parent, segment);
16842
- try {
16843
- const stat2 = import_node_fs10.default.lstatSync(candidate);
16844
- if (stat2.isDirectory() && !stat2.isSymbolicLink()) next.push(candidate);
16845
- } catch {
16846
- }
16847
- }
16848
- }
16849
- current = next;
16850
- if (current.length > MAX_SCANNED_DIRECTORIES) {
16851
- current.length = MAX_SCANNED_DIRECTORIES;
16852
- complete = false;
16853
- }
16854
- }
16855
- for (const directory of current) directories.add(directory);
16856
- }
16857
- return { directories: [...directories], complete };
16858
- }
16859
- function boundedConfigDirectories(rootDir) {
16860
- const queue = [{ dir: rootDir, depth: 0 }];
16861
- const matches = [];
16862
- let visited = 0;
16863
- let nextIndex = 0;
16864
- let complete = true;
16865
- while (nextIndex < queue.length && visited < MAX_SCANNED_DIRECTORIES) {
16866
- const current = queue[nextIndex++];
16867
- if (!current) break;
16868
- visited++;
16869
- if (current.depth > 0 && findExpoAppConfig(current.dir)) {
16870
- matches.push(current.dir);
16871
- if (matches.length > 1) return { directories: matches, complete: true };
16872
- }
16873
- if (current.depth >= MAX_SCAN_DEPTH) continue;
16874
- let entries;
16875
- try {
16876
- entries = import_node_fs10.default.readdirSync(current.dir, { withFileTypes: true });
16877
- } catch {
16878
- continue;
16879
- }
16880
- for (const entry of entries) {
16881
- if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
16882
- if (entry.name.startsWith(".") || SKIP_DIR_NAMES.has(entry.name)) continue;
16883
- if (queue.length >= MAX_SCANNED_DIRECTORIES) {
16884
- complete = false;
16885
- break;
16886
- }
16887
- queue.push({ dir: import_node_path12.default.join(current.dir, entry.name), depth: current.depth + 1 });
16888
- }
16889
- }
16890
- return { directories: matches, complete: complete && nextIndex === queue.length };
16891
- }
16892
- function resolveAppProject(startDir, opts = {}) {
16893
- const rootDir = import_node_path12.default.resolve(startDir);
16894
- const rootConfigPath = findSootSimAppConfig(rootDir);
16895
- if (findExpoAppConfig(rootDir) || opts.trustStart && import_node_fs10.default.existsSync(rootDir)) {
16896
- let configPath = rootConfigPath;
16897
- if (!configPath && opts.trustStart) {
16898
- let parent = import_node_path12.default.dirname(rootDir);
16899
- for (let depth = 0; depth < MAX_SCAN_DEPTH && parent !== import_node_path12.default.dirname(parent); depth++) {
16900
- configPath = findSootSimAppConfig(parent);
16901
- if (configPath) break;
16902
- parent = import_node_path12.default.dirname(parent);
16903
- }
16904
- }
16905
- return { appDir: rootDir, configPath };
16906
- }
16907
- const workspaceScan = workspaceDirectories(rootDir);
16908
- const workspaceApps = workspaceScan.directories.filter(
16909
- (directory) => findExpoAppConfig(directory)
16910
- );
16911
- if (workspaceApps.length > 1) return null;
16912
- if (!workspaceScan.complete) return null;
16913
- if (workspaceApps.length === 1) {
16914
- const workspaceAppDir = workspaceApps[0];
16915
- return {
16916
- appDir: workspaceAppDir,
16917
- configPath: rootConfigPath ?? findSootSimAppConfig(workspaceAppDir)
16918
- };
17003
+ return null;
16919
17004
  }
16920
- const fallbackScan = boundedConfigDirectories(rootDir);
16921
- const scannedApps = fallbackScan.directories;
16922
- if (scannedApps.length > 1) return null;
16923
- if (!fallbackScan.complete) return null;
16924
- if (scannedApps.length === 1) {
16925
- const scannedAppDir = scannedApps[0];
16926
- return {
16927
- appDir: scannedAppDir,
16928
- configPath: rootConfigPath ?? findSootSimAppConfig(scannedAppDir)
16929
- };
17005
+ const configPath = findSootSimAppConfig(appDir);
17006
+ if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs10.default.existsSync(import_node_path12.default.join(appDir, "package.json"))) {
17007
+ return null;
16930
17008
  }
16931
- if (rootConfigPath) return { appDir: rootDir, configPath: rootConfigPath };
16932
- return null;
17009
+ return { appDir, configPath };
16933
17010
  }
16934
- var import_node_fs10, import_node_path12, EXPO_APP_CONFIG_NAMES, SOOTSIM_APP_CONFIG_NAMES, SKIP_DIR_NAMES, MAX_SCANNED_DIRECTORIES, MAX_SCAN_DEPTH;
17011
+ var import_node_fs10, import_node_path12, EXPO_APP_CONFIG_NAMES, SOOTSIM_APP_CONFIG_NAMES;
16935
17012
  var init_app_project = __esm({
16936
17013
  "cli/app-project.ts"() {
16937
17014
  "use strict";
@@ -16950,28 +17027,6 @@ var init_app_project = __esm({
16950
17027
  "sootsim.config.mjs",
16951
17028
  "sootsim.config.cjs"
16952
17029
  ];
16953
- SKIP_DIR_NAMES = /* @__PURE__ */ new Set([
16954
- "node_modules",
16955
- "artifacts",
16956
- "dist",
16957
- "build",
16958
- "out",
16959
- "coverage",
16960
- "tmp",
16961
- "public",
16962
- "ios",
16963
- "android",
16964
- "macos",
16965
- "windows",
16966
- "__fixtures__",
16967
- "__tests__",
16968
- "fixtures",
16969
- "fixture",
16970
- "examples",
16971
- "example"
16972
- ]);
16973
- MAX_SCANNED_DIRECTORIES = 256;
16974
- MAX_SCAN_DEPTH = 4;
16975
17030
  }
16976
17031
  });
16977
17032
 
@@ -17049,28 +17104,18 @@ function localOriginOf(value) {
17049
17104
  }
17050
17105
  return null;
17051
17106
  }
17052
- function hasPackageJson(dir) {
17053
- try {
17054
- return import_node_fs11.default.statSync(import_node_path14.default.join(dir, "package.json")).isFile();
17055
- } catch {
17056
- return false;
17057
- }
17058
- }
17059
17107
  function discoverNativeLinkedAppFonts(opts) {
17060
17108
  const metroOrigin = localOriginOf(opts.bundleUrl);
17061
17109
  if (!metroOrigin) return [];
17062
- const explicit = opts.repoDir || process.env.CONTRAST_REPO_DIR;
17063
- const startDir = explicit ? import_node_path14.default.resolve(explicit) : process.cwd();
17064
- if (!explicit && !hasPackageJson(startDir)) return [];
17065
- const project = resolveAppProject(startDir, { trustStart: opts.exactProjectDir });
17110
+ const project = resolveAppProject(opts.projectDir);
17066
17111
  const selectedAppDir = project?.appDir ?? null;
17067
17112
  if (!selectedAppDir) return [];
17068
17113
  const platform = (opts.platform || process.env.CONTRAST_PLATFORM || "ios").toLowerCase();
17069
17114
  let getConfig = null;
17070
- if (explicit || project) {
17115
+ if (project) {
17071
17116
  try {
17072
17117
  const modulePath = require3.resolve("@expo/config", {
17073
- paths: [selectedAppDir, startDir]
17118
+ paths: [selectedAppDir]
17074
17119
  });
17075
17120
  const mod = require3(modulePath);
17076
17121
  if (typeof mod.getConfig === "function") {
@@ -17289,27 +17334,17 @@ function localOriginOf2(value) {
17289
17334
  }
17290
17335
  return null;
17291
17336
  }
17292
- function hasPackageJson2(dir) {
17293
- try {
17294
- return import_node_fs12.default.statSync(import_node_path15.default.join(dir, "package.json")).isFile();
17295
- } catch {
17296
- return false;
17297
- }
17298
- }
17299
17337
  function discoverConfigPluginAppSplash(opts) {
17300
17338
  const metroOrigin = localOriginOf2(opts.bundleUrl);
17301
17339
  if (!metroOrigin) return null;
17302
- const explicit = opts.repoDir || process.env.CONTRAST_REPO_DIR;
17303
- const startDir = explicit ? import_node_path15.default.resolve(explicit) : process.cwd();
17304
- if (!explicit && !hasPackageJson2(startDir)) return null;
17305
- const project = resolveAppProject(startDir, { trustStart: opts.exactProjectDir });
17340
+ const project = resolveAppProject(opts.projectDir);
17306
17341
  const selectedAppDir = project?.appDir ?? null;
17307
17342
  if (!selectedAppDir) return null;
17308
17343
  const platform = (opts.platform || process.env.CONTRAST_PLATFORM || "ios").toLowerCase();
17309
17344
  let getConfig = null;
17310
17345
  try {
17311
17346
  const modulePath = require4.resolve("@expo/config", {
17312
- paths: [selectedAppDir, startDir]
17347
+ paths: [selectedAppDir]
17313
17348
  });
17314
17349
  const mod = require4(modulePath);
17315
17350
  if (typeof mod.getConfig === "function") {
@@ -17334,13 +17369,11 @@ function discoverConfigPluginAppSplash(opts) {
17334
17369
  function encodeAppSplashWire(spec) {
17335
17370
  return encodeURIComponent(JSON.stringify(spec));
17336
17371
  }
17337
- var import_node_fs12, import_node_module3, import_node_path15, import_config_plugin_splash, require4;
17372
+ var import_node_module3, import_config_plugin_splash, require4;
17338
17373
  var init_app_splash = __esm({
17339
17374
  "cli/app-splash.ts"() {
17340
17375
  "use strict";
17341
- import_node_fs12 = __toESM(require("node:fs"), 1);
17342
17376
  import_node_module3 = require("node:module");
17343
- import_node_path15 = __toESM(require("node:path"), 1);
17344
17377
  import_config_plugin_splash = require("sootsim-engine/engine/config-plugin-splash");
17345
17378
  init_app_project();
17346
17379
  require4 = (0, import_node_module3.createRequire)(__sootsim_import_meta_url);
@@ -18358,7 +18391,7 @@ function resolvePlaywright() {
18358
18391
  try {
18359
18392
  const modulePath = requireFromRoot.resolve(spec);
18360
18393
  const packageJsonPath = requireFromRoot.resolve(`${spec}/package.json`);
18361
- const parsed = JSON.parse((0, import_node_fs13.readFileSync)(packageJsonPath, "utf8"));
18394
+ const parsed = JSON.parse((0, import_node_fs12.readFileSync)(packageJsonPath, "utf8"));
18362
18395
  if (!isRecord3(parsed) || typeof parsed.version !== "string") continue;
18363
18396
  const bin = parsed.bin;
18364
18397
  let binPath = null;
@@ -18370,8 +18403,8 @@ function resolvePlaywright() {
18370
18403
  binPath = typeof named === "string" ? named : typeof first === "string" ? first : null;
18371
18404
  }
18372
18405
  if (!binPath) continue;
18373
- const cliPath = (0, import_node_path16.join)((0, import_node_path16.dirname)(packageJsonPath), binPath);
18374
- if (!(0, import_node_fs13.existsSync)(cliPath)) continue;
18406
+ const cliPath = (0, import_node_path15.join)((0, import_node_path15.dirname)(packageJsonPath), binPath);
18407
+ if (!(0, import_node_fs12.existsSync)(cliPath)) continue;
18375
18408
  return {
18376
18409
  spec,
18377
18410
  modulePath,
@@ -18410,7 +18443,7 @@ function probeBrowser(resolved, env) {
18410
18443
  return {
18411
18444
  ok: true,
18412
18445
  executablePath: parsed.executablePath,
18413
- exists: parsed.exists && (0, import_node_fs13.existsSync)(parsed.executablePath)
18446
+ exists: parsed.exists && (0, import_node_fs12.existsSync)(parsed.executablePath)
18414
18447
  };
18415
18448
  } catch (error) {
18416
18449
  return {
@@ -18449,14 +18482,14 @@ ${detail}` : ""}`
18449
18482
  }
18450
18483
  return { ok: true, executablePath: after.executablePath, installed: true };
18451
18484
  }
18452
- var import_node_child_process5, import_node_fs13, import_node_module4, import_node_path16, PLAYWRIGHT_BROWSER_PROBE;
18485
+ var import_node_child_process5, import_node_fs12, import_node_module4, import_node_path15, PLAYWRIGHT_BROWSER_PROBE;
18453
18486
  var init_playwright_provisioning = __esm({
18454
18487
  "cli/drivers/playwright-provisioning.ts"() {
18455
18488
  "use strict";
18456
18489
  import_node_child_process5 = require("node:child_process");
18457
- import_node_fs13 = require("node:fs");
18490
+ import_node_fs12 = require("node:fs");
18458
18491
  import_node_module4 = require("node:module");
18459
- import_node_path16 = require("node:path");
18492
+ import_node_path15 = require("node:path");
18460
18493
  PLAYWRIGHT_BROWSER_PROBE = `
18461
18494
  const { existsSync } = require('node:fs');
18462
18495
  const modulePath = process.env.SOOTSIM_PW_MODULE;
@@ -19142,11 +19175,11 @@ function isChangelogEntry(value) {
19142
19175
  )
19143
19176
  );
19144
19177
  }
19145
- async function fetchRuntimeReleaseNotes(version, request = fetch) {
19178
+ async function fetchRuntimeReleaseNotes(version, fetchImpl = fetch) {
19146
19179
  try {
19147
19180
  const url = new URL(SOOTSIM_CHANGELOG_API_URL);
19148
19181
  url.searchParams.set("version", version);
19149
- const response = await request(url, {
19182
+ const response = await fetchImpl(url, {
19150
19183
  headers: { accept: "application/json" },
19151
19184
  signal: AbortSignal.timeout(5e3)
19152
19185
  });
@@ -19551,9 +19584,12 @@ function shouldOpenAsDeepLink(target) {
19551
19584
  return !/^https?:\/\//i.test(trimmed);
19552
19585
  }
19553
19586
  async function ensureBridgeForOpen(preferredPort, explicitPort = false) {
19554
- if (await canConnectToBridge(preferredPort, 250)) return preferredPort;
19555
19587
  const lock = readDaemonLockfile();
19556
19588
  assertExplicitBridgeLock(preferredPort, explicitPort, lock);
19589
+ const preferredWorld = worldForBridgePort(preferredPort);
19590
+ if ((explicitPort || preferredWorld !== null) && await canConnectToBridge(preferredPort, 250)) {
19591
+ return preferredPort;
19592
+ }
19557
19593
  if (lock && isDaemonLockfileFresh(lock) && await canConnectToBridge(lock.bridgePort, 500)) {
19558
19594
  return lock.bridgePort;
19559
19595
  }
@@ -19579,12 +19615,6 @@ async function ensureBridgeForOpen(preferredPort, explicitPort = false) {
19579
19615
  );
19580
19616
  return nextLock.bridgePort;
19581
19617
  }
19582
- if (await canConnectToBridge(preferredPort, 250)) {
19583
- console.error(
19584
- ` started local bridge on port ${preferredPort}; run \`sootsim setup\` to install the background daemon`
19585
- );
19586
- return preferredPort;
19587
- }
19588
19618
  if (childExit) {
19589
19619
  throw new Error(`rnx bridge exited before becoming ready (exit ${childExit})`);
19590
19620
  }
@@ -19604,14 +19634,14 @@ function assertExplicitBridgeLock(preferredPort, explicitPort, lock) {
19604
19634
  }
19605
19635
  async function ensureRuntimeForOpen() {
19606
19636
  const active = readActiveRuntime();
19607
- if (active && import_node_fs14.default.existsSync(runtimeDir(active))) return;
19637
+ if (active && import_node_fs13.default.existsSync(runtimeDir(active))) return;
19608
19638
  console.error(" installing rnx engine runtime...");
19609
19639
  const { runRuntime: runRuntime2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
19610
19640
  await runRuntime2(["install"], {});
19611
19641
  }
19612
19642
  function spawnSootsim(args) {
19613
19643
  const entry = process.argv[1];
19614
- if (entry && import_node_fs14.default.existsSync(entry)) {
19644
+ if (entry && import_node_fs13.default.existsSync(entry)) {
19615
19645
  return (0, import_node_child_process6.spawn)(process.execPath, [entry, ...args], {
19616
19646
  detached: true,
19617
19647
  stdio: "ignore",
@@ -19758,10 +19788,10 @@ async function buildResolvedBundleOpenUrl(target, baseUrl, opts = {}) {
19758
19788
  }
19759
19789
  function normalizeReplacementPath(filePath) {
19760
19790
  if (filePath.startsWith("~/")) {
19761
- return import_node_path17.default.join(import_node_os3.default.homedir(), filePath.slice(2));
19791
+ return import_node_path16.default.join(import_node_os3.default.homedir(), filePath.slice(2));
19762
19792
  }
19763
- if (import_node_path17.default.isAbsolute(filePath)) return filePath;
19764
- return import_node_path17.default.resolve(process.cwd(), filePath);
19793
+ if (import_node_path16.default.isAbsolute(filePath)) return filePath;
19794
+ return import_node_path16.default.resolve(process.cwd(), filePath);
19765
19795
  }
19766
19796
  function parseOpenRuntimeConfig(args) {
19767
19797
  const modules = {};
@@ -19780,7 +19810,7 @@ function parseOpenRuntimeConfig(args) {
19780
19810
  }
19781
19811
  const moduleName = raw.slice(0, eqIndex).trim();
19782
19812
  const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim());
19783
- if (!import_node_fs14.default.existsSync(filePath)) {
19813
+ if (!import_node_fs13.default.existsSync(filePath)) {
19784
19814
  console.error(` rnx open: replacement file not found: ${filePath}`);
19785
19815
  process.exit(1);
19786
19816
  }
@@ -19806,16 +19836,12 @@ function parseOpenRuntimeConfig(args) {
19806
19836
  return config.modules || config.network ? config : void 0;
19807
19837
  }
19808
19838
  async function resolveOpenAppProject(resolvedBundle) {
19809
- if (resolvedBundle) {
19810
- const advertisedRoot = await resolveMetroProjectRoot(resolvedBundle.bundleUrl);
19811
- if (advertisedRoot) {
19812
- return resolveAppProject(advertisedRoot, { trustStart: true });
19813
- }
19814
- }
19815
- return resolveAppProject(process.env.CONTRAST_REPO_DIR || process.cwd());
19839
+ if (!resolvedBundle) return null;
19840
+ const advertisedRoot = await resolveMetroProjectRoot(resolvedBundle.bundleUrl);
19841
+ return advertisedRoot ? resolveAppProject(advertisedRoot) : null;
19816
19842
  }
19817
19843
  async function ensureConfiguredRuntime(version) {
19818
- if (import_node_fs14.default.existsSync(import_node_path17.default.join(runtimeDir(version), "index.html"))) return;
19844
+ if (import_node_fs13.default.existsSync(import_node_path16.default.join(runtimeDir(version), "index.html"))) return;
19819
19845
  console.error(` installing configured engine runtime ${version}...`);
19820
19846
  const result = await sootsimRuntime.install({
19821
19847
  version,
@@ -19847,12 +19873,10 @@ async function stampAppFonts(urlStr, project) {
19847
19873
  try {
19848
19874
  const url = new URL(urlStr);
19849
19875
  const bundleUrl = url.searchParams.get("bundle") || "";
19850
- if (!bundleUrl) return urlStr;
19851
- const repoDir = project?.appDir ?? await resolveMetroProjectRoot(bundleUrl) ?? void 0;
19876
+ if (!bundleUrl || !project) return urlStr;
19852
19877
  const specs = discoverNativeLinkedAppFonts({
19853
19878
  bundleUrl,
19854
- repoDir,
19855
- exactProjectDir: !!project || !!repoDir
19879
+ projectDir: project.appDir
19856
19880
  });
19857
19881
  if (specs.length === 0) return urlStr;
19858
19882
  const reachable = await filterReachableAppFonts(specs);
@@ -19914,12 +19938,10 @@ async function stampAppSplash(urlStr, project) {
19914
19938
  try {
19915
19939
  const url = new URL(urlStr);
19916
19940
  const bundleUrl = url.searchParams.get("bundle") || "";
19917
- if (!bundleUrl) return urlStr;
19918
- const repoDir = project?.appDir ?? await resolveMetroProjectRoot(bundleUrl) ?? void 0;
19941
+ if (!bundleUrl || !project) return urlStr;
19919
19942
  const spec = discoverConfigPluginAppSplash({
19920
19943
  bundleUrl,
19921
- repoDir,
19922
- exactProjectDir: !!project || !!repoDir
19944
+ projectDir: project.appDir
19923
19945
  });
19924
19946
  if (!spec) return urlStr;
19925
19947
  if (spec.imageUrl) {
@@ -20179,7 +20201,7 @@ function playwrightHostPidForSim(sim) {
20179
20201
  function signalDriverLaunchConnected(result) {
20180
20202
  if (!result.connectAckFile) return false;
20181
20203
  try {
20182
- import_node_fs14.default.writeFileSync(
20204
+ import_node_fs13.default.writeFileSync(
20183
20205
  result.connectAckFile,
20184
20206
  `${JSON.stringify({ connectedAt: Date.now(), pid: result.pid ?? null })}
20185
20207
  `,
@@ -20193,19 +20215,19 @@ function signalDriverLaunchConnected(result) {
20193
20215
  function printDriverDiagnosticLogTail(result) {
20194
20216
  if (!result.diagnosticLogPath) return;
20195
20217
  try {
20196
- const detail = import_node_fs14.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
20218
+ const detail = import_node_fs13.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
20197
20219
  if (!detail) {
20198
- console.error(` ${import_node_path17.default.basename(result.diagnosticLogPath)} was empty`);
20220
+ console.error(` ${import_node_path16.default.basename(result.diagnosticLogPath)} was empty`);
20199
20221
  return;
20200
20222
  }
20201
20223
  const shown = detail.length > 4e3 ? `\u2026
20202
20224
  ${detail.slice(-4e3)}` : detail;
20203
- console.error(` ${import_node_path17.default.basename(result.diagnosticLogPath)}:`);
20225
+ console.error(` ${import_node_path16.default.basename(result.diagnosticLogPath)}:`);
20204
20226
  console.error(shown);
20205
20227
  } catch (err) {
20206
20228
  const message = err instanceof Error ? err.message : String(err);
20207
20229
  console.error(
20208
- ` failed to read ${import_node_path17.default.basename(result.diagnosticLogPath)}: ${message}`
20230
+ ` failed to read ${import_node_path16.default.basename(result.diagnosticLogPath)}: ${message}`
20209
20231
  );
20210
20232
  }
20211
20233
  }
@@ -20687,7 +20709,7 @@ async function runOpenCommand(args, opts = {}) {
20687
20709
  );
20688
20710
  printOpenedSim(shellUrl, { ...targetSim, url: shellUrl }, "current sim");
20689
20711
  await maybeDescribeAfterOpen(parsed.wsPort, targetSim.id, args);
20690
- return;
20712
+ return parsed.wsPort;
20691
20713
  }
20692
20714
  } finally {
20693
20715
  bridge.close();
@@ -20760,6 +20782,7 @@ async function runOpenCommand(args, opts = {}) {
20760
20782
  signalDriverLaunchConnected(result);
20761
20783
  printOpenedSim(openUrl2, match, `${driver.name} driver`);
20762
20784
  await maybeDescribeAfterOpen(parsed.wsPort, match.id, args);
20785
+ return parsed.wsPort;
20763
20786
  }
20764
20787
  async function runUseLikeCommand(args, opts = {}) {
20765
20788
  const parsed = parseBridgeCliArgs(args, {
@@ -21022,14 +21045,14 @@ async function runCloseCommand(args, opts = {}) {
21022
21045
  bridge.close();
21023
21046
  }
21024
21047
  }
21025
- var import_node_child_process6, import_node_fs14, import_node_os3, import_node_path17, 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, DEEP_LINK_SCHEME_RE;
21048
+ var import_node_child_process6, import_node_fs13, import_node_os3, import_node_path16, 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, DEEP_LINK_SCHEME_RE;
21026
21049
  var init_control = __esm({
21027
21050
  "cli/commands/control.ts"() {
21028
21051
  "use strict";
21029
21052
  import_node_child_process6 = require("node:child_process");
21030
- import_node_fs14 = __toESM(require("node:fs"), 1);
21053
+ import_node_fs13 = __toESM(require("node:fs"), 1);
21031
21054
  import_node_os3 = __toESM(require("node:os"), 1);
21032
- import_node_path17 = __toESM(require("node:path"), 1);
21055
+ import_node_path16 = __toESM(require("node:path"), 1);
21033
21056
  import_settings = require("sootsim-engine/settings");
21034
21057
  import_ws5 = require("ws");
21035
21058
  init_dev_server_scanner();
@@ -21811,14 +21834,14 @@ process.stdin.on('end', async () => {
21811
21834
  function getBaseDir() {
21812
21835
  const explicit = process.env[SESSION_FILE_ENV];
21813
21836
  if (explicit?.trim()) {
21814
- return (0, import_node_path18.dirname)((0, import_node_path18.resolve)(explicit));
21837
+ return (0, import_node_path17.dirname)((0, import_node_path17.resolve)(explicit));
21815
21838
  }
21816
21839
  return electronUserDataDir();
21817
21840
  }
21818
21841
  function getSharedDesktopAuthFilePath() {
21819
21842
  const explicit = process.env[SESSION_FILE_ENV];
21820
- if (explicit?.trim()) return (0, import_node_path18.resolve)(explicit);
21821
- return (0, import_node_path18.join)(getBaseDir(), "desktop-auth.json");
21843
+ if (explicit?.trim()) return (0, import_node_path17.resolve)(explicit);
21844
+ return (0, import_node_path17.join)(getBaseDir(), "desktop-auth.json");
21822
21845
  }
21823
21846
  function normalizeUser(input) {
21824
21847
  if (!input || typeof input !== "object") return null;
@@ -21865,26 +21888,26 @@ function normalizeSession(input) {
21865
21888
  }
21866
21889
  function readSharedDesktopAuthSession() {
21867
21890
  const filepath = getSharedDesktopAuthFilePath();
21868
- if (!(0, import_node_fs15.existsSync)(filepath)) return null;
21891
+ if (!(0, import_node_fs14.existsSync)(filepath)) return null;
21869
21892
  try {
21870
- const parsed = JSON.parse((0, import_node_fs15.readFileSync)(filepath, "utf8"));
21893
+ const parsed = JSON.parse((0, import_node_fs14.readFileSync)(filepath, "utf8"));
21871
21894
  const normalized = normalizeSession(parsed);
21872
21895
  if (!normalized) {
21873
- (0, import_node_fs15.rmSync)(filepath, { force: true });
21896
+ (0, import_node_fs14.rmSync)(filepath, { force: true });
21874
21897
  return null;
21875
21898
  }
21876
21899
  return normalized;
21877
21900
  } catch {
21878
- (0, import_node_fs15.rmSync)(filepath, { force: true });
21901
+ (0, import_node_fs14.rmSync)(filepath, { force: true });
21879
21902
  return null;
21880
21903
  }
21881
21904
  }
21882
- var import_node_fs15, import_node_path18, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
21905
+ var import_node_fs14, import_node_path17, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
21883
21906
  var init_shared_session = __esm({
21884
21907
  "src/auth/shared-session.ts"() {
21885
21908
  "use strict";
21886
- import_node_fs15 = require("node:fs");
21887
- import_node_path18 = require("node:path");
21909
+ import_node_fs14 = require("node:fs");
21910
+ import_node_path17 = require("node:path");
21888
21911
  init_home_paths();
21889
21912
  SESSION_VERSION = 1;
21890
21913
  SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
@@ -21967,33 +21990,13 @@ var init_run_registry = __esm({
21967
21990
  }
21968
21991
  });
21969
21992
 
21970
- // cli/telemetry.ts
21971
- var init_telemetry = __esm({
21972
- "cli/telemetry.ts"() {
21973
- "use strict";
21974
- init_agent_identity();
21975
- init_shared_session();
21976
- init_home_paths();
21977
- }
21978
- });
21979
-
21980
21993
  // cli/commands/upload.ts
21981
21994
  async function resolveDefaultUploadOrigin(explicitOrigin) {
21982
21995
  if (explicitOrigin) return explicitOrigin;
21983
21996
  if (process.env.SOOTSIM_UPLOAD_ORIGIN) return process.env.SOOTSIM_UPLOAD_ORIGIN;
21984
- try {
21985
- const res = await fetch(`${LOCAL_DEFAULT_ORIGIN}/api/preview/upload/init`, {
21986
- method: "OPTIONS",
21987
- signal: AbortSignal.timeout(LOCAL_UPLOAD_PROBE_TIMEOUT_MS)
21988
- });
21989
- if (res.ok || res.status === 204 || res.status === 405) {
21990
- return LOCAL_DEFAULT_ORIGIN;
21991
- }
21992
- } catch {
21993
- }
21994
21997
  return REMOTE_DEFAULT_ORIGIN;
21995
21998
  }
21996
- var import_compress, import_compress_node, import_presigned_upload, import_trace, REMOTE_DEFAULT_ORIGIN, REMOTE_DEFAULT_PUBLIC_PREVIEW_ORIGIN, LOCAL_DEFAULT_ORIGIN, LOCAL_UPLOAD_PROBE_TIMEOUT_MS;
21999
+ var import_compress, import_compress_node, import_presigned_upload, import_trace, REMOTE_DEFAULT_ORIGIN, REMOTE_DEFAULT_PUBLIC_PREVIEW_ORIGIN;
21997
22000
  var init_upload = __esm({
21998
22001
  "cli/commands/upload.ts"() {
21999
22002
  "use strict";
@@ -22001,19 +22004,15 @@ var init_upload = __esm({
22001
22004
  import_compress_node = require("sootsim-engine/preview/compress-node");
22002
22005
  import_presigned_upload = require("sootsim-engine/preview/presigned-upload");
22003
22006
  import_trace = require("sootsim-engine/preview/trace");
22004
- init_shared_session();
22005
22007
  init_public_brand();
22006
22008
  init_app_fonts();
22007
22009
  init_app_splash();
22008
22010
  init_auth();
22009
22011
  init_open_url2();
22010
22012
  init_run_registry();
22011
- init_telemetry();
22012
22013
  init_ws_bridge();
22013
22014
  REMOTE_DEFAULT_ORIGIN = process.env.SOOTSIM_UPLOAD_ORIGIN || "https://contrast.dev";
22014
22015
  REMOTE_DEFAULT_PUBLIC_PREVIEW_ORIGIN = process.env.SOOTSIM_PREVIEW_ORIGIN || rnxPublicBrand.origin;
22015
- LOCAL_DEFAULT_ORIGIN = "https://contrast.localhost:3000";
22016
- LOCAL_UPLOAD_PROBE_TIMEOUT_MS = 2e3;
22017
22016
  }
22018
22017
  });
22019
22018
 
@@ -22147,16 +22146,16 @@ function resolveFlowScreenshotPath(screenshotDir, name2) {
22147
22146
  }
22148
22147
  function resolveProjectRelativeFlowBase(flowDir) {
22149
22148
  if (!flowDir) return process.cwd();
22150
- return path17.basename(flowDir) === ".maestro" ? path17.dirname(flowDir) : flowDir;
22149
+ return path16.basename(flowDir) === ".maestro" ? path16.dirname(flowDir) : flowDir;
22151
22150
  }
22152
22151
  function resolveFlowScreenshotPathWithMode(screenshotDir, name2, opts) {
22153
22152
  const normalizedName = name2.endsWith(".png") ? name2 : `${name2}.png`;
22154
- if (path17.isAbsolute(normalizedName)) return normalizedName;
22153
+ if (path16.isAbsolute(normalizedName)) return normalizedName;
22155
22154
  if (opts.mode === "flow" && /[\\/]/.test(normalizedName)) {
22156
22155
  const baseDir = normalizedName.startsWith("./") || normalizedName.startsWith("../") ? opts.flowDir ?? process.cwd() : resolveProjectRelativeFlowBase(opts.flowDir);
22157
- return path17.resolve(baseDir, normalizedName);
22156
+ return path16.resolve(baseDir, normalizedName);
22158
22157
  }
22159
- return path17.join(screenshotDir, normalizedName);
22158
+ return path16.join(screenshotDir, normalizedName);
22160
22159
  }
22161
22160
  function normalizeFlowScreenshotSpec(step) {
22162
22161
  if (typeof step === "string") {
@@ -22213,12 +22212,12 @@ function parseFlowSteps(content) {
22213
22212
  const parsed = import_yaml.default.parse(candidate);
22214
22213
  return Array.isArray(parsed) ? parsed : [];
22215
22214
  }
22216
- var fs17, path17, import_settings3, import_yaml, DEFAULT_TIMEOUT, FLOW_TIMEOUT_SCALE, scaledTimeout, SCREEN_W, SCREEN_H, SootSimBridgeFlowRunner;
22215
+ var fs16, path16, import_settings3, import_yaml, DEFAULT_TIMEOUT, FLOW_TIMEOUT_SCALE, scaledTimeout, SCREEN_W, SCREEN_H, SootSimBridgeFlowRunner;
22217
22216
  var init_bridge_flow_runner = __esm({
22218
22217
  "cli/bridge-flow-runner.ts"() {
22219
22218
  "use strict";
22220
- fs17 = __toESM(require("fs"), 1);
22221
- path17 = __toESM(require("path"), 1);
22219
+ fs16 = __toESM(require("fs"), 1);
22220
+ path16 = __toESM(require("path"), 1);
22222
22221
  import_settings3 = require("sootsim-engine/settings");
22223
22222
  import_yaml = __toESM(require("yaml"), 1);
22224
22223
  init_frame_compose();
@@ -22515,14 +22514,14 @@ var init_bridge_flow_runner = __esm({
22515
22514
  if (chunks.length === 0) {
22516
22515
  throw new Error("recording requested but no video buffer was produced");
22517
22516
  }
22518
- fs17.mkdirSync(this.opts.recordingOutputDir, { recursive: true });
22517
+ fs16.mkdirSync(this.opts.recordingOutputDir, { recursive: true });
22519
22518
  const videoMime = stopped.mime || this.opts.recordingFormat || "video/webm";
22520
22519
  const extension = videoMime.includes("mp4") ? "mp4" : "webm";
22521
- const outputPath = path17.join(
22520
+ const outputPath = path16.join(
22522
22521
  this.opts.recordingOutputDir,
22523
22522
  `sootsim-${Date.now()}.${extension}`
22524
22523
  );
22525
- fs17.writeFileSync(outputPath, Buffer.concat(chunks));
22524
+ fs16.writeFileSync(outputPath, Buffer.concat(chunks));
22526
22525
  return outputPath;
22527
22526
  }
22528
22527
  async waitForRecordingTail(opts) {
@@ -23025,7 +23024,7 @@ var init_bridge_flow_runner = __esm({
23025
23024
  flowDir: this.opts.flowDir
23026
23025
  }
23027
23026
  );
23028
- fs17.mkdirSync(path17.dirname(outputPath), { recursive: true });
23027
+ fs16.mkdirSync(path16.dirname(outputPath), { recursive: true });
23029
23028
  const layers = spec.layers ?? this.opts.screenshotLayers;
23030
23029
  const screenshotRequest = {
23031
23030
  type: "screenshot",
@@ -23041,11 +23040,11 @@ var init_bridge_flow_runner = __esm({
23041
23040
  throw new Error("could not read current device model for framed screenshot");
23042
23041
  }
23043
23042
  const framed = await composeFramedScreenshot(rawBuffer, model);
23044
- fs17.writeFileSync(outputPath, framed);
23043
+ fs16.writeFileSync(outputPath, framed);
23045
23044
  console.log(`[flow] screenshot: ${outputPath} (frame: ${model})`);
23046
23045
  return outputPath;
23047
23046
  }
23048
- fs17.writeFileSync(outputPath, rawBuffer);
23047
+ fs16.writeFileSync(outputPath, rawBuffer);
23049
23048
  console.log(`[flow] screenshot: ${outputPath}`);
23050
23049
  return outputPath;
23051
23050
  }
@@ -23061,22 +23060,22 @@ var init_bridge_flow_runner = __esm({
23061
23060
  return model;
23062
23061
  }
23063
23062
  async captureScreenshot(outputPath) {
23064
- fs17.mkdirSync(path17.dirname(outputPath), { recursive: true });
23063
+ fs16.mkdirSync(path16.dirname(outputPath), { recursive: true });
23065
23064
  const dataUrl = await this.bridge.send({
23066
23065
  type: "screenshot",
23067
23066
  simId: this.opts.simId
23068
23067
  });
23069
23068
  const base64 = dataUrl.replace(/^data:image\/png;base64,/, "");
23070
- fs17.writeFileSync(outputPath, Buffer.from(base64, "base64"));
23069
+ fs16.writeFileSync(outputPath, Buffer.from(base64, "base64"));
23071
23070
  }
23072
23071
  // writes a per-failure debug bundle: screenshot + structured describe
23073
23072
  // + a11y tree + console errors/warnings + failed network requests +
23074
23073
  // shell state, all under one directory so post-mortem is one grep away.
23075
23074
  async captureFailureBundle(outputDir, context) {
23076
- fs17.mkdirSync(outputDir, { recursive: true });
23075
+ fs16.mkdirSync(outputDir, { recursive: true });
23077
23076
  const write = (name2, content) => {
23078
23077
  try {
23079
- fs17.writeFileSync(path17.join(outputDir, name2), content);
23078
+ fs16.writeFileSync(path16.join(outputDir, name2), content);
23080
23079
  } catch {
23081
23080
  }
23082
23081
  };
@@ -23595,8 +23594,8 @@ var init_bridge_flow_runner = __esm({
23595
23594
  runScript(target) {
23596
23595
  const spec = typeof target === "string" ? { file: target } : target;
23597
23596
  if (!spec.file) throw new Error("runScript requires a file");
23598
- const scriptPath = path17.resolve(this.opts.flowDir, spec.file);
23599
- const source = fs17.readFileSync(scriptPath, "utf8");
23597
+ const scriptPath = path16.resolve(this.opts.flowDir, spec.file);
23598
+ const source = fs16.readFileSync(scriptPath, "utf8");
23600
23599
  this.js.evaluate(source, { env: spec.env });
23601
23600
  console.log(`[flow] runScript: ${spec.file} done`);
23602
23601
  }
@@ -23890,10 +23889,10 @@ var init_bridge_flow_runner = __esm({
23890
23889
  for (const [k, v] of Object.entries(env)) this.js.putEnv(k, v);
23891
23890
  }
23892
23891
  if (file) {
23893
- const flowPath = path17.resolve(this.opts.flowDir, file);
23894
- const flowContent = fs17.readFileSync(flowPath, "utf8");
23892
+ const flowPath = path16.resolve(this.opts.flowDir, file);
23893
+ const flowContent = fs16.readFileSync(flowPath, "utf8");
23895
23894
  const previousDir = this.opts.flowDir;
23896
- this.opts.flowDir = path17.dirname(flowPath);
23895
+ this.opts.flowDir = path16.dirname(flowPath);
23897
23896
  try {
23898
23897
  await this.runFlow(parseFlowSteps(flowContent));
23899
23898
  } finally {
@@ -24048,8 +24047,8 @@ __export(registry_exports, {
24048
24047
  skillRegistry: () => skillRegistry
24049
24048
  });
24050
24049
  module.exports = __toCommonJS(registry_exports);
24051
- var fs18 = __toESM(require("fs"), 1);
24052
- var path18 = __toESM(require("path"), 1);
24050
+ var fs17 = __toESM(require("fs"), 1);
24051
+ var path17 = __toESM(require("path"), 1);
24053
24052
 
24054
24053
  // src/skills/builtin/a11y-review.ts
24055
24054
  init_reap_browser();
@@ -24068,8 +24067,8 @@ var skill = {
24068
24067
  },
24069
24068
  async execute(params, context) {
24070
24069
  const { chromium } = await import("playwright");
24071
- const path19 = await import("path");
24072
- const fs19 = await import("fs");
24070
+ const path18 = await import("path");
24071
+ const fs18 = await import("fs");
24073
24072
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
24074
24073
  headless: true
24075
24074
  });
@@ -24124,9 +24123,9 @@ var skill = {
24124
24123
  warnings: issues.filter((i) => i.severity === "warning").length,
24125
24124
  tree: typeof tree === "string" ? tree : JSON.stringify(tree)
24126
24125
  };
24127
- const reportPath = path19.join(context.outputDir, "a11y-report.json");
24128
- fs19.mkdirSync(path19.dirname(reportPath), { recursive: true });
24129
- fs19.writeFileSync(reportPath, JSON.stringify(report, null, 2));
24126
+ const reportPath = path18.join(context.outputDir, "a11y-report.json");
24127
+ fs18.mkdirSync(path18.dirname(reportPath), { recursive: true });
24128
+ fs18.writeFileSync(reportPath, JSON.stringify(report, null, 2));
24130
24129
  return {
24131
24130
  success: issues.filter((i) => i.severity === "error").length === 0,
24132
24131
  message: `${report.errors} errors, ${report.warnings} warnings across ${report.nodeCount} nodes`,
@@ -24159,14 +24158,14 @@ var skill2 = {
24159
24158
  }
24160
24159
  },
24161
24160
  async execute(params, context) {
24162
- const fs19 = await import("node:fs");
24163
- const path19 = await import("node:path");
24161
+ const fs18 = await import("node:fs");
24162
+ const path18 = await import("node:path");
24164
24163
  const { COMPAT_RESULT_CAVEAT: COMPAT_RESULT_CAVEAT2, formatPackageStatus: formatPackageStatus2, formatScanCounts: formatScanCounts2, scanDeps: scanDeps2 } = await Promise.resolve().then(() => (init_web(), web_exports));
24165
- const packagePath = path19.join(context.projectDir, "package.json");
24166
- if (!fs19.existsSync(packagePath)) {
24164
+ const packagePath = path18.join(context.projectDir, "package.json");
24165
+ if (!fs18.existsSync(packagePath)) {
24167
24166
  return { success: false, message: "No package.json found" };
24168
24167
  }
24169
- const parsed = JSON.parse(fs19.readFileSync(packagePath, "utf8"));
24168
+ const parsed = JSON.parse(fs18.readFileSync(packagePath, "utf8"));
24170
24169
  if (!parsed || typeof parsed !== "object") {
24171
24170
  return { success: false, message: "package.json is not an object" };
24172
24171
  }
@@ -24178,9 +24177,9 @@ var skill2 = {
24178
24177
  }) : scanDeps2(parsed);
24179
24178
  const packageResult = params.packageName ? scan.packages[0] : void 0;
24180
24179
  const message = packageResult ? `${params.packageName}: ${formatPackageStatus2(packageResult)}. ${COMPAT_RESULT_CAVEAT2}` : `${formatScanCounts2(scan)}. ${COMPAT_RESULT_CAVEAT2}`;
24181
- const reportPath = path19.join(context.outputDir, "compat-report.json");
24182
- fs19.mkdirSync(path19.dirname(reportPath), { recursive: true });
24183
- fs19.writeFileSync(reportPath, `${JSON.stringify(scan, null, 2)}
24180
+ const reportPath = path18.join(context.outputDir, "compat-report.json");
24181
+ fs18.mkdirSync(path18.dirname(reportPath), { recursive: true });
24182
+ fs18.writeFileSync(reportPath, `${JSON.stringify(scan, null, 2)}
24184
24183
  `);
24185
24184
  return {
24186
24185
  success: true,
@@ -24224,8 +24223,8 @@ var skill3 = {
24224
24223
  record: { type: "boolean", description: "Record video of the flow" }
24225
24224
  },
24226
24225
  async execute(params, context) {
24227
- const fs19 = await import("fs");
24228
- const path19 = await import("path");
24226
+ const fs18 = await import("fs");
24227
+ const path18 = await import("path");
24229
24228
  const yaml2 = await import("yaml");
24230
24229
  const { DEFAULT_SOOTSIM_BRIDGE_PORT: DEFAULT_SOOTSIM_BRIDGE_PORT2 } = await Promise.resolve().then(() => (init_bridge_constants(), bridge_constants_exports));
24231
24230
  const { SootSimBridgeHost: SootSimBridgeHost2 } = await Promise.resolve().then(() => (init_bridge_host(), bridge_host_exports));
@@ -24235,7 +24234,7 @@ var skill3 = {
24235
24234
  const host = new SootSimBridgeHost2({ port: DEFAULT_SOOTSIM_BRIDGE_PORT2 });
24236
24235
  host.start({ silent: true });
24237
24236
  try {
24238
- const flowContent = fs19.readFileSync(params.flowPath, "utf8");
24237
+ const flowContent = fs18.readFileSync(params.flowPath, "utf8");
24239
24238
  const fmMatch = flowContent.match(/^---\n([\s\S]*?)\n---\n?/);
24240
24239
  const stepsBody = fmMatch ? flowContent.slice(fmMatch[0].length) : flowContent;
24241
24240
  const steps = yaml2.parse(stepsBody);
@@ -24262,7 +24261,7 @@ var skill3 = {
24262
24261
  });
24263
24262
  const driver = new SootSimBridgeFlowRunner2(bridge, {
24264
24263
  screenshotDir: context.outputDir,
24265
- flowDir: path19.dirname(path19.resolve(params.flowPath)),
24264
+ flowDir: path18.dirname(path18.resolve(params.flowPath)),
24266
24265
  simId,
24267
24266
  recordingOutputDir: params.record ? context.outputDir : void 0
24268
24267
  });
@@ -24284,7 +24283,7 @@ var skill3 = {
24284
24283
  simId,
24285
24284
  videoPath: videoPath || void 0
24286
24285
  },
24287
- artifacts: videoPath ? [{ type: "video", name: path19.basename(videoPath), path: videoPath }] : void 0
24286
+ artifacts: videoPath ? [{ type: "video", name: path18.basename(videoPath), path: videoPath }] : void 0
24288
24287
  };
24289
24288
  } finally {
24290
24289
  try {
@@ -24331,8 +24330,8 @@ var skill4 = {
24331
24330
  },
24332
24331
  async execute(params, context) {
24333
24332
  const { chromium } = await import("playwright");
24334
- const path19 = await import("path");
24335
- const fs19 = await import("fs");
24333
+ const path18 = await import("path");
24334
+ const fs18 = await import("fs");
24336
24335
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
24337
24336
  headless: true
24338
24337
  });
@@ -24358,9 +24357,9 @@ var skill4 = {
24358
24357
  ...profile,
24359
24358
  fps: syncCount > 0 ? (syncCount / (duration / 1e3)).toFixed(1) : "0"
24360
24359
  };
24361
- const reportPath = path19.join(context.outputDir, "perf-report.json");
24362
- fs19.mkdirSync(path19.dirname(reportPath), { recursive: true });
24363
- fs19.writeFileSync(reportPath, JSON.stringify(report, null, 2));
24360
+ const reportPath = path18.join(context.outputDir, "perf-report.json");
24361
+ fs18.mkdirSync(path18.dirname(reportPath), { recursive: true });
24362
+ fs18.writeFileSync(reportPath, JSON.stringify(report, null, 2));
24364
24363
  return {
24365
24364
  success: true,
24366
24365
  message: `${report.fps} fps, ${profile.avgSyncMs?.toFixed(1)}ms avg sync, ${nodeCount} nodes`,
@@ -24393,8 +24392,8 @@ var skill5 = {
24393
24392
  },
24394
24393
  async execute(params, context) {
24395
24394
  const { chromium } = await import("playwright");
24396
- const path19 = await import("path");
24397
- const fs19 = await import("fs");
24395
+ const path18 = await import("path");
24396
+ const fs18 = await import("fs");
24398
24397
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
24399
24398
  headless: true
24400
24399
  });
@@ -24402,8 +24401,8 @@ var skill5 = {
24402
24401
  try {
24403
24402
  await page.goto(context.url, { waitUntil: "networkidle" });
24404
24403
  await page.waitForTimeout(2e3);
24405
- const outputPath = path19.join(context.outputDir, `${params.name}.png`);
24406
- fs19.mkdirSync(path19.dirname(outputPath), { recursive: true });
24404
+ const outputPath = path18.join(context.outputDir, `${params.name}.png`);
24405
+ fs18.mkdirSync(path18.dirname(outputPath), { recursive: true });
24407
24406
  await page.screenshot({ path: outputPath });
24408
24407
  return {
24409
24408
  success: true,
@@ -24444,8 +24443,8 @@ var skill6 = {
24444
24443
  },
24445
24444
  async execute(params, context) {
24446
24445
  const { chromium } = await import("playwright");
24447
- const path19 = await import("path");
24448
- const fs19 = await import("fs");
24446
+ const path18 = await import("path");
24447
+ const fs18 = await import("fs");
24449
24448
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
24450
24449
  headless: true
24451
24450
  });
@@ -24453,14 +24452,14 @@ var skill6 = {
24453
24452
  try {
24454
24453
  await page.goto(context.url, { waitUntil: "networkidle" });
24455
24454
  await page.waitForTimeout(2e3);
24456
- const currentPath = path19.join(context.outputDir, `${params.name}-current.png`);
24457
- fs19.mkdirSync(path19.dirname(currentPath), { recursive: true });
24455
+ const currentPath = path18.join(context.outputDir, `${params.name}-current.png`);
24456
+ fs18.mkdirSync(path18.dirname(currentPath), { recursive: true });
24458
24457
  await page.screenshot({ path: currentPath });
24459
24458
  try {
24460
24459
  const { PNG } = await import("pngjs");
24461
24460
  const pixelmatch = (await import("pixelmatch")).default;
24462
- const baseline = PNG.sync.read(fs19.readFileSync(params.baseline));
24463
- const current = PNG.sync.read(fs19.readFileSync(currentPath));
24461
+ const baseline = PNG.sync.read(fs18.readFileSync(params.baseline));
24462
+ const current = PNG.sync.read(fs18.readFileSync(currentPath));
24464
24463
  const { width, height } = baseline;
24465
24464
  const diff = new PNG({ width, height });
24466
24465
  const numDiffPixels = pixelmatch(
@@ -24471,8 +24470,8 @@ var skill6 = {
24471
24470
  height,
24472
24471
  { threshold: params.threshold || 0.1 }
24473
24472
  );
24474
- const diffPath = path19.join(context.outputDir, `${params.name}-diff.png`);
24475
- fs19.writeFileSync(diffPath, PNG.sync.write(diff));
24473
+ const diffPath = path18.join(context.outputDir, `${params.name}-diff.png`);
24474
+ fs18.writeFileSync(diffPath, PNG.sync.write(diff));
24476
24475
  const totalPixels = width * height;
24477
24476
  const diffPercent = (numDiffPixels / totalPixels * 100).toFixed(2);
24478
24477
  return {
@@ -24536,10 +24535,10 @@ var skillRegistry = {
24536
24535
  },
24537
24536
  // load skills from a project manifest (sootsim-skills.json)
24538
24537
  async loadFromManifest(projectDir) {
24539
- const manifestPath = path18.join(projectDir, "sootsim-skills.json");
24540
- if (!fs18.existsSync(manifestPath)) return;
24538
+ const manifestPath = path17.join(projectDir, "sootsim-skills.json");
24539
+ if (!fs17.existsSync(manifestPath)) return;
24541
24540
  try {
24542
- const manifest = JSON.parse(fs18.readFileSync(manifestPath, "utf8"));
24541
+ const manifest = JSON.parse(fs17.readFileSync(manifestPath, "utf8"));
24543
24542
  if (Array.isArray(manifest.skills)) {
24544
24543
  for (const entry of manifest.skills) {
24545
24544
  if (typeof entry === "string") {
@@ -24550,7 +24549,7 @@ var skillRegistry = {
24550
24549
  }
24551
24550
  } else if (entry.file) {
24552
24551
  try {
24553
- const mod = await import(path18.resolve(projectDir, entry.file));
24552
+ const mod = await import(path17.resolve(projectDir, entry.file));
24554
24553
  if (mod.skill) this.register(mod.skill);
24555
24554
  } catch {
24556
24555
  }
@@ -24563,10 +24562,10 @@ var skillRegistry = {
24563
24562
  // auto-discover skills based on project dependencies
24564
24563
  suggestSkills(projectDir) {
24565
24564
  const suggestions = [];
24566
- const pkgPath = path18.join(projectDir, "package.json");
24567
- if (!fs18.existsSync(pkgPath)) return suggestions;
24565
+ const pkgPath = path17.join(projectDir, "package.json");
24566
+ if (!fs17.existsSync(pkgPath)) return suggestions;
24568
24567
  try {
24569
- const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf8"));
24568
+ const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf8"));
24570
24569
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
24571
24570
  if (deps["detox"] || deps["maestro"]) {
24572
24571
  suggestions.push("maestro-test");