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.

637 lines 91.4 kB
#!/usr/bin/env node /** * CLI command for converting between Anthropic Skills and DollhouseMCP Skills * * Usage: * dollhouse convert to-anthropic <input> [options] * dollhouse convert from-anthropic <input> [options] * * Input formats: * to-anthropic: DollhouseMCP skill file (.md) * from-anthropic: Anthropic skill directory or ZIP file * * Options: * -o, --output <dir> Output directory * Default for from-anthropic: ~/.dollhouse/portfolio/skills * Default for to-anthropic: ./anthropic-skills * -v, --verbose Show detailed conversion steps * -r, --report Generate conversion report * --dry-run Preview conversion without executing * * Examples: * # Convert downloaded ZIP file from Claude.ai * dollhouse convert from-anthropic ~/Downloads/my-skill.zip * * # Convert Anthropic skill directory * dollhouse convert from-anthropic ~/Downloads/my-skill-folder * * # Export DollhouseMCP skill to share * dollhouse convert to-anthropic ~/.dollhouse/portfolio/skills/my-skill.md -o ./exported */ import * as fs from 'node:fs'; import * as path from 'node:path'; import * as os from 'node:os'; import { Command } from 'commander'; import chalk from 'chalk'; import extract from 'extract-zip'; import { DollhouseToAnthropicConverter, AnthropicToDollhouseConverter } from '../converters/index.js'; import { SecurityMonitor } from '../security/securityMonitor.js'; import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; import { resolvePathWithinBase } from '../utils/pathSecurity.js'; const program = new Command(); /** * Maximum ZIP file size (100MB) - prevents DoS attacks and system resource exhaustion */ const MAX_ZIP_SIZE_BYTES = 100 * 1024 * 1024; // 100MB /** * Maximum extracted size (500MB) - prevents zip bomb attacks */ const MAX_EXTRACTED_SIZE_BYTES = 500 * 1024 * 1024; // 500MB /** * Progress indicator threshold (10MB) - show progress message for files larger than this */ const PROGRESS_THRESHOLD_BYTES = 10 * 1024 * 1024; // 10MB /** * Get the default DollhouseMCP portfolio skills directory * SECURITY FIX (DMCP-SEC-004): Normalize HOME environment variable to prevent Unicode attacks * Previously: Used process.env.HOME directly without normalization * Now: Validate and normalize all path components including environment variables */ function getDefaultSkillsDirectory() { const homeDir = process.env.HOME || ''; // Normalize HOME environment variable to prevent homograph attacks via lookalike characters let normalizedHome = ''; if (homeDir) { const normalizationResult = UnicodeValidator.normalize(homeDir); normalizedHome = normalizationResult.normalizedContent; // [SECURITY WARNING] Log if normalization changed the HOME path // This could indicate a security issue or misconfiguration if (normalizedHome !== homeDir) { console.warn(chalk.yellow(`[WARNING] HOME environment variable was modified during Unicode normalization.\n` + `Original: "${homeDir}"\n` + `Normalized: "${normalizedHome}"\n` + `This may indicate a security issue or misconfiguration.`)); } } return path.join(normalizedHome, '.dollhouse', 'portfolio', 'skills'); } /** * Format bytes to human-readable string */ function formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; } /** * Calculate total size of extracted files */ function calculateExtractedSize(directory) { let totalSize = 0; function walkDir(dir) { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { walkDir(fullPath); } else if (entry.isFile()) { totalSize += fs.statSync(fullPath).size; } } } walkDir(directory); return totalSize; } /** * Extract a ZIP file to a temporary directory with size limits and progress indication * @returns Path to extracted directory * @throws Error if ZIP exceeds size limits */ async function extractZipFile(zipPath, verbose) { // FIX: Validate ZIP file size BEFORE extraction to prevent DoS attacks // Previously: No size validation, allowing extraction of arbitrarily large files // Now: Enforce 100MB ZIP size limit and 500MB extracted size limit const zipStats = fs.statSync(zipPath); const zipSize = zipStats.size; // SECURITY CHECK (typescript:S5042): Validate ZIP size before extraction if (zipSize > MAX_ZIP_SIZE_BYTES) { throw new Error(`ZIP file too large: ${formatBytes(zipSize)}. Maximum allowed: ${formatBytes(MAX_ZIP_SIZE_BYTES)}. ` + `This limit prevents DoS attacks and system resource exhaustion.`); } const tempDir = path.join(os.tmpdir(), `dollhouse-extract-${Date.now()}`); // FIX: Add security audit logging for ZIP operations // Previously: No logging of ZIP extraction operations // Now: Log all ZIP operations for security audit trail SecurityMonitor.logSecurityEvent({ type: 'FILE_COPIED', severity: 'LOW', source: 'convert CLI', details: `ZIP extraction: ${zipPath} (${formatBytes(zipSize)}) -> ${tempDir}` }); if (verbose) { console.log(chalk.blue('\nExtracting ZIP file...')); console.log(chalk.gray(` ZIP: ${zipPath}`)); console.log(chalk.gray(` Size: ${formatBytes(zipSize)}`)); console.log(chalk.gray(` Temp dir: ${tempDir}`)); } // FIX: Add progress indicator for large ZIP extractions // Previously: No feedback during extraction, poor UX for large files // Now: Show progress message for better user experience const startTime = Date.now(); const progressMessage = zipSize > PROGRESS_THRESHOLD_BYTES ? 'Extracting (this may take a moment)...' : 'Extracting...'; if (verbose || zipSize > PROGRESS_THRESHOLD_BYTES) { console.log(chalk.blue(` ${progressMessage}`)); } // SONARCLOUD FIX (typescript:S5042): Archive extraction is safe here // - ZIP size validated (max 100MB) at lines 110-114 to prevent DoS // - Extracted size validated (max 500MB) at lines 151-157 to prevent zip bombs // - Extraction to isolated temp directory (no path traversal risk) // - Full cleanup in finally block prevents resource leaks await extract(zipPath, { dir: tempDir }); const extractTime = Date.now() - startTime; if (verbose) { console.log(chalk.gray(` Extracted in ${extractTime}ms`)); } // SECURITY CHECK (typescript:S5042): Validate extracted size to prevent zip bomb attacks // Zip bombs are malicious archives that expand to enormous sizes (e.g., 42KB → 4.5PB) // This check prevents system resource exhaustion from maliciously compressed files const extractedSize = calculateExtractedSize(tempDir); if (extractedSize > MAX_EXTRACTED_SIZE_BYTES) { // Cleanup before throwing error to prevent temp file accumulation fs.rmSync(tempDir, { recursive: true, force: true }); throw new Error(`Extracted content too large: ${formatBytes(extractedSize)}. Maximum allowed: ${formatBytes(MAX_EXTRACTED_SIZE_BYTES)}. This may be a zip bomb attack.`); } if (verbose) { console.log(chalk.gray(` Extracted size: ${formatBytes(extractedSize)}`)); } // Find the skill directory (should be the only top-level directory) const contents = fs.readdirSync(tempDir); const directories = contents.filter(item => fs.statSync(path.join(tempDir, item)).isDirectory()); if (directories.length === 1) { // Return the skill directory path return path.join(tempDir, directories[0]); } else if (contents.length > 0) { // If multiple items or no directory, return temp dir itself return tempDir; } else { throw new Error('ZIP file appears to be empty'); } } /** * Check if a file is a ZIP file based on extension */ function isZipFile(filePath) { return path.extname(filePath).toLowerCase() === '.zip'; } /** * Prepare input for conversion (handles both directories and ZIP files) * @returns {actualInput, tempDir} - actualInput is the directory to convert, tempDir is the temp dir to cleanup (or null) */ async function prepareConversionInput(input, verbose) { // Verify input exists if (!fs.existsSync(input)) { console.error(chalk.red(`Input not found: ${input}`)); process.exit(1); } // Handle ZIP files if (isZipFile(input)) { const actualInput = await extractZipFile(input, verbose); const tempDir = path.dirname(actualInput); return { actualInput, tempDir }; } // Handle directories if (!fs.statSync(input).isDirectory()) { console.error(chalk.red(`Input must be a directory or ZIP file: ${input}`)); process.exit(1); } return { actualInput: input, tempDir: null }; } program .name('convert') .description('Convert between Anthropic Skills and DollhouseMCP Skills formats') .command('to-anthropic <input>') .description('Convert DollhouseMCP skill to Anthropic Skills format') .option('-o, --output <dir>', 'Output directory', './anthropic-skills') .option('-v, --verbose', 'Show detailed conversion steps', false) .option('-r, --report', 'Generate conversion report', false) .option('--dry-run', 'Preview conversion without executing', false) .action(async (input, options) => { await convertToAnthropic(input, options); }); program .command('from-anthropic <input>') .description('Convert Anthropic Skills to DollhouseMCP skill format (input can be a directory or ZIP file)') .option('-o, --output <dir>', 'Output directory', getDefaultSkillsDirectory()) .option('-v, --verbose', 'Show detailed conversion steps', false) .option('-r, --report', 'Generate conversion report', false) .option('--dry-run', 'Preview conversion without executing', false) .action(async (input, options) => { await convertFromAnthropic(input, options); }); /** * Convert DollhouseMCP skill to Anthropic format */ async function convertToAnthropic(input, options) { // SECURITY FIX (DMCP-SEC-004): Use UnicodeValidator to normalize all user input // Previously: Used built-in normalize() which doesn't detect homograph attacks // Now: UnicodeValidator.normalize() detects and prevents lookalike character attacks const inputValidation = UnicodeValidator.normalize(input); input = inputValidation.normalizedContent; if (options.output) { const outputValidation = UnicodeValidator.normalize(options.output); options.output = outputValidation.normalizedContent; } try { logOperation('to-anthropic', input, options); // Read input file if (!fs.existsSync(input)) { console.error(chalk.red(`Input file not found: ${input}`)); process.exit(1); } const inputContent = fs.readFileSync(input, 'utf-8'); const skillName = path.basename(input, path.extname(input)); if (options.verbose) { console.log(chalk.blue('\nReading DollhouseMCP skill...')); console.log(chalk.gray(` Input: ${input}`)); console.log(chalk.gray(` Size: ${inputContent.length} bytes`)); } // Convert const converter = new DollhouseToAnthropicConverter(); const operationsLog = []; if (options.verbose) { console.log(chalk.blue('\nConverting to Anthropic Skills format...')); } const structure = await converter.convertSkill(inputContent); logConversionSteps(structure, operationsLog, options.verbose); // Determine output directory const outputDir = resolvePathWithinBase(options.output || './anthropic-skills', skillName); if (options.dryRun) { console.log(chalk.yellow('\n[DRY RUN] Would create:')); console.log(chalk.gray(` Output directory: ${outputDir}`)); console.log(chalk.gray(` Files: ${countFiles(structure)} files`)); listFilesToCreate(structure); process.exit(0); } // Write output if (options.verbose) { console.log(chalk.blue(`\nWriting Anthropic skill to: ${outputDir}`)); } await converter.writeToDirectory(structure, outputDir); const filesCreated = getCreatedFiles(structure, outputDir); // Generate report if (options.report) { const report = generateReport({ timestamp: new Date().toISOString(), direction: 'to-anthropic', input, output: outputDir, filesCreated, operationsPerformed: operationsLog, success: true }); const reportPath = resolvePathWithinBase(outputDir, '.conversion-report.md'); fs.writeFileSync(reportPath, report); if (options.verbose) { console.log(chalk.gray(`\nConversion report: ${reportPath}`)); } } // Success console.log(chalk.green('\n✓ Conversion complete')); console.log(chalk.gray(` Created ${filesCreated.length} file(s) in: ${outputDir}`)); process.exit(0); } catch (error) { console.error(chalk.red('\n✗ Conversion failed:'), error); process.exit(1); } } /** * Cleanup temporary directory safely * SONARCLOUD FIX (typescript:S3776): Extracted from convertFromAnthropic to reduce cognitive complexity * Previously: Inline cleanup logic added nested conditions increasing complexity * Now: Separate function handles cleanup with proper error handling */ function cleanupTempDirectory(tempDir, verbose) { if (tempDir && fs.existsSync(tempDir)) { if (verbose) { console.log(chalk.gray(`\nCleaning up temporary files...`)); } try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch (cleanupError) { // Log error details but don't fail on cleanup errors const errorMessage = cleanupError instanceof Error ? cleanupError.message : String(cleanupError); console.warn(chalk.yellow(`Warning: Failed to cleanup temp directory: ${tempDir} - ${errorMessage}`)); } } } /** * Convert Anthropic skill to DollhouseMCP format */ async function convertFromAnthropic(input, options) { // SECURITY FIX (DMCP-SEC-004): Use UnicodeValidator to normalize all user input // Previously: Used built-in normalize() which doesn't detect homograph attacks // Now: UnicodeValidator.normalize() detects and prevents lookalike character attacks // Example: "file\u0041.txt" vs "file\u0301A.txt" both look like "fileA.txt" but are different const inputValidation = UnicodeValidator.normalize(input); input = inputValidation.normalizedContent; if (options.output) { const outputValidation = UnicodeValidator.normalize(options.output); options.output = outputValidation.normalizedContent; } let tempDir = null; try { logOperation('from-anthropic', input, options); // Prepare input (handles both directories and ZIP files) const { actualInput, tempDir: extractedTempDir } = await prepareConversionInput(input, options.verbose); tempDir = extractedTempDir; const skillName = path.basename(actualInput); if (options.verbose) { console.log(chalk.blue('\nReading Anthropic skill...')); console.log(chalk.gray(` Input: ${actualInput}`)); listAnthropicStructure(actualInput); } // Convert const converter = new AnthropicToDollhouseConverter(); const operationsLog = []; if (options.verbose) { console.log(chalk.blue('\nConverting to DollhouseMCP Skills format...')); } const dollhouseSkill = await converter.convertSkill(actualInput); logReverseConversionSteps(actualInput, operationsLog, options.verbose); // Determine output file const outputDir = path.resolve(options.output || getDefaultSkillsDirectory()); const outputFile = resolvePathWithinBase(outputDir, `${skillName}.md`); if (options.dryRun) { console.log(chalk.yellow('\n[DRY RUN] Would create:')); console.log(chalk.gray(` Output file: ${outputFile}`)); console.log(chalk.gray(` Size: ${dollhouseSkill.length} bytes`)); process.exit(0); } // Write output if (options.verbose) { console.log(chalk.blue(`\nWriting DollhouseMCP skill to: ${outputFile}`)); } if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } await converter.writeToFile(dollhouseSkill, outputFile); // Generate report if (options.report) { const report = generateReport({ timestamp: new Date().toISOString(), direction: 'from-anthropic', input, output: outputFile, filesCreated: [outputFile], operationsPerformed: operationsLog, success: true }); const reportPath = resolvePathWithinBase(outputDir, `${skillName}-conversion-report.md`); fs.writeFileSync(reportPath, report); if (options.verbose) { console.log(chalk.gray(`\nConversion report: ${reportPath}`)); } } // Success console.log(chalk.green('\n✓ Conversion complete')); console.log(chalk.gray(` Created: ${outputFile}`)); process.exit(0); } catch (error) { console.error(chalk.red('\n✗ Conversion failed:'), error); process.exit(1); } finally { // SONARCLOUD FIX (typescript:S3776): Use extracted cleanup function to reduce complexity // FIX: Ensure cleanup happens on both success and failure // Previously: Cleanup only on success path // Now: Cleanup in finally block to handle all error scenarios cleanupTempDirectory(tempDir, options.verbose); } } /** * Log conversion operation for security audit */ function logOperation(direction, input, options) { SecurityMonitor.logSecurityEvent({ type: 'FILE_COPIED', severity: 'LOW', source: 'convert CLI', details: `Conversion ${direction}: ${input} (dry-run: ${options.dryRun})` }); } /** * Helper to log directory operations with optional file listing */ function logDirectoryOperation(structure, options) { const { dirKey, dirName, action, itemType, operationsLog, verbose, listFiles = false } = options; if (structure[dirKey]) { const items = structure[dirKey]; const count = Object.keys(items).length; operationsLog.push(`${action} ${count} ${itemType}(s) to ${dirName}/`); if (verbose) { console.log(chalk.gray(` ✓ ${action} ${count} ${itemType}(s)`)); if (listFiles) { for (const filename of Object.keys(items)) { console.log(chalk.gray(` - ${dirName}/${filename}`)); } } } } } /** * Log conversion steps during to-anthropic conversion */ function logConversionSteps(structure, operationsLog, verbose) { // Log SKILL.md operationsLog.push('Created SKILL.md with simplified metadata'); if (verbose) { console.log(chalk.gray(' ✓ Created SKILL.md')); } // Log directory operations logDirectoryOperation(structure, { dirKey: 'scripts/', dirName: 'scripts', action: 'Extracted', itemType: 'script', operationsLog, verbose, listFiles: true }); logDirectoryOperation(structure, { dirKey: 'reference/', dirName: 'reference', action: 'Extracted', itemType: 'reference document', operationsLog, verbose, listFiles: true }); logDirectoryOperation(structure, { dirKey: 'examples/', dirName: 'examples', action: 'Extracted', itemType: 'example', operationsLog, verbose }); logDirectoryOperation(structure, { dirKey: 'themes/', dirName: 'themes', action: 'Extracted', itemType: 'template', operationsLog, verbose }); // Log metadata preservation if (structure['metadata/']) { operationsLog.push('Preserved full DollhouseMCP metadata to metadata/dollhouse.yaml'); if (verbose) { console.log(chalk.gray(' ✓ Preserved metadata for perfect roundtrip')); } } } /** * Helper to log directory operations for reverse conversion (from-anthropic) */ function logReverseDirectoryOperation(inputDir, dirName, itemType, operationsLog, verbose) { const dirPath = path.join(inputDir, dirName); if (fs.existsSync(dirPath)) { const count = fs.readdirSync(dirPath).length; operationsLog.push(`Combined ${count} ${itemType}(s)${itemType === 'script' ? ' as code blocks' : ''}`); if (verbose) { console.log(chalk.gray(` ✓ Combined ${count} ${itemType}(s)`)); } } } /** * Log conversion steps during from-anthropic conversion */ function logReverseConversionSteps(inputDir, operationsLog, verbose) { operationsLog.push('Read SKILL.md metadata', 'Enriched metadata with DollhouseMCP fields'); // Log directory operations using helper logReverseDirectoryOperation(inputDir, 'scripts', 'script', operationsLog, verbose); logReverseDirectoryOperation(inputDir, 'reference', 'reference document', operationsLog, verbose); logReverseDirectoryOperation(inputDir, 'examples', 'example', operationsLog, verbose); logReverseDirectoryOperation(inputDir, 'themes', 'template', operationsLog, verbose); // Handle metadata restoration if (fs.existsSync(path.join(inputDir, 'metadata'))) { operationsLog.push('Restored original DollhouseMCP metadata from metadata/dollhouse.yaml'); if (verbose) { console.log(chalk.gray(' ✓ Restored original metadata (perfect roundtrip)')); } } } /** * List Anthropic skill directory structure */ function listAnthropicStructure(inputDir) { const subdirs = ['scripts', 'reference', 'examples', 'themes', 'metadata']; for (const subdir of subdirs) { const dirPath = path.join(inputDir, subdir); if (fs.existsSync(dirPath)) { const files = fs.readdirSync(dirPath); console.log(chalk.gray(` ${subdir}/: ${files.length} file(s)`)); } } } /** * Count total files in Anthropic structure */ function countFiles(structure) { let count = 1; // SKILL.md if (structure['scripts/']) count += Object.keys(structure['scripts/']).length; if (structure['reference/']) count += Object.keys(structure['reference/']).length; if (structure['examples/']) count += Object.keys(structure['examples/']).length; if (structure['themes/']) count += Object.keys(structure['themes/']).length; if (structure['LICENSE.txt']) count += 1; return count; } /** * Iterate through all files in an Anthropic structure * Executes a callback for each file found */ function iterateStructureFiles(structure, callback) { // Always include SKILL.md callback(null, 'SKILL.md'); // Iterate through directories const directories = ['scripts/', 'reference/', 'examples/', 'themes/', 'metadata/']; for (const dir of directories) { const dirContent = structure[dir]; if (dirContent) { for (const filename of Object.keys(dirContent)) { callback(dir.slice(0, -1), filename); // Remove trailing slash } } } // Check for LICENSE.txt if (structure['LICENSE.txt']) { callback(null, 'LICENSE.txt'); } } /** * List files that would be created (dry-run mode) */ function listFilesToCreate(structure) { console.log(chalk.gray(' Files:')); iterateStructureFiles(structure, (dirName, filename) => { const filePath = dirName ? `${dirName}/${filename}` : filename; console.log(chalk.gray(` - ${filePath}`)); }); } /** * Get list of created files */ function getCreatedFiles(structure, outputDir) { const files = []; iterateStructureFiles(structure, (dirName, filename) => { const filePath = dirName ? resolvePathWithinBase(outputDir, dirName, filename) : resolvePathWithinBase(outputDir, filename); files.push(filePath); }); return files; } /** * Generate conversion report */ function generateReport(data) { const lines = [ '# Skill Conversion Report', '', `**Timestamp**: ${data.timestamp}`, `**Direction**: ${data.direction}`, `**Status**: ${data.success ? '✓ Success' : '✗ Failed'}`, '', '## Input/Output', '', `**Input**: \`${data.input}\``, `**Output**: \`${data.output}\``, '' ]; if (data.filesCreated.length > 0) { lines.push('## Files Created', '', ...data.filesCreated.map(file => `- \`${file}\``), ''); } lines.push('## Operations Performed', '', ...data.operationsPerformed.map(operation => `- ${operation}`), ''); if (data.error) { lines.push('## Error', '', '```', data.error, '```', ''); } lines.push('---', '', '*Generated by DollhouseMCP Converter*'); return lines.join('\n'); } // Parse command line arguments program.parse(process.argv); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udmVydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGkvY29udmVydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBRUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E0Qkc7QUFFSCxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEtBQUssSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUNsQyxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMxQixPQUFPLE9BQU8sTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxFQUNILDZCQUE2QixFQUM3Qiw2QkFBNkIsRUFFaEMsTUFBTSx3QkFBd0IsQ0FBQztBQUNoQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDakUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sNENBQTRDLENBQUM7QUFDOUUsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFakUsTUFBTSxPQUFPLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQztBQUU5Qjs7R0FFRztBQUNILE1BQU0sa0JBQWtCLEdBQUcsR0FBRyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQyxRQUFRO0FBRXREOztHQUVHO0FBQ0gsTUFBTSx3QkFBd0IsR0FBRyxHQUFHLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQyxDQUFDLFFBQVE7QUFFNUQ7O0dBRUc7QUFDSCxNQUFNLHdCQUF3QixHQUFHLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsT0FBTztBQUUxRDs7Ozs7R0FLRztBQUNILFNBQVMseUJBQXlCO0lBQzlCLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLEVB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