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.

97 lines 13.7 kB
/** * ResourceHandler - Manages MCP Resources capability * * Handles registration and serving of MCP resources including: * - Capability Index (summary, full, stats variants) * * Uses dependency injection pattern consistent with refactored architecture. * Resources are disabled by default and require explicit configuration. */ import { ListResourcesRequestSchema, ReadResourceRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; import { CapabilityIndexResource } from '../server/resources/CapabilityIndexResource.js'; import { logger } from '../utils/logger.js'; import { ErrorHandler } from '../utils/ErrorHandler.js'; import { FileOperationsService } from '../services/FileOperationsService.js'; import { FileLockManager } from '../security/fileLockManager.js'; /** * Handler for MCP Resources protocol */ export class ResourceHandler { configManager; capabilityIndexResource; isEnabled = false; enabledVariants = []; constructor(configManager) { this.configManager = configManager; } /** * Initialize and register resource handlers with the MCP server * * This method: * 1. Checks configuration to see if resources are enabled * 2. Initializes the CapabilityIndexResource * 3. Registers ListResourcesRequest and ReadResourceRequest handlers * * @param server - MCP Server instance to register handlers with */ async initialize(server) { try { // Check if resources are enabled in configuration const resourcesConfig = this.configManager.getSetting('elements.enhanced_index.resources'); if (!resourcesConfig?.advertise_resources) { logger.info('[ResourceHandler] MCP Resources disabled (future-proof implementation, opt-in required)'); return; } // Resources are enabled this.isEnabled = true; // Initialize resource handler with FileOperationsService const fileLockManager = new FileLockManager(); const fileOperations = new FileOperationsService(fileLockManager); this.capabilityIndexResource = new CapabilityIndexResource(fileOperations); // Determine which variants are enabled if (resourcesConfig.variants?.summary) this.enabledVariants.push('summary'); if (resourcesConfig.variants?.full) this.enabledVariants.push('full'); if (resourcesConfig.variants?.stats) this.enabledVariants.push('stats'); logger.info(`[ResourceHandler] MCP Resources enabled: capability-index (variants: ${this.enabledVariants.join(', ') || 'none'})`); // Register ListResourcesRequest handler server.setRequestHandler(ListResourcesRequestSchema, async () => { if (!this.capabilityIndexResource) { throw new McpError(ErrorCode.InternalError, 'Resource handler not initialized'); } const result = await this.capabilityIndexResource.listResources(); return result; // Type assertion needed for MCP SDK compatibility }); // Register ReadResourceRequest handler server.setRequestHandler(ReadResourceRequestSchema, async (request) => { if (!this.capabilityIndexResource) { throw new McpError(ErrorCode.InternalError, 'Resource handler not initialized'); } const result = await this.capabilityIndexResource.readResource(request.params.uri); return result; // Type assertion needed for MCP SDK compatibility }); logger.debug('[ResourceHandler] MCP resource handlers registered successfully'); } catch (error) { ErrorHandler.logError('ResourceHandler.initialize', error); // Don't throw - resource setup failures shouldn't prevent server startup logger.warn('[ResourceHandler] Failed to setup MCP resource handlers, continuing without resources'); this.isEnabled = false; } } /** * Check if resources are currently enabled */ getIsEnabled() { return this.isEnabled; } /** * Get list of enabled resource variants */ getEnabledVariants() { return [...this.enabledVariants]; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVzb3VyY2VIYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2hhbmRsZXJzL1Jlc291cmNlSGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7R0FRRztBQUdILE9BQU8sRUFBRSwwQkFBMEIsRUFBRSx5QkFBeUIsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDaEksT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sZ0RBQWdELENBQUM7QUFFekYsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN4RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUM3RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFakU7O0dBRUc7QUFDSCxNQUFNLE9BQU8sZUFBZTtJQU1QO0lBTFgsdUJBQXVCLENBQTJCO0lBQ2xELFNBQVMsR0FBWSxLQUFLLENBQUM7SUFDM0IsZUFBZSxHQUFhLEVBQUUsQ0FBQztJQUV2QyxZQUNtQixhQUE0QjtRQUE1QixrQkFBYSxHQUFiLGFBQWEsQ0FBZTtJQUM1QyxDQUFDO0lBRUo7Ozs7Ozs7OztPQVNHO0lBQ0gsS0FBSyxDQUFDLFVBQVUsQ0FBQyxNQUFjO1FBQzdCLElBQUksQ0FBQztZQUNILGtEQUFrRDtZQUNsRCxNQUFNLGVBQWUsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBTSxtQ0FBbUMsQ0FBQyxDQUFDO1lBRWhHLElBQUksQ0FBQyxlQUFlLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztnQkFDMUMsTUFBTSxDQUFDLElBQUksQ0FBQyx5RkFBeUYsQ0FBQyxDQUFDO2dCQUN2RyxPQUFPO1lBQ1QsQ0FBQztZQUVELHdCQUF3QjtZQUN4QixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztZQUV0Qix5REFBeUQ7WUFDekQsTUFBTSxlQUFlLEdBQUcsSUFBSSxlQUFlLEVBQUUsQ0FBQztZQUM5QyxNQUFNLGNBQWMsR0FBRyxJQUFJLHFCQUFxQixDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQ2xFLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLHVCQUF1QixDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBRTNFLHVDQUF1QztZQUN2QyxJQUFJLGVBQWUsQ0FBQyxRQUFRLEVBQUUsT0FBTztnQkFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM1RSxJQUFJLGVBQWUsQ0FBQyxRQUFRLEVBQUUsSUFBSTtnQkFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUN0RSxJQUFJLGVBQWUsQ0FBQyxRQUFRLEVBQUUsS0FBSztnQkFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUV4RSxNQUFNLENBQUMsSUFBSSxDQUFDLHdFQUF3RSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEdBQUcsQ0FBQyxDQUFDO1lBRWxJLHdDQUF3QztZQUN4QyxNQUFNLENBQUMsaUJBQWlCLENBQUMsMEJBQTBCLEVBQUUsS0FBSyxJQUFJLEVBQUU7Z0JBQzlELElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztvQkFDbEMsTUFBTSxJQUFJLFFBQVEsQ0FBQyxTQUFTLENBQUMsYUFBYSxFQUFFLGtDQUFrQyxDQUFDLENBQUM7Z0JBQ2xGLENBQUM7Z0JBQ0QsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsdUJBQXVCLENBQUMsYUFBYSxFQUFFLENBQUM7Z0JBQ2xFLE9BQU8sTUFBYSxDQUFDLENBQUMsa0RBQWtEO1lBQzFFLENBQUMsQ0FBQyxDQUFDO1lBRUgsdUNBQXVDO1lBQ3ZDLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyx5QkFBeUIsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7Z0JBQ3BFLElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztvQkFDbEMsTUFBTSxJQUFJLFFBQVEsQ0FBQyxTQUFTLENBQUMsYUFBYSxFQUFFLGtDQUFrQyxDQUFDLENBQUM7Z0JBQ2xGLENBQUM7Z0JBQ0QsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsdUJBQXVCLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ25GLE9BQU8sTUFBYSxDQUFDLENBQUMsa0RBQWtEO1lBQzFFLENBQUMsQ0FBQyxDQUFDO1lBRUgsTUFBTSxDQUFDLEtBQUssQ0FBQyxpRUFBaUUsQ0FBQyxDQUFDO1FBQ2xGLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsWUFBWSxDQUFDLFFBQVEsQ0FBQyw0QkFBNEIsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUMzRCx5RUFBeUU7WUFDekUsTUFBTSxDQUFDLElBQUksQ0FBQyx1RkFBdUYsQ0FBQyxDQUFDO1lBQ3JHLElBQUksQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDO1FBQ3pCLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxZQUFZO1FBQ1YsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7T0FFRztJQUNILGtCQUFrQjtRQUNoQixPQUFPLENBQUMsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDbkMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSZXNvdXJjZUhhbmRsZXIgLSBNYW5hZ2VzIE1DUCBSZXNvdXJjZXMgY2FwYWJpbGl0eVxuICpcbiAqIEhhbmRsZXMgcmVnaXN0cmF0aW9uIGFuZCBzZXJ2aW5nIG9mIE1DUCByZXNvdXJjZXMgaW5jbHVkaW5nOlxuICogLSBDYXBhYmlsaXR5IEluZGV4IChzdW1tYXJ5LCBmdWxsLCBzdGF0cyB2YXJpYW50cylcbiAqXG4gKiBVc2VzIGRlcGVuZGVuY3kgaW5qZWN0aW9uIHBhdHRlcm4gY29uc2lzdGVudCB3aXRoIHJlZmFjdG9yZWQgYXJjaGl0ZWN0dXJlLlxuICogUmVzb3VyY2VzIGFyZSBkaXNhYmxlZCBieSBkZWZhdWx0IGFuZCByZXF1aXJlIGV4cGxpY2l0IGNvbmZpZ3VyYXRpb24uXG4gKi9cblxuaW1wb3J0IHsgU2VydmVyIH0gZnJvbSBcIkBtb2RlbGNvbnRleHRwcm90b2NvbC9zZGsvc2VydmVyL2luZGV4LmpzXCI7XG5pbXBvcnQgeyBMaXN0UmVzb3VyY2VzUmVxdWVzdFNjaGVtYSwgUmVhZFJlc291cmNlUmVxdWVzdFNjaGVtYSwgRXJyb3JDb2RlLCBNY3BFcnJvciB9IGZyb20gXCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2RrL3R5cGVzLmpzXCI7XG5pbXBvcnQgeyBDYXBhYmlsaXR5SW5kZXhSZXNvdXJjZSB9IGZyb20gJy4uL3NlcnZlci9yZXNvdXJjZXMvQ2FwYWJpbGl0eUluZGV4UmVzb3VyY2UuanMnO1xuaW1wb3J0IHsgQ29uZmlnTWFuYWdlciB9IGZyb20gJy4uL2NvbmZpZy9Db25maWdNYW5hZ2VyLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBFcnJvckhhbmRsZXIgfSBmcm9tICcuLi91dGlscy9FcnJvckhhbmRsZXIuanMnO1xuaW1wb3J0IHsgRmlsZU9wZXJhdGlvbnNTZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvRmlsZU9wZXJhdGlvbnNTZXJ2aWNlLmpzJztcbmltcG9ydCB7IEZpbGVMb2NrTWFuYWdlciB9IGZyb20gJy4uL3NlY3VyaXR5L2ZpbGVMb2NrTWFuYWdlci5qcyc7XG5cbi8qKlxuICogSGFuZGxlciBmb3IgTUNQIFJlc291cmNlcyBwcm90b2NvbFxuICovXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VIYW5kbGVyIHtcbiAgcHJpdmF0ZSBjYXBhYmlsaXR5SW5kZXhSZXNvdXJjZT86IENhcGFiaWxpdHlJbmRleFJlc291cmNlO1xuICBwcml2YXRlIGlzRW5hYmxlZDogYm9vbGVhbiA9IGZhbHNlO1xuICBwcml2YXRlIGVuYWJsZWRWYXJpYW50czogc3RyaW5nW10gPSBbXTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbmZpZ01hbmFnZXI6IENvbmZpZ01hbmFnZXJcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplIGFuZCByZWdpc3RlciByZXNvdXJjZSBoYW5kbGVycyB3aXRoIHRoZSBNQ1Agc2VydmVyXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kOlxuICAgKiAxLiBDaGVja3MgY29uZmlndXJhdGlvbiB0byBzZWUgaWYgcmVzb3VyY2VzIGFyZSBlbmFibGVkXG4gICAqIDIuIEluaXRpYWxpemVzIHRoZSBDYXBhYmlsaXR5SW5kZXhSZXNvdXJjZVxuICAgKiAzLiBSZWdpc3RlcnMgTGlzdFJlc291cmNlc1JlcXVlc3QgYW5kIFJlYWRSZXNvdXJjZVJlcXVlc3QgaGFuZGxlcnNcbiAgICpcbiAgICogQHBhcmFtIHNlcnZlciAtIE1DUCBTZXJ2ZXIgaW5zdGFuY2UgdG8gcmVnaXN0ZXIgaGFuZGxlcnMgd2l0aFxuICAgKi9cbiAgYXN5bmMgaW5pdGlhbGl6ZShzZXJ2ZXI6IFNlcnZlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBDaGVjayBpZiByZXNvdXJjZXMgYXJlIGVuYWJsZWQgaW4gY29uZmlndXJhdGlvblxuICAgICAgY29uc3QgcmVzb3VyY2VzQ29uZmlnID0gdGhpcy5jb25maWdNYW5hZ2VyLmdldFNldHRpbmc8YW55PignZWxlbWVudHMuZW5oYW5jZWRfaW5kZXgucmVzb3VyY2VzJyk7XG5cbiAgICAgIGlmICghcmVzb3VyY2VzQ29uZmlnPy5hZHZlcnRpc2VfcmVzb3VyY2VzKSB7XG4gICAgICAgIGxvZ2dlci5pbmZvKCdbUmVzb3VyY2VIYW5kbGVyXSBNQ1AgUmVzb3VyY2VzIGRpc2FibGVkIChmdXR1cmUtcHJvb2YgaW1wbGVtZW50YXRpb24sIG9wdC1pbiByZXF1aXJlZCknKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvLyBSZXNvdXJjZXMgYXJlIGVuYWJsZWRcbiAgICAgIHRoaXMuaXNFbmFibGVkID0gdHJ1ZTtcblxuICAgICAgLy8gSW5pdGlhbGl6ZSByZXNvdXJjZSBoYW5kbGVyIHdpdGggRmlsZU9wZXJhdGlvbnNTZXJ2aWNlXG4gICAgICBjb25zdCBmaWxlTG9ja01hbmFnZXIgPSBuZXcgRmlsZUxvY2tNYW5hZ2VyKCk7XG4gICAgICBjb25zdCBmaWxlT3BlcmF0aW9ucyA9IG5ldyBGaWxlT3BlcmF0aW9uc1NlcnZpY2UoZmlsZUxvY2tNYW5hZ2VyKTtcbiAgICAgIHRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UgPSBuZXcgQ2FwYWJpbGl0eUluZGV4UmVzb3VyY2UoZmlsZU9wZXJhdGlvbnMpO1xuXG4gICAgICAvLyBEZXRlcm1pbmUgd2hpY2ggdmFyaWFudHMgYXJlIGVuYWJsZWRcbiAgICAgIGlmIChyZXNvdXJjZXNDb25maWcudmFyaWFudHM/LnN1bW1hcnkpIHRoaXMuZW5hYmxlZFZhcmlhbnRzLnB1c2goJ3N1bW1hcnknKTtcbiAgICAgIGlmIChyZXNvdXJjZXNDb25maWcudmFyaWFudHM/LmZ1bGwpIHRoaXMuZW5hYmxlZFZhcmlhbnRzLnB1c2goJ2Z1bGwnKTtcbiAgICAgIGlmIChyZXNvdXJjZXNDb25maWcudmFyaWFudHM/LnN0YXRzKSB0aGlzLmVuYWJsZWRWYXJpYW50cy5wdXNoKCdzdGF0cycpO1xuXG4gICAgICBsb2dnZXIuaW5mbyhgW1Jlc291cmNlSGFuZGxlcl0gTUNQIFJlc291cmNlcyBlbmFibGVkOiBjYXBhYmlsaXR5LWluZGV4ICh2YXJpYW50czogJHt0aGlzLmVuYWJsZWRWYXJpYW50cy5qb2luKCcsICcpIHx8ICdub25lJ30pYCk7XG5cbiAgICAgIC8vIFJlZ2lzdGVyIExpc3RSZXNvdXJjZXNSZXF1ZXN0IGhhbmRsZXJcbiAgICAgIHNlcnZlci5zZXRSZXF1ZXN0SGFuZGxlcihMaXN0UmVzb3VyY2VzUmVxdWVzdFNjaGVtYSwgYXN5bmMgKCkgPT4ge1xuICAgICAgICBpZiAoIXRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgTWNwRXJyb3IoRXJyb3JDb2RlLkludGVybmFsRXJyb3IsICdSZXNvdXJjZSBoYW5kbGVyIG5vdCBpbml0aWFsaXplZCcpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UubGlzdFJlc291cmNlcygpO1xuICAgICAgICByZXR1cm4gcmVzdWx0IGFzIGFueTsgLy8gVHlwZSBhc3NlcnRpb24gbmVlZGVkIGZvciBNQ1AgU0RLIGNvbXBhdGliaWxpdHlcbiAgICAgIH0pO1xuXG4gICAgICAvLyBSZWdpc3RlciBSZWFkUmVzb3VyY2VSZXF1ZXN0IGhhbmRsZXJcbiAgICAgIHNlcnZlci5zZXRSZXF1ZXN0SGFuZGxlcihSZWFkUmVzb3VyY2VSZXF1ZXN0U2NoZW1hLCBhc3luYyAocmVxdWVzdCkgPT4ge1xuICAgICAgICBpZiAoIXRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgTWNwRXJyb3IoRXJyb3JDb2RlLkludGVybmFsRXJyb3IsICdSZXNvdXJjZSBoYW5kbGVyIG5vdCBpbml0aWFsaXplZCcpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UucmVhZFJlc291cmNlKHJlcXVlc3QucGFyYW1zLnVyaSk7XG4gICAgICAgIHJldHVybiByZXN1bHQgYXMgYW55OyAvLyBUeXBlIGFzc2VydGlvbiBuZWVkZWQgZm9yIE1DUCBTREsgY29tcGF0aWJpbGl0eVxuICAgICAgfSk7XG5cbiAgICAgIGxvZ2dlci5kZWJ1ZygnW1Jlc291cmNlSGFuZGxlcl0gTUNQIHJlc291cmNlIGhhbmRsZXJzIHJlZ2lzdGVyZWQgc3VjY2Vzc2Z1bGx5Jyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIEVycm9ySGFuZGxlci5sb2dFcnJvcignUmVzb3VyY2VIYW5kbGVyLmluaXRpYWxpemUnLCBlcnJvcik7XG4gICAgICAvLyBEb24ndCB0aHJvdyAtIHJlc291cmNlIHNldHVwIGZhaWx1cmVzIHNob3VsZG4ndCBwcmV2ZW50IHNlcnZlciBzdGFydHVwXG4gICAgICBsb2dnZXIud2FybignW1Jlc291cmNlSGFuZGxlcl0gRmFpbGVkIHRvIHNldHVwIE1DUCByZXNvdXJjZSBoYW5kbGVycywgY29udGludWluZyB3aXRob3V0IHJlc291cmNlcycpO1xuICAgICAgdGhpcy5pc0VuYWJsZWQgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgcmVzb3VyY2VzIGFyZSBjdXJyZW50bHkgZW5hYmxlZFxuICAgKi9cbiAgZ2V0SXNFbmFibGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmlzRW5hYmxlZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgbGlzdCBvZiBlbmFibGVkIHJlc291cmNlIHZhcmlhbnRzXG4gICAqL1xuICBnZXRFbmFibGVkVmFyaWFudHMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiBbLi4udGhpcy5lbmFibGVkVmFyaWFudHNdO1xuICB9XG59XG4iXX0=