@@ -6,6 +6,7 @@ import { join } from "node:path";
66import { promisify } from "node:util" ;
77import { failureAvailability , windowAvailability } from "./availability.js" ;
88import { DESKTOP_BUTTONS , DESKTOP_CLICKS , DESKTOP_DIRECTIONS , DESKTOP_ELEMENT_ACTIONS , DESKTOP_MODIFIERS , DESKTOP_SELECTIONS , isDesktopAction , isDesktopKey , isDesktopManagement , } from "./protocol.js" ;
9+ import { NOTIFICATIONS_MS , notificationAction , readNotifications , validateNotificationAction , } from "./notifications.js" ;
910import { waitFor } from "./wait.js" ;
1011const exec = promisify ( execFile ) ;
1112const MAX_TEXT = 32_000 ;
@@ -575,6 +576,10 @@ function validateAction(request) {
575576 }
576577 return validateKey ( request ) ;
577578 }
579+ if ( request . op === "notification-action" ) {
580+ validateNotificationAction ( request ) ;
581+ return ;
582+ }
578583 if ( typeof request . snapshot !== "string" || ! request . snapshot || request . snapshot . length > 256 ) {
579584 throw new Error ( "Use the snapshot_id from a fresh desktop_inspect result." ) ;
580585 }
@@ -1191,6 +1196,8 @@ async function execute(native, request, abort) {
11911196 "inspect" ,
11921197 "read" ,
11931198 "wait" ,
1199+ "notifications" ,
1200+ "notification-action" ,
11941201 "screen" ,
11951202 "click" ,
11961203 "type" ,
@@ -1238,6 +1245,30 @@ async function execute(native, request, abort) {
12381245 } ) ,
12391246 } , request , abort ) ;
12401247 }
1248+ // Notifications compose several reads, each with its own 30-second limit, under one overall limit.
1249+ if ( request . op === "notifications" || request . op === "notification-action" ) {
1250+ const signal = AbortSignal . any ( [ ...( abort ? [ abort ] : [ ] ) , AbortSignal . timeout ( NOTIFICATIONS_MS ) ] ) ;
1251+ const io = {
1252+ observe : ( read , signal ) => execute ( native , read , signal ) ,
1253+ perform : ( input , signal , onDispatch ) => native ( [ "action" ] , signal , { input : JSON . stringify ( input ) , onDispatch } ) ,
1254+ } ;
1255+ if ( request . op === "notifications" ) {
1256+ if ( Object . keys ( request ) . some ( ( key ) => key !== "op" ) )
1257+ throw new Error ( "Reading notifications takes no arguments." ) ;
1258+ return { text : bounded ( await readNotifications ( io , signal ) , "notifications" ) } ;
1259+ }
1260+ try {
1261+ const { data, outcome } = await notificationAction ( io , request , signal ) ;
1262+ return actionResult ( data , outcome ) ;
1263+ }
1264+ catch ( error ) {
1265+ return actionResult ( {
1266+ outcome : "refused" ,
1267+ reason : ( error instanceof Error ? error . message : String ( error ) ) . slice ( 0 , 2000 ) ,
1268+ message : "No input was sent." ,
1269+ } , "refused" ) ;
1270+ }
1271+ }
12411272 const deadline = new AbortController ( ) ;
12421273 const timer = setTimeout ( ( ) => deadline . abort ( new Error ( "Native desktop operation timed out after 30 seconds." ) ) , 30_000 ) ;
12431274 const signal = abort ? AbortSignal . any ( [ abort , deadline . signal ] ) : deadline . signal ;
0 commit comments