El funnel promete "te escribimos por WhatsApp" pero el bot solo registraba la
sesión y esperaba a que el cliente escribiera primero → no llegaba nada. Ahora
WhatsappService escucha un startEmitter y manda el mensaje de apertura de Luisa
al teléfono (verifica el número con onWhatsApp), persiste estadoWa/botStep y el
intento. Además normaliza la clave de teléfono a solo-dígitos en leadSessions
(antes "+34..." no casaba con los dígitos del jid entrante → ignoraba al cliente).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review de seguridad: autenticar por query string filtra el secreto (logs,
historial, Referer) y usaba la misma FUNNEL_API_KEY que autoriza la API.
Ahora /qr usa HTTP Basic (credencial en cabecera), un QR_TOKEN dedicado
distinto de FUNNEL_API_KEY, comparación en tiempo constante (timingSafeEqual
sobre hashes) y Referrer-Policy: no-referrer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El QR de Baileys solo salía como ASCII en los logs (ilegible en Dokploy). Ahora
el WhatsappService empuja el QR al WebhookListener, que sirve GET /qr?key=
<FUNNEL_API_KEY> con el código como imagen (lib qrcode), autorrefresco y estado
"ya conectado". Añade dependencia qrcode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sandbox web (/sandbox) servido por el worker, reusando los mismos servicios del
pipeline, para iterar prompts/modelos con una imagen y ver variaciones+score, y
guardar la config ganadora (aplica al worker al instante, persistida en volumen):
- SettingsService: store de config (system prompts + modelos) con defaults de
prompts/*.txt + env y override persistido en /app/data (sobrevive a redeploys).
- /sandbox (HTML), GET /sandbox/config, POST /sandbox/render, POST /sandbox/save
(auth Bearer FUNNEL_API_KEY). DOM seguro, sin innerHTML de contenido externo.
- prompt-builder/supervisor/image-generator aceptan overrides y leen de Settings.
Fixes del pipeline de generación:
- image-generator: pide modalities ['image','text'] y extrae la imagen de
message.images[] (forma real de OpenRouter), no solo de content.
- main.ts: sube el límite de body a 30mb (las fotos en data URI rompían el 100kb).
Deja de versionar artefactos de build (dist/ + *.tsbuildinfo); .gitignore en
image-worker y Whatsapp-bot.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Dockerfiles multistage (node:20-alpine) + .dockerignore para mvp/Whatsapp-bot
(expone 3000+3001, persiste prompts) y mvp/image-worker (expone 3001).
- Añade los 2 GET que el bot necesita y faltaban en la API:
GET /api/leads/:id (estado del lead) y GET /api/leads/:id/conversacion (historial).
- Limpia el package.json/lock de la raíz (baileys colado por error; no es workspace).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>