@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
JavaScript
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=