fix(coding-agent): fail loudly when no POSIX bash exists; add stdin-isolation regression test

This commit is contained in:
metaphorics
2026-08-05 10:42:36 +00:00
parent f03581dc22
commit 85177a29ec
2 changed files with 27 additions and 1 deletions
+7 -1
View File
@@ -759,7 +759,13 @@ def _resolve_bash() -> str:
found = shutil.which("bash")
if found and "system32" not in found.lower():
return found
return "bash"
# WSL's System32 bash.exe runs in a separate Linux environment, so
# silently falling back to it would execute the cell somewhere the user
# did not intend; fail loudly instead.
raise RuntimeError(
"%%bash requires a POSIX bash, but none was found. "
"Install Git for Windows or add a non-WSL bash to PATH."
)
@cell_magic("bash")
@@ -148,6 +148,26 @@ describe("Python runner shell output streaming", () => {
expect(stdout).not.toContain("capturedChars=1048593");
});
it("isolates !cmd children from the runner's stdin control channel", async () => {
// The runner's stdin carries the host's NDJSON frames. A child that
// inherits it can steal frames or block forever waiting for input;
// with stdin=DEVNULL a stdin-reading child sees immediate EOF instead.
const child = ["import sys", "data = sys.stdin.read()", "print('read=' + repr(data))"].join(";");
const frames = await runCell(
[
`result = !${pythonPath} -c ${shellQuote(child)}`,
"print('return=' + str(result.returncode) + ' lines=' + repr(list(result)))",
].join("\n"),
);
const stdout = frames
.filter(frame => frame.type === "stdout")
.map(frame => frame.data)
.join("");
expect(stdout).toContain("read=''");
expect(stdout).toContain("return=0");
});
it("streams newline-free %%bash output without waiting for EOF", async () => {
const child = [
"import sys,time",