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

@singularity-layer/cli

v0.2.2

Published

Singularity developer CLI — deploy paid endpoints (Processors) on the Singularity Cloud Network

Downloads

470

Readme

singularity — Singularity developer CLI

Build on the Singularity Cloud Network from your terminal.

npm i -g @singularity-layer/cli
singularity --help

Requires Node 20+ and a Solana keypair (solana-keygen new). No account, no API key, no dashboard login — your wallet is the account.

Processors

A Processor is a loop you write; we host it, run it on demand, and buyers pay you directly per call. There is no platform cut and we never custody your money: an x402 payment is a USDC transfer from the buyer's wallet to yours.

mkdir my-loop && cd my-loop
singularity processors init my-loop      # scaffolds processor.json + processor.js
singularity processors deploy            # prints the invoke token ONCE
singularity processors run '{"hi":1}'    # invoke it, print the output
singularity processors publish           # instantly public — no review step
singularity processors logs              # runs + failure rate
singularity processors revenue           # sales and compute spend, everything you own

Your loop is a standard Worker module:

export default {
  async fetch(request) {
    const { input } = await request.json();
    // Call APIs and models with fetch(). Credentials for the hosts you declared
    // are injected for you, so no keys belong in this file.
    return Response.json({ ok: true, received: input });
  }
}

TypeScript and npm packages

Add --bundle (or "$bundle": true in processor.json) and the CLI runs esbuild locally, so import and npm dependencies work:

npm i zod
singularity processors deploy --bundle --entry processor.ts

The bundling happens on your machine, on purpose. We never run npm install for you: a postinstall script is arbitrary code, and running it on infrastructure that holds platform credentials is not something scanning makes safe. Your bundle crosses the boundary; your node_modules never does.

The bundler targets a Worker isolate, so a dependency reaching for fs, child_process or raw sockets fails here, on your terminal, naming the importer — instead of dying inside a run a buyer has already paid for.

Going back to a single file needs --no-bundle, because a stored bundle keeps winning at load time and plain code would silently never take effect.

Invoking

singularity processors run '{"n":10}'    # JSON in, output out
singularity processors call -i           # prompts per field, from your own input_schema
singularity processors schema            # what buyers and agents see

Secrets

Declare a secret in processor.json, then set its value:

singularity processors secrets OPENAI_API_KEY=sk-...
singularity processors env list                 # which are declared, which are set
singularity processors env unset OPENAI_API_KEY

Values are write-only to you: we never hand one back through the API.

Two modes, chosen per secret. The default is the strong one:

"secrets": [
  { "name": "OPENAI_API_KEY", "hosts": ["api.openai.com"],
    "inject": { "header": "Authorization", "format": "Bearer {value}" } },
  { "name": "SIGNING_KEY", "mode": "env" }
]

inject (the default) — the value never enters your isolate. You call fetch() with no credential and the egress gateway adds the header server-side, for the hosts you declared and nowhere else. Code that never holds a key cannot leak the key.

mode: "env"your code holds the value, via await SGL.secrets.get('SIGNING_KEY'). This is a real downgrade and it is opt-in for exactly that reason: once your code has the value it can print it into your logs, or send it to any host in your egress allowlist, and we cannot stop either. Use it only for credentials that genuinely cannot be an outbound header — a key you sign with locally, or a value an SDK insists on reading itself. The alternative most people reach for otherwise is hard-coding the secret in their source, which is worse in every way.

singularity processors env list shows which of yours is which.

Processor state

A processor is a fresh isolate every run, so anything you want to keep has to go somewhere:

// Key/value — a cursor, a cache, a dedupe set. Values are opaque strings, byte-exact.
await SGL.kv.put('cursor', '2026-08-12');
const cursor = await SGL.kv.get('cursor');

// Compare-and-swap, for a counter or a lock. Concurrent runs are the normal case.
const cur = await SGL.kv.getWithVersion('count');
await SGL.kv.put('count', String(Number(cur.value) + 1), { ifVersion: cur.version });

// Objects — a generated PDF, an image, a dataset.
await SGL.files.put('reports/august.pdf', bytes, { contentType: 'application/pdf' });
const { url } = await SGL.files.downloadUrl('reports/august.pdf', { ttlSeconds: 3600 });

downloadUrl gives you a signed, expiring link a buyer can fetch with no credential. It is always served as an attachment with a forced application/octet-stream type — your declared type is never echoed — so a stored HTML or SVG file cannot execute. Treat it as a bearer link: anyone who gets it can download until it expires.

None of this needs an egress.allow entry. It is not the network.

Turning it off without losing it

singularity processors pause      # buyers and token callers refused; no payment is taken
singularity processors resume

pause is the switch to reach for when an upstream key hits its quota or a loop misbehaves. Unlisting only hides you from the catalogue — the endpoint keeps answering anyone with the URL — and delete burns the slug forever. While paused you can still invoke it yourself with your wallet, so you can verify a fix before resuming.

For scripts and agents

Every command takes --json:

singularity processors list --json | jq '.processors[] | select(.paused_at)'
singularity processors run '{"n":1}' --json

Auth

A per-request Solana wallet signature, using ~/.config/solana/id.json by default (--keypair <path>, SINGULARITY_KEYPAIR, or singularity processors configure to change it). The key never leaves your machine — only a signature is sent, and it covers the method, the path and a hash of the body, so it authorizes exactly one request and cannot be replayed against another.

Programmatic use

import { bundleProcessor, api, loadKeypair, usd } from '@singularity-layer/cli';

The exported pieces are the ones that are easy to get subtly wrong: the signature scheme, the micro-USDC formatting (never use floats for money — the ledger is integers), and the bundler configuration. Subcommands are deliberately not exported; they call process.exit.

The other Singularity CLIs

These are separate on purpose — different audiences, different runtimes.

| CLI | Who it's for | |---|---| | singularity | Developers building on the platform | | sglcode | Agentic coding on the grid | | sgl | Node operators running compute and earning |

New capabilities here become subcommands, never new binaries.