@jalasem/dss
Version:
Dev Spaces Switcher (DSS) - Seamlessly manage isolated development environments with separate SSH keys and Git configurations. Enhanced UI with beautiful tables and improved documentation.
328 lines (327 loc) • 16.1 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.bulkUpdateSpaces = exports.importSpaceConfiguration = exports.exportSpaceConfiguration = exports.batchSwitchSpaces = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const prompts_1 = require("@inquirer/prompts");
const ui_1 = require("./ui");
const SpaceManager_1 = require("./SpaceManager");
const sshKeyGen_1 = require("./sshKeyGen");
const configPath = path_1.default.join(os_1.default.homedir(), '.dss', 'spaces', 'config.json');
function batchSwitchSpaces() {
return __awaiter(this, void 0, void 0, function* () {
const config = yield fs_extra_1.default.readJson(configPath);
if (config.spaces.length === 0) {
ui_1.UIHelper.warning('No spaces available for batch operations.');
return;
}
ui_1.UIHelper.printHeader('Batch Switch Spaces');
ui_1.UIHelper.info('Select multiple spaces to switch between them quickly.');
const selectedSpaces = yield (0, prompts_1.checkbox)({
message: 'Select spaces to switch between:',
choices: config.spaces.map(space => ({
name: space.name === config.activeSpace ? ui_1.UIHelper.activeSpace(space.name) : space.name,
value: space.name,
description: `${space.email} (${space.userName})`
}))
});
if (selectedSpaces.length === 0) {
ui_1.UIHelper.info('No spaces selected.');
return;
}
ui_1.UIHelper.info(`Selected ${selectedSpaces.length} spaces for batch switching.`);
for (const spaceName of selectedSpaces) {
try {
ui_1.UIHelper.info(`Switching to: ${ui_1.UIHelper.highlight(spaceName)}`);
yield (0, SpaceManager_1.switchSpace)(spaceName);
const continueNext = yield (0, prompts_1.confirm)({
message: 'Continue to next space?',
default: true
});
if (!continueNext)
break;
}
catch (error) {
ui_1.UIHelper.error(`Failed to switch to ${spaceName}: ${error.message}`);
const continueOnError = yield (0, prompts_1.confirm)({
message: 'Continue with remaining spaces?',
default: true
});
if (!continueOnError)
break;
}
}
ui_1.UIHelper.success('Batch operation completed!');
});
}
exports.batchSwitchSpaces = batchSwitchSpaces;
function exportSpaceConfiguration() {
return __awaiter(this, void 0, void 0, function* () {
const config = yield fs_extra_1.default.readJson(configPath);
if (config.spaces.length === 0) {
ui_1.UIHelper.warning('No spaces to export.');
return;
}
ui_1.UIHelper.printHeader('Export Space Configuration');
const selectedSpaces = yield (0, prompts_1.checkbox)({
message: 'Select spaces to export:',
choices: config.spaces.map(space => ({
name: space.name,
value: space.name,
description: `${space.email} (${space.userName})`
}))
});
if (selectedSpaces.length === 0) {
ui_1.UIHelper.info('No spaces selected for export.');
return;
}
const exportData = {
version: '1.0.0',
exportDate: new Date().toISOString(),
spaces: config.spaces.filter(space => selectedSpaces.includes(space.name))
.map(space => ({
name: space.name,
email: space.email,
userName: space.userName,
// Don't export SSH key paths for security
hasSSHKey: !!space.sshKeyPath
}))
};
const exportPath = path_1.default.join(os_1.default.homedir(), 'dss-export.json');
yield fs_extra_1.default.writeJson(exportPath, exportData, { spaces: 2 });
ui_1.UIHelper.printSuccessBox('Configuration Exported', [
`✓ ${selectedSpaces.length} spaces exported`,
`✓ Saved to: ${exportPath}`,
'',
'Note: SSH keys not included for security'
]);
});
}
exports.exportSpaceConfiguration = exportSpaceConfiguration;
function importSpaceConfiguration() {
return __awaiter(this, void 0, void 0, function* () {
ui_1.UIHelper.printHeader('Import Space Configuration');
const importPath = path_1.default.join(os_1.default.homedir(), 'dss-export.json');
if (!(yield fs_extra_1.default.pathExists(importPath))) {
ui_1.UIHelper.error(`Import file not found: ${importPath}`);
ui_1.UIHelper.info('Please ensure the export file exists in your home directory.');
return;
}
try {
const importData = yield fs_extra_1.default.readJson(importPath);
if (!importData.spaces || !Array.isArray(importData.spaces)) {
ui_1.UIHelper.error('Invalid import file format.');
return;
}
ui_1.UIHelper.info(`Found ${importData.spaces.length} spaces in import file.`);
const config = yield fs_extra_1.default.readJson(configPath).catch(() => ({ spaces: [] }));
const spacesToImport = importData.spaces.filter((importSpace) => {
const exists = config.spaces.some(existing => existing.name === importSpace.name);
if (exists) {
ui_1.UIHelper.warning(`Space '${importSpace.name}' already exists - skipping.`);
}
return !exists;
});
if (spacesToImport.length === 0) {
ui_1.UIHelper.info('No new spaces to import.');
return;
}
const confirmImport = yield (0, prompts_1.confirm)({
message: `Import ${spacesToImport.length} new spaces?`,
default: true
});
if (!confirmImport) {
ui_1.UIHelper.info('Import cancelled.');
return;
}
// Convert import format to internal format
const newSpaces = spacesToImport.map((importSpace) => ({
name: importSpace.name,
email: importSpace.email,
userName: importSpace.userName,
sshKeyPath: '' // Will need to be set up manually
}));
config.spaces.push(...newSpaces);
yield fs_extra_1.default.writeJson(configPath, config);
ui_1.UIHelper.printSuccessBox('Import Completed', [
`✓ ${spacesToImport.length} spaces imported`,
'',
'Note: SSH keys need to be set up manually',
'Use `dss edit <space>` to configure SSH keys'
]);
}
catch (error) {
ui_1.UIHelper.error(`Failed to import configuration: ${error.message}`);
}
});
}
exports.importSpaceConfiguration = importSpaceConfiguration;
function bulkUpdateSpaces() {
return __awaiter(this, void 0, void 0, function* () {
const config = yield fs_extra_1.default.readJson(configPath);
if (config.spaces.length === 0) {
ui_1.UIHelper.warning('No spaces available for bulk update.');
return;
}
ui_1.UIHelper.printHeader('Bulk Update Spaces');
const updateType = yield (0, prompts_1.select)({
message: 'What would you like to update?',
choices: [
{ name: 'Email domain', value: 'email-domain' },
{ name: 'User name prefix/suffix', value: 'username-pattern' },
{ name: 'Regenerate SSH keys', value: 'regenerate-keys' }
]
});
const selectedSpaces = yield (0, prompts_1.checkbox)({
message: 'Select spaces to update:',
choices: config.spaces.map(space => ({
name: space.name,
value: space.name,
description: `${space.email} (${space.userName})`
}))
});
if (selectedSpaces.length === 0) {
ui_1.UIHelper.info('No spaces selected.');
return;
}
ui_1.UIHelper.info(`Selected ${selectedSpaces.length} spaces for update.`);
let updatedCount = 0;
try {
switch (updateType) {
case 'email-domain': {
const oldDomain = yield (0, prompts_1.input)({
message: 'Enter the old domain to replace (e.g., oldcompany.com):',
validate: (input) => input.trim().length > 0 || 'Domain is required'
});
const newDomain = yield (0, prompts_1.input)({
message: 'Enter the new domain (e.g., newcompany.com):',
validate: (input) => input.trim().length > 0 || 'Domain is required'
});
ui_1.UIHelper.printProgress(`Updating email domains from ${oldDomain} to ${newDomain}`);
for (const spaceName of selectedSpaces) {
const space = config.spaces.find(s => s.name === spaceName);
if (space && space.email.includes(oldDomain)) {
space.email = space.email.replace(oldDomain, newDomain);
updatedCount++;
}
}
break;
}
case 'username-pattern': {
const operation = yield (0, prompts_1.select)({
message: 'What would you like to do with usernames?',
choices: [
{ name: 'Add prefix', value: 'add-prefix' },
{ name: 'Add suffix', value: 'add-suffix' },
{ name: 'Replace text', value: 'replace' }
]
});
if (operation === 'add-prefix') {
const prefix = yield (0, prompts_1.input)({
message: 'Enter prefix to add:',
validate: (input) => input.trim().length > 0 || 'Prefix is required'
});
ui_1.UIHelper.printProgress(`Adding prefix "${prefix}" to usernames`);
for (const spaceName of selectedSpaces) {
const space = config.spaces.find(s => s.name === spaceName);
if (space && !space.userName.startsWith(prefix)) {
space.userName = prefix + space.userName;
updatedCount++;
}
}
}
else if (operation === 'add-suffix') {
const suffix = yield (0, prompts_1.input)({
message: 'Enter suffix to add:',
validate: (input) => input.trim().length > 0 || 'Suffix is required'
});
ui_1.UIHelper.printProgress(`Adding suffix "${suffix}" to usernames`);
for (const spaceName of selectedSpaces) {
const space = config.spaces.find(s => s.name === spaceName);
if (space && !space.userName.endsWith(suffix)) {
space.userName = space.userName + suffix;
updatedCount++;
}
}
}
else if (operation === 'replace') {
const oldText = yield (0, prompts_1.input)({
message: 'Enter text to replace:',
validate: (input) => input.trim().length > 0 || 'Text is required'
});
const newText = yield (0, prompts_1.input)({
message: 'Enter replacement text:',
validate: (input) => input.trim().length > 0 || 'Replacement text is required'
});
ui_1.UIHelper.printProgress(`Replacing "${oldText}" with "${newText}" in usernames`);
for (const spaceName of selectedSpaces) {
const space = config.spaces.find(s => s.name === spaceName);
if (space && space.userName.includes(oldText)) {
space.userName = space.userName.replace(new RegExp(oldText, 'g'), newText);
updatedCount++;
}
}
}
break;
}
case 'regenerate-keys': {
const confirmRegenerate = yield (0, prompts_1.confirm)({
message: 'Are you sure you want to regenerate SSH keys? This will replace existing keys.',
default: false
});
if (!confirmRegenerate) {
ui_1.UIHelper.info('SSH key regeneration cancelled.');
return;
}
ui_1.UIHelper.printProgress('Regenerating SSH keys');
for (const spaceName of selectedSpaces) {
const space = config.spaces.find(s => s.name === spaceName);
if (space) {
try {
ui_1.UIHelper.updateProgress(`Regenerating SSH key for ${space.name}`);
const newKeyPath = yield (0, sshKeyGen_1.generateSSHKey)(space.name, space.email);
space.sshKeyPath = newKeyPath;
updatedCount++;
}
catch (error) {
ui_1.UIHelper.error(`Failed to regenerate SSH key for ${space.name}: ${error.message}`);
}
}
}
break;
}
}
ui_1.UIHelper.clearProgress();
if (updatedCount > 0) {
yield fs_extra_1.default.writeJson(configPath, config);
ui_1.UIHelper.printSuccessBox('Bulk Update Complete', [
`✓ ${updatedCount} spaces updated successfully`,
`✓ Operation: ${updateType}`,
'',
'Use `dss list` to view updated spaces'
]);
}
else {
ui_1.UIHelper.info('No spaces were updated.');
}
}
catch (error) {
ui_1.UIHelper.clearProgress();
ui_1.UIHelper.error(`Bulk update failed: ${error.message}`);
}
});
}
exports.bulkUpdateSpaces = bulkUpdateSpaces;