UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

117 lines 16.3 kB
/** * Filesystem and string manipulation utilities */ import * as fs from 'fs/promises'; import * as path from 'path'; import { ADJECTIVES, ANIMALS } from '../config/constants.js'; import { logger } from './logger.js'; import { FileOperationsService } from '../services/FileOperationsService.js'; import { FileLockManager } from '../security/fileLockManager.js'; // Singleton file operations service for utility functions let fileOperationsService = null; function getFileOperationsService() { if (!fileOperationsService) { fileOperationsService = new FileOperationsService(new FileLockManager()); } return fileOperationsService; } /** * Generate an anonymous ID for users without identity */ export function generateAnonymousId() { const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)]; const animal = ANIMALS[Math.floor(Math.random() * ANIMALS.length)]; const random = Math.random().toString(36).substring(2, 6); return `anon-${adjective}-${animal}-${random}`; } /** * Generate a unique ID for personas */ // Pre-compiled regex for better performance (avoids creating regex on each character) const ALPHANUMERIC_REGEX = /[a-z0-9]/; export function generateUniqueId(personaName, author) { const now = new Date(); const dateStr = now.toISOString().slice(0, 10).replaceAll('-', ''); const timeStr = now.toTimeString().slice(0, 8).replaceAll(':', ''); // Issue #848: Add milliseconds + random suffix for sub-second uniqueness. // Critical for copy-on-write, swarm operations, and rapid programmatic creation. const msStr = now.getMilliseconds().toString().padStart(3, '0'); const rand = Math.random().toString(36).substring(2, 6); // SECURITY FIX: Prevent ReDoS by using a single-pass approach // Previously: Multiple replace() operations with unbounded quantifiers could cause exponential backtracking // Now: Single-pass transformation with built-in length limit const normalized = personaName.toLowerCase(); const sanitizedName = normalized .split('') .map(char => ALPHANUMERIC_REGEX.test(char) ? char : '-') .join('') .substring(0, 100) // Limit after transformation to preserve structure .replaceAll(/(^-+)|(-+$)/g, '') // Only trim leading/trailing hyphens .replaceAll(/-{2,}/g, '-'); // Collapse multiple hyphens const whoMadeIt = author || generateAnonymousId(); return `${sanitizedName}_${dateStr}-${timeStr}${msStr}-${rand}_${whoMadeIt}`; } /** * Convert text to URL-safe slug */ export function slugify(text) { // SECURITY FIX: Prevent ReDoS by using a single-pass approach // Previously: Multiple replace() operations with unbounded quantifiers could cause exponential backtracking // Now: Single-pass transformation with built-in length limit const normalized = text.toLowerCase(); const transformed = normalized .split('') .map(char => ALPHANUMERIC_REGEX.test(char) ? char : '-') .join(''); // SECURITY FIX: Avoid polynomial regex by using separate operations // Trim leading hyphens let start = 0; while (start < transformed.length && transformed[start] === '-') { start++; } // Trim trailing hyphens let end = transformed.length - 1; while (end >= start && transformed[end] === '-') { end--; } // Extract the trimmed portion and collapse multiple hyphens const trimmed = transformed.slice(start, end + 1); return trimmed.replaceAll(/-{2,}/g, '-'); // This is safe as it's linear } /** * Ensure a directory exists, create if it doesn't */ export async function ensureDirectory(dirPath) { const fileOps = getFileOperationsService(); const exists = await fileOps.exists(dirPath); if (!exists) { logger.debug(`Creating directory: ${dirPath}`); await fileOps.createDirectory(dirPath); } } /** * Check if a file exists */ export async function fileExists(filePath) { const fileOps = getFileOperationsService(); return fileOps.exists(filePath); } /** * Get file size in bytes */ export async function getFileSize(filePath) { const fileOps = getFileOperationsService(); const stats = await fileOps.stat(filePath); return stats.size; } /** * Create a backup of a directory */ export async function createBackup(sourcePath, backupPath) { // Ensure backup directory exists const backupDir = path.dirname(backupPath); await ensureDirectory(backupDir); // Copy directory recursively await fs.cp(sourcePath, backupPath, { recursive: true }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZXN5c3RlbS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9maWxlc3lzdGVtLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM3RCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3JDLE9BQU8sRUFBMEIscUJBQXFCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNyRyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFakUsMERBQTBEO0FBQzFELElBQUkscUJBQXFCLEdBQWtDLElBQUksQ0FBQztBQUVoRSxTQUFTLHdCQUF3QjtJQUMvQixJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUMzQixxQkFBcUIsR0FBRyxJQUFJLHFCQUFxQixDQUFDLElBQUksZUFBZSxFQUFFLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBQ0QsT0FBTyxxQkFBcUIsQ0FBQztBQUMvQixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLFVBQVUsbUJBQW1CO0lBQ2pDLE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUM1RSxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDbkUsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FB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QyxJQUFJLENBQUM7QUFDcEIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxZQUFZLENBQUMsVUFBa0IsRUFBRSxVQUFrQjtJQUN2RSxpQ0FBaUM7SUFDakMsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUMzQyxNQUFNLGVBQWUsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUVqQyw2QkFBNkI7SUFDN0IsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDLFVBQVUsRUFBRSxVQUFVLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztBQUMzRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBGaWxlc3lzdGVtIGFuZCBzdHJpbmcgbWFuaXB1bGF0aW9uIHV0aWxpdGllc1xuICovXG5cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBBREpFQ1RJVkVTLCBBTklNQUxTIH0gZnJvbSAnLi4vY29uZmlnL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBJRmlsZU9wZXJhdGlvbnNTZXJ2aWNlLCBGaWxlT3BlcmF0aW9uc1NlcnZpY2UgfSBmcm9tICcuLi9zZXJ2aWNlcy9GaWxlT3BlcmF0aW9uc1NlcnZpY2UuanMnO1xuaW1wb3J0IHsgRmlsZUxvY2tNYW5hZ2VyIH0gZnJvbSAnLi4vc2VjdXJpdHkvZmlsZUxvY2tNYW5hZ2VyLmpzJztcblxuLy8gU2luZ2xldG9uIGZpbGUgb3BlcmF0aW9ucyBzZXJ2aWNlIGZvciB1dGlsaXR5IGZ1bmN0aW9uc1xubGV0IGZpbGVPcGVyYXRpb25zU2VydmljZTogSUZpbGVPcGVyYXRpb25zU2VydmljZSB8IG51bGwgPSBudWxsO1xuXG5mdW5jdGlvbiBnZXRGaWxlT3BlcmF0aW9uc1NlcnZpY2UoKTogSUZpbGVPcGVyYXRpb25zU2VydmljZSB7XG4gIGlmICghZmlsZU9wZXJhdGlvbnNTZXJ2aWNlKSB7XG4gICAgZmlsZU9wZXJhdGlvbnNTZXJ2aWNlID0gbmV3IEZpbGVPcGVyYXRpb25zU2VydmljZShuZXcgRmlsZUxvY2tNYW5hZ2VyKCkpO1xuICB9XG4gIHJldHVybiBmaWxlT3BlcmF0aW9uc1NlcnZpY2U7XG59XG5cbi8qKlxuICogR2VuZXJhdGUgYW4gYW5vbnltb3VzIElEIGZvciB1c2VycyB3aXRob3V0IGlkZW50aXR5XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZUFub255bW91c0lkKCk6IHN0cmluZyB7XG4gIGNvbnN0IGFkamVjdGl2ZSA9IEFESkVDVElWRVNbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogQURKRUNUSVZFUy5sZW5ndGgpXTtcbiAgY29uc3QgYW5pbWFsID0gQU5JTUFMU1tNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBBTklNQUxTLmxlbmd0aCldO1xuICBjb25zdCByYW5kb20gPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHJpbmcoMiwgNik7XG4gIHJldHVybiBgYW5vbi0ke2FkamVjdGl2ZX0tJHthbmltYWx9LSR7cmFuZG9tfWA7XG59XG5cbi8qKlxuICogR2VuZXJhdGUgYSB1bmlxdWUgSUQgZm9yIHBlcnNvbmFzXG4gKi9cbi8vIFByZS1jb21waWxlZCByZWdleCBmb3IgYmV0dGVyIHBlcmZvcm1hbmNlIChhdm9pZHMgY3JlYXRpbmcgcmVnZXggb24gZWFjaCBjaGFyYWN0ZXIpXG5jb25zdCBBTFBIQU5VTUVSSUNfUkVHRVggPSAvW2EtejAtOV0vO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVVbmlxdWVJZChwZXJzb25hTmFtZTogc3RyaW5nLCBhdXRob3I/OiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpO1xuICBjb25zdCBkYXRlU3RyID0gbm93LnRvSVNPU3RyaW5nKCkuc2xpY2UoMCwgMTApLnJlcGxhY2VBbGwoJy0nLCAnJyk7XG4gIGNvbnN0IHRpbWVTdHIgPSBub3cudG9UaW1lU3RyaW5nKCkuc2xpY2UoMCwgOCkucmVwbGFjZUFsbCgnOicsICcnKTtcbiAgLy8gSXNzdWUgIzg0ODogQWRkIG1pbGxpc2Vjb25kcyArIHJhbmRvbSBzdWZmaXggZm9yIHN1Yi1zZWNvbmQgdW5pcXVlbmVzcy5cbiAgLy8gQ3JpdGljYWwgZm9yIGNvcHktb24td3JpdGUsIHN3YXJtIG9wZXJhdGlvbnMsIGFuZCByYXBpZCBwcm9ncmFtbWF0aWMgY3JlYXRpb24uXG4gIGNvbnN0IG1zU3RyID0gbm93LmdldE1pbGxpc2Vjb25kcygpLnRvU3RyaW5nKCkucGFkU3RhcnQoMywgJzAnKTtcbiAgY29uc3QgcmFuZCA9IE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cmluZygyLCA2KTtcbiAgLy8gU0VDVVJJVFkgRklYOiBQcmV2ZW50IFJlRG9TIGJ5IHVzaW5nIGEgc2luZ2xlLXBhc3MgYXBwcm9hY2hcbiAgLy8gUHJldmlvdXNseTogTXVsdGlwbGUgcmVwbGFjZSgpIG9wZXJhdGlvbnMgd2l0aCB1bmJvdW5kZWQgcXVhbnRpZmllcnMgY291bGQgY2F1c2UgZXhwb25lbnRpYWwgYmFja3RyYWNraW5nXG4gIC8vIE5vdzogU2luZ2xlLXBhc3MgdHJhbnNmb3JtYXRpb24gd2l0aCBidWlsdC1pbiBsZW5ndGggbGltaXRcbiAgY29uc3Qgbm9ybWFsaXplZCA9IHBlcnNvbmFOYW1lLnRvTG93ZXJDYXNlKCk7XG4gIGNvbnN0IHNhbml0aXplZE5hbWUgPSBub3JtYWxpemVkXG4gICAgLnNwbGl0KCcnKVxuICAgIC5tYXAoY2hhciA9PiBBTFBIQU5VTUVSSUNfUkVHRVgudGVzdChjaGFyKSA/IGNoYXIgOiAnLScpXG4gICAgLmpvaW4oJycpXG4gICAgLnN1YnN0cmluZygwLCAxMDApIC8vIExpbWl0IGFmdGVyIHRyYW5zZm9ybWF0aW9uIHRvIHByZXNlcnZlIHN0cnVjdHVyZVxuICAgIC5yZXBsYWNlQWxsKC8oXi0rKXwoLSskKS9nLCAnJykgLy8gT25seSB0cmltIGxlYWRpbmcvdHJhaWxpbmcgaHlwaGVuc1xuICAgIC5yZXBsYWNlQWxsKC8tezIsfS9nLCAnLScpOyAvLyBDb2xsYXBzZSBtdWx0aXBsZSBoeXBoZW5zXG4gIGNvbnN0IHdob01hZGVJdCA9IGF1dGhvciB8fCBnZW5lcmF0ZUFub255bW91c0lkKCk7XG5cbiAgcmV0dXJuIGAke3Nhbml0aXplZE5hbWV9XyR7ZGF0ZVN0cn0tJHt0aW1lU3RyfSR7bXNTdHJ9LSR7cmFuZH1fJHt3aG9NYWRlSXR9YDtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IHRleHQgdG8gVVJMLXNhZmUgc2x1Z1xuICovXG5leHBvcnQgZnVuY3Rpb24gc2x1Z2lmeSh0ZXh0OiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBTRUNVUklUWSBGSVg6IFByZXZlbnQgUmVEb1MgYnkgdXNpbmcgYSBzaW5nbGUtcGFzcyBhcHByb2FjaFxuICAvLyBQcmV2aW91c2x5OiBNdWx0aXBsZSByZXBsYWNlKCkgb3BlcmF0aW9ucyB3aXRoIHVuYm91bmRlZCBxdWFudGlmaWVycyBjb3VsZCBjYXVzZSBleHBvbmVudGlhbCBiYWNrdHJhY2tpbmdcbiAgLy8gTm93OiBTaW5nbGUtcGFzcyB0cmFuc2Zvcm1hdGlvbiB3aXRoIGJ1aWx0LWluIGxlbmd0aCBsaW1pdFxuICBjb25zdCBub3JtYWxpemVkID0gdGV4dC50b0xvd2VyQ2FzZSgpO1xuICBjb25zdCB0cmFuc2Zvcm1lZCA9IG5vcm1hbGl6ZWRcbiAgICAuc3BsaXQoJycpXG4gICAgLm1hcChjaGFyID0+IEFMUEhBTlVNRVJJQ19SRUdFWC50ZXN0KGNoYXIpID8gY2hhciA6ICctJylcbiAgICAuam9pbignJyk7XG4gIFxuICAvLyBTRUNVUklUWSBGSVg6IEF2b2lkIHBvbHlub21pYWwgcmVnZXggYnkgdXNpbmcgc2VwYXJhdGUgb3BlcmF0aW9uc1xuICAvLyBUcmltIGxlYWRpbmcgaHlwaGVuc1xuICBsZXQgc3RhcnQgPSAwO1xuICB3aGlsZSAoc3RhcnQgPCB0cmFuc2Zvcm1lZC5sZW5ndGggJiYgdHJhbnNmb3JtZWRbc3RhcnRdID09PSAnLScpIHtcbiAgICBzdGFydCsrO1xuICB9XG4gIFxuICAvLyBUcmltIHRyYWlsaW5nIGh5cGhlbnNcbiAgbGV0IGVuZCA9IHRyYW5zZm9ybWVkLmxlbmd0aCAtIDE7XG4gIHdoaWxlIChlbmQgPj0gc3RhcnQgJiYgdHJhbnNmb3JtZWRbZW5kXSA9PT0gJy0nKSB7XG4gICAgZW5kLS07XG4gIH1cbiAgXG4gIC8vIEV4dHJhY3QgdGhlIHRyaW1tZWQgcG9ydGlvbiBhbmQgY29sbGFwc2UgbXVsdGlwbGUgaHlwaGVuc1xuICBjb25zdCB0cmltbWVkID0gdHJhbnNmb3JtZWQuc2xpY2Uoc3RhcnQsIGVuZCArIDEpO1xuICByZXR1cm4gdHJpbW1lZC5yZXBsYWNlQWxsKC8tezIsfS9nLCAnLScpOyAvLyBUaGlzIGlzIHNhZmUgYXMgaXQncyBsaW5lYXJcbn1cblxuLyoqXG4gKiBFbnN1cmUgYSBkaXJlY3RvcnkgZXhpc3RzLCBjcmVhdGUgaWYgaXQgZG9lc24ndFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZW5zdXJlRGlyZWN0b3J5KGRpclBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBmaWxlT3BzID0gZ2V0RmlsZU9wZXJhdGlvbnNTZXJ2aWNlKCk7XG4gIGNvbnN0IGV4aXN0cyA9IGF3YWl0IGZpbGVPcHMuZXhpc3RzKGRpclBhdGgpO1xuXG4gIGlmICghZXhpc3RzKSB7XG4gICAgbG9nZ2VyLmRlYnVnKGBDcmVhdGluZyBkaXJlY3Rvcnk6ICR7ZGlyUGF0aH1gKTtcbiAgICBhd2FpdCBmaWxlT3BzLmNyZWF0ZURpcmVjdG9yeShkaXJQYXRoKTtcbiAgfVxufVxuXG4vKipcbiAqIENoZWNrIGlmIGEgZmlsZSBleGlzdHNcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbGVFeGlzdHMoZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBjb25zdCBmaWxlT3BzID0gZ2V0RmlsZU9wZXJhdGlvbnNTZXJ2aWNlKCk7XG4gIHJldHVybiBmaWxlT3BzLmV4aXN0cyhmaWxlUGF0aCk7XG59XG5cbi8qKlxuICogR2V0IGZpbGUgc2l6ZSBpbiBieXRlc1xuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0RmlsZVNpemUoZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPiB7XG4gIGNvbnN0IGZpbGVPcHMgPSBnZXRGaWxlT3BlcmF0aW9uc1NlcnZpY2UoKTtcbiAgY29uc3Qgc3RhdHMgPSBhd2FpdCBmaWxlT3BzLnN0YXQoZmlsZVBhdGgpO1xuICByZXR1cm4gc3RhdHMuc2l6ZTtcbn1cblxuLyoqXG4gKiBDcmVhdGUgYSBiYWNrdXAgb2YgYSBkaXJlY3RvcnlcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUJhY2t1cChzb3VyY2VQYXRoOiBzdHJpbmcsIGJhY2t1cFBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAvLyBFbnN1cmUgYmFja3VwIGRpcmVjdG9yeSBleGlzdHNcbiAgY29uc3QgYmFja3VwRGlyID0gcGF0aC5kaXJuYW1lKGJhY2t1cFBhdGgpO1xuICBhd2FpdCBlbnN1cmVEaXJlY3RvcnkoYmFja3VwRGlyKTtcbiAgXG4gIC8vIENvcHkgZGlyZWN0b3J5IHJlY3Vyc2l2ZWx5XG4gIGF3YWl0IGZzLmNwKHNvdXJjZVBhdGgsIGJhY2t1cFBhdGgsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xufSJdfQ==