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

@j1nn0/herdr-plugin-sdk

v0.3.0

Published

Unofficial TypeScript SDK and toolkit for building, testing, and integrating Herdr plugins.

Readme

@j1nn0/herdr-plugin-sdk

Typed building blocks for Herdr Plugin v1.

npm version CI MIT License Node >= 22

Unofficial community project: This project is not affiliated with or endorsed by the Herdr project.

Unofficial TypeScript tooling for Herdr Plugin v1 that removes repetitive runtime, CLI, error-handling, and testing boilerplate. Herdr's official Plugin API remains command-based: a herdr-plugin.toml manifest and ordinary executable commands that Herdr launches. This package is a convenience layer over that model.

import { createHerdrClient, readPluginRuntime } from '@j1nn0/herdr-plugin-sdk';

const runtime = readPluginRuntime();
const workspaces = await createHerdrClient().workspace.list();
console.log(`${runtime.pluginId}: ${workspaces.length} workspace(s)`);
  • Typed Herdr CLI client for agent, pane, plugin pane, workspace, and tab operations.
  • Plugin runtime and context parsing from Herdr's environment.
  • Typed narrowing for supported plugin events.
  • Structured Herdr errors for environment, response, process, timeout, and CLI failures.
  • Generic run() for commands without a typed client method yet.
  • Test Herdr plugins without running Herdr with fixtures and an in-memory client.
  • Zero runtime dependencies in the SDK itself.

Quick Start

Install the SDK in a TypeScript plugin:

pnpm add @j1nn0/herdr-plugin-sdk

Herdr still launches ordinary executable commands declared in herdr-plugin.toml; the SDK does not change that command-based model.

An event command can validate and narrow a payload, then apply the plugin's own policy:

import {
  createHerdrClient,
  isPaneAgentStatusChanged,
  readPluginEvent,
} from '@j1nn0/herdr-plugin-sdk';

const event = readPluginEvent();
if (event !== null && isPaneAgentStatusChanged(event)) {
  if (event.data.agent_status === 'done') {
    const output = await createHerdrClient().pane.read(event.data.pane_id, {
      source: 'recent',
      lines: 20,
    });
    console.log(output);
  }
}

Plugin runtime

Herdr supplies the plugin environment variables when it launches a command. readPluginRuntime validates the required variables and normalizes the invocation into one of the real invocation kinds: action, event, startup, pane, or unknown.

import { readPluginRuntime } from '@j1nn0/herdr-plugin-sdk';

const { pluginId, invocation } = readPluginRuntime();

switch (invocation.kind) {
  case 'action':
    console.log(pluginId, invocation.actionId, invocation.clickedUrl);
    break;
  case 'event':
    console.log(pluginId, invocation.event);
    break;
  case 'startup':
    console.log(pluginId, 'startup');
    break;
  case 'pane':
    console.log(pluginId, invocation.entrypointId);
    break;
  case 'unknown':
    console.log(pluginId, 'unknown invocation');
    break;
}

Context and events

readPluginContext reads HERDR_PLUGIN_CONTEXT_JSON, while readPluginEvent reads the optional HERDR_PLUGIN_EVENT_JSON envelope. isPaneAgentStatusChanged narrows an event after its required fields have been checked. Explicit null on optional context fields is treated as absent.

Only an event-hook command receives HERDR_PLUGIN_EVENT_JSON, so readPluginEvent returns null when the variable is absent. A variable that is present but empty is a broken runtime boundary rather than "not an event hook", and throws HerdrEnvError.

import {
  isPaneAgentStatusChanged,
  readPluginContext,
  readPluginEvent,
} from '@j1nn0/herdr-plugin-sdk';

const context = readPluginContext();
console.log(context.workspace_id, context.focused_pane_id);

const event = readPluginEvent();
if (event !== null && isPaneAgentStatusChanged(event)) {
  const status = event.data.agent_status;
  console.log(event.data.pane_id, status);

  // The SDK applies no policy. `done` means nothing to the SDK;
  // deciding what to do is the plugin's job.
}

The SDK does not decide whether an agent_status of done should trigger an action, notification, or exit. Plugins decide what event data means for their own behavior.

CLI client

createHerdrClient exposes typed agent, pane, plugin.pane, workspace, and tab operations. Read output is returned as a string.

import {
  createHerdrClient,
  type ReadOptions,
} from '@j1nn0/herdr-plugin-sdk';

const client = createHerdrClient();
const readOptions: ReadOptions = {
  source: 'recent-unwrapped',
  lines: 80,
  format: 'text',
};

const agent = await client.agent.get('agent-target');
const agentText = await client.agent.read('agent-target', readOptions);
const pane = await client.pane.get('workspace:pane');
const paneText = await client.pane.read('workspace:pane', {
  source: 'recent-unwrapped',
});
const panes = await client.pane.list({ workspaceId: 'workspace' });
const workspaces = await client.workspace.list();
const tabs = await client.tab.list({ workspaceId: 'workspace' });
const renamedTab = await client.tab.rename('workspace:tab', 'My tab');
const renamedWorkspace = await client.workspace.rename('workspace', 'My workspace');

const pluginPane = await client.plugin.pane.open({
  pluginId: 'example.plugin',
  entrypoint: 'widget',
  placement: 'overlay',
  focus: false,
});
await client.pane.reportMetadata(pluginPane.pane_id, {
  source: 'plugin:example.plugin',
  tokens: { status: 'ready' },
});
await client.plugin.pane.close(pluginPane.pane_id);

console.log(agent, agentText, pane, paneText, panes, workspaces, tabs, renamedTab, renamedWorkspace);

plugin.pane.open accepts the six PluginPanePlacement contract values (overlay, popup, split, tab, zoomed, and fullscreen). The CLI's --help output can under-report supported placements; the operation contract is described by herdr api schema --json. The SDK does not set focus unless focus is provided: true emits --focus, false emits --no-focus, and omission leaves the CLI default in effect. The CLI and socket APIs have different focus defaults, so the CLI default is intentionally preserved. Width and height are not typed options; pass supported sizing flags through run() when needed.

pane.reportMetadata treats a zero exit code as success and ignores successful stdout. ttlMs must be an integer from 1 through 86400000. The SDK rejects blank source values but passes blank titles and empty token values through unchanged. Token syntax/count limits and other server-side metadata limits remain Herdr-owned. Tab and workspace rename labels are passed as one argv token, including empty strings or labels with spaces.

Herdr silently clamps --lines to approximately 1000 rows on the verified Herdr releases. This upstream behavior is undocumented; the SDK intentionally does not reject or normalize larger non-negative integer values, and exposes no SDK clamp constant.

The client also supports timeoutMs, maxBuffer, a custom env, and an executor seam through HerdrClientOptions. Typed operations use a 10,000 ms SDK process timeout by default; run() defaults to 0 (no SDK process timeout). An explicit timeoutMs applies to both. This is separate from Herdr's own --timeout for wait commands; when Herdr reports a wait timeout, it surfaces as HerdrCliError with code timeout.

Generic CLI commands

The Herdr CLI is the official Plugin v1 API. The typed client intentionally starts small. For a command without a typed method, use run(). For example, Herdr 0.9.1 exposes pane layout (herdr pane layout --help), which this client does not model:

import { createHerdrClient } from '@j1nn0/herdr-plugin-sdk';

const herdr = createHerdrClient();
const output = await herdr.run(['pane', 'layout', '--help']);

run() uses the same binary resolution, no-shell execution, buffer limits, and structured error handling as typed methods. Its SDK process timeout defaults to 0 (no timeout); an explicit timeoutMs on createHerdrClient applies to run() as well as typed operations. Successful stdout is returned unchanged. Prefer a typed method when the SDK provides one; use run() otherwise.

Testing without Herdr

You can test Herdr plugin code without a Herdr installation, running server, socket, or subprocess. The testing entrypoint provides an in-memory client and fixtures:

import { readPluginRuntime } from '@j1nn0/herdr-plugin-sdk';
import {
  createAgentFixture,
  createMockHerdrClient,
  createPluginEnvFixture,
} from '@j1nn0/herdr-plugin-sdk/testing';

const client = createMockHerdrClient({
  agents: {
    'workspace:pane': createAgentFixture(),
  },
});

const agent = await client.agent.get('workspace:pane');
const runtime = readPluginRuntime(createPluginEnvFixture());

console.log(agent.agent_status, runtime.pluginId);

createMockHerdrClient records calls and can be configured with agent, pane, pane-list, metadata-reporting, rename, plugin-pane, read, workspace, tab, and generic run() responses. For code that needs lower-level control, createHerdrClient accepts the exported HerdrCommandExecutor seam.

For protocol-shaped client tests, the testing entrypoint provides output envelopes for resource lookups, lists, plugin-pane open/close, and tab/workspace renames, plus a recording executor and a serializer for the CLI trailing newline:

import { createHerdrClient } from '@j1nn0/herdr-plugin-sdk';
import {
  createAgentGetOutputFixture,
  serializeCliOutput,
} from '@j1nn0/herdr-plugin-sdk/testing';

const client = createHerdrClient({
  env: {},
  executor: async () => ({
    stdout: serializeCliOutput(createAgentGetOutputFixture()),
    stderr: '',
    exitCode: 0,
    signal: null,
    timedOut: false,
  }),
});

const agent = await client.agent.get('w1G:p1');

Examples

All three examples are verified in CI against the packed package artifact.

Built with this SDK

Released Herdr plugin that uses this SDK, worth reading as a practical example:

  • herdr-harvest — captures an agent's terminal output when it finishes into a durable Result Inbox.

Errors

HerdrError is the base class. Runtime parsing uses HerdrEnvError when required runtime variables are missing or empty, or when context/event JSON is invalid.

The four command/client error classes are:

  • HerdrCliError is thrown when Herdr exits with a structured CLI error response.
  • HerdrResponseError is thrown when a successful command returns a response with an invalid shape or missing required fields.
  • HerdrProcessError is thrown for a spawn failure, an output-buffer overflow, or an unstructured non-zero process exit.
  • HerdrTimeoutError is thrown when a command exceeds its configured timeout.

An output-buffer overflow is exposed through HerdrProcessError.cause, whose code is ERR_CHILD_PROCESS_STDIO_MAXBUFFER.

A missing agent or pane always throws HerdrCliError; it does not return null. Use isHerdrCliError to handle a stable protocol code:

import {
  createHerdrClient,
  isHerdrCliError,
} from '@j1nn0/herdr-plugin-sdk';

try {
  await createHerdrClient().pane.get('missing-pane');
} catch (error: unknown) {
  if (isHerdrCliError(error, 'pane_not_found')) {
    console.log('The pane is not available.');
  }
}

The corresponding missing-agent code is agent_not_found.

Guarantees

  • Read output is returned exactly as Herdr produced it and is never parsed as JSON.
  • Successful stdout from run() is returned unchanged and is never parsed.
  • Typed resource responses validate the required top-level fields for each operation: identifiers, booleans, integer counters/revisions, and the modeled agent_status values.
  • When present and non-null, agent_session is validated as an object with string source, agent, and value fields plus kind set to id or path. It may contain additional nested fields.
  • Unknown fields in Herdr responses, contexts, and events are preserved and never cause failures. Optional TypeScript-only projections such as name, scroll, and state_labels are not runtime-validated; invalid values for the explicitly validated contracts throw HerdrResponseError.
  • Commands run with a binary plus an argument vector; they never run through a shell.
  • Errors never carry environment contents. HerdrProcessError may include only the truncated stderr diagnostic described by its API; an output-buffer overflow is identified by cause.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER' and does not copy stdout into the error.

Compatibility and design

  • Herdr >= 0.8.2
  • Node.js >= 22
  • CLI-first integration through the Herdr CLI

The client shells out to the Herdr CLI. By default it resolves the binary from HERDR_BIN_PATH (or uses herdr when that variable is not set); createHerdrClient also accepts an explicit binPath. This is the portable integration path. There is no socket client. The v0.3 typed client surface was source-checked against Herdr tags v0.8.2, v0.9.0, v0.9.1, and main; the six new operation contracts did not differ. Read-only live verification used Herdr 0.9.1 with protocol 22. herdr api schema --json is the contract source. The supported minimum remains >= 0.8.2.

Scope

The SDK provides runtime/environment parsing, context and event parsing, typed and generic CLI operations, safe error types, and testing fixtures/mock clients. It is intentionally a small CLI-first SDK for executable Herdr Plugin v1 commands.

Not included

  • Raw socket client
  • Event subscriptions
  • Manifest generation
  • definePlugin() or a plugin lifecycle framework
  • create-herdr-plugin scaffolding CLI
  • JSON Schema code generation
  • Plugin storage abstraction
  • Marketplace integration

License

MIT