- Modernize ups list command with logger.logTable() - Modernize group list command with logger.logTable() - Completely rewrite config show with tables and proper box styling - Add professional column definitions with themed colors - Replace all manual table formatting (padEnd, pipe separators) - Improve visual hierarchy with appropriate box styles (info, warning, success) - Ensure consistent theming across all CLI commands
		
			
				
	
	
		
			607 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			607 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import process from 'node:process';
 | |
| import { Nupst } from '../nupst.ts';
 | |
| import { logger, type ITableColumn } from '../logger.ts';
 | |
| import { theme } from '../colors.ts';
 | |
| import * as helpers from '../helpers/index.ts';
 | |
| import { type IGroupConfig } from '../daemon.ts';
 | |
| 
 | |
| /**
 | |
|  * Class for handling group-related CLI commands
 | |
|  * Provides interface for managing UPS groups
 | |
|  */
 | |
| export class GroupHandler {
 | |
|   private readonly nupst: Nupst;
 | |
| 
 | |
|   /**
 | |
|    * Create a new Group handler
 | |
|    * @param nupst Reference to the main Nupst instance
 | |
|    */
 | |
|   constructor(nupst: Nupst) {
 | |
|     this.nupst = nupst;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * List all UPS groups
 | |
|    */
 | |
|   public async list(): Promise<void> {
 | |
|     try {
 | |
|       // Try to load configuration
 | |
|       try {
 | |
|         await this.nupst.getDaemon().loadConfig();
 | |
|       } catch (error) {
 | |
|         logger.logBox('Configuration Error', [
 | |
|           'No configuration found.',
 | |
|           "Please run 'nupst ups add' first to create a configuration.",
 | |
|         ], 50, 'error');
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Get current configuration
 | |
|       const config = this.nupst.getDaemon().getConfig();
 | |
| 
 | |
|       // Check if multi-UPS config
 | |
|       if (!config.groups || !Array.isArray(config.groups)) {
 | |
|         logger.logBox('UPS Groups', [
 | |
|           'No groups configured.',
 | |
|           '',
 | |
|           `${theme.dim('Run')} ${theme.command('nupst group add')} ${theme.dim('to add a group')}`,
 | |
|         ], 50, 'info');
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Display group list with modern table
 | |
|       if (config.groups.length === 0) {
 | |
|         logger.logBox('UPS Groups', [
 | |
|           'No UPS groups configured.',
 | |
|           '',
 | |
|           `${theme.dim('Run')} ${theme.command('nupst group add')} ${theme.dim('to add a group')}`,
 | |
|         ], 60, 'info');
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Prepare table data
 | |
|       const rows = config.groups.map((group) => {
 | |
|         // Count UPS devices in this group
 | |
|         const upsInGroup = config.upsDevices.filter((ups) => ups.groups.includes(group.id));
 | |
|         const upsCount = upsInGroup.length;
 | |
|         const upsNames = upsInGroup.map((ups) => ups.name).join(', ');
 | |
| 
 | |
|         return {
 | |
|           id: group.id,
 | |
|           name: group.name || '',
 | |
|           mode: group.mode || 'unknown',
 | |
|           count: String(upsCount),
 | |
|           devices: upsCount > 0 ? upsNames : theme.dim('None'),
 | |
|         };
 | |
|       });
 | |
| 
 | |
|       const columns: ITableColumn[] = [
 | |
|         { header: 'ID', key: 'id', align: 'left', color: theme.highlight },
 | |
|         { header: 'Name', key: 'name', align: 'left' },
 | |
|         { header: 'Mode', key: 'mode', align: 'left', color: theme.info },
 | |
|         { header: 'UPS Count', key: 'count', align: 'right' },
 | |
|         { header: 'UPS Devices', key: 'devices', align: 'left' },
 | |
|       ];
 | |
| 
 | |
|       logger.log('');
 | |
|       logger.info(`UPS Groups (${config.groups.length}):`);
 | |
|       logger.log('');
 | |
|       logger.logTable(columns, rows);
 | |
|       logger.log('');
 | |
|     } catch (error) {
 | |
|       logger.error(
 | |
|         `Failed to list UPS groups: ${error instanceof Error ? error.message : String(error)}`,
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Add a new UPS group
 | |
|    */
 | |
|   public async add(): Promise<void> {
 | |
|     try {
 | |
|       // Import readline module for user input
 | |
|       const readline = await import('node:readline');
 | |
| 
 | |
|       const rl = readline.createInterface({
 | |
|         input: process.stdin,
 | |
|         output: process.stdout,
 | |
|       });
 | |
| 
 | |
|       // Helper function to prompt for input
 | |
|       const prompt = (question: string): Promise<string> => {
 | |
|         return new Promise((resolve) => {
 | |
|           rl.question(question, (answer: string) => {
 | |
|             resolve(answer);
 | |
|           });
 | |
|         });
 | |
|       };
 | |
| 
 | |
|       try {
 | |
|         // Try to load configuration
 | |
|         try {
 | |
|           await this.nupst.getDaemon().loadConfig();
 | |
|         } catch (error) {
 | |
|           logger.error(
 | |
|             'No configuration found. Please run "nupst setup" first to create a configuration.',
 | |
|           );
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         // Get current configuration
 | |
|         const config = this.nupst.getDaemon().getConfig();
 | |
| 
 | |
|         // Initialize groups array if not exists
 | |
|         if (!config.groups) {
 | |
|           config.groups = [];
 | |
|         }
 | |
| 
 | |
|         // Check if upsDevices is initialized
 | |
|         if (!config.upsDevices) {
 | |
|           config.upsDevices = [];
 | |
|         }
 | |
| 
 | |
|         logger.log('\nNUPST Add Group');
 | |
|         logger.log('==============\n');
 | |
|         logger.log('This will guide you through creating a new UPS group.\n');
 | |
| 
 | |
|         // Generate a new unique group ID
 | |
|         const groupId = helpers.shortId();
 | |
| 
 | |
|         // Get group name
 | |
|         const name = await prompt('Group Name: ');
 | |
| 
 | |
|         // Get group mode
 | |
|         const modeInput = await prompt('Group Mode (redundant/nonRedundant) [redundant]: ');
 | |
|         const mode = modeInput.toLowerCase() === 'nonredundant' ? 'nonRedundant' : 'redundant';
 | |
| 
 | |
|         // Get optional description
 | |
|         const description = await prompt('Group Description (optional): ');
 | |
| 
 | |
|         // Create the new group
 | |
|         const newGroup: IGroupConfig = {
 | |
|           id: groupId,
 | |
|           name: name || `Group-${groupId}`,
 | |
|           mode,
 | |
|           description: description || undefined,
 | |
|         };
 | |
| 
 | |
|         // Add the group to the configuration
 | |
|         config.groups.push(newGroup);
 | |
| 
 | |
|         // Save the configuration
 | |
|         await this.nupst.getDaemon().saveConfig(config);
 | |
| 
 | |
|         // Display summary
 | |
|         const boxWidth = 45;
 | |
|         logger.logBoxTitle('Group Created', boxWidth);
 | |
|         logger.logBoxLine(`ID: ${newGroup.id}`);
 | |
|         logger.logBoxLine(`Name: ${newGroup.name}`);
 | |
|         logger.logBoxLine(`Mode: ${newGroup.mode}`);
 | |
|         if (newGroup.description) {
 | |
|           logger.logBoxLine(`Description: ${newGroup.description}`);
 | |
|         }
 | |
|         logger.logBoxEnd();
 | |
| 
 | |
|         // Check if there are UPS devices to assign to this group
 | |
|         if (config.upsDevices.length > 0) {
 | |
|           const assignUps = await prompt(
 | |
|             'Would you like to assign UPS devices to this group now? (y/N): ',
 | |
|           );
 | |
|           if (assignUps.toLowerCase() === 'y') {
 | |
|             await this.assignUpsToGroup(newGroup.id, config, prompt);
 | |
| 
 | |
|             // Save again after assigning UPS devices
 | |
|             await this.nupst.getDaemon().saveConfig(config);
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // Check if service is running and restart it if needed
 | |
|         this.nupst.getUpsHandler().restartServiceIfRunning();
 | |
| 
 | |
|         logger.log('\nGroup setup complete!');
 | |
|       } finally {
 | |
|         rl.close();
 | |
|         process.stdin.destroy();
 | |
|       }
 | |
|     } catch (error) {
 | |
|       logger.error(`Add group error: ${error instanceof Error ? error.message : String(error)}`);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Edit an existing UPS group
 | |
|    * @param groupId ID of the group to edit
 | |
|    */
 | |
|   public async edit(groupId: string): Promise<void> {
 | |
|     try {
 | |
|       // Import readline module for user input
 | |
|       const readline = await import('node:readline');
 | |
| 
 | |
|       const rl = readline.createInterface({
 | |
|         input: process.stdin,
 | |
|         output: process.stdout,
 | |
|       });
 | |
| 
 | |
|       // Helper function to prompt for input
 | |
|       const prompt = (question: string): Promise<string> => {
 | |
|         return new Promise((resolve) => {
 | |
|           rl.question(question, (answer: string) => {
 | |
|             resolve(answer);
 | |
|           });
 | |
|         });
 | |
|       };
 | |
| 
 | |
|       try {
 | |
|         // Try to load configuration
 | |
|         try {
 | |
|           await this.nupst.getDaemon().loadConfig();
 | |
|         } catch (error) {
 | |
|           logger.error(
 | |
|             'No configuration found. Please run "nupst setup" first to create a configuration.',
 | |
|           );
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         // Get current configuration
 | |
|         const config = this.nupst.getDaemon().getConfig();
 | |
| 
 | |
|         // Check if groups are initialized
 | |
|         if (!config.groups || !Array.isArray(config.groups)) {
 | |
|           logger.error(
 | |
|             'No groups configured. Please run "nupst group add" first to create a group.',
 | |
|           );
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         // Find the group to edit
 | |
|         const groupIndex = config.groups.findIndex((group) => group.id === groupId);
 | |
|         if (groupIndex === -1) {
 | |
|           logger.error(`Group with ID "${groupId}" not found.`);
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         const group = config.groups[groupIndex];
 | |
| 
 | |
|         logger.log(`\nNUPST Edit Group: ${group.name} (${group.id})`);
 | |
|         logger.log('==============================================\n');
 | |
| 
 | |
|         // Edit group name
 | |
|         const newName = await prompt(`Group Name [${group.name}]: `);
 | |
|         if (newName.trim()) {
 | |
|           group.name = newName;
 | |
|         }
 | |
| 
 | |
|         // Edit group mode
 | |
|         const currentMode = group.mode || 'redundant';
 | |
|         const modeInput = await prompt(`Group Mode (redundant/nonRedundant) [${currentMode}]: `);
 | |
|         if (modeInput.trim()) {
 | |
|           group.mode = modeInput.toLowerCase() === 'nonredundant' ? 'nonRedundant' : 'redundant';
 | |
|         }
 | |
| 
 | |
|         // Edit description
 | |
|         const currentDesc = group.description || '';
 | |
|         const newDesc = await prompt(`Group Description [${currentDesc}]: `);
 | |
|         if (newDesc.trim() || newDesc === '') {
 | |
|           group.description = newDesc.trim() || undefined;
 | |
|         }
 | |
| 
 | |
|         // Update the group in the configuration
 | |
|         config.groups[groupIndex] = group;
 | |
| 
 | |
|         // Save the configuration
 | |
|         await this.nupst.getDaemon().saveConfig(config);
 | |
| 
 | |
|         // Display summary
 | |
|         const boxWidth = 45;
 | |
|         logger.logBoxTitle('Group Updated', boxWidth);
 | |
|         logger.logBoxLine(`ID: ${group.id}`);
 | |
|         logger.logBoxLine(`Name: ${group.name}`);
 | |
|         logger.logBoxLine(`Mode: ${group.mode}`);
 | |
|         if (group.description) {
 | |
|           logger.logBoxLine(`Description: ${group.description}`);
 | |
|         }
 | |
|         logger.logBoxEnd();
 | |
| 
 | |
|         // Edit UPS assignments if requested
 | |
|         const editAssignments = await prompt(
 | |
|           'Would you like to edit UPS assignments for this group? (y/N): ',
 | |
|         );
 | |
|         if (editAssignments.toLowerCase() === 'y') {
 | |
|           await this.assignUpsToGroup(group.id, config, prompt);
 | |
| 
 | |
|           // Save again after editing assignments
 | |
|           await this.nupst.getDaemon().saveConfig(config);
 | |
|         }
 | |
| 
 | |
|         // Check if service is running and restart it if needed
 | |
|         this.nupst.getUpsHandler().restartServiceIfRunning();
 | |
| 
 | |
|         logger.log('\nGroup edit complete!');
 | |
|       } finally {
 | |
|         rl.close();
 | |
|         process.stdin.destroy();
 | |
|       }
 | |
|     } catch (error) {
 | |
|       logger.error(`Edit group error: ${error instanceof Error ? error.message : String(error)}`);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Delete an existing UPS group
 | |
|    * @param groupId ID of the group to delete
 | |
|    */
 | |
|   public async remove(groupId: string): Promise<void> {
 | |
|     try {
 | |
|       // Try to load configuration
 | |
|       try {
 | |
|         await this.nupst.getDaemon().loadConfig();
 | |
|       } catch (error) {
 | |
|         logger.error(
 | |
|           'No configuration found. Please run "nupst setup" first to create a configuration.',
 | |
|         );
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Get current configuration
 | |
|       const config = this.nupst.getDaemon().getConfig();
 | |
| 
 | |
|       // Check if groups are initialized
 | |
|       if (!config.groups || !Array.isArray(config.groups)) {
 | |
|         logger.error('No groups configured.');
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Find the group to delete
 | |
|       const groupIndex = config.groups.findIndex((group) => group.id === groupId);
 | |
|       if (groupIndex === -1) {
 | |
|         logger.error(`Group with ID "${groupId}" not found.`);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       const groupToDelete = config.groups[groupIndex];
 | |
| 
 | |
|       // Get confirmation before deleting
 | |
|       const readline = await import('node:readline');
 | |
|       const rl = readline.createInterface({
 | |
|         input: process.stdin,
 | |
|         output: process.stdout,
 | |
|       });
 | |
| 
 | |
|       const confirm = await new Promise<string>((resolve) => {
 | |
|         rl.question(
 | |
|           `Are you sure you want to delete group "${groupToDelete.name}" (${groupId})? [y/N]: `,
 | |
|           (answer) => {
 | |
|             resolve(answer.toLowerCase());
 | |
|           },
 | |
|         );
 | |
|       });
 | |
| 
 | |
|       rl.close();
 | |
|       process.stdin.destroy();
 | |
| 
 | |
|       if (confirm !== 'y' && confirm !== 'yes') {
 | |
|         logger.log('Deletion cancelled.');
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Remove this group from all UPS device group assignments
 | |
|       if (config.upsDevices && Array.isArray(config.upsDevices)) {
 | |
|         for (const ups of config.upsDevices) {
 | |
|           const groupIndex = ups.groups.indexOf(groupId);
 | |
|           if (groupIndex !== -1) {
 | |
|             ups.groups.splice(groupIndex, 1);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Remove the group from the array
 | |
|       config.groups.splice(groupIndex, 1);
 | |
| 
 | |
|       // Save the configuration
 | |
|       await this.nupst.getDaemon().saveConfig(config);
 | |
| 
 | |
|       logger.log(`Group "${groupToDelete.name}" (${groupId}) has been deleted.`);
 | |
| 
 | |
|       // Check if service is running and restart it if needed
 | |
|       this.nupst.getUpsHandler().restartServiceIfRunning();
 | |
|     } catch (error) {
 | |
|       logger.error(
 | |
|         `Failed to delete group: ${error instanceof Error ? error.message : String(error)}`,
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Assign UPS devices to groups
 | |
|    * @param ups UPS configuration to update
 | |
|    * @param groups Available groups
 | |
|    * @param prompt Function to prompt for user input
 | |
|    */
 | |
|   public async assignUpsToGroups(
 | |
|     ups: any,
 | |
|     groups: any[],
 | |
|     prompt: (question: string) => Promise<string>,
 | |
|   ): Promise<void> {
 | |
|     // Initialize groups array if it doesn't exist
 | |
|     if (!ups.groups) {
 | |
|       ups.groups = [];
 | |
|     }
 | |
| 
 | |
|     // Show current group assignments
 | |
|     logger.log('\nCurrent Group Assignments:');
 | |
|     if (ups.groups && ups.groups.length > 0) {
 | |
|       for (const groupId of ups.groups) {
 | |
|         const group = groups.find((g) => g.id === groupId);
 | |
|         if (group) {
 | |
|           logger.log(`- ${group.name} (${group.id})`);
 | |
|         } else {
 | |
|           logger.log(`- Unknown group (${groupId})`);
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       logger.log('- None');
 | |
|     }
 | |
| 
 | |
|     // Show available groups
 | |
|     logger.log('\nAvailable Groups:');
 | |
|     if (groups.length === 0) {
 | |
|       logger.log('- No groups available. Use "nupst group add" to create groups.');
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     for (let i = 0; i < groups.length; i++) {
 | |
|       const group = groups[i];
 | |
|       const assigned = ups.groups && ups.groups.includes(group.id);
 | |
|       logger.log(
 | |
|         `${i + 1}) ${group.name} (${group.id}) [${assigned ? 'Assigned' : 'Not Assigned'}]`,
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     // Prompt for group selection
 | |
|     const selection = await prompt(
 | |
|       '\nSelect groups to assign/unassign (comma-separated numbers, or "clear" to remove all): ',
 | |
|     );
 | |
| 
 | |
|     if (selection.toLowerCase() === 'clear') {
 | |
|       // Clear all group assignments
 | |
|       ups.groups = [];
 | |
|       logger.log('All group assignments cleared.');
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (!selection.trim()) {
 | |
|       // No change if empty input
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // Process selections
 | |
|     const selections = selection.split(',').map((s) => s.trim());
 | |
| 
 | |
|     for (const sel of selections) {
 | |
|       const index = parseInt(sel, 10) - 1;
 | |
|       if (isNaN(index) || index < 0 || index >= groups.length) {
 | |
|         logger.error(`Invalid selection: ${sel}`);
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       const group = groups[index];
 | |
| 
 | |
|       // Initialize groups array if needed (should already be done above)
 | |
|       if (!ups.groups) {
 | |
|         ups.groups = [];
 | |
|       }
 | |
| 
 | |
|       // Toggle assignment
 | |
|       const groupIndex = ups.groups.indexOf(group.id);
 | |
|       if (groupIndex === -1) {
 | |
|         // Add to group
 | |
|         ups.groups.push(group.id);
 | |
|         logger.log(`Added to group: ${group.name} (${group.id})`);
 | |
|       } else {
 | |
|         // Remove from group
 | |
|         ups.groups.splice(groupIndex, 1);
 | |
|         logger.log(`Removed from group: ${group.name} (${group.id})`);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Assign UPS devices to a specific group
 | |
|    * @param groupId Group ID to assign UPS devices to
 | |
|    * @param config Full configuration
 | |
|    * @param prompt Function to prompt for user input
 | |
|    */
 | |
|   public async assignUpsToGroup(
 | |
|     groupId: string,
 | |
|     config: any,
 | |
|     prompt: (question: string) => Promise<string>,
 | |
|   ): Promise<void> {
 | |
|     if (!config.upsDevices || config.upsDevices.length === 0) {
 | |
|       logger.log('No UPS devices available. Use "nupst add" to add UPS devices.');
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     const group = config.groups.find((g: { id: string }) => g.id === groupId);
 | |
|     if (!group) {
 | |
|       logger.error(`Group with ID "${groupId}" not found.`);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // Show current assignments
 | |
|     logger.log(`\nUPS devices in group "${group.name}" (${group.id}):`);
 | |
|     const upsInGroup = config.upsDevices.filter((ups: { groups?: string[] }) =>
 | |
|       ups.groups && ups.groups.includes(groupId)
 | |
|     );
 | |
|     if (upsInGroup.length === 0) {
 | |
|       logger.log('- None');
 | |
|     } else {
 | |
|       for (const ups of upsInGroup) {
 | |
|         logger.log(`- ${ups.name} (${ups.id})`);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Show all UPS devices
 | |
|     logger.log('\nAvailable UPS devices:');
 | |
|     for (let i = 0; i < config.upsDevices.length; i++) {
 | |
|       const ups = config.upsDevices[i];
 | |
|       const assigned = ups.groups && ups.groups.includes(groupId);
 | |
|       logger.log(`${i + 1}) ${ups.name} (${ups.id}) [${assigned ? 'Assigned' : 'Not Assigned'}]`);
 | |
|     }
 | |
| 
 | |
|     // Prompt for UPS selection
 | |
|     const selection = await prompt(
 | |
|       '\nSelect UPS devices to assign/unassign (comma-separated numbers, or "clear" to remove all): ',
 | |
|     );
 | |
| 
 | |
|     if (selection.toLowerCase() === 'clear') {
 | |
|       // Clear all UPS from this group
 | |
|       for (const ups of config.upsDevices) {
 | |
|         if (ups.groups) {
 | |
|           const groupIndex = ups.groups.indexOf(groupId);
 | |
|           if (groupIndex !== -1) {
 | |
|             ups.groups.splice(groupIndex, 1);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       logger.log(`All UPS devices removed from group "${group.name}".`);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (!selection.trim()) {
 | |
|       // No change if empty input
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // Process selections
 | |
|     const selections = selection.split(',').map((s) => s.trim());
 | |
| 
 | |
|     for (const sel of selections) {
 | |
|       const index = parseInt(sel, 10) - 1;
 | |
|       if (isNaN(index) || index < 0 || index >= config.upsDevices.length) {
 | |
|         logger.error(`Invalid selection: ${sel}`);
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       const ups = config.upsDevices[index];
 | |
| 
 | |
|       // Initialize groups array if needed
 | |
|       if (!ups.groups) {
 | |
|         ups.groups = [];
 | |
|       }
 | |
| 
 | |
|       // Toggle assignment
 | |
|       const groupIndex = ups.groups.indexOf(groupId);
 | |
|       if (groupIndex === -1) {
 | |
|         // Add to group
 | |
|         ups.groups.push(groupId);
 | |
|         logger.log(`Added "${ups.name}" to group "${group.name}"`);
 | |
|       } else {
 | |
|         // Remove from group
 | |
|         ups.groups.splice(groupIndex, 1);
 | |
|         logger.log(`Removed "${ups.name}" from group "${group.name}"`);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 |