Create a Plugin
Learn how to create custom plugins to extend OrionOS agent capabilities.
Overview
Plugins are modular extensions that add new capabilities to OrionOS agents. This guide will walk you through creating your own plugin from scratch.
Plugin Structure
A basic OrionOS plugin has the following structure:
@orionos/plugin-example/
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # Main plugin export
│ ├── actions.ts # Custom actions
│ ├── providers.ts # Data providers
│ ├── services.ts # Services
│ └── types.ts # TypeScript types
├── tests/
│ └── index.test.ts
└── README.mdCreating Your First Plugin
1. Initialize the Plugin
mkdir orionos-plugin-custom
cd orionos-plugin-custom
npm init -y2. Install Dependencies
npm install --save-dev typescript @types/node
npm install axios dotenv3. Configure TypeScript
Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests"]
}4. Define Plugin Interface
Create src/types.ts:
export interface Plugin {
name: string;
version: string;
description: string;
actions?: Action[];
providers?: Provider[];
services?: Service[];
initialize?: (config: PluginConfig) => Promise<void>;
cleanup?: () => Promise<void>;
}
export interface Action {
name: string;
description: string;
handler: ActionHandler;
examples?: string[];
validate?: (input: string) => boolean;
}
export type ActionHandler = (
input: string,
context: ActionContext
) => Promise<ActionResult>;
export interface ActionContext {
agentId: string;
userId: string;
roomId?: string;
metadata?: Record<string, any>;
}
export interface ActionResult {
success: boolean;
data?: any;
error?: string;
metadata?: Record<string, any>;
}
export interface Provider {
name: string;
get: (key: string) => Promise<any>;
set: (key: string, value: any) => Promise<void>;
}
export interface Service {
name: string;
start: () => Promise<void>;
stop: () => Promise<void>;
}
export interface PluginConfig {
apiKey?: string;
options?: Record<string, any>;
}Example Plugin: Weather Service
Let's create a weather plugin that fetches weather data:
1. Define Actions
Create src/actions.ts:
import axios from 'axios';
import { Action, ActionHandler } from './types';
const getWeatherHandler: ActionHandler = async (input, context) => {
try {
// Extract city from input
const cityMatch = input.match(/weather (?:in |for )?(.+)/i);
if (!cityMatch) {
return {
success: false,
error: 'Could not parse city name from input'
};
}
const city = cityMatch[1].trim();
const apiKey = process.env.WEATHER_API_KEY;
// Fetch weather data
const response = await axios.get(
`https://api.openweathermap.org/data/2.5/weather`,
{
params: {
q: city,
appid: apiKey,
units: 'metric'
}
}
);
const weather = response.data;
return {
success: true,
data: {
city: weather.name,
temperature: weather.main.temp,
conditions: weather.weather[0].description,
humidity: weather.main.humidity,
windSpeed: weather.wind.speed
},
metadata: {
timestamp: new Date().toISOString(),
source: 'OpenWeatherMap'
}
};
} catch (error) {
return {
success: false,
error: error.message
};
}
};
const getForecastHandler: ActionHandler = async (input, context) => {
// Similar implementation for forecast
// ... implementation details
return {
success: true,
data: { /* forecast data */ }
};
};
export const weatherActions: Action[] = [
{
name: 'get_weather',
description: 'Get current weather for a city',
handler: getWeatherHandler,
examples: [
'What is the weather in London?',
'Get weather for Tokyo',
'Weather in New York'
],
validate: (input: string) => {
return /weather/i.test(input);
}
},
{
name: 'get_forecast',
description: 'Get weather forecast for a city',
handler: getForecastHandler,
examples: [
'Weather forecast for Paris',
'What will the weather be like in Berlin?'
]
}
];2. Create the Main Plugin
Create src/index.ts:
import { Plugin } from './types';
import { weatherActions } from './actions';
const weatherPlugin: Plugin = {
name: '@orionos/plugin-weather',
version: '1.0.0',
description: 'Weather data plugin for OrionOS agents',
actions: weatherActions,
initialize: async (config) => {
console.log('Weather plugin initialized');
// Validate required configuration
if (!process.env.WEATHER_API_KEY) {
throw new Error('WEATHER_API_KEY is required');
}
// Perform any setup tasks
return Promise.resolve();
},
cleanup: async () => {
console.log('Weather plugin cleanup');
// Perform cleanup tasks
return Promise.resolve();
}
};
export default weatherPlugin;
export * from './types';
export * from './actions';3. Update package.json
{
"name": "@orionos/plugin-weather",
"version": "1.0.0",
"description": "Weather data plugin for OrionOS",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"test": "jest",
"prepublishOnly": "npm run build"
},
"keywords": [
"orionos",
"plugin",
"weather",
"ai-agent"
],
"author": "Your Name",
"license": "MIT",
"peerDependencies": {
"@orionos/core": "^1.0.0"
},
"dependencies": {
"axios": "^1.6.0",
"dotenv": "^16.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}Advanced Plugin Features
Adding Providers
Providers supply data to agents:
import { Provider } from './types';
export const weatherProvider: Provider = {
name: 'weather_data',
get: async (key: string) => {
// Fetch and return weather data
const [city, dataType] = key.split(':');
if (dataType === 'current') {
return await fetchCurrentWeather(city);
} else if (dataType === 'forecast') {
return await fetchForecast(city);
}
return null;
},
set: async (key: string, value: any) => {
// Cache weather data
// Implementation details
}
};Adding Services
Services run background tasks:
import { Service } from './types';
export const weatherUpdateService: Service = {
name: 'weather_updates',
start: async () => {
console.log('Starting weather update service');
// Start periodic updates
setInterval(async () => {
await updateWeatherCache();
}, 300000); // Every 5 minutes
},
stop: async () => {
console.log('Stopping weather update service');
// Clean up intervals, connections, etc.
}
};Adding Middleware
export interface Middleware {
name: string;
process: (input: any, next: () => Promise<any>) => Promise<any>;
}
export const rateLimitMiddleware: Middleware = {
name: 'rate_limit',
process: async (input, next) => {
// Check rate limit
if (await isRateLimited(input.userId)) {
throw new Error('Rate limit exceeded');
}
// Continue to next middleware/handler
const result = await next();
// Track usage
await trackUsage(input.userId);
return result;
}
};Testing Your Plugin
Create tests/weather.test.ts:
import weatherPlugin from '../src/index';
import { ActionContext } from '../src/types';
describe('Weather Plugin', () => {
beforeAll(async () => {
process.env.WEATHER_API_KEY = 'test_key';
await weatherPlugin.initialize?.({});
});
afterAll(async () => {
await weatherPlugin.cleanup?.();
});
test('should get weather for a city', async () => {
const action = weatherPlugin.actions?.find(a => a.name === 'get_weather');
expect(action).toBeDefined();
const context: ActionContext = {
agentId: 'test_agent',
userId: 'test_user'
};
const result = await action!.handler(
'What is the weather in London?',
context
);
expect(result.success).toBe(true);
expect(result.data).toHaveProperty('city');
expect(result.data).toHaveProperty('temperature');
});
test('should validate weather queries', () => {
const action = weatherPlugin.actions?.find(a => a.name === 'get_weather');
expect(action?.validate?.('weather in Paris')).toBe(true);
expect(action?.validate?.('hello world')).toBe(false);
});
});Plugin Configuration
Allow users to configure your plugin:
export interface WeatherPluginConfig extends PluginConfig {
apiKey: string;
units?: 'metric' | 'imperial';
language?: string;
cacheDuration?: number;
}
const weatherPlugin: Plugin = {
// ... other properties
initialize: async (config: WeatherPluginConfig) => {
const {
apiKey,
units = 'metric',
language = 'en',
cacheDuration = 300000
} = config;
if (!apiKey) {
throw new Error('Weather API key is required');
}
// Store configuration
pluginConfig = { apiKey, units, language, cacheDuration };
console.log('Weather plugin initialized with config:', {
units,
language,
cacheDuration
});
}
};Building and Testing Locally
# Build the plugin
npm run build
# Link for local testing
npm link
# In your OrionOS project
npm link @orionos/plugin-weatherUsing Your Plugin
Once built, use your plugin with agents:
const agent = await client.post('/agents', {
name: 'Weather Bot',
plugins: [
{
name: '@orionos/plugin-weather',
enabled: true,
config: {
apiKey: process.env.WEATHER_API_KEY,
units: 'metric',
language: 'en'
}
}
]
});Plugin Best Practices
Error Handling: Always handle errors gracefully
Validation: Validate all inputs and configuration
Documentation: Provide clear README and inline documentation
TypeScript: Use TypeScript for type safety
Testing: Write comprehensive tests
Versioning: Follow semantic versioning
Performance: Optimize for performance and resource usage
Security: Never expose API keys or sensitive data
Compatibility: Ensure compatibility with OrionOS versions
Clean Code: Follow coding standards and best practices
Publishing Your Plugin
See Publish a Plugin for publishing instructions.
Example Plugins for Reference
Study these official plugins for examples:
@orionos/plugin-evm- Blockchain interactions@orionos/plugin-discord- Platform integration@orionos/plugin-images- External API integration@orionos/plugin-sql- Database operations
Next Steps
Publish a Plugin - Publish your plugin to npm
Plugin Reference - Browse existing plugins
API Reference - API documentation
Need Help?
Last updated

