browserforce 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -29
- package/bin.js +2 -1
- package/mcp/src/exec-engine.js +54 -1
- package/mcp/src/index.js +2 -1
- package/package.json +2 -2
- package/skills/browserforce/SKILL.md +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# BrowserForce
|
|
1
|
+
# BrowserForce //
|
|
2
2
|
|
|
3
3
|
> "a lion doesn't concern itself with token counting" — [@steipete](https://x.com/steipete), creator of [OpenClaw](https://github.com/openclaw/openclaw)
|
|
4
|
+
>
|
|
5
|
+
> "a 10x user doesn't concern itself with sandboxed browsers // sandboxes are for kids" — BrowserForce, your friendly neighborhood power source.
|
|
4
6
|
|
|
5
7
|
**You're giving an AI your real Chrome — your logins, cookies, and sessions. That takes conviction.** BrowserForce is built for people who use the best models and don't look back. Security is built in: lock URLs, block navigation, read-only mode, auto-cleanup — you stay in control.
|
|
6
8
|
|
|
@@ -10,15 +12,16 @@ Works with [OpenClaw](https://github.com/openclaw/openclaw), Claude, or any MCP-
|
|
|
10
12
|
|
|
11
13
|
## Comparison
|
|
12
14
|
|
|
13
|
-
| | Playwright MCP | Playwriter | Claude Extension |
|
|
15
|
+
| | Playwright MCP | OpenClaw Browser | Playwriter | Claude Extension | BrowserForce |
|
|
14
16
|
|---|---|---|---|---|---|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
17
|
+
| Browser | Spawns new Chrome | Separate profile | Your Chrome | Your Chrome | **Your Chrome** |
|
|
18
|
+
| Login state | Fresh | Fresh (isolated) | Yours | Yours | **Yours** |
|
|
19
|
+
| Tab access | N/A (new browser) | Managed by agent | Click each tab | Click each tab | **All tabs, automatic** |
|
|
20
|
+
| Autonomous | Yes | Yes | No (manual click) | No (manual click) | **Yes (fully autonomous)** |
|
|
21
|
+
| Context method | Screenshots (100KB+) | Screenshots + snapshots | A11y snapshots (5-20KB) | Screenshots (100KB+) | **A11y snapshots (5-20KB)** |
|
|
22
|
+
| Tools | Many dedicated | 1 `browser` tool | 1 `execute` tool | Built-in | **1 `execute` tool** |
|
|
23
|
+
| Agent support | Any MCP client | OpenClaw only | Any MCP client | Claude only | **Any MCP client** |
|
|
24
|
+
| Playwright API | Partial | No | Full | No | **Full** |
|
|
22
25
|
|
|
23
26
|
## Setup
|
|
24
27
|
|
|
@@ -43,41 +46,44 @@ pnpm install
|
|
|
43
46
|
3. Click **Load unpacked** → select the `extension/` folder
|
|
44
47
|
4. Extension icon appears in your toolbar (gray = disconnected)
|
|
45
48
|
|
|
46
|
-
### 3.
|
|
49
|
+
### 3. Done
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
browserforce serve
|
|
50
|
-
```
|
|
51
|
+
The relay auto-starts when you run any command or connect via MCP — no manual step needed. Extension icon turns green once connected.
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
To run the relay manually (optional):
|
|
53
54
|
|
|
54
55
|
```bash
|
|
55
|
-
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
BrowserForce
|
|
60
|
-
────────────────────────────────────────
|
|
61
|
-
Status: http://127.0.0.1:19222/
|
|
62
|
-
CDP: ws://127.0.0.1:19222/cdp?token=<TOKEN>
|
|
63
|
-
────────────────────────────────────────
|
|
56
|
+
browserforce serve
|
|
64
57
|
```
|
|
65
58
|
|
|
66
|
-
Extension icon turns green — you're connected.
|
|
67
|
-
|
|
68
59
|
## Connect Your Agent
|
|
69
60
|
|
|
70
61
|
### OpenClaw
|
|
71
62
|
|
|
72
|
-
|
|
63
|
+
Most OpenClaw users chat with their agent from Telegram or WhatsApp. BrowserForce lets your agent browse the web as you — no login flows, no captchas — even from a messaging app.
|
|
64
|
+
|
|
65
|
+
**Quick setup** (copy-paste into your terminal):
|
|
73
66
|
|
|
74
67
|
```bash
|
|
75
|
-
npx -y skills add ivalsaraj/browserforce
|
|
68
|
+
npm install -g browserforce && npx -y skills add ivalsaraj/browserforce
|
|
76
69
|
```
|
|
77
70
|
|
|
78
|
-
|
|
71
|
+
Then start the relay (keep this running):
|
|
79
72
|
|
|
80
|
-
|
|
73
|
+
```bash
|
|
74
|
+
browserforce serve
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Verify it works** — send this to your agent:
|
|
78
|
+
|
|
79
|
+
> Go to https://x.com and give me top tweets
|
|
80
|
+
|
|
81
|
+
If your agent browses to the page and responds with the title, you're all set.
|
|
82
|
+
|
|
83
|
+
<details>
|
|
84
|
+
<summary><b>Alternative: MCP server</b> (advanced)</summary>
|
|
85
|
+
|
|
86
|
+
If you prefer MCP over the skill, add to `~/.openclaw/openclaw.json`:
|
|
81
87
|
|
|
82
88
|
```json
|
|
83
89
|
{
|
|
@@ -101,6 +107,8 @@ Or add BrowserForce as an MCP server in `~/.openclaw/openclaw.json`:
|
|
|
101
107
|
}
|
|
102
108
|
```
|
|
103
109
|
|
|
110
|
+
</details>
|
|
111
|
+
|
|
104
112
|
### Claude Desktop
|
|
105
113
|
|
|
106
114
|
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
@@ -202,8 +210,137 @@ state.results = await page.evaluate(() => document.title);
|
|
|
202
210
|
| Tool | Description |
|
|
203
211
|
|------|-------------|
|
|
204
212
|
| `execute` | Run Playwright JavaScript in your real Chrome. Access `page`, `context`, `state`, `snapshot()`, `waitForPageLoad()`, `getLogs()`, and Node.js globals. |
|
|
213
|
+
| `screenshot_with_labels` | Take a screenshot with Vimium-style accessibility labels overlaid on interactive elements. |
|
|
205
214
|
| `reset` | Reconnect to the relay and clear state. Use when the connection drops. |
|
|
206
215
|
|
|
216
|
+
## Examples
|
|
217
|
+
|
|
218
|
+
Get started with simple prompts. The AI generates code and does the work.
|
|
219
|
+
|
|
220
|
+
<details>
|
|
221
|
+
<summary><b>Example 1: Read page content (X.com search)</b></summary>
|
|
222
|
+
|
|
223
|
+
**Prompt to AI:**
|
|
224
|
+
> Go to x.com/search and search for "browserforce". Show me the top 5 tweets you find.
|
|
225
|
+
|
|
226
|
+
**What the AI does:** Navigates to X, searches the term, extracts top tweets, returns them to you.
|
|
227
|
+
|
|
228
|
+
**Use case:** Quick research, trend tracking, social listening.
|
|
229
|
+
|
|
230
|
+
</details>
|
|
231
|
+
|
|
232
|
+
<details>
|
|
233
|
+
<summary><b>Example 2: Interact with a form (GitHub search)</b></summary>
|
|
234
|
+
|
|
235
|
+
**Prompt to AI:**
|
|
236
|
+
> Go to GitHub and search for "ai agents". Show me the top 3 repositories and their star counts.
|
|
237
|
+
|
|
238
|
+
**What the AI does:** Fills GitHub search, waits for results, extracts repo names + stars, returns them.
|
|
239
|
+
|
|
240
|
+
**Use case:** Finding libraries, competitive research, project discovery.
|
|
241
|
+
|
|
242
|
+
</details>
|
|
243
|
+
|
|
244
|
+
### Multi-Tab Workflows
|
|
245
|
+
|
|
246
|
+
<details>
|
|
247
|
+
<summary><b>Example 3: Search → Extract → Return</b></summary>
|
|
248
|
+
|
|
249
|
+
**Prompt to AI:**
|
|
250
|
+
> Search ProductHunt for "AI tools" and give me the top 5 products with their taglines and upvote counts.
|
|
251
|
+
|
|
252
|
+
**What the AI does:** Navigates ProductHunt, searches, extracts product info, returns structured data.
|
|
253
|
+
|
|
254
|
+
**Use case:** Market research, finding tools, competitive analysis.
|
|
255
|
+
|
|
256
|
+
</details>
|
|
257
|
+
|
|
258
|
+
<details>
|
|
259
|
+
<summary><b>Example 4: Open result in new tab, process there</b></summary>
|
|
260
|
+
|
|
261
|
+
**Prompt to AI:**
|
|
262
|
+
> Find the #1 product from your last ProductHunt search, click into it, and read the full description. Tell me what it does.
|
|
263
|
+
|
|
264
|
+
**What the AI does:** Opens the product page from previous results, reads the description, summarizes it.
|
|
265
|
+
|
|
266
|
+
**Use case:** Deep-dive research, understanding competitors, due diligence.
|
|
267
|
+
|
|
268
|
+
</details>
|
|
269
|
+
|
|
270
|
+
<details>
|
|
271
|
+
<summary><b>Example 5: Debugging workflow (inspect + verify)</b></summary>
|
|
272
|
+
|
|
273
|
+
**Prompt to AI:**
|
|
274
|
+
> Go to my staging site at staging.myapp.com/checkout and take a labeled screenshot. Tell me if the "Complete Purchase" button is visible and what's around it.
|
|
275
|
+
|
|
276
|
+
**What the AI does:** Navigates, takes screenshot with interactive labels, analyzes button state and layout.
|
|
277
|
+
|
|
278
|
+
**Use case:** Visual debugging, QA checks, spotting broken elements.
|
|
279
|
+
|
|
280
|
+
</details>
|
|
281
|
+
|
|
282
|
+
<details>
|
|
283
|
+
<summary><b>Example 6: Test form with data</b></summary>
|
|
284
|
+
|
|
285
|
+
**Prompt to AI:**
|
|
286
|
+
> Sign up for Substack using the email test.user@example.com. Tell me if the signup completes successfully.
|
|
287
|
+
|
|
288
|
+
**What the AI does:** Fills the form, submits, waits for confirmation, reports success/failure.
|
|
289
|
+
|
|
290
|
+
**Use case:** Testing sign-up flows, QA automation, form validation.
|
|
291
|
+
|
|
292
|
+
</details>
|
|
293
|
+
|
|
294
|
+
<details>
|
|
295
|
+
<summary><b>Example 7: Content pipeline (search → extract → compare)</b></summary>
|
|
296
|
+
|
|
297
|
+
**Prompt to AI:**
|
|
298
|
+
> Search for "AI regulation" on both X.com and LinkedIn. Give me the top 5 trending posts from each and tell me which topics overlap.
|
|
299
|
+
|
|
300
|
+
**What the AI does:** Searches both platforms, extracts posts, compares content, returns analysis.
|
|
301
|
+
|
|
302
|
+
**Use case:** Multi-source research, trend analysis, market sentiment.
|
|
303
|
+
|
|
304
|
+
</details>
|
|
305
|
+
|
|
306
|
+
<details>
|
|
307
|
+
<summary><b>Example 8: Data extraction → CSV pipeline</b></summary>
|
|
308
|
+
|
|
309
|
+
**Prompt to AI:**
|
|
310
|
+
> Go to Hacker News and extract the top 10 stories with their titles and vote counts. Format as CSV so I can import into a spreadsheet.
|
|
311
|
+
|
|
312
|
+
**What the AI does:** Navigates HN, extracts story data, formats as CSV, returns it ready to paste.
|
|
313
|
+
|
|
314
|
+
**Use case:** Data workflows, trend tracking, content curation.
|
|
315
|
+
|
|
316
|
+
</details>
|
|
317
|
+
|
|
318
|
+
<details>
|
|
319
|
+
<summary><b>Example 9: A/B testing across variants</b></summary>
|
|
320
|
+
|
|
321
|
+
**Prompt to AI:**
|
|
322
|
+
> Visit myapp.com/?variant=red and myapp.com/?variant=blue. Compare the two designs and tell me which button color is more prominent and what other differences exist.
|
|
323
|
+
|
|
324
|
+
**What the AI does:** Opens both variants, compares layouts/colors/text, reports visual differences.
|
|
325
|
+
|
|
326
|
+
**Use case:** Design QA, A/B testing, variant comparison.
|
|
327
|
+
|
|
328
|
+
</details>
|
|
329
|
+
|
|
330
|
+
<details>
|
|
331
|
+
<summary><b>Example 10: Monitor + alert workflow</b></summary>
|
|
332
|
+
|
|
333
|
+
**Prompt to AI:**
|
|
334
|
+
> Check our status page at status.myapp.com every few minutes. Tell me the current status of the API and database. Alert me if anything changes from green to red.
|
|
335
|
+
|
|
336
|
+
**What the AI does:** Monitors status page, reads indicators, alerts on degradation.
|
|
337
|
+
|
|
338
|
+
**Use case:** Uptime monitoring, incident detection, SLA tracking.
|
|
339
|
+
|
|
340
|
+
</details>
|
|
341
|
+
|
|
342
|
+
**More examples** and detailed walkthrough available in the [User Guide](GUIDE.md#examples).
|
|
343
|
+
|
|
207
344
|
## How It Works
|
|
208
345
|
|
|
209
346
|
```
|
package/bin.js
CHANGED
|
@@ -43,7 +43,8 @@ function httpGet(url) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
async function connectBrowser() {
|
|
46
|
-
const { getCdpUrl } = await import('./mcp/src/exec-engine.js');
|
|
46
|
+
const { getCdpUrl, ensureRelay } = await import('./mcp/src/exec-engine.js');
|
|
47
|
+
await ensureRelay();
|
|
47
48
|
// playwright-core lives in mcp/node_modules (pnpm workspace sub-package).
|
|
48
49
|
// Use createRequire from the mcp package context to locate it, then dynamic-import.
|
|
49
50
|
const { createRequire } = await import('node:module');
|
package/mcp/src/exec-engine.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import { readFileSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { spawn } from 'node:child_process';
|
|
7
9
|
import {
|
|
8
10
|
TEST_ID_ATTRS,
|
|
9
11
|
buildSnapshotText, parseSearchPattern, annotateStableAttrs,
|
|
@@ -11,8 +13,10 @@ import {
|
|
|
11
13
|
|
|
12
14
|
// ─── Configuration ───────────────────────────────────────────────────────────
|
|
13
15
|
|
|
16
|
+
const DEFAULT_PORT = 19222;
|
|
14
17
|
export const BF_DIR = join(homedir(), '.browserforce');
|
|
15
18
|
export const CDP_URL_FILE = join(BF_DIR, 'cdp-url');
|
|
19
|
+
const RELAY_SCRIPT = fileURLToPath(new URL('../../relay/src/index.js', import.meta.url));
|
|
16
20
|
|
|
17
21
|
export function getCdpUrl() {
|
|
18
22
|
if (process.env.BF_CDP_URL) return process.env.BF_CDP_URL;
|
|
@@ -34,10 +38,59 @@ export function getRelayHttpUrl() {
|
|
|
34
38
|
const parsed = new URL(cdpUrl);
|
|
35
39
|
return `http://${parsed.hostname}:${parsed.port}`;
|
|
36
40
|
} catch {
|
|
37
|
-
return
|
|
41
|
+
return `http://127.0.0.1:${DEFAULT_PORT}`;
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
// ─── Auto-start relay ───────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
function getRelayPort() {
|
|
48
|
+
if (process.env.RELAY_PORT) return parseInt(process.env.RELAY_PORT, 10);
|
|
49
|
+
try {
|
|
50
|
+
const url = readFileSync(CDP_URL_FILE, 'utf8').trim();
|
|
51
|
+
if (url) {
|
|
52
|
+
const port = new URL(url).port;
|
|
53
|
+
if (port) return parseInt(port, 10);
|
|
54
|
+
}
|
|
55
|
+
} catch { /* fall through */ }
|
|
56
|
+
return DEFAULT_PORT;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function isRelayRunning(port) {
|
|
60
|
+
try {
|
|
61
|
+
const res = await fetch(`http://127.0.0.1:${port}/`, {
|
|
62
|
+
signal: AbortSignal.timeout(500),
|
|
63
|
+
});
|
|
64
|
+
return res.ok;
|
|
65
|
+
} catch { return false; }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Ensure the relay server is running. If not, spawn it as a detached
|
|
70
|
+
* background process and wait for it to become reachable.
|
|
71
|
+
*/
|
|
72
|
+
export async function ensureRelay() {
|
|
73
|
+
const port = getRelayPort();
|
|
74
|
+
if (await isRelayRunning(port)) return;
|
|
75
|
+
|
|
76
|
+
const child = spawn(process.execPath, [RELAY_SCRIPT], {
|
|
77
|
+
detached: true,
|
|
78
|
+
stdio: 'ignore',
|
|
79
|
+
env: { ...process.env, RELAY_PORT: String(port) },
|
|
80
|
+
});
|
|
81
|
+
child.unref();
|
|
82
|
+
|
|
83
|
+
const deadline = Date.now() + 5000;
|
|
84
|
+
while (Date.now() < deadline) {
|
|
85
|
+
await new Promise(r => setTimeout(r, 200));
|
|
86
|
+
if (await isRelayRunning(port)) {
|
|
87
|
+
process.stderr.write('[browserforce] Relay auto-started\n');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw new Error('Failed to auto-start relay server within 5s');
|
|
92
|
+
}
|
|
93
|
+
|
|
41
94
|
// ─── Smart Page Load Detection ───────────────────────────────────────────────
|
|
42
95
|
// Filters analytics/ad requests that never finish, polls document.readyState +
|
|
43
96
|
// pending resource count.
|
package/mcp/src/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { chromium } from 'playwright-core';
|
|
9
9
|
import {
|
|
10
|
-
getCdpUrl, CodeExecutionTimeoutError, buildExecContext, runCode, formatResult,
|
|
10
|
+
getCdpUrl, ensureRelay, CodeExecutionTimeoutError, buildExecContext, runCode, formatResult,
|
|
11
11
|
} from './exec-engine.js';
|
|
12
12
|
import { screenshotWithLabels } from './a11y-labels.js';
|
|
13
13
|
|
|
@@ -65,6 +65,7 @@ let browser = null;
|
|
|
65
65
|
|
|
66
66
|
async function ensureBrowser() {
|
|
67
67
|
if (browser?.isConnected()) return;
|
|
68
|
+
await ensureRelay();
|
|
68
69
|
const cdpUrl = getCdpUrl();
|
|
69
70
|
browser = await chromium.connectOverCDP(cdpUrl);
|
|
70
71
|
browser.on('disconnected', () => {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browserforce",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Give AI agents your real Chrome browser
|
|
5
|
+
"description": "Give AI agents your real Chrome browser with progressive examples: simple reads, form interactions, multi-tab workflows, and state persistence. Search X and GitHub, extract ProductHunt data, test forms, compare A/B variants, monitor status pages. Works with OpenClaw, Claude, and any MCP agent.",
|
|
6
6
|
"homepage": "https://github.com/ivalsaraj/browserforce",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -19,8 +19,8 @@ cookies, and extensions already active. No headless browser, no fresh profiles.
|
|
|
19
19
|
## Prerequisites
|
|
20
20
|
|
|
21
21
|
The user must have:
|
|
22
|
-
1. BrowserForce
|
|
23
|
-
2.
|
|
22
|
+
1. BrowserForce Chrome extension installed and connected (green icon)
|
|
23
|
+
2. The relay auto-starts on first command — no manual step needed
|
|
24
24
|
|
|
25
25
|
Check with: `browserforce status`
|
|
26
26
|
|