@@ -25,6 +25,7 @@ const COMMAND_TIMEOUT_MS = readPositiveInt(
2525 120000 ,
2626 "OPENCLAW_SECRET_PROOF_COMMAND_MS" ,
2727) ;
28+ const COMMAND_TIMEOUT_KILL_GRACE_MS = 1000 ;
2829const READY_TIMEOUT_MS = readPositiveInt (
2930 process . env . OPENCLAW_SECRET_PROOF_READY_MS ,
3031 120000 ,
@@ -297,20 +298,27 @@ function runCommand(command, args, options = {}) {
297298 let timedOut = false ;
298299 let aborted = false ;
299300 let killTimer ;
301+ let forceKillAt ;
302+ const armForceKill = ( ) => {
303+ forceKillAt ??= Date . now ( ) + COMMAND_TIMEOUT_KILL_GRACE_MS ;
304+ killTimer ??= setTimeout (
305+ ( ) => terminateProcessTree ( child , "SIGKILL" ) ,
306+ COMMAND_TIMEOUT_KILL_GRACE_MS ,
307+ ) ;
308+ killTimer . unref ( ) ;
309+ } ;
300310 const abort = ( ) => {
301311 if ( usesProcessGroup ? ! processTreeIsAlive ( child ) : childHasExited ( child ) ) {
302312 return ;
303313 }
304314 aborted = true ;
305315 terminateProcessTree ( child , "SIGTERM" ) ;
306- killTimer ??= setTimeout ( ( ) => terminateProcessTree ( child , "SIGKILL" ) , 1000 ) ;
307- killTimer . unref ( ) ;
316+ armForceKill ( ) ;
308317 } ;
309318 const timer = setTimeout ( ( ) => {
310319 timedOut = true ;
311320 terminateProcessTree ( child , "SIGTERM" ) ;
312- killTimer = setTimeout ( ( ) => terminateProcessTree ( child , "SIGKILL" ) , 1000 ) ;
313- killTimer . unref ( ) ;
321+ armForceKill ( ) ;
314322 } , timeoutMs ) ;
315323 const abortSignal = options . signal ;
316324 if ( abortSignal ?. aborted ) {
@@ -347,27 +355,42 @@ function runCommand(command, args, options = {}) {
347355 if ( killTimer ) {
348356 clearTimeout ( killTimer ) ;
349357 }
358+ forceKillAt = undefined ;
350359 abortSignal ?. removeEventListener ( "abort" , abort ) ;
351360 removeParentSignalHandlers ( ) ;
352361 reject ( error instanceof Error ? error : new Error ( formatErrorMessage ( error ) ) ) ;
353362 } ) ;
354363 child . on ( "close" , ( code , signal ) => {
355364 clearTimeout ( timer ) ;
356- if ( killTimer ) {
357- clearTimeout ( killTimer ) ;
358- }
359365 abortSignal ?. removeEventListener ( "abort" , abort ) ;
360366 removeParentSignalHandlers ( ) ;
361367 const result = { code, signal, stdout : stdout . text ( ) , stderr : stderr . text ( ) } ;
368+ const finishTerminatedTree = async ( ) => {
369+ await finishTimedOutCommandProcessTree ( child , {
370+ forceKillAt,
371+ timeoutKillGraceMs : COMMAND_TIMEOUT_KILL_GRACE_MS ,
372+ } ) ;
373+ if ( killTimer ) {
374+ clearTimeout ( killTimer ) ;
375+ }
376+ forceKillAt = undefined ;
377+ } ;
362378 if ( aborted ) {
363- reject ( new Error ( scrub ( `command aborted: ${ command } ${ args . join ( " " ) } ` ) ) ) ;
379+ void finishTerminatedTree ( ) . finally ( ( ) =>
380+ reject ( new Error ( scrub ( `command aborted: ${ command } ${ args . join ( " " ) } ` ) ) ) ,
381+ ) ;
364382 return ;
365383 }
366384 if ( timedOut ) {
367- terminateProcessTree ( child , "SIGKILL" ) ;
368- reject ( new Error ( scrub ( `command timed out: ${ command } ${ args . join ( " " ) } ` ) ) ) ;
385+ void finishTerminatedTree ( ) . finally ( ( ) =>
386+ reject ( new Error ( scrub ( `command timed out: ${ command } ${ args . join ( " " ) } ` ) ) ) ,
387+ ) ;
369388 return ;
370389 }
390+ if ( killTimer ) {
391+ clearTimeout ( killTimer ) ;
392+ }
393+ forceKillAt = undefined ;
371394 if ( result . signal && options . allowFailure !== true ) {
372395 reject (
373396 new Error (
@@ -880,6 +903,21 @@ async function stopGateway(child) {
880903 await waitForProcessTreeExit ( child , 1000 ) ;
881904}
882905
906+ async function finishTimedOutCommandProcessTree ( child , { forceKillAt, timeoutKillGraceMs } ) {
907+ if ( ! processTreeIsAlive ( child ) ) {
908+ return ;
909+ }
910+ const graceRemainingMs =
911+ forceKillAt === undefined ? timeoutKillGraceMs : Math . max ( 0 , forceKillAt - Date . now ( ) ) ;
912+ if ( graceRemainingMs > 0 ) {
913+ await waitForProcessTreeExit ( child , graceRemainingMs ) ;
914+ }
915+ if ( processTreeIsAlive ( child ) ) {
916+ terminateProcessTree ( child , "SIGKILL" ) ;
917+ }
918+ await waitForProcessTreeExit ( child , timeoutKillGraceMs ) ;
919+ }
920+
883921function childHasExited ( child ) {
884922 return child . exitCode !== null || child . signalCode !== null ;
885923}
0 commit comments