UNPKG

vibetunnel

Version:

Terminal sharing server with web interface - supports macOS, Linux, and headless environments

359 lines 50.3 kB
"use strict"; /** * Activity detection system for terminal output * * Provides generic activity tracking and app-specific status parsing * for enhanced terminal title updates in dynamic mode. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ActivityDetector = void 0; exports.registerDetector = registerDetector; exports.testClaudeStatusDetection = testClaudeStatusDetection; const logger_js_1 = require("./logger.js"); const process_tree_js_1 = require("./process-tree.js"); const prompt_patterns_js_1 = require("./prompt-patterns.js"); const logger = (0, logger_js_1.createLogger)('activity-detector'); // Debug flag - set to true to enable verbose logging const CLAUDE_DEBUG = process.env.VIBETUNNEL_CLAUDE_DEBUG === 'true'; // Super debug logging wrapper function superDebug(message, ...args) { if (CLAUDE_DEBUG) { console.log(`[ActivityDetector:DEBUG] ${message}`, ...args); } } // ANSI escape code removal regex // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes need control characters const ANSI_REGEX = /\x1b\[[0-9;]*[a-zA-Z]/g; /** * Escape special regex characters in a string */ function escapeRegex(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } // Pre-compiled regex for Claude status lines // Format 1: ✻ Crafting… (205s · ↑ 6.0k tokens · <any text> to interrupt) // Format 2: ✻ Measuring… (6s · 100 tokens · esc to interrupt) // Format 3: ⏺ Calculating… (0s) - simpler format without tokens/interrupt // Format 4: ✳ Measuring… (120s · ⚒ 671 tokens · esc to interrupt) - with hammer symbol // Note: We match ANY non-whitespace character as the indicator since Claude uses many symbols const CLAUDE_STATUS_REGEX = /(\S)\s+([\w\s]+?)…\s*\((\d+)s(?:\s*·\s*(\S?)\s*([\d.]+)\s*k?\s*tokens\s*·\s*[^)]+to\s+interrupt)?\)/gi; /** * Parse Claude-specific status from output */ function parseClaudeStatus(data) { // Strip ANSI escape codes for cleaner matching const cleanData = data.replace(ANSI_REGEX, ''); // Reset regex lastIndex since we're using global flag CLAUDE_STATUS_REGEX.lastIndex = 0; // Log if we see something that looks like a Claude status if (cleanData.includes('interrupt') && cleanData.includes('tokens')) { superDebug('Potential Claude status detected'); superDebug('Clean data sample:', cleanData.substring(0, 200).replace(/\n/g, '\\n')); } const match = CLAUDE_STATUS_REGEX.exec(cleanData); if (!match) { // Debug log to see what we're trying to match if (cleanData.includes('interrupt') && cleanData.includes('tokens')) { superDebug('Claude status line NOT matched'); superDebug('Looking for pattern like: ✻ Crafting… (123s · ↑ 6.0k tokens · ... to interrupt)'); superDebug('Clean data preview:', cleanData.substring(0, 150)); // Try to find the specific line that contains the status const lines = cleanData.split('\n'); const statusLine = lines.find((line) => line.includes('interrupt') && line.includes('tokens')); if (statusLine) { superDebug('Found status line:', statusLine); superDebug('Line length:', statusLine.length); // Log each character to debug special symbols if (CLAUDE_DEBUG) { const chars = Array.from(statusLine.substring(0, 50)); chars.forEach((char, idx) => { console.log(` [${idx}] '${char}' = U+${char.charCodeAt(0).toString(16).padStart(4, '0')}`); }); } } } return null; } const [fullMatch, indicator, action, duration, direction, tokens] = match; // Handle both formats - with and without token information const hasTokenInfo = direction !== undefined && tokens !== undefined; superDebug(`Claude status MATCHED!`); superDebug(`Action: ${action}, Duration: ${duration}s, Direction: ${direction}, Tokens: ${tokens}`); superDebug(`Indicator: '${indicator}'`); logger.debug(`Claude status MATCHED! Action: ${action}, Duration: ${duration}s, Direction: ${direction}, Tokens: ${tokens}`); logger.debug(`Full match: "${fullMatch}"`); // Filter out the status line from output (need to search in original data with ANSI codes) // First try to remove the exact match from the clean data position const matchIndex = cleanData.indexOf(fullMatch); let filteredData = data; if (matchIndex >= 0) { // Find corresponding position in original data let originalPos = 0; let cleanPos = 0; while (cleanPos < matchIndex && originalPos < data.length) { if (data.startsWith('\x1b[', originalPos)) { // Skip ANSI sequence // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes need control characters const endMatch = /^\x1b\[[0-9;]*[a-zA-Z]/.exec(data.substring(originalPos)); if (endMatch) { originalPos += endMatch[0].length; } else { originalPos++; } } else { originalPos++; cleanPos++; } } // Now try to remove the status line from around this position const before = data.substring(0, Math.max(0, originalPos - 10)); const after = data.substring(originalPos + fullMatch.length + 50); const middle = data.substring(Math.max(0, originalPos - 10), originalPos + fullMatch.length + 50); // Look for the status pattern in the middle section const statusPattern = new RegExp(`[^\n]*${escapeRegex(indicator)}[^\n]*to\\s+interrupt[^\n]*`, 'gi'); const cleanedMiddle = middle.replace(statusPattern, ''); filteredData = before + cleanedMiddle + after; } // Create compact display text for title bar let displayText; if (hasTokenInfo) { // Format tokens - the input already has 'k' suffix in the regex pattern // So "6.0" means 6.0k tokens, not 6.0 tokens const formattedTokens = `${tokens}k`; // Include action and stats (without indicator to avoid title jumping) displayText = `${action} (${duration}s, ${direction}${formattedTokens})`; } else { // Simple format without token info (without indicator to avoid title jumping) displayText = `${action} (${duration}s)`; } return { filteredData, displayText, raw: { indicator, action, duration: Number.parseInt(duration), progress: hasTokenInfo ? `${direction}${tokens} tokens` : undefined, }, }; } // Registry of app-specific detectors const detectors = [ { name: 'claude', detect: (cmd) => { // First check if the command directly contains 'claude' const cmdStr = cmd.join(' ').toLowerCase(); if (cmdStr.includes('claude')) { logger.debug('Claude detected in command line'); return true; } // If not found in command, check the process tree // This catches cases where Claude is run through wrappers or scripts if ((0, process_tree_js_1.isClaudeInProcessTree)()) { logger.debug('Claude detected in process tree'); // Log the actual Claude command if available const claudeCmd = (0, process_tree_js_1.getClaudeCommandFromTree)(); if (claudeCmd) { logger.debug(`Claude command from tree: ${claudeCmd}`); } return true; } return false; }, parseStatus: parseClaudeStatus, }, // Future detectors can be added here: // npm, git, docker, etc. ]; /** * Activity detector for a terminal session * * Tracks general activity and provides app-specific status parsing */ class ActivityDetector { constructor(command, sessionId) { this.lastActivityTime = Date.now(); this.currentStatus = null; this.detector = null; this.lastStatusTime = 0; // Track when we last saw a status line this.ACTIVITY_TIMEOUT = 5000; // 5 seconds this.STATUS_TIMEOUT = 10000; // 10 seconds - clear status if not seen this.MEANINGFUL_OUTPUT_THRESHOLD = 5; // characters // Track Claude status transitions for turn notifications this.hadClaudeStatus = false; // Find matching detector for this command this.detector = detectors.find((d) => d.detect(command)) || null; this.sessionId = sessionId; if (this.detector) { logger.log(`ActivityDetector: Using ${this.detector.name} detector for command: ${command.join(' ')}`); } else { logger.debug(`ActivityDetector: No specific detector found for command: ${command.join(' ')}`); } } /** * Check if output is just a prompt */ isJustPrompt(data) { // Use unified prompt detector for consistency and performance return prompt_patterns_js_1.PromptDetector.isPromptOnly(data); } /** * Process terminal output and extract activity information */ processOutput(data) { // Don't count as activity if it's just a prompt or empty output const trimmed = data.trim(); const isMeaningfulOutput = trimmed.length > this.MEANINGFUL_OUTPUT_THRESHOLD && !this.isJustPrompt(trimmed); if (isMeaningfulOutput) { this.lastActivityTime = Date.now(); } // Log when we process output with a detector if (this.detector && data.length > 10) { superDebug(`Processing output with ${this.detector.name} detector (${data.length} chars)`); } // Try app-specific detection first if (this.detector) { try { const status = this.detector.parseStatus(data); if (status) { this.currentStatus = status; this.lastStatusTime = Date.now(); // Always update activity time for app-specific status this.lastActivityTime = Date.now(); // Update Claude status tracking if (this.detector.name === 'claude') { this.hadClaudeStatus = true; } return { filteredData: status.filteredData, activity: { isActive: true, lastActivityTime: this.lastActivityTime, specificStatus: { app: this.detector.name, status: status.displayText, }, }, }; } } catch (error) { logger.error(`Error in ${this.detector.name} status parser:`, error); // Continue with unfiltered data if parsing fails } } // Generic activity detection - use getActivityState for consistent time-based checking return { filteredData: data, activity: this.getActivityState(), }; } /** * Set callback for Claude turn notifications */ setOnClaudeTurn(callback) { this.onClaudeTurnCallback = callback; } /** * Get current activity state (for periodic updates) */ getActivityState() { const now = Date.now(); const isActive = now - this.lastActivityTime < this.ACTIVITY_TIMEOUT; // Clear status if we haven't seen it for a while if (this.currentStatus && now - this.lastStatusTime > this.STATUS_TIMEOUT) { logger.debug('Clearing stale status - not seen for', this.STATUS_TIMEOUT, 'ms'); this.currentStatus = null; // Check if this was a Claude status clearing if (this.hadClaudeStatus && this.detector?.name === 'claude') { logger.log("Claude turn detected - status cleared, it's the user's turn"); if (this.onClaudeTurnCallback && this.sessionId) { this.onClaudeTurnCallback(this.sessionId); } this.hadClaudeStatus = false; } } // If we have a specific status (like Claude running), always show it // The activity indicator in the title will show if it's active or not return { isActive, lastActivityTime: this.lastActivityTime, specificStatus: this.currentStatus && this.detector ? { app: this.detector.name, status: this.currentStatus.displayText, } : undefined, }; } /** * Clear current status (e.g., when session ends) */ clearStatus() { this.currentStatus = null; } } exports.ActivityDetector = ActivityDetector; /** * Register a new app detector * * @param detector The detector to register */ function registerDetector(detector) { const existing = detectors.findIndex((d) => d.name === detector.name); if (existing >= 0) { detectors[existing] = detector; logger.debug(`Updated ${detector.name} detector`); } else { detectors.push(detector); logger.debug(`Registered ${detector.name} detector`); } } /** * Test function to help debug Claude status detection * @param testData Sample data to test the regex against */ function testClaudeStatusDetection(testData) { console.log('\n=== Testing Claude Status Detection ==='); console.log('Raw data length:', testData.length); console.log('Raw data (first 300 chars):', testData.substring(0, 300).replace(/\n/g, '\\n')); // Test with current implementation const result = parseClaudeStatus(testData); if (result) { console.log('✅ Status detected:', result.displayText); } else { console.log('❌ No status detected'); // Try different variations const cleanData = testData.replace(ANSI_REGEX, ''); console.log('\nClean data (no ANSI):', cleanData.substring(0, 300).replace(/\n/g, '\\n')); // Test simpler patterns const patterns = [ /tokens.*interrupt/gi, /\d+s.*tokens/gi, /[↑↓]\s*\d+.*tokens/gi, /(\w+)….*\d+s/gi, ]; patterns.forEach((pattern, idx) => { if (pattern.test(cleanData)) { console.log(`✓ Pattern ${idx} matches:`, pattern.toString()); const match = pattern.exec(cleanData); if (match) { console.log(' Match:', match[0].substring(0, 100)); } } else { console.log(`✗ Pattern ${idx} no match:`, pattern.toString()); } pattern.lastIndex = 0; // Reset }); } console.log('=== End Test ===\n'); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aXZpdHktZGV0ZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmVyL3V0aWxzL2FjdGl2aXR5LWRldGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7R0FLRzs7O0FBNllILDRDQVNDO0FBTUQsOERBc0NDO0FBaGNELDJDQUEyQztBQUMzQyx1REFBb0Y7QUFDcEYsNkRBQXNEO0FBRXRELE1BQU0sTUFBTSxHQUFHLElBQUEsd0JBQVksRUFBQyxtQkFBbUIsQ0FBQyxDQUFDO0FBRWpELHFEQUFxRDtBQUNyRCxNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLHVCQUF1QixLQUFLLE1BQU0sQ0FBQztBQUVwRSw4QkFBOEI7QUFDOUIsU0FBUyxVQUFVLENBQUMsT0FBZSxFQUFFLEdBQUcsSUFBZTtJQUNyRCxJQUFJLFlBQVksRUFBRSxDQUFDO1FBQ2pCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLE9BQU8sRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7SUFDOUQsQ0FBQztBQUNILENBQUM7QUFFRCxpQ0FBaUM7QUFDakMscUdBQXFHO0FBQ3JHLE1BQU0sVUFBVSxHQUFHLHdCQUF3QixDQUFDO0FBRTVDOztHQUVHO0FBQ0gsU0FBUyxXQUFXLENBQUMsR0FBVztJQUM5QixPQUFPLEdBQUcsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQThDRCw2Q0FBNkM7QUFDN0MseUVBQXlFO0FBQ3pFLCtEQUErRDtBQUMvRCwwRUFBMEU7QUFDMUUsdUZBQXVGO0FBQ3ZGLDhGQUE4RjtBQUM5RixNQUFNLG1CQUFtQixHQUN2Qix1R0FBdUcsQ0FBQztBQUUxRzs7R0FFRztBQUNILFNBQVMsaUJBQWlCLENBQUMsSUFBWTtJQUNyQywrQ0FBK0M7SUFDL0MsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVB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SUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7Z0JBQzVCLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxHQUFHLFdBQVcsRUFBRSxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDN0QsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFDdEMsSUFBSSxLQUFLLEVBQUUsQ0FBQztvQkFDVixPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUN0RCxDQUFDO1lBQ0gsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxHQUFHLFlBQVksRUFBRSxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztZQUNoRSxDQUFDO1lBQ0QsT0FBTyxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRO1FBQ2pDLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUNELE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLENBQUMsQ0FBQztBQUNwQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBBY3Rpdml0eSBkZXRlY3Rpb24gc3lzdGVtIGZvciB0ZXJtaW5hbCBvdXRwdXRcbiAqXG4gKiBQcm92aWRlcyBnZW5lcmljIGFjdGl2aXR5IHRyYWNraW5nIGFuZCBhcHAtc3BlY2lmaWMgc3RhdHVzIHBhcnNpbmdcbiAqIGZvciBlbmhhbmNlZCB0ZXJtaW5hbCB0aXRsZSB1cGRhdGVzIGluIGR5bmFtaWMgbW9kZS5cbiAqL1xuXG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBnZXRDbGF1ZGVDb21tYW5kRnJvbVRyZWUsIGlzQ2xhdWRlSW5Qcm9jZXNzVHJlZSB9IGZyb20gJy4vcHJvY2Vzcy10cmVlLmpzJztcbmltcG9ydCB7IFByb21wdERldGVjdG9yIH0gZnJvbSAnLi9wcm9tcHQtcGF0dGVybnMuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ2FjdGl2aXR5LWRldGVjdG9yJyk7XG5cbi8vIERlYnVnIGZsYWcgLSBzZXQgdG8gdHJ1ZSB0byBlbmFibGUgdmVyYm9zZSBsb2dnaW5nXG5jb25zdCBDTEFVREVfREVCVUcgPSBwcm9jZXNzLmVudi5WSUJFVFVOTkVMX0NMQVVERV9ERUJVRyA9PT0gJ3RydWUnO1xuXG4vLyBTdXBlciBkZWJ1ZyBsb2dnaW5nIHdyYXBwZXJcbmZ1bmN0aW9uIHN1cGVyRGVidWcobWVzc2FnZTogc3RyaW5nLCAuLi5hcmdzOiB1bmtub3duW10pOiB2b2lkIHtcbiAgaWYgKENMQVVERV9ERUJVRykge1xuICAgIGNvbnNvbGUubG9nKGBbQWN0aXZpdHlEZXRlY3RvcjpERUJVR10gJHttZXNzYWdlfWAsIC4uLmFyZ3MpO1xuICB9XG59XG5cbi8vIEFOU0kgZXNjYXBlIGNvZGUgcmVtb3ZhbCByZWdleFxuLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnRyb2xDaGFyYWN0ZXJzSW5SZWdleDogQU5TSSBlc2NhcGUgY29kZXMgbmVlZCBjb250cm9sIGNoYXJhY3RlcnNcbmNvbnN0IEFOU0lfUkVHRVggPSAvXFx4MWJcXFtbMC05O10qW2EtekEtWl0vZztcblxuLyoqXG4gKiBFc2NhcGUgc3BlY2lhbCByZWdleCBjaGFyYWN0ZXJzIGluIGEgc3RyaW5nXG4gKi9cbmZ1bmN0aW9uIGVzY2FwZVJlZ2V4KHN0cjogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHN0ci5yZXBsYWNlKC9bLiorP14ke30oKXxbXFxdXFxcXF0vZywgJ1xcXFwkJicpO1xufVxuXG4vKipcbiAqIEFjdGl2aXR5IHN0YXR1cyByZXR1cm5lZCBieSBhcHAtc3BlY2lmaWMgcGFyc2Vyc1xuICovXG5leHBvcnQgaW50ZXJmYWNlIEFjdGl2aXR5U3RhdHVzIHtcbiAgLyoqIFRoZSBvdXRwdXQgZGF0YSB3aXRoIHN0YXR1cyBsaW5lcyBmaWx0ZXJlZCBvdXQgKi9cbiAgZmlsdGVyZWREYXRhOiBzdHJpbmc7XG4gIC8qKiBIdW1hbi1yZWFkYWJsZSBzdGF0dXMgdGV4dCBmb3IgZGlzcGxheSBpbiB0aXRsZSAqL1xuICBkaXNwbGF5VGV4dDogc3RyaW5nO1xuICAvKiogUmF3IHN0YXR1cyBkYXRhIGZvciBwb3RlbnRpYWwgZnV0dXJlIHVzZSAqL1xuICByYXc/OiB7XG4gICAgaW5kaWNhdG9yPzogc3RyaW5nO1xuICAgIGFjdGlvbj86IHN0cmluZztcbiAgICBkdXJhdGlvbj86IG51bWJlcjtcbiAgICBwcm9ncmVzcz86IHN0cmluZztcbiAgfTtcbn1cblxuLyoqXG4gKiBDdXJyZW50IGFjdGl2aXR5IHN0YXRlIGZvciBhIHRlcm1pbmFsIHNlc3Npb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBBY3Rpdml0eVN0YXRlIHtcbiAgLyoqIFdoZXRoZXIgdGhlIHRlcm1pbmFsIGlzIGN1cnJlbnRseSBhY3RpdmUgKi9cbiAgaXNBY3RpdmU6IGJvb2xlYW47XG4gIC8qKiBUaW1lc3RhbXAgb2YgbGFzdCBhY3Rpdml0eSAqL1xuICBsYXN0QWN0aXZpdHlUaW1lOiBudW1iZXI7XG4gIC8qKiBBcHAtc3BlY2lmaWMgc3RhdHVzIGlmIGRldGVjdGVkICovXG4gIHNwZWNpZmljU3RhdHVzPzoge1xuICAgIGFwcDogc3RyaW5nO1xuICAgIHN0YXR1czogc3RyaW5nO1xuICB9O1xufVxuXG4vKipcbiAqIEFwcC1zcGVjaWZpYyBkZXRlY3RvciBpbnRlcmZhY2VcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBBcHBEZXRlY3RvciB7XG4gIC8qKiBOYW1lIG9mIHRoZSBhcHAgdGhpcyBkZXRlY3RvciBoYW5kbGVzICovXG4gIG5hbWU6IHN0cmluZztcbiAgLyoqIENoZWNrIGlmIHRoaXMgZGV0ZWN0b3Igc2hvdWxkIGJlIHVzZWQgZm9yIHRoZSBnaXZlbiBjb21tYW5kICovXG4gIGRldGVjdDogKGNvbW1hbmQ6IHN0cmluZ1tdKSA9PiBib29sZWFuO1xuICAvKiogUGFyc2UgYXBwLXNwZWNpZmljIHN0YXR1cyBmcm9tIG91dHB1dCBkYXRhICovXG4gIHBhcnNlU3RhdHVzOiAoZGF0YTogc3RyaW5nKSA9PiBBY3Rpdml0eVN0YXR1cyB8IG51bGw7XG59XG5cbi8vIFByZS1jb21waWxlZCByZWdleCBmb3IgQ2xhdWRlIHN0YXR1cyBsaW5lc1xuLy8gRm9ybWF0IDE6IOKcuyBDcmFmdGluZ+KApiAoMjA1cyDCtyDihpEgNi4wayB0b2tlbnMgwrcgPGFueSB0ZXh0PiB0byBpbnRlcnJ1cHQpXG4vLyBGb3JtYXQgMjog4py7IE1lYXN1cmluZ+KApiAoNnMgwrcgIDEwMCB0b2tlbnMgwrcgZXNjIHRvIGludGVycnVwdClcbi8vIEZvcm1hdCAzOiDij7ogQ2FsY3VsYXRpbmfigKYgKDBzKSAtIHNpbXBsZXIgZm9ybWF0IHdpdGhvdXQgdG9rZW5zL2ludGVycnVwdFxuLy8gRm9ybWF0IDQ6IOKcsyBNZWFzdXJpbmfigKYgKDEyMHMgwrcg4pqSIDY3MSB0b2tlbnMgwrcgZXNjIHRvIGludGVycnVwdCkgLSB3aXRoIGhhbW1lciBzeW1ib2xcbi8vIE5vdGU6IFdlIG1hdGNoIEFOWSBub24td2hpdGVzcGFjZSBjaGFyYWN0ZXIgYXMgdGhlIGluZGljYXRvciBzaW5jZSBDbGF1ZGUgdXNlcyBtYW55IHN5bWJvbHNcbmNvbnN0IENMQVVERV9TVEFUVVNfUkVHRVggPVxuICAvKFxcUylcXHMrKFtcXHdcXHNdKz8p4oCmXFxzKlxcKChcXGQrKXMoPzpcXHMqwrdcXHMqKFxcUz8pXFxzKihbXFxkLl0rKVxccyprP1xccyp0b2tlbnNcXHMqwrdcXHMqW14pXSt0b1xccytpbnRlcnJ1cHQpP1xcKS9naTtcblxuLyoqXG4gKiBQYXJzZSBDbGF1ZGUtc3BlY2lmaWMgc3RhdHVzIGZyb20gb3V0cHV0XG4gKi9cbmZ1bmN0aW9uIHBhcnNlQ2xhdWRlU3RhdHVzKGRhdGE6IHN0cmluZyk6IEFjdGl2aXR5U3RhdHVzIHwgbnVsbCB7XG4gIC8vIFN0cmlwIEFOU0kgZXNjYXBlIGNvZGVzIGZvciBjbGVhbmVyIG1hdGNoaW5nXG4gIGNvbnN0IGNsZWFuRGF0YSA9IGRhdGEucmVwbGFjZShBTlNJX1JFR0VYLCAnJyk7XG5cbiAgLy8gUmVzZXQgcmVnZXggbGFzdEluZGV4IHNpbmNlIHdlJ3JlIHVzaW5nIGdsb2JhbCBmbGFnXG4gIENMQVVERV9TVEFUVVNfUkVHRVgubGFzdEluZGV4ID0gMDtcblxuICAvLyBMb2cgaWYgd2Ugc2VlIHNvbWV0aGluZyB0aGF0IGxvb2tzIGxpa2UgYSBDbGF1ZGUgc3RhdHVzXG4gIGlmIChjbGVhbkRhdGEuaW5jbHVkZXMoJ2ludGVycnVwdCcpICYmIGNsZWFuRGF0YS5pbmNsdWRlcygndG9rZW5zJykpIHtcbiAgICBzdXBlckRlYnVnKCdQb3RlbnRpYWwgQ2xhdWRlIHN0YXR1cyBkZXRlY3RlZCcpO1xuICAgIHN1cGVyRGVidWcoJ0NsZWFuIGRhdGEgc2FtcGxlOicsIGNsZWFuRGF0YS5zdWJzdHJpbmcoMCwgMjAwKS5yZXBsYWNlKC9cXG4vZywgJ1xcXFxuJykpO1xuICB9XG5cbiAgY29uc3QgbWF0Y2ggPSBDTEFVREVfU1RBVFVTX1JFR0VYLmV4ZWMoY2xlYW5EYXRhKTtcbiAgaWYgKCFtYXRjaCkge1xuICAgIC8vIERlYnVnIGxvZyB0byBzZWUgd2hhdCB3ZSdyZSB0cnlpbmcgdG8gbWF0Y2hcbiAgICBpZiAoY2xlYW5EYXRhLmluY2x1ZGVzKCdpbnRlcnJ1cHQnKSAmJiBjbGVhbkRhdGEuaW5jbHVkZXMoJ3Rva2VucycpKSB7XG4gICAgICBzdXBlckRlYnVnKCdDbGF1ZGUgc3RhdHVzIGxpbmUgTk9UIG1hdGNoZWQnKTtcbiAgICAgIHN1cGVyRGVidWcoJ0xvb2tpbmcgZm9yIHBhdHRlcm4gbGlrZTog4py7IENyYWZ0aW5n4oCmICgxMjNzIMK3IOKGkSA2LjBrIHRva2VucyDCtyAuLi4gdG8gaW50ZXJydXB0KScpO1xuICAgICAgc3VwZXJEZWJ1ZygnQ2xlYW4gZGF0YSBwcmV2aWV3OicsIGNsZWFuRGF0YS5zdWJzdHJpbmcoMCwgMTUwKSk7XG5cbiAgICAgIC8vIFRyeSB0byBmaW5kIHRoZSBzcGVjaWZpYyBsaW5lIHRoYXQgY29udGFpbnMgdGhlIHN0YXR1c1xuICAgICAgY29uc3QgbGluZXMgPSBjbGVhbkRhdGEuc3BsaXQoJ1xcbicpO1xuICAgICAgY29uc3Qgc3RhdHVzTGluZSA9IGxpbmVzLmZpbmQoXG4gICAgICAgIChsaW5lKSA9PiBsaW5lLmluY2x1ZGVzKCdpbnRlcnJ1cHQnKSAmJiBsaW5lLmluY2x1ZGVzKCd0b2tlbnMnKVxuICAgICAgKTtcbiAgICAgIGlmIChzdGF0dXNMaW5lKSB7XG4gICAgICAgIHN1cGVyRGVidWcoJ0ZvdW5kIHN0YXR1cyBsaW5lOicsIHN0YXR1c0xpbmUpO1xuICAgICAgICBzdXBlckRlYnVnKCdMaW5lIGxlbmd0aDonLCBzdGF0dXNMaW5lLmxlbmd0aCk7XG4gICAgICAgIC8vIExvZyBlYWNoIGNoYXJhY3RlciB0byBkZWJ1ZyBzcGVjaWFsIHN5bWJvbHNcbiAgICAgICAgaWYgKENMQVVERV9ERUJVRykge1xuICAgICAgICAgIGNvbnN0IGNoYXJzID0gQXJyYXkuZnJvbShzdGF0dXNMaW5lLnN1YnN0cmluZygwLCA1MCkpO1xuICAgICAgICAgIGNoYXJzLmZvckVhY2goKGNoYXIsIGlkeCkgPT4ge1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgIGAgIFske2lkeH1dICcke2NoYXJ9JyA9IFUrJHtjaGFyLmNoYXJDb2RlQXQoMCkudG9TdHJpbmcoMTYpLnBhZFN0YXJ0KDQsICcwJyl9YFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGNvbnN0IFtmdWxsTWF0Y2gsIGluZGljYXRvciwgYWN0aW9uLCBkdXJhdGlvbiwgZGlyZWN0aW9uLCB0b2tlbnNdID0gbWF0Y2g7XG5cbiAgLy8gSGFuZGxlIGJvdGggZm9ybWF0cyAtIHdpdGggYW5kIHdpdGhvdXQgdG9rZW4gaW5mb3JtYXRpb25cbiAgY29uc3QgaGFzVG9rZW5JbmZvID0gZGlyZWN0aW9uICE9PSB1bmRlZmluZWQgJiYgdG9rZW5zICE9PSB1bmRlZmluZWQ7XG5cbiAgc3VwZXJEZWJ1ZyhgQ2xhdWRlIHN0YXR1cyBNQVRDSEVEIWApO1xuICBzdXBlckRlYnVnKFxuICAgIGBBY3Rpb246ICR7YWN0aW9ufSwgRHVyYXRpb246ICR7ZHVyYXRpb259cywgRGlyZWN0aW9uOiAke2RpcmVjdGlvbn0sIFRva2VuczogJHt0b2tlbnN9YFxuICApO1xuICBzdXBlckRlYnVnKGBJbmRpY2F0b3I6ICcke2luZGljYXRvcn0nYCk7XG4gIGxvZ2dlci5kZWJ1ZyhcbiAgICBgQ2xhdWRlIHN0YXR1cyBNQVRDSEVEISBBY3Rpb246ICR7YWN0aW9ufSwgRHVyYXRpb246ICR7ZHVyYXRpb259cywgRGlyZWN0aW9uOiAke2RpcmVjdGlvbn0sIFRva2VuczogJHt0b2tlbnN9YFxuICApO1xuICBsb2dnZXIuZGVidWcoYEZ1bGwgbWF0Y2g6IFwiJHtmdWxsTWF0Y2h9XCJgKTtcblxuICAvLyBGaWx0ZXIgb3V0IHRoZSBzdGF0dXMgbGluZSBmcm9tIG91dHB1dCAobmVlZCB0byBzZWFyY2ggaW4gb3JpZ2luYWwgZGF0YSB3aXRoIEFOU0kgY29kZXMpXG4gIC8vIEZpcnN0IHRyeSB0byByZW1vdmUgdGhlIGV4YWN0IG1hdGNoIGZyb20gdGhlIGNsZWFuIGRhdGEgcG9zaXRpb25cbiAgY29uc3QgbWF0Y2hJbmRleCA9IGNsZWFuRGF0YS5pbmRleE9mKGZ1bGxNYXRjaCk7XG4gIGxldCBmaWx0ZXJlZERhdGEgPSBkYXRhO1xuICBpZiAobWF0Y2hJbmRleCA+PSAwKSB7XG4gICAgLy8gRmluZCBjb3JyZXNwb25kaW5nIHBvc2l0aW9uIGluIG9yaWdpbmFsIGRhdGFcbiAgICBsZXQgb3JpZ2luYWxQb3MgPSAwO1xuICAgIGxldCBjbGVhblBvcyA9IDA7XG4gICAgd2hpbGUgKGNsZWFuUG9zIDwgbWF0Y2hJbmRleCAmJiBvcmlnaW5hbFBvcyA8IGRhdGEubGVuZ3RoKSB7XG4gICAgICBpZiAoZGF0YS5zdGFydHNXaXRoKCdcXHgxYlsnLCBvcmlnaW5hbFBvcykpIHtcbiAgICAgICAgLy8gU2tpcCBBTlNJIHNlcXVlbmNlXG4gICAgICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db250cm9sQ2hhcmFjdGVyc0luUmVnZXg6IEFOU0kgZXNjYXBlIGNvZGVzIG5lZWQgY29udHJvbCBjaGFyYWN0ZXJzXG4gICAgICAgIGNvbnN0IGVuZE1hdGNoID0gL15cXHgxYlxcW1swLTk7XSpbYS16QS1aXS8uZXhlYyhkYXRhLnN1YnN0cmluZyhvcmlnaW5hbFBvcykpO1xuICAgICAgICBpZiAoZW5kTWF0Y2gpIHtcbiAgICAgICAgICBvcmlnaW5hbFBvcyArPSBlbmRNYXRjaFswXS5sZW5ndGg7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgb3JpZ2luYWxQb3MrKztcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb3JpZ2luYWxQb3MrKztcbiAgICAgICAgY2xlYW5Qb3MrKztcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gTm93IHRyeSB0byByZW1vdmUgdGhlIHN0YXR1cyBsaW5lIGZyb20gYXJvdW5kIHRoaXMgcG9zaXRpb25cbiAgICBjb25zdCBiZWZvcmUgPSBkYXRhLnN1YnN0cmluZygwLCBNYXRoLm1heCgwLCBvcmlnaW5hbFBvcyAtIDEwKSk7XG4gICAgY29uc3QgYWZ0ZXIgPSBkYXRhLnN1YnN0cmluZyhvcmlnaW5hbFBvcyArIGZ1bGxNYXRjaC5sZW5ndGggKyA1MCk7XG4gICAgY29uc3QgbWlkZGxlID0gZGF0YS5zdWJzdHJpbmcoXG4gICAgICBNYXRoLm1heCgwLCBvcmlnaW5hbFBvcyAtIDEwKSxcbiAgICAgIG9yaWdpbmFsUG9zICsgZnVsbE1hdGNoLmxlbmd0aCArIDUwXG4gICAgKTtcbiAgICAvLyBMb29rIGZvciB0aGUgc3RhdHVzIHBhdHRlcm4gaW4gdGhlIG1pZGRsZSBzZWN0aW9uXG4gICAgY29uc3Qgc3RhdHVzUGF0dGVybiA9IG5ldyBSZWdFeHAoXG4gICAgICBgW15cXG5dKiR7ZXNjYXBlUmVnZXgoaW5kaWNhdG9yKX1bXlxcbl0qdG9cXFxccytpbnRlcnJ1cHRbXlxcbl0qYCxcbiAgICAgICdnaSdcbiAgICApO1xuICAgIGNvbnN0IGNsZWFuZWRNaWRkbGUgPSBtaWRkbGUucmVwbGFjZShzdGF0dXNQYXR0ZXJuLCAnJyk7XG4gICAgZmlsdGVyZWREYXRhID0gYmVmb3JlICsgY2xlYW5lZE1pZGRsZSArIGFmdGVyO1xuICB9XG5cbiAgLy8gQ3JlYXRlIGNvbXBhY3QgZGlzcGxheSB0ZXh0IGZvciB0aXRsZSBiYXJcbiAgbGV0IGRpc3BsYXlUZXh0OiBzdHJpbmc7XG4gIGlmIChoYXNUb2tlbkluZm8pIHtcbiAgICAvLyBGb3JtYXQgdG9rZW5zIC0gdGhlIGlucHV0IGFscmVhZHkgaGFzICdrJyBzdWZmaXggaW4gdGhlIHJlZ2V4IHBhdHRlcm5cbiAgICAvLyBTbyBcIjYuMFwiIG1lYW5zIDYuMGsgdG9rZW5zLCBub3QgNi4wIHRva2Vuc1xuICAgIGNvbnN0IGZvcm1hdHRlZFRva2VucyA9IGAke3Rva2Vuc31rYDtcbiAgICAvLyBJbmNsdWRlIGFjdGlvbiBhbmQgc3RhdHMgKHdpdGhvdXQgaW5kaWNhdG9yIHRvIGF2b2lkIHRpdGxlIGp1bXBpbmcpXG4gICAgZGlzcGxheVRleHQgPSBgJHthY3Rpb259ICgke2R1cmF0aW9ufXMsICR7ZGlyZWN0aW9ufSR7Zm9ybWF0dGVkVG9rZW5zfSlgO1xuICB9IGVsc2Uge1xuICAgIC8vIFNpbXBsZSBmb3JtYXQgd2l0aG91dCB0b2tlbiBpbmZvICh3aXRob3V0IGluZGljYXRvciB0byBhdm9pZCB0aXRsZSBqdW1waW5nKVxuICAgIGRpc3BsYXlUZXh0ID0gYCR7YWN0aW9ufSAoJHtkdXJhdGlvbn1zKWA7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGZpbHRlcmVkRGF0YSxcbiAgICBkaXNwbGF5VGV4dCxcbiAgICByYXc6IHtcbiAgICAgIGluZGljYXRvcixcbiAgICAgIGFjdGlvbixcbiAgICAgIGR1cmF0aW9uOiBOdW1iZXIucGFyc2VJbnQoZHVyYXRpb24pLFxuICAgICAgcHJvZ3Jlc3M6IGhhc1Rva2VuSW5mbyA/IGAke2RpcmVjdGlvbn0ke3Rva2Vuc30gdG9rZW5zYCA6IHVuZGVmaW5lZCxcbiAgICB9LFxuICB9O1xufVxuXG4vLyBSZWdpc3RyeSBvZiBhcHAtc3BlY2lmaWMgZGV0ZWN0b3JzXG5jb25zdCBkZXRlY3RvcnM6IEFwcERldGVjdG9yW10gPSBbXG4gIHtcbiAgICBuYW1lOiAnY2xhdWRlJyxcbiAgICBkZXRlY3Q6IChjbWQpID0+IHtcbiAgICAgIC8vIEZpcnN0IGNoZWNrIGlmIHRoZSBjb21tYW5kIGRpcmVjdGx5IGNvbnRhaW5zICdjbGF1ZGUnXG4gICAgICBjb25zdCBjbWRTdHIgPSBjbWQuam9pbignICcpLnRvTG93ZXJDYXNlKCk7XG4gICAgICBpZiAoY21kU3RyLmluY2x1ZGVzKCdjbGF1ZGUnKSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0NsYXVkZSBkZXRlY3RlZCBpbiBjb21tYW5kIGxpbmUnKTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIG5vdCBmb3VuZCBpbiBjb21tYW5kLCBjaGVjayB0aGUgcHJvY2VzcyB0cmVlXG4gICAgICAvLyBUaGlzIGNhdGNoZXMgY2FzZXMgd2hlcmUgQ2xhdWRlIGlzIHJ1biB0aHJvdWdoIHdyYXBwZXJzIG9yIHNjcmlwdHNcbiAgICAgIGlmIChpc0NsYXVkZUluUHJvY2Vzc1RyZWUoKSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0NsYXVkZSBkZXRlY3RlZCBpbiBwcm9jZXNzIHRyZWUnKTtcbiAgICAgICAgLy8gTG9nIHRoZSBhY3R1YWwgQ2xhdWRlIGNvbW1hbmQgaWYgYXZhaWxhYmxlXG4gICAgICAgIGNvbnN0IGNsYXVkZUNtZCA9IGdldENsYXVkZUNvbW1hbmRGcm9tVHJlZSgpO1xuICAgICAgICBpZiAoY2xhdWRlQ21kKSB7XG4gICAgICAgICAgbG9nZ2VyLmRlYnVnKGBDbGF1ZGUgY29tbWFuZCBmcm9tIHRyZWU6ICR7Y2xhdWRlQ21kfWApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfSxcbiAgICBwYXJzZVN0YXR1czogcGFyc2VDbGF1ZGVTdGF0dXMsXG4gIH0sXG4gIC8vIEZ1dHVyZSBkZXRlY3RvcnMgY2FuIGJlIGFkZGVkIGhlcmU6XG4gIC8vIG5wbSwgZ2l0LCBkb2NrZXIsIGV0Yy5cbl07XG5cbi8qKlxuICogQWN0aXZpdHkgZGV0ZWN0b3IgZm9yIGEgdGVybWluYWwgc2Vzc2lvblxuICpcbiAqIFRyYWNrcyBnZW5lcmFsIGFjdGl2aXR5IGFuZCBwcm92aWRlcyBhcHAtc3BlY2lmaWMgc3RhdHVzIHBhcnNpbmdcbiAqL1xuZXhwb3J0IGNsYXNzIEFjdGl2aXR5RGV0ZWN0b3Ige1xuICBwcml2YXRlIGxhc3RBY3Rpdml0eVRpbWUgPSBEYXRlLm5vdygpO1xuICBwcml2YXRlIGN1cnJlbnRTdGF0dXM6IEFjdGl2aXR5U3RhdHVzIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgZGV0ZWN0b3I6IEFwcERldGVjdG9yIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgbGFzdFN0YXR1c1RpbWUgPSAwOyAvLyBUcmFjayB3aGVuIHdlIGxhc3Qgc2F3IGEgc3RhdHVzIGxpbmVcbiAgcHJpdmF0ZSByZWFkb25seSBBQ1RJVklUWV9USU1FT1VUID0gNTAwMDsgLy8gNSBzZWNvbmRzXG4gIHByaXZhdGUgcmVhZG9ubHkgU1RBVFVTX1RJTUVPVVQgPSAxMDAwMDsgLy8gMTAgc2Vjb25kcyAtIGNsZWFyIHN0YXR1cyBpZiBub3Qgc2VlblxuICBwcml2YXRlIHJlYWRvbmx5IE1FQU5JTkdGVUxfT1VUUFVUX1RIUkVTSE9MRCA9IDU7IC8vIGNoYXJhY3RlcnNcblxuICAvLyBUcmFjayBDbGF1ZGUgc3RhdHVzIHRyYW5zaXRpb25zIGZvciB0dXJuIG5vdGlmaWNhdGlvbnNcbiAgcHJpdmF0ZSBoYWRDbGF1ZGVTdGF0dXMgPSBmYWxzZTtcbiAgcHJpdmF0ZSBvbkNsYXVkZVR1cm5DYWxsYmFjaz86IChzZXNzaW9uSWQ6IHN0cmluZykgPT4gdm9pZDtcbiAgcHJpdmF0ZSBzZXNzaW9uSWQ/OiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IoY29tbWFuZDogc3RyaW5nW10sIHNlc3Npb25JZD86IHN0cmluZykge1xuICAgIC8vIEZpbmQgbWF0Y2hpbmcgZGV0ZWN0b3IgZm9yIHRoaXMgY29tbWFuZFxuICAgIHRoaXMuZGV0ZWN0b3IgPSBkZXRlY3RvcnMuZmluZCgoZCkgPT4gZC5kZXRlY3QoY29tbWFuZCkpIHx8IG51bGw7XG4gICAgdGhpcy5zZXNzaW9uSWQgPSBzZXNzaW9uSWQ7XG5cbiAgICBpZiAodGhpcy5kZXRlY3Rvcikge1xuICAgICAgbG9nZ2VyLmxvZyhcbiAgICAgICAgYEFjdGl2aXR5RGV0ZWN0b3I6IFVzaW5nICR7dGhpcy5kZXRlY3Rvci5uYW1lfSBkZXRlY3RvciBmb3IgY29tbWFuZDogJHtjb21tYW5kLmpvaW4oJyAnKX1gXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2dnZXIuZGVidWcoXG4gICAgICAgIGBBY3Rpdml0eURldGVjdG9yOiBObyBzcGVjaWZpYyBkZXRlY3RvciBmb3VuZCBmb3IgY29tbWFuZDogJHtjb21tYW5kLmpvaW4oJyAnKX1gXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBvdXRwdXQgaXMganVzdCBhIHByb21wdFxuICAgKi9cbiAgcHJpdmF0ZSBpc0p1c3RQcm9tcHQoZGF0YTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgLy8gVXNlIHVuaWZpZWQgcHJvbXB0IGRldGVjdG9yIGZvciBjb25zaXN0ZW5jeSBhbmQgcGVyZm9ybWFuY2VcbiAgICByZXR1cm4gUHJvbXB0RGV0ZWN0b3IuaXNQcm9tcHRPbmx5KGRhdGEpO1xuICB9XG5cbiAgLyoqXG4gICAqIFByb2Nlc3MgdGVybWluYWwgb3V0cHV0IGFuZCBleHRyYWN0IGFjdGl2aXR5IGluZm9ybWF0aW9uXG4gICAqL1xuICBwcm9jZXNzT3V0cHV0KGRhdGE6IHN0cmluZyk6IHsgZmlsdGVyZWREYXRhOiBzdHJpbmc7IGFjdGl2aXR5OiBBY3Rpdml0eVN0YXRlIH0ge1xuICAgIC8vIERvbid0IGNvdW50IGFzIGFjdGl2aXR5IGlmIGl0J3MganVzdCBhIHByb21wdCBvciBlbXB0eSBvdXRwdXRcbiAgICBjb25zdCB0cmltbWVkID0gZGF0YS50cmltKCk7XG4gICAgY29uc3QgaXNNZWFuaW5nZnVsT3V0cHV0ID1cbiAgICAgIHRyaW1tZWQubGVuZ3RoID4gdGhpcy5NRUFOSU5HRlVMX09VVFBVVF9USFJFU0hPTEQgJiYgIXRoaXMuaXNKdXN0UHJvbXB0KHRyaW1tZWQpO1xuXG4gICAgaWYgKGlzTWVhbmluZ2Z1bE91dHB1dCkge1xuICAgICAgdGhpcy5sYXN0QWN0aXZpdHlUaW1lID0gRGF0ZS5ub3coKTtcbiAgICB9XG5cbiAgICAvLyBMb2cgd2hlbiB3ZSBwcm9jZXNzIG91dHB1dCB3aXRoIGEgZGV0ZWN0b3JcbiAgICBpZiAodGhpcy5kZXRlY3RvciAmJiBkYXRhLmxlbmd0aCA+IDEwKSB7XG4gICAgICBzdXBlckRlYnVnKGBQcm9jZXNzaW5nIG91dHB1dCB3aXRoICR7dGhpcy5kZXRlY3Rvci5uYW1lfSBkZXRlY3RvciAoJHtkYXRhLmxlbmd0aH0gY2hhcnMpYCk7XG4gICAgfVxuXG4gICAgLy8gVHJ5IGFwcC1zcGVjaWZpYyBkZXRlY3Rpb24gZmlyc3RcbiAgICBpZiAodGhpcy5kZXRlY3Rvcikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3Qgc3RhdHVzID0gdGhpcy5kZXRlY3Rvci5wYXJzZVN0YXR1cyhkYXRhKTtcbiAgICAgICAgaWYgKHN0YXR1cykge1xuICAgICAgICAgIHRoaXMuY3VycmVudFN0YXR1cyA9IHN0YXR1cztcbiAgICAgICAgICB0aGlzLmxhc3RTdGF0dXNUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAvLyBBbHdheXMgdXBkYXRlIGFjdGl2aXR5IHRpbWUgZm9yIGFwcC1zcGVjaWZpYyBzdGF0dXNcbiAgICAgICAgICB0aGlzLmxhc3RBY3Rpdml0eVRpbWUgPSBEYXRlLm5vdygpO1xuXG4gICAgICAgICAgLy8gVXBkYXRlIENsYXVkZSBzdGF0dXMgdHJhY2tpbmdcbiAgICAgICAgICBpZiAodGhpcy5kZXRlY3Rvci5uYW1lID09PSAnY2xhdWRlJykge1xuICAgICAgICAgICAgdGhpcy5oYWRDbGF1ZGVTdGF0dXMgPSB0cnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBmaWx0ZXJlZERhdGE6IHN0YXR1cy5maWx0ZXJlZERhdGEsXG4gICAgICAgICAgICBhY3Rpdml0eToge1xuICAgICAgICAgICAgICBpc0FjdGl2ZTogdHJ1ZSxcbiAgICAgICAgICAgICAgbGFzdEFjdGl2aXR5VGltZTogdGhpcy5sYXN0QWN0aXZpdHlUaW1lLFxuICAgICAgICAgICAgICBzcGVjaWZpY1N0YXR1czoge1xuICAgICAgICAgICAgICAgIGFwcDogdGhpcy5kZXRlY3Rvci5uYW1lLFxuICAgICAgICAgICAgICAgIHN0YXR1czogc3RhdHVzLmRpc3BsYXlUZXh0LFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dnZXIuZXJyb3IoYEVycm9yIGluICR7dGhpcy5kZXRlY3Rvci5uYW1lfSBzdGF0dXMgcGFyc2VyOmAsIGVycm9yKTtcbiAgICAgICAgLy8gQ29udGludWUgd2l0aCB1bmZpbHRlcmVkIGRhdGEgaWYgcGFyc2luZyBmYWlsc1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEdlbmVyaWMgYWN0aXZpdHkgZGV0ZWN0aW9uIC0gdXNlIGdldEFjdGl2aXR5U3RhdGUgZm9yIGNvbnNpc3RlbnQgdGltZS1iYXNlZCBjaGVja2luZ1xuICAgIHJldHVybiB7XG4gICAgICBmaWx0ZXJlZERhdGE6IGRhdGEsXG4gICAgICBhY3Rpdml0eTogdGhpcy5nZXRBY3Rpdml0eVN0YXRlKCksXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgY2FsbGJhY2sgZm9yIENsYXVkZSB0dXJuIG5vdGlmaWNhdGlvbnNcbiAgICovXG4gIHNldE9uQ2xhdWRlVHVybihjYWxsYmFjazogKHNlc3Npb25JZDogc3RyaW5nKSA9PiB2b2lkKTogdm9pZCB7XG4gICAgdGhpcy5vbkNsYXVkZVR1cm5DYWxsYmFjayA9IGNhbGxiYWNrO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBjdXJyZW50IGFjdGl2aXR5IHN0YXRlIChmb3IgcGVyaW9kaWMgdXBkYXRlcylcbiAgICovXG4gIGdldEFjdGl2aXR5U3RhdGUoKTogQWN0aXZpdHlTdGF0ZSB7XG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCBpc0FjdGl2ZSA9IG5vdyAtIHRoaXMubGFzdEFjdGl2aXR5VGltZSA8IHRoaXMuQUNUSVZJVFlfVElNRU9VVDtcblxuICAgIC8vIENsZWFyIHN0YXR1cyBpZiB3ZSBoYXZlbid0IHNlZW4gaXQgZm9yIGEgd2hpbGVcbiAgICBpZiAodGhpcy5jdXJyZW50U3RhdHVzICYmIG5vdyAtIHRoaXMubGFzdFN0YXR1c1RpbWUgPiB0aGlzLlNUQVRVU19USU1FT1VUKSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ0NsZWFyaW5nIHN0YWxlIHN0YXR1cyAtIG5vdCBzZWVuIGZvcicsIHRoaXMuU1RBVFVTX1RJTUVPVVQsICdtcycpO1xuICAgICAgdGhpcy5jdXJyZW50U3RhdHVzID0gbnVsbDtcblxuICAgICAgLy8gQ2hlY2sgaWYgdGhpcyB3YXMgYSBDbGF1ZGUgc3RhdHVzIGNsZWFyaW5nXG4gICAgICBpZiAodGhpcy5oYWRDbGF1ZGVTdGF0dXMgJiYgdGhpcy5kZXRlY3Rvcj8ubmFtZSA9PT0gJ2NsYXVkZScpIHtcbiAgICAgICAgbG9nZ2VyLmxvZyhcIkNsYXVkZSB0dXJuIGRldGVjdGVkIC0gc3RhdHVzIGNsZWFyZWQsIGl0J3MgdGhlIHVzZXIncyB0dXJuXCIpO1xuICAgICAgICBpZiAodGhpcy5vbkNsYXVkZVR1cm5DYWxsYmFjayAmJiB0aGlzLnNlc3Npb25JZCkge1xuICAgICAgICAgIHRoaXMub25DbGF1ZGVUdXJuQ2FsbGJhY2sodGhpcy5zZXNzaW9uSWQpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuaGFkQ2xhdWRlU3RhdHVzID0gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gSWYgd2UgaGF2ZSBhIHNwZWNpZmljIHN0YXR1cyAobGlrZSBDbGF1ZGUgcnVubmluZyksIGFsd2F5cyBzaG93IGl0XG4gICAgLy8gVGhlIGFjdGl2aXR5IGluZGljYXRvciBpbiB0aGUgdGl0bGUgd2lsbCBzaG93IGlmIGl0J3MgYWN0aXZlIG9yIG5vdFxuICAgIHJldHVybiB7XG4gICAgICBpc0FjdGl2ZSxcbiAgICAgIGxhc3RBY3Rpdml0eVRpbWU6IHRoaXMubGFzdEFjdGl2aXR5VGltZSxcbiAgICAgIHNwZWNpZmljU3RhdHVzOlxuICAgICAgICB0aGlzLmN1cnJlbnRTdGF0dXMgJiYgdGhpcy5kZXRlY3RvclxuICAgICAgICAgID8ge1xuICAgICAgICAgICAgICBhcHA6IHRoaXMuZGV0ZWN0b3IubmFtZSxcbiAgICAgICAgICAgICAgc3RhdHVzOiB0aGlzLmN1cnJlbnRTdGF0dXMuZGlzcGxheVRleHQsXG4gICAgICAgICAgICB9XG4gICAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciBjdXJyZW50IHN0YXR1cyAoZS5nLiwgd2hlbiBzZXNzaW9uIGVuZHMpXG4gICAqL1xuICBjbGVhclN0YXR1cygpOiB2b2lkIHtcbiAgICB0aGlzLmN1cnJlbnRTdGF0dXMgPSBudWxsO1xuICB9XG59XG5cbi8qKlxuICogUmVnaXN0ZXIgYSBuZXcgYXBwIGRldGVjdG9yXG4gKlxuICogQHBhcmFtIGRldGVjdG9yIFRoZSBkZXRlY3RvciB0byByZWdpc3RlclxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJEZXRlY3RvcihkZXRlY3RvcjogQXBwRGV0ZWN0b3IpOiB2b2lkIHtcbiAgY29uc3QgZXhpc3RpbmcgPSBkZXRlY3RvcnMuZmluZEluZGV4KChkKSA9PiBkLm5hbWUgPT09IGRldGVjdG9yLm5hbWUpO1xuICBpZiAoZXhpc3RpbmcgPj0gMCkge1xuICAgIGRldGVjdG9yc1tleGlzdGluZ10gPSBkZXRlY3RvcjtcbiAgICBsb2dnZXIuZGVidWcoYFVwZGF0ZWQgJHtkZXRlY3Rvci5uYW1lfSBkZXRlY3RvcmApO1xuICB9IGVsc2Uge1xuICAgIGRldGVjdG9ycy5wdXNoKGRldGVjdG9yKTtcbiAgICBsb2dnZXIuZGVidWcoYFJlZ2lzdGVyZWQgJHtkZXRlY3Rvci5uYW1lfSBkZXRlY3RvcmApO1xuICB9XG59XG5cbi8qKlxuICogVGVzdCBmdW5jdGlvbiB0byBoZWxwIGRlYnVnIENsYXVkZSBzdGF0dXMgZGV0ZWN0aW9uXG4gKiBAcGFyYW0gdGVzdERhdGEgU2FtcGxlIGRhdGEgdG8gdGVzdCB0aGUgcmVnZXggYWdhaW5zdFxuICovXG5leHBvcnQgZnVuY3Rpb24gdGVzdENsYXVkZVN0YXR1c0RldGVjdGlvbih0ZXN0RGF0YTogc3RyaW5nKTogdm9pZCB7XG4gIGNvbnNvbGUubG9nKCdcXG49PT0gVGVzdGluZyBDbGF1ZGUgU3RhdHVzIERldGVjdGlvbiA9PT0nKTtcbiAgY29uc29sZS5sb2coJ1JhdyBkYXRhIGxlbmd0aDonLCB0ZXN0RGF0YS5sZW5ndGgpO1xuICBjb25zb2xlLmxvZygnUmF3IGRhdGEgKGZpcnN0IDMwMCBjaGFycyk6JywgdGVzdERhdGEuc3Vic3RyaW5nKDAsIDMwMCkucmVwbGFjZSgvXFxuL2csICdcXFxcbicpKTtcblxuICAvLyBUZXN0IHdpdGggY3VycmVudCBpbXBsZW1lbnRhdGlvblxuICBjb25zdCByZXN1bHQgPSBwYXJzZUNsYXVkZVN0YXR1cyh0ZXN0RGF0YSk7XG4gIGlmIChyZXN1bHQpIHtcbiAgICBjb25zb2xlLmxvZygn4pyFIFN0YXR1cyBkZXRlY3RlZDonLCByZXN1bHQuZGlzcGxheVRleHQpO1xuICB9IGVsc2Uge1xuICAgIGNvbnNvbGUubG9nKCfinYwgTm8gc3RhdHVzIGRldGVjdGVkJyk7XG5cbiAgICAvLyBUcnkgZGlmZmVyZW50IHZhcmlhdGlvbnNcbiAgICBjb25zdCBjbGVhbkRhdGEgPSB0ZXN0RGF0YS5yZXBsYWNlKEFOU0lfUkVHRVgsICcnKTtcbiAgICBjb25zb2xlLmxvZygnXFxuQ2xlYW4gZGF0YSAobm8gQU5TSSk6JywgY2xlYW5EYXRhLnN1YnN0cmluZygwLCAzMDApLnJlcGxhY2UoL1xcbi9nLCAnXFxcXG4nKSk7XG5cbiAgICAvLyBUZXN0IHNpbXBsZXIgcGF0dGVybnNcbiAgICBjb25zdCBwYXR0ZXJucyA9IFtcbiAgICAgIC90b2tlbnMuKmludGVycnVwdC9naSxcbiAgICAgIC9cXGQrcy4qdG9rZW5zL2dpLFxuICAgICAgL1vihpHihpNdXFxzKlxcZCsuKnRva2Vucy9naSxcbiAgICAgIC8oXFx3KynigKYuKlxcZCtzL2dpLFxuICAgIF07XG5cbiAgICBwYXR0ZXJucy5mb3JFYWNoKChwYXR0ZXJuLCBpZHgpID0+IHtcbiAgICAgIGlmIChwYXR0ZXJuLnRlc3QoY2xlYW5EYXRhKSkge1xuICAgICAgICBjb25zb2xlLmxvZyhg4pyTIFBhdHRlcm4gJHtpZHh9IG1hdGNoZXM6YCwgcGF0dGVybi50b1N0cmluZygpKTtcbiAgICAgICAgY29uc3QgbWF0Y2ggPSBwYXR0ZXJuLmV4ZWMoY2xlYW5EYXRhKTtcbiAgICAgICAgaWYgKG1hdGNoKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coJyAgTWF0Y2g6JywgbWF0Y2hbMF0uc3Vic3RyaW5nKDAsIDEwMCkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICA