mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-10 03:14:32 +08:00
fix(codeagent-wrapper): add timeout for Windows process termination
- Add forceKillWaitTimeout (5s) to prevent cmd.Wait() blocking forever - Enhance sendTermSignal with killProcessTree fallback using wmic - Update omo README: remove sisyphus, fix model names, update config Fixes #115 Generated with SWE-Agent.ai Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
)
|
||||
|
||||
const postMessageTerminateDelay = 1 * time.Second
|
||||
const forceKillWaitTimeout = 5 * time.Second
|
||||
|
||||
// commandRunner abstracts exec.Cmd for testability
|
||||
type commandRunner interface {
|
||||
@@ -1109,7 +1110,8 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
|
||||
waitLoop:
|
||||
for {
|
||||
select {
|
||||
case waitErr = <-waitCh:
|
||||
case err := <-waitCh:
|
||||
waitErr = err
|
||||
break waitLoop
|
||||
case <-ctx.Done():
|
||||
ctxCancelled = true
|
||||
@@ -1120,8 +1122,17 @@ waitLoop:
|
||||
terminated = true
|
||||
}
|
||||
}
|
||||
waitErr = <-waitCh
|
||||
break waitLoop
|
||||
for {
|
||||
select {
|
||||
case err := <-waitCh:
|
||||
waitErr = err
|
||||
break waitLoop
|
||||
case <-time.After(forceKillWaitTimeout):
|
||||
if proc := cmd.Process(); proc != nil {
|
||||
_ = proc.Kill()
|
||||
}
|
||||
}
|
||||
}
|
||||
case <-messageTimerCh:
|
||||
forcedAfterComplete = true
|
||||
messageTimerCh = nil
|
||||
@@ -1135,8 +1146,17 @@ waitLoop:
|
||||
// Close pipes to unblock stream readers, then wait for process exit.
|
||||
closeWithReason(stdout, "terminate")
|
||||
closeWithReason(stderr, "terminate")
|
||||
waitErr = <-waitCh
|
||||
break waitLoop
|
||||
for {
|
||||
select {
|
||||
case err := <-waitCh:
|
||||
waitErr = err
|
||||
break waitLoop
|
||||
case <-time.After(forceKillWaitTimeout):
|
||||
if proc := cmd.Process(); proc != nil {
|
||||
_ = proc.Kill()
|
||||
}
|
||||
}
|
||||
}
|
||||
case <-completeSeen:
|
||||
completeSeenObserved = true
|
||||
if messageTimer != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// sendTermSignal on Windows directly kills the process.
|
||||
@@ -31,6 +32,56 @@ func sendTermSignal(proc processHandle) error {
|
||||
if err := cmd.Run(); err == nil {
|
||||
return nil
|
||||
}
|
||||
if err := killProcessTree(pid); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return proc.Kill()
|
||||
}
|
||||
|
||||
func killProcessTree(pid int) error {
|
||||
if pid <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
wmic := "wmic"
|
||||
if root := os.Getenv("SystemRoot"); root != "" {
|
||||
wmic = filepath.Join(root, "System32", "wbem", "WMIC.exe")
|
||||
}
|
||||
|
||||
queryChildren := "(ParentProcessId=" + strconv.Itoa(pid) + ")"
|
||||
listCmd := exec.Command(wmic, "process", "where", queryChildren, "get", "ProcessId", "/VALUE")
|
||||
listCmd.Stderr = io.Discard
|
||||
out, err := listCmd.Output()
|
||||
if err == nil {
|
||||
for _, childPID := range parseWMICPIDs(out) {
|
||||
_ = killProcessTree(childPID)
|
||||
}
|
||||
}
|
||||
|
||||
querySelf := "(ProcessId=" + strconv.Itoa(pid) + ")"
|
||||
termCmd := exec.Command(wmic, "process", "where", querySelf, "call", "terminate")
|
||||
termCmd.Stdout = io.Discard
|
||||
termCmd.Stderr = io.Discard
|
||||
if termErr := termCmd.Run(); termErr != nil && err == nil {
|
||||
err = termErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func parseWMICPIDs(out []byte) []int {
|
||||
const prefix = "ProcessId="
|
||||
var pids []int
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if !strings.HasPrefix(line, prefix) {
|
||||
continue
|
||||
}
|
||||
n, err := strconv.Atoi(strings.TrimSpace(strings.TrimPrefix(line, prefix)))
|
||||
if err != nil || n <= 0 {
|
||||
continue
|
||||
}
|
||||
pids = append(pids, n)
|
||||
}
|
||||
return pids
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user