char-slop/ai-dots
ai dotfiles
git clone https://git.t4t.associates/char-slop/ai-dots
dc2c12a
main
1import { spawn } from "node:child_process" ; 2import { createInterface } from "node:readline" ; 3import { fileURLToPath } from "node:url" ; 4import { checkSignal , type DesktopBackend } from "./backend.ts" ; 5 6export function createWindowsBackend () :DesktopBackend { 7const child = spawn ( "powershell.exe" , [ 8"-NoLogo" , "-NoProfile" , "-NonInteractive" , "-ExecutionPolicy" , "Bypass" , 9"-File" , fileURLToPath ( new URL ( "./windows.ps1" , import . meta. url )), 10], { stdio :[ "pipe" , "pipe" , "pipe" ], windowsHide :true }); 11const lines = createInterface ({ input :child . stdout , crlfDelay :Infinity }); 12const responses = lines [ Symbol . asyncIterator ](); 13let stderr = "" ; 14let failure :Error | undefined ; 15child . stderr . setEncoding ( "utf8" ). on ( "data" , ( chunk :string ) => { 16stderr = ( stderr + chunk ). slice ( - 16_384 ); 17}); 18// A failed spawn can also emit an error on stdin. 19child . stdin . on ( "error" , ( error ) => { failure = error ; }); 20const exited = new Promise < void >(( resolve ) => { 21child . once ( "error" , ( error ) => { failure = error ; resolve (); }); 22child . once ( "close" , resolve ); 23}); 24 25return { 26async execute ( action , signal ) { 27checkSignal ( signal ); 28let abort :() => void = () => {}; 29const cancelled = new Promise < never >(( _ , reject ) => { 30abort = () => reject ( new Error ( "Operation aborted" )); 31signal ?. addEventListener ( "abort" , abort , { once :true }); 32}); 33try { 34if ( failure ) throw failure ; 35child . stdin . write ( ` ${ JSON . stringify ( action )} \n` ); 36const line = await Promise . race ([ responses . next (), cancelled ]); 37if ( line . done ) { 38await exited ; 39throw failure ?? new Error ( stderr . trim () || "Windows desktop helper disconnected" ); 40} 41const response = JSON . parse ( line . value ) as { error ?:string ; png ?:string }; 42if ( response . error ) throw new Error ( response . error ); 43if ( action . action === "screenshot" ) { 44if ( ! response . png ) throw new Error ( "Windows desktop helper returned no screenshot" ); 45return Buffer . from ( response . png , "base64" ); 46} 47} finally { 48signal ?. removeEventListener ( "abort" , abort ); 49} 50}, 51async close () { 52// EOF lets the helper release held inputs even after a cancelled request. 53child . stdin . end (); 54const timer = setTimeout (() => child . kill (), 5_000 ); 55try { 56await exited ; 57} finally { 58clearTimeout ( timer ); 59lines . close (); 60} 61}, 62}; 63}