From 897ca6abbb186a59ea575bb18a8503b38b7b27e3 Mon Sep 17 00:00:00 2001 From: "clawsweeper[bot]" <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:43:09 -0700 Subject: [PATCH] fix: Windows-specific reliability gap in the new timeout cleanup path (#74703) Co-authored-by: openclaw-clawsweeper[bot] <280122609+openclaw-clawsweeper[bot]@users.noreply.github.com> --- scripts/docs-i18n/codex_command_windows.go | 29 ++++++++++- .../docs-i18n/codex_command_windows_test.go | 49 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 scripts/docs-i18n/codex_command_windows_test.go diff --git a/scripts/docs-i18n/codex_command_windows.go b/scripts/docs-i18n/codex_command_windows.go index ffe638ae4d4..5f661be346c 100644 --- a/scripts/docs-i18n/codex_command_windows.go +++ b/scripts/docs-i18n/codex_command_windows.go @@ -2,8 +2,35 @@ package main -import "os/exec" +import ( + "context" + "errors" + "os" + "os/exec" + "strconv" +) + +var runWindowsTaskkill = func(pid int) error { + ctx, cancel := context.WithTimeout(context.Background(), docsI18nCommandWaitDelay()) + defer cancel() + return exec.CommandContext(ctx, "taskkill.exe", "/T", "/F", "/PID", strconv.Itoa(pid)).Run() +} func configureCodexPromptCommand(command *exec.Cmd) { + command.Cancel = func() error { + if command.Process == nil { + return os.ErrProcessDone + } + if err := runWindowsTaskkill(command.Process.Pid); err != nil { + killErr := command.Process.Kill() + if errors.Is(killErr, os.ErrProcessDone) { + return os.ErrProcessDone + } + if killErr != nil { + return errors.Join(err, killErr) + } + } + return nil + } command.WaitDelay = docsI18nCommandWaitDelay() } diff --git a/scripts/docs-i18n/codex_command_windows_test.go b/scripts/docs-i18n/codex_command_windows_test.go new file mode 100644 index 00000000000..cb8287aaa51 --- /dev/null +++ b/scripts/docs-i18n/codex_command_windows_test.go @@ -0,0 +1,49 @@ +//go:build windows + +package main + +import ( + "errors" + "os" + "os/exec" + "testing" + "time" +) + +func TestConfigureCodexPromptCommandWindowsCancelsProcessTree(t *testing.T) { + t.Setenv(envDocsI18nCommandWaitDelay, "25ms") + previousRunTaskkill := runWindowsTaskkill + defer func() { runWindowsTaskkill = previousRunTaskkill }() + + var gotPID int + runWindowsTaskkill = func(pid int) error { + gotPID = pid + return nil + } + + command := exec.Command("codex") + configureCodexPromptCommand(command) + command.Process = &os.Process{Pid: 1234} + + if command.WaitDelay != 25*time.Millisecond { + t.Fatalf("expected WaitDelay override, got %s", command.WaitDelay) + } + if command.Cancel == nil { + t.Fatal("expected Cancel to be configured") + } + if err := command.Cancel(); err != nil { + t.Fatalf("Cancel returned error: %v", err) + } + if gotPID != 1234 { + t.Fatalf("expected taskkill for pid 1234, got %d", gotPID) + } +} + +func TestConfigureCodexPromptCommandWindowsCancelBeforeStart(t *testing.T) { + command := exec.Command("codex") + configureCodexPromptCommand(command) + + if err := command.Cancel(); !errors.Is(err, os.ErrProcessDone) { + t.Fatalf("expected os.ErrProcessDone, got %v", err) + } +}