OpenCLI 开源项目解读
项目信息
项目名称:OpenCLI
项目描述:OpenCLI 是一个让你通过命令行操作网站、控制 Electron 应用、统一管理 CLI 工具的 AI 驱动型自动化平台。
核心功能:
- 70+ 预置网站适配器(bilibili、twitter、hackernews 等)
- 浏览器自动化(复用登录会话,无需密码)
- Electron 应用控制(Cursor、ChatGPT 等)
- CLI 工具统一管理
- AI Agent 友好的技能系统
1. 项目概览
1.1 项目定位与核心价值
一句话定位:OpenCLI 是一个将网站、浏览器会话、Electron 应用和本地工具转换为确定性命令行接口的 AI 驱动型自动化平台。
核心痛点解决:
- 网站操作自动化困难:传统爬虫易被反爬虫拦截,需要复用已登录会话。
- AI Agent 缺乏可靠的网站操作能力:需要标准化接口、安全的登录状态复用、确定性输出。
- CLI 工具碎片化:各种工具分散,缺乏统一发现和调用机制。
- Electron 应用自动化:桌面应用缺乏自动化接口。
目标用户:
- 普通用户:通过命令行快速使用网站功能
- AI Agent 开发者:为 Agent 提供浏览器操作和 CLI 调用能力
- 网站自动化开发者:编写适配器将网站转为 CLI 命令
- Claude Code / Cursor 用户:使用内置技能快速操作网站
1.2 目标用户与使用场景
核心场景:
- AI Agent 实时网站操作:Agent 通过 OpenCLI 操作用户已登录的浏览器
- 确定性网站 CLI 命令:使用预置适配器(70+ 网站)
- CLI 工具统一管理:作为统一入口发现、安装、调用各种 CLI
- Electron 应用控制:直接控制 Cursor、ChatGPT 等桌面应用
1.3 核心技术亮点
- 分层插件化架构:抽象浏览器操作、插件化适配器
- 安全的会话复用:Daemon + Extension 架构,多层安全防御
- AI Agent 友好:结构化技能、确定性输出
- 70+ 预置网站适配器:开箱即用
- 多种浏览器连接方式:BrowserBridge(扩展)、CDPBridge(直接 CDP)
- 策略模式适配器:PUBLIC/COOKIE/HEADER/INTERCEPT/UI 多种策略
1.4 技术栈与选型对比
| 层级 | 技术选型 |
|---|---|
| 运行时 | Node.js >= 21.0.0 |
| CLI 框架 | Commander.js |
| 浏览器通信 | Chrome DevTools Protocol (CDP), WebSocket (ws) |
| 数据格式 | JSON, YAML, CSV, Markdown |
| 测试框架 | Vitest |
| 文档工具 | VitePress |
关键选型决策:
- 直接使用 CDP 而非 Puppeteer:可连接任意 Chrome 实例、复用登录会话
- Daemon + Extension 架构:安全地桥接 CLI 和浏览器
- Skills 系统:为 AI Agent 提供结构化工作流
2. 整体架构设计
2.1 架构概述
OpenCLI 采用分层插件化架构,通过标准化的接口将 CLI、浏览器自动化、网站适配器和 AI 技能组合在一起。架构设计的核心理念是:
- 抽象浏览器操作:通过 IPage 接口统一不同浏览器控制方式
- 插件化适配器:网站适配器可以独立开发和注册
- 安全的会话复用:通过 Daemon + Extension 架构复用浏览器登录状态
- AI Agent 友好:提供结构化的技能和确定性的输出
2.2 整体架构图 (ASCII)
┌─────────────────────────────────────────────────────────────────────────────┐
│ 用户 / AI Agent 层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 人类用户 │ │ Claude Code │ │ 其他 Agent │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼───────────────────┼───────────────────┼───────────────────────────┘
│ │ │
└───────────────────┴───────────────────┘
│
┌─────────────────────────────▼───────────────────────────────────────────────┐
│ CLI 层 (main.ts + cli.ts) │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ Commander.js 命令解析器 │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ 内置命令 │ │ 适配器命令 │ │ 外部 CLI │ │ │
│ │ │ (list, │ │ (bilibili, │ │ (gh, │ │ │
│ │ │ doctor) │ │ twitter) │ │ docker) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
┌─────────────▼─────────────┐ ┌────────────▼─────────────┐
│ 适配器层 (registry.ts) │ │ 管道执行层 (pipeline/) │
│ ┌───────────────────────┐│ ┌─────────────────────────┐│
│ │ 策略枚举: ││ │ 步骤类型: ││
│ │ PUBLIC/COOKIE/ ││ │ fetch/extract/ ││
│ │ HEADER/INTERCEPT/UI ││ │ download/transform ││
│ └───────────────────────┘│ └─────────────────────────┘│
│ ┌───────────────────────┐│ ┌─────────────────────────┐│
│ │ 70+ 网站适配器 ││ │ 执行引擎 (executor.ts) ││
│ └───────────────────────┘│ └─────────────────────────┘│
└─────────────┬─────────────┘ └────────────┬──────────────┘
│ │
└───────────────┬───────────────┘
│
┌─────────────────────────────▼───────────────────────────────────────────────┐
│ 浏览器控制层 (src/browser/) │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ IPage 接口抽象 (types.ts) │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ BrowserBridge │ │ CDPBridge │ │ │
│ │ │ (Extension模式) │ │ (直接CDP连接) │ │ │
│ │ └────────┬─────────┘ └────────┬─────────┘ │ │
│ └───────────┼─────────────────────────┼───────────────────────────────────┘ │
└──────────────┼─────────────────────────┼───────────────────────────────────────┘
│ │
┌──────────▼──────────┐ ┌─────────▼─────────┐
│ DOM 快照与提取 │ │ 网络请求拦截 │
│ (dom-snapshot.ts) │ │ (network-cache.ts)│
└─────────────────────┘ └───────────────────┘
│ │
┌──────────────▼─────────────────────────▼───────────────────────────────────┐
│ 通信层 (Daemon + Extension) │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ daemon.ts │ │ Browser Extension │ │
│ │ (本地 HTTP 服务) │◄──────────►│ (WebSocket 连接) │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────┐
│ Chrome/Chromium │
│ (已登录会话) │
└───────────────────┘2.3 分层职责说明
| 层 | 职责 | 核心文件 |
|---|---|---|
| CLI 层 | 命令解析、参数处理、输出格式化 | main.ts, cli.ts, output.ts |
| 适配器层 | 70+ 网站适配器、命令注册、策略管理 | registry.ts, discovery.ts, clis/ |
| 浏览器控制层 | IPage 抽象、BrowserBridge/CDPBridge、DOM 操作 | browser/page.ts, bridge.ts, cdp.ts |
| 通信层 | Daemon HTTP+WS 服务、扩展通信 | daemon.ts, extension/src/ |
2.4 完整目录树
opencli/
├── src/ # 【核心基建】源代码主目录
│ ├── main.ts # 【核心基建】CLI 入口,快速路径优化
│ ├── cli.ts # 【核心基建】CLI 命令注册与 Commander 集成
│ ├── daemon.ts # 【核心基建】本地守护进程,HTTP+WS 服务
│ ├── types.ts # 【核心基建】核心类型定义(IPage, etc.)
│ ├── registry.ts # 【核心基建】命令注册中心(globalThis 单例)
│ ├── discovery.ts # 【核心基建】适配器/插件发现与加载
│ ├── execution.ts # 【核心基建】命令执行引擎
│ ├── launcher.ts # 【核心基建】浏览器会话启动器
│ ├── output.ts # 【核心基建】输出格式化(table/json/yaml/md/csv)
│ ├── errors.ts # 【核心基建】错误定义与处理
│ ├── constants.ts # 【核心基建】常量定义
│ ├── utils.ts # 【工具集】通用工具函数
│ ├── logger.ts # 【工具集】日志工具
│ ├── version.ts # 【工具集】版本信息
│ ├── hooks.ts # 【工具集】生命周期钩子
│ ├── update-check.ts # 【工具集】更新检查
│ ├── external.ts # 【业务模块】外部 CLI 集成
│ ├── plugin.ts # 【业务模块】插件系统
│ ├── plugin-scaffold.ts # 【业务模块】插件脚手架生成
│ ├── plugin-manifest.ts # 【工具集】插件 manifest 处理
│ ├── commanderAdapter.ts # 【工具集】Commander.js 适配器注册
│ ├── completion.ts # 【工具集】Shell 补全生成
│ ├── completion-fast.ts # 【工具集】快速补全(从 manifest 读取)
│ ├── completion-shared.ts # 【工具集】补全共享逻辑
│ ├── validate.ts # 【工具集】适配器验证
│ ├── verify.ts # 【工具集】适配器验证+测试
│ ├── build-manifest.ts # 【工具集】构建命令 manifest
│ ├── capabilityRouting.ts # 【工具集】功能路由
│ ├── node-network.ts # 【工具集】Node.js 网络工具
│ ├── runtime.ts # 【工具集】运行时检测
│ ├── runtime-detect.ts # 【工具集】运行时环境检测
│ ├── diagnostic.ts # 【工具集】诊断工具
│ ├── doctor.ts # 【工具集】Doctor 命令实现
│ ├── electron-apps.ts # 【业务模块】Electron 应用控制
│ ├── interceptor.ts # 【工具集】网络拦截器
│ ├── snapshotFormatter.ts # 【工具集】DOM 快照格式化
│ ├── tui.ts # 【UI 视图】终端 UI 组件
│ ├── analysis.ts # 【工具集】网站分析(反爬虫检测等)
│ ├── package-paths.ts # 【工具集】包路径解析
│ ├── external-clis.yaml # 【配置】外部 CLI 定义
│ │
│ ├── browser/ # 【核心基建】浏览器控制模块
│ │ ├── index.ts # 【核心基建】模块导出入口
│ │ ├── bridge.ts # 【核心基建】BrowserBridge - 扩展通信
│ │ ├── cdp.ts # 【核心基建】CDPBridge - 直接 CDP 连接
│ │ ├── page.ts # 【核心基建】Page - IPage 实现
│ │ ├── base-page.ts # 【核心基建】BasePage - 基础实现
│ │ ├── dom-snapshot.ts # 【核心基建】DOM 快照生成
│ │ ├── dom-helpers.ts # 【工具集】DOM 帮助函数
│ │ ├── html-tree.ts # 【工具集】HTML 树生成
│ │ ├── extract.ts # 【工具集】内容提取(文章/Markdown)
│ │ ├── find.ts # 【工具集】元素查找
│ │ ├── target-resolver.ts # 【核心基建】目标解析(CSS+数字引用)
│ │ ├── target-errors.ts # 【工具集】目标相关错误
│ │ ├── shape.ts # 【工具集】数据形状推断
│ │ ├── shape-filter.ts # 【工具集】形状过滤
│ │ ├── network-key.ts # 【工具集】网络请求 Key 生成
│ │ ├── network-cache.ts # 【核心基建】网络缓存(保存请求供分析)
│ │ ├── analyze.ts # 【核心基建】网站分析(反爬虫检测)
│ │ ├── stealth.ts # 【核心基建】反检测脚本注入
│ │ ├── daemon-client.ts # 【工具集】Daemon 客户端
│ │ ├── verify-fixture.ts # 【工具集】验证夹具
│ │ ├── errors.ts # 【工具集】错误定义
│ │ ├── compound.ts # 【工具集】复合操作
│ │ ├── tabs.ts # 【工具集】标签页管理
│ │ ├── utils.ts # 【工具集】浏览器工具函数
│ │ ├── article-extract.ts # 【工具集】文章提取(Readability)
│ │ └── __fixtures__/ # 【配置】测试夹具
│ │
│ ├── pipeline/ # 【核心基建】管道执行引擎
│ │ ├── index.ts # 【核心基建】模块导出
│ │ ├── executor.ts # 【核心基建】管道执行器
│ │ ├── registry.ts # 【核心基建】管道步骤注册
│ │ └── steps/ # 【核心基建】内置管道步骤
│ │ ├── browser.ts # 浏览器操作步骤
│ │ ├── fetch.ts # 网络请求步骤
│ │ └── download.test.ts # 下载步骤测试
│ │
│ ├── download/ # 【业务模块】下载功能
│ │ ├── index.ts # 【核心基建】下载模块入口
│ │ ├── media-download.ts # 【业务模块】媒体下载
│ │ ├── article-download.ts # 【业务模块】文章下载
│ │ └── progress.ts # 【工具集】下载进度显示
│ │
│ └── commands/ # 【业务模块】内置命令实现
│ └── daemon.ts # daemon 子命令
│
├── clis/ # 【业务模块】网站适配器目录(70+ 网站)
│ ├── _shared/ # 【工具集】适配器共享代码
│ ├── _shared/desktop-commands.js # 【工具集】桌面应用命令
│ ├── bilibili/ # 【业务模块】Bilibili 适配器
│ │ ├── hot.js # 热门
│ │ ├── search.js # 搜索
│ │ ├── video.js # 视频
│ │ ├── download.js # 下载
│ │ └── ...
│ ├── twitter/ # 【业务模块】Twitter/X 适配器
│ ├── hackernews/ # 【业务模块】HackerNews 适配器
│ ├── reddit/ # 【业务模块】Reddit 适配器
│ ├── 1688/ # 【业务模块】1688 适配器
│ └── ... (70+ 网站适配器)
│
├── extension/ # 【核心基建】浏览器扩展(Browser Bridge)
│ ├── manifest.json # 【配置】扩展 manifest
│ ├── src/background.ts # 【核心基建】Background script
│ ├── src/cdp.ts # 【核心基建】CDP 封装
│ ├── src/identity.ts # 【工具集】身份验证
│ ├── src/protocol.ts # 【工具集】协议定义
│ ├── popup.html # 【UI 视图】Popup 页面
│ ├── popup.js # 【UI 视图】Popup 脚本
│ └── package.json # 【配置】扩展依赖
│
├── skills/ # 【工具集】AI Agent 技能
│ ├── opencli-adapter-author/ # 【工具集】适配器编写技能
│ │ ├── SKILL.md # 技能主文件
│ │ └── references/ # 参考文档
│ ├── opencli-autofix/ # 【工具集】自动修复技能
│ ├── opencli-browser/ # 【工具集】浏览器操作参考
│ ├── opencli-usage/ # 【工具集】使用指南
│ └── smart-search/ # 【工具集】智能搜索
│
├── docs/ # 【配置】项目文档
│ ├── adapters/ # 适配器文档
│ └── guide/ # 使用指南
│
├── scripts/ # 【工具集】构建和部署脚本
│ ├── clean-dist.cjs # 清理 dist
│ ├── copy-yaml.cjs # 复制 yaml 文件
│ ├── fetch-adapters.js # 获取适配器
│ └── postinstall.js # 安装后脚本
│
├── autoresearch/ # 自动化研究相关
│
├── tests/ # 【质量保证】测试目录
│
├── .github/ # 【配置】GitHub Actions CI/CD
│
├── package.json # 【配置】项目配置
├── tsconfig.json # 【配置】TypeScript 配置
├── cli-manifest.json # 【配置】命令 manifest(构建生成)
├── README.md # 【配置】英文 README
├── README.zh-CN.md # 【配置】中文 README
├── LICENSE # 【配置】许可证
└── PRIVACY.md # 【配置】隐私政策3. 模块依赖与调用关系
3.1 全局入口与核心路由
逻辑说明
OpenCLI 的入口流程设计为快速路径优先,避免为简单命令付出完整启动成本:
快速路径 (
--version,completion,--get-completions):- 直接处理,不进行适配器发现
- 读取缓存的 manifest 文件获取补全信息
完整路径(其他命令):
- 并行执行:用户适配器兼容性检查 + 内置适配器发现
- 加载用户适配器(
~/.opencli/clis/) - 发现插件
- 注册更新检查钩子
- 解析命令并执行
调用拓扑 (plainText)
main.ts (入口)
│
├─── 快速路径检查
│ ├───> --version → 直接输出版本号 → 退出
│ ├───> completion <shell> → 打印补全脚本 → 退出
│ └───> --get-completions → 读取 manifest → 输出补全 → 退出
│
└─── 完整路径 (普通命令)
│
├─── 并行启动
│ ├───> ensureUserCliCompatShims()
│ ├───> ensureUserAdapters()
│ └───> discoverClis(BUILTIN_CLIS)
│
├───> discoverClis(USER_CLIS)
├───> discoverPlugins()
│
├───> registerUpdateNoticeOnExit()
├───> checkForUpdateBackground()
│
└───> cli.ts → runCli()
│
└───> Commander.js 解析命令
│
├───> 内置命令处理
│ ├─── list
│ ├─── doctor
│ ├─── browser <subcmd>
│ ├─── plugin <subcmd>
│ └─── ...
│
├───> 适配器命令处理
│ └───> execution.ts → executeCommand()
│ │
│ ├───> (如有必要) launcher.ts → 启动浏览器会话
│ ├───> 执行适配器 func() 或 pipeline
│ └───> output.ts → 格式化输出
│
└───> 外部 CLI 处理
└───> external.ts → 透传到外部工具3.2 核心业务实体与关联
实体定义:
| 实体 | 定义 | 核心属性 |
|---|---|---|
| CliCommand | 一个 CLI 命令(网站适配器或内置命令) | site, name, strategy, args, func/pipeline |
| IPage | 浏览器页面的抽象接口 | goto, evaluate, click, typeText, snapshot, 等 |
| Strategy | 适配器认证/执行策略枚举 | PUBLIC, COOKIE, HEADER, INTERCEPT, UI |
| Registry | 全局命令注册中心 | Map<site/name, CliCommand> |
实体引用拓扑 (ASCII):
[CliCommand] (策略: Strategy)
│
├─── 1:1 ──> [IPage] (浏览器页面抽象)
│ │
│ ├───> BrowserBridge 实现
│ └───> CDPBridge 实现
│
├─── 1:N ──> [Arg] (命令参数定义)
│
└─── 注册到
│
[Registry] (全局单例,使用 globalThis)4. 核心模块详解
4.1 浏览器控制模块 (src/browser/)
模块名:浏览器控制模块
设计说明:采用桥接模式 + 策略模式设计:
- IPage 接口:定义统一页面操作抽象
- BrowserBridge:通过浏览器扩展 + Daemon 通信
- CDPBridge:直接连接 Chrome DevTools Protocol(用于 Electron)
- Page 类:统一实现,依赖底层桥接
关键技术点:
目标解析 (Target Resolver):
- 支持 CSS 选择器和数字引用(来自 snapshot)
- 提供稳定的元素重识别机制(应对 DOM 变化)
- 匹配级别:exact → stable → reidentified
Stealth 脚本:
- 注入各种反检测脚本
- 隐藏自动化特征
网络缓存:
- 保存网络请求用于后续分析
- 支持按 key 检索完整响应
内部结构图 (ASCII):
┌─────────────────────────────────────────────────────────────────────────┐
│ IPage 接口 (types.ts) │
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ goto(url) evaluate(js) click(ref) ││
│ │ typeText(ref, text) pressKey(key) snapshot(opts) ││
│ │ wait(opts) tabs() networkRequests() ││
│ └─────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────┬─────────────────────────────────────────────┘
│
┌───────────────────┴───────────────────┐
│ │
┌─────────▼──────────┐ ┌────────▼─────────┐
│ BrowserBridge │ │ CDPBridge │
│ (Extension + │ │ (直接 CDP) │
│ Daemon) │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ bridge.ts │ │ │ │ cdp.ts │ │
│ │ 连接扩展 │ │ │ │ 连接 Chrome │ │
│ └─────────────┘ │ │ └─────────────┘ │
└─────────┬─────────┘ └────────┬─────────┘
│ │
└───────────────┬───────────────────────┘
│
┌───────▼───────┐
│ Page 类 │
│ (page.ts) │
└───────┬───────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌──────▼──────┐
│ DOM快照 │ │ 元素查找 │ │ 网络拦截 │
│ (dom- │ │ (find.ts)│ │ (network- │
│ snapshot│ │ │ │ cache.ts) │
│ .ts) │ └─────────┘ └────────────┘
└─────────┘
│
┌────▼────┐
│ 目标解析 │
│ (target-│
│ resolver│
│ .ts) │
└─────────┘4.2 适配器注册与发现 (src/registry.ts + discovery.ts)
模块名:适配器注册与发现模块
设计说明:采用注册中心模式 + 懒加载设计:
- 使用
globalThis确保单例(应对 npm link) - 支持多种来源:内置、用户目录、插件
- 策略自动归一化(strategy → browser + navigateBefore)
策略归一化规则:
| Strategy | browser | navigateBefore | 说明 |
|---|---|---|---|
| PUBLIC | false | undefined | 不需要浏览器 |
| COOKIE | true | "https://domain" | 需要浏览器,预导航 |
| HEADER | true | "https://domain" | 需要自定义 header |
| INTERCEPT | true | true | 需要拦截网络 |
| UI | true | true | 需要 UI 自动化 |
内部结构图:
┌─────────────────────────────────────────────────────────────────────────┐
│ Registry (registry.ts) │
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ globalThis.__opencli_registry__: Map<string, CliCommand> ││
│ └─────────────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ cli(opts) → 注册命令 ││
│ │ registerCommand(cmd) → 归一化后注册 ││
│ │ getRegistry() → 获取全局 Map ││
│ │ fullName(cmd) → "site/name" ││
│ └─────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────┬─────────────────────────────────────────────┘
│
┌─────────────────────────────▼─────────────────────────────────────────────┐
│ Discovery (discovery.ts) │
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ discoverClis(dir) → 扫描目录下的适配器 ││
│ │ - 支持 .js/.ts/.yaml 格式 ││
│ │ - 递归扫描子目录 ││
│ ├─────────────────────────────────────────────────────────────────────┤│
│ │ discoverPlugins() → 扫描已安装插件 ││
│ │ ensureUserAdapters() → 确保用户适配器目录存在 ││
│ └─────────────────────────────────────────────────────────────────────┘│
└───────────────────────────────────────────────────────────────────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌─────▼─────┐ ┌──────▼──────┐
│ 内置 │ │ 用户目录 │ │ 插件 │
│ clis/ │ │ ~/.opencli│ │ 动态加载 │
│ │ │ /clis/ │ │ │
└─────────┘ └───────────┘ └─────────────┘4.3 守护进程 (src/daemon.ts)
设计说明:
守护进程是 OpenCLI 安全架构的核心,采用多层防御设计:
- Origin 检查:只接受 chrome-extension:// 请求
- 自定义 Header:要求 X-OpenCLI(浏览器无法跨域发送)
- 无 CORS:防止网页直接调用
- Body 大小限制:防止 OOM 攻击
内部结构图:
┌─────────────────────────────────────────────────────────────────────────┐
│ Daemon (daemon.ts) │
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ HTTP Server (Node.js http.createServer) ││
│ │ 监听: 127.0.0.1:19825 (默认) ││
│ └─────────────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ HTTP 端点 ││
│ │ GET /ping ──┐ ││
│ │ GET /status │ 无 X-OpenCLI 要求 (健康检查) ││
│ │ GET /logs │ ││
│ │ DELETE /logs ──┘ ││
│ │ POST /command ──┐ ││
│ │ POST /shutdown │ 要求 X-OpenCLI header ││
│ │ OPTIONS * ──┘ (preflight 返回 204, 无 CORS) ││
│ └─────────────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ WebSocket Server (ws library) ││
│ │ 路径: /ext ││
│ │ verifyClient: 检查 Origin 必须是 chrome-extension:// ││
│ └─────────────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ 内部状态 ││
│ │ extensionWs: WebSocket | null (扩展连接) ││
│ │ pending: Map<id, {resolve, reject, timer}> (等待中的命令) ││
│ │ logBuffer: LogEntry[] (日志环形缓冲区) ││
│ └─────────────────────────────────────────────────────────────────────┘│
└───────────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────┐
│ 命令流转: │
│ CLI → HTTP → Daemon → WS → │
│ Extension → 执行 → WS → │
│ Daemon → HTTP → CLI │
└───────────────────────────────┘关键端点:
GET /ping:健康检查(无 X-OpenCLI 要求)GET /status:Daemon 状态POST /command:执行浏览器命令POST /shutdown:关闭 Daemon
心跳与重连机制:
- Extension 每 15s 发送 ping
- 错过 2 个 pong 则认为连接丢失
- 关闭连接,清理 pending 命令
5. 关键数据流程
5.1 浏览器命令执行流程
场景说明:用户执行 opencli browser open https://example.com
流转时序图 (Mermaid):
5.2 网站适配器执行流程
场景说明:用户执行 opencli bilibili hot --limit 5,这是典型的适配器使用流程。
流转时序图 (Mermaid):
6. 接口与契约规范
6.1 核心内部模块契约 (TypeScript)
IPage - 浏览器页面抽象:
IPage - 浏览器页面抽象
/**
* Page interface: type-safe abstraction over browser page.
* All pipeline steps and CLI adapters should use this interface.
*/
export interface IPage {
goto(url: string, options?: { waitUntil?: 'load' | 'none'; settleMs?: number }): Promise<void>;
evaluate(js: string): Promise<any>;
evaluateWithArgs?(js: string, args: Record<string, unknown>): Promise<any>;
getCookies(opts?: { domain?: string; url?: string }): Promise<BrowserCookie[]>;
snapshot(opts?: SnapshotOptions): Promise<any>;
click(ref: string, opts?: { nth?: number; firstOnMulti?: boolean }): Promise<{
matches_n: number;
match_level: 'exact' | 'stable' | 'reidentified';
}>;
typeText(ref: string, text: string, opts?: { nth?: number; firstOnMulti?: boolean }): Promise<{
matches_n: number;
match_level: 'exact' | 'stable' | 'reidentified';
}>;
pressKey(key: string): Promise<void>;
scrollTo(ref: string, opts?: { nth?: number; firstOnMulti?: boolean }): Promise<any>;
getFormState(): Promise<any>;
wait(options: number | WaitOptions): Promise<void>;
tabs(): Promise<any>;
closeTab?(target?: number | string): Promise<void>;
newTab?(url?: string): Promise<string | undefined>;
selectTab(target: number | string): Promise<void>;
networkRequests(includeStatic?: boolean): Promise<any>;
consoleMessages(level?: string): Promise<any>;
scroll(direction?: string, amount?: number): Promise<void>;
autoScroll(options?: { times?: number; delayMs?: number }): Promise<void>;
installInterceptor(pattern: string): Promise<void>;
getInterceptedRequests(): Promise<any[]>;
waitForCapture(timeout?: number): Promise<void>;
screenshot(options?: ScreenshotOptions): Promise<string>;
startNetworkCapture?(pattern?: string): Promise<boolean>;
readNetworkCapture?(): Promise<unknown[]>;
setFileInput?(files: string[], selector?: string): Promise<void>;
insertText?(text: string): Promise<void>;
closeWindow?(): Promise<void>;
getCurrentUrl?(): Promise<string | null>;
getActivePage?(): string | undefined;
setActivePage?(page?: string): void;
cdp?(method: string, params?: Record<string, unknown>): Promise<unknown>;
frames?(): Promise<Array<{ index: number; frameId: string; url: string; name: string }>>;
evaluateInFrame?(js: string, frameIndex: number): Promise<unknown>;
nativeClick?(x: number, y: number): Promise<void>;
nativeType?(text: string): Promise<void>;
nativeKeyPress?(key: string, modifiers?: string[]): Promise<void>;
}CliCommand - 命令定义:
/**
* Core command definition: describes a CLI adapter or built-in command.
*/
export enum Strategy {
PUBLIC = 'public',
LOCAL = 'local',
COOKIE = 'cookie',
HEADER = 'header',
INTERCEPT = 'intercept',
UI = 'ui',
}
export interface Arg {
name: string;
type?: string;
default?: unknown;
required?: boolean;
valueRequired?: boolean;
positional?: boolean;
help?: string;
choices?: string[];
}
export interface CliCommand {
site: string;
name: string;
aliases?: string[];
description: string;
domain?: string;
strategy?: Strategy;
browser?: boolean;
args: Arg[];
columns?: string[];
func?: (page: IPage, kwargs: CommandArgs, debug?: boolean) => Promise<unknown>;
pipeline?: Record<string, unknown>[];
timeoutSeconds?: number;
source?: string; // 'yaml', 'ts', or plugin name
footerExtra?: (kwargs: CommandArgs) => string | undefined;
requiredEnv?: RequiredEnv[];
validateArgs?: (kwargs: CommandArgs) => void;
deprecated?: boolean | string;
replacedBy?: string;
navigateBefore?: boolean | string;
defaultFormat?: 'table' | 'plain' | 'json' | 'yaml' | 'csv';
}6.2 Daemon HTTP API (OpenSpec 格式)
关键端点:
POST /command:执行浏览器命令(需要 X-OpenCLI header)GET /status:获取 Daemon 状态GET /ping:健康检查
openapi: 3.0.0
info:
title: OpenCLI Daemon API
version: 1.0.0
servers:
- url: http://127.0.0.1:19825
paths:
/ping:
get:
summary: 健康检查(无 X-OpenCLI 要求)
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
/status:
get:
summary: Daemon 状态
parameters:
- name: X-OpenCLI
in: header
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
pid:
type: integer
uptime:
type: number
daemonVersion:
type: string
extensionConnected:
type: boolean
extensionVersion:
type: string
pending:
type: integer
memoryMB:
type: number
port:
type: integer
/logs:
get:
summary: 获取日志
parameters:
- name: X-OpenCLI
in: header
required: true
schema:
type: string
- name: level
in: query
schema:
type: string
responses:
'200':
description: OK
delete:
summary: 清空日志
parameters:
- name: X-OpenCLI
in: header
required: true
schema:
type: string
responses:
'200':
description: OK
/command:
post:
summary: 执行浏览器命令
parameters:
- name: X-OpenCLI
in: header
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id:
type: string
cmd:
type: string
url:
type: string
timeout:
type: number
responses:
'200':
description: OK
'400':
description: Bad Request
'403':
description: Forbidden
'408':
description: Request Timeout
'503':
description: Extension Not Connected
/shutdown:
post:
summary: 关闭 daemon
parameters:
- name: X-OpenCLI
in: header
required: true
schema:
type: string
responses:
'200':
description: OK7. 快速开始
7.1 环境配置
- Node.js >= 21.0.0
- Chrome/Chromium 浏览器
- OpenCLI Browser Bridge 扩展
7.2 安装与运行
npm install -g @jackwener/opencli
opencli doctor # 检查安装
opencli list # 查看所有命令7.3 典型用例
# 浏览网站
opencli browser open https://example.com
# 使用预置适配器
opencli bilibili hot --limit 5
opencli hackernews top
opencli reddit hot
# 管理外部 CLI
opencli register mycli
opencli gh pr list