一、browser 工具
1.1 工具概述
功能 :控制浏览器(自动化)
核心特性:
- 支持多种操作(status/start/stop/profiles/tabs/open/snapshot/screenshot/actions)
- 支持沙盒桥接
- 支持节点代理
- 支持配置文件选择(openclaw/user)
- 支持 Playwright 自动化
1.2 Schema 定义
位置:第 23854 行附近
javascript
const BrowserToolSchema = Type.Object({
action: stringEnum(BROWSER_ACTIONS),
profile: Type.Optional(Type.String()),
node: Type.Optional(Type.String()),
target: Type.Optional(Type.String()),
url: Type.Optional(Type.String()),
targetId: Type.Optional(Type.String()),
fullPage: Type.Optional(Type.Boolean()),
ref: Type.Optional(Type.String()),
element: Type.Optional(Type.String()),
type: Type.Optional(Type.String())
// ... 更多参数
});
1.3 完整执行代码(部分)
位置:第 23854 行
javascript
function createBrowserTool(opts) {
// 1. 解析目标默认值
const targetDefault = opts?.sandboxBridgeUrl ? "sandbox" : "host";
const hostHint = opts?.allowHostControl === false ?
"Host target blocked by policy." : "Host target allowed.";
return {
label: "Browser",
name: "browser",
description: [
"Control the browser via OpenClaw's browser control server (status/start/stop/profiles/tabs/open/snapshot/screenshot/actions).",
"Browser choice: omit profile by default for the isolated OpenClaw-managed browser (`openclaw`).",
'For the logged-in user browser on the local host, use profile="user". A supported Chromium-based browser (v144+) must be running. Use only when existing logins/cookies matter and the user is present.',
"When a node-hosted browser proxy is available, the tool may auto-route to it. Pin a node with node=<id|name> or target=\"node\".",
"When using refs from snapshot (e.g. e12), keep the same tab: prefer passing targetId from the snapshot response into subsequent actions (act/click/type/etc).",
'For stable, self-resolving refs across calls, use snapshot with refs="aria" (Playwright aria-ref ids). Default refs="role" are role+name-based.',
"Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.",
`target selects browser location (sandbox|host|node). Default: ${targetDefault}.`,
hostHint
].join(" "),
parameters: BrowserToolSchema,
execute: async (_toolCallId, args) => {
const params = args;
// 2. 解析 action(必填)
const action = readStringParam$1(params, "action", { required: true });
const profile = readStringParam$1(params, "profile");
const requestedNode = readStringParam$1(params, "node");
let target = readStringParam$1(params, "target");
// 3. 检查节点限制
if (requestedNode && target && target !== "node") {
throw new Error('node is only supported with target="node".');
}
// 4. 解析目标
if (shouldPreferHostForProfile(profile)) {
if (requestedNode || target === "node") {
throw new Error(`profile="${profile}" only supports the local host browser.`);
}
if (target === "sandbox") {
throw new Error(`profile="${profile}" cannot use the sandbox browser; use target="host" or omit target.`);
}
if (!target && !requestedNode) target = "host";
}
// 5. 解析节点目标
const nodeTarget = await resolveBrowserNodeTarget({
requestedNode: requestedNode ?? void 0,
target,
sandboxBridgeUrl: opts?.sandboxBridgeUrl
});
// 6. 解析基础 URL
const baseUrl = nodeTarget ? void 0 : resolveBrowserBaseUrl({
target: target === "node" ? void 0 : target,
sandboxBridgeUrl: opts?.sandboxBridgeUrl,
allowHostControl: opts?.allowHostControl
});
// 7. 构建代理请求(节点模式)
const proxyRequest = nodeTarget ? async (opts) => {
const proxy = await callBrowserProxy({
nodeId: nodeTarget.nodeId,
method: opts.method,
path: opts.path,
query: opts.query,
body: opts.body,
timeoutMs: opts.timeoutMs,
profile: opts.profile
});
const mapping = await persistProxyFiles(proxy.files);
applyProxyPaths(proxy.result, mapping);
return proxy.result;
} : null;
// 8. 根据 action 执行
switch (action) {
// === action: status ===
case "status":
if (proxyRequest) {
return jsonResult(await proxyRequest({
method: "GET",
path: "/",
profile
}));
}
return jsonResult(await browserToolDeps.browserStatus(baseUrl, { profile }));
// === action: start ===
case "start":
if (proxyRequest) {
await proxyRequest({
method: "POST",
path: "/start",
profile
});
return jsonResult(await proxyRequest({
method: "GET",
path: "/",
profile
}));
}
await browserToolDeps.browserStart(baseUrl, { profile });
return jsonResult(await browserToolDeps.browserStatus(baseUrl, { profile }));
// === action: stop ===
case "stop":
if (proxyRequest) {
await proxyRequest({
method: "POST",
path: "/stop",
profile
});
return jsonResult(await proxyRequest({
method: "GET",
path: "/",
profile
}));
}
await browserToolDeps.browserStop(baseUrl, { profile });
return jsonResult(await browserToolDeps.browserStatus(baseUrl, { profile }));
// === action: profiles ===
case "profiles":
if (proxyRequest) {
return jsonResult(await proxyRequest({
method: "GET",
path: "/profiles"
}));
}
return jsonResult({ profiles: await browserToolDeps.browserProfiles(baseUrl) });
// === action: tabs ===
case "tabs":
return await executeTabsAction({ baseUrl, profile, proxyRequest });
// === action: open ===
case "open": {
const targetUrl = readTargetUrlParam(params);
if (proxyRequest) {
return jsonResult(await proxyRequest({
method: "POST",
path: "/tabs/open",
profile,
body: { url: targetUrl }
}));
}
const opened = await browserToolDeps.browserOpenTab(baseUrl, targetUrl, { profile });
browserToolDeps.trackSessionBrowserTab({
sessionKey: opts?.agentSessionKey,
targetId: opened.targetId,
baseUrl,
profile
});
return jsonResult(opened);
}
// === action: focus ===
case "focus": {
const targetId = readStringParam$1(params, "targetId", { required: true });
if (proxyRequest) {
return jsonResult(await proxyRequest({
method: "POST",
path: "/tabs/focus",
profile,
body: { targetId }
}));
}
await browserToolDeps.browserFocusTab(baseUrl, targetId, { profile });
return jsonResult({ ok: true });
}
// === action: close ===
case "close": {
const targetId = readStringParam$1(params, "targetId");
if (proxyRequest) {
return jsonResult(targetId ? await proxyRequest({
method: "DELETE",
path: `/tabs/${encodeURIComponent(targetId)}`,
profile
}) : await proxyRequest({
method: "POST",
path: "/act",
profile,
body: { kind: "close" }
}));
}
if (targetId) {
await browserToolDeps.browserCloseTab(baseUrl, targetId, { profile });
browserToolDeps.untrackSessionBrowserTab({
sessionKey: opts?.agentSessionKey,
targetId,
baseUrl,
profile
});
} else {
await browserToolDeps.browserAct(baseUrl, { kind: "close" }, { profile });
}
return jsonResult({ ok: true });
}
// === action: snapshot ===
case "snapshot":
return await executeSnapshotAction({ input: params, baseUrl, profile, proxyRequest });
// === action: screenshot ===
case "screenshot": {
const targetId = readStringParam$1(params, "targetId");
const fullPage = Boolean(params.fullPage);
const ref = readStringParam$1(params, "ref");
const element = readStringParam$1(params, "element");
const type = params.type === "jpeg" ? "jpeg" : "png";
const result = proxyRequest ? await proxyRequest({
method: "POST",
path: "/screenshot",
profile,
body: { targetId, fullPage, ref, element, type }
}) : await browserToolDeps.browserScreenshotAction(baseUrl, {
targetId, fullPage, ref, element, type, profile
});
return await browserToolDeps.imageResultFromFile({
label: "browser:screenshot",
path: result.path,
details: result
});
}
// === action: navigate ===
case "navigate": {
const targetUrl = readTargetUrlParam(params);
const targetId = readStringParam$1(params, "targetId");
if (proxyRequest) {
return jsonResult(await proxyRequest({
method: "POST",
path: "/navigate",
profile,
body: { url: targetUrl, targetId }
}));
}
await browserToolDeps.browserNavigate(baseUrl, { url: targetUrl, targetId, profile });
return jsonResult({ ok: true });
}
// === action: act ===
case "act":
return await executeActAction({ input: params, baseUrl, profile, proxyRequest });
// === 未知 action ===
default:
throw new Error(`Unknown action: ${action}`);
}
}
};
}
1.4 支持的 Actions
| Action | 说明 | 必需参数 |
|---|---|---|
status |
获取浏览器状态 | 无 |
start |
启动浏览器 | 无 |
stop |
停止浏览器 | 无 |
profiles |
列出配置文件 | 无 |
tabs |
列出标签页 | 无 |
open |
打开 URL | url |
focus |
聚焦标签页 | targetId |
close |
关闭标签页 | 无 |
snapshot |
获取页面快照 | 无 |
screenshot |
截图 | 无 |
navigate |
导航 | url |
act |
执行操作(点击/输入等) | action |
1.5 配置文件
| Profile | 说明 | 使用场景 |
|---|---|---|
| openclaw | OpenClaw 管理的隔离浏览器 | 默认,无需登录 |
| user | 用户已登录的浏览器 | 需要 Cookie/登录状态 |
二、agents_list 工具
2.1 工具概述
功能 :列出可用的 Agent
核心特性:
- 读取配置文件
- 支持过滤
- 支持排序
- 返回 Agent 元数据
2.2 Schema 定义
位置:第 22980 行附近
javascript
const AgentsListToolSchema = Type.Object({});
2.3 完整执行代码
位置:第 22986 行
javascript
function createAgentsListTool(opts) {
return {
label: "Agents List",
name: "agents_list",
description: "List available agents configured in the system.",
parameters: AgentsListToolSchema,
execute: async (_toolCallId, args) => {
// 1. 加载配置
const cfg = opts?.config ?? loadConfig();
// 2. 解析 Agent 列表
const agents = cfg?.agents?.list ?? [];
// 3. 构建结果
const result = agents.map((agent) => ({
id: agent.id,
label: agent.label,
description: agent.description,
model: agent.model,
thinking: agent.thinking,
verbose: agent.verbose,
tools: agent.tools,
sandbox: agent.sandbox,
enabled: agent.enabled !== false
}));
return jsonResult({
status: "ok",
count: result.length,
agents: result
});
}
};
}
2.4 返回结果格式
json
{
"status": "ok",
"count": 3,
"agents": [
{
"id": "main",
"label": "Main Agent",
"description": "Primary assistant agent",
"model": "bailian/qwen3.5-plus",
"thinking": "high",
"verbose": false,
"tools": ["read", "write", "exec", ...],
"sandbox": { "enabled": false },
"enabled": true
},
{
"id": "coding",
"label": "Coding Agent",
"description": "Specialized coding assistant",
"model": "bailian/qwen3.5-coder",
"thinking": "high",
"verbose": false,
"tools": ["read", "write", "edit", "exec"],
"sandbox": { "enabled": true },
"enabled": true
}
]
}
三、关键机制对比
3.1 功能定位
| 特性 | browser | agents_list |
|---|---|---|
| 用途 | 浏览器自动化 | Agent 配置查询 |
| 复杂度 | 高(多 actions) | 低(只读) |
| 外部依赖 | 浏览器服务 | 无 |
3.2 操作类型
| 特性 | browser | agents_list |
|---|---|---|
| actions 数量 | 12+ 个 | 1 个(隐式 list) |
| 写操作 | start/stop/open | 只读 |
| 媒体支持 | 截图/快照 | 不支持 |
3.3 安全限制
| 限制类型 | browser | agents_list |
|---|---|---|
| 沙盒支持 | sandboxBridgeUrl | 不支持 |
| 主机控制 | allowHostControl | 不支持 |
| 节点代理 | 支持 | 不支持 |
四、使用示例
4.1 browser 工具调用
用户 :打开浏览器并访问 Google
大模型返回:
json
{
"tool_call": {
"name": "browser",
"arguments": {
"action": "open",
"url": "https://google.com"
}
}
}
执行结果:
json
{
"targetId": "tab_abc123",
"url": "https://google.com",
"title": "Google",
"status": "loaded"
}
4.2 agents_list 工具调用
用户 :列出所有可用的 Agent
大模型返回:
json
{
"tool_call": {
"name": "agents_list",
"arguments": {}
}
}
执行结果:
json
{
"status": "ok",
"count": 2,
"agents": [
{
"id": "main",
"label": "Main Agent",
"model": "bailian/qwen3.5-plus",
"enabled": true
},
{
"id": "coding",
"label": "Coding Agent",
"model": "bailian/qwen3.5-coder",
"enabled": true
}
]
}