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.

140 lines 20.5 kB
/** * Install AI customization elements from collection * Supports all element types: personas, skills, templates, agents, memories, ensembles */ import * as fs from 'fs/promises'; import * as path from 'path'; import { validatePath, validateFilename, validateContentSize } from '../security/InputValidator.js'; import { SECURITY_LIMITS } from '../security/constants.js'; import { ContentValidator } from '../security/contentValidator.js'; import { SecureYamlParser } from '../security/secureYamlParser.js'; import { SecurityError } from '../errors/SecurityError.js'; import { PortfolioManager, ElementType } from '../portfolio/PortfolioManager.js'; export class ElementInstaller { githubClient; portfolioManager; baseUrl = 'https://api.github.com/repos/DollhouseMCP/collection/contents'; constructor(githubClient) { this.githubClient = githubClient; this.portfolioManager = PortfolioManager.getInstance(); } /** * Install AI customization element from the collection * Automatically detects element type from path structure */ async installContent(inputPath) { // Validate and sanitize the input path const sanitizedPath = validatePath(inputPath); // Detect element type from path structure // Expected format: library/[element-type]/[category]/[element].md const pathParts = sanitizedPath.split('/'); if (pathParts.length < 3 || pathParts[0] !== 'library') { throw new Error('Invalid collection path format. Expected: library/[element-type]/[category]/[element].md'); } const elementTypeStr = pathParts[1]; const elementType = this.getElementTypeFromString(elementTypeStr); // Ensure the path ends with .md if (!sanitizedPath.endsWith('.md')) { throw new Error('Invalid file type. Only .md files are allowed.'); } const url = `${this.baseUrl}/${sanitizedPath}`; const data = await this.githubClient.fetchFromGitHub(url); if (data.type !== 'file') { throw new Error('Path does not point to a file'); } // Check file size before downloading if (data.size > SECURITY_LIMITS.MAX_PERSONA_SIZE_BYTES) { throw new Error(`File too large (${data.size} bytes, max ${SECURITY_LIMITS.MAX_PERSONA_SIZE_BYTES} bytes)`); } // Decode Base64 content const content = Buffer.from(data.content, 'base64').toString('utf-8'); // Validate content size after decoding validateContentSize(content, SECURITY_LIMITS.MAX_PERSONA_SIZE_BYTES); // Sanitize content for security threats const sanitizedContent = ContentValidator.sanitizePersonaContent(content); // Use secure YAML parser let parsed; try { parsed = SecureYamlParser.safeMatter(sanitizedContent); } catch (error) { if (error instanceof SecurityError) { throw new Error(`Security threat in content: ${error.message}`); } throw error; } const metadata = parsed.data; // Additional metadata validation for injection attacks const metadataValidation = ContentValidator.validateMetadata(metadata); if (!metadataValidation.isValid) { throw new Error(`Security validation failed: ${metadataValidation.detectedPatterns?.join(', ')}`); } // Validate metadata if (!metadata.name || !metadata.description) { throw new Error('Invalid content: missing required name or description'); } // Generate and validate local filename const originalFilename = sanitizedPath.split('/').pop() || 'downloaded-element.md'; const filename = validateFilename(originalFilename); // Get appropriate directory for element type const elementDir = this.portfolioManager.getElementDir(elementType); const localPath = path.join(elementDir, filename); // Check if file already exists try { await fs.access(localPath); return { success: false, message: `AI customization element already exists: ${filename}\n\nThe element has already been installed.` }; } catch { // File doesn't exist, proceed with installation } // Write the sanitized file await fs.writeFile(localPath, sanitizedContent, 'utf-8'); return { success: true, message: `AI customization element installed successfully!`, metadata, filename, elementType }; } /** * Get ElementType from string */ getElementTypeFromString(typeStr) { const typeMap = { 'personas': ElementType.PERSONA, 'skills': ElementType.SKILL, 'templates': ElementType.TEMPLATE, 'agents': ElementType.AGENT }; const elementType = typeMap[typeStr]; if (!elementType) { throw new Error(`Unknown element type: ${typeStr}. Valid types: ${Object.keys(typeMap).join(', ')}`); } return elementType; } /** * Format installation success message */ formatInstallSuccess(metadata, filename, elementType) { const typeEmojis = { [ElementType.PERSONA]: '🎭', [ElementType.SKILL]: '🎯', [ElementType.TEMPLATE]: '📄', [ElementType.AGENT]: '🤖', [ElementType.MEMORY]: '🧠', [ElementType.ENSEMBLE]: '🎼' }; const emoji = typeEmojis[elementType] || '📦'; const typeName = elementType.charAt(0).toUpperCase() + elementType.slice(1); return `✅ **AI Customization Element Installed Successfully!**\n\n` + `${emoji} **${metadata.name}** ${metadata.author ? `by ${metadata.author}` : ''}\n` + `📁 Type: ${typeName}\n` + `📄 Saved as: ${filename}\n\n` + `🚀 **Ready to use!**`; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRWxlbWVudEluc3RhbGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb2xsZWN0aW9uL0VsZW1lbnRJbnN0YWxsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFHN0IsT0FBTyxFQUFFLFlBQVksRUFBRSxnQkFBZ0IsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3BHLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNuRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNuRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDM0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFdBQVcsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBRWpGLE1BQU0sT0FBTyxnQkFBZ0I7SUFDbkIsWUFBWSxDQUFlO0lBQzNCLGdCQUFnQixDQUFtQjtJQUNuQyxPQUFPLEdBQUcsK0RBQStELENBQUM7SUFFbEYsWUFBWSxZQUEwQjtRQUNwQyxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQztRQUNqQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDekQsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxjQUFjLENBQUMsU0FBaUI7UUFPcEMsdUNBQXVDO1FBQ3ZDLE1BQU0sYUFBYSxHQUFHLFlBQVksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUU5QywwQ0FBMEM7UUFDMUMsa0VBQWtFO1FBQ2xFLE1BQU0sU0FBUyxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFD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QyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZHLENBQUM7UUFFRCxPQUFPLFdBQVcsQ0FBQztJQUNyQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxvQkFBb0IsQ0FBQyxRQUEwQixFQUFFLFFBQWdCLEVBQUUsV0FBd0I7UUFDekYsTUFBTSxVQUFVLEdBQWdDO1lBQzlDLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxFQUFFLElBQUk7WUFDM0IsQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLEVBQUUsSUFBSTtZQUN6QixDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsRUFBRSxJQUFJO1lBQzVCLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLElBQUk7WUFDekIsQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLEVBQUUsSUFBSTtZQUMxQixDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsRUFBRSxJQUFJO1NBQzdCLENBQUM7UUFFRixNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsV0FBVyxDQUFDLElBQUksSUFBSSxDQUFDO1FBQzlDLE1BQU0sUUFBUSxHQUFHLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUU1RSxPQUFPLDREQUE0RDtZQUNqRSxHQUFHLEtBQUssTUFBTSxRQUFRLENBQUMsSUFBSSxNQUFNLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUk7WUFDbkYsWUFBWSxRQUFRLElBQUk7WUFDeEIsZ0JBQWdCLFFBQVEsTUFBTTtZQUM5QixzQkFBc0IsQ0FBQztJQUMzQixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEluc3RhbGwgQUkgY3VzdG9taXphdGlvbiBlbGVtZW50cyBmcm9tIGNvbGxlY3Rpb25cbiAqIFN1cHBvcnRzIGFsbCBlbGVtZW50IHR5cGVzOiBwZXJzb25hcywgc2tpbGxzLCB0ZW1wbGF0ZXMsIGFnZW50cywgbWVtb3JpZXMsIGVuc2VtYmxlc1xuICovXG5cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBHaXRIdWJDbGllbnQgfSBmcm9tICcuL0dpdEh1YkNsaWVudC5qcyc7XG5pbXBvcnQgeyBJRWxlbWVudE1ldGFkYXRhIH0gZnJvbSAnLi4vdHlwZXMvZWxlbWVudHMvSUVsZW1lbnQuanMnO1xuaW1wb3J0IHsgdmFsaWRhdGVQYXRoLCB2YWxpZGF0ZUZpbGVuYW1lLCB2YWxpZGF0ZUNvbnRlbnRTaXplIH0gZnJvbSAnLi4vc2VjdXJpdHkvSW5wdXRWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgU0VDVVJJVFlfTElNSVRTIH0gZnJvbSAnLi4vc2VjdXJpdHkvY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IENvbnRlbnRWYWxpZGF0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9jb250ZW50VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFNlY3VyZVlhbWxQYXJzZXIgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cmVZYW1sUGFyc2VyLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5RXJyb3IgfSBmcm9tICcuLi9lcnJvcnMvU2VjdXJpdHlFcnJvci5qcyc7XG5pbXBvcnQgeyBQb3J0Zm9saW9NYW5hZ2VyLCBFbGVtZW50VHlwZSB9IGZyb20gJy4uL3BvcnRmb2xpby9Qb3J0Zm9saW9NYW5hZ2VyLmpzJztcblxuZXhwb3J0IGNsYXNzIEVsZW1lbnRJbnN0YWxsZXIge1xuICBwcml2YXRlIGdpdGh1YkNsaWVudDogR2l0SHViQ2xpZW50O1xuICBwcml2YXRlIHBvcnRmb2xpb01hbmFnZXI6IFBvcnRmb2xpb01hbmFnZXI7XG4gIHByaXZhdGUgYmFzZVVybCA9ICdodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL0RvbGxob3VzZU1DUC9jb2xsZWN0aW9uL2NvbnRlbnRzJztcbiAgXG4gIGNvbnN0cnVjdG9yKGdpdGh1YkNsaWVudDogR2l0SHViQ2xpZW50KSB7XG4gICAgdGhpcy5naXRodWJDbGllbnQgPSBnaXRodWJDbGllbnQ7XG4gICAgdGhpcy5wb3J0Zm9saW9NYW5hZ2VyID0gUG9ydGZvbGlvTWFuYWdlci5nZXRJbnN0YW5jZSgpO1xuICB9XG4gIFxuICAvKipcbiAgICogSW5zdGFsbCBBSSBjdXN0b21pemF0aW9uIGVsZW1lbnQgZnJvbSB0aGUgY29sbGVjdGlvblxuICAgKiBBdXRvbWF0aWNhbGx5IGRldGVjdHMgZWxlbWVudCB0eXBlIGZyb20gcGF0aCBzdHJ1Y3R1cmVcbiAgICovXG4gIGFzeW5jIGluc3RhbGxDb250ZW50KGlucHV0UGF0aDogc3RyaW5nKTogUHJvbWlzZTx7IFxuICAgIHN1Y2Nlc3M6IGJvb2xlYW47IFxuICAgIG1lc3NhZ2U6IHN0cmluZztcbiAgICBtZXRhZGF0YT86IElFbGVtZW50TWV0YWRhdGE7XG4gICAgZWxlbWVudFR5cGU/OiBFbGVtZW50VHlwZTtcbiAgICBmaWxlbmFtZT86IHN0cmluZztcbiAgfT4ge1xuICAgIC8vIFZhbGlkYXRlIGFuZCBzYW5pdGl6ZSB0aGUgaW5wdXQgcGF0aFxuICAgIGNvbnN0IHNhbml0aXplZFBhdGggPSB2YWxpZGF0ZVBhdGgoaW5wdXRQYXRoKTtcbiAgICBcbiAgICAvLyBEZXRlY3QgZWxlbWVudCB0eXBlIGZyb20gcGF0aCBzdHJ1Y3R1cmVcbiAgICAvLyBFeHBlY3RlZCBmb3JtYXQ6IGxpYnJhcnkvW2VsZW1lbnQtdHlwZV0vW2NhdGVnb3J5XS9bZWxlbWVudF0ubWRcbiAgICBjb25zdCBwYXRoUGFydHMgPSBzYW5pdGl6ZWRQYXRoLnNwbGl0KCcvJyk7XG4gICAgaWYgKHBhdGhQYXJ0cy5sZW5ndGggPCAzIHx8IHBhdGhQYXJ0c1swXSAhPT0gJ2xpYnJhcnknKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgY29sbGVjdGlvbiBwYXRoIGZvcm1hdC4gRXhwZWN0ZWQ6IGxpYnJhcnkvW2VsZW1lbnQtdHlwZV0vW2NhdGVnb3J5XS9bZWxlbWVudF0ubWQnKTtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgZWxlbWVudFR5cGVTdHIgPSBwYXRoUGFydHNbMV07XG4gICAgY29uc3QgZWxlbWVudFR5cGUgPSB0aGlzLmdldEVsZW1lbnRUeXBlRnJvbVN0cmluZyhlbGVtZW50VHlwZVN0cik7XG4gICAgXG4gICAgLy8gRW5zdXJlIHRoZSBwYXRoIGVuZHMgd2l0aCAubWRcbiAgICBpZiAoIXNhbml0aXplZFBhdGguZW5kc1dpdGgoJy5tZCcpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZmlsZSB0eXBlLiBPbmx5IC5tZCBmaWxlcyBhcmUgYWxsb3dlZC4nKTtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgdXJsID0gYCR7dGhpcy5iYXNlVXJsfS8ke3Nhbml0aXplZFBhdGh9YDtcbiAgICBjb25zdCBkYXRhID0gYXdhaXQgdGhpcy5naXRodWJDbGllbnQuZmV0Y2hGcm9tR2l0SHViKHVybCk7XG4gICAgXG4gICAgaWYgKGRhdGEudHlwZSAhPT0gJ2ZpbGUnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BhdGggZG9lcyBub3QgcG9pbnQgdG8gYSBmaWxlJyk7XG4gICAgfVxuICAgIFxuICAgIC8vIENoZWNrIGZpbGUgc2l6ZSBiZWZvcmUgZG93bmxvYWRpbmdcbiAgICBpZiAoZGF0YS5zaXplID4gU0VDVVJJVFlfTElNSVRTLk1BWF9QRVJTT05BX1NJWkVfQllURVMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRmlsZSB0b28gbGFyZ2UgKCR7ZGF0YS5zaXplfSBieXRlcywgbWF4ICR7U0VDVVJJVFlfTElNSVRTLk1BWF9QRVJTT05BX1NJWkVfQllURVN9IGJ5dGVzKWApO1xuICAgIH1cbiAgICBcbiAgICAvLyBEZWNvZGUgQmFzZTY0IGNvbnRlbnRcbiAgICBjb25zdCBjb250ZW50ID0gQnVmZmVyLmZyb20oZGF0YS5jb250ZW50LCAnYmFzZTY0JykudG9TdHJpbmcoJ3V0Zi04Jyk7XG4gICAgXG4gICAgLy8gVmFsaWRhdGUgY29udGVudCBzaXplIGFmdGVyIGRlY29kaW5nXG4gICAgdmFsaWRhdGVDb250ZW50U2l6ZShjb250ZW50LCBTRUNVUklUWV9MSU1JVFMuTUFYX1BFUlNPTkFfU0laRV9CWVRFUyk7XG4gICAgXG4gICAgLy8gU2FuaXRpemUgY29udGVudCBmb3Igc2VjdXJpdHkgdGhyZWF0c1xuICAgIGNvbnN0IHNhbml0aXplZENvbnRlbnQgPSBDb250ZW50VmFsaWRhdG9yLnNhbml0aXplUGVyc29uYUNvbnRlbnQoY29udGVudCk7XG4gICAgXG4gICAgLy8gVXNlIHNlY3VyZSBZQU1MIHBhcnNlclxuICAgIGxldCBwYXJzZWQ7XG4gICAgdHJ5IHtcbiAgICAgIHBhcnNlZCA9IFNlY3VyZVlhbWxQYXJzZXIuc2FmZU1hdHRlcihzYW5pdGl6ZWRDb250ZW50KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgU2VjdXJpdHlFcnJvcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFNlY3VyaXR5IHRocmVhdCBpbiBjb250ZW50OiAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgbWV0YWRhdGEgPSBwYXJzZWQuZGF0YSBhcyBJRWxlbWVudE1ldGFkYXRhO1xuICAgIFxuICAgIC8vIEFkZGl0aW9uYWwgbWV0YWRhdGEgdmFsaWRhdGlvbiBmb3IgaW5qZWN0aW9uIGF0dGFja3NcbiAgICBjb25zdCBtZXRhZGF0YVZhbGlkYXRpb24gPSBDb250ZW50VmFsaWRhdG9yLnZhbGlkYXRlTWV0YWRhdGEobWV0YWRhdGEpO1xuICAgIGlmICghbWV0YWRhdGFWYWxpZGF0aW9uLmlzVmFsaWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgU2VjdXJpdHkgdmFsaWRhdGlvbiBmYWlsZWQ6ICR7bWV0YWRhdGFWYWxpZGF0aW9uLmRldGVjdGVkUGF0dGVybnM/LmpvaW4oJywgJyl9YCk7XG4gICAgfVxuICAgIFxuICAgIC8vIFZhbGlkYXRlIG1ldGFkYXRhXG4gICAgaWYgKCFtZXRhZGF0YS5uYW1lIHx8ICFtZXRhZGF0YS5kZXNjcmlwdGlvbikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGNvbnRlbnQ6IG1pc3NpbmcgcmVxdWlyZWQgbmFtZSBvciBkZXNjcmlwdGlvbicpO1xuICAgIH1cbiAgICBcbiAgICAvLyBHZW5lcmF0ZSBhbmQgdmFsaWRhdGUgbG9jYWwgZmlsZW5hbWVcbiAgICBjb25zdCBvcmlnaW5hbEZpbGVuYW1lID0gc2FuaXRpemVkUGF0aC5zcGxpdCgnLycpLnBvcCgpIHx8ICdkb3dubG9hZGVkLWVsZW1lbnQubWQnO1xuICAgIGNvbnN0IGZpbGVuYW1lID0gdmFsaWRhdGVGaWxlbmFtZShvcmlnaW5hbEZpbGVuYW1lKTtcbiAgICBcbiAgICAvLyBHZXQgYXBwcm9wcmlhdGUgZGlyZWN0b3J5IGZvciBlbGVtZW50IHR5cGVcbiAgICBjb25zdCBlbGVtZW50RGlyID0gdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmdldEVsZW1lbnREaXIoZWxlbWVudFR5cGUpO1xuICAgIGNvbnN0IGxvY2FsUGF0aCA9IHBhdGguam9pbihlbGVtZW50RGlyLCBmaWxlbmFtZSk7XG4gICAgXG4gICAgLy8gQ2hlY2sgaWYgZmlsZSBhbHJlYWR5IGV4aXN0c1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy5hY2Nlc3MobG9jYWxQYXRoKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBtZXNzYWdlOiBgQUkgY3VzdG9taXphdGlvbiBlbGVtZW50IGFscmVhZHkgZXhpc3RzOiAke2ZpbGVuYW1lfVxcblxcblRoZSBlbGVtZW50IGhhcyBhbHJlYWR5IGJlZW4gaW5zdGFsbGVkLmBcbiAgICAgIH07XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBGaWxlIGRvZXNuJ3QgZXhpc3QsIHByb2NlZWQgd2l0aCBpbnN0YWxsYXRpb25cbiAgICB9XG4gICAgXG4gICAgLy8gV3JpdGUgdGhlIHNhbml0aXplZCBmaWxlXG4gICAgYXdhaXQgZnMud3JpdGVGaWxlKGxvY2FsUGF0aCwgc2FuaXRpemVkQ29udGVudCwgJ3V0Zi04Jyk7XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICBtZXNzYWdlOiBgQUkgY3VzdG9taXphdGlvbiBlbGVtZW50IGluc3RhbGxlZCBzdWNjZXNzZnVsbHkhYCxcbiAgICAgIG1ldGFkYXRhLFxuICAgICAgZmlsZW5hbWUsXG4gICAgICBlbGVtZW50VHlwZVxuICAgIH07XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgRWxlbWVudFR5cGUgZnJvbSBzdHJpbmdcbiAgICovXG4gIHByaXZhdGUgZ2V0RWxlbWVudFR5cGVGcm9tU3RyaW5nKHR5cGVTdHI6IHN0cmluZyk6IEVsZW1lbnRUeXBlIHtcbiAgICBjb25zdCB0eXBlTWFwOiBSZWNvcmQ8c3RyaW5nLCBFbGVtZW50VHlwZT4gPSB7XG4gICAgICAncGVyc29uYXMnOiBFbGVtZW50VHlwZS5QRVJTT05BLFxuICAgICAgJ3NraWxscyc6IEVsZW1lbnRUeXBlLlNLSUxMLFxuICAgICAgJ3RlbXBsYXRlcyc6IEVsZW1lbnRUeXBlLlRFTVBMQVRFLFxuICAgICAgJ2FnZW50cyc6IEVsZW1lbnRUeXBlLkFHRU5UXG4gICAgfTtcbiAgICBcbiAgICBjb25zdCBlbGVtZW50VHlwZSA9IHR5cGVNYXBbdHlwZVN0cl07XG4gICAgaWYgKCFlbGVtZW50VHlwZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIGVsZW1lbnQgdHlwZTogJHt0eXBlU3RyfS4gVmFsaWQgdHlwZXM6ICR7T2JqZWN0LmtleXModHlwZU1hcCkuam9pbignLCAnKX1gKTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIGVsZW1lbnRUeXBlO1xuICB9XG4gIFxuICAvKipcbiAgICogRm9ybWF0IGluc3RhbGxhdGlvbiBzdWNjZXNzIG1lc3NhZ2VcbiAgICovXG4gIGZvcm1hdEluc3RhbGxTdWNjZXNzKG1ldGFkYXRhOiBJRWxlbWVudE1ldGFkYXRhLCBmaWxlbmFtZTogc3RyaW5nLCBlbGVtZW50VHlwZTogRWxlbWVudFR5cGUpOiBzdHJpbmcge1xuICAgIGNvbnN0IHR5cGVFbW9qaXM6IFJlY29yZDxFbGVtZW50VHlwZSwgc3RyaW5nPiA9IHtcbiAgICAgIFtFbGVtZW50VHlwZS5QRVJTT05BXTogJ/Cfjq0nLFxuICAgICAgW0VsZW1lbnRUeXBlLlNLSUxMXTogJ/Cfjq8nLFxuICAgICAgW0VsZW1lbnRUeXBlLlRFTVBMQVRFXTogJ/Cfk4QnLFxuICAgICAgW0VsZW1lbnRUeXBlLkFHRU5UXTogJ/CfpJYnLFxuICAgICAgW0VsZW1lbnRUeXBlLk1FTU9SWV06ICfwn6egJyxcbiAgICAgIFtFbGVtZW50VHlwZS5FTlNFTUJMRV06ICfwn468J1xuICAgIH07XG4gICAgXG4gICAgY29uc3QgZW1vamkgPSB0eXBlRW1vamlzW2VsZW1lbnRUeXBlXSB8fCAn8J+Tpic7XG4gICAgY29uc3QgdHlwZU5hbWUgPSBlbGVtZW50VHlwZS5jaGFyQXQoMCkudG9VcHBlckNhc2UoKSArIGVsZW1lbnRUeXBlLnNsaWNlKDEpO1xuICAgIFxuICAgIHJldHVybiBg4pyFICoqQUkgQ3VzdG9taXphdGlvbiBFbGVtZW50IEluc3RhbGxlZCBTdWNjZXNzZnVsbHkhKipcXG5cXG5gICtcbiAgICAgIGAke2Vtb2ppfSAqKiR7bWV0YWRhdGEubmFtZX0qKiAke21ldGFkYXRhLmF1dGhvciA/IGBieSAke21ldGFkYXRhLmF1dGhvcn1gIDogJyd9XFxuYCArXG4gICAgICBg8J+TgSBUeXBlOiAke3R5cGVOYW1lfVxcbmAgK1xuICAgICAgYPCfk4QgU2F2ZWQgYXM6ICR7ZmlsZW5hbWV9XFxuXFxuYCArXG4gICAgICBg8J+agCAqKlJlYWR5IHRvIHVzZSEqKmA7XG4gIH1cbn0iXX0=