fix(cli): Improve CLI formatting, refine debug option filtering, and remove unused dgram import in SNMP manager

This commit is contained in:
Philipp Kunz 2025-03-26 17:49:50 +00:00
parent 87005e72f1
commit 588aeabf4b
5 changed files with 269 additions and 181 deletions

View File

@ -1,5 +1,12 @@
# Changelog # Changelog
## 2025-03-26 - 2.6.8 - fix(cli)
Improve CLI formatting, refine debug option filtering, and remove unused dgram import in SNMP manager
- Standardize whitespace and formatting in ts/cli.ts for consistency
- Refine argument filtering for debug mode and prompt messages
- Remove unused 'dgram' import from ts/snmp/manager.ts
## 2025-03-26 - 2.6.7 - fix(setup.sh) ## 2025-03-26 - 2.6.7 - fix(setup.sh)
Clarify net-snmp dependency installation message in setup.sh Clarify net-snmp dependency installation message in setup.sh

2
pnpm-lock.yaml generated
View File

@ -9,7 +9,7 @@ importers:
.: .:
dependencies: dependencies:
net-snmp: net-snmp:
specifier: ^3.20.0 specifier: 3.20.0
version: 3.20.0 version: 3.20.0
devDependencies: devDependencies:
'@git.zone/tsbuild': '@git.zone/tsbuild':

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/nupst', name: '@serve.zone/nupst',
version: '2.6.7', version: '2.6.8',
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices' description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
} }

438
ts/cli.ts
View File

@ -30,7 +30,7 @@ export class NupstCli {
// Enable debug mode in the SNMP client // Enable debug mode in the SNMP client
this.nupst.getSnmp().enableDebug(); this.nupst.getSnmp().enableDebug();
} }
// Get the command (default to help if none provided) // Get the command (default to help if none provided)
const command = args[2] || 'help'; const command = args[2] || 'help';
@ -46,8 +46,8 @@ export class NupstCli {
private extractDebugOptions(args: string[]): { debugMode: boolean; cleanedArgs: string[] } { private extractDebugOptions(args: string[]): { debugMode: boolean; cleanedArgs: string[] } {
const debugMode = args.includes('--debug') || args.includes('-d'); const debugMode = args.includes('--debug') || args.includes('-d');
// Remove debug flags from args // Remove debug flags from args
const cleanedArgs = args.filter(arg => arg !== '--debug' && arg !== '-d'); const cleanedArgs = args.filter((arg) => arg !== '--debug' && arg !== '-d');
return { debugMode, cleanedArgs }; return { debugMode, cleanedArgs };
} }
@ -85,23 +85,23 @@ export class NupstCli {
case 'disable': case 'disable':
await this.disable(); await this.disable();
break; break;
case 'setup': case 'setup':
await this.setup(); await this.setup();
break; break;
case 'test': case 'test':
await this.test(debugMode); await this.test(debugMode);
break; break;
case 'update': case 'update':
await this.update(); await this.update();
break; break;
case 'uninstall': case 'uninstall':
await this.uninstall(); await this.uninstall();
break; break;
case 'config': case 'config':
await this.showConfig(); await this.showConfig();
break; break;
@ -149,17 +149,17 @@ export class NupstCli {
// Use exec with spawn to properly follow logs in real-time // Use exec with spawn to properly follow logs in real-time
const { spawn } = await import('child_process'); const { spawn } = await import('child_process');
console.log('Tailing nupst service logs (Ctrl+C to exit)...\n'); console.log('Tailing nupst service logs (Ctrl+C to exit)...\n');
const journalctl = spawn('journalctl', ['-u', 'nupst.service', '-n', '50', '-f'], { const journalctl = spawn('journalctl', ['-u', 'nupst.service', '-n', '50', '-f'], {
stdio: ['ignore', 'inherit', 'inherit'] stdio: ['ignore', 'inherit', 'inherit'],
}); });
// Forward signals to child process // Forward signals to child process
process.on('SIGINT', () => { process.on('SIGINT', () => {
journalctl.kill('SIGINT'); journalctl.kill('SIGINT');
process.exit(0); process.exit(0);
}); });
// Wait for process to exit // Wait for process to exit
await new Promise<void>((resolve) => { await new Promise<void>((resolve) => {
journalctl.on('exit', () => resolve()); journalctl.on('exit', () => resolve());
@ -229,21 +229,21 @@ export class NupstCli {
console.log('│ SNMP debugging enabled - detailed logs will be shown'); console.log('│ SNMP debugging enabled - detailed logs will be shown');
console.log('└──────────────────────────────────────────┘'); console.log('└──────────────────────────────────────────┘');
} }
// Try to load the configuration // Try to load the configuration
try { try {
await this.nupst.getDaemon().loadConfig(); await this.nupst.getDaemon().loadConfig();
} catch (error) { } catch (error) {
console.error('┌─ Configuration Error ─────────────────────┐'); console.error('┌─ Configuration Error ─────────────────────┐');
console.error('│ No configuration found.'); console.error('│ No configuration found.');
console.error('│ Please run \'nupst setup\' first to create a configuration.'); console.error("│ Please run 'nupst setup' first to create a configuration.");
console.error('└──────────────────────────────────────────┘'); console.error('└──────────────────────────────────────────┘');
return; return;
} }
// Get current configuration // Get current configuration
const config = this.nupst.getDaemon().getConfig(); const config = this.nupst.getDaemon().getConfig();
this.displayTestConfig(config); this.displayTestConfig(config);
await this.testConnection(config); await this.testConnection(config);
} catch (error) { } catch (error) {
@ -262,26 +262,26 @@ export class NupstCli {
console.log(`│ Port: ${config.snmp.port}`); console.log(`│ Port: ${config.snmp.port}`);
console.log(`│ Version: ${config.snmp.version}`); console.log(`│ Version: ${config.snmp.version}`);
console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`); console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`);
if (config.snmp.version === 1 || config.snmp.version === 2) { if (config.snmp.version === 1 || config.snmp.version === 2) {
console.log(`│ Community: ${config.snmp.community}`); console.log(`│ Community: ${config.snmp.community}`);
} else if (config.snmp.version === 3) { } else if (config.snmp.version === 3) {
console.log(`│ Security Level: ${config.snmp.securityLevel}`); console.log(`│ Security Level: ${config.snmp.securityLevel}`);
console.log(`│ Username: ${config.snmp.username}`); console.log(`│ Username: ${config.snmp.username}`);
// Show auth and privacy details based on security level // Show auth and privacy details based on security level
if (config.snmp.securityLevel === 'authNoPriv' || config.snmp.securityLevel === 'authPriv') { if (config.snmp.securityLevel === 'authNoPriv' || config.snmp.securityLevel === 'authPriv') {
console.log(`│ Auth Protocol: ${config.snmp.authProtocol || 'None'}`); console.log(`│ Auth Protocol: ${config.snmp.authProtocol || 'None'}`);
} }
if (config.snmp.securityLevel === 'authPriv') { if (config.snmp.securityLevel === 'authPriv') {
console.log(`│ Privacy Protocol: ${config.snmp.privProtocol || 'None'}`); console.log(`│ Privacy Protocol: ${config.snmp.privProtocol || 'None'}`);
} }
// Show timeout value // Show timeout value
console.log(`│ Timeout: ${config.snmp.timeout / 1000} seconds`); console.log(`│ Timeout: ${config.snmp.timeout / 1000} seconds`);
} }
// Show OIDs if custom model is selected // Show OIDs if custom model is selected
if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) { if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) {
console.log('│ Custom OIDs:'); console.log('│ Custom OIDs:');
@ -304,20 +304,20 @@ export class NupstCli {
console.log('\nTesting connection to UPS...'); console.log('\nTesting connection to UPS...');
try { try {
// Create a test config with a short timeout // Create a test config with a short timeout
const testConfig = { const testConfig = {
...config.snmp, ...config.snmp,
timeout: Math.min(config.snmp.timeout, 10000) // Use at most 10 seconds for testing timeout: Math.min(config.snmp.timeout, 10000), // Use at most 10 seconds for testing
}; };
const status = await this.nupst.getSnmp().getUpsStatus(testConfig); const status = await this.nupst.getSnmp().getUpsStatus(testConfig);
console.log('┌─ Connection Successful! ─────────────────┐'); console.log('┌─ Connection Successful! ─────────────────┐');
console.log('│ UPS Status:'); console.log('│ UPS Status:');
console.log(`│ Power Status: ${status.powerStatus}`); console.log(`│ Power Status: ${status.powerStatus}`);
console.log(`│ Battery Capacity: ${status.batteryCapacity}%`); console.log(`│ Battery Capacity: ${status.batteryCapacity}%`);
console.log(`│ Runtime Remaining: ${status.batteryRuntime} minutes`); console.log(`│ Runtime Remaining: ${status.batteryRuntime} minutes`);
console.log('└──────────────────────────────────────────┘'); console.log('└──────────────────────────────────────────┘');
// Check status against thresholds if on battery // Check status against thresholds if on battery
if (status.powerStatus === 'onBattery') { if (status.powerStatus === 'onBattery') {
this.analyzeThresholds(status, config); this.analyzeThresholds(status, config);
@ -326,7 +326,7 @@ export class NupstCli {
console.error('┌─ Connection Failed! ───────────────────────┐'); console.error('┌─ Connection Failed! ───────────────────────┐');
console.error(`│ Error: ${error.message}`); console.error(`│ Error: ${error.message}`);
console.error('└──────────────────────────────────────────┘'); console.error('└──────────────────────────────────────────┘');
console.log('\nPlease check your settings and run \'nupst setup\' to reconfigure.'); console.log("\nPlease check your settings and run 'nupst setup' to reconfigure.");
} }
} }
@ -337,25 +337,33 @@ export class NupstCli {
*/ */
private analyzeThresholds(status: any, config: any): void { private analyzeThresholds(status: any, config: any): void {
console.log('┌─ Threshold Analysis ───────────────────────┐'); console.log('┌─ Threshold Analysis ───────────────────────┐');
if (status.batteryCapacity < config.thresholds.battery) { if (status.batteryCapacity < config.thresholds.battery) {
console.log('│ ⚠️ WARNING: Battery capacity below threshold'); console.log('│ ⚠️ WARNING: Battery capacity below threshold');
console.log(`│ Current: ${status.batteryCapacity}% | Threshold: ${config.thresholds.battery}%`); console.log(
`│ Current: ${status.batteryCapacity}% | Threshold: ${config.thresholds.battery}%`
);
console.log('│ System would initiate shutdown'); console.log('│ System would initiate shutdown');
} else { } else {
console.log('│ ✓ Battery capacity above threshold'); console.log('│ ✓ Battery capacity above threshold');
console.log(`│ Current: ${status.batteryCapacity}% | Threshold: ${config.thresholds.battery}%`); console.log(
`│ Current: ${status.batteryCapacity}% | Threshold: ${config.thresholds.battery}%`
);
} }
if (status.batteryRuntime < config.thresholds.runtime) { if (status.batteryRuntime < config.thresholds.runtime) {
console.log('│ ⚠️ WARNING: Runtime below threshold'); console.log('│ ⚠️ WARNING: Runtime below threshold');
console.log(`│ Current: ${status.batteryRuntime} min | Threshold: ${config.thresholds.runtime} min`); console.log(
`│ Current: ${status.batteryRuntime} min | Threshold: ${config.thresholds.runtime} min`
);
console.log('│ System would initiate shutdown'); console.log('│ System would initiate shutdown');
} else { } else {
console.log('│ ✓ Runtime above threshold'); console.log('│ ✓ Runtime above threshold');
console.log(`│ Current: ${status.batteryRuntime} min | Threshold: ${config.thresholds.runtime} min`); console.log(
`│ Current: ${status.batteryRuntime} min | Threshold: ${config.thresholds.runtime} min`
);
} }
console.log('└──────────────────────────────────────────┘'); console.log('└──────────────────────────────────────────┘');
} }
@ -386,60 +394,72 @@ Options:
(Example: nupst test --debug) (Example: nupst test --debug)
`); `);
} }
/** /**
* Update NUPST from repository and refresh systemd service * Update NUPST from repository and refresh systemd service
*/ */
private async update(): Promise<void> { private async update(): Promise<void> {
try { try {
// Check if running as root // Check if running as root
this.checkRootAccess('This command must be run as root to update NUPST and refresh the systemd service.'); this.checkRootAccess(
'This command must be run as root to update NUPST and refresh the systemd service.'
);
console.log('┌─ NUPST Update Process ──────────────────┐'); console.log('┌─ NUPST Update Process ──────────────────┐');
console.log('│ Updating NUPST from repository...'); console.log('│ Updating NUPST from repository...');
// Determine the installation directory (assuming it's either /opt/nupst or the current directory) // Determine the installation directory (assuming it's either /opt/nupst or the current directory)
const { existsSync } = await import('fs'); const { existsSync } = await import('fs');
let installDir = '/opt/nupst'; let installDir = '/opt/nupst';
if (!existsSync(installDir)) { if (!existsSync(installDir)) {
// If not installed in /opt/nupst, use the current directory // If not installed in /opt/nupst, use the current directory
const { dirname } = await import('path'); const { dirname } = await import('path');
installDir = dirname(dirname(process.argv[1])); // Go up two levels from the executable installDir = dirname(dirname(process.argv[1])); // Go up two levels from the executable
console.log(`│ Using local installation directory: ${installDir}`); console.log(`│ Using local installation directory: ${installDir}`);
} }
try { try {
// 1. Update the repository // 1. Update the repository
console.log('│ Pulling latest changes from git repository...'); console.log('│ Pulling latest changes from git repository...');
execSync(`cd ${installDir} && git fetch origin && git reset --hard origin/main`, { stdio: 'pipe' }); execSync(`cd ${installDir} && git fetch origin && git reset --hard origin/main`, {
stdio: 'pipe',
});
// 2. Run the install.sh script // 2. Run the install.sh script
console.log('│ Running install.sh to update NUPST...'); console.log('│ Running install.sh to update NUPST...');
execSync(`cd ${installDir} && bash ./install.sh`, { stdio: 'pipe' }); execSync(`cd ${installDir} && bash ./install.sh`, { stdio: 'pipe' });
// 3. Run the setup.sh script with force flag to update Node.js and dependencies // 3. Run the setup.sh script with force flag to update Node.js and dependencies
console.log('│ Running setup.sh to update Node.js and dependencies...'); console.log('│ Running setup.sh to update Node.js and dependencies...');
execSync(`cd ${installDir} && bash ./setup.sh --force`, { stdio: 'pipe' }); execSync(`cd ${installDir} && bash ./setup.sh --force`, { stdio: 'pipe' });
// 4. Refresh the systemd service // 4. Refresh the systemd service
console.log('│ Refreshing systemd service...'); console.log('│ Refreshing systemd service...');
// First check if service exists // First check if service exists
const serviceExists = execSync('systemctl list-unit-files | grep nupst.service').toString().includes('nupst.service'); let serviceExists = false;
try {
const output = execSync('systemctl list-unit-files | grep nupst.service').toString();
serviceExists = output.includes('nupst.service');
} catch (error) {
// If grep fails (service not found), serviceExists remains false
serviceExists = false;
}
if (serviceExists) { if (serviceExists) {
// Stop the service if it's running // Stop the service if it's running
const isRunning = execSync('systemctl is-active nupst.service || true').toString().trim() === 'active'; const isRunning =
execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
if (isRunning) { if (isRunning) {
console.log('│ Stopping nupst service...'); console.log('│ Stopping nupst service...');
execSync('systemctl stop nupst.service'); execSync('systemctl stop nupst.service');
} }
// Reinstall the service // Reinstall the service
console.log('│ Reinstalling systemd service...'); console.log('│ Reinstalling systemd service...');
await this.nupst.getSystemd().install(); await this.nupst.getSystemd().install();
// Restart the service if it was running // Restart the service if it was running
if (isRunning) { if (isRunning) {
console.log('│ Restarting nupst service...'); console.log('│ Restarting nupst service...');
@ -449,7 +469,7 @@ Options:
console.log('│ Systemd service not installed, skipping service refresh.'); console.log('│ Systemd service not installed, skipping service refresh.');
console.log('│ Run "nupst enable" to install the service.'); console.log('│ Run "nupst enable" to install the service.');
} }
console.log('│ Update completed successfully!'); console.log('│ Update completed successfully!');
console.log('└──────────────────────────────────────────┘'); console.log('└──────────────────────────────────────────┘');
} catch (error) { } catch (error) {
@ -463,7 +483,7 @@ Options:
process.exit(1); process.exit(1);
} }
} }
/** /**
* Interactive setup for configuring SNMP settings * Interactive setup for configuring SNMP settings
*/ */
@ -471,12 +491,12 @@ Options:
try { try {
// Import readline module (ESM style) // Import readline module (ESM style)
const readline = await import('readline'); const readline = await import('readline');
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: process.stdout,
}); });
// Helper function to prompt for input // Helper function to prompt for input
const prompt = (question: string): Promise<string> => { const prompt = (question: string): Promise<string> => {
return new Promise((resolve) => { return new Promise((resolve) => {
@ -485,7 +505,7 @@ Options:
}); });
}); });
}; };
try { try {
await this.runSetupProcess(prompt); await this.runSetupProcess(prompt);
} finally { } finally {
@ -504,7 +524,7 @@ Options:
console.log('\nNUPST Interactive Setup'); console.log('\nNUPST Interactive Setup');
console.log('======================\n'); console.log('======================\n');
console.log('This will guide you through configuring your UPS SNMP settings.\n'); console.log('This will guide you through configuring your UPS SNMP settings.\n');
// Try to load existing config if available // Try to load existing config if available
let config; let config;
try { try {
@ -518,24 +538,24 @@ Options:
// Gather SNMP settings // Gather SNMP settings
config = await this.gatherSnmpSettings(config, prompt); config = await this.gatherSnmpSettings(config, prompt);
// Gather threshold settings // Gather threshold settings
config = await this.gatherThresholdSettings(config, prompt); config = await this.gatherThresholdSettings(config, prompt);
// Gather UPS model settings // Gather UPS model settings
config = await this.gatherUpsModelSettings(config, prompt); config = await this.gatherUpsModelSettings(config, prompt);
// Save the configuration // Save the configuration
await this.nupst.getDaemon().saveConfig(config); await this.nupst.getDaemon().saveConfig(config);
this.displayConfigSummary(config); this.displayConfigSummary(config);
// Test the connection if requested // Test the connection if requested
await this.optionallyTestConnection(config, prompt); await this.optionallyTestConnection(config, prompt);
// Check if service is running and restart it if needed // Check if service is running and restart it if needed
await this.restartServiceIfRunning(); await this.restartServiceIfRunning();
console.log('\nSetup complete!'); console.log('\nSetup complete!');
await this.optionallyEnableService(prompt); await this.optionallyEnableService(prompt);
} }
@ -546,18 +566,21 @@ Options:
* @param prompt Function to prompt for user input * @param prompt Function to prompt for user input
* @returns Updated configuration * @returns Updated configuration
*/ */
private async gatherSnmpSettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> { private async gatherSnmpSettings(
config: any,
prompt: (question: string) => Promise<string>
): Promise<any> {
// SNMP IP Address // SNMP IP Address
const defaultHost = config.snmp.host; const defaultHost = config.snmp.host;
const host = await prompt(`UPS IP Address [${defaultHost}]: `); const host = await prompt(`UPS IP Address [${defaultHost}]: `);
config.snmp.host = host.trim() || defaultHost; config.snmp.host = host.trim() || defaultHost;
// SNMP Port // SNMP Port
const defaultPort = config.snmp.port; const defaultPort = config.snmp.port;
const portInput = await prompt(`SNMP Port [${defaultPort}]: `); const portInput = await prompt(`SNMP Port [${defaultPort}]: `);
const port = parseInt(portInput, 10); const port = parseInt(portInput, 10);
config.snmp.port = (portInput.trim() && !isNaN(port)) ? port : defaultPort; config.snmp.port = portInput.trim() && !isNaN(port) ? port : defaultPort;
// SNMP Version // SNMP Version
const defaultVersion = config.snmp.version; const defaultVersion = config.snmp.version;
console.log('\nSNMP Version:'); console.log('\nSNMP Version:');
@ -566,8 +589,11 @@ Options:
console.log(' 3) SNMPv3 (with security features)'); console.log(' 3) SNMPv3 (with security features)');
const versionInput = await prompt(`Select SNMP version [${defaultVersion}]: `); const versionInput = await prompt(`Select SNMP version [${defaultVersion}]: `);
const version = parseInt(versionInput, 10); const version = parseInt(versionInput, 10);
config.snmp.version = (versionInput.trim() && (version === 1 || version === 2 || version === 3)) ? version : defaultVersion; config.snmp.version =
versionInput.trim() && (version === 1 || version === 2 || version === 3)
? version
: defaultVersion;
if (config.snmp.version === 1 || config.snmp.version === 2) { if (config.snmp.version === 1 || config.snmp.version === 2) {
// SNMP Community String (for v1/v2c) // SNMP Community String (for v1/v2c)
const defaultCommunity = config.snmp.community || 'public'; const defaultCommunity = config.snmp.community || 'public';
@ -577,7 +603,7 @@ Options:
// SNMP v3 settings // SNMP v3 settings
config = await this.gatherSnmpV3Settings(config, prompt); config = await this.gatherSnmpV3Settings(config, prompt);
} }
return config; return config;
} }
@ -587,20 +613,27 @@ Options:
* @param prompt Function to prompt for user input * @param prompt Function to prompt for user input
* @returns Updated configuration * @returns Updated configuration
*/ */
private async gatherSnmpV3Settings(config: any, prompt: (question: string) => Promise<string>): Promise<any> { private async gatherSnmpV3Settings(
config: any,
prompt: (question: string) => Promise<string>
): Promise<any> {
console.log('\nSNMPv3 Security Settings:'); console.log('\nSNMPv3 Security Settings:');
// Security Level // Security Level
console.log('\nSecurity Level:'); console.log('\nSecurity Level:');
console.log(' 1) noAuthNoPriv (No Authentication, No Privacy)'); console.log(' 1) noAuthNoPriv (No Authentication, No Privacy)');
console.log(' 2) authNoPriv (Authentication, No Privacy)'); console.log(' 2) authNoPriv (Authentication, No Privacy)');
console.log(' 3) authPriv (Authentication and Privacy)'); console.log(' 3) authPriv (Authentication and Privacy)');
const defaultSecLevel = config.snmp.securityLevel ? const defaultSecLevel = config.snmp.securityLevel
(config.snmp.securityLevel === 'noAuthNoPriv' ? 1 : ? config.snmp.securityLevel === 'noAuthNoPriv'
config.snmp.securityLevel === 'authNoPriv' ? 2 : 3) : 3; ? 1
: config.snmp.securityLevel === 'authNoPriv'
? 2
: 3
: 3;
const secLevelInput = await prompt(`Select Security Level [${defaultSecLevel}]: `); const secLevelInput = await prompt(`Select Security Level [${defaultSecLevel}]: `);
const secLevel = parseInt(secLevelInput, 10) || defaultSecLevel; const secLevel = parseInt(secLevelInput, 10) || defaultSecLevel;
if (secLevel === 1) { if (secLevel === 1) {
config.snmp.securityLevel = 'noAuthNoPriv'; config.snmp.securityLevel = 'noAuthNoPriv';
// No auth, no priv - clear out authentication and privacy settings // No auth, no priv - clear out authentication and privacy settings
@ -622,31 +655,33 @@ Options:
// Set appropriate timeout for security level // Set appropriate timeout for security level
config.snmp.timeout = 15000; // 15 seconds for full encryption config.snmp.timeout = 15000; // 15 seconds for full encryption
} }
// Username // Username
const defaultUsername = config.snmp.username || ''; const defaultUsername = config.snmp.username || '';
const username = await prompt(`SNMPv3 Username [${defaultUsername}]: `); const username = await prompt(`SNMPv3 Username [${defaultUsername}]: `);
config.snmp.username = username.trim() || defaultUsername; config.snmp.username = username.trim() || defaultUsername;
if (secLevel >= 2) { if (secLevel >= 2) {
// Authentication settings // Authentication settings
config = await this.gatherAuthenticationSettings(config, prompt); config = await this.gatherAuthenticationSettings(config, prompt);
if (secLevel === 3) { if (secLevel === 3) {
// Privacy settings // Privacy settings
config = await this.gatherPrivacySettings(config, prompt); config = await this.gatherPrivacySettings(config, prompt);
} }
// Allow customizing the timeout value // Allow customizing the timeout value
const defaultTimeout = config.snmp.timeout / 1000; // Convert from ms to seconds for display const defaultTimeout = config.snmp.timeout / 1000; // Convert from ms to seconds for display
console.log('\nSNMPv3 operations with authentication and privacy may require longer timeouts.'); console.log(
'\nSNMPv3 operations with authentication and privacy may require longer timeouts.'
);
const timeoutInput = await prompt(`SNMP Timeout in seconds [${defaultTimeout}]: `); const timeoutInput = await prompt(`SNMP Timeout in seconds [${defaultTimeout}]: `);
const timeout = parseInt(timeoutInput, 10); const timeout = parseInt(timeoutInput, 10);
if (timeoutInput.trim() && !isNaN(timeout)) { if (timeoutInput.trim() && !isNaN(timeout)) {
config.snmp.timeout = timeout * 1000; // Convert to ms config.snmp.timeout = timeout * 1000; // Convert to ms
} }
} }
return config; return config;
} }
@ -656,21 +691,26 @@ Options:
* @param prompt Function to prompt for user input * @param prompt Function to prompt for user input
* @returns Updated configuration * @returns Updated configuration
*/ */
private async gatherAuthenticationSettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> { private async gatherAuthenticationSettings(
config: any,
prompt: (question: string) => Promise<string>
): Promise<any> {
// Authentication protocol // Authentication protocol
console.log('\nAuthentication Protocol:'); console.log('\nAuthentication Protocol:');
console.log(' 1) MD5'); console.log(' 1) MD5');
console.log(' 2) SHA'); console.log(' 2) SHA');
const defaultAuthProtocol = config.snmp.authProtocol === 'SHA' ? 2 : 1; const defaultAuthProtocol = config.snmp.authProtocol === 'SHA' ? 2 : 1;
const authProtocolInput = await prompt(`Select Authentication Protocol [${defaultAuthProtocol}]: `); const authProtocolInput = await prompt(
`Select Authentication Protocol [${defaultAuthProtocol}]: `
);
const authProtocol = parseInt(authProtocolInput, 10) || defaultAuthProtocol; const authProtocol = parseInt(authProtocolInput, 10) || defaultAuthProtocol;
config.snmp.authProtocol = authProtocol === 2 ? 'SHA' : 'MD5'; config.snmp.authProtocol = authProtocol === 2 ? 'SHA' : 'MD5';
// Authentication Key/Password // Authentication Key/Password
const defaultAuthKey = config.snmp.authKey || ''; const defaultAuthKey = config.snmp.authKey || '';
const authKey = await prompt(`Authentication Password ${defaultAuthKey ? '[*****]' : ''}: `); const authKey = await prompt(`Authentication Password ${defaultAuthKey ? '[*****]' : ''}: `);
config.snmp.authKey = authKey.trim() || defaultAuthKey; config.snmp.authKey = authKey.trim() || defaultAuthKey;
return config; return config;
} }
@ -680,7 +720,10 @@ Options:
* @param prompt Function to prompt for user input * @param prompt Function to prompt for user input
* @returns Updated configuration * @returns Updated configuration
*/ */
private async gatherPrivacySettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> { private async gatherPrivacySettings(
config: any,
prompt: (question: string) => Promise<string>
): Promise<any> {
// Privacy protocol // Privacy protocol
console.log('\nPrivacy Protocol:'); console.log('\nPrivacy Protocol:');
console.log(' 1) DES'); console.log(' 1) DES');
@ -689,12 +732,12 @@ Options:
const privProtocolInput = await prompt(`Select Privacy Protocol [${defaultPrivProtocol}]: `); const privProtocolInput = await prompt(`Select Privacy Protocol [${defaultPrivProtocol}]: `);
const privProtocol = parseInt(privProtocolInput, 10) || defaultPrivProtocol; const privProtocol = parseInt(privProtocolInput, 10) || defaultPrivProtocol;
config.snmp.privProtocol = privProtocol === 2 ? 'AES' : 'DES'; config.snmp.privProtocol = privProtocol === 2 ? 'AES' : 'DES';
// Privacy Key/Password // Privacy Key/Password
const defaultPrivKey = config.snmp.privKey || ''; const defaultPrivKey = config.snmp.privKey || '';
const privKey = await prompt(`Privacy Password ${defaultPrivKey ? '[*****]' : ''}: `); const privKey = await prompt(`Privacy Password ${defaultPrivKey ? '[*****]' : ''}: `);
config.snmp.privKey = privKey.trim() || defaultPrivKey; config.snmp.privKey = privKey.trim() || defaultPrivKey;
return config; return config;
} }
@ -704,33 +747,43 @@ Options:
* @param prompt Function to prompt for user input * @param prompt Function to prompt for user input
* @returns Updated configuration * @returns Updated configuration
*/ */
private async gatherThresholdSettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> { private async gatherThresholdSettings(
config: any,
prompt: (question: string) => Promise<string>
): Promise<any> {
console.log('\nShutdown Thresholds:'); console.log('\nShutdown Thresholds:');
// Battery threshold // Battery threshold
const defaultBatteryThreshold = config.thresholds.battery; const defaultBatteryThreshold = config.thresholds.battery;
const batteryThresholdInput = await prompt(`Battery percentage threshold [${defaultBatteryThreshold}%]: `); const batteryThresholdInput = await prompt(
`Battery percentage threshold [${defaultBatteryThreshold}%]: `
);
const batteryThreshold = parseInt(batteryThresholdInput, 10); const batteryThreshold = parseInt(batteryThresholdInput, 10);
config.thresholds.battery = (batteryThresholdInput.trim() && !isNaN(batteryThreshold)) config.thresholds.battery =
? batteryThreshold batteryThresholdInput.trim() && !isNaN(batteryThreshold)
: defaultBatteryThreshold; ? batteryThreshold
: defaultBatteryThreshold;
// Runtime threshold // Runtime threshold
const defaultRuntimeThreshold = config.thresholds.runtime; const defaultRuntimeThreshold = config.thresholds.runtime;
const runtimeThresholdInput = await prompt(`Runtime minutes threshold [${defaultRuntimeThreshold} minutes]: `); const runtimeThresholdInput = await prompt(
`Runtime minutes threshold [${defaultRuntimeThreshold} minutes]: `
);
const runtimeThreshold = parseInt(runtimeThresholdInput, 10); const runtimeThreshold = parseInt(runtimeThresholdInput, 10);
config.thresholds.runtime = (runtimeThresholdInput.trim() && !isNaN(runtimeThreshold)) config.thresholds.runtime =
? runtimeThreshold runtimeThresholdInput.trim() && !isNaN(runtimeThreshold)
: defaultRuntimeThreshold; ? runtimeThreshold
: defaultRuntimeThreshold;
// Check interval // Check interval
const defaultInterval = config.checkInterval / 1000; // Convert from ms to seconds for display const defaultInterval = config.checkInterval / 1000; // Convert from ms to seconds for display
const intervalInput = await prompt(`Check interval in seconds [${defaultInterval}]: `); const intervalInput = await prompt(`Check interval in seconds [${defaultInterval}]: `);
const interval = parseInt(intervalInput, 10); const interval = parseInt(intervalInput, 10);
config.checkInterval = (intervalInput.trim() && !isNaN(interval)) config.checkInterval =
? interval * 1000 // Convert to ms intervalInput.trim() && !isNaN(interval)
: defaultInterval * 1000; ? interval * 1000 // Convert to ms
: defaultInterval * 1000;
return config; return config;
} }
@ -740,7 +793,10 @@ Options:
* @param prompt Function to prompt for user input * @param prompt Function to prompt for user input
* @returns Updated configuration * @returns Updated configuration
*/ */
private async gatherUpsModelSettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> { private async gatherUpsModelSettings(
config: any,
prompt: (question: string) => Promise<string>
): Promise<any> {
console.log('\nUPS Model Selection:'); console.log('\nUPS Model Selection:');
console.log(' 1) CyberPower'); console.log(' 1) CyberPower');
console.log(' 2) APC'); console.log(' 2) APC');
@ -748,17 +804,25 @@ Options:
console.log(' 4) TrippLite'); console.log(' 4) TrippLite');
console.log(' 5) Liebert/Vertiv'); console.log(' 5) Liebert/Vertiv');
console.log(' 6) Custom (Advanced)'); console.log(' 6) Custom (Advanced)');
const defaultModelValue = config.snmp.upsModel === 'cyberpower' ? 1 : const defaultModelValue =
config.snmp.upsModel === 'apc' ? 2 : config.snmp.upsModel === 'cyberpower'
config.snmp.upsModel === 'eaton' ? 3 : ? 1
config.snmp.upsModel === 'tripplite' ? 4 : : config.snmp.upsModel === 'apc'
config.snmp.upsModel === 'liebert' ? 5 : ? 2
config.snmp.upsModel === 'custom' ? 6 : 1; : config.snmp.upsModel === 'eaton'
? 3
: config.snmp.upsModel === 'tripplite'
? 4
: config.snmp.upsModel === 'liebert'
? 5
: config.snmp.upsModel === 'custom'
? 6
: 1;
const modelInput = await prompt(`Select UPS model [${defaultModelValue}]: `); const modelInput = await prompt(`Select UPS model [${defaultModelValue}]: `);
const modelValue = parseInt(modelInput, 10) || defaultModelValue; const modelValue = parseInt(modelInput, 10) || defaultModelValue;
if (modelValue === 1) { if (modelValue === 1) {
config.snmp.upsModel = 'cyberpower'; config.snmp.upsModel = 'cyberpower';
} else if (modelValue === 2) { } else if (modelValue === 2) {
@ -773,20 +837,20 @@ Options:
config.snmp.upsModel = 'custom'; config.snmp.upsModel = 'custom';
console.log('\nEnter custom OIDs for your UPS:'); console.log('\nEnter custom OIDs for your UPS:');
console.log('(Leave blank to use standard RFC 1628 OIDs as fallback)'); console.log('(Leave blank to use standard RFC 1628 OIDs as fallback)');
// Custom OIDs // Custom OIDs
const powerStatusOID = await prompt('Power Status OID: '); const powerStatusOID = await prompt('Power Status OID: ');
const batteryCapacityOID = await prompt('Battery Capacity OID: '); const batteryCapacityOID = await prompt('Battery Capacity OID: ');
const batteryRuntimeOID = await prompt('Battery Runtime OID: '); const batteryRuntimeOID = await prompt('Battery Runtime OID: ');
// Create custom OIDs object // Create custom OIDs object
config.snmp.customOIDs = { config.snmp.customOIDs = {
POWER_STATUS: powerStatusOID.trim(), POWER_STATUS: powerStatusOID.trim(),
BATTERY_CAPACITY: batteryCapacityOID.trim(), BATTERY_CAPACITY: batteryCapacityOID.trim(),
BATTERY_RUNTIME: batteryRuntimeOID.trim() BATTERY_RUNTIME: batteryRuntimeOID.trim(),
}; };
} }
return config; return config;
} }
@ -799,8 +863,10 @@ Options:
console.log(`│ SNMP Host: ${config.snmp.host}:${config.snmp.port}`); console.log(`│ SNMP Host: ${config.snmp.host}:${config.snmp.port}`);
console.log(`│ SNMP Version: ${config.snmp.version}`); console.log(`│ SNMP Version: ${config.snmp.version}`);
console.log(`│ UPS Model: ${config.snmp.upsModel}`); console.log(`│ UPS Model: ${config.snmp.upsModel}`);
console.log(`│ Thresholds: ${config.thresholds.battery}% battery, ${config.thresholds.runtime} min runtime`); console.log(
console.log(`│ Check Interval: ${config.checkInterval/1000} seconds`); `│ Thresholds: ${config.thresholds.battery}% battery, ${config.thresholds.runtime} min runtime`
);
console.log(`│ Check Interval: ${config.checkInterval / 1000} seconds`);
console.log('└──────────────────────────────────────────┘\n'); console.log('└──────────────────────────────────────────┘\n');
} }
@ -809,17 +875,22 @@ Options:
* @param config Current configuration * @param config Current configuration
* @param prompt Function to prompt for user input * @param prompt Function to prompt for user input
*/ */
private async optionallyTestConnection(config: any, prompt: (question: string) => Promise<string>): Promise<void> { private async optionallyTestConnection(
const testConnection = await prompt('Would you like to test the connection to your UPS? (y/N): '); config: any,
prompt: (question: string) => Promise<string>
): Promise<void> {
const testConnection = await prompt(
'Would you like to test the connection to your UPS? (y/N): '
);
if (testConnection.toLowerCase() === 'y') { if (testConnection.toLowerCase() === 'y') {
console.log('\nTesting connection to UPS...'); console.log('\nTesting connection to UPS...');
try { try {
// Create a test config with a short timeout // Create a test config with a short timeout
const testConfig = { const testConfig = {
...config.snmp, ...config.snmp,
timeout: Math.min(config.snmp.timeout, 10000) // Use at most 10 seconds for testing timeout: Math.min(config.snmp.timeout, 10000), // Use at most 10 seconds for testing
}; };
const status = await this.nupst.getSnmp().getUpsStatus(testConfig); const status = await this.nupst.getSnmp().getUpsStatus(testConfig);
console.log('\n┌─ Connection Successful! ─────────────────┐'); console.log('\n┌─ Connection Successful! ─────────────────┐');
console.log('│ UPS Status:'); console.log('│ UPS Status:');
@ -843,14 +914,15 @@ Options:
private async restartServiceIfRunning(): Promise<void> { private async restartServiceIfRunning(): Promise<void> {
try { try {
// Check if the service is active // Check if the service is active
const isActive = execSync('systemctl is-active nupst.service || true').toString().trim() === 'active'; const isActive =
execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
if (isActive) { if (isActive) {
// Service is running, restart it // Service is running, restart it
console.log('┌─ Service Update ─────────────────────────┐'); console.log('┌─ Service Update ─────────────────────────┐');
console.log('│ Configuration has changed.'); console.log('│ Configuration has changed.');
console.log('│ Restarting NUPST service to apply changes...'); console.log('│ Restarting NUPST service to apply changes...');
try { try {
if (process.getuid && process.getuid() === 0) { if (process.getuid && process.getuid() === 0) {
// We have root access, restart directly // We have root access, restart directly
@ -866,7 +938,7 @@ Options:
console.log('│ You may need to restart the service manually:'); console.log('│ You may need to restart the service manually:');
console.log('│ sudo systemctl restart nupst.service'); console.log('│ sudo systemctl restart nupst.service');
} }
console.log('└──────────────────────────────────────────┘'); console.log('└──────────────────────────────────────────┘');
} }
} catch (error) { } catch (error) {
@ -878,18 +950,24 @@ Options:
* Optionally enable and start systemd service * Optionally enable and start systemd service
* @param prompt Function to prompt for user input * @param prompt Function to prompt for user input
*/ */
private async optionallyEnableService(prompt: (question: string) => Promise<string>): Promise<void> { private async optionallyEnableService(
prompt: (question: string) => Promise<string>
): Promise<void> {
if (process.getuid && process.getuid() !== 0) { if (process.getuid && process.getuid() !== 0) {
console.log('\nNote: Run "sudo nupst enable" to set up NUPST as a system service.'); console.log('\nNote: Run "sudo nupst enable" to set up NUPST as a system service.');
} else { } else {
const setupService = await prompt('Would you like to enable NUPST as a system service? (y/N): '); const setupService = await prompt(
'Would you like to enable NUPST as a system service? (y/N): '
);
if (setupService.toLowerCase() === 'y') { if (setupService.toLowerCase() === 'y') {
try { try {
await this.nupst.getSystemd().install(); await this.nupst.getSystemd().install();
console.log('Service installed and enabled to start on boot.'); console.log('Service installed and enabled to start on boot.');
// Ask if the user wants to start the service now // Ask if the user wants to start the service now
const startService = await prompt('Would you like to start the NUPST service now? (Y/n): '); const startService = await prompt(
'Would you like to start the NUPST service now? (Y/n): '
);
if (startService.toLowerCase() !== 'n') { if (startService.toLowerCase() !== 'n') {
await this.nupst.getSystemd().start(); await this.nupst.getSystemd().start();
console.log('NUPST service started successfully.'); console.log('NUPST service started successfully.');
@ -902,7 +980,7 @@ Options:
} }
} }
} }
/** /**
* Display the current configuration * Display the current configuration
*/ */
@ -914,68 +992,75 @@ Options:
} catch (error) { } catch (error) {
console.error('┌─ Configuration Error ─────────────────────┐'); console.error('┌─ Configuration Error ─────────────────────┐');
console.error('│ No configuration found.'); console.error('│ No configuration found.');
console.error('│ Please run \'nupst setup\' first to create a configuration.'); console.error("│ Please run 'nupst setup' first to create a configuration.");
console.error('└──────────────────────────────────────────┘'); console.error('└──────────────────────────────────────────┘');
return; return;
} }
// Get current configuration // Get current configuration
const config = this.nupst.getDaemon().getConfig(); const config = this.nupst.getDaemon().getConfig();
console.log('┌─ NUPST Configuration ──────────────────────┐'); console.log('┌─ NUPST Configuration ──────────────────────┐');
// SNMP Settings // SNMP Settings
console.log('│ SNMP Settings:'); console.log('│ SNMP Settings:');
console.log(`│ Host: ${config.snmp.host}`); console.log(`│ Host: ${config.snmp.host}`);
console.log(`│ Port: ${config.snmp.port}`); console.log(`│ Port: ${config.snmp.port}`);
console.log(`│ Version: ${config.snmp.version}`); console.log(`│ Version: ${config.snmp.version}`);
console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`); console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`);
if (config.snmp.version === 1 || config.snmp.version === 2) { if (config.snmp.version === 1 || config.snmp.version === 2) {
console.log(`│ Community: ${config.snmp.community}`); console.log(`│ Community: ${config.snmp.community}`);
} else if (config.snmp.version === 3) { } else if (config.snmp.version === 3) {
console.log(`│ Security Level: ${config.snmp.securityLevel}`); console.log(`│ Security Level: ${config.snmp.securityLevel}`);
console.log(`│ Username: ${config.snmp.username}`); console.log(`│ Username: ${config.snmp.username}`);
// Show auth and privacy details based on security level // Show auth and privacy details based on security level
if (config.snmp.securityLevel === 'authNoPriv' || config.snmp.securityLevel === 'authPriv') { if (
config.snmp.securityLevel === 'authNoPriv' ||
config.snmp.securityLevel === 'authPriv'
) {
console.log(`│ Auth Protocol: ${config.snmp.authProtocol || 'None'}`); console.log(`│ Auth Protocol: ${config.snmp.authProtocol || 'None'}`);
} }
if (config.snmp.securityLevel === 'authPriv') { if (config.snmp.securityLevel === 'authPriv') {
console.log(`│ Privacy Protocol: ${config.snmp.privProtocol || 'None'}`); console.log(`│ Privacy Protocol: ${config.snmp.privProtocol || 'None'}`);
} }
// Show timeout value // Show timeout value
console.log(`│ Timeout: ${config.snmp.timeout / 1000} seconds`); console.log(`│ Timeout: ${config.snmp.timeout / 1000} seconds`);
} }
// Show OIDs if custom model is selected // Show OIDs if custom model is selected
if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) { if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) {
console.log('│ Custom OIDs:'); console.log('│ Custom OIDs:');
console.log(`│ Power Status: ${config.snmp.customOIDs.POWER_STATUS || 'Not set'}`); console.log(`│ Power Status: ${config.snmp.customOIDs.POWER_STATUS || 'Not set'}`);
console.log(`│ Battery Capacity: ${config.snmp.customOIDs.BATTERY_CAPACITY || 'Not set'}`); console.log(
`│ Battery Capacity: ${config.snmp.customOIDs.BATTERY_CAPACITY || 'Not set'}`
);
console.log(`│ Battery Runtime: ${config.snmp.customOIDs.BATTERY_RUNTIME || 'Not set'}`); console.log(`│ Battery Runtime: ${config.snmp.customOIDs.BATTERY_RUNTIME || 'Not set'}`);
} }
// Thresholds // Thresholds
console.log('│ Thresholds:'); console.log('│ Thresholds:');
console.log(`│ Battery: ${config.thresholds.battery}%`); console.log(`│ Battery: ${config.thresholds.battery}%`);
console.log(`│ Runtime: ${config.thresholds.runtime} minutes`); console.log(`│ Runtime: ${config.thresholds.runtime} minutes`);
console.log(`│ Check Interval: ${config.checkInterval / 1000} seconds`); console.log(`│ Check Interval: ${config.checkInterval / 1000} seconds`);
// Configuration file location // Configuration file location
console.log('│'); console.log('│');
console.log('│ Configuration File Location:'); console.log('│ Configuration File Location:');
console.log('│ /etc/nupst/config.json'); console.log('│ /etc/nupst/config.json');
console.log('└──────────────────────────────────────────┘'); console.log('└──────────────────────────────────────────┘');
// Show service status // Show service status
try { try {
const isActive = execSync('systemctl is-active nupst.service || true').toString().trim() === 'active'; const isActive =
const isEnabled = execSync('systemctl is-enabled nupst.service || true').toString().trim() === 'enabled'; execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
const isEnabled =
execSync('systemctl is-enabled nupst.service || true').toString().trim() === 'enabled';
console.log('┌─ Service Status ─────────────────────────┐'); console.log('┌─ Service Status ─────────────────────────┐');
console.log(`│ Service Active: ${isActive ? 'Yes' : 'No'}`); console.log(`│ Service Active: ${isActive ? 'Yes' : 'No'}`);
console.log(`│ Service Enabled: ${isEnabled ? 'Yes' : 'No'}`); console.log(`│ Service Enabled: ${isEnabled ? 'Yes' : 'No'}`);
@ -983,7 +1068,6 @@ Options:
} catch (error) { } catch (error) {
// Ignore errors checking service status // Ignore errors checking service status
} }
} catch (error) { } catch (error) {
console.error(`Failed to display configuration: ${error.message}`); console.error(`Failed to display configuration: ${error.message}`);
} }
@ -999,12 +1083,12 @@ Options:
try { try {
// Import readline module for user input // Import readline module for user input
const readline = await import('readline'); const readline = await import('readline');
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: process.stdout,
}); });
// Helper function to prompt for input // Helper function to prompt for input
const prompt = (question: string): Promise<string> => { const prompt = (question: string): Promise<string> => {
return new Promise((resolve) => { return new Promise((resolve) => {
@ -1019,11 +1103,13 @@ Options:
console.log('This will completely remove NUPST from your system.\n'); console.log('This will completely remove NUPST from your system.\n');
// Ask about removing configuration // Ask about removing configuration
const removeConfig = await prompt('Do you want to remove the NUPST configuration files? (y/N): '); const removeConfig = await prompt(
'Do you want to remove the NUPST configuration files? (y/N): '
);
// Find the uninstall.sh script location // Find the uninstall.sh script location
let uninstallScriptPath: string; let uninstallScriptPath: string;
// Try to determine script location based on executable path // Try to determine script location based on executable path
try { try {
// For ESM, we can use import.meta.url, but since we might be in CJS // For ESM, we can use import.meta.url, but since we might be in CJS
@ -1031,16 +1117,13 @@ Options:
const binPath = process.argv[1]; const binPath = process.argv[1];
const modulePath = dirname(dirname(binPath)); const modulePath = dirname(dirname(binPath));
uninstallScriptPath = join(modulePath, 'uninstall.sh'); uninstallScriptPath = join(modulePath, 'uninstall.sh');
// Check if the script exists // Check if the script exists
await fs.access(uninstallScriptPath); await fs.access(uninstallScriptPath);
} catch (error) { } catch (error) {
// If we can't find it in the expected location, try common installation paths // If we can't find it in the expected location, try common installation paths
const commonPaths = [ const commonPaths = ['/opt/nupst/uninstall.sh', join(process.cwd(), 'uninstall.sh')];
'/opt/nupst/uninstall.sh',
join(process.cwd(), 'uninstall.sh')
];
for (const path of commonPaths) { for (const path of commonPaths) {
try { try {
await fs.access(path); await fs.access(path);
@ -1050,37 +1133,36 @@ Options:
// Continue to next path // Continue to next path
} }
} }
if (!uninstallScriptPath) { if (!uninstallScriptPath) {
console.error('Could not locate uninstall.sh script. Aborting uninstall.'); console.error('Could not locate uninstall.sh script. Aborting uninstall.');
rl.close(); rl.close();
process.exit(1); process.exit(1);
} }
} }
// Close readline before executing script // Close readline before executing script
rl.close(); rl.close();
// Execute uninstall.sh with the appropriate option // Execute uninstall.sh with the appropriate option
console.log(`\nRunning uninstaller from ${uninstallScriptPath}...`); console.log(`\nRunning uninstaller from ${uninstallScriptPath}...`);
// Pass the configuration removal option as an environment variable // Pass the configuration removal option as an environment variable
const env = { const env = {
...process.env, ...process.env,
REMOVE_CONFIG: removeConfig.toLowerCase() === 'y' ? 'yes' : 'no', REMOVE_CONFIG: removeConfig.toLowerCase() === 'y' ? 'yes' : 'no',
REMOVE_REPO: 'yes', // Always remove repo as requested REMOVE_REPO: 'yes', // Always remove repo as requested
NUPST_CLI_CALL: 'true' // Flag to indicate this is being called from CLI NUPST_CLI_CALL: 'true', // Flag to indicate this is being called from CLI
}; };
// Run the uninstall script with sudo // Run the uninstall script with sudo
execSync(`sudo bash ${uninstallScriptPath}`, { execSync(`sudo bash ${uninstallScriptPath}`, {
env, env,
stdio: 'inherit' // Show output in the terminal stdio: 'inherit', // Show output in the terminal
}); });
} catch (error) { } catch (error) {
console.error(`Uninstall failed: ${error.message}`); console.error(`Uninstall failed: ${error.message}`);
process.exit(1); process.exit(1);
} }
} }
} }

View File

@ -1,4 +1,3 @@
import * as dgram from 'dgram';
import * as snmp from 'net-snmp'; import * as snmp from 'net-snmp';
import type { IOidSet, ISnmpConfig, TUpsModel, IUpsStatus } from './types.js'; import type { IOidSet, ISnmpConfig, TUpsModel, IUpsStatus } from './types.js';
import { UpsOidSets } from './oid-sets.js'; import { UpsOidSets } from './oid-sets.js';