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

@ingram-tech/pulumi-ingram-cloud

v0.7.1

Published

Pulumi dynamic resources for the Ingram Cloud /v1 API — projects, project tokens, agents, MCP servers, channels, webhooks, model keys.

Readme

@ingram-tech/pulumi-ingram-cloud

Pulumi dynamic resources for the Ingram Cloud /v1 API. Declare your tenant's IC configuration — agents, MCP servers, integrations, webhooks, BYOK model keys — as Pulumi resources instead of imperative register-*.ts / ensure-agent.ts scripts, so it lives in state and a change is a pulumi up.

The library is published alongside the Ingram Cloud /v1 API so the wrapper tracks the surface it wraps. The API version it targets is pinned per library version (IC_API_VERSION).

Install

bun add @ingram-tech/pulumi-ingram-cloud @pulumi/pulumi

How the repos interact

cloud.ingram.tech/pulumi/   THIS LIBRARY — knows nothing about any consumer
                            (Agent, McpServer, TelegramBot, WhatsAppConfig,
                             Webhook, ModelKey, Secret)

app repos (thornhill, …)    declare their AGENTS — the `instructions` are
                            app-owned content (prompts), so they stay in the app
                            repo, sourced from the app's own spec module. The app's
                            Pulumi program imports this library + those specs.

infra repo (~/src/infra)    declares tenant INTEGRATION config (MCP, Telegram, WhatsApp,
                            webhook, model keys) alongside DNS/DB/Vercel, and can
                            read an app stack's outputs via pulumi.StackReference to
                            push IC-derived values (agent ids, webhook secret)
                            into Vercel env. No prompts ever live here.

The hard rule: prompts never leave the app repo. This library carries no content; it only carries the machinery.

Connection

Every resource takes baseUrl + token (the tenant-admin bearer; IcProject / IcProjectToken take an organization key instead — see below). Resolve them once with connectionFromConfig() and spread:

import * as ic from "@ingram-tech/pulumi-ingram-cloud";

const conn = ic.connectionFromConfig(); // ingram-cloud:token (secret) / :baseUrl
                                        // or env INGRAM_CLOUD_TOKEN|CLOUD_API_KEY

Set the token as a stack secret:

pulumi config set --secret ingram-cloud:token tha_live_…

Resources

IcAgent (declare in the APP repo)

Create-or-adopt by slug → publish a new immutable version only when the content changed → roll it out. Mirrors the old ensure-agent.ts idempotency, so the first pulumi up after switching from a script adopts the existing agent (matched by slug) rather than recreating it.

slug is the immutable reconcile key; it defaults to the Pulumi resource name (here "curator"), so existing stacks keep adopting the same agent. name is a free display label you can rename without churning the agent. Changing slug replaces it.

import { AGENT_SPECS } from "../src/lib/cloud/agent-spec"; // app-owned

const curator = new ic.IcAgent("curator", {
  ...conn,
  // slug defaults to "curator"; pass `slug` to decouple the key from the name.
  name: AGENT_SPECS.curator.name, // display label, freely renamable
  instructions: AGENT_SPECS.curator.instructions,
  model: AGENT_SPECS.curator.model,
  autoMemory: AGENT_SPECS.curator.auto_memory,
  // memoryConsolidation: true,  // opt into background consolidation (billed)
  // variables: [...], enabledHostedTools: [...], rolloutPercent: 100,
});

export const curatorAgentId = curator.agentId;

Attaching existing smiths to a agent is a one-time fleet backfill — keep it as the app's migration script. New smiths attach at birth in app code. Pulumi does not own per-smith runtime state.

IcMcpServer, IcOauthProvider, IcTelegramBot, IcWhatsAppConfig, IcEmail, IcWebhook, IcModelKey (declare in INFRA)

// Your own server (raw URL).
new ic.IcMcpServer("thornhill-mcp", {
  ...conn, serverName: "thornhill", url: `${APP_URL}/api/mcp`,
  authKind: "static", secret: mcpSecret,
});

// An `oauth`-kind server forwards each end-user's own stored token; its
// provider record tells IC how tokens refresh. Webhook-delegation mode (a
// refreshWebhook and NO clientSecret): IC POSTs { connection_id } to the
// tenant near expiry and the tenant PATCHes a fresh pair back — the tenant
// owns the token format entirely.
new ic.IcMcpServer("thornhill-user-mcp", {
  ...conn, serverName: "thornhill-user", url: `${APP_URL}/api/user-mcp`,
  authKind: "oauth", authProvider: "thornhill",
});
new ic.IcOauthProvider("thornhill-provider", {
  ...conn, provider: "thornhill",
  refreshWebhook: `${APP_URL}/api/ic/refresh`,
});

// A curated third party: stamp from the catalog, gate which tools run, and
// require human approval on the scary ones. Per-smith OAuth tokens are NOT
// declared here — the runtime collects each end-user's identity (the hosted
// connect flow vaults it). `clientMode: "platform"` uses Ingram's OAuth client.
new ic.IcMcpServer("stripe", {
  ...conn, serverName: "stripe", catalog: "stripe",
  toolAllowlist: ["get_balance", "list_charges", "create_refund"],
  approvalPolicy: [{ match: "create_refund" }],
});

new ic.IcTelegramBot("thornhill-telegram", { ...conn, botToken });

// The agent's own email channel (BYO-Cloudflare transport). IC returns the
// inbound HMAC once — it becomes a secret output you flow into the inbound worker.
const email = new ic.IcEmail("thornhill-email", {
  ...conn, cloudflareAccountId, cloudflareApiToken, fromDomain: "mail.thornhill.app",
});
export const inboundEmailSecret = email.inboundSecret; // create-time secret output

const hook = new ic.IcWebhook("thornhill-events", {
  ...conn, url: `${APP_URL}/api/ic/events`,
  events: ["run.completed", "approval.required", /* … */],
});
export const webhookSigningSecret = hook.secret; // whsec_… (create-only secret output)

new ic.IcModelKey("anthropic", { ...conn, provider: "anthropic", apiKey });

IcWebhook.secret is the whsec_… signing secret IC returns exactly once — it becomes a secret Pulumi output, so the infra stack can flow it straight into Vercel env with no copy-paste.

IcProject + IcProjectToken (declare in the PLATFORM stack)

The tier above a project. These take an organization key (organization:*, your account master key) — not a project token. Hand it to a platform stack and it provisions projects and mints each one a project-scoped tenant:* token, which you drop straight into that app's env. The org key reads no run, memory, or smith itself; only the project tokens it mints can.

// In the PLATFORM stack, `ingram-cloud:token` is your ORGANIZATION key.
const project = new ic.IcProject("thornhill", { ...conn }); // adopt-by-name
const icToken = new ic.IcProjectToken("thornhill-admin", {
  ...conn,
  project: project.projectId, // the project id == the tenant
});

// Flow the minted tenant token straight into the app's runtime env:
new vercel.ProjectEnvironmentVariable("th-ic-token", {
  projectId: thornhillVercelId,
  key: "INGRAM_CLOUD_TOKEN",
  value: icToken.projectToken, // secret tenant:* token
  targets: ["production"],
});
export const thornhillProjectId = project.projectId;

IcProject adopts an existing project by projectName (defaults to the resource name), so re-runs reconcile. IcProjectToken.projectToken is a secret output; it re-mints on any change and revokes on destroy.

Importing resources a script already created

Agents and webhooks implement read, so you can adopt pre-existing ones:

pulumi import 'ingram-cloud:index:IcAgent' curator agt_123…

(Agents also self-adopt by slug on first create, so an explicit import is usually unnecessary — a plain pulumi up will reconcile the live agent.)

Notes

  • Built as CommonJS to match the Pulumi Node.js runtime and the infra stack's module: commonjs tsconfig.
  • token and every credential input are marked additionalSecretOutputs, so they are encrypted in Pulumi state.
  • CRUD runs inline in the Pulumi process over the global fetch (Node ≥18).
  • read treats a server-side 404 as "gone" (returns an empty id) so pulumi refresh prunes a resource deleted out-of-band — e.g. after an IC DB reset, pulumi refresh && pulumi up recreates it, no pulumi state delete. Non-404 errors still throw, so a transient API blip can't drop it from state.