@dollhousemcp/mcp-server 1.7.3 → 1.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/ConfigWizard.d.ts +78 -0
- package/dist/config/ConfigWizard.d.ts.map +1 -0
- package/dist/config/ConfigWizard.js +370 -0
- package/dist/config/ConfigWizardCheck.d.ts +47 -0
- package/dist/config/ConfigWizardCheck.d.ts.map +1 -0
- package/dist/config/ConfigWizardCheck.js +208 -0
- package/dist/config/ConfigWizardDisplay.d.ts +64 -0
- package/dist/config/ConfigWizardDisplay.d.ts.map +1 -0
- package/dist/config/ConfigWizardDisplay.js +150 -0
- package/dist/config/WizardFirstResponse.d.ts +25 -0
- package/dist/config/WizardFirstResponse.d.ts.map +1 -0
- package/dist/config/WizardFirstResponse.js +118 -0
- package/dist/config/portfolioConfig.d.ts +40 -0
- package/dist/config/portfolioConfig.d.ts.map +1 -0
- package/dist/config/portfolioConfig.js +58 -0
- package/dist/config/wizardTemplates.d.ts +84 -0
- package/dist/config/wizardTemplates.d.ts.map +1 -0
- package/dist/config/wizardTemplates.js +195 -0
- package/dist/elements/BaseElement.d.ts +15 -0
- package/dist/elements/BaseElement.d.ts.map +1 -1
- package/dist/elements/BaseElement.js +38 -5
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/handlers/PortfolioPullHandler.d.ts +69 -0
- package/dist/handlers/PortfolioPullHandler.d.ts.map +1 -0
- package/dist/handlers/PortfolioPullHandler.js +340 -0
- package/dist/scripts/scripts/run-config-wizard.js +57 -0
- package/dist/scripts/src/config/ConfigManager.js +799 -0
- package/dist/scripts/src/config/ConfigWizard.js +368 -0
- package/dist/scripts/src/errors/SecurityError.js +47 -0
- package/dist/scripts/src/security/constants.js +28 -0
- package/dist/scripts/src/security/contentValidator.js +415 -0
- package/dist/scripts/src/security/errors.js +32 -0
- package/dist/scripts/src/security/regexValidator.js +217 -0
- package/dist/scripts/src/security/secureYamlParser.js +272 -0
- package/dist/scripts/src/security/securityMonitor.js +111 -0
- package/dist/scripts/src/security/validators/unicodeValidator.js +315 -0
- package/dist/scripts/src/utils/logger.js +288 -0
- package/dist/sync/PortfolioDownloader.d.ts +27 -0
- package/dist/sync/PortfolioDownloader.d.ts.map +1 -0
- package/dist/sync/PortfolioDownloader.js +120 -0
- package/dist/sync/PortfolioSyncComparer.d.ts +50 -0
- package/dist/sync/PortfolioSyncComparer.d.ts.map +1 -0
- package/dist/sync/PortfolioSyncComparer.js +158 -0
- package/dist/tools/getWelcomeMessage.d.ts +41 -0
- package/dist/tools/getWelcomeMessage.d.ts.map +1 -0
- package/dist/tools/getWelcomeMessage.js +109 -0
- package/dist/utils/TemplateRenderer.d.ts +63 -0
- package/dist/utils/TemplateRenderer.d.ts.map +1 -0
- package/dist/utils/TemplateRenderer.js +154 -0
- package/package.json +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PortfolioDownloader - Downloads elements from GitHub repositories
|
|
3
|
+
*
|
|
4
|
+
* Handles fetching file contents from GitHub, decoding base64 content,
|
|
5
|
+
* and returning structured element data ready for local storage.
|
|
6
|
+
*/
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
|
|
9
|
+
export class PortfolioDownloader {
|
|
10
|
+
/**
|
|
11
|
+
* Download an element from GitHub
|
|
12
|
+
*/
|
|
13
|
+
async downloadFromGitHub(repoManager, elementPath, username, repository) {
|
|
14
|
+
try {
|
|
15
|
+
logger.info('Downloading element from GitHub', {
|
|
16
|
+
path: elementPath,
|
|
17
|
+
username,
|
|
18
|
+
repository
|
|
19
|
+
});
|
|
20
|
+
// Fetch the file content from GitHub
|
|
21
|
+
const response = await repoManager.githubRequest(`/repos/${username}/${repository}/contents/${elementPath}`);
|
|
22
|
+
if (!response || !response.content) {
|
|
23
|
+
throw new Error(`No content found at path: ${elementPath}`);
|
|
24
|
+
}
|
|
25
|
+
// Decode base64 content
|
|
26
|
+
const decodedContent = Buffer.from(response.content, 'base64').toString('utf-8');
|
|
27
|
+
// Normalize Unicode for security
|
|
28
|
+
const normalized = UnicodeValidator.normalize(decodedContent);
|
|
29
|
+
// Log download for audit trail
|
|
30
|
+
logger.info('Element downloaded successfully', {
|
|
31
|
+
path: elementPath,
|
|
32
|
+
repository: `${username}/${repository}`,
|
|
33
|
+
sha: response.sha
|
|
34
|
+
});
|
|
35
|
+
// Parse metadata from frontmatter if present
|
|
36
|
+
const metadata = this.extractMetadata(normalized.normalizedContent);
|
|
37
|
+
return {
|
|
38
|
+
content: normalized.normalizedContent,
|
|
39
|
+
metadata,
|
|
40
|
+
sha: response.sha
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
logger.error('Failed to download element from GitHub', {
|
|
45
|
+
error,
|
|
46
|
+
path: elementPath
|
|
47
|
+
});
|
|
48
|
+
// Re-throw with more context
|
|
49
|
+
if (error instanceof Error) {
|
|
50
|
+
if (error.message.includes('404')) {
|
|
51
|
+
throw new Error(`Element not found at path: ${elementPath}`);
|
|
52
|
+
}
|
|
53
|
+
if (error.message.includes('401') || error.message.includes('403')) {
|
|
54
|
+
throw new Error(`Authentication failed. Please check your GitHub token.`);
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
throw new Error(`Failed to download ${elementPath}: ${String(error)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Extract metadata from frontmatter
|
|
63
|
+
*/
|
|
64
|
+
extractMetadata(content) {
|
|
65
|
+
const metadata = {};
|
|
66
|
+
// Check for YAML frontmatter
|
|
67
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
68
|
+
if (frontmatterMatch) {
|
|
69
|
+
try {
|
|
70
|
+
// Parse the frontmatter as simple key-value pairs
|
|
71
|
+
// (avoiding using yaml.load for security)
|
|
72
|
+
const frontmatterContent = frontmatterMatch[1];
|
|
73
|
+
const lines = frontmatterContent.split('\n');
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
const colonIndex = line.indexOf(':');
|
|
76
|
+
if (colonIndex > 0) {
|
|
77
|
+
const key = line.substring(0, colonIndex).trim();
|
|
78
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
79
|
+
// Remove quotes if present
|
|
80
|
+
const cleanValue = value.replace(/^["']|["']$/g, '');
|
|
81
|
+
// Try to parse as JSON for arrays/objects, otherwise use as string
|
|
82
|
+
try {
|
|
83
|
+
metadata[key] = JSON.parse(cleanValue);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
metadata[key] = cleanValue;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
logger.warn('Failed to parse frontmatter metadata', { error });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return metadata;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Download multiple elements in batch
|
|
99
|
+
*/
|
|
100
|
+
async downloadBatch(repoManager, elementPaths, username, repository, onProgress) {
|
|
101
|
+
const results = new Map();
|
|
102
|
+
let downloaded = 0;
|
|
103
|
+
for (const path of elementPaths) {
|
|
104
|
+
try {
|
|
105
|
+
const elementData = await this.downloadFromGitHub(repoManager, path, username, repository);
|
|
106
|
+
results.set(path, elementData);
|
|
107
|
+
downloaded++;
|
|
108
|
+
if (onProgress) {
|
|
109
|
+
onProgress(downloaded, elementPaths.length);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
logger.error(`Failed to download ${path}`, { error });
|
|
114
|
+
// Continue with other downloads even if one fails
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return results;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUG9ydGZvbGlvRG93bmxvYWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zeW5jL1BvcnRmb2xpb0Rvd25sb2FkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFHSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sNENBQTRDLENBQUM7QUFROUUsTUFBTSxPQUFPLG1CQUFtQjtJQUM5Qjs7T0FFRztJQUNILEtBQUssQ0FBQyxrQkFBa0IsQ0FDdEIsV0FBaUMsRUFDakMsV0FBbUIsRUFDbkIsUUFBZ0IsRUFDaEIsVUFBa0I7UUFFbEIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxDQUFDLElBQUksQ0FBQyxpQ0FBaUMsRUFBRTtnQkFDN0MsSUFBSSxFQUFFLFdBQVc7Z0JBQ2pCLFFBQVE7Z0JBQ1IsVUFBVTthQUNYLENBQUMsQ0FBQztZQUVILHFDQUFxQztZQUNyQyxNQUFNLFFBQVEsR0FBRyxNQUFNLFdBQVcsQ0FBQyxhQUFhLENBQzlDLFVBQVUsUUFBUSxJQUFJLFVBQVUsYUFBYSxXQUFXLEVBQUUsQ0FDM0QsQ0FBQztZQUVGLElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ25DLE1BQU0sSUFBSSxLQUFLLENBQUMsNkJBQTZCLFdBQVcsRUFBRSxDQUFDLENBQUM7WUFDOUQsQ0FBQztZQUVELHdCQUF3QjtZQUN4QixNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBRWpGLGlDQUFpQztZQUNqQyxNQUFNLFVBQVUsR0FBRyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsY0FBYyxDQUFDLENBQUM7WUFFOUQsK0JBQStCO1lBQy9CLE1BQU0sQ0FBQyxJQUFJLENBQUMsaUNBQWlDLEVBQUU7Z0JBQzdDLElBQUksRUFBRSxXQUFXO2dCQUNqQixVQUFVLEVBQUUsR0FBRyxRQUFRLElBQUksVUFBVSxFQUFFO2dCQUN2QyxHQUFHLEVBQUUsUUFBUSxDQUFDLEdBQUc7YUFDbEIsQ0FBQyxDQUFDO1lBRUgsNkNBQTZDO1lBQzdDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLENBQUM7WUFFcEUsT0FBTztnQkFDTCxPQUFPLEVBQUUsVUFBVSxDQUFDLGlCQUFpQjtnQkFDckMsUUFBUTtnQkFDUixHQUFHLEVBQUUsUUFBUSxDQUFDLEdBQUc7YUFDbEIsQ0FBQztRQUVKLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLEtBQUssQ0FBQyx3Q0FBd0MsRUFBRTtnQkFDckQsS0FBSztnQkFDTCxJQUFJLEVBQUUsV0FBVzthQUNsQixDQUFDLENBQUM7WUFFSCw2QkFBNkI7WUFDN0IsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7Z0JBQzNCLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztvQkFDbEMsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsV0FBVyxFQUFFLENBQUMsQ0FBQztnQkFDL0QsQ0FBQztnQkFDRCxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7b0JBQ25FLE1BQU0sSUFBSSxLQUFLLENBQUMsd0RBQXdELENBQUMsQ0FBQztnQkFDNUUsQ0FBQztnQkFDRCxNQUFNLEtBQUssQ0FBQztZQUNkLENBQUM7WUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLHNCQUFzQixXQUFXLEtBQUssTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN6RSxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssZUFBZSxDQUFDLE9BQWU7UUFDckMsTUFBTSxRQUFRLEdBQXdCLEVBQUUsQ0FBQztRQUV6Qyw2QkFBNkI7UUFDN0IsTUFBTSxnQkFBZ0IsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLHVCQUF1QixDQUFDLENBQUM7UUFFaEUsSUFBSSxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3JCLElBQUksQ0FBQztnQkFDSCxrREFBa0Q7Z0JBQ2xELDBDQUEwQztnQkFDMUMsTUFBTSxrQkFBa0IsR0FBRyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0MsTUFBTSxLQUFLLEdBQUcsa0JBQWtCLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUU3QyxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO29CQUN6QixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUNyQyxJQUFJLFVBQVUsR0FBRyxDQUFDLEVBQUUsQ0FBQzt3QkFDbkIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsVUFBVSxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7d0JBQ2pELE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO3dCQUVwRCwyQkFBMkI7d0JBQzNCLE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsY0FBYyxFQUFFLEVBQUUsQ0FBQyxDQUFDO3dCQUVyRCxtRUFBbUU7d0JBQ25FLElBQUksQ0FBQzs0QkFDSCxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQzt3QkFDekMsQ0FBQzt3QkFBQyxNQUFNLENBQUM7NEJBQ1AsUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsQ0FBQzt3QkFDN0IsQ0FBQztvQkFDSCxDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixNQUFNLENBQUMsSUFBSSxDQUFDLHNDQUFzQyxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUNqRSxDQUFDO1FBQ0gsQ0FBQztRQUVELE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxhQUFhLENBQ2pCLFdBQWlDLEVBQ2pDLFlBQXNCLEVBQ3RCLFFBQWdCLEVBQ2hCLFVBQWtCLEVBQ2xCLFVBQXdEO1FBRXhELE1BQU0sT0FBTyxHQUFHLElBQUksR0FBRyxFQUF1QixDQUFDO1FBQy9DLElBQUksVUFBVSxHQUFHLENBQUMsQ0FBQztRQUVuQixLQUFLLE1BQU0sSUFBSSxJQUFJLFlBQVksRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQztnQkFDSCxNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxrQkFBa0IsQ0FDL0MsV0FBVyxFQUNYLElBQUksRUFDSixRQUFRLEVBQ1IsVUFBVSxDQUNYLENBQUM7Z0JBRUYsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUM7Z0JBQy9CLFVBQVUsRUFBRSxDQUFDO2dCQUViLElBQUksVUFBVSxFQUFFLENBQUM7b0JBQ2YsVUFBVSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQzlDLENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixNQUFNLENBQUMsS0FBSyxDQUFDLHNCQUFzQixJQUFJLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQ3RELGtEQUFrRDtZQUNwRCxDQUFDO1FBQ0gsQ0FBQztRQUVELE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUG9ydGZvbGlvRG93bmxvYWRlciAtIERvd25sb2FkcyBlbGVtZW50cyBmcm9tIEdpdEh1YiByZXBvc2l0b3JpZXNcbiAqIFxuICogSGFuZGxlcyBmZXRjaGluZyBmaWxlIGNvbnRlbnRzIGZyb20gR2l0SHViLCBkZWNvZGluZyBiYXNlNjQgY29udGVudCxcbiAqIGFuZCByZXR1cm5pbmcgc3RydWN0dXJlZCBlbGVtZW50IGRhdGEgcmVhZHkgZm9yIGxvY2FsIHN0b3JhZ2UuXG4gKi9cblxuaW1wb3J0IHsgUG9ydGZvbGlvUmVwb01hbmFnZXIgfSBmcm9tICcuLi9wb3J0Zm9saW8vUG9ydGZvbGlvUmVwb01hbmFnZXIuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEVsZW1lbnREYXRhIHtcbiAgY29udGVudDogc3RyaW5nO1xuICBtZXRhZGF0YTogUmVjb3JkPHN0cmluZywgYW55PjtcbiAgc2hhOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBjbGFzcyBQb3J0Zm9saW9Eb3dubG9hZGVyIHtcbiAgLyoqXG4gICAqIERvd25sb2FkIGFuIGVsZW1lbnQgZnJvbSBHaXRIdWJcbiAgICovXG4gIGFzeW5jIGRvd25sb2FkRnJvbUdpdEh1YihcbiAgICByZXBvTWFuYWdlcjogUG9ydGZvbGlvUmVwb01hbmFnZXIsXG4gICAgZWxlbWVudFBhdGg6IHN0cmluZyxcbiAgICB1c2VybmFtZTogc3RyaW5nLFxuICAgIHJlcG9zaXRvcnk6IHN0cmluZ1xuICApOiBQcm9taXNlPEVsZW1lbnREYXRhPiB7XG4gICAgdHJ5IHtcbiAgICAgIGxvZ2dlci5pbmZvKCdEb3dubG9hZGluZyBlbGVtZW50IGZyb20gR2l0SHViJywgeyBcbiAgICAgICAgcGF0aDogZWxlbWVudFBhdGgsIFxuICAgICAgICB1c2VybmFtZSwgXG4gICAgICAgIHJlcG9zaXRvcnkgXG4gICAgICB9KTtcblxuICAgICAgLy8gRmV0Y2ggdGhlIGZpbGUgY29udGVudCBmcm9tIEdpdEh1YlxuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCByZXBvTWFuYWdlci5naXRodWJSZXF1ZXN0KFxuICAgICAgICBgL3JlcG9zLyR7dXNlcm5hbWV9LyR7cmVwb3NpdG9yeX0vY29udGVudHMvJHtlbGVtZW50UGF0aH1gXG4gICAgICApO1xuXG4gICAgICBpZiAoIXJlc3BvbnNlIHx8ICFyZXNwb25zZS5jb250ZW50KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gY29udGVudCBmb3VuZCBhdCBwYXRoOiAke2VsZW1lbnRQYXRofWApO1xuICAgICAgfVxuXG4gICAgICAvLyBEZWNvZGUgYmFzZTY0IGNvbnRlbnRcbiAgICAgIGNvbnN0IGRlY29kZWRDb250ZW50ID0gQnVmZmVyLmZyb20ocmVzcG9uc2UuY29udGVudCwgJ2Jhc2U2NCcpLnRvU3RyaW5nKCd1dGYtOCcpO1xuICAgICAgXG4gICAgICAvLyBOb3JtYWxpemUgVW5pY29kZSBmb3Igc2VjdXJpdHlcbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShkZWNvZGVkQ29udGVudCk7XG4gICAgICBcbiAgICAgIC8vIExvZyBkb3dubG9hZCBmb3IgYXVkaXQgdHJhaWxcbiAgICAgIGxvZ2dlci5pbmZvKCdFbGVtZW50IGRvd25sb2FkZWQgc3VjY2Vzc2Z1bGx5Jywge1xuICAgICAgICBwYXRoOiBlbGVtZW50UGF0aCxcbiAgICAgICAgcmVwb3NpdG9yeTogYCR7dXNlcm5hbWV9LyR7cmVwb3NpdG9yeX1gLFxuICAgICAgICBzaGE6IHJlc3BvbnNlLnNoYVxuICAgICAgfSk7XG5cbiAgICAgIC8vIFBhcnNlIG1ldGFkYXRhIGZyb20gZnJvbnRtYXR0ZXIgaWYgcHJlc2VudFxuICAgICAgY29uc3QgbWV0YWRhdGEgPSB0aGlzLmV4dHJhY3RNZXRhZGF0YShub3JtYWxpemVkLm5vcm1hbGl6ZWRDb250ZW50KTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgY29udGVudDogbm9ybWFsaXplZC5ub3JtYWxpemVkQ29udGVudCxcbiAgICAgICAgbWV0YWRhdGEsXG4gICAgICAgIHNoYTogcmVzcG9uc2Uuc2hhXG4gICAgICB9O1xuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGRvd25sb2FkIGVsZW1lbnQgZnJvbSBHaXRIdWInLCB7IFxuICAgICAgICBlcnJvciwgXG4gICAgICAgIHBhdGg6IGVsZW1lbnRQYXRoIFxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIC8vIFJlLXRocm93IHdpdGggbW9yZSBjb250ZXh0XG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgICBpZiAoZXJyb3IubWVzc2FnZS5pbmNsdWRlcygnNDA0JykpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEVsZW1lbnQgbm90IGZvdW5kIGF0IHBhdGg6ICR7ZWxlbWVudFBhdGh9YCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVycm9yLm1lc3NhZ2UuaW5jbHVkZXMoJzQwMScpIHx8IGVycm9yLm1lc3NhZ2UuaW5jbHVkZXMoJzQwMycpKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBBdXRoZW50aWNhdGlvbiBmYWlsZWQuIFBsZWFzZSBjaGVjayB5b3VyIEdpdEh1YiB0b2tlbi5gKTtcbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZG93bmxvYWQgJHtlbGVtZW50UGF0aH06ICR7U3RyaW5nKGVycm9yKX1gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRXh0cmFjdCBtZXRhZGF0YSBmcm9tIGZyb250bWF0dGVyXG4gICAqL1xuICBwcml2YXRlIGV4dHJhY3RNZXRhZGF0YShjb250ZW50OiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHtcbiAgICBjb25zdCBtZXRhZGF0YTogUmVjb3JkPHN0cmluZywgYW55PiA9IHt9O1xuICAgIFxuICAgIC8vIENoZWNrIGZvciBZQU1MIGZyb250bWF0dGVyXG4gICAgY29uc3QgZnJvbnRtYXR0ZXJNYXRjaCA9IGNvbnRlbnQubWF0Y2goL14tLS1cXG4oW1xcc1xcU10qPylcXG4tLS0vKTtcbiAgICBcbiAgICBpZiAoZnJvbnRtYXR0ZXJNYXRjaCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gUGFyc2UgdGhlIGZyb250bWF0dGVyIGFzIHNpbXBsZSBrZXktdmFsdWUgcGFpcnNcbiAgICAgICAgLy8gKGF2b2lkaW5nIHVzaW5nIHlhbWwubG9hZCBmb3Igc2VjdXJpdHkpXG4gICAgICAgIGNvbnN0IGZyb250bWF0dGVyQ29udGVudCA9IGZyb250bWF0dGVyTWF0Y2hbMV07XG4gICAgICAgIGNvbnN0IGxpbmVzID0gZnJvbnRtYXR0ZXJDb250ZW50LnNwbGl0KCdcXG4nKTtcbiAgICAgICAgXG4gICAgICAgIGZvciAoY29uc3QgbGluZSBvZiBsaW5lcykge1xuICAgICAgICAgIGNvbnN0IGNvbG9uSW5kZXggPSBsaW5lLmluZGV4T2YoJzonKTtcbiAgICAgICAgICBpZiAoY29sb25JbmRleCA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGtleSA9IGxpbmUuc3Vic3RyaW5nKDAsIGNvbG9uSW5kZXgpLnRyaW0oKTtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlID0gbGluZS5zdWJzdHJpbmcoY29sb25JbmRleCArIDEpLnRyaW0oKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gUmVtb3ZlIHF1b3RlcyBpZiBwcmVzZW50XG4gICAgICAgICAgICBjb25zdCBjbGVhblZhbHVlID0gdmFsdWUucmVwbGFjZSgvXltcIiddfFtcIiddJC9nLCAnJyk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIFRyeSB0byBwYXJzZSBhcyBKU09OIGZvciBhcnJheXMvb2JqZWN0cywgb3RoZXJ3aXNlIHVzZSBhcyBzdHJpbmdcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIG1ldGFkYXRhW2tleV0gPSBKU09OLnBhcnNlKGNsZWFuVmFsdWUpO1xuICAgICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAgIG1ldGFkYXRhW2tleV0gPSBjbGVhblZhbHVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ0ZhaWxlZCB0byBwYXJzZSBmcm9udG1hdHRlciBtZXRhZGF0YScsIHsgZXJyb3IgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBtZXRhZGF0YTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEb3dubG9hZCBtdWx0aXBsZSBlbGVtZW50cyBpbiBiYXRjaFxuICAgKi9cbiAgYXN5bmMgZG93bmxvYWRCYXRjaChcbiAgICByZXBvTWFuYWdlcjogUG9ydGZvbGlvUmVwb01hbmFnZXIsXG4gICAgZWxlbWVudFBhdGhzOiBzdHJpbmdbXSxcbiAgICB1c2VybmFtZTogc3RyaW5nLFxuICAgIHJlcG9zaXRvcnk6IHN0cmluZyxcbiAgICBvblByb2dyZXNzPzogKGRvd25sb2FkZWQ6IG51bWJlciwgdG90YWw6IG51bWJlcikgPT4gdm9pZFxuICApOiBQcm9taXNlPE1hcDxzdHJpbmcsIEVsZW1lbnREYXRhPj4ge1xuICAgIGNvbnN0IHJlc3VsdHMgPSBuZXcgTWFwPHN0cmluZywgRWxlbWVudERhdGE+KCk7XG4gICAgbGV0IGRvd25sb2FkZWQgPSAwO1xuICAgIFxuICAgIGZvciAoY29uc3QgcGF0aCBvZiBlbGVtZW50UGF0aHMpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGVsZW1lbnREYXRhID0gYXdhaXQgdGhpcy5kb3dubG9hZEZyb21HaXRIdWIoXG4gICAgICAgICAgcmVwb01hbmFnZXIsXG4gICAgICAgICAgcGF0aCxcbiAgICAgICAgICB1c2VybmFtZSxcbiAgICAgICAgICByZXBvc2l0b3J5XG4gICAgICAgICk7XG4gICAgICAgIFxuICAgICAgICByZXN1bHRzLnNldChwYXRoLCBlbGVtZW50RGF0YSk7XG4gICAgICAgIGRvd25sb2FkZWQrKztcbiAgICAgICAgXG4gICAgICAgIGlmIChvblByb2dyZXNzKSB7XG4gICAgICAgICAgb25Qcm9ncmVzcyhkb3dubG9hZGVkLCBlbGVtZW50UGF0aHMubGVuZ3RoKTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKGBGYWlsZWQgdG8gZG93bmxvYWQgJHtwYXRofWAsIHsgZXJyb3IgfSk7XG4gICAgICAgIC8vIENvbnRpbnVlIHdpdGggb3RoZXIgZG93bmxvYWRzIGV2ZW4gaWYgb25lIGZhaWxzXG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiByZXN1bHRzO1xuICB9XG59Il19
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PortfolioSyncComparer - Compares local and GitHub portfolio elements
|
|
3
|
+
*
|
|
4
|
+
* Determines what actions need to be taken based on the sync mode:
|
|
5
|
+
* - additive: Only add missing elements
|
|
6
|
+
* - mirror: Make local exactly match GitHub
|
|
7
|
+
* - backup: Treat GitHub as authoritative source
|
|
8
|
+
*/
|
|
9
|
+
import { ElementType } from '../portfolio/types.js';
|
|
10
|
+
import { GitHubIndexEntry } from '../portfolio/GitHubPortfolioIndexer.js';
|
|
11
|
+
export type SyncMode = 'additive' | 'mirror' | 'backup';
|
|
12
|
+
export interface SyncAction {
|
|
13
|
+
type: ElementType;
|
|
14
|
+
name: string;
|
|
15
|
+
path: string;
|
|
16
|
+
action: 'add' | 'update' | 'delete' | 'skip';
|
|
17
|
+
reason?: string;
|
|
18
|
+
localSha?: string;
|
|
19
|
+
remoteSha?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface SyncActions {
|
|
22
|
+
toAdd: SyncAction[];
|
|
23
|
+
toUpdate: SyncAction[];
|
|
24
|
+
toDelete: SyncAction[];
|
|
25
|
+
toSkip: SyncAction[];
|
|
26
|
+
}
|
|
27
|
+
export declare class PortfolioSyncComparer {
|
|
28
|
+
/**
|
|
29
|
+
* Compare GitHub and local elements to determine sync actions
|
|
30
|
+
*/
|
|
31
|
+
compareElements(githubElements: Map<ElementType, GitHubIndexEntry[]>, localElements: Map<ElementType, any[]>, mode: SyncMode): SyncActions;
|
|
32
|
+
/**
|
|
33
|
+
* Compare elements of a specific type
|
|
34
|
+
*/
|
|
35
|
+
private compareTypeElements;
|
|
36
|
+
/**
|
|
37
|
+
* Determine if an element should be updated
|
|
38
|
+
*/
|
|
39
|
+
private shouldUpdate;
|
|
40
|
+
/**
|
|
41
|
+
* Normalize element name for comparison
|
|
42
|
+
* Handles different naming formats and extensions
|
|
43
|
+
*/
|
|
44
|
+
private normalizeElementName;
|
|
45
|
+
/**
|
|
46
|
+
* Count total elements in a map
|
|
47
|
+
*/
|
|
48
|
+
private countElements;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=PortfolioSyncComparer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortfolioSyncComparer.d.ts","sourceRoot":"","sources":["../../src/sync/PortfolioSyncComparer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAG1E,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAExD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,qBAAa,qBAAqB;IAChC;;OAEG;IACH,eAAe,CACb,cAAc,EAAE,GAAG,CAAC,WAAW,EAAE,gBAAgB,EAAE,CAAC,EACpD,aAAa,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,EACtC,IAAI,EAAE,QAAQ,GACb,WAAW;IA2Cd;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAuE3B;;OAEG;IACH,OAAO,CAAC,YAAY;IAgCpB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAe5B;;OAEG;IACH,OAAO,CAAC,aAAa;CAOtB"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PortfolioSyncComparer - Compares local and GitHub portfolio elements
|
|
3
|
+
*
|
|
4
|
+
* Determines what actions need to be taken based on the sync mode:
|
|
5
|
+
* - additive: Only add missing elements
|
|
6
|
+
* - mirror: Make local exactly match GitHub
|
|
7
|
+
* - backup: Treat GitHub as authoritative source
|
|
8
|
+
*/
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
export class PortfolioSyncComparer {
|
|
11
|
+
/**
|
|
12
|
+
* Compare GitHub and local elements to determine sync actions
|
|
13
|
+
*/
|
|
14
|
+
compareElements(githubElements, localElements, mode) {
|
|
15
|
+
const actions = {
|
|
16
|
+
toAdd: [],
|
|
17
|
+
toUpdate: [],
|
|
18
|
+
toDelete: [],
|
|
19
|
+
toSkip: []
|
|
20
|
+
};
|
|
21
|
+
logger.info('Comparing elements', {
|
|
22
|
+
githubCount: this.countElements(githubElements),
|
|
23
|
+
localCount: this.countElements(localElements),
|
|
24
|
+
mode
|
|
25
|
+
});
|
|
26
|
+
// Process each element type
|
|
27
|
+
const allTypes = new Set([
|
|
28
|
+
...githubElements.keys(),
|
|
29
|
+
...localElements.keys()
|
|
30
|
+
]);
|
|
31
|
+
for (const type of allTypes) {
|
|
32
|
+
const githubTypeElements = githubElements.get(type) || [];
|
|
33
|
+
const localTypeElements = localElements.get(type) || [];
|
|
34
|
+
this.compareTypeElements(type, githubTypeElements, localTypeElements, mode, actions);
|
|
35
|
+
}
|
|
36
|
+
logger.info('Comparison complete', {
|
|
37
|
+
toAdd: actions.toAdd.length,
|
|
38
|
+
toUpdate: actions.toUpdate.length,
|
|
39
|
+
toDelete: actions.toDelete.length,
|
|
40
|
+
toSkip: actions.toSkip.length
|
|
41
|
+
});
|
|
42
|
+
return actions;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Compare elements of a specific type
|
|
46
|
+
*/
|
|
47
|
+
compareTypeElements(type, githubElements, localElements, mode, actions) {
|
|
48
|
+
// Create maps for efficient lookup
|
|
49
|
+
const githubMap = new Map(githubElements.map(e => [this.normalizeElementName(e.name), e]));
|
|
50
|
+
const localMap = new Map(localElements.map(e => [this.normalizeElementName(e.name || e.metadata?.name), e]));
|
|
51
|
+
// Process GitHub elements (additions and updates)
|
|
52
|
+
for (const [name, githubElement] of githubMap) {
|
|
53
|
+
const localElement = localMap.get(name);
|
|
54
|
+
if (!localElement) {
|
|
55
|
+
// Element exists on GitHub but not locally
|
|
56
|
+
actions.toAdd.push({
|
|
57
|
+
type,
|
|
58
|
+
name: githubElement.name,
|
|
59
|
+
path: githubElement.path,
|
|
60
|
+
action: 'add',
|
|
61
|
+
remoteSha: githubElement.sha
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else if (mode === 'backup' || this.shouldUpdate(githubElement, localElement, mode)) {
|
|
65
|
+
// Element needs updating
|
|
66
|
+
actions.toUpdate.push({
|
|
67
|
+
type,
|
|
68
|
+
name: githubElement.name,
|
|
69
|
+
path: githubElement.path,
|
|
70
|
+
action: 'update',
|
|
71
|
+
reason: mode === 'backup' ? 'backup mode' : 'newer on GitHub',
|
|
72
|
+
localSha: localElement.sha,
|
|
73
|
+
remoteSha: githubElement.sha
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Element is up to date or should be skipped
|
|
78
|
+
actions.toSkip.push({
|
|
79
|
+
type,
|
|
80
|
+
name: githubElement.name,
|
|
81
|
+
path: githubElement.path,
|
|
82
|
+
action: 'skip',
|
|
83
|
+
reason: 'up to date',
|
|
84
|
+
localSha: localElement.sha,
|
|
85
|
+
remoteSha: githubElement.sha
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Process local elements (deletions - only in mirror mode)
|
|
90
|
+
if (mode === 'mirror') {
|
|
91
|
+
for (const [name, localElement] of localMap) {
|
|
92
|
+
if (!githubMap.has(name)) {
|
|
93
|
+
// Element exists locally but not on GitHub
|
|
94
|
+
actions.toDelete.push({
|
|
95
|
+
type,
|
|
96
|
+
name: localElement.name || localElement.metadata?.name || name,
|
|
97
|
+
path: `${type}/${name}.md`,
|
|
98
|
+
action: 'delete',
|
|
99
|
+
reason: 'not on GitHub',
|
|
100
|
+
localSha: localElement.sha
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Determine if an element should be updated
|
|
108
|
+
*/
|
|
109
|
+
shouldUpdate(githubElement, localElement, mode) {
|
|
110
|
+
// In backup mode, always update from GitHub
|
|
111
|
+
if (mode === 'backup') {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
// In additive mode, never update existing elements
|
|
115
|
+
if (mode === 'additive') {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
// In mirror mode, update if SHAs differ
|
|
119
|
+
// If we don't have SHAs, compare modified dates
|
|
120
|
+
if (githubElement.sha && localElement.sha) {
|
|
121
|
+
return githubElement.sha !== localElement.sha;
|
|
122
|
+
}
|
|
123
|
+
// Compare modified dates if available
|
|
124
|
+
if (githubElement.lastModified && localElement.lastModified) {
|
|
125
|
+
const githubDate = new Date(githubElement.lastModified).getTime();
|
|
126
|
+
const localDate = new Date(localElement.lastModified).getTime();
|
|
127
|
+
return githubDate > localDate;
|
|
128
|
+
}
|
|
129
|
+
// If we can't determine, skip updating in mirror mode
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Normalize element name for comparison
|
|
134
|
+
* Handles different naming formats and extensions
|
|
135
|
+
*/
|
|
136
|
+
normalizeElementName(name) {
|
|
137
|
+
if (!name)
|
|
138
|
+
return '';
|
|
139
|
+
// Remove .md extension if present
|
|
140
|
+
let normalized = name.replace(/\.md$/i, '');
|
|
141
|
+
// Convert to lowercase for comparison
|
|
142
|
+
normalized = normalized.toLowerCase();
|
|
143
|
+
// Replace spaces with hyphens (some systems use different formats)
|
|
144
|
+
normalized = normalized.replace(/\s+/g, '-');
|
|
145
|
+
return normalized;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Count total elements in a map
|
|
149
|
+
*/
|
|
150
|
+
countElements(elements) {
|
|
151
|
+
let count = 0;
|
|
152
|
+
for (const typeElements of elements.values()) {
|
|
153
|
+
count += typeElements.length;
|
|
154
|
+
}
|
|
155
|
+
return count;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUG9ydGZvbGlvU3luY0NvbXBhcmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N5bmMvUG9ydGZvbGlvU3luY0NvbXBhcmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0dBT0c7QUFJSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFxQjVDLE1BQU0sT0FBTyxxQkFBcUI7SUFDaEM7O09BRUc7SUFDSCxlQUFlLENBQ2IsY0FBb0QsRUFDcEQsYUFBc0MsRUFDdEMsSUFBYztRQUVkLE1BQU0sT0FBTyxHQUFnQjtZQUMzQixLQUFLLEVBQUUsRUFBRTtZQUNULFFBQVEsRUFBRSxFQUFFO1lBQ1osUUFBUSxFQUFFLEVBQUU7WUFDWixNQUFNLEVBQUUsRUFBRTtTQUNYLENBQUM7UUFFRixNQUFNLENBQUMsSUFBSSxDQUFDLG9CQUFvQixFQUFFO1lBQ2hDLFdBQVcsRUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLGNBQWMsQ0FBQztZQUMvQyxVQUFVLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUM7WUFDN0MsSUFBSTtTQUNMLENBQUMsQ0FBQztRQUVILDRCQUE0QjtRQUM1QixNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsQ0FBQztZQUN2QixHQUFHLGNBQWMsQ0FBQyxJQUFJLEVBQUU7WUFDeEIsR0FBRyxhQUFhLENBQUMsSUFBSSxFQUFFO1NBQ3hCLENBQUMsQ0FBQztRQUVILEtBQUssTUFBTSxJQUFJLElBQUksUUFBUSxFQUFFLENBQUM7WUFDNUIsTUFBTSxrQkFBa0IsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUMxRCxNQUFNLGlCQUFpQixHQUFHLGFBQWEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBRXhELElBQUksQ0FBQyxtQkFBbUIsQ0FDdEIsSUFBSSxFQUNKLGtCQUFrQixFQUNsQixp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SUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLFlBQVksQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2xFLE1BQU0sU0FBUyxHQUFHLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNoRSxPQUFPLFVBQVUsR0FBRyxTQUFTLENBQUM7UUFDaEMsQ0FBQztRQUVELHNEQUFzRDtRQUN0RCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7O09BR0c7SUFDSyxvQkFBb0IsQ0FBQyxJQUFZO1FBQ3ZDLElBQUksQ0FBQyxJQUFJO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFFckIsa0NBQWtDO1FBQ2xDLElBQUksVUFBVSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRTVDLHNDQUFzQztRQUN0QyxVQUFVLEdBQUcsVUFBVSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBRXRDLG1FQUFtRTtRQUNuRSxVQUFVLEdBQUcsVUFBVSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFFN0MsT0FBTyxVQUFVLENBQUM7SUFDcEIsQ0FBQztJQUVEOztPQUVHO0lBQ0ssYUFBYSxDQUFDLFFBQWlDO1FBQ3JELElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQztRQUNkLEtBQUssTUFBTSxZQUFZLElBQUksUUFBUSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7WUFDN0MsS0FBSyxJQUFJLFlBQVksQ0FBQyxNQUFNLENBQUM7UUFDL0IsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQb3J0Zm9saW9TeW5jQ29tcGFyZXIgLSBDb21wYXJlcyBsb2NhbCBhbmQgR2l0SHViIHBvcnRmb2xpbyBlbGVtZW50c1xuICogXG4gKiBEZXRlcm1pbmVzIHdoYXQgYWN0aW9ucyBuZWVkIHRvIGJlIHRha2VuIGJhc2VkIG9uIHRoZSBzeW5jIG1vZGU6XG4gKiAtIGFkZGl0aXZlOiBPbmx5IGFkZCBtaXNzaW5nIGVsZW1lbnRzXG4gKiAtIG1pcnJvcjogTWFrZSBsb2NhbCBleGFjdGx5IG1hdGNoIEdpdEh1YlxuICogLSBiYWNrdXA6IFRyZWF0IEdpdEh1YiBhcyBhdXRob3JpdGF0aXZlIHNvdXJjZVxuICovXG5cbmltcG9ydCB7IEVsZW1lbnRUeXBlIH0gZnJvbSAnLi4vcG9ydGZvbGlvL3R5cGVzLmpzJztcbmltcG9ydCB7IEdpdEh1YkluZGV4RW50cnkgfSBmcm9tICcuLi9wb3J0Zm9saW8vR2l0SHViUG9ydGZvbGlvSW5kZXhlci5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG5leHBvcnQgdHlwZSBTeW5jTW9kZSA9ICdhZGRpdGl2ZScgfCAnbWlycm9yJyB8ICdiYWNrdXAnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFN5bmNBY3Rpb24ge1xuICB0eXBlOiBFbGVtZW50VHlwZTtcbiAgbmFtZTogc3RyaW5nO1xuICBwYXRoOiBzdHJpbmc7XG4gIGFjdGlvbjogJ2FkZCcgfCAndXBkYXRlJyB8ICdkZWxldGUnIHwgJ3NraXAnO1xuICByZWFzb24/OiBzdHJpbmc7XG4gIGxvY2FsU2hhPzogc3RyaW5nO1xuICByZW1vdGVTaGE/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3luY0FjdGlvbnMge1xuICB0b0FkZDogU3luY0FjdGlvbltdO1xuICB0b1VwZGF0ZTogU3luY0FjdGlvbltdO1xuICB0b0RlbGV0ZTogU3luY0FjdGlvbltdO1xuICB0b1NraXA6IFN5bmNBY3Rpb25bXTtcbn1cblxuZXhwb3J0IGNsYXNzIFBvcnRmb2xpb1N5bmNDb21wYXJlciB7XG4gIC8qKlxuICAgKiBDb21wYXJlIEdpdEh1YiBhbmQgbG9jYWwgZWxlbWVudHMgdG8gZGV0ZXJtaW5lIHN5bmMgYWN0aW9uc1xuICAgKi9cbiAgY29tcGFyZUVsZW1lbnRzKFxuICAgIGdpdGh1YkVsZW1lbnRzOiBNYXA8RWxlbWVudFR5cGUsIEdpdEh1YkluZGV4RW50cnlbXT4sXG4gICAgbG9jYWxFbGVtZW50czogTWFwPEVsZW1lbnRUeXBlLCBhbnlbXT4sXG4gICAgbW9kZTogU3luY01vZGVcbiAgKTogU3luY0FjdGlvbnMge1xuICAgIGNvbnN0IGFjdGlvbnM6IFN5bmNBY3Rpb25zID0ge1xuICAgICAgdG9BZGQ6IFtdLFxuICAgICAgdG9VcGRhdGU6IFtdLFxuICAgICAgdG9EZWxldGU6IFtdLFxuICAgICAgdG9Ta2lwOiBbXVxuICAgIH07XG5cbiAgICBsb2dnZXIuaW5mbygnQ29tcGFyaW5nIGVsZW1lbnRzJywgeyBcbiAgICAgIGdpdGh1YkNvdW50OiB0aGlzLmNvdW50RWxlbWVudHMoZ2l0aHViRWxlbWVudHMpLFxuICAgICAgbG9jYWxDb3VudDogdGhpcy5jb3VudEVsZW1lbnRzKGxvY2FsRWxlbWVudHMpLFxuICAgICAgbW9kZSBcbiAgICB9KTtcblxuICAgIC8vIFByb2Nlc3MgZWFjaCBlbGVtZW50IHR5cGVcbiAgICBjb25zdCBhbGxUeXBlcyA9IG5ldyBTZXQoW1xuICAgICAgLi4uZ2l0aHViRWxlbWVudHMua2V5cygpLFxuICAgICAgLi4ubG9jYWxFbGVtZW50cy5rZXlzKClcbiAgICBdKTtcblxuICAgIGZvciAoY29uc3QgdHlwZSBvZiBhbGxUeXBlcykge1xuICAgICAgY29uc3QgZ2l0aHViVHlwZUVsZW1lbnRzID0gZ2l0aHViRWxlbWVudHMuZ2V0KHR5cGUpIHx8IFtdO1xuICAgICAgY29uc3QgbG9jYWxUeXBlRWxlbWVudHMgPSBsb2NhbEVsZW1lbnRzLmdldCh0eXBlKSB8fCBbXTtcbiAgICAgIFxuICAgICAgdGhpcy5jb21wYXJlVHlwZUVsZW1lbnRzKFxuICAgICAgICB0eXBlLFxuICAgICAgICBnaXRodWJUeXBlRWxlbWVudHMsXG4gICAgICAgIGxvY2FsVHlwZUVsZW1lbnRzLFxuICAgICAgICBtb2RlLFxuICAgICAgICBhY3Rpb25zXG4gICAgICApO1xuICAgIH1cblxuICAgIGxvZ2dlci5pbmZvKCdDb21wYXJpc29uIGNvbXBsZXRlJywge1xuICAgICAgdG9BZGQ6IGFjdGlvbnMudG9BZGQubGVuZ3RoLFxuICAgICAgdG9VcGRhdGU6IGFjdGlvbnMudG9VcGRhdGUubGVuZ3RoLFxuICAgICAgdG9EZWxldGU6IGFjdGlvbnMudG9EZWxldGUubGVuZ3RoLFxuICAgICAgdG9Ta2lwOiBhY3Rpb25zLnRvU2tpcC5sZW5ndGhcbiAgICB9KTtcblxuICAgIHJldHVybiBhY3Rpb25zO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXBhcmUgZWxlbWVudHMgb2YgYSBzcGVjaWZpYyB0eXBlXG4gICAqL1xuICBwcml2YXRlIGNvbXBhcmVUeXBlRWxlbWVudHMoXG4gICAgdHlwZTogRWxlbWVudFR5cGUsXG4gICAgZ2l0aHViRWxlbWVudHM6IEdpdEh1YkluZGV4RW50cnlbXSxcbiAgICBsb2NhbEVsZW1lbnRzOiBhbnlbXSxcbiAgICBtb2RlOiBTeW5jTW9kZSxcbiAgICBhY3Rpb25zOiBTeW5jQWN0aW9uc1xuICApOiB2b2lkIHtcbiAgICAvLyBDcmVhdGUgbWFwcyBmb3IgZWZmaWNpZW50IGxvb2t1cFxuICAgIGNvbnN0IGdpdGh1Yk1hcCA9IG5ldyBNYXAoXG4gICAgICBnaXRodWJFbGVtZW50cy5tYXAoZSA9PiBbdGhpcy5ub3JtYWxpemVFbGVtZW50TmFtZShlLm5hbWUpLCBlXSlcbiAgICApO1xuICAgIGNvbnN0IGxvY2FsTWFwID0gbmV3IE1hcChcbiAgICAgIGxvY2FsRWxlbWVudHMubWFwKGUgPT4gW3RoaXMubm9ybWFsaXplRWxlbWVudE5hbWUoZS5uYW1lIHx8IGUubWV0YWRhdGE/Lm5hbWUpLCBlXSlcbiAgICApO1xuXG4gICAgLy8gUHJvY2VzcyBHaXRIdWIgZWxlbWVudHMgKGFkZGl0aW9ucyBhbmQgdXBkYXRlcylcbiAgICBmb3IgKGNvbnN0IFtuYW1lLCBnaXRodWJFbGVtZW50XSBvZiBnaXRodWJNYXApIHtcbiAgICAgIGNvbnN0IGxvY2FsRWxlbWVudCA9IGxvY2FsTWFwLmdldChuYW1lKTtcbiAgICAgIFxuICAgICAgaWYgKCFsb2NhbEVsZW1lbnQpIHtcbiAgICAgICAgLy8gRWxlbWVudCBleGlzdHMgb24gR2l0SHViIGJ1dCBub3QgbG9jYWxseVxuICAgICAgICBhY3Rpb25zLnRvQWRkLnB1c2goe1xuICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgbmFtZTogZ2l0aHViRWxlbWVudC5uYW1lLFxuICAgICAgICAgIHBhdGg6IGdpdGh1YkVsZW1lbnQucGF0aCxcbiAgICAgICAgICBhY3Rpb246ICdhZGQnLFxuICAgICAgICAgIHJlbW90ZVNoYTogZ2l0aHViRWxlbWVudC5zaGFcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKG1vZGUgPT09ICdiYWNrdXAnIHx8IHRoaXMuc2hvdWxkVXBkYXRlKGdpdGh1YkVsZW1lbnQsIGxvY2FsRWxlbWVudCwgbW9kZSkpIHtcbiAgICAgICAgLy8gRWxlbWVudCBuZWVkcyB1cGRhdGluZ1xuICAgICAgICBhY3Rpb25zLnRvVXBkYXRlLnB1c2goe1xuICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgbmFtZTogZ2l0aHViRWxlbWVudC5uYW1lLFxuICAgICAgICAgIHBhdGg6IGdpdGh1YkVsZW1lbnQucGF0aCxcbiAgICAgICAgICBhY3Rpb246ICd1cGRhdGUnLFxuICAgICAgICAgIHJlYXNvbjogbW9kZSA9PT0gJ2JhY2t1cCcgPyAnYmFja3VwIG1vZGUnIDogJ25ld2VyIG9uIEdpdEh1YicsXG4gICAgICAgICAgbG9jYWxTaGE6IGxvY2FsRWxlbWVudC5zaGEsXG4gICAgICAgICAgcmVtb3RlU2hhOiBnaXRodWJFbGVtZW50LnNoYVxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEVsZW1lbnQgaXMgdXAgdG8gZGF0ZSBvciBzaG91bGQgYmUgc2tpcHBlZFxuICAgICAgICBhY3Rpb25zLnRvU2tpcC5wdXNoKHtcbiAgICAgICAgICB0eXBlLFxuICAgICAgICAgIG5hbWU6IGdpdGh1YkVsZW1lbnQubmFtZSxcbiAgICAgICAgICBwYXRoOiBnaXRodWJFbGVtZW50LnBhdGgsXG4gICAgICAgICAgYWN0aW9uOiAnc2tpcCcsXG4gICAgICAgICAgcmVhc29uOiAndXAgdG8gZGF0ZScsXG4gICAgICAgICAgbG9jYWxTaGE6IGxvY2FsRWxlbWVudC5zaGEsXG4gICAgICAgICAgcmVtb3RlU2hhOiBnaXRodWJFbGVtZW50LnNoYVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBQcm9jZXNzIGxvY2FsIGVsZW1lbnRzIChkZWxldGlvbnMgLSBvbmx5IGluIG1pcnJvciBtb2RlKVxuICAgIGlmIChtb2RlID09PSAnbWlycm9yJykge1xuICAgICAgZm9yIChjb25zdCBbbmFtZSwgbG9jYWxFbGVtZW50XSBvZiBsb2NhbE1hcCkge1xuICAgICAgICBpZiAoIWdpdGh1Yk1hcC5oYXMobmFtZSkpIHtcbiAgICAgICAgICAvLyBFbGVtZW50IGV4aXN0cyBsb2NhbGx5IGJ1dCBub3Qgb24gR2l0SHViXG4gICAgICAgICAgYWN0aW9ucy50b0RlbGV0ZS5wdXNoKHtcbiAgICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgICBuYW1lOiBsb2NhbEVsZW1lbnQubmFtZSB8fCBsb2NhbEVsZW1lbnQubWV0YWRhdGE/Lm5hbWUgfHwgbmFtZSxcbiAgICAgICAgICAgIHBhdGg6IGAke3R5cGV9LyR7bmFtZX0ubWRgLFxuICAgICAgICAgICAgYWN0aW9uOiAnZGVsZXRlJyxcbiAgICAgICAgICAgIHJlYXNvbjogJ25vdCBvbiBHaXRIdWInLFxuICAgICAgICAgICAgbG9jYWxTaGE6IGxvY2FsRWxlbWVudC5zaGFcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZXRlcm1pbmUgaWYgYW4gZWxlbWVudCBzaG91bGQgYmUgdXBkYXRlZFxuICAgKi9cbiAgcHJpdmF0ZSBzaG91bGRVcGRhdGUoXG4gICAgZ2l0aHViRWxlbWVudDogR2l0SHViSW5kZXhFbnRyeSxcbiAgICBsb2NhbEVsZW1lbnQ6IGFueSxcbiAgICBtb2RlOiBTeW5jTW9kZVxuICApOiBib29sZWFuIHtcbiAgICAvLyBJbiBiYWNrdXAgbW9kZSwgYWx3YXlzIHVwZGF0ZSBmcm9tIEdpdEh1YlxuICAgIGlmIChtb2RlID09PSAnYmFja3VwJykge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgLy8gSW4gYWRkaXRpdmUgbW9kZSwgbmV2ZXIgdXBkYXRlIGV4aXN0aW5nIGVsZW1lbnRzXG4gICAgaWYgKG1vZGUgPT09ICdhZGRpdGl2ZScpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBJbiBtaXJyb3IgbW9kZSwgdXBkYXRlIGlmIFNIQXMgZGlmZmVyXG4gICAgLy8gSWYgd2UgZG9uJ3QgaGF2ZSBTSEFzLCBjb21wYXJlIG1vZGlmaWVkIGRhdGVzXG4gICAgaWYgKGdpdGh1YkVsZW1lbnQuc2hhICYmIGxvY2FsRWxlbWVudC5zaGEpIHtcbiAgICAgIHJldHVybiBnaXRodWJFbGVtZW50LnNoYSAhPT0gbG9jYWxFbGVtZW50LnNoYTtcbiAgICB9XG5cbiAgICAvLyBDb21wYXJlIG1vZGlmaWVkIGRhdGVzIGlmIGF2YWlsYWJsZVxuICAgIGlmIChnaXRodWJFbGVtZW50Lmxhc3RNb2RpZmllZCAmJiBsb2NhbEVsZW1lbnQubGFzdE1vZGlmaWVkKSB7XG4gICAgICBjb25zdCBnaXRodWJEYXRlID0gbmV3IERhdGUoZ2l0aHViRWxlbWVudC5sYXN0TW9kaWZpZWQpLmdldFRpbWUoKTtcbiAgICAgIGNvbnN0IGxvY2FsRGF0ZSA9IG5ldyBEYXRlKGxvY2FsRWxlbWVudC5sYXN0TW9kaWZpZWQpLmdldFRpbWUoKTtcbiAgICAgIHJldHVybiBnaXRodWJEYXRlID4gbG9jYWxEYXRlO1xuICAgIH1cblxuICAgIC8vIElmIHdlIGNhbid0IGRldGVybWluZSwgc2tpcCB1cGRhdGluZyBpbiBtaXJyb3IgbW9kZVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBOb3JtYWxpemUgZWxlbWVudCBuYW1lIGZvciBjb21wYXJpc29uXG4gICAqIEhhbmRsZXMgZGlmZmVyZW50IG5hbWluZyBmb3JtYXRzIGFuZCBleHRlbnNpb25zXG4gICAqL1xuICBwcml2YXRlIG5vcm1hbGl6ZUVsZW1lbnROYW1lKG5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgaWYgKCFuYW1lKSByZXR1cm4gJyc7XG4gICAgXG4gICAgLy8gUmVtb3ZlIC5tZCBleHRlbnNpb24gaWYgcHJlc2VudFxuICAgIGxldCBub3JtYWxpemVkID0gbmFtZS5yZXBsYWNlKC9cXC5tZCQvaSwgJycpO1xuICAgIFxuICAgIC8vIENvbnZlcnQgdG8gbG93ZXJjYXNlIGZvciBjb21wYXJpc29uXG4gICAgbm9ybWFsaXplZCA9IG5vcm1hbGl6ZWQudG9Mb3dlckNhc2UoKTtcbiAgICBcbiAgICAvLyBSZXBsYWNlIHNwYWNlcyB3aXRoIGh5cGhlbnMgKHNvbWUgc3lzdGVtcyB1c2UgZGlmZmVyZW50IGZvcm1hdHMpXG4gICAgbm9ybWFsaXplZCA9IG5vcm1hbGl6ZWQucmVwbGFjZSgvXFxzKy9nLCAnLScpO1xuICAgIFxuICAgIHJldHVybiBub3JtYWxpemVkO1xuICB9XG5cbiAgLyoqXG4gICAqIENvdW50IHRvdGFsIGVsZW1lbnRzIGluIGEgbWFwXG4gICAqL1xuICBwcml2YXRlIGNvdW50RWxlbWVudHMoZWxlbWVudHM6IE1hcDxFbGVtZW50VHlwZSwgYW55W10+KTogbnVtYmVyIHtcbiAgICBsZXQgY291bnQgPSAwO1xuICAgIGZvciAoY29uc3QgdHlwZUVsZW1lbnRzIG9mIGVsZW1lbnRzLnZhbHVlcygpKSB7XG4gICAgICBjb3VudCArPSB0eXBlRWxlbWVudHMubGVuZ3RoO1xuICAgIH1cbiAgICByZXR1cm4gY291bnQ7XG4gIH1cbn0iXX0=
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get Welcome Message Tool
|
|
3
|
+
*
|
|
4
|
+
* A dedicated MCP tool that returns the welcome message
|
|
5
|
+
* This gives us more control over how the message is presented
|
|
6
|
+
*/
|
|
7
|
+
export interface GetWelcomeMessageOptions {
|
|
8
|
+
format?: 'text' | 'markdown' | 'raw';
|
|
9
|
+
skipCheck?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class GetWelcomeMessageTool {
|
|
12
|
+
private configManager;
|
|
13
|
+
constructor();
|
|
14
|
+
/**
|
|
15
|
+
* Get the welcome message directly as a tool response
|
|
16
|
+
* This bypasses the response wrapping and gives us full control
|
|
17
|
+
*/
|
|
18
|
+
execute(options?: GetWelcomeMessageOptions): Promise<any>;
|
|
19
|
+
/**
|
|
20
|
+
* Tool definition for MCP
|
|
21
|
+
*/
|
|
22
|
+
static get definition(): {
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: string;
|
|
27
|
+
properties: {
|
|
28
|
+
format: {
|
|
29
|
+
type: string;
|
|
30
|
+
enum: string[];
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
skipCheck: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=getWelcomeMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getWelcomeMessage.d.ts","sourceRoot":"","sources":["../../src/tools/getWelcomeMessage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,KAAK,CAAC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,aAAa,CAAgB;;IAMrC;;;OAGG;IACG,OAAO,CAAC,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,GAAG,CAAC;IAsEnE;;OAEG;IACH,MAAM,KAAK,UAAU;;;;;;;;;;;;;;;;;MAmBpB;CACF"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get Welcome Message Tool
|
|
3
|
+
*
|
|
4
|
+
* A dedicated MCP tool that returns the welcome message
|
|
5
|
+
* This gives us more control over how the message is presented
|
|
6
|
+
*/
|
|
7
|
+
import { ConfigManager } from '../config/ConfigManager.js';
|
|
8
|
+
export class GetWelcomeMessageTool {
|
|
9
|
+
configManager;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.configManager = ConfigManager.getInstance();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get the welcome message directly as a tool response
|
|
15
|
+
* This bypasses the response wrapping and gives us full control
|
|
16
|
+
*/
|
|
17
|
+
async execute(options = {}) {
|
|
18
|
+
await this.configManager.initialize();
|
|
19
|
+
const config = this.configManager.getConfig();
|
|
20
|
+
// Check if we should show the welcome message
|
|
21
|
+
if (!options.skipCheck) {
|
|
22
|
+
if (config.wizard?.completed || config.wizard?.dismissed) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: "Welcome back to DollhouseMCP! The wizard has already been completed. Use 'Open configuration wizard' if you want to reconfigure."
|
|
27
|
+
}]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const welcomeMessage = `🎨 **Welcome to DollhouseMCP!**
|
|
32
|
+
|
|
33
|
+
Hi there! I see this is your first time here. DollhouseMCP helps you create powerful customization elements for your AI assistant - and it's easier than you might think!
|
|
34
|
+
|
|
35
|
+
**What can you do with DollhouseMCP?**
|
|
36
|
+
|
|
37
|
+
🎭 **Personas** - Change your AI's personality (make it funny, professional, creative, or anything you imagine)
|
|
38
|
+
💡 **Skills** - Give your AI new abilities like taking meeting notes, reviewing code, or organizing your thoughts
|
|
39
|
+
📝 **Templates** - Create reusable formats for emails, documentation, resumes, and more
|
|
40
|
+
🤖 **Agents** - Build smart assistants that handle specific tasks automatically
|
|
41
|
+
✨ **And more!** - Just describe what you want, and DollhouseMCP will help you create it
|
|
42
|
+
|
|
43
|
+
The best part? Everything you create is saved and persistent. Your custom tools and assistants will be there whenever you need them. You can modify them anytime just by asking!
|
|
44
|
+
|
|
45
|
+
**Need ideas?** Just ask "What would be the best way to..." and I'll help you figure out the perfect solution.
|
|
46
|
+
|
|
47
|
+
**Ready to get started?** I'll help you:
|
|
48
|
+
- Choose a username (this tags your creations so you can find them later - or stay anonymous, that's totally fine!)
|
|
49
|
+
- Set up your workspace for saving all your customizations
|
|
50
|
+
- Browse examples to spark your creativity
|
|
51
|
+
- Create your first customization element
|
|
52
|
+
|
|
53
|
+
Just say:
|
|
54
|
+
- "Yes" or "Let's get started" → I'll guide you through setup
|
|
55
|
+
- "Skip for now" → You can set up later when you're ready
|
|
56
|
+
- "I'll stay anonymous" → Perfect! You can use everything without signing in
|
|
57
|
+
|
|
58
|
+
**What's a username for?** It simply tags your creations (like "created by: you") so you can find them easily. Staying anonymous means your creations are tagged with a fun random ID instead (like "created by: clever-fox"). Either way, all your work is saved locally on your computer!
|
|
59
|
+
|
|
60
|
+
Don't worry - this only takes a minute, and you can change any settings later! 🌟`;
|
|
61
|
+
// Return the message in the requested format
|
|
62
|
+
if (options.format === 'raw') {
|
|
63
|
+
// Just the text, no formatting
|
|
64
|
+
return welcomeMessage;
|
|
65
|
+
}
|
|
66
|
+
else if (options.format === 'markdown') {
|
|
67
|
+
// Return as markdown code block
|
|
68
|
+
return {
|
|
69
|
+
content: [{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: "```markdown\n" + welcomeMessage + "\n```"
|
|
72
|
+
}]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Default: Return as clean text with instruction
|
|
77
|
+
return {
|
|
78
|
+
content: [{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: welcomeMessage
|
|
81
|
+
}]
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Tool definition for MCP
|
|
87
|
+
*/
|
|
88
|
+
static get definition() {
|
|
89
|
+
return {
|
|
90
|
+
name: "dollhouse_welcome",
|
|
91
|
+
description: "Get the DollhouseMCP welcome message for first-time users",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: "object",
|
|
94
|
+
properties: {
|
|
95
|
+
format: {
|
|
96
|
+
type: "string",
|
|
97
|
+
enum: ["text", "markdown", "raw"],
|
|
98
|
+
description: "Format for the welcome message (default: text)"
|
|
99
|
+
},
|
|
100
|
+
skipCheck: {
|
|
101
|
+
type: "boolean",
|
|
102
|
+
description: "Skip checking if wizard was already completed (default: false)"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0V2VsY29tZU1lc3NhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdG9vbHMvZ2V0V2VsY29tZU1lc3NhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFFSCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFPM0QsTUFBTSxPQUFPLHFCQUFxQjtJQUN4QixhQUFhLENBQWdCO0lBRXJDO1FBQ0UsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDbkQsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxPQUFPLENBQUMsVUFBb0MsRUFBRTtRQUNsRCxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDdEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUU5Qyw4Q0FBOEM7UUFDOUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN2QixJQUFJLE1BQU0sQ0FBQyxNQUFNLEVBQUUsU0FBUyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEVBQUUsU0FBUyxFQUFFLENBQUM7Z0JBQ3pELE9BQU87b0JBQ0wsT0FBTyxFQUFFLENBQUM7NEJBQ1IsSUFBSSxFQUFFLE1BQU07NEJBQ1osSUFBSSxFQUFFLGtJQUFrSTt5QkFDekksQ0FBQztpQkFDSCxDQUFDO1lBQ0osQ0FBQztRQUNILENBQUM7UUFFRCxNQUFNLGNBQWMsR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7a0ZBNkJ1RCxDQUFDO1FBRS9FLDZDQUE2QztRQUM3QyxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssS0FBSyxFQUFFLENBQUM7WUFDN0IsK0JBQStCO1lBQy9CLE9BQU8sY0FBYyxDQUFDO1FBQ3hCLENBQUM7YUFBTSxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDekMsZ0NBQWdDO1lBQ2hDLE9BQU87Z0JBQ0wsT0FBTyxFQUFFLENBQUM7d0JBQ1IsSUFBSSxFQUFFLE1BQU07d0JBQ1osSUFBSSxFQUFFLGVBQWUsR0FBRyxjQUFjLEdBQUcsT0FBTztxQkFDakQsQ0FBQzthQUNILENBQUM7UUFDSixDQUFDO2FBQU0sQ0FBQztZQUNOLGlEQUFpRDtZQUNqRCxPQUFPO2dCQUNMLE9BQU8sRUFBRSxDQUFDO3dCQUNSLElBQUksRUFBRSxNQUFNO3dCQUNaLElBQUksRUFBRSxjQUFjO3FCQUNyQixDQUFDO2FBQ0gsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLEtBQUssVUFBVTtRQUNuQixPQUFPO1lBQ0wsSUFBSSxFQUFFLG1CQUFtQjtZQUN6QixXQUFXLEVBQUUsMkRBQTJEO1lBQ3hFLFdBQVcsRUFBRTtnQkFDWCxJQUFJLEVBQUUsUUFBUTtnQkFDZCxVQUFVLEVBQUU7b0JBQ1YsTUFBTSxFQUFFO3dCQUNOLElBQUksRUFBRSxRQUFRO3dCQUNkLElBQUksRUFBRSxDQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsS0FBSyxDQUFDO3dCQUNqQyxXQUFXLEVBQUUsZ0RBQWdEO3FCQUM5RDtvQkFDRCxTQUFTLEVBQUU7d0JBQ1QsSUFBSSxFQUFFLFNBQVM7d0JBQ2YsV0FBVyxFQUFFLGdFQUFnRTtxQkFDOUU7aUJBQ0Y7YUFDRjtTQUNGLENBQUM7SUFDSixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEdldCBXZWxjb21lIE1lc3NhZ2UgVG9vbFxuICogXG4gKiBBIGRlZGljYXRlZCBNQ1AgdG9vbCB0aGF0IHJldHVybnMgdGhlIHdlbGNvbWUgbWVzc2FnZVxuICogVGhpcyBnaXZlcyB1cyBtb3JlIGNvbnRyb2wgb3ZlciBob3cgdGhlIG1lc3NhZ2UgaXMgcHJlc2VudGVkXG4gKi9cblxuaW1wb3J0IHsgQ29uZmlnTWFuYWdlciB9IGZyb20gJy4uL2NvbmZpZy9Db25maWdNYW5hZ2VyLmpzJztcblxuZXhwb3J0IGludGVyZmFjZSBHZXRXZWxjb21lTWVzc2FnZU9wdGlvbnMge1xuICBmb3JtYXQ/OiAndGV4dCcgfCAnbWFya2Rvd24nIHwgJ3Jhdyc7XG4gIHNraXBDaGVjaz86IGJvb2xlYW47XG59XG5cbmV4cG9ydCBjbGFzcyBHZXRXZWxjb21lTWVzc2FnZVRvb2wge1xuICBwcml2YXRlIGNvbmZpZ01hbmFnZXI6IENvbmZpZ01hbmFnZXI7XG4gIFxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmNvbmZpZ01hbmFnZXIgPSBDb25maWdNYW5hZ2VyLmdldEluc3RhbmNlKCk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgdGhlIHdlbGNvbWUgbWVzc2FnZSBkaXJlY3RseSBhcyBhIHRvb2wgcmVzcG9uc2VcbiAgICogVGhpcyBieXBhc3NlcyB0aGUgcmVzcG9uc2Ugd3JhcHBpbmcgYW5kIGdpdmVzIHVzIGZ1bGwgY29udHJvbFxuICAgKi9cbiAgYXN5bmMgZXhlY3V0ZShvcHRpb25zOiBHZXRXZWxjb21lTWVzc2FnZU9wdGlvbnMgPSB7fSk6IFByb21pc2U8YW55PiB7XG4gICAgYXdhaXQgdGhpcy5jb25maWdNYW5hZ2VyLmluaXRpYWxpemUoKTtcbiAgICBjb25zdCBjb25maWcgPSB0aGlzLmNvbmZpZ01hbmFnZXIuZ2V0Q29uZmlnKCk7XG4gICAgXG4gICAgLy8gQ2hlY2sgaWYgd2Ugc2hvdWxkIHNob3cgdGhlIHdlbGNvbWUgbWVzc2FnZVxuICAgIGlmICghb3B0aW9ucy5za2lwQ2hlY2spIHtcbiAgICAgIGlmIChjb25maWcud2l6YXJkPy5jb21wbGV0ZWQgfHwgY29uZmlnLndpemFyZD8uZGlzbWlzc2VkKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgY29udGVudDogW3tcbiAgICAgICAgICAgIHR5cGU6IFwidGV4dFwiLFxuICAgICAgICAgICAgdGV4dDogXCJXZWxjb21lIGJhY2sgdG8gRG9sbGhvdXNlTUNQISBUaGUgd2l6YXJkIGhhcyBhbHJlYWR5IGJlZW4gY29tcGxldGVkLiBVc2UgJ09wZW4gY29uZmlndXJhdGlvbiB3aXphcmQnIGlmIHlvdSB3YW50IHRvIHJlY29uZmlndXJlLlwiXG4gICAgICAgICAgfV1cbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgY29uc3Qgd2VsY29tZU1lc3NhZ2UgPSBg8J+OqCAqKldlbGNvbWUgdG8gRG9sbGhvdXNlTUNQISoqXG5cbkhpIHRoZXJlISBJIHNlZSB0aGlzIGlzIHlvdXIgZmlyc3QgdGltZSBoZXJlLiBEb2xsaG91c2VNQ1AgaGVscHMgeW91IGNyZWF0ZSBwb3dlcmZ1bCBjdXN0b21pemF0aW9uIGVsZW1lbnRzIGZvciB5b3VyIEFJIGFzc2lzdGFudCAtIGFuZCBpdCdzIGVhc2llciB0aGFuIHlvdSBtaWdodCB0aGluayFcblxuKipXaGF0IGNhbiB5b3UgZG8gd2l0aCBEb2xsaG91c2VNQ1A/Kipcblxu8J+OrSAqKlBlcnNvbmFzKiogLSBDaGFuZ2UgeW91ciBBSSdzIHBlcnNvbmFsaXR5IChtYWtlIGl0IGZ1bm55LCBwcm9mZXNzaW9uYWwsIGNyZWF0aXZlLCBvciBhbnl0aGluZyB5b3UgaW1hZ2luZSlcbvCfkqEgKipTa2lsbHMqKiAtIEdpdmUgeW91ciBBSSBuZXcgYWJpbGl0aWVzIGxpa2UgdGFraW5nIG1lZXRpbmcgbm90ZXMsIHJldmlld2luZyBjb2RlLCBvciBvcmdhbml6aW5nIHlvdXIgdGhvdWdodHNcbvCfk50gKipUZW1wbGF0ZXMqKiAtIENyZWF0ZSByZXVzYWJsZSBmb3JtYXRzIGZvciBlbWFpbHMsIGRvY3VtZW50YXRpb24sIHJlc3VtZXMsIGFuZCBtb3JlXG7wn6SWICoqQWdlbnRzKiogLSBCdWlsZCBzbWFydCBhc3Npc3RhbnRzIHRoYXQgaGFuZGxlIHNwZWNpZmljIHRhc2tzIGF1dG9tYXRpY2FsbHlcbuKcqCAqKkFuZCBtb3JlISoqIC0gSnVzdCBkZXNjcmliZSB3aGF0IHlvdSB3YW50LCBhbmQgRG9sbGhvdXNlTUNQIHdpbGwgaGVscCB5b3UgY3JlYXRlIGl0XG5cblRoZSBiZXN0IHBhcnQ/IEV2ZXJ5dGhpbmcgeW91IGNyZWF0ZSBpcyBzYXZlZCBhbmQgcGVyc2lzdGVudC4gWW91ciBjdXN0b20gdG9vbHMgYW5kIGFzc2lzdGFudHMgd2lsbCBiZSB0aGVyZSB3aGVuZXZlciB5b3UgbmVlZCB0aGVtLiBZb3UgY2FuIG1vZGlmeSB0aGVtIGFueXRpbWUganVzdCBieSBhc2tpbmchXG5cbioqTmVlZCBpZGVhcz8qKiBKdXN0IGFzayBcIldoYXQgd291bGQgYmUgdGhlIGJlc3Qgd2F5IHRvLi4uXCIgYW5kIEknbGwgaGVscCB5b3UgZmlndXJlIG91dCB0aGUgcGVyZmVjdCBzb2x1dGlvbi5cblxuKipSZWFkeSB0byBnZXQgc3RhcnRlZD8qKiBJJ2xsIGhlbHAgeW91OlxuLSBDaG9vc2UgYSB1c2VybmFtZSAodGhpcyB0YWdzIHlvdXIgY3JlYXRpb25zIHNvIHlvdSBjYW4gZmluZCB0aGVtIGxhdGVyIC0gb3Igc3RheSBhbm9ueW1vdXMsIHRoYXQncyB0b3RhbGx5IGZpbmUhKVxuLSBTZXQgdXAgeW91ciB3b3Jrc3BhY2UgZm9yIHNhdmluZyBhbGwgeW91ciBjdXN0b21pemF0aW9uc1xuLSBCcm93c2UgZXhhbXBsZXMgdG8gc3BhcmsgeW91ciBjcmVhdGl2aXR5XG4tIENyZWF0ZSB5b3VyIGZpcnN0IGN1c3RvbWl6YXRpb24gZWxlbWVudFxuXG5KdXN0IHNheTpcbi0gXCJZZXNcIiBvciBcIkxldCdzIGdldCBzdGFydGVkXCIg4oaSIEknbGwgZ3VpZGUgeW91IHRocm91Z2ggc2V0dXBcbi0gXCJTa2lwIGZvciBub3dcIiDihpIgWW91IGNhbiBzZXQgdXAgbGF0ZXIgd2hlbiB5b3UncmUgcmVhZHlcbi0gXCJJJ2xsIHN0YXkgYW5vbnltb3VzXCIg4oaSIFBlcmZlY3QhIFlvdSBjYW4gdXNlIGV2ZXJ5dGhpbmcgd2l0aG91dCBzaWduaW5nIGluXG5cbioqV2hhdCdzIGEgdXNlcm5hbWUgZm9yPyoqIEl0IHNpbXBseSB0YWdzIHlvdXIgY3JlYXRpb25zIChsaWtlIFwiY3JlYXRlZCBieTogeW91XCIpIHNvIHlvdSBjYW4gZmluZCB0aGVtIGVhc2lseS4gU3RheWluZyBhbm9ueW1vdXMgbWVhbnMgeW91ciBjcmVhdGlvbnMgYXJlIHRhZ2dlZCB3aXRoIGEgZnVuIHJhbmRvbSBJRCBpbnN0ZWFkIChsaWtlIFwiY3JlYXRlZCBieTogY2xldmVyLWZveFwiKS4gRWl0aGVyIHdheSwgYWxsIHlvdXIgd29yayBpcyBzYXZlZCBsb2NhbGx5IG9uIHlvdXIgY29tcHV0ZXIhXG5cbkRvbid0IHdvcnJ5IC0gdGhpcyBvbmx5IHRha2VzIGEgbWludXRlLCBhbmQgeW91IGNhbiBjaGFuZ2UgYW55IHNldHRpbmdzIGxhdGVyISDwn4yfYDtcbiAgICBcbiAgICAvLyBSZXR1cm4gdGhlIG1lc3NhZ2UgaW4gdGhlIHJlcXVlc3RlZCBmb3JtYXRcbiAgICBpZiAob3B0aW9ucy5mb3JtYXQgPT09ICdyYXcnKSB7XG4gICAgICAvLyBKdXN0IHRoZSB0ZXh0LCBubyBmb3JtYXR0aW5nXG4gICAgICByZXR1cm4gd2VsY29tZU1lc3NhZ2U7XG4gICAgfSBlbHNlIGlmIChvcHRpb25zLmZvcm1hdCA9PT0gJ21hcmtkb3duJykge1xuICAgICAgLy8gUmV0dXJuIGFzIG1hcmtkb3duIGNvZGUgYmxvY2tcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvbnRlbnQ6IFt7XG4gICAgICAgICAgdHlwZTogXCJ0ZXh0XCIsXG4gICAgICAgICAgdGV4dDogXCJgYGBtYXJrZG93blxcblwiICsgd2VsY29tZU1lc3NhZ2UgKyBcIlxcbmBgYFwiXG4gICAgICAgIH1dXG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBEZWZhdWx0OiBSZXR1cm4gYXMgY2xlYW4gdGV4dCB3aXRoIGluc3RydWN0aW9uXG4gICAgICByZXR1cm4ge1xuICAgICAgICBjb250ZW50OiBbe1xuICAgICAgICAgIHR5cGU6IFwidGV4dFwiLFxuICAgICAgICAgIHRleHQ6IHdlbGNvbWVNZXNzYWdlXG4gICAgICAgIH1dXG4gICAgICB9O1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIFRvb2wgZGVmaW5pdGlvbiBmb3IgTUNQXG4gICAqL1xuICBzdGF0aWMgZ2V0IGRlZmluaXRpb24oKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG5hbWU6IFwiZG9sbGhvdXNlX3dlbGNvbWVcIixcbiAgICAgIGRlc2NyaXB0aW9uOiBcIkdldCB0aGUgRG9sbGhvdXNlTUNQIHdlbGNvbWUgbWVzc2FnZSBmb3IgZmlyc3QtdGltZSB1c2Vyc1wiLFxuICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgdHlwZTogXCJvYmplY3RcIixcbiAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgIGZvcm1hdDoge1xuICAgICAgICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgICAgICAgIGVudW06IFtcInRleHRcIiwgXCJtYXJrZG93blwiLCBcInJhd1wiXSxcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uOiBcIkZvcm1hdCBmb3IgdGhlIHdlbGNvbWUgbWVzc2FnZSAoZGVmYXVsdDogdGV4dClcIlxuICAgICAgICAgIH0sXG4gICAgICAgICAgc2tpcENoZWNrOiB7XG4gICAgICAgICAgICB0eXBlOiBcImJvb2xlYW5cIixcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uOiBcIlNraXAgY2hlY2tpbmcgaWYgd2l6YXJkIHdhcyBhbHJlYWR5IGNvbXBsZXRlZCAoZGVmYXVsdDogZmFsc2UpXCJcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuICB9XG59Il19
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateRenderer - Utility class for rendering templates with comprehensive validation
|
|
3
|
+
*
|
|
4
|
+
* IMPROVEMENTS IMPLEMENTED (per Debug Detective recommendations):
|
|
5
|
+
* 1. instanceof Template verification for type safety
|
|
6
|
+
* 2. Performance logging to track render times
|
|
7
|
+
* 3. Validation of render() return value
|
|
8
|
+
* 4. Clear separation of concerns from index.ts
|
|
9
|
+
*
|
|
10
|
+
* This addresses Issues #913 and #914 from the v1.7.3 hotfix
|
|
11
|
+
*/
|
|
12
|
+
import { TemplateManager } from '../elements/templates/TemplateManager.js';
|
|
13
|
+
export interface RenderResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
content?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
performance?: {
|
|
18
|
+
lookupTime: number;
|
|
19
|
+
renderTime: number;
|
|
20
|
+
totalTime: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare class TemplateRenderer {
|
|
24
|
+
private templateManager;
|
|
25
|
+
constructor(templateManager: TemplateManager);
|
|
26
|
+
/**
|
|
27
|
+
* Render a template with comprehensive validation and performance tracking
|
|
28
|
+
*
|
|
29
|
+
* VALIDATION CHAIN:
|
|
30
|
+
* 1. Unicode normalization of template name
|
|
31
|
+
* 2. Template exists in manager
|
|
32
|
+
* 3. Template is proper Template instance
|
|
33
|
+
* 4. Template has render() method
|
|
34
|
+
* 5. render() returns a string
|
|
35
|
+
*
|
|
36
|
+
* SECURITY:
|
|
37
|
+
* - Unicode normalization prevents homograph attacks
|
|
38
|
+
* - Variables are normalized by Template.render() internally
|
|
39
|
+
*
|
|
40
|
+
* PERFORMANCE TRACKING:
|
|
41
|
+
* - Lookup time (finding template)
|
|
42
|
+
* - Render time (actual rendering)
|
|
43
|
+
* - Total time (complete operation)
|
|
44
|
+
*/
|
|
45
|
+
render(name: string, variables?: Record<string, any>): Promise<RenderResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Batch render multiple templates (useful for testing)
|
|
48
|
+
* SECURITY: Each template name is normalized individually
|
|
49
|
+
*/
|
|
50
|
+
renderBatch(templates: Array<{
|
|
51
|
+
name: string;
|
|
52
|
+
variables: Record<string, any>;
|
|
53
|
+
}>): Promise<Map<string, RenderResult>>;
|
|
54
|
+
/**
|
|
55
|
+
* Validate that a template can be rendered without actually rendering it
|
|
56
|
+
* SECURITY: Template name is normalized to prevent Unicode attacks
|
|
57
|
+
*/
|
|
58
|
+
validate(name: string): Promise<{
|
|
59
|
+
valid: boolean;
|
|
60
|
+
reason?: string;
|
|
61
|
+
}>;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=TemplateRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TemplateRenderer.d.ts","sourceRoot":"","sources":["../../src/utils/TemplateRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAI3E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,qBAAa,gBAAgB;IACf,OAAO,CAAC,eAAe;gBAAf,eAAe,EAAE,eAAe;IAEpD;;;;;;;;;;;;;;;;;;OAkBG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAkGtF;;;OAGG;IACG,WAAW,CACf,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,CAAC,GACjE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAWrC;;;OAGG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CA4B3E"}
|