barebrowse 0.4.3 → 0.4.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.4
4
+
5
+ Snapshot URL prefix and MCP large-snapshot handling.
6
+
7
+ ### Snapshot URL (`src/index.js`)
8
+ - First line of every snapshot is now `# <current-page-url>`
9
+ - Works in both `browse()` (uses the url param) and `connect().snapshot()` (uses `Page.getNavigationHistory`)
10
+
11
+ ### MCP maxChars (`mcp-server.js`)
12
+ - `browse` and `snapshot` tools accept `maxChars` param (default 30000)
13
+ - If snapshot exceeds limit: saved to `.barebrowse/page-<timestamp>.yml`, returns file path message
14
+ - If under limit: returned inline as before
15
+
16
+ ### Docs
17
+ - barebrowse.context.md: snapshot format updated with URL line, maxChars documented
18
+ - commands/barebrowse.md + SKILL.md: snapshot example updated
19
+ - docs/00-context/system-state.md: pipeline step 8 updated, MCP maxChars noted
20
+
21
+ ## 0.4.3
22
+
23
+ Cookie consent expanded to 29 languages.
24
+
25
+ - Added: RU, UK, PL, CS, TR, RO, HU, EL, SV, DA, NO, FI, AR, FA, ZH, JA, KO, VI, TH, HI, ID/MS
26
+ - Dialog hints for 11 more languages
27
+ - `.npmignore`: added `.idea/` (leaked in 0.4.2 tarball)
28
+
3
29
  ## 0.4.2
4
30
 
5
31
  Authenticated browsing improvements. MCP sessions now auto-inject cookies and fall back to headed mode when bot-detected.
@@ -87,14 +87,13 @@ const snapshot = await browse('https://example.com', {
87
87
 
88
88
  ## Snapshot format
89
89
 
90
- The snapshot is a YAML-like ARIA tree. Each line is one node:
90
+ The snapshot is a YAML-like ARIA tree. First line is the page URL, second is pruning stats, then the tree:
91
91
 
92
92
  ```
93
- - WebArea "Example Domain" [ref=1]
94
- - heading "Example Domain" [level=1] [ref=3]
95
- - paragraph [ref=5]
96
- - StaticText "This domain is for use in illustrative examples." [ref=6]
97
- - link "More information..." [ref=8]
93
+ # https://example.com/
94
+ # 379 chars 45 chars (88% pruned)
95
+ - heading "Example Domain" [level=1] [ref=3]
96
+ - link "More information..." [ref=8]
98
97
  ```
99
98
 
100
99
  Key rules:
@@ -238,6 +237,8 @@ barebrowse ships an MCP server for direct use with Claude Desktop, Cursor, or an
238
237
 
239
238
  Action tools return `'ok'` -- the agent calls `snapshot` explicitly to observe. This avoids double-token output since MCP tool calls are cheap to chain.
240
239
 
240
+ `browse` and `snapshot` accept a `maxChars` param (default 30000). If the snapshot exceeds the limit, it's saved to `.barebrowse/page-<timestamp>.yml` and a short message with the file path is returned instead.
241
+
241
242
  Session runs in hybrid mode (headless with automatic headed fallback on bot detection). `goto` injects cookies from the user's browser before navigation for authenticated access.
242
243
 
243
244
  Session tools share a singleton page, lazy-created on first use.
@@ -96,11 +96,9 @@ All output files go to `.barebrowse/` in the current directory. Read them with t
96
96
  The snapshot is a YAML-like ARIA tree. Each line is one node:
97
97
 
98
98
  ```
99
- - WebArea "Example Domain" [ref=1]
100
- - heading "Example Domain" [level=1] [ref=3]
101
- - paragraph [ref=5]
102
- - StaticText "This domain is for use in illustrative examples." [ref=6]
103
- - link "More information..." [ref=8]
99
+ # https://example.com/
100
+ # 379 chars 45 chars (88% pruned)
101
+ - heading "Example Domain" [level=1] [ref=3]
104
102
  ```
105
103
 
106
104
  - `[ref=N]` — Use this number with click, type, fill, hover, select, drag, upload
@@ -95,11 +95,9 @@ All output files go to `.barebrowse/` in the current directory. Read them with t
95
95
  The snapshot is a YAML-like ARIA tree. Each line is one node:
96
96
 
97
97
  ```
98
- - WebArea "Example Domain" [ref=1]
99
- - heading "Example Domain" [level=1] [ref=3]
100
- - paragraph [ref=5]
101
- - StaticText "This domain is for use in illustrative examples." [ref=6]
102
- - link "More information..." [ref=8]
98
+ # https://example.com/
99
+ # 379 chars 45 chars (88% pruned)
100
+ - heading "Example Domain" [level=1] [ref=3]
103
101
  ```
104
102
 
105
103
  - `[ref=N]` — Use this number with click, type, fill, hover, select, drag, upload
@@ -155,7 +155,7 @@ Every action returns a **pruned ARIA snapshot** -- the agent's view of the page
155
155
 
156
156
  8. SNAPSHOT Accessibility.getFullAXTree -> nested tree (aria.js)
157
157
  prune.js: 9-step pipeline (47-95% token reduction)
158
- Output: YAML-like text with [ref=N] markers
158
+ Output: URL + pruning stats + YAML-like text with [ref=N] markers
159
159
 
160
160
  9. INTERACT interact.js dispatches real CDP Input events
161
161
  click: scrollIntoView -> getBoxModel -> mousePressed/Released
@@ -311,6 +311,7 @@ Raw JSON-RPC 2.0 over stdio. Zero SDK dependencies. `npm install barebrowse` the
311
311
 
312
312
  12 tools: browse (one-shot), goto, snapshot, click, type, press, scroll, back, forward, drag, upload, pdf.
313
313
  Action tools return `'ok'` -- agent calls `snapshot` explicitly (MCP tool calls are cheap to chain).
314
+ `browse` and `snapshot` accept `maxChars` (default 30000) — large snapshots are saved to `.barebrowse/` and a file path is returned.
314
315
  Session runs in hybrid mode (headless + automatic headed fallback on bot detection). `goto` injects cookies from the user's browser before navigation.
315
316
  Session tools share a singleton page, lazy-created on first use.
316
317
 
package/mcp-server.js CHANGED
@@ -10,6 +10,19 @@
10
10
  */
11
11
 
12
12
  import { browse, connect } from './src/index.js';
13
+ import { mkdirSync, writeFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+
16
+ const MAX_CHARS_DEFAULT = 30000;
17
+ const OUTPUT_DIR = join(process.cwd(), '.barebrowse');
18
+
19
+ function saveSnapshot(text) {
20
+ mkdirSync(OUTPUT_DIR, { recursive: true });
21
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
22
+ const file = join(OUTPUT_DIR, `page-${ts}.yml`);
23
+ writeFileSync(file, text);
24
+ return file;
25
+ }
13
26
 
14
27
  let _page = null;
15
28
 
@@ -27,6 +40,7 @@ const TOOLS = [
27
40
  properties: {
28
41
  url: { type: 'string', description: 'URL to browse' },
29
42
  mode: { type: 'string', enum: ['headless', 'headed', 'hybrid'], description: 'Browser mode (default: headless)' },
43
+ maxChars: { type: 'number', description: 'Max chars to return inline. Larger snapshots are saved to .barebrowse/ and a file path is returned instead. Default: 30000.' },
30
44
  },
31
45
  required: ['url'],
32
46
  },
@@ -45,7 +59,12 @@ const TOOLS = [
45
59
  {
46
60
  name: 'snapshot',
47
61
  description: 'Get the current ARIA snapshot of the session page. Returns a YAML-like tree with [ref=N] markers on interactive elements.',
48
- inputSchema: { type: 'object', properties: {} },
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ maxChars: { type: 'number', description: 'Max chars to return inline. Larger snapshots are saved to .barebrowse/ and a file path is returned instead. Default: 30000.' },
66
+ },
67
+ },
49
68
  },
50
69
  {
51
70
  name: 'click',
@@ -141,9 +160,15 @@ const TOOLS = [
141
160
 
142
161
  async function handleToolCall(name, args) {
143
162
  switch (name) {
144
- case 'browse':
145
- return await browse(args.url, { mode: args.mode });
146
-
163
+ case 'browse': {
164
+ const text = await browse(args.url, { mode: args.mode });
165
+ const limit = args.maxChars ?? MAX_CHARS_DEFAULT;
166
+ if (text.length > limit) {
167
+ const file = saveSnapshot(text);
168
+ return `Snapshot (${text.length} chars) saved to ${file}`;
169
+ }
170
+ return text;
171
+ }
147
172
  case 'goto': {
148
173
  const page = await getPage();
149
174
  try { await page.injectCookies(args.url); } catch {}
@@ -152,7 +177,13 @@ async function handleToolCall(name, args) {
152
177
  }
153
178
  case 'snapshot': {
154
179
  const page = await getPage();
155
- return await page.snapshot();
180
+ const text = await page.snapshot();
181
+ const limit = args.maxChars ?? MAX_CHARS_DEFAULT;
182
+ if (text.length > limit) {
183
+ const file = saveSnapshot(text);
184
+ return `Snapshot (${text.length} chars) saved to ${file}`;
185
+ }
186
+ return text;
156
187
  }
157
188
  case 'click': {
158
189
  const page = await getPage();
@@ -218,7 +249,7 @@ async function handleMessage(msg) {
218
249
  return jsonrpcResponse(id, {
219
250
  protocolVersion: '2024-11-05',
220
251
  capabilities: { tools: {} },
221
- serverInfo: { name: 'barebrowse', version: '0.4.2' },
252
+ serverInfo: { name: 'barebrowse', version: '0.4.4' },
222
253
  });
223
254
  }
224
255
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "barebrowse",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Authenticated web browsing for autonomous agents via CDP. URL in, pruned ARIA snapshot out.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -103,7 +103,7 @@ export async function browse(url, opts = {}) {
103
103
  } else {
104
104
  snapshot = raw;
105
105
  }
106
- const stats = `# ${raw.length.toLocaleString()} chars → ${snapshot.length.toLocaleString()} chars (${Math.round((1 - snapshot.length / raw.length) * 100)}% pruned)`;
106
+ const stats = `# ${url}\n# ${raw.length.toLocaleString()} chars → ${snapshot.length.toLocaleString()} chars (${Math.round((1 - snapshot.length / raw.length) * 100)}% pruned)`;
107
107
  snapshot = stats + '\n' + snapshot;
108
108
 
109
109
  // Step 7: Clean up
@@ -221,10 +221,12 @@ export async function connect(opts = {}) {
221
221
  const result = await ariaTree(page);
222
222
  refMap = result.refMap;
223
223
  const raw = formatTree(result.tree);
224
- if (pruneOpts === false) return raw;
224
+ const { currentIndex, entries } = await page.session.send('Page.getNavigationHistory');
225
+ const pageUrl = entries[currentIndex]?.url || '';
226
+ if (pruneOpts === false) return `# ${pageUrl}\n` + raw;
225
227
  const pruned = pruneTree(result.tree, { mode: pruneOpts?.mode || 'act' });
226
228
  const out = formatTree(pruned);
227
- const stats = `# ${raw.length.toLocaleString()} chars → ${out.length.toLocaleString()} chars (${Math.round((1 - out.length / raw.length) * 100)}% pruned)`;
229
+ const stats = `# ${pageUrl}\n# ${raw.length.toLocaleString()} chars → ${out.length.toLocaleString()} chars (${Math.round((1 - out.length / raw.length) * 100)}% pruned)`;
228
230
  return stats + '\n' + out;
229
231
  },
230
232