sootsim 0.1.294 → 0.1.296

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 (186) hide show
  1. package/README.md +13 -4
  2. package/dist-cli/bin.js +5 -5
  3. package/dist-cli/chunks/{agent-GDFF6G7N.js → agent-3F4K6MQK.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-ZUKQUOD2.js → agent-wrapper-5LLVCQRZ.js} +2 -2
  5. package/dist-cli/chunks/app-fonts-LPTY7A5G.js +4 -0
  6. package/dist-cli/chunks/{assert-GXOGZUKO.js → assert-R2ZUXEMS.js} +2 -2
  7. package/dist-cli/chunks/{auth-P7FAKLHQ.js → auth-CA62Q6IS.js} +2 -2
  8. package/dist-cli/chunks/{beta-RXT5RU3A.js → beta-4AXSOQLU.js} +2 -2
  9. package/dist-cli/chunks/{camera-RNVAC4IO.js → camera-Y4RBIONE.js} +2 -2
  10. package/dist-cli/chunks/{chunk-OYYQQBKP.js → chunk-222Y7NQA.js} +1 -1
  11. package/dist-cli/chunks/chunk-2LQOLMRK.js +4 -0
  12. package/dist-cli/chunks/{chunk-AFLGWOX3.js → chunk-2XUKWIAL.js} +1 -1
  13. package/dist-cli/chunks/{chunk-DI55E6CS.js → chunk-3PAZJSIG.js} +2 -2
  14. package/dist-cli/chunks/{chunk-2M7OZ6Y4.js → chunk-4MDLYOEI.js} +2 -2
  15. package/dist-cli/chunks/{chunk-EXTV6NWM.js → chunk-4NFGOGB7.js} +2 -2
  16. package/dist-cli/chunks/chunk-54TQ3ZGI.js +30 -0
  17. package/dist-cli/chunks/chunk-5FNPAMP4.js +125 -0
  18. package/dist-cli/chunks/{chunk-4Z4URZOG.js → chunk-6C24WCDK.js} +2 -2
  19. package/dist-cli/chunks/{chunk-KWIURH7N.js → chunk-74FOMZP7.js} +2 -2
  20. package/dist-cli/chunks/{chunk-JUOQ4ZPA.js → chunk-7GQWF7HE.js} +1 -1
  21. package/dist-cli/chunks/{chunk-BXO2PTNM.js → chunk-7V3JNANH.js} +4 -4
  22. package/dist-cli/chunks/{chunk-DUHBRW7J.js → chunk-7YQXQSG7.js} +1 -1
  23. package/dist-cli/chunks/{chunk-FSRRJGYC.js → chunk-AUBP44CY.js} +2 -2
  24. package/dist-cli/chunks/chunk-B2FB6IYX.js +4 -0
  25. package/dist-cli/chunks/{chunk-FNCSUHUR.js → chunk-B2LDO55K.js} +1 -1
  26. package/dist-cli/chunks/{chunk-K53YCAQM.js → chunk-BVWIR4CD.js} +1 -1
  27. package/dist-cli/chunks/{chunk-RWUNC66S.js → chunk-C462URRQ.js} +2 -2
  28. package/dist-cli/chunks/chunk-CAAADPRH.js +10 -0
  29. package/dist-cli/chunks/{chunk-VXGCZUX4.js → chunk-CDCVWGNU.js} +3 -3
  30. package/dist-cli/chunks/{chunk-WYRLRNIP.js → chunk-CS23DQB2.js} +2 -2
  31. package/dist-cli/chunks/chunk-DUYKBCM7.js +4 -0
  32. package/dist-cli/chunks/chunk-EISE56VD.js +6 -0
  33. package/dist-cli/chunks/{chunk-VVVORIQI.js → chunk-EJ6BAJKX.js} +1 -1
  34. package/dist-cli/chunks/chunk-F2MS365Z.js +4 -0
  35. package/dist-cli/chunks/{chunk-AMKQUZA2.js → chunk-FROCFR77.js} +1 -1
  36. package/dist-cli/chunks/{chunk-I233T2T4.js → chunk-FUPQIX4U.js} +2 -2
  37. package/dist-cli/chunks/{chunk-V2MYTL4C.js → chunk-G2SO5FPN.js} +2 -2
  38. package/dist-cli/chunks/chunk-GG5PWLGC.js +4 -0
  39. package/dist-cli/chunks/chunk-GG6XOQ5P.js +15 -0
  40. package/dist-cli/chunks/chunk-HSGS2U7P.js +6 -0
  41. package/dist-cli/chunks/{chunk-PZTKQG6H.js → chunk-IR3DUBRF.js} +5 -5
  42. package/dist-cli/chunks/chunk-LHUY46GE.js +4 -0
  43. package/dist-cli/chunks/{chunk-N4MT5KEB.js → chunk-MDBR4XTV.js} +3 -3
  44. package/dist-cli/chunks/{chunk-SLMUW2WU.js → chunk-MQ6QX46T.js} +2 -2
  45. package/dist-cli/chunks/chunk-N3FZRJT7.js +4 -0
  46. package/dist-cli/chunks/{chunk-ISJ4ACWL.js → chunk-NEARYPSC.js} +11 -11
  47. package/dist-cli/chunks/chunk-P7HBJBHH.js +4 -0
  48. package/dist-cli/chunks/{chunk-24BJFJFB.js → chunk-PEUWZRXA.js} +5 -5
  49. package/dist-cli/chunks/{chunk-PSOUSJSZ.js → chunk-QIDFGREY.js} +1 -1
  50. package/dist-cli/chunks/{chunk-ZUX5PIGI.js → chunk-R65XMN4D.js} +1 -1
  51. package/dist-cli/chunks/{chunk-AO65WEF7.js → chunk-S5Z4HKWX.js} +1 -1
  52. package/dist-cli/chunks/chunk-SUJSGGSS.js +23 -0
  53. package/dist-cli/chunks/{chunk-O6I5ZGMY.js → chunk-TIOPG6QZ.js} +1 -1
  54. package/dist-cli/chunks/chunk-WJVBD2UL.js +9 -0
  55. package/dist-cli/chunks/{chunk-D2R7PDN5.js → chunk-X7B5KJ6N.js} +1 -1
  56. package/dist-cli/chunks/{chunk-7HVMAGDK.js → chunk-XMVVZCIM.js} +2 -2
  57. package/dist-cli/chunks/chunk-ZIBKGZJD.js +4 -0
  58. package/dist-cli/chunks/chunk-ZPBIHFQW.js +12 -0
  59. package/dist-cli/chunks/cleanup-4RSKYPQ7.js +25 -0
  60. package/dist-cli/chunks/{cli-version-2JGMYL4O.js → cli-version-VZFF7GGM.js} +2 -2
  61. package/dist-cli/chunks/compat-FABLN4UI.js +20 -0
  62. package/dist-cli/chunks/config-74J37LKC.js +13 -0
  63. package/dist-cli/chunks/control-VBK32Y7F.js +4 -0
  64. package/dist-cli/chunks/daemon-VFO7LRR4.js +4 -0
  65. package/dist-cli/chunks/{debug-XUGWZLIW.js → debug-AOYA2CLR.js} +3 -3
  66. package/dist-cli/chunks/{desktop-IO5HPX5Z.js → desktop-G7XWDCTM.js} +3 -3
  67. package/dist-cli/chunks/{detox-E42OEJJZ.js → detox-QXJ3L2ZI.js} +2 -2
  68. package/dist-cli/chunks/{device-XA67L2EB.js → device-NZZ5ZT3Q.js} +2 -2
  69. package/dist-cli/chunks/{diagnose-36FD6ECF.js → diagnose-5CAEJ42M.js} +2 -2
  70. package/dist-cli/chunks/disk-cleanup-GZSUZJ6Y.js +4 -0
  71. package/dist-cli/chunks/drivers-OLJCUSPO.js +4 -0
  72. package/dist-cli/chunks/{film-XARTGFIZ.js → film-C25VRUNC.js} +3 -3
  73. package/dist-cli/chunks/flow-IW3WUF5D.js +4 -0
  74. package/dist-cli/chunks/help-GEVJF2DA.js +4 -0
  75. package/dist-cli/chunks/{hidden-runtime-alias-T4ZALBE2.js → hidden-runtime-alias-NSLIK6XT.js} +2 -2
  76. package/dist-cli/chunks/home-paths-OC6JZXIM.js +4 -0
  77. package/dist-cli/chunks/inspect-KXJNI7CU.js +4 -0
  78. package/dist-cli/chunks/install-desktop-IYWPRFPB.js +4 -0
  79. package/dist-cli/chunks/{login-GF6WVVER.js → login-LB4TMM4T.js} +4 -4
  80. package/dist-cli/chunks/{logout-6F6UKUNI.js → logout-33IOBD4X.js} +2 -2
  81. package/dist-cli/chunks/{maestro-FMPKHEMX.js → maestro-EEVXX536.js} +3 -3
  82. package/dist-cli/chunks/{maestro-generate-QGPGNXI6.js → maestro-generate-ICPWVXGA.js} +3 -3
  83. package/dist-cli/chunks/{mode-JSNMAHDZ.js → mode-6IXOZDK3.js} +2 -2
  84. package/dist-cli/chunks/{optional-demo-registry-K4VERLJV.js → optional-demo-registry-KBMC7SNI.js} +2 -2
  85. package/dist-cli/chunks/{perf-PO7ARUTG.js → perf-CTTJSAIJ.js} +2 -2
  86. package/dist-cli/chunks/{permissions-XI2QUAEZ.js → permissions-BFQS4DIY.js} +2 -2
  87. package/dist-cli/chunks/{record-4CJCJAFD.js → record-MJR5ELHE.js} +3 -3
  88. package/dist-cli/chunks/report-issue-FMJRNDKH.js +29 -0
  89. package/dist-cli/chunks/runtime-PV4HH2OB.js +4 -0
  90. package/dist-cli/chunks/{screenshot-command-NDVULWMZ.js → screenshot-command-MGNASAXR.js} +3 -3
  91. package/dist-cli/chunks/{screenshots-capture-Q7UTXF3V.js → screenshots-capture-N6PANAVT.js} +2 -2
  92. package/dist-cli/chunks/serve-RS6TIO7U.js +44 -0
  93. package/dist-cli/chunks/setup-MFYGYR2F.js +32 -0
  94. package/dist-cli/chunks/{skills-NFG6VAO5.js → skills-TD35LDKK.js} +2 -2
  95. package/dist-cli/chunks/{storage-POWCOUCQ.js → storage-VCFXDJNO.js} +2 -2
  96. package/dist-cli/chunks/store-VNSTKYBQ.js +4 -0
  97. package/dist-cli/chunks/telemetry-GPNHSYFF.js +4 -0
  98. package/dist-cli/chunks/{timeline-MNHZCVAL.js → timeline-UJCZAWJV.js} +2 -2
  99. package/dist-cli/chunks/upgrade-S24NH4YG.js +7 -0
  100. package/dist-cli/chunks/upload-CCKAO2M5.js +4 -0
  101. package/dist-cli/chunks/version-IVPWT2ZQ.js +6 -0
  102. package/dist-cli/chunks/web-6BMWAWJL.js +4 -0
  103. package/dist-cli/chunks/{what-happened-H74HHGRK.js → what-happened-HRHB6ETL.js} +2 -2
  104. package/dist-lib/agent-daemon-client.cjs +2 -1
  105. package/dist-lib/agent-events.cjs +1 -1
  106. package/dist-lib/agent-identity.cjs +1 -1
  107. package/dist-lib/agent-sessions.cjs +1 -1
  108. package/dist-lib/attached-projects.cjs +1 -1
  109. package/dist-lib/auth/shared-session.cjs +1 -1
  110. package/dist-lib/backend-origin.cjs +1 -1
  111. package/dist-lib/beta.cjs +1 -1
  112. package/dist-lib/beta.mjs +1 -1
  113. package/dist-lib/bridge-constants.cjs +4 -1
  114. package/dist-lib/cli-constants.cjs +1 -1
  115. package/dist-lib/config.cjs +3 -2
  116. package/dist-lib/detox/index.cjs +1 -1
  117. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  118. package/dist-lib/home-paths.cjs +41 -14
  119. package/dist-lib/host/bridge-host.cjs +216 -84
  120. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  121. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  122. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  123. package/dist-lib/host/websocket-proxy.cjs +1 -1
  124. package/dist-lib/index.cjs +5 -2
  125. package/dist-lib/jump-to-source-babel.cjs +1 -1
  126. package/dist-lib/menu.cjs +2 -3
  127. package/dist-lib/menu.mjs +2 -3
  128. package/dist-lib/metro.cjs +1 -1
  129. package/dist-lib/profiles.cjs +1 -1
  130. package/dist-lib/render-mode.cjs +1 -1
  131. package/dist-lib/scripts/dev-server-scanner.cjs +2 -2
  132. package/dist-lib/sdk.cjs +1 -1
  133. package/dist-lib/sdk.mjs +1 -1
  134. package/dist-lib/skills.cjs +373 -159
  135. package/dist-lib/vite.cjs +3 -2
  136. package/package.json +2 -1
  137. package/scripts/postinstall.cjs +2 -78
  138. package/skills/contrast/SKILL.md +3 -2
  139. package/skills/sootsim-setup/SKILL.md +25 -0
  140. package/src/bridge-constants.ts +1 -0
  141. package/src/config.ts +4 -0
  142. package/src/disk-cleanup.ts +5 -26
  143. package/src/home-paths.ts +58 -23
  144. package/src/host/bridge-host.ts +152 -57
  145. package/src/index.ts +4 -1
  146. package/src/menu.ts +0 -1
  147. package/src/runtime-assets.ts +35 -0
  148. package/src/runtime-delivery.ts +5 -8
  149. package/src/skills/builtin/compat-check.ts +32 -65
  150. package/dist-cli/chunks/app-fonts-IMYBO6AQ.js +0 -4
  151. package/dist-cli/chunks/chunk-37ZXWXM3.js +0 -4
  152. package/dist-cli/chunks/chunk-3F2GAJ4Z.js +0 -6
  153. package/dist-cli/chunks/chunk-5S3GPTX7.js +0 -6
  154. package/dist-cli/chunks/chunk-6YN3IAWJ.js +0 -4
  155. package/dist-cli/chunks/chunk-7Q77LTPT.js +0 -15
  156. package/dist-cli/chunks/chunk-BBVBMGTT.js +0 -31
  157. package/dist-cli/chunks/chunk-CTMPC5DN.js +0 -6
  158. package/dist-cli/chunks/chunk-JFWEDISV.js +0 -120
  159. package/dist-cli/chunks/chunk-JGGXQHXH.js +0 -10
  160. package/dist-cli/chunks/chunk-OKSGM6IG.js +0 -4
  161. package/dist-cli/chunks/chunk-SPQKJDIK.js +0 -4
  162. package/dist-cli/chunks/chunk-W44PDEQH.js +0 -4
  163. package/dist-cli/chunks/chunk-WQL3BLFD.js +0 -9
  164. package/dist-cli/chunks/chunk-XYFP466E.js +0 -12
  165. package/dist-cli/chunks/chunk-ZFMJFX27.js +0 -4
  166. package/dist-cli/chunks/cleanup-74SKOD2P.js +0 -25
  167. package/dist-cli/chunks/compat-ME6HXEEU.js +0 -35
  168. package/dist-cli/chunks/config-PM67EWT3.js +0 -13
  169. package/dist-cli/chunks/control-HV7GZXAH.js +0 -4
  170. package/dist-cli/chunks/daemon-LKRHF5OK.js +0 -4
  171. package/dist-cli/chunks/disk-cleanup-DDCFGFWK.js +0 -4
  172. package/dist-cli/chunks/drivers-C2SH3DWS.js +0 -4
  173. package/dist-cli/chunks/flow-E64G5LF3.js +0 -4
  174. package/dist-cli/chunks/help-4ZSRG4F4.js +0 -4
  175. package/dist-cli/chunks/home-paths-5L7N2FLS.js +0 -4
  176. package/dist-cli/chunks/inspect-U2SV25ER.js +0 -4
  177. package/dist-cli/chunks/install-desktop-LTGVT7ID.js +0 -4
  178. package/dist-cli/chunks/runtime-SN2RWDES.js +0 -4
  179. package/dist-cli/chunks/serve-UEUIBHCK.js +0 -44
  180. package/dist-cli/chunks/setup-ZEEWYK4B.js +0 -99
  181. package/dist-cli/chunks/store-BJK7V7FL.js +0 -4
  182. package/dist-cli/chunks/telemetry-EJP5EW7K.js +0 -4
  183. package/dist-cli/chunks/upgrade-X57SIG7L.js +0 -6
  184. package/dist-cli/chunks/upload-JABM3ITZ.js +0 -4
  185. package/dist-cli/chunks/version-V4IKIDX7.js +0 -6
  186. package/dist-cli/chunks/web-F5CLF34I.js +0 -4
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.296 | (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;
@@ -61,9 +61,10 @@ __export(home_paths_exports, {
61
61
  readActiveRuntime: () => readActiveRuntime,
62
62
  readDaemonLockfile: () => readDaemonLockfile,
63
63
  readDevBridgeLockfile: () => readDevBridgeLockfile,
64
+ readLiveRuntimeVersions: () => readLiveRuntimeVersions,
64
65
  readOrCreateAnonymousId: () => readOrCreateAnonymousId,
66
+ readPrivacyPreferences: () => readPrivacyPreferences,
65
67
  readSharedConfig: () => readSharedConfig,
66
- readTelemetryEnabled: () => readTelemetryEnabled,
67
68
  removeDaemonLockfile: () => removeDaemonLockfile,
68
69
  removeDevBridgeLockfile: () => removeDevBridgeLockfile,
69
70
  runtimeDir: () => runtimeDir,
@@ -74,9 +75,9 @@ __export(home_paths_exports, {
74
75
  writeActiveRuntime: () => writeActiveRuntime,
75
76
  writeDaemonLockfile: () => writeDaemonLockfile,
76
77
  writeDevBridgeLockfile: () => writeDevBridgeLockfile,
78
+ writePrivacyPreferences: () => writePrivacyPreferences,
77
79
  writeRuntimeUpgradeNotice: () => writeRuntimeUpgradeNotice,
78
- writeSharedConfig: () => writeSharedConfig,
79
- writeTelemetryEnabled: () => writeTelemetryEnabled
80
+ writeSharedConfig: () => writeSharedConfig
80
81
  });
81
82
  module.exports = __toCommonJS(home_paths_exports);
82
83
  var import_node_crypto = require("node:crypto");
@@ -182,8 +183,23 @@ function writeSharedConfig(patch) {
182
183
  import_node_fs.default.renameSync(tmp, configFilePath());
183
184
  return next;
184
185
  }
185
- function readTelemetryEnabled() {
186
- return readSharedConfig().telemetry !== false;
186
+ function readPrivacyPreferences() {
187
+ const settings = readSharedConfig().settings;
188
+ const productAnalytics = settings?.productAnalytics;
189
+ const crashReports = settings?.crashReports;
190
+ return {
191
+ productAnalytics: typeof productAnalytics === "boolean" ? productAnalytics : true,
192
+ crashReports: typeof crashReports === "boolean" ? crashReports : true,
193
+ configured: typeof productAnalytics === "boolean" && typeof crashReports === "boolean"
194
+ };
195
+ }
196
+ function writePrivacyPreferences(preferences) {
197
+ writeSharedConfig({
198
+ settings: {
199
+ productAnalytics: preferences.productAnalytics,
200
+ crashReports: preferences.crashReports
201
+ }
202
+ });
187
203
  }
188
204
  function readOrCreateAnonymousId() {
189
205
  const existing = readSharedConfig().anonymousId;
@@ -192,9 +208,6 @@ function readOrCreateAnonymousId() {
192
208
  writeSharedConfig({ anonymousId: created });
193
209
  return created;
194
210
  }
195
- function writeTelemetryEnabled(enabled) {
196
- writeSharedConfig({ telemetry: enabled });
197
- }
198
211
  function runtimeUpgradeNoticePath() {
199
212
  return import_node_path.default.join(sootsimHomeDir(), "runtime-upgraded.json");
200
213
  }
@@ -259,7 +272,7 @@ function readDaemonLockfile() {
259
272
  const bytesRead = import_node_fs.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
260
273
  const raw = buf.subarray(0, bytesRead).toString("utf8");
261
274
  const parsed = JSON.parse(raw);
262
- 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") {
275
+ 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" && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
263
276
  return parsed;
264
277
  }
265
278
  return null;
@@ -278,7 +291,7 @@ function readDevBridgeLockfile() {
278
291
  const bytesRead = import_node_fs.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
279
292
  const raw = buf.subarray(0, bytesRead).toString("utf8");
280
293
  const parsed = JSON.parse(raw);
281
- if (parsed && parsed.schema === 1 && parsed.source === "vite-dev" && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.cwd === "string" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number" && (parsed.shellPort === void 0 || typeof parsed.shellPort === "number")) {
294
+ if (parsed && parsed.schema === 1 && parsed.source === "vite-dev" && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.cwd === "string" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number" && (parsed.shellPort === void 0 || typeof parsed.shellPort === "number") && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
282
295
  return parsed;
283
296
  }
284
297
  return null;
@@ -309,6 +322,19 @@ function isDevBridgeLockfileFresh(lock, now = Date.now()) {
309
322
  return false;
310
323
  }
311
324
  }
325
+ function readLiveRuntimeVersions() {
326
+ const versions = /* @__PURE__ */ new Set();
327
+ const daemon = readDaemonLockfile();
328
+ if (isDaemonLockfileFresh(daemon)) {
329
+ if (daemon.activeRuntime) versions.add(daemon.activeRuntime);
330
+ for (const version of daemon.servedRuntimes ?? []) versions.add(version);
331
+ }
332
+ const devBridge = readDevBridgeLockfile();
333
+ if (isDevBridgeLockfileFresh(devBridge)) {
334
+ for (const version of devBridge.servedRuntimes ?? []) versions.add(version);
335
+ }
336
+ return [...versions].sort();
337
+ }
312
338
  function writeDaemonLockfile(data) {
313
339
  ensureSootsimHome();
314
340
  const tmp = `${daemonLockfilePath()}.tmp`;
@@ -375,9 +401,10 @@ function removeDevBridgeLockfile() {
375
401
  readActiveRuntime,
376
402
  readDaemonLockfile,
377
403
  readDevBridgeLockfile,
404
+ readLiveRuntimeVersions,
378
405
  readOrCreateAnonymousId,
406
+ readPrivacyPreferences,
379
407
  readSharedConfig,
380
- readTelemetryEnabled,
381
408
  removeDaemonLockfile,
382
409
  removeDevBridgeLockfile,
383
410
  runtimeDir,
@@ -388,7 +415,7 @@ function removeDevBridgeLockfile() {
388
415
  writeActiveRuntime,
389
416
  writeDaemonLockfile,
390
417
  writeDevBridgeLockfile,
418
+ writePrivacyPreferences,
391
419
  writeRuntimeUpgradeNotice,
392
- writeSharedConfig,
393
- writeTelemetryEnabled
420
+ writeSharedConfig
394
421
  });
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.296 | (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;
@@ -99,7 +99,7 @@ function hasOwnKeys(value) {
99
99
  }
100
100
  function hasSootSimConfig(config) {
101
101
  if (!config) return false;
102
- return hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
102
+ return typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
103
103
  }
104
104
  function applySootSimConfigToUrl(url, config) {
105
105
  const parsed = new URL(url);
@@ -920,6 +920,7 @@ async function scanDevServers(opts = {}) {
920
920
 
921
921
  // src/bridge-constants.ts
922
922
  var DEFAULT_SOOTSIM_BRIDGE_PORT = 7668;
923
+ var DEFAULT_SOOTSIM_BRIDGE_URL = `http://localhost:${DEFAULT_SOOTSIM_BRIDGE_PORT}/`;
923
924
  var SOOTSIM_BRIDGE_SIM_CLOSE_CODE = 4001;
924
925
  var SOOTSIM_BRIDGE_SIM_CLOSE_REASON = "sootsim close";
925
926
 
@@ -1081,7 +1082,7 @@ function readDaemonLockfile() {
1081
1082
  const bytesRead = import_node_fs2.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
1082
1083
  const raw = buf.subarray(0, bytesRead).toString("utf8");
1083
1084
  const parsed = JSON.parse(raw);
1084
- 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") {
1085
+ 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" && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
1085
1086
  return parsed;
1086
1087
  }
1087
1088
  return null;
@@ -1142,35 +1143,12 @@ function removeDevBridgeLockfile() {
1142
1143
  var import_fs = __toESM(require("fs"), 1);
1143
1144
  var import_path = __toESM(require("path"), 1);
1144
1145
  var import_url = require("url");
1145
- var _monorepoRootCache;
1146
- function findMonorepoRoot() {
1147
- if (_monorepoRootCache !== void 0) return _monorepoRootCache;
1148
- let dir = import_path.default.dirname((0, import_url.fileURLToPath)(__sootsim_import_meta_url));
1149
- for (let i = 0; i < 12 && dir !== import_path.default.dirname(dir); i++) {
1150
- if (import_fs.default.existsSync(import_path.default.join(dir, "packages/sootsim-engine/package.json")) && import_fs.default.existsSync(import_path.default.join(dir, "public/sootsim"))) {
1151
- _monorepoRootCache = dir;
1152
- return dir;
1153
- }
1154
- dir = import_path.default.dirname(dir);
1155
- }
1156
- _monorepoRootCache = null;
1157
- return null;
1158
- }
1159
- function resolveDevCheckoutRuntimeRoot() {
1160
- if (process.env.SOOTSIM_HOME && readActiveRuntime()) return null;
1161
- const root = findMonorepoRoot();
1162
- if (!root) return null;
1163
- const devBuild = import_path.default.join(root, "public/sootsim");
1164
- if (import_fs.default.existsSync(import_path.default.join(devBuild, "index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine/index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.js"))) {
1165
- return devBuild;
1166
- }
1167
- return null;
1168
- }
1169
1146
 
1170
1147
  // ../contrast-runtime-delivery/src/index.ts
1171
1148
  var import_node_child_process = require("node:child_process");
1172
1149
  var import_node_crypto = __toESM(require("node:crypto"), 1);
1173
1150
  var import_node_fs4 = __toESM(require("node:fs"), 1);
1151
+ var import_node_os2 = __toESM(require("node:os"), 1);
1174
1152
  var import_node_path4 = __toESM(require("node:path"), 1);
1175
1153
  var import_node_stream = require("node:stream");
1176
1154
  var import_promises2 = require("node:stream/promises");
@@ -1178,6 +1156,9 @@ var import_promises2 = require("node:stream/promises");
1178
1156
  // ../contrast-runtime-delivery/src/product.ts
1179
1157
  var import_node_fs3 = __toESM(require("node:fs"), 1);
1180
1158
  var import_node_path3 = __toESM(require("node:path"), 1);
1159
+ function isValidRuntimeVersion(value) {
1160
+ return value.length <= 100 && /^[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(value);
1161
+ }
1181
1162
  function listInstalledRuntimes(product) {
1182
1163
  try {
1183
1164
  return import_node_fs3.default.readdirSync(product.runtimesDir(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort(compareSemver);
@@ -1233,6 +1214,7 @@ function createRuntimeDelivery(product) {
1233
1214
  return input || process.env[product.env.channel] || config.runtimeChannel || "stable";
1234
1215
  }
1235
1216
  function resolvePin() {
1217
+ if (!product.env.pin) return null;
1236
1218
  const env = process.env[product.env.pin];
1237
1219
  if (env && env.trim()) return env.trim();
1238
1220
  const config = readConfig();
@@ -1262,6 +1244,9 @@ function createRuntimeDelivery(product) {
1262
1244
  `no version specified and channel '${channel}' has no latest entry in the manifest`
1263
1245
  );
1264
1246
  }
1247
+ if (!isValidRuntimeVersion(version)) {
1248
+ throw new Error(`invalid runtime version: ${version}`);
1249
+ }
1265
1250
  const entry = manifest.versions[version];
1266
1251
  if (!entry) {
1267
1252
  throw new Error(
@@ -1277,10 +1262,11 @@ function createRuntimeDelivery(product) {
1277
1262
  const { version, channel, entry } = resolveVersion(manifest, opts);
1278
1263
  const destDir = product.runtimeDir(version);
1279
1264
  const setActive = opts.setActive !== false;
1280
- pruneLocalRetention({ protectVersions: [version] });
1265
+ const protectVersions = [version, ...opts.protectVersions ?? []];
1266
+ pruneLocalRetention({ protectVersions });
1281
1267
  if (!opts.force && import_node_fs4.default.existsSync(import_node_path4.default.join(destDir, product.installedMarker))) {
1282
1268
  if (setActive) product.writeActiveRuntime(version);
1283
- pruneLocalRetention({ protectVersions: [version] });
1269
+ pruneLocalRetention({ protectVersions });
1284
1270
  return {
1285
1271
  version,
1286
1272
  channel,
@@ -1292,29 +1278,32 @@ function createRuntimeDelivery(product) {
1292
1278
  };
1293
1279
  }
1294
1280
  const tarUrl = entry.tarball || tarballUrl(version, cdnOrigin);
1295
- const tarCachePath = import_node_path4.default.join(
1296
- product.cacheDir(),
1281
+ const downloadDir = import_node_fs4.default.mkdtempSync(
1282
+ import_node_path4.default.join(import_node_os2.default.tmpdir(), `${product.id}-runtime-download-`)
1283
+ );
1284
+ const runtimeArchivePath = import_node_path4.default.join(
1285
+ downloadDir,
1297
1286
  runtimeTarballCacheName(product, version)
1298
1287
  );
1299
- process.stderr.write(`${product.id}: downloading runtime ${version}\u2026
1300
- `);
1301
- await downloadToFile(tarUrl, tarCachePath);
1302
- process.stderr.write(`${product.id}: extracting runtime ${version}\u2026
1303
- `);
1304
1288
  const tmpDir = import_node_path4.default.join(
1305
1289
  import_node_path4.default.dirname(destDir),
1306
1290
  `.installing-${version}-${process.pid}`
1307
1291
  );
1308
1292
  import_node_fs4.default.rmSync(tmpDir, { recursive: true, force: true });
1309
1293
  try {
1310
- const actualSha = await sha256File(tarCachePath);
1294
+ process.stderr.write(`${product.id}: downloading runtime ${version}\u2026
1295
+ `);
1296
+ await downloadToFile(tarUrl, runtimeArchivePath);
1297
+ process.stderr.write(`${product.id}: extracting runtime ${version}\u2026
1298
+ `);
1299
+ const actualSha = await sha256File(runtimeArchivePath);
1311
1300
  if (actualSha !== entry.sha256) {
1312
1301
  throw new Error(
1313
1302
  `sha256 mismatch for runtime ${version}: expected ${entry.sha256}, actual ${actualSha}`
1314
1303
  );
1315
1304
  }
1316
1305
  import_node_fs4.default.mkdirSync(tmpDir, { recursive: true });
1317
- await extractTarball(tarCachePath, tmpDir);
1306
+ await extractTarball(runtimeArchivePath, tmpDir);
1318
1307
  if (!import_node_fs4.default.existsSync(import_node_path4.default.join(tmpDir, product.installedMarker))) {
1319
1308
  throw new Error(
1320
1309
  `extracted tarball for runtime ${version} is missing ${product.installedMarker}`
@@ -1326,10 +1315,10 @@ function createRuntimeDelivery(product) {
1326
1315
  import_node_fs4.default.rmSync(tmpDir, { recursive: true, force: true });
1327
1316
  throw err;
1328
1317
  } finally {
1329
- import_node_fs4.default.rmSync(tarCachePath, { force: true });
1318
+ import_node_fs4.default.rmSync(downloadDir, { recursive: true, force: true });
1330
1319
  }
1331
1320
  if (setActive) product.writeActiveRuntime(version);
1332
- pruneLocalRetention({ protectVersions: [version] });
1321
+ pruneLocalRetention({ protectVersions });
1333
1322
  return {
1334
1323
  version,
1335
1324
  channel,
@@ -1342,7 +1331,7 @@ function createRuntimeDelivery(product) {
1342
1331
  }
1343
1332
  async function updateToLatest(opts = {}) {
1344
1333
  product.ensureHome();
1345
- pruneLocalRetention();
1334
+ pruneLocalRetention({ protectVersions: opts.protectVersions });
1346
1335
  const cdnOrigin = resolveCdnOrigin(opts.cdnOrigin);
1347
1336
  const channel = resolveChannel(opts.channel);
1348
1337
  const pin = resolvePin();
@@ -1364,7 +1353,8 @@ function createRuntimeDelivery(product) {
1364
1353
  version: pin,
1365
1354
  channel,
1366
1355
  cdnOrigin,
1367
- setActive: false
1356
+ setActive: false,
1357
+ protectVersions: opts.protectVersions
1368
1358
  });
1369
1359
  return {
1370
1360
  checked: true,
@@ -1412,7 +1402,8 @@ function createRuntimeDelivery(product) {
1412
1402
  version: latestVersion,
1413
1403
  channel,
1414
1404
  cdnOrigin,
1415
- setActive: false
1405
+ setActive: false,
1406
+ protectVersions: opts.protectVersions
1416
1407
  });
1417
1408
  return {
1418
1409
  checked: true,
@@ -1555,6 +1546,53 @@ function extractTarball(tarPath, destDir) {
1555
1546
  });
1556
1547
  }
1557
1548
 
1549
+ // src/runtime-assets.ts
1550
+ var _monorepoRootCache;
1551
+ function findMonorepoRoot() {
1552
+ if (_monorepoRootCache !== void 0) return _monorepoRootCache;
1553
+ let dir = import_path.default.dirname((0, import_url.fileURLToPath)(__sootsim_import_meta_url));
1554
+ for (let i = 0; i < 12 && dir !== import_path.default.dirname(dir); i++) {
1555
+ if (import_fs.default.existsSync(import_path.default.join(dir, "packages/sootsim-engine/package.json")) && import_fs.default.existsSync(import_path.default.join(dir, "public/sootsim"))) {
1556
+ _monorepoRootCache = dir;
1557
+ return dir;
1558
+ }
1559
+ dir = import_path.default.dirname(dir);
1560
+ }
1561
+ _monorepoRootCache = null;
1562
+ return null;
1563
+ }
1564
+ function resolveDevCheckoutRuntimeRoot() {
1565
+ if (process.env.SOOTSIM_HOME && readActiveRuntime()) return null;
1566
+ const root = findMonorepoRoot();
1567
+ if (!root) return null;
1568
+ const devBuild = import_path.default.join(root, "public/sootsim");
1569
+ if (import_fs.default.existsSync(import_path.default.join(devBuild, "index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine/index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.js"))) {
1570
+ return devBuild;
1571
+ }
1572
+ return null;
1573
+ }
1574
+ function runtimeVersionFromHostname(hostname) {
1575
+ const suffix = ".runtime.localhost";
1576
+ if (!hostname.toLowerCase().endsWith(suffix)) return null;
1577
+ const labels = hostname.slice(0, -suffix.length).split(".");
1578
+ if (labels.length === 0 || labels.some((label) => !/^[0-9a-f]{1,60}$/i.test(label))) {
1579
+ return null;
1580
+ }
1581
+ const encoded = labels.join("");
1582
+ if (encoded.length % 2 !== 0) return null;
1583
+ try {
1584
+ const version = Buffer.from(encoded, "hex").toString("utf8");
1585
+ return isValidRuntimeVersion(version) ? version : null;
1586
+ } catch {
1587
+ return null;
1588
+ }
1589
+ }
1590
+ function resolveInstalledRuntimeRoot(version) {
1591
+ const dir = runtimeDir(version);
1592
+ if (!import_fs.default.existsSync(import_path.default.join(dir, "index.html"))) return null;
1593
+ return dir;
1594
+ }
1595
+
1558
1596
  // src/runtime-delivery.ts
1559
1597
  var DEFAULT_RUNTIME_CDN_ORIGIN = "https://contrast.dev";
1560
1598
  var SOOTSIM_RUNTIME_PRODUCT = {
@@ -1568,8 +1606,7 @@ var SOOTSIM_RUNTIME_PRODUCT = {
1568
1606
  installedMarker: "index.html",
1569
1607
  env: {
1570
1608
  cdnOrigin: "SOOTSIM_CDN_ORIGIN",
1571
- channel: "SOOTSIM_RUNTIME_CHANNEL",
1572
- pin: "SOOTSIM_RUNTIME_VERSION"
1609
+ channel: "SOOTSIM_RUNTIME_CHANNEL"
1573
1610
  },
1574
1611
  ensureHome: ensureSootsimHome,
1575
1612
  configFile: configFilePath,
@@ -3862,6 +3899,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3862
3899
  recentActions: []
3863
3900
  };
3864
3901
  this.sims.set(sim.id, sim);
3902
+ this.writeConnectedRuntimeSnapshot();
3865
3903
  if (this.shouldPromoteSim(sim)) {
3866
3904
  this.primarySimId = sim.id;
3867
3905
  }
@@ -3953,6 +3991,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3953
3991
  sim = null;
3954
3992
  role = "cli";
3955
3993
  this.cliIdentityKeyBySocket.set(ws, `ws-${this.nextCliFallbackId++}`);
3994
+ this.writeConnectedRuntimeSnapshot();
3956
3995
  this.broadcastSimAssignments();
3957
3996
  this.broadcastSimClientStates();
3958
3997
  }
@@ -3966,6 +4005,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3966
4005
  sim.url = registration.url;
3967
4006
  sim.title = registration.title;
3968
4007
  sim.userAgent = registration.userAgent;
4008
+ this.writeConnectedRuntimeSnapshot();
3969
4009
  if (typeof registration.kind === "string" && registration.kind.trim()) {
3970
4010
  sim.kind = registration.kind.trim();
3971
4011
  }
@@ -3990,11 +4030,14 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3990
4030
  return;
3991
4031
  }
3992
4032
  if (msg.type === "bridge:write-shared-config") {
3993
- const patch = msg.patch && typeof msg.patch === "object" ? msg.patch : null;
3994
- if (!patch) return;
3995
- let next;
4033
+ if (!msg.patch || typeof msg.patch !== "object" || Array.isArray(msg.patch)) {
4034
+ return;
4035
+ }
4036
+ const patch = Object.fromEntries(
4037
+ Object.entries(msg.patch)
4038
+ );
3996
4039
  try {
3997
- next = writeSharedConfig(patch);
4040
+ this.writeAndBroadcastSharedConfig(patch);
3998
4041
  } catch (err) {
3999
4042
  process.stderr.write(
4000
4043
  `sootsim: bridge:write-shared-config failed: ${err instanceof Error ? err.message : String(err)}
@@ -4002,17 +4045,6 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4002
4045
  );
4003
4046
  return;
4004
4047
  }
4005
- const payload = JSON.stringify({
4006
- type: "bridge:shared-config-changed",
4007
- config: next
4008
- });
4009
- for (const peer of this.sims.values()) {
4010
- if (peer.ws.readyState !== import_ws2.WebSocket.OPEN) continue;
4011
- try {
4012
- peer.ws.send(payload);
4013
- } catch {
4014
- }
4015
- }
4016
4048
  return;
4017
4049
  }
4018
4050
  if (msg.type === "bridge:open-path") {
@@ -4327,6 +4359,18 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4327
4359
  void this.agentHost.seedOnBoot();
4328
4360
  }
4329
4361
  bootstrapping = true;
4362
+ connectedRuntimeVersions() {
4363
+ const versions = /* @__PURE__ */ new Set();
4364
+ for (const sim of this.sims.values()) {
4365
+ try {
4366
+ const url = new URL(sim.url || sim.origin || "http://localhost");
4367
+ const version = runtimeVersionFromHostname(url.hostname);
4368
+ if (version) versions.add(version);
4369
+ } catch {
4370
+ }
4371
+ }
4372
+ return [...versions].sort();
4373
+ }
4330
4374
  buildLockfileSnapshot() {
4331
4375
  return {
4332
4376
  schema: 1,
@@ -4336,6 +4380,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4336
4380
  runtimePort: this.effectivePort,
4337
4381
  activeRuntime: this.activeRuntimeVersion,
4338
4382
  activeRuntimeDir: this.activeRuntimeDirPath,
4383
+ servedRuntimes: this.connectedRuntimeVersions(),
4339
4384
  startedAt: this.startedAt,
4340
4385
  heartbeatAt: Date.now(),
4341
4386
  bootstrapping: this.bootstrapping
@@ -4353,7 +4398,8 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4353
4398
  cwd: process.cwd(),
4354
4399
  startedAt: this.startedAt,
4355
4400
  heartbeatAt: Date.now(),
4356
- source: "vite-dev"
4401
+ source: "vite-dev",
4402
+ servedRuntimes: this.connectedRuntimeVersions()
4357
4403
  };
4358
4404
  }
4359
4405
  writeLockfileSnapshot() {
@@ -4362,6 +4408,20 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4362
4408
  writeDevLockfileSnapshot() {
4363
4409
  writeDevBridgeLockfile(this.buildDevLockfileSnapshot());
4364
4410
  }
4411
+ writeConnectedRuntimeSnapshot() {
4412
+ if (this.shouldWriteLockfile && this.httpServer) {
4413
+ try {
4414
+ this.writeLockfileSnapshot();
4415
+ } catch {
4416
+ }
4417
+ }
4418
+ if (this.shouldWriteDevLockfile && this.httpServer) {
4419
+ try {
4420
+ this.writeDevLockfileSnapshot();
4421
+ } catch {
4422
+ }
4423
+ }
4424
+ }
4365
4425
  refreshActiveRuntime() {
4366
4426
  this.activeRuntimeVersion = readActiveRuntime();
4367
4427
  this.activeRuntimeDirPath = resolveDevCheckoutRuntimeRoot() ?? activeRuntimeDir();
@@ -4413,6 +4473,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4413
4473
  }
4414
4474
  startRuntimeUpdater() {
4415
4475
  if (!this.shouldWriteLockfile || this.runtimeUpdateTimer) return;
4476
+ if (import_fs3.default.existsSync(import_path3.default.join(sootsimHomeDir(), "runtime-update-disabled"))) return;
4416
4477
  if (process.env.SOOTSIM_HOME && isSootsimDevCheckout() && !readActiveRuntime()) {
4417
4478
  return;
4418
4479
  }
@@ -4430,7 +4491,9 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4430
4491
  if (reason === "startup") {
4431
4492
  process.stderr.write("sootsim: checking for runtime updates\u2026\n");
4432
4493
  }
4433
- const result = await sootsimRuntime.updateToLatest();
4494
+ const result = await sootsimRuntime.updateToLatest({
4495
+ protectVersions: this.connectedRuntimeVersions()
4496
+ });
4434
4497
  if (!result.updated || !result.latestVersion) {
4435
4498
  if (reason === "startup") {
4436
4499
  process.stderr.write(
@@ -4491,6 +4554,11 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4491
4554
  runtimeDir: this.activeRuntimeDirPath
4492
4555
  });
4493
4556
  for (const sim of this.sims.values()) {
4557
+ try {
4558
+ const simUrl = new URL(sim.url || sim.origin || "http://localhost");
4559
+ if (runtimeVersionFromHostname(simUrl.hostname)) continue;
4560
+ } catch {
4561
+ }
4494
4562
  if (sim.ws.readyState === import_ws2.WebSocket.OPEN) {
4495
4563
  try {
4496
4564
  sim.ws.send(payload);
@@ -4534,12 +4602,77 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4534
4602
  return;
4535
4603
  }
4536
4604
  const method = (req.method || "GET").toUpperCase();
4605
+ let url;
4606
+ try {
4607
+ url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
4608
+ } catch {
4609
+ res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
4610
+ res.end("invalid request URL or host");
4611
+ return;
4612
+ }
4613
+ if (url.pathname === "/__sootsim/shared-config") {
4614
+ res.setHeader("Access-Control-Allow-Origin", "*");
4615
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
4616
+ res.setHeader("Cache-Control", "no-store");
4617
+ if (method === "OPTIONS") {
4618
+ res.writeHead(204, { Allow: "GET, HEAD, POST, OPTIONS" });
4619
+ res.end();
4620
+ return;
4621
+ }
4622
+ if (method === "GET" || method === "HEAD") {
4623
+ let body = "{}";
4624
+ try {
4625
+ body = JSON.stringify(readSharedConfig());
4626
+ } catch {
4627
+ }
4628
+ res.writeHead(200, { "Content-Type": "application/json" });
4629
+ if (method === "HEAD") res.end();
4630
+ else res.end(body);
4631
+ return;
4632
+ }
4633
+ if (method === "POST") {
4634
+ void (async () => {
4635
+ try {
4636
+ const chunks = [];
4637
+ let size = 0;
4638
+ for await (const chunk of req) {
4639
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
4640
+ size += buffer.byteLength;
4641
+ if (size > 8 * 1024 * 1024) {
4642
+ res.writeHead(413, { "Content-Type": "text/plain; charset=utf-8" });
4643
+ res.end("shared config patch is too large");
4644
+ return;
4645
+ }
4646
+ chunks.push(buffer);
4647
+ }
4648
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
4649
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4650
+ throw new Error("shared config patch must be an object");
4651
+ }
4652
+ const patch = Object.fromEntries(
4653
+ Object.entries(parsed)
4654
+ );
4655
+ const next = this.writeAndBroadcastSharedConfig(patch);
4656
+ res.writeHead(200, { "Content-Type": "application/json" });
4657
+ res.end(JSON.stringify(next));
4658
+ } catch (error) {
4659
+ res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
4660
+ res.end(
4661
+ `invalid shared config patch: ${error instanceof Error ? error.message : String(error)}`
4662
+ );
4663
+ }
4664
+ })();
4665
+ return;
4666
+ }
4667
+ res.writeHead(405, { Allow: "GET, HEAD, POST, OPTIONS" });
4668
+ res.end("method not allowed");
4669
+ return;
4670
+ }
4537
4671
  if (method !== "GET" && method !== "HEAD") {
4538
4672
  res.writeHead(405, { Allow: "GET, HEAD" });
4539
4673
  res.end("method not allowed");
4540
4674
  return;
4541
4675
  }
4542
- const url = new URL(req.url || "/", "http://localhost");
4543
4676
  if (url.pathname === "/__bundle-proxy") {
4544
4677
  const target = url.searchParams.get("url");
4545
4678
  if (!target) {
@@ -4616,30 +4749,13 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4616
4749
  );
4617
4750
  return;
4618
4751
  }
4619
- if (url.pathname === "/__sootsim/shared-config") {
4620
- res.setHeader("Access-Control-Allow-Origin", "*");
4621
- res.setHeader("Cache-Control", "no-store");
4622
- if (method === "GET" || method === "HEAD") {
4623
- let body = "{}";
4624
- try {
4625
- body = JSON.stringify(readSharedConfig());
4626
- } catch {
4627
- }
4628
- res.writeHead(200, { "Content-Type": "application/json" });
4629
- if (method === "HEAD") res.end();
4630
- else res.end(body);
4631
- return;
4632
- }
4633
- res.writeHead(405, { Allow: "GET, HEAD" });
4634
- res.end("method not allowed (use the bridge over WS for writes)");
4635
- return;
4636
- }
4637
- this.refreshActiveRuntime();
4638
- const baseDir = this.activeRuntimeDirPath;
4752
+ const requestedRuntime = runtimeVersionFromHostname(url.hostname);
4753
+ if (!requestedRuntime) this.refreshActiveRuntime();
4754
+ const baseDir = requestedRuntime ? resolveInstalledRuntimeRoot(requestedRuntime) : this.activeRuntimeDirPath;
4639
4755
  if (!baseDir) {
4640
4756
  res.writeHead(503, { "Content-Type": "text/plain; charset=utf-8" });
4641
4757
  res.end(
4642
- "sootsim: no active runtime installed. run `sootsim runtime install` to fetch one."
4758
+ requestedRuntime ? `sootsim: runtime ${requestedRuntime} is not installed. run \`sootsim open\` from that app again.` : "sootsim: no active runtime installed. run `sootsim runtime install` to fetch one."
4643
4759
  );
4644
4760
  return;
4645
4761
  }
@@ -5246,6 +5362,21 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
5246
5362
  );
5247
5363
  }
5248
5364
  }
5365
+ writeAndBroadcastSharedConfig(patch) {
5366
+ const next = writeSharedConfig(patch);
5367
+ const payload = JSON.stringify({
5368
+ type: "bridge:shared-config-changed",
5369
+ config: next
5370
+ });
5371
+ for (const peer of this.sims.values()) {
5372
+ if (peer.ws.readyState !== import_ws2.WebSocket.OPEN) continue;
5373
+ try {
5374
+ peer.ws.send(payload);
5375
+ } catch {
5376
+ }
5377
+ }
5378
+ return next;
5379
+ }
5249
5380
  broadcastSimClientStates() {
5250
5381
  for (const sim of this.sims.values()) {
5251
5382
  if (sim.ws.readyState !== import_ws2.WebSocket.OPEN) continue;
@@ -5384,6 +5515,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
5384
5515
  expiresAt: Date.now() + _SootSimBridgeHost.SIM_RECONNECT_TTL_MS
5385
5516
  });
5386
5517
  this.sims.delete(sim.id);
5518
+ this.writeConnectedRuntimeSnapshot();
5387
5519
  }
5388
5520
  getRestorableSimState(simId) {
5389
5521
  const snapshot = this.restorableSims.get(simId);
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.296 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.296 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;