#!/usr/bin/env bash # ───────────────────────────────────────────────────────────────────────────── # Deploy completo de la solución WxO. Idempotente, re-runnable. # # Hace en orden: # 1. Connections (draft Y live — issue I-004) # 2. Tools (Python + OpenAPI según haya) # 3. KBs # 4. Agents (specialists primero — issue A4) # 5. Deploy a live de todos los agents # 6. Channel webchat (si hay landing) # # Variables de control: # PUBLIC_HOST — host público donde corre tu stack # WXO_ENV_NAME — env del ADK a usar (default: el activo) # SKIP_TOOLS — saltar tools # SKIP_KB — saltar KBs # SKIP_CHANNEL — saltar canal webchat # AGENTS_GLOB — patrón para detectar agents (default: wxo/agents/*.agent.yaml) # ───────────────────────────────────────────────────────────────────────────── set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" # ─── Config (con defaults razonables) ─────────────────────────────────────── PUBLIC_HOST="${PUBLIC_HOST:-mi-cliente.fitlabs.dev}" PUBLIC_BASE="https://${PUBLIC_HOST}" SKIP_TOOLS="${SKIP_TOOLS:-false}" SKIP_KB="${SKIP_KB:-false}" SKIP_CHANNEL="${SKIP_CHANNEL:-false}" AGENTS_GLOB="${AGENTS_GLOB:-wxo/agents/*.agent.yaml}" echo "════════════════════════════════════════════════════════════════" echo " WxO deploy — $PUBLIC_BASE" echo " env activo : $(orchestrate env list 2>/dev/null | grep ACTIVE || echo '???')" echo "════════════════════════════════════════════════════════════════" # ─── 1. CONNECTIONS ───────────────────────────────────────────────────────── # Auto-detect: cada wxo/connections/*.yaml define un app_id. # Mapping del BASE_URL se hace por convención: # nombre del archivo (sin extensión) → / # Override por archivo en `scripts/deploy-wxo.conf`. echo "→ [1/6] Connections" for f in wxo/connections/*.yaml; do [ -f "$f" ] || continue app_id=$(grep -E '^name:' "$f" | head -1 | awk '{print $2}') [ -z "$app_id" ] && { echo " skip $f (no name)"; continue; } # Convención: BASE_URL = $PUBLIC_BASE/ basename="${f##*/}" basename="${basename%_connection.yaml}" basename="${basename%.yaml}" base_url="${PUBLIC_BASE}/${basename}" echo " • $app_id → $base_url" orchestrate connections add -a "$app_id" 2>/dev/null || true for ENV in draft live; do # Detectar kind (key_value | api_key | mcp) del YAML kind=$(grep -E '^auth_type:' "$f" | awk '{print $2}') [ -z "$kind" ] && kind="key_value" orchestrate connections configure -a "$app_id" --env "$ENV" --type team --kind "$kind" 2>/dev/null || true orchestrate connections set-credentials -a "$app_id" --env "$ENV" -e "BASE_URL=${base_url}" 2>/dev/null || true done done # ─── 2. TOOLS PYTHON ──────────────────────────────────────────────────────── if [ "$SKIP_TOOLS" != "true" ]; then echo "→ [2/6] Tools (Python)" if [ -f wxo/tools/python/requirements.txt ]; then for f in wxo/tools/python/*.py; do [ -f "$f" ] || continue # Saltar los archivos de soporte (empiezan con _) base="${f##*/}" case "$base" in _*) continue ;; esac # Derivar app_id del nombre (convención: _tools.py → app_demo o app) app_id="${base%_tools.py}_demo" echo " • $f → app-id $app_id" orchestrate tools import -k python \ -f "$f" \ -r wxo/tools/python/requirements.txt \ --app-id "$app_id" || echo " (already imported, skipping)" done fi # ─── 3. TOOLS OPENAPI ───────────────────────────────────────────────────── echo "→ [3/6] Tools (OpenAPI)" for f in wxo/tools/openapi/*.yaml wxo/tools/openapi/*.json; do [ -f "$f" ] || continue base="${f##*/}" case "$base" in _*) continue ;; esac # Derivar app_id del nombre app_id="${base%.yaml}" app_id="${app_id%.json}" echo " • $f → app-id $app_id" orchestrate tools import -k openapi -f "$f" --app-id "$app_id" || echo " (already imported, skipping)" done fi # ─── 4. KNOWLEDGE BASES ───────────────────────────────────────────────────── if [ "$SKIP_KB" != "true" ]; then echo "→ [4/6] Knowledge bases" for f in wxo/knowledge_base/*.kb.yaml; do [ -f "$f" ] || continue base="${f##*/}" case "$base" in _*) continue ;; esac echo " • $f" orchestrate knowledge-bases import -f "$f" || echo " (already imported, skipping)" done fi # ─── 5. AGENTS ────────────────────────────────────────────────────────────── # IMPORTANTE: specialists antes que orchestrators (issue A4). # Convención: si el archivo se llama *orchestrator* o *n1_* va último. echo "→ [5/6] Agents" SPECIALIST_FILES=() ORCHESTRATOR_FILES=() for f in $AGENTS_GLOB; do [ -f "$f" ] || continue base="${f##*/}" case "$base" in _*) continue ;; esac case "$base" in *orchestrator*|*n1_*) ORCHESTRATOR_FILES+=("$f") ;; *) SPECIALIST_FILES+=("$f") ;; esac done for f in "${SPECIALIST_FILES[@]}"; do echo " • specialist $f" orchestrate agents import -f "$f" done for f in "${ORCHESTRATOR_FILES[@]}"; do echo " • orchestrator $f" orchestrate agents import -f "$f" done # Deploy a live de todos echo "→ [5b/6] Deploying agents to LIVE" for f in "${SPECIALIST_FILES[@]}" "${ORCHESTRATOR_FILES[@]}"; do name=$(grep -E '^name:' "$f" | head -1 | awk '{print $2}') [ -z "$name" ] && continue echo " • $name → live" orchestrate agents deploy --name "$name" --env live || echo " (deploy failed, see logs)" done # ─── 6. CHANNEL WEBCHAT ───────────────────────────────────────────────────── if [ "$SKIP_CHANNEL" != "true" ] && [ ${#ORCHESTRATOR_FILES[@]} -gt 0 ]; then echo "→ [6/6] Channel webchat (bound to orchestrator)" for f in "${ORCHESTRATOR_FILES[@]}"; do name=$(grep -E '^name:' "$f" | head -1 | awk '{print $2}') [ -z "$name" ] && continue orchestrate channels create webchat \ --agent "$name" \ --name "${name} - Web" 2>/dev/null || echo " (channel for $name already exists)" done fi # ─── DONE ─────────────────────────────────────────────────────────────────── echo "════════════════════════════════════════════════════════════════" echo " ✓ Deploy completado." echo "" echo " Próximos pasos manuales:" echo " 1. orchestrate agents list → capturar agentId y agentEnvironmentId del orchestrator" echo " 2. Pegarlos en tu landing HTML y env vars" echo " 3. WxO Console → Settings → Embed Security → OFF (sin esto, embed falla)" echo " 4. ./evals/smoke-test.sh → validar end-to-end" echo "════════════════════════════════════════════════════════════════"