| name | opencli |
| description | OpenCLI — Make any website or Electron App your CLI. Zero risk, AI-powered, reuse Chrome login. |
| version | 1.3.1 |
| author | jackwener |
| tags | ["cli","browser","web","chrome-extension","cdp","bilibili","zhihu","twitter","github","v2ex","hackernews","reddit","xiaohongshu","xueqiu","youtube","boss","coupang","yollomi","AI","agent"] |
OpenCLI
Make any website or Electron App your CLI. Reuse Chrome login, zero risk, AI-powered discovery.
[!CAUTION]
AI Agent 必读:创建或修改任何适配器之前,你必须先阅读 CLI-EXPLORER.md!
该文档包含完整的 API 发现工作流(必须使用浏览器探索)、5 级认证策略决策树、平台 SDK 速查表、tap 步骤调试流程、分页 API 模板、级联请求模式、以及常见陷阱。
本文件(SKILL.md)仅提供命令参考和简化模板,不足以正确开发适配器。
Install & Run
npm install -g @jackwener/opencli
opencli <command>
cd ~/code/opencli && npm install
npx tsx src/main.ts <command>
npm update -g @jackwener/opencli
Prerequisites
Browser commands require:
- Chrome browser running (logged into target sites)
- opencli Browser Bridge Chrome extension installed (load
extension/ as unpacked in chrome://extensions)
- No further setup needed — the daemon auto-starts on first browser command
Note: You must be logged into the target website in Chrome before running commands. Tabs opened during command execution are auto-closed afterwards.
Public API commands (hackernews, v2ex) need no browser.
Commands Reference
Data Commands
opencli bilibili hot --limit 10
opencli bilibili search "rust"
opencli bilibili me
opencli bilibili favorite
opencli bilibili history --limit 20
opencli bilibili feed --limit 10
opencli bilibili user-videos --uid 12345
opencli bilibili subtitle --bvid BV1xxx
opencli bilibili dynamic --limit 10
opencli bilibili ranking --limit 10
opencli bilibili following --limit 20
opencli zhihu hot --limit 10
opencli zhihu search "AI"
opencli zhihu question 34816524
opencli xiaohongshu search "美食"
opencli xiaohongshu notifications
opencli xiaohongshu feed --limit 10
opencli xiaohongshu user xxx
opencli xiaohongshu creator-notes --limit 10
opencli xiaohongshu creator-note-detail --note-id xxx
opencli xiaohongshu creator-notes-summary
opencli xiaohongshu creator-profile
opencli xiaohongshu creator-stats
opencli xueqiu hot-stock --limit 10
opencli xueqiu stock --symbol SH600519
opencli xueqiu watchlist
opencli xueqiu feed
opencli xueqiu hot --limit 10
opencli xueqiu search "特斯拉"
opencli gh repo list
opencli gh pr list --limit 5
opencli gh issue list
opencli twitter trending --limit 10
opencli twitter bookmarks --limit 20
opencli twitter search "AI"
opencli twitter profile elonmusk
opencli twitter timeline --limit 20
opencli twitter thread 1234567890
opencli twitter article 1891511252174299446
opencli twitter follow elonmusk
opencli twitter unfollow elonmusk
opencli twitter bookmark https://x.com/...
opencli twitter unbookmark https://x.com/...
opencli reddit hot --limit 10
opencli reddit hot --subreddit programming
opencli reddit frontpage --limit 10
opencli reddit popular --limit 10
opencli reddit search "AI" --sort top --time week
opencli reddit subreddit rust --sort top --time month
opencli reddit read --post-id 1abc123
opencli reddit user spez
opencli reddit user-posts spez
opencli reddit user-comments spez
opencli reddit upvote --post-id xxx --direction up
opencli reddit save --post-id xxx
opencli reddit comment --post-id xxx "Great!"
opencli reddit subscribe --subreddit python
opencli reddit saved --limit 10
opencli reddit upvoted --limit 10
opencli v2ex hot --limit 10
opencli v2ex latest --limit 10
opencli v2ex topic 1024
opencli v2ex daily
opencli v2ex me
opencli v2ex notifications --limit 10
opencli hackernews top --limit 10
opencli bbc news --limit 10
opencli weibo hot --limit 10
opencli boss search "AI agent"
opencli boss detail --security-id xxx
opencli boss recommend --limit 10
opencli boss joblist --limit 10
opencli boss greet --security-id xxx
opencli boss batchgreet --job-id xxx
opencli boss send --uid xxx "消息内容"
opencli boss chatlist --limit 10
opencli boss chatmsg --security-id xxx
opencli boss invite --security-id xxx
opencli boss mark --security-id xxx
opencli boss exchange --security-id xxx
opencli boss resume
opencli boss stats
opencli youtube search "rust"
opencli youtube video "https://www.youtube.com/watch?v=xxx"
opencli youtube transcript "https://www.youtube.com/watch?v=xxx"
opencli youtube transcript "xxx" --lang zh-Hans --mode raw
opencli yahoo-finance quote --symbol AAPL
opencli sinafinance news --limit 10 --type 1
opencli reuters search "AI"
opencli smzdm search "耳机"
opencli ctrip search "三亚"
opencli antigravity status
opencli antigravity send "hello"
opencli antigravity read
opencli antigravity new
opencli antigravity dump
opencli antigravity extract-code
opencli antigravity model claude
opencli antigravity watch
opencli barchart quote --symbol AAPL
opencli barchart options --symbol AAPL
opencli barchart greeks --symbol AAPL
opencli barchart flow --limit 20
opencli jike feed --limit 10
opencli jike search "AI"
opencli jike create "内容"
opencli jike like xxx
opencli jike comment xxx "评论"
opencli jike repost xxx
opencli jike notifications
opencli linux-do hot --limit 10
opencli linux-do latest --limit 10
opencli linux-do search "rust"
opencli linux-do topic 1024
opencli stackoverflow hot --limit 10
opencli stackoverflow search "typescript"
opencli stackoverflow bounties --limit 10
opencli weread shelf --limit 10
opencli weread search "AI"
opencli weread book xxx
opencli weread highlights xxx
opencli weread notes xxx
opencli weread ranking --limit 10
opencli jimeng generate --prompt "描述"
opencli jimeng history --limit 10
opencli yollomi models --type image
opencli yollomi generate "提示词" --model z-image-turbo
opencli yollomi video "提示词" --model kling-2-1
opencli yollomi upload ./photo.jpg
opencli yollomi remove-bg <image-url>
opencli yollomi edit <image-url> "改成油画风格"
opencli grok ask --prompt "问题"
opencli grok ask --prompt "问题" --web
opencli hf top --limit 10
opencli chaoxing assignments
opencli chaoxing exams
opencli douban search "三体"
opencli douban top250
opencli douban subject 1234567
opencli douban marks --limit 10
opencli douban reviews --limit 10
opencli facebook feed --limit 10
opencli facebook profile username
opencli facebook search "AI"
opencli facebook friends
opencli facebook groups
opencli facebook events
opencli facebook notifications
opencli facebook memories
opencli facebook add-friend username
opencli facebook join-group groupid
opencli instagram explore
opencli instagram profile username
opencli instagram search "AI"
opencli instagram user username
opencli instagram followers username
opencli instagram following username
opencli instagram follow username
opencli instagram unfollow username
opencli instagram like postid
opencli instagram unlike postid
opencli instagram comment postid "评论"
opencli instagram save postid
opencli instagram unsave postid
opencli instagram saved
opencli tiktok explore
opencli tiktok search "AI"
opencli tiktok profile username
opencli tiktok user username
opencli tiktok following username
opencli tiktok follow username
opencli tiktok unfollow username
opencli tiktok like videoid
opencli tiktok unlike videoid
opencli tiktok comment videoid "评论"
opencli tiktok save videoid
opencli tiktok unsave videoid
opencli tiktok live
opencli tiktok notifications
opencli tiktok friends
opencli medium feed --limit 10
opencli medium search "AI"
opencli medium user username
opencli substack feed --limit 10
opencli substack search "AI"
opencli substack publication name
opencli sinablog hot --limit 10
opencli sinablog search "AI"
opencli sinablog article url
opencli sinablog user username
opencli lobsters hot --limit 10
opencli lobsters newest --limit 10
opencli lobsters active --limit 10
opencli lobsters tag rust
opencli google news --limit 10
opencli google search "AI"
opencli google suggest "AI"
opencli google trends
opencli devto top --limit 10
opencli devto tag javascript --limit 10
opencli devto user username
opencli steam top-sellers --limit 10
opencli wikipedia search "AI"
opencli wikipedia summary "Python"
Desktop Adapter Commands
opencli cursor status
opencli cursor send "message"
opencli cursor read
opencli cursor new
opencli cursor dump
opencli cursor composer
opencli cursor model claude
opencli cursor extract-code
opencli cursor ask "question"
opencli cursor screenshot
opencli cursor history
opencli cursor export
opencli codex status
opencli codex send "message"
opencli codex read
opencli codex new
opencli codex dump
opencli codex extract-diff
opencli codex model gpt-4
opencli codex ask "question"
opencli codex screenshot
opencli codex history
opencli codex export
opencli chatgpt status
opencli chatgpt new
opencli chatgpt send "message"
opencli chatgpt read
opencli chatgpt ask "question"
opencli chatwise status
opencli chatwise new
opencli chatwise send "message"
opencli chatwise read
opencli chatwise ask "question"
opencli chatwise model claude
opencli chatwise history
opencli chatwise export
opencli chatwise screenshot
opencli notion status
opencli notion search "keyword"
opencli notion read
opencli notion new
opencli notion write "content"
opencli notion sidebar
opencli notion favorites
opencli notion export
opencli discord-app status
opencli discord-app send "message"
opencli discord-app read
opencli discord-app channels
opencli discord-app servers
opencli discord-app search "keyword"
opencli discord-app members
opencli doubao-app status
opencli doubao-app new
opencli doubao-app send "message"
opencli doubao-app read
opencli doubao-app ask "question"
opencli doubao-app screenshot
opencli doubao-app dump
Management Commands
opencli list
opencli list --json
opencli list -f yaml
opencli install <name>
opencli register <name>
opencli validate
opencli validate bilibili
opencli doctor
AI Agent Workflow
opencli explore <url> --site <name>
opencli synthesize <site>
opencli generate <url> --goal "hot"
opencli record <url>
opencli record <url> --site mysite
opencli record <url> --timeout 120000
opencli record <url> --poll 1000
opencli record <url> --out .opencli/record/x
opencli cascade <api-url>
opencli explore <url> --auto --click "字幕,CC,评论"
opencli validate
Output Formats
All built-in commands support --format / -f with table, json, yaml, md, and csv.
The list command supports the same formats and also keeps --json as a compatibility alias.
opencli list -f yaml
opencli bilibili hot -f table
opencli bilibili hot -f json
opencli bilibili hot -f yaml
opencli bilibili hot -f md
opencli bilibili hot -f csv
Verbose Mode
opencli bilibili hot -v
Record Workflow
record 是为「无法用 explore 自动发现」的页面(需要登录操作、复杂交互、SPA 内路由)准备的手动录制方案。
工作原理
opencli record <url>
→ 打开 automation window 并导航到目标 URL
→ 向所有 tab 注入 fetch/XHR 拦截器(幂等,可重复注入)
→ 每 2s 轮询一次:发现新 tab 自动注入,drain 所有 tab 的捕获缓冲区
→ 超时(默认 60s)或按 Enter 停止
→ 分析捕获到的 JSON 请求:去重 → 评分 → 生成候选 YAML
拦截器特性:
- 同时 patch
window.fetch 和 XMLHttpRequest
- 只捕获
Content-Type: application/json 的响应
- 过滤纯对象少于 2 个 key 的响应(避免 tracking/ping)
- 跨 tab 隔离:每个 tab 独立缓冲区,轮询时分别 drain
- 幂等注入:同一 tab 二次注入时先 restore 原始函数再重新 patch,不丢失已捕获数据
使用步骤
opencli record "https://example.com/page" --timeout 120000
cat .opencli/record/<site>/captured.json
ls .opencli/record/<site>/candidates/
页面类型与捕获预期
| 页面类型 | 预期捕获量 | 说明 |
|---|
| 列表/搜索页 | 多(5~20+) | 每次搜索/翻页都会触发新请求 |
| 详情页(只读) | 少(1~5) | 首屏数据一次性返回,后续操作走 form/redirect |
| SPA 内路由跳转 | 中等 | 路由切换会触发新接口,但首屏请求在注入前已发出 |
| 需要登录的页面 | 视操作而定 | 确保 Chrome 已登录目标网站 |
注意:如果页面在导航完成前就发出了大部分请求(服务端渲染 / SSR 注水),拦截器会错过这些请求。
解决方案:在页面加载完成后,手动触发能产生新请求的操作(搜索、翻页、切 Tab、展开折叠项等)。
候选 YAML → TS CLI 转换
生成的候选 YAML 是起点,通常需要转换为 TypeScript(尤其是 tae 等内部系统):
候选 YAML 结构(自动生成):
site: tae
name: getList
strategy: cookie
browser: true
pipeline:
- navigate: https://...
- evaluate: |
(async () => {
const res = await fetch('/approval/getList.json?procInsId=...', { credentials: 'include' });
const data = await res.json();
return (data?.content?.operatorRecords || []).map(item => ({ ... }));
})()
转换为 TS CLI(参考 src/clis/tae/add-expense.ts 风格):
import { cli, Strategy } from '../../registry.js';
cli({
site: 'tae',
name: 'get-approval',
description: '查看报销单审批流程和操作记录',
domain: 'tae.alibaba-inc.com',
strategy: Strategy.COOKIE,
browser: true,
args: [
{ name: 'proc_ins_id', type: 'string', required: true, positional: true, help: '流程实例 ID(procInsId)' },
],
columns: ['step', 'operator', 'action', 'time'],
func: async (page, kwargs) => {
await page.goto('https://tae.alibaba-inc.com/expense/pc.html?_authType=SAML');
await page.wait(2);
const result = await page.evaluate(`(async () => {
const res = await fetch('/approval/getList.json?taskId=&procInsId=${kwargs.proc_ins_id}', {
credentials: 'include'
});
const data = await res.json();
return data?.content?.operatorRecords || [];
})()`);
return (result as any[]).map((r, i) => ({
step: i + 1,
operator: r.operatorName || r.userId,
action: r.operationType,
time: r.operateTime,
}));
},
});
转换要点:
- URL 中的动态 ID(
procInsId、taskId 等)提取为 args
captured.json 里的真实 body 结构用于确定正确的数据路径(如 content.operatorRecords)
- tae 系统统一用
{ success, content, errorCode, errorMsg } 外层包裹,取数据要走 content.*
- 认证方式:cookie(
credentials: 'include'),不需要额外 header
- 文件放入
src/clis/<site>/,无需手动注册,npm run build 后自动发现
故障排查
| 现象 | 原因 | 解法 |
|---|
| 捕获 0 条请求 | 拦截器注入失败,或页面无 JSON API | 检查 daemon 是否运行:curl localhost:19825/status |
| 捕获量少(1~3 条) | 页面是只读详情页,首屏数据已在注入前发出 | 手动操作触发更多请求(搜索/翻页),或换用列表页 |
| 候选 YAML 为 0 | 捕获到的 JSON 都没有 array 结构 | 直接看 captured.json 手写 TS CLI |
| 新开的 tab 没有被拦截 | 轮询间隔内 tab 已关闭 | 缩短 --poll 500 |
| 二次运行 record 时数据不连续 | 正常,每次 record 启动都是新的 automation window | 无需处理 |
Creating Adapters
[!TIP]
快速模式:如果你只想为一个具体页面生成一个命令,直接看 CLI-ONESHOT.md。
只需要一个 URL + 一句话描述,4 步搞定。
[!IMPORTANT]
完整模式 — 在写任何代码之前,先阅读 CLI-EXPLORER.md。
它包含:① AI Agent 浏览器探索工作流 ② 认证策略决策树 ③ 平台 SDK(如 Bilibili 的 apiGet/fetchJson)④ YAML vs TS 选择指南 ⑤ tap 步骤调试方法 ⑥ 级联请求模板 ⑦ 常见陷阱表。
下方仅为简化模板参考,直接使用极易踩坑。
YAML Pipeline (declarative, recommended)
Create src/clis/<site>/<name>.yaml:
site: mysite
name: hot
description: Hot topics
domain: www.mysite.com
strategy: cookie
browser: true
args:
limit:
type: int
default: 20
description: Number of items
pipeline:
- navigate: https://www.mysite.com
- evaluate: |
(async () => {
const res = await fetch('/api/hot', { credentials: 'include' });
const d = await res.json();
return d.data.items.map(item => ({
title: item.title,
score: item.score,
}));
})()
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
score: ${{ item.score }}
- limit: ${{ args.limit }}
columns: [rank, title, score]
For public APIs (no browser):
strategy: public
browser: false
pipeline:
- fetch:
url: https://api.example.com/hot.json
- select: data.items
- map:
title: ${{ item.title }}
- limit: ${{ args.limit }}
TypeScript Adapter (programmatic)
Create src/clis/<site>/<name>.ts. It will be automatically dynamically loaded (DO NOT manually import it in index.ts):
import { cli, Strategy } from '../../registry.js';
cli({
site: 'mysite',
name: 'search',
strategy: Strategy.INTERCEPT,
args: [{ name: 'query', required: true, positional: true }],
columns: ['rank', 'title', 'url'],
func: async (page, kwargs) => {
await page.goto('https://www.mysite.com/search');
await page.installInterceptor('/api/search');
await page.autoScroll({ times: 3, delayMs: 2000 });
const requests = await page.getInterceptedRequests();
let results = [];
for (const req of requests) {
results.push(...req.data.items);
}
return results.map((item, i) => ({
rank: i + 1, title: item.title, url: item.url,
}));
},
});
When to use TS: XHR interception (page.installInterceptor), infinite scrolling (page.autoScroll), cookie extraction, complex data transforms (like GraphQL unwrapping).
Pipeline Steps
| Step | Description | Example |
|---|
navigate | Go to URL | navigate: https://example.com |
fetch | HTTP request (browser cookies) | fetch: { url: "...", params: { q: "..." } } |
evaluate | Run JavaScript in page | evaluate: | (async () => { ... })() |
select | Extract JSON path | select: data.items |
map | Map fields | map: { title: "${{ item.title }}" } |
filter | Filter items | filter: item.score > 100 |
sort | Sort items | sort: { by: score, order: desc } |
limit | Cap result count | limit: ${{ args.limit }} |
intercept | Declarative XHR capture | intercept: { trigger: "navigate:...", capture: "api/hot" } |
tap | Store action + XHR capture | tap: { store: "feed", action: "fetchFeeds", capture: "homefeed" } |
snapshot | Page accessibility tree | snapshot: { interactive: true } |
click | Click element | click: ${{ ref }} |
type | Type text | type: { ref: "@1", text: "hello" } |
wait | Wait for time/text | wait: 2 or wait: { text: "loaded" } |
press | Press key | press: Enter |
Template Syntax
${{ args.query }}
${{ args.limit | default(20) }}
${{ item.title }}
${{ item.data.nested.field }}
${{ index }}
${{ index + 1 }}
5-Tier Authentication Strategy
| Tier | Name | Method | Example |
|---|
| 1 | public | No auth, Node.js fetch | Hacker News, V2EX |
| 2 | cookie | Browser fetch with credentials: include | Bilibili, Zhihu |
| 3 | header | Custom headers (ct0, Bearer) | Twitter GraphQL |
| 4 | intercept | XHR interception + store mutation | 小红书 Pinia |
| 5 | ui | Full UI automation (click/type/scroll) | Last resort |
Environment Variables
| Variable | Default | Description |
|---|
OPENCLI_DAEMON_PORT | 19825 | Daemon listen port |
OPENCLI_BROWSER_CONNECT_TIMEOUT | 30 | Browser connection timeout (sec) |
OPENCLI_BROWSER_COMMAND_TIMEOUT | 45 | Command execution timeout (sec) |
OPENCLI_BROWSER_EXPLORE_TIMEOUT | 120 | Explore timeout (sec) |
OPENCLI_VERBOSE | — | Show daemon/extension logs |
Troubleshooting
| Issue | Solution |
|---|
npx not found | Install Node.js: brew install node |
Extension not connected | 1) Chrome must be open 2) Install opencli Browser Bridge extension |
Target page context error | Add navigate: step before evaluate: in YAML |
| Empty table data | Check if evaluate returns correct data path |
| Daemon issues | curl localhost:19825/status to check, curl localhost:19825/logs for extension logs |