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.

495 lines 69.7 kB
/** * 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