@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.
160 lines • 19.8 kB
JavaScript
/**
* 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';
import { UpdateConfigManager } from '../config/updateConfig.js';
export class InstallationDetector {
static cachedType = null;
// Maximum directory levels to search upward for .git directory
static get MAX_SEARCH_DEPTH() {
return UpdateConfigManager.getInstance().getMaxSearchDepth();
}
/**
* Detect the installation type (npm global, git clone, or unknown)
*/
static getInstallationType() {
// Return cached result if available
if (this.cachedType !== null) {
return this.cachedType;
}
try {
// Get the directory where this file is located
const currentFileUrl = import.meta.url;
const currentFilePath = fileURLToPath(currentFileUrl);
let currentDir = path.dirname(currentFilePath);
// Resolve symlinks to get the real path
try {
currentDir = fs.realpathSync(currentDir);
}
catch (error) {
// 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
const npmPattern = path.sep + path.join('node_modules', '@dollhousemcp', 'mcp-server') + path.sep;
if (currentDir.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 (fs.existsSync(gitDir) && fs.statSync(gitDir).isDirectory()) {
logger.debug('[InstallationDetector] Detected git installation');
this.cachedType = 'git';
return 'git';
}
}
catch (error) {
// 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;
}
try {
const currentFileUrl = import.meta.url;
const currentFilePath = fileURLToPath(currentFileUrl);
let currentDir = path.dirname(currentFilePath);
// Find the root of the npm package
while (currentDir.includes('node_modules/@dollhousemcp/mcp-server')) {
const packageJsonPath = path.join(currentDir, 'package.json');
if (fs.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;
}
try {
const currentFileUrl = import.meta.url;
const currentFilePath = fileURLToPath(currentFileUrl);
let currentDir = path.dirname(currentFilePath);
// Search up for .git directory
for (let i = 0; i < 10; i++) {
const gitDir = path.join(currentDir, '.git');
if (fs.existsSync(gitDir) && fs.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;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFsbGF0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL2luc3RhbGxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQztBQUN6QixPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDckMsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFJaEUsTUFBTSxPQUFPLG9CQUFvQjtJQUN2QixNQUFNLENBQUMsVUFBVSxHQUE0QixJQUFJLENBQUM7SUFFMUQsK0RBQStEO0lBQ3ZELE1BQU0sS0FBSyxnQkFBZ0I7UUFDakMsT0FBTyxtQkFBbUIsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO0lBQy9ELENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxtQkFBbUI7UUFDeEIsb0NBQW9DO1FBQ3BDLElBQUksSUFBSSxDQUFDLFVBQVUsS0FBSyxJQUFJLEVBQUUsQ0FBQztZQUM3QixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7UUFDekIsQ0FBQztRQUVELElBQUksQ0FBQztZQUNILCtDQUErQztZQUMvQyxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQztZQUN2QyxNQUFNLGVBQWUsR0FBRyxhQUFhLENBQUMsY0FBYyxDQUFDLENBQUM7WUFDdEQsSUFBSSxVQUFVLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztZQUUvQyx3Q0FBd0M7WUFDeEMsSUFBSSxDQUFDO2dCQUNILFVBQVUsR0FBRyxFQUFFLENBQUMsWUFBWSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQzNDLENBQUM7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Q0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7Z0JBQzNDLElBQUksU0FBUyxLQUFLLFVBQVU7b0JBQUUsTUFBTTtnQkFDcEMsVUFBVSxHQUFHLFNBQVMsQ0FBQztZQUN6QixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLENBQUMsS0FBSyxDQUFDLDJEQUEyRCxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ25GLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQywwQkFBMEI7UUFDL0IsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUM7UUFFeEMsUUFBUSxJQUFJLEVBQUUsQ0FBQztZQUNiLEtBQUssS0FBSztnQkFDUixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztnQkFDeEMsT0FBTyxPQUFPO29CQUNaLENBQUMsQ0FBQyw4QkFBOEIsT0FBTyxFQUFFO29CQUN6QyxDQUFDLENBQUMseUJBQXlCLENBQUM7WUFFaEMsS0FBSyxLQUFLO2dCQUNSLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO2dCQUM1QyxPQUFPLE9BQU87b0JBQ1osQ0FBQyxDQUFDLHVCQUF1QixPQUFPLEVBQUU7b0JBQ2xDLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQztZQUV6QjtnQkFDRSxPQUFPLDJCQUEyQixDQUFDO1FBQ3ZDLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsVUFBVTtRQUNmLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDO0lBQ3pCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEluc3RhbGxhdGlvbiBkZXRlY3Rpb24gdXRpbGl0aWVzXG4gKiBEZXRlcm1pbmVzIHdoZXRoZXIgdGhlIGFwcGxpY2F0aW9uIGlzIHJ1bm5pbmcgZnJvbSBucG0gb3IgZ2l0IGluc3RhbGxhdGlvblxuICovXG5cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4vbG9nZ2VyLmpzJztcbmltcG9ydCB7IFVwZGF0ZUNvbmZpZ01hbmFnZXIgfSBmcm9tICcuLi9jb25maWcvdXBkYXRlQ29uZmlnLmpzJztcblxuZXhwb3J0IHR5cGUgSW5zdGFsbGF0aW9uVHlwZSA9ICducG0nIHwgJ2dpdCcgfCAndW5rbm93bic7XG5cbmV4cG9ydCBjbGFzcyBJbnN0YWxsYXRpb25EZXRlY3RvciB7XG4gIHByaXZhdGUgc3RhdGljIGNhY2hlZFR5cGU6IEluc3RhbGxhdGlvblR5cGUgfCBudWxsID0gbnVsbDtcbiAgXG4gIC8vIE1heGltdW0gZGlyZWN0b3J5IGxldmVscyB0byBzZWFyY2ggdXB3YXJkIGZvciAuZ2l0IGRpcmVjdG9yeVxuICBwcml2YXRlIHN0YXRpYyBnZXQgTUFYX1NFQVJDSF9ERVBUSCgpOiBudW1iZXIge1xuICAgIHJldHVybiBVcGRhdGVDb25maWdNYW5hZ2VyLmdldEluc3RhbmNlKCkuZ2V0TWF4U2VhcmNoRGVwdGgoKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIERldGVjdCB0aGUgaW5zdGFsbGF0aW9uIHR5cGUgKG5wbSBnbG9iYWwsIGdpdCBjbG9uZSwgb3IgdW5rbm93bilcbiAgICovXG4gIHN0YXRpYyBnZXRJbnN0YWxsYXRpb25UeXBlKCk6IEluc3RhbGxhdGlvblR5cGUge1xuICAgIC8vIFJldHVybiBjYWNoZWQgcmVzdWx0IGlmIGF2YWlsYWJsZVxuICAgIGlmICh0aGlzLmNhY2hlZFR5cGUgIT09IG51bGwpIHtcbiAgICAgIHJldHVybiB0aGlzLmNhY2hlZFR5cGU7XG4gICAgfVxuICAgIFxuICAgIHRyeSB7XG4gICAgICAvLyBHZXQgdGhlIGRpcmVjdG9yeSB3aGVyZSB0aGlzIGZpbGUgaXMgbG9jYXRlZFxuICAgICAgY29uc3QgY3VycmVudEZpbGVVcmwgPSBpbXBvcnQubWV0YS51cmw7XG4gICAgICBjb25zdCBjdXJyZW50RmlsZVBhdGggPSBmaWxlVVJMVG9QYXRoKGN1cnJlbnRGaWxlVXJsKTtcbiAgICAgIGxldCBjdXJyZW50RGlyID0gcGF0aC5kaXJuYW1lKGN1cnJlbnRGaWxlUGF0aCk7XG4gICAgICBcbiAgICAgIC8vIFJlc29sdmUgc3ltbGlua3MgdG8gZ2V0IHRoZSByZWFsIHBhdGhcbiAgICAgIHRyeSB7XG4gICAgICAgIGN1cnJlbnREaXIgPSBmcy5yZWFscGF0aFN5bmMoY3VycmVudERpcik7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAvLyBJZiByZWFscGF0aCBmYWlscywgY29udGludWUgd2l0aCBvcmlnaW5hbCBwYXRoXG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnW0luc3RhbGxhdGlvbkRldGVjdG9yXSBDb3VsZCBub3QgcmVzb2x2ZSByZWFsIHBhdGgsIHVzaW5nIG9yaWdpbmFsJyk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGlmIHdlJ3JlIGluIGEgbm9kZV9tb2R1bGVzIGRpcmVjdG9yeSAobnBtIGluc3RhbGxhdGlvbilcbiAgICAgIC8vIFVzZSBwYXRoIHNlcGFyYXRvciB0byBlbnN1cmUgd2UgbWF0Y2ggdGhlIGV4YWN0IHBhY2thZ2UgbmFtZVxuICAgICAgY29uc3QgbnBtUGF0dGVybiA9IHBhdGguc2VwICsgcGF0aC5qb2luKCdub2RlX21vZHVsZXMnLCAnQGRvbGxob3VzZW1jcCcsICdtY3Atc2VydmVyJykgKyBwYXRoLnNlcDtcbiAgICAgIGlmIChjdXJyZW50RGlyLmluY2x1ZGVzKG5wbVBhdHRlcm4pKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnW0luc3RhbGxhdGlvbkRldGVjdG9yXSBEZXRlY3RlZCBucG0gaW5zdGFsbGF0aW9uJyk7XG4gICAgICAgIHRoaXMuY2FjaGVkVHlwZSA9ICducG0nO1xuICAgICAgICByZXR1cm4gJ25wbSc7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGZvciAuZ2l0IGRpcmVjdG9yeSAoZ2l0IGluc3RhbGxhdGlvbilcbiAgICAgIC8vIFNlYXJjaCB1cCBmcm9tIGN1cnJlbnQgZGlyZWN0b3J5XG4gICAgICBsZXQgc2VhcmNoRGlyID0gY3VycmVudERpcjtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5NQVhfU0VBUkNIX0RFUFRIOyBpKyspIHtcbiAgICAgICAgY29uc3QgZ2l0RGlyID0gcGF0aC5qb2luKHNlYXJjaERpciwgJy5naXQnKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBpZiAoZnMuZXhpc3RzU3luYyhnaXREaXIpICYmIGZzLnN0YXRTeW5jKGdpdERpcikuaXNEaXJlY3RvcnkoKSkge1xuICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKCdbSW5zdGFsbGF0aW9uRGV0ZWN0b3JdIERldGVjdGVkIGdpdCBpbnN0YWxsYXRpb24nKTtcbiAgICAgICAgICAgIHRoaXMuY2FjaGVkVHlwZSA9ICdnaXQnO1xuICAgICAgICAgICAgcmV0dXJuICdnaXQnO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAvLyBJZ25vcmUgZXJyb3JzIGFuZCBjb250aW51ZSBzZWFyY2hpbmdcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgY29uc3QgcGFyZW50RGlyID0gcGF0aC5kaXJuYW1lKHNlYXJjaERpcik7XG4gICAgICAgIGlmIChwYXJlbnREaXIgPT09IHNlYXJjaERpcikge1xuICAgICAgICAgIC8vIFJlYWNoZWQgcm9vdCBkaXJlY3RvcnlcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBzZWFyY2hEaXIgPSBwYXJlbnREaXI7XG4gICAgICB9XG4gICAgICBcbiAgICAgIGxvZ2dlci53YXJuKCdbSW5zdGFsbGF0aW9uRGV0ZWN0b3JdIENvdWxkIG5vdCBkZXRlcm1pbmUgaW5zdGFsbGF0aW9uIHR5cGUnKTtcbiAgICAgIHRoaXMuY2FjaGVkVHlwZSA9ICd1bmtub3duJztcbiAgICAgIHJldHVybiAndW5rbm93bic7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignW0luc3RhbGxhdGlvbkRldGVjdG9yXSBFcnJvciBkZXRlY3RpbmcgaW5zdGFsbGF0aW9uIHR5cGU6JywgZXJyb3IpO1xuICAgICAgdGhpcy5jYWNoZWRUeXBlID0gJ3Vua25vd24nO1xuICAgICAgcmV0dXJuICd1bmtub3duJztcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgdGhlIG5wbSBnbG9iYWwgaW5zdGFsbGF0aW9uIHBhdGggaWYgcnVubmluZyBmcm9tIG5wbVxuICAgKi9cbiAgc3RhdGljIGdldE5wbUdsb2JhbFBhdGgoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgaWYgKHRoaXMuZ2V0SW5zdGFsbGF0aW9uVHlwZSgpICE9PSAnbnBtJykge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIFxuICAgIHRyeSB7XG4gICAgICBjb25zdCBjdXJyZW50RmlsZVVybCA9IGltcG9ydC5tZXRhLnVybDtcbiAgICAgIGNvbnN0IGN1cnJlbnRGaWxlUGF0aCA9IGZpbGVVUkxUb1BhdGgoY3VycmVudEZpbGVVcmwpO1xuICAgICAgbGV0IGN1cnJlbnREaXIgPSBwYXRoLmRpcm5hbWUoY3VycmVudEZpbGVQYXRoKTtcbiAgICAgIFxuICAgICAgLy8gRmluZCB0aGUgcm9vdCBvZiB0aGUgbnBtIHBhY2thZ2VcbiAgICAgIHdoaWxlIChjdXJyZW50RGlyLmluY2x1ZGVzKCdub2RlX21vZHVsZXMvQGRvbGxob3VzZW1jcC9tY3Atc2VydmVyJykpIHtcbiAgICAgICAgY29uc3QgcGFja2FnZUpzb25QYXRoID0gcGF0aC5qb2luKGN1cnJlbnREaXIsICdwYWNrYWdlLmpzb24nKTtcbiAgICAgICAgaWYgKGZzLmV4aXN0c1N5bmMocGFja2FnZUpzb25QYXRoKSkge1xuICAgICAgICAgIHJldHVybiBjdXJyZW50RGlyO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHBhcmVudERpciA9IHBhdGguZGlybmFtZShjdXJyZW50RGlyKTtcbiAgICAgICAgaWYgKHBhcmVudERpciA9PT0gY3VycmVudERpcikgYnJlYWs7XG4gICAgICAgIGN1cnJlbnREaXIgPSBwYXJlbnREaXI7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignW0luc3RhbGxhdGlvbkRldGVjdG9yXSBFcnJvciBmaW5kaW5nIG5wbSBnbG9iYWwgcGF0aDonLCBlcnJvcik7XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IHRoZSBnaXQgcmVwb3NpdG9yeSByb290IHBhdGggaWYgcnVubmluZyBmcm9tIGdpdFxuICAgKi9cbiAgc3RhdGljIGdldEdpdFJlcG9zaXRvcnlQYXRoKCk6IHN0cmluZyB8IG51bGwge1xuICAgIGlmICh0aGlzLmdldEluc3RhbGxhdGlvblR5cGUoKSAhPT0gJ2dpdCcpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBcbiAgICB0cnkge1xuICAgICAgY29uc3QgY3VycmVudEZpbGVVcmwgPSBpbXBvcnQubWV0YS51cmw7XG4gICAgICBjb25zdCBjdXJyZW50RmlsZVBhdGggPSBmaWxlVVJMVG9QYXRoKGN1cnJlbnRGaWxlVXJsKTtcbiAgICAgIGxldCBjdXJyZW50RGlyID0gcGF0aC5kaXJuYW1lKGN1cnJlbnRGaWxlUGF0aCk7XG4gICAgICBcbiAgICAgIC8vIFNlYXJjaCB1cCBmb3IgLmdpdCBkaXJlY3RvcnlcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgMTA7IGkrKykge1xuICAgICAgICBjb25zdCBnaXREaXIgPSBwYXRoLmpvaW4oY3VycmVudERpciwgJy5naXQnKTtcbiAgICAgICAgaWYgKGZzLmV4aXN0c1N5bmMoZ2l0RGlyKSAmJiBmcy5zdGF0U3luYyhnaXREaXIpLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgICByZXR1cm4gY3VycmVudERpcjtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgY29uc3QgcGFyZW50RGlyID0gcGF0aC5kaXJuYW1lKGN1cnJlbnREaXIpO1xuICAgICAgICBpZiAocGFyZW50RGlyID09PSBjdXJyZW50RGlyKSBicmVhaztcbiAgICAgICAgY3VycmVudERpciA9IHBhcmVudERpcjtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdbSW5zdGFsbGF0aW9uRGV0ZWN0b3JdIEVycm9yIGZpbmRpbmcgZ2l0IHJlcG9zaXRvcnkgcGF0aDonLCBlcnJvcik7XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IGEgaHVtYW4tcmVhZGFibGUgZGVzY3JpcHRpb24gb2YgdGhlIGluc3RhbGxhdGlvblxuICAgKi9cbiAgc3RhdGljIGdldEluc3RhbGxhdGlvbkRlc2NyaXB0aW9uKCk6IHN0cmluZyB7XG4gICAgY29uc3QgdHlwZSA9IHRoaXMuZ2V0SW5zdGFsbGF0aW9uVHlwZSgpO1xuICAgIFxuICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgY2FzZSAnbnBtJzpcbiAgICAgICAgY29uc3QgbnBtUGF0aCA9IHRoaXMuZ2V0TnBtR2xvYmFsUGF0aCgpO1xuICAgICAgICByZXR1cm4gbnBtUGF0aCBcbiAgICAgICAgICA/IGBucG0gZ2xvYmFsIGluc3RhbGxhdGlvbiBhdCAke25wbVBhdGh9YFxuICAgICAgICAgIDogJ25wbSBnbG9iYWwgaW5zdGFsbGF0aW9uJztcbiAgICAgICAgICBcbiAgICAgIGNhc2UgJ2dpdCc6XG4gICAgICAgIGNvbnN0IGdpdFBhdGggPSB0aGlzLmdldEdpdFJlcG9zaXRvcnlQYXRoKCk7XG4gICAgICAgIHJldHVybiBnaXRQYXRoXG4gICAgICAgICAgPyBgZ2l0IGluc3RhbGxhdGlvbiBhdCAke2dpdFBhdGh9YFxuICAgICAgICAgIDogJ2dpdCBpbnN0YWxsYXRpb24nO1xuICAgICAgICAgIFxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuICd1bmtub3duIGluc3RhbGxhdGlvbiB0eXBlJztcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDbGVhciB0aGUgY2FjaGVkIGluc3RhbGxhdGlvbiB0eXBlIChtYWlubHkgZm9yIHRlc3RpbmcpXG4gICAqL1xuICBzdGF0aWMgY2xlYXJDYWNoZSgpOiB2b2lkIHtcbiAgICB0aGlzLmNhY2hlZFR5cGUgPSBudWxsO1xuICB9XG59Il19