← back to Answer Cockpit

start.sh

58 lines

#!/usr/bin/env bash
# Answer Cockpit launcher — nohup node server.js on 127.0.0.1:9805 (hard-coded).
#   bash start.sh            start (refuses if 9805 is already bound by a different command)
#   bash start.sh --stop     stop the instance recorded in .running
#   bash start.sh --status   show listener
set -uo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PORT=9805
mkdir -p "$DIR/tmp" "$DIR/data"
LOG="$DIR/tmp/server.log"

# The server must NEVER inherit a Claude session's bridge identity — its osascript
# children would otherwise trip the colordots.sh subagent rail / look like a subagent.
unset CLAUDE_CODE_CHILD_SESSION CLAUDE_PID CLAUDE_CODE_ENTRYPOINT 2>/dev/null || true

listener() { lsof -nP -iTCP:"$PORT" -sTCP:LISTEN 2>/dev/null | awk 'NR>1{print $2}' | head -1; }

case "${1:-}" in
  --stop)
    pid="$(listener)"; [ -z "$pid" ] && { echo "nothing on :$PORT"; exit 0; }
    cmd="$(ps -o command= -p "$pid" 2>/dev/null)"
    case "$cmd" in *answer-cockpit/server.js*|*"$DIR/server.js"*) kill "$pid" && echo "stopped pid $pid"; rm -f "$DIR/.running"; exit 0;;
      *) echo "refusing: :$PORT is held by a different command (pid $pid): $cmd"; exit 1;; esac;;
  --status)
    pid="$(listener)"; [ -z "$pid" ] && { echo "not running"; exit 1; }
    echo "pid $pid  $(ps -o command= -p "$pid")"; exit 0;;
esac

pid="$(listener)"
if [ -n "$pid" ]; then
  cmd="$(ps -o command= -p "$pid" 2>/dev/null)"
  case "$cmd" in
    *answer-cockpit/server.js*|*"$DIR/server.js"*) echo "already running (pid $pid): http://127.0.0.1:$PORT (admin / DW2024!)"; exit 0;;
    *) echo "refusing to start: :$PORT is already bound by a different command (pid $pid): $cmd" >&2; exit 1;;
  esac
fi

: > "$LOG"
nohup node "$DIR/server.js" >"$LOG" 2>&1 &
PID=$!
disown "$PID" 2>/dev/null || true

for _ in $(seq 1 50); do
  grep -q 'COCKPIT_LISTENING port=' "$LOG" 2>/dev/null && break
  kill -0 "$PID" 2>/dev/null || { echo "server failed to start:" >&2; cat "$LOG" >&2; exit 1; }
  sleep 0.1
done
grep -q 'COCKPIT_LISTENING port=' "$LOG" || { echo "timed out waiting for COCKPIT_LISTENING" >&2; cat "$LOG" >&2; exit 1; }

printf '%s\t%s\t%s\t%s\n' "$(date +%FT%T)" "$PID" "$PORT" "$LOG" > "$DIR/.running"
cat <<EOF
✅ Answer Cockpit is live
   URL:   http://127.0.0.1:$PORT  (admin / DW2024!)
   PID:   $PID   (log: $LOG)
   Stop:  bash $DIR/start.sh --stop
   cost:  \$0 (local)
EOF