Chrome Cdp
aiguicai/MCP-Gateway
Browser automation and debugging through bundled Chrome DevTools Protocol.
Lets the agent operate your own logged-in browser through the owb command to read pages, fill forms, audit sites and capture network traffic.
$ npx skills add woniu9524/open-web-bridge --skill owb -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install woniu9524/open-web-bridge owb --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/woniu9524/open-web-bridge.git skills-src && mkdir -p .claude/skills && cp -r skills-src/owb-skills/owb .claude/skills/owb && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "owb" agent skill from https://github.com/woniu9524/open-web-bridge/tree/master/owb-skills/owb into .claude/skills/owb/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "owb", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/woniu9524/open-web-bridge/tree/master/owb-skills/owbType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add woniu9524/open-web-bridge --skill owb -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install woniu9524/open-web-bridge owb --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/woniu9524/open-web-bridge.git skills-src && mkdir -p .agents/skills && cp -r skills-src/owb-skills/owb .agents/skills/owb && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "owb" agent skill from https://github.com/woniu9524/open-web-bridge/tree/master/owb-skills/owb into .agents/skills/owb/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "owb", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add woniu9524/open-web-bridge --skill owb -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install woniu9524/open-web-bridge owb --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/woniu9524/open-web-bridge.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/owb-skills/owb .cursor/skills/owb && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "owb" agent skill from https://github.com/woniu9524/open-web-bridge/tree/master/owb-skills/owb into .cursor/skills/owb/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "owb", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/woniu9524/open-web-bridge.git --path owb-skills/owb--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add woniu9524/open-web-bridge --skill owb -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install woniu9524/open-web-bridge owb --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/woniu9524/open-web-bridge.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/owb-skills/owb .gemini/skills/owb && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "owb" agent skill from https://github.com/woniu9524/open-web-bridge/tree/master/owb-skills/owb into .gemini/skills/owb/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "owb", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install woniu9524/open-web-bridge owbInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add woniu9524/open-web-bridge --skill owb -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/woniu9524/open-web-bridge.git skills-src && mkdir -p .github/skills && cp -r skills-src/owb-skills/owb .github/skills/owb && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "owb" agent skill from https://github.com/woniu9524/open-web-bridge/tree/master/owb-skills/owb into .github/skills/owb/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "owb", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add woniu9524/open-web-bridge --skill owb -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install woniu9524/open-web-bridge owb --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/woniu9524/open-web-bridge.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/owb-skills/owb .opencode/skills/owb && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "owb" agent skill from https://github.com/woniu9524/open-web-bridge/tree/master/owb-skills/owb into .opencode/skills/owb/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "owb", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
owbLets the agent operate your own logged-in browser through the owb command to read pages, fill forms, audit sites and capture network traffic.
The owb command-line tool controls the browser you are using right now, with your logged-in accounts, settings and open tabs. That gives the agent access to pages behind a login and to actions that need your identity. Because every step lands on a real account, the skill tells the agent to read freely, ask before changing anything, and open pages in new tabs so your current view is not taken over.
Work starts with a self-check that confirms the daemon and the browser extension are connected, and the agent is told to stop and ask you when the extension is not. The main file covers a core loop, and five reference files are read on demand: commands, recipes, debugging and reverse-engineering, field notes, and relay mode for remote control. The tool has about 80 commands in 16 groups, and the agent is told to look names up with owb help rather than guess.
Read from SKILL.md and the folder at commit 27c9ebe. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
npmnodeFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
news.ycombinator.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Open Web Bridge Browser Control loads about 8.1k tokens when it runs, and up to ~27k if it reads all its reference files. Until then it costs about 163 tokens; SKILL.md has 4,222 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from woniu9524/open-web-bridge at commit 27c9ebe, republished under its MIT licence (© woniu9524). 4,222 words, ~8,111 tokens.
.claude/skills/owb/SKILL.md (or your agent's skills folder). This skill also uses 5 other files; get the full folder from GitHub.owb, the Open Web Bridge CLI, operates the browser the user is using right
now: their logged-in accounts, their settings, their open tabs. That is why you
can reach content that exists only behind a login, and complete actions that
require an identity.
For the same reason, every step lands on a real person's account and they may be using this browser beside you. Read freely; ask before changing anything; open pages in new tabs so you don't take over what they are looking at.
In a hurry? Read "Before you start" → "The shape of a task" → "The core loop".
This file is the trunk; five reference files live in references/, read on demand:
| File | When to read it |
|---|---|
references/commands.md | Arguments, defaults, limits and return fields — check before writing a complex call instead of guessing argument names |
references/recipes.md | Longer recipes for a specific job: extraction, clicking quirks, drag/canvas, downloads, site debugging, responsive audits, logins, flows |
references/debugging.md | Debugging and reverse-engineering: capture, HAR processing, hooks, breakpoints, script patching, offline signature verification |
references/field-notes.md | Long-tail oddities seen in the field (the "Reality check" table below is the symptom index — come here once a symptom matches) |
references/relay.md | The user asks about remote-controlling their browser, or daemon-status reports relay mode |
For anything not documented here: this file cannot cover all 80 commands.
owb help lists all 16 groups, owb help <group> expands one, and
owb call <tool> --args '<json>' calls any underlying tool directly.
Don't guess command names — look them up.
owb # self-check✓ daemon + ✓ extension connected → go✗ extension not connected → stop and tell the user to click the extension
icon in their toolbar and check its status. Hammering the tools will only
produce NO_EXTENSION forever.npm i -g open-web-bridge, then owb setup[owb] daemon not running, starting it… on stderr during the first command is
not an error — the CLI is starting the daemon. Wait for it to connect;
don't report it as a fault.The self-check reports one of two modes: local (default; daemon and browser on
one machine) and relay (browser on the user's machine, agent elsewhere, paired
through a public relay). Local mode needs no configuration. Everything about relay
mode — setup, capability differences, troubleshooting — is in references/relay.md.
Read this section first. Individual commands are documented below, but if you don't know where a piece of work begins and ends, you will pile dozens of unrelated tabs into one place and have to close them one by one — measured: 25 tabs in a single session.
For multi-step work (anything beyond opening one page for a single look), the four steps are:
owb task begin "competitor pricing research" # ① open a task: creates a task group
owb open <url> --new-tab true # ② work: new tabs join that group
owb page / click / fill / wait # …normal operations
owb task end # ③ close out: stop recording, archive, report tabs
owb tab close-group # ④ clean up: close every tab from this taskEach step earns its place:
| Step | What happens without it |
|---|---|
task begin | Tabs pour into the shared scratch group, mixed with other tasks and with the user's own tabs |
--new-tab true | You navigate away from whatever the user is currently reading — this is a real person's browser, not yours |
task end | Recording keeps running and nothing is archived; flow save also fails with NEED_TASK |
tab close-group | Tabs accumulate without limit, and it gets progressively harder to tell whose is whose |
⚠️ task end does not close tabs. It archives, clears task context and
reports which tabIds were produced, but the pages stay open (sometimes you do
want to keep the results visible). To actually clean up you must also run
tab close-group. The word "end" does not mean what you would assume.
tab close-group closes the "OWB scratch" group plus every "task:" group;
handoff groups are kept by default (that is the user's live workspace). To
include them, pass --args '{"include_handoff":true}'. To close only the scratch
group and keep task groups, pass --args '{"include_tasks":false}'.
A one-off look needs no task — owb open <url> --new-tab true is enough, and the
tab lands in the "OWB scratch" group. The taskHint in the result tells you how
many tabs are sitting there; a rising number is the signal to task begin.
⚠️ Losing task context has two consequences. Chrome reclaims idle MV3 service
workers, and owb reload-ext or a daemon restart does the same; afterwards
currentTask is gone. (1) The group survives but new tabs stop joining it and
scatter into the scratch group — the symptom is tabs wandering off mid-task; just
run task begin again (same title reuses the group). (2) task end reports
NO_TASK and the task is never archived — though tab close-group still works,
since it matches by group name. If you need the archive, confirm
owb daemon-status still shows current_task before closing out.
🚨 Running several OWB missions at once (multiple agents/processes driving the
same browser concurrently) is a different failure mode from the one above, and
it's not idle recycling — it's contention. The daemon tracks "the current task"
as one shared value, not one per caller. Whichever task begin runs last holds
it; every earlier task's plain task end then either raises NO_TASK (if the
pointer's since gone null) or — worse — returns ok:true while archiving
someone else's task, with no error to catch it. New tabs opened in between two
task begin calls get grouped under whichever task happened to be "current" at
that exact moment, not the one that opened them — so tabs from different parallel
missions end up scattered across each other's groups. Measured: 5 concurrent tasks,
5/5 hit this. What's fixed, and what still isn't:
task begin returns task_id — hold onto it, and pass it back as
task end --task-id <id> instead of a bare task end. That reliably archives
your task's metadata even if the pointer has moved on (the result carries
ended_stale: true when it wasn't the live one) — but it does not retroactively
fix which group your tabs already landed in.task end's cleanup no longer reaches outside your task: it re-confirms ownership
with the extension by task id before touching anything, and stops only the
recorders on your task's tabs rather than every recorder in the browser. Tab
ownership is tracked per task as you navigate, so it covers tabs you reused as
well as ones you opened with --new-tab. If a recorder is running on a tab that
genuinely isn't yours, task end leaves it alone and names it in
recorders_not_archived — it is never silently dropped, but you do have to
har save it yourself.tab close-group when other missions might be running.
It closes by group name, and group membership is exactly what's unreliable
under concurrency — it can just as easily close another mission's live tabs as
your own. Track the tabIds you actually opened yourself and owb tab close --tab <id> each one individually (or batch them with owb seq) instead.task end's result has group.cleared: true — that means the task context
was cleared, not that any tab was closed (tabs_closed: false says so explicitly).
Closing tabs is always a separate step.owb runs inside the browser the user is using right now, so there are always
two populations of tabs. Confusing them means closing the user's work at best, and
reading the wrong page without noticing at worst.
| Group | Color | Whose | Created when |
|---|---|---|---|
task: <title> | cyan | yours | --new-tab after task begin |
OWB scratch | blue | yours | --new-tab with no active task (staging area) |
✋ OWB waiting for you | orange | handed to the user | after owb handoff (captcha, login) |
| ungrouped | — | the user's own | they opened it; never touch these |
Guardrail: owb tab close only permits the first three. Closing an ungrouped
tab raises FORBIDDEN. That is protection, not a fault — when you hit it, first
ask yourself "did I grab the wrong tabId?" rather than reaching for --force true.
⚠️ Never assume a tab is still the one you left there. The user may have
navigated it elsewhere, and other automation may reuse it. Measured: after a gap,
an old tabId used with eval to read a chess position returned a completely
unrelated search results page — every command "succeeded" and every piece of
data was wrong, which is far harder to catch than an error. Before reusing a
tabId across turns, confirm with owb tab list (or owb tab find --url-pattern "<regex>"), or just open a fresh one.
owb page turns the page into a numbered list, one @eN per interactive element,
so you act by number instead of guessing CSS selectors.
owb open https://example.com --new-tab true # ← don't skip this, see below
owb page # @e1 @e2 … each with role, text, href
owb fill @e3 "keyword"
owb keys --keys Enter # submit with a real Enter key
owb wait --text "results" # wait for readiness, never sleep
owb click @e5⚠️ --new-tab true is not optional. Skipping it navigates the user's current
tab away from what they were reading. Only skip it when you specifically intend to
operate on the current page.
💡 --active false opens the new tab in the background, so you don't steal
their screen focus. Especially worth it when opening several pages to read.
🚨 But a background tab is not the active tab, so every later command needs an
explicit --tab. Without --tab, page/eval/wait/click act on the
active tab — which is not the one you just opened; it is the page the user is
looking at. Skipping --tab does not produce an error, it produces a silent
read of the user's private page treated as your task data (measured: open
returned tabId A, and the very next page read the user's internal admin system).
owb open <url> --new-tab true --active false # → note the tabId in the result
owb page --tab <that tabId> # ← carry it on every commandThe open result includes backgroundTabHint naming the tabId to use. With an
active task, forgetting --tab raises AMBIGUOUS_TAB — but without an active
task there is no such protection, which is one more reason to task begin for
multi-step work.
owb keysfill only sets a value; it produces no key events. Submitting a search,
dismissing a dialog, or using a shortcut all require keys:
owb keys --keys Enter # submit
owb keys --keys Escape # close a dialog / exit fullscreen
owb keys --keys "ArrowDown ArrowDown Enter" # space-separated, pressed in order
owb keys --keys "Ctrl+a" # modifiers (Ctrl|Alt|Shift|Meta)
owb keys --text "typed text" # insertText — produces no per-key events⚠️ --text and --keys are mutually exclusive; passing both raises
BAD_ARGS. To type and then press Enter, call it twice. Named keys and the full
list are in references/commands.md.
One line carries more than you might expect:
@e17 link "Technology" href=https://example.com/lists/65.html
@e2 textbox "Search models, datasets, users..."
@e48 textbox "< 1B 6B 12B" type=range
@e86 textbox "Filter by name" type=search
checkbox "Subscribe to newsletter" checked
link "Next page" href=... disabledhref — even when the name is empty, the URL tells you
where it goes. This is an underrated fallback.type= (range/search/date/email…), and required, checked,
disabled are all marked; <select> lists its values=[...].iframes lists the page's iframes and shadowRoots reports how much content
comes from Web Components (snapshots descend into open shadow roots). To act
inside an iframe: owb frames for the list, then
owb eval --frame-pattern <regex>; contextId:null frames are cross-origin
and cannot be evaluated.After a navigation or re-render, old numbers stop working and report REF_STALE.
Run owb page again for fresh numbers — don't retry with the old ones.
Roughly a quarter of sites hit the cap on the first screen (default 20000 chars /
400 nodes); the result then carries truncated: true and omittedNodes.
Either raise the cap or narrow the scope:
owb page --max-nodes 1200 --max-chars 60000 # full detail (max-nodes caps at 2000)
owb page --mode article # article body onlyDense text-heavy sites hit the cap especially easily.
After acting, do not re-read the whole page. Use --since-last to get only
what changed:
owb fill @e86 "qwen3"
owb wait --network-idle true
owb page --since-last true
# → added=54 changed=6 removed=62 unchanged=340Only 54 new entries enter your context instead of 400 elements. Whether a long session survives comes down to this one habit.
| Mode | Use for | Main field |
|---|---|---|
owb page (default snapshot) | Acting on a page: finding buttons, filling forms, clicking links | lines |
owb page --mode article | Reading the body of an article page as clean markdown | content |
owb page --mode text | List pages, irregular structures, when you want all the text | text |
💡 All three modes also expose text as an alias for that mode's main field,
so scripts can read text without switching field names per mode.
⚠️ List pages and forum front pages return 0 characters in article mode —
they have no "article body"; that is correct, not a failure. The reason field on
an empty result explains why.
💡 Body reads as 0 characters while hiddenContentNote says the DOM holds
thousands → switch to --mode text, which falls back to textContent
automatically and marks the result textSource: "textContent-fallback"
(measured: article 3 chars → text 25161). ⚠️ That fallback is DOM order, not
visual order, and includes navigation chrome — fine for reading, not for
judging layout. Why it happens: references/field-notes.md, symptom C.
💡 Body nearly empty and the DOM really is empty too (title renders,
content doesn't — typical of client-side-rendered pages): the result carries a
_hint that says which case you are in — still loading (→ wait --network-idle), a bot-check interstitial (→ handoff), an SPA shell whose
data never arrived (→ check net list), or content behind interaction /
inside an iframe. Follow the hint instead of re-reading in a loop.
owb wait --selector ".result-item" # wait for an element
owb wait --text "128 results" # wait for text
owb wait --url-pattern "search" # wait for navigation
owb wait --network-idle true # wait for requests to settle (best for SPAs)Exactly one of the four; passing two raises BAD_ARGS. Never sleep.
⚠️ On SPAs, open returning loadCompleted:true does not mean the content has
rendered. Measured: one news site returned complete in 2.3 seconds with only 6
elements in the snapshot. When a snapshot looks suspiciously small, run
owb wait --network-idle true and read again.
| Layer | Argument | Default | Cap |
|---|---|---|---|
| CLI waiting for the ctl result | --timeout <seconds> | 120 | 300 |
| Tool-internal waiting | --timeout-ms <ms> | open 30000 / wait 10000 / wait-user 280000 | wait 110000 |
| A single CDP call | none | 30 seconds | hardcoded, no argument changes it |
--timeout is not forwarded to the tool; it only relaxes the CLI's wait
for a result. To let owb open wait more than 30 seconds on a slow site, write
--timeout-ms 60000. Writing --timeout 60 does nothing.owb wait waits only 10 seconds by default, not indefinitely. For slow
sites pass --timeout-ms 60000 explicitly.--timeout, or you hit the 120-second envelope first (ctl call timeout).
wait-user is the one exception — the CLI relaxes it automatically.owb shot and owb eval can hit is the CDP hard limit;
no argument helps. That is a different problem — see the Reality check table.Longer recipes — structured extraction, live-data provenance, click vs
click --mouse, dragging and canvas, rich text editors (ProseMirror-style),
speeding up slow sites, downloads and uploads, debugging a site,
responsive/accessibility audits, saving logins, recording flows, high-frequency
step sequences (owb seq) — live in references/recipes.md. The few below
are either used constantly or easy to get wrong.
The user's session is already in the browser, so just open it:
owb open <article URL> --new-tab true
owb page --mode articleFor multiple pages: owb page to find the "next page" number → owb click @eN →
owb wait → read again.
Comments and replies are not article body — --mode article doesn't recognize
them and returns 0 characters, while the default snapshot only draws structure
(who posted how long ago, upvote/reply links) and never shows what was said.
Skip snapshot and article for threads; go straight to text:
owb open https://news.ycombinator.com/item?id=<id> --new-tab true
owb page --mode text # measured: the whole thread is here; snapshot/article are emptyThe same applies to Reddit posts, forum replies, and Q&A answer lists.
The genuinely valuable pattern: pull a repository link from a paper page → open
GitHub to read stars and recent commits → conclude whether the code actually
exists and whether the project is alive. That conclusion requires more than one
site; no single-site scrape produces it. Pair it with task begin to keep the
whole chain in one group.
Don't try to get around it, and don't fill in credentials. Hand it back:
owb handoff --reason "Please complete the QR login on this page, then tell me"
owb wait-user --condition text --text "Sign out" # ← wait for a post-login marker⚠️ A bare owb wait-user waits for a URL change (condition: url_change by
default) with a 280-second timeout. QR logins and SPA logins often don't change
the URL — a bare call will silently wait almost five minutes and then time out.
For logins, always pass an explicit condition: --condition text --text "<text that only appears after login>" or --condition selector --selector "<avatar or username selector>".
Then tell the user plainly what you need them to do. When they are done,
confirm with owb page before continuing.
⚠️ net start must come before navigation, or you get orphan records with no
URL. 🚨 But don't write it as bare owb net start then owb open <url> —
without --tab, capture binds to the user's active tab and the open then
navigates their page away. Get your own blank tab first:
owb open about:blank --new-tab true --active false # ① your own tab, note the tabId
owb net start --tab <id> # ② bind capture to it
owb open http://localhost:3000 --tab <id> # ③ then navigate⚠️ har save is the only command that stops AND keeps the recording.
har discard also stops but throws the data away — it exists for
abandoning a recording, never as a step before save (that reports
NOT_RECORDING and the data is permanently lost).
The full ladder — HAR to replay scripts, HAR diffing, assertions, hooks,
breakpoints, script search and patching, offline signature verification, TLS
replay — is in references/debugging.md. Go there for "how is this parameter
computed" questions. The rest of the recipe is in references/recipes.md.
--foo-bar <value> → tool argument foo_bar; JSON-parsed when possible
(--new-tab true is a boolean)--tab <id> selects a tab; without it, the active tab is used--out <file> is the on-disk path for screenshots and PDFs--args '<json>' passes anything the flat aliases don't cover (highest priority)--raw for the full result envelope, --compact for
single-line JSON, --no-autostart to skip starting the daemon,
owb call <tool> --args '<json>' for any tool, owb cdp for raw CDPowb seq "<step>" "<step>" … runs several steps over one process and
connection — reach for it when a stretch of mechanical steps (game moves,
multi-field forms) would otherwise pay ~100–200ms of spawn overhead each.
Recipe in references/recipes.md, flags in references/commands.md@e3 is splatting syntax and silently
vanishes before owb runs (symptom: a confusing BAD_ARGS) — always quote
refs there: owb fill '@e3' 'x'. And Windows hosts that spawn processes
without a shell can't execute the owb.ps1/owb.cmd shims — invoke
node "<npm root -g>\open-web-bridge\owb-daemon\src\cli.js" instead.
Both are detailed in references/commands.md under "PowerShell"Success prints data JSON to stdout; failure prints one line
error CODE: message to stderr with a non-zero exit code (2 for usage errors).
⚠️ Screenshots and PDFs always go to disk; stdout returns only
{savedTo, bytes} rather than flooding your context with base64. For a full-page
capture add --full-page true.
Full argument tables, return fields and the underscore-prefixed CLI metadata
fields are in references/commands.md.
These are not malfunctions; they are what a real browser does. The table indexes
by symptom, with details in references/field-notes.md — go read the details
once a symptom matches, rather than pulling all of it into context up front.
| Symptom | Judgment in one line |
|---|---|
| Floating buttons unrelated to the page (translate / bookmark / quick settings) | Injected by another extension the user installed; extensionUiHidden only filters some. Don't click them |
Animation frozen / video stuck at readyState:0 / game unresponsive | The whole Chrome window lacks OS focus (not the tab being inactive). Run the focus diagnostic below |
owb shot hangs the full 30 seconds | First add owb eval "1+1" to fork: also times out = the tab is genuinely dead (close and reopen); instant = the window lacks focus (reopening won't help) |
Empty snapshot with renderNote | The tab isn't rendering; already auto-foregrounded and retried. renderNote says whether it is a content problem or window focus |
403 / empty snapshot / httpErrorHint | Anti-scraping refusal. Change approach; don't keep analyzing an empty snapshot |
Everything reports FORBIDDEN and the page looks different | The tab was replaced wholesale by an extension like OneTab. Confirm with tab list, reopen in a new tab |
FORBIDDEN mentioning cannot be scripted, a chrome-extension:// URL, or an interstitial | Chrome structurally forbids debugging these (Web Store, chrome://, devtools://, other extensions' pages, security interstitials). Not a fault, retrying won't help — find another route, or have the user handle it in the browser |
| A PDF URL yields no body text in any mode | Chrome's PDF viewer lives in a separate sandboxed view — architecturally out of reach. Find an HTML version, or screenshot it |
Garbage words like derosnopS in search results | Anti-scraping decoy text (invisible to humans). Skip it as noise |
Navigating to docs.google.com/**/create | ⚠️ Merely visiting creates a real file in the user's account, and a brand-new empty file cannot be trashed. Don't navigate to these URLs |
stderr shows extension unreachable … retrying in N s | The MV3 worker was recycled. Normal — the command retries itself (about 35 seconds total) to success |
Still NO_EXTENSION after the backoff finishes | This is not idle recycling; the script itself crashed and the heartbeat cannot wake it. Stop waiting and tell the user |
| Navigation is just slow | Measured across 219 sites: p50 4.7s / p90 15s / p99 30s. Beyond 30s it is usually the site — widen with --timeout-ms or use domcontentloaded |
Window focus diagnostic (check both signals; one alone will misdiagnose):
owb eval 'document.visibilityState + " hasFocus=" + document.hasFocus()'visibilityState (measured: hasFocus()
true while visibilityState was still hidden, and rAF never fired)shot hanging the full 30s → follow hasFocus() (measured: the page was
visible while hasFocus() was false and the compositor produced no frames)If either looks wrong, ask the user to bring the whole browser window to the
foreground, rather than retrying or concluding the tool is broken. This is
Chrome's power-saving design; there is no CDP-level workaround
(Emulation.setFocusEmulationEnabled was tried and does not help — don't go down
that road).
| Error | Meaning | What to do |
|---|---|---|
NO_EXTENSION | Extension not connected | The CLI already retried with backoff for ~35s; if it still fails, stop waiting (the script crashed and the heartbeat can't wake it) and ask the user to check the extension |
REF_STALE | Numbers expired | Run owb page again |
AMBIGUOUS_TAB | Several tabs match, or you gave no --tab and the active tab is the user's own page | owb tab list for ids, then pass --tab. The latter usually means you used --active false without --tab — use the tabId open returned |
BAD_ARGS | Wrong or duplicated arguments | The message lists the valid values; if unsure of an argument name, check references/commands.md |
PAUSED | The page is stopped at a breakpoint | owb debug resume |
FRAME_NOT_FOUND | iframe not found | owb frames for the list; contextId:null frames are cross-origin and can't be evaluated |
TIMEOUT | Busy main thread (most common) / breakpoint / modal | Try owb wait --network-idle true and retry; to wait longer use --timeout-ms (not --timeout). When even shot times out, first add owb eval "1+1" to fork the diagnosis |
ctl call timeout | You hit the CLI envelope | You raised tool-internal waiting past 110s — also pass --timeout <seconds> |
NEED_TASK | No active task | owb task begin "<title>" and retry |
NOT_RECORDING | The recorder was destroyed | You called har discard first; discard throws the recording away, and the data is gone — record again, then finish with har save alone |
FORBIDDEN + interstitial | Chrome security page | No workaround; the user must deal with the certificate warning |
FORBIDDEN + not in an OWB-managed group | You are closing a user's own tab | This is the guardrail, not a fault. Check the tabId with owb tab list; only consider --force true if it really should be closed |
16 groups, 80 commands — names only, so you know what exists. Arguments are in
references/commands.md, or run owb help <group>.
| Group | Commands |
|---|---|
| core | open back forward reload page shot click (--mouse for real mouse events) fill keys scroll eval wait frames status cdp |
| tab | tab list / find / close / close-group |
| net | net start / stop / list / detail / initiator / capture |
| har | har start / save / discard / status / to-replay / diff / assert |
| hook | hook preset / fn / remove / status / logs |
| debug | debug break-xhr / break-fn / break-remove / stack / step / resume / console; oracle is top-level (owb oracle, not owb debug oracle) |
| script | script list / source / search / patch / unpatch / watch / watch-remove |
| verify | verify signer / replay / evidence |
| cookie | cookie get / set / delete |
| state | state save / load / list / delete (raw primitives: owb call export_state / import_state) |
| env | env set / reset / compare |
| file | download, upload, pdf are top-level; only file fetch takes the file prefix |
| task | task begin / end / list |
| flow | flow save / run / list |
| human | handoff, wait-user |
| daemon | daemon-status, daemon-stop, reload-ext |
The net/har/hook/debug/script/verify groups are the debugging ladder — see references/debugging.md.
Versions iterate quickly, and a stale install is a real source of avoidable failures (this document itself is an installed copy that only updates on reinstall). After the session's main task is finished — once per session, not after every command — run:
owb update checkThis is a background chore, silent by default. Don't announce that you are about to check, and don't narrate the result — the only outcome worth a sentence to the user is an actual update. Three outcomes:
✓ up to date → say nothing at all (no "already on the latest version",
no mention that a check happened).? could not reach the npm registry → say nothing; never retry or
troubleshoot this on your own.⬆ update available → the output lists the exact upgrade steps
(npm i -g open-web-bridge@latest, then owb skill install to refresh this
skill, then owb daemon-stop so the new daemon starts on the next command;
a store-installed extension updates itself, a Load-unpacked one needs
owb reload-ext). Tell the user a new version exists and offer to run the
upgrade — run the steps only after they agree, then run owb to confirm
both ✓s before reporting success.These actions land on a real person's account, and many cannot be undone.
Ask the user first: sending messages, posting, commenting, sending email, submitting forms, placing orders or paying, deleting content, changing account settings, authorizing third parties.
Never do: enter passwords, verification codes or payment details (hand off instead), bypass human-verification challenges, open private pages unrelated to the task (direct messages, mail, billing).
Reverse-engineering and traffic analysis are for the user's own sites, or sites the user has explicitly asked you to investigate. Circumventing anti-abuse controls or breaking into someone else's system is out of scope.
Reading and browsing: just do it. For anything that changes state, say what you intend to do and wait for a yes.
© woniu9524, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 5 other files (references) in owb-skills/owb of woniu9524/open-web-bridge.
Open the folder on GitHubat commit 27c9ebe
Open Web Bridge Browser Control next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Open Web Bridge Browser Control this skillwoniu9524/open-web-bridge | 121 | — | ~8.1k | Automated safety check: Pass | MIT | |
| Chrome Cdpaiguicai/MCP-Gateway | 158 | — | ~2.1k | Automated safety check: Pass | MIT | |
| Dev Browser AutomationMemTensor/MemOS | 12k | 3 repos | ~1.7k | Automated safety check: Pass | Apache-2.0 | |
| OpenCLI Adapter Authorjackwener/OpenCLI | 30k | 1 repos | ~3.4k | Automated safety check: Pass | Apache-2.0 | |
| Skyvern Browser AutomationSkyvern-AI/skyvern | 23k | 1 repos | ~2.9k | Automated safety check: Pass | AGPL-3.0 | |
| OpenCLI Browser Controljackwener/OpenCLI | 30k | 2 repos | ~7.3k | Automated safety check: Pass | Apache-2.0 |
aiguicai/MCP-Gateway
Browser automation and debugging through bundled Chrome DevTools Protocol.
MemTensor/MemOS
Automates a real browser through short TypeScript scripts that keep page state between runs, for navigating, filling forms, taking screenshots and extracting data.
jackwener/OpenCLI
Walks through writing an OpenCLI adapter for a new site or a new command on an existing one, from first recon and field decoding to coding and verification.
Skyvern-AI/skyvern
Picks the right Skyvern CLI command for a web task, from quick yes/no checks to reusable multi-page workflows, instead of falling back to plain page fetching.
jackwener/OpenCLI
Drives a real Chrome window through the opencli CLI to inspect pages, fill forms, click through logged-in flows and extract data from structured command responses.
cloudflare/moltworker
Drives headless Chrome through Cloudflare Browser Rendering over a CDP WebSocket to take screenshots, navigate and scrape pages, and record multi-page videos.
Categories
Lets the agent operate your own logged-in browser through the owb command to read pages, fill forms, audit sites and capture network traffic. The owb command-line tool controls the browser you are using right now, with your logged-in accounts, settings and open tabs. That gives the agent access to pages behind a login and to actions that need your identity.
Open Web Bridge Browser Control fits situations like: reading or comparing pages that need your existing login; filling a form or walking a multi-step flow in your real browser; debugging a broken front end or auditing responsive and accessibility behavior; capturing network traffic to see how a request is built or signed.
Run `npx skills add woniu9524/open-web-bridge --skill owb -a claude-code`. Or copy the skill folder (owb-skills/owb in woniu9524/open-web-bridge) into .claude/skills/owb in your project. Claude Code loads it when a task matches its description.
Run `npx skills add woniu9524/open-web-bridge --skill owb -a codex`. Or copy the skill folder (owb-skills/owb in woniu9524/open-web-bridge) into .agents/skills/owb in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add woniu9524/open-web-bridge --skill owb -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/owb, .gemini/skills/owb, .github/skills/owb and .opencode/skills/owb in your project.
Going by SKILL.md and its folder, Open Web Bridge Browser Control needs the command-line tools its instructions call (npm and node). Our summary lists: The owb CLI from the open-web-bridge npm package; The Open Web Bridge extension connected to your browser; The owb daemon, which the first command starts.
SKILL.md names 1 domain. In commands or code: news.ycombinator.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Open Web Bridge Browser Control is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.1k tokens (SKILL.md is roughly 32k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 19k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Open Web Bridge Browser Control: Chrome Cdp (aiguicai/MCP-Gateway, 158 stars), Dev Browser Automation (MemTensor/MemOS, 12k stars), OpenCLI Adapter Author (jackwener/OpenCLI, 30k stars) and Skyvern Browser Automation (Skyvern-AI/skyvern, 23k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
woniu9524 (a GitHub user) maintains it in woniu9524/open-web-bridge, which has 121 GitHub stars. The repository was last updated on August 23, 2026.
Source: woniu9524/open-web-bridge on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.