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

@modelrunner/client

v1.3.0

Published

The modelrunner.ai client for JavaScript and TypeScript

Readme

modelrunner.ai JavaScript/TypeScript client library

Introduction

The modelrunner.ai JavaScript Client Library provides a seamless way to interact with modelrunner endpoints from your JavaScript or TypeScript applications. With built-in support for various platforms, it ensures consistent behavior across web, Node.js, and React Native environments.

Getting started

Before diving into the client-specific features, ensure you've set up your credentials:

import { modelrunner } from "@modelrunner/client";

modelrunner.config({
  // Can also be auto-configured using environment variables:
  credentials: "MODELRUNNER_KEY",
});

Setting MODELRUNNER_KEY (or the MODELRUNNER_KEY_ID and MODELRUNNER_KEY_SECRET pair) in the environment is enough — you can then drop the credentials option entirely.

Note: Ensure you've reviewed the modelrunner.ai getting started guide to acquire your credentials and register your functions. Also, make sure your credentials are always protected. See the ../proxy package for a secure way to use the client in client-side applications.

Long-running functions with modelrunner.subscribe

The modelrunner.subscribe method offers a powerful way to rely on the queue system to execute long-running functions. It returns the result once it's done like any other async function, so your don't have to deal with queue status updates yourself. However, it does support queue events, in case you want to listen and react to them:

const result = await modelrunner.subscribe("my-function-id", {
  input: { foo: "bar" },
  onQueueUpdate(update) {
    if (update.status === "IN_QUEUE") {
      console.log(`Your position in the queue is ${update.position}`);
    }
  },
});

Webhooks

Instead of polling or holding a connection open, you can have modelrunner.ai call you back. This is the only option that survives a restart on either side, which is what makes it the right choice for multi-minute video and training jobs.

const { request_id } = await modelrunner.queue.submit("my-function-id", {
  input: { foo: "bar" },
  webhookUrl: "https://example.com/webhooks/modelrunner",
  // optional — defaults to ["completed"]
  webhookEvents: ["start", "completed"],
});

start is best effort: a fast request can go straight from IN_QUEUE to COMPLETED between two polls, in which case only completed is delivered. Never block waiting for start.

Changed in 1.2.0. webhookUrl was previously accepted and silently ignored — it was sent as a query parameter the API does not read, so no callback was ever made. It is now sent correctly, which also means it is now validated: a value carried over from before (an unreachable URL, one over 2048 characters) turns a submit that used to succeed into a 400.

Verifying a delivery

Every delivery is signed with Standard Webhooks. Fetch your secret once and keep it in your receiver's environment — never in a browser:

const { key } = await modelrunner.webhooks.getSecret();

Then verify each delivery against the raw request body:

import express from "express";
import { modelrunner } from "@modelrunner/client";

app.post(
  "/webhooks/modelrunner",
  // the signature covers the delivered bytes, so the raw body is required —
  // express.json() would destroy it
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let payload;
    try {
      payload = await modelrunner.webhooks.verify({
        secret: process.env.MODELRUNNER_WEBHOOK_SECRET,
        headers: req.headers,
        body: req.body,
      });
    } catch (error) {
      return res.sendStatus(401);
    }
    res.sendStatus(200); // acknowledge first, then do the work
    await handle(payload);
  },
);

verify throws WebhookVerificationError on a missing header, a timestamp outside the 5-minute tolerance, or a signature that does not match. Treat every case the same way and never branch on the message.

What your endpoint must do

  • Respond 2xx directly. Redirects are never followed, so a 301 — a missing trailing slash, an httphttps upgrade, a www. canonicalization — is recorded as a failed attempt and you will see nothing but silence.
  • Deduplicate on the webhook-id header. Delivery is at-least-once and that id is stable across retries.
  • A failed attempt is retried on a fixed schedule, roughly 10 times over 2 hours. Reply 410 Gone to stop delivery permanently.
  • Acknowledge before doing slow work. The attempt has a timeout, and a slow 200 is a failed attempt.

Reading the payload

The body is the same object GET /{owner}/{alias}/requests/{id} returns, plus event and billingStatus. Timestamps are ISO-8601 strings.

🚨 status alone cannot tell success from failure. A failed generation is normalized to status: "COMPLETED" with billingStatus: "failed". Code that keys off status reads every failure as a success — use billingStatus.

if (payload.event === "completed" && payload.billingStatus !== "failed") {
  console.log(payload.output);
}

input is replaced by { _elided: string } when it serializes to more than 64KB; fetch the request itself in that case.

Rotating the secret

const { key } = await modelrunner.webhooks.rotateSecret();

Both the old and the new secret are signed with for 24 hours afterwards, so you have that long to deploy the new value. verify accepts secrets: [next, current] to bridge the gap. Rotating twice inside that window ends it early and breaks receivers still holding the original secret, so this call is never retried automatically.

More features

The client library offers a plethora of features designed to simplify your journey with modelrunner.ai. Dive into the official documentation for a comprehensive guide.