barebrowse 0.2.1 → 0.3.0
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/.claude/memory/AGENT_RULES.md +251 -0
- package/.claude/settings.local.json +37 -0
- package/.claude/skills/barebrowse/SKILL.md +107 -0
- package/.claude/stash/barebrowse-research-2026-02-22.md +49 -0
- package/.claude/stash/phase3-interactions-complete.md +69 -0
- package/.claude/stash/phase3-prep.md +88 -0
- package/.claude/stash/phase4-complete-2026-02-22.md +61 -0
- package/CHANGELOG.md +53 -0
- package/CLAUDE.md +4 -2
- package/README.md +54 -7
- package/barebrowse.context.md +27 -8
- package/cli.js +289 -48
- package/docs/00-context/assumptions.md +38 -0
- package/docs/{blueprint.md → 00-context/system-state.md} +30 -5
- package/docs/00-context/vision.md +52 -0
- package/docs/01-product/prd.md +284 -0
- package/docs/03-logs/bug-log.md +16 -0
- package/docs/03-logs/decisions-log.md +32 -0
- package/docs/03-logs/implementation-log.md +54 -0
- package/docs/03-logs/insights.md +35 -0
- package/docs/03-logs/validation-log.md +123 -0
- package/docs/04-process/definition-of-done.md +31 -0
- package/docs/04-process/dev-workflow.md +68 -0
- package/docs/{testing.md → 04-process/testing.md} +21 -2
- package/docs/README.md +55 -0
- package/docs/archive/poc-plan.md +230 -0
- package/mcp-server.js +1 -1
- package/package.json +1 -1
- package/src/aria.js +1 -1
- package/src/daemon.js +321 -0
- package/src/session-client.js +70 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
CLI session mode. Shell commands that output to disk — coding agents read files when needed instead of getting full snapshots in every tool response. ~4x more token-efficient than MCP for multi-step browsing flows.
|
|
6
|
+
|
|
7
|
+
### New: CLI session commands
|
|
8
|
+
- `barebrowse open [url] [flags]` — spawn background daemon holding a `connect()` session
|
|
9
|
+
- `barebrowse close` / `status` — session lifecycle
|
|
10
|
+
- `barebrowse goto <url>` — navigate
|
|
11
|
+
- `barebrowse snapshot [--mode=act|read]` — ARIA snapshot → `.barebrowse/page-*.yml`
|
|
12
|
+
- `barebrowse screenshot [--format]` — screenshot → `.barebrowse/screenshot-*.png`
|
|
13
|
+
- `barebrowse click/type/fill/press/scroll/hover/select` — all interactions from connect() API
|
|
14
|
+
- Open flags: `--mode`, `--port`, `--no-cookies`, `--browser`, `--timeout`, `--prune-mode`, `--no-consent`
|
|
15
|
+
|
|
16
|
+
### New: agent self-sufficiency
|
|
17
|
+
- `barebrowse eval <expression>` — run JS in page context via `Runtime.evaluate`
|
|
18
|
+
- `barebrowse console-logs [--level --clear]` — dump captured console logs → `.barebrowse/console-*.json`
|
|
19
|
+
- `barebrowse network-log [--failed]` — dump network requests → `.barebrowse/network-*.json`
|
|
20
|
+
- `barebrowse wait-idle [--timeout]` — wait for network idle
|
|
21
|
+
|
|
22
|
+
### New: daemon architecture (`src/daemon.js` + `src/session-client.js`)
|
|
23
|
+
- Background HTTP server on random localhost port, holding a `connect()` session
|
|
24
|
+
- Spawned as detached child process, communicates via `session.json`
|
|
25
|
+
- Console capture via `Runtime.consoleAPICalled`
|
|
26
|
+
- Network capture via `Network.requestWillBeSent` / `responseReceived` / `loadingFailed`
|
|
27
|
+
- Graceful shutdown on `close` command or SIGTERM
|
|
28
|
+
|
|
29
|
+
### New: SKILL.md for Claude Code
|
|
30
|
+
- `.claude/skills/barebrowse/SKILL.md` — skill definition + full CLI command reference
|
|
31
|
+
- `barebrowse install --skill` — copies SKILL.md to `~/.config/claude/skills/barebrowse/`
|
|
32
|
+
|
|
33
|
+
### Fixed: MCP setup instructions
|
|
34
|
+
- README now has per-client instructions: Claude Code (`claude mcp add`), Claude Desktop/Cursor (`npx barebrowse install`), VS Code (`.vscode/mcp.json`)
|
|
35
|
+
- `install` command no longer writes `.mcp.json` for Claude Code — prints `claude mcp add` hint instead
|
|
36
|
+
|
|
37
|
+
### Fixed: ARIA tree formatting (`src/aria.js`)
|
|
38
|
+
- Ignored nodes joined children with empty string instead of newline, causing sibling subtrees to concatenate on one line
|
|
39
|
+
- Fixed to `.filter(Boolean).join('\n')`
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
- `cli.js` — expanded from 3 commands to full dispatch table (20+ commands)
|
|
43
|
+
- `barebrowse.context.md` — added CLI as third integration path, updated MCP setup
|
|
44
|
+
- `README.md` — "Two ways" → "Three ways", added CLI section
|
|
45
|
+
|
|
46
|
+
### Docs
|
|
47
|
+
- `docs/04-process/testing.md` — updated to 64 tests, added CLI test section
|
|
48
|
+
- `docs/00-context/system-state.md` — added daemon/session-client to module table, CLI to integrations
|
|
49
|
+
- `docs/03-logs/validation-log.md` — full CLI manual validation results
|
|
50
|
+
|
|
51
|
+
### Tests
|
|
52
|
+
- 64 tests passing (was 54 in 0.2.x)
|
|
53
|
+
- New: `test/integration/cli.test.js` (10 tests) — full open → snapshot → goto → click → eval → console → network → close cycle
|
|
54
|
+
- All existing 54 tests unchanged and passing
|
|
55
|
+
|
|
3
56
|
## 0.2.1
|
|
4
57
|
|
|
5
58
|
- README rewritten: no code blocks, full obstacle course table with mode column, two usage paths (MCP vs framework), mcprune credited, measured token savings, context.md as code reference
|
package/CLAUDE.md
CHANGED
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
## Project Specifics
|
|
14
14
|
|
|
15
|
+
- **What:** Vanilla JS library — CDP-direct browsing for autonomous agents. URL in, pruned ARIA snapshot out.
|
|
15
16
|
- **Language:** Vanilla JavaScript, ES modules, no build step
|
|
16
17
|
- **Runtime:** Node.js >= 22 (built-in WebSocket, sqlite)
|
|
17
18
|
- **Protocol:** CDP (Chrome DevTools Protocol) direct — no Playwright
|
|
18
19
|
- **Browser:** Any installed Chromium-based browser (chromium, chrome, brave, edge)
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
20
|
+
- **Modules:** 11 files in `src/`, ~2,400 lines, zero required deps
|
|
21
|
+
- **Tests:** 54 passing — run with `node --test test/unit/*.test.js test/integration/*.test.js`
|
|
22
|
+
- **Docs:** `docs/README.md` (navigation guide to all documentation)
|
|
21
23
|
|
|
22
24
|
For full development and testing standards, see `.claude/memory/AGENT_RULES.md`.
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
barebrowse is agentic browsing stripped to the bone. It gives your AI agent eyes and hands on the web -- navigate any page, see what's there, click buttons, fill forms, scroll, and move on. It uses your installed Chromium browser (Chrome, Brave, Edge -- whatever you have), reuses your existing login sessions, and handles all the friction automatically: cookie consent walls, permission prompts, bot detection, GDPR dialogs.
|
|
20
20
|
|
|
21
|
-
Instead of dumping raw DOM or taking screenshots, barebrowse returns a **pruned ARIA snapshot** -- a compact semantic view of what's on the page and what the agent can interact with. Buttons, links, inputs, headings -- labeled with `[ref=N]` markers the agent uses to act. The pruning pipeline is ported from [mcprune](https://github.com/
|
|
21
|
+
Instead of dumping raw DOM or taking screenshots, barebrowse returns a **pruned ARIA snapshot** -- a compact semantic view of what's on the page and what the agent can interact with. Buttons, links, inputs, headings -- labeled with `[ref=N]` markers the agent uses to act. The pruning pipeline is ported from [mcprune](https://github.com/hamr0/mcprune) and cuts 40-90% of tokens compared to raw page output. Every token your agent reads is meaningful.
|
|
22
22
|
|
|
23
23
|
No Playwright. No bundled browser. No 200MB download. No broken dependencies. Zero deps. Just CDP over a WebSocket to whatever Chromium you already have.
|
|
24
24
|
|
|
@@ -30,18 +30,65 @@ npm install barebrowse
|
|
|
30
30
|
|
|
31
31
|
Requires Node.js >= 22 and any installed Chromium-based browser.
|
|
32
32
|
|
|
33
|
-
##
|
|
33
|
+
## Three ways to use it
|
|
34
34
|
|
|
35
|
-
### 1.
|
|
35
|
+
### 1. CLI session -- for coding agents and quick testing
|
|
36
36
|
|
|
37
|
+
```bash
|
|
38
|
+
barebrowse open https://example.com # Start session + navigate
|
|
39
|
+
barebrowse snapshot # ARIA snapshot → .barebrowse/page-*.yml
|
|
40
|
+
barebrowse click 8 # Click element
|
|
41
|
+
barebrowse close # End session
|
|
37
42
|
```
|
|
38
|
-
|
|
43
|
+
|
|
44
|
+
Outputs go to `.barebrowse/` as files -- agents read them with their file tools, no token waste in tool responses. Install the skill for Claude Code:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
barebrowse install --skill
|
|
48
|
+
# or: claude mcp add barebrowse -- npx barebrowse mcp
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Full command reference: [.claude/skills/barebrowse/SKILL.md](.claude/skills/barebrowse/SKILL.md)
|
|
52
|
+
|
|
53
|
+
### 2. MCP server -- for Claude Desktop, Cursor, and other MCP clients
|
|
54
|
+
|
|
55
|
+
**Claude Code:**
|
|
56
|
+
```bash
|
|
57
|
+
claude mcp add barebrowse -- npx barebrowse mcp
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Claude Desktop / Cursor:**
|
|
61
|
+
```bash
|
|
39
62
|
npx barebrowse install
|
|
40
63
|
```
|
|
41
64
|
|
|
42
|
-
|
|
65
|
+
Or manually add to your config (`claude_desktop_config.json`, `.cursor/mcp.json`):
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"barebrowse": {
|
|
70
|
+
"command": "npx",
|
|
71
|
+
"args": ["barebrowse", "mcp"]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**VS Code (`.vscode/mcp.json`):**
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"servers": {
|
|
81
|
+
"barebrowse": {
|
|
82
|
+
"command": "npx",
|
|
83
|
+
"args": ["barebrowse", "mcp"]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
7 tools: `browse`, `goto`, `snapshot`, `click`, `type`, `press`, `scroll`.
|
|
43
90
|
|
|
44
|
-
###
|
|
91
|
+
### 3. Library -- for agentic automation
|
|
45
92
|
|
|
46
93
|
Import barebrowse in your agent code. One-shot reads, interactive sessions, full observe-think-act loops. Works with any LLM orchestration library. Ships with a ready-made adapter for [bareagent](https://www.npmjs.com/package/bare-agent) (9 tools, auto-snapshot after every action).
|
|
47
94
|
|
|
@@ -80,7 +127,7 @@ This is the obstacle course your agent doesn't have to think about:
|
|
|
80
127
|
|
|
81
128
|
## What the agent sees
|
|
82
129
|
|
|
83
|
-
Raw ARIA output from a page is noisy -- decorative wrappers, hidden elements, structural junk. The pruning pipeline (ported from [mcprune](https://github.com/
|
|
130
|
+
Raw ARIA output from a page is noisy -- decorative wrappers, hidden elements, structural junk. The pruning pipeline (ported from [mcprune](https://github.com/hamr0/mcprune)) strips it down to what matters.
|
|
84
131
|
|
|
85
132
|
| Page | Raw | Pruned | Reduction |
|
|
86
133
|
|------|-----|--------|-----------|
|
package/barebrowse.context.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# barebrowse -- Integration Guide
|
|
2
2
|
|
|
3
3
|
> For AI assistants and developers wiring barebrowse into a project.
|
|
4
|
-
> v0.
|
|
4
|
+
> v0.3.0 | Node.js >= 22 | 0 required deps | MIT
|
|
5
5
|
|
|
6
6
|
## What this is
|
|
7
7
|
|
|
@@ -13,9 +13,10 @@ No Playwright. No bundled browser. No build step. Vanilla JS, ES modules.
|
|
|
13
13
|
npm install barebrowse
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
Three integration paths:
|
|
17
|
+
1. **Library:** `import { browse, connect } from 'barebrowse'` -- one-shot or interactive session
|
|
18
|
+
2. **MCP server:** `barebrowse mcp` -- JSON-RPC over stdio for Claude Desktop, Cursor, etc.
|
|
19
|
+
3. **CLI session:** `barebrowse open` / `click` / `snapshot` / `close` -- shell commands, outputs to disk
|
|
19
20
|
|
|
20
21
|
## Which mode do I need?
|
|
21
22
|
|
|
@@ -174,15 +175,33 @@ try {
|
|
|
174
175
|
|
|
175
176
|
Action tools (click, type, press, scroll, goto) auto-return a fresh snapshot so the LLM always sees the result. 300ms settle delay after actions for DOM updates.
|
|
176
177
|
|
|
177
|
-
##
|
|
178
|
+
## CLI session mode
|
|
178
179
|
|
|
179
|
-
|
|
180
|
+
For coding agents (Claude Code, Copilot, Cursor) and quick interactive testing. Commands output files to `.barebrowse/` -- agents read them with file tools, avoiding token waste in tool responses.
|
|
180
181
|
|
|
181
182
|
```bash
|
|
182
|
-
|
|
183
|
+
barebrowse open https://example.com # Start daemon + navigate
|
|
184
|
+
barebrowse snapshot # → .barebrowse/page-<timestamp>.yml
|
|
185
|
+
barebrowse click 8 # Click element ref=8
|
|
186
|
+
barebrowse type 12 hello world # Type into element ref=12
|
|
187
|
+
barebrowse screenshot # → .barebrowse/screenshot-<timestamp>.png
|
|
188
|
+
barebrowse console-logs # → .barebrowse/console-<timestamp>.json
|
|
189
|
+
barebrowse close # Kill daemon + browser
|
|
183
190
|
```
|
|
184
191
|
|
|
185
|
-
|
|
192
|
+
Session lifecycle: `open` spawns a background daemon holding a `connect()` session. Subsequent commands POST to the daemon over HTTP (localhost). `close` shuts everything down.
|
|
193
|
+
|
|
194
|
+
Full command reference: `.claude/skills/barebrowse/SKILL.md`
|
|
195
|
+
|
|
196
|
+
## MCP wrapper
|
|
197
|
+
|
|
198
|
+
barebrowse ships an MCP server for direct use with Claude Desktop, Cursor, or any MCP client.
|
|
199
|
+
|
|
200
|
+
**Claude Code:** `claude mcp add barebrowse -- npx barebrowse mcp`
|
|
201
|
+
|
|
202
|
+
**Claude Desktop / Cursor:** `npx barebrowse install` (auto-detects and writes config)
|
|
203
|
+
|
|
204
|
+
**Manual config** (`claude_desktop_config.json`, `.cursor/mcp.json`):
|
|
186
205
|
```json
|
|
187
206
|
{
|
|
188
207
|
"mcpServers": {
|
package/cli.js
CHANGED
|
@@ -2,28 +2,192 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* cli.js -- barebrowse CLI entry point.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Session commands:
|
|
6
|
+
* barebrowse open [url] [flags] Open browser session (daemon)
|
|
7
|
+
* barebrowse close Close session + kill daemon
|
|
8
|
+
* barebrowse status Check if session is running
|
|
9
|
+
*
|
|
10
|
+
* Navigation:
|
|
11
|
+
* barebrowse goto <url> Navigate to URL
|
|
12
|
+
* barebrowse snapshot [--mode] Get pruned ARIA snapshot → file
|
|
13
|
+
* barebrowse screenshot [--format] Take screenshot → file
|
|
14
|
+
*
|
|
15
|
+
* Interaction:
|
|
16
|
+
* barebrowse click <ref> Click element by ref
|
|
17
|
+
* barebrowse type <ref> <text> Type text into element
|
|
18
|
+
* barebrowse fill <ref> <text> Clear + type (replace content)
|
|
19
|
+
* barebrowse press <key> Press special key
|
|
20
|
+
* barebrowse scroll <deltaY> Scroll page
|
|
21
|
+
* barebrowse hover <ref> Hover over element
|
|
22
|
+
* barebrowse select <ref> <value> Select dropdown value
|
|
23
|
+
*
|
|
24
|
+
* Self-sufficiency:
|
|
25
|
+
* barebrowse eval <expression> Evaluate JS in page
|
|
26
|
+
* barebrowse wait-idle [--timeout] Wait for network idle
|
|
27
|
+
* barebrowse console-logs Dump console logs → file
|
|
28
|
+
* barebrowse network-log Dump network log → file
|
|
29
|
+
*
|
|
30
|
+
* Legacy / tools:
|
|
31
|
+
* barebrowse browse <url> [mode] One-shot browse (stdout)
|
|
32
|
+
* barebrowse mcp Start MCP server (stdio)
|
|
33
|
+
* barebrowse install [--skill] Auto-configure MCP or install skill
|
|
9
34
|
*/
|
|
10
35
|
|
|
11
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
12
|
-
import { join } from 'node:path';
|
|
36
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
37
|
+
import { join, resolve } from 'node:path';
|
|
13
38
|
import { homedir, platform } from 'node:os';
|
|
39
|
+
import { fileURLToPath } from 'node:url';
|
|
14
40
|
|
|
15
|
-
const
|
|
41
|
+
const args = process.argv.slice(2);
|
|
42
|
+
const cmd = args[0];
|
|
16
43
|
|
|
17
|
-
|
|
44
|
+
// Hidden internal flag: --daemon-internal
|
|
45
|
+
if (args.includes('--daemon-internal')) {
|
|
46
|
+
await runDaemonInternal();
|
|
47
|
+
} else if (cmd === 'mcp') {
|
|
18
48
|
await import('./mcp-server.js');
|
|
19
|
-
|
|
20
49
|
} else if (cmd === 'install') {
|
|
21
50
|
install();
|
|
51
|
+
} else if (cmd === 'browse' && args[1]) {
|
|
52
|
+
await oneShot();
|
|
53
|
+
} else if (cmd === 'open') {
|
|
54
|
+
await cmdOpen();
|
|
55
|
+
} else if (cmd === 'close') {
|
|
56
|
+
await cmdProxy('close');
|
|
57
|
+
} else if (cmd === 'status') {
|
|
58
|
+
await cmdStatus();
|
|
59
|
+
} else if (cmd === 'goto' && args[1]) {
|
|
60
|
+
await cmdProxy('goto', { url: args[1], timeout: parseFlag('--timeout') });
|
|
61
|
+
} else if (cmd === 'snapshot') {
|
|
62
|
+
await cmdProxy('snapshot', { mode: parseFlag('--mode') });
|
|
63
|
+
} else if (cmd === 'screenshot') {
|
|
64
|
+
await cmdProxy('screenshot', { format: parseFlag('--format') });
|
|
65
|
+
} else if (cmd === 'click' && args[1]) {
|
|
66
|
+
await cmdProxy('click', { ref: args[1] });
|
|
67
|
+
} else if (cmd === 'type' && args[1] && args[2]) {
|
|
68
|
+
await cmdProxy('type', { ref: args[1], text: args.slice(2).filter(a => !a.startsWith('--')).join(' '), clear: hasFlag('--clear') });
|
|
69
|
+
} else if (cmd === 'fill' && args[1] && args[2]) {
|
|
70
|
+
await cmdProxy('fill', { ref: args[1], text: args.slice(2).filter(a => !a.startsWith('--')).join(' ') });
|
|
71
|
+
} else if (cmd === 'press' && args[1]) {
|
|
72
|
+
await cmdProxy('press', { key: args[1] });
|
|
73
|
+
} else if (cmd === 'scroll' && args[1]) {
|
|
74
|
+
await cmdProxy('scroll', { deltaY: Number(args[1]) });
|
|
75
|
+
} else if (cmd === 'hover' && args[1]) {
|
|
76
|
+
await cmdProxy('hover', { ref: args[1] });
|
|
77
|
+
} else if (cmd === 'select' && args[1] && args[2]) {
|
|
78
|
+
await cmdProxy('select', { ref: args[1], value: args[2] });
|
|
79
|
+
} else if (cmd === 'eval' && args[1]) {
|
|
80
|
+
await cmdProxy('eval', { expression: args.slice(1).join(' ') });
|
|
81
|
+
} else if (cmd === 'wait-idle') {
|
|
82
|
+
await cmdProxy('wait-idle', { timeout: parseFlag('--timeout') });
|
|
83
|
+
} else if (cmd === 'console-logs') {
|
|
84
|
+
await cmdProxy('console-logs', { level: parseFlag('--level'), clear: hasFlag('--clear') });
|
|
85
|
+
} else if (cmd === 'network-log') {
|
|
86
|
+
await cmdProxy('network-log', { failed: hasFlag('--failed') });
|
|
87
|
+
} else {
|
|
88
|
+
printUsage();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
// --- Command implementations ---
|
|
93
|
+
|
|
94
|
+
async function cmdOpen() {
|
|
95
|
+
const { startDaemon } = await import('./src/daemon.js');
|
|
96
|
+
const { isAlive } = await import('./src/session-client.js');
|
|
97
|
+
const outputDir = resolve('.barebrowse');
|
|
98
|
+
|
|
99
|
+
// Check for existing session
|
|
100
|
+
if (await isAlive(outputDir)) {
|
|
101
|
+
process.stdout.write('Session already running. Use `barebrowse close` first.\n');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const url = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
106
|
+
const opts = {
|
|
107
|
+
mode: parseFlag('--mode') || 'headless',
|
|
108
|
+
port: parseFlag('--port'),
|
|
109
|
+
cookies: !hasFlag('--no-cookies'),
|
|
110
|
+
browser: parseFlag('--browser'),
|
|
111
|
+
timeout: parseFlag('--timeout'),
|
|
112
|
+
pruneMode: parseFlag('--prune-mode') || 'act',
|
|
113
|
+
consent: !hasFlag('--no-consent'),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const session = await startDaemon(opts, outputDir, url);
|
|
118
|
+
process.stdout.write(`Session started (pid ${session.pid}, port ${session.port})\n`);
|
|
119
|
+
if (url) process.stdout.write(`Navigated to ${url}\n`);
|
|
120
|
+
process.stdout.write(`Output dir: ${outputDir}\n`);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
22
126
|
|
|
23
|
-
|
|
127
|
+
async function cmdStatus() {
|
|
128
|
+
const { readSession, isAlive } = await import('./src/session-client.js');
|
|
129
|
+
const outputDir = resolve('.barebrowse');
|
|
130
|
+
const session = readSession(outputDir);
|
|
131
|
+
|
|
132
|
+
if (!session) {
|
|
133
|
+
process.stdout.write('No session found.\n');
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const alive = await isAlive(outputDir);
|
|
138
|
+
if (alive) {
|
|
139
|
+
process.stdout.write(`Session running (pid ${session.pid}, port ${session.port}, started ${session.startedAt})\n`);
|
|
140
|
+
} else {
|
|
141
|
+
process.stdout.write(`Session stale (pid ${session.pid} not responding). Run \`barebrowse close\` to clean up.\n`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function cmdProxy(command, cmdArgs) {
|
|
147
|
+
const { sendCommand, readSession } = await import('./src/session-client.js');
|
|
148
|
+
const { unlinkSync } = await import('node:fs');
|
|
149
|
+
const outputDir = resolve('.barebrowse');
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const result = await sendCommand(command, cmdArgs, outputDir);
|
|
153
|
+
|
|
154
|
+
if (!result.ok) {
|
|
155
|
+
process.stderr.write(`Error: ${result.error}\n`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Print result
|
|
160
|
+
if (result.file && result.count !== undefined) {
|
|
161
|
+
process.stdout.write(`${result.file} (${result.count} entries)\n`);
|
|
162
|
+
} else if (result.file) {
|
|
163
|
+
process.stdout.write(`${result.file}\n`);
|
|
164
|
+
} else if (result.value !== undefined) {
|
|
165
|
+
process.stdout.write(JSON.stringify(result.value) + '\n');
|
|
166
|
+
} else if (command === 'close') {
|
|
167
|
+
// Clean up session.json in case daemon didn't
|
|
168
|
+
const sessionPath = join(outputDir, 'session.json');
|
|
169
|
+
try { unlinkSync(sessionPath); } catch { /* already gone */ }
|
|
170
|
+
process.stdout.write('Session closed.\n');
|
|
171
|
+
} else {
|
|
172
|
+
process.stdout.write('ok\n');
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
if (command === 'close') {
|
|
176
|
+
// Daemon may have exited before responding — that's fine
|
|
177
|
+
const sessionPath = join(outputDir, 'session.json');
|
|
178
|
+
try { unlinkSync(sessionPath); } catch { /* already gone */ }
|
|
179
|
+
process.stdout.write('Session closed.\n');
|
|
180
|
+
} else {
|
|
181
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function oneShot() {
|
|
24
188
|
const { browse } = await import('./src/index.js');
|
|
25
|
-
const url =
|
|
26
|
-
const mode =
|
|
189
|
+
const url = args[1];
|
|
190
|
+
const mode = args[2] || 'headless';
|
|
27
191
|
try {
|
|
28
192
|
const snapshot = await browse(url, { mode });
|
|
29
193
|
process.stdout.write(snapshot + '\n');
|
|
@@ -32,28 +196,50 @@ if (cmd === 'mcp') {
|
|
|
32
196
|
process.stderr.write(`Error: ${err.message}\n`);
|
|
33
197
|
process.exit(1);
|
|
34
198
|
}
|
|
199
|
+
}
|
|
35
200
|
|
|
36
|
-
|
|
37
|
-
|
|
201
|
+
async function runDaemonInternal() {
|
|
202
|
+
const { runDaemon } = await import('./src/daemon.js');
|
|
203
|
+
const opts = {
|
|
204
|
+
mode: parseFlag('--mode') || 'headless',
|
|
205
|
+
port: parseFlag('--port'),
|
|
206
|
+
cookies: !hasFlag('--no-cookies'),
|
|
207
|
+
browser: parseFlag('--browser'),
|
|
208
|
+
timeout: parseFlag('--timeout'),
|
|
209
|
+
pruneMode: parseFlag('--prune-mode') || 'act',
|
|
210
|
+
consent: !hasFlag('--no-consent'),
|
|
211
|
+
};
|
|
212
|
+
const outputDir = parseFlag('--output-dir') || resolve('.barebrowse');
|
|
213
|
+
const url = parseFlag('--url');
|
|
214
|
+
await runDaemon(opts, outputDir, url || undefined);
|
|
215
|
+
}
|
|
38
216
|
|
|
39
|
-
Usage:
|
|
40
|
-
barebrowse mcp Start MCP server (JSON-RPC over stdio)
|
|
41
|
-
barebrowse install Auto-configure MCP for Claude Desktop / Cursor / Claude Code
|
|
42
|
-
barebrowse browse <url> One-shot browse, print ARIA snapshot
|
|
43
217
|
|
|
44
|
-
|
|
45
|
-
import { browse, connect } from 'barebrowse';
|
|
218
|
+
// --- Flag parsing helpers ---
|
|
46
219
|
|
|
47
|
-
|
|
48
|
-
|
|
220
|
+
function parseFlag(name) {
|
|
221
|
+
// --name=value or --name value
|
|
222
|
+
for (let i = 0; i < args.length; i++) {
|
|
223
|
+
if (args[i].startsWith(name + '=')) return args[i].slice(name.length + 1);
|
|
224
|
+
if (args[i] === name && args[i + 1] && !args[i + 1].startsWith('--')) return args[i + 1];
|
|
225
|
+
}
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
49
228
|
|
|
50
|
-
|
|
51
|
-
|
|
229
|
+
function hasFlag(name) {
|
|
230
|
+
return args.includes(name);
|
|
52
231
|
}
|
|
53
232
|
|
|
233
|
+
|
|
54
234
|
// --- MCP auto-installer ---
|
|
55
235
|
|
|
56
236
|
function install() {
|
|
237
|
+
// Handle --skill flag
|
|
238
|
+
if (hasFlag('--skill')) {
|
|
239
|
+
installSkill();
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
57
243
|
const mcpEntry = {
|
|
58
244
|
command: 'npx',
|
|
59
245
|
args: ['barebrowse', 'mcp'],
|
|
@@ -62,10 +248,7 @@ function install() {
|
|
|
62
248
|
const targets = detectTargets();
|
|
63
249
|
|
|
64
250
|
if (targets.length === 0) {
|
|
65
|
-
console.log('No MCP clients detected
|
|
66
|
-
console.log(JSON.stringify({ mcpServers: { barebrowse: mcpEntry } }, null, 2));
|
|
67
|
-
console.log('\nSupported clients: Claude Desktop, Cursor, Claude Code');
|
|
68
|
-
return;
|
|
251
|
+
console.log('No MCP clients detected.\n');
|
|
69
252
|
}
|
|
70
253
|
|
|
71
254
|
let installed = 0;
|
|
@@ -83,7 +266,6 @@ function install() {
|
|
|
83
266
|
|
|
84
267
|
config.mcpServers.barebrowse = mcpEntry;
|
|
85
268
|
|
|
86
|
-
// Ensure parent dir exists
|
|
87
269
|
const dir = join(target.path, '..');
|
|
88
270
|
mkdirSync(dir, { recursive: true });
|
|
89
271
|
|
|
@@ -97,8 +279,28 @@ function install() {
|
|
|
97
279
|
|
|
98
280
|
if (installed > 0) {
|
|
99
281
|
console.log(`\nDone. Restart your MCP client to pick up the new server.`);
|
|
100
|
-
console.log('Tools available: browse, goto, snapshot, click, type, press, scroll');
|
|
101
282
|
}
|
|
283
|
+
|
|
284
|
+
// Always print Claude Code hint (it uses `claude mcp add`, not JSON config)
|
|
285
|
+
console.log(`\nClaude Code: run this instead of install:`);
|
|
286
|
+
console.log(` claude mcp add barebrowse -- npx barebrowse mcp\n`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function installSkill() {
|
|
290
|
+
const thisDir = fileURLToPath(new URL('.', import.meta.url));
|
|
291
|
+
const src = join(thisDir, '.claude', 'skills', 'barebrowse', 'SKILL.md');
|
|
292
|
+
|
|
293
|
+
if (!existsSync(src)) {
|
|
294
|
+
console.error('SKILL.md not found in package. Reinstall barebrowse.');
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const dest = join(homedir(), '.config', 'claude', 'skills', 'barebrowse', 'SKILL.md');
|
|
299
|
+
const destDir = join(dest, '..');
|
|
300
|
+
mkdirSync(destDir, { recursive: true });
|
|
301
|
+
copyFileSync(src, dest);
|
|
302
|
+
console.log(`Skill installed: ${dest}`);
|
|
303
|
+
console.log('Claude Code will now see barebrowse as an available skill.');
|
|
102
304
|
}
|
|
103
305
|
|
|
104
306
|
function detectTargets() {
|
|
@@ -116,7 +318,6 @@ function detectTargets() {
|
|
|
116
318
|
claudeDesktop = join(home, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
|
|
117
319
|
}
|
|
118
320
|
if (claudeDesktop) {
|
|
119
|
-
// Check if Claude Desktop dir exists (even if config doesn't yet)
|
|
120
321
|
const dir = join(claudeDesktop, '..');
|
|
121
322
|
if (existsSync(dir)) {
|
|
122
323
|
targets.push({ name: 'Claude Desktop', path: claudeDesktop });
|
|
@@ -124,26 +325,11 @@ function detectTargets() {
|
|
|
124
325
|
}
|
|
125
326
|
|
|
126
327
|
// Cursor
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
cursorDir = join(home, '.cursor');
|
|
130
|
-
} else if (os === 'linux') {
|
|
131
|
-
cursorDir = join(home, '.cursor');
|
|
132
|
-
} else if (os === 'win32') {
|
|
133
|
-
cursorDir = join(home, '.cursor');
|
|
134
|
-
}
|
|
135
|
-
if (cursorDir && existsSync(cursorDir)) {
|
|
328
|
+
const cursorDir = join(home, '.cursor');
|
|
329
|
+
if (existsSync(cursorDir)) {
|
|
136
330
|
targets.push({ name: 'Cursor', path: join(cursorDir, 'mcp.json') });
|
|
137
331
|
}
|
|
138
332
|
|
|
139
|
-
// Claude Code (project-level .mcp.json in cwd)
|
|
140
|
-
const cwd = process.cwd();
|
|
141
|
-
const claudeCodePath = join(cwd, '.mcp.json');
|
|
142
|
-
// Only suggest if we're in a project directory (has package.json or .git)
|
|
143
|
-
if (existsSync(join(cwd, 'package.json')) || existsSync(join(cwd, '.git'))) {
|
|
144
|
-
targets.push({ name: 'Claude Code (this project)', path: claudeCodePath });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
333
|
return targets;
|
|
148
334
|
}
|
|
149
335
|
|
|
@@ -154,3 +340,58 @@ function readJsonOrEmpty(path) {
|
|
|
154
340
|
return {};
|
|
155
341
|
}
|
|
156
342
|
}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
// --- Usage ---
|
|
346
|
+
|
|
347
|
+
function printUsage() {
|
|
348
|
+
process.stdout.write(`barebrowse -- CDP-direct browsing for autonomous agents
|
|
349
|
+
|
|
350
|
+
Session:
|
|
351
|
+
barebrowse open [url] [flags] Open browser session
|
|
352
|
+
barebrowse close Close session
|
|
353
|
+
barebrowse status Check session status
|
|
354
|
+
|
|
355
|
+
Open flags:
|
|
356
|
+
--mode=headless|headed|hybrid Browser mode (default: headless)
|
|
357
|
+
--port=N CDP port for headed mode
|
|
358
|
+
--no-cookies Skip cookie injection
|
|
359
|
+
--browser=firefox|chromium Cookie source browser
|
|
360
|
+
--timeout=N Navigation timeout in ms
|
|
361
|
+
--prune-mode=act|read Default pruning mode
|
|
362
|
+
--no-consent Skip consent dismissal
|
|
363
|
+
|
|
364
|
+
Navigation:
|
|
365
|
+
barebrowse goto <url> Navigate to URL
|
|
366
|
+
barebrowse snapshot [--mode=M] ARIA snapshot -> .barebrowse/page-*.yml
|
|
367
|
+
barebrowse screenshot [--format] Screenshot -> .barebrowse/screenshot-*.png
|
|
368
|
+
|
|
369
|
+
Interaction:
|
|
370
|
+
barebrowse click <ref> Click element
|
|
371
|
+
barebrowse type <ref> <text> Type text (--clear to replace)
|
|
372
|
+
barebrowse fill <ref> <text> Clear + type
|
|
373
|
+
barebrowse press <key> Press key (Enter, Tab, Escape, ...)
|
|
374
|
+
barebrowse scroll <deltaY> Scroll (positive=down)
|
|
375
|
+
barebrowse hover <ref> Hover element
|
|
376
|
+
barebrowse select <ref> <value> Select dropdown value
|
|
377
|
+
|
|
378
|
+
Debugging:
|
|
379
|
+
barebrowse eval <expression> Run JS in page context
|
|
380
|
+
barebrowse wait-idle [--timeout] Wait for network idle
|
|
381
|
+
barebrowse console-logs Console logs -> .barebrowse/console-*.json
|
|
382
|
+
barebrowse network-log Network log -> .barebrowse/network-*.json
|
|
383
|
+
|
|
384
|
+
One-shot:
|
|
385
|
+
barebrowse browse <url> [mode] Browse + print snapshot to stdout
|
|
386
|
+
|
|
387
|
+
MCP:
|
|
388
|
+
barebrowse mcp Start MCP server (JSON-RPC over stdio)
|
|
389
|
+
barebrowse install Auto-configure MCP for Claude Desktop / Cursor
|
|
390
|
+
barebrowse install --skill Install SKILL.md for Claude Code
|
|
391
|
+
|
|
392
|
+
As a library:
|
|
393
|
+
import { browse, connect } from 'barebrowse';
|
|
394
|
+
|
|
395
|
+
More: see README.md or barebrowse.context.md
|
|
396
|
+
`);
|
|
397
|
+
}
|