feat(cli): Add UI components#453
Conversation
📝 WalkthroughWalkthroughA new ChangesAgentWatch Rich CLI UI
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🧪 PR Test Results
Python 3.12 · commit 0e07ec6 |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@agentwatch/cli/ui.py`:
- Around line 24-26: The get_real_metrics() function calls a protected API
endpoint at http://localhost:8000/api/v1/dashboard/summary without sending any
authentication credentials. This causes the request to fail with a 401 status
code, preventing the metrics from being retrieved. Add authentication to the
httpx.get() call by including the appropriate auth header or credentials
parameter. Ensure the authentication method matches what the upstream API
endpoint requires, so the request returns 200 with valid data instead of
failing.
- Around line 66-147: The render_ui() function does not call print_header(),
which means the metrics panel is never displayed in the main UI flow when the
application runs. Add a call to print_header() at the beginning of the
render_ui() function, before the main_panel is rendered with
console.print(main_panel), to ensure the metrics panel is properly included in
the output.
- Around line 95-96: The device authentication URL at line 95 contains a
hard-coded user code placeholder instead of the actual session-specific code,
which prevents users from logging in. Replace the static user code string
"ABCD-EFGH-IJKL-MNOP-QRST-UVWX-YZ01" in the auth URL with a dynamic variable
that contains the real user code generated for the current device authentication
session. Ensure this variable is available in the scope where the URL is being
constructed and passes the actual session-specific code to the user.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d4ef8051-1c78-46eb-b5aa-1c7bdfc00c4a
📒 Files selected for processing (1)
agentwatch/cli/ui.py
| resp = httpx.get("http://localhost:8000/api/v1/dashboard/summary", timeout=0.5) | ||
| if resp.status_code == 200: | ||
| return resp.json() |
There was a problem hiding this comment.
Protected dashboard endpoint is called without auth, so metrics will likely always fail.
get_real_metrics() calls a guarded API route (Line 24) but sends no credentials. Given the upstream contract, this typically returns 401 and forces perpetual N/A metrics.
Suggested direction
def get_real_metrics() -> dict:
try:
import httpx
+ import os
- resp = httpx.get("http://localhost:8000/api/v1/dashboard/summary", timeout=0.5)
+ api_key = os.getenv("AGENTWATCH_API_KEY")
+ headers = {"X-API-Key": api_key} if api_key else {}
+ resp = httpx.get(
+ "http://localhost:8000/api/v1/dashboard/summary",
+ timeout=0.5,
+ headers=headers,
+ )
if resp.status_code == 200:
return resp.json()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@agentwatch/cli/ui.py` around lines 24 - 26, The get_real_metrics() function
calls a protected API endpoint at http://localhost:8000/api/v1/dashboard/summary
without sending any authentication credentials. This causes the request to fail
with a 401 status code, preventing the metrics from being retrieved. Add
authentication to the httpx.get() call by including the appropriate auth header
or credentials parameter. Ensure the authentication method matches what the
upstream API endpoint requires, so the request returns 200 with valid data
instead of failing.
| def render_ui(): | ||
| # ========================================== | ||
| # 2. Main Body Container Content | ||
| # ========================================== | ||
| main_content = [] | ||
|
|
||
| # 3. Accent Badges | ||
| badge_text = Text("∗ Welcome to AgentWatch research preview!", style=COLOR_MAIN_ACCENT) | ||
| badge_panel = Panel(badge_text, border_style=COLOR_MAIN_ACCENT, expand=False, padding=(0, 1)) | ||
| main_content.append(badge_panel) | ||
|
|
||
| # 4. 3D Drop-Shadow ASCII Logo | ||
| # Retro double-stamped shadow effect colored in Salmon | ||
| ascii_logo = """ | ||
| █████╗ ██████╗ ███████╗███╗ ██╗████████╗██╗ ██╗ █████╗ ████████╗██████╗ ██╗ ██╗ | ||
| ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝██║ ██║██╔══██╗╚══██╔══╝██╔════╝ ██║ ██║ | ||
| ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ██║ █╗ ██║███████║ ██║ ██║ ███████║ | ||
| ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██║███╗██║██╔══██║ ██║ ██║ ██╔══██║ | ||
| ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ╚███╔███╔╝██║ ██║ ██║ ╚██████╗ ██║ ██║ | ||
| ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ | ||
| """ | ||
| logo_text = Text(ascii_logo.strip("\n"), style=f"bold {COLOR_MAIN_ACCENT}") | ||
| main_content.append(Padding(logo_text, (1, 0, 1, 0))) | ||
|
|
||
| # 5. Instructional Content & Links | ||
| login_instruction = Text( | ||
| "Browser didn't open? Use the url below to sign in:\n", style=COLOR_WHITE | ||
| ) | ||
| login_url = Text( | ||
| "https://auth.agentwatch.dev/device?user_code=ABCD-EFGH-IJKL-MNOP-QRST-UVWX-YZ01", | ||
| style=COLOR_DIM, | ||
| ) | ||
| main_content.append(login_instruction + login_url) | ||
|
|
||
| # Divider | ||
| main_content.append(Padding(Rule(style=COLOR_DIM), (1, 0, 1, 0))) | ||
|
|
||
| # 6. Duplicated Badge & Security Bullet Points | ||
| main_content.append(badge_panel) | ||
|
|
||
| security_title = Text("\nSecurity notes:", style=f"bold {COLOR_WHITE}") | ||
| main_content.append(security_title) | ||
|
|
||
| security_notes = Text() | ||
| security_notes.append( | ||
| "\n1. AgentWatch is currently in research preview\n", style=f"bold {COLOR_WHITE}" | ||
| ) | ||
| security_notes.append( | ||
| " AgentWatch is an experimental AI tool. It may produce incorrect or unexpected results.\n", | ||
| style=COLOR_DIM, | ||
| ) | ||
|
|
||
| security_notes.append("2. AgentWatch runs in your terminal\n", style=f"bold {COLOR_WHITE}") | ||
| security_notes.append( | ||
| " It has the ability to view your files and execute commands on your behalf.\n", | ||
| style=COLOR_DIM, | ||
| ) | ||
|
|
||
| security_notes.append("3. Review commands carefully\n", style=f"bold {COLOR_WHITE}") | ||
| security_notes.append( | ||
| " For your security, we strongly recommend carefully reviewing any commands before allowing AgentWatch to run them.\n", | ||
| style=COLOR_DIM, | ||
| ) | ||
| security_notes.append( | ||
| " Learn more about the AgentWatch security model at: https://agentwatch.dev/security", | ||
| style=COLOR_DIM, | ||
| ) | ||
|
|
||
| main_content.append(security_notes) | ||
|
|
||
| # Combine into main container | ||
| main_panel = Panel(Group(*main_content), border_style=COLOR_DIM, padding=(1, 2)) | ||
|
|
||
| # ========================================== | ||
| # 7. External Interactive Footer | ||
| # ========================================== | ||
| footer_text = Text("\nPress Enter to continue...", style=f"bold {COLOR_INDIGO}") | ||
|
|
||
| # Render the layout | ||
| console.print(main_panel) | ||
| console.print(footer_text) | ||
|
|
There was a problem hiding this comment.
Top metrics panel is never rendered from the main UI path.
render_ui() does not call print_header(), so the metrics panel introduced in this module is effectively dead in the __main__ flow.
Minimal fix
def render_ui():
+ print_header()
# ==========================================
# 2. Main Body Container Content
# ==========================================🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@agentwatch/cli/ui.py` around lines 66 - 147, The render_ui() function does
not call print_header(), which means the metrics panel is never displayed in the
main UI flow when the application runs. Add a call to print_header() at the
beginning of the render_ui() function, before the main_panel is rendered with
console.print(main_panel), to ensure the metrics panel is properly included in
the output.
| "https://auth.agentwatch.dev/device?user_code=ABCD-EFGH-IJKL-MNOP-QRST-UVWX-YZ01", | ||
| style=COLOR_DIM, |
There was a problem hiding this comment.
Device auth URL uses a hard-coded user code, which breaks real sign-in.
The login link embeds a static user_code; users will be directed to an invalid/stale code instead of their session-specific code.
Suggested shape
-def render_ui():
+def render_ui(device_auth_url: str):
@@
- login_url = Text(
- "https://auth.agentwatch.dev/device?user_code=ABCD-EFGH-IJKL-MNOP-QRST-UVWX-YZ01",
- style=COLOR_DIM,
- )
+ login_url = Text(device_auth_url, style=COLOR_DIM)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@agentwatch/cli/ui.py` around lines 95 - 96, The device authentication URL at
line 95 contains a hard-coded user code placeholder instead of the actual
session-specific code, which prevents users from logging in. Replace the static
user code string "ABCD-EFGH-IJKL-MNOP-QRST-UVWX-YZ01" in the auth URL with a
dynamic variable that contains the real user code generated for the current
device authentication session. Ensure this variable is available in the scope
where the URL is being constructed and passes the actual session-specific code
to the user.
|
Closing in favor of #454 which includes this code to pass CI. |
Splitting previous large PR. This PR adds the UI components for the CLI.
Summary by CodeRabbit