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

@yoctotta/kaman-sdk

v0.1.0

Published

TypeScript SDK for the Kaman 3.2 engine. Operations are generated from the engine's own OpenAPI description so they cannot drift from the server; streaming, auth refresh, retries and typed errors are hand-written on top. Includes an app layer for Kaman-ba

Readme

@yoctotta/kaman-sdk

TypeScript SDK for the Kaman 3.2 engine.

Operations are generated from the engine's own OpenAPI description, so they cannot drift from the server. Streaming, auth refresh, retries and typed errors are hand-written on top, because codegen does those badly.

npm install @yoctotta/kaman-sdk

Two entry points:

| Import | For | |---|---| | @yoctotta/kaman-sdk/app | Building an app on Kaman. Start here. | | @yoctotta/kaman-sdk | The full generated surface — every endpoint. |


The one rule

Server-side only. A platform key in a browser bundle is a credential handed to anyone who opens devtools, and it acts as its owner against every surface that owner can reach.

Import this from Next.js route handlers or server components. Browser code calls your own app's /api/* routes, which call this.


Quick start

// app/api/ask/route.ts
import { kamanApp } from "@yoctotta/kaman-sdk/app";

const kaman = kamanApp();  // reads KAMAN_BASE_URL + KAMAN_API_KEY

export async function POST(req: Request) {
  const { question } = await req.json();

  // 1. Retrieve the passages that bear on the question.
  const hits = await kaman.queryKb(process.env.KAMAN_KB_ID!, question, 4);
  if (hits.length === 0) {
    return Response.json({ answer: "That isn't covered by the handbook." });
  }

  // 2. Answer from ONLY those passages.
  const context = hits
    .map((h, i) => `[${i + 1}] from ${h.fileName}:\n${h.chunk}`)
    .join("\n\n");

  const answer = await kaman.chat([
    {
      role: "system",
      content:
        "Answer using ONLY the passages given. If they do not cover the " +
        "question, say so — do not fill the gap from general knowledge. " +
        "Cite the passage number next to the fact it supports.",
    },
    { role: "user", content: `${context}\n\nQuestion: ${question}` },
  ]);

  return Response.json({ answer, sources: hits.map((h) => h.fileName) });
}

Retrieval before generation is what makes the answer checkable. A model asked "how many days of leave do I get" will always produce a number; the retrieval step is what makes it your organisation's number, and the citation is what lets the reader verify it.


The app surface

queryKb(kbId, query, topK?)

Search a knowledge base. Returns scored passages, best first — each with chunk, fileName and score.

chat(messages, model?)

One LLM turn through the OpenAI-compatible gateway. model defaults to "kaman-default", which resolves your deployment's default.

askAgentStructured(agentId, schema, prompt, opts?)

Ask an agent for an answer matching a JSON schema, and get back fields rather than prose.

const triage = await kaman.askAgentStructured(agentId, {
  type: "object",
  properties: {
    severity: { type: "string", description: "low | medium | high" },
    summary: { type: "string" },
    needsHuman: { type: "boolean" },
  },
  required: ["severity", "summary", "needsHuman"],
}, `Triage this ticket:\n${body}`);

if (triage.needsHuman) await page(triage.severity, triage.summary);

Use this over a plain agent call whenever your code has to act on the answer. The plain surface is text in, text out — fine for "summarise this", wrong for anything you branch on, because turning prose back into fields is where apps break. Ask for a total and one reply says 1,240.50, the next says "about 1240 euros".

It works by handing the agent a tool whose input schema is the shape you want; the model either produces something matching it or it has not answered.

Pass onEvent to watch the run — tool calls arrive as they happen, which is what a progress UI needs for a turn that takes minutes.

queryLake(lake, schema, sql, limit?) · queryLakeObjects(...)

Read from a KDL lake with SQL. SELECT only — your row-level security and column masks apply. queryLakeObjects returns row objects instead of the column/row arrays.

insertRows(lake, schema, table, columns, rows)

Insert is the only write verb the engine exposes; there is no REST update or delete. An app that needs mutable records models them as revisions: add a monotonic column, insert a new row per change, and read through a view that keeps the newest per key.

runWorkflow(workflowId, initialState?)

Run a workflow to completion and return its terminal run — state plus per-node outputs. The wire is SSE; this collects it into one awaited result.

kaman.raw

The full generated client — raw.sessions, raw.lakes, raw.agents, raw.files, and every other domain. Reaching past the helpers never means leaving the SDK.


Configuration

| Variable | Meaning | |---|---| | KAMAN_BASE_URL | Engine base URL, e.g. https://kaman.example.com | | KAMAN_API_KEY | A kmn_ platform key. Server environment only. |

Inside a Kaman preview both are injected for you — the key is minted per preview and short-lived — so an app runs there with no setup. Your app's own configuration (which knowledge base, which lake) goes in a .env at the project root.

kamanApp() throws if either is missing, rather than quietly building an unauthenticated client whose failure surfaces much later as a confusing 401.


Errors

Every failure is a KamanApiError carrying the engine's stable error code, so you branch on the code rather than matching strings:

import { KamanApiError } from "@yoctotta/kaman-sdk/app";

try {
  await kaman.queryKb(kbId, question);
} catch (e) {
  if (e instanceof KamanApiError && e.status === 404) { /* no such KB */ }
  throw e;
}

Requirements

Node 20+. ESM only. Zero runtime dependencies.