sootsim 0.1.181 → 0.1.183

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 (150) hide show
  1. package/README.md +2 -0
  2. package/dist-cli/bin.js +3 -3
  3. package/dist-cli/chunks/{agent-RKWUNS7I.js → agent-AFAFZKMT.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-5EPZRSPB.js → agent-wrapper-MV5WGYCS.js} +2 -2
  5. package/dist-cli/chunks/{app-fonts-EU7F6Z3S.js → app-fonts-ERD5J644.js} +2 -2
  6. package/dist-cli/chunks/{assert-RKFBXF3E.js → assert-IBMYHSKD.js} +2 -2
  7. package/dist-cli/chunks/beta-T7Z7WSTK.js +2 -0
  8. package/dist-cli/chunks/{camera-TD6M3UW4.js → camera-Y6XUIEAP.js} +2 -2
  9. package/dist-cli/chunks/{chunk-DFOAKJLV.js → chunk-3JW7SSYR.js} +2 -2
  10. package/dist-cli/chunks/{chunk-FSWR7SL3.js → chunk-4FVYQIRB.js} +1 -1
  11. package/dist-cli/chunks/{chunk-QRJWVT3L.js → chunk-4HJKPRWO.js} +30 -30
  12. package/dist-cli/chunks/{chunk-PHPGJPFL.js → chunk-64EKAMA3.js} +2 -2
  13. package/dist-cli/chunks/{chunk-V53IQMHL.js → chunk-6GCEDCO5.js} +2 -2
  14. package/dist-cli/chunks/{chunk-VBHBFJZ3.js → chunk-6TNJLDAQ.js} +1 -1
  15. package/dist-cli/chunks/chunk-77GTIGLV.js +1 -0
  16. package/dist-cli/chunks/{chunk-TF24QNTS.js → chunk-7SP7PFUR.js} +1 -1
  17. package/dist-cli/chunks/{chunk-X25HNIMN.js → chunk-AP22KA3Q.js} +1 -1
  18. package/dist-cli/chunks/chunk-B3W63SZA.js +13 -0
  19. package/dist-cli/chunks/{chunk-Y6PKI2Q2.js → chunk-BTIARV2B.js} +2 -2
  20. package/dist-cli/chunks/{chunk-JFDRB2AQ.js → chunk-BYKEWDZO.js} +2 -2
  21. package/dist-cli/chunks/{chunk-IE7FPLMT.js → chunk-C7BIPAXT.js} +1 -1
  22. package/dist-cli/chunks/{chunk-WY2T6Z4D.js → chunk-CD3KUHS3.js} +2 -2
  23. package/dist-cli/chunks/{chunk-WAC236L6.js → chunk-CPZNH3B3.js} +2 -2
  24. package/dist-cli/chunks/chunk-D7ZB6A5T.js +2 -0
  25. package/dist-cli/chunks/{chunk-TVUKWQKF.js → chunk-F6DIP2WH.js} +2 -2
  26. package/dist-cli/chunks/{chunk-RYOEWY42.js → chunk-FO3RPGBR.js} +2 -2
  27. package/dist-cli/chunks/{chunk-7IY6HRHX.js → chunk-FODJBAWY.js} +1 -1
  28. package/dist-cli/chunks/{chunk-N5BWYBPY.js → chunk-HW65ZTXM.js} +2 -2
  29. package/dist-cli/chunks/{chunk-IGSJWOD3.js → chunk-IJMDXS7N.js} +1 -1
  30. package/dist-cli/chunks/{chunk-JXIJPGAA.js → chunk-JR7LKXPB.js} +1 -1
  31. package/dist-cli/chunks/{chunk-VPPTIGEA.js → chunk-KSV6H2IC.js} +1 -1
  32. package/dist-cli/chunks/{chunk-UQYVIUVV.js → chunk-L3FPLPA4.js} +2 -2
  33. package/dist-cli/chunks/chunk-MBR6TG2P.js +118 -0
  34. package/dist-cli/chunks/{chunk-3DX6C7P7.js → chunk-N2EXJ7HK.js} +2 -2
  35. package/dist-cli/chunks/chunk-NLI4YQOY.js +1 -0
  36. package/dist-cli/chunks/{chunk-5KF6ZRPL.js → chunk-OIFGVAV2.js} +1 -1
  37. package/dist-cli/chunks/{chunk-MOLUWZMJ.js → chunk-PY4P54SD.js} +2 -2
  38. package/dist-cli/chunks/{chunk-7WGUVK3H.js → chunk-QF6RJQ2U.js} +2 -2
  39. package/dist-cli/chunks/{chunk-6OJJAJRM.js → chunk-QP4W3SGO.js} +1 -1
  40. package/dist-cli/chunks/{chunk-ELFPYLAD.js → chunk-QQDPEM7F.js} +2 -2
  41. package/dist-cli/chunks/{chunk-WJ7YLEHP.js → chunk-RAEIXWER.js} +2 -2
  42. package/dist-cli/chunks/{chunk-VYFQBXF5.js → chunk-RC3FKTYP.js} +2 -2
  43. package/dist-cli/chunks/{chunk-NXOSVZ5V.js → chunk-RQDJPT6N.js} +2 -2
  44. package/dist-cli/chunks/{chunk-EXA5KQMJ.js → chunk-RQXZANGL.js} +1 -1
  45. package/dist-cli/chunks/{chunk-VBQD7ZVI.js → chunk-SGWTX6CO.js} +2 -2
  46. package/dist-cli/chunks/{chunk-DXXFRQDT.js → chunk-UXHEGFHQ.js} +1 -1
  47. package/dist-cli/chunks/{chunk-KW2G6CYT.js → chunk-VSDVOOWG.js} +2 -2
  48. package/dist-cli/chunks/{chunk-XCQ5SHW4.js → chunk-W3G5M4UB.js} +3 -3
  49. package/dist-cli/chunks/{chunk-TVAG4EDK.js → chunk-WN6XO6JV.js} +1 -1
  50. package/dist-cli/chunks/{chunk-ANIFL7F2.js → chunk-WOOWTKM7.js} +1 -1
  51. package/dist-cli/chunks/{chunk-VNE4KDKB.js → chunk-WV7QBUFY.js} +2 -2
  52. package/dist-cli/chunks/{chunk-HIDL7MR4.js → chunk-XGVVYY6P.js} +2 -2
  53. package/dist-cli/chunks/{chunk-4GUWBLYM.js → chunk-YSTCZI6L.js} +1 -1
  54. package/dist-cli/chunks/cli-version-P4SZNBPQ.js +2 -0
  55. package/dist-cli/chunks/{compat-2LW53L3X.js → compat-ZTFIQOVI.js} +3 -3
  56. package/dist-cli/chunks/control-XWU7LZWC.js +2 -0
  57. package/dist-cli/chunks/{cpu-profile-ILWMOPGM.js → cpu-profile-3F2OQNCM.js} +2 -2
  58. package/dist-cli/chunks/daemon-CUQB4QAP.js +2 -0
  59. package/dist-cli/chunks/{debug-B3YI66U3.js → debug-7MIHREPG.js} +3 -3
  60. package/dist-cli/chunks/{detox-P4ZT3VE5.js → detox-CDYD54PS.js} +2 -2
  61. package/dist-cli/chunks/{device-L2NDHRYR.js → device-H53IDQ67.js} +2 -2
  62. package/dist-cli/chunks/{diagnose-A4HWLBVV.js → diagnose-YWSZCFPG.js} +2 -2
  63. package/dist-cli/chunks/drivers-RRCIIDGS.js +2 -0
  64. package/dist-cli/chunks/{electron-RO3TPVY6.js → electron-UPH35PTF.js} +3 -3
  65. package/dist-cli/chunks/flow-ZDJI7ICC.js +2 -0
  66. package/dist-cli/chunks/help-U5QGSMDR.js +2 -0
  67. package/dist-cli/chunks/{hints-G7S3VDTD.js → hints-BYH6BMHP.js} +2 -2
  68. package/dist-cli/chunks/{home-paths-OE3OK3DY.js → home-paths-AXKOJS62.js} +2 -2
  69. package/dist-cli/chunks/{inspect-WXM6H76N.js → inspect-TGQU6MZQ.js} +3 -3
  70. package/dist-cli/chunks/{install-desktop-IMVDXK6B.js → install-desktop-AOJACCWA.js} +3 -3
  71. package/dist-cli/chunks/{keys-UKEWXNIR.js → keys-RC6SP7XY.js} +2 -2
  72. package/dist-cli/chunks/{login-JMJIS5DW.js → login-EQDNEJFU.js} +4 -4
  73. package/dist-cli/chunks/{logout-G3BGSQEH.js → logout-QEDUWSSY.js} +2 -2
  74. package/dist-cli/chunks/{maestro-QZ5TJPFV.js → maestro-6SVETVRL.js} +2 -2
  75. package/dist-cli/chunks/optional-demo-registry-KXYBVP4S.js +2 -0
  76. package/dist-cli/chunks/{preview-7QFRBTZB.js → preview-UNXWRROE.js} +2 -2
  77. package/dist-cli/chunks/{profile-MEH7CCAS.js → profile-UBHQS25I.js} +2 -2
  78. package/dist-cli/chunks/{react-LXBEY3LK.js → react-THVP5NZX.js} +2 -2
  79. package/dist-cli/chunks/{record-MRCRQ6YX.js → record-SOACBRM3.js} +2 -2
  80. package/dist-cli/chunks/runtime-BAS2IZFB.js +2 -0
  81. package/dist-cli/chunks/{screenshot-ML2KX4BZ.js → screenshot-MJKWIVV6.js} +2 -2
  82. package/dist-cli/chunks/{screenshot-mode-RAZUOTEN.js → screenshot-mode-RP67FQSA.js} +2 -2
  83. package/dist-cli/chunks/{screenshots-BZGYDJXS.js → screenshots-LWDOE4RD.js} +2 -2
  84. package/dist-cli/chunks/{screenshots-capture-XNC3UM3X.js → screenshots-capture-RUXUL5GO.js} +2 -2
  85. package/dist-cli/chunks/{serve-XWSFQLD7.js → serve-TMMMRVTI.js} +2 -2
  86. package/dist-cli/chunks/{setup-IDWFS4I4.js → setup-WYKWQPJW.js} +2 -2
  87. package/dist-cli/chunks/{skills-ROKHF5AD.js → skills-P3HEQGPB.js} +2 -2
  88. package/dist-cli/chunks/{slides-CWXBPPJD.js → slides-MICILFXO.js} +2 -2
  89. package/dist-cli/chunks/store-7CIU7X6W.js +2 -0
  90. package/dist-cli/chunks/{telemetry-WXD6NMEZ.js → telemetry-7ZVCPD7R.js} +2 -2
  91. package/dist-cli/chunks/telemetry-LVBDBPPV.js +2 -0
  92. package/dist-cli/chunks/{test-ZAEKQ7VB.js → test-NT7SGPG5.js} +3 -3
  93. package/dist-cli/chunks/{three-mode-EGZZJ65O.js → three-mode-6ADYURNM.js} +2 -2
  94. package/dist-cli/chunks/{timeline-EBZ2SDVV.js → timeline-JZNPRKEH.js} +2 -2
  95. package/dist-cli/chunks/{upgrade-MZRMKVVS.js → upgrade-K6V3HYCU.js} +2 -2
  96. package/dist-cli/chunks/upload-Q2ZFKOSR.js +2 -0
  97. package/dist-cli/chunks/{version-CMX4HPI4.js → version-GS2J6SFA.js} +2 -2
  98. package/dist-cli/chunks/{web-R7FNAVBN.js → web-J2XC4OK4.js} +2 -2
  99. package/dist-cli/chunks/{what-happened-DUQVN5IX.js → what-happened-O5MCMYJL.js} +2 -2
  100. package/dist-cli/chunks/{whoami-HJIRUPJA.js → whoami-Z2Y2X2DN.js} +2 -2
  101. package/dist-lib/agent-daemon-client.cjs +1 -1
  102. package/dist-lib/agent-events.cjs +1 -1
  103. package/dist-lib/agent-sessions.cjs +1 -1
  104. package/dist-lib/attached-projects.cjs +1 -1
  105. package/dist-lib/auth/shared-session.cjs +1 -1
  106. package/dist-lib/backend-origin.cjs +1 -1
  107. package/dist-lib/beta.cjs +1 -1
  108. package/dist-lib/beta.mjs +1 -1
  109. package/dist-lib/bridge-constants.cjs +1 -1
  110. package/dist-lib/cli-constants.cjs +1 -1
  111. package/dist-lib/config.cjs +1 -1
  112. package/dist-lib/detox/index.cjs +1 -1
  113. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  114. package/dist-lib/home-paths.cjs +1 -1
  115. package/dist-lib/host/bridge-host.cjs +1 -1
  116. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  117. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  118. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  119. package/dist-lib/host/websocket-proxy.cjs +1 -1
  120. package/dist-lib/index.cjs +1 -1
  121. package/dist-lib/jump-to-source-babel.cjs +1 -1
  122. package/dist-lib/menu.cjs +1 -1
  123. package/dist-lib/menu.mjs +1 -1
  124. package/dist-lib/metro.cjs +1 -1
  125. package/dist-lib/profiles.cjs +1 -1
  126. package/dist-lib/render-mode.cjs +1 -1
  127. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  128. package/dist-lib/sdk.cjs +1 -1
  129. package/dist-lib/sdk.mjs +1 -1
  130. package/dist-lib/skills.cjs +29 -7
  131. package/dist-lib/vite.cjs +1 -1
  132. package/package.json +1 -1
  133. package/skills/soot.md +3 -0
  134. package/dist-cli/chunks/beta-VXVNIBH6.js +0 -2
  135. package/dist-cli/chunks/chunk-455VGZ2C.js +0 -118
  136. package/dist-cli/chunks/chunk-7VFWDDKF.js +0 -1
  137. package/dist-cli/chunks/chunk-DG25DVQA.js +0 -1
  138. package/dist-cli/chunks/chunk-MBWEZB7G.js +0 -2
  139. package/dist-cli/chunks/chunk-SFI66XFY.js +0 -13
  140. package/dist-cli/chunks/cli-version-VH6P4API.js +0 -2
  141. package/dist-cli/chunks/control-TPJXIRFC.js +0 -2
  142. package/dist-cli/chunks/daemon-BE42RCVB.js +0 -2
  143. package/dist-cli/chunks/drivers-PZZCFUEV.js +0 -2
  144. package/dist-cli/chunks/flow-XB73D57F.js +0 -2
  145. package/dist-cli/chunks/help-S4Z6P3KT.js +0 -2
  146. package/dist-cli/chunks/optional-demo-registry-QZ3N5NTN.js +0 -2
  147. package/dist-cli/chunks/runtime-BQEZQUBD.js +0 -2
  148. package/dist-cli/chunks/store-FK6E5CQO.js +0 -2
  149. package/dist-cli/chunks/telemetry-CBM2UIAG.js +0 -2
  150. package/dist-cli/chunks/upload-XQH6ZVU6.js +0 -2
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.181 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as Be}from"./chunk-HIDL7MR4.js";import{a as Le}from"./chunk-3DX6C7P7.js";import{a as ve,b as Ee,c as ne,d as re,e as Ie,f as ie,h as oe,i as Pe,j as Me}from"./chunk-N5BWYBPY.js";import{g as Re,o as Ne,q as Ce,u as De}from"./chunk-SFI66XFY.js";import{f as Oe}from"./chunk-V53IQMHL.js";import{m as ke}from"./chunk-7WGUVK3H.js";import{m as Se,n as xe,o as Te,p as $e}from"./chunk-ANIFL7F2.js";import{a as Ae}from"./chunk-TF24QNTS.js";import{a as _e}from"./chunk-TVAG4EDK.js";import{a as je}from"./chunk-JFDRB2AQ.js";import{c as ee,d as te,e as Fe}from"./chunk-NXOSVZ5V.js";import{a as ge,b as Z}from"./chunk-KW2G6CYT.js";import{d as Q,e as be}from"./chunk-6OJJAJRM.js";import{g as ye}from"./chunk-DXXFRQDT.js";import*as _ from"fs";import{tmpdir as He}from"os";import*as b from"path";import*as v from"fs";import*as g from"path";import{spawnSync as nt}from"child_process";import*as A from"vm";var rt=3e5,it=`
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{a as Be}from"./chunk-XGVVYY6P.js";import{a as Ve}from"./chunk-N2EXJ7HK.js";import{a as Se,b as Ie,c as re,d as ne,e as Me,f as ie,h as se,i as Pe,j as oe}from"./chunk-HW65ZTXM.js";import{h as Re,p as Ne,s as Ce,w as De}from"./chunk-B3W63SZA.js";import{f as Oe}from"./chunk-6GCEDCO5.js";import{m as Ee}from"./chunk-QF6RJQ2U.js";import{m as xe,n as Te,o as $e,p as ke}from"./chunk-WOOWTKM7.js";import{a as Ae}from"./chunk-7SP7PFUR.js";import{a as _e}from"./chunk-WN6XO6JV.js";import{a as je}from"./chunk-BYKEWDZO.js";import{c as ee,d as te,e as Fe}from"./chunk-RQDJPT6N.js";import{a as ye,b as Z}from"./chunk-VSDVOOWG.js";import{d as Q,e as ve}from"./chunk-QP4W3SGO.js";import{g as be}from"./chunk-UXHEGFHQ.js";import*as _ from"fs";import{tmpdir as He}from"os";import*as b from"path";import*as v from"fs";import*as g from"path";import{spawnSync as rt}from"child_process";import*as A from"vm";var nt=3e5,it=`
3
3
  let input = '';
4
4
  process.stdin.on('data', (c) => { input += c });
5
5
  process.stdin.on('end', async () => {
@@ -21,7 +21,7 @@ process.stdin.on('end', async () => {
21
21
  process.exitCode = 1;
22
22
  }
23
23
  });
24
- `;function D(i,e,t){if(t?.multipartForm)throw new Error("http: multipartForm is not supported by sootsim yet");let n={...process.env};for(let a of Object.keys(n))(a.startsWith("BUN_INSPECT")||a==="NODE_OPTIONS")&&delete n[a];let r=nt(process.execPath,["-e",it],{input:JSON.stringify({url:i,method:e,headers:t?.headers,body:t?.body}),encoding:"utf8",timeout:rt,maxBuffer:64*1024*1024,env:n});if(r.error)throw new Error(`http ${e} ${i} failed: ${r.error.message}`);let o;try{o=JSON.parse(r.stdout||"")}catch{throw new Error(`http ${e} ${i} failed: no response (${(r.stderr||"").trim().slice(0,200)})`)}let s=o;if(s.__error)throw new Error(`http ${e} ${i} failed: ${s.__error}`);return s}var ot=/(?<!\\)\$\{([^$]*)\}/g,st=/\\(\$\{[^$]*\})/g,at=/^[A-Za-z_$][A-Za-z0-9_$]*$/,U=class{context;envBinding=new Map;envScopeStack=[];syncedEnvKeys=new Set;output={};maestro;constructor(e={}){this.maestro={copiedText:null,platform:e.platform??"ios"};let t=e.onLog??(r=>console.log(`[flow] js: ${r}`)),n={output:this.output,maestro:this.maestro,http:{get:(r,o)=>D(r,"GET",o),post:(r,o)=>D(r,"POST",o),put:(r,o)=>D(r,"PUT",o),delete:(r,o)=>D(r,"DELETE",o),request:(r,o)=>D(r,(o?.method??"GET").toUpperCase(),o)},console:{log:(...r)=>t(r.map(o=>typeof o=="string"?o:Ve(o)).join(" "))}};this.context=A.createContext(n),A.runInContext(`
24
+ `;function D(i,e,t){if(t?.multipartForm)throw new Error("http: multipartForm is not supported by sootsim yet");let r={...process.env};for(let a of Object.keys(r))(a.startsWith("BUN_INSPECT")||a==="NODE_OPTIONS")&&delete r[a];let n=rt(process.execPath,["-e",it],{input:JSON.stringify({url:i,method:e,headers:t?.headers,body:t?.body}),encoding:"utf8",timeout:nt,maxBuffer:64*1024*1024,env:r});if(n.error)throw new Error(`http ${e} ${i} failed: ${n.error.message}`);let s;try{s=JSON.parse(n.stdout||"")}catch{throw new Error(`http ${e} ${i} failed: no response (${(n.stderr||"").trim().slice(0,200)})`)}let o=s;if(o.__error)throw new Error(`http ${e} ${i} failed: ${o.__error}`);return o}var st=/(?<!\\)\$\{([^$]*)\}/g,ot=/\\(\$\{[^$]*\})/g,at=/^[A-Za-z_$][A-Za-z0-9_$]*$/,U=class{context;envBinding=new Map;envScopeStack=[];syncedEnvKeys=new Set;output={};maestro;constructor(e={}){this.maestro={copiedText:null,platform:e.platform??"ios"};let t=e.onLog??(n=>console.log(`[flow] js: ${n}`)),r={output:this.output,maestro:this.maestro,http:{get:(n,s)=>D(n,"GET",s),post:(n,s)=>D(n,"POST",s),put:(n,s)=>D(n,"PUT",s),delete:(n,s)=>D(n,"DELETE",s),request:(n,s)=>D(n,(s?.method??"GET").toUpperCase(),s)},console:{log:(...n)=>t(n.map(s=>typeof s=="string"?s:Le(s)).join(" "))}};this.context=A.createContext(r),A.runInContext(`
25
25
  globalThis.__maestroScope = new Proxy(globalThis, {
26
26
  has() { return true },
27
27
  get(target, key) { return target[key] },
@@ -32,13 +32,13 @@ process.stdin.on('end', async () => {
32
32
  var yPercent = Math.ceil(y * 100) + '%'
33
33
  return xPercent + ',' + yPercent
34
34
  }
35
- `,this.context);for(let[r,o]of Object.entries(process.env))o!==void 0&&this.envBinding.set(r,o)}putEnv(e,t){this.envBinding.set(e,t)}setCopiedText(e){this.maestro.copiedText=e}enterEnvScope(){this.envScopeStack.push(new Map(this.envBinding))}leaveEnvScope(){let e=this.envScopeStack.pop();if(e){this.envBinding.clear();for(let[t,n]of e)this.envBinding.set(t,n)}}evaluate(e,t={}){let n=t.env;if(n&&Object.keys(n).length>0){this.enterEnvScope();try{for(let[r,o]of Object.entries(n))this.envBinding.set(r,o);return this.evalRaw(e)}finally{this.leaveEnvScope()}}return this.evalRaw(e)}evalRaw(e){this.syncEnvToContext();let t=e.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$\{/g,"\\${");return A.runInContext(`(function(){ with(__maestroScope){ return eval(\`${t}\`) } })()`,this.context)}syncEnvToContext(){let e=A.runInContext("globalThis",this.context);for(let t of this.syncedEnvKeys)this.envBinding.has(t)||delete e[t];this.syncedEnvKeys=new Set;for(let[t,n]of this.envBinding)try{e[t]=n,this.syncedEnvKeys.add(t)}catch{}}evaluateStringTemplate(e){return e.replace(ot,(n,r)=>{if(r.trim().length===0)return"";let o=this.evaluate(r);if(o===void 0&&at.test(r.trim()))throw new Error(`missing variable for flow placeholder: ${r.trim()}`);return Ve(o)}).replace(st,(n,r)=>r)}interpolateStep(e){return this.interpolateValue(e,null)}interpolateValue(e,t){return typeof e=="string"?t==="evalScript"?e:this.evaluateStringTemplate(e):Array.isArray(e)?t==="commands"||t==="onFlowStart"||t==="onFlowComplete"?e:e.map(n=>this.interpolateValue(n,t)):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([n,r])=>[n,this.interpolateValue(r,n)])):e}};function Ve(i){if(i===void 0)return"undefined";if(i===null)return"null";if(typeof i=="object")try{return JSON.stringify(i)}catch{return String(i)}return String(i)}function Je(i){if(i==null)return!1;if(typeof i=="boolean")return i;let e=String(i);if(e.trim().length===0||e.toLowerCase()==="false"||e==="undefined"||e==="null")return!1;let t=Number(e);return!(!Number.isNaN(t)&&t===0)}var W=1e4,H=393,O=852,Ue="__sootsimCliPerf";function p(i){return new Promise(e=>setTimeout(e,i))}function lt(i){return i?g.basename(i)===".maestro"?g.dirname(i):i:process.cwd()}function ct(i,e,t){let n=e.endsWith(".png")?e:`${e}.png`;if(g.isAbsolute(n))return n;if(t.mode==="flow"&&/[\\/]/.test(n)){let r=n.startsWith("./")||n.startsWith("../")?t.flowDir??process.cwd():lt(t.flowDir);return g.resolve(r,n)}return g.join(i,n)}function dt(i){if(typeof i=="string")return{path:i,withFrame:!1};let e=i.path?.trim()||i.name?.trim();if(!e)throw new Error("takeScreenshot object form requires path or name");return{path:e,withFrame:i.withFrame===!0,layers:i.layers}}function se(i){let[e,t]=i.split(",").map(r=>r.trim()),n=(r,o)=>r.endsWith("%")?Number.parseFloat(r)/100*o:Number.parseFloat(r);return{x:n(e,H),y:n(t,O)}}function ut(i){switch(i){case"back":return{back:!0};case"hideKeyboard":return{hideKeyboard:!0};case"waitForAnimationToEnd":return{waitForAnimationToEnd:!0};case"stopApp":return{stopApp:!0};case"clearState":return{clearState:!0};case"clearKeychain":return{clearKeychain:!0};case"eraseText":return{eraseText:50};case"scroll":case"scrollDown":return{scroll:{direction:"DOWN"}};case"scrollUp":return{scroll:{direction:"UP"}};default:return{[i]:!0}}}function ft(i){let e=i.split(/^---$/m),t=e.length>1?e[e.length-1]:i,n=Ee.parse(t);return Array.isArray(n)?n:[]}var K=class{constructor(e,t){this.bridge=e;this.opts=t}stepDelay=0;js=new U({platform:"ios",onLog:e=>console.log(`[flow] js: ${e}`)});firstLaunchDone=!1;profilingEnabled=!1;recordingEnabled=!1;recordingAccessChecked=!1;recordingStartedAtMs=null;lastRecordingDurationMs=null;simRouteHint=null;lastFailedStep=null;get simId(){return this.opts.simId}setSimRouteHint(e){this.simRouteHint=this.normalizeSimRoute(e)}normalizeSimRoute(e){if(!e)return null;try{let t=new URL(e);t.searchParams.delete("inspectOpen");let n=t.searchParams.toString();return`${t.origin}${t.pathname}${n?`?${n}`:""}`}catch{return e}}async refreshSimId(){if(this.opts.simId)try{let e=await this.bridge.listSims(),t=e.find(l=>l.id===this.opts.simId);if(t&&(this.simRouteHint=this.normalizeSimRoute(t.url||t.origin)),e.find(l=>l.id===this.opts.simId&&l.readyState==="open")||!this.simRouteHint)return;let r=this.simRouteHint&&e.find(l=>l.readyState==="open"&&this.normalizeSimRoute(l.url||l.origin)===this.simRouteHint);if(!r)return;let o=e.find(l=>l.isPrimary&&l.readyState==="open"),s=e.find(l=>l.readyState==="open"),a=r??o??s;a&&(this.opts.simId=a.id,console.log(` [flow] sim rotated to ${a.id}`))}catch{}}async evaluate(e,t){return this.bridge.send({type:"evaluate",simId:this.opts.simId,code:e},t?{timeoutMs:t}:void 0)}async callTest(e,...t){return this.bridge.send({type:"call",simId:this.opts.simId,path:`__sootsimTest.${e}`,args:t})}async waitForTree(e=W){let t=Date.now()+e,n=Date.now(),r=null,o=0,s=0,a=-1,l=n;for(;Date.now()<t;){try{let c=await this.evaluate(Se,8e3);if(o=0,c?.externalError)throw new Error(`app failed to load: ${c.externalError}`);if(c){let h=Date.now();c.nodes!==a&&(a=c.nodes,l=h);let P=c.flag===!0&&Te(c),w=c.flag!==!0&&$e(c);if((P||w)&&c.externalReady!==!1&&!c.externalError&&!c.loadingText&&h-l>=xe)return}}catch(c){if(r=c instanceof Error?c:new Error(String(c)),r.message.startsWith("app failed to load:"))throw r;if(o++,o>=4)throw new Error(`target sim is not responding to the bridge (${r.message}). it is likely a stale orphan \u2014 run \`sootsim list\`, then close it with \`sootsim close <id>\` or start a fresh one with \`sootsim open --new <port>\`.`);await this.refreshSimId()}let f=Date.now()-n;f-s>=5e3&&(s=f,console.log(` [flow] waiting for app to be ready\u2026 (${Math.round(f/1e3)}s)`)),await p(500)}throw r?.message.startsWith("app failed to load:")?r:new Error(`app not ready after ${e}ms`)}async ensureRecordingStarted(){if(!this.opts.recordingOutputDir)throw new Error("recording output directory not configured");this.recordingAccessChecked||(await Le("flow --video",{originOverride:this.opts.billingOriginOverride,allowGitHubAuth:this.opts.allowGitHubRecording}),this.recordingAccessChecked=!0);let e=Date.now(),t=await this.evaluate(`(() => {
35
+ `,this.context);for(let[n,s]of Object.entries(process.env))s!==void 0&&this.envBinding.set(n,s)}putEnv(e,t){this.envBinding.set(e,t)}setCopiedText(e){this.maestro.copiedText=e}enterEnvScope(){this.envScopeStack.push(new Map(this.envBinding))}leaveEnvScope(){let e=this.envScopeStack.pop();if(e){this.envBinding.clear();for(let[t,r]of e)this.envBinding.set(t,r)}}evaluate(e,t={}){let r=t.env;if(r&&Object.keys(r).length>0){this.enterEnvScope();try{for(let[n,s]of Object.entries(r))this.envBinding.set(n,s);return this.evalRaw(e)}finally{this.leaveEnvScope()}}return this.evalRaw(e)}evalRaw(e){this.syncEnvToContext();let t=e.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$\{/g,"\\${");return A.runInContext(`(function(){ with(__maestroScope){ return eval(\`${t}\`) } })()`,this.context)}syncEnvToContext(){let e=A.runInContext("globalThis",this.context);for(let t of this.syncedEnvKeys)this.envBinding.has(t)||delete e[t];this.syncedEnvKeys=new Set;for(let[t,r]of this.envBinding)try{e[t]=r,this.syncedEnvKeys.add(t)}catch{}}evaluateStringTemplate(e){return e.replace(st,(r,n)=>{if(n.trim().length===0)return"";let s=this.evaluate(n);if(s===void 0&&at.test(n.trim()))throw new Error(`missing variable for flow placeholder: ${n.trim()}`);return Le(s)}).replace(ot,(r,n)=>n)}interpolateStep(e){return this.interpolateValue(e,null)}interpolateValue(e,t){return typeof e=="string"?t==="evalScript"?e:this.evaluateStringTemplate(e):Array.isArray(e)?t==="commands"||t==="onFlowStart"||t==="onFlowComplete"?e:e.map(r=>this.interpolateValue(r,t)):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([r,n])=>[r,this.interpolateValue(n,r)])):e}};function Le(i){if(i===void 0)return"undefined";if(i===null)return"null";if(typeof i=="object")try{return JSON.stringify(i)}catch{return String(i)}return String(i)}function Je(i){if(i==null)return!1;if(typeof i=="boolean")return i;let e=String(i);if(e.trim().length===0||e.toLowerCase()==="false"||e==="undefined"||e==="null")return!1;let t=Number(e);return!(!Number.isNaN(t)&&t===0)}var W=1e4,H=393,O=852,Ue="__sootsimCliPerf";function p(i){return new Promise(e=>setTimeout(e,i))}function lt(i){return i?g.basename(i)===".maestro"?g.dirname(i):i:process.cwd()}function ct(i,e,t){let r=e.endsWith(".png")?e:`${e}.png`;if(g.isAbsolute(r))return r;if(t.mode==="flow"&&/[\\/]/.test(r)){let n=r.startsWith("./")||r.startsWith("../")?t.flowDir??process.cwd():lt(t.flowDir);return g.resolve(n,r)}return g.join(i,r)}function dt(i){if(typeof i=="string")return{path:i,withFrame:!1};let e=i.path?.trim()||i.name?.trim();if(!e)throw new Error("takeScreenshot object form requires path or name");return{path:e,withFrame:i.withFrame===!0,layers:i.layers}}function ae(i){let[e,t]=i.split(",").map(n=>n.trim()),r=(n,s)=>n.endsWith("%")?Number.parseFloat(n)/100*s:Number.parseFloat(n);return{x:r(e,H),y:r(t,O)}}function ut(i){switch(i){case"back":return{back:!0};case"hideKeyboard":return{hideKeyboard:!0};case"waitForAnimationToEnd":return{waitForAnimationToEnd:!0};case"stopApp":return{stopApp:!0};case"clearState":return{clearState:!0};case"clearKeychain":return{clearKeychain:!0};case"eraseText":return{eraseText:50};case"scroll":case"scrollDown":return{scroll:{direction:"DOWN"}};case"scrollUp":return{scroll:{direction:"UP"}};default:return{[i]:!0}}}function ft(i){let e=i.split(/^---$/m),t=e.length>1?e[e.length-1]:i,r=Ie.parse(t);return Array.isArray(r)?r:[]}var K=class{constructor(e,t){this.bridge=e;this.opts=t}stepDelay=0;js=new U({platform:"ios",onLog:e=>console.log(`[flow] js: ${e}`)});firstLaunchDone=!1;profilingEnabled=!1;recordingEnabled=!1;recordingAccessChecked=!1;recordingStartedAtMs=null;lastRecordingDurationMs=null;lastVisualSettledAtMs=0;simRouteHint=null;lastFailedStep=null;get simId(){return this.opts.simId}setSimRouteHint(e){this.simRouteHint=this.normalizeSimRoute(e)}normalizeSimRoute(e){if(!e)return null;try{let t=new URL(e);t.searchParams.delete("inspectOpen");let r=t.searchParams.toString();return`${t.origin}${t.pathname}${r?`?${r}`:""}`}catch{return e}}async refreshSimId(){if(this.opts.simId)try{let e=await this.bridge.listSims(),t=e.find(l=>l.id===this.opts.simId);if(t&&(this.simRouteHint=this.normalizeSimRoute(t.url||t.origin)),e.find(l=>l.id===this.opts.simId&&l.readyState==="open")||!this.simRouteHint)return;let n=this.simRouteHint&&e.find(l=>l.readyState==="open"&&this.normalizeSimRoute(l.url||l.origin)===this.simRouteHint);if(!n)return;let s=e.find(l=>l.isPrimary&&l.readyState==="open"),o=e.find(l=>l.readyState==="open"),a=n??s??o;a&&(this.opts.simId=a.id,console.log(` [flow] sim rotated to ${a.id}`))}catch{}}async evaluate(e,t){return this.bridge.send({type:"evaluate",simId:this.opts.simId,code:e},t?{timeoutMs:t}:void 0)}async callTest(e,...t){return this.bridge.send({type:"call",simId:this.opts.simId,path:`__sootsimTest.${e}`,args:t})}async waitForTree(e=W){let t=Date.now()+e,r=Date.now(),n=null,s=0,o=0,a=-1,l=r;for(;Date.now()<t;){try{let c=await this.evaluate(xe,8e3);if(s=0,c?.externalError)throw new Error(`app failed to load: ${c.externalError}`);if(c){let h=Date.now();c.nodes!==a&&(a=c.nodes,l=h);let M=c.flag===!0&&$e(c),w=c.flag!==!0&&ke(c);if((M||w)&&c.externalReady!==!1&&!c.externalError&&!c.loadingText&&h-l>=Te)return}}catch(c){if(n=c instanceof Error?c:new Error(String(c)),n.message.startsWith("app failed to load:"))throw n;if(s++,s>=4)throw new Error(`target sim is not responding to the bridge (${n.message}). it is likely a stale orphan \u2014 run \`sootsim list\`, then close it with \`sootsim close <id>\` or start a fresh one with \`sootsim open --new <port>\`.`);await this.refreshSimId()}let f=Date.now()-r;f-o>=5e3&&(o=f,console.log(` [flow] waiting for app to be ready\u2026 (${Math.round(f/1e3)}s)`)),await p(500)}throw n?.message.startsWith("app failed to load:")?n:new Error(`app not ready after ${e}ms`)}async ensureRecordingStarted(){if(!this.opts.recordingOutputDir)throw new Error("recording output directory not configured");this.recordingAccessChecked||(await Ve("flow --video",{originOverride:this.opts.billingOriginOverride,allowGitHubAuth:this.opts.allowGitHubRecording}),this.recordingAccessChecked=!0);let e=Date.now(),t=await this.evaluate(`(() => {
36
36
  const rec = window.__sootsimRecorder
37
37
  if (!rec) return { ok: false, error: 'recorder unavailable on this page' }
38
38
  const format = ${JSON.stringify(this.opts.recordingFormat??"webm")}
39
39
  if (rec.state() === 'recording') return { ok: true, format }
40
40
  return rec.start({ format, fps: 24 })
41
- })()`);if(!t?.ok)throw new Error(t?.error||"recording unavailable on this page");this.recordingStartedAtMs||(this.recordingStartedAtMs=e)}async startRecording(){this.recordingEnabled=!0,await this.ensureRecordingStarted()}prepareRecording(){this.recordingEnabled=!0}getLastRecordingDurationMs(){return this.lastRecordingDurationMs}async stopRecording(){if(!this.recordingEnabled||!this.opts.recordingOutputDir)return null;this.recordingEnabled=!1;let e=Date.now(),t=await this.evaluate("window.__sootsimRecorder.stop()");if(!t?.ok)throw this.recordingStartedAtMs=null,new Error(t?.error||"recording stop failed");this.lastRecordingDurationMs=this.recordingStartedAtMs?Math.max(1,e-this.recordingStartedAtMs):null,this.recordingStartedAtMs=null;let n=[],r=0;for(;;){let l=await this.evaluate(`window.__sootsimRecorder.getBlobBase64({ offset: ${r}, chunk: 2097152 })`);if(!l||(n.push(Buffer.from(l.data,"base64")),r=l.offset,l.done))break}if(n.length===0)throw new Error("recording requested but no video buffer was produced");v.mkdirSync(this.opts.recordingOutputDir,{recursive:!0});let s=(t.mime||this.opts.recordingFormat||"video/webm").includes("mp4")?"mp4":"webm",a=g.join(this.opts.recordingOutputDir,`sootsim-${Date.now()}.${s}`);return v.writeFileSync(a,Buffer.concat(n)),a}async waitForRecordingTail(e){if(!this.recordingEnabled)return;let t=Math.max(0,Math.round(e.maxMs));if(t<=0)return;if(!e.smart){await p(t);return}let n=await Me({bridge:this.bridge,simId:this.opts.simId,maxMs:t,pollMs:80,stablePolls:6,strict:!0});await this.waitForVisualSettle(),console.log(n.settled?` [flow] recording tail settled in ${n.elapsed}ms`:` [flow] recording tail reached ${n.elapsed}ms budget`)}async findElement(e){if(e.childOf!=null||typeof e.index=="number")return this.findElementScoped(e);let t=!!e.text&&/[.*+?^$()[\]{}|\\]/.test(e.text);return this.evaluate(`(async () => {
41
+ })()`);if(!t?.ok)throw new Error(t?.error||"recording unavailable on this page");this.recordingStartedAtMs||(this.recordingStartedAtMs=e)}async startRecording(){this.recordingEnabled=!0,await this.ensureRecordingStarted()}prepareRecording(){this.recordingEnabled=!0}getLastRecordingDurationMs(){return this.lastRecordingDurationMs}async stopRecording(){if(!this.recordingEnabled||!this.opts.recordingOutputDir)return null;this.recordingEnabled=!1;let e=Date.now(),t=await this.evaluate("window.__sootsimRecorder.stop()");if(!t?.ok)throw this.recordingStartedAtMs=null,new Error(t?.error||"recording stop failed");this.lastRecordingDurationMs=this.recordingStartedAtMs?Math.max(1,e-this.recordingStartedAtMs):null,this.recordingStartedAtMs=null;let r=[],n=0;for(;;){let l=await this.evaluate(`window.__sootsimRecorder.getBlobBase64({ offset: ${n}, chunk: 2097152 })`);if(!l||(r.push(Buffer.from(l.data,"base64")),n=l.offset,l.done))break}if(r.length===0)throw new Error("recording requested but no video buffer was produced");v.mkdirSync(this.opts.recordingOutputDir,{recursive:!0});let o=(t.mime||this.opts.recordingFormat||"video/webm").includes("mp4")?"mp4":"webm",a=g.join(this.opts.recordingOutputDir,`sootsim-${Date.now()}.${o}`);return v.writeFileSync(a,Buffer.concat(r)),a}async waitForRecordingTail(e){if(!this.recordingEnabled)return;let t=Math.max(0,Math.round(e.maxMs));if(t<=0)return;if(!e.smart){await p(t);return}let r=await oe({bridge:this.bridge,simId:this.opts.simId,maxMs:t,pollMs:80,stablePolls:6,strict:!0});await this.waitForVisualSettle(),console.log(r.settled?` [flow] recording tail settled in ${r.elapsed}ms`:` [flow] recording tail reached ${r.elapsed}ms budget`)}async findElement(e){if(e.childOf!=null||typeof e.index=="number")return this.findElementScoped(e);let t=!!e.text&&/[.*+?^$()[\]{}|\\]/.test(e.text);return this.evaluate(`(async () => {
42
42
  const test = window.__sootsimTest
43
43
  if (!test) return null
44
44
  let node = null
@@ -187,18 +187,18 @@ process.stdin.on('end', async () => {
187
187
  )
188
188
  }
189
189
  return direct
190
- })()`)}async tap(e,t,n){return await this.bridge.send({type:"tap",simId:this.opts.simId,x:e,y:t,target:n})}async tapOn(e){let t=typeof e=="string"?{text:e}:e;if(t.point){let l=se(t.point);(await this.activatePressAt(l.x,l.y))?.ok||await this.tap(l.x,l.y),await p(300);return}let n=5e3,r=150,o=Date.now()+n,s=await this.findElement({text:t.text,id:t.id});for(;!s&&Date.now()<o;)await p(r),s=await this.findElement({text:t.text,id:t.id});if(s){let l=s.isTextInput?4:1;for(let f=0;f<l;f++){let c=f===0?s:await this.findElement({text:t.text,id:t.id});if(!c)break;let h=typeof c.nodeId=="number"?await this.resolveTapTarget(c.nodeId):null,P=h?.cx??c.absolutePosition.x+c.layout.width/2,w=h?.cy??c.absolutePosition.y+c.layout.height/2;if((await this.tap(P,w,{id:t.id??h?.target?.id??c.id??null,testID:t.id??h?.target?.testID??c.testID??null,text:t.text??h?.target?.text??h?.target?.accessibilityLabel??c.text??c.accessibilityLabel??null,type:h?.target?.type??c.type??null}))?.hit===!0){if(await p(300),!c.isTextInput||await this.waitForFocusedTextInput(600))return;continue}break}}if((await this.activatePressTarget(t))?.ok){await p(300);return}throw new Error(`tapOn: element not found: ${JSON.stringify(t)}`)}async tapAtCoords(e,t){await this.tap(e,t),await p(300)}async doubleTapAtCoords(e,t,n=80){let r=Math.max(0,Math.round(n));if(!await this.evaluate(`(async () => {
190
+ })()`)}async tap(e,t,r){return await this.bridge.send({type:"tap",simId:this.opts.simId,x:e,y:t,target:r})}async tapOn(e){let t=typeof e=="string"?{text:e}:e;if(t.point){let l=ae(t.point);(await this.activatePressAt(l.x,l.y))?.ok||await this.tap(l.x,l.y),await p(300);return}let r=5e3,n=150,s=Date.now()+r,o=await this.findElement({text:t.text,id:t.id});for(;!o&&Date.now()<s;)await p(n),o=await this.findElement({text:t.text,id:t.id});if(o){let l=o.isTextInput?4:1;for(let f=0;f<l;f++){let c=f===0?o:await this.findElement({text:t.text,id:t.id});if(!c)break;let h=typeof c.nodeId=="number"?await this.resolveTapTarget(c.nodeId):null,M=h?.cx??c.absolutePosition.x+c.layout.width/2,w=h?.cy??c.absolutePosition.y+c.layout.height/2;if((await this.tap(M,w,{id:t.id??h?.target?.id??c.id??null,testID:t.id??h?.target?.testID??c.testID??null,text:t.text??h?.target?.text??h?.target?.accessibilityLabel??c.text??c.accessibilityLabel??null,type:h?.target?.type??c.type??null}))?.hit===!0){if(await p(300),!c.isTextInput||await this.waitForFocusedTextInput(600))return;continue}break}}if((await this.activatePressTarget(t))?.ok){await p(300);return}throw new Error(`tapOn: element not found: ${JSON.stringify(t)}`)}async tapAtCoords(e,t){await this.tap(e,t),await p(300)}async doubleTapAtCoords(e,t,r=80){let n=Math.max(0,Math.round(r));if(!await this.evaluate(`(async () => {
191
191
  const interact = window.__sootsimInteract
192
192
  if (interact?.doubleTap) {
193
- return !!(await interact.doubleTap(${e}, ${t}, ${r}))
193
+ return !!(await interact.doubleTap(${e}, ${t}, ${n}))
194
194
  }
195
195
  if (!interact?.tap) return false
196
196
  const first = await interact.tap(${e}, ${t})
197
197
  if (!first || first.hit === false) return false
198
- await new Promise((resolve) => setTimeout(resolve, ${r}))
198
+ await new Promise((resolve) => setTimeout(resolve, ${n}))
199
199
  const second = await interact.tap(${e}, ${t})
200
200
  return !!second && second.hit !== false
201
- })()`))throw new Error(`doubleTapAtCoords failed at (${e}, ${t})`);await p(300)}async assertVisible(e){let t=typeof e=="string"?{text:e}:e;for(let o=0;o<15;o++){if(await this.isElementVisible(t))return;await p(200)}let n=await this.findElement(t),r=n?` (matched node at y=${Math.round(n.absolutePosition.y)} h=${Math.round(n.layout.height)} \u2014 off-screen)`:" (no matching node in tree)";throw new Error(`assertVisible: ${JSON.stringify(t)} not visible${r}`)}async assertNotVisible(e){let t=typeof e=="string"?{text:e}:e;if(await this.isElementVisible(t)){let n=await this.findElement(t),r=n?` at (${Math.round(n.absolutePosition.x)},${Math.round(n.absolutePosition.y)})`:"";throw new Error(`assertNotVisible: ${JSON.stringify(t)} IS visible${r}`)}}async isElementVisible(e){let t=await this.findElement(e);return!!(t&&t.absolutePosition.y+t.layout.height>0&&t.absolutePosition.y<O)}async inputText(e){await this.waitForFocusedTextInput(),await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"type",text:e}),await p(200)}async waitForFocusedTextInput(e=1500){let t=Date.now()+e;for(;Date.now()<t;){try{if(await this.callTest("getFocusedNode"))return!0}catch{}await p(50)}return!1}async pressKey(e){await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"press",text:e}),await p(120)}async dispatchKey(e){await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"dispatchKey",text:e}),await p(120)}async hideKeyboard(){try{await this.callTest("blurFocusedTextInput")}catch{}await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"dismiss"});let e=Date.now()+3e3;for(;Date.now()<e;){let t=await this.evaluate(`(() => {
201
+ })()`))throw new Error(`doubleTapAtCoords failed at (${e}, ${t})`);await p(300)}async assertVisible(e){let t=typeof e=="string"?{text:e}:e;for(let s=0;s<15;s++){if(await this.isElementVisible(t))return;await p(200)}let r=await this.findElement(t),n=r?` (matched node at y=${Math.round(r.absolutePosition.y)} h=${Math.round(r.layout.height)} \u2014 off-screen)`:" (no matching node in tree)";throw new Error(`assertVisible: ${JSON.stringify(t)} not visible${n}`)}async assertNotVisible(e){let t=typeof e=="string"?{text:e}:e;if(await this.isElementVisible(t)){let r=await this.findElement(t),n=r?` at (${Math.round(r.absolutePosition.x)},${Math.round(r.absolutePosition.y)})`:"";throw new Error(`assertNotVisible: ${JSON.stringify(t)} IS visible${n}`)}}async isElementVisible(e){let t=await this.findElement(e);return!!(t&&t.absolutePosition.y+t.layout.height>0&&t.absolutePosition.y<O)}async inputText(e){await this.waitForFocusedTextInput(),await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"type",text:e}),await p(200)}async waitForFocusedTextInput(e=1500){let t=Date.now()+e;for(;Date.now()<t;){try{if(await this.callTest("getFocusedNode"))return!0}catch{}await p(50)}return!1}async pressKey(e){await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"press",text:e}),await p(120)}async dispatchKey(e){await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"dispatchKey",text:e}),await p(120)}async hideKeyboard(){try{await this.callTest("blurFocusedTextInput")}catch{}await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"dismiss"});let e=Date.now()+3e3;for(;Date.now()<e;){let t=await this.evaluate(`(() => {
202
202
  const keyboard =
203
203
  window.__sootsimKeyboard ??
204
204
  window.SootSim?.bridges?.keyboard ??
@@ -208,7 +208,7 @@ process.stdin.on('end', async () => {
208
208
  visible: !!keyboard?.isVisible?.(),
209
209
  focused: !!test?.isTextInputFocused?.(),
210
210
  }
211
- })()`);if(!t?.visible&&!t?.focused){await p(80);return}await p(80)}}async eraseText(e){for(let t=0;t<e;t++)await this.pressKey("Backspace");await p(100)}async waitFor(e){let t=Date.now()+(e.timeout||W);for(;Date.now()<t;){if(await this.findElement(e))return;await p(200)}let n=await this.findElement(e),r=n?` (matched node y=${Math.round(n.absolutePosition.y)} \u2014 still off-screen at deadline)`:"";throw new Error(`waitFor: ${JSON.stringify(e)} not found after ${e.timeout||W}ms${r}`)}async takeScreenshot(e){try{await this.waitForVisualSettle()}catch{}await p(250);let t=dt(e),n=ct(this.opts.screenshotDir,t.path,{mode:this.opts.screenshotPathMode??"dir",flowDir:this.opts.flowDir});v.mkdirSync(g.dirname(n),{recursive:!0});let r=t.layers??this.opts.screenshotLayers,o={type:"screenshot",simId:this.opts.simId};r&&r!=="full"&&(o.layers=r);let a=(await this.bridge.send(o)).replace(/^data:image\/png;base64,/,""),l=Buffer.from(a,"base64");if(t.withFrame){let f=await this.readCurrentDeviceModel();if(!f)throw new Error("could not read current device model for framed screenshot");let c=await Be(l,f);v.writeFileSync(n,c),console.log(`[flow] screenshot: ${n} (frame: ${f})`);return}v.writeFileSync(n,l),console.log(`[flow] screenshot: ${n}`)}async readCurrentDeviceModel(){let e=await this.bridge.send({type:"call",simId:this.opts.simId,path:"SootSim.bridges.settings.get",args:[]}),t=e&&typeof e.deviceModel=="string"?e.deviceModel:null;return!t||!(t in ye)?null:t}async captureFailureScreenshot(e){v.mkdirSync(g.dirname(e),{recursive:!0});let n=(await this.bridge.send({type:"screenshot",simId:this.opts.simId})).replace(/^data:image\/png;base64,/,"");v.writeFileSync(e,Buffer.from(n,"base64"))}async captureFailureBundle(e,t){v.mkdirSync(e,{recursive:!0});let n=(c,h)=>{try{v.writeFileSync(g.join(e,c),h)}catch{}},r=async c=>{try{return await this.bridge.send({type:"evaluate",simId:this.opts.simId,code:c})}catch{return null}},o=await r(`(async () => {
211
+ })()`);if(!t?.visible&&!t?.focused){await p(80);return}await p(80)}}async eraseText(e){for(let t=0;t<e;t++)await this.pressKey("Backspace");await p(100)}async waitFor(e){let t=Date.now()+(e.timeout||W);for(;Date.now()<t;){if(await this.findElement(e))return;await p(200)}let r=await this.findElement(e),n=r?` (matched node y=${Math.round(r.absolutePosition.y)} \u2014 still off-screen at deadline)`:"";throw new Error(`waitFor: ${JSON.stringify(e)} not found after ${e.timeout||W}ms${n}`)}async takeScreenshot(e){if(!this.hasFreshVisualSettle())try{await this.waitForVisualSettle(),await p(250)}catch{}let t=dt(e),r=ct(this.opts.screenshotDir,t.path,{mode:this.opts.screenshotPathMode??"dir",flowDir:this.opts.flowDir});v.mkdirSync(g.dirname(r),{recursive:!0});let n=t.layers??this.opts.screenshotLayers,s={type:"screenshot",simId:this.opts.simId};n&&n!=="full"&&(s.layers=n);let a=(await this.bridge.send(s)).replace(/^data:image\/png;base64,/,""),l=Buffer.from(a,"base64");if(t.withFrame){let f=await this.readCurrentDeviceModel();if(!f)throw new Error("could not read current device model for framed screenshot");let c=await Be(l,f);v.writeFileSync(r,c),console.log(`[flow] screenshot: ${r} (frame: ${f})`);return}v.writeFileSync(r,l),console.log(`[flow] screenshot: ${r}`)}async readCurrentDeviceModel(){let e=await this.bridge.send({type:"call",simId:this.opts.simId,path:"SootSim.bridges.settings.get",args:[]}),t=e&&typeof e.deviceModel=="string"?e.deviceModel:null;return!t||!(t in be)?null:t}async captureFailureScreenshot(e){v.mkdirSync(g.dirname(e),{recursive:!0});let r=(await this.bridge.send({type:"screenshot",simId:this.opts.simId})).replace(/^data:image\/png;base64,/,"");v.writeFileSync(e,Buffer.from(r,"base64"))}async captureFailureBundle(e,t){v.mkdirSync(e,{recursive:!0});let r=(c,h)=>{try{v.writeFileSync(g.join(e,c),h)}catch{}},n=async c=>{try{return await this.bridge.send({type:"evaluate",simId:this.opts.simId,code:c})}catch{return null}},s=await n(`(async () => {
212
212
  const out = {
213
213
  simId: window.__sootsimBridge?.id ?? window.SootSim?.state?.simId ?? null,
214
214
  url: location.href,
@@ -232,7 +232,7 @@ process.stdin.on('end', async () => {
232
232
  }
233
233
  } catch {}
234
234
  return out
235
- })()`);n("error.json",JSON.stringify({message:t.error.message,stack:t.error.stack,stepIndex:t.stepIndex,stepKind:t.stepKind,stepTarget:t.stepTarget,capturedAt:new Date().toISOString(),sim:o??null},null,2));try{let h=(await this.bridge.send({type:"screenshot",simId:this.opts.simId})).replace(/^data:image\/png;base64,/,"");n("screenshot.png",Buffer.from(h,"base64"))}catch{}let s=await r(`(async () => {
235
+ })()`);r("error.json",JSON.stringify({message:t.error.message,stack:t.error.stack,stepIndex:t.stepIndex,stepKind:t.stepKind,stepTarget:t.stepTarget,capturedAt:new Date().toISOString(),sim:s??null},null,2));try{let h=(await this.bridge.send({type:"screenshot",simId:this.opts.simId})).replace(/^data:image\/png;base64,/,"");r("screenshot.png",Buffer.from(h,"base64"))}catch{}let o=await n(`(async () => {
236
236
  const t = window.__sootsimTest
237
237
  const mainShell = window.SootSim?.bridges?.mainShell
238
238
  if (!t) return { error: 'no test bridge' }
@@ -242,19 +242,19 @@ process.stdin.on('end', async () => {
242
242
  } catch {}
243
243
  const all = await t.queryAll({ pruneHidden: true })
244
244
  return { shell, nodes: all, url: location.href, title: document.title }
245
- })()`);s&&n("describe.json",JSON.stringify(s,null,2));let a=await r("(async () => await window.__sootsimTest?.dumpAccessibilityTree?.(20))()");typeof a=="string"&&n("a11y.txt",a);let l=await r("(async () => await window.__sootsimTest?.dumpTree?.(15))()");typeof l=="string"&&n("tree.txt",l);let f=await r(`(() => {
245
+ })()`);o&&r("describe.json",JSON.stringify(o,null,2));let a=await n("(async () => await window.__sootsimTest?.dumpAccessibilityTree?.(20))()");typeof a=="string"&&r("a11y.txt",a);let l=await n("(async () => await window.__sootsimTest?.dumpTree?.(15))()");typeof l=="string"&&r("tree.txt",l);let f=await n(`(() => {
246
246
  const c = window.__sootsimConsole
247
247
  return {
248
248
  errors: c?.getErrors?.() ?? [],
249
249
  warnings: c?.getWarnings?.() ?? [],
250
250
  requests: (window.__sootsimGetFailedRequests?.() ?? []),
251
251
  }
252
- })()`);return f&&n("console.json",JSON.stringify(f,null,2)),e}async swipe(e="UP",t=300){let n=H/2,r=O/2,o=200,s=n,a=r,l=n,f=r;switch(e.toUpperCase()){case"UP":a+=o,f-=o;break;case"DOWN":a-=o,f+=o;break;case"LEFT":s+=o,l-=o;break;case"RIGHT":s-=o,l+=o;break}let c=Math.max(10,Math.round(t/16));await this.drag(s,a,l,f,c,16)}async drag(e,t,n,r,o=12,s=16){if(!(await this.evaluate(`(async () => {
252
+ })()`);return f&&r("console.json",JSON.stringify(f,null,2)),e}async swipe(e="UP",t=300){let r=H/2,n=O/2,s=200,o=r,a=n,l=r,f=n;switch(e.toUpperCase()){case"UP":a+=s,f-=s;break;case"DOWN":a-=s,f+=s;break;case"LEFT":o+=s,l-=s;break;case"RIGHT":o-=s,l+=s;break}let c=Math.max(10,Math.round(t/16));await this.drag(o,a,l,f,c,16)}async drag(e,t,r,n,s=12,o=16){if(!(await this.evaluate(`(async () => {
253
253
  const interact = window.__sootsimInteract
254
254
  if (!interact?.drag) return { ok: false }
255
- const value = await interact.drag(${e}, ${t}, ${n}, ${r}, ${Math.max(1,Math.round(o))}, ${Math.max(0,Math.round(s))})
255
+ const value = await interact.drag(${e}, ${t}, ${r}, ${n}, ${Math.max(1,Math.round(s))}, ${Math.max(0,Math.round(o))})
256
256
  return { ok: !!value, value }
257
- })()`))?.ok)throw new Error("drag failed");await p(300)}async swipeCoords(e,t,n=300){let r=se(e),o=se(t),s=Math.max(10,Math.round(n/16));await this.drag(r.x,r.y,o.x,o.y,s,16)}async swipeFrom(e,t="UP",n=300){let r=await this.findElement(e);if(!r)throw new Error(`swipeFrom: element not found: ${JSON.stringify(e)}`);let o=r.absolutePosition.x+r.layout.width/2,s=r.absolutePosition.y+r.layout.height/2,a=180,l=o,f=s;switch(t.toUpperCase()){case"UP":f-=a;break;case"DOWN":f+=a;break;case"LEFT":l-=a;break;case"RIGHT":l+=a;break}let c=Math.max(10,Math.round(n/16));await this.drag(o,s,l,f,c,16)}async scrollTo(e,t,n){let r=await this.callTest("scrollTo",e,t,n,!1);if(!r?.ok)throw new Error(`scrollTo failed: ${r?.reason||"unknown error"}`);await p(250)}async pinch(e){let t=await this.evaluate(`(async () => {
257
+ })()`))?.ok)throw new Error("drag failed");await p(300)}async swipeCoords(e,t,r=300){let n=ae(e),s=ae(t),o=Math.max(10,Math.round(r/16));await this.drag(n.x,n.y,s.x,s.y,o,16)}async swipeFrom(e,t="UP",r=300){let n=await this.findElement(e);if(!n)throw new Error(`swipeFrom: element not found: ${JSON.stringify(e)}`);let s=n.absolutePosition.x+n.layout.width/2,o=n.absolutePosition.y+n.layout.height/2,a=180,l=s,f=o;switch(t.toUpperCase()){case"UP":f-=a;break;case"DOWN":f+=a;break;case"LEFT":l-=a;break;case"RIGHT":l+=a;break}let c=Math.max(10,Math.round(r/16));await this.drag(s,o,l,f,c,16)}async scrollTo(e,t,r){let n=await this.callTest("scrollTo",e,t,r,!1);if(!n?.ok)throw new Error(`scrollTo failed: ${n?.reason||"unknown error"}`);await p(250)}async pinch(e){let t=await this.evaluate(`(async () => {
258
258
  const interact = window.__sootsimInteract
259
259
  if (!interact?.pinch) return { ok: false, reason: 'no interact.pinch' }
260
260
  const ok = await interact.pinch(
@@ -263,7 +263,7 @@ process.stdin.on('end', async () => {
263
263
  ${e.steps||12}, ${e.stepMs||16}
264
264
  )
265
265
  return { ok: !!ok, reason: ok ? null : 'pinch returned false' }
266
- })()`);if(!t?.ok)throw new Error(`pinch failed: ${t?.reason||"unknown error"}`);await p(250)}async dumpTree(e=6){let t=await this.bridge.send({type:"tree",simId:this.opts.simId,depth:e});return console.log("[flow] tree:"),console.log(typeof t=="string"?t:JSON.stringify(t,null,2)),typeof t=="string"?t:JSON.stringify(t)}async assertTreeContains(e){if(!(await this.dumpTree(8)).includes(e))throw new Error(`assertTreeContains: "${e}" not in tree`)}async waitForAnimationToEnd(e=2e3){await p(e)}async back(){try{await this.tapOn("\u2039")}catch{await this.tapOn("<")}}async scrollUntilVisible(e){let t=Date.now()+(e.timeout||15e3),n=e.direction?.toUpperCase()||"DOWN",r=n==="DOWN"?"UP":n==="UP"?"DOWN":n;for(;Date.now()<t;){let o=await this.findElement({text:e.element});if(o&&o.absolutePosition.y>=0&&o.absolutePosition.y+o.layout.height>0&&o.absolutePosition.y<O-50){if(e.centerElement){let s=O/2,a=o.absolutePosition.y-s;if(Math.abs(a)>100){let l=Math.min(150,Math.abs(a)*.5),f=a>0?s-l:s+l;await this.drag(H/2,s,H/2,f,10,16)}}return}await this.swipe(r,250),await p(500)}throw new Error(`scrollUntilVisible: "${e.element}" not found`)}async extendedWaitUntil(e){let t=Date.now()+(e.timeout||W);for(;Date.now()<t;){let n=e.visible?await this.isElementVisible(typeof e.visible=="string"?{text:e.visible}:e.visible):!0,r=e.notVisible?!await this.isElementVisible(typeof e.notVisible=="string"?{text:e.notVisible}:e.notVisible):!0;if(n&&r)return;await p(200)}throw new Error("extendedWaitUntil timed out")}async reloadGuestApp(e){if(e){let r=!1;for(let o=0;o<3&&!r;o++)try{r=await this.evaluate(ve(!0),2e4)}catch{await p(1e3)}r||console.warn(" warn: tenant storage clear did not ack \u2014 hard-reloading anyway (worker reset follows)")}await this.evaluate("window.location.reload()").catch(()=>{}),await p(500),await this.refreshSimId()}async launchApp(e){await this.refreshSimId(),e&&typeof e=="object"&&e.arguments&&console.log("[flow] launchApp.arguments ignored (sootsim has no native process)");let t=!!(e&&typeof e=="object"&&e.clearState);if(!this.firstLaunchDone&&!t)this.firstLaunchDone=!0,await this.waitForTree(3e4);else if(this.firstLaunchDone=!0,await this.reloadGuestApp(t),await this.waitForTree(12e4),this.opts.onAfterLaunch)try{await this.opts.onAfterLaunch()}catch(n){console.warn(` warn: onAfterLaunch hook threw: ${n instanceof Error?n.message:n}`)}this.profilingEnabled&&await this.startProfile(),this.recordingEnabled&&(await this.waitForVisualSettle(),await this.ensureRecordingStarted())}async waitForVisualSettle(){try{await this.evaluate(`new Promise((resolve) => {
266
+ })()`);if(!t?.ok)throw new Error(`pinch failed: ${t?.reason||"unknown error"}`);await p(250)}async dumpTree(e=6){let t=await this.bridge.send({type:"tree",simId:this.opts.simId,depth:e});return console.log("[flow] tree:"),console.log(typeof t=="string"?t:JSON.stringify(t,null,2)),typeof t=="string"?t:JSON.stringify(t)}async assertTreeContains(e){if(!(await this.dumpTree(8)).includes(e))throw new Error(`assertTreeContains: "${e}" not in tree`)}async waitForAnimationToEnd(e=2e3){let t=Math.max(0,Math.round(e));if(t<=0)return;(await oe({bridge:this.bridge,simId:this.opts.simId,maxMs:t,pollMs:32,stablePolls:2,strict:!0})).settled&&this.markVisualSettled()}async back(){try{await this.tapOn("\u2039")}catch{await this.tapOn("<")}}async scrollUntilVisible(e){let t=Date.now()+(e.timeout||15e3),r=e.direction?.toUpperCase()||"DOWN",n=r==="DOWN"?"UP":r==="UP"?"DOWN":r;for(;Date.now()<t;){let s=await this.findElement({text:e.element});if(s&&s.absolutePosition.y>=0&&s.absolutePosition.y+s.layout.height>0&&s.absolutePosition.y<O-50){if(e.centerElement){let o=O/2,a=s.absolutePosition.y-o;if(Math.abs(a)>100){let l=Math.min(150,Math.abs(a)*.5),f=a>0?o-l:o+l;await this.drag(H/2,o,H/2,f,10,16)}}return}await this.swipe(n,250),await p(500)}throw new Error(`scrollUntilVisible: "${e.element}" not found`)}async extendedWaitUntil(e){let t=Date.now()+(e.timeout||W);for(;Date.now()<t;){let r=e.visible?await this.isElementVisible(typeof e.visible=="string"?{text:e.visible}:e.visible):!0,n=e.notVisible?!await this.isElementVisible(typeof e.notVisible=="string"?{text:e.notVisible}:e.notVisible):!0;if(r&&n)return;await p(200)}throw new Error("extendedWaitUntil timed out")}async reloadGuestApp(e){if(e){let n=!1;for(let s=0;s<3&&!n;s++)try{n=await this.evaluate(Se(!0),2e4)}catch{await p(1e3)}n||console.warn(" warn: tenant storage clear did not ack \u2014 hard-reloading anyway (worker reset follows)")}await this.evaluate("window.location.reload()").catch(()=>{}),await p(500),await this.refreshSimId()}async launchApp(e){await this.refreshSimId(),e&&typeof e=="object"&&e.arguments&&console.log("[flow] launchApp.arguments ignored (sootsim has no native process)");let t=!!(e&&typeof e=="object"&&e.clearState);if(!this.firstLaunchDone&&!t)this.firstLaunchDone=!0,await this.waitForTree(3e4);else if(this.firstLaunchDone=!0,await this.reloadGuestApp(t),await this.waitForTree(12e4),this.opts.onAfterLaunch)try{await this.opts.onAfterLaunch()}catch(r){console.warn(` warn: onAfterLaunch hook threw: ${r instanceof Error?r.message:r}`)}this.profilingEnabled&&await this.startProfile(),this.recordingEnabled&&(await this.waitForVisualSettle(),await this.ensureRecordingStarted())}async waitForVisualSettle(){try{await this.evaluate(`new Promise((resolve) => {
267
267
  requestAnimationFrame(() => {
268
268
  requestAnimationFrame(() => {
269
269
  requestAnimationFrame(() => {
@@ -271,7 +271,7 @@ process.stdin.on('end', async () => {
271
271
  })
272
272
  })
273
273
  })
274
- })`)}catch{await p(180)}}async startProfile(){await this.evaluate(`((sessionKey) => {
274
+ })`),this.markVisualSettled()}catch{await p(180),this.markVisualSettled()}}markVisualSettled(){this.lastVisualSettledAtMs=Date.now()}hasFreshVisualSettle(){return Date.now()-this.lastVisualSettledAtMs<750}async startProfile(){await this.evaluate(`((sessionKey) => {
275
275
  const perf = window.__sootsimPerf
276
276
  const a11y = window.__sootsimA11y
277
277
  perf?.enableFrameStats?.(true)
@@ -361,7 +361,7 @@ process.stdin.on('end', async () => {
361
361
  sampleCount: recent.length,
362
362
  worstFrames: recent.slice().sort((a, b) => b - a).slice(0, 10),
363
363
  }
364
- })(${JSON.stringify(Ue)})`);if(this.profilingEnabled=!1,e?.error)throw new Error(e.error);return e}async stopApp(){await this.reloadGuestApp(!0)}async clearState(){await this.reloadGuestApp(!0)}clearKeychain(){console.warn("[flow] clearKeychain: sootsim has no keychain surface \u2014 no-op (warning)")}async copyTextFrom(e){let t=typeof e=="string"?{id:e}:e,n=await this.findElement(t);if(!n)throw new Error(`copyTextFrom: element not found: ${JSON.stringify(t)}`);let r=n.text??n.testID??"";this.js.setCopiedText(r),this.js.putEnv("maestroCopiedText",r),console.log(`[flow] copied text: ${JSON.stringify(r)}`)}evalScript(e){let n=/(?<!\\)\$\{[^$]*\}/.test(e)?this.js.evaluateStringTemplate(e):this.js.evaluate(e);n!==void 0&&n!==""&&console.log("[flow] evalScript ->",n)}runScript(e){let t=typeof e=="string"?{file:e}:e;if(!t.file)throw new Error("runScript requires a file");let n=g.resolve(this.opts.flowDir,t.file),r=v.readFileSync(n,"utf8");this.js.evaluate(r,{env:t.env}),console.log(`[flow] runScript: ${t.file} done`)}async openLink(e){let t=typeof e=="string"?e:e.link;if(!t)throw new Error("openLink: missing link");let n=await this.callTest("openDeepLink",t);if(!n?.ok)throw new Error(`openLink failed: ${n?.error||"no test bridge"}`);await p(300)}async evaluateWhen(e){if(e.visible!==void 0){let t=typeof e.visible=="string"?{text:e.visible}:e.visible;return!!await this.findElement(t)}if(e.notVisible!==void 0){let t=typeof e.notVisible=="string"?{text:e.notVisible}:e.notVisible;return!await this.findElement(t)}return e.platform!==void 0?e.platform.toLowerCase()===this.js.maestro.platform.toLowerCase():e.true!==void 0?Je(e.true):!0}isOptional(e){if(e.optional===!0)return!0;let n=Object.entries(e).find(([o])=>o!=="when"&&o!=="optional"),r=n?n[1]:void 0;return!!(r&&typeof r=="object"&&"optional"in r&&r.optional)}async runStep(e,t){let n=typeof e=="string"?ut(e):e,r=Object.keys(n).find(s=>s!=="when")||Object.keys(n)[0];console.log(`[flow] step ${t+1}: ${r}`);let o;try{o=this.js.interpolateStep(n)}catch(s){if(this.isOptional(n)){console.log(`[flow] (optional, skipped: ${s.message.slice(0,80)})`);return}throw this.lastFailedStep={index:t,kind:r,target:n[r]},s}if(o.when&&!await this.evaluateWhen(o.when)){console.log("[flow] (when: predicate false, skipped)");return}try{let s=!!(o.extendedWaitUntil||o.waitFor||o.scrollUntilVisible||o.launchApp||o.runFlow||o.runScript),a=1e4,l=this.runStepInner(o);if(s)await l;else{let c=null,h=new Promise((P,w)=>{c=setTimeout(()=>{w(new Error(`step watchdog: ${r} made no progress in ${a}ms \u2014 bridge or bundle probably hung`))},a)});try{await Promise.race([l,h])}finally{c&&clearTimeout(c)}}let f=!!(o.assertVisible||o.assertNotVisible||o.extendedWaitUntil||o.waitFor);this.stepDelay>0&&!f&&await p(this.stepDelay)}catch(s){if(this.isOptional(o)){console.log(`[flow] (optional, skipped: ${s.message.slice(0,80)})`);return}throw this.lastFailedStep={index:t,kind:r,target:o[r]},s}}async runStepInner(e){if(e.tapOn)await this.tapOn(e.tapOn);else if(e.scrollUntilVisible)await this.scrollUntilVisible(e.scrollUntilVisible);else if(e.extendedWaitUntil)await this.extendedWaitUntil(e.extendedWaitUntil);else if(e.assertVisible)await this.assertVisible(e.assertVisible);else if(e.assertNotVisible)await this.assertNotVisible(e.assertNotVisible);else if(e.inputText)await this.inputText(e.inputText);else if(e.pressKey)await this.pressKey(e.pressKey);else if(e.dispatchKey)await this.dispatchKey(e.dispatchKey);else if(e.waitFor)await this.waitFor(e.waitFor);else if(e.takeScreenshot)await this.takeScreenshot(e.takeScreenshot);else if(e.swipe)e.swipe.start&&e.swipe.end?await this.swipeCoords(e.swipe.start,e.swipe.end,e.swipe.duration):e.swipe.from?await this.swipeFrom(e.swipe.from,e.swipe.direction,e.swipe.duration):await this.swipe(e.swipe.direction,e.swipe.duration);else if(e.scroll)await this.swipe(e.scroll.direction==="DOWN"?"UP":"DOWN");else if(e.scrollTo){let t=typeof e.scrollTo.nodeId=="number"?{nodeId:e.scrollTo.nodeId}:e.scrollTo.id;if(!t)throw new Error("scrollTo requires id or nodeId");await this.scrollTo(t,e.scrollTo.x,e.scrollTo.y)}else if(e.pinch)await this.pinch(e.pinch);else if(e.waitForAnimationToEnd)await this.waitForAnimationToEnd(typeof e.waitForAnimationToEnd=="number"?e.waitForAnimationToEnd:2e3);else if(e.back)await this.back();else if(e.hideKeyboard)await this.hideKeyboard();else if(e.launchApp)await this.launchApp(e.launchApp);else if(typeof e.wait=="number")await p(e.wait);else if(e.dumpTree)await this.dumpTree(e.dumpTree);else if(e.tapAtCoords)await this.tapAtCoords(e.tapAtCoords.x,e.tapAtCoords.y);else if(e.doubleTapAtCoords)await this.doubleTapAtCoords(e.doubleTapAtCoords.x,e.doubleTapAtCoords.y,e.doubleTapAtCoords.gapMs);else if(e.assertTreeContains)await this.assertTreeContains(e.assertTreeContains);else if(typeof e.eraseText=="number")await this.eraseText(e.eraseText);else if(e.repeat&&Array.isArray(e.repeat.commands)){let t=e.repeat.times,n=t==null||t===""?Number.MAX_SAFE_INTEGER:Number(t);if(!Number.isFinite(n))throw new Error(`repeat.times is not a number: ${t}`);let r=e.repeat.when??e.repeat.condition,o=async()=>r?this.evaluateWhen(r):!0,s=r?n:Math.min(n,1e3),a=0;for(;a<s&&await o();){for(let l=0;l<e.repeat.commands.length;l++)await this.runStep(e.repeat.commands[l],l);a++}}else if(e.stopApp!==void 0)await this.stopApp();else if(e.clearState!==void 0)await this.clearState();else if(e.clearKeychain!==void 0)this.clearKeychain();else if(e.copyTextFrom!==void 0)await this.copyTextFrom(e.copyTextFrom);else if(typeof e.evalScript=="string")this.evalScript(e.evalScript);else if(e.runScript){let t=e.runScript,n=typeof t=="string"?void 0:t.when;if(n&&!await this.evaluateWhen(n)){console.log("[flow] runScript skipped (when predicate false)");return}this.runScript(t)}else if(e.openLink!==void 0)await this.openLink(e.openLink);else if(e.runFlow){let t=e.runFlow,n=typeof t=="string"?t:t.file,r=typeof t=="string"?void 0:t.commands,o=typeof t=="string"?void 0:t.env,s=typeof t=="string"?void 0:t.when;if(s&&!await this.evaluateWhen(s)){console.log(`[flow] runFlow skipped (when predicate false): ${n??"inline commands"}`);return}if(!n&&!r)throw new Error("runFlow requires either file or commands");this.js.enterEnvScope();try{if(o)for(let[a,l]of Object.entries(o))this.js.putEnv(a,l);if(n){let a=g.resolve(this.opts.flowDir,n),l=v.readFileSync(a,"utf8"),f=this.opts.flowDir;this.opts.flowDir=g.dirname(a);try{await this.runFlow(ft(l))}finally{this.opts.flowDir=f}}else if(r)for(let a=0;a<r.length;a++)await this.runStep(r[a],a)}finally{this.js.leaveEnvScope()}}else throw new Error(`unsupported flow step: ${JSON.stringify(e)}`)}async runFlow(e){let t=[],n=[],r=[];for(let s of e){let a=Object.keys(s);if(a.length===1&&a[0]==="onFlowStart"&&Array.isArray(s.onFlowStart)){t.push(...s.onFlowStart);continue}if(a.length===1&&a[0]==="onFlowComplete"&&Array.isArray(s.onFlowComplete)){n.push(...s.onFlowComplete);continue}r.push(s)}if(t.length>0){console.log(`[flow] onFlowStart (${t.length} steps)`);for(let s=0;s<t.length;s++)await this.runStep(t[s],s)}let o=null;try{await this.runFlowBody(r)}catch(s){o=s instanceof Error?s:new Error(String(s))}if(n.length>0){console.log(`[flow] onFlowComplete (${n.length} steps)`);for(let s=0;s<n.length;s++)try{await this.runStep(n[s],s)}catch(a){console.warn(`[flow] onFlowComplete step ${s+1} failed: ${a instanceof Error?a.message:a}`)}}if(o)throw o}async runFlowBody(e){for(let t=0;t<e.length;t++){let n=e[t],r=typeof n.wait=="number"?n.wait:0,o=typeof n.waitFor?.timeout=="number"?n.waitFor.timeout:0,s=Math.max(6e4,r+15e3,o+15e3);if(n.runFlow||n.launchApp){await this.runStep(n,t);continue}await Promise.race([this.runStep(n,t),new Promise((a,l)=>setTimeout(()=>l(new Error(`step ${t+1} (${Object.keys(n)[0]}) exceeded ${s}ms deadline`)),s))])}}};async function mt(i){if(!i)return"";let e=i.trim();if(!e)return"";if(/^\d+$/.test(e)||e.startsWith("http://")||e.startsWith("https://"))return e;let n=(await Ae()).find(r=>r.name.toLowerCase()===e.toLowerCase());return n?String(n.preferredPort):e}async function pt(){let i=await Oe(),e=i.find(n=>n.patched);if(e)return`http://localhost:${e.port}/__soot/`;let t=i[0];return t?`http://localhost:${t.port}/__soot/`:null}async function Ke(i){(i.length===0||i.includes("--help")||i.includes("-h"))&&(console.log(`
364
+ })(${JSON.stringify(Ue)})`);if(this.profilingEnabled=!1,e?.error)throw new Error(e.error);return e}async stopApp(){await this.reloadGuestApp(!0)}async clearState(){await this.reloadGuestApp(!0)}clearKeychain(){console.warn("[flow] clearKeychain: sootsim has no keychain surface \u2014 no-op (warning)")}async copyTextFrom(e){let t=typeof e=="string"?{id:e}:e,r=await this.findElement(t);if(!r)throw new Error(`copyTextFrom: element not found: ${JSON.stringify(t)}`);let n=r.text??r.testID??"";this.js.setCopiedText(n),this.js.putEnv("maestroCopiedText",n),console.log(`[flow] copied text: ${JSON.stringify(n)}`)}evalScript(e){let r=/(?<!\\)\$\{[^$]*\}/.test(e)?this.js.evaluateStringTemplate(e):this.js.evaluate(e);r!==void 0&&r!==""&&console.log("[flow] evalScript ->",r)}runScript(e){let t=typeof e=="string"?{file:e}:e;if(!t.file)throw new Error("runScript requires a file");let r=g.resolve(this.opts.flowDir,t.file),n=v.readFileSync(r,"utf8");this.js.evaluate(n,{env:t.env}),console.log(`[flow] runScript: ${t.file} done`)}async openLink(e){let t=typeof e=="string"?e:e.link;if(!t)throw new Error("openLink: missing link");let r=await this.callTest("openDeepLink",t);if(!r?.ok)throw new Error(`openLink failed: ${r?.error||"no test bridge"}`);await p(300)}async evaluateWhen(e){if(e.visible!==void 0){let t=typeof e.visible=="string"?{text:e.visible}:e.visible;return!!await this.findElement(t)}if(e.notVisible!==void 0){let t=typeof e.notVisible=="string"?{text:e.notVisible}:e.notVisible;return!await this.findElement(t)}return e.platform!==void 0?e.platform.toLowerCase()===this.js.maestro.platform.toLowerCase():e.true!==void 0?Je(e.true):!0}isOptional(e){if(e.optional===!0)return!0;let r=Object.entries(e).find(([s])=>s!=="when"&&s!=="optional"),n=r?r[1]:void 0;return!!(n&&typeof n=="object"&&"optional"in n&&n.optional)}async runStep(e,t){let r=typeof e=="string"?ut(e):e,n=Object.keys(r).find(o=>o!=="when")||Object.keys(r)[0];console.log(`[flow] step ${t+1}: ${n}`);let s;try{s=this.js.interpolateStep(r)}catch(o){if(this.isOptional(r)){console.log(`[flow] (optional, skipped: ${o.message.slice(0,80)})`);return}throw this.lastFailedStep={index:t,kind:n,target:r[n]},o}if(s.when&&!await this.evaluateWhen(s.when)){console.log("[flow] (when: predicate false, skipped)");return}try{let o=!!(s.extendedWaitUntil||s.waitFor||s.scrollUntilVisible||s.launchApp||s.runFlow||s.runScript),a=1e4,l=this.runStepInner(s);if(o)await l;else{let c=null,h=new Promise((M,w)=>{c=setTimeout(()=>{w(new Error(`step watchdog: ${n} made no progress in ${a}ms \u2014 bridge or bundle probably hung`))},a)});try{await Promise.race([l,h])}finally{c&&clearTimeout(c)}}let f=!!(s.assertVisible||s.assertNotVisible||s.extendedWaitUntil||s.waitFor);this.stepDelay>0&&!f&&await p(this.stepDelay)}catch(o){if(this.isOptional(s)){console.log(`[flow] (optional, skipped: ${o.message.slice(0,80)})`);return}throw this.lastFailedStep={index:t,kind:n,target:s[n]},o}}async runStepInner(e){if(e.tapOn)await this.tapOn(e.tapOn);else if(e.scrollUntilVisible)await this.scrollUntilVisible(e.scrollUntilVisible);else if(e.extendedWaitUntil)await this.extendedWaitUntil(e.extendedWaitUntil);else if(e.assertVisible)await this.assertVisible(e.assertVisible);else if(e.assertNotVisible)await this.assertNotVisible(e.assertNotVisible);else if(e.inputText)await this.inputText(e.inputText);else if(e.pressKey)await this.pressKey(e.pressKey);else if(e.dispatchKey)await this.dispatchKey(e.dispatchKey);else if(e.waitFor)await this.waitFor(e.waitFor);else if(e.takeScreenshot)await this.takeScreenshot(e.takeScreenshot);else if(e.swipe)e.swipe.start&&e.swipe.end?await this.swipeCoords(e.swipe.start,e.swipe.end,e.swipe.duration):e.swipe.from?await this.swipeFrom(e.swipe.from,e.swipe.direction,e.swipe.duration):await this.swipe(e.swipe.direction,e.swipe.duration);else if(e.scroll)await this.swipe(e.scroll.direction==="DOWN"?"UP":"DOWN");else if(e.scrollTo){let t=typeof e.scrollTo.nodeId=="number"?{nodeId:e.scrollTo.nodeId}:e.scrollTo.id;if(!t)throw new Error("scrollTo requires id or nodeId");await this.scrollTo(t,e.scrollTo.x,e.scrollTo.y)}else if(e.pinch)await this.pinch(e.pinch);else if(e.waitForAnimationToEnd){let t=e.waitForAnimationToEnd;await this.waitForAnimationToEnd(typeof t=="number"?t:typeof t=="object"&&typeof t.timeout=="number"?t.timeout:2e3)}else if(e.back)await this.back();else if(e.hideKeyboard)await this.hideKeyboard();else if(e.launchApp)await this.launchApp(e.launchApp);else if(typeof e.wait=="number")await p(e.wait);else if(e.dumpTree)await this.dumpTree(e.dumpTree);else if(e.tapAtCoords)await this.tapAtCoords(e.tapAtCoords.x,e.tapAtCoords.y);else if(e.doubleTapAtCoords)await this.doubleTapAtCoords(e.doubleTapAtCoords.x,e.doubleTapAtCoords.y,e.doubleTapAtCoords.gapMs);else if(e.assertTreeContains)await this.assertTreeContains(e.assertTreeContains);else if(typeof e.eraseText=="number")await this.eraseText(e.eraseText);else if(e.repeat&&Array.isArray(e.repeat.commands)){let t=e.repeat.times,r=t==null||t===""?Number.MAX_SAFE_INTEGER:Number(t);if(!Number.isFinite(r))throw new Error(`repeat.times is not a number: ${t}`);let n=e.repeat.when??e.repeat.condition,s=async()=>n?this.evaluateWhen(n):!0,o=n?r:Math.min(r,1e3),a=0;for(;a<o&&await s();){for(let l=0;l<e.repeat.commands.length;l++)await this.runStep(e.repeat.commands[l],l);a++}}else if(e.stopApp!==void 0)await this.stopApp();else if(e.clearState!==void 0)await this.clearState();else if(e.clearKeychain!==void 0)this.clearKeychain();else if(e.copyTextFrom!==void 0)await this.copyTextFrom(e.copyTextFrom);else if(typeof e.evalScript=="string")this.evalScript(e.evalScript);else if(e.runScript){let t=e.runScript,r=typeof t=="string"?void 0:t.when;if(r&&!await this.evaluateWhen(r)){console.log("[flow] runScript skipped (when predicate false)");return}this.runScript(t)}else if(e.openLink!==void 0)await this.openLink(e.openLink);else if(e.runFlow){let t=e.runFlow,r=typeof t=="string"?t:t.file,n=typeof t=="string"?void 0:t.commands,s=typeof t=="string"?void 0:t.env,o=typeof t=="string"?void 0:t.when;if(o&&!await this.evaluateWhen(o)){console.log(`[flow] runFlow skipped (when predicate false): ${r??"inline commands"}`);return}if(!r&&!n)throw new Error("runFlow requires either file or commands");this.js.enterEnvScope();try{if(s)for(let[a,l]of Object.entries(s))this.js.putEnv(a,l);if(r){let a=g.resolve(this.opts.flowDir,r),l=v.readFileSync(a,"utf8"),f=this.opts.flowDir;this.opts.flowDir=g.dirname(a);try{await this.runFlow(ft(l))}finally{this.opts.flowDir=f}}else if(n)for(let a=0;a<n.length;a++)await this.runStep(n[a],a)}finally{this.js.leaveEnvScope()}}else throw new Error(`unsupported flow step: ${JSON.stringify(e)}`)}async runFlow(e){let t=[],r=[],n=[];for(let o of e){let a=Object.keys(o);if(a.length===1&&a[0]==="onFlowStart"&&Array.isArray(o.onFlowStart)){t.push(...o.onFlowStart);continue}if(a.length===1&&a[0]==="onFlowComplete"&&Array.isArray(o.onFlowComplete)){r.push(...o.onFlowComplete);continue}n.push(o)}if(t.length>0){console.log(`[flow] onFlowStart (${t.length} steps)`);for(let o=0;o<t.length;o++)await this.runStep(t[o],o)}let s=null;try{await this.runFlowBody(n)}catch(o){s=o instanceof Error?o:new Error(String(o))}if(r.length>0){console.log(`[flow] onFlowComplete (${r.length} steps)`);for(let o=0;o<r.length;o++)try{await this.runStep(r[o],o)}catch(a){console.warn(`[flow] onFlowComplete step ${o+1} failed: ${a instanceof Error?a.message:a}`)}}if(s)throw s}async runFlowBody(e){for(let t=0;t<e.length;t++){let r=e[t],n=typeof r.wait=="number"?r.wait:0,s=typeof r.waitFor?.timeout=="number"?r.waitFor.timeout:0,o=Math.max(6e4,n+15e3,s+15e3);if(r.runFlow||r.launchApp){await this.runStep(r,t);continue}await Promise.race([this.runStep(r,t),new Promise((a,l)=>setTimeout(()=>l(new Error(`step ${t+1} (${Object.keys(r)[0]}) exceeded ${o}ms deadline`)),o))])}}};async function mt(i){if(!i)return"";let e=i.trim();if(!e)return"";if(/^\d+$/.test(e)||e.startsWith("http://")||e.startsWith("https://"))return e;let r=(await Ae()).find(n=>n.name.toLowerCase()===e.toLowerCase());return r?String(r.preferredPort):e}async function pt(){let i=await Oe(),e=i.find(r=>r.patched);if(e)return`http://localhost:${e.port}/__soot/`;let t=i[0];return t?`http://localhost:${t.port}/__soot/`:null}async function Ke(i){(i.length===0||i.includes("--help")||i.includes("-h"))&&(console.log(`
365
365
  sootsim flow \u2014 run flows or manage a draft
366
366
 
367
367
  usage:
@@ -439,25 +439,25 @@ flow extension:
439
439
  takeScreenshot:
440
440
  path: marketing/hero
441
441
  withFrame: true
442
- `),process.exit(0));let e=i[0];_.existsSync(e)||(console.error(` error: ${e} not found`),process.exit(1));let t=m=>i.find((u,y)=>i[y-1]===m),n=i.includes("--profile"),r=i.includes("--headed"),o=i.includes("--new")||r,s=t("--out")||"/tmp/sootsim-recordings",a=t("--screenshots")||(t("--out")?s:"/tmp/sootsim-flow"),l=t("--screenshot-paths")==="flow"?"flow":"dir",f=i.includes("--no-shell")?"tenant":i.includes("--shell-only")?"shell":void 0,c=t("--device")?.trim()||"",h=i.includes("--slow")?+(t("--slow")||"500"):0,P=i.includes("--electron"),w=i.includes("--preview"),R=await je(t("--preview-origin")),ae=t("--preview-public-origin"),qe=i.includes("--preview-open"),le=t("--owner")?.trim()||"",ce=t("--repo")?.trim()||"",Ge=i.flatMap((m,u)=>i[u-1]==="--replace"?["--replace",m]:[]),ze=i.flatMap((m,u)=>i[u-1]==="--remap"?["--remap",m]:[]),$=i.includes("--record")||w,Ye=i.includes("--tail-wait")?Math.max(0,+(t("--tail-wait")||"2000")):$?w?6e3:2e3:0,Xe=_.readFileSync(e,"utf8"),{frontmatter:x,steps:E}=ne(Xe);E.length===0&&(console.error(" error: flow file must contain a YAML array of steps"),process.exit(1));let N=c||x.device||"",B=P||x.electron===!0,Ze=x.app===void 0||x.app===null?"":typeof x.app=="number"?String(x.app):x.app,q=await mt(Ze),de={stripBooleanFlags:["--record","--profile","--electron","--new","--preview","--preview-open","--headless","--headed"],stripValueFlags:["--out","--device","--slow","--tail-wait","--url","--replace","--remap","--screenshots","--screenshot-paths","--preview-origin","--preview-public-origin","--driver"]},S=ee(i,de),Qe=t("--url")||"";B&&!_e()&&(console.error(" error: desktop companion not found. install or build it first with `bun run build:electron`"),process.exit(1));let ue=B?"desktop companion":"bridge",et=q?` | target: ${q}`:"";console.log(`
442
+ `),process.exit(0));let e=i[0];_.existsSync(e)||(console.error(` error: ${e} not found`),process.exit(1));let t=m=>i.find((u,y)=>i[y-1]===m),r=i.includes("--profile"),n=i.includes("--headed"),s=i.includes("--new")||n,o=t("--out")||"/tmp/sootsim-recordings",a=t("--screenshots")||(t("--out")?o:"/tmp/sootsim-flow"),l=t("--screenshot-paths")==="flow"?"flow":"dir",f=i.includes("--no-shell")?"tenant":i.includes("--shell-only")?"shell":void 0,c=t("--device")?.trim()||"",h=i.includes("--slow")?+(t("--slow")||"500"):0,M=i.includes("--electron"),w=i.includes("--preview"),R=await je(t("--preview-origin")),le=t("--preview-public-origin"),qe=i.includes("--preview-open"),ce=t("--owner")?.trim()||"",de=t("--repo")?.trim()||"",Ge=i.flatMap((m,u)=>i[u-1]==="--replace"?["--replace",m]:[]),ze=i.flatMap((m,u)=>i[u-1]==="--remap"?["--remap",m]:[]),$=i.includes("--record")||w,Ye=i.includes("--tail-wait")?Math.max(0,+(t("--tail-wait")||"2000")):$?w?6e3:2e3:0,Xe=_.readFileSync(e,"utf8"),{frontmatter:x,steps:E}=re(Xe);E.length===0&&(console.error(" error: flow file must contain a YAML array of steps"),process.exit(1));let N=c||x.device||"",B=M||x.electron===!0,Ze=x.app===void 0||x.app===null?"":typeof x.app=="number"?String(x.app):x.app,q=await mt(Ze),ue={stripBooleanFlags:["--record","--profile","--electron","--new","--preview","--preview-open","--headless","--headed"],stripValueFlags:["--out","--device","--slow","--tail-wait","--url","--replace","--remap","--screenshots","--screenshot-paths","--preview-origin","--preview-public-origin","--driver"]},S=ee(i,ue),Qe=t("--url")||"";B&&!_e()&&(console.error(" error: desktop companion not found. install or build it first with `bun run build:electron`"),process.exit(1));let fe=B?"desktop companion":"bridge",et=q?` | target: ${q}`:"";console.log(`
443
443
  sootsim flow \u2014 ${b.basename(e)}
444
- ${E.length} steps | ${ue}${et}${$?" | recording":""}${n?" | profiling":""}${h?` | ${h}ms delay`:""}
445
- `),w&&ge({event:"preview_flow_started",properties:{flowName:b.basename(e),stepCount:E.length,mode:ue,electron:B,record:$,profile:n,previewOrigin:R}});let F=Qe||q||"",G=o||B&&S.simIdSource!=="flag",fe=t("--driver")||"",z=!1,Y=new Set,me=!1;if(!F&&!G&&S.simIdSource==="none"){let m=Fe(S);try{let u=await m.listSims(),y=u.find(J=>J.isPrimary&&J.readyState==="open"),V=u.find(J=>J.readyState==="open"),we=y??V;we&&(me=!0,console.log(` reusing active sim: ${we.id}`))}catch{}finally{m.close()}}if(!F&&!me&&(G||S.simIdSource==="none")){let m=await pt();m||(console.error(" error: no current sim and no sootsim target found"),await Z(),process.exit(1)),F=m}if(F&&N){let m=new URL(await Re(F));m.searchParams.set("device",N),F=m.toString()}if(F){let m=[F];if(m.push(...Ge),m.push(...ze),G&&m.push("--new"),S.simIdSource==="flag"&&S.simId&&m.push("--sim",S.simId),fe&&(m.push("--driver",fe),z=!0),i.includes("--headless")&&!r&&m.push("--headless"),await Ce(m,{port:S.wsPort,timeoutMs:S.commandTimeoutMs}),z){let u=Q();u&&Y.add(u)}}let M=ee(i,de),j=M.simIdSource==="flag"?M.simId:Q()||M.simId,tt=M.simIdSource==="flag"?"flag":j?"saved":"none",k=te(M.wsPort,{commandTimeoutMs:M.commandTimeoutMs,simId:j,simIdSource:tt}),d,pe=async()=>{if(!N)return!1;let m=await k.send({type:"evaluate",simId:d.simId,code:"window.location.href"});if(typeof m=="string"&&m.length>0){let u=new URL(m);u.searchParams.set("device",N),d.setSimRouteHint(u.toString())}return!!await k.send({type:"call",simId:d.simId,path:"SootSim.bridges.settings.set",args:["deviceModel",N]})};if(d=new K(k,{screenshotDir:a,flowDir:b.dirname(b.resolve(e)),screenshotPathMode:l,screenshotLayers:f,simId:j,recordingOutputDir:$?s:void 0,recordingFormat:w?"mp4":"webm",billingOriginOverride:w?R:void 0,allowGitHubRecording:$,onAfterLaunch:async()=>{await pe()&&await d.waitForTree(12e4),w&&await k.send({type:"evaluate",simId:d.simId,code:`(() => {
444
+ ${E.length} steps | ${fe}${et}${$?" | recording":""}${r?" | profiling":""}${h?` | ${h}ms delay`:""}
445
+ `),w&&ye({event:"preview_flow_started",properties:{flowName:b.basename(e),stepCount:E.length,mode:fe,electron:B,record:$,profile:r,previewOrigin:R}});let k=Qe||q||"",G=s||B&&S.simIdSource!=="flag",me=t("--driver")||"",z=!1,Y=new Set,pe=!1;if(!k&&!G&&S.simIdSource==="none"){let m=Fe(S);try{let u=await m.listSims(),y=u.find(J=>J.isPrimary&&J.readyState==="open"),L=u.find(J=>J.readyState==="open"),ge=y??L;ge&&(pe=!0,console.log(` reusing active sim: ${ge.id}`))}catch{}finally{m.close()}}if(!k&&!pe&&(G||S.simIdSource==="none")){let m=await pt();m||(console.error(" error: no current sim and no sootsim target found"),await Z(),process.exit(1)),k=m}if(k&&N){let m=new URL(await Re(k));m.searchParams.set("device",N),k=m.toString()}if(k){let m=[k];if(m.push(...Ge),m.push(...ze),G&&m.push("--new"),m.push("--no-describe"),S.simIdSource==="flag"&&S.simId&&m.push("--sim",S.simId),me&&(m.push("--driver",me),z=!0),i.includes("--headless")&&!n&&m.push("--headless"),await Ce(m,{port:S.wsPort,timeoutMs:S.commandTimeoutMs}),z){let u=Q();u&&Y.add(u)}}let P=ee(i,ue),j=P.simIdSource==="flag"?P.simId:Q()||P.simId,tt=P.simIdSource==="flag"?"flag":j?"saved":"none",F=te(P.wsPort,{commandTimeoutMs:P.commandTimeoutMs,simId:j,simIdSource:tt}),d,he=async()=>{if(!N)return!1;let m=await F.send({type:"evaluate",simId:d.simId,code:"window.location.href"});if(typeof m=="string"&&m.length>0){let u=new URL(m);u.searchParams.set("device",N),d.setSimRouteHint(u.toString())}return!!await F.send({type:"call",simId:d.simId,path:"SootSim.bridges.settings.set",args:["deviceModel",N]})};if(d=new K(F,{screenshotDir:a,flowDir:b.dirname(b.resolve(e)),screenshotPathMode:l,screenshotLayers:f,simId:j,recordingOutputDir:$?o:void 0,recordingFormat:w?"mp4":"webm",billingOriginOverride:w?R:void 0,allowGitHubRecording:$,onAfterLaunch:async()=>{await he()&&await d.waitForTree(12e4),w&&await F.send({type:"evaluate",simId:d.simId,code:`(() => {
446
446
  const r = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
447
447
  return r?.start?.() ?? false
448
- })()`})}}),h&&(d.stepDelay=h),d.js.putEnv("MAESTRO_FILENAME",b.basename(e,b.extname(e))),x.env&&typeof x.env=="object")for(let[m,u]of Object.entries(x.env))d.js.putEnv(m,u);let I=null,T=null,C=null,X=null,L=0,he=!!E[0]?.launchApp;try{await d.waitForTree(12e4),await pe()&&await d.waitForTree(12e4),w&&!he&&!process.env.SOOTSIM_PREVIEW_RESET_DONE&&await d.launchApp({clearState:!0}),$&&(he?d.prepareRecording():await d.startRecording()),n&&await d.startProfile(),w&&(await k.send({type:"evaluate",simId:d.simId,code:`(() => {
448
+ })()`})}}),h&&(d.stepDelay=h),d.js.putEnv("MAESTRO_FILENAME",b.basename(e,b.extname(e))),x.env&&typeof x.env=="object")for(let[m,u]of Object.entries(x.env))d.js.putEnv(m,u);let I=null,T=null,C=null,X=null,V=0,we=!!E[0]?.launchApp;try{await d.waitForTree(12e4),await he()&&await d.waitForTree(12e4),w&&!we&&!process.env.SOOTSIM_PREVIEW_RESET_DONE&&await d.launchApp({clearState:!0}),$&&(we?d.prepareRecording():await d.startRecording()),r&&await d.startProfile(),w&&(await F.send({type:"evaluate",simId:d.simId,code:`(() => {
449
449
  const r = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
450
450
  return r?.start?.() ?? false
451
- })()`})||console.warn(" warn: eventRecorder.start() returned false \u2014 preview will upload bundle without replay events")),await d.runFlow(E),n&&(I=await d.stopProfile()),await d.waitForRecordingTail({maxMs:Ye,smart:w}),$&&(T=await d.stopRecording(),C=d.getLastRecordingDurationMs()),w&&(X=await k.send({type:"evaluate",simId:d.simId,code:`(() => {
451
+ })()`})||console.warn(" warn: eventRecorder.start() returned false \u2014 preview will upload bundle without replay events")),await d.runFlow(E),r&&(I=await d.stopProfile()),await d.waitForRecordingTail({maxMs:Ye,smart:w}),$&&(T=await d.stopRecording(),C=d.getLastRecordingDurationMs()),w&&(X=await F.send({type:"evaluate",simId:d.simId,code:`(() => {
452
452
  const r = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
453
453
  return r?.stop?.() ?? []
454
454
  })()`}),console.log(` captured ${X.length} replay events`));let u=E.filter(y=>y&&typeof y=="object"&&"takeScreenshot"in y).length;console.log(`
455
- + completed (${E.length} steps)`),u>0&&console.log(` screenshots: ${u} \u2192 ${a}`),I&&We(I),T&&console.log(` video: ${T}`),console.log()}catch(m){if(n&&!I)try{I=await d.stopProfile()}catch{}if($&&!T)try{T=await d.stopRecording(),C=d.getLastRecordingDurationMs()}catch(u){console.warn(` recording stop failed: ${u?.message||String(u)}`)}console.error(`
455
+ + completed (${E.length} steps)`),u>0&&console.log(` screenshots: ${u} \u2192 ${a}`),I&&We(I),T&&console.log(` video: ${T}`),console.log()}catch(m){if(r&&!I)try{I=await d.stopProfile()}catch{}if($&&!T)try{T=await d.stopRecording(),C=d.getLastRecordingDurationMs()}catch(u){console.warn(` recording stop failed: ${u?.message||String(u)}`)}console.error(`
456
456
  x failed: ${m.message}
457
- `),I&&(console.log(" partial profile:"),We(I)),T&&console.log(` partial video: ${T}`);try{let u=d.lastFailedStep,y=u?`step-${String(u.index+1).padStart(2,"0")}-${u.kind}`:"unstaged",V=b.join(a,y);await d.captureFailureBundle(V,{error:m,stepIndex:u?.index,stepKind:u?.kind,stepTarget:u?.target}),console.log(` failure bundle: ${V}`),console.log(" (contents: screenshot.png, describe.json, a11y.txt, tree.txt, console.json, error.json)")}catch(u){try{let y=b.join(a,"error.png");await d.captureFailureScreenshot(y),console.log(` error screenshot: ${y}`)}catch{}console.log(` (failure bundle capture failed: ${u instanceof Error?u.message:String(u)})`)}await ke(k,{errorsCommand:`sootsim get errors 5${d.simId?` --sim ${d.simId}`:""}`,warningsCommand:`sootsim get warnings 5${d.simId?` --sim ${d.simId}`:""}`,requestsCommand:`sootsim get requests 5${d.simId?` --sim ${d.simId}`:""}`}),L=1}finally{k.close()}if(L===0&&w)try{let m=await wt(X??[]),u=["--origin",R,"--events",m];ae&&u.push("--public-origin",ae),T&&u.push("--video",T),T&&C&&Number.isFinite(C)&&u.push("--video-duration-ms",String(Math.round(C))),d.simId&&u.push("--sim",d.simId),qe&&u.push("--open"),le&&u.push("--owner",le),ce&&u.push("--repo",ce),console.log(`
458
- preparing preview upload\u2026`);let{runUpload:y}=await import("./upload-XQH6ZVU6.js");await y(u,{})}catch(m){console.error(` preview upload failed: ${m?.message||m}`),L=1}return z?(d.simId&&Y.add(d.simId),await ht(S.wsPort,S.commandTimeoutMs,[...Y])):d.simId&&d.simId!==j&&be(d.simId),await Z(),L}async function ht(i,e,t){let n=[...new Set(t.filter(Boolean))];if(n.length===0)return;let r=te(i,{commandTimeoutMs:e});try{let o=await r.listSims(),s=n.filter(l=>o.some(f=>f.id===l&&f.readyState==="open"));if(s.length===0)return;let a=await De(r,i,e,s);await Ne(o,s),a.closed.length>0&&console.log(` closed flow sim(s): ${a.closed.join(", ")}`),a.remaining.length>0&&console.warn(` warn: flow sim(s) still connected after close: ${a.remaining.join(", ")}`)}catch(o){console.warn(` warn: failed to close flow sim(s): ${o instanceof Error?o.message:String(o)}`)}finally{r.close()}}async function wt(i){let{gzipSync:e}=await import("zlib"),t=i.map(o=>JSON.stringify(o)).join(`
457
+ `),I&&(console.log(" partial profile:"),We(I)),T&&console.log(` partial video: ${T}`);try{let u=d.lastFailedStep,y=u?`step-${String(u.index+1).padStart(2,"0")}-${u.kind}`:"unstaged",L=b.join(a,y);await d.captureFailureBundle(L,{error:m,stepIndex:u?.index,stepKind:u?.kind,stepTarget:u?.target}),console.log(` failure bundle: ${L}`),console.log(" (contents: screenshot.png, describe.json, a11y.txt, tree.txt, console.json, error.json)")}catch(u){try{let y=b.join(a,"error.png");await d.captureFailureScreenshot(y),console.log(` error screenshot: ${y}`)}catch{}console.log(` (failure bundle capture failed: ${u instanceof Error?u.message:String(u)})`)}await Ee(F,{errorsCommand:`sootsim get errors 5${d.simId?` --sim ${d.simId}`:""}`,warningsCommand:`sootsim get warnings 5${d.simId?` --sim ${d.simId}`:""}`,requestsCommand:`sootsim get requests 5${d.simId?` --sim ${d.simId}`:""}`}),V=1}finally{F.close()}if(V===0&&w)try{let m=await wt(X??[]),u=["--origin",R,"--events",m];le&&u.push("--public-origin",le),T&&u.push("--video",T),T&&C&&Number.isFinite(C)&&u.push("--video-duration-ms",String(Math.round(C))),d.simId&&u.push("--sim",d.simId),qe&&u.push("--open"),ce&&u.push("--owner",ce),de&&u.push("--repo",de),console.log(`
458
+ preparing preview upload\u2026`);let{runUpload:y}=await import("./upload-Q2ZFKOSR.js");await y(u,{})}catch(m){console.error(` preview upload failed: ${m?.message||m}`),V=1}return z?(d.simId&&Y.add(d.simId),await ht(S.wsPort,S.commandTimeoutMs,[...Y])):d.simId&&d.simId!==j&&ve(d.simId),await Z(),V}async function ht(i,e,t){let r=[...new Set(t.filter(Boolean))];if(r.length===0)return;let n=te(i,{commandTimeoutMs:e});try{let s=await n.listSims(),o=r.filter(l=>s.some(f=>f.id===l&&f.readyState==="open"));if(o.length===0)return;let a=await De(n,i,e,o);await Ne(s,o),a.closed.length>0&&console.log(` closed flow sim(s): ${a.closed.join(", ")}`),a.remaining.length>0&&console.warn(` warn: flow sim(s) still connected after close: ${a.remaining.join(", ")}`)}catch(s){console.warn(` warn: failed to close flow sim(s): ${s instanceof Error?s.message:String(s)}`)}finally{n.close()}}async function wt(i){let{gzipSync:e}=await import("zlib"),t=i.map(s=>JSON.stringify(s)).join(`
459
459
  `)+(i.length?`
460
- `:""),n=e(Buffer.from(t,"utf8")),r=b.join(He(),`sootsim-events-${Date.now()}.jsonl.gz`);return _.writeFileSync(r,n),console.log(` events: ${i.length} written to ${r} (${n.length} bytes gz)`),r}async function gt(i){let e=i[0],t=n=>i.find((r,o)=>i[o-1]===n);switch(e){case"start":{let{path:n,state:r}=Ie();console.log(" flow draft started"),console.log(` session: ${n}`),console.log(` steps: ${r.steps.length}`);return}case"keep":case"good":{let n=oe();if(n.active||(console.error(" no active flow draft \u2014 run `sootsim flow start` first"),process.exit(1)),!n.kept){console.log(" no pending action to keep");return}console.log(` kept: ${n.candidate.summary}`),console.log(` steps: ${n.stepCount}`);return}case"end":{let n=t("--output")||(i[1]&&!i[1].startsWith("-")?i[1]:void 0),r=i.includes("--validate")||i.includes("--video"),o=i.includes("--video"),s=oe();s.active&&s.kept&&console.log(` auto-kept trailing action: ${s.candidate.summary}`);let a=Pe(n||(r?bt():void 0));if(a.active||(console.error(" no active flow draft \u2014 run `sootsim flow start` first"),process.exit(1)),!a.valid){console.error(" flow draft is not valid:");for(let l of a.issues)console.error(` - ${l}`);console.error(" draft preserved \u2014 keep at least one real interaction or run `sootsim flow start` to reset"),process.exit(1)}if(r){let l=vt(i,a.outputPath),f=await Ke(l);if(f!==0){console.error(`
461
- validation failed \u2014 draft preserved so you can keep iterating`),process.exitCode=f;return}ie(),console.log(` flow draft validated (${a.stepCount} step${a.stepCount===1?"":"s"})`),a.outputPath&&console.log(` saved: ${a.outputPath}`),o&&console.log(" video: recorded during validation run");return}if(ie(),console.log(` flow draft ended (${a.stepCount} step${a.stepCount===1?"":"s"})`),a.outputPath){console.log(` saved: ${a.outputPath}`),console.log(` next: sootsim flow ${a.outputPath} --record`);return}console.log(""),process.stdout.write(a.yaml);return}case"validate":{let n=i[1];(!n||n.startsWith("-"))&&(console.error(" usage: sootsim flow validate <path>"),process.exit(1));let r=re(n);if(r.length>0){console.error(` x ${n} failed validation:`);for(let o of r)console.error(` - ${o}`);process.exit(1)}console.log(` + ${n} looks valid`);return}}}function yt(i){return i[0]==="--sim"&&i.length>=2?[...i.slice(2),"--sim",i[1]]:i}async function Ht(i){let e=yt(i),t=e[0];if(t==="start"||t==="keep"||t==="good"||t==="end"||t==="validate")return await gt(e),0;let n=await Ke(e);return n!==0&&(process.exitCode=n),n}function We(i){let e=i.avgMs>0?(1e3/i.avgMs).toFixed(1):"?",t=i.sampleCount>0?(i.jankFrames/i.sampleCount*100).toFixed(1):"0";console.log(`
462
- profile:`),console.log(` frames: ${i.frames}`),console.log(` total: ${i.totalMs.toFixed(1)}ms`),console.log(` avg: ${i.avgMs.toFixed(2)}ms (${e} fps)`),console.log(` max: ${i.maxMs.toFixed(2)}ms`),i.layoutAvgMs!==void 0&&console.log(` breakdown: layout ${i.layoutAvgMs.toFixed(2)}ms | render ${i.renderAvgMs?.toFixed(2)||"0.00"}ms | copy ${i.copyAvgMs?.toFixed(2)||"0.00"}ms`),console.log(` p95: ${i.p95.toFixed(2)}ms`),console.log(` p99: ${i.p99.toFixed(2)}ms`),console.log(` jank: ${i.jankFrames}/${i.sampleCount} (${t}%) >16.67ms`);let n=Array.isArray(i.worstFrames)?i.worstFrames:[];if(n.length!==0){if(console.log(`
463
- worst frames:`),i.mode==="render-worker"){console.log(" total layout render copy t+");for(let r of n){let[o,s,a,l,f]=r;console.log(` ${s.toFixed(2).padStart(7)} ${a.toFixed(2).padStart(7)} ${l.toFixed(2).padStart(7)} ${f.toFixed(2).padStart(7)} ${String(o).padStart(5)}`)}return}console.log(` ${n.map(r=>r.toFixed(2)).join(", ")}`)}}function bt(){return b.join(He(),`sootsim-flow-draft-${Date.now()}.yaml`)}function vt(i,e){if(!e)throw new Error("validated flow draft requires an output path");let t=[e];for(let n=1;n<i.length;n++){let r=i[n];if(!(r==="--validate"||r==="--video")){if(r==="--output"){n+=1;continue}n===1&&!r.startsWith("-")||t.push(r)}}return!t.includes("--record")&&i.includes("--video")&&t.push("--record"),t}export{pt as a,Ke as b,yt as c,Ht as d};
460
+ `:""),r=e(Buffer.from(t,"utf8")),n=b.join(He(),`sootsim-events-${Date.now()}.jsonl.gz`);return _.writeFileSync(n,r),console.log(` events: ${i.length} written to ${n} (${r.length} bytes gz)`),n}async function gt(i){let e=i[0],t=r=>i.find((n,s)=>i[s-1]===r);switch(e){case"start":{let{path:r,state:n}=Me();console.log(" flow draft started"),console.log(` session: ${r}`),console.log(` steps: ${n.steps.length}`);return}case"keep":case"good":{let r=se();if(r.active||(console.error(" no active flow draft \u2014 run `sootsim flow start` first"),process.exit(1)),!r.kept){console.log(" no pending action to keep");return}console.log(` kept: ${r.candidate.summary}`),console.log(` steps: ${r.stepCount}`);return}case"end":{let r=t("--output")||(i[1]&&!i[1].startsWith("-")?i[1]:void 0),n=i.includes("--validate")||i.includes("--video"),s=i.includes("--video"),o=se();o.active&&o.kept&&console.log(` auto-kept trailing action: ${o.candidate.summary}`);let a=Pe(r||(n?bt():void 0));if(a.active||(console.error(" no active flow draft \u2014 run `sootsim flow start` first"),process.exit(1)),!a.valid){console.error(" flow draft is not valid:");for(let l of a.issues)console.error(` - ${l}`);console.error(" draft preserved \u2014 keep at least one real interaction or run `sootsim flow start` to reset"),process.exit(1)}if(n){let l=vt(i,a.outputPath),f=await Ke(l);if(f!==0){console.error(`
461
+ validation failed \u2014 draft preserved so you can keep iterating`),process.exitCode=f;return}ie(),console.log(` flow draft validated (${a.stepCount} step${a.stepCount===1?"":"s"})`),a.outputPath&&console.log(` saved: ${a.outputPath}`),s&&console.log(" video: recorded during validation run");return}if(ie(),console.log(` flow draft ended (${a.stepCount} step${a.stepCount===1?"":"s"})`),a.outputPath){console.log(` saved: ${a.outputPath}`),console.log(` next: sootsim flow ${a.outputPath} --record`);return}console.log(""),process.stdout.write(a.yaml);return}case"validate":{let r=i[1];(!r||r.startsWith("-"))&&(console.error(" usage: sootsim flow validate <path>"),process.exit(1));let n=ne(r);if(n.length>0){console.error(` x ${r} failed validation:`);for(let s of n)console.error(` - ${s}`);process.exit(1)}console.log(` + ${r} looks valid`);return}}}function yt(i){return i[0]==="--sim"&&i.length>=2?[...i.slice(2),"--sim",i[1]]:i}async function Ht(i){let e=yt(i),t=e[0];if(t==="start"||t==="keep"||t==="good"||t==="end"||t==="validate")return await gt(e),0;let r=await Ke(e);return r!==0&&(process.exitCode=r),r}function We(i){let e=i.avgMs>0?(1e3/i.avgMs).toFixed(1):"?",t=i.sampleCount>0?(i.jankFrames/i.sampleCount*100).toFixed(1):"0";console.log(`
462
+ profile:`),console.log(` frames: ${i.frames}`),console.log(` total: ${i.totalMs.toFixed(1)}ms`),console.log(` avg: ${i.avgMs.toFixed(2)}ms (${e} fps)`),console.log(` max: ${i.maxMs.toFixed(2)}ms`),i.layoutAvgMs!==void 0&&console.log(` breakdown: layout ${i.layoutAvgMs.toFixed(2)}ms | render ${i.renderAvgMs?.toFixed(2)||"0.00"}ms | copy ${i.copyAvgMs?.toFixed(2)||"0.00"}ms`),console.log(` p95: ${i.p95.toFixed(2)}ms`),console.log(` p99: ${i.p99.toFixed(2)}ms`),console.log(` jank: ${i.jankFrames}/${i.sampleCount} (${t}%) >16.67ms`);let r=Array.isArray(i.worstFrames)?i.worstFrames:[];if(r.length!==0){if(console.log(`
463
+ worst frames:`),i.mode==="render-worker"){console.log(" total layout render copy t+");for(let n of r){let[s,o,a,l,f]=n;console.log(` ${o.toFixed(2).padStart(7)} ${a.toFixed(2).padStart(7)} ${l.toFixed(2).padStart(7)} ${f.toFixed(2).padStart(7)} ${String(s).padStart(5)}`)}return}console.log(` ${r.map(n=>n.toFixed(2)).join(", ")}`)}}function bt(){return b.join(He(),`sootsim-flow-draft-${Date.now()}.yaml`)}function vt(i,e){if(!e)throw new Error("validated flow draft requires an output path");let t=[e];for(let r=1;r<i.length;r++){let n=i[r];if(!(n==="--validate"||n==="--video")){if(n==="--output"){r+=1;continue}r===1&&!n.startsWith("-")||t.push(n)}}return!t.includes("--record")&&i.includes("--video")&&t.push("--record"),t}export{pt as a,Ke as b,yt as c,Ht as d};
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.181 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{C as v,D as f,E as R,G as w,l as p,u as S,x as O}from"./chunk-X25HNIMN.js";import{spawn as I}from"child_process";import C from"crypto";import s from"fs";import l from"path";import{Readable as M}from"stream";import{pipeline as k}from"stream/promises";var N="https://sootbean.com",E="SOOTSIM_CDN_ORIGIN",T="SOOTSIM_RUNTIME_CHANNEL",q="https://sootsim.com/changelog";function $(){try{let t=JSON.parse(s.readFileSync(O(),"utf8"));return t&&typeof t=="object"?t:{}}catch{return{}}}function d(t){let e=$();return(t||process.env[E]||e.cdnOrigin||N).replace(/\/+$/,"")}function x(t){let e=$();return t||process.env[T]||e.runtimeChannel||"stable"}function D(t){let e=new URL(`${d(t)}/runtimes/manifest.json`);return e.searchParams.set("t",String(Date.now())),e.toString()}function U(t,e){return`${d(e)}/runtimes/sootsim-runtime-${t}.tar.gz`}async function y(t){let e=D(t),r=await fetch(e,{headers:{Accept:"application/json"}});if(!r.ok)throw new Error(`manifest fetch failed: ${r.status} ${r.statusText} (${e})`);return await r.json()}function P(t,e={}){let r=x(e.channel),n=e.version||t.channels[r]?.latest;if(!n)throw new Error(`no version specified and channel '${r}' has no latest entry in the manifest`);let i=t.versions[n];if(!i)throw new Error(`version ${n} not found in manifest; available: ${Object.keys(t.versions).slice(-10).join(", ")||"(none)"}`);return{version:n,channel:r,entry:i}}async function _(t={}){v();let e=d(t.cdnOrigin),r=await y(e),{version:n,channel:i,entry:a}=P(r,t),o=p(n),c=t.setActive!==!1;if(!t.force&&s.existsSync(l.join(o,"index.html")))return c&&R(n),{version:n,channel:i,cdnOrigin:e,runtimeDir:o,installed:!1,activated:c,manifest:r};let g=a.tarball||U(n,e),m=l.join(S(),`sootsim-runtime-${n}.tar.gz`);process.stderr.write(`sootsim: downloading runtime ${n}\u2026
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{C as v,D as f,E as R,G as w,l as p,u as S,x as O}from"./chunk-AP22KA3Q.js";import{spawn as I}from"child_process";import C from"crypto";import s from"fs";import l from"path";import{Readable as M}from"stream";import{pipeline as k}from"stream/promises";var N="https://sootbean.com",E="SOOTSIM_CDN_ORIGIN",T="SOOTSIM_RUNTIME_CHANNEL",q="https://sootsim.com/changelog";function $(){try{let t=JSON.parse(s.readFileSync(O(),"utf8"));return t&&typeof t=="object"?t:{}}catch{return{}}}function d(t){let e=$();return(t||process.env[E]||e.cdnOrigin||N).replace(/\/+$/,"")}function x(t){let e=$();return t||process.env[T]||e.runtimeChannel||"stable"}function D(t){let e=new URL(`${d(t)}/runtimes/manifest.json`);return e.searchParams.set("t",String(Date.now())),e.toString()}function U(t,e){return`${d(e)}/runtimes/sootsim-runtime-${t}.tar.gz`}async function y(t){let e=D(t),r=await fetch(e,{headers:{Accept:"application/json"}});if(!r.ok)throw new Error(`manifest fetch failed: ${r.status} ${r.statusText} (${e})`);return await r.json()}function P(t,e={}){let r=x(e.channel),n=e.version||t.channels[r]?.latest;if(!n)throw new Error(`no version specified and channel '${r}' has no latest entry in the manifest`);let i=t.versions[n];if(!i)throw new Error(`version ${n} not found in manifest; available: ${Object.keys(t.versions).slice(-10).join(", ")||"(none)"}`);return{version:n,channel:r,entry:i}}async function _(t={}){v();let e=d(t.cdnOrigin),r=await y(e),{version:n,channel:i,entry:a}=P(r,t),o=p(n),c=t.setActive!==!1;if(!t.force&&s.existsSync(l.join(o,"index.html")))return c&&R(n),{version:n,channel:i,cdnOrigin:e,runtimeDir:o,installed:!1,activated:c,manifest:r};let g=a.tarball||U(n,e),m=l.join(S(),`sootsim-runtime-${n}.tar.gz`);process.stderr.write(`sootsim: downloading runtime ${n}\u2026
3
3
  `),await V(g,m),process.stderr.write(`sootsim: extracting runtime ${n}\u2026
4
4
  `);let h=await A(m);if(h!==a.sha256)throw s.rmSync(m,{force:!0}),new Error(`sha256 mismatch for runtime ${n}: expected ${a.sha256}, actual ${h}`);let u=l.join(l.dirname(o),`.installing-${n}-${process.pid}`);s.rmSync(u,{recursive:!0,force:!0}),s.mkdirSync(u,{recursive:!0});try{if(await j(m,u),!s.existsSync(l.join(u,"index.html")))throw new Error(`extracted tarball for runtime ${n} is missing index.html`);s.rmSync(o,{recursive:!0,force:!0}),s.renameSync(u,o)}catch(b){throw s.rmSync(u,{recursive:!0,force:!0}),b}return c&&R(n),{version:n,channel:i,cdnOrigin:e,runtimeDir:o,installed:!0,activated:c,manifest:r}}async function B(t={}){v();let e=d(t.cdnOrigin),r=x(t.channel),n=await y(e),i=n.channels[r]?.latest;if(!i)return{checked:!0,updated:!1,reason:`channel '${r}' has no latest runtime`,activeVersion:f()};if(!n.versions[i])return{checked:!0,updated:!1,reason:`manifest is missing version ${i}`,activeVersion:f(),latestVersion:i};let o=f(),c=o?p(o):null,g=c?s.existsSync(l.join(c,"index.html")):!1;if(!(!o||!g||w(i,o)>0))return{checked:!0,updated:!1,reason:"active runtime is current",activeVersion:o,latestVersion:i};let h=await _({version:i,channel:r,cdnOrigin:e,setActive:!1});return{checked:!0,updated:!0,activeVersion:i,latestVersion:i,install:h}}async function K(t={}){let e=f();try{let r=await y(d(t.cdnOrigin)),n=x(t.channel),i=r.channels[n]?.latest??null,a=!!(i&&(!e||w(i,e)>0));return{active:e,latest:i,outdated:a}}catch{return{active:e,latest:null,outdated:!1}}}async function V(t,e){let r=await fetch(t);if(!r.ok||!r.body)throw new Error(`download failed: ${r.status} ${r.statusText} (${t})`);s.mkdirSync(l.dirname(e),{recursive:!0});let n=`${e}.partial`;try{await k(M.fromWeb(r.body),s.createWriteStream(n)),s.renameSync(n,e)}catch(i){try{s.unlinkSync(n)}catch{}throw i}}function A(t){return new Promise((e,r)=>{let n=C.createHash("sha256"),i=s.createReadStream(t);i.on("data",a=>n.update(a)),i.on("error",r),i.on("end",()=>e(n.digest("hex")))})}function j(t,e){return new Promise((r,n)=>{let i=I("tar",["-xzf",t,"-C",e],{stdio:["ignore","inherit","inherit"]});i.on("error",n),i.on("exit",a=>{a===0?r():n(new Error(`tar exited with code ${a}`))})})}export{N as a,q as b,d as c,x as d,y as e,_ as f,B as g,K as h};
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.181 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as M,b as U}from"./chunk-TF24QNTS.js";var _="sootsimConfig";function m(e){return!!e&&Object.keys(e).length>0}function $(e){return e?m(e.modules)||m(e.turboModules)||m(e.nativeModules)||m(e.env)||m(e.network?.remap)||m(e.settings)||m(e.initialState):!1}function D(e,o){let n=new URL(e);return $(o)?n.searchParams.set(_,JSON.stringify(o)):n.searchParams.delete(_),n.toString()}var ae="/index.bundle?platform=ios&dev=true&hot=true&minify=false";function E(e){return/^https?:\/\//i.test(e)}function T(e){return e.endsWith(".bundle")}function L(e){process.env.SOOTSIM_PREVIEW_PROD_BUNDLE&&(e.searchParams.set("dev","false"),e.searchParams.set("minify","true"),e.searchParams.has("hot")&&e.searchParams.set("hot","false"))}function N(e){try{let o=E(e),n=new URL(e,"http://soot.local");return n.pathname=n.pathname.replace(/\.\.bundle$/,".bundle"),T(n.pathname)?(n.searchParams.delete("transform.bytecode"),n.searchParams.get("lazy")==="true"&&n.searchParams.set("lazy","false"),L(n),o?n.toString():`${n.pathname}${n.search}${n.hash}`):e}catch{return e}}function ce(e,o){try{let n=E(e),t=new URL(e,"http://soot.local");return T(t.pathname)?(t.searchParams.set("hot",o?"true":"false"),n?t.toString():`${t.pathname}${t.search}${t.hash}`):e}catch{return e}}import{exec as O}from"child_process";import I from"http";import j from"net";import{promisify as B}from"util";var k=B(O);M();var H=250,F=1500,W=120;function G(e,o=W){return new Promise(n=>{let t=new j.Socket,s=!1,i=a=>{s||(s=!0,t.destroy(),n(a))};t.setTimeout(o),t.once("connect",()=>i(!0)),t.once("timeout",()=>i(!1)),t.once("error",()=>i(!1)),t.connect(e,"localhost")})}function h(e,o,n="GET",t=H,s={}){return new Promise(i=>{let a=I.request({hostname:"localhost",port:e,path:o,method:n,timeout:t,headers:s},r=>{let c="";r.on("data",l=>c+=l.toString());let u=(()=>{let l=r.headers["content-type"];if(typeof l=="string")return l;if(Array.isArray(l))return l[0]})();r.on("end",()=>i({statusCode:r.statusCode||0,body:c,contentType:u}))});a.on("error",()=>i(null)),a.setTimeout(t,()=>{a.destroy(),i(null)}),a.end()})}var z=[8081,8082,8083,8084,8085,8086,3e3,3001,19e3].map(e=>({port:e,pid:0}));function P(e,o){return!(e<=0||e>=2e4||o.has(e)||e>=5170&&e<=5200)}async function J(e=[]){let o=new Set(e);try{let{stdout:n}=await k("lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null | grep -E '^(node|bun)'",{encoding:"utf8",timeout:2e3});if(n.trim()){let t=new Map;for(let s of n.trim().split(`
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{a as M,b as U}from"./chunk-7SP7PFUR.js";var _="sootsimConfig";function m(e){return!!e&&Object.keys(e).length>0}function $(e){return e?m(e.modules)||m(e.turboModules)||m(e.nativeModules)||m(e.env)||m(e.network?.remap)||m(e.settings)||m(e.initialState):!1}function D(e,o){let n=new URL(e);return $(o)?n.searchParams.set(_,JSON.stringify(o)):n.searchParams.delete(_),n.toString()}var ae="/index.bundle?platform=ios&dev=true&hot=true&minify=false";function E(e){return/^https?:\/\//i.test(e)}function T(e){return e.endsWith(".bundle")}function L(e){process.env.SOOTSIM_PREVIEW_PROD_BUNDLE&&(e.searchParams.set("dev","false"),e.searchParams.set("minify","true"),e.searchParams.has("hot")&&e.searchParams.set("hot","false"))}function N(e){try{let o=E(e),n=new URL(e,"http://soot.local");return n.pathname=n.pathname.replace(/\.\.bundle$/,".bundle"),T(n.pathname)?(n.searchParams.delete("transform.bytecode"),n.searchParams.get("lazy")==="true"&&n.searchParams.set("lazy","false"),L(n),o?n.toString():`${n.pathname}${n.search}${n.hash}`):e}catch{return e}}function ce(e,o){try{let n=E(e),t=new URL(e,"http://soot.local");return T(t.pathname)?(t.searchParams.set("hot",o?"true":"false"),n?t.toString():`${t.pathname}${t.search}${t.hash}`):e}catch{return e}}import{exec as O}from"child_process";import I from"http";import j from"net";import{promisify as B}from"util";var k=B(O);M();var H=250,F=1500,W=120;function G(e,o=W){return new Promise(n=>{let t=new j.Socket,s=!1,i=a=>{s||(s=!0,t.destroy(),n(a))};t.setTimeout(o),t.once("connect",()=>i(!0)),t.once("timeout",()=>i(!1)),t.once("error",()=>i(!1)),t.connect(e,"localhost")})}function h(e,o,n="GET",t=H,s={}){return new Promise(i=>{let a=I.request({hostname:"localhost",port:e,path:o,method:n,timeout:t,headers:s},r=>{let c="";r.on("data",l=>c+=l.toString());let u=(()=>{let l=r.headers["content-type"];if(typeof l=="string")return l;if(Array.isArray(l))return l[0]})();r.on("end",()=>i({statusCode:r.statusCode||0,body:c,contentType:u}))});a.on("error",()=>i(null)),a.setTimeout(t,()=>{a.destroy(),i(null)}),a.end()})}var z=[8081,8082,8083,8084,8085,8086,3e3,3001,19e3].map(e=>({port:e,pid:0}));function P(e,o){return!(e<=0||e>=2e4||o.has(e)||e>=5170&&e<=5200)}async function J(e=[]){let o=new Set(e);try{let{stdout:n}=await k("lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null | grep -E '^(node|bun)'",{encoding:"utf8",timeout:2e3});if(n.trim()){let t=new Map;for(let s of n.trim().split(`
3
3
  `)){let i=s.trim().split(/\s+/);if(i.length<9)continue;let a=Number(i[1]),c=i[8].match(/:(\d+)$/);if(!c)continue;let u=Number(c[1]);P(u,o)&&(t.has(u)||t.set(u,a))}if(t.size>0)return[...t.entries()].map(([s,i])=>({port:s,pid:i}))}}catch{}try{let{stdout:n}=await k(`ss -tlnp 2>/dev/null | grep -E '"(node|bun)"'`,{encoding:"utf8",timeout:2e3});if(n.trim()){let t=new Map;for(let s of n.trim().split(`
4
4
  `)){let i=s.match(/:(\d+)\s/),a=s.match(/pid=(\d+)/);if(!i)continue;let r=Number(i[1]),c=a?Number(a[1]):0;P(r,o)&&(t.has(r)||t.set(r,c))}if(t.size>0)return[...t.entries()].map(([s,i])=>({port:s,pid:i}))}}catch{}return z.filter(n=>P(n.port,o))}var v=new Map;async function K(e){if(e<=0)return null;let o=v.get(e);if(o)return o;try{let{stdout:n}=await k(`lsof -p ${e} -a -d cwd -Fn 2>/dev/null`,{encoding:"utf8",timeout:1500});for(let t of n.split(`
5
5
  `))if(t.startsWith("n")&&t.length>1){let s=t.slice(1).trim();if(s)return v.set(e,s),s}}catch{}return null}function V(e,o){return{port:e,framework:o,bundleUrl:g(e,`http://localhost:${e}/index.bundle?platform=ios&dev=true&hot=true&minify=false`),hmrUrl:`ws://localhost:${e}/hot`,lastSeen:Date.now()}}function g(e,o){let n=U().find(s=>s.preferredPort===e),t=n?.runtimeConfig?D(o,n.runtimeConfig):o;return N(t)}function q(e){return e.includes("/node_modules/one/metro-entry.bundle")}function Q(e){try{let o=JSON.parse(e);return o&&typeof o=="object"?o:null}catch{return null}}function S(e,o,n){if(!o)return e;try{let t=JSON.parse(o.body),s=t?.extra?.expoClient||t?.extra||{};s.name&&(e.projectName=s.name),s.ios?.bundleIdentifier&&(e.bundleId=s.ios.bundleIdentifier),e.framework==="metro"&&s.sdkVersion&&(e.framework="expo");let i=t?.launchAsset?.url;i&&!e.patched&&!q(e.bundleUrl)&&(e.bundleUrl=g(e.port,i));let a=s.iconUrl||s.ios?.iconUrl||s.icon||s.ios?.icon;if(a)if(e.iconPath=a,n)if(a.startsWith("http"))e.iconUrl=n(a);else{let r=a.replace(/^\.\//,"");e.iconUrl=n(`http://localhost:${e.port}/assets/${r}`)}else e.iconUrl=a.startsWith("http")?a:`http://localhost:${e.port}/assets/${a.replace(/^\.\//,"")}`}catch{}return e}var Y=2e4,b=new Map,R=new Set;function C(e,o){if(e.projectName)return e;let n=b.get(e.port);if(n)return n.projectName&&(e.projectName=n.projectName),n.iconUrl&&!e.iconUrl&&(e.iconUrl=n.iconUrl,e.iconPath=n.iconPath),n.bundleId&&!e.bundleId&&(e.bundleId=n.bundleId),e;if(!R.has(e.port)){R.add(e.port);let t=e.port;X(t,o).finally(()=>R.delete(t))}return e}async function X(e,o){let n=await h(e,"/","GET",Y,{"expo-platform":"ios"});if(!n||n.statusCode!==200)return;let t=S({port:e,framework:"one",bundleUrl:"",lastSeen:0},n,o);!t.projectName&&!t.iconUrl||(b.set(e,{projectName:t.projectName,iconUrl:t.iconUrl,iconPath:t.iconPath,bundleId:t.bundleId}),w.delete(e))}var d=new Set,p=new Set,y=new Set;async function Z(e,o){if(!await G(e))return null;let n="/node_modules/one/metro-entry.bundle?platform=ios&dev=true",[t,s,i,a]=await Promise.all([d.has(e)?Promise.resolve(null):h(e,"/__soot/"),h(e,"/status"),y.has(e)?Promise.resolve(null):h(e,"/","GET",F,{"expo-platform":"ios"}),p.has(e)?Promise.resolve(null):h(e,"/_expo/status")]);a&&a.statusCode===200?p.delete(e):p.has(e)||p.add(e);let r=i?Q(i.body):null,c=typeof r?.launchAsset?.url=="string"?r.launchAsset.url:null,u=r?.extra?.expoClient||r?.extra||{};if(r&&(c||typeof u.name=="string")){d.add(e);let x=c||`http://localhost:${e}/index.bundle?platform=ios&dev=true&hot=true&minify=false`,A=x.includes("/one/metro-entry.bundle")?"one":"expo";return S({port:e,framework:A,bundleUrl:g(e,x),hmrUrl:`ws://localhost:${e}/hot`,lastSeen:Date.now()},i,o)}if(s&&s.body.includes("packager-status:running"))return d.add(e),C(S(V(e,a&&a.statusCode===200?"expo":"metro"),i,o),o);if(t&&t.statusCode===200&&t.body.includes("sootsim-patched"))return d.delete(e),C(S({port:e,framework:"one",bundleUrl:g(e,`http://localhost:${e}/__soot/bundle.js`),hmrUrl:`ws://localhost:${e}/hot`,lastSeen:Date.now(),patched:!0},i,o),o);if(s&&s.statusCode===200||a&&a.statusCode===200||i&&i.statusCode===200&&r!=null)return null;let f=await h(e,n,"HEAD");return f&&f.statusCode>0&&f.statusCode<400&&/application\/javascript/i.test(f.contentType||"")?(d.add(e),y.add(e),C(S({port:e,framework:"one",bundleUrl:g(e,`http://localhost:${e}${n}&minify=false`),hmrUrl:`ws://localhost:${e}/hot`,lastSeen:Date.now()},i,o),o)):(d.add(e),null)}function ee(e){return!!e.bundleId?.trim().toLowerCase()?.startsWith("dev.sootsim.")}var w=new Map,ne=3e4,te=1500;function re(e){return!e||e.framework==="metro"||e.framework==="unknown"}function oe(e){return e?g(e.port,e.bundleUrl)===e.bundleUrl:!0}function se(e,o,n=Date.now()){if(o===0||e.pid!==o||!oe(e.result))return!1;let t=n-e.cachedAt;return!(e.result===null&&t>=ne||re(e.result)&&t>=te)}async function Se(e={}){let o=await J(e.excludePorts),n=new Set(o.map(r=>r.port));for(let r of[...w.keys()])n.has(r)||w.delete(r);for(let r of[...d])n.has(r)||d.delete(r);for(let r of[...p])n.has(r)||p.delete(r);for(let r of[...y])n.has(r)||y.delete(r);for(let r of[...b.keys()])n.has(r)||b.delete(r);let t=[],s=[];for(let{port:r,pid:c}of o){let u=w.get(r);if(u&&se(u,c)){u.result&&t.push(u.result);continue}u&&u.pid!==c&&(d.delete(r),p.delete(r),y.delete(r),b.delete(r)),s.push({port:r,pid:c})}s.length>0&&(await Promise.all(s.map(c=>Z(c.port,e.buildIconProxyUrl)))).forEach((c,u)=>{let{port:l,pid:f}=s[u];f!==0&&w.set(l,{pid:f,result:c,cachedAt:Date.now()}),c&&t.push(c)});let i=new Map;for(let{port:r,pid:c}of o)c>0&&i.set(r,c);await Promise.all(t.map(async r=>{let c=i.get(r.port);if(!c)return;r.pid=c;let u=await K(c);u&&(r.cwd=u)}));let a=new Set(i.values());for(let r of[...v.keys()])a.has(r)||v.delete(r);return t.filter(r=>!ee(r))}export{D as a,ae as b,N as c,ce as d,Z as e,Se as f};
@@ -1,2 +1,2 @@
1
- /*! sootsim v0.1.181 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var o=!0,e="public beta",t=e,n="we send anonymous error and dependency data while you use it";export{o as a,e as b,t as c,n as d};
@@ -0,0 +1 @@
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
@@ -1,2 +1,2 @@
1
- /*! sootsim v0.1.181 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var t="./demo-app-registry.ts",o=null;async function n(){if(o)return o;try{let r=await import(t);o=Array.isArray(r.APPS)?r.APPS:[]}catch{o=[]}return o}function e(){return o??[]}export{n as a,e as b};
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.181 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import t from"node:fs";import{homedir as A}from"node:os";import o from"node:path";var E="SOOTSIM_HOME",_="active",I="daemon.json",N="dev-bridge.json",j="config.json",h=3e4;function u(){let e=process.env[E];return e&&e.length>0?o.resolve(e):o.join(A(),".sootsim")}function F(){if(process.env.SOOTSIM_FORCE_DAEMON_INSTALL==="1")return!1;let e=process.env.SOOTSIM_DEV;if(e==="1"||e==="true")return!0;if(e==="0"||e==="false")return!1;let r=process.argv[1];if(!r)return!1;try{return t.realpathSync(r).includes(`${o.sep}packages${o.sep}sootsim${o.sep}`)}catch{return!1}}function P(){return process.env.SOOTSIM_FORCE_DAEMON_INSTALL==="1"?!1:process.env.IS_TAMAGUI_DEV==="1"}function W(){return F()||P()}function f(){return o.join(u(),"runtimes")}function w(e){return o.join(f(),e)}function k(){return o.join(f(),_)}function D(){return o.join(u(),"electron")}function X(){return o.join(D(),"userData")}function q(e){return o.join(D(),e)}function T(){return o.join(u(),"profiles")}function B(){return o.join(u(),"daemon-app")}function M(){return o.join(B(),"SootSim Daemon.app")}function z(){return o.join(M(),"Contents","MacOS","sootsim-daemon")}function C(){return o.join(u(),"cache")}function p(){return o.join(u(),I)}function m(){return o.join(u(),N)}function b(){return o.join(u(),j)}function L(){try{let e=t.readFileSync(b(),"utf8"),r=JSON.parse(e);return r&&typeof r=="object"?r:{}}catch{return{}}}function R(e){g();let r=L(),i={...r,...e};e.settings&&typeof e.settings=="object"&&(i.settings={...r.settings&&typeof r.settings=="object"?r.settings:{},...e.settings});let c=`${b()}.tmp`;return t.writeFileSync(c,`${JSON.stringify(i,null,2)}
3
3
  `,"utf8"),t.renameSync(c,b()),i}function Q(){return L().telemetry!==!1}function Z(e){R({telemetry:e})}function g(){t.mkdirSync(u(),{recursive:!0}),t.mkdirSync(f(),{recursive:!0}),t.mkdirSync(D(),{recursive:!0}),t.mkdirSync(T(),{recursive:!0}),t.mkdirSync(C(),{recursive:!0})}function $(){try{let e=t.readFileSync(k(),"utf8").trim();return e.length>0?e:null}catch{return null}}function ee(e){t.mkdirSync(f(),{recursive:!0}),t.writeFileSync(k(),`${e}
4
4
  `,"utf8")}function re(){try{return t.readdirSync(f(),{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).sort(J)}catch{return[]}}function J(e,r){let i=s=>{let a=s.indexOf("-"),l=a>=0?s.slice(0,a):s,O=a>=0?s.slice(a+1):"",x=l.split(".").map(S=>Number.parseInt(S,10));return x.some(S=>!Number.isFinite(S))?[[Number.POSITIVE_INFINITY],s]:[x,O]},[c,n]=i(e),[v,y]=i(r);for(let s=0;s<Math.max(c.length,v.length);s++){let a=c[s]??0,l=v[s]??0;if(a!==l)return a-l}return n===y?0:n?y?n<y?-1:1:-1:1}function te(){let e=$();if(!e)return null;let r=w(e);try{if(t.statSync(r).isDirectory())return r}catch{}return null}var d=16*1024;function V(){try{let e=t.openSync(p(),"r");try{let r=Buffer.alloc(d),i=t.readSync(e,r,0,d,0),c=r.subarray(0,i).toString("utf8"),n=JSON.parse(c);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{t.closeSync(e)}}catch{return null}}function ne(){try{let e=t.openSync(m(),"r");try{let r=Buffer.alloc(d),i=t.readSync(e,r,0,d,0),c=r.subarray(0,i).toString("utf8"),n=JSON.parse(c);return n&&n.schema===1&&n.source==="vite-dev"&&typeof n.pid=="number"&&typeof n.bridgePort=="number"&&typeof n.runtimePort=="number"&&typeof n.cwd=="string"&&typeof n.startedAt=="number"&&typeof n.heartbeatAt=="number"?n:null}finally{t.closeSync(e)}}catch{return null}}function H(e,r=Date.now()){if(!e||r-e.heartbeatAt>h)return!1;try{return process.kill(e.pid,0),!0}catch{return!1}}function oe(e,r=Date.now()){if(!e||r-e.heartbeatAt>h)return!1;try{return process.kill(e.pid,0),!0}catch{return!1}}function G(e){g();let r=`${p()}.tmp`;t.writeFileSync(r,`${JSON.stringify(e,null,2)}
@@ -0,0 +1,13 @@
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{a as F,b as Q,c as Y,d as ye,e as Se}from"./chunk-6GCEDCO5.js";import{m as T}from"./chunk-QF6RJQ2U.js";import{m as he,o as we}from"./chunk-WOOWTKM7.js";import{g as te}from"./chunk-BTIARV2B.js";import{a as O,b as X}from"./chunk-WN6XO6JV.js";import{c as ve}from"./chunk-PY4P54SD.js";import{a as Ie,b as ke}from"./chunk-4FVYQIRB.js";import{a as $e,b as Z,c as xe}from"./chunk-C7BIPAXT.js";import{b as be,c as $,d as y,e as x}from"./chunk-RQDJPT6N.js";import{d as ee}from"./chunk-JR7LKXPB.js";import{a as ge}from"./chunk-D7ZB6A5T.js";import{d as E,e as b,f as M}from"./chunk-QP4W3SGO.js";import{D as fe,I as N,K as C,l as pe}from"./chunk-AP22KA3Q.js";import{spawn as Ue}from"node:child_process";import _ from"node:fs";import Ye from"node:os";import k from"node:path";import{WebSocket as Ze}from"ws";function We(e){return e.replace(/^\[|\]$/g,"").toLowerCase()}function H(e){let t=We(e);return t==="localhost"||t.endsWith(".localhost")||t==="0.0.0.0"||t==="::1"||/^127(?:\.\d{1,3}){3}$/.test(t)}function Pe(e){return new Error(`could not resolve a native bundle for ${e}. pass an explicit bundle URL or open Connect and choose the app there.`)}function je(e,t){return e?.includes("/one/metro-entry.bundle")?"one":typeof t=="string"&&t?"expo":"unknown"}function Ee(e,t,o){return`${e}//${t}:${o}`}function ze(e){if(typeof window>"u")return!1;try{let t=new URL(e);return H(t.hostname)&&H(window.location.hostname)&&t.origin!==window.location.origin}catch{return!1}}async function Ce(e,t){let o={...t,cache:t?.cache??"no-store"};return ze(e)?fetch(`/__fetch-proxy?url=${encodeURIComponent(e)}`,o):fetch(e,o)}function qe(e){return e==="https:"?443:80}function Ge(e){let t=e.pathname||"/";return(t==="/"||t==="")&&!e.search&&!e.hash}async function Ve(e){try{let t=await fetch("/__server-scan",{cache:"no-store"});if(!t.ok||!(t.headers.get("content-type")||"").toLowerCase().includes("json"))return null;let o=await t.json();if(!Array.isArray(o))return null;let n=o.find(r=>!!r&&typeof r=="object"&&r.port===e&&typeof r.bundleUrl=="string");return n?{bundleUrl:Y(n.bundleUrl),port:e,framework:typeof n.framework=="string"?n.framework:"unknown",projectName:typeof n.projectName=="string"?n.projectName:void 0}:null}catch{return null}}async function Me(e,t){let o=e.replace(/\/+$/,"");try{if(H(new URL(o).hostname)){let n=await Ve(t);if(n)return n}}catch{}try{let n=await Ce(`${o}/`,{headers:{"expo-platform":"ios"}});if(n.ok){let r=await n.json(),s=r?.extra?.expoClient||r?.extra||{},i=typeof r?.launchAsset?.url=="string"?r.launchAsset.url:void 0;if(i||s.name)return{bundleUrl:Y(i||`${o}${Q}`),port:t,framework:je(i,s.sdkVersion),projectName:s.name}}}catch{}try{let n=await Ce(`${o}/status`);if(n.ok&&(await n.text()).includes("packager-status:running"))return{bundleUrl:`${o}${Q}`,port:t,framework:"metro"}}catch{}return null}async function Je(e){return Me(Ee("http:","localhost",e),e)}async function Te(e){let t=e.trim();if(/^\d+$/.test(t)){let a=parseInt(t,10),l=await Je(a);if(l)return l;throw Pe(`localhost:${a}`)}let o=t.startsWith("http")?t:`http://${t}`,n;try{n=new URL(o)}catch{throw new Error(`could not parse "${e}". pass a dev-server port, a dev-server base URL, or a full bundle URL.`)}let r=n.protocol||"http:",s=n.port?parseInt(n.port,10):qe(r),i=Ee(r,n.hostname,s);if(Ge(n)){let a=await Me(i,s);if(a)return a;throw Pe(i)}return{bundleUrl:n.toString(),port:s,framework:"unknown"}}function jt(){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 run setup to install the background daemon:"),console.error(" sootsim setup"),console.error("")}function Oe(e){let t=O();if(console.log(` note: no sootsim bridge detected on port ${e}`),t){console.log(" launch the installed companion with:"),console.log(" sootsim electron");return}console.log(""),console.log(" run `sootsim setup` to install the background daemon,"),console.log(" or run `sootsim serve` in another shell for a foreground bridge.")}function Qe(e){console.error(""),console.error(` no sim is connected to the sootsim bridge on port ${e}.`),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")}async function zt(e,t,o){let n=[];try{n=await e.listSims()}catch{}if(console.error(""),n.length===0){Qe(t);return}console.error(` no sim with id "${o}" is connected to the bridge on port ${t}.`),console.error(""),console.error(" connected sims:");for(let r of n){let s=[r.isPrimary?"primary":null,r.readyState,r.userVisible===!1?"hidden":r.userVisible===!0?"visible":null,r.userFocused?"focused":null].filter(Boolean).join(", ");console.error(` ${r.id} (${s})`)}console.error(""),console.error(" pass a valid --sim id, or run `sootsim list` to see all sessions.")}var Xe=12e4,Re=100,et=5e3,_e=1e4,tt={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 v(e){return new Promise(t=>setTimeout(t,e))}function Be(e){let t=process.env[e]?.trim();if(!t)return null;let o=Number(t);return Number.isInteger(o)&&o>0?o:null}function ot(){let e=Be("SOOTSIM_DRIVER_CONNECT_TIMEOUT_MS")??Be("SOOTSIM_PW_CONNECT_TIMEOUT_MS")??Xe;return{timeoutMs:e,hostTimeoutMs:e+et,intervalMs:Re,attempts:Math.max(1,Math.ceil(e/Re))}}function nt(e){return e==="chromium"||e==="electron"||e==="playwright"||e==="system"}function rt(e,t){return e.includes("--headless")?!0:e.includes("--headed")?!1:t==="playwright"&&process.platform==="linux"&&!process.env.DISPLAY&&!process.env.WAYLAND_DISPLAY?(console.error(" no display detected \u2192 running playwright headless (override with --headed)"),!0):!1}function j(e){return e.trim()}var st=/^[A-Za-z][A-Za-z0-9+.-]*:/;function it(e){let t=e.trim();if(!t)throw new Error("expected <url-or-route>");return st.test(t)||t.startsWith("/")?t:`/${t}`}function at(e){return e!==null&&typeof e=="object"}async function lt(e){if(await K(e,250))return e;let t=N();if(t&&C(t)&&await K(t.bridgePort,500))return t.bridgePort;if(t&&C(t))throw new Error(`sootsim bridge lockfile is fresh (port ${t.bridgePort}) but the bridge is not reachable. run \`sootsim daemon restart\` or \`sootsim daemon uninstall\`.`);await ct();let o=dt(["serve","--quiet","--port",String(e)]),n=null;o.once("exit",(s,i)=>{n=String(s??i??"unknown")}),o.unref();let r=Date.now()+_e;for(;Date.now()<r;){let s=N();if(s&&C(s)&&await K(s.bridgePort,250))return console.error(` started local bridge on port ${s.bridgePort}; run \`sootsim setup\` to install the background daemon`),s.bridgePort;if(await K(e,250))return console.error(` started local bridge on port ${e}; run \`sootsim setup\` to install the background daemon`),e;if(n)throw new Error(`sootsim bridge exited before becoming ready (exit ${n})`);await v(150)}throw new Error(`sootsim bridge did not start within ${Math.round(_e/1e3)}s`)}async function ct(){let e=fe();if(e&&_.existsSync(pe(e)))return;console.error(" installing sootsim engine runtime...");let{runRuntime:t}=await import("./runtime-BAS2IZFB.js");await t(["install"],{})}function dt(e){let t=process.argv[1];return t&&_.existsSync(t)?Ue(process.execPath,[t,...e],{detached:!0,stdio:"ignore",env:process.env}):Ue("sootsim",e,{detached:!0,stdio:"ignore",env:process.env})}function K(e,t){return new Promise(o=>{let n=new Ze(`ws://127.0.0.1:${e}`,{handshakeTimeout:t}),r=!1,s=i=>{if(!r){r=!0;try{n.close()}catch{}o(i)}};n.once("open",()=>s(!0)),n.once("error",()=>s(!1)),setTimeout(()=>s(!1),t)})}function re(e){try{let t=new URL(e),o=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")||o==="/sootsim"||t.pathname==="/__soot"||t.pathname==="/__soot/"||o==="/rn"||/^\/rn\/[^/]+$/i.test(o)||/^\/app\/[^/]+$/i.test(o)||o==="/__soot/rn"||/^\/__soot\/rn\/[^/]+$/i.test(o)||/^\/__soot\/app\/[^/]+$/i.test(o)}catch{return!1}}function mt(e){try{let t=new URL(e).pathname.replace(/\/+$/,"")||"/";return/^\/preview\/[^/]+$/.test(t)||/^\/build\/.+/.test(t)}catch{return!1}}async function ho(e){let t=j(e);return(await Ne(t)).bundleUrl}function ut(e){try{let t=new URL(e.startsWith("http")?e:`http://${e}`),o=t.pathname||"/";if(o!=="/"&&o!==""||t.search||t.hash||!/^(localhost|127\.0\.0\.1|\[::1\]|::1)$/i.test(t.hostname))return null;let n=t.port?Number(t.port):t.protocol==="https:"?443:80;return Number.isFinite(n)&&n>0?n:null}catch{return null}}async function Ne(e){let t=j(e),o=/^\d+$/.test(t)?Number(t):ut(t);if(o&&o>0){let n=await Se(o);if(n)return{bundleUrl:n.bundleUrl,port:n.port,framework:n.framework,projectName:n.projectName}}return Te(t)}function ie(e){let t=e.replace(/\/+$/,"")||"/";return t==="/__soot"||t.startsWith("/__soot/")?"/__soot":""}function pt(e,t){let o=j(e),n=new URL(t),r=ie(n.pathname);return n.pathname=`${r}/rn`,n.searchParams.delete("bundle"),n.searchParams.delete("demo"),n.searchParams.delete("app"),n.searchParams.delete("open"),n.searchParams.delete("port"),/^\d+$/.test(o)?n.pathname=`${r}/rn/${o}`:(n.pathname=`${r}/rn`,n.searchParams.set("open",o)),n.toString()}function ft(e){let t=j(e);return/^\d+$/.test(t)||/^https?:\/\//i.test(t)?!0:/^(localhost|127\.0\.0\.1|\[::1\]|[^/]+\.localhost):\d+(?:\/.*)?$/i.test(t)}async function gt(e,t,o={}){let n=await Ne(e),r=new URL(t),s=ie(r.pathname);r.pathname=`${s}/rn`,r.searchParams.delete("open"),r.searchParams.delete("port"),r.searchParams.delete("demo"),r.searchParams.delete("app");let i=o.hot===void 0?n.bundleUrl:ye(n.bundleUrl,o.hot);return r.searchParams.set("bundle",i),r.toString()}function ht(e){return e.startsWith("~/")?k.join(Ye.homedir(),e.slice(2)):k.isAbsolute(e)?e:k.resolve(process.cwd(),e)}function wt(e){let t={},o={};for(let r=0;r<e.length;r++){if(e[r]==="--replace"){let s=e[r+1];s||(console.error(" sootsim open: --replace expects <module>=<file>"),process.exit(1));let i=s.indexOf("=");(i<=0||i===s.length-1)&&(console.error(" sootsim open: --replace expects <module>=<file>"),process.exit(1));let a=s.slice(0,i).trim(),l=ht(s.slice(i+1).trim());_.existsSync(l)||(console.error(` sootsim open: replacement file not found: ${l}`),process.exit(1)),t[a]={file:l},r++;continue}if(e[r]==="--remap"){let s=e[r+1],i=s?s.indexOf("="):-1;(!s||i<=0||i===s.length-1)&&(console.error(" sootsim open: --remap expects <host:port>=<host:port>"),process.exit(1)),o[s.slice(0,i).trim()]=s.slice(i+1).trim(),r++;continue}}let n={};return Object.keys(t).length>0&&(n.modules=t),Object.keys(o).length>0&&(n.network={remap:o}),n.modules||n.network?n:void 0}function Fe(){let e=N();return C(e)&&e.runtimePort>0?`http://localhost:${e.runtimePort}/`:ge}async function bt(e){try{let t=new URL(e),o=t.searchParams.get("bundle")||"";if(!o)return e;let n=process.env.SOOT_REPO_DIR||await Z(o)||void 0,r=$e({bundleUrl:o,repoDir:n});if(r.length===0)return e;let s=await yt(r);return s.length===0?e:(t.searchParams.set("appFonts",xe(s)),t.toString())}catch{return e}}async function yt(e){let t=await Promise.all(e.map(async n=>{let r=new AbortController,s=setTimeout(()=>r.abort(),1500);try{let i=await fetch(n.url,{method:"HEAD",signal:r.signal});return i.status>=200&&i.status<400?n:null}catch{return null}finally{clearTimeout(s)}})),o=[];for(let n of t)n!==null&&o.push(n);return o}async function St(e,t,o={}){if(!e)return new URL(t).toString();if(re(e))return new URL(e).toString();let n=tt[e.toLowerCase()];if(n){let r=new URL(t),s=ie(r.pathname);return r.pathname=`${s}${n}`,r.toString()}return ft(e)?gt(e,t,o):pt(e,t)}async function vt(e){try{let t=new URL(e),o=t.searchParams.get("bundle")||"";if(!o)return e;let n=process.env.SOOT_REPO_DIR||await Z(o)||void 0,r=Ie({bundleUrl:o,repoDir:n});return!r||r.imageUrl&&!await $t(r.imageUrl)?e:(t.searchParams.set("appSplash",ke(r)),t.toString())}catch{return e}}async function $t(e){let t=new AbortController,o=setTimeout(()=>t.abort(),1500);try{let n=await fetch(e,{method:"HEAD",signal:t.signal});return n.status>=200&&n.status<400}catch{return!1}finally{clearTimeout(o)}}async function W(e,t=Fe(),o={}){let n=await St(e,t,o);return vt(await bt(n))}function De(e,t){let o=e?.url||e?.origin||t;try{let n=new URL(o);return n.searchParams.delete("bundle"),n.searchParams.delete("demo"),n.searchParams.delete("app"),n.searchParams.delete("open"),n.searchParams.delete("port"),n.searchParams.delete("inspectOpen"),n.searchParams.delete("appFonts"),n.searchParams.delete("appSplash"),n.toString()}catch{return t}}async function xt(e,t,o,n={}){let r=new URL(await W(e,t,n));return r.searchParams.set("inspectOpen",o),r.toString()}async function It(e,t,o,n={}){let r=n.attempts??30,s=n.intervalMs??500,i=n.minNodeCount??10;for(let a=0;a<r;a++){let l=y(e,{commandTimeoutMs:t,simId:o,simIdSource:"flag"});try{let c=await l.send({type:"evaluate",code:"(async () => (await window.__sootsimTest?.getNodeCount()) || 0)()"});if(typeof c=="number"&&c>i)return{bridge:l,count:c}}catch{}l.close(),await v(s)}return null}function kt(e){if(!e)return null;try{let t=new URL(e);if(t.searchParams.has("bundle")){let o=t.searchParams.get("bundle")||"";try{let n=new URL(o),r=n.pathname.length>36?`...${n.pathname.slice(-36)}`:n.pathname;return`bundle ${n.host}${r}`}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 Pt(e){let t=e.indexOf(":");if(t<=0)return e.length>24?`${e.slice(0,21)}\u2026`:e;let o=e.slice(0,t),n=e.slice(t+1),r=n.length>12?`${n.slice(0,8)}\u2026`:n;return o==="AGENTBUS_SESSION"||o==="CLAUDE_CODE_SESSION_ID"||o==="CODEX_THREAD_ID"?`agent ${r}`:`${o} ${r}`}function Ct(e,t){if(e.length===0){console.log(" no sims connected");return}console.log(` connected sims (${e.length}):
3
+ `);for(let o of e){let n=o.lockedBy&&o.lockExpiresAt?`locked by ${Pt(o.lockedBy)} (${Math.max(0,Math.round((o.lockExpiresAt-Date.now())/1e3))}s)`:"",r=[o.isPrimary?"primary":"",o.id===t?"selected":"",o.readyState,o.userVisible===!1?"hidden":o.userVisible===!0?"visible":"",o.attachedCliCount&&o.attachedCliCount>0?"in use":"",o.userFocused?"focused":"",n].filter(Boolean);console.log(` ${o.id}${r.length?` [${r.join(", ")}]`:""}`);let s=kt(o.url);if(s)console.log(` loaded: ${s}`);else if(o.url){let a=o.url.length>96?`${o.url.slice(0,93)}\u2026`:o.url;console.log(` url: ${a}`)}else o.origin&&console.log(` origin: ${o.origin}`);let i=Et(o);i&&console.log(` engine: ${i}`),o.title&&console.log(` title: ${o.title}`),o.visibilityState&&o.visibilityState!=="visible"&&console.log(` visibility: ${o.visibilityState}`),console.log(` connected: ${Le(Date.now()-o.connectedAt)}`),o.lastActiveAt&&o.lastActiveAt>0&&console.log(` last active: ${Le(Date.now()-o.lastActiveAt)}`)}}function Et(e){let t=e.url||e.origin||"";if(!t)return null;try{let o=new URL(t).port;return o==="5173"?"dev \u2014 local source (:5173)":o==="3000"?"soot app embedded (:3000)":`runtime \u2014 published engine (:${o||"?"})`}catch{return null}}function Le(e){let t=Math.max(0,Math.round(e/1e3));return t<60?`${t}s ago`:t<3600?`${Math.round(t/60)}m ago`:t<86400?`${Math.round(t/3600)}h ago`:`${Math.round(t/86400)}d ago`}async function oe(e,t,o,n={}){let r=n.attempts??30,s=n.intervalMs??500;for(let i=0;i<r;i++){let a=y(e,{commandTimeoutMs:t});try{let c=(await a.listSims()).find(o);if(c)return c}catch{}finally{a.close()}await v(s)}return null}async function Mt(e,t,o,n,r={}){let s=r.attempts??30,i=r.intervalMs??500,a=!!n&&Number.isInteger(n)&&n>1,l=()=>a&&n!==void 0&&!se(n);for(let c=0;c<s;c++){if(l())return{kind:"host-exit"};let d=y(e,{commandTimeoutMs:t});try{let g=(await d.listSims()).find(o);if(g)return l()?{kind:"host-exit"}:{kind:"match",sim:g}}catch{}finally{d.close()}if(l())return{kind:"host-exit"};await v(i)}return{kind:"timeout"}}async function Tt(e,t,o,n={}){let r=n.attempts??20,s=n.intervalMs??250;for(let i=0;i<r;i++){let a=y(e,{commandTimeoutMs:t});try{let c=(await a.listSims()).find(d=>d.id===o);if(!c||c.readyState!=="open")return!0}catch{return!0}finally{a.close()}await v(s)}return!1}function Ot(e){let t=e.meta;if(!t||t.sootsimHostDriver!=="playwright")return null;let o=Number(t.sootsimHostPid);return!Number.isInteger(o)||o<=1||o===process.pid?null:o}function Ut(e){if(!e.connectAckFile)return!1;try{return _.writeFileSync(e.connectAckFile,`${JSON.stringify({connectedAt:Date.now(),pid:e.pid??null})}
4
+ `,{flag:"w"}),!0}catch{return!1}}function Ae(e){if(e.diagnosticLogPath)try{let t=_.readFileSync(e.diagnosticLogPath,"utf8").trim();if(!t){console.error(` ${k.basename(e.diagnosticLogPath)} was empty`);return}let o=t.length>4e3?`\u2026
5
+ ${t.slice(-4e3)}`:t;console.error(` ${k.basename(e.diagnosticLogPath)}:`),console.error(o)}catch(t){let o=t instanceof Error?t.message:String(t);console.error(` failed to read ${k.basename(e.diagnosticLogPath)}: ${o}`)}}function se(e){try{return process.kill(e,0),!0}catch(t){return t?.code==="EPERM"}}async function Rt(e,t=2500){let o=Date.now()+t;for(;Date.now()<o;){if(!se(e))return!0;await v(100)}return!se(e)}async function ne(e,t){let o=new Set(t),n=new Set;for(let r of e){if(!o.has(r.id))continue;let s=Ot(r);s&&n.add(s)}for(let r of n){try{process.kill(r,"SIGTERM")}catch{}if(await Rt(r)){console.log(` closed playwright host process ${r}`);continue}try{process.kill(r,"SIGKILL"),console.log(` force-closed playwright host process ${r}`)}catch{}}}function z(e,t){if(t){let r=t.trim(),s=e.find(i=>i.id===r);if(!s)throw new Error(`no sim connected with id ${r}`);return s}let o=e.find(r=>r.isPrimary&&r.readyState==="open");if(o)return o;let n=e.find(r=>r.readyState==="open");if(n)return n;throw new Error("no sim connected")}function U(e,t,o){console.log(` ${o==="current sim"?"loaded":"opened"}: ${e} [${o}]`),console.log(` current sim: ${t.id}`),console.log(JSON.stringify({simId:t.id,url:t.url},null,2))}async function R(e,t,o){if(o.includes("--no-describe"))return;let n=process.env.SOOTSIM_QUIET_TARGET_NOTICE;process.env.SOOTSIM_QUIET_TARGET_NOTICE="1";try{await _t(e,t,{stableMs:150,maxMs:400});let r=y(e,{commandTimeoutMs:3e3,simId:t,cliLabel:"open --describe",simIdSource:"flag"});try{let s=null;try{s=await r.send({type:"evaluate",code:he})}catch{}if(s&&!(s.flag===!0&&!s.loadingText&&we(s))){let d=s.loadingText?`still showing "${s.loadingText}"`:s.flag!==!0?"guest ready event has not fired":s.targets<=0?"no visible app content is inspectable yet":"app tree is still stabilizing";console.log(` app still loading: ${d} (nodes: ${s.nodes}, targets: ${s.targets})`),console.log(" before interacting, run: sootsim wait ready --max-ms 120000")}let a=`(async () => {
6
+ const t = window.__sootsimTest
7
+ const ms = window.SootSim?.bridges?.mainShell
8
+ if (!t) return null
9
+ let shell = null
10
+ try { shell = ms?.getState ? await ms.getState() : null } catch {}
11
+ const tree = await t.dumpTree(12, ${JSON.stringify({describe:!0,verbose:!1,filter:""})})
12
+ return { tree, shell }
13
+ })()`,l=await r.send({type:"evaluate",code:a});if(!l?.tree)return;if(console.log(""),l.shell?.state){let c=[`state=${l.shell.state}`,l.shell.activeApp?`app=${l.shell.activeApp}`:null].filter(Boolean);console.log(` shell: ${c.join(" ")}`)}console.log(l.tree)}finally{r.close()}}catch{}finally{n===void 0?delete process.env.SOOTSIM_QUIET_TARGET_NOTICE:process.env.SOOTSIM_QUIET_TARGET_NOTICE=n}}async function _t(e,t,o){let n=Date.now()+o.maxMs,r="(async () => (await window.__sootsimTest?.getNodeCount?.()) || 0)()",s=y(e,{commandTimeoutMs:2e3,simId:t,simIdSource:"flag"});try{let i=-1,a=0;for(;Date.now()<n;){let l=-1;try{let c=await s.send({type:"evaluate",code:r});typeof c=="number"&&(l=c)}catch{}if(l>=0&&l===i){if(Date.now()-a>=o.stableMs)return}else i=l,a=Date.now();await v(50)}}finally{s.close()}}async function wo(e,t={}){let o=$(e,{port:t.port,commandTimeoutMs:t.timeoutMs}),n=x(o);try{let r=await n.listSims();Ct(r,o.simId)}finally{n.close()}}async function bo(e,t={}){let o=$(e,{port:t.port,commandTimeoutMs:t.timeoutMs});o.positional.length!==1&&(console.error(" sootsim link: expected exactly one <url-or-route>"),console.error(" examples: sootsim link /settings | sootsim link myapp://settings"),process.exit(1));let n;try{n=it(o.positional[0]??"")}catch(i){console.error(` sootsim link: ${i instanceof Error?i.message:String(i)}`),process.exit(1)}let r=x(o),s=o.simId?` --sim ${o.simId}`:"";try{let i=await r.send({type:"call",path:"SootSim.bridges.test.openDeepLink",args:[n]});if(at(i)&&i.ok===!1)throw new Error(typeof i.error=="string"?i.error:"deep-link dispatch failed");console.log(` linked: ${n}`)}catch(i){console.error(` link failed: ${i instanceof Error?i.message:String(i)}`),await T(r,{errorsCommand:`sootsim get errors 5${s}`,warningsCommand:`sootsim get warnings 5${s}`,requestsCommand:`sootsim get requests 5${s}`}),process.exit(1)}finally{r.close()}}async function yo(e,t={}){let o=$(e,{port:t.port,commandTimeoutMs:t.timeoutMs,stripBooleanFlags:["--new","--headless","--headed","--ephemeral","--hot","--no-hmr","--no-describe"],stripValueFlags:["--base-url","--replace","--driver","--profile","--cdp-port"]}),n=e.find((u,p)=>e[p-1]==="--profile"),r=e.includes("--ephemeral"),s=e.find((u,p)=>e[p-1]==="--cdp-port"),i=s?Number(s):void 0;s&&(!Number.isFinite(i)||i<=0)&&(console.error(` sootsim open: --cdp-port must be a positive port number, got "${s}"`),process.exit(1)),n&&r&&(console.error(" sootsim open: --profile cannot be combined with --ephemeral"),process.exit(1));let a=n?ve(n).id:void 0,l=!!a||r,c=e.includes("--new")||l,d=wt(e);c&&o.simIdSource==="flag"&&(console.error(" sootsim open: --new, --profile, and --ephemeral cannot be combined with --sim"),process.exit(1));let m=o.positional[0]||"";if(mt(m)){let u=(()=>{try{return/^(localhost|127\.0\.0\.1|\[::1\]|::1)$/i.test(new URL(m).hostname)}catch{return!1}})();console.log(" that\u2019s a SootSim preview/build link \u2014 a full player page, not a driveable sim."),console.log(" opening it in your browser for viewing\u2026");try{await ee(m,{background:!1})}catch(p){console.error(` could not launch a browser: ${p instanceof Error?p.message:String(p)}`),console.error(` open it yourself: ${m}`),process.exit(1)}console.log(u?" to drive it under the CLI, point `sootsim open` at the app\u2019s dev port instead (e.g. `sootsim open 8081`).":" the CLI can\u2019t drive a remote preview (the engine only attaches to the local bridge from localhost). to drive the app, run it locally and `sootsim open <port>`.");return}o.wsPort=await lt(o.wsPort);let g=e.find((u,p)=>e[p-1]==="--driver")||"",q=rt(e,g);i&&g!=="playwright"&&(console.error(" sootsim open: --cdp-port is only honored by the playwright driver \u2014 add `--driver playwright`"),process.exit(1));let ae=e.includes("--hot"),le=e.includes("--no-hmr");ae&&le&&(console.error(" sootsim open: --hot and --no-hmr cannot be combined"),process.exit(1));let B={hot:ae?!0:le?!1:g!=="playwright"},P=e.find((u,p)=>e[p-1]==="--base-url")||Fe(),ce=e.includes("--base-url"),de=E();if(!g&&!c&&(o.simIdSource==="flag"||o.simIdSource==="saved"&&!!de)){let u=x(o),p=o.simId?` --sim ${o.simId}`:"",f=!1;try{let h=null;try{let w=await u.listSims();if(o.simIdSource==="saved"?(h=w.find(I=>I.id===de&&I.readyState==="open")??null,h||M()):h=z(w,o.simId),!h)if(o.simIdSource==="saved")f=!0;else throw new Error("no sim connected");if(!f&&h){let I=ce||re(m)?P:De(h,P),A=await W(m,F(I,d),B);u.send({type:"evaluate",simId:h.id,code:`window.location.href = ${JSON.stringify(A)}`}).catch(()=>{})}}catch(w){console.error(` open failed: ${w instanceof Error?w.message:String(w)}`),await T(u,{errorsCommand:`sootsim get errors 5${p}`,warningsCommand:`sootsim get warnings 5${p}`,requestsCommand:`sootsim get requests 5${p}`}),process.exit(1)}if(!f&&h){await v(1500);let w=await It(o.wsPort,o.commandTimeoutMs,h.id);w||(console.error(" timed out waiting for current sim to load target"),process.exit(1)),w.bridge.close(),b(h.id);let I=ce||re(m)?P:De(h,P),A=await W(m,F(I,d),B);U(A,{...h,url:A},"current sim"),await R(o.wsPort,h.id,e);return}}finally{u.close()}}let me=F(P,d),Ke=await W(m,me,B),ue=`cli-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`,S=await xt(m,me,ue,B),G={newWindow:!0},D=u=>u.url?.includes(`inspectOpen=${ue}`)??!1;if(g){nt(g)||(console.error(` unknown driver "${g}" \u2014 run \`sootsim list --drivers\``),process.exit(1));let u=te(g);u||(console.error(` unknown driver "${g}" \u2014 run \`sootsim list --drivers\``),process.exit(1));let p=ot(),f=await u.launch({url:S,headless:q,newWindow:G.newWindow,profileId:a,ephemeralProfile:r,cdpPort:i,connectTimeoutMs:p.hostTimeoutMs});f.launched||(console.error(` ${u.name} driver: ${f.message}`),process.exit(1));let h=await Mt(o.wsPort,o.commandTimeoutMs,D,f.pid,{attempts:p.attempts,intervalMs:p.intervalMs});if(h.kind==="host-exit"&&(Ae(f),console.error(` ${u.name} host crashed \u2014 sim is no longer attached`),process.exit(1)),h.kind==="timeout"){if(f.pid)try{process.kill(f.pid,"SIGTERM"),console.error(` closed ${u.name} host process ${f.pid}`)}catch{}Ae(f),console.error(` timed out after ${p.timeoutMs}ms waiting for opened sim to connect`),process.exit(1)}let w=h.sim;b(w.id),Ut(f),U(S,w,`${u.name} driver`),await R(o.wsPort,w.id,e);return}if(l){let u=O();u||(console.error(" profiles require electron or playwright; install the desktop companion or use `--driver playwright`"),process.exit(1));try{(await X(S,u,{profileId:a,ephemeralProfile:r})).launched||(console.error(" desktop companion failed to start"),process.exit(1))}catch(f){console.error(` desktop companion failed: ${f instanceof Error?f.message:String(f)}`),process.exit(1)}let p=await oe(o.wsPort,o.commandTimeoutMs,D,{attempts:40,intervalMs:500});p||(console.error(" timed out waiting for profiled sim to connect"),process.exit(1)),b(p.id),U(S,p,"desktop companion"),await R(o.wsPort,p.id,e);return}let V=!1,J=!1;try{let u=y(o.wsPort,{commandTimeoutMs:2e3});try{await u.listSims(),V=!0}finally{u.close()}}catch{}if(V)try{let u=y(o.wsPort,{commandTimeoutMs:3e3});try{await u.openUrl(S,G),J=!0}finally{u.close()}}catch{}if(!J){let u=O();if(u)try{if((await X(S,u)).launched){let f=await oe(o.wsPort,o.commandTimeoutMs,D,{attempts:20,intervalMs:500});if(f){b(f.id),U(S,f,"desktop companion"),await R(o.wsPort,f.id,e);return}}}catch{}try{await ee(S,G)}catch(p){console.error(` open failed: ${p instanceof Error?p.message:String(p)}`),te("playwright")?.availability().available?console.error(" no system browser found \u2014 retry with `sootsim open \u2026 --driver playwright` for a headless sim."):console.error(" run `sootsim list --drivers` to see available drivers."),process.exit(1)}if(!V){console.log(` opened: ${Ke}`),Oe(o.wsPort);return}}let L=await oe(o.wsPort,o.commandTimeoutMs,D,{attempts:30,intervalMs:500});L||(console.error(" timed out waiting for opened sim to connect"),process.exit(1)),b(L.id),U(S,L,J?"bridge":"direct shell open"),await R(o.wsPort,L.id,e)}async function He(e,t={}){let o=$(e,{port:t.port,commandTimeoutMs:t.timeoutMs,stripBooleanFlags:["--force"]}),n=e.includes("--force"),r=x(o),s=o.simId?` --sim ${o.simId}`:"";try{try{let i=await r.listSims(),a=z(i,o.positional[0]||o.simId);if(a.lockedBy&&a.lockExpiresAt&&a.lockExpiresAt>Date.now()){let l=Math.max(0,Math.round((a.lockExpiresAt-Date.now())/1e3));a.lockedByKind==="user-active"&&(console.error(` refused: ${a.id} is locked by the active user (${l}s) \u2014 that's a live human tab`),console.error(" run `sootsim open --new` for a fresh investigation sim"),process.exit(1)),n||(console.error(` refused: ${a.id} is leased by ${a.lockedBy} (${l}s)`),console.error(` \`sootsim use ${a.id} --force\` to take it, or \`sootsim open --new\` for a fresh sim`),process.exit(1))}await r.focusSim(a.id),b(a.id),console.log(` using: ${a.id}`)}catch(i){console.error(` use failed: ${i instanceof Error?i.message:String(i)}`),await T(r,{errorsCommand:`sootsim get errors 5${s}`,warningsCommand:`sootsim get warnings 5${s}`,requestsCommand:`sootsim get requests 5${s}`}),process.exit(1)}}finally{r.close()}}async function So(e,t={}){await He(e,t)}async function vo(e,t={}){await He(e,t)}async function $o(e,t={}){let o=$(e,{port:t.port,commandTimeoutMs:t.timeoutMs,stripBooleanFlags:["--force"]}),n=e.includes("--force"),r=x(o);try{try{let s=await r.listSims(),i=z(s,o.positional[0]||o.simId),a=n&&i.lockedBy&&i.lockedByKind!=="user-active"?i.lockedBy:null,l=await r.claim(i.id,{force:n});b(i.id);let c=Math.max(0,Math.round((l.lockExpiresAt-Date.now())/1e3)),d=l.bootedCount>0?` (booted ${l.bootedCount})`:"";console.log(` claimed: ${l.simId} [${c}s]${d}`),a&&console.log(` took over from: ${a}`)}catch(s){if(s instanceof be){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 sim"),process.exit(1)}console.error(` claim failed: ${s instanceof Error?s.message:String(s)}`),process.exit(1)}}finally{r.close()}}async function Bt(e,t,o,n){if(n.length===0)return{closed:[],remaining:[]};await Promise.all(n.map(s=>e.closeSim(s).catch(()=>{})));let r=new Set(n);for(let s=0;s<40;s++){let i=[];try{let a=y(t,{commandTimeoutMs:o});try{i=(await a.listSims()).filter(c=>r.has(c.id)&&c.readyState==="open").map(c=>c.id)}finally{a.close()}}catch{i=[]}if(i.length===0)return{closed:[...r],remaining:[]};if(s===39)return{closed:[...r].filter(a=>!i.includes(a)),remaining:i};await v(250)}return{closed:[...r],remaining:[]}}function Dt(e,t){let o=e.filter(m=>m.readyState==="open"),n=o.map(m=>m.id),r=t.explicitKeepId?.trim()||"",s=t.savedKeepId?.trim()||"",i=s?o.some(m=>m.id===s):!1,a=s&&!i?s:null,l=null,c=null,d=null;if(t.closeOthers)if(r){let m=o.find(g=>g.id===r);m?l=m.id:c=r}else if(s&&i)l=s;else{let m=o.find(g=>g.isPrimary)??o[0]??null;m&&(l=m.id,s&&!i&&(d=m.id))}return{openIds:n,keepId:l,targets:c?[]:n.filter(m=>m!==l),staleSavedId:a,missingExplicitKeepId:c,fallbackKeepId:d}}async function xo(e,t={}){let o=$(e,{port:t.port,commandTimeoutMs:t.timeoutMs,stripBooleanFlags:["--all","--others"]}),n=x(o),r=o.simId?` --sim ${o.simId}`:"",s=e.includes("--all"),i=e.includes("--others");if(s||i){try{let a=await n.listSims(),l=E(),c=o.positional[0]||(o.simIdSource==="flag"?o.simId:void 0),d=Dt(a,{closeOthers:i,explicitKeepId:c,savedKeepId:l});if(d.staleSavedId&&M(),d.fallbackKeepId&&(b(d.fallbackKeepId),console.log(` saved sim ${d.staleSavedId} is gone \u2014 keeping primary ${d.fallbackKeepId}`)),d.missingExplicitKeepId&&(console.error(` close failed: keep sim ${d.missingExplicitKeepId} is not connected; not closing other sims`),process.exit(1)),d.targets.length===0){console.log(d.keepId?` nothing to close \u2014 only the kept sim ${d.keepId} is connected`:d.staleSavedId?` nothing to close \u2014 no sims connected (cleared stale current sim ${d.staleSavedId})`:" nothing to close \u2014 no sims connected");return}let m=await Bt(n,o.wsPort,o.commandTimeoutMs,d.targets);await ne(a,d.targets);let g=E();g&&m.closed.includes(g)&&(d.keepId?b(d.keepId):M());let q=` closed ${m.closed.length} sim(s)${d.keepId?` (kept ${d.keepId})`:""}`;console.log(q),m.remaining.length>0&&(console.error(` close failed: still connected: ${m.remaining.join(", ")}`),process.exit(1))}catch(a){console.error(` close failed: ${a instanceof Error?a.message:String(a)}`),process.exit(1)}finally{n.close()}return}try{try{let a=await n.listSims(),l=z(a,o.positional[0]||o.simId),c=a.find(m=>m.id!==l.id&&m.readyState==="open");await n.closeSim(l.id),await Tt(o.wsPort,o.commandTimeoutMs,l.id)||(await ne(a,[l.id]),console.error(` close failed: ${l.id} is still connected`),process.exit(1)),await ne(a,[l.id]),E()===l.id&&(c?b(c.id):M()),console.log(` closed: ${l.id}`)}catch(a){console.error(` close failed: ${a instanceof Error?a.message:String(a)}`),await T(n,{errorsCommand:`sootsim get errors 5${r}`,warningsCommand:`sootsim get warnings 5${r}`,requestsCommand:`sootsim get requests 5${r}`}),process.exit(1)}}finally{n.close()}}export{jt as a,Qe as b,zt as c,ot as d,it as e,ho as f,Fe as g,W as h,xt as i,kt as j,Ct as k,oe as l,Mt as m,Ot as n,Ut as o,ne as p,wo as q,bo as r,yo as s,So as t,vo as u,$o as v,Bt as w,Dt as x,xo as y};
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.181 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as u,b as w}from"./chunk-TVAG4EDK.js";import{a as m,c as f}from"./chunk-MOLUWZMJ.js";import{a as d,b as g,c as v}from"./chunk-JXIJPGAA.js";import{c as p}from"./chunk-6OJJAJRM.js";function x(){let e=d();return e?{available:!0,reason:null,detail:e}:{available:!1,reason:"no chromium-family browser found (chrome, chromium, edge, brave, arc)"}}async function I(e){if(e.headless)return{launched:!1,message:"the chromium driver launches a visible browser window and cannot run headless \u2014 a headless `chrome --headless=new` process exits immediately and the sim never connects. for headless automation use `--driver playwright`."};let r=d();if(!r)return{launched:!1,message:"chromium not found \u2014 install chrome or run `sootsim list --drivers`"};if(!e.url)return{launched:!1,message:"chromium driver requires a target url"};if(e.profileId||e.ephemeralProfile)return{launched:!1,message:"profiles require electron or playwright; browser tabs use your current Chrome profile"};try{let t=await v(e.url,{detached:e.detached??!0});return{launched:!0,message:`chromium launched \u2192 ${e.url}`,pid:t.pid,target:t.target??r,attachUrl:e.url}}catch(t){return{launched:!1,message:`chromium spawn failed: ${t instanceof Error?t.message:String(t)}`}}}var y={id:"chromium",name:"chromium",description:"system chrome / chromium / edge \u2014 visible window (headless: use playwright)",kind:"browser",availability:x,launch:I};function _(){let e=u();return e?{available:!0,reason:null,detail:`${e.kind} @ ${e.path}`}:{available:!1,reason:"sootsim desktop app not installed (run `sootsim install-desktop`)"}}async function M(e){let r=u();if(!r)return{launched:!1,message:"sootsim desktop app not installed"};try{let t=await w(e.url,r,{device:e.device,profileId:e.profileId,ephemeralProfile:e.ephemeralProfile});return t.launched?{launched:!0,message:e.url?`electron launched via ${t.via} \u2192 ${e.url}`:`electron launched via ${t.via}`,target:t.target,attachUrl:e.url}:{launched:!1,message:"desktop companion failed to start"}}catch(t){return{launched:!1,message:`electron launch failed: ${t instanceof Error?t.message:String(t)}`}}}var b={id:"electron",name:"electron",description:"sootsim desktop companion app (native window, menu bar)",kind:"native",availability:_,launch:M};import{spawn as A}from"child_process";import{closeSync as C,mkdtempSync as L,openSync as E,readFileSync as W}from"fs";import{createRequire as R}from"module";import{tmpdir as P}from"os";import{join as S}from"path";var N=`
1
+ /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{a as u,b as w}from"./chunk-WN6XO6JV.js";import{a as m,c as f}from"./chunk-PY4P54SD.js";import{a as d,b as g,c as v}from"./chunk-JR7LKXPB.js";import{c as p}from"./chunk-QP4W3SGO.js";function x(){let e=d();return e?{available:!0,reason:null,detail:e}:{available:!1,reason:"no chromium-family browser found (chrome, chromium, edge, brave, arc)"}}async function I(e){if(e.headless)return{launched:!1,message:"the chromium driver launches a visible browser window and cannot run headless \u2014 a headless `chrome --headless=new` process exits immediately and the sim never connects. for headless automation use `--driver playwright`."};let r=d();if(!r)return{launched:!1,message:"chromium not found \u2014 install chrome or run `sootsim list --drivers`"};if(!e.url)return{launched:!1,message:"chromium driver requires a target url"};if(e.profileId||e.ephemeralProfile)return{launched:!1,message:"profiles require electron or playwright; browser tabs use your current Chrome profile"};try{let t=await v(e.url,{detached:e.detached??!0});return{launched:!0,message:`chromium launched \u2192 ${e.url}`,pid:t.pid,target:t.target??r,attachUrl:e.url}}catch(t){return{launched:!1,message:`chromium spawn failed: ${t instanceof Error?t.message:String(t)}`}}}var y={id:"chromium",name:"chromium",description:"system chrome / chromium / edge \u2014 visible window (headless: use playwright)",kind:"browser",availability:x,launch:I};function _(){let e=u();return e?{available:!0,reason:null,detail:`${e.kind} @ ${e.path}`}:{available:!1,reason:"sootsim desktop app not installed (run `sootsim install-desktop`)"}}async function M(e){let r=u();if(!r)return{launched:!1,message:"sootsim desktop app not installed"};try{let t=await w(e.url,r,{device:e.device,profileId:e.profileId,ephemeralProfile:e.ephemeralProfile});return t.launched?{launched:!0,message:e.url?`electron launched via ${t.via} \u2192 ${e.url}`:`electron launched via ${t.via}`,target:t.target,attachUrl:e.url}:{launched:!1,message:"desktop companion failed to start"}}catch(t){return{launched:!1,message:`electron launch failed: ${t instanceof Error?t.message:String(t)}`}}}var b={id:"electron",name:"electron",description:"sootsim desktop companion app (native window, menu bar)",kind:"native",availability:_,launch:M};import{spawn as A}from"child_process";import{closeSync as C,mkdtempSync as L,openSync as E,readFileSync as W}from"fs";import{createRequire as R}from"module";import{tmpdir as P}from"os";import{join as S}from"path";var N=`
3
3
  const modulePath = process.env.SOOTSIM_PW_MODULE;
4
4
  const url = process.env.SOOTSIM_PW_URL;
5
5
  const headless = process.env.SOOTSIM_PW_HEADLESS === '1';