diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index f1d14ebf48b..fb5f7ca12d3 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -4432,11 +4432,33 @@ def start_server( if open_browser: import webbrowser - def _open(): - time.sleep(1.0) - webbrowser.open(f"http://{host}:{port}") + # On headless Linux (no DISPLAY or WAYLAND_DISPLAY) some registered + # browsers are TUI programs (links, lynx, www-browser) that try to + # take over the terminal. That can send SIGHUP to the server process + # and cause an immediate exit even though uvicorn bound successfully. + # Skip the auto-open attempt on headless systems and let the user + # open the URL manually. macOS and Windows are always considered + # display-capable. + _has_display = ( + sys.platform != "linux" + or bool(os.environ.get("DISPLAY")) + or bool(os.environ.get("WAYLAND_DISPLAY")) + ) - threading.Thread(target=_open, daemon=True).start() + if _has_display: + def _open(): + try: + time.sleep(1.0) + webbrowser.open(f"http://{host}:{port}") + except Exception: + pass + + threading.Thread(target=_open, daemon=True).start() + else: + _log.debug( + "Skipping browser-open: no DISPLAY or WAYLAND_DISPLAY detected " + "(headless Linux). Pass --no-open to suppress this detection." + ) print(f" Hermes Web UI → http://{host}:{port}") uvicorn.run(app, host=host, port=port, log_level="warning")