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.

290 lines 41.5 kB
/** * File-based log sink: writes buffered entries to date-rotated files on disk. * * Error reporting in this file intentionally uses `process.stderr.write` rather * than the application logger. Using the logger would create a circular * dependency (logger → sink → logger) and risk infinite loops or masking the * original error. `process.stderr.write` is the correct pattern for any code * inside the logging subsystem itself. */ import os from 'os'; import fs from 'fs/promises'; import path from 'path'; const CATEGORY_PATTERN = /^(application|security|performance|telemetry)-(\d{4}-\d{2}-\d{2})/; // Captures category, date, optional sequence number, and extension const ROTATED_FILE_PATTERN = /^(application|security|performance|telemetry)-(\d{4}-\d{2}-\d{2})(?:\.(\d+))?(\.[^.]+)$/; export class FileLogSink { logDir; formatter; maxFileSize; retentionDays; securityRetentionDays; maxDirSizeBytes; maxFilesPerCategory; buffers = new Map(); currentDates = new Map(); sequenceCounters = new Map(); fileSizes = new Map(); initialized = false; cleanupTimer = null; constructor(options) { this.logDir = options.logDir.startsWith('~') ? path.join(os.homedir(), options.logDir.slice(1)) : options.logDir; this.formatter = options.formatter; this.maxFileSize = options.maxFileSize; this.retentionDays = options.retentionDays; this.securityRetentionDays = options.securityRetentionDays; this.maxDirSizeBytes = options.maxDirSizeBytes; this.maxFilesPerCategory = options.maxFilesPerCategory; } write(entry) { const formatted = this.formatter.format(entry); let buffer = this.buffers.get(entry.category); if (!buffer) { buffer = []; this.buffers.set(entry.category, buffer); } buffer.push(formatted); } async flush() { const categoriesToFlush = []; for (const [category, buffer] of this.buffers) { if (buffer.length === 0) continue; const content = buffer.splice(0).join(''); categoriesToFlush.push([category, content]); } if (categoriesToFlush.length === 0) return; try { await this.ensureLogDir(); for (const [category, content] of categoriesToFlush) { const filePath = await this.resolveFilePath(category, content.length); await fs.appendFile(filePath, content, { mode: 0o600 }); const currentSize = this.fileSizes.get(category) ?? 0; this.fileSizes.set(category, currentSize + content.length); } } catch (err) { // Best-effort: log to stderr, don't throw process.stderr.write(`[FileLogSink] flush error: ${err}\n`); } } async close() { await this.flush(); if (this.cleanupTimer !== null) { clearInterval(this.cleanupTimer); this.cleanupTimer = null; } } async cleanupExpiredFiles() { try { const files = await fs.readdir(this.logDir); const now = Date.now(); for (const file of files) { const match = CATEGORY_PATTERN.exec(file); if (!match) continue; const category = match[1]; const dateStr = match[2]; const fileDate = new Date(dateStr + 'T00:00:00Z'); if (isNaN(fileDate.getTime())) continue; const ageMs = now - fileDate.getTime(); const ageDays = ageMs / (1000 * 60 * 60 * 24); const retentionLimit = category === 'security' ? this.securityRetentionDays : this.retentionDays; if (ageDays > retentionLimit) { try { await fs.unlink(path.join(this.logDir, file)); } catch { // best-effort: skip files that can't be deleted } } } } catch (err) { process.stderr.write(`[FileLogSink] cleanup error: ${err}\n`); } await this.cleanupByFileCount(); await this.cleanupByDirSize(); } startCleanupTimer() { if (this.cleanupTimer !== null) return; // Run initial cleanup void this.cleanupExpiredFiles(); // Schedule every 24 hours this.cleanupTimer = setInterval(() => { void this.cleanupExpiredFiles(); }, 24 * 60 * 60 * 1000); if (this.cleanupTimer && typeof this.cleanupTimer === 'object' && 'unref' in this.cleanupTimer) { this.cleanupTimer.unref(); } } async ensureLogDir() { if (this.initialized) return; await fs.mkdir(this.logDir, { recursive: true, mode: 0o700 }); this.initialized = true; } /** * Scan the log directory to find the highest existing sequence number for a * given category+date. Used on startup/day-transition to resume from the * correct file instead of always resetting to sequence 0. * * Performance: O(n) over files in the directory, but called at most once per * category per calendar day (on process start or UTC midnight rollover). For * deployments with very large log directories, keep DOLLHOUSE_LOG_MAX_FILES_PER_CATEGORY * set (default 100) so this scan stays fast. * * NOTE: no inter-process locking — if two instances share a log dir they may * both scan and pick the same max seq, then both write to the same file. Each * instance should use its own dedicated log directory. See the troubleshooting * guide ("Multiple server instances sharing a log directory") for details. */ async scanMaxSequence(category, today) { try { const files = await fs.readdir(this.logDir); let maxSeq = 0; for (const file of files) { const m = ROTATED_FILE_PATTERN.exec(file); if (!m || m[1] !== category || m[2] !== today) continue; const seq = m[3] ? parseInt(m[3], 10) : 0; if (seq > maxSeq) maxSeq = seq; } return maxSeq; } catch { return 0; // log dir doesn't exist yet } } async resolveFilePath(category, contentSize) { const today = new Date().toISOString().slice(0, 10); const prevDate = this.currentDates.get(category); if (prevDate !== today) { this.currentDates.set(category, today); // Scan for max existing sequence to handle restarts correctly const existingMaxSeq = await this.scanMaxSequence(category, today); this.sequenceCounters.set(category, existingMaxSeq); this.fileSizes.set(category, 0); // Stat the highest-sequence file (not just base) to pick up its current size const suffix = existingMaxSeq > 0 ? `.${existingMaxSeq}` : ''; const targetPath = path.join(this.logDir, `${category}-${today}${suffix}${this.formatter.fileExtension}`); try { const stat = await fs.stat(targetPath); this.fileSizes.set(category, stat.size); } catch { // File doesn't exist yet } } const currentSize = this.fileSizes.get(category) ?? 0; let seq = this.sequenceCounters.get(category) ?? 0; // Check if we need to rotate if (currentSize > 0 && currentSize + contentSize > this.maxFileSize) { seq++; this.sequenceCounters.set(category, seq); this.fileSizes.set(category, 0); // Stat the new rotated file to get its size const rotatedPath = path.join(this.logDir, `${category}-${today}.${seq}${this.formatter.fileExtension}`); try { const stat = await fs.stat(rotatedPath); this.fileSizes.set(category, stat.size); } catch { // File doesn't exist yet } } const suffix = seq > 0 ? `.${seq}` : ''; return path.join(this.logDir, `${category}-${today}${suffix}${this.formatter.fileExtension}`); } /** * Delete oldest files per category when the file count exceeds * `maxFilesPerCategory`. Sorting is date ASC, then seq ASC within * the same date, so the oldest files are removed first. */ async cleanupByFileCount() { if (this.maxFilesPerCategory === 0) return; try { const files = await fs.readdir(this.logDir); const byCategory = new Map(); for (const file of files) { const m = ROTATED_FILE_PATTERN.exec(file); if (!m) continue; const cat = m[1]; if (!byCategory.has(cat)) byCategory.set(cat, []); byCategory.get(cat).push({ file, date: m[2], seq: m[3] ? parseInt(m[3], 10) : 0 }); } for (const [, entries] of byCategory) { if (entries.length <= this.maxFilesPerCategory) continue; // Sort oldest first: date ASC, then seq ASC within same date entries.sort((a, b) => a.date.localeCompare(b.date) || a.seq - b.seq); const toDelete = entries.slice(0, entries.length - this.maxFilesPerCategory); for (const entry of toDelete) { try { await fs.unlink(path.join(this.logDir, entry.file)); } catch { /* best-effort */ } } } } catch (err) { process.stderr.write(`[FileLogSink] cleanupByFileCount error: ${err}\n`); } } /** * Delete oldest log files (by mtime) when the total directory size exceeds * `maxDirSizeBytes`. Emits a stderr warning when a security log is deleted * so operators can investigate or increase the cap. */ async cleanupByDirSize() { if (this.maxDirSizeBytes === 0) return; try { const files = await fs.readdir(this.logDir); const entries = []; for (const file of files) { if (!ROTATED_FILE_PATTERN.exec(file)) continue; try { const stat = await fs.stat(path.join(this.logDir, file)); entries.push({ file, mtime: stat.mtimeMs, size: stat.size, isSecurity: file.startsWith('security-') }); } catch { /* best-effort */ } } const totalSize = entries.reduce((sum, e) => sum + e.size, 0); if (totalSize <= this.maxDirSizeBytes) return; // Sort oldest first by mtime entries.sort((a, b) => a.mtime - b.mtime); let remaining = totalSize; for (const entry of entries) { if (remaining <= this.maxDirSizeBytes) break; if (entry.isSecurity) { process.stderr.write(`[FileLogSink] dir-size cap: deleting security log ${entry.file} ` + `(dir=${remaining} > cap=${this.maxDirSizeBytes})\n`); } try { await fs.unlink(path.join(this.logDir, entry.file)); remaining -= entry.size; } catch { /* best-effort */ } } } catch (err) { process.stderr.write(`[FileLogSink] cleanupByDirSize error: ${err}\n`); } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmlsZUxvZ1NpbmsuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbG9nZ2luZy9zaW5rcy9GaWxlTG9nU2luay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7R0FRRztBQUVILE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQixPQUFPLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDN0IsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBYXhCLE1BQU0sZ0JBQWdCLEdBQUcsbUVBQW1FLENBQUM7QUFFN0YsbUVBQW1FO0FBQ25FLE1BQU0sb0JBQW9CLEdBQUcseUZBQXlGLENBQUM7QUFFdkgsTUFBTSxPQUFPLFdBQVc7SUFDTCxNQUFNLENBQVM7SUFDZixTQUFTLENBQWdCO0lBQ3pCLFdBQVcsQ0FBUztJQUNwQixhQUFhLENBQVM7SUFDdEIscUJBQXFCLENBQVM7SUFDOUIsZUFBZSxDQUFTO0lBQ3hCLG1CQUFtQixDQUFTO0lBRTVCLE9BQU8sR0FBRyxJQUFJLEdBQUcsRUFBeUIsQ0FBQztJQUMzQyxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFDOUMsZ0JBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFDbEQsU0FBUyxHQUFHLElBQUksR0FBRyxFQUF1QixDQUFDO0lBQ3BELFdBQVcsR0FBRyxLQUFLLENBQUM7SUFDcEIsWUFBWSxHQUEwQyxJQUFJLENBQUM7SUFFbkUsWUFBWSxPQUEyQjtRQUNyQyxJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQztZQUMxQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDbEQsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7UUFDbkIsSUFBSSxD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SSxLQUFLLENBQUMsSUFBSSxDQUFDO2dCQUMxQixDQUFDO2dCQUFDLE1BQU0sQ0FBQyxDQUFDLGlCQUFpQixDQUFDLENBQUM7WUFDL0IsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMseUNBQXlDLEdBQUcsSUFBSSxDQUFDLENBQUM7UUFDekUsQ0FBQztJQUNILENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRmlsZS1iYXNlZCBsb2cgc2luazogd3JpdGVzIGJ1ZmZlcmVkIGVudHJpZXMgdG8gZGF0ZS1yb3RhdGVkIGZpbGVzIG9uIGRpc2suXG4gKlxuICogRXJyb3IgcmVwb3J0aW5nIGluIHRoaXMgZmlsZSBpbnRlbnRpb25hbGx5IHVzZXMgYHByb2Nlc3Muc3RkZXJyLndyaXRlYCByYXRoZXJcbiAqIHRoYW4gdGhlIGFwcGxpY2F0aW9uIGxvZ2dlci4gVXNpbmcgdGhlIGxvZ2dlciB3b3VsZCBjcmVhdGUgYSBjaXJjdWxhclxuICogZGVwZW5kZW5jeSAobG9nZ2VyIOKGkiBzaW5rIOKGkiBsb2dnZXIpIGFuZCByaXNrIGluZmluaXRlIGxvb3BzIG9yIG1hc2tpbmcgdGhlXG4gKiBvcmlnaW5hbCBlcnJvci4gYHByb2Nlc3Muc3RkZXJyLndyaXRlYCBpcyB0aGUgY29ycmVjdCBwYXR0ZXJuIGZvciBhbnkgY29kZVxuICogaW5zaWRlIHRoZSBsb2dnaW5nIHN1YnN5c3RlbSBpdHNlbGYuXG4gKi9cblxuaW1wb3J0IG9zIGZyb20gJ29zJztcbmltcG9ydCBmcyBmcm9tICdmcy9wcm9taXNlcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB0eXBlIHsgSUxvZ1NpbmssIElMb2dGb3JtYXR0ZXIsIFVuaWZpZWRMb2dFbnRyeSwgTG9nQ2F0ZWdvcnkgfSBmcm9tICcuLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRmlsZUxvZ1NpbmtPcHRpb25zIHtcbiAgbG9nRGlyOiBzdHJpbmc7XG4gIGZvcm1hdHRlcjogSUxvZ0Zvcm1hdHRlcjtcbiAgbWF4RmlsZVNpemU6IG51bWJlcjtcbiAgcmV0ZW50aW9uRGF5czogbnVtYmVyO1xuICBzZWN1cml0eVJldGVudGlvbkRheXM6IG51bWJlcjtcbiAgbWF4RGlyU2l6ZUJ5dGVzOiBudW1iZXI7XG4gIG1heEZpbGVzUGVyQ2F0ZWdvcnk6IG51bWJlcjtcbn1cblxuY29uc3QgQ0FURUdPUllfUEFUVEVSTiA9IC9eKGFwcGxpY2F0aW9ufHNlY3VyaXR5fHBlcmZvcm1hbmNlfHRlbGVtZXRyeSktKFxcZHs0fS1cXGR7Mn0tXFxkezJ9KS87XG5cbi8vIENhcHR1cmVzIGNhdGVnb3J5LCBkYXRlLCBvcHRpb25hbCBzZXF1ZW5jZSBudW1iZXIsIGFuZCBleHRlbnNpb25cbmNvbnN0IFJPVEFURURfRklMRV9QQVRURVJOID0gL14oYXBwbGljYXRpb258c2VjdXJpdHl8cGVyZm9ybWFuY2V8dGVsZW1ldHJ5KS0oXFxkezR9LVxcZHsyfS1cXGR7Mn0pKD86XFwuKFxcZCspKT8oXFwuW14uXSspJC87XG5cbmV4cG9ydCBjbGFzcyBGaWxlTG9nU2luayBpbXBsZW1lbnRzIElMb2dTaW5rIHtcbiAgcHJpdmF0ZSByZWFkb25seSBsb2dEaXI6IHN0cmluZztcbiAgcHJpdmF0ZSByZWFkb25seSBmb3JtYXR0ZXI6IElMb2dGb3JtYXR0ZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWF4RmlsZVNpemU6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSByZXRlbnRpb25EYXlzOiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgc2VjdXJpdHlSZXRlbnRpb25EYXlzOiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWF4RGlyU2l6ZUJ5dGVzOiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWF4RmlsZXNQZXJDYXRlZ29yeTogbnVtYmVyO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgYnVmZmVycyA9IG5ldyBNYXA8TG9nQ2F0ZWdvcnksIHN0cmluZ1tdPigpO1xuICBwcml2YXRlIHJlYWRvbmx5IGN1cnJlbnREYXRlcyA9IG5ldyBNYXA8TG9nQ2F0ZWdvcnksIHN0cmluZz4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSBzZXF1ZW5jZUNvdW50ZXJzID0gbmV3IE1hcDxMb2dDYXRlZ29yeSwgbnVtYmVyPigpO1xuICBwcml2YXRlIHJlYWRvbmx5IGZpbGVTaXplcyA9IG5ldyBNYXA8TG9nQ2F0ZWdvcnksIG51bWJlcj4oKTtcbiAgcHJpdmF0ZSBpbml0aWFsaXplZCA9IGZhbHNlO1xuICBwcml2YXRlIGNsZWFudXBUaW1lcjogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0SW50ZXJ2YWw+IHwgbnVsbCA9IG51bGw7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogRmlsZUxvZ1NpbmtPcHRpb25zKSB7XG4gICAgdGhpcy5sb2dEaXIgPSBvcHRpb25zLmxvZ0Rpci5zdGFydHNXaXRoKCd+JylcbiAgICAgID8gcGF0aC5qb2luKG9zLmhvbWVkaXIoKSwgb3B0aW9ucy5sb2dEaXIuc2xpY2UoMSkpXG4gICAgICA6IG9wdGlvbnMubG9nRGlyO1xuICAgIHRoaXMuZm9ybWF0dGVyID0gb3B0aW9ucy5mb3JtYXR0ZXI7XG4gICAgdGhpcy5tYXhGaWxlU2l6ZSA9IG9wdGlvbnMubWF4RmlsZVNpemU7XG4gICAgdGhpcy5yZXRlbnRpb25EYXlzID0gb3B0aW9ucy5yZXRlbnRpb25EYXlzO1xuICAgIHRoaXMuc2VjdXJpdHlSZXRlbnRpb25EYXlzID0gb3B0aW9ucy5zZWN1cml0eVJldGVudGlvbkRheXM7XG4gICAgdGhpcy5tYXhEaXJTaXplQnl0ZXMgPSBvcHRpb25zLm1heERpclNpemVCeXRlcztcbiAgICB0aGlzLm1heEZpbGVzUGVyQ2F0ZWdvcnkgPSBvcHRpb25zLm1heEZpbGVzUGVyQ2F0ZWdvcnk7XG4gIH1cblxuICB3cml0ZShlbnRyeTogVW5pZmllZExvZ0VudHJ5KTogdm9pZCB7XG4gICAgY29uc3QgZm9ybWF0dGVkID0gdGhpcy5mb3JtYXR0ZXIuZm9ybWF0KGVudHJ5KTtcbiAgICBsZXQgYnVmZmVyID0gdGhpcy5idWZmZXJzLmdldChlbnRyeS5jYXRlZ29yeSk7XG4gICAgaWYgKCFidWZmZXIpIHtcbiAgICAgIGJ1ZmZlciA9IFtdO1xuICAgICAgdGhpcy5idWZmZXJzLnNldChlbnRyeS5jYXRlZ29yeSwgYnVmZmVyKTtcbiAgICB9XG4gICAgYnVmZmVyLnB1c2goZm9ybWF0dGVkKTtcbiAgfVxuXG4gIGFzeW5jIGZsdXNoKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNhdGVnb3JpZXNUb0ZsdXNoOiBBcnJheTxbTG9nQ2F0ZWdvcnksIHN0cmluZ10+ID0gW107XG5cbiAgICBmb3IgKGNvbnN0IFtjYXRlZ29yeSwgYnVmZmVyXSBvZiB0aGlzLmJ1ZmZlcnMpIHtcbiAgICAgIGlmIChidWZmZXIubGVuZ3RoID09PSAwKSBjb250aW51ZTtcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBidWZmZXIuc3BsaWNlKDApLmpvaW4oJycpO1xuICAgICAgY2F0ZWdvcmllc1RvRmx1c2gucHVzaChbY2F0ZWdvcnksIGNvbnRlbnRdKTtcbiAgICB9XG5cbiAgICBpZiAoY2F0ZWdvcmllc1RvRmx1c2gubGVuZ3RoID09PSAwKSByZXR1cm47XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5lbnN1cmVMb2dEaXIoKTtcblxuICAgICAgZm9yIChjb25zdCBbY2F0ZWdvcnksIGNvbnRlbnRdIG9mIGNhdGVnb3JpZXNUb0ZsdXNoKSB7XG4gICAgICAgIGNvbnN0IGZpbGVQYXRoID0gYXdhaXQgdGhpcy5yZXNvbHZlRmlsZVBhdGgoY2F0ZWdvcnksIGNvbnRlbnQubGVuZ3RoKTtcbiAgICAgICAgYXdhaXQgZnMuYXBwZW5kRmlsZShmaWxlUGF0aCwgY29udGVudCwgeyBtb2RlOiAwbzYwMCB9KTtcblxuICAgICAgICBjb25zdCBjdXJyZW50U2l6ZSA9IHRoaXMuZmlsZVNpemVzLmdldChjYXRlZ29yeSkgPz8gMDtcbiAgICAgICAgdGhpcy5maWxlU2l6ZXMuc2V0KGNhdGVnb3J5LCBjdXJyZW50U2l6ZSArIGNvbnRlbnQubGVuZ3RoKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIC8vIEJlc3QtZWZmb3J0OiBsb2cgdG8gc3RkZXJyLCBkb24ndCB0aHJvd1xuICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYFtGaWxlTG9nU2lua10gZmx1c2ggZXJyb3I6ICR7ZXJyfVxcbmApO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuZmx1c2goKTtcbiAgICBpZiAodGhpcy5jbGVhbnVwVGltZXIgIT09IG51bGwpIHtcbiAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5jbGVhbnVwVGltZXIpO1xuICAgICAgdGhpcy5jbGVhbnVwVGltZXIgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGNsZWFudXBFeHBpcmVkRmlsZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLmxvZ0Rpcik7XG4gICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgZmlsZXMpIHtcbiAgICAgICAgY29uc3QgbWF0Y2ggPSBDQVRFR09SWV9QQVRURVJOLmV4ZWMoZmlsZSk7XG4gICAgICAgIGlmICghbWF0Y2gpIGNvbnRpbnVlO1xuXG4gICAgICAgIGNvbnN0IGNhdGVnb3J5ID0gbWF0Y2hbMV0gYXMgc3RyaW5nO1xuICAgICAgICBjb25zdCBkYXRlU3RyID0gbWF0Y2hbMl07XG4gICAgICAgIGNvbnN0IGZpbGVEYXRlID0gbmV3IERhdGUoZGF0ZVN0ciArICdUMDA6MDA6MDBaJyk7XG4gICAgICAgIGlmIChpc05hTihmaWxlRGF0ZS5nZXRUaW1lKCkpKSBjb250aW51ZTtcblxuICAgICAgICBjb25zdCBhZ2VNcyA9IG5vdyAtIGZpbGVEYXRlLmdldFRpbWUoKTtcbiAgICAgICAgY29uc3QgYWdlRGF5cyA9IGFnZU1zIC8gKDEwMDAgKiA2MCAqIDYwICogMjQpO1xuICAgICAgICBjb25zdCByZXRlbnRpb25MaW1pdCA9IGNhdGVnb3J5ID09PSAnc2VjdXJpdHknXG4gICAgICAgICAgPyB0aGlzLnNlY3VyaXR5UmV0ZW50aW9uRGF5c1xuICAgICAgICAgIDogdGhpcy5yZXRlbnRpb25EYXlzO1xuXG4gICAgICAgIGlmIChhZ2VEYXlzID4gcmV0ZW50aW9uTGltaXQpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZnMudW5saW5rKHBhdGguam9pbih0aGlzLmxvZ0RpciwgZmlsZSkpO1xuICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgLy8gYmVzdC1lZmZvcnQ6IHNraXAgZmlsZXMgdGhhdCBjYW4ndCBiZSBkZWxldGVkXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShgW0ZpbGVMb2dTaW5rXSBjbGVhbnVwIGVycm9yOiAke2Vycn1cXG5gKTtcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLmNsZWFudXBCeUZpbGVDb3VudCgpO1xuICAgIGF3YWl0IHRoaXMuY2xlYW51cEJ5RGlyU2l6ZSgpO1xuICB9XG5cbiAgc3RhcnRDbGVhbnVwVGltZXIoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY2xlYW51cFRpbWVyICE9PSBudWxsKSByZXR1cm47XG5cbiAgICAvLyBSdW4gaW5pdGlhbCBjbGVhbnVwXG4gICAgdm9pZCB0aGlzLmNsZWFudXBFeHBpcmVkRmlsZXMoKTtcblxuICAgIC8vIFNjaGVkdWxlIGV2ZXJ5IDI0IGhvdXJzXG4gICAgdGhpcy5jbGVhbnVwVGltZXIgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICB2b2lkIHRoaXMuY2xlYW51cEV4cGlyZWRGaWxlcygpO1xuICAgIH0sIDI0ICogNjAgKiA2MCAqIDEwMDApO1xuXG4gICAgaWYgKHRoaXMuY2xlYW51cFRpbWVyICYmIHR5cGVvZiB0aGlzLmNsZWFudXBUaW1lciA9PT0gJ29iamVjdCcgJiYgJ3VucmVmJyBpbiB0aGlzLmNsZWFudXBUaW1lcikge1xuICAgICAgdGhpcy5jbGVhbnVwVGltZXIudW5yZWYoKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGVuc3VyZUxvZ0RpcigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5pbml0aWFsaXplZCkgcmV0dXJuO1xuICAgIGF3YWl0IGZzLm1rZGlyKHRoaXMubG9nRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgbW9kZTogMG83MDAgfSk7XG4gICAgdGhpcy5pbml0aWFsaXplZCA9IHRydWU7XG4gIH1cblxuICAvKipcbiAgICogU2NhbiB0aGUgbG9nIGRpcmVjdG9yeSB0byBmaW5kIHRoZSBoaWdoZXN0IGV4aXN0aW5nIHNlcXVlbmNlIG51bWJlciBmb3IgYVxuICAgKiBnaXZlbiBjYXRlZ29yeStkYXRlLiBVc2VkIG9uIHN0YXJ0dXAvZGF5LXRyYW5zaXRpb24gdG8gcmVzdW1lIGZyb20gdGhlXG4gICAqIGNvcnJlY3QgZmlsZSBpbnN0ZWFkIG9mIGFsd2F5cyByZXNldHRpbmcgdG8gc2VxdWVuY2UgMC5cbiAgICpcbiAgICogUGVyZm9ybWFuY2U6IE8obikgb3ZlciBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5LCBidXQgY2FsbGVkIGF0IG1vc3Qgb25jZSBwZXJcbiAgICogY2F0ZWdvcnkgcGVyIGNhbGVuZGFyIGRheSAob24gcHJvY2VzcyBzdGFydCBvciBVVEMgbWlkbmlnaHQgcm9sbG92ZXIpLiBGb3JcbiAgICogZGVwbG95bWVudHMgd2l0aCB2ZXJ5IGxhcmdlIGxvZyBkaXJlY3Rvcmllcywga2VlcCBET0xMSE9VU0VfTE9HX01BWF9GSUxFU19QRVJfQ0FURUdPUllcbiAgICogc2V0IChkZWZhdWx0IDEwMCkgc28gdGhpcyBzY2FuIHN0YXlzIGZhc3QuXG4gICAqXG4gICAqIE5PVEU6IG5vIGludGVyLXByb2Nlc3MgbG9ja2luZyDigJQgaWYgdHdvIGluc3RhbmNlcyBzaGFyZSBhIGxvZyBkaXIgdGhleSBtYXlcbiAgICogYm90aCBzY2FuIGFuZCBwaWNrIHRoZSBzYW1lIG1heCBzZXEsIHRoZW4gYm90aCB3cml0ZSB0byB0aGUgc2FtZSBmaWxlLiBFYWNoXG4gICAqIGluc3RhbmNlIHNob3VsZCB1c2UgaXRzIG93biBkZWRpY2F0ZWQgbG9nIGRpcmVjdG9yeS4gU2VlIHRoZSB0cm91Ymxlc2hvb3RpbmdcbiAgICogZ3VpZGUgKFwiTXVsdGlwbGUgc2VydmVyIGluc3RhbmNlcyBzaGFyaW5nIGEgbG9nIGRpcmVjdG9yeVwiKSBmb3IgZGV0YWlscy5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2Nhbk1heFNlcXVlbmNlKGNhdGVnb3J5OiBMb2dDYXRlZ29yeSwgdG9kYXk6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLmxvZ0Rpcik7XG4gICAgICBsZXQgbWF4U2VxID0gMDtcbiAgICAgIGZvciAoY29uc3QgZmlsZSBvZiBmaWxlcykge1xuICAgICAgICBjb25zdCBtID0gUk9UQVRFRF9GSUxFX1BBVFRFUk4uZXhlYyhmaWxlKTtcbiAgICAgICAgaWYgKCFtIHx8IG1bMV0gIT09IGNhdGVnb3J5IHx8IG1bMl0gIT09IHRvZGF5KSBjb250aW51ZTtcbiAgICAgICAgY29uc3Qgc2VxID0gbVszXSA/IHBhcnNlSW50KG1bM10sIDEwKSA6IDA7XG4gICAgICAgIGlmIChzZXEgPiBtYXhTZXEpIG1heFNlcSA9IHNlcTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBtYXhTZXE7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gMDsgLy8gbG9nIGRpciBkb2Vzbid0IGV4aXN0IHlldFxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVzb2x2ZUZpbGVQYXRoKGNhdGVnb3J5OiBMb2dDYXRlZ29yeSwgY29udGVudFNpemU6IG51bWJlcik6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3QgdG9kYXkgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkuc2xpY2UoMCwgMTApO1xuICAgIGNvbnN0IHByZXZEYXRlID0gdGhpcy5jdXJyZW50RGF0ZXMuZ2V0KGNhdGVnb3J5KTtcblxuICAgIGlmIChwcmV2RGF0ZSAhPT0gdG9kYXkpIHtcbiAgICAgIHRoaXMuY3VycmVudERhdGVzLnNldChjYXRlZ29yeSwgdG9kYXkpO1xuICAgICAgLy8gU2NhbiBmb3IgbWF4IGV4aXN0aW5nIHNlcXVlbmNlIHRvIGhhbmRsZSByZXN0YXJ0cyBjb3JyZWN0bHlcbiAgICAgIGNvbnN0IGV4aXN0aW5nTWF4U2VxID0gYXdhaXQgdGhpcy5zY2FuTWF4U2VxdWVuY2UoY2F0ZWdvcnksIHRvZGF5KTtcbiAgICAgIHRoaXMuc2VxdWVuY2VDb3VudGVycy5zZXQoY2F0ZWdvcnksIGV4aXN0aW5nTWF4U2VxKTtcbiAgICAgIHRoaXMuZmlsZVNpemVzLnNldChjYXRlZ29yeSwgMCk7XG5cbiAgICAgIC8vIFN0YXQgdGhlIGhpZ2hlc3Qtc2VxdWVuY2UgZmlsZSAobm90IGp1c3QgYmFzZSkgdG8gcGljayB1cCBpdHMgY3VycmVudCBzaXplXG4gICAgICBjb25zdCBzdWZmaXggPSBleGlzdGluZ01heFNlcSA+IDAgPyBgLiR7ZXhpc3RpbmdNYXhTZXF9YCA6ICcnO1xuICAgICAgY29uc3QgdGFyZ2V0UGF0aCA9IHBhdGguam9pbihcbiAgICAgICAgdGhpcy5sb2dEaXIsXG4gICAgICAgIGAke2NhdGVnb3J5fS0ke3RvZGF5fSR7c3VmZml4fSR7dGhpcy5mb3JtYXR0ZXIuZmlsZUV4dGVuc2lvbn1gLFxuICAgICAgKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHN0YXQgPSBhd2FpdCBmcy5zdGF0KHRhcmdldFBhdGgpO1xuICAgICAgICB0aGlzLmZpbGVTaXplcy5zZXQoY2F0ZWdvcnksIHN0YXQuc2l6ZSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gRmlsZSBkb2Vzbid0IGV4aXN0IHlldFxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IGN1cnJlbnRTaXplID0gdGhpcy5maWxlU2l6ZXMuZ2V0KGNhdGVnb3J5KSA/PyAwO1xuICAgIGxldCBzZXEgPSB0aGlzLnNlcXVlbmNlQ291bnRlcnMuZ2V0KGNhdGVnb3J5KSA/PyAwO1xuXG4gICAgLy8gQ2hlY2sgaWYgd2UgbmVlZCB0byByb3RhdGVcbiAgICBpZiAoY3VycmVudFNpemUgPiAwICYmIGN1cnJlbnRTaXplICsgY29udGVudFNpemUgPiB0aGlzLm1heEZpbGVTaXplKSB7XG4gICAgICBzZXErKztcbiAgICAgIHRoaXMuc2VxdWVuY2VDb3VudGVycy5zZXQoY2F0ZWdvcnksIHNlcSk7XG4gICAgICB0aGlzLmZpbGVTaXplcy5zZXQoY2F0ZWdvcnksIDApO1xuXG4gICAgICAvLyBTdGF0IHRoZSBuZXcgcm90YXRlZCBmaWxlIHRvIGdldCBpdHMgc2l6ZVxuICAgICAgY29uc3Qgcm90YXRlZFBhdGggPSBwYXRoLmpvaW4oXG4gICAgICAgIHRoaXMubG9nRGlyLFxuICAgICAgICBgJHtjYXRlZ29yeX0tJHt0b2RheX0uJHtzZXF9JHt0aGlzLmZvcm1hdHRlci5maWxlRXh0ZW5zaW9ufWAsXG4gICAgICApO1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3Qgc3RhdCA9IGF3YWl0IGZzLnN0YXQocm90YXRlZFBhdGgpO1xuICAgICAgICB0aGlzLmZpbGVTaXplcy5zZXQoY2F0ZWdvcnksIHN0YXQuc2l6ZSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gRmlsZSBkb2Vzbid0IGV4aXN0IHlldFxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHN1ZmZpeCA9IHNlcSA+IDAgPyBgLiR7c2VxfWAgOiAnJztcbiAgICByZXR1cm4gcGF0aC5qb2luKFxuICAgICAgdGhpcy5sb2dEaXIsXG4gICAgICBgJHtjYXRlZ29yeX0tJHt0b2RheX0ke3N1ZmZpeH0ke3RoaXMuZm9ybWF0dGVyLmZpbGVFeHRlbnNpb259YCxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIERlbGV0ZSBvbGRlc3QgZmlsZXMgcGVyIGNhdGVnb3J5IHdoZW4gdGhlIGZpbGUgY291bnQgZXhjZWVkc1xuICAgKiBgbWF4RmlsZXNQZXJDYXRlZ29yeWAuIFNvcnRpbmcgaXMgZGF0ZSBBU0MsIHRoZW4gc2VxIEFTQyB3aXRoaW5cbiAgICogdGhlIHNhbWUgZGF0ZSwgc28gdGhlIG9sZGVzdCBmaWxlcyBhcmUgcmVtb3ZlZCBmaXJzdC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgY2xlYW51cEJ5RmlsZUNvdW50KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLm1heEZpbGVzUGVyQ2F0ZWdvcnkgPT09IDApIHJldHVybjtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZmlsZXMgPSBhd2FpdCBmcy5yZWFkZGlyKHRoaXMubG9nRGlyKTtcbiAgICAgIGNvbnN0IGJ5Q2F0ZWdvcnkgPSBuZXcgTWFwPHN0cmluZywgQXJyYXk8eyBmaWxlOiBzdHJpbmc7IGRhdGU6IHN0cmluZzsgc2VxOiBudW1iZXIgfT4+KCk7XG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgZmlsZXMpIHtcbiAgICAgICAgY29uc3QgbSA9IFJPVEFURURfRklMRV9QQVRURVJOLmV4ZWMoZmlsZSk7XG4gICAgICAgIGlmICghbSkgY29udGludWU7XG4gICAgICAgIGNvbnN0IGNhdCA9IG1bMV07XG4gICAgICAgIGlmICghYnlDYXRlZ29yeS5oYXMoY2F0KSkgYnlDYXRlZ29yeS5zZXQoY2F0LCBbXSk7XG4gICAgICAgIGJ5Q2F0ZWdvcnkuZ2V0KGNhdCkhLnB1c2goeyBmaWxlLCBkYXRlOiBtWzJdLCBzZXE6IG1bM10gPyBwYXJzZUludChtWzNdLCAxMCkgOiAwIH0pO1xuICAgICAgfVxuICAgICAgZm9yIChjb25zdCBbLCBlbnRyaWVzXSBvZiBieUNhdGVnb3J5KSB7XG4gICAgICAgIGlmIChlbnRyaWVzLmxlbmd0aCA8PSB0aGlzLm1heEZpbGVzUGVyQ2F0ZWdvcnkpIGNvbnRpbnVlO1xuICAgICAgICAvLyBTb3J0IG9sZGVzdCBmaXJzdDogZGF0ZSBBU0MsIHRoZW4gc2VxIEFTQyB3aXRoaW4gc2FtZSBkYXRlXG4gICAgICAgIGVudHJpZXMuc29ydCgoYSwgYikgPT4gYS5kYXRlLmxvY2FsZUNvbXBhcmUoYi5kYXRlKSB8fCBhLnNlcSAtIGIuc2VxKTtcbiAgICAgICAgY29uc3QgdG9EZWxldGUgPSBlbnRyaWVzLnNsaWNlKDAsIGVudHJpZXMubGVuZ3RoIC0gdGhpcy5tYXhGaWxlc1BlckNhdGVnb3J5KTtcbiAgICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiB0b0RlbGV0ZSkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCBmcy51bmxpbmsocGF0aC5qb2luKHRoaXMubG9nRGlyLCBlbnRyeS5maWxlKSk7XG4gICAgICAgICAgfSBjYXRjaCB7IC8qIGJlc3QtZWZmb3J0ICovIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYFtGaWxlTG9nU2lua10gY2xlYW51cEJ5RmlsZUNvdW50IGVycm9yOiAke2Vycn1cXG5gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRGVsZXRlIG9sZGVzdCBsb2cgZmlsZXMgKGJ5IG10aW1lKSB3aGVuIHRoZSB0b3RhbCBkaXJlY3Rvcnkgc2l6ZSBleGNlZWRzXG4gICAqIGBtYXhEaXJTaXplQnl0ZXNgLiBFbWl0cyBhIHN0ZGVyciB3YXJuaW5nIHdoZW4gYSBzZWN1cml0eSBsb2cgaXMgZGVsZXRlZFxuICAgKiBzbyBvcGVyYXRvcnMgY2FuIGludmVzdGlnYXRlIG9yIGluY3JlYXNlIHRoZSBjYXAuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGNsZWFudXBCeURpclNpemUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMubWF4RGlyU2l6ZUJ5dGVzID09PSAwKSByZXR1cm47XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLmxvZ0Rpcik7XG4gICAgICBjb25zdCBlbnRyaWVzOiBBcnJheTx7IGZpbGU6IHN0cmluZzsgbXRpbWU6IG51bWJlcjsgc2l6ZTogbnVtYmVyOyBpc1NlY3VyaXR5OiBib29sZWFuIH0+ID0gW107XG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgZmlsZXMpIHtcbiAgICAgICAgaWYgKCFST1RBVEVEX0ZJTEVfUEFUVEVSTi5leGVjKGZpbGUpKSBjb250aW51ZTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBzdGF0ID0gYXdhaXQgZnMuc3RhdChwYXRoLmpvaW4odGhpcy5sb2dEaXIsIGZpbGUpKTtcbiAgICAgICAgICBlbnRyaWVzLnB1c2goeyBmaWxlLCBtdGltZTogc3RhdC5tdGltZU1zLCBzaXplOiBzdGF0LnNpemUsIGlzU2VjdXJpdHk6IGZpbGUuc3RhcnRzV2l0aCgnc2VjdXJpdHktJykgfSk7XG4gICAgICAgIH0gY2F0Y2ggeyAvKiBiZXN0LWVmZm9ydCAqLyB9XG4gICAgICB9XG4gICAgICBjb25zdCB0b3RhbFNpemUgPSBlbnRyaWVzLnJlZHVjZSgoc3VtLCBlKSA9PiBzdW0gKyBlLnNpemUsIDApO1xuICAgICAgaWYgKHRvdGFsU2l6ZSA8PSB0aGlzLm1heERpclNpemVCeXRlcykgcmV0dXJuO1xuICAgICAgLy8gU29ydCBvbGRlc3QgZmlyc3QgYnkgbXRpbWVcbiAgICAgIGVudHJpZXMuc29ydCgoYSwgYikgPT4gYS5tdGltZSAtIGIubXRpbWUpO1xuICAgICAgbGV0IHJlbWFpbmluZyA9IHRvdGFsU2l6ZTtcbiAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgICBpZiAocmVtYWluaW5nIDw9IHRoaXMubWF4RGlyU2l6ZUJ5dGVzKSBicmVhaztcbiAgICAgICAgaWYgKGVudHJ5LmlzU2VjdXJpdHkpIHtcbiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgIGBbRmlsZUxvZ1NpbmtdIGRpci1zaXplIGNhcDogZGVsZXRpbmcgc2VjdXJpdHkgbG9nICR7ZW50cnkuZmlsZX0gYCArXG4gICAgICAgICAgICBgKGRpcj0ke3JlbWFpbmluZ30gPiBjYXA9JHt0aGlzLm1heERpclNpemVCeXRlc30pXFxuYCxcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgZnMudW5saW5rKHBhdGguam9pbih0aGlzLmxvZ0RpciwgZW50cnkuZmlsZSkpO1xuICAgICAgICAgIHJlbWFpbmluZyAtPSBlbnRyeS5zaXplO1xuICAgICAgICB9IGNhdGNoIHsgLyogYmVzdC1lZmZvcnQgKi8gfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYFtGaWxlTG9nU2lua10gY2xlYW51cEJ5RGlyU2l6ZSBlcnJvcjogJHtlcnJ9XFxuYCk7XG4gICAgfVxuICB9XG59XG4iXX0=