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

@caisson-sh/tool-exec

v0.4.1

Published

Governed tool-call / sandboxed-exec primitive for Agentic-Dev: default-deny allowlist + Zod-strict argument schemas + execFile arg-arrays only (no shell) + structured argument provenance.

Readme

@caisson-sh/tool-exec

Governed tool-call / sandboxed-exec primitive — the security floor Agentic-Dev's tool layer stands on.

What it gives you

  • Default-deny command allowlist. createToolExec({ allowlist }) — a call names a registered logical command; anything unregistered (or an empty allowlist) is refused with a NotFoundError before anything spawns.
  • execFile arg-arrays only. Never execSync/exec/shell: true, never a concatenated command string. Each registered command declares a real executable + a Zod-.strict() schema its args must satisfy; args are validated with parseStrict and the VALIDATED result becomes the argv array passed to execFile — no user/agent value ever reaches a shell.
  • Structured argument provenance. Every call returns { command, args, exitCode, stdout, stderr, ok, reason?, at } — a plain-data audit record (not WORM), with output bounded to 64KB.
  • Injected everything. cwd, timeoutMs, the spawn seam (execFn), and the clock (now) are all config — no module-level secrets/constants for endpoints or executables.
  • Optional per-command env. A CommandSpec may set env: { … } to narrow the spawned child's environment. Absent (the default), the child inherits the parent's full environment — unchanged from before this option existed. When supplied, the object is used verbatim (never merged with process.env), so include PATH explicitly if the command needs it. A default flip to always-narrow would be a separate major version.

Entry points

  • . — the full surface, node-capable (createToolExec and its execFile spawn seam).
  • ./browser — the browser-safe subset: createToolProposer, the default-deny lookup + Zod argv validation with no spawn attached, so the gate can decide inside a client bundle. It is the same implementation createToolExec runs. Every name on ./browser is also on ..

Use

import { z } from "zod";
import { createToolExec } from "@caisson-sh/tool-exec";

const toolExec = createToolExec({
  allowlist: [
    {
      name: "git-status",
      command: "/usr/bin/git",
      argsSchema: z.array(z.string()).max(1).default(["status"]),
    },
  ],
  cwd: "/repo",
  timeoutMs: 30_000,
});

const result = await toolExec.run("git-status", ["status"], "agent-turn-14");
// result.ok / result.exitCode / result.stdout / result.args (the resolved argv array)

A name not on the allowlist, or args that fail the schema, throw before any process spawns.

Tests

bun test packages/tool-exec/src — default-deny (unregistered name, empty allowlist), schema-rejects-before-exec, a valid call's full provenance record, a non-zero exit captured (not thrown), and one real execFile spawn against node -e proving the bounded-output path end to end.

Two-phase approvals (ADR-0423, extended by ADR-0427)

await tool.propose(name, input, reason) returns a serializable ToolApproval: random approval ID, digest-bound expiresAt timestamp, canonical digest, policy version, command, validated argv and reason. Keep the ToolExec instance and its record store on the trusted server. Only call execute(approval) after your application authenticates and authorizes the approval actor; the package does not implement that actor's authentication or approval UI.

Execution atomically consumes the private record, checks the public digest, revalidates the saved original input with the current schema, and requires the resulting argv to match what was approved. Command, explicit environment or policyVersion rotation invalidates outstanding records. Environment is never accepted in the public envelope. The ADR-0420 inherited-environment default remains unchanged when a spec omits env.

The default store snapshots input and holds at most 1,000 pending records in memory; restart loses pending approvals. Every approval expires fifteen minutes after issuance (APPROVAL_TTL_MS), with no renewal. The server clock config.now supplies issuance and execution timestamps; adapters must use the same epoch-millisecond clock. Expiration is checked again after consuming a durable record, before spawn. A client cannot extend it.

After an authorized denial/cancellation, call await tool.reject(approval.approvalId). It atomically deletes without spawning, returns true if a pending record was removed, and false if absent/expired/already consumed. Rejected IDs cannot later execute. Reject and consume compete for one atomic deletion: rejection cannot cancel execution that has already consumed its record. Caller authorization is required for rejection as for execute.

For persistence, inject approvalStore implementing create-only put and mutually atomic get-and-delete consume and delete-only reject. Durable adapters must enforce expiry and reclaim expired records. Never expose store methods to clients. The default store removes expired records on put/reject and refuses them on consume; idle stale entries remain bounded until the next operation. Rejection/expiry free capacity without a process restart.

A digest is an integrity binding to this trusted record, not a signature over arbitrary client data. Invalid or rotated execution attempts consume the record once its ID is resolved; request a new proposal after rejection/expiry. createToolProposer remains a pure browser-safe validation preview; its ProposedToolCall cannot be passed to execute.