sootsim 0.1.139 → 0.1.141

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 (163) hide show
  1. package/dist-cli/bin.js +3 -3
  2. package/dist-cli/chunks/{agent-AMV3XDZN.js → agent-BFN5TIRU.js} +2 -2
  3. package/dist-cli/chunks/{agent-wrapper-GHQLZH5R.js → agent-wrapper-A4UNOJOD.js} +2 -2
  4. package/dist-cli/chunks/{app-fonts-DLEYOBO5.js → app-fonts-KEAMDLYZ.js} +2 -2
  5. package/dist-cli/chunks/{assert-Y5JVYKGT.js → assert-BWBAKX5N.js} +2 -2
  6. package/dist-cli/chunks/auto-bootstrap-WJH44ZIE.js +2 -0
  7. package/dist-cli/chunks/beta-VFKZHKER.js +2 -0
  8. package/dist-cli/chunks/{camera-R5LLADTA.js → camera-P5TTGC72.js} +2 -2
  9. package/dist-cli/chunks/{chunk-ZX23A5IN.js → chunk-33EJL7CG.js} +2 -2
  10. package/dist-cli/chunks/{chunk-TBSHXKZT.js → chunk-3LN2PP4L.js} +2 -2
  11. package/dist-cli/chunks/{chunk-XJAWWHYR.js → chunk-4ISWLDJG.js} +1 -1
  12. package/dist-cli/chunks/{chunk-E625N74G.js → chunk-53DRW635.js} +3 -3
  13. package/dist-cli/chunks/{chunk-WWSCWKNE.js → chunk-7A7VFR2C.js} +1 -1
  14. package/dist-cli/chunks/{chunk-FXTYQW4H.js → chunk-7SO2SJ4H.js} +1 -1
  15. package/dist-cli/chunks/{chunk-H4HHC2JY.js → chunk-A5W3HB44.js} +1 -1
  16. package/dist-cli/chunks/{chunk-IQ6KSDIL.js → chunk-AJGTCVQX.js} +1 -1
  17. package/dist-cli/chunks/{chunk-R5AS67IW.js → chunk-AMSIP5PD.js} +3 -3
  18. package/dist-cli/chunks/{chunk-VU7RMAQ4.js → chunk-AQVB4G34.js} +2 -2
  19. package/dist-cli/chunks/{chunk-ICQKMKDM.js → chunk-DM5KNA7Q.js} +2 -2
  20. package/dist-cli/chunks/{chunk-ACFQGYVP.js → chunk-EIZ6LJP2.js} +2 -2
  21. package/dist-cli/chunks/{chunk-7EKN4S4H.js → chunk-ETB6DHJM.js} +2 -2
  22. package/dist-cli/chunks/{chunk-AQBTE6OT.js → chunk-HDCTWR2Q.js} +2 -2
  23. package/dist-cli/chunks/chunk-I3JPJMJB.js +5 -0
  24. package/dist-cli/chunks/{chunk-ZUUM47FA.js → chunk-I6SWX7OI.js} +2 -2
  25. package/dist-cli/chunks/{chunk-TK3G654S.js → chunk-IILFDOSF.js} +1 -1
  26. package/dist-cli/chunks/{chunk-BMB7ZFSG.js → chunk-IITPPGBS.js} +1 -1
  27. package/dist-cli/chunks/{chunk-RXT2MXRM.js → chunk-ITJLXD4X.js} +2 -2
  28. package/dist-cli/chunks/{chunk-TA3BB6DU.js → chunk-JJBNA2JO.js} +2 -2
  29. package/dist-cli/chunks/{chunk-TCUZT7HW.js → chunk-JM5XCDBF.js} +52 -4
  30. package/dist-cli/chunks/{chunk-T367DMWW.js → chunk-JQTHQOPS.js} +2 -2
  31. package/dist-cli/chunks/{chunk-7IBHD3FE.js → chunk-KMQWCYU7.js} +2 -2
  32. package/dist-cli/chunks/{chunk-O5VKZEYP.js → chunk-LHIPDN4B.js} +1 -1
  33. package/dist-cli/chunks/chunk-LHS4PEAA.js +79 -0
  34. package/dist-cli/chunks/{chunk-MLQRWULD.js → chunk-M7S4VLEL.js} +55 -55
  35. package/dist-cli/chunks/{chunk-BUY6HG6I.js → chunk-MENSR2TZ.js} +1 -1
  36. package/dist-cli/chunks/chunk-MHUNWWWS.js +412 -0
  37. package/dist-cli/chunks/chunk-MYKHUFW2.js +1 -0
  38. package/dist-cli/chunks/chunk-NOAONZTZ.js +119 -0
  39. package/dist-cli/chunks/{chunk-JWBMOFM4.js → chunk-NQJD2WTM.js} +1 -1
  40. package/dist-cli/chunks/{chunk-PL55QQ5Z.js → chunk-PVPWCA2K.js} +1 -1
  41. package/dist-cli/chunks/{chunk-C2RSKTRR.js → chunk-RBUJZXNR.js} +5 -5
  42. package/dist-cli/chunks/chunk-RUXWU3UH.js +2 -0
  43. package/dist-cli/chunks/chunk-SXHBK5IJ.js +69 -0
  44. package/dist-cli/chunks/chunk-SYRS7NKN.js +1 -0
  45. package/dist-cli/chunks/chunk-TUZVZ5QS.js +1 -0
  46. package/dist-cli/chunks/{chunk-ZNHSTLAY.js → chunk-URPQTI2I.js} +2 -2
  47. package/dist-cli/chunks/{chunk-MGQ35ZJX.js → chunk-UYQDGXHQ.js} +1 -1
  48. package/dist-cli/chunks/chunk-WUQHVDJU.js +4 -0
  49. package/dist-cli/chunks/{chunk-65GV253M.js → chunk-XD7XHQUO.js} +2 -2
  50. package/dist-cli/chunks/{chunk-LFPX2KK4.js → chunk-XERRDQLM.js} +1 -1
  51. package/dist-cli/chunks/{chunk-OWI2ZHZE.js → chunk-XQRCVSK3.js} +1 -1
  52. package/dist-cli/chunks/{chunk-WCKN6BE2.js → chunk-YOJ6BZCH.js} +1 -1
  53. package/dist-cli/chunks/chunk-ZBN3P5JE.js +2 -0
  54. package/dist-cli/chunks/cli-version-3G6HHMRW.js +2 -0
  55. package/dist-cli/chunks/{compat-2EQAHCNM.js → compat-3Z67EPSJ.js} +3 -3
  56. package/dist-cli/chunks/{config-NBVL6FYM.js → config-SH3NTYZ2.js} +2 -2
  57. package/dist-cli/chunks/{control-V273ZZ3M.js → control-2XVEDKYZ.js} +2 -2
  58. package/dist-cli/chunks/{cpu-profile-5XNWKK3A.js → cpu-profile-FANLHU4S.js} +2 -2
  59. package/dist-cli/chunks/{daemon-NA3DX4ZX.js → daemon-QGDVR4JT.js} +2 -2
  60. package/dist-cli/chunks/{debug-EFOPNXRQ.js → debug-EIUEPLFN.js} +3 -3
  61. package/dist-cli/chunks/{detox-KVXJMO6N.js → detox-EV3KYFKR.js} +2 -2
  62. package/dist-cli/chunks/{device-CT3OAKPD.js → device-UC5DAVA6.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-7V6YJI6N.js → diagnose-Q2M2AJBD.js} +2 -2
  64. package/dist-cli/chunks/drivers-VJIWLDTC.js +2 -0
  65. package/dist-cli/chunks/{electron-JYEQ2OSP.js → electron-PXKF52Q6.js} +3 -3
  66. package/dist-cli/chunks/flow-RGMXGVLL.js +2 -0
  67. package/dist-cli/chunks/help-TBZZP4SA.js +2 -0
  68. package/dist-cli/chunks/{hints-BSLK5WFF.js → hints-HS7X2FIO.js} +2 -2
  69. package/dist-cli/chunks/{home-paths-O6YSN6UP.js → home-paths-KPMXMJQ7.js} +2 -2
  70. package/dist-cli/chunks/{inspect-D3QA6IZ7.js → inspect-ZUWEMCIG.js} +3 -3
  71. package/dist-cli/chunks/install-FPYDDDQ4.js +2 -0
  72. package/dist-cli/chunks/{install-desktop-5TDP7V6G.js → install-desktop-H6ZTTVXC.js} +7 -7
  73. package/dist-cli/chunks/{keys-PASWSEEO.js → keys-6OBZZLCJ.js} +2 -2
  74. package/dist-cli/chunks/{launch-QZQEGABG.js → launch-7JASEDHP.js} +3 -3
  75. package/dist-cli/chunks/{login-XY4M72YJ.js → login-JWMB6JNS.js} +4 -4
  76. package/dist-cli/chunks/{logout-YVNSQJSF.js → logout-LWVFUKF5.js} +2 -2
  77. package/dist-cli/chunks/maestro-WX22SLCR.js +94 -0
  78. package/dist-cli/chunks/optional-demo-registry-52MHQCYV.js +2 -0
  79. package/dist-cli/chunks/{preview-PTCJZJLX.js → preview-SDMLCW3U.js} +2 -2
  80. package/dist-cli/chunks/{profile-CR7H6TKR.js → profile-MQVLCO7R.js} +2 -2
  81. package/dist-cli/chunks/{react-Q7TN57VS.js → react-2JFP5W4W.js} +2 -2
  82. package/dist-cli/chunks/{record-LH43WKXK.js → record-IJJ3SPXP.js} +2 -2
  83. package/dist-cli/chunks/runtime-JJMZ4AF3.js +2 -0
  84. package/dist-cli/chunks/{runtime-delivery-EJUR3Y37.js → runtime-delivery-5M2P7LEI.js} +2 -2
  85. package/dist-cli/chunks/{screenshot-H6KGQMSK.js → screenshot-4QAGJCPZ.js} +2 -2
  86. package/dist-cli/chunks/{screenshot-mode-7YJNNJZZ.js → screenshot-mode-PSAU7TH4.js} +2 -2
  87. package/dist-cli/chunks/{screenshots-AR3C2NR2.js → screenshots-73VI4UUL.js} +2 -2
  88. package/dist-cli/chunks/{server-YG3GY22N.js → server-BXD5WKQS.js} +3 -3
  89. package/dist-cli/chunks/setup-repo-F2P5RZ4R.js +2 -0
  90. package/dist-cli/chunks/{skills-TUYB6WUS.js → skills-IEJOURWM.js} +2 -2
  91. package/dist-cli/chunks/{start-D5HVPGC5.js → start-CLBAJ4LJ.js} +4 -4
  92. package/dist-cli/chunks/store-7DIOVJNK.js +2 -0
  93. package/dist-cli/chunks/telemetry-3XHPQYOQ.js +2 -0
  94. package/dist-cli/chunks/{test-CHBKMHLB.js → test-42YWJ5ZT.js} +3 -3
  95. package/dist-cli/chunks/{three-mode-SXAD2GWI.js → three-mode-5DU4IBF5.js} +2 -2
  96. package/dist-cli/chunks/{timeline-BFF2I7CC.js → timeline-WJYQRW5S.js} +2 -2
  97. package/dist-cli/chunks/{upgrade-XLLL34R2.js → upgrade-F7ADMCLU.js} +2 -2
  98. package/dist-cli/chunks/upload-TJ2XKWDS.js +2 -0
  99. package/dist-cli/chunks/{version-ABW7HY2I.js → version-SLA5P34B.js} +2 -2
  100. package/dist-cli/chunks/web-OGC6GSKM.js +2 -0
  101. package/dist-cli/chunks/{what-happened-PHMTRHXF.js → what-happened-VYXK3O2C.js} +2 -2
  102. package/dist-cli/chunks/{whoami-K6RYDVQK.js → whoami-QL2LTKOF.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-sessions.cjs +1 -1
  106. package/dist-lib/attached-projects.cjs +42 -820
  107. package/dist-lib/auth/shared-session.cjs +1 -1
  108. package/dist-lib/backend-origin.cjs +1 -1
  109. package/dist-lib/beta.cjs +1 -1
  110. package/dist-lib/beta.mjs +1 -1
  111. package/dist-lib/bridge-constants.cjs +1 -1
  112. package/dist-lib/cli-constants.cjs +1 -1
  113. package/dist-lib/config.cjs +5 -2
  114. package/dist-lib/detox/index.cjs +1 -1
  115. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  116. package/dist-lib/home-paths.cjs +1 -1
  117. package/dist-lib/host/bridge-host.cjs +180 -957
  118. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  119. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  120. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  121. package/dist-lib/host/websocket-proxy.cjs +1 -1
  122. package/dist-lib/index.cjs +1 -1
  123. package/dist-lib/metro.cjs +1 -1
  124. package/dist-lib/profiles.cjs +1 -1
  125. package/dist-lib/render-mode.cjs +1 -1
  126. package/dist-lib/scripts/dev-server-scanner.cjs +30 -798
  127. package/dist-lib/sdk.cjs +1 -1
  128. package/dist-lib/sdk.mjs +1 -1
  129. package/dist-lib/skills.cjs +1261 -1362
  130. package/dist-lib/vite.cjs +1 -1
  131. package/package.json +2 -6
  132. package/scripts/dev-server-scanner.ts +10 -2
  133. package/scripts/optional-demo-registry.ts +54 -0
  134. package/src/attached-projects.ts +6 -27
  135. package/src/config.ts +13 -0
  136. package/dist-cli/chunks/auto-bootstrap-FGBI5CJ7.js +0 -2
  137. package/dist-cli/chunks/beta-52CSPPUC.js +0 -2
  138. package/dist-cli/chunks/chunk-25SHAK5L.js +0 -62
  139. package/dist-cli/chunks/chunk-4FZ7V6NP.js +0 -1
  140. package/dist-cli/chunks/chunk-5JA6QRQR.js +0 -380
  141. package/dist-cli/chunks/chunk-BGJDSWVB.js +0 -5
  142. package/dist-cli/chunks/chunk-DSQZJPSK.js +0 -79
  143. package/dist-cli/chunks/chunk-OESWG4DQ.js +0 -1
  144. package/dist-cli/chunks/chunk-P73XFXAI.js +0 -4
  145. package/dist-cli/chunks/chunk-PREGMTVP.js +0 -119
  146. package/dist-cli/chunks/chunk-TB5WPL7Q.js +0 -2
  147. package/dist-cli/chunks/chunk-VJ2PPTZJ.js +0 -64
  148. package/dist-cli/chunks/chunk-VR23KNQD.js +0 -1
  149. package/dist-cli/chunks/cli-version-QJGRKUNQ.js +0 -2
  150. package/dist-cli/chunks/demo-app-registry-3N4MPKCB.js +0 -2
  151. package/dist-cli/chunks/drivers-ALPQZSFQ.js +0 -2
  152. package/dist-cli/chunks/flow-KMYJNXG3.js +0 -2
  153. package/dist-cli/chunks/help-Q4U3HFXF.js +0 -2
  154. package/dist-cli/chunks/install-T7PQXSEL.js +0 -2
  155. package/dist-cli/chunks/maestro-BWLFDA2J.js +0 -80
  156. package/dist-cli/chunks/runtime-U3YSH5S6.js +0 -2
  157. package/dist-cli/chunks/setup-repo-VDA2UFDB.js +0 -2
  158. package/dist-cli/chunks/store-UG47OKR6.js +0 -2
  159. package/dist-cli/chunks/telemetry-276DYSYW.js +0 -2
  160. package/dist-cli/chunks/upload-JGRABYUF.js +0 -2
  161. package/dist-cli/chunks/web-V76A7KM4.js +0 -2
  162. package/dist-lib/scripts/demo-app-registry.cjs +0 -818
  163. package/scripts/demo-app-registry.ts +0 -1006
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.139 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as d,b as v}from"./chunk-IQ6KSDIL.js";import{a as f,c as w}from"./chunk-65GV253M.js";import{a as u,b as h,c as m}from"./chunk-O5VKZEYP.js";import{c as p}from"./chunk-H4HHC2JY.js";function _(){let e=u();return e?{available:!0,reason:null,detail:e}:{available:!1,reason:"no chromium-family browser found (chrome, chromium, edge, brave, arc)"}}async function M(e){if(e.headless)return{launched:!1,message:"the chromium driver launches a visible browser window and cannot run headless \u2014 a headless `chrome --headless=new` process exits immediately and the sim never connects. for headless automation use `--driver playwright`."};let r=u();if(!r)return{launched:!1,message:"chromium not found \u2014 install chrome or run `sootsim list --drivers`"};if(!e.url)return{launched:!1,message:"chromium driver requires a target url"};if(e.profileId||e.ephemeralProfile)return{launched:!1,message:"profiles require electron or playwright; browser tabs use your current Chrome profile"};try{let t=await m(e.url,{detached:e.detached??!0});return{launched:!0,message:`chromium launched \u2192 ${e.url}`,pid:t.pid,target:t.target??r,attachUrl:e.url}}catch(t){return{launched:!1,message:`chromium spawn failed: ${t instanceof Error?t.message:String(t)}`}}}var g={id:"chromium",name:"chromium",description:"system chrome / chromium / edge \u2014 visible window (headless: use playwright)",kind:"browser",availability:_,launch:M};function I(){let e=d();return e?{available:!0,reason:null,detail:`${e.kind} @ ${e.path}`}:{available:!1,reason:"sootsim desktop app not installed (run `sootsim install-desktop`)"}}async function x(e){let r=d();if(!r)return{launched:!1,message:"sootsim desktop app not installed"};try{let t=await v(e.url,r,{device:e.device,profileId:e.profileId,ephemeralProfile:e.ephemeralProfile});return t.launched?{launched:!0,message:e.url?`electron launched via ${t.via} \u2192 ${e.url}`:`electron launched via ${t.via}`,target:t.target,attachUrl:e.url}:{launched:!1,message:"desktop companion failed to start"}}catch(t){return{launched:!1,message:`electron launch failed: ${t instanceof Error?t.message:String(t)}`}}}var y={id:"electron",name:"electron",description:"sootsim desktop companion app (native window, menu bar)",kind:"native",availability:I,launch:x};import{spawn as C}from"child_process";import{closeSync as E,mkdtempSync as A,openSync as L,readFileSync as W}from"fs";import{createRequire as R}from"module";import{tmpdir as b}from"os";import{join as P}from"path";var N=`
1
+ /*! sootsim v0.1.141 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{a as d,b as g}from"./chunk-AJGTCVQX.js";import{a as f,c as w}from"./chunk-XD7XHQUO.js";import{a as u,b as h,c as m}from"./chunk-LHIPDN4B.js";import{c as p}from"./chunk-A5W3HB44.js";function I(){let e=u();return e?{available:!0,reason:null,detail:e}:{available:!1,reason:"no chromium-family browser found (chrome, chromium, edge, brave, arc)"}}async function M(e){if(e.headless)return{launched:!1,message:"the chromium driver launches a visible browser window and cannot run headless \u2014 a headless `chrome --headless=new` process exits immediately and the sim never connects. for headless automation use `--driver playwright`."};let r=u();if(!r)return{launched:!1,message:"chromium not found \u2014 install chrome or run `sootsim list --drivers`"};if(!e.url)return{launched:!1,message:"chromium driver requires a target url"};if(e.profileId||e.ephemeralProfile)return{launched:!1,message:"profiles require electron or playwright; browser tabs use your current Chrome profile"};try{let t=await m(e.url,{detached:e.detached??!0});return{launched:!0,message:`chromium launched \u2192 ${e.url}`,pid:t.pid,target:t.target??r,attachUrl:e.url}}catch(t){return{launched:!1,message:`chromium spawn failed: ${t instanceof Error?t.message:String(t)}`}}}var v={id:"chromium",name:"chromium",description:"system chrome / chromium / edge \u2014 visible window (headless: use playwright)",kind:"browser",availability:I,launch:M};function _(){let e=d();return e?{available:!0,reason:null,detail:`${e.kind} @ ${e.path}`}:{available:!1,reason:"sootsim desktop app not installed (run `sootsim install-desktop`)"}}async function x(e){let r=d();if(!r)return{launched:!1,message:"sootsim desktop app not installed"};try{let t=await g(e.url,r,{device:e.device,profileId:e.profileId,ephemeralProfile:e.ephemeralProfile});return t.launched?{launched:!0,message:e.url?`electron launched via ${t.via} \u2192 ${e.url}`:`electron launched via ${t.via}`,target:t.target,attachUrl:e.url}:{launched:!1,message:"desktop companion failed to start"}}catch(t){return{launched:!1,message:`electron launch failed: ${t instanceof Error?t.message:String(t)}`}}}var y={id:"electron",name:"electron",description:"sootsim desktop companion app (native window, menu bar)",kind:"native",availability:_,launch:x};import{spawn as C}from"child_process";import{closeSync as E,mkdtempSync as A,openSync as L,readFileSync as R}from"fs";import{createRequire as W}from"module";import{tmpdir as b}from"os";import{join as S}from"path";var N=`
3
3
  const modulePath = process.env.SOOTSIM_PW_MODULE;
4
4
  const url = process.env.SOOTSIM_PW_URL;
5
5
  const headless = process.env.SOOTSIM_PW_HEADLESS === '1';
@@ -17,6 +17,40 @@ const cdpPort = process.env.SOOTSIM_PW_CDP_PORT || '';
17
17
  const closeTimeoutMs = Number(process.env.SOOTSIM_PW_CLOSE_TIMEOUT_MS || 2500);
18
18
  const connectAckFile = process.env.SOOTSIM_PW_CONNECTED_ACK_FILE || '';
19
19
  const { existsSync, unlinkSync } = require('fs');
20
+ const { execSync } = require('child_process');
21
+ // chromium tree reaper \u2014 playwright's browser.close() does not reliably reap
22
+ // the chromium helper tree (gpu/renderer/zygote/utility reparent to init and
23
+ // survive), and a SIGTERM/SIGKILL of just the root leaves them behind too.
24
+ // repeated/aborted opens then accumulate orphan chromiums until the machine
25
+ // OOMs (31 leaked processes thrashed a 4GB VM in one smoke run). mirror the
26
+ // proven pid-diff + recursive killTree pattern from scripts/lib/reap-browser.ts:
27
+ // only pids matching the playwright-managed chromium install are ever touched
28
+ // (a system Chrome launched via channel:'chrome' lives outside ms-playwright/
29
+ // and is never killed), and the whole thing is skipped on windows where pgrep
30
+ // does not exist \u2014 there plain close() remains the only teardown.
31
+ const reapEnabled = process.platform !== 'win32';
32
+ const chromiumPids = () => {
33
+ if (!reapEnabled) return [];
34
+ try {
35
+ // [c]hromium: this host runs as \`node -e <script>\`, so its OWN cmdline
36
+ // contains this source \u2014 a plain pattern would match every sibling host
37
+ // process and a concurrent launch could cross-kill another host's tree.
38
+ // the character class keeps the pattern from matching its own literal.
39
+ return execSync('pgrep -f "ms-playwright/[c]hromium" || true', { stdio: ['ignore', 'pipe', 'ignore'] })
40
+ .toString().split('\\n').filter(Boolean);
41
+ } catch { return []; }
42
+ };
43
+ const killTree = (pid) => {
44
+ let kids = [];
45
+ try {
46
+ kids = execSync('pgrep -P ' + pid + ' || true', { stdio: ['ignore', 'pipe', 'ignore'] })
47
+ .toString().split('\\n').filter(Boolean);
48
+ } catch {}
49
+ for (const k of kids) killTree(Number(k));
50
+ try { process.kill(pid, 'SIGKILL'); } catch {}
51
+ };
52
+ const myChromiumRoots = new Set();
53
+ const reapChromium = () => { for (const pid of myChromiumRoots) killTree(pid); };
20
54
  (async () => {
21
55
  const pw = require(modulePath);
22
56
  let browser = null;
@@ -118,6 +152,9 @@ const { existsSync, unlinkSync } = require('fs');
118
152
  const b = maybeBrowser();
119
153
  if (b) await bounded('browser.close', () => b.close(), timeoutMs);
120
154
  await terminateBrowserProcess();
155
+ // close() / root-SIGTERM above handle the graceful case; this sweeps any
156
+ // helper processes they left behind. no-op for already-dead pids.
157
+ reapChromium();
121
158
  if (connectAckFile) {
122
159
  try { unlinkSync(connectAckFile); } catch {}
123
160
  }
@@ -125,6 +162,9 @@ const { existsSync, unlinkSync } = require('fs');
125
162
  };
126
163
  process.once('SIGTERM', () => { void shutdown(0); });
127
164
  process.once('SIGINT', () => { void shutdown(0); });
165
+ // the launch-failure path at the bottom of this script exits without going
166
+ // through shutdown(); the exit hook (sync killTree) still reaps the tree.
167
+ process.on('exit', () => { reapChromium(); });
128
168
  // owner-liveness watchdog \u2014 the load-bearing fix for leaked Chrome hosts.
129
169
  // we are spawned detached + unref'd so \`sootsim open\` returns immediately,
130
170
  // which means nothing in the OS process tree ties Chrome's lifetime to the
@@ -187,7 +227,15 @@ const { existsSync, unlinkSync } = require('fs');
187
227
  let launchedChannel = null;
188
228
  for (const channel of channelsToTry) {
189
229
  try {
230
+ // bracket the launch with a chromium pid-diff so only the tree THIS
231
+ // host spawned is ever reaped (playwright's Browser exposes no pid, so
232
+ // the diff is the portable way to find our roots \u2014 same approach as
233
+ // scripts/lib/reap-browser.ts).
234
+ const pidsBefore = new Set(chromiumPids());
190
235
  const launched = await launchWith(channel);
236
+ for (const p of chromiumPids()) {
237
+ if (!pidsBefore.has(p)) myChromiumRoots.add(Number(p));
238
+ }
191
239
  launchedChannel = channel || 'chromium';
192
240
  if (launched.kind === 'persistent') {
193
241
  context = launched.value;
@@ -269,5 +317,5 @@ const { existsSync, unlinkSync } = require('fs');
269
317
  process.stderr.write(String((err && err.stack) || err) + '\\n');
270
318
  process.exit(1);
271
319
  });
272
- `;function D(){let e=[process.cwd()+"/"];try{process.argv[1]&&e.push(process.argv[1])}catch{}for(let r of e)try{let t=R(r);for(let n of["playwright","playwright-chromium","playwright-core"])try{return{spec:n,modulePath:t.resolve(n)}}catch{}}catch{}return null}function $(){let e=D();return e?{available:!0,reason:null,detail:`resolved via ${e.spec}`}:{available:!1,reason:"playwright not installed in the current workspace"}}function F(e){return e.profileId?f(w(e.profileId).id):e.ephemeralProfile?A(P(b(),"sootsim-playwright-profile-")):""}async function U(e){let r=D();if(!r)return{launched:!1,message:"playwright not installed \u2014 run `bun add -D playwright` first"};if(!e.url)return{launched:!1,message:"playwright driver requires a target url"};let t=P(b(),`sootsim-playwright-host-${Date.now().toString(36)}-${process.pid}.log`),n=`${t}.connected`,a=L(t,"a");try{let o=p(),l=C(process.execPath,["-e",N],{detached:!0,stdio:["ignore","ignore",a],env:{...process.env,SOOTSIM_PW_MODULE:r.modulePath,SOOTSIM_PW_URL:e.url,SOOTSIM_PW_HEADLESS:e.headless??!0?"1":"0",SOOTSIM_PW_USERDATADIR:F(e),SOOTSIM_PW_CDP_PORT:e.cdpPort?String(e.cdpPort):"",SOOTSIM_PW_CONNECTED_ACK_FILE:n,SOOTSIM_PW_CONNECT_TIMEOUT_MS:String(e.connectTimeoutMs??12e4),SOOTSIM_PW_OWNER_PID:o&&o>1?String(o):""}});l.unref();let c=await new Promise(i=>{let k=setTimeout(()=>i(null),4e3);l.once("exit",T=>{clearTimeout(k),i(T??0)})});if(c!==null&&c!==0){let i=W(t,"utf8").trim();return{launched:!1,message:`playwright host exited early (code ${c}) \u2014 host log ${t}${i?`:
273
- ${i}`:""}`}}return{launched:!0,message:`playwright chrome launched \u2192 ${e.url}`,pid:l.pid,target:r.spec,attachUrl:e.url,connectAckFile:n,diagnosticLogPath:t}}catch(o){return{launched:!1,message:`playwright launch failed: ${o instanceof Error?o.message:String(o)}`}}finally{E(a)}}var S={id:"playwright",name:"playwright",description:"programmatic chromium via the playwright package \u2014 headless default",kind:"automation",availability:$,launch:U};function H(){return process.platform!=="darwin"&&process.platform!=="linux"&&process.platform!=="win32"?{available:!1,reason:`unsupported platform: ${process.platform}`}:{available:!0,reason:null,detail:process.platform==="darwin"?"open(1)":process.platform==="win32"?"cmd /c start":"xdg-open(1)"}}async function q(e){if(e.headless)return{launched:!1,message:"system driver does not support --headless; use chromium or playwright"};if(!e.url)return{launched:!1,message:"system driver requires a target url"};if(e.profileId||e.ephemeralProfile)return{launched:!1,message:"profiles require electron or playwright; browser tabs use your current browser profile"};try{let r=await h(e.url,{detached:e.detached??!0,newWindow:e.newWindow});return{launched:!0,message:`${r.via} launch \u2192 ${e.url}`,pid:r.pid,target:r.target,attachUrl:e.url}}catch(r){return{launched:!1,message:`system launch failed: ${r instanceof Error?r.message:String(r)}`}}}var O={id:"system",name:"system",description:"shared browser launcher \u2014 system default or chromium window",kind:"delegate",availability:H,launch:q};var s=[g,y,S,O];function ae(){return s}function le(e){return s.find(r=>r.id===e)??null}function ce(e,r){if(e)return s.find(n=>n.id===e)??null;for(let t of r){let n=s.find(a=>a.id===t);if(n&&n.availability().available)return n}return null}function ue(){return s.map(e=>{let r=e.availability();return{id:e.id,name:e.name,kind:e.kind,description:e.description,available:r.available,reason:r.reason,detail:r.detail??null}})}export{g as a,y as b,S as c,O as d,s as e,ae as f,le as g,ce as h,ue as i};
320
+ `;function P(){let e=[process.cwd()+"/"];try{process.argv[1]&&e.push(process.argv[1])}catch{}for(let r of e)try{let t=W(r);for(let n of["playwright","playwright-chromium","playwright-core"])try{return{spec:n,modulePath:t.resolve(n)}}catch{}}catch{}return null}function $(){let e=P();return e?{available:!0,reason:null,detail:`resolved via ${e.spec}`}:{available:!1,reason:"playwright not installed in the current workspace"}}function F(e){return e.profileId?f(w(e.profileId).id):e.ephemeralProfile?A(S(b(),"sootsim-playwright-profile-")):""}async function U(e){let r=P();if(!r)return{launched:!1,message:"playwright not installed \u2014 run `bun add -D playwright` first"};if(!e.url)return{launched:!1,message:"playwright driver requires a target url"};let t=S(b(),`sootsim-playwright-host-${Date.now().toString(36)}-${process.pid}.log`),n=`${t}.connected`,a=L(t,"a");try{let o=p(),l=C(process.execPath,["-e",N],{detached:!0,stdio:["ignore","ignore",a],env:{...process.env,SOOTSIM_PW_MODULE:r.modulePath,SOOTSIM_PW_URL:e.url,SOOTSIM_PW_HEADLESS:e.headless??!0?"1":"0",SOOTSIM_PW_USERDATADIR:F(e),SOOTSIM_PW_CDP_PORT:e.cdpPort?String(e.cdpPort):"",SOOTSIM_PW_CONNECTED_ACK_FILE:n,SOOTSIM_PW_CONNECT_TIMEOUT_MS:String(e.connectTimeoutMs??12e4),SOOTSIM_PW_OWNER_PID:o&&o>1?String(o):""}});l.unref();let c=await new Promise(i=>{let O=setTimeout(()=>i(null),4e3);l.once("exit",T=>{clearTimeout(O),i(T??0)})});if(c!==null&&c!==0){let i=R(t,"utf8").trim();return{launched:!1,message:`playwright host exited early (code ${c}) \u2014 host log ${t}${i?`:
321
+ ${i}`:""}`}}return{launched:!0,message:`playwright chrome launched \u2192 ${e.url}`,pid:l.pid,target:r.spec,attachUrl:e.url,connectAckFile:n,diagnosticLogPath:t}}catch(o){return{launched:!1,message:`playwright launch failed: ${o instanceof Error?o.message:String(o)}`}}finally{E(a)}}var k={id:"playwright",name:"playwright",description:"programmatic chromium via the playwright package \u2014 headless default",kind:"automation",availability:$,launch:U};function H(){return process.platform!=="darwin"&&process.platform!=="linux"&&process.platform!=="win32"?{available:!1,reason:`unsupported platform: ${process.platform}`}:{available:!0,reason:null,detail:process.platform==="darwin"?"open(1)":process.platform==="win32"?"cmd /c start":"xdg-open(1)"}}async function B(e){if(e.headless)return{launched:!1,message:"system driver does not support --headless; use chromium or playwright"};if(!e.url)return{launched:!1,message:"system driver requires a target url"};if(e.profileId||e.ephemeralProfile)return{launched:!1,message:"profiles require electron or playwright; browser tabs use your current browser profile"};try{let r=await h(e.url,{detached:e.detached??!0,newWindow:e.newWindow});return{launched:!0,message:`${r.via} launch \u2192 ${e.url}`,pid:r.pid,target:r.target,attachUrl:e.url}}catch(r){return{launched:!1,message:`system launch failed: ${r instanceof Error?r.message:String(r)}`}}}var D={id:"system",name:"system",description:"shared browser launcher \u2014 system default or chromium window",kind:"delegate",availability:H,launch:B};var s=[v,y,k,D];function ae(){return s}function le(e){return s.find(r=>r.id===e)??null}function ce(e,r){if(e)return s.find(n=>n.id===e)??null;for(let t of r){let n=s.find(a=>a.id===t);if(n&&n.availability().available)return n}return null}function ue(){return s.map(e=>{let r=e.availability();return{id:e.id,name:e.name,kind:e.kind,description:e.description,available:r.available,reason:r.reason,detail:r.detail??null}})}export{v as a,y as b,k as c,D as d,s as e,ae as f,le as g,ce as h,ue as i};
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.139 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as P,d as T,f as C}from"./chunk-H4HHC2JY.js";import{G as k,H as v}from"./chunk-FXTYQW4H.js";import{WebSocket as I}from"ws";function x(){let e=k();return e&&v(e)?e.bridgePort:7668}var w=class extends Error{lock;constructor(r,i){super(r),this.name="BridgeSimLockedError",this.lock=i}};function $(e){return e==="flag"?"via --sim":e==="saved"?"saved via `sootsim use`":"primary fallback \u2014 no sim pinned"}function N(e,r={}){let i=new Set,a=r.port??x(),l=r.commandTimeoutMs??15e3,t,m="none",h=new Set(r.stripBooleanFlags??[]),b=new Set(r.stripValueFlags??[]);for(let n=0;n<e.length;n++){let d=e[n];if(d==="--port"||d==="-p"){i.add(n),n+1<e.length&&(i.add(n+1),a=Number(e[n+1])),n++;continue}if(d.startsWith("--port=")){i.add(n),a=Number(d.slice(7));continue}if(d==="--timeout"){i.add(n),n+1<e.length&&(i.add(n+1),l=Number(e[n+1])),n++;continue}if(d==="--sim"||d==="--session"||d==="--tab"){i.add(n),n+1<e.length&&(i.add(n+1),t=e[n+1]?.trim()||void 0,m="flag"),n++;continue}let s=d.startsWith("--sim=")?d.slice(6):d.startsWith("--session=")?d.slice(10):d.startsWith("--tab=")?d.slice(6):void 0;if(s!==void 0){i.add(n),t=s.trim()||void 0,m="flag";continue}if(h.has(d)){i.add(n);continue}b.has(d)&&(i.add(n),n+1<e.length&&i.add(n+1),n++)}if(!t){let n=T();n&&(t=n.trim()||void 0,m="saved")}return{positional:e.filter((n,d)=>!i.has(d)),wsPort:a,simId:t,simIdSource:m,commandTimeoutMs:l}}function E(e,r={}){let i=1,a=r.commandTimeoutMs??15e3,l=new Map,t=new I(`ws://localhost:${e}`),m=P(),h=new Promise((s,o)=>{t.on("open",()=>{try{t.send(JSON.stringify({type:"bridge:hello",id:0,cliIdentityKey:m.key,cliIdentitySource:m.source,cliLabel:r.cliLabel}))}catch{}s()}),t.on("error",c=>{let g=c.message?`: ${c.message}`:"";o(new Error(`could not connect to ws://localhost:${e}${g}`))})}),b=!1,n=!1;function d(s,o){if(n||process.env.SOOTSIM_QUIET_TARGET_NOTICE==="1"||s.startsWith("bridge:")||s==="focus"||s==="close")return;n=!0;let c=o??"primary",g=$(o?r.simIdSource:"none");process.stderr.write(` \u2192 ${c} (${g})
1
+ /*! sootsim v0.1.141 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{a as P,d as T,f as C}from"./chunk-A5W3HB44.js";import{G as k,H as v}from"./chunk-7SO2SJ4H.js";import{WebSocket as I}from"ws";function x(){let e=k();return e&&v(e)?e.bridgePort:7668}var w=class extends Error{lock;constructor(r,i){super(r),this.name="BridgeSimLockedError",this.lock=i}};function $(e){return e==="flag"?"via --sim":e==="saved"?"saved via `sootsim use`":"primary fallback \u2014 no sim pinned"}function N(e,r={}){let i=new Set,a=r.port??x(),l=r.commandTimeoutMs??15e3,t,m="none",h=new Set(r.stripBooleanFlags??[]),b=new Set(r.stripValueFlags??[]);for(let n=0;n<e.length;n++){let d=e[n];if(d==="--port"||d==="-p"){i.add(n),n+1<e.length&&(i.add(n+1),a=Number(e[n+1])),n++;continue}if(d.startsWith("--port=")){i.add(n),a=Number(d.slice(7));continue}if(d==="--timeout"){i.add(n),n+1<e.length&&(i.add(n+1),l=Number(e[n+1])),n++;continue}if(d==="--sim"||d==="--session"||d==="--tab"){i.add(n),n+1<e.length&&(i.add(n+1),t=e[n+1]?.trim()||void 0,m="flag"),n++;continue}let s=d.startsWith("--sim=")?d.slice(6):d.startsWith("--session=")?d.slice(10):d.startsWith("--tab=")?d.slice(6):void 0;if(s!==void 0){i.add(n),t=s.trim()||void 0,m="flag";continue}if(h.has(d)){i.add(n);continue}b.has(d)&&(i.add(n),n+1<e.length&&i.add(n+1),n++)}if(!t){let n=T();n&&(t=n.trim()||void 0,m="saved")}return{positional:e.filter((n,d)=>!i.has(d)),wsPort:a,simId:t,simIdSource:m,commandTimeoutMs:l}}function E(e,r={}){let i=1,a=r.commandTimeoutMs??15e3,l=new Map,t=new I(`ws://localhost:${e}`),m=P(),h=new Promise((s,o)=>{t.on("open",()=>{try{t.send(JSON.stringify({type:"bridge:hello",id:0,cliIdentityKey:m.key,cliIdentitySource:m.source,cliLabel:r.cliLabel}))}catch{}s()}),t.on("error",c=>{let g=c.message?`: ${c.message}`:"";o(new Error(`could not connect to ws://localhost:${e}${g}`))})}),b=!1,n=!1;function d(s,o){if(n||process.env.SOOTSIM_QUIET_TARGET_NOTICE==="1"||s.startsWith("bridge:")||s==="focus"||s==="close")return;n=!0;let c=o??"primary",g=$(o?r.simIdSource:"none");process.stderr.write(` \u2192 ${c} (${g})
3
3
  `)}return t.on("message",s=>{let o;try{o=JSON.parse(s.toString())}catch{return}if(o.id===0)return;let c=l.get(o.id);c&&(l.delete(o.id),o._otherCliCount>0&&!b&&(b=!0,process.stderr.write(`
4
4
  \u26A0 ${o._otherCliCount} other CLI identity/identities are driving this sim
5
5
  taps, scrolls, and keyboard input from multiple agents will collide on
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.139 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{c as i,e as d,h as c}from"./chunk-T367DMWW.js";function g(r){let e=r[0]?.toLowerCase()??"toggle";return e!=="on"&&e!=="off"&&e!=="toggle"&&(console.error(` unknown argument: "${e}" (expected on | off | toggle)`),process.exit(1)),e}async function u(r,e,t){let l=i(r,{port:e.port}),s=g(l.positional),a=d({...l,commandTimeoutMs:5e3});try{let n=!!(await c(a,"SootSim.bridges.settings.get"))?.[t.modeKey],o=s==="on"?!0:s==="off"?!1:!n;if(n===o)return console.log(` ${t.displayName}: already ${o?"on":"off"}`),{current:n,target:o,changed:!1};if(!await a.send({type:"evaluate",acquireLock:!0,code:`(async () => {
1
+ /*! sootsim v0.1.141 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{c as i,e as d,h as c}from"./chunk-JQTHQOPS.js";function g(r){let e=r[0]?.toLowerCase()??"toggle";return e!=="on"&&e!=="off"&&e!=="toggle"&&(console.error(` unknown argument: "${e}" (expected on | off | toggle)`),process.exit(1)),e}async function u(r,e,t){let l=i(r,{port:e.port}),s=g(l.positional),a=d({...l,commandTimeoutMs:5e3});try{let n=!!(await c(a,"SootSim.bridges.settings.get"))?.[t.modeKey],o=s==="on"?!0:s==="off"?!1:!n;if(n===o)return console.log(` ${t.displayName}: already ${o?"on":"off"}`),{current:n,target:o,changed:!1};if(!await a.send({type:"evaluate",acquireLock:!0,code:`(async () => {
3
3
  const modeKey = ${JSON.stringify(t.modeKey)}
4
4
  const target = ${JSON.stringify(o)}
5
5
  window.dispatchEvent(new CustomEvent('sootsim:shell-command', {
@@ -1,2 +1,2 @@
1
- /*! sootsim v0.1.139 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.141 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import{execFileSync as p,spawn as l}from"child_process";import{existsSync as s}from"fs";import{homedir as u}from"os";import{join as d}from"path";var g=["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Chromium.app/Contents/MacOS/Chromium","~/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge","~/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge","/Applications/Brave Browser.app/Contents/MacOS/Brave Browser","~/Applications/Brave Browser.app/Contents/MacOS/Brave Browser","/Applications/Arc.app/Contents/MacOS/Arc","~/Applications/Arc.app/Contents/MacOS/Arc"],f=["/usr/bin/google-chrome","/usr/bin/chromium","/usr/bin/chromium-browser","/usr/bin/microsoft-edge","/usr/bin/brave-browser","/snap/bin/chromium"],h=["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe","C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe"],C=["google-chrome","chromium","chromium-browser","microsoft-edge","brave-browser"],O=["chrome","msedge","brave"];function w(r){return r.startsWith("~/")?d(u(),r.slice(2)):r}function a(r){for(let n of r){let o=w(n);if(s(o))return o}return null}function U(r,n){try{let e=p(n==="win32"?"where":"which",[r],{encoding:"utf8",timeout:1500,stdio:["ignore","pipe","ignore"]}).trim();return e?e.split(/\r?\n/)[0]:null}catch{return null}}function M(r=process.platform){let n=process.env.CHROME_PATH||process.env.CHROMIUM_PATH;if(n&&s(n))return n;let o=a(r==="darwin"?g:r==="win32"?h:f);if(o)return o;let e=r==="win32"?O:C;for(let t of e){let i=U(t,r);if(i)return i}return null}function A(r,n={}){if(!r)throw new Error("openUrl requires a url");let o=n.platform??process.platform;return n.newWindow?c(r,{...n,platform:o,newWindow:!0}):o==="darwin"?{command:"open",args:n.background===!1?[r]:["-g",r],via:"system"}:o==="win32"?{command:"cmd",args:["/c","start","",r],via:"system"}:{command:"xdg-open",args:[r],via:"system"}}function c(r,n={}){if(!r)throw new Error("openUrl requires a url");let o=n.platform??process.platform,e="chromiumBinary"in n?n.chromiumBinary:M(o);if(!e)throw new Error("browser launch requires Chrome, Chromium, Edge, Brave, or Arc");let t=[];return n.newWindow!==!1&&t.push("--new-window"),t.push(r),{command:e,args:t,via:"chromium",target:e}}async function m(r,n,o){return new Promise((e,t)=>{let i=l(r,n,{detached:o,stdio:"ignore"});i.once("error",t),i.once("spawn",()=>{o&&i.unref(),e(i.pid)})})}async function x(r,n={}){let o=A(r,n),e=await m(o.command,o.args,n.detached??!0);return{...o,pid:e,attachUrl:r}}async function y(r,n={}){let o=c(r,n),e=await m(o.command,o.args,n.detached??!0);return{...o,pid:e,attachUrl:r}}async function E(r,n={}){await x(r,n)}export{M as a,x as b,y as c,E as d};
@@ -0,0 +1,79 @@
1
+ /*! sootsim v0.1.141 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{d as ue,e as de,f as pe}from"./chunk-I6SWX7OI.js";import{d as ce}from"./chunk-MENSR2TZ.js";import{c as ae,e as le,g as P}from"./chunk-JQTHQOPS.js";import{d as ie}from"./chunk-LHIPDN4B.js";import{a as se,b}from"./chunk-ITJLXD4X.js";import{a as oe}from"./chunk-DM5KNA7Q.js";import{createHash as He}from"crypto";import{readFileSync as K}from"fs";import{gzipSync as Ce}from"zlib";function fe(e){let t=(e||"").toLowerCase().split(";")[0].trim();return t?!(t.startsWith("image/")||t.startsWith("video/")||t.startsWith("audio/")||t==="font/woff2"||t==="application/font-woff2"||t==="application/zip"||t==="application/gzip"):!0}import{brotliCompressSync as Le,constants as Ee}from"node:zlib";var ke=9;function ge(e){return Le(e,{params:{[Ee.BROTLI_PARAM_QUALITY]:ke}})}function G(e,t){let r=new AbortController,o=setTimeout(()=>r.abort(new Error(`${t} timed out after ${e/1e3}s`)),e);return{signal:r.signal,cancel:()=>clearTimeout(o)}}async function Ae(e){let t=G(12e4,`PUT ${e.url}`),r;try{r=await fetch(e.url,{method:"PUT",headers:{"content-type":e.contentType},body:e.bytes,signal:t.signal})}finally{t.cancel()}if(!r.ok){let o=await r.text().catch(()=>"");throw new Error(`PUT ${e.url} failed ${r.status}: ${o.slice(0,200)}`)}}async function Fe(e,t=8,r){let o=0,i=0,a=e.length;await Promise.all(Array.from({length:Math.min(t,a)},async()=>{for(;o<a;){let c=e[o++];await Ae(c),i++,r?.(i,a)}}))}function ze(e,t,r,o){let i=[];i.push({url:e.bundle.putUrl,contentType:e.bundle.contentType,bytes:t});for(let a of e.files){let c=r.get(a.urlhash);c&&i.push({url:a.putUrl,contentType:a.contentType,bytes:c})}e.events&&o?.eventsBytes&&i.push({url:e.events.putUrl,contentType:e.events.contentType,bytes:o.eventsBytes}),e.flowVideo&&o?.videoBytes&&i.push({url:e.flowVideo.putUrl,contentType:e.flowVideo.contentType,bytes:o.videoBytes}),e.storage&&o?.storageBytes&&i.push({url:e.storage.putUrl,contentType:e.storage.contentType,bytes:o.storageBytes});for(let a of e.snapshots??[]){let c=o?.snapshotBytes?.get(a.id);c&&i.push({url:a.putUrl,contentType:a.contentType,bytes:c})}return i}function Me(e){return e.replace(/\/+$/,"")}var F=class extends Error{constructor(r,o,i,a=""){super(r);this.status=o;this.phase=i;this.responseText=a;this.name="PresignedUploadError"}};async function he(e){let t=Me(e.originBase),r={"content-type":"application/json"};e.authHeader&&(r.authorization=e.authHeader),e.adminBypassToken&&(r["x-soot-admin-upload-bypass"]=e.adminBypassToken);let o=G(3e4,"init"),i;try{i=await fetch(`${t}/api/preview/upload/init`,{method:"POST",headers:r,body:JSON.stringify(e.initBody),signal:o.signal})}finally{o.cancel()}if(!i.ok){let p=await i.text().catch(()=>"");throw new F(`init failed: ${i.status} ${p.slice(0,200)}`,i.status,"init",p)}let a=await i.json(),c=ze(a,e.bundleBytes,e.filesByHash,e.extras);await Fe(c,e.concurrency??8,e.onPutProgress);let y=G(3e4,"finalize"),u;try{u=await fetch(`${t}/api/preview/upload/finalize`,{method:"POST",headers:r,body:JSON.stringify({token:a.token}),signal:y.signal})}finally{y.cancel()}if(!u.ok){let p=await u.text().catch(()=>"");throw new F(`finalize failed: ${u.status} ${p.slice(0,200)}`,u.status,"finalize",p)}let S=await u.json();return{init:a,finalize:S}}function Ne(e){if(!e)return null;try{let t=JSON.parse(e);return typeof t.message=="string"&&t.message?t.message:null}catch{return null}}var be=process.env.SOOTSIM_UPLOAD_ORIGIN||"https://sootbean.com",ve=process.env.SOOTSIM_PREVIEW_ORIGIN||"https://sootsim.com",J="http://localhost:3000",We=2e3;async function Ve(e){if(e)return e;if(process.env.SOOTSIM_UPLOAD_ORIGIN)return process.env.SOOTSIM_UPLOAD_ORIGIN;try{let t=await fetch(`${J}/api/preview/upload/init`,{method:"OPTIONS",signal:AbortSignal.timeout(We)});if(t.ok||t.status===204||t.status===405)return J}catch{}return be}function O(e){return e.replace(/\/$/,"")}function qe(e){try{let r=new URL(e).hostname.replace(/^\[|\]$/g,"").toLowerCase();return r==="localhost"||r.endsWith(".localhost")||r==="0.0.0.0"||r==="::1"||/^127(?:\.\d{1,3}){3}$/.test(r)}catch{return!1}}function De(e){try{let t=new URL(e).hostname.toLowerCase();return t==="sootbean.com"||t.endsWith(".sootbean.com")}catch{return!1}}function Ge(e,t){return t?O(t):process.env.SOOTSIM_PREVIEW_ORIGIN?O(process.env.SOOTSIM_PREVIEW_ORIGIN):qe(e)?O(e):De(e)?O(ve):O(e)}function Ke(){console.log(`
3
+ sootsim upload \u2014 publish the current recorded bundle as a /preview/<id> link
4
+
5
+ usage:
6
+ sootsim upload [--origin <url>] [--public-origin <url>] (--events <path> | --video <path>) [--screenshot <path>] [--owner <org> --repo <repo>] [--sim <sim>] [--open] [--assets-only]
7
+
8
+ options:
9
+ --origin <url> upload target (default: auto)
10
+ prefers ${J} when available, otherwise
11
+ falls back to ${be}
12
+ override with SOOTSIM_UPLOAD_ORIGIN env var
13
+ --public-origin <url>
14
+ public /preview link origin (default: ${ve}
15
+ for prod uploads, upload origin for localhost/custom origins).
16
+ override with SOOTSIM_PREVIEW_ORIGIN env var
17
+ --events <path> path to a gzipped events .jsonl.gz file to attach
18
+ required unless --video is present; previews must
19
+ have replay or recording playback data
20
+ --screenshot <path>
21
+ path to a PNG thumbnail to attach to the share
22
+ --video <path> path to a mp4/gif flow recording. embedded inline
23
+ in the pr sticky comment served at
24
+ /api/preview/flow-video?id=<share-id>
25
+ --video-duration-ms <ms>
26
+ duration hint for attached flow video
27
+ --owner <org> associate a session upload with a linked org repo
28
+ --repo <repo> repo name for --owner; the signed-in user must belong
29
+ to the local/org team that owns the link
30
+ --sim <sim> target a specific sim (see: sootsim list)
31
+ --open open the resulting /preview/<id> url in the browser
32
+ --assets-only drop API/JSON/HTML records before upload; keep images,
33
+ fonts, css, js, and binary blobs. live-data demos hit
34
+ the real network at replay time instead of serving
35
+ recorded API snapshots
36
+ -h, --help
37
+
38
+ examples:
39
+ sootsim upload --events ./my-session.jsonl.gz
40
+ sootsim upload --origin http://localhost:3000 --events ./my-session.jsonl.gz --open
41
+ sootsim upload --video /tmp/soot-flow.mp4 --video-duration-ms 12000
42
+ `)}function v(e,t){let r=e.findIndex(i=>i===t);if(r<0)return;let o=e[r+1];return e.splice(r,2),o}function ye(e,t){let r=e.findIndex(o=>o===t);return r<0?!1:(e.splice(r,1),!0)}function me(e){let t=(e||"").toLowerCase().split(";")[0].trim();return t?!!(t.startsWith("image/")||t.startsWith("font/")||t.startsWith("video/")||t.startsWith("audio/")||t.startsWith("model/")||t==="text/css"||t==="application/javascript"||t==="text/javascript"||t==="application/wasm"||t==="application/font-woff"||t==="application/font-woff2"||t==="application/octet-stream"):!1}async function we(e){let t=await fetch(e);if(!t.ok)throw new Error(`fetch ${e} -> ${t.status} ${t.statusText}`);return new Uint8Array(await t.arrayBuffer())}function je(e){return/\.bundle($|\?)/.test(e)||/\.js($|\?)/.test(e)?"application/javascript":/\.zip($|\?)/.test(e)?"application/zip":"application/javascript"}function Je(e){try{return new URL(e).searchParams.get("appFonts")||""}catch{return""}}function Ye(e){try{let t=new URL(e);return t.searchParams.get("lazy")==="true"?(t.searchParams.set("lazy","false"),t.toString()):e}catch{return e}}async function Qe(e,t){let o=[];for(let i=0;i<t;i+=15e5){let a=Math.min(i+15e5,t),c=await P(e,`(window.__sootsimLastTransformedBundle?.text || "").slice(${i}, ${a})`);if(typeof c!="string")return null;o.push(c)}return o.join("")}function j(e){let t=new Map;for(let r of e)t.set(r.urlhash||r.url,r);return Array.from(t.values())}async function ht(e,t){(e.includes("--help")||e.includes("-h"))&&(Ke(),process.exit(0));let r=[...e],o=await Ve(v(r,"--origin")),i=Ge(o,v(r,"--public-origin")),a=v(r,"--events"),c=v(r,"--screenshot"),y=v(r,"--video"),u=v(r,"--video-duration-ms"),S=v(r,"--owner"),p=v(r,"--repo"),Ue=ye(r,"--open");(S&&!p||!S&&p)&&(console.error(" --owner and --repo must be provided together"),process.exit(1)),!a&&!y&&(console.error(" preview uploads require playback data: pass --events or --video.\n for a normal local preview, use `sootsim record --mode combined --open`\n or `sootsim flow --preview` instead of uploading a bundle-only snapshot."),process.exit(1));let Y=ye(r,"--assets-only"),Be=ae(r,{stripBooleanFlags:[],stripValueFlags:[]}),U=le(Be),l,Q=[],m=[],z=null;try{if(l=await P(U,'(typeof window.__sootsimCaptureBundle === "function") ? window.__sootsimCaptureBundle() : null'),l?.bundleUrl){let n=new URL(l.bundleUrl).origin,[s,h]=await Promise.all([P(U,`(window.__sootsimPreviewRecorder?.list?.(${JSON.stringify(n)}) || [])`),P(U,`(async () => {
43
+ const list = window.__sootsimListWorkerFetchRecorder
44
+ return typeof list === 'function'
45
+ ? await list(${JSON.stringify(n)})
46
+ : []
47
+ })()`)]);Q=j([...s,...h]).filter(d=>{try{return!new URL(d.url).pathname.startsWith("/api/")}catch{return!0}});let E=await P(U,`(async () => {
48
+ const rec = window.__sootsimPreviewRecorder
49
+ const workerList = window.__sootsimListWorkerFetchRecorder
50
+ return {
51
+ main: rec?.list ? rec.list() : [],
52
+ worker: typeof workerList === 'function' ? await workerList() : [],
53
+ }
54
+ })()`),_=(E?j([...E.main,...E.worker]):[]).filter(d=>{try{return new URL(d.url).origin!==n}catch{return!1}}),T=_.length>0?{count:_.length,totalBytes:_.reduce((d,x)=>d+(x.size||0),0)}:null;if(T&&T.count>0){console.log(` ${T.count} recorded cross-origin responses (${(T.totalBytes/1024).toFixed(1)} KiB)`);let d=await P(U,`(async () => {
55
+ const rec = window.__sootsimPreviewRecorder
56
+ const workerDump = window.__sootsimDumpWorkerFetchRecorder
57
+ const bundleUrl = ${JSON.stringify(l.bundleUrl)}
58
+ const keep = (r) => {
59
+ try {
60
+ const u = new URL(r.url)
61
+ if (u.origin !== ${JSON.stringify(n)}) return true
62
+ // bundle-origin api responses came from the rewritten
63
+ // /__app-api proxy at record time (re-keyed by the
64
+ // recorder's unwrap step). they need bodies attached the
65
+ // same way as cross-origin records \u2014 direct re-fetch from
66
+ // the dev server lacks the bundle's session cookies.
67
+ if (r.url !== bundleUrl && u.pathname.startsWith('/api/')) return true
68
+ return false
69
+ } catch { return false }
70
+ }
71
+ return {
72
+ main: rec?.dump ? rec.dump().filter(keep) : [],
73
+ worker: typeof workerDump === 'function'
74
+ ? (await workerDump()).filter(keep)
75
+ : [],
76
+ }
77
+ })()`);if(m=j([...d.main,...d.worker]),Y){let x=m.length;m=m.filter(D=>me(D.contentType));let q=T.totalBytes-m.reduce((D,$e)=>D+($e.size||0),0);console.log(` --assets-only: kept ${m.length}/${x} records (dropped ${(q/1024).toFixed(1)} KiB of API responses)`)}}}l?.transformedBundle&&(z=await Qe(U,l.transformedBundle.byteLength))}finally{U.close()}l||(console.error(" could not read bundle snapshot \u2014 is sootsim running and is the bundle loaded?"),process.exit(2)),l.bundleUrl||(console.error(` no ?bundle= URL on the current sootsim tab.
78
+ open the app you want to share first (e.g. sootsim open 8082), then run upload.`),process.exit(2));let f,M=!1;if(z!==null)f=new TextEncoder().encode(z),M=!0,console.log(` using post-transform bundle: ${(f.byteLength/1024).toFixed(1)} KiB`);else{let n=Ye(l.bundleUrl);n!==l.bundleUrl&&console.log(" forcing lazy=false for self-contained bundle"),console.log(` capturing: ${n}`),f=await we(n),console.log(` main bundle: ${(f.byteLength/1024).toFixed(1)} KiB`)}let k=Q.filter(n=>n.url!==l.bundleUrl);Y&&(k=k.filter(n=>me(n.contentType))),console.log(` fetching ${k.length} extra files\u2026`);let A=(await Promise.all(k.map(async n=>{try{let s=await we(n.url);return{...n,bytes:s}}catch(s){return console.error(` warning: failed to re-fetch ${n.url}: ${s instanceof Error?s.message:s}`),null}}))).filter(n=>!!n),Te=A.reduce((n,s)=>n+s.bytes.byteLength,0);console.log(` ${A.length} extra files: ${(Te/1024).toFixed(1)} KiB`);let B;a&&(B=K(a));let g;c&&(g=K(c),console.log(` attaching screenshot: ${(g.byteLength/1024).toFixed(1)} KiB`));let w,$,X;if(y){if(w=K(y),$=y.endsWith(".mp4")?"video/mp4":y.endsWith(".gif")?"image/gif":void 0,$||(console.error(" preview flow videos must be mp4 or gif"),await b(),process.exit(1)),u){let n=Number(u);(!Number.isFinite(n)||n<=0)&&(console.error(` invalid --video-duration-ms: ${u}`),await b(),process.exit(1)),X=Math.round(n)}console.log(` attaching flow video: ${(w.byteLength/1024).toFixed(1)} KiB (${$})`)}let I=ue(),R=pe(I),Pe=oe();se({event:"preview_upload_started",identity:{userId:I?.kind==="session"?Pe?.user?.id??null:null,repoId:R?.repoId??null,installationId:R?.installationId??null},properties:{origin:o,hasAuth:!!I,authMode:I?.kind??"none",bundleBytes:f.byteLength,isTransformed:M,extraFiles:A.length,recordedFetches:m.length,hasEvents:!!B,hasScreenshot:!!g,hasVideo:!!w}}),I||(console.error(" preview uploads need auth."),console.error(" set SOOTSIM_API_KEY=sk_sootsim_..., run `sootsim login`, or use the soot github runner."),await b(),process.exit(1));let Ie=de(I),Re=(()=>{try{return new URL(l.bundleUrl).origin}catch{return null}})(),H=(n,s,h,E,re,_,T,d)=>{let x=fe(_),q=x?Ce(n):n;return{url:s,urlhash:h,method:E,bodyHash:re,requestVaryHash:d,contentType:_,responseHeaders:T,bytes:q,encoding:x?"gzip":void 0}},Z=[],C=Je(l.pageHref)||"";if(C){let n=await ce(C,{onStaged:({url:s,byteLength:h})=>console.log(` staged app font: ${s} (${(h/1024).toFixed(1)} KiB)`),onError:(s,h)=>console.error(` warning: failed to fetch app font ${s}: ${h instanceof Error?h.message:h}`)});for(let s of n)Z.push(H(Buffer.from(s.bytes),s.url,s.urlhash,"GET","-",s.contentType))}let N=[...A.map(n=>H(n.bytes,n.url,n.urlhash,"GET","-",n.contentType,n.responseHeaders,n.requestVaryHash)),...m.map(n=>H(Buffer.from(n.bodyBase64,"base64"),n.url,n.urlhash,n.method||"GET",n.bodyHash||"-",n.contentType,n.responseHeaders,n.requestVaryHash)),...Z],W=ge(f),_e=He("sha256").update(f).digest("hex");console.log(` bundle raw=${(f.byteLength/1024).toFixed(1)} KiB br=${(W.byteLength/1024).toFixed(1)} KiB`);let xe={contentHash:_e,bundleSizeBytes:f.byteLength,bundleContentType:je(l.bundleUrl),bundleEncoding:"br",bundleOrigin:Re,runtimeVersion:l.runtimeVersion??void 0,appFonts:C||void 0,entry:l.entry,isTransformed:M,deviceSpec:l.deviceSpec,installationId:R?.installationId??void 0,repoId:R?.repoId,owner:S??R?.owner,repo:p??R?.repo,files:N.map(n=>({url:n.url,urlhash:n.urlhash,method:n.method,bodyHash:n.bodyHash,contentType:n.contentType,responseHeaders:n.responseHeaders,requestVaryHash:n.requestVaryHash,encoding:n.encoding,sizeBytes:n.bytes.byteLength})),events:B?{sizeBytes:B.byteLength}:void 0,snapshots:g?[{id:"landing",label:"Landing",kind:"landing",t:0,contentType:"image/png",sizeBytes:g.byteLength}]:void 0,flowVideo:w&&$?{sizeBytes:w.byteLength,contentType:$,durationMs:X}:void 0},ee=new Map;for(let n of N)ee.set(n.urlhash,n.bytes);let te=new Map;g&&te.set("landing",g);let Oe=Date.now(),L=W.byteLength;for(let n of N)L+=n.bytes.byteLength;B&&(L+=B.byteLength),g&&(L+=g.byteLength),w&&(L+=w.byteLength);let Se=`${O(o)}/api/preview/upload/init`;console.log(` init: ${Se}`);let V;try{let n=await he({originBase:o,initBody:xe,bundleBytes:W,filesByHash:ee,extras:{eventsBytes:B,videoBytes:w,snapshotBytes:te},authHeader:Ie,concurrency:16});V=n.finalize;let s=1+n.init.files.length+(n.init.events?1:0)+n.init.snapshots.length+(n.init.flowVideo?1:0);console.log(` PUT ${s} objects in ${Date.now()-Oe}ms (${(L/1024).toFixed(1)} KiB)`)}catch(n){let s=n;s.phase==="init"&&s.status===401&&(console.error(" preview upload requires a valid login."),console.error(" run `sootsim login` and retry."),await b(),process.exit(1)),s.status===402&&(console.error(` ${Ne(s.responseText)??"preview uploads require Personal, Team, or an active trial \u2014 upgrade from the billing dialog."}`),await b(),process.exit(1)),console.error(` ${s.message??`${s.phase??"upload"} failed: ${String(n)}`}`),await b(),process.exit(1)}let ne=`${i}${V.url}`;console.log(`
79
+ stored ${V.filesStored??0} extra files`),console.log(` preview: ${ne}`),Ue&&await ie(ne),await b()}export{Ve as a,Ge as b,ht as c};