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

@three-ws/knock

v0.1.0

Published

Reach a real person, and pay their price to do it. Every three.ws account can publish a priced door; paying it buys one message through, delivered in person by their 3D companion. Wallet-agnostic: bring any x402 client.

Readme

@three-ws/knock

Reach a real person, and pay their price to do it.

Every three.ws account can publish a door at three.ws/knock/<handle>. The owner sets what one message from a stranger costs. Paying that price buys exactly one message through: it lands in their inbox, and their 3D companion walks on screen wherever they are on the site and delivers it out loud, with your name and what you paid.

The USDC settles directly to the recipient. three.ws never takes custody of it.

npm install @three-ws/knock

Node 20+. No dependencies.


Why this exists

Cold outreach is free to send and expensive to receive, so the person on the receiving end drowns and the message that mattered is lost in the pile. Knock inverts that: the sender pays, the recipient sets the price, and the price is the filter. An agent that genuinely needs a human can buy thirty seconds of their attention for five cents. A spammer cannot buy a million of them.

It is also the first thing an autonomous agent can do that a human notices in the room they are actually in, rather than in a queue they will read later.


Quick start

Read a door before you knock

import { quote } from '@three-ws/knock';

const door = await quote('nirholas');
console.log(door.price);      // "$0.05"
console.log(door.networks);   // ["solana"]
console.log(door.max_chars);  // 600
console.log(door.endpoint);   // "https://three.ws/api/x402/knock?to=nirholas"

quote() costs nothing and needs no account. Read it first: a door owner can change their price at any time.

Knock on a free door

Some doors are free. Those need nothing but fetch:

import { knock } from '@three-ws/knock';

const receipt = await knock({
	to: 'nirholas',
	from: 'Ada (research agent)',
	subject: 'Your x402 settle path',
	message: 'I index x402 endpoints and yours is the only one settling on Solana. Two questions about the facilitator.',
	url: 'https://example.com/ada',
});

console.log(receipt.receipt_url);

Knock on a priced door

This package never signs anything and never picks your chain. You pass the paying fetch from whatever x402 client and wallet you already trust:

import { knock } from '@three-ws/knock';
import { wrapFetchWithPayment } from 'x402-fetch';

const fetchWithPayment = wrapFetchWithPayment(fetch, wallet);

const result = await knock({
	to: 'nirholas',
	from: 'Ada (research agent)',
	subject: 'Your x402 settle path',
	message: 'Two questions about the facilitator, happy to pay for the time.',
	fetchWithPayment,
	maxPriceAtomics: 100_000n,  // never spend more than $0.10
	requestId: 'ada-2026-08-28-001',
});

console.log(result.paid);         // "$0.05"
console.log(result.announced);    // true when it cleared their interrupt bar
console.log(result.receipt_url);

maxPriceAtomics is checked against the door's live price before any payment is attempted. Set it on every unattended knock.

requestId is an idempotency key. Retrying with the same one returns the first knock instead of knocking (and paying) twice.

Read the answer

A knock returns a receipt URL. It carries its own proof, so you can read what became of your message without an account here:

import { receipt } from '@three-ws/knock';

const state = await receipt(result.receipt_url);
// { status: 'replied', reply: 'Ask away.', seen: true, amount: '$0.05', … }

Statuses: pending, read, replied, dismissed. The reply is the only thing about the recipient the receipt ever exposes.

Browse who is reachable

import { directory } from '@three-ws/knock';

for (const door of await directory({ limit: 20 })) {
	console.log(door.price, '@' + door.handle, door.headline ?? '');
}

Spending is always a deliberate act

An agent must never spend on a person's behalf by accident. confirmationFor() returns the four facts a human needs before approving a payment, so a CLI, a chat agent and a UI can all show the same thing:

import { quote, confirmationFor } from '@three-ws/knock';

const c = confirmationFor(await quote('nirholas'));
// {
//   recipient: 'nirholas (@nirholas)',
//   amount:    '$0.05',
//   token:     'USDC',
//   chains:    ['solana'],
//   endpoint:  'https://three.ws/api/x402/knock?to=nirholas',
//   note:      'The payment settles directly to the recipient.'
// }

The CLI below prints exactly this and stops for a yes.


CLI

npx @three-ws/knock quote nirholas
npx @three-ws/knock directory --limit 20

npx @three-ws/knock send nirholas "Two questions about the facilitator." \
  --from "Ada" --subject "Your x402 settle path"

npx @three-ws/knock receipt "https://three.ws/api/knock/reply?id=…&token=…"

A free door sends immediately. A priced door prints the recipient, the amount, the token and the chain, then waits for a y. With no TTY it refuses instead of assuming consent; pass --yes only when a human already approved the amount.

Paying needs an x402 client, supplied with --payer <module>. The module is imported and must export fetchWithPayment (or a default that is a function):

// payer.mjs
import { wrapFetchWithPayment } from 'x402-fetch';
import { wallet } from './my-wallet.mjs';

export const fetchWithPayment = wrapFetchWithPayment(fetch, wallet);
npx @three-ws/knock send nirholas "…" --from "Ada" --payer ./payer.mjs --yes

Add --json to any command for machine-readable output.


API

| Export | What it does | | --- | --- | | quote(handle, opts?) | The public door: price, chains, length limit, endpoint. No payment. | | directory(opts?) | Every open, listed door, cheapest first. | | knock(opts) | Send one message. Free doors need nothing; priced doors need fetchWithPayment. | | receipt(url, opts?) | What became of a knock you sent. No account needed. | | confirmationFor(door) | The recipient/amount/token/chain facts to confirm before spending. | | formatUsdc(atomics) | Atomic USDC to a price string, the same way the API renders it. | | KnockError | Every failure. Carries code, status, and the server's data. |

knock(opts)

| Option | Type | Notes | | --- | --- | --- | | to | string | Recipient handle, with or without the @. | | from | string | Who is knocking. Shown and spoken. Required. | | message | string | The body. Shown in full, never read aloud. Required. | | subject | string | One line their companion says out loud. | | url | string | An http(s) link about you. | | senderKind | 'agent' \| 'human' \| 'unknown' | Self-declared, shown in the inbox. Defaults to agent. | | requestId | string | Idempotency key. | | maxPriceAtomics | string \| number \| bigint | Hard ceiling, checked before paying. | | fetchWithPayment | (url, init) => Promise<Response> | Required for priced doors. | | origin | string | Defaults to https://three.ws. |

Error codes

| Code | Meaning | | --- | --- | | bad_handle | The handle could never be one. | | no_door | Nobody is answering there. | | door_closed | Shut right now (also what a blocked sender sees). | | door_full | The door hit its daily cap. Nothing was charged. | | message_too_long / message_too_short | Against that door's limits. | | over_budget | Above the maxPriceAtomics you set. Nothing was paid. | | payment_required | A priced door and no fetchWithPayment. |

Every refusal happens before the 402 is issued, so a knock that was never going to land is never a knock you paid for.


Related

License

Apache-2.0