sootsim 0.1.84 → 0.1.85

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 (144) hide show
  1. package/detox/element-types.ts +36 -0
  2. package/detox/expectations.ts +1 -1
  3. package/detox/index.ts +2 -35
  4. package/dist-cli/bin.js +3 -3
  5. package/dist-cli/chunks/{agent-2CWD6W6P.js → agent-T3DUH5YJ.js} +2 -2
  6. package/dist-cli/chunks/{agent-wrapper-5W3LOX6S.js → agent-wrapper-NSBF4THI.js} +2 -2
  7. package/dist-cli/chunks/{assert-ZOMAMKRT.js → assert-X3F7TRCZ.js} +2 -2
  8. package/dist-cli/chunks/auto-bootstrap-47RN2V5G.js +2 -0
  9. package/dist-cli/chunks/beta-BRCGAF2N.js +2 -0
  10. package/dist-cli/chunks/chunk-36RPD6JI.js +2 -0
  11. package/dist-cli/chunks/{chunk-5XCXOLG2.js → chunk-3WGHC7JN.js} +2 -2
  12. package/dist-cli/chunks/chunk-4DBPNLGI.js +1 -0
  13. package/dist-cli/chunks/{chunk-RF4R2U46.js → chunk-4EVSIUNB.js} +2 -2
  14. package/dist-cli/chunks/{chunk-TK3OJSEO.js → chunk-4QZHZ6BC.js} +2 -2
  15. package/dist-cli/chunks/{chunk-DUUSJDES.js → chunk-5DIGWOY7.js} +1 -1
  16. package/dist-cli/chunks/{chunk-4OWVPRZV.js → chunk-5N3V7OCG.js} +2 -2
  17. package/dist-cli/chunks/{chunk-KU6MSPAH.js → chunk-5S6D7K4L.js} +2 -2
  18. package/dist-cli/chunks/{chunk-QMSJR5R2.js → chunk-7LKUN46F.js} +2 -2
  19. package/dist-cli/chunks/{chunk-SV7FOGJ3.js → chunk-AC6QGW22.js} +2 -2
  20. package/dist-cli/chunks/{chunk-C3DPQZ4J.js → chunk-AFNDVS4E.js} +2 -2
  21. package/dist-cli/chunks/{chunk-PPKKA5VW.js → chunk-BESAZ2HA.js} +2 -2
  22. package/dist-cli/chunks/{chunk-73UZXB4B.js → chunk-BHZJ6RIH.js} +2 -2
  23. package/dist-cli/chunks/{chunk-ELJLF4SG.js → chunk-BZL6D4TV.js} +3 -3
  24. package/dist-cli/chunks/{chunk-YCIA4BHJ.js → chunk-CF2LPRXD.js} +2 -2
  25. package/dist-cli/chunks/{chunk-3HXQ7MJK.js → chunk-DWTLRPEN.js} +2 -2
  26. package/dist-cli/chunks/{chunk-BCBNVJVG.js → chunk-E2QE5FFP.js} +1 -1
  27. package/dist-cli/chunks/{chunk-WNVNU2OW.js → chunk-EBEL6TTJ.js} +2 -2
  28. package/dist-cli/chunks/{chunk-OOOR7NT2.js → chunk-EFM53PZ5.js} +1 -1
  29. package/dist-cli/chunks/{chunk-XQ2OBHBE.js → chunk-EKXK3SWK.js} +2 -2
  30. package/dist-cli/chunks/{chunk-4K7BH2D4.js → chunk-G7CIZ5S3.js} +3 -3
  31. package/dist-cli/chunks/{chunk-AJVTY6KY.js → chunk-GTAD6IUV.js} +1 -1
  32. package/dist-cli/chunks/{chunk-BKBL6K2G.js → chunk-H44IQHKZ.js} +1 -1
  33. package/dist-cli/chunks/{chunk-VH7F45CN.js → chunk-HQDJ5BOF.js} +1 -1
  34. package/dist-cli/chunks/{chunk-7NWNTUJF.js → chunk-KUSQ4NNJ.js} +1 -1
  35. package/dist-cli/chunks/{chunk-P7WDNKOS.js → chunk-MAO7F5PH.js} +3 -3
  36. package/dist-cli/chunks/{chunk-SQX5CAYG.js → chunk-NVTL3JQG.js} +1 -1
  37. package/dist-cli/chunks/{chunk-V2GQ4WXJ.js → chunk-O6N2CEET.js} +2 -2
  38. package/dist-cli/chunks/{chunk-EQCKGC4B.js → chunk-OISHLFON.js} +1 -1
  39. package/dist-cli/chunks/{chunk-SFGUPL2X.js → chunk-OUNLJM56.js} +2 -2
  40. package/dist-cli/chunks/{chunk-AWSQUOAS.js → chunk-OXOARRKR.js} +2 -2
  41. package/dist-cli/chunks/{chunk-SQZAC7C4.js → chunk-PHPXGLME.js} +1 -1
  42. package/dist-cli/chunks/{chunk-PS2G44GT.js → chunk-PQFFUJR6.js} +2 -2
  43. package/dist-cli/chunks/{chunk-TL7SIZ7S.js → chunk-QLJNSOS7.js} +1 -1
  44. package/dist-cli/chunks/chunk-QQAECG5B.js +2 -0
  45. package/dist-cli/chunks/{chunk-4OPRODFA.js → chunk-RZHREO3M.js} +2 -2
  46. package/dist-cli/chunks/{chunk-HYPJW65U.js → chunk-SBGOUA6F.js} +2 -2
  47. package/dist-cli/chunks/chunk-SSCA2AEA.js +1 -0
  48. package/dist-cli/chunks/{chunk-7YHDJLO2.js → chunk-UYRGCJ4N.js} +1 -1
  49. package/dist-cli/chunks/{chunk-D4HUVLZR.js → chunk-WGDL5V6C.js} +1 -1
  50. package/dist-cli/chunks/{chunk-RIXUH3NK.js → chunk-Y5PLPEEU.js} +2 -2
  51. package/dist-cli/chunks/chunk-ZFAM4N5B.js +1 -0
  52. package/dist-cli/chunks/{chunk-EQ7TFQ2F.js → chunk-ZO3VHP6W.js} +1 -1
  53. package/dist-cli/chunks/cli-version-WPFDM2A6.js +2 -0
  54. package/dist-cli/chunks/{compat-FWSEEGEH.js → compat-PCXGGZBZ.js} +3 -3
  55. package/dist-cli/chunks/{config-CYI2WAGP.js → config-LULEVEYL.js} +2 -2
  56. package/dist-cli/chunks/{control-UXY7YQVX.js → control-6P6HY7UF.js} +2 -2
  57. package/dist-cli/chunks/{cpu-profile-IKAE3KTY.js → cpu-profile-NOK73ZYW.js} +2 -2
  58. package/dist-cli/chunks/{daemon-ZUMF53YB.js → daemon-4A3DMUYL.js} +2 -2
  59. package/dist-cli/chunks/{debug-P6KULKKS.js → debug-74BWB2ZG.js} +3 -3
  60. package/dist-cli/chunks/{detox-SPWAZCYG.js → detox-HEOMINSC.js} +2 -2
  61. package/dist-cli/chunks/{device-JWEPK6I2.js → device-TTXXBJFZ.js} +2 -2
  62. package/dist-cli/chunks/{diagnose-IZODTXV2.js → diagnose-QZ3GOHSE.js} +2 -2
  63. package/dist-cli/chunks/drivers-QRPWNOIT.js +2 -0
  64. package/dist-cli/chunks/{electron-R5GP6RVB.js → electron-QVOWV44R.js} +3 -3
  65. package/dist-cli/chunks/flow-QMA7GVN6.js +2 -0
  66. package/dist-cli/chunks/{hints-DYDNYX7N.js → hints-YKWRNMJC.js} +2 -2
  67. package/dist-cli/chunks/{home-paths-GLMX5OKL.js → home-paths-SFADSTJM.js} +2 -2
  68. package/dist-cli/chunks/{inspect-FJOPCTY2.js → inspect-LEWGQCIU.js} +3 -3
  69. package/dist-cli/chunks/install-7N2N7Q32.js +2 -0
  70. package/dist-cli/chunks/{install-desktop-YPJZMZM5.js → install-desktop-22HYQZ2G.js} +3 -3
  71. package/dist-cli/chunks/{keys-GSYPHWNY.js → keys-3ZT3MICU.js} +2 -2
  72. package/dist-cli/chunks/{launch-4G2PKW5X.js → launch-ZXW2NFLG.js} +3 -3
  73. package/dist-cli/chunks/{login-KJQGHA64.js → login-NJKJ7GZO.js} +4 -4
  74. package/dist-cli/chunks/{logout-XM2SYH5C.js → logout-VMMQL7CB.js} +2 -2
  75. package/dist-cli/chunks/{maestro-EOWGI7DG.js → maestro-OJY4MTI7.js} +2 -2
  76. package/dist-cli/chunks/{preview-F73TKK37.js → preview-QU2GXTEV.js} +2 -2
  77. package/dist-cli/chunks/{profile-22FDKBUO.js → profile-7APWK47T.js} +2 -2
  78. package/dist-cli/chunks/{react-5L6VPFUP.js → react-RSVO5JZZ.js} +2 -2
  79. package/dist-cli/chunks/{record-JZXCQ4IN.js → record-UWH4MDEO.js} +2 -2
  80. package/dist-cli/chunks/runtime-3FUENRHM.js +2 -0
  81. package/dist-cli/chunks/{runtime-delivery-LXUM3R4A.js → runtime-delivery-QMKGRV7N.js} +2 -2
  82. package/dist-cli/chunks/{screenshot-HDRRG33Q.js → screenshot-43M27ALE.js} +2 -2
  83. package/dist-cli/chunks/{screenshot-mode-WY63LZIX.js → screenshot-mode-EBYYN6TY.js} +2 -2
  84. package/dist-cli/chunks/{screenshots-MPV2ENL5.js → screenshots-7TQZL6Z6.js} +2 -2
  85. package/dist-cli/chunks/{server-5LBMCJ3G.js → server-VCFM25Z6.js} +2 -2
  86. package/dist-cli/chunks/setup-repo-HFH4VKJQ.js +2 -0
  87. package/dist-cli/chunks/{skills-BQ73YOBF.js → skills-RQA6EJQL.js} +2 -2
  88. package/dist-cli/chunks/{start-2WU4W6ZU.js → start-ZT6MBYND.js} +4 -4
  89. package/dist-cli/chunks/store-BJBTDSZE.js +2 -0
  90. package/dist-cli/chunks/telemetry-ZZZKTILZ.js +2 -0
  91. package/dist-cli/chunks/{test-OVO4CQTG.js → test-RNRX5SWV.js} +3 -3
  92. package/dist-cli/chunks/{three-mode-BKM3KFM7.js → three-mode-TQZH25ZO.js} +2 -2
  93. package/dist-cli/chunks/{timeline-MDXGEDQL.js → timeline-GGN3AY6P.js} +2 -2
  94. package/dist-cli/chunks/{upgrade-JGQABWVF.js → upgrade-XT22D67C.js} +2 -2
  95. package/dist-cli/chunks/upload-NC2AYLC5.js +2 -0
  96. package/dist-cli/chunks/{web-WYFAYQ72.js → web-KEHVF5MB.js} +2 -2
  97. package/dist-cli/chunks/{what-happened-PZW2KW6A.js → what-happened-PATQRJ5T.js} +2 -2
  98. package/dist-cli/chunks/{whoami-7ATWJQS6.js → whoami-CXVY26VV.js} +2 -2
  99. package/dist-lib/agent-daemon-client.cjs +1 -1
  100. package/dist-lib/agent-events.cjs +1 -1
  101. package/dist-lib/agent-sessions.cjs +1 -1
  102. package/dist-lib/attached-projects.cjs +1 -1
  103. package/dist-lib/auth/shared-session.cjs +1 -1
  104. package/dist-lib/backend-origin.cjs +1 -1
  105. package/dist-lib/beta.cjs +1 -1
  106. package/dist-lib/bridge-constants.cjs +1 -1
  107. package/dist-lib/cli-constants.cjs +1 -1
  108. package/dist-lib/config.cjs +1 -1
  109. package/dist-lib/detox/index.cjs +1 -1
  110. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  111. package/dist-lib/home-paths.cjs +1 -1
  112. package/dist-lib/host/bridge-host.cjs +1 -1
  113. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  114. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  115. package/dist-lib/index.cjs +136 -138
  116. package/dist-lib/metro.cjs +31 -26
  117. package/dist-lib/profiles.cjs +1 -1
  118. package/dist-lib/render-mode.cjs +1 -1
  119. package/dist-lib/scripts/demo-app-registry.cjs +1 -1
  120. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  121. package/dist-lib/skills.cjs +9448 -4
  122. package/dist-lib/vite.cjs +129 -39
  123. package/package.json +2 -6
  124. package/src/metro-plugin.ts +9 -77
  125. package/src/runtime-assets.ts +84 -0
  126. package/src/skills/builtin/compat-check.ts +1 -1
  127. package/src/vite-plugin-one.ts +60 -58
  128. package/dist-cli/chunks/auto-bootstrap-NYYSMTIM.js +0 -2
  129. package/dist-cli/chunks/beta-4K2SQACK.js +0 -2
  130. package/dist-cli/chunks/chunk-67ZZ2CM5.js +0 -1
  131. package/dist-cli/chunks/chunk-D3ZSBIIY.js +0 -2
  132. package/dist-cli/chunks/chunk-FUCGLWNN.js +0 -1
  133. package/dist-cli/chunks/chunk-IILJQCZA.js +0 -2
  134. package/dist-cli/chunks/chunk-ZSMMJMPA.js +0 -1
  135. package/dist-cli/chunks/cli-version-QB4VH24H.js +0 -2
  136. package/dist-cli/chunks/drivers-MK6WJKBC.js +0 -2
  137. package/dist-cli/chunks/flow-6O4GEOPJ.js +0 -2
  138. package/dist-cli/chunks/install-A3TUGGHN.js +0 -2
  139. package/dist-cli/chunks/runtime-EEBX7CFV.js +0 -2
  140. package/dist-cli/chunks/setup-repo-SZSYNKNI.js +0 -2
  141. package/dist-cli/chunks/store-RE45SUBF.js +0 -2
  142. package/dist-cli/chunks/telemetry-DG6GJLCP.js +0 -2
  143. package/dist-cli/chunks/upload-UJNUA4ZV.js +0 -2
  144. package/dist-lib/vite-base.cjs +0 -6937
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.85 | (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;
@@ -36,11 +36,45 @@ __export(vite_plugin_one_exports, {
36
36
  sootsimPlugin: () => sootsimPlugin
37
37
  });
38
38
  module.exports = __toCommonJS(vite_plugin_one_exports);
39
+ var import_fs2 = __toESM(require("fs"), 1);
40
+ var import_path2 = __toESM(require("path"), 1);
41
+
42
+ // src/runtime-assets.ts
39
43
  var import_fs = __toESM(require("fs"), 1);
40
44
  var import_path = __toESM(require("path"), 1);
41
- var sootsimRoot = import_path.default.resolve(import_path.default.dirname(new URL(__sootsim_import_meta_url).pathname), "..");
42
- var distDir = import_path.default.join(sootsimRoot, "dist-plugin");
43
- var publicDir = import_path.default.join(sootsimRoot, "public");
45
+
46
+ // src/home-paths.ts
47
+ var import_node_fs = __toESM(require("node:fs"), 1);
48
+ var import_node_os = require("node:os");
49
+ var import_node_path = __toESM(require("node:path"), 1);
50
+ var SOOTSIM_HOME_ENV = "SOOTSIM_HOME";
51
+ var ACTIVE_RUNTIME_FILE = "active";
52
+ function sootsimHomeDir() {
53
+ const override = process.env[SOOTSIM_HOME_ENV];
54
+ if (override && override.length > 0) return import_node_path.default.resolve(override);
55
+ return import_node_path.default.join((0, import_node_os.homedir)(), ".sootsim");
56
+ }
57
+ function runtimesDir() {
58
+ return import_node_path.default.join(sootsimHomeDir(), "runtimes");
59
+ }
60
+ function runtimeDir(version) {
61
+ return import_node_path.default.join(runtimesDir(), version);
62
+ }
63
+ function activeRuntimeFile() {
64
+ return import_node_path.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
65
+ }
66
+ function readActiveRuntime() {
67
+ try {
68
+ const value = import_node_fs.default.readFileSync(activeRuntimeFile(), "utf8").trim();
69
+ return value.length > 0 ? value : null;
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
74
+ var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
75
+
76
+ // src/runtime-assets.ts
77
+ var SOOTSIM_RUNTIME_MISSING_MESSAGE = "[sootsim] no engine runtime installed \u2014 run `sootsim setup-repo` in your project, or `sootsim runtime install`";
44
78
  var MIME_TYPES = {
45
79
  ".js": "application/javascript",
46
80
  ".mjs": "application/javascript",
@@ -48,17 +82,64 @@ var MIME_TYPES = {
48
82
  ".html": "text/html",
49
83
  ".wasm": "application/wasm",
50
84
  ".json": "application/json",
85
+ ".jpg": "image/jpeg",
86
+ ".jpeg": "image/jpeg",
51
87
  ".png": "image/png",
52
88
  ".svg": "image/svg+xml",
89
+ ".webp": "image/webp",
90
+ ".glb": "model/gltf-binary",
53
91
  ".ttf": "font/ttf",
54
92
  ".otf": "font/otf",
55
93
  ".woff": "font/woff",
56
94
  ".woff2": "font/woff2",
57
95
  ".mp3": "audio/mpeg",
58
- ".wav": "audio/wav",
59
- ".jpg": "image/jpeg",
60
- ".webp": "image/webp"
96
+ ".wav": "audio/wav"
61
97
  };
98
+ var ROOT_RUNTIME_PATHS = [
99
+ "/assets/",
100
+ "/engine/",
101
+ "/engine-tenant/",
102
+ "/photos/",
103
+ "/three-mode/",
104
+ "/canvaskit.wasm",
105
+ "/fonts/",
106
+ "/icons/",
107
+ "/sounds/",
108
+ "/spike/",
109
+ "/test-wallpaper.jpg",
110
+ "/preview-sw.js"
111
+ ];
112
+ function resolveActiveRuntimeRoot() {
113
+ const active = readActiveRuntime();
114
+ if (!active) return null;
115
+ const dir = runtimeDir(active);
116
+ if (!import_fs.default.existsSync(import_path.default.join(dir, "index.html"))) return null;
117
+ return dir;
118
+ }
119
+ function isRootRuntimeAssetPath(pathname) {
120
+ return ROOT_RUNTIME_PATHS.some((p) => pathname === p || pathname.startsWith(p));
121
+ }
122
+ function resolveRuntimeFilePath(runtimeRoot, pathname) {
123
+ if (!pathname.startsWith("/")) return null;
124
+ if (pathname.includes("\0") || pathname.includes("\\")) return null;
125
+ for (const segment of pathname.split("/")) {
126
+ if (segment === "..") return null;
127
+ }
128
+ const fullPath = import_path.default.resolve(runtimeRoot, pathname.replace(/^\/+/, ""));
129
+ const rootWithSep = runtimeRoot.endsWith(import_path.default.sep) ? runtimeRoot : runtimeRoot + import_path.default.sep;
130
+ if (!fullPath.startsWith(rootWithSep) && fullPath !== runtimeRoot) return null;
131
+ if (!import_fs.default.existsSync(fullPath) || !import_fs.default.statSync(fullPath).isFile()) return null;
132
+ return fullPath;
133
+ }
134
+ function serveRuntimeFile(res, fullPath) {
135
+ const ext = import_path.default.extname(fullPath);
136
+ res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
137
+ res.setHeader("cache-control", "max-age=31536000,immutable");
138
+ import_fs.default.createReadStream(fullPath).pipe(res);
139
+ }
140
+
141
+ // src/vite-plugin-one.ts
142
+ var sootsimRoot = import_path2.default.resolve(import_path2.default.dirname(new URL(__sootsim_import_meta_url).pathname), "..");
62
143
  function sootsimPlugin(options = {}) {
63
144
  if (options.enabled === false) return [];
64
145
  const prefix = options.prefix || "/__soot";
@@ -85,14 +166,27 @@ function sootsimPlugin(options = {}) {
85
166
  });
86
167
  server.middlewares.use((req, res, next) => {
87
168
  const url = req.url || "";
88
- if (url === prefix || url === prefix + "/" || url.startsWith(prefix + "/?")) {
89
- const htmlPath = import_path.default.join(distDir, "index.html");
90
- if (!import_fs.default.existsSync(htmlPath)) {
169
+ const pathname = url.split("?")[0];
170
+ const runtimeRoot = resolveActiveRuntimeRoot();
171
+ if (!runtimeRoot) {
172
+ if (pathname === prefix || pathname === prefix + "/" || pathname.startsWith(prefix + "/")) {
91
173
  res.statusCode = 500;
92
- res.end("[sootsim] dist-plugin not built. run: bun scripts/build-plugin.ts");
174
+ res.end(SOOTSIM_RUNTIME_MISSING_MESSAGE);
175
+ return;
176
+ }
177
+ next();
178
+ return;
179
+ }
180
+ if (isRootRuntimeAssetPath(pathname)) {
181
+ const fullPath = resolveRuntimeFilePath(runtimeRoot, pathname);
182
+ if (fullPath) {
183
+ serveRuntimeFile(res, fullPath);
93
184
  return;
94
185
  }
95
- let html = import_fs.default.readFileSync(htmlPath, "utf8");
186
+ }
187
+ if (pathname === prefix || pathname === prefix + "/") {
188
+ const htmlPath = import_path2.default.join(runtimeRoot, "index.html");
189
+ let html = import_fs2.default.readFileSync(htmlPath, "utf8");
96
190
  html = html.replace(
97
191
  "</head>",
98
192
  `<script>history.replaceState(null,'','${prefix}/?bundle=${encodeURIComponent(bundleUrl)}')</script></head>`
@@ -101,31 +195,25 @@ function sootsimPlugin(options = {}) {
101
195
  res.end(html);
102
196
  return;
103
197
  }
104
- if (url.startsWith(prefix + "/")) {
105
- const filePath = url.slice(prefix.length).split("?")[0];
106
- const fullPath = import_path.default.join(distDir, filePath);
107
- if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
108
- const ext = import_path.default.extname(fullPath);
109
- res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
110
- res.setHeader("cache-control", "max-age=31536000,immutable");
111
- import_fs.default.createReadStream(fullPath).pipe(res);
112
- return;
113
- }
114
- const publicPath = import_path.default.join(publicDir, filePath);
115
- if (import_fs.default.existsSync(publicPath) && import_fs.default.statSync(publicPath).isFile()) {
116
- const ext = import_path.default.extname(publicPath);
117
- res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
118
- import_fs.default.createReadStream(publicPath).pipe(res);
198
+ if (pathname.startsWith(prefix + "/")) {
199
+ const fullPath = resolveRuntimeFilePath(
200
+ runtimeRoot,
201
+ pathname.slice(prefix.length)
202
+ );
203
+ if (fullPath) {
204
+ serveRuntimeFile(res, fullPath);
119
205
  return;
120
206
  }
121
- }
122
- const staticRoots = ["/canvaskit.wasm", "/fonts/", "/icons/", "/sounds/"];
123
- if (staticRoots.some((p) => url.startsWith(p))) {
124
- const fullPath = import_path.default.join(publicDir, url.split("?")[0]);
125
- if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
126
- const ext = import_path.default.extname(fullPath);
127
- res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
128
- import_fs.default.createReadStream(fullPath).pipe(res);
207
+ const ext = import_path2.default.extname(pathname);
208
+ if (!ext) {
209
+ const htmlPath = import_path2.default.join(runtimeRoot, "index.html");
210
+ let html = import_fs2.default.readFileSync(htmlPath, "utf8");
211
+ html = html.replace(
212
+ "</head>",
213
+ `<script>history.replaceState(null,'','${prefix}/?bundle=${encodeURIComponent(bundleUrl)}')</script></head>`
214
+ );
215
+ res.setHeader("content-type", "text/html");
216
+ res.end(html);
129
217
  return;
130
218
  }
131
219
  }
@@ -134,7 +222,9 @@ function sootsimPlugin(options = {}) {
134
222
  const port = server.config.server.port || 8081;
135
223
  const sootsimUrl = `http://localhost:${port}${prefix}/`;
136
224
  console.log(`[sootsim] serving at ${sootsimUrl}`);
137
- openElectronApp(sootsimUrl);
225
+ if (options.open !== false) {
226
+ openElectronApp(sootsimUrl);
227
+ }
138
228
  }
139
229
  }
140
230
  ];
@@ -150,10 +240,10 @@ async function openElectronApp(sootsimUrl) {
150
240
  }
151
241
  const candidates = [
152
242
  "/Applications/sootsim.app",
153
- import_path.default.join(process.env.HOME || "", "Applications/sootsim.app"),
154
- import_path.default.join(sootsimRoot, "release/mac-arm64/sootsim.app")
243
+ import_path2.default.join(process.env.HOME || "", "Applications/sootsim.app"),
244
+ import_path2.default.join(sootsimRoot, "release/mac-arm64/sootsim.app")
155
245
  ];
156
- let appPath = candidates.find((p) => import_fs.default.existsSync(p));
246
+ let appPath = candidates.find((p) => import_fs2.default.existsSync(p));
157
247
  if (!appPath) {
158
248
  try {
159
249
  const found = execSync(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sootsim",
3
- "version": "0.1.84",
3
+ "version": "0.1.85",
4
4
  "description": "sootsim CLI + vite/metro plugins + skills registry. bridge client for driving the proprietary sootsim-engine over WebSocket.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "MIT",
@@ -25,10 +25,6 @@
25
25
  "source": "./src/vite-plugin-one.ts",
26
26
  "default": "./dist-lib/vite.cjs"
27
27
  },
28
- "./vite-base": {
29
- "source": "./src/vite-plugin.ts",
30
- "default": "./dist-lib/vite-base.cjs"
31
- },
32
28
  "./metro": {
33
29
  "source": "./src/metro-plugin.ts",
34
30
  "default": "./dist-lib/metro.cjs"
@@ -137,7 +133,6 @@
137
133
  "postinstall": "node ./scripts/postinstall.cjs"
138
134
  },
139
135
  "dependencies": {
140
- "@soot/compat": "workspace:*",
141
136
  "ws": "^8.18.0"
142
137
  },
143
138
  "peerDependencies": {
@@ -149,6 +144,7 @@
149
144
  }
150
145
  },
151
146
  "devDependencies": {
147
+ "@soot/compat": "workspace:*",
152
148
  "@soot/sootsim-globals": "workspace:*",
153
149
  "@soot/sootsim-skills": "workspace:*",
154
150
  "@types/ws": "^8.5.13",
@@ -10,77 +10,16 @@
10
10
 
11
11
  import fs from 'fs'
12
12
  import path from 'path'
13
- import { readActiveRuntime, runtimeDir } from './home-paths'
13
+ import {
14
+ isRootRuntimeAssetPath,
15
+ resolveActiveRuntimeRoot,
16
+ resolveRuntimeFilePath,
17
+ serveRuntimeFile,
18
+ SOOTSIM_RUNTIME_MISSING_MESSAGE,
19
+ } from './runtime-assets'
14
20
 
15
21
  const prefix = '/__soot'
16
22
 
17
- function resolveRuntimeRoot(): string | null {
18
- const active = readActiveRuntime()
19
- if (!active) return null
20
- const dir = runtimeDir(active)
21
- if (!fs.existsSync(path.join(dir, 'index.html'))) return null
22
- return dir
23
- }
24
-
25
- const RUNTIME_MISSING_MESSAGE =
26
- '[sootsim] no engine runtime installed — run `sootsim setup-repo` in your project, or `sootsim runtime install`'
27
-
28
- const MIME_TYPES: Record<string, string> = {
29
- '.js': 'application/javascript',
30
- '.css': 'text/css',
31
- '.html': 'text/html',
32
- '.wasm': 'application/wasm',
33
- '.json': 'application/json',
34
- '.jpg': 'image/jpeg',
35
- '.jpeg': 'image/jpeg',
36
- '.png': 'image/png',
37
- '.svg': 'image/svg+xml',
38
- '.webp': 'image/webp',
39
- '.glb': 'model/gltf-binary',
40
- '.ttf': 'font/ttf',
41
- '.otf': 'font/otf',
42
- '.mp3': 'audio/mpeg',
43
- '.wav': 'audio/wav',
44
- }
45
-
46
- const ROOT_RUNTIME_PATHS = [
47
- '/assets/',
48
- '/engine/',
49
- '/engine-tenant/',
50
- '/photos/',
51
- '/three-mode/',
52
- '/canvaskit.wasm',
53
- '/fonts/',
54
- '/icons/',
55
- '/sounds/',
56
- '/spike/',
57
- '/test-wallpaper.jpg',
58
- '/preview-sw.js',
59
- ]
60
-
61
- export function isRootRuntimeAssetPath(pathname: string): boolean {
62
- return ROOT_RUNTIME_PATHS.some((p) => pathname === p || pathname.startsWith(p))
63
- }
64
-
65
- export function resolveRuntimeFilePath(
66
- runtimeRoot: string,
67
- pathname: string,
68
- ): string | null {
69
- if (!pathname.startsWith('/')) return null
70
- if (pathname.includes('\0') || pathname.includes('\\')) return null
71
- for (const segment of pathname.split('/')) {
72
- if (segment === '..') return null
73
- }
74
-
75
- const fullPath = path.resolve(runtimeRoot, pathname.replace(/^\/+/, ''))
76
- const rootWithSep = runtimeRoot.endsWith(path.sep)
77
- ? runtimeRoot
78
- : runtimeRoot + path.sep
79
- if (!fullPath.startsWith(rootWithSep) && fullPath !== runtimeRoot) return null
80
- if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) return null
81
- return fullPath
82
- }
83
-
84
23
  export interface SootsimMetroOptions {
85
24
  bundleUrl?: string
86
25
  enabled?: boolean
@@ -128,12 +67,12 @@ export function withSootsim<T extends Record<string, unknown>>(
128
67
  server.use((req: any, res: any, next: any) => {
129
68
  const url = req.url || ''
130
69
  const pathname = url.split('?')[0]
131
- const runtimeRoot = resolveRuntimeRoot()
70
+ const runtimeRoot = resolveActiveRuntimeRoot()
132
71
 
133
72
  if (!runtimeRoot) {
134
73
  if (pathname === prefix || pathname === prefix + '/') {
135
74
  res.statusCode = 500
136
- res.end(RUNTIME_MISSING_MESSAGE)
75
+ res.end(SOOTSIM_RUNTIME_MISSING_MESSAGE)
137
76
  return
138
77
  }
139
78
  next()
@@ -198,10 +137,3 @@ export function withSootsim<T extends Record<string, unknown>>(
198
137
  }
199
138
 
200
139
  export default withSootsim
201
-
202
- function serveRuntimeFile(res: any, fullPath: string) {
203
- const ext = path.extname(fullPath)
204
- res.setHeader('content-type', MIME_TYPES[ext] || 'application/octet-stream')
205
- res.setHeader('cache-control', 'max-age=31536000,immutable')
206
- fs.createReadStream(fullPath).pipe(res)
207
- }
@@ -0,0 +1,84 @@
1
+ // helpers for serving the installed sootsim engine runtime from framework
2
+ // dev servers. the runtime is managed by `sootsim runtime install` and lives
3
+ // under ~/.sootsim/runtimes/<version>.
4
+
5
+ import fs from 'fs'
6
+ import path from 'path'
7
+ import { readActiveRuntime, runtimeDir } from './home-paths'
8
+
9
+ export const SOOTSIM_RUNTIME_MISSING_MESSAGE =
10
+ '[sootsim] no engine runtime installed — run `sootsim setup-repo` in your project, or `sootsim runtime install`'
11
+
12
+ const MIME_TYPES: Record<string, string> = {
13
+ '.js': 'application/javascript',
14
+ '.mjs': 'application/javascript',
15
+ '.css': 'text/css',
16
+ '.html': 'text/html',
17
+ '.wasm': 'application/wasm',
18
+ '.json': 'application/json',
19
+ '.jpg': 'image/jpeg',
20
+ '.jpeg': 'image/jpeg',
21
+ '.png': 'image/png',
22
+ '.svg': 'image/svg+xml',
23
+ '.webp': 'image/webp',
24
+ '.glb': 'model/gltf-binary',
25
+ '.ttf': 'font/ttf',
26
+ '.otf': 'font/otf',
27
+ '.woff': 'font/woff',
28
+ '.woff2': 'font/woff2',
29
+ '.mp3': 'audio/mpeg',
30
+ '.wav': 'audio/wav',
31
+ }
32
+
33
+ const ROOT_RUNTIME_PATHS = [
34
+ '/assets/',
35
+ '/engine/',
36
+ '/engine-tenant/',
37
+ '/photos/',
38
+ '/three-mode/',
39
+ '/canvaskit.wasm',
40
+ '/fonts/',
41
+ '/icons/',
42
+ '/sounds/',
43
+ '/spike/',
44
+ '/test-wallpaper.jpg',
45
+ '/preview-sw.js',
46
+ ]
47
+
48
+ export function resolveActiveRuntimeRoot(): string | null {
49
+ const active = readActiveRuntime()
50
+ if (!active) return null
51
+ const dir = runtimeDir(active)
52
+ if (!fs.existsSync(path.join(dir, 'index.html'))) return null
53
+ return dir
54
+ }
55
+
56
+ export function isRootRuntimeAssetPath(pathname: string): boolean {
57
+ return ROOT_RUNTIME_PATHS.some((p) => pathname === p || pathname.startsWith(p))
58
+ }
59
+
60
+ export function resolveRuntimeFilePath(
61
+ runtimeRoot: string,
62
+ pathname: string,
63
+ ): string | null {
64
+ if (!pathname.startsWith('/')) return null
65
+ if (pathname.includes('\0') || pathname.includes('\\')) return null
66
+ for (const segment of pathname.split('/')) {
67
+ if (segment === '..') return null
68
+ }
69
+
70
+ const fullPath = path.resolve(runtimeRoot, pathname.replace(/^\/+/, ''))
71
+ const rootWithSep = runtimeRoot.endsWith(path.sep)
72
+ ? runtimeRoot
73
+ : runtimeRoot + path.sep
74
+ if (!fullPath.startsWith(rootWithSep) && fullPath !== runtimeRoot) return null
75
+ if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) return null
76
+ return fullPath
77
+ }
78
+
79
+ export function serveRuntimeFile(res: any, fullPath: string) {
80
+ const ext = path.extname(fullPath)
81
+ res.setHeader('content-type', MIME_TYPES[ext] || 'application/octet-stream')
82
+ res.setHeader('cache-control', 'max-age=31536000,immutable')
83
+ fs.createReadStream(fullPath).pipe(res)
84
+ }
@@ -31,7 +31,7 @@ export const skill: SootSimSkill = {
31
31
  const fs = await import('fs')
32
32
  const path = await import('path')
33
33
 
34
- const { POLYFILL_REGISTRY } = await import('@soot/compat/web')
34
+ const { POLYFILL_REGISTRY } = await import('../../../../compat/src/web.ts')
35
35
 
36
36
  if (params.packageName) {
37
37
  const entry = POLYFILL_REGISTRY[params.packageName]
@@ -1,5 +1,5 @@
1
- // sootsim plugin for One/Vite — serves pre-built sootsim at /__soot/
2
- // no runtime vite server — just static files from dist-plugin/
1
+ // sootsim plugin for One/Vite — serves the installed sootsim runtime at /__soot/
2
+ // from ~/.sootsim/runtimes/<version>.
3
3
  //
4
4
  // usage in vite.config.ts:
5
5
  // import { sootsimPlugin } from 'sootsim/vite'
@@ -7,11 +7,16 @@
7
7
 
8
8
  import fs from 'fs'
9
9
  import path from 'path'
10
+ import {
11
+ isRootRuntimeAssetPath,
12
+ resolveActiveRuntimeRoot,
13
+ resolveRuntimeFilePath,
14
+ serveRuntimeFile,
15
+ SOOTSIM_RUNTIME_MISSING_MESSAGE,
16
+ } from './runtime-assets'
10
17
  import type { Plugin } from 'vite'
11
18
 
12
19
  const sootsimRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..')
13
- const distDir = path.join(sootsimRoot, 'dist-plugin')
14
- const publicDir = path.join(sootsimRoot, 'public')
15
20
 
16
21
  export interface SootPluginOptions {
17
22
  // custom bundle URL (default: auto-detect from One's metro)
@@ -20,25 +25,8 @@ export interface SootPluginOptions {
20
25
  prefix?: string
21
26
  // disable sootsim
22
27
  enabled?: boolean
23
- }
24
-
25
- const MIME_TYPES: Record<string, string> = {
26
- '.js': 'application/javascript',
27
- '.mjs': 'application/javascript',
28
- '.css': 'text/css',
29
- '.html': 'text/html',
30
- '.wasm': 'application/wasm',
31
- '.json': 'application/json',
32
- '.png': 'image/png',
33
- '.svg': 'image/svg+xml',
34
- '.ttf': 'font/ttf',
35
- '.otf': 'font/otf',
36
- '.woff': 'font/woff',
37
- '.woff2': 'font/woff2',
38
- '.mp3': 'audio/mpeg',
39
- '.wav': 'audio/wav',
40
- '.jpg': 'image/jpeg',
41
- '.webp': 'image/webp',
28
+ // open the desktop app after the dev server starts (default true)
29
+ open?: boolean
42
30
  }
43
31
 
44
32
  export function sootsimPlugin(options: SootPluginOptions = {}): Plugin[] {
@@ -77,15 +65,36 @@ export function sootsimPlugin(options: SootPluginOptions = {}): Plugin[] {
77
65
 
78
66
  server.middlewares.use((req, res, next) => {
79
67
  const url = req.url || ''
80
-
81
- // serve the sootsim HTML shell — inject bundle URL
82
- if (url === prefix || url === prefix + '/' || url.startsWith(prefix + '/?')) {
83
- const htmlPath = path.join(distDir, 'index.html')
84
- if (!fs.existsSync(htmlPath)) {
68
+ const pathname = url.split('?')[0]
69
+ const runtimeRoot = resolveActiveRuntimeRoot()
70
+
71
+ if (!runtimeRoot) {
72
+ if (
73
+ pathname === prefix ||
74
+ pathname === prefix + '/' ||
75
+ pathname.startsWith(prefix + '/')
76
+ ) {
85
77
  res.statusCode = 500
86
- res.end('[sootsim] dist-plugin not built. run: bun scripts/build-plugin.ts')
78
+ res.end(SOOTSIM_RUNTIME_MISSING_MESSAGE)
79
+ return
80
+ }
81
+ next()
82
+ return
83
+ }
84
+
85
+ // root-relative runtime assets emitted by the shell and engine vite
86
+ // builds. only intercept when the asset exists in the active runtime.
87
+ if (isRootRuntimeAssetPath(pathname)) {
88
+ const fullPath = resolveRuntimeFilePath(runtimeRoot, pathname)
89
+ if (fullPath) {
90
+ serveRuntimeFile(res, fullPath)
87
91
  return
88
92
  }
93
+ }
94
+
95
+ // serve the sootsim HTML shell — inject bundle URL
96
+ if (pathname === prefix || pathname === prefix + '/') {
97
+ const htmlPath = path.join(runtimeRoot, 'index.html')
89
98
  let html = fs.readFileSync(htmlPath, 'utf8')
90
99
  // inject bundle URL as a query param so main.tsx picks it up
91
100
  html = html.replace(
@@ -97,37 +106,28 @@ export function sootsimPlugin(options: SootPluginOptions = {}): Plugin[] {
97
106
  return
98
107
  }
99
108
 
100
- // serve static files from dist-plugin/
101
- if (url.startsWith(prefix + '/')) {
102
- const filePath = url.slice(prefix.length).split('?')[0]
103
- const fullPath = path.join(distDir, filePath)
104
-
105
- if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
106
- const ext = path.extname(fullPath)
107
- res.setHeader('content-type', MIME_TYPES[ext] || 'application/octet-stream')
108
- res.setHeader('cache-control', 'max-age=31536000,immutable')
109
- fs.createReadStream(fullPath).pipe(res)
110
- return
111
- }
112
-
113
- // try public/ for wasm, fonts, icons, sounds
114
- const publicPath = path.join(publicDir, filePath)
115
- if (fs.existsSync(publicPath) && fs.statSync(publicPath).isFile()) {
116
- const ext = path.extname(publicPath)
117
- res.setHeader('content-type', MIME_TYPES[ext] || 'application/octet-stream')
118
- fs.createReadStream(publicPath).pipe(res)
109
+ // serve runtime files under /__soot/*.
110
+ if (pathname.startsWith(prefix + '/')) {
111
+ const fullPath = resolveRuntimeFilePath(
112
+ runtimeRoot,
113
+ pathname.slice(prefix.length),
114
+ )
115
+ if (fullPath) {
116
+ serveRuntimeFile(res, fullPath)
119
117
  return
120
118
  }
121
- }
122
119
 
123
- // also serve canvaskit.wasm etc. from public/ at root (some code references /canvaskit.wasm)
124
- const staticRoots = ['/canvaskit.wasm', '/fonts/', '/icons/', '/sounds/']
125
- if (staticRoots.some((p) => url.startsWith(p))) {
126
- const fullPath = path.join(publicDir, url.split('?')[0])
127
- if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
128
- const ext = path.extname(fullPath)
129
- res.setHeader('content-type', MIME_TYPES[ext] || 'application/octet-stream')
130
- fs.createReadStream(fullPath).pipe(res)
120
+ // extensionless in-shell routes should still load the shell.
121
+ const ext = path.extname(pathname)
122
+ if (!ext) {
123
+ const htmlPath = path.join(runtimeRoot, 'index.html')
124
+ let html = fs.readFileSync(htmlPath, 'utf8')
125
+ html = html.replace(
126
+ '</head>',
127
+ `<script>history.replaceState(null,'','${prefix}/?bundle=${encodeURIComponent(bundleUrl)}')</script></head>`,
128
+ )
129
+ res.setHeader('content-type', 'text/html')
130
+ res.end(html)
131
131
  return
132
132
  }
133
133
  }
@@ -140,7 +140,9 @@ export function sootsimPlugin(options: SootPluginOptions = {}): Plugin[] {
140
140
  const sootsimUrl = `http://localhost:${port}${prefix}/`
141
141
  console.log(`[sootsim] serving at ${sootsimUrl}`)
142
142
 
143
- openElectronApp(sootsimUrl)
143
+ if (options.open !== false) {
144
+ openElectronApp(sootsimUrl)
145
+ }
144
146
  },
145
147
  },
146
148
  ]
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d}from"./chunk-4K7BH2D4.js";import"./chunk-XQ2OBHBE.js";import"./chunk-EQCKGC4B.js";import"./chunk-IILJQCZA.js";import"./chunk-D4HUVLZR.js";import"./chunk-BKBL6K2G.js";export{c as ensureDaemonRunning,a as ensureRuntimeInstalled,d as ensureSootsimReady,b as resolveBootstrapPort};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d,e}from"./chunk-SQX5CAYG.js";import"./chunk-BKBL6K2G.js";export{e as BETA_ASK_HEADLINE,c as BETA_LABEL,d as BETA_TAGLINE,b as BETA_VERSION_TARGET,a as IS_BETA};
@@ -1 +0,0 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- var t="http://localhost:5173/";export{t as a};
@@ -1 +0,0 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- var O="sootsim close";export{O as a};
@@ -1 +0,0 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a}from"./chunk-TL7SIZ7S.js";import"./chunk-BKBL6K2G.js";export{a as getCliVersion};