Skip to main content
The @proj-airi/plugin-sdk is currently in active development. APIs may change without warning.

Overview

The Plugin SDK enables developers to create plugins that extend AIRI’s capabilities. Plugins can provide AI models, tools, integrations, and custom features through a standardized lifecycle and communication protocol.

Installation

npm install @proj-airi/plugin-sdk

Architecture

The Plugin SDK follows a host-guest architecture:
  • Plugin Host: Manages plugin lifecycle, communication channels, and capability orchestration
  • Plugins: Independent modules that contribute features through a standardized protocol
  • Channels: Transport-agnostic communication layer (in-memory, WebSocket, Worker)
  • Protocol: Structured events and lifecycle phases

Key Concepts

Plugin Lifecycle

Plugins progress through defined phases:
  1. Loading - Plugin module is imported
  2. Loaded - Plugin is successfully loaded
  3. Authenticating - Establishing secure connection
  4. Authenticated - Authentication successful
  5. Announced - Plugin registered in module registry
  6. Preparing - Checking dependencies and prerequisites
  7. Prepared - Ready for configuration
  8. Configuration Needed - Waiting for user configuration
  9. Configured - Configuration applied
  10. Ready - Plugin is fully active

Transport Types

The SDK supports multiple transport mechanisms:
  • In-Memory: Direct function calls within the same process
  • WebSocket: Remote communication over network (planned)
  • Worker: Web Workers or Node.js worker threads (planned)
  • Electron IPC: Electron main/renderer process communication (planned)

Capabilities

Plugins contribute capabilities that other plugins can depend on:
// Plugin A announces a capability
await apis.protocol.capabilities.markReady('llm:openai')

// Plugin B waits for the capability
await apis.protocol.capabilities.wait('llm:openai')

Basic Plugin Structure

import { definePlugin } from '@proj-airi/plugin-sdk'

export default definePlugin('my-plugin', '1.0.0', () => ({
  async init({ channels, apis }) {
    // Initialize plugin
    console.log('Plugin initializing...')
    
    // Register event handlers
    channels.host.on('module:configure', async (event) => {
      const config = event.payload.config
      // Apply configuration
    })
    
    return true // or false to abort
  },
  
  async setupModules({ channels, apis }) {
    // Contribute capabilities
    await apis.protocol.capabilities.markReady('my-feature')
    
    // Setup module functionality
    channels.host.on('custom:event', async (event) => {
      // Handle custom events
    })
  }
}))

Plugin Manifest

Plugins are loaded via a manifest that specifies entrypoints for different runtimes:
import type { ManifestV1 } from '@proj-airi/plugin-sdk/plugin-host'

const manifest: ManifestV1 = {
  apiVersion: 'v1',
  kind: 'manifest.plugin.airi.moeru.ai',
  name: 'my-plugin',
  entrypoints: {
    default: './dist/index.mjs',
    electron: './dist/electron.mjs',
    node: './dist/node.mjs',
    web: './dist/web.mjs'
  }
}

Core APIs

The SDK provides several API modules:

Protocol APIs

Communication with the plugin host:
  • Capabilities: Announce and wait for capabilities
  • Resources: Manage resources and providers

Client APIs

Access to client-side functionality:
  • Resources: Access shared resources
  • Providers: Query available providers

Event-Driven Architecture

Plugins communicate through events using @moeru/eventa:
import { defineEvent, defineInvokeHandler } from '@moeru/eventa'

// Define custom event
const myEvent = defineEvent<{ message: string }>('custom:my-event')

// Emit event
channels.host.emit(myEvent, { message: 'Hello' })

// Handle event
channels.host.on(myEvent, async (event) => {
  console.log(event.payload.message)
})

TypeScript Support

The SDK provides full TypeScript type definitions:
import type {
  Plugin,
  ContextInit,
  PluginApis,
  ManifestV1
} from '@proj-airi/plugin-sdk'

Testing Plugins

Test your plugins using the plugin host:
import { PluginHost } from '@proj-airi/plugin-sdk/plugin-host'

const host = new PluginHost({
  runtime: 'node',
  transport: { kind: 'in-memory' }
})

const session = await host.start(manifest, {
  cwd: '/path/to/plugin'
})

console.log('Plugin phase:', session.phase) // 'ready'

Plugin Examples

Simple Tool Provider

export default definePlugin('weather-tool', '1.0.0', () => ({
  async init({ channels, apis }) {
    console.log('Weather tool initializing')
    return true
  },
  
  async setupModules({ channels, apis }) {
    // Register weather tool
    await apis.protocol.capabilities.markReady('tool:weather')
    
    channels.host.on('tool:weather:invoke', async (event) => {
      const { location } = event.payload
      // Fetch weather data
      return { temperature: 72, condition: 'sunny' }
    })
  }
}))

LLM Provider

export default definePlugin('openai-provider', '1.0.0', () => ({
  async init({ channels, apis }) {
    // Wait for configuration
    channels.host.on('module:configure', async (event) => {
      const { apiKey } = event.payload.config
      // Store API key
    })
    return true
  },
  
  async setupModules({ channels, apis }) {
    // Announce LLM capability
    await apis.protocol.capabilities.markReady('llm:openai')
    
    // Handle generation requests
    channels.host.on('llm:generate', async (event) => {
      const { prompt } = event.payload
      // Call OpenAI API
      return { text: 'Generated response' }
    })
  }
}))

Best Practices

Each plugin should have a single, well-defined purpose. Split complex functionality into multiple plugins.
Return false from init() to abort initialization. Emit status events for degraded states.
Declare capabilities for features you provide, and wait for capabilities you depend on.
Always validate configuration data before applying it. Provide clear error messages.
Clean up resources when the plugin is stopped or reloaded.

Next Steps

Creating Plugins

Step-by-step guide to building your first plugin

Plugin API Reference

Complete API reference for plugin development

Build docs developers (and LLMs) love