The comprehensive SDK for developing plugins for the Qirvo platform with full marketplace integration. Build powerful extensions that integrate seamlessly with Qirvo's dashboard, CLI, automation system, and plugin marketplace.
- πͺ Full Marketplace Integration: Complete alignment with Qirvo's plugin marketplace
- π° Payment & Licensing: Built-in support for paid plugins and license management
- π§ Enhanced API Access: Calendar, health metrics, and extended HTTP methods
- ποΈ Plugin Runtime Management: Advanced plugin lifecycle and runtime control
- π§ͺ Testing Framework: Comprehensive testing utilities for plugin development
- π Analytics Ready: Plugin usage and performance tracking capabilities
- π Enhanced Permissions: Granular permission system with detailed descriptions
npm install @qirvo/plugin-sdkimport { BasePlugin, PluginRuntimeContext, createCommand } from '@qirvo/plugin-sdk';
export default class MyPlugin extends BasePlugin {
async onInstall(context: PluginRuntimeContext): Promise<void> {
this.log('info', 'Plugin installed successfully!');
}
async onEnable(context: PluginRuntimeContext): Promise<void> {
this.log('info', 'Plugin enabled!');
}
}Plugins have several lifecycle hooks you can implement:
onInstall()- Called when plugin is first installedonUninstall()- Called when plugin is removedonEnable()- Called when plugin is enabledonDisable()- Called when plugin is disabledonUpdate()- Called when plugin is updated to new versiononConfigChange()- Called when plugin configuration changes
Every plugin receives a runtime context with access to:
interface PluginRuntimeContext {
plugin: { id: string; name: string; version: string };
config: Record<string, any>;
storage: PluginStorage;
api: PluginAPI;
logger: PluginLogger;
}Persistent storage for your plugin data:
// Store data
await this.context.storage.set('key', { data: 'value' });
// Retrieve data
const data = await this.context.storage.get('key');
// Delete data
await this.context.storage.delete('key');
// Clear all data
await this.context.storage.clear();Interact with Qirvo's core features:
// Task management
const tasks = await this.context.api.tasks.list();
await this.context.api.tasks.create({ title: 'New task', description: 'Task description' });
// Notifications
this.context.api.notifications.show({
title: 'Success',
message: 'Operation completed',
type: 'success'
});
// HTTP requests
const response = await this.context.api.http.get('https://api.example.com/data');For direct API access, use the built-in HTTP client and endpoint utilities:
import { apiClient, endpoints } from '@qirvo/plugin-sdk';
// Set authentication token (if needed)
apiClient.setAuthToken('your-jwt-token');
// Plugin management
const plugins = await apiClient.get(endpoints.plugins.list);
await apiClient.post(endpoints.plugins.install, { pluginId: 'my-plugin' });
// Marketplace access
const marketplacePlugins = await apiClient.get(endpoints.marketplace.plugins);
const searchResults = await apiClient.get(`${endpoints.marketplace.search}?q=weather`);
// Storage operations
await apiClient.post(endpoints.storage.set('myKey'), { value: 'myData' });
const data = await apiClient.get(endpoints.storage.get('myKey'));
// Widget management
const widgets = await apiClient.get(endpoints.widgets.available);
await apiClient.post(endpoints.widgets.register, widgetConfig);- Plugins: Install, uninstall, configure, and manage plugins
- Widgets: Register and manage dashboard widgets
- Marketplace: Browse, search, and install from marketplace
- Storage: Key-value storage for plugin data
- Dashboard: Layout and widget management
- Commands: Register and execute CLI commands
Add custom commands to the Qirvo CLI:
export const commands = [
createCommand('hello', 'Say hello', async (args, context) => {
console.log(`Hello, ${args[0] || 'World'}!`);
})
];Create interactive dashboard components:
// manifest.json
{
"dashboard_widget": {
"name": "My Widget",
"component": "MyWidget",
"size": "medium",
"position": "sidebar"
}
}Run scheduled tasks and automation:
export default class BackgroundPlugin extends BasePlugin {
private timer?: NodeJS.Timeout;
async onEnable(): Promise<void> {
this.timer = setInterval(async () => {
// Run background task
await this.performTask();
}, 60000); // Every minute
}
async onDisable(): Promise<void> {
if (this.timer) {
clearInterval(this.timer);
}
}
}Every plugin needs a manifest.json file with the new enhanced schema:
{
"manifest_version": 1,
"name": "My Awesome Plugin",
"version": "1.0.0",
"description": "Does awesome things",
"type": "dashboard-widget",
"author": {
"name": "Your Name",
"email": "you@example.com",
"website": "https://yourwebsite.com"
},
"category": "productivity",
"permissions": ["network", "storage", "notifications"],
"external_services": [
{
"name": "OpenWeatherMap",
"base_url": "https://api.openweathermap.org/data/2.5",
"api_key_required": true,
"description": "Weather data API"
}
],
"dashboard_widget": {
"name": "My Widget",
"description": "Awesome dashboard widget",
"component": "MyWidget",
"defaultSize": { "width": 400, "height": 300 },
"configSchema": {
"type": "object",
"properties": {
"apiKey": {
"type": "string",
"title": "API Key",
"description": "Your API key"
}
}
}
},
"pages": [
{
"name": "settings",
"path": "/plugins/my-plugin/settings",
"component": "SettingsPage",
"title": "Plugin Settings",
"icon": "settings"
}
],
"menu_items": [
{
"label": "My Plugin",
"path": "/plugins/my-plugin",
"icon": "puzzle",
"order": 100
}
],
"commands": [
{
"name": "hello",
"description": "Say hello",
"usage": "hello [name]"
}
],
"repository": "https://github.com/user/my-plugin",
"homepage": "https://github.com/user/my-plugin",
"bugs": "https://github.com/user/my-plugin/issues",
"license": "MIT"
}Define user-configurable settings:
interface PluginConfigField {
key: string;
type: 'string' | 'number' | 'boolean' | 'select' | 'textarea';
title: string;
description?: string;
required?: boolean;
default?: any;
options?: { value: string; label: string }[];
min?: number;
max?: number;
}my-plugin/
βββ package.json
βββ manifest.json
βββ src/
β βββ index.ts
β βββ widget.tsx (optional)
βββ README.md
βββ tsconfig.json
npm run build
npm pack- Go to Qirvo Dashboard β Plugins β Installed Plugins
- Click "Upload Plugin"
- Select your
.tgzfile - Configure and test your plugin
import { BasePlugin, PluginRuntimeContext } from '@qirvo/plugin-sdk';
export default class WeatherPlugin extends BasePlugin {
async getWeather(location: string): Promise<any> {
const apiKey = await this.getConfig('apiKey');
const response = await this.context.api.http.get(
`https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${apiKey}`
);
return response.json();
}
async onEnable(): Promise<void> {
// Start weather updates
setInterval(async () => {
const location = await this.getConfig('defaultLocation');
if (location) {
const weather = await this.getWeather(location);
await this.setStorage('currentWeather', weather);
}
}, 15 * 60 * 1000); // Every 15 minutes
}
}export default class TaskAutomationPlugin extends BasePlugin {
async onConfigChange(): Promise<void> {
const rules = await this.getConfig('automationRules');
for (const rule of rules) {
if (rule.trigger === 'daily') {
await this.context.api.tasks.create({
title: rule.taskTitle,
description: rule.taskDescription,
dueDate: new Date(Date.now() + 24 * 60 * 60 * 1000)
});
}
}
}
}Plugins run in sandboxed environments with declared permissions:
network- Make HTTP requestsstorage- Access persistent storagetasks- Read/write taskscalendar- Access calendar datalocation- Access location servicesnotifications- Send notifications
The base class all plugins should extend:
abstract class BasePlugin {
protected context: PluginRuntimeContext;
// Lifecycle hooks (optional)
async onInstall?(context: PluginRuntimeContext): Promise<void>;
async onUninstall?(context: PluginRuntimeContext): Promise<void>;
async onEnable?(context: PluginRuntimeContext): Promise<void>;
async onDisable?(context: PluginRuntimeContext): Promise<void>;
async onUpdate?(context: PluginRuntimeContext, oldVersion: string): Promise<void>;
async onConfigChange?(context: PluginRuntimeContext, oldConfig: Record<string, any>): Promise<void>;
// Utility methods
protected log(level: 'info' | 'warn' | 'error' | 'debug', message: string, ...args: any[]): void;
protected async getConfig<T = any>(key?: string): Promise<T>;
protected async setConfig(key: string, value: any): Promise<void>;
protected async getStorage<T = any>(key: string): Promise<T | null>;
protected async setStorage(key: string, value: any): Promise<boolean>;
protected async notify(title: string, message: string, type?: 'info' | 'success' | 'warning' | 'error'): Promise<void>;
protected async httpGet(url: string, options?: RequestInit): Promise<Response>;
protected async httpPost(url: string, data: any, options?: RequestInit): Promise<Response>;
}// Create a command
createCommand(name: string, description: string, handler: CommandHandler, options?: CommandOptions): PluginCommand;
// Validate manifest
validateManifest(manifest: any): { valid: boolean; errors: string[] };
// Create plugin instance
createPlugin(pluginClass: new () => BasePlugin): BasePlugin;We welcome contributions! Please see our Contributing Guide for details.
GNU License - see LICENSE file for details.
- π Documentation
- π Issue Tracker
- π§ Email Support
Check out these example plugins to get started:
Made with β€οΈ by the Qirvo Team