プレビュー
プレビューは、Web UI を持つものを公開リンクに載せます。リレーのマシンのポートで待ち受ける開発サーバーか、通常のファイルを入れたフォルダーです。リンクはどのデバイスからでも開け、誰にでも送れます。リレーは公開されません。プレビューは、別のホスト名にある別のリスナーから配信されます。
よくある使い方:
- スマートフォンでアプリを開く:エージェントが開発サーバーを起動し、あなたはリンクをタップして実機でレイアウトを試します。
- 誰かにリンクを送る:同僚は、あなたのマシンやリレーにアクセスしなくても、モックアップやビルドしたサイトを開けます。
- Webhook を開発サーバーに向ける:公開アドレスを必要とするサービスから、作成中のアプリを呼び出せます。
このページは、オーナーと運用者向けのガイドです。エージェントのツールについては、パラメーター、結果、制限を含めて preview で説明しています。
プレビューのリンクは公開されます
リンク内のトークン(128 ビット)が唯一の保護です。リンクを持っている人は誰でも、サインインせずにアプリにアクセスできます。実データを扱うもののプレビューは、実際にデータを公開することになります。使い終わったらプレビューを閉じてください。
プレビューをリレーから切り離す仕組み #
プレビューは、専用のポートで動く 2 つ目の HTTP リスナーから配信され、専用のホスト名で公開されます。ここから 2 つのことが言えます。
- リレーはそのポートにありません:リレーの API、Web アプリ、ライブ接続、共有リンクは、そこにはマウントされていません。そのリスナー上に存在しないので、プレビューのトラフィックはそれらに届きません。プレビューの作成や終了にはリレーのアクセストークンが必要で、リレー自身のポートでのみ行われます。
- ホスト名が異なります:プレビューしているアプリは、リレーとは別のブラウザーのオリジンで動きます。その JavaScript は、リレーのサインイン用 Cookie を送れず、リレーの Web アプリのストレージも読めません。
ポートだけでなく、ホスト名が異なる必要があります。Cookie はポートを区別しないので、relay. は relay. のサインイン用 Cookie を共有してしまいます。ホスト名が同じ場合、リレーはレーンの起動を拒否します。
さらに、レーンは、リレーのサインイン用 Cookie(copilot_)や、リレーのアクセストークンを含む Authorization ヘッダーを、プレビューしているアプリに決して転送しません。手動で作成したリクエストにそれらが含まれていても同じです。アプリ自身の Authorization ヘッダーはそのまま通ります。
なお、プレビュー同士は 1 つのホスト名を共有し、それぞれが専用のパスの下にあります。注意点を参照してください。
設定する #
プレビューは、運用者が設定するまでオフです。手順は 3 つです。
1. ホスト名 #
自分が管理していて、名前がリレーのものと異なるホスト名を選びます。例:relay. に対する preview.。その DNS をフロントプロキシかトンネルに向けます。プレビューはサブドメインではなくパスで区別されるので、すでに持っているゾーンの下に 1 階層のホスト名が 1 つあれば十分です。Cloudflare では、その名前は Universal 証明書の対象になります。*. のような 2 階層のワイルドカードは対象にならないため、プレビューごとのサブドメインはありません。
2. フロントプロキシ #
プロキシは、プレビューのホスト名をプレビューのリスナーに送ります。リスナーのポートの既定値は、リレーのポートに 1 を足したものです(リレーが 3333 なら 3334)。previews. で固定したポートを使ってください。プロキシには安定した転送先が必要です。
Caddy
preview.example.com {
reverse_proxy 127.0.0.1:3334
}
Cloudflare Tunnel
すでに使っているトンネルに、2 つ目の公開ホスト名を追加します。追加する場所は、Cloudflare のダッシュボードの Public Hostnames → Add(パブリックホスト名 → 追加)、またはトンネルの config. です。
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. previews ブロック #
リレーの config. にこのブロックを追加し、リレーを再起動します(oar restart を参照)。
{
"publicHostnames": ["relay.example.com"],
"previews": {
"enabled": true,
"port": 3334,
"bindHost": "127.0.0.1",
"publicBaseUrl": "https://preview.example.com",
"allowedTargetHosts": [],
"maxLive": 8
}
}
| キー | 設定する値 |
|---|---|
public |
リレー自身が応答するすべてのホスト名です。public の確認はこのリスト(と、常に localhost と 127.)を使うので、リストに入れなかったホスト名は確認されません。 |
previews. |
レーンを有効にするには true にします。既定ではオフです。 |
previews. |
リスナーのポートです。既定値はリレーのポートに 1 を足したものです。0 は起動のたびに空いているポートを選ぶため、フロントプロキシが追従できません。 |
previews. |
リスナーがバインドする場所です。既定値は 127. です。ループバックのままにしてください。フロントプロキシは同じマシンで動きます。 |
previews. |
bind がループバックでないときだけ必要です。その場合、すべてのプレビューに、プロキシを経由せずネットワークから到達できるようになります。 |
previews. |
レーンの公開アドレスです(https://)。リンクはこのアドレスから作られます。 |
previews. |
このマシン以外で、ポートのプレビューの転送先にできるホストです。コンテナや VM などです。 |
previews. |
同時に存在できるプレビューの最大数で、リレー全体での数です。既定値は 8 です。 |
すべてのキー、既定値、例外的なケースは、config.json の表にあります。
環境変数による上書き #
COPILOT_、COPILOT_、COPILOT_、COPILOT_、COPILOT_ は、対応するキーを上書きします。値と、どれが何より優先されるかは、環境変数にあります。
レーンが起動を拒否するとき #
設定を誤ったレーンが、リレーを止めることはありません。リレーは動き続け、レーンはオフのままです。どの確認も、上で説明した分離を守るためのものなので、警告に格下げされることはありません。
| 理由 | メッセージ |
|---|---|
public がない、または http(s) のアドレスではない |
previews. |
public がリレーのホスト名を使っている |
previews. |
| ポートが 1024 未満 | previews. |
| ポートがリレーのポートか、その CLI ポート | previews. |
| ポートを決定できない | previews. |
bind がループバックではない |
previews. |
理由は 2 か所で確認できます。
- リレーのログ(起動時):確認に失敗するごとに、
PREVIEW LANE disabled: <message>という行が 1 行出ます。確認に通ると、ログには[preview] listener on http://と出ます。ログの場所はファイルとフォルダーにあります。127. 0. 0. 1: 3334 → https:// preview. example. com - 公開リクエストへの応答:API は 503 で
Preview lane failed its startup checksを返し、メッセージはdetailsに入ります。エージェントのpreviewツールはこれを伝え、エージェントはそれをあなたに伝えるよう指示されています。previews.が設定されていないときの応答はenabled Preview lane is disabled (set previews.で、ログには行が出ません。enabled and previews. public Base Url)
Settings → Previews(設定 → プレビュー)と / の通知には、理由は表示されません。設定タブには存在するプレビューが一覧表示され、通知は公開に失敗したことだけを伝えます(/ を参照)。リレーのログを確認してください。
ポートがほかのプログラムに使われている場合、確認は通りますが、リスナーはバインドできません。そのとき、ログには PREVIEW LANE failed to bind :<port> — <reason> と出ます。API から見るとレーンは動いていますが、プレビューのポートでは何も応答しないため、リンクは開きません。ポートを空けるか、別の previews. を設定してください。
公開する 3 つの方法 #
エージェントに頼む #
望むことを伝えます。たとえば「開発サーバーを起動してリンクをください」「ビルドしたサイトを見せて」です。エージェントは preview ツールを使います。ツールのないプロバイダーでは、リレーのプレビュー API を使います。エージェントは、リンクと、そのリンクが公開されていることを伝えます。どのプロバイダーにツールがあるかは、リファレンスページの表にあります。
入力欄の /preview #
このコマンドは、ブラウザーからリレーに対して実行されます。どのプロバイダーでも使え、エージェントのターンを消費しません。メッセージに何も添付されていないときに実行されます。通知は会話の中に表示されます。
| 入力 | 動作 |
|---|---|
/ |
ポート 5173 をラベル「web app」で公開します。Preview published: <url> と表示します。 |
/ |
フォルダー ./ を静的プレビューとして、ラベル「built site」で公開します。最初の語がフォルダーなので、フォルダー名にスペースは使えません。Preview published: <url> と表示します。 |
/ |
リレーのすべてのプレビューを 1 行に 1 つずつ一覧表示します。内容は、トークンの最初の 8 文字、ラベル、ポートまたはフォルダー、オフラインの場合は — offline、そしてリンクです。プレビューがなければ No live previews.、レーンがオフなら The preview lane is disabled on this relay. と表示します。 |
/ |
この会話のプレビューを閉じます。プレビューがなければ No live previews in this conversation. と表示します。複数あれば、該当するものを表示し、より長いプレフィックスを求めます。 |
/ |
トークンがその 4〜32 文字の 16 進数で始まるプレビューを、どの会話のものでも閉じます。Preview closed: <label> と表示します。一致するものがなければ No preview matches "3f9c". と表示します。 |
/ |
使い方の行を表示します。読み取れない入力でも同じです。 |
数字だけでできた最初の語は常にポートとして扱われ、1〜65535 の範囲外のポートでは使い方の行が表示されます。8080 という名前のフォルダーには、./ と入力します。
公開に失敗したとき、通知は原因(誤ったポート、作業フォルダーの外にあるフォルダー、満杯のレーン、無効なレーン)にかかわらず同じです。
Preview failed — check the port/path, or whether the preview lane is enabled (Settings → Live previews).
正確な理由は表示されません。一覧は Settings → Previews の見出し Live previews(ライブプレビュー)の下にあります。理由を確認するには、API(下記)を呼び出すか、トラブルシューティングの表を見てください。リレーに到達できない場合、通知は Preview command failed — could not reach the relay. です。コマンドメニューとそのキーは、入力欄にあります。
API #
上記はすべて、リレーのポートにある同じ API を使います。この API には、リレーのアクセストークン(config. の auth。サインインに使うのと同じトークン)をベアラートークンとして渡す必要があります。先に登録してベースパスを確認し、その下で開発サーバーを起動します。
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 は、その会話に同じポートまたはフォルダーのライブのプレビューがすでにあり、それが返されたときに true になります。
| リクエスト | 用途 |
|---|---|
POST / |
port または dir(両方は不可)。任意で conversation、host、label。 |
GET / |
すべてのプレビューです。?conversation、またはセッションワーカーが送る会話ヘッダーで、1 つの会話に絞り込みます。 |
DELETE / |
1 つを閉じます。開発サーバーには影響しません。0.9.14 で追加 会話を指定したリクエスト(GET と同様)は、その会話のプレビューしか閉じられず、別の会話のプレビューは拒否されます(403)。 |
プレビューのパスの下でアプリを動かす #
すべてのプレビューは、共有のプレビュー用ホスト名の / の下にあります。プロキシの動作から、次のルールが導かれます。
- ブラウザーは
/を要求します。test_<token>/ … - アプリは、そのプレフィックスなしのパスを受け取ります。
- そのためアプリは、ページ内のリンクとアセットの URL を、プレフィックス付きか相対のどちらかにする必要があり、かつプレフィックスのないリクエストにも応答しなければなりません。
プロキシが途中で行うこと:
| プロキシの処理 | |
|---|---|
| プレフィックスを取り除く | アプリは、/ より後のパスをクエリ付きで受け取ります。末尾のスラッシュがない / へのアクセスは、末尾にスラッシュを付けた同じアドレスへリダイレクト(301)されるので、相対 URL はプレビューの中で解決されます。 |
X- を送る |
末尾のスラッシュを除いたプレフィックスです。 |
X- を送る |
プレビューのリスナーが受け取った Host です。 |
X- を送る |
フロントプロキシが送った値です。送られていなければ https です。 |
X- を送る |
リスナーに接続した相手のアドレスです。同じマシン上のフロントプロキシの背後では、それはプロキシです。 |
Host を設定する |
アプリ自身のアドレス(127.)にします。これを確認する開発サーバーのためです。 |
Location を書き換える |
/ へのリダイレクトは / になります。アプリ自身のアドレスへの絶対リダイレクトも同様です。すでにプレフィックス付きのリダイレクト、// で始まるもの、相対のもの、別のサイトへのものはそのままです。 |
Set- の範囲を限定する |
Cookie には Path=/ が付き、Domain が外されるので、あるプレビューの Cookie が別のプレビューに届くことはありません。copilot_ という名前の Cookie は破棄されます。 |
| 応答に印を付ける | X- を付け、Service- を取り除きます。 |
ページ、スタイルシート、スクリプトの本文は書き換えません。HTML に / と書くアプリは、プレビュー用ホスト名のルートに要求を送りますが、そこにはプレビューがないため、ブラウザーには No such preview のページ(404)が返ります。これは、アプリが自分のプレフィックスを知らないことを示しています。
動作するもの #
- 相対 URL:ページで
./形式のアドレスを使うアプリは、そのまま動作します。ページはassets/ app. js /にあるので、アドレスはプレビューの中で解決されます。これはプロキシの仕組みから導かれることで、フレームワークごとに試したわけではありません。test_<token>/ - プレフィックスを読み取る:プレフィックスなしのパスでルーティングし、
X-からリンクを組み立てるアプリは、ルールにぴったり合います。Forwarded- Prefix - ビルドしたサイト:相対、またはプレフィックスと同じベースでビルドし、出力フォルダーを静的プレビューとして公開します。
/へのリクエストはフォルダーから配信されるので、どちらの種類の URL も動作します。test_<token>/ assets/ …
そのままでは動作しないもの #
プレフィックス付きのリクエストだけにアプリを応答させるツールの設定は合いません。プロキシがプレフィックスを取り除くためです。
baseをプレフィックスにした(--base)Vite 開発サーバー:リレーのマシン上の Vite 8.3.1 のソースで確認しました。開発サーバーは、baseで始まるパスしか扱いません。/へのリクエストにはbaseへのリダイレクトで応答し、プロキシはそれをそのままにするので、ブラウザーはループします。その他のプレフィックスなしのリクエストには、サーバーに公開ベース URL があることを示す 404 が返ります。ホットリロードはbaseの下のパスでしか待ち受けず、プレフィックスを取り除いたリクエストはそれに一致しません。ビルドしたサイトの方法を使うか、ルートから相対 URL で配信する Vite の構成を使ってください。- ルーターをプレフィックスにマウントした Express(
app.):アプリが受け取るのはuse(prefix, router) /ではなくtest_<token>/ x /なので、マウントが一致しません。ルーターをx /にマウントし、X-からリンクを組み立ててください。Forwarded- Prefix
Next.js(base、asset)と SvelteKit(kit.)は、OAR のエージェント向けのメモで言及されています。ただし、そのメモはプロキシの動作と照らし合わせて確認されておらず、どちらのツールもここではテストに使えません。ベースの設定によってアプリがすべてのリクエストにプレフィックスを期待するようになるなら、Vite と同じ理由で合いません。未検証です。頼る前に試してください。
アプリがプレフィックスを必要とするなら先に公開する
エージェントは開発サーバーを起動する前に create を呼び出すので、アプリが必要とする場合はプレフィックスを渡せます。相対 URL で動作するアプリでは、順序は関係ありません。
静的プレビュー #
port の代わりに dir を使うと、リレー自身がフォルダーを配信します。何も動かす必要がないので、ビルドの出力、モックアップ、単一の HTML ファイル、カバレッジレポートに向いています。
- フォルダーは会話の作業フォルダーを基準に決まり(絶対パス、またはそこからの相対パス)、その中になければなりません。会話なしで公開したプレビューは、リレー自身の作業フォルダーを使います。これは公開時とリクエストのたびに確認されるので、後から作ったシンボリックリンクで外に出ることはできません。
- フォルダーのパスには
index.が返されます。キャッシュはされません。html - 決して配信されないもの:名前がドットで始まるファイルとフォルダー(
.、git .)と、鍵ファイル(env *.、pem *.、key id_、rsa id_、ed25519 id_、ecdsa id_で始まる名前)。dsa - 相対のアセットパスは動作します。プレビューのプレフィックスで始まるものも動作します。ルートからの絶対パス(
/)は動作しません。assets/ app. js index.のないフォルダーと、存在しないファイルには、html Not foundのページ(404)が返ります。- 静的プレビューは常に
onlineで、WebSocket 接続を拒否します。
プレビューの管理 #
- バックグラウンドタスクパネルのカード:会話の各プレビューにはカードがあります。カードには、ラベル、ポート、リンクが公開されていることを示す注意書き、リレーが確認した後の
onlineまたはofflineのバッジ、ボタン Open(開く)、Copy link(リンクをコピー)、Close(閉じる)が表示されます。パネルについては、バックグラウンドタスクで説明しています。 - トランスクリプトのリンクカード:会話のターンの実行中にプレビューが公開されると、エージェントが公開した場合も、あなたが
/で公開した場合も、返信にリンクカードが付きます。ターンとターンの間に公開したプレビューには、トランスクリプトのカードはありません。プレビューがなくなると、カードにはpreview closedのバッジが表示され、リンクは表示されなくなります。共有された会話では、カードはラベルだけが表示され、本文中に引用されたプレビューのリンクはすべて[preview link removed]と表示されます。 - Settings → Previews:一覧 Live previews には、すべての会話から、リレーのすべてのプレビューが表示されます。会話 ID の最初の 8 文字と、0.9.11 で追加 公開した日時と最後に開いた日時も表示されます。その会話を離れた後は、ここで閉じてください。Settings(設定)を参照してください。
offline と closed は別物です。リレーは、ポートのプレビューすべてを 15 秒ごとに、ポートへの接続を試みて確認します(公開したときにも、すぐに 1 回確認します)。何も応答しないと、カードには offline のバッジと「offline — dev server not responding」という注意書きが表示されます。リンクは有効なままです。同じポートでサーバーを再び起動すれば、同じリンクが動作します。closed は、プレビューがなくなり、リンクがもう解決されないことを意味します。
プレビューを閉じても、終わるのはリンクだけです。開発サーバーは、バックグラウンドタスクとしてそのまま動き続けます。
プレビューは、自動では期限切れになりません。閉じたとき、または 0.9.11 で追加 その会話を削除したとき(リレーのキャッシュの再構築による削除も含む)に終わります。会話をアーカイブしても、そのプレビューはライブのまま Settings → Previews に残ります。
同じ会話で同じポートやフォルダーを再び公開すると、新しいプレビューではなく、すでにライブのプレビューが同じリンクで、ラベルも変えずに返されます。
同時にライブにできるプレビューは、リレー全体で最大 8 件です(previews.)。拒否のメッセージには、最も古い 3 件が、それぞれ公開した日時と最後に開いた日時、閉じ方とともに示されます。
リレーの再起動後 0.9.11 で追加 #
プレビューは、リレーの再起動、更新、クラッシュの後も残ります。リレーはプレビューをデータベースに保持し、起動時に復元するので、渡したリンクは動作し続けます。ただし、それぞれ先に再確認され、もう有効でないと判断されたものは終わります。
- フォルダーのプレビューで、フォルダーがなくなったか、会話の作業フォルダーの中にもうないもの
- マシンの再起動後のポートのプレビュー:開発サーバーはなくなっており、そのポートは後で別のものが使っている可能性があります
- 会話がもう存在しないプレビュー
Linux と macOS は、再起動を確実に報告します。Windows では、Restart(再起動)は再起動として数えられます。しかし fast startup(高速スタートアップ)がオン(Windows の既定)の状態で Shut down(シャットダウン)してから電源を入れた場合は、数えられません。その場合、以前のポートのプレビューが戻り、その時点でそのポートで待ち受けているものに到達します。不要になったプレビューは閉じてください。
確認が答えを出せない場合(データベースがビジー、またはフォルダーがなくなった以外の理由で読めない)、プレビューは終わりません。その間は配信されません。Settings → Previews とツールの list では配信されていない状態(unverified)として表示され、上限の数に含まれ、閉じることもできます。リレーは 15 秒ごとに再確認し、確認が答えを出したらすぐに、同じリンクで再び配信します。その会話で同じポートやフォルダーを再び公開すると、すぐにそうなります。10 分間答えが出ないと、プレビューは終わり、ログにその旨が記録されます。
ポートのプレビューは起動直後にも 1 回確認されるので、online や offline のバッジは最新の状態です。リレーのログでは、プレビューはトークンではなく、短いハッシュで示されます。再起動で保持されるもの、失われるものも参照してください。
WebSocket #
ポートのプレビューは、WebSocket 接続を同じプレフィックスの下で通します。プレフィックスは通常のリクエストと同様に取り除かれるので、ブラウザーがプレビューのリンクを通じて接続すれば、アプリ自身のソケットは動作します。(ホットリロードはアプリ側の問題です。上の Vite の項を参照してください。)静的プレビューと、不明または閉じたリンクは、WebSocket のリクエストに 404 で応答します。アプリがアップグレードに通常の応答を返すと、ブラウザーはそのステータスを受け取ります。アプリが停止していれば 502 です。
注意点 #
- プレビュー同士はオリジンを共有します:Cookie は各プレビューのパスに限定され、トークンは推測できません。しかし 2 つのプレビュー同士は、それぞれとリレーほどには分離されていません。
- 既定では自分のマシンだけ:ポートのプレビューの転送先にできるのは、ループバックアドレスと
previews.のホストだけです。リレー自身のポートは常に拒否されます。それを公開すると、リレーのサインインページが公開ホスト名に載ってしまうからです。allowed Target Hosts - リレーはアプリを動かしません:開発サーバーを起動したり再起動したりすることはありません。サーバーが止まると、カードは
offlineになります。
プロバイダー別 #
プレビューは、どのプロバイダーでも同じように見えます。違うのはエージェントが公開する方法だけで、リファレンスページに示されています。/ と Settings → Previews は、すべてのプロバイダーで使えます。
トラブルシューティング #
| 表示される内容 | 原因と対処 |
|---|---|
エージェントや API が Preview lane is disabled または Preview lane failed its startup checks(503)と応答する |
レーンがオフか、起動を拒否しました。リレーのログの PREVIEW LANE disabled の行を読み、previews を修正して、リレーを再起動してください。レーンが起動を拒否するときを参照してください。 |
リンクに Preview upstream unreachable のページ(502)が表示される |
ポートで何も待ち受けていないか、接続に失敗しました。同じポートで開発サーバーを再び起動し、再読み込みしてください。リンクは有効なままです。 |
アセットがない、またはページが空白で、/ へのリクエストに No such preview(404)が返る |
アプリがルートからの絶対パスを書き出しています。相対またはプレフィックス付きの URL を出力するようにしてください(ルール)。 |
| ページの読み込みがループする、またはアプリが「public base URL」についての注記付きで 404 を返す | アプリがすべてのリクエストにプレフィックスを期待していますが、プロキシがそれを取り除いています(そのままでは動作しないもの)。 |
リンク全体で No such preview が表示される |
リンクが不明か閉じられた、会話が削除された、または再起動時にもう有効でないと判断されました(リレーの再起動後)。もう一度公開してください。 |
プレビューが配信されていない状態(unverified)と表示される |
リレーが起動時に確認できませんでした。15 秒ごとに再確認されます。その会話で同じ対象を再び公開すると、すぐに戻ります。 |
| リンクがまったく開かない | フロントプロキシか DNS が設定されていないか、リスナーがポートにバインドできませんでした(ログに PREVIEW LANE failed to bind)。 |
カードに offline と表示される |
開発サーバーが応答しなくなりました。同じポートで再び起動してください。 |
Too many live previews (max 8); previews stay until closed. Oldest: …(429) |
Settings → Previews、/、または DELETE / で 1 つ閉じてください。エージェントは、ツールの close アクションで自分の会話のプレビューを閉じられます。または、previews. を引き上げてください。 |
Port <n> belongs to the relay and cannot be previewed(400) |
リレーのポート、その CLI ポート、またはプレビューのポートを指定しています。開発サーバーのポートを公開してください。 |
Target port must be between 1024 and 65535(400) |
もっと大きい番号のポートを使ってください。 |
Target host ... is not loopback and not in previews.(400) |
そのホストを previews. に追加してください。 |
dir must live inside the workspace root (...、Directory not found: ...、Not a directory: ...(400) |
フォルダーが存在し、会話の作業フォルダーの中にある必要があります。 |