@vooodooo/magic
Version:
Vooodooo - AI orchestration platform
392 lines • 14.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginManager = void 0;
exports.createPluginManager = createPluginManager;
const knowledge_system_js_1 = require("../core/knowledge-system.js");
const index_js_1 = require("../index.js");
const extension_registry_js_1 = require("./extension-registry.js");
/**
* Manager for Vooodooo plugins
*/
class PluginManager {
constructor(options = {}) {
/** Map of plugin ID to plugin instance */
this.plugins = new Map();
/** Plugin configuration storage */
this.config = new Map();
/** Registered CLI commands */
this.commands = new Map();
this.options = {
autoEnable: true,
config: {},
platformVersion: '0.1.0',
...options,
};
// Load initial configuration
if (options.config) {
for (const [pluginId, pluginConfig] of Object.entries(options.config)) {
this.config.set(pluginId, pluginConfig);
}
}
// Initialize extension registry
this.extensionRegistry = new extension_registry_js_1.ExtensionRegistry();
// Initialize knowledge system
this.knowledgeSystem = new knowledge_system_js_1.KnowledgeSystem();
}
/**
* Register a plugin with the system
*/
async registerPlugin(plugin) {
const pluginId = plugin.manifest.id;
if (this.plugins.has(pluginId)) {
throw new Error(`Plugin with ID ${pluginId} is already registered`);
}
try {
// Extract dependencies
const dependencies = this.extractDependencies(plugin);
// Create API for the plugin
const api = {
log: this.createLogFunction(pluginId),
registerExtension: this.createRegisterExtensionFunction(pluginId),
getPlatformVersion: () => this.options.platformVersion || '0.1.0',
getConfig: () => this.getPluginConfig(pluginId),
setConfig: (config) => this.setPluginConfig(pluginId, config),
getKnowledgeSystem: () => this.createKnowledgeSystemAPI(pluginId),
registerCommand: (command) => this.registerPluginCommand(pluginId, command),
getState: () => this.getPluginState(pluginId),
};
// Create plugin instance
const pluginInstance = {
plugin,
state: index_js_1.PluginState.REGISTERED,
api,
dependencies,
dependents: [],
};
// Add to registered plugins
this.plugins.set(pluginId, pluginInstance);
// Update dependents for dependencies
for (const depId of dependencies) {
const depPlugin = this.plugins.get(depId);
if (depPlugin) {
depPlugin.dependents.push(pluginId);
}
}
// Auto-enable if configured
if (this.options.autoEnable) {
await this.enablePlugin(pluginId);
}
console.log(`Registered plugin: ${plugin.manifest.name} (${pluginId})`);
}
catch (error) {
console.error(`Error registering plugin ${pluginId}:`, error);
throw error;
}
}
/**
* Enable a plugin
*/
async enablePlugin(pluginId) {
const pluginInstance = this.plugins.get(pluginId);
if (!pluginInstance) {
throw new Error(`Plugin ${pluginId} is not registered`);
}
if (pluginInstance.state === index_js_1.PluginState.ACTIVE) {
console.log(`Plugin ${pluginId} is already active`);
return;
}
// Check dependencies
for (const depId of pluginInstance.dependencies) {
if (!this.plugins.has(depId)) {
throw new Error(`Plugin ${pluginId} depends on ${depId}, but it is not registered`);
}
const depPlugin = this.plugins.get(depId);
if (depPlugin.state !== index_js_1.PluginState.ACTIVE) {
// Enable dependency first
await this.enablePlugin(depId);
}
}
try {
// Update state to initializing
pluginInstance.state = index_js_1.PluginState.INITIALIZING;
// Initialize the plugin
await pluginInstance.plugin.initialize(pluginInstance.api);
// Call onEnable if implemented
if (pluginInstance.plugin.onEnable) {
await pluginInstance.plugin.onEnable();
}
// Update state to active
pluginInstance.state = index_js_1.PluginState.ACTIVE;
console.log(`Enabled plugin: ${pluginInstance.plugin.manifest.name} (${pluginId})`);
}
catch (error) {
// Update state to error
pluginInstance.state = index_js_1.PluginState.ERROR;
pluginInstance.error = error;
console.error(`Error enabling plugin ${pluginId}:`, error);
throw error;
}
}
/**
* Disable a plugin
*/
async disablePlugin(pluginId) {
const pluginInstance = this.plugins.get(pluginId);
if (!pluginInstance) {
throw new Error(`Plugin ${pluginId} is not registered`);
}
if (pluginInstance.state !== index_js_1.PluginState.ACTIVE) {
console.log(`Plugin ${pluginId} is not active`);
return;
}
// Check dependents
for (const depId of pluginInstance.dependents) {
const depPlugin = this.plugins.get(depId);
if (depPlugin && depPlugin.state === index_js_1.PluginState.ACTIVE) {
throw new Error(`Cannot disable plugin ${pluginId} because ${depId} depends on it`);
}
}
try {
// Call onDisable if implemented
if (pluginInstance.plugin.onDisable) {
await pluginInstance.plugin.onDisable();
}
// Remove all extensions from this plugin
this.extensionRegistry.removeExtensionsFromPlugin(pluginId);
// Update state to disabled
pluginInstance.state = index_js_1.PluginState.DISABLED;
console.log(`Disabled plugin: ${pluginInstance.plugin.manifest.name} (${pluginId})`);
}
catch (error) {
// Update state to error
pluginInstance.state = index_js_1.PluginState.ERROR;
pluginInstance.error = error;
console.error(`Error disabling plugin ${pluginId}:`, error);
throw error;
}
}
/**
* Unregister a plugin from the system
*/
async unregisterPlugin(pluginId) {
const pluginInstance = this.plugins.get(pluginId);
if (!pluginInstance) {
console.warn(`Plugin ${pluginId} is not registered`);
return;
}
// Check dependents
if (pluginInstance.dependents.length > 0) {
throw new Error(`Cannot unregister plugin ${pluginId} because other plugins depend on it: ${pluginInstance.dependents.join(', ')}`);
}
try {
// Disable first if active
if (pluginInstance.state === index_js_1.PluginState.ACTIVE) {
await this.disablePlugin(pluginId);
}
// Update state to unloading
pluginInstance.state = index_js_1.PluginState.UNLOADING;
// Clean up the plugin
await pluginInstance.plugin.cleanup();
// Remove all extensions from this plugin
this.extensionRegistry.removeExtensionsFromPlugin(pluginId);
// Remove from dependencies lists
for (const depId of pluginInstance.dependencies) {
const depPlugin = this.plugins.get(depId);
if (depPlugin) {
depPlugin.dependents = depPlugin.dependents.filter((id) => id !== pluginId);
}
}
// Remove commands from this plugin
for (const [cmdName, cmd] of this.commands.entries()) {
if (cmd.pluginId === pluginId) {
this.commands.delete(cmdName);
}
}
// Remove the plugin
this.plugins.delete(pluginId);
console.log(`Unregistered plugin: ${pluginInstance.plugin.manifest.name} (${pluginId})`);
}
catch (error) {
console.error(`Error unregistering plugin ${pluginId}:`, error);
throw error;
}
}
/**
* Get all registered plugins
*/
getPlugins() {
return Array.from(this.plugins.values()).map((instance) => instance.plugin);
}
/**
* Get plugin state
*/
getPluginState(pluginId) {
const pluginInstance = this.plugins.get(pluginId);
return pluginInstance ? pluginInstance.state : index_js_1.PluginState.ERROR;
}
/**
* Get all plugins in a specific state
*/
getPluginsByState(state) {
return Array.from(this.plugins.values())
.filter((instance) => instance.state === state)
.map((instance) => instance.plugin);
}
/**
* Get plugin by ID
*/
getPlugin(pluginId) {
const pluginInstance = this.plugins.get(pluginId);
return pluginInstance ? pluginInstance.plugin : undefined;
}
/**
* Execute a function on all extensions for a specific extension point
*/
async executeExtensions(extensionPointId, fn) {
return this.extensionRegistry.executeExtensions(extensionPointId, fn);
}
/**
* Get all registered commands
*/
getCommands() {
return Array.from(this.commands.values());
}
/**
* Get a specific command by name
*/
getCommand(name) {
return this.commands.get(name);
}
/**
* Execute a command
*/
async executeCommand(name, args, options) {
const command = this.commands.get(name);
if (!command) {
throw new Error(`Command ${name} not found`);
}
// Ensure plugin is active
const pluginId = command.pluginId;
if (pluginId && this.getPluginState(pluginId) !== index_js_1.PluginState.ACTIVE) {
throw new Error(`Cannot execute command ${name} because plugin ${pluginId} is not active`);
}
await command.action(args, options);
}
/**
* Extract dependencies from a plugin
*/
extractDependencies(plugin) {
const dependencies = [];
if (plugin.manifest.dependencies) {
for (const depId of Object.keys(plugin.manifest.dependencies)) {
dependencies.push(depId);
}
}
return dependencies;
}
/**
* Create a log function for a plugin
*/
createLogFunction(pluginId) {
return (level, message, data) => {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] [${level.toUpperCase()}] [${pluginId}] ${message}`;
switch (level) {
case 'debug':
console.debug(logMessage, data);
break;
case 'info':
console.info(logMessage, data);
break;
case 'warn':
console.warn(logMessage, data);
break;
case 'error':
console.error(logMessage, data);
break;
}
};
}
/**
* Create a register extension function for a plugin
*/
createRegisterExtensionFunction(pluginId) {
return (extension) => {
// Verify plugin is active
const pluginInstance = this.plugins.get(pluginId);
if (!pluginInstance || pluginInstance.state !== index_js_1.PluginState.ACTIVE) {
throw new Error(`Cannot register extension for inactive plugin ${pluginId}`);
}
this.extensionRegistry.registerExtension(pluginId, extension);
};
}
/**
* Create knowledge system API for a plugin
*/
createKnowledgeSystemAPI(pluginId) {
return {
storeKnowledge: (key, data, metadata) => {
return this.knowledgeSystem.storeKnowledge(`${pluginId}:${key}`, data, {
pluginId,
...metadata,
});
},
retrieveKnowledge: (key) => {
return this.knowledgeSystem.retrieveKnowledge(`${pluginId}:${key}`);
},
contributeToCore: (knowledge, metadata) => {
return this.knowledgeSystem.contributeToCore(knowledge, {
pluginId,
...metadata,
});
},
listKnowledgeKeys: (pattern) => {
return this.knowledgeSystem.listKnowledgeKeys(pluginId, pattern);
},
};
}
/**
* Register a CLI command provided by a plugin
*/
registerPluginCommand(pluginId, command) {
// Add pluginId to command
const fullCommand = {
...command,
pluginId,
};
// Check for existing command
if (this.commands.has(command.name)) {
throw new Error(`Command ${command.name} is already registered`);
}
this.commands.set(command.name, fullCommand);
console.log(`Registered command: ${command.name} from plugin ${pluginId}`);
}
/**
* Get plugin configuration
*/
getPluginConfig(pluginId) {
return this.config.get(pluginId) || {};
}
/**
* Set plugin configuration
*/
async setPluginConfig(pluginId, config) {
const oldConfig = this.getPluginConfig(pluginId);
this.config.set(pluginId, config);
// Notify plugin of config change if implemented
const pluginInstance = this.plugins.get(pluginId);
if (pluginInstance &&
pluginInstance.state === index_js_1.PluginState.ACTIVE &&
pluginInstance.plugin.onConfigChange) {
await pluginInstance.plugin.onConfigChange(config, oldConfig);
}
}
}
exports.PluginManager = PluginManager;
/**
* Create a plugin manager instance
*/
function createPluginManager(options) {
return new PluginManager(options);
}
//# sourceMappingURL=plugin-manager.js.map