sootsim 0.1.36 → 0.1.37

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 (161) hide show
  1. package/README.md +20 -5
  2. package/dist-cli/bin.js +15 -20
  3. package/dist-cli/chunks/{agent-YZB6D3DR.js → agent-EQRQGSBL.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-VHCVS22I.js → agent-wrapper-AWKZ67GN.js} +10 -10
  5. package/dist-cli/chunks/{assert-AIVCKKLG.js → assert-ZVGELUZB.js} +2 -2
  6. package/dist-cli/chunks/auto-bootstrap-UEOLNAWJ.js +2 -0
  7. package/dist-cli/chunks/beta-4MD7WSI4.js +2 -0
  8. package/dist-cli/chunks/chunk-2ZPJHSIJ.js +11 -0
  9. package/dist-cli/chunks/{chunk-A5BRCXYE.js → chunk-4IO3D5XG.js} +1 -1
  10. package/dist-cli/chunks/chunk-4OHVCGMF.js +2 -0
  11. package/dist-cli/chunks/chunk-56BIMCDH.js +2 -0
  12. package/dist-cli/chunks/chunk-5FLDI6CV.js +66 -0
  13. package/dist-cli/chunks/{chunk-LHDWH7VS.js → chunk-B3RAGRK6.js} +1 -1
  14. package/dist-cli/chunks/{chunk-27P763IZ.js → chunk-BGAPLYMS.js} +2 -2
  15. package/dist-cli/chunks/chunk-CX3ZIPD3.js +3 -0
  16. package/dist-cli/chunks/{chunk-HWCKZXNJ.js → chunk-DSTV2VJT.js} +2 -2
  17. package/dist-cli/chunks/chunk-EDBFYOQB.js +2 -0
  18. package/dist-cli/chunks/chunk-ERLA3F77.js +1 -0
  19. package/dist-cli/chunks/chunk-FCQLQ7NA.js +117 -0
  20. package/dist-cli/chunks/chunk-H2HSOHXN.js +7 -0
  21. package/dist-cli/chunks/chunk-HYYMBXIX.js +2 -0
  22. package/dist-cli/chunks/chunk-JMGDVXAV.js +3 -0
  23. package/dist-cli/chunks/chunk-JMU5IGIU.js +1 -0
  24. package/dist-cli/chunks/chunk-KA5JJCWL.js +1 -0
  25. package/dist-cli/chunks/chunk-L4F4JRKJ.js +348 -0
  26. package/dist-cli/chunks/{chunk-G7XQD4KC.js → chunk-LDWXH43L.js} +2 -2
  27. package/dist-cli/chunks/chunk-PERKPZ7T.js +4 -0
  28. package/dist-cli/chunks/chunk-PN6FWLD4.js +5 -0
  29. package/dist-cli/chunks/chunk-QD7YIVPS.js +64 -0
  30. package/dist-cli/chunks/chunk-QWKO62QM.js +2 -0
  31. package/dist-cli/chunks/{chunk-VFDRZNPN.js → chunk-QXMZNJV5.js} +1 -1
  32. package/dist-cli/chunks/chunk-R77F5J3X.js +4 -0
  33. package/dist-cli/chunks/chunk-RLNIKWFO.js +27 -0
  34. package/dist-cli/chunks/chunk-RX6RHGSI.js +2 -0
  35. package/dist-cli/chunks/{chunk-IJMYFYDZ.js → chunk-S74RCIVB.js} +2 -2
  36. package/dist-cli/chunks/chunk-SK4SOISL.js +1 -0
  37. package/dist-cli/chunks/{chunk-YIO6S3R5.js → chunk-T5L73GJB.js} +1 -1
  38. package/dist-cli/chunks/{chunk-KAXZHEKM.js → chunk-UIQ3536J.js} +1 -1
  39. package/dist-cli/chunks/chunk-URSEYCC5.js +16 -0
  40. package/dist-cli/chunks/chunk-WFXYY3DU.js +3 -0
  41. package/dist-cli/chunks/{chunk-EWSQSALM.js → chunk-WHLHA5R5.js} +4 -4
  42. package/dist-cli/chunks/chunk-WLIVBPPY.js +3 -0
  43. package/dist-cli/chunks/{chunk-CYCXOAVZ.js → chunk-X6BP5JFC.js} +4 -4
  44. package/dist-cli/chunks/chunk-YFXTO4QX.js +5 -0
  45. package/dist-cli/chunks/{chunk-RMW5BO3S.js → chunk-Z5SVSAZO.js} +2 -2
  46. package/dist-cli/chunks/{chunk-OXN2PEB7.js → chunk-Z5X3PITK.js} +3 -3
  47. package/dist-cli/chunks/chunk-ZBOIGEGO.js +5 -0
  48. package/dist-cli/chunks/chunk-ZERYEI3L.js +17 -0
  49. package/dist-cli/chunks/{compat-Y2O2U7FL.js → compat-QQ3OJDBI.js} +2 -2
  50. package/dist-cli/chunks/{config-SRBOFUCI.js → config-LT27SC25.js} +2 -2
  51. package/dist-cli/chunks/control-3BO54QMO.js +2 -0
  52. package/dist-cli/chunks/cpu-profile-XEO3JCVB.js +22 -0
  53. package/dist-cli/chunks/daemon-3J2SAVQZ.js +83 -0
  54. package/dist-cli/chunks/{debug-BIDMW2PE.js → debug-OGQLIH4U.js} +4 -4
  55. package/dist-cli/chunks/demo-app-registry-5RZCXLWB.js +2 -0
  56. package/dist-cli/chunks/detox-Z2OSCIQU.js +49 -0
  57. package/dist-cli/chunks/device-RPTVD25S.js +16 -0
  58. package/dist-cli/chunks/diagnose-LAEXBNOQ.js +41 -0
  59. package/dist-cli/chunks/drivers-PSQUUAYC.js +2 -0
  60. package/dist-cli/chunks/electron-S2463O3P.js +18 -0
  61. package/dist-cli/chunks/flow-34YCVQDB.js +2 -0
  62. package/dist-cli/chunks/hints-E5PXPWFT.js +2 -0
  63. package/dist-cli/chunks/home-paths-F5SGBTRZ.js +2 -0
  64. package/dist-cli/chunks/inspect-EVGMEZ3G.js +1101 -0
  65. package/dist-cli/chunks/install-AM5PTJT3.js +2 -0
  66. package/dist-cli/chunks/{install-desktop-2MYEI4FM.js → install-desktop-ZNWYKTWQ.js} +3 -3
  67. package/dist-cli/chunks/{keys-7PNASIQR.js → keys-5ETF6DYO.js} +2 -2
  68. package/dist-cli/chunks/{launch-JNS47LAQ.js → launch-DHUCNFX6.js} +3 -3
  69. package/dist-cli/chunks/{login-YWZWUHBS.js → login-KDR34JIP.js} +4 -4
  70. package/dist-cli/chunks/{logout-O6SXMSBP.js → logout-R6WIJYCW.js} +2 -2
  71. package/dist-cli/chunks/maestro-ZOOJ2YVH.js +80 -0
  72. package/dist-cli/chunks/{preview-WGKJO5FS.js → preview-YFADHNBD.js} +2 -2
  73. package/dist-cli/chunks/profile-CQSC32HB.js +22 -0
  74. package/dist-cli/chunks/react-QSQD6CJE.js +30 -0
  75. package/dist-cli/chunks/{record-QPWLYH5R.js → record-IWLEYATN.js} +5 -5
  76. package/dist-cli/chunks/{runtime-KEMO2MSB.js → runtime-WKMNKYTN.js} +3 -3
  77. package/dist-cli/chunks/screenshot-VJXHV57I.js +28 -0
  78. package/dist-cli/chunks/screenshot-mode-FA4VQ76K.js +17 -0
  79. package/dist-cli/chunks/screenshots-U4FQXHVK.js +70 -0
  80. package/dist-cli/chunks/server-7WZLM5NQ.js +35 -0
  81. package/dist-cli/chunks/setup-repo-3BXLAX5E.js +2 -0
  82. package/dist-cli/chunks/{skills-MO7BFNVM.js → skills-KO7RCY24.js} +2 -2
  83. package/dist-cli/chunks/start-EBD7T2GW.js +23 -0
  84. package/dist-cli/chunks/store-ONX3EBS4.js +2 -0
  85. package/dist-cli/chunks/telemetry-MFR7TUW7.js +2 -0
  86. package/dist-cli/chunks/{test-XUI3KNNQ.js → test-OSVUG54G.js} +3 -3
  87. package/dist-cli/chunks/three-mode-MDBXZQG4.js +39 -0
  88. package/dist-cli/chunks/timeline-UJOKZKQR.js +22 -0
  89. package/dist-cli/chunks/upload-H2SMWP6T.js +2 -0
  90. package/dist-cli/chunks/what-happened-LFWH74FR.js +15 -0
  91. package/dist-cli/chunks/whoami-CUF56TLP.js +2 -0
  92. package/dist-lib/agent-daemon-client.cjs +4 -1
  93. package/dist-lib/agent-events.cjs +1 -1
  94. package/dist-lib/agent-sessions.cjs +41 -39
  95. package/dist-lib/attached-projects.cjs +30 -28
  96. package/dist-lib/auth/shared-session.cjs +35 -27
  97. package/dist-lib/backend-origin.cjs +1 -1
  98. package/dist-lib/bridge-constants.cjs +1 -1
  99. package/dist-lib/cli-constants.cjs +1 -1
  100. package/dist-lib/config.cjs +6 -2
  101. package/dist-lib/dev-bundle-resolution.cjs +5 -21
  102. package/dist-lib/home-paths.cjs +94 -38
  103. package/dist-lib/host/bridge-host.cjs +2131 -1333
  104. package/dist-lib/host/fetch-proxy-handler.cjs +248 -0
  105. package/dist-lib/index.cjs +21 -21
  106. package/dist-lib/metro.cjs +21 -21
  107. package/dist-lib/profiles.cjs +246 -0
  108. package/dist-lib/render-mode.cjs +1 -1
  109. package/dist-lib/vite-base.cjs +3402 -1640
  110. package/dist-lib/vite.cjs +1 -1
  111. package/package.json +7 -1
  112. package/dist-cli/chunks/auto-bootstrap-MLNTX23H.js +0 -2
  113. package/dist-cli/chunks/chunk-3UIWOHC2.js +0 -62
  114. package/dist-cli/chunks/chunk-5KGFHWVR.js +0 -1
  115. package/dist-cli/chunks/chunk-5QIUJNT3.js +0 -5
  116. package/dist-cli/chunks/chunk-6GGMKFWJ.js +0 -4
  117. package/dist-cli/chunks/chunk-6Z275LCY.js +0 -2
  118. package/dist-cli/chunks/chunk-75LBYBKW.js +0 -11
  119. package/dist-cli/chunks/chunk-DFN3GGH7.js +0 -5
  120. package/dist-cli/chunks/chunk-EBEHZJRG.js +0 -117
  121. package/dist-cli/chunks/chunk-EJLNUMMP.js +0 -3
  122. package/dist-cli/chunks/chunk-FE7UI3MT.js +0 -4
  123. package/dist-cli/chunks/chunk-G663654J.js +0 -1
  124. package/dist-cli/chunks/chunk-GW7XY5KC.js +0 -2
  125. package/dist-cli/chunks/chunk-H2QO4TDV.js +0 -22
  126. package/dist-cli/chunks/chunk-HWFHBMAQ.js +0 -27
  127. package/dist-cli/chunks/chunk-J7CTD37P.js +0 -1
  128. package/dist-cli/chunks/chunk-N32NCVL2.js +0 -3
  129. package/dist-cli/chunks/chunk-NIZBR7EK.js +0 -2
  130. package/dist-cli/chunks/chunk-NYY36OKU.js +0 -308
  131. package/dist-cli/chunks/chunk-PJL25JQV.js +0 -5
  132. package/dist-cli/chunks/chunk-SHO54NET.js +0 -2
  133. package/dist-cli/chunks/chunk-SMVJOWSV.js +0 -16
  134. package/dist-cli/chunks/chunk-TC6V7YFC.js +0 -3
  135. package/dist-cli/chunks/chunk-YLIIVTTQ.js +0 -3
  136. package/dist-cli/chunks/chunk-YR7BGGYE.js +0 -2
  137. package/dist-cli/chunks/chunk-ZEW3RF5Q.js +0 -1
  138. package/dist-cli/chunks/control-PL2V2O6S.js +0 -2
  139. package/dist-cli/chunks/daemon-IZC32PZW.js +0 -50
  140. package/dist-cli/chunks/demo-app-registry-5JFOUU3D.js +0 -2
  141. package/dist-cli/chunks/detox-B3FDOIS3.js +0 -49
  142. package/dist-cli/chunks/device-ZZSI363W.js +0 -16
  143. package/dist-cli/chunks/drivers-S4NGK4DB.js +0 -2
  144. package/dist-cli/chunks/electron-5YFHXEOI.js +0 -15
  145. package/dist-cli/chunks/flow-JJBO6TFY.js +0 -2
  146. package/dist-cli/chunks/hints-G5HBBV2O.js +0 -2
  147. package/dist-cli/chunks/home-paths-VWC3FWA3.js +0 -2
  148. package/dist-cli/chunks/inspect-POOPWUQI.js +0 -1034
  149. package/dist-cli/chunks/install-MP6FHXNZ.js +0 -2
  150. package/dist-cli/chunks/install-dev-desktop-SKH3KEHY.js +0 -100
  151. package/dist-cli/chunks/maestro-CW6XVUKV.js +0 -75
  152. package/dist-cli/chunks/profile-SUOBRPIC.js +0 -22
  153. package/dist-cli/chunks/screenshot-JTY46V7G.js +0 -26
  154. package/dist-cli/chunks/screenshot-mode-7OYBBX6D.js +0 -17
  155. package/dist-cli/chunks/screenshots-QISKC4GD.js +0 -70
  156. package/dist-cli/chunks/server-YSFJAKAV.js +0 -34
  157. package/dist-cli/chunks/setup-repo-LFB3HBEO.js +0 -2
  158. package/dist-cli/chunks/store-6MFL53I4.js +0 -2
  159. package/dist-cli/chunks/telemetry-CN42GMVC.js +0 -2
  160. package/dist-cli/chunks/upload-6FUT7AX5.js +0 -2
  161. package/dist-cli/chunks/whoami-TQFHY42N.js +0 -2
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.37 | (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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sootsim",
3
- "version": "0.1.36",
3
+ "version": "0.1.37",
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",
@@ -34,6 +34,7 @@
34
34
  "default": "./dist-lib/metro.cjs"
35
35
  },
36
36
  "./skills": "./src/skills/registry.ts",
37
+ "./beta": "./src/beta.ts",
37
38
  "./cli/registry": "./cli/registry.ts",
38
39
  "./agent-events": {
39
40
  "source": "./src/agent-events.ts",
@@ -67,6 +68,10 @@
67
68
  "source": "./src/home-paths.ts",
68
69
  "default": "./dist-lib/home-paths.cjs"
69
70
  },
71
+ "./profiles": {
72
+ "source": "./src/profiles.ts",
73
+ "default": "./dist-lib/profiles.cjs"
74
+ },
70
75
  "./dev-bundle-resolution": {
71
76
  "source": "./src/dev-bundle-resolution.ts",
72
77
  "default": "./dist-lib/dev-bundle-resolution.cjs"
@@ -79,6 +84,7 @@
79
84
  "source": "./src/host/bridge-host.ts",
80
85
  "default": "./dist-lib/host/bridge-host.cjs"
81
86
  },
87
+ "./host/fetch-proxy-handler": "./src/host/fetch-proxy-handler.ts",
82
88
  "./scripts/dev-server-scanner": "./scripts/dev-server-scanner.ts",
83
89
  "./scripts/demo-app-registry": "./scripts/demo-app-registry.ts",
84
90
  "./detox": "./detox/index.ts",
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d}from"./chunk-TC6V7YFC.js";import"./chunk-SMVJOWSV.js";import"./chunk-6GGMKFWJ.js";import"./chunk-ZEW3RF5Q.js";import"./chunk-5QIUJNT3.js";import"./chunk-LHDWH7VS.js";export{c as ensureDaemonRunning,a as ensureRuntimeInstalled,d as ensureSootsimReady,b as resolveBootstrapPort};
@@ -1,62 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{d as q,e as Y}from"./chunk-YLIIVTTQ.js";import{a as W,b as m}from"./chunk-IJMYFYDZ.js";import{a as J}from"./chunk-EJLNUMMP.js";import{d as V}from"./chunk-A5BRCXYE.js";import{c as D,e as G,g as b}from"./chunk-SMVJOWSV.js";import{createHash as we}from"crypto";import{readFileSync as Z}from"fs";import{gzipSync as ee}from"zlib";function Q(e){let n=(e||"").toLowerCase().split(";")[0].trim();return n?!(n.startsWith("image/")||n.startsWith("video/")||n.startsWith("audio/")||n==="font/woff2"||n==="application/font-woff2"||n==="application/zip"||n==="application/gzip"):!0}function L(e,n){let r=new AbortController,s=setTimeout(()=>r.abort(new Error(`${n} timed out after ${e/1e3}s`)),e);return{signal:r.signal,cancel:()=>clearTimeout(s)}}async function he(e){let n=L(12e4,`PUT ${e.url}`),r;try{r=await fetch(e.url,{method:"PUT",headers:{"content-type":e.contentType},body:e.bytes,signal:n.signal})}finally{n.cancel()}if(!r.ok){let s=await r.text().catch(()=>"");throw new Error(`PUT ${r.status}: ${s.slice(0,200)}`)}}async function ye(e,n=8,r){let s=0,i=0,a=e.length;await Promise.all(Array.from({length:Math.min(n,a)},async()=>{for(;s<a;){let u=e[s++];await he(u),i++,r?.(i,a)}}))}function me(e,n,r,s){let i=[];i.push({url:e.bundle.putUrl,contentType:e.bundle.contentType,bytes:n});for(let a of e.files){let u=r.get(a.urlhash);u&&i.push({url:a.putUrl,contentType:a.contentType,bytes:u})}return e.events&&s?.eventsBytes&&i.push({url:e.events.putUrl,contentType:e.events.contentType,bytes:s.eventsBytes}),e.flowVideo&&s?.videoBytes&&i.push({url:e.flowVideo.putUrl,contentType:e.flowVideo.contentType,bytes:s.videoBytes}),e.storage&&s?.storageBytes&&i.push({url:e.storage.putUrl,contentType:e.storage.contentType,bytes:s.storageBytes}),i}function be(e){return e.replace(/\/+$/,"")}var $=class extends Error{constructor(r,s,i){super(r);this.status=s;this.phase=i;this.name="PresignedUploadError"}};async function X(e){let n=be(e.originBase),r={"content-type":"application/json"};e.authHeader&&(r.authorization=e.authHeader);let s=L(3e4,"init"),i;try{i=await fetch(`${n}/api/preview/upload/init`,{method:"POST",headers:r,body:JSON.stringify(e.initBody),signal:s.signal})}finally{s.cancel()}if(!i.ok){let c=await i.text().catch(()=>"");throw new $(`init failed: ${i.status} ${c.slice(0,200)}`,i.status,"init")}let a=await i.json(),u=me(a,e.bundleBytes,e.filesByHash,e.extras);await ye(u,e.concurrency??8,e.onPutProgress);let B=L(3e4,"finalize"),p;try{p=await fetch(`${n}/api/preview/upload/finalize`,{method:"POST",headers:r,body:JSON.stringify({token:a.token}),signal:B.signal})}finally{B.cancel()}if(!p.ok){let c=await p.text().catch(()=>"");throw new $(`finalize failed: ${p.status} ${c.slice(0,200)}`,p.status,"finalize")}let z=await p.json();return{init:a,finalize:z}}var se=process.env.SOOTSIM_UPLOAD_ORIGIN||"https://sootbean.com",E="http://localhost:3000";async function ve(e){if(e)return e;if(process.env.SOOTSIM_UPLOAD_ORIGIN)return process.env.SOOTSIM_UPLOAD_ORIGIN;try{let n=await fetch(`${E}/api/preview/upload/init`,{method:"OPTIONS"});if(n.ok||n.status===204||n.status===405)return E}catch{}return se}function Be(){console.log(`
3
- sootsim upload \u2014 publish the current bundle as a /preview/<id> link
4
-
5
- usage:
6
- sootsim upload [--origin <url>] [--events <path>] [--session <tab-id>] [--open] [--assets-only]
7
-
8
- options:
9
- --origin <url> upload target (default: auto)
10
- prefers ${E} when available, otherwise
11
- falls back to ${se}
12
- override with SOOTSIM_UPLOAD_ORIGIN env var
13
- --events <path> path to a gzipped events .jsonl.gz file to attach
14
- --video <path> path to a webm/mp4/gif flow recording. embedded inline
15
- in the pr sticky comment served at
16
- /api/preview/flow-video?id=<share-id>
17
- --video-duration-ms <ms>
18
- duration hint for attached flow video
19
- --session <tab-id> target a specific sootsim tab (see: sootsim list)
20
- --open open the resulting /preview/<id> url in the browser
21
- --assets-only drop API/JSON/HTML records before upload; keep images,
22
- fonts, css, js, and binary blobs. live-data demos hit
23
- the real network at replay time instead of serving
24
- recorded API snapshots
25
- -h, --help
26
-
27
- examples:
28
- sootsim upload
29
- sootsim upload --origin http://localhost:3000 --open
30
- sootsim upload --events ./my-session.jsonl.gz
31
- sootsim upload --video /tmp/soot-flow.webm
32
- `)}function S(e,n){let r=e.findIndex(i=>i===n);if(r<0)return;let s=e[r+1];return e.splice(r,2),s}function te(e,n){let r=e.findIndex(s=>s===n);return r<0?!1:(e.splice(r,1),!0)}function ne(e){let n=(e||"").toLowerCase().split(";")[0].trim();return n?!!(n.startsWith("image/")||n.startsWith("font/")||n.startsWith("video/")||n.startsWith("audio/")||n.startsWith("model/")||n==="text/css"||n==="application/javascript"||n==="text/javascript"||n==="application/wasm"||n==="application/font-woff"||n==="application/font-woff2"||n==="application/octet-stream"):!1}async function re(e){let n=await fetch(e);if(!n.ok)throw new Error(`fetch ${e} -> ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}function Te(e){return/\.bundle($|\?)/.test(e)||/\.js($|\?)/.test(e)?"application/javascript":/\.zip($|\?)/.test(e)?"application/zip":"application/javascript"}function Ue(e){try{let n=new URL(e);return n.searchParams.get("lazy")==="true"?(n.searchParams.set("lazy","false"),n.toString()):e}catch{return e}}async function xe(e,n){let s=[];for(let i=0;i<n;i+=15e5){let a=Math.min(i+15e5,n),u=await b(e,`(window.__sootsimLastTransformedBundle?.text || "").slice(${i}, ${a})`);if(typeof u!="string")return null;s.push(u)}return s.join("")}async function ke(e,n){(e.includes("--help")||e.includes("-h"))&&(Be(),process.exit(0));let r=[...e],s=await ve(S(r,"--origin")),i=S(r,"--events"),a=S(r,"--video"),u=S(r,"--video-duration-ms"),B=te(r,"--open"),p=te(r,"--assets-only"),z=D(r,{stripBooleanFlags:[],stripValueFlags:[]}),c=G(z),l,k=[],f=[],O=null;try{if(l=await b(c,'(typeof window.__sootsimCaptureBundle === "function") ? window.__sootsimCaptureBundle() : null'),l?.bundleUrl){let t=new URL(l.bundleUrl).origin;k=(await b(c,`(window.__sootsimPreviewRecorder?.list?.(${JSON.stringify(t)}) || [])`)).filter(w=>{try{return!new URL(w.url).pathname.startsWith("/api/")}catch{return!0}});let o=await b(c,`(() => {
33
- const rec = window.__sootsimPreviewRecorder
34
- if (!rec?.list) return null
35
- const all = rec.list()
36
- const cross = all.filter((e) => {
37
- try { return new URL(e.url).origin !== ${JSON.stringify(t)} } catch { return false }
38
- })
39
- return {
40
- count: cross.length,
41
- totalBytes: cross.reduce((n, e) => n + (e.size || 0), 0),
42
- }
43
- })()`);if(o&&o.count>0&&(console.log(` ${o.count} recorded cross-origin responses (${(o.totalBytes/1024).toFixed(1)} KiB)`),f=await b(c,`(() => {
44
- const rec = window.__sootsimPreviewRecorder
45
- if (!rec?.dump) return []
46
- const bundleUrl = ${JSON.stringify(l.bundleUrl)}
47
- return rec.dump().filter((r) => {
48
- try {
49
- const u = new URL(r.url)
50
- if (u.origin !== ${JSON.stringify(t)}) return true
51
- // bundle-origin api responses came from the rewritten
52
- // /__app-api proxy at record time (re-keyed by the
53
- // recorder's unwrap step). they need bodies attached the
54
- // same way as cross-origin records \u2014 direct re-fetch from
55
- // the dev server lacks the bundle's session cookies.
56
- if (r.url !== bundleUrl && u.pathname.startsWith('/api/')) return true
57
- return false
58
- } catch { return false }
59
- })
60
- })()`),p)){let w=f.length;f=f.filter(v=>ne(v.contentType));let H=o.totalBytes-f.reduce((v,I)=>v+(I.size||0),0);console.log(` --assets-only: kept ${f.length}/${w} records (dropped ${(H/1024).toFixed(1)} KiB of API responses)`)}}l?.transformedBundle&&(O=await xe(c,l.transformedBundle.byteLength))}finally{c.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.
61
- open the app you want to share first (e.g. sootsim open 8082), then run upload.`),process.exit(2));let g,R=!1;if(O!==null)g=new TextEncoder().encode(O),R=!0,console.log(` using post-transform bundle: ${(g.byteLength/1024).toFixed(1)} KiB`);else{let t=Ue(l.bundleUrl);t!==l.bundleUrl&&console.log(" forcing lazy=false for self-contained bundle"),console.log(` capturing: ${t}`),g=await re(t),console.log(` main bundle: ${(g.byteLength/1024).toFixed(1)} KiB`)}let T=k.filter(t=>t.url!==l.bundleUrl);p&&(T=T.filter(t=>ne(t.contentType))),console.log(` fetching ${T.length} extra files\u2026`);let U=(await Promise.all(T.map(async t=>{try{let o=await re(t.url);return{...t,bytes:o}}catch(o){return console.error(` warning: failed to re-fetch ${t.url}: ${o instanceof Error?o.message:o}`),null}}))).filter(t=>!!t),ie=U.reduce((t,o)=>t+o.bytes.byteLength,0);console.log(` ${U.length} extra files: ${(ie/1024).toFixed(1)} KiB`);let y;i&&(y=Z(i));let h,x,M;if(a){if(h=Z(a),x=a.endsWith(".mp4")?"video/mp4":a.endsWith(".gif")?"image/gif":"video/webm",u){let t=Number(u);(!Number.isFinite(t)||t<=0)&&(console.error(` invalid --video-duration-ms: ${u}`),await m(),process.exit(1)),M=Math.round(t)}console.log(` attaching flow video: ${(h.byteLength/1024).toFixed(1)} KiB (${x})`)}let d=q(),oe=J();W({event:"preview_upload_started",identity:{userId:d?.kind==="session"?oe?.user?.id??null:null,repoId:d?.kind==="github"?d.repoId:null,installationId:d?.kind==="github"?d.installationId??null:null},properties:{origin:s,hasAuth:!!d,authMode:d?.kind??"none",bundleBytes:g.byteLength,isTransformed:R,extraFiles:U.length,recordedFetches:f.length,hasEvents:!!y,hasVideo:!!h}}),d||(console.error(" preview uploads need auth."),console.error(" set SOOTSIM_API_KEY=sk_sootsim_..., run `sootsim login`, or use the soot github runner."),await m(),process.exit(1));let ae=Y(d),le=(()=>{try{return new URL(l.bundleUrl).origin}catch{return null}})(),C=(t,o,w,H,v,I,ge)=>{let K=Q(I),fe=K?ee(t):t;return{url:o,urlhash:w,method:H,bodyHash:v,contentType:I,responseHeaders:ge,bytes:fe,encoding:K?"gzip":void 0}},_=[...U.map(t=>C(t.bytes,t.url,t.urlhash,"GET","-",t.contentType,t.responseHeaders)),...f.map(t=>C(Buffer.from(t.bodyBase64,"base64"),t.url,t.urlhash,t.method||"GET",t.bodyHash||"-",t.contentType,t.responseHeaders))],A=ee(g),ue=we("sha256").update(g).digest("hex");console.log(` bundle raw=${(g.byteLength/1024).toFixed(1)} KiB gz=${(A.byteLength/1024).toFixed(1)} KiB`);let de={contentHash:ue,bundleSizeBytes:g.byteLength,bundleContentType:Te(l.bundleUrl),bundleEncoding:"gzip",bundleOrigin:le,entry:l.entry,isTransformed:R,deviceSpec:l.deviceSpec,installationId:d.kind==="github"?d.installationId??void 0:void 0,repoId:d.kind==="github"?d.repoId:void 0,files:_.map(t=>({url:t.url,urlhash:t.urlhash,method:t.method,bodyHash:t.bodyHash,contentType:t.contentType,responseHeaders:t.responseHeaders,encoding:t.encoding,sizeBytes:t.bytes.byteLength})),events:y?{sizeBytes:y.byteLength}:void 0,flowVideo:h&&x?{sizeBytes:h.byteLength,contentType:x,durationMs:M}:void 0},N=new Map;for(let t of _)N.set(t.urlhash,t.bytes);let ce=Date.now(),P=A.byteLength;for(let t of _)P+=t.bytes.byteLength;y&&(P+=y.byteLength),h&&(P+=h.byteLength);let pe=`${s.replace(/\/$/,"")}/api/preview/upload/init`;console.log(` init: ${pe}`);let F;try{let t=await X({originBase:s,initBody:de,bundleBytes:A,filesByHash:N,extras:{eventsBytes:y,videoBytes:h},authHeader:ae,concurrency:16});F=t.finalize;let o=1+t.init.files.length+(t.init.events?1:0)+(t.init.flowVideo?1:0);console.log(` PUT ${o} objects in ${Date.now()-ce}ms (${(P/1024).toFixed(1)} KiB)`)}catch(t){let o=t;o.phase==="init"&&o.status===401&&(console.error(" preview upload requires a valid login."),console.error(" run `sootsim login` and retry."),o.message&&console.error(` ${o.message}`),await m(),process.exit(1)),console.error(` ${o.phase??"upload"} failed: ${o.message??String(t)}`),await m(),process.exit(1)}let j=`${s.replace(/\/$/,"")}${F.url}`;console.log(`
62
- stored ${F.filesStored??0} extra files`),console.log(` preview: ${j}`),B&&await V(j),await m()}export{ve as a,ke as b};
@@ -1 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
@@ -1,5 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import r from"fs";import{homedir as v}from"os";import i from"path";var L="SOOTSIM_HOME",E="active",O="daemon.json",F="config.json",I=3e4;function s(){let e=process.env[L];return e&&e.length>0?i.resolve(e):i.join(v(),".sootsim")}function a(){return i.join(s(),"runtimes")}function N(e){return i.join(a(),e)}function x(){return i.join(a(),E)}function A(){return i.join(s(),"electron")}function J(e){return i.join(A(),e)}function _(){return i.join(s(),"cache")}function m(){return i.join(s(),O)}function d(){return i.join(s(),F)}function D(){try{let e=r.readFileSync(d(),"utf8"),t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}function $(){return D().telemetry!==!1}function V(e){g();let t=D();t.telemetry=e;let c=`${d()}.tmp`;r.writeFileSync(c,`${JSON.stringify(t,null,2)}
3
- `,"utf8"),r.renameSync(c,d())}function g(){r.mkdirSync(s(),{recursive:!0}),r.mkdirSync(a(),{recursive:!0}),r.mkdirSync(_(),{recursive:!0})}function T(){try{let e=r.readFileSync(x(),"utf8").trim();return e.length>0?e:null}catch{return null}}function B(e){r.mkdirSync(a(),{recursive:!0}),r.writeFileSync(x(),`${e}
4
- `,"utf8")}function K(){try{return r.readdirSync(a(),{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).sort(j)}catch{return[]}}function j(e,t){let c=o=>{let u=o.indexOf("-"),f=u>=0?o.slice(0,u):o,k=u>=0?o.slice(u+1):"",b=f.split(".").map(y=>Number.parseInt(y,10));return b.some(y=>!Number.isFinite(y))?[[Number.POSITIVE_INFINITY],o]:[b,k]},[l,n]=c(e),[S,p]=c(t);for(let o=0;o<Math.max(l.length,S.length);o++){let u=l[o]??0,f=S[o]??0;if(u!==f)return u-f}return n===p?0:n?p?n<p?-1:1:-1:1}function Y(){let e=T();if(!e)return null;let t=N(e);try{if(r.statSync(t).isDirectory())return t}catch{}return null}var h=16*1024;function w(){try{let e=r.openSync(m(),"r");try{let t=Buffer.alloc(h),c=r.readSync(e,t,0,h,0),l=t.subarray(0,c).toString("utf8"),n=JSON.parse(l);return n&&n.schema===1&&typeof n.pid=="number"&&typeof n.bridgePort=="number"&&typeof n.runtimePort=="number"&&typeof n.startedAt=="number"&&typeof n.heartbeatAt=="number"?n:null}finally{r.closeSync(e)}}catch{return null}}function M(e,t=Date.now()){if(!e||t-e.heartbeatAt>I)return!1;try{return process.kill(e.pid,0),!0}catch{return!1}}function R(e){g();let t=`${m()}.tmp`;r.writeFileSync(t,`${JSON.stringify(e,null,2)}
5
- `,"utf8"),r.renameSync(t,m())}function G(e){g();let t=w();return t&&M(t)&&t.pid!==e.pid?!1:(R(e),!0)}function U(){try{r.unlinkSync(m())}catch{}}export{L as a,E as b,O as c,F as d,I as e,s as f,a as g,N as h,x as i,A as j,J as k,_ as l,m,d as n,$ as o,V as p,g as q,T as r,B as s,K as t,j as u,Y as v,w,M as x,R as y,G as z,U as A};
@@ -1,4 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{existsSync as m,mkdirSync as A,readFileSync as w,readdirSync as x,rmSync as o,writeFileSync as _}from"fs";import{tmpdir as l}from"os";import{dirname as L,join as p,resolve as h}from"path";import{execFileSync as y}from"child_process";import{readFileSync as N}from"fs";import{createRequire as E}from"module";var s=null;function u(r){s||(s=O());try{return s(r)}catch{return null}}function O(){let r=Number(process.env.PPID);if(Number.isFinite(r)&&r>1&&r!==process.ppid)return()=>r;if(process.platform==="linux")return g();if(process.platform==="darwin"){let t=T();return t||P()}return()=>null}function g(){return r=>{try{let t=N(`/proc/${r}/stat`,"utf8"),e=t.lastIndexOf(")");if(e<0)return null;let i=t.slice(e+1).trim().split(/\s+/),n=Number(i[1]);return Number.isFinite(n)&&n>0?n:null}catch{return null}}}var C=3,a=216,D=16;function T(){if(!process.versions?.bun)return null;try{let r=E(import.meta.url),{dlopen:t,FFIType:e}=r("bun:ffi"),i=t("/usr/lib/libproc.dylib",{proc_pidinfo:{args:[e.i32,e.i32,e.u64,e.ptr,e.i32],returns:e.i32}}),n=Buffer.alloc(a);return b=>{if(Number(i.symbols.proc_pidinfo(b,C,0n,n,a))<=0)return null;let d=n.readUInt32LE(D);return d>0?d:null}}catch{return null}}function P(){return r=>{try{let e=y("lsof",["-R","-p",String(r),"-d","cwd","-a"],{encoding:"utf8",timeout:2e3,stdio:["ignore","pipe","ignore"]}).split(`
3
- `)[1];if(!e)return null;let i=e.trim().split(/\s+/),n=Number(i[2]);return Number.isFinite(n)&&n>1?n:null}catch{return null}}}var c=1,S="SOOTSIM_CLI_SESSION_PATH",v=["SOOTSIM_SESSION_ID","CLAUDE_CODE_SESSION_ID","CODEX_THREAD_ID","TERM_SESSION_ID","ITERM_SESSION_ID","TMUX_PANE","STY","KITTY_WINDOW_ID","WEZTERM_PANE","ALACRITTY_WINDOW_ID","WINDOWID","VSCODE_INJECTION"];function F(r,t=20){let e=u(r);if(!e||e<=1)return null;for(let i=0;i<t;i++){let n=u(e);if(!n||n<=1)return e;e=n}return e}function R(){for(let t of v){let e=process.env[t];if(e&&e.trim())return{key:`${t}:${e.trim()}`,source:t,stable:!0}}let r=F(process.ppid);return r&&r>1?{key:`gppid-${r}`,source:"grand-ppid",stable:!0}:{key:`pid-${process.ppid}`,source:"ppid",stable:!1}}var I=p(l(),"sootsim-legacy-gc-done");function j(){if(!m(I)){try{_(I,String(Date.now()))}catch{return}try{let r=l();for(let t of x(r))if(t.startsWith("sootsim-gppid-"))try{o(p(r,t),{force:!0})}catch{}}catch{}}}function k(){return R().key}function f(){if(process.env[S])return h(process.env[S]);let r=k();return p(l(),`sootsim-cli-session-${r}.json`)}function W(){let r=f();if(!m(r))return null;try{let t=JSON.parse(w(r,"utf8"));return t.version!==c||typeof t.browserId!="string"||!t.browserId.trim()||typeof t.updatedAt!="string"?(o(r,{force:!0}),null):{version:c,browserId:t.browserId.trim(),updatedAt:t.updatedAt}}catch{return o(r,{force:!0}),null}}function G(){return W()?.browserId||null}function X(r){let t=r.trim();if(!t)return;let e=f();A(L(e),{recursive:!0}),_(e,JSON.stringify({version:c,browserId:t,updatedAt:new Date().toISOString()},null,2)+`
4
- `)}function Z(){o(f(),{force:!0})}export{R as a,j as b,k as c,G as d,X as e,Z as f};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- var t={width:3,lock:{top:253,height:103},ringToggle:{top:152,height:32},volumeUp:{top:214,height:61},volumeDown:{top:290,height:61}},h={width:2.5,lock:{top:130,height:78},ringToggle:{top:94,height:22},volumeUp:{top:126,height:48},volumeDown:{top:188,height:48}},c={"iphone-se":{name:"iPhone SE",width:375,height:667,scale:2,statusBarHeight:20,homeIndicatorHeight:0,cornerRadius:0,dynamicIsland:null,safeAreaInsets:{top:20,bottom:0,left:0,right:0},iosVersion:"15.8",physicalPPI:326,hardwareButtons:h},"iphone-14":{name:"iPhone 14",width:390,height:844,scale:3,statusBarHeight:47,homeIndicatorHeight:34,cornerRadius:55,dynamicIsland:null,safeAreaInsets:{top:47,bottom:34,left:0,right:0},iosVersion:"17.6",physicalPPI:460,hardwareButtons:t},"iphone-15":{name:"iPhone 15",width:393,height:852,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:37.3},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"17.6",physicalPPI:460,hardwareButtons:t},"iphone-15-plus":{name:"iPhone 15 Plus",width:430,height:932,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:37.3},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"17.6",physicalPPI:460,hardwareButtons:t},"iphone-15-pro":{name:"iPhone 15 Pro",width:393,height:852,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:38},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"18.2",physicalPPI:460,hardwareButtons:t},"iphone-15-pro-max":{name:"iPhone 15 Pro Max",width:430,height:932,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:38},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"18.2",physicalPPI:460,hardwareButtons:t},"iphone-16":{name:"iPhone 16",width:393,height:852,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:37.3},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"26.4",physicalPPI:460,hardwareButtons:t},"iphone-16-plus":{name:"iPhone 16 Plus",width:430,height:932,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:37.3},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"26.4",physicalPPI:460,hardwareButtons:t},"iphone-16-pro":{name:"iPhone 16 Pro",width:402,height:874,scale:3,statusBarHeight:62,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:38},safeAreaInsets:{top:62,bottom:34,left:0,right:0},iosVersion:"26.4",physicalPPI:460,hardwareButtons:t},"iphone-16-pro-max":{name:"iPhone 16 Pro Max",width:440,height:956,scale:3,statusBarHeight:62,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:38},safeAreaInsets:{top:62,bottom:34,left:0,right:0},iosVersion:"26.4",physicalPPI:460,hardwareButtons:t},"iphone-17":{name:"iPhone 17",width:402,height:874,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:37.3},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"26.4",physicalPPI:460,hardwareButtons:t},"iphone-17-air":{name:"iPhone 17 Air",width:430,height:932,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:38},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"26.4",physicalPPI:460,hardwareButtons:t},"iphone-17-pro":{name:"iPhone 17 Pro",width:402,height:874,scale:3,statusBarHeight:59,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:38},safeAreaInsets:{top:59,bottom:34,left:0,right:0},iosVersion:"26.4",physicalPPI:460,hardwareButtons:t},"iphone-17-pro-max":{name:"iPhone 17 Pro Max",width:440,height:956,scale:3,statusBarHeight:62,homeIndicatorHeight:34,cornerRadius:58,dynamicIsland:{width:126,height:38},safeAreaInsets:{top:62,bottom:34,left:0,right:0},iosVersion:"26.4",physicalPPI:460,hardwareButtons:t},"ipad-pro-13":{name:'iPad Pro 13"',width:1032,height:1376,scale:2,statusBarHeight:24,homeIndicatorHeight:20,cornerRadius:18,dynamicIsland:null,safeAreaInsets:{top:24,bottom:20,left:0,right:0},iosVersion:"18.2",physicalPPI:264,hardwareButtons:t}},p="iphone-17-pro";function u(e){return c[e]}function l(){return Object.keys(c)}var i={"iphone-6-9":{name:"iphone-6-9",label:'iPhone 6.9"',width:1290,height:2796,store:"apple"},"iphone-6-1":{name:"iphone-6-1",label:'iPhone 6.1"',width:1170,height:2532,store:"apple"},"ipad-13":{name:"ipad-13",label:'iPad 13"',width:2064,height:2752,store:"apple"}},a={dark:{type:"gradient",direction:180,glow:"#4caeff",stops:[{offset:0,color:"#111827"},{offset:40,color:"#101725"},{offset:100,color:"#06080d"}]},cyan:{type:"gradient",direction:180,glow:"#25c3ec",stops:[{offset:0,color:"#1b465c"},{offset:32,color:"#122336"},{offset:100,color:"#05070b"}]},gold:{type:"gradient",direction:180,glow:"#ffc25a",stops:[{offset:0,color:"#3b2f1a"},{offset:34,color:"#18140d"},{offset:100,color:"#050505"}]},red:{type:"gradient",direction:180,glow:"#ff6178",stops:[{offset:0,color:"#3e1018"},{offset:35,color:"#17080c"},{offset:100,color:"#050405"}]}},m={color:"#25c3ec",blur:120,spread:52,opacity:.34},f="iphone-17-pro";function y(e){return Object.prototype.hasOwnProperty.call(i,e)?i[e]:null}function d(e){return{type:e.type,color:e.color,direction:e.direction,glow:e.glow,stops:e.stops?.map(o=>({...o}))}}function w(e){return Object.prototype.hasOwnProperty.call(a,e)?d(a[e]):null}var s={deviceModel:{type:"enum",default:p,description:"device model to simulate",cliFlag:"device",cliFlagShort:"d",options:l(),group:"device"},orientation:{type:"enum",default:"portrait",description:"screen orientation",cliFlag:"orientation",options:["portrait","landscape"],group:"device"},colorScheme:{type:"enum",default:"auto",description:"color scheme (auto follows system preference)",cliFlag:"theme",cliFlagShort:"t",options:["light","dark","auto"],group:"appearance"},reduceMotion:{type:"boolean",default:!1,description:"reduce motion",group:"appearance"},boldText:{type:"boolean",default:!1,description:"bold text",group:"appearance"},fontSize:{type:"number",default:1,description:"font size multiplier (1.0 = default)",group:"appearance",validate:e=>e>=.5&&e<=2},networkCondition:{type:"enum",default:"wifi",description:"simulated network condition",cliFlag:"network",options:["wifi","lte","fast-3g","slow-3g","offline"],group:"network"},language:{type:"string",default:"en",description:"language code (ISO 639-1)",cliFlag:"language",group:"locale"},region:{type:"string",default:"US",description:"region code (ISO 3166-1)",cliFlag:"region",group:"locale"},showTouches:{type:"boolean",default:!1,description:"show touch indicators",group:"simulator"},showFrame:{type:"boolean",default:!0,description:"show device frame",cliFlag:"frame",group:"simulator"},showTopBar:{type:"boolean",default:!0,description:"show simulator top bar",group:"simulator"},screenshotMode:{type:"boolean",default:!1,description:"show screenshot composition mode in the browser shell",group:"simulator"},threeMode:{type:"boolean",default:!1,description:"show the experimental 3d device stage in the browser shell",group:"simulator"},screenshotCanvas:{type:"enum",default:"iphone-6-9",description:"target app-store canvas preset for screenshot mode",options:Object.keys(i),group:"simulator"},screenshotBackground:{type:"enum",default:"dark",description:"background preset rendered inside the screenshot canvas",options:Object.keys(a),group:"simulator"},screenshotText:{type:"enum",default:"bold-top",description:"text layout preset for the screenshot canvas",options:["bold-top","editorial-left","minimal-bottom","none"],group:"simulator"},screenshotPose:{type:"enum",default:"straight",description:"device pose preset inside the screenshot canvas",options:["straight","tilted-left","tilted-right","cut-bottom","cut-top"],group:"simulator"},screenshotDynamicSize:{type:"boolean",default:!1,description:"let the screenshot canvas rect fit available space (off = pin to exact preset pixels)",group:"simulator"},showStatusBar:{type:"boolean",default:!0,description:"show status bar",group:"simulator"},showHomeIndicator:{type:"boolean",default:!0,description:"show home indicator",group:"simulator"},a11yMode:{type:"enum",default:"delayed",description:"accessibility tree sync mode (off, delayed=100ms, active=30ms)",options:["off","delayed","active"],group:"simulator"},a11yDepth:{type:"enum",default:"deep",description:"accessibility tree structure depth (shallow or deep)",options:["shallow","deep"],group:"simulator"},inspectMode:{type:"boolean",default:!1,description:"inspect mode",group:"simulator"},errorReporting:{type:"boolean",default:!1,description:"send anonymous crash reports",group:"privacy"},agentProvider:{type:"enum",default:"codex",description:"ai agent used by the sootsim agent prompt bar",options:["codex","claude"],group:"agent"}};function v(){let e={};for(let[o,r]of Object.entries(s))e[o]=r.default;if(typeof window<"u")try{let o=new URLSearchParams(window.location.search),r=o.get("device");r&&s.deviceModel.options?.includes(r)&&(e.deviceModel=r);let n=o.get("colorScheme")??o.get("theme");n&&s.colorScheme.options?.includes(n)&&(e.colorScheme=n)}catch{}return e}export{m as a,f as b,y as c,d,w as e,c as f,u as g,l as h,s as i,v as j};
@@ -1,11 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as T,b as B,c as S,d as ee}from"./chunk-GW7XY5KC.js";import{b as k}from"./chunk-OXN2PEB7.js";import{g as re}from"./chunk-YR7BGGYE.js";import{a as I,b as oe}from"./chunk-PJL25JQV.js";import{d as te}from"./chunk-A5BRCXYE.js";import{a as V}from"./chunk-SHO54NET.js";import{b as Z,c as y,d as h,e as v}from"./chunk-SMVJOWSV.js";import{d as H,e as g,f as F}from"./chunk-6GGMKFWJ.js";import{w as X,x as Y}from"./chunk-5QIUJNT3.js";import $e from"node:fs";import ye from"node:os";import j from"node:path";function me(o){return o.replace(/^\[|\]$/g,"").toLowerCase()}function M(o){let t=me(o);return t==="localhost"||t.endsWith(".localhost")||t==="0.0.0.0"||t==="::1"||/^127(?:\.\d{1,3}){3}$/.test(t)}function ne(o){return new Error(`could not resolve a native bundle for ${o}. pass an explicit bundle URL or open Connect and choose the app there.`)}function pe(o,t){return o?.includes("/one/metro-entry.bundle")?"one":typeof t=="string"&&t?"expo":"unknown"}function se(o,t,e){return`${o}//${t}:${e}`}function fe(o){if(typeof window>"u")return!1;try{let t=new URL(o);return M(t.hostname)&&M(window.location.hostname)&&t.origin!==window.location.origin}catch{return!1}}async function C(o,t){let e={...t,cache:t?.cache??"no-store"};return fe(o)?fetch(`/__fetch-proxy?url=${encodeURIComponent(o)}`,e):fetch(o,e)}function ge(o){return o==="https:"?443:80}function he(o){let t=o.pathname||"/";return(t==="/"||t==="")&&!o.search&&!o.hash}async function we(o){try{return(await C(`${o}${B}`,{method:"HEAD"})).ok}catch{return!1}}async function ie(o,t){let e=o.replace(/\/+$/,"");try{let r=await C(`${e}/`,{headers:{"expo-platform":"ios"}});if(r.ok){let n=await r.json(),s=n?.extra?.expoClient||n?.extra||{},i=typeof n?.launchAsset?.url=="string"?n.launchAsset.url:void 0;if(i||s.name){let a=pe(i,s.sdkVersion);return{bundleUrl:a==="one"&&await we(e)?`${e}${B}`:ee(i||`${e}${S}`),port:t,framework:a,projectName:s.name}}}}catch{}try{if((await C(`${e}${B}`,{method:"HEAD"})).ok)return{bundleUrl:`${e}${B}`,port:t,framework:"one"}}catch{}try{if((await C(`${e}${S}`,{method:"HEAD"})).ok)return{bundleUrl:`${e}${S}`,port:t,framework:"metro"}}catch{}try{let r=await C(`${e}/status`);if(r.ok&&(await r.text()).includes("packager-status:running"))return{bundleUrl:`${e}${S}`,port:t,framework:"metro"}}catch{}return null}async function be(o){return ie(se("http:","localhost",o),o)}async function z(o){let t=o.trim();if(/^\d+$/.test(t)){let a=parseInt(t,10),c=await be(a);if(c)return c;throw ne(`localhost:${a}`)}let e=t.startsWith("http")?t:`http://${t}`,r;try{r=new URL(e)}catch{throw new Error(`could not parse "${o}". pass a dev-server port, a dev-server base URL, or a full bundle URL.`)}let n=r.protocol||"http:",s=r.port?parseInt(r.port,10):ge(n),i=se(n,r.hostname,s);if(M(r.hostname)&&he(r)){let a=await ie(i,s);if(a)return a;throw ne(i)}return{bundleUrl:r.toString(),port:s,framework:"unknown"}}function Ne(){console.error(" no sootsim desktop companion found."),console.error(""),console.error(" install the desktop app:"),console.error(" sootsim install-desktop"),console.error(""),console.error(" or wire sootsim into your own project so your dev server hosts the bridge:"),console.error(" sootsim setup-repo"),console.error("")}function ae(o){let t=I();if(console.log(` note: no sootsim bridge detected on port ${o}`),t){console.log(" launch the installed companion with:"),console.log(" sootsim electron");return}console.log(""),console.log(" to get a bridge running, either:"),console.log(" sootsim install-desktop # download the electron app"),console.log(" sootsim setup-repo # wire sootsim into your own project")}function He(o){console.error(""),console.error(` no sootsim session is connected to the bridge on port ${o}.`),console.error(""),console.error(" start your app dev server, then open it in sootsim:"),console.error(" npx expo start --localhost --port 8081"),console.error(" sootsim open 8081"),console.error(""),console.error(" then inspect the live app:"),console.error(" sootsim describe")}var ve={rn:"/rn",connectrn:"/rn","connect-rn":"/rn",clock:"/app/clock","native-ui":"/app/native-ui",tamagui:"/app/tamagui",settings:"/app/settings",photos:"/app/photos",camera:"/app/camera"};function x(o){return new Promise(t=>setTimeout(t,o))}function G(o){return o.trim()}function W(o){try{let t=new URL(o),e=t.pathname.replace(/\/+$/,"")||"/";return t.searchParams.has("open")||t.searchParams.has("port")||t.searchParams.has("bundle")||t.searchParams.has("demo")||t.pathname.includes("/sootsim/index.html")||e==="/sootsim"||t.pathname==="/__soot"||t.pathname==="/__soot/"||e==="/rn"||/^\/rn\/[^/]+$/i.test(e)||/^\/app\/[^/]+$/i.test(e)||e==="/__soot/rn"||/^\/__soot\/rn\/[^/]+$/i.test(e)||/^\/__soot\/app\/[^/]+$/i.test(e)}catch{return!1}}async function to(o){let t=G(o);return(await z(t)).bundleUrl}function Q(o){let t=o.replace(/\/+$/,"")||"/";return t==="/__soot"||t.startsWith("/__soot/")?"/__soot":""}function Be(o,t){let e=G(o),r=new URL(t),n=Q(r.pathname);return r.pathname=`${n}/rn`,r.searchParams.delete("bundle"),r.searchParams.delete("demo"),r.searchParams.delete("app"),r.searchParams.delete("open"),r.searchParams.delete("port"),/^\d+$/.test(e)?r.pathname=`${n}/rn/${e}`:(r.pathname=`${n}/rn`,r.searchParams.set("open",e)),r.toString()}function Se(o){let t=G(o);return/^\d+$/.test(t)||/^https?:\/\//i.test(t)?!0:/^(localhost|127\.0\.0\.1|\[::1\]|[^/]+\.localhost):\d+(?:\/.*)?$/i.test(t)}async function Ce(o,t){let e=await z(o),r=new URL(t),n=Q(r.pathname);return r.pathname=`${n}/rn`,r.searchParams.delete("open"),r.searchParams.delete("port"),r.searchParams.delete("demo"),r.searchParams.delete("app"),r.searchParams.set("bundle",e.bundleUrl),r.toString()}function xe(o){return o.startsWith("~/")?j.join(ye.homedir(),o.slice(2)):j.isAbsolute(o)?o:j.resolve(process.cwd(),o)}function Pe(o){let t={};for(let e=0;e<o.length;e++){if(o[e]!=="--replace")continue;let r=o[e+1];r||(console.error(" sootsim open: --replace expects <module>=<file>"),process.exit(1));let n=r.indexOf("=");(n<=0||n===r.length-1)&&(console.error(" sootsim open: --replace expects <module>=<file>"),process.exit(1));let s=r.slice(0,n).trim(),i=xe(r.slice(n+1).trim());$e.existsSync(i)||(console.error(` sootsim open: replacement file not found: ${i}`),process.exit(1)),t[s]={file:i},e++}return Object.keys(t).length>0?{modules:t}:void 0}function le(){let o=X();return Y(o)&&o.runtimePort>0?`http://localhost:${o.runtimePort}/`:V}async function _(o,t=le()){if(!o)return new URL(t).toString();if(W(o))return new URL(o).toString();let e=ve[o.toLowerCase()];if(e){let r=new URL(t),n=Q(r.pathname);return r.pathname=`${n}${e}`,r.toString()}return Se(o)?Ce(o,t):Be(o,t)}function ce(o,t){let e=o?.url||o?.origin||t;try{let r=new URL(e);return r.searchParams.delete("bundle"),r.searchParams.delete("demo"),r.searchParams.delete("app"),r.searchParams.delete("open"),r.searchParams.delete("port"),r.searchParams.delete("inspectOpen"),r.toString()}catch{return t}}async function Ue(o,t,e){let r=new URL(await _(o,t));return r.searchParams.set("inspectOpen",e),r.toString()}async function ke(o,t,e,r={}){let n=r.attempts??30,s=r.intervalMs??500,i=r.minNodeCount??10;for(let a=0;a<n;a++){let c=h(o,{commandTimeoutMs:t,browserId:e,browserIdSource:"flag"});try{let d=await c.send({type:"evaluate",code:"(async () => (await window.__sootsimTest?.getNodeCount()) || 0)()"});if(typeof d=="number"&&d>i)return{bridge:c,count:d}}catch{}c.close(),await x(s)}return null}function Te(o){if(!o)return null;try{let t=new URL(o);if(t.searchParams.has("bundle")){let e=t.searchParams.get("bundle")||"";try{let r=new URL(e),n=r.pathname.length>36?`...${r.pathname.slice(-36)}`:r.pathname;return`bundle ${r.host}${n}`}catch{return"bundle"}}return t.searchParams.has("port")?`connect :${t.searchParams.get("port")||""}`:t.searchParams.has("open")?`connect ${t.searchParams.get("open")||""}`:t.searchParams.has("demo")?`demo ${t.searchParams.get("demo")||"default"}`:t.pathname.includes("/sootsim/index.html")||t.pathname==="/sootsim/"||t.pathname==="/sootsim"?"embedded sootsim":null}catch{return null}}function Ie(o,t){if(o.length===0){console.log(" no browser tabs connected");return}console.log(` connected browser tabs (${o.length}):
3
- `);for(let e of o){let r=e.lockedBy&&e.lockExpiresAt?`locked by ${e.lockedBy} (${Math.max(0,Math.round((e.lockExpiresAt-Date.now())/1e3))}s)`:"",n=[e.isPrimary?"primary":"",e.id===t?"selected":"",e.readyState,e.attachedCliCount&&e.attachedCliCount>0?"in use":"",e.userFocused?"focused":"",r].filter(Boolean);console.log(` ${e.id}${n.length?` [${n.join(", ")}]`:""}`);let s=Te(e.url);if(s&&console.log(` loaded: ${s}`),e.url?console.log(` url: ${e.url}`):e.origin&&console.log(` origin: ${e.origin}`),e.title&&console.log(` title: ${e.title}`),console.log(` connected: ${new Date(e.connectedAt).toISOString()}`),e.lastActiveAt&&e.lastActiveAt>0){let i=Math.round((Date.now()-e.lastActiveAt)/1e3),a=i<60?`${i}s ago`:i<3600?`${Math.round(i/60)}m ago`:`${Math.round(i/3600)}h ago`;console.log(` last active: ${a}`)}}}async function q(o,t,e,r={}){let n=r.attempts??30,s=r.intervalMs??500;for(let i=0;i<n;i++){let a=h(o,{commandTimeoutMs:t});try{let d=(await a.listBrowsers()).find(e);if(d)return d}catch{}finally{a.close()}await x(s)}return null}async function Me(o,t,e,r={}){let n=r.attempts??20,s=r.intervalMs??250;for(let i=0;i<n;i++){let a=h(o,{commandTimeoutMs:t});try{let d=(await a.listBrowsers()).find(b=>b.id===e);if(!d||d.readyState!=="open")return!0}catch{return!0}finally{a.close()}await x(s)}return!1}function E(o,t){if(t){let n=o.find(s=>s.id===t);if(!n)throw new Error(`no browser connected with id ${t}`);return n}let e=o.find(n=>n.isPrimary&&n.readyState==="open");if(e)return e;let r=o.find(n=>n.readyState==="open");if(r)return r;throw new Error("no browser connected")}function R(o,t,e){console.log(` ${e==="current session"?"loaded":"opened"}: ${o} [${e}]`),console.log(` active session: ${t.id}`),console.log(JSON.stringify({id:t.id,url:t.url},null,2))}async function O(o,t,e){if(e.includes("--no-describe"))return;let r=process.env.SOOTSIM_QUIET_TARGET_NOTICE;process.env.SOOTSIM_QUIET_TARGET_NOTICE="1";try{await Re(o,t,{stableMs:150,maxMs:400});let n=h(o,{commandTimeoutMs:3e3,browserId:t,cliLabel:"open --describe",browserIdSource:"flag"});try{let i=`(async () => {
4
- const t = window.__sootsimTest
5
- const ms = window.SootSim?.bridges?.mainShell
6
- if (!t) return null
7
- let shell = null
8
- try { shell = ms?.getState ? await ms.getState() : null } catch {}
9
- const tree = await t.dumpTree(12, ${JSON.stringify({describe:!0,verbose:!1,filter:""})})
10
- return { tree, shell }
11
- })()`,a=await n.send({type:"evaluate",code:i});if(!a?.tree)return;if(console.log(""),a.shell?.state){let c=[`state=${a.shell.state}`,a.shell.activeApp?`app=${a.shell.activeApp}`:null].filter(Boolean);console.log(` shell: ${c.join(" ")}`)}console.log(a.tree)}finally{n.close()}}catch{}finally{r===void 0?delete process.env.SOOTSIM_QUIET_TARGET_NOTICE:process.env.SOOTSIM_QUIET_TARGET_NOTICE=r}}async function Re(o,t,e){let r=Date.now()+e.maxMs,n="(async () => (await window.__sootsimTest?.getNodeCount?.()) || 0)()",s=h(o,{commandTimeoutMs:2e3,browserId:t,browserIdSource:"flag"});try{let i=-1,a=0;for(;Date.now()<r;){let c=-1;try{let d=await s.send({type:"evaluate",code:n});typeof d=="number"&&(c=d)}catch{}if(c>=0&&c===i){if(Date.now()-a>=e.stableMs)return}else i=c,a=Date.now();await x(50)}}finally{s.close()}}async function ro(o,t={}){let e=y(o,{port:t.port,commandTimeoutMs:t.timeoutMs}),r=v(e);try{let n=await r.listBrowsers();Ie(n,e.browserId)}finally{r.close()}}async function no(o,t={}){let e=y(o,{port:t.port,commandTimeoutMs:t.timeoutMs,stripBooleanFlags:["--new","--headless"],stripValueFlags:["--base-url","--replace","--driver"]}),r=o.includes("--new"),n=Pe(o);r&&e.browserIdSource==="flag"&&(console.error(" sootsim open: --new cannot be combined with --session/--id"),process.exit(1));let s=e.positional[0]||"",i=o.find((l,p)=>o[p-1]==="--driver")||"",a=o.includes("--headless"),c=o.find((l,p)=>o[p-1]==="--base-url")||le(),d=o.includes("--base-url"),b=H();if(!r&&(e.browserIdSource==="flag"||e.browserIdSource==="saved"&&!!b)){let l=v(e),p=e.browserId?` --session ${e.browserId}`:"",u=!1;try{let m=null;try{let f=await l.listBrowsers();if(e.browserIdSource==="saved"?(m=f.find($=>$.id===b&&$.readyState==="open")??null,m||F()):m=E(f,e.browserId),!m)if(e.browserIdSource==="saved")u=!0;else throw new Error("no browser connected");if(!u&&m){let $=d||W(s)?c:ce(m,c),U=await _(s,T($,n));l.send({type:"evaluate",browserId:m.id,code:`window.location.href = ${JSON.stringify(U)}`}).catch(()=>{})}}catch(f){console.error(` open failed: ${f instanceof Error?f.message:String(f)}`),await k(l,{errorsCommand:`sootsim get errors 5${p}`,warningsCommand:`sootsim get warnings 5${p}`,requestsCommand:`sootsim get requests 5${p}`}),process.exit(1)}if(!u&&m){await x(1500);let f=await ke(e.wsPort,e.commandTimeoutMs,m.id);f||(console.error(" timed out waiting for current session to load target"),process.exit(1)),f.bridge.close(),g(m.id);let $=d||W(s)?c:ce(m,c),U=await _(s,T($,n));R(U,{...m,url:U},"current session"),await O(e.wsPort,m.id,o);return}}finally{l.close()}}let J=T(c,n),ue=await _(s,J),K=`cli-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`,w=await Ue(s,J,K),L={newWindow:!0},D=l=>l.url?.includes(`inspectOpen=${K}`)??!1;if(i){let l=re(i);l||(console.error(` unknown driver "${i}" \u2014 run \`sootsim list --drivers\``),process.exit(1));let p=await l.launch({url:w,headless:a,newWindow:L.newWindow});p.launched||(console.error(` ${l.name} driver: ${p.message}`),process.exit(1));let u=await q(e.wsPort,e.commandTimeoutMs,D,{attempts:60,intervalMs:500});u||(console.error(" timed out waiting for opened session to connect"),process.exit(1)),g(u.id),R(w,u,`${l.name} driver`),await O(e.wsPort,u.id,o);return}let A=!1,N=!1;try{let l=h(e.wsPort,{commandTimeoutMs:2e3});try{await l.listBrowsers(),A=!0}finally{l.close()}}catch{}if(A)try{let l=h(e.wsPort,{commandTimeoutMs:3e3});try{await l.openUrl(w,L),N=!0}finally{l.close()}}catch{}if(!N){let l=I();if(l)try{if((await oe(w,l)).launched){let u=await q(e.wsPort,e.commandTimeoutMs,D,{attempts:20,intervalMs:500});if(u){g(u.id),R(w,u,"desktop companion"),await O(e.wsPort,u.id,o);return}}}catch{}if(await te(w,L),!A){console.log(` opened: ${ue}`),ae(e.wsPort);return}}let P=await q(e.wsPort,e.commandTimeoutMs,D,{attempts:30,intervalMs:500});P||(console.error(" timed out waiting for opened session to connect"),process.exit(1)),g(P.id),R(w,P,N?"bridge":"direct shell open"),await O(e.wsPort,P.id,o)}async function de(o,t={}){let e=y(o,{port:t.port,commandTimeoutMs:t.timeoutMs}),r=v(e),n=e.browserId?` --session ${e.browserId}`:"";try{try{let s=await r.listBrowsers(),i=E(s,e.positional[0]||e.browserId);await r.focusBrowser(i.id),g(i.id),console.log(` using: ${i.id}`)}catch(s){console.error(` use failed: ${s instanceof Error?s.message:String(s)}`),await k(r,{errorsCommand:`sootsim get errors 5${n}`,warningsCommand:`sootsim get warnings 5${n}`,requestsCommand:`sootsim get requests 5${n}`}),process.exit(1)}}finally{r.close()}}async function so(o,t={}){await de(o,t)}async function io(o,t={}){await de(o,t)}async function ao(o,t={}){let e=y(o,{port:t.port,commandTimeoutMs:t.timeoutMs,stripBooleanFlags:["--force"]}),r=o.includes("--force"),n=v(e);try{try{let s=await n.listBrowsers(),i=E(s,e.positional[0]||e.browserId),a=r&&i.lockedBy&&i.lockedByKind!=="user-active"?i.lockedBy:null,c=await n.claim(i.id,{force:r});g(i.id);let d=Math.max(0,Math.round((c.lockExpiresAt-Date.now())/1e3)),b=c.bootedCount>0?` (booted ${c.bootedCount})`:"";console.log(` claimed: ${c.browserId} [${d}s]${b}`),a&&console.log(` took over from: ${a}`)}catch(s){if(s instanceof Z){let i=Math.max(0,Math.round(s.lock.expiresInMs/1e3));console.error(` claim failed: locked by ${s.lock.by} for ${i}s more`),console.error(" use --force to take it, or `sootsim open --new` for a fresh session"),process.exit(1)}console.error(` claim failed: ${s instanceof Error?s.message:String(s)}`),process.exit(1)}}finally{n.close()}}async function co(o,t={}){let e=y(o,{port:t.port,commandTimeoutMs:t.timeoutMs}),r=v(e),n=e.browserId?` --session ${e.browserId}`:"";try{try{let s=await r.listBrowsers(),i=E(s,e.positional[0]||e.browserId),a=s.find(d=>d.id!==i.id&&d.readyState==="open");if(await r.closeBrowser(i.id),!await Me(e.wsPort,e.commandTimeoutMs,i.id)){console.log(` close requested: ${i.id} (still connected)`);return}H()===i.id&&(a?g(a.id):F()),console.log(` closed: ${i.id}`)}catch(s){console.error(` close failed: ${s instanceof Error?s.message:String(s)}`),await k(r,{errorsCommand:`sootsim get errors 5${n}`,warningsCommand:`sootsim get warnings 5${n}`,requestsCommand:`sootsim get requests 5${n}`}),process.exit(1)}}finally{r.close()}}export{Ne as a,He as b,to as c,le as d,_ as e,Ue as f,Te as g,Ie as h,q as i,ro as j,no as k,so as l,io as m,ao as n,co as o};
@@ -1,5 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as x}from"./chunk-H2QO4TDV.js";import{a as P,d as C}from"./chunk-GW7XY5KC.js";import{exec as U}from"child_process";import D from"http";import _ from"net";import{promisify as k}from"util";var S=k(U),y=250,E=120;function T(e,o=E){return new Promise(s=>{let n=new _.Socket,r=!1,i=c=>{r||(r=!0,n.destroy(),s(c))};n.setTimeout(o),n.once("connect",()=>i(!0)),n.once("timeout",()=>i(!1)),n.once("error",()=>i(!1)),n.connect(e,"localhost")})}function m(e,o,s="GET",n=y,r={}){return new Promise(i=>{let c=D.request({hostname:"localhost",port:e,path:o,method:s,timeout:n,headers:r},t=>{let a="";t.on("data",u=>a+=u.toString()),t.on("end",()=>i({statusCode:t.statusCode||0,body:a}))});c.on("error",()=>i(null)),c.setTimeout(n,()=>{c.destroy(),i(null)}),c.end()})}var A=[8081,8082,8083,8084,8085,8086,3e3,3001,19e3].map(e=>({port:e,pid:0}));function g(e,o){return!(e<=0||e>=2e4||o.has(e)||e>=5170&&e<=5200)}async function M(e=[]){let o=new Set(e);try{let{stdout:s}=await S("lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null | grep -E '^(node|bun)'",{encoding:"utf8",timeout:2e3});if(s.trim()){let n=new Map;for(let r of s.trim().split(`
3
- `)){let i=r.trim().split(/\s+/);if(i.length<9)continue;let c=Number(i[1]),a=i[8].match(/:(\d+)$/);if(!a)continue;let u=Number(a[1]);g(u,o)&&(n.has(u)||n.set(u,c))}if(n.size>0)return[...n.entries()].map(([r,i])=>({port:r,pid:i}))}}catch{}try{let{stdout:s}=await S(`ss -tlnp 2>/dev/null | grep -E '"(node|bun)"'`,{encoding:"utf8",timeout:2e3});if(s.trim()){let n=new Map;for(let r of s.trim().split(`
4
- `)){let i=r.match(/:(\d+)\s/),c=r.match(/pid=(\d+)/);if(!i)continue;let t=Number(i[1]),a=c?Number(c[1]):0;g(t,o)&&(n.has(t)||n.set(t,a))}if(n.size>0)return[...n.entries()].map(([r,i])=>({port:r,pid:i}))}}catch{}return A.filter(s=>g(s.port,o))}var b=new Map;async function N(e){if(e<=0)return null;let o=b.get(e);if(o)return o;try{let{stdout:s}=await S(`lsof -p ${e} -a -d cwd -Fn 2>/dev/null`,{encoding:"utf8",timeout:1500});for(let n of s.split(`
5
- `))if(n.startsWith("n")&&n.length>1){let r=n.slice(1).trim();if(r)return b.set(e,r),r}}catch{}return null}function R(e,o){return{port:e,framework:o,bundleUrl:d(e,`http://localhost:${e}/index.bundle?platform=ios&dev=true&hot=true&minify=false`),hmrUrl:`ws://localhost:${e}/hot`,lastSeen:Date.now()}}function d(e,o){let s=x.find(r=>r.preferredPort===e),n=s?.runtimeConfig?P(o,s.runtimeConfig):o;return C(n)}function $(e){return e.includes("/node_modules/one/metro-entry.bundle")}function p(e,o,s){if(!o)return e;try{let n=JSON.parse(o.body),r=n?.extra?.expoClient||n?.extra||{};r.name&&(e.projectName=r.name),r.ios?.bundleIdentifier&&(e.bundleId=r.ios.bundleIdentifier),e.framework==="metro"&&r.sdkVersion&&(e.framework="expo");let i=n?.launchAsset?.url;i&&!e.patched&&!$(e.bundleUrl)&&(e.bundleUrl=d(e.port,i));let c=r.iconUrl||r.ios?.iconUrl||r.icon||r.ios?.icon;if(c)if(e.iconPath=c,s)if(c.startsWith("http"))e.iconUrl=s(c);else{let t=c.replace(/^\.\//,"");e.iconUrl=s(`http://localhost:${e.port}/assets/${t}`)}else e.iconUrl=c.startsWith("http")?c:`http://localhost:${e.port}/assets/${c.replace(/^\.\//,"")}`}catch{}return e}var l=new Set,f=new Set;async function L(e,o){if(!await T(e))return null;let s="/node_modules/one/metro-entry.bundle?platform=ios&dev=true",[n,r,i,c,t]=await Promise.all([l.has(e)?Promise.resolve(null):m(e,"/__soot/"),m(e,"/status"),m(e,s,"HEAD"),m(e,"/","GET",y,{"expo-platform":"ios"}),f.has(e)?Promise.resolve(null):m(e,"/_expo/status")]);if(t&&t.statusCode===200?f.delete(e):f.has(e)||f.add(e),i&&i.statusCode>0&&i.statusCode<400)return l.add(e),p({port:e,framework:"one",bundleUrl:d(e,`http://localhost:${e}${s}&minify=false`),hmrUrl:`ws://localhost:${e}/hot`,lastSeen:Date.now()},c,o);if(r&&r.body.includes("packager-status:running"))return l.add(e),p(R(e,t&&t.statusCode===200?"expo":"metro"),c,o);if(c)try{let a=JSON.parse(c.body);if((a?.extra?.expoClient||{}).name){let w=a?.launchAsset?.url||`http://localhost:${e}/index.bundle?platform=ios&dev=true&hot=true&minify=false`;return l.add(e),p({port:e,framework:"one",bundleUrl:d(e,w),hmrUrl:`ws://localhost:${e}/hot`,lastSeen:Date.now()},c,o)}}catch{}return n&&n.statusCode===200&&n.body.includes("sootsim-patched")?(l.delete(e),p({port:e,framework:"one",bundleUrl:d(e,`http://localhost:${e}/__soot/bundle.js`),hmrUrl:`ws://localhost:${e}/hot`,lastSeen:Date.now(),patched:!0},c,o)):(l.add(e),null)}function I(e){let o=e.projectName?.trim().toLowerCase();return!!(o==="soot"||o==="sootsim"||e.bundleId?.trim().toLowerCase()?.startsWith("dev.soot"))}var h=new Map,H=3e4,O=1500;function B(e){return!e||e.framework==="metro"||e.framework==="unknown"}function G(e){return e?d(e.port,e.bundleUrl)===e.bundleUrl:!0}function W(e,o,s=Date.now()){if(o===0||e.pid!==o||!G(e.result))return!1;let n=s-e.cachedAt;return!(e.result===null&&n>=H||B(e.result)&&n>=O)}async function Q(e={}){let o=await M(e.excludePorts),s=new Set(o.map(t=>t.port));for(let t of[...h.keys()])s.has(t)||h.delete(t);for(let t of[...l])s.has(t)||l.delete(t);for(let t of[...f])s.has(t)||f.delete(t);let n=[],r=[];for(let{port:t,pid:a}of o){let u=h.get(t);if(u&&W(u,a)){u.result&&n.push(u.result);continue}u&&u.pid!==a&&(l.delete(t),f.delete(t)),r.push({port:t,pid:a})}r.length>0&&(await Promise.all(r.map(a=>L(a.port,e.buildIconProxyUrl)))).forEach((a,u)=>{let{port:w,pid:v}=r[u];v!==0&&h.set(w,{pid:v,result:a,cachedAt:Date.now()}),a&&n.push(a)});let i=new Map;for(let{port:t,pid:a}of o)a>0&&i.set(t,a);await Promise.all(n.map(async t=>{let a=i.get(t.port);if(!a)return;t.pid=a;let u=await N(a);u&&(t.cwd=u)}));let c=new Set(i.values());for(let t of[...b.keys()])c.has(t)||b.delete(t);return n.filter(t=>!I(t))}export{Q as a};
@@ -1,117 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- var w={bridgePort:7668,defaultShellUrl:"http://localhost:5173/"};function t(o,i){return{flag:o,description:i}}function e(o,i){return{command:o,description:i}}var k=[{id:"preview",slug:"preview",navTitle:"preview",title:"sootsim preview",description:"production-like preview build",summary:"Build the sootsim frontend in preview mode, then either serve it locally or export a static artifact path for later packaging.",order:20,usage:["sootsim preview [options]"],options:[t("--export <path>","export a standalone preview artifact instead of serving"),t("--port <number>","preview server port (defaults to 4173)")],examples:[e("sootsim preview"),e("sootsim preview --export ./preview.html")],related:["electron"]},{id:"test",slug:"test",navTitle:"test",title:"sootsim test",description:"run tests against sootsim",summary:"Run package-local smoke and debugging tests, YAML flows, or the shared Detox-style conformance suites against a running sootsim target.",order:30,usage:["sootsim test [options]"],options:[t("--flows","run YAML flows through the sootsim flow runner"),t("--detox","run the shared Detox-style conformance suites against sootsim"),t("--parallel","run discovered flows in parallel where supported"),t("--watch","re-run interactively when files change"),t("--reporter <type>","select console, json, or junit reporting"),t("--grep <pattern>","filter the test set by name")],examples:[e("sootsim test","run package-local Playwright smoke/debug tests"),e("sootsim test --flows"),e("sootsim test --detox","run the shared conformance suite against sootsim"),e("sootsim test --detox --watch")],related:["flow","inspect","record"]},{id:"flow",slug:"flow",navTitle:"flow",title:"sootsim flow",description:"run flows through the current session or manage a draft from live inspect actions",summary:"Use flow files when you already have them, or start a draft session so inspect-driven actions can be kept one by one and exported into YAML once the path is proven. Flow playback, recording, and profiling now stay on the shared bridge/session runner.",order:40,usage:["sootsim flow <flow.yaml> [options]","sootsim flow start","sootsim flow keep","sootsim flow end [--output <path>] [--validate] [--video]"],aliases:["good"],options:[t("--record","record a video while the flow runs"),t("--profile","capture perf stats while the flow runs"),t("--out <dir>","directory for recordings"),t("--slow <ms>","delay between steps for easier debugging"),t("--url <url>","load this target before running the flow"),t("--new","open a fresh browser window before running the flow"),t("--driver <id>","when paired with --new, launch through a specific driver from the registry (e.g. playwright); see `sootsim list --drivers`"),t("--headless","pass headless=true to the launch driver (useful with --driver playwright)"),t("--session <id>","target a specific current session"),t("--electron","prefer the desktop companion for this flow run"),t("--output <path>","write the drafted flow when using `flow end`"),t("--validate","replay the drafted flow before clearing it"),t("--video","replay and record the drafted flow before clearing it"),t("--preview","record + upload the run as a shareable /preview/<id> link (implies --record)"),t("--preview-origin <url>","override the upload target for --preview (defaults to auto)"),t("--preview-open","open the resulting /preview/<id> url after upload")],examples:[e("sootsim flow flows/login.yaml"),e("sootsim flow flows/login.yaml --profile"),e("sootsim flow flows/demo.yaml --record --slow 500"),e("sootsim flow flows/demo.yaml --preview","record + upload a shareable link in one step"),e("sootsim flow start"),e("sootsim flow keep"),e("sootsim flow end --output ./flows/draft.yaml"),e("sootsim flow end --output ./flows/draft.yaml --validate"),e("sootsim flow end --output ./flows/draft.yaml --video")],related:["inspect","record","upload","electron","test"]},{id:"record",slug:"record",navTitle:"record",title:"sootsim record",description:"capture the live session as webm/mp4/gif, or sample N png frames (--frames)",summary:"Drives the engine's built-in canvas recorder over the WS bridge \u2014 no second browser, no ffmpeg. Encoding happens in the running page (MediaRecorder for webm, WebCodecs for mp4, gifenc for gif). Two modes: atomic (duration-bounded) or stateful (start \u2192 interact \u2192 stop). Stateful mode is webm/mp4 only; for gif/png use atomic mode. Use --frames to sample N evenly-spaced png snapshots instead of a video.",order:60,usage:["sootsim record [options]","sootsim record start [options]","sootsim record stop [--output <path>]","sootsim record status","sootsim record cancel"],options:[t("--format <type>","webm | mp4 | gif | png (inferred from --output extension; stateful mode is webm/mp4 only)"),t("--mode <kind>","video | live | combined (default: video). live captures the event stream; combined captures both and uploads automatically"),t("--duration <seconds>","recording duration (default: 10, atomic mode only)"),t("--fps <number>","target frame rate for video/gif (default: 30)"),t("--output <path>","output file, or directory when --frames is set"),t("--frames <n>","sample N evenly-spaced png frames instead of a video"),t("--max-width <px>","downscale gif frames to this width"),t("--session <tab-id>","target a specific bridge tab")],examples:[e("sootsim record --duration 5"),e("sootsim record --format mp4 --output demo.mp4 --duration 8"),e("sootsim record --output demo.gif --duration 3"),e("sootsim record --frames 10 --output ./frames/"),e("sootsim record --mode combined --duration 8 --open","capture video + events and upload a shareable preview link"),e("sootsim record start --format mp4","begin; interact freely across any number of CLI calls"),e("sootsim record start --mode combined","start a stateful preview-share recording"),e("sootsim record stop --output flow.mp4","finalize + save"),e("sootsim record cancel","discard an in-progress recording")],related:["screenshot","flow","upload"]},{id:"profile",slug:"profile",navTitle:"profile",title:"sootsim profile",description:"capture a sampled CPU trace from the tenant worker",summary:"Record a sampled CPU profile of the running sootsim tenant worker (where the guest app's JS executes) via the W3C JS Self-Profiler API, and write a standard `.cpuprofile` that loads directly in Chrome DevTools (Performance panel \u2192 Load profile). No CDP, no env flag \u2014 sootsim already serves `Document-Policy: js-profiling` in vite dev and in the electron companion. Ideal for A/B comparisons across commits: `profile` before, make a change, `profile` after, then diff in DevTools.",order:65,usage:["sootsim profile [options]"],options:[t("--duration <seconds>","recording duration (default: 5)"),t("--output <path>","output file (default: /tmp/sootsim.cpuprofile). if the path ends in .gz, the file is gzipped"),t("--sample-interval <ms>","requested sample interval in ms (default: 10 \u2014 chrome may clamp upward)"),t("--max-buffer <n>","max samples buffered (default: 100000)"),t("--session <tab-id>","target a specific bridge tab")],examples:[e("sootsim profile --duration 3","save to /tmp/sootsim.cpuprofile"),e("sootsim profile --duration 10 --output /tmp/after.cpuprofile.gz"),e("sootsim profile --duration 3 -o /tmp/before.cpuprofile","capture a baseline before a code change")],related:["record","flow"]},{id:"screenshot",slug:"screenshot",navTitle:"screenshot",title:"sootsim screenshot",description:"capture the canvas (full or cropped) as PNG",summary:"Capture the canvas from the currently connected sootsim session \u2014 full-screen by default, or cropped to a logical rect via --area / --id / --text. Falls back to a fresh headless chromium when no bridge is running.",order:70,usage:["sootsim screenshot [options]"],options:[t("--output <path>","output file path (default: /tmp/sootsim-inspect.png)"),t("--area <x,y,w,h>","crop to a logical sootsim rect"),t("--id <testID>","crop to the bounding box of a node by testID"),t("--text <text>","crop to the bounding box of a node by text content"),t("--gallery","crawl and capture multiple screens into an HTML gallery"),t("--themes","capture light and dark variants where possible")],examples:[e("sootsim screenshot --output app.png"),e("sootsim screenshot --area 0,200,393,400"),e("sootsim screenshot --id loginButton --output button.png"),e("sootsim screenshot --gallery --themes")],related:["sample-color","record","inspect"]},{id:"screenshots",slug:"screenshots",navTitle:"screenshots",title:"sootsim screenshots",description:"capture + compose app-store screenshot sets from a single plan",summary:"Run a YAML plan that can capture raw screenshots from a flow, frame them with the real device chrome, and export branded marketing canvases for App Store submission.",order:71,usage:["sootsim screenshots --plan <plan.yaml> [options]"],options:[t("--plan <path>","screenshot plan yaml"),t("--session <tab-id>","reuse a live sootsim session for capture"),t("--app <target>","override the plan app target for this run"),t("--device <model>","override the plan capture device for this run"),t("--capture-only","stop after raw/framed intermediates"),t("--compose-only","skip flow capture and reuse existing raw screenshots")],examples:[e("sootsim screenshots --plan .sootsim/app-store.yaml"),e("sootsim screenshots --plan .sootsim/app-store.yaml --session tab-9","reuse an already-open visible session"),e("sootsim screenshots --plan .sootsim/app-store.yaml --compose-only","rerender marketing canvases from existing raw PNGs")],related:["flow","screenshot","record"]},{id:"screenshot-mode",slug:"screenshot-mode",navTitle:"screenshot-mode",title:"sootsim screenshot-mode",description:"toggle the screenshot-mode chrome overlay",summary:'Flip the live session into (or out of) "screenshot mode" \u2014 the same state toggled from the MacMenuBar / rail. Hides the DOM browser chrome around the canvas so a plain `sootsim screenshot` can grab clean marketing-ready frames. Orthogonal to `screenshot --no-shell` (which excludes the iOS shell surfaces from inside the canvas).',order:72,usage:["sootsim screenshot-mode [on|off|toggle]"],options:[],examples:[e("sootsim screenshot-mode","toggle the current state"),e("sootsim screenshot-mode on","force screenshot-mode on"),e("sootsim screenshot-mode off","force screenshot-mode off")],related:["screenshot"]},{id:"assert",slug:"assert",navTitle:"assert",title:"sootsim assert",description:"run any read verb and convert its output into an exit code",summary:"Thin wrapper that spawns a read verb (describe, find, get errors, \u2026) with --json forced on, parses stdout, and evaluates assertion flags over the payload. Exit codes are bisect-friendly: 0 pass, 1 fail, 125 skip (app not loaded / verb crashed), 2 misuse. Flag-level composition (--count / --contains / --jq / --has-path / --within) means the same predicate surface works across every read verb.",order:69,usage:["sootsim assert <verb> <verb-args...> <assertion-flags...>"],options:[t("--count <n>","result array has exactly n items"),t("--count-at-least <n>","length >= n"),t("--count-at-most <n>","length <= n"),t("--exists","non-null, non-empty (default when no flag given)"),t("--empty","null or zero length"),t("--contains <str>","stringified result contains str (repeatable)"),t("--not-contains <str>","stringified result does not contain str"),t("--matches <regex>","stringified result matches regex"),t("--not-matches <regex>","stringified result does not match regex"),t("--equals <literal>","scalar equality"),t("--has-path <dotted.path>","nested path exists"),t("--path-equals <path> <value>","nested path equals value"),t("--jq <expr>","jq expression is truthy (requires jq on PATH)"),t("--within <ms>","retry verb + predicates until pass or deadline"),t("--allow-timeout","treat verb auto-wait timeout as fail, not skip"),t("--negate, !","flip pass/fail (does not flip skip)"),t("--quiet","suppress the one-line pass/fail summary")],examples:[e("sootsim assert find --testid set-max-input --count 4","exactly four matching nodes"),e("sootsim assert describe --contains swap-form-header","substring anywhere in the tree dump"),e("sootsim assert get errors --count 0","fail the commit if any console error"),e("sootsim assert find --testid submit --exists --within 3000","poll for 3s before failing"),e(`sootsim assert describe --jq '.shell.state == "app"'`,"jq predicate over the payload"),e("git bisect run sootsim assert find --testid set-max-input --count 4","git-bisect integration")],related:["find","describe","get","list"]},{id:"get",slug:"get",navTitle:"get",title:"sootsim get",description:"read runtime state (tree, a11y, url, count, errors, \u2026)",summary:"Grouping verb for pure reads. `sootsim get <noun>` covers the read side of the bridge \u2014 tree, a11y, url, count, console errors/warnings/requests, layout, globals, and single-node lookups.",order:70,usage:["sootsim get <noun> [args]"],examples:[e("sootsim get tree"),e("sootsim get a11y 3"),e("sootsim get url"),e("sootsim get count"),e("sootsim get errors 5"),e("sootsim get requests 5"),e("sootsim get node loginButton"),e("sootsim get layout loginButton"),e("sootsim get globals")],related:["do","find","describe","inspect"]},{id:"do",slug:"do",navTitle:"do",title:"sootsim do",description:"drive the app (tap, type, scroll, swipe, key, reload, \u2026)",summary:"Grouping verb for user-interaction actions. `sootsim do <action>` covers the write side of the bridge \u2014 taps, typing, key presses, touch/scroll/drag/swipe/pinch gestures, reload, and local timing helpers.",order:71,usage:["sootsim do <action> [args]"],examples:[e("sootsim do tap 196 727"),e("sootsim do double-tap 196 400"),e('sootsim do tap-text "Sign in"'),e("sootsim do tap-id loginNextButton"),e('sootsim do type "natew.bsky.social"'),e('sootsim do type-into email "me@example.com"'),e("sootsim do key return"),e("sootsim do key-sequence shift h i"),e("sootsim do dismiss"),e("sootsim do scroll feed 0 400"),e("sootsim do drag 200 720 200 160"),e("sootsim do swipe 200 720 200 160"),e("sootsim do gesture swipe-from-bottom-edge"),e("sootsim do reload")],related:["shell","get","find","describe","inspect","wait"]},{id:"wait",slug:"wait",navTitle:"wait",title:"sootsim wait",description:"block until the runtime reaches a known state",summary:"Grouping verb for explicit synchronization. Use instead of guessing with `sleep`. Every subverb has a bounded timeout and exits non-zero when it times out, so automations fail loudly rather than silently racing animations or missing nodes.",order:72,usage:["sootsim wait <kind> [args]"],examples:[e("sootsim wait ready","block until the app bundle is loaded and painted"),e("sootsim wait idle","block until animations + rAF queue are settled"),e("sootsim wait selector loginButton","block until a node with this testID is present"),e("sootsim wait selector feedList --max-ms 3000","cap how long to wait for a node")],related:["do","find","describe","get"]},{id:"shell",slug:"shell",navTitle:"shell",title:"sootsim shell",description:"drive shell state and simulator chrome",summary:"Top-level shell controls for app launch, home/switcher transitions, and simulator-owned chrome such as appearance and lock state.",order:72,usage:["sootsim shell launch <appId> [waitMs] [--clear-state]","sootsim shell home [waitMs]","sootsim shell switcher [waitMs]","sootsim shell open-card <appId> [waitMs]","sootsim shell appearance <light|dark|auto|toggle>","sootsim shell lock","sootsim shell shake"],examples:[e("sootsim shell launch photos"),e("sootsim shell home"),e("sootsim shell switcher 800"),e("sootsim shell appearance dark"),e("sootsim shell lock")],related:["do","inspect","debug"]},{id:"inspect",slug:"inspect",navTitle:"inspect",title:"sootsim inspect",description:"query, describe, and drive the running sootsim instance",summary:"Use the bridge-backed inspection surface for full access to the real node tree. `sootsim inspect <sub>` works for every subcommand in the surface; day-to-day, prefer the grouped verbs (`describe`, `find`, `get`, `do`, `shell`, `debug`) \u2014 run `sootsim --help` to see the full set.",order:80,usage:["sootsim inspect <subcommand> [args]"],options:[t("--session <session-id>","target a specific connected browser or desktop window"),t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--timeout <ms>","command timeout for bridge requests"),t("--base-url <url>","base shell URL used by open when wrapping bundle targets")],examples:[e("sootsim describe"),e("sootsim get errors 5"),e("sootsim get requests 5"),e('sootsim do tap-text "Sign in"'),e("sootsim list"),e("sootsim debug state shell --session tab-2")],related:["debug","get","do","shell","find","list","open","use"]},{id:"network",slug:"network",navTitle:"network",title:"sootsim network",description:"inspect live network traffic from the running sootsim worker",summary:"Read the shared observability store (fetch + XHR captured in the render worker) from the terminal. List recent requests, tail in real time, filter by URL or status, inspect one entry in detail, or clear the buffer. Shares its data source with the in-shell devtools dock so both views stay in lock-step.",order:85,usage:["sootsim network [limit]","sootsim network --failed","sootsim network --filter <substring>","sootsim network --tail","sootsim network get <id>","sootsim network clear"],options:[t("--failed","only show errored or non-2xx responses"),t("--filter <substring>","URL substring filter (case-insensitive)"),t("--limit <n>","max entries to print (default 20)"),t("--tail, -f","follow mode: stream new entries as they land"),t("--json","emit structured JSON instead of formatted rows"),t("--session <session-id>","target a specific connected session"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("sootsim network","list the last 20 requests"),e("sootsim network 100","list the last 100"),e("sootsim network --failed","only errors and 4xx/5xx"),e("sootsim network --filter api.example.com"),e("sootsim network --tail","live follow mode, ctrl-c to stop"),e("sootsim network --tail --failed"),e("sootsim network get req-42-l7k9"),e("sootsim network clear")],related:["inspect","debug","get","logs"]},{id:"logs",slug:"logs",navTitle:"logs",title:"sootsim logs",description:"inspect live console output from the running sootsim worker",summary:"Read the shared observability log store (console.log/info/warn/error/debug plus uncaught errors and unhandled rejections, captured in the render worker) from the terminal. List recent logs, tail in real time, filter by level or substring, or clear the buffer. Shares its data source with the devtools dock so both views stay in lock-step.",order:86,usage:["sootsim logs [limit]","sootsim logs --level <csv>","sootsim logs --filter <substring>","sootsim logs --tail","sootsim logs clear"],options:[t("--level <csv>","comma-separated level filter (log,info,warn,error,debug)"),t("--filter <substring>","message substring filter (case-insensitive)"),t("--limit <n>","max entries to print (default 50)"),t("--tail, -f","follow mode: stream new log entries as they land"),t("--json","emit structured JSON instead of formatted rows"),t("--session <session-id>","target a specific connected session"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("sootsim logs","last 50 log entries"),e("sootsim logs 200","last 200"),e("sootsim logs --level error,warn"),e('sootsim logs --filter "hydrate"'),e("sootsim logs --tail","live follow mode, ctrl-c to stop"),e("sootsim logs --tail --level error"),e("sootsim logs clear")],related:["network","inspect","get"]},{id:"debug",slug:"debug",navTitle:"debug",title:"sootsim debug",description:"drive __sootsimDebug: channels, snapshots, and inspectors",summary:"Toggle engine debug channels, capture snapshots, and diff internal state from the terminal while targeting any connected sootsim session.",order:90,usage:["sootsim debug <subcommand> [args]"],options:[t("--session <session-id>","target a specific connected session"),t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--json","emit compact JSON for machine consumption"),t("--pretty","emit formatted JSON for human inspection")],examples:[e("sootsim debug enable sheets,portals"),e("sootsim debug snapshot before"),e("sootsim debug diff before after"),e("sootsim debug recent portals 20")],related:["inspect","open"]},{id:"open",slug:"open",navTitle:"open",title:"sootsim open",description:"load a target into the current session or open a new one",summary:"Resolve a port, dev-server URL, bundle URL, or full shell URL, then load it into the current session by default. Fresh browser sessions open in a separate Chromium-family window.",order:100,usage:["sootsim open <port|target|bundle-url|sootsim-url> [--new]"],options:[t("--new","open a fresh browser window instead of reusing the current session"),t("--base-url <url>","base shell URL to wrap around bundle targets"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("sootsim open 8081"),e("sootsim open --new 8085"),e("sootsim open http://localhost:8082")],related:["list","use","close","electron"]},{id:"list",slug:"list",navTitle:"list",title:"sootsim list",description:"list connected sootsim sessions or available launch drivers",summary:"Show every connected browser tab / desktop window, or \u2014 with `--drivers` \u2014 enumerate the launch drivers (chromium, electron, playwright, system) along with their availability on the current machine.",order:110,usage:["sootsim list [--session <session-id>] [--json]","sootsim list --drivers"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--session <session-id>","highlight a specific session in the output"),t("--drivers, -D","list available launch drivers instead of connected sessions"),t("--json","emit array of session objects for scripts")],examples:[e("sootsim list"),e("sootsim list --session tab-6"),e("sootsim list --drivers","show every installable launch driver"),e('sootsim list --json | jq ".[] | select(.active)"')],related:["open","use","close","inspect"]},{id:"use",slug:"use",navTitle:"use",title:"sootsim use",description:"select a connected sootsim session",summary:"Make one connected session current by focusing it and saving it as the default target for later bridge-backed commands.",order:120,usage:["sootsim use [session-id]"],aliases:["focus"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--session <session-id>","target a specific connected session")],examples:[e("sootsim use"),e("sootsim use tab-6")],related:["list","open","close"]},{id:"claim",slug:"claim",navTitle:"claim",title:"sootsim claim",description:"take an exclusive lease on a sootsim session",summary:"Reserve a connected browser tab so other agents cannot send commands to it. Leases auto-expire after 60s of inactivity and refresh on every command from the owner. Use --force to steal the lease from another agent.",order:125,usage:["sootsim claim [session-id] [--force]"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--session <session-id>","target a specific connected session"),t("--force","take the lease even if another agent currently holds it")],examples:[e("sootsim claim"),e("sootsim claim tab-6"),e("sootsim claim tab-6 --force")],related:["list","use","open","close"]},{id:"close",slug:"close",navTitle:"close",title:"sootsim close",description:"close a connected sootsim session",summary:"Request that a session close itself over the bridge and wait for it to disappear from the session registry.",order:130,usage:["sootsim close [session-id]"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--session <session-id>","target a specific connected session")],examples:[e("sootsim close"),e("sootsim close tab-6")],related:["list","use"]},{id:"config",slug:"config",navTitle:"config",title:"sootsim config",description:"manage sootsim.config.ts",summary:"Create, validate, and inspect project-local configuration so sootsim setup becomes repeatable instead of a pile of ad hoc edits.",order:140,usage:["sootsim config <init|validate|show>"],examples:[e("sootsim config init"),e("sootsim config validate"),e("sootsim config show")],related:["setup-repo","compat"]},{id:"compat",slug:"compat",navTitle:"compat",title:"sootsim compat",description:"check package compatibility (alias: scan)",summary:"Scan a project or specific dependency against the compat registry and summarize where native packages are fully supported, partially shimmed, or still risky.",order:150,usage:["sootsim compat [package-name]"],aliases:["scan"],options:[t("--json","emit machine-readable JSON"),t("--brief","print a compact one-line summary"),t("--app <dir>","scan a target app directory instead of cwd")],examples:[e("sootsim compat"),e("sootsim compat react-native-reanimated"),e("sootsim scan --brief")],related:["setup-repo"]},{id:"electron",slug:"electron",navTitle:"electron",title:"sootsim electron",description:"launch the desktop companion",summary:"Launch the installed desktop companion directly, optionally opening a local Metro/dev-server port through the same shell URL resolver as `sootsim open`.",order:160,usage:["sootsim electron [--port <number>] [--driver <id>]"],options:[t("--port <number>","open a local app server through the sootsim shell, for example Metro on 8081"),t("--driver <id>","override the launch driver (electron|chromium|playwright|system)")],examples:[e("sootsim electron"),e("sootsim electron --port 8081"),e("sootsim electron --driver system","fall back to the OS default browser")],related:["open","list","debug"]},{id:"setup-repo",slug:"setup-repo",navTitle:"setup-repo",title:"sootsim setup-repo",description:"set up sootsim in your project",summary:"Detect the target app, add the right dependency, update the bundler config, and install a predictable dev script for new projects or monorepos.",order:180,usage:["sootsim setup-repo [options]"],options:[t("-y, --yes","skip confirmations and apply the plan immediately"),t("--dry-run","show the planned edits without changing files")],examples:[e("sootsim setup-repo"),e("sootsim setup-repo --yes"),e("sootsim setup-repo --dry-run")],related:["config","compat","install-desktop"]},{id:"daemon",slug:"daemon",navTitle:"daemon",title:"sootsim daemon",description:"manage the sootsim bridge as a login agent",summary:"Inspect, restart, start, stop, or remove the persistent sootsim bridge daemon. The daemon is registered automatically on first CLI use, so most users never need to run any of these subcommands directly. Use `sootsim daemon uninstall` to remove the launchd/systemd agent if you want sootsim off your machine.",order:173,usage:["sootsim daemon uninstall","sootsim daemon status","sootsim daemon restart","sootsim daemon start","sootsim daemon stop"],examples:[e("sootsim daemon status"),e("sootsim daemon restart"),e("sootsim daemon uninstall")],related:["server","open","install-desktop"]},{id:"server",slug:"server",navTitle:"server",title:"sootsim server",description:"run the sootsim bridge daemon in the foreground",summary:"Host the WS bridge so CLI commands work without needing vite or electron to start one. Any sootsim tab (browser, electron, headless playwright) that connects becomes drivable from the top-level inspect commands.",order:175,usage:["sootsim server [options]"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("-q, --quiet","suppress per-connection logging")],examples:[e("sootsim server"),e("sootsim server --quiet"),e("sootsim server --port 7668")],related:["electron","install-desktop","open"]},{id:"runtime",slug:"runtime",navTitle:"runtime",title:"sootsim runtime",description:"manage engine runtimes under ~/.sootsim/runtimes/",summary:"Install, list, switch, and remove versioned sootsim engine runtimes. The CLI downloads a tarball from the configured CDN (default sootbean.com), verifies its sha256, and extracts it. The daemon serves the active runtime to Electron and the browser shell.",order:176,usage:["sootsim runtime install [version]","sootsim runtime list","sootsim runtime use <version>","sootsim runtime remove <version>","sootsim runtime which"],options:[t("--channel <name>","channel for 'latest' (default: stable)"),t("--force","reinstall even if already on disk"),t("--no-set-active","don't switch active runtime after install")],examples:[e("sootsim runtime install"),e("sootsim runtime install 1.2.3"),e("sootsim runtime use 1.2.3"),e("sootsim runtime list")],related:["launch","server","daemon"]},{id:"launch",slug:"launch",navTitle:"launch",title:"sootsim launch",description:"one-shot: install runtime if needed, start daemon, open the app",summary:"The fast-path entrypoint. Ensures a runtime is installed + active (auto-installs the latest on first run), ensures the sootsim daemon is up, then launches the Electron desktop companion. Subsequent runs are a fast lockfile check + spawn.",order:177,usage:["sootsim launch [options]"],options:[t("--no-runtime-install","fail instead of auto-installing a runtime"),t("--channel <name>","runtime channel for auto-install (default: stable)"),t("--version <version>","specific runtime version to auto-install")],examples:[e("sootsim launch"),e("sootsim launch --version 1.2.3")],related:["runtime","server","daemon","install-desktop"]},{id:"install-desktop",slug:"install-desktop",navTitle:"install-desktop",title:"sootsim install-desktop",description:"download and install the optional desktop GUI",summary:"Fetch the right platform artifact (dmg / AppImage / exe) from the public release feed and install it into the OS-native location. The CLI + daemon are the canonical sootsim surface; the desktop GUI is optional. The installed app self-updates via electron-updater afterward.",order:185,usage:["sootsim install-desktop [options]"],options:[t("-y, --yes","skip confirmation and install immediately"),t("--force","reinstall even if the companion is already present")],examples:[e("sootsim install-desktop"),e("sootsim install-desktop --yes")],related:["electron","setup-repo","open"]},{id:"maestro",slug:"maestro",navTitle:"maestro",title:"sootsim maestro",description:"run existing Maestro YAML flows against sootsim",summary:"Drop-in replacement for the Maestro CLI. Runs real `.maestro/*.yaml` flows against a sootsim session \u2014 supports all three Maestro shapes (plain array, frontmatter, multi-doc). Flag surface mirrors the Maestro CLI for easy migration.",order:41,usage:["sootsim maestro test <flow.yaml> [options]","sootsim maestro --list-compat"],options:[t("--env KEY=VALUE","set env vars for ${KEY} interpolation (repeatable)"),t("--continuous","re-run the flow on file changes (alias for --watch)"),t("--format <fmt>","accepted for maestro cli compat (not used)"),t("--new","force a fresh browser window for this run"),t("--record","record a webm while the flow runs"),t("--slow <ms>","delay between steps for natural pacing"),t("--list-compat","print supported and unsupported Maestro verbs")],examples:[e("sootsim maestro test .maestro/login.yaml"),e("sootsim maestro --env USERNAME=alice test .maestro/login.yaml"),e("sootsim maestro test .maestro/flow.yaml --record"),e("sootsim maestro --list-compat")],related:["flow","detox","test","record"]},{id:"detox",slug:"detox",navTitle:"detox",title:"sootsim detox",description:"run existing Detox suites against sootsim on headless Chromium",summary:"Drop-in Detox runner. Existing `import from 'detox'` suites run unchanged via a Jest `moduleNameMapper` rewrite \u2014 no code edits, no config churn. Auto-launches a sootsim shell if none is running.",order:42,usage:["sootsim detox [options] [-- <jest args>]"],options:[t("--config <path>","use a specific jest config instead of the default"),t("--watch","jest watch mode"),t("--headed","keep the sootsim shell window visible"),t("-t <pattern>","pass a Jest --testNamePattern"),t("--no-launch","skip auto-launching a sootsim shell (manage it yourself)")],examples:[e("sootsim detox"),e("sootsim detox --watch"),e('sootsim detox -t "login flow"'),e("sootsim detox --headed","keep the shell visible while iterating on a failing suite")],related:["maestro","flow","test"]},{id:"upload",slug:"upload",navTitle:"upload",title:"sootsim upload",description:"publish the current bundle as a shareable /preview/<id> link",summary:"Capture the currently-loaded bundle + device spec from a running sootsim session and publish it to a shareable `/preview/<id>` URL. Unlisted-public (unguessable id), requires a signed-in desktop session. Attach a gzipped event stream with `--events` or a webm/mp4/gif recording with `--video` \u2014 the recording embeds inline in the preview page and in any PR sticky comment.",order:73,usage:["sootsim upload [options]"],options:[t("--origin <url>","upload target (default: auto \u2014 prefers localhost, falls back to sootbean.com). Override with SOOTSIM_UPLOAD_ORIGIN env var"),t("--events <path>","path to a gzipped events .jsonl.gz file to attach"),t("--video <path>","path to a webm/mp4/gif flow recording \u2014 embedded inline in the preview page"),t("--session <tab-id>","target a specific sootsim tab (see: sootsim list)"),t("--open","open the resulting /preview/<id> url in the browser"),t("--assets-only","drop API/JSON/HTML records before upload; keep only images, fonts, css, js, and binary blobs. live-data demos hit the real network at replay time instead of serving recorded API snapshots")],examples:[e("sootsim upload"),e("sootsim upload --open"),e("sootsim upload --origin http://localhost:3000 --open"),e("sootsim upload --events ./my-session.jsonl.gz"),e("sootsim upload --video /tmp/soot-flow.webm"),e("sootsim upload --assets-only","capture assets only, let API calls hit live at replay (for /download demo apps)")],related:["flow","record","login"]},{id:"skills",slug:"skills",navTitle:"skills",title:"sootsim skills",description:"install bundled sootsim skills into a project",summary:"Copy the bundled sootsim skill markdown files (a11y review, debug, perf, visual-diff, maestro, detox, test-flow) into a target project so Claude Code, Cursor, and other agent tools can discover them.",order:122,usage:["sootsim skills --list","sootsim skills <target-dir> [skill-name...]"],options:[t("-l, --list","list available bundled skills")],examples:[e("sootsim skills --list"),e("sootsim skills .claude/skills"),e("sootsim skills .claude/skills sootsim-debug sootsim-a11y")],related:["inspect","debug"]},{id:"login",slug:"login",navTitle:"login",title:"sootsim login",description:"sign in so preview uploads are associated with your account",summary:"Authenticate the local desktop session against the upload origin. Required before `sootsim upload` (and `sootsim flow --preview`) so published preview links are associated with your account.",order:190,usage:["sootsim login [--origin <url>]"],options:[t("--origin <url>","auth host (default: auto). Override with SOOTSIM_UPLOAD_ORIGIN")],examples:[e("sootsim login"),e("sootsim login --origin http://localhost:3000")],related:["logout","whoami","upload"]},{id:"logout",slug:"logout",navTitle:"logout",title:"sootsim logout",description:"clear the local desktop auth session",summary:"Remove the stored authentication credentials for the current machine. Next `sootsim upload` will prompt for login again.",order:191,usage:["sootsim logout"],examples:[e("sootsim logout")],related:["login","whoami"]},{id:"whoami",slug:"whoami",navTitle:"whoami",title:"sootsim whoami",description:"print the currently signed-in account",summary:"Show which account the current desktop session is signed in as (or report that no session is active).",order:192,usage:["sootsim whoami"],examples:[e("sootsim whoami")],related:["login","logout"]}],B=new Map(k.map(o=>[o.id,o])),D=new Map;for(let o of k){D.set(o.id,o);for(let i of o.aliases||[])D.set(i,o)}function A(o){return D.get(o)}function s(o,i,n,a,r,l){return{verb:o,description:i,usage:n,examples:a,notes:r,...l}}var S=[s("describe","show visible UI elements with type, text, position, size, and style. filters structural noise by default \u2014 use --all to include everything.",["sootsim describe [filter] [--verbose] [--watch] [--all] [--json]"],[e("sootsim describe"),e("sootsim describe button"),e("sootsim describe --verbose"),e("sootsim describe --all"),e("sootsim describe --json"),e("sootsim describe --watch")],"buttons show [button], tappable views show [tap], inputs show [input]. structural noise (icon wrappers, layout containers, duplicate text inside buttons) is pruned by default. use --all for the unfiltered view.",{shortDescription:"curated human-readable UI summary (default inspection tool)",options:[t("--verbose","include ids, labels, and extra style info"),t("--all","do not prune structural view nodes"),t("--watch","redraw as the UI changes (ctrl-c to stop)"),t("--json","emit structured JSON")],seeAlso:["tree","a11y","find"]}),s("find","unified finder (text, testid, role, type, pressable, visible)",["sootsim find <text>","sootsim find --text <text>","sootsim find --testid <id>","sootsim find --role <role>","sootsim find --type <component-type>","sootsim find --pressable","sootsim find --visible"],[e('sootsim find "Sign in"'),e("sootsim find --testid loginButton"),e("sootsim find --role button"),e("sootsim find --pressable"),e("sootsim find --visible --json"),e("sootsim find --testid loginButton --verbose")],"bare text matches findByText. flag-based forms replace the old `find-id` and `query <predicate>` subcommands. use --verbose to dump the full node JSON for each result instead of the one-liner summary.",{shortDescription:"locate nodes by text / testID / role / type / predicate",options:[t("--text <text>","match node text content (same as bare positional)"),t("--testid <id>","match node testID or id exactly"),t("--role <role>","match accessibilityRole"),t("--type <type>","match React component type"),t("--pressable","list all tappable nodes"),t("--visible","list all visible (non-zero) nodes"),t("--verbose, --dump","emit full node JSON for each result, not a one-liner"),t("--json","emit structured JSON")],seeAlso:["describe","node","selector"]}),s("count","show total node count",["sootsim get count [--json]"],[e("sootsim get count"),e("sootsim get count --json")],void 0,{group:"get",subgroup:"tree",shortDescription:"total node count",options:[t("--json","emit { nodes: n } for scripts")]}),s("layout","get layout info for a node by id",["sootsim get layout <id>"],void 0,void 0,{group:"get",subgroup:"tree",shortDescription:"layout {x, y, width, height} by id/testID"}),s("tree","dump the node tree",["sootsim get tree [depth] [--json]"],[e("sootsim get tree"),e("sootsim get tree 3"),e("sootsim get tree --json")],"raw, depth-limited tree dump. good for machine consumption and debugging when describe has filtered something out. for day-to-day inspection prefer describe.",{group:"get",subgroup:"tree",shortDescription:"raw node tree dump (default depth 5)",options:[t("--json","emit { depth, tree } for scripts")],seeAlso:["describe","a11y","node"]}),s("a11y","dump the accessibility tree with roles and labels",["sootsim get a11y [depth]"],[e("sootsim get a11y")],"only accessibility-relevant fields (role, label, hint). for general inspection use describe; for raw tree use get tree.",{group:"get",subgroup:"tree",shortDescription:"accessibility tree (roles, labels, hints)",seeAlso:["describe","tree"]}),s("node","resolve testID/id/text to a full node JSON",["sootsim get node <matcher>"],[e("sootsim get node loginButton"),e('sootsim get node "Sign in"')],void 0,{group:"get",subgroup:"tree",shortDescription:"resolve testID \u2192 id \u2192 text, full node JSON",seeAlso:["find","layout"]}),s("tap","tap at absolute coordinates, or resolve --testid / --text to a node center",["sootsim do tap <x> <y>","sootsim do tap --testid <id>","sootsim do tap --text <text>"],[e("sootsim do tap 196 400"),e("sootsim do tap --testid loginButton"),e('sootsim do tap --text "Sign in"')],"uses the internal interaction bridge (fireTap), not real PointerEvent dispatch. for gesture-handler debugging, use drag/swipe which dispatch real pointer events. --testid and --text also work on double-tap and long-press.",{group:"do",subgroup:"targeting",shortDescription:"tap absolute coords, or a node by --testid / --text",seeAlso:["tap-id","tap-text","double-tap","long-press"],order:10}),s("double-tap","tap the same spot twice with a short gap",["sootsim do double-tap <x> <y> [gapMs]","sootsim do double-tap --testid <id> [gapMs]"],[e("sootsim do double-tap 196 400"),e("sootsim do double-tap --testid myButton 80")],"uses the shared interact bridge so text selection and other double-tap gestures run through the same path as live taps.",{group:"do",subgroup:"targeting",shortDescription:"tap the same coordinates twice quickly",seeAlso:["tap"],order:20}),s("tap-text","find a node by text content and tap its center",["sootsim do tap-text <text> [--nth <n>] [--within <testID>]"," [--min-y <px>] [--max-y <px>] [--min-x <px>] [--max-x <px>]"," [--near <x> <y>] [--exact] [--role <role>] [--first]"],[e('sootsim do tap-text "Sign in"'),e("sootsim do tap-text Swap --within sheet-container"),e("sootsim do tap-text Swap --min-y 700"),e("sootsim do tap-text Cancel --nth 2"),e("sootsim do tap-text OK --near 283 784")],"errors with a ranked candidate list (testID + abs position + ancestor chain) when more than one visible match remains after filters, instead of silently picking one. pass --first to keep the old behavior.",{group:"do",subgroup:"targeting",shortDescription:"findByText then tap (rich disambiguation flags)",options:[t("--nth <n>","pick the nth visible match (supports -1)"),t("--within <testID>","descendants of a testID ancestor only"),t("--min-y <px>","absolute y-position floor"),t("--max-y <px>","absolute y-position ceiling"),t("--min-x <px>","absolute x-position floor"),t("--max-x <px>","absolute x-position ceiling"),t("--near <x> <y>","pick the closest match to a coordinate"),t("--exact","exact text match (default: substring)"),t("--role <role>","narrow to accessibilityRole"),t("--first","keep legacy pick-first-silently behavior")],seeAlso:["tap","tap-id","find"],order:30}),s("tap-id","find a node by testID and tap its center",["sootsim do tap-id <id>"],[e("sootsim do tap-id loginButton")],void 0,{group:"do",subgroup:"targeting",shortDescription:"findByTestId then tap center",seeAlso:["tap","tap-text"],order:40}),s("type","type text through the iOS keyboard (requires a focused input)",["sootsim do type <text>"],[e('sootsim do type "hello world"')],"a text input must be focused first via tap-id or tap-text. if no input is focused, this silently does nothing.",{group:"do",subgroup:"text input",shortDescription:"type through the iOS visual keyboard",seeAlso:["type-into","key","dismiss"],order:10}),s("type-into","find an input by testID, tap to focus, then type text",["sootsim do type-into <id> <text>"],[e('sootsim do type-into kav-input "hello world"')],"combines tap-id + type in one command. warns if the target is not a text input or if the keyboard did not open.",{group:"do",subgroup:"text input",shortDescription:"focus input by testID then type",seeAlso:["type","tap-id"],order:20}),s("key","press a special keyboard key",["sootsim do key <name>"],[e("sootsim do key return"),e("sootsim do key delete")],"valid keys: return, delete, space, shift, 123, ABC, #+=, num-back",{group:"do",subgroup:"text input",shortDescription:"special key (return, delete, space, shift, ...)",seeAlso:["key-sequence","keycode"],order:30}),s("key-sequence","press multiple visual keyboard keys in order",["sootsim do key-sequence <key> [<key> ...]"],[e("sootsim do key-sequence shift h i")],void 0,{group:"do",subgroup:"text input",shortDescription:"press multiple visual keyboard keys",seeAlso:["key","keycode"],order:40}),s("keycode","press DOM-style key codes through the visual keyboard",["sootsim do keycode <code> [<code> ...]"],[e("sootsim do keycode ShiftLeft KeyH KeyI")],void 0,{group:"do",subgroup:"text input",shortDescription:"press DOM-style key codes via visual keyboard",seeAlso:["key","key-sequence"],order:50}),s("dismiss","dismiss the keyboard",["sootsim do dismiss"],void 0,void 0,{group:"do",subgroup:"text input",shortDescription:"dismiss the iOS keyboard",order:60}),s("scroll","scroll a scrollview by testID",["sootsim do scroll <id> <dx> <dy>","sootsim do scroll --testid <id> <dx> <dy>"],[e("sootsim do scroll feed-list 0 500"),e("sootsim do scroll --testid feed-list 0 500")],void 0,{group:"do",subgroup:"gestures",shortDescription:"scroll a ScrollView by testID",seeAlso:["gesture","swipe"],order:10}),s("drag","drag from one point to another using real PointerEvent dispatch",["sootsim do drag <x1> <y1> <x2> <y2> [steps] [stepMs]"],[e("sootsim do drag 200 400 200 200 20 16")],"dispatches real PointerEvents to the canvas, unlike tap which uses the internal bridge. use this for gesture-handler and responder chain debugging.",{group:"do",subgroup:"gestures",shortDescription:"drag via real PointerEvents",seeAlso:["swipe","touch","pinch"],order:20}),s("swipe","swipe gesture (drag with fewer steps)",["sootsim do swipe <x1> <y1> <x2> <y2> [steps] [stepMs]"],[e("sootsim do swipe 350 400 50 400 10 16")],void 0,{group:"do",subgroup:"gestures",shortDescription:"drag with faster defaults",seeAlso:["drag","gesture"],order:30}),s("long-press","hold a press at coordinates",["sootsim do long-press <x> <y> [durationMs]","sootsim do long-press --testid <id> [durationMs]"],[e("sootsim do long-press 196 400 600"),e("sootsim do long-press --testid myButton 600")],void 0,{group:"do",subgroup:"gestures",shortDescription:"hold a press at coordinates",seeAlso:["tap","gesture"],order:40}),s("touch","low-level touch primitives",["sootsim do touch <down|move|up|cancel> <x> <y> [pointerId]"],[e("sootsim do touch down 200 720 1"),e("sootsim do touch up 200 720 1")],void 0,{group:"do",subgroup:"gestures",shortDescription:"low-level touch primitives",seeAlso:["drag","pinch"],order:50}),s("gesture","preset gestures such as scrolls and edge swipes",["sootsim do gesture <preset> [durationMs]"],[e("sootsim do gesture swipe-from-bottom-edge"),e("sootsim do gesture scroll-up 250")],void 0,{group:"do",subgroup:"gestures",shortDescription:"preset gestures (scroll / edge swipes)",seeAlso:["swipe","scroll"],order:60}),s("pinch","two-pointer pinch helper",["sootsim do pinch <x1> <y1> <x2> <y2> <x1'> <y1'> <x2'> <y2'> [steps] [stepMs]"],[e("sootsim do pinch 120 320 280 320 90 320 310 320")],void 0,{group:"do",subgroup:"gestures",shortDescription:"two-pointer pinch helper",seeAlso:["touch","gesture"],order:70}),s("state","dump raw runtime state (shell, worker, keyboard, node, scroll, hit, gesture)",["sootsim debug state <kind> [args]","sootsim debug state shell","sootsim debug state keyboard","sootsim debug state node <id>","sootsim debug state hit <x> <y>","sootsim debug state gesture <x> <y>"],[e("sootsim debug state shell"),e("sootsim debug state keyboard"),e("sootsim debug state node photos"),e("sootsim debug state hit 200 720")],void 0,{group:"debug",subgroup:"state",shortDescription:"runtime dumps (shell, worker, keyboard, hit, gesture, ...)",seeAlso:["js"]}),s("js","evaluate javascript in the browser context",["sootsim debug js <expression>"],[e("sootsim debug js SootSim.bridges.test.getNodeCount()"),e("sootsim debug js SootSim.bridges.keyboard.isVisible()"),e("sootsim debug js SootSim.state.root.children.length")],"reach into the SootSim global: bridges (test, debug, interact, keyboard, a11y, perf, ...), state (root, bridgeId, ...), chrome (shell, headless, engineBase, ...).",{group:"debug",subgroup:"eval",shortDescription:"evaluate JS in the engine realm (reach into SootSim.*)",seeAlso:["eval","state"]}),s("eval","evaluate javascript (same as js)",["sootsim debug eval <expression>"],[e('sootsim debug eval "document.title"')],void 0,{group:"debug",subgroup:"eval",shortDescription:"alias for js",seeAlso:["js"]}),s("errors","show recent console errors",["sootsim get errors [n|clear] [--json]"],[e("sootsim get errors 5"),e("sootsim get errors clear"),e("sootsim get errors --json | jq length")],void 0,{group:"get",subgroup:"diagnostics",shortDescription:"console errors (default 20)",options:[t("--json","emit array of { timestamp, args, stack } for scripts")],seeAlso:["warnings","requests","logs"]}),s("warnings","show recent console warnings",["sootsim get warnings [n] [--json]"],[e("sootsim get warnings 5"),e("sootsim get warnings --json")],void 0,{group:"get",subgroup:"diagnostics",shortDescription:"console warnings (default 20)",options:[t("--json","emit array of { timestamp, args } for scripts")],seeAlso:["errors","logs"]}),s("requests","show recent failed network requests",["sootsim get requests [n|all n|clear] [--json]"],[e("sootsim get requests 5"),e("sootsim get requests all 10"),e("sootsim get requests --json")],void 0,{group:"get",subgroup:"diagnostics",shortDescription:"failed / all / clear request buffer",options:[t("--json","emit array of request entries for scripts")],seeAlso:["errors","network"]}),s("animations","list every active animation with id, kind, progress, and current value",["sootsim get animations","sootsim get animations --json"],[e("sootsim get animations","one line per running animation"),e("sootsim get animations --json | jq","structured for scripts")],'answers "what is moving right now?" without resorting to screenshots or debug eval. pair with `sootsim get animation <id>` for full spec, `sootsim do stop-animation <id>` to halt one, or `sootsim debug trace anim on <id>` for per-tick values.',{group:"get",subgroup:"animations",shortDescription:"list active animations",seeAlso:["animation","stop-animation","state"]}),s("animation","full detail for a single active animation by id",["sootsim get animation <id>"],[e("sootsim get animation 3","JSON spec + current value + progress")],"ids come from `sootsim get animations`. poll this to watch a single animation settle without enabling a trace.",{group:"get",subgroup:"animations",shortDescription:"single animation detail",seeAlso:["animations","stop-animation"]}),s("state","session dashboard: url, active animation counts, diagnostics, shell scene",["sootsim get state"],[e("sootsim get state","what is this session doing right now")],'one-shot JSON snapshot answering "what is happening in this tab right now" \u2014 intended as the first call of a debugging session. separate from `sootsim debug state <kind>`, which dumps raw runtime state for one subsystem.',{group:"get",subgroup:"diagnostics",shortDescription:"session dashboard snapshot",seeAlso:["animations","errors","keyboard"]}),s("memory","show engine + worker memory usage (image cache + WASM + JS heap)",["sootsim get memory","sootsim get memory --json"],[e("sootsim get memory","image cache pressure + worker/host heap"),e("sootsim get memory --json | jq","structured for monitoring")],"pulls the engine queryStats memory block. use this to verify the image cache LRU is doing its job, watch WASM-pixel pressure during a long session, and catch regressions in worker heap usage. workerHeap and hostHeap are chrome-only (Safari omits performance.memory).",{group:"get",subgroup:"diagnostics",shortDescription:"engine + worker memory snapshot",options:[t("--json","emit { imageLoader, workerHeap, hostHeap } for scripts")],seeAlso:["state","animations"]}),s("stop-animation","stop a running animation by id, or stop all active animations",["sootsim do stop-animation <id|all>"],[e("sootsim do stop-animation 3"),e("sootsim do stop-animation all","freeze everything for inspection")],"the id comes from `sootsim get animations`. stopping fires the animation's finished callback with finished=false, matching react-native Animated.stop() semantics.",{group:"do",subgroup:"animations",shortDescription:"stop a running animation",seeAlso:["animations","animation"]}),s("perf","performance profiling (stats, start, stop, frames, transition)",["sootsim debug perf <stats|start|stop|frames|transition> [args]"],[e("sootsim debug perf stats"),e("sootsim debug perf start"),e("sootsim debug perf stop"),e("sootsim debug perf transition goHome")],void 0,{group:"debug",subgroup:"profiling",shortDescription:"frame-timing profiling"}),s("trace","opt-in trace recorders: shell animation frames or per-tick animation values",["sootsim debug trace shell [on [limit]|off|status|clear|<recentLimit>]","sootsim debug trace anim <on <id|all> [limit]|off [id|all]|status|clear|<id> [limit]>"],[e("sootsim debug trace shell on 240","arm the shell frame recorder"),e("sootsim debug trace shell 120","dump the last 120 shell samples"),e("sootsim debug trace anim on 3 240","record up to 240 ticks for anim #3"),e("sootsim debug trace anim 3 60","read last 60 ticks for anim #3"),e("sootsim debug trace anim on all","record every active animation")],"for a focused perf session: `get animations` to find the id, `trace anim on <id>` to arm, reproduce the issue, then `trace anim <id>` to pull per-tick value + velocity samples. `trace anim off` disables when done.",{group:"debug",subgroup:"profiling",shortDescription:"per-tick animation + shell frame trace",seeAlso:["animations","animation","stop-animation"]}),s("sample-color","average the canvas color over a pixel or rect; returns hex + rgba",["sootsim debug sample-color <x> <y> [w] [h]","sootsim debug sample-color --area x,y,w,h","sootsim debug sample-color --id <testID>","sootsim debug sample-color --text <text>"],[e("sootsim debug sample-color 196 400"),e("sootsim debug sample-color 100 200 50 80"),e("sootsim debug sample-color --id loginButton"),e('sootsim debug sample-color --text "Sign in" --json')],"coords are logical sootsim units. pixels are averaged across the rect. --json emits { r, g, b, a, hex, samples, rect } for scripts.",{group:"debug",subgroup:"profiling",shortDescription:"average canvas color over a rect",options:[t("--area x,y,w,h","rect to sample (coords are logical sootsim units)"),t("--id <testID>","sample over a node's layout rect"),t("--text <text>","sample over the node matching text"),t("--json","emit { r, g, b, a, hex, samples, rect }")],seeAlso:["screenshot"]}),s("screenshot","capture the canvas (full or a cropped area) as PNG",["sootsim screenshot [--output <path>]","sootsim screenshot --area x,y,w,h [--output <path>]","sootsim screenshot --id <testID> [--output <path>]","sootsim screenshot --text <text> [--output <path>]"],[e("sootsim screenshot"),e("sootsim screenshot --output /tmp/app.png"),e("sootsim screenshot --area 0,200,393,400"),e("sootsim screenshot --id loginButton --output button.png")],"area args use logical sootsim coords; default output is /tmp/sootsim-inspect.png.",{options:[t("--output <path>","write PNG here (default /tmp/sootsim-inspect.png)"),t("--area x,y,w,h","crop to rect (logical sootsim coords)"),t("--id <testID>","crop to a node's layout rect"),t("--text <text>","crop to the node matching text")],seeAlso:["sample-color","record"]}),s("url","show the current page URL",["sootsim get url [--json]"],[e("sootsim get url"),e("sootsim get url --json")],void 0,{group:"get",subgroup:"info",shortDescription:"current page URL",options:[t("--json","emit { url } for scripts")]}),s("reload","reload the page and wait for reconnection",["sootsim do reload"],void 0,void 0,{group:"do",subgroup:"lifecycle",shortDescription:"reload the page + wait for reconnect",seeAlso:["wait","ready"],order:10}),s("globals","list all __sootsim* globals available in the browser",["sootsim get globals"],void 0,void 0,{group:"get",subgroup:"info",shortDescription:"list available __sootsim* globals"}),s("sleep","simple local pause between commands",["sootsim do sleep [seconds]"],[e("sootsim do sleep 0.5")],void 0,{group:"do",subgroup:"lifecycle",shortDescription:"local pause between commands",seeAlso:["settle","idle"],order:40}),s("wait","wait for a browser/tab to reconnect",["sootsim do wait [seconds]"],[e("sootsim do wait 30")],void 0,{group:"do",subgroup:"lifecycle",shortDescription:"wait for browser/tab reconnect",seeAlso:["ready","idle","settle"],order:20}),s("settle","wait until the visible layout is stable (default max 3s)",["sootsim do settle [seconds] [--strict]"],[e("sootsim do settle 3"),e("sootsim do settle 2 --strict")],"default requires layout hash to be stable for 3 consecutive polls \u2014 this is the right signal for slide transitions, spring-driven position changes, and scroll momentum. --strict additionally requires the worker animation flags (hasActiveAnims, hasActiveNativeAnimations, hasPendingAnimationFrames) to all be false. use --strict only when the target app has no perpetual background animation, otherwise it will time out.",{group:"do",subgroup:"lifecycle",shortDescription:"wait for animations to settle",options:[t("--strict","also require all worker animation flags to be false (use only when no perpetual bg animation)")],seeAlso:["idle","ready","selector"],order:30}),s("ready","block until the guest app bundle is loaded and painted (20s default)",["sootsim wait ready [--max-ms <ms>]"],[e("sootsim wait ready"),e("sootsim wait ready --max-ms 30000","cold uniswap/expensify reload"),e("sootsim wait ready --max-ms 5000")],"authoritative signal: the persistent __sootsimExternalAppReady flag (set by the sootsim:externalAppReady event handler, cleared on reload). polls CLI-side so --max-ms can exceed the bridge per-command timeout. prefer this over `do wait`, which is a node-count heuristic that false-positives on ConnectRN skeletons. idempotent: calling it after the app is already ready returns in ~200ms. exits non-zero on timeout.",{group:"wait",shortDescription:"block until the guest app bundle is loaded and painted",options:[t("--max-ms <ms>","override the 20s default deadline")],seeAlso:["idle","selector","settle"],order:10}),s("idle","block until animations, rAF queue, and layout are all settled",["sootsim wait idle [--max-ms <ms>] [--strict]"],[e("sootsim wait idle"),e("sootsim wait idle --max-ms 2000 --strict")],"same signal as `do settle` but under the discoverable `wait` verb. exits non-zero on timeout.",{group:"wait",shortDescription:"block until animations + rAF + layout are settled",options:[t("--max-ms <ms>","override the idle deadline"),t("--strict","also require worker animation flags to all be false")],seeAlso:["ready","selector","settle"],order:20}),s("selector","block until a node with <testid> is present with non-zero layout",["sootsim wait selector <testid> [--max-ms <ms>]"],[e("sootsim wait selector loginButton"),e("sootsim wait selector feedList --max-ms 3000")],"playwright-style. when `find --testid <id>` returns no result, the CLI now prints this command as a hint. exits non-zero on timeout.",{group:"wait",shortDescription:"block until a testID appears with non-zero layout",options:[t("--max-ms <ms>","override the deadline")],seeAlso:["ready","idle","find"],order:30}),s("dispatch","dispatch a raw keyboard key (bypasses visual shift state)",["sootsim do dispatch <char>"],[e("sootsim do dispatch A")],void 0,{group:"do",subgroup:"text input",shortDescription:"raw key (bypasses visual shift state)",seeAlso:["key","type"],order:70}),s("keyboard","on-screen keyboard state (type, returnKey, autoCap, mode, shift, accessory)",["sootsim get keyboard [--json]"],[e("sootsim get keyboard"),e("sootsim get keyboard --json")],void 0,{group:"get",subgroup:"info",shortDescription:"on-screen keyboard state",options:[t("--json","emit structured JSON")],seeAlso:["type","dismiss","state"]}),s("screens","list the current navigation stack: active app, route stack, modal stack, keyboard",["sootsim get screens [--json]"],[e("sootsim get screens"),e("sootsim get screens --json")],'pulls from the shell (active app, switcher phase), the navigation bridge (route stack for react-navigation / one-router if present), and the top presented modal. use this when a screen id or nav-state is the fastest orientation signal \u2014 e.g. "am I on the onboarding landing, or did the login sheet open over it?". Exits quietly when no navigation bridge is available.',{group:"get",subgroup:"info",shortDescription:"current screen + modal + route stack",options:[t("--json","emit structured JSON")],seeAlso:["describe","state","keyboard"]})],O=new Map(S.map(o=>[o.verb,o]));function x(o){return O.get(o)}function f(o,i=w){return o.replaceAll("{{bridgePort}}",String(i.bridgePort)).replaceAll("{{defaultShellUrl}}",i.defaultShellUrl)}function R(){return k.slice().sort((o,i)=>o.order-i.order)}function y(o,i){let n=Math.max(...i.map(r=>r.name.length),0)+2,a=i.map(r=>` ${r.name.padEnd(n)}${r.desc}`);return` ${o}
3
- ${a.join(`
4
- `)}`}function j(o,i){let n=o.usage[0]??`sootsim ${o.verb}`,a=[i?`sootsim ${i} `:"","sootsim inspect ","sootsim "].filter(Boolean),r=n;for(let l of a)if(r.startsWith(l)){r=r.slice(l.length);break}return r}function C(o){return o.shortDescription??o.description}function I(o){return S.filter(i=>i.group===o)}function $(o){let i=I(o);if(!i.length)return"";let n=new Map;for(let l of i){let d=l.subgroup??"",p=n.get(d)??[];p.push(l),n.set(d,p)}let a=[];for(let l of i){let d=l.subgroup??"";a.includes(d)||a.push(d)}let r=[];for(let l of a){let d=n.get(l)??[];d.sort((g,v)=>{let c=g.order??Number.POSITIVE_INFINITY,b=v.order??Number.POSITIVE_INFINITY;return c!==b?c-b:g.verb.localeCompare(v.verb)});let p=d.map(g=>({name:j(g,o),desc:C(g)})),h=Math.max(...p.map(g=>g.name.length),0)+2,m=l?` ${l}:`:"",u=p.map(g=>` ${g.name.padEnd(h)}${g.desc}`);r.push([m,...u].filter(Boolean).join(`
5
- `))}return r.join(`
6
-
7
- `)}function H(o,i=w,n=""){let r={do:{title:"interact with the running sootsim instance",noun:"<action>"},get:{title:"read state from the running sootsim instance",noun:"<noun>"},debug:{title:"drive __sootsimDebug: channels, snapshots, inspectors",noun:"<tool>"},wait:{title:"block until a runtime condition is met",noun:"<condition>"}}[o];if(!r)return null;let l=$(o);if(!l)return null;let d=I(o).flatMap(u=>u.examples??[]).slice(0,8).map(u=>` ${f(u.command,i)}`).join(`
8
- `),p=`
9
- options:
10
- --port <port> WS bridge port (default: ${i.bridgePort})
11
- --session <id> target a specific connected browser tab
12
- --timeout <ms> per-command timeout (default: 15000)${n?`
13
- `+n:""}`,m=`see also: ${["do","get","debug","wait"].filter(u=>u!==o).map(u=>`sootsim ${u} --help`).join(", ")}, sootsim inspect --help`;return`
14
- sootsim ${o} ${r.noun} \u2014 ${r.title}
15
-
16
- ${l}
17
- ${d?`
18
- examples:
19
- ${d}
20
- `:""}${p}
21
-
22
- ${m}
23
- `.trim()}function K(o=w,i=""){let n=new Set(["get","do","debug","wait","inspect"]),a=R().filter(c=>!n.has(c.id)).map(c=>` ${c.id.padEnd(16)}${f(c.description,o)}`).join(`
24
- `),r=I("find"),l=x("find"),d=r.length?r.map(c=>({name:j(c,null),desc:C(c)})):[{name:"find <text>",desc:"findByText (shorthand \u2014 bare text)"},...(l?.options??[]).map(c=>({name:`find ${c.flag.split(",")[0]?.trim()??""}`.trim(),desc:c.description}))],p=C(x("describe")??{verb:"describe",description:"",usage:[]}),h=c=>I(c).slice().sort((b,T)=>{let M=b.order??Number.POSITIVE_INFINITY,L=T.order??Number.POSITIVE_INFINITY;return M!==L?M-L:b.verb.localeCompare(T.verb)}).map(b=>({name:j(b,c),desc:C(b)})),m=h("get"),u=h("do"),g=h("debug"),v=h("wait");return`
25
- sootsim \u2014 iOS simulator for React Native
26
-
27
- usage:
28
- sootsim show getting-started + this help
29
- sootsim [command] [options]
30
- sootsim [-- <bundler-command>]
31
-
32
- commands:
33
- ${a}
34
-
35
- runtime verbs:
36
- sootsim describe [filter] [--verbose] [--json]
37
- ${p}
38
-
39
- ${y("sootsim find <...> unified finder",d)}
40
-
41
- ${y("sootsim get <noun> read runtime state",m)}
42
-
43
- ${y("sootsim do <action> drive the app",u)}
44
-
45
- sootsim shell <action> simulator shell + chrome
46
- launch <appId> launch an app
47
- home go home
48
- switcher open app switcher
49
- appearance <mode> set light/dark/auto/toggle
50
- lock toggle lock state
51
- shake trigger shake
52
-
53
- ${y("sootsim debug <tool> instrumentation",g)}
54
-
55
- ${y("sootsim wait <condition> block on runtime state",v)}
56
-
57
- session targeting:
58
- sootsim list show every connected session and its id
59
- sootsim use <session-id> make one session current (saved default)
60
- sootsim --session <id> <cmd> override the saved default for one call
61
- sootsim claim <session-id> take an exclusive lease on a session
62
-
63
- global flags:
64
- -h, --help show help
65
- -V, --version show version
66
- -v, --verbose verbose output
67
- -p, --port <number> server port
68
- ${i}
69
-
70
- examples:
71
- sootsim open start in browser
72
- sootsim open 8081 start in browser, opening React Native app on 8081
73
- sootsim electron open electron
74
- sootsim describe inspect the current UI tree
75
- sootsim find --testid loginButton look up a node by testID
76
- sootsim get errors 5 show the latest console failures
77
- sootsim get screens current route / modal / keyboard state
78
- sootsim do tap-text "Sign in" tap a node by its text
79
- sootsim debug state shell inspect shell runtime state
80
- sootsim shell appearance dark set simulator appearance
81
- `.trimStart()}function N(o,i){if(!o?.length)return"";let n=Math.max(...o.map(r=>r.flag.length),0)+2;return`
82
- options:
83
- ${o.map(r=>` ${f(r.flag,i).padEnd(n)}${f(r.description,i)}`).join(`
84
- `)}
85
- `}function q(o,i){let n=N(o.options,i),a=o.examples?.length?`
86
- examples:
87
- ${o.examples.map(m=>` ${f(m.command,i)}`).join(`
88
- `)}
89
- `:"",r=o.notes?`
90
- note:
91
- ${o.notes}
92
- `:"",l=o.seeAlso?.length?`
93
- see also: ${o.seeAlso.map(m=>`sootsim ${m}`).join(", ")}
94
- `:"",d=A(o.verb)!=null,p=o.group?`sootsim ${o.group} --help`:null,h=p?`
95
- full list: ${p}`:d?"":`
96
- full list: sootsim inspect --help`;return`
97
- sootsim ${o.verb} \u2014 ${o.description}
98
-
99
- usage:
100
- ${o.usage.map(m=>f(m,i)).join(`
101
- `)}${n}${a}${r}${l}${h}`.trimEnd()}function X(o,i=w){let n=x(o);if(n)return q(n,i);let a=A(o);if(!a)return null;let r=a.aliases?.length?`
102
- aliases:
103
- ${a.aliases.join(", ")}
104
- `:"",l=a.options?.length?`
105
- options:
106
- ${a.options.map(p=>` ${f(p.flag,i).padEnd(24)}${f(p.description,i)}`).join(`
107
- `)}
108
- `:"",d=a.examples?.length?`
109
- examples:
110
- ${a.examples.map(p=>` ${f(p.command,i)}`).join(`
111
- `)}
112
- `:"";return`
113
- sootsim ${a.id} \u2014 ${f(a.description,i)}
114
-
115
- usage:
116
- ${a.usage.map(p=>f(p,i)).join(`
117
- `)}${r}${l}${d}`.trimEnd()}var E={"--help":{type:"boolean",short:"-h"},"--version":{type:"boolean",short:"-V"},"--verbose":{type:"boolean",short:"-v"},"--port":{type:"number",short:"-p"},"--device":{type:"string",short:"-d"},"--theme":{type:"string",short:"-t"},"--headless":{type:"boolean"},"--driver":{type:"string"},"--session":{type:"string"},"--tab":{type:"string"},"--id":{type:"string"}},P=new Set(["list","describe","find","get","do","wait","network","logs","shell"]),U={tap:"sootsim do tap","double-tap":"sootsim do double-tap","tap-text":"sootsim do tap-text","tap-id":"sootsim do tap-id",type:"sootsim do type","type-into":"sootsim do type-into",key:"sootsim do key",dispatch:"sootsim do dispatch",dismiss:"sootsim do dismiss",scroll:"sootsim do scroll",drag:"sootsim do drag",swipe:"sootsim do swipe",pinch:"sootsim do pinch",reload:"sootsim do reload",sleep:"sootsim do sleep",settle:"sootsim do settle",tree:"sootsim get tree",a11y:"sootsim get a11y",count:"sootsim get count",layout:"sootsim get layout",url:"sootsim get url",globals:"sootsim get globals",errors:"sootsim get errors",warnings:"sootsim get warnings",requests:"sootsim get requests",node:"sootsim get node",state:"sootsim debug state",js:"sootsim debug js",eval:"sootsim debug eval",perf:"sootsim debug perf","sample-color":"sootsim debug sample-color","find-id":"sootsim find --testid",query:"sootsim find --text|--role|--type|--pressable|--visible",capture:"sootsim screenshot"},F=new Set(["preview","test","detox","maestro","flow","record","profile","screenshot","screenshots","inspect","debug","shell","assert","device","open","list","use","focus","claim","close","config","compat","scan","electron","login","logout","whoami","install","setup-repo","install-desktop","install-dev-desktop","server","daemon","runtime","launch","skills","upload","keys","hints","agent-wrapper","agent",...P,...Object.keys(U)]);function Z(o){let i=o.slice(2),n={command:null,commandArgs:[],globalFlags:{},help:!1,version:!1,verbose:!1},a=0;for(;a<i.length;){let r=i[a];if(r==="--"){n.commandArgs.push(...i.slice(a+1));break}let l=Object.entries(E).find(([d,p])=>d===r||p.short===r);if(l){let[d,p]=l,h=d.replace(/^--/,"");if(p.type==="boolean")n.globalFlags[h]=!0,a++;else{let m=i[a+1];if(m===void 0||m.startsWith("-")){console.error(` warning: ${r} requires a value`),a++;continue}if(p.type==="number"){let u=Number(m);if(Number.isNaN(u)){console.error(` warning: ${r} requires a number, got "${m}"`),a+=2;continue}n.globalFlags[h]=u}else n.globalFlags[h]=m;a+=2}continue}if(!n.command&&!r.startsWith("-")){if(F.has(r)){n.command=r;let d=i.slice(a+1);n.commandArgs=d[0]==="--"?d.slice(1):d;break}n.commandArgs=i.slice(a);break}n.commandArgs.push(r),a++}return n.help=!!n.globalFlags.help,n.version=!!n.globalFlags.version,n.verbose=!!n.globalFlags.verbose,n}export{H as a,K as b,X as c,P as d,U as e,Z as f};
@@ -1,3 +0,0 @@
1
- /*! sootsim v0.1.36 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{chmodSync as m,existsSync as g,mkdirSync as A,readFileSync as k,rmSync as s,writeFileSync as y}from"node:fs";import{homedir as D,platform as w}from"node:os";import{dirname as l,join as n,resolve as p}from"node:path";var u=1,f="SOOTSIM_SHARED_AUTH_FILE",a="https://sootbean.com";function v(){let t=process.env[f];if(t?.trim())return l(p(t));let e=D();if(!e)throw new Error("could not determine home directory for sootsim auth storage");switch(w()){case"darwin":return n(e,"Library","Application Support","sootsim");case"win32":return n(process.env.APPDATA||n(e,"AppData","Roaming"),"sootsim");default:return n(process.env.XDG_CONFIG_HOME||n(e,".config"),"sootsim")}}function c(){let t=process.env[f];return t?.trim()?p(t):n(v(),"desktop-auth.json")}function h(t){if(!t||typeof t!="object")return null;let e=t;return typeof e.id!="string"||!e.id.trim()?null:{id:e.id.trim(),name:typeof e.name=="string"?e.name:void 0,email:typeof e.email=="string"?e.email:void 0,image:typeof e.image=="string"?e.image:void 0}}function O(t){if(!t||typeof t!="object")return null;let e=t;if(e.version!==u||typeof e.token!="string"||!e.token.trim())return null;let r=typeof e.origin=="string"&&e.origin.trim()?e.origin.trim():a,o=e.source==="cli"||e.source==="electron"||e.source==="browser"||e.source==="unknown"?e.source:"unknown",i=typeof e.updatedAt=="string"&&e.updatedAt?e.updatedAt:new Date().toISOString(),S=typeof e.validatedAt=="string"&&e.validatedAt?e.validatedAt:void 0;return{version:u,token:e.token.trim(),user:h(e.user),origin:r,source:o,updatedAt:i,validatedAt:S}}function x(){let t=c();if(!g(t))return null;try{let e=JSON.parse(k(t,"utf8")),r=O(e);return r||(s(t,{force:!0}),null)}catch{return s(t,{force:!0}),null}}function I(t){let e=c();A(l(e),{recursive:!0});let r={version:u,token:t.token.trim(),user:t.user?h(t.user):null,origin:t.origin?.trim()||a,source:t.source,updatedAt:t.updatedAt||new Date().toISOString(),validatedAt:t.validatedAt};y(e,JSON.stringify(r,null,2)+`
3
- `);try{m(e,384)}catch{}return r}function d(){s(c(),{force:!0})}async function N(t){let e=x();if(!e?.token)return null;let r=t||e.origin||a;try{let o=await fetch(`${r.replace(/\/$/,"")}/api/auth/me`,{headers:{authorization:`Bearer ${e.token}`}});if(o.status===401)return d(),null;if(!o.ok)return e;let i=await o.json();return i.user?.id?I({token:e.token,user:i.user,origin:r,source:e.source,validatedAt:new Date().toISOString()}):(d(),null)}catch{return e}}export{x as a,I as b,d as c,N as d};