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

@selat-ai/router-client

v0.2.0

Published

TypeScript client for interacting with SELAT Router.

Readme

@selat-ai/router-client

TypeScript SDK for selat-router that offers a fetch-like API to pay any endpoints simply with Circle Nanopayments.

Install

npm install @selat-ai/router-client

For Circle Agent Wallet signing, install the Circle CLI in the same app so serverless runtimes can resolve it without relying on a global circle binary:

npm install @selat-ai/router-client @circle-fin/cli

Quick Start

Supported signers include Private Keys, the Circle Agent Wallet, Circle Developer-Controlled Wallets, and custom Remote Signers. The recommended approach is to use the Circle Agent Wallet, ensuring that sensitive private keys remain entirely outside the SDK environment.

Starting with Private Key

import { RouterClient, createViemSigner } from "@selat-ai/router-client";

const signer = createViemSigner(process.env.X402_CLIENT_PRIVATE_KEY as `0x${string}`);

const client = new RouterClient({
  chain: "base",
  signer
});

const response = await client.fetch("https://upstream.example.com/v1/data", {
  method: "GET"
});

Using Circle Agent Wallet (RECOMMENDED)

Before using this path, install and set up your Circle Agent Wallet here:

https://developers.circle.com/agent-stack/agent-wallets/quickstart

You should already have:

  1. Circle CLI installed and authenticated
  2. A funded agent wallet on the target chain
import { RouterClient, createCircleAgentWalletSigner } from "@selat-ai/router-client";

const signer = createCircleAgentWalletSigner({
  address: process.env.SELAT_SIGNER_ADDRESS as `0x${string}`,
  chain: "base"
});

const client = new RouterClient({
  chain: "base",
  signer
});

const response = await client.fetch(
  "https://pro-api.coinmarketcap.com/x402/v3/cryptocurrency/quotes/latest?symbol=eth",
  { method: "GET" }
);

When @circle-fin/cli is installed locally, the SDK automatically runs the package entrypoint with the current Node executable. If you prefer a specific CLI binary, pass cliCommand; relative paths such as node_modules/.bin/circle are resolved from process.cwd().

For Next.js or Vercel serverless functions that use Circle Agent Wallet signing, include the CLI files in the function trace:

import { CIRCLE_AGENT_WALLET_NEXT_TRACE_INCLUDES } from "@selat-ai/router-client";

/** @type {import("next").NextConfig} */
const nextConfig = {
  outputFileTracingIncludes: {
    "/api/your-paid-route": CIRCLE_AGENT_WALLET_NEXT_TRACE_INCLUDES
  }
};

export default nextConfig;

Serverless signing

createCircleAgentWalletSigner shells out to the local circle CLI (child_process.spawn). That binary — and its authenticated circle login session — does not exist in a serverless function (Vercel, AWS Lambda, Netlify, Google Cloud Functions, Cloud Run), where the filesystem is ephemeral and read-only. Calling it there throws an explicit error rather than failing with an opaque spawn circle ENOENT.

For serverless deployments, use createHttpRemoteSigner to delegate signing to a service you host (where the Circle CLI / Agent Wallet credentials live). Unlike the low-level createRemoteSigner, it bakes in the HTTP transport and performs Gateway owner-address resolution, so it is a drop-in replacement for the CLI signer — correct for smart-contract-account wallets, not just EOAs.

import { RouterClient, createHttpRemoteSigner } from "@selat-ai/router-client";

const signer = createHttpRemoteSigner({
  address: process.env.SELAT_SIGNER_ADDRESS as `0x${string}`,
  endpoint: process.env.SELAT_SIGNER_API_URL as string,
  token: process.env.SELAT_SIGNER_API_TOKEN // optional bearer auth
});

const client = new RouterClient({ chain: "base", signer });

Your signing service must accept POST { address, typedData } and respond with { "signature": "0x..." }, signing typedData with the key of address.

Custom Signer

Use this when signing happens outside the SDK, for example in a wallet service, HSM, or KMS.

import { RouterClient, createRemoteSigner } from "@selat-ai/router-client";

const signer = createRemoteSigner(
  "0x1111111111111111111111111111111111111111",
  async ({ address, typedData }) => {
    // Delegate to your wallet/HSM/KMS signer service.
    const response = await fetch("https://signer.example.com/sign-typed-data", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ address, typedData })
    });
    const data = await response.json() as { signature: `0x${string}` };
    return data.signature;
  }
);

const client = new RouterClient({
  chain: "base",
  signer
});

Using Circle Developer-Controlled Wallet

Use this path when your application signs through Circle's Developer-Controlled Wallet.

import { RouterClient, createCircleDeveloperControlledWalletSigner } from "@selat-ai/router-client";

const signer = createCircleDeveloperControlledWalletSigner({
  address: process.env.SELAT_SIGNER_ADDRESS as `0x${string}`,
  apiKey: process.env.SELAT_CIRCLE_API_KEY!,
  entitySecret: process.env.SELAT_CIRCLE_ENTITY_SECRET!,
  walletId: process.env.SELAT_CIRCLE_WALLET_ID
});

const client = new RouterClient({
  chain: "base",
  signer
});

Refunds

Refund requests and status queries automatically send an X-SELAT-DUMMY-EIP712-SIGNATURE header. It contains a base64url-encoded JSON payload with a zero-value Gateway owner-probe EIP-712 TransferWithAuthorization typed data object and its signature.

The owner probe uses the GatewayWalletBatched domain, a zero-value transfer to the zero address, and the Gateway wallet as its verifying contract. This matches the typed data used to resolve the owner for Circle Gateway payments, so services recover the same signing owner. It is not a payment authorization. Services should use it only to recover the signing owner and validate the refund request.

import { RouterClient, createViemSigner } from "@selat-ai/router-client";

const signer = createViemSigner(process.env.X402_CLIENT_PRIVATE_KEY as `0x${string}`);
const client = new RouterClient({
  chain: "base",
  signer
});

const refund = await client.refundClaim("selatx<quote-id>");
console.log(refund);

const status = await client.refundQuery("selatx<quote-id>");
console.log(status);

For Circle Agent Wallet signing, replace the signer with:

const signer = createCircleAgentWalletSigner({
  address: process.env.SELAT_SIGNER_ADDRESS as `0x${string}`,
  chain: "base"
});

Run the Example

You can run the included example script to call a real upstream endpoint through router.

  1. Copy .env.example to .env and fill values.
  2. Run:

With Private key:

export SELAT_CHAIN=base
export SELAT_TARGET_URL="https://pro-api.coinmarketcap.com/x402/v3/cryptocurrency/quotes/latest?symbol=eth"
export X402_CLIENT_PRIVATE_KEY=0x<your-private-key>
pnpm run example-pay

Using Remote signer:

export SELAT_SIGNER_MODE=remote
export SELAT_CHAIN=base
export SELAT_TARGET_URL="https://pro-api.coinmarketcap.com/x402/v3/cryptocurrency/quotes/latest?symbol=eth"
export SELAT_SIGNER_ADDRESS=0x<your-signer-address>
export SELAT_SIGNER_API_URL="https://signer.example.com/sign-typed-data"
pnpm run example-pay

Using Circle Agent Wallet:

export SELAT_SIGNER_MODE=circle-agent-wallet
export SELAT_CHAIN=base
export SELAT_TARGET_URL="https://pro-api.coinmarketcap.com/x402/v3/cryptocurrency/quotes/latest?symbol=eth"
export SELAT_SIGNER_ADDRESS=0x<your-agent-wallet-address>
pnpm run example-pay

Using Circle Developer-Controlled Wallet:

export SELAT_SIGNER_MODE=circle-dev-controlled-wallet
export SELAT_CHAIN=base
export SELAT_TARGET_URL="https://pro-api.coinmarketcap.com/x402/v3/cryptocurrency/quotes/latest?symbol=eth"
export SELAT_SIGNER_ADDRESS=0x<your-wallet-address>
export SELAT_CIRCLE_API_KEY=<your-circle-api-key>
export SELAT_CIRCLE_ENTITY_SECRET=<your-entity-secret>
export SELAT_CIRCLE_WALLET_ID=<your-wallet-id>
pnpm run example-pay

Refund Example

Copy .env.example to .env, then set the refund quote and action:

export SELAT_CHAIN=base
export SELAT_QUOTE_ID=selatx<quote-id>
export SELAT_REFUND_ACTION=claim
export SELAT_SIGNER_MODE=private-key
export X402_CLIENT_PRIVATE_KEY=0x<your-private-key>
pnpm run example-refund

To query the refund status, set SELAT_REFUND_ACTION=query. To use Circle Agent Wallet instead, set SELAT_SIGNER_MODE=circle-agent-wallet and provide SELAT_SIGNER_ADDRESS.