sootsim 0.1.302 → 0.1.304

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 (159) hide show
  1. package/dist-cli/bin.js +5 -5
  2. package/dist-cli/chunks/{agent-QM5DJK6B.js → agent-EK355OQM.js} +2 -2
  3. package/dist-cli/chunks/{agent-wrapper-HDOQKZ2C.js → agent-wrapper-LCJJWHZE.js} +2 -2
  4. package/dist-cli/chunks/app-fonts-LIPU2DLT.js +4 -0
  5. package/dist-cli/chunks/{assert-URPY7JOQ.js → assert-OASKCPBX.js} +2 -2
  6. package/dist-cli/chunks/{auth-LDIGTG4T.js → auth-XYYKD33F.js} +2 -2
  7. package/dist-cli/chunks/{beta-7X7SMIUJ.js → beta-TOL55XPM.js} +2 -2
  8. package/dist-cli/chunks/{camera-44FBN2AD.js → camera-PHKVEQHR.js} +2 -2
  9. package/dist-cli/chunks/{chunk-47FKMZKZ.js → chunk-2MEUZOSD.js} +1 -1
  10. package/dist-cli/chunks/{chunk-N53HDXGW.js → chunk-2RLFUM3C.js} +8 -8
  11. package/dist-cli/chunks/{chunk-YY54A626.js → chunk-3UUHIHA6.js} +2 -2
  12. package/dist-cli/chunks/{chunk-QNA5RNYZ.js → chunk-5NJTX5OP.js} +2 -2
  13. package/dist-cli/chunks/{chunk-YY27LKQF.js → chunk-76OLOD2G.js} +2 -2
  14. package/dist-cli/chunks/{chunk-UEFW7TPW.js → chunk-7RFXZXKH.js} +1 -1
  15. package/dist-cli/chunks/{chunk-2BQCOQBK.js → chunk-A4UYZTV7.js} +2 -2
  16. package/dist-cli/chunks/{chunk-T3UJXMFJ.js → chunk-ABHNOM2C.js} +1 -1
  17. package/dist-cli/chunks/{chunk-PDB5OX3V.js → chunk-AJYA6R3N.js} +2 -2
  18. package/dist-cli/chunks/{chunk-TWOBAP7S.js → chunk-BJOT2LL6.js} +2 -2
  19. package/dist-cli/chunks/{chunk-UNDGU5PA.js → chunk-BPRMA4IC.js} +2 -2
  20. package/dist-cli/chunks/{chunk-QN5DSQNG.js → chunk-CMYVGJBQ.js} +1 -1
  21. package/dist-cli/chunks/{chunk-ZORZTTQL.js → chunk-ENXLKDQ6.js} +3 -3
  22. package/dist-cli/chunks/{chunk-WQWYWERD.js → chunk-EXW6TH3O.js} +2 -2
  23. package/dist-cli/chunks/{chunk-QOS3GSOM.js → chunk-FNI75AD7.js} +1 -1
  24. package/dist-cli/chunks/{chunk-KNBZZC7R.js → chunk-G4R7CPCY.js} +2 -2
  25. package/dist-cli/chunks/{chunk-XLAZCK5H.js → chunk-GF3LDVWF.js} +2 -2
  26. package/dist-cli/chunks/{chunk-ZADSXYX7.js → chunk-GMILMS7P.js} +2 -2
  27. package/dist-cli/chunks/{chunk-IJKBAKAA.js → chunk-HFA5ZACE.js} +21 -10
  28. package/dist-cli/chunks/{chunk-XRRW4YFF.js → chunk-HFKQUS4I.js} +3 -3
  29. package/dist-cli/chunks/{chunk-SMBMOSXY.js → chunk-INBBOCU4.js} +2 -2
  30. package/dist-cli/chunks/chunk-ITDTSNN2.js +4 -0
  31. package/dist-cli/chunks/{chunk-ZQLQVJUB.js → chunk-IWRRIQDJ.js} +2 -2
  32. package/dist-cli/chunks/{chunk-7F57RUPB.js → chunk-JDKWIB6V.js} +1 -1
  33. package/dist-cli/chunks/{chunk-XKUUILJ2.js → chunk-LRXSLMX4.js} +1 -1
  34. package/dist-cli/chunks/{chunk-H7J7U34H.js → chunk-M3ETYVNL.js} +1 -1
  35. package/dist-cli/chunks/{chunk-2AD7UVSQ.js → chunk-MSYPDBFG.js} +2 -2
  36. package/dist-cli/chunks/{chunk-LLCCT4VC.js → chunk-NXYKNKNL.js} +2 -2
  37. package/dist-cli/chunks/{chunk-NFC3BJMX.js → chunk-OSH6526H.js} +1 -1
  38. package/dist-cli/chunks/{chunk-HFHHYL3K.js → chunk-QWQHSUZC.js} +2 -2
  39. package/dist-cli/chunks/chunk-RJWQ3VAD.js +14 -0
  40. package/dist-cli/chunks/{chunk-VEOPRCNX.js → chunk-RQJDIDJ3.js} +1 -1
  41. package/dist-cli/chunks/{chunk-FNJ2IEDC.js → chunk-RT7TUHH7.js} +2 -2
  42. package/dist-cli/chunks/{chunk-W6XC5TUY.js → chunk-RYQUO6LT.js} +1 -1
  43. package/dist-cli/chunks/{chunk-N7ATA44P.js → chunk-SN6ARNCL.js} +2 -2
  44. package/dist-cli/chunks/{chunk-BJDIJYWM.js → chunk-SVFN66PF.js} +2 -2
  45. package/dist-cli/chunks/{chunk-5CWTAN4D.js → chunk-UB3OWUVK.js} +1 -1
  46. package/dist-cli/chunks/{chunk-D4BPRTTD.js → chunk-V6JDDX4B.js} +2 -2
  47. package/dist-cli/chunks/{chunk-RZLCDDE2.js → chunk-VUR5QXVX.js} +2 -2
  48. package/dist-cli/chunks/{chunk-BVUQ2PVZ.js → chunk-W3CSYOSQ.js} +1 -1
  49. package/dist-cli/chunks/{chunk-7UIDSSWQ.js → chunk-WNDYTKLC.js} +1 -1
  50. package/dist-cli/chunks/{chunk-2ELPPBLQ.js → chunk-XCVSB47R.js} +2 -2
  51. package/dist-cli/chunks/{chunk-C36FJ7OB.js → chunk-XHYSASAX.js} +1 -1
  52. package/dist-cli/chunks/{chunk-HNYGEEPF.js → chunk-XXS4TCOO.js} +3 -3
  53. package/dist-cli/chunks/{chunk-OOP74SPV.js → chunk-Y5YM7XHU.js} +1 -1
  54. package/dist-cli/chunks/{chunk-UJ7P2CWC.js → chunk-YAV6BH3A.js} +2 -2
  55. package/dist-cli/chunks/{chunk-AHPEOPNQ.js → chunk-YXHO2OUL.js} +1 -1
  56. package/dist-cli/chunks/chunk-ZDRI2LUG.js +125 -0
  57. package/dist-cli/chunks/{chunk-REJQMXOG.js → chunk-ZONISYZ5.js} +1 -1
  58. package/dist-cli/chunks/{cleanup-QTLASIO7.js → cleanup-GCTEGJMP.js} +2 -2
  59. package/dist-cli/chunks/{cli-version-EUG2MGT7.js → cli-version-G72HUH4E.js} +2 -2
  60. package/dist-cli/chunks/{compat-DA2Z6HOU.js → compat-IC4JHGBH.js} +2 -2
  61. package/dist-cli/chunks/{config-U65JAS2W.js → config-C65MZFU3.js} +2 -2
  62. package/dist-cli/chunks/{control-ZCFRXISU.js → control-RWLYBKQR.js} +2 -2
  63. package/dist-cli/chunks/daemon-D77E5G6Z.js +4 -0
  64. package/dist-cli/chunks/{debug-OXP67EDF.js → debug-LEL32PDN.js} +3 -3
  65. package/dist-cli/chunks/{desktop-ZEQXNFI2.js → desktop-RDSISJH2.js} +3 -3
  66. package/dist-cli/chunks/{detox-M4JBLH5P.js → detox-SCCWUIKK.js} +2 -2
  67. package/dist-cli/chunks/{device-ISL5YP5C.js → device-MTDEKDUK.js} +2 -2
  68. package/dist-cli/chunks/{diagnose-H2TRXSVS.js → diagnose-UZJCKLPZ.js} +2 -2
  69. package/dist-cli/chunks/disk-cleanup-HLWRVTVW.js +4 -0
  70. package/dist-cli/chunks/drivers-Q7GMKTBM.js +4 -0
  71. package/dist-cli/chunks/{film-LKYPZOJD.js → film-YBE544PG.js} +3 -3
  72. package/dist-cli/chunks/flow-5VOVAVZN.js +4 -0
  73. package/dist-cli/chunks/help-2PESJ7V2.js +4 -0
  74. package/dist-cli/chunks/{hidden-runtime-alias-JMWSVMJD.js → hidden-runtime-alias-KJYCJLSR.js} +2 -2
  75. package/dist-cli/chunks/{home-paths-S66AEW3L.js → home-paths-NSHWNEGK.js} +2 -2
  76. package/dist-cli/chunks/inspect-OIA3Q5MD.js +4 -0
  77. package/dist-cli/chunks/install-desktop-7KLACQUW.js +4 -0
  78. package/dist-cli/chunks/{login-EJEEYZGB.js → login-AEVUU72R.js} +4 -4
  79. package/dist-cli/chunks/{logout-QCEW2MME.js → logout-72XHF6J2.js} +2 -2
  80. package/dist-cli/chunks/{maestro-4IKBKIUF.js → maestro-OXSKQJ6B.js} +3 -3
  81. package/dist-cli/chunks/{maestro-generate-WSYSKMND.js → maestro-generate-AKZFORVJ.js} +3 -3
  82. package/dist-cli/chunks/{mode-KQ5VDMPP.js → mode-U6KRG2PE.js} +2 -2
  83. package/dist-cli/chunks/{optional-demo-registry-MMHEMCBJ.js → optional-demo-registry-L3VIWBI2.js} +2 -2
  84. package/dist-cli/chunks/{perf-LGZDBRUM.js → perf-KF2BJZIN.js} +2 -2
  85. package/dist-cli/chunks/{permissions-DA4JXY2N.js → permissions-TRJG4QAI.js} +2 -2
  86. package/dist-cli/chunks/{record-TPU2TOEU.js → record-TICJYZOA.js} +3 -3
  87. package/dist-cli/chunks/{report-issue-PPUNSEEQ.js → report-issue-OQPLBQMJ.js} +2 -2
  88. package/dist-cli/chunks/runtime-HDMLWNXW.js +4 -0
  89. package/dist-cli/chunks/screenshot-command-XLERKHVW.js +159 -0
  90. package/dist-cli/chunks/{screenshots-capture-AG2TIYLG.js → screenshots-capture-B6OORSD4.js} +2 -2
  91. package/dist-cli/chunks/{serve-C3WLV2OX.js → serve-5M7MIPWF.js} +2 -2
  92. package/dist-cli/chunks/{setup-4KZKLDZV.js → setup-HJZDC4PK.js} +2 -2
  93. package/dist-cli/chunks/{skills-65XCNSNM.js → skills-RXHLMGGL.js} +2 -2
  94. package/dist-cli/chunks/{storage-ADWG4UT7.js → storage-MMPD2XBS.js} +2 -2
  95. package/dist-cli/chunks/store-APKY72ZY.js +4 -0
  96. package/dist-cli/chunks/telemetry-W3YRHE67.js +4 -0
  97. package/dist-cli/chunks/{timeline-BRHYLQTW.js → timeline-6PF5BZ37.js} +2 -2
  98. package/dist-cli/chunks/{upgrade-AEJNAP6O.js → upgrade-QSMIK3RC.js} +2 -2
  99. package/dist-cli/chunks/upload-HQEOYILQ.js +4 -0
  100. package/dist-cli/chunks/{version-HZWBK4KJ.js → version-WLTFXEDC.js} +2 -2
  101. package/dist-cli/chunks/{web-LCSSJCZ4.js → web-DI73LQ7N.js} +2 -2
  102. package/dist-cli/chunks/{what-happened-GWA24LHG.js → what-happened-4SAMGZWR.js} +2 -2
  103. package/dist-lib/agent-daemon-client.cjs +1 -1
  104. package/dist-lib/agent-events.cjs +1 -1
  105. package/dist-lib/agent-identity.cjs +1 -1
  106. package/dist-lib/agent-sessions.cjs +1 -1
  107. package/dist-lib/attached-projects.cjs +1 -1
  108. package/dist-lib/auth/shared-session.cjs +1 -1
  109. package/dist-lib/backend-origin.cjs +1 -1
  110. package/dist-lib/beta.cjs +1 -1
  111. package/dist-lib/beta.mjs +1 -1
  112. package/dist-lib/bridge-constants.cjs +1 -1
  113. package/dist-lib/cli-constants.cjs +1 -1
  114. package/dist-lib/config.cjs +1 -1
  115. package/dist-lib/detox/index.cjs +1 -1
  116. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  117. package/dist-lib/home-paths.cjs +1 -1
  118. package/dist-lib/host/bridge-host.cjs +17 -8
  119. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  120. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  121. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  122. package/dist-lib/host/websocket-proxy.cjs +1 -1
  123. package/dist-lib/index.cjs +1 -1
  124. package/dist-lib/jump-to-source-babel.cjs +1 -1
  125. package/dist-lib/menu.cjs +1 -1
  126. package/dist-lib/menu.mjs +1 -1
  127. package/dist-lib/metro.cjs +1 -1
  128. package/dist-lib/profiles.cjs +1 -1
  129. package/dist-lib/render-mode.cjs +1 -1
  130. package/dist-lib/scripts/dev-server-scanner.cjs +58 -3
  131. package/dist-lib/sdk.cjs +18 -6
  132. package/dist-lib/sdk.mjs +18 -6
  133. package/dist-lib/skills.cjs +26 -12
  134. package/dist-lib/vite.cjs +1 -1
  135. package/package.json +2 -2
  136. package/scripts/dev-server-scanner.ts +95 -9
  137. package/skills/sootsim-debug/SKILL.md +6 -0
  138. package/skills/sootsim-setup/SKILL.md +38 -0
  139. package/src/attached-projects.ts +2 -2
  140. package/src/host/bridge-host.ts +2 -2
  141. package/src/runtime-assets.ts +1 -1
  142. package/src/runtime-delivery.ts +1 -1
  143. package/src/vite-plugin.ts +1 -1
  144. package/dist-cli/chunks/app-fonts-VPJDIM5M.js +0 -4
  145. package/dist-cli/chunks/chunk-5TWYZNIF.js +0 -125
  146. package/dist-cli/chunks/chunk-GRMK3BZY.js +0 -4
  147. package/dist-cli/chunks/chunk-I4NP3TJR.js +0 -12
  148. package/dist-cli/chunks/daemon-S25HJFIG.js +0 -4
  149. package/dist-cli/chunks/disk-cleanup-DY6JAHH3.js +0 -4
  150. package/dist-cli/chunks/drivers-EE4LTPSZ.js +0 -4
  151. package/dist-cli/chunks/flow-WGICJ5KS.js +0 -4
  152. package/dist-cli/chunks/help-YQ63GAW6.js +0 -4
  153. package/dist-cli/chunks/inspect-VKVWW64M.js +0 -4
  154. package/dist-cli/chunks/install-desktop-CH27FTQI.js +0 -4
  155. package/dist-cli/chunks/runtime-7MZZR6SC.js +0 -4
  156. package/dist-cli/chunks/screenshot-command-2YSWGL4X.js +0 -156
  157. package/dist-cli/chunks/store-FEQIGIYH.js +0 -4
  158. package/dist-cli/chunks/telemetry-KBOJKIRS.js +0 -4
  159. package/dist-cli/chunks/upload-QKJRNNL3.js +0 -4
@@ -14,6 +14,7 @@
14
14
  // pid changes. before any HTTP, a TCP connect gate skips unreachable ports.
15
15
 
16
16
  import { exec } from 'child_process'
17
+ import { readlink } from 'fs/promises'
17
18
  import http from 'http'
18
19
  import net from 'net'
19
20
  import { promisify } from 'util'
@@ -222,27 +223,112 @@ export async function discoverListeningPorts(
222
223
  return processes.map((p) => p.port)
223
224
  }
224
225
 
225
- // per-pid cwd cache. lsof is fairly cheap but we call it per discovered
226
- // server per scan (every 5s in the tray), so caching the result until the
227
- // pid goes away avoids redundant forks.
226
+ /** parse `netstat -anv -p tcp` output for the pid listening on one port.
227
+ *
228
+ * macOS lays a LISTEN row out as
229
+ * tcp4 0 0 127.0.0.1.8090 *.* LISTEN 0 0 131072 131072 node:97330 ...
230
+ * so the local address is the 4th column with a DOT before the port (which is
231
+ * what keeps port 8137 from matching a row for 18137), and the owner is the
232
+ * only token shaped "<name>:<pid>".
233
+ *
234
+ * exported because this column layout is the fragile part and a child process
235
+ * cannot exercise it under bun's test runner, which hands back empty stdout. */
236
+ export function pidFromNetstat(stdout: string, port: number): number | null {
237
+ for (const line of stdout.split('\n')) {
238
+ if (!line.includes('LISTEN')) continue
239
+ const parts = line.trim().split(/\s+/)
240
+ if (!parts[3]?.endsWith(`.${port}`)) continue
241
+ for (const token of parts) {
242
+ const owner = token.match(/^\S+:(\d+)$/)
243
+ if (owner) {
244
+ const pid = Number(owner[1])
245
+ if (pid > 0) return pid
246
+ }
247
+ }
248
+ }
249
+ return null
250
+ }
251
+
252
+ /** resolve the pid of the process listening on one local port.
253
+ *
254
+ * `lsof` walks every descriptor of every process, which measures 2-4s on a
255
+ * loaded machine — longer than any timeout worth giving it, so
256
+ * `discoverListeningProcesses` above degrades to its pid-less port list
257
+ * exactly when the machine is busy. netstat (macOS/BSD) and ss (linux) read
258
+ * the kernel socket table instead and answer in well under a second, and both
259
+ * carry the owning pid. use this whenever the question is "who owns this one
260
+ * port"; `discoverListeningProcesses` answers the different question of what
261
+ * is listening at all. */
262
+ export async function resolveListeningPid(port: number): Promise<number | null> {
263
+ if (!Number.isInteger(port) || port <= 0) return null
264
+
265
+ // macOS netstat: "tcp4 0 0 127.0.0.1.8090 *.* LISTEN ... node:97330 ..."
266
+ // the local address uses a dot before the port, and the owner column is the
267
+ // only token shaped "<name>:<pid>".
268
+ try {
269
+ const { stdout } = await execP('netstat -anv -p tcp 2>/dev/null', {
270
+ encoding: 'utf8',
271
+ timeout: 8000,
272
+ maxBuffer: 16 * 1024 * 1024,
273
+ })
274
+ const pid = pidFromNetstat(stdout, port)
275
+ if (pid !== null) return pid
276
+ } catch {}
277
+
278
+ // linux ss: 'LISTEN 0 511 0.0.0.0:8081 0.0.0.0:* users:(("node",pid=1234,fd=5))'
279
+ try {
280
+ const { stdout } = await execP(`ss -tlnp 2>/dev/null`, {
281
+ encoding: 'utf8',
282
+ timeout: 8000,
283
+ maxBuffer: 16 * 1024 * 1024,
284
+ })
285
+ for (const line of stdout.split('\n')) {
286
+ if (!new RegExp(`:${port}\\s`).test(line)) continue
287
+ const pid = Number(line.match(/pid=(\d+)/)?.[1])
288
+ if (pid > 0) return pid
289
+ }
290
+ } catch {}
291
+
292
+ return null
293
+ }
294
+
295
+ // per-pid cwd cache. lsof is the only way to read another process's cwd on
296
+ // macOS and it is NOT cheap — measured at 3-8s for a single pid on a machine
297
+ // running a few dozen node processes — so caching the result until the pid
298
+ // goes away is what keeps the 5s tray scan viable.
228
299
  const cwdByPid = new Map<number, string>()
229
300
 
230
- /** resolve the current working directory of a pid using `lsof -d cwd`.
231
- * macOS + linux both accept this. returns null for pid 0 (unknown owner)
232
- * and whenever lsof is unavailable or the fd isn't present.
233
- * async — sync exec blocked CrBrowserMain at ~600ms on macOS. */
301
+ /** resolve the current working directory of a pid. returns null for pid 0
302
+ * (unknown owner) and whenever neither mechanism below can see the process.
303
+ * async — sync exec blocked CrBrowserMain at ~600ms on macOS.
304
+ *
305
+ * `-b -w` skips the blocking stat calls lsof otherwise makes against every
306
+ * mount, which roughly halves it. the timeout is generous because the answer
307
+ * is cached per pid: too tight a limit silently returns null on exactly the
308
+ * busy machines where auto-attaching a project matters most. */
234
309
  export async function resolveProcessCwd(pid: number): Promise<string | null> {
235
310
  if (pid <= 0) return null
236
311
  const cached = cwdByPid.get(pid)
237
312
  if (cached) return cached
313
+
314
+ // linux publishes the cwd as a symlink, which costs one readlink and no
315
+ // subprocess at all. macOS has no /proc and has to pay for lsof below.
316
+ try {
317
+ const cwd = await readlink(`/proc/${pid}/cwd`)
318
+ if (cwd) {
319
+ cwdByPid.set(pid, cwd)
320
+ return cwd
321
+ }
322
+ } catch {}
323
+
238
324
  try {
239
325
  // -Fn → machine-readable, one field per line. lines look like:
240
326
  // p<pid>
241
327
  // fcwd
242
328
  // n<absolute path>
243
- const { stdout } = await execP(`lsof -p ${pid} -a -d cwd -Fn 2>/dev/null`, {
329
+ const { stdout } = await execP(`lsof -b -w -p ${pid} -a -d cwd -Fn 2>/dev/null`, {
244
330
  encoding: 'utf8',
245
- timeout: 1500,
331
+ timeout: 8000,
246
332
  })
247
333
  for (const line of stdout.split('\n')) {
248
334
  if (line.startsWith('n') && line.length > 1) {
@@ -63,6 +63,12 @@ regression test. a pile of ad-hoc logs helps no one and gets deleted.
63
63
  query against it.
64
64
  - **pin a sim once.** if more than one is live, an unpinned command
65
65
  refuses to choose. run `sootsim use <id>` at the start of the workflow.
66
+ - **keep one sim and reuse its browser tab.** before `sootsim open`, run
67
+ `sootsim list --all`. if the target is already connected, pin it and use
68
+ `sootsim do reload`; do not open another tab to reload or retry a capture.
69
+ re-run `list --all` after each reload or reconnect; when another entry appears,
70
+ run `sootsim close --others` against the pinned sim immediately.
71
+ if the current sim must be replaced, close it before opening the replacement.
66
72
  - **`debug eval` is an escape hatch.** if a top-level command feels
67
73
  broken or missing, file an issue — every agent after you benefits from
68
74
  a better CLI, not a pile of ad-hoc scripts.
@@ -23,6 +23,44 @@ debugging or `/sootsim-test` for writing tests.
23
23
  - **an empty `sootsim list` is not "broken sootsim"** — it usually means the
24
24
  daemon is down, the dev server port is wrong, or the runtime is missing.
25
25
  walk the recovery checklist before reinstalling.
26
+ - **one sim for your whole session. switch APPS by navigating that sim, never by
27
+ opening another.** `sootsim open <port> --sim <id>` resolves that exact sim and
28
+ navigates it in place — no new tab, no new browser tree. that is the supported
29
+ way to move between apps and ports:
30
+
31
+ ```sh
32
+ sootsim open 8086 --sim <id> # one app, in your sim
33
+ sootsim open 8090 --sim <id> # a different app, same tab
34
+ sootsim reload --sim <id> # reload whatever it is showing
35
+ sootsim close <id> # dispose it when you are done
36
+ ```
37
+
38
+ every `--new` spawns another browser tree, and each abandoned tab holds a
39
+ renderer process for the life of the browser. these land in somebody's actual
40
+ desktop Chrome. a run that opened one per app left 14 renderers against 2 live
41
+ sims on a shared machine and the owner noticed before we did.
42
+ - **sharing a machine with other agents? open ONE sim with `--new`, pin it, and
43
+ pass `--sim <id>` from then on.** that single `--new` is the only one you get.
44
+ an unpinned command has no sim of its own, so it falls back to whatever sim is
45
+ currently PRIMARY — and that can be another agent's live sim, mid-run. a plain
46
+ `sootsim open <port>` from an unpinned CLI will happily retarget somebody
47
+ else's sim to your port and take over their session; the warning it prints is
48
+ easy to miss in a transcript. the current-sim pin is stored per CLI identity
49
+ (`sootsim-current-sim-<identity>.json`, keyed by your process-tree root), so
50
+ pinning is genuinely per-agent and does not fight anyone else:
51
+
52
+ ```sh
53
+ sootsim open --new 8086 # your own sim; also records it as yours
54
+ sootsim list # read back its id
55
+ sootsim use <id> # pin it explicitly
56
+ sootsim describe --sim <id> # name it on every command after that
57
+ ```
58
+
59
+ `--new`, `--profile`, and `--ephemeral` are rejected outright when combined
60
+ with `--sim`, so if a command needs one of them you are starting a second sim
61
+ whether you meant to or not. if yours died, run `sootsim list --all` and adopt
62
+ an open unlocked sim with `sootsim use <id>` before you consider opening one.
63
+ always close yours when you finish.
26
64
  - **never retry-loop `sootsim open` against a wedged or slow stack.** a
27
65
  connect-timeout ("timed out waiting for opened sim to connect") means STOP and
28
66
  diagnose — not retry. each retried open spawns a new browser tree (a
@@ -10,7 +10,7 @@
10
10
  import { randomBytes, createHash } from 'node:crypto'
11
11
  import fs from 'node:fs'
12
12
  import path from 'node:path'
13
- import { electronUserDataDir } from './home-paths'
13
+ import { electronUserDataDir } from './home-paths.ts'
14
14
 
15
15
  // plan → attached-projects-and-agents.md §data model
16
16
  export interface AttachedProject {
@@ -510,7 +510,7 @@ export async function seedFromDemoAppRegistry(): Promise<void> {
510
510
  // optional internal-only registry — resolves to [] on a published install
511
511
  // (the registry is not shipped), so this seed is a no-op for real users and
512
512
  // never pins their ports to internal demo apps.
513
- const { loadOptionalDemoApps } = await import('../scripts/optional-demo-registry')
513
+ const { loadOptionalDemoApps } = await import('../scripts/optional-demo-registry.ts')
514
514
  const apps = await loadOptionalDemoApps()
515
515
  if (apps.length === 0) return
516
516
  mutateStore((store) => {
@@ -44,9 +44,9 @@ import {
44
44
  handleFetchProxyRequest,
45
45
  isAppApiRequestUrl,
46
46
  isFetchProxyRequestUrl,
47
- } from './fetch-proxy-handler'
47
+ } from './fetch-proxy-handler.ts'
48
48
  import { openUrl as openUrlInBrowser, type OpenUrlOptions } from './open-url.ts'
49
- import { handleWebSocketProxyUpgrade } from './websocket-proxy'
49
+ import { handleWebSocketProxyUpgrade } from './websocket-proxy.ts'
50
50
 
51
51
  const SOOTSIM_CROSS_ORIGIN_EMBEDDER_POLICY = 'require-corp'
52
52
 
@@ -6,7 +6,7 @@ import fs from 'fs'
6
6
  import path from 'path'
7
7
  import { fileURLToPath } from 'url'
8
8
  import { isValidRuntimeVersion } from '@contrast/runtime-delivery'
9
- import { readActiveRuntime, runtimeDir } from './home-paths'
9
+ import { readActiveRuntime, runtimeDir } from './home-paths.ts'
10
10
 
11
11
  export const SOOTSIM_RUNTIME_MISSING_MESSAGE =
12
12
  '[sootsim] no engine runtime installed — run `sootsim setup`, or `sootsim runtime install`'
@@ -19,7 +19,7 @@ import {
19
19
  runtimeDir,
20
20
  runtimesDir,
21
21
  writeActiveRuntime,
22
- } from './home-paths'
22
+ } from './home-paths.ts'
23
23
 
24
24
  export const DEFAULT_RUNTIME_CDN_ORIGIN = 'https://contrast.dev'
25
25
 
@@ -661,7 +661,7 @@ export function reanimatedNativeSeamRedirect(): Plugin {
661
661
  // the reanimated plugin above stays separate because it has package-specific
662
662
  // edge cases (fabricUtils, validate-worklets-version). new migrations should
663
663
  // use the manifest path unless they need similar special handling.
664
- import { NATIVE_SEAM_MANIFEST } from './native-seam-manifest'
664
+ import { NATIVE_SEAM_MANIFEST } from './native-seam-manifest.ts'
665
665
  export function manifestNativeSeamRedirect(): Plugin {
666
666
  // build a fast lookup: pkg → { basenames, target }
667
667
  const byPkg = new Map<
@@ -1,4 +0,0 @@
1
- /*! sootsim v0.1.302 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{d as p}from"./chunk-GRMK3BZY.js";import"./chunk-VEOPRCNX.js";import"./chunk-C36FJ7OB.js";import{existsSync as h,mkdirSync as d,readFileSync as y,writeFileSync as g}from"node:fs";import{join as m}from"node:path";import{gzipSync as w}from"node:zlib";function f(t,e){let o=t.indexOf(`--${e}`);if(o!==-1&&o+1<t.length)return t[o+1];let i=t.find(r=>r.startsWith(`--${e}=`));return i?i.slice(e.length+3):void 0}async function S(t){let e=f(t,"wire")||"",o=f(t,"out-dir"),i=f(t,"manifest");if((!o||!i)&&(console.error(" usage: sootsim app-fonts stage --wire <wire> --out-dir <dir> --manifest <path>"),process.exit(1)),!e.trim()){console.log("no app fonts declared \u2014 nothing to stage");return}d(o,{recursive:!0});let r=await p(e,{onStaged:({url:n,byteLength:s})=>console.log(` staged app font: ${n} (${(s/1024).toFixed(1)} KiB)`),onError:(n,s)=>console.error(` warning: failed to fetch app font ${n}: ${s instanceof Error?s.message:s}`)}),a=[];for(let n of r){let s=w(n.bytes);g(m(o,n.urlhash),s),a.push({url:n.url,urlhash:n.urlhash,contentType:n.contentType,encoding:"gzip",sizeBytes:s.length,rawBytes:n.bytes.byteLength})}let c=[];if(h(i))try{let n=JSON.parse(y(i,"utf8"));Array.isArray(n)&&(c=n)}catch{}let u=new Set(c.map(n=>n.urlhash)),l=[...c,...a.filter(n=>!u.has(n.urlhash))];g(i,JSON.stringify(l)),console.log(`staged ${a.length} app font(s); manifest now has ${l.length} file(s)`)}async function E(t){let e=t[0];if(e==="stage"){await S(t.slice(1));return}console.error(` unknown app-fonts subcommand: ${e??"(none)"}`),console.error(" available: stage"),process.exit(1)}export{E as runAppFonts};