This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/SDK.md
Jeffrey Smith 5883cb50e2 Changeset 0.30.2 cs2 (#202)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
2026-03-18 18:05:58 +00:00

312 lines
7.0 KiB
Markdown

# Switchboard SDK Reference
> `switchboard-sdk.js` — v0.30.2
The Switchboard SDK provides a unified API for surfaces and extensions.
It wraps platform internals (API, Events, Theme, ChatPane, etc.) into a
single `sw` namespace so authors never need to import platform files
directly.
## Initialization
```js
// Automatic — the SDK initializes on page load.
// Access via the global `sw` alias:
sw.api.get('/api/v1/channels').then(console.log);
```
The SDK emits a `sw:ready` CustomEvent on `document` when initialized.
Extensions loaded after boot can listen for it:
```js
document.addEventListener('sw:ready', (e) => {
const sw = e.detail.sw;
});
```
---
## sw.user
Current authenticated user (read-only getter).
```js
const u = sw.user;
// { id, username, display_name, email, role, avatar }
```
Returns `null` if not authenticated.
## sw.isAdmin
`true` if the current user has the `admin` role.
---
## sw.api
REST client with automatic auth header injection and 401 retry.
| Method | Signature | Returns |
|--------|-----------|---------|
| `get` | `(path, opts?)` | `Promise<object>` |
| `post` | `(path, body, opts?)` | `Promise<object>` |
| `put` | `(path, body, opts?)` | `Promise<object>` |
| `del` | `(path, opts?)` | `Promise<object>` |
| `stream` | `(path, body, signal?)` | `Promise<Response>` |
`opts` accepts `{ signal: AbortSignal }` for cancellation.
### Examples
```js
// List channels
const channels = await sw.api.get('/api/v1/channels');
// Create a channel
const ch = await sw.api.post('/api/v1/channels', {
title: 'Support',
type: 'direct'
});
// Streaming completion
const resp = await sw.api.stream('/api/v1/completions', {
channel_id: ch.id,
message: 'Hello'
});
const reader = resp.body.getReader();
```
---
## sw.on / sw.once / sw.off / sw.emit
EventBus integration. Supports wildcard patterns.
```js
// Subscribe
const unsub = sw.on('chat.message.*', (payload) => {
console.log('New message:', payload);
});
// One-shot
sw.once('channel.created', (ch) => { ... });
// Unsubscribe
sw.off('chat.message.*', handler);
// Emit
sw.emit('custom.event', { data: 123 });
```
### Common Events
| Event | Payload | Description |
|-------|---------|-------------|
| `chat.message.sent` | `{ message }` | User sent a message |
| `chat.message.received` | `{ message }` | AI response received |
| `channel.created` | `{ channel }` | New channel created |
| `channel.switched` | `{ channelId }` | Active channel changed |
| `theme.changed` | `{ theme }` | Theme mode changed |
---
## sw.theme
Theme observation and control.
| Property/Method | Type | Description |
|-----------------|------|-------------|
| `current` | `string` (getter) | Resolved theme: `'dark'` or `'light'` |
| `mode` | `string` (getter) | User preference: `'dark'`, `'light'`, or `'system'` |
| `set(mode)` | `void` | Set theme mode |
| `on('change', fn)` | `() => void` | Subscribe to theme changes; returns unsubscribe fn |
```js
// React to theme changes
const unsub = sw.theme.on('change', (resolved) => {
document.body.classList.toggle('dark', resolved === 'dark');
});
```
---
## sw.toast / sw.confirm / sw.modal
UI primitives for notifications and dialogs.
```js
// Toast notification
sw.toast('Saved successfully', 'success'); // types: success, error, info, warn
sw.toast('Something went wrong', 'error');
// Confirmation dialog (returns Promise<boolean>)
const ok = await sw.confirm('Delete this item?');
// Modal
sw.modal.open(htmlContentOrElementId);
sw.modal.close(id);
```
---
## sw.chat(container, opts)
Mount a ChatPane instance into a DOM element.
```js
const pane = sw.chat(document.getElementById('my-chat'), {
channelId: 'abc-123',
standalone: true, // default: true
});
// pane.renderMessages(), pane.destroy(), etc.
```
**Options:**
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `channelId` | `string` | `null` | Channel to load |
| `standalone` | `boolean` | `true` | Standalone mode (own input area) |
---
## sw.notes(container, opts)
Mount a NotePanel instance into a DOM element.
```js
const panel = sw.notes(document.getElementById('my-notes'), {
projectId: 'proj-123',
});
// panel.loadNotesList(), panel.openNoteEditor(id), panel.destroy()
```
---
## sw.panels()
Access the PanelRegistry for sidebar panel management.
```js
const panels = sw.panels();
panels.open('notes');
panels.toggle('editor');
panels.isOpen('notes'); // boolean
panels.active(); // current panel name or null
panels.cycle(); // cycle to next panel
panels.register('custom', { ... });
```
---
## sw.workflow
Workflow stage surface management (v0.30.2).
### sw.workflow(container, opts)
Mount a workflow stage surface.
```js
sw.workflow(document.getElementById('stage-mount'), {
channelId: 'ch-abc',
stageMode: 'form_only', // chat_only | form_only | form_chat | review
surfacePkgId: 'my-surface', // optional: custom package surface
formTemplate: { fields: [...] },
});
```
### sw.workflow.registerSurface(name, factory)
Register a custom stage surface from a package.
```js
sw.workflow.registerSurface('intake-form', (container, ctx) => {
// ctx: { channelId, basePath, stageMode, formTemplate, ... }
container.innerHTML = '<h2>Custom Intake</h2>';
return {
mount() { /* called on attach */ },
unmount() { /* called on detach */ },
};
});
```
### sw.workflow.getContext(channelId)
Fetch workflow instance status.
```js
const status = await sw.workflow.getContext('ch-abc');
// { workflow_id, current_stage, stage_data, ... }
```
### sw.workflow.advance(channelId, data?)
Advance to the next stage, optionally merging data.
```js
await sw.workflow.advance('ch-abc', { approved: true });
```
### sw.workflow.reject(channelId, reason)
Reject the current stage (go back).
```js
await sw.workflow.reject('ch-abc', 'Missing required documents');
```
---
## sw.pipe
Filter pipeline for intercepting chat messages at three stages:
pre-send, post-receive (stream), and post-render.
### Registration
```js
// Pre-send filter (runs before message is sent to API)
sw.pipe.pre(50, (ctx) => {
// ctx: { message, channel, metadata }
ctx.message += '\n\n[via my extension]';
return ctx; // return ctx to continue, null to halt
});
// Stream filter (runs on each SSE chunk)
sw.pipe.stream(50, (ctx) => {
// ctx: { chunk, channel, accumulated }
return ctx;
});
// Render filter (runs after markdown rendering)
sw.pipe.render(50, (ctx) => {
// ctx: { html, message, channel }
ctx.html = ctx.html.replace(/TODO/g, '<mark>TODO</mark>');
return ctx;
});
```
**Priority:** lower numbers run first. Convention: 0-49 system, 50-99
extensions, 100+ user customizations.
**Scope:** restrict a filter to specific channel types:
```js
sw.pipe.render(50, myFilter, {
scope: { channelType: ['direct', 'group'] },
source: 'my-extension',
});
```
### Introspection
```js
sw.pipe.list();
// { pre: [...], stream: [...], render: [...] }
// Each entry: { priority, source, scope, calls, avgMs, errors }
```