Compare commits
305 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ddb022ca99 | |||
| 5e73940a4b | |||
| 966f6e5019 | |||
| ea8324257d | |||
| dff0fe510c | |||
| 4343ec07e0 | |||
| f00a5d1929 | |||
| c7895eaf8e | |||
| a83027d19c | |||
| f8bf66390d | |||
| 22f5c7ade0 | |||
| 8ab0cd2a50 | |||
| d4b06be049 | |||
| 7073645e30 | |||
| 3a21be624e | |||
| 127a974ecd | |||
| fcb5a9d409 | |||
| 66c4b841f2 | |||
| 2a52906ed2 | |||
| 9cdc1274b2 | |||
| a6d993f37f | |||
| b7c6c02370 | |||
| 722c180a0a | |||
| 788126198b | |||
| 98ecfab8ad | |||
| 4b976da04f | |||
| 3bedaccc25 | |||
| 40bd11dfbe | |||
| 9a77dba139 | |||
| 49c2dc7426 | |||
| e4a13cb6f0 | |||
| 637161f0ab | |||
| a778f617ca | |||
| b4a8089224 | |||
| 428b831f85 | |||
| 7ebe8134cc | |||
| c44bc62b60 | |||
| 7f57e76485 | |||
| e9818c1b41 | |||
| 3e91876d13 | |||
| 7c02588105 | |||
| 112fdefa8d | |||
| e077ad2336 | |||
| 81384ede00 | |||
| 71b2c3961b | |||
| b3b9892836 | |||
| 520622ac75 | |||
| 2d1b8c1e76 | |||
| 70651d3ba8 | |||
| 9696db9ed4 | |||
| c76f86c9cb | |||
| 4cd0409ded | |||
| ea113a6471 | |||
| a439286398 | |||
| 1d56dd77a8 | |||
| e98cf756e9 | |||
| 387aa0d6e5 | |||
| 1ecac25df5 | |||
| 9921e5d696 | |||
| 4c8a447ead | |||
| fb2a145e36 | |||
| bd274ce2d7 | |||
| 28c393ec86 | |||
| 8a4ea411e1 | |||
| 45cee57ca0 | |||
| 4e3259976b | |||
| bdf5967abd | |||
| 7776db83d7 | |||
| fae1dce027 | |||
| d831b04d48 | |||
| a22875814b | |||
| ae30763e9b | |||
| 3669a89323 | |||
| eb0ccaf549 | |||
| fbf051d539 | |||
| fcff2e40be | |||
| 4391ccfcb8 | |||
| ce484c2a63 | |||
| 06c902aeed | |||
| 1d39295f4b | |||
| 50ec7c6868 | |||
| 644b8bcbd4 | |||
| 4e274a92b7 | |||
| d1ade61e8c | |||
| 5d84c6f63e | |||
| cbd50ccb91 | |||
| 3f16d42e27 | |||
| c8e8c773c0 | |||
| 5de920a994 | |||
| 476fe115de | |||
| 8d45019119 | |||
| 36cf3067f7 | |||
| 516f1be3e6 | |||
| d556eeb512 | |||
| d7c895592f | |||
| eeace115cb | |||
| 35676a101f | |||
| cd0c6f874e | |||
| 0b71c6c4da | |||
| 7700704923 | |||
| 4d3b972d67 | |||
| 15d3583c60 | |||
| 53a95ed0ce | |||
| 0a2591842c | |||
| d9a71da596 | |||
| 9a79501bfd | |||
| 46d0f00aa6 | |||
| b3e32d8a05 | |||
| 36bc57a962 | |||
| 0e8c96b6d9 | |||
| c63af6d418 | |||
| 35a0fed8a0 | |||
| 593436e4cb | |||
| 02793e990e | |||
| 03f067d907 | |||
| 7e973ca592 | |||
| 4600b9d46d | |||
| 3cda14a2ab | |||
| 4f74b45963 | |||
| 8f1725982e | |||
| 2876750891 | |||
| 4ab4f88bcd | |||
| 9eb7a1eaa1 | |||
| 4cabca12df | |||
| fafa990acd | |||
| 3bde01aa1c | |||
| 152cc48091 | |||
| dff8f1e9c4 | |||
| 9d8b6441be | |||
| 1e0e4cd660 | |||
| 024d9908b3 | |||
| 943e286815 | |||
| 31f58ae699 | |||
| 812db29ed8 | |||
| 47a898125f | |||
| b60c69950d | |||
| ce38a1604e | |||
| d6e0aa120b | |||
| 44f0bbe94d | |||
| 0ea6e4a15c | |||
| ee35ee723f | |||
| 92fc13d60e | |||
| f5f7a9500e | |||
| 3229294f08 | |||
| f945b51f43 | |||
| e9a3ef7538 | |||
| 39b6413e47 | |||
| 33957ea0ba | |||
| 691f835bdf | |||
| 5b447e7a11 | |||
| d7bf5d6e04 | |||
| 390dbe7199 | |||
| 40846291e6 | |||
| f8dea7d8fc | |||
| 67474bb6db | |||
| d1986f0144 | |||
| ebc5c09ad9 | |||
| 055403abc1 | |||
| 10af754c89 | |||
| a7e5307226 | |||
| c21250bd88 | |||
| 0fa9573790 | |||
| 65e30b9ac4 | |||
| 540f3c677a | |||
| b5c1b242e2 | |||
| 2f6d28a3e9 | |||
| fde618063f | |||
| 89947fee50 | |||
| 2e962b2e7c | |||
| 13e2345089 | |||
| 4bf946edaf | |||
| 4398202b05 | |||
| b01bf6769b | |||
| 6d3e595d36 | |||
| edb21ca67b | |||
| 1f270397f6 | |||
| aa2f37be32 | |||
| aeb1cb6a3a | |||
| 4e260ecdeb | |||
| f7c7230854 | |||
| 48e277bd0b | |||
| 8bb03ecc9b | |||
| 3b6f72ca08 | |||
| beda0b714c | |||
| 3b33ade214 | |||
| ebe4683a9e | |||
| 59c0d639a5 | |||
| 3d1f9640ea | |||
| 8e8c4a0229 | |||
| 01b8b6b5bf | |||
| b2fa7daf57 | |||
| 0374b77d16 | |||
| c3efc5b492 | |||
| f85464c1aa | |||
| a4f94912cd | |||
| deb568dbe5 | |||
| 32619fa553 | |||
| 1d871b35f0 | |||
| 75e6ed4593 | |||
| b07434b2a1 | |||
| 515ce75f3b | |||
| f539a44cfd | |||
| 832370f6e2 | |||
| fc9fc32d14 | |||
| 43b753fa02 | |||
| be194a1849 | |||
| 63489fb596 | |||
| c370bd0582 | |||
| 8a355dfd2d | |||
| 700d970f13 | |||
| b1fda7da3b | |||
| 40a6a4cace | |||
| 920ca3f7e5 | |||
| 799a616359 | |||
| 9c2a983e8b | |||
| 3d1ea9b15c | |||
| e1d4a6e5e6 | |||
| a06cdbf0ac | |||
| e76de39f42 | |||
| 813631e468 | |||
| 2afbb99660 | |||
| aa55c88069 | |||
| b32fe1cbc3 | |||
| 981cc1bc5e | |||
| cd63231b7e | |||
| 685658f7bd | |||
| cd6f7a1f7e | |||
| 4ce0345c9a | |||
| 3cc2cb5504 | |||
| abac070ce4 | |||
| 79fbac844e | |||
| 7e776e2bd5 | |||
| bde1c53a3e | |||
| 5e667b9c2f | |||
| 64e3a2d627 | |||
| 849d9faea1 | |||
| 29ea5ce059 | |||
| 12c4b8853b | |||
| cfad003220 | |||
| abfd4b902c | |||
| 2d52abde7c | |||
| f102501e4a | |||
| 2a983b6b8d | |||
| c114a9d7f1 | |||
| d2e179ced5 | |||
| c806f795cc | |||
| de5495bdd7 | |||
| d6222ff932 | |||
| e0395ce5ed | |||
| 4c8c6e8be7 | |||
| 7b5bdfa7d5 | |||
| 546c0b997a | |||
| 1e34e7e6d3 | |||
| 9ae9eb3fc6 | |||
| 612c0ab1af | |||
| 68840fc85c | |||
| 8263a06a85 | |||
| eb2c3fdf89 | |||
| 43ed0ace59 | |||
| de962eb5fb | |||
| d1da293ef9 | |||
| 25bd872a24 | |||
| ff3e5c6887 | |||
| 2e66e3183c | |||
| a1bcb23239 | |||
| 6024af3aa0 | |||
| 1393ce3327 | |||
| 0fe3b9b921 | |||
| 375beaa744 | |||
| 341c42c62f | |||
| 50b71c0936 | |||
| 981c167a0b | |||
| 2463689105 | |||
| c714254d8f | |||
| 8e7490407c | |||
| 14dcd2bc5f | |||
| e9b9beedfe | |||
| 59de5fb3f5 | |||
| 7555f14369 | |||
| 2652fa40e5 | |||
| a7c367a61b | |||
| fbec2f6f5d | |||
| c2a5cbe90e | |||
| 34e20d33f2 | |||
| 1c496bb85f | |||
| 0c845d58c8 | |||
| 7f55950fed | |||
| 1576396a21 | |||
| 77193a0003 | |||
| 05b7f1bccf | |||
| 9889a6db11 | |||
| 61ea05bff7 | |||
| e781d40408 | |||
| c230f3e5ad | |||
| 3b2f88b2cf | |||
| 7eec7ce89f | |||
| 788b069c02 | |||
| 433ad3a56a | |||
| 50508b954e | |||
| 35a843b8bd | |||
| 486e513d07 | |||
| 6c64f617c6 | |||
| cd186bddd3 | |||
| 6486a42def | |||
| b308d5594a |
@@ -0,0 +1,249 @@
|
||||
---
|
||||
name: cross-project-adapter-migration
|
||||
description: "Cross-project CLI command migration workflow for opencli. Use when importing commands from external CLI projects (python/node) like rdt-cli, twitter-cli, etc. Covers: source analysis → gap matrix → batch migration → README/SKILL.md update."
|
||||
---
|
||||
|
||||
# Cross-Project Adapter Migration
|
||||
|
||||
> 从外部 CLI 项目(Python/Node/Go 等)批量迁移命令到 opencli 的标准化流程。
|
||||
|
||||
## When to Use
|
||||
|
||||
- 用户说"把 xxx-cli 的命令迁移过来"
|
||||
- 用户说"看看 xxx 项目有什么可以借鉴的"
|
||||
- 用户说"对齐 xxx-cli 的功能"
|
||||
- 在为新平台扩展 opencli 时,发现已有第三方 CLI 工具
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- 熟悉 [CLI-EXPLORER.md](file:///Users/jakevin/code/opencli/CLI-EXPLORER.md)(adapter 开发决策树)
|
||||
- 熟悉 [SKILL.md](file:///Users/jakevin/code/opencli/SKILL.md)(命令参考 & 模板)
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: 源项目分析
|
||||
|
||||
### 1.1 克隆 & 理解源项目
|
||||
|
||||
```bash
|
||||
# 克隆源项目到 /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 现有命令
|
||||
|
||||
```bash
|
||||
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](file:///Users/jakevin/code/opencli/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 策略)
|
||||
|
||||
```yaml
|
||||
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 策略)
|
||||
|
||||
```typescript
|
||||
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` 工具文件:
|
||||
|
||||
```typescript
|
||||
// src/clis/<site>/utils.ts
|
||||
export async function fetchWithAuth(page, url) { ... }
|
||||
export function parseItem(raw) { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: 验证 & 发布
|
||||
|
||||
### 4.1 构建验证
|
||||
|
||||
```bash
|
||||
npx tsc --noEmit # TypeScript 编译检查
|
||||
opencli list | grep <site> # 确认所有命令已注册
|
||||
```
|
||||
|
||||
### 4.2 运行验证(关键!)
|
||||
|
||||
每个新命令必须实际运行:
|
||||
|
||||
```bash
|
||||
# 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 提交 & 推送
|
||||
|
||||
```bash
|
||||
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`
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
description: Migrate commands from an external CLI project into opencli adapters
|
||||
---
|
||||
|
||||
// turbo-all
|
||||
|
||||
## Steps
|
||||
|
||||
1. Clone the source CLI project for analysis:
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
9. Verify all commands are registered:
|
||||
```bash
|
||||
opencli list | grep <site>
|
||||
```
|
||||
|
||||
10. Run each new command to verify it works:
|
||||
```bash
|
||||
opencli <site> <command> --limit 3 -f json
|
||||
```
|
||||
|
||||
11. 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:
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "feat(<site>): migrate <N> commands from <source-cli>"
|
||||
git push
|
||||
```
|
||||
@@ -0,0 +1,83 @@
|
||||
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
|
||||
@@ -0,0 +1,8 @@
|
||||
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.
|
||||
@@ -0,0 +1,42 @@
|
||||
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
|
||||
@@ -0,0 +1,57 @@
|
||||
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
|
||||
@@ -0,0 +1,26 @@
|
||||
name: Setup Chrome + xvfb
|
||||
description: Install real Chrome and xvfb virtual display for headed browser testing
|
||||
|
||||
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 for headed mode
|
||||
shell: bash
|
||||
run: sudo apt-get install -y xvfb
|
||||
@@ -0,0 +1,27 @@
|
||||
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)"
|
||||
@@ -0,0 +1,31 @@
|
||||
## 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.md` table (if new adapter)
|
||||
- [ ] Updated sidebar in `docs/.vitepress/config.mts` (if new adapter)
|
||||
|
||||
## Screenshots / Output
|
||||
|
||||
<!-- If applicable, paste CLI output or screenshots here. -->
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Build Chrome Extension
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
tags: [ "v*.*.*" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
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: |
|
||||
rm -rf extension-package
|
||||
mkdir -p extension-package
|
||||
cp extension/manifest.json extension-package/
|
||||
cp -R extension/dist extension-package/
|
||||
cp -R extension/icons extension-package/
|
||||
|
||||
- name: Create Extension ZIP
|
||||
run: |
|
||||
cd extension-package
|
||||
zip -r ../opencli-extension.zip .
|
||||
|
||||
- name: Upload Artifacts (Action Run)
|
||||
uses: actions/upload-artifact@v4
|
||||
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 }}
|
||||
@@ -2,19 +2,28 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
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:
|
||||
check:
|
||||
# ── Fast gate: typecheck + build ──
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
@@ -24,3 +33,56 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
# ── Unit tests (vitest shard) ──
|
||||
unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: ['20', '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: npx vitest run src/ --reporter=verbose --shard=${{ matrix.shard }}/2
|
||||
|
||||
# ── Smoke tests (scheduled / manual only) ──
|
||||
smoke-test:
|
||||
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
||||
needs: 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: Setup Chrome + xvfb
|
||||
uses: ./.github/actions/setup-chrome
|
||||
id: setup-chrome
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Run smoke tests
|
||||
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 }}
|
||||
timeout-minutes: 15
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
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
|
||||
@@ -0,0 +1,17 @@
|
||||
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@v3
|
||||
with:
|
||||
token: ${{ secrets.WEBSITE_DEPLOY_TOKEN }}
|
||||
repository: jackwener/opencli-website
|
||||
event-type: docs-updated
|
||||
@@ -0,0 +1,41 @@
|
||||
name: E2E Headed Chrome
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main, dev]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: e2e-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
e2e-headed:
|
||||
runs-on: ubuntu-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 + xvfb
|
||||
uses: ./.github/actions/setup-chrome
|
||||
id: setup-chrome
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Run E2E tests (headed Chrome + xvfb)
|
||||
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 }}
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Publish Any Commit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main, dev]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
if: ${{ vars.PKG_PR_NEW_ENABLED == 'true' }}
|
||||
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
|
||||
run: npm run build
|
||||
|
||||
- name: Publish to pkg.pr.new
|
||||
run: npx pkg-pr-new publish
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '22'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
@@ -26,11 +26,8 @@ jobs:
|
||||
- name: Type check
|
||||
run: npx tsc --noEmit
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@v2.6.1
|
||||
with:
|
||||
generate_release_notes: true
|
||||
|
||||
@@ -38,3 +35,10 @@ jobs:
|
||||
run: npm publish --provenance --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Trigger website rebuild
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
token: ${{ secrets.WEBSITE_DEPLOY_TOKEN }}
|
||||
repository: jackwener/opencli-website
|
||||
event-type: version-released
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
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
|
||||
|
||||
- name: Check for known vulnerabilities
|
||||
run: npx --yes audit-ci@^7 --high --skip-dev
|
||||
+17
@@ -1,4 +1,21 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Changelog
|
||||
|
||||
## [1.1.0](https://github.com/jackwener/opencli/compare/v1.0.6...v1.1.0) (2026-03-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add antigravity serve command — Anthropic API proxy ([35a0fed](https://github.com/jackwener/opencli/commit/35a0fed8a0c1cb714298f672c19f017bbc9a9630))
|
||||
* add arxiv and wikipedia adapters ([#132](https://github.com/jackwener/opencli/issues/132)) ([3cda14a](https://github.com/jackwener/opencli/commit/3cda14a2ab502e3bebfba6cdd9842c35b2b66b41))
|
||||
* add external CLI hub for discovery, auto-installation, and execution of external tools. ([b3e32d8](https://github.com/jackwener/opencli/commit/b3e32d8a05744c9bcdfef96f5ff3085ac72bd353))
|
||||
* add sinafinance 7x24 news adapter ([#131](https://github.com/jackwener/opencli/issues/131)) ([02793e9](https://github.com/jackwener/opencli/commit/02793e990ef4bdfdde9d7a748960b8a9ed6ea988))
|
||||
* **boss:** add 8 new recruitment management commands ([#133](https://github.com/jackwener/opencli/issues/133)) ([7e973ca](https://github.com/jackwener/opencli/commit/7e973ca59270029f33021a483ca4974dc3975d36))
|
||||
* **serve:** implement auto new conv, model mapping, and precise completion detection ([0e8c96b](https://github.com/jackwener/opencli/commit/0e8c96b6d9baebad5deb90b9e0620af5570b259d))
|
||||
* **serve:** use CDP mouse click + Input.insertText for reliable message injection ([c63af6d](https://github.com/jackwener/opencli/commit/c63af6d41808dddf6f0f76789aa6c042f391f0b0))
|
||||
* xiaohongshu creator flows migration ([#124](https://github.com/jackwener/opencli/issues/124)) ([8f17259](https://github.com/jackwener/opencli/commit/8f1725982ec06d121d7c15b5cf3cda2f5941c32a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docs:** use base '/' for custom domain and add CNAME file ([#129](https://github.com/jackwener/opencli/issues/129)) ([2876750](https://github.com/jackwener/opencli/commit/2876750891bc8a66be577b06ead4db61852c8e81))
|
||||
* **serve:** update model mappings to match actual Antigravity UI ([36bc57a](https://github.com/jackwener/opencli/commit/36bc57a9624cdfaa50ffb2c1ad7f9c518c5e6c55))
|
||||
* type safety for wikiFetch and arxiv abstract truncation ([4600b9d](https://github.com/jackwener/opencli/commit/4600b9d46dc7b56ff564c5f100c3a94c6a792c06))
|
||||
* use UTC+8 for XHS timestamp formatting (CI timezone fix) ([03f067d](https://github.com/jackwener/opencli/commit/03f067d90764487f0439705df36e1a5c969a7f98))
|
||||
* **xiaohongshu:** use fixed UTC+8 offset in trend timestamp formatting (CI timezone fix) ([593436e](https://github.com/jackwener/opencli/commit/593436e4cb5852f396fbaaa9f87ef1a0b518e76d))
|
||||
|
||||
## [1.0.6](https://github.com/jackwener/opencli/compare/v1.0.5...v1.0.6) (2026-03-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use %20 instead of + for spaces in Bilibili WBI signed requests ([#126](https://github.com/jackwener/opencli/issues/126)) ([4cabca1](https://github.com/jackwener/opencli/commit/4cabca12dfa6ca027b938b80ee6b940b5e89ea5c)), closes [#125](https://github.com/jackwener/opencli/issues/125)
|
||||
+13
-12
@@ -9,12 +9,12 @@
|
||||
|
||||
---
|
||||
|
||||
## AI Agent 开发者必读:用 Playwright MCP Bridge 探索
|
||||
## AI Agent 开发者必读:用浏览器探索
|
||||
|
||||
> [!CAUTION]
|
||||
> **你(AI Agent)必须通过 Playwright MCP Bridge 打开浏览器去访问目标网站!**
|
||||
> **你(AI Agent)必须通过浏览器打开目标网站去探索!**
|
||||
> 不要只靠 `opencli explore` 命令或静态分析来发现 API。
|
||||
> 你拥有 Playwright MCP 工具,必须主动用它们浏览网页、观察网络请求、模拟用户交互。
|
||||
> 你拥有浏览器工具,必须主动用它们浏览网页、观察网络请求、模拟用户交互。
|
||||
|
||||
### 为什么?
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
| ❌ 错误做法 | ✅ 正确做法 |
|
||||
|------------|------------|
|
||||
| 只用 `opencli explore` 命令,等结果自动出来 | 用 MCP Bridge 打开浏览器,主动浏览页面 |
|
||||
| 只用 `opencli explore` 命令,等结果自动出来 | 用浏览器工具打开页面,主动浏览 |
|
||||
| 直接在代码里 `fetch(url)`,不看浏览器实际请求 | 先在浏览器中确认 API 可用,再写代码 |
|
||||
| 页面打开后直接抓包,期望所有 API 都出现 | 模拟点击交互(展开评论/切换标签/加载更多) |
|
||||
| 遇到 HTTP 200 但空数据就放弃 | 检查是否需要 Wbi 签名或 Cookie 鉴权 |
|
||||
@@ -196,7 +196,7 @@ cat src/clis/<site>/feed.ts # 读最相似的那个
|
||||
|
||||
写 TS 适配器之前,先看看你的目标站点有没有**现成的 helper 函数**可以复用:
|
||||
|
||||
#### Bilibili (`src/bilibili.ts`)
|
||||
#### Bilibili (`src/clis/bilibili/utils.ts`)
|
||||
|
||||
| 函数 | 用途 | 何时使用 |
|
||||
|------|------|----------|
|
||||
@@ -342,10 +342,11 @@ name: search
|
||||
description: 知乎搜索
|
||||
|
||||
args:
|
||||
keyword:
|
||||
query:
|
||||
type: str
|
||||
required: true
|
||||
description: Search keyword
|
||||
positional: true
|
||||
description: Search query
|
||||
limit:
|
||||
type: int
|
||||
default: 10
|
||||
@@ -355,7 +356,7 @@ pipeline:
|
||||
|
||||
- evaluate: |
|
||||
(async () => {
|
||||
const q = encodeURIComponent('${{ args.keyword }}');
|
||||
const q = encodeURIComponent('${{ args.query }}');
|
||||
const res = await fetch('/api/v4/search_v3?q=' + q + '&t=general&limit=${{ args.limit }}', {
|
||||
credentials: 'include'
|
||||
});
|
||||
@@ -455,7 +456,7 @@ cli({
|
||||
name: 'search',
|
||||
description: 'Search tweets',
|
||||
strategy: Strategy.HEADER,
|
||||
args: [{ name: 'keyword', required: true }],
|
||||
args: [{ name: 'query', required: true, positional: true }],
|
||||
columns: ['rank', 'author', 'text', 'likes'],
|
||||
func: async (page, kwargs) => {
|
||||
await page.goto('https://x.com');
|
||||
@@ -474,7 +475,7 @@ cli({
|
||||
'X-Twitter-Auth-Type': 'OAuth2Session',
|
||||
};
|
||||
|
||||
const variables = JSON.stringify({ rawQuery: '${kwargs.keyword}', count: 20 });
|
||||
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();
|
||||
@@ -631,7 +632,7 @@ git add src/clis/mysite/ && git commit -m "feat(mysite): add hot" && git push
|
||||
```typescript
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
import { apiGet } from '../../bilibili.js'; // 复用平台 SDK
|
||||
import { apiGet } from './utils.js'; // 复用平台 SDK
|
||||
|
||||
cli({
|
||||
site: 'bilibili',
|
||||
@@ -694,7 +695,7 @@ cli({
|
||||
| 嵌套字段访问 | `${{ 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.keyword }}` 被浏览器二次编码 | 在 evaluate 内用 `encodeURIComponent()` 手动编码 |
|
||||
| 搜索参数被 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 () => { ... })()` |
|
||||
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
# Contributing to OpenCLI
|
||||
|
||||
Thanks for your interest in contributing to OpenCLI.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# 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
|
||||
npx vitest run src/
|
||||
|
||||
# 5. Link globally (optional, for testing `opencli` command)
|
||||
npm link
|
||||
```
|
||||
|
||||
## Adding 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`:
|
||||
|
||||
```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`](src/clis/hackernews/top.yaml) for a real example.
|
||||
|
||||
### TypeScript Adapter (For complex browser interactions)
|
||||
|
||||
Create a file like `src/clis/<site>/<command>.ts`:
|
||||
|
||||
```typescript
|
||||
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](./CLI-EXPLORER.md) if you need the full adapter workflow.
|
||||
|
||||
### Validate Your Adapter
|
||||
|
||||
```bash
|
||||
# Validate YAML syntax and schema
|
||||
opencli validate
|
||||
|
||||
# Test your command
|
||||
opencli <site> <command> --limit 3 -f json
|
||||
|
||||
# Verbose mode for debugging
|
||||
opencli <site> <command> -v
|
||||
```
|
||||
|
||||
## Arg 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:
|
||||
```yaml
|
||||
args:
|
||||
query:
|
||||
positional: true # ← primary arg, user types it directly
|
||||
type: str
|
||||
required: true
|
||||
limit:
|
||||
type: int # ← config arg, user types --limit 10
|
||||
default: 20
|
||||
```
|
||||
|
||||
TS example:
|
||||
```typescript
|
||||
args: [
|
||||
{ name: 'query', positional: true, required: true, help: 'Search query' },
|
||||
{ name: 'limit', type: 'int', default: 10, help: 'Max results' },
|
||||
]
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
See [TESTING.md](./TESTING.md) for the full guide and exact test locations.
|
||||
|
||||
```bash
|
||||
npx vitest run src/ # Unit tests
|
||||
npx vitest run tests/e2e/ # E2E tests
|
||||
npx vitest run # All tests
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
- **TypeScript strict mode** — avoid `any` where possible.
|
||||
- **ES Modules** — use `.js` extensions in imports (TypeScript output).
|
||||
- **Naming**: `kebab-case` for files, `camelCase` for variables/functions, `PascalCase` for types/classes.
|
||||
- **No default exports** — use named exports.
|
||||
|
||||
## Commit Convention
|
||||
|
||||
We use [Conventional Commits](https://www.conventionalcommits.org/):
|
||||
|
||||
```
|
||||
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 v4
|
||||
```
|
||||
|
||||
Common 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:
|
||||
```bash
|
||||
npx tsc --noEmit # Type check
|
||||
npx vitest run src/ # Unit tests
|
||||
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](./LICENSE).
|
||||
@@ -1,28 +1,190 @@
|
||||
BSD 3-Clause License
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2025, jackwener
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Definitions.
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2025 jackwener
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# OpenCLI
|
||||
|
||||
> **Make any website your CLI.**
|
||||
> Zero risk · Reuse Chrome login · AI-powered discovery
|
||||
> **Make any website, Electron App, or Local Tool your CLI.**
|
||||
> Zero risk · Reuse Chrome login · AI-powered discovery · Universal CLI Hub
|
||||
|
||||
[中文文档](./README.zh-CN.md)
|
||||
|
||||
@@ -9,74 +9,53 @@
|
||||
[](https://nodejs.org)
|
||||
[](./LICENSE)
|
||||
|
||||
A CLI tool that turns **any website** into a command-line interface. **59 commands** across **18 sites** — bilibili, zhihu, xiaohongshu, twitter, reddit, xueqiu, github, v2ex, hackernews, bbc, weibo, boss, yahoo-finance, reuters, smzdm, ctrip, youtube, coupang — powered by browser session reuse and AI-native discovery.
|
||||
A CLI tool that turns **any website**, **Electron app**, or **local CLI tool** into a command-line interface — Bilibili, Zhihu, 小红书, Twitter/X, Reddit, YouTube, Antigravity, `gh`, `docker`, and [many more](#built-in-commands) — powered by browser session reuse and AI-native discovery.
|
||||
|
||||
---
|
||||
**Built for AI Agents**: Simply configure an instruction in your global `AGENT.md` or `.cursorrules` guiding the AI to execute `opencli list` via Bash to discover available tools. Register your favorite local CLIs (`opencli register mycli`), and the AI will automatically learn how to invoke all your tools perfectly!
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Highlights](#highlights)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Built-in Commands](#built-in-commands)
|
||||
- [Output Formats](#output-formats)
|
||||
- [For AI Agents (Developer Guide)](#for-ai-agents-developer-guide)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Releasing New Versions](#releasing-new-versions)
|
||||
- [License](#license)
|
||||
**CLI All Electron Apps! The Most Powerful Update Has Arrived!**
|
||||
Turn ANY Electron application into a CLI tool! Recombine, script, and extend applications like Antigravity Ultra seamlessly. Now AI can control itself natively. Unlimited possibilities await!
|
||||
|
||||
---
|
||||
|
||||
## Highlights
|
||||
|
||||
- **CLI All Electron** — CLI-ify apps like Antigravity Ultra! Now AI can control itself natively using cc/openclaw!
|
||||
- **Account-safe** — Reuses Chrome's logged-in state; your credentials never leave the browser.
|
||||
- **AI Agent ready** — `explore` discovers APIs, `synthesize` generates adapters, `cascade` finds auth strategies.
|
||||
- **External CLI Hub** — Discover, auto-install, and passthrough commands to any external CLI (gh, obsidian, docker, kubectl, etc). Zero setup.
|
||||
- **Self-healing setup** — `opencli doctor` diagnoses and auto-starts the daemon, extension, and live browser connectivity.
|
||||
- **Dynamic Loader** — Simply drop `.ts` or `.yaml` adapters into the `clis/` folder for auto-registration.
|
||||
- **Dual-Engine Architecture** — Supports both YAML declarative data pipelines and robust browser runtime typescript injections.
|
||||
- **Dual-Engine Architecture** — Supports both YAML declarative data pipelines and robust browser runtime TypeScript injections.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Node.js**: >= 18.0.0
|
||||
- **Node.js**: >= 20.0.0
|
||||
- **Chrome** running **and logged into the target site** (e.g. bilibili.com, zhihu.com, xiaohongshu.com).
|
||||
|
||||
> **⚠️ Important**: Browser commands reuse your Chrome login session. You must be logged into the target website in Chrome before running commands. If you get empty data or errors, check your login status first.
|
||||
|
||||
OpenCLI connects to your browser through the Playwright MCP Bridge extension.
|
||||
OpenCLI connects to your browser through a lightweight **Browser Bridge** Chrome Extension + micro-daemon (zero config, auto-start).
|
||||
|
||||
### Playwright MCP Bridge Extension Setup
|
||||
### Browser Bridge Extension Setup
|
||||
|
||||
1. Install **[Playwright MCP Bridge](https://chromewebstore.google.com/detail/playwright-mcp-bridge/mmlmfjhmonkocbjadbfplnigmagldckm)** extension in Chrome.
|
||||
2. Obtain your token by clicking the extension icon in the browser toolbar or from the extension settings page.
|
||||
You can install the extension via either method:
|
||||
|
||||
**You must configure this token in BOTH your MCP configuration AND system environment variables.**
|
||||
**Method 1: Download Pre-built Release (Recommended)**
|
||||
1. Go to the GitHub [Releases page](https://github.com/jackwener/opencli/releases) and download the latest `opencli-extension.zip`.
|
||||
2. Unzip the file and open `chrome://extensions`, enable **Developer mode** (top-right toggle).
|
||||
3. Click **Load unpacked** and select the unzipped folder.
|
||||
|
||||
First, add it to your MCP client config (e.g. Claude/Cursor):
|
||||
**Method 2: Load Source (For Developers)**
|
||||
1. Open `chrome://extensions` and enable **Developer mode**.
|
||||
2. Click **Load unpacked** and select the `extension/` directory from this repository.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"playwright": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@playwright/mcp@latest", "--extension"],
|
||||
"env": {
|
||||
"PLAYWRIGHT_MCP_EXTENSION_TOKEN": "<your-token-here>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
That's it! The daemon auto-starts when you run any browser command. No tokens, no manual configuration.
|
||||
|
||||
And, so that `opencli` commands can use it directly in the terminal, export it in your shell environment (e.g. `~/.zshrc`):
|
||||
|
||||
```bash
|
||||
export PLAYWRIGHT_MCP_EXTENSION_TOKEN="<your-token-here>"
|
||||
```
|
||||
|
||||
After configuring, run `opencli doctor` to verify your token is correctly set up across all locations:
|
||||
|
||||
```bash
|
||||
opencli doctor
|
||||
```
|
||||
> **Tip**: Use `opencli doctor` for ongoing diagnosis:
|
||||
> ```bash
|
||||
> opencli doctor # Check extension + daemon connectivity
|
||||
> ```
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -116,26 +95,157 @@ npm install -g @jackwener/opencli@latest
|
||||
|
||||
## Built-in Commands
|
||||
|
||||
Run `opencli list` for the live registry.
|
||||
|
||||
| Site | Commands | Mode |
|
||||
|------|----------|------|
|
||||
| **bilibili** | `hot` `search` `me` `favorite` ... (11 commands) | 🔐 Browser |
|
||||
| **zhihu** | `hot` `search` `question` | 🔐 Browser |
|
||||
| **xiaohongshu** | `search` `notifications` `feed` `me` `user` | 🔐 Browser |
|
||||
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 Browser |
|
||||
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `following` `followers` `notifications` `post` `reply` `delete` `like` | 🔐 Browser |
|
||||
| **reddit** | `hot` `frontpage` `search` `subreddit` | 🔐 Browser |
|
||||
| **weibo** | `hot` | 🔐 Browser |
|
||||
| **boss** | `search` | 🔐 Browser |
|
||||
| **coupang** | `search` `add-to-cart` | 🔐 Browser |
|
||||
| **youtube** | `search` | 🔐 Browser |
|
||||
| **yahoo-finance** | `quote` | 🔐 Browser |
|
||||
| **reuters** | `search` | 🔐 Browser |
|
||||
| **smzdm** | `search` | 🔐 Browser |
|
||||
| **ctrip** | `search` | 🔐 Browser |
|
||||
| **github** | `search` | 🌐 Public |
|
||||
| **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 🌐 Public / 🔐 Browser |
|
||||
| **hackernews** | `top` | 🌐 Public |
|
||||
| **bbc** | `news` | 🌐 Public |
|
||||
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` | Browser |
|
||||
| **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | Browser |
|
||||
| **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | Desktop |
|
||||
| **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | Browser |
|
||||
| **codex** | `status` `send` `read` `new` `dump` `extract-diff` `model` `ask` `screenshot` `history` `export` | Desktop |
|
||||
| **chatwise** | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` | Desktop |
|
||||
| **doubao** | `status` `new` `send` `read` `ask` | Browser |
|
||||
| **doubao-app** | `status` `new` `send` `read` `ask` `screenshot` `dump` | Desktop |
|
||||
| **notion** | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` | Desktop |
|
||||
| **discord-app** | `status` `send` `read` `channels` `servers` `search` `members` | Desktop |
|
||||
| **v2ex** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | Public / Browser |
|
||||
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` `earnings-date` | Browser |
|
||||
| **antigravity** | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` `serve` | Desktop |
|
||||
| **chatgpt** | `status` `new` `send` `read` `ask` | Desktop |
|
||||
| **xiaohongshu** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | Browser |
|
||||
| **apple-podcasts** | `search` `episodes` `top` | Public |
|
||||
| **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | Public |
|
||||
| **zhihu** | `hot` `search` `question` `download` | Browser |
|
||||
| **weixin** | `download` | Browser |
|
||||
| **youtube** | `search` `video` `transcript` | Browser |
|
||||
| **boss** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | Browser |
|
||||
| **coupang** | `search` `add-to-cart` | Browser |
|
||||
| **bbc** | `news` | Public |
|
||||
| **bloomberg** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | Public / Browser |
|
||||
| **ctrip** | `search` | Browser |
|
||||
| **devto** | `top` `tag` `user` | Public |
|
||||
| **arxiv** | `search` `paper` | Public |
|
||||
| **wikipedia** | `search` `summary` | Public |
|
||||
| **hackernews** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | Public |
|
||||
| **linkedin** | `search` | Browser |
|
||||
| **reuters** | `search` | Browser |
|
||||
| **smzdm** | `search` | Browser |
|
||||
| **weibo** | `hot` `search` | Browser |
|
||||
| **yahoo-finance** | `quote` | Browser |
|
||||
| **sinafinance** | `news` | 🌐 Public |
|
||||
| **barchart** | `quote` `options` `greeks` `flow` | Browser |
|
||||
| **chaoxing** | `assignments` `exams` | Browser |
|
||||
| **grok** | `ask` | Browser |
|
||||
| **hf** | `top` | Public |
|
||||
| **jike** | `feed` `search` `create` `like` `comment` `repost` `notifications` `post` `topic` `user` | Browser |
|
||||
| **jimeng** | `generate` `history` | Browser |
|
||||
| **yollomi** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | Browser |
|
||||
| **linux-do** | `hot` `latest` `search` `categories` `category` `topic` | Public |
|
||||
| **stackoverflow** | `hot` `search` `bounties` `unanswered` | Public |
|
||||
| **steam** | `top-sellers` | Public |
|
||||
| **weread** | `shelf` `search` `book` `highlights` `notes` `notebooks` `ranking` | Browser |
|
||||
| **douban** | `search` `top250` `subject` `marks` `reviews` | Browser |
|
||||
| **facebook** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | Browser |
|
||||
| **google** | `news` `search` `suggest` `trends` | Public |
|
||||
| **instagram** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | Browser |
|
||||
| **lobsters** | `hot` `newest` `active` `tag` | Public |
|
||||
| **medium** | `feed` `search` `user` `shared` | Browser |
|
||||
| **sinablog** | `hot` `search` `article` `user` `shared` | Browser |
|
||||
| **substack** | `feed` `search` `publication` `shared` | Browser |
|
||||
| **tiktok** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | Browser |
|
||||
|
||||
|
||||
### External CLI Hub
|
||||
|
||||
OpenCLI acts as a universal hub for your existing command-line tools. It provides unified discovery, automatic installation, and pure passthrough execution.
|
||||
|
||||
| External CLI | Description | Commands Example |
|
||||
|--------------|-------------|------------------|
|
||||
| **gh** | GitHub CLI | `opencli gh pr list --limit 5` |
|
||||
| **obsidian** | Obsidian vault management | `opencli obsidian search query="AI"` |
|
||||
| **docker** | Docker command-line interface | `opencli docker ps` |
|
||||
| **kubectl** | Kubernetes command-line tool | `opencli kubectl get pods` |
|
||||
| **readwise** | Readwise & Reader CLI | `opencli readwise login` |
|
||||
| **gws** | Google Workspace CLI — Docs, Sheets, Drive, Gmail, Calendar | `opencli gws docs list` |
|
||||
|
||||
**Zero Configuration**: OpenCLI purely passes your inputs to the underlying binary via standard I/O streams. The external CLI works exactly as it naturally would, maintaining its standard output formats.
|
||||
|
||||
**Auto-Installation**: If you run `opencli gh ...` and `gh` is not installed on your system, OpenCLI will automatically try to install it using your system's package manager (e.g., `brew install gh`) before seamlessly re-running the command.
|
||||
|
||||
**Register Your Own**:
|
||||
Add any local CLI to your OpenCLI registry so AI agents can automatically discover it via the `opencli list` command.
|
||||
```bash
|
||||
opencli register mycli
|
||||
```
|
||||
|
||||
### Desktop App Adapters
|
||||
|
||||
Each desktop adapter has its own detailed documentation with commands reference, setup guide, and examples:
|
||||
|
||||
| App | Description | Doc |
|
||||
|-----|-------------|-----|
|
||||
| **Cursor** | Control Cursor IDE — Composer, chat, code extraction | [Doc](./docs/adapters/desktop/cursor.md) |
|
||||
| **Codex** | Drive OpenAI Codex CLI agent headlessly | [Doc](./docs/adapters/desktop/codex.md) |
|
||||
| **Antigravity** | Control Antigravity Ultra from terminal | [Doc](./docs/adapters/desktop/antigravity.md) |
|
||||
| **ChatGPT** | Automate ChatGPT macOS desktop app | [Doc](./docs/adapters/desktop/chatgpt.md) |
|
||||
| **ChatWise** | Multi-LLM client (GPT-4, Claude, Gemini) | [Doc](./docs/adapters/desktop/chatwise.md) |
|
||||
| **Notion** | Search, read, write Notion pages | [Doc](./docs/adapters/desktop/notion.md) |
|
||||
| **Discord** | Discord Desktop — messages, channels, servers | [Doc](./docs/adapters/desktop/discord.md) |
|
||||
| **Doubao** | Control Doubao AI desktop app via CDP | [Doc](./docs/adapters/desktop/doubao-app.md) |
|
||||
|
||||
## Download Support
|
||||
|
||||
OpenCLI supports downloading images, videos, and articles from supported platforms.
|
||||
|
||||
### Supported Platforms
|
||||
|
||||
| Platform | Content Types | Notes |
|
||||
|----------|---------------|-------|
|
||||
| **xiaohongshu** | Images, Videos | Downloads all media from a note |
|
||||
| **bilibili** | Videos | Requires `yt-dlp` installed |
|
||||
| **twitter** | Images, Videos | Downloads from user media tab or single tweet |
|
||||
| **zhihu** | Articles (Markdown) | Exports articles with optional image download |
|
||||
| **weixin** | Articles (Markdown) | Exports WeChat Official Account articles |
|
||||
|
||||
### Prerequisites
|
||||
|
||||
For video downloads from streaming platforms, you need to install `yt-dlp`:
|
||||
|
||||
```bash
|
||||
# Install yt-dlp
|
||||
pip install yt-dlp
|
||||
# or
|
||||
brew install yt-dlp
|
||||
```
|
||||
|
||||
### Usage Examples
|
||||
|
||||
```bash
|
||||
# Download images/videos from Xiaohongshu note
|
||||
opencli xiaohongshu download abc123 --output ./xhs
|
||||
|
||||
# Download Bilibili video (requires yt-dlp)
|
||||
opencli bilibili download BV1xxx --output ./bilibili
|
||||
opencli bilibili download BV1xxx --quality 1080p # Specify quality
|
||||
|
||||
# Download Twitter media from user
|
||||
opencli twitter download elonmusk --limit 20 --output ./twitter
|
||||
|
||||
# Download single tweet media
|
||||
opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
|
||||
|
||||
# Export Zhihu article to Markdown
|
||||
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
|
||||
|
||||
# Export with local images
|
||||
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --download-images
|
||||
|
||||
# Export WeChat article to Markdown
|
||||
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --output ./weixin
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Output Formats
|
||||
|
||||
@@ -152,6 +262,25 @@ opencli bilibili hot -f csv # CSV
|
||||
opencli bilibili hot -v # Verbose: show pipeline debug steps
|
||||
```
|
||||
|
||||
## Plugins
|
||||
|
||||
Extend OpenCLI with community-contributed adapters. Plugins use the same YAML/TS format as built-in commands and are automatically discovered at startup.
|
||||
|
||||
```bash
|
||||
opencli plugin install github:user/opencli-plugin-my-tool # Install
|
||||
opencli plugin list # List installed
|
||||
opencli plugin update my-tool # Update to latest
|
||||
opencli plugin uninstall my-tool # Remove
|
||||
```
|
||||
|
||||
| Plugin | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| [opencli-plugin-github-trending](https://github.com/ByteYue/opencli-plugin-github-trending) | YAML | GitHub Trending repositories |
|
||||
| [opencli-plugin-hot-digest](https://github.com/ByteYue/opencli-plugin-hot-digest) | TS | Multi-platform trending aggregator |
|
||||
| [opencli-plugin-juejin](https://github.com/Astro-Han/opencli-plugin-juejin) | YAML | 稀土掘金 (Juejin) hot articles |
|
||||
|
||||
See [Plugins Guide](./docs/guide/plugins.md) for creating your own plugin.
|
||||
|
||||
## For AI Agents (Developer Guide)
|
||||
|
||||
If you are an AI assistant tasked with creating a new command adapter for `opencli`, please follow the AI Agent workflow below:
|
||||
@@ -176,26 +305,31 @@ opencli cascade https://api.example.com/data
|
||||
|
||||
Explore outputs to `.opencli/explore/<site>/` (manifest.json, endpoints.json, capabilities.json, auth.json).
|
||||
|
||||
## Testing
|
||||
|
||||
See **[TESTING.md](./TESTING.md)** for how to run and write tests.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **"Failed to connect to Playwright MCP Bridge"**
|
||||
- Ensure the Playwright MCP extension is installed and **enabled** in your running Chrome.
|
||||
- Restart the Chrome browser if you just installed the extension.
|
||||
- **"Extension not connected"**
|
||||
- Ensure the opencli Browser Bridge extension is installed and **enabled** in `chrome://extensions`.
|
||||
- **"attach failed: Cannot access a chrome-extension:// URL"**
|
||||
- Another Chrome extension (e.g. youmind, New Tab Override, or AI assistant extensions) may be interfering. Try **disabling other extensions** temporarily, then retry.
|
||||
- **Empty data returns or 'Unauthorized' error**
|
||||
- Your login session in Chrome might have expired. Open a normal Chrome tab, navigate to the target site, and log in or refresh the page to prove you are human.
|
||||
- Your login session in Chrome might have expired. Open a normal Chrome tab, navigate to the target site, and log in or refresh the page.
|
||||
- **Node API errors**
|
||||
- Make sure you are using Node.js >= 18. Some dependencies require modern Node APIs.
|
||||
- Make sure you are using Node.js >= 20. Some dependencies require modern Node APIs.
|
||||
- **Daemon issues**
|
||||
- Check daemon status: `curl localhost:19825/status`
|
||||
- View extension logs: `curl localhost:19825/logs`
|
||||
|
||||
## Releasing New Versions
|
||||
|
||||
```bash
|
||||
npm version patch # 0.1.0 → 0.1.1
|
||||
npm version minor # 0.1.0 → 0.2.0
|
||||
git push --follow-tags
|
||||
```
|
||||
## Star History
|
||||
|
||||
The CI will automatically build, create a GitHub release, and publish to npm.
|
||||
[](https://star-history.com/#jackwener/opencli&Date)
|
||||
|
||||
After publishing the new version, remember to update the browser extension in the Chrome Web Store as well, so the extension release stays in sync with the CLI release.
|
||||
|
||||
## License
|
||||
|
||||
[BSD-3-Clause](./LICENSE)
|
||||
[Apache-2.0](./LICENSE)
|
||||
|
||||
+210
-78
@@ -1,7 +1,7 @@
|
||||
# OpenCLI
|
||||
|
||||
> **把任何网站变成你的命令行工具。**
|
||||
> 零风控 · 复用 Chrome 登录 · AI 自动发现接口
|
||||
> **把任何网站、本地工具、Electron 应用变成能够让 AI 调用的命令行!**
|
||||
> 零风控 · 复用 Chrome 登录 · AI 自动发现接口 · 全能 CLI 枢纽
|
||||
|
||||
[English](./README.md)
|
||||
|
||||
@@ -9,74 +9,55 @@
|
||||
[](https://nodejs.org)
|
||||
[](./LICENSE)
|
||||
|
||||
OpenCLI 将任何网站变成命令行工具。**59 个命令**覆盖 **18 个站点** — B站、知乎、小红书、Twitter、Reddit、雪球、GitHub、V2EX、Hacker News、BBC、微博、BOSS直聘、Yahoo Finance、路透社、什么值得买、携程、YouTube、Coupang — 复用浏览器登录态,AI 驱动探索。
|
||||
OpenCLI 将任何网站、本地 CLI 或 Electron 应用(如 Antigravity)变成命令行工具 — B站、知乎、小红书、Twitter/X、Reddit、YouTube,以及 `gh`、`docker` 等[多种站点与工具](#内置命令) — 复用浏览器登录态,AI 驱动探索。
|
||||
|
||||
---
|
||||
**专为 AI Agent 打造**:只需在全局 `.cursorrules` 或 `AGENT.md` 中配置简单指令,引导 AI 通过 Bash 执行 `opencli list` 来检索可用的 CLI 工具及其用法。随后,将你常用的 CLI 列表整合注册进去(`opencli register mycli`),AI 便能瞬间学会自动调用相应的本地工具!
|
||||
|
||||
## 目录
|
||||
|
||||
- [亮点](#亮点)
|
||||
- [前置要求](#前置要求)
|
||||
- [快速开始](#快速开始)
|
||||
- [内置命令](#内置命令)
|
||||
- [输出格式](#输出格式)
|
||||
- [致 AI Agent(开发者指南)](#致-ai-agent开发者指南)
|
||||
- [常见问题排查](#常见问题排查)
|
||||
- [版本发布](#版本发布)
|
||||
- [License](#license)
|
||||
**opencli 支持 CLI 化所有 electron 应用!最强大更新来袭!**
|
||||
CLI all electron!现在支持把所有 electron 应用 CLI 化,从而组合出各种神奇的能力。
|
||||
如果你在使用诸如 Antigravity Ultra 等工具时觉得不够灵活或难以扩展,现在通过 OpenCLI 把他 CLI 化,轻松打破界限。
|
||||
现在,**AI 可以自己控制自己**!结合 cc/openclaw 就可以远程控制任何 electron 应用!无限玩法!!
|
||||
|
||||
---
|
||||
|
||||
## 亮点
|
||||
|
||||
- **59 个命令,18 个站点** — B站、知乎、小红书、Twitter、Reddit、雪球(xueqiu)、GitHub、V2EX、Hacker News、BBC、微博、BOSS直聘、Yahoo Finance、路透社、什么值得买、携程、YouTube、Coupang
|
||||
- **CLI All Electron** — 支持把所有 electron 应用(如 Antigravity Ultra)CLI 化,让 AI 控制自己!
|
||||
- **多站点覆盖** — 覆盖 B站、知乎、小红书、Twitter、Reddit,以及多种桌面应用
|
||||
- **零风控** — 复用 Chrome 登录态,无需存储任何凭证
|
||||
- **外部 CLI 枢纽** — 统一发现、自动安装、透传执行 `gh`、`docker`、`kubectl` 等本地 CLI
|
||||
- **自修复配置** — `opencli doctor` 自动启动 daemon,诊断扩展和浏览器连接状态
|
||||
- **AI 原生** — `explore` 自动发现 API,`synthesize` 生成适配器,`cascade` 探测认证策略
|
||||
- **动态加载引擎** — 声明式的 `.yaml` 或者底层定制的 `.ts` 适配器,放入 `clis/` 文件夹即可自动注册生效
|
||||
|
||||
## 前置要求
|
||||
|
||||
- **Node.js**: >= 18.0.0
|
||||
- **Node.js**: >= 20.0.0
|
||||
- **Chrome** 浏览器正在运行,且**已登录目标网站**(如 bilibili.com、zhihu.com、xiaohongshu.com)
|
||||
|
||||
> **⚠️ 重要**:大多数命令复用你的 Chrome 登录状态。运行命令前,你必须已在 Chrome 中打开目标网站并完成登录。如果获取到空数据或报错,请先检查你的浏览器登录状态。
|
||||
|
||||
OpenCLI 通过 Playwright MCP Bridge 扩展与你的浏览器通信。
|
||||
OpenCLI 通过轻量化的 **Browser Bridge** Chrome 扩展 + 微型 daemon 与浏览器通信(零配置,自动启动)。
|
||||
|
||||
### Playwright MCP Bridge 扩展配置
|
||||
### Browser Bridge 扩展配置
|
||||
|
||||
1. 安装 **[Playwright MCP Bridge](https://chromewebstore.google.com/detail/playwright-mcp-bridge/mmlmfjhmonkocbjadbfplnigmagldckm)** 扩展
|
||||
2. 在浏览器插件栏点击该插件,或者在插件设置页获取你的 Extension Token。
|
||||
你可以选择以下任一方式安装扩展:
|
||||
|
||||
**你必须将这个 Token 同时配置到你的 MCP 配置文件 AND 环境变量中。**
|
||||
**方式一:下载构建好的安装包(推荐)**
|
||||
1. 到 GitHub [Releases 页面](https://github.com/jackwener/opencli/releases) 下载最新的 `opencli-extension.zip`。
|
||||
2. 解压后打开 Chrome 的 `chrome://extensions`,启用右上角的 **开发者模式**。
|
||||
3. 点击 **加载已解压的扩展程序**,选择解压后的文件夹。
|
||||
|
||||
首先,配置你的 MCP 客户端(如 Claude/Cursor 等):
|
||||
**方式二:加载源码(针对开发者)**
|
||||
1. 同样在 `chrome://extensions` 开启 **开发者模式**。
|
||||
2. 点击 **加载已解压的扩展程序**,选择本仓库代码树中的 `extension/` 文件夹。
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"playwright": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@playwright/mcp@latest", "--extension"],
|
||||
"env": {
|
||||
"PLAYWRIGHT_MCP_EXTENSION_TOKEN": "<你的-token>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
完成!运行任何 opencli 浏览器命令时,后台微型 daemon 会自动启动与浏览器通信。无需配 API Token,零代码配置。
|
||||
|
||||
并且,为了让 `opencli` 命令行也能直接使用它,你必须在你的终端系统环境变量中导出它(建议写进 `~/.zshrc` 或 `~/.bashrc`):
|
||||
|
||||
```bash
|
||||
export PLAYWRIGHT_MCP_EXTENSION_TOKEN="<你的-token>"
|
||||
```
|
||||
|
||||
配置完成后,运行 `opencli doctor` 检测你的 Token 是否在所有位置都正确配置:
|
||||
|
||||
```bash
|
||||
opencli doctor
|
||||
```
|
||||
> **Tip**:后续诊断用 `opencli doctor`:
|
||||
> ```bash
|
||||
> opencli doctor # 检查扩展和 daemon 连通性
|
||||
> ```
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -116,26 +97,157 @@ npm install -g @jackwener/opencli@latest
|
||||
|
||||
## 内置命令
|
||||
|
||||
运行 `opencli list` 查看完整注册表。
|
||||
|
||||
| 站点 | 命令 | 模式 |
|
||||
|------|------|------|
|
||||
| **bilibili** | `hot` `search` `me` `favorite` ...(共11个) | 🔐 浏览器 |
|
||||
| **zhihu** | `hot` `search` `question` | 🔐 浏览器 |
|
||||
| **xiaohongshu** | `search` `notifications` `feed` `me` `user` | 🔐 浏览器 |
|
||||
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 浏览器 |
|
||||
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `following` `followers` `notifications` `post` `reply` `delete` `like` | 🔐 浏览器 |
|
||||
| **reddit** | `hot` `frontpage` `search` `subreddit` | 🔐 浏览器 |
|
||||
| **weibo** | `hot` | 🔐 浏览器 |
|
||||
| **boss** | `search` | 🔐 浏览器 |
|
||||
| **coupang** | `search` `add-to-cart` | 🔐 浏览器 |
|
||||
| **youtube** | `search` | 🔐 浏览器 |
|
||||
| **yahoo-finance** | `quote` | 🔐 浏览器 |
|
||||
| **reuters** | `search` | 🔐 浏览器 |
|
||||
| **smzdm** | `search` | 🔐 浏览器 |
|
||||
| **ctrip** | `search` | 🔐 浏览器 |
|
||||
| **github** | `search` | 🌐 公共 API |
|
||||
| **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 🌐 公共 API / 🔐 浏览器 |
|
||||
| **hackernews** | `top` | 🌐 公共 API |
|
||||
| **bbc** | `news` | 🌐 公共 API |
|
||||
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` | 浏览器 |
|
||||
| **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 浏览器 |
|
||||
| **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 桌面端 |
|
||||
| **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 浏览器 |
|
||||
| **codex** | `status` `send` `read` `new` `dump` `extract-diff` `model` `ask` `screenshot` `history` `export` | 桌面端 |
|
||||
| **chatwise** | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` | 桌面端 |
|
||||
| **doubao** | `status` `new` `send` `read` `ask` | 浏览器 |
|
||||
| **doubao-app** | `status` `new` `send` `read` `ask` `screenshot` `dump` | 桌面端 |
|
||||
| **notion** | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` | 桌面端 |
|
||||
| **discord-app** | `status` `send` `read` `channels` `servers` `search` `members` | 桌面端 |
|
||||
| **v2ex** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | 公开 / 浏览器 |
|
||||
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` `earnings-date` | 浏览器 |
|
||||
| **antigravity** | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` `serve` | 桌面端 |
|
||||
| **chatgpt** | `status` `new` `send` `read` `ask` | 桌面端 |
|
||||
| **xiaohongshu** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | 浏览器 |
|
||||
| **apple-podcasts** | `search` `episodes` `top` | 公开 |
|
||||
| **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 公开 |
|
||||
| **zhihu** | `hot` `search` `question` `download` | 浏览器 |
|
||||
| **weixin** | `download` | 浏览器 |
|
||||
| **youtube** | `search` `video` `transcript` | 浏览器 |
|
||||
| **boss** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | 浏览器 |
|
||||
| **coupang** | `search` `add-to-cart` | 浏览器 |
|
||||
| **bbc** | `news` | 公共 API |
|
||||
| **bloomberg** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | 公共 API / 浏览器 |
|
||||
| **ctrip** | `search` | 浏览器 |
|
||||
| **devto** | `top` `tag` `user` | 公开 |
|
||||
| **arxiv** | `search` `paper` | 公开 |
|
||||
| **wikipedia** | `search` `summary` | 公开 |
|
||||
| **hackernews** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 公共 API |
|
||||
| **linkedin** | `search` | 浏览器 |
|
||||
| **reuters** | `search` | 浏览器 |
|
||||
| **smzdm** | `search` | 浏览器 |
|
||||
| **weibo** | `hot` `search` | 浏览器 |
|
||||
| **yahoo-finance** | `quote` | 浏览器 |
|
||||
| **sinafinance** | `news` | 🌐 公开 |
|
||||
| **barchart** | `quote` `options` `greeks` `flow` | 浏览器 |
|
||||
| **chaoxing** | `assignments` `exams` | 浏览器 |
|
||||
| **grok** | `ask` | 浏览器 |
|
||||
| **hf** | `top` | 公开 |
|
||||
| **jike** | `feed` `search` `create` `like` `comment` `repost` `notifications` `post` `topic` `user` | 浏览器 |
|
||||
| **jimeng** | `generate` `history` | 浏览器 |
|
||||
| **yollomi** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | 浏览器 |
|
||||
| **linux-do** | `hot` `latest` `search` `categories` `category` `topic` | 公开 |
|
||||
| **stackoverflow** | `hot` `search` `bounties` `unanswered` | 公开 |
|
||||
| **steam** | `top-sellers` | 公开 |
|
||||
| **weread** | `shelf` `search` `book` `highlights` `notes` `notebooks` `ranking` | 浏览器 |
|
||||
| **douban** | `search` `top250` `subject` `marks` `reviews` | 浏览器 |
|
||||
| **facebook** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 浏览器 |
|
||||
| **google** | `news` `search` `suggest` `trends` | 公开 |
|
||||
| **instagram** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 浏览器 |
|
||||
| **lobsters** | `hot` `newest` `active` `tag` | 公开 |
|
||||
| **medium** | `feed` `search` `user` `shared` | 浏览器 |
|
||||
| **sinablog** | `hot` `search` `article` `user` `shared` | 浏览器 |
|
||||
| **substack** | `feed` `search` `publication` `shared` | 浏览器 |
|
||||
| **tiktok** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | 浏览器 |
|
||||
|
||||
|
||||
### 外部 CLI 枢纽
|
||||
|
||||
OpenCLI 也可以作为你现有命令行工具的统一入口,负责发现、自动安装和纯透传执行。
|
||||
|
||||
| 外部 CLI | 描述 | 示例 |
|
||||
|----------|------|------|
|
||||
| **gh** | GitHub CLI | `opencli gh pr list --limit 5` |
|
||||
| **obsidian** | Obsidian 仓库管理 | `opencli obsidian search query="AI"` |
|
||||
| **docker** | Docker 命令行工具 | `opencli docker ps` |
|
||||
| **kubectl** | Kubernetes CLI | `opencli kubectl get pods` |
|
||||
| **readwise** | Readwise / Reader CLI | `opencli readwise login` |
|
||||
| **gws** | Google Workspace CLI — Docs, Sheets, Drive, Gmail, Calendar | `opencli gws docs list` |
|
||||
|
||||
**零配置透传**:OpenCLI 会把你的输入原样转发给底层二进制,保留原生 stdout / stderr 行为。
|
||||
|
||||
**自动安装**:如果你运行 `opencli gh ...` 时系统中还没有 `gh`,OpenCLI 会优先尝试通过系统包管理器安装,然后自动重试命令。
|
||||
|
||||
**注册自定义本地 CLI**:
|
||||
|
||||
```bash
|
||||
opencli register mycli
|
||||
```
|
||||
|
||||
### 桌面应用适配器
|
||||
|
||||
每个桌面适配器都有自己详细的文档说明,包括命令参考、启动配置与使用示例:
|
||||
|
||||
| 应用 | 描述 | 文档 |
|
||||
|-----|-------------|-----|
|
||||
| **Cursor** | 控制 Cursor IDE — Composer、对话、代码提取等 | [Doc](./docs/adapters/desktop/cursor.md) |
|
||||
| **Codex** | 在后台(无头)驱动 OpenAI Codex CLI Agent | [Doc](./docs/adapters/desktop/codex.md) |
|
||||
| **Antigravity** | 在终端直接控制 Antigravity Ultra | [Doc](./docs/adapters/desktop/antigravity.md) |
|
||||
| **ChatGPT** | 自动化操作 ChatGPT macOS 桌面客户端 | [Doc](./docs/adapters/desktop/chatgpt.md) |
|
||||
| **ChatWise** | 多 LLM 客户端(GPT-4、Claude、Gemini) | [Doc](./docs/adapters/desktop/chatwise.md) |
|
||||
| **Notion** | 搜索、读取、写入 Notion 页面 | [Doc](./docs/adapters/desktop/notion.md) |
|
||||
| **Discord** | Discord 桌面版 — 消息、频道、服务器 | [Doc](./docs/adapters/desktop/discord.md) |
|
||||
| **Doubao** | 通过 CDP 控制豆包桌面应用 | [Doc](./docs/adapters/desktop/doubao-app.md) |
|
||||
|
||||
## 下载支持
|
||||
|
||||
OpenCLI 支持从各平台下载图片、视频和文章。
|
||||
|
||||
### 支持的平台
|
||||
|
||||
| 平台 | 内容类型 | 说明 |
|
||||
|------|----------|------|
|
||||
| **小红书** | 图片、视频 | 下载笔记中的所有媒体文件 |
|
||||
| **B站** | 视频 | 需要安装 `yt-dlp` |
|
||||
| **Twitter/X** | 图片、视频 | 从用户媒体页或单条推文下载 |
|
||||
| **知乎** | 文章(Markdown) | 导出文章,可选下载图片到本地 |
|
||||
| **微信公众号** | 文章(Markdown) | 导出微信公众号文章为 Markdown |
|
||||
|
||||
### 前置依赖
|
||||
|
||||
下载流媒体平台的视频需要安装 `yt-dlp`:
|
||||
|
||||
```bash
|
||||
# 安装 yt-dlp
|
||||
pip install yt-dlp
|
||||
# 或者
|
||||
brew install yt-dlp
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```bash
|
||||
# 下载小红书笔记中的图片/视频
|
||||
opencli xiaohongshu download abc123 --output ./xhs
|
||||
|
||||
# 下载B站视频(需要 yt-dlp)
|
||||
opencli bilibili download BV1xxx --output ./bilibili
|
||||
opencli bilibili download BV1xxx --quality 1080p # 指定画质
|
||||
|
||||
# 下载 Twitter 用户的媒体
|
||||
opencli twitter download elonmusk --limit 20 --output ./twitter
|
||||
|
||||
# 下载单条推文的媒体
|
||||
opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
|
||||
|
||||
# 导出知乎文章为 Markdown
|
||||
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
|
||||
|
||||
# 导出并下载图片
|
||||
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --download-images
|
||||
|
||||
# 导出微信公众号文章为 Markdown
|
||||
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --output ./weixin
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 输出格式
|
||||
|
||||
@@ -152,6 +264,25 @@ opencli bilibili hot -f csv # CSV
|
||||
opencli bilibili hot -v # 详细模式:展示管线执行步骤调试信息
|
||||
```
|
||||
|
||||
## 插件
|
||||
|
||||
通过社区贡献的插件扩展 OpenCLI。插件使用与内置命令相同的 YAML/TS 格式,启动时自动发现。
|
||||
|
||||
```bash
|
||||
opencli plugin install github:user/opencli-plugin-my-tool # 安装
|
||||
opencli plugin list # 查看已安装
|
||||
opencli plugin update my-tool # 更新到最新
|
||||
opencli plugin uninstall my-tool # 卸载
|
||||
```
|
||||
|
||||
| 插件 | 类型 | 描述 |
|
||||
|------|------|------|
|
||||
| [opencli-plugin-github-trending](https://github.com/ByteYue/opencli-plugin-github-trending) | YAML | GitHub Trending 仓库 |
|
||||
| [opencli-plugin-hot-digest](https://github.com/ByteYue/opencli-plugin-hot-digest) | TS | 多平台热榜聚合 |
|
||||
| [opencli-plugin-juejin](https://github.com/Astro-Han/opencli-plugin-juejin) | YAML | 稀土掘金热门文章 |
|
||||
|
||||
详见 [插件指南](./docs/zh/guide/plugins.md) 了解如何创建自己的插件。
|
||||
|
||||
## 致 AI Agent(开发者指南)
|
||||
|
||||
如果你是一个被要求查阅代码并编写新 `opencli` 适配器的 AI,请遵守以下工作流。
|
||||
@@ -178,24 +309,25 @@ opencli cascade https://api.example.com/data
|
||||
|
||||
## 常见问题排查
|
||||
|
||||
- **"Failed to connect to Playwright MCP Bridge"** 报错
|
||||
- 确保你当前的 Chrome 已安装且**开启了** Playwright MCP Bridge 浏览器插件。
|
||||
- 如果是刚装完插件,需要重启 Chrome 浏览器。
|
||||
- **"Extension not connected" 报错**
|
||||
- 确保你当前的 Chrome 已安装且**开启了** opencli Browser Bridge 扩展(在 `chrome://extensions` 中检查)。
|
||||
- **"attach failed: Cannot access a chrome-extension:// URL" 报错**
|
||||
- 其他 Chrome 扩展(如 youmind、New Tab Override 或 AI 助手类扩展)可能产生冲突。请尝试**暂时禁用其他扩展**后重试。
|
||||
- **返回空数据,或者报错 "Unauthorized"**
|
||||
- Chrome 里的登录态可能已经过期(甚至被要求过滑动验证码)。请打开当前 Chrome 页面,在新标签页重新手工登录或刷新该页面。
|
||||
- Chrome 里的登录态可能已经过期。请打开当前 Chrome 页面,在新标签页重新手工登录或刷新该页面。
|
||||
- **Node API 错误 (如 parseArgs, fs 等)**
|
||||
- 确保 Node.js 版本 `>= 18`。旧版不支持我们使用的现代核心库 API。
|
||||
- 确保 Node.js 版本 `>= 20`。
|
||||
- **Daemon 问题**
|
||||
- 检查 daemon 状态:`curl localhost:19825/status`
|
||||
- 查看扩展日志:`curl localhost:19825/logs`
|
||||
|
||||
## 版本发布
|
||||
|
||||
```bash
|
||||
npm version patch # 0.1.0 → 0.1.1
|
||||
npm version minor # 0.1.0 → 0.2.0
|
||||
## Star History
|
||||
|
||||
# 推送 tag,GitHub Actions 将自动执行发版和 npm 发布
|
||||
git push --follow-tags
|
||||
```
|
||||
[](https://star-history.com/#jackwener/opencli&Date)
|
||||
|
||||
发版完成后,记得也要去 Chrome Web Store 更新浏览器插件,保持插件版本和 CLI 版本同步。
|
||||
|
||||
## License
|
||||
|
||||
[BSD-3-Clause](./LICENSE)
|
||||
[Apache-2.0](./LICENSE)
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
---
|
||||
name: opencli
|
||||
description: "OpenCLI — Make any website your CLI. Zero risk, AI-powered, reuse Chrome login."
|
||||
version: 0.5.1
|
||||
description: "OpenCLI — Make any website or Electron App your CLI. Zero risk, AI-powered, reuse Chrome login. 150+ commands across 30+ sites."
|
||||
version: 1.1.0
|
||||
author: jackwener
|
||||
tags: [cli, browser, web, mcp, playwright, bilibili, zhihu, twitter, github, v2ex, hackernews, reddit, xiaohongshu, xueqiu, AI, agent]
|
||||
tags: [cli, browser, web, chrome-extension, cdp, bilibili, zhihu, twitter, github, v2ex, hackernews, reddit, xiaohongshu, xueqiu, youtube, boss, coupang, yollomi, AI, agent]
|
||||
---
|
||||
|
||||
# OpenCLI
|
||||
|
||||
> Make any website your CLI. Reuse Chrome login, zero risk, AI-powered discovery.
|
||||
> Make any website or Electron App your CLI. Reuse Chrome login, zero risk, AI-powered discovery.
|
||||
|
||||
> [!CAUTION]
|
||||
> **AI Agent 必读:创建或修改任何适配器之前,你必须先阅读 [CLI-EXPLORER.md](./CLI-EXPLORER.md)!**
|
||||
> 该文档包含完整的 API 发现工作流(必须使用 Playwright MCP Bridge 浏览器探索)、5 级认证策略决策树、平台 SDK 速查表、`tap` 步骤调试流程、分页 API 模板、级联请求模式、以及常见陷阱。
|
||||
> 该文档包含完整的 API 发现工作流(必须使用浏览器探索)、5 级认证策略决策树、平台 SDK 速查表、`tap` 步骤调试流程、分页 API 模板、级联请求模式、以及常见陷阱。
|
||||
> **本文件(SKILL.md)仅提供命令参考和简化模板,不足以正确开发适配器。**
|
||||
|
||||
## Install & Run
|
||||
@@ -34,11 +34,12 @@ npm update -g @jackwener/opencli
|
||||
|
||||
Browser commands require:
|
||||
1. Chrome browser running **(logged into target sites)**
|
||||
2. [Playwright MCP Bridge](https://chromewebstore.google.com/detail/playwright-mcp-bridge/mmlmfjhmonkocbjadbfplnigmagldckm) extension installed and configured
|
||||
2. **opencli Browser Bridge** Chrome extension installed (load `extension/` as unpacked in `chrome://extensions`)
|
||||
3. No further setup needed — the daemon auto-starts on first browser command
|
||||
|
||||
> **Note**: You must be logged into the target website in Chrome before running commands. Tabs opened during command execution are auto-closed afterwards.
|
||||
|
||||
Public API commands (`hackernews`, `github search`, `v2ex`) need no browser.
|
||||
Public API commands (`hackernews`, `v2ex`) need no browser.
|
||||
|
||||
## Commands Reference
|
||||
|
||||
@@ -47,7 +48,7 @@ Public API commands (`hackernews`, `github search`, `v2ex`) need no browser.
|
||||
```bash
|
||||
# Bilibili (browser)
|
||||
opencli bilibili hot --limit 10 # B站热门视频
|
||||
opencli bilibili search --keyword "rust" # 搜索视频
|
||||
opencli bilibili search "rust" # 搜索视频 (query positional)
|
||||
opencli bilibili me # 我的信息
|
||||
opencli bilibili favorite # 我的收藏
|
||||
opencli bilibili history --limit 20 # 观看历史
|
||||
@@ -60,15 +61,19 @@ opencli bilibili following --limit 20 # 我的关注列表 (支持 --uid 查
|
||||
|
||||
# 知乎 (browser)
|
||||
opencli zhihu hot --limit 10 # 知乎热榜
|
||||
opencli zhihu search --keyword "AI" # 搜索
|
||||
opencli zhihu question --id 34816524 # 问题详情和回答
|
||||
opencli zhihu search "AI" # 搜索 (query positional)
|
||||
opencli zhihu question 34816524 # 问题详情和回答 (id positional)
|
||||
|
||||
# 小红书 (browser)
|
||||
opencli xiaohongshu search --keyword "美食" # 搜索笔记
|
||||
opencli xiaohongshu search "美食" # 搜索笔记 (query positional)
|
||||
opencli xiaohongshu notifications # 通知(mentions/likes/connections)
|
||||
opencli xiaohongshu feed --limit 10 # 推荐 Feed
|
||||
opencli xiaohongshu me # 我的信息
|
||||
opencli xiaohongshu user --uid xxx # 用户主页
|
||||
opencli xiaohongshu user xxx # 用户主页 (id positional)
|
||||
opencli xiaohongshu creator-notes --limit 10 # 创作者笔记列表
|
||||
opencli xiaohongshu creator-note-detail --note-id xxx # 笔记详情
|
||||
opencli xiaohongshu creator-notes-summary # 笔记数据概览
|
||||
opencli xiaohongshu creator-profile # 创作者资料
|
||||
opencli xiaohongshu creator-stats # 创作者数据统计
|
||||
|
||||
# 雪球 Xueqiu (browser)
|
||||
opencli xueqiu hot-stock --limit 10 # 雪球热门股票榜
|
||||
@@ -76,29 +81,48 @@ opencli xueqiu stock --symbol SH600519 # 查看股票实时行情
|
||||
opencli xueqiu watchlist # 获取自选股/持仓列表
|
||||
opencli xueqiu feed # 我的关注 timeline
|
||||
opencli xueqiu hot --limit 10 # 雪球热榜
|
||||
opencli xueqiu search --keyword "特斯拉" # 搜索
|
||||
opencli xueqiu search "特斯拉" # 搜索 (query positional)
|
||||
|
||||
# GitHub (public)
|
||||
opencli github search --keyword "cli" # 搜索仓库
|
||||
# GitHub (via gh External CLI)
|
||||
opencli gh repo list # 列出仓库 (passthrough to gh)
|
||||
opencli gh pr list --limit 5 # PR 列表
|
||||
opencli gh issue list # Issue 列表
|
||||
|
||||
# Twitter/X (browser)
|
||||
opencli twitter trending --limit 10 # 热门话题
|
||||
opencli twitter bookmarks --limit 20 # 获取收藏的书签推文
|
||||
opencli twitter search --keyword "AI" # 搜索推文
|
||||
opencli twitter profile --username elonmusk # 用户资料
|
||||
opencli twitter search "AI" # 搜索推文 (query positional)
|
||||
opencli twitter profile elonmusk # 用户资料
|
||||
opencli twitter timeline --limit 20 # 时间线
|
||||
opencli twitter thread 1234567890 # 推文 thread(原文 + 回复)
|
||||
opencli twitter article 1891511252174299446 # 推文长文内容
|
||||
opencli twitter follow elonmusk # 关注用户
|
||||
opencli twitter unfollow elonmusk # 取消关注
|
||||
opencli twitter bookmark https://x.com/... # 收藏推文
|
||||
opencli twitter unbookmark https://x.com/... # 取消收藏
|
||||
|
||||
# Reddit (browser)
|
||||
opencli reddit hot --limit 10 # 热门帖子
|
||||
opencli reddit hot --subreddit programming # 指定子版块
|
||||
opencli reddit frontpage --limit 10 # 首页
|
||||
opencli reddit search --keyword "AI" # 搜索
|
||||
opencli reddit subreddit --name rust # 子版块浏览
|
||||
opencli reddit frontpage --limit 10 # 首页 /r/all
|
||||
opencli reddit popular --limit 10 # /r/popular 热门
|
||||
opencli reddit search "AI" --sort top --time week # 搜索(支持排序+时间过滤)
|
||||
opencli reddit subreddit rust --sort top --time month # 子版块浏览(支持时间过滤)
|
||||
opencli reddit read --post-id 1abc123 # 阅读帖子 + 评论
|
||||
opencli reddit user spez # 用户资料(karma、注册时间)
|
||||
opencli reddit user-posts spez # 用户发帖历史
|
||||
opencli reddit user-comments spez # 用户评论历史
|
||||
opencli reddit upvote --post-id xxx --direction up # 投票(up/down/none)
|
||||
opencli reddit save --post-id xxx # 收藏帖子
|
||||
opencli reddit comment --post-id xxx "Great!" # 发表评论 (text positional)
|
||||
opencli reddit subscribe --subreddit python # 订阅子版块
|
||||
opencli reddit saved --limit 10 # 我的收藏
|
||||
opencli reddit upvoted --limit 10 # 我的赞
|
||||
|
||||
# V2EX (public + browser)
|
||||
opencli v2ex hot --limit 10 # 热门话题
|
||||
opencli v2ex latest --limit 10 # 最新话题
|
||||
opencli v2ex topic --id 1024 # 主题详情
|
||||
opencli v2ex topic 1024 # 主题详情 (id positional)
|
||||
opencli v2ex daily # 每日签到 (browser)
|
||||
opencli v2ex me # 我的信息 (browser)
|
||||
opencli v2ex notifications --limit 10 # 通知 (browser)
|
||||
@@ -113,32 +137,122 @@ opencli bbc news --limit 10 # BBC News RSS headlines
|
||||
opencli weibo hot --limit 10 # 微博热搜
|
||||
|
||||
# BOSS直聘 (browser)
|
||||
opencli boss search --query "AI agent" # 搜索职位
|
||||
opencli boss search "AI agent" # 搜索职位 (query positional)
|
||||
opencli boss detail --security-id xxx # 职位详情
|
||||
opencli boss recommend --limit 10 # 推荐职位
|
||||
opencli boss joblist --limit 10 # 职位列表
|
||||
opencli boss greet --security-id xxx # 打招呼
|
||||
opencli boss batchgreet --job-id xxx # 批量打招呼
|
||||
opencli boss send --uid xxx "消息内容" # 发消息 (text positional)
|
||||
opencli boss chatlist --limit 10 # 聊天列表
|
||||
opencli boss chatmsg --security-id xxx # 聊天记录
|
||||
opencli boss invite --security-id xxx # 邀请沟通
|
||||
opencli boss mark --security-id xxx # 标记管理
|
||||
opencli boss exchange --security-id xxx # 交换联系方式
|
||||
opencli boss resume # 简历管理
|
||||
opencli boss stats # 数据统计
|
||||
|
||||
# YouTube (browser)
|
||||
opencli youtube search --query "rust" # 搜索视频
|
||||
opencli youtube search "rust" # 搜索视频 (query positional)
|
||||
opencli youtube video "https://www.youtube.com/watch?v=xxx" # 视频元数据
|
||||
opencli youtube transcript "https://www.youtube.com/watch?v=xxx" # 获取视频字幕/转录
|
||||
opencli youtube transcript "xxx" --lang zh-Hans --mode raw # 指定语言 + 原始时间戳模式
|
||||
|
||||
# Yahoo Finance (browser)
|
||||
opencli yahoo-finance quote --symbol AAPL # 股票行情
|
||||
|
||||
# Sina Finance
|
||||
opencli sinafinance news --limit 10 --type 1 # 7x24实时快讯 (0=全部 1=A股 2=宏观 3=公司 4=数据 5=市场 6=国际 7=观点 8=央行 9=其它)
|
||||
|
||||
# Reuters (browser)
|
||||
opencli reuters search --query "AI" # 路透社搜索
|
||||
opencli reuters search "AI" # 路透社搜索 (query positional)
|
||||
|
||||
# 什么值得买 (browser)
|
||||
opencli smzdm search --keyword "耳机" # 搜索好价
|
||||
opencli smzdm search "耳机" # 搜索好价 (query positional)
|
||||
|
||||
# 携程 (browser)
|
||||
opencli ctrip search --query "三亚" # 搜索目的地
|
||||
opencli ctrip search "三亚" # 搜索目的地 (query positional)
|
||||
|
||||
# Antigravity (Electron/CDP)
|
||||
opencli antigravity status # 检查 CDP 连接
|
||||
opencli antigravity send "hello" # 发送文本到当前 agent 聊天框
|
||||
opencli antigravity read # 读取整个聊天记录面板
|
||||
opencli antigravity new # 清空聊天、开启新对话
|
||||
opencli antigravity dump # 导出 DOM 和快照调试信息
|
||||
opencli antigravity extract-code # 自动抽取 AI 回复中的代码块
|
||||
opencli antigravity model claude # 切换底层模型
|
||||
opencli antigravity watch # 流式监听增量消息
|
||||
opencli antigravity serve --port 8082 # 启动 Anthropic 兼容代理
|
||||
|
||||
# Barchart (browser)
|
||||
opencli barchart quote --symbol AAPL # 股票行情
|
||||
opencli barchart options --symbol AAPL # 期权链
|
||||
opencli barchart greeks --symbol AAPL # 期权 Greeks
|
||||
opencli barchart flow --limit 20 # 异常期权活动
|
||||
|
||||
# Jike 即刻 (browser)
|
||||
opencli jike feed --limit 10 # 动态流
|
||||
opencli jike search "AI" # 搜索 (query positional)
|
||||
opencli jike create "内容" # 发布动态 (text positional)
|
||||
opencli jike like xxx # 点赞 (id positional)
|
||||
opencli jike comment xxx "评论" # 评论 (id + text positional)
|
||||
opencli jike repost xxx # 转发 (id positional)
|
||||
opencli jike notifications # 通知
|
||||
|
||||
# Linux.do (public)
|
||||
opencli linux-do hot --limit 10 # 热门话题
|
||||
opencli linux-do latest --limit 10 # 最新话题
|
||||
opencli linux-do search "rust" # 搜索 (query positional)
|
||||
opencli linux-do topic 1024 # 主题详情 (id positional)
|
||||
|
||||
# StackOverflow (public)
|
||||
opencli stackoverflow hot --limit 10 # 热门问题
|
||||
opencli stackoverflow search "typescript" # 搜索 (query positional)
|
||||
opencli stackoverflow bounties --limit 10 # 悬赏问题
|
||||
|
||||
# WeRead 微信读书 (browser)
|
||||
opencli weread shelf --limit 10 # 书架
|
||||
opencli weread search "AI" # 搜索图书 (query positional)
|
||||
opencli weread book xxx # 图书详情 (book-id positional)
|
||||
opencli weread highlights xxx # 划线笔记 (book-id positional)
|
||||
opencli weread notes xxx # 想法笔记 (book-id positional)
|
||||
opencli weread ranking --limit 10 # 排行榜
|
||||
|
||||
# Jimeng 即梦 AI (browser)
|
||||
opencli jimeng generate --prompt "描述" # AI 生图
|
||||
opencli jimeng history --limit 10 # 生成历史
|
||||
|
||||
# Yollomi yollomi.com (browser — 需在 Chrome 登录 yollomi.com,复用站点 session)
|
||||
opencli yollomi models --type image # 列出图像模型与积分
|
||||
opencli yollomi generate "提示词" --model z-image-turbo # 文生图
|
||||
opencli yollomi video "提示词" --model kling-2-1 # 视频
|
||||
opencli yollomi upload ./photo.jpg # 上传得 URL,供 img2img / 工具链使用
|
||||
opencli yollomi remove-bg <image-url> # 去背景(免费)
|
||||
opencli yollomi edit <image-url> "改成油画风格" # Qwen 图像编辑
|
||||
|
||||
# Grok (default + explicit web)
|
||||
opencli grok ask --prompt "问题" # 提问 Grok(兼容默认路径)
|
||||
opencli grok ask --prompt "问题" --web # 显式 grok.com consumer web UI 路径
|
||||
|
||||
# HuggingFace (public)
|
||||
opencli hf top --limit 10 # 热门模型
|
||||
|
||||
# 超星学习通 (browser)
|
||||
opencli chaoxing assignments # 作业列表
|
||||
opencli chaoxing exams # 考试列表
|
||||
```
|
||||
|
||||
### Management Commands
|
||||
|
||||
```bash
|
||||
opencli list # List all commands
|
||||
opencli list # List all commands (including External CLIs)
|
||||
opencli list --json # JSON output
|
||||
opencli list -f yaml # YAML output
|
||||
opencli install <name> # Auto-install an external CLI (e.g., gh, obsidian)
|
||||
opencli register <name> # Register a local custom CLI for unified discovery
|
||||
opencli validate # Validate all CLI definitions
|
||||
opencli validate bilibili # Validate specific site
|
||||
opencli doctor # Diagnose browser bridge (auto-starts daemon, includes live test)
|
||||
```
|
||||
|
||||
### AI Agent Workflow
|
||||
@@ -153,6 +267,18 @@ opencli synthesize <site>
|
||||
# Generate: one-shot explore → synthesize → register
|
||||
opencli generate <url> --goal "hot"
|
||||
|
||||
# Record: YOU operate the page, opencli captures every API call → YAML candidates
|
||||
# Opens the URL in automation window, injects fetch/XHR interceptor into ALL tabs,
|
||||
# polls every 2s, auto-stops after 60s (or press Enter to stop early).
|
||||
opencli record <url> # 录制,site name 从域名推断
|
||||
opencli record <url> --site mysite # 指定 site name
|
||||
opencli record <url> --timeout 120000 # 自定义超时(毫秒,默认 60000)
|
||||
opencli record <url> --poll 1000 # 缩短轮询间隔(毫秒,默认 2000)
|
||||
opencli record <url> --out .opencli/record/x # 自定义输出目录
|
||||
# Output:
|
||||
# .opencli/record/<site>/captured.json ← 原始捕获数据(带 url/method/body)
|
||||
# .opencli/record/<site>/candidates/*.yaml ← 高置信度候选适配器(score ≥ 8,有 array 结果)
|
||||
|
||||
# Strategy Cascade: auto-probe PUBLIC → COOKIE → HEADER
|
||||
opencli cascade <api-url>
|
||||
|
||||
@@ -183,6 +309,129 @@ opencli bilibili hot -f csv # CSV
|
||||
opencli bilibili hot -v # Show each pipeline step and data flow
|
||||
```
|
||||
|
||||
## Record Workflow
|
||||
|
||||
`record` 是为「无法用 `explore` 自动发现」的页面(需要登录操作、复杂交互、SPA 内路由)准备的手动录制方案。
|
||||
|
||||
### 工作原理
|
||||
|
||||
```
|
||||
opencli record <url>
|
||||
→ 打开 automation window 并导航到目标 URL
|
||||
→ 向所有 tab 注入 fetch/XHR 拦截器(幂等,可重复注入)
|
||||
→ 每 2s 轮询一次:发现新 tab 自动注入,drain 所有 tab 的捕获缓冲区
|
||||
→ 超时(默认 60s)或按 Enter 停止
|
||||
→ 分析捕获到的 JSON 请求:去重 → 评分 → 生成候选 YAML
|
||||
```
|
||||
|
||||
**拦截器特性**:
|
||||
- 同时 patch `window.fetch` 和 `XMLHttpRequest`
|
||||
- 只捕获 `Content-Type: application/json` 的响应
|
||||
- 过滤纯对象少于 2 个 key 的响应(避免 tracking/ping)
|
||||
- 跨 tab 隔离:每个 tab 独立缓冲区,轮询时分别 drain
|
||||
- 幂等注入:同一 tab 二次注入时先 restore 原始函数再重新 patch,不丢失已捕获数据
|
||||
|
||||
### 使用步骤
|
||||
|
||||
```bash
|
||||
# 1. 启动录制(建议 --timeout 给足操作时间)
|
||||
opencli record "https://example.com/page" --timeout 120000
|
||||
|
||||
# 2. 在弹出的 automation window 里正常操作页面:
|
||||
# - 打开列表、搜索、点击条目、切换 Tab
|
||||
# - 凡是触发网络请求的操作都会被捕获
|
||||
|
||||
# 3. 完成操作后按 Enter 停止(或等超时自动停止)
|
||||
|
||||
# 4. 查看结果
|
||||
cat .opencli/record/<site>/captured.json # 原始捕获
|
||||
ls .opencli/record/<site>/candidates/ # 候选 YAML
|
||||
```
|
||||
|
||||
### 页面类型与捕获预期
|
||||
|
||||
| 页面类型 | 预期捕获量 | 说明 |
|
||||
|---------|-----------|------|
|
||||
| 列表/搜索页 | 多(5~20+) | 每次搜索/翻页都会触发新请求 |
|
||||
| 详情页(只读) | 少(1~5) | 首屏数据一次性返回,后续操作走 form/redirect |
|
||||
| SPA 内路由跳转 | 中等 | 路由切换会触发新接口,但首屏请求在注入前已发出 |
|
||||
| 需要登录的页面 | 视操作而定 | 确保 Chrome 已登录目标网站 |
|
||||
|
||||
> **注意**:如果页面在导航完成前就发出了大部分请求(服务端渲染 / SSR 注水),拦截器会错过这些请求。
|
||||
> 解决方案:在页面加载完成后,手动触发能产生新请求的操作(搜索、翻页、切 Tab、展开折叠项等)。
|
||||
|
||||
### 候选 YAML → TS CLI 转换
|
||||
|
||||
生成的候选 YAML 是起点,通常需要转换为 TypeScript(尤其是 tae 等内部系统):
|
||||
|
||||
**候选 YAML 结构**(自动生成):
|
||||
```yaml
|
||||
site: tae
|
||||
name: getList # 从 URL path 推断的名称
|
||||
strategy: cookie
|
||||
browser: true
|
||||
pipeline:
|
||||
- navigate: https://...
|
||||
- evaluate: |
|
||||
(async () => {
|
||||
const res = await fetch('/approval/getList.json?procInsId=...', { credentials: 'include' });
|
||||
const data = await res.json();
|
||||
return (data?.content?.operatorRecords || []).map(item => ({ ... }));
|
||||
})()
|
||||
```
|
||||
|
||||
**转换为 TS CLI**(参考 `src/clis/tae/add-expense.ts` 风格):
|
||||
```typescript
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
|
||||
cli({
|
||||
site: 'tae',
|
||||
name: 'get-approval',
|
||||
description: '查看报销单审批流程和操作记录',
|
||||
domain: 'tae.alibaba-inc.com',
|
||||
strategy: Strategy.COOKIE,
|
||||
browser: true,
|
||||
args: [
|
||||
{ name: 'proc_ins_id', type: 'string', required: true, positional: true, help: '流程实例 ID(procInsId)' },
|
||||
],
|
||||
columns: ['step', 'operator', 'action', 'time'],
|
||||
func: async (page, kwargs) => {
|
||||
await page.goto('https://tae.alibaba-inc.com/expense/pc.html?_authType=SAML');
|
||||
await page.wait(2);
|
||||
const result = await page.evaluate(`(async () => {
|
||||
const res = await fetch('/approval/getList.json?taskId=&procInsId=${kwargs.proc_ins_id}', {
|
||||
credentials: 'include'
|
||||
});
|
||||
const data = await res.json();
|
||||
return data?.content?.operatorRecords || [];
|
||||
})()`);
|
||||
return (result as any[]).map((r, i) => ({
|
||||
step: i + 1,
|
||||
operator: r.operatorName || r.userId,
|
||||
action: r.operationType,
|
||||
time: r.operateTime,
|
||||
}));
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**转换要点**:
|
||||
1. URL 中的动态 ID(`procInsId`、`taskId` 等)提取为 `args`
|
||||
2. `captured.json` 里的真实 body 结构用于确定正确的数据路径(如 `content.operatorRecords`)
|
||||
3. tae 系统统一用 `{ success, content, errorCode, errorMsg }` 外层包裹,取数据要走 `content.*`
|
||||
4. 认证方式:cookie(`credentials: 'include'`),不需要额外 header
|
||||
5. 文件放入 `src/clis/<site>/`,无需手动注册,`npm run build` 后自动发现
|
||||
|
||||
### 故障排查
|
||||
|
||||
| 现象 | 原因 | 解法 |
|
||||
|------|------|------|
|
||||
| 捕获 0 条请求 | 拦截器注入失败,或页面无 JSON API | 检查 daemon 是否运行:`curl localhost:19825/status` |
|
||||
| 捕获量少(1~3 条) | 页面是只读详情页,首屏数据已在注入前发出 | 手动操作触发更多请求(搜索/翻页),或换用列表页 |
|
||||
| 候选 YAML 为 0 | 捕获到的 JSON 都没有 array 结构 | 直接看 `captured.json` 手写 TS CLI |
|
||||
| 新开的 tab 没有被拦截 | 轮询间隔内 tab 已关闭 | 缩短 `--poll 500` |
|
||||
| 二次运行 record 时数据不连续 | 正常,每次 `record` 启动都是新的 automation window | 无需处理 |
|
||||
|
||||
## Creating Adapters
|
||||
|
||||
> [!TIP]
|
||||
@@ -191,7 +440,7 @@ opencli bilibili hot -v # Show each pipeline step and data flow
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **完整模式 — 在写任何代码之前,先阅读 [CLI-EXPLORER.md](./CLI-EXPLORER.md)。**
|
||||
> 它包含:① AI Agent 浏览器探索工作流(必须用 Playwright MCP 抓包验证 API)② 认证策略决策树 ③ 平台 SDK(如 Bilibili 的 `apiGet`/`fetchJson`)④ YAML vs TS 选择指南 ⑤ `tap` 步骤调试方法 ⑥ 级联请求模板 ⑦ 常见陷阱表。
|
||||
> 它包含:① AI Agent 浏览器探索工作流 ② 认证策略决策树 ③ 平台 SDK(如 Bilibili 的 `apiGet`/`fetchJson`)④ YAML vs TS 选择指南 ⑤ `tap` 步骤调试方法 ⑥ 级联请求模板 ⑦ 常见陷阱表。
|
||||
> **下方仅为简化模板参考,直接使用极易踩坑。**
|
||||
|
||||
### YAML Pipeline (declarative, recommended)
|
||||
@@ -261,7 +510,7 @@ cli({
|
||||
site: 'mysite',
|
||||
name: 'search',
|
||||
strategy: Strategy.INTERCEPT, // Or COOKIE
|
||||
args: [{ name: 'keyword', required: true }],
|
||||
args: [{ name: 'query', required: true, positional: true }],
|
||||
columns: ['rank', 'title', 'url'],
|
||||
func: async (page, kwargs) => {
|
||||
await page.goto('https://www.mysite.com/search');
|
||||
@@ -312,7 +561,7 @@ cli({
|
||||
|
||||
```yaml
|
||||
# Arguments with defaults
|
||||
${{ args.keyword }}
|
||||
${{ args.query }}
|
||||
${{ args.limit | default(20) }}
|
||||
|
||||
# Current item (in map/filter)
|
||||
@@ -338,16 +587,18 @@ ${{ index + 1 }}
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `OPENCLI_DAEMON_PORT` | 19825 | Daemon listen port |
|
||||
| `OPENCLI_BROWSER_CONNECT_TIMEOUT` | 30 | Browser connection timeout (sec) |
|
||||
| `OPENCLI_BROWSER_COMMAND_TIMEOUT` | 45 | Command execution timeout (sec) |
|
||||
| `OPENCLI_BROWSER_EXPLORE_TIMEOUT` | 120 | Explore timeout (sec) |
|
||||
| `PLAYWRIGHT_MCP_EXTENSION_TOKEN` | — | Auto-approve extension connection |
|
||||
| `OPENCLI_VERBOSE` | — | Show daemon/extension logs |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| `npx not found` | Install Node.js: `brew install node` |
|
||||
| `Timed out connecting to browser` | 1) Chrome must be open 2) Install MCP Bridge extension and configure token |
|
||||
| `Extension not connected` | 1) Chrome must be open 2) Install opencli Browser Bridge extension |
|
||||
| `Target page context` error | Add `navigate:` step before `evaluate:` in YAML |
|
||||
| Empty table data | Check if evaluate returns JSON string (MCP parsing) or data path is wrong |
|
||||
| Empty table data | Check if evaluate returns correct data path |
|
||||
| Daemon issues | `curl localhost:19825/status` to check, `curl localhost:19825/logs` for extension logs |
|
||||
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
# Testing Guide
|
||||
|
||||
> 面向开发者和 AI Agent 的测试参考手册。
|
||||
|
||||
## 目录
|
||||
|
||||
- [测试架构](#测试架构)
|
||||
- [当前覆盖范围](#当前覆盖范围)
|
||||
- [本地运行测试](#本地运行测试)
|
||||
- [如何添加新测试](#如何添加新测试)
|
||||
- [CI/CD 流水线](#cicd-流水线)
|
||||
- [浏览器模式](#浏览器模式)
|
||||
- [站点兼容性](#站点兼容性)
|
||||
|
||||
---
|
||||
|
||||
## 测试架构
|
||||
|
||||
测试分为三层,全部使用 **vitest** 运行:
|
||||
|
||||
```text
|
||||
tests/
|
||||
├── e2e/ # E2E 集成测试(子进程运行真实 CLI)
|
||||
│ ├── helpers.ts # runCli() / parseJsonOutput() 共享工具
|
||||
│ ├── public-commands.test.ts # 公开 API 命令
|
||||
│ ├── browser-public.test.ts # 浏览器命令(公开数据)
|
||||
│ ├── browser-auth.test.ts # 需登录命令(graceful failure)
|
||||
│ ├── management.test.ts # 管理命令(list / validate / verify / help)
|
||||
│ └── output-formats.test.ts # 输出格式校验
|
||||
├── smoke/
|
||||
│ └── api-health.test.ts # 外部 API、adapter 定义、命令注册健康检查
|
||||
src/
|
||||
└── **/*.test.ts # 单元测试(当前 32 个文件)
|
||||
```
|
||||
|
||||
| 层 | 位置 | 当前文件数 | 运行方式 | 用途 |
|
||||
|---|---|---:|---|---|
|
||||
| 单元测试 | `src/**/*.test.ts` | 32 | `npx vitest run src/` | 内部模块、pipeline、adapter 工具函数 |
|
||||
| E2E 测试 | `tests/e2e/*.test.ts` | 5 | `npx vitest run tests/e2e/` | 真实 CLI 命令执行 |
|
||||
| 烟雾测试 | `tests/smoke/*.test.ts` | 1 | `npx vitest run tests/smoke/` | 外部 API 与注册完整性 |
|
||||
|
||||
---
|
||||
|
||||
## 当前覆盖范围
|
||||
|
||||
### 单元测试(32 个文件)
|
||||
|
||||
| 领域 | 文件 |
|
||||
|---|---|
|
||||
| 核心运行时与输出 | `src/browser.test.ts`, `src/browser/dom-snapshot.test.ts`, `src/build-manifest.test.ts`, `src/capabilityRouting.test.ts`, `src/doctor.test.ts`, `src/engine.test.ts`, `src/interceptor.test.ts`, `src/output.test.ts`, `src/plugin.test.ts`, `src/registry.test.ts`, `src/snapshotFormatter.test.ts` |
|
||||
| pipeline 与下载 | `src/download/index.test.ts`, `src/pipeline/executor.test.ts`, `src/pipeline/template.test.ts`, `src/pipeline/transform.test.ts` |
|
||||
| 站点 / adapter 逻辑 | `src/clis/apple-podcasts/commands.test.ts`, `src/clis/apple-podcasts/utils.test.ts`, `src/clis/bloomberg/utils.test.ts`, `src/clis/chaoxing/utils.test.ts`, `src/clis/coupang/utils.test.ts`, `src/clis/google/utils.test.ts`, `src/clis/grok/ask.test.ts`, `src/clis/twitter/timeline.test.ts`, `src/clis/weread/utils.test.ts`, `src/clis/xiaohongshu/creator-note-detail.test.ts`, `src/clis/xiaohongshu/creator-notes-summary.test.ts`, `src/clis/xiaohongshu/creator-notes.test.ts`, `src/clis/xiaohongshu/search.test.ts`, `src/clis/xiaohongshu/user-helpers.test.ts`, `src/clis/xiaoyuzhou/utils.test.ts`, `src/clis/youtube/transcript-group.test.ts`, `src/clis/zhihu/download.test.ts` |
|
||||
|
||||
这些测试覆盖的重点包括:
|
||||
|
||||
- Browser Bridge、DOM snapshot、interceptor、capability routing
|
||||
- manifest 生成、命令发现、插件安装与注册表
|
||||
- 输出格式渲染与 snapshot formatting
|
||||
- pipeline 模板求值、执行器与变换步骤
|
||||
- 各站点 adapter 的数据归一化、参数处理与容错逻辑
|
||||
|
||||
### E2E 测试(5 个文件)
|
||||
|
||||
| 文件 | 当前覆盖范围 |
|
||||
|---|---|
|
||||
| `tests/e2e/public-commands.test.ts` | `bloomberg`、`apple-podcasts`、`hackernews`、`v2ex`、`xiaoyuzhou`、`google suggest` 等公开命令 |
|
||||
| `tests/e2e/browser-public.test.ts` | `bbc`、`bloomberg`、`bilibili`、`weibo`、`zhihu`、`reddit`、`twitter`、`xueqiu`、`reuters`、`youtube`、`smzdm`、`boss`、`ctrip`、`coupang`、`xiaohongshu`、`google`、`yahoo-finance`、`v2ex daily` |
|
||||
| `tests/e2e/browser-auth.test.ts` | `bilibili`、`twitter`、`v2ex`、`xueqiu`、`linux-do`、`xiaohongshu` 的需登录命令 graceful failure |
|
||||
| `tests/e2e/management.test.ts` | `list`、`validate`、`verify`、`--version`、`--help`、unknown command |
|
||||
| `tests/e2e/output-formats.test.ts` | `json` / `yaml` / `csv` / `md` 输出格式校验 |
|
||||
|
||||
### 烟雾测试(1 个文件)
|
||||
|
||||
| 文件 | 当前覆盖范围 |
|
||||
|---|---|
|
||||
| `tests/smoke/api-health.test.ts` | `hackernews`、`v2ex` 公开 API 可用性,`validate` 全量 adapter 校验,以及命令注册表基础完整性 |
|
||||
|
||||
### 快速核对命令
|
||||
|
||||
需要刷新测试清单时,直接以仓库文件为准:
|
||||
|
||||
```bash
|
||||
find src -name '*.test.ts' | sort
|
||||
find tests/e2e -name '*.test.ts' | sort
|
||||
find tests/smoke -name '*.test.ts' | sort
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 本地运行测试
|
||||
|
||||
### 前置条件
|
||||
|
||||
```bash
|
||||
npm ci # 安装依赖
|
||||
npm run build # 编译(E2E / smoke 测试需要 dist/main.js)
|
||||
```
|
||||
|
||||
### 运行命令
|
||||
|
||||
```bash
|
||||
# 全部单元测试
|
||||
npx vitest run src/
|
||||
|
||||
# 全部 E2E 测试(会真实调用外部 API / 浏览器)
|
||||
npx vitest run tests/e2e/
|
||||
|
||||
# 全部 smoke 测试
|
||||
npx vitest run tests/smoke/
|
||||
|
||||
# 单个测试文件
|
||||
npx vitest run src/clis/apple-podcasts/commands.test.ts
|
||||
npx vitest run tests/e2e/management.test.ts
|
||||
|
||||
# 全部测试
|
||||
npx vitest run
|
||||
|
||||
# watch 模式(开发时推荐)
|
||||
npx vitest src/
|
||||
```
|
||||
|
||||
### 浏览器命令本地测试须知
|
||||
|
||||
- opencli 通过 Browser Bridge 扩展连接已运行的 Chrome 浏览器
|
||||
- E2E 测试通过 `tests/e2e/helpers.ts` 里的 `runCli()` 调用已构建的 `dist/main.js`
|
||||
- `browser-public.test.ts` 使用 `tryBrowserCommand()`,站点反爬或地域限制导致空数据时会 warn + pass
|
||||
- `browser-auth.test.ts` 验证 **graceful failure**,重点是不 crash、不 hang、错误信息可控
|
||||
- 如需测试完整登录态,保持 Chrome 登录态并安装 Browser Bridge 扩展,再手动运行对应测试
|
||||
|
||||
---
|
||||
|
||||
## 如何添加新测试
|
||||
|
||||
### 新增 YAML Adapter(如 `src/clis/producthunt/trending.yaml`)
|
||||
|
||||
1. `opencli validate` 的 E2E / smoke 测试会覆盖 adapter 结构校验
|
||||
2. 根据 adapter 类型,在对应测试文件补一个 `it()` block
|
||||
|
||||
```typescript
|
||||
// 如果 browser: false(公开 API)→ tests/e2e/public-commands.test.ts
|
||||
it('producthunt trending returns data', async () => {
|
||||
const { stdout, code } = await runCli(['producthunt', 'trending', '--limit', '3', '-f', 'json']);
|
||||
expect(code).toBe(0);
|
||||
const data = parseJsonOutput(stdout);
|
||||
expect(Array.isArray(data)).toBe(true);
|
||||
expect(data.length).toBeGreaterThanOrEqual(1);
|
||||
expect(data[0]).toHaveProperty('title');
|
||||
}, 30_000);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 如果 browser: true 但可公开访问 → tests/e2e/browser-public.test.ts
|
||||
it('producthunt trending returns data', async () => {
|
||||
const data = await tryBrowserCommand(['producthunt', 'trending', '--limit', '3', '-f', 'json']);
|
||||
expectDataOrSkip(data, 'producthunt trending');
|
||||
}, 60_000);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 如果 browser: true 且需登录 → tests/e2e/browser-auth.test.ts
|
||||
it('producthunt me fails gracefully without login', async () => {
|
||||
await expectGracefulAuthFailure(['producthunt', 'me', '-f', 'json'], 'producthunt me');
|
||||
}, 60_000);
|
||||
```
|
||||
|
||||
### 新增管理命令(如 `opencli export`)
|
||||
|
||||
在 `tests/e2e/management.test.ts` 添加测试;如果新命令会影响输出格式,也同步补 `tests/e2e/output-formats.test.ts`。
|
||||
|
||||
### 新增内部模块
|
||||
|
||||
在对应源码旁创建 `*.test.ts`,优先和被测模块放在同一目录下,便于发现与维护。
|
||||
|
||||
### 决策流程图
|
||||
|
||||
```text
|
||||
新增功能 → 是内部模块? → 是 → src/ 下加 *.test.ts
|
||||
↓ 否
|
||||
是 CLI 命令? → browser: false? → tests/e2e/public-commands.test.ts
|
||||
↓ true
|
||||
公开数据? → tests/e2e/browser-public.test.ts
|
||||
↓ 需登录
|
||||
tests/e2e/browser-auth.test.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD 流水线
|
||||
|
||||
### `ci.yml`
|
||||
|
||||
| Job | 触发条件 | 内容 |
|
||||
|---|---|---|
|
||||
| `build` | push/PR 到 `main`,`dev` | `tsc --noEmit` + `npm run build` |
|
||||
| `unit-test` | push/PR 到 `main`,`dev` | Node `20` 与 `22` 双版本运行 `src/` 单元测试,按 `2` shard 并行 |
|
||||
| `smoke-test` | `schedule` 或 `workflow_dispatch` | 安装真实 Chrome,`xvfb-run` 执行 `tests/smoke/` |
|
||||
|
||||
### `e2e-headed.yml`
|
||||
|
||||
| Job | 触发条件 | 内容 |
|
||||
|---|---|---|
|
||||
| `e2e-headed` | push/PR 到 `main`,`dev`,或手动触发 | 安装真实 Chrome,`xvfb-run` 执行 `tests/e2e/` |
|
||||
|
||||
E2E 与 smoke 都使用 `./.github/actions/setup-chrome` 准备真实 Chrome,并通过 `OPENCLI_BROWSER_EXECUTABLE_PATH` 注入浏览器路径。
|
||||
|
||||
### Sharding
|
||||
|
||||
单元测试使用 vitest 内置 shard,并在 Node `20` / `22` 两个版本上运行:
|
||||
|
||||
```yaml
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ['20', '22']
|
||||
shard: [1, 2]
|
||||
steps:
|
||||
- run: npx vitest run src/ --reporter=verbose --shard=${{ matrix.shard }}/2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 浏览器模式
|
||||
|
||||
opencli 通过 Browser Bridge 扩展连接浏览器:
|
||||
|
||||
| 条件 | 模式 | 使用场景 |
|
||||
|---|---|---|
|
||||
| 扩展已安装 / 已连接 | Extension 模式 | 本地用户,连接已登录的 Chrome |
|
||||
| 无扩展 token | CLI 自行拉起浏览器 | CI、无登录态或纯自动化场景 |
|
||||
|
||||
CI 中使用 `OPENCLI_BROWSER_EXECUTABLE_PATH` 指定真实 Chrome 路径:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 站点兼容性
|
||||
|
||||
GitHub Actions 的美国 runner 上,部分站点会因为地域限制、登录要求或反爬而返回空数据。当前 E2E 对这些场景采用 warn + pass 策略,避免偶发站点限制把整条 CI 打红。
|
||||
|
||||
| 站点 | CI 表现 | 常见原因 |
|
||||
|---|---|---|
|
||||
| `hackernews`、`bbc`、`v2ex`、`bloomberg` | 通常返回数据 | 公开接口或公开页面 |
|
||||
| `yahoo-finance`、`google` | 通常返回数据 | 页面公开,但仍可能受限流影响 |
|
||||
| `bilibili`、`zhihu`、`weibo`、`xiaohongshu`、`xueqiu` | 容易空数据 | 地域限制、反爬、登录要求 |
|
||||
| `reddit`、`twitter`、`youtube` | 容易空数据 | 登录态、cookie、机器人检测 |
|
||||
| `smzdm`、`boss`、`ctrip`、`coupang`、`linux-do` | 结果波动较大 | 地域限制、风控或页面结构变动 |
|
||||
|
||||
> 如果需要更稳定的浏览器 E2E 结果,优先使用具备目标站点网络可达性的 self-hosted runner。
|
||||
@@ -0,0 +1,208 @@
|
||||
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: 'Browser Bridge', link: '/guide/browser-bridge' },
|
||||
{ text: 'Troubleshooting', link: '/guide/troubleshooting' },
|
||||
{ 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: '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: 'Chaoxing', link: '/adapters/browser/chaoxing' },
|
||||
{ text: 'Grok', link: '/adapters/browser/grok' },
|
||||
{ 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: 'Public API Adapters',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'HackerNews', link: '/adapters/browser/hackernews' },
|
||||
{ text: 'Dev.to', link: '/adapters/browser/devto' },
|
||||
{ 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: 'Barchart', link: '/adapters/browser/barchart' },
|
||||
{ text: 'Hugging Face', link: '/adapters/browser/hf' },
|
||||
{ text: 'Sina Finance', link: '/adapters/browser/sinafinance' },
|
||||
{ text: 'Stack Overflow', link: '/adapters/browser/stackoverflow' },
|
||||
{ text: 'Wikipedia', link: '/adapters/browser/wikipedia' },
|
||||
],
|
||||
},
|
||||
{
|
||||
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' },
|
||||
],
|
||||
},
|
||||
],
|
||||
'/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: '插件', 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',
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli apple-podcasts search --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli apple-podcasts search -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli apple-podcasts search -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses public API
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses public arXiv API
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and able to open `barchart.com`
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,26 @@
|
||||
# BBC News
|
||||
|
||||
**Mode**: 🌐 Public · **Domain**: `bbc.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli bbc news` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli bbc news --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli bbc news -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli bbc news -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses public API
|
||||
@@ -0,0 +1,47 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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 -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** bilibili.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,70 @@
|
||||
# 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:
|
||||
- `main`
|
||||
- `markets`
|
||||
- `economics`
|
||||
- `industries`
|
||||
- `tech`
|
||||
- `politics`
|
||||
- `businessweek`
|
||||
- `opinions`
|
||||
- `feeds`
|
||||
- `bloomberg news` works 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
|
||||
|
||||
```bash
|
||||
# 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/example
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- RSS commands do not require Chrome.
|
||||
- `bloomberg news` requires:
|
||||
- Chrome running
|
||||
- a Chrome session that can already access the target Bloomberg article page
|
||||
- the [Browser Bridge extension](/guide/browser-bridge)
|
||||
|
||||
## Notes
|
||||
|
||||
- RSS commands support `--limit` with a maximum of 20 items.
|
||||
- If `bloomberg news` fails on a page from RSS, try a different standard story/article link first; not every Bloomberg URL in feeds is a normal article page.
|
||||
@@ -0,0 +1,28 @@
|
||||
# BOSS Zhipin
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `zhipin.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli boss search` | |
|
||||
| `opencli boss detail` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli boss search --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli boss search -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli boss search -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** zhipin.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,39 @@
|
||||
# 超星学习通 (Chaoxing)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `mooc2-ans.chaoxing.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli chaoxing assignments` | 学习通作业列表 |
|
||||
| `opencli chaoxing exams` | 学习通考试列表 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| 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](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,28 @@
|
||||
# Coupang
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `coupang.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli coupang search` | |
|
||||
| `opencli coupang add-to-cart` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli coupang search --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli coupang search -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli coupang search -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** coupang.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,27 @@
|
||||
# Ctrip (携程)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `ctrip.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli ctrip search` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli ctrip search --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli ctrip search -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli ctrip search -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** ctrip.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,35 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses the public DEV.to API
|
||||
@@ -0,0 +1,38 @@
|
||||
# 豆瓣 (Douban)
|
||||
|
||||
**Mode**: 🔐 Browser (Cookie) · **Domain**: `douban.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli douban movie-hot` | 豆瓣电影热门榜单 |
|
||||
| `opencli douban book-hot` | 豆瓣图书热门榜单 |
|
||||
| `opencli douban search` | 搜索豆瓣电影、图书或音乐 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# 电影热门
|
||||
opencli douban movie-hot --limit 10
|
||||
|
||||
# 图书热门
|
||||
opencli douban book-hot --limit 10
|
||||
|
||||
# 搜索电影
|
||||
opencli douban search "流浪地球"
|
||||
|
||||
# 搜索图书
|
||||
opencli douban search --type book "三体"
|
||||
|
||||
# 搜索音乐
|
||||
opencli douban search --type music "周杰伦"
|
||||
|
||||
# JSON output
|
||||
opencli douban movie-hot -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome logged into `douban.com`
|
||||
- Browser Bridge extension installed
|
||||
@@ -0,0 +1,35 @@
|
||||
# doubao
|
||||
|
||||
Browser adapter for [Doubao Chat](https://www.doubao.com/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](https://www.doubao.com/)
|
||||
- Playwright MCP Bridge / browser bridge is configured for OpenCLI
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
opencli doubao status
|
||||
opencli doubao new
|
||||
opencli doubao send "帮我总结这段文档"
|
||||
opencli doubao read
|
||||
opencli doubao ask "请写一个 Python 快速排序示例" --timeout 90
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The adapter targets the web chat page at `https://www.doubao.com/chat`
|
||||
- `new` first tries the visible "New Chat / 新对话" button, then falls back to the new-thread route
|
||||
- `ask` uses DOM polling, so very long generations may need a larger `--timeout`
|
||||
@@ -0,0 +1,36 @@
|
||||
# Facebook
|
||||
|
||||
**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
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** facebook.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,62 @@
|
||||
# Google
|
||||
|
||||
**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 needed
|
||||
- `news` — RSS feed, supports top stories and keyword search
|
||||
- `trends` — RSS feed, supports different regions
|
||||
- `google search` uses browser mode to extract results from google.com.
|
||||
|
||||
## Current limitations
|
||||
|
||||
- `google search` may trigger CAPTCHA in Standalone browser mode. Extension mode (with an established Chrome session) is more reliable.
|
||||
- Google frequently changes its DOM structure. If `search` stops returning results, selectors may need updating.
|
||||
- Snippet extraction may return empty for some results depending on Google's layout.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `suggest`, `news`, `trends` do not require Chrome.
|
||||
- `search` requires:
|
||||
- Chrome running (or Standalone mode will auto-launch)
|
||||
- For best results, use the [Browser Bridge extension](/guide/browser-bridge) with an established Google session
|
||||
|
||||
## Notes
|
||||
|
||||
- `suggest` defaults to `--lang zh-CN`; other commands default to `--lang en`.
|
||||
- `news` supports `--lang` and `--region` parameters for localized results.
|
||||
- `trends` traffic values are raw strings (e.g. "500K+", "1,000,000+"), not numeric.
|
||||
- `search` output includes three result types: `result` (standard), `snippet` (featured answer box), and `paa` (People Also Ask).
|
||||
@@ -0,0 +1,53 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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 180
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| 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 ask` keeps the upstream/default behavior intact.
|
||||
- `opencli grok ask --web` switches to the newer hardened consumer-web implementation.
|
||||
- The `--web` path 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](/guide/browser-bridge) installed
|
||||
|
||||
## Caveats
|
||||
|
||||
- `--web` drives 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.
|
||||
@@ -0,0 +1,42 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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 date
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses public API
|
||||
@@ -0,0 +1,42 @@
|
||||
# Hugging Face
|
||||
|
||||
**Mode**: 🌐 Public · **Domain**: `huggingface.co`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli hf top` | Top upvoted Hugging Face papers |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| 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
|
||||
@@ -0,0 +1,46 @@
|
||||
# Instagram
|
||||
|
||||
**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
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** instagram.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,45 @@
|
||||
# 即刻 (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
|
||||
|
||||
```bash
|
||||
# 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 json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** web.okjike.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,39 @@
|
||||
# 即梦AI (Jimeng)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `jimeng.jianying.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli jimeng generate` | 即梦AI 文生图 — 输入 prompt 生成图片 |
|
||||
| `opencli jimeng history` | 查看生成历史 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# 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 10
|
||||
```
|
||||
|
||||
### Options (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](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,27 @@
|
||||
# LinkedIn
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `linkedin.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli linkedin search` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli linkedin search --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli linkedin search -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli linkedin search -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** linkedin.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,45 @@
|
||||
# LINUX DO
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `linux.do`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli linux-do hot` | 热门话题 |
|
||||
| `opencli linux-do latest` | 最新话题 |
|
||||
| `opencli linux-do categories` | 板块列表 |
|
||||
| `opencli linux-do category` | 板块话题 |
|
||||
| `opencli linux-do search` | 搜索话题 |
|
||||
| `opencli linux-do topic` | 话题详情 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Hot topics this week
|
||||
opencli linux-do hot --limit 20
|
||||
|
||||
# Hot topics by period
|
||||
opencli linux-do hot --period daily
|
||||
opencli linux-do hot --period monthly
|
||||
|
||||
# Latest topics
|
||||
opencli linux-do latest --limit 10
|
||||
|
||||
# List all categories
|
||||
opencli linux-do categories
|
||||
|
||||
# Search topics
|
||||
opencli linux-do search "NixOS"
|
||||
|
||||
# View topic details
|
||||
opencli linux-do topic 12345
|
||||
|
||||
# JSON output
|
||||
opencli linux-do hot -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** linux.do
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,32 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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 -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
None — all commands use the public JSON API, no browser or login required.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Medium
|
||||
|
||||
**Mode**: 🌗 Mixed · **Domain**: `medium.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli medium feed` | Get hot Medium posts, optionally scoped to a topic |
|
||||
| `opencli medium search` | Search Medium posts by keyword |
|
||||
| `opencli medium user` | Get recent articles by a user |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Get the general Medium feed
|
||||
opencli medium feed --limit 10
|
||||
|
||||
# Search posts by keyword
|
||||
opencli medium search ai
|
||||
|
||||
# Get articles by a user
|
||||
opencli medium user @username
|
||||
|
||||
# Topic feed as JSON
|
||||
opencli medium feed --topic programming -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `opencli medium search` can run without a browser
|
||||
- `opencli medium feed` and `opencli medium user` require Browser Bridge access to `medium.com`
|
||||
@@ -0,0 +1,50 @@
|
||||
# Reddit
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `reddit.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli reddit hot` | |
|
||||
| `opencli reddit frontpage` | |
|
||||
| `opencli reddit popular` | |
|
||||
| `opencli reddit search` | |
|
||||
| `opencli reddit subreddit` | |
|
||||
| `opencli reddit read` | |
|
||||
| `opencli reddit user` | |
|
||||
| `opencli reddit user-posts` | |
|
||||
| `opencli reddit user-comments` | |
|
||||
| `opencli reddit upvote` | |
|
||||
| `opencli reddit save` | |
|
||||
| `opencli reddit comment` | |
|
||||
| `opencli reddit subscribe` | |
|
||||
| `opencli reddit saved` | |
|
||||
| `opencli reddit upvoted` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli reddit hot --limit 5
|
||||
|
||||
# Read one subreddit
|
||||
opencli reddit subreddit python --limit 10
|
||||
|
||||
# Read a post thread
|
||||
opencli reddit read 1abc123 --depth 2
|
||||
|
||||
# Comment on a post
|
||||
opencli reddit comment 1abc123 "Great post"
|
||||
|
||||
# JSON output
|
||||
opencli reddit hot -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli reddit hot -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** reddit.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,27 @@
|
||||
# Reuters
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `reuters.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli reuters search` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli reuters search --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli reuters search -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli reuters search -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** reuters.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,36 @@
|
||||
# 新浪博客 (Sina Blog)
|
||||
|
||||
**Mode**: 🌐 Public (search) / 🔐 Browser (hot, article, user) · **Domain**: `blog.sina.com.cn`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli sinablog hot` | 获取新浪博客热门文章/推荐 |
|
||||
| `opencli sinablog search` | 搜索新浪博客文章(通过新浪搜索,无需浏览器) |
|
||||
| `opencli sinablog article` | 获取新浪博客单篇文章详情 |
|
||||
| `opencli sinablog user` | 获取新浪博客用户的文章列表 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# 热门文章
|
||||
opencli sinablog hot --limit 10
|
||||
|
||||
# 搜索文章(公开 API,无需浏览器)
|
||||
opencli sinablog search "人工智能"
|
||||
|
||||
# 文章详情
|
||||
opencli sinablog article "https://blog.sina.com.cn/s/blog_xxx.html"
|
||||
|
||||
# 用户文章列表
|
||||
opencli sinablog user 1234567890 --limit 10
|
||||
|
||||
# JSON output
|
||||
opencli sinablog hot -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `search` command: No login required (public API)
|
||||
- `hot`, `article`, `user` commands: Chrome with `blog.sina.com.cn` accessible, Browser Bridge extension installed
|
||||
@@ -0,0 +1,35 @@
|
||||
# 新浪财经 (Sina Finance)
|
||||
|
||||
**Mode**: 🌐 Public · **Domain**: `finance.sina.com.cn`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli sinafinance news` | 新浪财经 7×24 小时实时快讯 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Latest financial news
|
||||
opencli sinafinance news --limit 20
|
||||
|
||||
# Filter by type
|
||||
opencli sinafinance news --type 1 # A股
|
||||
opencli sinafinance news --type 2 # 宏观
|
||||
opencli sinafinance news --type 6 # 国际
|
||||
|
||||
# JSON output
|
||||
opencli sinafinance news -f json
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--limit` | Max results, up to 50 (default: 20) |
|
||||
| `--type` | News type: `0`=全部, `1`=A股, `2`=宏观, `3`=公司, `4`=数据, `5`=市场, `6`=国际, `7`=观点, `8`=央行, `9`=其它 |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses public API
|
||||
@@ -0,0 +1,27 @@
|
||||
# SMZDM (什么值得买)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `smzdm.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli smzdm search` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli smzdm search --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli smzdm search -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli smzdm search -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** smzdm.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,35 @@
|
||||
# Stack Overflow
|
||||
|
||||
**Mode**: 🌐 Public · **Domain**: `stackoverflow.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli stackoverflow hot` | Hot questions |
|
||||
| `opencli stackoverflow search` | Search questions |
|
||||
| `opencli stackoverflow bounties` | Questions with active bounties |
|
||||
| `opencli stackoverflow unanswered` | Unanswered questions |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Hot questions
|
||||
opencli stackoverflow hot --limit 10
|
||||
|
||||
# Search questions
|
||||
opencli stackoverflow search "async await" --limit 20
|
||||
|
||||
# Active bounties
|
||||
opencli stackoverflow bounties --limit 10
|
||||
|
||||
# Unanswered questions
|
||||
opencli stackoverflow unanswered --limit 10
|
||||
|
||||
# JSON output
|
||||
opencli stackoverflow hot -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses public Stack Exchange API
|
||||
@@ -0,0 +1,26 @@
|
||||
# Steam
|
||||
|
||||
**Mode**: 🌐 Public · **Domain**: `store.steampowered.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli steam top-sellers` | Top selling games on Steam |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli steam top-sellers
|
||||
|
||||
# Limit results
|
||||
opencli steam top-sellers --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli steam top-sellers -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No login required (public API)
|
||||
@@ -0,0 +1,38 @@
|
||||
# Substack
|
||||
|
||||
**Mode**: 🌐 Public (search) / 🔐 Browser (feed, publication) · **Domain**: `substack.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli substack feed` | Substack 热门文章 Feed |
|
||||
| `opencli substack search` | 搜索 Substack 文章和 Newsletter(无需浏览器) |
|
||||
| `opencli substack publication` | 获取特定 Substack Newsletter 的最新文章 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# 热门 Feed
|
||||
opencli substack feed --limit 10
|
||||
|
||||
# 按分类浏览
|
||||
opencli substack feed --category tech --limit 10
|
||||
|
||||
# 搜索文章(公开 API,无需浏览器)
|
||||
opencli substack search "AI"
|
||||
|
||||
# 搜索 Newsletter
|
||||
opencli substack search "technology" --type publications
|
||||
|
||||
# 查看特定 Newsletter 的最新文章
|
||||
opencli substack publication "https://example.substack.com" --limit 10
|
||||
|
||||
# JSON output
|
||||
opencli substack search "AI" -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `search` command: No login required (public API)
|
||||
- `feed`, `publication` commands: Chrome with `substack.com` accessible, Browser Bridge extension installed
|
||||
@@ -0,0 +1,68 @@
|
||||
# TikTok
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `tiktok.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli tiktok profile` | Get user profile info |
|
||||
| `opencli tiktok search` | Search videos |
|
||||
| `opencli tiktok explore` | Trending videos from explore page |
|
||||
| `opencli tiktok user` | Get recent videos from a user |
|
||||
| `opencli tiktok following` | List accounts you follow |
|
||||
| `opencli tiktok friends` | Friend suggestions |
|
||||
| `opencli tiktok live` | Browse live streams |
|
||||
| `opencli tiktok notifications` | Get notifications |
|
||||
| `opencli tiktok like` | Like a video |
|
||||
| `opencli tiktok unlike` | Unlike a video |
|
||||
| `opencli tiktok save` | Add to Favorites |
|
||||
| `opencli tiktok unsave` | Remove from Favorites |
|
||||
| `opencli tiktok follow` | Follow a user |
|
||||
| `opencli tiktok unfollow` | Unfollow a user |
|
||||
| `opencli tiktok comment` | Comment on a video |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# View a user's profile
|
||||
opencli tiktok profile --username tiktok
|
||||
|
||||
# Search videos
|
||||
opencli tiktok search "cooking" --limit 10
|
||||
|
||||
# Trending explore videos
|
||||
opencli tiktok explore --limit 20
|
||||
|
||||
# Browse live streams
|
||||
opencli tiktok live --limit 10
|
||||
|
||||
# List who you follow
|
||||
opencli tiktok following
|
||||
|
||||
# Friend suggestions
|
||||
opencli tiktok friends --limit 10
|
||||
|
||||
# Like/unlike a video
|
||||
opencli tiktok like --url "https://www.tiktok.com/@user/video/123"
|
||||
opencli tiktok unlike --url "https://www.tiktok.com/@user/video/123"
|
||||
|
||||
# Save/unsave (Favorites)
|
||||
opencli tiktok save --url "https://www.tiktok.com/@user/video/123"
|
||||
opencli tiktok unsave --url "https://www.tiktok.com/@user/video/123"
|
||||
|
||||
# Follow/unfollow
|
||||
opencli tiktok follow --username nasa
|
||||
opencli tiktok unfollow --username nasa
|
||||
|
||||
# Comment on a video
|
||||
opencli tiktok comment --url "https://www.tiktok.com/@user/video/123" --text "Great!"
|
||||
|
||||
# JSON output
|
||||
opencli tiktok profile --username tiktok -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** tiktok.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,50 @@
|
||||
# Twitter / X
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `twitter.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli twitter trending` | |
|
||||
| `opencli twitter bookmarks` | |
|
||||
| `opencli twitter profile` | |
|
||||
| `opencli twitter search` | |
|
||||
| `opencli twitter timeline` | |
|
||||
| `opencli twitter thread` | |
|
||||
| `opencli twitter following` | |
|
||||
| `opencli twitter followers` | |
|
||||
| `opencli twitter notifications` | |
|
||||
| `opencli twitter post` | |
|
||||
| `opencli twitter reply` | |
|
||||
| `opencli twitter delete` | |
|
||||
| `opencli twitter like` | |
|
||||
| `opencli twitter article` | |
|
||||
| `opencli twitter follow` | |
|
||||
| `opencli twitter unfollow` | |
|
||||
| `opencli twitter bookmark` | |
|
||||
| `opencli twitter unbookmark` | |
|
||||
| `opencli twitter block` | |
|
||||
| `opencli twitter unblock` | |
|
||||
| `opencli twitter hide-reply` | |
|
||||
| `opencli twitter download` | |
|
||||
| `opencli twitter accept` | |
|
||||
| `opencli twitter reply-dm` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli twitter trending --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli twitter trending -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli twitter trending -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** twitter.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,53 @@
|
||||
# V2EX
|
||||
|
||||
**Mode**: 🌐 / 🔐 · **Domain**: `v2ex.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli v2ex hot` | Hot topics |
|
||||
| `opencli v2ex latest` | Latest topics |
|
||||
| `opencli v2ex topic <id>` | Topic detail |
|
||||
| `opencli v2ex node <name>` | Topics by node |
|
||||
| `opencli v2ex user <username>` | Topics by user |
|
||||
| `opencli v2ex member <username>` | User profile |
|
||||
| `opencli v2ex replies <id>` | Topic replies |
|
||||
| `opencli v2ex nodes` | All nodes (sorted by topic count) |
|
||||
| `opencli v2ex daily` | Daily hot |
|
||||
| `opencli v2ex me` | My profile (auth required) |
|
||||
| `opencli v2ex notifications` | My notifications (auth required) |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Hot topics
|
||||
opencli v2ex hot --limit 5
|
||||
|
||||
# Browse topics in a node
|
||||
opencli v2ex node python
|
||||
|
||||
# View topic replies
|
||||
opencli v2ex replies 1000
|
||||
|
||||
# User's topics
|
||||
opencli v2ex user Livid
|
||||
|
||||
# User profile
|
||||
opencli v2ex member Livid
|
||||
|
||||
# List all nodes
|
||||
opencli v2ex nodes --limit 10
|
||||
|
||||
# JSON output
|
||||
opencli v2ex hot -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Most commands (`hot`, `latest`, `topic`, `node`, `user`, `member`, `replies`, `nodes`) use the public V2EX API and **require no browser or login**.
|
||||
|
||||
For `daily`, `me`, and `notifications`:
|
||||
|
||||
- Chrome running and **logged into** v2ex.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,31 @@
|
||||
# Weibo (微博)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `weibo.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli weibo hot` | |
|
||||
| `opencli weibo search` | Search Weibo posts by keyword |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli weibo hot --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli weibo hot -f json
|
||||
|
||||
# Search
|
||||
opencli weibo search "OpenAI" --limit 5
|
||||
|
||||
# Verbose mode
|
||||
opencli weibo hot -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** weibo.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,33 @@
|
||||
# WeChat (微信公众号)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `mp.weixin.qq.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli weixin download` | 下载微信公众号文章为 Markdown 格式 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Export article to Markdown
|
||||
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --output ./weixin
|
||||
|
||||
# Export with locally downloaded images
|
||||
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --download-images
|
||||
|
||||
# Export without images
|
||||
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --no-download-images
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Downloads to `<output>/<article-title>/`:
|
||||
- `<article-title>.md` — Markdown with frontmatter (title, author, publish time, source URL)
|
||||
- `images/` — Downloaded images (if `--download-images` is enabled, default: true)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** mp.weixin.qq.com (for articles behind login wall)
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,48 @@
|
||||
# 微信读书 (WeRead)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `weread.qq.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli weread shelf` | List books on your bookshelf |
|
||||
| `opencli weread search` | Search books on WeRead |
|
||||
| `opencli weread book` | View book details |
|
||||
| `opencli weread ranking` | Book rankings by category |
|
||||
| `opencli weread notebooks` | List books that have highlights or notes |
|
||||
| `opencli weread highlights` | List your highlights (underlines) in a book |
|
||||
| `opencli weread notes` | List your notes (thoughts) on a book |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# View your bookshelf
|
||||
opencli weread shelf --limit 20
|
||||
|
||||
# Search books
|
||||
opencli weread search "三体"
|
||||
|
||||
# View book details
|
||||
opencli weread book <book-id>
|
||||
|
||||
# Book rankings
|
||||
opencli weread ranking --limit 10
|
||||
|
||||
# List books with notes/highlights
|
||||
opencli weread notebooks
|
||||
|
||||
# View highlights for a book
|
||||
opencli weread highlights <book-id>
|
||||
|
||||
# View your notes
|
||||
opencli weread notes <book-id>
|
||||
|
||||
# JSON output
|
||||
opencli weread shelf -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** weread.qq.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,39 @@
|
||||
# Wikipedia
|
||||
|
||||
**Mode**: 🌐 Public · **Domain**: `wikipedia.org`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli wikipedia search` | Search Wikipedia articles |
|
||||
| `opencli wikipedia summary` | Get Wikipedia article summary |
|
||||
| `opencli wikipedia random` | Get a random Wikipedia article |
|
||||
| `opencli wikipedia trending` | Most-read articles (yesterday) |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Search articles
|
||||
opencli wikipedia search "quantum computing" --limit 10
|
||||
|
||||
# Get article summary
|
||||
opencli wikipedia summary "Artificial intelligence"
|
||||
|
||||
# Get a random article
|
||||
opencli wikipedia random
|
||||
|
||||
# Most-read articles (yesterday)
|
||||
opencli wikipedia trending --limit 5
|
||||
|
||||
# Use with other languages
|
||||
opencli wikipedia search "人工智能" --lang zh
|
||||
opencli wikipedia random --lang ja
|
||||
|
||||
# JSON output
|
||||
opencli wikipedia search "Rust" -f json
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses public Wikipedia API
|
||||
@@ -0,0 +1,38 @@
|
||||
# Xiaohongshu (小红书)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `xiaohongshu.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli xiaohongshu search` | Search notes by keyword (returns title, author, likes, URL) |
|
||||
| `opencli xiaohongshu notifications` | |
|
||||
| `opencli xiaohongshu feed` | |
|
||||
| `opencli xiaohongshu user` | |
|
||||
| `opencli xiaohongshu download` | |
|
||||
| `opencli xiaohongshu creator-notes` | |
|
||||
| `opencli xiaohongshu creator-note-detail` | |
|
||||
| `opencli xiaohongshu creator-notes-summary` | |
|
||||
| `opencli xiaohongshu creator-profile` | |
|
||||
| `opencli xiaohongshu creator-stats` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Search for notes
|
||||
opencli xiaohongshu search 美食 --limit 10
|
||||
|
||||
# JSON output
|
||||
opencli xiaohongshu search 旅行 -f json
|
||||
|
||||
# Other commands
|
||||
opencli xiaohongshu feed
|
||||
opencli xiaohongshu notifications
|
||||
opencli xiaohongshu download <url>
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** xiaohongshu.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,28 @@
|
||||
# Xiaoyuzhou (小宇宙)
|
||||
|
||||
**Mode**: 🌐 Public · **Domain**: `xiaoyuzhou.fm`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli xiaoyuzhou podcast` | |
|
||||
| `opencli xiaoyuzhou podcast-episodes` | |
|
||||
| `opencli xiaoyuzhou episode` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli xiaoyuzhou podcast --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli xiaoyuzhou podcast -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli xiaoyuzhou podcast -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- No browser required — uses public API
|
||||
@@ -0,0 +1,42 @@
|
||||
# Xueqiu (雪球)
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `xueqiu.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli xueqiu feed` | |
|
||||
| `opencli xueqiu earnings-date` | |
|
||||
| `opencli xueqiu hot-stock` | |
|
||||
| `opencli xueqiu hot` | |
|
||||
| `opencli xueqiu search` | |
|
||||
| `opencli xueqiu stock` | |
|
||||
| `opencli xueqiu watchlist` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli xueqiu feed --limit 5
|
||||
|
||||
# Search stocks
|
||||
opencli xueqiu search 茅台
|
||||
|
||||
# View one stock
|
||||
opencli xueqiu stock SH600519
|
||||
|
||||
# Upcoming earnings dates
|
||||
opencli xueqiu earnings-date SH600519 --next
|
||||
|
||||
# JSON output
|
||||
opencli xueqiu feed -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli xueqiu feed -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** xueqiu.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,27 @@
|
||||
# Yahoo Finance
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `finance.yahoo.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli yahoo-finance quote` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli yahoo-finance quote AAPL
|
||||
|
||||
# JSON output
|
||||
opencli yahoo-finance quote TSLA -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli yahoo-finance quote NVDA -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and able to open `finance.yahoo.com`
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,69 @@
|
||||
# Yollomi
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `yollomi.com`
|
||||
|
||||
AI image/video generation and editing on [yollomi.com](https://yollomi.com). Uses the same `/api/ai/*` routes as the web app; authentication is your **logged-in Chrome session** (NextAuth cookies).
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli yollomi generate` | Text-to-image / image-to-image |
|
||||
| `opencli yollomi video` | Text-to-video / image-to-video |
|
||||
| `opencli yollomi edit` | Qwen image edit (prompt + image) |
|
||||
| `opencli yollomi upload` | Upload a local file → public URL for other commands |
|
||||
| `opencli yollomi models` | List image / video / tool models and credit costs |
|
||||
| `opencli yollomi remove-bg` | Remove background (free) |
|
||||
| `opencli yollomi upscale` | Image upscaling |
|
||||
| `opencli yollomi face-swap` | Face swap between two images |
|
||||
| `opencli yollomi restore` | Photo restoration |
|
||||
| `opencli yollomi try-on` | Virtual try-on |
|
||||
| `opencli yollomi background` | AI background for product/object images |
|
||||
| `opencli yollomi object-remover` | Remove objects (image + mask URLs) |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# List models
|
||||
opencli yollomi models --type image
|
||||
|
||||
# Text-to-image (default model: z-image-turbo)
|
||||
opencli yollomi generate "a red apple on a wooden table"
|
||||
|
||||
# Choose model and aspect ratio
|
||||
opencli yollomi generate "sunset" --model flux-schnell --ratio 16:9
|
||||
|
||||
# Image-to-image: upload first, then pass URL
|
||||
opencli yollomi upload ./photo.png
|
||||
opencli yollomi generate "oil painting style" --model flux-2-pro --image "https://..."
|
||||
|
||||
# Video
|
||||
opencli yollomi video "waves on a beach" --model kling-2-1
|
||||
|
||||
# Tools
|
||||
opencli yollomi remove-bg https://example.com/image.png
|
||||
opencli yollomi upscale https://example.com/image.png --scale 4
|
||||
opencli yollomi edit https://example.com/in.png "make it vintage"
|
||||
```
|
||||
|
||||
### Common options
|
||||
|
||||
| Option | Applies to | Description |
|
||||
|--------|------------|-------------|
|
||||
| `--model` | `generate`, `video` | Model id (see `yollomi models`) |
|
||||
| `--ratio` | `generate`, `video` | Aspect ratio, e.g. `1:1`, `16:9` |
|
||||
| `--image` | `generate`, `video` | Image URL for img2img / i2v |
|
||||
| `--output` | Most | Output directory (default `./yollomi-output`) |
|
||||
| `--no-download` | Several | Print URLs only, skip saving files |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** [yollomi.com](https://yollomi.com) (Google OAuth)
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed; daemon connects on first command
|
||||
|
||||
The CLI ensures the automation tab is on `yollomi.com` before calling APIs (same-origin `fetch` with session cookies).
|
||||
|
||||
## Notes
|
||||
|
||||
- **Credits**: Each model consumes account credits; insufficient credits returns HTTP 402.
|
||||
- **Upload**: Local paths for tools are not accepted directly — use `yollomi upload` to get a URL, or pass an existing HTTPS image URL.
|
||||
@@ -0,0 +1,29 @@
|
||||
# YouTube
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `youtube.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli youtube search` | |
|
||||
| `opencli youtube video` | |
|
||||
| `opencli youtube transcript` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli youtube search --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli youtube search -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli youtube search -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** youtube.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,30 @@
|
||||
# Zhihu
|
||||
|
||||
**Mode**: 🔐 Browser · **Domain**: `zhihu.com`
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli zhihu hot` | |
|
||||
| `opencli zhihu search` | |
|
||||
| `opencli zhihu question` | |
|
||||
| `opencli zhihu download` | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Quick start
|
||||
opencli zhihu hot --limit 5
|
||||
|
||||
# JSON output
|
||||
opencli zhihu hot -f json
|
||||
|
||||
# Verbose mode
|
||||
opencli zhihu hot -v
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Chrome running and **logged into** zhihu.com
|
||||
- [Browser Bridge extension](/guide/browser-bridge) installed
|
||||
@@ -0,0 +1,52 @@
|
||||
# Antigravity
|
||||
|
||||
🔥 **CLI All Electron Apps! The Most Powerful Update Has Arrived!** 🔥
|
||||
|
||||
Turn your local Antigravity desktop application into a programmable AI node via Chrome DevTools Protocol (CDP). This allows you to compose complex LLM workflows entirely through the terminal by manipulating the actual UI natively, bypassing any API restrictions.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Start the Antigravity desktop app with the Chrome DevTools `remote-debugging-port` flag:
|
||||
|
||||
```bash
|
||||
# Start Antigravity in the background
|
||||
/Applications/Antigravity.app/Contents/MacOS/Electron \
|
||||
--remote-debugging-port=9224
|
||||
```
|
||||
|
||||
> Depending on your installation, the executable might be named differently, e.g., `Antigravity` instead of `Electron`.
|
||||
|
||||
Then set the target port:
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9224"
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### `opencli antigravity status`
|
||||
Check the Chromium CDP connection. Returns the current window title and active internal URL.
|
||||
|
||||
### `opencli antigravity send <message>`
|
||||
Send a text prompt to the AI. Automatically locates the Lexical editor input box, types the prompt securely, and hits Enter.
|
||||
|
||||
### `opencli antigravity read`
|
||||
Scrape the entire current conversation history block as pure text.
|
||||
|
||||
### `opencli antigravity new`
|
||||
Click the "New Conversation" button to instantly clear the UI state and start fresh.
|
||||
|
||||
### `opencli antigravity dump`
|
||||
Dump the current DOM and snapshot artifacts to `/tmp` for reverse-engineering and selector debugging.
|
||||
|
||||
### `opencli antigravity extract-code`
|
||||
Extract any multi-line code blocks from the current conversation view. Ideal for automated script extraction (e.g. `opencli antigravity extract-code > script.sh`).
|
||||
|
||||
### `opencli antigravity model <name>`
|
||||
Quickly target and switch the active LLM engine. Example: `opencli antigravity model claude` or `opencli antigravity model gemini`.
|
||||
|
||||
### `opencli antigravity watch`
|
||||
A long-running, streaming process that continuously polls the Antigravity UI for chat updates and outputs them in real-time to standard output.
|
||||
|
||||
### `opencli antigravity serve --port 8082`
|
||||
Start an Anthropic-compatible `/v1/messages` proxy backed by the local Antigravity app. Useful when you want external tools to talk to Antigravity through an API-shaped interface.
|
||||
@@ -0,0 +1,44 @@
|
||||
# ChatGPT
|
||||
|
||||
Control the **ChatGPT macOS Desktop App** directly from the terminal. OpenCLI supports two automation approaches for ChatGPT.
|
||||
|
||||
## Approach 1: AppleScript (Default, No Setup)
|
||||
|
||||
The current built-in commands use native AppleScript automation — no extra launch flags needed.
|
||||
|
||||
### Prerequisites
|
||||
1. Install the official [ChatGPT Desktop App](https://openai.com/chatgpt/mac/) from OpenAI.
|
||||
2. Grant **Accessibility permissions** to your terminal app in **System Settings → Privacy & Security → Accessibility**.
|
||||
|
||||
### Commands
|
||||
- `opencli chatgpt status`: Check if the ChatGPT app is currently running.
|
||||
- `opencli chatgpt new`: Activate ChatGPT and press `Cmd+N` to start a new conversation.
|
||||
- `opencli chatgpt send "message"`: Copy your message to clipboard, activate ChatGPT, paste, and submit.
|
||||
- `opencli chatgpt read`: Read the last visible message from the focused ChatGPT window via the Accessibility tree.
|
||||
- `opencli chatgpt ask "message"`: Send a prompt and wait for the visible reply in one shot.
|
||||
|
||||
## Approach 2: CDP (Advanced, Electron Debug Mode)
|
||||
|
||||
ChatGPT Desktop is also an Electron app and can be launched with a remote debugging port:
|
||||
|
||||
```bash
|
||||
/Applications/ChatGPT.app/Contents/MacOS/ChatGPT \
|
||||
--remote-debugging-port=9224
|
||||
```
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9224"
|
||||
```
|
||||
|
||||
> The CDP approach is primarily for advanced automation and future desktop-only commands. The built-in command set above still works in the default AppleScript path unless you explicitly route through `OPENCLI_CDP_ENDPOINT`.
|
||||
|
||||
## How It Works
|
||||
|
||||
- **AppleScript mode**: Uses `osascript` to control ChatGPT, `pbcopy`/`pbpaste` to paste prompts, and the macOS Accessibility tree to read visible chat messages.
|
||||
- **CDP mode**: Connects via Chrome DevTools Protocol to the Electron renderer process.
|
||||
|
||||
## Limitations
|
||||
|
||||
- macOS only (AppleScript dependency)
|
||||
- AppleScript mode requires Accessibility permissions
|
||||
- `read` returns the last visible message in the focused ChatGPT window — scroll first if the message you want is not visible
|
||||
@@ -0,0 +1,38 @@
|
||||
# ChatWise
|
||||
|
||||
Control the **ChatWise Desktop App** from the terminal via Chrome DevTools Protocol (CDP). ChatWise is an Electron-based multi-LLM client supporting GPT-4, Claude, Gemini, and more.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Install [ChatWise](https://chatwise.app/).
|
||||
2. Launch with remote debugging port:
|
||||
```bash
|
||||
/Applications/ChatWise.app/Contents/MacOS/ChatWise \
|
||||
--remote-debugging-port=9228
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9228"
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### Diagnostics
|
||||
- `opencli chatwise status`: Check CDP connection status.
|
||||
- `opencli chatwise screenshot`: Export DOM + accessibility snapshot.
|
||||
|
||||
### Chat
|
||||
- `opencli chatwise new`: Start a new conversation (`Cmd+N`).
|
||||
- `opencli chatwise send "message"`: Send a message to the active chat.
|
||||
- `opencli chatwise read`: Read the current conversation.
|
||||
- `opencli chatwise ask "prompt"`: Send + wait for response + return it (one-shot).
|
||||
|
||||
### AI Features
|
||||
- `opencli chatwise model`: Get the current AI model.
|
||||
- `opencli chatwise model gpt-4`: Switch to a different model.
|
||||
|
||||
### Organization
|
||||
- `opencli chatwise history`: List conversations from the sidebar.
|
||||
- `opencli chatwise export`: Export conversation as Markdown.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Codex
|
||||
|
||||
Control the **OpenAI Codex Desktop App** headless or headfully via Chrome DevTools Protocol (CDP). Because Codex is built on Electron, OpenCLI can directly drive its internal UI, automate slash commands, and manipulate its AI agent threads.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. You must have the official OpenAI Codex app installed.
|
||||
2. Launch it via the terminal and expose the remote debugging port:
|
||||
```bash
|
||||
# macOS
|
||||
/Applications/Codex.app/Contents/MacOS/Codex --remote-debugging-port=9222
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9222"
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### Diagnostics
|
||||
- `opencli codex status`: Checks connection and reads the current active window URL/title.
|
||||
- `opencli codex dump`: Dumps the full UI DOM and Accessibility tree into `/tmp`.
|
||||
- `opencli codex screenshot`: Captures DOM + snapshot artifacts of the current window.
|
||||
|
||||
### Agent Manipulation
|
||||
- `opencli codex new`: Simulates `Cmd+N` to start a completely fresh and isolated Git Worktree thread context.
|
||||
- `opencli codex send "message"`: Robustly finds the active Thread Composer and injects your text.
|
||||
- *Pro-tip*: You can trigger internal shortcuts, e.g., `opencli codex send "/review"`.
|
||||
- `opencli codex ask "message"`: Send + wait + read in one shot.
|
||||
- `opencli codex read`: Extracts the entire current thread history and AI reasoning logs.
|
||||
- `opencli codex extract-diff`: Automatically scrapes any visual Patch chunks and Code Diffs.
|
||||
- `opencli codex model`: Get the currently active AI model.
|
||||
- `opencli codex history`: List recent conversation threads from the sidebar.
|
||||
- `opencli codex export`: Export the current conversation as Markdown.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Cursor
|
||||
|
||||
Control the **Cursor IDE** from the terminal via Chrome DevTools Protocol (CDP). Since Cursor is built on Electron (VS Code fork), OpenCLI can drive its internal UI, automate Composer interactions, and manipulate chat sessions.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Install [Cursor](https://cursor.sh/).
|
||||
2. Launch it with the remote debugging port:
|
||||
```bash
|
||||
/Applications/Cursor.app/Contents/MacOS/Cursor --remote-debugging-port=9226
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9226"
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### Diagnostics
|
||||
- `opencli cursor status`: Check CDP connection status.
|
||||
- `opencli cursor dump`: Dump the full DOM and Accessibility snapshot to `/tmp/cursor-dom.html` and `/tmp/cursor-snapshot.json`.
|
||||
- `opencli cursor screenshot`: Capture DOM + snapshot artifacts of the current window.
|
||||
|
||||
### Chat Manipulation
|
||||
- `opencli cursor new`: Press `Cmd+N` to start a new file/tab.
|
||||
- `opencli cursor send "message"`: Inject text into the active Composer/Chat input and submit.
|
||||
- `opencli cursor ask "message"`: Send + wait + read in one shot.
|
||||
- `opencli cursor read`: Extract the full conversation history from the active chat panel.
|
||||
|
||||
### AI Features
|
||||
- `opencli cursor composer "prompt"`: Open the Composer panel (`Cmd+I`) and send a prompt for inline AI editing.
|
||||
- `opencli cursor model`: Get the currently active AI model (e.g., `claude-4.5-sonnet`).
|
||||
- `opencli cursor extract-code`: Extract all code blocks from the current conversation.
|
||||
- `opencli cursor history`: List recent chat/composer sessions from the sidebar.
|
||||
- `opencli cursor export`: Export the current conversation as Markdown.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Discord
|
||||
|
||||
Control the **Discord Desktop App** from the terminal via Chrome DevTools Protocol (CDP).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Launch with remote debugging port:
|
||||
```bash
|
||||
/Applications/Discord.app/Contents/MacOS/Discord --remote-debugging-port=9232
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9232"
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli discord-app status` | Check CDP connection |
|
||||
| `opencli discord-app send "message"` | Send a message in the active channel |
|
||||
| `opencli discord-app read` | Read recent messages |
|
||||
| `opencli discord-app channels` | List channels in the current server |
|
||||
| `opencli discord-app servers` | List all joined servers |
|
||||
| `opencli discord-app search "query"` | Search messages (Cmd+F) |
|
||||
| `opencli discord-app members` | List online members |
|
||||
@@ -0,0 +1,41 @@
|
||||
# Doubao App
|
||||
|
||||
Drive the **Doubao (豆包) AI desktop app** via Chrome DevTools Protocol. This adapter controls the Electron-based Doubao client directly.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Install the Doubao desktop app from [doubao.com](https://www.doubao.com/).
|
||||
2. Launch with remote debugging enabled:
|
||||
|
||||
```bash
|
||||
/Applications/Doubao.app/Contents/MacOS/Doubao \
|
||||
--remote-debugging-port=9226
|
||||
```
|
||||
|
||||
3. Set the CDP endpoint:
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9226"
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli doubao-app status` | Check if the Doubao app is running and reachable |
|
||||
| `opencli doubao-app new` | Start a new conversation |
|
||||
| `opencli doubao-app send "message"` | Send a message to the active chat |
|
||||
| `opencli doubao-app read` | Read all messages in the current conversation |
|
||||
| `opencli doubao-app ask "message"` | Send a prompt and wait for the reply |
|
||||
| `opencli doubao-app screenshot` | Capture a screenshot of the app window |
|
||||
| `opencli doubao-app dump` | Dump the current page DOM snapshot |
|
||||
|
||||
## How It Works
|
||||
|
||||
The adapter connects to Doubao's Electron renderer via CDP and uses `data-testid` selectors to interact with the chat UI. Text injection uses React's internal value setter for reliable textarea updates.
|
||||
|
||||
## Limitations
|
||||
|
||||
- macOS only (Electron app path)
|
||||
- Requires Doubao to be launched with `--remote-debugging-port`
|
||||
- `read` returns messages visible in the current conversation only
|
||||
@@ -0,0 +1,29 @@
|
||||
# Notion
|
||||
|
||||
Control the **Notion Desktop App** from the terminal via Chrome DevTools Protocol (CDP).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Launch with remote debugging port:
|
||||
```bash
|
||||
/Applications/Notion.app/Contents/MacOS/Notion --remote-debugging-port=9230
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9230"
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `opencli notion status` | Check CDP connection |
|
||||
| `opencli notion search "query"` | Quick Find search (Cmd+P) |
|
||||
| `opencli notion read` | Read the current page content |
|
||||
| `opencli notion new "title"` | Create a new page (Cmd+N) |
|
||||
| `opencli notion write "text"` | Append text to the current page |
|
||||
| `opencli notion sidebar` | List pages from the sidebar |
|
||||
| `opencli notion favorites` | List pages from the Favorites section |
|
||||
| `opencli notion export` | Export page as Markdown |
|
||||
@@ -0,0 +1,70 @@
|
||||
# All Adapters
|
||||
|
||||
Run `opencli list` for the live registry.
|
||||
|
||||
## Browser Adapters
|
||||
|
||||
| Site | Commands | Mode |
|
||||
|------|----------|------|
|
||||
| **[twitter](/adapters/browser/twitter)** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` | 🔐 Browser |
|
||||
| **[reddit](/adapters/browser/reddit)** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 Browser |
|
||||
| **[bilibili](/adapters/browser/bilibili)** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 Browser |
|
||||
| **[zhihu](/adapters/browser/zhihu)** | `hot` `search` `question` `download` | 🔐 Browser |
|
||||
| **[xiaohongshu](/adapters/browser/xiaohongshu)** | `search` `notifications` `feed` `me` `user` `download` `publish` | 🔐 Browser |
|
||||
| **[xueqiu](/adapters/browser/xueqiu)** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 Browser |
|
||||
| **[youtube](/adapters/browser/youtube)** | `search` `video` `transcript` | 🔐 Browser |
|
||||
| **[v2ex](/adapters/browser/v2ex)** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | 🌐 / 🔐 |
|
||||
| **[bloomberg](/adapters/browser/bloomberg)** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | 🌐 / 🔐 |
|
||||
| **[weibo](/adapters/browser/weibo)** | `hot` `search` | 🔐 Browser |
|
||||
| **[linkedin](/adapters/browser/linkedin)** | `search` | 🔐 Browser |
|
||||
| **[coupang](/adapters/browser/coupang)** | `search` `add-to-cart` | 🔐 Browser |
|
||||
| **[boss](/adapters/browser/boss)** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | 🔐 Browser |
|
||||
| **[ctrip](/adapters/browser/ctrip)** | `search` | 🔐 Browser |
|
||||
| **[reuters](/adapters/browser/reuters)** | `search` | 🔐 Browser |
|
||||
| **[smzdm](/adapters/browser/smzdm)** | `search` | 🔐 Browser |
|
||||
| **[jike](/adapters/browser/jike)** | `feed` `search` `post` `topic` `user` `create` `comment` `like` `repost` `notifications` | 🔐 Browser |
|
||||
| **[jimeng](/adapters/browser/jimeng)** | `generate` `history` | 🔐 Browser |
|
||||
| **[yollomi](/adapters/browser/yollomi)** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | 🔐 Browser |
|
||||
| **[linux-do](/adapters/browser/linux-do)** | `hot` `latest` `categories` `category` `search` `topic` | 🔐 Browser |
|
||||
| **[chaoxing](/adapters/browser/chaoxing)** | `assignments` `exams` | 🔐 Browser |
|
||||
| **[grok](/adapters/browser/grok)** | `ask` | 🔐 Browser |
|
||||
| **[doubao](/adapters/browser/doubao)** | `status` `new` `send` `read` `ask` | 🔐 Browser |
|
||||
| **[weread](/adapters/browser/weread)** | `shelf` `search` `book` `ranking` `notebooks` `highlights` `notes` | 🔐 Browser |
|
||||
| **[douban](/adapters/browser/douban)** | `search` `top250` `subject` `marks` `reviews` | 🔐 Browser |
|
||||
| **[facebook](/adapters/browser/facebook)** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 🔐 Browser |
|
||||
| **[instagram](/adapters/browser/instagram)** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 🔐 Browser |
|
||||
| **[medium](/adapters/browser/medium)** | `feed` `search` `user` `shared` | 🔐 Browser |
|
||||
| **[sinablog](/adapters/browser/sinablog)** | `hot` `search` `article` `user` `shared` | 🔐 Browser |
|
||||
| **[substack](/adapters/browser/substack)** | `feed` `search` `publication` `shared` | 🔐 Browser |
|
||||
| **[tiktok](/adapters/browser/tiktok)** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | 🔐 Browser |
|
||||
|
||||
## Public API Adapters
|
||||
|
||||
| Site | Commands | Mode |
|
||||
|------|----------|------|
|
||||
| **[hackernews](/adapters/browser/hackernews)** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 🌐 Public |
|
||||
| **[bbc](/adapters/browser/bbc)** | `news` | 🌐 Public |
|
||||
| **[devto](/adapters/browser/devto)** | `top` `tag` `user` | 🌐 Public |
|
||||
| **[apple-podcasts](/adapters/browser/apple-podcasts)** | `search` `episodes` `top` | 🌐 Public |
|
||||
| **[xiaoyuzhou](/adapters/browser/xiaoyuzhou)** | `podcast` `podcast-episodes` `episode` | 🌐 Public |
|
||||
| **[yahoo-finance](/adapters/browser/yahoo-finance)** | `quote` | 🌐 Public |
|
||||
| **[arxiv](/adapters/browser/arxiv)** | `search` `paper` | 🌐 Public |
|
||||
| **[barchart](/adapters/browser/barchart)** | `quote` `options` `greeks` `flow` | 🌐 Public |
|
||||
| **[hf](/adapters/browser/hf)** | `top` | 🌐 Public |
|
||||
| **[sinafinance](/adapters/browser/sinafinance)** | `news` | 🌐 Public |
|
||||
| **[stackoverflow](/adapters/browser/stackoverflow)** | `hot` `search` `bounties` `unanswered` | 🌐 Public |
|
||||
| **[wikipedia](/adapters/browser/wikipedia)** | `search` `summary` | 🌐 Public |
|
||||
| **[lobsters](/adapters/browser/lobsters)** | `hot` `newest` `active` `tag` | 🌐 Public |
|
||||
|
||||
## Desktop Adapters
|
||||
|
||||
| App | Description | Commands |
|
||||
|-----|-------------|----------|
|
||||
| **[Cursor](/adapters/desktop/cursor)** | Control Cursor IDE | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` |
|
||||
| **[Codex](/adapters/desktop/codex)** | Drive OpenAI Codex CLI agent | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` |
|
||||
| **[Antigravity](/adapters/desktop/antigravity)** | Control Antigravity Ultra | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` |
|
||||
| **[ChatGPT](/adapters/desktop/chatgpt)** | Automate ChatGPT macOS app | `status` `new` `send` `read` `ask` |
|
||||
| **[ChatWise](/adapters/desktop/chatwise)** | Multi-LLM client | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` |
|
||||
| **[Notion](/adapters/desktop/notion)** | Search, read, write pages | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` |
|
||||
| **[Discord](/adapters/desktop/discord)** | Desktop messages & channels | `status` `send` `read` `channels` `servers` `search` `members` |
|
||||
| **[Doubao App](/adapters/desktop/doubao-app)** | Doubao AI desktop app via CDP | `status` `new` `send` `read` `ask` `screenshot` `dump` |
|
||||
@@ -0,0 +1,103 @@
|
||||
# Connecting OpenCLI via CDP (Remote/Headless Servers)
|
||||
|
||||
If you cannot use the opencli Browser Bridge extension (e.g., in a remote headless server environment without a UI), OpenCLI provides an alternative: connecting directly to Chrome via **CDP (Chrome DevTools Protocol)**.
|
||||
|
||||
Because CDP binds to `localhost` by default for security reasons, accessing it from a remote server requires an additional networking tunnel.
|
||||
|
||||
This guide is broken down into three phases:
|
||||
1. **Preparation**: Start Chrome with CDP enabled locally.
|
||||
2. **Network Tunnels**: Expose that CDP port to your remote server using either **SSH Tunnels** or **Reverse Proxies**.
|
||||
3. **Execution**: Run OpenCLI on your server.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Preparation (Local Machine)
|
||||
|
||||
First, you need to start a Chrome browser on your local machine with remote debugging enabled.
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir="$HOME/chrome-debug-profile" \
|
||||
--remote-allow-origins="*"
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
google-chrome \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir="$HOME/chrome-debug-profile" \
|
||||
--remote-allow-origins="*"
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```cmd
|
||||
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
|
||||
--remote-debugging-port=9222 ^
|
||||
--user-data-dir="%USERPROFILE%\chrome-debug-profile" ^
|
||||
--remote-allow-origins="*"
|
||||
```
|
||||
|
||||
> **Note**: The `--remote-allow-origins="*"` flag is often required for modern Chrome versions to accept cross-origin CDP WebSocket connections (e.g. from reverse proxies like ngrok).
|
||||
|
||||
Once this browser instance opens, **log into the target websites you want to use** (e.g., bilibili.com, zhihu.com) so that the session contains the correct cookies.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Remote Access Methods
|
||||
|
||||
Once CDP is running locally on port `9222`, you must securely expose this port to your remote server. Choose one of the two methods below depending on your network conditions.
|
||||
|
||||
### Method A: SSH Tunnel (Recommended)
|
||||
|
||||
If your local machine has SSH access to the remote server, this is the most secure and straightforward method.
|
||||
|
||||
Run this command on your **Local Machine** to forward the remote server's port `9222` back to your local port `9222`:
|
||||
|
||||
```bash
|
||||
ssh -R 9222:localhost:9222 your-server-user@your-server-ip
|
||||
```
|
||||
|
||||
Leave this SSH session running in the background.
|
||||
|
||||
### Method B: Reverse Proxy (ngrok / frp / socat)
|
||||
|
||||
If you cannot establish a direct SSH connection (e.g., due to NAT or firewalls), you can use an intranet penetration tool like `ngrok`.
|
||||
|
||||
Run this command on your **Local Machine** to expose your local port `9222` to the public internet securely via ngrok:
|
||||
|
||||
```bash
|
||||
ngrok http 9222
|
||||
```
|
||||
|
||||
This will print a forwarding URL, such as `https://abcdef.ngrok.app`. **Copy this URL**.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Execution (Remote Server)
|
||||
|
||||
Now switch to your **Remote Server** where OpenCLI is installed.
|
||||
|
||||
Depending on the network tunnel method you chose in Phase 2, set the `OPENCLI_CDP_ENDPOINT` environment variable and run your commands.
|
||||
|
||||
### If you used Method A (SSH Tunnel):
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://localhost:9222"
|
||||
opencli doctor # Verify connection
|
||||
opencli bilibili hot --limit 5 # Test a command
|
||||
```
|
||||
|
||||
### If you used Method B (Reverse Proxy like ngrok):
|
||||
|
||||
```bash
|
||||
# Use the URL you copied from ngrok earlier
|
||||
export OPENCLI_CDP_ENDPOINT="https://abcdef.ngrok.app"
|
||||
opencli doctor # Verify connection
|
||||
opencli bilibili hot --limit 5 # Test a command
|
||||
```
|
||||
|
||||
> *Tip: If you provide a standard HTTP/HTTPS CDP endpoint, OpenCLI requests the `/json` target list and picks the most likely inspectable app/page target automatically. If multiple app targets exist, you can further narrow selection with `OPENCLI_CDP_TARGET` (for example `antigravity` or `codex`).*
|
||||
|
||||
If you plan to use this setup frequently, you can persist the environment variable by adding the `export` line to your `~/.bashrc` or `~/.zshrc` on the server.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Download Support
|
||||
|
||||
OpenCLI supports downloading images, videos, and articles from supported platforms.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
| Platform | Content Types | Notes |
|
||||
|----------|---------------|-------|
|
||||
| **xiaohongshu** | Images, Videos | Downloads all media from a note |
|
||||
| **bilibili** | Videos | Requires `yt-dlp` installed |
|
||||
| **twitter** | Images, Videos | Downloads from user media tab or single tweet |
|
||||
| **zhihu** | Articles (Markdown) | Exports articles with optional image download |
|
||||
| **weixin** | Articles (Markdown) | Exports WeChat Official Account articles |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
For video downloads from streaming platforms, install `yt-dlp`:
|
||||
|
||||
```bash
|
||||
# Install yt-dlp
|
||||
pip install yt-dlp
|
||||
# or
|
||||
brew install yt-dlp
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Download images/videos from Xiaohongshu note
|
||||
opencli xiaohongshu download --note-id abc123 --output ./xhs
|
||||
|
||||
# Download Bilibili video (requires yt-dlp)
|
||||
opencli bilibili download --bvid BV1xxx --output ./bilibili
|
||||
opencli bilibili download --bvid BV1xxx --quality 1080p
|
||||
|
||||
# Download Twitter media from user
|
||||
opencli twitter download elonmusk --limit 20 --output ./twitter
|
||||
|
||||
# Download single tweet media
|
||||
opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
|
||||
|
||||
# Export Zhihu article to Markdown
|
||||
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
|
||||
|
||||
# Export with local images
|
||||
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --download-images
|
||||
|
||||
# Export WeChat article to Markdown
|
||||
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --output ./weixin
|
||||
```
|
||||
|
||||
## Pipeline Step (YAML Adapters)
|
||||
|
||||
The `download` step can be used in YAML pipelines:
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
pipeline:
|
||||
- fetch: https://api.example.com/media
|
||||
- download:
|
||||
url: ${{ item.imageUrl }}
|
||||
dir: ./downloads
|
||||
filename: ${{ item.title | sanitize }}.jpg
|
||||
concurrency: 5
|
||||
skip_existing: true
|
||||
```
|
||||
:::
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
description: How to CLI-ify and automate any Electron Desktop Application via CDP
|
||||
---
|
||||
|
||||
# CLI-ifying Electron Applications (Skill Guide)
|
||||
|
||||
Based on the successful automation of **Cursor**, **Codex**, **Antigravity**, **ChatWise**, **Notion**, and **Discord** desktop apps, this guide serves as the standard operating procedure (SOP) for adapting ANY Electron-based application into an OpenCLI adapter.
|
||||
|
||||
## Core Concept
|
||||
|
||||
Electron apps are essentially local Chromium browser instances. By exposing a debugging port (CDP — Chrome DevTools Protocol) at launch time, we can use the Browser Bridge to pierce through the UI layer, accessing and controlling all underlying state including React/Vue components and Shadow DOM.
|
||||
|
||||
> **Note:** Not all desktop apps are Electron. WeChat (native Cocoa) and Feishu/Lark (custom Lark Framework) embed Chromium but do NOT expose CDP. For those apps, use the AppleScript + clipboard approach instead (see [Non-Electron Pattern](#non-electron-pattern-applescript)).
|
||||
|
||||
### Launching the Target App
|
||||
```bash
|
||||
/Applications/AppName.app/Contents/MacOS/AppName --remote-debugging-port=9222
|
||||
```
|
||||
|
||||
### Verifying Electron
|
||||
```bash
|
||||
# Check for Electron Framework in the app bundle
|
||||
ls /Applications/AppName.app/Contents/Frameworks/Electron\ Framework.framework
|
||||
# If this directory exists → Electron → CDP works
|
||||
# If not → check for libEGL.dylib (embedded Chromium/CEF, CDP may not work)
|
||||
```
|
||||
|
||||
## The 5-Command Pattern (CDP / Electron)
|
||||
|
||||
Every new Electron adapter should implement these 5 commands in `src/clis/<app_name>/`:
|
||||
|
||||
### 1. `status.ts` — Connection Test
|
||||
```typescript
|
||||
export const statusCommand = cli({
|
||||
site: 'myapp',
|
||||
name: 'status',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.UI,
|
||||
browser: true, // Requires CDP connection
|
||||
args: [],
|
||||
columns: ['Status', 'Url', 'Title'],
|
||||
func: async (page: IPage) => {
|
||||
const url = await page.evaluate('window.location.href');
|
||||
const title = await page.evaluate('document.title');
|
||||
return [{ Status: 'Connected', Url: url, Title: title }];
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 2. `dump.ts` — Reverse Engineering Core
|
||||
Modern app DOMs are huge and obfuscated. **Never guess selectors.** Dump first, then extract precise class names with AI or `grep`:
|
||||
```typescript
|
||||
const dom = await page.evaluate('document.body.innerHTML');
|
||||
fs.writeFileSync('/tmp/app-dom.html', dom);
|
||||
const snap = await page.snapshot({ interactive: false });
|
||||
fs.writeFileSync('/tmp/app-snapshot.json', JSON.stringify(snap, null, 2));
|
||||
```
|
||||
|
||||
### 3. `send.ts` — Advanced Text Injection
|
||||
Electron apps often use complex rich-text editors (Monaco, Lexical, ProseMirror). Setting `.value` directly is ignored by React state.
|
||||
|
||||
**Best practice:** Use `document.execCommand('insertText')` to perfectly simulate real user input, fully piercing React state:
|
||||
```javascript
|
||||
const composer = document.querySelector('[contenteditable="true"]');
|
||||
composer.focus();
|
||||
document.execCommand('insertText', false, 'Hello');
|
||||
```
|
||||
Then submit with `await page.pressKey('Enter')`.
|
||||
|
||||
### 4. `read.ts` — Context Extraction
|
||||
Don't extract the entire page text. Use `dump.ts` output to find the real "conversation container":
|
||||
- Look for semantic selectors: `[role="log"]`, `[data-testid="conversation"]`, `[data-content-search-turn-key]`
|
||||
- Format output as Markdown — readable by both humans and LLMs
|
||||
|
||||
### 5. `new.ts` — Keyboard Shortcuts
|
||||
Many GUI actions respond to native shortcuts rather than button clicks:
|
||||
```typescript
|
||||
const isMac = process.platform === 'darwin';
|
||||
await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
|
||||
await page.wait(1); // Wait for re-render
|
||||
```
|
||||
|
||||
## Environment Variable
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9222"
|
||||
```
|
||||
|
||||
## Non-Electron Pattern (AppleScript)
|
||||
|
||||
For native macOS apps (WeChat, Feishu) that don't expose CDP:
|
||||
```typescript
|
||||
export const statusCommand = cli({
|
||||
site: 'myapp',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false, // No browser needed
|
||||
func: async (page: IPage | null) => {
|
||||
const output = execSync("osascript -e 'application \"MyApp\" is running'", { encoding: 'utf-8' }).trim();
|
||||
return [{ Status: output === 'true' ? 'Running' : 'Stopped' }];
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Core techniques:
|
||||
- **status**: `osascript -e 'application "AppName" is running'`
|
||||
- **send**: `pbcopy` → activate window → `Cmd+V` → `Enter`
|
||||
- **read**: `Cmd+A` → `Cmd+C` → `pbpaste`
|
||||
- **search**: Activate → `Cmd+F`/`Cmd+K` → `keystroke "query"`
|
||||
|
||||
## Pitfalls & Gotchas
|
||||
|
||||
1. **Port conflicts (EADDRINUSE)**: Only one app per port. Use unique ports: Codex=9222, ChatGPT=9224, Cursor=9226, ChatWise=9228, Notion=9230, Discord=9232
|
||||
2. **IPage abstraction**: OpenCLI wraps the browser page as `IPage` (`src/types.ts`). Use `page.pressKey()` and `page.evaluate()`, NOT direct DOM APIs
|
||||
3. **Timing**: Always add `await page.wait(0.5)` to `1.0` after DOM mutations. Returning too early disconnects prematurely
|
||||
4. **AppleScript requires Accessibility**: Terminal app must be granted permission in System Settings → Privacy & Security → Accessibility
|
||||
|
||||
## Port Assignment Table
|
||||
|
||||
| App | Port | Mode |
|
||||
|-----|------|------|
|
||||
| Codex | 9222 | CDP |
|
||||
| ChatGPT | 9224 | CDP / AppleScript |
|
||||
| Cursor | 9226 | CDP |
|
||||
| ChatWise | 9228 | CDP |
|
||||
| Notion | 9230 | CDP |
|
||||
| Discord App | 9232 | CDP |
|
||||
@@ -0,0 +1,72 @@
|
||||
# Remote Chrome
|
||||
|
||||
Run OpenCLI on a server or headless environment by connecting to a remote Chrome instance.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- Running CLI commands on a remote server
|
||||
- CI/CD automation with headed browser
|
||||
- Shared team browser sessions
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Start Chrome on the Remote Machine
|
||||
|
||||
```bash
|
||||
# On the remote machine (or your Mac)
|
||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
|
||||
--remote-debugging-port=9222
|
||||
```
|
||||
|
||||
### 2. SSH Tunnel (If Needed)
|
||||
|
||||
If the remote Chrome is on a different machine, create an SSH tunnel:
|
||||
|
||||
```bash
|
||||
# On your local machine or server
|
||||
ssh -L 9222:127.0.0.1:9222 user@remote-host
|
||||
```
|
||||
|
||||
::: warning
|
||||
Use `127.0.0.1` instead of `localhost` in the SSH command to avoid IPv6 resolution issues that can cause timeouts.
|
||||
:::
|
||||
|
||||
### 3. Configure OpenCLI
|
||||
|
||||
```bash
|
||||
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9222"
|
||||
```
|
||||
|
||||
### 4. Verify
|
||||
|
||||
```bash
|
||||
# Test the connection
|
||||
curl http://127.0.0.1:9222/json/version
|
||||
|
||||
# Run a diagnostic
|
||||
opencli doctor
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
For CI/CD environments, use a real Chrome instance with `xvfb`:
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
steps:
|
||||
- uses: browser-actions/setup-chrome@latest
|
||||
id: setup-chrome
|
||||
- run: |
|
||||
xvfb-run --auto-servernum \
|
||||
${{ steps.setup-chrome.outputs.chrome-path }} \
|
||||
--remote-debugging-port=9222 &
|
||||
```
|
||||
:::
|
||||
|
||||
Set the browser executable path:
|
||||
::: v-pre
|
||||
```yaml
|
||||
env:
|
||||
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||
```
|
||||
:::
|
||||
@@ -0,0 +1,66 @@
|
||||
# AI Workflow
|
||||
|
||||
OpenCLI is designed with AI agents in mind. This guide covers the AI-native discovery and code generation tools.
|
||||
|
||||
## Quick Mode (One-Shot)
|
||||
|
||||
Generate a single command for a specific page URL — just a URL + one-line goal, 4 steps done:
|
||||
|
||||
```bash
|
||||
opencli generate https://example.com --goal "trending"
|
||||
```
|
||||
|
||||
This runs: explore → synthesize → register in one shot.
|
||||
|
||||
For the complete one-shot workflow details, see [CLI-ONESHOT.md](https://github.com/jackwener/opencli/blob/main/CLI-ONESHOT.md).
|
||||
|
||||
## Full Mode (Explorer Workflow)
|
||||
|
||||
### Step 1: Deep Explore
|
||||
|
||||
Discover APIs, infer capabilities, and detect framework:
|
||||
|
||||
```bash
|
||||
opencli explore https://example.com --site mysite
|
||||
```
|
||||
|
||||
Outputs to `.opencli/explore/<site>/`:
|
||||
- `manifest.json` — Site metadata
|
||||
- `endpoints.json` — Discovered API endpoints
|
||||
- `capabilities.json` — Inferred capabilities
|
||||
- `auth.json` — Authentication strategy details
|
||||
|
||||
### Step 2: Synthesize
|
||||
|
||||
Generate YAML adapters from explore artifacts:
|
||||
|
||||
```bash
|
||||
opencli synthesize mysite
|
||||
```
|
||||
|
||||
### Step 3: Strategy Cascade
|
||||
|
||||
Auto-probe authentication strategies: `PUBLIC → COOKIE → HEADER`:
|
||||
|
||||
```bash
|
||||
opencli cascade https://api.example.com/data
|
||||
```
|
||||
|
||||
### Step 4: Validate & Test
|
||||
|
||||
```bash
|
||||
opencli validate # Validate generated YAML
|
||||
opencli <site> <command> --limit 3 -f json # Test the command
|
||||
```
|
||||
|
||||
## 5-Tier Authentication Strategy
|
||||
|
||||
The explorer uses a decision tree to determine the best authentication approach:
|
||||
|
||||
1. **PUBLIC** — No auth, direct API call
|
||||
2. **COOKIE** — Reuse Chrome session cookies
|
||||
3. **HEADER** — Custom auth headers
|
||||
4. **BROWSER** — Full browser automation
|
||||
5. **CDP** — Chrome DevTools Protocol for Electron apps
|
||||
|
||||
For the complete browser exploration workflow and debugging guide, see [CLI-EXPLORER.md](https://github.com/jackwener/opencli/blob/main/CLI-EXPLORER.md).
|
||||
@@ -0,0 +1,103 @@
|
||||
# Architecture
|
||||
|
||||
OpenCLI is built on a **Dual-Engine Architecture** that supports both declarative YAML pipelines and programmatic TypeScript adapters.
|
||||
|
||||
## High-Level Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ opencli CLI │
|
||||
│ (Commander.js entry point) │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Engine Layer │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
|
||||
│ │ Registry │ │ Dynamic │ │ Output │ │
|
||||
│ │ (commands) │ │ Loader │ │ Formatter │ │
|
||||
│ └──────────────┘ └──────────────┘ └────────────┘ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Adapter Layer │
|
||||
│ ┌─────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ YAML Pipeline │ │ TypeScript Adapters │ │
|
||||
│ │ (declarative) │ │ (browser/desktop/AI) │ │
|
||||
│ └─────────────────┘ └──────────────────────────┘ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Connection Layer │
|
||||
│ ┌─────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ Browser Bridge │ │ CDP (Chrome DevTools) │ │
|
||||
│ │ (Extension+WS) │ │ (Electron apps) │ │
|
||||
│ └─────────────────┘ └──────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Core Modules
|
||||
|
||||
### Registry (`src/registry.ts`)
|
||||
Central command registry. All adapters register their commands via the `cli()` function with metadata: site, name, description, domain, strategy, args, columns.
|
||||
|
||||
### Discovery (`src/discovery.ts`)
|
||||
CLI discovery and manifest loading. Discovers commands from YAML and TypeScript adapter files, parses YAML pipelines, and registers them into the central registry.
|
||||
|
||||
### Execution (`src/execution.ts`)
|
||||
Command execution: argument validation, lazy loading of adapter modules, and executing the appropriate handler function.
|
||||
|
||||
### Commander Adapter (`src/commanderAdapter.ts`)
|
||||
Bridges the Registry commands to Commander.js subcommands. Handles positional args, named options, browser session wiring, and output formatting. Isolates all Commander-specific logic so the core is framework-agnostic.
|
||||
|
||||
### Browser (`src/browser.ts`)
|
||||
Manages connections to Chrome via the Browser Bridge WebSocket daemon. Handles JSON-RPC messaging, tab management, and extension/standalone mode switching.
|
||||
|
||||
### Pipeline (`src/pipeline/`)
|
||||
The YAML pipeline engine. Processes declarative steps:
|
||||
- **fetch** — HTTP requests with cookie/header strategies
|
||||
- **map** — Data transformation with template expressions
|
||||
- **limit** — Result truncation
|
||||
- **filter** — Conditional filtering
|
||||
- **download** — Media download support
|
||||
|
||||
### Output (`src/output.ts`)
|
||||
Unified output formatting: `table`, `json`, `yaml`, `md`, `csv`.
|
||||
|
||||
## Authentication Strategies
|
||||
|
||||
OpenCLI uses a 3-tier authentication strategy:
|
||||
|
||||
| Strategy | How It Works | When to Use |
|
||||
|----------|-------------|-------------|
|
||||
| `public` | Direct HTTP fetch, no auth | Public APIs (HackerNews, BBC) |
|
||||
| `cookie` | Reuse Chrome cookies via Browser Bridge | Logged-in sites (Bilibili, Zhihu) |
|
||||
| `header` | Custom auth headers | API-key based services |
|
||||
| `intercept` | Network request interception | GraphQL/XHR capture (Twitter) |
|
||||
| `ui` | DOM interaction via accessibility snapshot | Desktop apps, write operations |
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.ts # Entry point
|
||||
├── cli.ts # Commander.js CLI setup + built-in commands
|
||||
├── commanderAdapter.ts # Registry → Commander bridge
|
||||
├── discovery.ts # CLI discovery, manifest loading, YAML parsing
|
||||
├── execution.ts # Arg validation, command execution
|
||||
├── registry.ts # Command registry
|
||||
├── serialization.ts # Command serialization helpers
|
||||
├── runtime.ts # Browser session & timeout management
|
||||
├── browser/ # Browser Bridge connection
|
||||
├── output.ts # Output formatting
|
||||
├── doctor.ts # Diagnostic tool
|
||||
├── pipeline/ # YAML pipeline engine
|
||||
│ ├── runner.ts
|
||||
│ ├── template.ts
|
||||
│ ├── transform.ts
|
||||
│ └── steps/
|
||||
│ ├── fetch.ts
|
||||
│ ├── map.ts
|
||||
│ ├── limit.ts
|
||||
│ ├── filter.ts
|
||||
│ └── download.ts
|
||||
└── clis/ # Site adapters
|
||||
├── twitter/
|
||||
├── reddit/
|
||||
├── bilibili/
|
||||
├── cursor/
|
||||
└── ...
|
||||
```
|
||||
@@ -0,0 +1,136 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for your interest in contributing to OpenCLI.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# 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
|
||||
npx vitest run src/
|
||||
|
||||
# 5. Link globally (optional, for testing `opencli` command)
|
||||
npm link
|
||||
```
|
||||
|
||||
## Adding 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`:
|
||||
|
||||
::: v-pre
|
||||
```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:
|
||||
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`](https://github.com/jackwener/opencli/blob/main/src/clis/hackernews/top.yaml) for a real example.
|
||||
|
||||
### TypeScript Adapter (For complex browser interactions)
|
||||
|
||||
Create a file like `src/clis/<site>/<command>.ts`:
|
||||
|
||||
```typescript
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
|
||||
cli({
|
||||
site: 'mysite',
|
||||
name: 'search',
|
||||
description: 'Search MySite',
|
||||
domain: 'www.mysite.com',
|
||||
strategy: Strategy.COOKIE,
|
||||
args: [
|
||||
{ name: 'query', 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;
|
||||
// ... browser automation logic
|
||||
return data.slice(0, Number(limit)).map((item: any) => ({
|
||||
title: item.title,
|
||||
url: item.url,
|
||||
date: item.created_at,
|
||||
}));
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Validate Your Adapter
|
||||
|
||||
```bash
|
||||
opencli validate # Validate YAML syntax and schema
|
||||
opencli <site> <command> --limit 3 -f json # Test your command
|
||||
opencli <site> <command> -v # Verbose mode for debugging
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
- **TypeScript strict mode** — avoid `any` where possible.
|
||||
- **ES Modules** — use `.js` extensions in imports (TypeScript output).
|
||||
- **Naming**: `kebab-case` for files, `camelCase` for variables/functions, `PascalCase` for types/classes.
|
||||
- **No default exports** — use named exports.
|
||||
|
||||
## Commit Convention
|
||||
|
||||
We use [Conventional Commits](https://www.conventionalcommits.org/):
|
||||
|
||||
```
|
||||
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 v4
|
||||
```
|
||||
|
||||
## 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:
|
||||
```bash
|
||||
npx tsc --noEmit # Type check
|
||||
npx vitest run src/ # Unit tests
|
||||
opencli validate # YAML validation (if applicable)
|
||||
```
|
||||
4. Commit using conventional commit format
|
||||
5. Push and open a PR
|
||||
@@ -0,0 +1,255 @@
|
||||
# Testing Guide
|
||||
|
||||
> 面向开发者和 AI Agent 的测试参考手册。
|
||||
|
||||
## 目录
|
||||
|
||||
- [测试架构](#测试架构)
|
||||
- [当前覆盖范围](#当前覆盖范围)
|
||||
- [本地运行测试](#本地运行测试)
|
||||
- [如何添加新测试](#如何添加新测试)
|
||||
- [CI/CD 流水线](#cicd-流水线)
|
||||
- [浏览器模式](#浏览器模式)
|
||||
- [站点兼容性](#站点兼容性)
|
||||
|
||||
---
|
||||
|
||||
## 测试架构
|
||||
|
||||
测试分为三层,全部使用 **vitest** 运行:
|
||||
|
||||
```text
|
||||
tests/
|
||||
├── e2e/ # E2E 集成测试(子进程运行真实 CLI)
|
||||
│ ├── helpers.ts # runCli() / parseJsonOutput() 共享工具
|
||||
│ ├── public-commands.test.ts # 公开 API 命令
|
||||
│ ├── browser-public.test.ts # 浏览器命令(公开数据)
|
||||
│ ├── browser-auth.test.ts # 需登录命令(graceful failure)
|
||||
│ ├── management.test.ts # 管理命令(list / validate / verify / help)
|
||||
│ └── output-formats.test.ts # 输出格式校验
|
||||
├── smoke/
|
||||
│ └── api-health.test.ts # 外部 API、adapter 定义、命令注册健康检查
|
||||
src/
|
||||
└── **/*.test.ts # 单元测试(当前 31 个文件)
|
||||
```
|
||||
|
||||
| 层 | 位置 | 当前文件数 | 运行方式 | 用途 |
|
||||
|---|---|---:|---|---|
|
||||
| 单元测试 | `src/**/*.test.ts` | 31 | `npx vitest run src/` | 内部模块、pipeline、adapter 工具函数 |
|
||||
| E2E 测试 | `tests/e2e/*.test.ts` | 5 | `npx vitest run tests/e2e/` | 真实 CLI 命令执行 |
|
||||
| 烟雾测试 | `tests/smoke/*.test.ts` | 1 | `npx vitest run tests/smoke/` | 外部 API 与注册完整性 |
|
||||
|
||||
---
|
||||
|
||||
## 当前覆盖范围
|
||||
|
||||
### 单元测试(31 个文件)
|
||||
|
||||
| 领域 | 文件 |
|
||||
|---|---|
|
||||
| 核心运行时与输出 | `src/browser.test.ts`, `src/browser/dom-snapshot.test.ts`, `src/build-manifest.test.ts`, `src/capabilityRouting.test.ts`, `src/doctor.test.ts`, `src/engine.test.ts`, `src/interceptor.test.ts`, `src/output.test.ts`, `src/plugin.test.ts`, `src/registry.test.ts`, `src/snapshotFormatter.test.ts` |
|
||||
| pipeline 与下载 | `src/download/index.test.ts`, `src/pipeline/executor.test.ts`, `src/pipeline/template.test.ts`, `src/pipeline/transform.test.ts` |
|
||||
| 站点 / adapter 逻辑 | `src/clis/apple-podcasts/commands.test.ts`, `src/clis/apple-podcasts/utils.test.ts`, `src/clis/bloomberg/utils.test.ts`, `src/clis/chaoxing/utils.test.ts`, `src/clis/coupang/utils.test.ts`, `src/clis/google/utils.test.ts`, `src/clis/grok/ask.test.ts`, `src/clis/twitter/timeline.test.ts`, `src/clis/weread/utils.test.ts`, `src/clis/xiaohongshu/creator-note-detail.test.ts`, `src/clis/xiaohongshu/creator-notes-summary.test.ts`, `src/clis/xiaohongshu/creator-notes.test.ts`, `src/clis/xiaohongshu/user-helpers.test.ts`, `src/clis/xiaoyuzhou/utils.test.ts`, `src/clis/youtube/transcript-group.test.ts`, `src/clis/zhihu/download.test.ts` |
|
||||
|
||||
这些测试覆盖的重点包括:
|
||||
|
||||
- Browser Bridge、DOM snapshot、interceptor、capability routing
|
||||
- manifest 生成、命令发现、插件安装与注册表
|
||||
- 输出格式渲染与 snapshot formatting
|
||||
- pipeline 模板求值、执行器与变换步骤
|
||||
- 各站点 adapter 的数据归一化、参数处理与容错逻辑
|
||||
|
||||
### E2E 测试(5 个文件)
|
||||
|
||||
| 文件 | 当前覆盖范围 |
|
||||
|---|---|
|
||||
| `tests/e2e/public-commands.test.ts` | `bloomberg`、`apple-podcasts`、`hackernews`、`v2ex`、`xiaoyuzhou`、`google suggest` 等公开命令 |
|
||||
| `tests/e2e/browser-public.test.ts` | `bbc`、`bloomberg`、`bilibili`、`weibo`、`zhihu`、`reddit`、`twitter`、`xueqiu`、`reuters`、`youtube`、`smzdm`、`boss`、`ctrip`、`coupang`、`xiaohongshu`、`google`、`yahoo-finance`、`v2ex daily` |
|
||||
| `tests/e2e/browser-auth.test.ts` | `bilibili`、`twitter`、`v2ex`、`xueqiu`、`linux-do`、`xiaohongshu` 的需登录命令 graceful failure |
|
||||
| `tests/e2e/management.test.ts` | `list`、`validate`、`verify`、`--version`、`--help`、unknown command |
|
||||
| `tests/e2e/output-formats.test.ts` | `json` / `yaml` / `csv` / `md` 输出格式校验 |
|
||||
|
||||
### 烟雾测试(1 个文件)
|
||||
|
||||
| 文件 | 当前覆盖范围 |
|
||||
|---|---|
|
||||
| `tests/smoke/api-health.test.ts` | `hackernews`、`v2ex` 公开 API 可用性,`validate` 全量 adapter 校验,以及命令注册表基础完整性 |
|
||||
|
||||
### 快速核对命令
|
||||
|
||||
需要刷新测试清单时,直接以仓库文件为准:
|
||||
|
||||
```bash
|
||||
find src -name '*.test.ts' | sort
|
||||
find tests/e2e -name '*.test.ts' | sort
|
||||
find tests/smoke -name '*.test.ts' | sort
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 本地运行测试
|
||||
|
||||
### 前置条件
|
||||
|
||||
```bash
|
||||
npm ci # 安装依赖
|
||||
npm run build # 编译(E2E / smoke 测试需要 dist/main.js)
|
||||
```
|
||||
|
||||
### 运行命令
|
||||
|
||||
```bash
|
||||
# 全部单元测试
|
||||
npx vitest run src/
|
||||
|
||||
# 全部 E2E 测试(会真实调用外部 API / 浏览器)
|
||||
npx vitest run tests/e2e/
|
||||
|
||||
# 全部 smoke 测试
|
||||
npx vitest run tests/smoke/
|
||||
|
||||
# 单个测试文件
|
||||
npx vitest run src/clis/apple-podcasts/commands.test.ts
|
||||
npx vitest run tests/e2e/management.test.ts
|
||||
|
||||
# 全部测试
|
||||
npx vitest run
|
||||
|
||||
# watch 模式(开发时推荐)
|
||||
npx vitest src/
|
||||
```
|
||||
|
||||
### 浏览器命令本地测试须知
|
||||
|
||||
- opencli 通过 Browser Bridge 扩展连接已运行的 Chrome 浏览器
|
||||
- E2E 测试通过 `tests/e2e/helpers.ts` 里的 `runCli()` 调用已构建的 `dist/main.js`
|
||||
- `browser-public.test.ts` 使用 `tryBrowserCommand()`,站点反爬或地域限制导致空数据时会 warn + pass
|
||||
- `browser-auth.test.ts` 验证 **graceful failure**,重点是不 crash、不 hang、错误信息可控
|
||||
- 如需测试完整登录态,保持 Chrome 登录态并安装 Browser Bridge 扩展,再手动运行对应测试
|
||||
|
||||
---
|
||||
|
||||
## 如何添加新测试
|
||||
|
||||
### 新增 YAML Adapter(如 `src/clis/producthunt/trending.yaml`)
|
||||
|
||||
1. `opencli validate` 的 E2E / smoke 测试会覆盖 adapter 结构校验
|
||||
2. 根据 adapter 类型,在对应测试文件补一个 `it()` block
|
||||
|
||||
```typescript
|
||||
// 如果 browser: false(公开 API)→ tests/e2e/public-commands.test.ts
|
||||
it('producthunt trending returns data', async () => {
|
||||
const { stdout, code } = await runCli(['producthunt', 'trending', '--limit', '3', '-f', 'json']);
|
||||
expect(code).toBe(0);
|
||||
const data = parseJsonOutput(stdout);
|
||||
expect(Array.isArray(data)).toBe(true);
|
||||
expect(data.length).toBeGreaterThanOrEqual(1);
|
||||
expect(data[0]).toHaveProperty('title');
|
||||
}, 30_000);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 如果 browser: true 但可公开访问 → tests/e2e/browser-public.test.ts
|
||||
it('producthunt trending returns data', async () => {
|
||||
const data = await tryBrowserCommand(['producthunt', 'trending', '--limit', '3', '-f', 'json']);
|
||||
expectDataOrSkip(data, 'producthunt trending');
|
||||
}, 60_000);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 如果 browser: true 且需登录 → tests/e2e/browser-auth.test.ts
|
||||
it('producthunt me fails gracefully without login', async () => {
|
||||
await expectGracefulAuthFailure(['producthunt', 'me', '-f', 'json'], 'producthunt me');
|
||||
}, 60_000);
|
||||
```
|
||||
|
||||
### 新增管理命令(如 `opencli export`)
|
||||
|
||||
在 `tests/e2e/management.test.ts` 添加测试;如果新命令会影响输出格式,也同步补 `tests/e2e/output-formats.test.ts`。
|
||||
|
||||
### 新增内部模块
|
||||
|
||||
在对应源码旁创建 `*.test.ts`,优先和被测模块放在同一目录下,便于发现与维护。
|
||||
|
||||
### 决策流程图
|
||||
|
||||
```text
|
||||
新增功能 → 是内部模块? → 是 → src/ 下加 *.test.ts
|
||||
↓ 否
|
||||
是 CLI 命令? → browser: false? → tests/e2e/public-commands.test.ts
|
||||
↓ true
|
||||
公开数据? → tests/e2e/browser-public.test.ts
|
||||
↓ 需登录
|
||||
tests/e2e/browser-auth.test.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD 流水线
|
||||
|
||||
### `ci.yml`
|
||||
|
||||
| Job | 触发条件 | 内容 |
|
||||
|---|---|---|
|
||||
| `build` | push/PR 到 `main`,`dev` | `tsc --noEmit` + `npm run build` |
|
||||
| `unit-test` | push/PR 到 `main`,`dev` | Node `20` 与 `22` 双版本运行 `src/` 单元测试,按 `2` shard 并行 |
|
||||
| `smoke-test` | `schedule` 或 `workflow_dispatch` | 安装真实 Chrome,`xvfb-run` 执行 `tests/smoke/` |
|
||||
|
||||
### `e2e-headed.yml`
|
||||
|
||||
| Job | 触发条件 | 内容 |
|
||||
|---|---|---|
|
||||
| `e2e-headed` | push/PR 到 `main`,`dev`,或手动触发 | 安装真实 Chrome,`xvfb-run` 执行 `tests/e2e/` |
|
||||
|
||||
E2E 与 smoke 都使用 `./.github/actions/setup-chrome` 准备真实 Chrome,并通过 `OPENCLI_BROWSER_EXECUTABLE_PATH` 注入浏览器路径。
|
||||
|
||||
### Sharding
|
||||
|
||||
单元测试使用 vitest 内置 shard,并在 Node `20` / `22` 两个版本上运行:
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ['20', '22']
|
||||
shard: [1, 2]
|
||||
steps:
|
||||
- run: npx vitest run src/ --reporter=verbose --shard=${{ matrix.shard }}/2
|
||||
```
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 浏览器模式
|
||||
|
||||
opencli 通过 Browser Bridge 扩展连接浏览器:
|
||||
|
||||
| 条件 | 模式 | 使用场景 |
|
||||
|---|---|---|
|
||||
| 扩展已安装 / 已连接 | Extension 模式 | 本地用户,连接已登录的 Chrome |
|
||||
| 无扩展 token | CLI 自行拉起浏览器 | CI、无登录态或纯自动化场景 |
|
||||
|
||||
CI 中使用 `OPENCLI_BROWSER_EXECUTABLE_PATH` 指定真实 Chrome 路径:
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
env:
|
||||
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||
```
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 站点兼容性
|
||||
|
||||
GitHub Actions 的美国 runner 上,部分站点会因为地域限制、登录要求或反爬而返回空数据。当前 E2E 对这些场景采用 warn + pass 策略,避免偶发站点限制把整条 CI 打红。
|
||||
|
||||
| 站点 | CI 表现 | 常见原因 |
|
||||
|---|---|---|
|
||||
| `hackernews`、`bbc`、`v2ex`、`bloomberg` | 通常返回数据 | 公开接口或公开页面 |
|
||||
| `yahoo-finance`、`google` | 通常返回数据 | 页面公开,但仍可能受限流影响 |
|
||||
| `bilibili`、`zhihu`、`weibo`、`xiaohongshu`、`xueqiu` | 容易空数据 | 地域限制、反爬、登录要求 |
|
||||
| `reddit`、`twitter`、`youtube` | 容易空数据 | 登录态、cookie、机器人检测 |
|
||||
| `smzdm`、`boss`、`ctrip`、`coupang`、`linux-do` | 结果波动较大 | 地域限制、风控或页面结构变动 |
|
||||
|
||||
> 如果需要更稳定的浏览器 E2E 结果,优先使用具备目标站点网络可达性的 self-hosted runner。
|
||||
@@ -0,0 +1,87 @@
|
||||
# TypeScript Adapter Guide
|
||||
|
||||
Use TypeScript adapters when you need browser-side logic, multi-step flows, DOM manipulation, or complex data extraction that goes beyond simple API fetching.
|
||||
|
||||
## Basic Structure
|
||||
|
||||
```typescript
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
|
||||
cli({
|
||||
site: 'mysite',
|
||||
name: 'search',
|
||||
description: 'Search MySite',
|
||||
domain: 'www.mysite.com',
|
||||
strategy: Strategy.COOKIE, // PUBLIC | COOKIE | HEADER
|
||||
args: [
|
||||
{ name: 'query', 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;
|
||||
|
||||
// Navigate and extract data
|
||||
await page.goto('https://www.mysite.com');
|
||||
|
||||
const data = await page.evaluate(`
|
||||
(async () => {
|
||||
const res = await fetch('/api/search?q=${encodeURIComponent(String(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,
|
||||
}));
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Strategy Types
|
||||
|
||||
| Strategy | Constant | Use Case |
|
||||
|----------|----------|----------|
|
||||
| Public | `Strategy.PUBLIC` | No auth needed |
|
||||
| Cookie | `Strategy.COOKIE` | Browser session cookies |
|
||||
| Header | `Strategy.HEADER` | Custom headers/tokens |
|
||||
|
||||
## The `page` Object
|
||||
|
||||
The `page` parameter provides browser interaction methods:
|
||||
|
||||
- `page.goto(url)` — Navigate to a URL
|
||||
- `page.evaluate(script)` — Execute JavaScript in the page context
|
||||
- `page.waitForSelector(selector)` — Wait for an element
|
||||
- `page.click(selector)` — Click an element
|
||||
- `page.type(selector, text)` — Type text into an input
|
||||
|
||||
## The `kwargs` Object
|
||||
|
||||
Contains parsed CLI arguments as key-value pairs. Always destructure with defaults:
|
||||
|
||||
```typescript
|
||||
const { query, limit = 10, format = 'json' } = kwargs;
|
||||
```
|
||||
|
||||
## AI-Assisted Development
|
||||
|
||||
Use the AI workflow tools to accelerate adapter creation:
|
||||
|
||||
```bash
|
||||
# Discover APIs and page structure
|
||||
opencli explore https://example.com --site mysite
|
||||
|
||||
# Auto-generate adapter from explore artifacts
|
||||
opencli synthesize mysite
|
||||
|
||||
# One-shot: explore → synthesize → register
|
||||
opencli generate https://example.com --goal "trending"
|
||||
```
|
||||
|
||||
See [AI Workflow](/developer/ai-workflow) for the complete guide.
|
||||
@@ -0,0 +1,108 @@
|
||||
# YAML Adapter Guide
|
||||
|
||||
YAML adapters are the recommended way to add new commands when the site offers a straightforward API. They use a declarative pipeline approach — no TypeScript required.
|
||||
|
||||
## Basic Structure
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
site: mysite # Site identifier
|
||||
name: trending # Command name (opencli mysite trending)
|
||||
description: ... # Help text
|
||||
domain: www.mysite.com
|
||||
strategy: public # public | cookie | header
|
||||
browser: false # true if browser session is needed
|
||||
|
||||
args: # CLI arguments
|
||||
limit:
|
||||
type: int
|
||||
default: 20
|
||||
description: Number of items
|
||||
|
||||
pipeline: # Data processing steps
|
||||
- fetch:
|
||||
url: https://api.mysite.com/trending
|
||||
|
||||
- map:
|
||||
rank: ${{ index + 1 }}
|
||||
title: ${{ item.title }}
|
||||
|
||||
- limit: ${{ args.limit }}
|
||||
|
||||
columns: [rank, title, score, url]
|
||||
```
|
||||
:::
|
||||
|
||||
## Pipeline Steps
|
||||
|
||||
### `fetch`
|
||||
Fetch data from a URL. Supports template expressions for dynamic URLs.
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
- fetch:
|
||||
url: https://api.example.com/search?q=${{ args.query }}
|
||||
headers:
|
||||
Accept: application/json
|
||||
```
|
||||
:::
|
||||
|
||||
### `map`
|
||||
|
||||
::: v-pre
|
||||
Transform each item in the result array. Use `${{ item.xxx }}` for field access and `${{ index }}` for position.
|
||||
|
||||
```yaml
|
||||
- map:
|
||||
rank: ${{ index + 1 }}
|
||||
title: ${{ item.title }}
|
||||
url: https://example.com${{ item.path }}
|
||||
```
|
||||
:::
|
||||
|
||||
### `limit`
|
||||
Truncate results to N items.
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
- limit: ${{ args.limit }}
|
||||
```
|
||||
:::
|
||||
|
||||
### `filter`
|
||||
Filter items by condition.
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
- filter: ${{ item.score > 100 }}
|
||||
```
|
||||
:::
|
||||
|
||||
### `download`
|
||||
Download media files.
|
||||
|
||||
::: v-pre
|
||||
```yaml
|
||||
- download:
|
||||
url: ${{ item.imageUrl }}
|
||||
dir: ./downloads
|
||||
filename: ${{ item.title | sanitize }}.jpg
|
||||
```
|
||||
:::
|
||||
|
||||
## Template Expressions
|
||||
|
||||
::: v-pre
|
||||
Use `${{ ... }}` for dynamic values:
|
||||
|
||||
| Expression | Description |
|
||||
|-----------|-------------|
|
||||
| `${{ args.limit }}` | CLI argument |
|
||||
| `${{ item.title }}` | Current item field |
|
||||
| `${{ index }}` | Current index (0-based) |
|
||||
| `${{ item.x \| sanitize }}` | Pipe filters |
|
||||
:::
|
||||
|
||||
## Real Example
|
||||
|
||||
See [`src/clis/hackernews/top.yaml`](https://github.com/jackwener/opencli/blob/main/src/clis/hackernews/top.yaml).
|
||||
@@ -0,0 +1,37 @@
|
||||
# Browser Bridge Setup
|
||||
|
||||
> **⚠️ Important**: Browser commands reuse your Chrome login session. You must be logged into the target website in Chrome before running commands.
|
||||
|
||||
OpenCLI connects to your browser through a lightweight **Browser Bridge** Chrome Extension + micro-daemon (zero config, auto-start).
|
||||
|
||||
## Extension Installation
|
||||
|
||||
### Method 1: Download Pre-built Release (Recommended)
|
||||
|
||||
1. Go to the GitHub [Releases page](https://github.com/jackwener/opencli/releases) and download the latest `opencli-extension.zip`.
|
||||
2. Unzip the file and open `chrome://extensions`, enable **Developer mode** (top-right toggle).
|
||||
3. Click **Load unpacked** and select the unzipped folder.
|
||||
|
||||
### Method 2: Load Unpacked Source (For Developers)
|
||||
|
||||
1. Open `chrome://extensions` and enable **Developer mode**.
|
||||
2. Click **Load unpacked** and select the `extension/` directory from the repository.
|
||||
|
||||
## Verification
|
||||
|
||||
That's it! The daemon auto-starts when you run any browser command. No tokens, no manual configuration.
|
||||
|
||||
```bash
|
||||
opencli doctor # Check extension + daemon connectivity
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌─────────────┐ WebSocket ┌──────────────┐ Chrome API ┌─────────┐
|
||||
│ opencli │ ◄──────────────► │ micro-daemon │ ◄──────────────► │ Chrome │
|
||||
│ (Node.js) │ localhost:19825 │ (auto-start) │ Extension │ Browser │
|
||||
└─────────────┘ └──────────────┘ └─────────┘
|
||||
```
|
||||
|
||||
The daemon manages the WebSocket connection between your CLI commands and the Chrome extension. The extension executes JavaScript in the context of web pages, with access to the logged-in session.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Getting Started
|
||||
|
||||
> **Make any website or Electron App your CLI.**
|
||||
> Zero risk · Reuse Chrome login · AI-powered discovery · Browser + Desktop automation
|
||||
|
||||
[](https://www.npmjs.com/package/@jackwener/opencli)
|
||||
[](https://nodejs.org)
|
||||
[](https://github.com/jackwener/opencli/blob/main/LICENSE)
|
||||
|
||||
OpenCLI turns **any website** or **Electron app** into a command-line interface — Bilibili, Zhihu, 小红书, Twitter/X, Reddit, YouTube, Antigravity, and [many more](/adapters/) — powered by browser session reuse and AI-native discovery.
|
||||
|
||||
## Highlights
|
||||
|
||||
- **CLI All Electron** — CLI-ify apps like Antigravity Ultra! Now AI can control itself natively.
|
||||
- **Account-safe** — Reuses Chrome's logged-in state; your credentials never leave the browser.
|
||||
- **AI Agent ready** — `explore` discovers APIs, `synthesize` generates adapters, `cascade` finds auth strategies.
|
||||
- **Self-healing setup** — `opencli doctor` auto-starts the daemon and diagnoses extension + live browser connectivity.
|
||||
- **Dynamic Loader** — Simply drop `.ts` or `.yaml` adapters into the `clis/` folder for auto-registration.
|
||||
- **Dual-Engine Architecture** — Supports both YAML declarative data pipelines and robust browser runtime TypeScript injections.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Install via npm
|
||||
|
||||
```bash
|
||||
npm install -g @jackwener/opencli
|
||||
```
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```bash
|
||||
opencli list # See all commands
|
||||
opencli hackernews top --limit 5 # Public API, no browser
|
||||
opencli bilibili hot --limit 5 # Browser command
|
||||
opencli zhihu hot -f json # JSON output
|
||||
```
|
||||
|
||||
### Output Formats
|
||||
|
||||
All built-in commands support `--format` / `-f`:
|
||||
|
||||
```bash
|
||||
opencli bilibili hot -f table # Default: rich terminal table
|
||||
opencli bilibili hot -f json # JSON (pipe to jq or LLMs)
|
||||
opencli bilibili hot -f yaml # YAML (human-readable)
|
||||
opencli bilibili hot -f md # Markdown
|
||||
opencli bilibili hot -f csv # CSV
|
||||
opencli bilibili hot -v # Verbose: show pipeline debug
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Installation details](/guide/installation)
|
||||
- [Browser Bridge setup](/guide/browser-bridge)
|
||||
- [Plugins — extend with community adapters](/guide/plugins)
|
||||
- [All available adapters](/adapters/)
|
||||
- [For developers / AI agents](/developer/contributing)
|
||||
@@ -0,0 +1,37 @@
|
||||
# Installation
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Node.js**: >= 20.0.0
|
||||
- **Chrome** running and logged into the target site (for browser commands)
|
||||
|
||||
## Install via npm (Recommended)
|
||||
|
||||
```bash
|
||||
npm install -g @jackwener/opencli
|
||||
```
|
||||
|
||||
## Install from Source
|
||||
|
||||
```bash
|
||||
git clone git@github.com:jackwener/opencli.git
|
||||
cd opencli
|
||||
npm install
|
||||
npm run build
|
||||
npm link # Link binary globally
|
||||
opencli list # Now you can use it anywhere!
|
||||
```
|
||||
|
||||
## Update
|
||||
|
||||
```bash
|
||||
npm install -g @jackwener/opencli@latest
|
||||
```
|
||||
|
||||
## Verify Installation
|
||||
|
||||
```bash
|
||||
opencli --version # Check version
|
||||
opencli list # List all commands
|
||||
opencli doctor # Diagnose connectivity
|
||||
```
|
||||
@@ -0,0 +1,153 @@
|
||||
# Plugins
|
||||
|
||||
OpenCLI supports community-contributed plugins. Install third-party adapters from GitHub, and they're automatically discovered alongside built-in commands.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Install a plugin
|
||||
opencli plugin install github:ByteYue/opencli-plugin-github-trending
|
||||
|
||||
# List installed plugins
|
||||
opencli plugin list
|
||||
|
||||
# Use the plugin (it's just a regular command)
|
||||
opencli github-trending repos --limit 10
|
||||
|
||||
# Remove a plugin
|
||||
opencli plugin uninstall github-trending
|
||||
```
|
||||
|
||||
## How Plugins Work
|
||||
|
||||
Plugins live in `~/.opencli/plugins/<name>/`. Each subdirectory is scanned at startup for `.yaml`, `.ts`, or `.js` command files — the same formats used by built-in adapters.
|
||||
|
||||
### Supported Source Formats
|
||||
|
||||
```bash
|
||||
opencli plugin install github:user/repo
|
||||
opencli plugin install https://github.com/user/repo
|
||||
```
|
||||
|
||||
The repo name prefix `opencli-plugin-` is automatically stripped for the local directory name. For example, `opencli-plugin-hot-digest` becomes `hot-digest`.
|
||||
|
||||
## Creating a Plugin
|
||||
|
||||
### Option 1: YAML Plugin (Simplest)
|
||||
|
||||
Zero dependencies, no build step. Just create a `.yaml` file:
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── my-command.yaml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
Example `my-command.yaml`:
|
||||
|
||||
```yaml
|
||||
site: my-plugin
|
||||
name: my-command
|
||||
description: My custom command
|
||||
strategy: public
|
||||
browser: false
|
||||
|
||||
args:
|
||||
limit:
|
||||
type: int
|
||||
default: 10
|
||||
|
||||
pipeline:
|
||||
- fetch:
|
||||
url: https://api.example.com/data
|
||||
- map:
|
||||
title: ${{ item.title }}
|
||||
score: ${{ item.score }}
|
||||
- limit: ${{ args.limit }}
|
||||
|
||||
columns: [title, score]
|
||||
```
|
||||
|
||||
### Option 2: TypeScript Plugin
|
||||
|
||||
For richer logic (multi-source aggregation, custom transformations, etc.):
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── package.json
|
||||
├── my-command.ts
|
||||
└── README.md
|
||||
```
|
||||
|
||||
`package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "opencli-plugin-my-plugin",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"peerDependencies": {
|
||||
"@jackwener/opencli": ">=1.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`my-command.ts`:
|
||||
|
||||
```typescript
|
||||
import { cli, Strategy } from '@jackwener/opencli/registry';
|
||||
|
||||
cli({
|
||||
site: 'my-plugin',
|
||||
name: 'my-command',
|
||||
description: 'My custom command',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [
|
||||
{ name: 'limit', type: 'int', default: 10, help: 'Number of items' },
|
||||
],
|
||||
columns: ['title', 'score'],
|
||||
func: async (_page, kwargs) => {
|
||||
const res = await fetch('https://api.example.com/data');
|
||||
const data = await res.json();
|
||||
return data.items.slice(0, kwargs.limit).map((item: any, i: number) => ({
|
||||
title: item.title,
|
||||
score: item.score,
|
||||
}));
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### TS Plugin Install Lifecycle
|
||||
|
||||
When you run `opencli plugin install`, TS plugins are automatically set up:
|
||||
|
||||
1. **Clone** — `git clone --depth 1` from GitHub
|
||||
2. **npm install** — Resolves regular dependencies
|
||||
3. **Host symlink** — Links the running `@jackwener/opencli` into the plugin's `node_modules/` so `import from '@jackwener/opencli/registry'` always resolves against the host
|
||||
4. **Transpile** — Compiles `.ts` → `.js` via `esbuild` (production `node` cannot load `.ts` directly)
|
||||
|
||||
On startup, if both `my-command.ts` and `my-command.js` exist, the `.js` version is loaded to avoid duplicate registration.
|
||||
|
||||
## Example Plugins
|
||||
|
||||
| Repo | Type | Description |
|
||||
|------|------|-------------|
|
||||
| [opencli-plugin-github-trending](https://github.com/ByteYue/opencli-plugin-github-trending) | YAML | GitHub Trending repositories |
|
||||
| [opencli-plugin-hot-digest](https://github.com/ByteYue/opencli-plugin-hot-digest) | TS | Multi-platform trending aggregator (zhihu, weibo, bilibili, v2ex, stackoverflow, reddit, linux-do) |
|
||||
| [opencli-plugin-juejin](https://github.com/Astro-Han/opencli-plugin-juejin) | YAML | 稀土掘金 (Juejin) hot articles, categories, and article feed |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Command not found after install
|
||||
|
||||
Restart opencli (or open a new terminal) — plugins are discovered at startup.
|
||||
|
||||
### TS plugin import errors
|
||||
|
||||
If you see `Cannot find module '@jackwener/opencli/registry'`, the host symlink may be broken. Reinstall the plugin:
|
||||
|
||||
```bash
|
||||
opencli plugin uninstall my-plugin
|
||||
opencli plugin install github:user/opencli-plugin-my-plugin
|
||||
```
|
||||
@@ -0,0 +1,56 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Common Issues
|
||||
|
||||
### "Extension not connected"
|
||||
|
||||
- Ensure the opencli Browser Bridge extension is installed and **enabled** in `chrome://extensions`.
|
||||
- Run `opencli doctor` to diagnose connectivity.
|
||||
|
||||
### Empty data or 'Unauthorized' error
|
||||
|
||||
- Your login session in Chrome might have expired. Open a normal Chrome tab, navigate to the target site, and log in or refresh the page.
|
||||
- Some sites have geographic restrictions (e.g., Bilibili, Zhihu from outside China).
|
||||
|
||||
### Node API errors
|
||||
|
||||
- Make sure you are using **Node.js >= 20**. Some dependencies require modern Node APIs.
|
||||
- Run `node --version` to verify.
|
||||
|
||||
### Daemon issues
|
||||
|
||||
```bash
|
||||
# Check daemon status
|
||||
curl localhost:19825/status
|
||||
|
||||
# View extension logs
|
||||
curl localhost:19825/logs
|
||||
|
||||
# Kill and restart daemon
|
||||
pkill -f opencli-daemon
|
||||
opencli doctor
|
||||
```
|
||||
|
||||
### Desktop adapter connection issues
|
||||
|
||||
For Electron/CDP-based adapters (Cursor, Codex, etc.):
|
||||
|
||||
1. Make sure the app is launched with `--remote-debugging-port=XXXX`
|
||||
2. Verify the endpoint is set: `echo $OPENCLI_CDP_ENDPOINT`
|
||||
3. Test the endpoint: `curl http://127.0.0.1:XXXX/json/version`
|
||||
|
||||
### Build errors
|
||||
|
||||
```bash
|
||||
# Clean rebuild
|
||||
rm -rf dist/
|
||||
npm run build
|
||||
|
||||
# Type check
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
- [GitHub Issues](https://github.com/jackwener/opencli/issues) — Bug reports and feature requests
|
||||
- Run `opencli doctor` for comprehensive diagnostics
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: OpenCLI
|
||||
text: Make any website or Electron App your CLI
|
||||
tagline: Zero risk · Reuse Chrome login · AI-powered discovery · Browser + Desktop automation
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Get Started
|
||||
link: /guide/getting-started
|
||||
- theme: alt
|
||||
text: View on GitHub
|
||||
link: https://github.com/jackwener/opencli
|
||||
|
||||
features:
|
||||
- icon: 🖥️
|
||||
title: CLI All Electron
|
||||
details: Turn ANY Electron application into a CLI tool — Cursor, Codex, Antigravity, ChatGPT, Notion, and more. AI can control itself natively.
|
||||
- icon: 🔐
|
||||
title: Account Safe
|
||||
details: Reuses Chrome's logged-in state. Your credentials never leave the browser — no tokens, no exposed passwords.
|
||||
- icon: 🤖
|
||||
title: AI Agent Ready
|
||||
details: "explore discovers APIs, synthesize generates adapters, cascade finds auth strategies. Built for AI-first workflows."
|
||||
- icon: ⚡
|
||||
title: Dual-Engine Architecture
|
||||
details: Supports both YAML declarative data pipelines and robust browser runtime TypeScript injections for maximum flexibility.
|
||||
- icon: 🔧
|
||||
title: Self-Healing Setup
|
||||
details: "opencli doctor auto-starts the daemon and diagnoses extension + live browser connectivity."
|
||||
- icon: 📦
|
||||
title: Dynamic Loader
|
||||
details: Simply drop .ts or .yaml adapters into the clis/ folder for auto-registration. Zero boilerplate.
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
# 所有适配器
|
||||
|
||||
运行 `opencli list` 查看完整命令列表。
|
||||
|
||||
详细文档请参考 [英文版本](/adapters/)。
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user