fix(brainstorm-server): tie stop-server PID check to the session's port

The node+server.cjs command match (from the adversarial review) still matched any
unrelated node process running a file named server.cjs. When we recorded the
bound port (state/server-info) and lsof is available, additionally require the
PID to be the process actually LISTENING on this session's port — which rules out
a different project's server.cjs / editor task runner that recycled the stale
PID. Falls back to the command match when the port or lsof isn't available.

Test: a 'node server.cjs' process not listening on the recorded port is spared.

Refs #1703
This commit is contained in:
Jesse Vincent
2026-06-09 17:27:30 -07:00
parent f8f87ff43a
commit 843c473382
2 changed files with 36 additions and 1 deletions

View File

@@ -21,9 +21,22 @@ PID_FILE="${STATE_DIR}/server.pid"
is_brainstorm_server() {
kill -0 "$1" 2>/dev/null || return 1
case "$(ps -p "$1" -o command= 2>/dev/null)" in
*node*server.cjs*) return 0 ;;
*node*server.cjs*) ;;
*) return 1 ;;
esac
# Stronger check: if we recorded the bound port and lsof is available, require
# the PID to be the process actually LISTENING on this session's port. This
# rules out an unrelated `node ... server.cjs` (another project, an editor task
# runner, a different session) that happened to recycle the stale PID.
local info="${STATE_DIR}/server-info"
if [[ -f "$info" ]] && command -v lsof >/dev/null 2>&1; then
local port
port=$(sed -n 's/.*"port":\([0-9][0-9]*\).*/\1/p' "$info" | head -1)
if [[ -n "$port" ]]; then
[[ "$(lsof -nP -iTCP:"$port" -sTCP:LISTEN -t 2>/dev/null | head -1)" == "$1" ]] || return 1
fi
fi
return 0
}
if [[ -f "$PID_FILE" ]]; then