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

@stdiobus/node

v2.1.3

Published

Native Node.js binding for stdio_bus — AI agent transport layer. Prebuild binaries, Docker fallback.

Readme

Installation

npm install @stdiobus/node

Prebuilt native binaries are included. No C compiler or build tools required.

Requirements:

  • Node.js >= 18.0.0
  • macOS (x64, arm64) or Linux (x64, arm64) for native backend
  • Docker (optional) — required only on Windows or unsupported platforms

Quick Start

const { StdioBus } = require('@stdiobus/node');

const bus = new StdioBus({
  config: {
    pools: [{
      id: 'echo',
      command: 'node',
      args: ['./workers/echo-worker.js'],
      instances: 1,
    }],
  },
});

await bus.start();

const result = await bus.request('echo', { message: 'hello' });
console.log(result);

await bus.stop();
bus.destroy();

TypeScript

The package ships with full type declarations.

import { StdioBus, BusState } from '@stdiobus/node';
import type { StdioBusOptions, BusStats } from '@stdiobus/node';

const options: StdioBusOptions = {
  config: {
    pools: [{
      id: 'worker',
      command: 'node',
      args: ['./worker.js'],
      instances: 2,
    }],
    limits: {
      max_input_buffer: 1_048_576,
      max_output_queue: 4_194_304,
      max_restarts: 5,
      restart_window_sec: 60,
    },
  },
};

const bus = new StdioBus(options);

try {
  await bus.start();

  bus.onMessage((msg: string) => {
    console.log('Received:', msg);
  });

  const result = await bus.request('tools/list', {}, { timeout: 10_000 });
  console.log('Tools:', result);
} finally {
  await bus.stop();
  bus.destroy();
}

Use Cases

ACP Agent

For typed streaming events, use @stdiobus/agentic with promptStream() or prompt(). The example below shows the low-level transport approach.

const { StdioBus } = require('@stdiobus/node');

const bus = new StdioBus({
  config: {
    pools: [{
      id: 'acp-worker',
      command: 'node',
      args: ['./workers/acp-worker.js'],
      instances: 1,
    }],
  },
});

await bus.start();

const init = await bus.request('initialize', {
  protocolVersion: 1,
  clientInfo: { name: 'my-app', version: '1.0.0' },
  clientCapabilities: {},
}, { timeout: 60_000 });

const session = await bus.request('session/new', {
  cwd: process.cwd(),
  mcpServers: [],
});

const result = await bus.request('session/prompt', {
  sessionId: session.sessionId,
  prompt: [{ type: 'text', text: 'What is 2+2?' }],
});

console.log('Response:', result);
await bus.stop();
bus.destroy();

MCP Tools

const bus = new StdioBus({
  config: {
    pools: [{
      id: 'mcp-tools',
      command: 'node',
      args: ['./workers/mcp-tools-worker.js'],
      instances: 2,
    }],
  },
});

await bus.start();

const tools = await bus.request('tools/list');
const output = await bus.request('tools/call', {
  name: 'search_docs',
  arguments: { query: 'retry policy' },
});

console.log('Tools:', tools);
console.log('Output:', output);

await bus.stop();
bus.destroy();

TCP Server

Accept external client connections over TCP:

const port = Number(process.env.PORT) || 8080;

const bus = new StdioBus({
  config: {
    pools: [{
      id: 'worker',
      command: 'node',
      args: ['./worker.js'],
      instances: 4,
    }],
  },
  listenMode: 'tcp',
  tcpHost: '0.0.0.0',
  tcpPort: port,
});

await bus.start();
console.log(`Listening on TCP port ${port}`);
// Clients connect and send NDJSON: nc localhost 8080

Docker Backend

On Windows or when native binaries are unavailable, the SDK runs stdio Bus inside a Docker container and communicates over TCP:

const bus = new StdioBus({
  configPath: './config.json',
  backend: 'docker',
  docker: {
    image: 'stdiobus/stdiobus:node20',
    pullPolicy: 'if-missing',
    startupTimeoutMs: 15_000,
  },
});

Set backend: 'auto' (default) to use native when available, Docker otherwise.

Platform Support

| Platform | Architecture | Native | Docker | |----------|-------------|--------|--------| | macOS | x64 | ✓ | ✓ | | macOS | arm64 | ✓ | ✓ | | Linux | x64 | ✓ | ✓ | | Linux | arm64 | ✓ | ✓ | | Windows | x64 | — | ✓ |

API Reference

Constructor

new StdioBus(options: StdioBusOptions)

| Option | Type | Default | Description | |--------|------|---------|-------------| | configJson | object | — | Programmatic config (recommended) | | configPath | string | — | Path to JSON config file | | backend | 'auto' \| 'native' \| 'docker' | 'auto' | Backend selection | | listenMode | 'none' \| 'tcp' \| 'unix' | 'none' | External listener mode | | tcpHost | string | '127.0.0.1' | TCP bind address | | tcpPort | number | — | TCP port (required for tcp mode) | | unixPath | string | — | Unix socket path (required for unix mode) | | logLevel | number | 1 | 0=DEBUG, 1=INFO, 2=WARN, 3=ERROR | | pollIntervalMs | number | 10 | Native backend poll interval | | docker | DockerOptions | — | Docker backend configuration |

configJson and configPath are mutually exclusive. One is required.

Lifecycle

| Method | Returns | Description | |--------|---------|-------------| | start() | Promise<void> | Start bus and spawn workers | | stop(timeoutSec?) | Promise<void> | Graceful shutdown (default: 30s) | | destroy() | void | Release all resources |

Messaging

| Method | Returns | Description | |--------|---------|-------------| | request(method, params?, options?) | Promise<T> | Send request, await response | | send(message) | boolean | Send raw JSON-RPC string | | onMessage(handler) | void | Register message handler |

State

| Method | Returns | Description | |--------|---------|-------------| | getState() | number | Bus state (0–4) | | getStats() | BusStats | Runtime statistics | | getWorkerCount() | number | Running workers | | getClientCount() | number | Connected clients (TCP/Unix) | | getBackendType() | 'native' \| 'docker' | Active backend | | getListenMode() | string | Listen mode | | isRunning() | boolean | Convenience check |

Constants

import { BusState, ListenMode, BackendMode } from '@stdiobus/node';

BusState.CREATED   // 0
BusState.STARTING  // 1
BusState.RUNNING   // 2
BusState.STOPPING  // 3
BusState.STOPPED   // 4

ListenMode.NONE    // 'none'
ListenMode.TCP     // 'tcp'
ListenMode.UNIX    // 'unix'

BackendMode.AUTO   // 'auto'
BackendMode.NATIVE // 'native'
BackendMode.DOCKER // 'docker'

Known Behavior

  • Workers that crash beyond max_restarts within restart_window_sec are not restarted.
  • stop() sends SIGTERM to workers and waits up to timeoutSec for graceful exit.
  • request() correlates responses by JSON-RPC id. Each request gets a unique ID.
  • In embedded mode (listenMode: 'none'), messages flow through send() / onMessage().
  • In TCP/Unix modes, external clients connect and send NDJSON directly.
  • configJson is serialized to a temp file internally, cleaned up on destroy().
  • Always call destroy() after stop() to release native resources and clean up temp files.

Development

npm install                # install dependencies
npm run build              # esbuild (JS) + tsc (declarations)
npm run typecheck          # type-check without emit
npm run test:e2e           # npm pack → install → verify on macOS + Docker Linux
npm run test:e2e:native    # macOS only
npm run test:e2e:docker    # Docker Linux only

Build output:

out/
  dist/index.js       # CJS bundle (esbuild, minified)
  tsc/*.d.ts          # Type declarations (tsc)

Contributing

  1. Open an issue describing the change before submitting a PR.
  2. All PRs must include tests covering the change.
  3. Run npm run typecheck && npm run test:e2e before submitting.

Security

To report a security vulnerability, email [email protected]. Do not open a public issue.

License

Apache-2.0