mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-21 10:22:18 +00:00
fix(debug): include gui.log (dashboard/TUI/pty/websocket) in hermes debug share
gui.log was registered in hermes_cli/logs.py::LOG_FILES (and surfaced by `hermes logs gui`) but was never wired into `hermes debug share`. The share report captured agent/errors/gateway/desktop tails plus full agent/gateway/ desktop logs — but nothing from gui.log, the surface the dashboard, TUI-over- PTY bridge, and websocket layer (hermes_cli.web_server / pty_bridge / tui_gateway) actually write to. A user reporting a dashboard or TUI bug shared zero breadcrumbs from the broken surface. Wire gui.log through all three share surfaces, matching the existing pattern: - _capture_default_log_snapshots(): capture the gui snapshot (redacted like the rest) - collect_debug_report(): add the gui.log summary tail block - build_debug_share(): pull gui full_text, prepend dump header + redaction banner, add to the upload loop - run_debug_share() --local branch: same, plus the local print block - _PRIVACY_NOTICE: name gui.log in both bullets Redaction is inherited for free — the gui snapshot goes through the same _capture_log_snapshot(..., redact=redact) path, so secrets are scrubbed in both the tail and full text (verified E2E: seeded key masked by default, passes through under --no-redact, raw token never leaks). Tests: seed gui.log in the fixture, add test_report_includes_gui_log, and bump the upload-count tripwire 4->5 (test_share_uploads_five_pastes).
This commit is contained in:
parent
ddca590cac
commit
01a6f11896
3 changed files with 47 additions and 11 deletions
|
|
@ -191,10 +191,10 @@ _PRIVACY_NOTICE = """\
|
|||
⚠️ This will upload the following to a public paste service:
|
||||
• System info (OS, Python version, Hermes version, provider, which API keys
|
||||
are configured — NOT the actual keys)
|
||||
• Recent log lines (agent.log, errors.log, gateway.log, desktop.log — may
|
||||
contain conversation fragments and file paths)
|
||||
• Full agent.log, gateway.log, and desktop.log (up to 512 KB each — likely
|
||||
contains conversation content, tool outputs, and file paths)
|
||||
• Recent log lines (agent.log, errors.log, gateway.log, gui.log, desktop.log
|
||||
— may contain conversation fragments and file paths)
|
||||
• Full agent.log, gateway.log, gui.log, and desktop.log (up to 512 KB each —
|
||||
likely contains conversation content, tool outputs, and file paths)
|
||||
|
||||
Pastes auto-delete after 6 hours.
|
||||
"""
|
||||
|
|
@ -503,6 +503,9 @@ def _capture_default_log_snapshots(
|
|||
"gateway": _capture_log_snapshot(
|
||||
"gateway", tail_lines=errors_lines, redact=redact
|
||||
),
|
||||
"gui": _capture_log_snapshot(
|
||||
"gui", tail_lines=errors_lines, redact=redact
|
||||
),
|
||||
"desktop": _capture_log_snapshot(
|
||||
"desktop", tail_lines=errors_lines, redact=redact
|
||||
),
|
||||
|
|
@ -574,6 +577,10 @@ def collect_debug_report(
|
|||
buf.write(log_snapshots["gateway"].tail_text)
|
||||
buf.write("\n\n")
|
||||
|
||||
buf.write(f"--- gui.log (last {errors_lines} lines) ---\n")
|
||||
buf.write(log_snapshots["gui"].tail_text)
|
||||
buf.write("\n\n")
|
||||
|
||||
buf.write(f"--- desktop.log (last {errors_lines} lines) ---\n")
|
||||
buf.write(log_snapshots["desktop"].tail_text)
|
||||
buf.write("\n")
|
||||
|
|
@ -639,6 +646,7 @@ def build_debug_share(
|
|||
)
|
||||
agent_log = log_snapshots["agent"].full_text
|
||||
gateway_log = log_snapshots["gateway"].full_text
|
||||
gui_log = log_snapshots["gui"].full_text
|
||||
desktop_log = log_snapshots["desktop"].full_text
|
||||
|
||||
# Prepend dump header to each full log so every paste is self-contained.
|
||||
|
|
@ -646,6 +654,8 @@ def build_debug_share(
|
|||
agent_log = dump_text + "\n\n--- full agent.log ---\n" + agent_log
|
||||
if gateway_log:
|
||||
gateway_log = dump_text + "\n\n--- full gateway.log ---\n" + gateway_log
|
||||
if gui_log:
|
||||
gui_log = dump_text + "\n\n--- full gui.log ---\n" + gui_log
|
||||
if desktop_log:
|
||||
desktop_log = dump_text + "\n\n--- full desktop.log ---\n" + desktop_log
|
||||
|
||||
|
|
@ -657,6 +667,8 @@ def build_debug_share(
|
|||
agent_log = _REDACTION_BANNER + agent_log
|
||||
if gateway_log:
|
||||
gateway_log = _REDACTION_BANNER + gateway_log
|
||||
if gui_log:
|
||||
gui_log = _REDACTION_BANNER + gui_log
|
||||
if desktop_log:
|
||||
desktop_log = _REDACTION_BANNER + desktop_log
|
||||
|
||||
|
|
@ -670,6 +682,7 @@ def build_debug_share(
|
|||
for label, content in (
|
||||
("agent.log", agent_log),
|
||||
("gateway.log", gateway_log),
|
||||
("gui.log", gui_log),
|
||||
("desktop.log", desktop_log),
|
||||
):
|
||||
if not content:
|
||||
|
|
@ -712,11 +725,14 @@ def run_debug_share(args):
|
|||
)
|
||||
agent_log = log_snapshots["agent"].full_text
|
||||
gateway_log = log_snapshots["gateway"].full_text
|
||||
gui_log = log_snapshots["gui"].full_text
|
||||
desktop_log = log_snapshots["desktop"].full_text
|
||||
if agent_log:
|
||||
agent_log = dump_text + "\n\n--- full agent.log ---\n" + agent_log
|
||||
if gateway_log:
|
||||
gateway_log = dump_text + "\n\n--- full gateway.log ---\n" + gateway_log
|
||||
if gui_log:
|
||||
gui_log = dump_text + "\n\n--- full gui.log ---\n" + gui_log
|
||||
if desktop_log:
|
||||
desktop_log = dump_text + "\n\n--- full desktop.log ---\n" + desktop_log
|
||||
if redact:
|
||||
|
|
@ -725,12 +741,15 @@ def run_debug_share(args):
|
|||
agent_log = _REDACTION_BANNER + agent_log
|
||||
if gateway_log:
|
||||
gateway_log = _REDACTION_BANNER + gateway_log
|
||||
if gui_log:
|
||||
gui_log = _REDACTION_BANNER + gui_log
|
||||
if desktop_log:
|
||||
desktop_log = _REDACTION_BANNER + desktop_log
|
||||
print(report)
|
||||
for title, body in (
|
||||
("FULL agent.log", agent_log),
|
||||
("FULL gateway.log", gateway_log),
|
||||
("FULL gui.log", gui_log),
|
||||
("FULL desktop.log", desktop_log),
|
||||
):
|
||||
if body:
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ def hermes_home(tmp_path, monkeypatch):
|
|||
(logs_dir / "gateway.log").write_text(
|
||||
"2026-04-12 17:00:10 INFO gateway.run: started\n"
|
||||
)
|
||||
(logs_dir / "gui.log").write_text(
|
||||
"2026-04-12 17:00:12 INFO hermes_cli.web_server: dashboard request\n"
|
||||
)
|
||||
(logs_dir / "desktop.log").write_text(
|
||||
"2026-04-12 17:00:15 INFO desktop: backend spawned\n"
|
||||
)
|
||||
|
|
@ -454,6 +457,15 @@ class TestCollectDebugReport:
|
|||
|
||||
assert "--- gateway.log" in report
|
||||
|
||||
def test_report_includes_gui_log(self, hermes_home):
|
||||
from hermes_cli.debug import collect_debug_report
|
||||
|
||||
with patch("hermes_cli.dump.run_dump"):
|
||||
report = collect_debug_report(log_lines=50)
|
||||
|
||||
assert "--- gui.log" in report
|
||||
assert "dashboard request" in report
|
||||
|
||||
def test_report_includes_desktop_log(self, hermes_home):
|
||||
from hermes_cli.debug import collect_debug_report
|
||||
|
||||
|
|
@ -538,8 +550,8 @@ class TestRunDebugShare:
|
|||
assert "FULL agent.log" in out
|
||||
assert "FULL gateway.log" in out
|
||||
|
||||
def test_share_uploads_four_pastes(self, hermes_home, capsys):
|
||||
"""Successful share uploads report + agent.log + gateway.log + desktop.log."""
|
||||
def test_share_uploads_five_pastes(self, hermes_home, capsys):
|
||||
"""Successful share uploads report + agent.log + gateway.log + gui.log + desktop.log."""
|
||||
from hermes_cli.debug import run_debug_share
|
||||
|
||||
args = MagicMock()
|
||||
|
|
@ -561,15 +573,17 @@ class TestRunDebugShare:
|
|||
run_debug_share(args)
|
||||
|
||||
out = capsys.readouterr().out
|
||||
# Should have 4 uploads: report, agent.log, gateway.log, desktop.log
|
||||
assert call_count[0] == 4
|
||||
# Should have 5 uploads: report, agent.log, gateway.log, gui.log, desktop.log
|
||||
assert call_count[0] == 5
|
||||
assert "paste.rs/paste1" in out # Report
|
||||
assert "paste.rs/paste2" in out # agent.log
|
||||
assert "paste.rs/paste3" in out # gateway.log
|
||||
assert "paste.rs/paste4" in out # desktop.log
|
||||
assert "paste.rs/paste4" in out # gui.log
|
||||
assert "paste.rs/paste5" in out # desktop.log
|
||||
assert "Report" in out
|
||||
assert "agent.log" in out
|
||||
assert "gateway.log" in out
|
||||
assert "gui.log" in out
|
||||
assert "desktop.log" in out
|
||||
|
||||
# Each log paste should start with the dump header
|
||||
|
|
@ -579,7 +593,10 @@ class TestRunDebugShare:
|
|||
gateway_paste = uploaded_content[2]
|
||||
assert "--- hermes dump ---" in gateway_paste
|
||||
assert "--- full gateway.log ---" in gateway_paste
|
||||
desktop_paste = uploaded_content[3]
|
||||
gui_paste = uploaded_content[3]
|
||||
assert "--- hermes dump ---" in gui_paste
|
||||
assert "--- full gui.log ---" in gui_paste
|
||||
desktop_paste = uploaded_content[4]
|
||||
assert "--- hermes dump ---" in desktop_paste
|
||||
assert "--- full desktop.log ---" in desktop_paste
|
||||
|
||||
|
|
|
|||
|
|
@ -734,7 +734,7 @@ Upload a debug report (system info + recent logs) to a paste service and get a s
|
|||
| `--expire <days>` | Paste expiry in days (default: 7). |
|
||||
| `--local` | Print the report locally instead of uploading. |
|
||||
|
||||
The report includes system info (OS, Python version, Hermes version), recent agent and gateway logs (512 KB limit per file), and redacted API key status. Keys are always redacted — no secrets are uploaded.
|
||||
The report includes system info (OS, Python version, Hermes version), recent agent, gateway, GUI/dashboard, and desktop logs (512 KB limit per file), and redacted API key status. Keys are always redacted — no secrets are uploaded.
|
||||
|
||||
Paste services tried in order: paste.rs, dpaste.com.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue