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

harnessrouterjs

v0.1.0

Published

JavaScript client and CLI for any server that speaks the Unified Harness Protocol: run agent tasks, stream progress, continue sessions, cancel work, collect artifacts.

Readme

harnessrouterjs

A JavaScript client and CLI for any server that speaks the Unified Harness Protocol.

A harness is a configured agent: a runtime, a model, instructions and limits. A task is one run of it — a real conversation against a real workspace, streamed back as it happens. This client sends work, follows it, continues the conversation, cancels it, and collects the files it produced.

It is a UHP client, not a client for one product. It works against the open-source HarnessRouter server, the hosted one, or any third implementation that passes the conformance suite.

npm install harnessrouterjs

Run a task

import { HarnessRouter } from 'harnessrouterjs';

const client = new HarnessRouter({
  baseUrl: 'http://localhost:3000',
  apiKey: process.env.UHP_API_KEY,
});

const [harness] = await client.harnesses();
const response = await client.run('Summarise README.md in three bullets.', {
  harnessId: harness.id,
});

response.status;   // 'completed'
response.output;   // ordered items: message, reasoning, function_call, function_call_output

Follow it while it happens

Agent tasks run for minutes. Without a stream a product can only show a spinner and hope.

for await (const event of client.stream('Add tests for the parser.', { harnessId })) {
  if (event.type === 'response.output_text.delta') process.stdout.write(event.delta);
}

Or let the client assemble it for you:

const task = await client.collect('Add tests for the parser.', { harnessId });
task.text;         // the finished text
task.toolCalls;    // what the agent ran
task.annotations;  // the files it wrote
task.sequence;     // { ok: false, missing: [12] } if the stream dropped an event

Continue, cancel, collect

const next = await client.continue(response.id, 'Now add tests for the function you just wrote.');
// same session, same working directory, same files

await client.cancel(next.id);              // a request to stop; the session stays continuable
const files = await client.sessionFiles(next.metadata.session_id);
const body = await client.downloadFile(files[0].container_id, files[0].id);

What it does for you

  • Idempotency by default. Every run carries an Idempotency-Key, so a retry cannot start a second agent against the same files.
  • Retries only what is worth retrying. session_busy and 503s back off and try again; quota_exhausted and every malformed request do not. A POST /v1/responses that may already have started is never resent on the client's own initiative.
  • Errors you can branch on. UhpError carries the server's type, code, param and detail, plus a retryable getter — no parsing prose to decide what to do.
  • Long timeouts. 15 minutes by default, because a 30-second HTTP timeout cancels healthy work.

CLI

export UHP_BASE_URL=http://localhost:3000
export UHP_API_KEY=…

npx harnessrouter discover                       # what the server is, and what it supports
npx harnessrouter harnesses                      # chrn_…  Research agent  [claude-code]
npx harnessrouter stream "Summarise README.md" --harness chrn_…

API

| Method | What it does | |---|---| | discover() | The server's version, conformance class and capabilities (no token needed) | | harnesses(), harness(id) | Configured harnesses in your scope | | models(), harnessModels(id) | Model catalogues, with a computed available | | run(input, opts) | Run a task and wait for the result | | stream(input, opts) | Run a task and iterate its events | | collect(input, opts) | Run, stream, and return the accumulated task | | continue(responseId, input) | Continue the session that produced that response | | getResponse(id), inputItems(id), wait(id) | Read a task back, or poll it to a terminal state | | cancel(id), deleteResponse(id) | Stop work; delete history. Deliberately different things | | sessions(), session(id), turns(id), sessionFiles(id) | Session listing and inspection | | uploadFile(file, name), downloadFile(containerId, fileId) | Files in, artifacts out |

Specification

https://github.com/HarnessRouter/harnessrouter/tree/main/protocol

Apache-2.0.