Vistas previas
Una vista previa pone algo con interfaz web en un enlace público: un servidor de desarrollo que escucha en un puerto de la máquina del relay, o una carpeta de archivos sin más. Abres el enlace desde cualquier dispositivo y puedes enviárselo a quien quieras. El relay no queda expuesto: las vistas previas las sirve un listener aparte en un nombre de host aparte.
Usos típicos:
- Abrir la app en tu teléfono. El agente arranca el servidor de desarrollo, tú tocas el enlace y pruebas el diseño en el dispositivo real.
- Enviarle un enlace a alguien. Un compañero abre una maqueta o un sitio compilado sin acceso a tu máquina ni a tu relay.
- Apuntar un webhook a un servidor de desarrollo. Un servicio que necesita una dirección pública puede llamar a la app que estás construyendo.
Esta página es la guía para propietarios y operadores. La herramienta del agente, con sus parámetros, resultados y límites, se describe en preview.
Un enlace de vista previa es público
El token del enlace (128 bits) es la única protección. Cualquiera que tenga el enlace llega a la app sin iniciar sesión. Una vista previa de algo con datos reales es una exposición real. Cierra una vista previa cuando termines.
Cómo se mantienen las vistas previas separadas del relay #
Las vistas previas las sirve un segundo listener HTTP en su propio puerto, publicado en un nombre de host propio. De ahí se siguen dos cosas:
- El relay no está en ese puerto. Su API, su app web, su conexión en vivo y sus enlaces compartidos no están montados allí. El tráfico de las vistas previas no puede llegar a ellos, porque no existen en ese listener. Crear o cerrar una vista previa requiere el token de acceso del relay y solo ocurre en el propio puerto del relay.
- El nombre de host es distinto. Una app en vista previa se ejecuta en otro origen del navegador que el relay. Su JavaScript no puede enviar tu cookie de inicio de sesión del relay ni leer el almacenamiento de la app web del relay.
Tiene que cambiar el nombre de host, no solo el puerto. Las cookies ignoran los
puertos, así que relay. compartiría la cookie de inicio de
sesión de relay.. El relay se niega a arrancar la lane cuando los
nombres de host coinciden.
Además, la lane nunca reenvía a una app en vista previa la cookie de inicio de
sesión del relay (copilot_) ni una cabecera Authorization que contenga el
token de acceso del relay, aunque una petición hecha a mano los lleve. La
cabecera Authorization propia de una app pasa sin cambios.
Las vistas previas sí comparten un nombre de host entre ellas, cada una bajo su propia ruta. Consulta Lo que hay que tener en cuenta.
Configurarlo #
Las vistas previas están desactivadas hasta que el operador las configura. Son tres pasos.
1. Un nombre de host #
Elige un nombre de host que controles y cuyo nombre sea distinto del del relay,
por ejemplo preview. junto a relay.. Apunta su DNS a tu
proxy frontal o a tu túnel. Las vistas previas se separan por ruta, no por
subdominio, así que basta con un único nombre de host de una etiqueta bajo una
zona que ya tengas. En Cloudflare, ese nombre lo cubre el certificado Universal.
Un comodín de dos etiquetas como *. no lo estaría, y por
eso no hay un subdominio por vista previa.
2. Un proxy frontal #
El proxy envía el nombre de host de las vistas previas al listener de vistas previas, que por defecto
usa el puerto del relay más uno (3334 cuando el relay usa 3333). Usa un
puerto que fijes con previews.; el proxy necesita un destino estable.
Caddy
preview.example.com {
reverse_proxy 127.0.0.1:3334
}
Cloudflare Tunnel
Añade un segundo nombre de host público al túnel que ya usas, en el panel de
Cloudflare en Public Hostnames → Add (Nombres de host públicos → Añadir) o en el config. del túnel:
ingress:
- hostname: preview.example.com
service: http://127.0.0.1:3334
- hostname: relay.example.com
service: http://127.0.0.1:3333
- service: http_status:404
3. El bloque previews #
Añade el bloque al config. del relay y reinicia el relay (consulta
oar restart):
{
"publicHostnames": ["relay.example.com"],
"previews": {
"enabled": true,
"port": 3334,
"bindHost": "127.0.0.1",
"publicBaseUrl": "https://preview.example.com",
"allowedTargetHosts": [],
"maxLive": 8
}
}
| Clave | Qué poner ahí |
|---|---|
public |
Todos los nombres de host en los que responde el propio relay. La comprobación de public usa esta lista (y siempre localhost y 127.), así que un nombre de host que dejes fuera no se comprueba. |
previews. |
true para activar la lane. Está desactivada por defecto. |
previews. |
El puerto del listener. Por defecto: el puerto del relay más uno. 0 elige un puerto libre en cada arranque, algo que un proxy frontal no puede seguir. |
previews. |
Dónde escucha el listener. Por defecto 127.. Mantenlo en loopback: el proxy frontal se ejecuta en la misma máquina. |
previews. |
Solo hace falta cuando bind no es loopback. Cada vista previa queda entonces accesible desde la red sin pasar por el proxy. |
previews. |
La dirección pública de la lane: https://. Los enlaces se construyen a partir de ella. |
previews. |
Hosts, además de esta máquina, a los que puede apuntar una vista previa de puerto, como un contenedor o una VM. |
previews. |
Máximo de vistas previas a la vez, para todo el relay. Por defecto 8. |
Cada clave, valor predeterminado y caso límite está en la tabla de config.json.
Sobrescrituras por entorno #
COPILOT_, COPILOT_,
COPILOT_, COPILOT_ y
COPILOT_ sobrescriben las claves correspondientes.
Sus valores y qué prevalece sobre qué están en
Variables de entorno.
Cuando la lane se niega a arrancar #
Una lane mal configurada nunca tumba el relay. El relay sigue funcionando y la lane queda desactivada. Ninguna de estas comprobaciones se convierte en un aviso, porque cada una protege la separación descrita arriba.
| Motivo | Mensaje |
|---|---|
Falta public o no es una dirección http(s) |
previews. |
public usa un nombre de host del relay |
previews. |
| El puerto es inferior a 1024 | previews. |
| El puerto es el del relay o su puerto de CLI | previews. |
| No se puede determinar el puerto | previews. |
bind no es loopback |
previews. |
Lo ves en dos sitios:
- El registro del relay, al arrancar: una línea
PREVIEW LANE disabled: <message>por cada comprobación fallida. Cuando las comprobaciones pasan, el registro dice[preview] listener on http://. Dónde está el registro: Archivos y carpetas.127. 0. 0. 1: 3334 → https:// preview. example. com - La respuesta a una petición de publicación. La API responde 503 con
Preview lane failed its startup checksy los mensajes endetails. La herramientapreviewdel agente lo transmite, y al agente se le indica que te lo diga. Cuandopreviews.no está fijado, la respuesta esenabled Preview lane is disabled (set previews., y el registro no tiene ninguna línea.enabled and previews. public Base Url)
Settings → Previews (Ajustes → Vistas previas) y el aviso de / no
muestran el motivo. La pestaña de ajustes lista las vistas previas que existen, y
el aviso solo dice que la publicación falló (consulta /).
Mira en el registro del relay.
Si otro programa ocupa el puerto, las comprobaciones pasan, pero el listener no
puede escuchar en él. El registro dice entonces
PREVIEW LANE failed to bind :<port> — <reason>. Para la API, la lane está
activa, pero nada responde en el puerto de vistas previas, así que los enlaces no
se abren. Libera el puerto o fija otro previews..
Tres formas de publicar #
Pedírselo al agente #
Di lo que quieres: «arranca el servidor de desarrollo y dame un enlace»,
«enséñame el sitio compilado». El agente usa la herramienta preview, o la API de
vistas previas del relay cuando un proveedor no tiene la herramienta. Te da el
enlace y te dice que es público. Qué proveedores tienen la herramienta está en la
tabla de la página de referencia.
/preview en el área de redacción #
El comando se ejecuta en tu navegador contra el relay. Funciona con todos los proveedores y no cuesta ningún turn del agente. Se ejecuta cuando no hay nada adjunto al mensaje. El aviso aparece en la conversación.
| Escribes | Qué ocurre |
|---|---|
/ |
Publica el puerto 5173 con la etiqueta «web app». Imprime Preview published: <url>. |
/ |
Publica la carpeta ./ como vista previa estática, con la etiqueta «built site». La primera palabra es la carpeta, así que no puede contener espacios. Imprime Preview published: <url>. |
/ |
Lista todas las vistas previas del relay, una por línea: los 8 primeros caracteres del token, la etiqueta, el puerto o la carpeta, — offline si lo está, y el enlace. Imprime No live previews. si no hay ninguna, y The preview lane is disabled on this relay. cuando está desactivada. |
/ |
Cierra la vista previa de esta conversación. Si no hay ninguna, imprime No live previews in this conversation. Si hay varias, imprime las coincidencias y pide un prefijo más largo. |
/ |
Cierra la vista previa cuyo token empieza por esos 4 a 32 caracteres hexadecimales, en cualquier conversación. Imprime Preview closed: <label>. Sin coincidencias: No preview matches "3f9c". |
/ |
Imprime la línea de uso, igual que cualquier cosa que no pueda interpretar. |
Una primera palabra formada por dígitos es siempre un puerto, y un puerto fuera
del rango 1 a 65535 imprime la línea de uso. Para una carpeta llamada 8080,
escribe ./.
Si la publicación falla, el aviso es el mismo para todas las causas (un puerto incorrecto, una carpeta fuera de la carpeta de trabajo, una lane llena, una lane desactivada):
Preview failed — check the port/path, or whether the preview lane is enabled (Settings → Live previews).
El motivo exacto no se muestra. La lista está en Settings → Previews, bajo
el encabezado Live previews (Vistas previas en vivo). Para ver el motivo,
llama a la API (abajo) o mira la tabla de Solución de problemas.
Si no se puede llegar al relay, el aviso es Preview command failed — could not reach the relay.
El menú de comandos y sus teclas están en
El área de redacción.
La API #
Todo lo anterior usa la misma API en el puerto del relay. Necesita el
token de acceso del relay (auth en config., el mismo token con el que inicias sesión) como
bearer token. Regístrala primero, para conocer la ruta base, y luego arranca el
servidor de desarrollo bajo ella:
curl -sX POST http://127.0.0.1:3333/api/previews \
-H "authorization: Bearer $RELAY_TOKEN" -H 'content-type: application/json' \
-d '{"conversationId":"<conversation id>","port":5173,"label":"web app"}'
{
"ok": true,
"url": "https://preview.example.com/test_0123456789abcdef0123456789abcdef/",
"basePath": "/test_0123456789abcdef0123456789abcdef/",
"preview": { "token": "0123456789abcdef0123456789abcdef" },
"existing": false
}
existing es true cuando la conversación ya tenía una vista previa activa del
mismo puerto o carpeta y te devolvió esa.
| Petición | Finalidad |
|---|---|
POST / |
port o dir (no ambos) y, opcionalmente, conversation, host, label. |
GET / |
Todas las vistas previas. ?conversation, o la cabecera de conversación que envía un worker de sesión, la limita a una conversación. |
DELETE / |
Cierra una. El servidor de desarrollo no se toca. Nuevo en 0.9.14 Una petición que nombra una conversación (como en GET) solo puede cerrar las vistas previas de esa conversación; la de otra se rechaza (403). |
Hacer que una app funcione bajo la ruta de la vista previa #
Cada vista previa vive bajo / en el nombre de host compartido de
las vistas previas. La regla que se deriva de lo que hace el proxy:
- El navegador pide
/.test_<token>/ … - La app recibe la ruta sin ese prefijo.
- Así que la app debe poner en sus páginas enlaces y URL de recursos que lleven el prefijo o sean relativos, y aun así debe responder a peticiones que no lo lleven.
Lo que hace el proxy por el camino:
| El proxy | |
|---|---|
| Quita el prefijo | La app recibe la ruta que sigue a /, con la query. Una visita a / a secas se redirige (301) a la misma dirección con una barra final, para que las URL relativas se resuelvan dentro de la vista previa. |
Envía X- |
El prefijo sin barra final. |
Envía X- |
El Host que recibió el listener de vistas previas. |
Envía X- |
El que envió el proxy frontal; https si no envió ninguno. |
Envía X- |
La dirección de quien se conectó al listener. Detrás de un proxy frontal en la misma máquina, es el proxy. |
Fija Host |
A la dirección propia de la app (127.), para los servidores de desarrollo que la comprueban. |
Reescribe Location |
Una redirección a / pasa a ser /, igual que una redirección absoluta a la dirección propia de la app. Una redirección que ya lleva el prefijo, una con //, una relativa o una a otro sitio se deja como está. |
Acota Set- |
La cookie recibe Path=/ y pierde Domain, para que las cookies de una vista previa no lleguen a otra. Una cookie llamada copilot_ se descarta. |
| Marca las respuestas | X-, y descarta Service-. |
No reescribe el cuerpo de páginas, hojas de estilo ni scripts. Una app que
escribe / en su HTML lo pide a la raíz del nombre de host de las
vistas previas, donde no vive ninguna vista previa, y el navegador recibe la
página No such preview (404). Esa es la señal de que la app no conoce su
prefijo.
Lo que funciona #
- URL relativas. Una app cuyas páginas usan direcciones del estilo
./funciona tal cual: la página está enassets/ app. js /, así que se resuelven dentro de la vista previa. Esto se deriva de cómo funciona el proxy; no se probó framework por framework.test_<token>/ - Leer el prefijo. Una app que enruta por rutas sin prefijo y construye sus
enlaces a partir de
X-cumple la regla exactamente.Forwarded- Prefix - Un sitio compilado. Compila con una base relativa o igual al prefijo y
luego publica la carpeta de salida como vista previa estática. Las
peticiones de
/se sirven desde la carpeta, así que funcionan los dos tipos de URL.test_<token>/ assets/ …
Lo que no funciona tal cual #
Un ajuste de la herramienta que hace que la app responda solo a peticiones que llevan el prefijo no encaja, porque el proxy lo quita.
- Servidor de desarrollo de Vite con
basefijado al prefijo (--base). Comprobado contra el código fuente de Vite 8.3.1 en la máquina del relay: el servidor de desarrollo solo atiende rutas que empiezan porbase. Responde a una petición de/con una redirección abase, que el proxy deja como está, así que el navegador entra en un bucle. Las demás peticiones sin prefijo reciben un 404 que dice que el servidor tiene una URL base pública. La recarga en caliente solo escucha en la ruta bajobase, con la que no coincide la petición recortada. Usa el enfoque del sitio compilado, o una configuración de Vite que sirva desde la raíz con URL relativas. - Express con el router montado en el prefijo
(
app.). La app recibeuse(prefix, router) /, nox /, así que el montaje no coincide. Monta el router entest_<token>/ x /y construye los enlaces a partir deX-.Forwarded- Prefix
Next.js (base, asset) y SvelteKit (kit.) se mencionan
en las notas propias de OAR para los agentes, pero esas notas no se comprobaron
contra lo que hace el proxy, y ninguna de las dos herramientas está disponible
aquí para probarla. Si su ajuste de base hace que la app espere el prefijo en
cada petición, no encaja por la misma razón que Vite. Sin verificar: pruébalo
antes de confiar en ello.
Publica primero cuando la app necesita el prefijo
Un agente llama a create antes de arrancar el servidor de desarrollo, para
poder darle el prefijo a la app si lo necesita. Para una app que funciona con URL
relativas, el orden da igual.
Vistas previas estáticas #
Con dir en lugar de port, el propio relay sirve una carpeta. No tiene que
ejecutarse nada, lo que encaja con una salida de compilación, una maqueta, un
único archivo HTML o un informe de cobertura.
- La carpeta se toma de la carpeta de trabajo de la conversación (absoluta, o relativa a ella) y debe estar dentro de ella. Una vista previa publicada sin conversación usa la propia carpeta de trabajo del relay. Esto se comprueba al publicar y en cada petición, así que un enlace simbólico creado más tarde no puede llevar fuera.
- Para una ruta de carpeta se sirve
index.. No se almacena nada en caché.html - Nunca se sirven: archivos y carpetas cuyo nombre empieza por un punto (
.,git .) ni archivos de claves (env *.,pem *., nombres que empiezan porkey id_,rsa id_,ed25519 id_oecdsa id_).dsa - Las rutas relativas de recursos funcionan. También las que empiezan por el
prefijo de la vista previa. Las absolutas desde la raíz (
/), no.assets/ app. js - Una carpeta sin
index., y cualquier archivo que falte, recibe una páginahtml Not found(404). - Una vista previa estática está siempre
onliney rechaza las conexiones WebSocket.
Gestionar las vistas previas #
- La tarjeta en el panel de tareas en segundo plano. Cada vista previa de la
conversación tiene una tarjeta con su etiqueta, el puerto, una nota de que el
enlace es público, una insignia
onlineuofflinecuando el relay la ha comprobado, y los botones Open (Abrir), Copy link (Copiar enlace) y Close (Cerrar). El panel se describe en Tareas en segundo plano. - Una tarjeta de enlace en la transcripción. Cuando se publica una vista
previa mientras corre un turn de la conversación, la respuesta lleva una
tarjeta de enlace, la haya publicado el agente o tú con
/. Una vista previa publicada entre turns no tiene tarjeta en la transcripción. Cuando la vista previa ya no existe, la tarjeta muestra una insigniapreview closedy ningún enlace. Una conversación compartida muestra la tarjeta solo con su etiqueta, y cada enlace de vista previa citado en su texto dice[preview link removed]. - Settings → Previews. La lista Live previews muestra todas las vistas previas del relay, de todas las conversaciones, con los 8 primeros caracteres del id de la conversación y Nuevo en 0.9.11 cuándo se publicó y cuándo se abrió por última vez. Cierra una aquí cuando hayas salido de su conversación. Consulta Ajustes.
offline y closed son cosas distintas. El relay comprueba cada vista previa de
puerto cada 15 segundos, intentando conectarse al puerto (y una vez de inmediato
al publicar). Cuando nada responde, la tarjeta muestra una insignia offline y
la nota «offline — dev server not responding». El enlace sigue siendo válido:
vuelve a arrancar el servidor en el mismo puerto y el mismo enlace funciona.
closed significa que la vista previa ya no existe y el enlace ya no se resuelve.
Cerrar una vista previa solo termina el enlace. El servidor de desarrollo sigue ejecutándose como la tarea en segundo plano que es.
Una vista previa no caduca sola. Termina cuando la cierras, o Nuevo en 0.9.11 cuando eliminas su conversación (también mediante una reconstrucción de la caché del relay). Archivar una conversación mantiene activas sus vistas previas, en Settings → Previews.
Publicar de nuevo el mismo puerto o carpeta en la misma conversación devuelve la vista previa que ya está activa, con el mismo enlace y la etiqueta sin cambios, en lugar de una nueva.
Como máximo hay 8 vistas previas activas a la vez, para todo el relay
(previews.). El rechazo nombra las tres más antiguas, con cuándo se
publicó y se abrió por última vez cada una, y cómo cerrarlas.
Después de un reinicio del relay Nuevo en 0.9.11 #
Las vistas previas sobreviven a un reinicio del relay, a una actualización o a un fallo: el relay las guarda en su base de datos y las restaura al arrancar, así que un enlace que hayas repartido sigue funcionando. Cada una se comprueba antes de nuevo, y termina cuando la comprobación determina que ya no es válida:
- una vista previa de carpeta cuya carpeta ya no existe, o ya no está dentro de la carpeta de trabajo de su conversación;
- una vista previa de puerto después de reiniciar la máquina: el servidor de desarrollo ya no existe, y el puerto puede pertenecer más adelante a otra cosa;
- una vista previa cuya conversación ya no existe.
Linux y macOS informan de un reinicio de forma fiable. En Windows, un Restart (Reiniciar) cuenta como tal, pero con fast startup (inicio rápido) activado (el valor predeterminado de Windows), Shut down (Apagar) y volver a encender no: una vista previa de puerto de antes vuelve y llega a lo que escuche entonces en su puerto. Cierra las vistas previas que ya no necesites.
Una comprobación que no puede responder, porque la base de datos está ocupada o
la carpeta no se puede leer por otro motivo que no sea que ya no existe, no
termina la vista previa. Mientras tanto no se sirve: Settings → Previews y el
list de la herramienta la muestran como no servida (unverified), cuenta para
el límite y se puede cerrar. El relay la vuelve a comprobar cada 15 segundos y la
sirve de nuevo bajo su enlace en cuanto la comprobación responde; publicar de
nuevo el mismo puerto o carpeta en su conversación lo hace al instante. Tras diez
minutos sin respuesta, la vista previa termina, y el registro lo indica.
Una vista previa de puerto también se sondea una vez justo después del arranque,
para que su insignia online u offline esté al día. El registro del relay
nombra una vista previa por un hash corto, nunca por su token. Consulta también
Lo que conserva y lo que pierde un reinicio.
WebSockets #
Una vista previa de puerto deja pasar las conexiones WebSocket bajo el mismo prefijo, quitándolo como en cualquier petición, así que los sockets propios de una app funcionan cuando el navegador se conecta a través del enlace de la vista previa. (La recarga en caliente es cosa de la app: consulta la nota sobre Vite de arriba.) Una vista previa estática, y un enlace desconocido o cerrado, responden a una petición WebSocket con 404. Si la app responde al upgrade con una respuesta normal, el navegador recibe ese estado. Si la app no está en marcha, 502.
Lo que hay que tener en cuenta #
- Las vistas previas comparten origen entre ellas. Las cookies se acotan a la ruta de cada vista previa y los tokens no se pueden adivinar, pero dos vistas previas no están tan separadas entre sí como lo está cada una del relay.
- Solo tu propia máquina, por defecto. Una vista previa de puerto solo puede
apuntar a direcciones de loopback, más los hosts de
previews.. Los propios puertos del relay siempre se rechazan, ya que publicar uno pondría la página de inicio de sesión del relay en el nombre de host público.allowed Target Hosts - El relay no ejecuta la app. Nunca arranca ni reinicia un servidor de
desarrollo. Si el servidor se detiene, la tarjeta pasa a
offline.
Por proveedor #
Las vistas previas se ven igual en todos los proveedores; solo cambia la forma en
que publica un agente, como muestra la
página de referencia.
/ y Settings → Previews funcionan con todos ellos.
Solución de problemas #
| Lo que ves | Causa y solución |
|---|---|
El agente o la API responde Preview lane is disabled o Preview lane failed its startup checks (503) |
La lane está desactivada o se negó a arrancar. Lee las líneas PREVIEW LANE disabled del registro del relay, corrige previews y reinicia el relay. Consulta Cuando la lane se niega a arrancar. |
El enlace muestra una página Preview upstream unreachable (502) |
Nada escucha en el puerto, o la conexión falló. Vuelve a arrancar el servidor de desarrollo en el mismo puerto y recarga; el enlace sigue siendo válido. |
Faltan recursos o la página sale en blanco, y las peticiones de / reciben No such preview (404) |
La app escribe rutas absolutas desde la raíz. Haz que emita URL relativas o con prefijo (regla). |
| La página se carga en bucle, o la app responde 404 con una nota sobre una «public base URL» | La app espera el prefijo en cada petición, pero el proxy lo quita (lo que no funciona). |
No such preview en todo el enlace |
El enlace es desconocido o está cerrado, su conversación se eliminó, o un reinicio determinó que ya no es válido (después de un reinicio). Vuelve a publicar. |
Una vista previa aparece como no servida (unverified) |
El relay no pudo comprobarla al arrancar. Se vuelve a comprobar cada 15 segundos; publicar de nuevo el mismo destino en su conversación la recupera al instante. |
| El enlace no se abre en absoluto | El proxy frontal o el DNS no están configurados, o el listener no pudo escuchar en su puerto (PREVIEW LANE failed to bind en el registro). |
La tarjeta dice offline |
El servidor de desarrollo ha dejado de responder. Vuelve a arrancarlo en el mismo puerto. |
Too many live previews (max 8); previews stay until closed. Oldest: … (429) |
Cierra una en Settings → Previews, con / o con DELETE /; un agente puede cerrar las de su propia conversación con la acción close de la herramienta; o sube previews.. |
Port <n> belongs to the relay and cannot be previewed (400) |
Diste el puerto del relay, su puerto de CLI o el puerto de vistas previas. Publica el puerto del servidor de desarrollo. |
Target port must be between 1024 and 65535 (400) |
Usa un puerto más alto. |
Target host ... is not loopback and not in previews. (400) |
Añade el host a previews.. |
dir must live inside the workspace root (..., Directory not found: ... o Not a directory: ... (400) |
La carpeta debe existir y estar dentro de la carpeta de trabajo de la conversación. |