preview
借助 preview,智能体可以把带网页界面的东西放到公开链接上,这样你就能在手机上打开它,或把它发给别人:可以是监听中继机器某个端口的开发服务器,也可以是一个存放普通文件的文件夹。它还可以列出和关闭自己所在对话的预览。
用日常语言提出请求:“启动开发服务器并给我一个链接”、“给我看看构建好的站点”。智能体会被告知,每当你想查看、试用或分享网页界面时就使用该工具,并且绝不让你去访问 localhost。
预览链接是公开的
任何拥有该链接的人都无需登录即可打开应用。链接中的令牌是唯一的保护。智能体被告知在给你链接时要说明这一点。用完后关闭预览。
你会看到什么 #
- 对话的后台任务面板中有一张预览卡片,带有 Open(打开)、Copy link(复制链接)和 Close(关闭)。
- 对话记录中的一张链接卡片,位于智能体发布它的那条回复上。预览消失后它会显示一个
closed徽标。对话的分享视图只显示卡片标签,绝不显示链接。 - 中继上所有对话的全部预览都列在 Settings → Previews(设置 → 预览)下,每个都带有一个关闭按钮。
- 当端口预览背后的开发服务器停止响应时,卡片上会出现一个
offline徽标。链接仍然保留登记:在同一端口再次启动服务器,同一链接就会再次生效。
你无需智能体也能做同样的事,在输入区输入 / 即可:
/preview 5173 web app publish a port
/preview ./dist built site publish a folder
/preview list list the previews
/preview close close one
设置 #
在中继的运维者设置之前,预览是关闭的。预览需要一个独立于中继的主机名,这样被预览的应用就永远无法访问中继本身。需要三样东西:
- 一个由你控制的主机名,它与中继的主机名不同,而不只是端口不同。
- 一个把该主机名指向预览端口的前置代理(Caddy、Cloudflare Tunnel 等)。
- 中继的
config.中的一个json previews块:
{
"publicHostnames": ["relay.example.com"],
"previews": {
"enabled": true,
"port": 3334,
"bindHost": "127.0.0.1",
"publicBaseUrl": "https://preview.example.com",
"allowedTargetHosts": [],
"maxLive": 8
}
}
无论是否完成这些设置,智能体都拥有该工具。没有完成时,create 调用会返回原因,智能体会被告知把原因转告你:这需要运维者来解决。
按提供商 #
| 提供商 | |
|---|---|
| Claude、Claude API 0.9.18 新增、DeepSeek 0.9.17 新增、Cursor | 拥有 preview 工具 |
| Grok;扩展引擎上的 GitHub Copilot 和 OpenAI | 拥有 preview 工具,通过 OAR 的 MCP 服务器 0.9.4 新增 |
| SDK 引擎上的 GitHub Copilot 和 OpenAI | 无工具。预览设置好后,智能体会被告知如何调用中继的预览 API,规则相同,调用中已写好它所在的对话。你看到的效果相同。 |
| Claude Cloud | 无:云端会话在 Anthropic 的沙箱中运行,你的机器上没有可发布的端口或文件夹 |
操作 #
| 操作 | 它做什么 | 参数 |
|---|---|---|
create |
发布一个端口或一个文件夹 | port 或 dir,可选 label |
list |
列出此对话的预览 | |
close |
关闭此对话的一个预览 | token,当对话恰好只有一个预览时可省略 |
结果是 JSON,带有 "ok": true,或者带有 "ok": false 和一条 error 文本。该工具不会以其他方式失败;错误的参数会以智能体可读的 error 返回。
create #
发布一个端口或一个文件夹,绝不在一次调用中同时发布两者。0.9.11 新增 在同一对话中再次发布同一端口或文件夹,会返回已在运行的那个预览,链接相同,标签不变;此时结果中带有 "existing": true。
| 参数 | 类型 | |
|---|---|---|
port |
1024–65535 | 开发服务器的本地端口 |
dir |
路径 | 作为纯文件提供的文件夹:绝对路径,或相对于对话工作目录的路径 |
label |
文本,最多 120 个字符 | 预览卡片上的名称 |
开发服务器(port) #
中继把请求转发给监听该端口的服务器。中继不会启动、监视或重启该服务器;它仍是智能体的一个普通后台任务,有自己的停止按钮。
每个预览都位于预览主机名上自己的路径下。浏览器请求 /;中继在转发请求之前去掉该前缀,并通过 X- 把前缀告知应用。它不会改写页面。因此,应用要正常工作,它发出的链接和资源地址必须是相对地址或带有该前缀,而它本身响应的是不带前缀的请求。从主机根路径请求 / 的应用会找不到该文件。
这就是为什么智能体会被告知要先调用 create,并在启动开发服务器之前从结果中读取 base。
告诉智能体的做法并不适用于所有开发服务器
该工具自身的描述建议在启动服务器时设置基础路径(Vite --base、Next.js base、Express 挂载前缀)。但如果服务器随后期望收到的请求带有该前缀,就与中继转发的请求不符:Vite 的开发服务器和挂载在该前缀上的 Express 路由器都属于这种情况。哪些做法可行,见预览。
{ "action": "create", "port": 5173, "label": "web app" }
{
"ok": true,
"existing": false,
"url": "https://preview.example.com/test_0123456789abcdef0123456789abcdef/",
"basePath": "/test_0123456789abcdef0123456789abcdef/",
"token": "0123456789abcdef0123456789abcdef",
"mode": "port",
"hint": "Serve the app under /test_0123456789abcdef0123456789abcdef/ (the proxy strips the prefix and sends X-Forwarded-Prefix). Bind the dev server to 127.0.0.1, not 0.0.0.0: the relay's proxy reaches it there and nothing else should. Tell the user the URL and that the link is public."
}
0.9.17 新增 hint 还告诉智能体把开发服务器绑定到 127.,而不是 0.:中继的代理在那里访问它,其他任何东西都不应访问它。只有拥有 preview 工具的会话会得到这一行(参见按提供商)。被教会使用预览 API 而非该工具的智能体则不会得到:SDK 引擎上的 GitHub Copilot 和 OpenAI,以及当中继以 OAR_ 运行时的 Grok 和 Copilot 扩展引擎。
文件夹(dir) #
中继自己提供该文件夹中的文件。不需要运行任何程序:适合构建产物、设计稿、单个 HTML 文件或覆盖率报告。
- 文件夹必须位于对话的工作目录内。每个请求都会重新检查这一点,因此链接以后也无法被引向目录之外。
- 名称以点开头的文件和文件夹(
.、git .)永远不会提供,密钥文件(env *.、pem *.、key id_)也不会。rsa… - 请求文件夹路径时提供其中的
index.。html - 相对资源路径无需改动即可使用。从主机根路径开始的路径(
/)则不行。assets/ app. js - 不支持 WebSocket 连接。
{ "action": "create", "dir": "./dist", "label": "built site" }
结果的字段相同,只是带有 "mode": "static"。
list #
列出智能体自己对话的预览,包含 token、label、url、target(文件夹,或 localhost:<port>)、online 和对话。0.9.14 新增 在 0.9.14 之前,该列表涵盖中继的所有预览;预览的令牌是它唯一的保护,因此智能体现在只能看到自己对话的预览。整个中继的列表归你查看,见 Settings → Previews 和 /。中继重启后无法检查的预览带有 "unverified": true,在检查得到回应之前不会提供服务(参见中继重启后)。
{ "action": "list" }
{
"ok": true,
"enabled": true,
"previews": [
{ "token": "0123456789abcdef0123456789abcdef", "label": "web app", "url": "https://preview.example.com/test_0123456789abcdef0123456789abcdef/", "target": "localhost:5173", "online": true, "conversationId": "3f9c…" }
]
}
close #
关闭链接。链接背后的开发服务器会继续运行。
| 参数 | 类型 | |
|---|---|---|
token |
32 个十六进制字符 | 来自 create 或 list 的 token。当对话恰好只有一个预览时可省略。 |
在没有 token 且对话中有多个预览时,调用会返回一个 error 以及可供选择的列表。
0.9.14 新增 智能体只能关闭自己对话的预览。其他对话预览的令牌会被拒绝:
{ "ok": false, "error": "This preview belongs to another conversation; a conversation can close only its own previews. The owner can close it in Settings → Previews." }
{ "action": "close", "token": "0123456789abcdef0123456789abcdef" }
{ "ok": true, "closed": { "token": "0123456789abcdef0123456789abcdef", "label": "web app" } }
限制 #
| 限制 | 值 |
|---|---|
| 整个中继同一时间的预览数 | 8(previews.) |
| 智能体可以列出和关闭哪些预览 | 它自己对话的 0.9.14 新增 |
| 端口 | 1024–65535,且绝不是中继自己的端口之一 |
| 端口预览可以指向哪里 | 中继自己的机器(环回),加上运维者在 previews. 中列出的主机 |
| 文件夹预览可以指向哪里 | 对话工作目录之内 |
| 生命周期 | 直到它被关闭或其对话被删除。0.9.11 新增 中继重启后它仍然保留;端口预览在机器重启时结束,文件夹预览在其文件夹离开工作目录时结束。预览不会自行过期。 |
| 标签 | 120 个字符 |
预览与中继相互隔离:它们在独立的端口、独立的主机名下提供,那里没有中继自己的任何页面或 API。各预览共用这个主机名,每个位于自己的路径下。
对话范围不是一堵墙
中继把预览请求限定在请求所指定的对话内,对话由 conversation 查询参数或对话请求头指定,每个会话 worker 和 OAR 的 MCP 服务器都会发送该请求头。智能体使用与你相同的中继令牌,因此范围是由请求声明的,而非经过验证:未指定对话的请求仍能获得所有预览。这个范围让工具以及教给智能体的调用只作用于它们自己的对话;但它挡不住读取中继令牌并手动调用 API 的智能体。参见 OAR 暴露什么。