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.

211 lines 29.2 kB
/** * Central coordinator for the Unified Logging System. * * Accepts UnifiedLogEntry objects from all sources, enforces level filtering * and entry size limits, routes entries to registered sinks, and manages * flush lifecycle (timer + buffer threshold + immediate flush for security). * * See docs/LOGGING-DESIGN.md §4.2 for the full design. */ import { EvictingQueue } from '../utils/EvictingQueue.js'; import { LOG_LEVEL_PRIORITY, } from './types.js'; export class LogManager { sinks = []; buffer; config; flushTimer = null; entryCounter = 0; dropCount = 0; previousBufferSize = 0; // Sliding-window rate limiter for immediate flushes (per-second window) immediateFlushWindowStart = 0; immediateFlushCount = 0; constructor(config) { this.config = config; this.buffer = new EvictingQueue(config.bufferSize); this.startFlushTimer(); } // --------------------------------------------------------------------------- // Public API // --------------------------------------------------------------------------- /** * Route a log entry to all registered sinks. * * - Skips entries below the minimum log level. * - Enforces max entry size (truncates `data`, sets `_truncated`). * - Security warn/error entries trigger an immediate (rate-limited) flush. * - Other entries are buffered; a flush is triggered when the buffer is full. */ log(entry) { // Level gate if (LOG_LEVEL_PRIORITY[entry.level] < LOG_LEVEL_PRIORITY[this.config.logLevel]) { return; } // Enforce entry size limit const enforced = this.enforceEntrySize(entry); // Route to every registered sink for (const sink of this.sinks) { sink.write(enforced); } // Determine flush strategy const needsImmediateFlush = enforced.category === 'security' && (enforced.level === 'warn' || enforced.level === 'error'); if (needsImmediateFlush && this.canImmediateFlush()) { // Fire-and-forget — logging must never block the caller void this.flush(); } else { // Track buffer occupancy for backpressure reporting const wasFull = this.buffer.size === this.buffer.capacity; this.buffer.push(enforced); // Detect eviction (buffer was at capacity before push) if (wasFull) { this.dropCount++; } // Buffer-full flush — only on the transition to full, not while already full. // The periodic flush timer handles steady-state draining. if (!wasFull && this.buffer.size >= this.config.bufferSize) { void this.flush(); } } } /** Register an output sink. */ registerSink(sink) { this.sinks.push(sink); } /** Flush all registered sinks and report drops if any occurred. */ async flush() { // Accumulate drops and only report when meaningful. // Single-entry evictions are normal ring-buffer cycling under load; // only warn when drops exceed 10% of buffer capacity per flush interval. const dropThreshold = Math.max(5, Math.ceil(this.config.bufferSize * 0.1)); if (this.dropCount >= dropThreshold) { const dropEntry = this.createMetaEntry('warn', `Backpressure: ${this.dropCount} buffered entries evicted since last flush`, { droppedCount: this.dropCount }); for (const sink of this.sinks) { sink.write(dropEntry); } this.dropCount = 0; } const flushPromises = this.sinks.map((sink) => sink.flush()); await Promise.allSettled(flushPromises); } /** Graceful shutdown: clear timer, flush, then close all sinks. */ async close() { this.stopFlushTimer(); await this.flush(); const closePromises = this.sinks.map((sink) => sink.close()); await Promise.allSettled(closePromises); } /** * Generate a unique log entry ID. * Format: `LOG-{timestamp}-{counter}` */ generateId() { return `LOG-${Date.now()}-${this.entryCounter++}`; } // --------------------------------------------------------------------------- // Internals // --------------------------------------------------------------------------- startFlushTimer() { if (this.config.flushIntervalMs > 0) { this.flushTimer = setInterval(() => { void this.flush(); }, this.config.flushIntervalMs); // Allow the process to exit even if the timer is still running if (this.flushTimer && typeof this.flushTimer === 'object' && 'unref' in this.flushTimer) { this.flushTimer.unref(); } } } stopFlushTimer() { if (this.flushTimer !== null) { clearInterval(this.flushTimer); this.flushTimer = null; } } /** * Sliding-window rate limiter for immediate flushes. * Returns `true` if an immediate flush is allowed right now. */ canImmediateFlush() { const now = Date.now(); const windowMs = 1000; if (now - this.immediateFlushWindowStart >= windowMs) { // New window this.immediateFlushWindowStart = now; this.immediateFlushCount = 1; return true; } if (this.immediateFlushCount < this.config.immediateFlushRate) { this.immediateFlushCount++; return true; } // Rate exceeded — demote to buffered path (caller will buffer instead) return false; } /** * Enforce `maxEntrySize` by truncating the `data` field if necessary. * Returns the original entry unmodified when under the limit, or a * shallow copy with `data` replaced when truncation is needed. */ enforceEntrySize(entry) { if (!entry.data) return entry; const serialized = JSON.stringify(entry); if (serialized.length <= this.config.maxEntrySize) return entry; const originalSize = serialized.length; // Emit a warn meta-entry about the truncation const truncationNotice = this.createMetaEntry('warn', `Entry truncated: original size ${originalSize} bytes exceeds limit ${this.config.maxEntrySize}`, { originalSize, maxSize: this.config.maxEntrySize, entryId: entry.id }); for (const sink of this.sinks) { sink.write(truncationNotice); } // Return a copy with data replaced by a truncation marker return { ...entry, data: { _truncated: true, _originalSize: originalSize }, }; } /** Create a meta/system log entry from the LogManager itself. */ createMetaEntry(level, message, data) { return { id: this.generateId(), timestamp: new Date().toISOString(), category: 'application', level, source: 'LogManager', message, data, }; } } // --------------------------------------------------------------------------- // Helper: build LogManagerConfig from validated env vars // --------------------------------------------------------------------------- /** * Map the flat env object (from Zod-parsed `process.env`) to a typed * `LogManagerConfig`. Keeps the mapping in one place so the DI container * only needs `buildLogManagerConfig(env)`. */ export function buildLogManagerConfig(envVars) { return { logDir: envVars.DOLLHOUSE_LOG_DIR, logFormat: envVars.DOLLHOUSE_LOG_FORMAT, retentionDays: envVars.DOLLHOUSE_LOG_RETENTION_DAYS, securityRetentionDays: envVars.DOLLHOUSE_LOG_SECURITY_RETENTION_DAYS, flushIntervalMs: envVars.DOLLHOUSE_LOG_FLUSH_INTERVAL_MS, bufferSize: envVars.DOLLHOUSE_LOG_BUFFER_SIZE, memoryCapacity: envVars.DOLLHOUSE_LOG_MEMORY_CAPACITY, memoryAppCapacity: envVars.DOLLHOUSE_LOG_MEMORY_APP_CAPACITY, memorySecurityCapacity: envVars.DOLLHOUSE_LOG_MEMORY_SECURITY_CAPACITY, memoryPerfCapacity: envVars.DOLLHOUSE_LOG_MEMORY_PERF_CAPACITY, memoryTelemetryCapacity: envVars.DOLLHOUSE_LOG_MEMORY_TELEMETRY_CAPACITY, maxEntrySize: envVars.DOLLHOUSE_LOG_MAX_ENTRY_SIZE, immediateFlushRate: envVars.DOLLHOUSE_LOG_IMMEDIATE_FLUSH_RATE, fileMaxSize: envVars.DOLLHOUSE_LOG_FILE_MAX_SIZE, maxDirSizeBytes: envVars.DOLLHOUSE_LOG_MAX_DIR_SIZE_BYTES, maxFilesPerCategory: envVars.DOLLHOUSE_LOG_MAX_FILES_PER_CATEGORY, logLevel: envVars.LOG_LEVEL, }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTG9nTWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sb2dnaW5nL0xvZ01hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFFSCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDMUQsT0FBTyxFQUtMLGtCQUFrQixHQUNuQixNQUFNLFlBQVksQ0FBQztBQUVwQixNQUFNLE9BQU8sVUFBVTtJQUNKLEtBQUssR0FBZSxFQUFFLENBQUM7SUFDdkIsTUFBTSxDQUFpQztJQUN2QyxNQUFNLENBQW1CO0lBQ2xDLFVBQVUsR0FBMEMsSUFBSSxDQUFDO0lBQ3pELFlBQVksR0FBRyxDQUFDLENBQUM7SUFDakIsU0FBUyxHQUFHLENBQUMsQ0FBQztJQUNkLGtCQUFrQixHQUFHLENBQUMsQ0FBQztJQUUvQix3RUFBd0U7SUFDaEUseUJBQXlCLEdBQUcsQ0FBQyxDQUFDO0lBQzlCLG1CQUFtQixHQUFHLENBQUMsQ0FBQztJQUVoQyxZQUFZLE1BQXdCO1FBQ2xDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxhQUFhLENBQWtCLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNwRSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7SUFDekIsQ0FBQztJQUVELDhFQUE4RTtJQUM5RSxhQUFhO0lBQ2IsOEVBQThFO0lBRTlFOzs7Ozs7O09BT0c7SUFDSCxHQUFHLENBQUMsS0FBc0I7UUFDeEIsYUFBYTtRQUNiLElBQUksa0JBQWtCLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLGtCQUFrQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUMvRSxPQUFPO1FBQ1QsQ0FBQztRQUVELDJCQUEyQjtRQUMzQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMs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QUFFLE9BQU8sQ0FBQyxnQ0FBZ0M7UUFDekQsbUJBQW1CLEVBQUUsT0FBTyxDQUFDLG9DQUFvQztRQUNqRSxRQUFRLEVBQUUsT0FBTyxDQUFDLFNBQVM7S0FDNUIsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENlbnRyYWwgY29vcmRpbmF0b3IgZm9yIHRoZSBVbmlmaWVkIExvZ2dpbmcgU3lzdGVtLlxuICpcbiAqIEFjY2VwdHMgVW5pZmllZExvZ0VudHJ5IG9iamVjdHMgZnJvbSBhbGwgc291cmNlcywgZW5mb3JjZXMgbGV2ZWwgZmlsdGVyaW5nXG4gKiBhbmQgZW50cnkgc2l6ZSBsaW1pdHMsIHJvdXRlcyBlbnRyaWVzIHRvIHJlZ2lzdGVyZWQgc2lua3MsIGFuZCBtYW5hZ2VzXG4gKiBmbHVzaCBsaWZlY3ljbGUgKHRpbWVyICsgYnVmZmVyIHRocmVzaG9sZCArIGltbWVkaWF0ZSBmbHVzaCBmb3Igc2VjdXJpdHkpLlxuICpcbiAqIFNlZSBkb2NzL0xPR0dJTkctREVTSUdOLm1kIMKnNC4yIGZvciB0aGUgZnVsbCBkZXNpZ24uXG4gKi9cblxuaW1wb3J0IHsgRXZpY3RpbmdRdWV1ZSB9IGZyb20gJy4uL3V0aWxzL0V2aWN0aW5nUXVldWUuanMnO1xuaW1wb3J0IHtcbiAgdHlwZSBVbmlmaWVkTG9nRW50cnksXG4gIHR5cGUgSUxvZ1NpbmssXG4gIHR5cGUgTG9nTGV2ZWwsXG4gIHR5cGUgTG9nTWFuYWdlckNvbmZpZyxcbiAgTE9HX0xFVkVMX1BSSU9SSVRZLFxufSBmcm9tICcuL3R5cGVzLmpzJztcblxuZXhwb3J0IGNsYXNzIExvZ01hbmFnZXIge1xuICBwcml2YXRlIHJlYWRvbmx5IHNpbmtzOiBJTG9nU2lua1tdID0gW107XG4gIHByaXZhdGUgcmVhZG9ubHkgYnVmZmVyOiBFdmljdGluZ1F1ZXVlPFVuaWZpZWRMb2dFbnRyeT47XG4gIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBMb2dNYW5hZ2VyQ29uZmlnO1xuICBwcml2YXRlIGZsdXNoVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldEludGVydmFsPiB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGVudHJ5Q291bnRlciA9IDA7XG4gIHByaXZhdGUgZHJvcENvdW50ID0gMDtcbiAgcHJpdmF0ZSBwcmV2aW91c0J1ZmZlclNpemUgPSAwO1xuXG4gIC8vIFNsaWRpbmctd2luZG93IHJhdGUgbGltaXRlciBmb3IgaW1tZWRpYXRlIGZsdXNoZXMgKHBlci1zZWNvbmQgd2luZG93KVxuICBwcml2YXRlIGltbWVkaWF0ZUZsdXNoV2luZG93U3RhcnQgPSAwO1xuICBwcml2YXRlIGltbWVkaWF0ZUZsdXNoQ291bnQgPSAwO1xuXG4gIGNvbnN0cnVjdG9yKGNvbmZpZzogTG9nTWFuYWdlckNvbmZpZykge1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMuYnVmZmVyID0gbmV3IEV2aWN0aW5nUXVldWU8VW5pZmllZExvZ0VudHJ5Pihjb25maWcuYnVmZmVyU2l6ZSk7XG4gICAgdGhpcy5zdGFydEZsdXNoVGltZXIoKTtcbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBQdWJsaWMgQVBJXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIC8qKlxuICAgKiBSb3V0ZSBhIGxvZyBlbnRyeSB0byBhbGwgcmVnaXN0ZXJlZCBzaW5rcy5cbiAgICpcbiAgICogLSBTa2lwcyBlbnRyaWVzIGJlbG93IHRoZSBtaW5pbXVtIGxvZyBsZXZlbC5cbiAgICogLSBFbmZvcmNlcyBtYXggZW50cnkgc2l6ZSAodHJ1bmNhdGVzIGBkYXRhYCwgc2V0cyBgX3RydW5jYXRlZGApLlxuICAgKiAtIFNlY3VyaXR5IHdhcm4vZXJyb3IgZW50cmllcyB0cmlnZ2VyIGFuIGltbWVkaWF0ZSAocmF0ZS1saW1pdGVkKSBmbHVzaC5cbiAgICogLSBPdGhlciBlbnRyaWVzIGFyZSBidWZmZXJlZDsgYSBmbHVzaCBpcyB0cmlnZ2VyZWQgd2hlbiB0aGUgYnVmZmVyIGlzIGZ1bGwuXG4gICAqL1xuICBsb2coZW50cnk6IFVuaWZpZWRMb2dFbnRyeSk6IHZvaWQge1xuICAgIC8vIExldmVsIGdhdGVcbiAgICBpZiAoTE9HX0xFVkVMX1BSSU9SSVRZW2VudHJ5LmxldmVsXSA8IExPR19MRVZFTF9QUklPUklUWVt0aGlzLmNvbmZpZy5sb2dMZXZlbF0pIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBFbmZvcmNlIGVudHJ5IHNpemUgbGltaXRcbiAgICBjb25zdCBlbmZvcmNlZCA9IHRoaXMuZW5mb3JjZUVudHJ5U2l6ZShlbnRyeSk7XG5cbiAgICAvLyBSb3V0ZSB0byBldmVyeSByZWdpc3RlcmVkIHNpbmtcbiAgICBmb3IgKGNvbnN0IHNpbmsgb2YgdGhpcy5zaW5rcykge1xuICAgICAgc2luay53cml0ZShlbmZvcmNlZCk7XG4gICAgfVxuXG4gICAgLy8gRGV0ZXJtaW5lIGZsdXNoIHN0cmF0ZWd5XG4gICAgY29uc3QgbmVlZHNJbW1lZGlhdGVGbHVzaCA9XG4gICAgICBlbmZvcmNlZC5jYXRlZ29yeSA9PT0gJ3NlY3VyaXR5JyAmJlxuICAgICAgKGVuZm9yY2VkLmxldmVsID09PSAnd2FybicgfHwgZW5mb3JjZWQubGV2ZWwgPT09ICdlcnJvcicpO1xuXG4gICAgaWYgKG5lZWRzSW1tZWRpYXRlRmx1c2ggJiYgdGhpcy5jYW5JbW1lZGlhdGVGbHVzaCgpKSB7XG4gICAgICAvLyBGaXJlLWFuZC1mb3JnZXQg4oCUIGxvZ2dpbmcgbXVzdCBuZXZlciBibG9jayB0aGUgY2FsbGVyXG4gICAgICB2b2lkIHRoaXMuZmx1c2goKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gVHJhY2sgYnVmZmVyIG9jY3VwYW5jeSBmb3IgYmFja3ByZXNzdXJlIHJlcG9ydGluZ1xuICAgICAgY29uc3Qgd2FzRnVsbCA9IHRoaXMuYnVmZmVyLnNpemUgPT09IHRoaXMuYnVmZmVyLmNhcGFjaXR5O1xuICAgICAgdGhpcy5idWZmZXIucHVzaChlbmZvcmNlZCk7XG5cbiAgICAgIC8vIERldGVjdCBldmljdGlvbiAoYnVmZmVyIHdhcyBhdCBjYXBhY2l0eSBiZWZvcmUgcHVzaClcbiAgICAgIGlmICh3YXNGdWxsKSB7XG4gICAgICAgIHRoaXMuZHJvcENvdW50Kys7XG4gICAgICB9XG5cbiAgICAgIC8vIEJ1ZmZlci1mdWxsIGZsdXNoIOKAlCBvbmx5IG9uIHRoZSB0cmFuc2l0aW9uIHRvIGZ1bGwsIG5vdCB3aGlsZSBhbHJlYWR5IGZ1bGwuXG4gICAgICAvLyBUaGUgcGVyaW9kaWMgZmx1c2ggdGltZXIgaGFuZGxlcyBzdGVhZHktc3RhdGUgZHJhaW5pbmcuXG4gICAgICBpZiAoIXdhc0Z1bGwgJiYgdGhpcy5idWZmZXIuc2l6ZSA+PSB0aGlzLmNvbmZpZy5idWZmZXJTaXplKSB7XG4gICAgICAgIHZvaWQgdGhpcy5mbHVzaCgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBSZWdpc3RlciBhbiBvdXRwdXQgc2luay4gKi9cbiAgcmVnaXN0ZXJTaW5rKHNpbms6IElMb2dTaW5rKTogdm9pZCB7XG4gICAgdGhpcy5zaW5rcy5wdXNoKHNpbmspO1xuICB9XG5cbiAgLyoqIEZsdXNoIGFsbCByZWdpc3RlcmVkIHNpbmtzIGFuZCByZXBvcnQgZHJvcHMgaWYgYW55IG9jY3VycmVkLiAqL1xuICBhc3luYyBmbHVzaCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBBY2N1bXVsYXRlIGRyb3BzIGFuZCBvbmx5IHJlcG9ydCB3aGVuIG1lYW5pbmdmdWwuXG4gICAgLy8gU2luZ2xlLWVudHJ5IGV2aWN0aW9ucyBhcmUgbm9ybWFsIHJpbmctYnVmZmVyIGN5Y2xpbmcgdW5kZXIgbG9hZDtcbiAgICAvLyBvbmx5IHdhcm4gd2hlbiBkcm9wcyBleGNlZWQgMTAlIG9mIGJ1ZmZlciBjYXBhY2l0eSBwZXIgZmx1c2ggaW50ZXJ2YWwuXG4gICAgY29uc3QgZHJvcFRocmVzaG9sZCA9IE1hdGgubWF4KDUsIE1hdGguY2VpbCh0aGlzLmNvbmZpZy5idWZmZXJTaXplICogMC4xKSk7XG4gICAgaWYgKHRoaXMuZHJvcENvdW50ID49IGRyb3BUaHJlc2hvbGQpIHtcbiAgICAgIGNvbnN0IGRyb3BFbnRyeSA9IHRoaXMuY3JlYXRlTWV0YUVudHJ5KFxuICAgICAgICAnd2FybicsXG4gICAgICAgIGBCYWNrcHJlc3N1cmU6ICR7dGhpcy5kcm9wQ291bnR9IGJ1ZmZlcmVkIGVudHJpZXMgZXZpY3RlZCBzaW5jZSBsYXN0IGZsdXNoYCxcbiAgICAgICAgeyBkcm9wcGVkQ291bnQ6IHRoaXMuZHJvcENvdW50IH0sXG4gICAgICApO1xuICAgICAgZm9yIChjb25zdCBzaW5rIG9mIHRoaXMuc2lua3MpIHtcbiAgICAgICAgc2luay53cml0ZShkcm9wRW50cnkpO1xuICAgICAgfVxuICAgICAgdGhpcy5kcm9wQ291bnQgPSAwO1xuICAgIH1cblxuICAgIGNvbnN0IGZsdXNoUHJvbWlzZXMgPSB0aGlzLnNpbmtzLm1hcCgoc2luaykgPT4gc2luay5mbHVzaCgpKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoZmx1c2hQcm9taXNlcyk7XG4gIH1cblxuICAvKiogR3JhY2VmdWwgc2h1dGRvd246IGNsZWFyIHRpbWVyLCBmbHVzaCwgdGhlbiBjbG9zZSBhbGwgc2lua3MuICovXG4gIGFzeW5jIGNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuc3RvcEZsdXNoVGltZXIoKTtcbiAgICBhd2FpdCB0aGlzLmZsdXNoKCk7XG4gICAgY29uc3QgY2xvc2VQcm9taXNlcyA9IHRoaXMuc2lua3MubWFwKChzaW5rKSA9PiBzaW5rLmNsb3NlKCkpO1xuICAgIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChjbG9zZVByb21pc2VzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBhIHVuaXF1ZSBsb2cgZW50cnkgSUQuXG4gICAqIEZvcm1hdDogYExPRy17dGltZXN0YW1wfS17Y291bnRlcn1gXG4gICAqL1xuICBnZW5lcmF0ZUlkKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGBMT0ctJHtEYXRlLm5vdygpfS0ke3RoaXMuZW50cnlDb3VudGVyKyt9YDtcbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBJbnRlcm5hbHNcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgcHJpdmF0ZSBzdGFydEZsdXNoVGltZXIoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmZsdXNoSW50ZXJ2YWxNcyA+IDApIHtcbiAgICAgIHRoaXMuZmx1c2hUaW1lciA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgICAgdm9pZCB0aGlzLmZsdXNoKCk7XG4gICAgICB9LCB0aGlzLmNvbmZpZy5mbHVzaEludGVydmFsTXMpO1xuXG4gICAgICAvLyBBbGxvdyB0aGUgcHJvY2VzcyB0byBleGl0IGV2ZW4gaWYgdGhlIHRpbWVyIGlzIHN0aWxsIHJ1bm5pbmdcbiAgICAgIGlmICh0aGlzLmZsdXNoVGltZXIgJiYgdHlwZW9mIHRoaXMuZmx1c2hUaW1lciA9PT0gJ29iamVjdCcgJiYgJ3VucmVmJyBpbiB0aGlzLmZsdXNoVGltZXIpIHtcbiAgICAgICAgdGhpcy5mbHVzaFRpbWVyLnVucmVmKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzdG9wRmx1c2hUaW1lcigpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5mbHVzaFRpbWVyICE9PSBudWxsKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMuZmx1c2hUaW1lcik7XG4gICAgICB0aGlzLmZsdXNoVGltZXIgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTbGlkaW5nLXdpbmRvdyByYXRlIGxpbWl0ZXIgZm9yIGltbWVkaWF0ZSBmbHVzaGVzLlxuICAgKiBSZXR1cm5zIGB0cnVlYCBpZiBhbiBpbW1lZGlhdGUgZmx1c2ggaXMgYWxsb3dlZCByaWdodCBub3cuXG4gICAqL1xuICBwcml2YXRlIGNhbkltbWVkaWF0ZUZsdXNoKCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3Qgd2luZG93TXMgPSAxMDAwO1xuXG4gICAgaWYgKG5vdyAtIHRoaXMuaW1tZWRpYXRlRmx1c2hXaW5kb3dTdGFydCA+PSB3aW5kb3dNcykge1xuICAgICAgLy8gTmV3IHdpbmRvd1xuICAgICAgdGhpcy5pbW1lZGlhdGVGbHVzaFdpbmRvd1N0YXJ0ID0gbm93O1xuICAgICAgdGhpcy5pbW1lZGlhdGVGbHVzaENvdW50ID0gMTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmltbWVkaWF0ZUZsdXNoQ291bnQgPCB0aGlzLmNvbmZpZy5pbW1lZGlhdGVGbHVzaFJhdGUpIHtcbiAgICAgIHRoaXMuaW1tZWRpYXRlRmx1c2hDb3VudCsrO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgLy8gUmF0ZSBleGNlZWRlZCDigJQgZGVtb3RlIHRvIGJ1ZmZlcmVkIHBhdGggKGNhbGxlciB3aWxsIGJ1ZmZlciBpbnN0ZWFkKVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmZvcmNlIGBtYXhFbnRyeVNpemVgIGJ5IHRydW5jYXRpbmcgdGhlIGBkYXRhYCBmaWVsZCBpZiBuZWNlc3NhcnkuXG4gICAqIFJldHVybnMgdGhlIG9yaWdpbmFsIGVudHJ5IHVubW9kaWZpZWQgd2hlbiB1bmRlciB0aGUgbGltaXQsIG9yIGFcbiAgICogc2hhbGxvdyBjb3B5IHdpdGggYGRhdGFgIHJlcGxhY2VkIHdoZW4gdHJ1bmNhdGlvbiBpcyBuZWVkZWQuXG4gICAqL1xuICBwcml2YXRlIGVuZm9yY2VFbnRyeVNpemUoZW50cnk6IFVuaWZpZWRMb2dFbnRyeSk6IFVuaWZpZWRMb2dFbnRyeSB7XG4gICAgaWYgKCFlbnRyeS5kYXRhKSByZXR1cm4gZW50cnk7XG5cbiAgICBjb25zdCBzZXJpYWxpemVkID0gSlNPTi5zdHJpbmdpZnkoZW50cnkpO1xuICAgIGlmIChzZXJpYWxpemVkLmxlbmd0aCA8PSB0aGlzLmNvbmZpZy5tYXhFbnRyeVNpemUpIHJldHVybiBlbnRyeTtcblxuICAgIGNvbnN0IG9yaWdpbmFsU2l6ZSA9IHNlcmlhbGl6ZWQubGVuZ3RoO1xuXG4gICAgLy8gRW1pdCBhIHdhcm4gbWV0YS1lbnRyeSBhYm91dCB0aGUgdHJ1bmNhdGlvblxuICAgIGNvbnN0IHRydW5jYXRpb25Ob3RpY2UgPSB0aGlzLmNyZWF0ZU1ldGFFbnRyeShcbiAgICAgICd3YXJuJyxcbiAgICAgIGBFbnRyeSB0cnVuY2F0ZWQ6IG9yaWdpbmFsIHNpemUgJHtvcmlnaW5hbFNpemV9IGJ5dGVzIGV4Y2VlZHMgbGltaXQgJHt0aGlzLmNvbmZpZy5tYXhFbnRyeVNpemV9YCxcbiAgICAgIHsgb3JpZ2luYWxTaXplLCBtYXhTaXplOiB0aGlzLmNvbmZpZy5tYXhFbnRyeVNpemUsIGVudHJ5SWQ6IGVudHJ5LmlkIH0sXG4gICAgKTtcbiAgICBmb3IgKGNvbnN0IHNpbmsgb2YgdGhpcy5zaW5rcykge1xuICAgICAgc2luay53cml0ZSh0cnVuY2F0aW9uTm90aWNlKTtcbiAgICB9XG5cbiAgICAvLyBSZXR1cm4gYSBjb3B5IHdpdGggZGF0YSByZXBsYWNlZCBieSBhIHRydW5jYXRpb24gbWFya2VyXG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLmVudHJ5LFxuICAgICAgZGF0YTogeyBfdHJ1bmNhdGVkOiB0cnVlLCBfb3JpZ2luYWxTaXplOiBvcmlnaW5hbFNpemUgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIENyZWF0ZSBhIG1ldGEvc3lzdGVtIGxvZyBlbnRyeSBmcm9tIHRoZSBMb2dNYW5hZ2VyIGl0c2VsZi4gKi9cbiAgcHJpdmF0ZSBjcmVhdGVNZXRhRW50cnkoXG4gICAgbGV2ZWw6IExvZ0xldmVsLFxuICAgIG1lc3NhZ2U6IHN0cmluZyxcbiAgICBkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICk6IFVuaWZpZWRMb2dFbnRyeSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGlkOiB0aGlzLmdlbmVyYXRlSWQoKSxcbiAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgY2F0ZWdvcnk6ICdhcHBsaWNhdGlvbicsXG4gICAgICBsZXZlbCxcbiAgICAgIHNvdXJjZTogJ0xvZ01hbmFnZXInLFxuICAgICAgbWVzc2FnZSxcbiAgICAgIGRhdGEsXG4gICAgfTtcbiAgfVxufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIEhlbHBlcjogYnVpbGQgTG9nTWFuYWdlckNvbmZpZyBmcm9tIHZhbGlkYXRlZCBlbnYgdmFyc1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi8qKlxuICogTWFwIHRoZSBmbGF0IGVudiBvYmplY3QgKGZyb20gWm9kLXBhcnNlZCBgcHJvY2Vzcy5lbnZgKSB0byBhIHR5cGVkXG4gKiBgTG9nTWFuYWdlckNvbmZpZ2AuIEtlZXBzIHRoZSBtYXBwaW5nIGluIG9uZSBwbGFjZSBzbyB0aGUgREkgY29udGFpbmVyXG4gKiBvbmx5IG5lZWRzIGBidWlsZExvZ01hbmFnZXJDb25maWcoZW52KWAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZExvZ01hbmFnZXJDb25maWcoZW52VmFyczoge1xuICBET0xMSE9VU0VfTE9HX0RJUjogc3RyaW5nO1xuICBET0xMSE9VU0VfTE9HX0ZPUk1BVDogJ3RleHQnIHwgJ2pzb25sJztcbiAgRE9MTEhPVVNFX0xPR19SRVRFTlRJT05fREFZUzogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX1NFQ1VSSVRZX1JFVEVOVElPTl9EQVlTOiBudW1iZXI7XG4gIERPTExIT1VTRV9MT0dfRkxVU0hfSU5URVJWQUxfTVM6IG51bWJlcjtcbiAgRE9MTEhPVVNFX0xPR19CVUZGRVJfU0laRTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01FTU9SWV9DQVBBQ0lUWTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01FTU9SWV9BUFBfQ0FQQUNJVFk6IG51bWJlcjtcbiAgRE9MTEhPVVNFX0xPR19NRU1PUllfU0VDVVJJVFlfQ0FQQUNJVFk6IG51bWJlcjtcbiAgRE9MTEhPVVNFX0xPR19NRU1PUllfUEVSRl9DQVBBQ0lUWTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01FTU9SWV9URUxFTUVUUllfQ0FQQUNJVFk6IG51bWJlcjtcbiAgRE9MTEhPVVNFX0xPR19NQVhfRU5UUllfU0laRTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX0lNTUVESUFURV9GTFVTSF9SQVRFOiBudW1iZXI7XG4gIERPTExIT1VTRV9MT0dfRklMRV9NQVhfU0laRTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01BWF9ESVJfU0laRV9CWVRFUzogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01BWF9GSUxFU19QRVJfQ0FURUdPUlk6IG51bWJlcjtcbiAgTE9HX0xFVkVMOiAnZGVidWcnIHwgJ2luZm8nIHwgJ3dhcm4nIHwgJ2Vycm9yJztcbn0pOiBMb2dNYW5hZ2VyQ29uZmlnIHtcbiAgcmV0dXJuIHtcbiAgICBsb2dEaXI6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19ESVIsXG4gICAgbG9nRm9ybWF0OiBlbnZWYXJzLkRPTExIT1VTRV9MT0dfRk9STUFULFxuICAgIHJldGVudGlvbkRheXM6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19SRVRFTlRJT05fREFZUyxcbiAgICBzZWN1cml0eVJldGVudGlvbkRheXM6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19TRUNVUklUWV9SRVRFTlRJT05fREFZUyxcbiAgICBmbHVzaEludGVydmFsTXM6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19GTFVTSF9JTlRFUlZBTF9NUyxcbiAgICBidWZmZXJTaXplOiBlbnZWYXJzLkRPTExIT1VTRV9MT0dfQlVGRkVSX1NJWkUsXG4gICAgbWVtb3J5Q2FwYWNpdHk6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NRU1PUllfQ0FQQUNJVFksXG4gICAgbWVtb3J5QXBwQ2FwYWNpdHk6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NRU1PUllfQVBQX0NBUEFDSVRZLFxuICAgIG1lbW9yeVNlY3VyaXR5Q2FwYWNpdHk6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NRU1PUllfU0VDVVJJVFlfQ0FQQUNJVFksXG4gICAgbWVtb3J5UGVyZkNhcGFjaXR5OiBlbnZWYXJzLkRPTExIT1VTRV9MT0dfTUVNT1JZX1BFUkZfQ0FQQUNJVFksXG4gICAgbWVtb3J5VGVsZW1ldHJ5Q2FwYWNpdHk6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NRU1PUllfVEVMRU1FVFJZX0NBUEFDSVRZLFxuICAgIG1heEVudHJ5U2l6ZTogZW52VmFycy5ET0xMSE9VU0VfTE9HX01BWF9FTlRSWV9TSVpFLFxuICAgIGltbWVkaWF0ZUZsdXNoUmF0ZTogZW52VmFycy5ET0xMSE9VU0VfTE9HX0lNTUVESUFURV9GTFVTSF9SQVRFLFxuICAgIGZpbGVNYXhTaXplOiBlbnZWYXJzLkRPTExIT1VTRV9MT0dfRklMRV9NQVhfU0laRSxcbiAgICBtYXhEaXJTaXplQnl0ZXM6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NQVhfRElSX1NJWkVfQllURVMsXG4gICAgbWF4RmlsZXNQZXJDYXRlZ29yeTogZW52VmFycy5ET0xMSE9VU0VfTE9HX01BWF9GSUxFU19QRVJfQ0FURUdPUlksXG4gICAgbG9nTGV2ZWw6IGVudlZhcnMuTE9HX0xFVkVMLFxuICB9O1xufVxuIl19