@itc-steve/pi-web-complete

extensionmaintained

Pi extension: multi-backend web_search, local web_read with query-ranked excerpts, and web_cowork shared browser control.

by — · v3.3.0 · published 4d ago

$ pi install npm:@itc-steve/pi-web-complete
downloads/mo
855
stars
0
last push
4d ago
open issues
0

Signals

license: MITtestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

2.2K downloads · last 12 weeks (weekly)

README

pi-web-complete routes Pi agent requests through search, local reading, shared browser control, and current documentation

pi-web-complete

Give Pi one extension for web discovery, clean local extraction, browser interaction and debugging, and version-current framework docs.

pi install npm:@itc-steve/pi-web-complete

One extension, four jobs

ToolUse it forWhat makes it useful
web_searchCurrent facts and discoveryBrave, Serper, Tavily, Exa, and Linkup with shuffled fallback
web_readReading a URLLocal extraction with query-ranked excerpts by default; web_fetch alias included
web_coworkBrowser interaction and debuggingExternal-window or headless CloakBrowser with shared control, DevTools inspection, Chrome device-mode emulation, and raw CDP
context7Library and framework APIsVersion-current documentation, registered only when configured

web_search finds the page. web_read turns it into focused context. web_cowork handles pages that need a person or browser UI. context7 keeps implementation work grounded in current docs.

Quick start

1. Install

pi install npm:@itc-steve/pi-web-complete

From a local checkout:

pi install /path/to/pi-web-complete

2. Configure

Configuration and secrets live in separate files:

cp /path/to/pi-web-complete/web.json.example ~/.pi/agent/web.json
cp /path/to/pi-web-complete/web.env.example ~/.pi/agent/web.env
chmod 600 ~/.pi/agent/web.env
# edit web.json, then paste your keys into web.env

~/.pi/agent/web.json:

{
  "defaultBackend": "auto",
  "allowPrivateHosts": [],
  "search": { "enabled": true },
  "cowork": { "enabled": true },
  "backends": {
    "brave":  { "enabled": true, "apiKeyEnv": "BRAVE_API_KEY" },
    "serper": { "enabled": true, "apiKeyEnv": "SERPER_API_KEY" },
    "tavily": { "enabled": true, "apiKeyEnv": "TAVILY_API_KEY" },
    "exa":    { "enabled": true, "apiKeyEnv": "EXA_API_KEY" },
    "linkup": { "enabled": true, "apiKeyEnv": "LINKUP_API_KEY", "depth": "standard" }
  },
  "context7": { "enabled": true, "apiKeyEnv": "CONTEXT7_API_KEY" }
}

~/.pi/agent/web.env:

BRAVE_API_KEY=replace-with-brave-key
SERPER_API_KEY=replace-with-serper-key
TAVILY_API_KEY=replace-with-tavily-key
EXA_API_KEY=replace-with-exa-key
LINKUP_API_KEY=replace-with-linkup-key
CONTEXT7_API_KEY=replace-with-context7-key
GITHUB_TOKEN=replace-with-github-token

Project overrides can live in .pi/web.json and .pi/web.env. Project JSON overrides global top-level settings while backends merge per backend; project secrets overlay global secrets. Keep key values in .env, never JSON.

3. Use

web_search({ query: "Node.js fetch timeout patterns", compact: true })
web_read({ url: "https://example.com/guide", query: "authentication setup" })
web_cowork({ action: "open", url: "https://example.com/login" })
context7({ library: "next.js", query: "app router middleware auth" })

Toggle web_search / web_cowork per session

Both tools start enabled in every new session. Disable one for the rest of a session with the /web command:

/web                # status: search / cowork / shared browser
/web search off     # hide web_search until re-enabled
/web cowork         # flip web_cowork on or off
/web                # status again

A bare target flips the current state; on / off set it explicitly. Disabling web_cowork hides the tool but leaves any open shared browser session running. Toggles last for the session only; set the per-session default in web.json:

{
  "search": { "enabled": false },
  "cowork": { "enabled": false }
}

Both default to true when absent, so a missing config section keeps auto-enable on.

Read pages without flooding context

web_read escalates from fast HTTP through fingerprinting and Readability to CloakBrowser only as needed, then returns ranked excerpts

web_read acquires the full page locally, then returns only the most relevant chunks for the query. Automatic mode escalates through fast HTTP, TLS-fingerprint fetch, alternate links, Readability, and CloakBrowser only when earlier paths are blocked or too sparse. A confirmed block (401/403/429/503 or cf-mitigated) lifts a per-host floor for the rest of the session so later reads of that host skip the failed tier. Residual challenge pages are reported as blocked, not returned as content. It also follows short meta-refresh redirects up to five hops.

web_read({ url, query: "HTTP caching Cache-Control" })
  • Default: ranked excerpts with a roughly 6k-character budget.
  • No query: compact page outline.
  • return: "full": complete main content, capped around 12k characters in chat.
  • savePath or saveDir: full extract goes to disk; chat receives a short summary.
  • mode: "browser": force CloakBrowser rendering.
  • PDFs: text extraction when the file has a text layer. Scanned PDFs stay a short placeholder (no OCR).
  • Dead links: archive=auto (default) tries the nearest Wayback snapshot and labels its date. archive=never skips.
  • stitch=true joins same-origin rel=next pages (max three extra) with part markers.
  • GitHub issues and pull requests: clean bodies and comments through GitHub REST API; optional GITHUB_TOKEN or GH_TOKEN raises limits and enables private repositories.
  • Metadata when available: author, publication date, site, and language.
  • maxBytes: download cap with a 2 MB floor and 5 MB default; oversized bodies truncate instead of failing.
web_read({ url, mode: "browser", saveDir: "~/vault/http-caching" })

URLs are restricted to HTTP(S). Requests to localhost, private IP ranges (including IPv4-mapped IPv6), and common internal or metadata hostnames are refused by default. This check is hostname-level and does not resolve DNS (127.0.0.1.nip.io is not treated as loopback). To test a trusted local app, explicitly allow its exact hostname (not a URL or wildcard) in global ~/.pi/agent/web.json:

{ "allowPrivateHosts": ["localhost", "127.0.0.1"] }

The allowlist applies to initial URLs and redirects in both web_read and web_cowork. Project config cannot relax it. Only allow hosts you trust because pages can access services on every port permitted by the URL guard.

Search with fallback

Auto mode shuffles enabled backends that have resolvable keys. Empty results and provider failures move to the next backend; aborts stop immediately.

  • Pin a provider with backend: "brave", "serper", "tavily", "exa", or "linkup".
  • Use compact: true for title-and-URL results while exploring.
  • Limit numResults from 1 to 20.
  • Set defaults in web.json.

Key resolution order for every apiKeyEnv:

  1. process.env[apiKeyEnv]
  2. .pi/web.env
  3. ~/.pi/agent/web.env
  4. Legacy literal apiKey in JSON

Legacy JSON paths remain supported when the new paths are absent: ~/.pi/agent/extensions/search.json and .pi/search.json.

Work together in an external or headless browser

web_cowork keeps one persistent CloakBrowser session open. Default mode is an external desktop window shared by agent and user; Linux without DISPLAY/WAYLAND_DISPLAY defaults to headless. Set headless: true for automation. Explicit headless: false without a display returns an actionable error (no virtual display is created). Headless is create-time only: a live session is reused even if a later open passes a different flag. Close first to switch. wait needs a visible window. Use web_read for one-shot extraction.

web_cowork({ action: "open", url: "https://example.com/login" })
web_cowork({ action: "wait", message: "Log in, then continue" })
web_cowork({
  action: "batch",
  fills: [
    { ref: "@e3", text: "name@example.com" },
    { ref: "@e4", text: "hello" }
  ],
  clickRef: "@e5"
})
web_cowork({ action: "close" })

State-changing actions return fresh, bounded interactive refs. snapshot supports interactive, content, and both modes. Password and secret-looking values appear as [redacted].

Persist sessions and choose a download directory with:

{
  "cowork": {
    "userDataDir": "~/.cloakbrowser/cowork-profile",
    "downloadDir": "~/Downloads",
    "headless": false
  }
}

Downloads default to ~/Downloads and apply to cowork sessions and browser-rendered reads.

Developer actions expose console and network capture, JavaScript evaluation, screenshots, accessibility trees, tab selection, Chrome device-mode emulation, and raw Chrome DevTools Protocol on page or browser targets. CDP uses Pi's existing Playwright connection; no loopback debugging port is opened. Blocked: Fetch interception, Target create/attach/close, and Browser/Page crash/close (case-insensitive). Raw CDP can still read cookies and run Runtime.evaluate against the persistent profile.

Mobile vs desktop

Cowork defaults to desktop Chrome. Resizing the window is not mobile: desktop Chromium ignores viewport meta, keeps (hover: hover) / (pointer: fine), and sends a desktop UA. action=emulate is Chrome DevTools device mode:

open and emulate report the observed CSS viewport, not just the preset. Screenshots use the same CDP session as emulation so capture cannot restore stale desktop metrics. Device labels follow the active tab; new tabs start desktop. A page without responsive viewport metadata can still have a wide mobile layout. Chrome Android emulation is not physical iPhone/Safari evidence.

Real-browser regression check: npx tsx test/cowork-browser.check.ts (requires installed CloakBrowser; asserts CSS/media-query widths before/after screenshots and desktop restore).

  • device=mobile — Pixel 7 metrics with mobile:true (viewport meta, overlay scrollbars, text autosizing), touch events, and a mobile UA plus Sec-CH-UA-Mobile. Reloads so the server sees the new UA.
  • device=desktop — restore. Reloads.
  • Optional device on open so the first load is already mobile.

Local apps need the host in allowPrivateHosts. Then:

web_cowork({ action: "open", url: "http://127.0.0.1:3000" })
web_cowork({ action: "screenshot" })
web_cowork({ action: "emulate", device: "mobile" })
web_cowork({ action: "screenshot" })
web_cowork({ action: "emulate", device: "desktop" })

This matches Chrome's device toolbar, not a real phone (GPU, software keyboard, 100vh URL bar, iOS Safari/WebKit). status reports the current Device.

Get current library docs

context7 resolves a plain package name or accepts a Context7 library ID, then returns documentation ranked for the task. Get an API key at context7.com/dashboard.

context7({ library: "next.js", query: "app router middleware auth" })
context7({ library: "/vercel/next.js/v14.3.0", query: "server actions form validation" })
  • Registered only when a Context7 key resolves.
  • IDs can be version-pinned with /v14.3.0 or @v14.3.0.
  • Results are capped at 12k characters; narrow the query when truncated.
  • fast: true skips LLM reranking for lower latency and lower relevance.
  • Set "enabled": false to keep the key configured while hiding the tool.

Tool reference

ToolParameters
web_searchquery, numResults, backend, compact
web_read / web_fetchurl, query, return, mode, format, onlyMainContent, maxChars, maxBytes, headless, savePath, saveDir, archive, stitch
web_coworkaction, url, mode, ref, role, name, selector, text, clear, fills, clickRef, key, deltaY, query, maxChars, message, timeoutMs, headless, pageIndex, expression, method, cdpParams/params, target, filter, fullPage, device
context7library, query, fast

Cowork actions

ActionPurpose
open, navigateOpen or move the shared browser and return fresh refs
emulateChrome device mode (device=mobile / device=desktop). Default desktop. Not a window resize. Optional device on open.
waitPause for user input, then return optional note and fresh refs
snapshotRead interactive refs, content, or both
click, type, press, scrollAct on the latest ref; role and name are fallbacks
batchFill 1–10 fields, then optionally click once
console, networkDrain captured developer events; optional substring filter
evaluate, screenshot, a11yInspect page runtime, pixels, or accessibility tree
pages, selectList tabs or choose active tab by zero-based index
cdpSend raw CDP method and parameters to page or browser target
status, closeInspect or end the session

Runtime behavior

  • Node.js 20.18.1+ is required.
  • postinstall runs cloakbrowser install and stores stealth Chromium under ~/.cloakbrowser/.
  • CloakBrowser checks for browser updates at launch. Tagged update logs are hidden because direct console output corrupts Pi's TUI; set DEBUG=1 to show them or CLOAKBROWSER_AUTO_UPDATE=false to disable checks.
  • Status chip stays empty until a service is used. Successful providers accumulate as one sorted line below the editor; active reads and cowork share that same line.
  • Set "showStatus": false to disable footer updates.
  • Set "read": { "headless": false } or pass headless: false to show browser-rendered one-shot reads.

License

MIT

CloakBrowser's JavaScript wrapper is MIT-licensed. Its downloaded Chromium binary uses CloakBrowser's separate binary license; see its LICENSE and BINARY-LICENSE.md.