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

@digital-gravy/etch-connector

v0.1.5

Published

Local bridge that lets an AI harness drive a live Etch tab by evaluating scripts against the etch-public-api over a WebSocket.

Readme

etch-connector

AGENT: read this first. Before interacting with Etch in any way, run etch-connector --help (or npx @digital-gravy/etch-connector --help) and read the entire output. It documents the backends, commands, flags, the stdout/stderr/exit-code contract, and how to pull the current Etch docs — everything you need to drive a tab correctly. Do not call any other command until you have read it.

A tiny local bridge that lets an AI harness — Claude Code, Cowork, or any tool that can run a shell command — drive a live Etch builder tab by evaluating scripts against the etch-public-api surface (window.etch.*).

Etch's scriptable API only exists inside the browser. There is no server you can call directly. So this connector inverts the usual direction: the browser dials out to the connector over a WebSocket (the same way Chrome remote debugging works, but in reverse), and the connector relays scripts into the tab and streams the result back.

Claude Code / Cowork
      │  shell:  etch-connector eval -t homepage "return ..."
      ▼
  etch-connector  (the `serve` daemon)
      │  ▲   WebSocket  (tab dials in via etch.connectAs)
      ▼  │
  Etch builder tab  ──►  window.etch.*

Status

This is an eval relay: the harness sends a JavaScript snippet, the page runs it as an async function, and the return value + console output come back. It is not an MCP server yet — see Going to MCP.

Scripts run in a page-enforced safe mode: the script sees only etch and the standard JS built-ins. window, document, fetch, storage, eval, … are withheld.

Install & run

No install needed — run it on demand with npx (npm) or bunx (Bun):

npx @digital-gravy/etch-connector serve
# or
bunx @digital-gravy/etch-connector serve

To get the short etch-connector command on your PATH, install it globally:

npm install -g @digital-gravy/etch-connector   # or: bun install -g …
etch-connector serve

Using the connector requires a valid, active Etch license — see LICENSE.

The examples below use the bare etch-connector command; if you're running via npx/bunx instead, prefix them with npx @digital-gravy/etch-connector (or bunx …).

Use

1. Start the daemon (once)

etch-connector serve
# bridge   ws://127.0.0.1:7331
# control  http://127.0.0.1:7332

2. Connect a tab

Primary way — the AI button. Enable the AI Connector in the Etch Settings, then open an Etch builder page and click the AI button in the left sidebar. It connects this tab for you, registering it under your site name (it just calls connectAs under the hood). Click it again to disconnect.

Secondary way — the console. You can also connect manually from the browser console:

etch.connectAs('homepage');

(connectAs is part of the public API; it opens the WebSocket back to the daemon and registers this tab under the given name. Pass { port: <n> } if you started the daemon on a non-default WebSocket port.)

3. Drive it

# list connected tabs
etch-connector tabs

# run a script (value → stdout, console output → stderr).
# reference the API as `etch.*`.
etch-connector eval -t homepage "return etch.apiVersion"

# multi-line / from a file / from stdin
etch-connector eval -t homepage -f ./script.js
echo "return await etch.blocks.getTree()" | etch-connector eval -t homepage

The script body runs as an async function, so it can return a value and use top-level await. Write scripts against the etch-public-api surface via the etch binding (window is not available). Examples:

return etch.apiVersion;
return await etch.blocks.getTree();
etch.blocks.select('block-id-123');
const groups = await etch.fields.listGroupsAsync();
return groups.length;

Output contract (for agents)

eval keeps stdout clean so a harness can capture it as the result:

  • stdout — the script's return value, as pretty JSON.
  • stderr — forwarded console.* output, prefixed with the level.
  • exit code0 success · 2 the script threw · 1 operational failure (no such tab, ambiguous target, timeout, daemon unreachable).

For AI agents

The authoritative agent instructions live in etch-connector --help — read the entire output before interacting with Etch. It covers the backends, commands and flags, the stdout/stderr/exit-code contract, how to pull the current Etch docs (Context7 MCP or https://docs.etchwp.com/public-api), and the conventions for structuring pages (e.g. the section/container layout pattern). The public API may also expose ready-made skills for common tasks — check the docs before improvising. Project conventions for working on this repo live in AGENTS.md.

CLI

etch-connector — drive a live Etch tab from the command line

Usage:
  etch-connector serve [--ws-port 7331] [--control-port 7332] [--ws-host 127.0.0.1]
  etch-connector tabs  [--json] [--control-port 7332] [--cdp]
  etch-connector eval  [code] [-t|--tab name] [-f|--file path] [--timeout ms] [--cdp]
  etch-connector shot      [-t name] [-s|--selector css] [--full] [--jpeg] [-o|--out file] [--freeze=false]   (CDP)
  etch-connector html      <selector> [-t name]                                              (CDP)
  etch-connector computed  <selector> [-t name] [--props a,b,c]                               (CDP)

Two backends:
  bridge  (default) the tab dials in via etch.connectAs(); page-sandboxed.
          Scripts see only "etch" + the standard JS built-ins.
  cdp     connect OUT to a Chrome started with --remote-debugging-port=9222.
          No daemon. Runs at DevTools privilege: full "window", plus screenshots,
          rendered HTML, and computed styles. Opt in with --cdp (or --cdp-port /
          --remote-debugging-port / --cdp-host). Target a tab with -t <url|title substring>.

Examples:
  etch-connector serve
  etch-connector tabs
  etch-connector eval -t homepage "return etch.apiVersion"
  echo "return await etch.blocks.getTree()" | etch-connector eval -t homepage
  etch-connector eval -t homepage -f ./script.js
  # CDP backend (Chrome launched with --remote-debugging-port=9222):
  etch-connector tabs --cdp
  etch-connector eval --cdp -t etch "return document.title"
  etch-connector shot --cdp -t etch -s .orbit -o orbit.png
  etch-connector computed --cdp -t etch -s .orbit__chip --props transform,animation-duration

When exactly one tab is connected, -t is optional. With several tabs you must name one.

HTTP control API

The daemon also exposes a loopback HTTP API (this is the seam the CLI uses, and the same one a future MCP server would call):

| Method | Path | Body | Returns | | ------ | --------- | ----------------------------------- | ------------ | | GET | /health | — | { ok } | | GET | /tabs | — | { tabs } | | POST | /eval | { tab?, code, timeoutMs?, mode? } | EvalResult |

Architecture

  • src/protocol.ts — the wire format, mirrored from the Etch-side bridge.ts. Keep the two in sync.
  • src/bridge-server.tsBridgeServer, the transport-agnostic core: owns the sockets, tracks tabs by name, correlates requests, handles timeouts and disconnects. This is the reusable piece.
  • src/control-server.ts — the HTTP control API over the core.
  • src/cli.ts — the serve / tabs / eval commands.

Going to MCP later

Today this is a CLI eval relay. A future version will expose the same bridge as a proper MCP server, so an AI harness can call first-class tools (list tabs, run a script) directly instead of shelling out — without changing how you connect a tab.

Security

Two layers, neither a substitute for the other:

Transport. Everything binds to 127.0.0.1, so any local process can reach the daemon. Run it only on a trusted machine and do not expose the ports beyond loopback.

Execution. Bridge mode runs the script inside a with-bound Proxy that exposes only etch plus the standard JS built-ins and throws on window, document, fetch, storage, eval, Function, … This is a guardrail, not a hardened sandbox: because the script shares the page's JS realm, a determined author can still reach globals through prototype / constructor chains (e.g. ({}).constructor.constructor). It reliably keeps casual and AI-authored scripts on the etch surface and closes the obvious escape hatches; it does not contain hostile code. For a true boundary against adversarial scripts, run the daemon only on a trusted machine.