#!/bin/sh # Trouve-KA — démarrage/réparation idempotent d'un node (exécutable à répétition) # Author: Simon-Pierre Boucher # Contact: contact@spboucher.ai # # Rôle du node lu dans ~/.trouveka-role : hub | search | embed | crawler # hub (M2M32) : PG+Redis (docker), tunnels OS/embed, pm2 tk-*, ngrok # search (M2M32b) : OpenSearch (docker), tunnels PG/Redis, worker crawl # embed (m4mc) : service embeddings/rerank, tunnels, worker crawl # crawler (autres) : tunnels PG/Redis/OS, worker crawl # # Conçu pour tourner sous LaunchDaemon (RunAtLoad) ET à la main ET via la clé # de guérison du watchdog. Ne touche à rien qui fonctionne déjà. # Règle d'or : les process python ne parlent qu'à 127.0.0.1 (tunnels ssh # système = seuls flux LAN, exempts du blocage « réseau local » macOS). PATH="$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" export PATH ROLE=$(cat "$HOME/.trouveka-role" 2>/dev/null || echo crawler) LOG="$HOME/trouveka-boot.log" TK="$HOME/trouve-ka" SSH_OPTS="-i $HOME/.ssh/trouveka_tunnel -N -o ControlMaster=no -o ControlPath=none -o IdentitiesOnly=yes -o IdentityAgent=none -o ForwardAgent=no -o StrictHostKeyChecking=accept-new -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes" PG_HOST=192.168.2.90 OS_HOST=192.168.2.77 EMBED_HOST=192.168.2.75 log() { echo "$(date '+%F %T') [$ROLE] $*" >> "$LOG"; } port_up() { nc -z -w 2 127.0.0.1 "$1" 2>/dev/null; } # Tunnel simple : ensure_tunnel ensure_tunnel() { port_up "$2" && return 0 pkill -f "trouveka-tunloop-$1" 2>/dev/null sleep 1 nohup sh -c "while true; do /usr/bin/ssh $SSH_OPTS -L 127.0.0.1:$2:localhost:$4 simon-pierreboucher@$3; sleep 5; done # trouveka-tunloop-$1" \ >> "$HOME/trouveka-tunnels.log" 2>&1 & log "tunnel $1 (127.0.0.1:$2 → $3:$4) relancé" } # Tunnel double PG+Redis vers le hub ensure_pg_redis_tunnel() { port_up 15432 && port_up 16379 && return 0 pkill -f "trouveka-tunloop-pg" 2>/dev/null sleep 1 nohup sh -c "while true; do /usr/bin/ssh $SSH_OPTS -L 127.0.0.1:15432:localhost:5432 -L 127.0.0.1:16379:localhost:6379 simon-pierreboucher@$PG_HOST; sleep 5; done # trouveka-tunloop-pg" \ >> "$HOME/trouveka-tunnels.log" 2>&1 & log "tunnel pg+redis relancé" } wait_port() { # $1 = port, $2 = secondes max t=0 while [ "$t" -lt "${2:-60}" ]; do port_up "$1" && return 0 sleep 2 t=$((t+2)) done log "attente port $1 : délai dépassé (${2:-60}s)" return 1 } ensure_worker() { pgrep -f trouveka.crawler.worker >/dev/null && return 0 [ -x "$TK/.venv/bin/python" ] || { log "worker : venv absent"; return 1; } # Au boot : laisser les tunnels s'établir avant de lancer le worker wait_port 15432 60 cd "$TK" && nohup .venv/bin/python -m trouveka.crawler.worker >> "$HOME/trouveka-crawler.log" 2>&1 & log "worker crawl relancé" } ensure_colima() { docker info >/dev/null 2>&1 && return 0 log "colima/docker arrêté : démarrage" colima start >> "$LOG" 2>&1 } case "$ROLE" in hub) ensure_colima cd "$TK" && docker compose up -d postgres redis >> "$LOG" 2>&1 ensure_tunnel search 9210 "$OS_HOST" 9200 ensure_tunnel embed 8191 "$EMBED_HOST" 8091 # pm2 : ressusciter la liste sauvegardée puis relancer tout process en erreur if ! pm2 pid tk-api >/dev/null 2>&1 || [ -z "$(pm2 pid tk-api 2>/dev/null)" ]; then pm2 resurrect >> "$LOG" 2>&1 log "pm2 resurrect" fi ERRORED=$(pm2 jlist 2>/dev/null | grep -o '"name":"[^"]*","pm2_env":{"status":"errored"' | grep -o 'tk-[a-z0-9-]*') for name in $ERRORED; do pm2 restart "$name" >> "$LOG" 2>&1; log "pm2 restart $name (errored)"; done # ngrok : LaunchAgent gui absent après reboot sans session → filet nohup if ! pgrep -f "ngrok http" >/dev/null; then nohup ngrok http --url=www.trouve-ka.com 3000 >> "$HOME/trouve-ka-ngrok.log" 2>&1 & log "ngrok relancé" fi ;; search) ensure_colima docker start trouveka-opensearch >/dev/null 2>&1 ensure_pg_redis_tunnel ensure_worker ;; embed) if ! pgrep -f "services.embedding.server" >/dev/null; then cd "$TK" && nohup .venv-embed/bin/uvicorn services.embedding.server:app --host 0.0.0.0 --port 8091 >> "$HOME/trouveka-embed.log" 2>&1 & log "service embeddings relancé" fi ensure_pg_redis_tunnel ensure_tunnel os 19200 "$OS_HOST" 9200 ensure_worker ;; crawler|*) ensure_pg_redis_tunnel ensure_tunnel os 19200 "$OS_HOST" 9200 ensure_worker ;; esac log "boot.sh terminé" exit 0