UNPKG

@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.

275 lines 38.6 kB
/** * Handoff Service — Session handoff for agent execution. * * Enables agent executions to be paused, serialized, and resumed * across session boundaries. Builds on GatheredData (#68) for * execution state aggregation. * * Part of the Agentic Loop Completion (Epic #380, Issues #69, #71). * * @since v2.0.0 */ import { createHash } from 'crypto'; import { gzipSync, gunzipSync } from 'zlib'; import { performance } from 'perf_hooks'; import { logger } from '../../utils/logger.js'; import { getHandoffMaxRecentEntries, getHandoffMaxRecentDecisions, getHandoffWarnPayloadBytes, } from '../../config/handoff-limits.js'; // ============================================================================= // Handoff State Types (#69) // ============================================================================= /** Schema version for forward compatibility */ const HANDOFF_VERSION = '1.0.0'; // ============================================================================= // Handoff State Preparation (#69) // ============================================================================= /** * Prepare a handoff state from gathered data and execution context. * * @param agentName - Agent name * @param gatheredData - Aggregated execution data from getGatheredData() * @param activeElements - Currently active elements (from execute result or state) * @param successCriteria - Goal success criteria * @returns Complete HandoffState with checksum */ export function prepareHandoffState(agentName, gatheredData, activeElements, successCriteria = []) { // Build gathered data summary (capped for size via configurable limits) const maxRecentEntries = getHandoffMaxRecentEntries(); const maxRecentDecisions = getHandoffMaxRecentDecisions(); const recentEntries = gatheredData.entries.slice(-maxRecentEntries); // Extract decision history from gathered data entries const decisionEntries = gatheredData.entries .filter(e => e.type === 'decision') .slice(-maxRecentDecisions); const decisionHistory = decisionEntries.map(e => ({ decision: e.content.summary, reasoning: e.content.details?.fullReasoning || '', outcome: e.content.details?.outcome, confidence: e.content.details?.confidence || 0, timestamp: e.timestamp, })); // Generate continuation instructions const continuationInstructions = generateContinuationInstructions(agentName, gatheredData); // Build the state without checksum first const stateWithoutChecksum = { version: HANDOFF_VERSION, agentName, goalId: gatheredData.goalId, preparedAt: new Date().toISOString(), goalProgress: { description: gatheredData.goal.description, status: gatheredData.goal.status, stepsCompleted: gatheredData.summary.totalSteps, successCriteria, }, gatheredDataSummary: { totalEntries: gatheredData.entries.length, recentEntries, summary: gatheredData.summary, }, decisionHistory, activeElements, continuationInstructions, }; // Compute checksum over the data payload const checksum = computeChecksum(stateWithoutChecksum); return { ...stateWithoutChecksum, checksum }; } /** * Validate a handoff state's checksum integrity. * * @param state - HandoffState to validate * @returns true if checksum is valid */ export function validateHandoffChecksum(state) { const { checksum, ...rest } = state; const computed = computeChecksum(rest); return computed === checksum; } // ============================================================================= // Handoff Block Generation (#71) // ============================================================================= /** Visual border for handoff blocks */ const HANDOFF_BORDER = '═'.repeat(60); const HANDOFF_MARKER_START = '╔' + HANDOFF_BORDER + '╗'; const HANDOFF_MARKER_END = '╚' + HANDOFF_BORDER + '╝'; const HANDOFF_PAYLOAD_START = '--- HANDOFF PAYLOAD START ---'; const HANDOFF_PAYLOAD_END = '--- HANDOFF PAYLOAD END ---'; /** * Generate a copy-pasteable handoff block with human-readable summary * and machine-readable compressed payload. * * The block format: * ``` * ╔════════...════╗ * ║ AGENT HANDOFF — {agentName} * ║ Goal: {description} * ║ Status: {status} | Steps: {n} | Confidence: {avg} * ║ * ║ Next Steps: * ║ {continuationInstructions} * ╚════════...════╝ * --- HANDOFF PAYLOAD START --- * {base64-compressed-json} * --- HANDOFF PAYLOAD END --- * ``` * * @param state - HandoffState to encode * @returns Formatted handoff block string */ export function generateHandoffBlock(state) { logger.info('Generating handoff block', { agentName: state.agentName, goalId: state.goalId, recentEntries: state.gatheredDataSummary.recentEntries.length, totalEntries: state.gatheredDataSummary.totalEntries, decisionCount: state.decisionHistory.length, }); const lines = []; // Human-readable header lines.push(HANDOFF_MARKER_START); lines.push(`║ AGENT HANDOFF — ${state.agentName}`); lines.push(`║ Goal: ${state.goalProgress.description.substring(0, 80)}`); lines.push(`║ Status: ${state.goalProgress.status} | ` + `Steps: ${state.goalProgress.stepsCompleted} | ` + `Confidence: ${state.gatheredDataSummary.summary.averageConfidence}`); lines.push('║'); lines.push('║ Next Steps:'); for (const line of state.continuationInstructions.split('\n').slice(0, 5)) { lines.push(`║ ${line}`); } lines.push(HANDOFF_MARKER_END); // Machine-readable payload lines.push(HANDOFF_PAYLOAD_START); lines.push(compressHandoffState(state)); lines.push(HANDOFF_PAYLOAD_END); return lines.join('\n'); } /** * Parse a handoff block and extract the HandoffState. * * @param text - Raw text containing a handoff block * @returns Parsed and validated HandoffState * @throws Error if block is malformed, payload is corrupted, or checksum fails */ export function parseHandoffBlock(text) { // Extract payload between markers const startIdx = text.indexOf(HANDOFF_PAYLOAD_START); const endIdx = text.indexOf(HANDOFF_PAYLOAD_END); if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) { throw new Error('Invalid handoff block: missing payload markers'); } const payloadStart = startIdx + HANDOFF_PAYLOAD_START.length; const payload = text.substring(payloadStart, endIdx).trim(); if (!payload) { throw new Error('Invalid handoff block: empty payload'); } // Decompress and parse const state = decompressHandoffState(payload); // Validate integrity if (!validateHandoffChecksum(state)) { throw new Error('Invalid handoff block: integrity check failed'); } // Version check if (!state.version) { throw new Error('Invalid handoff state: missing version'); } return state; } // ============================================================================= // Compression Utilities // ============================================================================= /** * Compress a HandoffState to a base64-encoded gzip string. * JSON → gzip → base64 for compact copy/paste. */ export function compressHandoffState(state) { const startTime = performance.now(); const json = JSON.stringify(state); const uncompressedBytes = Buffer.byteLength(json, 'utf-8'); const compressed = gzipSync(Buffer.from(json, 'utf-8')); const compressedBytes = compressed.length; const compressionTimeMs = Math.round((performance.now() - startTime) * 100) / 100; const compressionRatio = uncompressedBytes > 0 ? Math.round((1 - compressedBytes / uncompressedBytes) * 10000) / 100 : 0; logger.info('Handoff state compressed', { agentName: state.agentName, goalId: state.goalId, uncompressedBytes, compressedBytes, compressionRatio: `${compressionRatio}%`, compressionTimeMs, }); const warnThreshold = getHandoffWarnPayloadBytes(); if (compressedBytes > warnThreshold) { logger.warn('Handoff payload exceeds size threshold', { agentName: state.agentName, goalId: state.goalId, compressedBytes, warnThresholdBytes: warnThreshold, }); } return compressed.toString('base64'); } /** * Decompress a base64-encoded gzip string back to HandoffState. * base64 → gunzip → JSON.parse */ export function decompressHandoffState(encoded) { try { const compressed = Buffer.from(encoded, 'base64'); const decompressed = gunzipSync(compressed); const json = decompressed.toString('utf-8'); const state = JSON.parse(json); return state; } catch (error) { const detail = error instanceof Error ? error.message : String(error); logger.error('Handoff payload decompression failed', { detail }); throw new Error('Invalid handoff block: payload could not be restored'); } } // ============================================================================= // Internal Helpers // ============================================================================= /** * Compute SHA-256 checksum of a data payload. */ function computeChecksum(data) { const json = JSON.stringify(data, Object.keys(data).sort((a, b) => a.localeCompare(b))); return createHash('sha256').update(json).digest('hex'); } /** * Generate human-readable continuation instructions from gathered data. */ function generateContinuationInstructions(agentName, data) { const lines = []; lines.push(`Resume execution of agent '${agentName}'.`); if (data.goal.status === 'in_progress') { lines.push(`Goal is in progress: ${data.goal.description}`); lines.push(`${data.summary.totalSteps} steps completed so far.`); if (data.summary.failedSteps > 0) { lines.push(`Note: ${data.summary.failedSteps} step(s) failed — review before continuing.`); } } else if (data.goal.status === 'completed') { lines.push(`Goal was completed. Review results and close out.`); } else if (data.goal.status === 'failed') { lines.push(`Goal failed. Investigate and decide whether to retry.`); } // Recent decisions for context const recentDecisions = data.entries .filter(e => e.type === 'decision') .slice(-3); if (recentDecisions.length > 0) { lines.push(''); lines.push('Recent steps:'); for (const d of recentDecisions) { const outcome = d.content.details?.outcome || 'unknown'; lines.push(` - [${outcome}] ${d.content.summary.substring(0, 80)}`); } } return lines.join('\n'); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZG9mZi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9lbGVtZW50cy9hZ2VudHMvaGFuZG9mZi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7OztHQVVHO0FBRUgsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUNwQyxPQUFPLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUM1QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRXpDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBQ0wsMEJBQTBCLEVBQzFCLDRCQUE0QixFQUM1QiwwQkFBMEIsR0FDM0IsTUFBTSxnQ0FBZ0MsQ0FBQztBQUV4QyxnRkFBZ0Y7QUFDaEYsNEJBQTRCO0FBQzVCLGdGQUFnRjtBQUVoRiwrQ0FBK0M7QUFDL0MsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDO0FBd0RoQyxnRkFBZ0Y7QUFDaEYsa0NBQWtDO0FBQ2xDLGdGQUFnRjtBQUVoRjs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FDakMsU0FBaUIsRUFDakIsWUFBMEIsRUFDMUIsY0FBd0MsRUFDeEMsa0JBQTRCLEVBQUU7SUFFOUIsd0VBQXdFO0lBQ3hFLE1BQU0sZ0JBQWdCLEdBQUcsMEJBQTBCLEVBQUUsQ0FBQztJQUN0RCxNQUFNLGtCQUFrQixHQUFHLDRCQUE0QixFQUFFLENBQUM7SUFFMUQsTUFBTSxhQUFhLEdBQUcsWUFBWSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBRXBFLHNEQUFzRDtJQUN0RCxNQUFNLGVBQWUsR0FBRyxZQUFZLENBQUMsT0FBTztTQUN6QyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLFVBQVUsQ0FBQztTQUNsQyxLQUFL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dURBQXVELENBQUMsQ0FBQztJQUN0RSxDQUFDO0lBRUQsK0JBQStCO0lBQy9CLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxPQUFPO1NBQ2pDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssVUFBVSxDQUFDO1NBQ2xDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRWIsSUFBSSxlQUFlLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQy9CLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDZixLQUFLLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQzVCLEtBQUssTUFBTSxDQUFDLElBQUksZUFBZSxFQUFFLENBQUM7WUFDaEMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsT0FBTyxJQUFJLFNBQVMsQ0FBQztZQUN4RCxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsT0FBTyxLQUFLLENBQUMsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZFLENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQzFCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEhhbmRvZmYgU2VydmljZSDigJQgU2Vzc2lvbiBoYW5kb2ZmIGZvciBhZ2VudCBleGVjdXRpb24uXG4gKlxuICogRW5hYmxlcyBhZ2VudCBleGVjdXRpb25zIHRvIGJlIHBhdXNlZCwgc2VyaWFsaXplZCwgYW5kIHJlc3VtZWRcbiAqIGFjcm9zcyBzZXNzaW9uIGJvdW5kYXJpZXMuIEJ1aWxkcyBvbiBHYXRoZXJlZERhdGEgKCM2OCkgZm9yXG4gKiBleGVjdXRpb24gc3RhdGUgYWdncmVnYXRpb24uXG4gKlxuICogUGFydCBvZiB0aGUgQWdlbnRpYyBMb29wIENvbXBsZXRpb24gKEVwaWMgIzM4MCwgSXNzdWVzICM2OSwgIzcxKS5cbiAqXG4gKiBAc2luY2UgdjIuMC4wXG4gKi9cblxuaW1wb3J0IHsgY3JlYXRlSGFzaCB9IGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgeyBnemlwU3luYywgZ3VuemlwU3luYyB9IGZyb20gJ3psaWInO1xuaW1wb3J0IHsgcGVyZm9ybWFuY2UgfSBmcm9tICdwZXJmX2hvb2tzJztcbmltcG9ydCB0eXBlIHsgR2F0aGVyZWREYXRhLCBHYXRoZXJlZERhdGFFbnRyeSB9IGZyb20gJy4vZ2F0aGVyZWREYXRhLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQge1xuICBnZXRIYW5kb2ZmTWF4UmVjZW50RW50cmllcyxcbiAgZ2V0SGFuZG9mZk1heFJlY2VudERlY2lzaW9ucyxcbiAgZ2V0SGFuZG9mZldhcm5QYXlsb2FkQnl0ZXMsXG59IGZyb20gJy4uLy4uL2NvbmZpZy9oYW5kb2ZmLWxpbWl0cy5qcyc7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBIYW5kb2ZmIFN0YXRlIFR5cGVzICgjNjkpXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKiogU2NoZW1hIHZlcnNpb24gZm9yIGZvcndhcmQgY29tcGF0aWJpbGl0eSAqL1xuY29uc3QgSEFORE9GRl9WRVJTSU9OID0gJzEuMC4wJztcblxuLyoqXG4gKiBDb21wbGV0ZSBoYW5kb2ZmIHN0YXRlIHNuYXBzaG90IGZvciBzZXNzaW9uIHRyYW5zZmVyLlxuICpcbiAqIENvbnRhaW5zIGV2ZXJ5dGhpbmcgbmVlZGVkIHRvIHJlc3VtZSBhbiBhZ2VudCBleGVjdXRpb246XG4gKiAtIEdvYWwgcHJvZ3Jlc3MgYW5kIHN0YXR1c1xuICogLSBHYXRoZXJlZCBkYXRhIChkZWNpc2lvbnMsIGZpbmRpbmdzLCBtZXRyaWNzKVxuICogLSBBY3RpdmUgZWxlbWVudCBjb250ZXh0XG4gKiAtIENvbnRpbnVhdGlvbiBpbnN0cnVjdGlvbnNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBIYW5kb2ZmU3RhdGUge1xuICAvKiogU2NoZW1hIHZlcnNpb24gZm9yIGZvcndhcmQgY29tcGF0aWJpbGl0eSAqL1xuICB2ZXJzaW9uOiBzdHJpbmc7XG4gIC8qKiBBZ2VudCBuYW1lICovXG4gIGFnZW50TmFtZTogc3RyaW5nO1xuICAvKiogR29hbCBJRCBiZWluZyBoYW5kZWQgb2ZmICovXG4gIGdvYWxJZDogc3RyaW5nO1xuICAvKiogSVNPIDg2MDEgdGltZXN0YW1wIHdoZW4gaGFuZG9mZiB3YXMgcHJlcGFyZWQgKi9cbiAgcHJlcGFyZWRBdDogc3RyaW5nO1xuICAvKiogU0hBLTI1NiBjaGVja3N1bSBvZiB0aGUgZGF0YSBwYXlsb2FkIChleGNsdWRlcyBjaGVja3N1bSBmaWVsZCBpdHNlbGYpICovXG4gIGNoZWNrc3VtOiBzdHJpbmc7XG5cbiAgLyoqIEdvYWwgcHJvZ3Jlc3Mgc25hcHNob3QgKi9cbiAgZ29hbFByb2dyZXNzOiB7XG4gICAgZGVzY3JpcHRpb246IHN0cmluZztcbiAgICBzdGF0dXM6IHN0cmluZztcbiAgICBzdGVwc0NvbXBsZXRlZDogbnVtYmVyO1xuICAgIHN1Y2Nlc3NDcml0ZXJpYTogc3RyaW5nW107XG4gIH07XG5cbiAgLyoqIEFnZ3JlZ2F0ZWQgZXhlY3V0aW9uIGRhdGEgZnJvbSBHYXRoZXJlZERhdGEgKi9cbiAgZ2F0aGVyZWREYXRhU3VtbWFyeToge1xuICAgIHRvdGFsRW50cmllczogbnVtYmVyO1xuICAgIC8qKiBMYXN0IE4gZW50cmllcyAobW9zdCByZWNlbnQsIGNhcHBlZCBmb3Igc2l6ZSkgKi9cbiAgICByZWNlbnRFbnRyaWVzOiBHYXRoZXJlZERhdGFFbnRyeVtdO1xuICAgIC8qKiBTdW1tYXJ5IHN0YXRpc3RpY3MgKi9cbiAgICBzdW1tYXJ5OiBHYXRoZXJlZERhdGFbJ3N1bW1hcnknXTtcbiAgfTtcblxuICAvKiogUmVjZW50IGRlY2lzaW9uIGhpc3RvcnkgKGxhc3QgMTApICovXG4gIGRlY2lzaW9uSGlzdG9yeTogQXJyYXk8e1xuICAgIGRlY2lzaW9uOiBzdHJpbmc7XG4gICAgcmVhc29uaW5nOiBzdHJpbmc7XG4gICAgb3V0Y29tZT86IHN0cmluZztcbiAgICBjb25maWRlbmNlOiBudW1iZXI7XG4gICAgdGltZXN0YW1wOiBzdHJpbmc7XG4gIH0+O1xuXG4gIC8qKiBBY3RpdmUgZWxlbWVudHMgYXQgdGltZSBvZiBoYW5kb2ZmICovXG4gIGFjdGl2ZUVsZW1lbnRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT47XG5cbiAgLyoqIEh1bWFuLXJlYWRhYmxlIGNvbnRpbnVhdGlvbiBpbnN0cnVjdGlvbnMgKi9cbiAgY29udGludWF0aW9uSW5zdHJ1Y3Rpb25zOiBzdHJpbmc7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBIYW5kb2ZmIFN0YXRlIFByZXBhcmF0aW9uICgjNjkpXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIFByZXBhcmUgYSBoYW5kb2ZmIHN0YXRlIGZyb20gZ2F0aGVyZWQgZGF0YSBhbmQgZXhlY3V0aW9uIGNvbnRleHQuXG4gKlxuICogQHBhcmFtIGFnZW50TmFtZSAtIEFnZW50IG5hbWVcbiAqIEBwYXJhbSBnYXRoZXJlZERhdGEgLSBBZ2dyZWdhdGVkIGV4ZWN1dGlvbiBkYXRhIGZyb20gZ2V0R2F0aGVyZWREYXRhKClcbiAqIEBwYXJhbSBhY3RpdmVFbGVtZW50cyAtIEN1cnJlbnRseSBhY3RpdmUgZWxlbWVudHMgKGZyb20gZXhlY3V0ZSByZXN1bHQgb3Igc3RhdGUpXG4gKiBAcGFyYW0gc3VjY2Vzc0NyaXRlcmlhIC0gR29hbCBzdWNjZXNzIGNyaXRlcmlhXG4gKiBAcmV0dXJucyBDb21wbGV0ZSBIYW5kb2ZmU3RhdGUgd2l0aCBjaGVja3N1bVxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJlcGFyZUhhbmRvZmZTdGF0ZShcbiAgYWdlbnROYW1lOiBzdHJpbmcsXG4gIGdhdGhlcmVkRGF0YTogR2F0aGVyZWREYXRhLFxuICBhY3RpdmVFbGVtZW50czogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+LFxuICBzdWNjZXNzQ3JpdGVyaWE6IHN0cmluZ1tdID0gW11cbik6IEhhbmRvZmZTdGF0ZSB7XG4gIC8vIEJ1aWxkIGdhdGhlcmVkIGRhdGEgc3VtbWFyeSAoY2FwcGVkIGZvciBzaXplIHZpYSBjb25maWd1cmFibGUgbGltaXRzKVxuICBjb25zdCBtYXhSZWNlbnRFbnRyaWVzID0gZ2V0SGFuZG9mZk1heFJlY2VudEVudHJpZXMoKTtcbiAgY29uc3QgbWF4UmVjZW50RGVjaXNpb25zID0gZ2V0SGFuZG9mZk1heFJlY2VudERlY2lzaW9ucygpO1xuXG4gIGNvbnN0IHJlY2VudEVudHJpZXMgPSBnYXRoZXJlZERhdGEuZW50cmllcy5zbGljZSgtbWF4UmVjZW50RW50cmllcyk7XG5cbiAgLy8gRXh0cmFjdCBkZWNpc2lvbiBoaXN0b3J5IGZyb20gZ2F0aGVyZWQgZGF0YSBlbnRyaWVzXG4gIGNvbnN0IGRlY2lzaW9uRW50cmllcyA9IGdhdGhlcmVkRGF0YS5lbnRyaWVzXG4gICAgLmZpbHRlcihlID0+IGUudHlwZSA9PT0gJ2RlY2lzaW9uJylcbiAgICAuc2xpY2UoLW1heFJlY2VudERlY2lzaW9ucyk7XG5cbiAgY29uc3QgZGVjaXNpb25IaXN0b3J5ID0gZGVjaXNpb25FbnRyaWVzLm1hcChlID0+ICh7XG4gICAgZGVjaXNpb246IGUuY29udGVudC5zdW1tYXJ5LFxuICAgIHJlYXNvbmluZzogKGUuY29udGVudC5kZXRhaWxzPy5mdWxsUmVhc29uaW5nIGFzIHN0cmluZykgfHwgJycsXG4gICAgb3V0Y29tZTogZS5jb250ZW50LmRldGFpbHM/Lm91dGNvbWUgYXMgc3RyaW5nIHwgdW5kZWZpbmVkLFxuICAgIGNvbmZpZGVuY2U6IChlLmNvbnRlbnQuZGV0YWlscz8uY29uZmlkZW5jZSBhcyBudW1iZXIpIHx8IDAsXG4gICAgdGltZXN0YW1wOiBlLnRpbWVzdGFtcCxcbiAgfSkpO1xuXG4gIC8vIEdlbmVyYXRlIGNvbnRpbnVhdGlvbiBpbnN0cnVjdGlvbnNcbiAgY29uc3QgY29udGludWF0aW9uSW5zdHJ1Y3Rpb25zID0gZ2VuZXJhdGVDb250aW51YXRpb25JbnN0cnVjdGlvbnMoXG4gICAgYWdlbnROYW1lLFxuICAgIGdhdGhlcmVkRGF0YVxuICApO1xuXG4gIC8vIEJ1aWxkIHRoZSBzdGF0ZSB3aXRob3V0IGNoZWNrc3VtIGZpcnN0XG4gIGNvbnN0IHN0YXRlV2l0aG91dENoZWNrc3VtOiBPbWl0PEhhbmRvZmZTdGF0ZSwgJ2NoZWNrc3VtJz4gPSB7XG4gICAgdmVyc2lvbjogSEFORE9GRl9WRVJTSU9OLFxuICAgIGFnZW50TmFtZSxcbiAgICBnb2FsSWQ6IGdhdGhlcmVkRGF0YS5nb2FsSWQsXG4gICAgcHJlcGFyZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgIGdvYWxQcm9ncmVzczoge1xuICAgICAgZGVzY3JpcHRpb246IGdhdGhlcmVkRGF0YS5nb2FsLmRlc2NyaXB0aW9uLFxuICAgICAgc3RhdHVzOiBnYXRoZXJlZERhdGEuZ29hbC5zdGF0dXMsXG4gICAgICBzdGVwc0NvbXBsZXRlZDogZ2F0aGVyZWREYXRhLnN1bW1hcnkudG90YWxTdGVwcyxcbiAgICAgIHN1Y2Nlc3NDcml0ZXJpYSxcbiAgICB9LFxuICAgIGdhdGhlcmVkRGF0YVN1bW1hcnk6IHtcbiAgICAgIHRvdGFsRW50cmllczogZ2F0aGVyZWREYXRhLmVudHJpZXMubGVuZ3RoLFxuICAgICAgcmVjZW50RW50cmllcyxcbiAgICAgIHN1bW1hcnk6IGdhdGhlcmVkRGF0YS5zdW1tYXJ5LFxuICAgIH0sXG4gICAgZGVjaXNpb25IaXN0b3J5LFxuICAgIGFjdGl2ZUVsZW1lbnRzLFxuICAgIGNvbnRpbnVhdGlvbkluc3RydWN0aW9ucyxcbiAgfTtcblxuICAvLyBDb21wdXRlIGNoZWNrc3VtIG92ZXIgdGhlIGRhdGEgcGF5bG9hZFxuICBjb25zdCBjaGVja3N1bSA9IGNvbXB1dGVDaGVja3N1bShzdGF0ZVdpdGhvdXRDaGVja3N1bSk7XG5cbiAgcmV0dXJuIHsgLi4uc3RhdGVXaXRob3V0Q2hlY2tzdW0sIGNoZWNrc3VtIH07XG59XG5cbi8qKlxuICogVmFsaWRhdGUgYSBoYW5kb2ZmIHN0YXRlJ3MgY2hlY2tzdW0gaW50ZWdyaXR5LlxuICpcbiAqIEBwYXJhbSBzdGF0ZSAtIEhhbmRvZmZTdGF0ZSB0byB2YWxpZGF0ZVxuICogQHJldHVybnMgdHJ1ZSBpZiBjaGVja3N1bSBpcyB2YWxpZFxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVIYW5kb2ZmQ2hlY2tzdW0oc3RhdGU6IEhhbmRvZmZTdGF0ZSk6IGJvb2xlYW4ge1xuICBjb25zdCB7IGNoZWNrc3VtLCAuLi5yZXN0IH0gPSBzdGF0ZTtcbiAgY29uc3QgY29tcHV0ZWQgPSBjb21wdXRlQ2hlY2tzdW0ocmVzdCk7XG4gIHJldHVybiBjb21wdXRlZCA9PT0gY2hlY2tzdW07XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBIYW5kb2ZmIEJsb2NrIEdlbmVyYXRpb24gKCM3MSlcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKiBWaXN1YWwgYm9yZGVyIGZvciBoYW5kb2ZmIGJsb2NrcyAqL1xuY29uc3QgSEFORE9GRl9CT1JERVIgPSAn4pWQJy5yZXBlYXQoNjApO1xuY29uc3QgSEFORE9GRl9NQVJLRVJfU1RBUlQgPSAn4pWUJyArIEhBTkRPRkZfQk9SREVSICsgJ+KVlyc7XG5jb25zdCBIQU5ET0ZGX01BUktFUl9FTkQgPSAn4pWaJyArIEhBTkRPRkZfQk9SREVSICsgJ+KVnSc7XG5jb25zdCBIQU5ET0ZGX1BBWUxPQURfU1RBUlQgPSAnLS0tIEhBTkRPRkYgUEFZTE9BRCBTVEFSVCAtLS0nO1xuY29uc3QgSEFORE9GRl9QQVlMT0FEX0VORCA9ICctLS0gSEFORE9GRiBQQVlMT0FEIEVORCAtLS0nO1xuXG4vKipcbiAqIEdlbmVyYXRlIGEgY29weS1wYXN0ZWFibGUgaGFuZG9mZiBibG9jayB3aXRoIGh1bWFuLXJlYWRhYmxlIHN1bW1hcnlcbiAqIGFuZCBtYWNoaW5lLXJlYWRhYmxlIGNvbXByZXNzZWQgcGF5bG9hZC5cbiAqXG4gKiBUaGUgYmxvY2sgZm9ybWF0OlxuICogYGBgXG4gKiDilZTilZDilZDilZDilZDilZDilZDilZDilZAuLi7ilZDilZDilZDilZDilZdcbiAqIOKVkSBBR0VOVCBIQU5ET0ZGIOKAlCB7YWdlbnROYW1lfVxuICog4pWRIEdvYWw6IHtkZXNjcmlwdGlvbn1cbiAqIOKVkSBTdGF0dXM6IHtzdGF0dXN9IHwgU3RlcHM6IHtufSB8IENvbmZpZGVuY2U6IHthdmd9XG4gKiDilZFcbiAqIOKVkSBOZXh0IFN0ZXBzOlxuICog4pWRIHtjb250aW51YXRpb25JbnN0cnVjdGlvbnN9XG4gKiDilZrilZDilZDilZDilZDilZDilZDilZDilZAuLi7ilZDilZDilZDilZDilZ1cbiAqIC0tLSBIQU5ET0ZGIFBBWUxPQUQgU1RBUlQgLS0tXG4gKiB7YmFzZTY0LWNvbXByZXNzZWQtanNvbn1cbiAqIC0tLSBIQU5ET0ZGIFBBWUxPQUQgRU5EIC0tLVxuICogYGBgXG4gKlxuICogQHBhcmFtIHN0YXRlIC0gSGFuZG9mZlN0YXRlIHRvIGVuY29kZVxuICogQHJldHVybnMgRm9ybWF0dGVkIGhhbmRvZmYgYmxvY2sgc3RyaW5nXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZUhhbmRvZmZCbG9jayhzdGF0ZTogSGFuZG9mZlN0YXRlKTogc3RyaW5nIHtcbiAgbG9nZ2VyLmluZm8oJ0dlbmVyYXRpbmcgaGFuZG9mZiBibG9jaycsIHtcbiAgICBhZ2VudE5hbWU6IHN0YXRlLmFnZW50TmFtZSxcbiAgICBnb2FsSWQ6IHN0YXRlLmdvYWxJZCxcbiAgICByZWNlbnRFbnRyaWVzOiBzdGF0ZS5nYXRoZXJlZERhdGFTdW1tYXJ5LnJlY2VudEVudHJpZXMubGVuZ3RoLFxuICAgIHRvdGFsRW50cmllczogc3RhdGUuZ2F0aGVyZWREYXRhU3VtbWFyeS50b3RhbEVudHJpZXMsXG4gICAgZGVjaXNpb25Db3VudDogc3RhdGUuZGVjaXNpb25IaXN0b3J5Lmxlbmd0aCxcbiAgfSk7XG5cbiAgY29uc3QgbGluZXM6IHN0cmluZ1tdID0gW107XG5cbiAgLy8gSHVtYW4tcmVhZGFibGUgaGVhZGVyXG4gIGxpbmVzLnB1c2goSEFORE9GRl9NQVJLRVJfU1RBUlQpO1xuICBsaW5lcy5wdXNoKGDilZEgQUdFTlQgSEFORE9GRiDigJQgJHtzdGF0ZS5hZ2VudE5hbWV9YCk7XG4gIGxpbmVzLnB1c2goYOKVkSBHb2FsOiAke3N0YXRlLmdvYWxQcm9ncmVzcy5kZXNjcmlwdGlvbi5zdWJzdHJpbmcoMCwgODApfWApO1xuICBsaW5lcy5wdXNoKFxuICAgIGDilZEgU3RhdHVzOiAke3N0YXRlLmdvYWxQcm9ncmVzcy5zdGF0dXN9IHwgYCArXG4gICAgYFN0ZXBzOiAke3N0YXRlLmdvYWxQcm9ncmVzcy5zdGVwc0NvbXBsZXRlZH0gfCBgICtcbiAgICBgQ29uZmlkZW5jZTogJHtzdGF0ZS5nYXRoZXJlZERhdGFTdW1tYXJ5LnN1bW1hcnkuYXZlcmFnZUNvbmZpZGVuY2V9YFxuICApO1xuICBsaW5lcy5wdXNoKCfilZEnKTtcbiAgbGluZXMucHVzaCgn4pWRIE5leHQgU3RlcHM6Jyk7XG4gIGZvciAoY29uc3QgbGluZSBvZiBzdGF0ZS5jb250aW51YXRpb25JbnN0cnVjdGlvbnMuc3BsaXQoJ1xcbicpLnNsaWNlKDAsIDUpKSB7XG4gICAgbGluZXMucHVzaChg4pWRICAgJHtsaW5lfWApO1xuICB9XG4gIGxpbmVzLnB1c2goSEFORE9GRl9NQVJLRVJfRU5EKTtcblxuICAvLyBNYWNoaW5lLXJlYWRhYmxlIHBheWxvYWRcbiAgbGluZXMucHVzaChIQU5ET0ZGX1BBWUxPQURfU1RBUlQpO1xuICBsaW5lcy5wdXNoKGNvbXByZXNzSGFuZG9mZlN0YXRlKHN0YXRlKSk7XG4gIGxpbmVzLnB1c2goSEFORE9GRl9QQVlMT0FEX0VORCk7XG5cbiAgcmV0dXJuIGxpbmVzLmpvaW4oJ1xcbicpO1xufVxuXG4vKipcbiAqIFBhcnNlIGEgaGFuZG9mZiBibG9jayBhbmQgZXh0cmFjdCB0aGUgSGFuZG9mZlN0YXRlLlxuICpcbiAqIEBwYXJhbSB0ZXh0IC0gUmF3IHRleHQgY29udGFpbmluZyBhIGhhbmRvZmYgYmxvY2tcbiAqIEByZXR1cm5zIFBhcnNlZCBhbmQgdmFsaWRhdGVkIEhhbmRvZmZTdGF0ZVxuICogQHRocm93cyBFcnJvciBpZiBibG9jayBpcyBtYWxmb3JtZWQsIHBheWxvYWQgaXMgY29ycnVwdGVkLCBvciBjaGVja3N1bSBmYWlsc1xuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VIYW5kb2ZmQmxvY2sodGV4dDogc3RyaW5nKTogSGFuZG9mZlN0YXRlIHtcbiAgLy8gRXh0cmFjdCBwYXlsb2FkIGJldHdlZW4gbWFya2Vyc1xuICBjb25zdCBzdGFydElkeCA9IHRleHQuaW5kZXhPZihIQU5ET0ZGX1BBWUxPQURfU1RBUlQpO1xuICBjb25zdCBlbmRJZHggPSB0ZXh0LmluZGV4T2YoSEFORE9GRl9QQVlMT0FEX0VORCk7XG5cbiAgaWYgKHN0YXJ0SWR4ID09PSAtMSB8fCBlbmRJZHggPT09IC0xIHx8IGVuZElkeCA8PSBzdGFydElkeCkge1xuICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBoYW5kb2ZmIGJsb2NrOiBtaXNzaW5nIHBheWxvYWQgbWFya2VycycpO1xuICB9XG5cbiAgY29uc3QgcGF5bG9hZFN0YXJ0ID0gc3RhcnRJZHggKyBIQU5ET0ZGX1BBWUxPQURfU1RBUlQubGVuZ3RoO1xuICBjb25zdCBwYXlsb2FkID0gdGV4dC5zdWJzdHJpbmcocGF5bG9hZFN0YXJ0LCBlbmRJZHgpLnRyaW0oKTtcblxuICBpZiAoIXBheWxvYWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgaGFuZG9mZiBibG9jazogZW1wdHkgcGF5bG9hZCcpO1xuICB9XG5cbiAgLy8gRGVjb21wcmVzcyBhbmQgcGFyc2VcbiAgY29uc3Qgc3RhdGUgPSBkZWNvbXByZXNzSGFuZG9mZlN0YXRlKHBheWxvYWQpO1xuXG4gIC8vIFZhbGlkYXRlIGludGVncml0eVxuICBpZiAoIXZhbGlkYXRlSGFuZG9mZkNoZWNrc3VtKHN0YXRlKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBoYW5kb2ZmIGJsb2NrOiBpbnRlZ3JpdHkgY2hlY2sgZmFpbGVkJyk7XG4gIH1cblxuICAvLyBWZXJzaW9uIGNoZWNrXG4gIGlmICghc3RhdGUudmVyc2lvbikge1xuICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBoYW5kb2ZmIHN0YXRlOiBtaXNzaW5nIHZlcnNpb24nKTtcbiAgfVxuXG4gIHJldHVybiBzdGF0ZTtcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIENvbXByZXNzaW9uIFV0aWxpdGllc1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBDb21wcmVzcyBhIEhhbmRvZmZTdGF0ZSB0byBhIGJhc2U2NC1lbmNvZGVkIGd6aXAgc3RyaW5nLlxuICogSlNPTiDihpIgZ3ppcCDihpIgYmFzZTY0IGZvciBjb21wYWN0IGNvcHkvcGFzdGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb21wcmVzc0hhbmRvZmZTdGF0ZShzdGF0ZTogSGFuZG9mZlN0YXRlKTogc3RyaW5nIHtcbiAgY29uc3Qgc3RhcnRUaW1lID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIGNvbnN0IGpzb24gPSBKU09OLnN0cmluZ2lmeShzdGF0ZSk7XG4gIGNvbnN0IHVuY29tcHJlc3NlZEJ5dGVzID0gQnVmZmVyLmJ5dGVMZW5ndGgoanNvbiwgJ3V0Zi04Jyk7XG4gIGNvbnN0IGNvbXByZXNzZWQgPSBnemlwU3luYyhCdWZmZXIuZnJvbShqc29uLCAndXRmLTgnKSk7XG4gIGNvbnN0IGNvbXByZXNzZWRCeXRlcyA9IGNvbXByZXNzZWQubGVuZ3RoO1xuICBjb25zdCBjb21wcmVzc2lvblRpbWVNcyA9IE1hdGgucm91bmQoKHBlcmZvcm1hbmNlLm5vdygpIC0gc3RhcnRUaW1lKSAqIDEwMCkgLyAxMDA7XG4gIGNvbnN0IGNvbXByZXNzaW9uUmF0aW8gPSB1bmNvbXByZXNzZWRCeXRlcyA+IDBcbiAgICA/IE1hdGgucm91bmQoKDEgLSBjb21wcmVzc2VkQnl0ZXMgLyB1bmNvbXByZXNzZWRCeXRlcykgKiAxMDAwMCkgLyAxMDBcbiAgICA6IDA7XG5cbiAgbG9nZ2VyLmluZm8oJ0hhbmRvZmYgc3RhdGUgY29tcHJlc3NlZCcsIHtcbiAgICBhZ2VudE5hbWU6IHN0YXRlLmFnZW50TmFtZSxcbiAgICBnb2FsSWQ6IHN0YXRlLmdvYWxJZCxcbiAgICB1bmNvbXByZXNzZWRCeXRlcyxcbiAgICBjb21wcmVzc2VkQnl0ZXMsXG4gICAgY29tcHJlc3Npb25SYXRpbzogYCR7Y29tcHJlc3Npb25SYXRpb30lYCxcbiAgICBjb21wcmVzc2lvblRpbWVNcyxcbiAgfSk7XG5cbiAgY29uc3Qgd2FyblRocmVzaG9sZCA9IGdldEhhbmRvZmZXYXJuUGF5bG9hZEJ5dGVzKCk7XG4gIGlmIChjb21wcmVzc2VkQnl0ZXMgPiB3YXJuVGhyZXNob2xkKSB7XG4gICAgbG9nZ2VyLndhcm4oJ0hhbmRvZmYgcGF5bG9hZCBleGNlZWRzIHNpemUgdGhyZXNob2xkJywge1xuICAgICAgYWdlbnROYW1lOiBzdGF0ZS5hZ2VudE5hbWUsXG4gICAgICBnb2FsSWQ6IHN0YXRlLmdvYWxJZCxcbiAgICAgIGNvbXByZXNzZWRCeXRlcyxcbiAgICAgIHdhcm5UaHJlc2hvbGRCeXRlczogd2FyblRocmVzaG9sZCxcbiAgICB9KTtcbiAgfVxuXG4gIHJldHVybiBjb21wcmVzc2VkLnRvU3RyaW5nKCdiYXNlNjQnKTtcbn1cblxuLyoqXG4gKiBEZWNvbXByZXNzIGEgYmFzZTY0LWVuY29kZWQgZ3ppcCBzdHJpbmcgYmFjayB0byBIYW5kb2ZmU3RhdGUuXG4gKiBiYXNlNjQg4oaSIGd1bnppcCDihpIgSlNPTi5wYXJzZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZGVjb21wcmVzc0hhbmRvZmZTdGF0ZShlbmNvZGVkOiBzdHJpbmcpOiBIYW5kb2ZmU3RhdGUge1xuICB0cnkge1xuICAgIGNvbnN0IGNvbXByZXNzZWQgPSBCdWZmZXIuZnJvbShlbmNvZGVkLCAnYmFzZTY0Jyk7XG4gICAgY29uc3QgZGVjb21wcmVzc2VkID0gZ3VuemlwU3luYyhjb21wcmVzc2VkKTtcbiAgICBjb25zdCBqc29uID0gZGVjb21wcmVzc2VkLnRvU3RyaW5nKCd1dGYtOCcpO1xuICAgIGNvbnN0IHN0YXRlID0gSlNPTi5wYXJzZShqc29uKSBhcyBIYW5kb2ZmU3RhdGU7XG4gICAgcmV0dXJuIHN0YXRlO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGRldGFpbCA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKTtcbiAgICBsb2dnZXIuZXJyb3IoJ0hhbmRvZmYgcGF5bG9hZCBkZWNvbXByZXNzaW9uIGZhaWxlZCcsIHsgZGV0YWlsIH0pO1xuICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBoYW5kb2ZmIGJsb2NrOiBwYXlsb2FkIGNvdWxkIG5vdCBiZSByZXN0b3JlZCcpO1xuICB9XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBJbnRlcm5hbCBIZWxwZXJzXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIENvbXB1dGUgU0hBLTI1NiBjaGVja3N1bSBvZiBhIGRhdGEgcGF5bG9hZC5cbiAqL1xuZnVuY3Rpb24gY29tcHV0ZUNoZWNrc3VtKGRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogc3RyaW5nIHtcbiAgY29uc3QganNvbiA9IEpTT04uc3RyaW5naWZ5KGRhdGEsIE9iamVjdC5rZXlzKGRhdGEpLnNvcnQoKGEsIGIpID0+IGEubG9jYWxlQ29tcGFyZShiKSkpO1xuICByZXR1cm4gY3JlYXRlSGFzaCgnc2hhMjU2JykudXBkYXRlKGpzb24pLmRpZ2VzdCgnaGV4Jyk7XG59XG5cbi8qKlxuICogR2VuZXJhdGUgaHVtYW4tcmVhZGFibGUgY29udGludWF0aW9uIGluc3RydWN0aW9ucyBmcm9tIGdhdGhlcmVkIGRhdGEuXG4gKi9cbmZ1bmN0aW9uIGdlbmVyYXRlQ29udGludWF0aW9uSW5zdHJ1Y3Rpb25zKFxuICBhZ2VudE5hbWU6IHN0cmluZyxcbiAgZGF0YTogR2F0aGVyZWREYXRhXG4pOiBzdHJpbmcge1xuICBjb25zdCBsaW5lczogc3RyaW5nW10gPSBbXTtcblxuICBsaW5lcy5wdXNoKGBSZXN1bWUgZXhlY3V0aW9uIG9mIGFnZW50ICcke2FnZW50TmFtZX0nLmApO1xuXG4gIGlmIChkYXRhLmdvYWwuc3RhdHVzID09PSAnaW5fcHJvZ3Jlc3MnKSB7XG4gICAgbGluZXMucHVzaChgR29hbCBpcyBpbiBwcm9ncmVzczogJHtkYXRhLmdvYWwuZGVzY3JpcHRpb259YCk7XG4gICAgbGluZXMucHVzaChgJHtkYXRhLnN1bW1hcnkudG90YWxTdGVwc30gc3RlcHMgY29tcGxldGVkIHNvIGZhci5gKTtcblxuICAgIGlmIChkYXRhLnN1bW1hcnkuZmFpbGVkU3RlcHMgPiAwKSB7XG4gICAgICBsaW5lcy5wdXNoKGBOb3RlOiAke2RhdGEuc3VtbWFyeS5mYWlsZWRTdGVwc30gc3RlcChzKSBmYWlsZWQg4oCUIHJldmlldyBiZWZvcmUgY29udGludWluZy5gKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAoZGF0YS5nb2FsLnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcpIHtcbiAgICBsaW5lcy5wdXNoKGBHb2FsIHdhcyBjb21wbGV0ZWQuIFJldmlldyByZXN1bHRzIGFuZCBjbG9zZSBvdXQuYCk7XG4gIH0gZWxzZSBpZiAoZGF0YS5nb2FsLnN0YXR1cyA9PT0gJ2ZhaWxlZCcpIHtcbiAgICBsaW5lcy5wdXNoKGBHb2FsIGZhaWxlZC4gSW52ZXN0aWdhdGUgYW5kIGRlY2lkZSB3aGV0aGVyIHRvIHJldHJ5LmApO1xuICB9XG5cbiAgLy8gUmVjZW50IGRlY2lzaW9ucyBmb3IgY29udGV4dFxuICBjb25zdCByZWNlbnREZWNpc2lvbnMgPSBkYXRhLmVudHJpZXNcbiAgICAuZmlsdGVyKGUgPT4gZS50eXBlID09PSAnZGVjaXNpb24nKVxuICAgIC5zbGljZSgtMyk7XG5cbiAgaWYgKHJlY2VudERlY2lzaW9ucy5sZW5ndGggPiAwKSB7XG4gICAgbGluZXMucHVzaCgnJyk7XG4gICAgbGluZXMucHVzaCgnUmVjZW50IHN0ZXBzOicpO1xuICAgIGZvciAoY29uc3QgZCBvZiByZWNlbnREZWNpc2lvbnMpIHtcbiAgICAgIGNvbnN0IG91dGNvbWUgPSBkLmNvbnRlbnQuZGV0YWlscz8ub3V0Y29tZSB8fCAndW5rbm93bic7XG4gICAgICBsaW5lcy5wdXNoKGAgIC0gWyR7b3V0Y29tZX1dICR7ZC5jb250ZW50LnN1bW1hcnkuc3Vic3RyaW5nKDAsIDgwKX1gKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gbGluZXMuam9pbignXFxuJyk7XG59XG4iXX0=