vibetunnel
Version:
Terminal sharing server with web interface - supports macOS, Linux, and headless environments
415 lines • 60.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProcessTreeAnalyzer = void 0;
const child_process_1 = require("child_process");
const util_1 = require("util");
const logger_js_1 = require("../utils/logger.js");
const logger = (0, logger_js_1.createLogger)('process-tree-analyzer');
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class ProcessTreeAnalyzer {
/**
* Get the complete process tree for a given root process
*/
async getProcessTree(rootPid) {
try {
if (process.platform === 'win32') {
return await this.getWindowsProcessTree(rootPid);
}
else {
return await this.getUnixProcessTree(rootPid);
}
}
catch (_error) {
logger.warn('ProcessTreeAnalyzer', `Failed to get process tree for PID ${rootPid}:`, _error);
return [];
}
}
/**
* Get process tree on Unix-like systems (macOS, Linux)
*/
async getUnixProcessTree(rootPid) {
const isMacOS = process.platform === 'darwin';
// Always use the recursive approach since process groups aren't working reliably
logger.log('ProcessTreeAnalyzer', `Using recursive child search for ${rootPid} to find all descendants`);
try {
return await this.getProcessTreeRecursive(rootPid, isMacOS);
}
catch (fallbackError) {
logger.warn('ProcessTreeAnalyzer', `Recursive process search failed:`, fallbackError);
// Final fallback: try to get just the root process
try {
const psCommand = isMacOS
? `ps -o pid,ppid,pgid,tty,state,lstart,command -p ${rootPid}`
: `ps -o pid,ppid,pgid,sid,tty,state,lstart,command -p ${rootPid}`;
const { stdout } = await execAsync(psCommand, { timeout: 5000 });
return this.parseUnixProcessOutput(stdout, isMacOS);
}
catch (finalError) {
logger.warn('ProcessTreeAnalyzer', `Final fallback also failed:`, finalError);
return [];
}
}
}
/**
* Get process tree on Windows systems
*/
async getWindowsProcessTree(rootPid) {
try {
const { stdout } = await execAsync(`wmic process where "ParentProcessId=${rootPid}" get ProcessId,ParentProcessId,CommandLine /format:csv`, { timeout: 5000 });
return this.parseWindowsProcessOutput(stdout, rootPid);
}
catch (error) {
logger.warn('ProcessTreeAnalyzer', `Windows process query failed:`, error);
return [];
}
}
/**
* Parse Unix/Linux ps command output
*/
parseUnixProcessOutput(output, isMacOS = false) {
const lines = output.trim().split('\n');
const processes = [];
// Skip header line
for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (!line)
continue;
try {
if (isMacOS) {
// macOS ps output: PID PPID PGID TTY STATE STARTED COMMAND
// STARTED format: "Mon Jun 23 23:44:31 2025" (contains spaces)
// We need to handle the multi-word timestamp properly
const parts = line.trim().split(/\s+/);
if (parts.length >= 10) {
const pid = Number.parseInt(parts[0]);
const ppid = Number.parseInt(parts[1]);
const pgid = Number.parseInt(parts[2]);
const tty = parts[3] === '?' ? undefined : parts[3];
const state = parts[4];
// STARTED timestamp spans multiple parts: parts[5] through parts[9]
const startTime = parts.slice(5, 10).join(' ');
// COMMAND is everything from part 10 onwards
const command = parts.slice(10).join(' ');
if (!Number.isNaN(pid) && !Number.isNaN(ppid) && !Number.isNaN(pgid) && command) {
logger.log('ProcessTreeAnalyzer', `Parsed macOS process: PID=${pid}, COMMAND="${command.trim()}"`);
processes.push({
pid,
ppid,
pgid,
tty,
state,
startTime,
command: command.trim(),
});
}
}
}
else {
// Linux ps output: PID PPID PGID SID TTY STATE STARTED COMMAND
const match = line.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.+?)\s+(.+)$/);
if (match) {
const [, pid, ppid, pgid, sid, tty, state, startTime, command] = match;
processes.push({
pid: Number.parseInt(pid),
ppid: Number.parseInt(ppid),
pgid: Number.parseInt(pgid),
sid: Number.parseInt(sid),
tty: tty === '?' ? undefined : tty,
state,
startTime,
command: command.trim(),
});
}
}
}
catch (_parseError) {
logger.debug('ProcessTreeAnalyzer', `Failed to parse ps line: ${line}`);
}
}
return processes;
}
/**
* Parse Windows tasklist/wmic output
*/
parseWindowsProcessOutput(output, rootPid) {
const lines = output.trim().split('\n');
const processes = [];
// Add the root process (we only get children from wmic)
processes.push({
pid: rootPid,
ppid: 0,
pgid: rootPid,
command: 'shell', // Placeholder
});
for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (!line)
continue;
const parts = line.split(',');
if (parts.length >= 3) {
const pid = Number.parseInt(parts[1]);
const ppid = Number.parseInt(parts[2]);
const command = parts[3] || 'unknown';
if (!Number.isNaN(pid) && !Number.isNaN(ppid)) {
processes.push({
pid,
ppid,
pgid: pid, // Windows doesn't have process groups like Unix
command,
});
}
}
}
return processes;
}
/**
* Identify the most likely source of a bell event
*/
async identifyBellSource(sessionPid) {
const tree = await this.getProcessTree(sessionPid);
logger.log('ProcessTreeAnalyzer', `Process tree for session ${sessionPid}: ${JSON.stringify(tree.map((p) => ({ pid: p.pid, ppid: p.ppid, command: p.command })))}`);
if (tree.length === 0) {
logger.warn('ProcessTreeAnalyzer', `No processes found in tree for session ${sessionPid}`);
return null;
}
// Strategy 1: Look for foreground process (non-shell child)
const foreground = this.findForegroundProcess(tree, sessionPid);
if (foreground) {
logger.debug('ProcessTreeAnalyzer', `Identified foreground process: ${foreground.command} (PID: ${foreground.pid})`);
return foreground;
}
// Strategy 2: Look for most recently started child process
const recentChild = this.findMostRecentChild(tree, sessionPid);
if (recentChild) {
logger.debug('ProcessTreeAnalyzer', `Identified recent child process: ${recentChild.command} (PID: ${recentChild.pid})`);
return recentChild;
}
// Strategy 3: Look for any non-shell process in the tree
const nonShellProcess = tree.find((p) => p.pid !== sessionPid &&
!this.isShellProcess(p.command) &&
!this.isBackgroundProcess(p.command));
if (nonShellProcess) {
logger.debug('ProcessTreeAnalyzer', `Found non-shell process: ${nonShellProcess.command} (PID: ${nonShellProcess.pid})`);
return nonShellProcess;
}
// Strategy 4: Return the shell itself
const shellProcess = tree.find((p) => p.pid === sessionPid);
if (shellProcess) {
logger.debug('ProcessTreeAnalyzer', `Defaulting to shell process: ${shellProcess.command} (PID: ${shellProcess.pid})`);
return shellProcess;
}
return null;
}
/**
* Find the foreground process (likely the active process the user is interacting with)
*/
findForegroundProcess(tree, sessionPid) {
// Strategy 1: Direct children that are not shells or background processes
let candidates = tree.filter((p) => p.pid !== sessionPid &&
p.ppid === sessionPid &&
!this.isShellProcess(p.command) &&
!this.isBackgroundProcess(p.command));
logger.log('ProcessTreeAnalyzer', `Direct child candidates: ${JSON.stringify(candidates.map((p) => ({ pid: p.pid, command: p.command })))}`);
// Strategy 2: If no direct children, look for any descendant processes
if (candidates.length === 0) {
candidates = tree.filter((p) => p.pid !== sessionPid &&
!this.isShellProcess(p.command) &&
!this.isBackgroundProcess(p.command));
logger.log('ProcessTreeAnalyzer', `Descendant candidates: ${JSON.stringify(candidates.map((p) => ({ pid: p.pid, command: p.command })))}`);
}
if (candidates.length === 0) {
logger.log('ProcessTreeAnalyzer', 'No suitable candidate processes found, bell likely from shell itself');
return null;
}
// Filter out very short-lived processes (likely prompt utilities)
const now = new Date();
const recentCandidates = candidates.filter((p) => {
if (!p.startTime)
return true; // Keep if we can't determine age
const processStart = new Date(p.startTime);
const ageMs = now.getTime() - processStart.getTime();
// If process is less than 100ms old, it's likely a prompt utility
if (ageMs < 100) {
logger.log('ProcessTreeAnalyzer', `Filtering out very recent process: ${p.command} (age: ${ageMs}ms)`);
return false;
}
return true;
});
if (recentCandidates.length === 0) {
logger.log('ProcessTreeAnalyzer', 'All candidates were very recent (likely prompt utilities)');
return null;
}
// Prefer the most recently started process among the remaining candidates
const sorted = recentCandidates.sort((a, b) => {
if (a.startTime && b.startTime) {
return new Date(b.startTime).getTime() - new Date(a.startTime).getTime();
}
return 0;
});
logger.log('ProcessTreeAnalyzer', `Selected foreground candidate: ${sorted[0].command} (PID: ${sorted[0].pid})`);
return sorted[0];
}
/**
* Find the most recently started child process
*/
findMostRecentChild(tree, sessionPid) {
// Look for any non-shell children first
let children = tree.filter((p) => p.ppid === sessionPid && p.pid !== sessionPid && !this.isShellProcess(p.command));
// If no non-shell children, include all children
if (children.length === 0) {
children = tree.filter((p) => p.ppid === sessionPid && p.pid !== sessionPid);
}
logger.log('ProcessTreeAnalyzer', `Recent child candidates: ${JSON.stringify(children.map((p) => ({ pid: p.pid, command: p.command })))}`);
if (children.length === 0) {
return null;
}
// Sort by start time if available, otherwise return the last one found
const sorted = children.sort((a, b) => {
if (a.startTime && b.startTime) {
return new Date(b.startTime).getTime() - new Date(a.startTime).getTime();
}
return 0;
});
return sorted[0];
}
/**
* Check if a process is likely a shell process
*/
isShellProcess(command) {
const shellIndicators = ['bash', 'zsh', 'sh', 'fish', 'csh', 'tcsh', 'ksh'];
const processName = ProcessTreeAnalyzer.extractProcessName(command);
return shellIndicators.includes(processName.toLowerCase());
}
/**
* Check if a process is likely a background process or shell utility
*/
isBackgroundProcess(command) {
const backgroundIndicators = [
'ssh-agent',
'gpg-agent',
'dbus-daemon',
'systemd',
'kworker',
'ksoftirqd',
'migration',
'watchdog',
];
// Shell prompt utilities that shouldn't be considered bell sources
const promptUtilities = [
'git status',
'git branch',
'hg branch',
'hg status',
'svn status',
'pwd',
'whoami',
'hostname',
'date',
'ps ',
'ls -la',
'df -h',
];
const lowerCommand = command.toLowerCase();
// Check for general background processes
if (backgroundIndicators.some((indicator) => lowerCommand.includes(indicator))) {
return true;
}
// Check for shell prompt utilities
if (promptUtilities.some((utility) => lowerCommand.includes(utility))) {
logger.log('ProcessTreeAnalyzer', `Identified prompt utility: ${command}`);
return true;
}
return false;
}
/**
* Get process tree recursively by finding children
*/
async getProcessTreeRecursive(rootPid, isMacOS) {
const allProcesses = [];
const processedPids = new Set();
// Get all processes on the system
const psCommand = isMacOS
? 'ps -eo pid,ppid,pgid,tty,state,lstart,command'
: 'ps -eo pid,ppid,pgid,sid,tty,state,lstart,command';
logger.log('ProcessTreeAnalyzer', `Getting all system processes with: ${psCommand}`);
const { stdout } = await execAsync(psCommand, { timeout: 10000 });
const allSystemProcesses = this.parseUnixProcessOutput(stdout, isMacOS);
logger.log('ProcessTreeAnalyzer', `Found ${allSystemProcesses.length} total system processes`);
// Build a map of parent -> children
const childrenMap = new Map();
for (const proc of allSystemProcesses) {
if (!childrenMap.has(proc.ppid)) {
childrenMap.set(proc.ppid, []);
}
const children = childrenMap.get(proc.ppid);
if (children) {
children.push(proc);
}
}
// Check what children exist for our root PID
const directChildren = childrenMap.get(rootPid) || [];
logger.log('ProcessTreeAnalyzer', `Direct children of ${rootPid}: ${JSON.stringify(directChildren.map((p) => ({ pid: p.pid, command: p.command })))}`);
// Recursively collect the process tree starting from rootPid
const collectProcessTree = (pid) => {
if (processedPids.has(pid))
return;
processedPids.add(pid);
// Find the process itself
const process = allSystemProcesses.find((p) => p.pid === pid);
if (process) {
allProcesses.push(process);
}
// Find and collect children
const children = childrenMap.get(pid) || [];
for (const child of children) {
collectProcessTree(child.pid);
}
};
collectProcessTree(rootPid);
logger.log('ProcessTreeAnalyzer', `Final process tree: ${JSON.stringify(allProcesses.map((p) => ({ pid: p.pid, ppid: p.ppid, command: p.command })))}`);
return allProcesses;
}
/**
* Create a complete process snapshot for bell event analysis
*/
async captureProcessSnapshot(sessionPid) {
const processTree = await this.getProcessTree(sessionPid);
const foregroundProcess = this.findForegroundProcess(processTree, sessionPid);
const suspectedBellSource = await this.identifyBellSource(sessionPid);
return {
sessionPid,
processTree,
foregroundProcess,
suspectedBellSource,
capturedAt: new Date().toISOString(),
};
}
/**
* Extract a human-readable process name from a command string
*/
static extractProcessName(command) {
// Remove common shell prefixes and arguments
const cleaned = command
.replace(/^.*\//, '') // Remove path
.replace(/\s+.*$/, '') // Remove arguments
.replace(/^sudo\s+/, '') // Remove sudo
.replace(/^exec\s+/, ''); // Remove exec
return cleaned || 'unknown';
}
/**
* Get a short description of the process for notifications
*/
static getProcessDescription(processInfo) {
if (!processInfo) {
return 'unknown process';
}
const name = ProcessTreeAnalyzer.extractProcessName(processInfo.command);
// Return a user-friendly description
if (name === 'bash' || name === 'zsh' || name === 'sh' || name === 'fish') {
return 'shell';
}
return name;
}
}
exports.ProcessTreeAnalyzer = ProcessTreeAnalyzer;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy10cmVlLWFuYWx5emVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3NlcnZlci9zZXJ2aWNlcy9wcm9jZXNzLXRyZWUtYW5hbHl6ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaURBQXFDO0FBQ3JDLCtCQUFpQztBQUNqQyxrREFBa0Q7QUFFbEQsTUFBTSxNQUFNLEdBQUcsSUFBQSx3QkFBWSxFQUFDLHVCQUF1QixDQUFDLENBQUM7QUFFckQsTUFBTSxTQUFTLEdBQUcsSUFBQSxnQkFBUyxFQUFDLG9CQUFJLENBQUMsQ0FBQztBQXFCbEMsTUFBYSxtQkFBbUI7SUFDOUI7O09BRUc7SUFDSCxLQUFLLENBQUMsY0FBYyxDQUFDLE9BQWU7UUFDbEMsSUFBSSxDQUFDO1lBQ0gsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU8sRUFBRSxDQUFDO2dCQUNqQyxPQUFPLE1BQU0sSUFBSSxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ25ELENBQUM7aUJBQU0sQ0FBQztnQkFDTixPQUFPLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ2hELENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxNQUFNLEVBQUUsQ0FBQztZQUNoQixNQUFNLENBQUMsSUFBSSxDQUFDLHFCQUFxQixFQUFFLHNDQUFzQyxPQUFPLEdBQUcsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM3RixPQUFPLEVBQUUsQ0FBQztRQUNaLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMsa0JBQWtCLENBQUMsT0FBZTtRQUM5QyxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsUUFBUSxLQUFLLFFBQVEsQ0FBQztRQUU5QyxpRkFBaUY7UUFDakYsTUFBTSxDQUFDLEdBQUcsQ0FDUixxQkFBcUIsRUFDckIsb0NBQW9DLE9BQU8sMEJBQTBCLENBQ3RFLENBQUM7UUFFRixJQUFJ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VTtZQUNWLFdBQVc7WUFDWCxpQkFBaUI7WUFDakIsbUJBQW1CO1lBQ25CLFVBQVUsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTtTQUNyQyxDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLGtCQUFrQixDQUFDLE9BQWU7UUFDdkMsNkNBQTZDO1FBQzdDLE1BQU0sT0FBTyxHQUFHLE9BQU87YUFDcEIsT0FBTyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxjQUFjO2FBQ25DLE9BQU8sQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUMsbUJBQW1CO2FBQ3pDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDLENBQUMsY0FBYzthQUN0QyxPQUFPLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsY0FBYztRQUUxQyxPQUFPLE9BQU8sSUFBSSxTQUFTLENBQUM7SUFDOUIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLHFCQUFxQixDQUFDLFdBQStCO1FBQzFELElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNqQixPQUFPLGlCQUFpQixDQUFDO1FBQzNCLENBQUM7UUFFRCxNQUFNLElBQUksR0FBRyxtQkFBbUIsQ0FBQyxrQkFBa0IsQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFekUscUNBQXFDO1FBQ3JDLElBQUksSUFBSSxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksS0FBSyxJQUFJLElBQUksSUFBSSxLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQzFFLE9BQU8sT0FBTyxDQUFDO1FBQ2pCLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7Q0FDRjtBQWhoQkQsa0RBZ2hCQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4ZWMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCB7IHByb21pc2lmeSB9IGZyb20gJ3V0aWwnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdwcm9jZXNzLXRyZWUtYW5hbHl6ZXInKTtcblxuY29uc3QgZXhlY0FzeW5jID0gcHJvbWlzaWZ5KGV4ZWMpO1xuXG5leHBvcnQgaW50ZXJmYWNlIFByb2Nlc3NJbmZvIHtcbiAgcGlkOiBudW1iZXI7XG4gIHBwaWQ6IG51bWJlcjtcbiAgcGdpZDogbnVtYmVyO1xuICBzaWQ/OiBudW1iZXI7XG4gIHR0eT86IHN0cmluZztcbiAgY29tbWFuZDogc3RyaW5nO1xuICBzdGF0ZT86IHN0cmluZztcbiAgc3RhcnRUaW1lPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFByb2Nlc3NTbmFwc2hvdCB7XG4gIHNlc3Npb25QaWQ6IG51bWJlcjtcbiAgcHJvY2Vzc1RyZWU6IFByb2Nlc3NJbmZvW107XG4gIGZvcmVncm91bmRQcm9jZXNzOiBQcm9jZXNzSW5mbyB8IG51bGw7XG4gIHN1c3BlY3RlZEJlbGxTb3VyY2U6IFByb2Nlc3NJbmZvIHwgbnVsbDtcbiAgY2FwdHVyZWRBdDogc3RyaW5nO1xufVxuXG5leHBvcnQgY2xhc3MgUHJvY2Vzc1RyZWVBbmFseXplciB7XG4gIC8qKlxuICAgKiBHZXQgdGhlIGNvbXBsZXRlIHByb2Nlc3MgdHJlZSBmb3IgYSBnaXZlbiByb290IHByb2Nlc3NcbiAgICovXG4gIGFzeW5jIGdldFByb2Nlc3NUcmVlKHJvb3RQaWQ6IG51bWJlcik6IFByb21pc2U8UHJvY2Vzc0luZm9bXT4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAocHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJykge1xuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRXaW5kb3dzUHJvY2Vzc1RyZWUocm9vdFBpZCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRVbml4UHJvY2Vzc1RyZWUocm9vdFBpZCk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgICBsb2dnZXIud2FybignUHJvY2Vzc1RyZWVBbmFseXplcicsIGBGYWlsZWQgdG8gZ2V0IHByb2Nlc3MgdHJlZSBmb3IgUElEICR7cm9vdFBpZH06YCwgX2Vycm9yKTtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IHByb2Nlc3MgdHJlZSBvbiBVbml4LWxpa2Ugc3lzdGVtcyAobWFjT1MsIExpbnV4KVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBnZXRVbml4UHJvY2Vzc1RyZWUocm9vdFBpZDogbnVtYmVyKTogUHJvbWlzZTxQcm9jZXNzSW5mb1tdPiB7XG4gICAgY29uc3QgaXNNYWNPUyA9IHByb2Nlc3MucGxhdGZvcm0gPT09ICdkYXJ3aW4nO1xuXG4gICAgLy8gQWx3YXlzIHVzZSB0aGUgcmVjdXJzaXZlIGFwcHJvYWNoIHNpbmNlIHByb2Nlc3MgZ3JvdXBzIGFyZW4ndCB3b3JraW5nIHJlbGlhYmx5XG4gICAgbG9nZ2VyLmxvZyhcbiAgICAgICdQcm9jZXNzVHJlZUFuYWx5emVyJyxcbiAgICAgIGBVc2luZyByZWN1cnNpdmUgY2hpbGQgc2VhcmNoIGZvciAke3Jvb3RQaWR9IHRvIGZpbmQgYWxsIGRlc2NlbmRhbnRzYFxuICAgICk7XG5cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0UHJvY2Vzc1RyZWVSZWN1cnNpdmUocm9vdFBpZCwgaXNNYWNPUyk7XG4gICAgfSBjYXRjaCAoZmFsbGJhY2tFcnJvcikge1xuICAgICAgbG9nZ2VyLndhcm4oJ1Byb2Nlc3NUcmVlQW5hbHl6ZXInLCBgUmVjdXJzaXZlIHByb2Nlc3Mgc2VhcmNoIGZhaWxlZDpgLCBmYWxsYmFja0Vycm9yKTtcblxuICAgICAgLy8gRmluYWwgZmFsbGJhY2s6IHRyeSB0byBnZXQganVzdCB0aGUgcm9vdCBwcm9jZXNzXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBwc0NvbW1hbmQgPSBpc01hY09TXG4gICAgICAgICAgPyBgcHMgLW8gcGlkLHBwaWQscGdpZCx0dHksc3RhdGUsbHN0YXJ0LGNvbW1hbmQgLXAgJHtyb290UGlkfWBcbiAgICAgICAgICA6IGBwcyAtbyBwaWQscHBpZCxwZ2lkLHNpZCx0dHksc3RhdGUsbHN0YXJ0LGNvbW1hbmQgLXAgJHtyb290UGlkfWA7XG5cbiAgICAgICAgY29uc3QgeyBzdGRvdXQgfSA9IGF3YWl0IGV4ZWNBc3luYyhwc0NvbW1hbmQsIHsgdGltZW91dDogNTAwMCB9KTtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFyc2VVbml4UHJvY2Vzc091dHB1dChzdGRvdXQsIGlzTWFjT1MpO1xuICAgICAgfSBjYXRjaCAoZmluYWxFcnJvcikge1xuICAgICAgICBsb2dnZXIud2FybignUHJvY2Vzc1RyZWVBbmFseXplcicsIGBGaW5hbCBmYWxsYmFjayBhbHNvIGZhaWxlZDpgLCBmaW5hbEVycm9yKTtcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgcHJvY2VzcyB0cmVlIG9uIFdpbmRvd3Mgc3lzdGVtc1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBnZXRXaW5kb3dzUHJvY2Vzc1RyZWUocm9vdFBpZDogbnVtYmVyKTogUHJvbWlzZTxQcm9jZXNzSW5mb1tdPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHsgc3Rkb3V0IH0gPSBhd2FpdCBleGVjQXN5bmMoXG4gICAgICAgIGB3bWljIHByb2Nlc3Mgd2hlcmUgXCJQYXJlbnRQcm9jZXNzSWQ9JHtyb290UGlkfVwiIGdldCBQcm9jZXNzSWQsUGFyZW50UHJvY2Vzc0lkLENvbW1hbmRMaW5lIC9mb3JtYXQ6Y3N2YCxcbiAgICAgICAgeyB0aW1lb3V0OiA1MDAwIH1cbiAgICAgICk7XG5cbiAgICAgIHJldHVybiB0aGlzLnBhcnNlV2luZG93c1Byb2Nlc3NPdXRwdXQoc3Rkb3V0LCByb290UGlkKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLndhcm4oJ1Byb2Nlc3NUcmVlQW5hbHl6ZXInLCBgV2luZG93cyBwcm9jZXNzIHF1ZXJ5IGZhaWxlZDpgLCBlcnJvcik7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFBhcnNlIFVuaXgvTGludXggcHMgY29tbWFuZCBvdXRwdXRcbiAgICovXG4gIHByaXZhdGUgcGFyc2VVbml4UHJvY2Vzc091dHB1dChvdXRwdXQ6IHN0cmluZywgaXNNYWNPUzogYm9vbGVhbiA9IGZhbHNlKTogUHJvY2Vzc0luZm9bXSB7XG4gICAgY29uc3QgbGluZXMgPSBvdXRwdXQudHJpbSgpLnNwbGl0KCdcXG4nKTtcbiAgICBjb25zdCBwcm9jZXNzZXM6IFByb2Nlc3NJbmZvW10gPSBbXTtcblxuICAgIC8vIFNraXAgaGVhZGVyIGxpbmVcbiAgICBmb3IgKGxldCBpID0gMTsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBsaW5lID0gbGluZXNbaV0udHJpbSgpO1xuICAgICAgaWYgKCFsaW5lKSBjb250aW51ZTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgaWYgKGlzTWFjT1MpIHtcbiAgICAgICAgICAvLyBtYWNPUyBwcyBvdXRwdXQ6IFBJRCBQUElEIFBHSUQgVFRZIFNUQVRFIFNUQVJURUQgQ09NTUFORFxuICAgICAgICAgIC8vIFNUQVJURUQgZm9ybWF0OiBcIk1vbiBKdW4gMjMgMjM6NDQ6MzEgMjAyNVwiIChjb250YWlucyBzcGFjZXMpXG4gICAgICAgICAgLy8gV2UgbmVlZCB0byBoYW5kbGUgdGhlIG11bHRpLXdvcmQgdGltZXN0YW1wIHByb3Blcmx5XG4gICAgICAgICAgY29uc3QgcGFydHMgPSBsaW5lLnRyaW0oKS5zcGxpdCgvXFxzKy8pO1xuICAgICAgICAgIGlmIChwYXJ0cy5sZW5ndGggPj0gMTApIHtcbiAgICAgICAgICAgIGNvbnN0IHBpZCA9IE51bWJlci5wYXJzZUludChwYXJ0c1swXSk7XG4gICAgICAgICAgICBjb25zdCBwcGlkID0gTnVtYmVyLnBhcnNlSW50KHBhcnRzWzFdKTtcbiAgICAgICAgICAgIGNvbnN0IHBnaWQgPSBOdW1iZXIucGFyc2VJbnQocGFydHNbMl0pO1xuICAgICAgICAgICAgY29uc3QgdHR5ID0gcGFydHNbM10gPT09ICc/JyA/IHVuZGVmaW5lZCA6IHBhcnRzWzNdO1xuICAgICAgICAgICAgY29uc3Qgc3RhdGUgPSBwYXJ0c1s0XTtcbiAgICAgICAgICAgIC8vIFNUQVJURUQgdGltZXN0YW1wIHNwYW5zIG11bHRpcGxlIHBhcnRzOiBwYXJ0c1s1XSB0aHJvdWdoIHBhcnRzWzldXG4gICAgICAgICAgICBjb25zdCBzdGFydFRpbWUgPSBwYXJ0cy5zbGljZSg1LCAxMCkuam9pbignICcpO1xuICAgICAgICAgICAgLy8gQ09NTUFORCBpcyBldmVyeXRoaW5nIGZyb20gcGFydCAxMCBvbndhcmRzXG4gICAgICAgICAgICBjb25zdCBjb21tYW5kID0gcGFydHMuc2xpY2UoMTApLmpvaW4oJyAnKTtcblxuICAgICAgICAgICAgaWYgKCFOdW1iZXIuaXNOYU4ocGlkKSAmJiAhTnVtYmVyLmlzTmFOKHBwaWQpICYmICFOdW1iZXIuaXNOYU4ocGdpZCkgJiYgY29tbWFuZCkge1xuICAgICAgICAgICAgICBsb2dnZXIubG9nKFxuICAgICAgICAgICAgICAgICdQcm9jZXNzVHJlZUFuYWx5emVyJyxcbiAgICAgICAgICAgICAgICBgUGFyc2VkIG1hY09TIHByb2Nlc3M6IFBJRD0ke3BpZH0sIENPTU1BTkQ9XCIke2NvbW1hbmQudHJpbSgpfVwiYFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBwcm9jZXNzZXMucHVzaCh7XG4gICAgICAgICAgICAgICAgcGlkLFxuICAgICAgICAgICAgICAgIHBwaWQsXG4gICAgICAgICAgICAgICAgcGdpZCxcbiAgICAgICAgICAgICAgICB0dHksXG4gICAgICAgICAgICAgICAgc3RhdGUsXG4gICAgICAgICAgICAgICAgc3RhcnRUaW1lLFxuICAgICAgICAgICAgICAgIGNvbW1hbmQ6IGNvbW1hbmQudHJpbSgpLFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gTGludXggcHMgb3V0cHV0OiBQSUQgUFBJRCBQR0lEIFNJRCBUVFkgU1RBVEUgU1RBUlRFRCBDT01NQU5EXG4gICAgICAgICAgY29uc3QgbWF0Y2ggPSBsaW5lLm1hdGNoKFxuICAgICAgICAgICAgL15cXHMqKFxcZCspXFxzKyhcXGQrKVxccysoXFxkKylcXHMrKFxcZCspXFxzKyhcXFMrKVxccysoXFxTKylcXHMrKC4rPylcXHMrKC4rKSQvXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAobWF0Y2gpIHtcbiAgICAgICAgICAgIGNvbnN0IFssIHBpZCwgcHBpZCwgcGdpZCwgc2lkLCB0dHksIHN0YXRlLCBzdGFydFRpbWUsIGNvbW1hbmRdID0gbWF0Y2g7XG4gICAgICAgICAgICBwcm9jZXNzZXMucHVzaCh7XG4gICAgICAgICAgICAgIHBpZDogTnVtYmVyLnBhcnNlSW50KHBpZCksXG4gICAgICAgICAgICAgIHBwaWQ6IE51bWJlci5wYXJzZUludChwcGlkKSxcbiAgICAgICAgICAgICAgcGdpZDogTnVtYmVyLnBhcnNlSW50KHBnaWQpLFxuICAgICAgICAgICAgICBzaWQ6IE51bWJlci5wYXJzZUludChzaWQpLFxuICAgICAgICAgICAgICB0dHk6IHR0eSA9PT0gJz8nID8gdW5kZWZpbmVkIDogdHR5LFxuICAgICAgICAgICAgICBzdGF0ZSxcbiAgICAgICAgICAgICAgc3RhcnRUaW1lLFxuICAgICAgICAgICAgICBjb21tYW5kOiBjb21tYW5kLnRyaW0oKSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoX3BhcnNlRXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdQcm9jZXNzVHJlZUFuYWx5emVyJywgYEZhaWxlZCB0byBwYXJzZSBwcyBsaW5lOiAke2xpbmV9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHByb2Nlc3NlcztcbiAgfVxuXG4gIC8qKlxuICAgKiBQYXJzZSBXaW5kb3dzIHRhc2tsaXN0L3dtaWMgb3V0cHV0XG4gICAqL1xuICBwcml2YXRlIHBhcnNlV2luZG93c1Byb2Nlc3NPdXRwdXQob3V0cHV0OiBzdHJpbmcsIHJvb3RQaWQ6IG51bWJlcik6IFByb2Nlc3NJbmZvW10ge1xuICAgIGNvbnN0IGxpbmVzID0gb3V0cHV0LnRyaW0oKS5zcGxpdCgnXFxuJyk7XG4gICAgY29uc3QgcHJvY2Vzc2VzOiBQcm9jZXNzSW5mb1tdID0gW107XG5cbiAgICAvLyBBZGQgdGhlIHJvb3QgcHJvY2VzcyAod2Ugb25seSBnZXQgY2hpbGRyZW4gZnJvbSB3bWljKVxuICAgIHByb2Nlc3Nlcy5wdXNoKHtcbiAgICAgIHBpZDogcm9vdFBpZCxcbiAgICAgIHBwaWQ6IDAsXG4gICAgICBwZ2lkOiByb290UGlkLFxuICAgICAgY29tbWFuZDogJ3NoZWxsJywgLy8gUGxhY2Vob2xkZXJcbiAgICB9KTtcblxuICAgIGZvciAobGV0IGkgPSAxOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IGxpbmUgPSBsaW5lc1tpXS50cmltKCk7XG4gICAgICBpZiAoIWxpbmUpIGNvbnRpbnVlO1xuXG4gICAgICBjb25zdCBwYXJ0cyA9IGxpbmUuc3BsaXQoJywnKTtcbiAgICAgIGlmIChwYXJ0cy5sZW5ndGggPj0gMykge1xuICAgICAgICBjb25zdCBwaWQgPSBOdW1iZXIucGFyc2VJbnQocGFydHNbMV0pO1xuICAgICAgICBjb25zdCBwcGlkID0gTnVtYmVyLnBhcnNlSW50KHBhcnRzWzJdKTtcbiAgICAgICAgY29uc3QgY29tbWFuZCA9IHBhcnRzWzNdIHx8ICd1bmtub3duJztcblxuICAgICAgICBpZiAoIU51bWJlci5pc05hTihwaWQpICYmICFOdW1iZXIuaXNOYU4ocHBpZCkpIHtcbiAgICAgICAgICBwcm9jZXNzZXMucHVzaCh7XG4gICAgICAgICAgICBwaWQsXG4gICAgICAgICAgICBwcGlkLFxuICAgICAgICAgICAgcGdpZDogcGlkLCAvLyBXaW5kb3dzIGRvZXNuJ3QgaGF2ZSBwcm9jZXNzIGdyb3VwcyBsaWtlIFVuaXhcbiAgICAgICAgICAgIGNvbW1hbmQsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcHJvY2Vzc2VzO1xuICB9XG5cbiAgLyoqXG4gICAqIElkZW50aWZ5IHRoZSBtb3N0IGxpa2VseSBzb3VyY2Ugb2YgYSBiZWxsIGV2ZW50XG4gICAqL1xuICBhc3luYyBpZGVudGlmeUJlbGxTb3VyY2Uoc2Vzc2lvblBpZDogbnVtYmVyKTogUHJvbWlzZTxQcm9jZXNzSW5mbyB8IG51bGw+IHtcbiAgICBjb25zdCB0cmVlID0gYXdhaXQgdGhpcy5nZXRQcm9jZXNzVHJlZShzZXNzaW9uUGlkKTtcbiAgICBsb2dnZXIubG9nKFxuICAgICAgJ1Byb2Nlc3NUcmVlQW5hbHl6ZXInLFxuICAgICAgYFByb2Nlc3MgdHJlZSBmb3Igc2Vzc2lvbiAke3Nlc3Npb25QaWR9OiAke0pTT04uc3RyaW5naWZ5KHRyZWUubWFwKChwKSA9PiAoeyBwaWQ6IHAucGlkLCBwcGlkOiBwLnBwaWQsIGNvbW1hbmQ6IHAuY29tbWFuZCB9KSkpfWBcbiAgICApO1xuXG4gICAgaWYgKHRyZWUubGVuZ3RoID09PSAwKSB7XG4gICAgICBsb2dnZXIud2FybignUHJvY2Vzc1RyZWVBbmFseXplcicsIGBObyBwcm9jZXNzZXMgZm91bmQgaW4gdHJlZSBmb3Igc2Vzc2lvbiAke3Nlc3Npb25QaWR9YCk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBTdHJhdGVneSAxOiBMb29rIGZvciBmb3JlZ3JvdW5kIHByb2Nlc3MgKG5vbi1zaGVsbCBjaGlsZClcbiAgICBjb25zdCBmb3JlZ3JvdW5kID0gdGhpcy5maW5kRm9yZWdyb3VuZFByb2Nlc3ModHJlZSwgc2Vzc2lvblBpZCk7XG4gICAgaWYgKGZvcmVncm91bmQpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcbiAgICAgICAgJ1Byb2Nlc3NUcmVlQW5hbHl6ZXInLFxuICAgICAgICBgSWRlbnRpZmllZCBmb3JlZ3JvdW5kIHByb2Nlc3M6ICR7Zm9yZWdyb3VuZC5jb21tYW5kfSAoUElEOiAke2ZvcmVncm91bmQucGlkfSlgXG4gICAgICApO1xuICAgICAgcmV0dXJuIGZvcmVncm91bmQ7XG4gICAgfVxuXG4gICAgLy8gU3RyYXRlZ3kgMjogTG9vayBmb3IgbW9zdCByZWNlbnRseSBzdGFydGVkIGNoaWxkIHByb2Nlc3NcbiAgICBjb25zdCByZWNlbnRDaGlsZCA9IHRoaXMuZmluZE1vc3RSZWNlbnRDaGlsZCh0cmVlLCBzZXNzaW9uUGlkKTtcbiAgICBpZiAocmVjZW50Q2hpbGQpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcbiAgICAgICAgJ1Byb2Nlc3NUcmVlQW5hbHl6ZXInLFxuICAgICAgICBgSWRlbnRpZmllZCByZWNlbnQgY2hpbGQgcHJvY2VzczogJHtyZWNlbnRDaGlsZC5jb21tYW5kfSAoUElEOiAke3JlY2VudENoaWxkLnBpZH0pYFxuICAgICAgKTtcbiAgICAgIHJldHVybiByZWNlbnRDaGlsZDtcbiAgICB9XG5cbiAgICAvLyBTdHJhdGVneSAzOiBMb29rIGZvciBhbnkgbm9uLXNoZWxsIHByb2Nlc3MgaW4gdGhlIHRyZWVcbiAgICBjb25zdCBub25TaGVsbFByb2Nlc3MgPSB0cmVlLmZpbmQoXG4gICAgICAocCkgPT5cbiAgICAgICAgcC5waWQgIT09IHNlc3Npb25QaWQgJiZcbiAgICAgICAgIXRoaXMuaXNTaGVsbFByb2Nlc3MocC5jb21tYW5kKSAmJlxuICAgICAgICAhdGhpcy5pc0JhY2tncm91bmRQcm9jZXNzKHAuY29tbWFuZClcbiAgICApO1xuICAgIGlmIChub25TaGVsbFByb2Nlc3MpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcbiAgICAgICAgJ1Byb2Nlc3NUcmVlQW5hbHl6ZXInLFxuICAgICAgICBgRm91bmQgbm9uLXNoZWxsIHByb2Nlc3M6ICR7bm9uU2hlbGxQcm9jZXNzLmNvbW1hbmR9IChQSUQ6ICR7bm9uU2hlbGxQcm9jZXNzLnBpZH0pYFxuICAgICAgKTtcbiAgICAgIHJldHVybiBub25TaGVsbFByb2Nlc3M7XG4gICAgfVxuXG4gICAgLy8gU3RyYXRlZ3kgNDogUmV0dXJuIHRoZSBzaGVsbCBpdHNlbGZcbiAgICBjb25zdCBzaGVsbFByb2Nlc3MgPSB0cmVlLmZpbmQoKHApID0+IHAucGlkID09PSBzZXNzaW9uUGlkKTtcbiAgICBpZiAoc2hlbGxQcm9jZXNzKSB7XG4gICAgICBsb2dnZXIuZGVidWcoXG4gICAgICAgICdQcm9jZXNzVHJlZUFuYWx5emVyJyxcbiAgICAgICAgYERlZmF1bHRpbmcgdG8gc2hlbGwgcHJvY2VzczogJHtzaGVsbFByb2Nlc3MuY29tbWFuZH0gKFBJRDogJHtzaGVsbFByb2Nlc3MucGlkfSlgXG4gICAgICApO1xuICAgICAgcmV0dXJuIHNoZWxsUHJvY2VzcztcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kIHRoZSBmb3JlZ3JvdW5kIHByb2Nlc3MgKGxpa2VseSB0aGUgYWN0aXZlIHByb2Nlc3MgdGhlIHVzZXIgaXMgaW50ZXJhY3Rpbmcgd2l0aClcbiAgICovXG4gIHByaXZhdGUgZmluZEZvcmVncm91bmRQcm9jZXNzKHRyZWU6IFByb2Nlc3NJbmZvW10sIHNlc3Npb25QaWQ6IG51bWJlcik6IFByb2Nlc3NJbmZvIHwgbnVsbCB7XG4gICAgLy8gU3RyYXRlZ3kgMTogRGlyZWN0IGNoaWxkcmVuIHRoYXQgYXJlIG5vdCBzaGVsbHMgb3IgYmFja2dyb3VuZCBwcm9jZXNzZXNcbiAgICBsZXQgY2FuZGlkYXRlcyA9IHRyZWUuZmlsdGVyKFxuICAgICAgKHApID0+XG4gICAgICAgIHAucGlkICE9PSBzZXNzaW9uUGlkICYmXG4gICAgICAgIHAucHBpZCA9PT0gc2Vzc2lvblBpZCAmJlxuICAgICAgICAhdGhpcy5pc1NoZWxsUHJvY2VzcyhwLmNvbW1hbmQpICYmXG4gICAgICAgICF0aGlzLmlzQmFja2dyb3VuZFByb2Nlc3MocC5jb21tYW5kKVxuICAgICk7XG5cbiAgICBsb2dnZXIubG9nKFxuICAgICAgJ1Byb2Nlc3NUcmVlQW5hbHl6ZXInLFxuICAgICAgYERpcmVjdCBjaGlsZCBjYW5kaWRhdGVzOiAke0pTT04uc3RyaW5naWZ5KGNhbmRpZGF0ZXMubWFwKChwKSA9PiAoeyBwaWQ6IHAucGlkLCBjb21tYW5kOiBwLmNvbW1hbmQgfSkpKX1gXG4gICAgKTtcblxuICAgIC8vIFN0cmF0ZWd5IDI6IElmIG5vIGRpcmVjdCBjaGlsZHJlbiwgbG9vayBmb3IgYW55IGRlc2NlbmRhbnQgcHJvY2Vzc2VzXG4gICAgaWYgKGNhbmRpZGF0ZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBjYW5kaWRhdGVzID0gdHJlZS5maWx0ZXIoXG4gICAgICAgIChwKSA9PlxuICAgICAgICAgIHAucGlkICE9PSBzZXNzaW9uUGlkICYmXG4gICAgICAgICAgIXRoaXMuaXNTaGVsbFByb2Nlc3MocC5jb21tYW5kKSAmJlxuICAgICAgICAgICF0aGlzLmlzQmFja2dyb3VuZFByb2Nlc3