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

@ab0t/acp

v0.1.0

Published

TypeScript/browser SDK for ACP (Agent Coordination Protocol): shared filesystem, ordered event log, mailbox, fencing leases, CRDT co-editing, presence — over the acp/1 wire.

Readme

@ab0t/acp — the ACP TypeScript SDK

The TypeScript/browser SDK for ACP (Agent Coordination Protocol): a shared content-addressed filesystem, a totally-ordered event log, a directed mailbox, fencing-token leases, CRDT co-editing (text + JSON), and live presence — as ordinary async calls against a self-hosted coordd daemon, over the frozen acp/1 wire.

  • Zero runtime dependencies. Built-in fetch + web streams. Node ≥ 20 and evergreen browsers.
  • Same API shape as the Go SDK. Same verbs, same error taxonomy, same semantics (the SDK suite interface contract). Learn once.
  • No Go toolchain, no source access needed — the SDK speaks the public wire.

Availability: published on npm — npm install @ab0t/acp. It needs a running coordd daemon (self-hosted; 60 seconds to start, see below). The acp CLI and the acp-mcp bridge are the other public client surfaces; this SDK is the programmatic path (Node + browser).

Quickstart (≤ 5 minutes)

1. Run a daemon (60 seconds — one static binary or Docker):

coordd -data ./acp-data -token dev-token
# or: docker run -p 8443:8443 ab0tcom/acp
# prints: clients: --server https://<host>:8443  --cert ./acp-data/cert.pem

2. Install the SDK into your project:

npm install @ab0t/acp

3. Connect and coordinate (demo.mjs):

import { Client, APIError } from "@ab0t/acp";

const c = new Client({
  baseUrl: "https://localhost:8443",
  token: "dev-token",
  agent: "demo-1",
});

await c.health();                                   // reachable?

// The event log: append a fact, read it back.
const ev = await c.append({ action: "demo.start", entity: "run/1" });
console.log("appended seq", ev.seq);

// The shared filesystem: content-addressed blob + CAS commit.
const { hash, size } = await c.putBlob("# hello from TypeScript\n");
const m = await c.manifest();
try {
  await c.commit({
    base_version: m.version,
    changes: [{ path: "docs/hello.md", hash, size }],
    note: "first commit",
  });
} catch (err) {
  if (err instanceof APIError && err.conflict()) {
    // someone committed first — re-read the manifest, rebase, retry
  } else throw err;
}
console.log("committed docs/hello.md");

// The mailbox: a directed message to another agent.
await c.send({ to: "demo-2", type: "inform", subject: "hello", body: "file is up" });

// Follow the log live (Ctrl-C to stop).
await c.follow(ev.seq + 1, (e) => console.log(`#${e.seq} ${e.actor} ${e.action}`));

4. Run it (the daemon's cert is self-signed, so hand it to Node):

NODE_EXTRA_CA_CERTS=./acp-data/cert.pem node demo.mjs

That's the whole loop: facts on an ordered log, files under CAS, messages, and a live stream — one daemon, no other services.

The primitives (when to use what)

| You want to record… | Use | |---|---| | a fact / audit trail (correctness) | the event logappend, follow | | mutual exclusion across machines | a leaseacquireLease (the returned token is your fencing token; lease file:<path> to gate commits to that path) | | a directed handoff to one agent | the mailboxsend / inbox / ack (threads are keyed by the thread_id you set) | | an artifact (a file) | blobs + a commitputBlobcommit (CAS; 409 ⇒ rebase on err.current, retry) | | a live co-edited document | a CRDT docRGA + pushCRDTOps/pullCRDTOps (text), pushCRDTJSONOps/crdtJSONDoc (JSON) | | an ephemeral hint ("what I'm doing now") | awarenesssetAwareness / followAwareness (lossy; never correctness) |

Errors

Server-side failures reject with APIError:

try {
  await c.acquireLease("build:main", 30);
} catch (err) {
  if (err instanceof APIError) {
    if (err.conflict())  { /* 409 — contended: err.current is the holding lease */ }
    if (err.locked())    { /* 423 — a write gated by another holder's lease */ }
    if (err.overQuota()) { /* 507 — persistent quota; do NOT blind-retry */ }
    // err.status has the raw code (429 = transient, back off and retry)
  }
}

Co-editing text (CRDT)

import { RGA } from "@ab0t/acp";

const doc = new RGA("replica-A");                 // stable, unique per client
const pulled = await c.pullCRDTOps("notes.txt", 0);
for (const op of pulled.ops) doc.apply(op);       // fold in peers' ops

const ops = doc.generateOps(doc.text() + "\nmy new line");
await c.pushCRDTOps("notes.txt", ops, pulled.epoch);
// a 409 with a new epoch means the doc was compacted: rebuild from pullCRDTOps(doc, 0)

Two replicas (in any language) and the daemon converge on identical text — that cross-language convergence is pinned by the SDK gate.

Browser bundles (plain HTML pages, no build step)

npm run build also emits one-file browser bundles under dist/browser/:

<!-- ES module -->
<script type="module">
  import { Client, RGA } from "/dist/browser/acp.esm.js";
</script>

<!-- or a plain script tag: the SDK as a global -->
<script src="/dist/browser/acp.global.js"></script>
<script> const c = new ACP.Client({ baseUrl, token, agent: "page-1" }); </script>

Both are dependency-free, ES2022, sourcemapped. (Remember the browser rules below: tokenless pages behind a credential broker.)

Agent helpers (the two loops every agent hand-rolls)

// Follow the log FOREVER: auto-reconnect, resume from lastSeq+1, backoff.
const stop = new AbortController();
await c.followForever(0, (e) => handle(e), {
  filter: new EventFilter({ actions: ["task.*"] }),
  signal: stop.signal,
});

// Hold a lease SAFELY: waits out contention, renews at ~TTL/3, releases;
// `lost` fires if a renewal fails — stop side-effecting immediately.
await c.withLease("build:main", 30, async (lease, lost) => {
  // lease.token is your fencing token — present it on protected writes
  await doTheWork({ signal: lost });
});

The examples: collab-docs + collab-notebook

  • examples/collab-docs/ — a collaborative document: two browser tabs typing into one doc, conflict-free, live presence carets — then node agent.mjs and an agent co-author types alongside you. Uses the ESM bundle.
  • examples/collab-notebook/ — a Colab-style shared notebook: JSON-CRDT cell structure + a text-CRDT per cell, per-cell presence, and a local-only "Run" (code executes in YOUR tab — the daemon never runs code; outputs are shared as data). Its agent adds and fills cells next to yours. Uses the <script>-tag global bundle.
  • examples/serve.mjs — the shared static server + credential broker both demos run behind (tokenless pages). examples/lib/ — the reusable bindings (TextDocSync, Presence) that show the recommended sync-loop shapes.

Browsers, tokens, and TLS (read this before shipping a web page)

  • Do not put a space-wide writer token in an untrusted page. Today's tokens scope by role/space/path-prefix — per-end-user read-scoping is a daemon roadmap item. Until then, browser deployments should be trusted surfaces (internal tools, kiosks) or fronted by a gateway that holds the credential and proxies (the page stays tokenless).
  • TLS: browsers cannot pin a self-signed daemon cert. Use a real certificate on coordd, or terminate TLS at your gateway/reverse proxy.
  • WebSocket auth: coordd authenticates the awareness WebSocket upgrade via the Authorization header, which browsers cannot set on a WebSocket — browser pages use the HTTP awareness follow, or a credential-injecting gateway. In Node, pass a header-capable factory:
import WebSocket from "ws";
const c = new Client({
  baseUrl, token, agent: "dash-1",
  webSocket: (url, headers) => new WebSocket(url, { headers }),
});
const sock = await c.awarenessSocket({ onDelta: (d) => render(d) }); // needs coordd -awareness-ws
sock.set({ cursor: [12, 40] }, 30, "tab-1");
  • Clusters: a follower answers live awareness follows with a redirect to the leader; Node hops it automatically, browsers cannot (the SDK throws a clear error) — point browser clients at the leader or a gateway.

Testing

npm test                      # build + unit tests (CRDT convergence, matcher parity, transport)
../../hack/run-ts-sdk-gate.sh # the full acceptance gate against a real local coordd

Relationship to the other ACP surfaces

  • acp CLI — humans and scripts; the same primitives as commands.
  • acp-mcp — agents in MCP harnesses (Claude Code, Codex, …).
  • Go SDK (pkg/client) — Go services; same interface contract as this SDK.

The SDK suite contract (verbs, naming rules, error taxonomy) lives in the INTERFACE.md of the SDK-suite ticket; this package conforms to it.