OpenCockpitOpenCockpit
Reference

Cockpit CLI

The Cockpit CLI is a thin layer over the running Cockpit server's HTTP API. Two binaries are installed by npm install -g @surething/cockpit: cockpit (canonical name) and cock (short alias) — identical. The server itself runs continuously; sub-commands speak HTTP to localhost:3457 to inspect or drive what's open in your panels.

CommandPurpose
cockpitStart the server (the main entry point)
cockpit browserDrive a Browser bubble — navigate, click, evaluate JS, capture network
cockpit terminalRead a Terminal bubble's output (read-only; no stdin)
cockpit codegraphQuery the project-wide code index from a shell
cockpit connectionList all bubbles (terminal + browser) with their titles
cockpit updateUpgrade to the latest version

cockpit and cock

The cockpit binary is the main entry point. Its short alias is cock — both are installed when you npm install -g @surething/cockpit.

Usage

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

The two commands are identical; pick whichever you prefer to type.

Common forms

Start in the current directory:

cockpit

Start in a specific project:

cockpit ~/code/my-project

Start without auto-opening the browser:

cockpit . --no-open

Start on a different port:

cockpit . --port 4000

Show the version:

cockpit -v

Options

FlagDescription
-v, --versionPrint the version and exit.
-h, --helpShow inline help.
--port <n>Listen on a non-default port. Default 3457.
--no-openDon't auto-open the browser after the server starts.
[path]Working directory to open. Defaults to process.cwd().

Default port

Cockpit listens on port 3457. You can override per-run with --port, or make it permanent by writing to ~/.cockpit/server.json:

{ "port": 4000 }

The per-run flag wins when both are set.

Sub-commands

cockpit itself starts the server. Two sub-commands are used to drive Cockpit from external scripts (CI, ChatOps, automation):

Sub-commandPurpose
cockpit browser <id> <action>Drive a running Browser bubble (navigate, click, evaluate JS, capture network, …)
cockpit terminal <id> <action>Read from a running Terminal bubble (list / output / wait; read-only)

Both target an already-running Cockpit server at localhost:3457. They're the same APIs the in-app UI uses; exposing them as CLI commands lets you script the bubbles from anywhere.

See:

Upgrading

cockpit update

Equivalent to npm install -g @surething/cockpit@latest. See cockpit update for what gets preserved across upgrades.

Exit codes

CodeMeaning
0Normal exit (server stopped cleanly)
1Server failed to start (port in use, permission denied, missing Node, …)
130Killed by Ctrl+C (SIGINT)

Environment variables

VariableEffect
COCKPIT_PORTSame as --port, used by some downstream tools (/cg curl snippets, etc.).
PORTFallback if COCKPIT_PORT is unset.

cockpit browser

cockpit browser <id> <action> (or cock browser for the short form) drives a Browser bubble in your running Cockpit instance from outside — from the AI in a chat, from a shell script, from CI, from anywhere.

The <id> is the short ID badge from the Browser bubble's title bar. Click the badge to register the bubble and copy a starter command to your clipboard.

The CLI is designed for AI-driven E2E: every command has an actionable error template, silent failures are caught and warned, and the act/wait/assert cycle composes into atomic steps. Prefer selector-based interaction (--text, --selector) over snapshot refs — refs go stale on every re-render.

Quick examples

# Diagnose where the AI is currently parked (cheap, never blocks)
cock browser xa7k2 health
cock browser xa7k2 status

# Find and interact (selector-first; refs go stale on re-render)
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'

# Probe the backend (inherits page auth)
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 (atomic 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

# Test isolation
cock browser xa7k2 reset --cookies --storage --reload
cock browser xa7k2 set --type cookie --name token --value abc123 --path /

# Run arbitrary JS (escape hatch — prefer fetch/click-by-selector when possible)
cock browser xa7k2 evaluate "document.title"
cock browser xa7k2 evaluate --all-frames "await fetch('/api/x').then(r=>r.json())"

Full action list

Inspection

ActionWhat it does
listList every Browser bubble currently registered
snapshot [--filter <regex>] [--include-hidden-text] [--max-depth N]Accessibility tree. Banner explains the format. --filter greps server-side; --include-hidden-text surfaces text collapsed inside <summary> / container nodes
screenshotPNG of the page, saved to /tmp; the path is printed
urlCurrent URL
titlePage title
statusOne-line summary: URL, title, last console error, last failed request, top visible buttons. Run after a long gap to re-orient
bounds <ref>Position and size of an element
attrs <ref>All HTML attributes of an element
computed <ref>Computed CSS for an element
events <ref>Event listeners attached to an element
cookiesAll cookies for the page
storage --type local|sessionlocalStorage or sessionStorage contents
theme --mode dark|lightForce the bubble's theme

Interaction

Selector-based forms are preferred — refs are valid only until the next snapshot or re-render.

ActionWhat it does
click [<ref>] [--text <substr>] [--selector <css>] [--nth N] [--exact]Click by ref, by visible text/aria-label, or by CSS selector. Refs go stale on re-render; selector and --text survive
fill [<ref>] [--selector <css>] --value <v>Set the value via native setter + dispatch input event (works on React-controlled inputs)
type <ref> <text>Type into the focused input via CDP key events. May silently miss on React-controlled inputs — prefer fill --selector
submit [--form-selector <css>]Call form.requestSubmit(). Works where pressing Enter is ignored by onKeyDown
hover <ref>Hover over an element
focus <ref>Focus an element
scroll --direction up|down|left|rightScroll the page
key <key>Press a key (Enter, Ctrl+A, Shift+Tab, …). On React inputs, prefer submit

Wait — synchronisation between act and assert

ActionWhat it does
wait --network-idle [--quiet-ms 500] [--max-request-age-ms 30000]Wait until 0 in-flight HTTP requests for quiet-ms consecutive ms. Long-running streams (SSE / long-poll) older than max-request-age-ms are excluded
wait --selector <css> [--state visible|hidden|attached|detached]Wait for the matching element to reach the given state. Default state is visible
wait --dom-stable [--quiet-ms 300]Wait until a MutationObserver reports no DOM changes for quiet-ms (useful between an act and a snapshot)
wait --extension-ready [--quiet-ms 500]CLI-side poll of health. Never blocks on the page. Replaces manual until evaluate "1+1" loops when the page is busy
wait --text <substr> / --url <pat> / --ref <ref> / --time <ms>Classic conditions: text appears, URL matches, ref is still attached, sleep

Assert — non-zero exit on failure

ActionWhat it does
assert --selector <css> [--text <substr>] [--visible <bool>] [--attr "k=v"]Element-level assertion via selector. Refs are also accepted (--ref) but go stale; selector is preferred
assert --network --method <M> --url <pat> --status <S> [--since <ms>]Assert that a matching request occurred in the network buffer. --status accepts ints (200) or ranges (2xx)
assert --fetch <url> [--fetch-method M] [--body B] [--fetch-status N] [--jsonpath <P> --equals V | --contains V | --not-contains V]Make a fetch (inherits page auth) and assert the response status or a JSONPath value. JSONPath subset: $, .key, [N], [*]
assert --url <pat> / --title <substr> / --console-no-errorsPage-level assertions
ActionWhat it does
navigate --url <url>Go to a URL
reload [--noCache]Reload (optionally bypassing cache)
backGo back one history entry
forwardGo forward one history entry

Backend probing

ActionWhat it does
fetch <url> [--method <M>] [--body <B>] [--headers <JSON>] [--json <jsonpath>]GET (or any method) against the page's auth session. --json extracts a value via the same JSONPath subset as assert --fetch. Returns { status, contentType, data } or { status, jsonpath, value }

fetch is the preferred way to read or mutate the backend from the AI — clearer than wrapping await fetch(...).then(r => r.json()) inside evaluate.

JavaScript

ActionWhat it does
evaluate <js>Run a JS expression in the page; result printed as JSON. --all-frames runs in every iframe. Large results are transparently chunked back to the CLI

Use evaluate as an escape hatch when none of the higher-level actions fit. For plain HTTP calls, prefer fetch; for clicks, prefer click --text / --selector; for assertions, prefer assert --selector.

Lifecycle / fixtures

ActionWhat it does
reset [--cookies] [--storage] [--cache] [--reload]Atomic test-isolation helper. Combine flags as needed. --cookies expires JS-visible cookies; --storage clears localStorage and sessionStorage; --cache drops Cache Storage entries; --reload reloads the page after clearing
set --type cookie --name <K> --value <V> [--domain <D>] [--path <P>] [--secure] [--same-site Lax|Strict|None] [--expires <date>]Write a JS-visible cookie. Returns verified: false if the browser silently rejected the value (cross-domain / SameSite mismatch / Secure over HTTP)
set --type local-storage --name <K> --value <V>Write to localStorage
set --type session-storage --name <K> --value <V>Write to sessionStorage

Diagnostics

ActionWhat it does
healthServer-side snapshot of the bridge: WS status, pending command count, time since last successful command. Never round-trips to the page, so it works even when the page itself is blocked on a long-running evaluate
health --deepAlso probes the page itself (readyState, snapshotEpoch, page-side timestamp). May block if the page is busy

Network capture

ActionWhat it does
network [--status <code>] [--method <method>] [--type <type>] [--clear]List captured network requests with filters. --status accepts comma-separated 4xx,5xx
network_record start [--url <pat>] [--method <m>] [--status <code>]Start recording request/response bodies
network_record stopStop recording
network_record statusWhether recording is on
network_detail <reqId>Full request/response detail for one request
console [--level error|warn|info|debug] [--clear]Console messages
perf [--metric timing|memory|resources]Performance metrics including Core Web Vitals

Snapshot output

snapshot returns a plain-text accessibility tree. The first 4–5 lines are a banner explaining the format, including the current snapshot version (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]
  ...

Each addressable element gets a ref like e5#v3. The #v3 suffix is the snapshot epoch — refs from an earlier snapshot are rejected with a clear "stale" error that points you to click --text / click --selector as the re-render-safe alternative. Most AI workflows should skip refs entirely and use --text / --selector directly.

Post-verify for click / key / submit

These actions are most prone to silent failure — CDP reports "succeeded" even when the framework didn't actually react to the event (React-controlled inputs ignoring synthetic keydown, portal-rendered buttons with no real handler, etc.). The CLI silently probes the page state before and after each action; if nothing observable changed in the verify window, it writes a warning to stderr along with actionable templates (without affecting the action's main stdout).

FlagWhat it does
--verify-ms <ms>Override the verify window. Default is 1000 ms. Lower = faster but more false positives on slow-rendering React; higher = more tolerant
--skip-verify (or --no-verify)Disable post-verify for the current command (e.g. legitimate clicks with no observable side-effect)

Output format

Most actions return JSON on stdout — easy to pipe into jq, gron, or to read from the AI. url, title, and network_detail return plain text. screenshot returns a file path. snapshot returns the banner + plain-text accessibility tree. health, status, wait, and assert print a one-line human-friendly summary. fetch prints [status] (contentType) followed by the body, or [status] $.jsonpath = followed by the extracted value.

Warnings (silent-failure detection, cookie not accepted, etc.) go to stderr. The main result stays on stdout, so a > redirect captures the real data while letting the AI still see the warning.

Exit codes

0 on success, non-zero on failure (stale ref, network error, assertion failure, no matching element, etc.). See the main CLI page for the full exit code list.

When NOT to use this CLI

  • Testing LLM-agent driven flows end-to-end. The agent's stochastic tool choice and stop_reason make UI assertions flaky. Prefer a thin runtime script that calls the same middleware or service directly with controlled inputs.
  • Pages that stream or re-render for >10 s. evaluate calls queue behind page work and may time out (~15 s default). Run wait --extension-ready between acts and asserts; if it stays hung, pivot to a service-level test.
  • Multi-tab / popup OAuth flows. Each Browser bubble tracks one tab. Open the secondary tab in its own bubble, or stub the OAuth handshake.

cockpit terminal

cockpit terminal <id> <action> (or cock terminal) reads from a Terminal bubble in your running Cockpit — pulls the buffered output or waits for a running command to settle.

Note: the terminal CLI is intentionally read-only. There is no stdin and no follow (live streaming). The code comment says: "read-only by design; the write side belongs to the Bash tool / web UI." For interactive control, drive the bubble inside Cockpit's UI, or use a Browser bubble + cock browser for automation.

The <id> is the short ID badge from the Terminal bubble's title bar. Click the badge to register the bubble and copy a starter command to your clipboard.

Full action list

ActionWhat it does
listList every Terminal bubble currently registered. Shows status (running / idle) and the command in each.
outputPrint the entire buffered output of the terminal — the full history from when the bubble started.
waitBlock until the currently-running command settles. Useful in scripts that need to "wait for npm run build to finish before continuing".

Quick examples

# Find your bubbles
cock terminal list

# Snapshot what's on screen now
cock terminal xy789 output

# Wait for npm run build to finish
cock terminal xy789 wait

# Then look at the result
cock terminal xy789 output | tail -50

When to use this

Main patterns:

  • AI reads what your shell is doing through the bubble. Run npm run dev in a Terminal bubble. Hand cock terminal <id> to the AI in chat — it can output for recent logs and wait for a build to settle.
  • CI / scripts observe a long-running command from outside. A launcher script kicks off npm run dev in a Cockpit terminal; another script periodically runs cock terminal <id> output to scrape logs and assert.

Limits

The Terminal CLI is intentionally lighter than the Browser CLI:

  • No write to stdin. You can't push commands into the terminal process from the CLI.
  • No screen-scraping with structured selectors (output gives you the raw buffer, you parse it yourself).
  • No window-resize, signals, or Ctrl+C-style interrupt actions.

For full interactive control (Ctrl+C, typing commands, etc.), interact with the bubble in Cockpit's UI directly.

cockpit codegraph

cockpit codegraph queries the project-wide code index that powers the in-product CodeGraph feature (/cg mode), but from your shell so it composes with scripts, CI, and Unix pipelines.

The Cockpit server must be running — the CLI talks to it over HTTP on the local port.

Two families of subcommands

Lookups — coordinates only (file paths + line numbers), mirroring the in-product API:

SubcommandWhat it does
search <query>Find symbols by name. Returns file + qname hits.
callers <qname> [--file PATH]Direct callers of a symbol.
callees <qname> [--file PATH]What a symbol calls.
impact <qname> [--depth N=2]Transitive callers, BFS. Use risk for ranked output.
file <path>Symbol tree (functions/classes) inside a file.
coedit <path> [--commits N=100]Files co-edited in git history alongside this one.

Analytics — blended scoring across PPR / TF-IDF / Louvain communities / co-edit:

SubcommandWhat it does
context --query Q [--cursor C] [--open F1,F2,…] [--top N=15]Top-K semantically relevant coordinates for a free-form question.
related <qname> [--top N=10]Broader neighbours: callers + callees + PPR + co-edit + community.
risk <qname> [--depth N=2] [--top N=20]Risk-scored impact + suggested tests.
affected <files…|--stdin> [--depth N=10] [--filter G] [--as-cmd RUNNER]Test files transitively affected. CI-friendly.

Common flags

--json             # Raw JSON response (full schema; see per-cmd --help)
--help, -h         # Subcommand-specific help (output format + exit codes + examples)

Output format (plain text)

TAB-separated, one row per result. Pipeable into cut, awk, fzf, etc.

search    sym\t<file>:<line>\t<kind>\t<qname>            or  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>                  # after a '# history' comment
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>                                     # one test path per line

Pass --json to get the full structured payload instead.

Diagnostics on stderr

Designed so warnings don't break shell pipelines:

# ambiguousIn: <files…>     Same qname in multiple files — pass --file
# cursor: <note>            Cursor format auto-corrected ('.' → '::', etc.)
# degraded: <reason>        analytics-warming / coedit-unavailable / truncated

Exit codes

CodeMeaning
0Output produced
1Empty result (no callers, no tests, no hits) — short-circuits shell pipelines
2Argument or 4xx server error
3Cockpit server not reachable. Start it with cock <project-path>.

Prerequisites

The CLI hits http://localhost:3457 by default (the same port as the main Cockpit server — not a separate codegraph port). Override via:

COCKPIT_HOST=… COCKPIT_PORT=… cock codegraph …

Examples

cock codegraph search getCodeIndex
cock codegraph related getCodeIndex --top 5
cock codegraph risk searchIndex --depth 2
# Newline-separated test paths for whatever changed:
git diff --name-only | cock codegraph affected --stdin
# Drive jest directly with the affected tests:
git diff --name-only | cock codegraph affected --stdin --as-cmd jest
# Same idea for vitest:
git diff --name-only | cock codegraph affected --stdin --as-cmd "vitest run"

See also

  • CodeGraph (in-product) — the same index, but queried by the AI via /cg slash mode.
  • LSP — alternative for editor-grade go-to-definition / find-references.

cockpit connection

cockpit connection list enumerates every bubble — terminal and browser — that the running Cockpit server knows about, with each bubble's user-set title (set via the ✎ button next to the bubble's short id).

The point is to give an LLM (or a human at a shell) a way to map cryptic 4-character bubble ids to human-meaningful purposes before driving them via cockpit terminal <id> … or cockpit browser <id> …. This page maps to the /cc slash mode used by the agentic flow.

Usage

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

Only one subcommand exists today — list.

Flags

FlagMeaning
--cwd PATHOnly list bubbles whose project cwd matches PATH (canonicalised). Use $PWD to scope to the current shell.
--allInclude dead entries (exited terminals, disconnected browsers). Off by default.
--jsonEmit raw JSON instead of TAB-separated lines.

Output (plain, TAB-separated)

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

One row per bubble. <type> is term or browser.

Output (--json)

Array of:

{
  "type": "term" | "browser",
  "shortId": "abcd",
  "title": "optional user-set label",
  "projectCwd": "/abs/path",
  "tabId": "…",
  "command": "npm run dev",
  "alive": true
}

Exit codes

CodeMeaning
0Bubbles found
1No bubbles (after filters)
2Usage / argument error
3Cockpit server unreachable. Start it with cock <project-path>.

Examples

# All live bubbles across all projects:
cockpit connection list
# Only this project's bubbles:
cockpit connection list --cwd $PWD
# Everything (including dead), as JSON for programmatic use:
cockpit connection list --all --json | jq

See also

cockpit update

cockpit update upgrades Cockpit to the latest published version.

cockpit update

Equivalent to running:

npm install -g @surething/cockpit@latest

You can run either; they do the same thing.

What gets preserved

Everything in your Cockpit data folder (~/.cockpit/) is untouched by an upgrade:

  • API keys and engine settings
  • Sessions and pinned tabs
  • Scheduled tasks
  • Skills registry
  • Notes
  • Reviews
  • The Chrome extension cache

Just the global npm package gets replaced.

After upgrading

Restart any running cockpit process to pick up the new version. If you have Cockpit open in a browser tab, refresh the page after restart.

To verify:

cockpit -v

If cockpit update fails

The most common cause is that npm install -g needs elevated permissions on your system (a globally-installed npm package is owned by root on some setups). If you see an EACCES error, run with sudo:

sudo npm install -g @surething/cockpit@latest

Or, better, fix your npm permissions once so you don't need sudo. The npm docs have a guide for this — search "resolving EACCES permissions errors npm".

Pinning to a version

To install a specific version instead of the latest:

npm install -g @surething/[email protected]

The version list is at npmjs.com/package/@surething/cockpit.

Downgrade

The same command — just give it the older version you want:

npm install -g @surething/[email protected]

Your data folder is forward and backward compatible across minor versions; downgrading is safe.