import fs from "fs-extra";
import path from "path";
import { createHash } from "crypto";
export class DirectoryOperations {
constructor(securityManager) {
this.security = securityManager;
}
async createDirectory(dirPath) {
try {
const validPath = this.security.validatePath(dirPath);
await fs.ensureDir(validPath);
return {
content: [{
type: "text",
text: `โ
Directory created successfully: ${path.basename(validPath)}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `โ Error creating directory: ${error.message}`
}]
};
}
}
async listDirectory(dirPath, recursive = false, showHidden = false) {
try {
const validPath = this.security.validatePath(dirPath);
await this.security.checkPermissions(validPath, 'read');
const items = await this.getDirectoryContents(validPath, recursive, showHidden);
const output = this.formatDirectoryListing(items, validPath);
return {
content: [{
type: "text",
text: output
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `โ Error listing directory: ${error.message}`
}]
};
}
}
async getDirectoryContents(dirPath, recursive = false, showHidden = false) {
const items = [];
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
if (!showHidden && entry.name.startsWith('.')) {
continue;
}
const fullPath = path.join(dirPath, entry.name);
const stats = await fs.stat(fullPath);
const item = {
name: entry.name,
path: fullPath,
type: entry.isDirectory() ? 'directory' : 'file',
size: stats.size,
sizeHuman: this.formatFileSize(stats.size),
modified: stats.mtime,
permissions: stats.mode
};
items.push(item);
if (recursive && entry.isDirectory()) {
try {
const subItems = await this.getDirectoryContents(fullPath, recursive, showHidden);
items.push(...subItems);
} catch (error) {
// Skip directories we can't read
items.push({
name: `${entry.name}/ (access denied)`,
path: fullPath,
type: 'directory',
size: 0,
sizeHuman: 'N/A',
modified: stats.mtime,
permissions: stats.mode,
error: error.message
});
}
}
}
return items;
}
formatDirectoryListing(items, basePath) {
const files = items.filter(item => item.type === 'file');
const directories = items.filter(item => item.type === 'directory');
let output = `๐ Directory: ${basePath}\n`;
output += `๐ Summary: ${directories.length} directories, ${files.length} files\n\n`;
// Sort items
const sortedItems = [...directories, ...files].sort((a, b) => a.name.localeCompare(b.name));
for (const item of sortedItems) {
const icon = item.type === 'directory' ? '๐' : '๐';
const relativePath = path.relative(basePath, item.path);
const indent = ' '.repeat((relativePath.split(path.sep).length - 1));
output += `${indent}${icon} ${item.name}`;
if (item.type === 'file') {
output += ` (${item.sizeHuman})`;
}
if (item.error) {
output += ` โ ${item.error}`;
}
output += '\n';
}
return output;
}
async deleteDirectory(dirPath, backup = true) {
try {
const validPath = this.security.validatePath(dirPath);
await this.security.checkPermissions(validPath, 'delete');
let backupPath = null;
if (backup) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupName = `${path.basename(validPath)}_${timestamp}`;
backupPath = path.join(this.security.backupDir, backupName);
await fs.copy(validPath, backupPath);
}
await fs.remove(validPath);
const message = `โ
Directory deleted successfully: ${path.basename(validPath)}`;
return {
content: [{
type: "text",
text: backupPath ? `${message}\n๐พ Backup created: ${backupPath}` : message
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `โ Error deleting directory: ${error.message}`
}]
};
}
}
async getDirectoryInfo(dirPath) {
try {
const validPath = this.security.validatePath(dirPath);
await this.security.checkPermissions(validPath, 'read');
const stats = await fs.stat(validPath);
const contents = await this.getDirectoryContents(validPath, true, false);
const files = contents.filter(item => item.type === 'file');
const directories = contents.filter(item => item.type === 'directory');
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
const dirInfo = {
path: validPath,
name: path.basename(validPath),
created: stats.birthtime,
modified: stats.mtime,
accessed: stats.atime,
permissions: stats.mode,
totalFiles: files.length,
totalDirectories: directories.length,
totalSize: totalSize,
totalSizeHuman: this.formatFileSize(totalSize)
};
return {
content: [{
type: "text",
text: `๐ Directory Information:\n${JSON.stringify(dirInfo, null, 2)}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `โ Error getting directory info: ${error.message}`
}]
};
}
}
formatFileSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
if (bytes === 0) return '0 Bytes';
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}
}