sootsim 0.1.251 → 0.1.253

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 (150) hide show
  1. package/README.md +8 -5
  2. package/dist-cli/bin.js +4 -4
  3. package/dist-cli/chunks/{agent-OS5AFMNY.js → agent-VULBDRQX.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-JXCJFH4J.js → agent-wrapper-NIKM5YH7.js} +2 -2
  5. package/dist-cli/chunks/{app-fonts-KM5VGTPQ.js → app-fonts-H6Q7KUPX.js} +2 -2
  6. package/dist-cli/chunks/{assert-IWDBWZA3.js → assert-KG4DXXXU.js} +2 -2
  7. package/dist-cli/chunks/{auth-GGWN2KHL.js → auth-YFAM5XCG.js} +2 -2
  8. package/dist-cli/chunks/beta-X7YVGAZ7.js +2 -0
  9. package/dist-cli/chunks/{camera-W5JQ5P6L.js → camera-LDDRWGWU.js} +2 -2
  10. package/dist-cli/chunks/chunk-36BZF4FN.js +29 -0
  11. package/dist-cli/chunks/{chunk-CQKALFGJ.js → chunk-3UCSOXI3.js} +1 -1
  12. package/dist-cli/chunks/{chunk-XJYAJ5C3.js → chunk-45WNNP6S.js} +2 -2
  13. package/dist-cli/chunks/{chunk-UTNAJIRM.js → chunk-46TTKKZH.js} +2 -2
  14. package/dist-cli/chunks/chunk-57Y54BSR.js +1 -0
  15. package/dist-cli/chunks/{chunk-LJKL5JQ7.js → chunk-5FUEGJVZ.js} +2 -2
  16. package/dist-cli/chunks/{chunk-CDZTB6JX.js → chunk-5KIOJ7EY.js} +2 -2
  17. package/dist-cli/chunks/{chunk-IC4GREHT.js → chunk-5LRIUIDH.js} +1 -1
  18. package/dist-cli/chunks/{chunk-KEBLYJBJ.js → chunk-7X342NBJ.js} +2 -2
  19. package/dist-cli/chunks/chunk-AVDM5PJT.js +7 -0
  20. package/dist-cli/chunks/{chunk-7HCKUJ5R.js → chunk-BMDLK4TN.js} +1 -1
  21. package/dist-cli/chunks/{chunk-VO5UXLRE.js → chunk-C5WLLPSP.js} +2 -2
  22. package/dist-cli/chunks/chunk-DCSZ4W7L.js +4 -0
  23. package/dist-cli/chunks/{chunk-55GPRX3M.js → chunk-DEPT2HN2.js} +2 -2
  24. package/dist-cli/chunks/{chunk-KD347J3Y.js → chunk-DVL3QEUF.js} +2 -2
  25. package/dist-cli/chunks/{chunk-LETXRLKY.js → chunk-GQEV66R5.js} +1 -1
  26. package/dist-cli/chunks/{chunk-Z7IG2OVW.js → chunk-HWJY7OMU.js} +1 -1
  27. package/dist-cli/chunks/chunk-HWUTV647.js +1 -0
  28. package/dist-cli/chunks/{chunk-MK5FIMS5.js → chunk-I2SPWGIX.js} +2 -2
  29. package/dist-cli/chunks/{chunk-7MEV3VTL.js → chunk-IRKSPKGN.js} +1 -1
  30. package/dist-cli/chunks/{chunk-PM3MMXYH.js → chunk-L2MLV2CS.js} +2 -2
  31. package/dist-cli/chunks/{chunk-FBAFTRKJ.js → chunk-L6CZJTQL.js} +2 -2
  32. package/dist-cli/chunks/{chunk-KEJB5OS5.js → chunk-LQGKEJEY.js} +2 -2
  33. package/dist-cli/chunks/{chunk-WD4B3KV6.js → chunk-MJVYTFNP.js} +3 -3
  34. package/dist-cli/chunks/{chunk-YJEU2B3D.js → chunk-MWMCDY2F.js} +1 -1
  35. package/dist-cli/chunks/{chunk-RBHEKEEX.js → chunk-NGPLOHLX.js} +2 -2
  36. package/dist-cli/chunks/{chunk-63FDEQKS.js → chunk-NKBPE462.js} +1 -1
  37. package/dist-cli/chunks/{chunk-OOLWKYWS.js → chunk-NTITLMC6.js} +1 -1
  38. package/dist-cli/chunks/{chunk-TQJZ2AP4.js → chunk-NUEXSE26.js} +2 -2
  39. package/dist-cli/chunks/{chunk-DVLNBQO6.js → chunk-NUPOCIXQ.js} +2 -2
  40. package/dist-cli/chunks/{chunk-GMJ3ROI3.js → chunk-PDGZJCKZ.js} +2 -2
  41. package/dist-cli/chunks/{chunk-ULYIJUKO.js → chunk-PPEVHLXX.js} +2 -2
  42. package/dist-cli/chunks/{chunk-EY5V6OC5.js → chunk-Q5HUMK3X.js} +2 -2
  43. package/dist-cli/chunks/{chunk-NQCAEFUQ.js → chunk-QLD2JSAY.js} +1 -1
  44. package/dist-cli/chunks/{chunk-H3YZXLYR.js → chunk-RKLOJJIQ.js} +1 -1
  45. package/dist-cli/chunks/{chunk-IYPXOVE4.js → chunk-ROP4FQW2.js} +1 -1
  46. package/dist-cli/chunks/{chunk-ZL3QAP5O.js → chunk-SM36BLYO.js} +3 -3
  47. package/dist-cli/chunks/{chunk-37PWKV2H.js → chunk-UTHHCEOE.js} +1 -1
  48. package/dist-cli/chunks/{chunk-X4HNMVAY.js → chunk-VUUVN4CA.js} +2 -2
  49. package/dist-cli/chunks/{chunk-PZZPL7XB.js → chunk-WLTTYQFC.js} +1 -1
  50. package/dist-cli/chunks/{chunk-5T62AM5W.js → chunk-WXXCER5Y.js} +3 -3
  51. package/dist-cli/chunks/{chunk-7JTW5KO4.js → chunk-XRDM6KN6.js} +1 -1
  52. package/dist-cli/chunks/{chunk-HLD3JMUC.js → chunk-Y66ZEDLD.js} +2 -2
  53. package/dist-cli/chunks/{chunk-Z6ZYU2VN.js → chunk-YQDI7PAU.js} +1 -1
  54. package/dist-cli/chunks/cli-version-YK3KEAWE.js +2 -0
  55. package/dist-cli/chunks/{compat-Q7NF5FGR.js → compat-5MLHRLL7.js} +3 -3
  56. package/dist-cli/chunks/{config-CHFGC57H.js → config-XQU7NIBN.js} +2 -2
  57. package/dist-cli/chunks/{control-GMXYQHOC.js → control-CP27GWY2.js} +2 -2
  58. package/dist-cli/chunks/daemon-SZ5235QJ.js +2 -0
  59. package/dist-cli/chunks/{debug-JVFKA6ZJ.js → debug-XAS5RRTR.js} +3 -3
  60. package/dist-cli/chunks/{desktop-DT6WBVHN.js → desktop-DMRDH54D.js} +3 -3
  61. package/dist-cli/chunks/{detox-OQ3DVIZB.js → detox-5RWKQ4FY.js} +2 -2
  62. package/dist-cli/chunks/{device-XBBNWO67.js → device-K4QUDQJW.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-JFWKSZI6.js → diagnose-XQ3UCGAC.js} +2 -2
  64. package/dist-cli/chunks/drivers-4XIRJTRV.js +2 -0
  65. package/dist-cli/chunks/{film-3BI63O2P.js → film-NBO7R7YE.js} +3 -3
  66. package/dist-cli/chunks/flow-IPFJBZ2T.js +2 -0
  67. package/dist-cli/chunks/help-OVFNRXKE.js +2 -0
  68. package/dist-cli/chunks/{hidden-runtime-alias-JGY2SN3U.js → hidden-runtime-alias-HMRDTRPL.js} +2 -2
  69. package/dist-cli/chunks/home-paths-IHLLHTH3.js +2 -0
  70. package/dist-cli/chunks/inspect-OLDRJZFR.js +2 -0
  71. package/dist-cli/chunks/install-desktop-4LN5U3GV.js +2 -0
  72. package/dist-cli/chunks/{login-QT6OUDLD.js → login-JJ26E3ZL.js} +4 -4
  73. package/dist-cli/chunks/{logout-QM7I6EYI.js → logout-Z4T2ZUMV.js} +2 -2
  74. package/dist-cli/chunks/{maestro-YU5YDZ44.js → maestro-27CX2MYE.js} +3 -3
  75. package/dist-cli/chunks/{maestro-generate-YQ5GE6LW.js → maestro-generate-S2HMOTKV.js} +3 -3
  76. package/dist-cli/chunks/{mode-MHOIKOWG.js → mode-MUI6VEX5.js} +2 -2
  77. package/dist-cli/chunks/optional-demo-registry-B5KGRDC7.js +2 -0
  78. package/dist-cli/chunks/{perf-UDWM5CLN.js → perf-XLL4NAW4.js} +2 -2
  79. package/dist-cli/chunks/{record-EEYIMPNW.js → record-CO6ZZXOF.js} +3 -3
  80. package/dist-cli/chunks/runtime-3BT5LEI2.js +2 -0
  81. package/dist-cli/chunks/{screenshot-command-MDOO4W2M.js → screenshot-command-7TLIPP6M.js} +3 -3
  82. package/dist-cli/chunks/{screenshots-capture-MYFBKRYI.js → screenshots-capture-W47CLDWK.js} +2 -2
  83. package/dist-cli/chunks/{serve-TFGKULND.js → serve-JFWQW3PY.js} +15 -15
  84. package/dist-cli/chunks/{setup-WOB5DERX.js → setup-64WP25N3.js} +3 -3
  85. package/dist-cli/chunks/{skills-MFFPTR5A.js → skills-FEOY5MFR.js} +2 -2
  86. package/dist-cli/chunks/{storage-6IZXGU2O.js → storage-DVJEOSE5.js} +2 -2
  87. package/dist-cli/chunks/store-VWZLLKMA.js +2 -0
  88. package/dist-cli/chunks/telemetry-RRR64APJ.js +2 -0
  89. package/dist-cli/chunks/{timeline-BRXK7M5J.js → timeline-BN4FOIE5.js} +2 -2
  90. package/dist-cli/chunks/{upgrade-K6A4PITT.js → upgrade-R6FMWF53.js} +4 -4
  91. package/dist-cli/chunks/upload-VV6ASI6M.js +2 -0
  92. package/dist-cli/chunks/version-5GYDQEIP.js +4 -0
  93. package/dist-cli/chunks/{web-P3ANEMOL.js → web-SZQMAIF5.js} +2 -2
  94. package/dist-cli/chunks/{what-happened-OFMSOZZB.js → what-happened-2KMAVPKZ.js} +2 -2
  95. package/dist-lib/agent-daemon-client.cjs +1 -1
  96. package/dist-lib/agent-events.cjs +1 -1
  97. package/dist-lib/agent-identity.cjs +1 -1
  98. package/dist-lib/agent-sessions.cjs +1 -1
  99. package/dist-lib/attached-projects.cjs +1 -1
  100. package/dist-lib/auth/shared-session.cjs +1 -1
  101. package/dist-lib/backend-origin.cjs +1 -1
  102. package/dist-lib/beta.cjs +1 -1
  103. package/dist-lib/beta.mjs +1 -1
  104. package/dist-lib/bridge-constants.cjs +1 -1
  105. package/dist-lib/cli-constants.cjs +1 -1
  106. package/dist-lib/config.cjs +1 -1
  107. package/dist-lib/detox/index.cjs +1 -1
  108. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  109. package/dist-lib/home-paths.cjs +1 -33
  110. package/dist-lib/host/bridge-host.cjs +404 -329
  111. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  112. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  113. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  114. package/dist-lib/host/websocket-proxy.cjs +1 -1
  115. package/dist-lib/index.cjs +1 -1
  116. package/dist-lib/jump-to-source-babel.cjs +1 -1
  117. package/dist-lib/menu.cjs +1 -1
  118. package/dist-lib/menu.mjs +1 -1
  119. package/dist-lib/metro.cjs +1 -1
  120. package/dist-lib/profiles.cjs +1 -1
  121. package/dist-lib/render-mode.cjs +1 -1
  122. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  123. package/dist-lib/sdk.cjs +1 -1
  124. package/dist-lib/sdk.mjs +1 -1
  125. package/dist-lib/skills.cjs +523 -435
  126. package/dist-lib/vite.cjs +1 -1
  127. package/package.json +2 -1
  128. package/src/home-paths.ts +5 -40
  129. package/src/host/bridge-host.ts +4 -5
  130. package/src/runtime-delivery.ts +36 -377
  131. package/dist-cli/chunks/beta-LVHWRJCU.js +0 -2
  132. package/dist-cli/chunks/chunk-2273TU3I.js +0 -1
  133. package/dist-cli/chunks/chunk-6ACR2P3U.js +0 -1
  134. package/dist-cli/chunks/chunk-HMOZ6UBM.js +0 -29
  135. package/dist-cli/chunks/chunk-JFXVQVXW.js +0 -4
  136. package/dist-cli/chunks/chunk-OLCSJXAQ.js +0 -7
  137. package/dist-cli/chunks/cli-version-Z2MVFJOA.js +0 -2
  138. package/dist-cli/chunks/daemon-NM537LET.js +0 -2
  139. package/dist-cli/chunks/drivers-NOJ7CRI4.js +0 -2
  140. package/dist-cli/chunks/flow-7ETXJJ6C.js +0 -2
  141. package/dist-cli/chunks/help-FE5IIJIM.js +0 -2
  142. package/dist-cli/chunks/home-paths-BLOAGTA2.js +0 -2
  143. package/dist-cli/chunks/inspect-UA376AF4.js +0 -2
  144. package/dist-cli/chunks/install-desktop-KBXAATJW.js +0 -2
  145. package/dist-cli/chunks/optional-demo-registry-RF4SZYRO.js +0 -2
  146. package/dist-cli/chunks/runtime-UJOIMMBS.js +0 -2
  147. package/dist-cli/chunks/store-JG2JCUFN.js +0 -2
  148. package/dist-cli/chunks/telemetry-HI4GNTOS.js +0 -2
  149. package/dist-cli/chunks/upload-ZNSE6XIN.js +0 -2
  150. package/dist-cli/chunks/version-4HZWAXW3.js +0 -4
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.251 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.253 | (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;
@@ -70,10 +70,10 @@ __export(bridge_host_exports, {
70
70
  injectSharedConfigIntoHtml: () => injectSharedConfigIntoHtml
71
71
  });
72
72
  module.exports = __toCommonJS(bridge_host_exports);
73
- var import_child_process4 = require("child_process");
74
- var import_fs4 = __toESM(require("fs"), 1);
73
+ var import_child_process3 = require("child_process");
74
+ var import_fs3 = __toESM(require("fs"), 1);
75
75
  var import_http3 = require("http");
76
- var import_path4 = __toESM(require("path"), 1);
76
+ var import_path3 = __toESM(require("path"), 1);
77
77
  var import_ws2 = require("ws");
78
78
 
79
79
  // scripts/dev-server-scanner.ts
@@ -156,10 +156,10 @@ function tcpPing(port, timeout = TCP_GATE_MS) {
156
156
  sock.connect(port, "localhost");
157
157
  });
158
158
  }
159
- function httpGet(port, path9, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
159
+ function httpGet(port, path10, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
160
160
  return new Promise((resolve) => {
161
161
  const req = import_http.default.request(
162
- { hostname: "localhost", port, path: path9, method, timeout, headers },
162
+ { hostname: "localhost", port, path: path10, method, timeout, headers },
163
163
  (res) => {
164
164
  let body = "";
165
165
  res.on("data", (c) => body += c.toString());
@@ -706,34 +706,6 @@ function writeActiveRuntime(version) {
706
706
  import_node_fs2.default.writeFileSync(activeRuntimeFile(), `${version}
707
707
  `, "utf8");
708
708
  }
709
- function listInstalledRuntimes() {
710
- try {
711
- return import_node_fs2.default.readdirSync(runtimesDir(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort(compareSemver);
712
- } catch {
713
- return [];
714
- }
715
- }
716
- function compareSemver(a, b) {
717
- const parse = (v) => {
718
- const hyphen = v.indexOf("-");
719
- const core = hyphen >= 0 ? v.slice(0, hyphen) : v;
720
- const pre = hyphen >= 0 ? v.slice(hyphen + 1) : "";
721
- const parts = core.split(".").map((n) => Number.parseInt(n, 10));
722
- if (parts.some((n) => !Number.isFinite(n))) return [[Number.POSITIVE_INFINITY], v];
723
- return [parts, pre];
724
- };
725
- const [an, ap] = parse(a);
726
- const [bn, bp] = parse(b);
727
- for (let i = 0; i < Math.max(an.length, bn.length); i++) {
728
- const av = an[i] ?? 0;
729
- const bv = bn[i] ?? 0;
730
- if (av !== bv) return av - bv;
731
- }
732
- if (ap === bp) return 0;
733
- if (!ap) return 1;
734
- if (!bp) return -1;
735
- return ap < bp ? -1 : 1;
736
- }
737
709
  function activeRuntimeDir() {
738
710
  const version = readActiveRuntime();
739
711
  if (!version) return null;
@@ -839,149 +811,243 @@ function resolveDevCheckoutRuntimeRoot() {
839
811
  return null;
840
812
  }
841
813
 
842
- // src/runtime-delivery.ts
843
- var import_child_process2 = require("child_process");
844
- var import_crypto = __toESM(require("crypto"), 1);
845
- var import_fs2 = __toESM(require("fs"), 1);
846
- var import_path2 = __toESM(require("path"), 1);
847
- var import_stream = require("stream");
848
- var import_promises = require("stream/promises");
849
- var DEFAULT_RUNTIME_CDN_ORIGIN = "https://contrast.dev";
850
- var RUNTIME_CDN_ORIGIN_ENV = "SOOTSIM_CDN_ORIGIN";
851
- var RUNTIME_CHANNEL_ENV = "SOOTSIM_RUNTIME_CHANNEL";
852
- var RUNTIME_PIN_ENV = "SOOTSIM_RUNTIME_VERSION";
853
- function readConfig() {
814
+ // ../contrast-runtime-delivery/src/index.ts
815
+ var import_node_child_process = require("node:child_process");
816
+ var import_node_crypto = __toESM(require("node:crypto"), 1);
817
+ var import_node_fs4 = __toESM(require("node:fs"), 1);
818
+ var import_node_path3 = __toESM(require("node:path"), 1);
819
+ var import_node_stream = require("node:stream");
820
+ var import_promises = require("node:stream/promises");
821
+
822
+ // ../contrast-runtime-delivery/src/product.ts
823
+ var import_node_fs3 = __toESM(require("node:fs"), 1);
824
+ var import_node_path2 = __toESM(require("node:path"), 1);
825
+ function listInstalledRuntimes(product) {
854
826
  try {
855
- const parsed = JSON.parse(import_fs2.default.readFileSync(configFilePath(), "utf8"));
856
- return parsed && typeof parsed === "object" ? parsed : {};
827
+ return import_node_fs3.default.readdirSync(product.runtimesDir(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort(compareSemver);
857
828
  } catch {
858
- return {};
829
+ return [];
859
830
  }
860
831
  }
861
- function resolveRuntimeCdnOrigin(input) {
862
- const config = readConfig();
863
- const value = input || process.env[RUNTIME_CDN_ORIGIN_ENV] || config.cdnOrigin || DEFAULT_RUNTIME_CDN_ORIGIN;
864
- return value.replace(/\/+$/, "");
865
- }
866
- function resolveRuntimeChannel(input) {
867
- const config = readConfig();
868
- return input || process.env[RUNTIME_CHANNEL_ENV] || config.runtimeChannel || "stable";
832
+ function runtimeTarballCacheName(product, version) {
833
+ return import_node_path2.default.posix.basename(product.tarballPath(version));
869
834
  }
870
- function resolveRuntimePin() {
871
- const env = process.env[RUNTIME_PIN_ENV];
872
- if (env && env.trim()) return env.trim();
873
- const config = readConfig();
874
- return typeof config.runtimeVersion === "string" && config.runtimeVersion.trim() ? config.runtimeVersion.trim() : null;
875
- }
876
- function runtimeManifestUrl(cdnOrigin) {
877
- const url = new URL(`${resolveRuntimeCdnOrigin(cdnOrigin)}/runtimes/manifest.json`);
878
- url.searchParams.set("t", String(Date.now()));
879
- return url.toString();
880
- }
881
- function runtimeTarballUrl(version, cdnOrigin) {
882
- return `${resolveRuntimeCdnOrigin(cdnOrigin)}/runtimes/sootsim-runtime-${version}.tar.gz`;
883
- }
884
- async function fetchRuntimeManifest(cdnOrigin) {
885
- const url = runtimeManifestUrl(cdnOrigin);
886
- const res = await fetch(url, { headers: { Accept: "application/json" } });
887
- if (!res.ok) {
888
- throw new Error(`manifest fetch failed: ${res.status} ${res.statusText} (${url})`);
835
+ function compareSemver(a, b) {
836
+ const parse = (v) => {
837
+ const hyphen = v.indexOf("-");
838
+ const core = hyphen >= 0 ? v.slice(0, hyphen) : v;
839
+ const pre = hyphen >= 0 ? v.slice(hyphen + 1) : "";
840
+ const parts = core.split(".").map((n) => Number.parseInt(n, 10));
841
+ if (parts.some((n) => !Number.isFinite(n))) return [[Number.POSITIVE_INFINITY], v];
842
+ return [parts, pre];
843
+ };
844
+ const [an, ap] = parse(a);
845
+ const [bn, bp] = parse(b);
846
+ for (let i = 0; i < Math.max(an.length, bn.length); i++) {
847
+ const av = an[i] ?? 0;
848
+ const bv = bn[i] ?? 0;
849
+ if (av !== bv) return av - bv;
889
850
  }
890
- return await res.json();
851
+ if (ap === bp) return 0;
852
+ if (!ap) return 1;
853
+ if (!bp) return -1;
854
+ return ap < bp ? -1 : 1;
891
855
  }
892
- function resolveRuntimeVersion(manifest, opts = {}) {
893
- const channel = resolveRuntimeChannel(opts.channel);
894
- const version = opts.version || resolveRuntimePin() || manifest.channels[channel]?.latest;
895
- if (!version) {
896
- throw new Error(
897
- `no version specified and channel '${channel}' has no latest entry in the manifest`
856
+
857
+ // ../contrast-runtime-delivery/src/index.ts
858
+ function createRuntimeDelivery(product) {
859
+ function readConfig() {
860
+ try {
861
+ const parsed = JSON.parse(
862
+ import_node_fs4.default.readFileSync(product.configFile(), "utf8")
863
+ );
864
+ return parsed && typeof parsed === "object" ? parsed : {};
865
+ } catch {
866
+ return {};
867
+ }
868
+ }
869
+ function resolveCdnOrigin(input) {
870
+ const config = readConfig();
871
+ const value = input || process.env[product.env.cdnOrigin] || config.cdnOrigin || product.defaultCdnOrigin;
872
+ return value.replace(/\/+$/, "");
873
+ }
874
+ function resolveChannel(input) {
875
+ const config = readConfig();
876
+ return input || process.env[product.env.channel] || config.runtimeChannel || "stable";
877
+ }
878
+ function resolvePin() {
879
+ const env = process.env[product.env.pin];
880
+ if (env && env.trim()) return env.trim();
881
+ const config = readConfig();
882
+ return typeof config.runtimeVersion === "string" && config.runtimeVersion.trim() ? config.runtimeVersion.trim() : null;
883
+ }
884
+ function manifestUrl(cdnOrigin) {
885
+ const url = new URL(`${resolveCdnOrigin(cdnOrigin)}${product.manifestPath}`);
886
+ url.searchParams.set("t", String(Date.now()));
887
+ return url.toString();
888
+ }
889
+ function tarballUrl(version, cdnOrigin) {
890
+ return `${resolveCdnOrigin(cdnOrigin)}${product.tarballPath(version)}`;
891
+ }
892
+ async function fetchManifest(cdnOrigin) {
893
+ const url = manifestUrl(cdnOrigin);
894
+ const res = await fetch(url, { headers: { Accept: "application/json" } });
895
+ if (!res.ok) {
896
+ throw new Error(`manifest fetch failed: ${res.status} ${res.statusText} (${url})`);
897
+ }
898
+ return await res.json();
899
+ }
900
+ function resolveVersion(manifest, opts = {}) {
901
+ const channel = resolveChannel(opts.channel);
902
+ const version = opts.version || resolvePin() || manifest.channels[channel]?.latest;
903
+ if (!version) {
904
+ throw new Error(
905
+ `no version specified and channel '${channel}' has no latest entry in the manifest`
906
+ );
907
+ }
908
+ const entry = manifest.versions[version];
909
+ if (!entry) {
910
+ throw new Error(
911
+ `version ${version} not found in manifest; available: ${Object.keys(manifest.versions).slice(-10).join(", ") || "(none)"}`
912
+ );
913
+ }
914
+ return { version, channel, entry };
915
+ }
916
+ async function install(opts = {}) {
917
+ product.ensureHome();
918
+ const cdnOrigin = resolveCdnOrigin(opts.cdnOrigin);
919
+ const manifest = await fetchManifest(cdnOrigin);
920
+ const { version, channel, entry } = resolveVersion(manifest, opts);
921
+ const destDir = product.runtimeDir(version);
922
+ const setActive = opts.setActive !== false;
923
+ if (!opts.force && import_node_fs4.default.existsSync(import_node_path3.default.join(destDir, product.installedMarker))) {
924
+ if (setActive) product.writeActiveRuntime(version);
925
+ return {
926
+ version,
927
+ channel,
928
+ cdnOrigin,
929
+ runtimeDir: destDir,
930
+ installed: false,
931
+ activated: setActive,
932
+ manifest
933
+ };
934
+ }
935
+ const tarUrl = entry.tarball || tarballUrl(version, cdnOrigin);
936
+ const tarCachePath = import_node_path3.default.join(
937
+ product.cacheDir(),
938
+ runtimeTarballCacheName(product, version)
898
939
  );
899
- }
900
- const entry = manifest.versions[version];
901
- if (!entry) {
902
- throw new Error(
903
- `version ${version} not found in manifest; available: ${Object.keys(manifest.versions).slice(-10).join(", ") || "(none)"}`
940
+ process.stderr.write(`${product.id}: downloading runtime ${version}\u2026
941
+ `);
942
+ await downloadToFile(tarUrl, tarCachePath);
943
+ process.stderr.write(`${product.id}: extracting runtime ${version}\u2026
944
+ `);
945
+ const actualSha = await sha256File(tarCachePath);
946
+ if (actualSha !== entry.sha256) {
947
+ import_node_fs4.default.rmSync(tarCachePath, { force: true });
948
+ throw new Error(
949
+ `sha256 mismatch for runtime ${version}: expected ${entry.sha256}, actual ${actualSha}`
950
+ );
951
+ }
952
+ const tmpDir = import_node_path3.default.join(
953
+ import_node_path3.default.dirname(destDir),
954
+ `.installing-${version}-${process.pid}`
904
955
  );
905
- }
906
- return { version, channel, entry };
907
- }
908
- async function installRuntime(opts = {}) {
909
- ensureSootsimHome();
910
- const cdnOrigin = resolveRuntimeCdnOrigin(opts.cdnOrigin);
911
- const manifest = await fetchRuntimeManifest(cdnOrigin);
912
- const { version, channel, entry } = resolveRuntimeVersion(manifest, opts);
913
- const destDir = runtimeDir(version);
914
- const setActive = opts.setActive !== false;
915
- if (!opts.force && import_fs2.default.existsSync(import_path2.default.join(destDir, "index.html"))) {
916
- if (setActive) writeActiveRuntime(version);
956
+ import_node_fs4.default.rmSync(tmpDir, { recursive: true, force: true });
957
+ import_node_fs4.default.mkdirSync(tmpDir, { recursive: true });
958
+ try {
959
+ await extractTarball(tarCachePath, tmpDir);
960
+ if (!import_node_fs4.default.existsSync(import_node_path3.default.join(tmpDir, product.installedMarker))) {
961
+ throw new Error(
962
+ `extracted tarball for runtime ${version} is missing ${product.installedMarker}`
963
+ );
964
+ }
965
+ import_node_fs4.default.rmSync(destDir, { recursive: true, force: true });
966
+ import_node_fs4.default.renameSync(tmpDir, destDir);
967
+ } catch (err) {
968
+ import_node_fs4.default.rmSync(tmpDir, { recursive: true, force: true });
969
+ throw err;
970
+ }
971
+ if (setActive) product.writeActiveRuntime(version);
917
972
  return {
918
973
  version,
919
974
  channel,
920
975
  cdnOrigin,
921
976
  runtimeDir: destDir,
922
- installed: false,
977
+ installed: true,
923
978
  activated: setActive,
924
979
  manifest
925
980
  };
926
981
  }
927
- const tarUrl = entry.tarball || runtimeTarballUrl(version, cdnOrigin);
928
- const tarCachePath = import_path2.default.join(cacheDir(), `sootsim-runtime-${version}.tar.gz`);
929
- process.stderr.write(`sootsim: downloading runtime ${version}\u2026
930
- `);
931
- await downloadToFile(tarUrl, tarCachePath);
932
- process.stderr.write(`sootsim: extracting runtime ${version}\u2026
933
- `);
934
- const actualSha = await sha256File(tarCachePath);
935
- if (actualSha !== entry.sha256) {
936
- import_fs2.default.rmSync(tarCachePath, { force: true });
937
- throw new Error(
938
- `sha256 mismatch for runtime ${version}: expected ${entry.sha256}, actual ${actualSha}`
939
- );
940
- }
941
- const tmpDir = import_path2.default.join(import_path2.default.dirname(destDir), `.installing-${version}-${process.pid}`);
942
- import_fs2.default.rmSync(tmpDir, { recursive: true, force: true });
943
- import_fs2.default.mkdirSync(tmpDir, { recursive: true });
944
- try {
945
- await extractTarball(tarCachePath, tmpDir);
946
- if (!import_fs2.default.existsSync(import_path2.default.join(tmpDir, "index.html"))) {
947
- throw new Error(`extracted tarball for runtime ${version} is missing index.html`);
982
+ async function updateToLatest(opts = {}) {
983
+ product.ensureHome();
984
+ const cdnOrigin = resolveCdnOrigin(opts.cdnOrigin);
985
+ const channel = resolveChannel(opts.channel);
986
+ const pin = resolvePin();
987
+ if (pin) {
988
+ const activeVersion2 = product.readActiveRuntime();
989
+ const pinInstalled = import_node_fs4.default.existsSync(
990
+ import_node_path3.default.join(product.runtimeDir(pin), product.installedMarker)
991
+ );
992
+ if (activeVersion2 === pin && pinInstalled) {
993
+ return {
994
+ checked: true,
995
+ updated: false,
996
+ reason: `runtime is pinned to ${pin}`,
997
+ activeVersion: activeVersion2,
998
+ latestVersion: pin
999
+ };
1000
+ }
1001
+ const installed2 = await install({
1002
+ version: pin,
1003
+ channel,
1004
+ cdnOrigin,
1005
+ setActive: false
1006
+ });
1007
+ return {
1008
+ checked: true,
1009
+ updated: true,
1010
+ reason: `runtime is pinned to ${pin}`,
1011
+ activeVersion: pin,
1012
+ latestVersion: pin,
1013
+ install: installed2
1014
+ };
948
1015
  }
949
- import_fs2.default.rmSync(destDir, { recursive: true, force: true });
950
- import_fs2.default.renameSync(tmpDir, destDir);
951
- } catch (err) {
952
- import_fs2.default.rmSync(tmpDir, { recursive: true, force: true });
953
- throw err;
954
- }
955
- if (setActive) writeActiveRuntime(version);
956
- return {
957
- version,
958
- channel,
959
- cdnOrigin,
960
- runtimeDir: destDir,
961
- installed: true,
962
- activated: setActive,
963
- manifest
964
- };
965
- }
966
- async function updateRuntimeToLatest(opts = {}) {
967
- ensureSootsimHome();
968
- const cdnOrigin = resolveRuntimeCdnOrigin(opts.cdnOrigin);
969
- const channel = resolveRuntimeChannel(opts.channel);
970
- const pin = resolveRuntimePin();
971
- if (pin) {
972
- const activeVersion2 = readActiveRuntime();
973
- const pinInstalled = import_fs2.default.existsSync(import_path2.default.join(runtimeDir(pin), "index.html"));
974
- if (activeVersion2 === pin && pinInstalled) {
1016
+ const manifest = await fetchManifest(cdnOrigin);
1017
+ const latestVersion = manifest.channels[channel]?.latest;
1018
+ if (!latestVersion) {
975
1019
  return {
976
1020
  checked: true,
977
1021
  updated: false,
978
- reason: `runtime is pinned to ${pin}`,
979
- activeVersion: activeVersion2,
980
- latestVersion: pin
1022
+ reason: `channel '${channel}' has no latest runtime`,
1023
+ activeVersion: product.readActiveRuntime()
1024
+ };
1025
+ }
1026
+ const entry = manifest.versions[latestVersion];
1027
+ if (!entry) {
1028
+ return {
1029
+ checked: true,
1030
+ updated: false,
1031
+ reason: `manifest is missing version ${latestVersion}`,
1032
+ activeVersion: product.readActiveRuntime(),
1033
+ latestVersion
1034
+ };
1035
+ }
1036
+ const activeVersion = product.readActiveRuntime();
1037
+ const activeDir = activeVersion ? product.runtimeDir(activeVersion) : null;
1038
+ const activeInstalled = activeDir ? import_node_fs4.default.existsSync(import_node_path3.default.join(activeDir, product.installedMarker)) : false;
1039
+ const shouldInstall = !activeVersion || !activeInstalled || compareSemver(latestVersion, activeVersion) > 0;
1040
+ if (!shouldInstall) {
1041
+ return {
1042
+ checked: true,
1043
+ updated: false,
1044
+ reason: "active runtime is current",
1045
+ activeVersion,
1046
+ latestVersion
981
1047
  };
982
1048
  }
983
- const install2 = await installRuntime({
984
- version: pin,
1049
+ const installed = await install({
1050
+ version: latestVersion,
985
1051
  channel,
986
1052
  cdnOrigin,
987
1053
  setActive: false
@@ -989,57 +1055,40 @@ async function updateRuntimeToLatest(opts = {}) {
989
1055
  return {
990
1056
  checked: true,
991
1057
  updated: true,
992
- reason: `runtime is pinned to ${pin}`,
993
- activeVersion: pin,
994
- latestVersion: pin,
995
- install: install2
996
- };
997
- }
998
- const manifest = await fetchRuntimeManifest(cdnOrigin);
999
- const latestVersion = manifest.channels[channel]?.latest;
1000
- if (!latestVersion) {
1001
- return {
1002
- checked: true,
1003
- updated: false,
1004
- reason: `channel '${channel}' has no latest runtime`,
1005
- activeVersion: readActiveRuntime()
1058
+ activeVersion: latestVersion,
1059
+ latestVersion,
1060
+ install: installed
1006
1061
  };
1007
1062
  }
1008
- const entry = manifest.versions[latestVersion];
1009
- if (!entry) {
1010
- return {
1011
- checked: true,
1012
- updated: false,
1013
- reason: `manifest is missing version ${latestVersion}`,
1014
- activeVersion: readActiveRuntime(),
1015
- latestVersion
1016
- };
1017
- }
1018
- const activeVersion = readActiveRuntime();
1019
- const activeDir = activeVersion ? runtimeDir(activeVersion) : null;
1020
- const activeInstalled = activeDir ? import_fs2.default.existsSync(import_path2.default.join(activeDir, "index.html")) : false;
1021
- const shouldInstall = !activeVersion || !activeInstalled || compareSemver(latestVersion, activeVersion) > 0;
1022
- if (!shouldInstall) {
1023
- return {
1024
- checked: true,
1025
- updated: false,
1026
- reason: "active runtime is current",
1027
- activeVersion,
1028
- latestVersion
1029
- };
1063
+ async function checkUpToDate(opts = {}) {
1064
+ const active = product.readActiveRuntime();
1065
+ const pinned = resolvePin();
1066
+ if (pinned) {
1067
+ return { active, latest: pinned, pinned, outdated: active !== pinned };
1068
+ }
1069
+ try {
1070
+ const manifest = await fetchManifest(resolveCdnOrigin(opts.cdnOrigin));
1071
+ const channel = resolveChannel(opts.channel);
1072
+ const latest = manifest.channels[channel]?.latest ?? null;
1073
+ const outdated = !!(latest && (!active || compareSemver(latest, active) > 0));
1074
+ return { active, latest, pinned: null, outdated };
1075
+ } catch {
1076
+ return { active, latest: null, pinned: null, outdated: false };
1077
+ }
1030
1078
  }
1031
- const install = await installRuntime({
1032
- version: latestVersion,
1033
- channel,
1034
- cdnOrigin,
1035
- setActive: false
1036
- });
1037
1079
  return {
1038
- checked: true,
1039
- updated: true,
1040
- activeVersion: latestVersion,
1041
- latestVersion,
1042
- install
1080
+ product,
1081
+ resolveCdnOrigin,
1082
+ resolveChannel,
1083
+ resolvePin,
1084
+ manifestUrl,
1085
+ tarballUrl,
1086
+ fetchManifest,
1087
+ resolveVersion,
1088
+ install,
1089
+ updateToLatest,
1090
+ checkUpToDate,
1091
+ listInstalled: () => listInstalledRuntimes(product)
1043
1092
  };
1044
1093
  }
1045
1094
  async function downloadToFile(url, destPath) {
@@ -1047,17 +1096,17 @@ async function downloadToFile(url, destPath) {
1047
1096
  if (!res.ok || !res.body) {
1048
1097
  throw new Error(`download failed: ${res.status} ${res.statusText} (${url})`);
1049
1098
  }
1050
- import_fs2.default.mkdirSync(import_path2.default.dirname(destPath), { recursive: true });
1099
+ import_node_fs4.default.mkdirSync(import_node_path3.default.dirname(destPath), { recursive: true });
1051
1100
  const tmp = `${destPath}.partial`;
1052
1101
  try {
1053
1102
  await (0, import_promises.pipeline)(
1054
- import_stream.Readable.fromWeb(res.body),
1055
- import_fs2.default.createWriteStream(tmp)
1103
+ import_node_stream.Readable.fromWeb(res.body),
1104
+ import_node_fs4.default.createWriteStream(tmp)
1056
1105
  );
1057
- import_fs2.default.renameSync(tmp, destPath);
1106
+ import_node_fs4.default.renameSync(tmp, destPath);
1058
1107
  } catch (err) {
1059
1108
  try {
1060
- import_fs2.default.unlinkSync(tmp);
1109
+ import_node_fs4.default.unlinkSync(tmp);
1061
1110
  } catch {
1062
1111
  }
1063
1112
  throw err;
@@ -1065,8 +1114,8 @@ async function downloadToFile(url, destPath) {
1065
1114
  }
1066
1115
  function sha256File(filePath) {
1067
1116
  return new Promise((resolve, reject) => {
1068
- const hash = import_crypto.default.createHash("sha256");
1069
- const stream = import_fs2.default.createReadStream(filePath);
1117
+ const hash = import_node_crypto.default.createHash("sha256");
1118
+ const stream = import_node_fs4.default.createReadStream(filePath);
1070
1119
  stream.on("data", (chunk) => hash.update(chunk));
1071
1120
  stream.on("error", reject);
1072
1121
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -1074,7 +1123,7 @@ function sha256File(filePath) {
1074
1123
  }
1075
1124
  function extractTarball(tarPath, destDir) {
1076
1125
  return new Promise((resolve, reject) => {
1077
- const child = (0, import_child_process2.spawn)("tar", ["-xzf", tarPath, "-C", destDir], {
1126
+ const child = (0, import_node_child_process.spawn)("tar", ["-xzf", tarPath, "-C", destDir], {
1078
1127
  stdio: ["ignore", "inherit", "inherit"]
1079
1128
  });
1080
1129
  child.on("error", reject);
@@ -1085,15 +1134,41 @@ function extractTarball(tarPath, destDir) {
1085
1134
  });
1086
1135
  }
1087
1136
 
1137
+ // src/runtime-delivery.ts
1138
+ var DEFAULT_RUNTIME_CDN_ORIGIN = "https://contrast.dev";
1139
+ var SOOTSIM_RUNTIME_PRODUCT = {
1140
+ id: "sootsim",
1141
+ defaultCdnOrigin: DEFAULT_RUNTIME_CDN_ORIGIN,
1142
+ manifestPath: "/runtimes/manifest.json",
1143
+ tarballPath: (version) => `/runtimes/sootsim-runtime-${version}.tar.gz`,
1144
+ // the engine runtime is a static SPA: shell at the root, engine/ and
1145
+ // engine-tenant/ beneath it. index.html is what the daemon and the
1146
+ // vite/metro plugins serve, so its absence means the tarball is not one.
1147
+ installedMarker: "index.html",
1148
+ env: {
1149
+ cdnOrigin: "SOOTSIM_CDN_ORIGIN",
1150
+ channel: "SOOTSIM_RUNTIME_CHANNEL",
1151
+ pin: "SOOTSIM_RUNTIME_VERSION"
1152
+ },
1153
+ ensureHome: ensureSootsimHome,
1154
+ configFile: configFilePath,
1155
+ runtimesDir,
1156
+ runtimeDir,
1157
+ cacheDir,
1158
+ readActiveRuntime,
1159
+ writeActiveRuntime
1160
+ };
1161
+ var sootsimRuntime = createRuntimeDelivery(SOOTSIM_RUNTIME_PRODUCT);
1162
+
1088
1163
  // src/host/agent-host.ts
1089
- var import_node_fs6 = __toESM(require("node:fs"), 1);
1090
- var import_node_path5 = __toESM(require("node:path"), 1);
1164
+ var import_node_fs8 = __toESM(require("node:fs"), 1);
1165
+ var import_node_path7 = __toESM(require("node:path"), 1);
1091
1166
 
1092
1167
  // src/agent-sessions.ts
1093
- var import_node_child_process = require("node:child_process");
1094
- var import_node_crypto2 = require("node:crypto");
1095
- var import_node_fs4 = __toESM(require("node:fs"), 1);
1096
- var import_node_path3 = __toESM(require("node:path"), 1);
1168
+ var import_node_child_process2 = require("node:child_process");
1169
+ var import_node_crypto3 = require("node:crypto");
1170
+ var import_node_fs6 = __toESM(require("node:fs"), 1);
1171
+ var import_node_path5 = __toESM(require("node:path"), 1);
1097
1172
  var import_node_readline = __toESM(require("node:readline"), 1);
1098
1173
 
1099
1174
  // src/agent-events.ts
@@ -1136,9 +1211,9 @@ function encodeAgentPromptEnvelope(input) {
1136
1211
  }
1137
1212
 
1138
1213
  // src/attached-projects.ts
1139
- var import_node_crypto = require("node:crypto");
1140
- var import_node_fs3 = __toESM(require("node:fs"), 1);
1141
- var import_node_path2 = __toESM(require("node:path"), 1);
1214
+ var import_node_crypto2 = require("node:crypto");
1215
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
1216
+ var import_node_path4 = __toESM(require("node:path"), 1);
1142
1217
  var overrideDir = null;
1143
1218
  function userDataDir() {
1144
1219
  if (overrideDir) return overrideDir;
@@ -1150,7 +1225,7 @@ function getUserDataDir() {
1150
1225
  return userDataDir();
1151
1226
  }
1152
1227
  function storeFile() {
1153
- return import_node_path2.default.join(userDataDir(), "attached-projects.json");
1228
+ return import_node_path4.default.join(userDataDir(), "attached-projects.json");
1154
1229
  }
1155
1230
  function cloneEmpty() {
1156
1231
  return {
@@ -1164,7 +1239,7 @@ function loadStore() {
1164
1239
  const file = storeFile();
1165
1240
  let raw;
1166
1241
  try {
1167
- raw = import_node_fs3.default.readFileSync(file, "utf8");
1242
+ raw = import_node_fs5.default.readFileSync(file, "utf8");
1168
1243
  } catch (err) {
1169
1244
  if (err.code === "ENOENT") return cloneEmpty();
1170
1245
  throw err;
@@ -1181,7 +1256,7 @@ function loadStore() {
1181
1256
  } catch (err) {
1182
1257
  const quarantine = `${file}.corrupt-${Date.now()}`;
1183
1258
  try {
1184
- import_node_fs3.default.renameSync(file, quarantine);
1259
+ import_node_fs5.default.renameSync(file, quarantine);
1185
1260
  console.warn(
1186
1261
  `[sootsim] attached-projects.json was unparseable; quarantined to ${quarantine}. original error: ${err.message}`
1187
1262
  );
@@ -1192,16 +1267,16 @@ function loadStore() {
1192
1267
  }
1193
1268
  function writeStore(store) {
1194
1269
  const file = storeFile();
1195
- import_node_fs3.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
1270
+ import_node_fs5.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
1196
1271
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
1197
- const fd = import_node_fs3.default.openSync(tmp, "w", 384);
1272
+ const fd = import_node_fs5.default.openSync(tmp, "w", 384);
1198
1273
  try {
1199
- import_node_fs3.default.writeFileSync(fd, JSON.stringify(store, null, 2));
1200
- import_node_fs3.default.fsyncSync(fd);
1274
+ import_node_fs5.default.writeFileSync(fd, JSON.stringify(store, null, 2));
1275
+ import_node_fs5.default.fsyncSync(fd);
1201
1276
  } finally {
1202
- import_node_fs3.default.closeSync(fd);
1277
+ import_node_fs5.default.closeSync(fd);
1203
1278
  }
1204
- import_node_fs3.default.renameSync(tmp, file);
1279
+ import_node_fs5.default.renameSync(tmp, file);
1205
1280
  }
1206
1281
  function mutateStore(fn) {
1207
1282
  const store = loadStore();
@@ -1210,13 +1285,13 @@ function mutateStore(fn) {
1210
1285
  return store;
1211
1286
  }
1212
1287
  function projectIdForCwd(cwd) {
1213
- return (0, import_node_crypto.createHash)("sha256").update(import_node_path2.default.resolve(cwd)).digest("hex").slice(0, 16);
1288
+ return (0, import_node_crypto2.createHash)("sha256").update(import_node_path4.default.resolve(cwd)).digest("hex").slice(0, 16);
1214
1289
  }
1215
1290
  function newSessionId() {
1216
- return `s_${(0, import_node_crypto.randomBytes)(10).toString("hex")}`;
1291
+ return `s_${(0, import_node_crypto2.randomBytes)(10).toString("hex")}`;
1217
1292
  }
1218
1293
  function upsertProject(input) {
1219
- const cwd = import_node_path2.default.resolve(input.cwd);
1294
+ const cwd = import_node_path4.default.resolve(input.cwd);
1220
1295
  const id = projectIdForCwd(cwd);
1221
1296
  let result;
1222
1297
  mutateStore((store) => {
@@ -1242,7 +1317,7 @@ function upsertProject(input) {
1242
1317
  const now = Date.now();
1243
1318
  const created = {
1244
1319
  id,
1245
- name: input.name ?? import_node_path2.default.basename(cwd),
1320
+ name: input.name ?? import_node_path4.default.basename(cwd),
1246
1321
  cwd,
1247
1322
  repoRoot: input.repoRoot,
1248
1323
  sourceRoots: input.sourceRoots ?? [cwd],
@@ -1372,8 +1447,8 @@ async function seedFromDemoAppRegistry() {
1372
1447
  if (apps.length === 0) return;
1373
1448
  mutateStore((store) => {
1374
1449
  for (const app of apps) {
1375
- if (!import_node_fs3.default.existsSync(app.dir)) continue;
1376
- const cwd = import_node_path2.default.resolve(app.dir);
1450
+ if (!import_node_fs5.default.existsSync(app.dir)) continue;
1451
+ const cwd = import_node_path4.default.resolve(app.dir);
1377
1452
  const id = projectIdForCwd(cwd);
1378
1453
  if (store.attachedProjects.some((p) => p.id === id)) continue;
1379
1454
  const now = Date.now();
@@ -1397,16 +1472,16 @@ async function seedFromDemoAppRegistry() {
1397
1472
 
1398
1473
  // src/agent-sessions.ts
1399
1474
  function sessionDir(sessionId) {
1400
- return import_node_path3.default.join(getUserDataDir(), "sessions", sessionId);
1475
+ return import_node_path5.default.join(getUserDataDir(), "sessions", sessionId);
1401
1476
  }
1402
1477
  function promptFifoPath(sessionId) {
1403
- return import_node_path3.default.join(sessionDir(sessionId), "prompt.in");
1478
+ return import_node_path5.default.join(sessionDir(sessionId), "prompt.in");
1404
1479
  }
1405
1480
  function eventsFifoPath(sessionId) {
1406
- return import_node_path3.default.join(sessionDir(sessionId), "events.out");
1481
+ return import_node_path5.default.join(sessionDir(sessionId), "events.out");
1407
1482
  }
1408
1483
  function transcriptPath(sessionId) {
1409
- return import_node_path3.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
1484
+ return import_node_path5.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
1410
1485
  }
1411
1486
  function pidIsAlive(pid, sessionId) {
1412
1487
  if (!pid) return false;
@@ -1416,7 +1491,7 @@ function pidIsAlive(pid, sessionId) {
1416
1491
  return false;
1417
1492
  }
1418
1493
  if (sessionId) {
1419
- if (!import_node_fs4.default.existsSync(sessionDir(sessionId))) return false;
1494
+ if (!import_node_fs6.default.existsSync(sessionDir(sessionId))) return false;
1420
1495
  }
1421
1496
  return true;
1422
1497
  }
@@ -1428,11 +1503,11 @@ function resolveSootsimInvocation() {
1428
1503
  const resourcesPath = process.resourcesPath;
1429
1504
  if (resourcesPath) {
1430
1505
  const candidates = [
1431
- import_node_path3.default.join(resourcesPath, "bin", "sootsim"),
1432
- import_node_path3.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
1506
+ import_node_path5.default.join(resourcesPath, "bin", "sootsim"),
1507
+ import_node_path5.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
1433
1508
  ];
1434
1509
  for (const c of candidates) {
1435
- if (import_node_fs4.default.existsSync(c)) return { cmd: c, prefixArgs: [] };
1510
+ if (import_node_fs6.default.existsSync(c)) return { cmd: c, prefixArgs: [] };
1436
1511
  }
1437
1512
  }
1438
1513
  }
@@ -1455,15 +1530,15 @@ function tryWorkspaceSootsim() {
1455
1530
  const sootsimDir = resolveSootsimPackageDir();
1456
1531
  if (!sootsimDir) return null;
1457
1532
  const binaryName = `sootsim-${process.platform}-${process.arch}`;
1458
- const distBinary = import_node_path3.default.join(sootsimDir, "dist-bin", binaryName);
1459
- if (import_node_fs4.default.existsSync(distBinary)) return { cmd: distBinary, prefixArgs: [] };
1460
- const distBin = import_node_path3.default.join(sootsimDir, "dist-cli", "bin.js");
1461
- if (import_node_fs4.default.existsSync(distBin)) {
1533
+ const distBinary = import_node_path5.default.join(sootsimDir, "dist-bin", binaryName);
1534
+ if (import_node_fs6.default.existsSync(distBinary)) return { cmd: distBinary, prefixArgs: [] };
1535
+ const distBin = import_node_path5.default.join(sootsimDir, "dist-cli", "bin.js");
1536
+ if (import_node_fs6.default.existsSync(distBin)) {
1462
1537
  try {
1463
- const src = import_node_path3.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
1464
- if (import_node_fs4.default.existsSync(src)) {
1465
- const srcMtime = import_node_fs4.default.statSync(src).mtimeMs;
1466
- const buildMtime = import_node_fs4.default.statSync(distBin).mtimeMs;
1538
+ const src = import_node_path5.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
1539
+ if (import_node_fs6.default.existsSync(src)) {
1540
+ const srcMtime = import_node_fs6.default.statSync(src).mtimeMs;
1541
+ const buildMtime = import_node_fs6.default.statSync(distBin).mtimeMs;
1467
1542
  if (buildMtime < srcMtime) {
1468
1543
  console.warn(
1469
1544
  `[sootsim] dist-cli/bin.js is older than agent-wrapper.ts \u2014 rebuild with \`bun run --cwd packages/sootsim build:cli\`.`
@@ -1482,22 +1557,22 @@ function tryWorkspaceSootsim() {
1482
1557
  function resolveSootsimPackageDir() {
1483
1558
  try {
1484
1559
  const resolved = require.resolve("sootsim/package.json");
1485
- return import_node_path3.default.dirname(resolved);
1560
+ return import_node_path5.default.dirname(resolved);
1486
1561
  } catch {
1487
1562
  }
1488
1563
  const here = fileFromImportMeta();
1489
1564
  if (!here) return null;
1490
- let cur = import_node_path3.default.dirname(here);
1565
+ let cur = import_node_path5.default.dirname(here);
1491
1566
  for (let i = 0; i < 8; i++) {
1492
- const pkg = import_node_path3.default.join(cur, "package.json");
1567
+ const pkg = import_node_path5.default.join(cur, "package.json");
1493
1568
  try {
1494
- if (import_node_fs4.default.existsSync(pkg)) {
1495
- const parsed = JSON.parse(import_node_fs4.default.readFileSync(pkg, "utf8"));
1569
+ if (import_node_fs6.default.existsSync(pkg)) {
1570
+ const parsed = JSON.parse(import_node_fs6.default.readFileSync(pkg, "utf8"));
1496
1571
  if (parsed.name === "sootsim") return cur;
1497
1572
  }
1498
1573
  } catch {
1499
1574
  }
1500
- const parent = import_node_path3.default.dirname(cur);
1575
+ const parent = import_node_path5.default.dirname(cur);
1501
1576
  if (parent === cur) break;
1502
1577
  cur = parent;
1503
1578
  }
@@ -1513,28 +1588,28 @@ function fileFromImportMeta() {
1513
1588
  }
1514
1589
  }
1515
1590
  async function withStartLock(projectId, provider, fn) {
1516
- const lockDir = import_node_path3.default.join(getUserDataDir(), "locks");
1517
- import_node_fs4.default.mkdirSync(lockDir, { recursive: true });
1591
+ const lockDir = import_node_path5.default.join(getUserDataDir(), "locks");
1592
+ import_node_fs6.default.mkdirSync(lockDir, { recursive: true });
1518
1593
  try {
1519
- import_node_fs4.default.chmodSync(lockDir, 448);
1594
+ import_node_fs6.default.chmodSync(lockDir, 448);
1520
1595
  } catch {
1521
1596
  }
1522
- const lockPath = import_node_path3.default.join(lockDir, `start-${projectId}-${provider}.lock`);
1597
+ const lockPath = import_node_path5.default.join(lockDir, `start-${projectId}-${provider}.lock`);
1523
1598
  const deadline = Date.now() + 4e3;
1524
1599
  let fd = null;
1525
1600
  while (fd === null) {
1526
1601
  try {
1527
- fd = import_node_fs4.default.openSync(
1602
+ fd = import_node_fs6.default.openSync(
1528
1603
  lockPath,
1529
- import_node_fs4.constants.O_WRONLY | import_node_fs4.constants.O_CREAT | import_node_fs4.constants.O_EXCL,
1604
+ import_node_fs6.constants.O_WRONLY | import_node_fs6.constants.O_CREAT | import_node_fs6.constants.O_EXCL,
1530
1605
  384
1531
1606
  );
1532
1607
  } catch (err) {
1533
1608
  if (err.code !== "EEXIST") throw err;
1534
1609
  try {
1535
- const stale = Number(import_node_fs4.default.readFileSync(lockPath, "utf8").trim());
1610
+ const stale = Number(import_node_fs6.default.readFileSync(lockPath, "utf8").trim());
1536
1611
  if (stale && !isProcessAlive(stale)) {
1537
- import_node_fs4.default.unlinkSync(lockPath);
1612
+ import_node_fs6.default.unlinkSync(lockPath);
1538
1613
  continue;
1539
1614
  }
1540
1615
  } catch {
@@ -1548,15 +1623,15 @@ async function withStartLock(projectId, provider, fn) {
1548
1623
  }
1549
1624
  }
1550
1625
  try {
1551
- import_node_fs4.default.writeFileSync(fd, String(process.pid));
1626
+ import_node_fs6.default.writeFileSync(fd, String(process.pid));
1552
1627
  return await fn();
1553
1628
  } finally {
1554
1629
  try {
1555
- import_node_fs4.default.closeSync(fd);
1630
+ import_node_fs6.default.closeSync(fd);
1556
1631
  } catch {
1557
1632
  }
1558
1633
  try {
1559
- import_node_fs4.default.unlinkSync(lockPath);
1634
+ import_node_fs6.default.unlinkSync(lockPath);
1560
1635
  } catch {
1561
1636
  }
1562
1637
  }
@@ -1570,28 +1645,28 @@ function isProcessAlive(pid) {
1570
1645
  }
1571
1646
  }
1572
1647
  function mkfifoSync(p) {
1573
- const parent = import_node_path3.default.dirname(p);
1574
- import_node_fs4.default.mkdirSync(parent, { recursive: true });
1648
+ const parent = import_node_path5.default.dirname(p);
1649
+ import_node_fs6.default.mkdirSync(parent, { recursive: true });
1575
1650
  try {
1576
- import_node_fs4.default.chmodSync(parent, 448);
1651
+ import_node_fs6.default.chmodSync(parent, 448);
1577
1652
  } catch {
1578
1653
  }
1579
- if (import_node_fs4.default.existsSync(p)) {
1654
+ if (import_node_fs6.default.existsSync(p)) {
1580
1655
  try {
1581
- const stat = import_node_fs4.default.statSync(p);
1656
+ const stat = import_node_fs6.default.statSync(p);
1582
1657
  if (stat.isFIFO()) {
1583
1658
  try {
1584
- import_node_fs4.default.chmodSync(p, 384);
1659
+ import_node_fs6.default.chmodSync(p, 384);
1585
1660
  } catch {
1586
1661
  }
1587
1662
  return;
1588
1663
  }
1589
- import_node_fs4.default.unlinkSync(p);
1664
+ import_node_fs6.default.unlinkSync(p);
1590
1665
  } catch {
1591
- import_node_fs4.default.unlinkSync(p);
1666
+ import_node_fs6.default.unlinkSync(p);
1592
1667
  }
1593
1668
  }
1594
- const result = (0, import_node_child_process.spawnSync)("mkfifo", ["-m", "600", p]);
1669
+ const result = (0, import_node_child_process2.spawnSync)("mkfifo", ["-m", "600", p]);
1595
1670
  if (result.status !== 0) {
1596
1671
  throw new Error(
1597
1672
  `mkfifo(${p}) failed: ${result.stderr?.toString().trim() || "unknown error"}`
@@ -1621,7 +1696,7 @@ async function startSession(opts) {
1621
1696
  `session already running for project=${project.id} provider=${provider} (session ${existingLive.id}, pid ${existingLive.wrapperPid}). end it first with \`sootsim agent end <sessionId>\`.`
1622
1697
  );
1623
1698
  }
1624
- const claudeSessionUuid = provider === "claude" ? (0, import_node_crypto2.randomUUID)() : void 0;
1699
+ const claudeSessionUuid = provider === "claude" ? (0, import_node_crypto3.randomUUID)() : void 0;
1625
1700
  const session = upsertSession({
1626
1701
  projectId: project.id,
1627
1702
  provider,
@@ -1635,10 +1710,10 @@ async function startSession(opts) {
1635
1710
  const transcript = transcriptPath(session.id);
1636
1711
  mkfifoSync(promptIn);
1637
1712
  mkfifoSync(eventsOut);
1638
- const transcriptDir = import_node_path3.default.dirname(transcript);
1639
- import_node_fs4.default.mkdirSync(transcriptDir, { recursive: true });
1713
+ const transcriptDir = import_node_path5.default.dirname(transcript);
1714
+ import_node_fs6.default.mkdirSync(transcriptDir, { recursive: true });
1640
1715
  try {
1641
- import_node_fs4.default.chmodSync(transcriptDir, 448);
1716
+ import_node_fs6.default.chmodSync(transcriptDir, 448);
1642
1717
  } catch {
1643
1718
  }
1644
1719
  const { cmd, prefixArgs } = resolveSootsimInvocation();
@@ -1666,7 +1741,7 @@ async function startSession(opts) {
1666
1741
  if (claudeSessionUuid) {
1667
1742
  wrapperArgs.push("--claude-session-uuid", claudeSessionUuid);
1668
1743
  }
1669
- const child = (0, import_node_child_process.spawn)(cmd, wrapperArgs, {
1744
+ const child = (0, import_node_child_process2.spawn)(cmd, wrapperArgs, {
1670
1745
  detached: true,
1671
1746
  stdio: "ignore",
1672
1747
  env: {
@@ -1689,7 +1764,7 @@ async function startSession(opts) {
1689
1764
  }
1690
1765
  }
1691
1766
  try {
1692
- import_node_fs4.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
1767
+ import_node_fs6.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
1693
1768
  } catch {
1694
1769
  }
1695
1770
  updateSessionStatus(session.id, { status: "ended" });
@@ -1717,18 +1792,18 @@ async function sendPrompt(sessionId, prompt) {
1717
1792
  );
1718
1793
  }
1719
1794
  const fifo = promptFifoPath(sessionId);
1720
- if (!import_node_fs4.default.existsSync(fifo)) {
1795
+ if (!import_node_fs6.default.existsSync(fifo)) {
1721
1796
  throw new AgentSessionError("NO_FIFO", `prompt FIFO missing: ${fifo}`);
1722
1797
  }
1723
- const fd = import_node_fs4.default.openSync(fifo, import_node_fs4.constants.O_WRONLY);
1798
+ const fd = import_node_fs6.default.openSync(fifo, import_node_fs6.constants.O_WRONLY);
1724
1799
  try {
1725
1800
  const wireText = encodeAgentPromptEnvelope(prompt);
1726
1801
  if (!wireText) {
1727
1802
  throw new AgentSessionError("EMPTY_PROMPT", "prompt text is empty");
1728
1803
  }
1729
- import_node_fs4.default.writeSync(fd, wireText + "\n");
1804
+ import_node_fs6.default.writeSync(fd, wireText + "\n");
1730
1805
  } finally {
1731
- import_node_fs4.default.closeSync(fd);
1806
+ import_node_fs6.default.closeSync(fd);
1732
1807
  }
1733
1808
  updateSessionStatus(sessionId, {
1734
1809
  lastPrompt: prompt.displayText ?? prompt.text,
@@ -1750,7 +1825,7 @@ async function endSession(sessionId) {
1750
1825
  const base = getUserDataDir();
1751
1826
  if (dir.startsWith(base)) {
1752
1827
  try {
1753
- import_node_fs4.default.rmSync(dir, { recursive: true, force: true });
1828
+ import_node_fs6.default.rmSync(dir, { recursive: true, force: true });
1754
1829
  } catch {
1755
1830
  }
1756
1831
  }
@@ -1758,11 +1833,11 @@ async function endSession(sessionId) {
1758
1833
  }
1759
1834
  function subscribeEvents(sessionId, onEvent) {
1760
1835
  const fifo = eventsFifoPath(sessionId);
1761
- if (!import_node_fs4.default.existsSync(fifo)) {
1836
+ if (!import_node_fs6.default.existsSync(fifo)) {
1762
1837
  throw new AgentSessionError("NO_FIFO", `events FIFO missing: ${fifo}`);
1763
1838
  }
1764
- const fd = import_node_fs4.default.openSync(fifo, import_node_fs4.constants.O_RDWR);
1765
- const stream = import_node_fs4.default.createReadStream("", { fd, autoClose: true });
1839
+ const fd = import_node_fs6.default.openSync(fifo, import_node_fs6.constants.O_RDWR);
1840
+ const stream = import_node_fs6.default.createReadStream("", { fd, autoClose: true });
1766
1841
  const rl = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
1767
1842
  rl.on("line", (line) => {
1768
1843
  const event = parseAgentEventLine(line);
@@ -1783,7 +1858,7 @@ function subscribeEvents(sessionId, onEvent) {
1783
1858
  };
1784
1859
  }
1785
1860
  async function waitForFirstEvent(fifo, predicate, timeoutMs) {
1786
- const fd = import_node_fs4.default.openSync(fifo, import_node_fs4.constants.O_RDWR | import_node_fs4.constants.O_NONBLOCK);
1861
+ const fd = import_node_fs6.default.openSync(fifo, import_node_fs6.constants.O_RDWR | import_node_fs6.constants.O_NONBLOCK);
1787
1862
  const buf = Buffer.alloc(8192);
1788
1863
  let leftover = "";
1789
1864
  const deadline = Date.now() + timeoutMs;
@@ -1791,7 +1866,7 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
1791
1866
  while (Date.now() < deadline) {
1792
1867
  let n = 0;
1793
1868
  try {
1794
- n = import_node_fs4.default.readSync(fd, buf, 0, buf.length, null);
1869
+ n = import_node_fs6.default.readSync(fd, buf, 0, buf.length, null);
1795
1870
  } catch (err) {
1796
1871
  if (err.code !== "EAGAIN") throw err;
1797
1872
  n = 0;
@@ -1811,15 +1886,15 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
1811
1886
  }
1812
1887
  return null;
1813
1888
  } finally {
1814
- import_node_fs4.default.closeSync(fd);
1889
+ import_node_fs6.default.closeSync(fd);
1815
1890
  }
1816
1891
  }
1817
1892
 
1818
1893
  // src/agentbus-prompt.ts
1819
- var import_node_child_process2 = require("node:child_process");
1820
- var import_node_fs5 = __toESM(require("node:fs"), 1);
1894
+ var import_node_child_process3 = require("node:child_process");
1895
+ var import_node_fs7 = __toESM(require("node:fs"), 1);
1821
1896
  var import_node_os2 = __toESM(require("node:os"), 1);
1822
- var import_node_path4 = __toESM(require("node:path"), 1);
1897
+ var import_node_path6 = __toESM(require("node:path"), 1);
1823
1898
 
1824
1899
  // src/agent-identity.ts
1825
1900
  var AGENTBUS_SESSION_RE = /^[A-Za-z0-9._:-]{1,160}$/;
@@ -1842,8 +1917,8 @@ function resolveAgentbusCommand(explicit) {
1842
1917
  if (fromArg) return fromArg;
1843
1918
  const fromEnv = process.env.AGENTBUS_COMMAND?.trim();
1844
1919
  if (fromEnv) return fromEnv;
1845
- const localBin = import_node_path4.default.join(import_node_os2.default.homedir(), ".local", "bin", "agentbus");
1846
- if (import_node_fs5.default.existsSync(localBin)) return localBin;
1920
+ const localBin = import_node_path6.default.join(import_node_os2.default.homedir(), ".local", "bin", "agentbus");
1921
+ if (import_node_fs7.default.existsSync(localBin)) return localBin;
1847
1922
  return "agentbus";
1848
1923
  }
1849
1924
  async function sendPromptToAgentbusSession(input) {
@@ -1858,7 +1933,7 @@ async function sendPromptToAgentbusSession(input) {
1858
1933
  const command = resolveAgentbusCommand(input.command);
1859
1934
  const timeoutMs = input.timeoutMs ?? 15e3;
1860
1935
  await new Promise((resolve, reject) => {
1861
- const child = (0, import_node_child_process2.spawn)(command, ["send", sessionId, text], {
1936
+ const child = (0, import_node_child_process3.spawn)(command, ["send", sessionId, text], {
1862
1937
  stdio: ["ignore", "ignore", "pipe"],
1863
1938
  env: process.env
1864
1939
  });
@@ -2084,7 +2159,7 @@ var AgentHost = class {
2084
2159
  );
2085
2160
  const project = upsertProject({
2086
2161
  cwd: match.cwd,
2087
- name: match.projectName ?? import_node_path5.default.basename(match.cwd),
2162
+ name: match.projectName ?? import_node_path7.default.basename(match.cwd),
2088
2163
  preferredProvider: input.provider ?? existing?.preferredProvider,
2089
2164
  sourceRoots: existing?.sourceRoots ?? [match.cwd],
2090
2165
  knownBundleUrls,
@@ -2095,18 +2170,18 @@ var AgentHost = class {
2095
2170
  }
2096
2171
  getTranscript(sessionId) {
2097
2172
  const p = transcriptPath(sessionId);
2098
- if (!import_node_fs6.default.existsSync(p)) {
2173
+ if (!import_node_fs8.default.existsSync(p)) {
2099
2174
  return { error: "transcript not found", code: "NO_TRANSCRIPT" };
2100
2175
  }
2101
- return import_node_fs6.default.readFileSync(p, "utf8");
2176
+ return import_node_fs8.default.readFileSync(p, "utf8");
2102
2177
  }
2103
2178
  getPaths() {
2104
2179
  const dir = getUserDataDir();
2105
2180
  return {
2106
2181
  userDataDir: dir,
2107
- storeFile: import_node_path5.default.join(dir, "attached-projects.json"),
2108
- sessionsDir: import_node_path5.default.join(dir, "sessions"),
2109
- transcriptsDir: import_node_path5.default.join(dir, "transcripts")
2182
+ storeFile: import_node_path7.default.join(dir, "attached-projects.json"),
2183
+ sessionsDir: import_node_path7.default.join(dir, "sessions"),
2184
+ transcriptsDir: import_node_path7.default.join(dir, "transcripts")
2110
2185
  };
2111
2186
  }
2112
2187
  // --- subscription management ---
@@ -2691,10 +2766,10 @@ function handleAppApiRequest(req, res) {
2691
2766
  }
2692
2767
 
2693
2768
  // src/host/open-url.ts
2694
- var import_child_process3 = require("child_process");
2695
- var import_fs3 = require("fs");
2769
+ var import_child_process2 = require("child_process");
2770
+ var import_fs2 = require("fs");
2696
2771
  var import_os = require("os");
2697
- var import_path3 = require("path");
2772
+ var import_path2 = require("path");
2698
2773
  var MAC_CHROMIUM_CANDIDATES = [
2699
2774
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
2700
2775
  "~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
@@ -2732,19 +2807,19 @@ var UNIX_CHROMIUM_COMMANDS = [
2732
2807
  ];
2733
2808
  var WINDOWS_CHROMIUM_COMMANDS = ["chrome", "msedge", "brave"];
2734
2809
  function expandHome(candidate) {
2735
- return candidate.startsWith("~/") ? (0, import_path3.join)((0, import_os.homedir)(), candidate.slice(2)) : candidate;
2810
+ return candidate.startsWith("~/") ? (0, import_path2.join)((0, import_os.homedir)(), candidate.slice(2)) : candidate;
2736
2811
  }
2737
2812
  function firstExisting(candidates) {
2738
2813
  for (const candidate of candidates) {
2739
2814
  const expanded = expandHome(candidate);
2740
- if ((0, import_fs3.existsSync)(expanded)) return expanded;
2815
+ if ((0, import_fs2.existsSync)(expanded)) return expanded;
2741
2816
  }
2742
2817
  return null;
2743
2818
  }
2744
2819
  function lookupExecutable(command, platform) {
2745
2820
  try {
2746
2821
  const tool = platform === "win32" ? "where" : "which";
2747
- const out = (0, import_child_process3.execFileSync)(tool, [command], {
2822
+ const out = (0, import_child_process2.execFileSync)(tool, [command], {
2748
2823
  encoding: "utf8",
2749
2824
  timeout: 1500,
2750
2825
  stdio: ["ignore", "pipe", "ignore"]
@@ -2756,7 +2831,7 @@ function lookupExecutable(command, platform) {
2756
2831
  }
2757
2832
  function resolveChromiumBinary(platform = process.platform) {
2758
2833
  const envCandidate = process.env.CHROME_PATH || process.env.CHROMIUM_PATH;
2759
- if (envCandidate && (0, import_fs3.existsSync)(envCandidate)) return envCandidate;
2834
+ if (envCandidate && (0, import_fs2.existsSync)(envCandidate)) return envCandidate;
2760
2835
  const direct = platform === "darwin" ? firstExisting(MAC_CHROMIUM_CANDIDATES) : platform === "win32" ? firstExisting(WINDOWS_CHROMIUM_CANDIDATES) : firstExisting(LINUX_CHROMIUM_CANDIDATES);
2761
2836
  if (direct) return direct;
2762
2837
  const commands = platform === "win32" ? WINDOWS_CHROMIUM_COMMANDS : UNIX_CHROMIUM_COMMANDS;
@@ -2813,7 +2888,7 @@ function buildChromiumUrlCommand(url, options = {}) {
2813
2888
  }
2814
2889
  async function spawnLaunch(command, args, detached) {
2815
2890
  return new Promise((resolve, reject) => {
2816
- const child = (0, import_child_process3.spawn)(command, args, {
2891
+ const child = (0, import_child_process2.spawn)(command, args, {
2817
2892
  detached,
2818
2893
  stdio: "ignore"
2819
2894
  });
@@ -3328,7 +3403,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3328
3403
  return;
3329
3404
  }
3330
3405
  if (msg.type === "runtime:list") {
3331
- const versions = listInstalledRuntimes();
3406
+ const versions = sootsimRuntime.listInstalled();
3332
3407
  const active = this.getActiveRuntime();
3333
3408
  const reply = {
3334
3409
  type: "runtime:list:ok",
@@ -3345,7 +3420,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3345
3420
  }
3346
3421
  if (msg.type === "runtime:use") {
3347
3422
  const version = typeof msg.version === "string" ? msg.version : "";
3348
- const installed = listInstalledRuntimes();
3423
+ const installed = sootsimRuntime.listInstalled();
3349
3424
  if (!installed.includes(version)) {
3350
3425
  try {
3351
3426
  ws.send(
@@ -3874,7 +3949,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3874
3949
  if (reason === "startup") {
3875
3950
  process.stderr.write("sootsim: checking for runtime updates\u2026\n");
3876
3951
  }
3877
- const result = await updateRuntimeToLatest();
3952
+ const result = await sootsimRuntime.updateToLatest();
3878
3953
  if (!result.updated || !result.latestVersion) {
3879
3954
  if (reason === "startup") {
3880
3955
  process.stderr.write(
@@ -4110,34 +4185,34 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4110
4185
  return;
4111
4186
  }
4112
4187
  }
4113
- const resolved = import_path4.default.resolve(baseDir, "." + rel);
4114
- const baseWithSep = baseDir.endsWith(import_path4.default.sep) ? baseDir : baseDir + import_path4.default.sep;
4188
+ const resolved = import_path3.default.resolve(baseDir, "." + rel);
4189
+ const baseWithSep = baseDir.endsWith(import_path3.default.sep) ? baseDir : baseDir + import_path3.default.sep;
4115
4190
  if (!resolved.startsWith(baseWithSep) && resolved !== baseDir) {
4116
4191
  res.writeHead(403);
4117
4192
  res.end("forbidden");
4118
4193
  return;
4119
4194
  }
4120
- import_fs4.default.realpath(resolved, (realErr, realResolved) => {
4195
+ import_fs3.default.realpath(resolved, (realErr, realResolved) => {
4121
4196
  const servePath = realErr ? resolved : realResolved;
4122
- const servePathWithSep = servePath.endsWith(import_path4.default.sep) ? servePath : servePath + import_path4.default.sep;
4197
+ const servePathWithSep = servePath.endsWith(import_path3.default.sep) ? servePath : servePath + import_path3.default.sep;
4123
4198
  if (!realErr) {
4124
4199
  const realBaseWithSep = (() => {
4125
4200
  try {
4126
- const rb = import_fs4.default.realpathSync(baseDir);
4127
- return rb.endsWith(import_path4.default.sep) ? rb : rb + import_path4.default.sep;
4201
+ const rb = import_fs3.default.realpathSync(baseDir);
4202
+ return rb.endsWith(import_path3.default.sep) ? rb : rb + import_path3.default.sep;
4128
4203
  } catch {
4129
4204
  return baseWithSep;
4130
4205
  }
4131
4206
  })();
4132
- if (!servePathWithSep.startsWith(realBaseWithSep) && servePath + import_path4.default.sep !== realBaseWithSep) {
4207
+ if (!servePathWithSep.startsWith(realBaseWithSep) && servePath + import_path3.default.sep !== realBaseWithSep) {
4133
4208
  res.writeHead(403);
4134
4209
  res.end("forbidden");
4135
4210
  return;
4136
4211
  }
4137
4212
  }
4138
- import_fs4.default.stat(servePath, (err, stats) => {
4213
+ import_fs3.default.stat(servePath, (err, stats) => {
4139
4214
  if (err || !stats?.isFile()) {
4140
- const ext2 = import_path4.default.extname(rel).toLowerCase();
4215
+ const ext2 = import_path3.default.extname(rel).toLowerCase();
4141
4216
  if (ext2 && ext2 !== ".html") {
4142
4217
  res.writeHead(404);
4143
4218
  res.end("not found");
@@ -4148,8 +4223,8 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4148
4223
  res.end("not found");
4149
4224
  return;
4150
4225
  }
4151
- const indexPath = import_path4.default.join(baseDir, "index.html");
4152
- import_fs4.default.readFile(indexPath, (err2, data) => {
4226
+ const indexPath = import_path3.default.join(baseDir, "index.html");
4227
+ import_fs3.default.readFile(indexPath, (err2, data) => {
4153
4228
  if (err2) {
4154
4229
  res.writeHead(404);
4155
4230
  res.end("not found");
@@ -4169,7 +4244,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4169
4244
  });
4170
4245
  return;
4171
4246
  }
4172
- const ext = import_path4.default.extname(servePath).toLowerCase();
4247
+ const ext = import_path3.default.extname(servePath).toLowerCase();
4173
4248
  const contentType = HTTP_MIME_TYPES[ext] || "application/octet-stream";
4174
4249
  res.writeHead(200, {
4175
4250
  "Content-Type": contentType,
@@ -4180,7 +4255,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4180
4255
  return;
4181
4256
  }
4182
4257
  if (ext === ".html") {
4183
- import_fs4.default.readFile(servePath, (readErr, data) => {
4258
+ import_fs3.default.readFile(servePath, (readErr, data) => {
4184
4259
  if (readErr) {
4185
4260
  try {
4186
4261
  res.end();
@@ -4194,7 +4269,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4194
4269
  });
4195
4270
  return;
4196
4271
  }
4197
- const stream = import_fs4.default.createReadStream(servePath);
4272
+ const stream = import_fs3.default.createReadStream(servePath);
4198
4273
  stream.pipe(res);
4199
4274
  stream.on("error", () => {
4200
4275
  try {
@@ -4400,7 +4475,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4400
4475
  const target = `${filePath}${loc}`;
4401
4476
  const trySpawn = (cmd, args) => new Promise((resolve) => {
4402
4477
  try {
4403
- const child = (0, import_child_process4.spawn)(cmd, args, { detached: true, stdio: "ignore" });
4478
+ const child = (0, import_child_process3.spawn)(cmd, args, { detached: true, stdio: "ignore" });
4404
4479
  let settled = false;
4405
4480
  child.on("error", () => {
4406
4481
  if (settled) return;