fix(cli): warn when hermes runs unsupervised as PID 1
A deployment that overrides the image's `entrypoint:` to invoke hermes directly skips docker/entrypoint-dispatch.sh entirely, so hermes itself becomes PID 1 with no s6-overlay /init (or any other init) above it. Nothing then reaps orphaned grandchildren (browser tooling, MCP subprocesses, shell-tool children) reparented to PID 1, and they accumulate as zombies without bound. entrypoint-dispatch.sh already warns on its own non-PID-1 fallback path, but that script never runs in the entrypoint-override case, so there was no signal at all. Add the same style of warning inside hermes_cli.main, gated on being PID 1 on Linux, pointing users at the image's default ENTRYPOINT or `docker run --init` / `init: true`. Fixes #111577
This commit is contained in:
@@ -183,6 +183,38 @@ def _run_and_exit_oneshot(
|
||||
_exit_after_oneshot(rc)
|
||||
|
||||
|
||||
def _warn_if_unsupervised_pid1(pid: "int | None" = None) -> None:
|
||||
"""Warn when this process is PID 1 with nothing above it to reap orphans.
|
||||
|
||||
Docker/Podman normally run the image's own supervisor
|
||||
(``docker/entrypoint-dispatch.sh`` -> s6-overlay's ``/init``) as PID 1,
|
||||
which reaps orphaned grandchildren reparented to it. A deployment that
|
||||
overrides ``entrypoint:`` to invoke hermes directly skips that dispatcher
|
||||
entirely, so hermes itself becomes PID 1: nothing calls ``wait()`` on
|
||||
orphaned children (browser tooling, MCP subprocesses, shell-tool
|
||||
children), and they accumulate as zombies without bound. See
|
||||
NousResearch/hermes-agent#111577. This mirrors the warning
|
||||
entrypoint-dispatch.sh already prints on its own non-PID-1 fallback path.
|
||||
"""
|
||||
try:
|
||||
import platform
|
||||
|
||||
if platform.system() != "Linux":
|
||||
return
|
||||
if (pid if pid is not None else os.getpid()) != 1:
|
||||
return
|
||||
print(
|
||||
"[hermes] WARNING: this process is PID 1 with no init above it "
|
||||
"(entrypoint override?). Orphaned child processes will not be "
|
||||
"reaped and will accumulate as zombies. Use the image's default "
|
||||
"ENTRYPOINT (docker/entrypoint-dispatch.sh) instead of overriding "
|
||||
"it, or run with `docker run --init` / `init: true` in Compose.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _set_process_title() -> None:
|
||||
"""Cosmetic: show 'hermes' instead of 'python3.xx' in ps/top/htop.
|
||||
|
||||
@@ -3396,6 +3428,7 @@ def _default_to_chat(args) -> None:
|
||||
def main():
|
||||
"""Main entry point for hermes CLI."""
|
||||
_set_process_title()
|
||||
_warn_if_unsupervised_pid1()
|
||||
_advertise_agent_env()
|
||||
|
||||
# Force UTF-8 stdio on Windows before anything prints. No-op elsewhere.
|
||||
|
||||
Reference in New Issue
Block a user