OpenCockpitOpenCockpit
参考

Cockpit CLI

Cockpit CLI 是运行中的 Cockpit 服务器 HTTP API 的一层薄包装。npm install -g @surething/cockpit 装两个等价的二进制:cockpit(完整名)和 cock(短别名)。服务器本身常驻;子命令通过 HTTP 调 localhost:3457 来查看或驱动你面板里开着的东西。

命令作用
cockpit启动服务器(主入口)
cockpit browser驱动浏览器气泡 —— 导航、点击、执行 JS、抓网络
cockpit terminal读终端气泡的输出(只读;无 stdin)
cockpit codegraph在 shell 里查项目级代码索引
cockpit connection列出所有气泡(终端 + 浏览器)及其标题
cockpit update升级到最新版本

cockpit 和 cock

cockpit 是主命令。短别名是 cock —— npm install -g @surething/cockpit 时两个都装。

用法

cockpit [path] [options]
cock    [path] [options]

两个命令完全等价,选你喜欢打的。

常见形式

在当前目录启动:

cockpit

在指定项目启动:

cockpit ~/code/my-project

启动但不自动开浏览器:

cockpit . --no-open

用别的端口启动:

cockpit . --port 4000

显示版本:

cockpit -v

选项

标志说明
-v--version打印版本并退出。
-h--help显示帮助。
--port <n>用非默认端口监听。默认 3457
--no-open启动后不自动开浏览器。
[path]打开的工作目录。默认 process.cwd()

默认端口

Cockpit 监听 3457。可以单次用 --port 覆盖,或写入 ~/.cockpit/server.json 永久生效:

{ "port": 4000 }

两者都设时单次标志优先。

子命令

cockpit 本身启动服务。两个子命令用于从外部脚本驱动 Cockpit(CI、ChatOps、自动化):

子命令用途
cockpit browser <id> <action>驱动正在跑的浏览器气泡(导航、点击、执行 JS、抓网络等)
cockpit terminal <id> <action>读正在跑的终端气泡(list / output / wait;只读)

两者都连 localhost:3457 上正在跑的 Cockpit 服务。这些是 UI 内部用的同一套 API;暴露为 CLI 让你能从任何地方脚本化驱动气泡。

详见:

  • cockpit browser —— 全部 action 列表(25+ 个:snapshot、click、type、network、perf 等)
  • cockpit terminal —— list / output / wait(只读)

升级

cockpit update

等同于 npm install -g @surething/cockpit@latest。升级时保留什么见 cockpit update

退出码

含义
0正常退出(服务干净停了)
1服务启动失败(端口被占、权限拒绝、缺 Node 等)
130Ctrl+C 杀掉(SIGINT

环境变量

变量效果
COCKPIT_PORT--port,被一些下游工具使用(/cg curl 片段等)。
PORTCOCKPIT_PORT 未设时的兜底。

cockpit browser

cockpit browser <id> <action>(或短名 cock browser)从外部驱动你正在跑的 Cockpit 里的浏览器气泡 —— 从聊天里的 AI、从 shell 脚本、从 CI、从任何地方。

<id> 是浏览器气泡标题栏上的短 ID 徽章。点徽章注册气泡,把入门命令复制到剪贴板。

CLI 是为 AI 驱动的 E2E 设计的:每个命令都带可执行的错误模板,沉默失败会被捕获并告警,act/wait/assert 循环能组合成原子步骤。优先用基于 selector 的交互(--text--selector),别依赖 snapshot 返回的 ref —— 任何重渲染都会让 ref 失效。

快速示例

# 诊断 AI 当前停在哪(轻量,永不阻塞)
cock browser xa7k2 health
cock browser xa7k2 status

# 找元素并交互(selector 优先;ref 易失效)
cock browser xa7k2 snapshot --filter 'role=button' --include-hidden-text
cock browser xa7k2 click --text "Sign in"
cock browser xa7k2 click --selector 'button[type="submit"]'
cock browser xa7k2 fill --selector 'input[name="email"]' --value "[email protected]"
cock browser xa7k2 submit --form-selector 'form#login'

# 探后端(继承页面登录态)
cock browser xa7k2 fetch /api/users/me
cock browser xa7k2 fetch /api/items --method POST --body '{"name":"hello"}'
cock browser xa7k2 fetch /api/items --json '$.data[0].id'

# act → wait → assert(E2E 原子步骤)
cock browser xa7k2 click --text "Save"
cock browser xa7k2 wait --network-idle --quiet-ms 500
cock browser xa7k2 assert --selector '[role="status"]' --text "Saved"
cock browser xa7k2 assert --fetch /api/items --jsonpath '$.count' --equals 5

# 测试隔离
cock browser xa7k2 reset --cookies --storage --reload
cock browser xa7k2 set --type cookie --name token --value abc123 --path /

# 跑任意 JS(逃生口 —— 能用 fetch/click-by-selector 就别用 evaluate)
cock browser xa7k2 evaluate "document.title"
cock browser xa7k2 evaluate --all-frames "await fetch('/api/x').then(r=>r.json())"

完整 action 列表

检查

Action做什么
list列出所有当前注册的浏览器气泡
snapshot [--filter <regex>] [--include-hidden-text] [--max-depth N]无障碍树。开头几行 banner 解释格式。--filter 在服务端 grep;--include-hidden-text<summary>、容器节点折叠的文本展开
screenshot页面 PNG,存到 /tmp,打印路径
url当前 URL
title页面标题
status一行摘要:URL、title、上次 console 错、上次失败请求、可见 top 按钮。隔了一段时间后用它定位
bounds <ref>元素位置和尺寸
attrs <ref>元素所有 HTML 属性
computed <ref>元素计算后的 CSS
events <ref>元素绑定的事件监听器
cookies页面所有 cookie
storage --type local|sessionlocalStorage 或 sessionStorage 内容
theme --mode dark|light强制气泡主题

交互

基于 selector 的形式更稳 —— ref 只在 snapshot 之后到下次重渲染之前有效。

Action做什么
click [<ref>] [--text <substr>] [--selector <css>] [--nth N] [--exact]按 ref、可见文本 / aria-label、或 CSS selector 点击。Ref 重渲染就失效;selector 和 --text 不会
fill [<ref>] [--selector <css>] --value <v>用 native setter 写值 + 派 input 事件(在 React 受控 input 上有效)
type <ref> <text>通过 CDP 键盘事件往聚焦的 input 里打字。React 受控 input 上可能静默无效 —— 优先用 fill --selector
submit [--form-selector <css>]form.requestSubmit()。在 onKeyDown 无视 Enter 的场景能工作
hover <ref>悬停元素
focus <ref>聚焦元素
scroll --direction up|down|left|right滚动页面
key <key>按键(EnterCtrl+AShift+Tab …)。React input 上优先用 submit

等待(act 与 assert 之间的同步原语)

Action做什么
wait --network-idle [--quiet-ms 500] [--max-request-age-ms 30000]等到 in-flight HTTP 请求为 0 持续 quiet-ms。超过 max-request-age-ms 的长连接(SSE / long-poll)会被排除
wait --selector <css> [--state visible|hidden|attached|detached]等元素到指定状态。默认 visible
wait --dom-stable [--quiet-ms 300]MutationObserverquiet-ms 内无 DOM 变化(在 act 后拍 snapshot 前用)
wait --extension-ready [--quiet-ms 500]CLI 侧轮询 health不进 page,所以 page 卡时不会被拖死。替代手写 until evaluate "1+1"
wait --text <substr> / --url <pat> / --ref <ref> / --time <ms>经典条件:文本出现、URL 匹配、ref 仍存在、sleep

断言(失败时非零退出)

Action做什么
assert --selector <css> [--text <substr>] [--visible <bool>] [--attr "k=v"]元素级断言。--ref 仍可用但易失效;selector 更稳
assert --network --method <M> --url <pat> --status <S> [--since <ms>]断言 networkBuffer 里有一条匹配请求。--status 接整数(200)或范围(2xx
assert --fetch <url> [--fetch-method M] [--body B] [--fetch-status N] [--jsonpath <P> --equals V | --contains V | --not-contains V]发一个 fetch(继承 page auth)然后断言状态码或 JSONPath 值。JSONPath 子集:$.key[N][*]
assert --url <pat> / --title <substr> / --console-no-errors页面级断言

导航

Action做什么
navigate --url <url>前往 URL
reload [--noCache]刷新(可选跳过缓存)
back后退一格历史
forward前进一格历史

后端探查

Action做什么
fetch <url> [--method <M>] [--body <B>] [--headers <JSON>] [--json <jsonpath>]用 page 的登录态发请求。--json 用与 assert --fetch 同样的 JSONPath 子集提取值。返回 { status, contentType, data }{ status, jsonpath, value }

AI 读 / 改后端推荐用 fetch —— 比把 await fetch(...).then(r => r.json()) 裹进 evaluate 清晰。

JavaScript

Action做什么
evaluate <js>在页面里跑 JS 表达式;结果以 JSON 打印。--all-frames 在每个 iframe 里跑。大结果会被透明地分片传回 CLI

evaluate 当逃生口用 —— 高层 action 不够用时才上。普通 HTTP 调用用 fetch;点击用 click --text / --selector;断言用 assert --selector

生命周期 / 夹具

Action做什么
reset [--cookies] [--storage] [--cache] [--reload]原子化测试隔离。按需组合 flag。--cookies 让 JS 可见的 cookie 过期;--storagelocalStoragesessionStorage--cache 删 Cache Storage 条目;--reload 清完刷新
set --type cookie --name <K> --value <V> [--domain <D>] [--path <P>] [--secure] [--same-site Lax|Strict|None] [--expires <date>]写一个 JS 可见的 cookie。若浏览器拒收(跨域 / SameSite 不符 / HTTP 下 Secure),返回 verified: false
set --type local-storage --name <K> --value <V>localStorage
set --type session-storage --name <K> --value <V>sessionStorage

诊断

Action做什么
health服务端视角的桥接快照:WS 状态、pending 命令数、距上次成功命令的耗时。不经 page,所以即使页面卡在长 evaluate 上也工作
health --deep进 page 再探一次(readyStatesnapshotEpoch、page 时间戳)。page 卡时会一起卡

网络抓取

Action做什么
network [--status <code>] [--method <method>] [--type <type>] [--clear]列出抓到的请求,带过滤。--status4xx,5xx 这种逗号分隔
network_record start [--url <pat>] [--method <m>] [--status <code>]开始录制请求 / 响应 body
network_record stop停止录制
network_record status录制开了没
network_detail <reqId>某个请求的完整 request / response
console [--level error|warn|info|debug] [--clear]控制台消息
perf [--metric timing|memory|resources]性能指标,含 Core Web Vitals

snapshot 输出

snapshot 返回纯文本无障碍树。前 4-5 行是 banner,解释格式并标注当前 snapshot 版本v=N):

# a11y tree v=3 — refs valid until next snapshot
# Text inside <details>/<summary> and unnamed container <div>/<section> is collapsed.
# Grep on role / aria-label, NOT on user-visible emoji / text.
# Tips: --include-hidden-text surfaces collapsed innerText; --filter <regex> reduces output.
body [e0#v3]
  ...

每个可定位元素带形如 e5#v3 的 ref。#v3 后缀是 snapshot 的 epoch —— 来自更早 snapshot 的 ref 会被拒绝,错误信息直接指向 click --text / click --selector 这种重渲染安全的写法。大多数 AI 流程不需要碰 ref,直接用 --text / --selector 就够了。

click / key / submit 的 post-verify

这三个 action 最容易 沉默失败 —— CDP 报 "成功" 但框架没真反应(React 受控 input 忽略合成 keydown、portal 渲染的按钮没有真 handler 等)。CLI 会在 action 前后悄悄探一次页面状态;如果 verify 窗口内没有任何可观察的变化,就往 stderr 写警告 + 可执行模板(不影响 stdout 的主结果)。

Flag做什么
--verify-ms <ms>覆盖 verify 窗口。默认 1000 毫秒。调小 = 更快但 React 慢渲染上更容易误报;调大 = 更宽容
--skip-verify(或 --no-verify关闭本次命令的 post-verify(比如确认无可见副作用的合法 click)

输出格式

大多数 action 在 stdout 返回 JSON —— 容易管道到 jqgron,或 AI 读取。urltitlenetwork_detail 返回纯文本。screenshot 返回文件路径。snapshot 返回 banner + 纯文本树。healthstatuswaitassert 输出一行人类友好摘要。fetch 先打印 [status] (contentType) 再打印 body,或 [status] $.jsonpath = 再打印提取值。

警告(沉默失败检测、cookie 被拒收等)都写 stderr。主结果留在 stdout,所以 > 重定向能抓真数据同时让 AI 看到警告。

退出码

成功 0,失败非零(ref 失效、网络错、断言失败、selector 无匹配 …)。完整退出码列表见 主 CLI 页

何时不该用这个 CLI

  • 测 LLM agent 驱动的端到端流程。agent 的随机工具选择和 stop_reason 会让 UI 断言不稳定。优先写一个直接调中间件 / 服务的运行时脚本,用受控输入跑。
  • 页面持续 stream / 重渲染超过 10 秒evaluate 会被 page 工作排队,可能超时(~15 秒默认)。act 与 assert 之间用 wait --extension-ready;如果还是挂,切到服务层测试。
  • 多 tab / OAuth 弹窗。每个浏览器气泡只跟一个 tab。把第二个 tab 开成独立气泡,或把 OAuth 流 stub 掉。

cockpit terminal

cockpit terminal <id> <action>(或 cock terminal)从外部读取终端气泡的输出 —— 拿缓冲输出、等命令收尾、列出已注册的气泡。

注意:终端 CLI 故意设计成只读没有 stdin没有 follow 流式跟随 —— 想驱动 shell 用浏览器气泡 + cock browser,或在 Cockpit UI 里跟气泡直接交互。代码注释:"read-only by design; write side belongs to the Bash tool / web UI"。

<id> 是终端气泡标题栏的短 ID 徽章。点徽章注册气泡,把入门命令复制到剪贴板。

完整 action 列表

Action做什么
list列出所有当前注册的终端气泡。显示状态(running / idle)和各自的命令。
output打印终端的完整缓冲输出 —— 从气泡启动起的完整历史。
wait阻塞等当前运行的命令收尾后返回,适合脚本里"等 npm run build 跑完再继续"的场景。

快速示例

# 找你的气泡
cock terminal list

# 抓当前屏幕内容
cock terminal xy789 output

# 等 npm run build 跑完
cock terminal xy789 wait

# 跑完后看结果
cock terminal xy789 output | tail -50

什么时候用

主要场景:

  • AI 通过气泡读你 shell 在跑什么。 在 Cockpit 终端气泡里启动 npm run dev。聊天里把 cock terminal <id> 给 AI,它可以 output 拿最近日志、wait 等命令收尾。
  • CI / 脚本里从外部观察 Cockpit 里的长跑命令。 比如启动器脚本在 Cockpit 终端跑 npm run dev,然后另一个脚本 cock terminal <id> output 抓日志做断言。

限制

终端 CLI 跟浏览器 CLI 比有意做轻:

  • 没有写 stdin 的能力。 不能远程给终端进程发命令。
  • 没有带结构化选择器的屏幕抓取(output 给你原始 buffer,自己解析)。
  • 没有窗口大小调整 / 信号发送 / Ctrl+C 中断等 action。

要完整的交互式控制(Ctrl+C、打字执行命令等),直接在 Cockpit UI 里跟气泡交互。

cockpit codegraph

cockpit codegraph 是产品内 CodeGraph 功能(/cg 模式)背后那张项目级代码索引的 shell 入口。同一份索引、同一份 API,但跑在终端里,方便接进脚本、CI、Unix pipeline。

使用前 Cockpit 服务器需要在跑(CLI 通过本机 HTTP 端口调用)。

两类子命令

Lookups(查坐标) —— 只回文件路径 + 行号,对齐产品内 API:

子命令作用
search <query>按名字搜符号,返回 file + qname 命中。
callers <qname> [--file PATH]谁直接调了这个符号。
callees <qname> [--file PATH]这个符号调了什么。
impact <qname> [--depth N=2]上游传递性调用者,BFS。需要排序版本用 risk
file <path>一个文件的符号树(函数 / 类)。
coedit <path> [--commits N=100]git 历史里和它一起被改的文件。

Analytics(融合打分) —— PPR / TF-IDF / Louvain 社区 / co-edit 混合排序:

子命令作用
context --query Q [--cursor C] [--open F1,F2,…] [--top N=15]针对自由问句,给出 Top-K 相关坐标。
related <qname> [--top N=10]更宽的邻居:caller + callee + PPR + coedit + community。
risk <qname> [--depth N=2] [--top N=20]风险打分的 impact + 推荐测试。
affected <files…|--stdin> [--depth N=10] [--filter G] [--as-cmd RUNNER]传递性受影响的测试文件,CI 友好。

通用 flag

--json             # 输出原始 JSON(完整结构;每个子命令的 --help 看 schema)
--help, -h         # 子命令级帮助(输出格式 + 退出码 + 示例)

输出格式(纯文本)

TAB 分隔,每行一条,方便 cut / awk / fzf 接管。

search    sym\t<file>:<line>\t<kind>\t<qname>            或  file\t<file>
callers   <file>:<line>\t<qname>\t[<callLines>]
callees   <file>:<line>\t<qname>\t[<callLines>]
impact    d=<depth>\t<file>:<line>\t<qname>
file      <kind>\t<startLine>-<endLine>\t<qname>
coedit    <cooccur>/<total>\t<file>                  # 出现在 '# history' 注释之后
context   <score>\t<file>:<line>\t<qname>\t[<signals>]
related   <score>\t<file>:<line>\t<qname>\t<<relations>>
risk      <score>\td=<depth>\t<file>:<line>\t<qname>\t[<tags>]
affected  <file>                                     # 每行一个测试路径

需要结构化结果就加 --json

stderr 上的诊断信息

设计上不打断 pipeline:

# ambiguousIn: <files…>     同名 qname 出现在多个文件 —— 用 --file 限定
# cursor: <note>            cursor 格式自动纠正('.' → '::' 等)
# degraded: <reason>        analytics-warming / coedit-unavailable / truncated

退出码

含义
0有输出
1空结果(无 caller / 无测试 / 无命中)—— 配合 shell pipeline 短路
2参数错误 或 服务端 4xx
3Cockpit 服务器无法连接。用 cock <项目路径> 把它拉起。

前置条件

默认连接 http://localhost:3457(跟主 Cockpit 服务同一个端口,不是单独的 codegraph 端口)。如需换地址:

COCKPIT_HOST=… COCKPIT_PORT=… cock codegraph …

示例

cock codegraph search getCodeIndex
cock codegraph related getCodeIndex --top 5
cock codegraph risk searchIndex --depth 2
# 列出本次 diff 影响的测试路径(一行一个):
git diff --name-only | cock codegraph affected --stdin
# 直接驱动 jest 跑这些测试:
git diff --name-only | cock codegraph affected --stdin --as-cmd jest
# 同样思路给 vitest:
git diff --name-only | cock codegraph affected --stdin --as-cmd "vitest run"

相关页面

  • CodeGraph(产品内) —— 同一份索引,AI 通过 /cg 斜杠模式查询。
  • LSP —— 编辑器级 go-to-definition / find-references 的另一条路径。

cockpit connection

cockpit connection list 列出运行中的 Cockpit 服务器里所有气泡 —— 终端 + 浏览器一起 —— 每条带上用户设的标题(通过气泡 short id 旁边的 ✎ 按钮设置)。

目的是让 LLM(或终端里的人)把不认识的 4 字符 bubble id 映射到一个人可读的含义,再用 cockpit terminal <id> … / cockpit browser <id> … 去驱动它。配合 agentic flow 的 /cc 斜杠模式使用。

用法

cockpit connection list [--cwd PATH] [--all] [--json]

目前只有一个子命令:list

flag

Flag含义
--cwd PATH仅列出项目 cwd 匹配 PATH 的气泡(路径会规范化)。用 $PWD 限定到当前 shell 所在项目。
--all把死掉的也算进来(已退出的终端、断开的浏览器)。默认不含。
--json输出原始 JSON,不输出 TAB 分隔文本。

输出(纯文本,TAB 分隔)

<type>  <shortId>  <title-or-(none)>  <projectCwd-or-?>  <command-or-empty>

每行一个气泡。<type>termbrowser

输出(--json

数组,每条是:

{
  "type": "term" | "browser",
  "shortId": "abcd",
  "title": "用户设的标签(可选)",
  "projectCwd": "/abs/path",
  "tabId": "…",
  "command": "npm run dev",
  "alive": true
}

退出码

含义
0有气泡
1过滤后空(没气泡)
2用法 / 参数错误
3Cockpit 服务器连不上。用 cock <项目路径> 启动。

示例

# 列出所有项目里的活气泡:
cockpit connection list
# 仅当前项目:
cockpit connection list --cwd $PWD
# 全部(含死的),JSON 给程序:
cockpit connection list --all --json | jq

相关页面

cockpit update

cockpit update 把 Cockpit 升级到最新发布版。

cockpit update

等同于跑:

npm install -g @surething/cockpit@latest

哪个都行,做的是同一件事。

升级时保留什么

你的 Cockpit 数据目录(~/.cockpit/)里的一切升级时都不动:

  • API key 和引擎设置
  • 会话和固定 tab
  • 定时任务
  • Skills 注册表
  • 笔记
  • 评审
  • Chrome 扩展缓存

只是全局 npm 包被替换。

升级后

重启任何运行中的 cockpit 进程拿新版本。如果 Cockpit 在浏览器 tab 里开着,重启后刷新页面。

确认:

cockpit -v

cockpit update 失败时

最常见原因:你系统上 npm install -g 需要 root 权限(某些环境下全局 npm 包属 root)。看到 EACCES 错误时用 sudo

sudo npm install -g @surething/cockpit@latest

或者更好的方式:一次性修好 npm 权限以后不用 sudo。npm 文档有指南 —— 搜 "resolving EACCES permissions errors npm"。

锁定到某个版本

要装特定版本而不是最新:

npm install -g @surething/[email protected]

版本列表在 npmjs.com/package/@surething/cockpit

降级

同样的命令 —— 给它你想要的旧版本:

npm install -g @surething/[email protected]

你的数据目录在 minor 版本之间向前向后兼容;降级安全。