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

keel-sdk

v0.2.4

Published

Drop-in replacements for OpenAI, Anthropic, Google, xAI, and Meta SDKs with built-in AI governance. One-line import change adds permit-first policy enforcement, budget controls, audit trails, and usage reporting to every AI call.

Readme

keel-sdk

TypeScript SDK for the Keel AI governance API.

Keel lets you issue permits before AI calls, enforce policies, track usage, and audit decisions — across any provider.

⚠️ Keel is currently in private beta. You'll need a Keel account and API key to use this SDK. Sign up for early access →

One-Line Provider Migration

Add Keel governance to your existing AI code with a single import change — no other code modifications required.

OpenAI:

// BEFORE:
import OpenAI from "openai";

// AFTER:
import { OpenAI } from "keel-sdk/providers/openai";

Anthropic:

// BEFORE:
import Anthropic from "@anthropic-ai/sdk";

// AFTER:
import { Anthropic } from "keel-sdk/providers/anthropic";

Google (Gemini):

// BEFORE:
import { GoogleGenerativeAI } from "@google/generative-ai";

// AFTER:
import { GoogleGenerativeAI } from "keel-sdk/providers/google";

xAI (Grok):

// BEFORE:
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.x.ai/v1", apiKey: "..." });

// AFTER:
import { Grok } from "keel-sdk/providers/xai";
const client = new Grok();

Meta (Llama):

// BEFORE:
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.llama-api.com", apiKey: "..." });

// AFTER:
import { Llama } from "keel-sdk/providers/meta";
const client = new Llama();

The rest of your code stays identical. Under the hood, each call automatically requests a governance permit, proxies the request through Keel, and reports token usage. If a permit is denied, a KeelError with status 403 and code permit_denied is thrown.

Set three env vars and you're done:

export KEEL_BASE_URL="https://api.keelapi.com"
export KEEL_API_KEY="keel_sk_..."
export KEEL_PROJECT_ID="prj_..."

Optionally pass a subject to attribute usage to a specific user:

const client = new OpenAI({
  keelSubject: { type: "user", id: "usr_42" },
});

Streaming works transparently — pass stream: true and iterate as usual.

See examples/provider-swap.ts for a full working example.

Install

npm install keel-sdk

Setup

import { KeelClient } from "keel-sdk";

const client = new KeelClient({
  baseUrl: process.env.KEEL_BASE_URL!,
  apiKey: process.env.KEEL_API_KEY!,
});

Permits

Request a permit before making an AI call:

const permit = await client.permits.create({
  project_id: "proj_123",
  idempotency_key: crypto.randomUUID(),
  subject: { type: "user", id: "usr_123" },
  action: { name: "ai.generate" },
  resource: {
    type: "request",
    id: "req_123",
    attributes: {
      provider: "openai",
      model: "gpt-4o-mini",
      estimated_input_tokens: 200,
      estimated_output_tokens: 500,
    },
  },
});

if (permit.decision === "allow") {
  // proceed with AI call
}

Dry run

const result = await client.permits.dryRun(permitRequest);

List and get

const list = await client.permits.list({ project_id: "proj_123", limit: 50 });
const permit = await client.permits.get("permit_id");

Report usage

await client.permits.reportUsage("permit_id", {
  actual_input_tokens: 180,
  actual_output_tokens: 420,
});

Attestation, evidence, lineage

await client.permits.attest("permit_id", {
  attestor: "[email protected]",
  attestation_type: "approve",
  evidence_url: "https://example.com/review/123",
});
await client.permits.addEvidence("permit_id", {
  evidence_type: "hash",
  evidence_value: "abc123",
  label: "response_hash",
  attached_by: "audit-system",
});
const evidence = await client.permits.listEvidence("permit_id");
const lineage = await client.permits.lineage("permit_id");
const bundle = await client.permits.bundle("permit_id");

Executions

Run a model synchronously:

const result = await client.executions.create({
  operation: "generate.text",
  messages: [{ role: "user", content: "Summarize this document." }],
  routing: { provider: "openai", model: "gpt-4o-mini" },
});

Stream tokens as they arrive:

for await (const event of client.executions.stream({
  operation: "generate.text",
  messages: [{ role: "user", content: "Write a poem." }],
  routing: { provider: "openai", model: "gpt-4o-mini" },
})) {
  if (event.event_type === "content.delta") process.stdout.write(event.data.delta.text);
  if (event.event_type === "done") console.log("\nFinished.");
}

Execute (unified)

const result = await client.execute.run({
  model: "gpt-4o-mini",
  input: { text: "Translate to Spanish: Hello world" },
  provider: "openai",
});

Proxy

Pass requests through to providers with Keel governance applied:

const response = await client.proxy.openai({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hello" }],
});

// Also: client.proxy.anthropic(), .google(), .xai(), .meta()

Jobs

Submit async jobs and poll for results:

const job = await client.jobs.create({
  permit: { /* PermitRequest */ },
  provider_payload: {
    messages: [{ role: "user", content: "Analyze this dataset." }],
  },
  metadata: {},
});

const status = await client.jobs.get(job.job_id);
// status.status: "submitted" | "queued" | "processing" | "completed" | "failed"

API Keys

const key = await client.apiKeys.create();
const keys = await client.apiKeys.list();
await client.apiKeys.revoke(key.id);

Request Timeline

const timeline = await client.requests.timeline("request_id");

Error Handling

import { KeelError } from "keel-sdk";

try {
  await client.permits.create(request);
} catch (err) {
  if (err instanceof KeelError) {
    console.error(err.status);  // HTTP status code
    console.error(err.code);    // e.g. "permit_denied"
    console.error(err.message); // human-readable message
    console.error(err.field);   // field that caused the error, if any
  }
}

Automatic Retry

Enable automatic retry with exponential backoff for transient errors:

import { KeelClient, DEFAULT_RETRY_CONFIG } from "keel-sdk";

const client = new KeelClient({
  baseUrl: process.env.KEEL_BASE_URL!,
  apiKey: process.env.KEEL_API_KEY!,
  retryConfig: DEFAULT_RETRY_CONFIG,
});

The default configuration retries up to 3 times with exponential backoff (500ms initial delay, 2x multiplier, 30s max) on status codes 408, 429, 500, 502, 503, and 504.

Customize the retry behavior:

import { KeelClient } from "keel-sdk";
import type { RetryConfig } from "keel-sdk";

const retryConfig: RetryConfig = {
  maxRetries: 5,
  initialDelayMs: 1000,
  maxDelayMs: 60_000,
  backoffMultiplier: 3.0,
  retryableStatusCodes: [429, 500, 502, 503, 504],
};

const client = new KeelClient({
  baseUrl: process.env.KEEL_BASE_URL!,
  apiKey: process.env.KEEL_API_KEY!,
  retryConfig,
});

Retry applies to all non-streaming requests. Streaming requests (executions.stream()) are never retried. When the server sends a Retry-After header, the SDK respects it as a minimum delay before the next attempt.

Per-Request Timeout

Override the global timeout for individual requests by passing a RequestOptions object:

// Global timeout is 30 seconds (default)
const client = new KeelClient({
  baseUrl: process.env.KEEL_BASE_URL!,
  apiKey: process.env.KEEL_API_KEY!,
});

// This specific permit creation gets 30 seconds
const permit = await client.permits.create(permitRequest, { timeoutMs: 30_000 });

// Streaming calls can use a longer timeout
const stream = await client.executions.stream(
  { permit_id: permit.permit_id, operation: "generate.text", messages },
  { timeoutMs: 120_000 },
);

// Per-request timeout works on all sub-clients:
// client.permits, client.executions, client.execute, client.proxy, client.jobs, client.requests

For streaming, the effective timeout is timeoutMs * 6 (same multiplier as the global timeout). When no per-request timeout is provided, the global timeoutMs from client options is used.

Freshness Headers

For replay-protected endpoints, enable freshness headers:

const client = new KeelClient({
  baseUrl: process.env.KEEL_BASE_URL!,
  apiKey: process.env.KEEL_API_KEY!,
  requestFreshness: { enabled: true },
});

This adds X-Keel-Timestamp and X-Keel-Nonce to every request.

License

MIT