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

@microsandbox/agent-client

v0.5.7

Published

Low-level TypeScript client for speaking the microsandbox agent protocol from Node.js or browser/front-end runtimes.

Readme

@microsandbox/agent-client

Low-level TypeScript client for speaking the microsandbox agent protocol from Node.js or browser/front-end runtimes.

This package sits below the high-level microsandbox SDK. It owns the transport connection, relay handshake, correlation ID allocation, request/stream routing, message framing, protocol-version gating, and typed/encoded message helpers. It does not create sandboxes, resolve sandbox names, pull images, manage volumes, or expose high-level process/filesystem convenience APIs.

Use this package when you already have an agent relay endpoint and want direct protocol access from TypeScript. Use a higher-level microsandbox SDK when you want sandbox lifecycle management.

Install

npm install @microsandbox/agent-client

Node.js 22 or newer is required.

Entry Points

The default entry is browser-safe and does not import Node builtins:

import {
  AgentClient,
  WebSocketTransport,
  typedMessage,
} from "@microsandbox/agent-client";

Unix domain sockets are Node-only and live behind a separate entry:

import { connectUnix } from "@microsandbox/agent-client/node";

This split matters for front-end builds: importing from @microsandbox/agent-client is browser-safe, while @microsandbox/agent-client/node imports Node's net module.

Protocol Model

The agent protocol is a length-prefixed binary frame:

[len: u32 BE][id: u32 BE][flags: u8][CBOR Message body]

The CBOR Message body contains:

{ v, t, p }
  • v: protocol generation.
  • t: wire message type such as "core.exec.request".
  • p: CBOR-encoded payload for that message type.

AgentClient owns correlation IDs from the relay-assigned range. Callers pass typed or already-encoded messages; the client computes flags, gates unsupported message types against the negotiated protocol generation, frames messages, and routes responses by ID.

The relay handshake happens before regular frames:

[id_min: u32 BE][id_max: u32 BE][core.ready packet]

id_min..id_max is the correlation ID range reserved for this client connection. core.ready advertises the agent protocol generation and runtime metadata; the client uses it to negotiate the effective protocol version.

Payload objects passed to typedMessage() must match the CBOR schema expected by microsandbox-protocol for the selected message type. This package validates message type support and frame shape, but it does not yet ship generated domain payload types.

Node UDS Example

import { connectUnix } from "@microsandbox/agent-client/node";
import { typedMessage } from "@microsandbox/agent-client";

const client = await connectUnix("/tmp/msb-agent.sock", {
  handshakeTimeoutMs: 10_000,
});

const response = await client.request(
  typedMessage("core.fs.request", {
    op: {
      Stat: {
        path: "/etc/os-release",
        follow_symlink: true,
      },
    },
  }),
);

if (response.type === "core.fs.response") {
  console.log(response.decodePayload());
}
await client.close();

Browser WebSocket Example

import {
  AgentClient,
  WebSocketTransport,
  typedMessage,
} from "@microsandbox/agent-client";

const transport = await WebSocketTransport.connect(
  "wss://relay.example.com/agent",
);
const client = await AgentClient.connectTransport(transport);

const stream = await client.openStream(
  typedMessage("core.exec.request", {
    cmd: "sh",
    args: ["-lc", "echo hello"],
  }),
);

for await (const frame of stream) {
  if (frame.type === "core.exec.stdout") {
    console.log(frame.decodePayload());
  }
  if (frame.type === "core.exec.exited") break;
}

await client.close();

Browsers cannot dial Unix domain sockets directly. A front-end integration needs a WebSocket relay endpoint that forwards binary agent protocol packets to the runtime-side agent relay.

Typed And Encoded Messages

Use typedMessage() when this package should CBOR-encode the payload:

await client.request(
  typedMessage("core.fs.request", {
    op: { List: { path: "/" } },
  }),
);

Use encodedMessage() when another layer already produced CBOR payload bytes:

await client.request(
  encodedMessage("core.fs.request", payloadBytes),
);

Both forms still include a message type so the client can compute flags and fail fast when the connected peer does not support that message type.

encodedMessage() expects only the CBOR payload bytes for the message type, not the outer { v, t, p } envelope and not the length-prefixed transport frame. The client builds the envelope and frame after it assigns the correlation ID.

Streams

openStream() starts a session and returns an AgentStream:

const stream = await client.openStream(
  typedMessage("core.exec.request", { cmd: "cat" }),
);

await stream.send(
  typedMessage("core.exec.stdin", {
    data: new TextEncoder().encode("hello\n"),
  }),
);

const frame = await stream.next(5_000);

Streams are async iterable:

for await (const frame of stream) {
  if (frame.isTerminal()) break;
}

Protocol Errors

Peers may send core.error as a terminal response when they can recover from a message-level protocol problem for a specific correlation ID. The client surfaces it as an ordinary InboundFrame:

const frame = await client.request(message);
if (frame.type === "core.error") {
  const err = frame.decodePayload<{
    kind: string;
    message: string;
    offending_type?: string;
  }>();
  console.error(err.message);
}

Frame-level transport corruption still closes the connection instead.

TransportPacket Escape Hatch

TransportPacket represents exact wire bytes, including the length prefix. Use writeUnchecked() only for relays, tests, and specialized protocol tooling:

await client.writeUnchecked(TransportPacket.fromBytes(bytes));

Ordinary callers should use request(), openStream(), and stream.send().

Validation

Focused package checks:

npm run typecheck
npm run build
npm test