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.

302 lines 52.6 kB
import { logger } from '../../utils/logger.js'; import { createRelationship, RelationshipTypes } from '../types/RelationshipTypes.js'; import { parseElementId, parseElementIdStrict, formatElementId } from '../../utils/elementId.js'; export class SemanticRelationshipService { nlpScoring; relationshipManager; constructor({ nlpScoring, relationshipManager }) { this.nlpScoring = nlpScoring; this.relationshipManager = relationshipManager; } async calculate(index, config) { const startTime = Date.now(); const MAX_EXECUTION_TIME = config.performance.circuitBreakerTimeoutMs; const elementTexts = new Map(); const elementCount = Object.values(index.elements) .reduce((sum, elements) => sum + Object.keys(elements).length, 0); logger.debug('Starting semantic relationship calculation', { elementCount, maxForFullMatrix: config.performance.maxElementsForFullMatrix }); for (const [elementType, elements] of Object.entries(index.elements)) { for (const [name, element] of Object.entries(elements)) { if (Date.now() - startTime > MAX_EXECUTION_TIME) { logger.warn('Semantic relationship calculation timeout', { elapsed: Date.now() - startTime, processed: elementTexts.size }); return; } const textParts = [ element.core.name, element.core.description || '', ...(element.search?.keywords || []), ...(element.search?.tags || []), ...(element.search?.triggers || []) ]; const fullText = textParts.join(' '); const key = `${elementType}:${name}`; elementTexts.set(key, fullText); if (!element.semantic) { element.semantic = {}; } element.semantic.entropy = this.nlpScoring.calculateEntropy(fullText); element.semantic.unique_terms = fullText.split(/\s+/).filter(t => t.length > 1).length; } } const keys = Array.from(elementTexts.keys()); const MAX_SAFE_ELEMENTS = 50; const MAX_SAFE_COMPARISONS = 500; const safeConfig = { ...config, performance: { ...config.performance, maxElementsForFullMatrix: Math.min(config.performance.maxElementsForFullMatrix, MAX_SAFE_ELEMENTS), maxSimilarityComparisons: Math.min(config.performance.maxSimilarityComparisons, MAX_SAFE_COMPARISONS) } }; if (keys.length <= safeConfig.performance.maxElementsForFullMatrix) { await this.calculateFullMatrix(index, elementTexts, keys, safeConfig); } else { await this.calculateSampledRelationships(index, elementTexts, keys, safeConfig); } const duration = Date.now() - startTime; logger.info('Semantic relationships calculated', { elements: elementTexts.size, duration: `${duration}ms`, strategy: keys.length <= config.performance.maxElementsForFullMatrix ? 'full' : 'sampled', timedOut: duration > MAX_EXECUTION_TIME }); } async calculateFullMatrix(index, elementTexts, keys, config) { let comparisons = 0; const batchSize = config.performance.similarityBatchSize; const threshold = config.performance.similarityThreshold; const startTime = Date.now(); const MAX_EXECUTION_TIME = config.performance.circuitBreakerTimeoutMs; for (let i = 0; i < keys.length; i++) { if (Date.now() - startTime > MAX_EXECUTION_TIME) { logger.warn('Full matrix calculation timeout', { elapsed: Date.now() - startTime, processed: `${i}/${keys.length}`, comparisons }); return; } const key1 = keys[i]; const parsed1 = parseElementIdStrict(key1); const text1 = elementTexts.get(key1); const batch = []; for (let j = i + 1; j < keys.length && batch.length < batchSize; j++) { const key2 = keys[j]; const parsed2 = parseElementIdStrict(key2); batch.push({ key2, type2: parsed2.type, name2: parsed2.name }); } await Promise.all(batch.map(async ({ key2, type2, name2 }) => { const text2 = elementTexts.get(key2); const scoring = this.nlpScoring.scoreRelevance(text1, text2); if (scoring.combinedScore > threshold) { const element1 = index.elements[parsed1.type]?.[parsed1.name]; const element2 = index.elements[type2]?.[name2]; if (!element1 || !element2) return; this.storeRelationship(index, parsed1.type, parsed1.name, type2, name2, scoring); } })); comparisons += batch.length; if (comparisons % 100 === 0) { await new Promise(resolve => setImmediate(resolve)); } } } async calculateSampledRelationships(index, elementTexts, keys, config) { const threshold = config.performance.similarityThreshold; const maxComparisons = config.performance.maxSimilarityComparisons; logger.info('Using sampled relationship calculation', { elements: keys.length, maxComparisons }); if (keys.length === 0) { logger.debug('No elements to calculate relationships for'); return; } const keywordClusters = await this.buildKeywordClusters(index, keys); let comparisons = 0; const clusterBudgetRatio = 0.6; const clusterComparisons = Math.floor(maxComparisons * clusterBudgetRatio); for (const [, clusterKeys] of keywordClusters.entries()) { if (comparisons >= clusterComparisons) break; for (let i = 0; i < clusterKeys.length - 1; i++) { if (comparisons >= clusterComparisons) break; const key1 = clusterKeys[i]; const parsed1 = parseElementIdStrict(key1); const text1 = elementTexts.get(key1); const sampleSize = Math.min(Math.ceil(Math.sqrt(clusterKeys.length - i - 1)), config.sampling.clusterSampleLimit); const sampledIndices = this.randomSample(Array.from({ length: clusterKeys.length - i - 1 }, (_, j) => i + j + 1), sampleSize); for (const j of sampledIndices) { if (comparisons >= clusterComparisons) break; const key2 = clusterKeys[j]; const parsed2 = parseElementIdStrict(key2); const text2 = elementTexts.get(key2); const scoring = this.nlpScoring.scoreRelevance(text1, text2); comparisons++; if (scoring.combinedScore > threshold) { this.storeRelationship(index, parsed1.type, parsed1.name, parsed2.type, parsed2.name, scoring); } } } if (comparisons % 100 === 0) { await new Promise(resolve => setImmediate(resolve)); } } const crossTypeComparisons = maxComparisons - comparisons; if (comparisons >= maxComparisons || crossTypeComparisons <= 0) { logger.debug('Skipping cross-type sampling, comparison budget exhausted', { comparisons, maxComparisons }); return; } const elementsByType = new Map(); const typeCounts = new Map(); for (const key of keys) { const parsed = parseElementId(key); if (!parsed) continue; if (!elementsByType.has(parsed.type)) { elementsByType.set(parsed.type, []); typeCounts.set(parsed.type, 0); } elementsByType.get(parsed.type).push(key); typeCounts.set(parsed.type, typeCounts.get(parsed.type) + 1); } const totalElements = keys.length; const typeSampleSizes = new Map(); for (const [type, count] of typeCounts.entries()) { const proportion = count / totalElements; const allocatedComparisons = Math.max(1, Math.floor(crossTypeComparisons * proportion)); const sampleSize = Math.ceil(Math.sqrt(allocatedComparisons)); typeSampleSizes.set(type, sampleSize); } const maxKeysToProcess = Math.min(Math.ceil(Math.sqrt(keys.length)), Math.ceil(crossTypeComparisons / Math.max(1, typeSampleSizes.size))); const sampledKeys1 = this.randomSample(keys, maxKeysToProcess); for (const key1 of sampledKeys1) { if (comparisons >= maxComparisons) break; const parsed1 = parseElementIdStrict(key1); const text1 = elementTexts.get(key1); for (const [type, sampleSize] of typeSampleSizes.entries()) { if (comparisons >= maxComparisons) break; const keysOfType = elementsByType.get(type); if (!keysOfType || keysOfType.length === 0) continue; const sampledKeys2 = this.randomSample(keysOfType, sampleSize); for (const key2 of sampledKeys2) { if (comparisons >= maxComparisons) break; if (key1 === key2) continue; const parsed2 = parseElementIdStrict(key2); const text2 = elementTexts.get(key2); const scoring = this.nlpScoring.scoreRelevance(text1, text2); comparisons++; if (scoring.combinedScore > threshold) { this.storeRelationship(index, parsed1.type, parsed1.name, parsed2.type, parsed2.name, scoring); } } } } } async buildKeywordClusters(index, keys) { const keywordFrequency = new Map(); for (const key of keys) { const parsed = parseElementIdStrict(key); const element = index.elements[parsed.type]?.[parsed.name]; if (!element) continue; const keywords = [ ...(element.search?.keywords || []), ...(element.search?.tags || []), ...(element.search?.triggers || []) ]; for (const keyword of keywords) { const normalized = keyword.toLowerCase(); keywordFrequency.set(normalized, (keywordFrequency.get(normalized) || 0) + 1); } } const clusters = new Map(); for (const key of keys) { const parsed = parseElementIdStrict(key); const element = index.elements[parsed.type]?.[parsed.name]; if (!element) continue; const keywords = [ ...(element.search?.keywords || []), ...(element.search?.tags || []), ...(element.search?.triggers || []) ]; for (const keyword of keywords) { const normalized = keyword.toLowerCase(); const frequency = keywordFrequency.get(normalized) || 0; if (frequency < 2) continue; if (!clusters.has(normalized)) { clusters.set(normalized, []); } clusters.get(normalized).push(key); } } const significantClusters = new Map(); const maxFrequency = Math.floor(keys.length * 0.5); for (const [keyword, elementKeys] of clusters.entries()) { if (elementKeys.length >= 2 && elementKeys.length <= maxFrequency) { significantClusters.set(keyword, elementKeys); } } return significantClusters; } storeRelationship(index, type1, name1, type2, name2, scoring) { if (!index.elements[type1][name1].relationships) { index.elements[type1][name1].relationships = {}; } if (!index.elements[type1][name1].relationships.similar) { index.elements[type1][name1].relationships.similar = []; } const targetElement = formatElementId(type2, name2); const existing1 = index.elements[type1][name1].relationships.similar .find(r => r.element === targetElement); if (!existing1) { index.elements[type1][name1].relationships.similar.push(createRelationship(type2, name2, RelationshipTypes.SEMANTIC_SIMILARITY, scoring.combinedScore, { jaccard: scoring.jaccard, entropy_diff: Math.abs((index.elements[type1][name1].semantic?.entropy || 0) - (index.elements[type2][name2].semantic?.entropy || 0)) })); } if (!index.elements[type2][name2].relationships) { index.elements[type2][name2].relationships = {}; } if (!index.elements[type2][name2].relationships.similar) { index.elements[type2][name2].relationships.similar = []; } const sourceElement = formatElementId(type1, name1); const existing2 = index.elements[type2][name2].relationships.similar .find(r => r.element === sourceElement); if (!existing2) { index.elements[type2][name2].relationships.similar.push(createRelationship(type1, name1, RelationshipTypes.SEMANTIC_SIMILARITY, scoring.combinedScore, { jaccard: scoring.jaccard, entropy_diff: Math.abs((index.elements[type1][name1].semantic?.entropy || 0) - (index.elements[type2][name2].semantic?.entropy || 0)) })); } } randomSample(array, size) { const shuffled = [...array].sort(() => 0.5 - Math.random()); return shuffled.slice(0, size); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VtYW50aWNSZWxhdGlvbnNoaXBTZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BvcnRmb2xpby9lbmhhbmNlZC1pbmRleC9TZW1hbnRpY1JlbGF0aW9uc2hpcFNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9DLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBR3RGLE9BQU8sRUFBRSxjQUFjLEVBQUUsb0JBQW9CLEVBQUUsZUFBZSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFTakcsTUFBTSxPQUFPLDJCQUEyQjtJQUNyQixVQUFVLENBQW9CO0lBQzlCLG1CQUFtQixDQUFzQjtJQUUxRCxZQUFZLEVBQUUsVUFBVSxFQUFFLG1CQUFtQixFQUFtQztRQUM5RSxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQztRQUM3QixJQUFJLENBQUMsbUJBQW1CLEdBQUcsbUJBQW1CLENBQUM7SUFDakQsQ0FBQztJQUVNLEtBQUssQ0FBQyxTQUFTLENBQUMsS0FBb0IsRUFBRSxNQUEwQjtRQUNyRSxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0IsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDLHVCQUF1QixDQUFDO1FBRXRFLE1BQU0sWUFBWSxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO1FBQy9DLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQzthQUMvQyxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsUUFBUSxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFFcEUsTUFB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QUMsS0FBSyxDQUFDLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQ3hFLEtBQUssRUFDTCxLQUFLLEVBQ0wsaUJBQWlCLENBQUMsbUJBQW1CLEVBQ3JDLE9BQU8sQ0FBQyxhQUFhLEVBQ3JCO2dCQUNFLE9BQU8sRUFBRSxPQUFPLENBQUMsT0FBTztnQkFDeEIsWUFBWSxFQUFFLElBQUksQ0FBQyxHQUFHLENBQ3BCLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxRQUFRLEVBQUUsT0FBTyxJQUFJLENBQUMsQ0FBQztvQkFDckQsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsRUFBRSxPQUFPLElBQUksQ0FBQyxDQUFDLENBQ3REO2FBQ0YsQ0FDRixDQUFDLENBQUM7UUFDTCxDQUFDO0lBQ0gsQ0FBQztJQUVPLFlBQVksQ0FBSSxLQUFVLEVBQUUsSUFBWTtRQUM5QyxNQUFNLFFBQVEsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLEdBQUcsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUM1RCxPQUFPLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ2pDLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBjcmVhdGVSZWxhdGlvbnNoaXAsIFJlbGF0aW9uc2hpcFR5cGVzIH0gZnJvbSAnLi4vdHlwZXMvUmVsYXRpb25zaGlwVHlwZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBJbmRleENvbmZpZ3VyYXRpb24gfSBmcm9tICcuLi9jb25maWcvSW5kZXhDb25maWcuanMnO1xuaW1wb3J0IHR5cGUgeyBFbmhhbmNlZEluZGV4IH0gZnJvbSAnLi4vdHlwZXMvSW5kZXhUeXBlcy5qcyc7XG5pbXBvcnQgeyBwYXJzZUVsZW1lbnRJZCwgcGFyc2VFbGVtZW50SWRTdHJpY3QsIGZvcm1hdEVsZW1lbnRJZCB9IGZyb20gJy4uLy4uL3V0aWxzL2VsZW1lbnRJZC5qcyc7XG5pbXBvcnQgdHlwZSB7IE5MUFNjb3JpbmdNYW5hZ2VyIH0gZnJvbSAnLi4vTkxQU2NvcmluZ01hbmFnZXIuanMnO1xuaW1wb3J0IHR5cGUgeyBSZWxhdGlvbnNoaXBNYW5hZ2VyIH0gZnJvbSAnLi4vUmVsYXRpb25zaGlwTWFuYWdlci5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VtYW50aWNSZWxhdGlvbnNoaXBTZXJ2aWNlRGVwcyB7XG4gIG5scFNjb3Jpbmc6IE5MUFNjb3JpbmdNYW5hZ2VyO1xuICByZWxhdGlvbnNoaXBNYW5hZ2VyOiBSZWxhdGlvbnNoaXBNYW5hZ2VyO1xufVxuXG5leHBvcnQgY2xhc3MgU2VtYW50aWNSZWxhdGlvbnNoaXBTZXJ2aWNlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBubHBTY29yaW5nOiBOTFBTY29yaW5nTWFuYWdlcjtcbiAgcHJpdmF0ZSByZWFkb25seSByZWxhdGlvbnNoaXBNYW5hZ2VyOiBSZWxhdGlvbnNoaXBNYW5hZ2VyO1xuXG4gIGNvbnN0cnVjdG9yKHsgbmxwU2NvcmluZywgcmVsYXRpb25zaGlwTWFuYWdlciB9OiBTZW1hbnRpY1JlbGF0aW9uc2hpcFNlcnZpY2VEZXBzKSB7XG4gICAgdGhpcy5ubHBTY29yaW5nID0gbmxwU2NvcmluZztcbiAgICB0aGlzLnJlbGF0aW9uc2hpcE1hbmFnZXIgPSByZWxhdGlvbnNoaXBNYW5hZ2VyO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIGNhbGN1bGF0ZShpbmRleDogRW5oYW5jZWRJbmRleCwgY29uZmlnOiBJbmRleENvbmZpZ3VyYXRpb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIGNvbnN0IE1BWF9FWEVDVVRJT05fVElNRSA9IGNvbmZpZy5wZXJmb3JtYW5jZS5jaXJjdWl0QnJlYWtlclRpbWVvdXRNcztcblxuICAgIGNvbnN0IGVsZW1lbnRUZXh0cyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG4gICAgY29uc3QgZWxlbWVudENvdW50ID0gT2JqZWN0LnZhbHVlcyhpbmRleC5lbGVtZW50cylcbiAgICAgIC5yZWR1Y2UoKHN1bSwgZWxlbWVudHMpID0+IHN1bSArIE9iamVjdC5rZXlzKGVsZW1lbnRzKS5sZW5ndGgsIDApO1xuXG4gICAgbG9nZ2VyLmRlYnVnKCdTdGFydGluZyBzZW1hbnRpYyByZWxhdGlvbnNoaXAgY2FsY3VsYXRpb24nLCB7XG4gICAgICBlbGVtZW50Q291bnQsXG4gICAgICBtYXhGb3JGdWxsTWF0cml4OiBjb25maWcucGVyZm9ybWFuY2UubWF4RWxlbWVudHNGb3JGdWxsTWF0cml4XG4gICAgfSk7XG5cbiAgICBmb3IgKGNvbnN0IFtlbGVtZW50VHlwZSwgZWxlbWVudHNdIG9mIE9iamVjdC5lbnRyaWVzKGluZGV4LmVsZW1lbnRzKSkge1xuICAgICAgZm9yIChjb25zdCBbbmFtZSwgZWxlbWVudF0gb2YgT2JqZWN0LmVudHJpZXMoZWxlbWVudHMpKSB7XG4gICAgICAgIGlmIChEYXRlLm5vdygpIC0gc3RhcnRUaW1lID4gTUFYX0VYRUNVVElPTl9USU1FKSB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oJ1NlbWFudGljIHJlbGF0aW9uc2hpcCBjYWxjdWxhdGlvbiB0aW1lb3V0Jywge1xuICAgICAgICAgICAgZWxhcHNlZDogRGF0ZS5ub3coKSAtIHN0YXJ0VGltZSxcbiAgICAgICAgICAgIHByb2Nlc3NlZDogZWxlbWVudFRleHRzLnNpemVcbiAgICAgICAgICB9KTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB0ZXh0UGFydHMgPSBbXG4gICAgICAgICAgZWxlbWVudC5jb3JlLm5hbWUsXG4gICAgICAgICAgZWxlbWVudC5jb3JlLmRlc2NyaXB0aW9uIHx8ICcnLFxuICAgICAgICAgIC4uLihlbGVtZW50LnNlYXJjaD8ua2V5d29yZHMgfHwgW10pLFxuICAgICAgICAgIC4uLihlbGVtZW50LnNlYXJjaD8udGFncyB8fCBbXSksXG4gICAgICAgICAgLi4uKGVsZW1lbnQuc2VhcmNoPy50cmlnZ2VycyB8fCBbXSlcbiAgICAgICAgXTtcblxuICAgICAgICBjb25zdCBmdWxsVGV4dCA9IHRleHRQYXJ0cy5qb2luKCcgJyk7XG4gICAgICAgIGNvbnN0IGtleSA9IGAke2VsZW1lbnRUeXBlfToke25hbWV9YDtcbiAgICAgICAgZWxlbWVudFRleHRzLnNldChrZXksIGZ1bGxUZXh0KTtcblxuICAgICAgICBpZiAoIWVsZW1lbnQuc2VtYW50aWMpIHtcbiAgICAgICAgICBlbGVtZW50LnNlbWFudGljID0ge307XG4gICAgICAgIH1cbiAgICAgICAgZWxlbWVudC5zZW1hbnRpYy5lbnRyb3B5ID0gdGhpcy5ubHBTY29yaW5nLmNhbGN1bGF0ZUVudHJvcHkoZnVsbFRleHQpO1xuICAgICAgICBlbGVtZW50LnNlbWFudGljLnVuaXF1ZV90ZXJtcyA9IGZ1bGxUZXh0LnNwbGl0KC9cXHMrLykuZmlsdGVyKHQgPT4gdC5sZW5ndGggPiAxKS5sZW5ndGg7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3Qga2V5cyA9IEFycmF5LmZyb20oZWxlbWVudFRleHRzLmtleXMoKSk7XG4gICAgY29uc3QgTUFYX1NBRkVfRUxFTUVOVFMgPSA1MDtcbiAgICBjb25zdCBNQVhfU0FGRV9DT01QQVJJU09OUyA9IDUwMDtcblxuICAgIGNvbnN0IHNhZmVDb25maWcgPSB7XG4gICAgICAuLi5jb25maWcsXG4gICAgICBwZXJmb3JtYW5jZToge1xuICAgICAgICAuLi5jb25maWcucGVyZm9ybWFuY2UsXG4gICAgICAgIG1heEVsZW1lbnRzRm9yRnVsbE1hdHJpeDogTWF0aC5taW4oXG4gICAgICAgICAgY29uZmlnLnBlcmZvcm1hbmNlLm1heEVsZW1lbnRzRm9yRnVsbE1hdHJpeCxcbiAgICAgICAgICBNQVhfU0FGRV9FTEVNRU5UU1xuICAgICAgICApLFxuICAgICAgICBtYXhTaW1pbGFyaXR5Q29tcGFyaXNvbnM6IE1hdGgubWluKFxuICAgICAgICAgIGNvbmZpZy5wZXJmb3JtYW5jZS5tYXhTaW1pbGFyaXR5Q29tcGFyaXNvbnMsXG4gICAgICAgICAgTUFYX1NBRkVfQ09NUEFSSVNPTlNcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH0gYXMgSW5kZXhDb25maWd1cmF0aW9uO1xuXG4gICAgaWYgKGtleXMubGVuZ3RoIDw9IHNhZmVDb25maWcucGVyZm9ybWFuY2UubWF4RWxlbWVudHNGb3JGdWxsTWF0cml4KSB7XG4gICAgICBhd2FpdCB0aGlzLmNhbGN1bGF0ZUZ1bGxNYXRyaXgoaW5kZXgsIGVsZW1lbnRUZXh0cywga2V5cywgc2FmZUNvbmZpZyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IHRoaXMuY2FsY3VsYXRlU2FtcGxlZFJlbGF0aW9uc2hpcHMoaW5kZXgsIGVsZW1lbnRUZXh0cywga2V5cywgc2FmZUNvbmZpZyk7XG4gICAgfVxuXG4gICAgY29uc3QgZHVyYXRpb24gPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lO1xuICAgIGxvZ2dlci5pbmZvKCdTZW1hbnRpYyByZWxhdGlvbnNoaXBzIGNhbGN1bGF0ZWQnLCB7XG4gICAgICBlbGVtZW50czogZWxlbWVudFRleHRzLnNpemUsXG4gICAgICBkdXJhdGlvbjogYCR7ZHVyYXRpb259bXNgLFxuICAgICAgc3RyYXRlZ3k6IGtleXMubGVuZ3RoIDw9IGNvbmZpZy5wZXJmb3JtYW5jZS5tYXhFbGVtZW50c0ZvckZ1bGxNYXRyaXggPyAnZnVsbCcgOiAnc2FtcGxlZCcsXG4gICAgICB0aW1lZE91dDogZHVyYXRpb24gPiBNQVhfRVhFQ1VUSU9OX1RJTUVcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgY2FsY3VsYXRlRnVsbE1hdHJpeChcbiAgICBpbmRleDogRW5oYW5jZWRJbmRleCxcbiAgICBlbGVtZW50VGV4dHM6IE1hcDxzdHJpbmcsIHN0cmluZz4sXG4gICAga2V5czogc3RyaW5nW10sXG4gICAgY29uZmlnOiBJbmRleENvbmZpZ3VyYXRpb25cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IGNvbXBhcmlzb25zID0gMDtcbiAgICBjb25zdCBiYXRjaFNpemUgPSBjb25maWcucGVyZm9ybWFuY2Uuc2ltaWxhcml0eUJhdGNoU2l6ZTtcbiAgICBjb25zdCB0aHJlc2hvbGQgPSBjb25maWcucGVyZm9ybWFuY2Uuc2ltaWxhcml0eVRocmVzaG9sZDtcbiAgICBjb25zdCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIGNvbnN0IE1BWF9FWEVDVVRJT05fVElNRSA9IGNvbmZpZy5wZXJmb3JtYW5jZS5jaXJjdWl0QnJlYWtlclRpbWVvdXRNcztcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKERhdGUubm93KCkgLSBzdGFydFRpbWUgPiBNQVhfRVhFQ1VUSU9OX1RJTUUpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ0Z1bGwgbWF0cml4IGNhbGN1bGF0aW9uIHRpbWVvdXQnLCB7XG4gICAgICAgICAgZWxhcHNlZDogRGF0ZS5ub3coKSAtIHN0YXJ0VGltZSxcbiAgICAgICAgICBwcm9jZXNzZWQ6IGAke2l9LyR7a2V5cy5sZW5ndGh9YCxcbiAgICAgICAgICBjb21wYXJpc29uc1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBrZXkxID0ga2V5c1tpXTtcbiAgICAgIGNvbnN0IHBhcnNlZDEgPSBwYXJzZUVsZW1lbnRJZFN0cmljdChrZXkxKTtcbiAgICAgIGNvbnN0IHRleHQxID0gZWxlbWVudFRleHRzLmdldChrZXkxKSE7XG5cbiAgICAgIGNvbnN0IGJhdGNoOiBBcnJheTx7IGtleTI6IHN0cmluZzsgdHlwZTI6IHN0cmluZzsgbmFtZTI6IHN0cmluZyB9PiA9IFtdO1xuICAgICAgZm9yIChsZXQgaiA9IGkgKyAxOyBqIDwga2V5cy5sZW5ndGggJiYgYmF0Y2gubGVuZ3RoIDwgYmF0Y2hTaXplOyBqKyspIHtcbiAgICAgICAgY29uc3Qga2V5MiA9IGtleXNbal07XG4gICAgICAgIGNvbnN0IHBhcnNlZDIgPSBwYXJzZUVsZW1lbnRJZFN0cmljdChrZXkyKTtcbiAgICAgICAgYmF0Y2gucHVzaCh7IGtleTIsIHR5cGUyOiBwYXJzZWQyLnR5cGUsIG5hbWUyOiBwYXJzZWQyLm5hbWUgfSk7XG4gICAgICB9XG5cbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKGJhdGNoLm1hcChhc3luYyAoeyBrZXkyLCB0eXBlMiwgbmFtZTIgfSkgPT4ge1xuICAgICAgICBjb25zdCB0ZXh0MiA9IGVsZW1lbnRUZXh0cy5nZXQoa2V5MikhO1xuICAgICAgICBjb25zdCBzY29yaW5nID0gdGhpcy5ubHBTY29yaW5nLnNjb3JlUmVsZXZhbmNlKHRleHQxLCB0ZXh0Mik7XG5cbiAgICAgICAgaWYgKHNjb3JpbmcuY29tYmluZWRTY29yZSA+IHRocmVzaG9sZCkge1xuICAgICAgICAgIGNvbnN0IGVsZW1lbnQxID0gaW5kZXguZWxlbWVudHNbcGFyc2VkMS50eXBlXT8uW3BhcnNlZDEubmFtZV07XG4gICAgICAgICAgY29uc3QgZWxlbWVudDIgPSBpbmRleC5lbGVtZW50c1t0eXBlMl0/LltuYW1lMl07XG4gICAgICAgICAgaWYgKCFlbGVtZW50MSB8fCAhZWxlbWVudDIpIHJldHVybjtcbiAgICAgICAgICB0aGlzLnN0b3JlUmVsYXRpb25zaGlwKGluZGV4LCBwYXJzZWQxLnR5cGUsIHBhcnNlZDEubmFtZSwgdHlwZTIsIG5hbWUyLCBzY29yaW5nKTtcbiAgICAgICAgfVxuICAgICAgfSkpO1xuXG4gICAgICBjb21wYXJpc29ucyArPSBiYXRjaC5sZW5ndGg7XG5cbiAgICAgIGlmIChjb21wYXJpc29ucyAlIDEwMCA9PT0gMCkge1xuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldEltbWVkaWF0ZShyZXNvbHZlKSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBjYWxjdWxhdGVTYW1wbGVkUmVsYXRpb25zaGlwcyhcbiAgICBpbmRleDogRW5oYW5jZWRJbmRleCxcbiAgICBlbGVtZW50VGV4dHM6IE1hcDxzdHJpbmcsIHN0cmluZz4sXG4gICAga2V5czogc3RyaW5nW10sXG4gICAgY29uZmlnOiBJbmRleENvbmZpZ3VyYXRpb25cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdGhyZXNob2xkID0gY29uZmlnLnBlcmZvcm1hbmNlLnNpbWlsYXJpdHlUaHJlc2hvbGQ7XG4gICAgY29uc3QgbWF4Q29tcGFyaXNvbnMgPSBjb25maWcucGVyZm9ybWFuY2UubWF4U2ltaWxhcml0eUNvbXBhcmlzb25zO1xuXG4gICAgbG9nZ2VyLmluZm8oJ1VzaW5nIHNhbXBsZWQgcmVsYXRpb25zaGlwIGNhbGN1bGF0aW9uJywge1xuICAgICAgZWxlbWVudHM6IGtleXMubGVuZ3RoLFxuICAgICAgbWF4Q29tcGFyaXNvbnNcbiAgICB9KTtcblxuICAgIGlmIChrZXlzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdObyBlbGVtZW50cyB0byBjYWxjdWxhdGUgcmVsYXRpb25zaGlwcyBmb3InKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBrZXl3b3JkQ2x1c3RlcnMgPSBhd2FpdCB0aGlzLmJ1aWxkS2V5d29yZENsdXN0ZXJzKGluZGV4LCBrZXlzKTtcbiAgICBsZXQgY29tcGFyaXNvbnMgPSAwO1xuICAgIGNvbnN0IGNsdXN0ZXJCdWRnZXRSYXRpbyA9IDAuNjtcbiAgICBjb25zdCBjbHVzdGVyQ29tcGFyaXNvbnMgPSBNYXRoLmZsb29yKG1heENvbXBhcmlzb25zICogY2x1c3RlckJ1ZGdldFJhdGlvKTtcblxuICAgIGZvciAoY29uc3QgWywgY2x1c3RlcktleXNdIG9mIGtleXdvcmRDbHVzdGVycy5lbnRyaWVzKCkpIHtcbiAgICAgIGlmIChjb21wYXJpc29ucyA+PSBjbHVzdGVyQ29tcGFyaXNvbnMpIGJyZWFrO1xuXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNsdXN0ZXJLZXlzLmxlbmd0aCAtIDE7IGkrKykge1xuICAgICAgICBpZiAoY29tcGFyaXNvbnMgPj0gY2x1c3RlckNvbXBhcmlzb25zKSBicmVhaztcblxuICAgICAgICBjb25zdCBrZXkxID0gY2x1c3RlcktleXNbaV07XG4gICAgICAgIGNvbnN0IHBhcnNlZDEgPSBwYXJzZUVsZW1lbnRJZFN0cmljdChrZXkxKTtcbiAgICAgICAgY29uc3QgdGV4dDEgPSBlbGVtZW50VGV4dHMuZ2V0KGtleTEpITtcblxuICAgICAgICBjb25zdCBzYW1wbGVTaXplID0gTWF0aC5taW4oXG4gICAgICAgICAgTWF0aC5jZWlsKE1hdGguc3FydChjbHVzdGVyS2V5cy5sZW5ndGggLSBpIC0gMSkpLFxuICAgICAgICAgIGNvbmZpZy5zYW1wbGluZy5jbHVzdGVyU2FtcGxlTGltaXRcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCBzYW1wbGVkSW5kaWNlcyA9IHRoaXMucmFuZG9tU2FtcGxlKFxuICAgICAgICAgIEFycmF5LmZyb20oeyBsZW5ndGg6IGNsdXN0ZXJLZXlzLmxlbmd0aCAtIGkgLSAxIH0sIChfLCBqKSA9PiBpICsgaiArIDEpLFxuICAgICAgICAgIHNhbXBsZVNpemVcbiAgICAgICAgKTtcblxuICAgICAgICBmb3IgKGNvbnN0IGogb2Ygc2FtcGxlZEluZGljZXMpIHtcbiAgICAgICAgICBpZiAoY29tcGFyaXNvbnMgPj0gY2x1c3RlckNvbXBhcmlzb25zKSBicmVhaztcblxuICAgICAgICAgIGNvbnN0IGtleTIgPSBjbHVzdGVyS2V5c1tqXTtcbiAgICAgICAgICBjb25zdCBwYXJzZWQyID0gcGFyc2VFbGVtZW50SWRTdHJpY3Qoa2V5Mik7XG4gICAgICAgICAgY29uc3QgdGV4dDIgPSBlbGVtZW50VGV4dHMuZ2V0KGtleTIpITtcbiAgICAgICAgICBjb25zdCBzY29yaW5nID0gdGhpcy5ubHBTY29yaW5nLnNjb3JlUmVsZXZhbmNlKHRleHQxLCB0ZXh0Mik7XG4gICAgICAgICAgY29tcGFyaXNvbnMrKztcblxuICAgICAgICAgIGlmIChzY29yaW5nLmNvbWJpbmVkU2NvcmUgPiB0aHJlc2hvbGQpIHtcbiAgICAgICAgICAgIHRoaXMuc3RvcmVSZWxhdGlvbnNoaXAoaW5kZXgsIHBhcnNlZDEudHlwZSwgcGFyc2VkMS5uYW1lLCBwYXJzZWQyLnR5cGUsIHBhcnNlZDIubmFtZSwgc2NvcmluZyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChjb21wYXJpc29ucyAlIDEwMCA9PT0gMCkge1xuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldEltbWVkaWF0ZShyZXNvbHZlKSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgY3Jvc3NUeXBlQ29tcGFyaXNvbnMgPSBtYXhDb21wYXJpc29ucyAtIGNvbXBhcmlzb25zO1xuICAgIGlmIChjb21wYXJpc29ucyA+PSBtYXhDb21wYXJpc29ucyB8fCBjcm9zc1R5cGVDb21wYXJpc29ucyA8PSAwKSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ1NraXBwaW5nIGNyb3NzLXR5cGUgc2FtcGxpbmcsIGNvbXBhcmlzb24gYnVkZ2V0IGV4aGF1c3RlZCcsIHtcbiAgICAgICAgY29tcGFyaXNvbnMsXG4gICAgICAgIG1heENvbXBhcmlzb25zXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBlbGVtZW50c0J5VHlwZSA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmdbXT4oKTtcbiAgICBjb25zdCB0eXBlQ291bnRzID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcblxuICAgIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcbiAgICAgIGNvbnN0IHBhcnNlZCA9IHBhcnNlRWxlbWVudElkKGtleSk7XG4gICAgICBpZiAoIXBhcnNlZCkgY29udGludWU7XG4gICAgICBpZiAoIWVsZW1lbnRzQnlUeXBlLmhhcyhwYXJzZWQudHlwZSkpIHtcbiAgICAgICAgZWxlbWVudHNCeVR5cGUuc2V0KHBhcnNlZC50eXBlLCBbXSk7XG4gICAgICAgIHR5cGVDb3VudHMuc2V0KHBhcnNlZC50eXBlLCAwKTtcbiAgICAgIH1cbiAgICAgIGVsZW1lbnRzQnlUeXBlLmdldChwYXJzZWQudHlwZSkhLnB1c2goa2V5KTtcbiAgICAgIHR5cGVDb3VudHMuc2V0KHBhcnNlZC50eXBlLCB0eXBlQ291bnRzLmdldChwYXJzZWQudHlwZSkhICsgMSk7XG4gICAgfVxuXG4gICAgY29uc3QgdG90YWxFbGVtZW50cyA9IGtleXMubGVuZ3RoO1xuICAgIGNvbnN0IHR5cGVTYW1wbGVTaXplcyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG5cbiAgICBmb3IgKGNvbnN0IFt0eXBlLCBjb3VudF0gb2YgdHlwZUNvdW50cy5lbnRyaWVzKCkpIHtcbiAgICAgIGNvbnN0IHByb3BvcnRpb24gPSBjb3VudCAvIHRvdGFsRWxlbWVudHM7XG4gICAgICBjb25zdCBhbGxvY2F0ZWRDb21wYXJpc29ucyA9IE1hdGgubWF4KDEsIE1hdGguZmxvb3IoY3Jvc3NUeXBlQ29tcGFyaXNvbnMgKiBwcm9wb3J0aW9uKSk7XG4gICAgICBjb25zdCBzYW1wbGVTaXplID0gTWF0aC5jZWlsKE1hdGguc3FydChhbGxvY2F0ZWRDb21wYXJpc29ucykpO1xuICAgICAgdHlwZVNhbXBsZVNpemVzLnNldCh0eXBlLCBzYW1wbGVTaXplKTtcbiAgICB9XG5cbiAgICBjb25zdCBtYXhLZXlzVG9Qcm9jZXNzID0gTWF0aC5taW4oXG4gICAgICBNYXRoLmNlaWwoTWF0aC5zcXJ0KGtleXMubGVuZ3RoKSksXG4gICAgICBNYXRoLmNlaWwoY3Jvc3NUeXBlQ29tcGFyaXNvbnMgLyBNYXRoLm1heCgxLCB0eXBlU2FtcGxlU2l6ZXMuc2l6ZSkpXG4gICAgKTtcblxuICAgIGNvbnN0IHNhbXBsZWRLZXlzMSA9IHRoaXMucmFuZG9tU2FtcGxlKGtleXMsIG1heEtleXNUb1Byb2Nlc3MpO1xuXG4gICAgZm9yIChjb25zdCBrZXkxIG9mIHNhbXBsZWRLZXlzMSkge1xuICAgICAgaWYgKGNvbXBhcmlzb25zID49IG1heENvbXBhcmlzb25zKSBicmVhaztcblxuICAgICAgY29uc3QgcGFyc2VkMSA9IHBhcnNlRWxlbWVudElkU3RyaWN0KGtleTEpO1xuICAgICAgY29uc3QgdGV4dDEgPSBlbGVtZW50VGV4dHMuZ2V0KGtleTEpITtcblxuICAgICAgZm9yIChjb25zdCBbdHlwZSwgc2FtcGxlU2l6ZV0gb2YgdHlwZVNhbXBsZVNpemVzLmVudHJpZXMoKSkge1xuICAgICAgICBpZiAoY29tcGFyaXNvbnMgPj0gbWF4Q29tcGFyaXNvbnMpIGJyZWFrO1xuXG4gICAgICAgIGNvbnN0IGtleXNPZlR5cGUgPSBlbGVtZW50c0J5VHlwZS5nZXQodHlwZSk7XG4gICAgICAgIGlmICgha2V5c09mVHlwZSB8fCBrZXlzT2ZUeXBlLmxlbmd0aCA9PT0gMCkgY29udGludWU7XG5cbiAgICAgICAgY29uc3Qgc2FtcGxlZEtleXMyID0gdGhpcy5yYW5kb21TYW1wbGUoa2V5c09mVHlwZSwgc2FtcGxlU2l6ZSk7XG5cbiAgICAgICAgZm9yIChjb25zdCBrZXkyIG9mIHNhbXBsZWRLZXlzMikge1xuICAgICAgICAgIGlmIChjb21wYXJpc29ucyA+PSBtYXhDb21wYXJpc29ucykgYnJlYWs7XG4gICAgICAgICAgaWYgKGtleTEgPT09IGtleTIpIGNvbnRpbnVlO1xuXG4gICAgICAgICAgY29uc3QgcGFyc2VkMiA9IHBhcnNlRWxlbWVudElkU3RyaWN0KGtleTIpO1xuICAgICAgICAgIGNvbnN0IHRleHQyID0gZWxlbWVudFRleHRzLmdldChrZXkyKSE7XG4gICAgICAgICAgY29uc3Qgc2NvcmluZyA9IHRoaXMubmxwU2NvcmluZy5zY29yZVJlbGV2YW5jZSh0ZXh0MSwgdGV4dDIpO1xuICAgICAgICAgIGNvbXBhcmlzb25zKys7XG5cbiAgICAgICAgICBpZiAoc2NvcmluZy5jb21iaW5lZFNjb3JlID4gdGhyZXNob2xkKSB7XG4gICAgICAgICAgICB0aGlzLnN0b3JlUmVsYXRpb25zaGlwKGluZGV4LCBwYXJzZWQxLnR5cGUsIHBhcnNlZDEubmFtZSwgcGFyc2VkMi50eXBlLCBwYXJzZWQyLm5hbWUsIHNjb3JpbmcpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgYnVpbGRLZXl3b3JkQ2x1c3RlcnMoXG4gICAgaW5kZXg6IEVuaGFuY2VkSW5kZXgsXG4gICAga2V5czogc3RyaW5nW11cbiAgKTogUHJvbWlzZTxNYXA8c3RyaW5nLCBzdHJpbmdbXT4+IHtcbiAgICBjb25zdCBrZXl3b3JkRnJlcXVlbmN5ID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcblxuICAgIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcbiAgICAgIGNvbnN0IHBhcnNlZCA9IHBhcnNlRWxlbWVudElkU3RyaWN0KGtleSk7XG4gICAgICBjb25zdCBlbGVtZW50ID0gaW5kZXguZWxlbWVudHNbcGFyc2VkLnR5cGVdPy5bcGFyc2VkLm5hbWVdO1xuICAgICAgaWYgKCFlbGVtZW50KSBjb250aW51ZTtcblxuICAgICAgY29uc3Qga2V5d29yZHMgPSBbXG4gICAgICAgIC4uLihlbGVtZW50LnNlYXJjaD8ua2V5d29yZHMgfHwgW10pLFxuICAgICAgICAuLi4oZWxlbWVudC5zZWFyY2g/LnRhZ3MgfHwgW10pLFxuICAgICAgICAuLi4oZWxlbWVudC5zZWFyY2g/LnRyaWdnZXJzIHx8IFtdKVxuICAgICAgXTtcblxuICAgICAgZm9yIChjb25zdCBrZXl3b3JkIG9mIGtleXdvcmRzKSB7XG4gICAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBrZXl3b3JkLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGtleXdvcmRGcmVxdWVuY3kuc2V0KG5vcm1hbGl6ZWQsIChrZXl3b3JkRnJlcXVlbmN5LmdldChub3JtYWxpemVkKSB8fCAwKSArIDEpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IGNsdXN0ZXJzID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZ1tdPigpO1xuICAgIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcbiAgICAgIGNvbnN0IHBhcnNlZCA9IHBhcnNlRWxlbWVudElkU3RyaWN0KGtleSk7XG4gICAgICBjb25zdCBlbGVtZW50ID0gaW5kZXguZWxlbWVudHNbcGFyc2VkLnR5cGVdPy5bcGFyc2VkLm5hbWVdO1xuICAgICAgaWYgKCFlbGVtZW50KSBjb250aW51ZTtcblxuICAgICAgY29uc3Qga2V5d29yZHMgPSBbXG4gICAgICAgIC4uLihlbGVtZW50LnNlYXJjaD8ua2V5d29yZHMgfHwgW10pLFxuICAgICAgICAuLi4oZWxlbWVudC5zZWFyY2g/LnRhZ3MgfHwgW10pLFxuICAgICAgICAuLi4oZWxlbWVudC5zZWFyY2g/LnRyaWdnZXJzIHx8IFtdKVxuICAgICAgXTtcblxuICAgICAgZm9yIChjb25zdCBrZXl3b3JkIG9mIGtleXdvcmRzKSB7XG4gICAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBrZXl3b3JkLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGNvbnN0IGZyZXF1ZW5jeSA9IGtleXdvcmRGcmVxdWVuY3kuZ2V0KG5vcm1hbGl6ZWQpIHx8IDA7XG4gICAgICAgIGlmIChmcmVxdWVuY3kgPCAyKSBjb250aW51ZTtcblxuICAgICAgICBpZiAoIWNsdXN0ZXJzLmhhcyhub3JtYWxpemVkKSkge1xuICAgICAgICAgIGNsdXN0ZXJzLnNldChub3JtYWxpemVkLCBbXSk7XG4gICAgICAgIH1cbiAgICAgICAgY2x1c3RlcnMuZ2V0KG5vcm1hbGl6ZWQpIS5wdXNoKGtleSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3Qgc2lnbmlmaWNhbnRDbHVzdGVycyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmdbXT4oKTtcbiAgICBjb25zdCBtYXhGcmVxdWVuY3kgPSBNYXRoLmZsb29yKGtleXMubGVuZ3RoICogMC41KTtcblxuICAgIGZvciAoY29uc3QgW2tleXdvcmQsIGVsZW1lbnRLZXlzXSBvZiBjbHVzdGVycy5lbnRyaWVzKCkpIHtcbiAgICAgIGlmIChlbGVtZW50S2V5cy5sZW5ndGggPj0gMiAmJiBlbGVtZW50S2V5cy5sZW5ndGggPD0gbWF4RnJlcXVlbmN5KSB7XG4gICAgICAgIHNpZ25pZmljYW50Q2x1c3RlcnMuc2V0KGtleXdvcmQsIGVsZW1lbnRLZXlzKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gc2lnbmlmaWNhbnRDbHVzdGVycztcbiAgfVxuXG4gIHByaXZhdGUgc3RvcmVSZWxhdGlvbnNoaXAoXG4gICAgaW5kZXg6IEVuaGFuY2VkSW5kZXgsXG4gICAgdHlwZTE6IHN0cmluZyxcbiAgICBuYW1lMTogc3RyaW5nLFxuICAgIHR5cGUyOiBzdHJpbmcsXG4gICAgbmFtZTI6IHN0cmluZyxcbiAgICBzY29yaW5nOiBhbnlcbiAgKTogdm9pZCB7XG4gICAgaWYgKCFpbmRleC5lbGVtZW50c1t0eXBlMV1bbmFtZTFdLnJlbGF0aW9uc2hpcHMpIHtcbiAgICAgIGluZGV4LmVsZW1lbnRzW3R5cGUxXVtuYW1