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.

153 lines 23.1 kB
/** * Permission evaluation for PreToolUse hooks across all AI platforms. * * Provides the `evaluate_permission` MCP-AQL READ operation, enabling * Claude Code, Cursor, Gemini CLI, Windsurf, and Codex to use * DollhouseMCP as their permission evaluation backend via hooks. * * Three-stage evaluation pipeline: * 1. Rate limiting — prevents abuse * 2. Static tool classification — built-in allow/deny rules * 3. Element policy evaluation — active element gatekeeper policies * * Returns platform-specific response formats so each platform's hook * script receives the JSON shape it expects. */ /** Error thrown when permission evaluation fails at a specific stage */ export class PermissionEvaluationError extends Error { stage; toolName; constructor(message, stage, toolName, cause) { super(message, cause ? { cause } : undefined); this.name = 'PermissionEvaluationError'; this.stage = stage; this.toolName = toolName; } } /** Optional reason field, only included when reason is provided */ function withReason(obj, reason, key = 'reason') { return reason ? { ...obj, [key]: reason } : obj; } /** Gemini: maps 'ask' to 'deny' (no interactive support) */ function formatGemini(decision, reason) { return withReason({ decision: decision === 'ask' ? 'deny' : decision }, reason); } /** Cursor: uses 'permission' field instead of 'decision' */ function formatCursor(decision, reason) { return withReason({ permission: decision }, reason); } /** Windsurf: uses boolean 'allowed' field */ function formatWindsurf(decision, reason) { return withReason({ allowed: decision === 'allow' }, reason); } /** Codex PreToolUse currently supports block/deny only; allow is empty stdout. */ function formatCodex(decision, reason) { if (decision === 'allow') { return {}; } return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: decision === 'ask' ? 'deny' : decision, ...(reason ? { permissionDecisionReason: reason } : {}), }, }; } /** Claude Code (default): uses hookSpecificOutput.permissionDecision for PreToolUse */ function formatClaudeCode(decision, reason) { return { hookSpecificOutput: withReason({ hookEventName: 'PreToolUse', permissionDecision: decision, }, reason, 'permissionDecisionReason'), }; } /** Platform formatter lookup */ const platformFormatters = { gemini: formatGemini, cursor: formatCursor, windsurf: formatWindsurf, codex: formatCodex, vscode: formatClaudeCode, claude_code: formatClaudeCode, }; /** Known platform identifiers */ export const SUPPORTED_PLATFORMS = Object.keys(platformFormatters); function warnOnUnknownPlatform(platform) { void import('../../utils/logger.js') .then(({ logger }) => { logger.warn(`[evaluatePermission] Unknown platform "${platform}", defaulting to claude_code format. Supported: ${SUPPORTED_PLATFORMS.join(', ')}`); }) .catch((error) => { console.warn(`[evaluatePermission] Failed to load logger while handling unknown platform "${platform}".`, error); }); } /** * Format permission evaluation response for platform-specific hook scripts. * Each platform expects a different JSON shape from its hook response. * * Unknown platforms default to claude_code format with a warning log. */ export function formatPermissionResponse(decision, platform, _input, reason) { switch (platform) { case 'gemini': return formatGemini(decision, reason); case 'cursor': return formatCursor(decision, reason); case 'windsurf': return formatWindsurf(decision, reason); case 'codex': return formatCodex(decision, reason); case 'claude_code': return formatClaudeCode(decision, reason); default: // Import lazily to avoid circular dependency at module load time warnOnUnknownPlatform(platform); return formatClaudeCode(decision, reason); } } /** * Evaluate a CLI permission request for PreToolUse hooks. * * @param params - Tool name, input, and target platform * @param deps - Injected dependencies from MCPAQLHandler * @returns Platform-formatted permission decision */ export async function evaluatePermission(params, deps) { const toolName = typeof params.tool_name === 'string' ? params.tool_name : ''; const inputRaw = params.input; const input = (inputRaw && typeof inputRaw === 'object') ? inputRaw : {}; const platform = typeof params.platform === 'string' ? params.platform : 'claude_code'; const sessionId = typeof params.session_id === 'string' && params.session_id.trim() !== '' ? params.session_id : undefined; // Rate limit const rateStatus = deps.permissionPromptLimiter.checkLimit(); if (!rateStatus.allowed) { return formatPermissionResponse('deny', platform, input, 'Rate limit exceeded'); } deps.permissionPromptLimiter.consumeToken(); // Stage 1: Static classification const classification = deps.classifyTool(toolName, input); if (classification.behavior === 'allow') { return formatPermissionResponse('allow', platform, input); } if (classification.behavior === 'deny') { return formatPermissionResponse('deny', platform, input, classification.reason); } // Stage 2: Element policy evaluation let elements; try { elements = await deps.getActiveElements(sessionId); } catch (err) { throw new PermissionEvaluationError(`Failed to fetch active elements for policy evaluation: ${err instanceof Error ? err.message : String(err)}`, 'element_fetch', toolName, err); } const decision = deps.evaluateCliToolPolicy(toolName, input, elements); if (decision.behavior === 'deny') { return formatPermissionResponse('deny', platform, input, decision.message); } if (decision.behavior === 'confirm') { return formatPermissionResponse('ask', platform, input, decision.message || 'Requires confirmation per element policy'); } // Default: allow return formatPermissionResponse('allow', platform, input); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZhbHVhdGVQZXJtaXNzaW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2hhbmRsZXJzL21jcC1hcWwvZXZhbHVhdGVQZXJtaXNzaW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBTUgsd0VBQXdFO0FBQ3hFLE1BQU0sT0FBTyx5QkFBMEIsU0FBUSxLQUFLO0lBQ2xDLEtBQUssQ0FBK0Q7SUFDcEUsUUFBUSxDQUFTO0lBRWpDLFlBQ0UsT0FBZSxFQUNmLEtBQW1FLEVBQ25FLFFBQWdCLEVBQ2hCLEtBQWU7UUFFZixLQUFLLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDOUMsSUFBSSxDQUFDLElBQUksR0FBRywyQkFBMkIsQ0FBQztRQUN4QyxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztRQUNuQixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztJQUMzQixDQUFDO0NBQ0Y7QUFVRCxtRUFBbUU7QUFDbkUsU0FBUyxVQUFVLENBQUMsR0FBNEIsRUFBRSxNQUFlLEVBQUUsR0FBRyxHQUFHLFFBQVE7SUFDL0UsT0FBTyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO0FBQ2xELENBQUM7QUFFRCw0REFBNEQ7QUFDNUQsU0FBUyxZQUFZLENBQUMsUUFBZ0IsRUFBRSxNQUFlO0lBQ3JELE9BQU8sVUFBVSxDQUFDLEVBQUUsUUFBUSxFQUFFLFFBQVEsS0FBSyxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDbEYsQ0FBQztBQUVELDREQUE0RDtBQUM1RCxTQUFT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O0lBQzdFLENBQUM7SUFDRCxJQUFJLFFBQVEsQ0FBQyxRQUFRLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDcEMsT0FBTyx3QkFBd0IsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFDcEQsUUFBUSxDQUFDLE9BQU8sSUFBSSwwQ0FBMEMsQ0FBQyxDQUFDO0lBQ3BFLENBQUM7SUFFRCxpQkFBaUI7SUFDakIsT0FBTyx3QkFBd0IsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxDQUFDO0FBQzVELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBlcm1pc3Npb24gZXZhbHVhdGlvbiBmb3IgUHJlVG9vbFVzZSBob29rcyBhY3Jvc3MgYWxsIEFJIHBsYXRmb3Jtcy5cbiAqXG4gKiBQcm92aWRlcyB0aGUgYGV2YWx1YXRlX3Blcm1pc3Npb25gIE1DUC1BUUwgUkVBRCBvcGVyYXRpb24sIGVuYWJsaW5nXG4gKiBDbGF1ZGUgQ29kZSwgQ3Vyc29yLCBHZW1pbmkgQ0xJLCBXaW5kc3VyZiwgYW5kIENvZGV4IHRvIHVzZVxuICogRG9sbGhvdXNlTUNQIGFzIHRoZWlyIHBlcm1pc3Npb24gZXZhbHVhdGlvbiBiYWNrZW5kIHZpYSBob29rcy5cbiAqXG4gKiBUaHJlZS1zdGFnZSBldmFsdWF0aW9uIHBpcGVsaW5lOlxuICogMS4gUmF0ZSBsaW1pdGluZyDigJQgcHJldmVudHMgYWJ1c2VcbiAqIDIuIFN0YXRpYyB0b29sIGNsYXNzaWZpY2F0aW9uIOKAlCBidWlsdC1pbiBhbGxvdy9kZW55IHJ1bGVzXG4gKiAzLiBFbGVtZW50IHBvbGljeSBldmFsdWF0aW9uIOKAlCBhY3RpdmUgZWxlbWVudCBnYXRla2VlcGVyIHBvbGljaWVzXG4gKlxuICogUmV0dXJucyBwbGF0Zm9ybS1zcGVjaWZpYyByZXNwb25zZSBmb3JtYXRzIHNvIGVhY2ggcGxhdGZvcm0ncyBob29rXG4gKiBzY3JpcHQgcmVjZWl2ZXMgdGhlIEpTT04gc2hhcGUgaXQgZXhwZWN0cy5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFJhdGVMaW1pdGVyIH0gZnJvbSAnLi4vLi4vdXRpbHMvUmF0ZUxpbWl0ZXIuanMnO1xuaW1wb3J0IHR5cGUgeyBUb29sQ2xhc3NpZmljYXRpb25SZXN1bHQsIENsaVRvb2xQb2xpY3lSZXN1bHQgfSBmcm9tICcuL3BvbGljaWVzL1Rvb2xDbGFzc2lmaWNhdGlvbi5qcyc7XG5pbXBvcnQgdHlwZSB7IEFjdGl2ZUVsZW1lbnQgfSBmcm9tICcuL3BvbGljaWVzL0VsZW1lbnRQb2xpY2llcy5qcyc7XG5cbi8qKiBFcnJvciB0aHJvd24gd2hlbiBwZXJtaXNzaW9uIGV2YWx1YXRpb24gZmFpbHMgYXQgYSBzcGVjaWZpYyBzdGFnZSAqL1xuZXhwb3J0IGNsYXNzIFBlcm1pc3Npb25FdmFsdWF0aW9uRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIHB1YmxpYyByZWFkb25seSBzdGFnZTogJ3JhdGVfbGltaXQnIHwgJ2NsYXNzaWZpY2F0aW9uJyB8ICdwb2xpY3knIHwgJ2VsZW1lbnRfZmV0Y2gnO1xuICBwdWJsaWMgcmVhZG9ubHkgdG9vbE5hbWU6IHN0cmluZztcblxuICBjb25zdHJ1Y3RvcihcbiAgICBtZXNzYWdlOiBzdHJpbmcsXG4gICAgc3RhZ2U6ICdyYXRlX2xpbWl0JyB8ICdjbGFzc2lmaWNhdGlvbicgfCAncG9saWN5JyB8ICdlbGVtZW50X2ZldGNoJyxcbiAgICB0b29sTmFtZTogc3RyaW5nLFxuICAgIGNhdXNlPzogdW5rbm93bixcbiAgKSB7XG4gICAgc3VwZXIobWVzc2FnZSwgY2F1c2UgPyB7IGNhdXNlIH0gOiB1bmRlZmluZWQpO1xuICAgIHRoaXMubmFtZSA9ICdQZXJtaXNzaW9uRXZhbHVhdGlvbkVycm9yJztcbiAgICB0aGlzLnN0YWdlID0gc3RhZ2U7XG4gICAgdGhpcy50b29sTmFtZSA9IHRvb2xOYW1lO1xuICB9XG59XG5cbi8qKiBEZXBlbmRlbmNpZXMgaW5qZWN0ZWQgZnJvbSBNQ1BBUUxIYW5kbGVyICovXG5leHBvcnQgaW50ZXJmYWNlIEV2YWx1YXRlUGVybWlzc2lvbkRlcHMge1xuICBwZXJtaXNzaW9uUHJvbXB0TGltaXRlcjogUmF0ZUxpbWl0ZXI7XG4gIGNsYXNzaWZ5VG9vbDogKHRvb2xOYW1lOiBzdHJpbmcsIHRvb2xJbnB1dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pID0+IFRvb2xDbGFzc2lmaWNhdGlvblJlc3VsdDtcbiAgZXZhbHVhdGVDbGlUb29sUG9saWN5OiAodG9vbE5hbWU6IHN0cmluZywgdG9vbElucHV0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiwgZWxlbWVudHM6IEFjdGl2ZUVsZW1lbnRbXSkgPT4gQ2xpVG9vbFBvbGljeVJlc3VsdDtcbiAgZ2V0QWN0aXZlRWxlbWVudHM6IChzZXNzaW9uSWQ/OiBzdHJpbmcpID0+IFByb21pc2U8QWN0aXZlRWxlbWVudFtdPjtcbn1cblxuLyoqIE9wdGlvbmFsIHJlYXNvbiBmaWVsZCwgb25seSBpbmNsdWRlZCB3aGVuIHJlYXNvbiBpcyBwcm92aWRlZCAqL1xuZnVuY3Rpb24gd2l0aFJlYXNvbihvYmo6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCByZWFzb24/OiBzdHJpbmcsIGtleSA9ICdyZWFzb24nKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICByZXR1cm4gcmVhc29uID8geyAuLi5vYmosIFtrZXldOiByZWFzb24gfSA6IG9iajtcbn1cblxuLyoqIEdlbWluaTogbWFwcyAnYXNrJyB0byAnZGVueScgKG5vIGludGVyYWN0aXZlIHN1cHBvcnQpICovXG5mdW5jdGlvbiBmb3JtYXRHZW1pbmkoZGVjaXNpb246IHN0cmluZywgcmVhc29uPzogc3RyaW5nKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICByZXR1cm4gd2l0aFJlYXNvbih7IGRlY2lzaW9uOiBkZWNpc2lvbiA9PT0gJ2FzaycgPyAnZGVueScgOiBkZWNpc2lvbiB9LCByZWFzb24pO1xufVxuXG4vKiogQ3Vyc29yOiB1c2VzICdwZXJtaXNzaW9uJyBmaWVsZCBpbnN0ZWFkIG9mICdkZWNpc2lvbicgKi9cbmZ1bmN0aW9uIGZvcm1hdEN1cnNvcihkZWNpc2lvbjogc3RyaW5nLCByZWFzb24/OiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIHJldHVybiB3aXRoUmVhc29uKHsgcGVybWlzc2lvbjogZGVjaXNpb24gfSwgcmVhc29uKTtcbn1cblxuLyoqIFdpbmRzdXJmOiB1c2VzIGJvb2xlYW4gJ2FsbG93ZWQnIGZpZWxkICovXG5mdW5jdGlvbiBmb3JtYXRXaW5kc3VyZihkZWNpc2lvbjogc3RyaW5nLCByZWFzb24/OiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIHJldHVybiB3aXRoUmVhc29uKHsgYWxsb3dlZDogZGVjaXNpb24gPT09ICdhbGxvdycgfSwgcmVhc29uKTtcbn1cblxuLyoqIENvZGV4IFByZVRvb2xVc2UgY3VycmVudGx5IHN1cHBvcnRzIGJsb2NrL2Rlbnkgb25seTsgYWxsb3cgaXMgZW1wdHkgc3Rkb3V0LiAqL1xuZnVuY3Rpb24gZm9ybWF0Q29kZXgoZGVjaXNpb246IHN0cmluZywgcmVhc29uPzogc3RyaW5nKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICBpZiAoZGVjaXNpb24gPT09ICdhbGxvdycpIHtcbiAgICByZXR1cm4ge307XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGhvb2tTcGVjaWZpY091dHB1dDoge1xuICAgICAgaG9va0V2ZW50TmFtZTogJ1ByZVRvb2xVc2UnLFxuICAgICAgcGVybWlzc2lvbkRlY2lzaW9uOiBkZWNpc2lvbiA9PT0gJ2FzaycgPyAnZGVueScgOiBkZWNpc2lvbixcbiAgICAgIC4uLihyZWFzb24gPyB7IHBlcm1pc3Npb25EZWNpc2lvblJlYXNvbjogcmVhc29uIH0gOiB7fSksXG4gICAgfSxcbiAgfTtcbn1cblxuLyoqIENsYXVkZSBDb2RlIChkZWZhdWx0KTogdXNlcyBob29rU3BlY2lmaWNPdXRwdXQucGVybWlzc2lvbkRlY2lzaW9uIGZvciBQcmVUb29sVXNlICovXG5mdW5jdGlvbiBmb3JtYXRDbGF1ZGVDb2RlKGRlY2lzaW9uOiBzdHJpbmcsIHJlYXNvbj86IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgcmV0dXJuIHtcbiAgICBob29rU3BlY2lmaWNPdXRwdXQ6IHdpdGhSZWFzb24oe1xuICAgICAgaG9va0V2ZW50TmFtZTogJ1ByZVRvb2xVc2UnLFxuICAgICAgcGVybWlzc2lvbkRlY2lzaW9uOiBkZWNpc2lvbixcbiAgICB9LCByZWFzb24sICdwZXJtaXNzaW9uRGVjaXNpb25SZWFzb24nKSxcbiAgfTtcbn1cblxuLyoqIFBsYXRmb3JtIGZvcm1hdHRlciBsb29rdXAgKi9cbmNvbnN0IHBsYXRmb3JtRm9ybWF0dGVyczogUmVjb3JkPHN0cmluZywgKGRlY2lzaW9uOiBzdHJpbmcsIHJlYXNvbj86IHN0cmluZykgPT4gUmVjb3JkPHN0cmluZywgdW5rbm93bj4+ID0ge1xuICBnZW1pbmk6IGZvcm1hdEdlbWluaSxcbiAgY3Vyc29yOiBmb3JtYXRDdXJzb3IsXG4gIHdpbmRzdXJmOiBmb3JtYXRXaW5kc3VyZixcbiAgY29kZXg6IGZvcm1hdENvZGV4LFxuICB2c2NvZGU6IGZvcm1hdENsYXVkZUNvZGUsXG4gIGNsYXVkZV9jb2RlOiBmb3JtYXRDbGF1ZGVDb2RlLFxufTtcblxuLyoqIEtub3duIHBsYXRmb3JtIGlkZW50aWZpZXJzICovXG5leHBvcnQgY29uc3QgU1VQUE9SVEVEX1BMQVRGT1JNUyA9IE9iamVjdC5rZXlzKHBsYXRmb3JtRm9ybWF0dGVycyk7XG5cbmZ1bmN0aW9uIHdhcm5PblVua25vd25QbGF0Zm9ybShwbGF0Zm9ybTogc3RyaW5nKTogdm9pZCB7XG4gIHZvaWQgaW1wb3J0KCcuLi8uLi91dGlscy9sb2dnZXIuanMnKVxuICAgIC50aGVuKCh7IGxvZ2dlciB9KSA9PiB7XG4gICAgICBsb2dnZXIud2FybihcbiAgICAgICAgYFtldmFsdWF0ZVBlcm1pc3Npb25dIFVua25vd24gcGxhdGZvcm0gXCIke3BsYXRmb3JtfVwiLCBkZWZhdWx0aW5nIHRvIGNsYXVkZV9jb2RlIGZvcm1hdC4gU3VwcG9ydGVkOiAke1NVUFBPUlRFRF9QTEFURk9STVMuam9pbignLCAnKX1gLFxuICAgICAgKTtcbiAgICB9KVxuICAgIC5jYXRjaCgoZXJyb3IpID0+IHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYFtldmFsdWF0ZVBlcm1pc3Npb25dIEZhaWxlZCB0byBsb2FkIGxvZ2dlciB3aGlsZSBoYW5kbGluZyB1bmtub3duIHBsYXRmb3JtIFwiJHtwbGF0Zm9ybX1cIi5gLFxuICAgICAgICBlcnJvcixcbiAgICAgICk7XG4gICAgfSk7XG59XG5cbi8qKlxuICogRm9ybWF0IHBlcm1pc3Npb24gZXZhbHVhdGlvbiByZXNwb25zZSBmb3IgcGxhdGZvcm0tc3BlY2lmaWMgaG9vayBzY3JpcHRzLlxuICogRWFjaCBwbGF0Zm9ybSBleHBlY3RzIGEgZGlmZmVyZW50IEpTT04gc2hhcGUgZnJvbSBpdHMgaG9vayByZXNwb25zZS5cbiAqXG4gKiBVbmtub3duIHBsYXRmb3JtcyBkZWZhdWx0IHRvIGNsYXVkZV9jb2RlIGZvcm1hdCB3aXRoIGEgd2FybmluZyBsb2cuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRQZXJtaXNzaW9uUmVzcG9uc2UoXG4gIGRlY2lzaW9uOiAnYWxsb3cnIHwgJ2RlbnknIHwgJ2FzaycsXG4gIHBsYXRmb3JtOiBzdHJpbmcsXG4gIF9pbnB1dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gIHJlYXNvbj86IHN0cmluZyxcbik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgc3dpdGNoIChwbGF0Zm9ybSkge1xuICAgIGNhc2UgJ2dlbWluaSc6IHJldHVybiBmb3JtYXRHZW1pbmkoZGVjaXNpb24sIHJlYXNvbik7XG4gICAgY2FzZSAnY3Vyc29yJzogcmV0dXJuIGZvcm1hdEN1cnNvcihkZWNpc2lvbiwgcmVhc29uKTtcbiAgICBjYXNlICd3aW5kc3VyZic6IHJldHVybiBmb3JtYXRXaW5kc3VyZihkZWNpc2lvbiwgcmVhc29uKTtcbiAgICBjYXNlICdjb2RleCc6IHJldHVybiBmb3JtYXRDb2RleChkZWNpc2lvbiwgcmVhc29uKTtcbiAgICBjYXNlICdjbGF1ZGVfY29kZSc6IHJldHVybiBmb3JtYXRDbGF1ZGVDb2RlKGRlY2lzaW9uLCByZWFzb24pO1xuICAgIGRlZmF1bHQ6XG4gICAgICAvLyBJbXBvcnQgbGF6aWx5IHRvIGF2b2lkIGNpcmN1bGFyIGRlcGVuZGVuY3kgYXQgbW9kdWxlIGxvYWQgdGltZVxuICAgICAgd2Fybk9uVW5rbm93blBsYXRmb3JtKHBsYXRmb3JtKTtcbiAgICAgIHJldHVybiBmb3JtYXRDbGF1ZGVDb2RlKGRlY2lzaW9uLCByZWFzb24pO1xuICB9XG59XG5cbi8qKlxuICogRXZhbHVhdGUgYSBDTEkgcGVybWlzc2lvbiByZXF1ZXN0IGZvciBQcmVUb29sVXNlIGhvb2tzLlxuICpcbiAqIEBwYXJhbSBwYXJhbXMgLSBUb29sIG5hbWUsIGlucHV0LCBhbmQgdGFyZ2V0IHBsYXRmb3JtXG4gKiBAcGFyYW0gZGVwcyAtIEluamVjdGVkIGRlcGVuZGVuY2llcyBmcm9tIE1DUEFRTEhhbmRsZXJcbiAqIEByZXR1cm5zIFBsYXRmb3JtLWZvcm1hdHRlZCBwZXJtaXNzaW9uIGRlY2lzaW9uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBldmFsdWF0ZVBlcm1pc3Npb24oXG4gIHBhcmFtczogeyB0b29sX25hbWU/OiB1bmtub3duOyBpbnB1dD86IHVua25vd247IHBsYXRmb3JtPzogdW5rbm93bjsgc2Vzc2lvbl9pZD86IHVua25vd24gfSxcbiAgZGVwczogRXZhbHVhdGVQZXJtaXNzaW9uRGVwcyxcbik6IFByb21pc2U8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+IHtcbiAgY29uc3QgdG9vbE5hbWUgPSB0eXBlb2YgcGFyYW1zLnRvb2xfbmFtZSA9PT0gJ3N0cmluZycgPyBwYXJhbXMudG9vbF9uYW1lIDogJyc7XG4gIGNvbnN0IGlucHV0UmF3ID0gcGFyYW1zLmlucHV0O1xuICBjb25zdCBpbnB1dCA9IChpbnB1dFJhdyAmJiB0eXBlb2YgaW5wdXRSYXcgPT09ICdvYmplY3QnKVxuICAgID8gaW5wdXRSYXcgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj5cbiAgICA6IHt9O1xuICBjb25zdCBwbGF0Zm9ybSA9IHR5cGVvZiBwYXJhbXMucGxhdGZvcm0gPT09ICdzdHJpbmcnID8gcGFyYW1zLnBsYXRmb3JtIDogJ2NsYXVkZV9jb2RlJztcbiAgY29uc3Qgc2Vzc2lvbklkID0gdHlwZW9mIHBhcmFtcy5zZXNzaW9uX2lkID09PSAnc3RyaW5nJyAmJiBwYXJhbXMuc2Vzc2lvbl9pZC50cmltKCkgIT09ICcnXG4gICAgPyBwYXJhbXMuc2Vzc2lvbl9pZFxuICAgIDogdW5kZWZpbmVkO1xuXG4gIC8vIFJhdGUgbGltaXRcbiAgY29uc3QgcmF0ZVN0YXR1cyA9IGRlcHMucGVybWlzc2lvblByb21wdExpbWl0ZXIuY2hlY2tMaW1pdCgpO1xuICBpZiAoIXJhdGVTdGF0dXMuYWxsb3dlZCkge1xuICAgIHJldHVybiBmb3JtYXRQZXJtaXNzaW9uUmVzcG9uc2UoJ2RlbnknLCBwbGF0Zm9ybSwgaW5wdXQsICdSYXRlIGxpbWl0IGV4Y2VlZGVkJyk7XG4gIH1cbiAgZGVwcy5wZXJtaXNzaW9uUHJvbXB0TGltaXRlci5jb25zdW1lVG9rZW4oKTtcblxuICAvLyBTdGFnZSAxOiBTdGF0aWMgY2xhc3NpZmljYXRpb25cbiAgY29uc3QgY2xhc3NpZmljYXRpb24gPSBkZXBzLmNsYXNzaWZ5VG9vbCh0b29sTmFtZSwgaW5wdXQpO1xuICBpZiAoY2xhc3NpZmljYXRpb24uYmVoYXZpb3IgPT09ICdhbGxvdycpIHtcbiAgICByZXR1cm4gZm9ybWF0UGVybWlzc2lvblJlc3BvbnNlKCdhbGxvdycsIHBsYXRmb3JtLCBpbnB1dCk7XG4gIH1cbiAgaWYgKGNsYXNzaWZpY2F0aW9uLmJlaGF2aW9yID09PSAnZGVueScpIHtcbiAgICByZXR1cm4gZm9ybWF0UGVybWlzc2lvblJlc3BvbnNlKCdkZW55JywgcGxhdGZvcm0sIGlucHV0LCBjbGFzc2lmaWNhdGlvbi5yZWFzb24pO1xuICB9XG5cbiAgLy8gU3RhZ2UgMjogRWxlbWVudCBwb2xpY3kgZXZhbHVhdGlvblxuICBsZXQgZWxlbWVudHM6IEFjdGl2ZUVsZW1lbnRbXTtcbiAgdHJ5IHtcbiAgICBlbGVtZW50cyA9IGF3YWl0IGRlcHMuZ2V0QWN0aXZlRWxlbWVudHMoc2Vzc2lvbklkKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgdGhyb3cgbmV3IFBlcm1pc3Npb25FdmFsdWF0aW9uRXJyb3IoXG4gICAgICBgRmFpbGVkIHRvIGZldGNoIGFjdGl2ZSBlbGVtZW50cyBmb3IgcG9saWN5IGV2YWx1YXRpb246ICR7ZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpfWAsXG4gICAgICAnZWxlbWVudF9mZXRjaCcsIHRvb2xOYW1lLCBlcnIsXG4gICAgKTtcbiAgfVxuICBjb25zdCBkZWNpc2lvbiA9IGRlcHMuZXZhbHVhdGVDbGlUb29sUG9saWN5KHRvb2xOYW1lLCBpbnB1dCwgZWxlbWVudHMpO1xuXG4gIGlmIChkZWNpc2lvbi5iZWhhdmlvciA9PT0gJ2RlbnknKSB7XG4gICAgcmV0dXJuIGZvcm1hdFBlcm1pc3Npb25SZXNwb25zZSgnZGVueScsIHBsYXRmb3JtLCBpbnB1dCwgZGVjaXNpb24ubWVzc2FnZSk7XG4gIH1cbiAgaWYgKGRlY2lzaW9uLmJlaGF2aW9yID09PSAnY29uZmlybScpIHtcbiAgICByZXR1cm4gZm9ybWF0UGVybWlzc2lvblJlc3BvbnNlKCdhc2snLCBwbGF0Zm9ybSwgaW5wdXQsXG4gICAgICBkZWNpc2lvbi5tZXNzYWdlIHx8ICdSZXF1aXJlcyBjb25maXJtYXRpb24gcGVyIGVsZW1lbnQgcG9saWN5Jyk7XG4gIH1cblxuICAvLyBEZWZhdWx0OiBhbGxvd1xuICByZXR1cm4gZm9ybWF0UGVybWlzc2lvblJlc3BvbnNlKCdhbGxvdycsIHBsYXRmb3JtLCBpbnB1dCk7XG59XG4iXX0=