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 when a user asks to automatically generate a CLI for a website (e.g. "帮我生成 xxx.com 的 cli"). Covers automated generation, API discovery workflow, authentication strategy selection, TS adapter writing, and testing.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "opencli-explorer" with this command: npx skills add jackwener/opencli/jackwener-opencli-opencli-explorer

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 是懒加载的——字幕、评论、关注列表等深层数据只有点击后才触发。

浏览器探索工作流

步骤命令做什么
0. 打开页面opencli browser open <url>导航到目标页面,开始捕获
1. 观察元素opencli browser state查看可交互元素(按钮/标签),带 [N] 索引
2. 首次抓包opencli browser network列出捕获的 JSON API 请求
3. 模拟交互opencli browser click <N>点击按钮触发懒加载 API
4. 二次抓包opencli browser network找出新触发的 API
5. 查看响应opencli browser network --detail <N>查看完整响应体
6. 验证 APIopencli 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}&pn=1&ps=24
opencli browser network --detail 0
# 确认数据结构:{ code: 0, data: { total: 1342, list: [{mid, uname, ...}] } }
opencli browser eval "fetch('/x/relation/followings?vmid=137702077&pn=1&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

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

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

框架检测

opencli browser eval "(()=>{
  const vue3 = !!document.querySelector('#app')?.__vue_app__;
  const pinia = vue3 && !!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策略速度适用场景实例
1public⚡ ~1s公开 API,无需登录Hacker News, V2EX
2cookie🔄 ~7sCookie 认证即可Bilibili, Zhihu, Reddit
2.5localStorage Bearer🔄 ~7sJWT 存 localStorage,API 在独立 domainSlock, Linear, Notion
3header🔄 ~7s需要 CSRF token 或 BearerTwitter GraphQL
4intercept🔄 ~10s请求有复杂签名小红书 (Pinia + XHR)
5ui🐌 ~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: 测试

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

<!-- keep in sync with oneshot SKILL.md §测试 -->

两种开发场景

  • 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 && opencli mysite mycommand --limit 3   # 最终验证(Repo 贡献场景)
git add clis/mysite/ && git commit -m "feat(mysite): add mycommand" && git push

常见陷阱

陷阱表现解决方案
缺少 navigateTarget page context 错误在 evaluate 前加 page.goto()
缺少 strategy: public公开 API 也启动浏览器strategy: Strategy.PUBLIC + browser: false
风控被拦截(伪 200)JSON 里核心数据是空串必须断言!throw new AuthRequiredError(domain) 提示重新登录
SPA 返回 HTMLfetch('/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.mdTier 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/ 并微调。

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

opencli-operate

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

smart-search

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

opencli-usage

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

opencli-oneshot

No summary provided by upstream source.

Repository SourceNeeds Review