@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.
145 lines • 17.9 kB
JavaScript
/**
* Persona loading and file management
*/
import * as fs from 'fs/promises';
import * as path from 'path';
import { ensureDirectory, generateUniqueId } from '../utils/filesystem.js';
import { SecureYamlParser } from '../security/secureYamlParser.js';
import { SecurityError } from '../errors/SecurityError.js';
import { logger } from '../utils/logger.js';
import { PortfolioManager, ElementType } from '../portfolio/PortfolioManager.js';
export class PersonaLoader {
personasDir;
portfolioManager;
constructor(personasDir) {
// Use PortfolioManager for new portfolio structure
this.portfolioManager = PortfolioManager.getInstance();
// If personasDir is provided, it's for legacy compatibility
// Otherwise use the portfolio personas directory
this.personasDir = personasDir || this.portfolioManager.getElementDir(ElementType.PERSONA);
}
/**
* Load all personas from the personas directory
*/
async loadAll(getCurrentUser) {
// Ensure directory exists
await ensureDirectory(this.personasDir);
const personas = new Map();
try {
const files = await fs.readdir(this.personasDir);
const markdownFiles = files.filter(file => file.endsWith('.md'));
for (const file of markdownFiles) {
try {
const persona = await this.loadPersona(file, getCurrentUser);
if (persona) {
personas.set(file, persona);
logger.debug(`Loaded persona: ${persona.metadata.name} (${persona.unique_id})`);
}
}
catch (error) {
logger.error(`Error loading persona ${file}:`, error);
}
}
}
catch (error) {
logger.error(`Error reading personas directory:`, error);
}
return personas;
}
/**
* Load a single persona from file
*/
async loadPersona(filename, getCurrentUser) {
try {
const filePath = path.join(this.personasDir, filename);
const fileContent = await fs.readFile(filePath, 'utf-8');
// Use secure YAML parser instead of direct gray-matter
let parsed;
try {
parsed = SecureYamlParser.safeMatter(fileContent);
}
catch (error) {
if (error instanceof SecurityError) {
logger.error(`Security threat detected in persona ${filename}: ${error.message}`);
return null;
}
throw error;
}
const metadata = parsed.data;
const content = parsed.content;
if (!metadata.name) {
metadata.name = path.basename(filename, '.md');
}
// Generate unique ID if not present
let uniqueId = metadata.unique_id;
if (!uniqueId) {
const authorForId = metadata.author || getCurrentUser() || undefined;
uniqueId = generateUniqueId(metadata.name, authorForId);
logger.debug(`Generated unique ID for ${metadata.name}: ${uniqueId}`);
}
// Set default values for metadata fields
this.setDefaultMetadata(metadata);
const persona = {
metadata,
content,
filename,
unique_id: uniqueId,
};
return persona;
}
catch (error) {
logger.error(`Error loading persona ${filename}:`, error);
return null;
}
}
/**
* Save a persona to file
*/
async savePersona(persona) {
const filePath = path.join(this.personasDir, persona.filename);
// Use secure YAML stringification
const secureParser = SecureYamlParser.createSecureMatterParser();
const fileContent = secureParser.stringify(persona.content, persona.metadata);
await fs.writeFile(filePath, fileContent, 'utf-8');
}
/**
* Delete a persona file
*/
async deletePersona(filename) {
const filePath = path.join(this.personasDir, filename);
await fs.unlink(filePath);
}
/**
* Check if a persona file exists
*/
async personaExists(filename) {
try {
const filePath = path.join(this.personasDir, filename);
await fs.access(filePath);
return true;
}
catch {
return false;
}
}
/**
* Set default metadata values
*/
setDefaultMetadata(metadata) {
if (!metadata.category)
metadata.category = 'general';
if (!metadata.age_rating)
metadata.age_rating = 'all';
if (!metadata.content_flags)
metadata.content_flags = [];
if (metadata.ai_generated === undefined)
metadata.ai_generated = false;
if (!metadata.generation_method)
metadata.generation_method = 'human';
if (!metadata.price)
metadata.price = 'free';
if (!metadata.license)
metadata.license = 'CC-BY-SA-4.0';
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGVyc29uYUxvYWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wZXJzb25hL1BlcnNvbmFMb2FkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEtBQUssRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsQyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUc3QixPQUFPLEVBQUUsZUFBZSxFQUFFLGdCQUFnQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDM0UsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDbkUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQzNELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFFakYsTUFBTSxPQUFPLGFBQWE7SUFDaEIsV0FBVyxDQUFTO0lBQ3BCLGdCQUFnQixDQUFtQjtJQUUzQyxZQUFZLFdBQW9CO1FBQzlCLG1EQUFtRDtRQUNuRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDdkQsNERBQTREO1FBQzVELGlEQUFpRDtRQUNqRCxJQUFJLENBQUMsV0FBVyxHQUFHLFdBQVcsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsYUFBYSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3RixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsT0FBTyxDQUFDLGNBQW1DO1FBQy9DLDBCQUEwQjtRQUMxQixNQUFNLGVBQWUsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7UUFFeEMsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQW1CLENBQUM7UUFFNUMsSUFBSSxDQUFDO1lBQ0gsTUFBTSxLQUFLLEdBQUcs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Q0FBQyxDQUFDO1lBQ3ZELE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUMxQixPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxrQkFBa0IsQ0FBQyxRQUF5QjtRQUNsRCxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVE7WUFBRSxRQUFRLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQztRQUN0RCxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVU7WUFBRSxRQUFRLENBQUMsVUFBVSxHQUFHLEtBQUssQ0FBQztRQUN0RCxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWE7WUFBRSxRQUFRLENBQUMsYUFBYSxHQUFHLEVBQUUsQ0FBQztRQUN6RCxJQUFJLFFBQVEsQ0FBQyxZQUFZLEtBQUssU0FBUztZQUFFLFFBQVEsQ0FBQyxZQUFZLEdBQUcsS0FBSyxDQUFDO1FBQ3ZFLElBQUksQ0FBQyxRQUFRLENBQUMsaUJBQWlCO1lBQUUsUUFBUSxDQUFDLGlCQUFpQixHQUFHLE9BQU8sQ0FBQztRQUN0RSxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUs7WUFBRSxRQUFRLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQztRQUM3QyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU87WUFBRSxRQUFRLENBQUMsT0FBTyxHQUFHLGNBQWMsQ0FBQztJQUMzRCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBlcnNvbmEgbG9hZGluZyBhbmQgZmlsZSBtYW5hZ2VtZW50XG4gKi9cblxuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMvcHJvbWlzZXMnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCBtYXR0ZXIgZnJvbSAnZ3JheS1tYXR0ZXInO1xuaW1wb3J0IHsgUGVyc29uYSwgUGVyc29uYU1ldGFkYXRhIH0gZnJvbSAnLi4vdHlwZXMvcGVyc29uYS5qcyc7XG5pbXBvcnQgeyBlbnN1cmVEaXJlY3RvcnksIGdlbmVyYXRlVW5pcXVlSWQgfSBmcm9tICcuLi91dGlscy9maWxlc3lzdGVtLmpzJztcbmltcG9ydCB7IFNlY3VyZVlhbWxQYXJzZXIgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cmVZYW1sUGFyc2VyLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5RXJyb3IgfSBmcm9tICcuLi9lcnJvcnMvU2VjdXJpdHlFcnJvci5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgUG9ydGZvbGlvTWFuYWdlciwgRWxlbWVudFR5cGUgfSBmcm9tICcuLi9wb3J0Zm9saW8vUG9ydGZvbGlvTWFuYWdlci5qcyc7XG5cbmV4cG9ydCBjbGFzcyBQZXJzb25hTG9hZGVyIHtcbiAgcHJpdmF0ZSBwZXJzb25hc0Rpcjogc3RyaW5nO1xuICBwcml2YXRlIHBvcnRmb2xpb01hbmFnZXI6IFBvcnRmb2xpb01hbmFnZXI7XG4gIFxuICBjb25zdHJ1Y3RvcihwZXJzb25hc0Rpcj86IHN0cmluZykge1xuICAgIC8vIFVzZSBQb3J0Zm9saW9NYW5hZ2VyIGZvciBuZXcgcG9ydGZvbGlvIHN0cnVjdHVyZVxuICAgIHRoaXMucG9ydGZvbGlvTWFuYWdlciA9IFBvcnRmb2xpb01hbmFnZXIuZ2V0SW5zdGFuY2UoKTtcbiAgICAvLyBJZiBwZXJzb25hc0RpciBpcyBwcm92aWRlZCwgaXQncyBmb3IgbGVnYWN5IGNvbXBhdGliaWxpdHlcbiAgICAvLyBPdGhlcndpc2UgdXNlIHRoZSBwb3J0Zm9saW8gcGVyc29uYXMgZGlyZWN0b3J5XG4gICAgdGhpcy5wZXJzb25hc0RpciA9IHBlcnNvbmFzRGlyIHx8IHRoaXMucG9ydGZvbGlvTWFuYWdlci5nZXRFbGVtZW50RGlyKEVsZW1lbnRUeXBlLlBFUlNPTkEpO1xuICB9XG4gIFxuICAvKipcbiAgICogTG9hZCBhbGwgcGVyc29uYXMgZnJvbSB0aGUgcGVyc29uYXMgZGlyZWN0b3J5XG4gICAqL1xuICBhc3luYyBsb2FkQWxsKGdldEN1cnJlbnRVc2VyOiAoKSA9PiBzdHJpbmcgfCBudWxsKTogUHJvbWlzZTxNYXA8c3RyaW5nLCBQZXJzb25hPj4ge1xuICAgIC8vIEVuc3VyZSBkaXJlY3RvcnkgZXhpc3RzXG4gICAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5KHRoaXMucGVyc29uYXNEaXIpO1xuICAgIFxuICAgIGNvbnN0IHBlcnNvbmFzID0gbmV3IE1hcDxzdHJpbmcsIFBlcnNvbmE+KCk7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLnBlcnNvbmFzRGlyKTtcbiAgICAgIGNvbnN0IG1hcmtkb3duRmlsZXMgPSBmaWxlcy5maWx0ZXIoZmlsZSA9PiBmaWxlLmVuZHNXaXRoKCcubWQnKSk7XG4gICAgICBcbiAgICAgIGZvciAoY29uc3QgZmlsZSBvZiBtYXJrZG93bkZpbGVzKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgcGVyc29uYSA9IGF3YWl0IHRoaXMubG9hZFBlcnNvbmEoZmlsZSwgZ2V0Q3VycmVudFVzZXIpO1xuICAgICAgICAgIGlmIChwZXJzb25hKSB7XG4gICAgICAgICAgICBwZXJzb25hcy5zZXQoZmlsZSwgcGVyc29uYSk7XG4gICAgICAgICAgICBsb2dnZXIuZGVidWcoYExvYWRlZCBwZXJzb25hOiAke3BlcnNvbmEubWV0YWRhdGEubmFtZX0gKCR7cGVyc29uYS51bmlxdWVfaWR9KWApO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoYEVycm9yIGxvYWRpbmcgcGVyc29uYSAke2ZpbGV9OmAsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoYEVycm9yIHJlYWRpbmcgcGVyc29uYXMgZGlyZWN0b3J5OmAsIGVycm9yKTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHBlcnNvbmFzO1xuICB9XG4gIFxuICAvKipcbiAgICogTG9hZCBhIHNpbmdsZSBwZXJzb25hIGZyb20gZmlsZVxuICAgKi9cbiAgYXN5bmMgbG9hZFBlcnNvbmEoZmlsZW5hbWU6IHN0cmluZywgZ2V0Q3VycmVudFVzZXI6ICgpID0+IHN0cmluZyB8IG51bGwpOiBQcm9taXNlPFBlcnNvbmEgfCBudWxsPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZpbGVQYXRoID0gcGF0aC5qb2luKHRoaXMucGVyc29uYXNEaXIsIGZpbGVuYW1lKTtcbiAgICAgIGNvbnN0IGZpbGVDb250ZW50ID0gYXdhaXQgZnMucmVhZEZpbGUoZmlsZVBhdGgsICd1dGYtOCcpO1xuICAgICAgXG4gICAgICAvLyBVc2Ugc2VjdXJlIFlBTUwgcGFyc2VyIGluc3RlYWQgb2YgZGlyZWN0IGdyYXktbWF0dGVyXG4gICAgICBsZXQgcGFyc2VkO1xuICAgICAgdHJ5IHtcbiAgICAgICAgcGFyc2VkID0gU2VjdXJlWWFtbFBhcnNlci5zYWZlTWF0dGVyKGZpbGVDb250ZW50KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlY3VyaXR5RXJyb3IpIHtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoYFNlY3VyaXR5IHRocmVhdCBkZXRlY3RlZCBpbiBwZXJzb25hICR7ZmlsZW5hbWV9OiAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgICBcbiAgICAgIGNvbnN0IG1ldGFkYXRhID0gcGFyc2VkLmRhdGEgYXMgUGVyc29uYU1ldGFkYXRhO1xuICAgICAgY29uc3QgY29udGVudCA9IHBhcnNlZC5jb250ZW50O1xuICAgICAgXG4gICAgICBpZiAoIW1ldGFkYXRhLm5hbWUpIHtcbiAgICAgICAgbWV0YWRhdGEubmFtZSA9IHBhdGguYmFzZW5hbWUoZmlsZW5hbWUsICcubWQnKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gR2VuZXJhdGUgdW5pcXVlIElEIGlmIG5vdCBwcmVzZW50XG4gICAgICBsZXQgdW5pcXVlSWQgPSBtZXRhZGF0YS51bmlxdWVfaWQ7XG4gICAgICBpZiAoIXVuaXF1ZUlkKSB7XG4gICAgICAgIGNvbnN0IGF1dGhvckZvcklkID0gbWV0YWRhdGEuYXV0aG9yIHx8IGdldEN1cnJlbnRVc2VyKCkgfHwgdW5kZWZpbmVkO1xuICAgICAgICB1bmlxdWVJZCA9IGdlbmVyYXRlVW5pcXVlSWQobWV0YWRhdGEubmFtZSwgYXV0aG9yRm9ySWQpO1xuICAgICAgICBsb2dnZXIuZGVidWcoYEdlbmVyYXRlZCB1bmlxdWUgSUQgZm9yICR7bWV0YWRhdGEubmFtZX06ICR7dW5pcXVlSWR9YCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFNldCBkZWZhdWx0IHZhbHVlcyBmb3IgbWV0YWRhdGEgZmllbGRzXG4gICAgICB0aGlzLnNldERlZmF1bHRNZXRhZGF0YShtZXRhZGF0YSk7XG4gICAgICBcbiAgICAgIGNvbnN0IHBlcnNvbmE6IFBlcnNvbmEgPSB7XG4gICAgICAgIG1ldGFkYXRhLFxuICAgICAgICBjb250ZW50LFxuICAgICAgICBmaWxlbmFtZSxcbiAgICAgICAgdW5pcXVlX2lkOiB1bmlxdWVJZCxcbiAgICAgIH07XG4gICAgICBcbiAgICAgIHJldHVybiBwZXJzb25hO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoYEVycm9yIGxvYWRpbmcgcGVyc29uYSAke2ZpbGVuYW1lfTpgLCBlcnJvcik7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBTYXZlIGEgcGVyc29uYSB0byBmaWxlXG4gICAqL1xuICBhc3luYyBzYXZlUGVyc29uYShwZXJzb25hOiBQZXJzb25hKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgZmlsZVBhdGggPSBwYXRoLmpvaW4odGhpcy5wZXJzb25hc0RpciwgcGVyc29uYS5maWxlbmFtZSk7XG4gICAgXG4gICAgLy8gVXNlIHNlY3VyZSBZQU1MIHN0cmluZ2lmaWNhdGlvblxuICAgIGNvbnN0IHNlY3VyZVBhcnNlciA9IFNlY3VyZVlhbWxQYXJzZXIuY3JlYXRlU2VjdXJlTWF0dGVyUGFyc2VyKCk7XG4gICAgY29uc3QgZmlsZUNvbnRlbnQgPSBzZWN1cmVQYXJzZXIuc3RyaW5naWZ5KHBlcnNvbmEuY29udGVudCwgcGVyc29uYS5tZXRhZGF0YSk7XG4gICAgXG4gICAgYXdhaXQgZnMud3JpdGVGaWxlKGZpbGVQYXRoLCBmaWxlQ29udGVudCwgJ3V0Zi04Jyk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBEZWxldGUgYSBwZXJzb25hIGZpbGVcbiAgICovXG4gIGFzeW5jIGRlbGV0ZVBlcnNvbmEoZmlsZW5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGZpbGVQYXRoID0gcGF0aC5qb2luKHRoaXMucGVyc29uYXNEaXIsIGZpbGVuYW1lKTtcbiAgICBhd2FpdCBmcy51bmxpbmsoZmlsZVBhdGgpO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2hlY2sgaWYgYSBwZXJzb25hIGZpbGUgZXhpc3RzXG4gICAqL1xuICBhc3luYyBwZXJzb25hRXhpc3RzKGZpbGVuYW1lOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZmlsZVBhdGggPSBwYXRoLmpvaW4odGhpcy5wZXJzb25hc0RpciwgZmlsZW5hbWUpO1xuICAgICAgYXdhaXQgZnMuYWNjZXNzKGZpbGVQYXRoKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIFNldCBkZWZhdWx0IG1ldGFkYXRhIHZhbHVlc1xuICAgKi9cbiAgcHJpdmF0ZSBzZXREZWZhdWx0TWV0YWRhdGEobWV0YWRhdGE6IFBlcnNvbmFNZXRhZGF0YSk6IHZvaWQge1xuICAgIGlmICghbWV0YWRhdGEuY2F0ZWdvcnkpIG1ldGFkYXRhLmNhdGVnb3J5ID0gJ2dlbmVyYWwnO1xuICAgIGlmICghbWV0YWRhdGEuYWdlX3JhdGluZykgbWV0YWRhdGEuYWdlX3JhdGluZyA9ICdhbGwnO1xuICAgIGlmICghbWV0YWRhdGEuY29udGVudF9mbGFncykgbWV0YWRhdGEuY29udGVudF9mbGFncyA9IFtdO1xuICAgIGlmIChtZXRhZGF0YS5haV9nZW5lcmF0ZWQgPT09IHVuZGVmaW5lZCkgbWV0YWRhdGEuYWlfZ2VuZXJhdGVkID0gZmFsc2U7XG4gICAgaWYgKCFtZXRhZGF0YS5nZW5lcmF0aW9uX21ldGhvZCkgbWV0YWRhdGEuZ2VuZXJhdGlvbl9tZXRob2QgPSAnaHVtYW4nO1xuICAgIGlmICghbWV0YWRhdGEucHJpY2UpIG1ldGFkYXRhLnByaWNlID0gJ2ZyZWUnO1xuICAgIGlmICghbWV0YWRhdGEubGljZW5zZSkgbWV0YWRhdGEubGljZW5zZSA9ICdDQy1CWS1TQS00LjAnO1xuICB9XG59Il19