diff --git a/app/cli/core/utils.py b/app/cli/core/utils.py index d9f9f08..a0d013b 100644 --- a/app/cli/core/utils.py +++ b/app/cli/core/utils.py @@ -13,6 +13,7 @@ from rich.panel import Panel from rich.table import Table from app.core.errors import FeatureNotReadyError +from app.domain.models import Daemon from app.schemas.auth import AuthTokenResponse from app.schemas.portfolio import CommentResponse, PortfolioDetailResponse, PortfolioListItem from app.schemas.user import UserResponse @@ -163,6 +164,45 @@ def print_user_detail(console: Console, user: UserResponse) -> None: ) +def print_daemon_table(console: Console, daemons: list[Daemon]) -> None: + table = Table(title="Daemons", expand=True) + table.add_column("ID", style="cyan", no_wrap=True) + table.add_column("Slug", no_wrap=True) + table.add_column("Display Name") + table.add_column("Active", no_wrap=True) + table.add_column("Created At", no_wrap=True) + table.add_column("Updated At", no_wrap=True) + for daemon in daemons: + table.add_row( + daemon.id, + daemon.daemon_slug, + daemon.display_name or "-", + "yes" if daemon.is_active else "no", + daemon.created_at.isoformat(timespec="seconds") if daemon.created_at else "N/A", + daemon.updated_at.isoformat(timespec="seconds") if daemon.updated_at else "N/A", + ) + console.print(table) + + +def print_daemon_detail(console: Console, daemon: Daemon) -> None: + console.print( + Panel.fit( + "\n".join( + [ + f"id: {daemon.id}", + f"owner_user_id: {daemon.owner_user_id}", + f"daemon_slug: {daemon.daemon_slug}", + f"display_name: {daemon.display_name or '-'}", + f"is_active: {daemon.is_active}", + f"created_at: {daemon.created_at.isoformat(timespec='seconds') if daemon.created_at else 'N/A'}", + f"updated_at: {daemon.updated_at.isoformat(timespec='seconds') if daemon.updated_at else 'N/A'}", + ] + ), + title="Daemon", + ) + ) + + def print_service_token_table(console: Console, tokens: list[AuthTokenResponse]) -> None: table = Table(title="Service Tokens", expand=True) table.add_column("Name", style="cyan", no_wrap=True) @@ -235,6 +275,9 @@ def render_output(console: Console, output_format: str, payload: Any, *, empty_m if not payload and empty_message: console.print(empty_message) return + if all(isinstance(item, Daemon) for item in payload): + print_daemon_table(console, payload) + return if all(isinstance(item, PortfolioListItem) for item in payload): print_portfolio_table(console, payload) return @@ -257,6 +300,10 @@ def render_output(console: Console, output_format: str, payload: Any, *, empty_m print_user_detail(console, payload) return + if isinstance(payload, Daemon): + print_daemon_detail(console, payload) + return + if isinstance(payload, CommentResponse): print_comment_detail(console, payload) return diff --git a/tests/test_cli.py b/tests/test_cli.py index b35d50a..dd466e2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -903,6 +903,31 @@ def test_cli_help_exposes_bot_token_commands() -> None: assert "revoke" in result.stdout +def test_cli_daemon_create_and_list_render_entries() -> None: + repo = FakeRepo(_portfolio()) + repo.create_user("alice", permission="read") + runner = build_runner(repo) + + create_result = runner.invoke( + runner.app, + ["daemon", "create", "alpha-bot", "--user", "alice", "--display-name", "Alpha Bot"], + ) + list_result = runner.invoke( + runner.app, + ["daemon", "list", "--user", "alice"], + ) + + assert create_result.exit_code == 0 + assert "Daemon" in create_result.stdout + assert "alpha-bot" in create_result.stdout + assert "Alpha Bot" in create_result.stdout + + assert list_result.exit_code == 0 + assert "Daemons" in list_result.stdout + assert "alpha-bot" in list_result.stdout + assert "Alpha Bot" in list_result.stdout + + def test_cli_service_token_issue_supports_json() -> None: repo = FakeRepo(_portfolio()) user = repo.create_user("alice", permission="read")