diff --git a/nanobot/apps/cli/service.py b/nanobot/apps/cli/service.py index 90c0e099..8c5d6391 100644 --- a/nanobot/apps/cli/service.py +++ b/nanobot/apps/cli/service.py @@ -961,6 +961,8 @@ class CliAppManager: argv, capture_output=True, text=True, + encoding="utf-8", + errors="replace", timeout=timeout, ) logger.info("CLI Apps: command exited with code {}: {}", result.returncode, command) @@ -1364,6 +1366,8 @@ Use the `run_cli_app` tool with `name="{name}"` for command execution. Do not in cwd=str(cwd), capture_output=True, text=True, + encoding="utf-8", + errors="replace", timeout=effective_timeout, env=os.environ.copy(), ) diff --git a/tests/cli_apps/test_service.py b/tests/cli_apps/test_service.py index 9a8d60be..7b0a312a 100644 --- a/tests/cli_apps/test_service.py +++ b/tests/cli_apps/test_service.py @@ -423,10 +423,14 @@ def test_run_argv_logs_command_exit_and_output( *, capture_output: bool, text: bool, + encoding: str, + errors: str, timeout: int, ) -> subprocess.CompletedProcess[str]: assert capture_output is True assert text is True + assert encoding == "utf-8" + assert errors == "replace" assert timeout == 5 return subprocess.CompletedProcess(argv, 0, stdout="installed ok", stderr="") @@ -441,6 +445,22 @@ def test_run_argv_logs_command_exit_and_output( assert any("installed ok" in record for record in records) +def test_run_argv_decodes_utf8_output(tmp_path: Path) -> None: + manager = _manager(tmp_path) + + result = manager._run_argv( + [ + sys.executable, + "-c", + "import sys; sys.stdout.buffer.write(chr(0x2713).encode('utf-8'))", + ], + timeout=5, + ) + + assert result.returncode == 0 + assert result.stdout == "\u2713" + + def test_install_records_available_cli_without_reinstalling( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -863,6 +883,9 @@ def test_run_installed_cli_uses_argv_without_shell( def fake_run(argv: list[str], **kwargs: object) -> subprocess.CompletedProcess[str]: assert "shell" not in kwargs or kwargs["shell"] is False + assert kwargs["text"] is True + assert kwargs["encoding"] == "utf-8" + assert kwargs["errors"] == "replace" return subprocess.CompletedProcess( argv, 0,