UNPKG

@mickdarling/dollhousemcp

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.

260 lines 33.9 kB
/** * Manage backups during updates */ import * as fs from 'fs/promises'; import * as fsSync from 'fs'; import * as path from 'path'; import { safeExec } from '../utils/git.js'; export class BackupManager { rootDir; backupsDir; constructor(rootDir) { // Validate rootDir parameter if provided if (rootDir) { // Prevent path traversal attacks first if (rootDir.includes('../') || rootDir.includes('..\\')) { throw new Error('rootDir cannot contain path traversal sequences'); } // Then check if it's absolute if (!path.isAbsolute(rootDir)) { throw new Error('rootDir must be an absolute path'); } } // Allow override for testing, default to process.cwd() this.rootDir = rootDir || process.cwd(); // Safety check: Don't allow operations on directories containing critical files // This prevents accidental deletion of the actual project directory if (this.hasProductionFiles() && !this.isSafeTestDirectory()) { throw new Error('BackupManager cannot operate on production directory. Pass a safe test directory to the constructor.'); } this.backupsDir = path.join(this.rootDir, "..", "dollhousemcp-backups"); } /** * Check if the directory contains production files */ hasProductionFiles() { try { const productionIndicators = [ 'package.json', 'tsconfig.json', '.git', 'src', 'LICENSE' ]; const files = fsSync.readdirSync(this.rootDir); const hasProductionFile = productionIndicators.some(indicator => files.includes(indicator)); // Additional check: if package.json exists, check if it's a real project if (hasProductionFile && files.includes('package.json')) { const packageJsonPath = path.join(this.rootDir, 'package.json'); const packageJson = JSON.parse(fsSync.readFileSync(packageJsonPath, 'utf-8')); // If it has a name and dependencies, it's likely a real project return !!(packageJson.name && packageJson.dependencies); } return hasProductionFile; } catch { // If we can't read the directory, assume it's safe return false; } } /** * Check if this appears to be a safe test directory */ isSafeTestDirectory() { const safePaths = ['test', 'tmp', 'temp', '.test', '__test__']; const dirPath = this.rootDir.toLowerCase(); // Check if running in Docker container if (this.isDockerEnvironment()) { return true; // Docker containers are immutable, updates don't apply } return safePaths.some(safe => dirPath.includes(safe)); } /** * Check if running in a Docker container */ isDockerEnvironment() { // Check common Docker indicators if (process.env.DOLLHOUSE_DISABLE_UPDATES === 'true') { return true; } // Check if running from /app directory (common Docker practice) if (this.rootDir === '/app') { return true; } // Check for Docker-specific files try { const hasDockerEnv = fsSync.existsSync('/.dockerenv'); if (hasDockerEnv) { return true; } } catch { // Ignore errors } return false; } /** * Create a backup of the current installation */ async createBackup(version) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupName = `backup-${timestamp}`; const backupPath = path.join(this.backupsDir, backupName); // Ensure backups directory exists await fs.mkdir(this.backupsDir, { recursive: true }); // Use git to create a clean copy (respecting .gitignore) await safeExec('git', [ 'archive', '--format=tar', 'HEAD' ], { cwd: this.rootDir }).then(async ({ stdout }) => { // Create backup directory await fs.mkdir(backupPath, { recursive: true }); // Extract tar to backup directory const tarPath = path.join(backupPath, 'archive.tar'); await fs.writeFile(tarPath, stdout); // Extract using tar command await safeExec('tar', ['-xf', 'archive.tar'], { cwd: backupPath }); await fs.unlink(tarPath); }); // Also backup node_modules if it exists const nodeModulesPath = path.join(this.rootDir, 'node_modules'); try { await fs.access(nodeModulesPath); await safeExec('cp', ['-r', 'node_modules', backupPath], { cwd: this.rootDir }); } catch { // node_modules doesn't exist or copy failed, that's okay } // Backup all persona files (including user-created ones not in git) const personasDir = path.join(this.rootDir, 'personas'); const backupPersonasDir = path.join(backupPath, 'personas'); try { await fs.access(personasDir); await fs.mkdir(backupPersonasDir, { recursive: true }); const personaFiles = await fs.readdir(personasDir); for (const file of personaFiles) { if (file.endsWith('.md')) { const sourcePath = path.join(personasDir, file); const destPath = path.join(backupPersonasDir, file); // Copy the file, overwriting if it already exists from git archive await fs.copyFile(sourcePath, destPath); } } } catch (error) { // Log warning but don't fail the backup console.error('Warning: Could not backup all personas:', error); } // Save backup metadata const metadata = { timestamp, version, createdAt: new Date().toISOString() }; await fs.writeFile(path.join(backupPath, 'backup-metadata.json'), JSON.stringify(metadata, null, 2)); return { path: backupPath, timestamp, version }; } /** * List available backups */ async listBackups() { try { const entries = await fs.readdir(this.backupsDir, { withFileTypes: true }); const backups = []; for (const entry of entries) { if (entry.isDirectory() && entry.name.startsWith('backup-')) { const backupPath = path.join(this.backupsDir, entry.name); const timestamp = entry.name.replace('backup-', ''); // Try to read metadata let version; try { const metadataPath = path.join(backupPath, 'backup-metadata.json'); const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf-8')); version = metadata.version; } catch { // No metadata file, that's okay } backups.push({ path: backupPath, timestamp, version }); } } // Sort by timestamp descending (newest first) return backups.sort((a, b) => b.timestamp.localeCompare(a.timestamp)); } catch { return []; } } /** * Get the most recent backup */ async getLatestBackup() { const backups = await this.listBackups(); return backups.length > 0 ? backups[0] : null; } /** * Restore from a backup */ async restoreBackup(backupPath) { // Verify backup exists try { await fs.access(backupPath); } catch { throw new Error(`Backup not found: ${backupPath}`); } // Create a temporary directory for the current state const tempDir = path.join(this.backupsDir, 'temp-current'); await fs.mkdir(tempDir, { recursive: true }); // Move current files to temp (except .git and node_modules) const entries = await fs.readdir(this.rootDir); for (const entry of entries) { if (entry !== '.git' && entry !== 'node_modules' && entry !== 'dist') { const sourcePath = path.join(this.rootDir, entry); const destPath = path.join(tempDir, entry); await fs.rename(sourcePath, destPath); } } // Copy backup files to root const backupEntries = await fs.readdir(backupPath); for (const entry of backupEntries) { if (entry !== 'backup-metadata.json' && entry !== '.git') { const sourcePath = path.join(backupPath, entry); const destPath = path.join(this.rootDir, entry); await safeExec('cp', ['-r', sourcePath, destPath]); } } // Clean up temp directory await fs.rm(tempDir, { recursive: true, force: true }); } /** * Clean up old backups (keep the 5 most recent) */ async cleanupOldBackups(keepCount = 5) { const backups = await this.listBackups(); let deletedCount = 0; if (backups.length > keepCount) { const backupsToDelete = backups.slice(keepCount); for (const backup of backupsToDelete) { try { await fs.rm(backup.path, { recursive: true, force: true }); deletedCount++; } catch (error) { console.error(`Failed to delete backup ${backup.path}:`, error); } } } return deletedCount; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmFja3VwTWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy91cGRhdGUvQmFja3VwTWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxNQUFNLE1BQU0sSUFBSSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQVEzQyxNQUFNLE9BQU8sYUFBYTtJQUNoQixPQUFPLENBQVM7SUFDaEIsVUFBVSxDQUFTO0lBRTNCLFlBQVksT0FBZ0I7UUFDMUIseUNBQXlDO1FBQ3pDLElBQUksT0FBTyxFQUFFLENBQUM7WUFDWix1Q0FBdUM7WUFDdkMsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztnQkFDeEQsTUFBTSxJQUFJLEtBQUssQ0FBQyxpREFBaUQsQ0FBQyxDQUFDO1lBQ3JFLENBQUM7WUFDRCw4QkFBOEI7WUFDOUIsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDOUIsTUFBTSxJQUFJLEtBQUssQ0FBQyxrQ0FBa0MsQ0FBQyxDQUFDO1lBQ3RELENBQUM7UUFDSCxDQUFDO1FBRUQsdURBQXVEO1FBQ3ZELElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxJQUFJLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUV4QyxnRkFBZ0Y7UUFDaEYsb0VBQW9FO1FBQ3BFLElBQUksSUFBSSxDQUFDLGtCQUFrQixFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsRUFBRSxDQUFDO1lBQzdELE1BQU0sSUFBSSxLQUFLLENBQUMsc0dBQXNHLENBQUMsQ0FB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LCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE1hbmFnZSBiYWNrdXBzIGR1cmluZyB1cGRhdGVzXG4gKi9cblxuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMvcHJvbWlzZXMnO1xuaW1wb3J0ICogYXMgZnNTeW5jIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBzYWZlRXhlYyB9IGZyb20gJy4uL3V0aWxzL2dpdC5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQmFja3VwSW5mbyB7XG4gIHBhdGg6IHN0cmluZztcbiAgdGltZXN0YW1wOiBzdHJpbmc7XG4gIHZlcnNpb24/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBjbGFzcyBCYWNrdXBNYW5hZ2VyIHtcbiAgcHJpdmF0ZSByb290RGlyOiBzdHJpbmc7XG4gIHByaXZhdGUgYmFja3Vwc0Rpcjogc3RyaW5nO1xuICBcbiAgY29uc3RydWN0b3Iocm9vdERpcj86IHN0cmluZykge1xuICAgIC8vIFZhbGlkYXRlIHJvb3REaXIgcGFyYW1ldGVyIGlmIHByb3ZpZGVkXG4gICAgaWYgKHJvb3REaXIpIHtcbiAgICAgIC8vIFByZXZlbnQgcGF0aCB0cmF2ZXJzYWwgYXR0YWNrcyBmaXJzdFxuICAgICAgaWYgKHJvb3REaXIuaW5jbHVkZXMoJy4uLycpIHx8IHJvb3REaXIuaW5jbHVkZXMoJy4uXFxcXCcpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcigncm9vdERpciBjYW5ub3QgY29udGFpbiBwYXRoIHRyYXZlcnNhbCBzZXF1ZW5jZXMnKTtcbiAgICAgIH1cbiAgICAgIC8vIFRoZW4gY2hlY2sgaWYgaXQncyBhYnNvbHV0ZVxuICAgICAgaWYgKCFwYXRoLmlzQWJzb2x1dGUocm9vdERpcikpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdyb290RGlyIG11c3QgYmUgYW4gYWJzb2x1dGUgcGF0aCcpO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBBbGxvdyBvdmVycmlkZSBmb3IgdGVzdGluZywgZGVmYXVsdCB0byBwcm9jZXNzLmN3ZCgpXG4gICAgdGhpcy5yb290RGlyID0gcm9vdERpciB8fCBwcm9jZXNzLmN3ZCgpO1xuICAgIFxuICAgIC8vIFNhZmV0eSBjaGVjazogRG9uJ3QgYWxsb3cgb3BlcmF0aW9ucyBvbiBkaXJlY3RvcmllcyBjb250YWluaW5nIGNyaXRpY2FsIGZpbGVzXG4gICAgLy8gVGhpcyBwcmV2ZW50cyBhY2NpZGVudGFsIGRlbGV0aW9uIG9mIHRoZSBhY3R1YWwgcHJvamVjdCBkaXJlY3RvcnlcbiAgICBpZiAodGhpcy5oYXNQcm9kdWN0aW9uRmlsZXMoKSAmJiAhdGhpcy5pc1NhZmVUZXN0RGlyZWN0b3J5KCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQmFja3VwTWFuYWdlciBjYW5ub3Qgb3BlcmF0ZSBvbiBwcm9kdWN0aW9uIGRpcmVjdG9yeS4gUGFzcyBhIHNhZmUgdGVzdCBkaXJlY3RvcnkgdG8gdGhlIGNvbnN0cnVjdG9yLicpO1xuICAgIH1cbiAgICBcbiAgICB0aGlzLmJhY2t1cHNEaXIgPSBwYXRoLmpvaW4odGhpcy5yb290RGlyLCBcIi4uXCIsIFwiZG9sbGhvdXNlbWNwLWJhY2t1cHNcIik7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiB0aGUgZGlyZWN0b3J5IGNvbnRhaW5zIHByb2R1Y3Rpb24gZmlsZXNcbiAgICovXG4gIHByaXZhdGUgaGFzUHJvZHVjdGlvbkZpbGVzKCk6IGJvb2xlYW4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBwcm9kdWN0aW9uSW5kaWNhdG9ycyA9IFtcbiAgICAgICAgJ3BhY2thZ2UuanNvbicsXG4gICAgICAgICd0c2NvbmZpZy5qc29uJyxcbiAgICAgICAgJy5naXQnLFxuICAgICAgICAnc3JjJyxcbiAgICAgICAgJ0xJQ0VOU0UnXG4gICAgICBdO1xuICAgICAgXG4gICAgICBjb25zdCBmaWxlcyA9IGZzU3luYy5yZWFkZGlyU3luYyh0aGlzLnJvb3REaXIpO1xuICAgICAgY29uc3QgaGFzUHJvZHVjdGlvbkZpbGUgPSBwcm9kdWN0aW9uSW5kaWNhdG9ycy5zb21lKGluZGljYXRvciA9PiBcbiAgICAgICAgZmlsZXMuaW5jbHVkZXMoaW5kaWNhdG9yKVxuICAgICAgKTtcbiAgICAgIFxuICAgICAgLy8gQWRkaXRpb25hbCBjaGVjazogaWYgcGFja2FnZS5qc29uIGV4aXN0cywgY2hlY2sgaWYgaXQncyBhIHJlYWwgcHJvamVjdFxuICAgICAgaWYgKGhhc1Byb2R1Y3Rpb25GaWxlICYmIGZpbGVzLmluY2x1ZGVzKCdwYWNrYWdlLmpzb24nKSkge1xuICAgICAgICBjb25zdCBwYWNrYWdlSnNvblBhdGggPSBwYXRoLmpvaW4odGhpcy5yb290RGlyLCAncGFja2FnZS5qc29uJyk7XG4gICAgICAgIGNvbnN0IHBhY2thZ2VKc29uID0gSlNPTi5wYXJzZShmc1N5bmMucmVhZEZpbGVTeW5jKHBhY2thZ2VKc29uUGF0aCwgJ3V0Zi04JykpO1xuICAgICAgICAvLyBJZiBpdCBoYXMgYSBuYW1lIGFuZCBkZXBlbmRlbmNpZXMsIGl0J3MgbGlrZWx5IGEgcmVhbCBwcm9qZWN0XG4gICAgICAgIHJldHVybiAhIShwYWNrYWdlSnNvbi5uYW1lICYmIHBhY2thZ2VKc29uLmRlcGVuZGVuY2llcyk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHJldHVybiBoYXNQcm9kdWN0aW9uRmlsZTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIElmIHdlIGNhbid0IHJlYWQgdGhlIGRpcmVjdG9yeSwgYXNzdW1lIGl0J3Mgc2FmZVxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIHRoaXMgYXBwZWFycyB0byBiZSBhIHNhZmUgdGVzdCBkaXJlY3RvcnlcbiAgICovXG4gIHByaXZhdGUgaXNTYWZlVGVzdERpcmVjdG9yeSgpOiBib29sZWFuIHtcbiAgICBjb25zdCBzYWZlUGF0aHMgPSBbJ3Rlc3QnLCAndG1wJywgJ3RlbXAnLCAnLnRlc3QnLCAnX190ZXN0X18nXTtcbiAgICBjb25zdCBkaXJQYXRoID0gdGhpcy5yb290RGlyLnRvTG93ZXJDYXNlKCk7XG4gICAgXG4gICAgLy8gQ2hlY2sgaWYgcnVubmluZyBpbiBEb2NrZXIgY29udGFpbmVyXG4gICAgaWYgKHRoaXMuaXNEb2NrZXJFbnZpcm9ubWVudCgpKSB7XG4gICAgICByZXR1cm4gdHJ1ZTsgLy8gRG9ja2VyIGNvbnRhaW5lcnMgYXJlIGltbXV0YWJsZSwgdXBkYXRlcyBkb24ndCBhcHBseVxuICAgIH1cbiAgICBcbiAgICByZXR1cm4gc2FmZVBhdGhzLnNvbWUoc2FmZSA9PiBkaXJQYXRoLmluY2x1ZGVzKHNhZmUpKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIHJ1bm5pbmcgaW4gYSBEb2NrZXIgY29udGFpbmVyXG4gICAqL1xuICBwcml2YXRlIGlzRG9ja2VyRW52aXJvbm1lbnQoKTogYm9vbGVhbiB7XG4gICAgLy8gQ2hlY2sgY29tbW9uIERvY2tlciBpbmRpY2F0b3JzXG4gICAgaWYgKHByb2Nlc3MuZW52LkRPTExIT1VTRV9ESVNBQkxFX1VQREFURVMgPT09ICd0cnVlJykge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIFxuICAgIC8vIENoZWNrIGlmIHJ1bm5pbmcgZnJvbSAvYXBwIGRpcmVjdG9yeSAoY29tbW9uIERvY2tlciBwcmFjdGljZSlcbiAgICBpZiAodGhpcy5yb290RGlyID09PSAnL2FwcCcpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBcbiAgICAvLyBDaGVjayBmb3IgRG9ja2VyLXNwZWNpZmljIGZpbGVzXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGhhc0RvY2tlckVudiA9IGZzU3luYy5leGlzdHNTeW5jKCcvLmRvY2tlcmVudicpO1xuICAgICAgaWYgKGhhc0RvY2tlckVudikge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIElnbm9yZSBlcnJvcnNcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIFxuICAvKipcbiAgICogQ3JlYXRlIGEgYmFja3VwIG9mIHRoZSBjdXJyZW50IGluc3RhbGxhdGlvblxuICAgKi9cbiAgYXN5bmMgY3JlYXRlQmFja3VwKHZlcnNpb24/OiBzdHJpbmcpOiBQcm9taXNlPEJhY2t1cEluZm8+IHtcbiAgICBjb25zdCB0aW1lc3RhbXAgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkucmVwbGFjZSgvWzouXS9nLCAnLScpO1xuICAgIGNvbnN0IGJhY2t1cE5hbWUgPSBgYmFja3VwLSR7dGltZXN0YW1wfWA7XG4gICAgY29uc3QgYmFja3VwUGF0aCA9IHBhdGguam9pbih0aGlzLmJhY2t1cHNEaXIsIGJhY2t1cE5hbWUpO1xuICAgIFxuICAgIC8vIEVuc3VyZSBiYWNrdXBzIGRpcmVjdG9yeSBleGlzdHNcbiAgICBhd2FpdCBmcy5ta2Rpcih0aGlzLmJhY2t1cHNEaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIFxuICAgIC8vIFVzZSBnaXQgdG8gY3JlYXRlIGEgY2xlYW4gY29weSAocmVzcGVjdGluZyAuZ2l0aWdub3JlKVxuICAgIGF3YWl0IHNhZmVFeGVjKCdnaXQnLCBbXG4gICAgICAnYXJjaGl2ZScsXG4gICAgICAnLS1mb3JtYXQ9dGFyJyxcbiAgICAgICdIRUFEJ1xuICAgIF0sIHsgY3dkOiB0aGlzLnJvb3REaXIgfSkudGhlbihhc3luYyAoeyBzdGRvdXQgfSkgPT4ge1xuICAgICAgLy8gQ3JlYXRlIGJhY2t1cCBkaXJlY3RvcnlcbiAgICAgIGF3YWl0IGZzLm1rZGlyKGJhY2t1cFBhdGgsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgXG4gICAgICAvLyBFeHRyYWN0IHRhciB0byBiYWNrdXAgZGlyZWN0b3J5XG4gICAgICBjb25zdCB0YXJQYXRoID0gcGF0aC5qb2luKGJhY2t1cFBhdGgsICdhcmNoaXZlLnRhcicpO1xuICAgICAgYXdhaXQgZnMud3JpdGVGaWxlKHRhclBhdGgsIHN0ZG91dCk7XG4gICAgICBcbiAgICAgIC8vIEV4dHJhY3QgdXNpbmcgdGFyIGNvbW1hbmRcbiAgICAgIGF3YWl0IHNhZmVFeGVjKCd0YXInLCBbJy14ZicsICdhcmNoaXZlLnRhciddLCB7IGN3ZDogYmFja3VwUGF0aCB9KTtcbiAgICAgIGF3YWl0IGZzLnVubGluayh0YXJQYXRoKTtcbiAgICB9KTtcbiAgICBcbiAgICAvLyBBbHNvIGJhY2t1cCBub2RlX21vZHVsZXMgaWYgaXQgZXhpc3RzXG4gICAgY29uc3Qgbm9kZU1vZHVsZXNQYXRoID0gcGF0aC5qb2luKHRoaXMucm9vdERpciwgJ25vZGVfbW9kdWxlcycpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy5hY2Nlc3Mobm9kZU1vZHVsZXNQYXRoKTtcbiAgICAgIGF3YWl0IHNhZmVFeGVjKCdjcCcsIFsnLXInLCAnbm9kZV9tb2R1bGVzJywgYmFja3VwUGF0aF0sIHsgY3dkOiB0aGlzLnJvb3REaXIgfSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBub2RlX21vZHVsZXMgZG9lc24ndCBleGlzdCBvciBjb3B5IGZhaWxlZCwgdGhhdCdzIG9rYXlcbiAgICB9XG4gICAgXG4gICAgLy8gQmFja3VwIGFsbCBwZXJzb25hIGZpbGVzIChpbmNsdWRpbmcgdXNlci1jcmVhdGVkIG9uZXMgbm90IGluIGdpdClcbiAgICBjb25zdCBwZXJzb25hc0RpciA9IHBhdGguam9pbih0aGlzLnJvb3REaXIsICdwZXJzb25hcycpO1xuICAgIGNvbnN0IGJhY2t1cFBlcnNvbmFzRGlyID0gcGF0aC5qb2luKGJhY2t1cFBhdGgsICdwZXJzb25hcycpO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy5hY2Nlc3MocGVyc29uYXNEaXIpO1xuICAgICAgYXdhaXQgZnMubWtkaXIoYmFja3VwUGVyc29uYXNEaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgXG4gICAgICBjb25zdCBwZXJzb25hRmlsZXMgPSBhd2FpdCBmcy5yZWFkZGlyKHBlcnNvbmFzRGlyKTtcbiAgICAgIGZvciAoY29uc3QgZmlsZSBvZiBwZXJzb25hRmlsZXMpIHtcbiAgICAgICAgaWYgKGZpbGUuZW5kc1dpdGgoJy5tZCcpKSB7XG4gICAgICAgICAgY29uc3Qgc291cmNlUGF0aCA9IHBhdGguam9pbihwZXJzb25hc0RpciwgZmlsZSk7XG4gICAgICAgICAgY29uc3QgZGVzdFBhdGggPSBwYXRoLmpvaW4oYmFja3VwUGVyc29uYXNEaXIsIGZpbGUpO1xuICAgICAgICAgIFxuICAgICAgICAgIC8vIENvcHkgdGhlIGZpbGUsIG92ZXJ3cml0aW5nIGlmIGl0IGFscmVhZHkgZXhpc3RzIGZyb20gZ2l0IGFyY2hpdmVcbiAgICAgICAgICBhd2FpdCBmcy5jb3B5RmlsZShzb3VyY2VQYXRoLCBkZXN0UGF0aCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gTG9nIHdhcm5pbmcgYnV0IGRvbid0IGZhaWwgdGhlIGJhY2t1cFxuICAgICAgY29uc29sZS5lcnJvcignV2FybmluZzogQ291bGQgbm90IGJhY2t1cCBhbGwgcGVyc29uYXM6JywgZXJyb3IpO1xuICAgIH1cbiAgICBcbiAgICAvLyBTYXZlIGJhY2t1cCBtZXRhZGF0YVxuICAgIGNvbnN0IG1ldGFkYXRhID0ge1xuICAgICAgdGltZXN0YW1wLFxuICAgICAgdmVyc2lvbixcbiAgICAgIGNyZWF0ZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpXG4gICAgfTtcbiAgICBhd2FpdCBmcy53cml0ZUZpbGUoXG4gICAgICBwYXRoLmpvaW4oYmFja3VwUGF0aCwgJ2JhY2t1cC1tZXRhZGF0YS5qc29uJyksXG4gICAgICBKU09OLnN0cmluZ2lmeShtZXRhZGF0YSwgbnVsbCwgMilcbiAgICApO1xuICAgIFxuICAgIHJldHVybiB7XG4gICAgICBwYXRoOiBiYWNrdXBQYXRoLFxuICAgICAgdGltZXN0YW1wLFxuICAgICAgdmVyc2lvblxuICAgIH07XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBMaXN0IGF2YWlsYWJsZSBiYWNrdXBzXG4gICAqL1xuICBhc3luYyBsaXN0QmFja3VwcygpOiBQcm9taXNlPEJhY2t1cEluZm9bXT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLmJhY2t1cHNEaXIsIHsgd2l0aEZpbGVUeXBlczogdHJ1ZSB9KTtcbiAgICAgIGNvbnN0IGJhY2t1cHM6IEJhY2t1cEluZm9bXSA9IFtdO1xuICAgICAgXG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgICAgaWYgKGVudHJ5LmlzRGlyZWN0b3J5KCkgJiYgZW50cnkubmFtZS5zdGFydHNXaXRoKCdiYWNrdXAtJykpIHtcbiAgICAgICAgICBjb25zdCBiYWNrdXBQYXRoID0gcGF0aC5qb2luKHRoaXMuYmFja3Vwc0RpciwgZW50cnkubmFtZSk7XG4gICAgICAgICAgY29uc3QgdGltZXN0YW1wID0gZW50cnkubmFtZS5yZXBsYWNlKCdiYWNrdXAtJywgJycpO1xuICAgICAgICAgIFxuICAgICAgICAgIC8vIFRyeSB0byByZWFkIG1ldGFkYXRhXG4gICAgICAgICAgbGV0IHZlcnNpb246IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgbWV0YWRhdGFQYXRoID0gcGF0aC5qb2luKGJhY2t1cFBhdGgsICdiYWNrdXAtbWV0YWRhdGEuanNvbicpO1xuICAgICAgICAgICAgY29uc3QgbWV0YWRhdGEgPSBKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKG1ldGFkYXRhUGF0aCwgJ3V0Zi04JykpO1xuICAgICAgICAgICAgdmVyc2lvbiA9IG1ldGFkYXRhLnZlcnNpb247XG4gICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAvLyBObyBtZXRhZGF0YSBmaWxlLCB0aGF0J3Mgb2theVxuICAgICAgICAgIH1cbiAgICAgICAgICBcbiAgICAgICAgICBiYWNrdXBzLnB1c2goe1xuICAgICAgICAgICAgcGF0aDogYmFja3VwUGF0aCxcbiAgICAgICAgICAgIHRpbWVzdGFtcCxcbiAgICAgICAgICAgIHZlcnNpb25cbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBTb3J0IGJ5IHRpbWVzdGFtcCBkZXNjZW5kaW5nIChuZXdlc3QgZmlyc3QpXG4gICAgICByZXR1cm4gYmFja3Vwcy5zb3J0KChhLCBiKSA9PiBiLnRpbWVzdGFtcC5sb2NhbGVDb21wYXJlKGEudGltZXN0YW1wKSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogR2V0IHRoZSBtb3N0IHJlY2VudCBiYWNrdXBcbiAgICovXG4gIGFzeW5jIGdldExhdGVzdEJhY2t1cCgpOiBQcm9taXNlPEJhY2t1cEluZm8gfCBudWxsPiB7XG4gICAgY29uc3QgYmFja3VwcyA9IGF3YWl0IHRoaXMubGlzdEJhY2t1cHMoKTtcbiAgICByZXR1cm4gYmFja3Vwcy5sZW5ndGggPiAwID8gYmFja3Vwc1swXSA6IG51bGw7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXN0b3JlIGZyb20gYSBiYWNrdXBcbiAgICovXG4gIGFzeW5jIHJlc3RvcmVCYWNrdXAoYmFja3VwUGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gVmVyaWZ5IGJhY2t1cCBleGlzdHNcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZnMuYWNjZXNzKGJhY2t1cFBhdGgpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBCYWNrdXAgbm90IGZvdW5kOiAke2JhY2t1cFBhdGh9YCk7XG4gICAgfVxuICAgIFxuICAgIC8vIENyZWF0ZSBhIHRlbXBvcmFyeSBkaXJlY3RvcnkgZm9yIHRoZSBjdXJyZW50IHN0YXRlXG4gICAgY29uc3QgdGVtcERpciA9IHBhdGguam9pbih0aGlzLmJhY2t1cHNEaXIsICd0ZW1wLWN1cnJlbnQnKTtcbiAgICBhd2FpdCBmcy5ta2Rpcih0ZW1wRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICBcbiAgICAvLyBNb3ZlIGN1cnJlbnQgZmlsZXMgdG8gdGVtcCAoZXhjZXB0IC5naXQgYW5kIG5vZGVfbW9kdWxlcylcbiAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLnJvb3REaXIpO1xuICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgaWYgKGVudHJ5ICE9PSAnLmdpdCcgJiYgZW50cnkgIT09ICdub2RlX21vZHVsZXMnICYmIGVudHJ5ICE9PSAnZGlzdCcpIHtcbiAgICAgICAgY29uc3Qgc291cmNlUGF0aCA9IHBhdGguam9pbih0aGlzLnJvb3REaXIsIGVudHJ5KTtcbiAgICAgICAgY29uc3QgZGVzdFBhdGggPSBwYXRoLmpvaW4odGVtcERpciwgZW50cnkpO1xuICAgICAgICBhd2FpdCBmcy5yZW5hbWUoc291cmNlUGF0aCwgZGVzdFBhdGgpO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBDb3B5IGJhY2t1cCBmaWxlcyB0byByb290XG4gICAgY29uc3QgYmFja3VwRW50cmllcyA9IGF3YWl0IGZzLnJlYWRkaXIoYmFja3VwUGF0aCk7XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBiYWNrdXBFbnRyaWVzKSB7XG4gICAgICBpZiAoZW50cnkgIT09ICdiYWNrdXAtbWV0YWRhdGEuanNvbicgJiYgZW50cnkgIT09ICcuZ2l0Jykge1xuICAgICAgICBjb25zdCBzb3VyY2VQYXRoID0gcGF0aC5qb2luKGJhY2t1cFBhdGgsIGVudHJ5KTtcbiAgICAgICAgY29uc3QgZGVzdFBhdGggPSBwYXRoLmpvaW4odGhpcy5yb290RGlyLCBlbnRyeSk7XG4gICAgICAgIGF3YWl0IHNhZmVFeGVjKCdjcCcsIFsnLXInLCBzb3VyY2VQYXRoLCBkZXN0UGF0aF0pO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBDbGVhbiB1cCB0ZW1wIGRpcmVjdG9yeVxuICAgIGF3YWl0IGZzLnJtKHRlbXBEaXIsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENsZWFuIHVwIG9sZCBiYWNrdXBzIChrZWVwIHRoZSA1IG1vc3QgcmVjZW50KVxuICAgKi9cbiAgYXN5bmMgY2xlYW51cE9sZEJhY2t1cHMoa2VlcENvdW50OiBudW1iZXIgPSA1KTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBjb25zdCBiYWNrdXBzID0gYXdhaXQgdGhpcy5saXN0QmFja3VwcygpO1xuICAgIGxldCBkZWxldGVkQ291bnQgPSAwO1xuICAgIFxuICAgIGlmIChiYWNrdXBzLmxlbmd0aCA+IGtlZXBDb3VudCkge1xuICAgICAgY29uc3QgYmFja3Vwc1RvRGVsZXRlID0gYmFja3Vwcy5zbGljZShrZWVwQ291bnQpO1xuICAgICAgXG4gICAgICBmb3IgKGNvbnN0IGJhY2t1cCBvZiBiYWNrdXBzVG9EZWxldGUpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBmcy5ybShiYWNrdXAucGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgICAgICAgIGRlbGV0ZWRDb3VudCsrO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYEZhaWxlZCB0byBkZWxldGUgYmFja3VwICR7YmFja3VwLnBhdGh9OmAsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICByZXR1cm4gZGVsZXRlZENvdW50O1xuICB9XG59Il19