MCPcopy Create free account
hub / github.com/agentforce314/clawcodex / _run_bash_with_abort

Function _run_bash_with_abort

src/tool_system/tools/bash/bash_tool.py:62–149  ·  view source on GitHub ↗

Run ``argv`` with abort + timeout supervision. Replaces ``subprocess.run(..., timeout=...)``: launches the subprocess in its own session/process group, polls for completion while watching ``abort_signal.aborted`` and the timeout, and kills the whole group (SIGTERM → grace → SIGKILL)

(
    argv: list[str],
    *,
    cwd: str,
    timeout_s: int,
    abort_signal: Any | None,
)

Source from the content-addressed store, hash-verified

60
61
62def _run_bash_with_abort(
63 argv: list[str],
64 *,
65 cwd: str,
66 timeout_s: int,
67 abort_signal: Any | None,
68) -> _BashRunResult:
69 """Run ``argv`` with abort + timeout supervision.
70
71 Replaces ``subprocess.run(..., timeout=...)``: launches the
72 subprocess in its own session/process group, polls for completion
73 while watching ``abort_signal.aborted`` and the timeout, and kills
74 the whole group (SIGTERM → grace → SIGKILL) when either fires.
75 Returning quickly on abort is what makes ESC feel instant — the
76 previous ``subprocess.run`` had to wait the entire timeout.
77 """
78
79 # ``stdin=DEVNULL`` matches TS ``Shell.ts`` (stdio[0] = 'pipe' with the
80 # writable end never written to). Without this the child inherits the
81 # parent's stdin -- when clawcodex runs in a terminal, that's a TTY, and
82 # scaffolders like ``npm create vite`` see ``isatty(0)`` and try to prompt
83 # for confirmation, hanging the command until timeout.
84 popen_kwargs: dict[str, Any] = {
85 "cwd": cwd,
86 "stdin": subprocess.DEVNULL,
87 "stdout": subprocess.PIPE,
88 "stderr": subprocess.PIPE,
89 "text": True,
90 }
91 if _sys_mod.platform == "win32":
92 popen_kwargs["creationflags"] = getattr(
93 subprocess, "CREATE_NEW_PROCESS_GROUP", 0
94 )
95 else:
96 popen_kwargs["start_new_session"] = True
97
98 proc = subprocess.Popen(argv, **popen_kwargs)
99
100 deadline = _time_mod.monotonic() + timeout_s
101 interrupted = False
102 timed_out = False
103
104 while True:
105 if proc.poll() is not None:
106 break
107 if abort_signal is not None and getattr(abort_signal, "aborted", False):
108 interrupted = True
109 break
110 if _time_mod.monotonic() >= deadline:
111 timed_out = True
112 break
113 _time_mod.sleep(_ABORT_POLL_INTERVAL_S)
114
115 # Mirrors TS ``ShellCommand.ts:337-343`` (``#doKill``): both the
116 # abort and timeout paths actually call ``treeKill(pid, 'SIGKILL')``
117 # — the SIGTERM passed into ``#doKill(SIGTERM)`` from the timeout
118 # handler is a *label* used downstream by ``#handleExit`` to choose
119 # the stderr prefix, not the signal actually sent. Send SIGKILL

Calls 4

_BashRunResultClass · 0.85
_kill_process_groupFunction · 0.70
waitMethod · 0.45
communicateMethod · 0.45