
Opencli
- 372 installs
- 27.3k repo stars
- Updated July 20, 2026
- jackwener/opencli
opencli is an agent skill that helps build or extend command-line interfaces and agent-callable CLI workflows for developers who need to automate local tasks, script integrations, or expose tools to coding agents.
About
opencli is an agent skill from jackwener/opencli for designing, building, and extending command-line interfaces that coding agents can call during automation workflows. The skill guides developers through CLI structure, argument parsing, subcommands, and agent-invokable entry points when scripting local tasks or wiring integrations without a web API layer. Developers reach for opencli when they need a reproducible terminal tool—whether for human operators or agent sessions—that wraps scripts, APIs, or filesystem operations behind a clean command surface.
- CLI scaffolding and command patterns
- Agent-callable terminal workflows
- Integration-friendly command design
- Automation for dev and ops tasks
- Reusable open CLI conventions
Opencli by the numbers
- 372 all-time installs (skills.sh)
- Ranked #140 of 560 CLI & Terminal skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jackwener/opencli --skill opencliAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 372 |
|---|---|
| repo stars | ★ 27.3k |
| Last updated | July 20, 2026 |
| Repository | jackwener/opencli ↗ |
How do you build agent-callable CLI tools?
Build or extend command-line interfaces and agent-callable CLI workflows when automating local tasks, scripting integrations, or exposing tools to coding agents.
Who is it for?
Developers exposing local automation, scripts, or integrations as terminal commands that both humans and coding agents can invoke.
Skip if: Teams building only REST APIs or GUI-only products with no terminal automation needs should skip opencli.
When should I use this skill?
A developer wants to create a CLI, add agent-callable subcommands, or wrap local scripts as a structured command-line tool.
What you get
A working CLI with parsed arguments, subcommands, and agent-invokable entry points for local automation workflows.
- CLI executable or script
- Agent-callable command entry points
Files
Cross-Project Adapter Migration
从外部 CLI 项目(Python/Node/Go 等)批量迁移命令到 opencli 的标准化流程。
When to Use
- 用户说"把 xxx-cli 的命令迁移过来"
- 用户说"看看 xxx 项目有什么可以借鉴的"
- 用户说"对齐 xxx-cli 的功能"
- 在为新平台扩展 opencli 时,发现已有第三方 CLI 工具
Prerequisites
- 熟悉 CLI-EXPLORER.md(adapter 开发决策树)
- 熟悉 SKILL.md(命令参考 & 模板)
---
Phase 1: 源项目分析
1.1 克隆 & 理解源项目
# 克隆源项目到 /tmp 做分析
git clone <source_repo_url> /tmp/<source-cli>分析重点:
- 命令列表:找到所有可用命令(查看 CLI 入口文件、help 输出或 README)
- 认证方式:Cookie?API Key?OAuth?浏览器自动化?
- 数据源:公开 API?GraphQL?页面抓取?
- 输出字段:每个命令返回哪些数据字段
1.2 生成命令清单
列出源项目所有命令,包括:
| 命令 | 类型 | API/方法 | 输出字段 |
|---|---|---|---|
xxx feed | Read | GET /api/feed | title, author, time |
xxx post | Write | POST /api/tweet | status, id |
---
Phase 2: 功能对比矩阵
2.1 查看 opencli 现有命令
ls src/clis/<site>/ # 查看已有适配器
opencli list | grep <site> # 确认已注册命令2.2 生成对比矩阵
对每个源项目命令,标注三种状态:
| 功能 | 源项目 | opencli 现有 | 行动 |
|---|---|---|---|
| feed | ✅ xxx feed | ❌ 无 | ✅ 新增 |
| search | ✅ xxx search | ✅ search.ts | ❌ 已有,跳过 |
| hot | ✅ xxx hot | ⚠️ hot.yaml(不完整) | ✅ 增强 |
| like | ✅ xxx like | ✅ like.ts | ❌ 已有,跳过 |
2.3 筛选迁移目标
去掉已有的、低价值的,保留高价值缺失命令,按 Read/Write 分类:
筛选原则:
- ✅ 高使用频率的命令优先
- ✅ 已有但不完整的命令标记为"增强"
- ❌ 源项目特有但 opencli 架构不支持的功能(如需要持久化存储的)跳过
- ❌ 与现有功能完全重复的跳过
---
Phase 3: 批量实现
[!IMPORTANT]
实现前必须查阅 CLI-EXPLORER.md 确认策略选择。
3.1 选择实现方式
基于决策树分类:
| 类别 | 方式 | 适用条件 |
|---|---|---|
| Read + 简单 API | YAML pipeline | 纯 fetch/select/map,无复杂 JS |
| Read + GraphQL/分页/签名 | TypeScript adapter | 需要 JS 逻辑 |
| Write 操作 | TypeScript + Strategy.UI | 点击/输入等 DOM 操作 |
| Write + API | TypeScript + Strategy.COOKIE/HEADER | 直接 POST API |
3.2 实现顺序
先 Read 后 Write,先 YAML 后 TS:
1. Phase A: YAML Read 适配器(最快,通常每个 10-20 行) 2. Phase B: TS Read 适配器(需要 evaluate/intercept 的) 3. Phase C: TS Write 适配器(需 UI 自动化或 POST API)
3.3 实现模板
YAML Read 适配器模板(Cookie 策略)
site: <site>
name: <command>
description: <描述>
domain: www.<site>.com
strategy: cookie
browser: true
args:
limit:
type: int
default: 20
pipeline:
- navigate: https://www.<site>.com
- evaluate: |
(async () => {
const res = await fetch('<api_endpoint>', { credentials: 'include' });
const d = await res.json();
return (d.data?.items || []).map(item => ({
title: item.title,
// ... map source fields
}));
})()
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
- limit: ${{ args.limit }}
columns: [rank, title]TS Write 适配器模板(UI 策略)
import { cli, Strategy } from '../../registry.js';
cli({
site: '<site>',
name: '<command>',
description: '<描述>',
strategy: Strategy.UI,
args: [{ name: 'target', required: true, help: '<参数说明>' }],
columns: ['status', 'message'],
func: async (page, kwargs) => {
await page.goto(`https://www.<site>.com/${kwargs.target}`);
await page.wait({ text: '<expected_text>', timeout: 10 });
// 获取 snapshot 找到目标按钮
const snapshot = await page.accessibility.snapshot();
// 点击按钮 ...
return [{ status: 'success', message: '<action> completed' }];
},
});3.4 公共模式复用
迁移过程中如果发现多个适配器共享逻辑,考虑提取到 src/clis/<site>/utils.ts 工具文件:
// src/clis/<site>/utils.ts
export async function fetchWithAuth(page, url) { ... }
export function parseItem(raw) { ... }---
Phase 4: 验证 & 发布
4.1 构建验证
npx tsc --noEmit # TypeScript 编译检查
opencli list | grep <site> # 确认所有命令已注册4.2 运行验证(关键!)
每个新命令必须实际运行:
# Read 命令
opencli <site> <command> --limit 3 -f json
opencli <site> <command> --limit 3 -v # verbose 查看 pipeline
# Write 命令(谨慎!会实际操作)
opencli <site> <command> <test_target>4.3 更新文档
迁移完成后必须更新以下文件:
1. README.md — 在对应平台区域添加新命令示例 2. SKILL.md — 在 Commands Reference 中添加新命令
4.4 提交 & 推送
git add -A
git commit -m "feat(<site>): migrate <N> commands from <source-cli>
- Phase A: <N> YAML adapters (read operations)
- Phase B: <N> TS adapters (write operations)
- Source: <source_repo_url>"
git push---
Checklist
- [ ] 源项目命令清单已生成
- [ ] 对比矩阵已确认,高价值缺失命令已筛选
- [ ] 用户确认迁移范围
- [ ] Phase A: YAML Read 适配器已完成
- [ ] Phase B: TS Read 适配器已完成
- [ ] Phase C: TS Write 适配器已完成
- [ ]
npx tsc --noEmit编译通过 - [ ] 所有新命令已实际运行验证
- [ ] README.md 已更新
- [ ] SKILL.md 已更新
- [ ] 已 commit + push
实战案例参考
rdt-cli → opencli Reddit(2026-03-16)
- 源项目:
rdt-cli(25 个 Python 命令) - 筛选结果: 13 个高价值命令
- 实现: 7 个 YAML(read) + 6 个 TS(write)
- 产出: +11 文件,+767 行代码,Reddit 适配器从 4 → 15(+275%)
twitter-cli → opencli Twitter(2026-03-16)
- 源项目:
twitter-cli(20+ Python 命令) - 筛选结果: 11 个待实现
- 策略: Read 用
Strategy.COOKIE+ GraphQL fetch,Write 用Strategy.UI
// turbo-all
Steps
1. Clone the source CLI project for analysis:
git clone <source_repo_url> /tmp/<source-cli>2. Analyze source project: list all commands, auth method, API endpoints, and output fields.
3. Check existing opencli adapters for the target site:
ls src/clis/<site>/
opencli list | grep <site>4. Generate a comparison matrix table (source commands vs opencli existing). Mark each as: ✅ New / ✅ Enhance / ❌ Skip. Ask user to confirm which commands to migrate.
5. Implement YAML Read adapters first (highest ROI, 10-20 lines each). Place files in src/clis/<site>/<name>.yaml.
6. Implement TS Read adapters for complex cases (GraphQL, pagination, signing). Place files in src/clis/<site>/<name>.ts.
7. Implement TS Write adapters using Strategy.UI or Strategy.COOKIE. Place files in src/clis/<site>/<name>.ts.
8. Verify build:
npx tsc --noEmit9. Verify all commands are registered:
opencli list | grep <site>10. Run each new command to verify it works:
opencli <site> <command> --limit 3 -f json11. Update README.md with new command examples in the appropriate platform section.
12. Update SKILL.md Commands Reference with new commands.
13. Commit and push:
git add -A
git commit -m "feat(<site>): migrate <N> commands from <source-cli>"
git pushname: Setup Chrome
description: Install real Chrome for browser testing (with xvfb on Linux)
outputs:
chrome-path:
description: Path to the installed Chrome binary
value: ${{ steps.setup-chrome.outputs.chrome-path }}
runs:
using: composite
steps:
- name: Install real Chrome (stable)
uses: browser-actions/setup-chrome@v1
id: setup-chrome
with:
chrome-version: stable
- name: Verify Chrome installation
shell: bash
run: |
echo "Chrome path: ${{ steps.setup-chrome.outputs.chrome-path }}"
"${{ steps.setup-chrome.outputs.chrome-path }}" --version
- name: Install xvfb (Linux only)
if: runner.os == 'Linux'
shell: bash
run: sudo apt-get install -y xvfb
version: 2
updates:
# npm dependencies
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 10
labels:
- "dependencies"
commit-message:
prefix: "chore(deps)"
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 5
labels:
- "dependencies"
- "ci"
commit-message:
prefix: "chore(ci)"
name: "🐛 Bug Report"
description: Report a bug or unexpected behavior in OpenCLI
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report a bug. A short reproduction and any error output are usually enough.
- type: textarea
id: description
attributes:
label: Description
description: A clear and concise description of the bug.
placeholder: What happened?
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: How can we reproduce this behavior?
value: |
1. Run `opencli ...`
2. ...
3. See error
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: What did you expect to happen?
validations:
required: true
- type: input
id: version
attributes:
label: OpenCLI Version
description: "Run `opencli --version` to find out."
placeholder: "0.8.0"
validations:
required: true
- type: dropdown
id: node-version
attributes:
label: Node.js Version
options:
- "20.x"
- "22.x"
- Other
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating System
options:
- macOS
- Linux
- Windows
- Other
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs / Screenshots
description: |
Paste any relevant error output. Run with `-v` for verbose logs:
```
opencli <command> -v
```
render: shell
validations:
required: false
blank_issues_enabled: true
contact_links:
- name: 📖 Documentation
url: https://github.com/jackwener/opencli#readme
about: Check the README and docs before opening an issue.
- name: 🧪 Testing Guide
url: https://github.com/jackwener/opencli/blob/main/TESTING.md
about: How to run and write tests for OpenCLI.
name: "✨ Feature Request"
description: Suggest a new feature or improvement
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Have an idea to make OpenCLI better? We'd love to hear it!
- type: textarea
id: description
attributes:
label: Feature Description
description: A clear and concise description of the feature you'd like.
validations:
required: true
- type: textarea
id: use-case
attributes:
label: Use Case
description: What problem does this solve? Who benefits from this feature?
placeholder: "As a user, I want to ... so that ..."
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: Proposed Solution
description: If you have a specific implementation in mind, describe it here.
validations:
required: false
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Any alternative approaches you've thought about?
validations:
required: false
name: "🌐 New Site Adapter Request"
description: Request support for a new website
title: "[Site]: "
labels: ["new-adapter"]
body:
- type: markdown
attributes:
value: |
Want OpenCLI to support a new site? Tell us about it!
- type: input
id: site-name
attributes:
label: Site Name
description: The name of the website.
placeholder: "e.g. Product Hunt"
validations:
required: true
- type: input
id: site-url
attributes:
label: Site URL
description: The main URL of the website.
placeholder: "https://www.producthunt.com"
validations:
required: true
- type: textarea
id: commands
attributes:
label: Desired Commands
description: What commands would you like? List them with a brief description.
value: |
- `hot` — trending / popular items
- `search` — search the site
validations:
required: true
- type: textarea
id: api-examples
attributes:
label: Example Links or API Endpoints
description: Share any example page URLs or API endpoints if you have them (optional).
placeholder: |
Example page: https://www.producthunt.com/posts/example
GET https://api.producthunt.com/v2/posts?order=votes
Response: { "posts": [{ "name": "...", "tagline": "..." }] }
validations:
required: false
- type: checkboxes
id: contribution
attributes:
label: Willing to Contribute?
options:
- label: I'm willing to submit a PR for this adapter
Description
<!-- Briefly describe your changes and link to any related issues. -->
Related issue:
Type of Change
- [ ] 🐛 Bug fix
- [ ] ✨ New feature
- [ ] 🌐 New site adapter
- [ ] 📝 Documentation
- [ ] ♻️ Refactor
- [ ] 🔧 CI / build / tooling
Checklist
- [ ] I ran the checks relevant to this PR
- [ ] I updated tests or docs if needed
- [ ] I included output or screenshots when useful
Documentation (if adding/modifying an adapter)
- [ ] Added doc page under
docs/adapters/(if new adapter) - [ ] Updated
docs/adapters/index.mdtable (if new adapter) - [ ] Updated sidebar in
docs/.vitepress/config.mts(if new adapter) - [ ] Updated
README.md/README.zh-CN.mdwhen command discoverability changed - [ ] Used positional args for the command's primary subject unless a named flag is clearly better
- [ ] Normalized expected adapter failures to
CliErrorsubclasses instead of rawError
Screenshots / Output
<!-- If applicable, paste CLI output or screenshots here. -->
name: Build Chrome Extension
on:
push:
branches: [ "main" ]
tags: [ "v*.*.*" ]
paths:
- 'extension/**'
- '.github/workflows/build-extension.yml'
pull_request:
branches: [ "main" ]
paths:
- 'extension/**'
- '.github/workflows/build-extension.yml'
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 20
cache: 'npm'
cache-dependency-path: extension/package-lock.json
- name: Install extension dependencies
run: npm ci
working-directory: extension
- name: Build extension
run: npm run build
working-directory: extension
- name: Prepare extension package
run: npm run package:release -- --out ../extension-package
working-directory: extension
- name: Create Extension ZIP
run: |
cd extension-package
zip -r ../opencli-extension.zip .
- name: Upload Artifacts (Action Run)
uses: actions/upload-artifact@v7
with:
name: opencli-extension-build
path: |
opencli-extension.zip
retention-days: 7
- name: Attach to GitHub Release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2.6.1
with:
files: |
opencli-extension.zip
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: CI
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
schedule:
- cron: '0 8 * * 1' # Weekly Monday 08:00 UTC — smoke tests
workflow_dispatch:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── Fast gate: typecheck + build ──
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Type check
run: npx tsc --noEmit
- name: Build
run: npm run build
# ── Unit tests (vitest shard) ──
# PR: ubuntu + Node 22 only (fast feedback, 2 jobs).
# Push to main/dev: full matrix for cross-platform/cross-version coverage (12 jobs).
unit-test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ${{ (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && fromJSON('["ubuntu-latest","macos-latest","windows-latest"]') || fromJSON('["ubuntu-latest"]') }}
node-version: ${{ (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && fromJSON('["20","22"]') || fromJSON('["22"]') }}
shard: [1, 2]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests (Node ${{ matrix.node-version }}, shard ${{ matrix.shard }}/2)
run: npm test -- --reporter=verbose --shard=${{ matrix.shard }}/2
# ── Bun compatibility check ──
bun-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.5
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests under Bun
run: bun vitest run --project unit --reporter=verbose
# Adapter tests are pure unit tests — OS doesn't affect results.
adapter-test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run focused adapter tests
run: npm run test:adapter -- --reporter=verbose
# ── Smoke tests (scheduled / manual only) ──
smoke-test:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
needs: build
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# NOTE: Windows excluded — browser-actions/setup-chrome hangs during
# Chrome MSI installation on Windows runners (known issue).
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup Chrome
uses: ./.github/actions/setup-chrome
id: setup-chrome
- name: Build
run: npm run build
- name: Run smoke tests (Linux, via xvfb)
if: runner.os == 'Linux'
run: |
xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" \
npx vitest run tests/smoke/ --reporter=verbose
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
- name: Run smoke tests (macOS / Windows)
if: runner.os != 'Linux'
run: npx vitest run tests/smoke/ --reporter=verbose
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
timeout-minutes: 15
name: Doc Check
on:
pull_request:
branches: [main, dev]
concurrency:
group: doc-check-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── Adapter doc coverage ──
doc-coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check adapter doc coverage
run: bash scripts/check-doc-coverage.sh --strict
# ── VitePress build validation ──
docs-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build docs (catches broken links & sidebar refs)
run: npm run docs:build
name: Trigger Website Rebuild (Docs Updated)
on:
push:
branches: [main]
paths: ['docs/**']
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Trigger opencli-website rebuild
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.WEBSITE_DEPLOY_TOKEN }}
repository: jackwener/opencli-website
event-type: docs-updated
name: E2E Headed Chrome
on:
push:
branches: [main, dev]
paths:
- 'extension/**'
- 'src/browser/**'
- 'src/daemon.ts'
- 'src/execution.ts'
- 'src/interceptor.ts'
- 'tests/e2e/**'
- 'tests/smoke/**'
- '.github/actions/setup-chrome/**'
- '.github/workflows/e2e-headed.yml'
pull_request:
branches: [main, dev]
paths:
- 'extension/**'
- 'src/browser/**'
- 'src/daemon.ts'
- 'src/execution.ts'
- 'src/interceptor.ts'
- 'tests/e2e/**'
- 'tests/smoke/**'
- '.github/actions/setup-chrome/**'
- '.github/workflows/e2e-headed.yml'
workflow_dispatch:
concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e-headed:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# NOTE: Windows excluded — browser-actions/setup-chrome hangs during
# Chrome MSI installation on Windows runners (known issue).
os: [ubuntu-latest, macos-latest]
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup Chrome
uses: ./.github/actions/setup-chrome
id: setup-chrome
- name: Build
run: npm run build
- name: Run E2E tests (Linux, via xvfb)
if: runner.os == 'Linux'
run: |
xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" \
npx vitest run tests/e2e/ --reporter=verbose
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
- name: Run E2E tests (macOS / Windows)
if: runner.os != 'Linux'
run: npx vitest run tests/e2e/ --reporter=verbose
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
id-token: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Type check
run: npx tsc --noEmit
- name: Create GitHub Release
uses: softprops/action-gh-release@v2.6.1
with:
generate_release_notes: true
- name: Publish to npm
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Trigger website rebuild
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.WEBSITE_DEPLOY_TOKEN }}
repository: jackwener/opencli-website
event-type: version-released
name: Security Audit
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
schedule:
- cron: '0 9 * * 1' # Weekly Monday 09:00 UTC
permissions:
contents: read
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: true
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: npm audit (production)
run: npm audit --omit=dev --audit-level=high
node_modules/
dist/
!extension/dist/
*.tsbuildinfo
.opencli/
.mcp.json
*.log
.DS_Store
# VitePress
docs/.vitepress/dist
docs/.vitepress/cache
# Extensions & Secrets
*.pem
*.crx
*.zip
.envrc
.windsurf
.claude
.cortex
# Database files
*.db
Changelog
1.5.8 (2026-04-01)
Bug Fixes
- extension: avoid mutating healthy tabs before debugger attach and add regression coverage (#662)
1.5.7 (2026-04-01)
Features
- daemon: replace 5min idle timeout with long-lived daemon model (4h default, dual-condition exit) (#641)
- daemon: add
opencli daemon status/stop/restartCLI commands (#641) - youtube: add search filters —
--typeshorts/video/channel,--upload,--sort(#616) - notebooklm: add read commands and compatibility layer (#622)
- instagram: add media download command (#623)
- stealth: harden CDP debugger detection countermeasures (#644)
- v2ex: add id, node, url, content, member fields to topic output (#646, #648)
- electron: auto-launcher — zero-config CDP connection (#653)
Bug Fixes
- douyin: repair creator draft flow — switch from broken API pipeline to UI-driven approach (#640)
- douyin: support current creator API response shapes for activities, profile, collections, hashtag, videos (#618)
- bilibili: distinguish login-gated subtitles from empty results (#645)
- facebook: avoid in-page redirect in search — use navigate step instead of window.location.href (#642)
- substack: update selectors for DOM redesign (#624)
- weread: recover book details from cached shelf fallback (#628)
- docs: use relative links in adapter index (#629)
1.4.1 (2026-03-25)
Features
- douyin: add Douyin creator center adapter — 14 commands, 8-phase publish pipeline (#416)
- weibo,youtube: add Weibo commands and YouTube channel/comments (#418)
- twitter: add filter option for search (#410)
- extension: add popup UI, privacy policy, and CSP for Chrome Web Store (#415)
- add url field to 9 search adapters (67% -> 97% coverage) (#414)
Bug Fixes
- extension: improve UX when daemon is not running — show hint in popup, reduce reconnect noise (#424)
- remove incorrect gws and readwise external CLI entries (#419, #420)
CI
1.4.0 (2026-03-25)
Features
- pixiv: add Pixiv adapter — ranking, search, user illusts, detail, download (#403)
- plugin: add lifecycle hooks API — onStartup, onBeforeExecute, onAfterExecute (#376)
- plugin: validate plugin structure on install and update (#364)
- xueqiu: add Danjuan fund account commands — fund-holdings, fund-snapshot (#391)
- tiktok: add video URL to search results (#404)
- linkedin: add timeline feed command (#342)
- jd: add JD.com product details adapter (#344)
- web: add generic
web readcommand for any URL → Markdown (#343) - dictionary: add dictionary search, synonyms, and examples adapters (#241)
Bug Fixes
- analysis: fix hasLimit using wrong Set (SEARCH_PARAMS → LIMIT_PARAMS) (#412)
- pipeline: remove phantom scroll step — declared but never registered (#412)
- validate: add missing download step to KNOWN_STEP_NAMES (#412)
- extension: security hardening — tab isolation, URL validation, cookie scope (#409)
- sort: use localeCompare with natural numeric sort by default (#306)
- pipeline: evaluate chained || in template engine (#305)
- pipeline: check HTTP status in fetch step (#384)
- plugin: resolve Windows path and symlink issues (#400)
- download: scope cookies to target domain (#385)
- extension: fix same-url navigation timeout (#380)
- fix ChatWise Windows connect (#405)
- resolve 6 critical + 11 important bugs from deep code review (#337, #340)
- harden security-sensitive execution paths (#335)
- stealth: harden anti-detection against advanced fingerprinting (#357)
Code Quality
- replace all
catch (err: any)with typedgetErrorMessage()across 13 files (#412) - adopt CliError subclasses in social and desktop adapters (#367, #372, #375)
- simplify codebase with type dedup, shared analysis module, and consistent naming (#373)
- ci: add cross-platform CI matrix (Linux/macOS/Windows) (#402)
1.3.3 (2026-03-25)
Features
- browser: add stealth anti-detection for CDP and daemon modes (#319)
Bug Fixes
- stealth: review fixes — guard plugins, rewrite stack trace cleanup (#320)
1.3.2 (2026-03-24)
Features
- error-handling: refine error handling with semantic error types and emoji-coded output (#312) (b4d64ca)
Bug Fixes
- security: replace execSync with execFileSync to prevent command injection (#309) (41aedf6)
- remove duplicate getErrorMessage import in discovery.ts (#315) (75f4237)
- e2e: broaden xiaoyuzhou skip logic for overseas CI runners (#316) (a170873)
Documentation
Chores
1.3.1 (2026-03-22)
Features
- plugin: add update command, hot reload after install, README section (#307) (966f6e5)
- yollomi: add new commands and update documentation (#235) (ea83242)
- record: add live recording command for API capture (#300) (dff0fe5)
- weibo: add weibo search command (#299) (c7895ea)
- v2ex: add node, user, member, replies, nodes commands (#282) (a83027d)
- hackernews: add new, best, ask, show, jobs, search, user commands (#290) (127a974)
- doubao-app: add Doubao AI desktop app CLI adapter (#289) (66c4b84)
- doubao: add doubao browser adapter (#277) (9cdc127)
- xiaohongshu: add publish command for 图文 note automation (#276) (a6d993f)
- weixin: add weixin article download adapter & abstract download helpers (#280) (b7c6c02)
Bug Fixes
- tests: use positional arg syntax in browser search tests (#302) (4343ec0)
- xiaohongshu: improve search login-wall handling and detail output (#298) (f8bf663)
- ensure standard PATH is available for external CLIs (#285) (22f5c7a)
- xiaohongshu: scope image selector to avoid downloading avatars (#293) (3a21be6)
- add turndown dependency to package.json (#288) (2a52906)
1.3.0 (2026-03-21)
Features
Performance
Refactoring
1.2.3 (2026-03-21)
Bug Fixes
- replace all about:blank with data: URI to prevent New Tab Override interception (#257) (3e91876)
- harden resolveTabId against New Tab Override extension interception (#255) (112fdef)
1.2.2 (2026-03-21)
Bug Fixes
1.2.1 (2026-03-21)
Bug Fixes
- twitter: harden timeline review findings (#236) (4cd0409)
- wikipedia: fix search arg name + add random and trending commands (#231) (1d56dd7)
- resolve inconsistent doctor --live report (fix #121) (#224) (387aa0d)
1.2.0 (2026-03-21)
Features
- douban: add movie adapter with search, top250, subject, marks, reviews commands (#239) (70651d3)
- devto: add devto adapter (#234) (ea113a6)
- twitter: add --type flag to timeline command (#83) (e98cf75)
- google: add search, suggest, news, and trends adapters (#184) (4e32599)
- add douban, sinablog, substack adapters; upgrade medium to TS (#185) (bdf5967)
- xueqiu: add earnings-date command (#211) (fae1dce)
- browser: advanced DOM snapshot engine with 13-layer pruning pipeline (#210) (d831b04)
- instagram,facebook: add write actions and extended commands (#201) (eb0ccaf)
- grok: add opt-in --web flow for grok ask (#193) (fcff2e4)
- tiktok: add TikTok adapter with 15 commands (#202) (4391ccf)
- add Lobste.rs, Instagram, and Facebook adapters (#199) (ce484c2)
- medium: add medium adapter (#190) (06c902a)
- plugin system (Stage 0-2) (1d39295)
- make primary args positional across all CLIs (#242) (9696db9)
- xueqiu: make primary args positional (#213) (fb2a145)
Refactoring
- replace hardcoded skipPreNav with declarative navigateBefore field (#208) (a228758)
- boss: extract common.ts utilities, fix missing login detection (#200) (ae30763)
- type discovery core (#219) (bd274ce)
- type browser core (#218) (28c393e)
- type pipeline core (#217) (8a4ea41)
- reduce core any usage (#216) (45cee57)
- fail fast on invalid pipeline steps (#237) (c76f86c)
1.1.0 (2026-03-20)
Features
- add antigravity serve command — Anthropic API proxy (35a0fed)
- add arxiv and wikipedia adapters (#132) (3cda14a)
- add external CLI hub for discovery, auto-installation, and execution of external tools. (b3e32d8)
- add sinafinance 7x24 news adapter (#131) (02793e9)
- boss: add 8 new recruitment management commands (#133) (7e973ca)
- serve: implement auto new conv, model mapping, and precise completion detection (0e8c96b)
- serve: use CDP mouse click + Input.insertText for reliable message injection (c63af6d)
- xiaohongshu creator flows migration (#124) (8f17259)
Bug Fixes
- docs: use base '/' for custom domain and add CNAME file (#129) (2876750)
- serve: update model mappings to match actual Antigravity UI (36bc57a)
- type safety for wikiFetch and arxiv abstract truncation (4600b9d)
- use UTC+8 for XHS timestamp formatting (CI timezone fix) (03f067d)
- xiaohongshu: use fixed UTC+8 offset in trend timestamp formatting (CI timezone fix) (593436e)
1.0.6 (2026-03-20)
Bug Fixes
CLI-EXPLORER — 适配器探索式开发完全指南
本文档教你(或 AI Agent)如何为 OpenCLI 添加一个新网站的命令。
从零到发布,覆盖 API 发现、方案选择、适配器编写、测试验证全流程。
[!TIP]
只想为一个具体页面快速生成一个命令? 看 CLI-ONESHOT.md(~150 行,4 步搞定)。
本文档适合从零探索一个新站点的完整流程。
---
AI Agent 开发者必读:用浏览器探索
[!CAUTION]
你(AI Agent)必须通过浏览器打开目标网站去探索!
不要只靠 opencli explore 命令或静态分析来发现 API。你拥有浏览器工具,必须主动用它们浏览网页、观察网络请求、模拟用户交互。
为什么?
很多 API 是懒加载的(用户必须点击某个按钮/标签才会触发网络请求)。字幕、评论、关注列表等深层数据不会在页面首次加载时出现在 Network 面板中。如果你不主动去浏览和交互页面,你永远发现不了这些 API。
AI Agent 探索工作流(必须遵循)
| 步骤 | 工具 | 做什么 |
|---|---|---|
| 0. 打开浏览器 | browser_navigate | 导航到目标页面 |
| 1. 观察页面 | browser_snapshot | 观察可交互元素(按钮/标签/链接) |
| 2. 首次抓包 | browser_network_requests | 筛选 JSON API 端点,记录 URL pattern |
| 3. 模拟交互 | browser_click + browser_wait_for | 点击"字幕""评论""关注"等按钮 |
| 4. 二次抓包 | browser_network_requests | 对比步骤 2,找出新触发的 API |
| 5. 验证 API | browser_evaluate | fetch(url, {credentials:'include'}) 测试返回结构 |
| 6. 写代码 | — | 基于确认的 API 写适配器 |
常犯错误
| ❌ 错误做法 | ✅ 正确做法 |
|---|---|
只用 opencli explore 命令,等结果自动出来 | 用浏览器工具打开页面,主动浏览 |
直接在代码里 fetch(url),不看浏览器实际请求 | 先在浏览器中确认 API 可用,再写代码 |
| 页面打开后直接抓包,期望所有 API 都出现 | 模拟点击交互(展开评论/切换标签/加载更多) |
| 遇到 HTTP 200 但空数据就放弃 | 检查是否需要 Wbi 签名或 Cookie 鉴权 |
完全依赖 __INITIAL_STATE__ 拿所有数据 | __INITIAL_STATE__ 只有首屏数据,深层数据要调 API |
实战成功案例:5 分钟实现「关注列表」适配器
以下是用上述工作流实际发现 Bilibili 关注列表 API 的完整过程:
1. browser_navigate → https://space.bilibili.com/{uid}/fans/follow
2. browser_network_requests → 发现:
GET /x/relation/followings?vmid={uid}&pn=1&ps=24 → [200]
GET /x/relation/stat?vmid={uid} → [200]
3. browser_evaluate → 验证 API:
fetch('/x/relation/followings?vmid=137702077&pn=1&ps=5', {credentials:'include'})
→ { code: 0, data: { total: 1342, list: [{mid, uname, sign, ...}] } }
4. 结论:标准 Cookie API,无需 Wbi 签名
5. 写 following.ts → 一次构建通过关键决策点:
- 直接访问
fans/follow页面(不是首页),页面加载就会触发 following API - 看到 URL 里没有
/wbi/→ 不需要签名 → 直接用fetchJson而非apiGet - API 返回
code: 0+ 非空list→ Tier 2 Cookie 策略确认
---
核心流程
┌─────────────┐ ┌─────────────┐ ┌──────────────┐ ┌────────┐
│ 1. 发现 API │ ──▶ │ 2. 选择策略 │ ──▶ │ 3. 写适配器 │ ──▶ │ 4. 测试 │
└─────────────┘ └─────────────┘ └──────────────┘ └────────┘
explore cascade YAML / TS run + verify---
Step 1: 发现 API
1a. 自动化发现(推荐)
OpenCLI 内置 Deep Explore,自动分析网站网络请求:
opencli explore https://www.example.com --site mysite输出到 .opencli/explore/mysite/:
| 文件 | 内容 |
|---|---|
manifest.json | 站点元数据、框架检测(Vue2/3、React、Next.js、Pinia、Vuex) |
endpoints.json | 已发现的 API 端点,按评分排序,含 URL pattern、方法、响应类型 |
capabilities.json | 推理出的功能(hot、search、feed…),含置信度和推荐参数 |
auth.json | 认证方式检测(Cookie/Header/无认证),策略候选列表 |
1b. 手动抓包验证
Explore 的自动分析可能不完美,用 verbose 模式手动确认:
# 在浏览器中打开目标页面,观察网络请求
opencli explore https://www.example.com --site mysite -v
# 或直接用 evaluate 测试 API
opencli bilibili hot -v # 查看已有命令的 pipeline 每步数据流关注抓包结果中的关键信息:
- URL pattern:
/api/v2/hot?limit=20→ 这就是你要调用的端点 - Method:
GET/POST - Request Headers: Cookie? Bearer? 自定义签名头(X-s、X-t)?
- Response Body: JSON 结构,特别是数据在哪个路径(
data.items、data.list)
1c. 高阶 API 发现捷径法则 (Heuristics)
在开始死磕复杂的抓包拦截之前,按照以下优先级进行尝试:
1. 后缀爆破法 (`.json`): 像 Reddit 这样复杂的网站,只要在其 URL 后加上 .json(例如 /r/all.json),就能在带 Cookie 的情况下直接利用 fetch 拿到极其干净的 REST 数据(Tier 2 Cookie 策略极速秒杀)。另外如功能完备的雪球 (xueqiu) 也可以走这种纯 API 的方式极简获取,成为你构建简单 YAML 的黄金标杆。 2. 全局状态查找法 (`__INITIAL_STATE__`): 许多服务端渲染 (SSR) 的网站(如小红书、Bilibili)会将首页或详情页的完整数据挂载到全局 window 对象上。与其去拦截网络请求,不如直接 page.evaluate('() => window.__INITIAL_STATE__') 获取整个数据树。 3. 主动交互触发法 (Active Interaction): 很多深层 API(如视频字幕、评论下的回复)是懒加载的。在静态抓包找不到数据时,尝试在 evaluate 步骤或手动打断点时,主动去点击(Click)页面上的对应按钮(如"CC"、"展开全部"),从而诱发隐藏的 Network Fetch。 4. 框架探测与 Store Action 截断: 如果站点使用 Vue + Pinia,可以使用 tap 步骤调用 action,让前端框架代替你完成复杂的鉴权签名封装。 5. 底层 XHR/Fetch 拦截: 最后手段,当上述都不行时,使用 TypeScript 适配器进行无侵入式的请求抓取。
1d. 框架检测
Explore 自动检测前端框架。如果需要手动确认:
# 在已打开目标网站的情况下
opencli evaluate "(()=>{
const vue3 = !!document.querySelector('#app')?.__vue_app__;
const vue2 = !!document.querySelector('#app')?.__vue__;
const react = !!window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
const pinia = vue3 && !!document.querySelector('#app').__vue_app__.config.globalProperties.\$pinia;
return JSON.stringify({vue3, vue2, react, pinia});
})()"Vue + Pinia 的站点(如小红书)可以直接通过 Store Action 绕过签名。
---
Step 2: 选择认证策略
OpenCLI 提供 5 级认证策略。使用 cascade 命令自动探测:
opencli cascade https://api.example.com/hot策略决策树
直接 fetch(url) 能拿到数据?
→ ✅ Tier 1: public(公开 API,不需要浏览器)
→ ❌ fetch(url, {credentials:'include'}) 带 Cookie 能拿到?
→ ✅ Tier 2: cookie(最常见,evaluate 步骤内 fetch)
→ ❌ → 加上 Bearer / 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 |
| 3 | header | 🔄 ~7s | 中等 | 需要 CSRF token 或 Bearer | Twitter GraphQL |
| 4 | intercept | 🔄 ~10s | 较高 | 请求有复杂签名 | 小红书 (Pinia + XHR) |
| 5 | ui | 🐌 ~15s+ | 最高 | 无 API,纯 DOM 解析 | 遗留网站 |
---
Step 2.5: 准备工作(写代码之前)
先找模板:从最相似的现有适配器开始
不要从零开始写。先看看同站点已有哪些适配器:
ls src/clis/<site>/ # 看看已有什么
cat src/clis/<site>/feed.ts # 读最相似的那个最高效的方式是 复制最相似的适配器,然后改 3 个地方: 1. name → 新命令名 2. API URL → 你在 Step 1 发现的端点 3. 字段映射 → 对应新 API 的字段
平台 SDK 速查表
写 TS 适配器之前,先看看你的目标站点有没有现成的 helper 函数可以复用:
Bilibili (src/clis/bilibili/utils.ts)
| 函数 | 用途 | 何时使用 |
|---|---|---|
fetchJson(page, url) | 带 Cookie 的 fetch + JSON 解析 | 普通 Cookie-tier API |
apiGet(page, path, {signed, params}) | 带 Wbi 签名的 API 调用 | URL 含 /wbi/ 的接口 |
getSelfUid(page) | 获取当前登录用户的 UID | "我的xxx" 类命令 |
resolveUid(page, input) | 解析用户输入的 UID(支持数字/URL) | --uid 参数处理 |
wbiSign(page, params) | 底层 Wbi 签名生成 | 通常不直接用,apiGet 已封装 |
stripHtml(s) | 去除 HTML 标签 | 清理富文本字段 |
如何判断需不需要 `apiGet`?看 Network 请求 URL:
- 含
/wbi/或w_rid=→ 必须用apiGet(..., { signed: true }) - 不含 → 直接用
fetchJson
其他站点(Twitter、小红书等)暂无专用 SDK,直接用page.evaluate+fetch即可。
---
Step 3: 编写适配器
YAML vs TS?先看决策树
你的 pipeline 里有 evaluate 步骤(内嵌 JS 代码)?
→ ✅ 用 TypeScript (src/clis/<site>/<name>.ts),保存即自动动态注册
→ ❌ 纯声明式(navigate + tap + map + limit)?
→ ✅ 用 YAML (src/clis/<site>/<name>.yaml),保存即自动注册| 场景 | 选择 | 示例 |
|---|---|---|
| 纯 fetch/select/map/limit | YAML | v2ex/hot.yaml, hackernews/top.yaml |
| navigate + evaluate(fetch) + map | YAML(评估复杂度) | zhihu/hot.yaml |
| navigate + tap + map | YAML ✅ | xiaohongshu/feed.yaml, xiaohongshu/notifications.yaml |
| 有复杂 JS 逻辑(Pinia state 读取、条件分支) | TS | xiaohongshu/me.ts, bilibili/me.ts |
| XHR 拦截 + 签名 | TS | xiaohongshu/search.ts |
| GraphQL / 分页 / Wbi 签名 | TS | bilibili/search.ts, twitter/search.ts |
经验法则:如果你发现 YAML 里嵌了超过 10 行 JS,改用 TS 更可维护。
通用模式:分页 API
很多 API 使用 pn(页码)+ ps(每页数量)分页。标准处理模式:
args: [
{ name: 'page', type: 'int', required: false, default: 1, help: '页码' },
{ name: 'limit', type: 'int', required: false, default: 50, help: '每页数量 (最大 50)' },
],
func: async (page, kwargs) => {
const pn = kwargs.page ?? 1;
const ps = Math.min(kwargs.limit ?? 50, 50); // 尊重 API 的 ps 上限
const payload = await fetchJson(page,
`https://api.example.com/list?pn=${pn}&ps=${ps}`
);
return payload.data?.list || [];
},大多数站点的 ps 上限是 20~50。超过会被静默截断或返回错误。方式 A: YAML Pipeline(声明式,推荐)
文件路径: src/clis/<site>/<name>.yaml,放入即自动注册。
Tier 1 — 公开 API 模板
# src/clis/v2ex/hot.yaml
site: v2ex
name: hot
description: V2EX 热门话题
domain: www.v2ex.com
strategy: public
browser: false
args:
limit:
type: int
default: 20
pipeline:
- fetch:
url: https://www.v2ex.com/api/topics/hot.json
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
replies: ${{ item.replies }}
- limit: ${{ args.limit }}
columns: [rank, title, replies]Tier 2 — Cookie 认证模板(最常用)
# src/clis/zhihu/hot.yaml
site: zhihu
name: hot
description: 知乎热榜
domain: www.zhihu.com
pipeline:
- navigate: https://www.zhihu.com # 先加载页面建立 session
- evaluate: | # 在浏览器内发请求,自动带 Cookie
(async () => {
const res = await fetch('/api/v3/feed/topstory/hot-lists/total?limit=50', {
credentials: 'include'
});
const d = await res.json();
return (d?.data || []).map(item => {
const t = item.target || {};
return {
title: t.title,
heat: item.detail_text || '',
answers: t.answer_count,
};
});
})()
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
heat: ${{ item.heat }}
answers: ${{ item.answers }}
- limit: ${{ args.limit }}
columns: [rank, title, heat, answers]关键:evaluate步骤内的fetch运行在浏览器页面内,自动携带credentials: 'include',无需手动处理 Cookie。
进阶 — 带搜索参数
# src/clis/zhihu/search.yaml
site: zhihu
name: search
description: 知乎搜索
args:
query:
type: str
required: true
positional: true
description: Search query
limit:
type: int
default: 10
pipeline:
- navigate: https://www.zhihu.com
- evaluate: |
(async () => {
const q = encodeURIComponent('${{ args.query }}');
const res = await fetch('/api/v4/search_v3?q=' + q + '&t=general&limit=${{ args.limit }}', {
credentials: 'include'
});
const d = await res.json();
return (d?.data || [])
.filter(item => item.type === 'search_result')
.map(item => ({
title: (item.object?.title || '').replace(/<[^>]+>/g, ''),
type: item.object?.type || '',
author: item.object?.author?.name || '',
votes: item.object?.voteup_count || 0,
}));
})()
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
type: ${{ item.type }}
author: ${{ item.author }}
votes: ${{ item.votes }}
- limit: ${{ args.limit }}
columns: [rank, title, type, author, votes]Tier 4 — Store Action Bridge(tap 步骤,intercept 策略推荐)
适用于 Vue + Pinia/Vuex 的网站(如小红书),无须手动写 XHR 拦截代码:
# src/clis/xiaohongshu/notifications.yaml
site: xiaohongshu
name: notifications
description: "小红书通知"
domain: www.xiaohongshu.com
strategy: intercept
browser: true
args:
type:
type: str
default: mentions
description: "Notification type: mentions, likes, or connections"
limit:
type: int
default: 20
columns: [rank, user, action, content, note, time]
pipeline:
- navigate: https://www.xiaohongshu.com/notification
- wait: 3
- tap:
store: notification # Pinia store name
action: getNotification # Store action to call
args: # Action arguments
- ${{ args.type | default('mentions') }}
capture: /you/ # URL pattern to capture response
select: data.message_list # Extract sub-path from response
timeout: 8
- map:
rank: ${{ index + 1 }}
user: ${{ item.user_info.nickname }}
action: ${{ item.title }}
content: ${{ item.comment_info.content }}
- limit: ${{ args.limit | default(20) }}`tap` 步骤自动完成:注入 fetch+XHR 双拦截 → 查找 Pinia/Vuex store → 调用 action → 捕获匹配 URL 的响应 → 清理拦截。
如果 store 或 action 找不到,会返回 hint 列出所有可用的 store actions,方便调试。| tap 参数 | 必填 | 说明 |
|---|---|---|
store | ✅ | Pinia store 名称(如 feed, search, notification) |
action | ✅ | Store action 方法名 |
capture | ✅ | URL 子串匹配(匹配网络请求 URL) |
args | ❌ | 传给 action 的参数数组 |
select | ❌ | 从 captured JSON 中提取的路径(如 data.items) |
timeout | ❌ | 等待网络响应的超时秒数(默认 5s) |
framework | ❌ | pinia 或 vuex(默认自动检测) |
方式 B: TypeScript 适配器(编程式)
适用于需要嵌入 JS 代码读取 Pinia state、XHR 拦截、GraphQL、分页、复杂数据转换等场景。
文件路径: src/clis/<site>/<name>.ts。文件将会在运行时被动态扫描并注册(切勿在 index.ts 中手动 import)。
Tier 3 — Header 认证(Twitter)
// src/clis/twitter/search.ts
import { cli, Strategy } from '../../registry.js';
cli({
site: 'twitter',
name: 'search',
description: 'Search tweets',
strategy: Strategy.HEADER,
args: [{ name: 'query', required: true, positional: true }],
columns: ['rank', 'author', 'text', 'likes'],
func: async (page, kwargs) => {
await page.goto('https://x.com');
const data = await page.evaluate(`
(async () => {
// 从 Cookie 提取 CSRF token
const ct0 = document.cookie.split(';')
.map(c => c.trim())
.find(c => c.startsWith('ct0='))?.split('=')[1];
if (!ct0) return { error: 'Not logged in' };
const bearer = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D...';
const headers = {
'Authorization': 'Bearer ' + decodeURIComponent(bearer),
'X-Csrf-Token': ct0,
'X-Twitter-Auth-Type': 'OAuth2Session',
};
const variables = JSON.stringify({ rawQuery: '${kwargs.query}', count: 20 });
const url = '/i/api/graphql/xxx/SearchTimeline?variables=' + encodeURIComponent(variables);
const res = await fetch(url, { headers, credentials: 'include' });
return await res.json();
})()
`);
// ... 解析 data
},
});Tier 4 — XHR/Fetch 双重拦截 (Twitter/小红书 通用模式)
// src/clis/xiaohongshu/user.ts
import { cli, Strategy } from '../../registry.js';
cli({
site: 'xiaohongshu',
name: 'user',
description: '获取用户笔记',
strategy: Strategy.INTERCEPT,
args: [{ name: 'id', required: true }],
columns: ['rank', 'title', 'likes', 'url'],
func: async (page, kwargs) => {
await page.goto(`https://www.xiaohongshu.com/user/profile/${kwargs.id}`);
await page.wait(5);
// XHR/Fetch 底层拦截:捕获所有包含 'v1/user/posted' 的请求
await page.installInterceptor('v1/user/posted');
// 触发后端 API:模拟人类用户向底部滚动2次
await page.autoScroll({ times: 2, delayMs: 2000 });
// 提取所有被拦截捕获的 JSON 响应体
const requests = await page.getInterceptedRequests();
if (!requests || requests.length === 0) return [];
let results = [];
for (const req of requests) {
if (req.data?.data?.notes) {
for (const note of req.data.data.notes) {
results.push({
title: note.display_title || '',
likes: note.interact_info?.liked_count || '0',
url: `https://explore/${note.note_id || note.id}`
});
}
}
}
return results.slice(0, 20).map((item, i) => ({
rank: i + 1, ...item,
}));
},
});拦截核心思路:不自己构造签名,而是利用installInterceptor劫持网站自己的XMLHttpRequest和fetch,让网站发请求,我们直接在底层取出解析好的response.json()。
级联请求(如 BVID→CID→字幕)的完整模板和要点见下方进阶模式: 级联请求章节。
---
Step 4: 测试
构建通过 ≠ 功能正常。npm run build 只验证 TypeScript / YAML 语法,不验证运行时行为。每个新命令 必须实际运行 并确认输出正确后才算完成。
必做清单
# 1. 构建(确认语法无误)
npm run build
# 2. 确认命令已注册
opencli list | grep mysite
# 3. 实际运行命令(最关键!)
opencli mysite hot --limit 3 -v # verbose 查看每步数据流
opencli mysite hot --limit 3 -f json # JSON 输出确认字段完整tap 步骤调试(intercept 策略专用)
不要猜 store name / action name。先用 evaluate 探索,再写 YAML。
Step 1: 列出所有 Pinia store
在浏览器中打开目标网站后:
opencli evaluate "(() => {
const app = document.querySelector('#app')?.__vue_app__;
const pinia = app?.config?.globalProperties?.\$pinia;
return [...pinia._s.keys()];
})()"
# 输出: ["user", "feed", "search", "notification", ...]Step 2: 查看 store 的 action 名称
故意写一个错误 action 名,tap 会返回所有可用 actions:
⚠ tap: Action not found: wrongName on store notification
💡 Available: getNotification, replyComment, getNotificationCount, resetStep 3: 用 network requests 确认 capture 模式
# 在浏览器打开目标页面,查看网络请求
# 找到目标 API 的 URL 特征(如 "/you/mentions"、"homefeed")完整流程
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────┐
│ 1. navigate │ ──▶ │ 2. 探索 store │ ──▶ │ 3. 写 YAML │ ──▶ │ 4. 测试 │
│ 到目标页面 │ │ name/action │ │ tap 步骤 │ │ 运行验证 │
└──────────────┘ └──────────────┘ └──────────────┘ └────────┘Verbose 模式 & 输出验证
opencli bilibili hot --limit 1 -v # 查看 pipeline 每步数据流
opencli mysite hot -f json | jq '.[0]' # 确认 JSON 可被解析
opencli mysite hot -f csv > data.csv # 确认 CSV 可导入---
Step 5: 提交发布
文件放入 src/clis/<site>/ 即自动注册(YAML 或 TS 无需手动 import),然后:
opencli list | grep mysite # 确认注册
git add src/clis/mysite/ && git commit -m "feat(mysite): add hot" && git push架构理念:OpenCLI 内建 Zero-Dependency jq 数据流 — 所有解析在evaluate的原生 JS 内完成,外层 YAML 用select/map提取,无需依赖系统jq二进制。
---
进阶模式: 级联请求 (Cascading Requests)
当目标数据需要多步 API 链式获取时(如 BVID → CID → 字幕列表 → 字幕内容),必须使用 TS 适配器。YAML 无法处理这种多步逻辑。
模板代码
import { cli, Strategy } from '../../registry.js';
import type { IPage } from '../../types.js';
import { apiGet } from './utils.js'; // 复用平台 SDK
cli({
site: 'bilibili',
name: 'subtitle',
strategy: Strategy.COOKIE,
args: [{ name: 'bvid', required: true }],
columns: ['index', 'from', 'to', 'content'],
func: async (page: IPage | null, kwargs: any) => {
if (!page) throw new Error('Requires browser');
// Step 1: 建立 Session
await page.goto(`https://www.bilibili.com/video/${kwargs.bvid}/`);
// Step 2: 从页面提取中间 ID (__INITIAL_STATE__)
const cid = await page.evaluate(`(async () => {
return window.__INITIAL_STATE__?.videoData?.cid;
})()`);
if (!cid) throw new Error('无法提取 CID');
// Step 3: 用中间 ID 调用下一级 API (自动 Wbi 签名)
const payload = await apiGet(page, '/x/player/wbi/v2', {
params: { bvid: kwargs.bvid, cid },
signed: true, // ← 自动生成 w_rid
});
// Step 4: 检测风控降级 (空值断言)
const subtitles = payload.data?.subtitle?.subtitles || [];
const url = subtitles[0]?.subtitle_url;
if (!url) throw new Error('subtitle_url 为空,疑似风控降级');
// Step 5: 拉取最终数据 (CDN JSON)
const items = await page.evaluate(`(async () => {
const res = await fetch(${JSON.stringify('https:' + url)});
const json = await res.json();
return { data: json.body || json };
})()`);
return items.data.map((item, idx) => ({ ... }));
},
});关键要点
| 步骤 | 注意事项 |
|---|---|
| 提取中间 ID | 优先从 __INITIAL_STATE__ 拿,避免额外 API 调用 |
| Wbi 签名 | B 站 /wbi/ 接口强制校验 w_rid,纯 fetch 会被 403 |
| 空值断言 | 即使 HTTP 200,核心字段可能为空串(风控降级) |
| CDN URL | 常以 // 开头,记得补 https: |
JSON.stringify | 拼接 URL 到 evaluate 时必须用它转义,避免注入 |
---
常见陷阱
| 陷阱 | 表现 | 解决方案 |
|---|---|---|
缺少 navigate | evaluate 报 Target page context 错误 | 在 evaluate 前加 navigate: 步骤 |
| 嵌套字段访问 | ${{ item.node?.title }} 不工作 | 在 evaluate 中 flatten 数据,不在模板中用 optional chaining |
缺少 strategy: public | 公开 API 也启动浏览器,7s → 1s | 公开 API 加上 strategy: public + browser: false |
| evaluate 返回字符串 | map 步骤收到 "" 而非数组 | pipeline 有 auto-parse,但建议在 evaluate 内 .map() 整形 |
| 搜索参数被 URL 编码 | ${{ args.query }} 被浏览器二次编码 | 在 evaluate 内用 encodeURIComponent() 手动编码 |
| Cookie 过期 | 返回 401 / 空数据 | 在浏览器里重新登录目标站点 |
| Extension tab 残留 | Chrome 多出 chrome-extension:// tab | 已自动清理;若残留,手动关闭即可 |
| TS evaluate 格式 | () => {} 报 result is not a function | TS 中 page.evaluate() 必须用 IIFE:(async () => { ... })() |
| 页面异步加载 | evaluate 拿到空数据(store state 还没更新) | 在 evaluate 内用 polling 等待数据出现,或增加 wait 时间 |
| YAML 内嵌大段 JS | 调试困难,字符串转义问题 | 超过 10 行 JS 的命令改用 TS adapter |
| 风控被拦截(伪200) | 获取到的 JSON 里核心数据是 "" (空串) | 极易被误判。必须添加断言!无核心数据立刻要求升级鉴权 Tier 并重新配置 Cookie |
| API 没找见 | explore 工具打分出来的都拿不到深层数据 | 点击页面按钮诱发懒加载数据,再结合 getInterceptedRequests 获取 |
---
用 AI Agent 自动生成适配器
最快的方式是让 AI Agent 完成全流程:
# 一键:探索 → 分析 → 合成 → 注册
opencli generate https://www.example.com --goal "hot"
# 或分步执行:
opencli explore https://www.example.com --site mysite # 发现 API
opencli explore https://www.example.com --auto --click "字幕,CC" # 模拟点击触发懒加载 API
opencli synthesize mysite # 生成候选 YAML
opencli verify mysite/hot --smoke # 冒烟测试生成的候选 YAML 保存在 .opencli/explore/mysite/candidates/,可直接复制到 src/clis/mysite/ 并微调。
CLI-ONESHOT — 单点快速 CLI 生成
给一个 URL + 一句话描述,4 步生成一个 CLI 命令。
完整探索式开发请看 CLI-EXPLORER.md。
---
输入
| 项目 | 示例 |
|---|---|
| URL | https://x.com/jakevin7/lists |
| Goal | 获取我的 Twitter Lists |
---
流程
Step 1: 打开页面 + 抓包
1. browser_navigate → 打开目标 URL
2. 等待 3-5 秒(让页面加载完、API 请求触发)
3. browser_network_requests → 筛选 JSON API关键:只关注返回 application/json 的请求,忽略静态资源。 如果没有自动触发 API,手动点击目标按钮/标签再抓一次。
Step 2: 锁定一个接口
从抓包结果中找到那个目标 API。看这几个字段:
| 字段 | 关注什么 |
|---|---|
| URL | API 路径 pattern(如 /i/api/graphql/xxx/ListsManagePinTimeline) |
| Method | GET / POST |
| Headers | 有 Cookie? Bearer? CSRF? 自定义签名? |
| Response | 数据在哪个路径(如 data.list.lists) |
Step 3: 验证接口能复现
在 browser_evaluate 中用 fetch 复现请求:
// Tier 2 (Cookie): 大多数情况
fetch('/api/endpoint', { credentials: 'include' }).then(r => r.json())
// Tier 3 (Header): 如 Twitter 需要额外 header
const ct0 = document.cookie.match(/ct0=([^;]+)/)?.[1];
fetch('/api/endpoint', {
headers: { 'Authorization': 'Bearer ...', 'X-Csrf-Token': ct0 },
credentials: 'include'
}).then(r => r.json())如果 fetch 能拿到数据 → 用 YAML 或简单 TS adapter。 如果 fetch 拿不到(签名/风控)→ 用 intercept 策略。
Step 4: 套模板,生成 adapter
根据 Step 3 判定的策略,选一个模板生成文件。
---
认证速查
fetch(url) 直接能拿到? → Tier 1: public (YAML, browser: false)
fetch(url, {credentials:'include'})? → Tier 2: cookie (YAML)
加 Bearer/CSRF header 后拿到? → Tier 3: header (TS)
都不行,但页面自己能请求成功? → Tier 4: intercept (TS, installInterceptor)---
模板
YAML — Cookie/Public(最简)
# src/clis/<site>/<name>.yaml
site: mysite
name: mycommand
description: "一句话描述"
domain: www.example.com
strategy: cookie # 或 public (加 browser: false)
args:
limit:
type: int
default: 20
pipeline:
- navigate: https://www.example.com/target-page
- evaluate: |
(async () => {
const res = await fetch('/api/target', { credentials: 'include' });
const d = await res.json();
return (d.data?.items || []).map(item => ({
title: item.title,
value: item.value,
}));
})()
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
value: ${{ item.value }}
- limit: ${{ args.limit }}
columns: [rank, title, value]TS — Intercept(抓包模式)
// src/clis/<site>/<name>.ts
import { cli, Strategy } from '../../registry.js';
cli({
site: 'mysite',
name: 'mycommand',
description: '一句话描述',
domain: 'www.example.com',
strategy: Strategy.INTERCEPT,
browser: true,
args: [
{ name: 'limit', type: 'int', default: 20 },
],
columns: ['rank', 'title', 'value'],
func: async (page, kwargs) => {
// 1. 导航
await page.goto('https://www.example.com/target-page');
await page.wait(3);
// 2. 注入拦截器(URL 子串匹配)
await page.installInterceptor('target-api-keyword');
// 3. 触发 API(滚动/点击)
await page.autoScroll({ times: 2, delayMs: 2000 });
// 4. 读取拦截的响应
const requests = await page.getInterceptedRequests();
if (!requests?.length) return [];
let results: any[] = [];
for (const req of requests) {
const items = req.data?.data?.items || [];
results.push(...items);
}
return results.slice(0, kwargs.limit).map((item, i) => ({
rank: i + 1,
title: item.title || '',
value: item.value || '',
}));
},
});TS — Header(如 Twitter GraphQL)
import { cli, Strategy } from '../../registry.js';
cli({
site: 'twitter',
name: 'mycommand',
description: '一句话描述',
domain: 'x.com',
strategy: Strategy.HEADER,
browser: true,
args: [
{ name: 'limit', type: 'int', default: 20 },
],
columns: ['rank', 'name', 'value'],
func: async (page, kwargs) => {
await page.goto('https://x.com');
const data = await page.evaluate(`(async () => {
const ct0 = document.cookie.match(/ct0=([^;]+)/)?.[1];
if (!ct0) return { error: 'Not logged in' };
const bearer = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D...';
const res = await fetch('/i/api/graphql/QUERY_ID/Endpoint', {
headers: {
'Authorization': 'Bearer ' + decodeURIComponent(bearer),
'X-Csrf-Token': ct0,
'X-Twitter-Auth-Type': 'OAuth2Session',
},
credentials: 'include',
});
return res.json();
})()`);
// 解析 data...
return [];
},
});---
测试(必做)
npm run build # 语法检查
opencli list | grep mysite # 确认注册
opencli mysite mycommand --limit 3 -v # 实际运行---
就这样,没了
写完文件 → build → run → 提交。有问题再看 CLI-EXPLORER.md。
Contributing to OpenCLI
Thanks for your interest in contributing to OpenCLI.
Quick Start
# 1. Fork & clone
git clone git@github.com:<your-username>/opencli.git
cd opencli
# 2. Install dependencies
npm install
# 3. Build
npm run build
# 4. Run a few checks
npx tsc --noEmit
npm test
npm run test:adapter
# 5. Link globally (optional, for testing `opencli` command)
npm linkAdding a New Site Adapter
This is the most common type of contribution. Start with YAML when possible, and use TypeScript only when you need browser-side logic or multi-step flows.
YAML Adapter (Recommended for data-fetching commands)
Create a file like src/clis/<site>/<command>.yaml:
site: mysite
name: trending
description: Trending posts on MySite
domain: www.mysite.com
strategy: public # public | cookie | header
browser: false # true if browser session is needed
args:
query:
positional: true
type: str
required: true
description: Search keyword
limit:
type: int
default: 20
description: Number of items
pipeline:
- fetch:
url: https://api.mysite.com/trending
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
score: ${{ item.score }}
url: ${{ item.url }}
- limit: ${{ args.limit }}
columns: [rank, title, score, url]See `hackernews/top.yaml` for a real example.
TypeScript Adapter (For complex browser interactions)
Create a file like src/clis/<site>/<command>.ts:
import { cli, Strategy } from '../../registry.js';
cli({
site: 'mysite',
name: 'search',
description: 'Search MySite',
domain: 'www.mysite.com',
strategy: Strategy.COOKIE,
args: [
{ name: 'query', positional: true, required: true, help: 'Search query' },
{ name: 'limit', type: 'int', default: 10, help: 'Max results' },
],
columns: ['title', 'url', 'date'],
func: async (page, kwargs) => {
const { query, limit = 10 } = kwargs;
await page.goto('https://www.mysite.com');
const data = await page.evaluate(`
(async () => {
const res = await fetch('/api/search?q=${encodeURIComponent(query)}', {
credentials: 'include'
});
return (await res.json()).results;
})()
`);
return data.slice(0, Number(limit)).map((item: any) => ({
title: item.title,
url: item.url,
date: item.created_at,
}));
},
});Use opencli explore <url> to discover APIs and see CLI-EXPLORER.md if you need the full adapter workflow.
Validate Your Adapter
# Validate YAML syntax and schema
opencli validate
# Test your command
opencli <site> <command> --limit 3 -f json
# Verbose mode for debugging
opencli <site> <command> -vArg Design Convention
Use positional for the primary, required argument of a command (the "what" — query, symbol, id, url, username). Use named options (--flag) for secondary/optional configuration (limit, format, sort, page, filters, language, date).
Rule of thumb: Think about how the user will type the command. opencli xueqiu stock SH600519 is more natural than opencli xueqiu stock --symbol SH600519.
| Arg type | Positional? | Examples |
|---|---|---|
| Main target (query, symbol, id, url, username) | ✅ positional: true | search '茅台', stock SH600519, download BV1xxx |
| Configuration (limit, format, sort, page, type, filters) | ❌ Named --flag | --limit 10, --format json, --sort hot, --location seattle |
Do not convert an argument to positional just because it appears first in the file. If the argument is optional, acts like a filter, or selects a mode/configuration, it should usually stay a named option.
YAML example:
args:
query:
positional: true # ← primary arg, user types it directly
type: str
required: true
limit:
type: int # ← config arg, user types --limit 10
default: 20TS example:
args: [
{ name: 'query', positional: true, required: true, help: 'Search query' },
{ name: 'limit', type: 'int', default: 10, help: 'Max results' },
]Testing
See TESTING.md for the full guide and exact test locations.
npm test # Core unit tests (non-adapter)
npm run test:adapter # Focused adapter tests: zhihu/twitter/reddit/bilibili
npx vitest run tests/e2e/ # E2E tests
npx vitest run # All testsCode Style
- TypeScript strict mode — avoid
anywhere possible. - ES Modules — use
.jsextensions in imports (TypeScript output). - Naming:
kebab-casefor files,camelCasefor variables/functions,PascalCasefor types/classes. - No default exports — use named exports.
Commit Convention
We use Conventional Commits:
feat(twitter): add thread command
fix(browser): handle CDP timeout gracefully
docs: update CONTRIBUTING.md
test(reddit): add e2e test for save command
chore: bump vitest to v4Common scopes: site name (twitter, reddit) or module name (browser, pipeline, engine).
Submitting a Pull Request
1. Create a feature branch: git checkout -b feat/mysite-trending 2. Make your changes and add tests when relevant 3. Run the checks that apply:
npx tsc --noEmit # Type check
npm test # Core unit tests
npm run test:adapter # Focused adapter tests (if you touched adapter logic)
opencli validate # YAML validation (if applicable)4. Commit using conventional commit format 5. Push and open a PR
License
By contributing, you agree that your contributions will be licensed under the Apache-2.0 License.
import { defineConfig } from 'vitepress'
export default defineConfig({
base: '/docs/',
title: 'OpenCLI',
description: 'Make any website or Electron App your CLI — AI-powered, account-safe, self-healing.',
head: [
['meta', { property: 'og:title', content: 'OpenCLI Documentation' }],
['meta', { property: 'og:description', content: 'Make any website or Electron App your CLI.' }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
],
locales: {
root: {
label: 'English',
lang: 'en',
themeConfig: {
nav: [
{ text: 'Guide', link: '/guide/getting-started' },
{ text: 'Adapters', link: '/adapters/' },
{ text: 'Developer', link: '/developer/contributing' },
{ text: 'Advanced', link: '/advanced/cdp' },
],
sidebar: {
'/guide/': [
{
text: 'Guide',
items: [
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Installation', link: '/guide/installation' },
{ text: 'Comparison', link: '/comparison' },
{ text: 'Browser Bridge', link: '/guide/browser-bridge' },
{ text: 'Troubleshooting', link: '/guide/troubleshooting' },
{ text: 'Add an Electron App CLI', link: '/guide/electron-app-cli' },
{ text: 'Plugins', link: '/guide/plugins' },
],
},
],
'/adapters/': [
{
text: 'Adapters Overview',
items: [
{ text: 'All Adapters', link: '/adapters/' },
],
},
{
text: 'Browser Adapters',
collapsed: false,
items: [
{ text: 'Twitter / X', link: '/adapters/browser/twitter' },
{ text: 'Reddit', link: '/adapters/browser/reddit' },
{ text: 'Tieba', link: '/adapters/browser/tieba' },
{ text: 'Bilibili', link: '/adapters/browser/bilibili' },
{ text: 'Zhihu', link: '/adapters/browser/zhihu' },
{ text: 'Xiaohongshu', link: '/adapters/browser/xiaohongshu' },
{ text: 'Weibo', link: '/adapters/browser/weibo' },
{ text: 'YouTube', link: '/adapters/browser/youtube' },
{ text: 'Xueqiu', link: '/adapters/browser/xueqiu' },
{ text: 'V2EX', link: '/adapters/browser/v2ex' },
{ text: 'Bloomberg', link: '/adapters/browser/bloomberg' },
{ text: 'LinkedIn', link: '/adapters/browser/linkedin' },
{ text: 'Coupang', link: '/adapters/browser/coupang' },
{ text: 'BOSS Zhipin', link: '/adapters/browser/boss' },
{ text: 'Ctrip', link: '/adapters/browser/ctrip' },
{ text: 'Reuters', link: '/adapters/browser/reuters' },
{ text: 'SMZDM', link: '/adapters/browser/smzdm' },
{ text: 'Jike', link: '/adapters/browser/jike' },
{ text: 'Jimeng', link: '/adapters/browser/jimeng' },
{ text: 'Yollomi', link: '/adapters/browser/yollomi' },
{ text: 'LINUX DO', link: '/adapters/browser/linux-do' },
{ text: 'Band', link: '/adapters/browser/band' },
{ text: 'Chaoxing', link: '/adapters/browser/chaoxing' },
{ text: 'Grok', link: '/adapters/browser/grok' },
{ text: 'NotebookLM', link: '/adapters/browser/notebooklm' },
{ text: 'WeRead', link: '/adapters/browser/weread' },
{ text: 'Douban', link: '/adapters/browser/douban' },
{ text: 'Sina Blog', link: '/adapters/browser/sinablog' },
{ text: 'Substack', link: '/adapters/browser/substack' },
{ text: 'Pixiv', link: '/adapters/browser/pixiv' },
{ text: 'Douban', link: '/adapters/browser/douban' },
{ text: 'Doubao', link: '/adapters/browser/doubao' },
{ text: 'Facebook', link: '/adapters/browser/facebook' },
{ text: 'Google', link: '/adapters/browser/google' },
{ text: 'IMDb', link: '/adapters/browser/imdb' },
{ text: 'Instagram', link: '/adapters/browser/instagram' },
{ text: 'JD.com', link: '/adapters/browser/jd' },
{ text: 'Medium', link: '/adapters/browser/medium' },
{ text: 'TikTok', link: '/adapters/browser/tiktok' },
{ text: 'Web (Generic)', link: '/adapters/browser/web' },
{ text: 'Weixin', link: '/adapters/browser/weixin' },
],
},
{
text: 'Public API Adapters',
collapsed: false,
items: [
{ text: 'HackerNews', link: '/adapters/browser/hackernews' },
{ text: 'Dev.to', link: '/adapters/browser/devto' },
{ text: 'Dictionary', link: '/adapters/browser/dictionary' },
{ text: 'BBC', link: '/adapters/browser/bbc' },
{ text: 'Apple Podcasts', link: '/adapters/browser/apple-podcasts' },
{ text: 'Xiaoyuzhou', link: '/adapters/browser/xiaoyuzhou' },
{ text: 'Yahoo Finance', link: '/adapters/browser/yahoo-finance' },
{ text: 'arXiv', link: '/adapters/browser/arxiv' },
{ text: 'paperreview.ai', link: '/adapters/browser/paperreview' },
{ text: 'Barchart', link: '/adapters/browser/barchart' },
{ text: 'Hugging Face', link: '/adapters/browser/hf' },
{ text: 'Sina Finance', link: '/adapters/browser/sinafinance' },
{ text: 'Spotify', link: '/adapters/browser/spotify' },
{ text: 'Stack Overflow', link: '/adapters/browser/stackoverflow' },
{ text: 'Wikipedia', link: '/adapters/browser/wikipedia' },
{ text: 'Lobsters', link: '/adapters/browser/lobsters' },
{ text: 'Steam', link: '/adapters/browser/steam' },
],
},
{
text: 'Desktop Adapters',
collapsed: false,
items: [
{ text: 'Cursor', link: '/adapters/desktop/cursor' },
{ text: 'Codex', link: '/adapters/desktop/codex' },
{ text: 'Antigravity', link: '/adapters/desktop/antigravity' },
{ text: 'ChatGPT', link: '/adapters/desktop/chatgpt' },
{ text: 'ChatWise', link: '/adapters/desktop/chatwise' },
{ text: 'Notion', link: '/adapters/desktop/notion' },
{ text: 'Discord', link: '/adapters/desktop/discord' },
{ text: 'Doubao App', link: '/adapters/desktop/doubao-app' },
],
},
],
'/developer/': [
{
text: 'Developer Guide',
items: [
{ text: 'Contributing', link: '/developer/contributing' },
{ text: 'Testing', link: '/developer/testing' },
{ text: 'Architecture', link: '/developer/architecture' },
{ text: 'YAML Adapter Guide', link: '/developer/yaml-adapter' },
{ text: 'TypeScript Adapter Guide', link: '/developer/ts-adapter' },
{ text: 'AI Workflow', link: '/developer/ai-workflow' },
],
},
],
'/advanced/': [
{
text: 'Advanced',
items: [
{ text: 'Chrome DevTools Protocol', link: '/advanced/cdp' },
{ text: 'Electron Apps', link: '/advanced/electron' },
{ text: 'Remote Chrome', link: '/advanced/remote-chrome' },
{ text: 'Download Support', link: '/advanced/download' },
],
},
],
},
},
},
zh: {
label: '中文',
lang: 'zh-CN',
link: '/zh/',
themeConfig: {
nav: [
{ text: '指南', link: '/zh/guide/getting-started' },
{ text: '适配器', link: '/zh/adapters/' },
{ text: '开发者', link: '/zh/developer/contributing' },
{ text: '进阶', link: '/zh/advanced/cdp' },
],
sidebar: {
'/zh/guide/': [
{
text: '指南',
items: [
{ text: '快速开始', link: '/zh/guide/getting-started' },
{ text: '安装', link: '/zh/guide/installation' },
{ text: 'Browser Bridge', link: '/zh/guide/browser-bridge' },
{ text: '给新 Electron 应用生成 CLI', link: '/zh/guide/electron-app-cli' },
{ text: '插件', link: '/zh/guide/plugins' },
],
},
],
'/zh/adapters/': [
{
text: '适配器概览',
items: [
{ text: '所有适配器', link: '/zh/adapters/' },
],
},
],
'/zh/developer/': [
{
text: '开发者指南',
items: [
{ text: '贡献指南', link: '/zh/developer/contributing' },
],
},
],
'/zh/advanced/': [
{
text: '进阶',
items: [
{ text: 'Chrome DevTools Protocol', link: '/zh/advanced/cdp' },
],
},
],
},
},
},
},
themeConfig: {
search: {
provider: 'local',
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/jackwener/opencli' },
{ icon: 'npm', link: 'https://www.npmjs.com/package/@jackwener/opencli' },
],
editLink: {
pattern: 'https://github.com/jackwener/opencli/edit/main/docs/:path',
text: 'Edit this page on GitHub',
},
footer: {
message: 'Released under the Apache-2.0 License.',
copyright: 'Copyright © 2024-present jackwener',
},
},
})
ONES 项目管理平台(OpenCLI)
基于官方 ONES Project API,经 Chrome + Browser Bridge 在页面里 fetch(credentials: 'include')。
环境变量
| 变量 | 必填 | 说明 |
|---|---|---|
ONES_BASE_URL | 是 | 与 Chrome 中访问的 ONES 根 URL 一致 |
ONES_USER_ID / ONES_AUTH_TOKEN | 视部署 | 若接口强制要文档中的 Header,再设置(可先只依赖浏览器登录) |
ONES_EMAIL / ONES_PHONE / ONES_PASSWORD | 否 | 供 ones login 脚本化 |
命令
export ONES_BASE_URL=https://your-host
# 安装扩展,Chrome 已登录 ONES
opencli ones me
opencli ones token-info # teams column includes name(uuid), useful for tasks
opencli ones tasks <teamUUID> --limit 20 --project <optional>
opencli ones my-tasks <teamUUID> --limit 100 # default assignee=self
opencli ones my-tasks <teamUUID> --mode field004 # deployments using field004 as assignee
opencli ones my-tasks <teamUUID> --mode both # assignee OR creator
opencli ones task <taskUUID> --team <teamUUID> # single task (URL .../task/<uuid>)
opencli ones worklog <taskUUID> 2 --team <teamUUID> # log hours for today
opencli ones worklog <taskUUID> 1 --team <teamUUID> --date 2026-03-01 # backfill
opencli ones login --email you@corp.com --password '***' # optional; stderr prints header export hints
opencli ones logout更完整的说明见 docs/adapters/browser/ones.md。
36kr (36氪)
Mode: 🌐 Public / 🔐 Browser · Domain: 36kr.com
Commands
| Command | Description |
|---|---|
opencli 36kr hot | 36氪热榜 — trending articles |
opencli 36kr news | Latest tech/startup news from 36kr |
opencli 36kr search <query> | Search 36kr articles |
opencli 36kr article <id-or-url> | Read full article content |
Usage Examples
# Trending articles
opencli 36kr hot --limit 10
# Hot by type
opencli 36kr hot --type renqi --limit 10
opencli 36kr hot --type zonghe --limit 10
# Latest news
opencli 36kr news --limit 20
# Search articles
opencli 36kr search "AI" --limit 10
opencli 36kr search "OpenAI" --limit 5
# Read full article (by ID or URL)
opencli 36kr article 3000000123456
opencli 36kr article https://36kr.com/p/3000000123456
# JSON output
opencli 36kr hot -f jsonNotes
newsuses the public RSS feed and works without Browser Bridge.hot,search, andarticleuse Browser Bridge and are best run with Chrome open.hot --typeacceptscatalog,renqi,zonghe, andshoucang.
Prerequisites
- No browser required — uses public API
Apple Podcasts
Mode: 🌐 Public · Domain: podcasts.apple.com
Commands
| Command | Description |
|---|---|
opencli apple-podcasts search | |
opencli apple-podcasts episodes | |
opencli apple-podcasts top |
Usage Examples
# Quick start
opencli apple-podcasts search --limit 5
# JSON output
opencli apple-podcasts search -f json
# Verbose mode
opencli apple-podcasts search -vPrerequisites
- No browser required — uses public API
arXiv
Mode: 🌐 Public · Domain: arxiv.org
Commands
| Command | Description |
|---|---|
opencli arxiv search | Search arXiv papers |
opencli arxiv paper | Get arXiv paper details by ID |
Usage Examples
# Search for papers
opencli arxiv search "transformer attention" --limit 10
# Get paper details by arXiv ID
opencli arxiv paper 2301.00001
# JSON output
opencli arxiv search "LLM" -f jsonPrerequisites
- No browser required — uses public arXiv API
Band
Mode: 🔐 Browser · Domain: www.band.us
Read posts, comments, and notifications from Band, a private community platform. Authentication uses your logged-in Chrome session (cookie-based).
Commands
| Command | Description |
|---|---|
opencli band bands | List all Bands you belong to |
opencli band posts <band_no> | List posts from a Band |
opencli band post <band_no> <post_no> | Export full post content including nested comments |
opencli band mentions | Show notifications where you were @mentioned |
Usage Examples
# List all your bands (get band_no from here)
opencli band bands
# List recent posts in a band
opencli band posts 12345678 --limit 10
# Export a post with comments
opencli band post 12345678 987654321
# Export post body only (skip comments)
opencli band post 12345678 987654321 --comments false
# Export post and download attached photos
opencli band post 12345678 987654321 --output ./band-photos
# Show recent @mention notifications
opencli band mentions --limit 20
# Show only unread mentions
opencli band mentions --unread true
# Show all notification types
opencli band mentions --filter allband mentions filter options
| Filter | Description |
|---|---|
mentioned | Only notifications where you were @mentioned (default) |
all | All notifications |
post | Post-related notifications |
comment | Comment-related notifications |
Prerequisites
- Chrome running and logged into band.us
- Browser Bridge extension installed
Notes
band_nois the numeric ID in the Band URL:band.us/band/{band_no}/postband bandslists all your bands with theirband_novaluesband postoutput rows:type=post(the post itself),type=comment(top-level comment),type=reply(nested reply)- Photo downloads use the full-resolution URL (thumbnail query params are stripped automatically)
Barchart
Mode: 🔐 Browser · Domain: barchart.com
Commands
| Command | Description |
|---|---|
opencli barchart quote | Stock quote with price, volume, and key metrics |
opencli barchart options | Options chain with greeks, IV, volume, and open interest |
opencli barchart greeks | Options greeks overview (IV, delta, gamma, theta, vega) |
opencli barchart flow | Unusual options activity / options flow |
Usage Examples
# Get stock quote
opencli barchart quote AAPL
# View options chain
opencli barchart options TSLA
# Options greeks overview
opencli barchart greeks NVDA
# Unusual options flow
opencli barchart flow --limit 20 -f jsonPrerequisites
- Chrome running and able to open
barchart.com - Browser Bridge extension installed
BBC News
Mode: 🌐 Public · Domain: bbc.com
Commands
| Command | Description |
|---|---|
opencli bbc news |
Usage Examples
# Quick start
opencli bbc news --limit 5
# JSON output
opencli bbc news -f json
# Verbose mode
opencli bbc news -vPrerequisites
- No browser required — uses public API
Bilibili
Mode: 🔐 Browser · Domain: bilibili.com
Commands
| Command | Description |
|---|---|
opencli bilibili hot | |
opencli bilibili search | |
opencli bilibili me | |
opencli bilibili favorite | |
opencli bilibili history | |
opencli bilibili feed | |
opencli bilibili subtitle | |
opencli bilibili dynamic | |
opencli bilibili ranking | |
opencli bilibili following | |
opencli bilibili user-videos | |
opencli bilibili download |
Usage Examples
# Quick start
opencli bilibili hot --limit 5
# Search videos
opencli bilibili search 黑神话 --limit 10
# Read one creator's videos
opencli bilibili user-videos 2 --limit 10
# Fetch subtitles
opencli bilibili subtitle BV1xx411c7mD --lang zh-CN
# JSON output
opencli bilibili hot -f json
# Verbose mode
opencli bilibili hot -vPrerequisites
- Chrome running and logged into bilibili.com
- Browser Bridge extension installed
Bloomberg
Mode: 🌐 / 🔐 Mixed · Domains: feeds.bloomberg.com, www.bloomberg.com
Commands
| Command | Description |
|---|---|
opencli bloomberg main | Bloomberg homepage top stories from RSS |
opencli bloomberg markets | Bloomberg Markets top stories from RSS |
opencli bloomberg economics | Bloomberg Economics top stories from RSS |
opencli bloomberg industries | Bloomberg Industries top stories from RSS |
opencli bloomberg tech | Bloomberg Tech top stories from RSS |
opencli bloomberg politics | Bloomberg Politics top stories from RSS |
opencli bloomberg businessweek | Bloomberg Businessweek top stories from RSS |
opencli bloomberg opinions | Bloomberg Opinion top stories from RSS |
opencli bloomberg feeds | List the RSS feed aliases used by the adapter |
opencli bloomberg news <link> | Read a standard Bloomberg story/article page and return title, summary, media links, and article text |
What works today
- RSS-backed listing commands work without a browser:
mainmarketseconomicsindustriestechpoliticsbusinessweekopinionsfeedsbloomberg newsworks on standard Bloomberg story/article pages that expose#__NEXT_DATA__and are accessible to your current Chrome session.
Current limitations
- Audio pages and some other non-standard Bloomberg URLs may fail.
- Some Bloomberg pages can return bot-protection or access-gated responses instead of article data.
- This adapter is for data retrieval/extraction only. It does not bypass Bloomberg paywall, login, entitlement, or other access checks.
Usage Examples
# List supported RSS feed aliases
opencli bloomberg feeds
# Fetch Bloomberg homepage headlines
opencli bloomberg main --limit 5
# Fetch a section feed as JSON
opencli bloomberg tech --limit 3 -f json
# Read a standard article page
opencli bloomberg news https://www.bloomberg.com/news/articles/2026-03-19/example -f json
# Relative article paths also work
opencli bloomberg news /news/articles/2026-03-19/examplePrerequisites
- RSS commands do not require Chrome.
bloomberg newsrequires:- Chrome running
- a Chrome session that can already access the target Bloomberg article page
- the Browser Bridge extension
Notes
- RSS commands support
--limitwith a maximum of 20 items. - If
bloomberg newsfails on a page from RSS, try a different standard story/article link first; not every Bloomberg URL in feeds is a normal article page.
Bluesky
Mode: 🌐 Public · Domain: bsky.app
Commands
| Command | Description |
|---|---|
opencli bluesky profile | User profile info |
opencli bluesky user | Recent posts from a user |
opencli bluesky trending | Trending topics |
opencli bluesky search | Search users |
opencli bluesky feeds | Popular feed generators |
opencli bluesky followers | User's followers |
opencli bluesky following | Accounts a user follows |
opencli bluesky thread | Post thread with replies |
opencli bluesky starter-packs | User's starter packs |
Usage Examples
# User profile
opencli bluesky profile --handle bsky.app
# Recent posts
opencli bluesky user --handle bsky.app --limit 10
# Trending topics
opencli bluesky trending --limit 10
# Search users
opencli bluesky search --query "AI" --limit 10
# Popular feeds
opencli bluesky feeds --limit 10
# Followers / following
opencli bluesky followers --handle bsky.app --limit 10
opencli bluesky following --handle bsky.app
# Post thread with replies
opencli bluesky thread --uri "at://did:.../app.bsky.feed.post/..."
# Starter packs
opencli bluesky starter-packs --handle bsky.app
# JSON output
opencli bluesky profile --handle bsky.app -f jsonPrerequisites
None — all commands use the public Bluesky AT Protocol API, no browser or login required.
BOSS Zhipin
Mode: 🔐 Browser · Domain: zhipin.com
Commands
| Command | Description |
|---|---|
opencli boss search | |
opencli boss detail |
Usage Examples
# Quick start
opencli boss search --limit 5
# JSON output
opencli boss search -f json
# Verbose mode
opencli boss search -vPrerequisites
- Chrome running and logged into zhipin.com
- Browser Bridge extension installed
超星学习通 (Chaoxing)
Mode: 🔐 Browser · Domain: mooc2-ans.chaoxing.com
Commands
| Command | Description |
|---|---|
opencli chaoxing assignments | 学习通作业列表 |
opencli chaoxing exams | 学习通考试列表 |
Usage Examples
# List all assignments
opencli chaoxing assignments --limit 20
# Filter exams by course name
opencli chaoxing exams --course "高等数学"
# Filter exams by status
opencli chaoxing exams --status ongoing
# JSON output
opencli chaoxing assignments -f jsonOptions
| Option | Description |
|---|---|
--course | Filter by course name (fuzzy match) |
--status | Filter by status: all, upcoming, ongoing, finished |
--limit | Max number of results (default: 20) |
Prerequisites
- Chrome running and logged into mooc2-ans.chaoxing.com
- Browser Bridge extension installed
Coupang
Mode: 🔐 Browser · Domain: coupang.com
Commands
| Command | Description |
|---|---|
opencli coupang search | |
opencli coupang add-to-cart |
Usage Examples
# Quick start
opencli coupang search --limit 5
# JSON output
opencli coupang search -f json
# Verbose mode
opencli coupang search -vPrerequisites
- Chrome running and logged into coupang.com
- Browser Bridge extension installed
Ctrip (携程)
Mode: 🔐 Browser · Domain: ctrip.com
Commands
| Command | Description |
|---|---|
opencli ctrip search |
Usage Examples
# Quick start
opencli ctrip search --limit 5
# JSON output
opencli ctrip search -f json
# Verbose mode
opencli ctrip search -vPrerequisites
- Chrome running and logged into ctrip.com
- Browser Bridge extension installed
Dev.to
Mode: 🌐 Public · Domain: dev.to
Fetch the latest and greatest developer articles from the DEV community without needing an API key.
Commands
| Command | Description |
|---|---|
opencli devto top | Top DEV.to articles of the day |
opencli devto tag | Latest articles for a specific tag |
opencli devto user | Recent articles from a specific user |
Usage Examples
# Top articles today
opencli devto top --limit 5
# Articles by tag (positional argument)
opencli devto tag javascript
opencli devto tag python --limit 20
# Articles by a specific author
opencli devto user ben
opencli devto user thepracticaldev --limit 5
# JSON output
opencli devto top -f jsonPrerequisites
- No browser required — uses the public DEV.to API
Dictionary
Mode: 🌐 Public · Domain: api.dictionaryapi.dev
Search the open dictionary to quickly fetch native definitions, part of speech contexts, and phonetic pronunciations directly in your IDE terminal.
Commands
| Command | Description |
|---|---|
opencli dictionary search | Fetch the exact definition of a word |
opencli dictionary synonyms | Find related synonyms for a word |
opencli dictionary examples | Read real-world sentence usage examples |
Usage Examples
# Look up a complex term
opencli dictionary search serendipity
# Discover phonetics
opencli dictionary search ephemeralPrerequisites
- No browser required — utilizes the fast, open JSON definitions API.
豆瓣 (Douban)
Mode: 🔐 Browser (Cookie) · Domain: douban.com
Commands
| Command | Description |
|---|---|
opencli douban search | 搜索豆瓣电影、图书或音乐 |
opencli douban top250 | 豆瓣电影 Top 250 |
opencli douban subject | 条目详情 |
opencli douban photos | 获取电影海报/剧照图片列表 |
opencli douban download | 下载电影海报/剧照图片 |
opencli douban marks | 我的标记 |
opencli douban reviews | 我的短评 |
opencli douban movie-hot | 豆瓣电影热门榜单 |
opencli douban book-hot | 豆瓣图书热门榜单 |
Usage Examples
# 搜索电影
opencli douban search "流浪地球"
# 搜索图书
opencli douban search --type book "三体"
# 搜索音乐
opencli douban search --type music "周杰伦"
# 电影 Top 250
opencli douban top250 --limit 10
# 条目详情
opencli douban subject 1292052
# 获取海报直链(默认 type=Rb)
opencli douban photos 30382501 --limit 20
# 下载海报到本地目录
opencli douban download 30382501 --output ./douban
# 只下载指定 photo_id 的一张图
opencli douban download 30382501 --photo-id 2913621075 --output ./douban
# 返回 JSON,便于上层界面直接渲染图片并右键取图
opencli douban photos 30382501 -f json
# 电影热门
opencli douban movie-hot --limit 10
# 图书热门
opencli douban book-hot --limit 10
# JSON output
opencli douban top250 -f jsonPrerequisites
- Chrome logged into
douban.com - Browser Bridge extension installed
doubao
Browser adapter for Doubao Chat.
Commands
| Command | Description |
|---|---|
opencli doubao status | Check whether the page is reachable and whether Doubao appears logged in |
opencli doubao new | Start a new Doubao conversation |
opencli doubao send "..." | Send a message to the current Doubao chat |
opencli doubao read | Read the visible Doubao conversation |
opencli doubao ask "..." | Send a prompt and wait for a reply |
Prerequisites
- Chrome is running
- You are already logged into doubao.com
- Playwright MCP Bridge / browser bridge is configured for OpenCLI
Examples
opencli doubao status
opencli doubao new
opencli doubao send "帮我总结这段文档"
opencli doubao read
opencli doubao ask "请写一个 Python 快速排序示例" --timeout 90Notes
- The adapter targets the web chat page at
https://www.doubao.com/chat newfirst tries the visible "New Chat / 新对话" button, then falls back to the new-thread routeaskuses DOM polling, so very long generations may need a larger--timeout
Douyin (抖音创作者中心)
Mode: 🔐 Browser · Domain: creator.douyin.com
Commands
| Command | Description |
|---|---|
opencli douyin profile | 获取账号信息 |
opencli douyin videos | 获取作品列表 |
opencli douyin drafts | 获取草稿列表 |
opencli douyin draft | 上传视频并保存为草稿 |
opencli douyin publish | 定时发布视频到抖音 |
opencli douyin update | 更新视频信息 |
opencli douyin delete | 删除作品 |
opencli douyin stats | 查询作品数据分析 |
opencli douyin collections | 获取合集列表 |
opencli douyin activities | 获取官方活动列表 |
opencli douyin location | 搜索发布可用的地理位置 |
opencli douyin hashtag search | 按关键词搜索话题 |
opencli douyin hashtag suggest | 基于封面 URI 推荐话题 |
opencli douyin hashtag hot | 获取热点词 |
Usage Examples
# 账号与作品
opencli douyin profile
opencli douyin videos --limit 10
opencli douyin videos --status scheduled
opencli douyin drafts
# 发布前辅助信息
opencli douyin collections
opencli douyin activities
opencli douyin location "东京塔"
opencli douyin hashtag search "春游"
opencli douyin hashtag hot --limit 10
# 保存草稿
opencli douyin draft ./video.mp4 \
--title "春游 vlog" \
--caption "#春游 先存草稿"
# 定时发布
opencli douyin publish ./video.mp4 \
--title "春游 vlog" \
--caption "#春游 今天去看樱花" \
--schedule "2026-04-08T12:00:00+09:00"
# 也支持 Unix 秒字符串
opencli douyin publish ./video.mp4 \
--title "春游 vlog" \
--schedule 1775617200
# 更新与删除
opencli douyin update 1234567890 --caption "更新后的文案"
opencli douyin update 1234567890 --reschedule "2026-04-09T20:00:00+09:00"
opencli douyin delete 1234567890
# JSON 输出
opencli douyin profile -f jsonPrerequisites
- Chrome running and logged into
creator.douyin.com - The logged-in account must have access to Douyin Creator Center publishing features
- Browser Bridge extension installed
Notes
publishrequires--scheduleto be at least 2 hours later and no more than 14 days laterdraftandpublishupload the video through Douyin/ByteDance browser-authenticated APIs, so cookies in the active browser session must be validhashtag suggestexpects a validcover/cover_urivalue produced during the publish pipeline; for normal manual use,hashtag searchandhashtag hotare usually more convenient
Mode: 🔐 Browser · Domain: facebook.com
Commands
| Command | Description |
|---|---|
opencli facebook profile | Get user/page profile info |
opencli facebook notifications | Get recent notifications |
opencli facebook feed | Get news feed posts |
opencli facebook search | Search people, pages, posts |
Usage Examples
# View a profile
opencli facebook profile zuck
# Get notifications
opencli facebook notifications --limit 10
# News feed
opencli facebook feed --limit 5
# Search
opencli facebook search "OpenAI" --limit 5
# JSON output
opencli facebook profile zuck -f jsonPrerequisites
- Chrome running and logged into facebook.com
- Browser Bridge extension installed
Mode: 🌐 / 🔐 Mixed · Domains: google.com, suggestqueries.google.com, news.google.com, trends.google.com
Commands
| Command | Description |
|---|---|
opencli google search <keyword> | Search Google and extract results from the page |
opencli google suggest <keyword> | Get Google search suggestions |
opencli google news [keyword] | Get Google News headlines (top stories or search) |
opencli google trends | Get Google Trends daily trending searches |
What works today
- Public API commands work without a browser:
suggest— JSON API, no auth needednews— RSS feed, supports top stories and keyword searchtrends— RSS feed, supports different regionsgoogle searchuses browser mode to extract results from google.com.
Current limitations
google searchmay trigger CAPTCHA in Standalone browser mode. Extension mode (with an established Chrome session) is more reliable.- Google frequently changes its DOM structure. If
searchstops returning results, selectors may need updating. - Snippet extraction may return empty for some results depending on Google's layout.
Usage Examples
# Search Google
opencli google search "typescript tutorial" --limit 10
# Get search suggestions
opencli google suggest python
# Get top news headlines
opencli google news --limit 5
# Search news for a topic
opencli google news "artificial intelligence" --limit 10 --lang en --region US
# Get trending searches in Japan
opencli google trends --region JP --limit 10
# Output as JSON
opencli google search "machine learning" -f jsonPrerequisites
suggest,news,trendsdo not require Chrome.searchrequires:- Chrome running (or Standalone mode will auto-launch)
- For best results, use the Browser Bridge extension with an established Google session
Notes
suggestdefaults to--lang zh-CN; other commands default to--lang en.newssupports--langand--regionparameters for localized results.trendstraffic values are raw strings (e.g. "500K+", "1,000,000+"), not numeric.searchoutput includes three result types:result(standard),snippet(featured answer box), andpaa(People Also Ask).
Grok
Mode: Default Grok adapter + optional explicit consumer web path · Domain: grok.com
Commands
| Command | Description |
|---|---|
opencli grok ask | Keep the default Grok ask behavior |
opencli grok ask --web | Use the explicit grok.com consumer web UI flow |
Usage Examples
# Default / compatibility path
opencli grok ask --prompt "Explain quantum computing in simple terms"
# Explicit consumer web path
opencli grok ask --prompt "Explain quantum computing in simple terms" --web
# Best-effort fresh chat on the consumer web path
opencli grok ask --prompt "Hello" --web --new
# Set custom timeout (default: 120s)
opencli grok ask --prompt "Write a long essay" --web --timeout 180Options
| Option | Description |
|---|---|
--prompt | The message to send (required) |
--timeout | Wait timeout in seconds (default: 120) |
--new | Start a new chat before sending (default: false) |
--web | Opt into the explicit grok.com consumer web flow (default: false) |
Behavior
opencli grok askkeeps the upstream/default behavior intact.opencli grok ask --webswitches to the newer hardened consumer-web implementation.- The
--webpath adds stricter composer detection, clearer blocked/session-gated hints, and waits for a stabilized assistant bubble before returning.
Prerequisites
- The Grok adapter still depends on browser-backed access to
grok.com - For
--web, Chrome should already be running with an authenticated Grok consumer session - Browser Bridge extension installed
Caveats
--webdrives the Grok consumer web UI in the browser, not an API.- It depends on an already-authenticated session and can fail if Grok shows login, challenge, rate-limit, or other session-gating UI.
- It may break when the Grok composer DOM, submit button behavior, or message bubble structure changes.
HackerNews
Mode: 🌐 Public · Domain: news.ycombinator.com
Commands
| Command | Description |
|---|---|
opencli hackernews top | Hacker News top stories |
opencli hackernews new | Hacker News newest stories |
opencli hackernews best | Hacker News best stories |
opencli hackernews ask | Hacker News Ask HN posts |
opencli hackernews show | Hacker News Show HN posts |
opencli hackernews jobs | Hacker News job postings |
opencli hackernews search <query> | Search Hacker News stories |
opencli hackernews user <username> | Hacker News user profile |
Usage Examples
# Top stories
opencli hackernews top --limit 5
# Newest stories
opencli hackernews new --limit 10
# Search stories
opencli hackernews search "machine learning" --limit 5
# User profile
opencli hackernews user pg
# JSON output
opencli hackernews top -f json
# Sort search by date
opencli hackernews search "rust" --sort datePrerequisites
- No browser required — uses public API
Hugging Face
Mode: 🌐 Public · Domain: huggingface.co
Commands
| Command | Description |
|---|---|
opencli hf top | Top upvoted Hugging Face papers |
Usage Examples
# Today's top papers
opencli hf top --limit 10
# All papers (no limit)
opencli hf top --all
# Specific date
opencli hf top --date 2025-03-01
# Weekly/monthly top papers
opencli hf top --period weekly
opencli hf top --period monthly
# JSON output
opencli hf top -f jsonOptions
| Option | Description |
|---|---|
--limit | Number of papers (default: 20) |
--all | Return all papers, ignoring limit |
--date | Date in YYYY-MM-DD format (defaults to most recent) |
--period | Time period: daily, weekly, or monthly (default: daily) |
Prerequisites
- No browser required — uses public Hugging Face API
IMDb
Mode: 🌐 Public (Browser) · Domain: www.imdb.com
Commands
| Command | Description |
|---|---|
opencli imdb search | Search movies, TV shows, and people |
opencli imdb title | Get movie or TV show details |
opencli imdb top | IMDb Top 250 Movies |
opencli imdb trending | IMDb Most Popular Movies |
opencli imdb person | Get actor or director info |
opencli imdb reviews | Get user reviews for a title |
Usage Examples
# Search for a movie
opencli imdb search "inception" --limit 10
# Get movie details
opencli imdb title tt1375666
# Get TV series details (also accepts full URL)
opencli imdb title "https://www.imdb.com/title/tt0903747/"
# Top 250 movies
opencli imdb top --limit 20
# Currently trending movies
opencli imdb trending --limit 10
# Actor/director info with filmography
opencli imdb person nm0634240 --limit 5
# User reviews
opencli imdb reviews tt1375666 --limit 5
# JSON output
opencli imdb top --limit 5 -f jsonPrerequisites
- Chrome with Browser Bridge extension installed
- No login required (all data is public)
Mode: 🔐 Browser · Domain: instagram.com
Commands
| Command | Description |
|---|---|
opencli instagram profile | Get user profile info |
opencli instagram search | Search users |
opencli instagram user | Get recent posts from a user |
opencli instagram explore | Discover trending posts |
opencli instagram followers | List user's followers |
opencli instagram following | List user's following |
opencli instagram saved | Get your saved posts |
Usage Examples
# View a user's profile
opencli instagram profile nasa
# Search users
opencli instagram search nasa --limit 5
# View a user's recent posts
opencli instagram user nasa --limit 10
# Discover trending posts
opencli instagram explore --limit 20
# List followers/following
opencli instagram followers nasa --limit 20
opencli instagram following nasa --limit 20
# Get your saved posts
opencli instagram saved --limit 10
# JSON output
opencli instagram profile nasa -f jsonPrerequisites
- Chrome running and logged into instagram.com
- Browser Bridge extension installed
JD.com
Mode: 🔐 Browser · Domain: item.jd.com
Commands
| Command | Description |
|---|---|
opencli jd item <sku> | Fetch product details (price, shop, specs, AVIF images) |
Usage Examples
# Get product details by SKU
opencli jd item 100291143898
# Limit returned AVIF images
opencli jd item 100291143898 --images 5
# JSON output
opencli jd item 100291143898 -f jsonPrerequisites
- Chrome running and logged into jd.com
- Browser Bridge extension installed
即刻 (Jike)
Mode: 🔐 Browser · Domain: web.okjike.com
Commands
| Command | Description |
|---|---|
opencli jike feed | 即刻首页动态流 |
opencli jike search | 搜索即刻帖子 |
opencli jike post | 帖子详情及评论 |
opencli jike topic | 话题详情 |
opencli jike user | 用户资料 |
opencli jike create | 发布即刻动态 |
opencli jike comment | 评论即刻帖子 |
opencli jike like | 点赞即刻帖子 |
opencli jike repost | 转发即刻帖子 |
opencli jike notifications | 即刻通知 |
Usage Examples
# View feed
opencli jike feed --limit 10
# Search posts
opencli jike search "AI" --limit 20
# View post details and comments
opencli jike post <post-id>
# Create a new post
opencli jike create --content "Hello Jike!"
# Like a post
opencli jike like <post-id>
# JSON output
opencli jike feed -f jsonPrerequisites
- Chrome running and logged into web.okjike.com
- Browser Bridge extension installed
即梦AI (Jimeng)
Mode: 🔐 Browser · Domain: jimeng.jianying.com
Commands
| Command | Description |
|---|---|
opencli jimeng generate | 即梦AI 文生图 — 输入 prompt 生成图片 |
opencli jimeng history | 查看生成历史 |
Usage Examples
# Generate an image
opencli jimeng generate --prompt "一只在星空下的猫"
# Use a specific model
opencli jimeng generate --prompt "cyberpunk city" --model high_aes_general_v50
# Set custom wait timeout
opencli jimeng generate --prompt "sunset landscape" --wait 60
# View generation history
opencli jimeng history --limit 10Options (generate)
| Option | Description |
|---|---|
--prompt | Image description prompt (required) |
--model | Model: high_aes_general_v50 (5.0 Lite), high_aes_general_v42 (4.6), high_aes_general_v40 (4.0) |
--wait | Wait seconds for generation (default: 40) |
Prerequisites
- Chrome running and logged into jimeng.jianying.com
- Browser Bridge extension installed
Mode: 🔐 Browser · Domain: linkedin.com
Commands
| Command | Description |
|---|---|
opencli linkedin search | |
opencli linkedin timeline | Read posts from your LinkedIn home feed |
Usage Examples
# Quick start
opencli linkedin search --limit 5
# Read your home timeline
opencli linkedin timeline --limit 5
# JSON output
opencli linkedin search -f json
opencli linkedin timeline -f json
# Verbose mode
opencli linkedin search -vPrerequisites
- Chrome running and logged into linkedin.com
- Browser Bridge extension installed
Lobsters
Mode: 🌐 Public · Domain: lobste.rs
Commands
| Command | Description |
|---|---|
opencli lobsters hot | Hottest stories |
opencli lobsters newest | Latest stories |
opencli lobsters active | Most active discussions |
opencli lobsters tag | Stories by tag |
Usage Examples
# Quick start
opencli lobsters hot --limit 10
# Filter by tag
opencli lobsters tag --tag rust --limit 5
# JSON output
opencli lobsters hot -f json
# Verbose mode
opencli lobsters hot -vPrerequisites
None — all commands use the public JSON API, no browser or login required.
Related skills
How it compares
Pick opencli when the deliverable is a terminal command agents can invoke—not a web API route or GUI component.
FAQ
What does opencli help developers build?
opencli helps developers build or extend command-line interfaces with subcommands and agent-callable entry points. The skill targets local task automation, integration scripting, and tools that coding agents invoke from the terminal.
When should developers use opencli?
opencli fits projects needing a structured CLI instead of loose shell scripts—especially when coding agents must call commands reliably. Use it during build when exposing automation or integrations through the terminal.