@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.
384 lines • 52.1 kB
JavaScript
#!/usr/bin/env node
/**
* Cleanup utility for historical duplicate memory files.
*
* Issue #702: After Issue #699 fixed ongoing duplication, this utility
* deduplicates already duplicated memory files across date folders.
*/
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { generateContentHash } from '../elements/memories/utils.js';
import { SecureYamlParser } from '../security/secureYamlParser.js';
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
const DATE_FOLDER_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
const YAML_FILE_PATTERN = /\.ya?ml$/i;
const VOLATILE_METADATA_KEYS = new Set([
'unique_id',
'uniqueid',
'author',
'updated',
'updatedat',
'updated_at',
'modified',
'modifiedat',
'modified_at',
'lastmodified',
'last_modified',
'savedat',
'saved_at',
'id'
]);
function isBackupFile(filename) {
return filename.toLowerCase().includes('backup');
}
function normalizePathInput(input) {
const normalized = UnicodeValidator.normalize(input);
if (!normalized.isValid) {
throw new Error(`Invalid path input: ${normalized.detectedIssues?.join(', ')}`);
}
return normalized.normalizedContent;
}
function stableSerialize(value) {
if (value === null)
return 'null';
if (value === undefined)
return 'undefined';
if (typeof value !== 'object') {
return JSON.stringify(value);
}
if (Array.isArray(value)) {
return `[${value.map(item => stableSerialize(item)).join(',')}]`;
}
const objectValue = value;
const keys = Object.keys(objectValue).sort((a, b) => a.localeCompare(b));
const serializedPairs = keys.map(key => `${JSON.stringify(key)}:${stableSerialize(objectValue[key])}`);
return `{${serializedPairs.join(',')}}`;
}
function normalizeForHash(value) {
if (value === null || value === undefined)
return value;
if (typeof value !== 'object')
return value;
if (Array.isArray(value)) {
return value.map(item => normalizeForHash(item));
}
const inputObj = value;
const outputObj = {};
for (const [key, child] of Object.entries(inputObj)) {
const normalizedKey = key.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
if (VOLATILE_METADATA_KEYS.has(normalizedKey)) {
continue;
}
outputObj[key] = normalizeForHash(child);
}
return outputObj;
}
function getLatestEntryTimestamp(data) {
const entries = Array.isArray(data.entries) ? data.entries : [];
let latest = 0;
for (const entry of entries) {
if (!entry || typeof entry !== 'object')
continue;
const typedEntry = entry;
const candidates = [
typedEntry.timestamp,
typedEntry.created,
typedEntry.createdAt,
typedEntry.updatedAt
];
for (const candidate of candidates) {
if (typeof candidate !== 'string')
continue;
const parsed = Date.parse(candidate);
if (!Number.isNaN(parsed) && parsed > latest) {
latest = parsed;
}
}
}
return latest;
}
function parseMemoryData(rawContent) {
const wrapped = `---\n${rawContent}\n---\n`;
const parsed = SecureYamlParser.parse(wrapped, {
validateContent: false,
validateFields: false
});
return parsed.data ?? {};
}
async function findMemoryCandidates(memoriesDir) {
const rootEntries = await fs.readdir(memoriesDir, { withFileTypes: true });
const candidates = [];
const errors = [];
for (const entry of rootEntries) {
if (!entry.isDirectory() || !DATE_FOLDER_PATTERN.test(entry.name)) {
continue;
}
const folderPath = path.join(memoriesDir, entry.name);
const files = await fs.readdir(folderPath, { withFileTypes: true });
for (const file of files) {
if (!file.isFile())
continue;
if (!YAML_FILE_PATTERN.test(file.name))
continue;
if (isBackupFile(file.name))
continue;
const absolutePath = path.join(folderPath, file.name);
// Normalize to forward slashes for consistent cross-platform error messages and comparisons
const relativePath = path.join(entry.name, file.name).split(path.sep).join('/');
const rawContent = await fs.readFile(absolutePath, 'utf-8');
const stats = await fs.stat(absolutePath);
let data;
try {
data = parseMemoryData(rawContent);
}
catch (error) {
errors.push(`Failed to parse ${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
continue;
}
const metadata = (data.metadata && typeof data.metadata === 'object')
? data.metadata
: {};
const nameFromMeta = typeof metadata.name === 'string' ? metadata.name : undefined;
const memoryName = nameFromMeta || path.basename(file.name, path.extname(file.name));
const memoryType = typeof metadata.memoryType === 'string' ? metadata.memoryType : 'user';
const identityKey = `${memoryType.toLowerCase()}::${memoryName.trim().toLowerCase()}`;
const normalizedHash = generateContentHash(stableSerialize(normalizeForHash(data)));
const entries = Array.isArray(data.entries) ? data.entries : [];
candidates.push({
absolutePath,
relativePath,
identityKey,
normalizedHash,
memoryName,
memoryType,
entryCount: entries.length,
latestEntryTs: getLatestEntryTimestamp(data),
mtimeMs: stats.mtimeMs,
sizeBytes: stats.size
});
}
}
return {
candidates,
errors
};
}
function selectCanonicalAndRedundant(group) {
const sorted = [...group].sort((a, b) => {
if (a.entryCount !== b.entryCount)
return b.entryCount - a.entryCount;
if (a.latestEntryTs !== b.latestEntryTs)
return b.latestEntryTs - a.latestEntryTs;
if (a.mtimeMs !== b.mtimeMs)
return b.mtimeMs - a.mtimeMs;
return b.relativePath.localeCompare(a.relativePath);
});
return {
keep: sorted[0],
remove: sorted.slice(1)
};
}
function createRunId(now) {
return now.toISOString().replace(/[:.]/g, '-');
}
async function writeJsonReport(reportPath, report) {
const reportDir = path.dirname(reportPath);
await fs.mkdir(reportDir, { recursive: true });
await fs.writeFile(reportPath, JSON.stringify(report, null, 2), 'utf-8');
}
async function invalidateMemoryIndex(memoriesDir) {
const indexPath = path.join(memoriesDir, '_index.json');
try {
await fs.unlink(indexPath);
return true;
}
catch (error) {
if (error.code === 'ENOENT') {
return false;
}
throw error;
}
}
export async function cleanupDuplicateMemories(memoriesDirInput, options = {}) {
const memoriesDir = normalizePathInput(memoriesDirInput);
const apply = options.apply ?? false;
let errors = [];
let candidates = [];
try {
const scan = await findMemoryCandidates(memoriesDir);
candidates = scan.candidates;
errors = [...errors, ...scan.errors];
}
catch (error) {
throw new Error(`Failed scanning memory files: ${error instanceof Error ? error.message : String(error)}`);
}
const grouped = new Map();
for (const candidate of candidates) {
const key = `${candidate.identityKey}::${candidate.normalizedHash}`;
const list = grouped.get(key);
if (list) {
list.push(candidate);
}
else {
grouped.set(key, [candidate]);
}
}
const groups = [];
const movePlan = [];
let bytesReclaimedEstimate = 0;
for (const [key, items] of grouped.entries()) {
if (items.length < 2)
continue;
const { keep, remove } = selectCanonicalAndRedundant(items);
if (remove.length === 0)
continue;
groups.push({
key,
memoryName: keep.memoryName,
memoryType: keep.memoryType,
keep: keep.relativePath,
remove: remove.map(item => item.relativePath)
});
for (const removable of remove) {
movePlan.push(removable);
bytesReclaimedEstimate += removable.sizeBytes;
}
}
const now = options.now ?? new Date();
const runId = createRunId(now);
const defaultBackupDir = path.join(memoriesDir, 'backups', 'dedup', runId);
const backupDir = options.backupDir ? normalizePathInput(options.backupDir) : defaultBackupDir;
let filesMoved = 0;
let indexInvalidated = false;
if (apply && movePlan.length > 0) {
for (const item of movePlan) {
try {
const destinationPath = path.join(backupDir, item.relativePath);
await fs.mkdir(path.dirname(destinationPath), { recursive: true });
await fs.rename(item.absolutePath, destinationPath);
filesMoved += 1;
}
catch (error) {
errors.push(`Failed to move ${item.relativePath}: ${error instanceof Error ? error.message : String(error)}`);
}
}
try {
indexInvalidated = await invalidateMemoryIndex(memoriesDir);
}
catch (error) {
errors.push(`Failed to invalidate _index.json: ${error instanceof Error ? error.message : String(error)}`);
}
}
const report = {
mode: apply ? 'apply' : 'dry-run',
memoriesDir,
backupDir: apply ? backupDir : undefined,
scannedFiles: candidates.length,
duplicateGroups: groups.length,
filesToMove: movePlan.length,
filesMoved,
bytesReclaimedEstimate,
indexInvalidated,
groups,
errors
};
if (options.jsonReportPath) {
await writeJsonReport(normalizePathInput(options.jsonReportPath), report);
}
return report;
}
function parseCliArgs(argv) {
const args = [...argv];
let memoriesDir = path.join(os.homedir(), '.dollhouse/portfolio/memories');
const options = { apply: false };
if (args.length > 0 && !args[0].startsWith('--')) {
memoriesDir = args.shift();
}
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (!arg)
continue;
if (arg === '--apply') {
options.apply = true;
continue;
}
if (arg === '--dry-run') {
options.apply = false;
continue;
}
if (arg === '--backup-dir') {
const next = args[i + 1];
if (!next || next.startsWith('--')) {
throw new Error('Missing value for --backup-dir');
}
options.backupDir = next;
i += 1;
continue;
}
if (arg === '--json-report') {
const next = args[i + 1];
if (!next || next.startsWith('--')) {
throw new Error('Missing value for --json-report');
}
options.jsonReportPath = next;
i += 1;
continue;
}
if (arg === '--help' || arg === '-h') {
console.log('Usage: cleanup-duplicate-memories [memoriesDir] [--dry-run] [--apply] [--backup-dir <path>] [--json-report <path>]');
process.exit(0);
}
throw new Error(`Unknown argument: ${arg}`);
}
return {
memoriesDir,
options
};
}
function printReport(report) {
console.log('');
console.log('Memory duplicate cleanup report');
console.log(`Mode: ${report.mode}`);
console.log(`Memories directory: ${report.memoriesDir}`);
if (report.backupDir) {
console.log(`Backup directory: ${report.backupDir}`);
}
console.log(`Scanned files: ${report.scannedFiles}`);
console.log(`Duplicate groups: ${report.duplicateGroups}`);
console.log(`Files to move: ${report.filesToMove}`);
console.log(`Files moved: ${report.filesMoved}`);
console.log(`Estimated bytes reclaimed: ${report.bytesReclaimedEstimate}`);
console.log(`Index invalidated: ${report.indexInvalidated ? 'yes' : 'no'}`);
if (report.groups.length > 0) {
console.log('');
console.log('Duplicate groups:');
for (const group of report.groups) {
console.log(`- ${group.memoryName} (${group.memoryType})`);
console.log(` keep: ${group.keep}`);
for (const removePath of group.remove) {
console.log(` remove: ${removePath}`);
}
}
}
if (report.errors.length > 0) {
console.log('');
console.log('Errors:');
for (const error of report.errors) {
console.log(`- ${error}`);
}
}
console.log('');
}
if (import.meta.url === `file://${process.argv[1]}`) {
try {
const { memoriesDir, options } = parseCliArgs(process.argv.slice(2));
const report = await cleanupDuplicateMemories(memoriesDir, options);
printReport(report);
}
catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xlYW51cC1kdXBsaWNhdGUtbWVtb3JpZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvY2xlYW51cC1kdXBsaWNhdGUtbWVtb3JpZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUNBOzs7OztHQUtHO0FBRUgsT0FBTyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDbEMsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3pCLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUM3QixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNwRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNuRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUU5RSxNQUFNLG1CQUFtQixHQUFHLHFCQUFxQixDQUFDO0FBQ2xELE1BQU0saUJBQWlCLEdBQUcsV0FBVyxDQUFDO0FBQ3RDLE1BQU0sc0JBQXNCLEdBQUcsSUFBSSxHQUFHLENBQUM7SUFDckMsV0FBVztJQUNYLFVBQVU7SUFDVixRQUFRO0lBQ1IsU0FBUztJQUNULFdBQVc7SUFDWCxZQUFZO0lBQ1osVUFBVTtJQUNWLFlBQVk7SUFDWixhQUFhO0lBQ2IsY0FBYztJQUNkLGVBQWU7SUFDZixTQUFTO0lBQ1QsVUFBVTtJQUNWLElBQUk7Q0FDTCxDQUFDLENBQUM7QUFpREgsU0FBUyxZQUFZLENBQUMsUUFBZ0I7SUFDcEMsT0FBTyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ25ELENBQUM7QUFFRCxTQUFTLGtCQUFrQixDQUFDLEtBQWE7SUFDdkMsTUFBTSxVQUFVLEdBQUcsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3JELElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDeEIsTUFBTSxJQUFJLEtB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QUUsQ0FBQztZQUNsQyxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssS0FBSyxFQUFFLENBQUMsQ0FBQztRQUM1QixDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDbEIsQ0FBQztBQUVELElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEtBQUssVUFBVSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQztJQUNwRCxJQUFJLENBQUM7UUFDSCxNQUFNLEVBQUUsV0FBVyxFQUFFLE9BQU8sRUFBRSxHQUFHLFlBQVksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JFLE1BQU0sTUFBTSxHQUFHLE1BQU0sd0JBQXdCLENBQUMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3BFLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN0QixDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDdEUsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsQixDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIiMhL3Vzci9iaW4vZW52IG5vZGVcbi8qKlxuICogQ2xlYW51cCB1dGlsaXR5IGZvciBoaXN0b3JpY2FsIGR1cGxpY2F0ZSBtZW1vcnkgZmlsZXMuXG4gKlxuICogSXNzdWUgIzcwMjogQWZ0ZXIgSXNzdWUgIzY5OSBmaXhlZCBvbmdvaW5nIGR1cGxpY2F0aW9uLCB0aGlzIHV0aWxpdHlcbiAqIGRlZHVwbGljYXRlcyBhbHJlYWR5IGR1cGxpY2F0ZWQgbWVtb3J5IGZpbGVzIGFjcm9zcyBkYXRlIGZvbGRlcnMuXG4gKi9cblxuaW1wb3J0IGZzIGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnO1xuaW1wb3J0IG9zIGZyb20gJ25vZGU6b3MnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGdlbmVyYXRlQ29udGVudEhhc2ggfSBmcm9tICcuLi9lbGVtZW50cy9tZW1vcmllcy91dGlscy5qcyc7XG5pbXBvcnQgeyBTZWN1cmVZYW1sUGFyc2VyIH0gZnJvbSAnLi4vc2VjdXJpdHkvc2VjdXJlWWFtbFBhcnNlci5qcyc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcblxuY29uc3QgREFURV9GT0xERVJfUEFUVEVSTiA9IC9eXFxkezR9LVxcZHsyfS1cXGR7Mn0kLztcbmNvbnN0IFlBTUxfRklMRV9QQVRURVJOID0gL1xcLnlhP21sJC9pO1xuY29uc3QgVk9MQVRJTEVfTUVUQURBVEFfS0VZUyA9IG5ldyBTZXQoW1xuICAndW5pcXVlX2lkJyxcbiAgJ3VuaXF1ZWlkJyxcbiAgJ2F1dGhvcicsXG4gICd1cGRhdGVkJyxcbiAgJ3VwZGF0ZWRhdCcsXG4gICd1cGRhdGVkX2F0JyxcbiAgJ21vZGlmaWVkJyxcbiAgJ21vZGlmaWVkYXQnLFxuICAnbW9kaWZpZWRfYXQnLFxuICAnbGFzdG1vZGlmaWVkJyxcbiAgJ2xhc3RfbW9kaWZpZWQnLFxuICAnc2F2ZWRhdCcsXG4gICdzYXZlZF9hdCcsXG4gICdpZCdcbl0pO1xuXG5pbnRlcmZhY2UgTWVtb3J5Q2FuZGlkYXRlIHtcbiAgYWJzb2x1dGVQYXRoOiBzdHJpbmc7XG4gIHJlbGF0aXZlUGF0aDogc3RyaW5nO1xuICBpZGVudGl0eUtleTogc3RyaW5nO1xuICBub3JtYWxpemVkSGFzaDogc3RyaW5nO1xuICBtZW1vcnlOYW1lOiBzdHJpbmc7XG4gIG1lbW9yeVR5cGU6IHN0cmluZztcbiAgZW50cnlDb3VudDogbnVtYmVyO1xuICBsYXRlc3RFbnRyeVRzOiBudW1iZXI7XG4gIG10aW1lTXM6IG51bWJlcjtcbiAgc2l6ZUJ5dGVzOiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBDYW5kaWRhdGVTY2FuUmVzdWx0IHtcbiAgY2FuZGlkYXRlczogTWVtb3J5Q2FuZGlkYXRlW107XG4gIGVycm9yczogc3RyaW5nW107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRHVwbGljYXRlR3JvdXAge1xuICBrZXk6IHN0cmluZztcbiAgbWVtb3J5TmFtZTogc3RyaW5nO1xuICBtZW1vcnlUeXBlOiBzdHJpbmc7XG4gIGtlZXA6IHN0cmluZztcbiAgcmVtb3ZlOiBzdHJpbmdbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBNZW1vcnlEdXBsaWNhdGVDbGVhbnVwUmVwb3J0IHtcbiAgbW9kZTogJ2RyeS1ydW4nIHwgJ2FwcGx5JztcbiAgbWVtb3JpZXNEaXI6IHN0cmluZztcbiAgYmFja3VwRGlyPzogc3RyaW5nO1xuICBzY2FubmVkRmlsZXM6IG51bWJlcjtcbiAgZHVwbGljYXRlR3JvdXBzOiBudW1iZXI7XG4gIGZpbGVzVG9Nb3ZlOiBudW1iZXI7XG4gIGZpbGVzTW92ZWQ6IG51bWJlcjtcbiAgYnl0ZXNSZWNsYWltZWRFc3RpbWF0ZTogbnVtYmVyO1xuICBpbmRleEludmFsaWRhdGVkOiBib29sZWFuO1xuICBncm91cHM6IER1cGxpY2F0ZUdyb3VwW107XG4gIGVycm9yczogc3RyaW5nW107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2xlYW51cER1cGxpY2F0ZU1lbW9yaWVzT3B0aW9ucyB7XG4gIGFwcGx5PzogYm9vbGVhbjtcbiAgYmFja3VwRGlyPzogc3RyaW5nO1xuICBqc29uUmVwb3J0UGF0aD86IHN0cmluZztcbiAgbm93PzogRGF0ZTtcbn1cblxuZnVuY3Rpb24gaXNCYWNrdXBGaWxlKGZpbGVuYW1lOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgcmV0dXJuIGZpbGVuYW1lLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoJ2JhY2t1cCcpO1xufVxuXG5mdW5jdGlvbiBub3JtYWxpemVQYXRoSW5wdXQoaW5wdXQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShpbnB1dCk7XG4gIGlmICghbm9ybWFsaXplZC5pc1ZhbGlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHBhdGggaW5wdXQ6ICR7bm9ybWFsaXplZC5kZXRlY3RlZElzc3Vlcz8uam9pbignLCAnKX1gKTtcbiAgfVxuICByZXR1cm4gbm9ybWFsaXplZC5ub3JtYWxpemVkQ29udGVudDtcbn1cblxuZnVuY3Rpb24gc3RhYmxlU2VyaWFsaXplKHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHtcbiAgaWYgKHZhbHVlID09PSBudWxsKSByZXR1cm4gJ251bGwnO1xuICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuICd1bmRlZmluZWQnO1xuXG4gIGlmICh0eXBlb2YgdmFsdWUgIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHZhbHVlKTtcbiAgfVxuXG4gIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgIHJldHVybiBgWyR7dmFsdWUubWFwKGl0ZW0gPT4gc3RhYmxlU2VyaWFsaXplKGl0ZW0pKS5qb2luKCcsJyl9XWA7XG4gIH1cblxuICBjb25zdCBvYmplY3RWYWx1ZSA9IHZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMob2JqZWN0VmFsdWUpLnNvcnQoKGEsIGIpID0+IGEubG9jYWxlQ29tcGFyZShiKSk7XG4gIGNvbnN0IHNlcmlhbGl6ZWRQYWlycyA9IGtleXMubWFwKGtleSA9PiBgJHtKU09OLnN0cmluZ2lmeShrZXkpfToke3N0YWJsZVNlcmlhbGl6ZShvYmplY3RWYWx1ZVtrZXldKX1gKTtcbiAgcmV0dXJuIGB7JHtzZXJpYWxpemVkUGFpcnMuam9pbignLCcpfX1gO1xufVxuXG5mdW5jdGlvbiBub3JtYWxpemVGb3JIYXNoKHZhbHVlOiB1bmtub3duKTogdW5rbm93biB7XG4gIGlmICh2YWx1ZSA9PT0gbnVsbCB8fCB2YWx1ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm4gdmFsdWU7XG4gIGlmICh0eXBlb2YgdmFsdWUgIT09ICdvYmplY3QnKSByZXR1cm4gdmFsdWU7XG5cbiAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgcmV0dXJuIHZhbHVlLm1hcChpdGVtID0+IG5vcm1hbGl6ZUZvckhhc2goaXRlbSkpO1xuICB9XG5cbiAgY29uc3QgaW5wdXRPYmogPSB2YWx1ZSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgY29uc3Qgb3V0cHV0T2JqOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuICBmb3IgKGNvbnN0IFtrZXksIGNoaWxkXSBvZiBPYmplY3QuZW50cmllcyhpbnB1dE9iaikpIHtcbiAgICBjb25zdCBub3JtYWxpemVkS2V5ID0ga2V5LnJlcGxhY2UoL1teYS16QS1aMC05XS9nLCAnJykudG9Mb3dlckNhc2UoKTtcbiAgICBpZiAoVk9MQVRJTEVfTUVUQURBVEFfS0VZUy5oYXMobm9ybWFsaXplZEtleSkpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBvdXRwdXRPYmpba2V5XSA9IG5vcm1hbGl6ZUZvckhhc2goY2hpbGQpO1xuICB9XG4gIHJldHVybiBvdXRwdXRPYmo7XG59XG5cbmZ1bmN0aW9uIGdldExhdGVzdEVudHJ5VGltZXN0YW1wKGRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogbnVtYmVyIHtcbiAgY29uc3QgZW50cmllcyA9IEFycmF5LmlzQXJyYXkoZGF0YS5lbnRyaWVzKSA/IGRhdGEuZW50cmllcyA6IFtdO1xuICBsZXQgbGF0ZXN0ID0gMDtcblxuICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICBpZiAoIWVudHJ5IHx8IHR5cGVvZiBlbnRyeSAhPT0gJ29iamVjdCcpIGNvbnRpbnVlO1xuICAgIGNvbnN0IHR5cGVkRW50cnkgPSBlbnRyeSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBjb25zdCBjYW5kaWRhdGVzID0gW1xuICAgICAgdHlwZWRFbnRyeS50aW1lc3RhbXAsXG4gICAgICB0eXBlZEVudHJ5LmNyZWF0ZWQsXG4gICAgICB0eXBlZEVudHJ5LmNyZWF0ZWRBdCxcbiAgICAgIHR5cGVkRW50cnkudXBkYXRlZEF0XG4gICAgXTtcbiAgICBmb3IgKGNvbnN0IGNhbmRpZGF0ZSBvZiBjYW5kaWRhdGVzKSB7XG4gICAgICBpZiAodHlwZW9mIGNhbmRpZGF0ZSAhPT0gJ3N0cmluZycpIGNvbnRpbnVlO1xuICAgICAgY29uc3QgcGFyc2VkID0gRGF0ZS5wYXJzZShjYW5kaWRhdGUpO1xuICAgICAgaWYgKCFOdW1iZXIuaXNOYU4ocGFyc2VkKSAmJiBwYXJzZWQgPiBsYXRlc3QpIHtcbiAgICAgICAgbGF0ZXN0ID0gcGFyc2VkO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBsYXRlc3Q7XG59XG5cbmZ1bmN0aW9uIHBhcnNlTWVtb3J5RGF0YShyYXdDb250ZW50OiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIGNvbnN0IHdyYXBwZWQgPSBgLS0tXFxuJHtyYXdDb250ZW50fVxcbi0tLVxcbmA7XG4gIGNvbnN0IHBhcnNlZCA9IFNlY3VyZVlhbWxQYXJzZXIucGFyc2Uod3JhcHBlZCwge1xuICAgIHZhbGlkYXRlQ29udGVudDogZmFsc2UsXG4gICAgdmFsaWRhdGVGaWVsZHM6IGZhbHNlXG4gIH0pO1xuICByZXR1cm4gcGFyc2VkLmRhdGEgPz8ge307XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGZpbmRNZW1vcnlDYW5kaWRhdGVzKG1lbW9yaWVzRGlyOiBzdHJpbmcpOiBQcm9taXNlPENhbmRpZGF0ZVNjYW5SZXN1bHQ+IHtcbiAgY29uc3Qgcm9vdEVudHJpZXMgPSBhd2FpdCBmcy5yZWFkZGlyKG1lbW9yaWVzRGlyLCB7IHdpdGhGaWxlVHlwZXM6IHRydWUgfSk7XG4gIGNvbnN0IGNhbmRpZGF0ZXM6IE1lbW9yeUNhbmRpZGF0ZVtdID0gW107XG4gIGNvbnN0IGVycm9yczogc3RyaW5nW10gPSBbXTtcblxuICBmb3IgKGNvbnN0IGVudHJ5IG9mIHJvb3RFbnRyaWVzKSB7XG4gICAgaWYgKCFlbnRyeS5pc0RpcmVjdG9yeSgpIHx8ICFEQVRFX0ZPTERFUl9QQVRURVJOLnRlc3QoZW50cnkubmFtZSkpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGNvbnN0IGZvbGRlclBhdGggPSBwYXRoLmpvaW4obWVtb3JpZXNEaXIsIGVudHJ5Lm5hbWUpO1xuICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcihmb2xkZXJQYXRoLCB7IHdpdGhGaWxlVHlwZXM6IHRydWUgfSk7XG4gICAgZm9yIChjb25zdCBmaWxlIG9mIGZpbGVzKSB7XG4gICAgICBpZiAoIWZpbGUuaXNGaWxlKCkpIGNvbnRpbnVlO1xuICAgICAgaWYgKCFZQU1MX0ZJTEVfUEFUVEVSTi50ZXN0KGZpbGUubmFtZSkpIGNvbnRpbnVlO1xuICAgICAgaWYgKGlzQmFja3VwRmlsZShmaWxlLm5hbWUpKSBjb250aW51ZTtcblxuICAgICAgY29uc3QgYWJzb2x1dGVQYXRoID0gcGF0aC5qb2luKGZvbGRlclBhdGgsIGZpbGUubmFtZSk7XG4gICAgICAvLyBOb3JtYWxpemUgdG8gZm9yd2FyZCBzbGFzaGVzIGZvciBjb25zaXN0ZW50IGNyb3NzLXBsYXRmb3JtIGVycm9yIG1lc3NhZ2VzIGFuZCBjb21wYXJpc29uc1xuICAgICAgY29uc3QgcmVsYXRpdmVQYXRoID0gcGF0aC5qb2luKGVudHJ5Lm5hbWUsIGZpbGUubmFtZSkuc3BsaXQocGF0aC5zZXApLmpvaW4oJy8nKTtcbiAgICAgIGNvbnN0IHJhd0NvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShhYnNvbHV0ZVBhdGgsICd1dGYtOCcpO1xuICAgICAgY29uc3Qgc3RhdHMgPSBhd2FpdCBmcy5zdGF0KGFic29sdXRlUGF0aCk7XG5cbiAgICAgIGxldCBkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgIHRyeSB7XG4gICAgICAgIGRhdGEgPSBwYXJzZU1lbW9yeURhdGEocmF3Q29udGVudCk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBlcnJvcnMucHVzaChcbiAgICAgICAgICBgRmFpbGVkIHRvIHBhcnNlICR7cmVsYXRpdmVQYXRofTogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YFxuICAgICAgICApO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbWV0YWRhdGEgPSAoZGF0YS5tZXRhZGF0YSAmJiB0eXBlb2YgZGF0YS5tZXRhZGF0YSA9PT0gJ29iamVjdCcpXG4gICAgICAgID8gZGF0YS5tZXRhZGF0YSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPlxuICAgICAgICA6IHt9O1xuICAgICAgY29uc3QgbmFtZUZyb21NZXRhID0gdHlwZW9mIG1ldGFkYXRhLm5hbWUgPT09ICdzdHJpbmcnID8gbWV0YWRhdGEubmFtZSA6IHVuZGVmaW5lZDtcbiAgICAgIGNvbnN0IG1lbW9yeU5hbWUgPSBuYW1lRnJvbU1ldGEgfHwgcGF0aC5iYXNlbmFtZShmaWxlLm5hbWUsIHBhdGguZXh0bmFtZShmaWxlLm5hbWUpKTtcbiAgICAgIGNvbnN0IG1lbW9yeVR5cGUgPSB0eXBlb2YgbWV0YWRhdGEubWVtb3J5VHlwZSA9PT0gJ3N0cmluZycgPyBtZXRhZGF0YS5tZW1vcnlUeXBlIDogJ3VzZXInO1xuICAgICAgY29uc3QgaWRlbnRpdHlLZXkgPSBgJHttZW1vcnlUeXBlLnRvTG93ZXJDYXNlKCl9Ojoke21lbW9yeU5hbWUudHJpbSgpLnRvTG93ZXJDYXNlKCl9YDtcbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWRIYXNoID0gZ2VuZXJhdGVDb250ZW50SGFzaChzdGFibGVTZXJpYWxpemUobm9ybWFsaXplRm9ySGFzaChkYXRhKSkpO1xuICAgICAgY29uc3QgZW50cmllcyA9IEFycmF5LmlzQXJyYXkoZGF0YS5lbnRyaWVzKSA/IGRhdGEuZW50cmllcyA6IFtdO1xuXG4gICAgICBjYW5kaWRhdGVzLnB1c2goe1xuICAgICAgICBhYnNvbHV0ZVBhdGgsXG4gICAgICAgIHJlbGF0aXZlUGF0aCxcbiAgICAgICAgaWRlbnRpdHlLZXksXG4gICAgICAgIG5vcm1hbGl6ZWRIYXNoLFxuICAgICAgICBtZW1vcnlOYW1lLFxuICAgICAgICBtZW1vcnlUeXBlLFxuICAgICAgICBlbnRyeUNvdW50OiBlbnRyaWVzLmxlbmd0aCxcbiAgICAgICAgbGF0ZXN0RW50cnlUczogZ2V0TGF0ZXN0RW50cnlUaW1lc3RhbXAoZGF0YSksXG4gICAgICAgIG10aW1lTXM6IHN0YXRzLm10aW1lTXMsXG4gICAgICAgIHNpemVCeXRlczogc3RhdHMuc2l6ZVxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBjYW5kaWRhdGVzLFxuICAgIGVycm9yc1xuICB9O1xufVxuXG5mdW5jdGlvbiBzZWxlY3RDYW5vbmljYWxBbmRSZWR1bmRhbnQoZ3JvdXA6IE1lbW9yeUNhbmRpZGF0ZVtdKToge1xuICBrZWVwOiBNZW1vcnlDYW5kaWRhdGU7XG4gIHJlbW92ZTogTWVtb3J5Q2FuZGlkYXRlW107XG59IHtcbiAgY29uc3Qgc29ydGVkID0gWy4uLmdyb3VwXS5zb3J0KChhLCBiKSA9PiB7XG4gICAgaWYgKGEuZW50cnlDb3VudCAhPT0gYi5lbnRyeUNvdW50KSByZXR1cm4gYi5lbnRyeUNvdW50IC0gYS5lbnRyeUNvdW50O1xuICAgIGlmIChhLmxhdGVzdEVudHJ5VHMgIT09IGIubGF0ZXN0RW50cnlUcykgcmV0dXJuIGIubGF0ZXN0RW50cnlUcyAtIGEubGF0ZXN0RW50cnlUcztcbiAgICBpZiAoYS5tdGltZU1zICE9PSBiLm10aW1lTXMpIHJldHVybiBiLm10aW1lTXMgLSBhLm10aW1lTXM7XG4gICAgcmV0dXJuIGIucmVsYXRpdmVQYXRoLmxvY2FsZUNvbXBhcmUoYS5yZWxhdGl2ZVBhdGgpO1xuICB9KTtcbiAgcmV0dXJuIHtcbiAgICBrZWVwOiBzb3J0ZWRbMF0sXG4gICAgcmVtb3ZlOiBzb3J0ZWQuc2xpY2UoMSlcbiAgfTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlUnVuSWQobm93OiBEYXRlKTogc3RyaW5nIHtcbiAgcmV0dXJuIG5vdy50b0lTT1N0cmluZygpLnJlcGxhY2UoL1s6Ll0vZywgJy0nKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gd3JpdGVKc29uUmVwb3J0KHJlcG9ydFBhdGg6IHN0cmluZywgcmVwb3J0OiBNZW1vcnlEdXBsaWNhdGVDbGVhbnVwUmVwb3J0KTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHJlcG9ydERpciA9IHBhdGguZGlybmFtZShyZXBvcnRQYXRoKTtcbiAgYXdhaXQgZnMubWtkaXIocmVwb3J0RGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgYXdhaXQgZnMud3JpdGVGaWxlKHJlcG9ydFBhdGgsIEpTT04uc3RyaW5naWZ5KHJlcG9ydCwgbnVsbCwgMiksICd1dGYtOCcpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBpbnZhbGlkYXRlTWVtb3J5SW5kZXgobWVtb3JpZXNEaXI6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBjb25zdCBpbmRleFBhdGggPSBwYXRoLmpvaW4obWVtb3JpZXNEaXIsICdfaW5kZXguanNvbicpO1xuICB0cnkge1xuICAgIGF3YWl0IGZzLnVubGluayhpbmRleFBhdGgpO1xuICAgIHJldHVybiB0cnVlO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmICgoZXJyb3IgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uKS5jb2RlID09PSAnRU5PRU5UJykge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICB0aHJvdyBlcnJvcjtcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYW51cER1cGxpY2F0ZU1lbW9yaWVzKFxuICBtZW1vcmllc0RpcklucHV0OiBzdHJpbmcsXG4gIG9wdGlvbnM6IENsZWFudXBEdXBsaWNhdGVNZW1vcmllc09wdGlvbnMgPSB7fVxuKTogUHJvbWlzZTxNZW1vcnlEdXBsaWNhdGVDbGVhbnVwUmVwb3J0PiB7XG4gIGNvbnN0IG1lbW9yaWVzRGlyID0gbm9ybWFsaXplUGF0aElucHV0KG1lbW9yaWVzRGlySW5wdXQpO1xuICBjb25zdCBhcHBseSA9IG9wdGlvbnMuYXBwbHkgPz8gZmFsc2U7XG4gIGxldCBlcnJvcnM6IHN0cmluZ1tdID0gW107XG5cbiAgbGV0IGNhbmRpZGF0ZXM6IE1lbW9yeUNhbmRpZGF0ZVtdID0gW107XG4gIHRyeSB7XG4gICAgY29uc3Qgc2NhbiA9IGF3YWl0IGZpbmRNZW1vcnlDYW5kaWRhdGVzKG1lbW9yaWVzRGlyKTtcbiAgICBjYW5kaWRhdGVzID0gc2Nhbi5jYW5kaWRhdGVzO1xuICAgIGVycm9ycyA9IFsuLi5lcnJvcnMsIC4uLnNjYW4uZXJyb3JzXTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCBzY2FubmluZyBtZW1vcnkgZmlsZXM6ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpfWApO1xuICB9XG5cbiAgY29uc3QgZ3JvdXBlZCA9IG5ldyBNYXA8c3RyaW5nLCBNZW1vcnlDYW5kaWRhdGVbXT4oKTtcbiAgZm9yIChjb25zdCBjYW5kaWRhdGUgb2YgY2FuZGlkYXRlcykge1xuICAgIGNvbnN0IGtleSA9IGAke2NhbmRpZGF0ZS5pZGVudGl0eUtleX06OiR7Y2FuZGlkYXRlLm5vcm1hbGl6ZWRIYXNofWA7XG4gICAgY29uc3QgbGlzdCA9IGdyb3VwZWQuZ2V0KGtleSk7XG4gICAgaWYgKGxpc3QpIHtcbiAgICAgIGxpc3QucHVzaChjYW5kaWRhdGUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBncm91cGVkLnNldChrZXksIFtjYW5kaWRhdGVdKTtcbiAgICB9XG4gIH1cblxuICBjb25zdCBncm91cHM6IER1cGxpY2F0ZUdyb3VwW10gPSBbXTtcbiAgY29uc3QgbW92ZVBsYW46IE1lbW9yeUNhbmRpZGF0ZVtdID0gW107XG4gIGxldCBieXRlc1JlY2xhaW1lZEVzdGltYXRlID0gMDtcblxuICBmb3IgKGNvbnN0IFtrZXksIGl0ZW1zXSBvZiBncm91cGVkLmVudHJpZXMoKSkge1xuICAgIGlmIChpdGVtcy5sZW5ndGggPCAyKSBjb250aW51ZTtcblxuICAgIGNvbnN0IHsga2VlcCwgcmVtb3ZlIH0gPSBzZWxlY3RDYW5vbmljYWxBbmRSZWR1bmRhbnQoaXRlbXMpO1xuICAgIGlmIChyZW1vdmUubGVuZ3RoID09PSAwKSBjb250aW51ZTtcblxuICAgIGdyb3Vwcy5wdXNoKHtcbiAgICAgIGtleSxcbiAgICAgIG1lbW9yeU5hbWU6IGtlZXAubWVtb3J5TmFtZSxcbiAgICAgIG1lbW9yeVR5cGU6IGtlZXAubWVtb3J5VHlwZSxcbiAgICAgIGtlZXA6IGtlZXAucmVsYXRpdmVQYXRoLFxuICAgICAgcmVtb3ZlOiByZW1vdmUubWFwKGl0ZW0gPT4gaXRlbS5yZWxhdGl2ZVBhdGgpXG4gICAgfSk7XG5cbiAgICBmb3IgKGNvbnN0IHJlbW92YWJsZSBvZiByZW1vdmUpIHtcbiAgICAgIG1vdmVQbGFuLnB1c2gocmVtb3ZhYmxlKTtcbiAgICAgIGJ5dGVzUmVjbGFpbWVkRXN0aW1hdGUgKz0gcmVtb3ZhYmxlLnNpemVCeXRlcztcbiAgICB9XG4gIH1cblxuICBjb25zdCBub3cgPSBvcHRpb25zLm5vdyA/PyBuZXcgRGF0ZSgpO1xuICBjb25zdCBydW5JZCA9IGNyZWF0ZVJ1bklkKG5vdyk7XG4gIGNvbnN0IGRlZmF1bHRCYWNrdXBEaXIgPSBwYXRoLmpvaW4obWVtb3JpZXNEaXIsICdiYWNrdXBzJywgJ2RlZHVwJywgcnVuSWQpO1xuICBjb25zdCBiYWNrdXBEaXIgPSBvcHRpb25zLmJhY2t1cERpciA/IG5vcm1hbGl6ZVBhdGhJbnB1dChvcHRpb25zLmJhY2t1cERpcikgOiBkZWZhdWx0QmFja3VwRGlyO1xuXG4gIGxldCBmaWxlc01vdmVkID0gMDtcbiAgbGV0IGluZGV4SW52YWxpZGF0ZWQgPSBmYWxzZTtcblxuICBpZiAoYXBwbHkgJiYgbW92ZVBsYW4ubGVuZ3RoID4gMCkge1xuICAgIGZvciAoY29uc3QgaXRlbSBvZiBtb3ZlUGxhbikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgZGVzdGluYXRpb25QYXRoID0gcGF0aC5qb2luKGJhY2t1cERpciwgaXRlbS5yZWxhdGl2ZVBhdGgpO1xuICAgICAgICBhd2FpdCBmcy5ta2RpcihwYXRoLmRpcm5hbWUoZGVzdGluYXRpb25QYXRoKSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgICAgIGF3YWl0IGZzLnJlbmFtZShpdGVtLmFic29sdXRlUGF0aCwgZGVzdGluYXRpb25QYXRoKTtcbiAgICAgICAgZmlsZXNNb3ZlZCArPSAxO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgZXJyb3JzLnB1c2goXG4gICAgICAgICAgYEZhaWxlZCB0byBtb3ZlICR7aXRlbS5yZWxhdGl2ZVBhdGh9OiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKX1gXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIGluZGV4SW52YWxpZGF0ZWQgPSBhd2FpdCBpbnZhbGlkYXRlTWVtb3J5SW5kZXgobWVtb3JpZXNEaXIpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBlcnJvcnMucHVzaChgRmFpbGVkIHRvIGludmFsaWRhdGUgX2luZGV4Lmpzb246ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpfWApO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHJlcG9ydDogTWVtb3J5RHVwbGljYXRlQ2xlYW51cFJlcG9ydCA9IHtcbiAgICBtb2RlOiBhcHBseSA/ICdhcHBseScgOiAnZHJ5LXJ1bicsXG4gICAgbWVtb3JpZXNEaXIsXG4gICAgYmFja3VwRGlyOiBhcHBseSA/IGJhY2t1cERpciA6IHVuZGVmaW5lZCxcbiAgICBzY2FubmVkRmlsZXM6IGNhbmRpZGF0ZXMubGVuZ3RoLFxuICAgIGR1cGxpY2F0ZUdyb3VwczogZ3JvdXBzLmxlbmd0aCxcbiAgICBmaWxlc1RvTW92ZTogbW92ZVBsYW4ubGVuZ3RoLFxuICAgIGZpbGVzTW92ZWQsXG4gICAgYnl0ZXNSZWNsYWltZWRFc3RpbWF0ZSxcbiAgICBpbmRleEludmFsaWRhdGVkLFxuICAgIGdyb3VwcyxcbiAgICBlcnJvcnNcbiAgfTtcblxuICBpZiAob3B0aW9ucy5qc29uUmVwb3J0UGF0aCkge1xuICAgIGF3YWl0IHdyaXRlSnNvblJlcG9ydChub3JtYWxpemVQYXRoSW5wdXQob3B0aW9ucy5qc29uUmVwb3J0UGF0aCksIHJlcG9ydCk7XG4gIH1cblxuICByZXR1cm4gcmVwb3J0O1xufVxuXG5mdW5jdGlvbiBwYXJzZUNsaUFyZ3MoYXJndjogc3RyaW5nW10pOiB7XG4gIG1lbW9yaWVzRGlyOiBzdHJpbmc7XG4gIG9wdGlvbnM6IENsZWFudXBEdXBsaWNhdGVNZW1vcmllc09wdGlvbnM7XG59IHtcbiAgY29uc3QgYXJncyA9IFsuLi5hcmd2XTtcblxuICBsZXQgbWVtb3JpZXNEaXIgPSBwYXRoLmpvaW4ob3MuaG9tZWRpcigpLCAnLmRvbGxob3VzZS9wb3J0Zm9saW8vbWVtb3JpZXMnKTtcbiAgY29uc3Qgb3B0aW9uczogQ2xlYW51cER1cGxpY2F0ZU1lbW9yaWVzT3B0aW9ucyA9IHsgYXBwbHk6IGZhbHNlIH07XG5cbiAgaWYgKGFyZ3MubGVuZ3RoID4gMCAmJiAhYXJnc1swXS5zdGFydHNXaXRoKCctLScpKSB7XG4gICAgbWVtb3JpZXNEaXIgPSBhcmdzLnNoaWZ0KCkgYXMgc3RyaW5nO1xuICB9XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBhcmdzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgYXJnID0gYXJnc1tpXTtcbiAgICBpZiAoIWFyZykgY29udGludWU7XG5cbiAgICBpZiAoYXJnID09PSAnLS1hcHBseScpIHtcbiAgICAgIG9wdGlvbnMuYXBwbHkgPSB0cnVlO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgaWYgKGFyZyA9PT0gJy0tZHJ5LXJ1bicpIHtcbiAgICAgIG9wdGlvbnMuYXBwbHkgPSBmYWxzZTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGlmIChhcmcgPT09ICctLWJhY2t1cC1kaXInKSB7XG4gICAgICBjb25zdCBuZXh0ID0gYXJnc1tpICsgMV07XG4gICAgICBpZiAoIW5leHQgfHwgbmV4dC5zdGFydHNXaXRoKCctLScpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTWlzc2luZyB2YWx1ZSBmb3IgLS1iYWNrdXAtZGlyJyk7XG4gICAgICB9XG4gICAgICBvcHRpb25zLmJhY2t1cERpciA9IG5leHQ7XG4gICAgICBpICs9IDE7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBpZiAoYXJnID09PSAnLS1qc29uLXJlcG9ydCcpIHtcbiAgICAgIGNvbnN0IG5leHQgPSBhcmdzW2kgKyAxXTtcbiAgICAgIGlmICghbmV4dCB8fCBuZXh0LnN0YXJ0c1dpdGgoJy0tJykpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNaXNzaW5nIHZhbHVlIGZvciAtLWpzb24tcmVwb3J0Jyk7XG4gICAgICB9XG4gICAgICBvcHRpb25zLmpzb25SZXBvcnRQYXRoID0gbmV4dDtcbiAgICAgIGkgKz0gMTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGlmIChhcmcgPT09ICctLWhlbHAnIHx8IGFyZyA9PT0gJy1oJykge1xuICAgICAgY29uc29sZS5sb2coJ1VzYWdlOiBjbGVhbnVwLWR1cGxpY2F0ZS1tZW1vcmllcyBbbWVtb3JpZXNEaXJdIFstLWRyeS1ydW5dIFstLWFwcGx5XSBbLS1iYWNrdXAtZGlyIDxwYXRoPl0gWy0tanNvbi1yZXBvcnQgPHBhdGg+XScpO1xuICAgICAgcHJvY2Vzcy5leGl0KDApO1xuICAgIH1cblxuICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biBhcmd1bWVudDogJHthcmd9YCk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG1lbW9yaWVzRGlyLFxuICAgIG9wdGlvbnNcbiAgfTtcbn1cblxuZnVuY3Rpb24gcHJpbnRSZXBvcnQocmVwb3J0OiBNZW1vcnlEdXBsaWNhdGVDbGVhbnVwUmVwb3J0KTogdm9pZCB7XG4gIGNvbnNvbGUubG9nKCcnKTtcbiAgY29uc29sZS5sb2coJ01lbW9