#!/usr/bin/env bash # ── Hermes Master Startup ───────────────────────────────────────────── # Brings up the entire system after a reboot. # Called by launchd (ai.hermes.startup) or manually. # # Boot order: # 1. Gitea (homebrew launchd — already handles itself) # 2. Ollama (macOS app — already handles itself via login item) # 3. Hermes Gateway (launchd — already handles itself) # 4. Webhook listener (port 7777) # 5. Timmy-loop tmux session (4-pane dashboard) # 6. Hermes cron engine (runs inside gateway) # # This script ensures 4 and 5 are alive. 1-3 and 6 are handled by # their own launchd plists / login items. # ─────────────────────────────────────────────────────────────────────── set -euo pipefail export PATH="/opt/homebrew/bin:$HOME/.local/bin:$HOME/.hermes/bin:/usr/local/bin:$PATH" LOG="$HOME/.hermes/logs/startup.log" mkdir -p "$(dirname "$LOG")" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG" } wait_for_port() { local port=$1 name=$2 max=$3 local i=0 while ! lsof -ti:"$port" >/dev/null 2>&1; do sleep 1 i=$((i + 1)) if [ "$i" -ge "$max" ]; then log "WARN: $name not up on port $port after ${max}s" return 1 fi done log "OK: $name alive on port $port" return 0 } # ── Prerequisites ────────────────────────────────────────────────────── log "=== Hermes Master Startup ===" # Wait for Gitea (port 3000) — up to 30s log "Waiting for Gitea..." wait_for_port 3000 "Gitea" 30 # Wait for Ollama (port 11434) — up to 30s log "Waiting for Ollama..." wait_for_port 11434 "Ollama" 30 # ── Webhook Listener (port 7777) ─────────────────────────────────────── if lsof -ti:7777 >/dev/null 2>&1; then log "OK: Webhook listener already running on port 7777" else log "Starting webhook listener..." tmux has-session -t webhook 2>/dev/null && tmux kill-session -t webhook tmux new-session -d -s webhook "python3 $HOME/.hermes/bin/gitea-webhook-listener.py" sleep 2 if lsof -ti:7777 >/dev/null 2>&1; then log "OK: Webhook listener started on port 7777" else log "FAIL: Webhook listener did not start" fi fi # ── Timmy Loop (tmux session) ────────────────────────────────────────── STOP_FILE="$HOME/Timmy-Time-dashboard/.loop/STOP" if [ -f "$STOP_FILE" ]; then log "SKIP: Timmy loop — STOP file present at $STOP_FILE" elif tmux has-session -t timmy-loop 2>/dev/null; then # Check if the loop pane is actually alive PANE0_PID=$(tmux list-panes -t "timmy-loop:0.0" -F '#{pane_pid}' 2>/dev/null || true) if [ -n "$PANE0_PID" ] && kill -0 "$PANE0_PID" 2>/dev/null; then log "OK: Timmy loop session alive" else log "WARN: Timmy loop session exists but pane dead. Restarting..." tmux kill-session -t timmy-loop 2>/dev/null "$HOME/.hermes/bin/timmy-tmux.sh" log "OK: Timmy loop restarted" fi else log "Starting timmy-loop session..." "$HOME/.hermes/bin/timmy-tmux.sh" log "OK: Timmy loop started" fi log "=== Startup complete ==="