Co Browser logo

Co Browser

OrganizationPopular
openonion
co-browser

Drive one persistent, logged-in browser from the shell with `co browser`. Use when the user asks to open a page, log into a site, scrape/extract page content, fill forms, upload files, take screenshots, or automate any browser task — solo or with multiple agents sharing the browser.

Overview

Publisheropenonion
Repositoryconnectonion
Skill nameco-browser
Stars
1.5K
Forks
218
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by openonion on GitHub. Read the source before you install it.

Installation

Install the Co Browser AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/openonion/connectonion.git /tmp/connectonion
mkdir -p .claude/skills
cp -r /tmp/connectonion/connectonion/useful_skills/co-browser .claude/skills/co-browser
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Co Browser in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Co Browser on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Co Browser is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

co browser Skill

Drive one real browser from the shell. The browser stays open between commands — cookies and logins persist until co browser close. Every terminal on the machine talks to the same daemon: one browser, one board, no matter where you type.

Always read the output, not just the exit code. A failed action (selector not found, browser not open) often returns exit 0 with the error text on stdout — never chain co browser ... && next_step as your only success check.

Step 1: Identity — who are you?

The daemon attributes every tab to a caller and uses that identity to stop two agents from silently driving the same page. Set CO_WHO explicitly — shell state does not survive between your tool calls, so a lone export is lost. Either prefix every command:

bash
CO_WHO=alice co browser go_to example.com

or keep the export and the commands in the SAME shell invocation:

bash
export CO_WHO=alice && co browser go_to example.com && co browser get_text

(Some environments — e.g. Claude Code background jobs — are auto-identified, but setting CO_WHO is always safe and never worse.) An anonymous caller still works but gets no contention protection.

Step 2: Check the board, then claim a tab

One task = one tab. Before touching the browser, see what's already happening:

bash
co browser status      # browser state, headless flag, last command, the board
co browser tab ls      # every tab: who owns it, purpose, last command (--json for scripting)

Solo (nobody else on the board) — bare commands run on the shared main tab, no ceremony:

bash
co browser go_to example.com     # runs on 'main'
co browser get_text              # still 'main'

Concurrent (another agent or a second parallel task exists) — open your own tab and add -t <name> to EVERY command, including do. Pick an explicit literal tab name — a NAME=$(...) capture is lost between tool calls:

bash
CO_WHO=alice co browser tab open scrape --for "scrape pricing" --needs 10m
CO_WHO=alice co browser -t scrape go_to example.com/pricing
CO_WHO=alice co browser -t scrape do "extract every plan and its monthly price"
CO_WHO=alice co browser tab close scrape       # release when done

--needs is your estimate of how long you will hold the tab (30s, 10m, 2h). Say it: other agents leave your tab alone until then, and co browser tab ls shows them when you expect to finish. Without it, two minutes of silence is enough for another agent to take your tab — which is wrong when you are waiting on a slow page or a human.

When delegating browser work to subagents, write each subagent's tab name into its prompt — parallel agents share the one logged-in browser through separate tabs.

When the site opens its own tab — a target="_blank" link, a "view invoice" button, a payment popup — that page belongs to no session, so tab ls cannot show it as yours and every -t command keeps running in the page you were on. tab ls tells you one exists; these two reach it:

bash
co browser list_pages                  # every real page, with who is driving it
co browser -t mytask switch_page 1     # your session now drives page 1
co browser -t mytask get_text          # ...and every command after it
co browser -t mytask switch_page 0     # back to where you were

Indexes come from list_pages and shift when pages open or close, so read the list again rather than reusing a number from earlier in the task. A page another session is driving is refused by name — two agents cannot share one page, the same rule as tabs.

Step 3: Pick the right verb

Two ways to drive the browser — pick per command, mix freely:

Direct functions (deterministic, instant, free — no LLM). Use for every step you can spell out:

bash
co browser go_to https://example.com/login        # https:// assumed if omitted
co browser get_text                               # visible page text
co browser get_links_from_page                    # every link, one per line
co browser take_screenshot /tmp/page.png          # saves a PNG, prints its path
co browser click_element_by_selector "button" --index=0 --text="Send message"
co browser type_text_by_selector "#email" "user@example.com"
co browser fill_text_by_selector "#invite" --stdin < invite.txt
co browser scroll                                 # scroll the page (scroll 3 = fewer steps)
co browser get_current_url

co browser help prints the live list of every function with its arguments — trust it over any example here. Calling a function with wrong arguments returns its usage line: self-correct from that.

Typing into whatever already has focus — after a mouse_click into a field that no selector reaches cleanly — is keyboard_type, and pressing keys is keyboard_press. There is no type_text; that name returns "unknown command" and costs a round trip. Use fill_text_by_selector to replace a controlled input atomically, type_text_by_selector for humanized appending when a selector works, and keyboard_type when only focus is available.

keyboard_type appends — it does not replace. To overwrite a field: click into it, then keyboard_press "Meta+a" (macOS) and keyboard_press "Backspace" before typing, or you get 1:15 AM09:00 AM. And confirm the click actually landed inside the input: if it missed, Meta+a selects the entire page instead and your text goes nowhere while the screen turns blue. The screenshot after typing is the only way to tell those two outcomes apart. (The verb is scroll, not scroll_down; guessing a plausible name costs a round trip.)

Never hunt for an item by scrolling a feed. Infinite lists re-render as they lazy-load, so scroll position is not reproducible: the same scroll 6 lands somewhere different each time, Home/Meta+Home do not return you to the top, and the card you were aiming at moves between screenshots. Worse, clicking a per-item menu you located by eye can hit a neighbouring item — on a destructive action that is the wrong row deleted.

Extract the item's id once, then address it directly:

bash
# 1. one script over the whole list, returning ids -- not screenshots
co browser -t mytab run_page_script /abs/find-id.js     # match on text, return data-urn/data-id
# 2. open that item alone, where its controls cannot be confused with anything else
co browser -t mytab go_to "https://example.com/item/<id>/"

Six scroll-and-screenshot round trips became one script plus one go_to this way, and the per-item menu became unambiguous because the page held exactly one item.

do "<instruction>" (natural language — an AI agent sees the page and works out the steps). Use for judgment, not for steps you already know:

bash
co browser do "log in with the saved credentials and open my notifications"

Describe the end state, not the steps. A do is a full agent run — many LLM calls, possibly minutes — and the daemon is busy for its whole duration, so other commands queue behind it. Never wrap do in timeout: killing the client does not stop the run, it only orphans it (you pay for an answer nobody reads).

For state checks, prefer the free text probes first (exit 0 = matched):

bash
co browser get_current_url | grep "/feed"

Only when the state is visually ambiguous, fall back to a one-word do probe:

bash
co browser do "Look at the current page. Reply EXACTLY one of: STATE: feed | STATE: login | STATE: error"

Step 4: Verify with evidence

  • Routine checks are text checks: get_current_url / get_text after navigation and clicks. Reserve screenshots for evidence gates.

  • Irreversible actions (submit, post, publish, pay) get the full gate: screenshot before, act exactly once, screenshot after. Never click the same submit button twice — if the result is ambiguous, report uncertain state instead of retrying.

  • Long pages: dump once and read locally instead of re-extracting per chunk:

    bash
    co browser get_text > /tmp/page.txt    # one daemon round-trip
    sed -n '1,200p' /tmp/page.txt
  • To find stable selectors, save the DOM and grep it:

    bash
    co browser save_page_context li_composer
    grep -o 'aria-label="[^"]*"' ~/.co/browser_context/*li_composer*/page.html | sort -u
  • Site-specific DOM logic belongs in skill-local JS run with an absolute path (the daemon resolves relative paths against its own cwd, not yours):

    bash
    co browser run_page_script /path/to/project/.co/skills/<skill>/scripts/extract-items.js

Step 5: React to failures

Exit codes cover the daemon-level contract; the output text covers the action itself:

SignalMeaningWhat to do
exit 0, clean outputsuccesscontinue
exit 0, error text on stdoutthe action failed softly (e.g. "No element found for selector")read it, adjust selector or approach
exit 1the command raised (bad arguments print the usage line)self-correct from the message
exit 2usage error (bad flags, empty -t, tab misuse)fix the command syntax
exit 3unknown tabtab open the name first, then target it
exit 4tab busy — another agent is mid-task theredo NOT retry the same command — the error prints the exact commands to run instead; follow them

The exit-3/4 error messages ARE the documentation — they always carry the current recovery steps, so trust them over this table.

When a tab blocks you, read co browser tab ls before deciding. Each tab shows when its owner expects to finish:

[scrape] https://...  who=alice  purpose='scrape pricing'  open 3m
   owner expects to finish by 14:20 (7m left) — leave it alone until then

Inside that window, open your own tab instead. Once it has passed, the tab is free and closing it is a courtesy — an estimate that ran out with the tab still open means that agent crashed, not that it is still working. Every agent here is cooperative; tidying up after a dead peer is expected, taking a live agent's page is not.

Logging in

The default is a visible window — exactly what you want for a first login. Never automate credentials: open the login page, tell the user, then check with short waits — one short check per tool call, not one long blocking loop (a long loop hits the tool timeout, and every poll refreshes your claim on the tab, locking other agents out for its whole duration):

bash
co browser go_to https://site.com/login
# tell the user to log in in the visible window, then per check-in:
sleep 15 && co browser get_current_url | tail -1

Repeat the check a handful of times; if the URL is still the login page after that, stop and ask the user instead of looping forever. Logins live in the profile (~/.co/browser_profile/), not the daemon — they survive close and restarts. Log in once, stay logged in.

Headless: co browser --headless go_to ... — the mode is fixed by whichever command starts the daemon (status shows headless=true/false). To switch: co browser close, then relaunch.

Scripting hygiene

  • Wrap direct functions that might block in timeout 60 ...; never timeout a do.
  • Take the data line with tail -1 when output includes env banners.
  • Batch related commands in one tool call; never spend a whole call on a bare wait.
  • wait is in seconds — every other settle knob here is milliseconds (--wait_ms=2500), so wait 2500 looks right and means 41 minutes. It is capped at 60s and refuses anything longer. For a settle longer than a second or two you want a condition, not a sleep: wait_for_element / wait_for_text.
  • Uploads: try upload_file_by_selector 'input[type="file"]' <path> first; if the input is hidden behind a button, upload_file_after_click_by_selector '<button selector>' <path>.

Troubleshooting

  • "Where is my window?"co browser status says headless=true? An earlier command started the daemon headless. co browser close, rerun without the flag.

  • "daemon is busy" — a long do is holding the single-threaded daemon. Wait and retry; co browser status shows the last command once it frees up.

  • "Opening in existing browser session" / profile in use (shows a PID) — a manually opened Chrome or stale daemon holds the profile; kill that PID, retry.

  • TargetClosedError after a crash — the page died under the daemon; run co browser open_browser again before retrying the command.

  • "Chrome failed to start" — usually ssh/cron without a desktop session (run from a logged-in Terminal, or use --headless). Full launch log: ~/.co/browser.log.

  • Nuclear option — kill the daemon and let the next command start fresh (logins survive in the profile):

    bash
    pkill -f 'connectonion.cli.browser_agent[.]daemon'

Done checklist

  • Identity attached to every command (CO_WHO=... co browser ...)
  • Board checked (status / tab ls) before claiming a tab
  • Own tab used if any other agent/task shares the browser (-t on every command)
  • Output text read on every command, not just exit codes
  • Irreversible actions performed exactly once, with before/after screenshots
  • Tab closed (co browser tab close <name>) when the task is done
  • Browser left open for the next task unless the user asked to close

Frequently asked questions

What does the Co Browser AI skill do?

Drive one persistent, logged-in browser from the shell with `co browser`. Use when the user asks to open a page, log into a site, scrape/extract page content, fill forms, upload files, take screenshots, or automate any browser task — solo or with multiple agents sharing the browser.

Why use Co Browser on TypingMind?

Because you install it once and use it with any model. Co Browser is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Co Browser in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/openonion/connectonion/tree/main/connectonion/useful_skills/co-browser. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Co Browser?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Co Browser?

As many as you like. As long as a model supports skills, you can use Co Browser with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Co Browser AI skill free?

Yes. It is published on GitHub by openonion under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇