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

@hanzo/ai

v0.2.0

Published

Headless TypeScript client for the Hanzo AI backend — chat completions, Anthropic messages, models, and portable chat/message threads. No UI, no React.

Readme

@hanzo/ai

Headless TypeScript client for the Hanzo AI backend.

Pure client + types. No UI, no React, no DOM dependencies beyond the standard fetch / ReadableStream web APIs. This is the shared API layer consumed by hanzo.chat, hanzo.app, the Hanzo desktop app, and the hanzo-dev CLI. UI lives in separate packages (@hanzo/chat, @hanzo/agent).

It wraps the real Hanzo AI backend (github.com/hanzoai/ai, served via api.hanzo.ai):

  • OpenAI-compatible chat completions (/v1/chat/completions), streaming and not.
  • Anthropic-compatible messages (/v1/messages), streaming and not.
  • Models catalog (/v1/models).
  • Portable chat threads — the cross-surface conversation store (chats + their messages), so a conversation started on one surface resumes on another.
  • Account — the signed-in IAM identity.

Install

npm install @hanzo/ai

Usage

Auth is a Hanzo IAM access token (e.g. from @hanzo/iam) sent as Authorization: Bearer <token>.

import { createAiClient } from "@hanzo/ai";

const ai = createAiClient({
  // baseUrl defaults to https://api.hanzo.ai
  token: process.env.HANZO_TOKEN!,
  // or, for rotating tokens:
  // getToken: async () => session.accessToken,
});

Chat completions (OpenAI-compatible)

const res = await ai.chat.completions.create({
  model: "zen-1",
  messages: [{ role: "user", content: "Explain MoE routing in one sentence." }],
});
console.log(res.choices[0].message.content);

Streaming:

const stream = await ai.chat.completions.create({
  model: "zen-1",
  messages: [{ role: "user", content: "Write a haiku about Go." }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta.content ?? "");
}

Tool calling works by passing tools / tool_choice (forwarded to the provider).

Messages (Anthropic-compatible)

const msg = await ai.messages.create({
  model: "claude-sonnet",
  max_tokens: 512,
  system: "You are concise.",
  messages: [{ role: "user", content: "Summarize the CAP theorem." }],
});

// Streaming yields Anthropic SSE events:
const events = await ai.messages.create({ /* ...,*/ stream: true } as any);
for await (const ev of events) {
  if (ev.type === "content_block_delta") { /* ev.delta.text */ }
}

Models

const models = await ai.models.list(); // Model[]

Portable chat threads

A Chat is a durable thread keyed by "owner/name"; its turns are Messages. This is the store that lets a conversation move between surfaces.

await ai.chats.create({ owner: "hanzo", name: "thread-1", user: "alice", type: "AI" });

await ai.chats.messages.append({
  owner: "hanzo",
  name: "msg-1",
  chat: "thread-1",
  author: "alice",
  text: "Hello",
});

const thread = await ai.chats.get("hanzo/thread-1");
const turns = await ai.chats.messages.list({ chat: "thread-1" });

Account

const me = await ai.account.get();

Errors

All failures throw a subclass of HanzoAIError:

  • APIError — non-2xx HTTP, or a CRUD envelope with status: "error". Carries .status and .body.
  • AuthError — no token was available for an authenticated request.
import { APIError } from "@hanzo/ai";

try {
  await ai.models.list();
} catch (err) {
  if (err instanceof APIError) console.error(err.status, err.message);
}

Advanced

createAiClient accepts:

| option | type | default | | ---------- | -------------------------------------- | ------------------------ | | baseUrl | string | https://api.hanzo.ai | | token | string | — | | getToken | () => string \| Promise<string> | — | | fetch | typeof fetch | globalThis.fetch | | headers | Record<string, string> | {} |

The low-level transport (client.http) and the SSE helpers (parseSSE, streamChatCompletion, streamMessage) are exported for custom calls.

License

Apache-2.0