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

@dioschub/client

v0.2.0

Published

Client SDK for the DioscHub Assistant Kit — script loader and typed command API (createDiosc).

Readme

@dioschub/client

The official client SDK for the Diosc AI Assistant Kit — a typed, namespaced API for embedding and driving the Diosc chat assistant on your web app.

Zero runtime dependencies. Framework-agnostic (React, Vue, Angular, vanilla).


Mental model (read this first)

A few things shape the entire API:

  • The kit owns the chat UI. When you embed Diosc, the assistant-kit web components are loaded from DioscHub at runtime and render the whole chat experience. You don't build a chat UI; you configure one and wire it to your app.
  • Two display layouts, one widget. The same <diosc-chat> renders either as a FAB (floating bubble, default) or embed (inline panel). There is no headless mode — the SDK never asks you to render messages, sessions, or approvals yourself.
  • Two planes of integration:
    • Control — drive the widget (connect, send, open/close).
    • Extend — give the AI host capabilities (tools, mentions, page context, custom approval UI, navigation).
  • Identity is BYOA (Bring Your Own Auth). Diosc never sees your credentials. Your app authenticates the user and binds that identity to the connection server-to-server. There is no auth-header / token API in this SDK by design.

This package gives you two entry points:

| Entry point | Use when | |---|---| | createDiosc() | The supported, fully-typed instance API. Use this. | | loadDiosc() | Lower-level script injector + raw command queue. Use only if you need the gtag-style global directly. |


Installation

npm install @dioschub/client

Quick start

import { createDiosc } from '@dioschub/client';

const diosc = createDiosc({
  apiKey: 'ak_xxx',                       // public embed key (like a Maps key)
  backendUrl: 'https://hub.example.com',
});

await diosc.ready;                        // optional — resolves when the kit loads

diosc.on('stream:chunk', (c) => console.log(c));
diosc.send('Hello!');

Then render the widget anywhere in your page:

<diosc-chat></diosc-chat>

That's the whole happy path. Everything below is opt-in.


The widget element

The kit auto-mounts <diosc-chat>. You can configure it via createDiosc(...) (recommended) or with attributes:

<!-- FAB (default) -->
<diosc-chat></diosc-chat>

<!-- Inline embed panel -->
<diosc-chat mode="embed"></diosc-chat>

<!-- Fully attribute-driven (no JS) -->
<diosc-chat
  api-key="ak_xxx"
  backend-url="https://hub.example.com"
  bind-endpoint="/api/diosc/bind">
</diosc-chat>

| Attribute | Maps to | |---|---| | api-key | apiKey | | backend-url | backendUrl | | assistant-id | assistantId (auto-detected if omitted) | | bind-endpoint | bindEndpoint (see Identity) | | mode | "embed" for inline; omit for FAB |


The instance API

createDiosc(options) returns a DioscInstance with a small core plus three namespaces: ui, extend, identity.

createDiosc(options)

options is your DioscConfig plus two loader controls:

| Option | Type | Default | Notes | |---|---|---|---| | apiKey | string | — | Public embed key. | | backendUrl | string | — | DioscHub URL. | | assistantId | string | auto | Resolved from config fetch if omitted. | | bindEndpoint | string | — | Your server endpoint for BYOA bind. | | autoConnect | boolean | true | Connect on init. | | verbose | boolean | false | Verbose logging. | | reconnectAttempts / reconnectDelay / reconnectDelayMax / connectionTimeout | number | — | Transport tuning. | | scriptUrl | string | derived | Custom kit script URL. | | autoLoad | boolean | true | Set false if the loader <script> is already on the page. |

Core

await diosc.ready;                         // Promise<void> — kit script loaded

await diosc.connect();                     // open the connection (no-op if autoConnect)
diosc.disconnect();

await diosc.send('Summarize my order', { pageContext: { orderId } });
diosc.cancelStream();                      // drain the in-flight response

const off = diosc.on('stream:chunk', (chunk) => render(chunk));
off();                                     // unsubscribe

diosc.onAny((event, payload) => log(event, payload));

await diosc.fetchAssistantConfig();        // re-fetch public assistant config

Attachments caveat: send(text, { attachments }) accepts an attachments field for forward-compatibility, but it is not wired end-to-end yet — today it is a no-op (file sends go through the composer UI). See Known gaps.

ui — drive the widget shell

diosc.ui.open();
diosc.ui.close();
diosc.ui.toggle();
diosc.ui.setPosition('bottom-left');       // 'bottom-left' | 'bottom-right'

// Change listeners (fire immediately with the current value, then on change):
const offOpen = diosc.ui.onOpenChange((isOpen) => syncMyButton(isOpen));
const offPos  = diosc.ui.onPositionChange((pos) => layout(pos));

extend — give the AI host capabilities

This is the high-value plane. Everything here is optional.

Tools — let the AI call your code

diosc.extend.tool('navigate', async (params) => {
  router.push(params.path);
  return { navigatedTo: params.path };
});

Mentions — populate the composer @-popover

The host filter is authoritative — return exactly what the popover should show. Selecting an item serializes to the wire format @[Name](kind:id), where id is what the LLM sees.

const people = [{ id: 'u_1', name: 'Ada', kind: 'user' }];

diosc.extend.mentions((needle) =>
  people.filter((p) => p.name.toLowerCase().includes(needle.toLowerCase())),
);

diosc.extend.mentions(null);               // clear

Browser adapter — expose page state + actionable intents

The adapter gives the AI a fresh page snapshot each turn plus a set of intents it may invoke. Each intent can declare a client-local approval gate that the AI cannot bypass.

diosc.extend.browser({
  read: async () => ({ url: location.href, title: document.title, data: getState() }),
  intents: [
    {
      name: 'apply_coupon',
      description: 'Apply a discount coupon to the cart',
      schema: { type: 'object', properties: { code: { type: 'string' } }, required: ['code'] },
      handler: async ({ code }) => {
        await cart.applyCoupon(code);
        return { success: true, data: { code } };
      },
      // Optional: gate behind a local approval dialog before running
      approval: {
        severity: 'medium',
        summary: ({ code }) => `Apply coupon ${code}?`,
      },
    },
  ],
});

Custom consensus view — restyle the approval body

The kit ships the consensus (approval) dialog and always owns the decision. What you can replace is the dialog's detail body, per tool, so a gated call reads in your domain's terms instead of a generic schema form:

const off = diosc.extend.consensusView(/acme-helpdesk_/, {
  mount(el, ctx) {
    // `el` is a light-DOM anchor — your stylesheets apply. Portal into it from
    // your own component tree to keep context/providers.
    render(<TicketBody ctx={ctx} />, el);
  },
  update(ctx) { /* operator edited a field, or switched request */ },
  unmount() { /* teardown */ },
});

ctx.fields is the kit's computed diff (render from it, don't re-derive), and ctx.setField(key, value) reports an edit, which becomes modifiedArgs on submit. The descriptor has no approve/reject — see below.

There is intentionally no diosc.approve() / reject(), and no host-rendered approval surface at all. Approval decisions resolve only from the built-in consensus dialog. This preserves the human-in-the-loop (Responsibility-First) guarantee.

Navigation observer — push SPA route changes into chat

diosc.extend.observeNavigation((notify) => {
  const stop = router.afterEach((route) => notify({ path: route.path }));
  return stop;                             // return a cleanup fn (optional)
});

identity — BYOA

Diosc never receives your credentials. Your app authenticates the user and binds that identity to the live connection server-to-server.

Flow:

  1. You set bindEndpoint (a route on your server).
  2. When the connection needs identity, the kit POSTs { wsId } to your bindEndpoint with credentials: 'include' (so your auth cookie rides along).
  3. Your endpoint authenticates the user and forwards the identity to DioscHub's POST /auth/bind. The bound identity (and feature flags) flow back to the kit.
const diosc = createDiosc({
  apiKey: 'ak_xxx',
  backendUrl: 'https://hub.example.com',
  bindEndpoint: '/api/diosc/bind',
});

// If your auth lives in JS (e.g. a Bearer token) rather than a cookie,
// supply extra headers for the bind request:
diosc.identity.setBindHeaders(() => ({ Authorization: `Bearer ${getToken()}` }));

// After the user signs in (e.g. anonymous → authenticated), rebind in place:
diosc.identity.reauth();

A minimal bindEndpoint on your server:

// POST /api/diosc/bind   body: { wsId: string }
app.post('/api/diosc/bind', requireAuth, async (req, res) => {
  const { wsId } = req.body;
  // Forward the authenticated identity to DioscHub.
  await fetch(`${HUB_URL}/auth/bind`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({
      wsId,
      identity: { userId: req.user.id, username: req.user.name, role: req.user.role },
    }),
  });
  res.sendStatus(204);
});

See the DioscHub docs for the authoritative bind protocol, including feature flags and REST-token rotation.

Events

Subscribe with diosc.on(event, handler) (typed) or diosc.onAny(handler).

| Event | Payload (shape) | Meaning | |---|---|---| | stream:start | { … } | Assistant began responding | | stream:chunk | { … } | Streamed content delta | | stream:end | { … } | Response complete | | tool:started | { toolCallId, toolName, toolParameters } | A tool call started | | tool:completed | { toolCallId, toolName, durationMs, resultSummary } | Tool call finished | | tool:failed | { toolCallId, toolName, errorMessage } | Tool call failed | | approval:request | { toolCalls, … } | A human decision is required | | content_blocked | { reason, message } | Output guardrail tripped | | session:started / joined / loaded / restored | { session, … } | Session lifecycle | | session:renamed / pinned | { sessionId, … } | Session list change | | auth:refreshed / auth:failed | { … } | Identity bind lifecycle | | files:updated | { fileId?, files? } | File set changed | | browser:read_page | { … } | Kit requested a page snapshot |

ProtocolEventName is the full typed union; on() also accepts any string for forward-compat.

Short-lived AI triggers (one-shot, non-chat calls like generate a description from the selected items) are out of scope for this SDK. Today they belong on your backend. If introduced later, they will ship as UI components, not as a bare client method. See the DioscHub docs.


Framework examples

React

import { useEffect, useRef } from 'react';
import { createDiosc, type DioscInstance } from '@dioschub/client';

export function Assistant() {
  const ref = useRef<DioscInstance>();

  useEffect(() => {
    const diosc = createDiosc({
      apiKey: import.meta.env.VITE_DIOSC_KEY,
      backendUrl: import.meta.env.VITE_DIOSC_URL,
      bindEndpoint: '/api/diosc/bind',
    });
    ref.current = diosc;

    diosc.extend.tool('navigate', async ({ path }) => {
      window.history.pushState({}, '', path);
      return { navigatedTo: path };
    });

    const off = diosc.on('approval:request', () => toast('Action needs approval'));
    return () => off();
  }, []);

  return <diosc-chat />;
}

Vue

<template><diosc-chat /></template>

<script setup lang="ts">
import { onMounted } from 'vue';
import { createDiosc } from '@dioschub/client';

onMounted(() => {
  const diosc = createDiosc({
    apiKey: import.meta.env.VITE_DIOSC_KEY,
    backendUrl: import.meta.env.VITE_DIOSC_URL,
  });
  diosc.extend.observeNavigation((notify) =>
    router.afterEach((r) => notify({ path: r.path })),
  );
});
</script>

Angular

import { Injectable } from '@angular/core';
import { createDiosc, type DioscInstance } from '@dioschub/client';

@Injectable({ providedIn: 'root' })
export class DioscService {
  readonly diosc: DioscInstance = createDiosc({
    apiKey: environment.dioscKey,
    backendUrl: environment.dioscUrl,
    bindEndpoint: '/api/diosc/bind',
  });

  constructor() {
    this.diosc.identity.setBindHeaders(() => ({
      Authorization: `Bearer ${this.auth.token}`,
    }));
  }
}

Vanilla JS

<script type="module">
  import { createDiosc } from 'https://esm.sh/@dioschub/client';

  const diosc = createDiosc({ apiKey: 'ak_xxx', backendUrl: 'https://hub.example.com' });
  diosc.ui.setPosition('bottom-left');
</script>

<diosc-chat></diosc-chat>

Lower-level: loadDiosc + the raw command doorway

If you need the gtag-style global directly (or are migrating older code), loadDiosc() injects the script and returns the raw command function and a ready promise. The raw doorway is also reachable via instance.raw.

import { loadDiosc } from '@dioschub/client';

const { diosc, ready } = loadDiosc({ backendUrl: 'https://hub.example.com', apiKey: 'ak_xxx' });
diosc('config', { autoConnect: true });    // commands buffered until the script loads
await ready;

createDiosc() is built on top of loadDiosc() — prefer the instance API; reach for the raw doorway only as an escape hatch.


What this SDK intentionally does not have

These omissions are deliberate design decisions, not gaps:

  • No headless mode. The kit renders the chat UI (FAB/embed). You never build message lists, session pickers, or the approval dialog yourself.
  • No host session API. loadSession / startNewSession / renameSession / etc. are driven by the kit's own session-history panel, not host code.
  • No approve / reject global, and no host-rendered approval surface. Decisions resolve only from the kit's own consensus dialog. You may restyle its body via extend.consensusView — that carries no verdict channel — Responsibility-First.
  • No auth-header / token API. Identity is bound server-to-server via bindEndpoint. The credential-blind principle means Diosc never sees tokens.

See the DioscHub docs for the full rationale.


Known gaps

| Gap | Status | |---|---| | send(text, { attachments }) | attachments is accepted but a no-op (engine invoke doesn't forward it yet). |


Exported types

DioscInstance, CreateDioscOptions, DioscUi, DioscExtend, DioscIdentity, SendOptions, WidgetPosition, DioscConfig, BoundIdentity, BoundRole, NavigationData, NavigationObserverCallback, BrowserToolHandler, BrowserAdapter, PageSnapshot, IntentDefinition, IntentApproval, IntentResult, JsonSchema, MentionItem, MentionQuery, ApprovalRequest, ApprovalToolCall, ApprovalActions, ApprovalValidationError, ApprovalHandler, SessionEventName, ServerEventName, ProtocolEventName, EventHandler, WildcardEventHandler, Unsubscribe, DioscCommand, DioscFunction, DioscEngineHandle, LoadDioscOptions, LoadDioscResult.


License

MIT