feat: kimi independence + system watchdog + webhook listener

- Kimi user activated, token created, write access granted
- Watchdog now monitors: timmy-loop, webhook (port 7777), gitea, ollama
- Webhook listener for instant replies on rockachopa comments
- Master startup script (launchd) brings up full system on reboot
This commit is contained in:
Alexander Whitestone
2026-03-15 13:45:41 -04:00
parent b42220aa79
commit c4210ece4b
4 changed files with 171 additions and 53 deletions

94
bin/hermes-startup.sh Executable file
View File

@@ -0,0 +1,94 @@
#!/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 ==="

View File

@@ -1,39 +1,85 @@
#!/usr/bin/env bash
# ── Timmy Loop Watchdog ────────────────────────────────────────────────
# Checks if the timmy-loop tmux session is alive. Restarts if dead.
# Designed to run via cron every 5 minutes.
# ── Hermes System Watchdog ─────────────────────────────────────────────
# Monitors all critical services. Restarts anything that's dead.
# Runs via hermes cron every 8 minutes.
#
# Watches:
# 1. timmy-loop tmux session (4-pane dev dashboard)
# 2. Webhook listener on port 7777 (Gitea comment watcher)
# 3. Gitea on port 3000 (just reports, can't restart homebrew services)
# 4. Ollama on port 11434 (just reports)
# ───────────────────────────────────────────────────────────────────────
SESSION="timmy-loop"
LAUNCHER="$HOME/.hermes/bin/timmy-tmux.sh"
export PATH="/opt/homebrew/bin:$HOME/.local/bin:$HOME/.hermes/bin:/usr/local/bin:$PATH"
WATCHDOG_LOG="$HOME/Timmy-Time-dashboard/.loop/watchdog.log"
mkdir -p "$(dirname "$WATCHDOG_LOG")"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$WATCHDOG_LOG"
}
# Check if tmux session exists
if tmux has-session -t "$SESSION" 2>/dev/null; then
# Session exists. Check if the loop pane (pane 0) is still running.
STATUS="ok"
# ── 1. Timmy Loop ──────────────────────────────────────────────────────
SESSION="timmy-loop"
LAUNCHER="$HOME/.hermes/bin/timmy-tmux.sh"
STOP_FILE="$HOME/Timmy-Time-dashboard/.loop/STOP"
if [ -f "$STOP_FILE" ]; then
log "timmy-loop: STOPPED (STOP file present)"
elif tmux has-session -t "$SESSION" 2>/dev/null; then
PANE0_PID=$(tmux list-panes -t "$SESSION:.0" -F '#{pane_pid}' 2>/dev/null)
if [ -n "$PANE0_PID" ] && kill -0 "$PANE0_PID" 2>/dev/null; then
# All good, loop is alive
exit 0
: # alive, say nothing
else
log "WARN: Session exists but loop pane is dead. Restarting..."
log "timmy-loop: pane dead, restarting..."
tmux kill-session -t "$SESSION" 2>/dev/null
"$LAUNCHER"
log "timmy-loop: restarted"
STATUS="repaired"
fi
else
log "WARN: Session '$SESSION' not found."
log "timmy-loop: session missing, starting..."
"$LAUNCHER"
log "timmy-loop: started"
STATUS="repaired"
fi
# Check for a stop file — lets Alexander or an agent halt the loop
if [ -f "$HOME/Timmy-Time-dashboard/.loop/STOP" ]; then
log "STOP file found. Not restarting. Remove .loop/STOP to resume."
exit 0
# ── 2. Webhook Listener (port 7777) ────────────────────────────────────
if lsof -ti:7777 >/dev/null 2>&1; then
: # alive
else
log "webhook: dead, restarting..."
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 "webhook: restarted on port 7777"
STATUS="repaired"
else
log "webhook: FAILED to restart"
STATUS="degraded"
fi
fi
log "Restarting timmy-loop session..."
export PATH="$HOME/.local/bin:$HOME/.hermes/bin:$PATH"
"$LAUNCHER"
log "Session restarted."
# ── 3. Gitea (port 3000) — report only ─────────────────────────────────
if ! lsof -ti:3000 >/dev/null 2>&1; then
log "gitea: NOT RUNNING on port 3000 — try: brew services start gitea"
STATUS="degraded"
fi
# ── 4. Ollama (port 11434) — report only ───────────────────────────────
if ! lsof -ti:11434 >/dev/null 2>&1; then
log "ollama: NOT RUNNING on port 11434 — open Ollama.app"
STATUS="degraded"
fi
# Only log if something needed fixing
if [ "$STATUS" != "ok" ]; then
log "watchdog: status=$STATUS"
fi