Skip to content

Commit dd3cd7f

Browse files
authored
More robust process killing (#3136)
1 parent 0f97772 commit dd3cd7f

File tree

5 files changed

+132
-4
lines changed

5 files changed

+132
-4
lines changed

.changeset/great-sheep-speak.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
More robust process killing

package-lock.json

Lines changed: 89 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@
417417
"pkce-challenge": "^4.1.0",
418418
"posthog-node": "^4.7.0",
419419
"pretty-bytes": "^6.1.1",
420+
"ps-tree": "^1.2.0",
420421
"puppeteer-chromium-resolver": "^23.0.0",
421422
"puppeteer-core": "^23.4.0",
422423
"reconnecting-eventsource": "^1.6.4",
@@ -449,6 +450,7 @@
449450
"@types/node": "20.x",
450451
"@types/node-cache": "^4.1.3",
451452
"@types/node-ipc": "^9.2.3",
453+
"@types/ps-tree": "^1.1.6",
452454
"@types/string-similarity": "^4.0.2",
453455
"@typescript-eslint/eslint-plugin": "^7.14.1",
454456
"@typescript-eslint/parser": "^7.11.0",

src/core/tools/executeCommandTool.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ export async function executeCommand(
174174
completed = true
175175
},
176176
onShellExecutionStarted: (pid: number | undefined) => {
177+
console.log(`[executeCommand] onShellExecutionStarted: ${pid}`)
177178
const status: CommandExecutionStatus = { executionId, status: "started", pid, command }
178179
clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
179180
},

src/integrations/terminal/ExecaTerminalProcess.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { execa, ExecaError } from "execa"
2+
import psTree from "ps-tree"
3+
import process from "process"
24

35
import type { RooTerminal } from "./types"
46
import { BaseTerminalProcess } from "./BaseTerminalProcess"
57

68
export class ExecaTerminalProcess extends BaseTerminalProcess {
79
private terminalRef: WeakRef<RooTerminal>
8-
private controller?: AbortController
910
private aborted = false
11+
private pid?: number
1012

1113
constructor(terminal: RooTerminal) {
1214
super()
@@ -30,18 +32,17 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
3032

3133
public override async run(command: string) {
3234
this.command = command
33-
this.controller = new AbortController()
3435

3536
try {
3637
this.isHot = true
3738

3839
const subprocess = execa({
3940
shell: true,
4041
cwd: this.terminal.getCurrentWorkingDirectory(),
41-
cancelSignal: this.controller.signal,
4242
all: true,
4343
})`${command}`
4444

45+
this.pid = subprocess.pid
4546
const stream = subprocess.iterable({ from: "all", preserveNewlines: true })
4647
this.terminal.setActiveStream(stream, subprocess.pid)
4748

@@ -116,7 +117,37 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
116117

117118
public override abort() {
118119
this.aborted = true
119-
this.controller?.abort()
120+
121+
if (this.pid) {
122+
psTree(this.pid, async (err, children) => {
123+
if (!err) {
124+
const pids = children.map((p) => parseInt(p.PID))
125+
126+
for (const pid of pids) {
127+
try {
128+
process.kill(pid, "SIGINT")
129+
} catch (e) {
130+
console.warn(
131+
`[ExecaTerminalProcess] Failed to send SIGINT to child PID ${pid}: ${e instanceof Error ? e.message : String(e)}`,
132+
)
133+
// Optionally try SIGTERM or SIGKILL on failure, depending on desired behavior.
134+
}
135+
}
136+
} else {
137+
console.error(
138+
`[ExecaTerminalProcess] Failed to get process tree for PID ${this.pid}: ${err.message}`,
139+
)
140+
}
141+
})
142+
143+
try {
144+
process.kill(this.pid, "SIGINT")
145+
} catch (e) {
146+
console.warn(
147+
`[ExecaTerminalProcess] Failed to send SIGINT to main PID ${this.pid}: ${e instanceof Error ? e.message : String(e)}`,
148+
)
149+
}
150+
}
120151
}
121152

122153
public override hasUnretrievedOutput() {

0 commit comments

Comments
 (0)