mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-30 05:43:55 +00:00
fix: kill orphaned MCP child processes and expose OPENCODE_PID on shu… (#15516)
This commit is contained in:
parent
38704acacd
commit
c4c0b23bff
@ -145,6 +145,7 @@ try {
|
||||
Object.assign(process.env, serverEnv)
|
||||
process.env.AGENT = "1"
|
||||
process.env.OPENCODE = "1"
|
||||
process.env.OPENCODE_PID = String(process.pid)
|
||||
|
||||
const log = await import("../../opencode/src/util/log")
|
||||
const install = await import("../../opencode/src/installation")
|
||||
|
||||
@ -76,6 +76,7 @@ let cli = yargs(hideBin(process.argv))
|
||||
|
||||
process.env.AGENT = "1"
|
||||
process.env.OPENCODE = "1"
|
||||
process.env.OPENCODE_PID = String(process.pid)
|
||||
|
||||
Log.Default.info("opencode", {
|
||||
version: Installation.VERSION,
|
||||
|
||||
@ -160,6 +160,28 @@ export namespace MCP {
|
||||
return typeof entry === "object" && entry !== null && "type" in entry
|
||||
}
|
||||
|
||||
async function descendants(pid: number): Promise<number[]> {
|
||||
if (process.platform === "win32") return []
|
||||
const pids: number[] = []
|
||||
const queue = [pid]
|
||||
while (queue.length > 0) {
|
||||
const current = queue.shift()!
|
||||
const proc = Bun.spawn(["pgrep", "-P", String(current)], { stdout: "pipe", stderr: "pipe" })
|
||||
const [code, out] = await Promise.all([proc.exited, new Response(proc.stdout).text()]).catch(
|
||||
() => [-1, ""] as const,
|
||||
)
|
||||
if (code !== 0) continue
|
||||
for (const tok of out.trim().split(/\s+/)) {
|
||||
const cpid = parseInt(tok, 10)
|
||||
if (!isNaN(cpid) && pids.indexOf(cpid) === -1) {
|
||||
pids.push(cpid)
|
||||
queue.push(cpid)
|
||||
}
|
||||
}
|
||||
}
|
||||
return pids
|
||||
}
|
||||
|
||||
const state = Instance.state(
|
||||
async () => {
|
||||
const cfg = await Config.get()
|
||||
@ -196,6 +218,21 @@ export namespace MCP {
|
||||
}
|
||||
},
|
||||
async (state) => {
|
||||
// The MCP SDK only signals the direct child process on close.
|
||||
// Servers like chrome-devtools-mcp spawn grandchild processes
|
||||
// (e.g. Chrome) that the SDK never reaches, leaving them orphaned.
|
||||
// Kill the full descendant tree first so the server exits promptly
|
||||
// and no processes are left behind.
|
||||
for (const client of Object.values(state.clients)) {
|
||||
const pid = (client.transport as any)?.pid
|
||||
if (typeof pid !== "number") continue
|
||||
for (const dpid of await descendants(pid)) {
|
||||
try {
|
||||
process.kill(dpid, "SIGTERM")
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
Object.values(state.clients).map((client) =>
|
||||
client.close().catch((error) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user