#!/usr/bin/env bash set -euo pipefail API_PORT="${API_PORT:-8012}" DASHBOARD_PORT="${DASHBOARD_PORT:-3012}" API_BASE="http://127.0.0.1:${API_PORT}" DASHBOARD_URL="http://127.0.0.1:${DASHBOARD_PORT}/?api_base=${API_BASE}" API_LOG="${API_LOG:-/tmp/statehub-api-${API_PORT}.log}" DASHBOARD_LOG="${DASHBOARD_LOG:-/tmp/statehub-dashboard-${DASHBOARD_PORT}.log}" OVERVIEW_JSON="${OVERVIEW_JSON:-/tmp/statehub-overview-${API_PORT}.json}" OVERVIEW_HEADERS="${OVERVIEW_HEADERS:-/tmp/statehub-overview-${API_PORT}.headers}" DASHBOARD_HTML="${DASHBOARD_HTML:-/tmp/statehub-dashboard-${DASHBOARD_PORT}.html}" rm -f "$API_LOG" "$DASHBOARD_LOG" "$OVERVIEW_JSON" "$OVERVIEW_HEADERS" "$DASHBOARD_HTML" .venv/bin/python -m uvicorn api.main:app --host 127.0.0.1 --port "$API_PORT" \ > "$API_LOG" 2>&1 & api_pid=$! (cd dashboard && npm run dev -- --host 127.0.0.1 --port "$DASHBOARD_PORT" \ > "$DASHBOARD_LOG" 2>&1) & dashboard_pid=$! cleanup() { kill "$api_pid" "$dashboard_pid" 2>/dev/null || true } trap cleanup EXIT wait_for_url() { local label="$1" local url="$2" local output="$3" local attempts="${4:-40}" local i for i in $(seq 1 "$attempts"); do if curl -fsS "$url" -o "$output" >/dev/null 2>&1; then return 0 fi sleep 1 done echo "$label did not become ready: $url" >&2 return 1 } if ! wait_for_url "API overview" "${API_BASE}/state/overview" "$OVERVIEW_JSON"; then echo "API log:" >&2 tail -80 "$API_LOG" >&2 || true exit 1 fi if ! wait_for_url "Dashboard" "$DASHBOARD_URL" "$DASHBOARD_HTML"; then echo "Dashboard log:" >&2 tail -80 "$DASHBOARD_LOG" >&2 || true exit 1 fi curl -sS -D "$OVERVIEW_HEADERS" -o "$OVERVIEW_JSON" \ -w "overview %{http_code} %{time_total} %{size_download}\n" \ "${API_BASE}/state/overview" printf "dashboard 200 %s\n" "$DASHBOARD_URL" wc -c "$OVERVIEW_JSON" grep -i "x-statehub" "$OVERVIEW_HEADERS" || true