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

@projectpac/flow-joint-search

v0.6.0

Published

Part of PAC: @projectpac/flow-joint-search.

Readme

@projectpac/flow-joint-search

Two principals have a question neither one's data can answer alone, and neither will hand their data to the other.

This is the flow the rest of PAC exists for, and it negotiates twice.

First over what to compute. Each side sees only the other's descriptor -- source names and a sentence each, never contents -- and the two agents write objectives into a shared versioned resource: one suggests, the other reads the diff and suggests back, until one agrees. An objective names, per node, exactly which sources it may read, what the terms in it mean, the rules a program must follow, and what may and may not come out.

Then over how. The same machinery produces program.py -- real Python defining run(parties), which is what the box's runner executes -- and both sides approve those exact bytes. The instructions carry the box's own execution contract: the party order, the shape each party's input arrives in, the modules that may be imported, the bounds, and the rule that nothing may depend on the clock or randomness. Only then does anything private move, and it moves into a box both sides verified, carrying exactly the sources the agreed objective named.

Everything hard here belongs to a component: the versioned resource and its bundles are the artifact store's, the runs and their output contract the loop's, the sessions and delivery the adapter's, and every step that touches material is an apply. What is left is the state machine -- who initiates, what each side owes next, and when a negotiation has gone on long enough.

And what the flow says about itself, it says itself. It declares its route to the network, advertises that this node runs it under kind: "flow" -- which is also how it finds its peers: a node that advertises joint-search under that kind runs it, and nothing else can answer -- declares its search intent to the router from an optional scope, so a node with no router still runs it, keeps its records through ctx.records, and runs its own timers. The host says none of it.

| | | | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | routes | POST /queries, GET /queries, GET /tasks, GET /works, GET /descriptor | | payloads | probe.* (this flow's), plan.* and program.* (the rounds' wire), work.* (the box drive's), task.failed | | records | queries, verdicts, tasks, works: the state machine's memory, kept through ctx.records | | background | publish 30s, triage 5s, pair 5s, advance 2s, sweep 30s, each on an interval of its own | | needs | the sources named in sources -- which of their offers is the negotiation's own answer -- and a box adapter, whose egress allowlist is what this node will let a run reach | | intents | search, declared to the router from an optional scope; not the fallback, and publishing a query needs nobody to have offered anything first | | results | one search per task, carrying its works' summaries and outputs, declared to ctx.results from an optional scope: GET /flows/results/results reads them beside every flow's |

Roles are elected from the two node ids, so both sides derive the same initiator without being told. Seven files: pairing.ts is everything before a negotiation, negotiation.ts the three engines' specs, runs.ts what each kind of answer means, prompts.ts what each run is asked (and the box contract), state.ts the durable records, flow.ts the manifest, the config and the handle, index.ts the wiring and the routes.

Running it live needs a real box, because the in-process double cannot join two processes: see devtools/README.md. The scripted path is tests/joint-search.spec.ts, which is also the reference for what a negotiation script looks like.

Running it

pnpm check           # includes tests/joint-search.spec.ts: the scripted negotiation
just demo            # headless floor: a directory, two seeded nodes, alike, down
MODEL=pi just dev    # the real lane: panes, sources, and the box -- then, in another terminal:
just query "find the spotify artists we both listen to"

just dev owns the terminal it runs in, so the query goes in another one, once the seed pane says joint search seeded. just a1 api GET /flows/joint-search/works shows every stage on the way to done.

The sibling layout

Cross-repo dependencies are link: paths into checkouts beside this one -- nothing of PAC's is fetched from a registry. Clone the repos this one links (the link: values in the package manifests) into the same parent directory, run pnpm install, and everything resolves from source. The flow's three runtime dependencies beyond the sdk -- the negotiation engine, prompting, and plugin-lib -- are pinned by version and redirected to the checkouts by pnpm-workspace.yaml. The flow's whole-node suite lives in tests/, standing up real nodes from the sibling pac-node. The design repo (pac) is the front door: what PAC is, and how the repos fit together.