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.

461 lines 61.1 kB
/** * Operational Telemetry System * * Privacy-first anonymous installation analytics * * What is collected: * - Anonymous installation UUID (generated locally, persistent per install) * - Server version, OS type, Node.js version, MCP client type * - Installation timestamp * * What is NOT collected: * - User identity, personal information, or identifiable data * - Element content, persona data, or user-created content * - Usage patterns, commands, or interactions * - Network data, file paths, or system details beyond OS type * * Telemetry control: * - DOLLHOUSE_TELEMETRY=true - Enables local telemetry (default: OFF) * - DOLLHOUSE_TELEMETRY=false - Disables all telemetry (local and remote) * - DOLLHOUSE_TELEMETRY_OPTIN=true - Enables remote telemetry with default PostHog project * - DOLLHOUSE_TELEMETRY_NO_REMOTE=true - Local telemetry only, no PostHog * - POSTHOG_API_KEY - Custom PostHog project key (overrides default) * - Delete telemetry files: rm ~/.dollhouse/.telemetry-id ~/.dollhouse/telemetry.log * * Data storage (only when enabled): * - Local: ~/.dollhouse/.telemetry-id (UUID) and ~/.dollhouse/telemetry.log (events) * - Remote (opt-in): PostHog analytics when DOLLHOUSE_TELEMETRY_OPTIN=true or POSTHOG_API_KEY is set * * Design principles: * - Fail gracefully: errors never crash the server * - Debug-only logging: no user-facing telemetry noise * - Check opt-out early: no file operations if disabled * - Remote telemetry is opt-in: requires DOLLHOUSE_TELEMETRY_OPTIN=true or explicit POSTHOG_API_KEY * - Local telemetry is opt-in: requires DOLLHOUSE_TELEMETRY=true */ import * as path from 'node:path'; import * as os from 'node:os'; import { v4 as uuidv4 } from 'uuid'; import { PostHog } from 'posthog-node'; import { logger } from '../utils/logger.js'; import { PACKAGE_VERSION as VERSION } from '../generated/version.js'; import { detectMCPClient } from './clientDetector.js'; import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; import { SecurityMonitor } from '../security/securityMonitor.js'; // PostHog Project API Key (safe to expose publicly - write-only) // Used for opt-in telemetry when DOLLHOUSE_TELEMETRY_OPTIN=true // Can be overridden with POSTHOG_API_KEY for custom installations const DEFAULT_POSTHOG_PROJECT_KEY = 'phc_xFJKIHAqRX1YLa0TSdTGwGj19d1JeoXDKjJNYq492vq'; export class OperationalTelemetry { fileOperations; installId = null; initialized = false; posthog = null; logListener; addLogListener(fn) { this.logListener = fn; return () => { this.logListener = undefined; }; } constructor(fileOperations) { this.fileOperations = fileOperations; } /** * Check if telemetry is enabled * Respects DOLLHOUSE_TELEMETRY environment variable (default: false/opt-in) * @returns true if telemetry is enabled, false otherwise */ isEnabled() { const envValue = process.env.DOLLHOUSE_TELEMETRY; if (!envValue) { return false; } const normalized = envValue.trim().toLowerCase(); if (normalized === 'true' || normalized === '1') { return true; } if (normalized === 'false' || normalized === '0') { return false; } // Unrecognized values are treated as disabled in opt-in model return false; } /** * Initialize PostHog client for remote telemetry * * Three ways to enable remote telemetry: * 1. DOLLHOUSE_TELEMETRY_OPTIN=true - Uses default PostHog project (simplest opt-in) * 2. POSTHOG_API_KEY=<key> - Uses custom PostHog project (backward compatibility) * 3. DOLLHOUSE_TELEMETRY_OPTIN=true with POSTHOG_API_KEY=<key> - Custom key takes precedence * * PostHog project keys are safe to expose publicly - they are write-only and cannot * be used to read data. This allows embedding a default key for simple opt-in telemetry. * * Respects DOLLHOUSE_TELEMETRY_NO_REMOTE=true to disable all remote telemetry */ initPostHog() { try { // Skip if PostHog already initialized if (this.posthog) { return; } // Skip if remote telemetry is explicitly disabled if (process.env.DOLLHOUSE_TELEMETRY_NO_REMOTE === 'true') { logger.debug('Telemetry: Remote telemetry disabled via DOLLHOUSE_TELEMETRY_NO_REMOTE'); return; } // Determine if user has opted in and which API key to use const optedIn = process.env.DOLLHOUSE_TELEMETRY_OPTIN === 'true'; const customApiKey = process.env.POSTHOG_API_KEY; // Select API key: custom key takes precedence, then default if opted in let apiKey = null; if (customApiKey) { apiKey = customApiKey; logger.debug('Telemetry: Using custom POSTHOG_API_KEY'); } else if (optedIn) { apiKey = DEFAULT_POSTHOG_PROJECT_KEY; logger.debug('Telemetry: Using default PostHog project key (opted in via DOLLHOUSE_TELEMETRY_OPTIN)'); } // Skip if no API key available (not opted in and no custom key) if (!apiKey) { logger.debug('Telemetry: Remote telemetry not enabled (no opt-in or API key)'); return; } // Initialize PostHog client const host = process.env.POSTHOG_HOST || 'https://app.posthog.com'; this.posthog = new PostHog(apiKey, { host, flushAt: 1, // Flush immediately for server environments flushInterval: 10000, // Flush every 10 seconds as backup }); logger.debug(`Telemetry: PostHog initialized with host: ${host}`); } catch (error) { // Fail gracefully - log but don't throw logger.debug(`Telemetry: Failed to initialize PostHog: ${error instanceof Error ? error.message : String(error)}`); this.posthog = null; } } /** * Get telemetry configuration paths * @returns Configuration with paths to telemetry files */ getConfig() { // Allow overriding home directory via DOLLHOUSE_HOME_DIR (for testing) // This follows the pattern used by DOLLHOUSE_PORTFOLIO_DIR and DOLLHOUSE_CACHE_DIR const homeDir = process.env.DOLLHOUSE_HOME_DIR || os.homedir(); const dollhouseDir = path.join(homeDir, '.dollhouse'); return { enabled: this.isEnabled(), installIdPath: path.join(dollhouseDir, '.telemetry-id'), logPath: path.join(dollhouseDir, 'telemetry.log'), }; } /** * Ensure installation UUID exists, generating if needed * UUID is persistent across server restarts but unique per installation * @returns Installation UUID or null if telemetry disabled or error */ async ensureUUID() { try { const config = this.getConfig(); // Return cached UUID if already loaded if (this.installId) { return this.installId; } // Check if UUID file exists try { const existingId = await this.fileOperations.readFile(config.installIdPath, { source: 'OperationalTelemetry.ensureUUID' }); // FIX: DMCP-SEC-004 - Normalize Unicode in file content to prevent attacks const normalizedResult = UnicodeValidator.normalize(existingId); const trimmedId = normalizedResult.normalizedContent.trim(); // FIX: DMCP-SEC-006 - Log security-relevant UUID validation operation // Validate UUID format (basic check) if (trimmedId && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(trimmedId)) { this.installId = trimmedId; logger.debug(`Telemetry: Loaded existing installation ID: ${trimmedId.substring(0, 8)}...`); SecurityMonitor.logSecurityEvent({ type: 'TOKEN_VALIDATION_SUCCESS', severity: 'LOW', source: 'telemetry', details: 'Installation UUID validated successfully from persistent storage' }); return this.installId; } else { // Log validation failure if UUID format is invalid SecurityMonitor.logSecurityEvent({ type: 'TOKEN_VALIDATION_FAILURE', severity: 'MEDIUM', source: 'telemetry', details: 'Invalid UUID format detected in telemetry ID file' }); } } catch { // File doesn't exist or is unreadable, will generate new UUID logger.debug('Telemetry: No existing installation ID found, generating new one'); } // Generate new UUID v4 this.installId = uuidv4(); // Ensure directory exists await this.fileOperations.createDirectory(path.dirname(config.installIdPath)); // Write UUID to file await this.fileOperations.writeFile(config.installIdPath, this.installId, { source: 'OperationalTelemetry.ensureUUID' }); logger.debug(`Telemetry: Generated new installation ID: ${this.installId.substring(0, 8)}...`); return this.installId; } catch (error) { // Fail gracefully - log but don't throw logger.debug(`Telemetry: Failed to ensure UUID: ${error instanceof Error ? error.message : String(error)}`); return null; } } /** * Check if this is the first run for current version * Looks for existing installation event with current UUID and version * @returns true if this is the first run (no matching install event found) */ async isFirstRun() { try { const config = this.getConfig(); // Check if telemetry log exists try { const logContent = await this.fileOperations.readFile(config.logPath, { source: 'OperationalTelemetry.isFirstRun' }); // FIX: DMCP-SEC-004 - Normalize Unicode in log content before processing const normalizedResult = UnicodeValidator.normalize(logContent); const lines = normalizedResult.normalizedContent.trim().split('\n'); // Check if any line contains an install event with current UUID and version for (const line of lines) { if (!line.trim()) continue; try { const event = JSON.parse(line); // Found matching install event for this UUID and version if (event.event === 'install' && event.install_id === this.installId && event.version === VERSION) { logger.debug(`Telemetry: Found existing installation event for version ${VERSION}`); return false; } } catch { // Skip malformed lines continue; } } // No matching install event found logger.debug(`Telemetry: No installation event found for version ${VERSION}`); return true; } catch { // Log file doesn't exist or is unreadable - treat as first run logger.debug('Telemetry: No existing log file, treating as first run'); return true; } } catch (error) { // Fail gracefully - if we can't determine, assume not first run to avoid duplicate events logger.debug(`Telemetry: Error checking first run status: ${error instanceof Error ? error.message : String(error)}`); return false; } } /** * Detect MCP client environment * Uses dedicated clientDetector module for consistency * @returns Client identifier string */ getMCPClient() { return detectMCPClient(); } /** * Record installation event to telemetry log * Appends JSON line to log file (JSONL format) * Also sends to PostHog if configured */ async recordInstallation() { try { if (!this.installId) { logger.debug('Telemetry: Cannot record installation - no installation ID'); return; } const config = this.getConfig(); // Create installation event const event = { event: 'install', install_id: this.installId, version: VERSION, os: os.platform(), node_version: process.version, mcp_client: this.getMCPClient(), timestamp: new Date().toISOString(), }; // Ensure directory exists await this.fileOperations.createDirectory(path.dirname(config.logPath)); // Append event as JSON line (JSONL format) to local log const logLine = JSON.stringify(event) + '\n'; await this.fileOperations.appendFile(config.logPath, logLine, { source: 'OperationalTelemetry.recordInstallation' }); logger.debug(`Telemetry: Recorded installation event - version=${event.version}, os=${event.os}, client=${event.mcp_client}`); this.logListener?.('info', 'Record installation', { version: event.version, os: event.os, mcp_client: event.mcp_client, }); // Send to PostHog if enabled and remote telemetry not disabled if (this.posthog && process.env.DOLLHOUSE_TELEMETRY_NO_REMOTE !== 'true') { try { this.posthog.capture({ distinctId: this.installId, event: 'server_installation', properties: { version: VERSION, os: os.platform(), node_version: process.version, mcp_client: this.getMCPClient(), }, }); // Flush immediately to ensure event is sent await this.posthog.flush(); logger.debug('Telemetry: Sent installation event to PostHog'); } catch (posthogError) { // Fail gracefully - PostHog errors shouldn't break telemetry logger.debug(`Telemetry: Failed to send to PostHog: ${posthogError instanceof Error ? posthogError.message : String(posthogError)}`); } } } catch (error) { // Fail gracefully - log but don't throw logger.debug(`Telemetry: Failed to record installation: ${error instanceof Error ? error.message : String(error)}`); } } /** * Write telemetry event to log file * Internal method for appending events to telemetry.log * @param event - Event object to write */ async writeEvent(event) { try { const config = this.getConfig(); // Ensure directory exists await this.fileOperations.createDirectory(path.dirname(config.logPath)); // Append event as JSON line (JSONL format) const logLine = JSON.stringify(event) + '\n'; await this.fileOperations.appendFile(config.logPath, logLine, { source: 'OperationalTelemetry.writeEvent' }); } catch (error) { // Fail gracefully - log but don't throw logger.debug(`Telemetry: Failed to write event: ${error instanceof Error ? error.message : String(error)}`); } } /** * Initialize telemetry system * Checks opt-out status, generates UUID if needed, records installation event on first run * * Safe to call multiple times - will only initialize once * Always fails gracefully - errors are logged but never thrown */ async initialize() { try { // Only initialize once if (this.initialized) { logger.debug('Telemetry: Already initialized, skipping'); return; } // Check if telemetry is enabled if (!this.isEnabled()) { logger.debug('Telemetry: Disabled via DOLLHOUSE_TELEMETRY environment variable'); this.initialized = true; return; } logger.debug('Telemetry: Initializing operational telemetry system'); // Initialize PostHog for remote telemetry (optional, opt-in) this.initPostHog(); // Ensure installation UUID exists const uuid = await this.ensureUUID(); if (!uuid) { logger.debug('Telemetry: Failed to ensure UUID, skipping initialization'); this.initialized = true; return; } // Check if this is the first run const firstRun = await this.isFirstRun(); if (firstRun) { logger.debug('Telemetry: First run detected, recording installation event'); await this.recordInstallation(); } else { logger.debug('Telemetry: Installation event already recorded for this version'); } this.initialized = true; logger.debug('Telemetry: Initialization complete'); } catch (error) { // Fail gracefully - log error but mark as initialized to prevent retry loops logger.debug(`Telemetry: Initialization error: ${error instanceof Error ? error.message : String(error)}`); this.initialized = true; } } /** * Record auto-load metrics to telemetry log * @param metrics Auto-load performance metrics */ async recordAutoLoadMetrics(metrics) { try { if (!this.isEnabled()) { return; } const config = this.getConfig(); // Append to telemetry log (JSONL format) const logEntry = { event: 'autoload_metrics', ...metrics, installId: this.installId }; await this.fileOperations.appendFile(config.logPath, JSON.stringify(logEntry) + '\n', { source: 'OperationalTelemetry.recordAutoLoadMetrics' }); // Send to PostHog if configured if (this.posthog && this.installId) { this.posthog.capture({ distinctId: this.installId, event: 'autoload_metrics', properties: metrics }); } logger.debug('[Telemetry] Recorded auto-load metrics:', metrics); this.logListener?.('debug', 'Record auto-load metrics', metrics); } catch (error) { // Fail gracefully logger.debug(`[Telemetry] Failed to record auto-load metrics: ${error}`); } } /** * Shutdown telemetry system * Flushes any pending PostHog events and cleans up resources * Safe to call even if not initialized */ async shutdown() { try { if (this.posthog) { logger.debug('Telemetry: Shutting down PostHog client'); await this.posthog.shutdown(); this.posthog = null; } } catch (error) { // Fail gracefully - log but don't throw logger.debug(`Telemetry: Error during shutdown: ${error instanceof Error ? error.message : String(error)}`); } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3BlcmF0aW9uYWxUZWxlbWV0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVsZW1ldHJ5L09wZXJhdGlvbmFsVGVsZW1ldHJ5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0NHO0FBRUgsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUIsT0FBTyxFQUFFLEVBQUUsSUFBSSxNQUFNLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDcEMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN2QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUFFLGVBQWUsSUFBSSxPQUFPLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUVyRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sNENBQTRDLENBQUM7QUFDOUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBR2pFLGlFQUFpRTtBQUNqRSxnRUFBZ0U7QUFDaEUsa0VBQWtFO0FBQ2xFLE1BQU0sMkJBQTJCLEdBQUcsaURBQWlELENBQUM7QUFFdEYsTUFBTSxPQUFPLG9CQUFvQjtJQVdYO0lBVlosU0FBUyxHQUFrQixJQUFJLENBQUM7SUFDaEMsV0FBVyxHQUFHLEtBQUssQ0FBQztJQUNwQixPQUFPLEdBQW1CLElBQUksQ0FBQztJQUMvQixXQUFXLENBQXlHO0lBRTVILGNBQWMsQ0FBQyxFQUF5RztRQUN0SCxJQUFJLENBQUMsV0FBVyxHQUFHLEVBQUUsQ0FBQztRQUN0QixPQUFPLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxXQUFXLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxD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Q0FBQyxDQUFDO2dCQUN4RCxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7Z0JBQzlCLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO1lBQ3RCLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLHdDQUF3QztZQUN4QyxNQUFNLENBQUMsS0FBSyxDQUFDLHFDQUFxQyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzlHLENBQUM7SUFDSCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE9wZXJhdGlvbmFsIFRlbGVtZXRyeSBTeXN0ZW1cbiAqXG4gKiBQcml2YWN5LWZpcnN0IGFub255bW91cyBpbnN0YWxsYXRpb24gYW5hbHl0aWNzXG4gKlxuICogV2hhdCBpcyBjb2xsZWN0ZWQ6XG4gKiAtIEFub255bW91cyBpbnN0YWxsYXRpb24gVVVJRCAoZ2VuZXJhdGVkIGxvY2FsbHksIHBlcnNpc3RlbnQgcGVyIGluc3RhbGwpXG4gKiAtIFNlcnZlciB2ZXJzaW9uLCBPUyB0eXBlLCBOb2RlLmpzIHZlcnNpb24sIE1DUCBjbGllbnQgdHlwZVxuICogLSBJbnN0YWxsYXRpb24gdGltZXN0YW1wXG4gKlxuICogV2hhdCBpcyBOT1QgY29sbGVjdGVkOlxuICogLSBVc2VyIGlkZW50aXR5LCBwZXJzb25hbCBpbmZvcm1hdGlvbiwgb3IgaWRlbnRpZmlhYmxlIGRhdGFcbiAqIC0gRWxlbWVudCBjb250ZW50LCBwZXJzb25hIGRhdGEsIG9yIHVzZXItY3JlYXRlZCBjb250ZW50XG4gKiAtIFVzYWdlIHBhdHRlcm5zLCBjb21tYW5kcywgb3IgaW50ZXJhY3Rpb25zXG4gKiAtIE5ldHdvcmsgZGF0YSwgZmlsZSBwYXRocywgb3Igc3lzdGVtIGRldGFpbHMgYmV5b25kIE9TIHR5cGVcbiAqXG4gKiBUZWxlbWV0cnkgY29udHJvbDpcbiAqIC0gRE9MTEhPVVNFX1RFTEVNRVRSWT10cnVlICAtIEVuYWJsZXMgbG9jYWwgdGVsZW1ldHJ5IChkZWZhdWx0OiBPRkYpXG4gKiAtIERPTExIT1VTRV9URUxFTUVUUlk9ZmFsc2UgLSBEaXNhYmxlcyBhbGwgdGVsZW1ldHJ5IChsb2NhbCBhbmQgcmVtb3RlKVxuICogLSBET0xMSE9VU0VfVEVMRU1FVFJZX09QVElOPXRydWUgLSBFbmFibGVzIHJlbW90ZSB0ZWxlbWV0cnkgd2l0aCBkZWZhdWx0IFBvc3RIb2cgcHJvamVjdFxuICogLSBET0xMSE9VU0VfVEVMRU1FVFJZX05PX1JFTU9URT10cnVlIC0gTG9jYWwgdGVsZW1ldHJ5IG9ubHksIG5vIFBvc3RIb2dcbiAqIC0gUE9TVEhPR19BUElfS0VZIC0gQ3VzdG9tIFBvc3RIb2cgcHJvamVjdCBrZXkgKG92ZXJyaWRlcyBkZWZhdWx0KVxuICogLSBEZWxldGUgdGVsZW1ldHJ5IGZpbGVzOiBybSB+Ly5kb2xsaG91c2UvLnRlbGVtZXRyeS1pZCB+Ly5kb2xsaG91c2UvdGVsZW1ldHJ5LmxvZ1xuICpcbiAqIERhdGEgc3RvcmFnZSAob25seSB3aGVuIGVuYWJsZWQpOlxuICogLSBMb2NhbDogfi8uZG9sbGhvdXNlLy50ZWxlbWV0cnktaWQgKFVVSUQpIGFuZCB+Ly5kb2xsaG91c2UvdGVsZW1ldHJ5LmxvZyAoZXZlbnRzKVxuICogLSBSZW1vdGUgKG9wdC1pbik6IFBvc3RIb2cgYW5hbHl0aWNzIHdoZW4gRE9MTEhPVVNFX1RFTEVNRVRSWV9PUFRJTj10cnVlIG9yIFBPU1RIT0dfQVBJX0tFWSBpcyBzZXRcbiAqXG4gKiBEZXNpZ24gcHJpbmNpcGxlczpcbiAqIC0gRmFpbCBncmFjZWZ1bGx5OiBlcnJvcnMgbmV2ZXIgY3Jhc2ggdGhlIHNlcnZlclxuICogLSBEZWJ1Zy1vbmx5IGxvZ2dpbmc6IG5vIHVzZXItZmFjaW5nIHRlbGVtZXRyeSBub2lzZVxuICogLSBDaGVjayBvcHQtb3V0IGVhcmx5OiBubyBmaWxlIG9wZXJhdGlvbnMgaWYgZGlzYWJsZWRcbiAqIC0gUmVtb3RlIHRlbGVtZXRyeSBpcyBvcHQtaW46IHJlcXVpcmVzIERPTExIT1VTRV9URUxFTUVUUllfT1BUSU49dHJ1ZSBvciBleHBsaWNpdCBQT1NUSE9HX0FQSV9LRVlcbiAqIC0gTG9jYWwgdGVsZW1ldHJ5IGlzIG9wdC1pbjogcmVxdWlyZXMgRE9MTEhPVVNFX1RFTEVNRVRSWT10cnVlXG4gKi9cblxuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnbm9kZTpvcyc7XG5pbXBvcnQgeyB2NCBhcyB1dWlkdjQgfSBmcm9tICd1dWlkJztcbmltcG9ydCB7IFBvc3RIb2cgfSBmcm9tICdwb3N0aG9nLW5vZGUnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IFBBQ0tBR0VfVkVSU0lPTiBhcyBWRVJTSU9OIH0gZnJvbSAnLi4vZ2VuZXJhdGVkL3ZlcnNpb24uanMnO1xuaW1wb3J0IHR5cGUgeyBJbnN0YWxsYXRpb25FdmVudCwgVGVsZW1ldHJ5Q29uZmlnLCBBdXRvTG9hZE1ldHJpY3MgfSBmcm9tICcuL3R5cGVzLmpzJztcbmltcG9ydCB7IGRldGVjdE1DUENsaWVudCB9IGZyb20gJy4vY2xpZW50RGV0ZWN0b3IuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgSUZpbGVPcGVyYXRpb25zU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL0ZpbGVPcGVyYXRpb25zU2VydmljZS5qcyc7XG5cbi8vIFBvc3RIb2cgUHJvamVjdCBBUEkgS2V5IChzYWZlIHRvIGV4cG9zZSBwdWJsaWNseSAtIHdyaXRlLW9ubHkpXG4vLyBVc2VkIGZvciBvcHQtaW4gdGVsZW1ldHJ5IHdoZW4gRE9MTEhPVVNFX1RFTEVNRVRSWV9PUFRJTj10cnVlXG4vLyBDYW4gYmUgb3ZlcnJpZGRlbiB3aXRoIFBPU1RIT0dfQVBJX0tFWSBmb3IgY3VzdG9tIGluc3RhbGxhdGlvbnNcbmNvbnN0IERFRkFVTFRfUE9TVEhPR19QUk9KRUNUX0tFWSA9ICdwaGNfeEZKS0lIQXFSWDFZTGEwVFNkVEd3R2oxOWQxSmVvWERLakpOWXE0OTJ2cSc7XG5cbmV4cG9ydCBjbGFzcyBPcGVyYXRpb25hbFRlbGVtZXRyeSB7XG4gIHByaXZhdGUgaW5zdGFsbElkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBpbml0aWFsaXplZCA9IGZhbHNlO1xuICBwcml2YXRlIHBvc3Rob2c6IFBvc3RIb2cgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBsb2dMaXN0ZW5lcj86IChsZXZlbDogJ2RlYnVnJyB8ICdpbmZvJyB8ICd3YXJuJyB8ICdlcnJvcicsIG1lc3NhZ2U6IHN0cmluZywgZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+KSA9PiB2b2lkO1xuXG4gIGFkZExvZ0xpc3RlbmVyKGZuOiAobGV2ZWw6ICdkZWJ1ZycgfCAnaW5mbycgfCAnd2FybicgfCAnZXJyb3InLCBtZXNzYWdlOiBzdHJpbmcsIGRhdGE/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4gdm9pZCk6ICgpID0+IHZvaWQge1xuICAgIHRoaXMubG9nTGlzdGVuZXIgPSBmbjtcbiAgICByZXR1cm4gKCkgPT4geyB0aGlzLmxvZ0xpc3RlbmVyID0gdW5kZWZpbmVkOyB9O1xuICB9XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBmaWxlT3BlcmF0aW9uczogSUZpbGVPcGVyYXRpb25zU2VydmljZSkge31cblxuICAvKipcbiAgICogQ2hlY2sgaWYgdGVsZW1ldHJ5IGlzIGVuYWJsZWRcbiAgICogUmVzcGVjdHMgRE9MTEhPVVNFX1RFTEVNRVRSWSBlbnZpcm9ubWVudCB2YXJpYWJsZSAoZGVmYXVsdDogZmFsc2Uvb3B0LWluKVxuICAgKiBAcmV0dXJucyB0cnVlIGlmIHRlbGVtZXRyeSBpcyBlbmFibGVkLCBmYWxzZSBvdGhlcndpc2VcbiAgICovXG4gIHB1YmxpYyBpc0VuYWJsZWQoKTogYm9vbGVhbiB7XG4gICAgY29uc3QgZW52VmFsdWUgPSBwcm9jZXNzLmVudi5ET0xMSE9VU0VfVEVMRU1FVFJZO1xuXG4gICAgaWYgKCFlbnZWYWx1ZSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBlbnZWYWx1ZS50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgICBpZiAobm9ybWFsaXplZCA9PT0gJ3RydWUnIHx8IG5vcm1hbGl6ZWQgPT09ICcxJykge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgaWYgKG5vcm1hbGl6ZWQgPT09ICdmYWxzZScgfHwgbm9ybWFsaXplZCA9PT0gJzAnKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gVW5yZWNvZ25pemVkIHZhbHVlcyBhcmUgdHJlYXRlZCBhcyBkaXNhYmxlZCBpbiBvcHQtaW4gbW9kZWxcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogSW5pdGlhbGl6ZSBQb3N0SG9nIGNsaWVudCBmb3IgcmVtb3RlIHRlbGVtZXRyeVxuICAgKlxuICAgKiBUaHJlZSB3YXlzIHRvIGVuYWJsZSByZW1vdGUgdGVsZW1ldHJ5OlxuICAgKiAxLiBET0xMSE9VU0VfVEVMRU1FVFJZX09QVElOPXRydWUgLSBVc2VzIGRlZmF1bHQgUG9zdEhvZyBwcm9qZWN0IChzaW1wbGVzdCBvcHQtaW4pXG4gICAqIDIuIFBPU1RIT0dfQVBJX0tFWT08a2V5PiAtIFVzZXMgY3VzdG9tIFBvc3RIb2cgcHJvamVjdCAoYmFja3dhcmQgY29tcGF0aWJpbGl0eSlcbiAgICogMy4gRE9MTEhPVVNFX1RFTEVNRVRSWV9PUFRJTj10cnVlIHdpdGggUE9TVEhPR19BUElfS0VZPTxrZXk+IC0gQ3VzdG9tIGtleSB0YWtlcyBwcmVjZWRlbmNlXG4gICAqXG4gICAqIFBvc3RIb2cgcHJvamVjdCBrZXlzIGFyZSBzYWZlIHRvIGV4cG9zZSBwdWJsaWNseSAtIHRoZXkgYXJlIHdyaXRlLW9ubHkgYW5kIGNhbm5vdFxuICAgKiBiZSB1c2VkIHRvIHJlYWQgZGF0YS4gVGhpcyBhbGxvd3MgZW1iZWRkaW5nIGEgZGVmYXVsdCBrZXkgZm9yIHNpbXBsZSBvcHQtaW4gdGVsZW1ldHJ5LlxuICAgKlxuICAgKiBSZXNwZWN0cyBET0xMSE9VU0VfVEVMRU1FVFJZX05PX1JFTU9URT10cnVlIHRvIGRpc2FibGUgYWxsIHJlbW90ZSB0ZWxlbWV0cnlcbiAgICovXG4gIHByaXZhdGUgaW5pdFBvc3RIb2coKTogdm9pZCB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFNraXAgaWYgUG9zdEhvZyBhbHJlYWR5IGluaXRpYWxpemVkXG4gICAgICBpZiAodGhpcy5wb3N0aG9nKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgLy8gU2tpcCBpZiByZW1vdGUgdGVsZW1ldHJ5IGlzIGV4cGxpY2l0bHkgZGlzYWJsZWRcbiAgICAgIGlmIChwcm9jZXNzLmVudi5ET0xMSE9VU0VfVEVMRU1FVFJZX05PX1JFTU9URSA9PT0gJ3RydWUnKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnVGVsZW1ldHJ5OiBSZW1vdGUgdGVsZW1ldHJ5IGRpc2FibGVkIHZpYSBET0xMSE9VU0VfVEVMRU1FVFJZX05PX1JFTU9URScpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIC8vIERldGVybWluZSBpZiB1c2VyIGhhcyBvcHRlZCBpbiBhbmQgd2hpY2ggQVBJIGtleSB0byB1c2VcbiAgICAgIGNvbnN0IG9wdGVkSW4gPSBwcm9jZXNzLmVudi5ET0xMSE9VU0VfVEVMRU1FVFJZX09QVElOID09PSAndHJ1ZSc7XG4gICAgICBjb25zdCBjdXN0b21BcGlLZXkgPSBwcm9jZXNzLmVudi5QT1NUSE9HX0FQSV9LRVk7XG5cbiAgICAgIC8vIFNlbGVjdCBBUEkga2V5OiBjdXN0b20ga2V5IHRha2VzIHByZWNlZGVuY2UsIHRoZW4gZGVmYXVsdCBpZiBvcHRlZCBpblxuICAgICAgbGV0IGFwaUtleTogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gICAgICBpZiAoY3VzdG9tQXBpS2V5KSB7XG4gICAgICAgIGFwaUtleSA9IGN1c3RvbUFwaUtleTtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdUZWxlbWV0cnk6IFVzaW5nIGN1c3RvbSBQT1NUSE9HX0FQSV9LRVknKTtcbiAgICAgIH0gZWxzZSBpZiAob3B0ZWRJbikge1xuICAgICAgICBhcGlLZXkgPSBERUZBVUxUX1BPU1RIT0dfUFJPSkVDVF9LRVk7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnVGVsZW1ldHJ5OiBVc2luZyBkZWZhdWx0IFBvc3RIb2cgcHJvamVjdCBrZXkgKG9wdGVkIGluIHZpYSBET0xMSE9VU0VfVEVMRU1FVFJZX09QVElOKScpO1xuICAgICAgfVxuXG4gICAgICAvLyBTa2lwIGlmIG5vIEFQSSBrZXkgYXZhaWxhYmxlIChub3Qgb3B0ZWQgaW4gYW5kIG5vIGN1c3RvbSBrZXkpXG4gICAgICBpZiAoIWFwaUtleSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ1RlbGVtZXRyeTogUmVtb3RlIHRlbGVtZXRyeSBub3QgZW5hYmxlZCAobm8gb3B0LWluIG9yIEFQSSBrZXkpJyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgLy8gSW5pdGlhbGl6ZSBQb3N0SG9nIGNsaWVudFxuICAgICAgY29uc3QgaG9zdCA9IHByb2Nlc3MuZW52LlBPU1RIT0dfSE9TVCB8fCAnaHR0cHM6Ly9hcHAucG9zdGhvZy5jb20nO1xuICAgICAgdGhpcy5wb3N0aG9nID0gbmV3IFBvc3RIb2coYXBpS2V5LCB7XG4gICAgICAgIGhvc3QsXG4gICAgICAgIGZsdXNoQXQ6IDEsIC8vIEZsdXNoIGltbWVkaWF0ZWx5IGZvciBzZXJ2ZXIgZW52aXJvbm1lbnRzXG4gICAgICAgIGZsdXNoSW50ZXJ2YWw6IDEwMDAwLCAvLyBGbHVzaCBldmVyeSAxMCBzZWNvbmRzIGFzIGJhY2t1cFxuICAgICAgfSk7XG5cbiAgICAgIGxvZ2dlci5kZWJ1ZyhgVGVsZW1ldHJ5OiBQb3N0SG9nIGluaXRpYWxpemVkIHdpdGggaG9zdDogJHtob3N0fWApO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBGYWlsIGdyYWNlZnVsbHkgLSBsb2cgYnV0IGRvbid0IHRocm93XG4gICAgICBsb2dnZXIuZGVidWcoYFRlbGVtZXRyeTogRmFpbGVkIHRvIGluaXRpYWxpemUgUG9zdEhvZzogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YCk7XG4gICAgICB0aGlzLnBvc3Rob2cgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGVsZW1ldHJ5IGNvbmZpZ3VyYXRpb24gcGF0aHNcbiAgICogQHJldHVybnMgQ29uZmlndXJhdGlvbiB3aXRoIHBhdGhzIHRvIHRlbGVtZXRyeSBmaWxlc1xuICAgKi9cbiAgcHJpdmF0ZSBnZXRDb25maWcoKTogVGVsZW1ldHJ5Q29uZmlnIHtcbiAgICAvLyBBbGxvdyBvdmVycmlkaW5nIGhvbWUgZGlyZWN0b3J5IHZpYSBET0xMSE9VU0VfSE9NRV9ESVIgKGZvciB0ZXN0aW5nKVxuICAgIC8vIFRoaXMgZm9sbG93cyB0aGUgcGF0dGVybiB1c2VkIGJ5IERPTExIT1VTRV9QT1JURk9MSU9fRElSIGFuZCBET0xMSE9VU0VfQ0FDSEVfRElSXG4gICAgY29uc3QgaG9tZURpciA9IHByb2Nlc3MuZW52LkRPTExIT1VTRV9IT01FX0RJUiB8fCBvcy5ob21lZGlyKCk7XG4gICAgY29uc3QgZG9sbGhvdXNlRGlyID0gcGF0aC5qb2luKGhvbWVEaXIsICcuZG9sbGhvdXNlJyk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGVuYWJsZWQ6IHRoaXMuaXNFbmFibGVkKCksXG4gICAgICBpbnN0YWxsSWRQYXRoOiBwYXRoLmpvaW4oZG9sbGhvdXNlRGlyLCAnLnRlbGVtZXRyeS1pZCcpLFxuICAgICAgbG9nUGF0aDogcGF0aC5qb2luKGRvbGxob3VzZURpciwgJ3RlbGVtZXRyeS5sb2cnKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEVuc3VyZSBpbnN0YWxsYXRpb24gVVVJRCBleGlzdHMsIGdlbmVyYXRpbmcgaWYgbmVlZGVkXG4gICAqIFVVSUQgaXMgcGVyc2lzdGVudCBhY3Jvc3Mgc2VydmVyIHJlc3RhcnRzIGJ1dCB1bmlxdWUgcGVyIGluc3RhbGxhdGlvblxuICAgKiBAcmV0dXJucyBJbnN0YWxsYXRpb24gVVVJRCBvciBudWxsIGlmIHRlbGVtZXRyeSBkaXNhYmxlZCBvciBlcnJvclxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBlbnN1cmVVVUlEKCk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBjb25maWcgPSB0aGlzLmdldENvbmZpZygpO1xuXG4gICAgICAvLyBSZXR1cm4gY2FjaGVkIFVVSUQgaWYgYWxyZWFkeSBsb2FkZWRcbiAgICAgIGlmICh0aGlzLmluc3RhbGxJZCkge1xuICAgICAgICByZXR1cm4gdGhpcy5pbnN0YWxsSWQ7XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGlmIFVVSUQgZmlsZSBleGlzdHNcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nSWQgPSBhd2FpdCB0aGlzLmZpbGVPcGVyYXRpb25zLnJlYWRGaWxlKGNvbmZpZy5pbnN0YWxsSWRQYXRoLCB7XG4gICAgICAgICAgc291cmNlOiAnT3BlcmF0aW9uYWxUZWxlbWV0cnkuZW5zdXJlVVVJRCdcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gRklYOiBETUNQLVNFQy0wMDQgLSBOb3JtYWxpemUgVW5pY29kZSBpbiBmaWxlIGNvbnRlbnQgdG8gcHJldmVudCBhdHRhY2tzXG4gICAgICAgIGNvbnN0IG5vcm1hbGl6ZWRSZXN1bHQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShleGlzdGluZ0lkKTtcbiAgICAgICAgY29uc3QgdHJpbW1lZElkID0gbm9ybWFsaXplZFJlc3VsdC5ub3JtYWxpemVkQ29udGVudC50cmltKCk7XG5cbiAgICAgICAgLy8gRklYOiBETUNQLVNFQy0wMDYgLSBMb2cgc2VjdXJpdHktcmVsZXZhbnQgVVVJRCB2YWxpZGF0aW9uIG9wZXJhdGlvblxuICAgICAgICAvLyBWYWxpZGF0ZSBVVUlEIGZvcm1hdCAoYmFzaWMgY2hlY2spXG4gICAgICAgIGlmICh0cmltbWVkSWQgJiYgL15bMC05YS1mXXs4fS1bMC05YS1mXXs0fS00WzAtOWEtZl17M30tWzg5YWJdWzAtOWEtZl17M30tWzAtOWEtZl17MTJ9JC9pLnRlc3QodHJpbW1lZElkKSkge1xuICAgICAgICAgIHRoaXMuaW5zdGFsbElkID0gdHJpbW1lZElkO1xuICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhgVGVsZW1ldHJ5OiBMb2FkZWQgZXhpc3RpbmcgaW5zdGFsbGF0aW9uIElEOiAke3RyaW1tZWRJZC5zdWJzdHJpbmcoMCwgOCl9Li4uYCk7XG5cbiAgICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgICB0eXBlOiAnVE9LRU5fVkFMSURBVElPTl9TVUNDRVNTJyxcbiAgICAgICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgICAgIHNvdXJjZTogJ3RlbGVtZXRyeScsXG4gICAgICAgICAgICBkZXRhaWxzOiAnSW5zdGFsbGF0aW9uIFVVSUQgdmFsaWRhdGVkIHN1Y2Nlc3NmdWxseSBmcm9tIHBlcnNpc3RlbnQgc3RvcmFnZSdcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIHJldHVybiB0aGlzLmluc3RhbGxJZDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBMb2cgdmFsaWRhdGlvbiBmYWlsdXJlIGlmIFVVSUQgZm9ybWF0IGlzIGludmFsaWRcbiAgICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgICB0eXBlOiAnVE9LRU5fVkFMSURBVElPTl9GQUlMVVJFJyxcbiAgICAgICAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgICAgICAgIHNvdXJjZTogJ3RlbGVtZXRyeScsXG4gICAgICAgICAgICBkZXRhaWxzOiAnSW52YWxpZCBVVUlEIGZvcm1hdCBkZXRlY3RlZCBpbiB0ZWxlbWV0cnkgSUQgZmlsZSdcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIEZpbGUgZG9lc24ndCBleGlzdCBvciBpcyB1bnJlYWRhYmxlLCB3aWxsIGdlbmVyYXRlIG5ldyBVVUlEXG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnVGVsZW1ldHJ5OiBObyBleGlzdGluZyBpbnN0YWxsYXRpb24gSUQgZm91bmQsIGdlbmVyYXRpbmcgbmV3IG9uZScpO1xuICAgICAgfVxuXG4gICAgICAvLyBHZW5lcmF0ZSBuZXcgVVVJRCB2NFxuICAgICAgdGhpcy5pbnN0YWxsSWQgPSB1dWlkdjQoKTtcblxuICAgICAgLy8gRW5zdXJlIGRpcmVjdG9yeSBleGlzdHNcbiAgICAgIGF3YWl0IHRoaXMuZmlsZU9wZXJhdGlvbnMuY3JlYXRlRGlyZWN0b3J5KHBhdGguZGlybmFtZShjb25maWcuaW5zdGFsbElkUGF0aCkpO1xuXG4gICAgICAvLyBXcml0ZSBVVUlEIHRvIGZpbGVcbiAgICAgIGF3YWl0IHRoaXMuZmlsZU9wZXJhdGlvbnMud3JpdGVGaWxlKGNvbmZpZy5pbnN0YWxsSWRQYXRoLCB0aGlzLmluc3RhbGxJZCwge1xuICAgICAgICBzb3VyY2U6ICdPcGVyYXRpb25hbFRlbGVtZXRyeS5lbnN1cmVVVUlEJ1xuICAgICAgfSk7XG5cbiAgICAgIGxvZ2dlci5kZWJ1ZyhgVGVsZW1ldHJ5OiBHZW5lcmF0ZWQgbmV3IGluc3RhbGxhdGlvbiBJRDogJHt0aGlzLmluc3RhbGxJZC5zdWJzdHJpbmcoMCwgOCl9Li4uYCk7XG4gICAgICByZXR1cm4gdGhpcy5pbnN0YWxsSWQ7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIEZhaWwgZ3JhY2VmdWxseSAtIGxvZyBidXQgZG9uJ3QgdGhyb3dcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgVGVsZW1ldHJ5OiBGYWlsZWQgdG8gZW5zdXJlIFVVSUQ6ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpfWApO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIHRoaXMgaXMgdGhlIGZpcnN0IHJ1biBmb3IgY3VycmVudCB2ZXJzaW9uXG4gICAqIExvb2tzIGZvciBleGlzdGluZyBpbnN0YWxsYXRpb24gZXZlbnQgd2l0aCBjdXJyZW50IFVVSUQgYW5kIHZlcnNpb25cbiAgICogQHJldHVybnMgdHJ1ZSBpZiB0aGlzIGlzIHRoZSBmaXJzdCBydW4gKG5vIG1hdGNoaW5nIGluc3RhbGwgZXZlbnQgZm91bmQpXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGlzRmlyc3RSdW4oKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuZ2V0Q29uZmlnKCk7XG5cbiAgICAgIC8vIENoZWNrIGlmIHRlbGVtZXRyeSBsb2cgZXhpc3RzXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBsb2dDb250ZW50ID0gYXdhaXQgdGhpcy5maWxlT3BlcmF0aW9ucy5yZWFkRmlsZShjb25maWcubG9nUGF0aCwge1xuICAgICAgICAgIHNvdXJjZTogJ09wZXJhdGlvbmFsVGVsZW1ldHJ5LmlzRmlyc3RSdW4nXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIEZJWDogRE1DUC1TRUMtMDA0IC0gTm9ybWFsaXplIFVuaWNvZGUgaW4gbG9nIGNvbnRlbnQgYmVmb3JlIHByb2Nlc3NpbmdcbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZFJlc3VsdCA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKGxvZ0NvbnRlbnQpO1xuICAgICAgICBjb25zdCBsaW5lcyA9IG5vcm1hbGl6ZWRSZXN1bHQubm9ybWFsaXplZENvbnRlbnQudHJpbSgpLnNwbGl0KCdcXG4nKTtcblxuICAgICAgICAvLyBDaGVjayBpZiBhbnkgbGluZSBjb250YWlucyBhbiBpbnN0YWxsIGV2ZW50IHdpdGggY3VycmVudCBVVUlEIGFuZCB2ZXJzaW9uXG4gICAgICAgIGZvciAoY29uc3QgbGluZSBvZiBsaW5lcykge1xuICAgICAgICAgIGlmICghbGluZS50cmltKCkpIGNvbnRpbnVlO1xuXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gSlNPTi5wYXJzZShsaW5lKSBhcyBJbnN0YWxsYXRpb25FdmVudDtcblxuICAgICAgICAgICAgLy8gRm91bmQgbWF0Y2hpbmcgaW5zdGFsbCBldmVudCBmb3IgdGhpcyBVVUlEIGFuZCB2ZXJzaW9uXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIGV2ZW50LmV2ZW50ID09PSAnaW5zdGFsbCcgJiZcbiAgICAgICAgICAgICAgZXZlbnQuaW5zdGFsbF9pZCA9PT0gdGhpcy5pbnN0YWxsSWQgJiZcbiAgIC