#!/usr/bin/env bash set -Eeuo pipefail IFS=$'\n\t' log() { printf '%s\n' "$*"; } log_err() { printf 'ERR: %s\n' "$*" >&2; } WORKERS=${WORKERS:-2} PORT=${PORT:-8090} BASE_DIR=${BASE_DIR:-"$(cd "$(dirname "$0")" && pwd)"} LOGS_DIR=${LOGS_DIR:-"/Volumes/Omnicron/AI_Brain/logs"} DB_URL=${DATABASE_URL:-} export DATABASE_URL="$DB_URL" if [[ -z "$DB_URL" ]]; then log_err "DATABASE_URL required in env" exit 1 fi VENV_BIN="$BASE_DIR/.venv/bin" PY_WORKER="$BASE_DIR/worker.py" PY_DASHBOARD="$BASE_DIR/dashboard.py" mkdir -p "$LOGS_DIR" log "Killing stale processes (workers/dashboard)" pgrep -f "$PY_WORKER" >/dev/null 2>&1 && pkill -f "$PY_WORKER" || true pgrep -f "$PY_DASHBOARD" >/dev/null 2>&1 && pkill -f "$PY_DASHBOARD" || true log "Starting $WORKERS workers" for i in $(seq 1 "$WORKERS"); do LOGS_DIR="$LOGS_DIR" WORKER_ID="$i" nohup "$VENV_BIN/python" "$PY_WORKER" \ >>"$LOGS_DIR/worker_${i}.nohup.out" 2>&1 & echo $! >> "$LOGS_DIR/workers.pids" done pick_port() { local p=$1 python3 - "$p" <<'PY' import socket, sys p = int(sys.argv[1]) for port in range(p, p+50): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(("0.0.0.0", port)) except OSError: continue print(port) break PY } FREE_PORT=$(pick_port "$PORT") log "Starting dashboard on port $FREE_PORT" nohup "$VENV_BIN/python" -m uvicorn dashboard:app --host 0.0.0.0 --port "$FREE_PORT" \ --app-dir "$BASE_DIR" >>"$LOGS_DIR/dashboard.nohup.out" 2>&1 & echo $! > "$LOGS_DIR/dashboard.pid" if command -v omnicron >/dev/null 2>&1; then omnicron notify info "Registered tile /dashboard/money_agents on port $FREE_PORT" else log "omnicron CLI not found; skipping tile registration" fi log "Done. Dashboard: http://localhost:$FREE_PORT"