Add chat-friendly shell read rendering
Make workspace shell reads usable as direct chat-model input without changing the PTY or cursor model. This adds optional plain rendering and idle-window batching across CLI, SDK, and MCP while keeping raw reads backward-compatible. Implement the rendering and wait-for-idle logic in the manager layer so the existing guest/backend shell transport stays unchanged. The new helper strips ANSI and other terminal control noise, handles carriage-return overwrite and backspace, and preserves raw cursor semantics even when plain output is requested. Refresh the stable shell docs/examples to recommend --plain --wait-for-idle-ms 300, mark the 3.5.0 roadmap milestone done, and bump the package/catalog version to 3.5.0. Validation: uv lock; UV_CACHE_DIR=.uv-cache make check; UV_CACHE_DIR=.uv-cache make dist-check; real guest-backed Firecracker smoke covering shell open/write/read with ANSI plus delayed output.
This commit is contained in:
parent
eecfd7a7d7
commit
21a88312b6
22 changed files with 539 additions and 45 deletions
|
|
@ -278,6 +278,8 @@ def test_cli_subcommand_help_includes_examples_and_guidance() -> None:
|
|||
_subparser_choice(_subparser_choice(parser, "workspace"), "shell"), "read"
|
||||
).format_help()
|
||||
assert "Shell output is written to stdout." in workspace_shell_read_help
|
||||
assert "--plain" in workspace_shell_read_help
|
||||
assert "--wait-for-idle-ms" in workspace_shell_read_help
|
||||
|
||||
workspace_shell_write_help = _subparser_choice(
|
||||
_subparser_choice(_subparser_choice(parser, "workspace"), "shell"), "write"
|
||||
|
|
@ -2259,11 +2261,15 @@ def test_cli_workspace_shell_open_and_read_human(
|
|||
*,
|
||||
cursor: int,
|
||||
max_chars: int,
|
||||
plain: bool = False,
|
||||
wait_for_idle_ms: int | None = None,
|
||||
) -> dict[str, Any]:
|
||||
assert workspace_id == "workspace-123"
|
||||
assert shell_id == "shell-123"
|
||||
assert cursor == 0
|
||||
assert max_chars == 1024
|
||||
assert plain is True
|
||||
assert wait_for_idle_ms == 300
|
||||
return {
|
||||
"workspace_id": workspace_id,
|
||||
"shell_id": shell_id,
|
||||
|
|
@ -2279,6 +2285,8 @@ def test_cli_workspace_shell_open_and_read_human(
|
|||
"next_cursor": 14,
|
||||
"output": "pyro$ pwd\n",
|
||||
"truncated": False,
|
||||
"plain": plain,
|
||||
"wait_for_idle_ms": wait_for_idle_ms,
|
||||
}
|
||||
|
||||
class OpenParser:
|
||||
|
|
@ -2309,6 +2317,8 @@ def test_cli_workspace_shell_open_and_read_human(
|
|||
shell_id="shell-123",
|
||||
cursor=0,
|
||||
max_chars=1024,
|
||||
plain=True,
|
||||
wait_for_idle_ms=300,
|
||||
json=False,
|
||||
)
|
||||
|
||||
|
|
@ -2319,6 +2329,8 @@ def test_cli_workspace_shell_open_and_read_human(
|
|||
assert "pyro$ pwd\n" in captured.out
|
||||
assert "[workspace-shell-open] workspace_id=workspace-123 shell_id=shell-123" in captured.err
|
||||
assert "[workspace-shell-read] workspace_id=workspace-123 shell_id=shell-123" in captured.err
|
||||
assert "plain=True" in captured.err
|
||||
assert "wait_for_idle_ms=300" in captured.err
|
||||
|
||||
|
||||
def test_cli_workspace_shell_write_signal_close_json(
|
||||
|
|
@ -2478,7 +2490,11 @@ def test_cli_workspace_shell_open_and_read_json(
|
|||
*,
|
||||
cursor: int,
|
||||
max_chars: int,
|
||||
plain: bool = False,
|
||||
wait_for_idle_ms: int | None = None,
|
||||
) -> dict[str, Any]:
|
||||
assert plain is False
|
||||
assert wait_for_idle_ms is None
|
||||
return {
|
||||
"workspace_id": workspace_id,
|
||||
"shell_id": shell_id,
|
||||
|
|
@ -2494,6 +2510,8 @@ def test_cli_workspace_shell_open_and_read_json(
|
|||
"next_cursor": max_chars,
|
||||
"output": "pyro$ pwd\n",
|
||||
"truncated": False,
|
||||
"plain": plain,
|
||||
"wait_for_idle_ms": wait_for_idle_ms,
|
||||
}
|
||||
|
||||
class OpenParser:
|
||||
|
|
@ -2526,6 +2544,8 @@ def test_cli_workspace_shell_open_and_read_json(
|
|||
shell_id="shell-123",
|
||||
cursor=0,
|
||||
max_chars=1024,
|
||||
plain=False,
|
||||
wait_for_idle_ms=None,
|
||||
json=True,
|
||||
)
|
||||
|
||||
|
|
@ -2655,7 +2675,16 @@ def test_cli_workspace_shell_write_signal_close_human(
|
|||
("shell_command", "kwargs"),
|
||||
[
|
||||
("open", {"cwd": "/workspace", "cols": 120, "rows": 30}),
|
||||
("read", {"shell_id": "shell-123", "cursor": 0, "max_chars": 1024}),
|
||||
(
|
||||
"read",
|
||||
{
|
||||
"shell_id": "shell-123",
|
||||
"cursor": 0,
|
||||
"max_chars": 1024,
|
||||
"plain": False,
|
||||
"wait_for_idle_ms": None,
|
||||
},
|
||||
),
|
||||
("write", {"shell_id": "shell-123", "input": "pwd", "no_newline": False}),
|
||||
("signal", {"shell_id": "shell-123", "signal": "INT"}),
|
||||
("close", {"shell_id": "shell-123"}),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue