Skip to content

Commit 8e3de5d

Browse files
committed
fix: add quotePath to buildTerminateCommand for SSH tilde support
buildTerminateCommand now accepts optional quotePath parameter (like buildSpawnCommand) to avoid double-quoting when used with SSH paths. SSHBackgroundHandle.terminate() passes raw path + expandTildeForSSH instead of pre-expanded path that would get quoted again by shellQuote.
1 parent c8f723a commit 8e3de5d

File tree

3 files changed

+31
-5
lines changed

3 files changed

+31
-5
lines changed

src/node/runtime/SSHBackgroundHandle.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ export class SSHBackgroundHandle implements BackgroundHandle {
6161

6262
try {
6363
// Use shared buildTerminateCommand for parity with Local
64-
// Must expand tilde - buildTerminateCommand uses shellQuote which prevents expansion
65-
const exitCodePath = expandTildeForSSH(`${this.outputDir}/exit_code`);
66-
const terminateCmd = buildTerminateCommand(this.pid, exitCodePath);
64+
// Pass raw path + expandTildeForSSH to avoid double-quoting
65+
// (expandTildeForSSH returns quoted strings, buildTerminateCommand would quote again)
66+
const exitCodePath = `${this.outputDir}/exit_code`;
67+
const terminateCmd = buildTerminateCommand(this.pid, exitCodePath, expandTildeForSSH);
6768
await execBuffered(this.sshRuntime, terminateCmd, {
6869
cwd: "/",
6970
timeout: 15,

src/node/runtime/backgroundCommands.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,23 @@ describe("backgroundCommands", () => {
249249

250250
expect(result).toContain("'/tmp/my dir/exit_code'");
251251
});
252+
253+
it("should use custom quotePath function when provided", () => {
254+
// Simulate expandTildeForSSH behavior (returns double-quoted string)
255+
const customQuote = (p: string) => `"${p}"`;
256+
const result = buildTerminateCommand(1234, "/tmp/exit_code", customQuote);
257+
258+
expect(result).toContain('"/tmp/exit_code"');
259+
expect(result).not.toContain("'/tmp/exit_code'");
260+
});
261+
262+
it("should handle tilde paths with custom quotePath", () => {
263+
// Simulate expandTildeForSSH("~/mux/exit_code") → "$HOME/mux/exit_code"
264+
const expandTilde = (p: string) => (p.startsWith("~/") ? `"$HOME/${p.slice(2)}"` : `"${p}"`);
265+
const result = buildTerminateCommand(1234, "~/mux/exit_code", expandTilde);
266+
267+
expect(result).toContain('"$HOME/mux/exit_code"');
268+
});
252269
});
253270

254271
describe("parseExitCode", () => {

src/node/runtime/backgroundCommands.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,23 @@ export function buildSpawnCommand(options: SpawnCommandOptions): string {
116116
* Uses negative PID to kill entire process group (setsid makes process a group leader).
117117
* Sends SIGTERM, waits 2 seconds, then SIGKILL if still running.
118118
* Writes EXIT_CODE_SIGKILL on force kill.
119+
*
120+
* @param pid - Process ID to terminate
121+
* @param exitCodePath - Path to write exit code (raw, will be quoted by quotePath)
122+
* @param quotePath - Function to quote path (default: shellQuote). Use expandTildeForSSH for SSH.
119123
*/
120-
export function buildTerminateCommand(pid: number, exitCodePath: string): string {
124+
export function buildTerminateCommand(
125+
pid: number,
126+
exitCodePath: string,
127+
quotePath: (p: string) => string = shellQuote
128+
): string {
121129
const pgid = -pid;
122130
return (
123131
`kill -TERM ${pgid} 2>/dev/null || true; ` +
124132
`sleep 2; ` +
125133
`if kill -0 ${pid} 2>/dev/null; then ` +
126134
`kill -KILL ${pgid} 2>/dev/null || true; ` +
127-
`echo ${EXIT_CODE_SIGKILL} > ${shellQuote(exitCodePath)}; ` +
135+
`echo ${EXIT_CODE_SIGKILL} > ${quotePath(exitCodePath)}; ` +
128136
`fi`
129137
);
130138
}

0 commit comments

Comments
 (0)