2026-03-15 18:38:00 +00:00
|
|
|
#!/usr/bin/env bash
|
2026-03-06 12:55:18 -08:00
|
|
|
# Stop the brainstorm server and clean up
|
2026-03-24 11:07:59 -07:00
|
|
|
# Usage: stop-server.sh <session_dir>
|
2026-03-06 12:55:18 -08:00
|
|
|
#
|
|
|
|
|
# Kills the server process. Only deletes session directory if it's
|
|
|
|
|
# under /tmp (ephemeral). Persistent directories (.superpowers/) are
|
|
|
|
|
# kept so mockups can be reviewed later.
|
|
|
|
|
|
2026-03-24 11:07:59 -07:00
|
|
|
SESSION_DIR="$1"
|
2026-03-06 12:55:18 -08:00
|
|
|
|
2026-03-24 11:07:59 -07:00
|
|
|
if [[ -z "$SESSION_DIR" ]]; then
|
|
|
|
|
echo '{"error": "Usage: stop-server.sh <session_dir>"}'
|
2026-03-06 12:55:18 -08:00
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-24 11:07:59 -07:00
|
|
|
STATE_DIR="${SESSION_DIR}/state"
|
|
|
|
|
PID_FILE="${STATE_DIR}/server.pid"
|
2026-03-06 12:55:18 -08:00
|
|
|
|
2026-06-09 14:57:44 -07:00
|
|
|
# Confirm a PID is actually our brainstorm server (node running server.cjs),
|
|
|
|
|
# not a reused/unrelated process whose PID was recycled into a stale pid file.
|
|
|
|
|
is_brainstorm_server() {
|
|
|
|
|
kill -0 "$1" 2>/dev/null || return 1
|
|
|
|
|
case "$(ps -p "$1" -o command= 2>/dev/null)" in
|
2026-06-09 17:27:30 -07:00
|
|
|
*node*server.cjs*) ;;
|
2026-06-09 14:57:44 -07:00
|
|
|
*) return 1 ;;
|
|
|
|
|
esac
|
2026-06-09 17:27:30 -07:00
|
|
|
# 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
|
2026-06-09 14:57:44 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-06 12:55:18 -08:00
|
|
|
if [[ -f "$PID_FILE" ]]; then
|
|
|
|
|
pid=$(cat "$PID_FILE")
|
2026-03-16 01:23:32 +08:00
|
|
|
|
2026-06-09 14:57:44 -07:00
|
|
|
# Refuse to signal a PID we can't prove is our server. A stale pid file may
|
|
|
|
|
# point at an unrelated process after a reboot/PID wraparound.
|
|
|
|
|
if ! is_brainstorm_server "$pid"; then
|
|
|
|
|
rm -f "$PID_FILE"
|
|
|
|
|
echo '{"status": "stale_pid"}'
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-16 01:23:32 +08:00
|
|
|
# Try to stop gracefully, fallback to force if still alive
|
|
|
|
|
kill "$pid" 2>/dev/null || true
|
|
|
|
|
|
|
|
|
|
# Wait for graceful shutdown (up to ~2s)
|
2026-06-10 14:58:16 -07:00
|
|
|
for _ in {1..20}; do
|
2026-03-16 01:23:32 +08:00
|
|
|
if ! kill -0 "$pid" 2>/dev/null; then
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
sleep 0.1
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# If still running, escalate to SIGKILL
|
|
|
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
|
|
|
kill -9 "$pid" 2>/dev/null || true
|
|
|
|
|
|
|
|
|
|
# Give SIGKILL a moment to take effect
|
|
|
|
|
sleep 0.1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
|
|
|
echo '{"status": "failed", "error": "process still running"}'
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-24 11:07:59 -07:00
|
|
|
rm -f "$PID_FILE" "${STATE_DIR}/server.log"
|
2026-03-06 12:55:18 -08:00
|
|
|
|
|
|
|
|
# Only delete ephemeral /tmp directories
|
2026-03-24 11:07:59 -07:00
|
|
|
if [[ "$SESSION_DIR" == /tmp/* ]]; then
|
|
|
|
|
rm -rf "$SESSION_DIR"
|
2026-03-06 12:55:18 -08:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo '{"status": "stopped"}'
|
|
|
|
|
else
|
|
|
|
|
echo '{"status": "not_running"}'
|
|
|
|
|
fi
|