@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.
120 lines • 16.4 kB
JavaScript
/**
* PortfolioDownloader - Downloads elements from GitHub repositories
*
* Handles fetching file contents from GitHub, decoding base64 content,
* and returning structured element data ready for local storage.
*/
import { logger } from '../utils/logger.js';
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
export class PortfolioDownloader {
/**
* Download an element from GitHub
*/
async downloadFromGitHub(repoManager, elementPath, username, repository) {
try {
logger.info('Downloading element from GitHub', {
path: elementPath,
username,
repository
});
// Fetch the file content from GitHub
const response = await repoManager.githubRequest(`/repos/${username}/${repository}/contents/${elementPath}`);
if (!response || !response.content) {
throw new Error(`No content found at path: ${elementPath}`);
}
// Decode base64 content
const decodedContent = Buffer.from(response.content, 'base64').toString('utf-8');
// Normalize Unicode for security
const normalized = UnicodeValidator.normalize(decodedContent);
// Log download for audit trail
logger.info('Element downloaded successfully', {
path: elementPath,
repository: `${username}/${repository}`,
sha: response.sha
});
// Parse metadata from frontmatter if present
const metadata = this.extractMetadata(normalized.normalizedContent);
return {
content: normalized.normalizedContent,
metadata,
sha: response.sha
};
}
catch (error) {
logger.error('Failed to download element from GitHub', {
error,
path: elementPath
});
// Re-throw with more context
if (error instanceof Error) {
if (error.message.includes('404')) {
throw new Error(`Element not found at path: ${elementPath}`);
}
if (error.message.includes('401') || error.message.includes('403')) {
throw new Error(`Authentication failed. Please check your GitHub token.`);
}
throw error;
}
throw new Error(`Failed to download ${elementPath}: ${String(error)}`);
}
}
/**
* Extract metadata from frontmatter
*/
extractMetadata(content) {
const metadata = {};
// Check for YAML frontmatter
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (frontmatterMatch) {
try {
// Parse the frontmatter as simple key-value pairs
// (avoiding using yaml.load for security)
const frontmatterContent = frontmatterMatch[1];
const lines = frontmatterContent.split('\n');
for (const line of lines) {
const colonIndex = line.indexOf(':');
if (colonIndex > 0) {
const key = line.substring(0, colonIndex).trim();
const value = line.substring(colonIndex + 1).trim();
// Remove quotes if present (e.g., "value" -> value, 'value' -> value)
const cleanValue = value.replaceAll(/(^["'])|(['"]$)/g, '');
// Try to parse as JSON for arrays/objects, otherwise use as string
try {
metadata[key] = JSON.parse(cleanValue);
}
catch {
metadata[key] = cleanValue;
}
}
}
}
catch (error) {
logger.warn('Failed to parse frontmatter metadata', { error });
}
}
return metadata;
}
/**
* Download multiple elements in batch
*/
async downloadBatch(repoManager, elementPaths, username, repository, onProgress) {
const results = new Map();
let downloaded = 0;
for (const path of elementPaths) {
try {
const elementData = await this.downloadFromGitHub(repoManager, path, username, repository);
results.set(path, elementData);
downloaded++;
if (onProgress) {
onProgress(downloaded, elementPaths.length);
}
}
catch (error) {
logger.error(`Failed to download ${path}`, { error });
// Continue with other downloads even if one fails
}
}
return results;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUG9ydGZvbGlvRG93bmxvYWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zeW5jL1BvcnRmb2xpb0Rvd25sb2FkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFHSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sNENBQTRDLENBQUM7QUFROUUsTUFBTSxPQUFPLG1CQUFtQjtJQUM5Qjs7T0FFRztJQUNILEtBQUssQ0FBQyxrQkFBa0IsQ0FDdEIsV0FBaUMsRUFDakMsV0FBbUIsRUFDbkIsUUFBZ0IsRUFDaEIsVUFBa0I7UUFFbEIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxDQUFDLElBQUksQ0FBQyxpQ0FBaUMsRUFBRTtnQkFDN0MsSUFBSSxFQUFFLFdBQVc7Z0JBQ2pCLFFBQVE7Z0JBQ1IsVUFBVTthQUNYLENBQUMsQ0FBQztZQUVILHFDQUFxQztZQUNyQyxNQUFNLFFBQVEsR0FBRyxNQUFNLFdBQVcsQ0FBQyxhQUFhLENBQzlDLFVBQVUsUUFBUSxJQUFJLFVBQVUsYUFBYSxXQUFXLEVBQUUsQ0FDM0QsQ0FBQztZQUVGLElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ25DLE1BQU0sSUFBSSxLQUFLLENBQUMsNkJBQTZCLFdBQVcsRUFBRSxDQUFDLENBQUM7WUFDOUQsQ0FBQztZQUVELHdCQUF3QjtZQUN4QixNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBRWpGLGlDQUFpQztZQUNqQyxNQUFNLFVBQVUsR0FBRyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsY0FBYyxDQUFDLENBQUM7WUFFOUQsK0JBQStCO1lBQy9CLE1BQU0sQ0FBQyxJQUFJLENB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QUM7WUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO2dCQUNmLE1BQU0sQ0FBQyxJQUFJLENBQUMsc0NBQXNDLEVBQUUsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBQ2pFLENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxRQUFRLENBQUM7SUFDbEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLGFBQWEsQ0FDakIsV0FBaUMsRUFDakMsWUFBc0IsRUFDdEIsUUFBZ0IsRUFDaEIsVUFBa0IsRUFDbEIsVUFBd0Q7UUFFeEQsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7UUFDL0MsSUFBSSxVQUFVLEdBQUcsQ0FBQyxDQUFDO1FBRW5CLEtBQUssTUFBTSxJQUFJLElBQUksWUFBWSxFQUFFLENBQUM7WUFDaEMsSUFBSSxDQUFDO2dCQUNILE1BQU0sV0FBVyxHQUFHLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUMvQyxXQUFXLEVBQ1gsSUFBSSxFQUNKLFFBQVEsRUFDUixVQUFVLENBQ1gsQ0FBQztnQkFFRixPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQztnQkFDL0IsVUFBVSxFQUFFLENBQUM7Z0JBRWIsSUFBSSxVQUFVLEVBQUUsQ0FBQztvQkFDZixVQUFVLENBQUMsVUFBVSxFQUFFLFlBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDOUMsQ0FBQztZQUNILENBQUM7WUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO2dCQUNmLE1BQU0sQ0FBQyxLQUFLLENBQUMsc0JBQXNCLElBQUksRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDdEQsa0RBQWtEO1lBQ3BELENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQb3J0Zm9saW9Eb3dubG9hZGVyIC0gRG93bmxvYWRzIGVsZW1lbnRzIGZyb20gR2l0SHViIHJlcG9zaXRvcmllc1xuICogXG4gKiBIYW5kbGVzIGZldGNoaW5nIGZpbGUgY29udGVudHMgZnJvbSBHaXRIdWIsIGRlY29kaW5nIGJhc2U2NCBjb250ZW50LFxuICogYW5kIHJldHVybmluZyBzdHJ1Y3R1cmVkIGVsZW1lbnQgZGF0YSByZWFkeSBmb3IgbG9jYWwgc3RvcmFnZS5cbiAqL1xuXG5pbXBvcnQgeyBQb3J0Zm9saW9SZXBvTWFuYWdlciB9IGZyb20gJy4uL3BvcnRmb2xpby9Qb3J0Zm9saW9SZXBvTWFuYWdlci5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRWxlbWVudERhdGEge1xuICBjb250ZW50OiBzdHJpbmc7XG4gIG1ldGFkYXRhOiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuICBzaGE6IHN0cmluZztcbn1cblxuZXhwb3J0IGNsYXNzIFBvcnRmb2xpb0Rvd25sb2FkZXIge1xuICAvKipcbiAgICogRG93bmxvYWQgYW4gZWxlbWVudCBmcm9tIEdpdEh1YlxuICAgKi9cbiAgYXN5bmMgZG93bmxvYWRGcm9tR2l0SHViKFxuICAgIHJlcG9NYW5hZ2VyOiBQb3J0Zm9saW9SZXBvTWFuYWdlcixcbiAgICBlbGVtZW50UGF0aDogc3RyaW5nLFxuICAgIHVzZXJuYW1lOiBzdHJpbmcsXG4gICAgcmVwb3NpdG9yeTogc3RyaW5nXG4gICk6IFByb21pc2U8RWxlbWVudERhdGE+IHtcbiAgICB0cnkge1xuICAgICAgbG9nZ2VyLmluZm8oJ0Rvd25sb2FkaW5nIGVsZW1lbnQgZnJvbSBHaXRIdWInLCB7XG4gICAgICAgIHBhdGg6IGVsZW1lbnRQYXRoLFxuICAgICAgICB1c2VybmFtZSxcbiAgICAgICAgcmVwb3NpdG9yeVxuICAgICAgfSk7XG5cbiAgICAgIC8vIEZldGNoIHRoZSBmaWxlIGNvbnRlbnQgZnJvbSBHaXRIdWJcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgcmVwb01hbmFnZXIuZ2l0aHViUmVxdWVzdChcbiAgICAgICAgYC9yZXBvcy8ke3VzZXJuYW1lfS8ke3JlcG9zaXRvcnl9L2NvbnRlbnRzLyR7ZWxlbWVudFBhdGh9YFxuICAgICAgKTtcblxuICAgICAgaWYgKCFyZXNwb25zZSB8fCAhcmVzcG9uc2UuY29udGVudCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5vIGNvbnRlbnQgZm91bmQgYXQgcGF0aDogJHtlbGVtZW50UGF0aH1gKTtcbiAgICAgIH1cblxuICAgICAgLy8gRGVjb2RlIGJhc2U2NCBjb250ZW50XG4gICAgICBjb25zdCBkZWNvZGVkQ29udGVudCA9IEJ1ZmZlci5mcm9tKHJlc3BvbnNlLmNvbnRlbnQsICdiYXNlNjQnKS50b1N0cmluZygndXRmLTgnKTtcbiAgICAgIFxuICAgICAgLy8gTm9ybWFsaXplIFVuaWNvZGUgZm9yIHNlY3VyaXR5XG4gICAgICBjb25zdCBub3JtYWxpemVkID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUoZGVjb2RlZENvbnRlbnQpO1xuICAgICAgXG4gICAgICAvLyBMb2cgZG93bmxvYWQgZm9yIGF1ZGl0IHRyYWlsXG4gICAgICBsb2dnZXIuaW5mbygnRWxlbWVudCBkb3dubG9hZGVkIHN1Y2Nlc3NmdWxseScsIHtcbiAgICAgICAgcGF0aDogZWxlbWVudFBhdGgsXG4gICAgICAgIHJlcG9zaXRvcnk6IGAke3VzZXJuYW1lfS8ke3JlcG9zaXRvcnl9YCxcbiAgICAgICAgc2hhOiByZXNwb25zZS5zaGFcbiAgICAgIH0pO1xuXG4gICAgICAvLyBQYXJzZSBtZXRhZGF0YSBmcm9tIGZyb250bWF0dGVyIGlmIHByZXNlbnRcbiAgICAgIGNvbnN0IG1ldGFkYXRhID0gdGhpcy5leHRyYWN0TWV0YWRhdGEobm9ybWFsaXplZC5ub3JtYWxpemVkQ29udGVudCk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvbnRlbnQ6IG5vcm1hbGl6ZWQubm9ybWFsaXplZENvbnRlbnQsXG4gICAgICAgIG1ldGFkYXRhLFxuICAgICAgICBzaGE6IHJlc3BvbnNlLnNoYVxuICAgICAgfTtcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBkb3dubG9hZCBlbGVtZW50IGZyb20gR2l0SHViJywgeyBcbiAgICAgICAgZXJyb3IsIFxuICAgICAgICBwYXRoOiBlbGVtZW50UGF0aCBcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICAvLyBSZS10aHJvdyB3aXRoIG1vcmUgY29udGV4dFxuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgICAgaWYgKGVycm9yLm1lc3NhZ2UuaW5jbHVkZXMoJzQwNCcpKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFbGVtZW50IG5vdCBmb3VuZCBhdCBwYXRoOiAke2VsZW1lbnRQYXRofWApO1xuICAgICAgICB9XG4gICAgICAgIGlmIChlcnJvci5tZXNzYWdlLmluY2x1ZGVzKCc0MDEnKSB8fCBlcnJvci5tZXNzYWdlLmluY2x1ZGVzKCc0MDMnKSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQXV0aGVudGljYXRpb24gZmFpbGVkLiBQbGVhc2UgY2hlY2sgeW91ciBHaXRIdWIgdG9rZW4uYCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGRvd25sb2FkICR7ZWxlbWVudFBhdGh9OiAke1N0cmluZyhlcnJvcil9YCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEV4dHJhY3QgbWV0YWRhdGEgZnJvbSBmcm9udG1hdHRlclxuICAgKi9cbiAgcHJpdmF0ZSBleHRyYWN0TWV0YWRhdGEoY29udGVudDogc3RyaW5nKTogUmVjb3JkPHN0cmluZywgYW55PiB7XG4gICAgY29uc3QgbWV0YWRhdGE6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fTtcbiAgICBcbiAgICAvLyBDaGVjayBmb3IgWUFNTCBmcm9udG1hdHRlclxuICAgIGNvbnN0IGZyb250bWF0dGVyTWF0Y2ggPSBjb250ZW50Lm1hdGNoKC9eLS0tXFxuKFtcXHNcXFNdKj8pXFxuLS0tLyk7XG4gICAgXG4gICAgaWYgKGZyb250bWF0dGVyTWF0Y2gpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIFBhcnNlIHRoZSBmcm9udG1hdHRlciBhcyBzaW1wbGUga2V5LXZhbHVlIHBhaXJzXG4gICAgICAgIC8vIChhdm9pZGluZyB1c2luZyB5YW1sLmxvYWQgZm9yIHNlY3VyaXR5KVxuICAgICAgICBjb25zdCBmcm9udG1hdHRlckNvbnRlbnQgPSBmcm9udG1hdHRlck1hdGNoWzFdO1xuICAgICAgICBjb25zdCBsaW5lcyA9IGZyb250bWF0dGVyQ29udGVudC5zcGxpdCgnXFxuJyk7XG4gICAgICAgIFxuICAgICAgICBmb3IgKGNvbnN0IGxpbmUgb2YgbGluZXMpIHtcbiAgICAgICAgICBjb25zdCBjb2xvbkluZGV4ID0gbGluZS5pbmRleE9mKCc6Jyk7XG4gICAgICAgICAgaWYgKGNvbG9uSW5kZXggPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBrZXkgPSBsaW5lLnN1YnN0cmluZygwLCBjb2xvbkluZGV4KS50cmltKCk7XG4gICAgICAgICAgICBjb25zdCB2YWx1ZSA9IGxpbmUuc3Vic3RyaW5nKGNvbG9uSW5kZXggKyAxKS50cmltKCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIFJlbW92ZSBxdW90ZXMgaWYgcHJlc2VudCAoZS5nLiwgXCJ2YWx1ZVwiIC0+IHZhbHVlLCAndmFsdWUnIC0+IHZhbHVlKVxuICAgICAgICAgICAgY29uc3QgY2xlYW5WYWx1ZSA9IHZhbHVlLnJlcGxhY2VBbGwoLyheW1wiJ10pfChbJ1wiXSQpL2csICcnKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gVHJ5IHRvIHBhcnNlIGFzIEpTT04gZm9yIGFycmF5cy9vYmplY3RzLCBvdGhlcndpc2UgdXNlIGFzIHN0cmluZ1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgbWV0YWRhdGFba2V5XSA9IEpTT04ucGFyc2UoY2xlYW5WYWx1ZSk7XG4gICAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgICAgbWV0YWRhdGFba2V5XSA9IGNsZWFuVmFsdWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dnZXIud2FybignRmFpbGVkIHRvIHBhcnNlIGZyb250bWF0dGVyIG1ldGFkYXRhJywgeyBlcnJvciB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIG1ldGFkYXRhO1xuICB9XG5cbiAgLyoqXG4gICAqIERvd25sb2FkIG11bHRpcGxlIGVsZW1lbnRzIGluIGJhdGNoXG4gICAqL1xuICBhc3luYyBkb3dubG9hZEJhdGNoKFxuICAgIHJlcG9NYW5hZ2VyOiBQb3J0Zm9saW9SZXBvTWFuYWdlcixcbiAgICBlbGVtZW50UGF0aHM6IHN0cmluZ1tdLFxuICAgIHVzZXJuYW1lOiBzdHJpbmcsXG4gICAgcmVwb3NpdG9yeTogc3RyaW5nLFxuICAgIG9uUHJvZ3Jlc3M/OiAoZG93bmxvYWRlZDogbnVtYmVyLCB0b3RhbDogbnVtYmVyKSA9PiB2b2lkXG4gICk6IFByb21pc2U8TWFwPHN0cmluZywgRWxlbWVudERhdGE+PiB7XG4gICAgY29uc3QgcmVzdWx0cyA9IG5ldyBNYXA8c3RyaW5nLCBFbGVtZW50RGF0YT4oKTtcbiAgICBsZXQgZG93bmxvYWRlZCA9IDA7XG4gICAgXG4gICAgZm9yIChjb25zdCBwYXRoIG9mIGVsZW1lbnRQYXRocykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgZWxlbWVudERhdGEgPSBhd2FpdCB0aGlzLmRvd25sb2FkRnJvbUdpdEh1YihcbiAgICAgICAgICByZXBvTWFuYWdlcixcbiAgICAgICAgICBwYXRoLFxuICAgICAgICAgIHVzZXJuYW1lLFxuICAgICAgICAgIHJlcG9zaXRvcnlcbiAgICAgICAgKTtcbiAgICAgICAgXG4gICAgICAgIHJlc3VsdHMuc2V0KHBhdGgsIGVsZW1lbnREYXRhKTtcbiAgICAgICAgZG93bmxvYWRlZCsrO1xuICAgICAgICBcbiAgICAgICAgaWYgKG9uUHJvZ3Jlc3MpIHtcbiAgICAgICAgICBvblByb2dyZXNzKGRvd25sb2FkZWQsIGVsZW1lbnRQYXRocy5sZW5ndGgpO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dnZXIuZXJyb3IoYEZhaWxlZCB0byBkb3dubG9hZCAke3BhdGh9YCwgeyBlcnJvciB9KTtcbiAgICAgICAgLy8gQ29udGludWUgd2l0aCBvdGhlciBkb3dubG9hZHMgZXZlbiBpZiBvbmUgZmFpbHNcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHJlc3VsdHM7XG4gIH1cbn0iXX0=