#!/usr/bin/env bash set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" LAB_ROOT="${AZEROTHAI_PLAYABLE_ROOT:-$HOME/azerothai-pc-lab}" DEV_PORT="${AZEROTHAI_REMOTE_UI_DEV_PORT:-5173}" PROXY_PORT="${AZEROTHAI_TAILNET_PROXY_PORT:-8788}" APP_PORT="${AZEROTHAI_APP_PORT:-8787}" PID_FILE="$LAB_ROOT/azerothai-ui-dev.pid" LOG_FILE="$LAB_ROOT/azerothai-ui-dev.log" NGINX_SITE="/etc/nginx/sites-available/azerothai-tailnet" NGINX_LINK="/etc/nginx/sites-enabled/azerothai-tailnet" need(){ command -v "$1" >/dev/null 2>&1 || { echo "Missing required command: $1" >&2; exit 1; }; } need curl need tailscale need pnpm TAILSCALE_IP="$(tailscale ip -4 2>/dev/null | head -n1 || true)" if [[ -z "$TAILSCALE_IP" ]]; then echo "No active Tailscale IPv4 address found." >&2 exit 2 fi DNS_NAME="$(tailscale status --json 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); print((d.get("Self",{}).get("DNSName") or "").rstrip("."))' 2>/dev/null || true)" echo "==> Rebuilding live AzerothAI backend with current source" cd "$REPO_ROOT" pnpm build if [[ -f "$LAB_ROOT/azerothai.pid" ]]; then OLD_APP_PID="$(cat "$LAB_ROOT/azerothai.pid" 2>/dev/null || true)" if [[ -n "$OLD_APP_PID" ]]; then kill "$OLD_APP_PID" 2>/dev/null || true; fi fi pkill -f "wrangler.*8787" 2>/dev/null || true pkill -f "pnpm.*start:lan" 2>/dev/null || true sleep 2 nohup pnpm start:lan >"$LAB_ROOT/azerothai.log" 2>&1 & echo $! > "$LAB_ROOT/azerothai.pid" echo "==> Waiting for live AzerothAI backend" for _ in {1..90}; do if curl -fsS "http://127.0.0.1:$APP_PORT/api/health" >/dev/null 2>&1; then break; fi sleep 1 done curl -fsS "http://127.0.0.1:$APP_PORT/api/health" >/dev/null echo "==> Restarting autonomous high-level service if this lab is configured" RUNTIME_ENV="$LAB_ROOT/runtime.env" if [[ -f "$RUNTIME_ENV" ]] && grep -q '^AUTONOMY_TOKEN=' "$RUNTIME_ENV"; then bash "$REPO_ROOT/deploy/playable/install-autonomy-service.sh" fi echo "==> Ensuring durable ALE database relay is active" if [[ -f "$RUNTIME_ENV" ]] && grep -q '^ALE_BRIDGE_TOKEN=' "$RUNTIME_ENV" && grep -q '^DOCKER_DB_ROOT_PASSWORD=' "$RUNTIME_ENV"; then bash "$REPO_ROOT/deploy/playable/install-ale-relay-service.sh" fi echo "==> Starting hydration-safe dashboard UI" mkdir -p "$LAB_ROOT" if [[ -f "$PID_FILE" ]]; then OLD_PID="$(cat "$PID_FILE" 2>/dev/null || true)" if [[ -n "$OLD_PID" ]]; then kill "$OLD_PID" 2>/dev/null || true; fi fi pkill -f "vinext.*$DEV_PORT" 2>/dev/null || true cd "$REPO_ROOT" nohup pnpm dev >"$LOG_FILE" 2>&1 & echo $! > "$PID_FILE" for _ in {1..90}; do if curl -fsS "http://127.0.0.1:$DEV_PORT/" >/dev/null 2>&1; then break fi sleep 1 done if ! curl -fsS "http://127.0.0.1:$DEV_PORT/" >/dev/null 2>&1; then echo "Dashboard UI dev server did not start." >&2 tail -80 "$LOG_FILE" >&2 || true exit 3 fi echo "==> Installing nginx if needed" if ! command -v nginx >/dev/null 2>&1; then sudo apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y nginx fi echo "==> Wiring UI to the live AzerothAI API" sudo tee "$NGINX_SITE" >/dev/null < Verifying dashboard and live API through proxy" VERIFY_HOST="${DNS_NAME:-$TAILSCALE_IP}" curl -fsS -H "Host: $VERIFY_HOST" "http://127.0.0.1:$PROXY_PORT/" >/dev/null curl -fsS -H "Host: $VERIFY_HOST" "http://127.0.0.1:$PROXY_PORT/api/state" >/dev/null ORIGIN="https://$VERIFY_HOST" ACTION_BODY="$(mktemp)" ACTION_CODE="$(curl -sS -o "$ACTION_BODY" -w '%{http_code}' -X POST -H "Host: $VERIFY_HOST" -H "Origin: $ORIGIN" -H "Content-Type: application/json" --data '{}' "http://127.0.0.1:$PROXY_PORT/api/actions" || true)" if [[ "$ACTION_CODE" == "403" ]] && grep -q "Cross-origin action rejected" "$ACTION_BODY"; then echo "Remote action origin check is still rejecting the trusted tailnet host." >&2 cat "$ACTION_BODY" >&2 rm -f "$ACTION_BODY" exit 47 fi rm -f "$ACTION_BODY" echo "Remote action origin check: accepted (HTTP $ACTION_CODE expected for empty test action)" echo "==> Publishing privately with Tailscale Serve" sudo tailscale serve --bg "$PROXY_PORT" STAMP="$(date +%s)" echo echo "============================================================" echo " AzerothAI remote dashboard is ready" echo "============================================================" if [[ -n "$DNS_NAME" ]]; then echo "Open this fresh URL:" echo " https://$DNS_NAME/?v=$STAMP" else echo "Run: tailscale serve status" fi echo echo "UI log: $LOG_FILE" echo "Live API remains on port $APP_PORT."