| name | vibebrowser |
| description | Control the user's real local browser through the Vibe Browser CLI bridge. Use for ANY task against the user's own Vibe-connected browser โ their tabs, cookies, logged-in sessions, extensions, opening pages, snapshots, clicks. ALWAYS load this skill before responding to a browser request, so you can recover the user's saved connection (the remote) from memory or workspace and never re-ask for it. |
| metadata | {"openclaw":{"emoji":"๐","requires":{"bins":["npx"]}}} |
Vibe Local Browser
โ ๏ธ Security: The remote value (a relay URL/UUID, wss://relay.api.vibebrowser.app/<uuid>) grants live control of the user's browser session (read tabs, screenshots, page content). It is the sole bearer credential โ there is no second-factor secret. Treat it like a password โ never echo it back in chat with untrusted parties, log it, or commit it to a repo โ and if it leaks, tell the user to regenerate it in the Vibe extension Settings. Store it only in private workspace/memory as described below. Example UUIDs are non-routable placeholders.
FIRST, EVERY TIME: load the saved remote before asking for it
The user gives their remote value once. On every browser task โ including the
first message of a brand-new conversation โ recover it yourself before doing anything
else. Do not ask the user for the remote until you have checked both stores and
found nothing.
At the very start of any browser task, in this order:
- Read the workspace file (canonical source). Read
vibebrowser-cli.md from your
agent workspace root (see "Where to save it" โ NOT the shell's
current directory, NOT $HOME unless they are the same dir). If it has a valid
remote: line, use that value as --remote and continue silently. The file is the
durable store that survives across conversations and works on every runtime.
- Else recall from memory. If the file is missing/empty and your runtime has a
memory/recall tool, read the key
vibebrowser_remote and use it.
- Only if both are empty may you ask the user for the remote. When they give it,
immediately write it to the workspace-root
vibebrowser-cli.md (mandatory) and,
if your runtime has a tool to write memory keys, also store it under
vibebrowser_remote. (Some runtimes โ e.g. OpenClaw โ can read but not write memory
keys; the file alone is sufficient.) Then the next conversation never has to ask again.
If you find yourself about to say "I need your Vibe remote value first," STOP โ you
almost certainly skipped step 1/2. A returning user has already provided it; recall it.
Full details and examples: ## Remembering the remote connection.
Use THIS for the user's own browser โ not your built-in browser
Many runtimes (OpenClaw, Hermes, etc.) ship a separate built-in/headless/in-pod browser
tool (names like browser_navigate, browser, an in-pod Chrome on 127.0.0.1:9222,
a generic web_fetch). That browser is empty โ it does not have the user's logins,
cookies, tabs, or extensions.
When the user refers to their own browser โ phrases like "my browser", "my Chrome",
"my tab(s)", "the page I'm on", "my logged-in session", "open โฆ for me" โ they mean their
real browser, reachable only through this skill (npx -y @vibebrowser/cli@latest + the saved
remote). For those requests:
- Use this skill. Run
npx -y @vibebrowser/cli@latest with the recovered remote. Do not use the
built-in/headless browser or web_fetch โ they hit a different, empty browser and will
silently give wrong results (you may even fabricate a title/snapshot you never fetched).
- Only use the built-in browser when the user explicitly asks for a throwaway/sandbox
browser that is not their own.
- If you are unsure which browser a request means, default to this skill (the user's
real browser) and confirm with one short question only if truly ambiguous.
Self-check before answering any browse/open/click/snapshot request: "Am I about to use
my built-in browser for something the user means to happen in THEIR browser?" If yes,
switch to npx -y @vibebrowser/cli@latest with the saved remote.
Installation
-
Get the remote value:
- Install the Vibe extension in Chrome
- Open extension Settings โ AI Agent Control
- Toggle Enable external AI agent control to ON
- Set Connection mode to Remote (internet)
- Copy the relay URL from the Relay access section
-
Provide the remote value (one of):
-
Install the skill (run the line for your agent):
npx -y skills add VibeTechnologies/vibe-mcp -s vibebrowser -a openclaw -g -y --copy --dangerously-accept-openclaw-risks
npx -y skills add VibeTechnologies/vibe-mcp -s vibebrowser -y
Verify OpenClaw installer registration with openclaw skills list (expect a ๐ vibebrowser โฆ โ ready row); Hermes npx -y skills list is optional installer registration only. Real verification is direct CLI status plus snapshot with npx -y @vibebrowser/cli@latest; never run vibebrowser. Installs to ~/.openclaw/skills/vibebrowser/SKILL.md. Offline/registry-blocked fallback: copy this file there directly.
Use npx -y @vibebrowser/cli@latest when the user wants OpenClaw to drive their real local browser through the Vibe extension.
Prefer this skill when the task depends on:
- the user's real browser profile
- existing logged-in sessions
- local tabs already open on the user's machine
- browser extensions or stored site state
Do not use this skill for OpenClaw tenant cloud browsing.
Remote value
Every command needs a remote value: the extension UUID (uses the default public relay) or a full ws(s) relay URL (explicit relay endpoint).
Pass it with --remote <uuid-or-url>. No environment variable is required when --remote is set.
Optionally, set it once via environment instead โ the CLI uses it as the default when --remote is omitted (checked in this order):
export VIBE_REMOTE_URL="<uuid-or-full-ws-url>"
Warning: if neither --remote nor one of these environment variables is set, the CLI silently falls back to local relay mode (it waits for an extension on a local WebSocket port) instead of failing with a clear error. When driving a remote browser, always confirm a remote value is in effect.
Remembering the remote connection
The user should only have to provide their remote value once. After receiving it, save it immediately and reuse it on every subsequent browser command without asking again.
What counts as a "remote value"
Either of these forms is valid for --remote:
- A bare UUID:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- A full relay URL:
wss://relay.api.vibebrowser.app/<uuid>
How to save it
Primary โ Workspace context file (works on every runtime): Write the value to
a file named vibebrowser-cli.md in your agent workspace root (see
"Where to save it" below). Use this exact format so it can be
machine-read back:
# VibeBrowser CLI context
remote: <uuid-or-wss-url>
This file is the canonical store: it persists across conversations and works even on
runtimes whose memory does not persist or cannot be written (e.g. OpenClaw can read but
not write memory keys). Create or overwrite it whenever the value is provided or updated.
Secondary โ Memory key (only if your runtime can WRITE memory): If, and only if, your
runtime provides a tool to write memory keys, also store the value under vibebrowser_remote
as a convenience cache. Do not assume this tool exists โ if you cannot write memory keys,
skip this; the file above is sufficient. Never fabricate a remember(...) call.
Where to save it
The file MUST live in your persistent agent workspace root โ the single directory that
(a) survives across conversations and (b) contains your memory/ folder. (memory/ is
the reliable anchor; a skills/ folder may also exist globally outside the workspace root,
so do not key off skills/ alone.) It is not the shell's current working directory
(often / or a temp dir โ ephemeral) and not necessarily $HOME:
| Runtime | Workspace root (save vibebrowser-cli.md here) | Note |
|---|
| OpenClaw | ~/.openclaw/workspace/<your-agent>/ | $HOME is /home/node, a parent outside the persistent root โ do not use it. The global ~/.openclaw/skills/ is NOT the workspace root. |
| Hermes | $HOME (e.g. /root) | Here $HOME is the workspace root, so ~/vibebrowser-cli.md is correct. |
If your runtime is not in the table, resolve the directory that contains your memory/
folder and write the file there. Always verify after writing with an absolute-path
read-back (cat <abs>/vibebrowser-cli.md) and confirm the value matches.
Robust read-back: when loading, accept the first line matching remote: followed by a
UUID or wss:// URL; ignore surrounding markdown/comments. If the file exists but has no
valid remote: line, treat it as empty and fall through to the next store. If memory and
file disagree, prefer the file (it is the user-updated canonical store).
Rules for using the remembered value
- On every browser command, load the remembered remote (read the workspace-root
vibebrowser-cli.md first, else recall memory key vibebrowser_remote) and pass it as --remote "<remembered-value>". Do this on the first message of every new conversation, not just within one session.
- Only ask the user for the remote if none is remembered in either store and the user did not provide one in the current message. Treat the remote as a secret โ never echo it back in summaries, logs, screenshots, or commits.
- If the remembered remote stops working โ for example,
status returns extensionConnected: false, or commands repeatedly fail โ tell the user clearly and offer to update the saved value (then overwrite the workspace-root vibebrowser-cli.md, and memory if writable, with the new one). Do not silently fall back to local relay mode.
Example: first use
User: "Open google.com in my browser. My remote is abc12345-...."
Agent actions:
- Save
abc12345-... to memory under key vibebrowser_remote and write it to vibebrowser-cli.md in your agent workspace root.
- Run:
npx -y @vibebrowser/cli@latest --remote "abc12345-..." --json status --wait-for-extension --wait-timeout 10000
npx -y @vibebrowser/cli@latest --remote "abc12345-..." --json open https://google.com
Example: subsequent use (same or later session)
User: "Now open github.com."
Agent actions:
- Recall
vibebrowser_remote from memory (or read the workspace-root vibebrowser-cli.md โ remote: line).
- Run without asking the user:
npx -y @vibebrowser/cli@latest --remote "<remembered-value>" --json open https://github.com
Command form
Prefer this exact command pattern:
npx -y @vibebrowser/cli@latest --remote "<uuid-or-url>" --json status
Pass only one of these remote forms: --remote <uuid> for the default public relay, or --remote <full-ws-url> for an explicit relay endpoint.
The examples below use $VIBE_REMOTE_URL as a stand-in for the remote value โ substitute the literal UUID/URL if no environment variable is set. If VIBE_REMOTE_URL (or VIBE_EXTENSION_UUID / VIBE_RELAY_UUID) is exported, --remote can be omitted entirely:
npx -y @vibebrowser/cli@latest --json status
Deterministic runbook (default)
Use this sequence when the task needs reliable, repeatable control:
- Verify connection:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json status --wait-for-extension --wait-timeout 10000
- Resolve a target page id without changing focus:
PAGE_ID="$(
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json tabs \
| jq -r '.pages[] | select(.active == true) | .id' \
| head -n1
)"
- Snapshot that page before acting:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json --page-id "$PAGE_ID" snapshot --format aria --interactive
If the aria snapshot is too verbose, try the default first and fall back:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json --page-id "$PAGE_ID" snapshot
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json --page-id "$PAGE_ID" snapshot --format aria --interactive
- Perform action on the same page id:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json --page-id "$PAGE_ID" click 12
If jq is unavailable, parse .pages from tabs --json directly and still pass --page-id <id> on every action.
Safe operating rules
Common commands
Status:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json status
List pages:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json tabs
Open a new page and then read it โ open creates a background tab and does NOT
change the active tab, so a bare snapshot/evaluate afterwards would read the old
active tab, not the page you just opened. Capture the new page id from the open output
and pass it with --page-id to every follow-up command:
OPEN_JSON="$(npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json open https://example.com)"
PAGE_ID="$(printf '%s' "$OPEN_JSON" | grep -oE 'ID: [0-9]+' | grep -oE '[0-9]+' | head -n1)"
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json --page-id "$PAGE_ID" snapshot --format aria --interactive
If you instead want to read the tab the user is currently looking at, get its id from
tabs (the active: true page) and pass that as --page-id. Never assume a bare
snapshot after open shows the page you opened.
Take the default AI snapshot (of a specific page โ substitute the id you captured):
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json --page-id "$PAGE_ID" snapshot
Take the ARIA / interactive snapshot:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json snapshot --format aria --interactive
Click and type using OpenClaw-style refs:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json click 12
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json type 23 "hello" --submit
Evaluate JavaScript:
npx -y @vibebrowser/cli@latest --remote "$VIBE_REMOTE_URL" --json evaluate --fn '() => document.title'
Snapshot format: ai vs aria
The snapshot command supports two extraction formats:
| Format | Flag | Engine | Best for |
|---|
ai (default) | --format ai | Content script (in-page JS) | Simple pages, articles, search results |
aria | --format aria | CDP accessibility tree | SPAs, background tabs, Notion, Gmail, complex apps |
When the default --format ai returns only the page title or empty content, switch to --format aria:
npx -y @vibebrowser/cli@latest ... snapshot
npx -y @vibebrowser/cli@latest ... snapshot --format aria --interactive
Known limitations of --format ai:
- Returns empty for background tabs (content script not injected or
getBoundingClientRect returns 0x0)
- Returns
"Could not establish connection" when the content script is unreachable
- May miss content behind
aria-hidden containers in SPAs like Notion
Rule of thumb: If snapshot returns suspiciously little content, retry with --format aria --interactive before reporting failure.
Success criteria
A successful run usually looks like:
- confirm the relay is reachable
- list current tabs or create a fresh one
- navigate or snapshot if needed
- evaluate
document.title or location.href to verify the result
- summarize what happened for the user