@escalated-dev/plugin-sdk
v0.1.0
Published
SDK for building Escalated plugins — framework-agnostic, TypeScript-first
Readme
@escalated-dev/plugin-sdk
SDK for building Escalated plugins. Write plugins once in TypeScript, run them across all Escalated backend frameworks (Laravel, Django, AdonisJS, Rails).
Quick Start
import { definePlugin } from '@escalated-dev/plugin-sdk'
export default definePlugin({
name: 'my-plugin',
version: '1.0.0',
actions: {
'ticket.created': async (event, ctx) => {
ctx.log.info('New ticket!', event)
},
},
})Installation
npm install @escalated-dev/plugin-sdkCore API
definePlugin(definition)
The single entry point for defining a plugin. Returns a ResolvedPlugin with normalized filters and endpoints and a toManifest() method used by the Escalated host to register your plugin.
import { definePlugin, type PluginContext } from '@escalated-dev/plugin-sdk'
export default definePlugin({
name: 'my-plugin',
version: '1.0.0',
description: 'Does something useful',
// Config fields shown in the admin settings UI
config: [
{ name: 'api_key', label: 'API Key', type: 'password', required: true },
{ name: 'notify_channel', label: 'Notify Channel', type: 'text' },
],
// Action hooks — fire-and-forget
actions: {
'ticket.created': async (event, ctx) => { /* ... */ },
'ticket.resolved': async (event, ctx) => { /* ... */ },
},
// Filter hooks — must return the (possibly modified) value
filters: {
'ticket.reply.compose': async (data, ctx) => {
return { ...data, extra_field: 'value' }
},
// Shorthand with explicit priority
'ticket.statuses': {
priority: 5,
handler: async (statuses, ctx) => [...statuses, { key: 'custom', name: 'Custom' }],
},
},
// REST endpoints exposed by the plugin
endpoints: {
'GET /my-plugin/status': async (ctx, req) => ({ ok: true }),
'POST /my-plugin/action': {
capability: 'manage_settings',
handler: async (ctx, req) => { /* ... */ },
},
},
// Incoming webhooks
webhooks: {
'POST /webhooks/my-service': async (ctx, req) => { /* ... */ },
},
// Scheduled tasks (cron expression as key)
cron: {
'0 9 * * 1': async (ctx) => {
ctx.log.info('Running weekly report')
},
},
// Lifecycle
onActivate: async (ctx) => { await ctx.store.set('data', 'init', { activated: true }) },
onDeactivate: async (ctx) => { /* cleanup */ },
})Plugin Context (ctx)
Every handler receives a PluginContext with the following services:
| Property | Type | Description |
|---|---|---|
| ctx.config | ConfigStore | Read/write plugin configuration values |
| ctx.store | DataStore | Persistent key-value and query store scoped to the plugin |
| ctx.http | HttpClient | Make outbound HTTP requests |
| ctx.broadcast | BroadcastClient | Push real-time events to channels, users, or tickets |
| ctx.log | Logger | Structured logging (info, warn, error, debug) |
| ctx.tickets | TicketRepository | Find, query, create, and update tickets |
| ctx.replies | ReplyRepository | Find, query, and create replies |
| ctx.contacts | ContactRepository | Find contacts by ID or email, create contacts |
| ctx.tags | TagRepository | List and create tags |
| ctx.departments | DepartmentRepository | List and find departments |
| ctx.agents | AgentRepository | List and find agents |
| ctx.emit | function | Emit a custom hook for other plugins to listen to |
| ctx.currentUser | function | Returns the currently authenticated user, or null |
UI Extensions
definePlugin({
// ...
// Full-page routes
pages: [
{
route: '/admin/my-plugin',
component: 'MySettingsPage',
layout: 'admin',
capability: 'manage_settings',
menu: { label: 'My Plugin', section: 'admin', position: 50 },
},
],
// Inject components into existing pages
components: [
{
page: 'ticket.show',
slot: 'sidebar',
component: 'MyTicketPanel',
order: 20,
},
],
// Dashboard widgets
widgets: [
{
component: 'MyWidget',
label: 'My Stats',
size: 'half',
badge: async (ctx) => {
const count = await ctx.store.query('alerts', { unread: true })
return count.length || null
},
},
],
})Broadcastable Events
Plugins can listen to real-time broadcasting events in addition to standard action hooks. When the host framework has broadcasting enabled (Pusher, Reverb, ActionCable, Django Channels, Mercure, Phoenix PubSub, SSE, etc.), the following events are forwarded to plugins:
| Event | Description |
|---|---|
| broadcast.ticket.updated | Ticket data changed and was broadcast to connected clients |
| broadcast.ticket.replied | A new reply was broadcast on a ticket channel |
| broadcast.ticket.assigned | Agent assignment was broadcast |
| broadcast.ticket.status_changed | Status transition was broadcast |
| broadcast.presence.updated | Ticket presence (who is viewing) was broadcast |
| broadcast.notification.sent | A real-time notification was pushed to a user |
Subscribe to these in your plugin's actions block just like any other hook:
actions: {
'broadcast.ticket.replied': async (event, ctx) => {
// React to a reply that was just broadcast in real time
ctx.log.info('Broadcast reply on ticket', event.ticketId)
},
},Documentation
See the Escalated Docs for the full plugin development guide.
Related Packages
- Plugin Runtime — Runtime host that loads and executes plugins
- Escalated — Shared frontend (Vue 3 + Inertia.js)
- Escalated Docs — Full documentation
Requirements
- Node.js >= 18
- TypeScript >= 5.4
License
MIT
