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

@operon/sdk

v0.2.0

Published

Operon SDK - request placements from the Operon ad network

Downloads

824

Readme

@operon/sdk

Operon's TypeScript SDK. Connect any agent to the ad network.

Install

npm install @operon/sdk

Works in both ESM and CommonJS Node.js projects.

Quickstart

The SDK ships dual ESM + CJS builds, so the same package works in both project styles. Pick the snippet that matches your project's "type" field in package.json (default for npm init -y is CommonJS).

ESM ("type": "module", or .mjs):

import { initOperon } from "@operon/sdk";

const operon = initOperon({ url: "https://api.operon.so" });

const result = await operon.getPlacement(userQuery, {
  placement_context: "user asked about swapping ETH",
});

if (result.decision === "filled") {
  console.log("Recommendation:", result.placement?.service);
}

// Inspect the meta block to see how trust scoring is treating you.
console.log(result._meta);

CommonJS (default for npm init -y, no "type": "module"):

const { initOperon } = require("@operon/sdk");

const operon = initOperon({ url: "https://api.operon.so" });

const result = await operon.getPlacement(userQuery, {
  placement_context: "user asked about swapping ETH",
});

if (result.decision === "filled") {
  console.log("Recommendation:", result.placement?.service);
}

// Inspect the meta block to see how trust scoring is treating you.
console.log(result._meta);

Once you've made a call, confirm the SDK is wired up correctly:

npx @operon/sdk test     # fire a sandbox getPlacement
npx @operon/sdk status   # show local SDK state (UUID, source, registration)

Register for production demand

After integrating, register to bump your sandbox quota and join the early-access list for production demand:

npx @operon/sdk register

The CLI prompts for email, framework name, agent description, and a monthly call volume tier:

Operon Publisher Registration

Registering raises your sandbox quota from 100 to 1000 placements/hr
and puts you on the early-access list for production demand.

We'll only email you about quota changes and demand availability.

We'll use this to follow up about production demand pool access. No marketing spam.
Email: [email protected]

Help us prioritize which framework SDKs to ship next.
Framework (e.g. elizaos): elizaos

Help us understand who's building and what for.
What does your agent do? DeFi research agent

Used to assign appropriate quotas and prioritize early-access invites.
Expected monthly calls:
  1) <1K       (testing / hobbyist)
  2) 1K-10K    (small production agent) [default]
  3) 10K-100K  (mid-tier production)
  4) 100K-1M   (high-volume)
  5) >1M       (commercial)
Choice [2]:

On success, the CLI echoes back your registration and the new quota:

Done. You're registered.

  Email:                 [email protected]
  Framework:             elizaos
  Agent:                 DeFi research agent
  Expected volume:       1K-10K
  Source:                (not set, that's fine)
  Status:                registered
  Sandbox quota:         1000 placements/hr (was 100)

Next steps:
  - Try a placement (see https://operon.so/developers)
  - Watch your impressions log
  - Email [email protected] when you're ready for production demand

API

initOperon(options)

| Option | Type | Description | |--------|------|-------------| | url | string | Base URL of the Operon API. | | publisherName | string? | Display name for your agent. Defaults to operon-sdk. | | apiKey | string? | Optional Bearer token. Only needed for authenticated publishers. Sandbox usage does not require a key. | | timeoutMs | number? | Request timeout. Defaults to 10000. | | source | string? | Marketplace/skill attribution tag ([A-Za-z0-9._-], max 64). First-touch wins: persisted on first valid call, ignored on later inits with a different value. | | onRetryable | (err: OperonRetryableError) => void? | Observability hook fired when the server returns 503 + Retry-After, before the error is thrown. Use for metrics, Sentry, alerting, etc. Errors thrown by the callback are swallowed. Note that the SDK does not await the callback's return value - async callbacks should not throw; wrap async work in a try/catch internally. | | maxRetryAfterMs | number? | Cap on Retry-After-derived backoff hints (ms). Defaults to 60_000. Invalid values (0, negative, NaN, Infinity) fall back to the default. |

getPlacement(query, context)

query is the user's natural-language input. context must include placement_context (a one-sentence description of what the user is doing). Optional fields: asset, intent, category, sentiment, actions.

Returns a placement response. Inspect _meta.is_sandbox to know if you got mock demand.

Configuration

| Env var | Purpose | |---------|---------| | OPERON_CLIENT_ID | Override the persisted UUID. Use in CI or ephemeral environments without writeable home directories. Also disables source persistence so CI runs don't pollute developer-machine attribution. | | OPERON_SOURCE | Override the attribution tag at request time. Doesn't write to disk. Use in CI/containers to test attribution behavior without affecting persisted state. | | OPERON_URL | Default URL for npx @operon/sdk register. Defaults to https://api.operon.so. |

Sandbox vs production

Until production demand is live, all placements are sandbox. Check _meta.is_sandbox in responses. Auction logic, trust scoring, and _meta are real - only the demand pool is mock.

The first 100 calls per UUID return a register prompt. Past 100, the message upgrades to a quota nudge. Past 1000, it includes a personal note pointing to [email protected]. Registered users get 10x quota and a different message.

Circuit breaker

Five failures within 30 seconds opens the circuit. While open, getPlacement throws synchronously without making a request. The window slides, so the circuit auto-closes once 30 seconds pass without new failures.

Handling 503 throttling

When the server returns 503 with a Retry-After header (sandbox-lane rate ceiling), the SDK throws OperonRetryableError carrying retryAfterMs. This does NOT count toward the circuit breaker.

import { initOperon, OperonRetryableError } from "@operon/sdk";

const op = initOperon({ url: "https://api.operon.so" });
try {
  const result = await op.getPlacement(query, ctx);
  // use result
} catch (err) {
  if (err instanceof OperonRetryableError) {
    // Server asked us to wait; e.g. skip this placement and continue
    console.log(`throttled, retry after ${err.retryAfterMs}ms`);
    return null;
  }
  throw err;
}

Observability hooks and backoff tuning

Use onRetryable to pipe throttle events into your metrics or alerting stack. The hook fires before the throw - your try/catch still runs normally. Use maxRetryAfterMs to override the default 60s cap on Retry-After hints (useful when your loop budget is tighter):

import { initOperon, OperonRetryableError } from "@operon/sdk";

const op = initOperon({
  url: "https://api.operon.so",
  // Fired when 503 + Retry-After comes back. Doesn't gate the throw -
  // your try/catch still runs. Use this for metrics, alerts, etc.
  onRetryable: (err) => {
    metrics.increment("operon.retryable", { retryAfterMs: err.retryAfterMs });
  },
  // Override the default 60s cap on retry hints.
  maxRetryAfterMs: 30_000,
});