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

@atulo/sage

v0.1.0

Published

Official TypeScript / Node.js client for Sage by Atulo. Wrap every AI call in your healthcare product and prove it was HIPAA-defensible.

Readme

@atulo/sage

Official TypeScript / Node.js client for Sage by Atulo. Wrap every AI call inside your healthcare product and prove it was HIPAA-defensible.

Sage is the quiet observer that sits next to every AI call your team makes. It watches, it remembers, and when a regulator asks "how did your AI decide that," Sage answers, with receipts.

Install

npm install @atulo/sage
# or
bun add @atulo/sage

Quick start

import { Sage } from "@atulo/sage";

const sage = new Sage({
  apiKey: process.env.SAGE_API_KEY!,
  baseUrl: process.env.SAGE_BASE_URL!, // e.g. "https://sage.your-domain.com"
});

const audit = await sage.log({
  patient_id: "pt_a1b2c3",
  context_tag: "diagnosis_summary",
  ai_input: "Patient presents with persistent cough lasting 3 weeks.",
  ai_output: "Recommend chest X-ray and CBC.",
  consent_verified: true,
});

console.log(audit.audit_id); // -> "aud_8f3c2e91d6b4a702"

API surface

new Sage({ apiKey, baseUrl?, fetchImpl?, timeoutMs? })

baseUrl is the URL of your Sage by Atulo deployment. Resolved in this order:

  1. The baseUrl constructor option, if passed.
  2. The SAGE_BASE_URL env var, if set.
  3. A loud placeholder (https://your-sage-domain.example) so a missing configuration fails visibly instead of silently shipping requests to a dead host.

Set this to whatever you deploy to: https://sage.your-domain.com, http://localhost:3000 for local dev, a staging URL, or a self-hosted instance.

sage.log(input) — POST /v1/log

The core call. Wrap any AI completion you do in your product. Returns the sealed audit envelope including immutable_hash, consent_log, bias_check, and a Gemini-generated explanation.

sage.consent.upsert({ patient_id, consented, consent_scope?, revoked? }) — POST /v1/consent

Create or update the consent ledger entry for a patient. consent_scope is an array of allowed context_tag values. Pass revoked: true to flip the record off.

sage.consent.get(patient_id) — GET /v1/consent/:patient_id

Read current consent state.

sage.audit.list({ ...filters }) — GET /v1/audit

Paginated + filterable list. Filters: patient_id, context_tag, consent_status, bias_flag, from (ISO 8601), to, page, limit.

sage.audit.get(audit_id) — GET /v1/audit/:id

Single record by aud_* id.

sage.audit.export({ ...filters }) — GET /v1/audit/export

Returns the CSV body as a string (up to 10,000 rows per call).

sage.usage() — GET /v1/usage

Month-to-date usage + plan limit + remaining.

Sage.verifyWebhook({ body, signature, secret })

Static. Constant-time verification of the X-Atulo-Signature header on inbound webhook deliveries. Pass the raw request body (the same bytes we POSTed you, before any JSON parsing).

import { Sage } from "@atulo/sage";

export async function POST(req: Request) {
  const rawBody = await req.text();
  const signature = req.headers.get("X-Atulo-Signature") ?? "";
  const ok = Sage.verifyWebhook({
    body: rawBody,
    signature,
    secret: process.env.SAGE_WEBHOOK_SECRET!,
  });
  if (!ok) return new Response("bad signature", { status: 401 });
  const event = JSON.parse(rawBody);
  // ... handle event.type
  return new Response("ok");
}

Errors: SageApiError

Every non-2xx response throws SageApiError with .status, .code (typed against the server's ApiErrorCode enum), and .message. Catch this to handle rate limits, missing consent, etc.

import { Sage, SageApiError } from "@atulo/sage";

try {
  await sage.log({ ... });
} catch (e) {
  if (e instanceof SageApiError && e.code === "rate_limit_exceeded") {
    // back off and retry later
  } else {
    throw e;
  }
}

Browser / Edge

The client uses native fetch and is ESM-only. It works in Node 18+, Deno, Bun, Cloudflare Workers, and Vercel Edge Functions. Pass a custom fetchImpl if you need to inject one (testing, retries, etc.).

Sage.verifyWebhook uses node:crypto. If you receive webhooks in a Web-Crypto environment (Edge), use crypto.subtle.verify directly with the secret. See docs/INTEGRATION_GUIDE.md.

License

MIT