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

figuard

v1.2.0

Published

FiGuard TypeScript SDK — pre-flight spend authorization for AI agents

Readme

FiGuard TypeScript SDK

npm version Node License

Pre-flight spend authorization for AI agents. Stop your agent from overspending before it happens.

Install

npm install figuard

Requires Node.js 18+.

Zero-config demo (no account needed)

import { FiGuardClient } from "figuard";

// No arguments — connects to the shared public sandbox automatically
const client = new FiGuardClient();

Note: The shared sandbox is for demos only. Data is wiped periodically. For production, self-host FiGuard and pass your API key: new FiGuardClient({ apiKey: "fg_live_...", baseUrl: "https://your-figuard.example.com" }) or set FIGUARD_API_KEY / FIGUARD_BASE_URL environment variables.

Quickstart

import { FiGuardClient, FiGuardDeniedException } from "figuard";

const client = new FiGuardClient({ apiKey: "fg_live_..." });

// 1. Create a budget for your user's session
const budget = await client.createBudget({
  userId: "user_123",
  totalLimit: 500,
  expiresIn: "24h",
  currency: "USD",
});

// 2. Pre-authorize every spend before it happens
try {
  const result = (await client.authorize({
    sessionToken: budget.primaryToken!.sessionToken!,
    agentId: "agent_flight_booker",
    actionType: "PURCHASE",
    description: "NYC to LAX flight",
    requestedQuantity: 299,
    idempotencyKey: "txn-abc-001", // required — use a stable unique key
  })).raiseIfDenied();

  // 3. Execute the real transaction, then confirm
  await client.confirmEvent({
    eventId: result.eventId,
    confirmedQuantity: 299,
    externalTransactionId: externalTxId,
  });

} catch (e) {
  if (e instanceof FiGuardDeniedException) {
    console.log("Spend denied:", e.denialReason);
    // e.g. INSUFFICIENT_FUNDS, BUDGET_PAUSED, ANOMALY_DETECTED
  }
}

Allocation-based budgets

Allocations ring-fence spend by category and enforce item-type rules:

const budget = await client.createBudget({
  userId: "user_123",
  totalLimit: 500,
  expiresIn: "24h",
  currency: "USD",
  allocations: [
    {
      category: "flights",
      allowedCategories: ["flight", "airline"],
      limit: 300,
      enforcementMode: "STRICT",
      forbiddenItemTypes: ["gift_card", "upgrade"],
    },
    {
      category: "hotels",
      allowedCategories: ["hotel", "accommodation"],
      limit: 200,
      enforcementMode: "CATEGORY_CONSTRAINED",
    },
  ],
});

// claimedCategory must match one of allowedCategories
const result = await client.authorize({
  sessionToken: budget.primaryToken!.sessionToken!,
  agentId: "travel_agent",
  actionType: "PURCHASE",
  description: "Flight to NYC",
  requestedQuantity: 250,
  idempotencyKey: "flight-nyc-001",
  claimedCategory: "flight",
  claimedItemType: "economy_ticket",
});

Payment lifecycle

// Authorize reserves funds — money has not moved yet
const result = (await client.authorize({ ... })).raiseIfDenied();

// Confirm when payment succeeds — finalizes the spend
await client.confirmEvent({ eventId: result.eventId, confirmedQuantity: 249 });

// Fail when the payment processor declines — releases the reservation
await client.failEvent({ eventId: result.eventId, reason: "PAYMENT_DECLINED" });

// Void if the action is cancelled before payment
await client.voidEvent({ eventId: result.eventId, reason: "USER_CANCELLED" });

Anomaly detection

Enable per-budget anomaly detection to auto-pause budgets when a request is statistically unusual:

const budget = await client.createBudget({
  userId: "user_123",
  totalLimit: 2000,
  expiresIn: "24h",
  currency: "USD",
  anomalyDetectionEnabled: true,
});

When a request exceeds mean × multiplier (default 3×) and at least 5 prior transactions exist, the budget is auto-paused and an ANOMALY_DETECTED webhook fires. Resume after review:

const budget = await client.resumeBudget({
  budgetId,
  overrideReason: "Reviewed — legitimate bulk purchase",
  overrideBy: "ops-team",
});

Delegation tokens (multi-agent fleets)

Delegate a capped slice of a fleet budget to a sub-agent. The sub-agent gets its own sessionToken scoped to specific categories and limits — it cannot exceed its caps even if the parent budget still has funds.

// Parent agent creates the fleet budget
const fleetBudget = await client.createBudget({
  userId: "user_123",
  totalLimit: 2000,
  expiresIn: "8h",
  currency: "USD",
  allocations: [
    { category: "flights", limit: 1000 },
    { category: "hotels", limit: 1000 },
  ],
});

// Issue a scoped token for a sub-agent (e.g. a flight-booking specialist)
const token = await client.createDelegationToken({
  budgetId: fleetBudget.id,
  label: "flight-agent",
  caps: [{ category: "flights", limit: 300 }],
});
// Hand token.sessionToken to the sub-agent
const flightAgentToken = token.sessionToken!;

// Sub-agent authorizes against its cap — cannot exceed $300 flights
const result = (await subClient.authorize({
  sessionToken: flightAgentToken,
  agentId: "flight-specialist",
  actionType: "PURCHASE",
  description: "NYC flight",
  requestedQuantity: 250,
  idempotencyKey: "flight-nyc-001",
  claimedCategory: "flights",
})).raiseIfDenied();

// Revoke at any time
await client.revokeDelegationToken(token.id);

Error handling

import {
  FiGuardDeniedException,  // decision === DENIED (not an HTTP error)
  FiGuardApiError,         // 4xx / 5xx from the API
  FiGuardConnectionError,  // network failure after all retries
} from "figuard";

try {
  const result = (await client.authorize({ ... })).raiseIfDenied();
} catch (e) {
  if (e instanceof FiGuardDeniedException) {
    console.log(e.denialReason);    // e.g. "INSUFFICIENT_FUNDS"
    console.log(e.denialMessage);   // human-readable explanation
    // if denialReason === "ENTITY_ALREADY_AUTHORIZED":
    //   e.originalEventId          // UUID of the existing event
  } else if (e instanceof FiGuardApiError) {
    console.log(e.statusCode, e.message);
  } else if (e instanceof FiGuardConnectionError) {
    console.log("Network failure:", e.message);
  }
}

The SDK automatically retries 5xx responses up to 3 times with exponential backoff (1s, 2s, 4s). 4xx errors are never retried.

Ledger and reporting

// Paginated spend history
const page = await client.getLedger({
  budgetId,
  page: 0,
  size: 20,
  decision: "CONFIRMED",
});
for (const event of page.events) {
  console.log(event.id, event.decision, event.confirmedQuantity);
}

// Causal spend tree (which agent triggered which spend)
const tree = await client.getSpendTree(budgetId);
for (const root of tree.roots) {
  console.log(root.event.agentId, root.children.length, "child events");
}

Multi-resource authorization (CompositeGuard)

Authorize across multiple budgets atomically — if any resource denies, all prior authorizations in the same call are voided automatically:

import { CompositeGuard, GuardedResource } from "figuard";

const guard = new CompositeGuard([
  new GuardedResource(client, tokenBudget.sessionToken!, "tokens"),
  new GuardedResource(client, usdBudget.sessionToken!, "USD"),
]);

const result = await guard.authorize({
  agentId: "travel_agent",
  actionType: "LLM_CALL",
  description: "search flights",
  requested: { tokens: 1500, USD: 0.09 },
  idempotencyKey: crypto.randomUUID(),
});

if (result.allAuthorized) {
  // ... do the work ...
  await guard.confirm(result, { tokens: 1423, USD: 0.085 });
} else {
  console.log(`Denied on ${result.firstDenialResource}: ${result.firstDenial?.denialReason}`);
}

dry_run mode

Test your integration without writing to the ledger or firing webhooks:

const result = await client.authorize({
  sessionToken: budget.primaryToken!.sessionToken!,
  agentId: "agent_1",
  actionType: "PURCHASE",
  description: "Test authorization",
  requestedQuantity: 100,
  idempotencyKey: "test-key-001",
  dryRun: true, // nothing written, no webhooks fired
});
console.log(result.isAuthorized); // true/false based on real enforcement

Configuration

const client = new FiGuardClient({
  apiKey: "fg_live_...",
  baseUrl: "https://your-figuard.example.com", // your self-hosted instance
  timeoutMs: 30_000,                 // per-request timeout (default: 30s)
});

Security notes

  • The raw sessionToken is returned once on createBudget() and never again. Store it securely — treat it like a password.
  • idempotencyKey is optional — a UUID is auto-generated if omitted. Provide a stable key per logical spend intent so retries collapse to the same event instead of creating duplicates.

Self-hosting

Point baseUrl at your own FiGuard deployment:

const client = new FiGuardClient({
  apiKey: "fg_live_...",
  baseUrl: "http://localhost:8080",
});

Run FiGuard locally: see figuard-core for Docker setup.

MCP Server

Use FiGuard directly from Claude Code, Cursor, or Claude Desktop — no SDK code required:

npx figuard-mcp

See figuard-mcp for setup instructions.