@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.
187 lines • 27.8 kB
JavaScript
/**
* CLI commands for console token management (#1790).
*
* Usage:
* dollhouse console token show [--json]
* dollhouse console token rotate [--json]
* dollhouse console token revoke [--id <uuid>] [--json]
*
* Exit codes:
* 0 — success
* 1 — user error (missing file, invalid args)
* 2 — auth/confirmation failure (wrong TOTP code, not enrolled)
*
* @since v2.1.0 — Issue #1790
*/
import { Command } from 'commander';
import chalk from 'chalk';
import { createInterface } from 'node:readline/promises';
import { stdin, stdout } from 'node:process';
import { ConsoleTokenStore, readTokenFileRaw, TotpError, DEFAULT_TOKEN_FILE, } from '../web/console/consoleToken.js';
import { env } from '../config/env.js';
/** Resolve the token file path from env or default. */
function resolveTokenFilePath() {
return env.DOLLHOUSE_CONSOLE_TOKEN_FILE || DEFAULT_TOKEN_FILE;
}
/** Read token file or exit with error. */
async function readFileOrExit(filePath) {
const data = await readTokenFileRaw(filePath);
if (!data) {
console.error(chalk.red('Token file not found or corrupt: ' + filePath));
console.error(chalk.gray('The token file is created automatically when the server starts.'));
process.exit(1);
}
return data;
}
/** Prompt the user for a TOTP code on stdin. */
async function promptTotpCode() {
const rl = createInterface({ input: stdin, output: stdout });
try {
const code = await rl.question(chalk.cyan('Enter TOTP code (or backup code): '));
// Strip whitespace and dashes — users may type backup codes as "XXXX-XXXX"
const trimmed = code.trim().replaceAll(/[\s-]/g, '');
if (!trimmed) {
console.error(chalk.red('No confirmation code provided.'));
process.exit(2);
}
// Accept 6-digit TOTP codes or 8-char alphanumeric backup codes
if (!/^\d{6}$/.test(trimmed) && !/^[0-9A-Za-z]{8}$/.test(trimmed)) {
console.error(chalk.red('Invalid code format. Enter a 6-digit TOTP code or an 8-character backup code.'));
process.exit(2);
}
return trimmed;
}
finally {
rl.close();
}
}
/**
* Shared rotation logic for both `rotate` and `revoke` commands.
* Initializes the store, checks TOTP enrollment, obtains the confirmation
* code (from --code flag or interactive prompt), and calls rotatePrimary.
*
* @param operation - 'rotation' or 'revocation' for user-facing messages
*/
async function executeRotation(options, operation) {
const filePath = resolveTokenFilePath();
const store = new ConsoleTokenStore(filePath);
await store.ensureInitialized('CLI');
if (!store.isTotpEnrolled()) {
if (options.json) {
console.log(JSON.stringify({ error: 'TOTP enrollment required', code: 'TOTP_REQUIRED' }));
}
else {
console.error(chalk.red(`Token ${operation} requires TOTP enrollment.`));
console.error(chalk.gray('Enroll via the Security tab or the TOTP enrollment API.'));
}
process.exit(2);
}
const code = options.code || await promptTotpCode();
try {
return await store.rotatePrimary(code);
}
catch (err) {
if (err instanceof TotpError) {
if (options.json) {
console.log(JSON.stringify({ error: err.message, code: err.code }));
}
else {
console.error(chalk.red(`${operation.charAt(0).toUpperCase() + operation.slice(1)} failed: ${err.message}`));
}
process.exit(2);
}
console.error(chalk.red(`Unexpected error during ${operation}: ${err instanceof Error ? err.message : String(err)}`));
process.exit(1);
}
}
/**
* Format and print the result of a rotation/revocation operation.
* Handles both JSON and human-readable output for rotate and revoke.
*/
function printRotationResult(result, options, operation) {
if (options.json) {
const output = operation === 'rotation'
? { token: result.token, rotatedAt: result.rotatedAt, graceUntil: result.graceUntil }
: { revoked: true, newToken: result.token, rotatedAt: result.rotatedAt };
console.log(JSON.stringify(output, null, 2));
}
else {
const successMsg = operation === 'rotation'
? 'Token rotated successfully.'
: 'Token revoked. A new token has been issued.';
const statusMsg = operation === 'rotation'
? `Old token valid until: ${new Date(result.graceUntil).toISOString()}`
: 'All sessions using the old token will lose access after the grace window.';
console.log(chalk.green(successMsg));
console.log(result.token);
console.log(chalk.gray(`Rotated at: ${result.rotatedAt}`));
console.log(chalk.gray(statusMsg));
}
}
// ── Program ─────────────────────────────────────────────────────────────
const program = new Command();
program
.name('dollhouse console token')
.description('Manage console authentication tokens');
// ── show ────────────────────────────────────────────────────────────────
program
.command('show')
.description('Print the current primary console token')
.option('--json', 'Output as JSON for scripted consumption')
.option('--masked', 'Show masked token instead of full value')
.action(async (options) => {
const filePath = resolveTokenFilePath();
const data = await readFileOrExit(filePath);
const primary = data.tokens[0];
if (!primary) {
console.error(chalk.red('No tokens found in the token file.'));
process.exit(1);
}
if (options.json) {
const output = {
id: primary.id,
name: primary.name,
kind: primary.kind,
token: options.masked ? primary.token.slice(0, 8) + '...' : primary.token,
scopes: primary.scopes,
createdAt: primary.createdAt,
lastUsedAt: primary.lastUsedAt,
createdVia: primary.createdVia,
filePath,
totpEnrolled: data.totp.enrolled,
};
console.log(JSON.stringify(output, null, 2));
}
else {
const tokenDisplay = options.masked
? primary.token.slice(0, 8) + chalk.gray('•'.repeat(56))
: primary.token;
console.log(tokenDisplay);
}
});
// ── rotate ──────────────────────────────────────────────────────────────
program
.command('rotate')
.description('Rotate the primary console token (requires TOTP confirmation)')
.option('--json', 'Output as JSON for scripted consumption')
.option('--code <code>', 'TOTP confirmation code (prompts if omitted)')
.action(async (options) => {
const result = await executeRotation(options, 'rotation');
printRotationResult(result, options, 'rotation');
});
// ── revoke ──────────────────────────────────────────────────────────────
program
.command('revoke')
.description('Revoke a token — rotates the primary to invalidate the current value')
.option('--json', 'Output as JSON for scripted consumption')
.option('--code <code>', 'TOTP confirmation code (prompts if omitted)')
.action(async (options) => {
// For Phase 2 with a single primary token, revoke == rotate.
// When multi-device tokens land, --id <uuid> removes a specific
// non-primary token from the store.
const result = await executeRotation(options, 'revocation');
printRotationResult(result, options, 'revocation');
});
program.parse(process.argv);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc29sZS10b2tlbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGkvY29uc29sZS10b2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBRUE7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFFSCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMxQixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDekQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDN0MsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixnQkFBZ0IsRUFDaEIsU0FBUyxFQUNULGtCQUFrQixHQUduQixNQUFNLGdDQUFnQyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUV2Qyx1REFBdUQ7QUFDdkQsU0FBUyxvQkFBb0I7SUFDM0IsT0FBTyxHQUFHLENBQUMsNEJBQTRCLElBQUksa0JBQWtCLENBQUM7QUFDaEUsQ0FBQztBQUVELDBDQUEwQztBQUMxQyxLQUFLLFVBQVUsY0FBYyxDQUFDLFFBQWdCO0lBQzVDLE1BQU0sSUFBSSxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDOUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1YsT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLG1DQUFtQyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDekUsT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGlFQUFpRSxDQUFDLENBQUMsQ0FBQztRQUM3RixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2xCLENBQUM7SUFDRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRCxnREFBZ0Q7QUFDaEQsS0FB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LENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIjIS91c3IvYmluL2VudiBub2RlXG5cbi8qKlxuICogQ0xJIGNvbW1hbmRzIGZvciBjb25zb2xlIHRva2VuIG1hbmFnZW1lbnQgKCMxNzkwKS5cbiAqXG4gKiBVc2FnZTpcbiAqICAgZG9sbGhvdXNlIGNvbnNvbGUgdG9rZW4gc2hvdyBbLS1qc29uXVxuICogICBkb2xsaG91c2UgY29uc29sZSB0b2tlbiByb3RhdGUgWy0tanNvbl1cbiAqICAgZG9sbGhvdXNlIGNvbnNvbGUgdG9rZW4gcmV2b2tlIFstLWlkIDx1dWlkPl0gWy0tanNvbl1cbiAqXG4gKiBFeGl0IGNvZGVzOlxuICogICAwIOKAlCBzdWNjZXNzXG4gKiAgIDEg4oCUIHVzZXIgZXJyb3IgKG1pc3NpbmcgZmlsZSwgaW52YWxpZCBhcmdzKVxuICogICAyIOKAlCBhdXRoL2NvbmZpcm1hdGlvbiBmYWlsdXJlICh3cm9uZyBUT1RQIGNvZGUsIG5vdCBlbnJvbGxlZClcbiAqXG4gKiBAc2luY2UgdjIuMS4wIOKAlCBJc3N1ZSAjMTc5MFxuICovXG5cbmltcG9ydCB7IENvbW1hbmQgfSBmcm9tICdjb21tYW5kZXInO1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCB7IGNyZWF0ZUludGVyZmFjZSB9IGZyb20gJ25vZGU6cmVhZGxpbmUvcHJvbWlzZXMnO1xuaW1wb3J0IHsgc3RkaW4sIHN0ZG91dCB9IGZyb20gJ25vZGU6cHJvY2Vzcyc7XG5pbXBvcnQge1xuICBDb25zb2xlVG9rZW5TdG9yZSxcbiAgcmVhZFRva2VuRmlsZVJhdyxcbiAgVG90cEVycm9yLFxuICBERUZBVUxUX1RPS0VOX0ZJTEUsXG4gIHR5cGUgQ29uc29sZVRva2VuRmlsZSxcbiAgdHlwZSBSb3RhdGlvblJlc3VsdCxcbn0gZnJvbSAnLi4vd2ViL2NvbnNvbGUvY29uc29sZVRva2VuLmpzJztcbmltcG9ydCB7IGVudiB9IGZyb20gJy4uL2NvbmZpZy9lbnYuanMnO1xuXG4vKiogUmVzb2x2ZSB0aGUgdG9rZW4gZmlsZSBwYXRoIGZyb20gZW52IG9yIGRlZmF1bHQuICovXG5mdW5jdGlvbiByZXNvbHZlVG9rZW5GaWxlUGF0aCgpOiBzdHJpbmcge1xuICByZXR1cm4gZW52LkRPTExIT1VTRV9DT05TT0xFX1RPS0VOX0ZJTEUgfHwgREVGQVVMVF9UT0tFTl9GSUxFO1xufVxuXG4vKiogUmVhZCB0b2tlbiBmaWxlIG9yIGV4aXQgd2l0aCBlcnJvci4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlYWRGaWxlT3JFeGl0KGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPENvbnNvbGVUb2tlbkZpbGU+IHtcbiAgY29uc3QgZGF0YSA9IGF3YWl0IHJlYWRUb2tlbkZpbGVSYXcoZmlsZVBhdGgpO1xuICBpZiAoIWRhdGEpIHtcbiAgICBjb25zb2xlLmVycm9yKGNoYWxrLnJlZCgnVG9rZW4gZmlsZSBub3QgZm91bmQgb3IgY29ycnVwdDogJyArIGZpbGVQYXRoKSk7XG4gICAgY29uc29sZS5lcnJvcihjaGFsay5ncmF5KCdUaGUgdG9rZW4gZmlsZSBpcyBjcmVhdGVkIGF1dG9tYXRpY2FsbHkgd2hlbiB0aGUgc2VydmVyIHN0YXJ0cy4nKSk7XG4gICAgcHJvY2Vzcy5leGl0KDEpO1xuICB9XG4gIHJldHVybiBkYXRhO1xufVxuXG4vKiogUHJvbXB0IHRoZSB1c2VyIGZvciBhIFRPVFAgY29kZSBvbiBzdGRpbi4gKi9cbmFzeW5jIGZ1bmN0aW9uIHByb21wdFRvdHBDb2RlKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHJsID0gY3JlYXRlSW50ZXJmYWNlKHsgaW5wdXQ6IHN0ZGluLCBvdXRwdXQ6IHN0ZG91dCB9KTtcbiAgdHJ5IHtcbiAgICBjb25zdCBjb2RlID0gYXdhaXQgcmwucXVlc3Rpb24oY2hhbGsuY3lhbignRW50ZXIgVE9UUCBjb2RlIChvciBiYWNrdXAgY29kZSk6ICcpKTtcbiAgICAvLyBTdHJpcCB3aGl0ZXNwYWNlIGFuZCBkYXNoZXMg4oCUIHVzZXJzIG1heSB0eXBlIGJhY2t1cCBjb2RlcyBhcyBcIlhYWFgtWFhYWFwiXG4gICAgY29uc3QgdHJpbW1lZCA9IGNvZGUudHJpbSgpLnJlcGxhY2VBbGwoL1tcXHMtXS9nLCAnJyk7XG4gICAgaWYgKCF0cmltbWVkKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGNoYWxrLnJlZCgnTm8gY29uZmlybWF0aW9uIGNvZGUgcHJvdmlkZWQuJykpO1xuICAgICAgcHJvY2Vzcy5leGl0KDIpO1xuICAgIH1cbiAgICAvLyBBY2NlcHQgNi1kaWdpdCBUT1RQIGNvZGVzIG9yIDgtY2hhciBhbHBoYW51bWVyaWMgYmFja3VwIGNvZGVzXG4gICAgaWYgKCEvXlxcZHs2fSQvLnRlc3QodHJpbW1lZCkgJiYgIS9eWzAtOUEtWmEtel17OH0kLy50ZXN0KHRyaW1tZWQpKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGNoYWxrLnJlZCgnSW52YWxpZCBjb2RlIGZvcm1hdC4gRW50ZXIgYSA2LWRpZ2l0IFRPVFAgY29kZSBvciBhbiA4LWNoYXJhY3RlciBiYWNrdXAgY29kZS4nKSk7XG4gICAgICBwcm9jZXNzLmV4aXQoMik7XG4gICAgfVxuICAgIHJldHVybiB0cmltbWVkO1xuICB9IGZpbmFsbHkge1xuICAgIHJsLmNsb3NlKCk7XG4gIH1cbn1cblxuLyoqXG4gKiBTaGFyZWQgcm90YXRpb24gbG9naWMgZm9yIGJvdGggYHJvdGF0ZWAgYW5kIGByZXZva2VgIGNvbW1hbmRzLlxuICogSW5pdGlhbGl6ZXMgdGhlIHN0b3JlLCBjaGVja3MgVE9UUCBlbnJvbGxtZW50LCBvYnRhaW5zIHRoZSBjb25maXJtYXRpb25cbiAqIGNvZGUgKGZyb20gLS1jb2RlIGZsYWcgb3IgaW50ZXJhY3RpdmUgcHJvbXB0KSwgYW5kIGNhbGxzIHJvdGF0ZVByaW1hcnkuXG4gKlxuICogQHBhcmFtIG9wZXJhdGlvbiAtICdyb3RhdGlvbicgb3IgJ3Jldm9jYXRpb24nIGZvciB1c2VyLWZhY2luZyBtZXNzYWdlc1xuICovXG5hc3luYyBmdW5jdGlvbiBleGVjdXRlUm90YXRpb24oXG4gIG9wdGlvbnM6IHsganNvbj86IGJvb2xlYW47IGNvZGU/OiBzdHJpbmcgfSxcbiAgb3BlcmF0aW9uOiAncm90YXRpb24nIHwgJ3Jldm9jYXRpb24nLFxuKTogUHJvbWlzZTxSb3RhdGlvblJlc3VsdD4ge1xuICBjb25zdCBmaWxlUGF0aCA9IHJlc29sdmVUb2tlbkZpbGVQYXRoKCk7XG4gIGNvbnN0IHN0b3JlID0gbmV3IENvbnNvbGVUb2tlblN0b3JlKGZpbGVQYXRoKTtcbiAgYXdhaXQgc3RvcmUuZW5zdXJlSW5pdGlhbGl6ZWQoJ0NMSScpO1xuXG4gIGlmICghc3RvcmUuaXNUb3RwRW5yb2xsZWQoKSkge1xuICAgIGlmIChvcHRpb25zLmpzb24pIHtcbiAgICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHsgZXJyb3I6ICdUT1RQIGVucm9sbG1lbnQgcmVxdWlyZWQnLCBjb2RlOiAnVE9UUF9SRVFVSVJFRCcgfSkpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zb2xlLmVycm9yKGNoYWxrLnJlZChgVG9rZW4gJHtvcGVyYXRpb259IHJlcXVpcmVzIFRPVFAgZW5yb2xsbWVudC5gKSk7XG4gICAgICBjb25zb2xlLmVycm9yKGNoYWxrLmdyYXkoJ0Vucm9sbCB2aWEgdGhlIFNlY3VyaXR5IHRhYiBvciB0aGUgVE9UUCBlbnJvbGxtZW50IEFQSS4nKSk7XG4gICAgfVxuICAgIHByb2Nlc3MuZXhpdCgyKTtcbiAgfVxuXG4gIGNvbnN0IGNvZGUgPSBvcHRpb25zLmNvZGUgfHwgYXdhaXQgcHJvbXB0VG90cENvZGUoKTtcblxuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCBzdG9yZS5yb3RhdGVQcmltYXJ5KGNvZGUpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBpZiAoZXJyIGluc3RhbmNlb2YgVG90cEVycm9yKSB7XG4gICAgICBpZiAob3B0aW9ucy5qc29uKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHsgZXJyb3I6IGVyci5tZXNzYWdlLCBjb2RlOiBlcnIuY29kZSB9KSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLmVycm9yKGNoYWxrLnJlZChgJHtvcGVyYXRpb24uY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyBvcGVyYXRpb24uc2xpY2UoMSl9IGZhaWxlZDogJHtlcnIubWVzc2FnZX1gKSk7XG4gICAgICB9XG4gICAgICBwcm9jZXNzLmV4aXQoMik7XG4gICAgfVxuICAgIGNvbnNvbGUuZXJyb3IoY2hhbGsucmVkKGBVbmV4cGVjdGVkIGVycm9yIGR1cmluZyAke29wZXJhdGlvbn06ICR7ZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpfWApKTtcbiAgICBwcm9jZXNzLmV4aXQoMSk7XG4gIH1cbn1cblxuLyoqXG4gKiBGb3JtYXQgYW5kIHByaW50IHRoZSByZXN1bHQgb2YgYSByb3RhdGlvbi9yZXZvY2F0aW9uIG9wZXJhdGlvbi5cbiAqIEhhbmRsZXMgYm90aCBKU09OIGFuZCBodW1hbi1yZWFkYWJsZSBvdXRwdXQgZm9yIHJvdGF0ZSBhbmQgcmV2b2tlLlxuICovXG5mdW5jdGlvbiBwcmludFJvdGF0aW9uUmVzdWx0KFxuICByZXN1bHQ6IFJvdGF0aW9uUmVzdWx0LFxuICBvcHRpb25zOiB7IGpzb24/OiBib29sZWFuIH0sXG4gIG9wZXJhdGlvbjogJ3JvdGF0aW9uJyB8ICdyZXZvY2F0aW9uJyxcbik6IHZvaWQge1xuICBpZiAob3B0aW9ucy5qc29uKSB7XG4gICAgY29uc3Qgb3V0cHV0ID0gb3BlcmF0aW9uID09PSAncm90YXRpb24nXG4gICAgICA/IHsgdG9rZW46IHJlc3VsdC50b2tlbiwgcm90YXRlZEF0OiByZXN1bHQucm90YXRlZEF0LCBncmFjZVVudGlsOiByZXN1bHQuZ3JhY2VVbnRpbCB9XG4gICAgICA6IHsgcmV2b2tlZDogdHJ1ZSwgbmV3VG9rZW46IHJlc3VsdC50b2tlbiwgcm90YXRlZEF0OiByZXN1bHQucm90YXRlZEF0IH07XG4gICAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkob3V0cHV0LCBudWxsLCAyKSk7XG4gIH0gZWxzZSB7XG4gICAgY29uc3Qgc3VjY2Vzc01zZyA9IG9wZXJhdGlvbiA9PT0gJ3JvdGF0aW9uJ1xuICAgICAgPyAnVG9rZW4gcm90YXRlZCBzdWNjZXNzZnVsbHkuJ1xuICAgICAgOiAnVG9rZW4gcmV2b2tlZC4gQSBuZXcgdG9rZW4gaGFzIGJlZW4gaXNzdWVkLic7XG4gICAgY29uc3Qgc3RhdHVzTXNnID0gb3BlcmF0aW9uID09PSAncm90YXRpb24nXG4gICAgICA/IGBPbGQgdG9rZW4gdmFsaWQgdW50aWw6ICR7bmV3IERhdGUocmVzdWx0LmdyYWNlVW50aWwpLnRvSVNPU3RyaW5nKCl9YFxuICAgICAgOiAnQWxsIHNlc3Npb25zIHVzaW5nIHRoZSBvbGQgdG9rZW4gd2lsbCBsb3NlIGFjY2VzcyBhZnRlciB0aGUgZ3JhY2Ugd2luZG93Lic7XG4gICAgY29uc29sZS5sb2coY2hhbGsuZ3JlZW4oc3VjY2Vzc01zZykpO1xuICAgIGNvbnNvbGUubG9nKHJlc3VsdC50b2tlbik7XG4gICAgY29uc29sZS5sb2coY2hhbGsuZ3JheShgUm90YXRlZCBhdDogJHtyZXN1bHQucm90YXRlZEF0fWApKTtcbiAgICBjb25zb2xlLmxvZyhjaGFsay5ncmF5KHN0YXR1c01zZykpO1xuICB9XG59XG5cbi8vIOKUgOKUgCBQcm9ncmFtIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5jb25zdCBwcm9ncmFtID0gbmV3IENvbW1hbmQoKTtcblxucHJvZ3JhbVxuICAubmFtZSgnZG9sbGhvdXNlIGNvbnNvbGUgdG9rZW4nKVxuICAuZGVzY3JpcHRpb24oJ01hbmFnZSBjb25zb2xlIGF1dGhlbnRpY2F0aW9uIHRva2VucycpO1xuXG4vLyDilIDilIAgc2hvdyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxucHJvZ3JhbVxuICAuY29tbWFuZCgnc2hvdycpXG4gIC5kZXNjcmlwdGlvbignUHJpbnQgdGhlIGN1cnJlbnQgcHJpbWFyeSBjb25zb2xlIHRva2VuJylcbiAgLm9wdGlvbignLS1qc29uJywgJ091dHB1dCBhcyBKU09OIGZvciBzY3JpcHRlZCBjb25zdW1wdGlvbicpXG4gIC5vcHRpb24oJy0tbWFza2VkJywgJ1Nob3cgbWFza2VkIHRva2VuIGluc3RlYWQgb2YgZnVsbCB2YWx1ZScpXG4gIC5hY3Rpb24oYXN5bmMgKG9wdGlvbnM6IHsganNvbj86IGJvb2xlYW47IG1hc2tlZD86IGJvb2xlYW4gfSkgPT4ge1xuICAgIGNvbnN0IGZpbGVQYXRoID0gcmVzb2x2ZVRva2VuRmlsZVBhdGgoKTtcbiAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVhZEZpbGVPckV4aXQoZmlsZVBhdGgpO1xuICAgIGNvbnN0IHByaW1hcnkgPSBkYXRhLnRva2Vuc1swXTtcbiAgICBpZiAoIXByaW1hcnkpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoY2hhbGsucmVkKCdObyB0b2tlbnMgZm91bmQgaW4gdGhlIHRva2VuIGZpbGUuJykpO1xuICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgIH1cblxuICAgIGlmIChvcHRpb25zLmpzb24pIHtcbiAgICAgIGNvbnN0IG91dHB1dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7XG4gICAgICAgIGlkOiBwcmltYXJ5LmlkLFxuICAgICAgICBuYW1lOiBwcmltYXJ5Lm5hbWUsXG4gICAgICAgIGtpbmQ6IHByaW1hcnkua2luZCxcbiAgICAgICAgdG9rZW46IG9wdGlvbnMubWFza2VkID8gcHJpbWFyeS50b2tlbi5zbGljZSgwLCA4KSArICcuLi4nIDogcHJpbWFyeS50b2tlbixcbiAgICAgICAgc2NvcGVzOiBwcmltYXJ5LnNjb3BlcyxcbiAgICAgICAgY3JlYXRlZEF0OiBwcmltYXJ5LmNyZWF0ZWRBdCxcbiAgICAgICAgbGFzdFVzZWRBdDogcHJpbWFyeS5sYXN0VXNlZEF0LFxuICAgICAgICBjcmVhdGVkVmlhOiBwcmltYXJ5LmNyZWF0ZWRWaWEsXG4gICAgICAgIGZpbGVQYXRoLFxuICAgICAgICB0b3RwRW5yb2xsZWQ6IGRhdGEudG90cC5lbnJvbGxlZCxcbiAgICAgIH07XG4gICAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeShvdXRwdXQsIG51bGwsIDIpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgdG9rZW5EaXNwbGF5ID0gb3B0aW9ucy5tYXNrZWRcbiAgICAgICAgPyBwcmltYXJ5LnRva2VuLnNsaWNlKDAsIDgpICsgY2hhbGsuZ3JheSgn4oCiJy5yZXBlYXQoNTYpKVxuICAgICAgICA6IHByaW1hcnkudG9rZW47XG4gICAgICBjb25zb2xlLmxvZyh0b2tlbkRpc3BsYXkpO1xuICAgIH1cbiAgfSk7XG5cbi8vIOKUgOKUgCByb3RhdGUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbnByb2dyYW1cbiAgLmNvbW1hbmQoJ3JvdGF0ZScpXG4gIC5kZXNjcmlwdGlvbignUm90YXRlIHRoZSBwcmltYXJ5IGNvbnNvbGUgdG9rZW4gKHJlcXVpcmVzIFRPVFAgY29uZmlybWF0aW9uKScpXG4gIC5vcHRpb24oJy0tanNvbicsICdPdXRwdXQgYXMgSlNPTiBmb3Igc2NyaXB0ZWQgY29uc3VtcHRpb24nKVxuICAub3B0aW9uKCctLWNvZGUgPGNvZGU+JywgJ1RPVFAgY29uZmlybWF0aW9uIGNvZGUgKHByb21wdHMgaWYgb21pdHRlZCknKVxuICAuYWN0aW9uKGFzeW5jIChvcHRpb25zOiB7IGpzb24/OiBib29sZWFuOyBjb2RlPzogc3RyaW5nIH0pID0+IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBleGVjdXRlUm90YXRpb24ob3B0aW9ucywgJ3JvdGF0aW9uJyk7XG4gICAgcHJpbnRSb3RhdGlvblJlc3VsdChyZXN1bHQsIG9wdGlvbnMsICdyb3RhdGlvbicpO1xuICB9KTtcblxuLy8g4pSA4pSAIHJldm9rZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxucHJvZ3JhbVxuICAuY29tbWFuZCgncmV2b2tlJylcbiAgLmRlc2NyaXB0aW9uKCdSZXZva2UgYSB0b2tlbiDigJQgcm90YXRlcyB0aGUgcHJpbWFyeSB0byBpbnZhbGlkYXRlIHRoZSBjdXJyZW50IHZhbHVlJylcbiAgLm9wdGlvbignLS1qc29uJywgJ091dHB1dCBhcyBKU09OIGZvciBzY3JpcHRlZCBjb25zdW1wdGlvbicpXG4gIC5vcHRpb24oJy0tY29kZSA8Y29kZT4nLCAnVE9UUCBjb25maXJtYXRpb24gY29kZSAocHJvbXB0cyBpZiBvbWl0dGVkKScpXG4gIC5hY3Rpb24oYXN5bmMgKG9wdGlvbnM6IHsganNvbj86IGJvb2xlYW47IGNvZGU/OiBzdHJpbmcgfSkgPT4ge1xuICAgIC8vIEZvciBQaGFzZSAyIHdpdGggYSBzaW5nbGUgcHJpbWFyeSB0b2tlbiwgcmV2b2tlID09IHJvdGF0ZS5cbiAgICAvLyBXaGVuIG11bHRpLWRldmljZSB0b2tlbnMgbGFuZCwgLS1pZCA8dXVpZD4gcmVtb3ZlcyBhIHNwZWNpZmljXG4gICAgLy8gbm9uLXByaW1hcnkgdG9rZW4gZnJvbSB0aGUgc3RvcmUuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZXhlY3V0ZVJvdGF0aW9uKG9wdGlvbnMsICdyZXZvY2F0aW9uJyk7XG4gICAgcHJpbnRSb3RhdGlvblJlc3VsdChyZXN1bHQsIG9wdGlvbnMsICdyZXZvY2F0aW9uJyk7XG4gIH0pO1xuXG5wcm9ncmFtLnBhcnNlKHByb2Nlc3MuYXJndik7XG4iXX0=