← back to Exo Keepalive

run-exo.sh

29 lines

#!/usr/bin/env bash
# exo cluster keepalive runner — launched by com.steve.exo-keepalive (KeepAlive=true).
# Starts the exo master+worker + OpenAI-compatible API at http://localhost:52415.
# Steve's standing rule: the cluster is ALWAYS up. launchd respawns this if it exits.
set -uo pipefail
export HOME=/Users/macstudio3
export PATH="/Users/macstudio3/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
cd "$HOME/exo" || exit 1
LOG="$HOME/Projects/exo-keepalive/exo.log"
echo "[$(date +%FT%T%z)] === exo start (pid $$) ===" >> "$LOG"
# TK-11874: reap any ORPHANED exo python still holding our ports before we start.
# `uv run` SPAWNS python (it does not exec), so when launchd kills the tracked uv
# (watchdog `kickstart -k`, SIGKILL) the python child survives as a PPID-1 orphan
# holding :52414 (zenoh) and every respawn dies with "Address already in use" —
# three multi-hour outages 09-12..09-16. Scoped to exo's OWN venv python only, so
# this can never touch any other process. Anything holding these ports when this
# script starts is by definition not the process launchd is about to track.
for p in $(lsof -tnP -iTCP:52414 -iTCP:52415 -sTCP:LISTEN 2>/dev/null); do
  if ps -o command= -p "$p" 2>/dev/null | grep -qF "$HOME/exo/.venv/bin/python"; then
    echo "[$(date +%FT%T%z)] reaping orphan exo python pid $p holding :52414/:52415" >> "$LOG"
    kill "$p" 2>/dev/null
    for _ in 1 2 3 4 5 6 7 8 9 10; do kill -0 "$p" 2>/dev/null || break; sleep 1; done
    kill -0 "$p" 2>/dev/null && kill -9 "$p" 2>/dev/null
  fi
done
sleep 1
# exec so launchd tracks the real exo process (respawns on crash)
exec /Users/macstudio3/.local/bin/uv run --extra mlx exo >> "$LOG" 2>&1