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

@howells/envelope

v0.3.0

Published

Strict Zod IO wrapper around local CLI LLMs (Claude Code, Codex, Gemini), plus an AI SDK adapter.

Readme

envelope

Thin wrapper around the claude (Claude Code), codex (Codex CLI), and gemini (Gemini CLI) for strict Zod-validated input/output.

This is designed for local Node apps where you want "model calls" to look like a normal API:

  • validate inputs with Zod
  • request structured JSON output via --json-schema / --output-schema
  • validate outputs with Zod again (defense in depth)

Install

npm install @howells/envelope

Requirements

  • Node 20+
  • claude CLI installed and authenticated (Claude Code), or
  • codex CLI installed and authenticated (Codex), or
  • gemini CLI installed and authenticated (Gemini CLI)

Claude Code Options

createClaudeCodeClient()

| Option | Type | Default | Description | |--------|------|---------|-------------| | model | string | "opus" | Model name passed to --model | | maxBudgetUsd | number | 5 | Spend cap per call via --max-budget-usd | | timeoutMs | number | 120_000 | Kill the process after this many ms | | options.claudePath | string | "claude" | Path to the claude binary | | options.cwd | string | process.cwd() | Working directory for the subprocess | | options.env | NodeJS.ProcessEnv | process.env | Environment variables for the subprocess | | options.permissionMode | string | "dontAsk" | One of "default", "plan", "dontAsk", "acceptEdits", "bypassPermissions", "auto" | | options.tools | string | "" | Tools flag; "" (default) omits the flag entirely, "default" enables built-ins | | options.systemPrompt | string | — | Full system prompt via --system-prompt | | options.appendSystemPrompt | string | — | Appended system prompt via --append-system-prompt | | options.allowedTools | string[] | [] | Repeated --allowedTools per entry | | options.disallowedTools | string[] | [] | Repeated --disallowedTools per entry | | options.fallbackModel | string | — | Fallback model via --fallback-model | | options.betas | string[] | [] | Repeated --betas per entry | | options.agent | string | — | Agent name via --agent | | options.agents | string | — | JSON object string for custom agents via --agents | | options.retries | number | 1 | Retries after timeout kill (total attempts = 1 + retries) | | options.retryDelayMs | number | 800 | Base delay between retries (linear backoff) |

Note: total_cost_usd is reported by the Claude Code CLI. If you're using a subscription plan, this may be an estimate and not necessarily an incremental billed amount.

Codex Options

createCodexClient()

| Option | Type | Default | Description | |--------|------|---------|-------------| | model | string | "gpt-5.3-codex" | Model name passed to --model | | timeoutMs | number | 180_000 | Kill the process after this many ms | | options.codexPath | string | "codex" | Path to the codex binary | | options.cwd | string | process.cwd() | Working directory for the subprocess | | options.env | NodeJS.ProcessEnv | process.env | Environment variables for the subprocess | | options.skipGitRepoCheck | boolean | true | Skip git repo validation via --skip-git-repo-check | | options.sandbox | string | "workspace-write" | One of "read-only", "workspace-write", "danger-full-access" | | options.profile | string | — | Profile name via --profile | | options.config | string[] | [] | Repeated --config key=value per entry | | options.jsonlEvents | boolean | false | Enable JSONL event output via --json | | options.image | string[] | [] | Repeated --image path per entry |

Gemini Options

createGeminiClient()

| Option | Type | Default | Description | |--------|------|---------|-------------| | model | string | "gemini-3-flash-preview" | Model name passed to --model | | timeoutMs | number | 180_000 | Kill the process after this many ms | | options.geminiPath | string | "gemini" | Path to the gemini binary | | options.cwd | string | process.cwd() | Working directory for the subprocess | | options.env | NodeJS.ProcessEnv | process.env | Environment variables for the subprocess | | options.approvalMode | string | "plan" | One of "default", "auto_edit", "yolo", "plan" | | options.sandbox | boolean | false | Enable Gemini sandbox mode | | options.debug | boolean | false | Enable Gemini debug mode | | options.policy | string[] | [] | Repeated --policy path per entry | | options.adminPolicy | string[] | [] | Repeated --admin-policy path per entry | | options.extensions | string[] | [] | Repeated --extensions name per entry | | options.includeDirectories | string[] | [] | Repeated --include-directories path per entry |

Note: Gemini does not currently expose a native JSON-schema flag in its CLI. Envelope's Gemini structured mode embeds the JSON Schema into the prompt, parses the returned JSON strictly, and then validates it again with Zod.

Tracking native schema support upstream:

  • https://github.com/google-gemini/gemini-cli/issues/13388
  • https://github.com/google-gemini/gemini-cli/issues/5021

Usage (Zod envelope)

import { z } from "zod";
import {
  createEnvelope,
  createClaudeCodeClient,
  createCodexClient,
  createGeminiClient,
} from "@howells/envelope";

const summarizeClaude = createEnvelope({
  client: createClaudeCodeClient({ model: "opus", maxBudgetUsd: 2 }),
  input: z.object({ text: z.string().min(1) }),
  output: z.object({ summary: z.string().min(1) }),
  prompt: ({ text }) =>
    `Summarize this in 1 sentence. Return JSON only: ${JSON.stringify({ text })}`,
});

const summarizeCodex = createEnvelope({
  client: createCodexClient({ model: "gpt-5.3-codex" }),
  input: z.object({ text: z.string().min(1) }),
  output: z.object({ summary: z.string().min(1) }),
  prompt: ({ text }) =>
    `Summarize this in 1 sentence. Return JSON only: ${JSON.stringify({ text })}`,
});

const summarizeGemini = createEnvelope({
  client: createGeminiClient({ model: "gemini-3-flash-preview" }),
  input: z.object({ text: z.string().min(1) }),
  output: z.object({ summary: z.string().min(1) }),
  prompt: ({ text }) =>
    `Summarize this in 1 sentence. Return JSON only: ${JSON.stringify({ text })}`,
});

const out = await summarizeClaude({ text: "..." });
console.log(out.summary);

Usage (AI SDK 6)

import { generateText } from "ai";
import { claudeCode, codex, gemini } from "@howells/envelope/ai-sdk";

const { text } = await generateText({
  model: claudeCode("opus"),
  prompt: "Write a haiku about camellias.",
});

const r2 = await generateText({
  model: codex("gpt-5.3-codex"),
  prompt: "Write a haiku about camellias.",
});

const r3 = await generateText({
  model: gemini("gemini-3-flash-preview"),
  prompt: "Write a haiku about camellias.",
});

Structured JSON output is also supported via Output.object():

import { generateText, Output, jsonSchema } from "ai";
import { claudeCode } from "@howells/envelope/ai-sdk";

const { output } = await generateText({
  model: claudeCode("sonnet"),
  prompt: "List three colours and their hex codes.",
  output: Output.object({
    schema: jsonSchema({
      type: "object",
      properties: {
        colours: {
          type: "array",
          items: {
            type: "object",
            properties: {
              name: { type: "string" },
              hex: { type: "string" },
            },
            required: ["name", "hex"],
          },
        },
      },
      required: ["colours"],
    }),
  }),
});

Notes:

  • The adapter uses single-shot calls under the hood (streaming is simulated).
  • If we want true streaming, we can extend it to use claude --output-format stream-json.
  • Codex defaults to workspace-write; opt into danger-full-access explicitly when you really need it.
  • Gemini defaults to approvalMode: "plan" so the CLI stays in a read-only posture unless you opt into a more permissive mode.
  • Gemini structured output is prompt-guided rather than CLI-schema-native, so strict Zod validation after parsing matters even more there.