@@ -10,6 +10,7 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
1010 private aborted = false
1111 private pid ?: number
1212 private subprocess ?: ReturnType < typeof execa >
13+ private pidUpdatePromise ?: Promise < void >
1314
1415 constructor ( terminal : RooTerminal ) {
1516 super ( )
@@ -54,15 +55,20 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
5455 // When using shell: true, the PID is for the shell, not the actual command
5556 // Find the actual command PID after a small delay
5657 if ( this . pid ) {
57- setTimeout ( ( ) => {
58- psTree ( this . pid ! , ( err , children ) => {
59- if ( ! err && children . length > 0 ) {
60- // Update PID to the first child (the actual command)
61- const actualPid = parseInt ( children [ 0 ] . PID )
62- this . pid = actualPid
63- }
64- } )
65- } , 100 )
58+ this . pidUpdatePromise = new Promise < void > ( ( resolve ) => {
59+ setTimeout ( ( ) => {
60+ psTree ( this . pid ! , ( err , children ) => {
61+ if ( ! err && children . length > 0 ) {
62+ // Update PID to the first child (the actual command)
63+ const actualPid = parseInt ( children [ 0 ] . PID )
64+ if ( ! isNaN ( actualPid ) ) {
65+ this . pid = actualPid
66+ }
67+ }
68+ resolve ( )
69+ } )
70+ } , 100 )
71+ } )
6672 }
6773
6874 const rawStream = this . subprocess . iterable ( { from : "all" , preserveNewlines : true } )
@@ -74,7 +80,7 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
7480 }
7581 } ) ( )
7682
77- this . terminal . setActiveStream ( stream , this . subprocess . pid )
83+ this . terminal . setActiveStream ( stream , this . pid )
7884
7985 for await ( const line of stream ) {
8086 if ( this . aborted ) {
@@ -133,6 +139,7 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
133139
134140 this . emit ( "shell_execution_complete" , { exitCode : 1 } )
135141 }
142+ this . subprocess = undefined
136143 }
137144
138145 this . terminal . setActiveStream ( undefined )
@@ -152,23 +159,40 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
152159 public override abort ( ) {
153160 this . aborted = true
154161
155- // Try to kill using the subprocess object
156- if ( this . subprocess ) {
157- try {
158- this . subprocess . kill ( "SIGKILL" )
159- } catch ( e ) {
160- // Ignore errors, will try direct kill below
162+ // Function to perform the kill operations
163+ const performKill = ( ) => {
164+ // Try to kill using the subprocess object
165+ if ( this . subprocess ) {
166+ try {
167+ this . subprocess . kill ( "SIGKILL" )
168+ } catch ( e ) {
169+ console . warn (
170+ `[ExecaTerminalProcess#abort] Failed to kill subprocess: ${ e instanceof Error ? e . message : String ( e ) } ` ,
171+ )
172+ }
161173 }
162- }
163174
164- // Kill the stored PID (which should be the actual command after our update)
165- if ( this . pid ) {
166- try {
167- process . kill ( this . pid , "SIGKILL" )
168- } catch ( e ) {
169- // Process might already be dead
175+ // Kill the stored PID (which should be the actual command after our update)
176+ if ( this . pid ) {
177+ try {
178+ process . kill ( this . pid , "SIGKILL" )
179+ } catch ( e ) {
180+ console . warn (
181+ `[ExecaTerminalProcess#abort] Failed to kill process ${ this . pid } : ${ e instanceof Error ? e . message : String ( e ) } ` ,
182+ )
183+ }
170184 }
185+ }
171186
187+ // If PID update is in progress, wait for it before killing
188+ if ( this . pidUpdatePromise ) {
189+ this . pidUpdatePromise . then ( performKill ) . catch ( ( ) => performKill ( ) )
190+ } else {
191+ performKill ( )
192+ }
193+
194+ // Continue with the rest of the abort logic
195+ if ( this . pid ) {
172196 // Also check for any child processes
173197 psTree ( this . pid , async ( err , children ) => {
174198 if ( ! err ) {
0 commit comments