@@ -66,6 +66,69 @@ async function forceKillPort(port: number): Promise<boolean> {
6666 }
6767}
6868
69+ type LinuxTerminalLauncher = { cmd : string ; args : string [ ] } ;
70+
71+ function parseCommandLine ( value : string ) : string [ ] {
72+ // Handles simple quoted values in TERMINAL, e.g. "kitty -1" or '/usr/bin/xfce4-terminal --disable-server'
73+ const matches = value . match ( / ( [ ^ \s " ' ] + | " [ ^ " ] * " | ' [ ^ ' ] * ' ) + / g) ;
74+ return ( matches || [ ] ) . map ( token => token . replace ( / ^ [ ' " ] | [ ' " ] $ / g, '' ) ) ;
75+ }
76+
77+ function findLinuxTerminal ( ) : LinuxTerminalLauncher | null {
78+ const pathEnv = process . env . PATH || '' ;
79+ const pathDirs = pathEnv . split ( ':' ) . filter ( Boolean ) ;
80+
81+ const isExecutablePath = ( executablePath : string ) : boolean => {
82+ try {
83+ fs . accessSync ( executablePath , fs . constants . X_OK ) ;
84+ return true ;
85+ } catch {
86+ return false ;
87+ }
88+ } ;
89+
90+ const resolveExecutable = ( cmd : string ) : string | null = > {
91+ if ( ! cmd ) return null ;
92+
93+ if ( cmd . includes ( '/' ) ) {
94+ return isExecutablePath ( cmd ) ? cmd : null ;
95+ }
96+
97+ for ( const dir of pathDirs ) {
98+ const fullPath = path . join ( dir , cmd ) ;
99+ if ( isExecutablePath ( fullPath ) ) return fullPath ;
100+ }
101+
102+ return null ;
103+ } ;
104+
105+ const terminalFromEnv = process . env . TERMINAL ?. trim ( ) ;
106+ if ( terminalFromEnv ) {
107+ const [ terminalCmd , ...terminalArgs ] = parseCommandLine ( terminalFromEnv ) ;
108+ const terminalPath = terminalCmd ? resolveExecutable ( terminalCmd ) : null ;
109+ if ( terminalPath ) {
110+ return { cmd : terminalPath , args : [ ...terminalArgs , '-e' ] } ;
111+ }
112+ }
113+
114+ const candidates : LinuxTerminalLauncher [ ] = [
115+ { cmd : 'x-terminal-emulator' , args : [ '-e' ] } ,
116+ { cmd : 'gnome-terminal' , args : [ '--' ] } ,
117+ { cmd : 'konsole' , args : [ '-e' ] } ,
118+ { cmd : 'xfce4-terminal' , args : [ '-e' ] } ,
119+ { cmd : 'xterm' , args : [ '-e' ] } ,
120+ ] ;
121+
122+ for ( const candidate of candidates ) {
123+ const resolved = resolveExecutable ( candidate . cmd ) ;
124+ if ( resolved ) {
125+ return { cmd : resolved , args : candidate . args } ;
126+ }
127+ }
128+
129+ return null ;
130+ }
131+
69132program
70133 . name ( 'clawd-cursor' )
71134 . description ( '🐾 AI Desktop Agent — native screen control' )
@@ -334,26 +397,33 @@ done
334397
335398 if ( platform === 'win32' ) {
336399 // Write temp PS1 and open in new Windows Terminal / PowerShell window
337- const fs = await import ( 'fs' ) ;
338- const path = await import ( 'path' ) ;
339400 const tmpScript = path . join ( os . tmpdir ( ) , `clawd-task-${ Date . now ( ) } .ps1` ) ;
340401 fs . writeFileSync ( tmpScript , scriptContent ) ;
341402 spawnExec ( 'powershell.exe' , [
342403 '-Command' , `Start-Process powershell -ArgumentList '-NoExit','-ExecutionPolicy','Bypass','-File','${ tmpScript } '`
343404 ] , { detached : true , stdio : 'ignore' } as any ) ;
344405 } else if ( platform === 'darwin' ) {
345- const fs = await import ( 'fs' ) ;
346- const path = await import ( 'path' ) ;
347406 const tmpScript = path . join ( os . tmpdir ( ) , `clawd-task-${ Date . now ( ) } .sh` ) ;
348407 fs . writeFileSync ( tmpScript , scriptContent , { mode : 0o755 } ) ;
349408 spawnExec ( 'open' , [ '-a' , 'Terminal' , tmpScript ] , { detached : true , stdio : 'ignore' } as any ) ;
350409 } else {
351410 // Linux fallback
352- const fs = await import ( 'fs' ) ;
353- const path = await import ( 'path' ) ;
354411 const tmpScript = path . join ( os . tmpdir ( ) , `clawd-task-${ Date . now ( ) } .sh` ) ;
355412 fs . writeFileSync ( tmpScript , scriptContent , { mode : 0o755 } ) ;
356- spawnExec ( 'x-terminal-emulator' , [ '-e' , tmpScript ] , { detached : true , stdio : 'ignore' } as any ) ;
413+
414+ const terminal = findLinuxTerminal ( ) ;
415+ if ( terminal ) {
416+ spawnExec ( terminal . cmd , [ ...terminal . args , tmpScript ] , { detached : true , stdio : 'ignore' } as any ) ;
417+ } else {
418+ console . warn ( '⚠️ No supported Linux terminal emulator found. Running interactive mode in the current terminal.' ) ;
419+ process . stdout . write ( '\n' ) ;
420+ await new Promise < void > ( ( resolve ) => {
421+ const child = spawnExec ( 'bash' , [ tmpScript ] , { stdio : 'inherit' } as any ) ;
422+ child . on ( 'close' , ( ) => resolve ( ) ) ;
423+ child . on ( 'error' , ( ) => resolve ( ) ) ;
424+ } ) ;
425+ return ;
426+ }
357427 }
358428
359429 console . log ( '🐾 Task console opened in a new terminal window.' ) ;
0 commit comments