@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.
358 lines • 43.6 kB
JavaScript
/**
* Gatekeeper Session Management
*
* Manages per-connection session state for the Gatekeeper Policy Engine.
* Each MCP client connection gets a separate session with isolated state.
*
* CRITICAL: Session state is IN-MEMORY only.
* - Confirmations are NOT persisted to disk
* - Each Claude Code / Claude Desktop / etc. = separate session
* - No cross-session policy leakage
* - Crash = fresh session (security-first decision)
*/
import { randomUUID } from 'crypto';
import { env } from '../../config/env.js';
/**
* Gatekeeper Session Manager.
* Manages per-connection session state with isolated confirmation tracking.
*
* DESIGN NOTES:
* - Session ID is generated on MCP connection initialization
* - Tied to the specific transport/connection
* - NOT shared via filesystem (in-memory per server instance)
* - Each `claude` CLI invocation spawns separate MCP server process = separate session
*
* PROCESS ISOLATION:
* Each Claude Code session = separate node process (verified)
* MCP stdio transport = child process per client. No sharing possible.
*
* HTTP/HTTPS TRANSPORT CONSIDERATIONS (for future implementers):
* If implementing HTTP/HTTPS transport instead of stdio:
* - Session state must be tied to authenticated client identity (API key, token)
* - Multiple LLM clients could connect to a shared server (unlike stdio's 1:1)
* - Consider signed session tokens (JWT) for stateless deployments
* - Implement session expiration and renewal mechanisms
* - For load-balanced deployments, use Redis or similar for shared session state
*/
/** Default maximum number of CLI approval records before LRU eviction */
const DEFAULT_MAX_CLI_APPROVALS = env.DOLLHOUSE_CLI_APPROVAL_MAX;
/** Default TTL for CLI approval records (5 minutes) */
const DEFAULT_APPROVAL_TTL_MS = env.DOLLHOUSE_CLI_APPROVAL_TTL_MS;
/** Minimum TTL for CLI approval records (1 second) */
const MIN_APPROVAL_TTL_MS = 1_000;
/** Maximum TTL for CLI approval records (24 hours) */
const MAX_APPROVAL_TTL_MS = 86_400_000;
/** Throttle interval for expiry sweeps (10 seconds) */
const EXPIRY_SWEEP_INTERVAL_MS = 10_000;
export class GatekeeperSession {
state;
maxConfirmations;
maxCliApprovals;
lastExpirySweep = 0;
constructor(clientInfo, maxConfirmations = 100, maxCliApprovals = DEFAULT_MAX_CLI_APPROVALS) {
this.maxConfirmations = maxConfirmations;
this.maxCliApprovals = maxCliApprovals;
this.state = {
sessionId: randomUUID(),
clientInfo,
createdAt: new Date().toISOString(),
lastActivity: new Date().toISOString(),
confirmations: new Map(),
cliApprovals: new Map(),
cliSessionApprovals: new Map(),
permissionPromptActive: false,
};
}
/**
* Get the unique session identifier.
*/
get sessionId() {
return this.state.sessionId;
}
/**
* Get the client information.
*/
get clientInfo() {
return this.state.clientInfo;
}
/**
* Get the session creation timestamp.
*/
get createdAt() {
return this.state.createdAt;
}
/**
* Get the last activity timestamp.
*/
get lastActivity() {
return this.state.lastActivity;
}
/**
* Whether permission_prompt has been invoked at least once this session.
* Used for fail-safe detection (Issue #625 Phase 4).
*/
get isPermissionPromptActive() {
return this.state.permissionPromptActive;
}
/**
* Mark that permission_prompt has been invoked.
* Called on first invocation to track that the CLI client supports it.
*/
markPermissionPromptActive() {
this.state.permissionPromptActive = true;
}
/**
* Update the last activity timestamp.
*/
touch() {
this.state.lastActivity = new Date().toISOString();
}
/**
* Record a confirmation for an operation.
* Confirmations are scoped to this session only.
*
* @param operation - The operation being confirmed
* @param permissionLevel - The permission level being confirmed
* @param elementType - Optional element type scope
*/
recordConfirmation(operation, permissionLevel, elementType) {
this.touch();
// Enforce max confirmations (LRU eviction)
if (this.state.confirmations.size >= this.maxConfirmations) {
// Remove oldest confirmation
const oldestKey = this.state.confirmations.keys().next().value;
if (oldestKey) {
this.state.confirmations.delete(oldestKey);
}
}
const key = this.getConfirmationKey(operation, elementType);
this.state.confirmations.set(key, {
operation,
confirmedAt: new Date().toISOString(),
permissionLevel,
useCount: 0,
elementType,
});
}
/**
* Check if an operation has a valid session confirmation.
* For CONFIRM_SINGLE_USE, this invalidates the confirmation after checking.
*
* RACE CONDITION NOTE:
* The check-then-delete for CONFIRM_SINGLE_USE is safe because:
* 1. Node.js is single-threaded - no concurrent access to Map
* 2. MCP requests are processed sequentially per connection
* 3. Each session instance is tied to a single client connection
* If this code is ever used in a multi-threaded context or with shared
* state across processes, atomic operations would be required.
*
* @param operation - The operation to check
* @param elementType - Optional element type scope
* @returns The confirmation record if valid, undefined otherwise
*/
checkConfirmation(operation, elementType) {
this.touch();
const key = this.getConfirmationKey(operation, elementType);
let confirmation = this.state.confirmations.get(key);
// Fall back to unscoped confirmation when element-type-scoped key not found.
// A session-wide confirmation for "create_element" covers "create_element:skill" etc.
if (!confirmation && elementType) {
confirmation = this.state.confirmations.get(operation);
}
if (!confirmation) {
return undefined;
}
// Increment use count
confirmation.useCount++;
// For CONFIRM_SINGLE_USE, invalidate after first use
if (confirmation.permissionLevel === 'CONFIRM_SINGLE_USE') {
// Delete whichever key matched (scoped or unscoped)
if (this.state.confirmations.has(key)) {
this.state.confirmations.delete(key);
}
else {
this.state.confirmations.delete(operation);
}
}
return confirmation;
}
/**
* Check if an operation has a session confirmation WITHOUT consuming it.
* Use this to check status without affecting the confirmation state.
*
* @param operation - The operation to check
* @param elementType - Optional element type scope
* @returns The confirmation record if exists, undefined otherwise
*/
peekConfirmation(operation, elementType) {
const key = this.getConfirmationKey(operation, elementType);
return this.state.confirmations.get(key);
}
/**
* Revoke a confirmation for an operation.
*
* @param operation - The operation to revoke
* @param elementType - Optional element type scope
* @returns true if a confirmation was revoked
*/
revokeConfirmation(operation, elementType) {
this.touch();
const key = this.getConfirmationKey(operation, elementType);
return this.state.confirmations.delete(key);
}
/**
* Revoke all confirmations for this session.
* Useful when security-sensitive changes occur.
*/
revokeAllConfirmations() {
this.touch();
this.state.confirmations.clear();
}
/**
* Get all active confirmations for this session.
* Useful for debugging and audit logging.
*/
getActiveConfirmations() {
return Array.from(this.state.confirmations.values());
}
// ── CLI Approval Store (Issue #625 Phase 3) ────────────────────
/**
* Create a CLI approval request.
* Returns a unique request ID (format: cli-<UUIDv4>).
*/
createCliApprovalRequest(toolName, toolInput, riskLevel, riskScore, irreversible, denyReason, policySource, ttlMs) {
this.touch();
this.expireStaleApprovals(true); // Force sweep on write path to ensure capacity
// LRU eviction at max capacity
if (this.state.cliApprovals.size >= this.maxCliApprovals) {
const oldestKey = this.state.cliApprovals.keys().next().value;
if (oldestKey) {
this.state.cliApprovals.delete(oldestKey);
}
}
const requestId = `cli-${randomUUID()}`;
// Clamp ttlMs to valid bounds (1s-24h) if provided
const clampedTtl = ttlMs != null
? Math.max(MIN_APPROVAL_TTL_MS, Math.min(MAX_APPROVAL_TTL_MS, ttlMs))
: undefined;
const record = {
requestId,
toolName,
toolInput,
riskLevel,
riskScore,
irreversible,
requestedAt: new Date().toISOString(),
consumed: false,
scope: 'single',
denyReason,
policySource,
ttlMs: clampedTtl,
};
this.state.cliApprovals.set(requestId, record);
return requestId;
}
/**
* Approve a pending CLI approval request.
* Sets approvedAt, and promotes to session approvals if tool_session scope.
*
* @returns The approved record, or undefined if not found
*/
approveCliRequest(requestId, scope = 'single') {
this.touch();
const record = this.state.cliApprovals.get(requestId);
if (!record || record.approvedAt) {
return undefined;
}
record.approvedAt = new Date().toISOString();
record.scope = scope;
// Promote to session approvals if tool_session scope
if (scope === 'tool_session') {
this.state.cliSessionApprovals.set(record.toolName, record);
}
return record;
}
/**
* Check if a CLI tool call has a valid approval.
* Checks session approvals first (fast path), then individual approvals.
* Consumes single-scope approvals on use.
*
* @returns The matching approval record, or undefined
*/
checkCliApproval(toolName, _toolInput) {
this.touch();
this.expireStaleApprovals();
// Fast path: check session-scoped approvals by tool name
const sessionApproval = this.state.cliSessionApprovals.get(toolName);
if (sessionApproval) {
return sessionApproval;
}
// Check individual approvals
for (const [, record] of this.state.cliApprovals) {
if (record.toolName === toolName && record.approvedAt && !record.consumed) {
if (record.scope === 'single') {
record.consumed = true;
}
return record;
}
}
return undefined;
}
/**
* Get all pending (unapproved) CLI approval requests.
* Forces expiry sweep to ensure accurate user-facing results.
*/
getPendingCliApprovals() {
this.expireStaleApprovals(true);
const pending = [];
for (const [, record] of this.state.cliApprovals) {
if (!record.approvedAt) {
pending.push(record);
}
}
return pending;
}
/**
* Expire stale approval requests.
* Uses per-record ttlMs if set, otherwise DEFAULT_APPROVAL_TTL_MS (5 minutes).
*
* @param force - Skip throttle check (used by write paths that must ensure capacity)
*/
expireStaleApprovals(force = false) {
const now = Date.now();
if (!force && now - this.lastExpirySweep < EXPIRY_SWEEP_INTERVAL_MS)
return;
this.lastExpirySweep = now;
for (const [key, record] of this.state.cliApprovals) {
const ttl = record.ttlMs ?? DEFAULT_APPROVAL_TTL_MS;
const age = now - new Date(record.requestedAt).getTime();
// Evict stale pending requests AND consumed single-use approvals (#1782)
if (age > ttl && (!record.approvedAt || record.consumed)) {
this.state.cliApprovals.delete(key);
}
}
}
/**
* Get a summary of the session state.
* Safe to expose for debugging without revealing sensitive data.
*/
getSummary() {
return {
sessionId: this.state.sessionId,
clientInfo: this.state.clientInfo,
createdAt: this.state.createdAt,
lastActivity: this.state.lastActivity,
confirmationCount: this.state.confirmations.size,
cliApprovalCount: this.state.cliApprovals.size,
permissionPromptActive: this.state.permissionPromptActive,
};
}
/**
* Generate a unique key for confirmation lookups.
* Combines operation and optional element type.
*/
getConfirmationKey(operation, elementType) {
return elementType ? `${operation}:${elementType}` : operation;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR2F0ZWtlZXBlclNlc3Npb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaGFuZGxlcnMvbWNwLWFxbC9HYXRla2VlcGVyU2Vzc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7R0FXRztBQUVILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFcEMsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBb0MxQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBcUJHO0FBQ0gseUVBQXlFO0FBQ3pFLE1BQU0seUJBQXlCLEdBQUcsR0FBRyxDQUFDLDBCQUEwQixDQUFDO0FBRWpFLHVEQUF1RDtBQUN2RCxNQUFNLHVCQUF1QixHQUFHLEdBQUcsQ0FBQyw2QkFBNkIsQ0FBQztBQUVsRSxzREFBc0Q7QUFDdEQsTUFBTSxtQkFBbUIsR0FBRyxLQUFLLENBQUM7QUFFbEMsc0RBQXNEO0FBQ3RELE1BQU0sbUJBQW1CLEdBQUcsVUFBVSxDQUFDO0FBRXZDLHVEQUF1RDtBQUN2RCxNQUFNLHdCQUF3QixHQUFHLE1BQU0sQ0FBQztBQUV4QyxNQUFNLE9BQU8saUJBQWlCO0lBQ1gsS0FBSyxDQUF5QjtJQUM5QixnQkFBZ0IsQ0FBUztJQUN6QixlQUFlLENBQVM7SUFDakMsZUFBZSxHQUFHLENBQUMsQ0FBQztJQUU1QixZQUFZLFVBQXVCLEVBQUUsbUJBQTJCLEdBQUcsRUFBRSxrQkFBMEIseUJBQXlCO1FBQ3RILElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsQ0FBQztRQUN6QyxJQUFJLENBQUMsZUFBZSxHQUFHLGVBQWUsQ0FBQztRQUN2QyxJQUFJLENBQUMsS0FBSyxHQUFHO1lBQ1gsU0FBUyxFQUFFLFVBQVUsRUFBRTtZQUN2QixVQUFVO1lBQ1YsU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFO1lBQ25DLFlBQVksRUFBRSxJQUFJLElBQUksRUFB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QUNKLENBQUM7SUFFRDs7O09BR0c7SUFDSyxrQkFBa0IsQ0FBQyxTQUFpQixFQUFFLFdBQW9CO1FBQ2hFLE9BQU8sV0FBVyxDQUFDLENBQUMsQ0FBQyxHQUFHLFNBQVMsSUFBSSxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQ2pFLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2F0ZWtlZXBlciBTZXNzaW9uIE1hbmFnZW1lbnRcbiAqXG4gKiBNYW5hZ2VzIHBlci1jb25uZWN0aW9uIHNlc3Npb24gc3RhdGUgZm9yIHRoZSBHYXRla2VlcGVyIFBvbGljeSBFbmdpbmUuXG4gKiBFYWNoIE1DUCBjbGllbnQgY29ubmVjdGlvbiBnZXRzIGEgc2VwYXJhdGUgc2Vzc2lvbiB3aXRoIGlzb2xhdGVkIHN0YXRlLlxuICpcbiAqIENSSVRJQ0FMOiBTZXNzaW9uIHN0YXRlIGlzIElOLU1FTU9SWSBvbmx5LlxuICogLSBDb25maXJtYXRpb25zIGFyZSBOT1QgcGVyc2lzdGVkIHRvIGRpc2tcbiAqIC0gRWFjaCBDbGF1ZGUgQ29kZSAvIENsYXVkZSBEZXNrdG9wIC8gZXRjLiA9IHNlcGFyYXRlIHNlc3Npb25cbiAqIC0gTm8gY3Jvc3Mtc2Vzc2lvbiBwb2xpY3kgbGVha2FnZVxuICogLSBDcmFzaCA9IGZyZXNoIHNlc3Npb24gKHNlY3VyaXR5LWZpcnN0IGRlY2lzaW9uKVxuICovXG5cbmltcG9ydCB7IHJhbmRvbVVVSUQgfSBmcm9tICdjcnlwdG8nO1xuaW1wb3J0IHR5cGUgeyBDb25maXJtYXRpb25SZWNvcmQsIFBlcm1pc3Npb25MZXZlbCwgQ2xpQXBwcm92YWxSZWNvcmQsIENsaUFwcHJvdmFsU2NvcGUgfSBmcm9tICcuL0dhdGVrZWVwZXJUeXBlcy5qcyc7XG5pbXBvcnQgeyBlbnYgfSBmcm9tICcuLi8uLi9jb25maWcvZW52LmpzJztcblxuLyoqXG4gKiBDbGllbnQgaW5mb3JtYXRpb24gZnJvbSBNQ1AgY2FwYWJpbGl0aWVzLlxuICogSWRlbnRpZmllcyB3aGljaCBNQ1AgY2xpZW50IGlzIG1ha2luZyByZXF1ZXN0cy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDbGllbnRJbmZvIHtcbiAgLyoqIENsaWVudCBuYW1lIChlLmcuLCBcImNsYXVkZS1jb2RlXCIsIFwiY2xhdWRlLWRlc2t0b3BcIikgKi9cbiAgbmFtZTogc3RyaW5nO1xuICAvKiogQ2xpZW50IHZlcnNpb24gKi9cbiAgdmVyc2lvbjogc3RyaW5nO1xufVxuXG4vKipcbiAqIFNlc3Npb24gc3RhdGUgZm9yIGEgc2luZ2xlIE1DUCBjb25uZWN0aW9uLlxuICogVHJhY2tzIGNvbmZpcm1hdGlvbnMgYW5kIGFjdGl2aXR5IGZvciB0aGlzIHNlc3Npb24gb25seS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBHYXRla2VlcGVyU2Vzc2lvblN0YXRlIHtcbiAgLyoqIFVuaXF1ZSBzZXNzaW9uIGlkZW50aWZpZXIgKi9cbiAgc2Vzc2lvbklkOiBzdHJpbmc7XG4gIC8qKiBDbGllbnQgaW5mb3JtYXRpb24gZnJvbSBNQ1AgY2FwYWJpbGl0aWVzICovXG4gIGNsaWVudEluZm8/OiBDbGllbnRJbmZvO1xuICAvKiogV2hlbiB0aGUgc2Vzc2lvbiB3YXMgY3JlYXRlZCAqL1xuICBjcmVhdGVkQXQ6IHN0cmluZztcbiAgLyoqIExhc3QgYWN0aXZpdHkgdGltZXN0YW1wICovXG4gIGxhc3RBY3Rpdml0eTogc3RyaW5nO1xuICAvKiogTWFwIG9mIG9wZXJhdGlvbiAtPiBjb25maXJtYXRpb24gcmVjb3JkICovXG4gIGNvbmZpcm1hdGlvbnM6IE1hcDxzdHJpbmcsIENvbmZpcm1hdGlvblJlY29yZD47XG4gIC8qKiBNYXAgb2YgcmVxdWVzdElkIC0+IENMSSBhcHByb3ZhbCByZWNvcmQgKElzc3VlICM2MjUgUGhhc2UgMykgKi9cbiAgY2xpQXBwcm92YWxzOiBNYXA8c3RyaW5nLCBDbGlBcHByb3ZhbFJlY29yZD47XG4gIC8qKiBNYXAgb2YgdG9vbE5hbWUgLT4gc2Vzc2lvbi1zY29wZWQgQ0xJIGFwcHJvdmFsIChJc3N1ZSAjNjI1IFBoYXNlIDMpICovXG4gIGNsaVNlc3Npb25BcHByb3ZhbHM6IE1hcDxzdHJpbmcsIENsaUFwcHJvdmFsUmVjb3JkPjtcbiAgLyoqIFdoZXRoZXIgcGVybWlzc2lvbl9wcm9tcHQgaGFzIGJlZW4gY2FsbGVkIGF0IGxlYXN0IG9uY2UgdGhpcyBzZXNzaW9uIChJc3N1ZSAjNjI1IFBoYXNlIDQpICovXG4gIHBlcm1pc3Npb25Qcm9tcHRBY3RpdmU6IGJvb2xlYW47XG59XG5cbi8qKlxuICogR2F0ZWtlZXBlciBTZXNzaW9uIE1hbmFnZXIuXG4gKiBNYW5hZ2VzIHBlci1jb25uZWN0aW9uIHNlc3Npb24gc3RhdGUgd2l0aCBpc29sYXRlZCBjb25maXJtYXRpb24gdHJhY2tpbmcuXG4gKlxuICogREVTSUdOIE5PVEVTOlxuICogLSBTZXNzaW9uIElEIGlzIGdlbmVyYXRlZCBvbiBNQ1AgY29ubmVjdGlvbiBpbml0aWFsaXphdGlvblxuICogLSBUaWVkIHRvIHRoZSBzcGVjaWZpYyB0cmFuc3BvcnQvY29ubmVjdGlvblxuICogLSBOT1Qgc2hhcmVkIHZpYSBmaWxlc3lzdGVtIChpbi1tZW1vcnkgcGVyIHNlcnZlciBpbnN0YW5jZSlcbiAqIC0gRWFjaCBgY2xhdWRlYCBDTEkgaW52b2NhdGlvbiBzcGF3bnMgc2VwYXJhdGUgTUNQIHNlcnZlciBwcm9jZXNzID0gc2VwYXJhdGUgc2Vzc2lvblxuICpcbiAqIFBST0NFU1MgSVNPTEFUSU9OOlxuICogRWFjaCBDbGF1ZGUgQ29kZSBzZXNzaW9uID0gc2VwYXJhdGUgbm9kZSBwcm9jZXNzICh2ZXJpZmllZClcbiAqIE1DUCBzdGRpbyB0cmFuc3BvcnQgPSBjaGlsZCBwcm9jZXNzIHBlciBjbGllbnQuIE5vIHNoYXJpbmcgcG9zc2libGUuXG4gKlxuICogSFRUUC9IVFRQUyBUUkFOU1BPUlQgQ09OU0lERVJBVElPTlMgKGZvciBmdXR1cmUgaW1wbGVtZW50ZXJzKTpcbiAqIElmIGltcGxlbWVudGluZyBIVFRQL0hUVFBTIHRyYW5zcG9ydCBpbnN0ZWFkIG9mIHN0ZGlvOlxuICogLSBTZXNzaW9uIHN0YXRlIG11c3QgYmUgdGllZCB0byBhdXRoZW50aWNhdGVkIGNsaWVudCBpZGVudGl0eSAoQVBJIGtleSwgdG9rZW4pXG4gKiAtIE11bHRpcGxlIExMTSBjbGllbnRzIGNvdWxkIGNvbm5lY3QgdG8gYSBzaGFyZWQgc2VydmVyICh1bmxpa2Ugc3RkaW8ncyAxOjEpXG4gKiAtIENvbnNpZGVyIHNpZ25lZCBzZXNzaW9uIHRva2VucyAoSldUKSBmb3Igc3RhdGVsZXNzIGRlcGxveW1lbnRzXG4gKiAtIEltcGxlbWVudCBzZXNzaW9uIGV4cGlyYXRpb24gYW5kIHJlbmV3YWwgbWVjaGFuaXNtc1xuICogLSBGb3IgbG9hZC1iYWxhbmNlZCBkZXBsb3ltZW50cywgdXNlIFJlZGlzIG9yIHNpbWlsYXIgZm9yIHNoYXJlZCBzZXNzaW9uIHN0YXRlXG4gKi9cbi8qKiBEZWZhdWx0IG1heGltdW0gbnVtYmVyIG9mIENMSSBhcHByb3ZhbCByZWNvcmRzIGJlZm9yZSBMUlUgZXZpY3Rpb24gKi9cbmNvbnN0IERFRkFVTFRfTUFYX0NMSV9BUFBST1ZBTFMgPSBlbnYuRE9MTEhPVVNFX0NMSV9BUFBST1ZBTF9NQVg7XG5cbi8qKiBEZWZhdWx0IFRUTCBmb3IgQ0xJIGFwcHJvdmFsIHJlY29yZHMgKDUgbWludXRlcykgKi9cbmNvbnN0IERFRkFVTFRfQVBQUk9WQUxfVFRMX01TID0gZW52LkRPTExIT1VTRV9DTElfQVBQUk9WQUxfVFRMX01TO1xuXG4vKiogTWluaW11bSBUVEwgZm9yIENMSSBhcHByb3ZhbCByZWNvcmRzICgxIHNlY29uZCkgKi9cbmNvbnN0IE1JTl9BUFBST1ZBTF9UVExfTVMgPSAxXzAwMDtcblxuLyoqIE1heGltdW0gVFRMIGZvciBDTEkgYXBwcm92YWwgcmVjb3JkcyAoMjQgaG91cnMpICovXG5jb25zdCBNQVhfQVBQUk9WQUxfVFRMX01TID0gODZfNDAwXzAwMDtcblxuLyoqIFRocm90dGxlIGludGVydmFsIGZvciBleHBpcnkgc3dlZXBzICgxMCBzZWNvbmRzKSAqL1xuY29uc3QgRVhQSVJZX1NXRUVQX0lOVEVSVkFMX01TID0gMTBfMDAwO1xuXG5leHBvcnQgY2xhc3MgR2F0ZWtlZXBlclNlc3Npb24ge1xuICBwcml2YXRlIHJlYWRvbmx5IHN0YXRlOiBHYXRla2VlcGVyU2Vzc2lvblN0YXRlO1xuICBwcml2YXRlIHJlYWRvbmx5IG1heENvbmZpcm1hdGlvbnM6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBtYXhDbGlBcHByb3ZhbHM6IG51bWJlcjtcbiAgcHJpdmF0ZSBsYXN0RXhwaXJ5U3dlZXAgPSAwO1xuXG4gIGNvbnN0cnVjdG9yKGNsaWVudEluZm8/OiBDbGllbnRJbmZvLCBtYXhDb25maXJtYXRpb25zOiBudW1iZXIgPSAxMDAsIG1heENsaUFwcHJvdmFsczogbnVtYmVyID0gREVGQVVMVF9NQVhfQ0xJX0FQUFJPVkFMUykge1xuICAgIHRoaXMubWF4Q29uZmlybWF0aW9ucyA9IG1heENvbmZpcm1hdGlvbnM7XG4gICAgdGhpcy5tYXhDbGlBcHByb3ZhbHMgPSBtYXhDbGlBcHByb3ZhbHM7XG4gICAgdGhpcy5zdGF0ZSA9IHtcbiAgICAgIHNlc3Npb25JZDogcmFuZG9tVVVJRCgpLFxuICAgICAgY2xpZW50SW5mbyxcbiAgICAgIGNyZWF0ZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgbGFzdEFjdGl2aXR5OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBjb25maXJtYXRpb25zOiBuZXcgTWFwKCksXG4gICAgICBjbGlBcHByb3ZhbHM6IG5ldyBNYXAoKSxcbiAgICAgIGNsaVNlc3Npb25BcHByb3ZhbHM6IG5ldyBNYXAoKSxcbiAgICAgIHBlcm1pc3Npb25Qcm9tcHRBY3RpdmU6IGZhbHNlLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSB1bmlxdWUgc2Vzc2lvbiBpZGVudGlmaWVyLlxuICAgKi9cbiAgZ2V0IHNlc3Npb25JZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLnN0YXRlLnNlc3Npb25JZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIGNsaWVudCBpbmZvcm1hdGlvbi5cbiAgICovXG4gIGdldCBjbGllbnRJbmZvKCk6IENsaWVudEluZm8gfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB0aGlzLnN0YXRlLmNsaWVudEluZm87XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBzZXNzaW9uIGNyZWF0aW9uIHRpbWVzdGFtcC5cbiAgICovXG4gIGdldCBjcmVhdGVkQXQoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5zdGF0ZS5jcmVhdGVkQXQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBsYXN0IGFjdGl2aXR5IHRpbWVzdGFtcC5cbiAgICovXG4gIGdldCBsYXN0QWN0aXZpdHkoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5zdGF0ZS5sYXN0QWN0aXZpdHk7XG4gIH1cblxuICAvKipcbiAgICogV2hldGhlciBwZXJtaXNzaW9uX3Byb21wdCBoYXMgYmVlbiBpbnZva2VkIGF0IGxlYXN0IG9uY2UgdGhpcyBzZXNzaW9uLlxuICAgKiBVc2VkIGZvciBmYWlsLXNhZmUgZGV0ZWN0aW9uIChJc3N1ZSAjNjI1IFBoYXNlIDQpLlxuICAgKi9cbiAgZ2V0IGlzUGVybWlzc2lvblByb21wdEFjdGl2ZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zdGF0ZS5wZXJtaXNzaW9uUHJvbXB0QWN0aXZlO1xuICB9XG5cbiAgLyoqXG4gICAqIE1hcmsgdGhhdCBwZXJtaXNzaW9uX3Byb21wdCBoYXMgYmVlbiBpbnZva2VkLlxuICAgKiBDYWxsZWQgb24gZmlyc3QgaW52b2NhdGlvbiB0byB0cmFjayB0aGF0IHRoZSBDTEkgY2xpZW50IHN1cHBvcnRzIGl0LlxuICAgKi9cbiAgbWFya1Blcm1pc3Npb25Qcm9tcHRBY3RpdmUoKTogdm9pZCB7XG4gICAgdGhpcy5zdGF0ZS5wZXJtaXNzaW9uUHJvbXB0QWN0aXZlID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgdGhlIGxhc3QgYWN0aXZpdHkgdGltZXN0YW1wLlxuICAgKi9cbiAgdG91Y2goKTogdm9pZCB7XG4gICAgdGhpcy5zdGF0ZS5sYXN0QWN0aXZpdHkgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG4gIH1cblxuICAvKipcbiAgICogUmVjb3JkIGEgY29uZmlybWF0aW9uIGZvciBhbiBvcGVyYXRpb24uXG4gICAqIENvbmZpcm1hdGlvbnMgYXJlIHNjb3BlZCB0byB0aGlzIHNlc3Npb24gb25seS5cbiAgICpcbiAgICogQHBhcmFtIG9wZXJhdGlvbiAtIFRoZSBvcGVyYXRpb24gYmVpbmcgY29uZmlybWVkXG4gICAqIEBwYXJhbSBwZXJtaXNzaW9uTGV2ZWwgLSBUaGUgcGVybWlzc2lvbiBsZXZlbCBiZWluZyBjb25maXJtZWRcbiAgICogQHBhcmFtIGVsZW1lbnRUeXBlIC0gT3B0aW9uYWwgZWxlbWVudCB0eXBlIHNjb3BlXG4gICAqL1xuICByZWNvcmRDb25maXJtYXRpb24oXG4gICAgb3BlcmF0aW9uOiBzdHJpbmcsXG4gICAgcGVybWlzc2lvbkxldmVsOiBQZXJtaXNzaW9uTGV2ZWwuQ09ORklSTV9TRVNTSU9OIHwgUGVybWlzc2lvbkxldmVsLkNPTkZJUk1fU0lOR0xFX1VTRSxcbiAgICBlbGVtZW50VHlwZT86IHN0cmluZ1xuICApOiB2b2lkIHtcbiAgICB0aGlzLnRvdWNoKCk7XG5cbiAgICAvLyBFbmZvcmNlIG1heCBjb25maXJtYXRpb25zIChMUlUgZXZpY3Rpb24pXG4gICAgaWYgKHRoaXMuc3RhdGUuY29uZmlybWF0aW9ucy5zaXplID49IHRoaXMubWF4Q29uZmlybWF0aW9ucykge1xuICAgICAgLy8gUmVtb3ZlIG9sZGVzdCBjb25maXJtYXRpb25cbiAgICAgIGNvbnN0IG9sZGVzdEtleSA9IHRoaXMuc3RhdGUuY29uZmlybWF0aW9ucy5rZXlzKCkubmV4dCgpLnZhbHVlO1xuICAgICAgaWYgKG9sZGVzdEtleSkge1xuICAgICAgICB0aGlzLnN0YXRlLmNvbmZpcm1hdGlvbnMuZGVsZXRlKG9sZGVzdEtleSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3Qga2V5ID0gdGhpcy5nZXRDb25maXJtYXRpb25LZXkob3BlcmF0aW9uLCBlbGVtZW50VHlwZSk7XG4gICAgdGhpcy5zdGF0ZS5jb25maXJtYXRpb25zLnNldChrZXksIHtcbiAgICAgIG9wZXJhdGlvbixcbiAgICAgIGNvbmZpcm1lZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBwZXJtaXNzaW9uTGV2ZWwsXG4gICAgICB1c2VDb3VudDogMCxcbiAgICAgIGVsZW1lbnRUeXBlLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGFuIG9wZXJhdGlvbiBoYXMgYSB2YWxpZCBzZXNzaW9uIGNvbmZpcm1hdGlvbi5cbiAgICogRm9yIENPTkZJUk1fU0lOR0xFX1VTRSwgdGhpcyBpbnZhbGlkYXRlcyB0aGUgY29uZmlybWF0aW9uIGFmdGVyIGNoZWNraW5nLlxuICAgKlxuICAgKiBSQUNFIENPTkRJVElPTiBOT1RFOlxuICAgKiBUaGUgY2hlY2stdGhlbi1kZWxldGUgZm9yIENPTkZJUk1fU0lOR0xFX1VTRSBpcyBzYWZlIGJlY2F1c2U6XG4gICAqIDEuIE5vZGUuanMgaXMgc2luZ2xlLXRocmVhZGVkIC0gbm8gY29uY3VycmVudCBhY2Nlc3MgdG8gTWFwXG4gICAqIDIuIE1DUCByZXF1ZXN0cyBhcmUgcHJvY2Vzc2VkIHNlcXVlbnRpYWxseSBwZXIgY29ubmVjdGlvblxuICAgKiAzLiBFYWNoIHNlc3Npb24gaW5zdGFuY2UgaXMgdGllZCB0byBhIHNpbmdsZSBjbGllbnQgY29ubmVjdGlvblxuICAgKiBJZiB0aGlzIGNvZGUgaXMgZXZlciB1c2VkIGluIGEgbXVsdGktdGhyZWFkZWQgY29udGV4dCBvciB3aXRoIHNoYXJlZFxuICAgKiBzdGF0ZSBhY3Jvc3MgcHJvY2Vzc2VzLCBhdG9taWMgb3BlcmF0aW9ucyB3b3VsZCBiZSByZXF1aXJlZC5cbiAgICpcbiAgICogQHBhcmFtIG9wZXJhdGlvbiAtIFRoZSBvcGVyYXRpb24gdG8gY2hlY2tcbiAgICogQHBhcmFtIGVsZW1lbnRUeXBlIC0gT3B0aW9uYWwgZWxlbWVudCB0eXBlIHNjb3BlXG4gICAqIEByZXR1cm5zIFRoZSBjb25maXJtYXRpb24gcmVjb3JkIGlmIHZhbGlkLCB1bmRlZmluZWQgb3RoZXJ3aXNlXG4gICAqL1xuICBjaGVja0NvbmZpcm1hdGlvbihvcGVyYXRpb246IHN0cmluZywgZWxlbWVudFR5cGU/OiBzdHJpbmcpOiBDb25maXJtYXRpb25SZWNvcmQgfCB1bmRlZmluZWQge1xuICAgIHRoaXMudG91Y2goKTtcblxuICAgIGNvbnN0IGtleSA9IHRoaXMuZ2V0Q29uZmlybWF0aW9uS2V5KG9wZXJhdGlvbiwgZWxlbWVudFR5cGUpO1xuICAgIGxldCBjb25maXJtYXRpb24gPSB0aGlzLnN0YXRlLmNvbmZpcm1hdGlvbnMuZ2V0KGtleSk7XG5cbiAgICAvLyBGYWxsIGJhY2sgdG8gdW5zY29wZWQgY29uZmlybWF0aW9uIHdoZW4gZWxlbWVudC10eXBlLXNjb3BlZCBrZXkgbm90IGZvdW5kLlxuICAgIC8vIEEgc2Vzc2lvbi13aWRlIGNvbmZpcm1hdGlvbiBmb3IgXCJjcmVhdGVfZWxlbWVudFwiIGNvdmVycyBcImNyZWF0ZV9lbGVtZW50OnNraWxsXCIgZXRjLlxuICAgIGlmICghY29uZmlybWF0aW9uICYmIGVsZW1lbnRUeXBlKSB7XG4gICAgICBjb25maXJtYXRpb24gPSB0aGlzLnN0YXRlLmNvbmZpcm1hdGlvbnMuZ2V0KG9wZXJhdGlvbik7XG4gICAgfVxuXG4gICAgaWYgKCFjb25maXJtYXRpb24pIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgLy8gSW5jcmVtZW50IHVzZSBjb3VudFxuICAgIGNvbmZpcm1hdGlvbi51c2VDb3VudCsrO1xuXG4gICAgLy8gRm9yIENPTkZJUk1fU0lOR0xFX1VTRSwgaW52YWxpZGF0ZSBhZnRlciBmaXJzdCB1c2VcbiAgICBpZiAoY29uZmlybWF0aW9uLnBlcm1pc3Npb25MZXZlbCA9PT0gJ0NPTkZJUk1fU0lOR0xFX1VTRScpIHtcbiAgICAgIC8vIERlbGV0ZSB3aGljaGV2ZXIga2V5IG1hdGNoZWQgKHNjb3BlZCBvciB1bnNjb3BlZClcbiAgICAgIGlmICh0aGlzLnN0YXRlLmNvbmZpcm1hdGlvbnMuaGFzKGtleSkpIHtcbiAgICAgICAgdGhpcy5zdGF0ZS5jb25maXJtYXRpb25zLmRlbGV0ZShrZXkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5zdGF0ZS5jb25maXJtYXRpb25zLmRlbGV0ZShvcGVyYXRpb24pO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBjb25maXJtYXRpb247XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgYW4gb3BlcmF0aW9uIGhhcyBhIHNlc3Npb24gY29uZmlybWF0aW9uIFdJVEhPVVQgY29uc3VtaW5nIGl0LlxuICAgKiBVc2UgdGhpcyB0byBjaGVjayBzdGF0dXMgd2l0aG91dCBhZmZlY3RpbmcgdGhlIGNvbmZpcm1hdGlvbiBzdGF0ZS5cbiAgICpcbiAgICogQHBhcmFtIG9wZXJhdGlvbiAtIFRoZSBvcGVyYXRpb24gdG8gY2hlY2tcbiAgICogQHBhcmFtIGVsZW1lbnRUeXBlIC0gT3B0aW9uYWwgZWxlbWVudCB0eXBlIHNjb3BlXG4gICAqIEByZXR1cm5zIFRoZSBjb25maXJtYXRpb24gcmVjb3JkIGlmIGV4aXN0cywgdW5kZWZpbmVkIG90aGVyd2lzZVxuICAgKi9cbiAgcGVla0NvbmZpcm1hdGlvbihvcGVyYXRpb246IHN0cmluZywgZWxlbWVudFR5cGU/OiBzdHJpbmcpOiBDb25maXJtYXRpb25SZWNvcmQgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IGtleSA9IHRoaXMuZ2V0Q29uZmlybWF0aW9uS2V5KG9wZXJhdGlvbiwgZWxlbWVudFR5cGUpO1xuICAgIHJldHVybiB0aGlzLnN0YXRlLmNvbmZpcm1hdGlvbnMuZ2V0KGtleSk7XG4gIH1cblxuICAvKipcbiAgICogUmV2b2tlIGEgY29uZmlybWF0aW9uIGZvciBhbiBvcGVyYXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSBvcGVyYXRpb24gLSBUaGUgb3BlcmF0aW9uIHRvIHJldm9rZVxuICAgKiBAcGFyYW0gZWxlbWVudFR5cGUgLSBPcHRpb25hbCBlbGVtZW50IHR5cGUgc2NvcGVcbiAgICogQHJldHVybnMgdHJ1ZSBpZiBhIGNvbmZpcm1hdGlvbiB3YXMgcmV2b2tlZFxuICAgKi9cbiAgcmV2b2tlQ29uZmlybWF0aW9uKG9wZXJhdGlvbjogc3RyaW5nLCBlbGVtZW50VHlwZT86IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHRoaXMudG91Y2goKTtcbiAgICBjb25zdCBrZXkgPSB0aGlzLmdldENvbmZpcm1hdGlvbktleShvcGVyYXRpb24sIGVsZW1lbnRUeXBlKTtcbiAgICByZXR1cm4gdGhpcy5zdGF0ZS5jb25maXJtYXRpb25zLmRlbGV0ZShrZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldm9rZSBhbGwgY29uZmlybWF0aW9ucyBmb3IgdGhpcyBzZXNzaW9uLlxuICAgKiBVc2VmdWwgd2hlbiBzZWN1cml0eS1zZW5zaXRpdmUgY2hhbmdlcyBvY2N1ci5cbiAgICovXG4gIHJldm9rZUFsbENvbmZpcm1hdGlvbnMoKTogdm9pZCB7XG4gICAgdGhpcy50b3VjaCgpO1xuICAgIHRoaXMuc3RhdGUuY29uZmlybWF0aW9ucy5jbGVhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbGwgYWN0aXZlIGNvbmZpcm1hdGlvbnMgZm9yIHRoaXMgc2Vzc2lvbi5cbiAgICogVXNlZnVsIGZvciBkZWJ1Z2dpbmcgYW5kIGF1ZGl0IGxvZ2dpbmcuXG4gICAqL1xuICBnZXRBY3RpdmVDb25maXJtYXRpb25zKCk6IENvbmZpcm1hdGlvblJlY29yZFtdIHtcbiAgICByZXR1cm4gQXJyYXkuZnJvbSh0aGlzLnN0YXRlLmNvbmZpcm1hdGlvbnMudmFsdWVzKCkpO1xuICB9XG5cbiAgLy8g4pSA4pSAIENMSSBBcHByb3ZhbCBTdG9yZSAoSXNzdWUgIzYyNSBQaGFzZSAzKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKipcbiAgICogQ3JlYXRlIGEgQ0xJIGFwcHJvdmFsIHJlcXVlc3QuXG4gICAqIFJldHVybnMgYSB1bmlxdWUgcmVxdWVzdCBJRCAoZm9ybWF0OiBjbGktPFVVSUR2ND4pLlxuICAgKi9cbiAgY3JlYXRlQ2xpQXBwcm92YWxSZXF1ZXN0KFxuICAgIHRvb2xOYW1lOiBzdHJpbmcsXG4gICAgdG9vbElucHV0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICByaXNrTGV2ZWw6IHN0cmluZyxcbiAgICByaXNrU2NvcmU6IG51bWJlcixcbiAgICBpcnJldmVyc2libGU6IGJvb2xlYW4sXG4gICAgZGVueVJlYXNvbjogc3RyaW5nLFxuICAgIHBvbGljeVNvdXJjZT86IHN0cmluZyxcbiAgICB0dGxNcz86IG51bWJlcixcbiAgKTogc3RyaW5nIHtcbiAgICB0aGlzLnRvdWNoKCk7XG4gICAgdGhpcy5leHBpcmVTdGFsZUFwcHJvdmFscyh0cnVlKTsgLy8gRm9yY2Ugc3dlZXAgb24gd3JpdGUgcGF0aCB0byBlbnN1cmUgY2FwYWNpdHlcblxuICAgIC8vIExSVSBldmljdGlvbiBhdCBtYXggY2FwYWNpdHlcbiAgICBpZiAodGhpcy5zdGF0ZS5jbGlBcHByb3ZhbHMuc2l6ZSA+PSB0aGlzLm1heENsaUFwcHJvdmFscykge1xuICAgICAgY29uc3Qgb2xkZXN0S2V5ID0gdGhpcy5zdGF0ZS5jbGlBcHByb3ZhbHMua2V5cygpLm5leHQoKS52YWx1ZTtcbiAgICAgIGlmIChvbGRlc3RLZXkpIHtcbiAgICAgICAgdGhpcy5zdGF0ZS5jbGlBcHByb3ZhbHMuZGVsZXRlKG9sZGVzdEtleSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgcmVxdWVzdElkID0gYGNsaS0ke3JhbmRvbVVVSUQoKX1gO1xuICAgIC8vIENsYW1wIHR0bE1zIHRvIHZhbGlkIGJvdW5kcyAoMXMtMjRoKSBpZiBwcm92aWRlZFxuICAgIGNvbnN0IGNsYW1wZWRUdGwgPSB0dGxNcyAhPSBudWxsXG4gICAgICA/IE1hdGgubWF4KE1JTl9BUFBST1ZBTF9UVExfTVMsIE1hdGgubWluKE1BWF9BUFBST1ZBTF9UVExfTVMsIHR0bE1zKSlcbiAgICAgIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IHJlY29yZDogQ2xpQXBwcm92YWxSZWNvcmQgPSB7XG4gICAgICByZXF1ZXN0SWQsXG4gICAgICB0b29sTmFtZSxcbiAgICAgIHRvb2xJbnB1dCxcbiAgICAgIHJpc2tMZXZlbCxcbiAgICAgIHJpc2tTY29yZSxcbiAgICAgIGlycmV2ZXJzaWJsZSxcbiAgICAgIHJlcXVlc3RlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBjb25zdW1lZDogZmFsc2UsXG4gICAgICBzY29wZTogJ3NpbmdsZScsXG4gICAgICBkZW55UmVhc29uLFxuICAgICAgcG9saWN5U291cmNlLFxuICAgICAgdHRsTXM6IGNsYW1wZWRUdGwsXG4gICAgfTtcbiAgICB0aGlzLnN0YXRlLmNsaUFwcHJvdmFscy5zZXQocmVxdWVzdElkLCByZWNvcmQpO1xuICAgIHJldHVybiByZXF1ZXN0SWQ7XG4gIH1cblxuICAvKipcbiAgICogQXBwcm92ZSBhIHBlbmRpbmcgQ0xJIGFwcHJvdmFsIHJlcXVlc3QuXG4gICAqIFNldHMgYXBwcm92ZWRBdCwgYW5kIHByb21vdGVzIHRvIHNlc3Npb24gYXBwcm92YWxzIGlmIHRvb2xfc2Vzc2lvbiBzY29wZS5cbiAgICpcbiAgICogQHJldHVybnMgVGhlIGFwcHJvdmVkIHJlY29yZCwgb3IgdW5kZWZpbmVkIGlmIG5vdCBmb3VuZFxuICAgKi9cbiAgYXBwcm92ZUNsaVJlcXVlc3QocmVxdWVzdElkOiBzdHJpbmcsIHNjb3BlOiBDbGlBcHByb3ZhbFNjb3BlID0gJ3NpbmdsZScpOiBDbGlBcHByb3ZhbFJlY29yZCB8IHVuZGVmaW5lZCB7XG4gICAgdGhpcy50b3VjaCgpO1xuICAgIGNvbnN0IHJlY29yZCA9IHRoaXMuc3RhdGUuY2xpQXBwcm92YWxzLmdldChyZXF1ZXN0SWQpO1xuICAgIGlmICghcmVjb3JkIHx8IHJlY29yZC5hcHByb3ZlZEF0KSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIHJlY29yZC5hcHByb3ZlZEF0ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuICAgIHJlY29yZC5zY29wZSA9IHNjb3BlO1xuXG4gICAgLy8gUHJvbW90ZSB0byBzZXNzaW9uIGFwcHJvdmFscyBpZiB0b29sX3Nlc3Npb24gc2NvcGVcbiAgICBpZiAoc2NvcGUgPT09ICd0b29sX3Nlc3Npb24nKSB7XG4gICAgICB0aGlzLnN0YXRlLmNsaVNlc3Npb25BcHByb3ZhbHMuc2V0KHJlY29yZC50b29sTmFtZSwgcmVjb3JkKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVjb3JkO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGEgQ0xJIHRvb2wgY2FsbCBoYXMgYSB2YWxpZCBhcHByb3ZhbC5cbiAgICogQ2hlY2tzIHNlc3Npb24gYXBwcm92YWxzIGZpcnN0IChmYXN0IHBhdGgpLCB0aGVuIGluZGl2aWR1YWwgYXBwcm92YWxzLlxuICAgKiBDb25zdW1lcyBzaW5nbGUtc2NvcGUgYXBwcm92YWxzIG9uIHVzZS5cbiAgICpcbiAgICogQHJldHVybnMgVGhlIG1hdGNoaW5nIGFwcHJvdmFsIHJlY29yZCwgb3IgdW5kZWZpbmVkXG4gICAqL1xuICBjaGVja0NsaUFwcHJvdmFsKHRvb2xOYW1lOiBzdHJpbmcsIF90b29sSW5wdXQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogQ2xpQXBwcm92YWxSZWNvcmQgfCB1bmRlZmluZWQge1xuICAgIHRoaXMudG91Y2goKTtcbiAgICB0aGlzLmV4cGlyZVN0YWxlQXBwcm92YWxzKCk7XG5cbiAgICAvLyBGYXN0IHBhdGg6IGNoZWNrIHNlc3Npb24tc2NvcGVkIGFwcHJvdmFscyBieSB0b29sIG5hbWVcbiAgICBjb25zdCBzZXNzaW9uQXBwcm92YWwgPSB0aGlzLnN0YXRlLmNsaVNlc3Npb25BcHByb3ZhbHMuZ2V0KHRvb2xOYW1lKTtcbiAgICBpZiAoc2Vzc2lvbkFwcHJvdmFsKSB7XG4gICAgICByZXR1cm4gc2Vzc2lvbkFwcHJvdmFsO1xuICAgIH1cblxuICAgIC8vIENoZWNrIGluZGl2aWR1YWwgYXBwcm92YWxzXG4gICAgZm9yIChjb25zdCBbLCByZWNvcmRdIG9mIHRoaXMuc3RhdGUuY2xpQXBwcm92YWxzKSB7XG4gICAgICBpZiAocmVjb3JkLnRvb2xOYW1lID09PSB0b29sTmFtZSAmJiByZWNvcmQuYXBwcm92ZWRBdCAmJiAhcmVjb3JkLmNvbnN1bWVkKSB7XG4gICAgICAgIGlmIChyZWNvcmQuc2NvcGUgPT09ICdzaW5nbGUnKSB7XG4gICAgICAgICAgcmVjb3JkLmNvbnN1bWVkID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVjb3JkO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGFsbCBwZW5kaW5nICh1bmFwcHJvdmVkKSBDTEkgYXBwcm92YWwgcmVxdWVzdHMuXG4gICAqIEZvcmNlcyBleHBpcnkgc3dlZXAgdG8gZW5zdXJlIGFjY3VyYXRlIHVzZXItZmFjaW5nIHJlc3VsdHMuXG4gICAqL1xuICBnZXRQZW5kaW5nQ2xpQXBwcm92YWxzKCk6IENsaUFwcHJvdmFsUmVjb3JkW10ge1xuICAgIHRoaXMuZXhwaXJlU3RhbGVBcHByb3ZhbHModHJ1ZSk7XG4gICAgY29uc3QgcGVuZGluZzogQ2xpQXBwcm92YWxSZWNvcmRbXSA9IFtdO1xuICAgIGZvciAoY29uc3QgWywgcmVjb3JkXSBvZiB0aGlzLnN0YXRlLmNsaUFwcHJvdmFscykge1xuICAgICAgaWYgKCFyZWNvcmQuYXBwcm92ZWRBdCkge1xuICAgICAgICBwZW5kaW5nLnB1c2gocmVjb3JkKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHBlbmRpbmc7XG4gIH1cblxuICAvKipcbiAgICogRXhwaXJlIHN0YWxlIGFwcHJvdmFsIHJlcXVlc3RzLlxuICAgKiBVc2VzIHBlci1yZWNvcmQgdHRsTXMgaWYgc2V0LCBvdGhlcndpc2UgREVGQVVMVF9BUFBST1ZBTF9UVExfTVMgKDUgbWludXRlcykuXG4gICAqXG4gICAqIEBwYXJhbSBmb3JjZSAtIFNraXAgdGhyb3R0bGUgY2hlY2sgKHVzZWQgYnkgd3JpdGUgcGF0aHMgdGhhdCBtdXN0IGVuc3VyZSBjYXBhY2l0eSlcbiAgICovXG4gIHByaXZhdGUgZXhwaXJlU3RhbGVBcHByb3ZhbHMoZm9yY2UgPSBmYWxzZSk6IHZvaWQge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgaWYgKCFmb3JjZSAmJiBub3cgLSB0aGlzLmxhc3RFeHBpcnlTd2VlcCA8IEVYUElSWV9TV0VFUF9JTlRFUlZBTF9NUykgcmV0dXJuO1xuICAgIHRoaXMubGFzdEV4cGlyeVN3ZWVwID0gbm93O1xuXG4gICAgZm9yIChjb25zdCBba2V5LCByZWNvcmRdIG9mIHRoaXMuc3RhdGUuY2xpQXBwcm92YWxzKSB7XG4gICAgICBjb25zdCB0dGwgPSByZWNvcmQudHRsTXMgPz8gREVGQVVMVF9BUFBST1ZBTF9UVExfTVM7XG4gICAgICBjb25zdCBhZ2UgPSBub3cgLSBuZXcgRGF0ZShyZWNvcmQucmVxdWVzdGVkQXQpLmdldFRpbWUoKTtcbiAgICAgIC8vIEV2aWN0IHN0YWxlIHBlbmRpbmcgcmVxdWVzdHMgQU5EIGNvbnN1bWVkIHNpbmdsZS11c2UgYXBwcm92YWxzICgjMTc4MilcbiAgICAgIGlmIChhZ2UgPiB0dGwgJiYgKCFyZWNvcmQuYXBwcm92ZWRBdCB8fCByZWNvcmQuY29uc3VtZWQpKSB7XG4gICAgICAgIHRoaXMuc3RhdGUuY2xpQXBwcm92YWxzLmRlbGV0ZShrZXkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYSBzdW1tYXJ5IG9mIHRoZSBzZXNzaW9uIHN0YXRlLlxuICAgKiBTYWZlIHRvIGV4cG9zZSBmb3IgZGVidWdnaW5nIHdpdGhvdXQgcmV2ZWFsaW5nIHNlbnNpdGl2ZSBkYXRhLlxuICAgKi9cbiAgZ2V0U3VtbWFyeSgpOiB7XG4gICAgc2Vzc2lvbklkOiBzdHJpbmc7XG4gICAgY2xpZW50SW5mbz86IENsaWVudEluZm87XG4gICAgY3JlYXRlZEF0OiBzdHJpbmc7XG4gICAgbGFzdEFjdGl2aXR5OiBzdHJpbmc7XG4gICAgY29uZmlybWF0aW9uQ291bnQ6IG51bWJlcjtcbiAgICBjbGlBcHByb3ZhbENvdW50OiBudW1iZXI7XG4gICAgcGVybWlzc2lvblByb21wdEFjdGl2ZTogYm9vbGVhbjtcbiAgfSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNlc3Npb25JZDogdGhpcy5zdGF0ZS5zZXNzaW9uSWQsXG4gICAgICBjbGllbnRJbmZvOiB0aGlzLnN0YXRlLmNsaWVudEluZm8sXG4gICAgICBjcmVhdGVkQXQ6IHRoaXMuc3RhdGUuY3JlYXRlZEF0LFxuICAgICAgbGFzdEFjdGl2aXR5OiB0aGlzLnN0YXRlLmxhc3RBY3Rpdml0eSxcbiAgICAgIGNvbmZpcm1hdGlvbkNvdW50OiB0aGlzLnN0YXRlLmNvbmZpcm1hdGlvbnMuc2l6ZSxcbiAgICAgIGNsaUFwcHJvdmFsQ291bnQ6IHRoaXMuc3RhdGUuY2xpQXBwcm92YWxzLnNpemUsXG4gICAgICBwZXJtaXNzaW9uUHJvbXB0QWN0aXZlOiB0aGlzLnN0YXRlLnBlcm1pc3Npb25Qcm9tcHRBY3RpdmUsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBhIHVuaXF1ZSBrZXkgZm9yIGNvbmZpcm1hdGlvbiBsb29rdXBzLlxuICAgKiBDb21iaW5lcyBvcGVyYXRpb24gYW5kIG9wdGlvbmFsIGVsZW1lbnQgdHlwZS5cbiAgICovXG4gIHByaXZhdGUgZ2V0Q29uZmlybWF0aW9uS2V5KG9wZXJhdGlvbjogc3RyaW5nLCBlbGVtZW50VHlwZT86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGVsZW1lbnRUeXBlID8gYCR7b3BlcmF0aW9ufToke2VsZW1lbnRUeXBlfWAgOiBvcGVyYXRpb247XG4gIH1cbn1cbiJdfQ==