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