diff --git a/strix/interface/viewer/cli.py b/strix/interface/viewer/cli.py index eb64b8c8..59152cf3 100644 --- a/strix/interface/viewer/cli.py +++ b/strix/interface/viewer/cli.py @@ -87,7 +87,7 @@ def run_view(argv: list[str]) -> None: posthog.viewer_opened(source="cli", live=live) - state_label = "[#eab308]live[/]" if live else "[#22c55e]finished[/]" + state_label = _state_label(summary) console.print() console.print(f"Serving [bold white]{run_name}[/] ({state_label}) at:") # Print the URL alone on its own line with soft_wrap so Rich never inserts a @@ -107,6 +107,18 @@ def run_view(argv: list[str]) -> None: httpd.server_close() +def _state_label(summary: dict[str, object]) -> str: + if not summary.get("finished", False): + return "[#eab308]live[/]" + + status = summary.get("status") + if status == "failed": + return "[#ef4444]failed[/]" + if status in {"stopped", "interrupted"}: + return f"[#eab308]{status}[/]" + return "[#22c55e]finished[/]" + + def _resolve_run_dir(run: str | None, console: Console) -> Path: if run: run_dir = run_dir_for(run) diff --git a/tests/test_viewer.py b/tests/test_viewer.py index 34fa92e6..5fb95b7b 100644 --- a/tests/test_viewer.py +++ b/tests/test_viewer.py @@ -11,7 +11,7 @@ from typing import TYPE_CHECKING from urllib.parse import urlsplit from strix.core.paths import latest_run_dir, runs_base_dir -from strix.interface.viewer.cli import run_view +from strix.interface.viewer.cli import _state_label, run_view from strix.interface.viewer.server import serve from strix.interface.viewer.transcript import ( build_run_state, @@ -115,6 +115,14 @@ def test_read_run_summary_surfaces_mcp_connection_status(tmp_path: Path) -> None assert read_run_summary(run_dir)["mcp_connection_status"] == roster +def test_viewer_cli_labels_terminal_statuses() -> None: + assert _state_label({"status": "completed", "finished": True}) == "[#22c55e]finished[/]" + assert _state_label({"status": "stopped", "finished": True}) == "[#eab308]stopped[/]" + assert _state_label({"status": "interrupted", "finished": True}) == "[#eab308]interrupted[/]" + assert _state_label({"status": "failed", "finished": True}) == "[#ef4444]failed[/]" + assert _state_label({"status": "running", "finished": False}) == "[#eab308]live[/]" + + def test_read_missing_artifacts_return_defaults(tmp_path: Path) -> None: run_dir = _make_run(tmp_path, "empty", status="running", end_time=None) assert read_vulnerabilities(run_dir) == []