fix(update): kill stale dashboards instead of warning - #17832
Merged
Conversation
`hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from #16872 and the #17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
teknium1
added a commit
that referenced
this pull request
Apr 30, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR #17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
teknium1
added a commit
that referenced
this pull request
Apr 30, 2026
…n reload (#17881) Six tests in this file failed in CI (-n auto) after #17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
donald131
pushed a commit
to donald131/hermes-agent
that referenced
this pull request
May 2, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
donald131
pushed a commit
to donald131/hermes-agent
that referenced
this pull request
May 2, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
nickdlkk
pushed a commit
to nickdlkk/hermes-agent
that referenced
this pull request
May 11, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
nickdlkk
pushed a commit
to nickdlkk/hermes-agent
that referenced
this pull request
May 11, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR NousResearch#17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
nickdlkk
pushed a commit
to nickdlkk/hermes-agent
that referenced
this pull request
May 11, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR NousResearch#17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
jsboige
pushed a commit
to jsboige/hermes-agent
that referenced
this pull request
May 14, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
jsboige
pushed a commit
to jsboige/hermes-agent
that referenced
this pull request
May 14, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR NousResearch#17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
jsboige
pushed a commit
to jsboige/hermes-agent
that referenced
this pull request
May 14, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
dannyJ848
pushed a commit
to dannyJ848/hermes-agent
that referenced
this pull request
May 17, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
dannyJ848
pushed a commit
to dannyJ848/hermes-agent
that referenced
this pull request
May 17, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR NousResearch#17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
dannyJ848
pushed a commit
to dannyJ848/hermes-agent
that referenced
this pull request
May 17, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR NousResearch#17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
Seven74AI
pushed a commit
to Seven74AI/hermes-agent
that referenced
this pull request
Jun 13, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
Seven74AI
pushed a commit
to Seven74AI/hermes-agent
that referenced
this pull request
Jun 13, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR NousResearch#17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
Seven74AI
pushed a commit
to Seven74AI/hermes-agent
that referenced
this pull request
Jun 13, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR NousResearch#17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…esearch#17832) `hermes update` previously just printed a warning when it detected a running `hermes dashboard` process from the previous version, telling the user to kill and restart it themselves. In practice dashboards get started and forgotten, so the warning was routinely ignored and users ended up with a silent frontend/backend mismatch (new JS bundle served against the old in-memory Python backend, e.g. new auth headers the old code doesn't recognise → every API call 401s). The dashboard has no service manager, no PID file, and we don't record the original launch args (--host, --port, --insecure, --tui, --no-open) so we can't auto-restart it. But we CAN stop it, which is what the user wants — the failure mode when the stale process is left alive is worse than the dashboard just being down. - POSIX: SIGTERM, poll for ~3s, SIGKILL any survivors. - Windows: `taskkill /PID <pid> /F`. - Print each PID's outcome plus a one-line restart hint. - Detection logic is unchanged (same ps / wmic scan, same guards against the `pgrep -f` greedy-match trap from NousResearch#16872 and the NousResearch#17049 wmic UnicodeDecodeError fix). Also split the old monolithic `_warn_stale_dashboard_processes` into `_find_stale_dashboard_pids` (scan) + `_kill_stale_dashboard_processes` (kill), keeping the old name as an alias so any external callers still work. E2E verified: spawned a fake `hermes dashboard` cmdline via `exec -a 'hermes dashboard …' sleep 300`, ran `_kill_stale_dashboard_processes()`, confirmed SIGTERM exit (-15) and that a post-scan returns an empty PID list.
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
`hermes dashboard` is a long-lived foreground server that users often start and forget about, sometimes in a shell they've since closed. We didn't have a way to stop it — users had to find the PID manually. Adds two lifecycle flags that reuse the same detection + termination path the post-`hermes update` cleanup (PR NousResearch#17832) uses: hermes dashboard --status List running hermes dashboard processes with PID + cmdline. Exit 0, informational. hermes dashboard --stop Terminate all running dashboards (3s grace then force-kill survivors). Exit 0 if none remain, 1 if any couldn't be stopped. Windows uses `taskkill /F` as before. Both flags short-circuit before any fastapi/uvicorn import so they work even on installations where the dashboard extras aren't installed — useful when you're cleaning up after uninstalling. The kill helper gained an optional `reason=...` param so the output reads "(requested via --stop)" instead of the post-update-specific "running backend no longer matches the updated frontend" wording. E2E: `hermes dashboard --status` with nothing running prints the empty message; with a fake `hermes dashboard ...` cmdline spawned via `exec -a`, `--status` lists it, `--stop` terminates it (exit -15), and a follow-up `--status` returns empty.
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…n reload (NousResearch#17881) Six tests in this file failed in CI (-n auto) after NousResearch#17832 landed because other tests on the same xdist worker reload hermes_cli.main: tests/hermes_cli/test_env_loader.py:85-86 sys.modules.pop('hermes_cli.main', None) importlib.import_module('hermes_cli.main') tests/hermes_cli/test_skills_subparser.py:24-25 del sys.modules['hermes_cli.main'] When either ran first on a worker, our top-of-file 'from hermes_cli.main import _kill_stale_dashboard_processes' captured a stale function object whose __globals__ points at the old module dict. patch('hermes_cli.main._find_stale_dashboard_pids', ...) then patched the new module, but the stale function resolved the dependency via its stale __globals__, so every patch became a no-op: pids=[] → early return → no signals, no output, assertions failed. Fix: add an autouse fixture that rebinds the three module-level names to whatever is currently live in sys.modules['hermes_cli.main'] before each test runs. The pollutants in the other two files are load-bearing for their own tests, so fixing it on the consumer side is correct. Repro: pytest tests/hermes_cli/test_env_loader.py tests/hermes_cli/test_update_stale_dashboard.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
hermes updatenow stops any runninghermes dashboardprocesses at the end of the update instead of just warning the user to restart them.The old warning was routinely ignored, leaving users with a stale Python backend serving a freshly-updated JS bundle → silent auth-header mismatches, 401s on every API call, empty dashboards.
The dashboard has no service manager and no saved launch args, so we can't auto-restart it — but stopping it is strictly better than leaving the broken backend alive. A restart hint is printed.
Changes
hermes_cli/main.py: split detection (_find_stale_dashboard_pids) from action (_kill_stale_dashboard_processes). POSIX: SIGTERM → 3s grace → SIGKILL. Windows:taskkill /F. Old_warn_stale_dashboard_processeskept as back-compat alias.tests/hermes_cli/test_update_stale_dashboard.py: rewrote around the new semantics. Covers SIGTERM graceful exit, SIGKILL fallback for survivors, PermissionError reporting, already-gone-PID handling, Windowstaskkillpath including failure reporting, plus the preserved [Bug]: UnicodeDecodeError and AttributeError in Windows gateway process scanning #17049 wmic encoding regression tests.Detection logic itself is unchanged (same
ps/wmicscan, same greedy-match guards from #16872, same #17049 wmic UnicodeDecodeError fix).Validation
hermes updateps/wmicmissing or timing outscripts/run_tests.sh)E2E: spawned
exec -a 'hermes dashboard …' sleep 300, called_kill_stale_dashboard_processes(), confirmed SIGTERM exit (-15) and empty post-scan.