Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ff61153
Add the MCP server foundation for the TypeScript port of fast-chrome
afif-reap Sep 28, 2026
ab6ccaf
Add generic Browser Control, 1Password session, and verification skills
afif-reap Sep 28, 2026
265989d
Follow the browser-control skill and variable renames in the server
afif-reap Sep 28, 2026
78ebfa7
Record the one browser-control skill and its tested merge recipe
afif-reap Sep 28, 2026
84d25a0
Port the private transfer, vault reader and clipboard guard
afif-reap Sep 28, 2026
96e80f7
Keep a process's SQLite locks when it opens the same lock file again
afif-reap Sep 28, 2026
c5af109
Port the browser pool, preferences and startup to TypeScript
afif-reap Sep 28, 2026
8228592
Add the browser-control install, doctor and config commands
afif-reap Sep 28, 2026
005ce8d
Merge branches 'afif/ts-server-pool' and 'afif/ts-server-private' int…
afif-reap Sep 28, 2026
344ed26
Port the native server's tab tools, argument pipeline and stdio trans…
afif-reap Sep 28, 2026
18e7a55
Port the act_steps tests and share the MCP server wiring with in-memo…
afif-reap Sep 28, 2026
533f086
Port the lease routing, controller-prefixed handle and claim_browser …
afif-reap Sep 28, 2026
8f41d2a
Port the in-process shutdown tests: refusals, act_steps stops, bounde…
afif-reap Sep 28, 2026
a27f57e
Check the MCP surface against the captured Python tools, arguments, e…
afif-reap Sep 28, 2026
f4ef812
Port the paste_1password_field tool tests
afif-reap Sep 28, 2026
6b1f313
Port the stdio subprocess tests against a fake native host
afif-reap Sep 28, 2026
e7eb6c6
Map every server Python test in tests/server/parity/server.md and nam…
afif-reap Sep 28, 2026
f049e78
Wire the real server into the stdio entry and keep the bundle and shu…
afif-reap Sep 28, 2026
0f30318
Merge branches 'afif/ts-server-packaging' and 'afif/ts-server-skills'…
afif-reap Sep 28, 2026
e4b4db3
Integrate install, doctor and the skills with the real server and its…
afif-reap Sep 28, 2026
01f4f05
Merge Browser Control rename and verify the live native bridge
afif-reap Sep 28, 2026
b68826a
Keep shutdown, stable copies and native integers exact, and gate pari…
afif-reap Sep 28, 2026
ab79b02
Read stdin without a line bound, keep the user socket under the state…
afif-reap Sep 28, 2026
1ee3e5a
Merge origin/main (Browser Control 0.2.2 rename, PR #3) into afif/ts-…
afif-reap Sep 28, 2026
c7ea815
Recover an orphaned lease's owner from pool status, not the receipt
afif-reap Sep 28, 2026
cbc1049
Keep the zip installer out of its own directory and document both ins…
afif-reap Sep 28, 2026
92e0c82
Note the zip installer changes that the next release's reviewer steps…
afif-reap Sep 28, 2026
73668e5
Build releases and store uploads on Node 24
afif-reap Sep 28, 2026
4d66aea
Wait for real readiness in two load-sensitive tests
afif-reap Sep 28, 2026
eeeba16
Record the pre-PR round in the design
afif-reap Sep 28, 2026
4ecb51a
Serialize both native-host installers with one shared lock
afif-reap Sep 28, 2026
4788174
Pass the install socket to the server and name a socket mismatch in d…
afif-reap Sep 28, 2026
3adc597
Name the pool reap command in the pool reference
afif-reap Sep 28, 2026
c0d2812
Record the installer race round in the design
afif-reap Sep 28, 2026
c4603f0
Refuse an installer lock that another user could change
afif-reap Sep 28, 2026
3c405a0
Record the install lock trust round and steady the act wait-timeout test
afif-reap Sep 28, 2026
25dc747
Trust every directory above the installer lock and bound its cleanup
afif-reap Sep 28, 2026
b763a84
Write the manifest in the locked directory and record real state paths
afif-reap Sep 28, 2026
7f8721f
Apply one trusted-path rule to installers, the native host and the se…
afif-reap Sep 28, 2026
ad2677b
Check the skill's clients and trust only the uid's own manifest
afif-reap Sep 28, 2026
8b56ea8
Merge remote-tracking branch 'origin/main' into afif/ts-server
afif-reap Sep 28, 2026
cdeb189
Check every configured root from / down and use it by its canonical path
afif-reap Sep 28, 2026
efc0d03
Remove the temporary skill link after a failed rename
afif-reap Sep 28, 2026
46a840e
Wait for the extension handshake before the live browser test connects
afif-reap Sep 28, 2026
4e33783
Retry only a pending host in the live browser test
afif-reap Sep 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
data/public_suffix_list.dat -text
native/clipboard-guard/clipboard_guard.swift -text
21 changes: 20 additions & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,26 @@ jobs:
version: 10.33.2
- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run check

browser:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10.33.2
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright-core install chromium
- run: pnpm run build
- name: Verify disposable Chrome and native messaging
run: |
export BROWSER_CONTROL_SYNTHETIC_CHROME="$(node -p 'require("playwright-core").chromium.executablePath()')"
pnpm exec vitest run tests/security/private-input.browser.test.ts tests/server/packaging/live-browser.test.ts
2 changes: 1 addition & 1 deletion .github/workflows/chrome-web-store.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
- uses: actions/setup-node@v4
if: ${{ inputs.action == 'upload' || inputs.action == 'submit' }}
with:
node-version: 22
node-version: 24
cache: pnpm

- if: ${{ inputs.action == 'upload' || inputs.action == 'submit' }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
version: 10.33.2
- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run check
Expand Down
78 changes: 41 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,69 @@
# Browser Control

Browser Control is a Chrome extension and native messaging host that let an AI agent on your own computer work in your browser tabs. The extension does nothing until you install the local host.
Browser Control connects an AI agent to Chrome through an MCP server, a Chrome extension, and a native messaging host. It provides observed page actions, tab ownership, screenshots, and isolated Chrome for Testing leases.

- Homepage: <https://browser-control.pages.dev/>
- Privacy policy: <https://browser-control.pages.dev/privacy/>
- Support: <https://browser-control.pages.dev/support/>
## Install

## Identifiers
The npm package requires Node.js 24 or later on macOS or Linux. Isolated browser leases and private 1Password transfer require macOS and a separately installed cua-driver.

| What | Value |
| --- | --- |
| Chrome Web Store item ID | `dcnjjnecbhipdbngkhjppkckpkellmld` |
| Native messaging host name | `com.opzero.chrome` |
| Host and page protocol | version 2 |
| Unpacked extension ID | Derived by Chrome from the absolute path of the loaded folder. The manifest has no `key` field. |
1. Install the extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/dcnjjnecbhipdbngkhjppkckpkellmld).
2. Install the native host:

```sh
npx -y @op1/browser-control install
```

Chrome derives an unpacked extension's ID from its folder path, so every checkout or install location has its own ID. For example, the fast-chrome controller loads `~/.config/opencode/mcp/fast-chrome/op-chrome/dist/extension`, which gives the ID `pncpgnbanebkeopjghjleodgmphmmmcp`. The native host manifest must allow the ID of the extension that connects to it.
3. Open the extension popup and click **Reload host**.
4. Print the configuration for your MCP client:

## Install for users
```sh
npx -y @op1/browser-control config claude
```

1. Install Browser Control from the [Chrome Web Store](https://chromewebstore.google.com/detail/dcnjjnecbhipdbngkhjppkckpkellmld).
2. Install the native host. It needs Node.js 18 or later on macOS or Linux:
Replace `claude` with `opencode`, `codex`, or `cursor` for another client. Add the printed configuration to that client.
5. Check the installation:

```sh
mkdir -p ~/.config/opencode/skills/browser-control
curl -fsSL https://github.com/opzero1/browser-control/releases/latest/download/browser-control-skill.zip -o /tmp/browser-control-skill.zip
unzip -o /tmp/browser-control-skill.zip -d ~/.config/opencode/skills/browser-control
cd ~/.config/opencode/skills/browser-control
node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld
npx -y @op1/browser-control doctor
```

3. Open the Browser Control popup and click **Reload host**. It shows **Connected**.
The server command is `npx -y @op1/browser-control mcp`. Installation stores stable copies under `~/.local/state/browser-control`. Set `BROWSER_CONTROL_STATE_DIR` to use another directory. To install the three bundled skills, pass `--skills-dir <your-client-skills-directory>` to `install`; no skills directory is chosen automatically.

The popup also has **Pause host**, which disconnects the host and ends every agent session until you click **Resume host**. The pause setting survives browser restarts. If the host exits, the extension reconnects automatically within about 30 seconds.
See [installation and troubleshooting](docs/server/INSTALL.md) for flags, optional tools, and the isolated smoke check. The generated native-host wrapper uses the Node executable that ran `install`. Rerun `install` after removing or replacing that Node installation.

To install the agent skill with one command instead:
## Use

```sh
curl -fsSL https://raw.githubusercontent.com/opzero1/browser-control/main/scripts/install-browser-control-skill.sh | sh
```
Load the [browser-control skill](skills/browser-control/SKILL.md) in your agent. Start with `status` and `tabs`, then claim a tab or open one. Use `act_steps` to batch actions selected from observed controls. Release tabs before releasing a browser lease.

## Verify
The popup's **Pause host** disconnects the host and ends its agent sessions until **Resume host** is selected. The pause setting survives browser restarts. If the host exits, the extension retries within about 30 seconds.

```sh
node native-host/client.js ping
node native-host/client.js getInfo
```
The [bundled native host scripts](skills/browser-control/references/native-host.md) also support the standalone release-zip installation. Use one installer per Chrome profile because both installers write the same native-messaging manifest.

`ping` returns `pong` from the extension. `getInfo` reports `protocolVersion: 2`.
## Identifiers

## Development
| Component | Value |
| --- | --- |
| npm package and CLI | `@op1/browser-control`, `browser-control` |
| Chrome Web Store extension | `dcnjjnecbhipdbngkhjppkckpkellmld` |
| Isolated-profile extension | `mpodnojmjjafgogldgieimgbmfhhknbe` |
| Native messaging host | `com.opzero.chrome` |
| Host and page protocols | Version 2 |

The isolated copy has a public key that fixes its extension ID across paths and upgrades. The Web Store build has no injected key. A manually loaded unpacked build without a key gets an ID derived from its absolute path.

See [docs/DEVELOPER.md](docs/DEVELOPER.md) to load an unpacked build and connect a local host.
## Develop

```sh
pnpm install
pnpm run check
```

`pnpm run check` builds the extension, native host and installable skill, then runs type checks, tests and the project checks. The headless-browser tests for private input run only when `BROWSER_CONTROL_SYNTHETIC_CHROME` names a Chrome for Testing binary.
`check` builds the extension, native host, MCP server, and skill, then runs type checks, tests, project checks, and Python parity mapping checks. To include disposable headless-browser tests, set `BROWSER_CONTROL_SYNTHETIC_CHROME` to a Chrome for Testing executable.

Repository guides: [development](https://github.com/opzero1/browser-control/blob/main/docs/DEVELOPER.md) and [release](https://github.com/opzero1/browser-control/blob/main/docs/RELEASE.md).

## Release
## Support and privacy

See [docs/RELEASE.md](docs/RELEASE.md). The store listing text is in [store/listing.md](store/listing.md), the store images are in [store/assets](store/assets), and the website is in [site](site).
- [Homepage](https://browser-control.pages.dev/)
- [Privacy policy](docs/PRIVACY.md)
- [Support](https://browser-control.pages.dev/support/)
19 changes: 19 additions & 0 deletions data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Vendored data

`public_suffix_list.dat` is the Public Suffix List, including ICANN and private rules, used to map hosts to
cookie sites (`src/server/sites.ts`).

- Source: https://publicsuffix.org/list/public_suffix_list.dat
- Fetched: 2026-09-26 (2026-09-25T17:23Z)
- `// VERSION: 2026-09-24_13-26-36_UTC`
- `// COMMIT: a179a48c465e818cfd8d626691cb317985da87fb`
- Size: 334,786 bytes
- SHA-256: `257b298daca42f6d8ec964e238c2a55518e14f09d3117917ec8acee6f188503e`

The server refuses a file with any other hash (`fast-chrome-public-suffix-list-mismatch`). To update it,
replace the file, update `PSL_SHA256` in `src/server/sites.ts`, this README and the check in
`scripts/check-project.js`, and rerun the site tests. The list is published under the Mozilla Public
License 2.0.

`../native/clipboard-guard/clipboard_guard.swift` is copied verbatim from the reference fast-chrome server.
`browser-control install` builds it into `<state>/bin/clipboard-guard-<sha12>`.
Loading
Loading