peachsim 0.1.626 → 0.1.628

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 (106) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -2
  3. package/cli/bin.ts +6 -2
  4. package/cli/bridge-flow-runner.ts +79 -72
  5. package/cli/commands/box/bundle-plane.ts +7 -2
  6. package/cli/commands/box.ts +9 -2
  7. package/cli/commands/cleanup.ts +6 -6
  8. package/cli/commands/control.ts +36 -6
  9. package/cli/commands/detox.ts +25 -11
  10. package/cli/commands/flow.ts +20 -8
  11. package/cli/commands/inspect.ts +22 -37
  12. package/cli/commands/maestro-generate.ts +5 -12
  13. package/cli/commands/maestro.ts +16 -6
  14. package/cli/commands/mode.ts +11 -7
  15. package/cli/commands/platform.ts +1 -0
  16. package/cli/commands/report-issue.ts +1 -1
  17. package/cli/commands/setup.ts +16 -11
  18. package/cli/commands/show-touches-mode.ts +33 -0
  19. package/cli/commands/skills.ts +16 -0
  20. package/cli/commands/test.ts +22 -1
  21. package/cli/commands/upload.ts +18 -8
  22. package/cli/commands/welcome.ts +3 -3
  23. package/cli/drivers/playwright-sim-host.ts +16 -8
  24. package/cli/drivers/playwright.ts +8 -5
  25. package/cli/help-core.ts +9 -4
  26. package/cli/internal-child.ts +14 -2
  27. package/cli/levenshtein.ts +27 -0
  28. package/cli/main.ts +28 -18
  29. package/cli/run-rnx.ts +8 -4
  30. package/cli/ws-bridge.ts +3 -2
  31. package/detox/index.ts +234 -7
  32. package/dist-lib/agent-daemon-client.cjs +2 -1
  33. package/dist-lib/agent-events.cjs +1 -1
  34. package/dist-lib/agent-identity.cjs +1 -1
  35. package/dist-lib/agent-sessions.cjs +1 -1
  36. package/dist-lib/attached-projects.cjs +1 -1
  37. package/dist-lib/auth/shared-session.cjs +1 -1
  38. package/dist-lib/backend-origin.cjs +1 -1
  39. package/dist-lib/beta.cjs +1 -1
  40. package/dist-lib/beta.mjs +1 -1
  41. package/dist-lib/bridge-constants.cjs +4 -1
  42. package/dist-lib/bridge-contract-input.cjs +1 -1
  43. package/dist-lib/bridge-contract-input.mjs +1 -1
  44. package/dist-lib/bridge-contract.cjs +1 -1
  45. package/dist-lib/bridge-contract.mjs +1 -1
  46. package/dist-lib/capture-contract.cjs +18 -8
  47. package/dist-lib/capture-contract.mjs +18 -8
  48. package/dist-lib/cli-constants.cjs +1 -1
  49. package/dist-lib/cloud-contract.cjs +1 -1
  50. package/dist-lib/cloud-contract.mjs +1 -1
  51. package/dist-lib/cloud-test-args.cjs +1 -1
  52. package/dist-lib/cloud-test-args.mjs +1 -1
  53. package/dist-lib/cloud.cjs +1 -1
  54. package/dist-lib/cloud.mjs +1 -1
  55. package/dist-lib/config.cjs +1 -1
  56. package/dist-lib/detox/index.cjs +170 -13
  57. package/dist-lib/dev-bundle-resolution.cjs +2 -2
  58. package/dist-lib/home-paths.cjs +1 -1
  59. package/dist-lib/host/bridge-host.cjs +280 -138
  60. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  61. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  62. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  63. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  64. package/dist-lib/host/websocket-proxy.cjs +1 -1
  65. package/dist-lib/index.cjs +62 -12
  66. package/dist-lib/jump-to-source-babel.cjs +1 -1
  67. package/dist-lib/jump-to-source-native.cjs +1 -1
  68. package/dist-lib/menu.cjs +7 -1
  69. package/dist-lib/menu.mjs +7 -1
  70. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  71. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  72. package/dist-lib/metro-production-bundle.cjs +1 -1
  73. package/dist-lib/metro-production-bundle.mjs +1 -1
  74. package/dist-lib/metro.cjs +43 -4
  75. package/dist-lib/profiles.cjs +1 -1
  76. package/dist-lib/public-brand.cjs +1 -1
  77. package/dist-lib/react-native-host-modules.cjs +1 -1
  78. package/dist-lib/react-native-host-modules.mjs +1 -1
  79. package/dist-lib/render-mode.cjs +1 -1
  80. package/dist-lib/scripts/dev-server-scanner.cjs +215 -28
  81. package/dist-lib/sdk.cjs +1 -1
  82. package/dist-lib/sdk.mjs +1 -1
  83. package/dist-lib/skills.cjs +431 -276
  84. package/dist-lib/swift-compile.cjs +1 -1
  85. package/dist-lib/swift-compile.mjs +1 -1
  86. package/dist-lib/swift-package.cjs +1 -1
  87. package/dist-lib/swift-package.mjs +1 -1
  88. package/dist-lib/swift-submit.cjs +1 -1
  89. package/dist-lib/swift-submit.mjs +1 -1
  90. package/dist-lib/swift.cjs +14512 -14208
  91. package/dist-lib/vite-kotlin.cjs +1 -1
  92. package/dist-lib/vite-swift.cjs +1 -1
  93. package/dist-lib/vite.cjs +44 -4
  94. package/install.mjs +16 -0
  95. package/install.sh +199 -0
  96. package/package.json +6 -1
  97. package/scripts/dev-server-scanner.ts +77 -46
  98. package/skills/contrast/SKILL.md +1 -1
  99. package/src/bridge-constants.ts +11 -0
  100. package/src/capture-contract.ts +40 -7
  101. package/src/dev-bundle-resolution.ts +1 -1
  102. package/src/host/bridge-host.ts +26 -137
  103. package/src/menu.ts +7 -0
  104. package/src/native-seam-manifest.ts +4 -16
  105. package/src/runtime-assets.ts +59 -2
  106. package/src/vite-plugin.ts +723 -25
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
@@ -973,6 +973,7 @@ function resolveSootsimBridgePort(input = {}) {
973
973
  if (explicitPort !== null) return explicitPort;
974
974
  return DEFAULT_SOOTSIM_BRIDGE_PORT + parsePortOffset(input.portOffset);
975
975
  }
976
+ var AUTOMATION_SIM_IDLE_REAP_TTL_MS = 5 * 6e4;
976
977
 
977
978
  // src/cli-constants.ts
978
979
  var DEFAULT_SOOTSIM_SHELL_URL = "http://localhost:5173/";
@@ -1027,7 +1028,7 @@ function resolveDefaultDevBridgeLockfile() {
1027
1028
  ) ?? sameCheckout[0];
1028
1029
  }
1029
1030
  }
1030
- return lockfiles.find((lockfile) => lockfile.bridgePort === DEFAULT_SOOTSIM_BRIDGE_PORT) ?? lockfiles[0];
1031
+ return lockfiles.find((lockfile) => lockfile.bridgePort === DEFAULT_SOOTSIM_BRIDGE_PORT) ?? null;
1031
1032
  }
1032
1033
  function resolveBridgeWorld() {
1033
1034
  const homeDir = rnxHomeDir();
@@ -3155,13 +3156,18 @@ async function connect(options = {}) {
3155
3156
 
3156
3157
  // src/capture-contract.ts
3157
3158
  var RNX_SCREEN_CAPTURE_VERSION = 1;
3159
+ function isLiveRasterSource(node) {
3160
+ const source = node.props?.source;
3161
+ return node.type === "image" && !!source && typeof source === "object" && "uri" in source && typeof source.uri === "string" && source.uri.startsWith("live:");
3162
+ }
3158
3163
  function selectRnxRasterNodeIds(tree) {
3159
3164
  const nodesById = new Map(tree.nodes.map((node) => [node.nodeId, node]));
3160
3165
  const unsupported = /* @__PURE__ */ new Set();
3161
3166
  for (const node of tree.nodes) {
3162
- const visibleFrame = node.visibleFrame ?? node.frame;
3167
+ const liveVideo = isLiveRasterSource(node);
3168
+ const visibleFrame = liveVideo && node.visibleFrame && (node.visibleFrame.width <= 0 || node.visibleFrame.height <= 0) ? node.frame : node.visibleFrame ?? node.frame;
3163
3169
  if (!visibleFrame || visibleFrame.width <= 0 || visibleFrame.height <= 0) continue;
3164
- if (node.type === "masked-view" || node.type === "text" && typeof node.text === "string" && (new RegExp("\\p{Emoji_Presentation}", "u").test(node.text) || new RegExp("\\uFE0F|\\u200D|\\u20E3|\\p{Emoji_Modifier}|\\p{Regional_Indicator}", "u").test(
3170
+ if (node.type === "masked-view" || liveVideo || node.type === "text" && typeof node.text === "string" && (new RegExp("\\p{Emoji_Presentation}", "u").test(node.text) || new RegExp("\\uFE0F|\\u200D|\\u20E3|\\p{Emoji_Modifier}|\\p{Regional_Indicator}", "u").test(
3165
3171
  node.text
3166
3172
  )) || node.style._liquidGlass !== void 0 || node.style._liquidGlassEdge !== void 0 || node.style._liquidGlassMaterialTint !== void 0) {
3167
3173
  unsupported.add(node.nodeId);
@@ -3179,17 +3185,21 @@ function selectRnxRasterNodeIds(tree) {
3179
3185
  }
3180
3186
  const screenArea = tree.width * tree.height;
3181
3187
  const maxLayerArea = screenArea * 0.25;
3182
- const maxAtlasArea = screenArea * 0.5;
3183
3188
  const candidates = [...unsupported].map((nodeId) => {
3184
3189
  const node = nodesById.get(nodeId);
3185
- const frame = node?.visibleFrame ?? node?.frame;
3186
- return frame ? { nodeId, area: frame.width * frame.height } : null;
3190
+ if (!node) return null;
3191
+ const liveVideo = isLiveRasterSource(node);
3192
+ const frame = liveVideo && node.visibleFrame && (node.visibleFrame.width <= 0 || node.visibleFrame.height <= 0) ? node.frame : node.visibleFrame ?? node.frame;
3193
+ return frame ? { nodeId, area: frame.width * frame.height, liveVideo } : null;
3187
3194
  }).filter(
3188
- (candidate) => candidate !== null && candidate.area <= maxLayerArea
3189
- ).sort((left, right) => left.area - right.area || left.nodeId - right.nodeId);
3195
+ (candidate) => candidate !== null && (candidate.liveVideo || candidate.area <= maxLayerArea)
3196
+ ).sort(
3197
+ (left, right) => Number(right.liveVideo) - Number(left.liveVideo) || left.area - right.area || left.nodeId - right.nodeId
3198
+ );
3190
3199
  const selected = /* @__PURE__ */ new Set();
3191
3200
  let atlasArea = 0;
3192
3201
  for (const candidate of candidates) {
3202
+ const maxAtlasArea = screenArea * (candidate.liveVideo ? 1 : 0.5);
3193
3203
  if (atlasArea + candidate.area > maxAtlasArea) continue;
3194
3204
  selected.add(candidate.nodeId);
3195
3205
  atlasArea += candidate.area;
@@ -3270,6 +3280,7 @@ function rnxCaptureRasterRegions(capture) {
3270
3280
  nodeId: node.nodeId,
3271
3281
  parentNodeId: node.parentNodeId,
3272
3282
  type: node.type,
3283
+ props: node.props,
3273
3284
  text: node.text,
3274
3285
  style: node.style.resolved,
3275
3286
  layout: node.layout.local,
@@ -4002,7 +4013,11 @@ var MIME_TYPES = {
4002
4013
  ".woff": "font/woff",
4003
4014
  ".woff2": "font/woff2",
4004
4015
  ".mp3": "audio/mpeg",
4005
- ".wav": "audio/wav"
4016
+ ".wav": "audio/wav",
4017
+ ".mp4": "video/mp4",
4018
+ ".m4v": "video/mp4",
4019
+ ".mov": "video/quicktime",
4020
+ ".webm": "video/webm"
4006
4021
  };
4007
4022
  var ROOT_RUNTIME_PATHS = [
4008
4023
  "/assets/",
@@ -4016,7 +4031,7 @@ var ROOT_RUNTIME_PATHS = [
4016
4031
  "/spike/",
4017
4032
  "/test-wallpaper.jpg",
4018
4033
  "/preview-sw.js",
4019
- "/sootsim.svg",
4034
+ "/peach.svg",
4020
4035
  "/bedrock-sqlite.wasm",
4021
4036
  "/bedrock-sqlite.js",
4022
4037
  "/sootsim/"
@@ -4100,7 +4115,42 @@ function serveRuntimeFile(req, res, fullPath) {
4100
4115
  res.end();
4101
4116
  return;
4102
4117
  }
4103
- import_fs3.default.createReadStream(fullPath).pipe(res);
4118
+ sendFileWithRanges(req, res, fullPath, stat.size, {});
4119
+ }
4120
+ function sendFileWithRanges(req, res, filePath, total, headers) {
4121
+ const head = (req?.method || "GET").toUpperCase() === "HEAD";
4122
+ const range = /^bytes=(\d*)-(\d*)$/.exec(req?.headers?.range || "");
4123
+ const invalid = range && range[1] && range[2] && Number(range[2]) < Number(range[1]);
4124
+ if (range && (range[1] || range[2]) && !invalid) {
4125
+ const [, startRaw, endRaw] = range;
4126
+ const start = startRaw ? Number(startRaw) : Math.max(0, total - Number(endRaw));
4127
+ const end = startRaw && endRaw ? Math.min(Number(endRaw), total - 1) : total - 1;
4128
+ if (start > end || start >= total) {
4129
+ res.writeHead(416, { ...headers, "Content-Range": `bytes */${total}` });
4130
+ res.end();
4131
+ return;
4132
+ }
4133
+ res.writeHead(206, {
4134
+ ...headers,
4135
+ "Content-Range": `bytes ${start}-${end}/${total}`,
4136
+ "Accept-Ranges": "bytes",
4137
+ "Content-Length": String(end - start + 1)
4138
+ });
4139
+ if (head) res.end();
4140
+ else streamFile(res, import_fs3.default.createReadStream(filePath, { start, end }));
4141
+ return;
4142
+ }
4143
+ res.writeHead(200, {
4144
+ ...headers,
4145
+ "Accept-Ranges": "bytes",
4146
+ "Content-Length": String(total)
4147
+ });
4148
+ if (head || total === 0) res.end();
4149
+ else streamFile(res, import_fs3.default.createReadStream(filePath, { start: 0, end: total - 1 }));
4150
+ }
4151
+ function streamFile(res, stream) {
4152
+ stream.on("error", (error) => res.destroy(error));
4153
+ stream.pipe(res);
4104
4154
  }
4105
4155
 
4106
4156
  // src/vite-plugin-one.ts
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
package/dist-lib/menu.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
@@ -404,6 +404,12 @@ function windowMenu(state) {
404
404
  checked: state.showBezels,
405
405
  action: "toggle-bezels"
406
406
  },
407
+ {
408
+ label: "Show Touches",
409
+ type: "checkbox",
410
+ checked: state.showTouches,
411
+ action: "toggle-touches"
412
+ },
407
413
  {
408
414
  label: "Show Menu Bar",
409
415
  type: "checkbox",
package/dist-lib/menu.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/public-brand.ts
4
4
  var name = "Peach";
@@ -363,6 +363,12 @@ function windowMenu(state) {
363
363
  checked: state.showBezels,
364
364
  action: "toggle-bezels"
365
365
  },
366
+ {
367
+ label: "Show Touches",
368
+ type: "checkbox",
369
+ checked: state.showTouches,
370
+ action: "toggle-touches"
371
+ },
366
372
  {
367
373
  label: "Show Menu Bar",
368
374
  type: "checkbox",
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/is-record.ts
4
4
  function isRecord(value) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-production-bundle.ts
4
4
  var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
@@ -612,7 +612,11 @@ var MIME_TYPES = {
612
612
  ".woff": "font/woff",
613
613
  ".woff2": "font/woff2",
614
614
  ".mp3": "audio/mpeg",
615
- ".wav": "audio/wav"
615
+ ".wav": "audio/wav",
616
+ ".mp4": "video/mp4",
617
+ ".m4v": "video/mp4",
618
+ ".mov": "video/quicktime",
619
+ ".webm": "video/webm"
616
620
  };
617
621
  var ROOT_RUNTIME_PATHS = [
618
622
  "/assets/",
@@ -626,7 +630,7 @@ var ROOT_RUNTIME_PATHS = [
626
630
  "/spike/",
627
631
  "/test-wallpaper.jpg",
628
632
  "/preview-sw.js",
629
- "/sootsim.svg",
633
+ "/peach.svg",
630
634
  "/bedrock-sqlite.wasm",
631
635
  "/bedrock-sqlite.js",
632
636
  "/sootsim/"
@@ -710,7 +714,42 @@ function serveRuntimeFile(req, res, fullPath) {
710
714
  res.end();
711
715
  return;
712
716
  }
713
- import_fs.default.createReadStream(fullPath).pipe(res);
717
+ sendFileWithRanges(req, res, fullPath, stat.size, {});
718
+ }
719
+ function sendFileWithRanges(req, res, filePath, total, headers) {
720
+ const head = (req?.method || "GET").toUpperCase() === "HEAD";
721
+ const range = /^bytes=(\d*)-(\d*)$/.exec(req?.headers?.range || "");
722
+ const invalid = range && range[1] && range[2] && Number(range[2]) < Number(range[1]);
723
+ if (range && (range[1] || range[2]) && !invalid) {
724
+ const [, startRaw, endRaw] = range;
725
+ const start = startRaw ? Number(startRaw) : Math.max(0, total - Number(endRaw));
726
+ const end = startRaw && endRaw ? Math.min(Number(endRaw), total - 1) : total - 1;
727
+ if (start > end || start >= total) {
728
+ res.writeHead(416, { ...headers, "Content-Range": `bytes */${total}` });
729
+ res.end();
730
+ return;
731
+ }
732
+ res.writeHead(206, {
733
+ ...headers,
734
+ "Content-Range": `bytes ${start}-${end}/${total}`,
735
+ "Accept-Ranges": "bytes",
736
+ "Content-Length": String(end - start + 1)
737
+ });
738
+ if (head) res.end();
739
+ else streamFile(res, import_fs.default.createReadStream(filePath, { start, end }));
740
+ return;
741
+ }
742
+ res.writeHead(200, {
743
+ ...headers,
744
+ "Accept-Ranges": "bytes",
745
+ "Content-Length": String(total)
746
+ });
747
+ if (head || total === 0) res.end();
748
+ else streamFile(res, import_fs.default.createReadStream(filePath, { start: 0, end: total - 1 }));
749
+ }
750
+ function streamFile(res, stream) {
751
+ stream.on("error", (error) => res.destroy(error));
752
+ stream.pipe(res);
714
753
  }
715
754
 
716
755
  // src/metro-plugin.ts
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/react-native-host-modules.ts
4
4
  var REACT_NATIVE_PASSTHROUGH_SPECIFIERS = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
@@ -47,6 +47,161 @@ __export(dev_server_scanner_exports, {
47
47
  });
48
48
  module.exports = __toCommonJS(dev_server_scanner_exports);
49
49
  var import_child_process = require("child_process");
50
+
51
+ // ../contrast/src/runtime/process-group.ts
52
+ var import_node_child_process = require("node:child_process");
53
+
54
+ // ../contrast/src/sleep.ts
55
+ function sleep(ms) {
56
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
57
+ }
58
+
59
+ // ../contrast/src/runtime/process-group.ts
60
+ var GRACEFUL_TIMEOUT_MS = 3e3;
61
+ var FORCED_EXIT_TIMEOUT_MS = 1e3;
62
+ function signalProcessGroup(child, signal) {
63
+ if (!child.pid) return;
64
+ try {
65
+ process.kill(-child.pid, signal);
66
+ } catch {
67
+ try {
68
+ process.kill(child.pid, signal);
69
+ } catch {
70
+ }
71
+ }
72
+ }
73
+ function runningPids(pids) {
74
+ return pids.filter((pid) => {
75
+ try {
76
+ process.kill(pid, 0);
77
+ return true;
78
+ } catch (error) {
79
+ return error instanceof Error && "code" in error && error.code === "EPERM";
80
+ }
81
+ });
82
+ }
83
+ function groupIsEmpty(pgid) {
84
+ try {
85
+ process.kill(-pgid, 0);
86
+ } catch (error) {
87
+ if (!(error instanceof Error && "code" in error && error.code === "EPERM"))
88
+ return true;
89
+ }
90
+ const result = (0, import_node_child_process.spawnSync)("ps", ["-A", "-o", "pgid=,stat="], {
91
+ encoding: "utf8",
92
+ stdio: ["ignore", "pipe", "pipe"]
93
+ });
94
+ if (result.error || result.status !== 0) return false;
95
+ return !result.stdout.split("\n").some((line) => {
96
+ const [group, state] = line.trim().split(/\s+/);
97
+ return Number(group) === pgid && state !== void 0 && !state.startsWith("Z");
98
+ });
99
+ }
100
+ async function waitForExit(pids, pgid, timeoutMs) {
101
+ const deadline = Date.now() + timeoutMs;
102
+ const gone = () => runningPids(pids).length === 0 && groupIsEmpty(pgid);
103
+ while (Date.now() < deadline) {
104
+ if (gone()) return true;
105
+ await sleep(50);
106
+ }
107
+ return gone();
108
+ }
109
+ async function terminateProcessGroup(child, signal = "SIGTERM", gracefulTimeoutMs = GRACEFUL_TIMEOUT_MS) {
110
+ if (!child.pid) {
111
+ try {
112
+ child.kill(signal);
113
+ } catch {
114
+ }
115
+ return;
116
+ }
117
+ signalProcessGroup(child, "SIGSTOP");
118
+ const descendants = /* @__PURE__ */ new Set();
119
+ let parents = [child.pid];
120
+ let inspectionError = null;
121
+ while (parents.length > 0) {
122
+ const result = (0, import_node_child_process.spawnSync)("pgrep", ["-P", parents.join(",")], {
123
+ encoding: "utf8",
124
+ stdio: ["ignore", "pipe", "pipe"]
125
+ });
126
+ if (result.error || result.status !== 0 && result.status !== 1) {
127
+ inspectionError = new Error(
128
+ `could not inspect descendants of processes ${parents.join(",")}: ${result.error?.message || result.stderr.trim() || `pgrep exited ${result.status}`}`
129
+ );
130
+ break;
131
+ }
132
+ const next = [];
133
+ for (const line of result.stdout.split("\n")) {
134
+ const descendantPid = Number(line.trim());
135
+ if (Number.isInteger(descendantPid) && descendantPid > 1 && !descendants.has(descendantPid)) {
136
+ descendants.add(descendantPid);
137
+ next.push(descendantPid);
138
+ try {
139
+ process.kill(descendantPid, "SIGSTOP");
140
+ } catch {
141
+ }
142
+ }
143
+ }
144
+ parents = next;
145
+ }
146
+ const pids = [child.pid, ...descendants];
147
+ const gracefulDeadline = Date.now() + gracefulTimeoutMs;
148
+ try {
149
+ process.kill(child.pid, signal);
150
+ } catch {
151
+ }
152
+ signalProcessGroup(child, "SIGCONT");
153
+ for (const pid of descendants) {
154
+ try {
155
+ process.kill(pid, "SIGCONT");
156
+ } catch {
157
+ }
158
+ }
159
+ let rootExited = child.exitCode !== null || child.signalCode !== null;
160
+ if (!rootExited) {
161
+ rootExited = await new Promise((resolveExit) => {
162
+ const onExit = () => {
163
+ clearTimeout(deadlineTimer);
164
+ resolveExit(true);
165
+ };
166
+ const deadlineTimer = setTimeout(
167
+ () => {
168
+ child.removeListener("exit", onExit);
169
+ resolveExit(false);
170
+ },
171
+ Math.max(0, gracefulDeadline - Date.now())
172
+ );
173
+ child.once("exit", onExit);
174
+ if (child.exitCode !== null || child.signalCode !== null) {
175
+ child.removeListener("exit", onExit);
176
+ onExit();
177
+ }
178
+ });
179
+ }
180
+ if (rootExited) {
181
+ signalProcessGroup(child, signal);
182
+ for (const pid of descendants) {
183
+ try {
184
+ process.kill(pid, signal);
185
+ } catch {
186
+ }
187
+ }
188
+ }
189
+ if (await waitForExit(pids, child.pid, Math.max(0, gracefulDeadline - Date.now()))) {
190
+ if (inspectionError) throw inspectionError;
191
+ return;
192
+ }
193
+ for (const pid of pids) {
194
+ try {
195
+ process.kill(pid, "SIGKILL");
196
+ } catch {
197
+ }
198
+ }
199
+ signalProcessGroup(child, "SIGKILL");
200
+ await waitForExit(pids, child.pid, FORCED_EXIT_TIMEOUT_MS);
201
+ if (inspectionError) throw inspectionError;
202
+ }
203
+
204
+ // scripts/dev-server-scanner.ts
50
205
  var import_promises2 = require("fs/promises");
51
206
  var import_http = __toESM(require("http"), 1);
52
207
  var import_net = __toESM(require("net"), 1);
@@ -522,29 +677,51 @@ function optionalDemoAppsSync() {
522
677
  }
523
678
 
524
679
  // scripts/dev-server-scanner.ts
525
- var EXEC_EXIT_GRACE_MS = 2e3;
526
- async function execGuarded(command, timeout, extra = {}) {
527
- let child;
528
- const ran = new Promise((resolve2, reject) => {
529
- child = (0, import_child_process.exec)(command, { encoding: "utf8", timeout, ...extra }, (err, stdout) => {
680
+ function execGuarded(command, timeout, extra = {}) {
681
+ const maxBuffer = extra.maxBuffer ?? 1024 * 1024;
682
+ return new Promise((resolve2, reject) => {
683
+ const child = (0, import_child_process.spawn)("/bin/sh", ["-c", command], {
684
+ detached: true,
685
+ stdio: ["ignore", "pipe", "ignore"]
686
+ });
687
+ let stdout = "";
688
+ let settled = false;
689
+ let killing = false;
690
+ const settle = (err) => {
691
+ if (settled) return;
692
+ settled = true;
693
+ clearTimeout(deadline);
530
694
  if (err) reject(err);
531
695
  else resolve2({ stdout });
696
+ };
697
+ const killThenSettle = (err) => {
698
+ if (killing) return;
699
+ killing = true;
700
+ clearTimeout(deadline);
701
+ terminateProcessGroup(child, "SIGKILL", 0).finally(() => settle(err));
702
+ };
703
+ const deadline = setTimeout(() => {
704
+ killThenSettle(
705
+ Object.assign(new Error(`\`${command}\` did not exit within ${timeout}ms`), {
706
+ killed: true
707
+ })
708
+ );
709
+ }, timeout);
710
+ child.stdout.setEncoding("utf8");
711
+ child.stdout.on("data", (chunk) => {
712
+ stdout += chunk;
713
+ if (stdout.length > maxBuffer) {
714
+ killThenSettle(new Error(`\`${command}\` exceeded maxBuffer ${maxBuffer}`));
715
+ }
716
+ });
717
+ child.on("error", (err) => {
718
+ if (!killing) settle(err);
719
+ });
720
+ child.on("close", (code) => {
721
+ if (killing) return;
722
+ settle(code === 0 ? null : new Error(`\`${command}\` exited with ${code}`));
532
723
  });
533
724
  });
534
- let deadlineTimer;
535
- const deadline = new Promise((_resolve, reject) => {
536
- deadlineTimer = setTimeout(() => {
537
- child?.kill("SIGKILL");
538
- const err = new Error(`\`${command}\` did not exit within ${timeout}ms`);
539
- Object.assign(err, { killed: true });
540
- reject(err);
541
- }, timeout + EXEC_EXIT_GRACE_MS);
542
- });
543
- try {
544
- return await Promise.race([ran, deadline]);
545
- } finally {
546
- clearTimeout(deadlineTimer);
547
- }
548
725
  }
549
726
  var TIMEOUT_MS = 250;
550
727
  var MANIFEST_TIMEOUT_MS = 1500;
@@ -579,6 +756,7 @@ async function tcpPing(port, timeout = TCP_GATE_MS) {
579
756
  function httpGet(hostname, port, path, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
580
757
  return new Promise((resolve2) => {
581
758
  let settled = false;
759
+ let killing = false;
582
760
  const settle = (value) => {
583
761
  if (settled) return;
584
762
  settled = true;
@@ -657,6 +835,21 @@ async function discoverListeningProcesses(excludePorts = []) {
657
835
  }
658
836
  async function scanListeningProcesses(excludePorts = []) {
659
837
  const excluded = new Set(excludePorts);
838
+ const listing = await listListenersShared();
839
+ if (listing.state === "could-not-look") return listing;
840
+ return {
841
+ state: "looked",
842
+ processes: listing.processes.filter((p) => acceptPort(p.port, excluded))
843
+ };
844
+ }
845
+ var inflightListing = null;
846
+ function listListenersShared() {
847
+ inflightListing ??= listListeners().finally(() => {
848
+ inflightListing = null;
849
+ });
850
+ return inflightListing;
851
+ }
852
+ async function listListeners() {
660
853
  let timedOut = false;
661
854
  try {
662
855
  const { stdout } = await execGuarded(
@@ -668,11 +861,9 @@ async function scanListeningProcesses(excludePorts = []) {
668
861
  const parts = line.trim().split(/\s+/);
669
862
  if (parts.length < 9) continue;
670
863
  const pid = Number(parts[1]);
671
- const addr = parts[8];
672
- const m = addr.match(/:(\d+)$/);
864
+ const m = parts[8].match(/:(\d+)$/);
673
865
  if (!m) continue;
674
866
  const port = Number(m[1]);
675
- if (!acceptPort(port, excluded)) continue;
676
867
  if (!seen.has(port)) seen.set(port, pid);
677
868
  }
678
869
  return {
@@ -694,7 +885,6 @@ async function scanListeningProcesses(excludePorts = []) {
694
885
  if (!portMatch) continue;
695
886
  const port = Number(portMatch[1]);
696
887
  const pid = pidMatch ? Number(pidMatch[1]) : 0;
697
- if (!acceptPort(port, excluded)) continue;
698
888
  if (!seen.has(port)) seen.set(port, pid);
699
889
  }
700
890
  return {
@@ -705,10 +895,7 @@ async function scanListeningProcesses(excludePorts = []) {
705
895
  if (wasKilledByTimeout(err)) timedOut = true;
706
896
  }
707
897
  if (timedOut) return { state: "could-not-look" };
708
- return {
709
- state: "looked",
710
- processes: FALLBACK_PORTS.filter((p) => acceptPort(p.port, excluded))
711
- };
898
+ return { state: "looked", processes: FALLBACK_PORTS };
712
899
  }
713
900
  async function discoverListeningPorts(excludePorts = []) {
714
901
  const processes = await discoverListeningProcesses(excludePorts);
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.626 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);