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

@northbound-run/agent-wrap

v0.3.0

Published

Analyze a codebase, detect skills/agents/MCP servers, and bundle into a Docker container running Claude Code as a headless service

Readme

agent-wrap

Analyze a codebase, detect its skills, agents, and MCP servers, generate a config file, and bundle everything into a Docker container that runs Claude Code as a headless service.

Core principle: Claude Code IS the agent runtime. agent-wrap wraps it, not replaces it. You prototype locally in Claude Code, then agent-wrap build bundles the same environment into a deployable container. The experience is 1:1.

Quickstart

# In your project directory
bunx @northbound-run/agent-wrap init        # Analyze codebase, generate config
bunx @northbound-run/agent-wrap validate    # Check config + skill commands
bunx @northbound-run/agent-wrap dev         # Run locally via Docker Compose
bunx @northbound-run/agent-wrap build       # Build production Docker image
bunx @northbound-run/agent-wrap deploy fly  # Deploy to Fly.io (or railway, render, ssh)

How it works

  1. agent-wrap init scans your project for CLI entry points (skills), MCP servers, and agent definitions
  2. Generates agent-wrap.config.yaml with detected capabilities and confidence scores
  3. agent-wrap build produces a Docker image with your project + Claude Code CLI installed
  4. The container exposes a REST API for autonomous tasks and direct skill execution
POST /api/task    --> Claude Code runs autonomously (needs LLM API key)
POST /api/skills/ --> Direct skill execution (no LLM needed)
POST /webhook     --> Webhook ingestion (HMAC auth, async callbacks)

Two interaction modes

Autonomous mode (primary)

POST a task prompt and Claude Code executes it with full access to your project's tools, MCP servers, and AGENTS.md -- exactly as if running claude -p "do this" locally.

# Create a task
curl -X POST http://localhost:3000/api/task \
  -H "X-API-Key: $AGENT_WRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Transcribe meeting.m4a and generate a summary report"}'
# Returns: {"taskId": "task_a1b2c3d4"}

# Check status
curl http://localhost:3000/api/task/task_a1b2c3d4 \
  -H "X-API-Key: $AGENT_WRAP_API_KEY"

# Stream progress via SSE
curl http://localhost:3000/api/task/task_a1b2c3d4 \
  -H "X-API-Key: $AGENT_WRAP_API_KEY" \
  -H "Accept: text/event-stream"

# Cancel a running task
curl -X POST http://localhost:3000/api/task/task_a1b2c3d4/cancel \
  -H "X-API-Key: $AGENT_WRAP_API_KEY"

Direct mode (no LLM needed)

Call detected skills directly via REST -- no Claude API key required. Useful for deterministic operations like transcription, PDF generation, or image processing.

curl -X POST http://localhost:3000/api/skills/transcribe \
  -H "X-API-Key: $AGENT_WRAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "base.en"}'

Config reference

agent-wrap init generates agent-wrap.config.yaml. Key sections:

schema_version: 3
name: my-project
version: "0.1.0"
language: typescript          # or python
runtime: oven/bun:1           # Docker base image

skills:
  transcribe:
    description: "Transcribe audio files"
    command: ["bun", "run", "src/cli/transcribe.ts", "--model", "${model}"]
    inputs:
      - name: model
        type: string
        default: "base.en"
    outputs:
      type: text
    timeout: 300
    concurrency: 1

# LLM provider config -- env vars passed to Claude Code
llm:
  env:
    ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY}"
    # Override for alternative providers:
    # ANTHROPIC_BASE_URL: "${MIMO_BASE_URL}"
    # ANTHROPIC_AUTH_TOKEN: "${MIMO_API_KEY}"

# Claude Code settings
claude_code:
  version: ""                         # Pin Claude Code version (empty = latest)
  max_budget_usd: 5.00                # Cost cap per task
  task_timeout: 1800                  # Wall-clock timeout (seconds)
  max_concurrent_tasks: 5             # Max parallel autonomous tasks
  append_system_prompt: ""            # Additional instructions for Claude Code
  permission_mode: "bypassPermissions" # Required for headless containers
  mcp_config: ".mcp.json"             # MCP config forwarded to Claude Code
  channels:
    enabled: []                       # Official channels -> --channels
    development: []                   # Dev channels -> --dangerously-load-development-channels
    permission_prompt_tool: ""        # Optional MCP tool for relayed approvals

# Server settings
server:
  port: 3000
  idle_timeout: 1800                  # Auto-shutdown after idle (0 = disable)
  max_upload_size: "500mb"
  max_output_capture: "10mb"

# Auth
auth:
  type: api_key
  header: "X-API-Key"
  # Key set via AGENT_WRAP_API_KEY env var at runtime

# Webhook channel (separate port, HMAC auth)
channel:
  webhook:
    port: 3001
    hmac:
      algorithm: sha256
      header: "X-Webhook-Signature"
    replay_protection:
      max_age_seconds: 300

# Lifecycle hooks
hooks:
  pre_execute: []
  post_execute: []
  on_error: []

# Session state passthrough
session:
  enabled: false
  header: "X-Session-Params"
  env_prefix: "SESSION_"

Language support

TypeScript/Bun

Detects skills from package.json scripts, commander/yargs option definitions, and process.argv destructuring patterns.

Python

Detects skills from pyproject.toml [project.scripts], setup.py console_scripts, Click decorators (@click.option, @click.argument), and argparse add_argument calls.

Auto-detection

agent-wrap init detects:

| What | Source | Notes | |---|---|---| | Skills | package.json scripts, pyproject.toml, CLI frameworks | Confidence-scored; >= 0.5 = active | | MCP servers | .mcp.json, mcp.config.json | Claude Code reads these natively | | Claude channels | .claude/settings*.json, channel-capable .mcp.json servers | Detects official plugin channels and local dev channels | | Agents | AGENTS.md, CLAUDE.md | Claude Code reads these natively | | System deps | Native module imports | Mapped to apt packages |

Re-running init on an existing config appends new detections as commented blocks -- it never modifies existing entries.

Webhook integration

The webhook adapter runs on a separate port (default 3001) with HMAC-SHA256 authentication:

# Generate signature
TIMESTAMP=$(date +%s)
BODY='{"type":"task","payload":{"prompt":"Run tests"}}'
SIGNATURE=$(echo -n "${TIMESTAMP}.${BODY}" | openssl dgst -sha256 -hmac "$AGENT_WRAP_WEBHOOK_SECRET" | cut -d' ' -f2)

curl -X POST http://localhost:3001/webhook \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Timestamp: $TIMESTAMP" \
  -H "X-Webhook-Signature: sha256=$SIGNATURE" \
  -H "X-Reply-URL: https://my-server.com/callback" \
  -d "$BODY"

Webhook payloads use type to route: "task" for autonomous execution, "skill" for direct skill calls.

LLM provider configuration

The llm.env section supports ${VAR} interpolation from the container environment:

# Default: Anthropic API
llm:
  env:
    ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY}"

# Alternative provider (e.g., Xiomi/Mimo)
llm:
  env:
    ANTHROPIC_BASE_URL: "${MIMO_BASE_URL}"
    ANTHROPIC_AUTH_TOKEN: "${MIMO_API_KEY}"
    ANTHROPIC_MODEL: "mimo-v2-pro"

LLM env vars are passed only to Claude Code processes, never to skill child processes.

Native Claude Code channels

agent-wrap can now forward native Claude Code channel flags from YAML:

claude_code:
  mcp_config: ".mcp.json"
  channels:
    enabled:
      - plugin:discord@claude-plugins-official
    development:
      - server:webhook
    permission_prompt_tool: "reply"

Notes:

  • enabled maps to claude --channels ...
  • development maps to claude --dangerously-load-development-channels ...
  • Native Claude channels require a Claude.ai login. Anthropic's docs say Console/API-key-only auth is not sufficient for channels.
  • When channel config is present, agent-wrap checks claude auth status and fails early if Claude Code is not logged in with authMethod: "claude.ai".
  • For Docker/dev deployments, make sure the container has access to a pre-authenticated Claude home (for example by mounting ~/.claude for the runtime user).

Security model

  • REST auth: API key via header (timing-safe comparison). /health and /ready are unauthenticated.
  • Webhook auth: HMAC-SHA256 over {timestamp}.{body}, with replay protection (300s window).
  • SSRF protection: Webhook reply URLs to private IPs (10.x, 172.16-31.x, 192.168.x, 127.x) are blocked unless explicitly allowlisted.
  • Process isolation: Skills run with shell: false, stdin closed, filtered env (allowlist only).
  • LLM var isolation: API keys and LLM env vars are never passed to skill child processes.
  • Container security: Non-root appuser (UID 1001), output capture capped at 10MB.

Docker

Build

bunx @northbound-run/agent-wrap build                    # Builds agent-wrap/{name}:{version}
bunx @northbound-run/agent-wrap build -t my-image:latest # Custom tag

Local dev

bunx @northbound-run/agent-wrap dev   # Generates docker-compose.yml + starts with live reload

When dev mode is running, agent-wrap also serves a small browser terminal at http://localhost:3000/__dev (or your configured server port). The dev UI reuses the task execution/streaming flow but stays outside the authenticated /api/* surface, so it works even when AGENT_WRAP_API_KEY is set without exposing that key to browser JavaScript.

The generated dev compose file now mounts:

  • your project-local .claude/ into /app/.claude
  • your user Claude home ${HOME}/.claude into /home/appuser/.claude

That gives Claude Code access to repo settings/plugins plus your host Claude.ai login state, which is required for native channels.

Run

docker run -p 3000:3000 -p 3001:3001 \
  -e AGENT_WRAP_API_KEY=your-api-key \
  -e AGENT_WRAP_WEBHOOK_SECRET=your-secret \
  -e ANTHROPIC_API_KEY=your-anthropic-key \
  agent-wrap/my-project:0.1.0

Deploying to production

agent-wrap deploy generates platform-specific config for Fly.io, Railway, Render, or any SSH-reachable host:

bunx @northbound-run/agent-wrap deploy fly       # Generate fly.toml
bunx @northbound-run/agent-wrap deploy railway    # Generate railway.toml
bunx @northbound-run/agent-wrap deploy render     # Generate render.yaml
bunx @northbound-run/agent-wrap deploy ssh --hostname app.example.com --ssh-host [email protected]

See DEPLOY.md for detailed guides per platform.

API endpoints

| Method | Path | Auth | Description | |---|---|---|---| | GET | /health | None | Health check | | GET | /ready | None | Readiness probe (Claude Code available) | | GET | /openapi.json | None | OpenAPI 3.1 schema | | POST | /api/task | API key | Create autonomous task | | GET | /api/task/:id | API key | Get task status (JSON or SSE) | | POST | /api/task/:id/cancel | API key | Cancel running task | | POST | /api/skills/:name | API key | Execute skill directly | | POST | /webhook | HMAC | Webhook ingestion (port 3001) |

Development

bun install     # Install dependencies
bun test        # Run test suite (73 tests)
bunx tsc --noEmit  # Type check

Architecture

Event Sources:              Channel Dispatch:              Handlers:
  REST adapter    ->                                    -> Claude Code Runner (autonomous)
  Webhook adapter ->     validate -> route -> execute   -> Skill Executor (direct, no LLM)

All interactions flow through a unified channel dispatch layer. REST and webhook are event source adapters feeding into the same dispatch. This means one auth layer, one interop surface, and every capability gets both REST and webhook access automatically.

License

MIT

npm publishing

This package is configured to publish to npm as @northbound-run/agent-wrap from the GitHub repository Northbound-Run/agent-wrap.

Publishing is handled by .github/workflows/publish.yml and is triggered by pushing a version tag like v0.1.0.

One-time npm setup

Configure npm trusted publishing for this package. npm only lets you attach a trusted publisher after the package already exists, so the very first release may need a one-off manual publish (or a temporary granular publish token) before switching fully to trusted publishing.

Configure npm trusted publishing for this package:

  • GitHub organization/user: Northbound-Run
  • Repository: agent-wrap
  • Workflow filename: publish.yml

Release flow

# 1. Bump package.json version
# 2. Commit and push your branch
# 3. Create and push a matching tag

git tag v0.1.0
git push origin main --follow-tags

The workflow verifies that the Git tag matches package.json, runs bun test, previews the package with npm pack --dry-run, then publishes with npm publish.