@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,368 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ConfigWizard - Interactive configuration wizard for new DollhouseMCP installations
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Guided walkthrough of all configuration options
|
|
7
|
+
* - Educational explanations for each setting
|
|
8
|
+
* - Tracking of completion/dismissal status
|
|
9
|
+
* - Non-intrusive first-run experience
|
|
10
|
+
* - Re-runnable via MCP tool
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.ConfigWizard = void 0;
|
|
14
|
+
const readline = require("readline");
|
|
15
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
16
|
+
const chalk_1 = require("chalk");
|
|
17
|
+
const WIZARD_VERSION = '1.0.0';
|
|
18
|
+
class ConfigWizard {
|
|
19
|
+
constructor(configManager) {
|
|
20
|
+
this.configManager = configManager;
|
|
21
|
+
// Check if we're in an interactive terminal
|
|
22
|
+
this.isInteractive = process.stdin.isTTY && process.stdout.isTTY;
|
|
23
|
+
// Only create readline interface if interactive
|
|
24
|
+
if (this.isInteractive) {
|
|
25
|
+
this.rl = readline.createInterface({
|
|
26
|
+
input: process.stdin,
|
|
27
|
+
output: process.stdout,
|
|
28
|
+
terminal: true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// No interface in non-interactive mode
|
|
33
|
+
this.rl = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if the wizard should run
|
|
38
|
+
*/
|
|
39
|
+
async shouldRunWizard() {
|
|
40
|
+
// Don't run in non-interactive environments (CI, automated scripts)
|
|
41
|
+
if (!this.isInteractive) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const config = this.configManager.getConfig();
|
|
45
|
+
return !config.wizard?.completed && !config.wizard?.dismissed;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Show initial prompt to user
|
|
49
|
+
*/
|
|
50
|
+
async promptInitial() {
|
|
51
|
+
console.log('\n' + chalk_1.default.cyan('═'.repeat(60)));
|
|
52
|
+
console.log(chalk_1.default.cyan.bold(' Welcome to DollhouseMCP! 🎭'));
|
|
53
|
+
console.log(chalk_1.default.cyan('═'.repeat(60)) + '\n');
|
|
54
|
+
console.log(chalk_1.default.yellow('This appears to be your first time using DollhouseMCP.'));
|
|
55
|
+
console.log('Would you like a guided walkthrough of the configuration options?\n');
|
|
56
|
+
console.log(chalk_1.default.green(' [1]') + ' Yes, guide me through the configuration');
|
|
57
|
+
console.log(chalk_1.default.yellow(' [2]') + ' Skip for now (ask again next time)');
|
|
58
|
+
console.log(chalk_1.default.gray(' [3]') + ' Don\'t show this again\n');
|
|
59
|
+
const choice = await this.prompt('Choice [1/2/3]: ', '2');
|
|
60
|
+
switch (choice) {
|
|
61
|
+
case '1':
|
|
62
|
+
return 'yes';
|
|
63
|
+
case '3':
|
|
64
|
+
return 'never';
|
|
65
|
+
default:
|
|
66
|
+
return 'skip';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Run the full configuration wizard
|
|
71
|
+
*/
|
|
72
|
+
async runWizard() {
|
|
73
|
+
console.log('\n' + chalk_1.default.cyan('Starting Configuration Wizard...'));
|
|
74
|
+
console.log(chalk_1.default.gray('You can press Ctrl+C at any time to exit\n'));
|
|
75
|
+
try {
|
|
76
|
+
// Track skipped sections
|
|
77
|
+
const skippedSections = [];
|
|
78
|
+
// Section 1: User Identity
|
|
79
|
+
const skipIdentity = await this.runUserIdentitySection();
|
|
80
|
+
if (skipIdentity)
|
|
81
|
+
skippedSections.push('user');
|
|
82
|
+
// Section 2: GitHub Integration
|
|
83
|
+
const skipGitHub = await this.runGitHubSection();
|
|
84
|
+
if (skipGitHub)
|
|
85
|
+
skippedSections.push('github');
|
|
86
|
+
// Section 3: Sync Settings
|
|
87
|
+
const skipSync = await this.runSyncSection();
|
|
88
|
+
if (skipSync)
|
|
89
|
+
skippedSections.push('sync');
|
|
90
|
+
// Section 4: Collection Settings
|
|
91
|
+
const skipCollection = await this.runCollectionSection();
|
|
92
|
+
if (skipCollection)
|
|
93
|
+
skippedSections.push('collection');
|
|
94
|
+
// Section 5: Display Settings
|
|
95
|
+
const skipDisplay = await this.runDisplaySection();
|
|
96
|
+
if (skipDisplay)
|
|
97
|
+
skippedSections.push('display');
|
|
98
|
+
// Show summary and confirm
|
|
99
|
+
await this.showSummary();
|
|
100
|
+
const save = await this.promptYesNo('Save this configuration?', true);
|
|
101
|
+
if (save) {
|
|
102
|
+
await this.markCompleted(skippedSections);
|
|
103
|
+
console.log(chalk_1.default.green('\n✅ Configuration saved successfully!'));
|
|
104
|
+
console.log(chalk_1.default.gray('You can re-run this wizard anytime with the "run_config_wizard" tool.\n'));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.log(chalk_1.default.yellow('\n⚠️ Configuration not saved. You can run the wizard again later.\n'));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error instanceof Error) {
|
|
112
|
+
if (error.message.includes('canceled')) {
|
|
113
|
+
console.log(chalk_1.default.yellow('\n⚠️ Wizard canceled. You can run it again later.\n'));
|
|
114
|
+
}
|
|
115
|
+
else if (error.message.includes('EACCES') || error.message.includes('permission')) {
|
|
116
|
+
logger_js_1.logger.error('Permission denied while saving configuration', { error });
|
|
117
|
+
console.log(chalk_1.default.red('\n❌ Permission denied: Unable to save configuration.'));
|
|
118
|
+
console.log(chalk_1.default.yellow(' Please check file permissions for ~/.dollhouse/config.yml\n'));
|
|
119
|
+
}
|
|
120
|
+
else if (error.message.includes('ENOSPC')) {
|
|
121
|
+
logger_js_1.logger.error('No space left on device', { error });
|
|
122
|
+
console.log(chalk_1.default.red('\n❌ Disk full: Unable to save configuration.'));
|
|
123
|
+
console.log(chalk_1.default.yellow(' Please free up some disk space and try again.\n'));
|
|
124
|
+
}
|
|
125
|
+
else if (error.message.includes('readline')) {
|
|
126
|
+
logger_js_1.logger.error('Terminal input error', { error });
|
|
127
|
+
console.log(chalk_1.default.red('\n❌ Terminal error: Unable to read input.'));
|
|
128
|
+
console.log(chalk_1.default.yellow(' Please ensure you\'re running in an interactive terminal.\n'));
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
logger_js_1.logger.error('Unexpected error in configuration wizard', { error });
|
|
132
|
+
console.log(chalk_1.default.red('\n❌ An unexpected error occurred.'));
|
|
133
|
+
console.log(chalk_1.default.gray(` Error: ${error.message}\n`));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
logger_js_1.logger.error('Unknown error in configuration wizard', { error });
|
|
138
|
+
console.log(chalk_1.default.red('\n❌ An unknown error occurred. Please try again later.\n'));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* User Identity Section
|
|
144
|
+
*/
|
|
145
|
+
async runUserIdentitySection() {
|
|
146
|
+
console.log('\n' + chalk_1.default.cyan.bold('=== User Identity Configuration ==='));
|
|
147
|
+
console.log('Let\'s set up your identity for persona attribution.\n');
|
|
148
|
+
const config = this.configManager.getConfig();
|
|
149
|
+
// Username
|
|
150
|
+
console.log(chalk_1.default.white('Username (for persona credits):'));
|
|
151
|
+
console.log(chalk_1.default.gray(` Current: ${config.user.username || 'not set'}`));
|
|
152
|
+
console.log(chalk_1.default.gray(' Recommended: Your GitHub username'));
|
|
153
|
+
const username = await this.prompt(' > ', config.user.username || '');
|
|
154
|
+
if (username) {
|
|
155
|
+
await this.configManager.updateSetting('user.username', username);
|
|
156
|
+
}
|
|
157
|
+
// Email with validation
|
|
158
|
+
console.log(chalk_1.default.white('\nEmail (for Git commits and GitHub attribution):'));
|
|
159
|
+
console.log(chalk_1.default.gray(' Used for: Git author info, GitHub commits, and element attribution'));
|
|
160
|
+
console.log(chalk_1.default.gray(` Current: ${config.user.email || 'not set'}`));
|
|
161
|
+
console.log(chalk_1.default.gray(' Recommended: Your GitHub email address'));
|
|
162
|
+
let email = await this.prompt(' > ', config.user.email || '');
|
|
163
|
+
// Validate email if provided
|
|
164
|
+
while (email && !this.isValidEmail(email)) {
|
|
165
|
+
console.log(chalk_1.default.yellow(' ⚠️ Invalid email format. Please enter a valid email or leave empty to skip.'));
|
|
166
|
+
email = await this.prompt(' > ', '');
|
|
167
|
+
}
|
|
168
|
+
if (email) {
|
|
169
|
+
await this.configManager.updateSetting('user.email', email);
|
|
170
|
+
}
|
|
171
|
+
// Display Name
|
|
172
|
+
console.log(chalk_1.default.white('\nDisplay Name (how you appear in the community):'));
|
|
173
|
+
console.log(chalk_1.default.gray(` Current: ${config.user.display_name || 'not set'}`));
|
|
174
|
+
const displayName = await this.prompt(' > ', config.user.display_name || '');
|
|
175
|
+
if (displayName) {
|
|
176
|
+
await this.configManager.updateSetting('user.display_name', displayName);
|
|
177
|
+
}
|
|
178
|
+
return false; // Section not skipped
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* GitHub Integration Section
|
|
182
|
+
*/
|
|
183
|
+
async runGitHubSection() {
|
|
184
|
+
console.log('\n' + chalk_1.default.cyan.bold('=== GitHub Integration ==='));
|
|
185
|
+
console.log('DollhouseMCP can sync your personas with GitHub for backup and sharing.\n');
|
|
186
|
+
const setupGitHub = await this.promptYesNo('Would you like to set up GitHub integration?', false);
|
|
187
|
+
if (!setupGitHub) {
|
|
188
|
+
console.log(chalk_1.default.gray(' Skipping GitHub setup. You can configure this later.\n'));
|
|
189
|
+
return true; // Section skipped
|
|
190
|
+
}
|
|
191
|
+
const config = this.configManager.getConfig();
|
|
192
|
+
// Repository name
|
|
193
|
+
console.log(chalk_1.default.white('\nGitHub Portfolio Repository:'));
|
|
194
|
+
console.log(chalk_1.default.gray(' This will store your personal collection'));
|
|
195
|
+
console.log(chalk_1.default.gray(' Format: username/repository-name'));
|
|
196
|
+
console.log(chalk_1.default.gray(` Default: ${config.github.portfolio.repository_name}`));
|
|
197
|
+
const repoName = await this.prompt(' > ', config.github.portfolio.repository_name);
|
|
198
|
+
if (repoName) {
|
|
199
|
+
await this.configManager.updateSetting('github.portfolio.repository_name', repoName);
|
|
200
|
+
}
|
|
201
|
+
// Auto-create repository
|
|
202
|
+
const autoCreate = await this.promptYesNo('\nAuto-create repository if it doesn\'t exist?', true);
|
|
203
|
+
await this.configManager.updateSetting('github.portfolio.auto_create', autoCreate);
|
|
204
|
+
// OAuth authentication
|
|
205
|
+
const useOAuth = await this.promptYesNo('\nUse OAuth for authentication? (More secure than tokens)', true);
|
|
206
|
+
await this.configManager.updateSetting('github.auth.use_oauth', useOAuth);
|
|
207
|
+
return false; // Section not skipped
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Sync Settings Section
|
|
211
|
+
*/
|
|
212
|
+
async runSyncSection() {
|
|
213
|
+
console.log('\n' + chalk_1.default.cyan.bold('=== Sync Settings ==='));
|
|
214
|
+
console.log('Control how your personas sync with GitHub.\n');
|
|
215
|
+
const enableSync = await this.promptYesNo('Enable sync features? (You can always enable later)', false);
|
|
216
|
+
await this.configManager.updateSetting('sync.enabled', enableSync);
|
|
217
|
+
if (!enableSync) {
|
|
218
|
+
console.log(chalk_1.default.gray(' Sync disabled. You can enable it later in settings.\n'));
|
|
219
|
+
return true; // Section skipped (partially)
|
|
220
|
+
}
|
|
221
|
+
// Individual sync settings
|
|
222
|
+
console.log(chalk_1.default.white('\nIndividual Sync Settings:'));
|
|
223
|
+
const requireConfirm = await this.promptYesNo(' Require confirmation before sync?', true);
|
|
224
|
+
await this.configManager.updateSetting('sync.individual.require_confirmation', requireConfirm);
|
|
225
|
+
const showDiff = await this.promptYesNo(' Show diff before syncing?', true);
|
|
226
|
+
await this.configManager.updateSetting('sync.individual.show_diff_before_sync', showDiff);
|
|
227
|
+
const trackVersions = await this.promptYesNo(' Keep version history?', true);
|
|
228
|
+
await this.configManager.updateSetting('sync.individual.track_versions', trackVersions);
|
|
229
|
+
if (trackVersions) {
|
|
230
|
+
const historyCount = await this.prompt(' How many versions to keep? [10]: ', '10');
|
|
231
|
+
await this.configManager.updateSetting('sync.individual.keep_history', parseInt(historyCount) || 10);
|
|
232
|
+
}
|
|
233
|
+
// Privacy settings
|
|
234
|
+
console.log(chalk_1.default.white('\nPrivacy Settings:'));
|
|
235
|
+
const scanSecrets = await this.promptYesNo(' Scan for secrets before upload?', true);
|
|
236
|
+
await this.configManager.updateSetting('sync.privacy.scan_for_secrets', scanSecrets);
|
|
237
|
+
const scanPII = await this.promptYesNo(' Scan for PII (personal info)?', true);
|
|
238
|
+
await this.configManager.updateSetting('sync.privacy.scan_for_pii', scanPII);
|
|
239
|
+
const warnSensitive = await this.promptYesNo(' Warn on sensitive content?', true);
|
|
240
|
+
await this.configManager.updateSetting('sync.privacy.warn_on_sensitive', warnSensitive);
|
|
241
|
+
return false; // Section not skipped
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Collection Settings Section
|
|
245
|
+
*/
|
|
246
|
+
async runCollectionSection() {
|
|
247
|
+
console.log('\n' + chalk_1.default.cyan.bold('=== Collection Settings ==='));
|
|
248
|
+
console.log('The DollhouseMCP Collection is our community marketplace.\n');
|
|
249
|
+
const autoSubmit = await this.promptYesNo('Auto-submit to collection when uploading?', false);
|
|
250
|
+
await this.configManager.updateSetting('collection.auto_submit', autoSubmit);
|
|
251
|
+
const requireReview = await this.promptYesNo('Require review before submission?', true);
|
|
252
|
+
await this.configManager.updateSetting('collection.require_review', requireReview);
|
|
253
|
+
const addAttribution = await this.promptYesNo('Add attribution to your personas? (Adds your username)', true);
|
|
254
|
+
await this.configManager.updateSetting('collection.add_attribution', addAttribution);
|
|
255
|
+
return false; // Section not skipped
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Display Settings Section
|
|
259
|
+
*/
|
|
260
|
+
async runDisplaySection() {
|
|
261
|
+
console.log('\n' + chalk_1.default.cyan.bold('=== Display Settings ==='));
|
|
262
|
+
console.log('How DollhouseMCP appears in your terminal.\n');
|
|
263
|
+
const showIndicators = await this.promptYesNo('Show persona indicators in responses?', true);
|
|
264
|
+
await this.configManager.updateSetting('display.persona_indicators.enabled', showIndicators);
|
|
265
|
+
if (showIndicators) {
|
|
266
|
+
console.log(chalk_1.default.white('\nIndicator Style:'));
|
|
267
|
+
console.log(' [1] Full - Complete information');
|
|
268
|
+
console.log(' [2] Minimal - Just the name');
|
|
269
|
+
console.log(' [3] Compact - Name and version');
|
|
270
|
+
console.log(' [4] Custom - Define your own format');
|
|
271
|
+
const styleChoice = await this.prompt(' Choice [1-4]: ', '2');
|
|
272
|
+
const styles = ['full', 'minimal', 'compact', 'custom'];
|
|
273
|
+
const style = styles[parseInt(styleChoice) - 1] || 'minimal';
|
|
274
|
+
await this.configManager.updateSetting('display.persona_indicators.style', style);
|
|
275
|
+
const includeEmoji = await this.promptYesNo('\n Include emoji in indicators?', true);
|
|
276
|
+
await this.configManager.updateSetting('display.persona_indicators.include_emoji', includeEmoji);
|
|
277
|
+
}
|
|
278
|
+
const verboseLogging = await this.promptYesNo('\nEnable verbose logging?', false);
|
|
279
|
+
await this.configManager.updateSetting('display.verbose_logging', verboseLogging);
|
|
280
|
+
const showProgress = await this.promptYesNo('Show progress indicators?', true);
|
|
281
|
+
await this.configManager.updateSetting('display.show_progress', showProgress);
|
|
282
|
+
return false; // Section not skipped
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Show configuration summary
|
|
286
|
+
*/
|
|
287
|
+
async showSummary() {
|
|
288
|
+
console.log('\n' + chalk_1.default.cyan.bold('=== Configuration Summary ==='));
|
|
289
|
+
console.log('Here\'s what we\'ll set up:\n');
|
|
290
|
+
const config = this.configManager.getConfig();
|
|
291
|
+
// User
|
|
292
|
+
if (config.user.username) {
|
|
293
|
+
console.log(chalk_1.default.green('✓') + ` Username: ${config.user.username}`);
|
|
294
|
+
}
|
|
295
|
+
if (config.user.email) {
|
|
296
|
+
console.log(chalk_1.default.green('✓') + ` Email: ${config.user.email}`);
|
|
297
|
+
}
|
|
298
|
+
// GitHub
|
|
299
|
+
if (config.github.portfolio.repository_name !== 'dollhouse-portfolio') {
|
|
300
|
+
console.log(chalk_1.default.green('✓') + ` GitHub: ${config.github.portfolio.repository_name}`);
|
|
301
|
+
}
|
|
302
|
+
// Sync
|
|
303
|
+
console.log(chalk_1.default.green('✓') + ` Sync: ${config.sync.enabled ? 'Enabled' : 'Disabled'}`);
|
|
304
|
+
// Collection
|
|
305
|
+
console.log(chalk_1.default.green('✓') + ` Collection: ${config.collection.auto_submit ? 'Auto-submit' : 'Manual submission'}`);
|
|
306
|
+
// Display
|
|
307
|
+
if (config.display.persona_indicators.enabled) {
|
|
308
|
+
console.log(chalk_1.default.green('✓') + ` Display: ${config.display.persona_indicators.style} indicators` +
|
|
309
|
+
(config.display.persona_indicators.include_emoji ? ' with emoji' : ''));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Mark wizard as completed
|
|
314
|
+
*/
|
|
315
|
+
async markCompleted(skippedSections = []) {
|
|
316
|
+
await this.configManager.updateSetting('wizard.completed', true);
|
|
317
|
+
await this.configManager.updateSetting('wizard.completedAt', new Date().toISOString());
|
|
318
|
+
await this.configManager.updateSetting('wizard.version', WIZARD_VERSION);
|
|
319
|
+
if (skippedSections.length > 0) {
|
|
320
|
+
await this.configManager.updateSetting('wizard.skippedSections', skippedSections);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Mark wizard as dismissed
|
|
325
|
+
*/
|
|
326
|
+
async markDismissed() {
|
|
327
|
+
await this.configManager.updateSetting('wizard.dismissed', true);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Helper: Prompt for user input
|
|
331
|
+
*/
|
|
332
|
+
prompt(question, defaultValue = '') {
|
|
333
|
+
return new Promise((resolve) => {
|
|
334
|
+
if (!this.isInteractive || !this.rl) {
|
|
335
|
+
resolve(defaultValue);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
this.rl.question(question, (answer) => {
|
|
339
|
+
resolve(answer || defaultValue);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Helper: Prompt for yes/no
|
|
345
|
+
*/
|
|
346
|
+
async promptYesNo(question, defaultYes = true) {
|
|
347
|
+
const hint = defaultYes ? '[Y/n]' : '[y/N]';
|
|
348
|
+
const answer = await this.prompt(`${question} ${hint}: `, defaultYes ? 'y' : 'n');
|
|
349
|
+
return answer.toLowerCase().startsWith('y');
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Helper: Validate email format
|
|
353
|
+
*/
|
|
354
|
+
isValidEmail(email) {
|
|
355
|
+
// Basic email regex - not perfect but good enough for wizard validation
|
|
356
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
357
|
+
return emailRegex.test(email);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Cleanup readline interface
|
|
361
|
+
*/
|
|
362
|
+
close() {
|
|
363
|
+
if (this.isInteractive && this.rl) {
|
|
364
|
+
this.rl.close();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
exports.ConfigWizard = ConfigWizard;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security-specific error class for DollhouseMCP
|
|
4
|
+
*
|
|
5
|
+
* Used to indicate security violations, validation failures,
|
|
6
|
+
* and other security-related issues.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.SecurityError = void 0;
|
|
10
|
+
class SecurityError extends Error {
|
|
11
|
+
constructor(message, code = 'SECURITY_VIOLATION', severity = 'high', details) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'SecurityError';
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.severity = severity;
|
|
16
|
+
this.details = details;
|
|
17
|
+
// Maintains proper stack trace for where our error was thrown
|
|
18
|
+
if (Error.captureStackTrace) {
|
|
19
|
+
Error.captureStackTrace(this, SecurityError);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a SecurityError for content validation failures
|
|
24
|
+
*/
|
|
25
|
+
static contentValidation(message, patterns) {
|
|
26
|
+
return new SecurityError(message, 'CONTENT_VALIDATION_FAILED', 'high', { detectedPatterns: patterns });
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates a SecurityError for YAML injection attempts
|
|
30
|
+
*/
|
|
31
|
+
static yamlInjection(message) {
|
|
32
|
+
return new SecurityError(message, 'YAML_INJECTION_DETECTED', 'critical');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a SecurityError for path traversal attempts
|
|
36
|
+
*/
|
|
37
|
+
static pathTraversal(message, path) {
|
|
38
|
+
return new SecurityError(message, 'PATH_TRAVERSAL_DETECTED', 'high', { attemptedPath: path });
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates a SecurityError for token validation failures
|
|
42
|
+
*/
|
|
43
|
+
static tokenValidation(message) {
|
|
44
|
+
return new SecurityError(message, 'TOKEN_VALIDATION_FAILED', 'high');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.SecurityError = SecurityError;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security-related constants and limits
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.VALIDATION_PATTERNS = exports.SECURITY_LIMITS = void 0;
|
|
7
|
+
// Security and performance limits
|
|
8
|
+
exports.SECURITY_LIMITS = {
|
|
9
|
+
MAX_PERSONA_SIZE_BYTES: 1024 * 1024 * 2, // 2MB max persona file size
|
|
10
|
+
MAX_FILENAME_LENGTH: 255, // Max filename length
|
|
11
|
+
MAX_PATH_DEPTH: 10, // Max directory depth for paths
|
|
12
|
+
MAX_CONTENT_LENGTH: 500000, // Max persona content length (500KB)
|
|
13
|
+
MAX_YAML_LENGTH: 64 * 1024, // Max YAML frontmatter length (64KB)
|
|
14
|
+
MAX_METADATA_FIELD_LENGTH: 1024, // Max individual metadata field length (1KB)
|
|
15
|
+
MAX_FILE_SIZE: 1024 * 1024 * 2, // Max file size (2MB)
|
|
16
|
+
RATE_LIMIT_REQUESTS: 100, // Max requests per window
|
|
17
|
+
RATE_LIMIT_WINDOW_MS: 60 * 1000, // 1 minute window
|
|
18
|
+
CACHE_TTL_MS: 5 * 60 * 1000, // 5 minute cache TTL
|
|
19
|
+
MAX_SEARCH_RESULTS: 50 // Max search results to return
|
|
20
|
+
};
|
|
21
|
+
// Input validation patterns
|
|
22
|
+
exports.VALIDATION_PATTERNS = {
|
|
23
|
+
SAFE_FILENAME: /^[a-zA-Z0-9][a-zA-Z0-9\-_.]{0,250}[a-zA-Z0-9]$/,
|
|
24
|
+
SAFE_PATH: /^[a-zA-Z0-9:/\-_.~]{1,500}$/,
|
|
25
|
+
SAFE_USERNAME: /^[a-zA-Z0-9][a-zA-Z0-9\-_.]{0,30}[a-zA-Z0-9]$/,
|
|
26
|
+
SAFE_CATEGORY: /^[a-zA-Z][a-zA-Z0-9\-_]{0,20}$/,
|
|
27
|
+
SAFE_EMAIL: /^[^\s@]{1,64}@[^\s@]{1,253}\.[^\s@]{1,63}$/ // RFC 5321 compliant limits
|
|
28
|
+
};
|