Toutes les pages
Sur cette page

Aperçus

Un aperçu met quelque chose qui a une interface web sur un lien public : un serveur de dev qui écoute sur un port de la machine du relais, ou un dossier de fichiers ordinaires. Vous ouvrez le lien depuis n’importe quel appareil, et vous pouvez l’envoyer à n’importe qui. Le relais n’est pas exposé : les aperçus sont servis par un écouteur distinct, sur un nom d’hôte distinct.

Usages typiques :

  • Ouvrir l’app sur votre téléphone. L’agent démarre le serveur de dev, vous appuyez sur le lien et essayez la mise en page sur l’appareil réel.
  • Envoyer un lien à quelqu’un. Un collègue ouvre une maquette ou un site construit sans accès à votre machine ni à votre relais.
  • Diriger un webhook vers un serveur de dev. Un service qui a besoin d’une adresse publique peut appeler l’app que vous construisez.

Cette page est le guide des propriétaires et des opérateurs. L’outil de l’agent, avec ses paramètres, ses résultats et ses limites, est décrit sur preview.

Un lien d’aperçu est public

Le token du lien (128 bits) est la seule protection. Quiconque a le lien atteint l’app sans se connecter. Un aperçu de quelque chose qui contient de vraies données est une vraie exposition. Fermez un aperçu quand vous avez terminé.

Comment les aperçus sont tenus à l’écart du relais #

Les aperçus sont servis par un second écouteur HTTP sur son propre port, publié sur un nom d’hôte à lui. Il en découle deux choses :

  • Le relais n’est pas sur ce port. Son API, son app web, sa connexion en direct et ses liens de partage n’y sont pas montés. Le trafic des aperçus ne peut pas les atteindre, parce qu’ils n’existent pas sur cet écouteur. Créer ou fermer un aperçu demande le token d’accès du relais et ne se fait que sur le port propre du relais.
  • Le nom d’hôte diffère. Une app en aperçu tourne dans une autre origine de navigateur que le relais. Son JavaScript ne peut pas envoyer votre cookie de connexion au relais et ne peut pas lire le stockage de l’app web du relais.

Le nom d’hôte doit différer, pas seulement le port. Les cookies ignorent les ports, donc relay.example.com:3334 partagerait le cookie de connexion de relay.example.com. Le relais refuse de démarrer la preview lane quand les noms d’hôte sont identiques.

De plus, la preview lane ne transmet jamais à une app en aperçu le cookie de connexion du relais (copilot_auth) ni un en-tête Authorization qui contient le token d’accès du relais, même quand une requête fabriquée à la main les porte. L’en-tête Authorization propre à une app, lui, passe.

Les aperçus partagent en revanche un même nom d’hôte entre eux, chacun sous son propre chemin. Voir Ce qu’il faut garder en tête.

La mise en place #

Les aperçus sont désactivés tant que l’opérateur ne les a pas mis en place. Il y a trois étapes.

1. Un nom d’hôte #

Choisissez un nom d’hôte que vous contrôlez et dont le nom diffère de celui du relais, par exemple preview.example.com à côté de relay.example.com. Faites pointer son DNS vers votre proxy frontal ou votre tunnel. Les aperçus sont séparés par chemin, pas par sous-domaine, donc un seul nom d’hôte à un niveau, sous une zone que vous avez déjà, suffit. Chez Cloudflare, ce nom est couvert par le certificat Universal. Un joker à deux niveaux comme *.preview.example.com ne le serait pas, c’est pourquoi il n’y a pas de sous-domaine par aperçu.

2. Un proxy frontal #

Le proxy envoie le nom d’hôte des aperçus vers l’écouteur des aperçus, qui utilise par défaut le port du relais plus un (3334 quand le relais utilise 3333). Utilisez un port que vous fixez avec previews.port ; le proxy a besoin d’une cible stable.

Caddy

preview.example.com {
    reverse_proxy 127.0.0.1:3334
}

Cloudflare Tunnel

Ajoutez un second nom d’hôte public au tunnel que vous utilisez déjà, dans le tableau de bord Cloudflare sous Public Hostnames → Add (Noms d’hôte publics → Ajouter) ou dans le config.yml du tunnel :

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. Le bloc previews #

Ajoutez le bloc au config.json du relais et redémarrez le relais (voir oar restart) :

{
  "publicHostnames": ["relay.example.com"],
  "previews": {
    "enabled": true,
    "port": 3334,
    "bindHost": "127.0.0.1",
    "publicBaseUrl": "https://preview.example.com",
    "allowedTargetHosts": [],
    "maxLive": 8
  }
}
Clé Ce qu’il faut y mettre
publicHostnames Chaque nom d’hôte sur lequel le relais lui-même répond. La vérification de publicBaseUrl utilise cette liste (et toujours localhost et 127.0.0.1), donc un nom d’hôte que vous omettez n’est pas vérifié.
previews.enabled true pour activer la preview lane. Elle est désactivée par défaut.
previews.port Le port de l’écouteur. Par défaut : le port du relais plus un. 0 choisit un port libre à chaque démarrage, ce qu’un proxy frontal ne peut pas suivre.
previews.bindHost L’adresse sur laquelle l’écouteur se lie. Par défaut 127.0.0.1. Restez sur l’adresse de bouclage : le proxy frontal tourne sur la même machine.
previews.allowPublicBind Nécessaire uniquement quand bindHost n’est pas une adresse de bouclage. Chaque aperçu est alors joignable depuis le réseau sans passer par le proxy.
previews.publicBaseUrl L’adresse publique de la preview lane : https://preview.example.com. Les liens sont construits à partir d’elle.
previews.allowedTargetHosts Les hôtes, en plus de cette machine, vers lesquels un aperçu de port peut pointer, comme un conteneur ou une VM.
previews.maxLive Le nombre maximal d’aperçus à la fois, pour tout le relais. Par défaut 8.

Chaque clé, valeur par défaut et cas limite figure dans le tableau de config.json.

Surcharges par l’environnement #

COPILOT_PREVIEWS_ENABLED, COPILOT_PREVIEWS_PORT, COPILOT_PREVIEWS_BIND_HOST, COPILOT_PREVIEWS_PUBLIC_BASE_URL et COPILOT_PREVIEWS_ALLOWED_TARGET_HOSTS surchargent les clés correspondantes. Leurs valeurs et ce qui l’emporte sur quoi sont décrits sur Variables d’environnement.

Quand la preview lane refuse de démarrer #

Une preview lane mal configurée ne fait jamais tomber le relais. Le relais continue de tourner et la preview lane reste désactivée. Aucune de ces vérifications ne se réduit à un avertissement, car chacune protège la séparation décrite plus haut.

Raison Message
publicBaseUrl manque ou n’est pas une adresse http(s) previews.publicBaseUrl is required (an http(s) URL on a hostname of your own)
publicBaseUrl utilise un nom d’hôte du relais previews.publicBaseUrl must use a different hostname than the relay (got <hostname>)
Le port est inférieur à 1024 previews.port must be >= 1024 (got <port>)
Le port est celui du relais ou son port CLI previews.port <port> collides with a relay port
Le port ne peut pas être déterminé previews.port could not be resolved (set previews.port or config.port)
bindHost n’est pas une adresse de bouclage previews.bindHost <address> is not loopback; set previews.allowPublicBind to override

Vous le voyez à deux endroits :

  • Le journal du relais, au démarrage : une ligne PREVIEW LANE disabled: <message> par vérification échouée. Quand les vérifications passent, le journal indique [preview] listener on http://127.0.0.1:3334 → https://preview.example.com. Où se trouve le journal : Fichiers et dossiers.
  • La réponse à une demande de publication. L’API répond 503 avec Preview lane failed its startup checks et les messages dans details. L’outil preview de l’agent transmet cette réponse, et l’agent a pour consigne de vous en informer. Quand previews.enabled n’est pas défini, la réponse est Preview lane is disabled (set previews.enabled and previews.publicBaseUrl), et le journal ne contient aucune ligne.

Settings → Previews (Réglages → Aperçus) et l’avis de /preview n’indiquent pas la raison. L’onglet des réglages liste les aperçus qui existent, et l’avis dit seulement que la publication a échoué (voir /preview). Regardez dans le journal du relais.

Si le port est pris par un autre programme, les vérifications passent mais l’écouteur ne peut pas se lier. Le journal indique alors PREVIEW LANE failed to bind :<port> — <reason>. La preview lane est active du point de vue de l’API, mais rien ne répond sur le port des aperçus, donc les liens ne s’ouvrent pas. Libérez le port ou définissez un autre previews.port.

Trois façons de publier #

Demander à l’agent #

Dites ce que vous voulez : « démarre le serveur de dev et donne-moi un lien », « montre-moi le site construit ». L’agent utilise l’outil preview, ou l’API d’aperçu du relais quand un fournisseur n’a pas l’outil. Il vous donne le lien et vous indique qu’il est public. Les fournisseurs qui ont l’outil figurent dans le tableau de la page de référence.

/preview dans la zone de saisie #

La commande s’exécute dans votre navigateur, auprès du relais. Elle fonctionne avec tous les fournisseurs et ne coûte aucun turn d’agent. Elle s’exécute quand rien n’est joint au message. L’avis apparaît dans la conversation.

Vous tapez Ce qui se passe
/preview 5173 web app Publie le port 5173 avec le libellé « web app ». Affiche Preview published: <url>.
/preview ./dist built site Publie le dossier ./dist comme aperçu statique, avec le libellé « built site ». Le premier mot est le dossier, il ne peut donc pas contenir d’espace. Affiche Preview published: <url>.
/preview list Liste chaque aperçu du relais, un par ligne : les 8 premiers caractères du token, le libellé, le port ou le dossier, — offline s’il l’est, et le lien. Affiche No live previews. s’il n’y en a aucun, et The preview lane is disabled on this relay. quand la preview lane est désactivée.
/preview close Ferme l’aperçu de cette conversation. S’il n’y en a aucun, affiche No live previews in this conversation. S’il y en a plusieurs, affiche les correspondances et demande un préfixe plus long.
/preview close 3f9c Ferme l’aperçu dont le token commence par ces 4 à 32 caractères hexadécimaux, dans n’importe quelle conversation. Affiche Preview closed: <label>. Sans correspondance : No preview matches "3f9c".
/preview Affiche la ligne d’utilisation, comme pour tout ce que la commande ne sait pas lire.

Un premier mot fait uniquement de chiffres est toujours un port, et un port hors de la plage 1 à 65535 affiche la ligne d’utilisation. Pour un dossier nommé 8080, tapez ./8080.

Si la publication échoue, l’avis est le même quelle que soit la cause (un mauvais port, un dossier hors du dossier de travail, une preview lane pleine, une preview lane désactivée) :

Preview failed — check the port/path, or whether the preview lane is enabled (Settings → Live previews).

La raison exacte n’est pas affichée. La liste se trouve dans Settings → Previews, sous le titre Live previews (Aperçus en direct). Pour voir la raison, appelez l’API (ci-dessous) ou consultez le tableau de Dépannage. Si le relais est injoignable, l’avis est Preview command failed — could not reach the relay. Le menu des commandes et ses touches sont décrits sur La zone de saisie.

L’API #

Tout ce qui précède utilise la même API sur le port du relais. Elle demande le token d’accès du relais (authToken dans config.json, le token avec lequel vous vous connectez) comme bearer token. Enregistrez d’abord l’aperçu, pour connaître le chemin de base, puis démarrez le serveur de dev sous ce chemin :

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 vaut true quand la conversation avait déjà un aperçu actif du même port ou du même dossier et que c’est celui-là qui vous a été renvoyé.

Requête Rôle
POST /api/previews port ou dir (pas les deux), et en option conversationId, host, label.
GET /api/previews Tous les aperçus. ?conversationId=, ou l’en-tête de conversation qu’envoie un worker de session, restreint la liste à une conversation.
DELETE /api/previews/<token> En ferme un. Le serveur de dev n’est pas touché. Ajouté en 0.9.14 Une requête qui nomme une conversation (comme pour GET) ne peut fermer que les aperçus de cette conversation ; celui d’une autre est refusé (403).

Faire fonctionner une app sous le chemin de l’aperçu #

Chaque aperçu vit sous /test_<token>/ sur le nom d’hôte partagé des aperçus. La règle qui découle de ce que fait le proxy :

  • Le navigateur demande /test_<token>/….
  • L’app reçoit le chemin sans ce préfixe.
  • L’app doit donc mettre dans ses pages des liens et des URL de ressources qui portent le préfixe ou qui sont relatifs, et elle doit quand même répondre aux requêtes qui ne le portent pas.

Ce que fait le proxy au passage :

Le proxy
Retire le préfixe L’app reçoit le chemin qui suit /test_<token>, avec la query string. Une visite de /test_<token> seul est redirigée (301) vers la même adresse avec une barre oblique finale, pour que les URL relatives se résolvent à l’intérieur de l’aperçu.
Envoie X-Forwarded-Prefix Le préfixe sans barre oblique finale.
Envoie X-Forwarded-Host Le Host que l’écouteur des aperçus a reçu.
Envoie X-Forwarded-Proto Celui qu’a envoyé le proxy frontal ; https s’il n’en a envoyé aucun.
Envoie X-Forwarded-For L’adresse de celui qui s’est connecté à l’écouteur. Derrière un proxy frontal sur la même machine, c’est le proxy.
Définit Host Sur l’adresse propre de l’app (127.0.0.1:5173), pour les serveurs de dev qui la vérifient.
Réécrit Location Une redirection vers /x devient /test_<token>/x, de même qu’une redirection absolue vers l’adresse propre de l’app. Une redirection qui porte déjà le préfixe, une redirection //, une redirection relative ou une redirection vers un autre site reste telle quelle.
Limite la portée de Set-Cookie Le cookie reçoit Path=/test_<token>/ et perd Domain, pour que les cookies d’un aperçu n’atteignent pas un autre aperçu. Un cookie nommé copilot_auth est supprimé.
Marque les réponses X-Robots-Tag: noindex, nofollow, noarchive, et supprime Service-Worker-Allowed.

Il ne réécrit pas le corps des pages, des feuilles de style ni des scripts. Une app qui écrit /assets/app.js dans son HTML s’adresse à la racine du nom d’hôte des aperçus, où aucun aperçu ne vit, et le navigateur obtient la page No such preview (404). C’est le signe que l’app ne connaît pas son préfixe.

Ce qui fonctionne #

  • Les URL relatives. Une app dont les pages utilisent des adresses du type ./assets/app.js fonctionne telle quelle : la page est à /test_<token>/, donc ces adresses se résolvent à l’intérieur de l’aperçu. Cela découle du fonctionnement du proxy ; cela n’a pas été essayé framework par framework.
  • Lire le préfixe. Une app qui route sur des chemins sans préfixe et construit ses liens à partir de X-Forwarded-Prefix correspond exactement à la règle.
  • Un site construit. Construisez avec une base relative ou égale au préfixe, puis publiez le dossier de sortie comme aperçu statique. Les requêtes pour /test_<token>/assets/… sont servies depuis le dossier, donc les deux types d’URL fonctionnent.

Ce qui ne fonctionne pas tel quel #

Un réglage d’outil qui fait que l’app ne répond qu’aux requêtes qui portent le préfixe ne convient pas, parce que le proxy retire ce préfixe.

  • Le serveur de dev Vite avec base réglé sur le préfixe (--base). Vérifié dans le code source de Vite 8.3.1 sur la machine du relais : le serveur de dev ne traite que les chemins qui commencent par base. Il répond à une requête pour / par une redirection vers base, que le proxy laisse telle quelle, donc le navigateur tourne en boucle. Les autres requêtes sans préfixe reçoivent une 404 qui indique que le serveur a une URL de base publique. Le rechargement à chaud n’écoute qu’au chemin situé sous base, auquel la requête dépouillée du préfixe ne correspond pas. Utilisez l’approche du site construit, ou une configuration Vite qui sert depuis la racine avec des URL relatives.
  • Express avec le routeur monté sur le préfixe (app.use(prefix, router)). L’app reçoit /x, pas /test_<token>/x, donc le montage ne correspond pas. Montez le routeur sur / et construisez les liens à partir de X-Forwarded-Prefix.

Next.js (basePath, assetPrefix) et SvelteKit (kit.paths.base) sont cités dans les notes d’OAR destinées aux agents, mais ces notes n’ont pas été confrontées à ce que fait le proxy, et aucun des deux outils n’est disponible ici pour tester. Si leur réglage de base fait que l’app attend le préfixe sur chaque requête, cela ne convient pas, pour la même raison que Vite. Non vérifié : essayez avant de vous y fier.

Publiez d’abord quand l’app a besoin du préfixe

Un agent appelle create avant de démarrer le serveur de dev, pour que l’app puisse recevoir le préfixe si elle en a besoin. Pour une app qui fonctionne avec des URL relatives, l’ordre n’a pas d’importance.

Aperçus statiques #

Avec dir au lieu de port, le relais sert lui-même un dossier. Rien n’a besoin de tourner, ce qui convient à une sortie de build, une maquette, un fichier HTML unique ou un rapport de couverture.

  • Le dossier est pris à partir du dossier de travail de la conversation (chemin absolu, ou relatif à ce dossier) et doit se trouver à l’intérieur. Un aperçu publié sans conversation utilise le dossier de travail propre du relais. C’est vérifié à la publication et à chaque requête, donc un lien symbolique créé plus tard ne peut pas mener à l’extérieur.
  • index.html est servi pour un chemin de dossier. Rien n’est mis en cache.
  • Jamais servis : les fichiers et dossiers dont le nom commence par un point (.git, .env) et les fichiers de clés (*.pem, *.key, les noms qui commencent par id_rsa, id_ed25519, id_ecdsa ou id_dsa).
  • Les chemins de ressources relatifs fonctionnent. Ceux qui commencent par le préfixe de l’aperçu aussi. Les chemins absolus depuis la racine (/assets/app.js) ne fonctionnent pas.
  • Un dossier sans index.html, et tout fichier manquant, reçoit une page Not found (404).
  • Un aperçu statique est toujours online et refuse les connexions WebSocket.

Gérer les aperçus #

  • La carte dans le panneau des tâches de fond. Chaque aperçu de la conversation a une carte avec son libellé, le port, une note indiquant que le lien est public, un badge online ou offline une fois que le relais l’a vérifié, et les boutons Open (Ouvrir), Copy link (Copier le lien) et Close (Fermer). Le panneau est décrit sur Tâches de fond.
  • Une carte de lien dans le fil de la conversation. Quand un aperçu est publié pendant qu’un turn de la conversation tourne, la réponse porte une carte de lien, que ce soit l’agent qui l’ait publié ou vous avec /preview. Un aperçu publié entre deux turns n’a pas de carte dans le fil. Une fois l’aperçu disparu, la carte affiche un badge closed et plus de lien. Une conversation partagée affiche la carte avec son libellé seulement, et chaque lien d’aperçu cité dans son texte devient [preview link removed].
  • Settings → Previews. La liste Live previews montre chaque aperçu du relais, de toutes les conversations, avec les 8 premiers caractères de l’id de la conversation et Ajouté en 0.9.11 la date de publication et de dernière ouverture. Fermez-en un ici quand vous avez quitté sa conversation. Voir Settings (Réglages).

offline et closed ne sont pas la même chose. Le relais vérifie chaque aperçu de port toutes les 15 secondes, en essayant de se connecter au port (et une fois tout de suite quand vous publiez). Quand rien ne répond, la carte affiche un badge offline et la note « offline — dev server not responding ». Le lien reste valide : redémarrez le serveur sur le même port et le même lien fonctionne. closed signifie que l’aperçu a disparu et que le lien ne mène plus nulle part.

Fermer un aperçu ne met fin qu’au lien. Le serveur de dev continue de tourner comme la tâche de fond qu’il est.

Un aperçu n’expire pas de lui-même. Il prend fin quand vous le fermez, ou Ajouté en 0.9.11 quand vous supprimez sa conversation (y compris par une reconstruction du cache du relais). Archiver une conversation garde ses aperçus actifs, dans Settings → Previews.

Publier à nouveau le même port ou le même dossier dans la même conversation renvoie l’aperçu déjà actif, avec le même lien et le libellé inchangé, au lieu d’en créer un nouveau.

Au plus 8 aperçus actifs à la fois, pour tout le relais (previews.maxLive). Le refus nomme les trois plus anciens, avec la date de publication et de dernière ouverture de chacun, et la façon de les fermer.

Après un redémarrage du relais Ajouté en 0.9.11 #

Les aperçus survivent à un redémarrage du relais, à une mise à jour ou à un plantage : le relais les garde dans sa base de données et les restaure au démarrage, donc un lien que vous avez distribué continue de fonctionner. Chacun est d’abord vérifié à nouveau, et prend fin si la vérification constate qu’il n’est plus valide :

  • un aperçu de dossier dont le dossier a disparu, ou n’est plus dans le dossier de travail de sa conversation ;
  • un aperçu de port après un redémarrage de la machine : le serveur de dev a disparu, et le port peut appartenir plus tard à autre chose ;
  • un aperçu dont la conversation n’existe plus.

Linux et macOS signalent un redémarrage de façon fiable. Sous Windows, un Restart (Redémarrer) compte comme tel, mais avec fast startup (démarrage rapide) activé (la valeur par défaut de Windows), Shut down (Arrêter) suivi d’un rallumage ne compte pas : un aperçu de port d’avant revient et atteint ce qui écoute alors sur son port. Fermez les aperçus dont vous n’avez plus besoin.

Une vérification qui ne peut pas aboutir, parce que la base de données est occupée ou que le dossier ne peut pas être lu pour une autre raison que sa disparition, ne met pas fin à l’aperçu. Celui-ci n’est pas servi en attendant : Settings → Previews et l’action list de l’outil le montrent comme non servi (unverified), il compte dans la limite, et il peut être fermé. Le relais le vérifie à nouveau toutes les 15 secondes et le sert de nouveau sous son lien dès que la vérification aboutit ; publier à nouveau le même port ou le même dossier dans sa conversation le fait immédiatement. Après dix minutes sans réponse, l’aperçu prend fin, et le journal le signale.

Un aperçu de port est aussi sondé une fois juste après le démarrage, pour que son badge online ou offline soit à jour. Le journal du relais désigne un aperçu par un hash court, jamais par son token. Voir aussi Ce qu’un redémarrage conserve et ce qu’il perd.

WebSockets #

Un aperçu de port laisse passer les connexions WebSocket sous le même préfixe, préfixe retiré comme pour toute requête, donc les sockets propres d’une app fonctionnent quand le navigateur se connecte par le lien de l’aperçu. (Le rechargement à chaud est l’affaire de l’app : voir la note sur Vite plus haut.) Un aperçu statique, ainsi qu’un lien inconnu ou fermé, répondent à une requête WebSocket par 404. Si l’app répond à l’upgrade par une réponse ordinaire, le navigateur reçoit ce statut. Si l’app est arrêtée, 502.

Ce qu’il faut garder en tête #

  • Les aperçus partagent une origine entre eux. Les cookies sont limités au chemin de chaque aperçu et les tokens ne peuvent pas être devinés, mais deux aperçus ne sont pas aussi séparés l’un de l’autre que chacun l’est du relais.
  • Uniquement votre propre machine, par défaut. Un aperçu de port ne peut pointer que vers des adresses de bouclage, plus les hôtes de previews.allowedTargetHosts. Les ports propres du relais sont toujours refusés, car en publier un mettrait la page de connexion du relais sur le nom d’hôte public.
  • Le relais ne fait pas tourner l’app. Il ne démarre ni ne redémarre jamais un serveur de dev. Si le serveur s’arrête, la carte passe à offline.

Par fournisseur #

Les aperçus sont identiques avec tous les fournisseurs ; seule la façon dont un agent publie diffère, comme le montre la page de référence. /preview et Settings → Previews fonctionnent avec tous.

Dépannage #

Ce que vous voyez Cause et solution
L’agent ou l’API répond Preview lane is disabled ou Preview lane failed its startup checks (503) La preview lane est désactivée ou a refusé de démarrer. Lisez les lignes PREVIEW LANE disabled dans le journal du relais, corrigez previews, redémarrez le relais. Voir Quand la preview lane refuse de démarrer.
Le lien affiche une page Preview upstream unreachable (502) Rien n’écoute sur le port, ou la connexion a échoué. Redémarrez le serveur de dev sur le même port et rechargez la page ; le lien reste valide.
Des ressources manquent ou la page est vide, et les requêtes pour /assets/... reçoivent No such preview (404) L’app écrit des chemins absolus depuis la racine. Faites-lui produire des URL relatives ou préfixées (règle).
La page se charge en boucle, ou l’app répond 404 en mentionnant une « public base URL » L’app attend le préfixe sur chaque requête, mais le proxy le retire (ce qui ne fonctionne pas).
No such preview sur tout le lien Le lien est inconnu ou fermé, sa conversation a été supprimée, ou un redémarrage a constaté qu’il n’était plus valide (après un redémarrage). Publiez à nouveau.
Un aperçu apparaît comme non servi (unverified) Le relais n’a pas pu le vérifier à son démarrage. Il est vérifié à nouveau toutes les 15 secondes ; publier à nouveau la même cible dans sa conversation le rétablit immédiatement.
Le lien ne s’ouvre pas du tout Le proxy frontal ou le DNS n’est pas configuré, ou l’écouteur n’a pas pu se lier à son port (PREVIEW LANE failed to bind dans le journal).
La carte indique offline Le serveur de dev a cessé de répondre. Redémarrez-le sur le même port.
Too many live previews (max 8); previews stay until closed. Oldest: … (429) Fermez-en un dans Settings → Previews, avec /preview close ou avec DELETE /api/previews/<token> ; un agent peut fermer ceux de sa propre conversation avec l’action close de l’outil ; ou augmentez previews.maxLive.
Port <n> belongs to the relay and cannot be previewed (400) Vous avez donné le port du relais, son port CLI ou le port des aperçus. Publiez le port du serveur de dev.
Target port must be between 1024 and 65535 (400) Utilisez un port plus élevé.
Target host ... is not loopback and not in previews.allowedTargetHosts (400) Ajoutez l’hôte à previews.allowedTargetHosts.
dir must live inside the workspace root (...), Directory not found: ... ou Not a directory: ... (400) Le dossier doit exister et se trouver dans le dossier de travail de la conversation.