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.

291 lines 34.7 kB
import * as fs from 'fs/promises'; import * as path from 'path'; import { logger } from './logger.js'; /** * Cross-platform file operations utility * Centralizes common file operations with progress reporting and error handling */ export class FileOperations { /** * Recursively copy a directory with progress reporting * Works cross-platform without relying on shell commands */ static async copyDirectory(src, dest, options = {}) { const { onProgress, excludePatterns = [], maxRetries = 3 } = options; // First, calculate total files for progress reporting const stats = await this.calculateDirectoryStats(src, excludePatterns); let copiedFiles = 0; await this.copyDirectoryRecursive(src, dest, excludePatterns, maxRetries, (currentFile) => { copiedFiles++; if (onProgress) { onProgress(copiedFiles, stats.totalFiles, currentFile); } }); } /** * Calculate directory statistics for progress reporting */ static async calculateDirectoryStats(dir, excludePatterns) { let totalFiles = 0; let totalSize = 0; try { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); // Skip excluded patterns if (this.shouldExclude(entry.name, excludePatterns)) { continue; } if (entry.isDirectory()) { const subStats = await this.calculateDirectoryStats(fullPath, excludePatterns); totalFiles += subStats.totalFiles; totalSize += subStats.totalSize; } else { totalFiles++; try { const stat = await fs.stat(fullPath); totalSize += stat.size; } catch { // Ignore stat errors } } } } catch (error) { logger.warn(`[FileOperations] Error calculating stats for ${dir}:`, error); } return { totalFiles, totalSize }; } /** * Internal recursive copy implementation */ static async copyDirectoryRecursive(src, dest, excludePatterns, maxRetries, onFileCopied) { // Ensure destination directory exists await fs.mkdir(dest, { recursive: true }); const entries = await fs.readdir(src, { withFileTypes: true }); for (const entry of entries) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); // Skip excluded patterns if (this.shouldExclude(entry.name, excludePatterns)) { logger.debug(`[FileOperations] Skipping excluded: ${entry.name}`); continue; } if (entry.isDirectory()) { await this.copyDirectoryRecursive(srcPath, destPath, excludePatterns, maxRetries, onFileCopied); } else { await this.copyFileWithRetry(srcPath, destPath, maxRetries); onFileCopied(srcPath); } } } /** * Copy a single file with retry logic */ static async copyFileWithRetry(src, dest, maxRetries) { let lastError = null; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { await fs.copyFile(src, dest); return; // Success } catch (error) { lastError = error; logger.debug(`[FileOperations] Copy attempt ${attempt} failed for ${src}: ${error}`); if (attempt < maxRetries) { // Wait before retry (exponential backoff) await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 100)); } } } // All retries failed throw new Error(`Failed to copy ${src} after ${maxRetries} attempts: ${lastError?.message}`); } /** * Check if a file/directory should be excluded */ static shouldExclude(name, patterns) { for (const pattern of patterns) { if (pattern.includes('*')) { // Simple glob support const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$'); if (regex.test(name)) return true; } else if (name === pattern) { return true; } } return false; } /** * Remove a directory with progress reporting */ static async removeDirectory(dir, options = {}) { const stats = await this.calculateDirectoryStats(dir, []); let removedFiles = 0; await this.removeDirectoryRecursive(dir, () => { removedFiles++; if (options.onProgress) { options.onProgress(removedFiles, stats.totalFiles); } }); } /** * Internal recursive remove implementation */ static async removeDirectoryRecursive(dir, onFileRemoved) { try { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { await this.removeDirectoryRecursive(fullPath, onFileRemoved); } else { await fs.unlink(fullPath); onFileRemoved(); } } // Remove the now-empty directory await fs.rmdir(dir); } catch (error) { logger.error(`[FileOperations] Error removing directory ${dir}:`, error); throw error; } } /** * Create a transaction manager for atomic file operations */ static createTransaction() { return new FileTransaction(); } } /** * Transaction manager for atomic file operations * Ensures all operations succeed or all are rolled back */ export class FileTransaction { operations = []; completed = false; /** * Add a move operation to the transaction */ async addMove(source, destination) { if (this.completed) { throw new Error('Transaction already completed'); } // Perform the move await fs.rename(source, destination); // Add rollback operation this.operations.push({ type: 'move', source, destination, rollback: async () => { try { await fs.rename(destination, source); } catch (error) { logger.error(`[FileTransaction] Failed to rollback move from ${destination} to ${source}:`, error); } } }); } /** * Add a copy operation to the transaction */ async addCopy(source, destination) { if (this.completed) { throw new Error('Transaction already completed'); } // Perform the copy await FileOperations.copyDirectory(source, destination); // Add rollback operation this.operations.push({ type: 'copy', source, destination, rollback: async () => { try { await fs.rm(destination, { recursive: true, force: true }); } catch (error) { logger.error(`[FileTransaction] Failed to rollback copy at ${destination}:`, error); } } }); } /** * Add a delete operation to the transaction */ async addDelete(path, backupPath) { if (this.completed) { throw new Error('Transaction already completed'); } // If backup path provided, move instead of delete if (backupPath) { await fs.rename(path, backupPath); this.operations.push({ type: 'delete', source: path, destination: backupPath, rollback: async () => { try { await fs.rename(backupPath, path); } catch (error) { logger.error(`[FileTransaction] Failed to restore deleted item from ${backupPath} to ${path}:`, error); } } }); } else { // Direct delete (no rollback possible) await fs.rm(path, { recursive: true, force: true }); this.operations.push({ type: 'delete', source: path, rollback: async () => { logger.warn(`[FileTransaction] Cannot rollback permanent deletion of ${path}`); } }); } } /** * Commit the transaction (mark as successful) */ commit() { this.completed = true; } /** * Rollback all operations in reverse order */ async rollback() { logger.info(`[FileTransaction] Rolling back ${this.operations.length} operations`); // Rollback in reverse order for (let i = this.operations.length - 1; i >= 0; i--) { const operation = this.operations[i]; logger.info(`[FileTransaction] Rolling back ${operation.type} operation`); try { await operation.rollback(); } catch (error) { logger.error(`[FileTransaction] Rollback failed for operation ${i}:`, error); // Continue with other rollbacks } } this.completed = true; } /** * Check if any operations have been performed */ hasOperations() { return this.operations.length > 0; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZU9wZXJhdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvZmlsZU9wZXJhdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQWFyQzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sY0FBYztJQUN6Qjs7O09BR0c7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FDeEIsR0FBVyxFQUNYLElBQVksRUFDWixVQUF1QixFQUFFO1FBRXpCLE1BQU0sRUFBRSxVQUFVLEVBQUUsZUFBZSxHQUFHLEVBQUUsRUFBRSxVQUFVLEdBQUcsQ0FBQyxFQUFFLEdBQUcsT0FBTyxDQUFDO1FBRXJFLHNEQUFzRDtRQUN0RCxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxHQUFHLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDdkUsSUFBSSxXQUFXLEdBQUcsQ0FBQyxDQUFDO1FBRXBCLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUMvQixHQUFHLEVBQ0gsSUFBSSxFQUNKLGVBQWUsRUFDZixVQUFVLEVBQ1YsQ0FBQyxXQUFXLEVBQUUsRUFBRTtZQUNkLFdBQVcsRUFBRSxDQUFDO1lBQ2QsSUFBSSxVQUFVLEVBQUUsQ0FBQztnQkFDZixVQUFVLENBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQyxVQUFVLEVBQUUsV0FBVyxDQUFDLENBQUM7WUFDekQsQ0FBQztRQUNILENBQUMsQ0FDRixDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0ssTUFBTSxDQUFDLEtBQUssQ0FBQyx1QkFBdUIsQ0FDMUMsR0FBVyxFQUNYLGVBQXlCO1FBRXpCLElBQUksVUFBVSxHQUFHLENBQUMsQ0FBQztRQUNuQixJQUFJLFNB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SSxFQUFFLENBQUMsQ0FBQztnQkFDakYsQ0FBQzthQUNGLENBQUMsQ0FBQztRQUNMLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNO1FBQ0osSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLFFBQVE7UUFDWixNQUFNLENBQUMsSUFBSSxDQUFDLGtDQUFrQyxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sYUFBYSxDQUFDLENBQUM7UUFFbkYsNEJBQTRCO1FBQzVCLEtBQUssSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNyRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3JDLE1BQU0sQ0FBQyxJQUFJLENBQUMsa0NBQWtDLFNBQVMsQ0FBQyxJQUFJLFlBQVksQ0FBQyxDQUFDO1lBRTFFLElBQUksQ0FBQztnQkFDSCxNQUFNLFNBQVMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUM3QixDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixNQUFNLENBQUMsS0FBSyxDQUFDLG1EQUFtRCxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztnQkFDN0UsZ0NBQWdDO1lBQ2xDLENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsYUFBYTtRQUNYLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29weU9wdGlvbnMge1xuICBvblByb2dyZXNzPzogKGNvcGllZDogbnVtYmVyLCB0b3RhbDogbnVtYmVyLCBjdXJyZW50RmlsZTogc3RyaW5nKSA9PiB2b2lkO1xuICBleGNsdWRlUGF0dGVybnM/OiBzdHJpbmdbXTtcbiAgbWF4UmV0cmllcz86IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBGaWxlU3RhdHMge1xuICB0b3RhbEZpbGVzOiBudW1iZXI7XG4gIHRvdGFsU2l6ZTogbnVtYmVyO1xufVxuXG4vKipcbiAqIENyb3NzLXBsYXRmb3JtIGZpbGUgb3BlcmF0aW9ucyB1dGlsaXR5XG4gKiBDZW50cmFsaXplcyBjb21tb24gZmlsZSBvcGVyYXRpb25zIHdpdGggcHJvZ3Jlc3MgcmVwb3J0aW5nIGFuZCBlcnJvciBoYW5kbGluZ1xuICovXG5leHBvcnQgY2xhc3MgRmlsZU9wZXJhdGlvbnMge1xuICAvKipcbiAgICogUmVjdXJzaXZlbHkgY29weSBhIGRpcmVjdG9yeSB3aXRoIHByb2dyZXNzIHJlcG9ydGluZ1xuICAgKiBXb3JrcyBjcm9zcy1wbGF0Zm9ybSB3aXRob3V0IHJlbHlpbmcgb24gc2hlbGwgY29tbWFuZHNcbiAgICovXG4gIHN0YXRpYyBhc3luYyBjb3B5RGlyZWN0b3J5KFxuICAgIHNyYzogc3RyaW5nLCBcbiAgICBkZXN0OiBzdHJpbmcsIFxuICAgIG9wdGlvbnM6IENvcHlPcHRpb25zID0ge31cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyBvblByb2dyZXNzLCBleGNsdWRlUGF0dGVybnMgPSBbXSwgbWF4UmV0cmllcyA9IDMgfSA9IG9wdGlvbnM7XG4gICAgXG4gICAgLy8gRmlyc3QsIGNhbGN1bGF0ZSB0b3RhbCBmaWxlcyBmb3IgcHJvZ3Jlc3MgcmVwb3J0aW5nXG4gICAgY29uc3Qgc3RhdHMgPSBhd2FpdCB0aGlzLmNhbGN1bGF0ZURpcmVjdG9yeVN0YXRzKHNyYywgZXhjbHVkZVBhdHRlcm5zKTtcbiAgICBsZXQgY29waWVkRmlsZXMgPSAwO1xuICAgIFxuICAgIGF3YWl0IHRoaXMuY29weURpcmVjdG9yeVJlY3Vyc2l2ZShcbiAgICAgIHNyYywgXG4gICAgICBkZXN0LCBcbiAgICAgIGV4Y2x1ZGVQYXR0ZXJucyxcbiAgICAgIG1heFJldHJpZXMsXG4gICAgICAoY3VycmVudEZpbGUpID0+IHtcbiAgICAgICAgY29waWVkRmlsZXMrKztcbiAgICAgICAgaWYgKG9uUHJvZ3Jlc3MpIHtcbiAgICAgICAgICBvblByb2dyZXNzKGNvcGllZEZpbGVzLCBzdGF0cy50b3RhbEZpbGVzLCBjdXJyZW50RmlsZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICApO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2FsY3VsYXRlIGRpcmVjdG9yeSBzdGF0aXN0aWNzIGZvciBwcm9ncmVzcyByZXBvcnRpbmdcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIGFzeW5jIGNhbGN1bGF0ZURpcmVjdG9yeVN0YXRzKFxuICAgIGRpcjogc3RyaW5nLFxuICAgIGV4Y2x1ZGVQYXR0ZXJuczogc3RyaW5nW11cbiAgKTogUHJvbWlzZTxGaWxlU3RhdHM+IHtcbiAgICBsZXQgdG90YWxGaWxlcyA9IDA7XG4gICAgbGV0IHRvdGFsU2l6ZSA9IDA7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGVudHJpZXMgPSBhd2FpdCBmcy5yZWFkZGlyKGRpciwgeyB3aXRoRmlsZVR5cGVzOiB0cnVlIH0pO1xuICAgICAgXG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgICAgY29uc3QgZnVsbFBhdGggPSBwYXRoLmpvaW4oZGlyLCBlbnRyeS5uYW1lKTtcbiAgICAgICAgXG4gICAgICAgIC8vIFNraXAgZXhjbHVkZWQgcGF0dGVybnNcbiAgICAgICAgaWYgKHRoaXMuc2hvdWxkRXhjbHVkZShlbnRyeS5uYW1lLCBleGNsdWRlUGF0dGVybnMpKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIGlmIChlbnRyeS5pc0RpcmVjdG9yeSgpKSB7XG4gICAgICAgICAgY29uc3Qgc3ViU3RhdHMgPSBhd2FpdCB0aGlzLmNhbGN1bGF0ZURpcmVjdG9yeVN0YXRzKGZ1bGxQYXRoLCBleGNsdWRlUGF0dGVybnMpO1xuICAgICAgICAgIHRvdGFsRmlsZXMgKz0gc3ViU3RhdHMudG90YWxGaWxlcztcbiAgICAgICAgICB0b3RhbFNpemUgKz0gc3ViU3RhdHMudG90YWxTaXplO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRvdGFsRmlsZXMrKztcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3Qgc3RhdCA9IGF3YWl0IGZzLnN0YXQoZnVsbFBhdGgpO1xuICAgICAgICAgICAgdG90YWxTaXplICs9IHN0YXQuc2l6ZTtcbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIC8vIElnbm9yZSBzdGF0IGVycm9yc1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIud2FybihgW0ZpbGVPcGVyYXRpb25zXSBFcnJvciBjYWxjdWxhdGluZyBzdGF0cyBmb3IgJHtkaXJ9OmAsIGVycm9yKTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHsgdG90YWxGaWxlcywgdG90YWxTaXplIH07XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBJbnRlcm5hbCByZWN1cnNpdmUgY29weSBpbXBsZW1lbnRhdGlvblxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgYXN5bmMgY29weURpcmVjdG9yeVJlY3Vyc2l2ZShcbiAgICBzcmM6IHN0cmluZyxcbiAgICBkZXN0OiBzdHJpbmcsXG4gICAgZXhjbHVkZVBhdHRlcm5zOiBzdHJpbmdbXSxcbiAgICBtYXhSZXRyaWVzOiBudW1iZXIsXG4gICAgb25GaWxlQ29waWVkOiAoZmlsZTogc3RyaW5nKSA9PiB2b2lkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIEVuc3VyZSBkZXN0aW5hdGlvbiBkaXJlY3RvcnkgZXhpc3RzXG4gICAgYXdhaXQgZnMubWtkaXIoZGVzdCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgXG4gICAgY29uc3QgZW50cmllcyA9IGF3YWl0IGZzLnJlYWRkaXIoc3JjLCB7IHdpdGhGaWxlVHlwZXM6IHRydWUgfSk7XG4gICAgXG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgICBjb25zdCBzcmNQYXRoID0gcGF0aC5qb2luKHNyYywgZW50cnkubmFtZSk7XG4gICAgICBjb25zdCBkZXN0UGF0aCA9IHBhdGguam9pbihkZXN0LCBlbnRyeS5uYW1lKTtcbiAgICAgIFxuICAgICAgLy8gU2tpcCBleGNsdWRlZCBwYXR0ZXJuc1xuICAgICAgaWYgKHRoaXMuc2hvdWxkRXhjbHVkZShlbnRyeS5uYW1lLCBleGNsdWRlUGF0dGVybnMpKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgW0ZpbGVPcGVyYXRpb25zXSBTa2lwcGluZyBleGNsdWRlZDogJHtlbnRyeS5uYW1lfWApO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgaWYgKGVudHJ5LmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5jb3B5RGlyZWN0b3J5UmVjdXJzaXZlKFxuICAgICAgICAgIHNyY1BhdGgsIFxuICAgICAgICAgIGRlc3RQYXRoLCBcbiAgICAgICAgICBleGNsdWRlUGF0dGVybnMsXG4gICAgICAgICAgbWF4UmV0cmllcyxcbiAgICAgICAgICBvbkZpbGVDb3BpZWRcbiAgICAgICAgKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGF3YWl0IHRoaXMuY29weUZpbGVXaXRoUmV0cnkoc3JjUGF0aCwgZGVzdFBhdGgsIG1heFJldHJpZXMpO1xuICAgICAgICBvbkZpbGVDb3BpZWQoc3JjUGF0aCk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogQ29weSBhIHNpbmdsZSBmaWxlIHdpdGggcmV0cnkgbG9naWNcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIGFzeW5jIGNvcHlGaWxlV2l0aFJldHJ5KFxuICAgIHNyYzogc3RyaW5nLFxuICAgIGRlc3Q6IHN0cmluZyxcbiAgICBtYXhSZXRyaWVzOiBudW1iZXJcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IGxhc3RFcnJvcjogRXJyb3IgfCBudWxsID0gbnVsbDtcbiAgICBcbiAgICBmb3IgKGxldCBhdHRlbXB0ID0gMTsgYXR0ZW1wdCA8PSBtYXhSZXRyaWVzOyBhdHRlbXB0KyspIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZzLmNvcHlGaWxlKHNyYywgZGVzdCk7XG4gICAgICAgIHJldHVybjsgLy8gU3VjY2Vzc1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbGFzdEVycm9yID0gZXJyb3IgYXMgRXJyb3I7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgW0ZpbGVPcGVyYXRpb25zXSBDb3B5IGF0dGVtcHQgJHthdHRlbXB0fSBmYWlsZWQgZm9yICR7c3JjfTogJHtlcnJvcn1gKTtcbiAgICAgICAgXG4gICAgICAgIGlmIChhdHRlbXB0IDwgbWF4UmV0cmllcykge1xuICAgICAgICAgIC8vIFdhaXQgYmVmb3JlIHJldHJ5IChleHBvbmVudGlhbCBiYWNrb2ZmKVxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlc29sdmUgPT4gc2V0VGltZW91dChyZXNvbHZlLCBNYXRoLnBvdygyLCBhdHRlbXB0KSAqIDEwMCkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIC8vIEFsbCByZXRyaWVzIGZhaWxlZFxuICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGNvcHkgJHtzcmN9IGFmdGVyICR7bWF4UmV0cmllc30gYXR0ZW1wdHM6ICR7bGFzdEVycm9yPy5tZXNzYWdlfWApO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2hlY2sgaWYgYSBmaWxlL2RpcmVjdG9yeSBzaG91bGQgYmUgZXhjbHVkZWRcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIHNob3VsZEV4Y2x1ZGUobmFtZTogc3RyaW5nLCBwYXR0ZXJuczogc3RyaW5nW10pOiBib29sZWFuIHtcbiAgICBmb3IgKGNvbnN0IHBhdHRlcm4gb2YgcGF0dGVybnMpIHtcbiAgICAgIGlmIChwYXR0ZXJuLmluY2x1ZGVzKCcqJykpIHtcbiAgICAgICAgLy8gU2ltcGxlIGdsb2Igc3VwcG9ydFxuICAgICAgICBjb25zdCByZWdleCA9IG5ldyBSZWdFeHAoJ14nICsgcGF0dGVybi5yZXBsYWNlKC9cXCovZywgJy4qJykgKyAnJCcpO1xuICAgICAgICBpZiAocmVnZXgudGVzdChuYW1lKSkgcmV0dXJuIHRydWU7XG4gICAgICB9IGVsc2UgaWYgKG5hbWUgPT09IHBhdHRlcm4pIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJlbW92ZSBhIGRpcmVjdG9yeSB3aXRoIHByb2dyZXNzIHJlcG9ydGluZ1xuICAgKi9cbiAgc3RhdGljIGFzeW5jIHJlbW92ZURpcmVjdG9yeShcbiAgICBkaXI6IHN0cmluZyxcbiAgICBvcHRpb25zOiB7IG9uUHJvZ3Jlc3M/OiAocmVtb3ZlZDogbnVtYmVyLCB0b3RhbDogbnVtYmVyKSA9PiB2b2lkIH0gPSB7fVxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzdGF0cyA9IGF3YWl0IHRoaXMuY2FsY3VsYXRlRGlyZWN0b3J5U3RhdHMoZGlyLCBbXSk7XG4gICAgbGV0IHJlbW92ZWRGaWxlcyA9IDA7XG4gICAgXG4gICAgYXdhaXQgdGhpcy5yZW1vdmVEaXJlY3RvcnlSZWN1cnNpdmUoZGlyLCAoKSA9PiB7XG4gICAgICByZW1vdmVkRmlsZXMrKztcbiAgICAgIGlmIChvcHRpb25zLm9uUHJvZ3Jlc3MpIHtcbiAgICAgICAgb3B0aW9ucy5vblByb2dyZXNzKHJlbW92ZWRGaWxlcywgc3RhdHMudG90YWxGaWxlcyk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBJbnRlcm5hbCByZWN1cnNpdmUgcmVtb3ZlIGltcGxlbWVudGF0aW9uXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBhc3luYyByZW1vdmVEaXJlY3RvcnlSZWN1cnNpdmUoXG4gICAgZGlyOiBzdHJpbmcsXG4gICAgb25GaWxlUmVtb3ZlZDogKCkgPT4gdm9pZFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZW50cmllcyA9IGF3YWl0IGZzLnJlYWRkaXIoZGlyLCB7IHdpdGhGaWxlVHlwZXM6IHRydWUgfSk7XG4gICAgICBcbiAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgICBjb25zdCBmdWxsUGF0aCA9IHBhdGguam9pbihkaXIsIGVudHJ5Lm5hbWUpO1xuICAgICAgICBcbiAgICAgICAgaWYgKGVudHJ5LmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgICBhd2FpdCB0aGlzLnJlbW92ZURpcmVjdG9yeVJlY3Vyc2l2ZShmdWxsUGF0aCwgb25GaWxlUmVtb3ZlZCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYXdhaXQgZnMudW5saW5rKGZ1bGxQYXRoKTtcbiAgICAgICAgICBvbkZpbGVSZW1vdmVkKCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gUmVtb3ZlIHRoZSBub3ctZW1wdHkgZGlyZWN0b3J5XG4gICAgICBhd2FpdCBmcy5ybWRpcihkaXIpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoYFtGaWxlT3BlcmF0aW9uc10gRXJyb3IgcmVtb3ZpbmcgZGlyZWN0b3J5ICR7ZGlyfTpgLCBlcnJvcik7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDcmVhdGUgYSB0cmFuc2FjdGlvbiBtYW5hZ2VyIGZvciBhdG9taWMgZmlsZSBvcGVyYXRpb25zXG4gICAqL1xuICBzdGF0aWMgY3JlYXRlVHJhbnNhY3Rpb24oKTogRmlsZVRyYW5zYWN0aW9uIHtcbiAgICByZXR1cm4gbmV3IEZpbGVUcmFuc2FjdGlvbigpO1xuICB9XG59XG5cbi8qKlxuICogVHJhbnNhY3Rpb24gbWFuYWdlciBmb3IgYXRvbWljIGZpbGUgb3BlcmF0aW9uc1xuICogRW5zdXJlcyBhbGwgb3BlcmF0aW9ucyBzdWNjZWVkIG9yIGFsbCBhcmUgcm9sbGVkIGJhY2tcbiAqL1xuZXhwb3J0IGNsYXNzIEZpbGVUcmFuc2FjdGlvbiB7XG4gIHByaXZhdGUgb3BlcmF0aW9uczogQXJyYXk8e1xuICAgIHR5cGU6ICdtb3ZlJyB8ICdjb3B5JyB8ICdkZWxldGUnIHwgJ2NyZWF0ZSc7XG4gICAgc291cmNlPzogc3RyaW5nO1xuICAgIGRlc3RpbmF0aW9uPzogc3RyaW5nO1xuICAgIHJvbGxiYWNrOiAoKSA9PiBQcm9taXNlPHZvaWQ+O1xuICB9PiA9IFtdO1xuICBcbiAgcHJpdmF0ZSBjb21wbGV0ZWQgPSBmYWxzZTtcbiAgXG4gIC8qKlxuICAgKiBBZGQgYSBtb3ZlIG9wZXJhdGlvbiB0byB0aGUgdHJhbnNhY3Rpb25cbiAgICovXG4gIGFzeW5jIGFkZE1vdmUoc291cmNlOiBzdHJpbmcsIGRlc3RpbmF0aW9uOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5jb21wbGV0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVHJhbnNhY3Rpb24gYWxyZWFkeSBjb21wbGV0ZWQnKTtcbiAgICB9XG4gICAgXG4gICAgLy8gUGVyZm9ybSB0aGUgbW92ZVxuICAgIGF3YWl0IGZzLnJlbmFtZShzb3VyY2UsIGRlc3RpbmF0aW9uKTtcbiAgICBcbiAgICAvLyBBZGQgcm9sbGJhY2sgb3BlcmF0aW9uXG4gICAgdGhpcy5vcGVyYXRpb25zLnB1c2goe1xuICAgICAgdHlwZTogJ21vdmUnLFxuICAgICAgc291cmNlLFxuICAgICAgZGVzdGluYXRpb24sXG4gICAgICByb2xsYmFjazogYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IGZzLnJlbmFtZShkZXN0aW5hdGlvbiwgc291cmNlKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoYFtGaWxlVHJhbnNhY3Rpb25dIEZhaWxlZCB0byByb2xsYmFjayBtb3ZlIGZyb20gJHtkZXN0aW5hdGlvbn0gdG8gJHtzb3VyY2V9OmAsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogQWRkIGEgY29weSBvcGVyYXRpb24gdG8gdGhlIHRyYW5zYWN0aW9uXG4gICAqL1xuICBhc3luYyBhZGRDb3B5KHNvdXJjZTogc3RyaW5nLCBkZXN0aW5hdGlvbjogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuY29tcGxldGVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RyYW5zYWN0aW9uIGFscmVhZHkgY29tcGxldGVkJyk7XG4gICAgfVxuICAgIFxuICAgIC8vIFBlcmZvcm0gdGhlIGNvcHlcbiAgICBhd2FpdCBGaWxlT3BlcmF0aW9ucy5jb3B5RGlyZWN0b3J5KHNvdXJjZSwgZGVzdGluYXRpb24pO1xuICAgIFxuICAgIC8vIEFkZCByb2xsYmFjayBvcGVyYXRpb25cbiAgICB0aGlzLm9wZXJhdGlvbnMucHVzaCh7XG4gICAgICB0eXBlOiAnY29weScsXG4gICAgICBzb3VyY2UsXG4gICAgICBkZXN0aW5hdGlvbixcbiAgICAgIHJvbGxiYWNrOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgZnMucm0oZGVzdGluYXRpb24sIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoYFtGaWxlVHJhbnNhY3Rpb25dIEZhaWxlZCB0byByb2xsYmFjayBjb3B5IGF0ICR7ZGVzdGluYXRpb259OmAsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogQWRkIGEgZGVsZXRlIG9wZXJhdGlvbiB0byB0aGUgdHJhbnNhY3Rpb25cbiAgICovXG4gIGFzeW5jIGFkZERlbGV0ZShwYXRoOiBzdHJpbmcsIGJhY2t1cFBhdGg/OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5jb21wbGV0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVHJhbnNhY3Rpb24gYWxyZWFkeSBjb21wbGV0ZWQnKTtcbiAgICB9XG4gICAgXG4gICAgLy8gSWYgYmFja3VwIHBhdGggcHJvdmlkZWQsIG1vdmUgaW5zdGVhZCBvZiBkZWxldGVcbiAgICBpZiAoYmFja3VwUGF0aCkge1xuICAgICAgYXdhaXQgZnMucmVuYW1lKHBhdGgsIGJhY2t1cFBhdGgpO1xuICAgICAgXG4gICAgICB0aGlzLm9wZXJhdGlvbnMucHVzaCh7XG4gICAgICAgIHR5cGU6ICdkZWxldGUnLFxuICAgICAgICBzb3VyY2U6IHBhdGgsXG4gICAgICAgIGRlc3RpbmF0aW9uOiBiYWNrdXBQYXRoLFxuICAgICAgICByb2xsYmFjazogYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCBmcy5yZW5hbWUoYmFja3VwUGF0aCwgcGF0aCk7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGxvZ2dlci5lcnJvcihgW0ZpbGVUcmFuc2FjdGlvbl0gRmFpbGVkIHRvIHJlc3RvcmUgZGVsZXRlZCBpdGVtIGZyb20gJHtiYWNrdXBQYXRofSB0byAke3BhdGh9OmAsIGVycm9yKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBEaXJlY3QgZGVsZXRlIChubyByb2xsYmFjayBwb3NzaWJsZSlcbiAgICAgIGF3YWl0IGZzLnJtKHBhdGgsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICAgIFxuICAgICAgdGhpcy5vcGVyYXRpb25zLnB1c2goe1xuICAgICAgICB0eXBlOiAnZGVsZXRlJyxcbiAgICAgICAgc291cmNlOiBwYXRoLFxuICAgICAgICByb2xsYmFjazogYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIGxvZ2dlci53YXJuKGBbRmlsZVRyYW5zYWN0aW9uXSBDYW5ub3Qgcm9sbGJhY2sgcGVybWFuZW50IGRlbGV0aW9uIG9mICR7cGF0aH1gKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogQ29tbWl0IHRoZSB0cmFuc2FjdGlvbiAobWFyayBhcyBzdWNjZXNzZnVsKVxuICAgKi9cbiAgY29tbWl0KCk6IHZvaWQge1xuICAgIHRoaXMuY29tcGxldGVkID0gdHJ1ZTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJvbGxiYWNrIGFsbCBvcGVyYXRpb25zIGluIHJldmVyc2Ugb3JkZXJcbiAgICovXG4gIGFzeW5jIHJvbGxiYWNrKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGxvZ2dlci5pbmZvKGBbRmlsZVRyYW5zYWN0aW9uXSBSb2xsaW5nIGJhY2sgJHt0aGlzLm9wZXJhdGlvbnMubGVuZ3RofSBvcGVyYXRpb25zYCk7XG4gICAgXG4gICAgLy8gUm9sbGJhY2sgaW4gcmV2ZXJzZSBvcmRlclxuICAgIGZvciAobGV0IGkgPSB0aGlzLm9wZXJhdGlvbnMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIGNvbnN0IG9wZXJhdGlvbiA9IHRoaXMub3BlcmF0aW9uc1tpXTtcbiAgICAgIGxvZ2dlci5pbmZvKGBbRmlsZVRyYW5zYWN0aW9uXSBSb2xsaW5nIGJhY2sgJHtvcGVyYXRpb24udHlwZX0gb3BlcmF0aW9uYCk7XG4gICAgICBcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IG9wZXJhdGlvbi5yb2xsYmFjaygpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKGBbRmlsZVRyYW5zYWN0aW9uXSBSb2xsYmFjayBmYWlsZWQgZm9yIG9wZXJhdGlvbiAke2l9OmAsIGVycm9yKTtcbiAgICAgICAgLy8gQ29udGludWUgd2l0aCBvdGhlciByb2xsYmFja3NcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgdGhpcy5jb21wbGV0ZWQgPSB0cnVlO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2hlY2sgaWYgYW55IG9wZXJhdGlvbnMgaGF2ZSBiZWVuIHBlcmZvcm1lZFxuICAgKi9cbiAgaGFzT3BlcmF0aW9ucygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5vcGVyYXRpb25zLmxlbmd0aCA+IDA7XG4gIH1cbn0iXX0=