sootsim 0.1.250 → 0.1.252

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 (151) hide show
  1. package/README.md +8 -5
  2. package/dist-cli/bin.js +4 -4
  3. package/dist-cli/chunks/{agent-JPKDUK4P.js → agent-JRN5KS75.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-Z64YICSQ.js → agent-wrapper-2ZH2WQ2L.js} +2 -2
  5. package/dist-cli/chunks/{app-fonts-524IXHZF.js → app-fonts-LBQ7A7LL.js} +2 -2
  6. package/dist-cli/chunks/{assert-WZRU7L2S.js → assert-XFQCRREO.js} +2 -2
  7. package/dist-cli/chunks/{auth-3ZPMASCJ.js → auth-DAMRQQ53.js} +2 -2
  8. package/dist-cli/chunks/beta-JJ4YSCOE.js +2 -0
  9. package/dist-cli/chunks/{camera-FPGFHVAE.js → camera-NQWFOHXI.js} +2 -2
  10. package/dist-cli/chunks/chunk-25FMG2DR.js +1 -0
  11. package/dist-cli/chunks/{chunk-ZFJZLRAG.js → chunk-4P7EXY3Q.js} +2 -2
  12. package/dist-cli/chunks/{chunk-JMJJJQ3N.js → chunk-54RVP6DQ.js} +1 -1
  13. package/dist-cli/chunks/{chunk-ODXX3PDC.js → chunk-56CSO42W.js} +2 -2
  14. package/dist-cli/chunks/{chunk-7SDQP3K6.js → chunk-5PVVFMLA.js} +2 -2
  15. package/dist-cli/chunks/{chunk-LFRFK56L.js → chunk-677KF26C.js} +1 -1
  16. package/dist-cli/chunks/{chunk-ZBAOOQ7L.js → chunk-AJVA4XFD.js} +2 -2
  17. package/dist-cli/chunks/{chunk-OOYVCERU.js → chunk-BU4QJGYM.js} +2 -2
  18. package/dist-cli/chunks/{chunk-LCEWHXGT.js → chunk-F2DZ3EHN.js} +2 -2
  19. package/dist-cli/chunks/chunk-F7NVK7U5.js +29 -0
  20. package/dist-cli/chunks/{chunk-GVEBFN3A.js → chunk-FCNI6ZWQ.js} +2 -2
  21. package/dist-cli/chunks/{chunk-7CESRUUF.js → chunk-GBJS3QVL.js} +2 -2
  22. package/dist-cli/chunks/{chunk-RDQ4RP2I.js → chunk-GPECMCCD.js} +2 -2
  23. package/dist-cli/chunks/{chunk-HQVZVJWB.js → chunk-GRPNPAFQ.js} +1 -1
  24. package/dist-cli/chunks/{chunk-EJQGI6U6.js → chunk-HERRSHL3.js} +2 -2
  25. package/dist-cli/chunks/{chunk-QQDVBF5C.js → chunk-HNYCB3ZY.js} +1 -1
  26. package/dist-cli/chunks/chunk-HV773SED.js +4 -0
  27. package/dist-cli/chunks/{chunk-DOOM7A5G.js → chunk-J4XJHPJV.js} +2 -2
  28. package/dist-cli/chunks/{chunk-TIWQBBA6.js → chunk-JYJ37QKN.js} +2 -2
  29. package/dist-cli/chunks/chunk-K5UURTEB.js +1 -0
  30. package/dist-cli/chunks/{chunk-AV734IPM.js → chunk-LHAUZJH5.js} +1 -1
  31. package/dist-cli/chunks/{chunk-XBWJ7DPK.js → chunk-LL7KIGNG.js} +2 -2
  32. package/dist-cli/chunks/{chunk-HTGCR74O.js → chunk-LXRPMNYD.js} +2 -2
  33. package/dist-cli/chunks/{chunk-2FGQSNTC.js → chunk-LYIN47A5.js} +2 -2
  34. package/dist-cli/chunks/{chunk-Q72PZST6.js → chunk-MJU6KNLI.js} +3 -3
  35. package/dist-cli/chunks/{chunk-23DV2XSE.js → chunk-MMCNYDBD.js} +1 -1
  36. package/dist-cli/chunks/{chunk-G2QKQWCY.js → chunk-NADQK2CU.js} +1 -1
  37. package/dist-cli/chunks/{chunk-N52AECUU.js → chunk-NB6RTLLI.js} +3 -3
  38. package/dist-cli/chunks/{chunk-S5ILN4OM.js → chunk-NNEUAQME.js} +3 -3
  39. package/dist-cli/chunks/{chunk-RMP6YTMJ.js → chunk-NTZYSZ3Q.js} +2 -2
  40. package/dist-cli/chunks/{chunk-AWT2ISDX.js → chunk-NYKHYDBN.js} +1 -1
  41. package/dist-cli/chunks/{chunk-7WCUYHR3.js → chunk-OSPFE643.js} +1 -1
  42. package/dist-cli/chunks/{chunk-DX6CUGUF.js → chunk-PLK4SCBY.js} +2 -2
  43. package/dist-cli/chunks/{chunk-HLDQHWK5.js → chunk-PVDPE5BH.js} +1 -1
  44. package/dist-cli/chunks/{chunk-L5VG4RQT.js → chunk-QHNYUT7D.js} +1 -1
  45. package/dist-cli/chunks/{chunk-ZA4K47LA.js → chunk-S6DIPG7L.js} +2 -2
  46. package/dist-cli/chunks/{chunk-YACJ3KQ4.js → chunk-T2BJFGBC.js} +1 -1
  47. package/dist-cli/chunks/{chunk-CJMASJXU.js → chunk-TDWFR6H5.js} +2 -2
  48. package/dist-cli/chunks/{chunk-COON6LFC.js → chunk-U5JMZPBD.js} +1 -1
  49. package/dist-cli/chunks/{chunk-N6WXF6OS.js → chunk-UA5FVOEO.js} +1 -1
  50. package/dist-cli/chunks/chunk-UVE2LY6V.js +7 -0
  51. package/dist-cli/chunks/{chunk-GHJRRNR3.js → chunk-WS3DDBN3.js} +1 -1
  52. package/dist-cli/chunks/{chunk-KZZFQRPX.js → chunk-X2IKDV5A.js} +1 -1
  53. package/dist-cli/chunks/{chunk-GXNJSLZM.js → chunk-ZKN7GCX4.js} +2 -2
  54. package/dist-cli/chunks/cli-version-OZF5OMW4.js +2 -0
  55. package/dist-cli/chunks/{compat-C3N6PFK5.js → compat-ME4QWRZ6.js} +3 -3
  56. package/dist-cli/chunks/{config-UVEGEV7N.js → config-TMMSBXDV.js} +2 -2
  57. package/dist-cli/chunks/{control-ROSEFQH3.js → control-QGYYWKJP.js} +2 -2
  58. package/dist-cli/chunks/daemon-6NBHEVXS.js +2 -0
  59. package/dist-cli/chunks/{debug-VLB6FCUL.js → debug-LFJ46Q45.js} +3 -3
  60. package/dist-cli/chunks/{desktop-LAPJ2XXG.js → desktop-W5GPU7JA.js} +3 -3
  61. package/dist-cli/chunks/{detox-UKRIYJBM.js → detox-U3B3N3K7.js} +2 -2
  62. package/dist-cli/chunks/{device-HABOB4FC.js → device-I2WVQFXL.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-TPTVALBD.js → diagnose-6TQOA4QJ.js} +2 -2
  64. package/dist-cli/chunks/drivers-5J25VDIG.js +2 -0
  65. package/dist-cli/chunks/{film-RZABTGXV.js → film-P5E64XQH.js} +3 -3
  66. package/dist-cli/chunks/flow-LD6MOZQC.js +2 -0
  67. package/dist-cli/chunks/help-VCWJZBQU.js +2 -0
  68. package/dist-cli/chunks/{hidden-runtime-alias-AITHRBI5.js → hidden-runtime-alias-ICP5W4W5.js} +2 -2
  69. package/dist-cli/chunks/home-paths-MCD3DISJ.js +2 -0
  70. package/dist-cli/chunks/inspect-VF575AD3.js +2 -0
  71. package/dist-cli/chunks/install-desktop-Q4Y6FTSA.js +2 -0
  72. package/dist-cli/chunks/{login-LIPDXOLT.js → login-A5LRYTNO.js} +4 -4
  73. package/dist-cli/chunks/{logout-7TCXGQM4.js → logout-M3HFZGFU.js} +2 -2
  74. package/dist-cli/chunks/{maestro-CQRCUEZJ.js → maestro-HNREOYXF.js} +3 -3
  75. package/dist-cli/chunks/{maestro-generate-TBBDRYEU.js → maestro-generate-BEEDUOOJ.js} +3 -3
  76. package/dist-cli/chunks/{mode-Z7E6YZTJ.js → mode-IBFHS3G2.js} +2 -2
  77. package/dist-cli/chunks/optional-demo-registry-6XYQJXI5.js +2 -0
  78. package/dist-cli/chunks/{perf-OS7TTPQ2.js → perf-62BD5VIU.js} +2 -2
  79. package/dist-cli/chunks/{record-YH22UVU2.js → record-2ILPO4XB.js} +3 -3
  80. package/dist-cli/chunks/runtime-WCDQFR7G.js +2 -0
  81. package/dist-cli/chunks/{screenshot-command-DV35HE24.js → screenshot-command-YZR5KVV2.js} +3 -3
  82. package/dist-cli/chunks/{screenshots-capture-OV6RMSA2.js → screenshots-capture-QHQ43OBD.js} +2 -2
  83. package/dist-cli/chunks/{serve-WTZRYS6X.js → serve-GQ4PUD5P.js} +15 -15
  84. package/dist-cli/chunks/{setup-AQ46RLHL.js → setup-C74I2RO3.js} +3 -3
  85. package/dist-cli/chunks/{skills-P6P5L7CV.js → skills-I7EVR3MU.js} +2 -2
  86. package/dist-cli/chunks/{storage-PIHIVEBL.js → storage-CH6BAYB4.js} +2 -2
  87. package/dist-cli/chunks/store-PTQ4QMFA.js +2 -0
  88. package/dist-cli/chunks/telemetry-AJDAGOFX.js +2 -0
  89. package/dist-cli/chunks/{timeline-334MEYTC.js → timeline-FUS7F5LI.js} +2 -2
  90. package/dist-cli/chunks/{upgrade-UVEGTAME.js → upgrade-DHMZFD5R.js} +4 -4
  91. package/dist-cli/chunks/upload-XJXPQMKX.js +2 -0
  92. package/dist-cli/chunks/version-M2GSAVKM.js +4 -0
  93. package/dist-cli/chunks/web-NV7UM2T7.js +2 -0
  94. package/dist-cli/chunks/{what-happened-3UH7ZQW6.js → what-happened-VRDDXQWR.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 +396 -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 +517 -437
  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 +32 -377
  131. package/dist-cli/chunks/beta-BBI3GYZ6.js +0 -2
  132. package/dist-cli/chunks/chunk-2UGQF7XX.js +0 -4
  133. package/dist-cli/chunks/chunk-J3XNJTO5.js +0 -1
  134. package/dist-cli/chunks/chunk-M7NRJV7O.js +0 -1
  135. package/dist-cli/chunks/chunk-PZR5U4RD.js +0 -29
  136. package/dist-cli/chunks/chunk-TJBXFUCI.js +0 -7
  137. package/dist-cli/chunks/cli-version-GWSUBSWJ.js +0 -2
  138. package/dist-cli/chunks/daemon-NVFYFCWX.js +0 -2
  139. package/dist-cli/chunks/drivers-ZUD4PX2W.js +0 -2
  140. package/dist-cli/chunks/flow-NHVT35XI.js +0 -2
  141. package/dist-cli/chunks/help-CM77GYXU.js +0 -2
  142. package/dist-cli/chunks/home-paths-XIHGBDLL.js +0 -2
  143. package/dist-cli/chunks/inspect-2WK43KRH.js +0 -2
  144. package/dist-cli/chunks/install-desktop-TS2RBWU5.js +0 -2
  145. package/dist-cli/chunks/optional-demo-registry-ANMKFJWS.js +0 -2
  146. package/dist-cli/chunks/runtime-WYDPR5RI.js +0 -2
  147. package/dist-cli/chunks/store-SVTB7QDS.js +0 -2
  148. package/dist-cli/chunks/telemetry-HM3ULN5U.js +0 -2
  149. package/dist-cli/chunks/upload-BIEPTIMK.js +0 -2
  150. package/dist-cli/chunks/version-VITMYQHS.js +0 -4
  151. package/dist-cli/chunks/web-G5IDDAAI.js +0 -2
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.250 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.252 | (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,239 @@ 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, "index.html"))) {
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, "index.html"))) {
961
+ throw new Error(`extracted tarball for runtime ${version} is missing index.html`);
962
+ }
963
+ import_node_fs4.default.rmSync(destDir, { recursive: true, force: true });
964
+ import_node_fs4.default.renameSync(tmpDir, destDir);
965
+ } catch (err) {
966
+ import_node_fs4.default.rmSync(tmpDir, { recursive: true, force: true });
967
+ throw err;
968
+ }
969
+ if (setActive) product.writeActiveRuntime(version);
917
970
  return {
918
971
  version,
919
972
  channel,
920
973
  cdnOrigin,
921
974
  runtimeDir: destDir,
922
- installed: false,
975
+ installed: true,
923
976
  activated: setActive,
924
977
  manifest
925
978
  };
926
979
  }
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`);
980
+ async function updateToLatest(opts = {}) {
981
+ product.ensureHome();
982
+ const cdnOrigin = resolveCdnOrigin(opts.cdnOrigin);
983
+ const channel = resolveChannel(opts.channel);
984
+ const pin = resolvePin();
985
+ if (pin) {
986
+ const activeVersion2 = product.readActiveRuntime();
987
+ const pinInstalled = import_node_fs4.default.existsSync(import_node_path3.default.join(product.runtimeDir(pin), "index.html"));
988
+ if (activeVersion2 === pin && pinInstalled) {
989
+ return {
990
+ checked: true,
991
+ updated: false,
992
+ reason: `runtime is pinned to ${pin}`,
993
+ activeVersion: activeVersion2,
994
+ latestVersion: pin
995
+ };
996
+ }
997
+ const installed2 = await install({
998
+ version: pin,
999
+ channel,
1000
+ cdnOrigin,
1001
+ setActive: false
1002
+ });
1003
+ return {
1004
+ checked: true,
1005
+ updated: true,
1006
+ reason: `runtime is pinned to ${pin}`,
1007
+ activeVersion: pin,
1008
+ latestVersion: pin,
1009
+ install: installed2
1010
+ };
948
1011
  }
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) {
1012
+ const manifest = await fetchManifest(cdnOrigin);
1013
+ const latestVersion = manifest.channels[channel]?.latest;
1014
+ if (!latestVersion) {
975
1015
  return {
976
1016
  checked: true,
977
1017
  updated: false,
978
- reason: `runtime is pinned to ${pin}`,
979
- activeVersion: activeVersion2,
980
- latestVersion: pin
1018
+ reason: `channel '${channel}' has no latest runtime`,
1019
+ activeVersion: product.readActiveRuntime()
1020
+ };
1021
+ }
1022
+ const entry = manifest.versions[latestVersion];
1023
+ if (!entry) {
1024
+ return {
1025
+ checked: true,
1026
+ updated: false,
1027
+ reason: `manifest is missing version ${latestVersion}`,
1028
+ activeVersion: product.readActiveRuntime(),
1029
+ latestVersion
1030
+ };
1031
+ }
1032
+ const activeVersion = product.readActiveRuntime();
1033
+ const activeDir = activeVersion ? product.runtimeDir(activeVersion) : null;
1034
+ const activeInstalled = activeDir ? import_node_fs4.default.existsSync(import_node_path3.default.join(activeDir, "index.html")) : false;
1035
+ const shouldInstall = !activeVersion || !activeInstalled || compareSemver(latestVersion, activeVersion) > 0;
1036
+ if (!shouldInstall) {
1037
+ return {
1038
+ checked: true,
1039
+ updated: false,
1040
+ reason: "active runtime is current",
1041
+ activeVersion,
1042
+ latestVersion
981
1043
  };
982
1044
  }
983
- const install2 = await installRuntime({
984
- version: pin,
1045
+ const installed = await install({
1046
+ version: latestVersion,
985
1047
  channel,
986
1048
  cdnOrigin,
987
1049
  setActive: false
@@ -989,57 +1051,40 @@ async function updateRuntimeToLatest(opts = {}) {
989
1051
  return {
990
1052
  checked: true,
991
1053
  updated: true,
992
- reason: `runtime is pinned to ${pin}`,
993
- activeVersion: pin,
994
- latestVersion: pin,
995
- install: install2
1054
+ activeVersion: latestVersion,
1055
+ latestVersion,
1056
+ install: installed
996
1057
  };
997
1058
  }
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()
1006
- };
1007
- }
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
- };
1059
+ async function checkUpToDate(opts = {}) {
1060
+ const active = product.readActiveRuntime();
1061
+ const pinned = resolvePin();
1062
+ if (pinned) {
1063
+ return { active, latest: pinned, pinned, outdated: active !== pinned };
1064
+ }
1065
+ try {
1066
+ const manifest = await fetchManifest(resolveCdnOrigin(opts.cdnOrigin));
1067
+ const channel = resolveChannel(opts.channel);
1068
+ const latest = manifest.channels[channel]?.latest ?? null;
1069
+ const outdated = !!(latest && (!active || compareSemver(latest, active) > 0));
1070
+ return { active, latest, pinned: null, outdated };
1071
+ } catch {
1072
+ return { active, latest: null, pinned: null, outdated: false };
1073
+ }
1030
1074
  }
1031
- const install = await installRuntime({
1032
- version: latestVersion,
1033
- channel,
1034
- cdnOrigin,
1035
- setActive: false
1036
- });
1037
1075
  return {
1038
- checked: true,
1039
- updated: true,
1040
- activeVersion: latestVersion,
1041
- latestVersion,
1042
- install
1076
+ product,
1077
+ resolveCdnOrigin,
1078
+ resolveChannel,
1079
+ resolvePin,
1080
+ manifestUrl,
1081
+ tarballUrl,
1082
+ fetchManifest,
1083
+ resolveVersion,
1084
+ install,
1085
+ updateToLatest,
1086
+ checkUpToDate,
1087
+ listInstalled: () => listInstalledRuntimes(product)
1043
1088
  };
1044
1089
  }
1045
1090
  async function downloadToFile(url, destPath) {
@@ -1047,17 +1092,17 @@ async function downloadToFile(url, destPath) {
1047
1092
  if (!res.ok || !res.body) {
1048
1093
  throw new Error(`download failed: ${res.status} ${res.statusText} (${url})`);
1049
1094
  }
1050
- import_fs2.default.mkdirSync(import_path2.default.dirname(destPath), { recursive: true });
1095
+ import_node_fs4.default.mkdirSync(import_node_path3.default.dirname(destPath), { recursive: true });
1051
1096
  const tmp = `${destPath}.partial`;
1052
1097
  try {
1053
1098
  await (0, import_promises.pipeline)(
1054
- import_stream.Readable.fromWeb(res.body),
1055
- import_fs2.default.createWriteStream(tmp)
1099
+ import_node_stream.Readable.fromWeb(res.body),
1100
+ import_node_fs4.default.createWriteStream(tmp)
1056
1101
  );
1057
- import_fs2.default.renameSync(tmp, destPath);
1102
+ import_node_fs4.default.renameSync(tmp, destPath);
1058
1103
  } catch (err) {
1059
1104
  try {
1060
- import_fs2.default.unlinkSync(tmp);
1105
+ import_node_fs4.default.unlinkSync(tmp);
1061
1106
  } catch {
1062
1107
  }
1063
1108
  throw err;
@@ -1065,8 +1110,8 @@ async function downloadToFile(url, destPath) {
1065
1110
  }
1066
1111
  function sha256File(filePath) {
1067
1112
  return new Promise((resolve, reject) => {
1068
- const hash = import_crypto.default.createHash("sha256");
1069
- const stream = import_fs2.default.createReadStream(filePath);
1113
+ const hash = import_node_crypto.default.createHash("sha256");
1114
+ const stream = import_node_fs4.default.createReadStream(filePath);
1070
1115
  stream.on("data", (chunk) => hash.update(chunk));
1071
1116
  stream.on("error", reject);
1072
1117
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -1074,7 +1119,7 @@ function sha256File(filePath) {
1074
1119
  }
1075
1120
  function extractTarball(tarPath, destDir) {
1076
1121
  return new Promise((resolve, reject) => {
1077
- const child = (0, import_child_process2.spawn)("tar", ["-xzf", tarPath, "-C", destDir], {
1122
+ const child = (0, import_node_child_process.spawn)("tar", ["-xzf", tarPath, "-C", destDir], {
1078
1123
  stdio: ["ignore", "inherit", "inherit"]
1079
1124
  });
1080
1125
  child.on("error", reject);
@@ -1085,15 +1130,37 @@ function extractTarball(tarPath, destDir) {
1085
1130
  });
1086
1131
  }
1087
1132
 
1133
+ // src/runtime-delivery.ts
1134
+ var DEFAULT_RUNTIME_CDN_ORIGIN = "https://contrast.dev";
1135
+ var SOOTSIM_RUNTIME_PRODUCT = {
1136
+ id: "sootsim",
1137
+ defaultCdnOrigin: DEFAULT_RUNTIME_CDN_ORIGIN,
1138
+ manifestPath: "/runtimes/manifest.json",
1139
+ tarballPath: (version) => `/runtimes/sootsim-runtime-${version}.tar.gz`,
1140
+ env: {
1141
+ cdnOrigin: "SOOTSIM_CDN_ORIGIN",
1142
+ channel: "SOOTSIM_RUNTIME_CHANNEL",
1143
+ pin: "SOOTSIM_RUNTIME_VERSION"
1144
+ },
1145
+ ensureHome: ensureSootsimHome,
1146
+ configFile: configFilePath,
1147
+ runtimesDir,
1148
+ runtimeDir,
1149
+ cacheDir,
1150
+ readActiveRuntime,
1151
+ writeActiveRuntime
1152
+ };
1153
+ var sootsimRuntime = createRuntimeDelivery(SOOTSIM_RUNTIME_PRODUCT);
1154
+
1088
1155
  // 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);
1156
+ var import_node_fs8 = __toESM(require("node:fs"), 1);
1157
+ var import_node_path7 = __toESM(require("node:path"), 1);
1091
1158
 
1092
1159
  // 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);
1160
+ var import_node_child_process2 = require("node:child_process");
1161
+ var import_node_crypto3 = require("node:crypto");
1162
+ var import_node_fs6 = __toESM(require("node:fs"), 1);
1163
+ var import_node_path5 = __toESM(require("node:path"), 1);
1097
1164
  var import_node_readline = __toESM(require("node:readline"), 1);
1098
1165
 
1099
1166
  // src/agent-events.ts
@@ -1136,9 +1203,9 @@ function encodeAgentPromptEnvelope(input) {
1136
1203
  }
1137
1204
 
1138
1205
  // 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);
1206
+ var import_node_crypto2 = require("node:crypto");
1207
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
1208
+ var import_node_path4 = __toESM(require("node:path"), 1);
1142
1209
  var overrideDir = null;
1143
1210
  function userDataDir() {
1144
1211
  if (overrideDir) return overrideDir;
@@ -1150,7 +1217,7 @@ function getUserDataDir() {
1150
1217
  return userDataDir();
1151
1218
  }
1152
1219
  function storeFile() {
1153
- return import_node_path2.default.join(userDataDir(), "attached-projects.json");
1220
+ return import_node_path4.default.join(userDataDir(), "attached-projects.json");
1154
1221
  }
1155
1222
  function cloneEmpty() {
1156
1223
  return {
@@ -1164,7 +1231,7 @@ function loadStore() {
1164
1231
  const file = storeFile();
1165
1232
  let raw;
1166
1233
  try {
1167
- raw = import_node_fs3.default.readFileSync(file, "utf8");
1234
+ raw = import_node_fs5.default.readFileSync(file, "utf8");
1168
1235
  } catch (err) {
1169
1236
  if (err.code === "ENOENT") return cloneEmpty();
1170
1237
  throw err;
@@ -1181,7 +1248,7 @@ function loadStore() {
1181
1248
  } catch (err) {
1182
1249
  const quarantine = `${file}.corrupt-${Date.now()}`;
1183
1250
  try {
1184
- import_node_fs3.default.renameSync(file, quarantine);
1251
+ import_node_fs5.default.renameSync(file, quarantine);
1185
1252
  console.warn(
1186
1253
  `[sootsim] attached-projects.json was unparseable; quarantined to ${quarantine}. original error: ${err.message}`
1187
1254
  );
@@ -1192,16 +1259,16 @@ function loadStore() {
1192
1259
  }
1193
1260
  function writeStore(store) {
1194
1261
  const file = storeFile();
1195
- import_node_fs3.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
1262
+ import_node_fs5.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
1196
1263
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
1197
- const fd = import_node_fs3.default.openSync(tmp, "w", 384);
1264
+ const fd = import_node_fs5.default.openSync(tmp, "w", 384);
1198
1265
  try {
1199
- import_node_fs3.default.writeFileSync(fd, JSON.stringify(store, null, 2));
1200
- import_node_fs3.default.fsyncSync(fd);
1266
+ import_node_fs5.default.writeFileSync(fd, JSON.stringify(store, null, 2));
1267
+ import_node_fs5.default.fsyncSync(fd);
1201
1268
  } finally {
1202
- import_node_fs3.default.closeSync(fd);
1269
+ import_node_fs5.default.closeSync(fd);
1203
1270
  }
1204
- import_node_fs3.default.renameSync(tmp, file);
1271
+ import_node_fs5.default.renameSync(tmp, file);
1205
1272
  }
1206
1273
  function mutateStore(fn) {
1207
1274
  const store = loadStore();
@@ -1210,13 +1277,13 @@ function mutateStore(fn) {
1210
1277
  return store;
1211
1278
  }
1212
1279
  function projectIdForCwd(cwd) {
1213
- return (0, import_node_crypto.createHash)("sha256").update(import_node_path2.default.resolve(cwd)).digest("hex").slice(0, 16);
1280
+ return (0, import_node_crypto2.createHash)("sha256").update(import_node_path4.default.resolve(cwd)).digest("hex").slice(0, 16);
1214
1281
  }
1215
1282
  function newSessionId() {
1216
- return `s_${(0, import_node_crypto.randomBytes)(10).toString("hex")}`;
1283
+ return `s_${(0, import_node_crypto2.randomBytes)(10).toString("hex")}`;
1217
1284
  }
1218
1285
  function upsertProject(input) {
1219
- const cwd = import_node_path2.default.resolve(input.cwd);
1286
+ const cwd = import_node_path4.default.resolve(input.cwd);
1220
1287
  const id = projectIdForCwd(cwd);
1221
1288
  let result;
1222
1289
  mutateStore((store) => {
@@ -1242,7 +1309,7 @@ function upsertProject(input) {
1242
1309
  const now = Date.now();
1243
1310
  const created = {
1244
1311
  id,
1245
- name: input.name ?? import_node_path2.default.basename(cwd),
1312
+ name: input.name ?? import_node_path4.default.basename(cwd),
1246
1313
  cwd,
1247
1314
  repoRoot: input.repoRoot,
1248
1315
  sourceRoots: input.sourceRoots ?? [cwd],
@@ -1372,8 +1439,8 @@ async function seedFromDemoAppRegistry() {
1372
1439
  if (apps.length === 0) return;
1373
1440
  mutateStore((store) => {
1374
1441
  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);
1442
+ if (!import_node_fs5.default.existsSync(app.dir)) continue;
1443
+ const cwd = import_node_path4.default.resolve(app.dir);
1377
1444
  const id = projectIdForCwd(cwd);
1378
1445
  if (store.attachedProjects.some((p) => p.id === id)) continue;
1379
1446
  const now = Date.now();
@@ -1397,16 +1464,16 @@ async function seedFromDemoAppRegistry() {
1397
1464
 
1398
1465
  // src/agent-sessions.ts
1399
1466
  function sessionDir(sessionId) {
1400
- return import_node_path3.default.join(getUserDataDir(), "sessions", sessionId);
1467
+ return import_node_path5.default.join(getUserDataDir(), "sessions", sessionId);
1401
1468
  }
1402
1469
  function promptFifoPath(sessionId) {
1403
- return import_node_path3.default.join(sessionDir(sessionId), "prompt.in");
1470
+ return import_node_path5.default.join(sessionDir(sessionId), "prompt.in");
1404
1471
  }
1405
1472
  function eventsFifoPath(sessionId) {
1406
- return import_node_path3.default.join(sessionDir(sessionId), "events.out");
1473
+ return import_node_path5.default.join(sessionDir(sessionId), "events.out");
1407
1474
  }
1408
1475
  function transcriptPath(sessionId) {
1409
- return import_node_path3.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
1476
+ return import_node_path5.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
1410
1477
  }
1411
1478
  function pidIsAlive(pid, sessionId) {
1412
1479
  if (!pid) return false;
@@ -1416,7 +1483,7 @@ function pidIsAlive(pid, sessionId) {
1416
1483
  return false;
1417
1484
  }
1418
1485
  if (sessionId) {
1419
- if (!import_node_fs4.default.existsSync(sessionDir(sessionId))) return false;
1486
+ if (!import_node_fs6.default.existsSync(sessionDir(sessionId))) return false;
1420
1487
  }
1421
1488
  return true;
1422
1489
  }
@@ -1428,11 +1495,11 @@ function resolveSootsimInvocation() {
1428
1495
  const resourcesPath = process.resourcesPath;
1429
1496
  if (resourcesPath) {
1430
1497
  const candidates = [
1431
- import_node_path3.default.join(resourcesPath, "bin", "sootsim"),
1432
- import_node_path3.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
1498
+ import_node_path5.default.join(resourcesPath, "bin", "sootsim"),
1499
+ import_node_path5.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
1433
1500
  ];
1434
1501
  for (const c of candidates) {
1435
- if (import_node_fs4.default.existsSync(c)) return { cmd: c, prefixArgs: [] };
1502
+ if (import_node_fs6.default.existsSync(c)) return { cmd: c, prefixArgs: [] };
1436
1503
  }
1437
1504
  }
1438
1505
  }
@@ -1455,15 +1522,15 @@ function tryWorkspaceSootsim() {
1455
1522
  const sootsimDir = resolveSootsimPackageDir();
1456
1523
  if (!sootsimDir) return null;
1457
1524
  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)) {
1525
+ const distBinary = import_node_path5.default.join(sootsimDir, "dist-bin", binaryName);
1526
+ if (import_node_fs6.default.existsSync(distBinary)) return { cmd: distBinary, prefixArgs: [] };
1527
+ const distBin = import_node_path5.default.join(sootsimDir, "dist-cli", "bin.js");
1528
+ if (import_node_fs6.default.existsSync(distBin)) {
1462
1529
  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;
1530
+ const src = import_node_path5.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
1531
+ if (import_node_fs6.default.existsSync(src)) {
1532
+ const srcMtime = import_node_fs6.default.statSync(src).mtimeMs;
1533
+ const buildMtime = import_node_fs6.default.statSync(distBin).mtimeMs;
1467
1534
  if (buildMtime < srcMtime) {
1468
1535
  console.warn(
1469
1536
  `[sootsim] dist-cli/bin.js is older than agent-wrapper.ts \u2014 rebuild with \`bun run --cwd packages/sootsim build:cli\`.`
@@ -1482,22 +1549,22 @@ function tryWorkspaceSootsim() {
1482
1549
  function resolveSootsimPackageDir() {
1483
1550
  try {
1484
1551
  const resolved = require.resolve("sootsim/package.json");
1485
- return import_node_path3.default.dirname(resolved);
1552
+ return import_node_path5.default.dirname(resolved);
1486
1553
  } catch {
1487
1554
  }
1488
1555
  const here = fileFromImportMeta();
1489
1556
  if (!here) return null;
1490
- let cur = import_node_path3.default.dirname(here);
1557
+ let cur = import_node_path5.default.dirname(here);
1491
1558
  for (let i = 0; i < 8; i++) {
1492
- const pkg = import_node_path3.default.join(cur, "package.json");
1559
+ const pkg = import_node_path5.default.join(cur, "package.json");
1493
1560
  try {
1494
- if (import_node_fs4.default.existsSync(pkg)) {
1495
- const parsed = JSON.parse(import_node_fs4.default.readFileSync(pkg, "utf8"));
1561
+ if (import_node_fs6.default.existsSync(pkg)) {
1562
+ const parsed = JSON.parse(import_node_fs6.default.readFileSync(pkg, "utf8"));
1496
1563
  if (parsed.name === "sootsim") return cur;
1497
1564
  }
1498
1565
  } catch {
1499
1566
  }
1500
- const parent = import_node_path3.default.dirname(cur);
1567
+ const parent = import_node_path5.default.dirname(cur);
1501
1568
  if (parent === cur) break;
1502
1569
  cur = parent;
1503
1570
  }
@@ -1513,28 +1580,28 @@ function fileFromImportMeta() {
1513
1580
  }
1514
1581
  }
1515
1582
  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 });
1583
+ const lockDir = import_node_path5.default.join(getUserDataDir(), "locks");
1584
+ import_node_fs6.default.mkdirSync(lockDir, { recursive: true });
1518
1585
  try {
1519
- import_node_fs4.default.chmodSync(lockDir, 448);
1586
+ import_node_fs6.default.chmodSync(lockDir, 448);
1520
1587
  } catch {
1521
1588
  }
1522
- const lockPath = import_node_path3.default.join(lockDir, `start-${projectId}-${provider}.lock`);
1589
+ const lockPath = import_node_path5.default.join(lockDir, `start-${projectId}-${provider}.lock`);
1523
1590
  const deadline = Date.now() + 4e3;
1524
1591
  let fd = null;
1525
1592
  while (fd === null) {
1526
1593
  try {
1527
- fd = import_node_fs4.default.openSync(
1594
+ fd = import_node_fs6.default.openSync(
1528
1595
  lockPath,
1529
- import_node_fs4.constants.O_WRONLY | import_node_fs4.constants.O_CREAT | import_node_fs4.constants.O_EXCL,
1596
+ import_node_fs6.constants.O_WRONLY | import_node_fs6.constants.O_CREAT | import_node_fs6.constants.O_EXCL,
1530
1597
  384
1531
1598
  );
1532
1599
  } catch (err) {
1533
1600
  if (err.code !== "EEXIST") throw err;
1534
1601
  try {
1535
- const stale = Number(import_node_fs4.default.readFileSync(lockPath, "utf8").trim());
1602
+ const stale = Number(import_node_fs6.default.readFileSync(lockPath, "utf8").trim());
1536
1603
  if (stale && !isProcessAlive(stale)) {
1537
- import_node_fs4.default.unlinkSync(lockPath);
1604
+ import_node_fs6.default.unlinkSync(lockPath);
1538
1605
  continue;
1539
1606
  }
1540
1607
  } catch {
@@ -1548,15 +1615,15 @@ async function withStartLock(projectId, provider, fn) {
1548
1615
  }
1549
1616
  }
1550
1617
  try {
1551
- import_node_fs4.default.writeFileSync(fd, String(process.pid));
1618
+ import_node_fs6.default.writeFileSync(fd, String(process.pid));
1552
1619
  return await fn();
1553
1620
  } finally {
1554
1621
  try {
1555
- import_node_fs4.default.closeSync(fd);
1622
+ import_node_fs6.default.closeSync(fd);
1556
1623
  } catch {
1557
1624
  }
1558
1625
  try {
1559
- import_node_fs4.default.unlinkSync(lockPath);
1626
+ import_node_fs6.default.unlinkSync(lockPath);
1560
1627
  } catch {
1561
1628
  }
1562
1629
  }
@@ -1570,28 +1637,28 @@ function isProcessAlive(pid) {
1570
1637
  }
1571
1638
  }
1572
1639
  function mkfifoSync(p) {
1573
- const parent = import_node_path3.default.dirname(p);
1574
- import_node_fs4.default.mkdirSync(parent, { recursive: true });
1640
+ const parent = import_node_path5.default.dirname(p);
1641
+ import_node_fs6.default.mkdirSync(parent, { recursive: true });
1575
1642
  try {
1576
- import_node_fs4.default.chmodSync(parent, 448);
1643
+ import_node_fs6.default.chmodSync(parent, 448);
1577
1644
  } catch {
1578
1645
  }
1579
- if (import_node_fs4.default.existsSync(p)) {
1646
+ if (import_node_fs6.default.existsSync(p)) {
1580
1647
  try {
1581
- const stat = import_node_fs4.default.statSync(p);
1648
+ const stat = import_node_fs6.default.statSync(p);
1582
1649
  if (stat.isFIFO()) {
1583
1650
  try {
1584
- import_node_fs4.default.chmodSync(p, 384);
1651
+ import_node_fs6.default.chmodSync(p, 384);
1585
1652
  } catch {
1586
1653
  }
1587
1654
  return;
1588
1655
  }
1589
- import_node_fs4.default.unlinkSync(p);
1656
+ import_node_fs6.default.unlinkSync(p);
1590
1657
  } catch {
1591
- import_node_fs4.default.unlinkSync(p);
1658
+ import_node_fs6.default.unlinkSync(p);
1592
1659
  }
1593
1660
  }
1594
- const result = (0, import_node_child_process.spawnSync)("mkfifo", ["-m", "600", p]);
1661
+ const result = (0, import_node_child_process2.spawnSync)("mkfifo", ["-m", "600", p]);
1595
1662
  if (result.status !== 0) {
1596
1663
  throw new Error(
1597
1664
  `mkfifo(${p}) failed: ${result.stderr?.toString().trim() || "unknown error"}`
@@ -1621,7 +1688,7 @@ async function startSession(opts) {
1621
1688
  `session already running for project=${project.id} provider=${provider} (session ${existingLive.id}, pid ${existingLive.wrapperPid}). end it first with \`sootsim agent end <sessionId>\`.`
1622
1689
  );
1623
1690
  }
1624
- const claudeSessionUuid = provider === "claude" ? (0, import_node_crypto2.randomUUID)() : void 0;
1691
+ const claudeSessionUuid = provider === "claude" ? (0, import_node_crypto3.randomUUID)() : void 0;
1625
1692
  const session = upsertSession({
1626
1693
  projectId: project.id,
1627
1694
  provider,
@@ -1635,10 +1702,10 @@ async function startSession(opts) {
1635
1702
  const transcript = transcriptPath(session.id);
1636
1703
  mkfifoSync(promptIn);
1637
1704
  mkfifoSync(eventsOut);
1638
- const transcriptDir = import_node_path3.default.dirname(transcript);
1639
- import_node_fs4.default.mkdirSync(transcriptDir, { recursive: true });
1705
+ const transcriptDir = import_node_path5.default.dirname(transcript);
1706
+ import_node_fs6.default.mkdirSync(transcriptDir, { recursive: true });
1640
1707
  try {
1641
- import_node_fs4.default.chmodSync(transcriptDir, 448);
1708
+ import_node_fs6.default.chmodSync(transcriptDir, 448);
1642
1709
  } catch {
1643
1710
  }
1644
1711
  const { cmd, prefixArgs } = resolveSootsimInvocation();
@@ -1666,7 +1733,7 @@ async function startSession(opts) {
1666
1733
  if (claudeSessionUuid) {
1667
1734
  wrapperArgs.push("--claude-session-uuid", claudeSessionUuid);
1668
1735
  }
1669
- const child = (0, import_node_child_process.spawn)(cmd, wrapperArgs, {
1736
+ const child = (0, import_node_child_process2.spawn)(cmd, wrapperArgs, {
1670
1737
  detached: true,
1671
1738
  stdio: "ignore",
1672
1739
  env: {
@@ -1689,7 +1756,7 @@ async function startSession(opts) {
1689
1756
  }
1690
1757
  }
1691
1758
  try {
1692
- import_node_fs4.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
1759
+ import_node_fs6.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
1693
1760
  } catch {
1694
1761
  }
1695
1762
  updateSessionStatus(session.id, { status: "ended" });
@@ -1717,18 +1784,18 @@ async function sendPrompt(sessionId, prompt) {
1717
1784
  );
1718
1785
  }
1719
1786
  const fifo = promptFifoPath(sessionId);
1720
- if (!import_node_fs4.default.existsSync(fifo)) {
1787
+ if (!import_node_fs6.default.existsSync(fifo)) {
1721
1788
  throw new AgentSessionError("NO_FIFO", `prompt FIFO missing: ${fifo}`);
1722
1789
  }
1723
- const fd = import_node_fs4.default.openSync(fifo, import_node_fs4.constants.O_WRONLY);
1790
+ const fd = import_node_fs6.default.openSync(fifo, import_node_fs6.constants.O_WRONLY);
1724
1791
  try {
1725
1792
  const wireText = encodeAgentPromptEnvelope(prompt);
1726
1793
  if (!wireText) {
1727
1794
  throw new AgentSessionError("EMPTY_PROMPT", "prompt text is empty");
1728
1795
  }
1729
- import_node_fs4.default.writeSync(fd, wireText + "\n");
1796
+ import_node_fs6.default.writeSync(fd, wireText + "\n");
1730
1797
  } finally {
1731
- import_node_fs4.default.closeSync(fd);
1798
+ import_node_fs6.default.closeSync(fd);
1732
1799
  }
1733
1800
  updateSessionStatus(sessionId, {
1734
1801
  lastPrompt: prompt.displayText ?? prompt.text,
@@ -1750,7 +1817,7 @@ async function endSession(sessionId) {
1750
1817
  const base = getUserDataDir();
1751
1818
  if (dir.startsWith(base)) {
1752
1819
  try {
1753
- import_node_fs4.default.rmSync(dir, { recursive: true, force: true });
1820
+ import_node_fs6.default.rmSync(dir, { recursive: true, force: true });
1754
1821
  } catch {
1755
1822
  }
1756
1823
  }
@@ -1758,11 +1825,11 @@ async function endSession(sessionId) {
1758
1825
  }
1759
1826
  function subscribeEvents(sessionId, onEvent) {
1760
1827
  const fifo = eventsFifoPath(sessionId);
1761
- if (!import_node_fs4.default.existsSync(fifo)) {
1828
+ if (!import_node_fs6.default.existsSync(fifo)) {
1762
1829
  throw new AgentSessionError("NO_FIFO", `events FIFO missing: ${fifo}`);
1763
1830
  }
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 });
1831
+ const fd = import_node_fs6.default.openSync(fifo, import_node_fs6.constants.O_RDWR);
1832
+ const stream = import_node_fs6.default.createReadStream("", { fd, autoClose: true });
1766
1833
  const rl = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
1767
1834
  rl.on("line", (line) => {
1768
1835
  const event = parseAgentEventLine(line);
@@ -1783,7 +1850,7 @@ function subscribeEvents(sessionId, onEvent) {
1783
1850
  };
1784
1851
  }
1785
1852
  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);
1853
+ const fd = import_node_fs6.default.openSync(fifo, import_node_fs6.constants.O_RDWR | import_node_fs6.constants.O_NONBLOCK);
1787
1854
  const buf = Buffer.alloc(8192);
1788
1855
  let leftover = "";
1789
1856
  const deadline = Date.now() + timeoutMs;
@@ -1791,7 +1858,7 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
1791
1858
  while (Date.now() < deadline) {
1792
1859
  let n = 0;
1793
1860
  try {
1794
- n = import_node_fs4.default.readSync(fd, buf, 0, buf.length, null);
1861
+ n = import_node_fs6.default.readSync(fd, buf, 0, buf.length, null);
1795
1862
  } catch (err) {
1796
1863
  if (err.code !== "EAGAIN") throw err;
1797
1864
  n = 0;
@@ -1811,15 +1878,15 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
1811
1878
  }
1812
1879
  return null;
1813
1880
  } finally {
1814
- import_node_fs4.default.closeSync(fd);
1881
+ import_node_fs6.default.closeSync(fd);
1815
1882
  }
1816
1883
  }
1817
1884
 
1818
1885
  // src/agentbus-prompt.ts
1819
- var import_node_child_process2 = require("node:child_process");
1820
- var import_node_fs5 = __toESM(require("node:fs"), 1);
1886
+ var import_node_child_process3 = require("node:child_process");
1887
+ var import_node_fs7 = __toESM(require("node:fs"), 1);
1821
1888
  var import_node_os2 = __toESM(require("node:os"), 1);
1822
- var import_node_path4 = __toESM(require("node:path"), 1);
1889
+ var import_node_path6 = __toESM(require("node:path"), 1);
1823
1890
 
1824
1891
  // src/agent-identity.ts
1825
1892
  var AGENTBUS_SESSION_RE = /^[A-Za-z0-9._:-]{1,160}$/;
@@ -1842,8 +1909,8 @@ function resolveAgentbusCommand(explicit) {
1842
1909
  if (fromArg) return fromArg;
1843
1910
  const fromEnv = process.env.AGENTBUS_COMMAND?.trim();
1844
1911
  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;
1912
+ const localBin = import_node_path6.default.join(import_node_os2.default.homedir(), ".local", "bin", "agentbus");
1913
+ if (import_node_fs7.default.existsSync(localBin)) return localBin;
1847
1914
  return "agentbus";
1848
1915
  }
1849
1916
  async function sendPromptToAgentbusSession(input) {
@@ -1858,7 +1925,7 @@ async function sendPromptToAgentbusSession(input) {
1858
1925
  const command = resolveAgentbusCommand(input.command);
1859
1926
  const timeoutMs = input.timeoutMs ?? 15e3;
1860
1927
  await new Promise((resolve, reject) => {
1861
- const child = (0, import_node_child_process2.spawn)(command, ["send", sessionId, text], {
1928
+ const child = (0, import_node_child_process3.spawn)(command, ["send", sessionId, text], {
1862
1929
  stdio: ["ignore", "ignore", "pipe"],
1863
1930
  env: process.env
1864
1931
  });
@@ -2084,7 +2151,7 @@ var AgentHost = class {
2084
2151
  );
2085
2152
  const project = upsertProject({
2086
2153
  cwd: match.cwd,
2087
- name: match.projectName ?? import_node_path5.default.basename(match.cwd),
2154
+ name: match.projectName ?? import_node_path7.default.basename(match.cwd),
2088
2155
  preferredProvider: input.provider ?? existing?.preferredProvider,
2089
2156
  sourceRoots: existing?.sourceRoots ?? [match.cwd],
2090
2157
  knownBundleUrls,
@@ -2095,18 +2162,18 @@ var AgentHost = class {
2095
2162
  }
2096
2163
  getTranscript(sessionId) {
2097
2164
  const p = transcriptPath(sessionId);
2098
- if (!import_node_fs6.default.existsSync(p)) {
2165
+ if (!import_node_fs8.default.existsSync(p)) {
2099
2166
  return { error: "transcript not found", code: "NO_TRANSCRIPT" };
2100
2167
  }
2101
- return import_node_fs6.default.readFileSync(p, "utf8");
2168
+ return import_node_fs8.default.readFileSync(p, "utf8");
2102
2169
  }
2103
2170
  getPaths() {
2104
2171
  const dir = getUserDataDir();
2105
2172
  return {
2106
2173
  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")
2174
+ storeFile: import_node_path7.default.join(dir, "attached-projects.json"),
2175
+ sessionsDir: import_node_path7.default.join(dir, "sessions"),
2176
+ transcriptsDir: import_node_path7.default.join(dir, "transcripts")
2110
2177
  };
2111
2178
  }
2112
2179
  // --- subscription management ---
@@ -2691,10 +2758,10 @@ function handleAppApiRequest(req, res) {
2691
2758
  }
2692
2759
 
2693
2760
  // src/host/open-url.ts
2694
- var import_child_process3 = require("child_process");
2695
- var import_fs3 = require("fs");
2761
+ var import_child_process2 = require("child_process");
2762
+ var import_fs2 = require("fs");
2696
2763
  var import_os = require("os");
2697
- var import_path3 = require("path");
2764
+ var import_path2 = require("path");
2698
2765
  var MAC_CHROMIUM_CANDIDATES = [
2699
2766
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
2700
2767
  "~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
@@ -2732,19 +2799,19 @@ var UNIX_CHROMIUM_COMMANDS = [
2732
2799
  ];
2733
2800
  var WINDOWS_CHROMIUM_COMMANDS = ["chrome", "msedge", "brave"];
2734
2801
  function expandHome(candidate) {
2735
- return candidate.startsWith("~/") ? (0, import_path3.join)((0, import_os.homedir)(), candidate.slice(2)) : candidate;
2802
+ return candidate.startsWith("~/") ? (0, import_path2.join)((0, import_os.homedir)(), candidate.slice(2)) : candidate;
2736
2803
  }
2737
2804
  function firstExisting(candidates) {
2738
2805
  for (const candidate of candidates) {
2739
2806
  const expanded = expandHome(candidate);
2740
- if ((0, import_fs3.existsSync)(expanded)) return expanded;
2807
+ if ((0, import_fs2.existsSync)(expanded)) return expanded;
2741
2808
  }
2742
2809
  return null;
2743
2810
  }
2744
2811
  function lookupExecutable(command, platform) {
2745
2812
  try {
2746
2813
  const tool = platform === "win32" ? "where" : "which";
2747
- const out = (0, import_child_process3.execFileSync)(tool, [command], {
2814
+ const out = (0, import_child_process2.execFileSync)(tool, [command], {
2748
2815
  encoding: "utf8",
2749
2816
  timeout: 1500,
2750
2817
  stdio: ["ignore", "pipe", "ignore"]
@@ -2756,7 +2823,7 @@ function lookupExecutable(command, platform) {
2756
2823
  }
2757
2824
  function resolveChromiumBinary(platform = process.platform) {
2758
2825
  const envCandidate = process.env.CHROME_PATH || process.env.CHROMIUM_PATH;
2759
- if (envCandidate && (0, import_fs3.existsSync)(envCandidate)) return envCandidate;
2826
+ if (envCandidate && (0, import_fs2.existsSync)(envCandidate)) return envCandidate;
2760
2827
  const direct = platform === "darwin" ? firstExisting(MAC_CHROMIUM_CANDIDATES) : platform === "win32" ? firstExisting(WINDOWS_CHROMIUM_CANDIDATES) : firstExisting(LINUX_CHROMIUM_CANDIDATES);
2761
2828
  if (direct) return direct;
2762
2829
  const commands = platform === "win32" ? WINDOWS_CHROMIUM_COMMANDS : UNIX_CHROMIUM_COMMANDS;
@@ -2813,7 +2880,7 @@ function buildChromiumUrlCommand(url, options = {}) {
2813
2880
  }
2814
2881
  async function spawnLaunch(command, args, detached) {
2815
2882
  return new Promise((resolve, reject) => {
2816
- const child = (0, import_child_process3.spawn)(command, args, {
2883
+ const child = (0, import_child_process2.spawn)(command, args, {
2817
2884
  detached,
2818
2885
  stdio: "ignore"
2819
2886
  });
@@ -3328,7 +3395,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3328
3395
  return;
3329
3396
  }
3330
3397
  if (msg.type === "runtime:list") {
3331
- const versions = listInstalledRuntimes();
3398
+ const versions = sootsimRuntime.listInstalled();
3332
3399
  const active = this.getActiveRuntime();
3333
3400
  const reply = {
3334
3401
  type: "runtime:list:ok",
@@ -3345,7 +3412,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3345
3412
  }
3346
3413
  if (msg.type === "runtime:use") {
3347
3414
  const version = typeof msg.version === "string" ? msg.version : "";
3348
- const installed = listInstalledRuntimes();
3415
+ const installed = sootsimRuntime.listInstalled();
3349
3416
  if (!installed.includes(version)) {
3350
3417
  try {
3351
3418
  ws.send(
@@ -3874,7 +3941,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3874
3941
  if (reason === "startup") {
3875
3942
  process.stderr.write("sootsim: checking for runtime updates\u2026\n");
3876
3943
  }
3877
- const result = await updateRuntimeToLatest();
3944
+ const result = await sootsimRuntime.updateToLatest();
3878
3945
  if (!result.updated || !result.latestVersion) {
3879
3946
  if (reason === "startup") {
3880
3947
  process.stderr.write(
@@ -4110,34 +4177,34 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4110
4177
  return;
4111
4178
  }
4112
4179
  }
4113
- const resolved = import_path4.default.resolve(baseDir, "." + rel);
4114
- const baseWithSep = baseDir.endsWith(import_path4.default.sep) ? baseDir : baseDir + import_path4.default.sep;
4180
+ const resolved = import_path3.default.resolve(baseDir, "." + rel);
4181
+ const baseWithSep = baseDir.endsWith(import_path3.default.sep) ? baseDir : baseDir + import_path3.default.sep;
4115
4182
  if (!resolved.startsWith(baseWithSep) && resolved !== baseDir) {
4116
4183
  res.writeHead(403);
4117
4184
  res.end("forbidden");
4118
4185
  return;
4119
4186
  }
4120
- import_fs4.default.realpath(resolved, (realErr, realResolved) => {
4187
+ import_fs3.default.realpath(resolved, (realErr, realResolved) => {
4121
4188
  const servePath = realErr ? resolved : realResolved;
4122
- const servePathWithSep = servePath.endsWith(import_path4.default.sep) ? servePath : servePath + import_path4.default.sep;
4189
+ const servePathWithSep = servePath.endsWith(import_path3.default.sep) ? servePath : servePath + import_path3.default.sep;
4123
4190
  if (!realErr) {
4124
4191
  const realBaseWithSep = (() => {
4125
4192
  try {
4126
- const rb = import_fs4.default.realpathSync(baseDir);
4127
- return rb.endsWith(import_path4.default.sep) ? rb : rb + import_path4.default.sep;
4193
+ const rb = import_fs3.default.realpathSync(baseDir);
4194
+ return rb.endsWith(import_path3.default.sep) ? rb : rb + import_path3.default.sep;
4128
4195
  } catch {
4129
4196
  return baseWithSep;
4130
4197
  }
4131
4198
  })();
4132
- if (!servePathWithSep.startsWith(realBaseWithSep) && servePath + import_path4.default.sep !== realBaseWithSep) {
4199
+ if (!servePathWithSep.startsWith(realBaseWithSep) && servePath + import_path3.default.sep !== realBaseWithSep) {
4133
4200
  res.writeHead(403);
4134
4201
  res.end("forbidden");
4135
4202
  return;
4136
4203
  }
4137
4204
  }
4138
- import_fs4.default.stat(servePath, (err, stats) => {
4205
+ import_fs3.default.stat(servePath, (err, stats) => {
4139
4206
  if (err || !stats?.isFile()) {
4140
- const ext2 = import_path4.default.extname(rel).toLowerCase();
4207
+ const ext2 = import_path3.default.extname(rel).toLowerCase();
4141
4208
  if (ext2 && ext2 !== ".html") {
4142
4209
  res.writeHead(404);
4143
4210
  res.end("not found");
@@ -4148,8 +4215,8 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4148
4215
  res.end("not found");
4149
4216
  return;
4150
4217
  }
4151
- const indexPath = import_path4.default.join(baseDir, "index.html");
4152
- import_fs4.default.readFile(indexPath, (err2, data) => {
4218
+ const indexPath = import_path3.default.join(baseDir, "index.html");
4219
+ import_fs3.default.readFile(indexPath, (err2, data) => {
4153
4220
  if (err2) {
4154
4221
  res.writeHead(404);
4155
4222
  res.end("not found");
@@ -4169,7 +4236,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4169
4236
  });
4170
4237
  return;
4171
4238
  }
4172
- const ext = import_path4.default.extname(servePath).toLowerCase();
4239
+ const ext = import_path3.default.extname(servePath).toLowerCase();
4173
4240
  const contentType = HTTP_MIME_TYPES[ext] || "application/octet-stream";
4174
4241
  res.writeHead(200, {
4175
4242
  "Content-Type": contentType,
@@ -4180,7 +4247,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4180
4247
  return;
4181
4248
  }
4182
4249
  if (ext === ".html") {
4183
- import_fs4.default.readFile(servePath, (readErr, data) => {
4250
+ import_fs3.default.readFile(servePath, (readErr, data) => {
4184
4251
  if (readErr) {
4185
4252
  try {
4186
4253
  res.end();
@@ -4194,7 +4261,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4194
4261
  });
4195
4262
  return;
4196
4263
  }
4197
- const stream = import_fs4.default.createReadStream(servePath);
4264
+ const stream = import_fs3.default.createReadStream(servePath);
4198
4265
  stream.pipe(res);
4199
4266
  stream.on("error", () => {
4200
4267
  try {
@@ -4400,7 +4467,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
4400
4467
  const target = `${filePath}${loc}`;
4401
4468
  const trySpawn = (cmd, args) => new Promise((resolve) => {
4402
4469
  try {
4403
- const child = (0, import_child_process4.spawn)(cmd, args, { detached: true, stdio: "ignore" });
4470
+ const child = (0, import_child_process3.spawn)(cmd, args, { detached: true, stdio: "ignore" });
4404
4471
  let settled = false;
4405
4472
  child.on("error", () => {
4406
4473
  if (settled) return;