vercel-labs/remote-agent-browser
GitHub: vercel-labs/remote-agent-browser
一个基于 Vercel Sandbox 的云端可编程浏览器,为 AI Agent 提供隔离的、有状态的网页自动化执行环境。
Stars: 79 | Forks: 3
# remote-agent-browser
通过隔离的 [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) 在云端运行 [agent-browser](https://github.com/vercel-labs/agent-browser)。
## 安装
```
pnpm add remote-agent-browser
```
## 使用
`AgentBrowser.create()` 会从预构建的浏览器镜像启动一个全新的 Vercel Sandbox。同一个客户端中的命令会共享相同的页面、cookies、标签页和元素引用。`close()` 会关闭 Chromium 并停止该 Sandbox。
## 示例
- [捕获屏幕截图](./examples/screenshot/README.md) — 捕获整页 PNG 并将其保存在本地。
- [Agent Bash 工具集成](./examples/agent-bash-tool/README.md) — 让 agent 通过其 Bash 工具使用常规的 `agent-browser ` 调用。
- [为可信自动化绕过 BotID](./examples/botid-bypass/README.md) — 将 Vercel 自动化绕过 token 作为 origin 作用域的 header 传入。
- [通过代理路由流量](./examples/proxy/README.md) — 配置带有身份验证的代理并验证浏览器的出口地址。
## 身份验证
在 Vercel 上通过 `VERCEL_OIDC_TOKEN` 运行时,身份验证是自动完成的。
对于本地开发,请关联一个 Vercel 项目并拉取其环境配置:
```
vercel link
vercel env pull .env.local
node --env-file=.env.local examples/screenshot/index.mjs
```
## API
### `AgentBrowser.create()`
在一个一次性的 Vercel Sandbox 中启动全新的浏览器。完成后请务必调用 `browser.close()`。
通过 `args` 传递 agent-browser 的全局选项。它们会被放置在每条命令之前,这对于颜色配置、用户配置以及初始化脚本等启动设置是必需的:
```
const browser = await AgentBrowser.create({
args: ['--color-scheme', 'dark', '--enable', 'react-devtools'],
})
```
### 代理
使用 `proxy` 为浏览器客户端的生命周期设置代理。使用对象形式来配置需要直接连接的主机:
```
const browser = await AgentBrowser.create({
proxy: {
url: 'http://user:password@proxy.example.com:8080',
bypass: ['localhost', '*.internal.example.com'],
},
})
```
对于没有绕过规则的代理,请直接传递其 URL:
```
const browser = await AgentBrowser.create({
proxy: 'http://proxy.example.com:8080',
})
```
### `browser.run(commands)`
在同一会话中运行多个 agent-browser 命令:
```
const result = await browser.run([
['open', 'https://my-preview.vercel.app'],
['wait', '--load', 'networkidle'],
['snapshot', '-i', '--json'],
['click', '@e3'],
])
```
### `browser.exec(command, options?)`
运行带有参数和标志的单一命令:
```
await browser.exec('find', {
args: ['role', 'button', 'click'],
flags: { name: 'Submit' },
})
```
### `browser.shell(command, options?)`
当需要转发 Bash-tool 命令或将 `agent-browser` 与常规 shell 工具组合使用时,请使用此方法。该命令会原封不动地执行,因此引号、管道、重定向和控制操作符都会保留其 shell 语义。对于不需要 shell 的浏览器命令,首选 `exec()` 或 `run()`。每次 `agent-browser` 调用都会通过 `AGENT_BROWSER_SESSION` 继承客户端的 CLI 会话。
```
const result = await browser.shell(
'agent-browser read "https://example.com" | grep -io "example" | wc -l',
)
console.log(result.stdout)
```
客户端全局的 `args` 不会被插入到 shell 字符串中,因为这样做需要重写任意的 shell 语法。在使用 `shell()` 时,请将全局 CLI 参数直接放在命令中。
### Typed JSON
对于支持 `--json` 的命令,请设置 `output: 'json'`。`exec()` 会添加该标志,解包 CLI 响应信封,并将常规命令字段与类型化的 `data` 值一并保留:
```
type UrlResult = { url: string }
const result = await browser.exec('get', {
args: ['url'],
output: 'json',
})
console.log(result.data.url, result.ok)
```
### 文件传输
通过页面的文件输入框上传本地缓冲区数据,或者收集浏览器下载文件,而无需暴露 Sandbox 文件系统:
```
await browser.upload('#avatar', [
{ name: 'avatar.png', bytes: await readFile('avatar.png') },
])
const { file } = await browser.download('#export', { filename: 'report.csv' })
await writeFile('report.csv', file.bytes)
```
如果未提供输出路径,其他生成文件的命令会将其产物收集到 `result.file` 中。这包括屏幕截图、PDF、追踪记录、性能分析、HAR 文件、保存的浏览器状态以及录像:
```
await browser.exec('network', { args: ['har', 'start'] })
// ...interact with the page...
const result = await browser.exec('network', { args: ['har', 'stop'] })
await writeFile('capture.har', result.file.bytes)
```
当命令已经包含明确的远程输出路径时,无需更改命令即可直接读取:
```
await browser.shell(
'agent-browser screenshot /tmp/verification.png',
)
const file = await browser.readFile('/tmp/verification.png', 'image/png')
await writeFile('verification.png', file.bytes)
```
### 便捷方法
- `browser.snapshot(url?)` 可选地打开一个页面,然后返回其交互式快照。省略 URL 则检查当前页面。
- `browser.screenshot(url?, { fullPage: true })` 可选地打开一个页面,然后返回一个 PNG 缓冲区。将 options 对象放在第一个参数以捕获当前页面。
- `browser.close()` 关闭会话并停止 Sandbox。
所有方法都会使用同一个一次性浏览器会话,直到调用 `close()`。
### 稳定的浏览器 ID
当浏览器身份需要跨越进程边界保留时,请使用 `AgentBrowser.session()`。它返回一个惰性句柄:构造它或启动 keepalive 并不会创建 Sandbox。第一个浏览器命令会找到或创建一个派生自调用者定义 ID 的 runtime,而另一个使用相同 ID 的进程会再次找到该 runtime。
```
const browser = AgentBrowser.session({ id: `environment:${chatId}` })
const stopKeepalive = browser.keepalive()
try {
await browser.exec('open', { args: ['https://example.com'] })
} finally {
stopKeepalive()
}
```
ID 是项目作用域的。当同一个应用程序在多个环境中使用一个 Vercel 项目时,请包含部署环境或其他命名空间。
底层的 Sandbox 名称是私有的,并派生自该 ID 的哈希值。
如果已过期的 Sandbox 恢复运行,其 Chromium 进程将全新启动。当调用者需要提示页面、cookies、引用、标签页和控制台历史记录已丢失时,请在运行命令前订阅 `reset`:
```
browser.on('reset', ({ reason }) => {
console.log(`Browser runtime reset: ${reason}`)
})
```
`browser.destroy()` 会永久移除具名的 runtime。停止 keepalive 仅允许其按照正常的空闲超时时间过期。
### Keepalive
在长时间运行的任务中,让 Sandbox 在空闲期间保持存活。请务必在 `finally` 块中停止心跳;停止后,除非提前关闭,否则 Sandbox 将在其正常超时时间到期:
```
const stopKeepalive = browser.keepalive()
try {
await runLongAgentTurn(browser)
} finally {
stopKeepalive()
}
```
默认情况下,每次心跳都会恢复由 `AgentBrowser.create()` 配置的墙上时钟超时(wall-clock timeout),并在该时间窗口过半时运行,且最长不超过五分钟。可以通过 `timeoutMs` 和 `intervalMs` 覆盖这两个值。续订操作是尽力而为的;可通过传递 `onError` 来观察失败情况。
容器镜像和开发详情请参阅 [docs.md](./docs.md)。
标签:AI智能体, GNU通用公共许可证, MITM代理, Node.js, Vercel沙箱, 云浏览器, 无头浏览器, 自动化攻击