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.

200 lines 24.2 kB
/** * Installation detection utilities * Determines whether the application is running from npm or git installation */ import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; import { logger } from './logger.js'; // Default filesystem implementation const defaultFs = { realpathSync: fs.realpathSync, existsSync: fs.existsSync, statSync: fs.statSync }; export class InstallationDetector { static cachedType = null; static fsOverride = null; static importMetaUrlOverride = null; // Maximum directory levels to search upward for .git directory static MAX_SEARCH_DEPTH = 10; /** * Set filesystem operations for testing (DI) */ static setFileSystem(fs) { this.fsOverride = fs; } /** * Set import.meta.url for testing */ static setImportMetaUrl(url) { this.importMetaUrlOverride = url; } /** * Get the current filesystem implementation */ static getFs() { return this.fsOverride || defaultFs; } /** * Get the current import.meta.url */ static getImportMetaUrl() { return this.importMetaUrlOverride || import.meta.url; } /** * Detect the installation type (npm global, git clone, or unknown) */ static getInstallationType() { // Return cached result if available if (this.cachedType !== null) { return this.cachedType; } const fileSystem = this.getFs(); try { // Get the directory where this file is located const currentFileUrl = this.getImportMetaUrl(); const currentFilePath = fileURLToPath(currentFileUrl); let currentDir = path.dirname(currentFilePath); // Resolve symlinks to get the real path try { currentDir = fileSystem.realpathSync(currentDir); } catch { // If realpath fails, continue with original path logger.debug('[InstallationDetector] Could not resolve real path, using original'); } // Check if we're in a node_modules directory (npm installation) // Use path separator to ensure we match the exact package name // Normalize the path to handle both Windows and Unix separators const normalizedCurrentDir = path.normalize(currentDir); const npmPattern = path.normalize(path.join('node_modules', '@dollhousemcp', 'mcp-server')); if (normalizedCurrentDir.includes(npmPattern)) { logger.debug('[InstallationDetector] Detected npm installation'); this.cachedType = 'npm'; return 'npm'; } // Check for .git directory (git installation) // Search up from current directory let searchDir = currentDir; for (let i = 0; i < this.MAX_SEARCH_DEPTH; i++) { const gitDir = path.join(searchDir, '.git'); try { if (fileSystem.existsSync(gitDir) && fileSystem.statSync(gitDir).isDirectory()) { logger.debug('[InstallationDetector] Detected git installation'); this.cachedType = 'git'; return 'git'; } } catch { // Ignore errors and continue searching } const parentDir = path.dirname(searchDir); if (parentDir === searchDir) { // Reached root directory break; } searchDir = parentDir; } logger.warn('[InstallationDetector] Could not determine installation type'); this.cachedType = 'unknown'; return 'unknown'; } catch (error) { logger.error('[InstallationDetector] Error detecting installation type:', error); this.cachedType = 'unknown'; return 'unknown'; } } /** * Get the npm global installation path if running from npm */ static getNpmGlobalPath() { if (this.getInstallationType() !== 'npm') { return null; } const fileSystem = this.getFs(); try { const currentFileUrl = this.getImportMetaUrl(); const currentFilePath = fileURLToPath(currentFileUrl); let currentDir = path.dirname(currentFilePath); // Find the root of the npm package // Normalize paths to handle Windows and Unix separators const npmPackagePattern = path.normalize('node_modules/@dollhousemcp/mcp-server'); while (path.normalize(currentDir).includes(npmPackagePattern)) { const packageJsonPath = path.join(currentDir, 'package.json'); if (fileSystem.existsSync(packageJsonPath)) { return currentDir; } const parentDir = path.dirname(currentDir); if (parentDir === currentDir) break; currentDir = parentDir; } } catch (error) { logger.error('[InstallationDetector] Error finding npm global path:', error); } return null; } /** * Get the git repository root path if running from git */ static getGitRepositoryPath() { if (this.getInstallationType() !== 'git') { return null; } const fileSystem = this.getFs(); try { const currentFileUrl = this.getImportMetaUrl(); const currentFilePath = fileURLToPath(currentFileUrl); let currentDir = path.dirname(currentFilePath); // Search up for .git directory for (let i = 0; i < this.MAX_SEARCH_DEPTH; i++) { const gitDir = path.join(currentDir, '.git'); if (fileSystem.existsSync(gitDir) && fileSystem.statSync(gitDir).isDirectory()) { return currentDir; } const parentDir = path.dirname(currentDir); if (parentDir === currentDir) break; currentDir = parentDir; } } catch (error) { logger.error('[InstallationDetector] Error finding git repository path:', error); } return null; } /** * Get a human-readable description of the installation */ static getInstallationDescription() { const type = this.getInstallationType(); switch (type) { case 'npm': { const npmPath = this.getNpmGlobalPath(); return npmPath ? `npm global installation at ${npmPath}` : 'npm global installation'; } case 'git': { const gitPath = this.getGitRepositoryPath(); return gitPath ? `git installation at ${gitPath}` : 'git installation'; } default: return 'unknown installation type'; } } /** * Clear the cached installation type (mainly for testing) */ static clearCache() { this.cachedType = null; this.fsOverride = null; this.importMetaUrlOverride = null; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFsbGF0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL2luc3RhbGxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQztBQUN6QixPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFXckMsb0NBQW9DO0FBQ3BDLE1BQU0sU0FBUyxHQUFnQjtJQUM3QixZQUFZLEVBQUUsRUFBRSxDQUFDLFlBQVk7SUFDN0IsVUFBVSxFQUFFLEVBQUUsQ0FBQyxVQUFVO0lBQ3pCLFFBQVEsRUFBRSxFQUFFLENBQUMsUUFBUTtDQUN0QixDQUFDO0FBRUYsTUFBTSxPQUFPLG9CQUFvQjtJQUN2QixNQUFNLENBQUMsVUFBVSxHQUE0QixJQUFJLENBQUM7SUFDbEQsTUFBTSxDQUFDLFVBQVUsR0FBdUIsSUFBSSxDQUFDO0lBQzdDLE1BQU0sQ0FBQyxxQkFBcUIsR0FBa0IsSUFBSSxDQUFDO0lBRTNELCtEQUErRDtJQUN2RCxNQUFNLENBQVUsZ0JBQWdCLEdBQUcsRUFBRSxDQUFDO0lBRTlDOztPQUVHO0lBQ0gsTUFBTSxDQUFDLGFBQWEsQ0FBQyxFQUFzQjtRQUN6QyxJQUFJLENBQUMsVUFBVSxHQUFHLEVBQUUsQ0FBQztJQUN2QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsR0FBa0I7UUFDeEMsSUFBSSxDQUFDLHFCQUFxQixHQUFHLEdBQUcsQ0FBQztJQUNuQyxDQUFDO0lBRUQ7O09BRUc7SUFDSyxNQUFNLENBQUMsS0FBSztRQUNsQixPQUFPLElBQUksQ0FBQyxVQUFVLElBQUksU0FBUyxDQUFDO0lBQ3RDLENBQUM7SUFFRDs7T0FFRztJQUNLLE1BQU0s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QUFFLENBQUM7WUFDZixNQUFNLENBQUMsS0FBSyxDQUFDLDJEQUEyRCxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ25GLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQywwQkFBMEI7UUFDL0IsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUM7UUFFeEMsUUFBUSxJQUFJLEVBQUUsQ0FBQztZQUNiLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFDWCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztnQkFDeEMsT0FBTyxPQUFPO29CQUNaLENBQUMsQ0FBQyw4QkFBOEIsT0FBTyxFQUFFO29CQUN6QyxDQUFDLENBQUMseUJBQXlCLENBQUM7WUFDaEMsQ0FBQztZQUVELEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFDWCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztnQkFDNUMsT0FBTyxPQUFPO29CQUNaLENBQUMsQ0FBQyx1QkFBdUIsT0FBTyxFQUFFO29CQUNsQyxDQUFDLENBQUMsa0JBQWtCLENBQUM7WUFDekIsQ0FBQztZQUVEO2dCQUNFLE9BQU8sMkJBQTJCLENBQUM7UUFDdkMsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxVQUFVO1FBQ2YsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDdkIsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDdkIsSUFBSSxDQUFDLHFCQUFxQixHQUFHLElBQUksQ0FBQztJQUNwQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBJbnN0YWxsYXRpb24gZGV0ZWN0aW9uIHV0aWxpdGllc1xuICogRGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSBhcHBsaWNhdGlvbiBpcyBydW5uaW5nIGZyb20gbnBtIG9yIGdpdCBpbnN0YWxsYXRpb25cbiAqL1xuXG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5cbmV4cG9ydCB0eXBlIEluc3RhbGxhdGlvblR5cGUgPSAnbnBtJyB8ICdnaXQnIHwgJ3Vua25vd24nO1xuXG4vLyBGaWxlc3lzdGVtIG9wZXJhdGlvbnMgaW50ZXJmYWNlIGZvciBkZXBlbmRlbmN5IGluamVjdGlvblxuZXhwb3J0IGludGVyZmFjZSBJRmlsZVN5c3RlbSB7XG4gIHJlYWxwYXRoU3luYyhwYXRoOiBzdHJpbmcpOiBzdHJpbmc7XG4gIGV4aXN0c1N5bmMocGF0aDogc3RyaW5nKTogYm9vbGVhbjtcbiAgc3RhdFN5bmMocGF0aDogc3RyaW5nKTogeyBpc0RpcmVjdG9yeSgpOiBib29sZWFuIH07XG59XG5cbi8vIERlZmF1bHQgZmlsZXN5c3RlbSBpbXBsZW1lbnRhdGlvblxuY29uc3QgZGVmYXVsdEZzOiBJRmlsZVN5c3RlbSA9IHtcbiAgcmVhbHBhdGhTeW5jOiBmcy5yZWFscGF0aFN5bmMsXG4gIGV4aXN0c1N5bmM6IGZzLmV4aXN0c1N5bmMsXG4gIHN0YXRTeW5jOiBmcy5zdGF0U3luY1xufTtcblxuZXhwb3J0IGNsYXNzIEluc3RhbGxhdGlvbkRldGVjdG9yIHtcbiAgcHJpdmF0ZSBzdGF0aWMgY2FjaGVkVHlwZTogSW5zdGFsbGF0aW9uVHlwZSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHN0YXRpYyBmc092ZXJyaWRlOiBJRmlsZVN5c3RlbSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHN0YXRpYyBpbXBvcnRNZXRhVXJsT3ZlcnJpZGU6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8vIE1heGltdW0gZGlyZWN0b3J5IGxldmVscyB0byBzZWFyY2ggdXB3YXJkIGZvciAuZ2l0IGRpcmVjdG9yeVxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBNQVhfU0VBUkNIX0RFUFRIID0gMTA7XG5cbiAgLyoqXG4gICAqIFNldCBmaWxlc3lzdGVtIG9wZXJhdGlvbnMgZm9yIHRlc3RpbmcgKERJKVxuICAgKi9cbiAgc3RhdGljIHNldEZpbGVTeXN0ZW0oZnM6IElGaWxlU3lzdGVtIHwgbnVsbCk6IHZvaWQge1xuICAgIHRoaXMuZnNPdmVycmlkZSA9IGZzO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCBpbXBvcnQubWV0YS51cmwgZm9yIHRlc3RpbmdcbiAgICovXG4gIHN0YXRpYyBzZXRJbXBvcnRNZXRhVXJsKHVybDogc3RyaW5nIHwgbnVsbCk6IHZvaWQge1xuICAgIHRoaXMuaW1wb3J0TWV0YVVybE92ZXJyaWRlID0gdXJsO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgY3VycmVudCBmaWxlc3lzdGVtIGltcGxlbWVudGF0aW9uXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBnZXRGcygpOiBJRmlsZVN5c3RlbSB7XG4gICAgcmV0dXJuIHRoaXMuZnNPdmVycmlkZSB8fCBkZWZhdWx0RnM7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBjdXJyZW50IGltcG9ydC5tZXRhLnVybFxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgZ2V0SW1wb3J0TWV0YVVybCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmltcG9ydE1ldGFVcmxPdmVycmlkZSB8fCBpbXBvcnQubWV0YS51cmw7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBEZXRlY3QgdGhlIGluc3RhbGxhdGlvbiB0eXBlIChucG0gZ2xvYmFsLCBnaXQgY2xvbmUsIG9yIHVua25vd24pXG4gICAqL1xuICBzdGF0aWMgZ2V0SW5zdGFsbGF0aW9uVHlwZSgpOiBJbnN0YWxsYXRpb25UeXBlIHtcbiAgICAvLyBSZXR1cm4gY2FjaGVkIHJlc3VsdCBpZiBhdmFpbGFibGVcbiAgICBpZiAodGhpcy5jYWNoZWRUeXBlICE9PSBudWxsKSB7XG4gICAgICByZXR1cm4gdGhpcy5jYWNoZWRUeXBlO1xuICAgIH1cblxuICAgIGNvbnN0IGZpbGVTeXN0ZW0gPSB0aGlzLmdldEZzKCk7XG5cbiAgICB0cnkge1xuICAgICAgLy8gR2V0IHRoZSBkaXJlY3Rvcnkgd2hlcmUgdGhpcyBmaWxlIGlzIGxvY2F0ZWRcbiAgICAgIGNvbnN0IGN1cnJlbnRGaWxlVXJsID0gdGhpcy5nZXRJbXBvcnRNZXRhVXJsKCk7XG4gICAgICBjb25zdCBjdXJyZW50RmlsZVBhdGggPSBmaWxlVVJMVG9QYXRoKGN1cnJlbnRGaWxlVXJsKTtcbiAgICAgIGxldCBjdXJyZW50RGlyID0gcGF0aC5kaXJuYW1lKGN1cnJlbnRGaWxlUGF0aCk7XG5cbiAgICAgIC8vIFJlc29sdmUgc3ltbGlua3MgdG8gZ2V0IHRoZSByZWFsIHBhdGhcbiAgICAgIHRyeSB7XG4gICAgICAgIGN1cnJlbnREaXIgPSBmaWxlU3lzdGVtLnJlYWxwYXRoU3luYyhjdXJyZW50RGlyKTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvLyBJZiByZWFscGF0aCBmYWlscywgY29udGludWUgd2l0aCBvcmlnaW5hbCBwYXRoXG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnW0luc3RhbGxhdGlvbkRldGVjdG9yXSBDb3VsZCBub3QgcmVzb2x2ZSByZWFsIHBhdGgsIHVzaW5nIG9yaWdpbmFsJyk7XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGlmIHdlJ3JlIGluIGEgbm9kZV9tb2R1bGVzIGRpcmVjdG9yeSAobnBtIGluc3RhbGxhdGlvbilcbiAgICAgIC8vIFVzZSBwYXRoIHNlcGFyYXRvciB0byBlbnN1cmUgd2UgbWF0Y2ggdGhlIGV4YWN0IHBhY2thZ2UgbmFtZVxuICAgICAgLy8gTm9ybWFsaXplIHRoZSBwYXRoIHRvIGhhbmRsZSBib3RoIFdpbmRvd3MgYW5kIFVuaXggc2VwYXJhdG9yc1xuICAgICAgY29uc3Qgbm9ybWFsaXplZEN1cnJlbnREaXIgPSBwYXRoLm5vcm1hbGl6ZShjdXJyZW50RGlyKTtcbiAgICAgIGNvbnN0IG5wbVBhdHRlcm4gPSBwYXRoLm5vcm1hbGl6ZShwYXRoLmpvaW4oJ25vZGVfbW9kdWxlcycsICdAZG9sbGhvdXNlbWNwJywgJ21jcC1zZXJ2ZXInKSk7XG4gICAgICBpZiAobm9ybWFsaXplZEN1cnJlbnREaXIuaW5jbHVkZXMobnBtUGF0dGVybikpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdbSW5zdGFsbGF0aW9uRGV0ZWN0b3JdIERldGVjdGVkIG5wbSBpbnN0YWxsYXRpb24nKTtcbiAgICAgICAgdGhpcy5jYWNoZWRUeXBlID0gJ25wbSc7XG4gICAgICAgIHJldHVybiAnbnBtJztcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgZm9yIC5naXQgZGlyZWN0b3J5IChnaXQgaW5zdGFsbGF0aW9uKVxuICAgICAgLy8gU2VhcmNoIHVwIGZyb20gY3VycmVudCBkaXJlY3RvcnlcbiAgICAgIGxldCBzZWFyY2hEaXIgPSBjdXJyZW50RGlyO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLk1BWF9TRUFSQ0hfREVQVEg7IGkrKykge1xuICAgICAgICBjb25zdCBnaXREaXIgPSBwYXRoLmpvaW4oc2VhcmNoRGlyLCAnLmdpdCcpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmIChmaWxlU3lzdGVtLmV4aXN0c1N5bmMoZ2l0RGlyKSAmJiBmaWxlU3lzdGVtLnN0YXRTeW5jKGdpdERpcikuaXNEaXJlY3RvcnkoKSkge1xuICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKCdbSW5zdGFsbGF0aW9uRGV0ZWN0b3JdIERldGVjdGVkIGdpdCBpbnN0YWxsYXRpb24nKTtcbiAgICAgICAgICAgIHRoaXMuY2FjaGVkVHlwZSA9ICdnaXQnO1xuICAgICAgICAgICAgcmV0dXJuICdnaXQnO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gSWdub3JlIGVycm9ycyBhbmQgY29udGludWUgc2VhcmNoaW5nXG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBwYXJlbnREaXIgPSBwYXRoLmRpcm5hbWUoc2VhcmNoRGlyKTtcbiAgICAgICAgaWYgKHBhcmVudERpciA9PT0gc2VhcmNoRGlyKSB7XG4gICAgICAgICAgLy8gUmVhY2hlZCByb290IGRpcmVjdG9yeVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIHNlYXJjaERpciA9IHBhcmVudERpcjtcbiAgICAgIH1cblxuICAgICAgbG9nZ2VyLndhcm4oJ1tJbnN0YWxsYXRpb25EZXRlY3Rvcl0gQ291bGQgbm90IGRldGVybWluZSBpbnN0YWxsYXRpb24gdHlwZScpO1xuICAgICAgdGhpcy5jYWNoZWRUeXBlID0gJ3Vua25vd24nO1xuICAgICAgcmV0dXJuICd1bmtub3duJztcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdbSW5zdGFsbGF0aW9uRGV0ZWN0b3JdIEVycm9yIGRldGVjdGluZyBpbnN0YWxsYXRpb24gdHlwZTonLCBlcnJvcik7XG4gICAgICB0aGlzLmNhY2hlZFR5cGUgPSAndW5rbm93bic7XG4gICAgICByZXR1cm4gJ3Vua25vd24nO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCB0aGUgbnBtIGdsb2JhbCBpbnN0YWxsYXRpb24gcGF0aCBpZiBydW5uaW5nIGZyb20gbnBtXG4gICAqL1xuICBzdGF0aWMgZ2V0TnBtR2xvYmFsUGF0aCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICBpZiAodGhpcy5nZXRJbnN0YWxsYXRpb25UeXBlKCkgIT09ICducG0nKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBjb25zdCBmaWxlU3lzdGVtID0gdGhpcy5nZXRGcygpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGN1cnJlbnRGaWxlVXJsID0gdGhpcy5nZXRJbXBvcnRNZXRhVXJsKCk7XG4gICAgICBjb25zdCBjdXJyZW50RmlsZVBhdGggPSBmaWxlVVJMVG9QYXRoKGN1cnJlbnRGaWxlVXJsKTtcbiAgICAgIGxldCBjdXJyZW50RGlyID0gcGF0aC5kaXJuYW1lKGN1cnJlbnRGaWxlUGF0aCk7XG5cbiAgICAgIC8vIEZpbmQgdGhlIHJvb3Qgb2YgdGhlIG5wbSBwYWNrYWdlXG4gICAgICAvLyBOb3JtYWxpemUgcGF0aHMgdG8gaGFuZGxlIFdpbmRvd3MgYW5kIFVuaXggc2VwYXJhdG9yc1xuICAgICAgY29uc3QgbnBtUGFja2FnZVBhdHRlcm4gPSBwYXRoLm5vcm1hbGl6ZSgnbm9kZV9tb2R1bGVzL0Bkb2xsaG91c2VtY3AvbWNwLXNlcnZlcicpO1xuICAgICAgd2hpbGUgKHBhdGgubm9ybWFsaXplKGN1cnJlbnREaXIpLmluY2x1ZGVzKG5wbVBhY2thZ2VQYXR0ZXJuKSkge1xuICAgICAgICBjb25zdCBwYWNrYWdlSnNvblBhdGggPSBwYXRoLmpvaW4oY3VycmVudERpciwgJ3BhY2thZ2UuanNvbicpO1xuICAgICAgICBpZiAoZmlsZVN5c3RlbS5leGlzdHNTeW5jKHBhY2thZ2VKc29uUGF0aCkpIHtcbiAgICAgICAgICByZXR1cm4gY3VycmVudERpcjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBwYXJlbnREaXIgPSBwYXRoLmRpcm5hbWUoY3VycmVudERpcik7XG4gICAgICAgIGlmIChwYXJlbnREaXIgPT09IGN1cnJlbnREaXIpIGJyZWFrO1xuICAgICAgICBjdXJyZW50RGlyID0gcGFyZW50RGlyO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ1tJbnN0YWxsYXRpb25EZXRlY3Rvcl0gRXJyb3IgZmluZGluZyBucG0gZ2xvYmFsIHBhdGg6JywgZXJyb3IpO1xuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IHRoZSBnaXQgcmVwb3NpdG9yeSByb290IHBhdGggaWYgcnVubmluZyBmcm9tIGdpdFxuICAgKi9cbiAgc3RhdGljIGdldEdpdFJlcG9zaXRvcnlQYXRoKCk6IHN0cmluZyB8IG51bGwge1xuICAgIGlmICh0aGlzLmdldEluc3RhbGxhdGlvblR5cGUoKSAhPT0gJ2dpdCcpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGZpbGVTeXN0ZW0gPSB0aGlzLmdldEZzKCk7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgY3VycmVudEZpbGVVcmwgPSB0aGlzLmdldEltcG9ydE1ldGFVcmwoKTtcbiAgICAgIGNvbnN0IGN1cnJlbnRGaWxlUGF0aCA9IGZpbGVVUkxUb1BhdGgoY3VycmVudEZpbGVVcmwpO1xuICAgICAgbGV0IGN1cnJlbnREaXIgPSBwYXRoLmRpcm5hbWUoY3VycmVudEZpbGVQYXRoKTtcblxuICAgICAgLy8gU2VhcmNoIHVwIGZvciAuZ2l0IGRpcmVjdG9yeVxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLk1BWF9TRUFSQ0hfREVQVEg7IGkrKykge1xuICAgICAgICBjb25zdCBnaXREaXIgPSBwYXRoLmpvaW4oY3VycmVudERpciwgJy5naXQnKTtcbiAgICAgICAgaWYgKGZpbGVTeXN0ZW0uZXhpc3RzU3luYyhnaXREaXIpICYmIGZpbGVTeXN0ZW0uc3RhdFN5bmMoZ2l0RGlyKS5pc0RpcmVjdG9yeSgpKSB7XG4gICAgICAgICAgcmV0dXJuIGN1cnJlbnREaXI7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBwYXJlbnREaXIgPSBwYXRoLmRpcm5hbWUoY3VycmVudERpcik7XG4gICAgICAgIGlmIChwYXJlbnREaXIgPT09IGN1cnJlbnREaXIpIGJyZWFrO1xuICAgICAgICBjdXJyZW50RGlyID0gcGFyZW50RGlyO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ1tJbnN0YWxsYXRpb25EZXRlY3Rvcl0gRXJyb3IgZmluZGluZyBnaXQgcmVwb3NpdG9yeSBwYXRoOicsIGVycm9yKTtcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBhIGh1bWFuLXJlYWRhYmxlIGRlc2NyaXB0aW9uIG9mIHRoZSBpbnN0YWxsYXRpb25cbiAgICovXG4gIHN0YXRpYyBnZXRJbnN0YWxsYXRpb25EZXNjcmlwdGlvbigpOiBzdHJpbmcge1xuICAgIGNvbnN0IHR5cGUgPSB0aGlzLmdldEluc3RhbGxhdGlvblR5cGUoKTtcbiAgICBcbiAgICBzd2l0Y2ggKHR5cGUpIHtcbiAgICAgIGNhc2UgJ25wbSc6IHtcbiAgICAgICAgY29uc3QgbnBtUGF0aCA9IHRoaXMuZ2V0TnBtR2xvYmFsUGF0aCgpO1xuICAgICAgICByZXR1cm4gbnBtUGF0aFxuICAgICAgICAgID8gYG5wbSBnbG9iYWwgaW5zdGFsbGF0aW9uIGF0ICR7bnBtUGF0aH1gXG4gICAgICAgICAgOiAnbnBtIGdsb2JhbCBpbnN0YWxsYXRpb24nO1xuICAgICAgfVxuXG4gICAgICBjYXNlICdnaXQnOiB7XG4gICAgICAgIGNvbnN0IGdpdFBhdGggPSB0aGlzLmdldEdpdFJlcG9zaXRvcnlQYXRoKCk7XG4gICAgICAgIHJldHVybiBnaXRQYXRoXG4gICAgICAgICAgPyBgZ2l0IGluc3RhbGxhdGlvbiBhdCAke2dpdFBhdGh9YFxuICAgICAgICAgIDogJ2dpdCBpbnN0YWxsYXRpb24nO1xuICAgICAgfVxuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gJ3Vua25vd24gaW5zdGFsbGF0aW9uIHR5cGUnO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENsZWFyIHRoZSBjYWNoZWQgaW5zdGFsbGF0aW9uIHR5cGUgKG1haW5seSBmb3IgdGVzdGluZylcbiAgICovXG4gIHN0YXRpYyBjbGVhckNhY2hlKCk6IHZvaWQge1xuICAgIHRoaXMuY2FjaGVkVHlwZSA9IG51bGw7XG4gICAgdGhpcy5mc092ZXJyaWRlID0gbnVsbDtcbiAgICB0aGlzLmltcG9ydE1ldGFVcmxPdmVycmlkZSA9IG51bGw7XG4gIH1cbn0iXX0=