Release PQ (v0.51.456): start.sh detects an already-running server (#4308) (#4309)

* Release PQ (v0.51.456): start.sh detects an already-running server (#4308)

start.sh now probes /health (best-effort, 2s, curl/wget) before launching
bootstrap.py; if a server already answers it prints a clear 'already running +
how to restart' message and exits 0 instead of spawning a child that fails to
bind. Brings the detached start.sh path to parity with ctl.sh's double-start
refusal. Container path (server.py direct) unaffected.

Co-authored-by: tomas-forgac

* gate fix (Codex CORE): parse $@ for --host/--host=/positional port in the start.sh preflight

bootstrap.py accepts a positional port + --host that override the env defaults;
the preflight probed env/defaults only, so './start.sh <altport>' while a server
ran on the env-default port falsely reported 'already running' and refused the
legitimate launch. Now scans $@ the same way bootstrap.py's argparse does before
building the /health probe URL.

---------

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
This commit is contained in:
nesquena-hermesandnesquena-hermes authored and GitHub committed 2026-06-16 07:24:50 -07:00
1 parent 50e7589a92
commit 44032ab5ab
2 files changed
+97

No files matched your search

+6
View File
@@ -7,6 +7,12 @@
- **Windows pytest-harness compatibility (#3664).** Hardened the test suite to run on Windows: profile-home fallback paths are path-normalized, strict POSIX file-mode (`0o600`) assertions are gated behind `os.name != "nt"` (Linux still asserts them at full strictness), the conftest cleanup handles Windows process-tree/port teardown and the Py3.12+ `shutil.rmtree` `onexc` shim, and tests that require `fork`/`fcntl` carry `@requires_fork` / `@requires_fcntl` markers (which never skip on Linux). Test-only — no runtime or app behavior change, no Linux CI behavior change. (#4254, #4255, #4256, #4257, #4259, #4263, #4266, #4274)
## [v0.51.456] — 2026-06-16 — Release PQ (start.sh tells you when the server is already running)
### Changed
- **`./start.sh` now detects an already-running server instead of pretending to start a new one.** Previously, running `./start.sh` while a server was already bound to the host:port would have `bootstrap.py` spawn a child that fails to bind and dies, while the existing instance answered the `/health` probe — so the script reported "ready" and exited 0 without actually starting anything. `start.sh` now probes `/health` first (best-effort, 2s timeout, via `curl`/`wget`; skipped if neither is present) and, when a server already answers, prints a clear message that nothing was (re)started plus how to restart (`./ctl.sh restart`, or stop-the-PID + `./start.sh`), then exits 0. This brings the detached `start.sh` path to parity with `ctl.sh`'s existing double-start refusal. The container path (which runs `server.py` directly) is unaffected. (#4308)
## [v0.51.455] — 2026-06-16 — Release PP (drop stale background-process completions instead of prepending them to a later turn)
### Fixed
+91
View File
@@ -62,4 +62,95 @@ if [[ -z "${PYTHON}" ]]; then
fi
fi
# Pre-flight: detect an already-running server before launching bootstrap.py.
#
# bootstrap.py's detached (non-foreground) path spawns server.py, then probes
# /health and reports success once *anything* answers. If a server is already
# bound to this host:port, the freshly spawned child fails to bind and dies,
# but the EXISTING (often orphaned) server answers the /health probe — so
# bootstrap.py prints "ready" and exits 0 without having started anything. The
# user just keeps re-confirming the old instance on every run.
#
# To avoid that, probe /health here first. If a server is already up, tell the
# user plainly that nothing was (re)started and how to restart, then exit 0.
# ctl.sh already refuses to double-start via PID/state tracking; this brings
# the detached start.sh path to parity using a health probe (start.sh keeps no
# PID file of its own).
#
# Resolve host/port the same way bootstrap.py does: HERMES_WEBUI_HOST /
# HERMES_WEBUI_PORT (possibly just sourced from .env above), else the
# bootstrap.py defaults of 127.0.0.1 / 8787. A 0.0.0.0 / :: bind is probed via
# loopback, matching server.py's _abort_if_already_serving.
_hermes_host="${HERMES_WEBUI_HOST:-127.0.0.1}"
_hermes_port="${HERMES_WEBUI_PORT:-8787}"
# CLI args override the env/defaults exactly as bootstrap.py's argparse does
# (`port` is the first bare numeric positional; `--host VALUE` / `--host=VALUE`).
# Without this, `./start.sh <port>` or `--host X` would probe the wrong endpoint
# and could falsely report "already running" against a different instance.
_hermes_args=("$@")
_hermes_i=0
while [[ ${_hermes_i} -lt ${#_hermes_args[@]} ]]; do
_hermes_arg="${_hermes_args[${_hermes_i}]}"
case "${_hermes_arg}" in
--host)
_hermes_next=$(( _hermes_i + 1 ))
if [[ ${_hermes_next} -lt ${#_hermes_args[@]} ]]; then
_hermes_host="${_hermes_args[${_hermes_next}]}"
_hermes_i=${_hermes_next}
fi
;;
--host=*)
_hermes_host="${_hermes_arg#--host=}"
;;
--*)
: # other flags (e.g. --no-browser) carry no positional value here
;;
*)
# First bare numeric positional is the port (bootstrap.py: nargs="?").
if [[ "${_hermes_arg}" =~ ^[0-9]+$ ]]; then
_hermes_port="${_hermes_arg}"
fi
;;
esac
_hermes_i=$(( _hermes_i + 1 ))
done
case "${_hermes_host}" in
0.0.0.0|""|::|"[::]") _hermes_probe_host="127.0.0.1" ;;
*) _hermes_probe_host="${_hermes_host}" ;;
esac
_hermes_health_url="http://${_hermes_probe_host}:${_hermes_port}/health"
# Best-effort probe. If neither curl nor wget is present we skip the check and
# fall through to the normal launch (unchanged behavior). Short 2s timeout so a
# normal cold start is not delayed.
_hermes_already_up=""
if command -v curl >/dev/null 2>&1; then
_hermes_already_up="$(curl -fsS --max-time 2 "${_hermes_health_url}" 2>/dev/null || true)"
elif command -v wget >/dev/null 2>&1; then
_hermes_already_up="$(wget -qO- --timeout=2 --tries=1 "${_hermes_health_url}" 2>/dev/null || true)"
fi
if [[ -n "${_hermes_already_up}" ]]; then
cat >&2 <<EOF
[==] Hermes WebUI is already running at http://${_hermes_probe_host}:${_hermes_port}
The server was NOT started again (start.sh does not double-start).
If you need to restart the server, do the following:
Preferred — use the daemon controller:
./ctl.sh restart
Otherwise, stop the running server and start it again manually:
1. Find the process listening on port ${_hermes_port}:
lsof -iTCP:${_hermes_port} -sTCP:LISTEN # macOS / Linux
2. Stop it (use -9 only if it ignores a normal stop):
kill <PID>
3. Start it again:
./start.sh
EOF
exit 0
fi
exec "${PYTHON}" "${REPO_ROOT}/bootstrap.py" --no-browser "$@"