sootsim 0.1.260 → 0.1.261
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/detox/jest-preset.cjs +1 -0
- package/detox/jest-setup-after-env.cjs +18 -0
- package/dist-cli/bin.js +4 -4
- package/dist-cli/chunks/{agent-QEM7DWI7.js → agent-RXKGHJYJ.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-YARBZO5R.js → agent-wrapper-A2CMG2BA.js} +2 -2
- package/dist-cli/chunks/{app-fonts-67B5QIJ2.js → app-fonts-JMZWYM5K.js} +2 -2
- package/dist-cli/chunks/{assert-WDAU7DKC.js → assert-EOJ3EHJQ.js} +2 -2
- package/dist-cli/chunks/{auth-QZ2WHSMO.js → auth-SIJSL6ZW.js} +2 -2
- package/dist-cli/chunks/beta-BCHJBPJM.js +2 -0
- package/dist-cli/chunks/{camera-FMLGCTHM.js → camera-BKZSMPSA.js} +2 -2
- package/dist-cli/chunks/chunk-2XGPQPBQ.js +1 -0
- package/dist-cli/chunks/{chunk-WXXDXVMX.js → chunk-6ZERINEJ.js} +2 -2
- package/dist-cli/chunks/{chunk-FUX47ANO.js → chunk-75TC4YBZ.js} +2 -2
- package/dist-cli/chunks/{chunk-GWJV5HUP.js → chunk-7EIZLBLT.js} +1 -1
- package/dist-cli/chunks/chunk-AXJTPAHA.js +2 -0
- package/dist-cli/chunks/{chunk-3HOUA4LS.js → chunk-BARVOECN.js} +3 -3
- package/dist-cli/chunks/{chunk-UARIKSS5.js → chunk-BM5GBB56.js} +1 -1
- package/dist-cli/chunks/{chunk-HKH3LIXK.js → chunk-DKDYYXRY.js} +2 -2
- package/dist-cli/chunks/{chunk-34KMCHTZ.js → chunk-GIVBLMYH.js} +2 -2
- package/dist-cli/chunks/{chunk-LWSWST7V.js → chunk-HK3WEUHS.js} +2 -2
- package/dist-cli/chunks/{chunk-SI46S2OK.js → chunk-HZHQMMJ3.js} +1 -1
- package/dist-cli/chunks/{chunk-4XZN3SAE.js → chunk-ILTQ4ZPZ.js} +3 -3
- package/dist-cli/chunks/{chunk-EKRF2M3X.js → chunk-JHJYM5IX.js} +1 -1
- package/dist-cli/chunks/chunk-JNHCJEUS.js +2 -0
- package/dist-cli/chunks/{chunk-XV2DDWNN.js → chunk-K2DYN4E4.js} +3 -3
- package/dist-cli/chunks/{chunk-JTV3HJ2N.js → chunk-KNRPSHXD.js} +1 -1
- package/dist-cli/chunks/{chunk-F3H6TLYO.js → chunk-LFT2G6CS.js} +1 -1
- package/dist-cli/chunks/{chunk-OQEYYNJR.js → chunk-MAKP6XWE.js} +1 -1
- package/dist-cli/chunks/{chunk-DHDJMWF6.js → chunk-MLKQ6UCQ.js} +2 -2
- package/dist-cli/chunks/{chunk-CN7QV3LE.js → chunk-NC3JIEPR.js} +2 -2
- package/dist-cli/chunks/{chunk-X3W2PIEI.js → chunk-NE6RIOHA.js} +2 -2
- package/dist-cli/chunks/{chunk-DOJIA6WT.js → chunk-NER5BICF.js} +2 -2
- package/dist-cli/chunks/{chunk-C6Y5VVCB.js → chunk-NIW6FA7Z.js} +1 -1
- package/dist-cli/chunks/{chunk-XSDJS5XS.js → chunk-NPFX6EB3.js} +1 -1
- package/dist-cli/chunks/{chunk-2QV6LLGL.js → chunk-NRPETN6L.js} +2 -2
- package/dist-cli/chunks/{chunk-OIUV3IH7.js → chunk-OILZ3XOV.js} +2 -2
- package/dist-cli/chunks/{chunk-LVXU7Y7I.js → chunk-PQMCTB2S.js} +2 -2
- package/dist-cli/chunks/{chunk-P63Y6VSA.js → chunk-QHFMMVZS.js} +1 -1
- package/dist-cli/chunks/{chunk-3KI6AK35.js → chunk-QKU7E35D.js} +1 -1
- package/dist-cli/chunks/{chunk-QCAW6OLY.js → chunk-QM6C26WM.js} +3 -3
- package/dist-cli/chunks/{chunk-NNSKMXH2.js → chunk-QP7HQHRB.js} +2 -2
- package/dist-cli/chunks/{chunk-VYAAN4PM.js → chunk-QS4WA56I.js} +2 -2
- package/dist-cli/chunks/{chunk-NBXXWFSZ.js → chunk-R5AUBYLC.js} +1 -1
- package/dist-cli/chunks/chunk-RLPH773J.js +1 -0
- package/dist-cli/chunks/{chunk-HJKPM4ED.js → chunk-RZYPYVAE.js} +2 -2
- package/dist-cli/chunks/{chunk-EEFBNJMK.js → chunk-SYJEB5TC.js} +2 -2
- package/dist-cli/chunks/{chunk-OAMPRGSQ.js → chunk-UH722XMQ.js} +3 -3
- package/dist-cli/chunks/{chunk-ZAIZHZ6T.js → chunk-UPXPN2SA.js} +2 -2
- package/dist-cli/chunks/{chunk-ZEMU7CCR.js → chunk-V5JUP7E5.js} +2 -2
- package/dist-cli/chunks/{chunk-4HTKBGLM.js → chunk-WNCRDOVZ.js} +1 -1
- package/dist-cli/chunks/{chunk-UUDLRAVQ.js → chunk-WVA7UPF2.js} +1 -1
- package/dist-cli/chunks/{chunk-6MTZIGGD.js → chunk-XGZRIJRY.js} +25 -25
- package/dist-cli/chunks/{chunk-3ELOVAAF.js → chunk-ZUDTLJQC.js} +1 -1
- package/dist-cli/chunks/{chunk-GAZMBTOQ.js → chunk-ZUKJYGD4.js} +2 -2
- package/dist-cli/chunks/cli-version-QSLFZF4X.js +2 -0
- package/dist-cli/chunks/{compat-SXQ35F5Y.js → compat-GVAGEQAZ.js} +3 -3
- package/dist-cli/chunks/{config-OFPBHXF6.js → config-GMSFGZWY.js} +2 -2
- package/dist-cli/chunks/{control-QO7BWRB5.js → control-DUUEIJ4L.js} +2 -2
- package/dist-cli/chunks/daemon-7D67LUBQ.js +2 -0
- package/dist-cli/chunks/{debug-WI7OTMJ7.js → debug-IIBOAEZB.js} +3 -3
- package/dist-cli/chunks/{desktop-XG4DTYWV.js → desktop-MEMBM7BZ.js} +3 -3
- package/dist-cli/chunks/detox-J2ZIS7ZG.js +50 -0
- package/dist-cli/chunks/{device-RCPLGXOD.js → device-NEMPOW5X.js} +2 -2
- package/dist-cli/chunks/{diagnose-25WWGC7H.js → diagnose-XLD6UYAA.js} +2 -2
- package/dist-cli/chunks/drivers-QUSXEJ5B.js +2 -0
- package/dist-cli/chunks/{film-5R2WD43D.js → film-PN6U3DXD.js} +3 -3
- package/dist-cli/chunks/flow-MDGQP7KY.js +2 -0
- package/dist-cli/chunks/help-ZBFQ4TRI.js +2 -0
- package/dist-cli/chunks/{hidden-runtime-alias-MNAWS4PN.js → hidden-runtime-alias-ASB2DGU3.js} +2 -2
- package/dist-cli/chunks/{home-paths-WTO4UWLQ.js → home-paths-FLQYZTZC.js} +2 -2
- package/dist-cli/chunks/inspect-GE7F3UD5.js +2 -0
- package/dist-cli/chunks/install-desktop-NJFBRUQK.js +2 -0
- package/dist-cli/chunks/{login-H5RLZETJ.js → login-ELTUXIGJ.js} +4 -4
- package/dist-cli/chunks/{logout-7DEOZCC3.js → logout-JUHMBKCF.js} +2 -2
- package/dist-cli/chunks/{maestro-NQ6Q6MEN.js → maestro-FX22V3QM.js} +3 -3
- package/dist-cli/chunks/maestro-generate-QALAHEDN.js +34 -0
- package/dist-cli/chunks/{mode-KSNYDLZX.js → mode-Z32XIGKC.js} +2 -2
- package/dist-cli/chunks/optional-demo-registry-P5LGBWMR.js +2 -0
- package/dist-cli/chunks/{perf-MHPMVPCV.js → perf-4W3HMHDG.js} +2 -2
- package/dist-cli/chunks/{record-5W4YHCHU.js → record-J23EU2PJ.js} +3 -3
- package/dist-cli/chunks/runtime-NUA23EAD.js +2 -0
- package/dist-cli/chunks/{screenshot-command-ZN5HJKXW.js → screenshot-command-AVQKBRJD.js} +5 -5
- package/dist-cli/chunks/{screenshots-capture-PLSFL6NK.js → screenshots-capture-LQ4WG4YD.js} +2 -2
- package/dist-cli/chunks/{serve-SKTRPCQ5.js → serve-VJW3DHXR.js} +2 -2
- package/dist-cli/chunks/{setup-SPC4R5OR.js → setup-DLZZ5GYH.js} +3 -3
- package/dist-cli/chunks/{skills-KBJFXVCE.js → skills-43K6ZI7K.js} +2 -2
- package/dist-cli/chunks/{storage-YT54JXP2.js → storage-6AFPJODY.js} +2 -2
- package/dist-cli/chunks/store-MNIZHXQW.js +2 -0
- package/dist-cli/chunks/telemetry-WK7QRXMO.js +2 -0
- package/dist-cli/chunks/{timeline-FLW3NIRM.js → timeline-4GTSVWUU.js} +2 -2
- package/dist-cli/chunks/{upgrade-GWSOM7YZ.js → upgrade-KHV5HMEC.js} +2 -2
- package/dist-cli/chunks/upload-56DF47MA.js +2 -0
- package/dist-cli/chunks/{version-AT5GGE4C.js → version-DZ3NNQYK.js} +2 -2
- package/dist-cli/chunks/{web-VUTEIWWM.js → web-OALK6L2K.js} +2 -2
- package/dist-cli/chunks/{what-happened-6RUMBWUV.js → what-happened-COLALBX2.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.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 +12 -9
- package/dist-lib/detox/jest-preset.cjs +1 -0
- 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 +20 -14
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/src/screenshots/frame-compose.ts +8 -5
- package/detox/jest-environment.ts +0 -86
- package/detox/run-test.ts +0 -113
- package/dist-cli/chunks/beta-6VNA237J.js +0 -2
- package/dist-cli/chunks/chunk-5FPVNQY4.js +0 -2
- package/dist-cli/chunks/chunk-HASL3RLT.js +0 -1
- package/dist-cli/chunks/chunk-JBPTFDQJ.js +0 -1
- package/dist-cli/chunks/chunk-TTQ7VHSJ.js +0 -2
- package/dist-cli/chunks/cli-version-VBYG2QF2.js +0 -2
- package/dist-cli/chunks/daemon-4LVN23GY.js +0 -2
- package/dist-cli/chunks/detox-PWG7VD4S.js +0 -50
- package/dist-cli/chunks/drivers-N7T7XYGQ.js +0 -2
- package/dist-cli/chunks/flow-VLH2N63S.js +0 -2
- package/dist-cli/chunks/help-OL3JKXM3.js +0 -2
- package/dist-cli/chunks/inspect-GKERSMOV.js +0 -2
- package/dist-cli/chunks/install-desktop-XNHP4BDI.js +0 -2
- package/dist-cli/chunks/maestro-generate-3KVTMPCE.js +0 -34
- package/dist-cli/chunks/optional-demo-registry-C7FB3S7R.js +0 -2
- package/dist-cli/chunks/runtime-CJCVTE7I.js +0 -2
- package/dist-cli/chunks/store-Z5JL3JTC.js +0 -2
- package/dist-cli/chunks/telemetry-7X4TQ3JD.js +0 -2
- package/dist-cli/chunks/upload-POEDJR7F.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{a as
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{a as st}from"./chunk-V5JUP7E5.js";import{a as it}from"./chunk-ILTQ4ZPZ.js";import{a as ge,c as we,d as be,e as tt,f as ye,g as Se,h as rt,i as ve,j as nt}from"./chunk-K2DYN4E4.js";import{a as et}from"./chunk-MLKQ6UCQ.js";import{e as ot}from"./chunk-PQMCTB2S.js";import{h as Ye,p as Ze,r as Xe,v as Qe}from"./chunk-NRPETN6L.js";import{f as je}from"./chunk-DKDYYXRY.js";import{a as qe}from"./chunk-JHJYM5IX.js";import{a as We}from"./chunk-QHFMMVZS.js";import{h as me,m as Ke}from"./chunk-6ZERINEJ.js";import{I as G,m as Ue,n as Ve,o as Je,p as Ge}from"./chunk-QKU7E35D.js";import{g as he,h as fe,i as ze}from"./chunk-GIVBLMYH.js";import{a as Be,b as ue}from"./chunk-HK3WEUHS.js";import{e as pe,f as He}from"./chunk-BARVOECN.js";import{a as Le,b as Ne,c as Ce}from"./chunk-AXJTPAHA.js";import*as P from"fs";import{createHash as zt,randomUUID as Kt}from"node:crypto";import{tmpdir as Y}from"os";import*as w from"path";import*as v from"fs";import*as S from"path";var Pt="inset 0 0 0 0.5px #000, inset 0 0 0 2px #757575, inset 0 0 0 5px #212121",lt=1;function kt(i){let e=Ce(i),t=e.scale,r=e.statusBarHeight>0&&!e.dynamicIsland&&e.homeIndicatorHeight===0&&e.cornerRadius===0,n=e.width,o=e.height,a=r?e.width+58:e.width+e.bezelWidth*2,s=r?e.height+111+111:e.height+e.bezelWidth*2,l=9.5,p=lt,u=a+l*2,m=s+lt*2,b=l+(r?29:e.bezelWidth),g=r?111:e.bezelWidth,I=Le(e),k=r?52:I.frameRadius,H=[{side:"right",top:e.hardwareButtons.lock.top,height:e.hardwareButtons.lock.height,width:e.hardwareButtons.width},{side:"left",top:e.hardwareButtons.ringToggle.top,height:e.hardwareButtons.ringToggle.height,width:e.hardwareButtons.width},{side:"left",top:e.hardwareButtons.volumeUp.top,height:e.hardwareButtons.volumeUp.height,width:e.hardwareButtons.width},{side:"left",top:e.hardwareButtons.volumeDown.top,height:e.hardwareButtons.volumeDown.height,width:e.hardwareButtons.width}];return{model:i,renderScale:t,outerWidth:Math.round(u*t),outerHeight:Math.round(m*t),logicalOuterWidth:u,logicalOuterHeight:m,logicalFrameWidth:a,logicalFrameHeight:s,logicalFrameLeft:l,logicalFrameTop:p,logicalScreenWidth:n,logicalScreenHeight:o,logicalScreenLeft:b,logicalScreenTop:g,logicalScreenRadius:r?0:I.screenRadius,logicalFrameRadius:k,frameBackground:r?"linear-gradient(180deg, #1b1c20 0%, #0f1012 42%, #050608 100%)":"#000000",frameOutline:r?"0 0 0 1px #1f2125":"0 0 0 1px #333",metallicRingShadow:r?null:Pt,buttons:H,legacyHomeButton:r?{top:111+e.height+47/2,left:e.width/2-64/2+29,size:64,ring:1.5}:null,showHomeIndicator:e.homeIndicatorHeight>0,logicalHomeIndicatorStripHeight:e.homeIndicatorHeight}}function At(i){let e=i.side==="right",t=e?"inset(-3px -3px -3px 0)":"inset(-3px 0 -3px -3px)";return`
|
|
3
3
|
<div
|
|
4
4
|
aria-hidden="true"
|
|
5
5
|
style="
|
|
@@ -24,7 +24,7 @@ import{a as ot}from"./chunk-ZEMU7CCR.js";import{a as nt}from"./chunk-4XZN3SAE.js
|
|
|
24
24
|
z-index:2;
|
|
25
25
|
"
|
|
26
26
|
></div>
|
|
27
|
-
`}function
|
|
27
|
+
`}function Mt(i,e){let t=i.buttons.map(At).join(""),r=i.metallicRingShadow?`
|
|
28
28
|
<div
|
|
29
29
|
aria-hidden="true"
|
|
30
30
|
style="
|
|
@@ -177,7 +177,7 @@ import{a as ot}from"./chunk-ZEMU7CCR.js";import{a as nt}from"./chunk-4XZN3SAE.js
|
|
|
177
177
|
</div>
|
|
178
178
|
</div>
|
|
179
179
|
</body>
|
|
180
|
-
</html>`}async function
|
|
180
|
+
</html>`}async function ct(i,e){let{chromium:t}=await import("playwright"),r=await it(n=>t.launch(n),{headless:!0});try{let n=await Rt(r);try{return await n.compose(i,e)}finally{await n.close()}}finally{await r.close()}}async function Rt(i){let e=new Map;async function t(r){let n=e.get(r);if(n)return n;let o=kt(r),a=await i.newContext({viewport:{width:o.outerWidth,height:o.outerHeight},deviceScaleFactor:1}),s=await a.newPage(),l={layout:o,context:a,page:s};return e.set(r,l),l}return{async compose(r,n){let o=await t(n),a=`data:image/png;base64,${r.toString("base64")}`;return await o.page.setContent(Mt(o.layout,a),{waitUntil:"load"}),await o.page.waitForTimeout(20),await o.page.screenshot({type:"png",clip:{x:0,y:0,width:o.layout.outerWidth,height:o.layout.outerHeight},omitBackground:!0})},async close(){for(let r of e.values())await r.context.close();e.clear()}}}import{spawnSync as _t}from"child_process";import*as N from"vm";var Ot=3e5,Dt=`
|
|
181
181
|
let input = '';
|
|
182
182
|
process.stdin.on('data', (c) => { input += c });
|
|
183
183
|
process.stdin.on('end', async () => {
|
|
@@ -199,7 +199,7 @@ process.stdin.on('end', async () => {
|
|
|
199
199
|
process.exitCode = 1;
|
|
200
200
|
}
|
|
201
201
|
});
|
|
202
|
-
`;function
|
|
202
|
+
`;function z(i,e,t){if(t?.multipartForm)throw new Error("http: multipartForm is not supported by sootsim yet");let r={...process.env};for(let s of Object.keys(r))(s.startsWith("BUN_INSPECT")||s==="NODE_OPTIONS")&&delete r[s];let n=_t(process.execPath,["-e",Dt],{input:JSON.stringify({url:i,method:e,headers:t?.headers,body:t?.body}),encoding:"utf8",timeout:Ot,maxBuffer:64*1024*1024,env:r});if(n.error)throw new Error(`http ${e} ${i} failed: ${n.error.message}`);let o;try{o=JSON.parse(n.stdout||"")}catch{throw new Error(`http ${e} ${i} failed: no response (${(n.stderr||"").trim().slice(0,200)})`)}let a=o;if(a.__error)throw new Error(`http ${e} ${i} failed: ${a.__error}`);return a}var Bt=/(?<!\\)\$\{([^$]*)\}/g,Lt=/\\(\$\{[^$]*\})/g,Nt=/^[A-Za-z_$][A-Za-z0-9_$]*$/,ee=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,o)=>z(n,"GET",o),post:(n,o)=>z(n,"POST",o),put:(n,o)=>z(n,"PUT",o),delete:(n,o)=>z(n,"DELETE",o),request:(n,o)=>z(n,(o?.method??"GET").toUpperCase(),o)},console:{log:(...n)=>t(n.map(o=>typeof o=="string"?o:dt(o)).join(" "))}};this.context=N.createContext(r),N.runInContext(`
|
|
203
203
|
globalThis.__maestroScope = new Proxy(globalThis, {
|
|
204
204
|
has() { return true },
|
|
205
205
|
get(target, key) { return target[key] },
|
|
@@ -210,7 +210,7 @@ process.stdin.on('end', async () => {
|
|
|
210
210
|
var yPercent = Math.ceil(y * 100) + '%'
|
|
211
211
|
return xPercent + ',' + yPercent
|
|
212
212
|
}
|
|
213
|
-
`,this.context);for(let[n,o]of Object.entries(process.env))o!==void 0&&this.envBinding.set(n,o)}putEnv(e,t){this.envBinding.set(e,t)}setCopiedText(e){this.maestro.copiedText=e}enterEnvScope(){this.envScopeStack.push(new Map(this.envBinding))}leaveEnvScope(){let e=this.envScopeStack.pop();if(e){this.envBinding.clear();for(let[t,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,o]of Object.entries(r))this.envBinding.set(n,o);return this.evalRaw(e)}finally{this.leaveEnvScope()}}return this.evalRaw(e)}evalRaw(e){this.syncEnvToContext();let t=e.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$\{/g,"\\${");return N.runInContext(`(function(){ with(__maestroScope){ return eval(\`${t}\`) } })()`,this.context)}syncEnvToContext(){let e=N.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(
|
|
213
|
+
`,this.context);for(let[n,o]of Object.entries(process.env))o!==void 0&&this.envBinding.set(n,o)}putEnv(e,t){this.envBinding.set(e,t)}setCopiedText(e){this.maestro.copiedText=e}enterEnvScope(){this.envScopeStack.push(new Map(this.envBinding))}leaveEnvScope(){let e=this.envScopeStack.pop();if(e){this.envBinding.clear();for(let[t,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,o]of Object.entries(r))this.envBinding.set(n,o);return this.evalRaw(e)}finally{this.leaveEnvScope()}}return this.evalRaw(e)}evalRaw(e){this.syncEnvToContext();let t=e.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$\{/g,"\\${");return N.runInContext(`(function(){ with(__maestroScope){ return eval(\`${t}\`) } })()`,this.context)}syncEnvToContext(){let e=N.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(Bt,(r,n)=>{if(n.trim().length===0)return"";let o=this.evaluate(n);if(o===void 0&&Nt.test(n.trim()))throw new Error(`missing variable for flow placeholder: ${n.trim()}`);return dt(o)}).replace(Lt,(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 dt(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 ut(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 te=1e4,Ct=Math.min(10,Math.max(1,Number(process.env.SOOTSIM_FLOW_TIMEOUT_SCALE)||1)),K=i=>Math.round(i*Ct),ne=393,C=852;function f(i){return new Promise(e=>setTimeout(e,i))}function Fe(i){return!!(i&&typeof i=="object"&&!Array.isArray(i))}function q(i,e){if(!Fe(i))return null;let t=i[e];return typeof t=="string"&&t.length>0?t:null}function Ht(i,e){return Fe(i)?i[e]===!0:!1}function Wt(i){if(!i)return null;let e=i.recentApps;if(Array.isArray(e))for(let r of e){let n=q(r,"id");if(n)return n}let t=i.surfaceBindings;return q(t,"app:one")||q(t,"app:two")||null}function jt(i){return i?S.basename(i)===".maestro"?S.dirname(i):i:process.cwd()}function Ut(i,e,t){let r=e.endsWith(".png")?e:`${e}.png`;if(S.isAbsolute(r))return r;if(t.mode==="flow"&&/[\\/]/.test(r)){let n=r.startsWith("./")||r.startsWith("../")?t.flowDir??process.cwd():jt(t.flowDir);return S.resolve(n,r)}return S.join(i,r)}function Vt(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 re(i,e){let[t,r]=i.split(",").map(s=>s.trim()),n=(s,l)=>s.endsWith("%")?Number.parseFloat(s)/100*l:Number.parseFloat(s),o=e?.x??0,a=e?.y??0;return{x:o+n(t,e?.width??ne),y:a+n(r,e?.height??C)}}function pt(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 Jt(i){let e=i.split(/^---$/m),t=e.length>1?e[e.length-1]:i,r=et.parse(t);return Array.isArray(r)?r:[]}var ie=class{constructor(e,t){this.bridge=e;this.opts=t}stepDelay=0;js=new ee({platform:"ios",onLog:e=>console.log(`[flow] js: ${e}`)});firstLaunchDone=!1;profilingEnabled=!1;recordingEnabled=!1;recordingAccessChecked=!1;recordingStartedAtMs=null;lastRecordingStartedAtMs=null;lastRecordingDurationMs=null;lastRecordingFrameStats=null;flowTraceSteps=[];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 o=e.find(l=>l.isPrimary&&l.readyState==="open"),a=e.find(l=>l.readyState==="open"),s=n??o??a;s&&(this.opts.simId=s.id,console.log(` [flow] sim rotated to ${s.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=te){let t=Date.now()+K(e),r=Date.now(),n=null,o=0,a=0,s=-1,l=r;for(;Date.now()<t;){try{let u=await this.evaluate(Ue,8e3);if(o=0,u?.externalError)throw new Error(`app failed to load: ${u.externalError}`);if(u){let m=Date.now();u.nodes!==s&&(s=u.nodes,l=m);let b=u.flag===!0&&Je(u),g=u.flag!==!0&&Ge(u);if((b||g)&&u.externalReady!==!1&&!u.externalError&&!u.loadingText&&m-l>=Ve)return}}catch(u){if(n=u instanceof Error?u:new Error(String(u)),n.message.startsWith("app failed to load:"))throw n;if(o++,o>=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 p=Date.now()-r;p-a>=5e3&&(a=p,console.log(` [flow] waiting for app to be ready\u2026 (${Math.round(p/1e3)}s)`)),await f(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||(this.opts.requireRecordingEntitlement&&await st("flow --video",{originOverride:this.opts.billingOriginOverride,allowGitHubAuth:this.opts.allowGitHubRecording}),this.recordingAccessChecked=!0);let e=Date.now(),t=await this.evaluate(`(() => {
|
|
214
214
|
const rec = window.__sootsimRecorder
|
|
215
215
|
if (!rec) return { ok: false, error: 'recorder unavailable on this page' }
|
|
216
216
|
const format = ${JSON.stringify(this.opts.recordingFormat??"webm")}
|
|
@@ -365,7 +365,7 @@ process.stdin.on('end', async () => {
|
|
|
365
365
|
)
|
|
366
366
|
}
|
|
367
367
|
return direct
|
|
368
|
-
})()`)}async tap(e,t,r){return await this.bridge.send({type:"tap",simId:this.opts.simId,x:e,y:t,target:r})}async longPress(e,t,r,n){return await this.bridge.send({type:"longPress",simId:this.opts.simId,x:e,y:t,durationMs:r,target:n})}async waitForElement(e,t=5e3){let r=Date.now()+t,n=await this.findElement(e);for(;!n&&Date.now()<r;)await f(150),n=await this.findElement(e);if(!n)return n;let o=Date.now()+1200,a=n;for(;Date.now()<o;){await f(120);let s=await this.findElement(e);if(!s)break;let l=Math.abs(s.absolutePosition.x-a.absolutePosition.x)>.5||Math.abs(s.absolutePosition.y-a.absolutePosition.y)>.5||Math.abs(s.layout.width-a.layout.width)>.5||Math.abs(s.layout.height-a.layout.height)>.5;if(n=s,!l)return n;a=s}return n}replayTargetForElement(e,t,r){return{id:e.id??r?.target?.id??t.id??null,testID:e.id??r?.target?.testID??t.testID??null,text:e.text??r?.target?.text??r?.target?.accessibilityLabel??t.text??t.accessibilityLabel??null,type:r?.target?.type??t.type??null}}async resolveInteractionPoint(e){if(e.point&&!e.id&&!e.text&&e.index===void 0&&!e.childOf)return{...
|
|
368
|
+
})()`)}async tap(e,t,r){return await this.bridge.send({type:"tap",simId:this.opts.simId,x:e,y:t,target:r})}async longPress(e,t,r,n){return await this.bridge.send({type:"longPress",simId:this.opts.simId,x:e,y:t,durationMs:r,target:n})}async waitForElement(e,t=5e3){let r=Date.now()+t,n=await this.findElement(e);for(;!n&&Date.now()<r;)await f(150),n=await this.findElement(e);if(!n)return n;let o=Date.now()+1200,a=n;for(;Date.now()<o;){await f(120);let s=await this.findElement(e);if(!s)break;let l=Math.abs(s.absolutePosition.x-a.absolutePosition.x)>.5||Math.abs(s.absolutePosition.y-a.absolutePosition.y)>.5||Math.abs(s.layout.width-a.layout.width)>.5||Math.abs(s.layout.height-a.layout.height)>.5;if(n=s,!l)return n;a=s}return n}replayTargetForElement(e,t,r){return{id:e.id??r?.target?.id??t.id??null,testID:e.id??r?.target?.testID??t.testID??null,text:e.text??r?.target?.text??r?.target?.accessibilityLabel??t.text??t.accessibilityLabel??null,type:r?.target?.type??t.type??null}}async resolveInteractionPoint(e){if(e.point&&!e.id&&!e.text&&e.index===void 0&&!e.childOf)return{...re(e.point),element:null,target:void 0};let t=await this.waitForElement(e);if(!t)return null;let r=typeof t.nodeId=="number"?await this.resolveTapTarget(t.nodeId):null,n=this.replayTargetForElement(e,t,r);return e.point?{...re(e.point,{x:t.absolutePosition.x,y:t.absolutePosition.y,width:t.layout.width,height:t.layout.height}),element:t,target:n}:{x:r?.cx??t.absolutePosition.x+t.layout.width/2,y:r?.cy??t.absolutePosition.y+t.layout.height/2,element:t,target:n}}async tapOn(e){let t=typeof e=="string"?{text:e}:e;if(t.point){let o=await this.resolveInteractionPoint(t);if(!o)throw new Error(`tapOn: element not found: ${JSON.stringify(t)}`);(await this.activatePressAt(o.x,o.y))?.ok||await this.tap(o.x,o.y,o.target),await f(300);return}let r=await this.waitForElement(t);if(r){let o=r.isTextInput?4:1;for(let a=0;a<o;a++){let s=a===0?r:await this.findElement({text:t.text,id:t.id});if(!s)break;let l=typeof s.nodeId=="number"?await this.resolveTapTarget(s.nodeId):null,p=l?.cx??s.absolutePosition.x+s.layout.width/2,u=l?.cy??s.absolutePosition.y+s.layout.height/2;if((await this.tap(p,u,this.replayTargetForElement(t,s,l)))?.hit===!0){if(await f(300),!s.isTextInput||await this.waitForFocusedTextInput(600,s))return;continue}break}}if((await this.activatePressTarget(t))?.ok){await f(300);return}throw new Error(`tapOn: element not found: ${JSON.stringify(t)}`)}async longPressOn(e){let t=typeof e=="string"?{text:e}:e,r=await this.resolveInteractionPoint(t);if(!r)throw new Error(`longPressOn: element not found: ${JSON.stringify(t)}`);let n=await this.longPress(r.x,r.y,3e3,r.target);if(n?.ok===!1||n?.value===!1)throw new Error(`longPressOn: long press missed: ${JSON.stringify(t)}`);await f(300)}async tapAtCoords(e,t){await this.tap(e,t),await f(300)}async doubleTapAtCoords(e,t,r=80){let n=Math.max(0,Math.round(r));if(!await this.evaluate(`(async () => {
|
|
369
369
|
const interact = window.__sootsimInteract
|
|
370
370
|
if (interact?.doubleTap) {
|
|
371
371
|
return !!(await interact.doubleTap(${e}, ${t}, ${n}))
|
|
@@ -386,7 +386,7 @@ process.stdin.on('end', async () => {
|
|
|
386
386
|
visible: !!keyboard?.isVisible?.(),
|
|
387
387
|
focused: !!test?.isTextInputFocused?.(),
|
|
388
388
|
}
|
|
389
|
-
})()`);if(!t?.visible&&!t?.focused){await f(80);return}await f(80)}}async eraseText(e){for(let t=0;t<e;t++)await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"dispatchKey",text:"Backspace"});await f(100)}async waitFor(e){let t=Date.now()+
|
|
389
|
+
})()`);if(!t?.visible&&!t?.focused){await f(80);return}await f(80)}}async eraseText(e){for(let t=0;t<e;t++)await this.bridge.send({type:"keyboard",simId:this.opts.simId,action:"dispatchKey",text:"Backspace"});await f(100)}async waitFor(e){let t=Date.now()+K(e.timeout||te);for(;Date.now()<t;){if(await this.findElement(e))return;await f(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||te}ms${n}`)}async takeScreenshot(e){if(!this.hasFreshVisualSettle())try{await this.waitForVisualSettle(),await f(250)}catch{}let t=Vt(e),r=Ut(this.opts.screenshotDir,t.path,{mode:this.opts.screenshotPathMode??"dir",flowDir:this.opts.flowDir});v.mkdirSync(S.dirname(r),{recursive:!0});let n=t.layers??this.opts.screenshotLayers,o={type:"screenshot",simId:this.opts.simId};n&&n!=="full"&&(o.layers=n);let s=(await this.bridge.send(o)).replace(/^data:image\/png;base64,/,""),l=Buffer.from(s,"base64");if(t.withFrame){let p=await this.readCurrentDeviceModel();if(!p)throw new Error("could not read current device model for framed screenshot");let u=await ct(l,p);return v.writeFileSync(r,u),console.log(`[flow] screenshot: ${r} (frame: ${p})`),r}return v.writeFileSync(r,l),console.log(`[flow] screenshot: ${r}`),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 Ne)?null:t}async captureScreenshot(e){v.mkdirSync(S.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=(u,m)=>{try{v.writeFileSync(S.join(e,u),m)}catch{}},n=async u=>{try{return await this.bridge.send({type:"evaluate",simId:this.opts.simId,code:u})}catch{return null}},o=await n(`(async () => {
|
|
390
390
|
const out = {
|
|
391
391
|
simId: window.__sootsimBridge?.id ?? window.SootSim?.state?.simId ?? null,
|
|
392
392
|
url: location.href,
|
|
@@ -427,12 +427,12 @@ process.stdin.on('end', async () => {
|
|
|
427
427
|
warnings: c?.getWarnings?.() ?? [],
|
|
428
428
|
requests: (window.__sootsimGetFailedRequests?.() ?? []),
|
|
429
429
|
}
|
|
430
|
-
})()`);return p&&r("console.json",JSON.stringify(p,null,2)),e}async swipe(e="UP",t=300){let r=
|
|
430
|
+
})()`);return p&&r("console.json",JSON.stringify(p,null,2)),e}async swipe(e="UP",t=300){let r=ne/2,n=C/2,o=200,a=r,s=n,l=r,p=n;switch(e.toUpperCase()){case"UP":s+=o,p-=o;break;case"DOWN":s-=o,p+=o;break;case"LEFT":a+=o,l-=o;break;case"RIGHT":a-=o,l+=o;break}let u=Math.max(10,Math.round(t/16));await this.drag(a,s,l,p,u,16)}async drag(e,t,r,n,o=12,a=16){if(!(await this.evaluate(`(async () => {
|
|
431
431
|
const interact = window.__sootsimInteract
|
|
432
432
|
if (!interact?.drag) return { ok: false }
|
|
433
433
|
const value = await interact.drag(${e}, ${t}, ${r}, ${n}, ${Math.max(1,Math.round(o))}, ${Math.max(0,Math.round(a))})
|
|
434
434
|
return { ok: !!value, value }
|
|
435
|
-
})()`))?.ok)throw new Error("drag failed");await f(300)}async swipeCoords(e,t,r=300){let n=
|
|
435
|
+
})()`))?.ok)throw new Error("drag failed");await f(300)}async swipeCoords(e,t,r=300){let n=re(e),o=re(t),a=Math.max(10,Math.round(r/16));await this.drag(n.x,n.y,o.x,o.y,a,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 o=n.absolutePosition.x+n.layout.width/2,a=n.absolutePosition.y+n.layout.height/2,s=180,l=o,p=a;switch(t.toUpperCase()){case"UP":p-=s;break;case"DOWN":p+=s;break;case"LEFT":l-=s;break;case"RIGHT":l+=s;break}let u=Math.max(10,Math.round(r/16));await this.drag(o,a,l,p,u,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 f(250)}async pinch(e){let t=await this.evaluate(`(async () => {
|
|
436
436
|
const interact = window.__sootsimInteract
|
|
437
437
|
if (!interact?.pinch) return { ok: false, reason: 'no interact.pinch' }
|
|
438
438
|
const ok = await interact.pinch(
|
|
@@ -441,7 +441,7 @@ process.stdin.on('end', async () => {
|
|
|
441
441
|
${e.steps||12}, ${e.stepMs||16}
|
|
442
442
|
)
|
|
443
443
|
return { ok: !!ok, reason: ok ? null : 'pinch returned false' }
|
|
444
|
-
})()`);if(!t?.ok)throw new Error(`pinch failed: ${t?.reason||"unknown error"}`);await f(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 ve({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 o=await this.findElement({text:e.element});if(o&&o.absolutePosition.y>=0&&o.absolutePosition.y+o.layout.height>0&&o.absolutePosition.y<C-50){if(e.centerElement){let a=C/2,s=o.absolutePosition.y-a;if(Math.abs(s)>100){let l=Math.min(150,Math.abs(s)*.5),p=s>0?a-l:a+l;await this.drag(
|
|
444
|
+
})()`);if(!t?.ok)throw new Error(`pinch failed: ${t?.reason||"unknown error"}`);await f(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 ve({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 o=await this.findElement({text:e.element});if(o&&o.absolutePosition.y>=0&&o.absolutePosition.y+o.layout.height>0&&o.absolutePosition.y<C-50){if(e.centerElement){let a=C/2,s=o.absolutePosition.y-a;if(Math.abs(s)>100){let l=Math.min(150,Math.abs(s)*.5),p=s>0?a-l:a+l;await this.drag(ne/2,a,ne/2,p,10,16)}}return}await this.swipe(n,250),await f(500)}throw new Error(`scrollUntilVisible: "${e.element}" not found`)}async extendedWaitUntil(e){let t=Date.now()+K(e.timeout||te);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 f(200)}throw new Error("extendedWaitUntil timed out")}async reloadGuestApp(e){if(e){let n=!1;for(let o=0;o<3&&!n;o++)try{n=await this.evaluate(ge(!0),2e4)}catch{await f(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 f(500),await this.refreshSimId()}async softReloadGuestApp(e){if(!await this.evaluate(ge(e),12e4))throw new Error("guest app soft reload bridge unavailable")}async rearmCaptureAfterRuntimeReload(){if(!this.recordingEnabled&&!this.profilingEnabled)return;let e=Date.now()+1e4,t=null;for(;Date.now()<e;){await this.refreshSimId();try{this.recordingEnabled&&await this.ensureRecordingStarted(),this.profilingEnabled&&await this.startProfile();return}catch(r){t=r,await f(250)}}throw new Error(`capture re-arm failed after app reload: ${t instanceof Error?t.message:String(t)}`)}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),r=Ht(e,"resetRuntime");if(!this.firstLaunchDone&&!t){if(this.firstLaunchDone=!0,r){let n=await G(this.bridge).catch(()=>null),o=n?.state==="home"&&n.activeApp==null;await this.softReloadGuestApp(!1),await this.waitForTree(12e4),o&&(await me(this.bridge,"goHome",1e3),await this.waitForShellHome(1e3)),await this.launchShellAppFromHomeIfNeeded(e,o?{...n??{},state:"home",activeApp:null}:null)}else await this.launchShellAppFromHomeIfNeeded(e);await this.waitForTree(3e4)}else if(this.firstLaunchDone=!0,await this.reloadGuestApp(t),await this.waitForTree(12e4),this.opts.onAfterLaunch)try{await this.opts.onAfterLaunch()}catch(n){console.warn(` warn: onAfterLaunch hook threw: ${n instanceof Error?n.message:n}`)}this.profilingEnabled&&await this.startProfile(),this.recordingEnabled&&(await this.waitForVisualSettle(),await this.ensureRecordingStarted())}async waitForVisualSettle(){try{await this.evaluate(`new Promise((resolve) => {
|
|
445
445
|
requestAnimationFrame(() => {
|
|
446
446
|
requestAnimationFrame(() => {
|
|
447
447
|
requestAnimationFrame(() => {
|
|
@@ -449,7 +449,7 @@ process.stdin.on('end', async () => {
|
|
|
449
449
|
})
|
|
450
450
|
})
|
|
451
451
|
})
|
|
452
|
-
})`),this.markVisualSettled()}catch{await f(180),this.markVisualSettled()}}markVisualSettled(){this.lastVisualSettledAtMs=Date.now()}hasFreshVisualSettle(){return Date.now()-this.lastVisualSettledAtMs<750}resolveShellLaunchAppId(e,t){if(typeof e=="string"&&e.length>0)return e;if(Fe(e)){let r=
|
|
452
|
+
})`),this.markVisualSettled()}catch{await f(180),this.markVisualSettled()}}markVisualSettled(){this.lastVisualSettledAtMs=Date.now()}hasFreshVisualSettle(){return Date.now()-this.lastVisualSettledAtMs<750}resolveShellLaunchAppId(e,t){if(typeof e=="string"&&e.length>0)return e;if(Fe(e)){let r=q(e,"appId")||q(e,"id");if(r)return r}return Wt(t)||"connect"}async waitForShellAppLaunched(e,t){let r=Date.now()+t,n=null;for(;Date.now()<r;){try{n=await G(this.bridge)}catch{n=null}if(n?.state==="app"&&n.activeApp===e&&n.showSwitcher===!1&&typeof n.launchProgress=="number"&&n.launchProgress>=.98)return;await f(16)}}async waitForShellHome(e){let t=Date.now()+e;for(;Date.now()<t;){let r=await G(this.bridge).catch(()=>null);if(r?.state==="home"&&r.activeApp==null&&r.showSwitcher===!1)return;await f(16)}}async launchShellAppFromHomeIfNeeded(e,t){let r=null;if(t)r=t;else try{r=await G(this.bridge)}catch{return!1}if(r?.state!=="home"||r.activeApp!=null)return!1;let n=this.resolveShellLaunchAppId(e,r);return this.recordingEnabled&&await this.ensureRecordingStarted(),await me(this.bridge,"launchApp",1e3,n),await this.waitForShellAppLaunched(n,1e3),!0}async startProfile(){let e=await this.evaluate(`(() => {
|
|
453
453
|
if (!window.__sootsimShellPerf) {
|
|
454
454
|
return { error: "shell frame profile unavailable (__sootsimShellPerf missing on the page)" }
|
|
455
455
|
}
|
|
@@ -460,7 +460,7 @@ process.stdin.on('end', async () => {
|
|
|
460
460
|
return { error: "shell frame profile unavailable (__sootsimShellPerf missing on the page)" }
|
|
461
461
|
}
|
|
462
462
|
return await window.__sootsimShellPerf.stop()
|
|
463
|
-
})()`);if(this.profilingEnabled=!1,e?.error)throw new Error(e.error);return e}async stopApp(){await this.reloadGuestApp(!1),await this.rearmCaptureAfterRuntimeReload()}async clearState(){await this.reloadGuestApp(!0),await this.rearmCaptureAfterRuntimeReload()}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=S.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 f(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?dt(e.true):!0}isOptional(e){if(e.optional===!0)return!0;let r=Object.entries(e).find(([o])=>o!=="when"&&o!=="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(l=>l!=="when")||Object.keys(r)[0];console.log(`[flow] step ${t+1}: ${n}`);let o=Date.now(),a=this.flowStepTargetLabel(r,n),s;try{s=this.js.interpolateStep(r),a=this.flowStepTargetLabel(s,n)??a}catch(l){if(this.isOptional(r)){console.log(`[flow] (optional, skipped: ${l.message.slice(0,80)})`),this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"skipped",error:l});return}throw this.lastFailedStep={index:t,kind:n,target:r[n]},this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"failure",error:l}),l}if(s.when&&!await this.evaluateWhen(s.when)){console.log("[flow] (when: predicate false, skipped)"),this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"skipped"});return}try{let l=!!(s.extendedWaitUntil||s.waitFor||s.scrollUntilVisible||s.launchApp||s.runFlow||s.runScript),p=1e4,u=this.runStepInner(s),m;if(l){let g=await u;typeof g=="string"&&(m=g)}else{let g=null,P=new Promise((k,oe)=>{g=setTimeout(()=>{oe(new Error(`step watchdog: ${n} made no progress in ${p}ms \u2014 bridge or bundle probably hung`))},p)});try{let k=await Promise.race([u,P]);typeof k=="string"&&(m=k)}finally{g&&clearTimeout(g)}}let b=!!(s.assertVisible||s.assertNotVisible||s.extendedWaitUntil||s.waitFor);this.stepDelay>0&&!b&&await f(this.stepDelay),this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"success",screenshotPath:m})}catch(l){if(this.isOptional(s)){console.log(`[flow] (optional, skipped: ${l.message.slice(0,80)})`),this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"skipped",error:l});return}throw this.lastFailedStep={index:t,kind:n,target:s[n]},this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"failure",error:l}),l}}async runStepInner(e){if(e.tapOn)await this.tapOn(e.tapOn);else if(e.longPressOn)await this.longPressOn(e.longPressOn);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)return this.takeScreenshot(e.takeScreenshot);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 f(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,o=async()=>n?this.evaluateWhen(n):!0,a=n?r:Math.min(r,1e3),s=0;for(;s<a&&await o();){for(let l=0;l<e.repeat.commands.length;l++)await this.runStep(e.repeat.commands[l],l);s++}}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,o=typeof t=="string"?void 0:t.env,a=typeof t=="string"?void 0:t.when;if(a&&!await this.evaluateWhen(a)){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(o)for(let[s,l]of Object.entries(o))this.js.putEnv(s,l);if(r){let s=S.resolve(this.opts.flowDir,r),l=v.readFileSync(s,"utf8"),p=this.opts.flowDir;this.opts.flowDir=S.dirname(s);try{await this.runFlow(Vt(l))}finally{this.opts.flowDir=p}}else if(n)for(let s=0;s<n.length;s++)await this.runStep(n[s],s)}finally{this.js.leaveEnvScope()}}else throw new Error(`unsupported flow step: ${JSON.stringify(e)}`)}}liveStatus=null;async runFlow(e){let t=[],r=[],n=[];for(let a of e){let s=Object.keys(a);if(s.length===1&&s[0]==="onFlowStart"&&Array.isArray(a.onFlowStart)){t.push(...a.onFlowStart);continue}if(s.length===1&&s[0]==="onFlowComplete"&&Array.isArray(a.onFlowComplete)){r.push(...a.onFlowComplete);continue}n.push(a)}if(this.liveStatus&&await this.liveStatus.plan(n.map((a,s)=>{let l=typeof a=="string"?ut(a):a,p=Object.keys(l).find(u=>u!=="when")??Object.keys(l)[0];return{index:s,name:p,target:this.flowStepTargetLabel(l,p)}})),t.length>0){console.log(`[flow] onFlowStart (${t.length} steps)`);for(let a=0;a<t.length;a++)await this.runStep(t[a],a)}let o=null;try{await this.runFlowBody(n)}catch(a){o=a instanceof Error?a:new Error(String(a))}if(r.length>0){console.log(`[flow] onFlowComplete (${r.length} steps)`);for(let a=0;a<r.length;a++)try{await this.runStep(r[a],a)}catch(s){console.warn(`[flow] onFlowComplete step ${a+1} failed: ${s instanceof Error?s.message:s}`)}}if(o)throw o}async runFlowBody(e){for(let t=0;t<e.length;t++){let r=e[t],n=typeof r.wait=="number"?r.wait:0,o=typeof r.waitFor?.timeout=="number"?r.waitFor.timeout:0,a=typeof r.extendedWaitUntil?.timeout=="number"?r.extendedWaitUntil.timeout:0,s=Math.max(6e4,n+15e3,z(o)+15e3,z(a)+15e3);this.liveStatus&&(await this.liveStatus.waitWhilePaused(),await this.liveStatus.step(t,"running"));let l=Date.now(),p=async(u,m)=>{if(!this.liveStatus)return;let b=this.flowTraceSteps[this.flowTraceSteps.length-1],g=b&&b.stepIndex===t&&b.startedAtMs>=l;await this.liveStatus.step(t,u?"failure":g&&b.status==="skipped"?"skipped":"success",{durationMs:Date.now()-l,error:u?m instanceof Error?m.message.slice(0,300):String(m).slice(0,300):g&&b.error||void 0})};try{r.runFlow||r.launchApp?await this.runStep(r,t):await Promise.race([this.runStep(r,t),new Promise((u,m)=>setTimeout(()=>m(new Error(`step ${t+1} (${Object.keys(r)[0]}) exceeded ${s}ms deadline`)),s))])}catch(u){let m=this.flowTraceSteps[this.flowTraceSteps.length-1];if(!(m&&m.stepIndex===t&&m.startedAtMs>=l)){let b=Object.keys(r)[0];this.lastFailedStep={index:t,kind:b,target:r[b]},this.recordFlowTraceStep({stepIndex:t,stepName:b,targetLabel:this.flowStepTargetLabel(r,b),startedAtMs:l,status:"failure",error:u})}throw await p(!0,u),u}await p(!1)}}};function Jt(i){if(typeof i!="object"||i==null||!("control"in i))return!1;let e=i.control;return typeof e=="object"&&e!=null&&"paused"in e&&e.paused===!0}var ie=class{constructor(e,t,r){this.bridge=e;this.flowName=t;this.getSimId=r}disabled=!1;paused=!1;lastSimId;plannedSteps=null;completedSteps=new Map;runId=`flr_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,8)}`;async send(e,t){let r=await this.bridge.send({type:"flowStatus",simId:t,status:{runId:this.runId,flowName:this.flowName,...e}});this.paused=Jt(r)}async push(e){if(!this.disabled)try{let t=this.getSimId?.();if(t!==this.lastSimId&&this.lastSimId!=null&&this.plannedSteps&&e.phase!=="plan"){await this.send({phase:"plan",state:"running",steps:this.plannedSteps},t);for(let r of this.completedSteps.values())await this.send({phase:"step",state:"running",step:r},t)}this.lastSimId=t,await this.send(e,t)}catch(t){this.disabled=!0,console.log(`[flow] live status unavailable (${t instanceof Error?t.message.slice(0,120):t}) \u2014 continuing without the devtools rail`)}}async plan(e){this.plannedSteps=e.map(t=>({...t})),this.completedSteps.clear(),await this.push({phase:"plan",state:"running",steps:e})}async step(e,t,r){let n={index:e,status:t,...r};await this.push({phase:"step",state:"running",step:n}),this.completedSteps.set(e,n)}async end(e){await this.push({phase:"end",state:e})}async waitWhilePaused(){if(!this.disabled)for(;this.paused;){for(console.log("[flow] paused from devtools \u2014 waiting\u2026");this.paused&&!this.disabled;)await new Promise(e=>setTimeout(e,400)),await this.push({phase:"heartbeat",state:"paused"});this.disabled||console.log("[flow] resumed")}}};var Ie=null;function Cr(){return Ie}var $e=[];function Hr(){return $e}async function Kt(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 He()).find(n=>n.name.toLowerCase()===e.toLowerCase());return r?String(r.preferredPort):e}async function qt(){let i=await We(),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 ht(i){Ie=null,$e=[],(i.length===0||i.includes("--help")||i.includes("-h"))&&(console.log(`
|
|
463
|
+
})()`);if(this.profilingEnabled=!1,e?.error)throw new Error(e.error);return e}async stopApp(){await this.reloadGuestApp(!1),await this.rearmCaptureAfterRuntimeReload()}async clearState(){await this.reloadGuestApp(!0),await this.rearmCaptureAfterRuntimeReload()}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=S.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 f(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?ut(e.true):!0}isOptional(e){if(e.optional===!0)return!0;let r=Object.entries(e).find(([o])=>o!=="when"&&o!=="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"?pt(e):e,n=Object.keys(r).find(l=>l!=="when")||Object.keys(r)[0];console.log(`[flow] step ${t+1}: ${n}`);let o=Date.now(),a=this.flowStepTargetLabel(r,n),s;try{s=this.js.interpolateStep(r),a=this.flowStepTargetLabel(s,n)??a}catch(l){if(this.isOptional(r)){console.log(`[flow] (optional, skipped: ${l.message.slice(0,80)})`),this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"skipped",error:l});return}throw this.lastFailedStep={index:t,kind:n,target:r[n]},this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"failure",error:l}),l}if(s.when&&!await this.evaluateWhen(s.when)){console.log("[flow] (when: predicate false, skipped)"),this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"skipped"});return}try{let l=!!(s.extendedWaitUntil||s.waitFor||s.scrollUntilVisible||s.launchApp||s.runFlow||s.runScript),p=1e4,u=this.runStepInner(s),m;if(l){let g=await u;typeof g=="string"&&(m=g)}else{let g=null,I=new Promise((k,H)=>{g=setTimeout(()=>{H(new Error(`step watchdog: ${n} made no progress in ${p}ms \u2014 bridge or bundle probably hung`))},p)});try{let k=await Promise.race([u,I]);typeof k=="string"&&(m=k)}finally{g&&clearTimeout(g)}}let b=!!(s.assertVisible||s.assertNotVisible||s.extendedWaitUntil||s.waitFor);this.stepDelay>0&&!b&&await f(this.stepDelay),this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"success",screenshotPath:m})}catch(l){if(this.isOptional(s)){console.log(`[flow] (optional, skipped: ${l.message.slice(0,80)})`),this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"skipped",error:l});return}throw this.lastFailedStep={index:t,kind:n,target:s[n]},this.recordFlowTraceStep({stepIndex:t,stepName:n,targetLabel:a,startedAtMs:o,status:"failure",error:l}),l}}async runStepInner(e){if(e.tapOn)await this.tapOn(e.tapOn);else if(e.longPressOn)await this.longPressOn(e.longPressOn);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)return this.takeScreenshot(e.takeScreenshot);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 f(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,o=async()=>n?this.evaluateWhen(n):!0,a=n?r:Math.min(r,1e3),s=0;for(;s<a&&await o();){for(let l=0;l<e.repeat.commands.length;l++)await this.runStep(e.repeat.commands[l],l);s++}}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,o=typeof t=="string"?void 0:t.env,a=typeof t=="string"?void 0:t.when;if(a&&!await this.evaluateWhen(a)){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(o)for(let[s,l]of Object.entries(o))this.js.putEnv(s,l);if(r){let s=S.resolve(this.opts.flowDir,r),l=v.readFileSync(s,"utf8"),p=this.opts.flowDir;this.opts.flowDir=S.dirname(s);try{await this.runFlow(Jt(l))}finally{this.opts.flowDir=p}}else if(n)for(let s=0;s<n.length;s++)await this.runStep(n[s],s)}finally{this.js.leaveEnvScope()}}else throw new Error(`unsupported flow step: ${JSON.stringify(e)}`)}}liveStatus=null;async runFlow(e){let t=[],r=[],n=[];for(let a of e){let s=Object.keys(a);if(s.length===1&&s[0]==="onFlowStart"&&Array.isArray(a.onFlowStart)){t.push(...a.onFlowStart);continue}if(s.length===1&&s[0]==="onFlowComplete"&&Array.isArray(a.onFlowComplete)){r.push(...a.onFlowComplete);continue}n.push(a)}if(this.liveStatus&&await this.liveStatus.plan(n.map((a,s)=>{let l=typeof a=="string"?pt(a):a,p=Object.keys(l).find(u=>u!=="when")??Object.keys(l)[0];return{index:s,name:p,target:this.flowStepTargetLabel(l,p)}})),t.length>0){console.log(`[flow] onFlowStart (${t.length} steps)`);for(let a=0;a<t.length;a++)await this.runStep(t[a],a)}let o=null;try{await this.runFlowBody(n)}catch(a){o=a instanceof Error?a:new Error(String(a))}if(r.length>0){console.log(`[flow] onFlowComplete (${r.length} steps)`);for(let a=0;a<r.length;a++)try{await this.runStep(r[a],a)}catch(s){console.warn(`[flow] onFlowComplete step ${a+1} failed: ${s instanceof Error?s.message:s}`)}}if(o)throw o}async runFlowBody(e){for(let t=0;t<e.length;t++){let r=e[t],n=typeof r.wait=="number"?r.wait:0,o=typeof r.waitFor?.timeout=="number"?r.waitFor.timeout:0,a=typeof r.extendedWaitUntil?.timeout=="number"?r.extendedWaitUntil.timeout:0,s=Math.max(6e4,n+15e3,K(o)+15e3,K(a)+15e3);this.liveStatus&&(await this.liveStatus.waitWhilePaused(),await this.liveStatus.step(t,"running"));let l=Date.now(),p=async(u,m)=>{if(!this.liveStatus)return;let b=this.flowTraceSteps[this.flowTraceSteps.length-1],g=b&&b.stepIndex===t&&b.startedAtMs>=l;await this.liveStatus.step(t,u?"failure":g&&b.status==="skipped"?"skipped":"success",{durationMs:Date.now()-l,error:u?m instanceof Error?m.message.slice(0,300):String(m).slice(0,300):g&&b.error||void 0})};try{r.runFlow||r.launchApp?await this.runStep(r,t):await Promise.race([this.runStep(r,t),new Promise((u,m)=>setTimeout(()=>m(new Error(`step ${t+1} (${Object.keys(r)[0]}) exceeded ${s}ms deadline`)),s))])}catch(u){let m=this.flowTraceSteps[this.flowTraceSteps.length-1];if(!(m&&m.stepIndex===t&&m.startedAtMs>=l)){let b=Object.keys(r)[0];this.lastFailedStep={index:t,kind:b,target:r[b]},this.recordFlowTraceStep({stepIndex:t,stepName:b,targetLabel:this.flowStepTargetLabel(r,b),startedAtMs:l,status:"failure",error:u})}throw await p(!0,u),u}await p(!1)}}};function Gt(i){if(typeof i!="object"||i==null||!("control"in i))return!1;let e=i.control;return typeof e=="object"&&e!=null&&"paused"in e&&e.paused===!0}var oe=class{constructor(e,t,r){this.bridge=e;this.flowName=t;this.getSimId=r}disabled=!1;paused=!1;lastSimId;plannedSteps=null;completedSteps=new Map;runId=`flr_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,8)}`;async send(e,t){let r=await this.bridge.send({type:"flowStatus",simId:t,status:{runId:this.runId,flowName:this.flowName,...e}});this.paused=Gt(r)}async push(e){if(!this.disabled)try{let t=this.getSimId?.();if(t!==this.lastSimId&&this.lastSimId!=null&&this.plannedSteps&&e.phase!=="plan"){await this.send({phase:"plan",state:"running",steps:this.plannedSteps},t);for(let r of this.completedSteps.values())await this.send({phase:"step",state:"running",step:r},t)}this.lastSimId=t,await this.send(e,t)}catch(t){this.disabled=!0,console.log(`[flow] live status unavailable (${t instanceof Error?t.message.slice(0,120):t}) \u2014 continuing without the devtools rail`)}}async plan(e){this.plannedSteps=e.map(t=>({...t})),this.completedSteps.clear(),await this.push({phase:"plan",state:"running",steps:e})}async step(e,t,r){let n={index:e,status:t,...r};await this.push({phase:"step",state:"running",step:n}),this.completedSteps.set(e,n)}async end(e){await this.push({phase:"end",state:e})}async waitWhilePaused(){if(!this.disabled)for(;this.paused;){for(console.log("[flow] paused from devtools \u2014 waiting\u2026");this.paused&&!this.disabled;)await new Promise(e=>setTimeout(e,400)),await this.push({phase:"heartbeat",state:"paused"});this.disabled||console.log("[flow] resumed")}}};var Ie=null;function Hr(){return Ie}var $e=[];function Wr(){return $e}async function qt(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 We()).find(n=>n.name.toLowerCase()===e.toLowerCase());return r?String(r.preferredPort):e}async function Yt(){let i=await je(),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 ft(i){Ie=null,$e=[],(i.length===0||i.includes("--help")||i.includes("-h"))&&(console.log(`
|
|
464
464
|
sootsim maestro \u2014 author and run Maestro YAML flows
|
|
465
465
|
|
|
466
466
|
usage:
|
|
@@ -540,28 +540,28 @@ flow extension:
|
|
|
540
540
|
takeScreenshot:
|
|
541
541
|
path: marketing/hero
|
|
542
542
|
withFrame: true
|
|
543
|
-
`),process.exit(0));let e=i[0]
|
|
543
|
+
`),process.exit(0));let e=i[0];P.existsSync(e)||(console.error(` error: ${e} not found`),process.exit(1));let t=h=>i.find((c,y)=>i[y-1]===h),r=i.includes("--profile"),n=i.includes("--headed"),o=i.includes("--new")||n,a=t("--out")||"/tmp/sootsim-recordings",s=t("--screenshots")||(t("--out")?a:"/tmp/sootsim-flow"),l=t("--screenshot-paths")==="flow"?"flow":"dir",p=i.includes("--no-shell")?"tenant":i.includes("--shell-only")?"shell":void 0,u=t("--device")?.trim()||"",m=i.includes("--slow")?+(t("--slow")||"500"):0,b=i.includes("--electron"),g=i.includes("--preview"),I=await ot(t("--preview-origin")),k=t("--preview-public-origin"),H=i.includes("--preview-open"),Pe=t("--owner")?.trim()||"",ke=t("--repo")?.trim()||"",W=t("--billing-kind")?.trim()||"",mt=g?`maestro:${process.env.GITHUB_RUN_ID?`github:${process.env.GITHUB_RUN_ID}:attempt:${process.env.GITHUB_RUN_ATTEMPT||"1"}:job:${process.env.GITHUB_JOB||"preview"}`:`local:${Kt()}`}:flow:${zt("sha256").update(w.relative(process.cwd(),w.resolve(e))).digest("hex").slice(0,16)}`:"";W&&W!=="test_run"&&(console.error(` error: invalid --billing-kind: ${W}`),process.exit(1));let gt=i.flatMap((h,c)=>i[c-1]==="--replace"?["--replace",h]:[]),wt=i.flatMap((h,c)=>i[c-1]==="--remap"?["--remap",h]:[]),M=i.includes("--record")||g,bt=i.includes("--tail-wait")?Math.max(0,+(t("--tail-wait")||"2000")):M?g?6e3:2e3:0,yt=P.readFileSync(e,"utf8"),{frontmatter:T,steps:R}=we(yt);R.length===0&&(console.error(" error: flow file must contain a YAML array of steps"),process.exit(1));let j=u||T.device||"",Ae=T.theme||"",Z=b||T.electron===!0,St=T.app===void 0||T.app===null?"":typeof T.app=="number"?String(T.app):T.app,se=await qt(St),Me={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","--billing-kind","--owner","--repo"]},x=he(i,Me),vt=t("--url")||"";Z&&!qe()&&(console.error(" error: desktop companion not found. install or build it first with `bun run build:electron`"),process.exit(1));let Re=Z?"desktop companion":"bridge",xt=se?` | target: ${se}`:"";console.log(`
|
|
544
544
|
sootsim maestro \u2014 ${w.basename(e)}
|
|
545
|
-
${R.length} steps | ${Re}${
|
|
546
|
-
`),g&&Be({event:"preview_flow_started",properties:{flowName:w.basename(e),stepCount:R.length,mode:Re,electron:
|
|
545
|
+
${R.length} steps | ${Re}${xt}${M?" | recording":""}${r?" | profiling":""}${m?` | ${m}ms delay`:""}
|
|
546
|
+
`),g&&Be({event:"preview_flow_started",properties:{flowName:w.basename(e),stepCount:R.length,mode:Re,electron:Z,record:M,profile:r,previewOrigin:I}});let A=vt||se||"",ae=o||Z&&x.simIdSource!=="flag",_e=t("--driver")||"",le=!1,ce=new Set,Oe=!1;if(!A&&!ae&&x.simIdSource==="none"){let h=ze(x);try{let c=await h.listSims(),y=c.find(D=>D.isPrimary&&D.readyState==="open"),$=c.find(D=>D.readyState==="open"),J=y??$;J&&(Oe=!0,console.log(` reusing active sim: ${J.id}`))}catch{}finally{h.close()}}if(!A&&!Oe&&(ae||x.simIdSource==="none")){let h=await Yt();h||(console.error(" error: no current sim and no sootsim target found"),await ue(),process.exit(1)),A=h}if(A&&j){let h=new URL(await Ye(A));h.searchParams.set("device",j),A=h.toString()}if(A){let h=[A];if(h.push(...gt),h.push(...wt),ae&&h.push("--new"),h.push("--no-describe"),x.simIdSource==="flag"&&x.simId&&h.push("--sim",x.simId),_e&&(h.push("--driver",_e),le=!0),i.includes("--headless")&&!n&&h.push("--headless"),await Xe(h,{port:x.wsPort,timeoutMs:x.commandTimeoutMs}),le){let c=pe();c&&ce.add(c)}}let B=he(i,Me),X=B.simIdSource==="flag"?B.simId:pe()||B.simId,Tt=B.simIdSource==="flag"?"flag":X?"saved":"none",E=fe(B.wsPort,{commandTimeoutMs:B.commandTimeoutMs,simId:X,simIdSource:Tt}),d,De=async()=>{if(Ae&&await E.send({type:"call",simId:d.simId,path:"SootSim.bridges.settings.set",args:["colorScheme",Ae]}),!j)return!1;let h=await E.send({type:"evaluate",simId:d.simId,code:"window.location.href"});if(typeof h=="string"&&h.length>0){let c=new URL(h);c.searchParams.set("device",j),d.setSimRouteHint(c.toString())}return!!await E.send({type:"call",simId:d.simId,path:"SootSim.bridges.settings.set",args:["deviceModel",j]})};if(d=new ie(E,{screenshotDir:s,flowDir:w.dirname(w.resolve(e)),screenshotPathMode:l,screenshotLayers:p,simId:X,recordingOutputDir:M?a:void 0,recordingFormat:g?"mp4":"webm",billingOriginOverride:g?I:void 0,allowGitHubRecording:g,requireRecordingEntitlement:g,onAfterLaunch:async()=>{await De()&&await d.waitForTree(12e4),g&&await E.send({type:"evaluate",simId:d.simId,code:`(() => {
|
|
547
547
|
const r = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
|
|
548
548
|
return r?.start?.() ?? false
|
|
549
|
-
})()`})}}),m&&(d.stepDelay=m),d.liveStatus=new
|
|
549
|
+
})()`})}}),m&&(d.stepDelay=m),d.liveStatus=new oe(E,w.basename(e),()=>d.simId),d.js.putEnv("MAESTRO_FILENAME",w.basename(e,w.extname(e))),T.env&&typeof T.env=="object")for(let[h,c]of Object.entries(T.env))d.js.putEnv(h,c);let _=null,F=null,L=null,O=null,U=null,de=null,V=0,Q=!!R[0]?.launchApp;try{Q||await d.waitForTree(12e4),await De()&&!Q&&await d.waitForTree(12e4),g&&!Q&&!process.env.SOOTSIM_PREVIEW_RESET_DONE&&await d.launchApp({clearState:!0}),M&&(Q?d.prepareRecording():await d.startRecording()),r&&await d.startProfile(),g&&(await E.send({type:"evaluate",simId:d.simId,code:`(() => {
|
|
550
550
|
const r = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
|
|
551
551
|
return r?.start?.() ?? false
|
|
552
|
-
})()`})||console.warn(" warn: eventRecorder.start() returned false \u2014 preview will upload bundle without replay events")),await d.runFlow(R),r&&(_=await d.stopProfile()),await d.waitForRecordingTail({maxMs:
|
|
552
|
+
})()`})||console.warn(" warn: eventRecorder.start() returned false \u2014 preview will upload bundle without replay events")),await d.runFlow(R),r&&(_=await d.stopProfile()),await d.waitForRecordingTail({maxMs:bt,smart:g}),M&&(F=await d.stopRecording(),L=d.getLastRecordingDurationMs()),g&&(O=await E.send({type:"evaluate",simId:d.simId,code:`(() => {
|
|
553
553
|
const r = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
|
|
554
554
|
return r?.stop?.() ?? []
|
|
555
|
-
})()`}),console.log(` captured ${O.length} replay events`),
|
|
556
|
-
+ completed (${R.length} steps)`),c>0&&console.log(` screenshots: ${c} \u2192 ${s}`),_&&
|
|
555
|
+
})()`}),console.log(` captured ${O.length} replay events`),U=w.join(s,"preview-final.png"),await d.captureScreenshot(U),console.log(` final screenshot: ${U}`));let c=R.filter(y=>y&&typeof y=="object"&&"takeScreenshot"in y).length;console.log(`
|
|
556
|
+
+ completed (${R.length} steps)`),c>0&&console.log(` screenshots: ${c} \u2192 ${s}`),_&&ht(_),F&&console.log(` video: ${F}`),console.log()}catch(h){if(r&&!_)try{_=await d.stopProfile()}catch{}if(M&&!F)try{F=await d.stopRecording(),L=d.getLastRecordingDurationMs()}catch(c){console.warn(` recording stop failed: ${c?.message||String(c)}`)}if(g&&!O)try{O=await E.send({type:"evaluate",simId:d.simId,code:`(() => {
|
|
557
557
|
const r = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
|
|
558
558
|
return r?.stop?.() ?? []
|
|
559
559
|
})()`}),console.log(` captured ${O.length} partial replay events`)}catch(c){console.warn(` event recorder stop failed: ${c?.message||String(c)}`)}console.error(`
|
|
560
560
|
x failed: ${h.message}
|
|
561
|
-
`),_&&(console.log(" partial profile:"),
|
|
562
|
-
preparing preview upload\u2026`);let
|
|
561
|
+
`),_&&(console.log(" partial profile:"),ht(_)),F&&console.log(` partial video: ${F}`);try{let c=d.lastFailedStep,y=c?`step-${String(c.index+1).padStart(2,"0")}-${c.kind}`:"unstaged",$=w.join(s,y);await d.captureFailureBundle($,{error:h,stepIndex:c?.index,stepKind:c?.kind,stepTarget:c?.target}),de=$,console.log(` failure bundle: ${$}`),console.log(" (contents: screenshot.png, describe.json, a11y.txt, tree.txt, console.json, error.json)")}catch(c){try{let y=w.join(s,"error.png");await d.captureScreenshot(y),console.log(` error screenshot: ${y}`)}catch{}console.log(` (failure bundle capture failed: ${c instanceof Error?c.message:String(c)})`)}await Ke(E,{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}if(await d.liveStatus?.end(V===0?"passed":"failed"),g&&(V===0||F||O))try{let h=await Xt(O??[]),c=["--origin",I,"--events",h];k&&c.push("--public-origin",k),F&&c.push("--video",F),U&&c.push("--screenshot",U),F&&L&&Number.isFinite(L)&&(c.push("--video-duration-ms",String(Math.round(L))),c.push("--recorded-duration-ms",String(Math.round(L))));let y=d.getLastRecordingStartedAtMs();y&&Number.isFinite(y)&&c.push("--recording-started-at-ms",String(Math.round(y))),x.wsPort&&c.push("--port",String(x.wsPort)),d.simId&&c.push("--sim",d.simId),H&&c.push("--open"),Pe&&c.push("--owner",Pe),ke&&c.push("--repo",ke),c.push("--run-scope",mt),W&&c.push("--billing-kind",W),console.log(`
|
|
562
|
+
preparing preview upload\u2026`);let $=d.getFlowTraceSteps();$.length>0&&c.push("--timeline-events",await Qt($));let J=await er($,y);J&&c.push("--snapshot-manifest",J),de&&c.push("--failure-bundle",de);let D=d.getLastRecordingFrameStats();D&&c.push("--trace-frame-stats",await tr(D));let{runUpload:Et}=await import("./upload-56DF47MA.js");Ie=await Et(c,{})}catch(h){console.error(` preview upload failed: ${h?.message||h}`),V=1}return $e=d.getFlowTraceSteps(),E.close(),le?(d.simId&&ce.add(d.simId),await Zt(x.wsPort,x.commandTimeoutMs,[...ce])):d.simId&&d.simId!==X&&He(d.simId),await ue(),V}async function Zt(i,e,t){let r=[...new Set(t.filter(Boolean))];if(r.length===0)return;let n=fe(i,{commandTimeoutMs:e});try{let o=await n.listSims(),a=r.filter(l=>o.some(p=>p.id===l&&p.readyState==="open"));if(a.length===0)return;let s=await Qe(n,i,e,a);await Ze(o,a),s.closed.length>0&&console.log(` closed flow sim(s): ${s.closed.join(", ")}`),s.remaining.length>0&&console.warn(` warn: flow sim(s) still connected after close: ${s.remaining.join(", ")}`)}catch(o){console.warn(` warn: failed to close flow sim(s): ${o instanceof Error?o.message:String(o)}`)}finally{n.close()}}async function Xt(i){let{gzipSync:e}=await import("zlib"),t=i.map(o=>JSON.stringify(o)).join(`
|
|
563
563
|
`)+(i.length?`
|
|
564
|
-
`:""),r=e(Buffer.from(t,"utf8")),n=w.join(
|
|
564
|
+
`:""),r=e(Buffer.from(t,"utf8")),n=w.join(Y(),`sootsim-events-${Date.now()}.jsonl.gz`);return P.writeFileSync(n,r),console.log(` events: ${i.length} written to ${n} (${r.length} bytes gz)`),n}async function Qt(i){let{gzipSync:e}=await import("zlib"),t=i.map((a,s)=>({schemaVersion:1,t:a.endedAtMs,seq:1e6+s,context:"host",kind:"flow-step",id:`flow-step-${a.stepIndex}`,data:{stepIndex:a.stepIndex,stepNumber:a.stepIndex+1,stepName:a.stepName,...a.targetLabel?{targetLabel:a.targetLabel}:{},status:a.status,durationMs:a.durationMs,startedAtMs:a.startedAtMs,endedAtMs:a.endedAtMs,...a.error?{error:a.error}:{}}})),r=t.map(a=>JSON.stringify(a)).join(`
|
|
565
565
|
`)+`
|
|
566
|
-
`,n=e(Buffer.from(r,"utf8")),o=w.join(
|
|
567
|
-
validation failed \u2014 draft preserved so you can keep iterating`),process.exitCode=p;return}ye(),console.log(` flow draft validated (${s.stepCount} step${s.stepCount===1?"":"s"})`),s.outputPath&&console.log(` saved: ${s.outputPath}`),o&&console.log(" video: recorded during validation run");return}if(ye(),console.log(` flow draft ended (${s.stepCount} step${s.stepCount===1?"":"s"})`),s.outputPath){console.log(` saved: ${s.outputPath}`),console.log(` next: sootsim maestro test ${s.outputPath} --record`);return}console.log(""),process.stdout.write(s.yaml);return}case"validate":{let r=i[1];(!r||r.startsWith("-"))&&(console.error(" usage: sootsim maestro validate <path>"),process.exit(1));let n=be(r);if(n.length>0){console.error(` x ${r} failed validation:`);for(let o of n)console.error(` - ${o}`);process.exit(1)}console.log(` + ${r} looks valid`);return}}}function
|
|
566
|
+
`,n=e(Buffer.from(r,"utf8")),o=w.join(Y(),`sootsim-flow-steps-${Date.now()}.jsonl.gz`);return P.writeFileSync(o,n),console.log(` flow trace: ${t.length} step events written to ${o} (${n.length} bytes gz)`),o}async function er(i,e){if(!e)return null;let t=i.flatMap(n=>n.screenshotPath?[{id:`flow-step-${n.stepIndex}`,label:n.targetLabel??`Step ${n.stepIndex+1}`,kind:"interaction",t:Math.max(0,n.endedAtMs-e),path:n.screenshotPath}]:[]);if(t.length===0)return null;let r=w.join(Y(),`sootsim-flow-snapshots-${Date.now()}.json`);return P.writeFileSync(r,JSON.stringify(t)),console.log(` flow snapshots: ${t.length} written to ${r}`),r}async function tr(i){let e=w.join(Y(),`sootsim-frame-stats-${Date.now()}.json`);return P.writeFileSync(e,JSON.stringify(i)),console.log(` frame stats: written to ${e}`),e}async function rr(i){let e=i[0],t=r=>i.find((n,o)=>i[o-1]===r);switch(e){case"start":{let{path:r,state:n}=tt();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 maestro 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"),o=i.includes("--video"),a=Se();a.active&&a.kept&&console.log(` auto-kept trailing action: ${a.candidate.summary}`);let s=rt(r||(n?ir():void 0));if(s.active||(console.error(" no active flow draft \u2014 run `sootsim maestro start` first"),process.exit(1)),!s.valid){console.error(" flow draft is not valid:");for(let l of s.issues)console.error(` - ${l}`);console.error(" draft preserved \u2014 keep at least one real interaction or run `sootsim maestro start` to reset"),process.exit(1)}if(n){let l=or(i,s.outputPath),p=await ft(l);if(p!==0){console.error(`
|
|
567
|
+
validation failed \u2014 draft preserved so you can keep iterating`),process.exitCode=p;return}ye(),console.log(` flow draft validated (${s.stepCount} step${s.stepCount===1?"":"s"})`),s.outputPath&&console.log(` saved: ${s.outputPath}`),o&&console.log(" video: recorded during validation run");return}if(ye(),console.log(` flow draft ended (${s.stepCount} step${s.stepCount===1?"":"s"})`),s.outputPath){console.log(` saved: ${s.outputPath}`),console.log(` next: sootsim maestro test ${s.outputPath} --record`);return}console.log(""),process.stdout.write(s.yaml);return}case"validate":{let r=i[1];(!r||r.startsWith("-"))&&(console.error(" usage: sootsim maestro validate <path>"),process.exit(1));let n=be(r);if(n.length>0){console.error(` x ${r} failed validation:`);for(let o of n)console.error(` - ${o}`);process.exit(1)}console.log(` + ${r} looks valid`);return}}}function nr(i){return i[0]==="--sim"&&i.length>=2?[...i.slice(2),"--sim",i[1]]:i}async function jr(i){let e=nr(i),t=e[0];if(t==="start"||t==="keep"||t==="good"||t==="end"||t==="validate")return await rr(e),0;let r=await ft(e);return r!==0&&(process.exitCode=r),r}function ht(i){console.log(""),nt(i)}function ir(){return w.join(Y(),`sootsim-flow-draft-${Date.now()}.yaml`)}function or(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{ct as a,Rt as b,Hr as c,Wr as d,Yt as e,ft as f,nr as g,jr as h};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
import{readFileSync as i}from"node:fs";import{fileURLToPath as e}from"node:url";var r=null;function l(){if(r!=null)return r;let o=[()=>e(import.meta.resolve("sootsim/package.json")),()=>e(new URL("../package.json",import.meta.url))];for(let n of o)try{let t=JSON.parse(i(n(),"utf8")).version;if(typeof t=="string"&&t)return r=t,r}catch{}return r="0.0.0",r}export{l as a};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{o as d}from"./chunk-
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{o as d}from"./chunk-HZHQMMJ3.js";import{chmodSync as S,existsSync as f,mkdirSync as k,readFileSync as A,rmSync as i,writeFileSync as y}from"node:fs";import{dirname as c,join as D,resolve as m}from"node:path";var s=1,p="SOOTSIM_SHARED_AUTH_FILE",u="https://contrast.dev";function w(){let t=process.env[p];return t?.trim()?c(m(t)):d()}function a(){let t=process.env[p];return t?.trim()?m(t):D(w(),"desktop-auth.json")}function g(t){if(!t||typeof t!="object")return null;let e=t;return typeof e.id!="string"||!e.id.trim()?null:{id:e.id.trim(),name:typeof e.name=="string"?e.name:void 0,email:typeof e.email=="string"?e.email:void 0,image:typeof e.image=="string"?e.image:void 0,githubUsername:typeof e.githubUsername=="string"?e.githubUsername:void 0,teams:Array.isArray(e.teams)?e.teams.map(r=>{if(!r||typeof r!="object")return null;let n=r;return typeof n.id!="string"||typeof n.name!="string"?null:{id:n.id,name:n.name,role:n.role==="owner"?"owner":"member",githubOrg:typeof n.githubOrg=="string"?n.githubOrg:null}}).filter(r=>!!r):void 0}}function v(t){if(!t||typeof t!="object")return null;let e=t;if(e.version!==s||typeof e.token!="string"||!e.token.trim())return null;let r=typeof e.origin=="string"&&e.origin.trim()?e.origin.trim():u,n=e.source==="cli"||e.source==="electron"||e.source==="browser"||e.source==="unknown"?e.source:"unknown",o=typeof e.updatedAt=="string"&&e.updatedAt?e.updatedAt:new Date().toISOString(),h=typeof e.validatedAt=="string"&&e.validatedAt?e.validatedAt:void 0;return{version:s,token:e.token.trim(),user:g(e.user),origin:r,source:n,updatedAt:o,validatedAt:h}}function O(){let t=a();if(!f(t))return null;try{let e=JSON.parse(A(t,"utf8")),r=v(e);return r||(i(t,{force:!0}),null)}catch{return i(t,{force:!0}),null}}function b(t){let e=a();k(c(e),{recursive:!0});let r={version:s,token:t.token.trim(),user:t.user?g(t.user):null,origin:t.origin?.trim()||u,source:t.source,updatedAt:t.updatedAt||new Date().toISOString(),validatedAt:t.validatedAt};y(e,JSON.stringify(r,null,2)+`
|
|
3
3
|
`);try{S(e,384)}catch{}return r}function l(){i(a(),{force:!0})}async function E(t){let e=O();if(!e?.token)return null;let r=t||e.origin||u;try{let n=await fetch(`${r.replace(/\/$/,"")}/api/auth/me`,{headers:{authorization:`Bearer ${e.token}`}});if(n.status===401)return l(),null;if(!n.ok)return e;let o=await n.json();return o.user?.id?b({token:e.token,user:o.user,origin:r,source:e.source,validatedAt:new Date().toISOString()}):(l(),null)}catch{return e}}export{O as a,b,l as c,E as d};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import"./chunk-
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import"./chunk-NIW6FA7Z.js";import{existsSync as x,readFileSync as j}from"fs";import{resolve as g}from"path";async function P(s){(s.includes("--help")||s.includes("-h"))&&(console.log(`
|
|
3
3
|
sootsim compat \u2014 check package compatibility
|
|
4
4
|
|
|
5
5
|
usage:
|
|
@@ -20,7 +20,7 @@ examples:
|
|
|
20
20
|
sootsim compat react-native-reanimated
|
|
21
21
|
sootsim compat --json
|
|
22
22
|
sootsim compat --brief
|
|
23
|
-
`),process.exit(0));let c=s.includes("--json"),e=s.includes("--brief"),l=s.indexOf("--app"),n=l!==-1&&s[l+1]?g(s[l+1]):process.cwd(),k=s.filter((o,a)=>!o.startsWith("-")&&(l===-1||a!==l+1))[0];if(c||e){let{scanDeps:o}=await import("./web-
|
|
23
|
+
`),process.exit(0));let c=s.includes("--json"),e=s.includes("--brief"),l=s.indexOf("--app"),n=l!==-1&&s[l+1]?g(s[l+1]):process.cwd(),k=s.filter((o,a)=>!o.startsWith("-")&&(l===-1||a!==l+1))[0];if(c||e){let{scanDeps:o}=await import("./web-OALK6L2K.js"),a=g(n,"package.json");x(a)||(console.error(" no package.json found in",n),process.exit(1));let u=JSON.parse(j(a,"utf8")),r=o(u);if(c){console.log(JSON.stringify(r,null,2));return}if(r.packages.length===0){console.log(" no RN ecosystem packages detected");return}let T=Math.round(r.overallScore*100),v=r.packages.filter(i=>i.status==="full").length,$=r.packages.filter(i=>i.status==="partial"||i.status==="auto-stub").length,w=r.packages.filter(i=>i.status==="unsupported").length,p=[];v&&p.push(`${v} fully supported`),$&&p.push(`${$} partial`),w&&p.push(`${w} unsupported`),console.log(` ${p.join(", ")} \u2014 ${T}% compatible`);return}let{POLYFILL_REGISTRY:d}=await import("./web-OALK6L2K.js");if(k){W(k,d);return}let b=g(n,"package.json");x(b)||(console.error(" no package.json found in",n),process.exit(1));let m=JSON.parse(j(b,"utf8")),S={...m.dependencies,...m.devDependencies},t={full:[],partial:[],autoStub:[],unsupported:[],justWorks:[],unknown:[]};for(let o of Object.keys(S)){let a=d[o];if(a){let u=a.stubType==="native"?"full":a.stubType==="works"?"not-relevant":a.stubType;if(a.stubType!=="native"&&a.versions?.length){let r=Math.max(...a.versions.map(y=>y.coverage||0));u=r>=.8?"full":r>=.3?"partial":"auto-stub"}switch(u){case"full":t.full.push(o);break;case"partial":t.partial.push(o);break;case"auto-stub":t.autoStub.push(o);break;case"unsupported":t.unsupported.push(o);break;case"not-relevant":t.justWorks.push(o);break;default:f(o)&&t.unknown.push(o)}}else f(o)&&t.unknown.push(o)}console.log(`
|
|
24
24
|
sootsim compatibility report
|
|
25
25
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
26
26
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{A as i,C as s,x as t}from"./chunk-
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{A as i,C as s,x as t}from"./chunk-HZHQMMJ3.js";import"./chunk-NIW6FA7Z.js";function l(){let e=i();console.log(` telemetry is ${e?"ON":"OFF"}`),console.log(` config: ${t()}`);let o=process.env.DO_NOT_TRACK;(o==="1"||o==="true")&&console.log(` note: DO_NOT_TRACK=${o} is set \u2014 telemetry is off for this run`)}async function r(e){let o=e.find(n=>!n.startsWith("-"))?.toLowerCase();switch(o){case void 0:case"status":l();return;case"on":case"enable":s(!0),console.log(" telemetry ON"),console.log(` config: ${t()}`);return;case"off":case"disable":s(!1),console.log(" telemetry OFF"),console.log(` config: ${t()}`);return;default:console.error(` unknown telemetry action: ${o}`),console.error(" usage: sootsim config telemetry [on|off|status]"),process.exit(1)}}function c(){console.log(`
|
|
3
3
|
sootsim config \u2014 manage local CLI preferences
|
|
4
4
|
|
|
5
5
|
usage:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{d as a,e as b,f as c,g as d,h as e,i as f,j as g,k as h,l as i,m as j,n as k,o as l,p as m,q as n,r as o,s as p,t as q,u as r,v as s,w as t,x as u}from"./chunk-
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{d as a,e as b,f as c,g as d,h as e,i as f,j as g,k as h,l as i,m as j,n as k,o as l,p as m,q as n,r as o,s as p,t as q,u as r,v as s,w as t,x as u}from"./chunk-NRPETN6L.js";import"./chunk-DKDYYXRY.js";import"./chunk-2XGPQPBQ.js";import"./chunk-SYJEB5TC.js";import"./chunk-JHJYM5IX.js";import"./chunk-LFT2G6CS.js";import"./chunk-WVA7UPF2.js";import"./chunk-WNCRDOVZ.js";import"./chunk-QHFMMVZS.js";import"./chunk-6ZERINEJ.js";import"./chunk-QKU7E35D.js";import"./chunk-GIVBLMYH.js";import"./chunk-RZYPYVAE.js";import"./chunk-R5AUBYLC.js";import"./chunk-BARVOECN.js";import"./chunk-HZHQMMJ3.js";import"./chunk-JNHCJEUS.js";import"./chunk-AXJTPAHA.js";import"./chunk-NIW6FA7Z.js";export{f as buildOpenUrl,e as buildShellUrl,s as closeSimsBulk,b as normalizeDeepLinkTarget,t as planBulkCloseTargets,k as playwrightHostPidForSim,h as printConnectedSims,c as resolveBundleTarget,d as resolveDefaultShellBaseUrl,a as resolveDriverConnectWaitOptions,r as runClaimCommand,u as runCloseCommand,q as runFocusCommand,n as runLinkCommand,o as runOpenCommand,p as runUseCommand,l as signalDriverLaunchConnected,g as summarizeSimUrl,m as terminatePlaywrightHostsForSims,i as waitForSimMatch,j as waitForSimMatchOrHostExit};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{a,b,c,d,e}from"./chunk-OILZ3XOV.js";import"./chunk-HK3WEUHS.js";import"./chunk-ZUKJYGD4.js";import"./chunk-QS4WA56I.js";import"./chunk-NER5BICF.js";import"./chunk-QP7HQHRB.js";import"./chunk-RZYPYVAE.js";import"./chunk-R5AUBYLC.js";import"./chunk-BARVOECN.js";import"./chunk-7EIZLBLT.js";import"./chunk-HZHQMMJ3.js";import"./chunk-ZUDTLJQC.js";import"./chunk-MAKP6XWE.js";import"./chunk-AXJTPAHA.js";import"./chunk-NIW6FA7Z.js";export{c as daemonInstall,b as getDaemonServiceStatus,a as runDaemon,d as teardownDaemonService,e as teardownSootsimHome};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{f,m as T}from"./chunk-
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{f,m as T}from"./chunk-6ZERINEJ.js";import{K as S,L as $,M as N,N as A,O as v}from"./chunk-QKU7E35D.js";import{g as D,i as O,k as x}from"./chunk-GIVBLMYH.js";import"./chunk-R5AUBYLC.js";import"./chunk-BARVOECN.js";import"./chunk-HZHQMMJ3.js";import"./chunk-NIW6FA7Z.js";var H=["portals","sheets","layout","onlayout","animated","render","touch","yoga","all"],R="__sootsimShellAnimationTrace",q="__sootsimDebugAnimation";function L(){console.log(`
|
|
3
3
|
sootsim debug \u2014 drive __sootsimDebug from the terminal
|
|
4
4
|
|
|
5
5
|
usage:
|
|
@@ -170,7 +170,7 @@ examples:
|
|
|
170
170
|
} : null,
|
|
171
171
|
samples,
|
|
172
172
|
}
|
|
173
|
-
})()`}async function c(i,l){return x(i,l)}async function Y(i,l){let g=D(i,{port:l.port,stripBooleanFlags:["--pretty","--json","--help","-h"]}),u=g.positional;(!u[0]||i.includes("--help")||i.includes("-h"))&&(L(),process.exit(0));let P=g.wsPort,_=g.simId,n=!i.includes("--json"),y=_?` --sim ${_}`:"",k=u[0],r=u.slice(1);if(new Set(["state","js","eval","perf","memory","sample-color"]).has(k)){let{runInspect:e}=await import("./inspect-
|
|
173
|
+
})()`}async function c(i,l){return x(i,l)}async function Y(i,l){let g=D(i,{port:l.port,stripBooleanFlags:["--pretty","--json","--help","-h"]}),u=g.positional;(!u[0]||i.includes("--help")||i.includes("-h"))&&(L(),process.exit(0));let P=g.wsPort,_=g.simId,n=!i.includes("--json"),y=_?` --sim ${_}`:"",k=u[0],r=u.slice(1);if(new Set(["state","js","eval","perf","memory","sample-color"]).has(k)){let{runInspect:e}=await import("./inspect-GE7F3UD5.js");await e(["debug",...i],{port:l.port,verbose:l.verbose});return}let t=O(g);try{switch(k){case"enable":{let e=B(r[0]);e.length===0&&(console.error(` usage: sootsim debug enable <channel[,channel,...]>
|
|
174
174
|
known: ${H.join(", ")}`),process.exit(1));let o=await v(t,"enable",e);console.log(a({active:o},n));break}case"disable":{let e=B(r[0]),o=await v(t,"disable",e);console.log(a({active:o},n));break}case"toggle":{let e=r[0];e||(console.error(" usage: sootsim debug toggle <channel>"),process.exit(1));let o=await c(t,`window.__sootsimDebug.toggle(${JSON.stringify(e)})`);console.log(a({[e]:o},n));break}case"status":{let e=await S(t);console.log(a(e,n));break}case"channels":{let e=await c(t,"window.__sootsimDebug.channels()");console.log(a(e,n));break}case"flags":{let e=await $(t);console.log(a(e,n));break}case"snapshot":{let e=r[0],o=e?`window.__sootsimDebug.snapshot(${JSON.stringify(e)})`:"window.__sootsimDebug.snapshot()",s=await c(t,`(() => { const s = ${o}; if (!s) return null; return { label: s.label, at: s.at, size: s.nodes.size }; })()`);console.log(a(s,n));break}case"snapshots":{let e=await c(t,"window.__sootsimDebug.snapshots()");console.log(a(e,n));break}case"diff":{let e=r[0],o=r[1];(!e||!o)&&(console.error(" usage: sootsim debug diff <labelA> <labelB>"),process.exit(1));let s=`(() => {
|
|
175
175
|
const d = window.__sootsimDebug.diff(${JSON.stringify(e)}, ${JSON.stringify(o)});
|
|
176
176
|
if (!d) return null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{a as g}from"./chunk-
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{a as g}from"./chunk-QM6C26WM.js";import"./chunk-RLPH773J.js";import{a as v,h as k}from"./chunk-NRPETN6L.js";import"./chunk-DKDYYXRY.js";import"./chunk-2XGPQPBQ.js";import{b as m,g as f,l as h}from"./chunk-SYJEB5TC.js";import{a as c}from"./chunk-JHJYM5IX.js";import"./chunk-LFT2G6CS.js";import"./chunk-WVA7UPF2.js";import"./chunk-WNCRDOVZ.js";import"./chunk-QHFMMVZS.js";import{a as u}from"./chunk-BM5GBB56.js";import"./chunk-6ZERINEJ.js";import"./chunk-QKU7E35D.js";import"./chunk-GIVBLMYH.js";import"./chunk-RZYPYVAE.js";import"./chunk-R5AUBYLC.js";import"./chunk-BARVOECN.js";import"./chunk-HZHQMMJ3.js";import"./chunk-JNHCJEUS.js";import"./chunk-AXJTPAHA.js";import"./chunk-NIW6FA7Z.js";function I(e){let i=e.indexOf("--port");if(i<0)return;let o=e[i+1],t=o?Number(o):Number.NaN;return(!Number.isInteger(t)||t<=0)&&(console.error(` invalid --port value: ${o||"(missing)"}`),process.exit(1)),t}async function O(e,i){let o=I(e)??i.port;return o?k(String(o)):void 0}async function w(e,i){(e.includes("--help")||e.includes("-h"))&&(console.log(`
|
|
3
3
|
sootsim desktop \u2014 launch the desktop companion
|
|
4
4
|
|
|
5
5
|
usage:
|
|
@@ -16,7 +16,7 @@ examples:
|
|
|
16
16
|
sootsim desktop --port 5173
|
|
17
17
|
sootsim desktop --profile qa
|
|
18
18
|
sootsim desktop --driver system
|
|
19
|
-
`),process.exit(0));let o=e.indexOf("--driver"),t=o>=0?e[o+1]:void 0,n=e.find((r,a)=>e[a-1]==="--profile"),d=e.includes("--ephemeral");n&&d&&(console.error(" sootsim desktop: --profile cannot be combined with --ephemeral"),process.exit(1));let x=n?m(n).id:void 0,s=t||i.driver,l=f;if(s){let r=h(s);r||(console.error(` unknown driver "${s}" \u2014 run \`sootsim list --drivers\``),process.exit(1)),l=r}if(l.id==="electron"){let r=c();if(!r){if(v(),process.stdin.isTTY&&process.env.CI!=="1"&&process.env.SOOTSIM_NO_PROMPT!=="1"&&await u("run sootsim desktop install now?",!0)){console.log();let{runInstallDesktop:D}=await import("./install-desktop-
|
|
19
|
+
`),process.exit(0));let o=e.indexOf("--driver"),t=o>=0?e[o+1]:void 0,n=e.find((r,a)=>e[a-1]==="--profile"),d=e.includes("--ephemeral");n&&d&&(console.error(" sootsim desktop: --profile cannot be combined with --ephemeral"),process.exit(1));let x=n?m(n).id:void 0,s=t||i.driver,l=f;if(s){let r=h(s);r||(console.error(` unknown driver "${s}" \u2014 run \`sootsim list --drivers\``),process.exit(1)),l=r}if(l.id==="electron"){let r=c();if(!r){if(v(),process.stdin.isTTY&&process.env.CI!=="1"&&process.env.SOOTSIM_NO_PROMPT!=="1"&&await u("run sootsim desktop install now?",!0)){console.log();let{runInstallDesktop:D}=await import("./install-desktop-NJFBRUQK.js");await D(["--yes"]),r=c()}r||process.exit(1)}console.log(` launching ${r.path}`)}let b=await O(e,i),p=await l.launch({url:b,device:i.device,profileId:x,ephemeralProfile:d});p.launched||(console.error(` ${p.message}`),process.exit(1)),console.log(` ${p.message}`)}function y(){console.log(`
|
|
20
20
|
sootsim desktop \u2014 launch or install the desktop companion
|
|
21
21
|
|
|
22
22
|
usage:
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*! sootsim v0.1.261 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{e as E}from"./chunk-XGZRIJRY.js";import"./chunk-V5JUP7E5.js";import"./chunk-ILTQ4ZPZ.js";import"./chunk-K2DYN4E4.js";import"./chunk-MLKQ6UCQ.js";import{c as P,d as F,e as M}from"./chunk-PQMCTB2S.js";import{a as R}from"./chunk-UPXPN2SA.js";import"./chunk-NRPETN6L.js";import"./chunk-DKDYYXRY.js";import"./chunk-2XGPQPBQ.js";import"./chunk-SYJEB5TC.js";import"./chunk-JHJYM5IX.js";import"./chunk-LFT2G6CS.js";import"./chunk-WVA7UPF2.js";import"./chunk-WNCRDOVZ.js";import"./chunk-QHFMMVZS.js";import"./chunk-6ZERINEJ.js";import"./chunk-QKU7E35D.js";import"./chunk-GIVBLMYH.js";import"./chunk-KNRPSHXD.js";import"./chunk-HK3WEUHS.js";import"./chunk-ZUKJYGD4.js";import"./chunk-QP7HQHRB.js";import"./chunk-RZYPYVAE.js";import"./chunk-R5AUBYLC.js";import"./chunk-BARVOECN.js";import"./chunk-7EIZLBLT.js";import"./chunk-HZHQMMJ3.js";import"./chunk-JNHCJEUS.js";import"./chunk-AXJTPAHA.js";import"./chunk-NIW6FA7Z.js";import{spawn as L}from"child_process";import{existsSync as x,mkdirSync as G,readFileSync as C,writeFileSync as w,unlinkSync as J}from"fs";import{tmpdir as $}from"os";import{dirname as W,resolve as l,join as N,relative as B}from"path";function q(){try{let t=import.meta.resolve("sootsim/detox");return W(t.startsWith("file://")?t.slice(7):t)}catch{return l(import.meta.dirname,"..","..","detox")}}var H=`
|
|
3
|
+
sootsim detox \u2014 run detox-style tests against a sootsim shell
|
|
4
|
+
|
|
5
|
+
usage:
|
|
6
|
+
sootsim detox [testFiles...] run all tests under e2e/, test/e2e/, or detox/
|
|
7
|
+
sootsim detox --config <path> use a specific jest config
|
|
8
|
+
sootsim detox init scaffold sootsim-detox.config.cjs + sample test
|
|
9
|
+
sootsim detox --watch jest watch mode
|
|
10
|
+
sootsim detox --headed keep the sootsim shell window visible
|
|
11
|
+
sootsim detox -t <pattern> pass a jest --testNamePattern
|
|
12
|
+
sootsim detox --no-launch skip auto-launching a sootsim shell
|
|
13
|
+
|
|
14
|
+
sootsim detox auto-launches a sootsim shell if none is running on the
|
|
15
|
+
expected port. disable with --no-launch if you're managing the shell yourself.
|
|
16
|
+
`;async function ie(t,s={}){if((t.includes("--help")||t.includes("-h"))&&(console.log(H),process.exit(0)),t[0]==="init")return Q();let i=e=>{let o=t.indexOf(e);return o>=0?t[o+1]:void 0},n=e=>t.includes(e),u=n("--watch"),f=n("--headed"),d=n("--no-launch"),T=i("--config"),p=s.port||Number(process.env.SOOTSIM_PORT)||5173,U=t.filter((e,o)=>{if(e.startsWith("--")||e==="-t"||e==="-h")return!1;let a=t[o-1];return!(a==="--config"||a==="-t"||a==="--testNamePattern"||a==="--grep")}),k=t.filter(e=>e==="--runInBand"||e.startsWith("--maxWorkers=")),O=["e2e","test/e2e","detox"],c=process.env.SOOTSIM_TEST_DIR?l(process.cwd(),process.env.SOOTSIM_TEST_DIR):null;if(!c)for(let e of O){let o=l(process.cwd(),e);if(x(o)){c=o;break}}c||(console.error(` error: no detox tests found. expected one of: ${O.join(", ")}
|
|
17
|
+
run 'sootsim detox init' to scaffold a starter suite.`),process.exit(1)),d||await K(p);let r=["jest"],h=T||z(["sootsim-detox.config.cjs","jest.config.cjs","jest.config.js"]),m=null;if(h)r.push("--config",l(process.cwd(),h));else{let e=l(q(),"jest-preset.cjs");m=N($(),`sootsim-detox-${process.pid}.config.cjs`);let o={rootDir:c,roots:["<rootDir>"],testMatch:["<rootDir>/**/*.test.ts","<rootDir>/**/*.test.js"]};w(m,`module.exports = { ...require(${JSON.stringify(e)}), ...${JSON.stringify(o)} }
|
|
18
|
+
`),r.push("--config",m)}u&&r.push("--watch");let y=i("-t")||i("--testNamePattern")||i("--grep");y&&r.push("-t",y);let I=R(),v=!u&&P({uploadedShare:!1,auth:I}),g=process.env.SOOTSIM_JEST_OUTPUT_FILE||null;process.env.SOOTSIM_JEST_JSON==="1"&&r.push("--json"),process.env.SOOTSIM_JEST_OUTPUT_FILE?r.push("--outputFile",process.env.SOOTSIM_JEST_OUTPUT_FILE):v&&(g=N($(),`sootsim-detox-results-${process.pid}.json`),r.push("--json","--outputFile",g)),r.push(...k),r.push(...U),console.log(" sootsim detox"),console.log(` test dir: ${c}`),console.log(` port: ${p}${f?" (headed)":""}`),h&&console.log(` config: ${h}`);let j=L("npx",r,{cwd:process.cwd(),stdio:"inherit",env:{...process.env,SOOTSIM_TEST_DIR:c,SOOTSIM_PORT:String(p),SOOTSIM_HEADED:f?"1":"",SOOTSIM_URL:process.env.SOOTSIM_URL||`http://localhost:${p}`}}),b=e=>()=>{j.kill(e)},_=b("SIGTERM"),D=b("SIGINT");process.on("SIGTERM",_),process.on("SIGINT",D);let A=Date.now(),S=await new Promise(e=>j.on("exit",o=>e(o||0)));if(process.off("SIGTERM",_),process.off("SIGINT",D),m)try{J(m)}catch{}if(v){let e=g?V(g):null,o=await M(),a=await F({origin:o,kind:"detox",name:B(process.cwd(),c)||"e2e",status:S===0?"passed":"failed",summary:e?`${e.numPassedTests}/${e.numTotalTests} tests passed`:null,failureMessage:S===0?null:e?.firstFailure??null,stepCount:e?.numTotalTests??null,failedStepIndex:e?.firstFailedIndex??null,durationMs:Date.now()-A,auth:I});a&&console.log(` run: ${a.id}`)}process.exit(S)}function V(t){try{let s=JSON.parse(C(t,"utf8")),i=null,n=null,u=0;for(let f of s.testResults??[])for(let d of f.testResults??[]){if(n==null&&d.status==="failed"){n=u;let T=d.failureMessages?.[0]?.split(`
|
|
19
|
+
`)[0];i=[d.fullName,T].filter(Boolean).join(": ")||null}u+=1}return typeof s.numTotalTests!="number"?null:{numTotalTests:s.numTotalTests,numPassedTests:s.numPassedTests??0,firstFailure:i,firstFailedIndex:n}}catch{return null}finally{if(!process.env.SOOTSIM_JEST_OUTPUT_FILE)try{J(t)}catch{}}}function z(t){for(let s of t)if(x(l(process.cwd(),s)))return s;return null}async function K(t){await E()||console.warn(` warn: no sootsim shell reachable on :${t}. start one with \`bun run dev:sootsim:shell\`
|
|
20
|
+
or pass --no-launch if you're managing the shell yourself.`)}function Q(){let t=process.cwd(),s=l(t,"sootsim-detox.config.cjs");x(s)?console.log(` skip: ${s} already exists`):(w(s,`// sootsim detox jest config.
|
|
21
|
+
// extends the sootsim preset, which rewrites \`import ... from 'detox'\`
|
|
22
|
+
// to the sootsim driver and keeps your jest transforms intact.
|
|
23
|
+
|
|
24
|
+
/** @type {import('@jest/types').Config.InitialOptions} */
|
|
25
|
+
module.exports = {
|
|
26
|
+
preset: 'sootsim/detox/jest-preset',
|
|
27
|
+
rootDir: __dirname,
|
|
28
|
+
testMatch: ['<rootDir>/e2e/**/*.test.ts', '<rootDir>/e2e/**/*.test.js'],
|
|
29
|
+
}
|
|
30
|
+
`),console.log(` created ${s}`));let i=l(t,"e2e"),n=l(i,"example.test.ts");x(n)?console.log(` skip: ${n} already exists`):(G(i,{recursive:!0}),w(n,`// sample sootsim detox test. resolve \`detox\` via the jest preset in
|
|
31
|
+
// sootsim-detox.config.cjs \u2014 no extra imports required.
|
|
32
|
+
|
|
33
|
+
import { by, device, element, expect, waitFor } from 'detox'
|
|
34
|
+
|
|
35
|
+
describe('example', () => {
|
|
36
|
+
beforeAll(async () => {
|
|
37
|
+
await device.launchApp()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('shows the welcome text', async () => {
|
|
41
|
+
await waitFor(element(by.text('Welcome'))).toBeVisible().withTimeout(5000)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('taps a button by id', async () => {
|
|
45
|
+
await element(by.id('start-button')).tap()
|
|
46
|
+
await expect(element(by.id('home-screen'))).toBeVisible()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
`),console.log(` created ${n}`)),console.log(`
|
|
50
|
+
next:`),console.log(" sootsim detox # run the sample"),console.log(" sootsim detox --watch"),process.exit(0)}export{ie as runDetox};
|