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

@rivetplane/sdk

v0.3.0

Published

First-party TypeScript SDK for the Rivetplane control-plane API

Readme

@rivetplane/sdk

The first-party TypeScript SDK for the Rivetplane control-plane API. It uses standard fetch, streams SSE without EventSource, and uses the standard WebSocket API. The same package works in Node.js 24 or later, Bun, and modern browsers.

Install

npm install @rivetplane/sdk

Use the REST API

import { Rivetplane } from "@rivetplane/sdk";

const rivetplane = new Rivetplane({
  authentication: process.env.RIVETPLANE_TOKEN!,
});

for (const session of await rivetplane.listSessions({ status: "waiting_approval" })) {
  console.log(session.title, session.model?.provider_id, session.model?.model_id);
  const pending = await rivetplane.sessions.pending(session.id);
  if (pending?.type === "approval") {
    await rivetplane.sessions.respondToPending(session.id, {
      pending_id: pending.id,
      response: "approve",
      scope: "once",
    });
  }
}

Authentication can be a token, a function, or an object with getToken(). Use a provider when tokens can rotate:

const rivetplane = new Rivetplane({
  baseUrl: "http://127.0.0.1:8080",
  authentication: async () => tokenStore.current(),
});

The default server is https://rivetplane.com. Set baseUrl only for a self-hosted server or the local runner API.

Session lists support stable time-based pagination:

const sessions = await rivetplane.listSessions({ before: new Date().toISOString(), limit: 100 });

Session list and detail responses can include harness-reported identity fields. All are optional so clients remain compatible with adapters that do not report them.

const session = await rivetplane.getSession(sessionId);

console.log(session.title);
console.log(session.model?.provider_id, session.model?.model_id);
console.log(session.agent, session.read_only, session.metadata);

Use attention for the fleet-wide approval and question inbox. Pending items include the same optional session identity fields. By default, the server returns actionable items only. listPending() remains as a compatibility alias for attention.list().

const inbox = await rivetplane.attention.list();
const diagnostics = await rivetplane.attention.list({ includeNonActionable: true });

for (const item of inbox) {
  console.log(item.pending.id, item.title, item.model, item.agent, item.read_only);
}

const approval = inbox.find((item) => item.pending.type === "approval");
if (approval?.actionable) {
  await rivetplane.attention.respond(approval.pending.id, {
    response: "approve",
    scope: "once",
  });
}

Harness adapters can report normalized command, description, source, response_mode, and expires_at fields. Consumers should prefer those structured fields and retain tool_input_summary only as a compatibility fallback.

AI usage

Use usage.get() to get token totals, cost semantics, breakdowns, and the latest context and quota data. The getUsage() method is a short alias. All filters are optional.

const usage = await rivetplane.usage.get({
  from: "2026-08-25T00:00:00Z",
  to: "2026-08-26T00:00:00Z",
  machine: "laptop",
  harness: "codex",
  provider: "openai",
  model: "gpt-5.4",
});

console.log(usage.totals.tokens.total);
console.log(usage.totals.cost.coverage); // "complete", "partial", or "none"
if (usage.totals.cost.status === "reported") {
  console.log("Reported cost", usage.totals.cost.amount, usage.totals.cost.currency);
} else if (usage.totals.cost.status === "estimated") {
  console.log("Estimated cost (not a bill)", usage.totals.cost.amount, usage.totals.cost.currency);
} else {
  console.log("Cost unavailable");
}

Token fields use null when a source does not report a counter. Cost status is always explicit: reported, estimated, or unavailable. Do not treat an estimate as authoritative billing. Cost summaries also contain coverage, priced_samples, and unavailable_samples, so a priced subset cannot look like complete spend. by_currency can contain separate totals when a single aggregate amount cannot represent multiple currencies. Older harness clients can produce an empty report with unavailable values.

Pagination and streaming

transcriptPages() gets all transcript pages lazily. transcriptEvents() flattens those pages. streamTranscript() reads live SSE events with an authenticated fetch call. Thus, it works in browsers where EventSource cannot set an authorization header.

for await (const event of rivetplane.sessions.transcriptEvents(sessionId, { limit: 100 })) {
  console.log(event.type, event.payload);
}

const controller = new AbortController();
for await (const event of rivetplane.sessions.streamTranscript(sessionId, { signal: controller.signal })) {
  console.log(event);
}

The account-wide WebSocket reconnects with exponential backoff by default. Browser authentication uses Rivetplane's bearer.<base64url-token> subprotocol.

for await (const event of rivetplane.events({
  reconnect: { initialDelayMs: 500, maxDelayMs: 10_000 },
})) {
  console.log(event.type, event.session_id);
}

Node.js 24, Bun, and modern browsers provide the required WebSocket implementation. You can also pass a WHATWG-compatible WebSocket constructor in options.webSocket.

Errors

Non-success HTTP responses throw RivetplaneApiError. It contains status, method, url, body, requestId, and retryable. Transport failures throw RivetplaneNetworkError. Invalid JSON or event data throws RivetplaneProtocolError.

See examples/basic.ts, examples/streaming.ts, and docs/release.md.