peachsim 0.1.638 → 0.1.640

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 (81) hide show
  1. package/cli/command-registry.ts +1 -0
  2. package/cli/commands/control.ts +75 -6
  3. package/cli/commands/swift.ts +326 -0
  4. package/cli/main.ts +11 -0
  5. package/cli/outbound-endpoints.ts +5 -1
  6. package/dist-lib/agent-daemon-client.cjs +1 -1
  7. package/dist-lib/agent-events.cjs +1 -1
  8. package/dist-lib/agent-identity.cjs +1 -1
  9. package/dist-lib/agent-sessions.cjs +1 -1
  10. package/dist-lib/attached-projects.cjs +1 -1
  11. package/dist-lib/auth/shared-session.cjs +1 -1
  12. package/dist-lib/backend-origin.cjs +1 -1
  13. package/dist-lib/beta.cjs +1 -1
  14. package/dist-lib/beta.mjs +1 -1
  15. package/dist-lib/bridge-constants.cjs +1 -1
  16. package/dist-lib/bridge-contract-input.cjs +1 -1
  17. package/dist-lib/bridge-contract-input.mjs +1 -1
  18. package/dist-lib/bridge-contract.cjs +1 -1
  19. package/dist-lib/bridge-contract.mjs +1 -1
  20. package/dist-lib/capture-contract.cjs +1 -1
  21. package/dist-lib/capture-contract.mjs +1 -1
  22. package/dist-lib/cli-constants.cjs +1 -1
  23. package/dist-lib/cloud-contract.cjs +1 -1
  24. package/dist-lib/cloud-contract.mjs +1 -1
  25. package/dist-lib/cloud-test-args.cjs +1 -1
  26. package/dist-lib/cloud-test-args.mjs +1 -1
  27. package/dist-lib/cloud.cjs +1 -1
  28. package/dist-lib/cloud.mjs +1 -1
  29. package/dist-lib/config.cjs +1 -1
  30. package/dist-lib/detox/index.cjs +1 -1
  31. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  32. package/dist-lib/home-paths.cjs +1 -1
  33. package/dist-lib/host/bridge-host.cjs +1 -1
  34. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  35. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  36. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  37. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  38. package/dist-lib/host/websocket-proxy.cjs +1 -1
  39. package/dist-lib/index.cjs +13 -4
  40. package/dist-lib/jump-to-source-babel.cjs +1 -1
  41. package/dist-lib/jump-to-source-native.cjs +1 -1
  42. package/dist-lib/kotlin-host.cjs +1 -1
  43. package/dist-lib/menu.cjs +1 -1
  44. package/dist-lib/menu.mjs +1 -1
  45. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  46. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  47. package/dist-lib/metro-production-bundle.cjs +1 -1
  48. package/dist-lib/metro-production-bundle.mjs +1 -1
  49. package/dist-lib/metro.cjs +1 -1
  50. package/dist-lib/native-app-identity.cjs +1 -1
  51. package/dist-lib/native-app-identity.mjs +1 -1
  52. package/dist-lib/profiles.cjs +1 -1
  53. package/dist-lib/public-brand.cjs +1 -1
  54. package/dist-lib/react-native-host-modules.cjs +1 -1
  55. package/dist-lib/react-native-host-modules.mjs +1 -1
  56. package/dist-lib/render-mode.cjs +1 -1
  57. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  58. package/dist-lib/sdk.cjs +1 -1
  59. package/dist-lib/sdk.mjs +1 -1
  60. package/dist-lib/skills.cjs +389 -232
  61. package/dist-lib/swift-compile.cjs +1 -1
  62. package/dist-lib/swift-compile.mjs +1 -1
  63. package/dist-lib/swift-package.cjs +1 -1
  64. package/dist-lib/swift-package.mjs +1 -1
  65. package/dist-lib/swift-submit.cjs +89 -8
  66. package/dist-lib/swift-submit.mjs +78 -8
  67. package/dist-lib/swift.cjs +16 -33308
  68. package/dist-lib/vite-swift.cjs +422 -247
  69. package/dist-lib/vite.cjs +13 -4
  70. package/package.json +1 -1
  71. package/skills/contrast/SKILL.md +1 -0
  72. package/src/assetCatalogContract.ts +16 -0
  73. package/src/dot-env.ts +148 -0
  74. package/src/metro-runtime-source.ts +15 -0
  75. package/src/swift-guest.ts +51 -0
  76. package/src/swift-host.ts +900 -0
  77. package/src/swift-submit.ts +97 -6
  78. package/src/swift.ts +36 -8
  79. package/src/swiftContract.ts +52 -0
  80. package/src/vite-plugin-one.ts +4 -3
  81. package/src/vite-plugin-swift.ts +43 -755
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.638 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.640 | (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;
@@ -4447,6 +4447,8 @@ var init_supported_native_packages = __esm({
4447
4447
  // seam
4448
4448
  "@metamask/native-utils",
4449
4449
  // seam+coverage
4450
+ "@metamask/react-native-search-api",
4451
+ // seam+coverage
4450
4452
  "@metamask/react-native-webview",
4451
4453
  // seam
4452
4454
  "@mhpdev/react-native-haptics",
@@ -4807,6 +4809,8 @@ var init_supported_native_packages = __esm({
4807
4809
  // coverage
4808
4810
  "react-native-auto-skeleton",
4809
4811
  // seam
4812
+ "react-native-background-fetch",
4813
+ // seam+coverage
4810
4814
  "react-native-background-geolocation",
4811
4815
  // seam
4812
4816
  "react-native-background-timer",
@@ -4850,7 +4854,7 @@ var init_supported_native_packages = __esm({
4850
4854
  "react-native-compressor",
4851
4855
  // seam
4852
4856
  "react-native-config",
4853
- // coverage
4857
+ // seam+coverage
4854
4858
  "react-native-config-reader",
4855
4859
  // seam
4856
4860
  "react-native-contacts",
@@ -5001,6 +5005,8 @@ var init_supported_native_packages = __esm({
5001
5005
  // seam
5002
5006
  "react-native-nitro-buffer",
5003
5007
  // seam+coverage
5008
+ "react-native-nitro-cookies",
5009
+ // seam
5004
5010
  "react-native-nitro-device-info",
5005
5011
  // seam
5006
5012
  "react-native-nitro-fetch",
@@ -5622,7 +5628,7 @@ var init_registry = __esm({
5622
5628
  usage: 2,
5623
5629
  importance: 2,
5624
5630
  estimate: 0.98,
5625
- rationale: "Initial detents, sheet geometry, corner radius, grabber, undimmed range, dismissal, numeric-detent dragging, nearest settling, content interception, larger-detent scrolling, selected geometry and dimming, collapse callbacks and guarded dismissal are implemented. Fit-to-content and non-numeric detent modes and expandsWhenScrolledToEdge are not."
5631
+ rationale: "Initial detents, sheet geometry, corner radius, grabber, undimmed range, dismissal, numeric-detent dragging, nearest settling, content interception, larger-detent scrolling, selected geometry and dimming, collapse callbacks and guarded dismissal are implemented. Fit-to-content and legacy detents, edge-scroll expansion, velocity-driven settling and pointer cancellation are implemented. The remaining native fidelity deductions are the over-drag return spring and sheet-to-scroll handoff beyond the largest detent."
5626
5632
  },
5627
5633
  {
5628
5634
  id: "tabs-freeze-and-overlay",
@@ -5635,7 +5641,7 @@ var init_registry = __esm({
5635
5641
  capabilityCoverage: CAPABILITY_INVENTORY_PACKAGE_COVERAGE["react-native-screens"],
5636
5642
  note: "screen container, stack, header and search chrome, native tabs, navigation props",
5637
5643
  working: "Screen, ScreenContainer, ScreenStack, enableFreeze/freezeEnabled with delayed React freeze, Android modal/pageSheet push fallback, Android statusBarHidden/statusBarStyle/navigationBarHidden traits, push/pop slide, replaceAnimation push/pop direction, zoom/fade/fade_from_bottom transitions, edge swipe-back, parallax behind-screen, large title collapse, header (back button, inline/large title, custom left/center/right/search bar subviews, native header bar buttons/menus/badges, crossfade), formSheet custom initial detents/corner radius/grabber/undimmed range, numeric-detent dragging and settling, eligible content scrolling, detent callbacks, guarded dismissal and stacked sheets, useHeaderHeight, useTransitionProgress, FullWindowOverlay, SearchBar with stacked and iOS 26 automatic/inline/integrated/integratedCentered/integratedButton toolbar placement, Tabs.Host/Screen with detached system Search on the Duo trailing column (inline fifth tab on iPhone per the iOS 27 oracle), regular/inline bottomAccessory, tabBarMinimizeBehavior never/automatic/onScrollDown/onScrollUp, active-tab restoration on reselect, lifecycle callbacks (onAppear/onDisappear/onWillAppear/onWillDisappear), onDismissed",
5638
- missing: "native velocity and spring fidelity, pointer cancellation, fit-to-content and non-numeric detents, numeric 1.0 native geometry, and expandsWhenScrolledToEdge remain incomplete or unverified"
5644
+ missing: "over-drag return spring fidelity, sheet-to-scroll handoff creep beyond the largest detent"
5639
5645
  }
5640
5646
  ]
5641
5647
  },
@@ -6882,10 +6888,10 @@ var init_registry = __esm({
6882
6888
  versions: [
6883
6889
  {
6884
6890
  range: ">=4.4.0",
6885
- coverage: 0.75,
6886
- note: "static BackgroundFetch API with status constants, task scheduling state, callback wiring, and finish/start/stop semantics",
6887
- working: "STATUS_* and NETWORK_TYPE_* constants, configure, scheduleTask, registerHeadlessTask, start, stop, finish, status promise/callback forms, fetch/timeout callback dispatch",
6888
- missing: "actual iOS/Android background execution, BGTaskScheduler/TSBackgroundFetch wakeups, headless JS launch while suspended"
6891
+ coverage: 0.9,
6892
+ note: "native-seam: index.js runs unchanged from the bundle; only the RNBackgroundFetch module is ours, answering as TSBackgroundFetch does on the iOS simulator: configure/start/status resolve UIBackgroundRefreshStatusAvailable (2), scheduleTask rejects because BGTaskScheduler is unavailable (or the identifier is not in BGTaskSchedulerPermittedIdentifiers), and no fetch event fires, as none fires on the simulator without Xcode",
6893
+ working: "configure, start, stop, finish, status (promise and callback forms), scheduleTask rejection with the simulator message, the real STATUS_* and NETWORK_TYPE_* constants and NativeEventEmitter wiring",
6894
+ missing: "Xcode's Simulate Background Fetch has no Peach equivalent; Android mode answers as iOS (headless tasks, JobScheduler)"
6889
6895
  }
6890
6896
  ]
6891
6897
  },
@@ -7593,9 +7599,10 @@ var init_registry = __esm({
7593
7599
  versions: [
7594
7600
  {
7595
7601
  range: ">=1.0.0",
7596
- coverage: 1,
7597
- note: "proxy reads all keys from process.env; supports any key via Proxy ownKeys/get",
7598
- working: "Config proxy (all keys via process.env), default and named Config export"
7602
+ coverage: 0.9,
7603
+ note: "native-seam: index.js runs unchanged from the bundle; only RNCConfigModule is ours. `peach open --load-env` reads the project's env file as the iOS build phase does (ENVFILE, .env fallback, the same line pattern and Objective-C string decoding) and getConfig() answers with it; without the flag it answers as a build that found no env file, with an empty config and a console warning",
7604
+ working: "Config and default exports from the real package, getConfig() in the native shape, ENVFILE selection and build-setting expansion, invalid-entry failure",
7605
+ missing: "Android build flavors (envConfigFiles); a cloud preview carries no env file"
7599
7606
  }
7600
7607
  ]
7601
7608
  },
@@ -7976,10 +7983,10 @@ var init_registry = __esm({
7976
7983
  versions: [
7977
7984
  {
7978
7985
  range: ">=1.0.0",
7979
- coverage: 0.75,
7980
- note: "iOS Search API wrapper with in-memory Spotlight/app-history state and native event helper surface",
7981
- working: "default SearchApi instance, spotlight/app-history event listener helpers, indexSpotlightItem(s), delete by identifiers/domains, deleteAll, indexAppHistoryItem",
7982
- missing: "real CoreSpotlight indexing, NSUserActivity handoff/continueUserActivity delivery from iOS, and public index integration"
7986
+ coverage: 0.9,
7987
+ note: "native-seam: index.js runs unchanged from the bundle; only SearchApiManager is ours, resolving every index, delete and user-activity call as CoreSpotlight completes them on the iOS simulator",
7988
+ working: "indexSpotlightItem(s), deleteSpotlightItemsWithIdentifiers/InDomains, deleteAllSpotlightItems, indexAppHistoryItem, and the real event emitter wiring",
7989
+ missing: "Peach has no Spotlight search or Handoff, so spotlightSearchItemTapped and appHistorySearchItemTapped never fire"
7983
7990
  }
7984
7991
  ]
7985
7992
  },
@@ -9126,11 +9133,11 @@ var init_registry = __esm({
9126
9133
  stubType: "native",
9127
9134
  versions: [
9128
9135
  {
9129
- range: ">=1.0.0",
9136
+ range: ">=1.2.0",
9130
9137
  coverage: 0.85,
9131
- note: "document.cookie backed; get/set/remove/clearAll fully functional in browser",
9132
- working: "get, getAll, set (with path/domain/expires/secure options), remove (with path/domain options), clearAll, Cookie interface",
9133
- missing: "httpOnly attribute (not settable via document.cookie by design), SameSite option, URL-scoped cookie reading (always reads all document.cookie)"
9138
+ note: "native-seam: the package's JS runs unchanged from the bundle; only the NitroCookies hybrid object is ours, following NitroCookies.swift over the app's one cookie jar (shared with @react-native-cookies/cookies): HTTPCookieStorage and a separate WebKit store, the Swift domain-only matching, fractional-seconds-only expiry parsing, the 400-day expiry cap, httpOnly cookies gaining a dotted domain, and the NSError descriptions Nitro hands to JS",
9139
+ working: "every sync and async method: get/getSync, set/setSync, setMany/setManySync, getCookieHeader/getCookieHeaderSync, setFromResponse/setFromResponseSync, clearByName/clearByNameSync, getAll, clearAll, flush, removeSessionCookies, with useWebKit",
9140
+ missing: "getFromResponse makes the request but returns no cookies (a browser fetch hides Set-Cookie); the jar's iteration order for getAll and the Cookie header follows insertion, where HTTPCookieStorage orders by its own rule; the jar is not the one fetch sends"
9134
9141
  }
9135
9142
  ]
9136
9143
  },
@@ -9210,22 +9217,6 @@ var init_registry = __esm({
9210
9217
  }
9211
9218
  ]
9212
9219
  },
9213
- "@welldone-software/why-did-you-render": {
9214
- category: "Device & Platform",
9215
- // pure JS (no native code), but deliberately intercepted: see the note
9216
- // below. do not "restore the real package" — it patches React internals
9217
- // and takes the bridged React down with it.
9218
- stubType: "native",
9219
- shipsNativeCode: false,
9220
- versions: [
9221
- {
9222
- range: ">=8.0.0",
9223
- coverage: 0,
9224
- note: "the reference iOS simulator performs this package's behavior. Peach currently provides only this unsupported shape: intentional noop : wdyr mutates React internals and crashes sootsim's bridged React. imports and API shape receive no behavior credit",
9225
- working: "default export callable as wdyr(React, opts) without error"
9226
- }
9227
- ]
9228
- },
9229
9220
  "react-native-pure-jwt": {
9230
9221
  category: "Auth & Payments",
9231
9222
  stubType: "native",
@@ -19440,9 +19431,9 @@ function safeChildPlistName(value) {
19440
19431
  name: file.toLowerCase().endsWith(".plist") ? file : `${file}.plist`
19441
19432
  };
19442
19433
  }
19443
- async function readableSettingsBundle(path22) {
19434
+ async function readableSettingsBundle(path23) {
19444
19435
  try {
19445
- const root = await (0, import_promises.stat)((0, import_node_path.join)(path22, "Root.plist"));
19436
+ const root = await (0, import_promises.stat)((0, import_node_path.join)(path23, "Root.plist"));
19446
19437
  return root.isFile() && root.size <= MAX_PLIST_BYTES;
19447
19438
  } catch {
19448
19439
  return false;
@@ -19487,11 +19478,11 @@ async function settingsBundleFingerprint(bundlePath) {
19487
19478
  return null;
19488
19479
  }
19489
19480
  }
19490
- async function parsePlistRecord(path22) {
19481
+ async function parsePlistRecord(path23) {
19491
19482
  try {
19492
- const metadata = await (0, import_promises.stat)(path22);
19483
+ const metadata = await (0, import_promises.stat)(path23);
19493
19484
  if (!metadata.isFile() || metadata.size > MAX_PLIST_BYTES) return null;
19494
- const source = await (0, import_promises.readFile)(path22, "utf8");
19485
+ const source = await (0, import_promises.readFile)(path23, "utf8");
19495
19486
  const parsed = parsePlistSource(source);
19496
19487
  return isRecord2(parsed) ? parsed : null;
19497
19488
  } catch {
@@ -19815,7 +19806,7 @@ async function tcpPing(port, timeout = TCP_GATE_MS) {
19815
19806
  return null;
19816
19807
  }
19817
19808
  }
19818
- function httpGet(hostname, port, path22, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
19809
+ function httpGet(hostname, port, path23, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
19819
19810
  return new Promise((resolve9) => {
19820
19811
  let settled = false;
19821
19812
  let killing = false;
@@ -19824,7 +19815,7 @@ function httpGet(hostname, port, path22, method = "GET", timeout = TIMEOUT_MS, h
19824
19815
  settled = true;
19825
19816
  resolve9(value);
19826
19817
  };
19827
- const req = import_http.default.request({ hostname, port, path: path22, method, headers }, (res) => {
19818
+ const req = import_http.default.request({ hostname, port, path: path23, method, headers }, (res) => {
19828
19819
  let body = "";
19829
19820
  res.on("data", (c) => body += c.toString());
19830
19821
  const contentType = (() => {
@@ -21702,8 +21693,8 @@ function sourceEntryPath() {
21702
21693
  return null;
21703
21694
  }
21704
21695
  }
21705
- function isCliEntry(path22) {
21706
- const name2 = (0, import_node_path8.basename)(path22);
21696
+ function isCliEntry(path23) {
21697
+ const name2 = (0, import_node_path8.basename)(path23);
21707
21698
  return name2 === "bin.ts" || name2 === "bin.js";
21708
21699
  }
21709
21700
  function rnxSelfInvocation() {
@@ -26541,8 +26532,8 @@ function metroIosAssetDestRelatives(httpServerLocation, filename) {
26541
26532
  }
26542
26533
  const out = [];
26543
26534
  for (const dir of dirs) {
26544
- const path22 = `${dir}/${filename}`;
26545
- if (!out.includes(path22)) out.push(path22);
26535
+ const path23 = `${dir}/${filename}`;
26536
+ if (!out.includes(path23)) out.push(path23);
26546
26537
  }
26547
26538
  if (!out.includes(filename)) out.push(filename);
26548
26539
  return out;
@@ -27492,13 +27483,13 @@ function readFactoryStringLiterals(factory) {
27492
27483
  return literals;
27493
27484
  }
27494
27485
  function normalizeFingerprintLiteral(literal) {
27495
- const path22 = literal.replaceAll("\\\\", "/").replaceAll("\\/", "/");
27486
+ const path23 = literal.replaceAll("\\\\", "/").replaceAll("\\/", "/");
27496
27487
  const nodeModulesMarker = "/node_modules/";
27497
- const nodeModulesIndex = path22.lastIndexOf(nodeModulesMarker);
27488
+ const nodeModulesIndex = path23.lastIndexOf(nodeModulesMarker);
27498
27489
  if (nodeModulesIndex >= 0) {
27499
- return `node_modules/${path22.slice(nodeModulesIndex + nodeModulesMarker.length)}`;
27490
+ return `node_modules/${path23.slice(nodeModulesIndex + nodeModulesMarker.length)}`;
27500
27491
  }
27501
- if ((path22.startsWith("/") || /^[A-Za-z]:\//.test(path22) || path22.startsWith("file:/")) && /\.[cm]?[jt]sx?(?::\d+(?::\d+)?)?$/.test(path22)) {
27492
+ if ((path23.startsWith("/") || /^[A-Za-z]:\//.test(path23) || path23.startsWith("file:/")) && /\.[cm]?[jt]sx?(?::\d+(?::\d+)?)?$/.test(path23)) {
27502
27493
  return null;
27503
27494
  }
27504
27495
  return literal;
@@ -27736,10 +27727,10 @@ function templateAssignment(template, embeddings) {
27736
27727
  if (existingNode !== void 0 && existingNode !== nodeIndex) return null;
27737
27728
  nodesByModuleId.set(moduleId, nodeIndex);
27738
27729
  const node = template.nodes[nodeIndex];
27739
- const path22 = node.landmarkPath ?? `node_modules/${template.packageName}/__rnx_inferred__/${moduleId}.js`;
27730
+ const path23 = node.landmarkPath ?? `node_modules/${template.packageName}/__rnx_inferred__/${moduleId}.js`;
27740
27731
  const existingPath = pathsByModuleId.get(moduleId);
27741
- if (existingPath !== void 0 && existingPath !== path22) return null;
27742
- pathsByModuleId.set(moduleId, path22);
27732
+ if (existingPath !== void 0 && existingPath !== path23) return null;
27733
+ pathsByModuleId.set(moduleId, path23);
27743
27734
  }
27744
27735
  }
27745
27736
  return {
@@ -27901,14 +27892,14 @@ async function inferMetroModuleIdentity(args) {
27901
27892
  const packageClaims = claimsByPackage.values().next().value;
27902
27893
  if (!packageClaims) continue;
27903
27894
  const exactClaims = packageClaims.filter((assignment2) => {
27904
- const path22 = assignment2.pathsByModuleId.get(moduleId);
27905
- return path22 !== void 0 && !path22.includes("/__rnx_inferred__/");
27895
+ const path23 = assignment2.pathsByModuleId.get(moduleId);
27896
+ return path23 !== void 0 && !path23.includes("/__rnx_inferred__/");
27906
27897
  });
27907
27898
  const preferredClaims = exactClaims.length > 0 ? exactClaims : packageClaims;
27908
27899
  const routes = new Set(
27909
27900
  preferredClaims.flatMap((assignment2) => {
27910
- const path22 = assignment2.pathsByModuleId.get(moduleId);
27911
- return path22 ? [metroFingerprintModuleClaimRoute(assignment2.packageName, path22)] : [];
27901
+ const path23 = assignment2.pathsByModuleId.get(moduleId);
27902
+ return path23 ? [metroFingerprintModuleClaimRoute(assignment2.packageName, path23)] : [];
27912
27903
  })
27913
27904
  );
27914
27905
  if (routes.size !== 1) continue;
@@ -28229,11 +28220,11 @@ function detectProject(dir) {
28229
28220
  devCommand: typeof devCommand === "string" ? devCommand : null
28230
28221
  };
28231
28222
  }
28232
- function readProjectMetadata(path22) {
28223
+ function readProjectMetadata(path23) {
28233
28224
  try {
28234
- const info = (0, import_node_fs15.lstatSync)(path22);
28225
+ const info = (0, import_node_fs15.lstatSync)(path23);
28235
28226
  if (!info.isFile() || info.size > 4 * 1024 * 1024) return null;
28236
- return (0, import_node_fs15.readFileSync)(path22, "utf8");
28227
+ return (0, import_node_fs15.readFileSync)(path23, "utf8");
28237
28228
  } catch {
28238
28229
  return null;
28239
28230
  }
@@ -28242,8 +28233,8 @@ function metadataValue(value, key) {
28242
28233
  if (!value || typeof value !== "object") return void 0;
28243
28234
  return Object.getOwnPropertyDescriptor(value, key)?.value;
28244
28235
  }
28245
- function readProjectJson(path22) {
28246
- const text = readProjectMetadata(path22);
28236
+ function readProjectJson(path23) {
28237
+ const text = readProjectMetadata(path23);
28247
28238
  if (text === null) return null;
28248
28239
  try {
28249
28240
  const value = JSON.parse(text);
@@ -31985,11 +31976,11 @@ async function evalInBridge(bridge, code, opts = {}) {
31985
31976
  if (opts.acquireLock) payload.acquireLock = true;
31986
31977
  return bridge.send(payload);
31987
31978
  }
31988
- async function callInBridge(bridge, path22, ...args) {
31989
- return bridge.send({ type: "call", path: path22, args });
31979
+ async function callInBridge(bridge, path23, ...args) {
31980
+ return bridge.send({ type: "call", path: path23, args });
31990
31981
  }
31991
- async function callInBridgeWrite(bridge, path22, ...args) {
31992
- return bridge.send({ type: "call", path: path22, args, acquireLock: true });
31982
+ async function callInBridgeWrite(bridge, path23, ...args) {
31983
+ return bridge.send({ type: "call", path: path23, args, acquireLock: true });
31993
31984
  }
31994
31985
  var import_node_fs18, import_node_path19, import_ws2, PEACH_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError, BRIDGE_VALUE_FLAGS_TABLE, BRIDGE_VALUE_FLAGS;
31995
31986
  var init_ws_bridge = __esm({
@@ -32124,8 +32115,8 @@ function getDefaultPortForProtocol(protocol) {
32124
32115
  return protocol === "https:" ? 443 : 80;
32125
32116
  }
32126
32117
  function isBaseServerUrl(url) {
32127
- const path22 = url.pathname || "/";
32128
- return (path22 === "/" || path22 === "") && !url.search && !url.hash;
32118
+ const path23 = url.pathname || "/";
32119
+ return (path23 === "/" || path23 === "") && !url.search && !url.hash;
32129
32120
  }
32130
32121
  async function resolvePortViaServerScan(port) {
32131
32122
  try {
@@ -32287,6 +32278,124 @@ var init_dev_bundle_resolution = __esm({
32287
32278
  }
32288
32279
  });
32289
32280
 
32281
+ // src/dot-env.ts
32282
+ function selectEnvFile(env, notices) {
32283
+ if (import_node_fs19.default.existsSync("/tmp/envfile")) {
32284
+ return { name: import_node_fs19.default.readFileSync("/tmp/envfile", "utf8").trim(), fromEnvfile: false };
32285
+ }
32286
+ const requested = env.ENVFILE;
32287
+ if (!requested) return { name: DEFAULT_ENV_FILE, fromEnvfile: false };
32288
+ const unset = [];
32289
+ const name2 = requested.replace(BUILD_SETTING_PATTERN, (_, braced, bare) => {
32290
+ const variable = braced ?? bare;
32291
+ const value = env[variable];
32292
+ if (!value) unset.push(variable);
32293
+ return value ?? "";
32294
+ });
32295
+ notices.push(`ENVFILE=${requested}`);
32296
+ if (name2 !== requested) notices.push(` expands to ${name2}`);
32297
+ if (unset.length) {
32298
+ notices.push(
32299
+ ` note: ${[...new Set(unset)].join(", ")} resolved to nothing. Outside Xcode these build settings are not set - pass the file name directly, or run the build through Xcode.`
32300
+ );
32301
+ }
32302
+ return { name: name2, fromEnvfile: true };
32303
+ }
32304
+ function decodeObjCStringLiteral(source) {
32305
+ let out = "";
32306
+ for (let i = 0; i < source.length; i++) {
32307
+ const char = source[i];
32308
+ if (char === '"') throw new Error(`GeneratedDotEnv.m does not compile: ${source}`);
32309
+ if (char !== "\\") {
32310
+ out += char;
32311
+ continue;
32312
+ }
32313
+ const next = source[++i];
32314
+ if (next === void 0)
32315
+ throw new Error(`GeneratedDotEnv.m does not compile: ${source}`);
32316
+ const simple = {
32317
+ n: "\n",
32318
+ t: " ",
32319
+ r: "\r",
32320
+ a: "\x07",
32321
+ b: "\b",
32322
+ f: "\f",
32323
+ v: "\v",
32324
+ e: "\x1B"
32325
+ };
32326
+ if (next in simple) out += simple[next];
32327
+ else if (/[0-7]/.test(next)) {
32328
+ const digits = /^[0-7]{1,3}/.exec(source.slice(i))[0];
32329
+ out += String.fromCodePoint(Number.parseInt(digits, 8));
32330
+ i += digits.length - 1;
32331
+ } else if (next === "x" || next === "u" || next === "U") {
32332
+ const digits = next === "x" ? /^[0-9a-fA-F]+/.exec(source.slice(i + 1))?.[0] ?? "" : source.slice(i + 1, i + 1 + (next === "u" ? 4 : 8));
32333
+ const code = Number.parseInt(digits, 16);
32334
+ if (!/^[0-9a-fA-F]+$/.test(digits) || code > (next === "x" ? 255 : 1114111)) {
32335
+ throw new Error(`GeneratedDotEnv.m does not compile: ${source}`);
32336
+ }
32337
+ out += String.fromCodePoint(code);
32338
+ i += digits.length;
32339
+ } else out += next;
32340
+ }
32341
+ return out;
32342
+ }
32343
+ function readProjectDotEnv(envsRoot, env = process.env) {
32344
+ const notices = [];
32345
+ const selection = selectEnvFile(env, notices);
32346
+ const requestedPaths = [
32347
+ import_node_path20.default.resolve(import_node_path20.default.join(envsRoot, selection.name)),
32348
+ import_node_path20.default.resolve(selection.name)
32349
+ ];
32350
+ const candidates = [
32351
+ ...requestedPaths,
32352
+ import_node_path20.default.resolve(import_node_path20.default.join(envsRoot, DEFAULT_ENV_FILE))
32353
+ ];
32354
+ if (!import_node_fs19.default.existsSync(candidates[2])) candidates.push(import_node_path20.default.resolve(DEFAULT_ENV_FILE));
32355
+ const tried = [...new Set(candidates)];
32356
+ const resolved = tried.find((candidate) => import_node_fs19.default.existsSync(candidate));
32357
+ if (!resolved) {
32358
+ notices.push(
32359
+ "*** Missing .env file ****",
32360
+ "Tried, in order:",
32361
+ ...tried.map((candidate) => ` - ${candidate}`),
32362
+ "The build will succeed and Config will be empty at runtime."
32363
+ );
32364
+ return { config: {}, notices };
32365
+ }
32366
+ if (selection.fromEnvfile && !requestedPaths.includes(resolved)) {
32367
+ notices.push(
32368
+ "*** ENVFILE was set, but that file is missing ",
32369
+ `Asked for: ${selection.name}`,
32370
+ "Not found at:",
32371
+ ...requestedPaths.map((candidate) => ` - ${candidate}`),
32372
+ `Falling back to: ${resolved}`
32373
+ );
32374
+ }
32375
+ const config = {};
32376
+ for (const line of import_node_fs19.default.readFileSync(resolved, "utf8").split("\n")) {
32377
+ if (!line.trim()) continue;
32378
+ if (/^[ \t\n\v\f\r]*#/.test(line)) continue;
32379
+ const match = DOTENV_PATTERN.exec(line);
32380
+ if (!match?.groups) throw new Error(INVALID_ENTRY);
32381
+ const literal = (match.groups.val ?? "").replaceAll('"', '\\"').replace(/(\r\n|\n|\r)$/, "");
32382
+ config[match.groups.key] = decodeObjCStringLiteral(literal);
32383
+ }
32384
+ return { config, notices };
32385
+ }
32386
+ var import_node_fs19, import_node_path20, DEFAULT_ENV_FILE, DOTENV_PATTERN, BUILD_SETTING_PATTERN, INVALID_ENTRY;
32387
+ var init_dot_env = __esm({
32388
+ "src/dot-env.ts"() {
32389
+ "use strict";
32390
+ import_node_fs19 = __toESM(require("node:fs"), 1);
32391
+ import_node_path20 = __toESM(require("node:path"), 1);
32392
+ DEFAULT_ENV_FILE = ".env";
32393
+ DOTENV_PATTERN = /^(?:export[ \t\n\v\f\r]+|)(?<key>[\p{Alphabetic}\p{Nd}_]+)[ \t\n\v\f\r]*=[ \t\n\v\f\r]*((?<quote>["'])?(?<val>[^\n]*?[^\\\n])\k<quote>?|)$/u;
32394
+ BUILD_SETTING_PATTERN = /\$[({]([A-Za-z_][A-Za-z0-9_]*)[)}]|\$([A-Za-z_][A-Za-z0-9_]*)/g;
32395
+ INVALID_ENTRY = "Invalid entry in .env file. Please verify your .env file is correctly formatted.";
32396
+ }
32397
+ });
32398
+
32290
32399
  // src/flow-timeout-scale.ts
32291
32400
  function flowTimeoutScale() {
32292
32401
  return Math.min(10, Math.max(1, Number(process.env[FLOW_TIMEOUT_SCALE_ENV]) || 1));
@@ -32308,9 +32417,9 @@ function readNativeFirebaseConfig(projectRoot) {
32308
32417
  "app.config.ts",
32309
32418
  "app.config.cjs",
32310
32419
  "app.config.mjs"
32311
- ].some((name2) => import_node_fs19.default.existsSync(import_node_path20.default.join(projectRoot, name2))))
32420
+ ].some((name2) => import_node_fs20.default.existsSync(import_node_path21.default.join(projectRoot, name2))))
32312
32421
  return result;
32313
- const require5 = (0, import_node_module.createRequire)(import_node_path20.default.join(projectRoot, "package.json"));
32422
+ const require5 = (0, import_node_module.createRequire)(import_node_path21.default.join(projectRoot, "package.json"));
32314
32423
  const configModule = require5("@expo/config");
32315
32424
  if (!isRecord(configModule) || typeof configModule.getConfig !== "function") {
32316
32425
  throw new Error("Expo native configuration is unavailable.");
@@ -32324,8 +32433,8 @@ function readNativeFirebaseConfig(projectRoot) {
32324
32433
  const native = config[platform];
32325
32434
  if (!isRecord(native) || typeof native.googleServicesFile !== "string") continue;
32326
32435
  try {
32327
- const source = import_node_fs19.default.readFileSync(
32328
- import_node_path20.default.resolve(projectRoot, native.googleServicesFile),
32436
+ const source = import_node_fs20.default.readFileSync(
32437
+ import_node_path21.default.resolve(projectRoot, native.googleServicesFile),
32329
32438
  "utf8"
32330
32439
  );
32331
32440
  const parsed = platform === "ios" ? parsePlistSource(source) : JSON.parse(source);
@@ -32377,13 +32486,13 @@ function readNativeFirebaseConfig(projectRoot) {
32377
32486
  }
32378
32487
  return result;
32379
32488
  }
32380
- var import_node_fs19, import_node_module, import_node_path20;
32489
+ var import_node_fs20, import_node_module, import_node_path21;
32381
32490
  var init_native_firebase_config = __esm({
32382
32491
  "src/native-firebase-config.ts"() {
32383
32492
  "use strict";
32384
- import_node_fs19 = __toESM(require("node:fs"), 1);
32493
+ import_node_fs20 = __toESM(require("node:fs"), 1);
32385
32494
  import_node_module = require("node:module");
32386
- import_node_path20 = __toESM(require("node:path"), 1);
32495
+ import_node_path21 = __toESM(require("node:path"), 1);
32387
32496
  init_is_record();
32388
32497
  init_plist();
32389
32498
  }
@@ -32547,7 +32656,7 @@ function colorsetValue(color) {
32547
32656
  }
32548
32657
  function enumerateBundleColors(paths, readText2) {
32549
32658
  const table = {};
32550
- for (const contentsPath of paths.filter((path22) => baseOf(path22) === "Contents.json")) {
32659
+ for (const contentsPath of paths.filter((path23) => baseOf(path23) === "Contents.json")) {
32551
32660
  const name2 = catalogEntryName(contentsPath, ".colorset", readText2);
32552
32661
  if (name2 === null || name2 in table) continue;
32553
32662
  let colors;
@@ -32595,59 +32704,59 @@ var init_bundleAssets = __esm({
32595
32704
  // cli/app-catalog.ts
32596
32705
  function listDir(dir) {
32597
32706
  try {
32598
- return import_node_fs20.default.readdirSync(dir, { withFileTypes: true });
32707
+ return import_node_fs21.default.readdirSync(dir, { withFileTypes: true });
32599
32708
  } catch {
32600
32709
  return [];
32601
32710
  }
32602
32711
  }
32603
32712
  function readText(file) {
32604
32713
  try {
32605
- return import_node_fs20.default.readFileSync(file, "utf8");
32714
+ return import_node_fs21.default.readFileSync(file, "utf8");
32606
32715
  } catch {
32607
32716
  return null;
32608
32717
  }
32609
32718
  }
32610
32719
  function catalogFiles(dir, found) {
32611
32720
  for (const entry of listDir(dir)) {
32612
- const full = import_node_path21.default.join(dir, entry.name);
32721
+ const full = import_node_path22.default.join(dir, entry.name);
32613
32722
  if (entry.isDirectory()) catalogFiles(full, found);
32614
32723
  else if (entry.name === "Contents.json") found.push(full);
32615
32724
  }
32616
32725
  }
32617
32726
  function discoverAppCatalog(appDir) {
32618
- const iosDir = import_node_path21.default.join(appDir, "ios");
32727
+ const iosDir = import_node_path22.default.join(appDir, "ios");
32619
32728
  const target = listDir(iosDir).find(
32620
- (entry) => entry.isDirectory() && entry.name !== "Pods" && listDir(import_node_path21.default.join(iosDir, entry.name)).some(
32729
+ (entry) => entry.isDirectory() && entry.name !== "Pods" && listDir(import_node_path22.default.join(iosDir, entry.name)).some(
32621
32730
  (file) => /^AppDelegate\.(swift|mm|m)$/.test(file.name)
32622
32731
  )
32623
32732
  );
32624
32733
  if (!target) return null;
32625
- const targetDir = import_node_path21.default.join(iosDir, target.name);
32734
+ const targetDir = import_node_path22.default.join(iosDir, target.name);
32626
32735
  const files = [];
32627
32736
  for (const entry of listDir(targetDir)) {
32628
32737
  if (entry.isDirectory() && entry.name.endsWith(".xcassets"))
32629
- catalogFiles(import_node_path21.default.join(targetDir, entry.name), files);
32738
+ catalogFiles(import_node_path22.default.join(targetDir, entry.name), files);
32630
32739
  }
32631
32740
  const relative2 = files.map(
32632
- (file) => import_node_path21.default.relative(targetDir, file).split(import_node_path21.default.sep).join(import_node_path21.default.posix.sep)
32741
+ (file) => import_node_path22.default.relative(targetDir, file).split(import_node_path22.default.sep).join(import_node_path22.default.posix.sep)
32633
32742
  );
32634
32743
  const colors = enumerateBundleColors(
32635
32744
  relative2,
32636
- (candidate) => candidate.endsWith("Contents.json") ? readText(import_node_path21.default.join(targetDir, candidate)) : null
32745
+ (candidate) => candidate.endsWith("Contents.json") ? readText(import_node_path22.default.join(targetDir, candidate)) : null
32637
32746
  );
32638
32747
  const pbxproj = listDir(iosDir).find((entry) => entry.name.endsWith(".xcodeproj"));
32639
- const accentName = readText(import_node_path21.default.join(targetDir, "Info.plist"))?.match(PLIST_ACCENT)?.[1] ?? (pbxproj ? readText(import_node_path21.default.join(iosDir, pbxproj.name, "project.pbxproj"))?.match(
32748
+ const accentName = readText(import_node_path22.default.join(targetDir, "Info.plist"))?.match(PLIST_ACCENT)?.[1] ?? (pbxproj ? readText(import_node_path22.default.join(iosDir, pbxproj.name, "project.pbxproj"))?.match(
32640
32749
  ACCENT_BUILD_SETTING
32641
32750
  )?.[1] : void 0) ?? null;
32642
32751
  if (accentName === null && Object.keys(colors).length === 0) return null;
32643
32752
  return { colors, accentName };
32644
32753
  }
32645
- var import_node_fs20, import_node_path21, ACCENT_BUILD_SETTING, PLIST_ACCENT;
32754
+ var import_node_fs21, import_node_path22, ACCENT_BUILD_SETTING, PLIST_ACCENT;
32646
32755
  var init_app_catalog = __esm({
32647
32756
  "cli/app-catalog.ts"() {
32648
32757
  "use strict";
32649
- import_node_fs20 = __toESM(require("node:fs"), 1);
32650
- import_node_path21 = __toESM(require("node:path"), 1);
32758
+ import_node_fs21 = __toESM(require("node:fs"), 1);
32759
+ import_node_path22 = __toESM(require("node:path"), 1);
32651
32760
  init_bundleAssets();
32652
32761
  ACCENT_BUILD_SETTING = /ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "?([^";]+)"?;/;
32653
32762
  PLIST_ACCENT = /<key>NSAccentColorName<\/key>\s*<string>([^<]+)<\/string>/;
@@ -32657,9 +32766,9 @@ var init_app_catalog = __esm({
32657
32766
  // cli/app-project.ts
32658
32767
  function firstNamedFile(dir, names) {
32659
32768
  for (const name2 of names) {
32660
- const candidate = import_node_path22.default.join(dir, name2);
32769
+ const candidate = import_node_path23.default.join(dir, name2);
32661
32770
  try {
32662
- if (import_node_fs21.default.statSync(candidate).isFile()) return candidate;
32771
+ if (import_node_fs22.default.statSync(candidate).isFile()) return candidate;
32663
32772
  } catch {
32664
32773
  }
32665
32774
  }
@@ -32672,24 +32781,24 @@ function findRNXAppConfig(dir) {
32672
32781
  return firstNamedFile(dir, RNX_APP_CONFIG_NAMES);
32673
32782
  }
32674
32783
  function resolveAppProject(projectRoot) {
32675
- const appDir = import_node_path22.default.resolve(projectRoot);
32784
+ const appDir = import_node_path23.default.resolve(projectRoot);
32676
32785
  try {
32677
- if (!import_node_fs21.default.statSync(appDir).isDirectory()) return null;
32786
+ if (!import_node_fs22.default.statSync(appDir).isDirectory()) return null;
32678
32787
  } catch {
32679
32788
  return null;
32680
32789
  }
32681
32790
  const configPath = findRNXAppConfig(appDir);
32682
- if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs21.default.existsSync(import_node_path22.default.join(appDir, "package.json"))) {
32791
+ if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs22.default.existsSync(import_node_path23.default.join(appDir, "package.json"))) {
32683
32792
  return null;
32684
32793
  }
32685
32794
  return { appDir, configPath };
32686
32795
  }
32687
- var import_node_fs21, import_node_path22, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
32796
+ var import_node_fs22, import_node_path23, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
32688
32797
  var init_app_project = __esm({
32689
32798
  "cli/app-project.ts"() {
32690
32799
  "use strict";
32691
- import_node_fs21 = __toESM(require("node:fs"), 1);
32692
- import_node_path22 = __toESM(require("node:path"), 1);
32800
+ import_node_fs22 = __toESM(require("node:fs"), 1);
32801
+ import_node_path23 = __toESM(require("node:path"), 1);
32693
32802
  EXPO_APP_CONFIG_NAMES = [
32694
32803
  "app.config.js",
32695
32804
  "app.config.ts",
@@ -32718,13 +32827,13 @@ function isRNXConfig(value) {
32718
32827
  }
32719
32828
  function resolveConfigReplacementFiles(config, configPath) {
32720
32829
  if (!config.modules) return config;
32721
- const configDir = import_node_path23.default.dirname(configPath);
32830
+ const configDir = import_node_path24.default.dirname(configPath);
32722
32831
  return {
32723
32832
  ...config,
32724
32833
  modules: Object.fromEntries(
32725
32834
  Object.entries(config.modules).map(([name2, resolution]) => [
32726
32835
  name2,
32727
- resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path23.default.resolve(configDir, resolution.file) } : resolution
32836
+ resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path24.default.resolve(configDir, resolution.file) } : resolution
32728
32837
  ])
32729
32838
  )
32730
32839
  };
@@ -32754,12 +32863,12 @@ async function resolveRNXAppConfig(startDir = process.cwd()) {
32754
32863
  const project = resolveAppProject(startDir);
32755
32864
  return { project, config: await loadRNXAppConfig(project) };
32756
32865
  }
32757
- var import_node_module2, import_node_path23, import_jiti, require2;
32866
+ var import_node_module2, import_node_path24, import_jiti, require2;
32758
32867
  var init_app_config = __esm({
32759
32868
  "cli/app-config.ts"() {
32760
32869
  "use strict";
32761
32870
  import_node_module2 = require("node:module");
32762
- import_node_path23 = __toESM(require("node:path"), 1);
32871
+ import_node_path24 = __toESM(require("node:path"), 1);
32763
32872
  init_src();
32764
32873
  import_jiti = require("jiti");
32765
32874
  init_public_brand();
@@ -32816,13 +32925,13 @@ function discoverNativeLinkedAppFonts(opts) {
32816
32925
  scannedFontDirectories++;
32817
32926
  let entries;
32818
32927
  try {
32819
- entries = import_node_fs22.default.readdirSync(current.dir, { withFileTypes: true });
32928
+ entries = import_node_fs23.default.readdirSync(current.dir, { withFileTypes: true });
32820
32929
  } catch {
32821
32930
  continue;
32822
32931
  }
32823
32932
  for (const entry of entries) {
32824
32933
  if (entry.isSymbolicLink()) continue;
32825
- const fullPath = import_node_path24.default.join(current.dir, entry.name);
32934
+ const fullPath = import_node_path25.default.join(current.dir, entry.name);
32826
32935
  if (entry.isDirectory()) {
32827
32936
  if (current.depth < 4 && pending.length < 64 && !entry.name.startsWith(".")) {
32828
32937
  pending.push({ dir: fullPath, depth: current.depth + 1 });
@@ -32830,15 +32939,15 @@ function discoverNativeLinkedAppFonts(opts) {
32830
32939
  continue;
32831
32940
  }
32832
32941
  if (!entry.isFile() || !/\.(ttf|otf|woff|woff2)$/i.test(entry.name)) continue;
32833
- const relative2 = import_node_path24.default.relative(root, fullPath).split(import_node_path24.default.sep).join("/");
32834
- fontPaths.push(import_node_path24.default.posix.join(metroPathPrefix, relative2));
32942
+ const relative2 = import_node_path25.default.relative(root, fullPath).split(import_node_path25.default.sep).join("/");
32943
+ fontPaths.push(import_node_path25.default.posix.join(metroPathPrefix, relative2));
32835
32944
  if (fontPaths.length >= 256) break;
32836
32945
  }
32837
32946
  }
32838
32947
  };
32839
32948
  try {
32840
32949
  const packageJson = JSON.parse(
32841
- import_node_fs22.default.readFileSync(import_node_path24.default.join(selectedAppDir, "package.json"), "utf8")
32950
+ import_node_fs23.default.readFileSync(import_node_path25.default.join(selectedAppDir, "package.json"), "utf8")
32842
32951
  );
32843
32952
  const dependencyNames = /* @__PURE__ */ new Set();
32844
32953
  for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
@@ -32853,32 +32962,32 @@ function discoverNativeLinkedAppFonts(opts) {
32853
32962
  const config = Reflect.get(packageJson, "reactNativeVectorIcons");
32854
32963
  const configuredFontDir = config && typeof config === "object" ? Reflect.get(config, "fontDir") : null;
32855
32964
  const configuredDir = typeof configuredFontDir === "string" ? configuredFontDir : "rnvi-fonts";
32856
- const root = import_node_path24.default.resolve(selectedAppDir, configuredDir);
32857
- const relativeRoot = import_node_path24.default.relative(selectedAppDir, root);
32858
- if (!relativeRoot.startsWith("..") && !import_node_path24.default.isAbsolute(relativeRoot)) {
32859
- collectFontFiles(root, relativeRoot.split(import_node_path24.default.sep).join("/"));
32965
+ const root = import_node_path25.default.resolve(selectedAppDir, configuredDir);
32966
+ const relativeRoot = import_node_path25.default.relative(selectedAppDir, root);
32967
+ if (!relativeRoot.startsWith("..") && !import_node_path25.default.isAbsolute(relativeRoot)) {
32968
+ collectFontFiles(root, relativeRoot.split(import_node_path25.default.sep).join("/"));
32860
32969
  }
32861
- const bundleDir = import_node_path24.default.posix.dirname(new URL(opts.bundleUrl).pathname).replace(/^\/+/, "");
32862
- const appRelativeDir = bundleDir.split("/").join(import_node_path24.default.sep);
32970
+ const bundleDir = import_node_path25.default.posix.dirname(new URL(opts.bundleUrl).pathname).replace(/^\/+/, "");
32971
+ const appRelativeDir = bundleDir.split("/").join(import_node_path25.default.sep);
32863
32972
  const metroRootCandidates = [selectedAppDir];
32864
- if (bundleDir && selectedAppDir.endsWith(import_node_path24.default.sep + appRelativeDir)) {
32973
+ if (bundleDir && selectedAppDir.endsWith(import_node_path25.default.sep + appRelativeDir)) {
32865
32974
  metroRootCandidates.push(selectedAppDir.slice(0, -appRelativeDir.length - 1));
32866
32975
  }
32867
- const vectorIconRequire = (0, import_node_module3.createRequire)(import_node_path24.default.join(selectedAppDir, "package.json"));
32976
+ const vectorIconRequire = (0, import_node_module3.createRequire)(import_node_path25.default.join(selectedAppDir, "package.json"));
32868
32977
  for (const dependencyName of vectorIconDependencies) {
32869
32978
  try {
32870
32979
  const entryPath = vectorIconRequire.resolve(dependencyName);
32871
- const dependencyPath = dependencyName.split("/").join(import_node_path24.default.sep);
32872
- const marker = `${import_node_path24.default.sep}node_modules${import_node_path24.default.sep}${dependencyPath}${import_node_path24.default.sep}`;
32980
+ const dependencyPath = dependencyName.split("/").join(import_node_path25.default.sep);
32981
+ const marker = `${import_node_path25.default.sep}node_modules${import_node_path25.default.sep}${dependencyPath}${import_node_path25.default.sep}`;
32873
32982
  const markerIndex = entryPath.lastIndexOf(marker);
32874
32983
  if (markerIndex === -1) continue;
32875
32984
  const packageRoot = entryPath.slice(0, markerIndex + marker.length - 1);
32876
32985
  const fontsDir = dependencyName === "react-native-vector-icons" ? "Fonts" : "fonts";
32877
- const fontsRoot = import_node_path24.default.join(packageRoot, fontsDir);
32986
+ const fontsRoot = import_node_path25.default.join(packageRoot, fontsDir);
32878
32987
  for (const metroRoot of metroRootCandidates) {
32879
32988
  collectFontFiles(
32880
32989
  fontsRoot,
32881
- import_node_path24.default.relative(metroRoot, fontsRoot).split(import_node_path24.default.sep).join("/")
32990
+ import_node_path25.default.relative(metroRoot, fontsRoot).split(import_node_path25.default.sep).join("/")
32882
32991
  );
32883
32992
  }
32884
32993
  } catch {
@@ -32888,37 +32997,37 @@ function discoverNativeLinkedAppFonts(opts) {
32888
32997
  } catch {
32889
32998
  }
32890
32999
  try {
32891
- const configPath = import_node_path24.default.join(selectedAppDir, "react-native.config.js");
32892
- if (import_node_fs22.default.statSync(configPath).isFile()) {
32893
- const appConfigRequire = (0, import_node_module3.createRequire)(import_node_path24.default.join(selectedAppDir, "package.json"));
33000
+ const configPath = import_node_path25.default.join(selectedAppDir, "react-native.config.js");
33001
+ if (import_node_fs23.default.statSync(configPath).isFile()) {
33002
+ const appConfigRequire = (0, import_node_module3.createRequire)(import_node_path25.default.join(selectedAppDir, "package.json"));
32894
33003
  const rnConfig = appConfigRequire(configPath);
32895
33004
  const assets = rnConfig && typeof rnConfig === "object" ? Reflect.get(rnConfig, "assets") : null;
32896
33005
  if (Array.isArray(assets)) {
32897
33006
  for (const assetDir of assets) {
32898
33007
  if (typeof assetDir !== "string") continue;
32899
- const root = import_node_path24.default.resolve(selectedAppDir, assetDir);
32900
- const relativeRoot = import_node_path24.default.relative(selectedAppDir, root);
32901
- if (relativeRoot.startsWith("..") || import_node_path24.default.isAbsolute(relativeRoot)) continue;
32902
- collectFontFiles(root, relativeRoot.split(import_node_path24.default.sep).join("/"));
33008
+ const root = import_node_path25.default.resolve(selectedAppDir, assetDir);
33009
+ const relativeRoot = import_node_path25.default.relative(selectedAppDir, root);
33010
+ if (relativeRoot.startsWith("..") || import_node_path25.default.isAbsolute(relativeRoot)) continue;
33011
+ collectFontFiles(root, relativeRoot.split(import_node_path25.default.sep).join("/"));
32903
33012
  }
32904
33013
  }
32905
33014
  }
32906
33015
  } catch {
32907
33016
  }
32908
33017
  if (platform === "ios") {
32909
- const iosDir = import_node_path24.default.join(selectedAppDir, "ios");
33018
+ const iosDir = import_node_path25.default.join(selectedAppDir, "ios");
32910
33019
  try {
32911
- const targetDirs = import_node_fs22.default.readdirSync(iosDir, { withFileTypes: true }).filter(
33020
+ const targetDirs = import_node_fs23.default.readdirSync(iosDir, { withFileTypes: true }).filter(
32912
33021
  (entry) => entry.isDirectory() && !entry.isSymbolicLink() && entry.name !== "Pods" && entry.name !== "build" && !entry.name.startsWith(".")
32913
33022
  ).slice(0, 64);
32914
- const plistPaths = [import_node_path24.default.join(iosDir, "Info.plist")];
33023
+ const plistPaths = [import_node_path25.default.join(iosDir, "Info.plist")];
32915
33024
  for (const entry of targetDirs) {
32916
- plistPaths.push(import_node_path24.default.join(iosDir, entry.name, "Info.plist"));
33025
+ plistPaths.push(import_node_path25.default.join(iosDir, entry.name, "Info.plist"));
32917
33026
  }
32918
33027
  for (const plistPath of plistPaths) {
32919
33028
  let parsed;
32920
33029
  try {
32921
- parsed = parsePlistSource(import_node_fs22.default.readFileSync(plistPath, "utf8"));
33030
+ parsed = parsePlistSource(import_node_fs23.default.readFileSync(plistPath, "utf8"));
32922
33031
  } catch {
32923
33032
  continue;
32924
33033
  }
@@ -32928,21 +33037,21 @@ function discoverNativeLinkedAppFonts(opts) {
32928
33037
  for (const declaredFont of declaredFonts) {
32929
33038
  if (typeof declaredFont !== "string") continue;
32930
33039
  const candidates = [
32931
- import_node_path24.default.resolve(import_node_path24.default.dirname(plistPath), declaredFont),
32932
- import_node_path24.default.resolve(iosDir, declaredFont)
33040
+ import_node_path25.default.resolve(import_node_path25.default.dirname(plistPath), declaredFont),
33041
+ import_node_path25.default.resolve(iosDir, declaredFont)
32933
33042
  ];
32934
33043
  const fontPath = candidates.find((candidate) => {
32935
- const relative2 = import_node_path24.default.relative(selectedAppDir, candidate);
32936
- if (relative2.startsWith("..") || import_node_path24.default.isAbsolute(relative2)) return false;
33044
+ const relative2 = import_node_path25.default.relative(selectedAppDir, candidate);
33045
+ if (relative2.startsWith("..") || import_node_path25.default.isAbsolute(relative2)) return false;
32937
33046
  try {
32938
- return import_node_fs22.default.statSync(candidate).isFile();
33047
+ return import_node_fs23.default.statSync(candidate).isFile();
32939
33048
  } catch {
32940
33049
  return false;
32941
33050
  }
32942
33051
  });
32943
33052
  if (!fontPath) continue;
32944
33053
  fontPaths.push(
32945
- import_node_path24.default.relative(selectedAppDir, fontPath).split(import_node_path24.default.sep).join("/")
33054
+ import_node_path25.default.relative(selectedAppDir, fontPath).split(import_node_path25.default.sep).join("/")
32946
33055
  );
32947
33056
  }
32948
33057
  }
@@ -32981,13 +33090,13 @@ async function resolveMetroProjectRoot(bundleUrl, opts) {
32981
33090
  function encodeAppFontsWire(specs) {
32982
33091
  return specs.map((spec) => `${encodeURIComponent(spec.url)}::${encodeURIComponent(spec.family)}`).join(",");
32983
33092
  }
32984
- var import_node_fs22, import_node_module3, import_node_path24, import_config_plugin_fonts, require3;
33093
+ var import_node_fs23, import_node_module3, import_node_path25, import_config_plugin_fonts, require3;
32985
33094
  var init_app_fonts = __esm({
32986
33095
  "cli/app-fonts.ts"() {
32987
33096
  "use strict";
32988
- import_node_fs22 = __toESM(require("node:fs"), 1);
33097
+ import_node_fs23 = __toESM(require("node:fs"), 1);
32989
33098
  import_node_module3 = require("node:module");
32990
- import_node_path24 = __toESM(require("node:path"), 1);
33099
+ import_node_path25 = __toESM(require("node:path"), 1);
32991
33100
  import_config_plugin_fonts = require("peach-engine/engine/config-plugin-fonts");
32992
33101
  init_dev_server_scanner();
32993
33102
  init_backend_origin();
@@ -34239,13 +34348,13 @@ function resolvePlaywright() {
34239
34348
  try {
34240
34349
  const modulePath = requireFromRoot.resolve(spec);
34241
34350
  const packageJsonPath = requireFromRoot.resolve(`${spec}/package.json`);
34242
- const parsed = JSON.parse((0, import_node_fs23.readFileSync)(packageJsonPath, "utf8"));
34351
+ const parsed = JSON.parse((0, import_node_fs24.readFileSync)(packageJsonPath, "utf8"));
34243
34352
  if (!isRecord(parsed) || typeof parsed.version !== "string") continue;
34244
34353
  const corePackagePath = (0, import_node_module5.createRequire)(modulePath).resolve(
34245
34354
  "playwright-core/package.json"
34246
34355
  );
34247
- const cliPath = (0, import_node_path25.join)((0, import_node_path25.dirname)(corePackagePath), "cli.js");
34248
- if (!(0, import_node_fs23.existsSync)(cliPath)) continue;
34356
+ const cliPath = (0, import_node_path26.join)((0, import_node_path26.dirname)(corePackagePath), "cli.js");
34357
+ if (!(0, import_node_fs24.existsSync)(cliPath)) continue;
34249
34358
  return {
34250
34359
  spec,
34251
34360
  modulePath,
@@ -34273,7 +34382,7 @@ async function provisionPlaywright() {
34273
34382
  await installVerifiedTarball({
34274
34383
  url: `https://registry.npmjs.org/playwright-core/-/playwright-core-${version}.tgz`,
34275
34384
  digest: { algorithm, encoding: "base64", value: digest },
34276
- destDir: (0, import_node_path25.join)(playwrightCoreDir(version), "node_modules", "playwright-core"),
34385
+ destDir: (0, import_node_path26.join)(playwrightCoreDir(version), "node_modules", "playwright-core"),
34277
34386
  installedMarker: "package.json",
34278
34387
  // npm tarballs hold the package under a leading `package/`
34279
34388
  stripComponents: 1,
@@ -34316,7 +34425,7 @@ function probeBrowser(resolved, env) {
34316
34425
  return {
34317
34426
  ok: true,
34318
34427
  executablePath: parsed.executablePath,
34319
- exists: parsed.exists && (0, import_node_fs23.existsSync)(parsed.executablePath)
34428
+ exists: parsed.exists && (0, import_node_fs24.existsSync)(parsed.executablePath)
34320
34429
  };
34321
34430
  } catch (error) {
34322
34431
  return {
@@ -34367,14 +34476,14 @@ ${detail}` : ""}`
34367
34476
  }
34368
34477
  return { ok: true, executablePath: after.executablePath, installed: true };
34369
34478
  }
34370
- var import_node_child_process6, import_node_fs23, import_node_module5, import_node_path25;
34479
+ var import_node_child_process6, import_node_fs24, import_node_module5, import_node_path26;
34371
34480
  var init_playwright_provisioning = __esm({
34372
34481
  "cli/drivers/playwright-provisioning.ts"() {
34373
34482
  "use strict";
34374
34483
  import_node_child_process6 = require("node:child_process");
34375
- import_node_fs23 = require("node:fs");
34484
+ import_node_fs24 = require("node:fs");
34376
34485
  import_node_module5 = require("node:module");
34377
- import_node_path25 = require("node:path");
34486
+ import_node_path26 = require("node:path");
34378
34487
  init_src();
34379
34488
  init_home_paths();
34380
34489
  init_is_record();
@@ -34643,6 +34752,7 @@ var init_command_registry = __esm({
34643
34752
  "android",
34644
34753
  "remote",
34645
34754
  "preview",
34755
+ "swift",
34646
34756
  "box",
34647
34757
  "list",
34648
34758
  "use",
@@ -35233,7 +35343,7 @@ function resolveDriverHeadless(args, driverId) {
35233
35343
  function normalizeKnownTarget(target) {
35234
35344
  return target.trim();
35235
35345
  }
35236
- async function ensureBridgeForOpen(preferredPort, explicitPort = false) {
35346
+ async function ensureBridgeForOpen(preferredPort, explicitPort = false, resources) {
35237
35347
  const lock = readDaemonLockfile();
35238
35348
  assertExplicitBridgeLock(preferredPort, explicitPort, lock);
35239
35349
  const preferredWorld = worldForBridgePort(preferredPort);
@@ -35255,6 +35365,10 @@ async function ensureBridgeForOpen(preferredPort, explicitPort = false) {
35255
35365
  }
35256
35366
  await ensureRuntimeForOpen();
35257
35367
  const child = spawnSootsim(["serve", "--quiet", "--port", String(preferredPort)]);
35368
+ if (resources) {
35369
+ resources.bridge = child;
35370
+ resources.port = preferredPort;
35371
+ }
35258
35372
  let childExit = null;
35259
35373
  child.once("exit", (code, signal) => {
35260
35374
  childExit = String(code ?? signal ?? "unknown");
@@ -35275,6 +35389,10 @@ async function ensureBridgeForOpen(preferredPort, explicitPort = false) {
35275
35389
  console.error(
35276
35390
  ` Started a local bridge on port ${selectedPort}. Run \`peach daemon install\` to keep it ready between commands.`
35277
35391
  );
35392
+ if (resources) {
35393
+ resources.bridgeReady = true;
35394
+ resources.port = selectedPort;
35395
+ }
35278
35396
  return selectedPort;
35279
35397
  }
35280
35398
  if (childExit) {
@@ -35302,7 +35420,7 @@ function assertExplicitBridgeLock(preferredPort, explicitPort, lock) {
35302
35420
  async function ensureRuntimeForOpen() {
35303
35421
  if (resolveDevCheckoutRuntimeRoot()) return;
35304
35422
  const active = readActiveRuntime();
35305
- if (active && import_node_fs24.default.existsSync(runtimeDir(active))) return;
35423
+ if (active && import_node_fs25.default.existsSync(runtimeDir(active))) return;
35306
35424
  console.error(" installing peach engine runtime...");
35307
35425
  const { runRuntime: runRuntime2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
35308
35426
  await runRuntime2(["install"], {});
@@ -35456,10 +35574,10 @@ async function buildResolvedBundleOpenUrl(target, baseUrl, opts = {}) {
35456
35574
  }
35457
35575
  function normalizeReplacementPath(filePath) {
35458
35576
  if (filePath.startsWith("~/")) {
35459
- return import_node_path26.default.join(import_node_os4.default.homedir(), filePath.slice(2));
35577
+ return import_node_path27.default.join(import_node_os4.default.homedir(), filePath.slice(2));
35460
35578
  }
35461
- if (import_node_path26.default.isAbsolute(filePath)) return filePath;
35462
- return import_node_path26.default.resolve(process.cwd(), filePath);
35579
+ if (import_node_path27.default.isAbsolute(filePath)) return filePath;
35580
+ return import_node_path27.default.resolve(process.cwd(), filePath);
35463
35581
  }
35464
35582
  function parseOpenRuntimeConfig(args) {
35465
35583
  const modules = {};
@@ -35478,7 +35596,7 @@ function parseOpenRuntimeConfig(args) {
35478
35596
  }
35479
35597
  const moduleName = raw.slice(0, eqIndex).trim();
35480
35598
  const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim());
35481
- if (!import_node_fs24.default.existsSync(filePath)) {
35599
+ if (!import_node_fs25.default.existsSync(filePath)) {
35482
35600
  console.error(` peach open: replacement file not found: ${filePath}`);
35483
35601
  rnxExit(1);
35484
35602
  }
@@ -35509,7 +35627,7 @@ async function resolveAppProjectForBundleUrl(bundleUrl) {
35509
35627
  return advertisedRoot ? resolveAppProject(advertisedRoot) : null;
35510
35628
  }
35511
35629
  async function ensureConfiguredRuntime(version) {
35512
- if (import_node_fs24.default.existsSync(import_node_path26.default.join(runtimeDir(version), "index.html"))) return;
35630
+ if (import_node_fs25.default.existsSync(import_node_path27.default.join(runtimeDir(version), "index.html"))) return;
35513
35631
  console.error(` installing configured engine runtime ${version}...`);
35514
35632
  const result = await rnxRuntime.install({
35515
35633
  version,
@@ -35684,9 +35802,10 @@ async function buildShellUrl(target, baseUrl = resolveDefaultShellBaseUrl(), opt
35684
35802
  ),
35685
35803
  project
35686
35804
  );
35687
- if (!opts.device && !opts.proof && !opts.posture && !opts.osVersion && !opts.env?.length)
35805
+ if (!opts.device && !opts.proof && !opts.posture && !opts.osVersion && !opts.env?.length && !opts.dotEnv)
35688
35806
  return stamped;
35689
35807
  const url = new URL(stamped);
35808
+ if (opts.dotEnv) url.searchParams.set("dotEnv", JSON.stringify(opts.dotEnv));
35690
35809
  if (opts.device) url.searchParams.set("device", opts.device);
35691
35810
  if (opts.posture) url.searchParams.set("posture", opts.posture);
35692
35811
  if (opts.osVersion) url.searchParams.set("osVersion", opts.osVersion);
@@ -35771,6 +35890,7 @@ async function waitForSimMatchOrHostExit(wsPort, commandTimeoutMs, predicate, ho
35771
35890
  const watchHost = !!hostPid && Number.isInteger(hostPid) && hostPid > 1;
35772
35891
  const hostExited = () => watchHost && hostPid !== void 0 && !isProcessAlive2(hostPid);
35773
35892
  for (let i = 0; i < attempts; i++) {
35893
+ opts.signal?.throwIfAborted();
35774
35894
  if (hostExited()) {
35775
35895
  return { kind: "host-exit" };
35776
35896
  }
@@ -35823,7 +35943,7 @@ function playwrightHostPidForSim(sim) {
35823
35943
  function signalDriverLaunchConnected(result) {
35824
35944
  if (!result.connectAckFile) return false;
35825
35945
  try {
35826
- import_node_fs24.default.writeFileSync(
35946
+ import_node_fs25.default.writeFileSync(
35827
35947
  result.connectAckFile,
35828
35948
  `${JSON.stringify({ connectedAt: Date.now(), pid: result.pid ?? null })}
35829
35949
  `,
@@ -35837,19 +35957,19 @@ function signalDriverLaunchConnected(result) {
35837
35957
  function printDriverDiagnosticLogTail(result) {
35838
35958
  if (!result.diagnosticLogPath) return;
35839
35959
  try {
35840
- const detail = import_node_fs24.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
35960
+ const detail = import_node_fs25.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
35841
35961
  if (!detail) {
35842
- console.error(` ${import_node_path26.default.basename(result.diagnosticLogPath)} was empty`);
35962
+ console.error(` ${import_node_path27.default.basename(result.diagnosticLogPath)} was empty`);
35843
35963
  return;
35844
35964
  }
35845
35965
  const shown = detail.length > 4e3 ? `\u2026
35846
35966
  ${detail.slice(-4e3)}` : detail;
35847
- console.error(` ${import_node_path26.default.basename(result.diagnosticLogPath)}:`);
35967
+ console.error(` ${import_node_path27.default.basename(result.diagnosticLogPath)}:`);
35848
35968
  console.error(shown);
35849
35969
  } catch (err) {
35850
35970
  const message = err instanceof Error ? err.message : String(err);
35851
35971
  console.error(
35852
- ` failed to read ${import_node_path26.default.basename(result.diagnosticLogPath)}: ${message}`
35972
+ ` failed to read ${import_node_path27.default.basename(result.diagnosticLogPath)}: ${message}`
35853
35973
  );
35854
35974
  }
35855
35975
  }
@@ -36092,7 +36212,8 @@ async function runOpenCommand(args, opts = {}) {
36092
36212
  "--no-hmr",
36093
36213
  "--no-describe",
36094
36214
  "--quiet",
36095
- "--proof"
36215
+ "--proof",
36216
+ "--load-env"
36096
36217
  ],
36097
36218
  stripValueFlags: [
36098
36219
  "--base-url",
@@ -36203,7 +36324,14 @@ async function runOpenCommand(args, opts = {}) {
36203
36324
  parsed.wsPort = pinned.port;
36204
36325
  }
36205
36326
  }
36206
- parsed.wsPort = await ensureBridgeForOpen(parsed.wsPort, parsed.explicitPort);
36327
+ opts.signal?.throwIfAborted();
36328
+ parsed.wsPort = await ensureBridgeForOpen(
36329
+ parsed.wsPort,
36330
+ parsed.explicitPort,
36331
+ opts.resources
36332
+ );
36333
+ if (opts.resources) opts.resources.port = parsed.wsPort;
36334
+ opts.signal?.throwIfAborted();
36207
36335
  const requestedDriverId = args.find((_, i) => args[i - 1] === "--driver") || "";
36208
36336
  if (requestedDriverId && !isDriverId(requestedDriverId)) {
36209
36337
  console.error(
@@ -36268,6 +36396,25 @@ async function runOpenCommand(args, opts = {}) {
36268
36396
  rnxExit(1);
36269
36397
  }
36270
36398
  const explicitViewport = viewportMatch ? { width: Number(viewportMatch[1]), height: Number(viewportMatch[2]) } : void 0;
36399
+ let dotEnv;
36400
+ if (args.includes("--load-env")) {
36401
+ if (!project) {
36402
+ console.error(
36403
+ " peach open: --load-env needs a local app project to read the env file from"
36404
+ );
36405
+ rnxExit(1);
36406
+ }
36407
+ try {
36408
+ const result2 = readProjectDotEnv(project.appDir);
36409
+ if (!quiet) for (const notice of result2.notices) console.log(` ${notice}`);
36410
+ dotEnv = result2.config;
36411
+ } catch (error) {
36412
+ console.error(
36413
+ ` peach open: ${error instanceof Error ? error.message : String(error)}`
36414
+ );
36415
+ rnxExit(1);
36416
+ }
36417
+ }
36271
36418
  const buildShellUrlOpts = {
36272
36419
  hot: hotMode,
36273
36420
  device: deviceArg,
@@ -36276,7 +36423,8 @@ async function runOpenCommand(args, opts = {}) {
36276
36423
  resolvedBundle,
36277
36424
  project,
36278
36425
  proof: args.includes("--proof"),
36279
- env: envArgs.length ? envArgs : void 0
36426
+ env: envArgs.length ? envArgs : void 0,
36427
+ dotEnv
36280
36428
  };
36281
36429
  const selectedShellBaseUrl = configuredRuntimeVersion ? `http://localhost:${parsed.wsPort}/` : explicitBaseUrl || resolveShellBaseUrlForBridgePort(parsed.wsPort);
36282
36430
  const baseUrl = applyRuntimeGenerationOrigin(
@@ -36423,7 +36571,7 @@ async function runOpenCommand(args, opts = {}) {
36423
36571
  }
36424
36572
  const configuredBaseUrl = applyRNXConfigToUrl(baseUrl, runtimeConfig);
36425
36573
  const openUrl2 = await buildOpenUrl(target, configuredBaseUrl, token, buildShellUrlOpts);
36426
- const ownerPid = getStableOwnerPid();
36574
+ const ownerPid = opts.ownerPid ?? getStableOwnerPid();
36427
36575
  if (!ownerPid) {
36428
36576
  console.error(
36429
36577
  " could not resolve a stable owning process; refusing unsupervised sim host"
@@ -36436,6 +36584,7 @@ async function runOpenCommand(args, opts = {}) {
36436
36584
  rnxExit(1);
36437
36585
  }
36438
36586
  const connectWait = resolveDriverConnectWaitOptions();
36587
+ opts.signal?.throwIfAborted();
36439
36588
  const result = await driver.launch({
36440
36589
  url: openUrl2,
36441
36590
  // the shell serves the sim page but its WS bridge may have drifted off
@@ -36454,6 +36603,8 @@ async function runOpenCommand(args, opts = {}) {
36454
36603
  viewport: explicitViewport ?? (deviceFitBox ? { width: deviceFitBox.width + 120, height: deviceFitBox.height + 60 } : void 0),
36455
36604
  connectTimeoutMs: connectWait.hostTimeoutMs
36456
36605
  });
36606
+ if (opts.resources) opts.resources.driver = { ...result, driverId };
36607
+ opts.signal?.throwIfAborted();
36457
36608
  if (!result.launched) {
36458
36609
  console.error(` ${driver.name} driver: ${result.message}`);
36459
36610
  rnxExit(1);
@@ -36463,7 +36614,11 @@ async function runOpenCommand(args, opts = {}) {
36463
36614
  parsed.commandTimeoutMs,
36464
36615
  simMatchesToken,
36465
36616
  result.pid,
36466
- { attempts: connectWait.attempts, intervalMs: connectWait.intervalMs }
36617
+ {
36618
+ attempts: connectWait.attempts,
36619
+ intervalMs: connectWait.intervalMs,
36620
+ signal: opts.signal
36621
+ }
36467
36622
  );
36468
36623
  if (outcome.kind === "host-exit") {
36469
36624
  printDriverDiagnosticLogTail(result);
@@ -36485,6 +36640,7 @@ async function runOpenCommand(args, opts = {}) {
36485
36640
  rnxExit(1);
36486
36641
  }
36487
36642
  const match = outcome.sim;
36643
+ if (opts.resources) opts.resources.simId = match.id;
36488
36644
  saveCurrentSimId(match.id);
36489
36645
  signalDriverLaunchConnected(result);
36490
36646
  printOpenedSim(openUrl2, match, `${driver.name} driver`, quiet);
@@ -36782,14 +36938,14 @@ async function runCloseCommand(args, opts = {}) {
36782
36938
  bridge.close();
36783
36939
  }
36784
36940
  }
36785
- var import_node_child_process7, import_node_fs24, import_node_os4, import_node_path26, import_app_catalog_wire, import_devices2, import_ws4, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS, DEV_BUNDLE_RESOLVE_ATTEMPTS, DEV_BUNDLE_RESOLVE_INTERVAL_MS;
36941
+ var import_node_child_process7, import_node_fs25, import_node_os4, import_node_path27, import_app_catalog_wire, import_devices2, import_ws4, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS, DEV_BUNDLE_RESOLVE_ATTEMPTS, DEV_BUNDLE_RESOLVE_INTERVAL_MS;
36786
36942
  var init_control = __esm({
36787
36943
  "cli/commands/control.ts"() {
36788
36944
  "use strict";
36789
36945
  import_node_child_process7 = require("node:child_process");
36790
- import_node_fs24 = __toESM(require("node:fs"), 1);
36946
+ import_node_fs25 = __toESM(require("node:fs"), 1);
36791
36947
  import_node_os4 = __toESM(require("node:os"), 1);
36792
- import_node_path26 = __toESM(require("node:path"), 1);
36948
+ import_node_path27 = __toESM(require("node:path"), 1);
36793
36949
  import_app_catalog_wire = require("peach-engine/engine/app-catalog-wire");
36794
36950
  import_devices2 = require("peach-engine/settings/devices");
36795
36951
  import_ws4 = require("ws");
@@ -36799,6 +36955,7 @@ var init_control = __esm({
36799
36955
  init_bridge_constants();
36800
36956
  init_config();
36801
36957
  init_dev_bundle_resolution();
36958
+ init_dot_env();
36802
36959
  init_flow_timeout_scale();
36803
36960
  init_home_paths();
36804
36961
  init_native_dev_bundle_url();
@@ -38134,16 +38291,16 @@ function readRecentShellAppId(state) {
38134
38291
  }
38135
38292
  function resolveProjectRelativeFlowBase(flowDir) {
38136
38293
  if (!flowDir) return process.cwd();
38137
- return path20.basename(flowDir) === ".maestro" ? path20.dirname(flowDir) : flowDir;
38294
+ return path21.basename(flowDir) === ".maestro" ? path21.dirname(flowDir) : flowDir;
38138
38295
  }
38139
38296
  function resolveFlowScreenshotPathWithMode(screenshotDir, name2, opts) {
38140
38297
  const normalizedName = name2.endsWith(".png") ? name2 : `${name2}.png`;
38141
- if (path20.isAbsolute(normalizedName)) return normalizedName;
38298
+ if (path21.isAbsolute(normalizedName)) return normalizedName;
38142
38299
  if (opts.mode === "flow" && /[\\/]/.test(normalizedName)) {
38143
38300
  const baseDir = normalizedName.startsWith("./") || normalizedName.startsWith("../") ? opts.flowDir ?? process.cwd() : resolveProjectRelativeFlowBase(opts.flowDir);
38144
- return path20.resolve(baseDir, normalizedName);
38301
+ return path21.resolve(baseDir, normalizedName);
38145
38302
  }
38146
- return path20.join(screenshotDir, normalizedName);
38303
+ return path21.join(screenshotDir, normalizedName);
38147
38304
  }
38148
38305
  function normalizeFlowScreenshotSpec(step) {
38149
38306
  if (typeof step === "string") {
@@ -38200,12 +38357,12 @@ function parseFlowSteps(content) {
38200
38357
  const parsed = import_yaml2.default.parse(candidate);
38201
38358
  return Array.isArray(parsed) ? parsed : [];
38202
38359
  }
38203
- var fs20, path20, import_devices4, import_yaml2, import_proof_frame, DEFAULT_TIMEOUT, ASSERTION_LOOKUP_MS, FLOW_TIMEOUT_SCALE, scaledTimeout, MAESTRO_SELECTOR_KEYS, SELECTOR_FILTER_KEYS, UNSUPPORTED_SELECTOR_KEYS, ACCESSIBILITY_SELECTOR_KEYS, ACCESSIBILITY_TRAIT_FILTERS, MAESTRO_PERMISSION_KINDS, MAESTRO_PERMISSION_STATUS, SootSimBridgeFlowRunner;
38360
+ var fs21, path21, import_devices4, import_yaml2, import_proof_frame, DEFAULT_TIMEOUT, ASSERTION_LOOKUP_MS, FLOW_TIMEOUT_SCALE, scaledTimeout, MAESTRO_SELECTOR_KEYS, SELECTOR_FILTER_KEYS, UNSUPPORTED_SELECTOR_KEYS, ACCESSIBILITY_SELECTOR_KEYS, ACCESSIBILITY_TRAIT_FILTERS, MAESTRO_PERMISSION_KINDS, MAESTRO_PERMISSION_STATUS, SootSimBridgeFlowRunner;
38204
38361
  var init_bridge_flow_runner = __esm({
38205
38362
  "cli/bridge-flow-runner.ts"() {
38206
38363
  "use strict";
38207
- fs20 = __toESM(require("fs"), 1);
38208
- path20 = __toESM(require("path"), 1);
38364
+ fs21 = __toESM(require("fs"), 1);
38365
+ path21 = __toESM(require("path"), 1);
38209
38366
  import_devices4 = require("peach-engine/settings/devices");
38210
38367
  import_yaml2 = __toESM(require("yaml"), 1);
38211
38368
  import_proof_frame = __toESM(require_proof_frame(), 1);
@@ -38531,14 +38688,14 @@ var init_bridge_flow_runner = __esm({
38531
38688
  if (chunks.length === 0) {
38532
38689
  throw new Error("recording requested but no video buffer was produced");
38533
38690
  }
38534
- fs20.mkdirSync(this.opts.recordingOutputDir, { recursive: true });
38691
+ fs21.mkdirSync(this.opts.recordingOutputDir, { recursive: true });
38535
38692
  const videoMime = stopped.mime || this.opts.recordingFormat || "video/webm";
38536
38693
  const extension = videoMime.includes("mp4") ? "mp4" : "webm";
38537
- const outputPath = path20.join(
38694
+ const outputPath = path21.join(
38538
38695
  this.opts.recordingOutputDir,
38539
38696
  `peach-${Date.now()}.${extension}`
38540
38697
  );
38541
- fs20.writeFileSync(outputPath, Buffer.concat(chunks));
38698
+ fs21.writeFileSync(outputPath, Buffer.concat(chunks));
38542
38699
  return outputPath;
38543
38700
  }
38544
38701
  async waitForRecordingTail(opts) {
@@ -39214,7 +39371,7 @@ var init_bridge_flow_runner = __esm({
39214
39371
  flowDir: this.opts.flowDir
39215
39372
  }
39216
39373
  );
39217
- fs20.mkdirSync(path20.dirname(outputPath), { recursive: true });
39374
+ fs21.mkdirSync(path21.dirname(outputPath), { recursive: true });
39218
39375
  const layers = spec.layers ?? this.opts.screenshotLayers;
39219
39376
  const screenshotRequest = {
39220
39377
  type: "screenshot",
@@ -39249,11 +39406,11 @@ var init_bridge_flow_runner = __esm({
39249
39406
  throw new Error("could not read current device model for framed screenshot");
39250
39407
  }
39251
39408
  const framed = await composeFramedScreenshot(rawBuffer, model);
39252
- fs20.writeFileSync(outputPath, framed);
39409
+ fs21.writeFileSync(outputPath, framed);
39253
39410
  console.log(`[flow] screenshot: ${outputPath} (frame: ${model})`);
39254
39411
  return outputPath;
39255
39412
  }
39256
- fs20.writeFileSync(outputPath, rawBuffer);
39413
+ fs21.writeFileSync(outputPath, rawBuffer);
39257
39414
  console.log(`[flow] screenshot: ${outputPath}`);
39258
39415
  return outputPath;
39259
39416
  }
@@ -39275,10 +39432,10 @@ var init_bridge_flow_runner = __esm({
39275
39432
  // + a11y tree + console errors/warnings + failed network requests +
39276
39433
  // shell state, all under one directory so post-mortem is one grep away.
39277
39434
  async captureFailureBundle(outputDir, context) {
39278
- fs20.mkdirSync(outputDir, { recursive: true });
39435
+ fs21.mkdirSync(outputDir, { recursive: true });
39279
39436
  const write = (name2, content) => {
39280
39437
  try {
39281
- fs20.writeFileSync(path20.join(outputDir, name2), content);
39438
+ fs21.writeFileSync(path21.join(outputDir, name2), content);
39282
39439
  } catch {
39283
39440
  }
39284
39441
  };
@@ -39876,8 +40033,8 @@ var init_bridge_flow_runner = __esm({
39876
40033
  runScript(target) {
39877
40034
  const spec = typeof target === "string" ? { file: target } : target;
39878
40035
  if (!spec.file) throw new Error("runScript requires a file");
39879
- const scriptPath = path20.resolve(this.opts.flowDir, spec.file);
39880
- const source = fs20.readFileSync(scriptPath, "utf8");
40036
+ const scriptPath = path21.resolve(this.opts.flowDir, spec.file);
40037
+ const source = fs21.readFileSync(scriptPath, "utf8");
39881
40038
  this.js.evaluate(source, { env: spec.env });
39882
40039
  console.log(`[flow] runScript: ${spec.file} done`);
39883
40040
  }
@@ -40237,10 +40394,10 @@ var init_bridge_flow_runner = __esm({
40237
40394
  for (const [k, v] of Object.entries(env)) this.js.putEnv(k, v);
40238
40395
  }
40239
40396
  if (file) {
40240
- const flowPath = path20.resolve(this.opts.flowDir, file);
40241
- const flowContent = fs20.readFileSync(flowPath, "utf8");
40397
+ const flowPath = path21.resolve(this.opts.flowDir, file);
40398
+ const flowContent = fs21.readFileSync(flowPath, "utf8");
40242
40399
  const previousDir = this.opts.flowDir;
40243
- this.opts.flowDir = path20.dirname(flowPath);
40400
+ this.opts.flowDir = path21.dirname(flowPath);
40244
40401
  try {
40245
40402
  await this.runFlow(parseFlowSteps(flowContent));
40246
40403
  } finally {
@@ -40411,8 +40568,8 @@ __export(registry_exports, {
40411
40568
  skillRegistry: () => skillRegistry
40412
40569
  });
40413
40570
  module.exports = __toCommonJS(registry_exports);
40414
- var fs21 = __toESM(require("fs"), 1);
40415
- var path21 = __toESM(require("path"), 1);
40571
+ var fs22 = __toESM(require("fs"), 1);
40572
+ var path22 = __toESM(require("path"), 1);
40416
40573
 
40417
40574
  // src/skills/builtin/a11y-review.ts
40418
40575
  init_reap_browser();
@@ -40431,8 +40588,8 @@ var skill = {
40431
40588
  },
40432
40589
  async execute(params, context) {
40433
40590
  const { chromium } = await import("playwright");
40434
- const path22 = await import("path");
40435
- const fs22 = await import("fs");
40591
+ const path23 = await import("path");
40592
+ const fs23 = await import("fs");
40436
40593
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
40437
40594
  headless: true
40438
40595
  });
@@ -40487,9 +40644,9 @@ var skill = {
40487
40644
  warnings: issues.filter((i) => i.severity === "warning").length,
40488
40645
  tree: typeof tree === "string" ? tree : JSON.stringify(tree)
40489
40646
  };
40490
- const reportPath = path22.join(context.outputDir, "a11y-report.json");
40491
- fs22.mkdirSync(path22.dirname(reportPath), { recursive: true });
40492
- fs22.writeFileSync(reportPath, JSON.stringify(report, null, 2));
40647
+ const reportPath = path23.join(context.outputDir, "a11y-report.json");
40648
+ fs23.mkdirSync(path23.dirname(reportPath), { recursive: true });
40649
+ fs23.writeFileSync(reportPath, JSON.stringify(report, null, 2));
40493
40650
  return {
40494
40651
  success: issues.filter((i) => i.severity === "error").length === 0,
40495
40652
  message: `${report.errors} errors, ${report.warnings} warnings across ${report.nodeCount} nodes`,
@@ -40522,14 +40679,14 @@ var skill2 = {
40522
40679
  }
40523
40680
  },
40524
40681
  async execute(params, context) {
40525
- const fs22 = await import("node:fs");
40526
- const path22 = await import("node:path");
40682
+ const fs23 = await import("node:fs");
40683
+ const path23 = await import("node:path");
40527
40684
  const { COMPAT_RESULT_CAVEAT: COMPAT_RESULT_CAVEAT2, formatPackageStatus: formatPackageStatus2, formatScanCounts: formatScanCounts2, scanDeps: scanDeps2 } = await Promise.resolve().then(() => (init_web(), web_exports));
40528
- const packagePath = path22.join(context.projectDir, "package.json");
40529
- if (!fs22.existsSync(packagePath)) {
40685
+ const packagePath = path23.join(context.projectDir, "package.json");
40686
+ if (!fs23.existsSync(packagePath)) {
40530
40687
  return { success: false, message: "No package.json found" };
40531
40688
  }
40532
- const parsed = JSON.parse(fs22.readFileSync(packagePath, "utf8"));
40689
+ const parsed = JSON.parse(fs23.readFileSync(packagePath, "utf8"));
40533
40690
  if (!parsed || typeof parsed !== "object") {
40534
40691
  return { success: false, message: "package.json is not an object" };
40535
40692
  }
@@ -40541,9 +40698,9 @@ var skill2 = {
40541
40698
  }) : scanDeps2(parsed);
40542
40699
  const packageResult = params.packageName ? scan.packages[0] : void 0;
40543
40700
  const message = packageResult ? `${params.packageName}: ${formatPackageStatus2(packageResult)}. ${COMPAT_RESULT_CAVEAT2}` : `${formatScanCounts2(scan)}. ${COMPAT_RESULT_CAVEAT2}`;
40544
- const reportPath = path22.join(context.outputDir, "compat-report.json");
40545
- fs22.mkdirSync(path22.dirname(reportPath), { recursive: true });
40546
- fs22.writeFileSync(reportPath, `${JSON.stringify(scan, null, 2)}
40701
+ const reportPath = path23.join(context.outputDir, "compat-report.json");
40702
+ fs23.mkdirSync(path23.dirname(reportPath), { recursive: true });
40703
+ fs23.writeFileSync(reportPath, `${JSON.stringify(scan, null, 2)}
40547
40704
  `);
40548
40705
  return {
40549
40706
  success: true,
@@ -40585,8 +40742,8 @@ var skill3 = {
40585
40742
  record: { type: "boolean", description: "Record video of the flow" }
40586
40743
  },
40587
40744
  async execute(params, context) {
40588
- const fs22 = await import("fs");
40589
- const path22 = await import("path");
40745
+ const fs23 = await import("fs");
40746
+ const path23 = await import("path");
40590
40747
  const yaml2 = await import("yaml");
40591
40748
  const { DEFAULT_SOOTSIM_BRIDGE_PORT: DEFAULT_SOOTSIM_BRIDGE_PORT3 } = await Promise.resolve().then(() => (init_bridge_constants(), bridge_constants_exports));
40592
40749
  const { SootSimBridgeHost: SootSimBridgeHost2 } = await Promise.resolve().then(() => (init_bridge_host(), bridge_host_exports));
@@ -40596,7 +40753,7 @@ var skill3 = {
40596
40753
  const host = new SootSimBridgeHost2({ port: DEFAULT_SOOTSIM_BRIDGE_PORT3 });
40597
40754
  host.start({ silent: true });
40598
40755
  try {
40599
- const flowContent = fs22.readFileSync(params.flowPath, "utf8");
40756
+ const flowContent = fs23.readFileSync(params.flowPath, "utf8");
40600
40757
  const fmMatch = flowContent.match(/^---\n([\s\S]*?)\n---\n?/);
40601
40758
  const stepsBody = fmMatch ? flowContent.slice(fmMatch[0].length) : flowContent;
40602
40759
  const steps = yaml2.parse(stepsBody);
@@ -40623,7 +40780,7 @@ var skill3 = {
40623
40780
  });
40624
40781
  const driver = new SootSimBridgeFlowRunner2(bridge, {
40625
40782
  screenshotDir: context.outputDir,
40626
- flowDir: path22.dirname(path22.resolve(params.flowPath)),
40783
+ flowDir: path23.dirname(path23.resolve(params.flowPath)),
40627
40784
  simId,
40628
40785
  recordingOutputDir: params.record ? context.outputDir : void 0
40629
40786
  });
@@ -40645,7 +40802,7 @@ var skill3 = {
40645
40802
  simId,
40646
40803
  videoPath: videoPath || void 0
40647
40804
  },
40648
- artifacts: videoPath ? [{ type: "video", name: path22.basename(videoPath), path: videoPath }] : void 0
40805
+ artifacts: videoPath ? [{ type: "video", name: path23.basename(videoPath), path: videoPath }] : void 0
40649
40806
  };
40650
40807
  } finally {
40651
40808
  try {
@@ -40692,8 +40849,8 @@ var skill4 = {
40692
40849
  },
40693
40850
  async execute(params, context) {
40694
40851
  const { chromium } = await import("playwright");
40695
- const path22 = await import("path");
40696
- const fs22 = await import("fs");
40852
+ const path23 = await import("path");
40853
+ const fs23 = await import("fs");
40697
40854
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
40698
40855
  headless: true
40699
40856
  });
@@ -40719,9 +40876,9 @@ var skill4 = {
40719
40876
  ...profile,
40720
40877
  fps: syncCount > 0 ? (syncCount / (duration / 1e3)).toFixed(1) : "0"
40721
40878
  };
40722
- const reportPath = path22.join(context.outputDir, "perf-report.json");
40723
- fs22.mkdirSync(path22.dirname(reportPath), { recursive: true });
40724
- fs22.writeFileSync(reportPath, JSON.stringify(report, null, 2));
40879
+ const reportPath = path23.join(context.outputDir, "perf-report.json");
40880
+ fs23.mkdirSync(path23.dirname(reportPath), { recursive: true });
40881
+ fs23.writeFileSync(reportPath, JSON.stringify(report, null, 2));
40725
40882
  return {
40726
40883
  success: true,
40727
40884
  message: `${report.fps} fps, ${profile.avgSyncMs?.toFixed(1)}ms avg sync, ${nodeCount} nodes`,
@@ -40754,8 +40911,8 @@ var skill5 = {
40754
40911
  },
40755
40912
  async execute(params, context) {
40756
40913
  const { chromium } = await import("playwright");
40757
- const path22 = await import("path");
40758
- const fs22 = await import("fs");
40914
+ const path23 = await import("path");
40915
+ const fs23 = await import("fs");
40759
40916
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
40760
40917
  headless: true
40761
40918
  });
@@ -40763,8 +40920,8 @@ var skill5 = {
40763
40920
  try {
40764
40921
  await page.goto(context.url, { waitUntil: "networkidle" });
40765
40922
  await page.waitForTimeout(2e3);
40766
- const outputPath = path22.join(context.outputDir, `${params.name}.png`);
40767
- fs22.mkdirSync(path22.dirname(outputPath), { recursive: true });
40923
+ const outputPath = path23.join(context.outputDir, `${params.name}.png`);
40924
+ fs23.mkdirSync(path23.dirname(outputPath), { recursive: true });
40768
40925
  await page.screenshot({ path: outputPath });
40769
40926
  return {
40770
40927
  success: true,
@@ -40805,8 +40962,8 @@ var skill6 = {
40805
40962
  },
40806
40963
  async execute(params, context) {
40807
40964
  const { chromium } = await import("playwright");
40808
- const path22 = await import("path");
40809
- const fs22 = await import("fs");
40965
+ const path23 = await import("path");
40966
+ const fs23 = await import("fs");
40810
40967
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
40811
40968
  headless: true
40812
40969
  });
@@ -40814,14 +40971,14 @@ var skill6 = {
40814
40971
  try {
40815
40972
  await page.goto(context.url, { waitUntil: "networkidle" });
40816
40973
  await page.waitForTimeout(2e3);
40817
- const currentPath = path22.join(context.outputDir, `${params.name}-current.png`);
40818
- fs22.mkdirSync(path22.dirname(currentPath), { recursive: true });
40974
+ const currentPath = path23.join(context.outputDir, `${params.name}-current.png`);
40975
+ fs23.mkdirSync(path23.dirname(currentPath), { recursive: true });
40819
40976
  await page.screenshot({ path: currentPath });
40820
40977
  try {
40821
40978
  const { PNG } = await import("pngjs");
40822
40979
  const pixelmatch = (await import("pixelmatch")).default;
40823
- const baseline = PNG.sync.read(fs22.readFileSync(params.baseline));
40824
- const current = PNG.sync.read(fs22.readFileSync(currentPath));
40980
+ const baseline = PNG.sync.read(fs23.readFileSync(params.baseline));
40981
+ const current = PNG.sync.read(fs23.readFileSync(currentPath));
40825
40982
  const { width, height } = baseline;
40826
40983
  const diff = new PNG({ width, height });
40827
40984
  const numDiffPixels = pixelmatch(
@@ -40832,8 +40989,8 @@ var skill6 = {
40832
40989
  height,
40833
40990
  { threshold: params.threshold || 0.1 }
40834
40991
  );
40835
- const diffPath = path22.join(context.outputDir, `${params.name}-diff.png`);
40836
- fs22.writeFileSync(diffPath, PNG.sync.write(diff));
40992
+ const diffPath = path23.join(context.outputDir, `${params.name}-diff.png`);
40993
+ fs23.writeFileSync(diffPath, PNG.sync.write(diff));
40837
40994
  const totalPixels = width * height;
40838
40995
  const diffPercent = (numDiffPixels / totalPixels * 100).toFixed(2);
40839
40996
  return {
@@ -40897,10 +41054,10 @@ var skillRegistry = {
40897
41054
  },
40898
41055
  // load skills from a project manifest (rnx-skills.json)
40899
41056
  async loadFromManifest(projectDir) {
40900
- const manifestPath = path21.join(projectDir, "rnx-skills.json");
40901
- if (!fs21.existsSync(manifestPath)) return;
41057
+ const manifestPath = path22.join(projectDir, "rnx-skills.json");
41058
+ if (!fs22.existsSync(manifestPath)) return;
40902
41059
  try {
40903
- const manifest = JSON.parse(fs21.readFileSync(manifestPath, "utf8"));
41060
+ const manifest = JSON.parse(fs22.readFileSync(manifestPath, "utf8"));
40904
41061
  if (Array.isArray(manifest.skills)) {
40905
41062
  for (const entry of manifest.skills) {
40906
41063
  if (typeof entry === "string") {
@@ -40911,7 +41068,7 @@ var skillRegistry = {
40911
41068
  }
40912
41069
  } else if (entry.file) {
40913
41070
  try {
40914
- const mod = await import(path21.resolve(projectDir, entry.file));
41071
+ const mod = await import(path22.resolve(projectDir, entry.file));
40915
41072
  if (mod.skill) this.register(mod.skill);
40916
41073
  } catch {
40917
41074
  }
@@ -40924,10 +41081,10 @@ var skillRegistry = {
40924
41081
  // auto-discover skills based on project dependencies
40925
41082
  suggestSkills(projectDir) {
40926
41083
  const suggestions = [];
40927
- const pkgPath = path21.join(projectDir, "package.json");
40928
- if (!fs21.existsSync(pkgPath)) return suggestions;
41084
+ const pkgPath = path22.join(projectDir, "package.json");
41085
+ if (!fs22.existsSync(pkgPath)) return suggestions;
40929
41086
  try {
40930
- const pkg = JSON.parse(fs21.readFileSync(pkgPath, "utf8"));
41087
+ const pkg = JSON.parse(fs22.readFileSync(pkgPath, "utf8"));
40931
41088
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
40932
41089
  if (deps["detox"] || deps["maestro"]) {
40933
41090
  suggestions.push("maestro-test");