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.

409 lines β€’ 60.4 kB
/** * Enhanced Index handler for semantic search and relationships * * Implements the MCP tool handlers for Enhanced Index functionality * including similarity search, relationship discovery, and verb-based search. * * FIXES IMPLEMENTED (Issue #1099): * - Uses centralized element ID parsing utilities * - Consistent ID format handling * - Better error handling for invalid IDs * * Uses dependency injection for all services: * - EnhancedIndexManager for semantic search and relationships * - PersonaIndicatorService for persona indicator formatting */ import { ErrorHandler } from '../utils/ErrorHandler.js'; import { SecureErrorHandler } from '../security/errorHandler.js'; import { logger } from '../utils/logger.js'; import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; import { SecurityMonitor } from '../security/securityMonitor.js'; import { parseElementIdWithFallback, formatElementId } from '../utils/elementId.js'; import { parseRelationship, isParsedRelationship } from '../portfolio/types/RelationshipTypes.js'; import { normalizeElementTypeInput } from './element-crud/helpers.js'; export class EnhancedIndexHandler { enhancedIndexManager; indicatorService; constructor(enhancedIndexManager, indicatorService) { this.enhancedIndexManager = enhancedIndexManager; this.indicatorService = indicatorService; } /** * Find semantically similar elements using NLP scoring */ async findSimilarElements(options) { try { // Validate inputs if (!options.elementName || typeof options.elementName !== 'string') { throw new Error('Element name is required and must be a string'); } // FIX: DMCP-SEC-004 - Normalize Unicode in user input to prevent homograph attacks const normalized = UnicodeValidator.normalize(options.elementName); if (!normalized.isValid) { throw new Error(`Invalid element name: ${normalized.detectedIssues?.join(', ')}`); } options.elementName = normalized.normalizedContent; // Also normalize element type if provided if (options.elementType) { const normalizedType = UnicodeValidator.normalize(options.elementType); if (!normalizedType.isValid) { throw new Error(`Invalid element type: ${normalizedType.detectedIssues?.join(', ')}`); } options.elementType = normalizedType.normalizedContent; } if (options.limit <= 0 || options.limit > 100) { options.limit = 5; // Default to reasonable limit } if (options.threshold < 0 || options.threshold > 1) { options.threshold = 0.3; // Default to reasonable threshold } // Ensure the enhanced index is available with error handling try { await this.enhancedIndexManager.getIndex(); } catch (indexError) { logger.error('Failed to get Enhanced Index', indexError); // Try to recover by forcing rebuild try { await this.enhancedIndexManager.getIndex({ forceRebuild: true }); } catch { throw new Error('Enhanced Index is unavailable. Please try again later.'); } } // FIX: DMCP-SEC-006 - Add security audit logging for index operations SecurityMonitor.logSecurityEvent({ type: 'MEMORY_SEARCHED', severity: 'LOW', source: 'EnhancedIndexHandler.findSimilarElements', details: `Similarity search for: ${options.elementName}`, additionalData: { elementType: options.elementType, limit: options.limit, threshold: options.threshold } }); // Find the element const elementId = options.elementType ? `${options.elementType}/${options.elementName}` : options.elementName; // Get connected elements (similar/related) const connectedMap = await this.enhancedIndexManager.getConnectedElements(elementId, { maxDepth: 1, // Direct relationships only minStrength: options.threshold }); // Convert to array and sort by relationship strength const similarElements = Array.from(connectedMap.entries()) .map(([id, path]) => { const [type, name] = id.split('/'); return { type, name, score: path.totalStrength || 0, relationships: path.relationships || [] // relationships is already an array of strings }; }) .sort((a, b) => b.score - a.score) .slice(0, options.limit); // Format results let text = `${this.indicatorService.getPersonaIndicator()}πŸ” **Similar Elements**\n\n`; text += `**Reference**: ${options.elementName}\n`; if (options.elementType) { text += `**Type**: ${options.elementType}\n`; } text += `**Found**: ${similarElements.length} similar elements\n\n`; if (similarElements.length === 0) { text += `No similar elements found with similarity score >= ${options.threshold}\n`; } else { for (const element of similarElements) { const icon = this.getElementIcon(element.type); text += `${icon} **${element.name}** (${element.type})\n`; text += ` πŸ“Š Similarity: ${(element.score * 100).toFixed(1)}%\n`; if (element.relationships && element.relationships.length > 0) { text += ` πŸ”— Relationships: ${element.relationships.join(', ')}\n`; } text += '\n'; } } return { content: [{ type: "text", text }] }; } catch (error) { ErrorHandler.logError('EnhancedIndexHandler.findSimilarElements', error, options); return { content: [{ type: "text", text: `${this.indicatorService.getPersonaIndicator()}❌ Failed to find similar elements: ${SecureErrorHandler.sanitizeError(error).message}` }] }; } } /** * Get all relationships for a specific element */ async getElementRelationships(options) { try { // FIX: DMCP-SEC-004 - Normalize Unicode in user input const normalized = UnicodeValidator.normalize(options.elementName); if (!normalized.isValid) { throw new Error(`Invalid element name: ${normalized.detectedIssues?.join(', ')}`); } options.elementName = normalized.normalizedContent; if (options.elementType) { const normalizedType = UnicodeValidator.normalize(options.elementType); if (!normalizedType.isValid) { throw new Error(`Invalid element type: ${normalizedType.detectedIssues?.join(', ')}`); } options.elementType = normalizedType.normalizedContent; } // Get the index with error handling await this.enhancedIndexManager.getIndex().catch(async (error) => { logger.error('Failed to get Enhanced Index, attempting rebuild', error); return this.enhancedIndexManager.getIndex({ forceRebuild: true }); }); // FIX: DMCP-SEC-006 - Add security audit logging SecurityMonitor.logSecurityEvent({ type: 'ELEMENT_CREATED', severity: 'LOW', source: 'EnhancedIndexHandler.getElementRelationships', details: `Relationship query performed for element: ${options.elementName}`, additionalData: { elementType: options.elementType, relationshipTypes: options.relationshipTypes } }); // Issue #749: Normalize element type (singularβ†’plural) before ID formatting if (options.elementType) { const { type: resolvedType } = normalizeElementTypeInput(options.elementType); if (resolvedType) { options.elementType = resolvedType; } else { logger.warn(`Unknown element type '${options.elementType}' in relationship query β€” proceeding with original value`); } } // FIX: Use centralized element ID formatting // If no element type provided, use full element name as-is (may already include type) const elementId = options.elementType ? formatElementId(options.elementType, options.elementName) : options.elementName; const relationships = await this.enhancedIndexManager.getElementRelationships(elementId); // Filter by type if requested let filteredRelationships = relationships; if (options.relationshipTypes && options.relationshipTypes.length > 0) { filteredRelationships = {}; for (const type of options.relationshipTypes) { if (relationships[type]) { filteredRelationships[type] = relationships[type]; } } } // Format results let text = `${this.indicatorService.getPersonaIndicator()}πŸ”— **Element Relationships**\n\n`; text += `**Element**: ${options.elementName}\n`; if (options.elementType) { text += `**Type**: ${options.elementType}\n`; } text += '\n'; const relationshipCount = Object.values(filteredRelationships) .reduce((sum, rels) => sum + (Array.isArray(rels) ? rels.length : 0), 0); if (relationshipCount === 0) { text += `No relationships found for this element.\n`; } else { for (const [relType, relations] of Object.entries(filteredRelationships)) { if (Array.isArray(relations) && relations.length > 0) { text += `**${relType.charAt(0).toUpperCase() + relType.slice(1)} (${relations.length})**\n`; for (const rel of relations) { // FIX: Use type-safe relationship parsing const parsedRel = parseRelationship(rel); if (isParsedRelationship(parsedRel)) { const icon = this.getElementIcon(parsedRel.targetType); text += ` ${icon} ${parsedRel.targetName}`; if (parsedRel.strength) { text += ` (strength: ${(parsedRel.strength * 100).toFixed(0)}%)`; } text += '\n'; } else { // Fallback for invalid relationships const idToParse = typeof rel === 'string' ? rel : rel.element; const parsed = parseElementIdWithFallback(idToParse); const icon = this.getElementIcon(parsed.type); text += ` ${icon} ${parsed.name} ⚠️\n`; } } text += '\n'; } } } return { content: [{ type: "text", text }] }; } catch (error) { ErrorHandler.logError('EnhancedIndexHandler.getElementRelationships', error, options); return { content: [{ type: "text", text: `${this.indicatorService.getPersonaIndicator()}❌ Failed to get relationships: ${SecureErrorHandler.sanitizeError(error).message}` }] }; } } /** * Search for elements by action verb */ async searchByVerb(options) { try { // FIX: DMCP-SEC-004 - Normalize Unicode in user input const normalized = UnicodeValidator.normalize(options.verb); if (!normalized.isValid) { throw new Error(`Invalid verb: ${normalized.detectedIssues?.join(', ')}`); } options.verb = normalized.normalizedContent; // Get the index with error handling await this.enhancedIndexManager.getIndex().catch(async (error) => { logger.error('Failed to get Enhanced Index, attempting rebuild', error); return this.enhancedIndexManager.getIndex({ forceRebuild: true }); }); // FIX: DMCP-SEC-006 - Add security audit logging SecurityMonitor.logSecurityEvent({ type: 'MEMORY_SEARCHED', severity: 'LOW', source: 'EnhancedIndexHandler.searchByVerb', details: `Verb search for action: ${options.verb}`, additionalData: { verb: options.verb, limit: options.limit } }); // Search by verb const results = await this.enhancedIndexManager.getElementsByAction(options.verb); // Limit results const limited = results.slice(0, options.limit); // Format results let text = `${this.indicatorService.getPersonaIndicator()}🎯 **Elements for Action: "${options.verb}"**\n\n`; text += `**Found**: ${limited.length} element${limited.length === 1 ? '' : 's'}\n\n`; if (limited.length === 0) { text += `No elements found that can handle the action "${options.verb}".\n\n`; text += `**Tips:**\n`; text += `β€’ Try related verbs (e.g., "analyze" β†’ "review", "examine")\n`; text += `β€’ Use common action verbs like "create", "debug", "optimize"\n`; text += `β€’ Check element descriptions for supported actions\n`; } else { for (const elementName of limited) { // FIX: Use centralized element ID parsing // Note: getElementsByAction returns names in "type/name" format for legacy reasons const parsed = elementName.includes('/') ? { type: elementName.split('/')[0], name: elementName.split('/')[1] } : parseElementIdWithFallback(elementName); const icon = this.getElementIcon(parsed.type); text += `${icon} **${parsed.name}** (${parsed.type})\n`; } } return { content: [{ type: "text", text }] }; } catch (error) { ErrorHandler.logError('EnhancedIndexHandler.searchByVerb', error, options); return { content: [{ type: "text", text: `${this.indicatorService.getPersonaIndicator()}❌ Failed to search by verb: ${SecureErrorHandler.sanitizeError(error).message}` }] }; } } /** * Get statistics about the Enhanced Index relationships */ async getRelationshipStats() { try { // Get the index with error handling await this.enhancedIndexManager.getIndex().catch(async (error) => { logger.error('Failed to get Enhanced Index, attempting rebuild', error); return this.enhancedIndexManager.getIndex({ forceRebuild: true }); }); // FIX: DMCP-SEC-006 - Add security audit logging SecurityMonitor.logSecurityEvent({ type: 'MEMORY_SEARCHED', severity: 'LOW', source: 'EnhancedIndexHandler.getRelationshipStats', details: 'Enhanced Index statistics retrieved', additionalData: {} }); // Get stats const stats = await this.enhancedIndexManager.getRelationshipStats(); // Get the index for additional info const index = await this.enhancedIndexManager.getIndex(); // Format results let text = `${this.indicatorService.getPersonaIndicator()}πŸ“Š **Enhanced Index Statistics**\n\n`; text += `**Index Metadata:**\n`; text += `β€’ Version: ${index.metadata.version}\n`; text += `β€’ Last Updated: ${new Date(index.metadata.last_updated).toLocaleString()}\n`; text += `β€’ Total Elements: ${index.metadata.total_elements}\n\n`; text += `**Relationship Statistics:**\n`; for (const [type, count] of Object.entries(stats)) { text += `β€’ ${type}: ${count}\n`; } // Count verb triggers const verbCount = Object.keys(index.action_triggers || {}).length; text += `\n**Verb Triggers:** ${verbCount} verbs mapped\n`; // Show top verbs if any if (verbCount > 0) { const topVerbs = Object.entries(index.action_triggers) .sort((a, b) => b[1].length - a[1].length) .slice(0, 5); text += `**Top Action Verbs:**\n`; for (const [verb, elements] of topVerbs) { text += `β€’ ${verb}: ${elements.length} elements\n`; } } return { content: [{ type: "text", text }] }; } catch (error) { ErrorHandler.logError('EnhancedIndexHandler.getRelationshipStats', error); return { content: [{ type: "text", text: `${this.indicatorService.getPersonaIndicator()}❌ Failed to get stats: ${SecureErrorHandler.sanitizeError(error).message}` }] }; } } /** * Get icon for element type */ getElementIcon(type) { const icons = { personas: '🎭', skills: 'πŸ› οΈ', templates: 'πŸ“„', agents: 'πŸ€–', memories: '🧠', ensembles: '🎨', unknown: 'πŸ“¦' }; return icons[type] || icons.unknown; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRW5oYW5jZWRJbmRleEhhbmRsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaGFuZGxlcnMvRW5oYW5jZWRJbmRleEhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFHSCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDakUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBQzlFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNqRSxPQUFPLEVBQUUsMEJBQTBCLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDcEYsT0FBTyxFQUFFLGlCQUFpQixFQUFFLG9CQUFvQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFFbEcsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFdEUsTUFBTSxPQUFPLG9CQUFvQjtJQUVaO0lBQ0E7SUFGbkIsWUFDbUIsb0JBQTBDLEVBQzFDLGdCQUF5QztRQUR6Qyx5QkFBb0IsR0FBcEIsb0JBQW9CLENBQXNCO1FBQzFDLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBeUI7SUFDekQsQ0FBQztJQUVKOztPQUVHO0lBQ0gsS0FBSyxDQUFDLG1CQUFtQixDQUFDLE9BS3pCO1FBQ0MsSUFBSSxDQUFDO1lBQ0gsa0JBQWtCO1lBQ2xCLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxJQUFJLE9BQU8sT0FBTyxDQUFDLFdBQVcsS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDcEUsTUFBTSxJQUFJLEtBQUssQ0FBQywrQ0FBK0MsQ0FBQyxDQUFDO1lBQ25FLENBQUM7WUFFRCxtRkFBbUY7WUFDbkYs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WUFDSCxDQUFDO1lBRUQsT0FBTztnQkFDTCxPQUFPLEVBQUUsQ0FBQzt3QkFDUixJQUFJLEVBQUUsTUFBTTt3QkFDWixJQUFJO3FCQUNMLENBQUM7YUFDSCxDQUFDO1FBQ0osQ0FBQztRQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7WUFDcEIsWUFBWSxDQUFDLFFBQVEsQ0FBQywyQ0FBMkMsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUMxRSxPQUFPO2dCQUNMLE9BQU8sRUFBRSxDQUFDO3dCQUNSLElBQUksRUFBRSxNQUFNO3dCQUNaLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxtQkFBbUIsRUFBRSwwQkFBMEIsa0JBQWtCLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sRUFBRTtxQkFDaEksQ0FBQzthQUNILENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssY0FBYyxDQUFDLElBQVk7UUFDakMsTUFBTSxLQUFLLEdBQThCO1lBQ3ZDLFFBQVEsRUFBRSxJQUFJO1lBQ2QsTUFBTSxFQUFFLEtBQUs7WUFDYixTQUFTLEVBQUUsSUFBSTtZQUNmLE1BQU0sRUFBRSxJQUFJO1lBQ1osUUFBUSxFQUFFLElBQUk7WUFDZCxTQUFTLEVBQUUsSUFBSTtZQUNmLE9BQU8sRUFBRSxJQUFJO1NBQ2QsQ0FBQztRQUNGLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFDdEMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBFbmhhbmNlZCBJbmRleCBoYW5kbGVyIGZvciBzZW1hbnRpYyBzZWFyY2ggYW5kIHJlbGF0aW9uc2hpcHNcbiAqXG4gKiBJbXBsZW1lbnRzIHRoZSBNQ1AgdG9vbCBoYW5kbGVycyBmb3IgRW5oYW5jZWQgSW5kZXggZnVuY3Rpb25hbGl0eVxuICogaW5jbHVkaW5nIHNpbWlsYXJpdHkgc2VhcmNoLCByZWxhdGlvbnNoaXAgZGlzY292ZXJ5LCBhbmQgdmVyYi1iYXNlZCBzZWFyY2guXG4gKlxuICogRklYRVMgSU1QTEVNRU5URUQgKElzc3VlICMxMDk5KTpcbiAqIC0gVXNlcyBjZW50cmFsaXplZCBlbGVtZW50IElEIHBhcnNpbmcgdXRpbGl0aWVzXG4gKiAtIENvbnNpc3RlbnQgSUQgZm9ybWF0IGhhbmRsaW5nXG4gKiAtIEJldHRlciBlcnJvciBoYW5kbGluZyBmb3IgaW52YWxpZCBJRHNcbiAqXG4gKiBVc2VzIGRlcGVuZGVuY3kgaW5qZWN0aW9uIGZvciBhbGwgc2VydmljZXM6XG4gKiAtIEVuaGFuY2VkSW5kZXhNYW5hZ2VyIGZvciBzZW1hbnRpYyBzZWFyY2ggYW5kIHJlbGF0aW9uc2hpcHNcbiAqIC0gUGVyc29uYUluZGljYXRvclNlcnZpY2UgZm9yIHBlcnNvbmEgaW5kaWNhdG9yIGZvcm1hdHRpbmdcbiAqL1xuXG5pbXBvcnQgeyBFbmhhbmNlZEluZGV4TWFuYWdlciB9IGZyb20gJy4uL3BvcnRmb2xpby9FbmhhbmNlZEluZGV4TWFuYWdlci5qcyc7XG5pbXBvcnQgeyBFcnJvckhhbmRsZXIgfSBmcm9tICcuLi91dGlscy9FcnJvckhhbmRsZXIuanMnO1xuaW1wb3J0IHsgU2VjdXJlRXJyb3JIYW5kbGVyIH0gZnJvbSAnLi4vc2VjdXJpdHkvZXJyb3JIYW5kbGVyLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5TW9uaXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3NlY3VyaXR5TW9uaXRvci5qcyc7XG5pbXBvcnQgeyBwYXJzZUVsZW1lbnRJZFdpdGhGYWxsYmFjaywgZm9ybWF0RWxlbWVudElkIH0gZnJvbSAnLi4vdXRpbHMvZWxlbWVudElkLmpzJztcbmltcG9ydCB7IHBhcnNlUmVsYXRpb25zaGlwLCBpc1BhcnNlZFJlbGF0aW9uc2hpcCB9IGZyb20gJy4uL3BvcnRmb2xpby90eXBlcy9SZWxhdGlvbnNoaXBUeXBlcy5qcyc7XG5pbXBvcnQgeyBQZXJzb25hSW5kaWNhdG9yU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL1BlcnNvbmFJbmRpY2F0b3JTZXJ2aWNlLmpzJztcbmltcG9ydCB7IG5vcm1hbGl6ZUVsZW1lbnRUeXBlSW5wdXQgfSBmcm9tICcuL2VsZW1lbnQtY3J1ZC9oZWxwZXJzLmpzJztcblxuZXhwb3J0IGNsYXNzIEVuaGFuY2VkSW5kZXhIYW5kbGVyIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBlbmhhbmNlZEluZGV4TWFuYWdlcjogRW5oYW5jZWRJbmRleE1hbmFnZXIsXG4gICAgcHJpdmF0ZSByZWFkb25seSBpbmRpY2F0b3JTZXJ2aWNlOiBQZXJzb25hSW5kaWNhdG9yU2VydmljZVxuICApIHt9XG5cbiAgLyoqXG4gICAqIEZpbmQgc2VtYW50aWNhbGx5IHNpbWlsYXIgZWxlbWVudHMgdXNpbmcgTkxQIHNjb3JpbmdcbiAgICovXG4gIGFzeW5jIGZpbmRTaW1pbGFyRWxlbWVudHMob3B0aW9uczoge1xuICAgIGVsZW1lbnROYW1lOiBzdHJpbmc7XG4gICAgZWxlbWVudFR5cGU/OiBzdHJpbmc7XG4gICAgbGltaXQ6IG51bWJlcjtcbiAgICB0aHJlc2hvbGQ6IG51bWJlcjtcbiAgfSkge1xuICAgIHRyeSB7XG4gICAgICAvLyBWYWxpZGF0ZSBpbnB1dHNcbiAgICAgIGlmICghb3B0aW9ucy5lbGVtZW50TmFtZSB8fCB0eXBlb2Ygb3B0aW9ucy5lbGVtZW50TmFtZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdFbGVtZW50IG5hbWUgaXMgcmVxdWlyZWQgYW5kIG11c3QgYmUgYSBzdHJpbmcnKTtcbiAgICAgIH1cblxuICAgICAgLy8gRklYOiBETUNQLVNFQy0wMDQgLSBOb3JtYWxpemUgVW5pY29kZSBpbiB1c2VyIGlucHV0IHRvIHByZXZlbnQgaG9tb2dyYXBoIGF0dGFja3NcbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShvcHRpb25zLmVsZW1lbnROYW1lKTtcbiAgICAgIGlmICghbm9ybWFsaXplZC5pc1ZhbGlkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBlbGVtZW50IG5hbWU6ICR7bm9ybWFsaXplZC5kZXRlY3RlZElzc3Vlcz8uam9pbignLCAnKX1gKTtcbiAgICAgIH1cbiAgICAgIG9wdGlvbnMuZWxlbWVudE5hbWUgPSBub3JtYWxpemVkLm5vcm1hbGl6ZWRDb250ZW50O1xuXG4gICAgICAvLyBBbHNvIG5vcm1hbGl6ZSBlbGVtZW50IHR5cGUgaWYgcHJvdmlkZWRcbiAgICAgIGlmIChvcHRpb25zLmVsZW1lbnRUeXBlKSB7XG4gICAgICAgIGNvbnN0IG5vcm1hbGl6ZWRUeXBlID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUob3B0aW9ucy5lbGVtZW50VHlwZSk7XG4gICAgICAgIGlmICghbm9ybWFsaXplZFR5cGUuaXNWYWxpZCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBlbGVtZW50IHR5cGU6ICR7bm9ybWFsaXplZFR5cGUuZGV0ZWN0ZWRJc3N1ZXM/LmpvaW4oJywgJyl9YCk7XG4gICAgICAgIH1cbiAgICAgICAgb3B0aW9ucy5lbGVtZW50VHlwZSA9IG5vcm1hbGl6ZWRUeXBlLm5vcm1hbGl6ZWRDb250ZW50O1xuICAgICAgfVxuICAgICAgaWYgKG9wdGlvbnMubGltaXQgPD0gMCB8fCBvcHRpb25zLmxpbWl0ID4gMTAwKSB7XG4gICAgICAgIG9wdGlvbnMubGltaXQgPSA1OyAvLyBEZWZhdWx0IHRvIHJlYXNvbmFibGUgbGltaXRcbiAgICAgIH1cbiAgICAgIGlmIChvcHRpb25zLnRocmVzaG9sZCA8IDAgfHwgb3B0aW9ucy50aHJlc2hvbGQgPiAxKSB7XG4gICAgICAgIG9wdGlvbnMudGhyZXNob2xkID0gMC4zOyAvLyBEZWZhdWx0IHRvIHJlYXNvbmFibGUgdGhyZXNob2xkXG4gICAgICB9XG4gICAgICAvLyBFbnN1cmUgdGhlIGVuaGFuY2VkIGluZGV4IGlzIGF2YWlsYWJsZSB3aXRoIGVycm9yIGhhbmRsaW5nXG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCB0aGlzLmVuaGFuY2VkSW5kZXhNYW5hZ2VyLmdldEluZGV4KCk7XG4gICAgICB9IGNhdGNoIChpbmRleEVycm9yKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGdldCBFbmhhbmNlZCBJbmRleCcsIGluZGV4RXJyb3IpO1xuICAgICAgICAvLyBUcnkgdG8gcmVjb3ZlciBieSBmb3JjaW5nIHJlYnVpbGRcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCB0aGlzLmVuaGFuY2VkSW5kZXhNYW5hZ2VyLmdldEluZGV4KHsgZm9yY2VSZWJ1aWxkOiB0cnVlIH0pO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0VuaGFuY2VkIEluZGV4IGlzIHVuYXZhaWxhYmxlLiBQbGVhc2UgdHJ5IGFnYWluIGxhdGVyLicpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEZJWDogRE1DUC1TRUMtMDA2IC0gQWRkIHNlY3VyaXR5IGF1ZGl0IGxvZ2dpbmcgZm9yIGluZGV4IG9wZXJhdGlvbnNcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ01FTU9SWV9TRUFSQ0hFRCcsXG4gICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgc291cmNlOiAnRW5oYW5jZWRJbmRleEhhbmRsZXIuZmluZFNpbWlsYXJFbGVtZW50cycsXG4gICAgICAgIGRldGFpbHM6IGBTaW1pbGFyaXR5IHNlYXJjaCBmb3I6ICR7b3B0aW9ucy5lbGVtZW50TmFtZX1gLFxuICAgICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICAgIGVsZW1lbnRUeXBlOiBvcHRpb25zLmVsZW1lbnRUeXBlLFxuICAgICAgICAgIGxpbWl0OiBvcHRpb25zLmxpbWl0LFxuICAgICAgICAgIHRocmVzaG9sZDogb3B0aW9ucy50aHJlc2hvbGRcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICAgIC8vIEZpbmQgdGhlIGVsZW1lbnRcbiAgICAgIGNvbnN0IGVsZW1lbnRJZCA9IG9wdGlvbnMuZWxlbWVudFR5cGUgP1xuICAgICAgICBgJHtvcHRpb25zLmVsZW1lbnRUeXBlfS8ke29wdGlvbnMuZWxlbWVudE5hbWV9YCA6XG4gICAgICAgIG9wdGlvbnMuZWxlbWVudE5hbWU7XG5cbiAgICAgIC8vIEdldCBjb25uZWN0ZWQgZWxlbWVudHMgKHNpbWlsYXIvcmVsYXRlZClcbiAgICAgIGNvbnN0IGNvbm5lY3RlZE1hcCA9IGF3YWl0IHRoaXMuZW5oYW5jZWRJbmRleE1hbmFnZXIuZ2V0Q29ubmVjdGVkRWxlbWVudHMoXG4gICAgICAgIGVsZW1lbnRJZCxcbiAgICAgICAge1xuICAgICAgICAgIG1heERlcHRoOiAxLCAgLy8gRGlyZWN0IHJlbGF0aW9uc2hpcHMgb25seVxuICAgICAgICAgIG1pblN0cmVuZ3RoOiBvcHRpb25zLnRocmVzaG9sZFxuICAgICAgICB9XG4gICAgICApO1xuXG4gICAgICAvLyBDb252ZXJ0IHRvIGFycmF5IGFuZCBzb3J0IGJ5IHJlbGF0aW9uc2hpcCBzdHJlbmd0aFxuICAgICAgY29uc3Qgc2ltaWxhckVsZW1lbnRzID0gQXJyYXkuZnJvbShjb25uZWN0ZWRNYXAuZW50cmllcygpKVxuICAgICAgICAubWFwKChbaWQsIHBhdGhdKSA9PiB7XG4gICAgICAgICAgY29uc3QgW3R5cGUsIG5hbWVdID0gaWQuc3BsaXQoJy8nKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgIG5hbWUsXG4gICAgICAgICAgICBzY29yZTogcGF0aC50b3RhbFN0cmVuZ3RoIHx8IDAsXG4gICAgICAgICAgICByZWxhdGlvbnNoaXBzOiBwYXRoLnJlbGF0aW9uc2hpcHMgfHwgW10gIC8vIHJlbGF0aW9uc2hpcHMgaXMgYWxyZWFkeSBhbiBhcnJheSBvZiBzdHJpbmdzXG4gICAgICAgICAgfTtcbiAgICAgICAgfSlcbiAgICAgICAgLnNvcnQoKGEsIGIpID0+IGIuc2NvcmUgLSBhLnNjb3JlKVxuICAgICAgICAuc2xpY2UoMCwgb3B0aW9ucy5saW1pdCk7XG5cbiAgICAgIC8vIEZvcm1hdCByZXN1bHRzXG4gICAgICBsZXQgdGV4dCA9IGAke3RoaXMuaW5kaWNhdG9yU2VydmljZS5nZXRQZXJzb25hSW5kaWNhdG9yKCl98J+UjSAqKlNpbWlsYXIgRWxlbWVudHMqKlxcblxcbmA7XG4gICAgICB0ZXh0ICs9IGAqKlJlZmVyZW5jZSoqOiAke29wdGlvbnMuZWxlbWVudE5hbWV9XFxuYDtcbiAgICAgIGlmIChvcHRpb25zLmVsZW1lbnRUeXBlKSB7XG4gICAgICAgIHRleHQgKz0gYCoqVHlwZSoqOiAke29wdGlvbnMuZWxlbWVudFR5cGV9XFxuYDtcbiAgICAgIH1cbiAgICAgIHRleHQgKz0gYCoqRm91bmQqKjogJHtzaW1pbGFyRWxlbWVudHMubGVuZ3RofSBzaW1pbGFyIGVsZW1lbnRzXFxuXFxuYDtcblxuICAgICAgaWYgKHNpbWlsYXJFbGVtZW50cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgdGV4dCArPSBgTm8gc2ltaWxhciBlbGVtZW50cyBmb3VuZCB3aXRoIHNpbWlsYXJpdHkgc2NvcmUgPj0gJHtvcHRpb25zLnRocmVzaG9sZH1cXG5gO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIHNpbWlsYXJFbGVtZW50cykge1xuICAgICAgICAgIGNvbnN0IGljb24gPSB0aGlzLmdldEVsZW1lbnRJY29uKGVsZW1lbnQudHlwZSk7XG4gICAgICAgICAgdGV4dCArPSBgJHtpY29ufSAqKiR7ZWxlbWVudC5uYW1lfSoqICgke2VsZW1lbnQudHlwZX0pXFxuYDtcbiAgICAgICAgICB0ZXh0ICs9IGAgICDwn5OKIFNpbWlsYXJpdHk6ICR7KGVsZW1lbnQuc2NvcmUgKiAxMDApLnRvRml4ZWQoMSl9JVxcbmA7XG4gICAgICAgICAgaWYgKGVsZW1lbnQucmVsYXRpb25zaGlwcyAmJiBlbGVtZW50LnJlbGF0aW9uc2hpcHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgdGV4dCArPSBgICAg8J+UlyBSZWxhdGlvbnNoaXBzOiAke2VsZW1lbnQucmVsYXRpb25zaGlwcy5qb2luKCcsICcpfVxcbmA7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRleHQgKz0gJ1xcbic7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgY29udGVudDogW3tcbiAgICAgICAgICB0eXBlOiBcInRleHRcIixcbiAgICAgICAgICB0ZXh0XG4gICAgICAgIH1dXG4gICAgICB9O1xuICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgIEVycm9ySGFuZGxlci5sb2dFcnJvcignRW5oYW5jZWRJbmRleEhhbmRsZXIuZmluZFNpbWlsYXJFbGVtZW50cycsIGVycm9yLCBvcHRpb25zKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvbnRlbnQ6IFt7XG4gICAgICAgICAgdHlwZTogXCJ0ZXh0XCIsXG4gICAgICAgICAgdGV4dDogYCR7dGhpcy5pbmRpY2F0b3JTZXJ2aWNlLmdldFBlcnNvbmFJbmRpY2F0b3IoKX3inYwgRmFpbGVkIHRvIGZpbmQgc2ltaWxhciBlbGVtZW50czogJHtTZWN1cmVFcnJvckhhbmRsZXIuc2FuaXRpemVFcnJvcihlcnJvcikubWVzc2FnZX1gXG4gICAgICAgIH1dXG4gICAgICB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYWxsIHJlbGF0aW9uc2hpcHMgZm9yIGEgc3BlY2lmaWMgZWxlbWVudFxuICAgKi9cbiAgYXN5bmMgZ2V0RWxlbWVudFJlbGF0aW9uc2hpcHMob3B0aW9uczoge1xuICAgIGVsZW1lbnROYW1lOiBzdHJpbmc7XG4gICAgZWxlbWVudFR5cGU/OiBzdHJpbmc7XG4gICAgcmVsYXRpb25zaGlwVHlwZXM/OiBzdHJpbmdbXTtcbiAgfSkge1xuICAgIHRyeSB7XG4gICAgICAvLyBGSVg6IERNQ1AtU0VDLTAwNCAtIE5vcm1hbGl6ZSBVbmljb2RlIGluIHVzZXIgaW5wdXRcbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShvcHRpb25zLmVsZW1lbnROYW1lKTtcbiAgICAgIGlmICghbm9ybWFsaXplZC5pc1ZhbGlkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBlbGVtZW50IG5hbWU6ICR7bm9ybWFsaXplZC5kZXRlY3RlZElzc3Vlcz8uam9pbignLCAnKX1gKTtcbiAgICAgIH1cbiAgICAgIG9wdGlvbnMuZWxlbWVudE5hbWUgPSBub3JtYWxpemVkLm5vcm1hbGl6ZWRDb250ZW50O1xuXG4gICAgICBpZiAob3B0aW9ucy5lbGVtZW50VHlwZSkge1xuICAgICAgICBjb25zdCBub3JtYWxpemVkVHlwZSA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKG9wdGlvbnMuZWxlbWVudFR5cGUpO1xuICAgICAgICBpZiAoIW5vcm1hbGl6ZWRUeXBlLmlzVmFsaWQpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZWxlbWVudCB0eXBlOiAke25vcm1hbGl6ZWRUeXBlLmRldGVjdGVkSXNzdWVzPy5qb2luKCcsICcpfWApO1xuICAgICAgICB9XG4gICAgICAgIG9wdGlvbnMuZWxlbWVudFR5cGUgPSBub3JtYWxpemVkVHlwZS5ub3JtYWxpemVkQ29udGVudDtcbiAgICAgIH1cblxuICAgICAgLy8gR2V0IHRoZSBpbmRleCB3aXRoIGVycm9yIGhhbmRsaW5nXG4gICAgICBhd2FpdCB0aGlzLmVuaGFuY2VkSW5kZXhNYW5hZ2VyLmdldEluZGV4KCkuY2F0Y2goYXN5bmMgKGVycm9yKSA9PiB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGdldCBFbmhhbmNlZCBJbmRleCwgYXR0ZW1wdGluZyByZWJ1aWxkJywgZXJyb3IpO1xuICAgICAgICByZXR1cm4gdGhpcy5lbmhhbmNlZEluZGV4TWFuYWdlci5nZXRJbmRleCh7IGZvcmNlUmVidWlsZDogdHJ1ZSB9KTtcbiAgICAgIH0pO1xuXG4gICAgICAvLyBGSVg6IERNQ1AtU0VDLTAwNiAtIEFkZCBzZWN1cml0eSBhdWRpdCBsb2dnaW5nXG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdFTEVNRU5UX0NSRUFURUQnLFxuICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgIHNvdXJjZTogJ0VuaGFuY2VkSW5kZXhIYW5kbGVyLmdldEVsZW1lbnRSZWxhdGlvbnNoaXBzJyxcbiAgICAgICAgZGV0YWlsczogYFJlbGF0aW9uc2hpcCBxdWVyeSBwZXJmb3JtZWQgZm9yIGVsZW1lbnQ6ICR7b3B0aW9ucy5lbGVtZW50TmFtZX1gLFxuICAgICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICAgIGVsZW1lbnRUeXBlOiBvcHRpb25zLmVsZW1lbnRUeXBlLFxuICAgICAgICAgIHJlbGF0aW9uc2hpcFR5cGVzOiBvcHRpb25zLnJlbGF0aW9uc2hpcFR5cGVzXG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICAvLyBJc3N1ZSAjNzQ5OiBOb3JtYWxpemUgZWxlbWVudCB0eXBlIChzaW5ndWxhcuKGknBsdXJhbCkgYmVmb3JlIElEIGZvcm1hdHRpbmdcbiAgICAgIGlmIChvcHRpb25zLmVsZW1lbnRUeXBlKSB7XG4gICAgICAgIGNvbnN0IHsgdHlwZTogcmVzb2x2ZWRUeXBlIH0gPSBub3JtYWxpemVFbGVtZW50VHlwZUlucHV0KG9wdGlvbnMuZWxlbWVudFR5cGUpO1xuICAgICAgICBpZiAocmVzb2x2ZWRUeXBlKSB7XG4gICAgICAgICAgb3B0aW9ucy5lbGVtZW50VHlwZSA9IHJlc29sdmVkVHlwZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsb2dnZXIud2FybihgVW5rbm93biBlbGVtZW50IHR5cGUgJyR7b3B0aW9ucy5lbGVtZW50VHlwZX0nIGluIHJlbGF0aW9uc2hpcCBxdWVyeSDigJQgcHJvY2VlZGluZyB3aXRoIG9yaWdpbmFsIHZhbHVlYCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gRklYOiBVc2UgY2VudHJhbGl6ZWQgZWxlbWVudCBJRCBmb3JtYXR0aW5nXG4gICAgICAvLyBJZiBubyBlbGVtZW50IHR5cGUgcHJvdmlkZWQsIHVzZSBmdWxsIGVsZW1lbnQgbmFtZSBhcy1pcyAobWF5IGFscmVhZHkgaW5jbHVkZSB0eXBlKVxuICAgICAgY29uc3QgZWxlbWVudElkID0gb3B0aW9ucy5lbGVtZW50VHlwZSA/XG4gICAgICAgIGZvcm1hdEVsZW1lbnRJZChvcHRpb25zLmVsZW1lbnRUeXBlLCBvcHRpb25zLmVsZW1lbnROYW1lKSA6XG4gICAgICAgIG9wdGlvbnMuZWxlbWVudE5hbWU7XG5cbiAgICAgIGNvbnN0IHJlbGF0aW9uc2hpcHMgPSBhd2FpdCB0aGlzLmVuaGFuY2VkSW5kZXhNYW5hZ2VyLmdldEVsZW1lbnRSZWxhdGlvbnNoaXBzKGVsZW1lbnRJZCk7XG5cbiAgICAgIC8vIEZpbHRlciBieSB0eXBlIGlmIHJlcXVlc3RlZFxuICAgICAgbGV0IGZpbHRlcmVkUmVsYXRpb25zaGlwcyA9IHJlbGF0aW9uc2hpcHM7XG4gICAgICBpZiAob3B0aW9ucy5yZWxhdGlvbnNoaXBUeXBlcyAmJiBvcHRpb25zLnJlbGF0aW9uc2hpcFR5cGVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgZmlsdGVyZWRSZWxhdGlvbnNoaXBzID0ge307XG4gICAgICAgIGZvciAoY29uc3QgdHlwZSBvZiBvcHRpb25zLnJlbGF0aW9uc2hpcFR5cGVzKSB7XG4gICAgICAgICAgaWYgKHJlbGF0aW9uc2hpcHNbdHlwZV0pIHtcbiAgICAgICAgICAgIGZpbHRlcmVkUmVsYXRpb25zaGlwc1t0eXBlXSA9IHJlbGF0aW9uc2hpcHNbdHlwZV07XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEZvcm1hdCByZXN1bHRzXG4gICAgICBsZXQgdGV4dCA9IGAke3RoaXMuaW5kaWNhdG9yU2VydmljZS5nZXRQZXJzb25hSW5kaWNhdG9yKCl98J+UlyAqKkVsZW1lbnQgUmVsYXRpb25zaGlwcyoqXFxuXFxuYDtcbiAgICAgIHRleHQgKz0gYCoqRWxlbWVudCoqOiAke29wdGlvbnMuZWxlbWVudE5hbWV9XFxuYDtcbiAgICAgIGlmIChvcHRpb25zLmVsZW1lbnRUeXBlKSB7XG4gICAgICAgIHRleHQgKz0gYCoqVHlwZSoqOiAke29wdGlvbnMuZWxlbWVudFR5cGV9XFxuYDtcbiAgICAgIH1cbiAgICAgIHRleHQgKz0gJ1xcbic7XG5cbiAgICAgIGNvbnN0IHJlbGF0aW9uc2hpcENvdW50ID0gT2JqZWN0LnZhbHVlcyhmaWx0ZXJlZFJlbGF0aW9uc2hpcHMpXG4gICAgICAgIC5yZWR1Y2UoKHN1bSwgcmVscykgPT4gc3VtICsgKEFycmF5LmlzQXJyYXkocmVscykgPyByZWxzLmxlbmd0aCA6IDApLCAwK