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

@kontourai/station-sdk

v0.4.1

Published

SDK for building Station workspace plugins

Readme

@kontourai/station-sdk

Station is Kontour's local-first agent workspace: you direct agent work, and the gate verdicts, evidence, and trust state stay in the same context as the work. Plugins are how the workspace is extended — a plugin contributes layouts, agents, MCP integrations, providers, and knowledge namespaces to the Station shell.

This package is the client-side SDK those plugins build against: theme-aware UI components, React hooks for agents, chat, navigation and notifications, and typed access to the Station host API.

Installation

npm install @kontourai/station-sdk

Requires a bundler (ships TypeScript source)

This package publishes raw TypeScript. Every entry in exports points at a .ts file under src/; there is no compiled dist/. That is deliberate — the supported consumer is a Station plugin, whose npm run build calls buildPlugin() from @kontourai/station-shared/build and bundles the plugin with esbuild, which reads .ts from node_modules directly.

  • Supported: esbuild, Vite, webpack, Rollup, or any TS-aware loader/runtime (tsx, ts-node, Bun, Deno).
  • Not supported today: plain-Node require() / import of this package without a TS-aware step.

This is a disclosed constraint, not an accident. If you need a precompiled build for a non-bundled runtime, open an issue.

Start from npm

A plugin needs nothing but npm and these two packages — no Station checkout.

mkdir hello-station && cd hello-station
npm init -y
npm pkg set type=module
npm pkg set scripts.build="tsx build.ts"
npm pkg set scripts.dev="tsx build.ts --dev"

npm install @kontourai/station-sdk @kontourai/station-shared
npm install -D tsx @types/react

mkdir src

plugin.json — the manifest Station reads:

{
  "name": "hello-station",
  "version": "1.0.0",
  "sdkVersion": "^0.4.0",
  "displayName": "Hello Station",
  "description": "A Station layout plugin",
  "entrypoint": "src/index.tsx",
  "capabilities": ["navigation"],
  "permissions": ["navigation.dock"],
  "layout": { "slug": "hello-station", "source": "./layout.json" }
}

layout.json — the layout the manifest points at:

{
  "name": "Hello Station",
  "slug": "hello-station",
  "icon": "👋",
  "tabs": [{ "id": "home", "label": "Home", "component": "hello-station-home" }]
}

src/index.tsx — the entrypoint. Export a components map keyed by the tab component ids in layout.json:

import { type LayoutComponentProps, useNavigation } from '@kontourai/station-sdk';

function Home({ onShowChat }: LayoutComponentProps) {
  const { setDockState } = useNavigation();
  return (
    <section style={{ padding: '1.5rem' }}>
      <h1>Hello Station</h1>
      <button type="button" onClick={() => { setDockState(true); onShowChat?.(); }}>
        Open Chat
      </button>
    </section>
  );
}

export const components = { 'hello-station-home': Home };
export default Home;

build.ts — the build. buildPlugin is the same function Station itself runs; tsx is what lets Node import it from the TypeScript source these packages ship:

import { buildPlugin } from '@kontourai/station-shared/build';

const mode = process.argv.includes('--dev') ? 'dev' : 'production';
const result = await buildPlugin(process.cwd(), mode);

if (!result.built) console.log('No entrypoint in plugin.json — nothing to bundle.');
else console.log(`Built ${result.bundlePath}`);

Then build it:

npm run build   # production bundle at dist/bundle.js
npm run dev     # dev bundle with inline sourcemaps

React, @tanstack/react-query, and this SDK are supplied by the Station host at runtime, so buildPlugin externalizes them instead of bundling them.

Load the bundle

Point a running Station instance at the plugin directory:

curl -X POST http://localhost:3141/api/plugins/install \
  -H 'Content-Type: application/json' \
  -d "{\"source\": \"$PWD\"}"

Station copies the plugin to ~/.station/plugins/<name>/, rebuilds it, and registers its layout; the layout is then available to add to a project. Active or trusted permissions come back as pendingConsent for you to approve.

UI Components

The SDK provides pre-built, theme-aware components for consistent styling across workspaces.

Button

import { Button } from '@kontourai/station-sdk';

function MyComponent() {
  return (
    <>
      <Button variant="primary" onClick={handleClick}>
        Primary Action
      </Button>
      
      <Button variant="secondary" size="sm">
        Secondary
      </Button>
      
      <Button variant="success" loading={isLoading}>
        Save Changes
      </Button>
      
      <Button variant="ghost" disabled>
        Disabled
      </Button>
    </>
  );
}

Props:

  • variant: 'primary' | 'secondary' | 'success' | 'ghost' (default: 'primary')
  • size: 'sm' | 'md' | 'lg' (default: 'md')
  • loading: boolean - Shows loading state
  • All standard button HTML attributes

Pill

import { Pill } from '@kontourai/station-sdk';

function MyComponent() {
  return (
    <>
      <Pill variant="primary">Active</Pill>
      
      <Pill variant="success">Completed</Pill>
      
      <Pill variant="warning">Pending</Pill>
      
      <Pill variant="error">Failed</Pill>
      
      <Pill 
        variant="default" 
        removable 
        onRemove={() => console.log('removed')}
      >
        Removable Tag
      </Pill>
    </>
  );
}

Props:

  • variant: 'default' | 'primary' | 'success' | 'warning' | 'error' (default: 'default')
  • size: 'sm' | 'md' (default: 'md')
  • removable: boolean - Shows remove button
  • onRemove: () => void - Called when remove button is clicked
  • All standard span HTML attributes

Hooks

Agent Management

import { useAgents, useAgent } from '@kontourai/station-sdk';

const agents = useAgents();
const agent = useAgent('my-agent');

Chat Operations

import { useSendMessage, useCreateChatSession } from '@kontourai/station-sdk';

const sendMessage = useSendMessage();
const createSession = useCreateChatSession();

// Send a message
sendMessage('Hello, agent!');

// Create a new chat session
createSession('my-agent');

Navigation

import { useNavigation, useDockState } from '@kontourai/station-sdk';

const { setDockState } = useNavigation();
const [isDockOpen] = useDockState();

// Open chat dock
setDockState(true);

Notifications

import { useToast, useNotifications } from '@kontourai/station-sdk';

const { showToast } = useToast();
const { notify } = useNotifications();

showToast('Success!', 'success');
notify({ title: 'New message', message: 'You have a new message' });

Tool Invocation

import { callTool, invokeAgent } from '@kontourai/station-sdk';

// Call an MCP tool directly
const result = await callTool('my-agent', 'tool-name', { param: 'value' });

// Invoke agent silently
const response = await invokeAgent('my-agent', 'Do something');

Layout Navigation

import { useLayoutNavigation } from '@kontourai/station-sdk';

const { getTabState, setTabState } = useLayoutNavigation();

// Save state
setTabState('my-tab', 'key=value&other=data');

// Restore state
const state = getTabState('my-tab');

Theme Variables

All components use CSS variables for theming:

  • --color-primary - Primary brand color
  • --color-success - Success state color
  • --color-warning - Warning state color
  • --color-error - Error state color
  • --color-bg - Background color
  • --color-bg-secondary - Secondary background
  • --color-text - Primary text color
  • --color-text-secondary - Secondary text color
  • --color-border - Border color

Components automatically adapt to light/dark mode.

Related packages

License

Apache-2.0 — see LICENSE.