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

@microboxlabs/miot-harness-client

v0.2.0

Published

A zero-runtime-dep, typed HTTP + SSE client for the [`miot-harness`](../../../miot-harness) streaming API. Sibling of [`@microboxlabs/miot-calendar-client`](../miot-calendar-client) — same conventions, same tooling, different backend.

Readme

@microboxlabs/miot-harness-client

A zero-runtime-dep, typed HTTP + SSE client for the miot-harness streaming API. Sibling of @microboxlabs/miot-calendar-client — same conventions, same tooling, different backend.

Role in the ecosystem

This package is the one place where the harness HTTP + SSE contract is described in TypeScript. Every consumer — terminal, web, future tools — imports from here so they share the same types, the same error class, and the same wire-format guarantees.

          miot-harness backend (FastAPI)
          REST  POST /runs:start
          SSE   GET  /runs/{id}/stream
          REST  GET  /runs/{id}
                     ▲
                     │  HTTP + SSE (fetch)
       ┌──────────────────────────────────┐
       │   @microboxlabs/                  │  zero runtime deps
       │   miot-harness-client             │  dual ESM (.mjs) + CJS (.cjs)
       │   (typed, hand-written, this pkg) │  shared by every consumer
       └──────────────────────────────────┘
                ▲             ▲              ▲
                │             │              │
   ┌──────────────────┐  ┌─────────────┐  ┌──────────────────┐
   │ @microboxlabs/   │  │ @microboxlabs/│  │  turbo-repo/    │
   │ miot-chat        │  │ miot-cli     │  │  apps/app       │
   │ live SSE REPL    │  │ scripting    │  │  (future)       │
   │ + slash commands │  │ create / get │  │  Next.js admin  │
   └──────────────────┘  └─────────────┘  └──────────────────┘

| Consumer | Role | Status | |---|---|---| | @microboxlabs/miot-chat | Interactive Copilot-style REPL — owns the live SSE UX, slash commands, conversation memory. | ✅ Consumes this library | | @microboxlabs/miot-cli | Operator CLI — miot harness create / miot harness runs get for scripting; no live streaming UX. | ✅ Consumes this library | | turbo-repo/apps/app | Next.js admin — can consume from server-side API routes the same way it consumes @microboxlabs/miot-calendar-client. | 🔜 Future |

Why a separate library (and not a folder inside miot-chat)

  • Single source of truth. Anytime the backend contract evolves, exactly one TypeScript surface needs to change. Admins, CLIs, agents, and future automation cannot drift apart on event shapes or error codes.
  • Two surfaces, one engine. The interactive REPL and the scriptable miot harness … subcommands share validation, error class, and request plumbing — a bug fixed inside this library is fixed everywhere at once.
  • Independent release cadence. Bump the CLI without touching the library, or ship a new library version and let consumers adopt it on their own schedule.

This is the same shape that @microboxlabs/miot-calendar-client has with miot-cli + apps/app — applied to the harness's streaming surface.

Install

Workspace consumers reference it as "@microboxlabs/miot-harness-client": "*". After publish:

npm install @microboxlabs/miot-harness-client

Authentication

In production the harness sits behind the Quarkus reverse proxy at /api/v1/orgs/{slug}/harness and rejects unauthenticated /runs* requests. The Quarkus side terminates the user-facing Auth0 token and resolves the caller's tenant from Alfresco; the harness re-verifies the token as defense in depth. See the Authentication doc for the full trust model.

Practical implications for this client:

  • Point baseUrl at the proxy: https://api.miot.example.com/api/v1/orgs/{slug}/harness. Hitting the harness directly only works for local dev, where the harness defaults to its legacy unauthenticated mode.
  • token is the Auth0 access token from the rest of the MIOT API. It must carry the harness audience and be unexpired; the client sends it as Authorization: Bearer <token>. With auth enabled on the harness, missing or invalid tokens surface as MiotHarnessApiError with code: "http_401".
  • tenant_id in the request body is ignored in production — the proxy injects an X-Miot-Tenant-Client-Id header from the verified org membership, and the harness uses that value. The field is kept on the wire for now to preserve local-dev ergonomics; it will be removed from the schema in a follow-up release (issue #522).

Quickstart

import {
  createMiotHarnessClient,
  MiotHarnessApiError,
} from "@microboxlabs/miot-harness-client";

const client = createMiotHarnessClient({
  // Production: hit the Quarkus proxy for the target org.
  // Local dev: "http://localhost:8000" hits the harness directly
  // (legacy unauthenticated mode).
  baseUrl: "https://api.miot.example.com/api/v1/orgs/gama-mobility/harness",
  token: process.env.MIOT_HARNESS_TOKEN, // Auth0 access token
});

// 1. Dispatch a run (async; returns immediately with the id).
//    `tenant_id` is overridden server-side from the verified
//    proxy header — pass anything in dev, ignore in prod.
const { run_id } = await client.runs.create({
  message: "what's in stock?",
  mode: "agentic",
  conversation_id: crypto.randomUUID(),
});

// 2. Stream events as they arrive (SSE)
try {
  for await (const evt of client.runs.stream(run_id)) {
    console.log(evt.type, evt.message);
    if (evt.type === "run.completed" || evt.type === "run.failed") break;
  }
} catch (e) {
  if (e instanceof MiotHarnessApiError) {
    console.error(`harness error: ${e.code} — ${e.message}`);
  } else {
    throw e;
  }
}

// 3. Fetch the authoritative record for the final answer
const record = await client.runs.get(run_id);
console.log(record.answer);

API surface

createMiotHarnessClient(config: ClientConfig) => MiotHarnessClient

| Group | Method | HTTP | Notes | |---|---|---|---| | runs | create(body, opts?) | POST /runs:start | Returns { run_id } (the harness assigns the id). opts.signal is forwarded to fetch. | | runs | stream(id, opts?) | GET /runs/{id}/stream | AsyncIterable<HarnessEvent>. Pass opts.lastEventId for resume; pass opts.signal to cancel. Throws MiotHarnessApiError on event: error frames. | | runs | get(id) | GET /runs/{id} | Full HarnessRunRecord with events[], answer, artifacts, conversation_id. |

ClientConfig

| Field | Type | Default | |---|---|---| | baseUrl | string | required. In prod set it to the Quarkus proxy path: …/api/v1/orgs/{slug}/harness | | token | string \| null | undefined — no Authorization header sent. Required when targeting an auth-enabled harness (any production deployment); send the Auth0 access token from the rest of the MIOT API | | headers | Record<string, string> | merged into every request (request-level headers win) | | fetch | typeof globalThis.fetch | globalThis.fetch — pass a custom impl for testing or polyfills |

Errors

Every failure surfaces as MiotHarnessApiError:

class MiotHarnessApiError extends Error {
  readonly code: string;        // "http_404" | "unknown_run_id" | "no_body" | ...
  readonly runId?: string;      // set for stream/getRun errors
  readonly body?: ErrorResponse | string;
  readonly status?: number;     // set for HTTP-origin errors
}
  • HTTP non-2xx → code = http_<status>, status set, body parsed (JSON) or string fallback.
  • SSE event: error frame → code = whatever the harness sent (e.g. unknown_run_id), runId set, no status.
  • Stream body missing → code = no_body.
  • Unparseable SSE payload → code = unparseable_event or unparseable_error.

Error.message is derived from body.message ?? body.error ?? body.detail so harness, FastAPI, and plain-text errors all surface a useful string.

Develop

npm run build         # tsup, dual ESM (.mjs) + CJS (.cjs), dts on both
npm test              # vitest
npm run lint
npm run check-types
npm pack --dry-run    # confirm shape

Design notes

  • Zero runtime dependencies. fetch, ReadableStream, TextDecoder, URL are native on Node ≥18 and any modern browser.
  • Dual ESM + CJS build with conditional exports so the package works under both import and require without type: "module".
  • SSE parser is a pure async iterator over ReadableStream<Uint8Array> — survives chunk-boundary splits, multi-line data: fields, \r\n, leading-space stripping after :, comment lines, and the harness's event: error close frame.
  • Resource grouping (runs.create / .stream / .get) mirrors @microboxlabs/miot-calendar-client's calendars.* / slots.* / bookings.* so future harness resources (conversations, artifacts, etc.) drop in naturally.