> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hitheo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 嵌入 Theo Browser

> 只需一个 iframe，即可将实时 Theo Browser 会话嵌入到你自己的产品中。

每个 Theo Browser 会话响应都包含一个 `embed_url`，它会在第三方 iframe 中渲染完整的 Theo 品牌浏览器外壳（地址栏、实时状态指示器、自动重连界面）。无需 SDK 接线，无需手动处理 CDP —— 只要把 `<iframe>` 指向该 URL 即可上线。

## 快速开始

1. 在你的后端创建会话（切勿将 `THEO_API_KEY` 暴露给浏览器）：

```typescript theme={null}
import { Theo } from "@hitheo/sdk";
const theo = new Theo({ apiKey: process.env.THEO_API_KEY! });

const session = await theo.browser.create({
  url: "https://example.com",
  region: "us-west-2",
});
return Response.json({ embed_url: session.embed_url });
```

2. 将该 URL 放入前端的 iframe 中：

```html theme={null}
<iframe
  src="{embed_url}"
  width="100%"
  height="600"
  allow="clipboard-read; clipboard-write"
  sandbox="allow-scripts allow-same-origin"
></iframe>
```

就这么简单。嵌入页面会自动处理：

* 首次渲染时获取正确的实时视图 URL（不会出现 `about:blank` 闪屏）
* 监听 `browserbase-disconnected` 的 postMessage 消息
* 在 CDP 目标发生变化时自动重新获取并为 iframe 重新赋 key
* 断开连接时以 10 秒为间隔紧密轮询，连接正常时放宽为 30 秒
* 带有 Theo 品牌的地址栏 + 实时状态指示器 + 重连遮罩层

## 令牌的工作原理

`embed_url` 是一个签名、短时效的 URL，绑定到 `(sessionId, userId)`：

* **格式：** `{APP_ORIGIN}/embed/browser/{sessionId}?token=<hmac>`
* **作用范围：** 该令牌与特定会话以及创建该会话的用户绑定
* **有效期：** 60 分钟（Theo Browser 会话硬性上限为 20 分钟，因此这为 `keep_alive` 会话留出了余量）
* **密钥：** 由服务器端使用 `BROWSER_EMBED_TOKEN_SECRET` 签名，永不暴露给客户端
* **轮换：** 每次调用 `GET /api/v1/browser/sessions/:id` 都会生成一个新令牌，因此长时间运行的仪表盘可以按需刷新 iframe

第三方无法为自己不拥有的会话伪造令牌 —— 即使猜中了会话 id，HMAC 校验也会失败。

## 断线处理

嵌入页面已经实现了完整的恢复协议，但如果你要构建自定义的 iframe 包装层，以下是确切的约定：

```javascript theme={null}
// Listen on the parent window for the iframe's disconnect event.
window.addEventListener("message", (ev) => {
  if (ev.data === "browserbase-disconnected") {
    // Option A: let Theo handle it — refetch /live?force=1 and rekey.
    // Option B: surface a "reconnecting…" UI to your user.
  }
});
```

在内部，一旦触发该事件，嵌入页面就会强制重新获取其实时 URL。服务器端的会话管理器每次解析出新的调试器 URL 时都会递增一个单调的 `live_view_version` 计数器 —— iframe 会在版本号变化时重新赋 key，而不是在 URL 相等性变化时，因为 BrowserBase 可能返回相同的 URL 字符串，却在背后悄悄更换了底层的 CDP 页面目标。

## 安全姿态

* **Iframe 沙箱：** `allow-scripts allow-same-origin` 是保持 DevTools 前端与 CDP WebSocket 存活所需的最小权限集合。添加 `allow-forms` / `allow-popups` 没有任何功能上的好处，反而可能触发意外的导航，把 iframe 带离实时视图。
* **`frame-ancestors *`：** 嵌入页面设置了 `Content-Security-Policy: frame-ancestors *`，因此任何来源都可以将其加载到 iframe 中。如果你想限制哪些来源可以加载该嵌入页面，请在你自己的 CDN 层终止该 iframe 请求并应用你自己的响应头。
* **iframe 中不含任何凭据：** 嵌入页面永远不会接触到你的 API 密钥。它通过一个使用令牌鉴权的配套路由 `/embed/browser/:id/live` 进行通信。

## 结束会话

当用户关闭 iframe 时，务必从你的后端结束该会话，否则你将按剩余的时钟时间被计费：

```typescript theme={null}
await theo.browser.end(session.session_id);
```

## 相关内容

* [Browser Sessions API](/api-reference/browser/sessions) —— 完整的 `/api/v1/browser/sessions` 接口
* [Theo Browser SDK](/sdk-reference/browser) —— 适用于 Node/Deno/edge 的类型化辅助工具


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.