@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.
441 lines • 58.4 kB
JavaScript
/**
* Activation Store
*
* Persists per-session element activation state to disk.
* Each MCP session (identified by DOLLHOUSE_SESSION_ID) gets its own
* activation file so concurrent sessions maintain independent profiles.
*
* Follows the DangerZoneEnforcer pattern:
* - DI-managed singleton with FileOperationsService
* - initialize() loads from disk (tolerates missing/corrupt files)
* - In-memory state is the hot path; disk writes are fire-and-forget
* - atomicWriteFile (write-to-temp + rename) prevents partial reads
*
* Forward compatibility: The versioned file format (v1) can evolve to
* include userId, orgId, and audit fields for multi-user HTTPS mode
* without breaking existing installations.
*
* @since v2.0.0 - Issue #598
*/
import os from 'os';
import path from 'path';
import fs from 'fs/promises';
import { logger } from '../utils/logger.js';
import { SecurityMonitor } from '../security/securityMonitor.js';
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
import { resolveSessionIdentity, SESSION_ID_PATTERN, } from './sessionIdentity.js';
/** Valid element types that support activation (stored in singular form) */
const ACTIVATABLE_TYPES = new Set(['persona', 'skill', 'agent', 'memory', 'ensemble']);
/**
* Normalize element type to singular form for consistent storage.
* ElementType enum uses plural ('personas', 'skills', etc.) but we
* store in singular form for readability and forward compatibility.
*/
const PLURAL_TO_SINGULAR = {
personas: 'persona',
skills: 'skill',
agents: 'agent',
memories: 'memory',
ensembles: 'ensemble',
};
function normalizeType(elementType) {
const lower = elementType.toLowerCase();
return PLURAL_TO_SINGULAR[lower] ?? lower;
}
function normalizeActivationIdentifier(value) {
return UnicodeValidator.normalize(value).normalizedContent.trim();
}
/**
* Checks whether activation persistence is enabled.
*/
function isPersistenceEnabled() {
const envValue = process.env.DOLLHOUSE_ACTIVATION_PERSISTENCE?.trim().toLowerCase();
if (envValue === 'false' || envValue === '0' || envValue === 'no') {
return false;
}
return true;
}
/** Maximum number of retry attempts for transient disk failures */
const PERSIST_MAX_RETRIES = 2;
/** Delay between retry attempts in milliseconds */
const PERSIST_RETRY_DELAY_MS = 100;
/**
* Per-session activation state persistence.
*
* Persists element activation state to `~/.dollhouse/state/activations-{sessionId}.json`.
* Each MCP session is identified by the `DOLLHOUSE_SESSION_ID` environment variable.
*
* Thread-safety note: Node.js is single-threaded, so Map operations are safe.
* For multi-process deployments (future HTTPS), consider Redis or DB backing.
*
* @example
* ```ts
* // Session ID set via environment:
* // DOLLHOUSE_SESSION_ID=claude-code
*
* const store = new ActivationStore(fileOps);
* await store.initialize(); // loads ~/.dollhouse/state/activations-claude-code.json
*
* store.recordActivation('skill', 'code-reviewer');
* store.recordActivation('persona', 'Creative Dev', 'creative-dev.md');
*
* // On next server start, initialize() restores these activations
* ```
*/
export class ActivationStore {
fileOps;
stateDir;
sessionId;
runtimeSessionId;
persistPath;
enabled;
state;
constructor(fileOps, stateDir) {
this.fileOps = fileOps;
const identity = resolveSessionIdentity();
this.sessionId = identity.sessionId;
this.runtimeSessionId = identity.runtimeSessionId;
this.enabled = isPersistenceEnabled();
this.stateDir = stateDir ?? path.join(os.homedir(), '.dollhouse', 'state');
this.persistPath = path.join(this.stateDir, `activations-${this.sessionId}.json`);
this.state = this.createEmptyState();
this.logResolvedSessionIdentity(identity);
}
/**
* Load persisted activations from disk.
* Call once after construction to restore state from a previous session.
* If the file is missing or corrupt, starts with empty activations.
*/
async initialize() {
if (!this.enabled) {
logger.debug('[ActivationStore] Persistence disabled via DOLLHOUSE_ACTIVATION_PERSISTENCE');
return;
}
try {
const content = await this.fileOps.readFile(this.persistPath);
const data = JSON.parse(content);
if (data.version === 1 && data.activations && typeof data.activations === 'object') {
// Validate and load only known element types
for (const [type, activations] of Object.entries(data.activations)) {
if (ACTIVATABLE_TYPES.has(type) && Array.isArray(activations)) {
this.state.activations[type] = activations.flatMap((a) => {
if (!a || typeof a.name !== 'string')
return [];
const normalizedName = normalizeActivationIdentifier(a.name);
if (!normalizedName)
return [];
const normalizedFilename = typeof a.filename === 'string'
? normalizeActivationIdentifier(a.filename)
: undefined;
return [{
...a,
name: normalizedName,
...(normalizedFilename ? { filename: normalizedFilename } : {}),
}];
});
}
}
const totalCount = this.getTotalActivationCount();
if (totalCount > 0) {
logger.info(`[ActivationStore] Restored ${totalCount} activation(s) for session '${this.sessionId}'`);
SecurityMonitor.logSecurityEvent({
type: 'ELEMENT_ACTIVATED',
severity: 'LOW',
source: 'ActivationStore.initialize',
details: `Restored ${totalCount} activation(s) from disk for session '${this.sessionId}'`,
additionalData: {
sessionId: this.sessionId,
counts: this.getActivationCounts(),
},
});
}
}
}
catch (error) {
if (error.code === 'ENOENT') {
logger.debug(`[ActivationStore] No activation file found for session '${this.sessionId}', starting fresh`);
}
else {
logger.warn(`[ActivationStore] Failed to load activation file for session '${this.sessionId}', starting fresh`, { error });
SecurityMonitor.logSecurityEvent({
type: 'ELEMENT_ACTIVATED',
severity: 'MEDIUM',
source: 'ActivationStore.initialize',
details: `Failed to load activation file for session '${this.sessionId}' — starting fresh (possible data corruption)`,
additionalData: { error: String(error), sessionId: this.sessionId },
});
}
}
}
/**
* Record an element activation. Fire-and-forget persist.
*/
recordActivation(elementType, name, filename) {
if (!this.enabled)
return;
const type = normalizeType(elementType);
if (!ACTIVATABLE_TYPES.has(type))
return;
const normalizedName = normalizeActivationIdentifier(name);
if (!normalizedName)
return;
const normalizedFilename = typeof filename === 'string'
? normalizeActivationIdentifier(filename)
: undefined;
if (!this.state.activations[type]) {
this.state.activations[type] = [];
}
// Deduplicate — don't add if already present
const existing = this.state.activations[type];
const alreadyActive = existing.some(a => a.name === normalizedName);
if (alreadyActive)
return;
existing.push({
name: normalizedName,
...(normalizedFilename ? { filename: normalizedFilename } : {}),
activatedAt: new Date().toISOString(),
});
this.persistAsync();
}
/**
* Record an element deactivation. Fire-and-forget persist.
*/
recordDeactivation(elementType, name) {
if (!this.enabled)
return;
const type = normalizeType(elementType);
if (!ACTIVATABLE_TYPES.has(type))
return;
const normalizedName = normalizeActivationIdentifier(name);
if (!normalizedName)
return;
const activations = this.state.activations[type];
if (!activations)
return;
const initialLength = activations.length;
this.state.activations[type] = activations.filter(a => a.name !== normalizedName);
// Only persist if something actually changed
if (this.state.activations[type].length !== initialLength) {
this.persistAsync();
}
}
/**
* Remove a specific activation by name (used during restore to prune stale entries).
*/
removeStaleActivation(elementType, name) {
this.recordDeactivation(elementType, name);
}
/**
* Get all persisted activations for a given element type.
*/
getActivations(elementType) {
const type = normalizeType(elementType);
return this.state.activations[type] ? [...this.state.activations[type]] : [];
}
/**
* Read persisted activation snapshots from disk for reporting/diagnostics.
*
* This intentionally does not mutate the store's in-memory state, and it is
* safe to call from the web console to inspect other sessions' persisted
* activations without changing live policy enforcement for the current
* process.
*/
async listPersistedActivationStates(sessionId) {
if (!this.enabled) {
return [];
}
const normalizedSessionId = typeof sessionId === 'string' && sessionId.trim()
? normalizeActivationIdentifier(sessionId)
: undefined;
try {
const filenames = await this.getPersistedActivationFilenames(normalizedSessionId);
const states = await Promise.all(filenames.map(filename => this.readPersistedActivationState(filename)));
return states
.flatMap((state) => (state ? [state] : []))
.sort((a, b) => a.sessionId.localeCompare(b.sessionId));
}
catch (error) {
if (error.code !== 'ENOENT') {
logger.debug('[ActivationStore] Failed to enumerate activation snapshots for reporting', {
stateDir: this.stateDir,
error: error instanceof Error ? error.message : String(error),
});
}
}
return [];
}
async getPersistedActivationFilenames(sessionId) {
if (sessionId) {
return [`activations-${sessionId}.json`];
}
const filenames = await fs.readdir(this.stateDir);
return filenames.filter(name => /^activations-[^.]+\.json$/u.test(name));
}
async readPersistedActivationState(filename) {
const filePath = path.join(this.stateDir, filename);
try {
const content = await this.fileOps.readFile(filePath);
const data = JSON.parse(content);
if (!this.isPersistedActivationState(data)) {
return null;
}
return {
sessionId: data.sessionId,
lastUpdated: data.lastUpdated,
activations: this.normalizePersistedActivations(data.activations),
};
}
catch (error) {
this.logSnapshotReadError(filePath, error);
return null;
}
}
isPersistedActivationState(data) {
return data.version === 1
&& typeof data.sessionId === 'string'
&& Boolean(data.activations)
&& typeof data.activations === 'object';
}
normalizePersistedActivations(activations) {
const normalized = {};
for (const [type, entries] of Object.entries(activations)) {
if (!ACTIVATABLE_TYPES.has(type) || !Array.isArray(entries)) {
continue;
}
const normalizedEntries = entries.flatMap((entry) => this.normalizePersistedActivation(entry));
if (normalizedEntries.length > 0) {
normalized[type] = normalizedEntries;
}
}
return normalized;
}
normalizePersistedActivation(entry) {
if (!entry || typeof entry.name !== 'string') {
return [];
}
const normalizedName = normalizeActivationIdentifier(entry.name);
if (!normalizedName) {
return [];
}
const normalizedFilename = typeof entry.filename === 'string'
? normalizeActivationIdentifier(entry.filename)
: undefined;
return [{
...entry,
name: normalizedName,
...(normalizedFilename ? { filename: normalizedFilename } : {}),
}];
}
logSnapshotReadError(filePath, error) {
if (error.code === 'ENOENT') {
return;
}
logger.debug('[ActivationStore] Skipping unreadable activation snapshot during reporting', {
filePath,
error: error instanceof Error ? error.message : String(error),
});
}
/**
* Get the session ID this store is scoped to.
*/
getSessionId() {
return this.sessionId;
}
/**
* Runtime-unique session identifier used for live console/session registry
* surfaces when the stable persistence identity is shared across unnamed
* sessions.
*/
getRuntimeSessionId() {
return this.runtimeSessionId;
}
/**
* Check if persistence is enabled.
*/
isEnabled() {
return this.enabled;
}
/**
* Clear all persisted activations. Used for testing or admin reset.
*/
clearAll() {
this.state = this.createEmptyState();
if (this.enabled) {
this.persistAsync();
}
}
/**
* Fire-and-forget persistence with retry for transient disk failures.
* Retries up to PERSIST_MAX_RETRIES times with a short delay.
* Disk failure does not block activation operations.
*/
persistAsync() {
this.persistWithRetry(0).catch(error => {
logger.warn('[ActivationStore] Failed to persist activation state after retries', { error });
SecurityMonitor.logSecurityEvent({
type: 'ELEMENT_ACTIVATED',
severity: 'MEDIUM',
source: 'ActivationStore.persistAsync',
details: `Failed to persist activation state for session '${this.sessionId}' after ${PERSIST_MAX_RETRIES + 1} attempts — state continues in-memory only`,
additionalData: { error: String(error), sessionId: this.sessionId },
});
});
}
/**
* Attempt to persist with retries for transient failures (e.g., EBUSY, EAGAIN).
*/
async persistWithRetry(attempt) {
try {
await this.persist();
}
catch (error) {
if (attempt < PERSIST_MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, PERSIST_RETRY_DELAY_MS));
return this.persistWithRetry(attempt + 1);
}
throw error;
}
}
/**
* Write current activation state to disk.
*/
async persist() {
this.state.lastUpdated = new Date().toISOString();
await fs.mkdir(this.stateDir, { recursive: true });
await this.fileOps.writeFile(this.persistPath, JSON.stringify(this.state, null, 2));
}
createEmptyState() {
return {
version: 1,
sessionId: this.sessionId,
lastUpdated: new Date().toISOString(),
activations: {},
};
}
getTotalActivationCount() {
return Object.values(this.state.activations).reduce((sum, arr) => sum + (arr?.length ?? 0), 0);
}
getActivationCounts() {
const counts = {};
for (const [type, arr] of Object.entries(this.state.activations)) {
if (arr && arr.length > 0) {
counts[type] = arr.length;
}
}
return counts;
}
logResolvedSessionIdentity(identity) {
const rawEnvValue = process.env.DOLLHOUSE_SESSION_ID?.trim();
if (identity.source === 'env') {
return;
}
if (!rawEnvValue) {
logger.info(`[ActivationStore] No DOLLHOUSE_SESSION_ID set — derived stable session '${identity.sessionId}' from workspace context; runtime session '${identity.runtimeSessionId}' remains unique for this process.`);
return;
}
if (!SESSION_ID_PATTERN.test(rawEnvValue)) {
logger.warn(`Invalid DOLLHOUSE_SESSION_ID '${rawEnvValue}' — must start with a letter, then alphanumeric/hyphens/underscores, 1-64 chars. Using derived session '${identity.sessionId}' instead.`);
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWN0aXZhdGlvblN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZpY2VzL0FjdGl2YXRpb25TdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBRUgsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUN4QixPQUFPLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDN0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUVqRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUM5RSxPQUFPLEVBQ0wsc0JBQXNCLEVBQ3RCLGtCQUFrQixHQUVuQixNQUFNLHNCQUFzQixDQUFDO0FBOEI5Qiw0RUFBNEU7QUFDNUUsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxVQUFVLENBQUMsQ0FBQyxDQUFDO0FBRXZGOzs7O0dBSUc7QUFDSCxNQUFNLGtCQUFrQixHQUEyQjtJQUNqRCxRQUFRLEVBQUUsU0FBUztJQUNuQixNQUFNLEVBQUUsT0FBTztJQUNmLE1BQU0sRUFBRSxPQUFPO0lBQ2YsUUFBUSxFQUFFLFFBQVE7SUFDbEIsU0FBUyxFQUFFLFVBQVU7Q0FDdEIsQ0FBQztBQUVGLFNBQVMsYUFBYSxDQUFDLFdBQW1CO0lBQ3hDLE1BQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUN4QyxPQUFPLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQztBQUM1QyxDQUFDO0FBRUQsU0FBUyw2QkFBNkIsQ0FBQyxLQUFhO0lBQ2xELE9BQU8sZ0JBQWdCLENBQUMsU0FBUyxDQUFD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QUMsTUFBTSxDQUFDO1lBQzVCLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVPLDBCQUEwQixDQUFDLFFBQXlCO1FBQzFELE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLEVBQUUsSUFBSSxFQUFFLENBQUM7UUFDN0QsSUFBSSxRQUFRLENBQUMsTUFBTSxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQzlCLE9BQU87UUFDVCxDQUFDO1FBRUQsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ2pCLE1BQU0sQ0FBQyxJQUFJLENBQ1QsMkVBQTJFLFFBQVEsQ0FBQyxTQUFTLDhDQUE4QyxRQUFRLENBQUMsZ0JBQWdCLG9DQUFvQyxDQUN6TSxDQUFDO1lBQ0YsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFDMUMsTUFBTSxDQUFDLElBQUksQ0FDVCxpQ0FBaUMsV0FBVywyR0FBMkcsUUFBUSxDQUFDLFNBQVMsWUFBWSxDQUN0TCxDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQWN0aXZhdGlvbiBTdG9yZVxuICpcbiAqIFBlcnNpc3RzIHBlci1zZXNzaW9uIGVsZW1lbnQgYWN0aXZhdGlvbiBzdGF0ZSB0byBkaXNrLlxuICogRWFjaCBNQ1Agc2Vzc2lvbiAoaWRlbnRpZmllZCBieSBET0xMSE9VU0VfU0VTU0lPTl9JRCkgZ2V0cyBpdHMgb3duXG4gKiBhY3RpdmF0aW9uIGZpbGUgc28gY29uY3VycmVudCBzZXNzaW9ucyBtYWludGFpbiBpbmRlcGVuZGVudCBwcm9maWxlcy5cbiAqXG4gKiBGb2xsb3dzIHRoZSBEYW5nZXJab25lRW5mb3JjZXIgcGF0dGVybjpcbiAqIC0gREktbWFuYWdlZCBzaW5nbGV0b24gd2l0aCBGaWxlT3BlcmF0aW9uc1NlcnZpY2VcbiAqIC0gaW5pdGlhbGl6ZSgpIGxvYWRzIGZyb20gZGlzayAodG9sZXJhdGVzIG1pc3NpbmcvY29ycnVwdCBmaWxlcylcbiAqIC0gSW4tbWVtb3J5IHN0YXRlIGlzIHRoZSBob3QgcGF0aDsgZGlzayB3cml0ZXMgYXJlIGZpcmUtYW5kLWZvcmdldFxuICogLSBhdG9taWNXcml0ZUZpbGUgKHdyaXRlLXRvLXRlbXAgKyByZW5hbWUpIHByZXZlbnRzIHBhcnRpYWwgcmVhZHNcbiAqXG4gKiBGb3J3YXJkIGNvbXBhdGliaWxpdHk6IFRoZSB2ZXJzaW9uZWQgZmlsZSBmb3JtYXQgKHYxKSBjYW4gZXZvbHZlIHRvXG4gKiBpbmNsdWRlIHVzZXJJZCwgb3JnSWQsIGFuZCBhdWRpdCBmaWVsZHMgZm9yIG11bHRpLXVzZXIgSFRUUFMgbW9kZVxuICogd2l0aG91dCBicmVha2luZyBleGlzdGluZyBpbnN0YWxsYXRpb25zLlxuICpcbiAqIEBzaW5jZSB2Mi4wLjAgLSBJc3N1ZSAjNTk4XG4gKi9cblxuaW1wb3J0IG9zIGZyb20gJ29zJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHR5cGUgeyBGaWxlT3BlcmF0aW9uc1NlcnZpY2UgfSBmcm9tICcuL0ZpbGVPcGVyYXRpb25zU2VydmljZS5qcyc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcbmltcG9ydCB7XG4gIHJlc29sdmVTZXNzaW9uSWRlbnRpdHksXG4gIFNFU1NJT05fSURfUEFUVEVSTixcbiAgdHlwZSBTZXNzaW9uSWRlbnRpdHksXG59IGZyb20gJy4vc2Vzc2lvbklkZW50aXR5LmpzJztcblxuLyoqXG4gKiBBIHBlcnNpc3RlZCBhY3RpdmF0aW9uIHJlY29yZCBmb3IgYSBzaW5nbGUgZWxlbWVudC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQZXJzaXN0ZWRBY3RpdmF0aW9uIHtcbiAgLyoqIEVsZW1lbnQgbmFtZSAoaHVtYW4tcmVhZGFibGUsIHVzZWQgZm9yIGFsbCB0eXBlcykgKi9cbiAgbmFtZTogc3RyaW5nO1xuICAvKiogRm9yIHBlcnNvbmFzIG9ubHk6IHRoZSBmaWxlbmFtZSBrZXkgdXNlZCBieSBQZXJzb25hTWFuYWdlciAqL1xuICBmaWxlbmFtZT86IHN0cmluZztcbiAgLyoqIElTTy04NjAxIHRpbWVzdGFtcCBvZiB3aGVuIGFjdGl2YXRpb24gd2FzIHBlcnNpc3RlZCAqL1xuICBhY3RpdmF0ZWRBdDogc3RyaW5nO1xufVxuXG4vKipcbiAqIFBlcnNpc3RlZCBmaWxlIGZvcm1hdCAodmVyc2lvbmVkIGZvciBmb3J3YXJkIGNvbXBhdGliaWxpdHkpLlxuICovXG5pbnRlcmZhY2UgUGVyc2lzdGVkQWN0aXZhdGlvblN0YXRlIHtcbiAgdmVyc2lvbjogbnVtYmVyO1xuICBzZXNzaW9uSWQ6IHN0cmluZztcbiAgbGFzdFVwZGF0ZWQ6IHN0cmluZztcbiAgYWN0aXZhdGlvbnM6IFJlY29yZDxzdHJpbmcsIFBlcnNpc3RlZEFjdGl2YXRpb25bXT47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGVyc2lzdGVkQWN0aXZhdGlvblN0YXRlU25hcHNob3Qge1xuICBzZXNzaW9uSWQ6IHN0cmluZztcbiAgbGFzdFVwZGF0ZWQ6IHN0cmluZztcbiAgYWN0aXZhdGlvbnM6IFJlY29yZDxzdHJpbmcsIFBlcnNpc3RlZEFjdGl2YXRpb25bXT47XG59XG5cbi8qKiBWYWxpZCBlbGVtZW50IHR5cGVzIHRoYXQgc3VwcG9ydCBhY3RpdmF0aW9uIChzdG9yZWQgaW4gc2luZ3VsYXIgZm9ybSkgKi9cbmNvbnN0IEFDVElWQVRBQkxFX1RZUEVTID0gbmV3IFNldChbJ3BlcnNvbmEnLCAnc2tpbGwnLCAnYWdlbnQnLCAnbWVtb3J5JywgJ2Vuc2VtYmxlJ10pO1xuXG4vKipcbiAqIE5vcm1hbGl6ZSBlbGVtZW50IHR5cGUgdG8gc2luZ3VsYXIgZm9ybSBmb3IgY29uc2lzdGVudCBzdG9yYWdlLlxuICogRWxlbWVudFR5cGUgZW51bSB1c2VzIHBsdXJhbCAoJ3BlcnNvbmFzJywgJ3NraWxscycsIGV0Yy4pIGJ1dCB3ZVxuICogc3RvcmUgaW4gc2luZ3VsYXIgZm9ybSBmb3IgcmVhZGFiaWxpdHkgYW5kIGZvcndhcmQgY29tcGF0aWJpbGl0eS5cbiAqL1xuY29uc3QgUExVUkFMX1RPX1NJTkdVTEFSOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICBwZXJzb25hczogJ3BlcnNvbmEnLFxuICBza2lsbHM6ICdza2lsbCcsXG4gIGFnZW50czogJ2FnZW50JyxcbiAgbWVtb3JpZXM6ICdtZW1vcnknLFxuICBlbnNlbWJsZXM6ICdlbnNlbWJsZScsXG59O1xuXG5mdW5jdGlvbiBub3JtYWxpemVUeXBlKGVsZW1lbnRUeXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBsb3dlciA9IGVsZW1lbnRUeXBlLnRvTG93ZXJDYXNlKCk7XG4gIHJldHVybiBQTFVSQUxfVE9fU0lOR1VMQVJbbG93ZXJdID8/IGxvd2VyO1xufVxuXG5mdW5jdGlvbiBub3JtYWxpemVBY3RpdmF0aW9uSWRlbnRpZmllcih2YWx1ZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHZhbHVlKS5ub3JtYWxpemVkQ29udGVudC50cmltKCk7XG59XG5cbi8qKlxuICogQ2hlY2tzIHdoZXRoZXIgYWN0aXZhdGlvbiBwZXJzaXN0ZW5jZSBpcyBlbmFibGVkLlxuICovXG5mdW5jdGlvbiBpc1BlcnNpc3RlbmNlRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgY29uc3QgZW52VmFsdWUgPSBwcm9jZXNzLmVudi5ET0xMSE9VU0VfQUNUSVZBVElPTl9QRVJTSVNURU5DRT8udHJpbSgpLnRvTG93ZXJDYXNlKCk7XG4gIGlmIChlbnZWYWx1ZSA9PT0gJ2ZhbHNlJyB8fCBlbnZWYWx1ZSA9PT0gJzAnIHx8IGVudlZhbHVlID09PSAnbm8nKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIHJldHVybiB0cnVlO1xufVxuXG4vKiogTWF4aW11bSBudW1iZXIgb2YgcmV0cnkgYXR0ZW1wdHMgZm9yIHRyYW5zaWVudCBkaXNrIGZhaWx1cmVzICovXG5jb25zdCBQRVJTSVNUX01BWF9SRVRSSUVTID0gMjtcblxuLyoqIERlbGF5IGJldHdlZW4gcmV0cnkgYXR0ZW1wdHMgaW4gbWlsbGlzZWNvbmRzICovXG5jb25zdCBQRVJTSVNUX1JFVFJZX0RFTEFZX01TID0gMTAwO1xuXG4vKipcbiAqIFBlci1zZXNzaW9uIGFjdGl2YXRpb24gc3RhdGUgcGVyc2lzdGVuY2UuXG4gKlxuICogUGVyc2lzdHMgZWxlbWVudCBhY3RpdmF0aW9uIHN0YXRlIHRvIGB+Ly5kb2xsaG91c2Uvc3RhdGUvYWN0aXZhdGlvbnMte3Nlc3Npb25JZH0uanNvbmAuXG4gKiBFYWNoIE1DUCBzZXNzaW9uIGlzIGlkZW50aWZpZWQgYnkgdGhlIGBET0xMSE9VU0VfU0VTU0lPTl9JRGAgZW52aXJvbm1lbnQgdmFyaWFibGUuXG4gKlxuICogVGhyZWFkLXNhZmV0eSBub3RlOiBOb2RlLmpzIGlzIHNpbmdsZS10aHJlYWRlZCwgc28gTWFwIG9wZXJhdGlvbnMgYXJlIHNhZmUuXG4gKiBGb3IgbXVsdGktcHJvY2VzcyBkZXBsb3ltZW50cyAoZnV0dXJlIEhUVFBTKSwgY29uc2lkZXIgUmVkaXMgb3IgREIgYmFja2luZy5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIC8vIFNlc3Npb24gSUQgc2V0IHZpYSBlbnZpcm9ubWVudDpcbiAqIC8vIERPTExIT1VTRV9TRVNTSU9OX0lEPWNsYXVkZS1jb2RlXG4gKlxuICogY29uc3Qgc3RvcmUgPSBuZXcgQWN0aXZhdGlvblN0b3JlKGZpbGVPcHMpO1xuICogYXdhaXQgc3RvcmUuaW5pdGlhbGl6ZSgpOyAgLy8gbG9hZHMgfi8uZG9sbGhvdXNlL3N0YXRlL2FjdGl2YXRpb25zLWNsYXVkZS1jb2RlLmpzb25cbiAqXG4gKiBzdG9yZS5yZWNvcmRBY3RpdmF0aW9uKCdza2lsbCcsICdjb2RlLXJldmlld2VyJyk7XG4gKiBzdG9yZS5yZWNvcmRBY3RpdmF0aW9uKCdwZXJzb25hJywgJ0NyZWF0aXZlIERldicsICdjcmVhdGl2ZS1kZXYubWQnKTtcbiAqXG4gKiAvLyBPbiBuZXh0IHNlcnZlciBzdGFydCwgaW5pdGlhbGl6ZSgpIHJlc3RvcmVzIHRoZXNlIGFjdGl2YXRpb25zXG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIEFjdGl2YXRpb25TdG9yZSB7XG4gIHByaXZhdGUgcmVhZG9ubHkgZmlsZU9wczogRmlsZU9wZXJhdGlvbnNTZXJ2aWNlO1xuICBwcml2YXRlIHJlYWRvbmx5IHN0YXRlRGlyOiBzdHJpbmc7XG4gIHByaXZhdGUgcmVhZG9ubHkgc2Vzc2lvbklkOiBzdHJpbmc7XG4gIHByaXZhdGUgcmVhZG9ubHkgcnVudGltZVNlc3Npb25JZDogc3RyaW5nO1xuICBwcml2YXRlIHJlYWRvbmx5IHBlcnNpc3RQYXRoOiBzdHJpbmc7XG4gIHByaXZhdGUgcmVhZG9ubHkgZW5hYmxlZDogYm9vbGVhbjtcblxuICBwcml2YXRlIHN0YXRlOiBQZXJzaXN0ZWRBY3RpdmF0aW9uU3RhdGU7XG5cbiAgY29uc3RydWN0b3IoZmlsZU9wczogRmlsZU9wZXJhdGlvbnNTZXJ2aWNlLCBzdGF0ZURpcj86IHN0cmluZykge1xuICAgIHRoaXMuZmlsZU9wcyA9IGZpbGVPcHM7XG4gICAgY29uc3QgaWRlbnRpdHkgPSByZXNvbHZlU2Vzc2lvbklkZW50aXR5KCk7XG4gICAgdGhpcy5zZXNzaW9uSWQgPSBpZGVudGl0eS5zZXNzaW9uSWQ7XG4gICAgdGhpcy5ydW50aW1lU2Vzc2lvbklkID0gaWRlbnRpdHkucnVudGltZVNlc3Npb25JZDtcbiAgICB0aGlzLmVuYWJsZWQgPSBpc1BlcnNpc3RlbmNlRW5hYmxlZCgpO1xuICAgIHRoaXMuc3RhdGVEaXIgPSBzdGF0ZURpciA/PyBwYXRoLmpvaW4ob3MuaG9tZWRpcigpLCAnLmRvbGxob3VzZScsICdzdGF0ZScpO1xuICAgIHRoaXMucGVyc2lzdFBhdGggPSBwYXRoLmpvaW4odGhpcy5zdGF0ZURpciwgYGFjdGl2YXRpb25zLSR7dGhpcy5zZXNzaW9uSWR9Lmpzb25gKTtcblxuICAgIHRoaXMuc3RhdGUgPSB0aGlzLmNyZWF0ZUVtcHR5U3RhdGUoKTtcbiAgICB0aGlzLmxvZ1Jlc29sdmVkU2Vzc2lvbklkZW50aXR5KGlkZW50aXR5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMb2FkIHBlcnNpc3RlZCBhY3RpdmF0aW9ucyBmcm9tIGRpc2suXG4gICAqIENhbGwgb25jZSBhZnRlciBjb25zdHJ1Y3Rpb24gdG8gcmVzdG9yZSBzdGF0ZSBmcm9tIGEgcHJldmlvdXMgc2Vzc2lvbi5cbiAgICogSWYgdGhlIGZpbGUgaXMgbWlzc2luZyBvciBjb3JydXB0LCBzdGFydHMgd2l0aCBlbXB0eSBhY3RpdmF0aW9ucy5cbiAgICovXG4gIGFzeW5jIGluaXRpYWxpemUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLmVuYWJsZWQpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnW0FjdGl2YXRpb25TdG9yZV0gUGVyc2lzdGVuY2UgZGlzYWJsZWQgdmlhIERPTExIT1VTRV9BQ1RJVkFUSU9OX1BFUlNJU1RFTkNFJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCB0aGlzLmZpbGVPcHMucmVhZEZpbGUodGhpcy5wZXJzaXN0UGF0aCk7XG4gICAgICBjb25zdCBkYXRhID0gSlNPTi5wYXJzZShjb250ZW50KSBhcyBQZXJzaXN0ZWRBY3RpdmF0aW9uU3RhdGU7XG5cbiAgICAgIGlmIChkYXRhLnZlcnNpb24gPT09IDEgJiYgZGF0YS5hY3RpdmF0aW9ucyAmJiB0eXBlb2YgZGF0YS5hY3RpdmF0aW9ucyA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgLy8gVmFsaWRhdGUgYW5kIGxvYWQgb25seSBrbm93biBlbGVtZW50IHR5cGVzXG4gICAgICAgIGZvciAoY29uc3QgW3R5cGUsIGFjdGl2YXRpb25zXSBvZiBPYmplY3QuZW50cmllcyhkYXRhLmFjdGl2YXRpb25zKSkge1xuICAgICAgICAgIGlmIChBQ1RJVkFUQUJMRV9UWVBFUy5oYXModHlwZSkgJiYgQXJyYXkuaXNBcnJheShhY3RpdmF0aW9ucykpIHtcbiAgICAgICAgICAgIHRoaXMuc3RhdGUuYWN0aXZhdGlvbnNbdHlwZV0gPSBhY3RpdmF0aW9ucy5mbGF0TWFwKChhKSA9PiB7XG4gICAgICAgICAgICAgIGlmICghYSB8fCB0eXBlb2YgYS5uYW1lICE9PSAnc3RyaW5nJykgcmV0dXJuIFtdO1xuXG4gICAgICAgICAgICAgIGNvbnN0IG5vcm1hbGl6ZWROYW1lID0gbm9ybWFsaXplQWN0aXZhdGlvbklkZW50aWZpZXIoYS5uYW1lKTtcbiAgICAgICAgICAgICAgaWYgKCFub3JtYWxpemVkTmFtZSkgcmV0dXJuIFtdO1xuXG4gICAgICAgICAgICAgIGNvbnN0IG5vcm1hbGl6ZWRGaWxlbmFtZSA9IHR5cGVvZiBhLmZpbGVuYW1lID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgICAgID8gbm9ybWFsaXplQWN0aXZhdGlvbklkZW50aWZpZXIoYS5maWxlbmFtZSlcbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZDtcblxuICAgICAgICAgICAgICByZXR1cm4gW3tcbiAgICAgICAgICAgICAgICAuLi5hLFxuICAgICAgICAgICAgICAgIG5hbWU6IG5vcm1hbGl6ZWROYW1lLFxuICAgICAgICAgICAgICAgIC4uLihub3JtYWxpemVkRmlsZW5hbWUgPyB7IGZpbGVuYW1lOiBub3JtYWxpemVkRmlsZW5hbWUgfSA6IHt9KSxcbiAgICAgICAgICAgICAgfV07XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB0b3RhbENvdW50ID0gdGhpcy5nZXRUb3RhbEFjdGl2YXRpb25Db3VudCgpO1xuICAgICAgICBpZiAodG90YWxDb3VudCA+IDApIHtcbiAgICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBbQWN0aXZhdGlvblN0b3JlXSBSZXN0b3JlZCAke3RvdGFsQ291bnR9IGFjdGl2YXRpb24ocykgZm9yIHNlc3Npb24gJyR7dGhpcy5zZXNzaW9uSWR9J2BcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgICAgdHlwZTogJ0VMRU1FTlRfQUNUSVZBVEVEJyxcbiAgICAgICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgICAgIHNvdXJjZTogJ0FjdGl2YXRpb25TdG9yZS5pbml0aWFsaXplJyxcbiAgICAgICAgICAgIGRldGFpbHM6IGBSZXN0b3JlZCAke3RvdGFsQ291bnR9IGFjdGl2YXRpb24ocykgZnJvbSBkaXNrIGZvciBzZXNzaW9uICcke3RoaXMuc2Vzc2lvbklkfSdgLFxuICAgICAgICAgICAgYWRkaXRpb25hbERhdGE6IHtcbiAgICAgICAgICAgICAgc2Vzc2lvbklkOiB0aGlzLnNlc3Npb25JZCxcbiAgICAgICAgICAgICAgY291bnRzOiB0aGlzLmdldEFjdGl2YXRpb25Db3VudHMoKSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKChlcnJvciBhcyBOb2RlSlMuRXJybm9FeGNlcHRpb24pLmNvZGUgPT09ICdFTk9FTlQnKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgW0FjdGl2YXRpb25TdG9yZV0gTm8gYWN0aXZhdGlvbiBmaWxlIGZvdW5kIGZvciBzZXNzaW9uICcke3RoaXMuc2Vzc2lvbklkfScsIHN0YXJ0aW5nIGZyZXNoYCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsb2dnZXIud2FybihgW0FjdGl2YXRpb25TdG9yZV0gRmFpbGVkIHRvIGxvYWQgYWN0aXZhdGlvbiBmaWxlIGZvciBzZXNzaW9uICcke3RoaXMuc2Vzc2lvbklkfScsIHN0YXJ0aW5nIGZyZXNoYCwgeyBlcnJvciB9KTtcblxuICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgdHlwZTogJ0VMRU1FTlRfQUNUSVZBVEVEJyxcbiAgICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgICAgc291cmNlOiAnQWN0aXZhdGlvblN0b3JlLmluaXRpYWxpemUnLFxuICAgICAgICAgIGRldGFpbHM6IGBGYWlsZWQgdG8gbG9hZCBhY3RpdmF0aW9uIGZpbGUgZm9yIHNlc3Npb24gJyR7dGhpcy5zZXNzaW9uSWR9JyDigJQgc3RhcnRpbmcgZnJlc2ggKHBvc3NpYmxlIGRhdGEgY29ycnVwdGlvbilgLFxuICAgICAgICAgIGFkZGl0aW9uYWxEYXRhOiB7IGVycm9yOiBTdHJpbmcoZXJyb3IpLCBzZXNzaW9uSWQ6IHRoaXMuc2Vzc2lvbklkIH0sXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvcmQgYW4gZWxlbWVudCBhY3RpdmF0aW9uLiBGaXJlLWFuZC1mb3JnZXQgcGVyc2lzdC5cbiAgICovXG4gIHJlY29yZEFjdGl2YXRpb24oZWxlbWVudFR5cGU6IHN0cmluZywgbmFtZTogc3RyaW5nLCBmaWxlbmFtZT86IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICghdGhpcy5lbmFibGVkKSByZXR1cm47XG5cbiAgICBjb25zdCB0eXBlID0gbm9ybWFsaXplVHlwZShlbGVtZW50VHlwZSk7XG4gICAgaWYgKCFBQ1RJVkFUQUJMRV9UWVBFUy5oYXModHlwZSkpIHJldHVybjtcbiAgICBjb25zdCBub3JtYWxpemVkTmFtZSA9IG5vcm1hbGl6ZUFjdGl2YXRpb25JZGVudGlmaWVyKG5hbWUpO1xuICAgIGlmICghbm9ybWFsaXplZE5hbWUpIHJldHVybjtcbiAgICBjb25zdCBub3JtYWxpemVkRmlsZW5hbWUgPSB0eXBlb2YgZmlsZW5hbWUgPT09ICdzdHJpbmcnXG4gICAgICA/IG5vcm1hbGl6ZUFjdGl2YXRpb25JZGVudGlmaWVyKGZpbGVuYW1lKVxuICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICBpZiAoIXRoaXMuc3RhdGUuYWN0aXZhdGlvbnNbdHlwZV0pIHtcbiAgICAgIHRoaXMuc3RhdGUuYWN0aXZhdGlvbnNbdHlwZV0gPSBbXTtcbiAgICB9XG5cbiAgICAvLyBEZWR1cGxpY2F0ZSDigJQgZG9uJ3QgYWRkIGlmIGFscmVhZHkgcHJlc2VudFxuICAgIGNvbnN0IGV4aXN0aW5nID0gdGhpcy5zdGF0ZS5hY3RpdmF0aW9uc1t0eXBlXSE7XG4gICAgY29uc3QgYWxyZWFkeUFjdGl2ZSA9IGV4aXN0aW5nLnNvbWUoYSA9PiBhLm5hbWUgPT09IG5vcm1hbGl6ZWROYW1lKTtcbiAgICBpZiAoYWxyZWFkeUFjdGl2ZSkgcmV0dXJuO1xuXG4gICAgZXhpc3RpbmcucHVzaCh7XG4gICAgICBuYW1lOiBub3JtYWxpemVkTmFtZSxcbiAgICAgIC4uLihub3JtYWxpemVkRmlsZW5hbWUgPyB7IGZpbGVuYW1lOiBub3JtYWxpemVkRmlsZW5hbWUgfSA6IHt9KSxcbiAgICAgIGFjdGl2YXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgfSk7XG5cbiAgICB0aGlzLnBlcnNpc3RBc3luYygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlY29yZCBhbiBlbGVtZW50IGRlYWN0aXZhdGlvbi4gRmlyZS1hbmQtZm9yZ2V0IHBlcnNpc3QuXG4gICAqL1xuICByZWNvcmREZWFjdGl2YXRpb24oZWxlbWVudFR5cGU6IHN0cmluZywgbmFtZTogc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmVuYWJsZWQpIHJldHVybjtcblxuICAgIGNvbnN0IHR5cGUgPSBub3JtYWxpemVUeXBlKGVsZW1lbnRUeXBlKTtcbiAgICBpZiAoIUFDVElWQVRBQkxFX1RZUEVTLmhhcyh0eXBlKSkgcmV0dXJuO1xuICAgIGNvbnN0IG5vcm1hbGl6ZWROYW1lID0gbm9ybWFsaXplQWN0aXZhdGlvbklkZW50aWZpZXIobmFtZSk7XG4gICAgaWYgKCFub3JtYWxpemVkTmFtZSkgcmV0dXJuO1xuXG4gICAgY29uc3QgYWN0aXZhdGlvbnMgPSB0aGlzLnN0YXRlLmFjdGl2YXRpb25zW3R5cGVdO1xuICAgIGlmICghYWN0aXZhdGlvbnMpIHJldHVybjtcblxuICAgIGNvbnN0IGluaXRpYWxMZW5ndGggPSBhY3RpdmF0aW9ucy5sZW5ndGg7XG4gICAgdGhpcy5zdGF0ZS5hY3RpdmF0aW9uc1t0eXBlXSA9IGFjdGl2YXRpb25zLmZpbHRlcihhID0+IGEubmFtZSAhPT0gbm9ybWFsaXplZE5hbWUpO1xuXG4gICAgLy8gT25seSBwZXJzaXN0IGlmIHNvbWV0aGluZyBhY3R1YWxseSBjaGFuZ2VkXG4gICAgaWYgKHRoaXMuc3RhdGUuYWN0aXZhdGlvbnNbdHlwZV0hLmxlbmd0aCAhPT0gaW5pdGlhbExlbmd0aCkge1xuICAgICAgdGhpcy5wZXJzaXN0QXN5bmMoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIGEgc3BlY2lmaWMgYWN0aXZhdGlvbiBieSBuYW1lICh1c2VkIGR1cmluZyByZXN0b3JlIHRvIHBydW5lIHN0YWxlIGVudHJpZXMpLlxuICAgKi9cbiAgcmVtb3ZlU3RhbGVBY3RpdmF0aW9uKGVsZW1lbnRUeXBlOiBzdHJpbmcsIG5hbWU6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMucmVjb3JkRGVhY3RpdmF0aW9uKGVsZW1lbnRUeXBlLCBuYW1lKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYWxsIHBlcnNpc3RlZCBhY3RpdmF0aW9ucyBmb3IgYSBnaXZlbiBlbGVtZW50IHR5cGUuXG4gICAqL1xuICBnZXRBY3RpdmF0aW9ucyhlbGVtZW50VHlwZTogc3RyaW5nKTogUGVyc2lzdGVkQWN0aXZhdGlvbltdIHtcbiAgICBjb25zdCB0eXBlID0gbm9ybWFsaXplVHlwZShlbGVtZW50VHlwZSk7XG4gICAgcmV0dXJuIHRoaXMuc3RhdGUuYWN0aXZhdGlvbnNbdHlwZV0gPyBbLi4udGhpcy5zdGF0ZS5hY3RpdmF0aW9uc1t0eXBlXSFdIDogW107XG4gIH1cblxuICAvKipcbiAgICogUmVhZCBwZXJzaXN0ZWQgYWN0aXZhdGlvbiBzbmFwc2hvdHMgZnJvbSBkaXNrIGZvciByZXBvcnRpbmcvZGlhZ25vc3RpY3MuXG4gICAqXG4gICAqIFRoaXMgaW50ZW50aW9uYWxseSBkb2VzIG5vdCBtdXRhdGUgdGhlIHN0b3JlJ3MgaW4tbWVtb3J5IHN0YXRlLCBhbmQgaXQgaXNcbiAgICogc2FmZSB0byBjYWxsIGZyb20gdGhlIHdlYiBjb25zb2xlIHRvIGluc3BlY3Qgb3RoZXIgc2Vzc2lvbnMnIHBlcnNpc3RlZFxuICAgKiBhY3RpdmF0aW9ucyB3aXRob3V0IGNoYW5naW5nIGxpdmUgcG9saWN5IGVuZm9yY2VtZW50IGZvciB0aGUgY3VycmVudFxuICAgKiBwcm9jZXNzLlxuICAgKi9cbiAgYXN5bmMgbGlzdFBlcnNpc3RlZEFjdGl2YXRpb25TdGF0ZXMoc2Vzc2lvbklkPzogc3RyaW5nKTogUHJvbWlzZTxQZXJzaXN0ZWRBY3RpdmF0aW9uU3RhdGVTbmFwc2hvdFtdPiB7XG4gICAgaWYgKCF0aGlzLmVuYWJsZWQpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICBjb25zdCBub3JtYWxpemVkU2Vzc2lvbklkID0gdHlwZW9mIHNlc3Npb25JZCA9PT0gJ3N0cmluZycgJiYgc2Vzc2lvbklkLnRyaW0oKVxuICAgICAgPyBub3JtYWxpemVBY3RpdmF0aW9uSWRlbnRpZmllcihzZXNzaW9uSWQpXG4gICAgICA6IHVuZGVmaW5lZDtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBmaWxlbmFtZXMgPSBhd2FpdCB0aGlzLmdldFBlcnNpc3RlZEFjdGl2YXRpb25GaWxlbmFtZXMobm9ybWFsaXplZFNlc3Npb25JZCk7XG4gICAgICBjb25zdCBzdGF0ZXMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgZmlsZW5hbWVzLm1hcChmaWxlbmFtZSA9PiB0aGlzLnJlYWRQZXJzaXN0ZWRBY3RpdmF0aW9uU3RhdGUoZmlsZW5hbWUpKSxcbiAgICAgICk7XG4gICAgICByZXR1cm4gc3RhdGVzXG4gICAgICAgIC5mbGF0TWFwKChzdGF0ZSkgPT4gKHN0YXRlID8gW3N0YXRlXSA6IFtdKSlcbiAgICAgICAgLnNvcnQoKGEsIGIpID0+IGEuc2Vzc2lvbklkLmxvY2FsZUNvbXBhcmUoYi5zZXNzaW9uSWQpKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKChlcnJvciBhcyBOb2RlSlMuRXJybm9FeGNlcHRpb24pLmNvZGUgIT09ICdFTk9FTlQnKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnW0FjdGl2YXRpb25TdG9yZV0gRmFpbGVkIHRvIGVudW1lcmF0ZSBhY3RpdmF0aW9uIHNuYXBzaG90cyBmb3IgcmVwb3J0aW5nJywge1xuICAgICAgICAgIHN0YXRlRGlyOiB0aGlzLnN0YXRlRGlyLFxuICAgICAgICAgIGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvciksXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBbXTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZ2V0UGVyc2lzdGVkQWN0aXZhdGlvbkZpbGVuYW1lcyhzZXNzaW9uSWQ/OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gICAgaWYgKHNlc3Npb25JZCkge1xuICAgICAgcmV0dXJuIFtgYWN0aXZhdGlvbnMtJHtzZXNzaW9uSWR9Lmpzb25gXTtcbiAgICB9XG5cbiAgICBjb25zdCBmaWxlbmFtZXMgPSBhd2FpdCBmcy5yZWFkZGlyKHRoaXMuc3RhdGVEaXIpO1xuICAgIHJldHVybiBmaWxlbmFtZXMuZmlsdGVyKG5hbWUgPT4gL15hY3RpdmF0aW9ucy1bXi5dK1xcLmpzb24kL3UudGVzdChuYW1lKSk7XG4gIH1cblxuICBwcml2YXRlI