@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.
550 lines • 90.4 kB
JavaScript
/**
* DollhouseMCP Web UI Server
*
* Lightweight Express server for browsing portfolio elements in a browser.
* Bound to 127.0.0.1 only (localhost). Read-only for V1.
*
* Can be started standalone (`--web` flag) or from within the MCP server
* process via `openPortfolioBrowser()`.
*
* @see https://github.com/DollhouseMCP/mcp-server-v2-refactor/issues/704
* @see https://github.com/DollhouseMCP/mcp-server-v2-refactor/issues/774
*/
import express from 'express';
import { join, dirname, extname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { execFile } from 'node:child_process';
import { platform } from 'node:os';
import { mkdir, readdir, readFile as readFileFs } from 'node:fs/promises';
import { createApiRoutes, createGatewayApiRoutes } from './routes.js';
import { createLogRoutes } from './routes/logRoutes.js';
import { createMetricsRoutes } from './routes/metricsRoutes.js';
import { createHealthRoutes } from './routes/healthRoutes.js';
import { createSetupRoutes, repairNvmLauncherOnStartup } from './routes/setupRoutes.js';
import { repairPermissionHooksOnStartup } from '../utils/permissionHooks.js';
import { createTotpRoutes } from './routes/totpRoutes.js';
import { createTokenRoutes } from './routes/tokenRoutes.js';
import { logger } from '../utils/logger.js';
import { env } from '../config/env.js';
import { createAuthMiddleware } from './middleware/authMiddleware.js';
import { PACKAGE_VERSION } from '../generated/version.js';
/**
* Public path prefixes that never require authentication (#1780).
* These endpoints return safe metadata or act as health probes — requiring
* tokens on them would break monitoring and client detection without adding
* real security value.
*/
const PUBLIC_PATH_PREFIXES = [
'/api/health',
'/api/setup/version',
'/api/setup/mcpb',
'/api/setup/detect',
'/api/setup/license',
];
/** Placeholder in index.html that is replaced with the current console token. */
const TOKEN_META_PLACEHOLDER = '{{CONSOLE_TOKEN}}';
/** Placeholder in index.html that is replaced with the running server version. */
const VERSION_META_PLACEHOLDER = '{{DOLLHOUSE_VERSION}}';
/** Placeholder in index.html that is replaced with the stable Dollhouse session ID. */
const SESSION_ID_META_PLACEHOLDER = '{{DOLLHOUSE_SESSION_ID}}';
/** Placeholder in index.html that is replaced with the runtime session ID. */
const RUNTIME_SESSION_ID_META_PLACEHOLDER = '{{DOLLHOUSE_RUNTIME_SESSION_ID}}';
/** Placeholder in index.html that is replaced with the asset cache-busting version. */
const ASSET_VERSION_META_PLACEHOLDER = '{{DOLLHOUSE_ASSET_VERSION}}';
const __dirname = dirname(fileURLToPath(import.meta.url));
/**
* Default port for standalone `startWebServer` calls. Reads from the
* `DOLLHOUSE_WEB_CONSOLE_PORT` env var so there is a single source of
* truth (see `src/config/env.ts`). Callers passing an explicit `port` in
* `WebServerOptions` override this default.
*/
const DEFAULT_PORT = env.DOLLHOUSE_WEB_CONSOLE_PORT;
const CONSOLE_HOST = 'dollhouse.localhost';
const ALLOWED_PAGE_EXTENSIONS = new Set(['.html', '.htm']);
/** Max JSON body for setup routes (install/open-config). Ingest routes use their own 1mb limit. */
const SETUP_BODY_LIMIT = '1kb';
/** Track whether the web server is already running in-process. */
let serverRunning = false;
let serverPort = DEFAULT_PORT;
/** Active HTTP server instance — tracked so _resetServerState can close it. */
let activeHttpServer = null;
/** Cached token store for openPortfolioBrowser — prevents duplicate instances on the same file. */
let cachedTokenStore = null;
/** Check whether the web server has been started in this process. */
export function isWebServerRunning() {
return serverRunning;
}
/**
* Reset module-level server state. Exported for testing only —
* allows tests to exercise startWebServer/bindAndListen without
* interference from prior runs in the same process.
* @internal
*/
export function _resetServerState() {
if (activeHttpServer) {
activeHttpServer.close();
activeHttpServer = null;
}
serverRunning = false;
serverPort = DEFAULT_PORT;
cachedTokenStore = null;
}
/**
* Gracefully shut down the HTTP server (#1856).
* Closes the active server and resets module state so the port is freed
* immediately rather than lingering until process exit.
*/
export function shutdownWebServer() {
if (activeHttpServer) {
activeHttpServer.close();
activeHttpServer = null;
serverRunning = false;
logger.info(`[WebUI] HTTP server closed on port ${serverPort}`);
}
}
/**
* Open a URL in the system's default browser.
*
* Platform-aware:
* - macOS: `open`
* - Linux: `xdg-open`
* - Windows: `start`
*
* @param url - The URL to open
* @returns Promise that resolves to true if the browser opened, false with error message if not
*/
function openInBrowser(url) {
return new Promise((resolve) => {
const plat = platform();
const cmd = plat === 'darwin' ? 'open'
: plat === 'win32' ? 'start'
: 'xdg-open';
// Security: use execFile with URL as argument array, not string interpolation
const urlStr = String(url);
// Accept localhost, 127.0.0.1, and *.localhost subdomains (RFC 6761)
if (!/^https?:\/\/(localhost|127\.0\.0\.1|[\w-]+\.localhost)[:/]/.test(urlStr)) {
resolve({ success: false, error: 'URL must be a localhost HTTP URL' });
return;
}
execFile(cmd, [urlStr], (err) => {
if (err) {
logger.warn(`[WebUI] Could not auto-open browser: ${err.message}`);
resolve({ success: false, error: err.message });
}
else {
resolve({ success: true });
}
});
});
}
/**
* Start the portfolio web server.
*
* Binds to 127.0.0.1 only (localhost). Serves the portfolio browser
* frontend and API routes for reading elements.
*
* Idempotent: if the server is already running, optionally opens the
* browser without starting a second instance.
*
* @param options - Server configuration
* @returns Hooks for DI wiring (log broadcast, metrics onSnapshot)
*/
export async function startWebServer(options) {
const port = options.port || DEFAULT_PORT;
const result = {};
if (serverRunning) {
if (options.openBrowser) {
openInBrowser(`http://${CONSOLE_HOST}:${serverPort}`);
}
return result;
}
const app = express();
result.app = app;
app.disable('x-powered-by');
// Security headers
app.use((_req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Referrer-Policy', 'no-referrer');
res.setHeader('Access-Control-Allow-Origin', `http://${CONSOLE_HOST}:${port}`);
res.setHeader('Content-Security-Policy', [
"default-src 'self'",
"script-src 'self' cdn.jsdelivr.net cdnjs.cloudflare.com",
"style-src 'self' 'unsafe-inline' cdnjs.cloudflare.com cdn.jsdelivr.net",
"img-src 'self' data:",
"connect-src 'self' raw.githubusercontent.com",
"font-src 'self'",
].join('; '));
next();
});
// Console token authentication middleware (#1780). Mounted before any /api
// routes so every protected endpoint goes through it. When the feature flag
// DOLLHOUSE_WEB_AUTH_ENABLED is false (Phase 1 default) this is a pass-through.
// Public endpoints in PUBLIC_PATH_PREFIXES always bypass auth regardless of flag.
if (options.tokenStore) {
const authMiddleware = createAuthMiddleware({
store: options.tokenStore,
enabled: env.DOLLHOUSE_WEB_AUTH_ENABLED,
publicPathPrefixes: PUBLIC_PATH_PREFIXES,
label: 'api',
});
app.use('/api', authMiddleware);
logger.info(`[WebUI] Console auth middleware mounted ${env.DOLLHOUSE_WEB_AUTH_ENABLED ? 'ENFORCING' : 'pass-through (flag off)'}`);
// TOTP enrollment routes (#1794). Mounted AFTER the /api auth middleware
// because the router adds its own always-on auth guard — the global auth
// middleware at /api is a pass-through during Phase 1 rollout, but the
// TOTP router enforces regardless of DOLLHOUSE_WEB_AUTH_ENABLED so an
// attacker with local port access cannot pre-enroll a second factor and
// lock the legitimate user out.
app.use('/api/console/totp', createTotpRoutes({ store: options.tokenStore }));
logger.info('[WebUI] TOTP routes mounted at /api/console/totp (always-on auth)');
// Token management routes (#1795). Mounted alongside the TOTP router with
// the same always-on auth pattern. Currently hosts the rotation endpoint;
// future token management operations (list, revoke) go here too.
app.use('/api/console/token', createTokenRoutes({ store: options.tokenStore }));
logger.info('[WebUI] Token routes mounted at /api/console/token (always-on auth)');
}
// Setup routes: auto-install DollhouseMCP to MCP clients (mount BEFORE API routes)
// Body limit scoped to setup routes only — ingest routes need 1mb for follower log forwarding
const setupJsonParser = express.json({ limit: SETUP_BODY_LIMIT, type: 'application/json' });
const { installHandler, openConfigHandler, versionHandler, mcpbRedirectHandler, detectHandler, getLicenseHandler, setLicenseHandler, verifyLicenseHandler, resendVerificationHandler } = createSetupRoutes();
app.post('/api/setup/install', setupJsonParser, installHandler);
app.post('/api/setup/open-config', setupJsonParser, openConfigHandler);
app.get('/api/setup/version', versionHandler);
app.get('/api/setup/mcpb', mcpbRedirectHandler);
app.get('/api/setup/detect', detectHandler);
app.get('/api/setup/license', getLicenseHandler);
app.post('/api/setup/license', setupJsonParser, setLicenseHandler);
app.post('/api/setup/license/verify', setupJsonParser, verifyLicenseHandler);
app.post('/api/setup/license/resend', setupJsonParser, resendVerificationHandler);
logger.info('[WebUI] Setup routes mounted at /api/setup');
// Fire-and-forget NVM launcher repair: recreates the wrapper if deleted and
// patches any pre-existing configs that still use bare `npx`. No-ops when
// NVM is absent or on Windows. Never delays server startup.
repairNvmLauncherOnStartup().catch(err => logger.warn(`[Setup] NVM startup repair threw unexpectedly: ${err instanceof Error ? err.message : String(err)}`));
if (process.env.NODE_ENV !== 'test') {
repairPermissionHooksOnStartup()
.then((summary) => {
if (summary.needsRepairCount > 0) {
logger.warn(`[Setup] Permission hook startup repair completed with ${summary.needsRepairCount} issue(s) ` +
`across ${summary.hostResults.length} host(s)`);
}
})
.catch(err => logger.warn(`[Setup] Permission hook startup repair threw unexpectedly: ${err instanceof Error ? err.message : String(err)}`));
}
// API routes — use MCP-AQL gateway when handler is available (Issue #796)
if (options.mcpAqlHandler) {
app.use('/api', createGatewayApiRoutes(options.mcpAqlHandler, options.portfolioDir));
// Permission evaluation routes (POST /evaluate_permission, GET /permissions/status)
const { registerPermissionRoutes } = await import('./routes/permissionRoutes.js');
const permRouter = (await import('express')).Router();
registerPermissionRoutes(permRouter, options.mcpAqlHandler);
app.use('/api', permRouter);
logger.info('[WebUI] API routes using MCP-AQL Gateway + permission routes');
}
else {
app.use('/api', createApiRoutes(options.portfolioDir));
logger.warn('[WebUI] API routes using direct filesystem access (no MCP-AQL handler available)');
}
// Console routes: logs, metrics, health — extracted to keep cognitive
// complexity of startWebServer manageable.
mountConsoleRoutes(app, options, result);
// Serve ~/.dollhouse/pages/ at /pages/ — dashboards, generated content, stack views
const pagesDir = join(dirname(options.portfolioDir), 'pages');
mkdir(pagesDir, { recursive: true }).catch(err => {
logger.warn(`[WebUI] Could not create pages directory: ${err.message}`);
});
app.use('/pages', express.static(pagesDir));
/**
* GET /api/pages
* Lists available HTML pages in ~/.dollhouse/pages/.
* Returns page names and their URLs for the management console.
*/
app.get('/api/pages', async (_req, res) => {
try {
const files = await readdir(pagesDir);
const pages = files
.filter(f => !f.startsWith('.') && ALLOWED_PAGE_EXTENSIONS.has(extname(f)))
.map(f => ({ name: f, url: `/pages/${f}` }));
res.json({ pages, directory: pagesDir });
}
catch {
res.json({ pages: [], directory: pagesDir });
}
});
// Additional routers (e.g., unified console ingest routes) — must mount before SPA fallback
options.additionalRouters?.forEach(router => app.use(router));
// Static frontend files
const publicDir = join(__dirname, 'public');
// Serve static assets but skip index.html — the SPA fallback below
// handles it with token injection (replaces {{CONSOLE_TOKEN}} in the
// meta tag). Without this, express.static serves the raw template
// and the browser never gets the auth token (#1780).
const isDebug = Boolean(process.env.DOLLHOUSE_DEBUG || process.env.ENABLE_DEBUG);
app.use(express.static(publicDir, {
index: false,
// In debug mode, disable caching on all static assets (JS, CSS) so
// UI changes are picked up on normal reload without Cmd+Shift+R.
...(isDebug
? { etag: false, lastModified: false, maxAge: 0 }
: { maxAge: '1y', immutable: true }),
}));
// SPA fallback with console token injection (#1780).
// Reads index.html on first request, substitutes the {{CONSOLE_TOKEN}} placeholder
// with the current token value, and caches the rendered string. The cache is
// auto-invalidated when the primary token changes (rotation), so a page reload
// after rotation picks up the new token without a server restart.
let cachedIndexHtml = null;
let cachedTokenValue = null;
const indexHtmlPath = join(publicDir, 'index.html');
const renderIndexHtml = async () => {
const tokenValue = options.tokenStore?.getPrimaryTokenValue() ?? '';
// Auto-invalidate cache when the token changes (rotation).
// In debug mode, always re-read from disk so UI changes are picked up on reload.
if (!isDebug && cachedIndexHtml !== null && cachedTokenValue === tokenValue) {
return cachedIndexHtml;
}
const template = await readFileFs(indexHtmlPath, 'utf8');
// Defensive HTML attribute escape. Tokens are strict 64-char lowercase hex
// today so no escaping is actually needed, but if the token format ever
// changes this prevents an HTML-injection regression from landing silently.
const escapedToken = tokenValue
.replaceAll('&', '&')
.replaceAll('"', '"')
.replaceAll("'", ''')
.replaceAll('<', '<')
.replaceAll('>', '>');
const escapedSessionId = (options.sessionId ?? '')
.replaceAll('&', '&')
.replaceAll('"', '"')
.replaceAll("'", ''')
.replaceAll('<', '<')
.replaceAll('>', '>');
const escapedRuntimeSessionId = (options.runtimeSessionId ?? '')
.replaceAll('&', '&')
.replaceAll('"', '"')
.replaceAll("'", ''')
.replaceAll('<', '<')
.replaceAll('>', '>');
cachedIndexHtml = template
.replaceAll(TOKEN_META_PLACEHOLDER, escapedToken)
.replaceAll(VERSION_META_PLACEHOLDER, PACKAGE_VERSION)
.replaceAll(SESSION_ID_META_PLACEHOLDER, escapedSessionId)
.replaceAll(RUNTIME_SESSION_ID_META_PLACEHOLDER, escapedRuntimeSessionId)
.replaceAll(ASSET_VERSION_META_PLACEHOLDER, PACKAGE_VERSION);
cachedTokenValue = tokenValue;
return cachedIndexHtml;
};
app.get('/{*path}', async (req, res) => {
const normalizedPath = req.path.normalize('NFC');
if (normalizedPath.startsWith('/api/')) {
res.status(404).json({ error: `API route not found: ${normalizedPath}` });
return;
}
if (normalizedPath.startsWith('/pages/')) {
res.status(404).json({ error: `Page not found: ${normalizedPath}` });
return;
}
try {
const html = await renderIndexHtml();
res.setHeader('Content-Type', 'text/html; charset=utf-8');
// The shell should always revalidate so a forced reload can pick up
// a new leader/version immediately. Asset files themselves carry a
// version query and can be cached aggressively.
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.send(html);
}
catch (err) {
logger.error(`[WebUI] Failed to render index.html: ${err.message}`);
res.status(500).send('Failed to load console');
}
});
// Global error handler — catch Express errors and route to logger instead of terminal.
// Without this, Express dumps stack traces to stderr (visible in --web terminal).
// All errors still appear in the management console's Logs tab via MemoryLogSink.
app.use((err, _req, res, _next) => {
const status = err.status || err.statusCode || 500;
logger.warn(`[WebUI] ${err.name}: ${err.message}`);
if (!res.headersSent) {
res.status(status).json({ error: err.message });
}
});
// Bind to localhost only — handle port conflicts gracefully.
// Extracted to a helper to keep startWebServer's cognitive complexity manageable.
result.bindResult = await bindAndListen(app, port, options);
return result;
}
/**
* Mount the logs, metrics, and health routes. These are only mounted when the
* corresponding sinks are provided (memory log sink for logs+health, metrics
* sink for the metrics tab). Extracted from startWebServer to cap the main
* function's cognitive complexity.
*/
function mountConsoleRoutes(app, options, result) {
let logRoutes;
let metricsRoutes;
if (options.memorySink) {
logRoutes = createLogRoutes(options.memorySink);
app.use('/api', logRoutes.router);
result.logBroadcast = logRoutes.broadcast;
logger.info('[WebUI] Log viewer routes mounted at /api/logs');
}
if (options.metricsSink) {
metricsRoutes = createMetricsRoutes(options.metricsSink);
app.use('/api', metricsRoutes.router);
result.metricsOnSnapshot = metricsRoutes.onSnapshot;
logger.info('[WebUI] Metrics routes mounted at /api/metrics');
}
if (options.memorySink) {
const healthRouter = createHealthRoutes({
memorySink: options.memorySink,
metricsSink: options.metricsSink,
logClientCount: logRoutes ? logRoutes.clientCount : () => 0,
metricsClientCount: metricsRoutes ? metricsRoutes.clientCount : () => 0,
});
app.use('/api', healthRouter);
}
}
/**
* Print the startup banner to stderr.
*
* NOTE: Use stderr for terminal output, not stdout. In MCP stdio mode, stdout
* is reserved for JSON-RPC messages — any non-JSON output corrupts the protocol.
* stderr is safe for human-readable messages in both MCP and standalone modes.
*/
function printStartupBanner(port, tokenStore) {
const url = `http://${CONSOLE_HOST}:${port}`;
const fallbackUrl = `http://127.0.0.1:${port}`;
logger.info(`[WebUI] Management console running at ${url}`);
console.error(`\n DollhouseMCP Management Console\n ${url}\n ${fallbackUrl} (fallback)\n`);
if (tokenStore) {
console.error(` Session token: ${tokenStore.getFilePath()}\n`);
}
console.error(` Type "q" or "quit" to exit.\n`);
}
// Stale process recovery — extracted to StaleProcessRecovery.ts for independent testing (#1850).
import { recoverStalePort } from './console/StaleProcessRecovery.js';
export { findPidOnPort, killStaleProcess, recoverStalePort } from './console/StaleProcessRecovery.js';
/**
* Attempt a single port bind. Returns a BindResult without any recovery logic.
*/
function attemptBind(app, port, options) {
return new Promise((resolve) => {
const httpServer = app.listen(port, '127.0.0.1', () => {
serverRunning = true;
serverPort = port;
activeHttpServer = httpServer;
printStartupBanner(port, options.tokenStore);
if (options.openBrowser) {
openInBrowser(`http://${CONSOLE_HOST}:${port}`);
}
resolve({ success: true });
});
httpServer.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
resolve({ success: false, error: 'EADDRINUSE', detail: `Port ${port} already in use` });
}
else {
logger.error(`[WebUI] Failed to bind port ${port}: ${err.message}`);
resolve({ success: false, error: 'OTHER', detail: err.message });
}
});
});
}
/**
* Bind the Express app to 127.0.0.1:port. On EADDRINUSE, attempt to find
* and kill the stale DollhouseMCP process holding the port, then retry once.
*/
async function bindAndListen(app, port, options) {
const result = await attemptBind(app, port, options);
if (result.success || result.error !== 'EADDRINUSE')
return result;
// Port occupied — attempt stale process recovery and retry
if (await recoverStalePort(port)) {
const retryResult = await attemptBind(app, port, options);
if (retryResult.success)
return retryResult;
}
// Still can't bind — fall through with warning
logger.warn(`[WebUI] Port ${port} already in use — another process holds this port`);
console.error(`\n DollhouseMCP Management Console (existing instance)\n http://${CONSOLE_HOST}:${port}\n`);
if (options.openBrowser) {
openInBrowser(`http://${CONSOLE_HOST}:${port}`);
}
return result;
}
/**
* Self-provision sinks, token store, and ingest routes, then start the web
* server. Extracted from openPortfolioBrowser to keep cognitive complexity
* manageable (SonarCloud S3776).
*/
async function startFallbackServer(options, port) {
let memorySink = options.memorySink;
let metricsSink = options.metricsSink;
if (!memorySink) {
const { MemoryLogSink: LogSink } = await import('../logging/sinks/MemoryLogSink.js');
memorySink = new LogSink({ appCapacity: 10000, securityCapacity: 5000, perfCapacity: 2000, telemetryCapacity: 1000 });
}
if (!metricsSink) {
const { MemoryMetricsSink: MetricsSink } = await import('../metrics/sinks/MemoryMetricsSink.js');
metricsSink = new MetricsSink(240);
}
// Reuse cached token store — two instances on the same file can race on writes.
if (!cachedTokenStore) {
const { ConsoleTokenStore: TokenStore } = await import('./console/consoleToken.js');
const { pickRandomTokenName } = await import('./console/SessionNames.js');
cachedTokenStore = new TokenStore(env.DOLLHOUSE_CONSOLE_TOKEN_FILE);
try {
await cachedTokenStore.ensureInitialized(pickRandomTokenName());
}
catch (err) {
logger.warn('[WebUI] Failed to init token store for browser open', err);
}
}
// logBroadcast is deferred — wired after startWebServer returns the real broadcast fn.
let liveBroadcast;
const { createIngestRoutes } = await import('./console/IngestRoutes.js');
const ingestResult = createIngestRoutes({
logBroadcast: (entry) => liveBroadcast?.(entry),
storeMetricsSnapshot: (snapshot) => metricsSink?.onSnapshot(snapshot),
});
ingestResult.registerConsoleSession();
const webResult = await startWebServer({
portfolioDir: options.portfolioDir,
port,
openBrowser: false,
mcpAqlHandler: options.mcpAqlHandler,
memorySink,
metricsSink,
tokenStore: cachedTokenStore,
additionalRouters: [ingestResult.router],
});
liveBroadcast = webResult.logBroadcast;
}
export async function openPortfolioBrowser(options) {
const targetPort = options.port || DEFAULT_PORT;
const baseUrl = `http://${CONSOLE_HOST}:${targetPort}`;
// Build URL with optional tab hash and query parameters
let url = baseUrl;
if (options.tab) {
const qs = options.urlParams ? new URLSearchParams(options.urlParams).toString() : '';
url = `${baseUrl}/#${options.tab}${qs ? '?' + qs : ''}`;
}
else if (options.urlParams && Object.keys(options.urlParams).length > 0) {
const qs = new URLSearchParams(options.urlParams).toString();
url = `${baseUrl}/#portfolio?${qs}`;
}
const alreadyRunning = serverRunning;
if (!serverRunning) {
await startFallbackServer(options, targetPort);
}
const browserResult = await openInBrowser(url);
return {
url,
alreadyRunning,
browserOpened: browserResult.success,
...(browserResult.error ? { warning: `Browser could not be opened automatically: ${browserResult.error}. Open ${url} manually.` } : {}),
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dlYi9zZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7O0dBV0c7QUFFSCxPQUFPLE9BQU8sTUFBTSxTQUFTLENBQUM7QUFDOUIsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ25ELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDekMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDbkMsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsUUFBUSxJQUFJLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzFFLE9BQU8sRUFBRSxlQUFlLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDdEUsT0FBTyxFQUFFLGVBQWUsRUFBd0IsTUFBTSx1QkFBdUIsQ0FBQztBQUM5RSxPQUFPLEVBQUUsbUJBQW1CLEVBQTRCLE1BQU0sMkJBQTJCLENBQUM7QUFDMUYsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDOUQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLDBCQUEwQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDeEYsT0FBTyxFQUFFLDhCQUE4QixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDN0UsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDMUQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUt2QyxPQUFPLEVBQUUs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R0FBRyxHQUFHLE9BQU8sZUFBZSxFQUFFLEVBQUUsQ0FBQztJQUN0QyxDQUFDO0lBRUQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDO0lBRXJDLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUNuQixNQUFNLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBRUQsTUFBTSxhQUFhLEdBQUcsTUFBTSxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFL0MsT0FBTztRQUNMLEdBQUc7UUFDSCxjQUFjO1FBQ2QsYUFBYSxFQUFFLGFBQWEsQ0FBQyxPQUFPO1FBQ3BDLEdBQUcsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSw4Q0FBOEMsYUFBYSxDQUFDLEtBQUssVUFBVSxHQUFHLFlBQVksRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7S0FDeEksQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIERvbGxob3VzZU1DUCBXZWIgVUkgU2VydmVyXG4gKlxuICogTGlnaHR3ZWlnaHQgRXhwcmVzcyBzZXJ2ZXIgZm9yIGJyb3dzaW5nIHBvcnRmb2xpbyBlbGVtZW50cyBpbiBhIGJyb3dzZXIuXG4gKiBCb3VuZCB0byAxMjcuMC4wLjEgb25seSAobG9jYWxob3N0KS4gUmVhZC1vbmx5IGZvciBWMS5cbiAqXG4gKiBDYW4gYmUgc3RhcnRlZCBzdGFuZGFsb25lIChgLS13ZWJgIGZsYWcpIG9yIGZyb20gd2l0aGluIHRoZSBNQ1Agc2VydmVyXG4gKiBwcm9jZXNzIHZpYSBgb3BlblBvcnRmb2xpb0Jyb3dzZXIoKWAuXG4gKlxuICogQHNlZSBodHRwczovL2dpdGh1Yi5jb20vRG9sbGhvdXNlTUNQL21jcC1zZXJ2ZXItdjItcmVmYWN0b3IvaXNzdWVzLzcwNFxuICog