Files
moxytool/ts/moxytool.classes.scriptrunner.ts

171 lines
5.3 KiB
TypeScript
Raw Permalink Normal View History

import * as plugins from './moxytool.plugins.ts';
import { logger } from './moxytool.logging.ts';
import type { IScriptMetadata } from './moxytool.classes.scriptindex.ts';
/**
* ScriptRunner class handles the execution of Proxmox community scripts
* - Executes scripts via bash with curl
* - Ensures proper stdin/stdout/stderr passthrough for interactive prompts
* - Handles script exit codes
*/
export class ScriptRunner {
private static readonly SCRIPT_BASE_URL =
'https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main';
/**
* Execute a community script
* @param script The script metadata
* @returns The exit code of the script
*/
public async execute(script: IScriptMetadata): Promise<number> {
try {
// Get the script URL from install_methods
if (!script.install_methods || script.install_methods.length === 0) {
logger.log('error', 'Script has no install methods defined');
return 1;
}
const installMethod = script.install_methods[0];
const scriptPath = installMethod.script;
if (!scriptPath) {
logger.log('error', 'Script path is not defined');
return 1;
}
// Construct the full script URL
const scriptUrl = `${ScriptRunner.SCRIPT_BASE_URL}${scriptPath}`;
logger.log('info', `Executing script: ${script.name}`);
logger.log('info', `URL: ${scriptUrl}`);
logger.log('info', '');
// Show script details
if (script.description) {
logger.log('info', `Description: ${script.description}`);
}
if (script.notes && script.notes.length > 0) {
logger.log('info', '');
logger.log('info', 'Important Notes:');
for (const note of script.notes) {
const prefix = note.type === 'warning' ? '⚠️ ' : ' ';
logger.log('warn', `${prefix}${note.content}`);
}
}
if (installMethod.resources) {
logger.log('info', '');
logger.log('info', 'Resource Requirements:');
if (installMethod.resources.cpu) {
logger.log('info', ` CPU: ${installMethod.resources.cpu} cores`);
}
if (installMethod.resources.ram) {
logger.log('info', ` RAM: ${installMethod.resources.ram} MB`);
}
if (installMethod.resources.hdd) {
logger.log('info', ` Disk: ${installMethod.resources.hdd} GB`);
}
if (installMethod.resources.os) {
logger.log(
'info',
` OS: ${installMethod.resources.os} ${installMethod.resources.version || ''}`,
);
}
}
logger.log('info', '');
logger.log('info', 'Starting installation...');
logger.log('info', '═'.repeat(60));
logger.log('info', '');
// Execute the script using smartshell
// The command structure: bash -c "$(curl -fsSL <url>)"
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
});
// Construct the command that will be executed
const command = `bash -c "$(curl -fsSL ${scriptUrl})"`;
// Execute with inherited stdio for full interactivity
const result = await smartshellInstance.exec(command);
logger.log('info', '');
logger.log('info', '═'.repeat(60));
if (result.exitCode === 0) {
logger.log('success', `✓ Script completed successfully`);
if (script.interface_port) {
logger.log('info', '');
logger.log('info', `Access the service at: http://<your-ip>:${script.interface_port}`);
}
if (script.default_credentials) {
logger.log('info', '');
logger.log('info', 'Default Credentials:');
if (script.default_credentials.username) {
logger.log('info', ` Username: ${script.default_credentials.username}`);
}
if (script.default_credentials.password) {
logger.log('info', ` Password: ${script.default_credentials.password}`);
}
}
if (script.documentation) {
logger.log('info', '');
logger.log('info', `Documentation: ${script.documentation}`);
}
} else {
logger.log('error', `✗ Script failed with exit code: ${result.exitCode}`);
if (result.stderr) {
logger.log('error', `Error output: ${result.stderr}`);
}
}
return result.exitCode;
} catch (error) {
logger.log('error', `Failed to execute script: ${error}`);
return 1;
}
}
/**
* Validate that we're running on a Proxmox host
*/
public async validateProxmoxHost(): Promise<boolean> {
try {
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
});
const result = await smartshellInstance.exec('which pveversion');
return result.exitCode === 0;
} catch {
return false;
}
}
/**
* Get Proxmox version information
*/
public async getProxmoxVersion(): Promise<string | null> {
try {
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
});
const result = await smartshellInstance.exec('pveversion');
if (result.exitCode === 0 && result.stdout) {
return result.stdout.trim();
}
return null;
} catch {
return null;
}
}
}