@@ -17,9 +17,15 @@ import {
1717 readMaestroSelectorPlatform ,
1818 resolveVisibleMaestroNodeFromSnapshot ,
1919} from './runtime-targets.ts' ;
20+ import { sleep } from '../../utils/timeouts.ts' ;
21+
22+ const MAESTRO_RUN_FLOW_WHEN_POLICY = {
23+ visibleTimeoutMs : 1000 ,
24+ visiblePollMs : 250 ,
25+ } as const ;
2026
2127type MaestroRunFlowWhenCondition =
22- | { ok : true ; mode : string ; predicate : string ; selector : string }
28+ | { ok : true ; mode : string ; selector : string }
2329 | { ok : false ; response : DaemonResponse } ;
2430
2531export async function invokeMaestroRunFlowWhen ( params : {
@@ -80,7 +86,6 @@ function readMaestroRunFlowWhenCondition(positionals: string[]): MaestroRunFlowW
8086 return {
8187 ok : true ,
8288 mode,
83- predicate : mode === 'visible' ? 'visible' : 'hidden' ,
8489 selector,
8590 } ;
8691}
@@ -92,22 +97,67 @@ async function evaluateMaestroRunFlowWhenCondition(
9297 } ,
9398 condition : Extract < MaestroRunFlowWhenCondition , { ok : true } > ,
9499) : Promise < { ok : true ; matched : boolean } | { ok : false ; response : DaemonResponse } > {
100+ if ( condition . mode === 'visible' ) {
101+ return await waitForMaestroRunFlowVisibleCondition ( params , condition ) ;
102+ }
103+
95104 const response = await captureMaestroRawSnapshot ( params ) ;
96105 if ( ! response . ok ) return { ok : false , response } ;
106+ const result = readMaestroRunFlowVisibleCondition ( params , condition . selector , response ) ;
107+ if ( ! result . ok ) {
108+ return {
109+ ok : false ,
110+ response : result . response ,
111+ } ;
112+ }
113+ return { ok : true , matched : ! result . matched } ;
114+ }
115+
116+ async function waitForMaestroRunFlowVisibleCondition (
117+ params : {
118+ baseReq : ReplayBaseRequest ;
119+ invoke : ( req : DaemonRequest ) => Promise < DaemonResponse > ;
120+ } ,
121+ condition : Extract < MaestroRunFlowWhenCondition , { ok : true } > ,
122+ ) : Promise < { ok : true ; matched : boolean } | { ok : false ; response : DaemonResponse } > {
123+ // Maestro conditionals commonly guard UI that appears immediately after the
124+ // previous command. Keep this short and only for visible; notVisible stays a
125+ // point-in-time condition so optional cleanup blocks do not become waits.
126+ const startedAt = Date . now ( ) ;
127+ while ( true ) {
128+ const response = await captureMaestroRawSnapshot ( params ) ;
129+ if ( ! response . ok ) return { ok : false , response } ;
130+ const result = readMaestroRunFlowVisibleCondition ( params , condition . selector , response ) ;
131+ if ( ! result . ok ) return { ok : false , response : result . response } ;
132+ if ( result . matched ) return { ok : true , matched : true } ;
133+ if ( Date . now ( ) - startedAt >= MAESTRO_RUN_FLOW_WHEN_POLICY . visibleTimeoutMs ) {
134+ return { ok : true , matched : false } ;
135+ }
136+ await sleep ( MAESTRO_RUN_FLOW_WHEN_POLICY . visiblePollMs ) ;
137+ }
138+ }
139+
140+ function readMaestroRunFlowVisibleCondition (
141+ params : {
142+ baseReq : ReplayBaseRequest ;
143+ } ,
144+ selector : string ,
145+ response : Extract < DaemonResponse , { ok : true } > ,
146+ ) : { ok : true ; matched : boolean } | { ok : false ; response : DaemonResponse } {
97147 const snapshot = readSnapshotState ( response . data ) ;
98148 if ( ! snapshot ) {
99149 return {
100150 ok : false ,
101151 response : errorResponse ( 'COMMAND_FAILED' , 'Unable to read snapshot data for runFlow.when.' ) ,
102152 } ;
103153 }
104- const visible = resolveVisibleMaestroNodeFromSnapshot (
154+ const matched = resolveVisibleMaestroNodeFromSnapshot (
105155 snapshot ,
106- condition . selector ,
156+ selector ,
107157 readMaestroSelectorPlatform ( params . baseReq . flags ) ,
108158 getSnapshotReferenceFrame ( snapshot ) ,
109159 ) . ok ;
110- return { ok : true , matched : condition . mode === 'visible' ? visible : ! visible } ;
160+ return { ok : true , matched } ;
111161}
112162
113163async function invokeMaestroRunFlowWhenSteps (
0 commit comments