@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.
162 lines • 20.4 kB
JavaScript
/**
* Portfolio configuration utilities
* Handles repository name configuration for portfolio sync
*/
import { logger } from '../utils/logger.js';
/**
* Validates a GitHub repository name against GitHub's naming requirements.
*
* GitHub repository names must:
* - Contain only alphanumeric characters, hyphens, underscores, and dots
* - Not contain slashes (use PORTFOLIO_REPOSITORY_NAME instead of owner/repo format)
* - Not be empty or only whitespace
*
* @param name - The repository name to validate
* @returns The trimmed repository name if valid
* @throws Error if the repository name is invalid with guidance on how to fix it
*
* @example
* // Valid repository names
* validateRepositoryName('dollhouse-portfolio'); // 'dollhouse-portfolio'
* validateRepositoryName('my_repo.test'); // 'my_repo.test'
* validateRepositoryName(' repo-123 '); // 'repo-123' (trimmed)
*
* @example
* // Invalid repository names (throw errors)
* validateRepositoryName('owner/repo'); // Error: contains slash
* validateRepositoryName(''); // Error: empty
* validateRepositoryName('repo@name'); // Error: invalid character
*/
export function validateRepositoryName(name) {
const trimmed = name?.trim() ?? '';
// Check for empty or whitespace-only
if (!trimmed) {
throw new Error(`Repository name cannot be empty.\n` +
`Please set PORTFOLIO_REPOSITORY_NAME to a valid GitHub repository name.\n` +
`Example: PORTFOLIO_REPOSITORY_NAME=dollhouse-portfolio`);
}
// Check for slash character (common mistake with GITHUB_REPOSITORY in CI)
if (trimmed.includes('/')) {
throw new Error(`Repository name cannot contain '/' character.\n` +
`Expected: Just the repository name (e.g., 'dollhouse-portfolio')\n` +
`Got: '${trimmed}'\n\n` +
`It looks like you're using GitHub Actions' GITHUB_REPOSITORY format (owner/repo).\n` +
`For portfolio configuration, use PORTFOLIO_REPOSITORY_NAME with just the repo name:\n\n` +
` PORTFOLIO_REPOSITORY_NAME=dollhouse-portfolio\n\n` +
`The username should be specified separately in GITHUB_USERNAME.`);
}
// Check for invalid characters (GitHub allows: a-z, A-Z, 0-9, -, _, .)
const validPattern = /^[a-zA-Z0-9._-]+$/;
if (!validPattern.test(trimmed)) {
throw new Error(`Repository name contains invalid characters.\n` +
`Got: '${trimmed}'\n` +
`Allowed characters: letters (a-z, A-Z), numbers (0-9), hyphens (-), underscores (_), dots (.)\n` +
`Example: PORTFOLIO_REPOSITORY_NAME=my-portfolio_v2.0`);
}
return trimmed;
}
/**
* Get the configured portfolio repository name.
*
* Priority order:
* 1. PORTFOLIO_REPOSITORY_NAME environment variable (preferred, dedicated config)
* 2. GITHUB_REPOSITORY environment variable (legacy, deprecated for portfolio use)
* - Only used if it doesn't contain '/' (not in GitHub Actions "owner/repo" format)
* - Shows deprecation warning
* 3. TEST_GITHUB_REPO environment variable (deprecated, backward compatibility)
* 4. GITHUB_TEST_REPO environment variable (deprecated, backward compatibility)
* 5. TEST_PORTFOLIO_REPO environment variable (deprecated, backward compatibility)
* 6. Default: 'dollhouse-portfolio'
*
* @returns The configured portfolio repository name (validated)
* @throws Error if any configured name is invalid
*
* @example
* // Default usage (no env vars set)
* const repo = getPortfolioRepositoryName(); // 'dollhouse-portfolio'
*
* @example
* // With PORTFOLIO_REPOSITORY_NAME (preferred)
* process.env.PORTFOLIO_REPOSITORY_NAME = 'my-portfolio';
* const repo = getPortfolioRepositoryName(); // 'my-portfolio'
*
* @example
* // With legacy GITHUB_REPOSITORY (shows deprecation warning)
* process.env.GITHUB_REPOSITORY = 'my-portfolio';
* const repo = getPortfolioRepositoryName(); // 'my-portfolio' + warning
*
* @example
* // Error: GITHUB_REPOSITORY in GitHub Actions format
* process.env.GITHUB_REPOSITORY = 'owner/repo';
* getPortfolioRepositoryName(); // throws Error with migration guidance
*/
export function getPortfolioRepositoryName() {
// 1. Check preferred dedicated variable first
let repo = process.env.PORTFOLIO_REPOSITORY_NAME?.trim();
if (repo) {
return validateRepositoryName(repo);
}
// 2. Check legacy GITHUB_REPOSITORY (with deprecation warning and validation)
repo = process.env.GITHUB_REPOSITORY?.trim();
if (repo) {
// If it contains '/', it's in GitHub Actions "owner/repo" format
// Skip it silently and fall through to other options or defaults
if (repo.includes('/')) {
logger.debug(`Ignoring GITHUB_REPOSITORY='${repo}' (GitHub Actions owner/repo format). ` +
`Use PORTFOLIO_REPOSITORY_NAME for portfolio configuration.`);
}
else {
// Valid repo name format, but show deprecation warning
logger.warn('⚠️ DEPRECATED: Using GITHUB_REPOSITORY for portfolio configuration is deprecated.\n' +
' Please use PORTFOLIO_REPOSITORY_NAME instead to avoid conflicts with GitHub Actions.\n' +
' Add to your .env.local: PORTFOLIO_REPOSITORY_NAME=' + repo);
return validateRepositoryName(repo);
}
}
// 3-5. Backward compatibility: Check for old variable names
repo = process.env.TEST_GITHUB_REPO?.trim();
if (repo) {
logger.warn('⚠️ DEPRECATED: TEST_GITHUB_REPO is deprecated. Use PORTFOLIO_REPOSITORY_NAME for production or GITHUB_TEST_REPOSITORY for tests.');
return validateRepositoryName(repo);
}
repo = process.env.GITHUB_TEST_REPO?.trim();
if (repo) {
logger.warn('⚠️ DEPRECATED: GITHUB_TEST_REPO is deprecated. Use PORTFOLIO_REPOSITORY_NAME for production or GITHUB_TEST_REPOSITORY for tests.');
return validateRepositoryName(repo);
}
repo = process.env.TEST_PORTFOLIO_REPO?.trim();
if (repo) {
logger.warn('⚠️ DEPRECATED: TEST_PORTFOLIO_REPO is deprecated. Use PORTFOLIO_REPOSITORY_NAME for production or GITHUB_TEST_REPOSITORY for tests.');
return validateRepositoryName(repo);
}
// TODO: Add support for reading from config file
// const config = ConfigManager.getInstance();
// if (config.github?.portfolio?.repository_name) {
// return validateRepositoryName(config.github.portfolio.repository_name);
// }
// 6. Default repository name
return 'dollhouse-portfolio';
}
/**
* Check if we're in a test environment
*
* @example
* // When NODE_ENV=test
* process.env.NODE_ENV = 'test';
* const isTest = isTestEnvironment(); // true
*
* @example
* // In production environment
* process.env.NODE_ENV = 'production';
* const isTest = isTestEnvironment(); // false
*
* @returns {boolean} True if running in a test environment
*/
export function isTestEnvironment() {
return process.env.TEST_MODE === 'true' ||
process.env.NODE_ENV === 'test' ||
// Backward compatibility: old test detection
!!process.env.TEST_GITHUB_REPO?.trim() ||
!!process.env.GITHUB_TEST_TOKEN?.trim();
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9ydGZvbGlvQ29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbmZpZy9wb3J0Zm9saW9Db25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRTVDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXVCRztBQUNILE1BQU0sVUFBVSxzQkFBc0IsQ0FBQyxJQUF3QjtJQUM3RCxNQUFNLE9BQU8sR0FBRyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDO0lBRW5DLHFDQUFxQztJQUNyQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDYixNQUFNLElBQUksS0FBSyxDQUNiLG9DQUFvQztZQUNwQywyRUFBMkU7WUFDM0Usd0RBQXdELENBQ3pELENBQUM7SUFDSixDQUFDO0lBRUQsMEVBQTBFO0lBQzFFLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQzFCLE1BQU0sSUFBSSxLQUFLLENBQ2IsaURBQWlEO1lBQ2pELG9FQUFvRTtZQUNwRSxTQUFTLE9BQU8sT0FBTztZQUN2QixxRkFBcUY7WUFDckYseUZBQXlGO1lBQ3pGLHFEQUFxRDtZQUNyRCxpRUFBaUUsQ0FDbEUsQ0FBQztJQUNKLENBQUM7SUFFRCx1RUFBdUU7SUFDdkUsTUFBTSxZQUFZLEdBQUcsbUJBQW1CLENBQUM7SUFDekMsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUNoQyxNQUFNLElBQUksS0FBSyxDQUNiLGdEQUFnRDtZQUNoRCxTQUFTLE9BQU8sS0FBSztZQUNyQixpR0FBaUc7WUFDakcsc0RBQXNELENBQ3ZELENBQUM7SUFDSixDQUFDO0lBRUQsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0NHO0FBQ0gsTUFBTSxVQUFVLDBCQUEwQjtJQUN4Qyw4Q0FBOEM7SUFDOUMsSUFBSSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyx5QkFBeUIsRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUN6RCxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ1QsT0FBTyxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0QyxDQUFDO0lBRUQsOEVBQThFO0lBQzlFLElBQUksR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixFQUFFLElBQUksRUFBRSxDQUFDO0lBQzdDLElBQUksSUFBSSxFQUFFLENBQUM7UUFDVCxpRUFBaUU7UUFDakUsaUVBQWlFO1FBQ2pFLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3ZCLE1BQU0sQ0FBQyxLQUFLLENBQ1YsK0JBQStCLElBQUksd0NBQXdDO2dCQUMzRSw0REFBNEQsQ0FDN0QsQ0FBQztRQUNKLENBQUM7YUFBTSxDQUFDO1lBQ04sdURBQXVEO1lBQ3ZELE1BQU0sQ0FBQyxJQUFJLENBQ1Qsc0ZBQXNGO2dCQUN0RiwyRkFBMkY7Z0JBQzNGLHVEQUF1RCxHQUFHLElBQUksQ0FDL0QsQ0FBQztZQUVGLE9BQU8sc0JBQXNCLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdEMsQ0FBQztJQUNILENBQUM7SUFFRCw0REFBNEQ7SUFDNUQsSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLEVBQUUsSUFBSSxFQUFFLENBQUM7SUFDNUMsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUNULE1BQU0sQ0FBQyxJQUFJLENBQUMsbUlBQW1JLENBQUMsQ0FBQztRQUNqSixPQUFPLHNCQUFzQixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFFRCxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUM1QyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ1QsTUFBTSxDQUFDLElBQUksQ0FBQyxtSUFBbUksQ0FBQyxDQUFDO1FBQ2pKLE9BQU8sc0JBQXNCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVELElBQUksR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixFQUFFLElBQUksRUFBRSxDQUFDO0lBQy9DLElBQUksSUFBSSxFQUFFLENBQUM7UUFDVCxNQUFNLENBQUMsSUFBSSxDQUFDLHNJQUFzSSxDQUFDLENBQUM7UUFDcEosT0FBTyxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0QyxDQUFDO0lBRUQsaURBQWlEO0lBQ2pELDhDQUE4QztJQUM5QyxtREFBbUQ7SUFDbkQsNEVBQTRFO0lBQzVFLElBQUk7SUFFSiw2QkFBNkI7SUFDN0IsT0FBTyxxQkFBcUIsQ0FBQztBQUMvQixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxNQUFNLFVBQVUsaUJBQWlCO0lBQy9CLE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLEtBQUssTUFBTTtRQUNoQyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsS0FBSyxNQUFNO1FBQy9CLDZDQUE2QztRQUM3QyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsRUFBRSxJQUFJLEVBQUU7UUFDdEMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDakQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUG9ydGZvbGlvIGNvbmZpZ3VyYXRpb24gdXRpbGl0aWVzXG4gKiBIYW5kbGVzIHJlcG9zaXRvcnkgbmFtZSBjb25maWd1cmF0aW9uIGZvciBwb3J0Zm9saW8gc3luY1xuICovXG5cbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5cbi8qKlxuICogVmFsaWRhdGVzIGEgR2l0SHViIHJlcG9zaXRvcnkgbmFtZSBhZ2FpbnN0IEdpdEh1YidzIG5hbWluZyByZXF1aXJlbWVudHMuXG4gKlxuICogR2l0SHViIHJlcG9zaXRvcnkgbmFtZXMgbXVzdDpcbiAqIC0gQ29udGFpbiBvbmx5IGFscGhhbnVtZXJpYyBjaGFyYWN0ZXJzLCBoeXBoZW5zLCB1bmRlcnNjb3JlcywgYW5kIGRvdHNcbiAqIC0gTm90IGNvbnRhaW4gc2xhc2hlcyAodXNlIFBPUlRGT0xJT19SRVBPU0lUT1JZX05BTUUgaW5zdGVhZCBvZiBvd25lci9yZXBvIGZvcm1hdClcbiAqIC0gTm90IGJlIGVtcHR5IG9yIG9ubHkgd2hpdGVzcGFjZVxuICpcbiAqIEBwYXJhbSBuYW1lIC0gVGhlIHJlcG9zaXRvcnkgbmFtZSB0byB2YWxpZGF0ZVxuICogQHJldHVybnMgVGhlIHRyaW1tZWQgcmVwb3NpdG9yeSBuYW1lIGlmIHZhbGlkXG4gKiBAdGhyb3dzIEVycm9yIGlmIHRoZSByZXBvc2l0b3J5IG5hbWUgaXMgaW52YWxpZCB3aXRoIGd1aWRhbmNlIG9uIGhvdyB0byBmaXggaXRcbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gVmFsaWQgcmVwb3NpdG9yeSBuYW1lc1xuICogdmFsaWRhdGVSZXBvc2l0b3J5TmFtZSgnZG9sbGhvdXNlLXBvcnRmb2xpbycpOyAvLyAnZG9sbGhvdXNlLXBvcnRmb2xpbydcbiAqIHZhbGlkYXRlUmVwb3NpdG9yeU5hbWUoJ215X3JlcG8udGVzdCcpOyAgICAgICAgLy8gJ215X3JlcG8udGVzdCdcbiAqIHZhbGlkYXRlUmVwb3NpdG9yeU5hbWUoJyAgcmVwby0xMjMgICcpOyAgICAgICAgLy8gJ3JlcG8tMTIzJyAodHJpbW1lZClcbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gSW52YWxpZCByZXBvc2l0b3J5IG5hbWVzICh0aHJvdyBlcnJvcnMpXG4gKiB2YWxpZGF0ZVJlcG9zaXRvcnlOYW1lKCdvd25lci9yZXBvJyk7ICAgICAgICAgIC8vIEVycm9yOiBjb250YWlucyBzbGFzaFxuICogdmFsaWRhdGVSZXBvc2l0b3J5TmFtZSgnJyk7ICAgICAgICAgICAgICAgICAgICAvLyBFcnJvcjogZW1wdHlcbiAqIHZhbGlkYXRlUmVwb3NpdG9yeU5hbWUoJ3JlcG9AbmFtZScpOyAgICAgICAgICAgLy8gRXJyb3I6IGludmFsaWQgY2hhcmFjdGVyXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZVJlcG9zaXRvcnlOYW1lKG5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZCk6IHN0cmluZyB7XG4gIGNvbnN0IHRyaW1tZWQgPSBuYW1lPy50cmltKCkgPz8gJyc7XG5cbiAgLy8gQ2hlY2sgZm9yIGVtcHR5IG9yIHdoaXRlc3BhY2Utb25seVxuICBpZiAoIXRyaW1tZWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgUmVwb3NpdG9yeSBuYW1lIGNhbm5vdCBiZSBlbXB0eS5cXG5gICtcbiAgICAgIGBQbGVhc2Ugc2V0IFBPUlRGT0xJT19SRVBPU0lUT1JZX05BTUUgdG8gYSB2YWxpZCBHaXRIdWIgcmVwb3NpdG9yeSBuYW1lLlxcbmAgK1xuICAgICAgYEV4YW1wbGU6IFBPUlRGT0xJT19SRVBPU0lUT1JZX05BTUU9ZG9sbGhvdXNlLXBvcnRmb2xpb2BcbiAgICApO1xuICB9XG5cbiAgLy8gQ2hlY2sgZm9yIHNsYXNoIGNoYXJhY3RlciAoY29tbW9uIG1pc3Rha2Ugd2l0aCBHSVRIVUJfUkVQT1NJVE9SWSBpbiBDSSlcbiAgaWYgKHRyaW1tZWQuaW5jbHVkZXMoJy8nKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBSZXBvc2l0b3J5IG5hbWUgY2Fubm90IGNvbnRhaW4gJy8nIGNoYXJhY3Rlci5cXG5gICtcbiAgICAgIGBFeHBlY3RlZDogSnVzdCB0aGUgcmVwb3NpdG9yeSBuYW1lIChlLmcuLCAnZG9sbGhvdXNlLXBvcnRmb2xpbycpXFxuYCArXG4gICAgICBgR290OiAnJHt0cmltbWVkfSdcXG5cXG5gICtcbiAgICAgIGBJdCBsb29rcyBsaWtlIHlvdSdyZSB1c2luZyBHaXRIdWIgQWN0aW9ucycgR0lUSFVCX1JFUE9TSVRPUlkgZm9ybWF0IChvd25lci9yZXBvKS5cXG5gICtcbiAgICAgIGBGb3IgcG9ydGZvbGlvIGNvbmZpZ3VyYXRpb24sIHVzZSBQT1JURk9MSU9fUkVQT1NJVE9SWV9OQU1FIHdpdGgganVzdCB0aGUgcmVwbyBuYW1lOlxcblxcbmAgK1xuICAgICAgYCAgUE9SVEZPTElPX1JFUE9TSVRPUllfTkFNRT1kb2xsaG91c2UtcG9ydGZvbGlvXFxuXFxuYCArXG4gICAgICBgVGhlIHVzZXJuYW1lIHNob3VsZCBiZSBzcGVjaWZpZWQgc2VwYXJhdGVseSBpbiBHSVRIVUJfVVNFUk5BTUUuYFxuICAgICk7XG4gIH1cblxuICAvLyBDaGVjayBmb3IgaW52YWxpZCBjaGFyYWN0ZXJzIChHaXRIdWIgYWxsb3dzOiBhLXosIEEtWiwgMC05LCAtLCBfLCAuKVxuICBjb25zdCB2YWxpZFBhdHRlcm4gPSAvXlthLXpBLVowLTkuXy1dKyQvO1xuICBpZiAoIXZhbGlkUGF0dGVybi50ZXN0KHRyaW1tZWQpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYFJlcG9zaXRvcnkgbmFtZSBjb250YWlucyBpbnZhbGlkIGNoYXJhY3RlcnMuXFxuYCArXG4gICAgICBgR290OiAnJHt0cmltbWVkfSdcXG5gICtcbiAgICAgIGBBbGxvd2VkIGNoYXJhY3RlcnM6IGxldHRlcnMgKGEteiwgQS1aKSwgbnVtYmVycyAoMC05KSwgaHlwaGVucyAoLSksIHVuZGVyc2NvcmVzIChfKSwgZG90cyAoLilcXG5gICtcbiAgICAgIGBFeGFtcGxlOiBQT1JURk9MSU9fUkVQT1NJVE9SWV9OQU1FPW15LXBvcnRmb2xpb192Mi4wYFxuICAgICk7XG4gIH1cblxuICByZXR1cm4gdHJpbW1lZDtcbn1cblxuLyoqXG4gKiBHZXQgdGhlIGNvbmZpZ3VyZWQgcG9ydGZvbGlvIHJlcG9zaXRvcnkgbmFtZS5cbiAqXG4gKiBQcmlvcml0eSBvcmRlcjpcbiAqIDEuIFBPUlRGT0xJT19SRVBPU0lUT1JZX05BTUUgZW52aXJvbm1lbnQgdmFyaWFibGUgKHByZWZlcnJlZCwgZGVkaWNhdGVkIGNvbmZpZylcbiAqIDIuIEdJVEhVQl9SRVBPU0lUT1JZIGVudmlyb25tZW50IHZhcmlhYmxlIChsZWdhY3ksIGRlcHJlY2F0ZWQgZm9yIHBvcnRmb2xpbyB1c2UpXG4gKiAgICAtIE9ubHkgdXNlZCBpZiBpdCBkb2Vzbid0IGNvbnRhaW4gJy8nIChub3QgaW4gR2l0SHViIEFjdGlvbnMgXCJvd25lci9yZXBvXCIgZm9ybWF0KVxuICogICAgLSBTaG93cyBkZXByZWNhdGlvbiB3YXJuaW5nXG4gKiAzLiBURVNUX0dJVEhVQl9SRVBPIGVudmlyb25tZW50IHZhcmlhYmxlIChkZXByZWNhdGVkLCBiYWNrd2FyZCBjb21wYXRpYmlsaXR5KVxuICogNC4gR0lUSFVCX1RFU1RfUkVQTyBlbnZpcm9ubWVudCB2YXJpYWJsZSAoZGVwcmVjYXRlZCwgYmFja3dhcmQgY29tcGF0aWJpbGl0eSlcbiAqIDUuIFRFU1RfUE9SVEZPTElPX1JFUE8gZW52aXJvbm1lbnQgdmFyaWFibGUgKGRlcHJlY2F0ZWQsIGJhY2t3YXJkIGNvbXBhdGliaWxpdHkpXG4gKiA2LiBEZWZhdWx0OiAnZG9sbGhvdXNlLXBvcnRmb2xpbydcbiAqXG4gKiBAcmV0dXJucyBUaGUgY29uZmlndXJlZCBwb3J0Zm9saW8gcmVwb3NpdG9yeSBuYW1lICh2YWxpZGF0ZWQpXG4gKiBAdGhyb3dzIEVycm9yIGlmIGFueSBjb25maWd1cmVkIG5hbWUgaXMgaW52YWxpZFxuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBEZWZhdWx0IHVzYWdlIChubyBlbnYgdmFycyBzZXQpXG4gKiBjb25zdCByZXBvID0gZ2V0UG9ydGZvbGlvUmVwb3NpdG9yeU5hbWUoKTsgLy8gJ2RvbGxob3VzZS1wb3J0Zm9saW8nXG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIFdpdGggUE9SVEZPTElPX1JFUE9TSVRPUllfTkFNRSAocHJlZmVycmVkKVxuICogcHJvY2Vzcy5lbnYuUE9SVEZPTElPX1JFUE9TSVRPUllfTkFNRSA9ICdteS1wb3J0Zm9saW8nO1xuICogY29uc3QgcmVwbyA9IGdldFBvcnRmb2xpb1JlcG9zaXRvcnlOYW1lKCk7IC8vICdteS1wb3J0Zm9saW8nXG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIFdpdGggbGVnYWN5IEdJVEhVQl9SRVBPU0lUT1JZIChzaG93cyBkZXByZWNhdGlvbiB3YXJuaW5nKVxuICogcHJvY2Vzcy5lbnYuR0lUSFVCX1JFUE9TSVRPUlkgPSAnbXktcG9ydGZvbGlvJztcbiAqIGNvbnN0IHJlcG8gPSBnZXRQb3J0Zm9saW9SZXBvc2l0b3J5TmFtZSgpOyAvLyAnbXktcG9ydGZvbGlvJyArIHdhcm5pbmdcbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gRXJyb3I6IEdJVEhVQl9SRVBPU0lUT1JZIGluIEdpdEh1YiBBY3Rpb25zIGZvcm1hdFxuICogcHJvY2Vzcy5lbnYuR0lUSFVCX1JFUE9TSVRPUlkgPSAnb3duZXIvcmVwbyc7XG4gKiBnZXRQb3J0Zm9saW9SZXBvc2l0b3J5TmFtZSgpOyAvLyB0aHJvd3MgRXJyb3Igd2l0aCBtaWdyYXRpb24gZ3VpZGFuY2VcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFBvcnRmb2xpb1JlcG9zaXRvcnlOYW1lKCk6IHN0cmluZyB7XG4gIC8vIDEuIENoZWNrIHByZWZlcnJlZCBkZWRpY2F0ZWQgdmFyaWFibGUgZmlyc3RcbiAgbGV0IHJlcG8gPSBwcm9jZXNzLmVudi5QT1JURk9MSU9fUkVQT1NJVE9SWV9OQU1FPy50cmltKCk7XG4gIGlmIChyZXBvKSB7XG4gICAgcmV0dXJuIHZhbGlkYXRlUmVwb3NpdG9yeU5hbWUocmVwbyk7XG4gIH1cblxuICAvLyAyLiBDaGVjayBsZWdhY3kgR0lUSFVCX1JFUE9TSVRPUlkgKHdpdGggZGVwcmVjYXRpb24gd2FybmluZyBhbmQgdmFsaWRhdGlvbilcbiAgcmVwbyA9IHByb2Nlc3MuZW52LkdJVEhVQl9SRVBPU0lUT1JZPy50cmltKCk7XG4gIGlmIChyZXBvKSB7XG4gICAgLy8gSWYgaXQgY29udGFpbnMgJy8nLCBpdCdzIGluIEdpdEh1YiBBY3Rpb25zIFwib3duZXIvcmVwb1wiIGZvcm1hdFxuICAgIC8vIFNraXAgaXQgc2lsZW50bHkgYW5kIGZhbGwgdGhyb3VnaCB0byBvdGhlciBvcHRpb25zIG9yIGRlZmF1bHRzXG4gICAgaWYgKHJlcG8uaW5jbHVkZXMoJy8nKSkge1xuICAgICAgbG9nZ2VyLmRlYnVnKFxuICAgICAgICBgSWdub3JpbmcgR0lUSFVCX1JFUE9TSVRPUlk9JyR7cmVwb30nIChHaXRIdWIgQWN0aW9ucyBvd25lci9yZXBvIGZvcm1hdCkuIGAgK1xuICAgICAgICBgVXNlIFBPUlRGT0xJT19SRVBPU0lUT1JZX05BTUUgZm9yIHBvcnRmb2xpbyBjb25maWd1cmF0aW9uLmBcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFZhbGlkIHJlcG8gbmFtZSBmb3JtYXQsIGJ1dCBzaG93IGRlcHJlY2F0aW9uIHdhcm5pbmdcbiAgICAgIGxvZ2dlci53YXJuKFxuICAgICAgICAn4pqg77iPICBERVBSRUNBVEVEOiBVc2luZyBHSVRIVUJfUkVQT1NJVE9SWSBmb3IgcG9ydGZvbGlvIGNvbmZpZ3VyYXRpb24gaXMgZGVwcmVjYXRlZC5cXG4nICtcbiAgICAgICAgJyAgIFBsZWFzZSB1c2UgUE9SVEZPTElPX1JFUE9TSVRPUllfTkFNRSBpbnN0ZWFkIHRvIGF2b2lkIGNvbmZsaWN0cyB3aXRoIEdpdEh1YiBBY3Rpb25zLlxcbicgK1xuICAgICAgICAnICAgQWRkIHRvIHlvdXIgLmVudi5sb2NhbDogUE9SVEZPTElPX1JFUE9TSVRPUllfTkFNRT0nICsgcmVwb1xuICAgICAgKTtcblxuICAgICAgcmV0dXJuIHZhbGlkYXRlUmVwb3NpdG9yeU5hbWUocmVwbyk7XG4gICAgfVxuICB9XG5cbiAgLy8gMy01LiBCYWNrd2FyZCBjb21wYXRpYmlsaXR5OiBDaGVjayBmb3Igb2xkIHZhcmlhYmxlIG5hbWVzXG4gIHJlcG8gPSBwcm9jZXNzLmVudi5URVNUX0dJVEhVQl9SRVBPPy50cmltKCk7XG4gIGlmIChyZXBvKSB7XG4gICAgbG9nZ2VyLndhcm4oJ+KaoO+4jyAgREVQUkVDQVRFRDogVEVTVF9HSVRIVUJfUkVQTyBpcyBkZXByZWNhdGVkLiBVc2UgUE9SVEZPTElPX1JFUE9TSVRPUllfTkFNRSBmb3IgcHJvZHVjdGlvbiBvciBHSVRIVUJfVEVTVF9SRVBPU0lUT1JZIGZvciB0ZXN0cy4nKTtcbiAgICByZXR1cm4gdmFsaWRhdGVSZXBvc2l0b3J5TmFtZShyZXBvKTtcbiAgfVxuXG4gIHJlcG8gPSBwcm9jZXNzLmVudi5HSVRIVUJfVEVTVF9SRVBPPy50cmltKCk7XG4gIGlmIChyZXBvKSB7XG4gICAgbG9nZ2VyLndhcm4oJ+KaoO+4jyAgREVQUkVDQVRFRDogR0lUSFVCX1RFU1RfUkVQTyBpcyBkZXByZWNhdGVkLiBVc2UgUE9SVEZPTElPX1JFUE9TSVRPUllfTkFNRSBmb3IgcHJvZHVjdGlvbiBvciBHSVRIVUJfVEVTVF9SRVBPU0lUT1JZIGZvciB0ZXN0cy4nKTtcbiAgICByZXR1cm4gdmFsaWRhdGVSZXBvc2l0b3J5TmFtZShyZXBvKTtcbiAgfVxuXG4gIHJlcG8gPSBwcm9jZXNzLmVudi5URVNUX1BPUlRGT0xJT19SRVBPPy50cmltKCk7XG4gIGlmIChyZXBvKSB7XG4gICAgbG9nZ2VyLndhcm4oJ+KaoO+4jyAgREVQUkVDQVRFRDogVEVTVF9QT1JURk9MSU9fUkVQTyBpcyBkZXByZWNhdGVkLiBVc2UgUE9SVEZPTElPX1JFUE9TSVRPUllfTkFNRSBmb3IgcHJvZHVjdGlvbiBvciBHSVRIVUJfVEVTVF9SRVBPU0lUT1JZIGZvciB0ZXN0cy4nKTtcbiAgICByZXR1cm4gdmFsaWRhdGVSZXBvc2l0b3J5TmFtZShyZXBvKTtcbiAgfVxuXG4gIC8vIFRPRE86IEFkZCBzdXBwb3J0IGZvciByZWFkaW5nIGZyb20gY29uZmlnIGZpbGVcbiAgLy8gY29uc3QgY29uZmlnID0gQ29uZmlnTWFuYWdlci5nZXRJbnN0YW5jZSgpO1xuICAvLyBpZiAoY29uZmlnLmdpdGh1Yj8ucG9ydGZvbGlvPy5yZXBvc2l0b3J5X25hbWUpIHtcbiAgLy8gICByZXR1cm4gdmFsaWRhdGVSZXBvc2l0b3J5TmFtZShjb25maWcuZ2l0aHViLnBvcnRmb2xpby5yZXBvc2l0b3J5X25hbWUpO1xuICAvLyB9XG5cbiAgLy8gNi4gRGVmYXVsdCByZXBvc2l0b3J5IG5hbWVcbiAgcmV0dXJuICdkb2xsaG91c2UtcG9ydGZvbGlvJztcbn1cblxuLyoqXG4gKiBDaGVjayBpZiB3ZSdyZSBpbiBhIHRlc3QgZW52aXJvbm1lbnRcbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gV2hlbiBOT0RFX0VOVj10ZXN0XG4gKiBwcm9jZXNzLmVudi5OT0RFX0VOViA9ICd0ZXN0JztcbiAqIGNvbnN0IGlzVGVzdCA9IGlzVGVzdEVudmlyb25tZW50KCk7IC8vIHRydWVcbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gSW4gcHJvZHVjdGlvbiBlbnZpcm9ubWVudFxuICogcHJvY2Vzcy5lbnYuTk9ERV9FTlYgPSAncHJvZHVjdGlvbic7XG4gKiBjb25zdCBpc1Rlc3QgPSBpc1Rlc3RFbnZpcm9ubWVudCgpOyAvLyBmYWxzZVxuICpcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHJ1bm5pbmcgaW4gYSB0ZXN0IGVudmlyb25tZW50XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1Rlc3RFbnZpcm9ubWVudCgpOiBib29sZWFuIHtcbiAgcmV0dXJuIHByb2Nlc3MuZW52LlRFU1RfTU9ERSA9PT0gJ3RydWUnIHx8XG4gICAgICAgICBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ3Rlc3QnIHx8XG4gICAgICAgICAvLyBCYWNrd2FyZCBjb21wYXRpYmlsaXR5OiBvbGQgdGVzdCBkZXRlY3Rpb25cbiAgICAgICAgICEhcHJvY2Vzcy5lbnYuVEVTVF9HSVRIVUJfUkVQTz8udHJpbSgpIHx8XG4gICAgICAgICAhIXByb2Nlc3MuZW52LkdJVEhVQl9URVNUX1RPS0VOPy50cmltKCk7XG59Il19