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

actauth

v0.0.12

Published

A self-hosted policy gate for AI agent tool calls.

Downloads

1,099

Readme

ActAuth (TypeScript)

Part of LoopEngine — a runtime for defining and running AI agents through a transparent ReAct loop — handling rule-based permission gating (allow/ask/deny) with human-approval hooks. Works standalone too, no dependency on LoopEngine itself.

Same rule engine, scoped resolution, conditions, and audit log as the Python package at the repo root — ported line-for-line so both stay easy to keep in sync. See the root README for the product pitch and rule format; this file only covers what's specific to the TS build.

Install

npm install

Quickstart

npm run quickstart
import { AuditLog, Gate, type Scope } from "./src/index.js";

const gate = Gate.fromConfig("examples/actauth.yml", {
  auditLog: new AuditLog("actauth-audit.jsonl"),
});

const scope: Scope = { tenant: "beta-fintech", environment: "production", agent: "payments-agent" };
const result = await gate.evaluate("send_refund", { amount: 5000 }, scope);
console.log(result.decision, result.reason);

Test / build

npm test    # vitest
npm run build   # tsc -> dist/

Status

Same as the Python package: rule engine, scoping, conditions, audit log, SlackChatApprover, and WebchatApprover are real and tested. No agent-SDK adapter yet.

ConsoleApprover

The default Approver — a blocking terminal prompt, useful for local dev and for exercising the full ask pipeline without standing up Slack:

import { Gate, ConsoleApprover } from "actauth";

const gate = Gate.fromConfig("actauth.yml", {
  approver: new ConsoleApprover(), // this is also the default if you omit `approver`
});

const result = await gate.evaluate("send_refund", { amount: 900 }, scope);
// prints scope/tool/args/reason, then blocks on `approve? [y/N]`

SlackChatApprover

import { SlackChatApprover } from "actauth";

const approver = new SlackChatApprover({
  botToken: process.env.SLACK_BOT_TOKEN!,
  channel: "#approvals",
  signingSecret: process.env.SLACK_SIGNING_SECRET!,
});

// wire your own route for the Slack app's Interactivity Request URL:
app.post("/slack/interactions", async (req, res) => {
  await approver.handleInteraction(req.rawBody, {
    timestamp: req.headers["x-slack-request-timestamp"],
    signature: req.headers["x-slack-signature"],
  });
  res.status(200).end();
});

requestApproval() posts an interactive Approve/Deny message and resolves when handleInteraction() is called with the matching click — verified against Slack's request signature, timing out (deny) after timeoutMs (default 5 minutes) if nobody responds.

WebchatApprover

For a web UI you own instead of Slack. Like SlackChatApprover, this class only holds pending requests and resolves them — it doesn't serve HTTP itself, so you wire two routes of your own: one that shows list(), and one that calls decide(id, approved) when a human clicks Approve/Deny.

import { WebchatApprover } from "actauth";

const approver = new WebchatApprover();

app.get("/approvals", (req, res) => res.json(approver.list()));

app.post("/approvals/:id/:decision", (req, res) => {
  const approved = req.params.decision === "approve";
  const found = approver.decide(req.params.id, approved);
  res.status(found ? 200 : 404).end();
});

onPending/onSettled are for pushing an approval live instead of requiring a poller to notice it in list() — e.g. writing it straight to an SSE stream that's already open for the exact request that's blocked waiting on it, the way LoopEngine's own playground does (webApprover.ts creates one WebchatApprover per streamed chat turn, wired to onPending so the popup appears inline in that conversation rather than on a separate page):

const approver = new WebchatApprover({
  onPending: (approval) => sseWrite(res, "approval:pending", approval),
  onSettled: (id) => pendingById.delete(id),
});

Same fail-closed-on-timeout behavior as SlackChatApprover — denies after timeoutMs (default 5 minutes) if nobody decides.