#!/usr/bin/env bash # ────────────────────────────────────────────────────────────────────────── # scripts/start_demo.sh — one-shot demo for supervisor showcase # # What it does: # 1. Kills any previous demo processes (uvicorn / cloudflared) # 2. Starts FastAPI on 127.0.0.1:8181 (clean env, isolated from other venvs) # 3. Waits until /api/health returns 200 # 4. Starts a Cloudflare Quick Tunnel and prints the public URL # 5. On Ctrl-C, cleanly shuts down both processes # # Usage: # ./scripts/start_demo.sh # # Prereqs (already done by the agent on this machine): # - .venv/ Python 3.9 venv with all deps installed # - .local/bin/cloudflared (macOS arm64, downloaded from GitHub releases) # - models/rf_model.pkl (217 MB, real ERA5-trained Random Forest) # ────────────────────────────────────────────────────────────────────────── set -euo pipefail PORT="${PORT:-8181}" ROOT="$(cd "$(dirname "$0")/.." && pwd)" LOG_DIR="${TMPDIR:-/tmp}" UVICORN_LOG="$LOG_DIR/mcx-uvicorn.log" TUNNEL_LOG="$LOG_DIR/mcx-tunnel.log" cd "$ROOT" # ── 1. Kill leftovers from a previous run ──────────────────────────────── pkill -f "uvicorn backend.main:app.*--port $PORT" 2>/dev/null || true pkill -f "cloudflared tunnel --url http://127.0.0.1:$PORT" 2>/dev/null || true sleep 1 # ── 2. Start FastAPI in the background ─────────────────────────────────── echo "▶ Starting FastAPI on http://127.0.0.1:$PORT …" env -u PYTHONPATH -u VIRTUAL_ENV -u PYTHONHOME \ ".venv/bin/python" -m uvicorn backend.main:app \ --host 127.0.0.1 --port "$PORT" \ > "$UVICORN_LOG" 2>&1 & UVICORN_PID=$! cleanup() { echo echo "▶ Shutting down (uvicorn=$UVICORN_PID, cloudflared=${CF_PID:-n/a})…" [[ -n "${CF_PID:-}" ]] && kill "$CF_PID" 2>/dev/null || true kill "$UVICORN_PID" 2>/dev/null || true wait 2>/dev/null || true echo "✓ Stopped. Logs preserved at:" echo " $UVICORN_LOG" echo " $TUNNEL_LOG" } trap cleanup EXIT INT TERM # ── 3. Wait for /api/health ────────────────────────────────────────────── printf " waiting for ML model load " for _ in $(seq 1 40); do if curl -sf --max-time 1 --noproxy '*' "http://127.0.0.1:$PORT/api/health" >/dev/null 2>&1; then echo " ✓" break fi printf "." sleep 1 done if ! curl -sf --max-time 1 --noproxy '*' "http://127.0.0.1:$PORT/api/health" >/dev/null 2>&1; then echo echo "❌ FastAPI did not become ready in 40 s. Last log lines:" tail -20 "$UVICORN_LOG" exit 1 fi HEALTH=$(curl -s --noproxy '*' "http://127.0.0.1:$PORT/api/health") ML_LOADED=$(echo "$HEALTH" | python3 -c 'import json,sys; print(json.load(sys.stdin)["ml_loaded"])' 2>/dev/null || echo "?") echo " ML model loaded: $ML_LOADED (response: ${HEALTH:0:80}…)" echo # ── 4. Start Cloudflare Quick Tunnel ───────────────────────────────────── echo "▶ Opening Cloudflare Quick Tunnel …" echo " (your public URL will print below as 'https://*.trycloudflare.com')" echo " ─────────────────────────────────────────────────────────────────" # Run cloudflared in foreground so the user sees the URL and can Ctrl-C. ./.local/bin/cloudflared tunnel --url "http://127.0.0.1:$PORT" 2>&1 | tee "$TUNNEL_LOG" & CF_PID=$! wait "$CF_PID"