> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# Web / Puppeteer SDK

`@midscene/web/puppeteer` 提供绑定 Puppeteer 页面或 Browser 的 Agent。两种 Agent 都继承基础 Agent 的 [AI 方法](/zh/reference.md#interaction-methods)，并支持 Web 配置选项。

## 导入与 API 概览

```typescript
import {
  PuppeteerPageAgent,
  PuppeteerAgent,
  PuppeteerBrowserAgent,
} from '@midscene/web/puppeteer';
```

| API                     | 作用范围         | 用途                                      |
| ----------------------- | ------------ | --------------------------------------- |
| `PuppeteerPageAgent`    | 单个 `Page`    | 始终操作同一个页面                               |
| `PuppeteerAgent`        | 单个 `Page`    | `PuppeteerPageAgent` 的兼容别名，行为相同         |
| `PuppeteerBrowserAgent` | 一个 `Browser` | 在浏览器的多个页面之间切换，后续 AI 操作作用于当前 active page |

以下签名中的 `Page`、`Browser` 来自 `puppeteer`；配置类型来自 `@midscene/web/puppeteer`。

[](#)
[](#)
[](#)
## `PuppeteerPageAgent` {#puppeteer-agent}

```typescript
new PuppeteerPageAgent(page: Page, options?: PuppeteerPageAgentOpt)
```

- `page`：必填，已有的 Puppeteer 页面。未传入时抛出错误。
- `options`：可选，支持[通用 Agent 配置](/zh/reference.md#agent-options)和下方的 [Web 配置](#web-options)。

```typescript
const agent = new PuppeteerPageAgent(page, {
  forceSameTabNavigation: false,
});
await agent.aiAct('点击登录按钮');
```

Agent 绑定传入的页面。默认 `forceSameTabNavigation: true`，会拦截 `window.open` 和 `target="_blank"` 链接，使其在当前页面打开。设为 `false` 后保留新标签页行为，但 Agent 不会自动切换到新页面。

## `PuppeteerBrowserAgent` {#puppeteer-browser-agent}

```typescript
new PuppeteerBrowserAgent(
  browser: Browser,
  initialPage: Page,
  options?: PuppeteerBrowserAgentOpt,
)
```

- `browser`：必填，用于监听页面创建事件、列出和创建页面的 Browser。
- `initialPage`：必填，初始 active page，应使用该 browser 中的页面。
- `options`：可选，支持通用 Agent 配置、Web 配置和下面的专属选项。

未传入 `browser` 或 `initialPage` 时抛出错误。Browser Agent 不接受 `forceSameTabNavigation`；即使通过 JavaScript 传入 `false` 也会报错。

### `PuppeteerBrowserAgent.create()`

```typescript
PuppeteerBrowserAgent.create(
  browser: Browser,
  options?: PuppeteerBrowserAgentCreateOpt,
): Promise<PuppeteerBrowserAgent>
```

异步创建 Agent。初始页面按以下顺序选择：`options.initialPage` → `(await browser.pages())[0]` → `await browser.newPage()`。

```typescript
const agent = await PuppeteerBrowserAgent.create(browser, {
  autoFollowNewPage: true,
});
```

### 专属配置

| 参数                  | 类型        | 默认值     | 说明                               |
| ------------------- | --------- | ------- | -------------------------------- |
| `autoFollowNewPage` | `boolean` | `false` | browser 创建新页面时自动将其设为 active page |
| `newPageTimeout`    | `number`  | `5000`  | `waitForNewPage()` 默认等待时间，单位毫秒   |
| `initialPage`       | `Page`    | 自动选择    | 仅用于 `create()`，指定初始 active page  |

[](#)
## 页面管理 API {#page-management-api}

以下属性和方法仅属于 `PuppeteerBrowserAgent`。

### `activePage`

```typescript
agent.activePage: Page
```

只读属性，返回当前 AI 操作使用的 Puppeteer 页面。使用 `setActivePage()` 切换。

### `pages()`

```typescript
agent.pages(): Promise<Page[]>
```

返回绑定 browser 中的页面，不改变 active page。

### `newPage()`

```typescript
agent.newPage(): Promise<Page>
```

在绑定浏览器的默认 BrowserContext 中创建页面，将其设为 active page，并返回该页面。

### `setActivePage()`

```typescript
agent.setActivePage(page: Page): Promise<void>
```

将指定页面设为 active page，并尝试将其置于前台。传入无效或已关闭的页面时抛出错误。应传入绑定 browser 中的页面。

### `waitForNewPage()`

```typescript
agent.waitForNewPage(
  action?: () => Promise<unknown> | unknown,
  options?: { timeout?: number },
): Promise<Page>
```

先监听 browser 的 `targetcreated` 事件（仅处理页面类型的 target），再执行可选的 `action`，返回监听到的新页面。`options.timeout` 覆盖本次等待时间，单位毫秒；默认使用 `newPageTimeout`。超时或 `action` 失败时抛出错误。

此方法本身不会切换 active page。若设置了 `autoFollowNewPage: true`，独立的新页面监听器仍会自动切换。需要显式控制切换时，保持该选项为 `false`：

```typescript
const agent = await PuppeteerBrowserAgent.create(browser, {
  autoFollowNewPage: false,
});
const newPage = await agent.waitForNewPage(
  () => agent.aiTap('打开详情'),
  { timeout: 10000 },
);
await agent.setActivePage(newPage);
await agent.aiAssert('详情页面已打开');
```

## 配置选项 {#web-options}

除下表外，两种 Agent 均接受[通用 Agent 配置](/zh/reference.md#agent-options)，例如模型、报告和缓存配置。

| 参数                               | 类型                                                  | 默认值        | 说明                                                                                     |
| -------------------------------- | --------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------- |
| `forceSameTabNavigation`         | `boolean`                                           | `true`     | 仅 Page Agent 可用，强制在当前页面打开新标签页链接                                                        |
| `waitForNavigationTimeout`       | `number`                                            | `5000`     | 等待页面 `html` 元素的超时，单位毫秒；`0` 跳过检查。超时后继续执行，不保证应用就绪                                        |
| `waitForNetworkIdleTimeout`      | `number`                                            | `2000`     | 动作后等待网络空闲的超时，单位毫秒；要求连续 `200` 毫秒最多有两个进行中的请求。`0` 跳过检查，超时后继续执行                            |
| `enableTouchEventsInActionSpace` | `boolean`                                           | `false`    | 向动作空间添加触摸手势                                                                            |
| `keyboardTypeDelay`              | `number`                                            | 未设置        | 字符输入间隔，单位毫秒，必须是有限非负数；legacy 模式下由 Puppeteer 的 `page.keyboard.type` 处理正数延迟，未设置时使用底层驱动默认值 |
| `inputStrategy`                  | `'legacy' \| 'sequential' \| 'bulk'`                | `'legacy'` | 文本输入策略；`sequential` 逐个 Unicode 码点输入，`bulk` 一次插入完整文本                                    |
| `forceChromeSelectRendering`     | `boolean`                                           | `true`     | 注入 `base-select` 样式，使原生 `select` 的选项可被截图捕捉；要求 Puppeteer ≥ `24.6.0`，且浏览器支持该样式           |
| `customActions`                  | `DeviceAction[]`                                    | 未设置        | 向规划器添加[自定义动作](/zh/integrate-with-any-interface.md)                                     |
| `beforeInvokeAction`             | `(actionName: string, param: any) => Promise<void>` | 未设置        | 动作执行前的回调                                                                               |
| `afterInvokeAction`              | `(actionName: string, param: any) => Promise<void>` | 未设置        | 动作执行后的回调                                                                               |
| `testRunner`                     | `PuppeteerPageAgentOpt['testRunner']`               | 未设置        | 为 Midscene Test 的 Puppeteer Nodes 提供 base URL 和 Cookie 等配置                             |

`inputStrategy: 'bulk'` 要求不设置 `keyboardTypeDelay` 或将其设为 `0`。需要延迟输入时使用 `'sequential'`。等待业务界面就绪时，使用 `aiWaitFor()` 或 Puppeteer 自身的等待 API。

## 生命周期 {#lifecycle}

```typescript
agent.destroy(): Promise<void>
```

两种 Agent 都支持 `destroy()`：等待相关操作完成，清理 Agent 持有的资源、调用内部 Device 的 `destroy()`，并完成报告处理。Browser Agent 还会移除用于自动跟随新页面的 `targetcreated` 监听器。

Puppeteer Device 的 `destroy()` 不关闭传入的 Page、BrowserContext 或 Browser，也不主动断开远程浏览器连接。由调用方管理这些 Puppeteer 资源；销毁 Agent 后，页面和浏览器仍可继续使用。

```typescript
try {
  await agent.aiAct('提交表单');
} finally {
  await agent.destroy();
  // 按资源所有权，由调用方关闭 page、context 或 browser。
}
```

## 浏览器支持与限制

推荐使用 Chrome / Chromium。Midscene 的 Puppeteer 集成依赖 CDP，包括触摸手势和部分交互操作。不要将 Puppeteer 自身支持的其他浏览器或协议视为 Midscene 的支持范围。

### 远程浏览器

Agent 接受已有的 Puppeteer 页面，创建方式与本地浏览器一致。例如使用 CDP 连接后：

```typescript
import puppeteer from 'puppeteer';
import { PuppeteerPageAgent } from '@midscene/web/puppeteer';

const browser = await puppeteer.connect({ browserWSEndpoint: cdpWsUrl });
const page = (await browser.pages())[0] ?? await browser.newPage();
const agent = new PuppeteerPageAgent(page);
```

连接和身份验证由调用方管理。远程浏览器使用完毕后，可调用 `browser.disconnect()` 断开 Puppeteer 连接并保留浏览器进程；`browser.close()` 会关闭浏览器。

[](#)
#### 获取 CDP WebSocket URL {#get-cdp-url}

你可以从多种来源获取 CDP WebSocket URL：

- **Browser Use**：在 [https://cloud.browser-use.com](https://cloud.browser-use.com) 获取 API Key，并连接 `wss://connect.browser-use.com?apiKey=YOUR_API_KEY`
- **BrowserBase**：在 [https://browserbase.com](https://browserbase.com) 注册并获取你的 CDP URL
- **Browserless**：使用 [https://browserless.io](https://browserless.io) 或运行你自己的实例
- **本地 Chrome**：使用 `--remote-debugging-port=9222` 参数运行 Chrome，然后使用 `ws://localhost:9222/devtools/browser/...`
- **Docker**：在 Docker 容器中运行 Chrome 并暴露调试端口

## FAQ

### 安装 Puppeteer 很慢，或安装时卡住

Puppeteer 会在安装时通过 `postinstall` 自动下载浏览器镜像。下载慢或网络不通时，安装过程就会卡住。

```bash
# 先跳过浏览器下载，让依赖安装完成
PUPPETEER_SKIP_DOWNLOAD=true pnpm install

# 再单独从镜像站下载 Chrome
# 下面以 https://registry.npmmirror.com 为例，不同镜像平台的 base-url 规则可能不同
pnpm exec puppeteer browsers install chrome --base-url="https://registry.npmmirror.com/-/binary/chrome-for-testing"
```

### 下拉框点击不到

如果点击下拉框后，报告中的截图没有出现下拉选项，通常是页面使用了原生 `select` 标签，导致展开后的下拉面板由操作系统原生控件渲染，没有真正出现在浏览器页面里。

Midscene 默认开启 `forceChromeSelectRendering` 选项，强制由 Chrome 来渲染下拉框；如需关闭可将其设为 `false`。详细原因、判断方式和效果说明，请参考 [Playwright FAQ — 下拉框点击不到](/zh/sdk/playwright.md#下拉框点击不到)。

### 浏览器界面持续闪动

通常是 viewport 的 `deviceScaleFactor` 与系统像素比不匹配所致。将 `deviceScaleFactor` 设为 `0` 可自动适配：

```typescript
await page.setViewport({
  width: 1280,
  height: 720,
  deviceScaleFactor: 0,
});
```

更多详情请参考 [Playwright FAQ — 浏览器界面持续闪动](/zh/sdk/playwright.md#浏览器界面持续闪动)。

### 自定义网络超时

通过 `waitForNavigationTimeout` 和 `waitForNetworkIdleTimeout` 调整或关闭内置检查，详见[配置选项](#web-options)。
