@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.
132 lines • 16.4 kB
JavaScript
/**
* Agent Tool Policy Translator (Issue #449)
*
* Translates an agent's `tools` configuration (allowed/denied endpoint names)
* into an `ElementGatekeeperPolicy` that the Gatekeeper can enforce.
*
* This bridges the informational `AgentToolConfig` with the enforceable
* Gatekeeper policy system, giving agent tool restrictions programmatic teeth.
*
* **Policy precedence:** This translator is only used when an agent has no
* explicit `gatekeeper` policy. If both `gatekeeper` and `tools` are present,
* the explicit `gatekeeper` policy takes precedence (see MCPAQLHandler).
*
* @module AgentToolPolicyTranslator
*/
import { getOperationsForEndpoint } from '../OperationRouter.js';
/**
* Operations that are exempt from tools-based deny synthesis.
* These must always be available for agent lifecycle and safety flows.
*
* - **Execution lifecycle**: An agent must be able to manage its own execution
* (execute, get state, update state, complete, continue).
* - **Safety system**: An agent must interact with Gatekeeper confirmation
* and danger zone verification flows.
*/
const EXEMPT_OPERATIONS = new Set([
// Execution lifecycle — agent must manage its own execution
'execute_agent',
'get_execution_state',
'record_execution_step',
'complete_execution',
'continue_execution',
'abort_execution', // Issue #249
'get_gathered_data', // Issue #68
'prepare_handoff', // Issue #69
'resume_from_handoff', // Issue #69
// Safety system — agent must interact with Gatekeeper/verification
'confirm_operation',
'verify_challenge',
'release_deadlock',
'permission_prompt', // Issue #625: CLI-level permission delegation
'approve_cli_permission', // Issue #625 Phase 3: CLI approval workflow
'get_pending_cli_approvals', // Issue #625 Phase 3: CLI approval visibility
]);
/**
* Maps MCP-AQL tool names (as used in `AgentToolConfig.allowed/denied`)
* to their corresponding CRUD endpoint types in the OperationRouter.
*
* Tool names follow the format `mcp_aql_{endpoint}` and map to one of
* the five CRUD endpoints: CREATE, READ, UPDATE, DELETE, EXECUTE.
*/
const ENDPOINT_TOOL_MAP = new Map([
['mcp_aql_create', 'CREATE'],
['mcp_aql_read', 'READ'],
['mcp_aql_update', 'UPDATE'],
['mcp_aql_delete', 'DELETE'],
['mcp_aql_execute', 'EXECUTE'],
]);
/**
* Translates an {@link AgentToolConfig} into an {@link ElementGatekeeperPolicy}.
*
* The translation works as follows:
* - If `tools.allowed` is specified, all operations **not** in the allowed
* endpoints are added to the deny list (allowlist → denylist inversion).
* - If `tools.denied` is specified, all operations from the denied endpoints
* are added to the deny list directly.
* - Both `allowed` and `denied` can be specified simultaneously; their effects
* are cumulative (union of denied operations).
*
* Lifecycle and safety operations ({@link EXEMPT_OPERATIONS}) are **never**
* included in the synthesized deny list, regardless of the tool config.
*
* @param toolConfig - The agent's tool configuration containing allowed/denied endpoint names
* @returns A synthesized {@link ElementGatekeeperPolicy} with a deny list,
* or `undefined` if the config produces no restrictions
*
* @example
* ```typescript
* // Agent that can only read — all other endpoints denied
* translateToolConfigToPolicy({ allowed: ['mcp_aql_read'] });
* // → { deny: ['addEntry', 'clear', 'create_element', 'delete_element', 'edit_element', ...] }
*
* // Agent that cannot delete — only delete operations denied
* translateToolConfigToPolicy({ allowed: ['mcp_aql_create', 'mcp_aql_read', 'mcp_aql_update', 'mcp_aql_execute'] });
* // → { deny: ['clear', 'delete_element'] }
*
* // No restrictions — returns undefined
* translateToolConfigToPolicy({ allowed: ['mcp_aql_create', 'mcp_aql_read', 'mcp_aql_update', 'mcp_aql_delete', 'mcp_aql_execute'] });
* // → undefined
* ```
*/
export function translateToolConfigToPolicy(toolConfig) {
const denySet = new Set();
if (toolConfig.allowed && toolConfig.allowed.length > 0) {
// Compute the set of allowed operations from allowed endpoints
const allowedOps = new Set();
for (const toolName of toolConfig.allowed) {
const endpoint = ENDPOINT_TOOL_MAP.get(toolName);
if (endpoint) {
for (const op of getOperationsForEndpoint(endpoint)) {
allowedOps.add(op);
}
}
}
// Everything NOT in the allowed set gets denied (except exempt operations)
for (const endpoint of ENDPOINT_TOOL_MAP.values()) {
for (const op of getOperationsForEndpoint(endpoint)) {
if (!allowedOps.has(op) && !EXEMPT_OPERATIONS.has(op)) {
denySet.add(op);
}
}
}
}
if (toolConfig.denied && toolConfig.denied.length > 0) {
// Add all operations from denied endpoints
for (const toolName of toolConfig.denied) {
const endpoint = ENDPOINT_TOOL_MAP.get(toolName);
if (endpoint) {
for (const op of getOperationsForEndpoint(endpoint)) {
if (!EXEMPT_OPERATIONS.has(op)) {
denySet.add(op);
}
}
}
}
}
if (denySet.size === 0) {
return undefined;
}
return { deny: Array.from(denySet).sort((a, b) => a.localeCompare(b)) };
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWdlbnRUb29sUG9saWN5VHJhbnNsYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9oYW5kbGVycy9tY3AtYXFsL3BvbGljaWVzL0FnZW50VG9vbFBvbGljeVRyYW5zbGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFJSCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdqRTs7Ozs7Ozs7R0FRRztBQUNILE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLENBQUM7SUFDaEMsNERBQTREO0lBQzVELGVBQWU7SUFDZixxQkFBcUI7SUFDckIsdUJBQXVCO0lBQ3ZCLG9CQUFvQjtJQUNwQixvQkFBb0I7SUFDcEIsaUJBQWlCLEVBQUUsYUFBYTtJQUNoQyxtQkFBbUIsRUFBRSxZQUFZO0lBQ2pDLGlCQUFpQixFQUFFLFlBQVk7SUFDL0IscUJBQXFCLEVBQUUsWUFBWTtJQUNuQyxtRUFBbUU7SUFDbkUsbUJBQW1CO0lBQ25CLGtCQUFrQjtJQUNsQixrQkFBa0I7SUFDbEIsbUJBQW1CLEVBQUUsOENBQThDO0lBQ25FLHdCQUF3QixFQUFFLDRDQUE0QztJQUN0RSwyQkFBMkIsRUFBRSw4Q0FBOEM7Q0FDNUUsQ0FBQyxDQUFDO0FBRUg7Ozs7OztHQU1HO0FBQ0gsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsQ0FBdUI7SUFDdEQsQ0FBQyxnQkFBZ0IsRUFBRSxRQUFRLENBQUM7SUFDNUIsQ0FBQyxjQUFjLEVBQUUsTUFBTSxDQUFDO0lBQ3hCLENBQUMsZ0JBQWdCLEVBQUUsUUFBUSxDQUFDO0lBQzVCLENBQUMsZ0JBQWdCLEVBQUUsUUFBUSxDQUFDO0lBQzVCLENBQUMsaUJBQWlCLEVBQUUsU0FBUyxDQUFDO0NBQy9CLENBQUMsQ0FBQztBQUVIOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdDRztBQUNILE1BQU0sVUFBVSwyQkFBMkIsQ0FDekMsVUFBMkI7SUFFM0IsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztJQUVsQyxJQUFJLFVBQVUsQ0FBQyxPQUFPLElBQUksVUFBVSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDeEQsK0RBQStEO1FBQy9ELE1BQU0sVUFBVSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7UUFDckMsS0FBSyxNQUFNLFFBQVEsSUFBSSxVQUFVLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDMUMsTUFBTSxRQUFRLEdBQUcsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ2pELElBQUksUUFBUSxFQUFFLENBQUM7Z0JBQ2IsS0FBSyxNQUFNLEVBQUUsSUFBSSx3QkFBd0IsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO29CQUNwRCxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUNyQixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFFRCwyRUFBMkU7UUFDM0UsS0FBSyxNQUFNLFFBQVEsSUFBSSxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDO1lBQ2xELEtBQUssTUFBTSxFQUFFLElBQUksd0JBQXdCLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztnQkFDcEQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQztvQkFDdEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDbEIsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVELElBQUksVUFBVSxDQUFDLE1BQU0sSUFBSSxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN0RCwyQ0FBMkM7UUFDM0MsS0FBSyxNQUFNLFFBQVEsSUFBSSxVQUFVLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDekMsTUFBTSxRQUFRLEdBQUcsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ2pELElBQUksUUFBUSxFQUFFLENBQUM7Z0JBQ2IsS0FBSyxNQUFNLEVBQUUsSUFBSSx3QkFBd0IsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO29CQUNwRCxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7d0JBQy9CLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBQ2xCLENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVELElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN2QixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQsT0FBTyxFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0FBQzFFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEFnZW50IFRvb2wgUG9saWN5IFRyYW5zbGF0b3IgKElzc3VlICM0NDkpXG4gKlxuICogVHJhbnNsYXRlcyBhbiBhZ2VudCdzIGB0b29sc2AgY29uZmlndXJhdGlvbiAoYWxsb3dlZC9kZW5pZWQgZW5kcG9pbnQgbmFtZXMpXG4gKiBpbnRvIGFuIGBFbGVtZW50R2F0ZWtlZXBlclBvbGljeWAgdGhhdCB0aGUgR2F0ZWtlZXBlciBjYW4gZW5mb3JjZS5cbiAqXG4gKiBUaGlzIGJyaWRnZXMgdGhlIGluZm9ybWF0aW9uYWwgYEFnZW50VG9vbENvbmZpZ2Agd2l0aCB0aGUgZW5mb3JjZWFibGVcbiAqIEdhdGVrZWVwZXIgcG9saWN5IHN5c3RlbSwgZ2l2aW5nIGFnZW50IHRvb2wgcmVzdHJpY3Rpb25zIHByb2dyYW1tYXRpYyB0ZWV0aC5cbiAqXG4gKiAqKlBvbGljeSBwcmVjZWRlbmNlOioqIFRoaXMgdHJhbnNsYXRvciBpcyBvbmx5IHVzZWQgd2hlbiBhbiBhZ2VudCBoYXMgbm9cbiAqIGV4cGxpY2l0IGBnYXRla2VlcGVyYCBwb2xpY3kuIElmIGJvdGggYGdhdGVrZWVwZXJgIGFuZCBgdG9vbHNgIGFyZSBwcmVzZW50LFxuICogdGhlIGV4cGxpY2l0IGBnYXRla2VlcGVyYCBwb2xpY3kgdGFrZXMgcHJlY2VkZW5jZSAoc2VlIE1DUEFRTEhhbmRsZXIpLlxuICpcbiAqIEBtb2R1bGUgQWdlbnRUb29sUG9saWN5VHJhbnNsYXRvclxuICovXG5cbmltcG9ydCB0eXBlIHsgRWxlbWVudEdhdGVrZWVwZXJQb2xpY3kgfSBmcm9tICcuLi9HYXRla2VlcGVyVHlwZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBBZ2VudFRvb2xDb25maWcgfSBmcm9tICcuLi8uLi8uLi9lbGVtZW50cy9hZ2VudHMvdHlwZXMuanMnO1xuaW1wb3J0IHsgZ2V0T3BlcmF0aW9uc0ZvckVuZHBvaW50IH0gZnJvbSAnLi4vT3BlcmF0aW9uUm91dGVyLmpzJztcbmltcG9ydCB0eXBlIHsgQ1JVREVuZHBvaW50IH0gZnJvbSAnLi4vT3BlcmF0aW9uUm91dGVyLmpzJztcblxuLyoqXG4gKiBPcGVyYXRpb25zIHRoYXQgYXJlIGV4ZW1wdCBmcm9tIHRvb2xzLWJhc2VkIGRlbnkgc3ludGhlc2lzLlxuICogVGhlc2UgbXVzdCBhbHdheXMgYmUgYXZhaWxhYmxlIGZvciBhZ2VudCBsaWZlY3ljbGUgYW5kIHNhZmV0eSBmbG93cy5cbiAqXG4gKiAtICoqRXhlY3V0aW9uIGxpZmVjeWNsZSoqOiBBbiBhZ2VudCBtdXN0IGJlIGFibGUgdG8gbWFuYWdlIGl0cyBvd24gZXhlY3V0aW9uXG4gKiAgIChleGVjdXRlLCBnZXQgc3RhdGUsIHVwZGF0ZSBzdGF0ZSwgY29tcGxldGUsIGNvbnRpbnVlKS5cbiAqIC0gKipTYWZldHkgc3lzdGVtKio6IEFuIGFnZW50IG11c3QgaW50ZXJhY3Qgd2l0aCBHYXRla2VlcGVyIGNvbmZpcm1hdGlvblxuICogICBhbmQgZGFuZ2VyIHpvbmUgdmVyaWZpY2F0aW9uIGZsb3dzLlxuICovXG5jb25zdCBFWEVNUFRfT1BFUkFUSU9OUyA9IG5ldyBTZXQoW1xuICAvLyBFeGVjdXRpb24gbGlmZWN5Y2xlIOKAlCBhZ2VudCBtdXN0IG1hbmFnZSBpdHMgb3duIGV4ZWN1dGlvblxuICAnZXhlY3V0ZV9hZ2VudCcsXG4gICdnZXRfZXhlY3V0aW9uX3N0YXRlJyxcbiAgJ3JlY29yZF9leGVjdXRpb25fc3RlcCcsXG4gICdjb21wbGV0ZV9leGVjdXRpb24nLFxuICAnY29udGludWVfZXhlY3V0aW9uJyxcbiAgJ2Fib3J0X2V4ZWN1dGlvbicsIC8vIElzc3VlICMyNDlcbiAgJ2dldF9nYXRoZXJlZF9kYXRhJywgLy8gSXNzdWUgIzY4XG4gICdwcmVwYXJlX2hhbmRvZmYnLCAvLyBJc3N1ZSAjNjlcbiAgJ3Jlc3VtZV9mcm9tX2hhbmRvZmYnLCAvLyBJc3N1ZSAjNjlcbiAgLy8gU2FmZXR5IHN5c3RlbSDigJQgYWdlbnQgbXVzdCBpbnRlcmFjdCB3aXRoIEdhdGVrZWVwZXIvdmVyaWZpY2F0aW9uXG4gICdjb25maXJtX29wZXJhdGlvbicsXG4gICd2ZXJpZnlfY2hhbGxlbmdlJyxcbiAgJ3JlbGVhc2VfZGVhZGxvY2snLFxuICAncGVybWlzc2lvbl9wcm9tcHQnLCAvLyBJc3N1ZSAjNjI1OiBDTEktbGV2ZWwgcGVybWlzc2lvbiBkZWxlZ2F0aW9uXG4gICdhcHByb3ZlX2NsaV9wZXJtaXNzaW9uJywgLy8gSXNzdWUgIzYyNSBQaGFzZSAzOiBDTEkgYXBwcm92YWwgd29ya2Zsb3dcbiAgJ2dldF9wZW5kaW5nX2NsaV9hcHByb3ZhbHMnLCAvLyBJc3N1ZSAjNjI1IFBoYXNlIDM6IENMSSBhcHByb3ZhbCB2aXNpYmlsaXR5XG5dKTtcblxuLyoqXG4gKiBNYXBzIE1DUC1BUUwgdG9vbCBuYW1lcyAoYXMgdXNlZCBpbiBgQWdlbnRUb29sQ29uZmlnLmFsbG93ZWQvZGVuaWVkYClcbiAqIHRvIHRoZWlyIGNvcnJlc3BvbmRpbmcgQ1JVRCBlbmRwb2ludCB0eXBlcyBpbiB0aGUgT3BlcmF0aW9uUm91dGVyLlxuICpcbiAqIFRvb2wgbmFtZXMgZm9sbG93IHRoZSBmb3JtYXQgYG1jcF9hcWxfe2VuZHBvaW50fWAgYW5kIG1hcCB0byBvbmUgb2ZcbiAqIHRoZSBmaXZlIENSVUQgZW5kcG9pbnRzOiBDUkVBVEUsIFJFQUQsIFVQREFURSwgREVMRVRFLCBFWEVDVVRFLlxuICovXG5jb25zdCBFTkRQT0lOVF9UT09MX01BUCA9IG5ldyBNYXA8c3RyaW5nLCBDUlVERW5kcG9pbnQ+KFtcbiAgWydtY3BfYXFsX2NyZWF0ZScsICdDUkVBVEUnXSxcbiAgWydtY3BfYXFsX3JlYWQnLCAnUkVBRCddLFxuICBbJ21jcF9hcWxfdXBkYXRlJywgJ1VQREFURSddLFxuICBbJ21jcF9hcWxfZGVsZXRlJywgJ0RFTEVURSddLFxuICBbJ21jcF9hcWxfZXhlY3V0ZScsICdFWEVDVVRFJ10sXG5dKTtcblxuLyoqXG4gKiBUcmFuc2xhdGVzIGFuIHtAbGluayBBZ2VudFRvb2xDb25maWd9IGludG8gYW4ge0BsaW5rIEVsZW1lbnRHYXRla2VlcGVyUG9saWN5fS5cbiAqXG4gKiBUaGUgdHJhbnNsYXRpb24gd29ya3MgYXMgZm9sbG93czpcbiAqIC0gSWYgYHRvb2xzLmFsbG93ZWRgIGlzIHNwZWNpZmllZCwgYWxsIG9wZXJhdGlvbnMgKipub3QqKiBpbiB0aGUgYWxsb3dlZFxuICogICBlbmRwb2ludHMgYXJlIGFkZGVkIHRvIHRoZSBkZW55IGxpc3QgKGFsbG93bGlzdCDihpIgZGVueWxpc3QgaW52ZXJzaW9uKS5cbiAqIC0gSWYgYHRvb2xzLmRlbmllZGAgaXMgc3BlY2lmaWVkLCBhbGwgb3BlcmF0aW9ucyBmcm9tIHRoZSBkZW5pZWQgZW5kcG9pbnRzXG4gKiAgIGFyZSBhZGRlZCB0byB0aGUgZGVueSBsaXN0IGRpcmVjdGx5LlxuICogLSBCb3RoIGBhbGxvd2VkYCBhbmQgYGRlbmllZGAgY2FuIGJlIHNwZWNpZmllZCBzaW11bHRhbmVvdXNseTsgdGhlaXIgZWZmZWN0c1xuICogICBhcmUgY3VtdWxhdGl2ZSAodW5pb24gb2YgZGVuaWVkIG9wZXJhdGlvbnMpLlxuICpcbiAqIExpZmVjeWNsZSBhbmQgc2FmZXR5IG9wZXJhdGlvbnMgKHtAbGluayBFWEVNUFRfT1BFUkFUSU9OU30pIGFyZSAqKm5ldmVyKipcbiAqIGluY2x1ZGVkIGluIHRoZSBzeW50aGVzaXplZCBkZW55IGxpc3QsIHJlZ2FyZGxlc3Mgb2YgdGhlIHRvb2wgY29uZmlnLlxuICpcbiAqIEBwYXJhbSB0b29sQ29uZmlnIC0gVGhlIGFnZW50J3MgdG9vbCBjb25maWd1cmF0aW9uIGNvbnRhaW5pbmcgYWxsb3dlZC9kZW5pZWQgZW5kcG9pbnQgbmFtZXNcbiAqIEByZXR1cm5zIEEgc3ludGhlc2l6ZWQge0BsaW5rIEVsZW1lbnRHYXRla2VlcGVyUG9saWN5fSB3aXRoIGEgZGVueSBsaXN0LFxuICogICAgICAgICAgb3IgYHVuZGVmaW5lZGAgaWYgdGhlIGNvbmZpZyBwcm9kdWNlcyBubyByZXN0cmljdGlvbnNcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gQWdlbnQgdGhhdCBjYW4gb25seSByZWFkIOKAlCBhbGwgb3RoZXIgZW5kcG9pbnRzIGRlbmllZFxuICogdHJhbnNsYXRlVG9vbENvbmZpZ1RvUG9saWN5KHsgYWxsb3dlZDogWydtY3BfYXFsX3JlYWQnXSB9KTtcbiAqIC8vIOKGkiB7IGRlbnk6IFsnYWRkRW50cnknLCAnY2xlYXInLCAnY3JlYXRlX2VsZW1lbnQnLCAnZGVsZXRlX2VsZW1lbnQnLCAnZWRpdF9lbGVtZW50JywgLi4uXSB9XG4gKlxuICogLy8gQWdlbnQgdGhhdCBjYW5ub3QgZGVsZXRlIOKAlCBvbmx5IGRlbGV0ZSBvcGVyYXRpb25zIGRlbmllZFxuICogdHJhbnNsYXRlVG9vbENvbmZpZ1RvUG9saWN5KHsgYWxsb3dlZDogWydtY3BfYXFsX2NyZWF0ZScsICdtY3BfYXFsX3JlYWQnLCAnbWNwX2FxbF91cGRhdGUnLCAnbWNwX2FxbF9leGVjdXRlJ10gfSk7XG4gKiAvLyDihpIgeyBkZW55OiBbJ2NsZWFyJywgJ2RlbGV0ZV9lbGVtZW50J10gfVxuICpcbiAqIC8vIE5vIHJlc3RyaWN0aW9ucyDigJQgcmV0dXJucyB1bmRlZmluZWRcbiAqIHRyYW5zbGF0ZVRvb2xDb25maWdUb1BvbGljeSh7IGFsbG93ZWQ6IFsnbWNwX2FxbF9jcmVhdGUnLCAnbWNwX2FxbF9yZWFkJywgJ21jcF9hcWxfdXBkYXRlJywgJ21jcF9hcWxfZGVsZXRlJywgJ21jcF9hcWxfZXhlY3V0ZSddIH0pO1xuICogLy8g4oaSIHVuZGVmaW5lZFxuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0cmFuc2xhdGVUb29sQ29uZmlnVG9Qb2xpY3koXG4gIHRvb2xDb25maWc6IEFnZW50VG9vbENvbmZpZ1xuKTogRWxlbWVudEdhdGVrZWVwZXJQb2xpY3kgfCB1bmRlZmluZWQge1xuICBjb25zdCBkZW55U2V0ID0gbmV3IFNldDxzdHJpbmc+KCk7XG5cbiAgaWYgKHRvb2xDb25maWcuYWxsb3dlZCAmJiB0b29sQ29uZmlnLmFsbG93ZWQubGVuZ3RoID4gMCkge1xuICAgIC8vIENvbXB1dGUgdGhlIHNldCBvZiBhbGxvd2VkIG9wZXJhdGlvbnMgZnJvbSBhbGxvd2VkIGVuZHBvaW50c1xuICAgIGNvbnN0IGFsbG93ZWRPcHMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBmb3IgKGNvbnN0IHRvb2xOYW1lIG9mIHRvb2xDb25maWcuYWxsb3dlZCkge1xuICAgICAgY29uc3QgZW5kcG9pbnQgPSBFTkRQT0lOVF9UT09MX01BUC5nZXQodG9vbE5hbWUpO1xuICAgICAgaWYgKGVuZHBvaW50KSB7XG4gICAgICAgIGZvciAoY29uc3Qgb3Agb2YgZ2V0T3BlcmF0aW9uc0ZvckVuZHBvaW50KGVuZHBvaW50KSkge1xuICAgICAgICAgIGFsbG93ZWRPcHMuYWRkKG9wKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEV2ZXJ5dGhpbmcgTk9UIGluIHRoZSBhbGxvd2VkIHNldCBnZXRzIGRlbmllZCAoZXhjZXB0IGV4ZW1wdCBvcGVyYXRpb25zKVxuICAgIGZvciAoY29uc3QgZW5kcG9pbnQgb2YgRU5EUE9JTlRfVE9PTF9NQVAudmFsdWVzKCkpIHtcbiAgICAgIGZvciAoY29uc3Qgb3Agb2YgZ2V0T3BlcmF0aW9uc0ZvckVuZHBvaW50KGVuZHBvaW50KSkge1xuICAgICAgICBpZiAoIWFsbG93ZWRPcHMuaGFzKG9wKSAmJiAhRVhFTVBUX09QRVJBVElPTlMuaGFzKG9wKSkge1xuICAgICAgICAgIGRlbnlTZXQuYWRkKG9wKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGlmICh0b29sQ29uZmlnLmRlbmllZCAmJiB0b29sQ29uZmlnLmRlbmllZC5sZW5ndGggPiAwKSB7XG4gICAgLy8gQWRkIGFsbCBvcGVyYXRpb25zIGZyb20gZGVuaWVkIGVuZHBvaW50c1xuICAgIGZvciAoY29uc3QgdG9vbE5hbWUgb2YgdG9vbENvbmZpZy5kZW5pZWQpIHtcbiAgICAgIGNvbnN0IGVuZHBvaW50ID0gRU5EUE9JTlRfVE9PTF9NQVAuZ2V0KHRvb2xOYW1lKTtcbiAgICAgIGlmIChlbmRwb2ludCkge1xuICAgICAgICBmb3IgKGNvbnN0IG9wIG9mIGdldE9wZXJhdGlvbnNGb3JFbmRwb2ludChlbmRwb2ludCkpIHtcbiAgICAgICAgICBpZiAoIUVYRU1QVF9PUEVSQVRJT05TLmhhcyhvcCkpIHtcbiAgICAgICAgICAgIGRlbnlTZXQuYWRkKG9wKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBpZiAoZGVueVNldC5zaXplID09PSAwKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHJldHVybiB7IGRlbnk6IEFycmF5LmZyb20oZGVueVNldCkuc29ydCgoYSwgYikgPT4gYS5sb2NhbGVDb21wYXJlKGIpKSB9O1xufVxuIl19