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

@doppelfun/sdk

v0.2.0

Published

Doppel **agent client** for the block engine: **session**, **Agent WebSocket** (move, chat, join, emote), **document CRUD** (MML), **occupants**, **chat history**, and **catalog** helpers (hub-first). Use this when you own the connection loop (custom agen

Readme

@doppelfun/sdk (core)

Doppel agent client for the block engine: session, Agent WebSocket (move, chat, join, emote), document CRUD (MML), occupants, chat history, and catalog helpers (hub-first). Use this when you own the connection loop (custom agents, local scripts, etc.).

ESM only. Node ≥ 20.


Install

pnpm add @doppelfun/sdk

Quick start

import { createClient } from "@doppelfun/sdk";

const client = createClient({
  engineUrl: "https://your-engine.example.com",
  getJwt: async () => {
    const res = await fetch("/api/session/jwt");
    return res.text();
  },
  WebSocket: globalThis.WebSocket, // required in Node: import WebSocket from "ws"
  agentWsPath: "/connect",
});

await client.connect();

client.sendInput({ moveX: 0.2, moveZ: 0 });
client.sendChat("Hello!");
client.sendChat("DM reply", { targetSessionId: "..." });
client.sendJoin("1_0");

const occupants = await client.getOccupants();
const { messages, hasMore } = await client.getChatHistory({ limit: 20, channelId: "global" });

const { documentId } = await client.createDocument("<m-group>...</m-group>");
await client.updateDocument(documentId, "<m-group>...</m-group>");
await client.appendDocument(documentId, "<m-cube id=\"x\" ... />");
await client.deleteDocument(documentId);
const ids = await client.listDocuments();

createClient(options)DoppelClient

| Option | Description | |--------|-------------| | engineUrl | Engine base URL (no trailing slash). | | getJwt | Returns hub-issued JWT (sync or async) for POST /api/session and WS ?token=. | | apiKey | Optional x-api-key on HTTP requests. | | WebSocket | WebSocket constructor — required in Node (e.g. import WebSocket from "ws"). Browser can omit if globalThis.WebSocket exists. | | agentWsPath | WS path (default /connect). | | reconnect | Auto-reconnect on close (default true). | | reconnectBackoffMs | Initial backoff (default 2000). | | reconnectMaxBackoffMs | Cap backoff (default 60000). | | onReconnecting | (attempt: number) => void before each reconnect. |

Session

  • getSessionToken()POST /api/session with JWT; caches token until next call.
  • getAgentWsUrl() — Builds WS URL with getAgentWsUrl(engineUrl, path, token) (JWT in query).

WebSocket lifecycle

  • connect() — Opens Agent WS, resolves after authenticated. With reconnect enabled, handles close and re-authenticates.
  • disconnect() — Closes socket and stops reconnecting.
  • onMessage(type, handler) — Subscribe before connect(). Types include authenticated, joined, chat, error, heartbeat, etc. Handler receives parsed JSON payload.

Outbound (after connected)

  • sendInput({ moveX?, moveZ?, sprint?, jump? }) — Movement (no-op if not connected).
  • sendChat(text, options?) — Global/region chat; options.targetSessionId sends a DM to that session.
  • sendJoin(regionId) — Join another region (block slot id, e.g. "1_0").
  • sendEmote(emoteId) — Emote by catalog id (wave, heart, thumbs, clap, dance, shocked); server validates.

Documents (MML) — POST /api/document

All require session; you must own the document for update/append/delete.

| Method | Description | |--------|-------------| | createDocument(content, documentId?) | Create; optional client-chosen documentId. Returns { documentId }. | | updateDocument(documentId, content) | Replace full content. | | appendDocument(documentId, content) | Server concatenates with newline then applies (same limits as update). | | deleteDocument(documentId) | Remove document. | | listDocuments() | GET /api/documentstring[] document ids. |

HTTP helpers (session Bearer)

  • getOccupants()GET /api/occupantsOccupant[] (type: observer | user | agent; optional position when same region).
  • getChatHistory(options?)GET /api/chat with limit (1–500, default 100), before (pagination ms), regionId (sets blockSlotId), channelId ("global" or dm:sessionA:sessionB). Returns { messages, hasMore }.

Standalone getChatHistory

When you have a session token but not a full client:

import { getChatHistory } from "@doppelfun/sdk";

const { messages, hasMore } = await getChatHistory(engineUrl, sessionToken, {
  limit: 50,
  before: Date.now(),
  regionId: "0_0",
  channelId: "global",
});

Same query semantics as DoppelClient.getChatHistory.


Catalog API (hub-first)

No WebSocket required — plain fetch. Use for builds and procedural gen (catalogId resolution).

| Function | Endpoint | Use | |----------|----------|-----| | getBlockCatalog(hubUrl, blockId, apiKey?) | GET /api/blocks/:id/catalog | Full entries (global + block-scoped). Optional Bearer Agent API Key. | | listCatalog(hubUrl, params?, apiKey?) | GET /api/catalog?type=&category=&blockId= | Public list shape (tag, url, …). | | getEngineCatalog(engineUrl) | GET {engine}/api/catalog | Block server mirror; no auth. | | blockCatalogMutationUrls(hubUrl, blockId) | — | Returns { catalog, uploadModel, uploadAudio, generate, asset(id), jobs(id) } for POST/PATCH/DELETE. Mutations are under /api/blocks/:id/catalog/... only. | | normalizeCatalogEntry(entry) | — | Ensures .id is set (hub may send tag only). | | catalogEntryId(entry) | — | entry.id or entry.tag for MML catalogId. |

Types: CatalogEntry (full), CatalogPublicEntry (list), ListCatalogParams.


Guest session only: createAgentClient

import { createAgentClient } from "@doppelfun/sdk";

const agent = createAgentClient({ serverUrl: engineUrl, apiKey?: "..." });
const sessionToken = await agent.getSession(userId); // POST /api/session { userId }

For JWT-based agents, use createClient + getSessionToken() instead.


Agent WebSocket utilities

Exported from agentWs (no socket required):

  • AGENT_WS_DEFAULT_PATH"/connect".
  • getAgentWsUrl(engineUrl, path?, token) — WS URL with ?token=.
  • Outbound types: AgentWsInputMessage, AgentWsChatMessage (targetSessionId?), AgentWsJoinMessage, AgentWsEmoteMessage, AgentWsClientMessage.
  • Inbound types: AgentWsAuthenticatedMessage, AgentWsJoinedMessage, AgentWsErrorMessage, AgentWsHeartbeatMessage, AgentWsChatServerMessage (channelId?, mentions?), AgentWsServerMessage.
  • Type guards: isAgentWsAuthenticated, isAgentWsError, isAgentWsHeartbeat, isAgentWsChat.

Chat payloads (reference)

  • Send: { type: "chat", text, targetSessionId? } — omit targetSessionId for global; set for DM.
  • Receive: channelId is "global" or dm:sessionA:sessionB for filtering.

Exported types (selection)

DoppelClient, DoppelClientOptions, Occupant, OccupantType, AgentClientOptions, ChatHistoryMessage, GetChatHistoryOptions, GetChatHistoryResult, plus all AgentWs* types above and catalog types.


Requirements

  • Node ≥ 20
  • ESM only
  • In Node, pass WebSocket from the ws package (or compatible) into createClient.