opencli Checked
Installation
Copy the prompt below and send it to your AI assistant (e.g. Claude Code) — it will follow the instructions and install automatically.
Install the "opencli" skill by following the instructions at https://skill123.me/install/opencli.
Run in your terminal — downloads and installs to ~/.claude/skills/.
curl -fsSL https://skill123.me/install/opencli.sh | bash
Download the zip and extract it into your skills directory (e.g. ~/.claude/skills/), then restart your session.
⬇ Download v1.0.ff9c824 · 151 KBNot live-tested yet — this skill is rated on static analysis only.
About this skill
用 OpenCLI 驱动用户本机那个真实的、已登录的 Chrome,或调用它的 160+ 站点 adapter。任何需要登录态的页面操作都从这里开始——读登录后的后台、抓没有 API 的表格、填表提交、跑一个站点命令、把页面数据取回来。也覆盖会话命名与租约纪律("我的标签页被别人抢了")、批量取数与落盘、adapter 的编写与自修复、opencli doctor 排障。用户提到 opencli、浏览器自动化、用我的浏览器、驱动 Chrome、登录态、抓后台数据、抓表格、导出报表、填表、自动点击、截图、adapter、doctor 报错、session 撞名、标签页被抢、tab 泄漏,或说"打开这个页面看看""帮我登录后台查一下""这个站没有 API"时,务必使用本 Skill。也在需要判断"这件事该不该开浏览器"时使用——本 Skill 第零节就是那张判断表(要不要登录态、有没有现成脚本或 adapter、配额站能不能动手、什么时候该转给 agent-reach 或业务 Skill)。只要动作会落在浏览器上,先读这里再动手。
OpenCLI
OpenCLI 把任意网站、Electron 桌面应用和外部 CLI 收敛成一条 opencli <site> <command>,
再加一条 opencli browser <session> <command> 用来现场驱动浏览器。
它走的是**用户本机那个真实的、已登录的 Chrome**(浏览器扩展 + 本地守护进程),
不是无痕实例、不是沙箱。这一个事实决定了本 Skill 里几乎所有规则。
本 Skill 面向的是我们自己维护的 fork(yan-labs/OpenCLI),和上游 jackwener/opencli
有差异,差异清单见 references/our-fork.md (references/our-fork.md)。
---
Documentation
用 OpenCLI 驱动用户本机那个真实的、已登录的 Chrome,或调用它的 160+ 站点 adapter。任何需要登录态的页面操作都从这里开始——读登录后的后台、抓没有 API 的表格、填表提交、跑一个站点命令、把页面数据取回来。也覆盖会话命名与租约纪律("我的标签页被别人抢了")、批量取数与落盘、adapter 的编写与自修复、opencli doctor 排障。用户提到 opencli、浏览器自动化、用我的浏览器、驱动 Chrome、登录态、抓后台数据、抓表格、导出报表、填表、自动点击、截图、adapter、doctor 报错、session 撞名、标签页被抢、tab 泄漏,或说"打开这个页面看看""帮我登录后台查一下""这个站没有 API"时,务必使用本 Skill。也在需要判断"这件事该不该开浏览器"时使用——本 Skill 第零节就是那张判断表(要不要登录态、有没有现成脚本或 adapter、配额站能不能动手、什么时候该转给 agent-reach 或业务 Skill)。只要动作会落在浏览器上,先读这里再动手。
OpenCLI
OpenCLI 把任意网站、Electron 桌面应用和外部 CLI 收敛成一条 opencli <site> <command>,
再加一条 opencli browser <session> <command> 用来现场驱动浏览器。
它走的是**用户本机那个真实的、已登录的 Chrome**(浏览器扩展 + 本地守护进程),
不是无痕实例、不是沙箱。这一个事实决定了本 Skill 里几乎所有规则。
本 Skill 面向的是我们自己维护的 fork(yan-labs/OpenCLI),和上游 jackwener/opencli
有差异,差异清单见 references/our-fork.md (references/our-fork.md)。
---
零、我遇到这种情况,该不该用 OpenCLI
本 Skill 是**底层能力**,不是业务流程。用户不会说「用 OpenCLI」,他会说下面左边那些话。
这张表回答的是「这句话该不该落到浏览器上」——**判错方向的代价是拿到看起来正常、
内容却不同的数据**,比慢一轮贵得多。
| 用户大概会这么说 | 该走哪条 | 为什么 |
|---|---|---|
| 「帮我登录后台查一下」「看我的 GSC / 数据面板」 | **用**(opencli browser) | 需要身份。沙箱浏览器要么跳登录页,要么以匿名身份返回**更少的字段、更低的配额** |
| 「这个站没有 API,把表格给我」 | **用**,但先看第四节有没有 adapter | adapter 里封装过的坑,现场驱动要重踩一遍 |
| 「填一下这个表单」「帮我提交」 | **用**,但提交动作归业务 Skill 管 | 本 Skill 只负责把浏览器开对;能不能按提交见 backlink 的三道闸 |
| 「打开这个页面看看写了什么」(公开页) | **先不用** | 先问有没有 curl / 公开 API。只为读一段公开文本开浏览器是浪费 |
| 「帮我调研一下 X」「搜搜大家怎么说」 | **不用** → agent-reach | 它已经做好多平台路由。本 Skill 不做「找信息」,只做「把数据取回来」 |
| 「查这个词的搜索量 / 难度」「看看竞品外链」 | **不用直接开浏览器** → 先进 rankup / backlink | 那两个 Skill 里已经有现成脚本,直接跑;现写等价实现是本阶梯第 1 级明令禁止的 |
| 「Semrush / Similarweb 上帮我看个数」 | **用,但先读配额纪律** | 见下面「配额站」一节:固定会话名 + 整轮持机器级工具锁。先跑 node <opencli-skill-dir>/scripts/pressure.mjs --tool semrush |
| 「开十个 agent 一起抓」 | **不要** | 扇出的单位是 agent,资源却是标签页。采集落盘(scripts/receiver.mjs),N 个 agent 读文件,站点侧并发度 0 |
| 「我的标签页被别人抢了」「读回来的页面不对」 | **用本 Skill 排障** | 先怀疑会话撞名,见第三节四条法律 |
| 「浏览器连不上 / doctor 报红 / 命令行为和文档不符」 | **用本 Skill 排障** | 第二节:先看扩展版本,商店版会让每条规则都对不上 |
| 「过一下验证码」 | **半自动** | 把前面全部做完,只把那一下点击留给用户,见第八节 |
**一句话判据**:*无痕窗口打开它,还是不是同一个东西?* 不是 → 必须走用户真实的
Chrome(也就是本 Skill);是 → 先找 API 或现成脚本。
---
一、先判断:这件事该不该用浏览器
**动手之前先走这条阶梯,命中即停。** 每一级往下的唯一理由是「上一级确实不存在」,
不是「我对下一级更熟」。跳级的代价不是慢,是拿到看起来正常但内容不同的数据。
| 级 | 手段 | 什么时候用 |
|---|---|---|
| 1 | **现成脚本** | 项目里、兄弟 Skill 里已经有的 .mjs。直接跑,不要现写等价实现 |
| 2 | **HTTP / REST API**(curl / fetch) | 没脚本但服务有 API。先用 API,跑通后固化成脚本 |
| 3 | **opencli <site> <command> adapter** | 目标站已有 adapter。opencli list \| grep -i <site> 一眼就知道 |
| 4 | **opencli browser <session> 现场驱动** | 没有 adapter,或 adapter 不覆盖这个动作 |
| 5 | 写一个新 adapter | 这个动作以后还要重复做。见 references/adapters.md (references/adapters.md) |
### 判据:无痕窗口打开,还是不是同一个东西?
答案是「不是」,就**必须**走用户的真实浏览器(也就是 OpenCLI)。
需要身份的一切——第三方数据面板、Search Console、社区后台、聊天式 AI 工具——
用运行环境自带的沙箱浏览器打开,要么直接跳登录页,要么以匿名身份返回**看起来正常
但内容不同**的结果(配额更低、字段更少、国家库不同)。这种失败会伪装成
「这个工具没有这项数据」,而正确的结论其实是「你没登录」。
反过来,**只是看一段公开文本就不要开浏览器**——先问有没有 curl 或公开 API。
View full documentation
三个 driver 的取舍(为什么默认是 OpenCLI 而不是 agent-browser 或 Claude in Chrome,
各自的实测泄漏数据)见 references/drivers.md (references/drivers.md)。
### 不在本 Skill 范围
- **找信息、做调研、搜某个话题** → 用
agent-reach,它已经负责多平台路由。
本 Skill 只管「怎么把浏览器开对、把数据取回来」。
---
二、开工前:doctor
opencli doctor
doctor 只诊断**浏览器桥**(守护进程 + 扩展 + Chrome 连线)。
PUBLIC / LOCAL 策略的 adapter、opencli list、外部 CLI 透传都不需要它绿。
COOKIE / INTERCEPT / UI 策略和所有 opencli browser * 才需要。
### 行为和这份文档对不上时,第一件事是查扩展版本
**本 Skill 描述的默认行为全部住在扩展里**——后台默认、opencli browser 与 adapter 命令
都在用户当前窗口开标签页、不切走活动标签页、每个会话一个以会话名命名的标签页组、
--window isolated、sessions 报 windowId / groupTitle / windowFallbackReason。
装成 Chrome 应用商店那个版本的话,**每条命令都照样成功,只是行为回到上游**:
默认前台、自己开一个窗口、抢走用户正在看的标签页、isolated 被忽略。
**这类失败没有报错,只有「怎么和文档说的不一样」。** 所以:
| 观察到 | 该做什么 |
|---|---|
| 命令成功但窗口/焦点行为与本文档不符 | 跑 opencli doctor,看 Extension 那行的版本 |
| 版本 < 1.0.33 | **告诉用户他装的是应用商店版**,需要换成 yan-labs 的 Release 里的 zip,并把商店版移除或停用 |
| doctor 自己就报了这条 | 照它说的做——它会打印下载地址和加载步骤 |
doctor 会在扩展低于 1.0.33 时主动报这个问题,**不要跳过它的输出**。
改过扩展源码(或刚拉了新构建)之后要在 chrome://extensions 里对 OpenCLI 点 **reload**——
没 reload 时 Chrome 跑的仍是旧版,doctor 会提示已加载版本低于最低要求,那不是装错,是没 reload。
红了先看 references/troubleshooting.md (references/troubleshooting.md)。
排障的第一步永远是 **npm ls -g @jackwener/opencli 确认 CLI 是发布版还是本地源码 link**——
这一步决定后面是查代码还是查环境,跳过它会浪费一整轮。
**doctor 前两行绿、第三行红**是一个特定信号:守护进程和扩展这两个组件都活着,
坏的是它们之间那条命令路径,重启守护进程通常没用。
---
三、会话纪律:本 Skill 最贵的一节
opencli browser <session> 里的 <session> **就是标签页的所有权声明**。
同名会话共用同一个标签页,不同名之间互不干扰。所以「我的标签页被别人抢了」
最常见的成因是:**两个任务挑了同一个会话名**。
OpenCLI 1.8.7 的守护进程会保护同一 profile + surface + session:第二个**并发写**
会留在本机排队,每 2 秒检查一次;前一个任务结束后自动继续,不把 session_busy 交给
外层 Agent,避免它立即重试。排队检查只访问本机 daemon,不会访问目标网站;首次等待会
明确打印占用者、等待原因和下次检查时间。默认最多等 10 分钟,超时会说明命令尚未发往
Chrome/目标网站,并要求不要立即重试。读操作仍可并行;含任一写操作的混合 batch
整体按写处理。
这只串行化同一时刻的写入。两个任务顺序或交替复用同名会话,仍会操作同一个标签页,
随后读到对方打开的页面,所以唯一会话名规则不变。
这把锁也不管站点账号的并发与限速。数据源脚本若同一账号不能并发,仍要自己加全局锁。
### 四条法律(完整实测数据见 references/session-laws.md (references/session-laws.md))
| # | 法律 | 一句话理由 |
|---|---|---|
| 1 | **一个会话一个标签页;N 个页面就要 N 个会话名** | 三个 agent 各用独立名字:跨 agent 抢占 0 次。共用 work:3 / 12 / 2 次,其中一个每次读都读错。**唯一例外是配额站,见下一节** |
| 2 | **不要用 tab new / tab select / open --tab 在一个会话里放多个页面** | 三个都**静默**失败:命令报成功,下一次读回错误的页面。一次三 agent 运行把用户的 Chrome 从 11 个标签页涨到 30 个孤儿页 |
| 3 | **绝不硬编码会话名** | opencli browser --help 的第一个例子就是 work,抄它的人全撞在一起 |
| 4 | **开工前一次性把要用的会话全部开好、handle 全部拿到,再进工作循环** | 边创建边使用会把理论上的竞态变成可复现的竞态 |
**法律 1 保护的是标签页身份,不是站点的服务端状态。** 所有会话共用同一个 Chrome
profile 和同一个登录身份,所以如果站点把「当前选中的项目/客户」存在服务端会话里,
一个标签页切换目标,其它标签页刷新后会跟着变——会话名分得再开也拦不住。
**判据:在站点里切换目标之后 URL 变不变?** 不变就先验证再并行,
细节见 references/session-laws.md (references/session-laws.md)。
### 配额站:法律 1 的唯一例外
有些站**同时加载**会触发上限。实测(2026-08-28)Semrush 大约 3 个标签页同时 load
就出问题,一个个开、中间隔几秒则没事。**受限的是导航事件,不是标签页存在**——
所以它要的不是信号量,是串行加间隔。
而串行 daemon 已经免费给了:同名会话的写会在本机排队。于是配额站的解法是把法律 1
反过来用——**一个站一个固定会话名,不带任何 per-agent 后缀**:
semrush-nav similarweb-nav
十个 agent 拿到同一个名字,daemon 就把它们排成一队,Semrush 那边永远只看到
一个标签页在一页页地翻。
**动手前先跑 pressure.mjs:**
node <opencli-skill-dir>/scripts/pressure.mjs --tool semrush
它一句话回答「现在动手会不会把事情搞砸」——配额站已经几个标签页、到没到线、
tools-share 锁被谁拿着、那个 pid 还活着吗,然后给 go / wait / stale-lock。
退出码可以直接串起来:node pressure.mjs --tool semrush && node my-crawler.mjs。
**它报 unknown 时不要当成「没人在用」**——那是「会话列表拿不到」,不是「0 个标签页」。
| 规则 | 为什么 |
|---|---|
| **配额站用固定会话名**,sessionForUrl(url, base) 自动判 | 会话名就是并发度。名字固定 = 并发度 1 |
| **一次访问 = 一个 batch**(openAndExtract) | 「含任一写操作的混合 batch 整体按写处理」,所以整包是原子的,别人插不进来——这正是共用名字仍然安全的原因 |
| **禁止 open 一次隔几轮对话再读** | 会话一直占着,后面全在排队。实测 daemon.log 一天 1016 条 busy 轮询 |
| **采集写成顺序循环**(sequentialCrawl),不要扇出 | 排队是兜底不是调度器:daemon 默认只等 10 分钟,20 个词顺序跑就快贴到上限 |
| **间隔用 sleepStep(),不要用 wait time** | wait time 5 在 1.8.7 是坏的:报 "Waited 5s",实测 928ms 就返回。写错了整套节流静默失效 |
| **撞上限的第一动作是 close,不是 sleep** | 释放标签页本身就是退避。当成「页面没加载好」去重试只会再开一个,越retry越糟 |
**daemon 排队只串行化单条命令 / 单个 batch,保护不了跨多条命令的整轮采集。**
同名会话排队意味着两条命令之间的间隙对别人是敞开的:一轮横跨几十条命令的采集
(poll → 截图 → 滚动循环),任何 poll 间隙里别的工作流都能往同一个固定名标签页
open 自己的 URL。实测 2026-08-29 一天抓到 4 次现行接管。所以整轮采集必须**另持
机器级工具锁**(yan-tools-share-<tool>.lock,pressure.mjs 报告的就是它),
整轮持有、结束释放——「排队所以安全」只对单条 batch 成立。完整法律与参考实现见
backlink Skill 的 one-collector-per-quota-tool(backlink/SKILL.md,实现在
backlink/scripts/ground-truth.mjs)。
**分析阶段一律不碰配额站。** 采集落盘(scripts/receiver.mjs),N 个分析 agent 读文件,
站点侧并发度是 0。这是唯一能让 agent 数量和站点压力彻底解耦的做法——今天那
19 个 tm-* 标签页全开在同一个 Semrush 报表上,就是因为扇出的单位是 agent 而资源是页面。
**导航超时不等于页面没开。** 扩展硬编码 15 秒且改不了,Semrush 的重报表经常超。
标签页那时已经建好了,正确反应是先 extract 探活,确认真没内容才在**同一个会话里**
重新导航。openAndExtract 已经这么做了;手写的话千万别开新会话去重试。
> Semrush / Similarweb **一个 adapter 都没有**(opencli list 里 0 条),所以每次取数
> 都必须开真标签页。想从根上删掉这个问题,就得给最高频的几个报表写 COOKIE/INTERCEPT
> adapter——从日志看是 analytics/overview、keywordoverview、keywordmagic
> (也正好是超时最多的三个:9 / 8 / 5 次)。
### $$ 在脚本里安全,在 Bash tool 里不安全
这是我们踩过的真实事故,必须区分:
| 场景 | $$ / process.pid 行为 | 正确做法 |
|---|---|---|
| **Node 脚本**(一个进程跑完全程) | 整个生命周期同一个 PID,安全 | ` let session = ahs-${process.pid} ` |
| **Claude Code 的 Bash tool** | **每次调用都是新进程,PID 不同** | 用**描述性字面常量**(naver-birthstone、bing-check-mysite),或 S=$(uuidgen \| cut -c1-8) 存进文件再读回 |
已验证事故(2026-08-23):sub agent 用 S="naver-bs-$$" 连续调用 OpenCLI,
每条命令都创建了新会话(新空白标签页),上一条打开的页面被遗弃。
agent 看到的永远是空白页,以为页面没加载好不断重试,最终泄漏 9 个会话。
**名字要描述工作**,不只是唯一:backlink-probe-<后缀> 胜过 bl-1。
会话名是唯一存在的标识符,一个唯一但无意义的名字仍然回答不了「这是谁的标签页」。
JS 里不要手搓后缀,用 scripts/opencli-core.mjs 的 defaultSession(base);
**Bash 里 `sou
Score breakdown
Assessment methodology
Version history
— Refreshed from GitHub yan-labs/yan-skills@ff9c824
Imported from yan-labs/yan-skills@main