feat(dashboard): reliable Restart Hermes via SIGUSR1 gateway restart - #75845
feat(dashboard): reliable Restart Hermes via SIGUSR1 gateway restart#75845MistakerXI wants to merge 1 commit into
Conversation
- POST /api/system/restart-hermes now signals the gateway directly (SIGUSR1) instead of spawning a subprocess that systemd cgroup-kills with the dashboard - Web UI no longer polls for a spawned action record on restart-hermes (fire-and-forget endpoint) — dashboard reload is the real feedback, so the bogus "Action failed (exit ?)" toast is gone - Web bundle rebuilt from source so the feature survives `hermes update`
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing the dashboard/gateway restart interaction. The current main implementation does not already provide this endpoint, but the service-management path needs revision before this can be safely salvaged.
Problems
hermes_cli/web_server.py:4059probes only system scope and:4113usessudo systemctl. Main's existing managed-dashboard logic probessystemctl --userfirst and preserves that scope (hermes_cli/main.py:7283-7335), so the normal user unit is missed.hermes_cli/web_server.py:4174signals any live gateway PID. SIGUSR1 requests a service restart (gateway/run.py:25364-25365) and exits with code 75 (gateway/run.py:12372-12393); a manually run gateway has no supervisor guaranteed to relaunch it.web/src/contexts/SystemActions.tsx:88pollsdashboard-restart, but the endpoint creates no action record. The status endpoint reportsrunning=false, exit_code=nullfor an unrecorded action (hermes_cli/web_server.py:4652-4657).
Suggested changes
- Route dashboard restart through the existing scope-aware service logic, gate SIGUSR1 on confirmed supervision, and treat dashboard restart as structured fire-and-forget unless it has a real action record.
- Add focused backend and frontend regression tests for these paths.
Automated hermes-sweeper review.
| # appears where the command we would run actually exists. | ||
| try: | ||
| out = subprocess.run( | ||
| ["systemctl", "list-unit-files", "hermes-dashboard.service"], |
There was a problem hiding this comment.
This probes only the system manager. Current main deliberately probes systemctl --user first and keeps that scope for restart because Hermes installs Linux services in user scope by default (hermes_cli/main.py:7283-7335). Please reuse or extract that scope-aware logic; otherwise the standard hermes-dashboard.service is not detected here.
| if not pid: | ||
| return None | ||
| try: | ||
| os.kill(pid, signal.SIGUSR1) # POSIX-only, guarded above |
There was a problem hiding this comment.
get_running_pid() proves this is a live gateway, not that it has a supervisor. SIGUSR1 sets via_service=True (gateway/run.py:25364-25365) and the gateway exits with code 75 (gateway/run.py:12372-12393), so a manually launched gateway can be stopped without being relaunched. Gate this path on confirmed service supervision and otherwise use the existing restart fallback.
| }); | ||
| return; | ||
| } | ||
| setActiveAction(action); |
There was a problem hiding this comment.
This activates the shared action-status poller, but restart_dashboard() does not register _ACTION_PROCS or a successful _ACTION_RESULTS entry. GET /api/actions/dashboard-restart/status therefore returns running: false, exit_code: null (hermes_cli/web_server.py:4652-4657) and produces the same synthetic failure toast if the page remains available. Treat this as fire-and-forget or create a real pollable action.
Problem
The dashboard "Restart Hermes" button was unreliable on systemd installs:
systemctl restart hermes-dashboard. systemd kills the entire cgroup on restart, including that child, so the gateway restart never happened.exit_code: nulland showed a red error even though the restart succeeded.Fix
hermes_cli/web_server.py):POST /api/system/restart-hermesnow sendsSIGUSR1directly to the running gateway process (same graceful, drain-aware pathhermes gateway restartuses). The gateway is a user service withRestart=alwaysin its own cgroup, so it survives the dashboard restart. Falls back to the old spawn path when no PID file is found or on platforms without SIGUSR1.web/src/contexts/SystemActions.tsx): thehermesbranch no longer callssetActiveAction/polls — the dashboard reload itself is the real feedback, eliminating the false error toast.Evidence
test_web_server.py100/100 passPOST /api/system/restart-hermes→ 200{ok:true, via:sigusr1}, gateway PID 30928→31115, dashboard back ~10s, service active