本文档记录了 web-access skill 在 opencode 环境下的安装、配置过程,以及遇到的问题和解决方案。

目录


什么是 web-access skill

web-access 是一个为 AI Agent 提供完整联网能力的 skill,它补足了 Agent 在以下方面的能力:

能力 说明
联网工具自动选择 WebSearch / WebFetch / curl / Jina / CDP,按场景自主判断
CDP Proxy 浏览器操作 直连用户日常浏览器,天然携带登录态,支持动态页面、交互操作
并行分治 多目标时分发子 Agent 并行执行,共享一个 Proxy,tab 级隔离
站点经验积累 按域名存储操作经验,跨 session 复用

GitHub: https://github.com/eze-is/web-access


核心原理

架构图

( S K I L L . m d ) C W h e r b o S m o e c w C l k e D o D e b P c e t - a v o a P l T p c r h o e c o o o ( n e x s l D c s y t s i o s : a d ( 3 P / e s H 4 r C k T 5 o h A i T 6 t r g l P o o e l c m n ( A o e t r P l / e I A f ) ( r e C c r D ) e P n ) c e s / )

关键组件

  1. SKILL.md: 核心 skill 文件,包含浏览哲学、工具选择策略、操作指南
  2. cdp-proxy.mjs: Node.js 服务,提供 HTTP API 来操控浏览器
  3. check-deps.sh: 环境检查脚本,验证依赖并启动 proxy
  4. references/: 参考文档和站点经验存储

工具选择策略

场景 推荐工具
搜索摘要或关键词结果 WebSearch
URL 已知,需要从页面提取特定信息 WebFetch
URL 已知,需要原始 HTML 源码 curl
需要登录态、交互操作、动态页面 浏览器 CDP

安装步骤

1. 克隆仓库到 opencode skills 目录

# 创建 skills 目录(如果不存在)
mkdir -p ~/.config/opencode/skills

# 克隆 web-access 仓库
git clone https://github.com/eze-is/web-access.git ~/.config/opencode/skills/web-access

2. 验证目录结构

ls -la ~/.config/opencode/skills/web-access/

# 预期输出:
# SKILL.md
# scripts/
#   ├── cdp-proxy.mjs
#   └── check-deps.sh
# references/
#   ├── cdp-api.md
#   └── site-patterns/

3. 设置执行权限

chmod +x ~/.config/opencode/skills/web-access/scripts/*.sh
chmod +x ~/.config/opencode/skills/web-access/scripts/*.mjs

4. 验证 skill 已被识别

opencode 会在启动时自动扫描 ~/.config/opencode/skills/*/SKILL.md,skill 的 frontmatter 格式如下:

---
name: web-access
license: MIT
description:
  所有联网操作必须通过此 skill 处理,包括:搜索、网页抓取、登录后操作等。
metadata:
  author: 一泽Eze
  version: "2.4.0"
---

CDP 模式配置

前置要求

  • Node.js 22+: CDP Proxy 使用原生 WebSocket
  • 浏览器: Chrome / Dia / Arc / Brave 等 Chromium 系浏览器

必须使用命令行启动浏览器

重要: Dia 浏览器基于 Arc 架构,有额外的安全限制。必须使用命令行参数启动:

# macOS - Dia(推荐)
/Applications/Dia.app/Contents/MacOS/Dia --remote-debugging-port=9222

# macOS - Chrome
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

# macOS - Arc
/Applications/Arc.app/Contents/MacOS/Arc --remote-debugging-port=9222

# Linux
google-chrome --remote-debugging-port=9222

为什么不能用 chrome://inspect 方式?

Dia/Arc 等基于 Arc 架构的浏览器在使用 chrome://inspect/#remote-debugging 勾选方式开启调试时,有以下限制:

  1. Origin 验证: 只允许来自 chrome://inspect 的 WebSocket 连接
  2. 外部连接被拒绝: 其他来源的连接返回 403 Forbidden
  3. 错误信息示例:
    R U e s j e e c t - e r d e m a o n t e i - n a c l o l m o i w n - g o r W i e g b i S n o s c = k * e t t o c o a n l n l e o c w t i a o l n l f o r r o i m g i t n h s e . c h r o m e : / / i n s p e c t o r i g i n .

使用 --remote-debugging-port=9222 参数启动可以绑过这个限制。

验证配置

# 1. 先用命令行启动浏览器(在另一个终端)
/Applications/Dia.app/Contents/MacOS/Dia --remote-debugging-port=9222

# 2. 运行环境检查
bash ~/.config/opencode/skills/web-access/scripts/check-deps.sh

# 预期输出:
# node: ok (v25.x.x)
# dia/chrome: ok (port 9222)
# proxy: ready

验证 HTTP 调试端点

# 检查浏览器调试端口是否正常
curl -s http://127.0.0.1:9222/json/version

# 预期输出包含 webSocketDebuggerUrl:
# {
#   "Browser": "Chrome/146.0.7680.154",
#   "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/xxx"
# }

遇到的问题与解决方案

问题 1:Dia 浏览器 WebSocket 连接被拒绝(403 Forbidden)

现象

[ C D P P r o x y ] :

调试过程

# 使用 TCP 测试发现返回 403
$ node -e "..." # 发送 WebSocket 握手请求

# 收到响应:
HTTP/1.1 403 Forbidden
Rejected an incoming WebSocket connection from the chrome://inspect origin.
Use the command line flag --remote-allow-origins=chrome://inspect to allow...

根本原因

  • Dia 浏览器基于 Arc 架构,有额外的安全限制
  • 使用 chrome://inspect 方式开启调试时,只允许来自 chrome://inspect 的连接
  • 外部 WebSocket 连接被拒绝

解决方案: 使用 --remote-debugging-port=9222 参数启动浏览器:

/Applications/Dia.app/Contents/MacOS/Dia --remote-debugging-port=9222

问题 2:DevToolsActivePort 文件中的 WebSocket URL 失效

现象

# 9 / # " 2 d w D 2 e e e 2 v b v t / S T o j o o o s c o l o k l s n e s / / t A b v D c r e e t o r b i w s u v s i g e e o g P r n e o / r r a U t 4 r a l b " 0 U : f R 0 L " 1 w - s 9 : 9 / b / 1 1 - 2 4 7 9 . 0 0 8 . - 0 9 . f 1 a : a 9 - 2 9 2 8 2 8 / a d b e 9 v 5 t 7 o 6 o d l b s 1 / b r o w s e r / f a 4 d 3 7 6 d - 5 7 4 5 - 4 9 1 1 - 8 7 e a - 8 0 9 a 0 7 f c 9 0 e f "

根本原因

  • DevToolsActivePort 文件中的 WebSocket URL 可能已过期
  • 或者该 URL 有额外的访问限制
  • /json/version 返回的 URL 是当前有效的

解决方案:修改 cdp-proxy.mjs,优先使用 /json/version 获取 WebSocket URL:

// 修改后的 discoverChromePort 函数
async function discoverChromePort() {
  const commonPorts = [9222, 9229, 9333];
  
  for (const port of commonPorts) {
    const ok = await checkPort(port);
    if (ok) {
      // 优先从 /json/version 获取 WebSocket URL
      try {
        const http = await import('node:http');
        const wsUrl = await new Promise((resolve, reject) => {
          const req = http.get(`http://127.0.0.1:${port}/json/version`, { timeout: 2000 }, (res) => {
            let data = '';
            res.on('data', chunk => data += chunk);
            res.on('end', () => {
              try {
                const json = JSON.parse(data);
                if (json.webSocketDebuggerUrl) {
                  resolve(json.webSocketDebuggerUrl);
                } else {
                  reject(new Error('无 webSocketDebuggerUrl'));
                }
              } catch (e) {
                reject(e);
              }
            });
          });
          req.on('error', reject);
          req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
        });
        return { port, wsUrl };
      } catch {
        // /json/version 不可用,回退到 DevToolsActivePort
      }
    }
  }
  // ... 回退逻辑
}

问题 3:Dia 浏览器配置路径未识别

现象

c h r o m e : n o t c o n n e c t e d c h r o m e : / / i n s p e c t / # r e m o t e - d e b u g g i n g

原因:原 skill 只支持标准 Chrome 路径,不支持 Dia 浏览器。

解决方案:修改 check-deps.shcdp-proxy.mjs,添加 Dia 的配置路径:

// macOS 路径列表(Dia 优先)
case 'darwin':
  return [
    path.join(home, 'Library/Application Support/Dia/User Data/DevToolsActivePort'),
    path.join(home, 'Library/Application Support/Google/Chrome/DevToolsActivePort'),
    // ...
  ];

问题 4:opencode 与 Claude Code 路径差异

现象:skill 中的路径指向 ~/.claude/skills/,在 opencode 中不存在。

解决方案:修改所有路径引用:

Claude Code 原路径 opencode 适配路径
~/.claude/skills/web-access/ ~/.config/opencode/skills/web-access/
${CLAUDE_SKILL_DIR} ${OPENCODE_SKILL_DIR} 或直接使用绝对路径

针对 Dia/opencode 的修改清单

1. SKILL.md 修改

- bash ~/.claude/skills/web-access/scripts/check-deps.sh
+ bash ~/.config/opencode/skills/web-access/scripts/check-deps.sh

- Chrome remote-debugging:在 Chrome 地址栏打开 chrome://inspect/#remote-debugging...
+ Dia/Chrome remote-debugging:需要以调试模式启动浏览器:
+   - Dia: /Applications/Dia.app/Contents/MacOS/Dia --remote-debugging-port=9222
+   - Chrome: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

- 用户日常 Chrome 天然携带登录态
+ 用户日常 Dia 浏览器天然携带登录态

- 请在你的 Chrome 中登录 [网站名]
+ 请在你的 Dia 浏览器中登录 [网站名]

- ${CLAUDE_SKILL_DIR}/references/site-patterns/
+ ${OPENCODE_SKILL_DIR}/references/site-patterns/ 或 ~/.config/opencode/skills/web-access/references/site-patterns/

2. cdp-proxy.mjs 修改

修改 1: 添加 Dia 浏览器路径支持

// discoverChromePort 函数中的 possiblePaths
if (platform === 'darwin') {
  possiblePaths.push(
    path.join(home, 'Library/Application Support/Dia/User Data/DevToolsActivePort'),  // 新增
    path.join(home, 'Library/Application Support/Google/Chrome/DevToolsActivePort'),
    // ...
  );
}

修改 2: 优先使用 /json/version 获取 WebSocket URL(核心修改)

// 新增逻辑:先扫描端口,尝试通过 HTTP API 获取有效的 WebSocket URL
for (const port of commonPorts) {
  const ok = await checkPort(port);
  if (ok) {
    try {
      const wsUrl = await fetchWebSocketUrlFromHttp(port);
      if (wsUrl) {
        console.log(`[CDP Proxy] 从 /json/version 获取 WebSocket URL (端口 ${port})`);
        return { port, wsUrl };
      }
    } catch {}
  }
}

修改 3: 更新错误提示信息

throw new Error(
  'Dia/Chrome 未开启远程调试端口。请在浏览器地址栏打开 chrome://inspect/#remote-debugging 并勾选 Allow remote debugging。\n' +
  '  或用以下方式启动浏览器:\n' +
  '  macOS: /Applications/Dia.app/Contents/MacOS/Dia --remote-debugging-port=9222\n' +
  '  或: /Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-debugging-port=9222\n' +
  '  Linux: google-chrome --remote-debugging-port=9222'
);

3. check-deps.sh 修改

修改 1: 添加 Dia 浏览器路径

case 'darwin':
  return [
    path.join(home, 'Library/Application Support/Dia/User Data/DevToolsActivePort'),  // 新增
    path.join(home, 'Library/Application Support/Google/Chrome/DevToolsActivePort'),
    // ...
  ];

修改 2: 更新提示信息

- echo "chrome: not connected — 请打开 chrome://inspect/#remote-debugging 并勾选 Allow remote debugging"
+ echo "dia/chrome: not connected — 请使用 --remote-debugging-port=9222 参数启动浏览器"
+ echo "  Dia: /Applications/Dia.app/Contents/MacOS/Dia --remote-debugging-port=9222"
+ echo "  Chrome: /Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-debugging-port=9222"

4. references/cdp-api.md 修改

- 启动:node ~/.claude/skills/web-access/scripts/cdp-proxy.mjs &
+ 启动:node ~/.config/opencode/skills/web-access/scripts/cdp-proxy.mjs &

- 重启需 Chrome 重新授权
+ 重启需浏览器重新授权

- `Chrome 未开启远程调试端口` | 提示用户打开 chrome://inspect/#remote-debugging 并勾选 Allow
+ `Dia/Chrome 未开启远程调试端口` | 使用 --remote-debugging-port=9222 参数启动浏览器

API 参考

基础信息

  • 地址: http://localhost:3456
  • 启动: node ~/.config/opencode/skills/web-access/scripts/cdp-proxy.mjs &
  • 停止: pkill -f cdp-proxy.mjs

API 端点

端点 方法 说明
/health GET 健康检查
/targets GET 列出所有页面 tab
/new?url=URL GET 创建新后台 tab
/close?target=ID GET 关闭 tab
/navigate?target=ID&url=URL GET 导航到新 URL
/back?target=ID GET 后退一页
/info?target=ID GET 页面信息
/eval?target=ID POST 执行 JS
/click?target=ID POST JS 点击
/clickAt?target=ID POST 真实鼠标点击
/setFiles?target=ID POST 文件上传
/scroll?target=ID GET 滚动页面
/screenshot?target=ID GET 截图

使用示例

# 列出所有 tab
curl -s http://localhost:3456/targets

# 创建新 tab
curl -s "http://localhost:3456/new?url=https://example.com"
# 返回: {"targetId":"xxx"}

# 执行 JS
curl -s -X POST "http://localhost:3456/eval?target=xxx" -d 'document.title'
# 返回: {"value":"Example Domain"}

# 获取页面内容
curl -s -X POST "http://localhost:3456/eval?target=xxx" -d 'document.body.innerText'

# 点击元素
curl -s -X POST "http://localhost:3456/click?target=xxx" -d 'button.submit'

# 滚动到底部
curl -s "http://localhost:3456/scroll?target=xxx&direction=bottom"

# 截图
curl -s "http://localhost:3456/screenshot?target=xxx&file=/tmp/shot.png"

# 关闭 tab
curl -s "http://localhost:3456/close?target=xxx"

使用示例

示例 1:搜索信息

: T y p e S c r i p t 5 . 0

Agent 会:

  1. 使用 WebSearch 搜索相关信息
  2. 如果需要更详细内容,使用 CDP 打开官方文档页面
  3. 提取并总结关键信息

示例 2:访问需要登录的网站

: x x x

Agent 会:

  1. 检测到小红书是反爬平台
  2. 使用 CDP 模式连接用户浏览器(天然携带登录态)
  3. 在后台 tab 中打开小红书并搜索
  4. 提取搜索结果

示例 3:并行调研

: 5

Agent 会:

  1. 创建 5 个子 Agent 并行执行
  2. 每个子 Agent 独立访问一个官网
  3. 主 Agent 汇总结果并生成对比报告

最佳实践

1. 浏览哲学

像人一样思考:带着目标进入,边看边判断,遇到阻碍就解决。

  • 拿到请求:明确用户要做什么,定义成功标准
  • 选择起点:根据任务性质选择最可能直达的方式
  • 过程校验:每一步的结果都是证据,方向错了立即调整
  • 完成判断:对照成功标准确认完成,不过度操作

2. 工具选择

  • 一手信息优于二手信息
  • 搜索引擎是定位工具,不是证明工具
  • 不确定时先查官方文档

3. CDP 操作

  • 不主动操作用户已有 tab
  • 所有操作在后台 tab 中进行
  • 完成后关闭自己创建的 tab
  • 先 eval 了解页面结构,再决定下一步动作

4. 错误处理

  • 平台返回的"内容不存在"可能是访问方式问题
  • 程序化方式受阻时,GUI 交互是可靠的兜底
  • 站点自己生成的链接比手动构造的 URL 更可靠

文件结构

~ / . c o S s r n K c e f I r f i L i e g L p c c r c s / . t d h e d i o m s p e n p t p d / - c c - e e C p W H k N P e a - x w n D r e T - o r s p p i e c P o b T d d o / i a a i o x S P e e x . t o b d y o p . y m t h o e . c A s j d e o . / m k P . s r n m s j e I s n g d k s t h s s i / h l u l . s D m / i d w a e b - a c # # # # # # # c # e C C s D D s P P / s A k P i I l l D 2 i 0 a K 1 B 5 K B

常见问题

Q: CDP Proxy 无法连接浏览器?

  1. 确认浏览器启动方式:必须使用 --remote-debugging-port=9222
    /Applications/Dia.app/Contents/MacOS/Dia --remote-debugging-port=9222
    
  2. 检查端口是否被占用:lsof -i :9222
  3. 查看 proxy 日志:cat /tmp/cdp-proxy.log
  4. 验证 HTTP 端点:curl http://127.0.0.1:9222/json/version

Q: 为什么 chrome://inspect 方式不工作?

Dia/Arc 等基于 Arc 架构的浏览器有安全限制,只允许来自 chrome://inspect 的 WebSocket 连接。必须使用命令行参数启动。

Q: 执行 JS 返回空值?

  1. 确认 targetId 正确
  2. 检查页面是否加载完成
  3. 尝试用 JSON.stringify() 包裹返回值

Q: 截图失败?

  1. 确认有写入目标文件的权限
  2. 检查磁盘空间
  3. 尝试不指定 file 参数,直接返回二进制

Q: 如何添加新的浏览器支持?

check-deps.shcdp-proxy.mjs 中的 activePortFiles() / possiblePaths 添加新浏览器的配置路径。


参考资料


更新日志

日期 版本 更新内容
2026-03-26 2.4.0-opencode-dia 适配 opencode + Dia 浏览器,解决 WebSocket 403 问题

文档生成时间: 2026-03-26