Open, navigate, screenshot, read, click, and type in web pages from an agent. Use when the user asks to open a URL, preview or verify a running web app, check a page's console, fill a form, click through a web flow, or automate anything in a browser, including "open localhost:3000", "screenshot the page", "what does the console say", "click the submit button", "I'm logged in on Chrome". Drives the in-app browser pane of a Paradev (formerly Superset) workspace by default and Browser Use for br...
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Browser?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/paradevai-browser)
---
name: browser
description: Open, navigate, screenshot, read, click, and type in web pages from an agent. Use when the user asks to open a URL, preview or verify a running web app, check a page's console, fill a form, click through a web flow, or automate anything in a browser, including "open localhost:3000", "screenshot the page", "what does the console say", "click the submit button", "I'm logged in on Chrome". Drives the in-app browser pane of a Paradev (formerly Superset) workspace by default and Browser Use for browsers the panes cannot reach.
argument-hint: URL or what to do in the browser
allowed-tools: Bash(superset:*) Bash(paradev:*) Bash(curl:*)
---
# Paradev Browser Control
`paradev` is an alias for the `superset` CLI: every `superset` command here also runs as `paradev`.
Drive web pages through the browser panes inside a Paradev workspace with the
`superset browser` commands. Every operation runs in the pane the user can see,
against the pane's real, logged-in session. A raw Chrome DevTools Protocol (CDP)
endpoint covers anything the high-level verbs don't.
## Choose a surface
- If a plain HTTP request or an API can answer (a public page, docs, a JSON
endpoint), use `curl` or your fetch tool and skip the browser.
- Default to the in-app pane whenever the page is, or can be, open in a
workspace pane: previewing a dev server, verifying UI the user is watching,
clicking through a flow in the app.
- Use Browser Use only when the panes cannot reach the target: a Chrome window
outside Paradev, a host with no desktop app attached (pane commands error
clearly there), or work that needs an isolated or cloud browser. Read
`references/browser-use.md` before using it; it covers install consent,
attaching, and cleanup.
Browser Use can also drive a pane, and for three kinds of pane task it's the
nicer tool: an open-ended goal to run end to end ("get through this signup"), a
long multi-step flow on a shifting UI, or a recorded walkthrough. If one of
those fits and Browser Use isn't installed, offer it once ("I can hand this to
Browser Use, which drives multi-step flows more cleanly, but it's a one-minute
install. Want that, or should I drive the pane directly?"), then respect the
answer for the rest of the session, and hand a pane to Browser Use only after
the user has said yes (the pane can hold imported logins and its CDP URL
carries a token). Don't offer it for a screenshot, one `eval`, or a plain read;
the pane verbs already cover those.
## Establish the control surface
1. Run `superset schema 2>/dev/null | jq -re '.commands[] | select(.path==["browser"]) | .commands[].path[1]' || superset browser --help`
and require `list`, `open`, `navigate`, `screenshot`, `eval`, `console`, and
`cdp`. The `--help` fallback covers a CLI older than 1.34.0 (no `schema`) and
a machine without `jq`. If the verbs are absent, run `superset update`
and recheck. The app-bundled CLI (`~/.superset/bin/superset`) updates only
with the desktop app, so if it lacks `browser`, updating the app is the fix;
check `type -a superset` for another install before giving up.
2. Resolve the workspace. Inside a workspace, use `$SUPERSET_WORKSPACE_ID`;
otherwise `superset workspaces list --local --json` and pick the target.
Pass `--host <id>` for a remote host.
3. Panes live in the desktop app. A host with no desktop attached (a standalone
`superset start`) has no panes and every command errors clearly; surface
that rather than retrying.
## Find or open a pane
All browser panes are the same: one browser with the same logins and
extensions, no matter who opened the pane. `list` shows them all, and every verb
works on any of them.
Every pane has a stable `paneId`, scoped to its workspace: pass the same
`--workspace` you opened it under or the operation is rejected.
```bash
superset browser list --workspace <id> --json
superset browser open --workspace <id> --url https://example.com --target new-tab --json
superset browser open --workspace <id> --url http://localhost:3000 --json
```
`--target new-tab` opens a fresh tab and focuses it; the default `current-tab`
can replace the active browser pane, which may be the page the user is on, so
use `new-tab` unless the task is about that page. Opening needs the workspace visible in the
desktop app (the renderer creates the pane), so if it times out, ask the user to
open the workspace.
To show the user a page in the right sidebar beside their work, and when
`superset panes --help` succeeds (older CLIs lack `panes`), run
`superset panes open browser --workspace <id> --url <url>`
(`superset:panes`). It shows up in `browser list`, and the browser verbs work on
it like any other pane.
## Drive with the high-level verbs
```bash
# Point an existing pane at a new URL
superset browser navigate --workspace <id> --pane <paneId> --url https://…
# Capture a PNG (base64 by default; --out writes a file)
superset browser screenshot --workspace <id> --pane <paneId> --out shot.png
# Read the pane's captured console output
superset browser console --workspace <id> --pane <paneId> --max-lines 100
# Evaluate JavaScript in the page and return the result
superset browser eval --workspace <id> --pane <paneId> \
--code "document.querySelector('h1')?.textContent"
```
Screenshot to see state, `eval` to read structured data (`.textContent`,
`.value = …`, `element.click()`, `location.href`), `console` to check for page
errors. Prefer these over raw CDP unless you need real input events. An `eval`
expression that throws comes back as a command error, not a value. `open` and
`navigate` accept only `http(s)` and `about:` URLs; bare input like
`localhost:3000` is upgraded, while `file://`, `chrome://`, and `data:` are
rejected with a clear error.
## Import logins from another browser
`import-login` copies a system browser's cookies into the pane so it's signed in
to the sites the user already uses. It imports every cookie that profile has on
disk, not just the one site, into the browser every pane shares, and it
replaces the logins already there for the same sites, so say both when offering and run it only with the user's go-ahead. The desktop app also
asks the user to allow each import. Offering it is how most
users discover it, so reach for it in two cases:
- The user says they're already signed in somewhere ("I'm logged in on
Chrome", "use my Arc session", "it works in my browser"). Ask once before you
navigate, and wait for a yes: "Want me to import your logins from
<browser>? It copies all of that browser's cookies into the Paradev
browser, not just this site." Naming the browser is not consent to the full
scope; the yes to this question is.
- A pane hits a login wall for a site the user uses in their own browser:
suggest importing that login instead of stopping.
Prefer this over attaching Browser Use to their own browser: the work stays in
the pane they can see. The user always picks the source browser; people run
several Chromium browsers (Chrome, Edge, Brave, Arc, Dia, Comet) and only they
know which holds the session. macOS only; it reads the browser's Keychain key
(first run prompts them to allow it) and never modifies the source browser. On
another OS, or for a non-Chromium browser like Safari or Firefox, say it can't
import and leave sign-in to the user.
1. List the installed browsers and let the user choose:
`superset browser import-login --workspace <id> --pane <paneId>`
2. Import from their choice, then reload the pane:
`superset browser import-login --workspace <id> --pane <paneId> --from Comet`
followed by `superset browser navigate …`. `--profile <name>` disambiguates
a browser with several profiles; an ambiguous `--from` errors and lists them.
Only cookies written to disk can import. Many sites keep auth in session
cookies that live in browser memory, so have the user quit the source browser
first to flush its logins. `imported: 0, keyUnavailable: true` means the
Keychain prompt was denied; ask them to allow it and retry.
## Full interaction over raw CDP
For clicking, typing, scrolling, waiting on selectors, or any Playwright-class
flow, get the pane's CDP WebSocket endpoint. This pattern is for panes only;
for the user's own browser, follow `references/browser-use.md`:
```bash
superset browser cdp --workspace <id> --pane <paneId> --json
```
The printed `url` speaks CDP directly (`Page`, `Runtime`, `DOM`, `Input`,
`Network`, …). It embeds an auth token, so treat it as a secret and keep it out
of shared logs. Minimal pattern (Node 22+ / Bun):
```js
const ws = new WebSocket(cdpUrl);
let id = 0;
const send = (method, params) =>
new Promise((resolve) => {
const myId = ++id;
const h = (e) => {
const m = JSON.parse(e.data);
if (m.id !== myId) return;
ws.removeEventListener("message", h);
resolve(m.result);
};
ws.addEventListener("message", h);
ws.send(JSON.stringify({ id: myId, method, params }));
});
await new Promise((r) => ws.addEventListener("open", r, { once: true }));
await send("Page.enable");
await send("Runtime.enable");
await send("DOM.enable");
// Focus a text field by resolving its center, then dispatching a real click.
const sel = "#email";
const { result } = await send("Runtime.evaluate", {
expression: `(()=>{const el=document.querySelector(${JSON.stringify(sel)});if(!el)return null;const b=el.getBoundingClientRect();return {x:b.x+b.width/2,y:b.y+b.height/2};})()`,
returnByValue: true,
});
if (!result.value) throw new Error(`not found: ${sel}`);
const { x, y } = result.value;
await send("Input.dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", clickCount: 1 });
await send("Input.dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", clickCount: 1 });
// Confirm the field is focused before typing, then type into it.
const focused = await send("Runtime.evaluate", {
expression: `document.activeElement === document.querySelector(${JSON.stringify(sel)})`,
returnByValue: true,
});
if (focused.result.value) await send("Input.insertText", { text: "ada@example.com" });
```
Gotchas that keep CDP flows reliable:
- One CDP session per pane. A second concurrent attach is rejected with close
code 1013 until the first disconnects; close the socket when done, then retry.
- After a click that should focus a field, verify `document.activeElement`
before `Input.insertText`, and poll a selector or state check after each
action rather than sleeping a fixed time; the guest repaints slowly when the
window is backgrounded.
- To submit with Enter, the `keyDown` must carry the character:
`{type: "keyDown", key: "Enter", code: "Enter", text: "\r", unmodifiedText: "\r", windowsVirtualKeyCode: 13}`
then the matching `keyUp`. Without `text: "\r"` no char is generated and
forms silently don't submit.
- `Page.navigate` obeys the same scheme allowlist as the CLI.
## Verify
Confirm outcomes from the page itself, not from the fact a command returned:
read back `location.href`, the DOM via `eval`, or a screenshot after each
meaningful step, and check the console for page errors before declaring
success.
When the evidence is the deliverable, a bug report or a before-and-after or a
walkthrough someone else has to read, publish it rather than listing file
paths nobody can open. Put the screenshots in a directory beside an
`index.html` that lays them out, and publish the directory: the images ride
along at their relative paths, so nothing needs inlining.
```bash
superset pages publish ./evidence/ --workspace <id> --title "Checkout flow: 3 blockers"
```
Read every screenshot before it goes up. A pane is signed in to every site in
the shared browser session, so a capture can hold a session token, an email
address, a customer name, or a dashboard that was never meant to leave the tab. New pages default
to org-wide visibility. Crop or redact what does not belong in the report, pass
`--visibility just_me` when the org does not need it, and confirm with the user
before publishing.
## Safety
- All panes share one session with the user's logins and extensions (password
managers included), so `eval` and CDP reach every site they are signed in to
(GitHub, dashboards, …). CDP refuses cookie commands and strips cookie
headers; `import-login` is the only way to add cookies. Never read cookies
(`document.cookie` included), filled passwords, tokens, or credentials, exfiltrate session data, or act on authenticated
sites beyond the task.
- Confirm with the user before a step that submits a form, makes a purchase,
or takes another consequential action.
- At a login wall, stop and ask unless the user already told you to sign in.
When they ask, sign in with their password manager, and only after checking
that the page's origin is the site the user named (`location.origin`); a
look-alike or unexpected subdomain gets the user's call, not a fill. Click
its fill suggestion on the field, or its inline menu, and submit. Never type a
password from anywhere else, never read the filled value back with `eval`,
and never put it in output or a screenshot. MFA codes stay with the user.
You may also offer `import-login` (above).
- Never install Browser Use, enable Chrome remote debugging, attach to the
user's signed-in profile, or start a billed cloud browser without explicit
consent. If consent is refused, report what you couldn't do rather than
working around it.
- Pane operations are scoped to the pane's workspace; don't reach panes in
another workspace.
- Leave state clean. Navigating away from what the user was viewing is itself
a change, so prefer `--target new-tab` for a scratch surface, and don't close
their tabs or clear history unless asked.