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

@blackbelt-technology/dashboard-plugin-runtime

v0.5.3

Published

Plugin loader, slot registry, slot consumers, and context API for pi-dashboard

Readme

@blackbelt-technology/dashboard-plugin-runtime

Plugin loader, slot registry, slot consumers, plugin context API, and Vite plugin for pi-dashboard.

Import paths

| Path | Contents | |------|----------| | @blackbelt-technology/dashboard-plugin-runtime | Slot consumers, registry types, barrel | | @blackbelt-technology/dashboard-plugin-runtime/context | Client-side hooks (usePluginConfig, useAllSessions, etc.) | | @blackbelt-technology/dashboard-plugin-runtime/server | Loader, ServerPluginContext, config validator | | @blackbelt-technology/dashboard-plugin-runtime/vite-plugin | viteDashboardPluginsPlugin |

Plugins MUST only import from these public paths. Importing from packages/client/, packages/server/, or any other internal package is banned and will fail the lint suite.

Minimum manifest

Add a pi-dashboard-plugin field to your package's package.json:

{
  "name": "@blackbelt-technology/my-feature-plugin",
  "pi-dashboard-plugin": {
    "id": "my-feature",
    "displayName": "My Feature",
    "priority": 100,
    "client": "./dist/client/index.js",
    "server": "./dist/server/index.js",
    "claims": [
      { "slot": "session-card-badge", "component": "MyFeatureBadge" }
    ]
  }
}

Fields:

  • id — globally unique kebab-case id.
  • priority — sort order for multi-contribution slots. Lower = rendered first. Default 1000; first-party = 100.
  • client — path to the built client entry (exporting React components by name).
  • server — optional path to the server entry (exports default function registerPlugin(ctx: ServerPluginContext)).
  • bridge — optional path to a pi-extension/bridge entry (auto-registered into ~/.pi/agent/settings.json under dashboard-<id>).
  • configSchema — optional relative path to a JSON Schema 7 file for plugin config validation.
  • claims — array of slot claims.

Slot claims

Each claim targets one slot:

{ "slot": "session-card-badge", "component": "MyBadge" }
{ "slot": "tool-renderer", "toolName": "MyTool", "component": "MyToolRenderer" }
{ "slot": "command-route", "command": "/myfeature", "component": "MyFeatureView" }
{ "slot": "anchored-popover", "trigger": "my-trigger-button", "component": "MyPopover" }
{ "slot": "settings-section", "component": "MySettings", "tab": "general" }

settings-section tab field

Use tab to control which tab of the Settings page your section appears in:

| Value | Tab | |-------|-----| | general (default) | General | | servers | Servers | | packages | Packages | | providers | Providers | | security | Security | | advanced | Advanced |

Client-side PluginContext API

Plugin client components receive props from the slot consumer. Hooks are available via the nearest PluginContextProvider:

import {
  usePluginConfig,
  useAllSessions,
  useSessionState,
  usePluginLogger,
  usePluginSend,
  usePluginRouter,
} from "@blackbelt-technology/dashboard-plugin-runtime/context";

function MyBadge({ session }) {
  const config = usePluginConfig<{ enabled: boolean }>();
  const logger = usePluginLogger(); // logs as [plugin:my-feature]
  const send = usePluginSend();
  // ...
}

usePluginConfig<T>() is reactive — it re-renders when POST /api/config/plugins/<id> succeeds and the server broadcasts plugin_config_update.

You MUST call these hooks from within a slot contribution component (i.e. inside a CurrentPluginLayer). Calling them from outside throws a descriptive error.

Server-side ServerPluginContext API

Your server entry must export a default registerPlugin function:

// packages/my-feature-plugin/server/index.ts
import type { ServerPluginContext } from "@blackbelt-technology/dashboard-plugin-runtime/server";

export default async function registerPlugin(ctx: ServerPluginContext): Promise<void> {
  ctx.fastify.get("/api/my-feature/status", async () => {
    return { ok: true };
  });

  ctx.logger.info("my-feature plugin loaded");
}

Available on ctx:

  • fastify — Fastify instance for REST routes.
  • sessionManager / eventStore — read-only dashboard state.
  • broadcastToSubscribers(msg) — send a WebSocket message to all subscribed browsers.
  • registerPiHandler(type, handler) / registerBrowserHandler(type, handler) — hook into WebSocket message flows.
  • getPluginConfig<T>() — read this plugin's config from ~/.pi/dashboard/config.json#plugins.<id>.*.
  • updatePluginConfig<T>(partial) — validate, merge, persist, and broadcast plugin_config_update.
  • logger — namespaced logger ([plugin:<id>]).

Bridge auto-register

If your manifest declares bridge, the dashboard auto-registers it in ~/.pi/agent/settings.json under dashboardPluginBridges["dashboard-<id>"] on server startup. The dashboard removes the entry when the plugin is disabled.

The dashboard- key prefix is reserved. User-owned extension entries in packages[] are never touched.

Plugin config persistence

Plugin settings live at ~/.pi/dashboard/config.json#plugins.<id>.*.

{
  "plugins": {
    "my-feature": { "enabled": true, "pollInterval": 30 }
  }
}

If your manifest declares configSchema, the loader:

  • Applies schema default values on read.
  • Validates writes before persisting (rejects with ValidationError on schema violation).

Failure isolation rules

  • A plugin throwing during manifest validation, server-side load, or client-side render does NOT crash the dashboard.
  • Failures are reflected in /api/health.plugins[] as { loaded: false, error: "..." }.
  • Slot consumer error boundaries catch React render errors per-claim (not per-slot), so one plugin crashing does not suppress siblings.

Demo plugin

packages/demo-plugin/ is a private fixture package that exercises the runtime end-to-end. It is excluded from production builds (manifest declares fixture: true). Do not use it as a template for real plugins.