@@ -91,21 +91,10 @@ export class SSHRuntime implements Runtime {
9191 parts . push ( command ) ;
9292
9393 // Join all parts with && to ensure each step succeeds before continuing
94- let fullCommand = parts . join ( " && " ) ;
95-
96- // Wrap command with timeout if specified
97- // This ensures the remote command is killed even if the SSH connection persists
98- // (e.g., due to ControlMaster multiplexing)
99- if ( options . timeout !== undefined ) {
100- // Use timeout command with KILL signal after timeout expires
101- // The timeout value is slightly longer than our local timeout to ensure
102- // the local timeout fires first in normal cases
103- const remoteTimeout = Math . ceil ( options . timeout ) + 1 ;
104- fullCommand = `timeout --signal=KILL ${ remoteTimeout } bash -c ${ shescape . quote ( fullCommand ) } ` ;
105- }
94+ const fullCommand = parts . join ( " && " ) ;
10695
10796 // Wrap in bash -c with shescape for safe shell execution
108- const remoteCommand = options . timeout ? fullCommand : `bash -c ${ shescape . quote ( fullCommand ) } ` ;
97+ const remoteCommand = `bash -c ${ shescape . quote ( fullCommand ) } ` ;
10998
11099 // Build SSH args
111100 const sshArgs : string [ ] = [ "-T" ] ;
@@ -129,9 +118,19 @@ export class SSHRuntime implements Runtime {
129118 // ControlMaster=auto: Create master connection if none exists, otherwise reuse
130119 // ControlPath: Unix socket path for multiplexing
131120 // ControlPersist=60: Keep master connection alive for 60s after last session
132- sshArgs . push ( "-o" , "ControlMaster=auto" ) ;
133- sshArgs . push ( "-o" , `ControlPath=${ this . controlPath } ` ) ;
134- sshArgs . push ( "-o" , "ControlPersist=60" ) ;
121+ //
122+ // IMPORTANT: Disable multiplexing when timeout is set, because:
123+ // - Killing an SSH client with multiplexing doesn't kill the remote command
124+ // - The remote command continues running under the master connection
125+ // - Without multiplexing, killing SSH reliably terminates the remote command
126+ if ( options . timeout === undefined ) {
127+ sshArgs . push ( "-o" , "ControlMaster=auto" ) ;
128+ sshArgs . push ( "-o" , `ControlPath=${ this . controlPath } ` ) ;
129+ sshArgs . push ( "-o" , "ControlPersist=60" ) ;
130+ } else {
131+ // Explicitly disable multiplexing for timed commands
132+ sshArgs . push ( "-o" , "ControlMaster=no" ) ;
133+ }
135134
136135 // Set comprehensive timeout options to ensure SSH respects the timeout
137136 // ConnectTimeout: Maximum time to wait for connection establishment (DNS, TCP handshake, SSH auth)
0 commit comments