@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.
355 lines • 51.6 kB
JavaScript
/**
* Stale process detection and recovery (#1850).
*
* Finds and kills zombie DollhouseMCP processes that squat on the console
* port after their session has ended. Used by bindAndListen in server.ts
* when EADDRINUSE occurs.
*
* Extracted to a standalone module so it can be tested without importing
* the full Express server and its dependency chain.
*/
import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
// Use lazy import for logger to avoid pulling in the full env.ts/config chain
// at module load time. This keeps the module independently testable.
/** Timeout for lsof/fuser/ps system calls (ms) */
const COMMAND_TIMEOUT_MS = 1000;
/** Polling interval when waiting for SIGTERM to take effect (ms) */
const SIGTERM_POLL_MS = 300;
/** Number of polls before escalating to SIGKILL */
const KILL_POLL_COUNT = 10;
/** Wait after SIGKILL before returning (ms) */
const SIGKILL_WAIT_MS = 500;
/** Wait between lock file reads for TOCTOU mitigation (ms) */
const LOCK_RECHECK_DELAY_MS = 500;
/** Number of lock-file checks before deciding the port holder is not a fresh leader. */
const LOCK_RECHECK_ATTEMPTS = 2;
/** PID used by the OS init/launchd process; direct children are effectively orphaned. */
const ROOT_PARENT_PID = 1;
/** Number of `ps` columns requested by inspectProcess: user, pid, ppid, command. */
const PROCESS_INSPECTION_FIELD_COUNT = 4;
let _logger = null;
async function getLogger() {
if (!_logger) {
try {
_logger = (await import('../../utils/logger.js')).logger;
}
catch { /* fallback below */ }
}
return _logger;
}
const logger = {
warn: async (...args) => {
const l = await getLogger();
if (l)
l.warn(args[0], args[1]);
else
console.error('[WARN]', ...args);
},
info: async (...args) => {
const l = await getLogger();
if (l)
l.info(args[0], args[1]);
else
console.error('[INFO]', ...args);
},
debug: async (...args) => {
const l = await getLogger();
if (l)
l.debug(args[0], args[1]);
},
};
const MCP_HOST_PARENT_PATTERNS = [
/Claude\.app\/Contents\/Helpers\/disclaimer/i,
/Codex\.app\/Contents\/Resources\/codex app-server/i,
/Cursor\.app\//i,
/Windsurf\.app\//i,
];
export function isRecognizedMcpHostParent(command) {
const normalizedCommand = UnicodeValidator.normalize(command).normalizedContent;
return MCP_HOST_PARENT_PATTERNS.some((pattern) => pattern.test(normalizedCommand));
}
function isDollhouseProcessCommand(cmdLine) {
const normalizedCommand = UnicodeValidator.normalize(cmdLine).normalizedContent;
const isDollhouseBin = /(?:^|\/)dollhousemcp(?:\s|$)/.test(normalizedCommand) ||
normalizedCommand.includes('.bin/dollhousemcp');
const isMcpServerBin = normalizedCommand.includes('.bin/mcp-server') ||
/(?:dollhousemcp|mcp-server)[/\\]dist[/\\]index\.js/.test(normalizedCommand);
return isDollhouseBin || isMcpServerBin;
}
function parsePidToken(value) {
if (!value)
return null;
for (let i = 0; i < value.length; i++) {
const codePoint = value.codePointAt(i);
if (codePoint === undefined || codePoint < 48 || codePoint > 57) {
return null;
}
}
const parsed = Number.parseInt(value, 10);
if (!Number.isSafeInteger(parsed) || parsed < ROOT_PARENT_PID) {
return null;
}
return parsed;
}
function isWhitespaceChar(value) {
return value === ' ' || value === '\t' || value === '\n' || value === '\r' || value === '\f' || value === '\v';
}
function buildKillOutcome(killed, reason, processInfo, parentCommand, detail) {
return {
killed,
reason,
pid: processInfo.pid,
parentPid: processInfo.parentPid,
command: processInfo.command,
parentCommand,
...(detail ? { detail } : {}),
};
}
async function getKillGuardFailure(processInfo, port, options = {}) {
const currentUser = (await import('node:os')).userInfo().username;
if (processInfo.user !== currentUser) {
await logger.warn(`[WebUI] Port ${port} held by different user (pid ${processInfo.pid}) — not killing`);
return buildKillOutcome(false, 'different_user', processInfo);
}
if (!isDollhouseProcessCommand(processInfo.command)) {
await logger.warn(`[WebUI] Port ${port} held by non-DollhouseMCP process (pid ${processInfo.pid}) — not killing`, {
cmdLine: processInfo.command,
});
return buildKillOutcome(false, 'not_dollhouse_process', processInfo);
}
if (processInfo.parentPid <= ROOT_PARENT_PID || !isPidAlive(processInfo.parentPid)) {
return null;
}
const parentCommand = (await getProcessCommand(processInfo.parentPid)) ?? undefined;
if (!options.allowActiveHostParent && parentCommand && isRecognizedMcpHostParent(parentCommand)) {
await logger.warn(`[WebUI] Port ${port} held by active client-backed DollhouseMCP process (pid ${processInfo.pid}) — not killing`, {
cmdLine: processInfo.command,
parentPid: processInfo.parentPid,
parentCommand,
});
return buildKillOutcome(false, 'active_host_parent', processInfo, parentCommand);
}
return null;
}
async function terminateProcess(processInfo, port, parentCommand) {
process.kill(processInfo.pid, 'SIGTERM');
logger.warn(`[WebUI] Sent SIGTERM to stale process ${processInfo.pid} on port ${port}`, {
cmdLine: processInfo.command,
parentPid: processInfo.parentPid,
parentCommand,
});
for (let i = 0; i < KILL_POLL_COUNT; i++) {
await new Promise(r => setTimeout(r, SIGTERM_POLL_MS));
if (!isPidAlive(processInfo.pid)) {
return buildKillOutcome(true, 'terminated', processInfo, parentCommand);
}
}
process.kill(processInfo.pid, 'SIGKILL');
logger.warn(`[WebUI] Sent SIGKILL to stale process ${processInfo.pid} on port ${port}`);
await new Promise(r => setTimeout(r, SIGKILL_WAIT_MS));
return isPidAlive(processInfo.pid)
? buildKillOutcome(false, 'still_alive', processInfo, parentCommand)
: buildKillOutcome(true, 'terminated', processInfo, parentCommand);
}
function splitProcessInspectionFields(line) {
const fields = [];
let index = 0;
const normalizedLine = UnicodeValidator.normalize(line).normalizedContent.trim();
while (index < normalizedLine.length && fields.length < PROCESS_INSPECTION_FIELD_COUNT - 1) {
while (index < normalizedLine.length && isWhitespaceChar(normalizedLine[index])) {
index++;
}
if (index >= normalizedLine.length)
break;
const fieldStart = index;
while (index < normalizedLine.length && !isWhitespaceChar(normalizedLine[index])) {
index++;
}
fields.push(normalizedLine.slice(fieldStart, index));
}
while (index < normalizedLine.length && isWhitespaceChar(normalizedLine[index])) {
index++;
}
if (index < normalizedLine.length) {
fields.push(normalizedLine.slice(index));
}
return fields.length === PROCESS_INSPECTION_FIELD_COUNT ? fields : null;
}
async function inspectProcess(pid) {
const { execFile: execFileCb } = await import('node:child_process');
const { promisify } = await import('node:util');
const execFileAsync = promisify(execFileCb);
try {
const { stdout } = await execFileAsync('ps', ['-p', String(pid), '-o', 'user=,pid=,ppid=,command='], { timeout: COMMAND_TIMEOUT_MS });
const fields = splitProcessInspectionFields(stdout);
if (!fields)
return null;
const [user, pidToken, parentPidToken, command] = fields;
const parsedPid = parsePidToken(pidToken);
const parsedParentPid = parsePidToken(parentPidToken);
if (parsedPid === null || parsedParentPid === null)
return null;
return {
user: UnicodeValidator.normalize(user).normalizedContent,
pid: parsedPid,
parentPid: parsedParentPid,
command: UnicodeValidator.normalize(command).normalizedContent,
};
}
catch {
return null;
}
}
async function getProcessCommand(pid) {
const { execFile: execFileCb } = await import('node:child_process');
const { promisify } = await import('node:util');
const execFileAsync = promisify(execFileCb);
try {
const { stdout } = await execFileAsync('ps', ['-p', String(pid), '-o', 'command='], { timeout: COMMAND_TIMEOUT_MS });
const normalized = UnicodeValidator.normalize(stdout).normalizedContent.trim();
return normalized || null;
}
catch {
return null;
}
}
function isPidAlive(pid) {
try {
process.kill(pid, 0);
return true;
}
catch {
return false;
}
}
/**
* Find the PID of the process listening on a given port.
* Uses lsof on macOS/Linux. Returns null if not found or on error.
*
* Timeout: 1s — lsof on localhost is typically <100ms. The 1s ceiling
* handles slow NFS-mounted /dev/fd or overloaded CI runners without
* delaying startup noticeably.
*/
export async function findPidOnPort(port) {
const { execFile: execFileCb } = await import('node:child_process');
const { promisify } = await import('node:util');
const execFileAsync = promisify(execFileCb);
// Query only LISTEN sockets so established client connections to the console
// don't get mistaken for the owning leader process.
for (const cmd of [
{ bin: 'lsof', args: ['-nP', '-iTCP:' + String(port), '-sTCP:LISTEN', '-t'] },
{ bin: 'ss', args: ['-ltnp', `sport = :${port}`] },
{ bin: 'fuser', args: ['-n', 'tcp', String(port)] },
]) {
try {
const { stdout, stderr } = await execFileAsync(cmd.bin, cmd.args, { timeout: COMMAND_TIMEOUT_MS });
const output = (stdout || stderr || '').trim();
if (!output) {
continue;
}
let pids = [];
if (cmd.bin === 'ss') {
pids = output
.split('\n')
.flatMap((line) => Array.from(line.matchAll(/pid=(\d+)/g), (match) => parsePidToken(match[1])))
.filter((pid) => pid !== null);
}
else {
// fuser outputs to stderr on some systems; lsof emits one PID per line.
pids = output
.split(/\s+/)
.map((token) => parsePidToken(token))
.filter((pid) => pid !== null);
}
const otherPid = pids.find(p => p !== process.pid);
if (otherPid)
return otherPid;
}
catch {
continue; // command not found or no results — try next
}
}
return null;
}
/**
* Kill a stale process holding a port. Sends SIGTERM, waits briefly,
* then SIGKILL if still alive. Only kills DollhouseMCP processes
* (verified by checking the command line and user ownership).
*
* Timeout: 1s for ps verification. Kill wait: 300ms × 10 polls = 3s
* before escalating to SIGKILL. Total worst case: ~4s.
*/
export async function killStaleProcess(pid, port) {
const outcome = await killStaleProcessDetailed(pid, port);
return outcome.killed;
}
export async function killStaleProcessDetailed(pid, port, options = {}) {
const processInfo = await inspectProcess(pid);
if (!processInfo) {
await logger.debug(`[WebUI] Cannot verify process ${pid} — skipping kill`);
return { killed: false, reason: 'inspect_failed', pid };
}
const guardFailure = await getKillGuardFailure(processInfo, port, options);
if (guardFailure) {
return guardFailure;
}
const parentCommand = processInfo.parentPid > ROOT_PARENT_PID
? (await getProcessCommand(processInfo.parentPid)) ?? undefined
: undefined;
await logger.debug(`[WebUI] Verified stale process ${pid} is DollhouseMCP`, { cmdLine: processInfo.command, parentPid: processInfo.parentPid, parentCommand });
try {
return await terminateProcess(processInfo, port, parentCommand);
}
catch (err) {
if (!isPidAlive(pid)) {
return buildKillOutcome(true, 'already_dead', processInfo, parentCommand);
}
return buildKillOutcome(false, 'signal_failed', processInfo, parentCommand, err instanceof Error ? err.message : String(err));
}
}
/**
* Detect and recover from a stale process squatting on the port.
* Compares the port holder's PID against the leader lock file to determine
* if it's a squatter. Returns true if the squatter was killed.
*
* Timeouts: lsof 1s, ps 1s, SIGTERM wait 3s — max ~5s total.
*/
export async function recoverStalePort(port) {
const stalePid = await findPidOnPort(port);
if (!stalePid)
return false;
// TOCTOU mitigation: a new process may have just bound the port but not yet
// written its lock file. Read the lock, pause, re-read. If the second read
// now matches the port holder, it's a fresh leader — don't kill.
const { readLeaderLock } = await import('./LeaderElection.js');
for (let check = 0; check < LOCK_RECHECK_ATTEMPTS; check++) {
try {
const lock = await readLeaderLock();
if (lock?.pid === stalePid && lock?.port === port && lock.pid !== process.pid) {
await logger.warn(`[WebUI] Port ${port} held by legitimate leader (pid ${stalePid}) — not killing`);
return false;
}
}
catch {
// Can't read lock file — continue to next check or kill
}
if (check < LOCK_RECHECK_ATTEMPTS - 1) {
await new Promise(r => setTimeout(r, LOCK_RECHECK_DELAY_MS));
}
}
const outcome = await killStaleProcessDetailed(stalePid, port);
if (outcome.killed) {
logger.info(`[WebUI] Stale process ${stalePid} removed from port ${port}`);
await new Promise(r => setTimeout(r, SIGKILL_WAIT_MS)); // brief pause for port release
}
else {
await logger.debug(`[WebUI] Stale-port recovery skipped for pid ${stalePid}`, {
reason: outcome.reason,
parentPid: outcome.parentPid,
parentCommand: outcome.parentCommand,
detail: outcome.detail,
});
}
return outcome.killed;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3RhbGVQcm9jZXNzUmVjb3ZlcnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvd2ViL2NvbnNvbGUvU3RhbGVQcm9jZXNzUmVjb3ZlcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7OztHQVNHO0FBRUgsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFFakYsOEVBQThFO0FBQzlFLHFFQUFxRTtBQUNyRSxrREFBa0Q7QUFDbEQsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLENBQUM7QUFDaEMsb0VBQW9FO0FBQ3BFLE1BQU0sZUFBZSxHQUFHLEdBQUcsQ0FBQztBQUM1QixtREFBbUQ7QUFDbkQsTUFBTSxlQUFlLEdBQUcsRUFBRSxDQUFDO0FBQzNCLCtDQUErQztBQUMvQyxNQUFNLGVBQWUsR0FBRyxHQUFHLENBQUM7QUFDNUIsOERBQThEO0FBQzlELE1BQU0scUJBQXFCLEdBQUcsR0FBRyxDQUFDO0FBQ2xDLHdGQUF3RjtBQUN4RixNQUFNLHFCQUFxQixHQUFHLENBQUMsQ0FBQztBQUNoQyx5RkFBeUY7QUFDekYsTUFBTSxlQUFlLEdBQUcsQ0FBQyxDQUFDO0FBQzFCLG9GQUFvRjtBQUNwRixNQUFNLDhCQUE4QixHQUFHLENBQUMsQ0FBQztBQUV6QyxJQUFJLE9BQU8sR0FBeUQsSUFBSSxDQUFDO0FBQ3pFLEtBQUssVUFBVSxTQUFTO0lBQ3RCLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNiLElBQUksQ0FBQztZQUFDLE9BQU8sR0FBRyxDQUFDLE1BQU0sTUFBTSxDQUFDLHVCQUF1QixDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFBQyxDQUFDO1FBQ2pFLE1BQU0sQ0FBQyxDQUFDLG9CQUFvQixDQUFDLENBQUM7SUFDaEMsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFDRCxNQUFNLE1BQU0sR0FBRztJQUNiLElBQUksRUFBRSxLQUFLLEVBQUUsR0FB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QUssR0FBRyxxQkFBcUIsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN0QyxNQUFNLElBQUksT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxxQkFBcUIsQ0FBQyxDQUFDLENBQUM7UUFDL0QsQ0FBQztJQUNILENBQUM7SUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLHdCQUF3QixDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMvRCxJQUFJLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNuQixNQUFNLENBQUMsSUFBSSxDQUFDLHlCQUF5QixRQUFRLHNCQUFzQixJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzNFLE1BQU0sSUFBSSxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQywrQkFBK0I7SUFDekYsQ0FBQztTQUFNLENBQUM7UUFDTixNQUFNLE1BQU0sQ0FBQyxLQUFLLENBQUMsK0NBQStDLFFBQVEsRUFBRSxFQUFFO1lBQzVFLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTTtZQUN0QixTQUFTLEVBQUUsT0FBTyxDQUFDLFNBQVM7WUFDNUIsYUFBYSxFQUFFLE9BQU8sQ0FBQyxhQUFhO1lBQ3BDLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTTtTQUN2QixDQUFDLENBQUM7SUFDTCxDQUFDO0lBQ0QsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDO0FBQ3hCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFN0YWxlIHByb2Nlc3MgZGV0ZWN0aW9uIGFuZCByZWNvdmVyeSAoIzE4NTApLlxuICpcbiAqIEZpbmRzIGFuZCBraWxscyB6b21iaWUgRG9sbGhvdXNlTUNQIHByb2Nlc3NlcyB0aGF0IHNxdWF0IG9uIHRoZSBjb25zb2xlXG4gKiBwb3J0IGFmdGVyIHRoZWlyIHNlc3Npb24gaGFzIGVuZGVkLiBVc2VkIGJ5IGJpbmRBbmRMaXN0ZW4gaW4gc2VydmVyLnRzXG4gKiB3aGVuIEVBRERSSU5VU0Ugb2NjdXJzLlxuICpcbiAqIEV4dHJhY3RlZCB0byBhIHN0YW5kYWxvbmUgbW9kdWxlIHNvIGl0IGNhbiBiZSB0ZXN0ZWQgd2l0aG91dCBpbXBvcnRpbmdcbiAqIHRoZSBmdWxsIEV4cHJlc3Mgc2VydmVyIGFuZCBpdHMgZGVwZW5kZW5jeSBjaGFpbi5cbiAqL1xuXG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcblxuLy8gVXNlIGxhenkgaW1wb3J0IGZvciBsb2dnZXIgdG8gYXZvaWQgcHVsbGluZyBpbiB0aGUgZnVsbCBlbnYudHMvY29uZmlnIGNoYWluXG4vLyBhdCBtb2R1bGUgbG9hZCB0aW1lLiBUaGlzIGtlZXBzIHRoZSBtb2R1bGUgaW5kZXBlbmRlbnRseSB0ZXN0YWJsZS5cbi8qKiBUaW1lb3V0IGZvciBsc29mL2Z1c2VyL3BzIHN5c3RlbSBjYWxscyAobXMpICovXG5jb25zdCBDT01NQU5EX1RJTUVPVVRfTVMgPSAxMDAwO1xuLyoqIFBvbGxpbmcgaW50ZXJ2YWwgd2hlbiB3YWl0aW5nIGZvciBTSUdURVJNIHRvIHRha2UgZWZmZWN0IChtcykgKi9cbmNvbnN0IFNJR1RFUk1fUE9MTF9NUyA9IDMwMDtcbi8qKiBOdW1iZXIgb2YgcG9sbHMgYmVmb3JlIGVzY2FsYXRpbmcgdG8gU0lHS0lMTCAqL1xuY29uc3QgS0lMTF9QT0xMX0NPVU5UID0gMTA7XG4vKiogV2FpdCBhZnRlciBTSUdLSUxMIGJlZm9yZSByZXR1cm5pbmcgKG1zKSAqL1xuY29uc3QgU0lHS0lMTF9XQUlUX01TID0gNTAwO1xuLyoqIFdhaXQgYmV0d2VlbiBsb2NrIGZpbGUgcmVhZHMgZm9yIFRPQ1RPVSBtaXRpZ2F0aW9uIChtcykgKi9cbmNvbnN0IExPQ0tfUkVDSEVDS19ERUxBWV9NUyA9IDUwMDtcbi8qKiBOdW1iZXIgb2YgbG9jay1maWxlIGNoZWNrcyBiZWZvcmUgZGVjaWRpbmcgdGhlIHBvcnQgaG9sZGVyIGlzIG5vdCBhIGZyZXNoIGxlYWRlci4gKi9cbmNvbnN0IExPQ0tfUkVDSEVDS19BVFRFTVBUUyA9IDI7XG4vKiogUElEIHVzZWQgYnkgdGhlIE9TIGluaXQvbGF1bmNoZCBwcm9jZXNzOyBkaXJlY3QgY2hpbGRyZW4gYXJlIGVmZmVjdGl2ZWx5IG9ycGhhbmVkLiAqL1xuY29uc3QgUk9PVF9QQVJFTlRfUElEID0gMTtcbi8qKiBOdW1iZXIgb2YgYHBzYCBjb2x1bW5zIHJlcXVlc3RlZCBieSBpbnNwZWN0UHJvY2VzczogdXNlciwgcGlkLCBwcGlkLCBjb21tYW5kLiAqL1xuY29uc3QgUFJPQ0VTU19JTlNQRUNUSU9OX0ZJRUxEX0NPVU5UID0gNDtcblxubGV0IF9sb2dnZXI6IHR5cGVvZiBpbXBvcnQoJy4uLy4uL3V0aWxzL2xvZ2dlci5qcycpLmxvZ2dlciB8IG51bGwgPSBudWxsO1xuYXN5bmMgZnVuY3Rpb24gZ2V0TG9nZ2VyKCkge1xuICBpZiAoIV9sb2dnZXIpIHtcbiAgICB0cnkgeyBfbG9nZ2VyID0gKGF3YWl0IGltcG9ydCgnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJykpLmxvZ2dlcjsgfVxuICAgIGNhdGNoIHsgLyogZmFsbGJhY2sgYmVsb3cgKi8gfVxuICB9XG4gIHJldHVybiBfbG9nZ2VyO1xufVxuY29uc3QgbG9nZ2VyID0ge1xuICB3YXJuOiBhc3luYyAoLi4uYXJnczogdW5rbm93bltdKSA9PiB7XG4gICAgY29uc3QgbCA9IGF3YWl0IGdldExvZ2dlcigpO1xuICAgIGlmIChsKSBsLndhcm4oYXJnc1swXSBhcyBzdHJpbmcsIGFyZ3NbMV0pO1xuICAgIGVsc2UgY29uc29sZS5lcnJvcignW1dBUk5dJywgLi4uYXJncyk7XG4gIH0sXG4gIGluZm86IGFzeW5jICguLi5hcmdzOiB1bmtub3duW10pID0+IHtcbiAgICBjb25zdCBsID0gYXdhaXQgZ2V0TG9nZ2VyKCk7XG4gICAgaWYgKGwpIGwuaW5mbyhhcmdzWzBdIGFzIHN0cmluZywgYXJnc1sxXSk7XG4gICAgZWxzZSBjb25zb2xlLmVycm9yKCdbSU5GT10nLCAuLi5hcmdzKTtcbiAgfSxcbiAgZGVidWc6IGFzeW5jICguLi5hcmdzOiB1bmtub3duW10pID0+IHtcbiAgICBjb25zdCBsID0gYXdhaXQgZ2V0TG9nZ2VyKCk7XG4gICAgaWYgKGwpIGwuZGVidWcoYXJnc1swXSBhcyBzdHJpbmcsIGFyZ3NbMV0pO1xuICB9LFxufTtcblxuY29uc3QgTUNQX0hPU1RfUEFSRU5UX1BBVFRFUk5TID0gW1xuICAvQ2xhdWRlXFwuYXBwXFwvQ29udGVudHNcXC9IZWxwZXJzXFwvZGlzY2xhaW1lci9pLFxuICAvQ29kZXhcXC5hcHBcXC9Db250ZW50c1xcL1Jlc291cmNlc1xcL2NvZGV4IGFwcC1zZXJ2ZXIvaSxcbiAgL0N1cnNvclxcLmFwcFxcLy9pLFxuICAvV2luZHN1cmZcXC5hcHBcXC8vaSxcbl07XG5cbmludGVyZmFjZSBQcm9jZXNzSW5zcGVjdGlvbiB7XG4gIHVzZXI6IHN0cmluZztcbiAgcGlkOiBudW1iZXI7XG4gIHBhcmVudFBpZDogbnVtYmVyO1xuICBjb21tYW5kOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgS2lsbFN0YWxlUHJvY2Vzc091dGNvbWUge1xuICBraWxsZWQ6IGJvb2xlYW47XG4gIHJlYXNvbjpcbiAgICB8ICdpbnNwZWN0X2ZhaWxlZCdcbiAgICB8ICdkaWZmZXJlbnRfdXNlcidcbiAgICB8ICdub3RfZG9sbGhvdXNlX3Byb2Nlc3MnXG4gICAgfCAnYWN0aXZlX2hvc3RfcGFyZW50J1xuICAgIHwgJ3Rlcm1pbmF0ZWQnXG4gICAgfCAnYWxyZWFkeV9kZWFkJ1xuICAgIHwgJ3N0aWxsX2FsaXZlJ1xuICAgIHwgJ3NpZ25hbF9mYWlsZWQnO1xuICBwaWQ6IG51bWJlcjtcbiAgcGFyZW50UGlkPzogbnVtYmVyO1xuICBjb21tYW5kPzogc3RyaW5nO1xuICBwYXJlbnRDb21tYW5kPzogc3RyaW5nO1xuICBkZXRhaWw/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgS2lsbFN0YWxlUHJvY2Vzc09wdGlvbnMge1xuICBhbGxvd0FjdGl2ZUhvc3RQYXJlbnQ/OiBib29sZWFuO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNSZWNvZ25pemVkTWNwSG9zdFBhcmVudChjb21tYW5kOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3Qgbm9ybWFsaXplZENvbW1hbmQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShjb21tYW5kKS5ub3JtYWxpemVkQ29udGVudDtcbiAgcmV0dXJuIE1DUF9IT1NUX1BBUkVOVF9QQVRURVJOUy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3Qobm9ybWFsaXplZENvbW1hbmQpKTtcbn1cblxuZnVuY3Rpb24gaXNEb2xsaG91c2VQcm9jZXNzQ29tbWFuZChjbWRMaW5lOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3Qgbm9ybWFsaXplZENvbW1hbmQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShjbWRMaW5lKS5ub3JtYWxpemVkQ29udGVudDtcbiAgY29uc3QgaXNEb2xsaG91c2VCaW4gPSAvKD86XnxcXC8pZG9sbGhvdXNlbWNwKD86XFxzfCQpLy50ZXN0KG5vcm1hbGl6ZWRDb21tYW5kKSB8fFxuICAgIG5vcm1hbGl6ZWRDb21tYW5kLmluY2x1ZGVzKCcuYmluL2RvbGxob3VzZW1jcCcpO1xuICBjb25zdCBpc01jcFNlcnZlckJpbiA9IG5vcm1hbGl6ZWRDb21tYW5kLmluY2x1ZGVzKCcuYmluL21jcC1zZXJ2ZXInKSB8fFxuICAgIC8oPzpkb2xsaG91c2VtY3B8bWNwLXNlcnZlcilbL1xcXFxdZGlzdFsvXFxcXF1pbmRleFxcLmpzLy50ZXN0KG5vcm1hbGl6ZWRDb21tYW5kKTtcbiAgcmV0dXJuIGlzRG9sbGhvdXNlQmluIHx8IGlzTWNwU2VydmVyQmluO1xufVxuXG5mdW5jdGlvbiBwYXJzZVBpZFRva2VuKHZhbHVlOiBzdHJpbmcpOiBudW1iZXIgfCBudWxsIHtcbiAgaWYgKCF2YWx1ZSkgcmV0dXJuIG51bGw7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWUubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBjb2RlUG9pbnQgPSB2YWx1ZS5jb2RlUG9pbnRBdChpKTtcbiAgICBpZiAoY29kZVBvaW50ID09PSB1bmRlZmluZWQgfHwgY29kZVBvaW50IDwgNDggfHwgY29kZVBvaW50ID4gNTcpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHBhcnNlZCA9IE51bWJlci5wYXJzZUludCh2YWx1ZSwgMTApO1xuICBpZiAoIU51bWJlci5pc1NhZmVJbnRlZ2VyKHBhcnNlZCkgfHwgcGFyc2VkIDwgUk9PVF9QQVJFTlRfUElEKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgcmV0dXJuIHBhcnNlZDtcbn1cblxuZnVuY3Rpb24gaXNXaGl0ZXNwYWNlQ2hhcih2YWx1ZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiB2YWx1ZSA9PT0gJyAnIHx8IHZhbHVlID09PSAnXFx0JyB8fCB2YWx1ZSA9PT0gJ1xcbicgfHwgdmFsdWUgPT09ICdcXHInIHx8IHZhbHVlID09PSAnXFxmJyB8fCB2YWx1ZSA9PT0gJ1xcdic7XG59XG5cbmZ1bmN0aW9uIGJ1aWxkS2lsbE91dGNvbWUoXG4gIGtpbGxlZDogYm9vbGVhbixcbiAgcmVhc29uOiBLaWxsU3RhbGVQcm9jZXNzT3V0Y29tZVsncmVhc29uJ10sXG4gIHByb2Nlc3NJbmZvOiBQcm9jZXNzSW5zcGVjdGlvbixcbiAgcGFyZW50Q29tbWFuZD86IHN0cmluZyxcbiAgZGV0YWlsPzogc3RyaW5nLFxuKTogS2lsbFN0YWxlUHJvY2Vzc091dGNvbWUge1xuICByZXR1cm4ge1xuICAgIGtpbGxlZCxcbiAgICByZWFzb24sXG4gICAgcGlkOiBwcm9jZXNzSW5mby5waWQsXG4gICAgcGFyZW50UGlkOiBwcm9jZXNzSW5mby5wYXJlbnRQaWQsXG4gICAgY29tbWFuZDogcHJvY2Vzc0luZm8uY29tbWFuZCxcbiAgICBwYXJlbnRDb21tYW5kLFxuICAgIC4uLihkZXRhaWwgPyB7IGRldGFpbCB9IDoge30pLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXRLaWxsR3VhcmRGYWlsdXJlKFxuICBwcm9jZXNzSW5mbzogUHJvY2Vzc0luc3BlY3Rpb24sXG4gIHBvcnQ6IG51bWJlcixcbiAgb3B0aW9uczogS2lsbFN0YWxlUHJvY2Vzc09wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8S2lsbFN0YWxlUHJvY2Vzc091dGNvbWUgfCBudWxsPiB7XG4gIGNvbnN0IGN1cnJlbnRVc2VyID0gKGF3YWl0IGltcG9ydCgnbm9kZTpvcycpKS51c2VySW5mbygpLnVzZXJuYW1lO1xuICBpZiAocHJvY2Vzc0luZm8udXNlciAhPT0gY3VycmVudFVzZXIpIHtcbiAgICBhd2FpdCBsb2dnZXIud2FybihgW1dlYlVJXSBQb3J0ICR7cG9ydH0gaGVsZCBieSBkaWZmZXJlbnQgdXNlciAocGlkICR7cHJvY2Vzc0luZm8ucGlkfSkg4oCUIG5vdCBraWxsaW5nYCk7XG4gICAgcmV0dXJuIGJ1aWxkS2lsbE91dGNvbWUoZmFsc2UsICdkaWZmZXJlbnRfdXNlcicsIHByb2Nlc3NJbmZvKTtcbiAgfVxuXG4gIGlmICghaXNEb2xsaG91c2VQcm9jZXNzQ29tbWFuZChwcm9jZXNzSW5mby5jb21tYW5kKSkge1xuICAgIGF3YWl0IGxvZ2dlci53YXJuKGBbV2ViVUldIFBvcnQgJHtwb3J0fSBoZWxkIGJ5IG5vbi1Eb2xsaG91c2VNQ1AgcHJvY2VzcyAocGlkICR7cHJvY2Vzc0luZm8ucGlkfSkg4oCUIG5vdCBraWxsaW5nYCwge1xuICAgICAgY21kTGluZTogcHJvY2Vzc0luZm8uY29tbWFuZCxcbiAgICB9KTtcbiAgICByZXR1cm4gYnVpbGRLaWxsT3V0Y29tZShmYWxzZSwgJ25vdF9kb2xsaG91c2VfcHJvY2VzcycsIHByb2Nlc3NJbmZvKTtcbiAgfVxuXG4gIGlmIChwcm9jZXNzSW5mby5wYXJlbnRQaWQgPD0gUk9PVF9QQVJFTlRfUElEIHx8ICFpc1BpZEFsaXZlKHByb2Nlc3NJbmZvLnBhcmVudFBpZCkpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGNvbnN0IHBhcmVudENvbW1hbmQgPSAoYXdhaXQgZ2V0UHJvY2Vzc0NvbW1hbmQocHJvY2Vzc0luZm8ucGFyZW50UGlkKSkgPz8gdW5kZWZpbmVkO1xuICBpZiAoIW9wdGlvbnMuYWxsb3dBY3RpdmVIb3N0UGFyZW50ICYmIHBhcmVudENvbW1hbmQgJiYgaXNSZWNvZ25pemVkTWNwSG9zdFBhcmVudChwYXJlbnRDb21tYW5kKSkge1xuICAgIGF3YWl0IGxvZ2dlci53YXJuKGBbV2ViVUldIFBvcnQgJHtwb3J0fSBoZWxkIGJ5IGFjdGl2ZSBjbGllbnQtYmFja2VkIERvbGxob3VzZU1DUCBwcm9jZXNzIChwaWQgJHtwcm9jZXNzSW5mby5waWR9KSDigJQgbm90IGtpbGxpbmdgLCB7XG4gICAgICBjbWRMaW5lOiBwcm9jZXNzSW5mby5jb21tYW5kLFxuICAgICAgcGFyZW50UGlkOiBwcm9jZXNzSW5mby5wYXJlbnRQaWQsXG4gICAgICBwYXJlbnRDb21tYW5kLFxuICAgIH0pO1xuICAgIHJldHVybiBidWlsZEtpbGxPdXRjb21lKGZhbHNlLCAnYWN0aXZlX2hvc3RfcGFyZW50JywgcHJvY2Vzc0luZm8sIHBhcmVudENvbW1hbmQpO1xuICB9XG5cbiAgcmV0dXJuIG51bGw7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHRlcm1pbmF0ZVByb2Nlc3MoXG4gIHByb2Nlc3NJbmZvOiBQcm9jZXNzSW5zcGVjdGlvbixcbiAgcG9ydDogbnVtYmVyLFxuICBwYXJlbnRDb21tYW5kPzogc3RyaW5nLFxuKTogUHJvbWlzZTxLaWxsU3RhbGVQcm9jZXNzT3V0Y29tZT4ge1xuICBwcm9jZXNzLmtpbGwocHJvY2Vzc0luZm8ucGlkLCAnU0lHVEVSTScpO1xuICBsb2dnZXIud2FybihgW1dlYlVJXSBTZW50IFNJR1RFUk0gdG8gc3RhbGUgcHJvY2VzcyAke3Byb2Nlc3NJbmZvLnBpZH0gb24gcG9ydCAke3BvcnR9YCwge1xuICAgIGNtZExpbmU6IHByb2Nlc3NJbmZvLmNvbW1hbmQsXG4gICAgcGFyZW50UGlkOiBwcm9jZXNzSW5mby5wYXJlbnRQaWQsXG4gICAgcGFyZW50Q29tbWFuZCxcbiAgfSk7XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBLSUxMX1BPTExfQ09VTlQ7IGkrKykge1xuICAgIGF3YWl0IG5ldyBQcm9taXNlKHIgPT4gc2V0VGltZW91dChyLCBTSUdURVJNX1BPTExfTVMpKTtcbiAgICBpZiAoIWlzUGlkQWxpdmUocHJvY2Vzc0luZm8ucGlkKSkge1xuICAgICAgcmV0dXJuIGJ1aWxkS2lsbE91dGNvbWUodHJ1ZSwgJ3Rlcm1pbmF0ZWQnLCBwcm9jZXNzSW5mbywgcGFyZW50Q29tbWFuZCk7XG4gICAgfVxuICB9XG5cbiAgcHJvY2Vzcy5raWxsKHByb2Nlc3NJbmZvLnBpZCwgJ1NJR0tJTEwnKTtcbiAgbG9nZ2VyLndhcm4oYFtXZWJVSV0gU2VudCBTSUdLSUxMIHRvIHN0YWxlIHByb2Nlc3MgJHtwcm9jZXNzSW5mby5waWR9IG9uIHBvcnQgJHtwb3J0fWApO1xuICBhd2FpdCBuZXcgUHJvbWlzZShyID0+IHNldFRpbWVvdXQociwgU0lHS0lMTF9XQUlUX01TKSk7XG4gIHJldHVybiBpc1BpZEFsaXZlKHByb2Nlc3NJbmZvLnBpZClcbiAgICA/IGJ1aWxkS2lsbE91dGNvbWUoZmFsc2UsICdzdGlsbF9hbGl2ZScsIHByb2Nlc3NJbmZvLCBwYXJlbnRDb21tYW5kKVxuICAgIDogYnVpbGRLaWxsT3V0Y29tZSh0cnVlLCAndGVybWluYXRlZCcsIHByb2Nlc3NJbmZvLCBwYXJlbnRDb21tYW5kKTtcbn1cblxuZnVuY3Rpb24gc3BsaXRQcm9jZXNzSW5zcGVjdGlvbkZpZWxkcyhsaW5lOiBzdHJpbmcpOiBzdHJpbmdbXSB8IG51bGwge1xuICBjb25zdCBmaWVsZHM6IHN0cmluZ1tdID0gW107XG4gIGxldCBpbmRleCA9IDA7XG4gIGNvbnN0IG5vcm1hbGl6ZWRMaW5lID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUobGluZSkubm9ybWFsaXplZENvbnRlbnQudHJpbSgpO1xuXG4gIHdoaWxlIChpbmRleCA8IG5vcm1hbGl6ZWRMaW5lLmxlbmd0aCAmJiBmaWVsZHMubGVuZ3RoIDwgUFJPQ0VTU19JTlNQRUNUSU9OX0ZJRUxEX0NPVU5UIC0gMSkge1xuICAgIHdoaWxlIChpbmRleCA8IG5vcm1hbGl6ZWRMaW5lLmxlbmd0aCAmJiBpc1doaXRlc3BhY2VDaGFyKG5vcm1hbGl6ZWRMaW5lW2luZGV4XSkpIHtcbiAgICAgIGluZGV4Kys7XG4gICAgfVxuICAgIGlmIChpbmRleCA+PSBub3JtYWxpemVkTGluZS5sZW5ndGgpIGJyZWFrO1xuXG4gICAgY29uc3QgZmllbGRTdGFydCA9IGluZGV4O1xuICAgIHdoaWxlIChpbmRleCA8IG5vcm1hbGl6ZWRMaW5lLmxlbmd0aCAmJiAhaXNXaGl0ZXNwYWNlQ2hhcihub3JtYWxpemVkTGluZVtpbmRleF0pKSB7XG4gICAgICBpbmRleCsrO1xuICAgIH1cbiAgICBmaWVsZHMucHVzaChub3JtYWxpemVkTGluZS5zbGljZShmaWVsZFN0YXJ0LCBpbmRleCkpO1xuICB9XG5cbiAgd2hpbGUgKGluZGV4IDwgbm9ybWFsaXplZExpbmUubGVuZ3RoICYmIGlzV2hpdGVzcGFjZUNoYXIobm9ybWFsaXplZExpbmVbaW5kZXhdKSkge1xuICAgIGluZGV4Kys7XG4gIH1cbiAgaWYgKGluZGV4IDwgbm9ybWFsaXplZExpbmUubGVuZ3RoKSB7XG4gICAgZmllbGRzLnB1c2gobm9ybWFsaXplZExpbmUuc2xpY2UoaW5kZXgpKTtcbiAgfVxuXG4gIHJldHVybiBmaWVsZHMubGVuZ3RoID09PSBQUk9DRVNTX0lOU1BFQ1RJT05fRklFTERfQ09VTlQgPyBmaWVsZHMgOiBudWxsO1xufVxuXG5hc3luYyBmdW5jdGlvbiBpbnNwZWN0UHJvY2VzcyhwaWQ6IG51bWJlcik6IFByb21pc2U8UHJvY2Vzc0luc3BlY3Rpb24gfCBudWxsPiB7XG4gIGNvbnN0IHsgZXhlY0ZpbGU6IGV4ZWNGaWxlQ2IgfSA9IGF3YWl0IGltcG9ydCgnbm9kZTpjaGlsZF9wcm9jZXNzJyk7XG4gIGNvbnN0IHsgcHJvbWlzaWZ5IH0gPSBhd2FpdCBpbXBvcnQoJ25vZGU6dXRpbCcpO1xuICBjb25zdCBleGVjRmlsZUFzeW5jID0gcHJvbWlzaWZ5KGV4ZWNGaWxlQ2IpO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgeyBzdGRvdXQgfSA9IGF3YWl0IGV4ZWNGaWxlQXN5bmMoXG4gICAgICAncHMnLFxuICAgICAgWyctcCcsIFN0cmluZyhwaWQpLCAnLW8nLCAndXNlcj0scGlkPSxwcGlkPSxjb21tYW5kPSddLFxuICAgICAgeyB0aW1lb3V0OiBDT01NQU5EX1RJTUVPVVRfTVMgfSxcbiAgICApO1xuICAgIGNvbnN0IGZpZWxkcyA9IHNwbGl0UHJvY2Vzc0luc3BlY3Rpb25GaWVsZHMoc3Rkb3V0KTtcbiAgICBpZiAoIWZpZWxkcykgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgW3VzZXIsIHBpZFRva2VuLCBwYXJlbnRQaWRUb2tlbiwgY29tbWFuZF0gPSBmaWVsZHM7XG4gICAgY29uc3QgcGFyc2VkUGlkID0gcGFyc2VQaWRUb2tlbihwaWRUb2tlbik7XG4gICAgY29uc3QgcGFyc2VkUGFyZW50UGlkID0gcGFyc2VQaWRUb2tlbihwYXJlbnRQaWRUb2tlbik7XG4gICAgaWYgKHBhcnNlZFBpZCA9PT0gbnVsbCB8fCBwYXJzZWRQYXJlbnRQaWQgPT09IG51bGwpIHJldHVybiBudWxsO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHVzZXI6IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHVzZXIpLm5vcm1hbGl6ZWRDb250ZW50LFxuICAgICAgcGlkOiBwYXJzZWRQaWQsXG4gICAgICBwYXJlbnRQaWQ6IHBhcnNlZFBhcmVudFBpZCxcbiAgICAgIGNvbW1hbmQ6IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKGNvbW1hbmQpLm5vcm1hbGl6ZWRDb250ZW50LFxuICAgIH07XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldFByb2Nlc3NDb21tYW5kKHBpZDogbnVtYmVyKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gIGNvbnN0IHsgZXhlY0ZpbGU6IGV4ZWNGaWxlQ2IgfSA9IGF3YWl0IGltcG9ydCgnbm9kZTpjaGlsZF9wcm9jZXNzJyk7XG4gIGNvbnN0IHsgcHJvbWlzaWZ5IH0gPSBhd2FpdCBpbXBvcnQoJ25vZGU6dXRpbCcpO1xuICBjb25zdCBleGVjRmlsZUFzeW5jID0gcHJvbWlzaWZ5KGV4ZWNGaWxlQ2IpO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgeyBzdGRvdXQgfSA9IGF3YWl0IGV4ZWNGaWxlQXN5bmMoJ3BzJywgWyctcCcsIFN0cmluZyhwaWQpLCAnLW8nLCAnY29tbWFuZD0nXSwgeyB0aW1lb3V0OiBDT01NQU5EX1RJTUVPVVRfTVMgfSk7XG4gICAgY29uc3Qgbm9ybWFsaXplZCA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHN0ZG91dCkubm9ybWFsaXplZENvbnRlbnQudHJpbSgpO1xuICAgIHJldHVybiBub3JtYWxpemVkIHx8IG51bGw7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbmZ1bmN0aW9uIGlzUGlkQWxpdmUocGlkOiBudW1iZXIpOiBib29sZWFuIHtcbiAgdHJ5IHtcbiAgICBwcm9jZXNzLmtpbGwocGlkLCAwKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8qKlxuICogRmluZCB0aGUgUElEIG9mIHRoZSBwcm9jZXNzIGxpc3RlbmluZyBvbiBhIGdpdmVuIHBvcnQuXG4gKiBVc2VzIGxzb2Ygb24gbWFjT1MvTGludXguIFJldHVybnMgbnVsbCBpZiBub3QgZm91bmQgb3Igb24gZXJyb3IuXG4gKlxuICogVGltZW91dDogMXMg4oCUIGxzb2Ygb24gbG9jYWxob3N0IGlzIHR5cGljYWxseSA8MTAwbXMuIFRoZSAxcyBjZWlsaW5nXG4gKiBoYW5kbGVzIHNsb3cgTkZTLW1vdW50ZWQgL2Rldi9mZCBvciBvdmVybG9hZGVkIENJIHJ1bm5lcnMgd2l0aG91dFxuICogZGVsYXlpbmcgc3RhcnR1cCBub3RpY2VhYmx5LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmluZFBpZE9uUG9ydChwb3J0OiBudW1iZXIpOiBQcm9taXNlPG51bWJlciB8IG51bGw+IHtcbiAgY29uc3QgeyBleGVjRmlsZTogZXhlY0ZpbGVDYiB9ID0gYXdhaXQgaW1wb3J0KCdub2RlOmNoaWxkX3Byb2Nlc3MnKTtcbiAgY29uc3QgeyBwcm9taXNpZnkgfSA9IGF3YWl0IGltcG9ydCgnbm9kZTp1dGlsJyk7XG4gIGNvbnN0IGV4ZWNGaWxlQXN5bmMgPSBwcm9taXNpZnkoZXhlY0ZpbGVDYik7XG5cbiAgLy8gUXVlcnkgb25seSBMSVNURU4gc29ja2V0cyBzbyBlc3RhYmxpc2hlZCBjbGllbnQgY29ubmVjdGlvbnMgdG8gdGhlIGNvbnNvbGVcbiAgLy8gZG9uJ3QgZ2V0IG1pc3Rha2VuIGZvciB0aGUgb3duaW5nIGxlYWRlciBwcm9jZXNzLlxuICBmb3IgKGNvbnN0IGNtZCBvZiBbXG4gICAgeyBiaW46ICdsc29mJywgYXJnczogWyctblAnLCAnLWlUQ1A6JyArIFN0cmluZyhwb3J0KSwgJy1zVENQOkxJU1RFTicsICctdCddIH0sXG4gICAgeyBiaW46ICdzcycsIGFyZ3M6IFsnLWx0bnAnLCBgc3BvcnQgPSA6JHtwb3J0fWBdIH0sXG4gICAgeyBiaW46ICdmdXNlcicsIGFyZ3M6IFsnLW4nLCAndGNwJywgU3RyaW5nKHBvcnQpXSB9LFxuICBdKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHsgc3Rkb3V0LCBzdGRlcnIgfSA9IGF3YWl0IGV4ZWNGaWxlQXN5bmMoY21kLmJpbiwgY21kLmFyZ3MsIHsgdGltZW91dDogQ09NTUFORF9USU1FT1VUX01TIH0pO1xuICAgICAgY29uc3Qgb3V0cHV0ID0gKHN0ZG91dCB8fCBzdGRlcnIgfHwgJycpLnRyaW0oKTtcbiAgICAgIGlmICghb3V0cHV0KSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBsZXQgcGlkczogbnVtYmVyW10gPSBbXTtcbiAgICAgIGlmIChjbWQuYmluID09PSAnc3MnKSB7XG4gICAgICAgIHBpZHMgPSBvdXRwdXRcbiAgICAgICAgICAuc3BsaXQoJ1xcbicpXG4gICAgICAgICAgLmZsYXRNYXAoKGxpbmUpID0+IEFycmF5LmZyb20obGluZS5tYXRjaEFsbCgvcGlkPShcXGQrKS9nKSwgKG1hdGNoKSA9PiBwYXJzZVBpZFRva2VuKG1hdGNoWzFdKSkpXG4gICAgICAgICAgLmZpbHRlcigocGlkKTogcGlkIGlzIG51bWJlciA9PiBwaWQgIT09IG51bGwpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gZnVzZXIgb3V0cHV0cyB0byBzdGRlcnIgb24gc29tZSBzeXN0ZW1zOyBsc29mIGVtaXRzIG9uZSBQSUQgcGVyIGxpbmUuXG4gICAgICAgIHBpZHMgPSBvdXRwdXRcbiAgICAgICAgICAuc3BsaXQoL1xccysvKVxuICAgICAgICAgIC5tYXAoKHRva2VuKSA9PiBwYXJzZVBpZFRva2VuKHRva2VuKSlcbiAgICAgICAgICAuZmlsdGVyKChwaWQpOiBwaWQgaXMgbnVtYmVyID0+IHBpZCAhPT0gbnVsbCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IG90aGVyUGlkID0gcGlkcy5maW5kKHAgPT4gcCAhPT0gcHJvY2Vzcy5waWQpO1xuICAgICAgaWYgKG90aGVyUGlkKSByZXR1cm4gb3RoZXJQaWQ7XG4gICAgfSBjYXRjaCB7XG4gICAgICBjb250aW51ZTsgLy8gY29tbWFuZCBub3QgZm91bmQgb3Igbm8gcmVzdWx0cyDigJQgdHJ5IG5leHRcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogS2lsbCBhIHN0YWxlIHByb2Nlc3MgaG9sZGluZyBhIHBvcnQuIFNlbmRzIFNJR1RFUk0sIHdhaXRzIGJyaWVmbHksXG4gKiB0aGVuIFNJR0tJTEwgaWYgc3RpbGwgYWxpdmUuIE9ubHkga2lsbHMgRG9sbGhvdXNlTUNQIHByb2Nlc3Nlc1xuICogKHZlcmlmaWVkIGJ5IGNoZWNraW5nIHRoZSBjb21tYW5kIGxpbmUgYW5kIHVzZXIgb3duZXJzaGlwKS5cbiAqXG4gKiBUaW1lb3V0OiAxcyBmb3IgcHMgdmVyaWZpY2F0aW9uLiBLaWxsIHdhaXQ6IDMwMG1zIMOXIDEwIHBvbGxzID0gM3NcbiAqIGJlZm9yZSBlc2NhbGF0aW5nIHRvIFNJR0tJTEwuIFRvdGFsIHdvcnN0IGNhc2U6IH40cy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGtpbGxTdGFsZVByb2Nlc3MocGlkOiBudW1iZXIsIHBvcnQ6IG51bWJlcik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBjb25zdCBvdXRjb21lID0gYXdhaXQga2lsbFN0YWxlUHJvY2Vzc0RldGFpbGVkKHBpZCwgcG9ydCk7XG4gIHJldHVybiBvdXRjb21lLmtpbGxlZDtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGtpbGxTdGFsZVByb2Nlc3NEZXRhaWxlZChcbiAgcGlkOiBudW1iZXIsXG4gIHBvcnQ6IG51bWJlcixcbiAgb3B0aW9uczogS2lsbFN0YWxlUHJvY2Vzc09wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8S2lsbFN0YWxlUHJvY2Vzc091dGNvbWU+IHtcbiAgY29uc3QgcHJvY2Vzc0luZm8gPSBhd2FpdCBpbnNwZWN0UHJvY2VzcyhwaWQpO1xuICBpZiAoIXByb2Nlc3NJbmZvKSB7XG4gICAgYXdhaXQgbG9nZ2VyLmRlYnVnKGBbV2ViVUldIENhbm5vdCB2ZXJpZnkgcHJvY2VzcyAke3BpZH0g4oCUIHNraXBwaW5nIGtpbGxgKTtcbiAgICByZXR1cm4geyBraWxsZWQ6IGZhbHNlLCByZWFzb246ICdpbnNwZWN0X2ZhaWxlZCcsIHBpZCB9O1xuICB9XG5cbiAgY29uc3QgZ3VhcmRGYWlsdXJlID0gYXdhaXQgZ2V0S2lsbEd1YXJkRmFpbHVyZShwcm9jZXNzSW5mbywgcG9ydCwgb3B0aW9ucyk7XG4gIGlmIChndWFyZEZhaWx1cmUpIHtcbiAgICByZXR1cm4gZ3VhcmRGYWlsdXJlO1xuICB9XG4gIGNvbnN0IHBhcmVudENvbW1hbmQgPSBwcm9jZXNzSW5mby5wYXJlbnRQaWQgPiBST09UX1BBUkVOVF9QSURcbiAgICA/IChhd2FpdCBnZXRQcm9jZXNzQ29tbWFuZChwcm9jZXNzSW5mby5wYXJlbnRQaWQpKSA/PyB1bmRlZmluZWRcbiAgICA6IHVuZGVmaW5lZDtcblxuICBhd2FpdCBsb2dnZXIuZGVidWcoYFtXZWJVSV0gVmVyaWZpZWQgc3RhbGUgcHJvY2VzcyAke3BpZH0gaXMgRG9sbGhvdXNlTUNQYCwgeyBjbWRMaW5lOiBwcm9jZXNzSW5mby5jb21tYW5kLCBwYXJlbnRQaWQ6IHByb2Nlc3NJbmZvLnBhcmVudFBpZCwgcGFyZW50Q29tbWFuZCB9KTtcblxuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCB0ZXJtaW5hdGVQcm9jZXNzKHByb2Nlc3NJbmZvLCBwb3J0LCBwYXJlbnRDb21tYW5kKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgaWYgKCFpc1BpZEFsaXZlKHBpZCkpIHtcbiAgICAgIHJldHVybiBidWlsZEtpbGxPdXRjb21lKHRydWUsICdhbHJlYWR5X2RlYWQnLCBwcm9jZXNzSW5mbywgcGFyZW50Q29tbWFuZCk7XG4gICAgfVxuICAgIHJldHVybiBidWlsZEtpbGxPdXRjb21lKGZhbHNlLCAnc2lnbmFsX2ZhaWxlZCcsIHByb2Nlc3NJbmZvLCBwYXJlbnRDb21tYW5kLCBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycikpO1xuICB9XG59XG5cbi8qKlxuICogRGV0ZWN0IGFuZCByZWNvdmVyIGZyb20gYSBzdGFsZSBwcm9jZXNzIHNxdWF0dGluZyBvbiB0aGUgcG9ydC5cbiAqIENvbXBhcmVzIHRoZSBwb3J0IGhvbGRlcidzIFBJRCBhZ2FpbnN0IHRoZSBsZWFkZXIgbG9jayBmaWxlIHRvIGRldGVybWluZVxuICogaWYgaXQncyBhIHNxdWF0dGVyLiBSZXR1cm5zIHRydWUgaWYgdGhlIHNxdWF0dGVyIHdhcyBraWxsZWQuXG4gKlxuICogVGltZW91dHM6IGxzb2YgMXMsIHBzIDFzLCBTSUdURVJNIHdhaXQgM3Mg4oCUIG1heCB+NXMgdG90YWwuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWNvdmVyU3RhbGVQb3J0KHBvcnQ6IG51bWJlcik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBjb25zdCBzdGFsZVBpZCA9IGF3YWl0IGZpbmRQaWRPblBvcnQocG9ydCk7XG4gIGlmICghc3RhbGVQaWQpIHJldHVybiBmYWxzZTtcblxuICAvLyBUT0NUT1UgbWl0aWdhdGlvbjogYSBuZXcgcHJvY2VzcyBtYXkgaGF2ZSBqdXN0IGJvdW5kIHRoZSBwb3J0IGJ1dCBub3QgeWV0XG4gIC8vIHdyaXR0ZW4gaXRzIGxvY2sgZmlsZS4gUmVhZCB0aGUgbG9jaywgcGF1c2UsIHJlLXJlYWQuIElmIHRoZSBzZWNvbmQgcmVhZFxuICAvLyBub3cgbWF0Y2hlcyB0aGUgcG9ydCBob2xkZXIsIGl0J3MgYSBmcmVzaCBs