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.

339 lines 46.1 kB
/** * Portfolio Manager - Manages the portfolio directory structure for all element types */ import * as fs from 'fs/promises'; import * as path from 'path'; import { homedir } from 'os'; import { logger } from '../utils/logger.js'; import { ElementType } from './types.js'; import { SecurityMonitor } from '../security/securityMonitor.js'; import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; import { DefaultElementProvider } from './DefaultElementProvider.js'; // Constants const ELEMENT_FILE_EXTENSION = '.md'; export { ElementType }; export class PortfolioManager { static instance; static instanceLock = false; static initializationLock = false; static initializationPromise = null; baseDir; constructor(config) { // Get potential directory from environment or config const envDir = process.env.DOLLHOUSE_PORTFOLIO_DIR; const configDir = config?.baseDir; const defaultDir = path.join(homedir(), '.dollhouse', 'portfolio'); // Validate environment variable if provided if (envDir) { if (!path.isAbsolute(envDir)) { throw new Error('DOLLHOUSE_PORTFOLIO_DIR must be an absolute path'); } // Additional validation for suspicious paths if (envDir.includes('..') || envDir.startsWith('/etc') || envDir.startsWith('/sys')) { throw new Error('DOLLHOUSE_PORTFOLIO_DIR contains suspicious path segments'); } } // Validate config directory if provided if (configDir && !path.isAbsolute(configDir)) { throw new Error('Portfolio config baseDir must be an absolute path'); } // Use environment variable if set, otherwise config, otherwise default this.baseDir = envDir || configDir || defaultDir; logger.info(`[PortfolioManager] Portfolio base directory: ${this.baseDir}`); } static getInstance(config) { if (!PortfolioManager.instance) { // Check if another thread is already creating the instance if (PortfolioManager.instanceLock) { throw new Error('PortfolioManager instance is being created by another thread'); } try { PortfolioManager.instanceLock = true; PortfolioManager.instance = new PortfolioManager(config); } finally { PortfolioManager.instanceLock = false; } } return PortfolioManager.instance; } /** * Get the base portfolio directory */ getBaseDir() { return this.baseDir; } /** * Get the directory for a specific element type */ getElementDir(type) { return path.join(this.baseDir, type); } /** * Initialize the portfolio directory structure * Uses locking to prevent race conditions during concurrent initialization */ async initialize() { // If already initializing, wait for the existing initialization if (PortfolioManager.initializationPromise) { return PortfolioManager.initializationPromise; } // If already initialized, check if directories exist if (await this.exists()) { logger.debug('[PortfolioManager] Portfolio already initialized'); return; } // Create initialization promise to prevent concurrent initialization PortfolioManager.initializationPromise = this.performInitialization(); try { await PortfolioManager.initializationPromise; } finally { // Clear the promise after completion PortfolioManager.initializationPromise = null; } } /** * Perform the actual initialization - should only be called once */ async performInitialization() { logger.info('[PortfolioManager] Initializing portfolio directory structure'); // Create base directory try { await fs.mkdir(this.baseDir, { recursive: true }); } catch (error) { const err = error; // In read-only environments (like Docker), we can't create directories // Log but continue - the portfolio will be empty but functional if (err.code === 'EACCES' || err.code === 'EROFS' || err.code === 'ENOENT') { logger.warn(`[PortfolioManager] Cannot create portfolio directory (read-only environment?): ${err.message}`); console.log(`[DollhouseMCP] Running in read-only mode - portfolio features disabled`); return; } throw error; } // Create subdirectories for each element type for (const elementType of Object.values(ElementType)) { const elementDir = path.join(this.baseDir, elementType); await fs.mkdir(elementDir, { recursive: true }); logger.debug(`[PortfolioManager] Created directory: ${elementDir}`); } // Create special directories for stateful elements const agentStateDir = path.join(this.baseDir, ElementType.AGENT, '.state'); await fs.mkdir(agentStateDir, { recursive: true }); logger.info('[PortfolioManager] Portfolio directory structure initialized'); // Migration for v1.4.2 users: rename singular directories to plural await this.migrateFromSingularDirectories(); // Populate with default elements if this is a new installation // Skip during tests to avoid interference if (process.env.NODE_ENV !== 'test') { try { const defaultProvider = new DefaultElementProvider(); await defaultProvider.populateDefaults(this.baseDir); } catch (error) { logger.error('[PortfolioManager] Error populating default elements:', error); // Log to stderr for Claude Desktop visibility console.error(`[PortfolioManager] CRITICAL: Failed to populate default elements: ${error instanceof Error ? error.message : String(error)}`); // Continue anyway - empty portfolio is valid } } } /** * Check if portfolio directory exists */ async exists() { try { await fs.access(this.baseDir); return true; } catch { return false; } } /** * List all elements of a specific type */ async listElements(type) { const elementDir = this.getElementDir(type); try { const files = await fs.readdir(elementDir); // Filter for markdown files only return files.filter(file => file.endsWith(ELEMENT_FILE_EXTENSION)); } catch (error) { const err = error; if (err.code === 'ENOENT') { // Directory doesn't exist yet - this is expected for new installations logger.debug(`[PortfolioManager] Element directory doesn't exist yet: ${elementDir}`); return []; } if (err.code === 'EACCES' || err.code === 'EPERM') { // Permission denied - log but return empty array logger.error(`[PortfolioManager] Permission denied accessing directory: ${elementDir}`, { code: err.code, message: err.message }); return []; } if (err.code === 'ENOTDIR') { // Path exists but is not a directory logger.error(`[PortfolioManager] Path is not a directory: ${elementDir}`, { code: err.code, message: err.message }); throw new Error(`Path is not a directory: ${elementDir}`); } // For any other errors, throw with context logger.error(`[PortfolioManager] Error reading directory: ${elementDir}`, { code: err.code, message: err.message, stack: err.stack }); throw error; } } /** * Get full path to an element file */ getElementPath(type, filename) { // Validate filename to prevent path traversal if (!filename || typeof filename !== 'string') { SecurityMonitor.logSecurityEvent({ type: 'PATH_TRAVERSAL_ATTEMPT', severity: 'MEDIUM', source: 'PortfolioManager.getElementPath', details: `Invalid filename provided: ${typeof filename}`, additionalData: { elementType: type, filename: String(filename) } }); throw new Error('Invalid filename: must be a non-empty string'); } // Check for path traversal attempts if (filename.includes('..') || filename.includes('/') || filename.includes('\\') || path.isAbsolute(filename)) { SecurityMonitor.logSecurityEvent({ type: 'PATH_TRAVERSAL_ATTEMPT', severity: 'HIGH', source: 'PortfolioManager.getElementPath', details: `Path traversal attempt detected in filename: ${filename}`, additionalData: { elementType: type, filename } }); throw new Error(`Invalid filename: contains path traversal characters: ${filename}`); } // Additional validation for hidden files and special characters if (filename.startsWith('.') || filename.includes('\0')) { SecurityMonitor.logSecurityEvent({ type: 'PATH_TRAVERSAL_ATTEMPT', severity: 'MEDIUM', source: 'PortfolioManager.getElementPath', details: `Invalid filename characters detected: ${filename}`, additionalData: { elementType: type, filename, hasHiddenFile: filename.startsWith('.'), hasNullByte: filename.includes('\0') } }); throw new Error(`Invalid filename: contains invalid characters: ${filename}`); } // Ensure filename ends with .md const safeFilename = filename.endsWith('.md') ? filename : `${filename}.md`; return path.join(this.getElementDir(type), safeFilename); } /** * Check if an element exists */ async elementExists(type, filename) { try { await fs.access(this.getElementPath(type, filename)); return true; } catch { return false; } } /** * Get legacy personas directory path (for migration) */ getLegacyPersonasDir() { return path.join(homedir(), '.dollhouse', 'personas'); } /** * Check if legacy personas directory exists */ async hasLegacyPersonas() { try { await fs.access(this.getLegacyPersonasDir()); const files = await fs.readdir(this.getLegacyPersonasDir()); return files.some(file => file.endsWith('.md')); } catch { return false; } } /** * Get portfolio statistics */ async getStatistics() { const stats = {}; for (const elementType of Object.values(ElementType)) { const elements = await this.listElements(elementType); stats[elementType] = elements.length; } return stats; } /** * Migrate from v1.4.2 singular directory names to v1.4.3 plural names * This handles the upgrade path for existing users */ async migrateFromSingularDirectories() { const oldToNew = { 'persona': 'personas', 'skill': 'skills', 'template': 'templates', 'agent': 'agents', 'memory': 'memories', 'ensemble': 'ensembles' }; for (const [oldName, newName] of Object.entries(oldToNew)) { // Unicode normalize the directory names (even though they're hardcoded, for security audit) const normalizedOld = UnicodeValidator.normalize(oldName); const normalizedNew = UnicodeValidator.normalize(newName); if (!normalizedOld.isValid || !normalizedNew.isValid) { // This should never happen with our hardcoded values, but for completeness logger.error(`[PortfolioManager] Invalid Unicode in directory names during migration`); continue; } const oldDir = path.join(this.baseDir, normalizedOld.normalizedContent); const newDir = path.join(this.baseDir, normalizedNew.normalizedContent); try { // Check if old directory exists await fs.access(oldDir); // Check if new directory already has content try { const newDirFiles = await fs.readdir(newDir); if (newDirFiles.length > 0) { logger.warn(`[PortfolioManager] Both ${oldName} and ${newName} directories exist. Keeping ${newName}, skipping migration.`, { oldDir, newDir, fileCount: newDirFiles.length }); continue; } } catch { // New directory doesn't exist or is empty, proceed with migration } // Perform the migration logger.info(`[PortfolioManager] Migrating ${oldName}${newName}`); await fs.rename(oldDir, newDir); // Log security event for audit trail SecurityMonitor.logSecurityEvent({ type: 'DIRECTORY_MIGRATION', severity: 'LOW', source: 'PortfolioManager.migrateFromSingularDirectories', details: `Migrated directory from ${oldName} to ${newName} for v1.4.3 compatibility`, metadata: { oldDir, newDir } }); } catch (error) { // Old directory doesn't exist, which is fine if (error.code !== 'ENOENT') { logger.error(`[PortfolioManager] Error during migration of ${oldName}:`, error); } } } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUG9ydGZvbGlvTWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wb3J0Zm9saW8vUG9ydGZvbGlvTWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDN0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxXQUFXLEVBQW1CLE1BQU0sWUFBWSxDQUFDO0FBQzFELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNqRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUM5RSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUVyRSxZQUFZO0FBQ1osTUFBTSxzQkFBc0IsR0FBRyxLQUFLLENBQUM7QUFFckMsT0FBTyxFQUFFLFdBQVcsRUFBRSxDQUFDO0FBR3ZCLE1BQU0sT0FBTyxnQkFBZ0I7SUFDbkIsTUFBTSxDQUFDLFFBQVEsQ0FBbUI7SUFDbEMsTUFBTSxDQUFDLFlBQVksR0FBRyxLQUFLLENBQUM7SUFDNUIsTUFBTSxDQUFDLGtCQUFrQixHQUFHLEtBQUssQ0FBQztJQUNsQyxNQUFNLENBQUMscUJBQXFCLEdBQXlCLElBQUksQ0FBQztJQUMxRCxPQUFPLENBQVM7SUFFeEIsWUFBb0IsTUFBd0I7UUFDMUMscURBQXFEO1FBQ3JELE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsdUJBQXVCLENBQUM7UUFDbkQsTUFBTSxTQUFTLEdBQUcsTUFBTSxFQUFFLE9BQU8sQ0FBQztRQUNsQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8s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QUM7d0JBQzNCLE1BQU0sQ0FBQyxJQUFJLENBQ1QsMkJBQTJCLE9BQU8sUUFBUSxPQUFPLCtCQUErQixPQUFPLHVCQUF1QixFQUM5RyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLFdBQVcsQ0FBQyxNQUFNLEVBQUUsQ0FDbEQsQ0FBQzt3QkFDRixTQUFTO29CQUNYLENBQUM7Z0JBQ0gsQ0FBQztnQkFBQyxNQUFNLENBQUM7b0JBQ1Asa0VBQWtFO2dCQUNwRSxDQUFDO2dCQUVELHdCQUF3QjtnQkFDeEIsTUFBTSxDQUFDLElBQUksQ0FBQyxnQ0FBZ0MsT0FBTyxNQUFNLE9BQU8sRUFBRSxDQUFDLENBQUM7Z0JBQ3BFLE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7Z0JBRWhDLHFDQUFxQztnQkFDckMsZUFBZSxDQUFDLGdCQUFnQixDQUFDO29CQUMvQixJQUFJLEVBQUUscUJBQXFCO29CQUMzQixRQUFRLEVBQUUsS0FBSztvQkFDZixNQUFNLEVBQUUsaURBQWlEO29CQUN6RCxPQUFPLEVBQUUsMkJBQTJCLE9BQU8sT0FBTyxPQUFPLDJCQUEyQjtvQkFDcEYsUUFBUSxFQUFFLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRTtpQkFDN0IsQ0FBQyxDQUFDO1lBRUwsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsNkNBQTZDO2dCQUM3QyxJQUFLLEtBQWEsQ0FBQyxJQUFJLEtBQUssUUFBUSxFQUFFLENBQUM7b0JBQ3JDLE1BQU0sQ0FBQyxLQUFLLENBQUMsZ0RBQWdELE9BQU8sR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO2dCQUNsRixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQb3J0Zm9saW8gTWFuYWdlciAtIE1hbmFnZXMgdGhlIHBvcnRmb2xpbyBkaXJlY3Rvcnkgc3RydWN0dXJlIGZvciBhbGwgZWxlbWVudCB0eXBlc1xuICovXG5cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBob21lZGlyIH0gZnJvbSAnb3MnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IEVsZW1lbnRUeXBlLCBQb3J0Zm9saW9Db25maWcgfSBmcm9tICcuL3R5cGVzLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5TW9uaXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3NlY3VyaXR5TW9uaXRvci5qcyc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IERlZmF1bHRFbGVtZW50UHJvdmlkZXIgfSBmcm9tICcuL0RlZmF1bHRFbGVtZW50UHJvdmlkZXIuanMnO1xuXG4vLyBDb25zdGFudHNcbmNvbnN0IEVMRU1FTlRfRklMRV9FWFRFTlNJT04gPSAnLm1kJztcblxuZXhwb3J0IHsgRWxlbWVudFR5cGUgfTtcbmV4cG9ydCB0eXBlIHsgUG9ydGZvbGlvQ29uZmlnIH07XG5cbmV4cG9ydCBjbGFzcyBQb3J0Zm9saW9NYW5hZ2VyIHtcbiAgcHJpdmF0ZSBzdGF0aWMgaW5zdGFuY2U6IFBvcnRmb2xpb01hbmFnZXI7XG4gIHByaXZhdGUgc3RhdGljIGluc3RhbmNlTG9jayA9IGZhbHNlO1xuICBwcml2YXRlIHN0YXRpYyBpbml0aWFsaXphdGlvbkxvY2sgPSBmYWxzZTtcbiAgcHJpdmF0ZSBzdGF0aWMgaW5pdGlhbGl6YXRpb25Qcm9taXNlOiBQcm9taXNlPHZvaWQ+IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgYmFzZURpcjogc3RyaW5nO1xuICBcbiAgcHJpdmF0ZSBjb25zdHJ1Y3Rvcihjb25maWc/OiBQb3J0Zm9saW9Db25maWcpIHtcbiAgICAvLyBHZXQgcG90ZW50aWFsIGRpcmVjdG9yeSBmcm9tIGVudmlyb25tZW50IG9yIGNvbmZpZ1xuICAgIGNvbnN0IGVudkRpciA9IHByb2Nlc3MuZW52LkRPTExIT1VTRV9QT1JURk9MSU9fRElSO1xuICAgIGNvbnN0IGNvbmZpZ0RpciA9IGNvbmZpZz8uYmFzZURpcjtcbiAgICBjb25zdCBkZWZhdWx0RGlyID0gcGF0aC5qb2luKGhvbWVkaXIoKSwgJy5kb2xsaG91c2UnLCAncG9ydGZvbGlvJyk7XG4gICAgXG4gICAgLy8gVmFsaWRhdGUgZW52aXJvbm1lbnQgdmFyaWFibGUgaWYgcHJvdmlkZWRcbiAgICBpZiAoZW52RGlyKSB7XG4gICAgICBpZiAoIXBhdGguaXNBYnNvbHV0ZShlbnZEaXIpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignRE9MTEhPVVNFX1BPUlRGT0xJT19ESVIgbXVzdCBiZSBhbiBhYnNvbHV0ZSBwYXRoJyk7XG4gICAgICB9XG4gICAgICAvLyBBZGRpdGlvbmFsIHZhbGlkYXRpb24gZm9yIHN1c3BpY2lvdXMgcGF0aHNcbiAgICAgIGlmIChlbnZEaXIuaW5jbHVkZXMoJy4uJykgfHwgZW52RGlyLnN0YXJ0c1dpdGgoJy9ldGMnKSB8fCBlbnZEaXIuc3RhcnRzV2l0aCgnL3N5cycpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignRE9MTEhPVVNFX1BPUlRGT0xJT19ESVIgY29udGFpbnMgc3VzcGljaW91cyBwYXRoIHNlZ21lbnRzJyk7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIC8vIFZhbGlkYXRlIGNvbmZpZyBkaXJlY3RvcnkgaWYgcHJvdmlkZWRcbiAgICBpZiAoY29uZmlnRGlyICYmICFwYXRoLmlzQWJzb2x1dGUoY29uZmlnRGlyKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdQb3J0Zm9saW8gY29uZmlnIGJhc2VEaXIgbXVzdCBiZSBhbiBhYnNvbHV0ZSBwYXRoJyk7XG4gICAgfVxuICAgIFxuICAgIC8vIFVzZSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpZiBzZXQsIG90aGVyd2lzZSBjb25maWcsIG90aGVyd2lzZSBkZWZhdWx0XG4gICAgdGhpcy5iYXNlRGlyID0gZW52RGlyIHx8IGNvbmZpZ0RpciB8fCBkZWZhdWx0RGlyO1xuICAgIFxuICAgIGxvZ2dlci5pbmZvKGBbUG9ydGZvbGlvTWFuYWdlcl0gUG9ydGZvbGlvIGJhc2UgZGlyZWN0b3J5OiAke3RoaXMuYmFzZURpcn1gKTtcbiAgfVxuICBcbiAgcHVibGljIHN0YXRpYyBnZXRJbnN0YW5jZShjb25maWc/OiBQb3J0Zm9saW9Db25maWcpOiBQb3J0Zm9saW9NYW5hZ2VyIHtcbiAgICBpZiAoIVBvcnRmb2xpb01hbmFnZXIuaW5zdGFuY2UpIHtcbiAgICAgIC8vIENoZWNrIGlmIGFub3RoZXIgdGhyZWFkIGlzIGFscmVhZHkgY3JlYXRpbmcgdGhlIGluc3RhbmNlXG4gICAgICBpZiAoUG9ydGZvbGlvTWFuYWdlci5pbnN0YW5jZUxvY2spIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQb3J0Zm9saW9NYW5hZ2VyIGluc3RhbmNlIGlzIGJlaW5nIGNyZWF0ZWQgYnkgYW5vdGhlciB0aHJlYWQnKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgdHJ5IHtcbiAgICAgICAgUG9ydGZvbGlvTWFuYWdlci5pbnN0YW5jZUxvY2sgPSB0cnVlO1xuICAgICAgICBQb3J0Zm9saW9NYW5hZ2VyLmluc3RhbmNlID0gbmV3IFBvcnRmb2xpb01hbmFnZXIoY29uZmlnKTtcbiAgICAgIH0gZmluYWxseSB7XG4gICAgICAgIFBvcnRmb2xpb01hbmFnZXIuaW5zdGFuY2VMb2NrID0gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBQb3J0Zm9saW9NYW5hZ2VyLmluc3RhbmNlO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IHRoZSBiYXNlIHBvcnRmb2xpbyBkaXJlY3RvcnlcbiAgICovXG4gIHB1YmxpYyBnZXRCYXNlRGlyKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuYmFzZURpcjtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCB0aGUgZGlyZWN0b3J5IGZvciBhIHNwZWNpZmljIGVsZW1lbnQgdHlwZVxuICAgKi9cbiAgcHVibGljIGdldEVsZW1lbnREaXIodHlwZTogRWxlbWVudFR5cGUpOiBzdHJpbmcge1xuICAgIHJldHVybiBwYXRoLmpvaW4odGhpcy5iYXNlRGlyLCB0eXBlKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEluaXRpYWxpemUgdGhlIHBvcnRmb2xpbyBkaXJlY3Rvcnkgc3RydWN0dXJlXG4gICAqIFVzZXMgbG9ja2luZyB0byBwcmV2ZW50IHJhY2UgY29uZGl0aW9ucyBkdXJpbmcgY29uY3VycmVudCBpbml0aWFsaXphdGlvblxuICAgKi9cbiAgcHVibGljIGFzeW5jIGluaXRpYWxpemUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gSWYgYWxyZWFkeSBpbml0aWFsaXppbmcsIHdhaXQgZm9yIHRoZSBleGlzdGluZyBpbml0aWFsaXphdGlvblxuICAgIGlmIChQb3J0Zm9saW9NYW5hZ2VyLmluaXRpYWxpemF0aW9uUHJvbWlzZSkge1xuICAgICAgcmV0dXJuIFBvcnRmb2xpb01hbmFnZXIuaW5pdGlhbGl6YXRpb25Qcm9taXNlO1xuICAgIH1cbiAgICBcbiAgICAvLyBJZiBhbHJlYWR5IGluaXRpYWxpemVkLCBjaGVjayBpZiBkaXJlY3RvcmllcyBleGlzdFxuICAgIGlmIChhd2FpdCB0aGlzLmV4aXN0cygpKSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ1tQb3J0Zm9saW9NYW5hZ2VyXSBQb3J0Zm9saW8gYWxyZWFkeSBpbml0aWFsaXplZCcpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBcbiAgICAvLyBDcmVhdGUgaW5pdGlhbGl6YXRpb24gcHJvbWlzZSB0byBwcmV2ZW50IGNvbmN1cnJlbnQgaW5pdGlhbGl6YXRpb25cbiAgICBQb3J0Zm9saW9NYW5hZ2VyLmluaXRpYWxpemF0aW9uUHJvbWlzZSA9IHRoaXMucGVyZm9ybUluaXRpYWxpemF0aW9uKCk7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IFBvcnRmb2xpb01hbmFnZXIuaW5pdGlhbGl6YXRpb25Qcm9taXNlO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAvLyBDbGVhciB0aGUgcHJvbWlzZSBhZnRlciBjb21wbGV0aW9uXG4gICAgICBQb3J0Zm9saW9NYW5hZ2VyLmluaXRpYWxpemF0aW9uUHJvbWlzZSA9IG51bGw7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogUGVyZm9ybSB0aGUgYWN0dWFsIGluaXRpYWxpemF0aW9uIC0gc2hvdWxkIG9ubHkgYmUgY2FsbGVkIG9uY2VcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcGVyZm9ybUluaXRpYWxpemF0aW9uKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGxvZ2dlci5pbmZvKCdbUG9ydGZvbGlvTWFuYWdlcl0gSW5pdGlhbGl6aW5nIHBvcnRmb2xpbyBkaXJlY3Rvcnkgc3RydWN0dXJlJyk7XG4gICAgXG4gICAgLy8gQ3JlYXRlIGJhc2UgZGlyZWN0b3J5XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLm1rZGlyKHRoaXMuYmFzZURpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnN0IGVyciA9IGVycm9yIGFzIE5vZGVKUy5FcnJub0V4Y2VwdGlvbjtcbiAgICAgIC8vIEluIHJlYWQtb25seSBlbnZpcm9ubWVudHMgKGxpa2UgRG9ja2VyKSwgd2UgY2FuJ3QgY3JlYXRlIGRpcmVjdG9yaWVzXG4gICAgICAvLyBMb2cgYnV0IGNvbnRpbnVlIC0gdGhlIHBvcnRmb2xpbyB3aWxsIGJlIGVtcHR5IGJ1dCBmdW5jdGlvbmFsXG4gICAgICBpZiAoZXJyLmNvZGUgPT09ICdFQUNDRVMnIHx8IGVyci5jb2RlID09PSAnRVJPRlMnIHx8IGVyci5jb2RlID09PSAnRU5PRU5UJykge1xuICAgICAgICBsb2dnZXIud2FybihgW1BvcnRmb2xpb01hbmFnZXJdIENhbm5vdCBjcmVhdGUgcG9ydGZvbGlvIGRpcmVjdG9yeSAocmVhZC1vbmx5IGVudmlyb25tZW50Pyk6ICR7ZXJyLm1lc3NhZ2V9YCk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBbRG9sbGhvdXNlTUNQXSBSdW5uaW5nIGluIHJlYWQtb25seSBtb2RlIC0gcG9ydGZvbGlvIGZlYXR1cmVzIGRpc2FibGVkYCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgICBcbiAgICAvLyBDcmVhdGUgc3ViZGlyZWN0b3JpZXMgZm9yIGVhY2ggZWxlbWVudCB0eXBlXG4gICAgZm9yIChjb25zdCBlbGVtZW50VHlwZSBvZiBPYmplY3QudmFsdWVzKEVsZW1lbnRUeXBlKSkge1xuICAgICAgY29uc3QgZWxlbWVudERpciA9IHBhdGguam9pbih0aGlzLmJhc2VEaXIsIGVsZW1lbnRUeXBlKTtcbiAgICAgIGF3YWl0IGZzLm1rZGlyKGVsZW1lbnREaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgbG9nZ2VyLmRlYnVnKGBbUG9ydGZvbGlvTWFuYWdlcl0gQ3JlYXRlZCBkaXJlY3Rvcnk6ICR7ZWxlbWVudERpcn1gKTtcbiAgICB9XG4gICAgXG4gICAgLy8gQ3JlYXRlIHNwZWNpYWwgZGlyZWN0b3JpZXMgZm9yIHN0YXRlZnVsIGVsZW1lbnRzXG4gICAgY29uc3QgYWdlbnRTdGF0ZURpciA9IHBhdGguam9pbih0aGlzLmJhc2VEaXIsIEVsZW1lbnRUeXBlLkFHRU5ULCAnLnN0YXRlJyk7XG4gICAgYXdhaXQgZnMubWtkaXIoYWdlbnRTdGF0ZURpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgXG4gICAgbG9nZ2VyLmluZm8oJ1tQb3J0Zm9saW9NYW5hZ2VyXSBQb3J0Zm9saW8gZGlyZWN0b3J5IHN0cnVjdHVyZSBpbml0aWFsaXplZCcpO1xuICAgIFxuICAgIC8vIE1pZ3JhdGlvbiBmb3IgdjEuNC4yIHVzZXJzOiByZW5hbWUgc2luZ3VsYXIgZGlyZWN0b3JpZXMgdG8gcGx1cmFsXG4gICAgYXdhaXQgdGhpcy5taWdyYXRlRnJvbVNpbmd1bGFyRGlyZWN0b3JpZXMoKTtcbiAgICBcbiAgICAvLyBQb3B1bGF0ZSB3aXRoIGRlZmF1bHQgZWxlbWVudHMgaWYgdGhpcyBpcyBhIG5ldyBpbnN0YWxsYXRpb25cbiAgICAvLyBTa2lwIGR1cmluZyB0ZXN0cyB0byBhdm9pZCBpbnRlcmZlcmVuY2VcbiAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICd0ZXN0Jykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgZGVmYXVsdFByb3ZpZGVyID0gbmV3IERlZmF1bHRFbGVtZW50UHJvdmlkZXIoKTtcbiAgICAgICAgYXdhaXQgZGVmYXVsdFByb3ZpZGVyLnBvcHVsYXRlRGVmYXVsdHModGhpcy5iYXNlRGlyKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignW1BvcnRmb2xpb01hbmFnZXJdIEVycm9yIHBvcHVsYXRpbmcgZGVmYXVsdCBlbGVtZW50czonLCBlcnJvcik7XG4gICAgICAgIC8vIExvZyB0byBzdGRlcnIgZm9yIENsYXVkZSBEZXNrdG9wIHZpc2liaWxpdHlcbiAgICAgICAgY29uc29sZS5lcnJvcihgW1BvcnRmb2xpb01hbmFnZXJdIENSSVRJQ0FMOiBGYWlsZWQgdG8gcG9wdWxhdGUgZGVmYXVsdCBlbGVtZW50czogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YCk7XG4gICAgICAgIC8vIENvbnRpbnVlIGFueXdheSAtIGVtcHR5IHBvcnRmb2xpbyBpcyB2YWxpZFxuICAgICAgfVxuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIHBvcnRmb2xpbyBkaXJlY3RvcnkgZXhpc3RzXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZXhpc3RzKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy5hY2Nlc3ModGhpcy5iYXNlRGlyKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIExpc3QgYWxsIGVsZW1lbnRzIG9mIGEgc3BlY2lmaWMgdHlwZVxuICAgKi9cbiAgcHVibGljIGFzeW5jIGxpc3RFbGVtZW50cyh0eXBlOiBFbGVtZW50VHlwZSk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICBjb25zdCBlbGVtZW50RGlyID0gdGhpcy5nZXRFbGVtZW50RGlyKHR5cGUpO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IGZzLnJlYWRkaXIoZWxlbWVudERpcik7XG4gICAgICAvLyBGaWx0ZXIgZm9yIG1hcmtkb3duIGZpbGVzIG9ubHlcbiAgICAgIHJldHVybiBmaWxlcy5maWx0ZXIoZmlsZSA9PiBmaWxlLmVuZHNXaXRoKEVMRU1FTlRfRklMRV9FWFRFTlNJT04pKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc3QgZXJyID0gZXJyb3IgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uO1xuICAgICAgXG4gICAgICBpZiAoZXJyLmNvZGUgPT09ICdFTk9FTlQnKSB7XG4gICAgICAgIC8vIERpcmVjdG9yeSBkb2Vzbid0IGV4aXN0IHlldCAtIHRoaXMgaXMgZXhwZWN0ZWQgZm9yIG5ldyBpbnN0YWxsYXRpb25zXG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgW1BvcnRmb2xpb01hbmFnZXJdIEVsZW1lbnQgZGlyZWN0b3J5IGRvZXNuJ3QgZXhpc3QgeWV0OiAke2VsZW1lbnREaXJ9YCk7XG4gICAgICAgIHJldHVybiBbXTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgaWYgKGVyci5jb2RlID09PSAnRUFDQ0VTJyB8fCBlcnIuY29kZSA9PT0gJ0VQRVJNJykge1xuICAgICAgICAvLyBQZXJtaXNzaW9uIGRlbmllZCAtIGxvZyBidXQgcmV0dXJuIGVtcHR5IGFycmF5XG4gICAgICAgIGxvZ2dlci5lcnJvcihgW1BvcnRmb2xpb01hbmFnZXJdIFBlcm1pc3Npb24gZGVuaWVkIGFjY2Vzc2luZyBkaXJlY3Rvcnk6ICR7ZWxlbWVudERpcn1gLCB7XG4gICAgICAgICAgY29kZTogZXJyLmNvZGUsXG4gICAgICAgICAgbWVzc2FnZTogZXJyLm1lc3NhZ2VcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBbXTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgaWYgKGVyci5jb2RlID09PSAnRU5PVERJUicpIHtcbiAgICAgICAgLy8gUGF0aCBleGlzdHMgYnV0IGlzIG5vdCBhIGRpcmVjdG9yeVxuICAgICAgICBsb2dnZXIuZXJyb3IoYFtQb3J0Zm9saW9NYW5hZ2VyXSBQYXRoIGlzIG5vdCBhIGRpcmVjdG9yeTogJHtlbGVtZW50RGlyfWAsIHtcbiAgICAgICAgICBjb2RlOiBlcnIuY29kZSxcbiAgICAgICAgICBtZXNzYWdlOiBlcnIubWVzc2FnZVxuICAgICAgICB9KTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBQYXRoIGlzIG5vdCBhIGRpcmVjdG9yeTogJHtlbGVtZW50RGlyfWApO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBGb3IgYW55IG90aGVyIGVycm9ycywgdGhyb3cgd2l0aCBjb250ZXh0XG4gICAgICBsb2dnZXIuZXJyb3IoYFtQb3J0Zm9saW9NYW5hZ2VyXSBFcnJvciByZWFkaW5nIGRpcmVjdG9yeTogJHtlbGVtZW50RGlyfWAsIHtcbiAgICAgICAgY29kZTogZXJyLmNvZGUsXG4gICAgICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlLFxuICAgICAgICBzdGFjazogZXJyLnN0YWNrXG4gICAgICB9KTtcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBmdWxsIHBhdGggdG8gYW4gZWxlbWVudCBmaWxlXG4gICAqL1xuICBwdWJsaWMgZ2V0RWxlbWVudFBhdGgodHlwZTogRWxlbWVudFR5cGUsIGZpbGVuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIC8vIFZhbGlkYXRlIGZpbGVuYW1lIHRvIHByZXZlbnQgcGF0aCB0cmF2ZXJzYWxcbiAgICBpZiAoIWZpbGVuYW1lIHx8IHR5cGVvZiBmaWxlbmFtZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1BBVEhfVFJBVkVSU0FMX0FUVEVNUFQnLFxuICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgIHNvdXJjZTogJ1BvcnRmb2xpb01hbmFnZXIuZ2V0RWxlbWVudFBhdGgnLFxuICAgICAgICBkZXRhaWxzOiBgSW52YWxpZCBmaWxlbmFtZSBwcm92aWRlZDogJHt0eXBlb2YgZmlsZW5hbWV9YCxcbiAgICAgICAgYWRkaXRpb25hbERhdGE6IHsgZWxlbWVudFR5cGU6IHR5cGUsIGZpbGVuYW1lOiBTdHJpbmcoZmlsZW5hbWUpIH1cbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGZpbGVuYW1lOiBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZycpO1xuICAgIH1cbiAgICBcbiAgICAvLyBDaGVjayBmb3IgcGF0aCB0cmF2ZXJzYWwgYXR0ZW1wdHNcbiAgICBpZiAoZmlsZW5hbWUuaW5jbHVkZXMoJy4uJykgfHwgZmlsZW5hbWUuaW5jbHVkZXMoJy8nKSB8fCBmaWxlbmFtZS5pbmNsdWRlcygnXFxcXCcpIHx8IHBhdGguaXNBYnNvbHV0ZShmaWxlbmFtZSkpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1BBVEhfVFJBVkVSU0FMX0FUVEVNUFQnLFxuICAgICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgICBzb3VyY2U6ICdQb3J0Zm9saW9NYW5hZ2VyLmdldEVsZW1lbnRQYXRoJyxcbiAgICAgICAgZGV0YWlsczogYFBhdGggdHJhdmVyc2FsIGF0dGVtcHQgZGV0ZWN0ZWQgaW4gZmlsZW5hbWU6ICR7ZmlsZW5hbWV9YCxcbiAgICAgICAgYWRkaXRpb25hbERhdGE6IHsgZWxlbWVudFR5cGU6IHR5cGUsIGZpbGVuYW1lIH1cbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGZpbGVuYW1lOiBjb250YWlucyBwYXRoIHRyYXZlcnNhbCBjaGFyYWN0ZXJzOiAke2ZpbGVuYW1lfWApO1xuICAgIH1cbiAgICBcbiAgICAvLyBBZGRpdGlvbmFsIHZhbGlkYXRpb24gZm9yIGhpZGRlbiBmaWxlcyBhbmQgc3BlY2lhbCBjaGFyYWN0ZXJzXG4gICAgaWYgKGZpbGVuYW1lLnN0YXJ0c1dpdGgoJy4nKSB8fCBmaWxlbmFtZS5pbmNsdWRlcygnXFwwJykpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1BBVEhfVFJBVkVSU0FMX0FUVEVNUFQnLFxuICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgIHNvdXJjZTogJ1BvcnRmb2xpb01hbmFnZXIuZ2V0RWxlbWVudFBhdGgnLFxuICAgICAgICBkZXRhaWxzOiBgSW52YWxpZCBmaWxlbmFtZSBjaGFyYWN0ZXJzIGRldGVjdGVkOiAke2ZpbGVuYW1lfWAsXG4gICAgICAgIGFkZGl0aW9uYWxEYXRhOiB7IGVsZW1lbnRUeXBlOiB0eXBlLCBmaWxlbmFtZSwgaGFzSGlkZGVuRmlsZTogZmlsZW5hbWUuc3RhcnRzV2l0aCgnLicpLCBoYXNOdWxsQnl0ZTogZmlsZW5hbWUuaW5jbHVkZXMoJ1xcMCcpIH1cbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGZpbGVuYW1lOiBjb250YWlucyBpbnZhbGlkIGNoYXJhY3RlcnM6ICR7ZmlsZW5hbWV9YCk7XG4gICAgfVxuICAgIFxuICAgIC8vIEVuc3VyZSBmaWxlbmFtZSBlbmRzIHdpdGggLm1kXG4gICAgY29uc3Qgc2FmZUZpbGVuYW1lID0gZmlsZW5hbWUuZW5kc1dpdGgoJy5tZCcpID8gZmlsZW5hbWUgOiBgJHtmaWxlbmFtZX0ubWRgO1xuICAgIHJldHVybiBwYXRoLmpvaW4odGhpcy5nZXRFbGVtZW50RGlyKHR5cGUpLCBzYWZlRmlsZW5hbWUpO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2hlY2sgaWYgYW4gZWxlbWVudCBleGlzdHNcbiAgICovXG4gIHB1YmxpYyBhc3luYyBlbGVtZW50RXhpc3RzKHR5cGU6IEVsZW1lbnRUeXBlLCBmaWxlbmFtZTogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLmFjY2Vzcyh0aGlzLmdldEVsZW1lbnRQYXRoKHR5cGUsIGZpbGVuYW1lKSk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgbGVnYWN5IHBlcnNvbmFzIGRpcmVjdG9yeSBwYXRoIChmb3IgbWlncmF0aW9uKVxuICAgKi9cbiAgcHVibGljIGdldExlZ2FjeVBlcnNvbmFzRGlyKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHBhdGguam9pbihob21lZGlyKCksICcuZG9sbGhvdXNlJywgJ3BlcnNvbmFzJyk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiBsZWdhY3kgcGVyc29uYXMgZGlyZWN0b3J5IGV4aXN0c1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGhhc0xlZ2FjeVBlcnNvbmFzKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy5hY2Nlc3ModGhpcy5nZXRMZWdhY3lQZXJzb25hc0RpcigpKTtcbiAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLmdldExlZ2FjeVBlcnNvbmFzRGlyKCkpO1xuICAgICAgcmV0dXJuIGZpbGVzLnNvbWUoZmlsZSA9PiBmaWxlLmVuZHNXaXRoKCcubWQnKSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogR2V0IHBvcnRmb2xpbyBzdGF0aXN0aWNzXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZ2V0U3RhdGlzdGljcygpOiBQcm9taXNlPFJlY29yZDxFbGVtZW50VHlwZSwgbnVtYmVyPj4ge1xuICAgIGNvbnN0IHN0YXRzOiBSZWNvcmQ8c3RyaW5nLCBudW1iZXI+ID0ge307XG4gICAgXG4gICAgZm9yIChjb25zdCBlbGVtZW50VHlwZSBvZiBPYmplY3QudmFsdWVzKEVsZW1lbnRUeXBlKSkge1xuICAgICAgY29uc3QgZWxlbWVudHMgPSBhd2FpdCB0aGlzLmxpc3RFbGVtZW50cyhlbGVtZW50VHlwZSk7XG4gICAgICBzdGF0c1tlbGVtZW50VHlwZV0gPSBlbGVtZW50cy5sZW5ndGg7XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBzdGF0cyBhcyBSZWNvcmQ8RWxlbWVudFR5cGUsIG51bWJlcj47XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBNaWdyYXRlIGZyb20gdjEuNC4yIHNpbmd1bGFyIGRpcmVjdG9yeSBuYW1lcyB0byB2MS40LjMgcGx1cmFsIG5hbWVzXG4gICAqIFRoaXMgaGFuZGxlcyB0aGUgdXBncmFkZSBwYXRoIGZvciBleGlzdGluZyB1c2Vyc1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBtaWdyYXRlRnJvbVNpbmd1bGFyRGlyZWN0b3JpZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgb2xkVG9OZXc6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgICAncGVyc29uYSc6ICdwZXJzb25hcycsXG4gICAgICAnc2tpbGwnOiAnc2tpbGxzJyxcbiAgICAgICd0ZW1wbGF0ZSc6ICd0ZW1wbGF0ZXMnLFxuICAgICAgJ2FnZW50JzogJ2FnZW50cycsXG4gICAgICAnbWVtb3J5JzogJ21lbW9yaWVzJyxcbiAgICAgICdlbnNlbWJsZSc6ICdlbnNlbWJsZXMnXG4gICAgfTtcbiAgICBcbiAgICBmb3IgKGNvbnN0IFtvbGROYW1lLCBuZXdOYW1lXSBvZiBPYmplY3QuZW50cmllcyhvbGRUb05ldykpIHtcbiAgICAgIC8vIFVuaWNvZGUgbm9ybWFsaXplIHRoZSBkaXJlY3RvcnkgbmFtZXMgKGV2ZW4gdGhvdWdoIHRoZXkncmUgaGFyZGNvZGVkLCBmb3Igc2VjdXJpdHkgYXVkaXQpXG4gICAgICBjb25zdCBub3JtYWxpemVkT2xkID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUob2xkTmFtZSk7XG4gICAgICBjb25zdCBub3JtYWxpemVkTmV3ID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUobmV3TmFtZSk7XG4gICAgICBcbiAgICAgIGlmICghbm9ybWFsaXplZE9sZC5pc1ZhbGlkIHx8ICFub3JtYWxpemVkTmV3LmlzVmFsaWQpIHtcbiAgICAgICAgLy8gVGhpcyBzaG91bGQgbmV2ZXIgaGFwcGVuIHdpdGggb3VyIGhhcmRjb2RlZCB2YWx1ZXMsIGJ1dCBmb3IgY29tcGxldGVuZXNzXG4gICAgICAgIGxvZ2dlci5lcnJvcihgW1BvcnRmb2xpb01hbmFnZXJdIEludmFsaWQgVW5pY29kZSBpbiBkaXJlY3RvcnkgbmFtZXMgZHVyaW5nIG1pZ3JhdGlvbmApO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgY29uc3Qgb2xkRGlyID0gcGF0aC5qb2luKHRoaXMuYmFzZURpciwgbm9ybWFsaXplZE9sZC5ub3JtYWxpemVkQ29udGVudCk7XG4gICAgICBjb25zdCBuZXdEaXIgPSBwYXRoLmpvaW4odGhpcy5iYXNlRGlyLCBub3JtYWxpemVkTmV3Lm5vcm1hbGl6ZWRDb250ZW50KTtcbiAgICAgIFxuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gQ2hlY2sgaWYgb2xkIGRpcmVjdG9yeSBleGlzdHNcbiAgICAgICAgYXdhaXQgZnMuYWNjZXNzKG9sZERpcik7XG4gICAgICAgIFxuICAgICAgICAvLyBDaGVjayBpZiBuZXcgZGlyZWN0b3J5IGFscmVhZHkgaGFzIGNvbnRlbnRcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBuZXdEaXJGaWxlcyA9IGF3YWl0IGZzLnJlYWRkaXIobmV3RGlyKTtcbiAgICAgICAgICBpZiAobmV3RGlyRmlsZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgbG9nZ2VyLndhcm4oXG4gICAgICAgICAgICAgIGBbUG9ydGZvbGlvTWFuYWdlcl0gQm90aCAke29sZE5hbWV9IGFuZCAke25ld05hbWV9IGRpcmVjdG9yaWVzIGV4aXN0LiBLZWVwaW5nICR7bmV3TmFtZX0sIHNraXBwaW5nIG1pZ3JhdGlvbi5gLFxuICAgICAgICAgICAgICB7IG9sZERpciwgbmV3RGlyLCBmaWxlQ291bnQ6IG5ld0RpckZpbGVzLmxlbmd0aCB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvLyBOZXcgZGlyZWN0b3J5IGRvZXNuJ3QgZXhpc3Qgb3IgaXMgZW1wdHksIHByb2NlZWQgd2l0aCBtaWdyYXRpb25cbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLy8gUGVyZm9ybSB0aGUgbWlncmF0aW9uXG4gICAgICAgIGxvZ2dlci5pbmZvKGBbUG9ydGZvbGlvTWFuYWdlcl0gTWlncmF0aW5nICR7b2xkTmFtZX0g4oaSICR7bmV3TmFtZX1gKTtcbiAgICAgICAgYXdhaXQgZnMucmVuYW1lKG9sZERpciwgbmV3RGlyKTtcbiAgICAgICAgXG4gICAgICAgIC8vIExvZyBzZWN1cml0eSBldmVudCBmb3IgYXVkaXQgdHJhaWxcbiAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgIHR5cGU6ICdESVJFQ1RPUllfTUlHUkFUSU9OJyxcbiAgICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgICAgc291cmNlOiAnUG9ydGZvbGlvTWFuYWdlci5taWdyYXRlRnJvbVNpbmd1bGFyRGlyZWN0b3JpZXMnLFxuICAgICAgICAgIGRldGFpbHM6IGBNaWdyYXRlZCBkaXJlY3RvcnkgZnJvbSAke29sZE5hbWV9IHRvICR7bmV3TmFtZX0gZm9yIHYxLjQuMyBjb21wYXRpYmlsaXR5YCxcbiAgICAgICAgICBtZXRhZGF0YTogeyBvbGREaXIsIG5ld0RpciB9XG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIC8vIE9sZCBkaXJlY3RvcnkgZG9lc24ndCBleGlzdCwgd2hpY2ggaXMgZmluZVxuICAgICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSAhPT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoYFtQb3J0Zm9saW9NYW5hZ2VyXSBFcnJvciBkdXJpbmcgbWlncmF0aW9uIG9mICR7b2xkTmFtZX06YCwgZXJyb3IpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG59Il19