Add interactive plugin installation

This commit is contained in:
2026-04-17 02:36:00 +10:00
parent cd20c40d95
commit 4400d68f7b
2 changed files with 112 additions and 0 deletions

View File

@@ -4,6 +4,11 @@ import { join, resolve, dirname } from 'path';
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import { exec } from 'child_process';
import { promisify } from 'util';
import readline from 'readline';
const execAsync = promisify(exec);
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
@@ -291,6 +296,99 @@ export class PluginHost extends EventEmitter {
return Array.from(this.pluginRegistry.values()); return Array.from(this.pluginRegistry.values());
} }
/**
* Install a plugin from its repository
*/
async installPlugin(pluginId) {
const pluginDef = this.pluginRegistry.get(pluginId);
if (!pluginDef) {
throw new Error(`Plugin ${pluginId} not found in registry`);
}
const pluginPath = join(this.pluginsDir, pluginId);
if (existsSync(pluginPath)) {
console.log(`⚠ Plugin ${pluginId} already installed`);
return false;
}
console.log(`📥 Installing ${pluginDef.name} v${pluginDef.version}...`);
try {
await execAsync(`git clone ${pluginDef.repository} "${pluginPath}"`, {
cwd: this.pluginsDir
});
console.log(`✅ Installed ${pluginDef.name}`);
this.emit('plugin-installed', pluginDef);
return true;
} catch (error) {
console.error(`❌ Failed to install ${pluginId}:`, error.message);
return false;
}
}
/**
* Interactive plugin installation
*/
async interactiveInstall() {
const available = this.getRegistry();
const loadedIds = new Set(this.getLoadedPlugins().map(p => p.id));
const installedPluginDirs = existsSync(this.pluginsDir)
? await readdir(this.pluginsDir, { withFileTypes: true }).then(entries =>
entries.filter(e => e.isDirectory()).map(e => e.name)
)
: [];
const installedIds = new Set(installedPluginDirs);
const installable = available.filter(p => !installedIds.has(p.id));
if (installable.length === 0) {
console.log('\n✨ All available plugins are already installed!');
return;
}
console.log('\n📦 Available Plugins to Install:');
console.log('================================');
installable.forEach((p, i) => {
console.log(`${i + 1}. ${p.name} v${p.version} by ${p.author}`);
console.log(` ${p.description || 'No description'}`);
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const question = (query) => new Promise(resolve => rl.question(query, resolve));
console.log('\nEnter plugin numbers to install (comma-separated), or "all" for all plugins:');
const answer = await question('> ');
rl.close();
let toInstall = [];
if (answer.toLowerCase().trim() === 'all') {
toInstall = installable;
} else {
const indices = answer.split(',').map(s => parseInt(s.trim()) - 1).filter(i => !isNaN(i));
toInstall = indices.map(i => installable[i]).filter(Boolean);
}
if (toInstall.length === 0) {
console.log('\n❌ No valid plugins selected');
return;
}
console.log(`\n📥 Installing ${toInstall.length} plugin(s)...`);
for (const plugin of toInstall) {
await this.installPlugin(plugin.id);
}
console.log('\n✅ Installation complete!');
}
/** /**
* Shutdown the plugin host * Shutdown the plugin host
*/ */

View File

@@ -10,6 +10,20 @@ const host = new PluginHost({
console.log('🔌 Initializing Plugin Host...\n'); console.log('🔌 Initializing Plugin Host...\n');
await host.initialize(); await host.initialize();
// Check if user wants to install plugins interactively
const availableCount = host.getRegistry().length;
const alreadyInstalled = (await import('fs/promises')).readdir(host.pluginsDir, { withFileTypes: true })
.then(entries => entries.filter(e => e.isDirectory()).length)
.catch(() => 0);
const installedCount = await alreadyInstalled;
if (availableCount > installedCount) {
await host.interactiveInstall();
console.log('');
}
await host.loadPlugins(); await host.loadPlugins();
console.log('\n📊 Plugin Host Status:'); console.log('\n📊 Plugin Host Status:');