@ulpi/browse 0.6.0 → 0.7.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulpi/browse",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/ulpi-io/browse"
package/skill/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: browse
3
- version: 2.4.0
3
+ version: 2.5.0
4
4
  description: |
5
5
  Fast web browsing for AI coding agents via persistent headless Chromium daemon. Navigate to any URL,
6
6
  read page content, click elements, fill forms, run JavaScript, take screenshots,
@@ -260,7 +260,8 @@ browse accessibility Accessibility tree snapshot (ARIA)
260
260
  ### Snapshot (ref-based element selection)
261
261
  ```
262
262
  browse snapshot Full accessibility tree with @refs
263
- browse snapshot -i Interactive elements only (buttons, links, inputs)
263
+ browse snapshot -i Interactive elements only — terse flat list (minimal tokens)
264
+ browse snapshot -i -v Interactive elements — verbose indented tree with props
264
265
  browse snapshot -c Compact (no empty structural elements)
265
266
  browse snapshot -C Cursor-interactive (detect divs with cursor:pointer/onclick/tabindex)
266
267
  browse snapshot -d <N> Limit depth to N levels
package/src/cli.ts CHANGED
@@ -20,7 +20,6 @@ const cliFlags = {
20
20
  contentBoundaries: false,
21
21
  allowedDomains: '' as string,
22
22
  headed: false,
23
- runtime: '' as string,
24
23
  };
25
24
 
26
25
  const BROWSE_PORT = parseInt(process.env.BROWSE_PORT || '0', 10);
@@ -253,7 +252,7 @@ async function startServer(): Promise<ServerState> {
253
252
  : ['bun', 'run', SERVER_SCRIPT];
254
253
  const proc = Bun.spawn(spawnCmd, {
255
254
  stdio: ['ignore', 'pipe', 'pipe'],
256
- env: { ...process.env, __BROWSE_SERVER_MODE: '1', BROWSE_LOCAL_DIR: LOCAL_DIR, BROWSE_INSTANCE, ...(cliFlags.headed ? { BROWSE_HEADED: '1' } : {}), ...(cliFlags.runtime ? { BROWSE_RUNTIME: cliFlags.runtime } : {}) },
255
+ env: { ...process.env, __BROWSE_SERVER_MODE: '1', BROWSE_LOCAL_DIR: LOCAL_DIR, BROWSE_INSTANCE, ...(cliFlags.headed ? { BROWSE_HEADED: '1' } : {}) },
257
256
  });
258
257
 
259
258
  // Don't hold the CLI open
@@ -512,7 +511,7 @@ export async function main() {
512
511
  for (let i = 0; i < a.length; i++) {
513
512
  if (!a[i].startsWith('-')) return i;
514
513
  // Skip flag values for known value-flags
515
- if (a[i] === '--session' || a[i] === '--allowed-domains' || a[i] === '--runtime') i++;
514
+ if (a[i] === '--session' || a[i] === '--allowed-domains') i++;
516
515
  }
517
516
  return a.length;
518
517
  }
@@ -570,27 +569,19 @@ export async function main() {
570
569
  }
571
570
  headed = headed || process.env.BROWSE_HEADED === '1';
572
571
 
573
- // Extract --runtime flag (only before command)
574
- let runtimeName: string | undefined;
575
- const runtimeIdx = args.indexOf('--runtime');
576
- if (runtimeIdx !== -1 && runtimeIdx < findCommandIndex(args)) {
577
- runtimeName = args[runtimeIdx + 1];
578
- if (!runtimeName || runtimeName.startsWith('-')) {
579
- console.error('Usage: browse --runtime <name> <command> [args...]');
580
- process.exit(1);
581
- }
582
- args.splice(runtimeIdx, 2);
583
- }
584
- runtimeName = runtimeName || process.env.BROWSE_RUNTIME || config.runtime || undefined;
585
-
586
572
  // Set global flags for sendCommand()
587
573
  cliFlags.json = jsonMode;
588
574
  cliFlags.contentBoundaries = contentBoundaries;
589
575
  cliFlags.allowedDomains = allowedDomains || '';
590
576
  cliFlags.headed = headed;
591
- cliFlags.runtime = runtimeName || '';
592
577
 
593
578
  // ─── Local commands (no server needed) ─────────────────────
579
+ if (args[0] === 'version' || args[0] === '--version' || args[0] === '-V') {
580
+ const pkg = await import('../package.json');
581
+ console.log(pkg.version);
582
+ return;
583
+ }
584
+
594
585
  if (args[0] === 'instances') {
595
586
  await listInstances();
596
587
  return;
@@ -620,8 +611,8 @@ Inspection: js <expr> | eval <file> | css <sel> <prop> | attrs <sel>
620
611
  element-state <sel> | console [--clear] | network [--clear]
621
612
  cookies | storage [set <k> <v>] | perf
622
613
  value <sel> | count <sel> | clipboard [write <text>]
623
- Visual: screenshot [path] [--full] [--annotate] | pdf [path] | responsive [prefix]
624
- Snapshot: snapshot [-i] [-c] [-C] [-d N] [-s sel]
614
+ Visual: screenshot [path] | pdf [path] | responsive [prefix]
615
+ Snapshot: snapshot [-i] [-v] [-c] [-C] [-d N] [-s sel]
625
616
  Find: find role|text|label|placeholder|testid <query> [name]
626
617
  Compare: diff <url1> <url2> | screenshot-diff <baseline> [current]
627
618
  Multi-step: chain (reads JSON from stdin)
@@ -645,10 +636,10 @@ Options:
645
636
  --content-boundaries Wrap page content in nonce-delimited markers
646
637
  --allowed-domains <d,d> Block navigation/resources outside allowlist
647
638
  --headed Run browser in headed (visible) mode
648
- --runtime <name> Browser runtime (playwright, rebrowser, lightpanda)
649
639
 
650
640
  Snapshot flags:
651
- -i Interactive elements only (buttons, links, inputs)
641
+ -i Interactive elements only (terse flat list by default)
642
+ -v Verbose — full indented tree with props (use with -i)
652
643
  -c Compact — remove empty structural elements
653
644
  -C Cursor-interactive — detect divs with cursor:pointer,
654
645
  onclick, tabindex, data-action (missed by ARIA tree)
package/src/snapshot.ts CHANGED
@@ -30,7 +30,8 @@ const INTERACTIVE_ROLES = new Set([
30
30
  ]);
31
31
 
32
32
  interface SnapshotOptions {
33
- interactive?: boolean; // -i: only interactive elements
33
+ interactive?: boolean; // -i: only interactive elements (terse flat list by default)
34
+ verbose?: boolean; // -v: full indented ARIA tree with props/children (overrides -i terse default)
34
35
  compact?: boolean; // -c: remove empty structural elements
35
36
  depth?: number; // -d N: limit tree depth
36
37
  selector?: string; // -s SEL: scope to CSS selector
@@ -72,6 +73,10 @@ export function parseSnapshotArgs(args: string[]): SnapshotOptions {
72
73
  case '--compact':
73
74
  opts.compact = true;
74
75
  break;
76
+ case '-v':
77
+ case '--verbose':
78
+ opts.verbose = true;
79
+ break;
75
80
  case '-C':
76
81
  case '--cursor':
77
82
  opts.cursor = true;
@@ -417,10 +422,18 @@ export async function handleSnapshot(
417
422
  refMap.set(ref, locator);
418
423
 
419
424
  // Format output line
420
- let outputLine = `${indent}@${ref} [${node.role}]`;
421
- if (node.name) outputLine += ` "${node.name}"`;
422
- if (node.props) outputLine += ` ${node.props}`;
423
- if (node.children) outputLine += `: ${node.children}`;
425
+ // -i without -v: terse flat list (no indent, no props, no children)
426
+ const terse = opts.interactive && !opts.verbose;
427
+ let outputLine: string;
428
+ if (terse) {
429
+ outputLine = `@${ref} [${node.role}]`;
430
+ if (node.name) outputLine += ` "${node.name}"`;
431
+ } else {
432
+ outputLine = `${indent}@${ref} [${node.role}]`;
433
+ if (node.name) outputLine += ` "${node.name}"`;
434
+ if (node.props) outputLine += ` ${node.props}`;
435
+ if (node.children) outputLine += `: ${node.children}`;
436
+ }
424
437
 
425
438
  output.push(outputLine);
426
439
  }