fix(shell): address Windows backspace and TUI shell regressions
This commit is contained in:
@@ -59,16 +59,7 @@ function MultilineTextInput({
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.backspace || key.delete) {
|
||||
if (key.delete) {
|
||||
if (cursorOffset >= value.length) {
|
||||
return;
|
||||
}
|
||||
const nextValue = value.slice(0, cursorOffset) + value.slice(cursorOffset + 1);
|
||||
onChange(nextValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.backspace) {
|
||||
if (cursorOffset === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -78,6 +69,15 @@ function MultilineTextInput({
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.delete) {
|
||||
if (cursorOffset >= value.length) {
|
||||
return;
|
||||
}
|
||||
const nextValue = value.slice(0, cursorOffset) + value.slice(cursorOffset + 1);
|
||||
onChange(nextValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ class BashTool(BaseTool):
|
||||
arguments.command,
|
||||
cwd=cwd,
|
||||
prefer_pty=True,
|
||||
stdin=asyncio.subprocess.DEVNULL,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
)
|
||||
|
||||
@@ -34,14 +34,14 @@ def resolve_shell_command(
|
||||
if bash:
|
||||
argv = [bash, "-lc", command]
|
||||
if prefer_pty:
|
||||
wrapped = _wrap_command_with_script(argv)
|
||||
wrapped = _wrap_command_with_script(argv, platform_name=resolved_platform)
|
||||
if wrapped is not None:
|
||||
return wrapped
|
||||
return argv
|
||||
shell = shutil.which("sh") or os.environ.get("SHELL") or "/bin/sh"
|
||||
argv = [shell, "-lc", command]
|
||||
if prefer_pty:
|
||||
wrapped = _wrap_command_with_script(argv)
|
||||
wrapped = _wrap_command_with_script(argv, platform_name=resolved_platform)
|
||||
if wrapped is not None:
|
||||
return wrapped
|
||||
return argv
|
||||
@@ -104,7 +104,14 @@ async def create_shell_subprocess(
|
||||
return process
|
||||
|
||||
|
||||
def _wrap_command_with_script(argv: list[str]) -> list[str] | None:
|
||||
def _wrap_command_with_script(
|
||||
argv: list[str],
|
||||
*,
|
||||
platform_name: PlatformName | None = None,
|
||||
) -> list[str] | None:
|
||||
resolved_platform = platform_name or get_platform()
|
||||
if resolved_platform == "macos":
|
||||
return None
|
||||
script = shutil.which("script")
|
||||
if script is None:
|
||||
return None
|
||||
|
||||
@@ -154,3 +154,28 @@ async def test_bash_tool_collects_combined_output(monkeypatch, tmp_path: Path):
|
||||
assert result.is_error is False
|
||||
assert result.output == "line one\nline two"
|
||||
assert result.metadata["returncode"] == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bash_tool_uses_devnull_stdin_for_non_interactive_shell(monkeypatch, tmp_path: Path):
|
||||
process = _FakeProcess(
|
||||
stdout=_FakeStdout([b"ok\n", b""]),
|
||||
returncode=0,
|
||||
)
|
||||
seen_kwargs: dict[str, object] = {}
|
||||
|
||||
async def fake_create_shell_subprocess(*args, **kwargs):
|
||||
del args
|
||||
seen_kwargs.update(kwargs)
|
||||
return process
|
||||
|
||||
monkeypatch.setattr("openharness.tools.bash_tool.create_shell_subprocess", fake_create_shell_subprocess)
|
||||
|
||||
result = await BashTool().execute(
|
||||
BashToolInput(command="echo ok"),
|
||||
ToolExecutionContext(cwd=tmp_path),
|
||||
)
|
||||
|
||||
assert result.is_error is False
|
||||
assert seen_kwargs["stdin"] == asyncio.subprocess.DEVNULL
|
||||
assert seen_kwargs["prefer_pty"] is True
|
||||
|
||||
@@ -49,3 +49,32 @@ def test_resolve_shell_command_uses_powershell_on_windows(monkeypatch):
|
||||
"-Command",
|
||||
"Write-Output hi",
|
||||
]
|
||||
|
||||
|
||||
def test_resolve_shell_command_skips_script_on_macos(monkeypatch):
|
||||
def fake_which(name: str) -> str | None:
|
||||
mapping = {
|
||||
"bash": "/bin/bash",
|
||||
"script": "/usr/bin/script",
|
||||
}
|
||||
return mapping.get(name)
|
||||
|
||||
monkeypatch.setattr("openharness.utils.shell.shutil.which", fake_which)
|
||||
|
||||
command = resolve_shell_command("echo hi", platform_name="macos", prefer_pty=True)
|
||||
|
||||
assert command == ["/bin/bash", "-lc", "echo hi"]
|
||||
|
||||
|
||||
def test_resolve_shell_command_linux_without_script_falls_back(monkeypatch):
|
||||
def fake_which(name: str) -> str | None:
|
||||
mapping = {
|
||||
"bash": "/usr/bin/bash",
|
||||
}
|
||||
return mapping.get(name)
|
||||
|
||||
monkeypatch.setattr("openharness.utils.shell.shutil.which", fake_which)
|
||||
|
||||
command = resolve_shell_command("echo hi", platform_name="linux", prefer_pty=True)
|
||||
|
||||
assert command == ["/usr/bin/bash", "-lc", "echo hi"]
|
||||
|
||||
Reference in New Issue
Block a user