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.
- package/README.md +2 -0
- package/dist-cli/bin.js +3 -3
- package/dist-cli/chunks/{agent-RKWUNS7I.js → agent-AFAFZKMT.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-5EPZRSPB.js → agent-wrapper-MV5WGYCS.js} +2 -2
- package/dist-cli/chunks/{app-fonts-EU7F6Z3S.js → app-fonts-ERD5J644.js} +2 -2
- package/dist-cli/chunks/{assert-RKFBXF3E.js → assert-IBMYHSKD.js} +2 -2
- package/dist-cli/chunks/beta-T7Z7WSTK.js +2 -0
- package/dist-cli/chunks/{camera-TD6M3UW4.js → camera-Y6XUIEAP.js} +2 -2
- package/dist-cli/chunks/{chunk-DFOAKJLV.js → chunk-3JW7SSYR.js} +2 -2
- package/dist-cli/chunks/{chunk-FSWR7SL3.js → chunk-4FVYQIRB.js} +1 -1
- package/dist-cli/chunks/{chunk-QRJWVT3L.js → chunk-4HJKPRWO.js} +30 -30
- package/dist-cli/chunks/{chunk-PHPGJPFL.js → chunk-64EKAMA3.js} +2 -2
- package/dist-cli/chunks/{chunk-V53IQMHL.js → chunk-6GCEDCO5.js} +2 -2
- package/dist-cli/chunks/{chunk-VBHBFJZ3.js → chunk-6TNJLDAQ.js} +1 -1
- package/dist-cli/chunks/chunk-77GTIGLV.js +1 -0
- package/dist-cli/chunks/{chunk-TF24QNTS.js → chunk-7SP7PFUR.js} +1 -1
- package/dist-cli/chunks/{chunk-X25HNIMN.js → chunk-AP22KA3Q.js} +1 -1
- package/dist-cli/chunks/chunk-B3W63SZA.js +13 -0
- package/dist-cli/chunks/{chunk-Y6PKI2Q2.js → chunk-BTIARV2B.js} +2 -2
- package/dist-cli/chunks/{chunk-JFDRB2AQ.js → chunk-BYKEWDZO.js} +2 -2
- package/dist-cli/chunks/{chunk-IE7FPLMT.js → chunk-C7BIPAXT.js} +1 -1
- package/dist-cli/chunks/{chunk-WY2T6Z4D.js → chunk-CD3KUHS3.js} +2 -2
- package/dist-cli/chunks/{chunk-WAC236L6.js → chunk-CPZNH3B3.js} +2 -2
- package/dist-cli/chunks/chunk-D7ZB6A5T.js +2 -0
- package/dist-cli/chunks/{chunk-TVUKWQKF.js → chunk-F6DIP2WH.js} +2 -2
- package/dist-cli/chunks/{chunk-RYOEWY42.js → chunk-FO3RPGBR.js} +2 -2
- package/dist-cli/chunks/{chunk-7IY6HRHX.js → chunk-FODJBAWY.js} +1 -1
- package/dist-cli/chunks/{chunk-N5BWYBPY.js → chunk-HW65ZTXM.js} +2 -2
- package/dist-cli/chunks/{chunk-IGSJWOD3.js → chunk-IJMDXS7N.js} +1 -1
- package/dist-cli/chunks/{chunk-JXIJPGAA.js → chunk-JR7LKXPB.js} +1 -1
- package/dist-cli/chunks/{chunk-VPPTIGEA.js → chunk-KSV6H2IC.js} +1 -1
- package/dist-cli/chunks/{chunk-UQYVIUVV.js → chunk-L3FPLPA4.js} +2 -2
- package/dist-cli/chunks/chunk-MBR6TG2P.js +118 -0
- package/dist-cli/chunks/{chunk-3DX6C7P7.js → chunk-N2EXJ7HK.js} +2 -2
- package/dist-cli/chunks/chunk-NLI4YQOY.js +1 -0
- package/dist-cli/chunks/{chunk-5KF6ZRPL.js → chunk-OIFGVAV2.js} +1 -1
- package/dist-cli/chunks/{chunk-MOLUWZMJ.js → chunk-PY4P54SD.js} +2 -2
- package/dist-cli/chunks/{chunk-7WGUVK3H.js → chunk-QF6RJQ2U.js} +2 -2
- package/dist-cli/chunks/{chunk-6OJJAJRM.js → chunk-QP4W3SGO.js} +1 -1
- package/dist-cli/chunks/{chunk-ELFPYLAD.js → chunk-QQDPEM7F.js} +2 -2
- package/dist-cli/chunks/{chunk-WJ7YLEHP.js → chunk-RAEIXWER.js} +2 -2
- package/dist-cli/chunks/{chunk-VYFQBXF5.js → chunk-RC3FKTYP.js} +2 -2
- package/dist-cli/chunks/{chunk-NXOSVZ5V.js → chunk-RQDJPT6N.js} +2 -2
- package/dist-cli/chunks/{chunk-EXA5KQMJ.js → chunk-RQXZANGL.js} +1 -1
- package/dist-cli/chunks/{chunk-VBQD7ZVI.js → chunk-SGWTX6CO.js} +2 -2
- package/dist-cli/chunks/{chunk-DXXFRQDT.js → chunk-UXHEGFHQ.js} +1 -1
- package/dist-cli/chunks/{chunk-KW2G6CYT.js → chunk-VSDVOOWG.js} +2 -2
- package/dist-cli/chunks/{chunk-XCQ5SHW4.js → chunk-W3G5M4UB.js} +3 -3
- package/dist-cli/chunks/{chunk-TVAG4EDK.js → chunk-WN6XO6JV.js} +1 -1
- package/dist-cli/chunks/{chunk-ANIFL7F2.js → chunk-WOOWTKM7.js} +1 -1
- package/dist-cli/chunks/{chunk-VNE4KDKB.js → chunk-WV7QBUFY.js} +2 -2
- package/dist-cli/chunks/{chunk-HIDL7MR4.js → chunk-XGVVYY6P.js} +2 -2
- package/dist-cli/chunks/{chunk-4GUWBLYM.js → chunk-YSTCZI6L.js} +1 -1
- package/dist-cli/chunks/cli-version-P4SZNBPQ.js +2 -0
- package/dist-cli/chunks/{compat-2LW53L3X.js → compat-ZTFIQOVI.js} +3 -3
- package/dist-cli/chunks/control-XWU7LZWC.js +2 -0
- package/dist-cli/chunks/{cpu-profile-ILWMOPGM.js → cpu-profile-3F2OQNCM.js} +2 -2
- package/dist-cli/chunks/daemon-CUQB4QAP.js +2 -0
- package/dist-cli/chunks/{debug-B3YI66U3.js → debug-7MIHREPG.js} +3 -3
- package/dist-cli/chunks/{detox-P4ZT3VE5.js → detox-CDYD54PS.js} +2 -2
- package/dist-cli/chunks/{device-L2NDHRYR.js → device-H53IDQ67.js} +2 -2
- package/dist-cli/chunks/{diagnose-A4HWLBVV.js → diagnose-YWSZCFPG.js} +2 -2
- package/dist-cli/chunks/drivers-RRCIIDGS.js +2 -0
- package/dist-cli/chunks/{electron-RO3TPVY6.js → electron-UPH35PTF.js} +3 -3
- package/dist-cli/chunks/flow-ZDJI7ICC.js +2 -0
- package/dist-cli/chunks/help-U5QGSMDR.js +2 -0
- package/dist-cli/chunks/{hints-G7S3VDTD.js → hints-BYH6BMHP.js} +2 -2
- package/dist-cli/chunks/{home-paths-OE3OK3DY.js → home-paths-AXKOJS62.js} +2 -2
- package/dist-cli/chunks/{inspect-WXM6H76N.js → inspect-TGQU6MZQ.js} +3 -3
- package/dist-cli/chunks/{install-desktop-IMVDXK6B.js → install-desktop-AOJACCWA.js} +3 -3
- package/dist-cli/chunks/{keys-UKEWXNIR.js → keys-RC6SP7XY.js} +2 -2
- package/dist-cli/chunks/{login-JMJIS5DW.js → login-EQDNEJFU.js} +4 -4
- package/dist-cli/chunks/{logout-G3BGSQEH.js → logout-QEDUWSSY.js} +2 -2
- package/dist-cli/chunks/{maestro-QZ5TJPFV.js → maestro-6SVETVRL.js} +2 -2
- package/dist-cli/chunks/optional-demo-registry-KXYBVP4S.js +2 -0
- package/dist-cli/chunks/{preview-7QFRBTZB.js → preview-UNXWRROE.js} +2 -2
- package/dist-cli/chunks/{profile-MEH7CCAS.js → profile-UBHQS25I.js} +2 -2
- package/dist-cli/chunks/{react-LXBEY3LK.js → react-THVP5NZX.js} +2 -2
- package/dist-cli/chunks/{record-MRCRQ6YX.js → record-SOACBRM3.js} +2 -2
- package/dist-cli/chunks/runtime-BAS2IZFB.js +2 -0
- package/dist-cli/chunks/{screenshot-ML2KX4BZ.js → screenshot-MJKWIVV6.js} +2 -2
- package/dist-cli/chunks/{screenshot-mode-RAZUOTEN.js → screenshot-mode-RP67FQSA.js} +2 -2
- package/dist-cli/chunks/{screenshots-BZGYDJXS.js → screenshots-LWDOE4RD.js} +2 -2
- package/dist-cli/chunks/{screenshots-capture-XNC3UM3X.js → screenshots-capture-RUXUL5GO.js} +2 -2
- package/dist-cli/chunks/{serve-XWSFQLD7.js → serve-TMMMRVTI.js} +2 -2
- package/dist-cli/chunks/{setup-IDWFS4I4.js → setup-WYKWQPJW.js} +2 -2
- package/dist-cli/chunks/{skills-ROKHF5AD.js → skills-P3HEQGPB.js} +2 -2
- package/dist-cli/chunks/{slides-CWXBPPJD.js → slides-MICILFXO.js} +2 -2
- package/dist-cli/chunks/store-7CIU7X6W.js +2 -0
- package/dist-cli/chunks/{telemetry-WXD6NMEZ.js → telemetry-7ZVCPD7R.js} +2 -2
- package/dist-cli/chunks/telemetry-LVBDBPPV.js +2 -0
- package/dist-cli/chunks/{test-ZAEKQ7VB.js → test-NT7SGPG5.js} +3 -3
- package/dist-cli/chunks/{three-mode-EGZZJ65O.js → three-mode-6ADYURNM.js} +2 -2
- package/dist-cli/chunks/{timeline-EBZ2SDVV.js → timeline-JZNPRKEH.js} +2 -2
- package/dist-cli/chunks/{upgrade-MZRMKVVS.js → upgrade-K6V3HYCU.js} +2 -2
- package/dist-cli/chunks/upload-Q2ZFKOSR.js +2 -0
- package/dist-cli/chunks/{version-CMX4HPI4.js → version-GS2J6SFA.js} +2 -2
- package/dist-cli/chunks/{web-R7FNAVBN.js → web-J2XC4OK4.js} +2 -2
- package/dist-cli/chunks/{what-happened-DUQVN5IX.js → what-happened-O5MCMYJL.js} +2 -2
- package/dist-cli/chunks/{whoami-HJIRUPJA.js → whoami-Z2Y2X2DN.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +1 -1
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +1 -1
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +29 -7
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/skills/soot.md +3 -0
- package/dist-cli/chunks/beta-VXVNIBH6.js +0 -2
- package/dist-cli/chunks/chunk-455VGZ2C.js +0 -118
- package/dist-cli/chunks/chunk-7VFWDDKF.js +0 -1
- package/dist-cli/chunks/chunk-DG25DVQA.js +0 -1
- package/dist-cli/chunks/chunk-MBWEZB7G.js +0 -2
- package/dist-cli/chunks/chunk-SFI66XFY.js +0 -13
- package/dist-cli/chunks/cli-version-VH6P4API.js +0 -2
- package/dist-cli/chunks/control-TPJXIRFC.js +0 -2
- package/dist-cli/chunks/daemon-BE42RCVB.js +0 -2
- package/dist-cli/chunks/drivers-PZZCFUEV.js +0 -2
- package/dist-cli/chunks/flow-XB73D57F.js +0 -2
- package/dist-cli/chunks/help-S4Z6P3KT.js +0 -2
- package/dist-cli/chunks/optional-demo-registry-QZ3N5NTN.js +0 -2
- package/dist-cli/chunks/runtime-BQEZQUBD.js +0 -2
- package/dist-cli/chunks/store-FK6E5CQO.js +0 -2
- package/dist-cli/chunks/telemetry-CBM2UIAG.js +0 -2
- package/dist-cli/chunks/upload-XQH6ZVU6.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{a as Be}from"./chunk-
|
|
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
|
|
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[
|
|
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
|
|
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,
|
|
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}, ${
|
|
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, ${
|
|
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
|
|
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
|
|
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
|
-
})()`);
|
|
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
|
-
})()`);
|
|
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&&
|
|
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}, ${
|
|
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,
|
|
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){
|
|
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,
|
|
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),
|
|
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 | ${
|
|
445
|
-
`),w&&
|
|
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,
|
|
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),
|
|
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(
|
|
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",
|
|
458
|
-
preparing preview upload\u2026`);let{runUpload:y}=await import("./upload-
|
|
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
|
-
`:""),
|
|
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}`),
|
|
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
|
|
463
|
-
worst frames:`),i.mode==="render-worker"){console.log(" total layout render copy t+");for(let
|
|
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.
|
|
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-
|
|
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.
|
|
2
|
-
import{a as M,b as U}from"./chunk-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
2
|
-
import{a as u,b as w}from"./chunk-
|
|
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';
|