From 8cff1f87ee288c1e4d6d683d80c5f41689f2565a Mon Sep 17 00:00:00 2001 From: Dividesbyzer0 <54127744+zoomdbz@users.noreply.github.com> Date: Mon, 15 Jun 2026 02:12:48 -0400 Subject: [PATCH] fix(cookbook): stop local Windows process trees Track the inner Bash runner PID for local Windows Cookbook tasks and stop the full child process tree during cleanup. --- routes/cookbook_routes.py | 6 +++++- static/js/cookbookRunning.js | 5 +++-- tests/test_cookbook_helpers.py | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/routes/cookbook_routes.py b/routes/cookbook_routes.py index 320b17780..cfbb514ac 100644 --- a/routes/cookbook_routes.py +++ b/routes/cookbook_routes.py @@ -354,7 +354,11 @@ def setup_cookbook_routes() -> APIRouter: # all output to the log the poller reads. Paths handed to bash use # POSIX form + shell-quoting so drive paths / spaces survive. inner = TMUX_LOG_DIR / f"{session_id}_run.sh" - inner.write_text("\n".join(bash_lines) + "\n", encoding="utf-8") + pp = shlex.quote(pid_path.as_posix()) + inner.write_text( + f"printf '%s\\n' \"$$\" > {pp}\n" + "\n".join(bash_lines) + "\n", + encoding="utf-8", + ) lp = shlex.quote(log_path.as_posix()) ip = shlex.quote(inner.as_posix()) script_path = TMUX_LOG_DIR / f"{session_id}.sh" diff --git a/static/js/cookbookRunning.js b/static/js/cookbookRunning.js index 06b557c1c..47f7a1b62 100644 --- a/static/js/cookbookRunning.js +++ b/static/js/cookbookRunning.js @@ -793,9 +793,10 @@ function _winSessionCmd(task, tmuxArgs) { return host ? `ssh ${pf}${host} "powershell -Command \\"${ps}\\""` : `powershell -Command "${ps}"`; } if (tmuxArgs.includes('kill-session')) { + const stopTree = `function Stop-Tree([int]$Id) { Get-CimInstance Win32_Process -Filter "ParentProcessId = $Id" -ErrorAction SilentlyContinue | ForEach-Object { Stop-Tree ([int]$_.ProcessId) }; Stop-Process -Id $Id -Force -ErrorAction SilentlyContinue }`; const ps = host - ? `$p = Get-Content '${sd}\\${sid}.pid' -ErrorAction SilentlyContinue; if ($p) { Stop-Process -Id $p -Force -ErrorAction SilentlyContinue }; Remove-Item '${sd}\\${sid}.*' -Force -ErrorAction SilentlyContinue` - : `$p = Get-Content (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.pid') -ErrorAction SilentlyContinue; if ($p) { Stop-Process -Id $p -Force -ErrorAction SilentlyContinue }; Remove-Item (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.*') -Force -ErrorAction SilentlyContinue`; + ? `${stopTree}; $p = Get-Content '${sd}\\${sid}.pid' -ErrorAction SilentlyContinue; if ($p -match '^\\d+$') { Stop-Tree ([int]$p) }; Remove-Item '${sd}\\${sid}.*' -Force -ErrorAction SilentlyContinue` + : `${stopTree}; $p = Get-Content (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.pid') -ErrorAction SilentlyContinue; if ($p -match '^\\d+$') { Stop-Tree ([int]$p) }; Remove-Item (Join-Path $env:TEMP 'odysseus-tmux\\${sid}.*') -Force -ErrorAction SilentlyContinue`; return host ? `ssh ${pf}${host} "powershell -Command \\"${ps}\\""` : `powershell -Command "${ps}"`; } if (tmuxArgs.includes('send-keys') && tmuxArgs.includes('C-c')) { diff --git a/tests/test_cookbook_helpers.py b/tests/test_cookbook_helpers.py index 1259132cd..a02de24c0 100644 --- a/tests/test_cookbook_helpers.py +++ b/tests/test_cookbook_helpers.py @@ -706,6 +706,16 @@ def test_llama_cpp_rebuild_cmd_clears_cached_build_paths(): assert 'curl' not in cmd and 'wget' not in cmd +def test_local_windows_download_pid_tracks_inner_bash_and_stop_kills_tree(): + routes_src = (Path(__file__).resolve().parents[1] / "routes" / "cookbook_routes.py").read_text(encoding="utf-8") + running_src = (Path(__file__).resolve().parents[1] / "static" / "js" / "cookbookRunning.js").read_text(encoding="utf-8") + + assert 'printf \'%s\\\\n\' \\"$$\\" > {pp}' in routes_src + assert "function Stop-Tree([int]$Id)" in running_src + assert "ParentProcessId = $Id" in running_src + assert "Stop-Tree ([int]$p)" in running_src + + def test_llama_cpp_rebuild_cmd_runs_clean_on_a_fresh_home(tmp_path): """The command should succeed even when neither path exists yet.""" import os