8000 GitHub - hmmhmmhm/ax-grep at feat/agent-readiness-benchmark · GitHub
[go: up one dir, main page]

Skip to content
hmmhmmhmPublic
 
 

Repository files navigation

ax-grep

ax-grep turns HTML or a live DOM into a compact semantic tree that is easier for agents and automation code to inspect than raw markup.

It is built for browser extensions, injected scripts, WebView bridges, Workers, and agent pipelines that need page structure without Chrome DevTools Protocol or a platform accessibility API.

import { extract, formatSemanticTreeText } from "ax-grep";

const html = await fetch("https://example.com").then((response) => response.text());
const tree = extract(html);

console.log(formatSemanticTreeText(tree));
document
  main
    heading 'Example Domain'
    p 'This domain is for use in illustrative examples...'
    [i] link 'More information...'

ax-grep is not a replacement for a real browser accessibility tree. It approximates one from DOM, ARIA, computed style, labels, focusability, and element state.

Install

pnpm add ax-grep
npm install ax-grep

Entry Points

Situation Use
HTML string from fetch(), SSR, or a Worker extract(html) from ax-grep
Small Worker/static-only bundle extract(html) from ax-grep/static
Code already running inside the page extract() from ax-grep/browser
Puppeteer, Playwright, WebView, or another external page controller createExtractorScript() from ax-grep

Static HTML

Use the root entry when you have an HTML string. This path does not create a DOM with jsdom and does not launch a browser.

import { extract } from "ax-grep";

const response = await fetch("https://example.com");
const html = await response.text();
const tree = extract(html);

The root extract(html) function is the same static extractor exposed at ax-grep/static.

import { extract } from "ax-grep/static";

const tree = extract(html, {
  includeAttributes: false,
});

Static extraction can infer roles, names, labels, ARIA state, links, forms, headings, tables, and lists from SSR markup. It also applies conservative prompt-size controls by default:

  • skips non-semantic payload tags such as script, style, and template
  • prunes hidden markup and collapsed controlled regions
  • summarizes very large child lists and repeated template-like subtrees
  • detects broad wiki-like and forum-like HTML shapes to tune summarization

Static extraction cannot see computed CSS, layout bounds, client-rendered DOM, shadow DOM, iframe contents, or post-load mutations.

Browser Injection

Use createExtractorScript() when you control a page from the outside. This is the right path for Puppeteer, Playwright, Android WebView, iOS WKWebView, or an agent browser that can evaluate JavaScript.

import { createExtractorScript } from "ax-grep";

const tree = await page.evaluate(createExtractorScript());

Playwright example:

import { chromium } from "playwright";
import { createExtractorScript, formatSemanticTreeText } from "ax-grep";

const browser = await chromium.launch();
const page = await browser.newPage();

await page.goto("https://example.com");

const tree = await page.evaluate(createExtractorScript({
  includeBounds: false,
  includeAttributes: false,
}));

console.log(formatSemanticTreeText(tree));

await browser.close();

WebView-style injection works the same way:

import { createExtractorScript } from "ax-grep";

const script = createExtractorScript({
  mode: "interactive",
  format: "json",
});

// Android: webView.evaluateJavascript(script, callback)
// iOS: webView.evaluateJavaScript(script, completionHandler)

Direct In-Page Usage

Use ax-grep/browser when your code is already executing in the page, such as a browser extension content script.

import { extract, formatSemanticTreeText } from "ax-grep/browser";

const tree = extract({
  mode: "interactive",
  includeBounds: false,
});

console.log(formatSemanticTreeText(tree));

Output Shape

extract() returns a SemanticNode tree:

type SemanticNode = {
  id: string;
  tag: string;
  role: string | null;
  name: string;
  interactive: boolean;
  focusable: boolean;
  selector?: string;
  xpath?: string;
  text?: string;
  value?: string;
  state?: Record<string, unknown>;
  attributes?: Record<string, string>;
  children: SemanticNode[];
};

Use formatSemanticTreeText(tree) for a compact prompt-friendly text view, or flattenSemanticTree(tree) and summarizeSemanticTree(tree) for analysis and benchmarks.

Cloudflare Worker Example

import { extract } from "ax-grep/static";
import { formatSemanticTreeText } from "ax-grep";

export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url).searchParams.get("url");
    if (!url) return new Response("Missing url", { status: 400 });

    const response = await fetch(url);
    const html = await response.text();
    const tree = extract(html);

    return new Response(formatSemanticTreeText(tree), {
      headers: { "content-type": "text/plain; charset=utf-8" },
    });
  },
};

Options

The common options are shared across browser and static extraction where they make sense.

const tree = extract(html, {
  mode: "compact",
  includeAttributes: false,
  includeHidden: false,
  includeSelectOptions: true,
  maxTextLength: 240,
});

Useful options:

Option Default Notes
mode "compact" Use "interactive" to keep mostly actionable nodes.
includeAttributes true Turn off for smaller prompt payloads.
includeHidden false Keep hidden/collapsed content only when needed.
includeSelectOptions true Useful for agent planning, verbose for huge selects.
includeTextNodes browser: true, static: false Static extraction relies more on semantic names by default.
maxTextLength 240 Clips long direct text/name fragments.
excludeLikelyAds false Optional heuristic pruning for benchmark or prompt use.
summarizeLargeSubtrees static: true Keeps SSR payloads bounded.
summarizeLikelyLinkFarms static: true Helps forum/sidebar/navigation-heavy pages.

Mutation Stream

import { observeSemanticTree } from "ax-grep/browser";

const observer = observeSemanticTree((change) => {
  console.log(change.mutationCount, change.tree);
}, { debounceMs: 50 });

observer.disconnect();

For injected-script use, createObserverScript() installs an observer on window.__AX_LITE_OBSERVER__ and dispatches __AX_LITE_OBSERVER__:change events.

What It Does Not Do

  • It does not call the browser accessibility tree API.
  • It does not use DevTools Protocol or CDP.
  • It does not run JavaScript for static HTML input.
  • It does not bypass login, bot checks, or site challenges.
  • It does not guarantee identical output to Playwright or agent-browser accessibility snapshots.

Benchmarking

pnpm compare:sample
pnpm compare:static https://example.com https://news.ycombinator.com
pnpm compare:tokens https://example.com https://news.ycombinator.com
pnpm compare:static:korea-social
pnpm compare:tokens:korea-social
pnpm compare:static:china-japan
pnpm compare:tokens:china-japan

The comparison scripts compare ax-grep output with agent-browser snapshot output and estimate token cost for compact agent prompts. See docs/comparison-baseline.md for the current baseline run.

Current benchmark suites include:

  • static HTML vs browser snapshots
  • token-cost comparison for compact prompt text
  • Korean forum/search/social targets
  • Chinese and Japanese wiki/news/forum/search targets
  • challenge and volatile-page diagnostics

Package Status

ax-grep is early-stage software. The public API is intentionally small:

  • extract(html) from ax-grep or ax-grep/static
  • extract() from ax-grep/browser
  • createExtractorScript() and createObserverScript() from ax-grep

The long-form aliases extractStaticSemanticTree() and extractSemanticTree() are kept for compatibility.

0