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
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Validation Log
|
|
2
|
+
|
|
3
|
+
What's been tested against the real world. Updated when new sites or features are validated.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Test suite (64 tests, 6 files)
|
|
8
|
+
|
|
9
|
+
| File | Tests | Type | What it covers |
|
|
10
|
+
|------|-------|------|----------------|
|
|
11
|
+
| `test/unit/prune.test.js` | 16 | Unit | 9-step pruning pipeline in isolation |
|
|
12
|
+
| `test/unit/auth.test.js` | 7 | Unit | Cookie extraction from Firefox/Chromium |
|
|
13
|
+
| `test/unit/cdp.test.js` | 5 | Unit | Browser discovery, launch, CDP client, sessions |
|
|
14
|
+
| `test/integration/browse.test.js` | 11 | Integration | Full `browse()` and `connect()` pipeline |
|
|
15
|
+
| `test/integration/cli.test.js` | 10 | Integration | CLI session lifecycle: open/snapshot/goto/click/eval/console/network/close |
|
|
16
|
+
| `test/integration/interact.test.js` | 15 | E2E | Real interactions on data: fixtures + live sites |
|
|
17
|
+
|
|
18
|
+
Run all: `node --test test/unit/*.test.js test/integration/*.test.js`
|
|
19
|
+
|
|
20
|
+
## Site validation matrix
|
|
21
|
+
|
|
22
|
+
Tested across 16+ sites, 8 countries, 7 languages.
|
|
23
|
+
|
|
24
|
+
| Site | Consent | Cookies | Interactions | Notes |
|
|
25
|
+
|------|---------|---------|-------------|-------|
|
|
26
|
+
| google.com | NL dialog dismissed | Firefox injection | Search (combobox + Enter) | Bot-blocks headless |
|
|
27
|
+
| youtube.com | Bypassed via cookies | Firefox injection | Search + video playback | Full e2e demo, SPA nav |
|
|
28
|
+
| bbc.com | SourcePoint dismissed | -- | -- | Button outside dialog |
|
|
29
|
+
| wikipedia.org | -- | -- | Link click + navigation | Clean, no consent |
|
|
30
|
+
| github.com | -- | -- | SPA navigation | Needs settle time |
|
|
31
|
+
| duckduckgo.com | -- | -- | Search + results | Headless-friendly |
|
|
32
|
+
| news.ycombinator.com | -- | -- | Story link click | Clean, simple DOM |
|
|
33
|
+
| amazon.de | Banner dismissed | -- | -- | |
|
|
34
|
+
| theguardian.com | CMP dismissed | -- | -- | |
|
|
35
|
+
| spiegel.de | CMP dismissed | -- | -- | German |
|
|
36
|
+
| lemonde.fr | CMP dismissed | -- | -- | French |
|
|
37
|
+
| elpais.com | CMP dismissed | -- | -- | Spanish |
|
|
38
|
+
| corriere.it | CMP dismissed | -- | -- | Italian |
|
|
39
|
+
| nos.nl | CMP dismissed | -- | -- | Dutch |
|
|
40
|
+
| bild.de | CMP dismissed | -- | -- | German |
|
|
41
|
+
| nu.nl | CMP dismissed | -- | -- | Dutch |
|
|
42
|
+
| booking.com | Banner dismissed | -- | -- | |
|
|
43
|
+
| nytimes.com | -- | -- | -- | No consent wall |
|
|
44
|
+
| stackoverflow.com | Footer link only | -- | -- | Not blocking |
|
|
45
|
+
| cnn.com | -- | -- | -- | No consent wall |
|
|
46
|
+
| reddit.com | -- | -- | Fallback to old.reddit | Bot-blocks headless |
|
|
47
|
+
|
|
48
|
+
## Token reduction measurements
|
|
49
|
+
|
|
50
|
+
| Page | Raw ARIA | Pruned | Reduction |
|
|
51
|
+
|------|----------|--------|-----------|
|
|
52
|
+
| example.com | 377 chars | 45 chars | 88% |
|
|
53
|
+
| Hacker News | 51,726 chars | 27,197 chars | 47% |
|
|
54
|
+
| Wikipedia (article) | 109,479 chars | 40,566 chars | 63% |
|
|
55
|
+
| DuckDuckGo | 42,254 chars | 5,407 chars | 87% |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## CLI manual validation (v0.3.0)
|
|
60
|
+
|
|
61
|
+
Full end-to-end validation of every CLI command against real websites.
|
|
62
|
+
|
|
63
|
+
### Session lifecycle
|
|
64
|
+
|
|
65
|
+
| Command | Result |
|
|
66
|
+
|---------|--------|
|
|
67
|
+
| `barebrowse open https://example.com` | Session started, pid+port printed, session.json created |
|
|
68
|
+
| `barebrowse status` | Shows running pid, port, start time |
|
|
69
|
+
| `barebrowse close` | "Session closed", session.json removed, daemon exited |
|
|
70
|
+
| `status` after close | "No session found", exit code 1 |
|
|
71
|
+
| `click 5` with no session | "No active session. Run `barebrowse open` first.", exit 1 |
|
|
72
|
+
| double `open` | "Session already running. Use `barebrowse close` first.", exit 1 |
|
|
73
|
+
|
|
74
|
+
### Navigation + snapshots (example.com, HN)
|
|
75
|
+
|
|
76
|
+
| Command | Result |
|
|
77
|
+
|---------|--------|
|
|
78
|
+
| `snapshot` (example.com) | `.barebrowse/page-*.yml` created, clean formatting |
|
|
79
|
+
| `snapshot --mode=read` | Read mode includes paragraphs, each node on own line |
|
|
80
|
+
| `goto https://news.ycombinator.com` | "ok" |
|
|
81
|
+
| `snapshot` (HN) | Clean ARIA tree with refs, proper newline separation |
|
|
82
|
+
| `screenshot` | Valid 780x493 PNG file |
|
|
83
|
+
|
|
84
|
+
### Interactions (DuckDuckGo search)
|
|
85
|
+
|
|
86
|
+
| Command | Result |
|
|
87
|
+
|---------|--------|
|
|
88
|
+
| `type 12 barebrowse npm` | "ok", multi-word text correctly joined |
|
|
89
|
+
| `press Enter` | "ok", search submitted |
|
|
90
|
+
| `wait-idle` | "ok", waited for network settle |
|
|
91
|
+
| `eval "document.title"` | `"barebrowse npm at DuckDuckGo"` |
|
|
92
|
+
| `snapshot` | Search results page, clean formatting with refs |
|
|
93
|
+
| `fill 2583 hello world` | "ok", cleared search box + typed new text |
|
|
94
|
+
| `hover 2402` | "ok" |
|
|
95
|
+
| `scroll 300` | "ok" |
|
|
96
|
+
|
|
97
|
+
### Debugging commands
|
|
98
|
+
|
|
99
|
+
| Command | Result |
|
|
100
|
+
|---------|--------|
|
|
101
|
+
| `eval "1 + 1"` | `2` |
|
|
102
|
+
| `eval "document.location.href"` | `"https://news.ycombinator.com/news"` |
|
|
103
|
+
| `eval "console.log('test'); console.error('err')"` | `ok` (undefined return) |
|
|
104
|
+
| `console-logs` | `.json (2 entries)` — log + error captured with types and timestamps |
|
|
105
|
+
| `network-log` | `.json (15 entries)` — all requests with URL, method, status |
|
|
106
|
+
| `network-log --failed` | `.json (1 entries)` — filtered to failed/4xx+ only |
|
|
107
|
+
|
|
108
|
+
### Legacy + install commands
|
|
109
|
+
|
|
110
|
+
| Command | Result |
|
|
111
|
+
|---------|--------|
|
|
112
|
+
| `browse https://example.com` | One-shot snapshot to stdout |
|
|
113
|
+
| `install` | "No MCP clients detected" + Claude Code hint |
|
|
114
|
+
| `install --skill` | SKILL.md copied to `~/.config/claude/skills/barebrowse/` |
|
|
115
|
+
| (no args) | Clean help output with all commands |
|
|
116
|
+
|
|
117
|
+
### Bug found and fixed during validation
|
|
118
|
+
|
|
119
|
+
**`src/aria.js` line 23**: ignored nodes joined children with `''` instead of `'\n'`, causing sibling subtrees to concatenate on one line (e.g. `[ref=15]- _promote`). Fixed to `.filter(Boolean).join('\n')`. All 64 tests pass with the fix.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
*Add new validation entries when testing against new sites or features.*
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Definition of Done
|
|
2
|
+
|
|
3
|
+
A feature or change is "done" when ALL of these are true.
|
|
4
|
+
|
|
5
|
+
## Code
|
|
6
|
+
|
|
7
|
+
- [ ] Works end-to-end (not just the happy path)
|
|
8
|
+
- [ ] No heavy dependencies added (vanilla -> stdlib -> external hierarchy respected)
|
|
9
|
+
- [ ] Under reasonable line count -- no bloat
|
|
10
|
+
- [ ] Clean process management -- no orphan browser processes
|
|
11
|
+
- [ ] No security vulnerabilities introduced (command injection, XSS, etc.)
|
|
12
|
+
|
|
13
|
+
## Tests
|
|
14
|
+
|
|
15
|
+
- [ ] Existing tests still pass: `node --test test/unit/*.test.js test/integration/*.test.js`
|
|
16
|
+
- [ ] New behavior has test coverage (integration preferred over unit)
|
|
17
|
+
- [ ] Bug fixes include a regression test that fails before the fix
|
|
18
|
+
|
|
19
|
+
## Documentation
|
|
20
|
+
|
|
21
|
+
- [ ] `docs/00-context/system-state.md` updated if architecture changed
|
|
22
|
+
- [ ] `docs/03-logs/decisions-log.md` updated if a design decision was made
|
|
23
|
+
- [ ] `barebrowse.context.md` updated if public API changed
|
|
24
|
+
- [ ] `CHANGELOG.md` updated with what changed
|
|
25
|
+
|
|
26
|
+
## Not required (avoid over-engineering)
|
|
27
|
+
|
|
28
|
+
- 100% code coverage
|
|
29
|
+
- TypeScript types
|
|
30
|
+
- Cross-platform testing (Linux first, others later)
|
|
31
|
+
- Performance benchmarks (unless performance is the feature)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Development Workflow
|
|
2
|
+
|
|
3
|
+
## Dev rules
|
|
4
|
+
|
|
5
|
+
**POC first.** Always validate logic with a ~15min proof-of-concept before building. Cover happy path + common edges. POC works -> design properly -> build with tests. Never ship the POC.
|
|
6
|
+
|
|
7
|
+
**Build incrementally.** Break work into small independent modules. One piece at a time, each must work on its own before integrating.
|
|
8
|
+
|
|
9
|
+
**Dependency hierarchy -- follow strictly:**
|
|
10
|
+
1. Vanilla language -- write it yourself if <50 lines and not security-critical
|
|
11
|
+
2. Standard library -- `node:test`, `node:fs`, `node:crypto`, `node:sqlite`
|
|
12
|
+
3. External -- only when stdlib can't do it in <100 lines. Must be maintained, lightweight, widely adopted
|
|
13
|
+
|
|
14
|
+
**Exception:** Always use vetted libraries for security-critical code (crypto, auth, sanitization).
|
|
15
|
+
|
|
16
|
+
**Lightweight over complex.** Fewer moving parts, fewer deps, less config. Simple > clever. Readable > elegant.
|
|
17
|
+
|
|
18
|
+
**Open-source only.** No vendor lock-in. Every line of code must have a purpose -- no speculative code, no premature abstractions.
|
|
19
|
+
|
|
20
|
+
## Language and runtime
|
|
21
|
+
|
|
22
|
+
- Vanilla JavaScript, ES modules, no build step
|
|
23
|
+
- Node.js >= 22 (built-in WebSocket, built-in SQLite)
|
|
24
|
+
- No TypeScript -- can add types later if needed
|
|
25
|
+
|
|
26
|
+
## Running tests
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# All 54 tests
|
|
30
|
+
node --test test/unit/*.test.js test/integration/*.test.js
|
|
31
|
+
|
|
32
|
+
# Unit only (fast, no network)
|
|
33
|
+
node --test test/unit/prune.test.js
|
|
34
|
+
node --test test/unit/auth.test.js
|
|
35
|
+
node --test test/unit/cdp.test.js
|
|
36
|
+
|
|
37
|
+
# Integration (needs Chromium + network)
|
|
38
|
+
node --test test/integration/browse.test.js
|
|
39
|
+
node --test test/integration/interact.test.js
|
|
40
|
+
|
|
41
|
+
# Quick smoke test
|
|
42
|
+
node -e "import { browse } from './src/index.js'; console.log(await browse('https://example.com'))"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Testing standards
|
|
46
|
+
|
|
47
|
+
- **Test behavior, not implementation.** Call the public API, assert on observable output.
|
|
48
|
+
- **Integration tests are the sweet spot.** Real components working together.
|
|
49
|
+
- **No test framework deps.** `node:test` and `node:assert/strict` only.
|
|
50
|
+
- **Always `page.close()` in a `finally` block** to avoid leaked browser processes.
|
|
51
|
+
- **Use `data:` URL fixtures** for deterministic tests (no network dependency).
|
|
52
|
+
- **Real-site tests** go in `interact.test.js`, grouped by site.
|
|
53
|
+
|
|
54
|
+
See `docs/04-process/testing.md` for the full test guide.
|
|
55
|
+
|
|
56
|
+
## Git workflow
|
|
57
|
+
|
|
58
|
+
- Main branch: `main`
|
|
59
|
+
- Commit messages: conventional (`fix:`, `feat:`, `chore:`, `docs:`, `release:`)
|
|
60
|
+
- No force pushes to main
|
|
61
|
+
|
|
62
|
+
## Environment
|
|
63
|
+
|
|
64
|
+
- OS: Fedora Linux, KDE Plasma, Wayland
|
|
65
|
+
- Node: 22.22.0
|
|
66
|
+
- Browser: `/usr/bin/chromium-browser`
|
|
67
|
+
- Default browser: Firefox (cookies extracted from `~/.mozilla/firefox/*.default-release/cookies.sqlite`)
|
|
68
|
+
- KWallet has Chromium Safe Storage key
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
node --test test/unit/*.test.js test/integration/*.test.js
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
64 tests, 6 files, ~60s on a typical machine. No test framework -- uses Node's built-in `node:test` runner.
|
|
10
10
|
|
|
11
11
|
## Test pyramid
|
|
12
12
|
|
|
13
13
|
```
|
|
14
14
|
/ E2E \ 15 tests — real websites (Google, Wikipedia, GitHub, etc.)
|
|
15
15
|
/----------\
|
|
16
|
-
/ Integration \
|
|
16
|
+
/ Integration \ 21 tests — browse/connect pipeline + CLI session lifecycle
|
|
17
17
|
/----------------\
|
|
18
18
|
/ Unit \ 28 tests — pruning, cookie extraction, CDP client, browser launch
|
|
19
19
|
/--------------------\
|
|
@@ -98,6 +98,25 @@ Tests the full `browse()` and `connect()` pipeline end-to-end against real pages
|
|
|
98
98
|
| 10 | connect() | supports multiple navigations in one session | Multiple goto() calls on same page |
|
|
99
99
|
| 11 | connect() | snapshot accepts prune: false for raw output | snapshot(false) preserves full tree |
|
|
100
100
|
|
|
101
|
+
### `test/integration/cli.test.js` -- 10 tests
|
|
102
|
+
|
|
103
|
+
Tests the full CLI session lifecycle: daemon spawn, command dispatch over HTTP, and cleanup. Uses a temp directory so tests don't pollute the project.
|
|
104
|
+
|
|
105
|
+
| # | Test | What it validates |
|
|
106
|
+
|---|------|-------------------|
|
|
107
|
+
| 1 | open starts a daemon and creates session.json | `barebrowse open about:blank` spawns daemon, writes session.json with port+pid |
|
|
108
|
+
| 2 | status shows running session | `barebrowse status` reports pid, port, start time |
|
|
109
|
+
| 3 | snapshot creates a .yml file | `barebrowse snapshot` writes .barebrowse/page-*.yml |
|
|
110
|
+
| 4 | goto navigates and snapshot shows new page content | `barebrowse goto example.com` + snapshot contains "Example Domain" + refs |
|
|
111
|
+
| 5 | click sends click command | `barebrowse click <ref>` returns "ok" |
|
|
112
|
+
| 6 | eval executes JS and returns result | `barebrowse eval 1+1` returns "2" |
|
|
113
|
+
| 7 | console-logs creates a .json file | After eval with console.log, `console-logs` writes JSON |
|
|
114
|
+
| 8 | network-log creates a .json file | `network-log` writes JSON with request entries |
|
|
115
|
+
| 9 | close shuts down the daemon | `barebrowse close` removes session.json, daemon exits |
|
|
116
|
+
| 10 | status after close shows no session | `barebrowse status` exits non-zero when no session |
|
|
117
|
+
|
|
118
|
+
Note: Tests run sequentially within the suite (each depends on the session opened in test 1). The `after()` hook ensures daemon cleanup even if tests fail.
|
|
119
|
+
|
|
101
120
|
---
|
|
102
121
|
|
|
103
122
|
## E2E tests (15 tests)
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# barebrowse -- Documentation
|
|
2
|
+
|
|
3
|
+
## Navigation
|
|
4
|
+
|
|
5
|
+
### 00-context/ -- Why and what exists
|
|
6
|
+
|
|
7
|
+
| File | What's in it |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| [vision.md](00-context/vision.md) | What barebrowse is, what it's not, the core insight, success criteria |
|
|
10
|
+
| [assumptions.md](00-context/assumptions.md) | Hard constraints, assumptions, known limitations, risks |
|
|
11
|
+
| [system-state.md](00-context/system-state.md) | Current architecture, full pipeline, module table, capabilities, tested sites |
|
|
12
|
+
|
|
13
|
+
### 01-product/ -- What the product must do
|
|
14
|
+
|
|
15
|
+
| File | What's in it |
|
|
16
|
+
|------|-------------|
|
|
17
|
+
| [prd.md](01-product/prd.md) | Product requirements, API design, three modes, pruning strategy, future features |
|
|
18
|
+
|
|
19
|
+
### 02-features/ -- How features are designed
|
|
20
|
+
|
|
21
|
+
*Feature-specific docs go here as the project grows.*
|
|
22
|
+
|
|
23
|
+
### 03-logs/ -- What changed over time
|
|
24
|
+
|
|
25
|
+
| File | What's in it |
|
|
26
|
+
|------|-------------|
|
|
27
|
+
| [decisions-log.md](03-logs/decisions-log.md) | Settled design decisions with rationale (don't re-debate these) |
|
|
28
|
+
| [implementation-log.md](03-logs/implementation-log.md) | What changed per version (summary of CHANGELOG) |
|
|
29
|
+
| [bug-log.md](03-logs/bug-log.md) | Bugs: symptom, root cause, fix, regression test |
|
|
30
|
+
| [validation-log.md](03-logs/validation-log.md) | Test suite results, site validation matrix, token reduction measurements |
|
|
31
|
+
| [insights.md](03-logs/insights.md) | Lessons learned, repos studied, technical patterns |
|
|
32
|
+
|
|
33
|
+
### 04-process/ -- How to work with this system
|
|
34
|
+
|
|
35
|
+
| File | What's in it |
|
|
36
|
+
|------|-------------|
|
|
37
|
+
| [dev-workflow.md](04-process/dev-workflow.md) | Dev rules, dependency hierarchy, running tests, environment setup |
|
|
38
|
+
| [definition-of-done.md](04-process/definition-of-done.md) | Checklist: when is a feature/fix actually done |
|
|
39
|
+
| [testing.md](04-process/testing.md) | Test pyramid, all 64 tests documented, writing new tests, CI strategy |
|
|
40
|
+
|
|
41
|
+
### archive/ -- Historical docs
|
|
42
|
+
|
|
43
|
+
| File | Why archived |
|
|
44
|
+
|------|-------------|
|
|
45
|
+
| [poc-plan.md](archive/poc-plan.md) | All 4 POC phases completed. Useful bits migrated to system-state.md and testing.md. |
|
|
46
|
+
|
|
47
|
+
## Also at project root
|
|
48
|
+
|
|
49
|
+
| File | Purpose |
|
|
50
|
+
|------|---------|
|
|
51
|
+
| `README.md` | Public-facing project overview |
|
|
52
|
+
| `barebrowse.context.md` | LLM-consumable integration guide (full API, gotchas, wiring) |
|
|
53
|
+
| `.claude/skills/barebrowse/SKILL.md` | CLI command reference + Claude Code skill definition |
|
|
54
|
+
| `CHANGELOG.md` | Detailed version-by-version changelog |
|
|
55
|
+
| `CLAUDE.md` | AI agent instructions for this project |
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# barebrowse — POC Plan
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-02-22
|
|
4
|
+
**Goal:** Prove that an autonomous agent can get an authenticated, pruned ARIA snapshot of any web page via CDP — no Playwright, no bundled browser.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Repo Structure
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
barebrowse/
|
|
12
|
+
├── src/
|
|
13
|
+
│ ├── index.js # Public API: browse(), connect()
|
|
14
|
+
│ ├── chromium.js # Find/launch/connect to Chromium browsers
|
|
15
|
+
│ ├── cdp.js # Vanilla WebSocket CDP client
|
|
16
|
+
│ ├── aria.js # Accessibility.getFullAXTree → structured tree
|
|
17
|
+
│ ├── auth.js # Cookie extraction + CDP Network.setCookie injection
|
|
18
|
+
│ ├── prune.js # ARIA tree pruning (ported from mcprune)
|
|
19
|
+
│ ├── interact.js # Click, type, scroll via CDP Input domain
|
|
20
|
+
│ ├── consent.js # Auto-dismiss cookie consent dialogs
|
|
21
|
+
│ └── stealth.js # Anti-detection patches via Runtime.evaluate (Phase 4)
|
|
22
|
+
├── test/
|
|
23
|
+
│ ├── integration/
|
|
24
|
+
│ │ ├── browse.test.js # End-to-end: URL → pruned snapshot
|
|
25
|
+
│ │ ├── auth.test.js # Cookie injection → authenticated page
|
|
26
|
+
│ │ └── interact.test.js # Click/type on a live page
|
|
27
|
+
│ └── unit/
|
|
28
|
+
│ ├── cdp.test.js # CDP client message handling
|
|
29
|
+
│ ├── aria.test.js # ARIA tree formatting
|
|
30
|
+
│ └── prune.test.js # Pruning logic on sample trees
|
|
31
|
+
├── docs/
|
|
32
|
+
│ ├── prd.md # Product requirements (comprehensive)
|
|
33
|
+
│ └── poc-plan.md # This file
|
|
34
|
+
├── package.json
|
|
35
|
+
└── CLAUDE.md
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**No build step.** Vanilla JS, ES modules, runs directly with Node.js >= 22.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Phases
|
|
43
|
+
|
|
44
|
+
### Phase 1 — CDP + ARIA Foundation
|
|
45
|
+
|
|
46
|
+
**Prove:** Get an ARIA tree from any page via CDP, no Playwright.
|
|
47
|
+
|
|
48
|
+
**Files:**
|
|
49
|
+
- `src/chromium.js` — Find installed Chromium browsers on the system (Chrome, Chromium, Brave, Edge). Launch headless with `--headless=new --remote-debugging-port=<port>`. Parse CDP WebSocket URL from stderr output.
|
|
50
|
+
- `src/cdp.js` — Vanilla WebSocket client that speaks CDP. Send JSON commands, receive responses and events. Handle command IDs, promises, event subscriptions. ~100 lines.
|
|
51
|
+
- `src/aria.js` — Call `Accessibility.getFullAXTree` via CDP. Transform the raw CDP response (flat array of AXNodes with parentId references) into a nested tree structure. Format as readable output.
|
|
52
|
+
- `src/index.js` — Wire chromium → cdp → aria into `browse(url)` function. Minimal, just the pipeline.
|
|
53
|
+
|
|
54
|
+
**Test:**
|
|
55
|
+
```bash
|
|
56
|
+
node -e "import { browse } from './src/index.js'; console.log(await browse('https://example.com'))"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**DoD:**
|
|
60
|
+
- [x] `chromium.js` finds and launches at least one Chromium browser on Fedora Linux
|
|
61
|
+
- [x] `cdp.js` connects via WebSocket, sends commands, receives responses
|
|
62
|
+
- [x] `aria.js` returns a structured ARIA tree for any public page
|
|
63
|
+
- [x] `browse(url)` works end-to-end with zero external dependencies
|
|
64
|
+
- [x] Headless Chrome process is cleaned up on close
|
|
65
|
+
|
|
66
|
+
### Phase 2 — Auth + Prune
|
|
67
|
+
|
|
68
|
+
**Prove:** Authenticated, pruned ARIA snapshot of a Cloudflare-protected page.
|
|
69
|
+
|
|
70
|
+
**Files:**
|
|
71
|
+
- `src/auth.js` — Extract cookies from user's browser profile (use sweet-cookie or implement minimal extraction from Chrome's Cookies SQLite DB + Linux keyring decryption via `secret-tool`). Inject via CDP `Network.setCookie` before navigation.
|
|
72
|
+
- `src/prune.js` — Port mcprune's pruning logic as a pure function. Input: raw ARIA tree. Output: pruned ARIA tree. Role-based: keep landmarks + interactive elements, drop noise/structural wrappers.
|
|
73
|
+
- Update `src/index.js` — Add cookie injection and pruning to the `browse()` pipeline.
|
|
74
|
+
|
|
75
|
+
**Test:**
|
|
76
|
+
```bash
|
|
77
|
+
# Should return authenticated content, not a login wall or CF challenge
|
|
78
|
+
node -e "import { browse } from './src/index.js'; console.log(await browse('https://some-cf-protected-site.com'))"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**DoD:**
|
|
82
|
+
- [x] `auth.js` extracts cookies from Firefox profile on Linux (also supports Chromium when installed)
|
|
83
|
+
- [x] Cookies injected via CDP before navigation
|
|
84
|
+
- [ ] CF-protected page returns real content, not challenge page (needs active session to test)
|
|
85
|
+
- [x] `prune.js` reduces ARIA tree by 47%+ on HN (minimal site — heavier sites will see 70%+)
|
|
86
|
+
- [x] Pruned output preserves all interactive elements and landmarks
|
|
87
|
+
- [x] `browse(url)` returns pruned, authenticated snapshot by default
|
|
88
|
+
|
|
89
|
+
### Phase 3 — Headed Mode + Interaction
|
|
90
|
+
|
|
91
|
+
**Prove:** Connect to user's running browser and interact with a logged-in page.
|
|
92
|
+
|
|
93
|
+
**Files:**
|
|
94
|
+
- Update `src/chromium.js` — Add `connect()` mode: connect to an already-running browser's debug port instead of launching a new one. Detect running browsers with debug ports.
|
|
95
|
+
- `src/interact.js` — Click (`Input.dispatchMouseEvent`), type (`Input.dispatchKeyEvent`), scroll. Resolve ARIA node IDs to DOM coordinates for click targets.
|
|
96
|
+
- Update `src/index.js` — Add `connect()` export for long-lived sessions. Add `mode: 'headed'` option.
|
|
97
|
+
|
|
98
|
+
**Prerequisite:** User must launch their browser with `--remote-debugging-port=9222` flag.
|
|
99
|
+
|
|
100
|
+
**Test:**
|
|
101
|
+
```bash
|
|
102
|
+
# User has Chrome open with debug port, logged into GitHub
|
|
103
|
+
node -e "
|
|
104
|
+
import { connect } from './src/index.js';
|
|
105
|
+
const page = await connect({ mode: 'headed' });
|
|
106
|
+
await page.goto('https://github.com/notifications');
|
|
107
|
+
console.log(await page.snapshot());
|
|
108
|
+
"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**DoD:**
|
|
112
|
+
- [x] `connect()` attaches to a running Chromium browser via CDP
|
|
113
|
+
- [x] Same ARIA + prune pipeline works on headed browser
|
|
114
|
+
- [x] `click()` and `type()` send real input events via CDP
|
|
115
|
+
- [x] `press()` sends special keys (Enter, Tab, Escape, arrows) — triggers form submit
|
|
116
|
+
- [x] `scrollIntoView` before click ensures off-screen elements are reachable
|
|
117
|
+
- [x] `type({ clear: true })` replaces pre-filled input content
|
|
118
|
+
- [x] `waitForNavigation()` waits for page load after link clicks
|
|
119
|
+
- [x] Interactions tested against real sites: Wikipedia, GitHub, Google, Hacker News, DuckDuckGo, YouTube
|
|
120
|
+
- [x] Browser stays open after barebrowse disconnects
|
|
121
|
+
- [x] Cookie injection via `page.injectCookies()` for headed mode (Firefox → Chromium)
|
|
122
|
+
- [x] Permission prompts suppressed via launch flags + CDP `Browser.setPermission`
|
|
123
|
+
- [x] Cookie consent dialogs auto-dismissed across 16+ sites in 7 languages
|
|
124
|
+
- [x] YouTube end-to-end: Firefox cookies → search → click → video playback in headed mode
|
|
125
|
+
|
|
126
|
+
### Phase 4 — Hybrid + bareagent Integration
|
|
127
|
+
|
|
128
|
+
**Prove:** Agent autonomously browses the web using barebrowse tools.
|
|
129
|
+
|
|
130
|
+
**Files:**
|
|
131
|
+
- Update `src/chromium.js` — Add `mode: 'hybrid'`. Try headless first. If navigation returns a CF challenge or 403, automatically retry in headed mode.
|
|
132
|
+
- `src/stealth.js` — Basic anti-detection: patch `navigator.webdriver`, `navigator.plugins`, `window.chrome`. Applied via `Runtime.evaluate` on new page.
|
|
133
|
+
- Update `src/index.js` — Final API surface: `browse()`, `connect()`.
|
|
134
|
+
|
|
135
|
+
**Test:**
|
|
136
|
+
```js
|
|
137
|
+
import { Loop } from 'bare-agent';
|
|
138
|
+
import { browse } from './src/index.js';
|
|
139
|
+
|
|
140
|
+
const tools = [
|
|
141
|
+
{ name: 'browse', execute: ({ url }) => browse(url) },
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
const loop = new Loop({ provider });
|
|
145
|
+
await loop.run([
|
|
146
|
+
{ role: 'user', content: 'Go to hacker news and tell me the top 3 stories' }
|
|
147
|
+
], tools);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**DoD:**
|
|
151
|
+
- [ ] Hybrid mode automatically falls back when headless is blocked
|
|
152
|
+
- [ ] Stealth patches reduce headless detection on common sites
|
|
153
|
+
- [ ] bareagent can use `browse()` as a tool in its think/act/observe loop
|
|
154
|
+
- [ ] Agent successfully completes a multi-page research task autonomously
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Definition of Done — Full POC
|
|
159
|
+
|
|
160
|
+
The POC is complete when ALL of these are true:
|
|
161
|
+
|
|
162
|
+
1. **`browse(url)` works end-to-end** — URL in, pruned ARIA snapshot out, authenticated as the user
|
|
163
|
+
2. **Zero heavy deps** — no Playwright, no Puppeteer. Only deps: `ws` (WebSocket client, if Node's built-in isn't sufficient) and optionally `sweet-cookie`
|
|
164
|
+
3. **Three modes work** — headless (default), headed (connect to running browser), hybrid (auto-fallback)
|
|
165
|
+
4. **Works on Fedora Linux** — finds Chrome/Chromium/Brave, launches headless, connects headed
|
|
166
|
+
5. **Token-efficient output** — pruned ARIA tree is 70%+ smaller than raw tree
|
|
167
|
+
6. **Clean process management** — headless browser spawned and killed cleanly, no orphan processes
|
|
168
|
+
7. **Under 1,000 lines total** for core src/ (excluding tests)
|
|
169
|
+
8. **Documented** — PRD captures all decisions, this file captures all phases
|
|
170
|
+
|
|
171
|
+
## What the POC is NOT
|
|
172
|
+
|
|
173
|
+
- Not production-ready. No error recovery, no retry logic, no edge case handling beyond happy path.
|
|
174
|
+
- Not cross-platform tested. Linux first (Fedora). macOS/Windows later.
|
|
175
|
+
- Not an MCP server. That's a future wrapper.
|
|
176
|
+
- Not a published npm package. Local development only.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Running Tests
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# All tests (47+ tests)
|
|
184
|
+
node --test test/unit/*.test.js test/integration/*.test.js
|
|
185
|
+
|
|
186
|
+
# Unit tests only (fast, no network)
|
|
187
|
+
node --test test/unit/prune.test.js # 16 tests — pruning logic
|
|
188
|
+
node --test test/unit/auth.test.js # 7 tests — cookie extraction (2 fail when Chromium locked)
|
|
189
|
+
node --test test/unit/cdp.test.js # 5 tests — CDP client + browser launch
|
|
190
|
+
|
|
191
|
+
# Integration tests (needs network + Chromium)
|
|
192
|
+
node --test test/integration/browse.test.js # 11 tests — end-to-end pipeline
|
|
193
|
+
node --test test/integration/interact.test.js # 15 tests — interactions on real sites
|
|
194
|
+
|
|
195
|
+
# Quick smoke test
|
|
196
|
+
node -e "import { browse } from './src/index.js'; console.log(await browse('https://example.com'))"
|
|
197
|
+
|
|
198
|
+
# Headed mode demos (requires: chromium-browser --remote-debugging-port=9222)
|
|
199
|
+
node examples/headed-demo.js # Wikipedia → DuckDuckGo search
|
|
200
|
+
node examples/yt-demo.js # YouTube: Firefox cookies → search → play video
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Repos Studied — What We Borrowed vs Built
|
|
206
|
+
|
|
207
|
+
| steipete repo | What we studied | What we used | Why not more |
|
|
208
|
+
|---|---|---|---|
|
|
209
|
+
| **sweet-cookie** | Cookie extraction (SQLite + keyring) | **Concept only** — wrote `auth.js` ourselves | Not on npm (different package). Our version is simpler, tailored, vanilla JS |
|
|
210
|
+
| **sweetlink** | CDP dual-channel, selector discovery, daemon | **CDP-direct concept only** | Daemon + WebSocket bridge + in-page runtime = bloat. CDP direct is 100 lines vs ~2,000 |
|
|
211
|
+
| **canvas** | Stealth/anti-detection patterns | **Noted for Phase 4** `stealth.js` | Not needed yet — headless + real cookies handles most cases |
|
|
212
|
+
| **mcprune (own)** | ARIA pruning pipeline | **Full port** — `prune.js` (472 lines) | Proven code, adapted node format from Playwright YAML to CDP tree objects |
|
|
213
|
+
|
|
214
|
+
### What to explore in later phases
|
|
215
|
+
|
|
216
|
+
- **Selector discovery** (sweetlink) — crawl ARIA tree, score interactive elements, rank action targets. Phase 3/4.
|
|
217
|
+
- **Stealth patches** (canvas) — `navigator.webdriver`, plugins, chrome object spoofing. Phase 4.
|
|
218
|
+
- **In-page JS execution** (sweetlink) — `Runtime.evaluate` for complex interactions. Phase 3.
|
|
219
|
+
- **Screenshot + visual grounding** — `Page.captureScreenshot` for multimodal agents. Post-POC.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Dev Rules (from AGENT_RULES.md)
|
|
224
|
+
|
|
225
|
+
- **Vanilla JS only.** No TypeScript, no build step, no transpilation.
|
|
226
|
+
- **Dependency hierarchy:** vanilla → stdlib → external. Write it yourself if <50 lines.
|
|
227
|
+
- **Simple > clever.** Readable code a junior can follow.
|
|
228
|
+
- **POC first.** Validate logic before designing. Never ship the POC — rewrite it.
|
|
229
|
+
- **Test behavior, not implementation.** Integration tests over unit tests.
|
|
230
|
+
- **No speculative code.** Every line must have a purpose.
|
package/mcp-server.js
CHANGED
|
@@ -149,7 +149,7 @@ async function handleMessage(msg) {
|
|
|
149
149
|
return jsonrpcResponse(id, {
|
|
150
150
|
protocolVersion: '2024-11-05',
|
|
151
151
|
capabilities: { tools: {} },
|
|
152
|
-
serverInfo: { name: 'barebrowse', version: '0.2.
|
|
152
|
+
serverInfo: { name: 'barebrowse', version: '0.2.2' },
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
package/package.json
CHANGED
package/src/aria.js
CHANGED
|
@@ -20,7 +20,7 @@ export function formatTree(node, depth = 0) {
|
|
|
20
20
|
|
|
21
21
|
// Skip ignored nodes but still process their children
|
|
22
22
|
if (node.ignored) {
|
|
23
|
-
return node.children.map((c) => formatTree(c, depth)).join('');
|
|
23
|
+
return node.children.map((c) => formatTree(c, depth)).filter(Boolean).join('\n');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Skip low-level rendering nodes that are noise for agents
|