opencli-explorer

Use when creating a new OpenCLI adapter from scratch, adding support for a new website or platform, exploring a site's API endpoints via browser DevTools, or…

INSTALLATION
npx skills add https://github.com/jackwener/opencli --skill opencli-explorer
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

CLI-EXPLORER — 适配器探索式开发完全指南

从零到发布:API 发现 → 认证策略 → 写适配器 → 测试验证。

先选路径

情况

走这里

只要为一个具体页面生成一个命令

opencli-oneshot skill

想先让机器自动试一遍

opencli generate <url> [--goal <goal>],失败再回来

新站点 / 多个命令 / oneshot 卡住了

继续往下读本文档

产物要提 PR

本文档 + clis/<site>/ + npm run build

只是本地私用,不提 PR

本文档 + ~/.opencli/clis/<site>/

核心流程

┌─────────────┐     ┌─────────────┐     ┌──────────────┐     ┌────────┐

 │ 1. 发现 API  │ ──▶ │ 2. 选择策略  │ ──▶ │ 3. 写适配器   │ ──▶ │ 4. 测试 │

 └─────────────┘     └─────────────┘     └──────────────┘     └────────┘

   browser explore     cascade             TS cli() API         verify

AI Agent 必读:必须用浏览器探索

[!CAUTION]

必须通过浏览器打开目标网站去探索! 不要只靠静态分析。

很多 API 是懒加载的——字幕、评论、关注列表等深层数据只有点击后才触发。

浏览器探索工作流

步骤

命令

做什么

  1. 打开页面

opencli browser open <url>

导航到目标页面,开始捕获

  1. 观察元素

opencli browser state

查看可交互元素(按钮/标签),带 [N] 索引

  1. 首次抓包

opencli browser network

列出捕获的 JSON API 请求

  1. 模拟交互

opencli browser click <N>

点击按钮触发懒加载 API

  1. 二次抓包

opencli browser network

找出新触发的 API

  1. 查看响应

opencli browser network --detail <N>

查看完整响应体

  1. 验证 API

opencli browser eval "fetch(...).then(r=>r.json())"

确认 API 可复现

常犯错误

❌ 错误做法

✅ 正确做法

只用 opencli explore,等结果出来

opencli browser open 主动浏览

不看浏览器请求,直接写代码

opencli browser network 确认 API

打开页面后直接抓包

opencli browser click 模拟交互触发懒加载

HTTP 200 但数据为空就放弃

检查是否需要签名或 Cookie 鉴权(伪 200 风控)

依赖 __INITIAL_STATE__ 拿所有数据

__INITIAL_STATE__ 只有首屏数据,深层要调 API

opencli browser network 为空

重新 open 刷新捕获;或检查 API 是否在独立 domain

实战示例:5 分钟实现「关注列表」适配器

opencli browser open https://space.bilibili.com/{uid}/fans/follow

opencli browser network

#   [0] GET 200 /x/relation/followings?vmid={uid}&#x26;pn=1&#x26;ps=24

opencli browser network --detail 0

# 确认数据结构:{ code: 0, data: { total: 1342, list: [{mid, uname, ...}] } }

opencli browser eval "fetch('/x/relation/followings?vmid=137702077&#x26;pn=1&#x26;ps=5', {credentials:'include'}).then(r=>r.json())"

# → 有数据,结论:Tier 2 Cookie,写 following.js

Step 1: 发现 API

主路径:浏览器主动探索

用上方工作流打开页面 → 模拟交互 → 抓包。关注:

  • URL pattern/api/v2/hot?limit=20 → 要调用的端点
  • Method:GET / POST
  • Request Headers:Cookie? Bearer? 自定义签名头(X-s、X-t)?
  • Response Body:数据路径(data.itemsdata.list

高阶捷径(按优先级尝试)

  • **后缀爆破法 (.json)**:Reddit、雪球等,URL 加 .json 直接拿 REST 数据(Tier 2 秒杀)
  • **全局状态法 (__INITIAL_STATE__)**:SSR 站点(B站、小红书)首页数据挂载在 window 上
  • 主动交互触发法:懒加载 API 需要点击按钮("CC"、"展开全部")才触发
  • 框架 Store 截断:Vue + Pinia 站点,Store Action 代替你完成签名
  • XHR/Fetch 拦截:最后手段,用 installInterceptor 抓包

框架检测

opencli browser eval "(()=>{

  const vue3 = !!document.querySelector('#app')?.__vue_app__;

  const pinia = vue3 &#x26;&#x26; !!document.querySelector('#app').__vue_app__.config.globalProperties.\$pinia;

  const react = !!window.__REACT_DEVTOOLS_GLOBAL_HOOK__;

  return JSON.stringify({vue3, pinia, react});

})()"

Vue + Pinia → 可用 Store Action 绕过签名(Tier 4)。

Step 2: 选择认证策略

opencli cascade https://api.example.com/hot   # 自动探测

策略决策树

fetch(url) 直接能拿到?

  → ✅ Tier 1: public(browser: false,~1s)

  → ❌ fetch(url, {credentials:'include'}) 带 Cookie 能拿到?

       → ✅ Tier 2: cookie(最常见)

       → ❌ localStorage 有 token,Bearer header 能拿到?

              → ✅ Tier 2.5: localStorage Bearer(现代 SaaS 主流)

                  带了 Bearer 但 400 "Missing X-Xxx header"?

                  → 先调 /servers 或 /workspaces 拿业务上下文 ID

              → ❌ 加 CSRF header 后能拿到?

                     → ✅ Tier 3: header(如 Twitter ct0 + Bearer)

                     → ❌ 网站有 Pinia/Vuex Store?

                            → ✅ Tier 4: intercept(Store Action + XHR 拦截)

                            → ❌ Tier 5: ui(UI 自动化,最后手段)

策略对比

Tier

策略

速度

适用场景

实例

1

public

⚡ ~1s

公开 API,无需登录

Hacker News, V2EX

2

cookie

🔄 ~7s

Cookie 认证即可

Bilibili, Zhihu, Reddit

2.5

localStorage Bearer

🔄 ~7s

JWT 存 localStorage,API 在独立 domain

Slock, Linear, Notion

3

header

🔄 ~7s

需要 CSRF token 或 Bearer

Twitter GraphQL

4

intercept

🔄 ~10s

请求有复杂签名

小红书 (Pinia + XHR)

5

ui

🐌 ~15s+

无 API,纯 DOM 解析

遗留网站

Step 2.5: 准备工作

先复用现有适配器,不要从零开始:

ls clis/<site>/             # 看同站点已有什么

cat clis/<site>/feed.js     # 读最相似的那个

改 3 处即可:name、API URL、字段映射。

Bilibili 平台 SDKclis/bilibili/utils.js):

  • fetchJson(page, url) → 普通 Cookie-tier API
  • apiGet(page, path, {signed, params}) → URL 含 /wbi/ 的接口(自动签名)
  • getSelfUid(page) / resolveUid(page, input) → 用户 UID 处理

Step 3: 编写适配器

所有适配器统一使用 cli() API,放入 clis/<site>/<name>.js 即自动注册。

完整模板(Tier 1~4)、分页模式、错误处理规范(throw CliError + YAML envelope)→ adapter-templates.md

最简结构(Tier 2 Cookie):

import { cli, Strategy } from '@jackwener/opencli/registry';

cli({

  site: 'mysite',

  name: 'mycommand',

  description: '一句话描述',

  domain: 'www.example.com',

  strategy: Strategy.COOKIE,

  browser: true,

  args: [{ name: 'limit', type: 'int', default: 20 }],

  columns: ['rank', 'title', 'value'],

  func: async (page, kwargs) => {

    await page.goto('https://www.example.com');

    const data = await page.evaluate(`(async () => {

      const res = await fetch('/api/items', { credentials: 'include' });

      const d = await res.json();

      return d.data?.items || [];

    })()`);

    return (data as any[]).slice(0, kwargs.limit).map((item, i) => ({

      rank: i + 1,

      title: item.title || '',

      value: item.value || '',

    }));

  },

});

级联请求、tap 调试、抗变更模式 → advanced-patterns.md

Step 4: 测试

构建通过 ≠ 功能正常。必须实际运行并确认输出。

两种开发场景

  • Repo 贡献:文件放 clis/<site>/<name>.jsnpm run build 后自动注册
  • 私人 adapter(本地使用,无需提 PR):文件放 ~/.opencli/clis/<site>/<name>.js,无需 build
# Repo 贡献:build 后直接运行

npm run build

opencli list | grep mysite                 # 确认注册

opencli mysite mycommand --limit 3 -v      # 实际运行

# 私人 adapter(~/.opencli/clis/):一键验证

opencli browser verify <site>/<name>

Done 标准:命令运行后返回非空表格,且字段符合预期。

Step 5: 提交发布

npm run build &#x26;&#x26; opencli mysite mycommand --limit 3   # 最终验证(Repo 贡献场景)

git add clis/mysite/ &#x26;&#x26; git commit -m "feat(mysite): add mycommand" &#x26;&#x26; git push

常见陷阱

陷阱

表现

解决方案

缺少 navigate

Target page context 错误

在 evaluate 前加 page.goto()

缺少 strategy: public

公开 API 也启动浏览器

strategy: Strategy.PUBLIC + browser: false

风控被拦截(伪 200)

JSON 里核心数据是空串

必须断言!throw new AuthRequiredError(domain) 提示重新登录

SPA 返回 HTML

fetch('/api/xxx') 返回 <!DOCTYPE html>

页面 host 是 app.xxx.com,真实 API 在 api.xxx.com;搜 JS bundle 找 baseURL

400 缺少上下文 Header

带了 Bearer 仍然 400,报 Missing X-Server-Id

先调 /servers 拿业务上下文 ID,加进 headers

文件写错目录

opencli list 找不到命令

Repo 贡献放 clis/<site>/ + build;私人 adapter 放 ~/.opencli/clis/<site>/

TS evaluate 格式

() => {}result is not a function

必须用 IIFE:(async () => { ... })()

evaluate 内嵌大段 JS

字符串转义问题,调试困难

逻辑放在 func() 内用原生 TS 编写

页面异步加载

evaluate 拿到空数据

evaluate 内用 polling 等待,或增加 wait 时间

Cookie 过期

返回 401 / 空数据

在浏览器里重新登录目标站点

更多参考

文档

内容

adapter-templates.md

Tier 1~4 完整模板、分页模式、错误处理规范

advanced-patterns.md

级联请求、tap 调试、Verbose 模式、抗变更模式

record-workflow.md

手动录制方案(适用于复杂交互页面)

opencli-oneshot skill

单点快速生成(只需一个 URL + 目标描述)

用 AI Agent 自动生成

# 一键:探索 → 分析 → 合成 → 注册

opencli generate https://www.example.com --goal "hot"

# 或分步:

opencli explore https://www.example.com --site mysite

opencli synthesize mysite

opencli verify mysite/hot --smoke

生成的候选 TS 保存在 .opencli/explore/mysite/candidates/,复制到 clis/mysite/ 并微调。

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card