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

@odla-ai/o11y

v2.2.2

Published

Official observability client for odla Cloudflare Workers — OpenTelemetry traces, metrics, structured errors, and LLM cost, exported over OTLP to the odla-o11y collector.

Readme

@odla-ai/o11y

⚠️ Early access — pre-1.0. Agents work from bounded runbooks; humans approve credentials, production changes, releases, and merges. APIs and exact package availability can change. Review the documented guarantees and limitations; this software is MIT-licensed and provided without warranty.

Official observability client for odla Cloudflare Workers. Wrap your Worker once and get OpenTelemetry traces, metrics, structured errors, and LLM cost — exported over OTLP to the odla-o11y collector.

  • Traces via the maintained @pydantic/otel-cf-workers adapter (auto-instruments incoming/outgoing fetch, Durable Objects, and bindings). Version 2 floors its OpenTelemetry stack at the baggage-allocation fix.
  • Metrics + logs via lightweight OTLP/JSON emitters (the traces-only otel library doesn't cover them, and the Node metrics SDK doesn't fit the isolate model).
  • Targets the Cloudflare Workers runtime; requires the nodejs_compat flag.
  • Distributed context is deliberately W3C traceparent/tracestate only. tracestate is capped at 512 bytes and baggage is neither parsed nor emitted.

Install

npm i @odla-ai/o11y

For an odla app, add o11y to services in odla.config.mjs, make the source change below, then let the CLI own enablement and credentials:

npx @odla-ai/cli provision --write-dev-vars --push-secrets

The CLI issues or reuses the per-environment ingest token, persists it locally, writes .dev.vars, and transfers ODLA_O11Y_TOKEN to each planned Worker over Wrangler stdin. It never prints the value. Rotate only when necessary with npx @odla-ai/cli provision --rotate-o11y-token --push-secrets (--yes when production is in the plan). The manual token control in Studio is a recovery path and does not update the repository's cached credential.

Migrating from 1.x

  • Replace metrics().histogram(...) with metrics().observe(...). The deprecated alias now emits a non-monotonic gauge observation; use count(...) only for true delta counters.
  • recordLlmUsage(...) now returns { costUsd: number | null, priced: boolean }. Branch on priced (or check costUsd !== null) before doing cost arithmetic; an unknown model is unpriced, not a false $0 call.
  • Self-hosted collectors should apply 0005_ingest_idempotency before upgrading emitters. The collector remains compatible with 1.x clients, while 2.x adds byte/point chunking and idempotent batch headers.

Use

import { withObservability, span, count, recordError, recordLlmUsage } from "@odla-ai/o11y";

const handler = {
  async fetch(req: Request, env: Env): Promise<Response> {
    count("http.requests", 1, { "http.route": new URL(req.url).pathname });
    return span("handle", async () => new Response("ok"), { kind: "server" });
  },
} satisfies ExportedHandler<Env>;

export default withObservability(handler);

Wrap a Durable Object class with instrumentDurableObject(MyDO).

Cloudflare Workflows and other class entrypoints do not pass through a Worker's fetch/scheduled handler. Give them the real invocation context so their spans, metrics, errors, and LLM usage use the same sink and private collector binding:

import { runWithObservability } from "@odla-ai/o11y";

export class ReviewWorkflow extends WorkflowEntrypoint<Env, Params> {
  run(event: WorkflowEvent<Params>, step: WorkflowStep) {
    return runWithObservability(this.env, this.ctx, "workflow.review", () =>
      runReview(event, step));
  }
}

Configuration

For public ingest, ODLA_O11Y_TOKEN enables export. The SDK then defaults the endpoint to https://o11y.odla.ai and the service label to ODLA_O11Y_SERVICE ?? ODLA_APP_ID. These variables remain available for self-hosting and explicit overrides (or override per call via the second withObservability argument):

| Var | Meaning | | --- | --- | | ODLA_O11Y_ENDPOINT | Collector base URL (defaults to https://o11y.odla.ai when a token exists) | | ODLA_O11Y_SERVICE | This service's name (falls back to ODLA_APP_ID) | | ODLA_O11Y_TOKEN | Per-service bearer token (secret) | | ODLA_O11Y_VERSION | Release tag (optional) |

wrangler.jsonc needs "compatibility_flags": ["nodejs_compat"] (for AsyncLocalStorage).

Private ingest via a service binding

By default all signals export over fetch to ODLA_O11Y_ENDPOINT. If the Worker is bound to the collector (a service binding named ODLA_O11Y_COLLECTOR, or one passed as opts.fetcher), the SDK routes traces, metrics, and logs through that binding instead — private worker-to-worker, no public hop, and the endpoint host becomes irrelevant. This is the transport odla's first-party hosting uses; the binding and its credential are injected at deploy time, so instrumented code stays export default withObservability(handler) with nothing else to set.

On public ingest, keep only ODLA_O11Y_TOKEN secret. The CLI supplies the full local values; for deployed odla apps, the SDK defaults above mean the token is the only required o11y Worker setting. Without a token, public export is disabled; with no token, explicit endpoint, or collector binding, every signal exporter is a network no-op and the app itself is unaffected.

The boundary is intentional: the CLI owns deterministic platform work from odla.config.mjs (enablement, token issuance/storage, .dev.vars, and Wrangler secret transfer). Your coding agent or developer owns source semantics: installing this package, applying withObservability, and selecting useful spans, metrics, errors, and LLM-usage records. Humans approve device codes, production changes, and destructive rotations.

API

  • withObservability(handler, opts?) — wrap a Worker handler (fetch/scheduled).
  • runWithObservability(env, ctx, name, operation, opts?) — establish the traced invocation plus metrics/log sink for Workflows or another class entrypoint that does not pass through the exported Worker handler. Signal flushing is attached to the supplied ExecutionContext.waitUntil.
  • instrumentDurableObject(cls, opts?) — wrap a Durable Object class.
  • span(name, fn, opts?) — run fn inside an active span.
  • count(name, by?, attrs?) / metrics() — counters, gauges, and observe(name, value, attrs?, unit?). histogram() remains a deprecated compatibility alias for observe; observations are gauge points, not an OTLP histogram or monotonic sum.
  • recordError(err, report?) — structured error; message/stack go only to the collector's R2 artifact bundle, never to metrics. Caller artifacts are cycle/BigInt/accessor-safe and capped at 64 KB so reporting cannot replace the application error with a serialization failure.
  • recordLlmUsage(usage, opts) — turn {calls, inputTokens, outputTokens} into cost + token metrics and GenAI span attributes. Unknown models return { costUsd: null, priced: false } and emit no false $0 cost point; pass an explicit price override until the shared table knows the model.
  • calculateLlmCost(usage, opts) — perform the same pricing as a pure durable accounting step without emitting telemetry. Pass price: null to record an explicitly unpriced call when a safe provider/cache rate is unavailable.
  • Keep metric attributes low-cardinality. Put run/app/actor ids in spanAttributes; those are stamped only on the active span and never copied onto metric series.

Private-binding traces, metrics, and logs retry network/408/429/5xx failures once by default (exportRetries, maximum 3). Failed metric/log records stay in a bounded per-invocation buffer (maxPendingRecords, default 1,000, hard maximum 10,000). The cap is enforced as records are added, not only during export. Concurrent flushes are serialized, and records appended during an export stay queued for the next pass. This is invocation-local memory, not a durable queue: isolate eviction can still lose a failed export. A non-2xx response is never acknowledged as a successful export. Collector attributes use an exact semantic allowlist for spans and metrics; register custom keys explicitly rather than relying on broad prefixes. Denied keys always remain denied.

The SDK splits every signal under the collector's 1,000-point and 1 MB encoded limits. Each chunk carries a stable x-odla-batch-id across bounded retries; the collector atomically deduplicates it per service across UTC midnight before consuming quota, so an accepted response lost in transit is not stored or charged twice. Generic OTLP clients that omit the header remain backward-compatible but at-least-once. One record that cannot fit by itself is dropped deterministically, counted on the internal sink/exporter drop counter, and reported through a safe console.warn that never includes record content or emits recursive telemetry. Retry timing honors bounded Retry-After and otherwise uses bounded exponential jitter. Trace export itself crosses an explicit instrumentation boundary: public export uses the adapter's original unwrapped fetch, while a configured collector binding is captured before handler bindings are proxied. Export traffic therefore cannot create spans about its own retries or recurse when the collector is down.

Incoming trace propagation accepts only the fixed-size W3C traceparent format and at most 512 bytes of tracestate. The SDK intentionally ignores baggage: odla does not use it, so parsing arbitrary baggage would add an unnecessary attacker-controlled allocation surface. If application data must cross a service boundary, pass it through a validated application header or payload.

Console & debugging

What you emit shows up in the Studio o11y console (per app, in odla.ai): RED charts, LLM cost, a dimension explorer (slice p95/errors by route/status/method/env), error groups that drill into the message + stack + trace waterfall, and alert triggers that fire into your app's odla-db, email, or a webhook. To triage from a coding agent, point it at the locally installed odla-o11y-debug skill from npx @odla-ai/cli setup. The rendered SDK reference is at https://odla.ai/docs/packages/o11y.

License

MIT