terminal-browserBLOCK
A browser inside your terminal
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
A real browser that runs inside your terminal
Installation
curl (macOS & Linux):
curl -fsSL https://terminal-browser.sh/install | bash
Note: Run terminal-browser upgrade to upgrade versionsHomebrew (macOS & Linux):
brew install terminal-browser
Windows
To install on Windows you must be using WSL. Terminals with kitty graphics support are also very limited on Windows, the following are terminals I have tested that terminal-browser will work on inside Windows:
- https://noctty.com/
curl -fsSL https://terminal-browser.sh/install | bash
Claude code plugin
Install instructions here
Usage
terminal-browser # launches the browser terminal-browser open # opens the browser at a url terminal-browser --split right # opens the browser in a split pane to the right terminal-browser open --ssh # performs all network requests through a remote server terminal-browser open --transparent # the terminal background will show through pages without a background color terminal-browser ls # lists open browsers terminal-browser action # an agent-browser compatible cli for interacting with open terminal-browsers terminal-browser upgrade # upgrade to the latest version
Use cases:
- You can have a coding agent and website scoped to the same terminal tab
- Your agent has full access to interact with open terminal-browsers, which gives your agent the capability to use the web
- You can ask an agent to make HTML plans and then open them inside terminal-browser, which will automatically open in a split pane next to your agent
- terminal-browser works over SSH, which allows you to preview websites running on remote machines easily
Shortcuts
015423d7ed78OBSERVED · 2026-10-07Install
Commands as the repository documents them. They are shown, not run.
claude plugin install terminal-browser@terminal-browser
npm install
npm install @zenbu-labs/pixel
Host compatibility
What the documentation claims. We have not run a compatibility test.
| Host | Status | Notes |
|---|---|---|
| claude-code | mentioned | |
| codex | mentioned | |
| cursor | mentioned |
What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
---
name: verify
description: Drive an engine app headlessly in a pty, record a video of the whole verification, and open a summary page (video + timeline + checks) with pixel open.
---
Apps here render via the kitty graphics protocol, so they can be verified
without a real terminal. Every verification is **recorded**: the harness in
`tools/verify-recorder/` captures every frame the app emits, overlays your
inputs (click ripples, caption bar), encodes a video, and generates a summary
page. Do not hand-roll one-off pty scripts that only dump PNGs.
## Writing a verification
Write a driver script (in /tmp is fine) using the checked-in package:
```python
import sys
sys.path.insert(0, "<repo>/tools/verify-recorder")
from driver import Driver
from recorder import Recorder
rec = Recorder("wheel-pan", title="Wheel pan keeps cursor anchored")
d = Driver(["<repo>/engine/target/debug/typing"], rec,
cols=120, rows=32, xpixel=1200, ypixel=800)
d.pump(3.0) # pump between actions so frames arrive
rec.check("app painted", d.frame_size is not None, f"{d.frame_size}")
w, h = d.frame_size # REAL framebuffer size — always use this
d.text("hello", "type into editor") # every input takes a description
d.click(w // 2, h // 3, "select the note") # mouse coords are pixels (1016 mode)
d.wheel(w // 2, h // 2, down=True, n=3, description="scroll content")
d.pump(1.0)
rec.check("scroll redrew", len(rec.frames) > 40, f"{len(rec.frames)} frames")
rec.still("after-scroll") # named snapshot for the summary page
d.stop("ctrl+c") # or "ctrl+q" depending on the app
rec.finish() # composites markers, encodes mp4, writes summary
```
- `Driver` spawns the argv in a pty (TERM=xterm-kitty, TIOCSWINSZ with pixel
dims, answers the `\x1b[?1016$p` mouse probe), decodes kitty `a=T,f=32,o=z`
frames, and feeds them to the recorder. Node apps: spawn
`["npx", "tsx", "src/main.tsx", ...]` with `cwd=` the package dir (tsx
resolves tsconfig from cwd; a wrong cwd silently drops jsx config).
- Input methods: `key("enter"/"esc"/"ctrl+q"/"super+shift+z")`, `text`,
`click`, `press`/`drag`/`release` (a drag needs all three — `click` sends
press+release together), `move`, `wheel`. Descriptions become the video
caption bar and the summary timeline — write what the step is *testing*.
- `rec.check(name, ok, detail)` for every assertion; `rec.still(name)` to pin
the current frame into the summary.
- Give checks real assertions (frame deltas, decoded pixel colors via
`recorder.png_read(rec.frames[-1]["path"])`) — the summary shows pass/fail.
## Ending a verification (required)
`rec.finish()` prints the run dir and summary path. **Always end by opening
the summary in a split:**
```
pixel open file:///tmp/verify-runs/<name>-<stamp>/summary.html
```
That page is the deliverable: what was tested (clickable timeline that seeks
the video), the checks table, the stills, and the video of the whole run.
Watch out for FAIL rows before declaring the verification passed.
## Gotchas
- The engine rounds the window down to the cell grid, so the framebuffer can
be narrower than the requested winsize. Take coordinates from
`d.frame_size`, never from the requested pixels, or clicks land ~5% off.
- Keep pumping after quit (`d.stop` does) or the exit is never observed.
- Escape must be kitty CSI-u (`d.key("esc")` handles it); a bare `\x1b` makes
the app swallow the next escape sequence as literal text.
- If a press lands on a node without handlers, the engine dispatches the click
at the *release* position — a missed drag can silently click something else.
- Hover state only updates on move events; end interactions with a `move` if
the screenshot should show hover styling.
- Apps taking a file path argv need an ABSOLUTE path (their cwd is the
package dir).
- Run artifacts live in `/tmp/verify-runs/<name>-<stamp>/`: `frames/`,
`events.jsonl`, `run.json`, `verification.mp4`, `summary.html`.Trust audit
BLOCKgrade D · trust 69/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | FAIL |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (12 observation(s))
- Network
- declared (9 observation(s))
- Shell
- declared (6 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- none-found
Findings (24)
payload.rs
const running = exec(bin, args, { env, maxBuffer: 4 * 1024 * 1024, timeout: 8000 });const bridgeUrl = (path: string) => `http://127.0.0.1:${state.port}${path}`env.TERMINAL_BROWSER_AGENT_BRIDGE = `http://127.0.0.1:${this.port}`;const url = new URL(request.url ?? "/", "http://127.0.0.1");
.createHash("sha1")const resolved = resolveSshTarget("ssh -i ~/.ssh/id -p 2200 dev@box");assert.deepEqual(resolved.hostArgs, ["-i", "~/.ssh/id", "-p", "2200"]);
include_bytes!("../../../assets/fonts/JetBrainsMono-Regular.ttf");include_bytes!("../../../assets/fonts/JetBrainsMono-Regular.ttf");static FONT_BYTES: &[u8] = include_bytes!("../../../../assets/fonts/JetBrainsMono-Regular.ttf");include_bytes!("../../../assets/fonts/JetBrainsMono-Regular.ttf");include_bytes!("../../../../assets/fonts/JetBrainsMono-Regular.ttf");echo "READY http://127.0.0.1:$PORT/"
micromark, micromark-extension-gfm, react, zod, @types/node, @types/react, typescript
zod, @types/node, typescript
@types/node, typescript
react, @types/node, @types/react, typescript
@electron/osx-sign, esbuild
- Your agent has full access to interact with open terminal-browsers, which gives your agent the capability to use the web
- Your agent has full access to interact with open terminal-browsers, which gives your agent the capability to use the web
curl -fsSL https://terminal-browser.sh/install | bash
curl -fsSL https://terminal-browser.sh/install | bash
curl -fsSL https://terminal-browser.sh/install | bash # or brew install terminal-browser
Gates applied: no_behavioural_pass.
015423d7ed78full audit observations/trust-audit/skill/zenbu-labs__terminal-browser.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-07 | 015423d7ed78 | BLOCK | D | 69 | first audit |
Questions
What does the terminal-browser skill do?
A browser inside your terminal
Is terminal-browser safe to install?
No — not without reading the findings first. The audit graded it D (69/100) and found 2 critical or high issues in the source. Each one is listed on this page with the file and line it is on.
What can terminal-browser access on my machine?
The audit observed that it reaches the network, runs shell commands and reads or writes files. Each of those is consistent with what it says it does. Secrets in the source: none found.
What do I need installed to use terminal-browser?
Its own instructions reference click. Dependencies are not all pinned to exact versions, so what installs today may differ tomorrow.
Which assistants does terminal-browser work with?
Its documentation mentions claude-code, codex and cursor. That is what the text claims, not a compatibility test we ran.
How current is this page?
The grade is for one exact copy of the source (015423d7ed78), read on 2026-10-07. The repository is watched, and a new audit runs when it changes — this is the first audit.