@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.
495 lines • 69.7 kB
JavaScript
/**
* Relationship Manager - Discovers and manages cross-element relationships
*
* Implements GraphRAG-style relationship tracking between elements:
* - similar_to: Semantic similarity (Jaccard-based)
* - used_by / uses: Usage dependencies
* - prerequisite_for / depends_on: Learning paths
* - helps_debug / debugged_by: Debugging relationships
* - contradicts / supports: Conflicting or supporting elements
*
* Features:
* - Automatic relationship discovery from content
* - Graph traversal for relationship paths
* - Relationship strength scoring
* - Bidirectional relationship tracking
*
* FIXES IMPLEMENTED (Issue #1099):
* - Uses centralized element ID parsing utilities
* - Consistent ID format handling
*/
import { logger } from '../utils/logger.js';
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
import { parseElementId } from '../utils/elementId.js';
/**
* Relationship types and their inverse mappings
*/
export const RELATIONSHIP_TYPES = {
// Similarity relationships
similar_to: 'similar_to', // Bidirectional
// Usage relationships
uses: 'used_by',
used_by: 'uses',
// Dependency relationships
prerequisite_for: 'depends_on',
depends_on: 'prerequisite_for',
requires: 'required_by',
required_by: 'requires',
// Debugging relationships
helps_debug: 'debugged_by',
debugged_by: 'helps_debug',
// Support/conflict relationships
supports: 'supported_by',
supported_by: 'supports',
contradicts: 'contradicts', // Bidirectional
complements: 'complements', // Bidirectional
// Hierarchical relationships
parent_of: 'child_of',
child_of: 'parent_of',
contains: 'contained_by',
contained_by: 'contains',
// Temporal relationships
follows: 'preceded_by',
preceded_by: 'follows',
// Example relationships
example_of: 'has_example',
has_example: 'example_of'
};
export class RelationshipManager {
_nlpScoring;
verbTriggers;
config;
// Default patterns for relationship discovery
defaultPatterns = [
// Usage patterns
{ type: 'uses', pattern: /uses?\s+(\w+[-\w]*)/gi, confidence: 0.8 },
{ type: 'uses', pattern: /requires?\s+(\w+[-\w]*)/gi, confidence: 0.7 },
{ type: 'uses', pattern: /depends?\s+on\s+(\w+[-\w]*)/gi, confidence: 0.7 },
// Prerequisite patterns
{ type: 'prerequisite_for', pattern: /prerequisite\s+for\s+(\w+[-\w]*)/gi, confidence: 0.9 },
{ type: 'depends_on', pattern: /after\s+(\w+[-\w]*)/gi, confidence: 0.6 },
// Debug patterns
{ type: 'helps_debug', pattern: /debug(?:s|ging)?\s+(\w+[-\w]*)/gi, confidence: 0.7 },
{ type: 'helps_debug', pattern: /troubleshoot(?:s|ing)?\s+(\w+[-\w]*)/gi, confidence: 0.7 },
// Support patterns
{ type: 'supports', pattern: /supports?\s+(\w+[-\w]*)/gi, confidence: 0.8 },
{ type: 'complements', pattern: /complements?\s+(\w+[-\w]*)/gi, confidence: 0.8 },
{ type: 'contradicts', pattern: /contradicts?\s+(\w+[-\w]*)/gi, confidence: 0.9 },
// Example patterns
{ type: 'example_of', pattern: /example\s+of\s+(\w+[-\w]*)/gi, confidence: 0.9 },
{ type: 'has_example', pattern: /see\s+(\w+[-\w]*)\s+for\s+example/gi, confidence: 0.7 }
];
constructor(options) {
const { config = {}, verbTriggerManager, nlpScoring, } = options;
this.config = {
minConfidence: config.minConfidence || 0.5,
maxRelationshipsPerElement: config.maxRelationshipsPerElement || 20,
enableAutoDiscovery: config.enableAutoDiscovery !== false,
customPatterns: config.customPatterns || []
};
this._nlpScoring = nlpScoring;
this.verbTriggers = verbTriggerManager;
logger.debug('RelationshipManager initialized', { config: this.config });
}
/**
* Discover relationships for all elements in the index
*/
async discoverRelationships(index) {
const startTime = Date.now();
let relationshipsFound = 0;
// FIX: Add timeout to prevent infinite loops
const MAX_DISCOVERY_TIME = 3000; // 3 seconds max
logger.debug('Starting relationship discovery');
// First, ensure semantic relationships are calculated
// (This is already done in EnhancedIndexManager)
// Then discover other relationship types
for (const [type, elements] of Object.entries(index.elements)) {
for (const [name, element] of Object.entries(elements)) {
// FIX: Check timeout
if (Date.now() - startTime > MAX_DISCOVERY_TIME) {
logger.warn('Relationship discovery timeout', {
elapsed: Date.now() - startTime,
relationshipsFound
});
return;
}
const discovered = await this.discoverElementRelationships(element, `${type}:${name}`, index);
// Merge discovered relationships with existing ones
if (discovered.length > 0) {
if (!element.relationships) {
element.relationships = {};
}
for (const rel of discovered) {
const relType = rel.type?.split('_').join('_') || 'related';
if (!element.relationships[relType]) {
element.relationships[relType] = [];
}
// Check if relationship already exists
const existing = element.relationships[relType].find(r => r.element === rel.element);
if (!existing) {
element.relationships[relType].push(rel);
relationshipsFound++;
// Add inverse relationship if needed
await this.addInverseRelationship(rel, `${type}:${name}`, index);
}
}
}
}
}
const duration = Date.now() - startTime;
logger.info('Relationship discovery completed', {
duration: `${duration}ms`,
relationshipsFound
});
}
/**
* Discover relationships for a single element
*/
async discoverElementRelationships(element, elementId, index) {
const relationships = [];
if (!this.config.enableAutoDiscovery) {
return relationships;
}
// Combine text from various fields for analysis
let text = this.getElementText(element);
// FIX: Prevent ReDoS attacks by limiting input length
const MAX_TEXT_LENGTH = 50000; // 50KB limit
if (text.length > MAX_TEXT_LENGTH) {
logger.warn('Content too large for relationship extraction, truncating', {
originalLength: text.length,
elementId,
truncatedTo: MAX_TEXT_LENGTH
});
text = text.substring(0, MAX_TEXT_LENGTH);
}
// Apply patterns to discover relationships
const patterns = [...this.defaultPatterns, ...(this.config.customPatterns || [])];
for (const pattern of patterns) {
const matches = text.matchAll(pattern.pattern);
for (const match of matches) {
const targetName = match[1];
// Find matching element
const targetElement = this.findElementByName(targetName, index);
if (targetElement && targetElement !== elementId) {
relationships.push({
element: targetElement,
type: pattern.type,
strength: pattern.confidence,
metadata: {
discoveryMethod: 'pattern',
pattern: pattern.pattern.source
}
});
}
}
}
// Discover verb-based relationships (fixed: now passes index to avoid circular dependency)
const verbRelationships = this.discoverVerbRelationships(element, elementId, index);
relationships.push(...verbRelationships);
// Filter by confidence and limit
const filtered = relationships
.filter(r => (r.strength || 0) >= this.config.minConfidence)
.sort((a, b) => (b.strength || 0) - (a.strength || 0))
.slice(0, this.config.maxRelationshipsPerElement);
return filtered;
}
/**
* Discover relationships based on verb associations
*/
discoverVerbRelationships(_element, elementId, index) {
const relationships = [];
try {
// FIX: Use centralized element ID parsing
const parsed = parseElementId(elementId);
if (!parsed) {
return [];
}
// Get verbs associated with this element
const verbs = this.verbTriggers.getVerbsForElement(parsed.name, index);
for (const verb of verbs) {
// Find other elements with same verb
const matches = this.verbTriggers.getElementsForVerb(verb, index);
for (const match of matches) {
if (match.name !== parsed.name) {
// Determine relationship type based on verb category
const category = this.verbTriggers.getVerbCategory(verb);
let relType = 'similar_to';
switch (category) {
case 'debugging':
relType = 'helps_debug';
break;
case 'creation':
relType = 'complements';
break;
case 'explanation':
relType = 'supports';
break;
case 'analysis':
relType = 'complements';
break;
}
relationships.push({
element: `${match.type}:${match.name}`,
type: relType,
strength: match.confidence * 0.7, // Slightly lower confidence for verb-based
metadata: {
discoveryMethod: 'verb',
verb,
category
}
});
}
}
}
}
catch (error) {
// If verb trigger fails, just continue without verb-based relationships
logger.debug('Verb-based relationship discovery failed', { elementId, error });
}
return relationships;
}
/**
* Add inverse relationship if applicable
*/
async addInverseRelationship(relationship, sourceElement, index) {
const relType = relationship.type;
const inverseType = RELATIONSHIP_TYPES[relType];
if (!inverseType || inverseType === relType) {
// Bidirectional or no inverse
return;
}
// FIX: Use centralized element ID parsing
const parsed = parseElementId(relationship.element);
if (!parsed) {
return;
}
const targetElement = index.elements[parsed.type]?.[parsed.name];
if (!targetElement) {
return;
}
if (!targetElement.relationships) {
targetElement.relationships = {};
}
if (!targetElement.relationships[inverseType]) {
targetElement.relationships[inverseType] = [];
}
// Check if inverse already exists
const existing = targetElement.relationships[inverseType].find(r => r.element === sourceElement);
if (!existing) {
targetElement.relationships[inverseType].push({
element: sourceElement,
type: inverseType,
strength: relationship.strength,
metadata: {
...relationship.metadata,
inverse: true
}
});
}
}
/**
* Find element by partial name match
*/
findElementByName(name, index) {
const nameLower = name.toLowerCase().replaceAll(/[_-]/g, '');
for (const [type, elements] of Object.entries(index.elements)) {
for (const [elementName, element] of Object.entries(elements)) {
const elementNameLower = elementName.toLowerCase().replaceAll(/[_-]/g, '');
// Check exact match first
if (elementNameLower === nameLower) {
return `${type}:${elementName}`;
}
// Check if element name contains the search term
if (elementNameLower.includes(nameLower) || nameLower.includes(elementNameLower)) {
return `${type}:${elementName}`;
}
// Check display name
const displayNameLower = element.core.name?.toLowerCase().replaceAll(/[_-]/g, '');
if (displayNameLower && (displayNameLower === nameLower || displayNameLower.includes(nameLower))) {
return `${type}:${elementName}`;
}
}
}
return null;
}
/**
* Get combined text from element for analysis
*/
getElementText(element) {
const parts = [];
// Core fields
if (element.core.name)
parts.push(element.core.name);
if (element.core.description)
parts.push(element.core.description);
// Keywords and tags from search optimization
if (element.search?.keywords)
parts.push(...element.search.keywords);
if (element.search?.tags)
parts.push(...element.search.tags);
// Custom fields (might contain relevant text)
if (element.custom) {
const customText = this.extractTextFromObject(element.custom);
if (customText)
parts.push(customText);
}
// Normalize Unicode for security (DMCP-SEC-004)
const combinedText = parts.join(' ');
const validation = UnicodeValidator.normalize(combinedText);
if (validation.detectedIssues && validation.detectedIssues.length > 0) {
logger.warn('Unicode issues in relationship text', { issues: validation.detectedIssues });
}
return validation.normalizedContent;
}
/**
* Extract text from nested object
*/
extractTextFromObject(obj) {
const texts = [];
for (const value of Object.values(obj)) {
if (typeof value === 'string') {
texts.push(value);
}
else if (Array.isArray(value)) {
texts.push(...value.filter(v => typeof v === 'string'));
}
else if (typeof value === 'object' && value !== null) {
const nested = this.extractTextFromObject(value);
if (nested)
texts.push(nested);
}
}
return texts.join(' ');
}
/**
* Find shortest path between two elements
*/
findPath(fromElement, toElement, index, options = {}) {
const { maxDepth = 5, relationshipTypes, minStrength = 0, visited = new Set() } = options;
// BFS to find shortest path
const queue = [{
path: [fromElement],
relationships: [],
totalStrength: 1.0
}];
visited.add(fromElement);
while (queue.length > 0) {
const current = queue.shift();
if (current.path.length > maxDepth) {
continue;
}
const currentElement = current.path[current.path.length - 1];
if (currentElement === toElement) {
return current;
}
// FIX: Use centralized element ID parsing
const parsed = parseElementId(currentElement);
if (!parsed)
continue;
const element = index.elements[parsed.type]?.[parsed.name];
if (!element?.relationships) {
continue;
}
// Explore all relationships
for (const [relType, relations] of Object.entries(element.relationships)) {
// Filter by relationship types if specified
if (relationshipTypes && !relationshipTypes.includes(relType)) {
continue;
}
for (const rel of relations) {
// Filter by strength
if ((rel.strength || 0) < minStrength) {
continue;
}
if (!visited.has(rel.element)) {
visited.add(rel.element);
queue.push({
path: [...current.path, rel.element],
relationships: [...current.relationships, relType],
totalStrength: current.totalStrength * (rel.strength || 0.5)
});
}
}
}
}
return null;
}
/**
* Get all connected elements within a certain depth
*/
getConnectedElements(element, index, options = {}) {
const { maxDepth = 2, relationshipTypes, minStrength = 0 } = options;
const connected = new Map();
const visited = new Set([element]);
const queue = [{
path: [element],
relationships: [],
totalStrength: 1.0
}];
while (queue.length > 0) {
const current = queue.shift();
if (current.path.length > maxDepth + 1) {
continue;
}
const currentElement = current.path[current.path.length - 1];
// FIX: Use centralized element ID parsing
const parsed = parseElementId(currentElement);
if (!parsed)
continue;
const elementDef = index.elements[parsed.type]?.[parsed.name];
if (!elementDef?.relationships) {
continue;
}
for (const [relType, relations] of Object.entries(elementDef.relationships)) {
if (relationshipTypes && !relationshipTypes.includes(relType)) {
continue;
}
for (const rel of relations) {
if ((rel.strength || 0) < minStrength) {
continue;
}
if (!visited.has(rel.element)) {
visited.add(rel.element);
const path = {
path: [...current.path, rel.element],
relationships: [...current.relationships, relType],
totalStrength: current.totalStrength * (rel.strength || 0.5)
};
connected.set(rel.element, path);
if (current.path.length < maxDepth) {
queue.push(path);
}
}
}
}
}
return connected;
}
/**
* Get relationship statistics for the index
*/
getRelationshipStats(index) {
const stats = {
totalRelationships: 0,
elementsWithRelationships: 0
};
// Count by relationship type
for (const relType of Object.keys(RELATIONSHIP_TYPES)) {
stats[relType] = 0;
}
for (const elements of Object.values(index.elements)) {
for (const element of Object.values(elements)) {
if (element.relationships) {
stats.elementsWithRelationships++;
for (const [relType, relations] of Object.entries(element.relationships)) {
const count = relations.length;
stats.totalRelationships += count;
stats[relType] = (stats[relType] || 0) + count;
}
}
}
}
return stats;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVsYXRpb25zaGlwTWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wb3J0Zm9saW8vUmVsYXRpb25zaGlwTWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUk1QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUM5RSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFTdkQ7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRztJQUNoQywyQkFBMkI7SUFDM0IsVUFBVSxFQUFFLFlBQVksRUFBRyxnQkFBZ0I7SUFFM0Msc0JBQXNCO0lBQ3RCLElBQUksRUFBRSxTQUFTO0lBQ2YsT0FBTyxFQUFFLE1BQU07SUFFZiwyQkFBMkI7SUFDM0IsZ0JBQWdCLEVBQUUsWUFBWTtJQUM5QixVQUFVLEVBQUUsa0JBQWtCO0lBQzlCLFFBQVEsRUFBRSxhQUFhO0lBQ3ZCLFdBQVcsRUFBRSxVQUFVO0lBRXZCLDBCQUEwQjtJQUMxQixXQUFXLEVBQUUsYUFBYTtJQUMxQixXQUFXLEVBQUUsYUFBYTtJQUUxQixpQ0FBaUM7SUFDakMsUUFBUSxFQUFFLGNBQWM7SUFDeEIsWUFBWSxFQUFFLFVBQVU7SUFDeEIsV0FBVyxFQUFFLGFBQWEsRUFBRyxnQkFBZ0I7SUFDN0MsV0FBVyxFQUFFLGFBQWEsRUFBRyxnQkFBZ0I7SUFFN0MsNkJBQTZCO0lBQzdCLFNBQVMsRUFBRSxVQUFVO0lBQ3JCLFFBQVEsRUFBRSxXQUFXO0lBQ3JCLFFBQVEsRUFBRSxjQUFjO0lBQ3hCLFlBQVksRUFBRSxVQUFVO0lBRXhCLHlCQUF5QjtJQUN6QixPQUFPLEVBQUUsYUFBYTtJQUN0QixXQUFXLEVBQUUsU0FBUztJQUV0Qix3QkFBd0I7SUFDeEIsVUFBVSxFQUFF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LElBQUksQ0FBQyxDQUFDLEdBQUcsS0FBSyxDQUFDO29CQUNqRCxDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSZWxhdGlvbnNoaXAgTWFuYWdlciAtIERpc2NvdmVycyBhbmQgbWFuYWdlcyBjcm9zcy1lbGVtZW50IHJlbGF0aW9uc2hpcHNcbiAqXG4gKiBJbXBsZW1lbnRzIEdyYXBoUkFHLXN0eWxlIHJlbGF0aW9uc2hpcCB0cmFja2luZyBiZXR3ZWVuIGVsZW1lbnRzOlxuICogLSBzaW1pbGFyX3RvOiBTZW1hbnRpYyBzaW1pbGFyaXR5IChKYWNjYXJkLWJhc2VkKVxuICogLSB1c2VkX2J5IC8gdXNlczogVXNhZ2UgZGVwZW5kZW5jaWVzXG4gKiAtIHByZXJlcXVpc2l0ZV9mb3IgLyBkZXBlbmRzX29uOiBMZWFybmluZyBwYXRoc1xuICogLSBoZWxwc19kZWJ1ZyAvIGRlYnVnZ2VkX2J5OiBEZWJ1Z2dpbmcgcmVsYXRpb25zaGlwc1xuICogLSBjb250cmFkaWN0cyAvIHN1cHBvcnRzOiBDb25mbGljdGluZyBvciBzdXBwb3J0aW5nIGVsZW1lbnRzXG4gKlxuICogRmVhdHVyZXM6XG4gKiAtIEF1dG9tYXRpYyByZWxhdGlvbnNoaXAgZGlzY292ZXJ5IGZyb20gY29udGVudFxuICogLSBHcmFwaCB0cmF2ZXJzYWwgZm9yIHJlbGF0aW9uc2hpcCBwYXRoc1xuICogLSBSZWxhdGlvbnNoaXAgc3RyZW5ndGggc2NvcmluZ1xuICogLSBCaWRpcmVjdGlvbmFsIHJlbGF0aW9uc2hpcCB0cmFja2luZ1xuICpcbiAqIEZJWEVTIElNUExFTUVOVEVEIChJc3N1ZSAjMTA5OSk6XG4gKiAtIFVzZXMgY2VudHJhbGl6ZWQgZWxlbWVudCBJRCBwYXJzaW5nIHV0aWxpdGllc1xuICogLSBDb25zaXN0ZW50IElEIGZvcm1hdCBoYW5kbGluZ1xuICovXG5cbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBFbmhhbmNlZEluZGV4LCBFbGVtZW50RGVmaW5pdGlvbiB9IGZyb20gJy4vdHlwZXMvSW5kZXhUeXBlcy5qcyc7XG5pbXBvcnQgeyBOTFBTY29yaW5nTWFuYWdlciB9IGZyb20gJy4vTkxQU2NvcmluZ01hbmFnZXIuanMnO1xuaW1wb3J0IHsgVmVyYlRyaWdnZXJNYW5hZ2VyIH0gZnJvbSAnLi9WZXJiVHJpZ2dlck1hbmFnZXIuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBwYXJzZUVsZW1lbnRJZCB9IGZyb20gJy4uL3V0aWxzL2VsZW1lbnRJZC5qcyc7XG5pbXBvcnQgeyBJbmRleENvbmZpZ01hbmFnZXIgfSBmcm9tICcuL2NvbmZpZy9JbmRleENvbmZpZy5qcyc7XG5pbXBvcnQge1xuICBCYXNlUmVsYXRpb25zaGlwXG59IGZyb20gJy4vdHlwZXMvUmVsYXRpb25zaGlwVHlwZXMuanMnO1xuXG4vLyBVc2UgQmFzZVJlbGF0aW9uc2hpcCBpbnN0ZWFkIG9mIGltcG9ydGluZyBSZWxhdGlvbnNoaXAgZnJvbSBFbmhhbmNlZEluZGV4TWFuYWdlclxudHlwZSBSZWxhdGlvbnNoaXAgPSBCYXNlUmVsYXRpb25zaGlwO1xuXG4vKipcbiAqIFJlbGF0aW9uc2hpcCB0eXBlcyBhbmQgdGhlaXIgaW52ZXJzZSBtYXBwaW5nc1xuICovXG5leHBvcnQgY29uc3QgUkVMQVRJT05TSElQX1RZUEVTID0ge1xuICAvLyBTaW1pbGFyaXR5IHJlbGF0aW9uc2hpcHNcbiAgc2ltaWxhcl90bzogJ3NpbWlsYXJfdG8nLCAgLy8gQmlkaXJlY3Rpb25hbFxuXG4gIC8vIFVzYWdlIHJlbGF0aW9uc2hpcHNcbiAgdXNlczogJ3VzZWRfYnknLFxuICB1c2VkX2J5OiAndXNlcycsXG5cbiAgLy8gRGVwZW5kZW5jeSByZWxhdGlvbnNoaXBzXG4gIHByZXJlcXVpc2l0ZV9mb3I6ICdkZXBlbmRzX29uJyxcbiAgZGVwZW5kc19vbjogJ3ByZXJlcXVpc2l0ZV9mb3InLFxuICByZXF1aXJlczogJ3JlcXVpcmVkX2J5JyxcbiAgcmVxdWlyZWRfYnk6ICdyZXF1aXJlcycsXG5cbiAgLy8gRGVidWdnaW5nIHJlbGF0aW9uc2hpcHNcbiAgaGVscHNfZGVidWc6ICdkZWJ1Z2dlZF9ieScsXG4gIGRlYnVnZ2VkX2J5OiAnaGVscHNfZGVidWcnLFxuXG4gIC8vIFN1cHBvcnQvY29uZmxpY3QgcmVsYXRpb25zaGlwc1xuICBzdXBwb3J0czogJ3N1cHBvcnRlZF9ieScsXG4gIHN1cHBvcnRlZF9ieTogJ3N1cHBvcnRzJyxcbiAgY29udHJhZGljdHM6ICdjb250cmFkaWN0cycsICAvLyBCaWRpcmVjdGlvbmFsXG4gIGNvbXBsZW1lbnRzOiAnY29tcGxlbWVudHMnLCAgLy8gQmlkaXJlY3Rpb25hbFxuXG4gIC8vIEhpZXJhcmNoaWNhbCByZWxhdGlvbnNoaXBzXG4gIHBhcmVudF9vZjogJ2NoaWxkX29mJyxcbiAgY2hpbGRfb2Y6ICdwYXJlbnRfb2YnLFxuICBjb250YWluczogJ2NvbnRhaW5lZF9ieScsXG4gIGNvbnRhaW5lZF9ieTogJ2NvbnRhaW5zJyxcblxuICAvLyBUZW1wb3JhbCByZWxhdGlvbnNoaXBzXG4gIGZvbGxvd3M6ICdwcmVjZWRlZF9ieScsXG4gIHByZWNlZGVkX2J5OiAnZm9sbG93cycsXG5cbiAgLy8gRXhhbXBsZSByZWxhdGlvbnNoaXBzXG4gIGV4YW1wbGVfb2Y6ICdoYXNfZXhhbXBsZScsXG4gIGhhc19leGFtcGxlOiAnZXhhbXBsZV9vZidcbn0gYXMgY29uc3Q7XG5cbmV4cG9ydCB0eXBlIFJlbGF0aW9uc2hpcFR5cGUgPSBrZXlvZiB0eXBlb2YgUkVMQVRJT05TSElQX1RZUEVTO1xuXG4vKipcbiAqIFJlbGF0aW9uc2hpcCBkaXNjb3ZlcnkgY29uZmlndXJhdGlvblxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlbGF0aW9uc2hpcENvbmZpZyB7XG4gIC8vIE1pbmltdW0gY29uZmlkZW5jZSB0byBlc3RhYmxpc2ggcmVsYXRpb25zaGlwXG4gIG1pbkNvbmZpZGVuY2U/OiBudW1iZXI7XG5cbiAgLy8gTWF4aW11bSByZWxhdGlvbnNoaXBzIHBlciBlbGVtZW50XG4gIG1heFJlbGF0aW9uc2hpcHNQZXJFbGVtZW50PzogbnVtYmVyO1xuXG4gIC8vIEVuYWJsZSBhdXRvbWF0aWMgZGlzY292ZXJ5XG4gIGVuYWJsZUF1dG9EaXNjb3Zlcnk/OiBib29sZWFuO1xuXG4gIC8vIEN1c3RvbSByZWxhdGlvbnNoaXAgcGF0dGVybnNcbiAgY3VzdG9tUGF0dGVybnM/OiBSZWxhdGlvbnNoaXBQYXR0ZXJuW107XG59XG5cbi8qKlxuICogUGF0dGVybiBmb3IgZGlzY292ZXJpbmcgcmVsYXRpb25zaGlwc1xuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlbGF0aW9uc2hpcFBhdHRlcm4ge1xuICB0eXBlOiBSZWxhdGlvbnNoaXBUeXBlO1xuICBwYXR0ZXJuOiBSZWdFeHA7XG4gIGNvbmZpZGVuY2U6IG51bWJlcjtcbiAgYmlkaXJlY3Rpb25hbD86IGJvb2xlYW47XG59XG5cbi8qKlxuICogR3JhcGggdHJhdmVyc2FsIG9wdGlvbnNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUcmF2ZXJzYWxPcHRpb25zIHtcbiAgbWF4RGVwdGg/OiBudW1iZXI7XG4gIHJlbGF0aW9uc2hpcFR5cGVzPzogUmVsYXRpb25zaGlwVHlwZVtdO1xuICBtaW5TdHJlbmd0aD86IG51bWJlcjtcbiAgdmlzaXRlZD86IFNldDxzdHJpbmc+O1xufVxuXG4vKipcbiAqIFBhdGggYmV0d2VlbiBlbGVtZW50c1xuICovXG5leHBvcnQgaW50ZXJmYWNlIEVsZW1lbnRQYXRoIHtcbiAgcGF0aDogc3RyaW5nW107XG4gIHJlbGF0aW9uc2hpcHM6IFJlbGF0aW9uc2hpcFR5cGVbXTtcbiAgdG90YWxTdHJlbmd0aDogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlbGF0aW9uc2hpcE1hbmFnZXJPcHRpb25zIHtcbiAgY29uZmlnPzogUmVsYXRpb25zaGlwQ29uZmlnO1xuICBpbmRleENvbmZpZ01hbmFnZXI6IEluZGV4Q29uZmlnTWFuYWdlcjtcbiAgdmVyYlRyaWdnZXJNYW5hZ2VyOiBWZXJiVHJpZ2dlck1hbmFnZXI7XG4gIG5scFNjb3Jpbmc6IE5MUFNjb3JpbmdNYW5hZ2VyO1xufVxuXG5leHBvcnQgY2xhc3MgUmVsYXRpb25zaGlwTWFuYWdlciB7XG4gIHByaXZhdGUgX25scFNjb3Jpbmc6IE5MUFNjb3JpbmdNYW5hZ2VyO1xuICBwcml2YXRlIHZlcmJUcmlnZ2VyczogVmVyYlRyaWdnZXJNYW5hZ2VyO1xuICBwcml2YXRlIGNvbmZpZzogUmVsYXRpb25zaGlwQ29uZmlnO1xuXG4gIC8vIERlZmF1bHQgcGF0dGVybnMgZm9yIHJlbGF0aW9uc2hpcCBkaXNjb3ZlcnlcbiAgcHJpdmF0ZSByZWFkb25seSBkZWZhdWx0UGF0dGVybnM6IFJlbGF0aW9uc2hpcFBhdHRlcm5bXSA9IFtcbiAgICAvLyBVc2FnZSBwYXR0ZXJuc1xuICAgIHsgdHlwZTogJ3VzZXMnLCBwYXR0ZXJuOiAvdXNlcz9cXHMrKFxcdytbLVxcd10qKS9naSwgY29uZmlkZW5jZTogMC44IH0sXG4gICAgeyB0eXBlOiAndXNlcycsIHBhdHRlcm46IC9yZXF1aXJlcz9cXHMrKFxcdytbLVxcd10qKS9naSwgY29uZmlkZW5jZTogMC43IH0sXG4gICAgeyB0eXBlOiAndXNlcycsIHBhdHRlcm46IC9kZXBlbmRzP1xccytvblxccysoXFx3K1stXFx3XSopL2dpLCBjb25maWRlbmNlOiAwLjcgfSxcblxuICAgIC8vIFByZXJlcXVpc2l0ZSBwYXR0ZXJuc1xuICAgIHsgdHlwZTogJ3ByZXJlcXVpc2l0ZV9mb3InLCBwYXR0ZXJuOiAvcHJlcmVxdWlzaXRlXFxzK2ZvclxccysoXFx3K1stXFx3XSopL2dpLCBjb25maWRlbmNlOiAwLjkgfSxcbiAgICB7IHR5cGU6ICdkZXBlbmRzX29uJywgcGF0dGVybjogL2FmdGVyXFxzKyhcXHcrWy1cXHddKikvZ2ksIGNvbmZpZGVuY2U6IDAuNiB9LFxuXG4gICAgLy8gRGVidWcgcGF0dGVybnNcbiAgICB7IHR5cGU6ICdoZWxwc19kZWJ1ZycsIHBhdHRlcm46IC9kZWJ1Zyg/OnN8Z2luZyk/XFxzKyhcXHcrWy1cXHddKikvZ2ksIGNvbmZpZGVuY2U6IDAuNyB9LFxuICAgIHsgdHlwZTogJ2hlbHBzX2RlYnVnJywgcGF0dGVybjogL3Ryb3VibGVzaG9vdCg/OnN8aW5nKT9cXHMrKFxcdytbLVxcd10qKS9naSwgY29uZmlkZW5jZTogMC43IH0sXG5cbiAgICAvLyBTdXBwb3J0IHBhdHRlcm5zXG4gICAgeyB0eXBlOiAnc3VwcG9ydHMnLCBwYXR0ZXJuOiAvc3VwcG9ydHM/XFxzKyhcXHcrWy1cXHddKikvZ2ksIGNvbmZpZGVuY2U6IDAuOCB9LFxuICAgIHsgdHlwZTogJ2NvbXBsZW1lbnRzJywgcGF0dGVybjogL2NvbXBsZW1lbnRzP1xccysoXFx3K1stXFx3XSopL2dpLCBjb25maWRlbmNlOiAwLjggfSxcbiAgICB7IHR5cGU6ICdjb250cmFkaWN0cycsIHBhdHRlcm46IC9jb250cmFkaWN0cz9cXHMrKFxcdytbLVxcd10qKS9naSwgY29uZmlkZW5jZTogMC45IH0sXG5cbiAgICAvLyBFeGFtcGxlIHBhdHRlcm5zXG4gICAgeyB0eXBlOiAnZXhhbXBsZV9vZicsIHBhdHRlcm46IC9leGFtcGxlXFxzK29mXFxzKyhcXHcrWy1cXHddKikvZ2ksIGNvbmZpZGVuY2U6IDAuOSB9LFxuICAgIHsgdHlwZTogJ2hhc19leGFtcGxlJywgcGF0dGVybjogL3NlZVxccysoXFx3K1stXFx3XSopXFxzK2ZvclxccytleGFtcGxlL2dpLCBjb25maWRlbmNlOiAwLjcgfVxuICBdO1xuXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM6IFJlbGF0aW9uc2hpcE1hbmFnZXJPcHRpb25zKSB7XG4gICAgY29uc3Qge1xuICAgICAgY29uZmlnID0ge30sXG4gICAgICB2ZXJiVHJpZ2dlck1hbmFnZXIsXG4gICAgICBubHBTY29yaW5nLFxuICAgIH0gPSBvcHRpb25zO1xuXG4gICAgdGhpcy5jb25maWcgPSB7XG4gICAgICBtaW5Db25maWRlbmNlOiBjb25maWcubWluQ29uZmlkZW5jZSB8fCAwLjUsXG4gICAgICBtYXhSZWxhdGlvbnNoaXBzUGVyRWxlbWVudDogY29uZmlnLm1heFJlbGF0aW9uc2hpcHNQZXJFbGVtZW50IHx8IDIwLFxuICAgICAgZW5hYmxlQXV0b0Rpc2NvdmVyeTogY29uZmlnLmVuYWJsZUF1dG9EaXNjb3ZlcnkgIT09IGZhbHNlLFxuICAgICAgY3VzdG9tUGF0dGVybnM6IGNvbmZpZy5jdXN0b21QYXR0ZXJucyB8fCBbXVxuICAgIH07XG5cbiAgICB0aGlzLl9ubHBTY29yaW5nID0gbmxwU2NvcmluZztcbiAgICB0aGlzLnZlcmJUcmlnZ2VycyA9IHZlcmJUcmlnZ2VyTWFuYWdlcjtcblxuICAgIGxvZ2dlci5kZWJ1ZygnUmVsYXRpb25zaGlwTWFuYWdlciBpbml0aWFsaXplZCcsIHsgY29uZmlnOiB0aGlzLmNvbmZpZyB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNjb3ZlciByZWxhdGlvbnNoaXBzIGZvciBhbGwgZWxlbWVudHMgaW4gdGhlIGluZGV4XG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZGlzY292ZXJSZWxhdGlvbnNoaXBzKGluZGV4OiBFbmhhbmNlZEluZGV4KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBsZXQgcmVsYXRpb25zaGlwc0ZvdW5kID0gMDtcblxuICAgIC8vIEZJWDogQWRkIHRpbWVvdXQgdG8gcHJldmVudCBpbmZpbml0ZSBsb29wc1xuICAgIGNvbnN0IE1BWF9ESVNDT1ZFUllfVElNRSA9IDMwMDA7IC8vIDMgc2Vjb25kcyBtYXhcblxuICAgIGxvZ2dlci5kZWJ1ZygnU3RhcnRpbmcgcmVsYXRpb25zaGlwIGRpc2NvdmVyeScpO1xuXG4gICAgLy8gRmlyc3QsIGVuc3VyZSBzZW1hbnRpYyByZWxhdGlvbnNoaXBzIGFyZSBjYWxjdWxhdGVkXG4gICAgLy8gKFRoaXMgaXMgYWxyZWFkeSBkb25lIGluIEVuaGFuY2VkSW5kZXhNYW5hZ2VyKVxuXG4gICAgLy8gVGhlbiBkaXNjb3ZlciBvdGhlciByZWxhdGlvbnNoaXAgdHlwZXNcbiAgICBmb3IgKGNvbnN0IFt0eXBlLCBlbGVtZW50c10gb2YgT2JqZWN0LmVudHJpZXMoaW5kZXguZWxlbWVudHMpKSB7XG4gICAgICBmb3IgKGNvbnN0IFtuYW1lLCBlbGVtZW50XSBvZiBPYmplY3QuZW50cmllcyhlbGVtZW50cykpIHtcbiAgICAgICAgLy8gRklYOiBDaGVjayB0aW1lb3V0XG4gICAgICAgIGlmIChEYXRlLm5vdygpIC0gc3RhcnRUaW1lID4gTUFYX0RJU0NPVkVSWV9USU1FKSB7XG4gICAgICAgICAgbG9nZ2V