npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pi-browser-search

v1.0.0

Published

Pi extension: web search and browsing through a real Playwright browser — JS-rendered pages, cookie walls, SPAs — with prompt-injection sanitization and network-level SSRF protection.

Downloads

147

Readme

pi-browser-search

test npm

A pi extension that adds browser_search, browser_open, browser_act, and browser_read tools backed by a real Playwright Chromium browser.

Static fetching can't handle JavaScript-rendered pages, SPAs, cookie walls, or lazy-loaded content. This extension drives an actual browser instead, while enforcing the same defense-in-depth as pi-safe-search: prompt-injection sanitization on every result, and SSRF protection enforced at the network layer on every request the browser makes — not just the ones the model asked for.

Install

From npm:

pi install npm:pi-browser-search

Or from git:

pi install git:github.com/sebaxzero/pi-browser-search.git

Add -l to either form to install project-locally (adds to .pi/settings.json only).

No manual setup: the playwright npm dependency installs automatically with the package, and the Chromium binary (~150 MB) downloads once on the first browser launch. To pre-download it instead, run npx playwright install chromium in the install directory.

How it works

Sanitization pipeline (runs on every result)

Shared with pi-safe-search. Every piece of page content passes through this pipeline before reaching the LLM:

  1. Unicode normalization — NFKC normalization plus a homoglyph map folds lookalike characters to ASCII
  2. Zero-width character removal — strips invisible characters used to hide instructions
  3. Control character stripping — removes everything below space except \t, \n, \r
  4. HTML entity decode → re-strip — decodes <script> then strips the resulting tags
  5. URL decode — catches percent-encoded payloads
  6. Base64 blob redaction — replaces suspicious base64 blobs with [BASE64_ENCODED_DATA]
  7. Injection pattern redaction — override directives, role hijacking, system prompt extraction, mode switching, and more
  8. Random-delimiter wrapping — content is fenced with a random token so the LLM treats everything inside as data, never instructions

A second sanitization pass runs on every tool result via the tool_result hook, catching anything that slips through.

SSRF protection at the network layer

Unlike a plain fetch-based tool, a rendered page can issue requests the model never asked for — redirects, iframes, XHR calls, subresources. A context.route("**/*") interceptor checks every one of them against:

  • Non-http(s) schemes (file://, ftp://, etc.)
  • Dangerous ports: 21, 22, 25, 53, 3306, 5432, 6379, and more
  • RFC-1918, loopback, link-local, and reserved IP ranges (DNS-resolved, 60s cache)

browser_open additionally validates the URL up front with descriptive errors before navigation starts.

Browsing model

The extension keeps one lazy-launched browser/context and one "current page." browser_open navigates and extracts text and links; browser_act clicks, types, presses keys, scrolls, or waits on that page; browser_read pages through the last extracted content by character offset without refetching. A click that opens a new tab switches to it, and dialogs are auto-dismissed.

browser_search deliberately uses DuckDuckGo's static HTML endpoint over plain fetch — DDG bot-walls headless Chromium — so the browser is spent only on what needs it: rendering and interacting with pages.

Tools

browser_search — Searches DuckDuckGo and returns titles, URLs, and snippets.

Parameters:

  • query (required) — search query
  • max_results (optional) — number of results, default 5, max 10

browser_open — Navigates to a URL and returns rendered text plus a link list.

Parameters:

  • url (required) — must be http or https

browser_act — Interacts with the currently open page: click, type, press, scroll, or wait_for.

browser_read — Pages through the last snapshot's extracted text by character offset, no refetch.

Configuration

Persistent configuration lives in extensions/browser-search.json (auto-created on first load with defaults). You can ask the agent to edit it directly:

{
  "MAX_RESULTS": 5,
  "MAX_CHARS": 8000,
  "MAX_LINKS": 25,
  "NAV_TIMEOUT_MS": 30000,
  "HEADLESS": true,
  "BLOCK_MEDIA": true
}

| Key | Default | Description | |-----|---------|-------------| | MAX_RESULTS | 5 | Default number of search results returned by browser_search (1–10) | | MAX_CHARS | 8000 | Page text returned per browser_read chunk | | MAX_LINKS | 25 | Links listed per page | | NAV_TIMEOUT_MS | 30000 | Navigation timeout in milliseconds | | HEADLESS | true | Set false to watch the browser window | | BLOCK_MEDIA | true | Skip loading images/media/fonts |

Changing HEADLESS or BLOCK_MEDIA or NAV_TIMEOUT_MS closes the current browser so the next call relaunches with the new options.

Changes to the JSON take effect on the next session. For live tuning within a session, use the command below.

Command

/browser-search                 — show current status and config
/browser-search set KEY=VAL     — override config for the current session only
/browser-search reset           — close the browser

Compatibility

Shares its sanitization and SSRF model with pi-safe-search — install both if you want cheap static fetch for most pages and a real browser reserved for JS-heavy ones.

Dependencies

playwright (^1.53.0) — installed automatically with the package, whether via pi install npm: or a git-based install. The Chromium binary is downloaded separately on first browser launch (see Install above).

Tests

node --test test.mjs

17 tests covering IP range checks, URL validation, DuckDuckGo redirect unwrapping, and a sanitization smoke test. Pure logic lives in net.ts and sanitize.ts (no Playwright import), so the suite runs without a browser. CI runs it on every push and pull request.

Releasing

Bump version in package.json, commit, tag vX.Y.Z, and push the tag — the publish workflow runs the tests and publishes to npm.

License

MIT


Built with Claude.