sootsim 0.1.138 → 0.1.140

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 (161) hide show
  1. package/dist-cli/bin.js +3 -3
  2. package/dist-cli/chunks/{agent-TD7SHXRH.js → agent-N767ME6M.js} +2 -2
  3. package/dist-cli/chunks/{agent-wrapper-H6JLLQAZ.js → agent-wrapper-ELHIUFZZ.js} +2 -2
  4. package/dist-cli/chunks/{app-fonts-JDQFGE3V.js → app-fonts-OZ3OSNAJ.js} +2 -2
  5. package/dist-cli/chunks/{assert-2XLXLJOD.js → assert-GGFUQBNB.js} +2 -2
  6. package/dist-cli/chunks/auto-bootstrap-L5E6XMCK.js +2 -0
  7. package/dist-cli/chunks/beta-D74SDA2L.js +2 -0
  8. package/dist-cli/chunks/camera-5PYEDLLB.js +20 -0
  9. package/dist-cli/chunks/chunk-4FBFOWV3.js +4 -0
  10. package/dist-cli/chunks/{chunk-7ELG4PYE.js → chunk-4FGNOWB6.js} +1 -1
  11. package/dist-cli/chunks/{chunk-ZSPUTLK7.js → chunk-5EP5RRNS.js} +35 -34
  12. package/dist-cli/chunks/{chunk-BA3QQ5B3.js → chunk-5VTYMBUK.js} +2 -2
  13. package/dist-cli/chunks/chunk-6KUVOSFF.js +79 -0
  14. package/dist-cli/chunks/{chunk-CTXIRBH2.js → chunk-77TL74AM.js} +1 -1
  15. package/dist-cli/chunks/{chunk-BDOLDROC.js → chunk-7SMEXHWJ.js} +1 -1
  16. package/dist-cli/chunks/{chunk-JFQVDRBK.js → chunk-AI65UVSN.js} +52 -4
  17. package/dist-cli/chunks/{chunk-XWHR6IKV.js → chunk-AOICEZCA.js} +2 -2
  18. package/dist-cli/chunks/chunk-AP2MOP44.js +2 -0
  19. package/dist-cli/chunks/chunk-BEA3AO36.js +5 -0
  20. package/dist-cli/chunks/{chunk-PILBWKNB.js → chunk-BGXNYXBC.js} +1 -1
  21. package/dist-cli/chunks/{chunk-FFVME5EQ.js → chunk-BLQBPN76.js} +2 -2
  22. package/dist-cli/chunks/{chunk-Q4J2SESV.js → chunk-BM7QWZRI.js} +2 -2
  23. package/dist-cli/chunks/{chunk-5N5MQQM5.js → chunk-C3FURRBF.js} +1 -1
  24. package/dist-cli/chunks/{chunk-RHU73TIJ.js → chunk-CQ3LV5VL.js} +2 -2
  25. package/dist-cli/chunks/chunk-EH6YDCSN.js +119 -0
  26. package/dist-cli/chunks/{chunk-T3HJHXXN.js → chunk-EMNVJOEL.js} +3 -3
  27. package/dist-cli/chunks/{chunk-C563J47N.js → chunk-FYUDIUFX.js} +2 -2
  28. package/dist-cli/chunks/{chunk-WHIZNJLR.js → chunk-GI43DDEI.js} +1 -1
  29. package/dist-cli/chunks/{chunk-KLFGCFE4.js → chunk-GL74P6NR.js} +1 -1
  30. package/dist-cli/chunks/{chunk-GYZ5ZZMH.js → chunk-GYZNLDBX.js} +2 -2
  31. package/dist-cli/chunks/chunk-HPTMLB7V.js +2 -0
  32. package/dist-cli/chunks/{chunk-NPUHFOSG.js → chunk-MRQRJINW.js} +1 -1
  33. package/dist-cli/chunks/{chunk-IEGRNKTT.js → chunk-N2XPC4Z7.js} +2 -2
  34. package/dist-cli/chunks/chunk-NQG4R5K2.js +1 -0
  35. package/dist-cli/chunks/{chunk-62DPZPFC.js → chunk-O2SQNUDP.js} +1 -1
  36. package/dist-cli/chunks/{chunk-I7RL227U.js → chunk-PBS47TD5.js} +2 -2
  37. package/dist-cli/chunks/chunk-Q5Z7P54I.js +2 -0
  38. package/dist-cli/chunks/{chunk-FBDOREAI.js → chunk-QOZ2Q4FH.js} +1 -1
  39. package/dist-cli/chunks/{chunk-SX6BVZL5.js → chunk-QRRBQE6M.js} +2 -2
  40. package/dist-cli/chunks/{chunk-KJOBH5CA.js → chunk-QXOZB7WD.js} +2 -2
  41. package/dist-cli/chunks/{chunk-3XBLSPSH.js → chunk-R5F7XGAO.js} +3 -3
  42. package/dist-cli/chunks/{chunk-VIZ5BLM2.js → chunk-RAY2DLSK.js} +1 -1
  43. package/dist-cli/chunks/{chunk-TP3NTZUM.js → chunk-REIC7HRW.js} +1 -1
  44. package/dist-cli/chunks/chunk-S7I6ZCT2.js +1 -0
  45. package/dist-cli/chunks/{chunk-GSGRNYPC.js → chunk-SSZBFWV7.js} +1 -1
  46. package/dist-cli/chunks/chunk-UHTE6FLK.js +69 -0
  47. package/dist-cli/chunks/{chunk-MZ7TYYP7.js → chunk-WGDXZMKT.js} +2 -2
  48. package/dist-cli/chunks/chunk-WN3WZEE5.js +1 -0
  49. package/dist-cli/chunks/{chunk-JEL7BQG7.js → chunk-X6AWKKL4.js} +2 -2
  50. package/dist-cli/chunks/{chunk-T6QPOIJN.js → chunk-XGYD25DJ.js} +1 -1
  51. package/dist-cli/chunks/{chunk-XV6XIC4J.js → chunk-ZGDJ5TFU.js} +2 -2
  52. package/dist-cli/chunks/{chunk-KBOMW6U3.js → chunk-ZN4Z2W7F.js} +2 -2
  53. package/dist-cli/chunks/{chunk-MNKYCXHQ.js → chunk-ZVQOEMXS.js} +52 -52
  54. package/dist-cli/chunks/cli-version-EZRTEUXF.js +2 -0
  55. package/dist-cli/chunks/{compat-6KRCWKBL.js → compat-U3772TUK.js} +3 -3
  56. package/dist-cli/chunks/{config-JGTLOWS6.js → config-6CZCIYKJ.js} +2 -2
  57. package/dist-cli/chunks/{control-3LHOESUK.js → control-NVA25O65.js} +2 -2
  58. package/dist-cli/chunks/{cpu-profile-KWE5IXBN.js → cpu-profile-WRLTZYO6.js} +2 -2
  59. package/dist-cli/chunks/{daemon-S5LNAQAU.js → daemon-QQPGLGS6.js} +2 -2
  60. package/dist-cli/chunks/{debug-KJ3TNXUN.js → debug-RKYLSDZV.js} +3 -3
  61. package/dist-cli/chunks/{detox-UJIPM47N.js → detox-ZRUQQU22.js} +2 -2
  62. package/dist-cli/chunks/{device-23SEWSUA.js → device-HXTOLEWY.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-53JGSJF2.js → diagnose-QN4E4WA6.js} +2 -2
  64. package/dist-cli/chunks/drivers-EVK22AX5.js +2 -0
  65. package/dist-cli/chunks/{electron-F43274XT.js → electron-ZHRC4WQ5.js} +3 -3
  66. package/dist-cli/chunks/flow-ICCRVJVX.js +2 -0
  67. package/dist-cli/chunks/help-QMZ35MIQ.js +2 -0
  68. package/dist-cli/chunks/{hints-6EFUPUX6.js → hints-HCHKMDUE.js} +2 -2
  69. package/dist-cli/chunks/{home-paths-TAGKOEXO.js → home-paths-3OHKRAE4.js} +2 -2
  70. package/dist-cli/chunks/{inspect-UVBYBYJB.js → inspect-QXUBDKJJ.js} +3 -3
  71. package/dist-cli/chunks/install-JSDZIE2T.js +2 -0
  72. package/dist-cli/chunks/{install-desktop-M2QEFAIN.js → install-desktop-QCX44BGL.js} +7 -7
  73. package/dist-cli/chunks/{keys-IJNQHCGZ.js → keys-VEP2KPUF.js} +2 -2
  74. package/dist-cli/chunks/{launch-UK4I6UYE.js → launch-K4BPQLTP.js} +3 -3
  75. package/dist-cli/chunks/{login-OBLKQALN.js → login-UVC5XRKR.js} +4 -4
  76. package/dist-cli/chunks/{logout-HUOLFAU6.js → logout-3VW34IBA.js} +2 -2
  77. package/dist-cli/chunks/{maestro-B6JMWFEO.js → maestro-HTFXXMLR.js} +2 -2
  78. package/dist-cli/chunks/optional-demo-registry-5YYEAL46.js +2 -0
  79. package/dist-cli/chunks/{preview-TYICI3EN.js → preview-TZYJMLEJ.js} +2 -2
  80. package/dist-cli/chunks/{profile-NQO5HFFJ.js → profile-HDJRF7YB.js} +2 -2
  81. package/dist-cli/chunks/{react-WXEGMAH6.js → react-RLJR26M7.js} +2 -2
  82. package/dist-cli/chunks/{record-3DK7CCXV.js → record-2LDDHHUY.js} +2 -2
  83. package/dist-cli/chunks/runtime-QPYF2WQX.js +2 -0
  84. package/dist-cli/chunks/{runtime-delivery-QAHWI6JH.js → runtime-delivery-3F6O57E5.js} +2 -2
  85. package/dist-cli/chunks/{screenshot-SQ7ETUJY.js → screenshot-QAUSSEPK.js} +2 -2
  86. package/dist-cli/chunks/{screenshot-mode-YYIBQI6S.js → screenshot-mode-K6HEVK4K.js} +2 -2
  87. package/dist-cli/chunks/{screenshots-YH5P24GR.js → screenshots-TWMCGUBT.js} +2 -2
  88. package/dist-cli/chunks/{server-PVYHPTCD.js → server-ZPIZCBW5.js} +3 -3
  89. package/dist-cli/chunks/setup-repo-VHOZJEIV.js +2 -0
  90. package/dist-cli/chunks/{skills-2V2A7R4O.js → skills-NI3RH7CW.js} +2 -2
  91. package/dist-cli/chunks/{start-JKY3T6AY.js → start-G6CQL6HJ.js} +4 -4
  92. package/dist-cli/chunks/store-F6MJUJYW.js +2 -0
  93. package/dist-cli/chunks/telemetry-DM2Y7OQJ.js +2 -0
  94. package/dist-cli/chunks/{test-FVA5WGGM.js → test-V47D6EPK.js} +3 -3
  95. package/dist-cli/chunks/{three-mode-GVODLLP3.js → three-mode-G7N3LRHM.js} +2 -2
  96. package/dist-cli/chunks/{timeline-LCJYWJBG.js → timeline-EBUOILTJ.js} +2 -2
  97. package/dist-cli/chunks/{upgrade-4TVPQJRL.js → upgrade-OQJLTCQP.js} +2 -2
  98. package/dist-cli/chunks/upload-W3YCA65H.js +2 -0
  99. package/dist-cli/chunks/{version-2XWRCYTM.js → version-3YJPPUE5.js} +2 -2
  100. package/dist-cli/chunks/web-ZKUX5ICX.js +2 -0
  101. package/dist-cli/chunks/{what-happened-R57H6WK6.js → what-happened-HOY3TA42.js} +2 -2
  102. package/dist-cli/chunks/{whoami-YCVHJHIM.js → whoami-GOCSBVTG.js} +2 -2
  103. package/dist-lib/agent-daemon-client.cjs +1 -1
  104. package/dist-lib/agent-events.cjs +1 -1
  105. package/dist-lib/agent-sessions.cjs +1 -1
  106. package/dist-lib/attached-projects.cjs +42 -820
  107. package/dist-lib/auth/shared-session.cjs +1 -1
  108. package/dist-lib/backend-origin.cjs +1 -1
  109. package/dist-lib/beta.cjs +1 -1
  110. package/dist-lib/beta.mjs +1 -1
  111. package/dist-lib/bridge-constants.cjs +1 -1
  112. package/dist-lib/cli-constants.cjs +1 -1
  113. package/dist-lib/config.cjs +1 -1
  114. package/dist-lib/detox/index.cjs +1 -1
  115. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  116. package/dist-lib/home-paths.cjs +1 -1
  117. package/dist-lib/host/bridge-host.cjs +179 -956
  118. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  119. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  120. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  121. package/dist-lib/host/websocket-proxy.cjs +1 -1
  122. package/dist-lib/index.cjs +1 -1
  123. package/dist-lib/metro.cjs +1 -1
  124. package/dist-lib/profiles.cjs +1 -1
  125. package/dist-lib/render-mode.cjs +1 -1
  126. package/dist-lib/scripts/dev-server-scanner.cjs +29 -797
  127. package/dist-lib/sdk.cjs +1 -1
  128. package/dist-lib/sdk.mjs +1 -1
  129. package/dist-lib/skills.cjs +660 -1032
  130. package/dist-lib/vite.cjs +1 -1
  131. package/package.json +2 -6
  132. package/scripts/dev-server-scanner.ts +10 -2
  133. package/scripts/optional-demo-registry.ts +54 -0
  134. package/src/attached-projects.ts +6 -27
  135. package/dist-cli/chunks/auto-bootstrap-Q7Z5522T.js +0 -2
  136. package/dist-cli/chunks/beta-LW4TRSKL.js +0 -2
  137. package/dist-cli/chunks/chunk-3AVKOCIL.js +0 -5
  138. package/dist-cli/chunks/chunk-BXITI3GQ.js +0 -4
  139. package/dist-cli/chunks/chunk-DBJCMZX7.js +0 -1
  140. package/dist-cli/chunks/chunk-DV4DKAHB.js +0 -64
  141. package/dist-cli/chunks/chunk-KYAAYSMS.js +0 -2
  142. package/dist-cli/chunks/chunk-M6GZUJXH.js +0 -62
  143. package/dist-cli/chunks/chunk-N4DFR7UX.js +0 -79
  144. package/dist-cli/chunks/chunk-N7TXV2XT.js +0 -1
  145. package/dist-cli/chunks/chunk-SIKDBA2E.js +0 -119
  146. package/dist-cli/chunks/chunk-YB7NANNJ.js +0 -1
  147. package/dist-cli/chunks/chunk-ZS55UGAA.js +0 -2
  148. package/dist-cli/chunks/cli-version-ROECZHPK.js +0 -2
  149. package/dist-cli/chunks/demo-app-registry-SU74KYFQ.js +0 -2
  150. package/dist-cli/chunks/drivers-LQS4T2IT.js +0 -2
  151. package/dist-cli/chunks/flow-H4F2X23Y.js +0 -2
  152. package/dist-cli/chunks/help-BT5NJNJ5.js +0 -2
  153. package/dist-cli/chunks/install-P5R6747T.js +0 -2
  154. package/dist-cli/chunks/runtime-IFNZGBPZ.js +0 -2
  155. package/dist-cli/chunks/setup-repo-Y63FUXRC.js +0 -2
  156. package/dist-cli/chunks/store-KNRC4EGM.js +0 -2
  157. package/dist-cli/chunks/telemetry-HDSITSDA.js +0 -2
  158. package/dist-cli/chunks/upload-3AXP6K6R.js +0 -2
  159. package/dist-cli/chunks/web-4Z6SFQFV.js +0 -2
  160. package/dist-lib/scripts/demo-app-registry.cjs +0 -818
  161. package/scripts/demo-app-registry.ts +0 -1006
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.138 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.140 | (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;
@@ -1078,10 +1078,10 @@ var init_registry = __esm({
1078
1078
  versions: [
1079
1079
  {
1080
1080
  range: ">=3.0.0",
1081
- coverage: 0.4,
1082
- note: "OffscreenCanvas produces a real PNG/JPG/WebP blob URL at the requested dimensions, validateOptions mirrors upstream src/index.tsx warning text, ViewShot exposes capture() on the inner ref with mount/continuous/update captureMode, tmpfile mode tracks blob URLs for releaseCapture revocation. BUT the captured pixels are a placeholder diagonal pattern, not the actual view tree \u2014 apps that share / upload / display the result get a placeholder image, not their UI. The primary purpose (visual capture of the rendered view) is not satisfied \u2014 would need CanvasKit pixel readback through the engine bridge.",
1083
- working: "captureRef/captureScreen returning a real blob URL/base64/data-uri at the right dimensions and format, validateOptions parity with upstream warning text, all format types (png/jpg/webp/webm\u2192webp/raw\u2192png), quality forwarding for lossy formats, ViewShot component with capture() ref method, captureMode mount/continuous/update, releaseCapture revoking tracked blob URLs",
1084
- missing: "real pixel content matching the view tree (placeholder pattern instead \u2014 the primary purpose of the package), zip-base64 falls back to plain base64, raw format falls back to png"
1081
+ coverage: 0.82,
1082
+ note: "captureRef/captureScreen now return REAL captured pixels: the tenant resolves the target node's absolute rect (unstable_getBoundingClientRect) and asks the shell \u2014 the only context that paints the visible app pixels \u2014 to crop that region of the active app surface and re-encode it (viewShot.capture round-trip over the native-UI bus \u2192 shell-side createImageBitmap(offscreen, sx,sy,sw,sh) + OffscreenCanvas convertToBlob). The data-uri is coerced into the requested result shape (tmpfile blob URL / base64 / data-uri). validateOptions mirrors upstream src/index.tsx warning text; ViewShot exposes capture() with mount/continuous/update captureMode; releaseCapture revokes tracked blob URLs. When the shell can't sample the surface (no painted pixels, fully out of bounds, or a non-shell worker mode), it falls back to a validly-encoded placeholder of the right size so uploads/share/<Image> still get a working result \u2014 one fallback path, no toggle.",
1083
+ working: "captureRef (real pixels of the target subtree region via shell snapshot), captureScreen (full app-surface snapshot), tmpfile/base64/data-uri results, png/jpg/webp formats with quality for lossy, explicit width/height as output dims, validateOptions parity with upstream warning text, ViewShot component with capture() ref method, captureMode mount/continuous/update, releaseCapture revoking tracked blob URLs",
1084
+ missing: "true subtree isolation (the shell crops a screen rectangle, so an overlapping sibling/overlay inside the bbox is included \u2014 real captureRef renders only the target's hierarchy), presentation-layer transforms (getBoundingClientRect reports the laid-out rect, not an animated/transformed on-screen position), zip-base64 falls back to plain base64, raw format falls back to png, snapshotContentContainer (full scroll content beyond the viewport \u2014 only the painted surface region is available)"
1085
1085
  }
1086
1086
  ]
1087
1087
  },
@@ -1221,10 +1221,10 @@ var init_registry = __esm({
1221
1221
  versions: [
1222
1222
  {
1223
1223
  range: ">=18.0.0",
1224
- coverage: 0.25,
1225
- note: "noop Firestore reference/query API \u2014 CRUD methods resolve with empty snapshots",
1226
- working: "default firestore(), getFirestore, collection/doc refs, set/update/delete/get, collection.get empty snapshot, onSnapshot unsubscribe, batch commit, FieldValue helpers",
1227
- missing: "real document storage, query filtering/order/limit semantics, realtime sync, transactions, offline persistence"
1224
+ coverage: 0.6,
1225
+ note: "upstream @react-native-firebase/firestore JS runs unchanged against noop RNFBFirestore{,Collection,Document,Transaction}Module native seams \u2014 reads resolve empty snapshots, writes resolve, listeners never emit",
1226
+ working: "full public API surface from the real package: default firestore(), getFirestore, modular helpers, collection/doc references, queries with where/orderBy/limit modifiers, get() returning structurally-correct empty document/query snapshots, set/update/delete/batch resolving, onSnapshot unsubscribe, count/aggregate (0), transactions, write batches, FieldValue/FieldPath/Timestamp/GeoPoint statics, settings/network/persistence/emulator config",
1227
+ missing: "real document storage, query filtering/order/limit results, realtime sync events, transaction read-modify-write semantics, offline persistence"
1228
1228
  }
1229
1229
  ]
1230
1230
  },
@@ -1273,10 +1273,10 @@ var init_registry = __esm({
1273
1273
  versions: [
1274
1274
  {
1275
1275
  range: ">=18.0.0",
1276
- coverage: 0.25,
1277
- note: "noop Storage refs \u2014 upload/download metadata calls resolve without cloud storage",
1278
- working: "default storage(), ref/refFromURL, child refs, put/putFile success snapshots, getDownloadURL stub URI, metadata/list/delete methods",
1279
- missing: "real upload/download bytes, resumable tasks, progress events, Firebase Storage rules/auth integration"
1276
+ coverage: 0.55,
1277
+ note: "upstream @react-native-firebase/storage JS runs unchanged against a noop RNFBStorageModule native seam \u2014 uploads resolve a success snapshot without moving bytes, reads resolve empty metadata/lists",
1278
+ working: "full public API surface from the real package: default storage(), ref/refFromURL, child/parent refs, put/putString/putFile/writeToFile upload+download tasks resolving success snapshots, StorageTask pause/resume/cancel, getDownloadURL stub URI, getMetadata/updateMetadata structurally-correct empty metadata, list/listAll empty results, delete, retry-time constants and setters, emulator config",
1279
+ missing: "real upload/download bytes, resumable tasks, live progress events, Firebase Storage rules/auth integration"
1280
1280
  }
1281
1281
  ]
1282
1282
  },
@@ -1377,10 +1377,10 @@ var init_registry = __esm({
1377
1377
  versions: [
1378
1378
  {
1379
1379
  range: ">=10.0.0",
1380
- coverage: 0.4,
1381
- note: "placeholder map surface; camera and shape layers are noops",
1382
- working: "MapView (placeholder render, ref API), Camera (setCamera/fitBounds), ShapeSource, LineLayer, CircleLayer, SymbolLayer, FillLayer, MarkerView, PointAnnotation, UserLocation, Images, setAccessToken",
1383
- missing: "no tile rendering, no real geo interaction, onMapIdle/onDidFinishLoadingMap fire but shapes are invisible"
1380
+ coverage: 0.7,
1381
+ note: "real maplibre tiles via the host native seam (map-host.ts). the package JS runs from the bundle; RNMBX* host components + the RNMBXModule native module are seam-provided. keyless: mapbox:// styles fall back to OpenFreeMap.",
1382
+ working: "MapView (real vector tiles, pinch-zoom + pan), Camera (centerCoordinate/zoomLevel/bounds/pitch/heading via the stop prop \u2192 camera/fit), ShapeSource (geojson), LineLayer/CircleLayer/FillLayer/SymbolLayer/HeatmapLayer/FillExtrusionLayer/BackgroundLayer (reactStyle \u2192 GL paint/layout), PointAnnotation/MarkerView (host pin), setAccessToken/setWellKnownTileServer (no-op, keyless)",
1383
+ missing: "custom PointAnnotation/MarkerView child views (need host\u2192tenant coordinate projection), imperative queries (queryRenderedFeaturesAtPoint/getZoom/getCenter/takeSnap), raster/vector/image tile sources, terrain/sky/atmosphere, real onCameraChanged/onMapIdle payloads"
1384
1384
  }
1385
1385
  ]
1386
1386
  },
@@ -1479,10 +1479,10 @@ var init_registry = __esm({
1479
1479
  versions: [
1480
1480
  {
1481
1481
  range: ">=1.0.0",
1482
- coverage: 0.7,
1483
- note: "real OpenStreetMap-tile rendering via the standard /export/embed.html iframe (https://wiki.openstreetmap.org/wiki/Export#Embeddable_HTML). bbox computed from initialRegion/region (latitudeDelta + longitudeDelta around the center), marker at the region center, getCamera derives zoom from latitudeDelta via log2(360/delta), getMapBoundaries returns the real region rectangle. Pan/zoom UI is provided by OSM. onMapReady fires on iframe load. Polyline/Polygon/Circle/Marker/Overlay still render null because OSM embed has no overlay API \u2014 would need Leaflet bundled in to add them.",
1484
- working: "MapView with real OSM tile rendering, initialRegion/region honored, getCamera/getMapBoundaries returning real region-derived values, onMapReady firing on iframe load, MapView ref API surface, Marker/Callout/CalloutSubview/AnimatedRegion/MAP_TYPES/PROVIDER_GOOGLE/DEFAULT/enableLatestRenderer exports",
1485
- missing: "overlay components (Polyline/Polygon/Circle/Overlay/Heatmap/Geojson/UrlTile/WMSTile/LocalTile, Marker visuals \u2014 would need Leaflet integration), pan/zoom callbacks (onRegionChange) since we cannot intercept OSM embed events from the parent frame, showsUserLocation tracking, animateToRegion / animateCamera with smooth animation, takeSnapshot, gesture-based onPress / onLongPress"
1482
+ coverage: 0.82,
1483
+ note: "native-seam over a real host-thread maplibre-gl driver (render-worker/map-host.ts), NOT an OSM iframe. the package JS (MapView/Marker/Callout/AnimatedRegion/createFabricMap) runs unchanged; only the RNMapsMapView/RNMapsMarker host components + the RNMapsAirModule TurboModule are ours. maplibre renders keyless OpenFreeMap vector tiles at device scale on the host and ships each frame to the shell painter via the live-frame channel. Markers render as real maplibre circle layers. Pinch (focal-point zoom) + pan are forwarded from the tenant responder. The imperative ref API now works: getCamera/getMapBoundaries/getPointForCoordinate/getCoordinateForPoint answer from web-mercator math over the seam's tracked camera; animateCamera/animateToRegion/setCamera/fitToCoordinates/fitToElements route through codegenNativeCommands \u2192 __dispatchCommandImpl \u2192 the maplibre easeTo/jumpTo/fitBounds host commands. getCamera reflects the LAST COMMANDED camera (programmatic drive), not gesture-driven changes \u2014 there's no onRegionChange feedback channel yet.",
1484
+ working: "MapView with real maplibre vector-tile rendering at device scale, initialRegion/region/camera honored, Marker (real map pins via maplibre circle layers, pinColor), pinch+pan gestures with focal-point zoom, getCamera/getMapBoundaries (web-mercator unproject of the view corners), getPointForCoordinate/getCoordinateForPoint (projection round-trip), getMarkersFrames, animateCamera/animateToRegion/setCamera (easeTo/jumpTo), fitToCoordinates/fitToElements/fitToSuppliedMarkers (fitBounds), onMapReady, MapView ref API, Callout/CalloutSubview/AnimatedRegion/MAP_TYPES/PROVIDER_GOOGLE/DEFAULT/enableLatestRenderer exports",
1485
+ missing: "onRegionChange/onRegionChangeComplete (no feedback channel from the host map back to the tenant, so getCamera is stale after a user gesture), custom Marker child views (coordinate-only pins today), shape overlays (Polyline/Polygon/Circle/Overlay/Heatmap/Geojson/UrlTile/WMSTile/LocalTile render null \u2014 the GL style reconcile path exists in map-host but react-native-maps does not emit it yet, only @rnmapbox/maps does), takeSnapshot (map pixels render host-side \u2014 needs a snapshot round-trip), getAddressFromCoordinates (no reverse geocoder), showsUserLocation tracking, gesture-based onPress/onLongPress"
1486
1486
  }
1487
1487
  ]
1488
1488
  },
@@ -1916,10 +1916,10 @@ var init_registry = __esm({
1916
1916
  versions: [
1917
1917
  {
1918
1918
  range: ">=4.0.0",
1919
- coverage: 0.75,
1920
- note: "Android-only; tracks requested navigation-bar state and modern component/static API shape",
1921
- working: "NavigationBar component with setStyle/setHidden statics, set/get background color, set border/position/behavior/translucent, setVisibilityAsync/getVisibilityAsync, visibility listeners, useVisibility, button style APIs",
1922
- missing: "real Android system-bar mutation, OS contrast enforcement, actual navigation-bar dimensions/insets, emulator-specific behavior"
1919
+ coverage: 0.85,
1920
+ note: "Android-only; full modern component/static API that drives the rendered Android navigation bar through the engine systemBars bridge (visibility, color, style, translucency feed the shell nav-bar + safe-area insets)",
1921
+ working: "NavigationBar component with setStyle/setHidden statics, set/get background color, set border/position/behavior/translucent, setVisibilityAsync/getVisibilityAsync, visibility listeners + emitted change events, useVisibility hook, button style APIs; all setters publish to getEngineConfig().systemBars so android-system-bars.tsx actually shows/hides/tints the rendered nav bar and updates navigation-bar height/insets; non-Android runtimes return the upstream unavailable values",
1922
+ missing: "OS contrast enforcement, exact per-emulator nav-bar pixel dimensions"
1923
1923
  }
1924
1924
  ]
1925
1925
  },
@@ -1929,10 +1929,10 @@ var init_registry = __esm({
1929
1929
  versions: [
1930
1930
  {
1931
1931
  range: ">=14.0.0",
1932
- coverage: 0.75,
1933
- note: 'only `window.print()` is real (printAsync routes through it for browser print dialog). PDF-output APIs are mocked \u2014 printToFileAsync returns a fabricated `uri: "mock://print.pdf"` with empty `base64`, so apps that upload/share/preview the returned URI silently break.',
1934
- working: "upstream validation and concurrency guard, printAsync (real window.print), printToFileAsync (stable URI/numberOfPages/base64 shape), selectPrinterAsync, Orientation enum",
1935
- missing: "printToFileAsync produces `mock://print.pdf` + empty base64 instead of real PDF bytes (would need a worker-side PDF encoder); any consumer that uploads the URI, decodes the base64, or feeds it into a share/preview flow gets a non-PDF. native printer selection / AirPrint sheet unavailable in browser."
1932
+ coverage: 0.8,
1933
+ note: "printAsync routes through real window.print() (browser print dialog). printToFileAsync now produces a REAL, structurally-valid PDF (verified by `file`/macOS as com.adobe.pdf) carrying the html's text content + a real page count, written to the same in-memory cache the expo-file-system stub serves \u2014 so the returned `file:///sootsim-cache/Print/*.pdf` URI resolves to genuine PDF bytes (readAsStringAsync / FormData upload / Sharing / Print preview all work), and base64 (when requested) decodes to those bytes. This replaces the old `mock://print.pdf` dangling URI that broke every consumer. Fidelity caveat: the worker has no WebView render engine, so the PDF reproduces the document's TEXT (paginated, escaped) but NOT CSS/image layout \u2014 a degraded-but-real simulation of iOS's WKWebView\u2192UIPrintPageRenderer output.",
1934
+ working: "upstream validation and concurrency guard, printAsync (real window.print), printToFileAsync (real PDF bytes: %PDF header, catalog/pages tree, xref, paginated text content, real numberOfPages, real file:// uri + base64), selectPrinterAsync, Orientation enum",
1935
+ missing: "CSS/image/font layout fidelity in the generated PDF (text-only render \u2014 needs a worker WebView engine to match WKWebView pixel output), useMarkupFormatter (same text path), printerUrl/AirPrint physical printing, native printer selection sheet (selectPrinterAsync returns undefined)"
1936
1936
  }
1937
1937
  ]
1938
1938
  },
@@ -2185,10 +2185,10 @@ var init_registry = __esm({
2185
2185
  versions: [
2186
2186
  {
2187
2187
  range: ">=4.6.1",
2188
- coverage: 0.65,
2189
- note: "simple CanvasKit blur/liquid-glass approximation for latest native component exports",
2190
- working: "BlurView, VibrancyView, ProgressiveBlurView, LiquidGlassView, LiquidGlassContainer, BlurSwitch, native component export aliases",
2191
- missing: "true native progressive gradient blur, exact iOS 26 liquid glass behavior, Android QmBlurView behavior, native switch event payload fidelity, reduced transparency system setting integration"
2188
+ coverage: 0.7,
2189
+ note: "CanvasKit backdrop-blur + engine liquid-glass material for the full latest component surface; 40-material iOS color table drives per-blurType overlay tint",
2190
+ working: "BlurView/VibrancyView/ProgressiveBlurView render real backdropFilter (sigma from blurAmount) over a per-material rgba overlay from the full iOS material constant table (xlight\u2026systemChromeMaterialDark); LiquidGlassView/LiquidGlassContainer drive the engine _liquidGlass material (glassType, parsed tintColor, opacity, cornerRadius); BlurSwitch is a real interactive switch (value/onValueChange/disabled, switch a11y role+state, track color); all native component export aliases",
2191
+ missing: "progressive gradient direction (direction/startOffset accepted but ignored \u2014 renders uniform blur), exact iOS 26 liquid glass behavior, Android QmBlurView behavior, reduced-transparency system-setting fallback color"
2192
2192
  }
2193
2193
  ]
2194
2194
  },
@@ -2276,10 +2276,10 @@ var init_registry = __esm({
2276
2276
  versions: [
2277
2277
  {
2278
2278
  range: ">=6.0.0",
2279
- coverage: 0.1,
2280
- note: "all zip/unzip operations are noops \u2014 no browser ZIP impl",
2281
- working: "API shape present (zip, unzip, unzipAssets, zipWithPassword, subscribe, isPasswordProtected, getUncompressedSize), path constants, subscribe returns removable listener",
2282
- missing: "zip (noop), unzip (noop), unzipAssets (noop), zipWithPassword (noop), isPasswordProtected always false, getUncompressedSize always 0 \u2014 no actual compression/decompression"
2279
+ coverage: 0.6,
2280
+ note: "real zip/unzip via fflate over the shared compat VFS; only password/AES variants unsupported (no browser zip-crypto codec)",
2281
+ working: "zip/zipFolder/zipFiles (real DEFLATE, basename + folder-relative entries), unzip (real inflate to VFS), isPasswordProtected (reads GP encryption bit), getUncompressedSize (sums central-directory sizes, -1 on parse error), zipArchiveProgressEvent (0%/per-entry/100%) into subscribe()",
2282
+ missing: "zipWithPassword / unzipWithPassword / zipFilesWithPassword / zipFolderWithPassword \u2014 fflate has no ZipCrypto/AES codec, so these reject with the native zip_error/unzip_error shape; unzipAssets (no native asset bundle); unzip of an encrypted archive rejects"
2283
2283
  }
2284
2284
  ]
2285
2285
  },
@@ -9343,6 +9343,408 @@ var init_registry = __esm({
9343
9343
  missing: "no HealthKit data store, permission prompt, authorization state, observer events, native input validation, or persisted writes in the browser simulator"
9344
9344
  }
9345
9345
  ]
9346
+ },
9347
+ // ── lane A2: ranks 751-1000, native-filtered zero-scorers lifted ──
9348
+ // pure-JS mis-flags: no real native module on iOS, run as-is from the bundle.
9349
+ "rn-css": {
9350
+ category: "UI Components",
9351
+ stubType: "works",
9352
+ versions: [
9353
+ {
9354
+ range: ">=2.0.0",
9355
+ coverage: 0.95,
9356
+ note: "pure JS \u2014 styled-components-style css template tag over RN primitives; no native modules, runs as-is from the bundle",
9357
+ working: "styled.View/Text/etc, css template literals, theme provider, media queries, units, inline style resolution",
9358
+ missing: "nothing native to provide \u2014 any gap is the pure-JS surface itself, not a missing native seam"
9359
+ }
9360
+ ]
9361
+ },
9362
+ "react-native-gauge": {
9363
+ category: "UI Components",
9364
+ stubType: "works",
9365
+ versions: [
9366
+ {
9367
+ range: ">=1.0.0",
9368
+ coverage: 0.95,
9369
+ note: "pure JS \u2014 composes react-native-progress + masked-view + expo-linear-gradient + Animated; no native modules, runs as-is from the bundle",
9370
+ working: "Gauge component, animated needle, gradient fill, triangle tip/base, circle indicator \u2014 all JS-rendered",
9371
+ missing: "no native module \u2014 fidelity is bounded only by the underlying JS rendering libraries"
9372
+ }
9373
+ ]
9374
+ },
9375
+ "react-native-material-you-colors": {
9376
+ category: "UI Components",
9377
+ stubType: "works",
9378
+ versions: [
9379
+ {
9380
+ range: ">=1.0.0",
9381
+ coverage: 0.95,
9382
+ note: "pure JS on iOS \u2014 the Monet palette generator runs in JS; the native module is android-only (isMaterialYouSupported=false, getPalletteFromAndroid=null on iOS)",
9383
+ working: "MaterialYou.generatePaletteFromColor, createThemeContext, useMaterialYouTheme, Palette/Monet algorithm, isSupported=false on iOS",
9384
+ missing: "dynamic system-wallpaper palette extraction (android-12+ only) \u2014 iOS has no system Material You source, matching upstream"
9385
+ }
9386
+ ]
9387
+ },
9388
+ "react-native-shake-event": {
9389
+ category: "Device & Platform",
9390
+ stubType: "works",
9391
+ versions: [
9392
+ {
9393
+ range: ">=1.0.0",
9394
+ coverage: 0.9,
9395
+ note: "pure JS \u2014 addEventListener wires a DeviceEventEmitter listener for 'ShakeEvent'; no native module method is called, so it runs as-is and the shake event simply never fires in the simulator (correct: no shake gesture)",
9396
+ working: "RNShakeEvent.addEventListener/removeEventListener for the shake event",
9397
+ missing: "no device shake gesture in the simulator, so the listener never fires \u2014 matching the absence of a real accelerometer"
9398
+ }
9399
+ ]
9400
+ },
9401
+ // native-seam only: real package JS runs from the bundle; SootSim registers the
9402
+ // named TurboModule/NativeModule as a structurally-correct noop.
9403
+ // rudderstack device-mode analytics integrations — each is a one-line
9404
+ // `NativeModules.RudderIntegration<Name>ReactNative.setup()` wrapper. delivery
9405
+ // intentionally absent per docs/compat-scoring.md.
9406
+ "@rudderstack/rudder-integration-facebook-react-native": {
9407
+ category: "Analytics",
9408
+ stubType: "native",
9409
+ versions: [
9410
+ {
9411
+ range: ">=1.0.0",
9412
+ coverage: 0.7,
9413
+ note: "native-seam only \u2014 the setup() wrapper runs from the bundle; RudderIntegrationFacebookReactNative.setup() is a noop. delivery intentionally absent in browser preview.",
9414
+ working: "default setup() resolves; device-mode registration accepted without throwing",
9415
+ missing: "no Facebook SDK delivery \u2014 same constraint as every analytics integration in browser preview"
9416
+ }
9417
+ ]
9418
+ },
9419
+ "@rudderstack/rudder-integration-clevertap-react-native": {
9420
+ category: "Analytics",
9421
+ stubType: "native",
9422
+ versions: [
9423
+ {
9424
+ range: ">=1.0.0",
9425
+ coverage: 0.7,
9426
+ note: "native-seam only \u2014 the setup() wrapper runs from the bundle; RudderIntegrationCleverTapReactNative.setup() is a noop. delivery intentionally absent in browser preview.",
9427
+ working: "default setup() resolves; device-mode registration accepted without throwing",
9428
+ missing: "no CleverTap SDK delivery \u2014 same constraint as every analytics integration in browser preview"
9429
+ }
9430
+ ]
9431
+ },
9432
+ "@rudderstack/rudder-integration-singular-react-native": {
9433
+ category: "Analytics",
9434
+ stubType: "native",
9435
+ versions: [
9436
+ {
9437
+ range: ">=1.0.0",
9438
+ coverage: 0.7,
9439
+ note: "native-seam only \u2014 the setup() wrapper runs from the bundle; RudderIntegrationSingularReactNative.setup() is a noop. delivery intentionally absent in browser preview.",
9440
+ working: "default setup() resolves; device-mode registration accepted without throwing",
9441
+ missing: "no Singular SDK delivery \u2014 same constraint as every analytics integration in browser preview"
9442
+ }
9443
+ ]
9444
+ },
9445
+ "@rudderstack/rudder-integration-braze-react-native": {
9446
+ category: "Analytics",
9447
+ stubType: "native",
9448
+ versions: [
9449
+ {
9450
+ range: ">=1.0.0",
9451
+ coverage: 0.7,
9452
+ note: "native-seam only \u2014 the setup() wrapper runs from the bundle; RudderIntegrationBrazeReactNative.setup() is a noop. delivery intentionally absent in browser preview.",
9453
+ working: "default setup() resolves; device-mode registration accepted without throwing",
9454
+ missing: "no Braze SDK delivery \u2014 same constraint as every analytics integration in browser preview"
9455
+ }
9456
+ ]
9457
+ },
9458
+ "@rudderstack/rudder-integration-amplitude-react-native": {
9459
+ category: "Analytics",
9460
+ stubType: "native",
9461
+ versions: [
9462
+ {
9463
+ range: ">=1.0.0",
9464
+ coverage: 0.7,
9465
+ note: "native-seam only \u2014 the setup() wrapper runs from the bundle; RudderIntegrationAmplitudeReactNative.setup() is a noop. delivery intentionally absent in browser preview.",
9466
+ working: "default setup() resolves; device-mode registration accepted without throwing",
9467
+ missing: "no Amplitude SDK delivery \u2014 same constraint as every analytics integration in browser preview"
9468
+ }
9469
+ ]
9470
+ },
9471
+ "react-native-apple-ads-attribution": {
9472
+ category: "Analytics",
9473
+ stubType: "native",
9474
+ versions: [
9475
+ {
9476
+ range: ">=1.0.0",
9477
+ coverage: 0.7,
9478
+ note: "native-seam only \u2014 the AppleAdsAttribution JS class runs from the bundle; RNAppleAdsAttribution.getAttributionData() resolves {} (exactly what the real iOS module returns on the no-attribution/error path). attribution data intentionally absent.",
9479
+ working: "getAttributionData() resolves an empty object",
9480
+ missing: "no Apple Search Ads attribution payload in the simulator \u2014 matches the real iOS module resolving {} when attribution is unavailable"
9481
+ }
9482
+ ]
9483
+ },
9484
+ "@logicwind/react-native-matomo-tracker": {
9485
+ category: "Analytics",
9486
+ stubType: "native",
9487
+ versions: [
9488
+ {
9489
+ range: ">=1.0.0",
9490
+ coverage: 0.7,
9491
+ note: "native-seam only \u2014 the tracker JS API runs from the bundle; ReactNativeMatomoTracker exposes all track*/createTracker/session/dimension methods as noops. delivery intentionally absent in browser preview.",
9492
+ working: "createTracker, trackScreen/Event/Goal/Search/Outlink/Download/Impression/Interaction/Media/Campaign/CustomDimension, trackDispatch, set/disable/enable tracking, setUserId/VisitorId, startSession, setLogger",
9493
+ missing: "no Matomo delivery \u2014 same constraint as every analytics SDK in browser preview"
9494
+ }
9495
+ ]
9496
+ },
9497
+ "react-native-appearance-control": {
9498
+ category: "Device & Platform",
9499
+ stubType: "native",
9500
+ versions: [
9501
+ {
9502
+ range: ">=1.0.0",
9503
+ coverage: 0.7,
9504
+ note: "native-seam only \u2014 the JS wrapper runs from the bundle; the AppearanceControl TurboModule accepts setAppearanceDark/Light/System as noops",
9505
+ working: "setAppearanceDark(), setAppearanceLight(), setAppearanceSystem() resolve",
9506
+ missing: "no runtime override of the host appearance in the simulator \u2014 calls resolve without changing the canvas color scheme"
9507
+ }
9508
+ ]
9509
+ },
9510
+ "rn-date-format": {
9511
+ category: "Device & Platform",
9512
+ stubType: "native",
9513
+ versions: [
9514
+ {
9515
+ range: ">=1.0.0",
9516
+ coverage: 0.7,
9517
+ note: "native-seam only \u2014 the JS wrapper runs from the bundle; the DeviceTimeFormat TurboModule's is24HourFormat() returns false (the iOS-default 12-hour reading)",
9518
+ working: "is24HourFormat() returns a boolean",
9519
+ missing: "no per-device locale time-format reading in the simulator \u2014 returns the 12-hour default"
9520
+ }
9521
+ ]
9522
+ },
9523
+ "react-native-app-review": {
9524
+ category: "Device & Platform",
9525
+ stubType: "native",
9526
+ versions: [
9527
+ {
9528
+ range: ">=1.0.0",
9529
+ coverage: 0.7,
9530
+ note: "native-seam only \u2014 the JS wrapper runs from the bundle; the AppReview native module exposes launch() as a noop (no StoreKit review prompt in the simulator)",
9531
+ working: "AppReview.launch() accepted",
9532
+ missing: "no SKStoreReviewController prompt in the simulator \u2014 launch() is a noop, matching the OS rate-limiting/no-op the real API has in most contexts"
9533
+ }
9534
+ ]
9535
+ },
9536
+ // ── Lane A1b: pure-JS mis-flagged (no stub — real package runs from bundle) ──
9537
+ "react-native-cardview": {
9538
+ category: "UI Components",
9539
+ stubType: "works",
9540
+ versions: [
9541
+ {
9542
+ range: ">=1.0.0",
9543
+ coverage: 0.95,
9544
+ note: "pure JS on iOS \u2014 CardView.ios.js renders a plain <View> with shadow/borderRadius styles; the native RNCardView is Android-only, never touched on the iOS-shaped simulator",
9545
+ working: "CardView with cardElevation/cornerRadius/shadowOpacity mapped to View shadow styles"
9546
+ }
9547
+ ]
9548
+ },
9549
+ "@klarna/react-native-vector-drawable": {
9550
+ category: "Image",
9551
+ stubType: "works",
9552
+ versions: [
9553
+ {
9554
+ range: ">=0.1.0",
9555
+ coverage: 0.95,
9556
+ note: "pure JS on iOS \u2014 index.ios.js is `export { View as default } from 'react-native'`; the native RNVectorDrawable component is Android-only and never resolved on the simulator",
9557
+ working: "VectorDrawable degrades to a plain RN View on iOS (matching upstream Android-only native)"
9558
+ }
9559
+ ]
9560
+ },
9561
+ "react-native-image-mapper": {
9562
+ category: "UI Components",
9563
+ stubType: "works",
9564
+ versions: [
9565
+ {
9566
+ range: ">=0.1.0",
9567
+ coverage: 0.95,
9568
+ note: "pure JS \u2014 image overlay built from ImageBackground + TouchableOpacity hotspots, no ios/ or android/ native code at all",
9569
+ working: "ImageMapper, clickable mapping areas over an image, onPress per area"
9570
+ }
9571
+ ]
9572
+ },
9573
+ "react-native-basic-carousel": {
9574
+ category: "UI Components",
9575
+ stubType: "works",
9576
+ versions: [
9577
+ {
9578
+ range: ">=1.0.0",
9579
+ coverage: 0.95,
9580
+ note: "pure JS \u2014 the Carousel component is an Animated.FlatList with pagination; the package only references NativeModules.RNBasicCarouselModule for a vestigial default export (undefined on the simulator, load-safe)",
9581
+ working: "Carousel (data/renderItem/itemWidth/pagination/autoplay), snap-to-item, paging, pagination dots"
9582
+ }
9583
+ ]
9584
+ },
9585
+ "react-native-insta-story": {
9586
+ category: "UI Components",
9587
+ stubType: "works",
9588
+ versions: [
9589
+ {
9590
+ range: ">=0.1.0",
9591
+ coverage: 0.95,
9592
+ note: 'pure JS \u2014 Story component built on modal + swipe gestures; the bundled InstaStory native module is an unimplemented placeholder (a red "*****" UILabel) that the JS never imports',
9593
+ working: "InstaStory list, per-story progress, swipe between users, tap to advance"
9594
+ }
9595
+ ]
9596
+ },
9597
+ "react-native-in-app-updates": {
9598
+ category: "Device & Platform",
9599
+ stubType: "works",
9600
+ versions: [
9601
+ {
9602
+ range: ">=1.0.0",
9603
+ coverage: 0.95,
9604
+ note: 'load-safe on iOS \u2014 upstream guards the missing native module with a Proxy and every method short-circuits to `Promise.reject("only available on Android")`, exactly the clean unavailable state an app expects on iOS',
9605
+ working: "checkNeedsUpdate / startUpdate / addStatusUpdateListener importable; iOS calls reject with the Android-only error (matching real device behavior)"
9606
+ }
9607
+ ]
9608
+ },
9609
+ // ── Lane A1b: native-seam only (real pure-JS package runs from the bundle) ──
9610
+ "react-native-xxhash": {
9611
+ category: "Device & Platform",
9612
+ stubType: "native",
9613
+ versions: [
9614
+ {
9615
+ range: ">=1.0.0",
9616
+ coverage: 0.95,
9617
+ note: "native-seam only \u2014 the real JS runs from the bundle; the xxhash JSI install() mounts faithful XXH3-64/128 implementations (bit-exact vs upstream xxhash 0.8.3)",
9618
+ working: "hash64 / hash128 (globalThis.__xxhash64 / __xxhash128) \u2014 deterministic, matches the native C++ digest"
9619
+ }
9620
+ ]
9621
+ },
9622
+ "react-native-ulid-jsi": {
9623
+ category: "Device & Platform",
9624
+ stubType: "native",
9625
+ versions: [
9626
+ {
9627
+ range: ">=1.0.0",
9628
+ coverage: 0.95,
9629
+ note: "native-seam only \u2014 the real JS runs from the bundle; the RNUlid JSI install() mounts a faithful ULID spec implementation (Crockford base32, 48-bit time, monotonic)",
9630
+ working: "getUlid / checkIsValid / decodeTime (globalThis.__getUlid / __checkIsValid / __decodeTime)"
9631
+ }
9632
+ ]
9633
+ },
9634
+ "react-native-nitro-buffer": {
9635
+ category: "Device & Platform",
9636
+ stubType: "native",
9637
+ versions: [
9638
+ {
9639
+ range: ">=1.0.0",
9640
+ coverage: 0.9,
9641
+ note: "native-seam only \u2014 the Buffer JS class runs from the bundle; the NitroBuffer Nitro hybrid mirrors the upstream C++ byte ops faithfully (deterministic, no native dependency)",
9642
+ working: "alloc/allocUnsafe, byteLength, write/decode (utf8/utf16le/latin1/ascii/hex/base64/base64url), compare, fill, indexOf/lastIndexOf"
9643
+ }
9644
+ ]
9645
+ },
9646
+ "react-native-nitro-geocoder": {
9647
+ category: "Maps",
9648
+ stubType: "native",
9649
+ versions: [
9650
+ {
9651
+ range: ">=1.0.0",
9652
+ coverage: 0.7,
9653
+ note: "native-seam only \u2014 the JS runs from the bundle; the NitroGeocoder hybrid implements calculateDistance (faithful Haversine) and reports geocoding unavailable (no CLGeocoder in the simulator)",
9654
+ working: "calculateDistance (real great-circle distance), isGeocodingAvailable=false",
9655
+ missing: "geocode / reverseGeocode reject (CLGeocoder unavailable in the simulator \u2014 matches the no-results native path)"
9656
+ }
9657
+ ]
9658
+ },
9659
+ "react-native-video-duration": {
9660
+ category: "Media",
9661
+ stubType: "native",
9662
+ versions: [
9663
+ {
9664
+ range: ">=0.1.0",
9665
+ coverage: 0.7,
9666
+ note: "native-seam only \u2014 the JS runs from the bundle; VideoDuration.getVideoDuration() resolves 0 (no AVAsset in the simulator; upstream maps non-positive durations to 0)",
9667
+ working: "getVideoDuration importable + resolves cleanly",
9668
+ missing: "no real AVFoundation duration probe in the simulator"
9669
+ }
9670
+ ]
9671
+ },
9672
+ "@meedwire/react-native-image-rotate": {
9673
+ category: "Image",
9674
+ stubType: "native",
9675
+ versions: [
9676
+ {
9677
+ range: ">=0.1.0",
9678
+ coverage: 0.7,
9679
+ note: "native-seam only \u2014 the JS runs from the bundle; ImageRotate.rotate() returns the source content unchanged (no native pixel rotation in the simulator)",
9680
+ working: "rotate({content,type,angle}) resolves a string uri",
9681
+ missing: "pixels are not actually rotated in the simulator"
9682
+ }
9683
+ ]
9684
+ },
9685
+ "react-native-tiktok": {
9686
+ category: "Auth & Payments",
9687
+ stubType: "native",
9688
+ versions: [
9689
+ {
9690
+ range: ">=0.1.0",
9691
+ coverage: 0.7,
9692
+ note: "native-seam only \u2014 the JS runs from the bundle; Tiktok.authorize() invokes its callback with the no-auth shape (no TikTok OAuth handoff in the simulator)",
9693
+ working: "authorize importable + callback invoked, Scopes constants",
9694
+ missing: "no real TikTok login (the simulator has no native TikTok SDK app-switch)"
9695
+ }
9696
+ ]
9697
+ },
9698
+ "react-native-store-age-signals-native-modules": {
9699
+ category: "Device & Platform",
9700
+ stubType: "native",
9701
+ versions: [
9702
+ {
9703
+ range: ">=0.1.0",
9704
+ coverage: 0.7,
9705
+ note: "native-seam only \u2014 the JS runs from the bundle; the native module resolves the all-null declared-age-range result (the Declared Age Range SDK is unavailable, matching upstream non-iOS fallback)",
9706
+ working: "requestIOSDeclaredAgeRange / getAndroidPlayAgeRangeStatus resolve the documented null result shape",
9707
+ missing: "no real StoreKit age-signal lookup in the simulator"
9708
+ }
9709
+ ]
9710
+ },
9711
+ "react-native-widget-picker": {
9712
+ category: "Device & Platform",
9713
+ stubType: "native",
9714
+ versions: [
9715
+ {
9716
+ range: ">=0.1.0",
9717
+ coverage: 0.85,
9718
+ note: "native-seam only \u2014 the JS runs from the bundle; WidgetPickerModule reports unsupported (home-screen widget pinning is Android-only, matching upstream index.ios.ts)",
9719
+ working: "isRequestPinAppWidgetSupported()=false, requestPinAppWidget() resolves the not-supported message"
9720
+ }
9721
+ ]
9722
+ },
9723
+ "react-native-appstack-sdk": {
9724
+ category: "Analytics",
9725
+ stubType: "native",
9726
+ versions: [
9727
+ {
9728
+ range: ">=0.1.0",
9729
+ coverage: 0.85,
9730
+ note: "native-seam only \u2014 the JS runs from the bundle; the AppstackReactNative attribution/event TurboModule is a load-safe noop. delivery intentionally absent in the simulator",
9731
+ working: "configure, sendEvent, enableAppleAdsAttribution, getAppstackId, isSdkDisabled, getAttributionParams (all resolve cleanly)",
9732
+ missing: "no network attribution delivery from the simulator"
9733
+ }
9734
+ ]
9735
+ },
9736
+ "react-native-fcm": {
9737
+ category: "Notifications",
9738
+ stubType: "native",
9739
+ versions: [
9740
+ {
9741
+ range: ">=0.1.0",
9742
+ coverage: 0.85,
9743
+ note: "native-seam only \u2014 the real JS runs from the bundle; the RNFIRMessaging native module is a load-safe noop (synthetic token, no events). delivery intentionally absent in the simulator",
9744
+ working: "getFCMToken/getAPNSToken (synthetic token), local-notification + topic + badge plumbing importable and resolve cleanly",
9745
+ missing: "no real push delivery or token registration from the simulator"
9746
+ }
9747
+ ]
9346
9748
  }
9347
9749
  };
9348
9750
  }
@@ -9632,815 +10034,47 @@ var init_native_dev_bundle_url = __esm({
9632
10034
  }
9633
10035
  });
9634
10036
 
9635
- // scripts/demo-app-registry.ts
9636
- var demo_app_registry_exports = {};
9637
- __export(demo_app_registry_exports, {
9638
- APPS: () => APPS
10037
+ // scripts/optional-demo-registry.ts
10038
+ var optional_demo_registry_exports = {};
10039
+ __export(optional_demo_registry_exports, {
10040
+ loadOptionalDemoApps: () => loadOptionalDemoApps,
10041
+ optionalDemoAppsSync: () => optionalDemoAppsSync
9639
10042
  });
9640
- function findWorkspaceRoot(startDir) {
9641
- let dir = startDir;
9642
- while (true) {
9643
- if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "pnpm-workspace.yaml")) || (0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "turbo.json")) || (0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "nx.json")) || (0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "lerna.json"))) {
9644
- return dir;
9645
- }
9646
- const packageJsonPath = (0, import_node_path.join)(dir, "package.json");
9647
- if ((0, import_node_fs.existsSync)(packageJsonPath)) {
9648
- try {
9649
- const pkg = JSON.parse((0, import_node_fs.readFileSync)(packageJsonPath, "utf8"));
9650
- if (pkg.workspaces) return dir;
9651
- } catch {
9652
- }
9653
- }
9654
- const parent = (0, import_node_path.dirname)(dir);
9655
- if (parent === dir) return null;
9656
- dir = parent;
9657
- }
9658
- }
9659
- function resolveWorkspaceScriptPath(workspaceRelativePath, packageRelativePath) {
9660
- const workspaceRoot = findWorkspaceRoot(process.cwd());
9661
- const candidates = [
9662
- workspaceRoot ? (0, import_node_path.resolve)(workspaceRoot, workspaceRelativePath) : null,
9663
- (0, import_node_path.resolve)(process.cwd(), workspaceRelativePath),
9664
- (0, import_node_path.resolve)(process.cwd(), packageRelativePath)
9665
- ].filter((candidate) => Boolean(candidate));
9666
- for (const candidate of candidates) {
9667
- if ((0, import_node_fs.existsSync)(candidate)) return candidate;
9668
- }
9669
- return candidates[0] ?? (0, import_node_path.resolve)(process.cwd(), workspaceRelativePath);
9670
- }
9671
- function parseEnvFile(filePath) {
9672
- if (!(0, import_node_fs.existsSync)(filePath)) return {};
9673
- const env = {};
9674
- const source = (0, import_node_fs.readFileSync)(filePath, "utf8");
9675
- for (const rawLine of source.split(/\r?\n/)) {
9676
- const line = rawLine.trim();
9677
- if (!line || line.startsWith("#")) continue;
9678
- const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
9679
- if (!match) continue;
9680
- let value = match[2].trim();
9681
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
9682
- value = value.slice(1, -1);
9683
- }
9684
- env[match[1]] = value;
9685
- }
9686
- return env;
9687
- }
9688
- function isUsableUniswapEnvValue(value) {
9689
- if (!value) return false;
9690
- const trimmed = value.trim();
9691
- if (!trimmed) return false;
9692
- if (trimmed.includes(UNISWAP_PLACEHOLDER)) return false;
9693
- if (trimmed === "TRADING_API_KEY" || trimmed === "UNISWAP_API_KEY") return false;
9694
- return true;
9695
- }
9696
- function pickEnvValue(sources, keys) {
9697
- for (const source of sources) {
9698
- for (const key of keys) {
9699
- const value = source[key];
9700
- if (isUsableUniswapEnvValue(value)) return value.trim();
9701
- }
9702
- }
9703
- return void 0;
9704
- }
9705
- function resolveUniswapDemoEnv() {
9706
- const localEnv = parseEnvFile(UNISWAP_ENV_LOCAL_FILE);
9707
- const webEnv = parseEnvFile((0, import_node_path.join)(UNISWAP_REPO_DIR, "apps/web/.env"));
9708
- const sources = [
9709
- process.env,
9710
- localEnv,
9711
- webEnv
9712
- ];
9713
- const env = {};
9714
- const bindings = [
9715
- ["AMPLITUDE_PROXY_URL_OVERRIDE", ["REACT_APP_AMPLITUDE_PROXY_URL"]],
9716
- ["QUICKNODE_ENDPOINT_NAME", ["REACT_APP_QUICKNODE_ENDPOINT_NAME"]],
9717
- ["QUICKNODE_ENDPOINT_TOKEN", ["REACT_APP_QUICKNODE_ENDPOINT_TOKEN"]],
9718
- ["INFURA_KEY", ["REACT_APP_INFURA_KEY"]],
9719
- ["STATSIG_API_KEY", ["REACT_APP_STATSIG_API_KEY"]],
9720
- ["STATSIG_PROXY_URL_OVERRIDE", ["REACT_APP_STATSIG_PROXY_URL"]],
9721
- ["WALLETCONNECT_PROJECT_ID", ["REACT_APP_WALLET_CONNECT_PROJECT_ID"]],
9722
- ["WALLETCONNECT_PROJECT_ID_BETA", ["REACT_APP_WALLET_CONNECT_PROJECT_ID"]],
9723
- ["WALLETCONNECT_PROJECT_ID_DEV", ["REACT_APP_WALLET_CONNECT_PROJECT_ID"]],
9724
- ["TRADING_API_KEY", ["REACT_APP_TRADING_API_KEY"]],
9725
- ["UNISWAP_API_KEY", []]
9726
- ];
9727
- for (const [target, aliases] of bindings) {
9728
- const value = pickEnvValue(sources, [target, ...aliases]);
9729
- if (!value) continue;
9730
- env[target] = value;
9731
- for (const alias of aliases) {
9732
- env[alias] = value;
9733
- }
9734
- }
9735
- const hasPrivateGatewayKeys = isUsableUniswapEnvValue(env.TRADING_API_KEY) && isUsableUniswapEnvValue(env.UNISWAP_API_KEY);
9736
- if (!hasPrivateGatewayKeys) {
9737
- const publicGraphqlUrl = pickEnvValue(sources, ["GRAPHQL_URL_OVERRIDE", "REACT_APP_AWS_API_ENDPOINT"]) || "https://interface.gateway.uniswap.org/v1/graphql";
9738
- env.API_BASE_URL_OVERRIDE = "https://interface.gateway.uniswap.org";
9739
- env.API_BASE_URL_V2_OVERRIDE = "https://interface.gateway.uniswap.org/v2";
9740
- env.GRAPHQL_URL_OVERRIDE = publicGraphqlUrl;
9741
- env.TRADING_API_URL_OVERRIDE = "https://trading-api-labs.interface.gateway.uniswap.org";
9742
- env.FOR_API_URL_OVERRIDE = "https://for.interface.gateway.uniswap.org/v2/FOR.v1.FORService";
9743
- }
9744
- return env;
9745
- }
9746
- function ensureUniswapDemoEnvLocal() {
9747
- const existingSource = (0, import_node_fs.existsSync)(UNISWAP_ENV_LOCAL_FILE) ? (0, import_node_fs.readFileSync)(UNISWAP_ENV_LOCAL_FILE, "utf8") : "";
9748
- if (existingSource && !existingSource.includes(UNISWAP_DEMO_ENV_MARKER)) {
9749
- return;
9750
- }
9751
- const env = resolveUniswapDemoEnv();
9752
- const lines = [UNISWAP_DEMO_ENV_MARKER];
9753
- for (const [key, value] of Object.entries(env).sort(([a], [b]) => a.localeCompare(b))) {
9754
- lines.push(`${key}=${JSON.stringify(value)}`);
9755
- }
9756
- lines.push("");
9757
- (0, import_node_fs.writeFileSync)(UNISWAP_ENV_LOCAL_FILE, `${lines.join("\n")}
9758
- `);
9759
- }
9760
- function ensureUniswapForceUpgradePatched() {
9761
- const hookNeedle = `export function useForceUpgradeStatus(): ForceUpgradeStatus {
9762
- `;
9763
- const hookPatch = ` // sootsim demo: bypass the force-upgrade gate during local engine demos.
9764
- return 'not-required'
9765
-
9766
- `;
9767
- const hookLegacyPatch = ` // sootsim demo: bypass the force-upgrade gate during local engine demos.
9768
- if (process.env.${UNISWAP_FORCE_UPGRADE_PATCH_MARKER} === 'true') {
9769
- return 'not-required'
9770
- }
9771
-
9772
- `;
9773
- const notificationNeedle = ` const getForceUpgradeStatus = (): ForceUpgradeStatus => {
9774
- `;
9775
- const notificationPatch = ` // sootsim demo: bypass the force-upgrade gate during local engine demos.
9776
- return 'not-required'
9777
-
9778
- `;
9779
- const notificationLegacyPatch = ` // sootsim demo: bypass the force-upgrade gate during local engine demos.
9780
- if (process.env.${UNISWAP_FORCE_UPGRADE_PATCH_MARKER} === 'true') {
9781
- return 'not-required'
9782
- }
9783
-
9784
- `;
9785
- const patchWithMigration = (filePath, needle, patch, legacyPatch) => {
9786
- const source = (0, import_node_fs.readFileSync)(filePath, "utf8");
9787
- if (source.includes(patch)) return;
9788
- if (source.includes(legacyPatch)) {
9789
- (0, import_node_fs.writeFileSync)(filePath, source.replace(legacyPatch, patch));
9790
- return;
9791
- }
9792
- if (!source.includes(needle)) {
9793
- throw new Error(
9794
- `uniswap demo patch failed: expected snippet not found in ${filePath}`
9795
- );
9796
- }
9797
- (0, import_node_fs.writeFileSync)(filePath, source.replace(needle, `${needle}${patch}`));
9798
- };
9799
- patchWithMigration(
9800
- UNISWAP_FORCE_UPGRADE_HOOK_FILE,
9801
- hookNeedle,
9802
- hookPatch,
9803
- hookLegacyPatch
9804
- );
9805
- patchWithMigration(
9806
- UNISWAP_FORCE_UPGRADE_NOTIFICATION_FILE,
9807
- notificationNeedle,
9808
- notificationPatch,
9809
- notificationLegacyPatch
9810
- );
9811
- }
9812
- function ensureJoplinWatchmanConfigs() {
9813
- if (!(0, import_node_fs.existsSync)(JOPLIN_DIR)) return;
9814
- for (const rel of JOPLIN_WATCH_ROOTS) {
9815
- const dir = (0, import_node_path.join)(JOPLIN_DIR, rel);
9816
- if (!(0, import_node_fs.existsSync)(dir)) continue;
9817
- const cfg = (0, import_node_path.join)(dir, ".watchmanconfig");
9818
- if (!(0, import_node_fs.existsSync)(cfg)) (0, import_node_fs.writeFileSync)(cfg, "{}\n");
9819
- }
9820
- }
9821
- function ensureJoplinBuilt() {
9822
- if (!(0, import_node_fs.existsSync)(JOPLIN_DIR)) return;
9823
- const missing = JOPLIN_BUILD_SENTINELS.filter(
9824
- (rel) => !(0, import_node_fs.existsSync)((0, import_node_path.join)(JOPLIN_DIR, rel))
9825
- );
9826
- if (missing.length === 0) return;
9827
- const { execSync } = require("node:child_process");
9828
- execSync("yarn buildParallel", {
9829
- cwd: JOPLIN_DIR,
9830
- stdio: "inherit",
9831
- env: { ...process.env, NO_FLIPPER: "1", CI: "" }
9832
- });
9833
- const stillMissing = JOPLIN_BUILD_SENTINELS.filter(
9834
- (rel) => !(0, import_node_fs.existsSync)((0, import_node_path.join)(JOPLIN_DIR, rel))
9835
- );
9836
- if (stillMissing.length > 0) {
9837
- throw new Error(
9838
- `joplin demo: yarn buildParallel did not produce: ${stillMissing.join(", ")}`
9839
- );
9840
- }
9841
- }
9842
- function runRainbowSetupCommand(command, cwd) {
9843
- const { execSync } = require("node:child_process");
9844
- execSync(command, {
9845
- cwd,
9846
- stdio: "inherit",
9847
- env: {
9848
- ...process.env,
9849
- METADATA_BASE_URL: RAINBOW_METADATA_BASE_URL,
9850
- RAINBOW_RELAY_QUOTE_SIGNER: process.env.RAINBOW_RELAY_QUOTE_SIGNER ?? RAINBOW_DEMO_QUOTE_SIGNER
9851
- }
9852
- });
9853
- }
9854
- function ensureRainbowGraphqlConfig() {
9855
- if (!(0, import_node_fs.existsSync)(RAINBOW_GRAPHQL_DIR)) return;
9856
- const source = (0, import_node_fs.existsSync)(RAINBOW_GRAPHQL_CONFIG_FILE) ? (0, import_node_fs.readFileSync)(RAINBOW_GRAPHQL_CONFIG_FILE, "utf8") : "";
9857
- if (source.includes(RAINBOW_PUBLIC_ENS_GRAPHQL_URL) && source.includes(RAINBOW_METADATA_BASE_URL)) {
9858
- return;
9859
- }
9860
- (0, import_node_fs.writeFileSync)(
9861
- RAINBOW_GRAPHQL_CONFIG_FILE,
9862
- `exports.config = {
9863
- ens: {
9864
- __name: 'ens',
9865
- document: './queries/ens.graphql',
9866
- schema: {
9867
- method: 'POST',
9868
- url: '${RAINBOW_PUBLIC_ENS_GRAPHQL_URL}',
9869
- },
9870
- },
9871
- metadata: {
9872
- __name: 'metadata',
9873
- document: './queries/metadata.graphql',
9874
- schema: { method: 'GET', url: '${RAINBOW_METADATA_BASE_URL}/v1/graph' },
9875
- },
9876
- metadataPOST: {
9877
- __name: 'metadataPOST',
9878
- document: './queries/metadata.graphql',
9879
- schema: { method: 'POST', url: '${RAINBOW_METADATA_BASE_URL}/v1/graph' },
9880
- },
9881
- arc: {
9882
- __name: 'arc',
9883
- document: './queries/arc.graphql',
9884
- schema: {
9885
- method: 'GET',
9886
- url: 'https://arc-graphql.rainbow.me/graphql',
9887
- headers: {
9888
- 'x-api-key': 'ARC_GRAPHQL_API_KEY',
9889
- },
9890
- },
9891
- },
9892
- arcDev: {
9893
- __name: 'arcDev',
9894
- document: './queries/arc.graphql',
9895
- schema: {
9896
- method: 'GET',
9897
- url: 'https://arc-graphql.rainbowdotme.workers.dev/graphql',
9898
- headers: {},
9899
- },
9900
- },
9901
- };
9902
- `
9903
- );
9904
- }
9905
- function resolveRainbowDemoEnv() {
9906
- const env = {
9907
- ENABLE_DEV_MODE: "1",
9908
- IS_TESTING: "false",
9909
- METADATA_BASE_URL: process.env.RAINBOW_METADATA_BASE_URL ?? RAINBOW_DEMO_METADATA_BASE_URL,
9910
- ADDYS_API_KEY: process.env.ADDYS_API_KEY ?? RAINBOW_DEMO_SERVICE_API_KEY,
9911
- ADDYS_BASE_URL: process.env.ADDYS_BASE_URL ?? RAINBOW_DEMO_METADATA_BASE_URL,
9912
- PLATFORM_API_KEY: process.env.PLATFORM_API_KEY ?? RAINBOW_DEMO_SERVICE_API_KEY,
9913
- PLATFORM_BASE_URL: process.env.PLATFORM_BASE_URL ?? RAINBOW_DEMO_METADATA_BASE_URL,
9914
- RAINBOW_MASTER_KEY: process.env.RAINBOW_MASTER_KEY ?? RAINBOW_DEMO_MASTER_KEY,
9915
- RAINBOW_RELAY_QUOTE_SIGNER: process.env.RAINBOW_RELAY_QUOTE_SIGNER ?? RAINBOW_DEMO_QUOTE_SIGNER,
9916
- RAINBOW_RELAY_URL: process.env.RAINBOW_RELAY_URL ?? RAINBOW_DEMO_RELAY_URL,
9917
- RPC_PROXY_API_KEY_PROD: RAINBOW_DEMO_RPC_API_KEY,
9918
- RPC_PROXY_BASE_URL_PROD: process.env.RAINBOW_RPC_PROXY_BASE_URL ?? process.env.RPC_PROXY_BASE_URL_PROD ?? RAINBOW_DEMO_RPC_PROXY_BASE_URL,
9919
- SECURE_WALLET_HASH_KEY: process.env.SECURE_WALLET_HASH_KEY ?? RAINBOW_DEMO_SECURE_WALLET_HASH_KEY,
9920
- WC_PROJECT_ID: process.env.WC_PROJECT_ID ?? process.env.RAINBOW_WALLETCONNECT_PROJECT_ID ?? RAINBOW_DEMO_WALLETCONNECT_PROJECT_ID
9921
- };
9922
- const optionalEnvVars = [
9923
- "ADDYS_API_KEY",
9924
- "ADDYS_BASE_URL",
9925
- "IMGIX_DOMAIN",
9926
- "IMGIX_TOKEN",
9927
- "PLATFORM_API_KEY",
9928
- "PLATFORM_BASE_URL",
9929
- "RAINBOW_TEST_WALLET",
9930
- "RAINBOW_RELAY_API_KEY",
9931
- "RAINBOW_RELAY_URL",
9932
- "SECURE_WALLET_HASH_KEY",
9933
- "TOKEN_SEARCH_URL"
9934
- ];
9935
- for (const key of optionalEnvVars) {
9936
- const value = process.env[key];
9937
- if (value) env[key] = value;
9938
- }
9939
- return env;
9940
- }
9941
- function ensureRainbowDemoNetworks() {
9942
- if (!(0, import_node_fs.existsSync)(RAINBOW_NETWORKS_FILE)) return;
9943
- const payload = JSON.parse((0, import_node_fs.readFileSync)(RAINBOW_NETWORKS_FILE, "utf8"));
9944
- if (!Array.isArray(payload.networks)) return;
9945
- let changed = false;
9946
- for (const network of payload.networks) {
9947
- const url = network.id ? RAINBOW_DEMO_PUBLIC_RPC_URLS[network.id] : void 0;
9948
- if (!url || !network.defaultRPC) continue;
9949
- if (network.defaultRPC.url === url) continue;
9950
- network.defaultRPC.url = url;
9951
- changed = true;
9952
- }
9953
- if (changed) {
9954
- (0, import_node_fs.writeFileSync)(RAINBOW_NETWORKS_FILE, `${JSON.stringify(payload)}
9955
- `);
9956
- }
9957
- }
9958
- function ensureRainbowSetup() {
9959
- if (!(0, import_node_fs.existsSync)(RAINBOW_DIR)) return;
9960
- ensureRainbowGraphqlConfig();
9961
- if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(RAINBOW_GRAPHQL_DIR, "node_modules/.bin/graphql-codegen"))) {
9962
- runRainbowSetupCommand("fnm exec --using=22 yarn install", RAINBOW_GRAPHQL_DIR);
9963
- }
9964
- const missingGraphql = RAINBOW_GRAPHQL_SENTINELS.some(
9965
- (rel) => !(0, import_node_fs.existsSync)((0, import_node_path.join)(RAINBOW_DIR, rel))
9966
- );
9967
- if (missingGraphql) {
9968
- runRainbowSetupCommand("fnm exec --using=22 yarn codegen", RAINBOW_GRAPHQL_DIR);
9969
- }
9970
- if (!(0, import_node_fs.existsSync)(RAINBOW_NETWORKS_FILE)) {
9971
- runRainbowSetupCommand("fnm exec --using=22 yarn fetch:networks", RAINBOW_DIR);
9972
- }
9973
- ensureRainbowDemoNetworks();
9974
- }
9975
- function readArtsyKeysPayload() {
9976
- if (!(0, import_node_fs.existsSync)(ARTSY_KEYS_FILE)) return void 0;
10043
+ async function loadOptionalDemoApps() {
10044
+ if (cached) return cached;
9977
10045
  try {
9978
- const parsed = JSON.parse((0, import_node_fs.readFileSync)(ARTSY_KEYS_FILE, "utf8"));
9979
- if (!parsed || typeof parsed !== "object") return void 0;
9980
- const secure = parsed.secure && typeof parsed.secure === "object" ? parsed.secure : void 0;
9981
- const publicKeys = parsed.public && typeof parsed.public === "object" ? parsed.public : void 0;
9982
- if (!secure && !publicKeys) return void 0;
9983
- return { secure, public: publicKeys };
10046
+ const mod = await import(
10047
+ /* @vite-ignore */
10048
+ REGISTRY_SPECIFIER
10049
+ );
10050
+ cached = Array.isArray(mod.APPS) ? mod.APPS : [];
9984
10051
  } catch {
9985
- return void 0;
9986
- }
9987
- }
9988
- function resolveArtsyRuntimeConfig() {
9989
- const email = process.env.SOOTSIM_ARTSY_EMAIL ?? process.env.MAESTRO_TEST_EMAIL;
9990
- const password = process.env.SOOTSIM_ARTSY_PASSWORD ?? process.env.MAESTRO_TEST_PASSWORD;
9991
- const keys = readArtsyKeysPayload();
9992
- const env = {};
9993
- if (email && password) {
9994
- env.SOOTSIM_LAUNCH_ARGUMENTS = JSON.stringify({
9995
- email,
9996
- password,
9997
- useMaestroInit: true
9998
- });
9999
- }
10000
- if (keys) {
10001
- env.SOOTSIM_REACT_NATIVE_KEYS_JSON = JSON.stringify(keys);
10052
+ cached = [];
10002
10053
  }
10003
- if (Object.keys(env).length === 0) return void 0;
10004
- return {
10005
- env
10006
- };
10054
+ return cached;
10007
10055
  }
10008
- function ensureArtsySetup() {
10009
- if (!(0, import_node_fs.existsSync)(ARTSY_DIR)) return;
10010
- const { execSync } = require("node:child_process");
10011
- const yarnRelease = (0, import_node_path.join)(ARTSY_DIR, ".yarn/releases/yarn-4.10.3.cjs");
10012
- if ((0, import_node_fs.existsSync)(yarnRelease)) {
10013
- const src = (0, import_node_fs.readFileSync)(yarnRelease, "utf8");
10014
- const needle = '["clone","-c core.autocrlf=false",';
10015
- if (src.includes(needle)) {
10016
- (0, import_node_fs.writeFileSync)(
10017
- yarnRelease,
10018
- src.replace(needle, '["clone","-c","core.autocrlf=false",')
10019
- );
10020
- }
10021
- }
10022
- if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(ARTSY_DIR, "node_modules/.yarn-state.yml"))) {
10023
- execSync("yarn install", {
10024
- cwd: ARTSY_DIR,
10025
- stdio: "inherit",
10026
- env: { ...process.env, YARN_CHECKSUM_BEHAVIOR: "update" }
10027
- });
10028
- }
10029
- if (!(0, import_node_fs.existsSync)(ARTSY_KEYS_FILE) || !(0, import_node_fs.existsSync)(ARTSY_METAFLAGS_FILE)) {
10030
- try {
10031
- execSync("yarn setup:oss", { cwd: ARTSY_DIR, stdio: "inherit" });
10032
- } catch {
10033
- if (!(0, import_node_fs.existsSync)(ARTSY_KEYS_FILE) || !(0, import_node_fs.existsSync)(ARTSY_METAFLAGS_FILE)) {
10034
- throw new Error("artsy demo: setup:oss did not create keys/metaflags");
10035
- }
10036
- }
10037
- }
10038
- const rnlaPkgJson = (0, import_node_path.join)(
10039
- ARTSY_DIR,
10040
- "node_modules/react-native-launch-arguments/package.json"
10041
- );
10042
- if ((0, import_node_fs.existsSync)(rnlaPkgJson)) {
10043
- const raw = (0, import_node_fs.readFileSync)(rnlaPkgJson, "utf8");
10044
- if (raw.includes('"dist/index.js"')) {
10045
- (0, import_node_fs.writeFileSync)(rnlaPkgJson, raw.replace('"dist/index.js"', '"src/index.ts"'));
10046
- }
10047
- }
10048
- if (!(0, import_node_fs.existsSync)(ARTSY_RELAY_SENTINEL)) {
10049
- execSync("yarn relay", { cwd: ARTSY_DIR, stdio: "inherit" });
10050
- (0, import_node_fs.writeFileSync)(ARTSY_RELAY_SENTINEL, "");
10051
- }
10056
+ function optionalDemoAppsSync() {
10057
+ return cached ?? [];
10052
10058
  }
10053
- var import_node_fs, import_node_os, import_node_path, HOME, getExpensifyProxyScript, getRainbowMetadataProxyScript, getMattermostRNUtilsNativeModule, getMattermostKeychainNativeModule, getMattermostNetworkClientNativeModule, getMattermostPreviewServerScript, EXPENSIFY_NATIVE_PROXY_ENV, MATTERMOST_DIR, UNISWAP_REPO_DIR, UNISWAP_APP_DIR, UNISWAP_ENV_LOCAL_FILE, UNISWAP_PLACEHOLDER, UNISWAP_DEMO_ENV_MARKER, UNISWAP_FORCE_UPGRADE_HOOK_FILE, UNISWAP_FORCE_UPGRADE_NOTIFICATION_FILE, UNISWAP_FORCE_UPGRADE_PATCH_MARKER, JOPLIN_DIR, JOPLIN_APP_DIR, JOPLIN_WATCH_ROOTS, JOPLIN_BUILD_SENTINELS, RAINBOW_DIR, RAINBOW_GRAPHQL_DIR, RAINBOW_GRAPHQL_CONFIG_FILE, RAINBOW_NETWORKS_FILE, RAINBOW_METADATA_BASE_URL, RAINBOW_METADATA_PROXY_PORT, RAINBOW_DEMO_METADATA_BASE_URL, RAINBOW_PUBLIC_ENS_GRAPHQL_URL, RAINBOW_DEMO_QUOTE_SIGNER, RAINBOW_DEMO_RELAY_URL, RAINBOW_DEMO_MASTER_KEY, RAINBOW_DEMO_RPC_PROXY_BASE_URL, RAINBOW_DEMO_RPC_API_KEY, RAINBOW_DEMO_PUBLIC_RPC_URLS, RAINBOW_DEMO_SERVICE_API_KEY, RAINBOW_DEMO_SECURE_WALLET_HASH_KEY, RAINBOW_DEMO_WALLETCONNECT_PROJECT_ID, RAINBOW_GRAPHQL_SENTINELS, ARTSY_DIR, ARTSY_KEYS_FILE, ARTSY_METAFLAGS_FILE, ARTSY_RELAY_SENTINEL, APPS;
10054
- var init_demo_app_registry = __esm({
10055
- "scripts/demo-app-registry.ts"() {
10059
+ var REGISTRY_SPECIFIER, cached;
10060
+ var init_optional_demo_registry = __esm({
10061
+ "scripts/optional-demo-registry.ts"() {
10056
10062
  "use strict";
10057
- import_node_fs = require("node:fs");
10058
- import_node_os = require("node:os");
10059
- import_node_path = require("node:path");
10060
- HOME = (0, import_node_os.homedir)();
10061
- getExpensifyProxyScript = () => resolveWorkspaceScriptPath(
10062
- "packages/sootsim-engine/scripts/expensify-web-proxy.ts",
10063
- "scripts/expensify-web-proxy.ts"
10064
- );
10065
- getRainbowMetadataProxyScript = () => resolveWorkspaceScriptPath(
10066
- "packages/sootsim-engine/scripts/rainbow-metadata-proxy.ts",
10067
- "scripts/rainbow-metadata-proxy.ts"
10068
- );
10069
- getMattermostRNUtilsNativeModule = () => resolveWorkspaceScriptPath(
10070
- "packages/compat/src/stubs/mattermost-rnutils-native.ts",
10071
- "../compat/src/stubs/mattermost-rnutils-native.ts"
10072
- );
10073
- getMattermostKeychainNativeModule = () => resolveWorkspaceScriptPath(
10074
- "packages/compat/src/stubs/native-seams/react-native-keychain-manager.ts",
10075
- "../compat/src/stubs/native-seams/react-native-keychain-manager.ts"
10076
- );
10077
- getMattermostNetworkClientNativeModule = () => resolveWorkspaceScriptPath(
10078
- "packages/compat/src/stubs/mattermost-network-client-native.ts",
10079
- "../compat/src/stubs/mattermost-network-client-native.ts"
10080
- );
10081
- getMattermostPreviewServerScript = () => resolveWorkspaceScriptPath(
10082
- "packages/sootsim-engine/scripts/mattermost-preview-server.ts",
10083
- "scripts/mattermost-preview-server.ts"
10084
- );
10085
- EXPENSIFY_NATIVE_PROXY_ENV = {
10086
- USE_NGROK: "true",
10087
- NGROK_URL: "http://localhost:9000/",
10088
- SECURE_NGROK_URL: "http://localhost:9000/"
10089
- };
10090
- MATTERMOST_DIR = (0, import_node_path.join)(HOME, "github/mattermost-mobile");
10091
- UNISWAP_REPO_DIR = (0, import_node_path.join)(HOME, "github/uniswap-interface");
10092
- UNISWAP_APP_DIR = (0, import_node_path.join)(UNISWAP_REPO_DIR, "apps/mobile");
10093
- UNISWAP_ENV_LOCAL_FILE = (0, import_node_path.join)(UNISWAP_REPO_DIR, ".env.defaults.local");
10094
- UNISWAP_PLACEHOLDER = "stored-in-.env.local";
10095
- UNISWAP_DEMO_ENV_MARKER = "# sootsim demo env overrides";
10096
- UNISWAP_FORCE_UPGRADE_HOOK_FILE = (0, import_node_path.join)(
10097
- UNISWAP_REPO_DIR,
10098
- "packages/uniswap/src/features/forceUpgrade/hooks/useForceUpgradeStatus.ts"
10099
- );
10100
- UNISWAP_FORCE_UPGRADE_NOTIFICATION_FILE = (0, import_node_path.join)(
10101
- UNISWAP_REPO_DIR,
10102
- "apps/mobile/src/notification-service/data-sources/createForceUpgradeNotificationDataSource.ts"
10103
- );
10104
- UNISWAP_FORCE_UPGRADE_PATCH_MARKER = "SOOTSIM_DEMO_DISABLE_FORCE_UPGRADE";
10105
- JOPLIN_DIR = (0, import_node_path.join)(HOME, "github/joplin");
10106
- JOPLIN_APP_DIR = (0, import_node_path.join)(JOPLIN_DIR, "packages/app-mobile");
10107
- JOPLIN_WATCH_ROOTS = [
10108
- "packages/lib",
10109
- "packages/renderer",
10110
- "packages/turndown",
10111
- "packages/turndown-plugin-gfm",
10112
- "packages/editor",
10113
- "packages/tools",
10114
- "packages/utils",
10115
- "packages/fork-htmlparser2",
10116
- "packages/fork-uslug",
10117
- "packages/fork-sax",
10118
- "packages/htmlpack",
10119
- "packages/react-native-saf-x",
10120
- "packages/react-native-alarm-notification"
10121
- ];
10122
- JOPLIN_BUILD_SENTINELS = [
10123
- "packages/lib/models/Setting.js",
10124
- "packages/turndown/lib/turndown.cjs.js",
10125
- "packages/app-mobile/pluginAssets/index.js"
10126
- ];
10127
- RAINBOW_DIR = (0, import_node_path.join)(HOME, "github/rainbow");
10128
- RAINBOW_GRAPHQL_DIR = (0, import_node_path.join)(RAINBOW_DIR, "src/graphql");
10129
- RAINBOW_GRAPHQL_CONFIG_FILE = (0, import_node_path.join)(RAINBOW_GRAPHQL_DIR, "config.js");
10130
- RAINBOW_NETWORKS_FILE = (0, import_node_path.join)(RAINBOW_DIR, "src/references/networks.json");
10131
- RAINBOW_METADATA_BASE_URL = "https://metadata.p.rainbow.me";
10132
- RAINBOW_METADATA_PROXY_PORT = 9011;
10133
- RAINBOW_DEMO_METADATA_BASE_URL = `http://127.0.0.1:${RAINBOW_METADATA_PROXY_PORT}`;
10134
- RAINBOW_PUBLIC_ENS_GRAPHQL_URL = "https://api.thegraph.com/subgraphs/name/ensdomains/ens";
10135
- RAINBOW_DEMO_QUOTE_SIGNER = "0x0000000000000000000000000000000000000000";
10136
- RAINBOW_DEMO_RELAY_URL = "https://relay.rainbow.me";
10137
- RAINBOW_DEMO_MASTER_KEY = "sootsim-rainbow-demo-master-key-do-not-use-for-real-wallets";
10138
- RAINBOW_DEMO_RPC_PROXY_BASE_URL = "https://rpc.rainbow.me/v1";
10139
- RAINBOW_DEMO_RPC_API_KEY = "";
10140
- RAINBOW_DEMO_PUBLIC_RPC_URLS = {
10141
- "1": "https://ethereum-rpc.publicnode.com"
10142
- };
10143
- RAINBOW_DEMO_SERVICE_API_KEY = "sootsim-rainbow-demo-api-key";
10144
- RAINBOW_DEMO_SECURE_WALLET_HASH_KEY = "0x736f6f7473696d2d7261696e626f772d64656d6f2d686173682d6b6579000000";
10145
- RAINBOW_DEMO_WALLETCONNECT_PROJECT_ID = "00000000000000000000000000000000";
10146
- RAINBOW_GRAPHQL_SENTINELS = [
10147
- "src/graphql/__generated__/ens.ts",
10148
- "src/graphql/__generated__/metadata.ts",
10149
- "src/graphql/__generated__/metadataPOST.ts"
10150
- ];
10151
- ARTSY_DIR = (0, import_node_path.join)(HOME, "github/eigen");
10152
- ARTSY_KEYS_FILE = (0, import_node_path.join)(ARTSY_DIR, "keys.shared.json");
10153
- ARTSY_METAFLAGS_FILE = (0, import_node_path.join)(ARTSY_DIR, "metaflags.json");
10154
- ARTSY_RELAY_SENTINEL = (0, import_node_path.join)(ARTSY_DIR, "src/__generated__/.relay-complete");
10155
- APPS = [
10156
- {
10157
- name: "bluesky",
10158
- label: "Bluesky",
10159
- dir: (0, import_node_path.join)(HOME, "github/bluesky"),
10160
- preferredPort: 8082,
10161
- framework: "expo",
10162
- command: (p) => ({ cmd: `npx expo start --port ${p}` }),
10163
- credentials: {
10164
- envVars: ["SOOTSIM_BLUESKY_PASSWORD"],
10165
- known: { HANDLE: "natew.bsky.social" }
10166
- }
10167
- },
10168
- {
10169
- name: "3pc",
10170
- label: "3PunchConvo",
10171
- dir: (0, import_node_path.join)(HOME, "lightstrike-labs/three-punch-convo-app/apps/one"),
10172
- preferredPort: 8081,
10173
- framework: "one",
10174
- command: (p) => ({ cmd: "npx one dev", env: { ONE_PORT: String(p) } })
10175
- },
10176
- {
10177
- name: "uniswap",
10178
- label: "Uniswap",
10179
- dir: UNISWAP_APP_DIR,
10180
- preferredPort: 8085,
10181
- framework: "expo",
10182
- prepare: () => {
10183
- ensureUniswapDemoEnvLocal();
10184
- ensureUniswapForceUpgradePatched();
10185
- },
10186
- command: (p) => ({
10187
- cmd: `npx expo start --clear --port ${p}`,
10188
- // prefer the real local mobile env when present, otherwise fall back
10189
- // to Uniswap's checked-in public web RPC settings so demo boots cleanly.
10190
- env: resolveUniswapDemoEnv()
10191
- })
10192
- },
10193
- {
10194
- name: "takeout",
10195
- label: "Takeout",
10196
- dir: (0, import_node_path.join)(HOME, "takeout"),
10197
- preferredPort: 8086,
10198
- framework: "one",
10199
- // takeout needs more than Metro for the demo to actually work: better-auth
10200
- // (login), zero-cache (sync), and a postgres for both. `bun lite` brings
10201
- // up the orez-backed stack (PG + zero + s3 in one binary) plus the One
10202
- // dev server. takeout's env system shifts every port (web, pg, zero,
10203
- // minio) uniformly by PORT_OFFSET, so we derive offset = port - 8081
10204
- // (the base web port) to keep all backend ports clear of the default
10205
- // dev stack while pinning One to the demo slot. OREZ_DATA_DIR is isolated
10206
- // to a per-demo path so we don't fight a soot dev orez that may already
10207
- // be holding pglite locks on ~/takeout/.orez (soot can attach takeout as
10208
- // a project and spin up its own orez against the same data dir).
10209
- readyTimeoutMs: 24e4,
10210
- managedPorts: (p) => {
10211
- const offset = p - 8081;
10212
- return [5433 + offset, 4848 + offset, 9200 + offset];
10213
- },
10214
- command: (p) => ({
10215
- cmd: "bun lite:demo",
10216
- env: {
10217
- TAKEOUT_ENV_MODE: "development",
10218
- PORT_OFFSET: String(p - 8081),
10219
- OREZ_DATA_DIR: `${HOME}/.cache/sootsim-demo/takeout-orez`,
10220
- VITE_DEMO_MODE: "1",
10221
- ZERO_APP_ID: "takeout",
10222
- ZERO_APP_PUBLICATIONS: "zero_takeout",
10223
- ZERO_CVR_MAX_CONNS: "4",
10224
- ZERO_NUM_SYNC_WORKERS: "2",
10225
- ZERO_UPSTREAM_MAX_CONNS: "8"
10226
- }
10227
- })
10228
- },
10229
- {
10230
- name: "expensify",
10231
- label: "Expensify",
10232
- dir: (0, import_node_path.join)(HOME, "github/expensify"),
10233
- preferredPort: 8087,
10234
- framework: "rock",
10235
- runtimeConfig: {
10236
- env: EXPENSIFY_NATIVE_PROXY_ENV
10237
- },
10238
- sidecars: [
10239
- {
10240
- name: "web-proxy",
10241
- port: 9e3,
10242
- readyPath: "/api/Ping",
10243
- command: () => ({
10244
- cmd: `bun ${JSON.stringify(getExpensifyProxyScript())}`,
10245
- env: EXPENSIFY_NATIVE_PROXY_ENV
10246
- })
10247
- }
10248
- ],
10249
- command: (p) => ({
10250
- cmd: `fnm exec --using=20.20.0 npx rock start --port ${p} --no-interactive`,
10251
- env: EXPENSIFY_NATIVE_PROXY_ENV
10252
- })
10253
- },
10254
- {
10255
- name: "artsy",
10256
- label: "Artsy",
10257
- dir: ARTSY_DIR,
10258
- preferredPort: 8088,
10259
- framework: "expo",
10260
- runtimeConfig: resolveArtsyRuntimeConfig(),
10261
- prepare: () => {
10262
- ensureArtsySetup();
10263
- },
10264
- command: (p) => ({
10265
- // eigen's `yarn start` wraps `react-native start` with a relay watcher
10266
- // via concurrently; for the demo we run relay once in prepare and
10267
- // invoke the metro server directly so --port is respected.
10268
- cmd: `npx react-native start --port ${p}`
10269
- }),
10270
- credentials: {
10271
- envVars: ["SOOTSIM_ARTSY_EMAIL", "SOOTSIM_ARTSY_PASSWORD"],
10272
- note: "auto-login reuses Artsy\u2019s built-in Maestro launch-arguments hook"
10273
- }
10274
- },
10275
- {
10276
- name: "rainbow",
10277
- label: "Rainbow",
10278
- dir: RAINBOW_DIR,
10279
- preferredPort: 8089,
10280
- framework: "rock",
10281
- sidecars: [
10282
- {
10283
- name: "metadata-proxy",
10284
- port: RAINBOW_METADATA_PROXY_PORT,
10285
- readyPath: "/health",
10286
- command: () => ({
10287
- cmd: `bun ${JSON.stringify(getRainbowMetadataProxyScript())}`,
10288
- env: {
10289
- PORT: String(RAINBOW_METADATA_PROXY_PORT),
10290
- RAINBOW_PUBLIC_RPC_URLS: JSON.stringify(RAINBOW_DEMO_PUBLIC_RPC_URLS),
10291
- RAINBOW_UPSTREAM_METADATA_BASE_URL: RAINBOW_METADATA_BASE_URL
10292
- }
10293
- })
10294
- }
10295
- ],
10296
- prepare: () => {
10297
- ensureRainbowSetup();
10298
- },
10299
- command: (p) => ({
10300
- cmd: `fnm exec --using=22 yarn start --port ${p} --reset-cache`,
10301
- env: resolveRainbowDemoEnv()
10302
- }),
10303
- credentials: {
10304
- envVars: ["RAINBOW_TEST_WALLET"],
10305
- optionalEnvVars: [
10306
- "RAINBOW_WALLETCONNECT_PROJECT_ID",
10307
- "WC_PROJECT_ID",
10308
- "IMGIX_DOMAIN",
10309
- "IMGIX_TOKEN"
10310
- ],
10311
- note: "launcher supplies demo encryption, public mainnet RPC, and a placeholder WalletConnect project id; use only a public throwaway mnemonic for RAINBOW_TEST_WALLET"
10312
- }
10313
- },
10314
- {
10315
- name: "rocket-chat",
10316
- label: "Rocket.Chat",
10317
- dir: (0, import_node_path.join)(HOME, "github/Rocket.Chat.ReactNative"),
10318
- preferredPort: 8093,
10319
- framework: "expo",
10320
- command: (p) => ({
10321
- cmd: `npx react-native start --port ${p}`,
10322
- env: { RUNNING_E2E_TESTS: "true" }
10323
- }),
10324
- credentials: {
10325
- envVars: [
10326
- "ROCKET_CHAT_DEMO_SERVER",
10327
- "ROCKET_CHAT_DEMO_USERNAME",
10328
- "ROCKET_CHAT_DEMO_PASSWORD"
10329
- ],
10330
- known: { SERVER: "https://mobile.qa.rocket.chat" },
10331
- note: "use packages/sootsim-engine/scripts/rocket-chat-demo-auth.ts to create/login a disposable QA user and print the rocketchat://auth deep link"
10332
- }
10333
- },
10334
- {
10335
- name: "mattermost",
10336
- label: "Mattermost",
10337
- dir: MATTERMOST_DIR,
10338
- preferredPort: 8090,
10339
- framework: "expo",
10340
- sidecars: [
10341
- {
10342
- name: "preview-server",
10343
- port: 8065,
10344
- readyPath: "/api/v4/system/ping",
10345
- command: () => ({
10346
- cmd: `bun ${JSON.stringify(getMattermostPreviewServerScript())}`
10347
- })
10348
- }
10349
- ],
10350
- runtimeConfig: {
10351
- modules: {
10352
- // mattermost patches react-native-keychain's JS entry in its own
10353
- // repo; run that package and provide only the native manager seam.
10354
- "react-native-keychain": false,
10355
- "dist/assets/config.json": {
10356
- inline: {
10357
- AuthUrlScheme: "mmauth://",
10358
- AuthUrlSchemeDev: "mmauthbeta://",
10359
- DefaultServerUrl: "http://localhost:8065",
10360
- DefaultServerName: "Mattermost Demo",
10361
- TestServerUrl: "http://localhost:8065",
10362
- AutoSelectServerUrl: true,
10363
- WebsiteURL: "https://mattermost.com",
10364
- ServerNoticeURL: "https://github.com/mattermost/mattermost-server/blob/master/NOTICE.txt",
10365
- MobileNoticeURL: "https://github.com/mattermost/mattermost-mobile/blob/master/NOTICE.txt",
10366
- RudderApiKey: "",
10367
- SentryEnabled: false,
10368
- SentryDsnIos: "",
10369
- SentryDsnAndroid: "",
10370
- SentryOptions: {
10371
- deactivateStacktraceMerging: true,
10372
- autoBreadcrumbs: {
10373
- xhr: false,
10374
- console: true
10375
- },
10376
- severityLevelFilter: ["fatal"]
10377
- },
10378
- ShowReview: false,
10379
- ShowOnboarding: false,
10380
- ExperimentalNormalizeMarkdownLinks: false,
10381
- CustomRequestHeaders: {},
10382
- CollectNetworkMetrics: false
10383
- }
10384
- }
10385
- },
10386
- nativeModules: {
10387
- RNKeychainManager: { file: getMattermostKeychainNativeModule() },
10388
- RNUtils: { file: getMattermostRNUtilsNativeModule() },
10389
- GenericClient: { file: getMattermostNetworkClientNativeModule() },
10390
- ApiClient: { file: getMattermostNetworkClientNativeModule() },
10391
- WebSocketClient: { file: getMattermostNetworkClientNativeModule() }
10392
- }
10393
- },
10394
- command: (p) => ({
10395
- cmd: `npx react-native start --host 127.0.0.1 --port ${p}`
10396
- }),
10397
- credentials: {
10398
- known: {
10399
- SERVER: "http://localhost:8065",
10400
- USERNAME: "demo",
10401
- PASSWORD: "DemoPassword1!"
10402
- },
10403
- note: "local mattermost-preview seeded through the real REST API; no signup or OTP needed"
10404
- }
10405
- },
10406
- {
10407
- name: "joplin",
10408
- label: "Joplin",
10409
- dir: JOPLIN_APP_DIR,
10410
- preferredPort: 8084,
10411
- framework: "expo",
10412
- // joplin is local-first: sync.target defaults to 0 ("None") and the
10413
- // mobile startup runs WelcomeUtils.install() on first launch, which
10414
- // seeds a "Welcome!" folder + welcome notes. no login or external
10415
- // credentials are needed for a usable demo — just boot it. tenant
10416
- // bedrock SQLite (via react-native-sqlite-storage stub) carries the
10417
- // seeded notes across reloads.
10418
- prepare: () => {
10419
- ensureJoplinWatchmanConfigs();
10420
- ensureJoplinBuilt();
10421
- },
10422
- command: (p) => ({
10423
- cmd: `npx expo start --port ${p}`,
10424
- env: { BROWSERSLIST_IGNORE_OLD_DATA: "true" }
10425
- }),
10426
- credentials: {
10427
- note: "no login required \u2014 sync.target=0 (None), seed via WelcomeUtils"
10428
- }
10429
- }
10430
- ];
10063
+ REGISTRY_SPECIFIER = "./demo-app-registry.ts";
10064
+ cached = null;
10431
10065
  }
10432
10066
  });
10433
10067
 
10434
10068
  // scripts/dev-server-scanner.ts
10435
10069
  function tcpPing(port, timeout = TCP_GATE_MS) {
10436
- return new Promise((resolve7) => {
10070
+ return new Promise((resolve6) => {
10437
10071
  const sock = new import_net.default.Socket();
10438
10072
  let settled = false;
10439
10073
  const done = (ok) => {
10440
10074
  if (settled) return;
10441
10075
  settled = true;
10442
10076
  sock.destroy();
10443
- resolve7(ok);
10077
+ resolve6(ok);
10444
10078
  };
10445
10079
  sock.setTimeout(timeout);
10446
10080
  sock.once("connect", () => done(true));
@@ -10450,7 +10084,7 @@ function tcpPing(port, timeout = TCP_GATE_MS) {
10450
10084
  });
10451
10085
  }
10452
10086
  function httpGet(port, path9, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
10453
- return new Promise((resolve7) => {
10087
+ return new Promise((resolve6) => {
10454
10088
  const req = import_http.default.request(
10455
10089
  { hostname: "localhost", port, path: path9, method, timeout, headers },
10456
10090
  (res) => {
@@ -10464,14 +10098,14 @@ function httpGet(port, path9, method = "GET", timeout = TIMEOUT_MS, headers = {}
10464
10098
  })();
10465
10099
  res.on(
10466
10100
  "end",
10467
- () => resolve7({ statusCode: res.statusCode || 0, body, contentType })
10101
+ () => resolve6({ statusCode: res.statusCode || 0, body, contentType })
10468
10102
  );
10469
10103
  }
10470
10104
  );
10471
- req.on("error", () => resolve7(null));
10105
+ req.on("error", () => resolve6(null));
10472
10106
  req.setTimeout(timeout, () => {
10473
10107
  req.destroy();
10474
- resolve7(null);
10108
+ resolve6(null);
10475
10109
  });
10476
10110
  req.end();
10477
10111
  });
@@ -10534,8 +10168,8 @@ async function discoverListeningProcesses(excludePorts = []) {
10534
10168
  }
10535
10169
  async function resolveProcessCwd(pid) {
10536
10170
  if (pid <= 0) return null;
10537
- const cached3 = cwdByPid.get(pid);
10538
- if (cached3) return cached3;
10171
+ const cached4 = cwdByPid.get(pid);
10172
+ if (cached4) return cached4;
10539
10173
  try {
10540
10174
  const { stdout } = await execP(`lsof -p ${pid} -a -d cwd -Fn 2>/dev/null`, {
10541
10175
  encoding: "utf8",
@@ -10567,7 +10201,7 @@ function makeResult(port, framework) {
10567
10201
  };
10568
10202
  }
10569
10203
  function withRuntimeConfig(port, bundleUrl) {
10570
- const knownApp = APPS.find((app) => app.preferredPort === port);
10204
+ const knownApp = optionalDemoAppsSync().find((app) => app.preferredPort === port);
10571
10205
  const configured = knownApp?.runtimeConfig ? applySootSimConfigToUrl(bundleUrl, knownApp.runtimeConfig) : bundleUrl;
10572
10206
  return normalizeNativeDevBundleUrl(configured);
10573
10207
  }
@@ -10737,12 +10371,12 @@ async function scanDevServers(opts = {}) {
10737
10371
  const results = [];
10738
10372
  const toProbe = [];
10739
10373
  for (const { port, pid } of processes) {
10740
- const cached3 = portCache.get(port);
10741
- if (cached3 && __shouldReuseScannerCacheEntry(cached3, pid)) {
10742
- if (cached3.result) results.push(cached3.result);
10374
+ const cached4 = portCache.get(port);
10375
+ if (cached4 && __shouldReuseScannerCacheEntry(cached4, pid)) {
10376
+ if (cached4.result) results.push(cached4.result);
10743
10377
  continue;
10744
10378
  }
10745
- if (cached3 && cached3.pid !== pid) {
10379
+ if (cached4 && cached4.pid !== pid) {
10746
10380
  knownNonPatched.delete(port);
10747
10381
  knownNonExpo.delete(port);
10748
10382
  knownOne.delete(port);
@@ -10788,8 +10422,9 @@ var init_dev_server_scanner = __esm({
10788
10422
  import_util = require("util");
10789
10423
  init_config();
10790
10424
  init_native_dev_bundle_url();
10791
- init_demo_app_registry();
10425
+ init_optional_demo_registry();
10792
10426
  execP = (0, import_util.promisify)(import_child_process.exec);
10427
+ void loadOptionalDemoApps();
10793
10428
  TIMEOUT_MS = 250;
10794
10429
  MANIFEST_TIMEOUT_MS = 1500;
10795
10430
  TCP_GATE_MS = 120;
@@ -10816,71 +10451,71 @@ var init_dev_server_scanner = __esm({
10816
10451
 
10817
10452
  // src/cli-version.ts
10818
10453
  function getCliVersion() {
10819
- if (cached != null) return cached;
10454
+ if (cached2 != null) return cached2;
10820
10455
  const candidates = [
10821
10456
  () => (0, import_node_url.fileURLToPath)(import_meta.resolve("sootsim/package.json")),
10822
10457
  () => (0, import_node_url.fileURLToPath)(new URL("../package.json", __sootsim_import_meta_url))
10823
10458
  ];
10824
- for (const resolve7 of candidates) {
10459
+ for (const resolve6 of candidates) {
10825
10460
  try {
10826
- const version = JSON.parse((0, import_node_fs2.readFileSync)(resolve7(), "utf8")).version;
10461
+ const version = JSON.parse((0, import_node_fs.readFileSync)(resolve6(), "utf8")).version;
10827
10462
  if (typeof version === "string" && version) {
10828
- cached = version;
10829
- return cached;
10463
+ cached2 = version;
10464
+ return cached2;
10830
10465
  }
10831
10466
  } catch {
10832
10467
  }
10833
10468
  }
10834
- cached = "0.0.0";
10835
- return cached;
10469
+ cached2 = "0.0.0";
10470
+ return cached2;
10836
10471
  }
10837
- var import_node_fs2, import_node_url, import_meta, cached;
10472
+ var import_node_fs, import_node_url, import_meta, cached2;
10838
10473
  var init_cli_version = __esm({
10839
10474
  "src/cli-version.ts"() {
10840
10475
  "use strict";
10841
- import_node_fs2 = require("node:fs");
10476
+ import_node_fs = require("node:fs");
10842
10477
  import_node_url = require("node:url");
10843
10478
  import_meta = {};
10844
- cached = null;
10479
+ cached2 = null;
10845
10480
  }
10846
10481
  });
10847
10482
 
10848
10483
  // src/home-paths.ts
10849
10484
  function sootsimHomeDir() {
10850
10485
  const override = process.env[SOOTSIM_HOME_ENV];
10851
- if (override && override.length > 0) return import_node_path2.default.resolve(override);
10852
- return import_node_path2.default.join((0, import_node_os2.homedir)(), ".sootsim");
10486
+ if (override && override.length > 0) return import_node_path.default.resolve(override);
10487
+ return import_node_path.default.join((0, import_node_os.homedir)(), ".sootsim");
10853
10488
  }
10854
10489
  function runtimesDir() {
10855
- return import_node_path2.default.join(sootsimHomeDir(), "runtimes");
10490
+ return import_node_path.default.join(sootsimHomeDir(), "runtimes");
10856
10491
  }
10857
10492
  function runtimeDir(version) {
10858
- return import_node_path2.default.join(runtimesDir(), version);
10493
+ return import_node_path.default.join(runtimesDir(), version);
10859
10494
  }
10860
10495
  function activeRuntimeFile() {
10861
- return import_node_path2.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
10496
+ return import_node_path.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
10862
10497
  }
10863
10498
  function electronDir() {
10864
- return import_node_path2.default.join(sootsimHomeDir(), "electron");
10499
+ return import_node_path.default.join(sootsimHomeDir(), "electron");
10865
10500
  }
10866
10501
  function electronUserDataDir() {
10867
- return import_node_path2.default.join(electronDir(), "userData");
10502
+ return import_node_path.default.join(electronDir(), "userData");
10868
10503
  }
10869
10504
  function profilesDir() {
10870
- return import_node_path2.default.join(sootsimHomeDir(), "profiles");
10505
+ return import_node_path.default.join(sootsimHomeDir(), "profiles");
10871
10506
  }
10872
10507
  function cacheDir() {
10873
- return import_node_path2.default.join(sootsimHomeDir(), "cache");
10508
+ return import_node_path.default.join(sootsimHomeDir(), "cache");
10874
10509
  }
10875
10510
  function daemonLockfilePath() {
10876
- return import_node_path2.default.join(sootsimHomeDir(), DAEMON_LOCKFILE);
10511
+ return import_node_path.default.join(sootsimHomeDir(), DAEMON_LOCKFILE);
10877
10512
  }
10878
10513
  function configFilePath() {
10879
- return import_node_path2.default.join(sootsimHomeDir(), CONFIG_FILE);
10514
+ return import_node_path.default.join(sootsimHomeDir(), CONFIG_FILE);
10880
10515
  }
10881
10516
  function readSharedConfig() {
10882
10517
  try {
10883
- const raw = import_node_fs3.default.readFileSync(configFilePath(), "utf8");
10518
+ const raw = import_node_fs2.default.readFileSync(configFilePath(), "utf8");
10884
10519
  const parsed = JSON.parse(raw);
10885
10520
  return parsed && typeof parsed === "object" ? parsed : {};
10886
10521
  } catch {
@@ -10898,34 +10533,34 @@ function writeSharedConfig(patch) {
10898
10533
  };
10899
10534
  }
10900
10535
  const tmp = `${configFilePath()}.tmp`;
10901
- import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(next, null, 2)}
10536
+ import_node_fs2.default.writeFileSync(tmp, `${JSON.stringify(next, null, 2)}
10902
10537
  `, "utf8");
10903
- import_node_fs3.default.renameSync(tmp, configFilePath());
10538
+ import_node_fs2.default.renameSync(tmp, configFilePath());
10904
10539
  return next;
10905
10540
  }
10906
10541
  function ensureSootsimHome() {
10907
- import_node_fs3.default.mkdirSync(sootsimHomeDir(), { recursive: true });
10908
- import_node_fs3.default.mkdirSync(runtimesDir(), { recursive: true });
10909
- import_node_fs3.default.mkdirSync(electronDir(), { recursive: true });
10910
- import_node_fs3.default.mkdirSync(profilesDir(), { recursive: true });
10911
- import_node_fs3.default.mkdirSync(cacheDir(), { recursive: true });
10542
+ import_node_fs2.default.mkdirSync(sootsimHomeDir(), { recursive: true });
10543
+ import_node_fs2.default.mkdirSync(runtimesDir(), { recursive: true });
10544
+ import_node_fs2.default.mkdirSync(electronDir(), { recursive: true });
10545
+ import_node_fs2.default.mkdirSync(profilesDir(), { recursive: true });
10546
+ import_node_fs2.default.mkdirSync(cacheDir(), { recursive: true });
10912
10547
  }
10913
10548
  function readActiveRuntime() {
10914
10549
  try {
10915
- const value = import_node_fs3.default.readFileSync(activeRuntimeFile(), "utf8").trim();
10550
+ const value = import_node_fs2.default.readFileSync(activeRuntimeFile(), "utf8").trim();
10916
10551
  return value.length > 0 ? value : null;
10917
10552
  } catch {
10918
10553
  return null;
10919
10554
  }
10920
10555
  }
10921
10556
  function writeActiveRuntime(version) {
10922
- import_node_fs3.default.mkdirSync(runtimesDir(), { recursive: true });
10923
- import_node_fs3.default.writeFileSync(activeRuntimeFile(), `${version}
10557
+ import_node_fs2.default.mkdirSync(runtimesDir(), { recursive: true });
10558
+ import_node_fs2.default.writeFileSync(activeRuntimeFile(), `${version}
10924
10559
  `, "utf8");
10925
10560
  }
10926
10561
  function listInstalledRuntimes() {
10927
10562
  try {
10928
- return import_node_fs3.default.readdirSync(runtimesDir(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort(compareSemver);
10563
+ return import_node_fs2.default.readdirSync(runtimesDir(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort(compareSemver);
10929
10564
  } catch {
10930
10565
  return [];
10931
10566
  }
@@ -10956,17 +10591,17 @@ function activeRuntimeDir() {
10956
10591
  if (!version) return null;
10957
10592
  const dir = runtimeDir(version);
10958
10593
  try {
10959
- if (import_node_fs3.default.statSync(dir).isDirectory()) return dir;
10594
+ if (import_node_fs2.default.statSync(dir).isDirectory()) return dir;
10960
10595
  } catch {
10961
10596
  }
10962
10597
  return null;
10963
10598
  }
10964
10599
  function readDaemonLockfile() {
10965
10600
  try {
10966
- const fd = import_node_fs3.default.openSync(daemonLockfilePath(), "r");
10601
+ const fd = import_node_fs2.default.openSync(daemonLockfilePath(), "r");
10967
10602
  try {
10968
10603
  const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
10969
- const bytesRead = import_node_fs3.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
10604
+ const bytesRead = import_node_fs2.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
10970
10605
  const raw = buf.subarray(0, bytesRead).toString("utf8");
10971
10606
  const parsed = JSON.parse(raw);
10972
10607
  if (parsed && parsed.schema === 1 && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number") {
@@ -10974,7 +10609,7 @@ function readDaemonLockfile() {
10974
10609
  }
10975
10610
  return null;
10976
10611
  } finally {
10977
- import_node_fs3.default.closeSync(fd);
10612
+ import_node_fs2.default.closeSync(fd);
10978
10613
  }
10979
10614
  } catch {
10980
10615
  return null;
@@ -10993,9 +10628,9 @@ function isDaemonLockfileFresh(lock, now = Date.now()) {
10993
10628
  function writeDaemonLockfile(data) {
10994
10629
  ensureSootsimHome();
10995
10630
  const tmp = `${daemonLockfilePath()}.tmp`;
10996
- import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
10631
+ import_node_fs2.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
10997
10632
  `, "utf8");
10998
- import_node_fs3.default.renameSync(tmp, daemonLockfilePath());
10633
+ import_node_fs2.default.renameSync(tmp, daemonLockfilePath());
10999
10634
  }
11000
10635
  function claimDaemonLockfile(data) {
11001
10636
  ensureSootsimHome();
@@ -11008,17 +10643,17 @@ function claimDaemonLockfile(data) {
11008
10643
  }
11009
10644
  function removeDaemonLockfile() {
11010
10645
  try {
11011
- import_node_fs3.default.unlinkSync(daemonLockfilePath());
10646
+ import_node_fs2.default.unlinkSync(daemonLockfilePath());
11012
10647
  } catch {
11013
10648
  }
11014
10649
  }
11015
- var import_node_fs3, import_node_os2, import_node_path2, SOOTSIM_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, CONFIG_FILE, DAEMON_HEARTBEAT_STALE_MS, DAEMON_LOCKFILE_MAX_BYTES;
10650
+ var import_node_fs2, import_node_os, import_node_path, SOOTSIM_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, CONFIG_FILE, DAEMON_HEARTBEAT_STALE_MS, DAEMON_LOCKFILE_MAX_BYTES;
11016
10651
  var init_home_paths = __esm({
11017
10652
  "src/home-paths.ts"() {
11018
10653
  "use strict";
11019
- import_node_fs3 = __toESM(require("node:fs"), 1);
11020
- import_node_os2 = require("node:os");
11021
- import_node_path2 = __toESM(require("node:path"), 1);
10654
+ import_node_fs2 = __toESM(require("node:fs"), 1);
10655
+ import_node_os = require("node:os");
10656
+ import_node_path = __toESM(require("node:path"), 1);
11022
10657
  SOOTSIM_HOME_ENV = "SOOTSIM_HOME";
11023
10658
  ACTIVE_RUNTIME_FILE = "active";
11024
10659
  DAEMON_LOCKFILE = "daemon.json";
@@ -11209,22 +10844,22 @@ async function downloadToFile(url, destPath) {
11209
10844
  }
11210
10845
  }
11211
10846
  function sha256File(filePath) {
11212
- return new Promise((resolve7, reject) => {
10847
+ return new Promise((resolve6, reject) => {
11213
10848
  const hash = import_crypto.default.createHash("sha256");
11214
10849
  const stream = import_fs.default.createReadStream(filePath);
11215
10850
  stream.on("data", (chunk) => hash.update(chunk));
11216
10851
  stream.on("error", reject);
11217
- stream.on("end", () => resolve7(hash.digest("hex")));
10852
+ stream.on("end", () => resolve6(hash.digest("hex")));
11218
10853
  });
11219
10854
  }
11220
10855
  function extractTarball(tarPath, destDir) {
11221
- return new Promise((resolve7, reject) => {
10856
+ return new Promise((resolve6, reject) => {
11222
10857
  const child = (0, import_child_process2.spawn)("tar", ["-xzf", tarPath, "-C", destDir], {
11223
10858
  stdio: ["ignore", "inherit", "inherit"]
11224
10859
  });
11225
10860
  child.on("error", reject);
11226
10861
  child.on("exit", (code) => {
11227
- if (code === 0) resolve7();
10862
+ if (code === 0) resolve6();
11228
10863
  else reject(new Error(`tar exited with code ${code}`));
11229
10864
  });
11230
10865
  });
@@ -11307,7 +10942,7 @@ function getUserDataDir() {
11307
10942
  return userDataDir();
11308
10943
  }
11309
10944
  function storeFile() {
11310
- return import_node_path3.default.join(userDataDir(), "attached-projects.json");
10945
+ return import_node_path2.default.join(userDataDir(), "attached-projects.json");
11311
10946
  }
11312
10947
  function cloneEmpty() {
11313
10948
  return {
@@ -11321,7 +10956,7 @@ function loadStore() {
11321
10956
  const file = storeFile();
11322
10957
  let raw;
11323
10958
  try {
11324
- raw = import_node_fs4.default.readFileSync(file, "utf8");
10959
+ raw = import_node_fs3.default.readFileSync(file, "utf8");
11325
10960
  } catch (err) {
11326
10961
  if (err.code === "ENOENT") return cloneEmpty();
11327
10962
  throw err;
@@ -11338,7 +10973,7 @@ function loadStore() {
11338
10973
  } catch (err) {
11339
10974
  const quarantine = `${file}.corrupt-${Date.now()}`;
11340
10975
  try {
11341
- import_node_fs4.default.renameSync(file, quarantine);
10976
+ import_node_fs3.default.renameSync(file, quarantine);
11342
10977
  console.warn(
11343
10978
  `[sootsim] attached-projects.json was unparseable; quarantined to ${quarantine}. original error: ${err.message}`
11344
10979
  );
@@ -11349,16 +10984,16 @@ function loadStore() {
11349
10984
  }
11350
10985
  function writeStore(store) {
11351
10986
  const file = storeFile();
11352
- import_node_fs4.default.mkdirSync(import_node_path3.default.dirname(file), { recursive: true });
10987
+ import_node_fs3.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
11353
10988
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
11354
- const fd = import_node_fs4.default.openSync(tmp, "w", 384);
10989
+ const fd = import_node_fs3.default.openSync(tmp, "w", 384);
11355
10990
  try {
11356
- import_node_fs4.default.writeFileSync(fd, JSON.stringify(store, null, 2));
11357
- import_node_fs4.default.fsyncSync(fd);
10991
+ import_node_fs3.default.writeFileSync(fd, JSON.stringify(store, null, 2));
10992
+ import_node_fs3.default.fsyncSync(fd);
11358
10993
  } finally {
11359
- import_node_fs4.default.closeSync(fd);
10994
+ import_node_fs3.default.closeSync(fd);
11360
10995
  }
11361
- import_node_fs4.default.renameSync(tmp, file);
10996
+ import_node_fs3.default.renameSync(tmp, file);
11362
10997
  }
11363
10998
  function mutateStore(fn) {
11364
10999
  const store = loadStore();
@@ -11367,13 +11002,13 @@ function mutateStore(fn) {
11367
11002
  return store;
11368
11003
  }
11369
11004
  function projectIdForCwd(cwd) {
11370
- return (0, import_node_crypto.createHash)("sha256").update(import_node_path3.default.resolve(cwd)).digest("hex").slice(0, 16);
11005
+ return (0, import_node_crypto.createHash)("sha256").update(import_node_path2.default.resolve(cwd)).digest("hex").slice(0, 16);
11371
11006
  }
11372
11007
  function newSessionId() {
11373
11008
  return `s_${(0, import_node_crypto.randomBytes)(10).toString("hex")}`;
11374
11009
  }
11375
11010
  function upsertProject(input) {
11376
- const cwd = import_node_path3.default.resolve(input.cwd);
11011
+ const cwd = import_node_path2.default.resolve(input.cwd);
11377
11012
  const id = projectIdForCwd(cwd);
11378
11013
  let result;
11379
11014
  mutateStore((store) => {
@@ -11399,7 +11034,7 @@ function upsertProject(input) {
11399
11034
  const now = Date.now();
11400
11035
  const created = {
11401
11036
  id,
11402
- name: input.name ?? import_node_path3.default.basename(cwd),
11037
+ name: input.name ?? import_node_path2.default.basename(cwd),
11403
11038
  cwd,
11404
11039
  repoRoot: input.repoRoot,
11405
11040
  sourceRoots: input.sourceRoots ?? [cwd],
@@ -11523,23 +11158,13 @@ function updateSessionStatus(id, patch) {
11523
11158
  async function seedFromDemoAppRegistry() {
11524
11159
  const existing = loadStore().attachedProjects;
11525
11160
  if (existing.length > 0) return;
11526
- let APPS2;
11527
- try {
11528
- const mod = await Promise.resolve().then(() => (init_demo_app_registry(), demo_app_registry_exports));
11529
- APPS2 = mod.APPS;
11530
- } catch (err) {
11531
- console.warn(
11532
- "[sootsim] seedFromDemoAppRegistry: could not load demo registry:",
11533
- err.message
11534
- );
11535
- return;
11536
- }
11537
- if (!Array.isArray(APPS2)) return;
11538
- const apps = APPS2;
11161
+ const { loadOptionalDemoApps: loadOptionalDemoApps2 } = await Promise.resolve().then(() => (init_optional_demo_registry(), optional_demo_registry_exports));
11162
+ const apps = await loadOptionalDemoApps2();
11163
+ if (apps.length === 0) return;
11539
11164
  mutateStore((store) => {
11540
11165
  for (const app of apps) {
11541
- if (!import_node_fs4.default.existsSync(app.dir)) continue;
11542
- const cwd = import_node_path3.default.resolve(app.dir);
11166
+ if (!import_node_fs3.default.existsSync(app.dir)) continue;
11167
+ const cwd = import_node_path2.default.resolve(app.dir);
11543
11168
  const id = projectIdForCwd(cwd);
11544
11169
  if (store.attachedProjects.some((p) => p.id === id)) continue;
11545
11170
  const now = Date.now();
@@ -11560,13 +11185,13 @@ async function seedFromDemoAppRegistry() {
11560
11185
  }
11561
11186
  });
11562
11187
  }
11563
- var import_node_crypto, import_node_fs4, import_node_path3, overrideDir, COST_HISTORY_MAX_AGE_MS;
11188
+ var import_node_crypto, import_node_fs3, import_node_path2, overrideDir, COST_HISTORY_MAX_AGE_MS;
11564
11189
  var init_attached_projects = __esm({
11565
11190
  "src/attached-projects.ts"() {
11566
11191
  "use strict";
11567
11192
  import_node_crypto = require("node:crypto");
11568
- import_node_fs4 = __toESM(require("node:fs"), 1);
11569
- import_node_path3 = __toESM(require("node:path"), 1);
11193
+ import_node_fs3 = __toESM(require("node:fs"), 1);
11194
+ import_node_path2 = __toESM(require("node:path"), 1);
11570
11195
  init_home_paths();
11571
11196
  overrideDir = null;
11572
11197
  COST_HISTORY_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
@@ -11575,16 +11200,16 @@ var init_attached_projects = __esm({
11575
11200
 
11576
11201
  // src/agent-sessions.ts
11577
11202
  function sessionDir(sessionId) {
11578
- return import_node_path4.default.join(getUserDataDir(), "sessions", sessionId);
11203
+ return import_node_path3.default.join(getUserDataDir(), "sessions", sessionId);
11579
11204
  }
11580
11205
  function promptFifoPath(sessionId) {
11581
- return import_node_path4.default.join(sessionDir(sessionId), "prompt.in");
11206
+ return import_node_path3.default.join(sessionDir(sessionId), "prompt.in");
11582
11207
  }
11583
11208
  function eventsFifoPath(sessionId) {
11584
- return import_node_path4.default.join(sessionDir(sessionId), "events.out");
11209
+ return import_node_path3.default.join(sessionDir(sessionId), "events.out");
11585
11210
  }
11586
11211
  function transcriptPath(sessionId) {
11587
- return import_node_path4.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
11212
+ return import_node_path3.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
11588
11213
  }
11589
11214
  function pidIsAlive(pid, sessionId) {
11590
11215
  if (!pid) return false;
@@ -11594,7 +11219,7 @@ function pidIsAlive(pid, sessionId) {
11594
11219
  return false;
11595
11220
  }
11596
11221
  if (sessionId) {
11597
- if (!import_node_fs5.default.existsSync(sessionDir(sessionId))) return false;
11222
+ if (!import_node_fs4.default.existsSync(sessionDir(sessionId))) return false;
11598
11223
  }
11599
11224
  return true;
11600
11225
  }
@@ -11606,11 +11231,11 @@ function resolveSootsimInvocation() {
11606
11231
  const resourcesPath = process.resourcesPath;
11607
11232
  if (resourcesPath) {
11608
11233
  const candidates = [
11609
- import_node_path4.default.join(resourcesPath, "bin", "sootsim"),
11610
- import_node_path4.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
11234
+ import_node_path3.default.join(resourcesPath, "bin", "sootsim"),
11235
+ import_node_path3.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
11611
11236
  ];
11612
11237
  for (const c of candidates) {
11613
- if (import_node_fs5.default.existsSync(c)) return { cmd: c, prefixArgs: [] };
11238
+ if (import_node_fs4.default.existsSync(c)) return { cmd: c, prefixArgs: [] };
11614
11239
  }
11615
11240
  }
11616
11241
  }
@@ -11633,15 +11258,15 @@ function tryWorkspaceSootsim() {
11633
11258
  const sootsimDir = resolveSootsimPackageDir();
11634
11259
  if (!sootsimDir) return null;
11635
11260
  const binaryName = `sootsim-${process.platform}-${process.arch}`;
11636
- const distBinary = import_node_path4.default.join(sootsimDir, "dist-bin", binaryName);
11637
- if (import_node_fs5.default.existsSync(distBinary)) return { cmd: distBinary, prefixArgs: [] };
11638
- const distBin = import_node_path4.default.join(sootsimDir, "dist-cli", "bin.js");
11639
- if (import_node_fs5.default.existsSync(distBin)) {
11261
+ const distBinary = import_node_path3.default.join(sootsimDir, "dist-bin", binaryName);
11262
+ if (import_node_fs4.default.existsSync(distBinary)) return { cmd: distBinary, prefixArgs: [] };
11263
+ const distBin = import_node_path3.default.join(sootsimDir, "dist-cli", "bin.js");
11264
+ if (import_node_fs4.default.existsSync(distBin)) {
11640
11265
  try {
11641
- const src = import_node_path4.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
11642
- if (import_node_fs5.default.existsSync(src)) {
11643
- const srcMtime = import_node_fs5.default.statSync(src).mtimeMs;
11644
- const buildMtime = import_node_fs5.default.statSync(distBin).mtimeMs;
11266
+ const src = import_node_path3.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
11267
+ if (import_node_fs4.default.existsSync(src)) {
11268
+ const srcMtime = import_node_fs4.default.statSync(src).mtimeMs;
11269
+ const buildMtime = import_node_fs4.default.statSync(distBin).mtimeMs;
11645
11270
  if (buildMtime < srcMtime) {
11646
11271
  console.warn(
11647
11272
  `[sootsim] dist-cli/bin.js is older than agent-wrapper.ts \u2014 rebuild with \`bun run --cwd packages/sootsim build:cli\` (watch:cli:binary builds dist-bin/ instead).`
@@ -11660,22 +11285,22 @@ function tryWorkspaceSootsim() {
11660
11285
  function resolveSootsimPackageDir() {
11661
11286
  try {
11662
11287
  const resolved = require.resolve("sootsim/package.json");
11663
- return import_node_path4.default.dirname(resolved);
11288
+ return import_node_path3.default.dirname(resolved);
11664
11289
  } catch {
11665
11290
  }
11666
11291
  const here = fileFromImportMeta();
11667
11292
  if (!here) return null;
11668
- let cur = import_node_path4.default.dirname(here);
11293
+ let cur = import_node_path3.default.dirname(here);
11669
11294
  for (let i = 0; i < 8; i++) {
11670
- const pkg = import_node_path4.default.join(cur, "package.json");
11295
+ const pkg = import_node_path3.default.join(cur, "package.json");
11671
11296
  try {
11672
- if (import_node_fs5.default.existsSync(pkg)) {
11673
- const parsed = JSON.parse(import_node_fs5.default.readFileSync(pkg, "utf8"));
11297
+ if (import_node_fs4.default.existsSync(pkg)) {
11298
+ const parsed = JSON.parse(import_node_fs4.default.readFileSync(pkg, "utf8"));
11674
11299
  if (parsed.name === "sootsim") return cur;
11675
11300
  }
11676
11301
  } catch {
11677
11302
  }
11678
- const parent = import_node_path4.default.dirname(cur);
11303
+ const parent = import_node_path3.default.dirname(cur);
11679
11304
  if (parent === cur) break;
11680
11305
  cur = parent;
11681
11306
  }
@@ -11691,28 +11316,28 @@ function fileFromImportMeta() {
11691
11316
  }
11692
11317
  }
11693
11318
  async function withStartLock(projectId, provider, fn) {
11694
- const lockDir = import_node_path4.default.join(getUserDataDir(), "locks");
11695
- import_node_fs5.default.mkdirSync(lockDir, { recursive: true });
11319
+ const lockDir = import_node_path3.default.join(getUserDataDir(), "locks");
11320
+ import_node_fs4.default.mkdirSync(lockDir, { recursive: true });
11696
11321
  try {
11697
- import_node_fs5.default.chmodSync(lockDir, 448);
11322
+ import_node_fs4.default.chmodSync(lockDir, 448);
11698
11323
  } catch {
11699
11324
  }
11700
- const lockPath = import_node_path4.default.join(lockDir, `start-${projectId}-${provider}.lock`);
11325
+ const lockPath = import_node_path3.default.join(lockDir, `start-${projectId}-${provider}.lock`);
11701
11326
  const deadline = Date.now() + 4e3;
11702
11327
  let fd = null;
11703
11328
  while (fd === null) {
11704
11329
  try {
11705
- fd = import_node_fs5.default.openSync(
11330
+ fd = import_node_fs4.default.openSync(
11706
11331
  lockPath,
11707
- import_node_fs5.constants.O_WRONLY | import_node_fs5.constants.O_CREAT | import_node_fs5.constants.O_EXCL,
11332
+ import_node_fs4.constants.O_WRONLY | import_node_fs4.constants.O_CREAT | import_node_fs4.constants.O_EXCL,
11708
11333
  384
11709
11334
  );
11710
11335
  } catch (err) {
11711
11336
  if (err.code !== "EEXIST") throw err;
11712
11337
  try {
11713
- const stale = Number(import_node_fs5.default.readFileSync(lockPath, "utf8").trim());
11338
+ const stale = Number(import_node_fs4.default.readFileSync(lockPath, "utf8").trim());
11714
11339
  if (stale && !isProcessAlive(stale)) {
11715
- import_node_fs5.default.unlinkSync(lockPath);
11340
+ import_node_fs4.default.unlinkSync(lockPath);
11716
11341
  continue;
11717
11342
  }
11718
11343
  } catch {
@@ -11726,15 +11351,15 @@ async function withStartLock(projectId, provider, fn) {
11726
11351
  }
11727
11352
  }
11728
11353
  try {
11729
- import_node_fs5.default.writeFileSync(fd, String(process.pid));
11354
+ import_node_fs4.default.writeFileSync(fd, String(process.pid));
11730
11355
  return await fn();
11731
11356
  } finally {
11732
11357
  try {
11733
- import_node_fs5.default.closeSync(fd);
11358
+ import_node_fs4.default.closeSync(fd);
11734
11359
  } catch {
11735
11360
  }
11736
11361
  try {
11737
- import_node_fs5.default.unlinkSync(lockPath);
11362
+ import_node_fs4.default.unlinkSync(lockPath);
11738
11363
  } catch {
11739
11364
  }
11740
11365
  }
@@ -11748,25 +11373,25 @@ function isProcessAlive(pid) {
11748
11373
  }
11749
11374
  }
11750
11375
  function mkfifoSync(p) {
11751
- const parent = import_node_path4.default.dirname(p);
11752
- import_node_fs5.default.mkdirSync(parent, { recursive: true });
11376
+ const parent = import_node_path3.default.dirname(p);
11377
+ import_node_fs4.default.mkdirSync(parent, { recursive: true });
11753
11378
  try {
11754
- import_node_fs5.default.chmodSync(parent, 448);
11379
+ import_node_fs4.default.chmodSync(parent, 448);
11755
11380
  } catch {
11756
11381
  }
11757
- if (import_node_fs5.default.existsSync(p)) {
11382
+ if (import_node_fs4.default.existsSync(p)) {
11758
11383
  try {
11759
- const stat = import_node_fs5.default.statSync(p);
11384
+ const stat = import_node_fs4.default.statSync(p);
11760
11385
  if (stat.isFIFO()) {
11761
11386
  try {
11762
- import_node_fs5.default.chmodSync(p, 384);
11387
+ import_node_fs4.default.chmodSync(p, 384);
11763
11388
  } catch {
11764
11389
  }
11765
11390
  return;
11766
11391
  }
11767
- import_node_fs5.default.unlinkSync(p);
11392
+ import_node_fs4.default.unlinkSync(p);
11768
11393
  } catch {
11769
- import_node_fs5.default.unlinkSync(p);
11394
+ import_node_fs4.default.unlinkSync(p);
11770
11395
  }
11771
11396
  }
11772
11397
  const result = (0, import_node_child_process.spawnSync)("mkfifo", ["-m", "600", p]);
@@ -11806,10 +11431,10 @@ async function startSession(opts) {
11806
11431
  const transcript = transcriptPath(session.id);
11807
11432
  mkfifoSync(promptIn);
11808
11433
  mkfifoSync(eventsOut);
11809
- const transcriptDir = import_node_path4.default.dirname(transcript);
11810
- import_node_fs5.default.mkdirSync(transcriptDir, { recursive: true });
11434
+ const transcriptDir = import_node_path3.default.dirname(transcript);
11435
+ import_node_fs4.default.mkdirSync(transcriptDir, { recursive: true });
11811
11436
  try {
11812
- import_node_fs5.default.chmodSync(transcriptDir, 448);
11437
+ import_node_fs4.default.chmodSync(transcriptDir, 448);
11813
11438
  } catch {
11814
11439
  }
11815
11440
  const { cmd, prefixArgs } = resolveSootsimInvocation();
@@ -11860,7 +11485,7 @@ async function startSession(opts) {
11860
11485
  }
11861
11486
  }
11862
11487
  try {
11863
- import_node_fs5.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
11488
+ import_node_fs4.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
11864
11489
  } catch {
11865
11490
  }
11866
11491
  updateSessionStatus(session.id, { status: "ended" });
@@ -11888,18 +11513,18 @@ async function sendPrompt(sessionId, prompt) {
11888
11513
  );
11889
11514
  }
11890
11515
  const fifo = promptFifoPath(sessionId);
11891
- if (!import_node_fs5.default.existsSync(fifo)) {
11516
+ if (!import_node_fs4.default.existsSync(fifo)) {
11892
11517
  throw new AgentSessionError("NO_FIFO", `prompt FIFO missing: ${fifo}`);
11893
11518
  }
11894
- const fd = import_node_fs5.default.openSync(fifo, import_node_fs5.constants.O_WRONLY);
11519
+ const fd = import_node_fs4.default.openSync(fifo, import_node_fs4.constants.O_WRONLY);
11895
11520
  try {
11896
11521
  const wireText = encodeAgentPromptEnvelope(prompt);
11897
11522
  if (!wireText) {
11898
11523
  throw new AgentSessionError("EMPTY_PROMPT", "prompt text is empty");
11899
11524
  }
11900
- import_node_fs5.default.writeSync(fd, wireText + "\n");
11525
+ import_node_fs4.default.writeSync(fd, wireText + "\n");
11901
11526
  } finally {
11902
- import_node_fs5.default.closeSync(fd);
11527
+ import_node_fs4.default.closeSync(fd);
11903
11528
  }
11904
11529
  updateSessionStatus(sessionId, {
11905
11530
  lastPrompt: prompt.displayText ?? prompt.text,
@@ -11921,7 +11546,7 @@ async function endSession(sessionId) {
11921
11546
  const base = getUserDataDir();
11922
11547
  if (dir.startsWith(base)) {
11923
11548
  try {
11924
- import_node_fs5.default.rmSync(dir, { recursive: true, force: true });
11549
+ import_node_fs4.default.rmSync(dir, { recursive: true, force: true });
11925
11550
  } catch {
11926
11551
  }
11927
11552
  }
@@ -11929,11 +11554,11 @@ async function endSession(sessionId) {
11929
11554
  }
11930
11555
  function subscribeEvents(sessionId, onEvent) {
11931
11556
  const fifo = eventsFifoPath(sessionId);
11932
- if (!import_node_fs5.default.existsSync(fifo)) {
11557
+ if (!import_node_fs4.default.existsSync(fifo)) {
11933
11558
  throw new AgentSessionError("NO_FIFO", `events FIFO missing: ${fifo}`);
11934
11559
  }
11935
- const fd = import_node_fs5.default.openSync(fifo, import_node_fs5.constants.O_RDWR);
11936
- const stream = import_node_fs5.default.createReadStream("", { fd, autoClose: true });
11560
+ const fd = import_node_fs4.default.openSync(fifo, import_node_fs4.constants.O_RDWR);
11561
+ const stream = import_node_fs4.default.createReadStream("", { fd, autoClose: true });
11937
11562
  const rl = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
11938
11563
  rl.on("line", (line) => {
11939
11564
  const event = parseAgentEventLine(line);
@@ -11954,7 +11579,7 @@ function subscribeEvents(sessionId, onEvent) {
11954
11579
  };
11955
11580
  }
11956
11581
  async function waitForFirstEvent(fifo, predicate, timeoutMs) {
11957
- const fd = import_node_fs5.default.openSync(fifo, import_node_fs5.constants.O_RDWR | import_node_fs5.constants.O_NONBLOCK);
11582
+ const fd = import_node_fs4.default.openSync(fifo, import_node_fs4.constants.O_RDWR | import_node_fs4.constants.O_NONBLOCK);
11958
11583
  const buf = Buffer.alloc(8192);
11959
11584
  let leftover = "";
11960
11585
  const deadline = Date.now() + timeoutMs;
@@ -11962,7 +11587,7 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
11962
11587
  while (Date.now() < deadline) {
11963
11588
  let n = 0;
11964
11589
  try {
11965
- n = import_node_fs5.default.readSync(fd, buf, 0, buf.length, null);
11590
+ n = import_node_fs4.default.readSync(fd, buf, 0, buf.length, null);
11966
11591
  } catch (err) {
11967
11592
  if (err.code !== "EAGAIN") throw err;
11968
11593
  n = 0;
@@ -11982,17 +11607,17 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
11982
11607
  }
11983
11608
  return null;
11984
11609
  } finally {
11985
- import_node_fs5.default.closeSync(fd);
11610
+ import_node_fs4.default.closeSync(fd);
11986
11611
  }
11987
11612
  }
11988
- var import_node_child_process, import_node_crypto2, import_node_fs5, import_node_path4, import_node_readline, AgentSessionError;
11613
+ var import_node_child_process, import_node_crypto2, import_node_fs4, import_node_path3, import_node_readline, AgentSessionError;
11989
11614
  var init_agent_sessions = __esm({
11990
11615
  "src/agent-sessions.ts"() {
11991
11616
  "use strict";
11992
11617
  import_node_child_process = require("node:child_process");
11993
11618
  import_node_crypto2 = require("node:crypto");
11994
- import_node_fs5 = __toESM(require("node:fs"), 1);
11995
- import_node_path4 = __toESM(require("node:path"), 1);
11619
+ import_node_fs4 = __toESM(require("node:fs"), 1);
11620
+ import_node_path3 = __toESM(require("node:path"), 1);
11996
11621
  import_node_readline = __toESM(require("node:readline"), 1);
11997
11622
  init_agent_events();
11998
11623
  init_agent_prompt();
@@ -12021,12 +11646,12 @@ function mapFrameworkToProjectFramework(fw) {
12021
11646
  if (fw === "one" || fw === "vxrn") return "one";
12022
11647
  return "unknown";
12023
11648
  }
12024
- var import_node_fs6, import_node_path5, WS_OPEN, AgentHost;
11649
+ var import_node_fs5, import_node_path4, WS_OPEN, AgentHost;
12025
11650
  var init_agent_host = __esm({
12026
11651
  "src/host/agent-host.ts"() {
12027
11652
  "use strict";
12028
- import_node_fs6 = __toESM(require("node:fs"), 1);
12029
- import_node_path5 = __toESM(require("node:path"), 1);
11653
+ import_node_fs5 = __toESM(require("node:fs"), 1);
11654
+ import_node_path4 = __toESM(require("node:path"), 1);
12030
11655
  init_dev_server_scanner();
12031
11656
  init_agent_sessions();
12032
11657
  init_attached_projects();
@@ -12188,7 +11813,7 @@ var init_agent_host = __esm({
12188
11813
  );
12189
11814
  const project = upsertProject({
12190
11815
  cwd: match.cwd,
12191
- name: match.projectName ?? import_node_path5.default.basename(match.cwd),
11816
+ name: match.projectName ?? import_node_path4.default.basename(match.cwd),
12192
11817
  preferredProvider: input.provider ?? existing?.preferredProvider,
12193
11818
  sourceRoots: existing?.sourceRoots ?? [match.cwd],
12194
11819
  knownBundleUrls,
@@ -12199,18 +11824,18 @@ var init_agent_host = __esm({
12199
11824
  }
12200
11825
  getTranscript(sessionId) {
12201
11826
  const p = transcriptPath(sessionId);
12202
- if (!import_node_fs6.default.existsSync(p)) {
11827
+ if (!import_node_fs5.default.existsSync(p)) {
12203
11828
  return { error: "transcript not found", code: "NO_TRANSCRIPT" };
12204
11829
  }
12205
- return import_node_fs6.default.readFileSync(p, "utf8");
11830
+ return import_node_fs5.default.readFileSync(p, "utf8");
12206
11831
  }
12207
11832
  getPaths() {
12208
11833
  const dir = getUserDataDir();
12209
11834
  return {
12210
11835
  userDataDir: dir,
12211
- storeFile: import_node_path5.default.join(dir, "attached-projects.json"),
12212
- sessionsDir: import_node_path5.default.join(dir, "sessions"),
12213
- transcriptsDir: import_node_path5.default.join(dir, "transcripts")
11836
+ storeFile: import_node_path4.default.join(dir, "attached-projects.json"),
11837
+ sessionsDir: import_node_path4.default.join(dir, "sessions"),
11838
+ transcriptsDir: import_node_path4.default.join(dir, "transcripts")
12214
11839
  };
12215
11840
  }
12216
11841
  // --- subscription management ---
@@ -12825,7 +12450,7 @@ function buildChromiumUrlCommand(url, options = {}) {
12825
12450
  };
12826
12451
  }
12827
12452
  async function spawnLaunch(command, args, detached) {
12828
- return new Promise((resolve7, reject) => {
12453
+ return new Promise((resolve6, reject) => {
12829
12454
  const child = (0, import_child_process3.spawn)(command, args, {
12830
12455
  detached,
12831
12456
  stdio: "ignore"
@@ -12833,7 +12458,7 @@ async function spawnLaunch(command, args, detached) {
12833
12458
  child.once("error", reject);
12834
12459
  child.once("spawn", () => {
12835
12460
  if (detached) child.unref();
12836
- resolve7(child.pid);
12461
+ resolve6(child.pid);
12837
12462
  });
12838
12463
  });
12839
12464
  }
@@ -13290,7 +12915,7 @@ var init_bridge_host = __esm({
13290
12915
  );
13291
12916
  }
13292
12917
  bindOnce(port, _silent) {
13293
- return new Promise((resolve7, reject) => {
12918
+ return new Promise((resolve6, reject) => {
13294
12919
  const server = (0, import_http3.createServer)((req, res) => this.handleHttpRequest(req, res));
13295
12920
  let settled = false;
13296
12921
  const onError = (err) => {
@@ -13322,7 +12947,7 @@ var init_bridge_host = __esm({
13322
12947
  this.wss?.emit("connection", ws, req);
13323
12948
  });
13324
12949
  });
13325
- resolve7();
12950
+ resolve6();
13326
12951
  });
13327
12952
  });
13328
12953
  }
@@ -14297,7 +13922,7 @@ var init_bridge_host = __esm({
14297
13922
  async sendCommand(cmd) {
14298
13923
  const sim = await this.waitForSim(cmd.simId);
14299
13924
  const id = this.nextCommandId++;
14300
- return new Promise((resolve7, reject) => {
13925
+ return new Promise((resolve6, reject) => {
14301
13926
  const timeout = setTimeout(() => {
14302
13927
  this.pendingCommands.delete(id);
14303
13928
  this.broadcastSimClientStates();
@@ -14309,7 +13934,7 @@ var init_bridge_host = __esm({
14309
13934
  clearTimeout(timeout);
14310
13935
  this.pendingCommands.delete(id);
14311
13936
  this.broadcastSimClientStates();
14312
- resolve7(value);
13937
+ resolve6(value);
14313
13938
  },
14314
13939
  reject: (error) => {
14315
13940
  clearTimeout(timeout);
@@ -14335,23 +13960,23 @@ var init_bridge_host = __esm({
14335
13960
  async openPathInEditor(filePath, line, column) {
14336
13961
  const loc = line != null ? `:${line}${column != null ? `:${column}` : ""}` : "";
14337
13962
  const target = `${filePath}${loc}`;
14338
- const trySpawn = (cmd, args) => new Promise((resolve7) => {
13963
+ const trySpawn = (cmd, args) => new Promise((resolve6) => {
14339
13964
  try {
14340
13965
  const child = (0, import_child_process4.spawn)(cmd, args, { detached: true, stdio: "ignore" });
14341
13966
  let settled = false;
14342
13967
  child.on("error", () => {
14343
13968
  if (settled) return;
14344
13969
  settled = true;
14345
- resolve7(false);
13970
+ resolve6(false);
14346
13971
  });
14347
13972
  child.on("spawn", () => {
14348
13973
  if (settled) return;
14349
13974
  settled = true;
14350
13975
  child.unref();
14351
- resolve7(true);
13976
+ resolve6(true);
14352
13977
  });
14353
13978
  } catch {
14354
- resolve7(false);
13979
+ resolve6(false);
14355
13980
  }
14356
13981
  });
14357
13982
  const envEditor = process.env.REACT_EDITOR || process.env.EDITOR;
@@ -14578,7 +14203,7 @@ var init_bridge_host = __esm({
14578
14203
  for (let attempt = 0; attempt < attempts; attempt++) {
14579
14204
  const sim = this.getOpenSim(simId);
14580
14205
  if (sim) return sim;
14581
- await new Promise((resolve7) => setTimeout(resolve7, intervalMs));
14206
+ await new Promise((resolve6) => setTimeout(resolve6, intervalMs));
14582
14207
  }
14583
14208
  throw new Error(simId ? `no sim connected with id ${simId}` : "no sim connected");
14584
14209
  }
@@ -14801,9 +14426,9 @@ var init_bridge_host = __esm({
14801
14426
 
14802
14427
  // cli/parent-pid.ts
14803
14428
  function getParentPid(pid) {
14804
- if (!cached2) cached2 = build();
14429
+ if (!cached3) cached3 = build();
14805
14430
  try {
14806
- return cached2(pid);
14431
+ return cached3(pid);
14807
14432
  } catch {
14808
14433
  return null;
14809
14434
  }
@@ -14877,14 +14502,14 @@ function buildDarwinLsof() {
14877
14502
  }
14878
14503
  };
14879
14504
  }
14880
- var import_child_process5, import_fs4, import_module, cached2, PROC_PIDTBSDINFO, PROC_BSDINFO_SIZE, PBI_PPID_OFFSET;
14505
+ var import_child_process5, import_fs4, import_module, cached3, PROC_PIDTBSDINFO, PROC_BSDINFO_SIZE, PBI_PPID_OFFSET;
14881
14506
  var init_parent_pid = __esm({
14882
14507
  "cli/parent-pid.ts"() {
14883
14508
  "use strict";
14884
14509
  import_child_process5 = require("child_process");
14885
14510
  import_fs4 = require("fs");
14886
14511
  import_module = require("module");
14887
- cached2 = null;
14512
+ cached3 = null;
14888
14513
  PROC_PIDTBSDINFO = 3;
14889
14514
  PROC_BSDINFO_SIZE = 216;
14890
14515
  PBI_PPID_OFFSET = 16;
@@ -15095,7 +14720,7 @@ function createBridge(wsPort, opts = {}) {
15095
14720
  const pending = /* @__PURE__ */ new Map();
15096
14721
  const ws = new import_ws3.WebSocket(`ws://localhost:${wsPort}`);
15097
14722
  const identity = getCliIdentity();
15098
- const ready = new Promise((resolve7, reject) => {
14723
+ const ready = new Promise((resolve6, reject) => {
15099
14724
  ws.on("open", () => {
15100
14725
  try {
15101
14726
  ws.send(
@@ -15109,7 +14734,7 @@ function createBridge(wsPort, opts = {}) {
15109
14734
  );
15110
14735
  } catch {
15111
14736
  }
15112
- resolve7();
14737
+ resolve6();
15113
14738
  });
15114
14739
  ws.on("error", (err) => {
15115
14740
  const cause = err.message ? `: ${err.message}` : "";
@@ -15175,7 +14800,7 @@ function createBridge(wsPort, opts = {}) {
15175
14800
  const sendOnce = async (defaultSimId) => {
15176
14801
  await ready;
15177
14802
  const id = nextId++;
15178
- return new Promise((resolve7, reject) => {
14803
+ return new Promise((resolve6, reject) => {
15179
14804
  const timeout = setTimeout(() => {
15180
14805
  pending.delete(id);
15181
14806
  reject(
@@ -15187,7 +14812,7 @@ function createBridge(wsPort, opts = {}) {
15187
14812
  pending.set(id, {
15188
14813
  resolve: (v) => {
15189
14814
  clearTimeout(timeout);
15190
- resolve7(v);
14815
+ resolve6(v);
15191
14816
  },
15192
14817
  reject: (e) => {
15193
14818
  clearTimeout(timeout);
@@ -15632,19 +15257,19 @@ async function createFramedScreenshotComposer(browser) {
15632
15257
  }
15633
15258
  return {
15634
15259
  async compose(rawPng, model) {
15635
- const cached3 = await getCachedPage(model);
15260
+ const cached4 = await getCachedPage(model);
15636
15261
  const rawImageDataUrl = `data:image/png;base64,${rawPng.toString("base64")}`;
15637
- await cached3.page.setContent(renderFrameHtml(cached3.layout, rawImageDataUrl), {
15262
+ await cached4.page.setContent(renderFrameHtml(cached4.layout, rawImageDataUrl), {
15638
15263
  waitUntil: "load"
15639
15264
  });
15640
- await cached3.page.waitForTimeout(20);
15641
- return await cached3.page.screenshot({
15265
+ await cached4.page.waitForTimeout(20);
15266
+ return await cached4.page.screenshot({
15642
15267
  type: "png",
15643
15268
  clip: {
15644
15269
  x: 0,
15645
15270
  y: 0,
15646
- width: cached3.layout.outerWidth,
15647
- height: cached3.layout.outerHeight
15271
+ width: cached4.layout.outerWidth,
15272
+ height: cached4.layout.outerHeight
15648
15273
  },
15649
15274
  omitBackground: true
15650
15275
  });
@@ -15877,14 +15502,14 @@ var init_flow_file = __esm({
15877
15502
  function getBaseDir() {
15878
15503
  const explicit = process.env[SESSION_FILE_ENV];
15879
15504
  if (explicit?.trim()) {
15880
- return (0, import_node_path6.dirname)((0, import_node_path6.resolve)(explicit));
15505
+ return (0, import_node_path5.dirname)((0, import_node_path5.resolve)(explicit));
15881
15506
  }
15882
15507
  return electronUserDataDir();
15883
15508
  }
15884
15509
  function getSharedDesktopAuthFilePath() {
15885
15510
  const explicit = process.env[SESSION_FILE_ENV];
15886
- if (explicit?.trim()) return (0, import_node_path6.resolve)(explicit);
15887
- return (0, import_node_path6.join)(getBaseDir(), "desktop-auth.json");
15511
+ if (explicit?.trim()) return (0, import_node_path5.resolve)(explicit);
15512
+ return (0, import_node_path5.join)(getBaseDir(), "desktop-auth.json");
15888
15513
  }
15889
15514
  function normalizeUser(input) {
15890
15515
  if (!input || typeof input !== "object") return null;
@@ -15930,26 +15555,26 @@ function normalizeSession(input) {
15930
15555
  }
15931
15556
  function readSharedDesktopAuthSession() {
15932
15557
  const filepath = getSharedDesktopAuthFilePath();
15933
- if (!(0, import_node_fs7.existsSync)(filepath)) return null;
15558
+ if (!(0, import_node_fs6.existsSync)(filepath)) return null;
15934
15559
  try {
15935
- const parsed = JSON.parse((0, import_node_fs7.readFileSync)(filepath, "utf8"));
15560
+ const parsed = JSON.parse((0, import_node_fs6.readFileSync)(filepath, "utf8"));
15936
15561
  const normalized = normalizeSession(parsed);
15937
15562
  if (!normalized) {
15938
- (0, import_node_fs7.rmSync)(filepath, { force: true });
15563
+ (0, import_node_fs6.rmSync)(filepath, { force: true });
15939
15564
  return null;
15940
15565
  }
15941
15566
  return normalized;
15942
15567
  } catch {
15943
- (0, import_node_fs7.rmSync)(filepath, { force: true });
15568
+ (0, import_node_fs6.rmSync)(filepath, { force: true });
15944
15569
  return null;
15945
15570
  }
15946
15571
  }
15947
- var import_node_fs7, import_node_path6, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
15572
+ var import_node_fs6, import_node_path5, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
15948
15573
  var init_shared_session = __esm({
15949
15574
  "src/auth/shared-session.ts"() {
15950
15575
  "use strict";
15951
- import_node_fs7 = require("node:fs");
15952
- import_node_path6 = require("node:path");
15576
+ import_node_fs6 = require("node:fs");
15577
+ import_node_path5 = require("node:path");
15953
15578
  init_home_paths();
15954
15579
  SESSION_VERSION = 1;
15955
15580
  SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
@@ -15959,13 +15584,13 @@ var init_shared_session = __esm({
15959
15584
 
15960
15585
  // cli/auth.ts
15961
15586
  function credentialsPath() {
15962
- return (0, import_node_path7.join)(electronUserDataDir(), "credentials.json");
15587
+ return (0, import_node_path6.join)(electronUserDataDir(), "credentials.json");
15963
15588
  }
15964
15589
  function readStoredApiKey() {
15965
15590
  const file = credentialsPath();
15966
- if (!(0, import_node_fs8.existsSync)(file)) return null;
15591
+ if (!(0, import_node_fs7.existsSync)(file)) return null;
15967
15592
  try {
15968
- const parsed = JSON.parse((0, import_node_fs8.readFileSync)(file, "utf8"));
15593
+ const parsed = JSON.parse((0, import_node_fs7.readFileSync)(file, "utf8"));
15969
15594
  if (parsed.version !== 1) return null;
15970
15595
  if (typeof parsed.apiKey !== "string" || !parsed.apiKey.startsWith("sk_sootsim_")) {
15971
15596
  return null;
@@ -16021,12 +15646,12 @@ function resolveCliAuth() {
16021
15646
  function authHeaderValue(auth) {
16022
15647
  return auth.kind === "api-key" ? `Bearer ${auth.secret}` : `Bearer ${auth.token}`;
16023
15648
  }
16024
- var import_node_fs8, import_node_path7, GITHUB_TOKEN_PREFIXES;
15649
+ var import_node_fs7, import_node_path6, GITHUB_TOKEN_PREFIXES;
16025
15650
  var init_auth = __esm({
16026
15651
  "cli/auth.ts"() {
16027
15652
  "use strict";
16028
- import_node_fs8 = require("node:fs");
16029
- import_node_path7 = require("node:path");
15653
+ import_node_fs7 = require("node:fs");
15654
+ import_node_path6 = require("node:path");
16030
15655
  init_shared_session();
16031
15656
  init_home_paths();
16032
15657
  GITHUB_TOKEN_PREFIXES = ["ghs_", "ghp_", "gho_", "ghu_", "github_pat_"];
@@ -16203,7 +15828,7 @@ function clearFlowScopedVars() {
16203
15828
  flowScopedVars.clear();
16204
15829
  }
16205
15830
  function sleep(ms) {
16206
- return new Promise((resolve7) => setTimeout(resolve7, ms));
15831
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
16207
15832
  }
16208
15833
  function resolveFlowScreenshotPath(screenshotDir, name) {
16209
15834
  return resolveFlowScreenshotPathWithMode(screenshotDir, name, {
@@ -16434,8 +16059,9 @@ var init_bridge_flow_runner = __esm({
16434
16059
  const result = await this.evaluate(`(() => {
16435
16060
  const rec = window.__sootsimRecorder
16436
16061
  if (!rec) return { ok: false, error: 'recorder unavailable on this page' }
16437
- if (rec.state() === 'recording') return { ok: true, format: 'webm' }
16438
- return rec.start({ format: 'webm', fps: 24 })
16062
+ const format = ${JSON.stringify(this.opts.recordingFormat ?? "webm")}
16063
+ if (rec.state() === 'recording') return { ok: true, format }
16064
+ return rec.start({ format, fps: 24 })
16439
16065
  })()`);
16440
16066
  if (!result?.ok) {
16441
16067
  throw new Error(result?.error || "recording unavailable on this page");
@@ -16480,9 +16106,11 @@ var init_bridge_flow_runner = __esm({
16480
16106
  throw new Error("recording requested but no video buffer was produced");
16481
16107
  }
16482
16108
  fs7.mkdirSync(this.opts.recordingOutputDir, { recursive: true });
16109
+ const videoMime = stopped.mime || this.opts.recordingFormat || "video/webm";
16110
+ const extension = videoMime.includes("mp4") ? "mp4" : "webm";
16483
16111
  const outputPath = path7.join(
16484
16112
  this.opts.recordingOutputDir,
16485
- `sootsim-${Date.now()}.webm`
16113
+ `sootsim-${Date.now()}.${extension}`
16486
16114
  );
16487
16115
  fs7.writeFileSync(outputPath, Buffer.concat(chunks));
16488
16116
  return outputPath;
@@ -17838,7 +17466,7 @@ var skill4 = {
17838
17466
 
17839
17467
  // src/skills/builtin/test-flow.ts
17840
17468
  function sleep2(ms) {
17841
- return new Promise((resolve7) => setTimeout(resolve7, ms));
17469
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
17842
17470
  }
17843
17471
  var skill5 = {
17844
17472
  name: "test-flow",