@dollhousemcp/mcp-server 1.5.2 → 1.6.0
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/CHANGELOG.md +56 -0
- package/README.md +494 -111
- package/data/agents/code-reviewer.md +8 -1
- package/data/agents/research-assistant.md +8 -1
- package/data/agents/task-manager.md +8 -1
- package/data/ensembles/business-advisor.md +8 -1
- package/data/ensembles/creative-studio.md +8 -1
- package/data/ensembles/development-team.md +8 -1
- package/data/ensembles/security-analysis-team.md +8 -1
- package/data/memories/conversation-history.md +8 -1
- package/data/memories/learning-progress.md +8 -1
- package/data/memories/project-context.md +8 -1
- package/data/personas/business-consultant.md +8 -1
- package/data/personas/creative-writer.md +8 -1
- package/data/personas/debug-detective.md +8 -1
- package/data/personas/eli5-explainer.md +8 -1
- package/data/personas/security-analyst.md +8 -1
- package/data/personas/technical-analyst.md +8 -1
- package/data/skills/code-review.md +8 -1
- package/data/skills/creative-writing.md +8 -1
- package/data/skills/data-analysis.md +8 -1
- package/data/skills/penetration-testing.md +8 -1
- package/data/skills/research.md +8 -1
- package/data/skills/threat-modeling.md +8 -1
- package/data/skills/translation.md +8 -1
- package/data/templates/code-documentation.md +8 -1
- package/data/templates/email-professional.md +8 -1
- package/data/templates/meeting-notes.md +8 -1
- package/data/templates/penetration-test-report.md +8 -1
- package/data/templates/project-brief.md +8 -1
- package/data/templates/report-executive.md +8 -1
- package/data/templates/security-vulnerability-report.md +8 -1
- package/data/templates/threat-assessment-report.md +8 -1
- package/dist/auth/GitHubAuthManager.d.ts +6 -1
- package/dist/auth/GitHubAuthManager.d.ts.map +1 -1
- package/dist/auth/GitHubAuthManager.js +45 -18
- package/dist/benchmarks/IndexPerformanceBenchmark.d.ts +98 -0
- package/dist/benchmarks/IndexPerformanceBenchmark.d.ts.map +1 -0
- package/dist/benchmarks/IndexPerformanceBenchmark.js +531 -0
- package/dist/cache/CollectionCache.d.ts.map +1 -1
- package/dist/cache/CollectionCache.js +13 -3
- package/dist/cache/CollectionIndexCache.d.ts +77 -0
- package/dist/cache/CollectionIndexCache.d.ts.map +1 -0
- package/dist/cache/CollectionIndexCache.js +349 -0
- package/dist/cache/LRUCache.d.ts +93 -0
- package/dist/cache/LRUCache.d.ts.map +1 -0
- package/dist/cache/LRUCache.js +299 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +2 -1
- package/dist/collection/CollectionBrowser.d.ts +21 -1
- package/dist/collection/CollectionBrowser.d.ts.map +1 -1
- package/dist/collection/CollectionBrowser.js +130 -10
- package/dist/collection/CollectionIndexManager.d.ts +151 -0
- package/dist/collection/CollectionIndexManager.d.ts.map +1 -0
- package/dist/collection/CollectionIndexManager.js +499 -0
- package/dist/collection/CollectionSearch.d.ts +55 -0
- package/dist/collection/CollectionSearch.d.ts.map +1 -1
- package/dist/collection/CollectionSearch.js +338 -13
- package/dist/collection/CollectionSeeder.d.ts.map +1 -1
- package/dist/collection/CollectionSeeder.js +38 -1
- package/dist/collection/ElementInstaller.d.ts +31 -0
- package/dist/collection/ElementInstaller.d.ts.map +1 -1
- package/dist/collection/ElementInstaller.js +77 -15
- package/dist/collection/PersonaSubmitter.d.ts +1 -1
- package/dist/collection/PersonaSubmitter.d.ts.map +1 -1
- package/dist/collection/PersonaSubmitter.js +2 -2
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.d.ts.map +1 -1
- package/dist/collection/index.js +2 -1
- package/dist/config/ConfigManager.d.ts +78 -0
- package/dist/config/ConfigManager.d.ts.map +1 -0
- package/dist/config/ConfigManager.js +216 -0
- package/dist/config/element-types.d.ts +135 -0
- package/dist/config/element-types.d.ts.map +1 -0
- package/dist/config/element-types.js +108 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +3 -1
- package/dist/config/portfolio-constants.d.ts +83 -0
- package/dist/config/portfolio-constants.d.ts.map +1 -0
- package/dist/config/portfolio-constants.js +99 -0
- package/dist/elements/BaseElement.d.ts +14 -2
- package/dist/elements/BaseElement.d.ts.map +1 -1
- package/dist/elements/BaseElement.js +88 -6
- package/dist/elements/agents/Agent.d.ts +10 -1
- package/dist/elements/agents/Agent.d.ts.map +1 -1
- package/dist/elements/agents/Agent.js +66 -19
- package/dist/elements/agents/AgentManager.d.ts +2 -0
- package/dist/elements/agents/AgentManager.d.ts.map +1 -1
- package/dist/elements/agents/AgentManager.js +12 -10
- package/dist/elements/skills/Skill.d.ts +10 -1
- package/dist/elements/skills/Skill.d.ts.map +1 -1
- package/dist/elements/skills/Skill.js +40 -3
- package/dist/elements/skills/SkillManager.d.ts +1 -0
- package/dist/elements/skills/SkillManager.d.ts.map +1 -1
- package/dist/elements/skills/SkillManager.js +10 -4
- package/dist/elements/templates/Template.d.ts +10 -1
- package/dist/elements/templates/Template.d.ts.map +1 -1
- package/dist/elements/templates/Template.js +35 -18
- package/dist/elements/templates/TemplateManager.d.ts +1 -1
- package/dist/elements/templates/TemplateManager.d.ts.map +1 -1
- package/dist/elements/templates/TemplateManager.js +6 -5
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/index.barrel.d.ts +1 -2
- package/dist/index.barrel.d.ts.map +1 -1
- package/dist/index.barrel.js +2 -4
- package/dist/index.d.ts +143 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1883 -310
- package/dist/persona/PersonaElement.d.ts +10 -0
- package/dist/persona/PersonaElement.d.ts.map +1 -1
- package/dist/persona/PersonaElement.js +55 -32
- package/dist/persona/PersonaElementManager.d.ts.map +1 -1
- package/dist/persona/PersonaElementManager.js +13 -11
- package/dist/persona/PersonaLoader.d.ts.map +1 -1
- package/dist/persona/PersonaLoader.js +8 -2
- package/dist/persona/export-import/PersonaImporter.d.ts.map +1 -1
- package/dist/persona/export-import/PersonaImporter.js +24 -5
- package/dist/persona/export-import/PersonaSharer.d.ts +21 -0
- package/dist/persona/export-import/PersonaSharer.d.ts.map +1 -1
- package/dist/persona/export-import/PersonaSharer.js +198 -22
- package/dist/portfolio/DefaultElementProvider.d.ts +90 -0
- package/dist/portfolio/DefaultElementProvider.d.ts.map +1 -1
- package/dist/portfolio/DefaultElementProvider.js +499 -7
- package/dist/portfolio/GitHubPortfolioIndexer.d.ts +129 -0
- package/dist/portfolio/GitHubPortfolioIndexer.d.ts.map +1 -0
- package/dist/portfolio/GitHubPortfolioIndexer.js +475 -0
- package/dist/portfolio/MigrationManager.d.ts.map +1 -1
- package/dist/portfolio/MigrationManager.js +136 -3
- package/dist/portfolio/PortfolioIndexManager.d.ts +130 -0
- package/dist/portfolio/PortfolioIndexManager.d.ts.map +1 -0
- package/dist/portfolio/PortfolioIndexManager.js +478 -0
- package/dist/portfolio/PortfolioManager.d.ts +5 -0
- package/dist/portfolio/PortfolioManager.d.ts.map +1 -1
- package/dist/portfolio/PortfolioManager.js +61 -20
- package/dist/portfolio/PortfolioRepoManager.d.ts +75 -0
- package/dist/portfolio/PortfolioRepoManager.d.ts.map +1 -0
- package/dist/portfolio/PortfolioRepoManager.js +337 -0
- package/dist/portfolio/UnifiedIndexManager.d.ts +388 -0
- package/dist/portfolio/UnifiedIndexManager.d.ts.map +1 -0
- package/dist/portfolio/UnifiedIndexManager.js +1434 -0
- package/dist/portfolio/index.d.ts +15 -0
- package/dist/portfolio/index.d.ts.map +1 -0
- package/dist/portfolio/index.js +15 -0
- package/dist/portfolio/types.d.ts +7 -0
- package/dist/portfolio/types.d.ts.map +1 -1
- package/dist/portfolio/types.js +6 -1
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +50 -48
- package/dist/security/audit/SecurityAuditor.d.ts.map +1 -1
- package/dist/security/audit/SecurityAuditor.js +17 -9
- package/dist/security/audit/config/suppressions.d.ts.map +1 -1
- package/dist/security/audit/config/suppressions.js +19 -3
- package/dist/security/contentValidator.d.ts +2 -0
- package/dist/security/contentValidator.d.ts.map +1 -1
- package/dist/security/contentValidator.js +115 -4
- package/dist/security/secureYamlParser.d.ts +1 -0
- package/dist/security/secureYamlParser.d.ts.map +1 -1
- package/dist/security/secureYamlParser.js +29 -7
- package/dist/security/securityMonitor.d.ts +1 -1
- package/dist/security/securityMonitor.d.ts.map +1 -1
- package/dist/security/securityMonitor.js +1 -1
- package/dist/security/tokenManager.d.ts +1 -1
- package/dist/security/tokenManager.d.ts.map +1 -1
- package/dist/security/tokenManager.js +30 -10
- package/dist/server/ServerSetup.d.ts +22 -2
- package/dist/server/ServerSetup.d.ts.map +1 -1
- package/dist/server/ServerSetup.js +77 -12
- package/dist/server/tools/AuthTools.d.ts.map +1 -1
- package/dist/server/tools/AuthTools.js +33 -1
- package/dist/server/tools/BuildInfoTools.d.ts +25 -0
- package/dist/server/tools/BuildInfoTools.d.ts.map +1 -0
- package/dist/server/tools/BuildInfoTools.js +36 -0
- package/dist/server/tools/CollectionTools.d.ts.map +1 -1
- package/dist/server/tools/CollectionTools.js +55 -46
- package/dist/server/tools/ConfigTools.d.ts.map +1 -1
- package/dist/server/tools/ConfigTools.js +29 -1
- package/dist/server/tools/PersonaTools.d.ts +4 -2
- package/dist/server/tools/PersonaTools.d.ts.map +1 -1
- package/dist/server/tools/PersonaTools.js +5 -152
- package/dist/server/tools/PortfolioTools.d.ts +12 -0
- package/dist/server/tools/PortfolioTools.d.ts.map +1 -0
- package/dist/server/tools/PortfolioTools.js +221 -0
- package/dist/server/tools/index.d.ts +3 -1
- package/dist/server/tools/index.d.ts.map +1 -1
- package/dist/server/tools/index.js +4 -2
- package/dist/server/types.d.ts +40 -5
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +1 -1
- package/dist/services/BuildInfoService.d.ts +84 -0
- package/dist/services/BuildInfoService.d.ts.map +1 -0
- package/dist/services/BuildInfoService.js +271 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.d.ts +54 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.d.ts.map +1 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.js +229 -0
- package/dist/tools/portfolio/submitToPortfolioTool.d.ts +164 -0
- package/dist/tools/portfolio/submitToPortfolioTool.d.ts.map +1 -0
- package/dist/tools/portfolio/submitToPortfolioTool.js +1523 -0
- package/dist/tools/portfolio/types.d.ts +41 -0
- package/dist/tools/portfolio/types.d.ts.map +1 -0
- package/dist/tools/portfolio/types.js +15 -0
- package/dist/types/collection.d.ts +51 -0
- package/dist/types/collection.d.ts.map +1 -1
- package/dist/types/collection.js +1 -1
- package/dist/utils/EarlyTerminationSearch.d.ts +41 -0
- package/dist/utils/EarlyTerminationSearch.d.ts.map +1 -0
- package/dist/utils/EarlyTerminationSearch.js +164 -0
- package/dist/utils/ErrorHandler.d.ts +86 -0
- package/dist/utils/ErrorHandler.d.ts.map +1 -0
- package/dist/utils/ErrorHandler.js +201 -0
- package/dist/utils/FileDiscoveryUtil.d.ts +53 -0
- package/dist/utils/FileDiscoveryUtil.d.ts.map +1 -0
- package/dist/utils/FileDiscoveryUtil.js +169 -0
- package/dist/utils/GitHubRateLimiter.d.ts +88 -0
- package/dist/utils/GitHubRateLimiter.d.ts.map +1 -0
- package/dist/utils/GitHubRateLimiter.js +315 -0
- package/dist/utils/PerformanceMonitor.d.ts +134 -0
- package/dist/utils/PerformanceMonitor.d.ts.map +1 -0
- package/dist/utils/PerformanceMonitor.js +347 -0
- package/dist/utils/RateLimiter.d.ts.map +1 -0
- package/dist/utils/RateLimiter.js +172 -0
- package/dist/utils/SecureDownloader.d.ts +241 -0
- package/dist/utils/SecureDownloader.d.ts.map +1 -0
- package/dist/utils/SecureDownloader.js +759 -0
- package/dist/utils/ToolCache.d.ts +82 -0
- package/dist/utils/ToolCache.d.ts.map +1 -0
- package/dist/utils/ToolCache.js +196 -0
- package/dist/utils/errorCodes.d.ts +136 -0
- package/dist/utils/errorCodes.d.ts.map +1 -0
- package/dist/utils/errorCodes.js +87 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -1
- package/dist/utils/installation.d.ts +1 -1
- package/dist/utils/installation.d.ts.map +1 -1
- package/dist/utils/installation.js +9 -8
- package/dist/utils/searchUtils.d.ts +31 -0
- package/dist/utils/searchUtils.d.ts.map +1 -1
- package/dist/utils/searchUtils.js +62 -1
- package/package.json +17 -7
- package/dist/config/updateConfig.d.ts +0 -84
- package/dist/config/updateConfig.d.ts.map +0 -1
- package/dist/config/updateConfig.js +0 -148
- package/dist/server/tools/UpdateTools.d.ts +0 -10
- package/dist/server/tools/UpdateTools.d.ts.map +0 -1
- package/dist/server/tools/UpdateTools.js +0 -85
- package/dist/update/BackupManager.d.ts +0 -63
- package/dist/update/BackupManager.d.ts.map +0 -1
- package/dist/update/BackupManager.js +0 -370
- package/dist/update/DependencyChecker.d.ts +0 -41
- package/dist/update/DependencyChecker.d.ts.map +0 -1
- package/dist/update/DependencyChecker.js +0 -132
- package/dist/update/RateLimiter.d.ts.map +0 -1
- package/dist/update/RateLimiter.js +0 -172
- package/dist/update/SignatureVerifier.d.ts +0 -71
- package/dist/update/SignatureVerifier.d.ts.map +0 -1
- package/dist/update/SignatureVerifier.js +0 -214
- package/dist/update/UpdateChecker.d.ts +0 -132
- package/dist/update/UpdateChecker.d.ts.map +0 -1
- package/dist/update/UpdateChecker.js +0 -506
- package/dist/update/UpdateManager.d.ts +0 -60
- package/dist/update/UpdateManager.d.ts.map +0 -1
- package/dist/update/UpdateManager.js +0 -730
- package/dist/update/VersionManager.d.ts +0 -31
- package/dist/update/VersionManager.d.ts.map +0 -1
- package/dist/update/VersionManager.js +0 -181
- package/dist/update/index.d.ts +0 -9
- package/dist/update/index.d.ts.map +0 -1
- package/dist/update/index.js +0 -9
- /package/dist/{update → utils}/RateLimiter.d.ts +0 -0
|
@@ -1,730 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Manage server updates and rollbacks
|
|
3
|
-
*/
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import * as fs from 'fs/promises';
|
|
6
|
-
import { safeExec } from '../utils/git.js';
|
|
7
|
-
import { VersionManager } from './VersionManager.js';
|
|
8
|
-
import { UpdateChecker } from './UpdateChecker.js';
|
|
9
|
-
import { DependencyChecker } from './DependencyChecker.js';
|
|
10
|
-
import { BackupManager } from './BackupManager.js';
|
|
11
|
-
import { InstallationDetector } from '../utils/installation.js';
|
|
12
|
-
import { logger } from '../utils/logger.js';
|
|
13
|
-
import { compareVersions } from '../utils/version.js';
|
|
14
|
-
import { FileOperations } from '../utils/fileOperations.js';
|
|
15
|
-
import { UpdateConfigManager } from '../config/updateConfig.js';
|
|
16
|
-
export class UpdateManager {
|
|
17
|
-
versionManager;
|
|
18
|
-
updateChecker;
|
|
19
|
-
dependencyChecker;
|
|
20
|
-
backupManager;
|
|
21
|
-
rootDir;
|
|
22
|
-
constructor(rootDir) {
|
|
23
|
-
this.rootDir = rootDir || process.cwd();
|
|
24
|
-
this.versionManager = new VersionManager();
|
|
25
|
-
this.updateChecker = new UpdateChecker(this.versionManager);
|
|
26
|
-
this.dependencyChecker = new DependencyChecker(this.versionManager);
|
|
27
|
-
this.backupManager = new BackupManager(this.rootDir);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Check for available updates
|
|
31
|
-
*/
|
|
32
|
-
async checkForUpdates() {
|
|
33
|
-
try {
|
|
34
|
-
const result = await this.updateChecker.checkForUpdates();
|
|
35
|
-
const text = this.updateChecker.formatUpdateCheckResult(result);
|
|
36
|
-
return { text };
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
const text = this.updateChecker.formatUpdateCheckResult(null, error);
|
|
40
|
-
return { text };
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Perform server update
|
|
45
|
-
*/
|
|
46
|
-
async updateServer(createBackup = true, personaIndicator = '') {
|
|
47
|
-
const progress = [];
|
|
48
|
-
try {
|
|
49
|
-
// Detect installation type
|
|
50
|
-
const installationType = InstallationDetector.getInstallationType();
|
|
51
|
-
logger.info(`[UpdateManager] Detected installation type: ${installationType}`);
|
|
52
|
-
// Handle npm installations differently
|
|
53
|
-
if (installationType === 'npm') {
|
|
54
|
-
return this.updateNpmInstallation(createBackup, personaIndicator);
|
|
55
|
-
}
|
|
56
|
-
// For git installations, proceed with existing logic
|
|
57
|
-
// Step 1: Check dependencies
|
|
58
|
-
progress.push({ step: 'dependencies', message: 'Checking system dependencies...', isComplete: false });
|
|
59
|
-
const dependencies = await this.dependencyChecker.checkDependencies();
|
|
60
|
-
if (!dependencies.git.installed || dependencies.git.error) {
|
|
61
|
-
return {
|
|
62
|
-
text: personaIndicator + '❌ **Update Failed**\n\n' +
|
|
63
|
-
'Git is required for updates but is not available.\n' +
|
|
64
|
-
dependencies.git.error || 'Git is not installed.'
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
if (!dependencies.npm.installed || dependencies.npm.error) {
|
|
68
|
-
return {
|
|
69
|
-
text: personaIndicator + '❌ **Update Failed**\n\n' +
|
|
70
|
-
'npm is required for updates but is not available.\n' +
|
|
71
|
-
dependencies.npm.error || 'npm is not installed.'
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
progress[0].isComplete = true;
|
|
75
|
-
// Step 2: Create backup if requested
|
|
76
|
-
if (createBackup) {
|
|
77
|
-
progress.push({ step: 'backup', message: 'Creating backup...', isComplete: false });
|
|
78
|
-
const currentVersion = await this.versionManager.getCurrentVersion();
|
|
79
|
-
const backup = await this.backupManager.createBackup(currentVersion);
|
|
80
|
-
progress[1].isComplete = true;
|
|
81
|
-
progress[1].message = `Backup created at: ${backup.timestamp}`;
|
|
82
|
-
}
|
|
83
|
-
// Step 3: Git fetch
|
|
84
|
-
progress.push({ step: 'fetch', message: 'Fetching latest changes...', isComplete: false });
|
|
85
|
-
await safeExec('git', ['fetch', 'origin'], { cwd: this.rootDir });
|
|
86
|
-
progress[progress.length - 1].isComplete = true;
|
|
87
|
-
// Step 4: Check for uncommitted changes
|
|
88
|
-
progress.push({ step: 'check', message: 'Checking for uncommitted changes...', isComplete: false });
|
|
89
|
-
const { stdout: statusOutput } = await safeExec('git', ['status', '--porcelain'], { cwd: this.rootDir });
|
|
90
|
-
if (statusOutput.trim()) {
|
|
91
|
-
return {
|
|
92
|
-
text: personaIndicator + '❌ **Update Failed**\n\n' +
|
|
93
|
-
'You have uncommitted changes. Please commit or stash them before updating.\n\n' +
|
|
94
|
-
'Modified files:\n' + statusOutput
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
progress[progress.length - 1].isComplete = true;
|
|
98
|
-
// Step 5: Git pull
|
|
99
|
-
progress.push({ step: 'pull', message: 'Pulling latest changes...', isComplete: false });
|
|
100
|
-
const { stdout: pullOutput } = await safeExec('git', ['pull', 'origin', 'main'], { cwd: this.rootDir });
|
|
101
|
-
progress[progress.length - 1].isComplete = true;
|
|
102
|
-
// Check if already up to date
|
|
103
|
-
if (pullOutput.includes('Already up to date')) {
|
|
104
|
-
return {
|
|
105
|
-
text: personaIndicator + '✅ **Already Up to Date**\n\n' +
|
|
106
|
-
'Your DollhouseMCP installation is already at the latest version.\n\n' +
|
|
107
|
-
'No changes were pulled from the repository.'
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
// Step 6: npm install
|
|
111
|
-
progress.push({ step: 'install', message: 'Installing dependencies...', isComplete: false });
|
|
112
|
-
await safeExec('npm', ['install'], { cwd: this.rootDir });
|
|
113
|
-
progress[progress.length - 1].isComplete = true;
|
|
114
|
-
// Step 7: Build
|
|
115
|
-
progress.push({ step: 'build', message: 'Building TypeScript...', isComplete: false });
|
|
116
|
-
await safeExec('npm', ['run', 'build'], { cwd: this.rootDir });
|
|
117
|
-
progress[progress.length - 1].isComplete = true;
|
|
118
|
-
// Step 8: Cleanup old backups
|
|
119
|
-
if (createBackup) {
|
|
120
|
-
progress.push({ step: 'cleanup', message: 'Cleaning up old backups...', isComplete: false });
|
|
121
|
-
const deletedCount = await this.backupManager.cleanupOldBackups();
|
|
122
|
-
progress[progress.length - 1].isComplete = true;
|
|
123
|
-
progress[progress.length - 1].message = `Cleaned up ${deletedCount} old backup(s)`;
|
|
124
|
-
}
|
|
125
|
-
// Format success message
|
|
126
|
-
const successParts = [
|
|
127
|
-
personaIndicator + '✅ **Update Complete!**\n\n',
|
|
128
|
-
'**Update Summary:**\n'
|
|
129
|
-
];
|
|
130
|
-
progress.forEach(p => {
|
|
131
|
-
successParts.push(`${p.isComplete ? '✅' : '❌'} ${p.message}\n`);
|
|
132
|
-
});
|
|
133
|
-
successParts.push('\n**Next Steps:**\n', '1. The server will restart automatically\n', '2. All personas will be reloaded\n', '3. Check `get_server_status` to verify the new version\n\n', '💡 **Tip:** If you encounter issues, use `rollback_update true` to restore the previous version.');
|
|
134
|
-
return { text: successParts.join('') };
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
138
|
-
return {
|
|
139
|
-
text: personaIndicator + '❌ **Update Failed**\n\n' +
|
|
140
|
-
'Error: ' + errorMessage + '\n\n' +
|
|
141
|
-
'**Progress:**\n' +
|
|
142
|
-
progress.map(p => `${p.isComplete ? '✅' : '❌'} ${p.message}`).join('\n') + '\n\n' +
|
|
143
|
-
'**Recovery Options:**\n' +
|
|
144
|
-
'• Try running the update again\n' +
|
|
145
|
-
'• Check your internet connection\n' +
|
|
146
|
-
'• Ensure you have proper permissions\n' +
|
|
147
|
-
'• If a backup was created, use `rollback_update true` to restore'
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Rollback to previous version
|
|
153
|
-
*/
|
|
154
|
-
async rollbackUpdate(force = false, personaIndicator = '') {
|
|
155
|
-
try {
|
|
156
|
-
// Check installation type
|
|
157
|
-
const installationType = InstallationDetector.getInstallationType();
|
|
158
|
-
if (installationType === 'npm') {
|
|
159
|
-
return this.rollbackNpmInstallation(force, personaIndicator);
|
|
160
|
-
}
|
|
161
|
-
// For git installations, use existing logic
|
|
162
|
-
// Get latest backup
|
|
163
|
-
const latestBackup = await this.backupManager.getLatestBackup();
|
|
164
|
-
if (!latestBackup) {
|
|
165
|
-
return {
|
|
166
|
-
text: personaIndicator + '❌ **No Backups Found**\n\n' +
|
|
167
|
-
'There are no backups available to restore.\n\n' +
|
|
168
|
-
'Backups are created automatically when you run `update_server true`.'
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
// Check if rollback is needed
|
|
172
|
-
if (!force) {
|
|
173
|
-
try {
|
|
174
|
-
// Test if the server is working by checking version
|
|
175
|
-
await this.versionManager.getCurrentVersion();
|
|
176
|
-
return {
|
|
177
|
-
text: personaIndicator + '⚠️ **Rollback Confirmation Required**\n\n' +
|
|
178
|
-
'The server appears to be working normally.\n\n' +
|
|
179
|
-
`**Latest Backup:** ${latestBackup.timestamp}\n` +
|
|
180
|
-
`**Backup Version:** ${latestBackup.version || 'Unknown'}\n\n` +
|
|
181
|
-
'To force rollback anyway, use: `rollback_update true`\n\n' +
|
|
182
|
-
'⚠️ **Warning:** This will restore all files to the backup state.'
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
// Server is broken, proceed with rollback
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// Perform rollback
|
|
190
|
-
await this.backupManager.restoreBackup(latestBackup.path);
|
|
191
|
-
// Reinstall dependencies
|
|
192
|
-
await safeExec('npm', ['install'], { cwd: this.rootDir });
|
|
193
|
-
// Rebuild
|
|
194
|
-
await safeExec('npm', ['run', 'build'], { cwd: this.rootDir });
|
|
195
|
-
return {
|
|
196
|
-
text: personaIndicator + '✅ **Rollback Complete!**\n\n' +
|
|
197
|
-
`Restored from backup: ${latestBackup.timestamp}\n` +
|
|
198
|
-
`Backup version: ${latestBackup.version || 'Unknown'}\n\n` +
|
|
199
|
-
'**What was restored:**\n' +
|
|
200
|
-
'• All source files\n' +
|
|
201
|
-
'• Configuration files\n' +
|
|
202
|
-
'• Dependencies reinstalled\n' +
|
|
203
|
-
'• TypeScript rebuilt\n\n' +
|
|
204
|
-
'**Next Steps:**\n' +
|
|
205
|
-
'1. The server will restart automatically\n' +
|
|
206
|
-
'2. Check `get_server_status` to verify the version\n' +
|
|
207
|
-
'3. Test your personas to ensure everything works'
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
catch (error) {
|
|
211
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
212
|
-
return {
|
|
213
|
-
text: personaIndicator + '❌ **Rollback Failed**\n\n' +
|
|
214
|
-
'Error: ' + errorMessage + '\n\n' +
|
|
215
|
-
'**Manual Recovery:**\n' +
|
|
216
|
-
'1. Check the backups directory: ../dollhousemcp-backups/\n' +
|
|
217
|
-
'2. Manually restore files if needed\n' +
|
|
218
|
-
'3. Run `npm install` and `npm run build`\n' +
|
|
219
|
-
'4. Contact support if issues persist'
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Update npm installation
|
|
225
|
-
*/
|
|
226
|
-
async updateNpmInstallation(createBackup, personaIndicator = '') {
|
|
227
|
-
try {
|
|
228
|
-
logger.info('[UpdateManager] Starting npm update process');
|
|
229
|
-
// Check npm is available
|
|
230
|
-
const dependencies = await this.dependencyChecker.checkDependencies();
|
|
231
|
-
if (!dependencies.npm.installed || dependencies.npm.error) {
|
|
232
|
-
return {
|
|
233
|
-
text: personaIndicator + '❌ **Update Failed**\n\n' +
|
|
234
|
-
'npm is required for updates but is not available.\n' +
|
|
235
|
-
dependencies.npm.error || 'npm is not installed.'
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
// Get current version
|
|
239
|
-
const currentVersion = await this.versionManager.getCurrentVersion();
|
|
240
|
-
logger.info(`[UpdateManager] Current version: ${currentVersion}`);
|
|
241
|
-
// Check latest version from npm registry
|
|
242
|
-
logger.info('[UpdateManager] Checking npm registry for latest version');
|
|
243
|
-
// Security: Validate package name to prevent any potential injection
|
|
244
|
-
const packageName = '@dollhousemcp/mcp-server';
|
|
245
|
-
if (!/^@[a-z0-9-]+\/[a-z0-9-]+$/.test(packageName)) {
|
|
246
|
-
throw new Error('Invalid package name format');
|
|
247
|
-
}
|
|
248
|
-
const { stdout: npmViewOutput } = await safeExec('npm', ['view', packageName, 'version'], {
|
|
249
|
-
cwd: this.rootDir,
|
|
250
|
-
timeout: 30000
|
|
251
|
-
});
|
|
252
|
-
const latestVersion = npmViewOutput.trim();
|
|
253
|
-
logger.info(`[UpdateManager] Latest npm version: ${latestVersion}`);
|
|
254
|
-
// Compare versions
|
|
255
|
-
const comparison = compareVersions(currentVersion, latestVersion);
|
|
256
|
-
if (comparison >= 0) {
|
|
257
|
-
return {
|
|
258
|
-
text: personaIndicator + '✅ **Already up to date!**\n\n' +
|
|
259
|
-
`Current version: ${currentVersion}\n` +
|
|
260
|
-
`Latest version: ${latestVersion}\n\n` +
|
|
261
|
-
'No update needed.'
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
// Get configuration
|
|
265
|
-
const config = UpdateConfigManager.getInstance();
|
|
266
|
-
// Progress tracking
|
|
267
|
-
let progressMessage = personaIndicator + '🔄 **NPM Update in Progress**\n\n';
|
|
268
|
-
progressMessage += '**Steps:**\n';
|
|
269
|
-
progressMessage += '✅ Version check complete\n';
|
|
270
|
-
progressMessage += '⏳ Creating backup...\n';
|
|
271
|
-
// For npm installations, backup is mandatory for safety
|
|
272
|
-
logger.info('[UpdateManager] Creating backup before npm update');
|
|
273
|
-
try {
|
|
274
|
-
// For npm installations, we backup the global installation directory
|
|
275
|
-
const npmGlobalPath = InstallationDetector.getNpmGlobalPath();
|
|
276
|
-
if (!npmGlobalPath) {
|
|
277
|
-
throw new Error('Could not determine npm global installation path');
|
|
278
|
-
}
|
|
279
|
-
// Create npm-specific backup with progress
|
|
280
|
-
const backupPath = await this.backupManager.createNpmBackup(npmGlobalPath, currentVersion);
|
|
281
|
-
logger.info(`[UpdateManager] Backup created at: ${backupPath}`);
|
|
282
|
-
progressMessage = progressMessage.replace('⏳ Creating backup...', '✅ Backup created');
|
|
283
|
-
progressMessage += '⏳ Downloading and installing update...\n';
|
|
284
|
-
}
|
|
285
|
-
catch (backupError) {
|
|
286
|
-
logger.error('[UpdateManager] Backup failed:', backupError);
|
|
287
|
-
return {
|
|
288
|
-
text: personaIndicator + '❌ **Update Failed**\n\n' +
|
|
289
|
-
'Failed to create backup before update.\n' +
|
|
290
|
-
'Error: ' + (backupError instanceof Error ? backupError.message : String(backupError)) + '\n\n' +
|
|
291
|
-
'**Note:** Backup is mandatory for npm installations to ensure safe rollback.\n' +
|
|
292
|
-
'Please check disk space and permissions.'
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
// Perform npm update with progress
|
|
296
|
-
logger.info('[UpdateManager] Running npm update -g @dollhousemcp/mcp-server');
|
|
297
|
-
progressMessage += '\n**Progress:**\n';
|
|
298
|
-
progressMessage += '```\n';
|
|
299
|
-
progressMessage += 'Running: npm update -g @dollhousemcp/mcp-server\n';
|
|
300
|
-
progressMessage += 'This may take a few minutes...\n';
|
|
301
|
-
progressMessage += '```\n';
|
|
302
|
-
const updateResult = await safeExec('npm', ['update', '-g', '@dollhousemcp/mcp-server'], {
|
|
303
|
-
cwd: this.rootDir,
|
|
304
|
-
timeout: config.getNpmUpdateTimeout()
|
|
305
|
-
});
|
|
306
|
-
logger.info('[UpdateManager] npm update completed', updateResult);
|
|
307
|
-
// Verify update succeeded
|
|
308
|
-
const { stdout: verifyOutput } = await safeExec('npm', ['list', '-g', '@dollhousemcp/mcp-server', '--depth=0'], {
|
|
309
|
-
cwd: this.rootDir,
|
|
310
|
-
timeout: 30000
|
|
311
|
-
});
|
|
312
|
-
const versionMatch = verifyOutput.match(/@dollhousemcp\/mcp-server@(\d+\.\d+\.\d+)/);
|
|
313
|
-
const installedVersion = versionMatch ? versionMatch[1] : 'unknown';
|
|
314
|
-
if (installedVersion !== latestVersion) {
|
|
315
|
-
logger.warn(`[UpdateManager] Version mismatch after update. Expected: ${latestVersion}, Got: ${installedVersion}`);
|
|
316
|
-
}
|
|
317
|
-
return {
|
|
318
|
-
text: personaIndicator + '✅ **Update Complete!**\n\n' +
|
|
319
|
-
`Updated from v${currentVersion} to v${latestVersion}\n\n` +
|
|
320
|
-
'**What was updated:**\n' +
|
|
321
|
-
'• DollhouseMCP server package\n' +
|
|
322
|
-
'• All dependencies\n\n' +
|
|
323
|
-
'**Next Steps:**\n' +
|
|
324
|
-
'1. The server will restart automatically\n' +
|
|
325
|
-
'2. Check `get_server_status` to verify the new version\n' +
|
|
326
|
-
'3. Test your personas to ensure everything works\n\n' +
|
|
327
|
-
'💡 **Tip:** If you encounter issues, use `rollback_update true` to restore the previous version.'
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
catch (error) {
|
|
331
|
-
logger.error('[UpdateManager] npm update failed:', error);
|
|
332
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
333
|
-
return {
|
|
334
|
-
text: personaIndicator + '❌ **Update Failed**\n\n' +
|
|
335
|
-
'Error: ' + errorMessage + '\n\n' +
|
|
336
|
-
'**Troubleshooting:**\n' +
|
|
337
|
-
'1. Ensure you have permission to update global npm packages\n' +
|
|
338
|
-
'2. Try running with sudo if on macOS/Linux\n' +
|
|
339
|
-
'3. Check your internet connection\n' +
|
|
340
|
-
'4. Verify npm registry is accessible\n\n' +
|
|
341
|
-
'**Manual Update:**\n' +
|
|
342
|
-
'```\n' +
|
|
343
|
-
'npm update -g @dollhousemcp/mcp-server\n' +
|
|
344
|
-
'```'
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Rollback npm installation
|
|
350
|
-
*/
|
|
351
|
-
async rollbackNpmInstallation(force, personaIndicator = '') {
|
|
352
|
-
try {
|
|
353
|
-
logger.info('[UpdateManager] Starting npm rollback process');
|
|
354
|
-
// Get npm backup manifest from configuration
|
|
355
|
-
const config = UpdateConfigManager.getInstance();
|
|
356
|
-
const npmBackupsDir = config.getNpmBackupDir();
|
|
357
|
-
const manifestPath = path.join(npmBackupsDir, 'manifest.json');
|
|
358
|
-
let manifest;
|
|
359
|
-
try {
|
|
360
|
-
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
361
|
-
manifest = JSON.parse(content);
|
|
362
|
-
}
|
|
363
|
-
catch (error) {
|
|
364
|
-
return {
|
|
365
|
-
text: personaIndicator + '❌ **No NPM Backups Found**\n\n' +
|
|
366
|
-
'There are no npm backups available to restore.\n\n' +
|
|
367
|
-
'Backups are created automatically when you run `update_server true` with npm installations.'
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
if (!manifest.backups || manifest.backups.length === 0) {
|
|
371
|
-
return {
|
|
372
|
-
text: personaIndicator + '❌ **No NPM Backups Found**\n\n' +
|
|
373
|
-
'The backup manifest is empty.\n\n' +
|
|
374
|
-
'Backups are created automatically when you run `update_server true`.'
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
// Get latest backup
|
|
378
|
-
const latestBackup = manifest.backups[0];
|
|
379
|
-
// Check if rollback is needed
|
|
380
|
-
if (!force) {
|
|
381
|
-
try {
|
|
382
|
-
// Test if the server is working
|
|
383
|
-
await this.versionManager.getCurrentVersion();
|
|
384
|
-
return {
|
|
385
|
-
text: personaIndicator + '⚠️ **Rollback Confirmation Required**\n\n' +
|
|
386
|
-
'The server appears to be working normally.\n\n' +
|
|
387
|
-
`**Latest Backup:** ${latestBackup.timestamp}\n` +
|
|
388
|
-
`**Backup Version:** ${latestBackup.version || 'Unknown'}\n\n` +
|
|
389
|
-
'To force rollback anyway, use: `rollback_update true`\n\n' +
|
|
390
|
-
'⚠️ **Warning:** This will restore the npm package to the backup state.'
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
catch {
|
|
394
|
-
// Server is broken, proceed with rollback
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
// Get npm global path
|
|
398
|
-
const npmGlobalPath = InstallationDetector.getNpmGlobalPath();
|
|
399
|
-
if (!npmGlobalPath) {
|
|
400
|
-
return {
|
|
401
|
-
text: personaIndicator + '❌ **Rollback Failed**\n\n' +
|
|
402
|
-
'Could not determine npm global installation path.\n\n' +
|
|
403
|
-
'Please reinstall manually:\n' +
|
|
404
|
-
'```\n' +
|
|
405
|
-
'npm install -g @dollhousemcp/mcp-server@' + (latestBackup.version || 'latest') + '\n' +
|
|
406
|
-
'```'
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
logger.info(`[UpdateManager] Restoring npm backup from: ${latestBackup.path}`);
|
|
410
|
-
// First validate that the backup is restorable
|
|
411
|
-
const backupPackagePath = path.join(latestBackup.path, 'package');
|
|
412
|
-
try {
|
|
413
|
-
await fs.access(backupPackagePath);
|
|
414
|
-
const packageJsonPath = path.join(backupPackagePath, 'package.json');
|
|
415
|
-
await fs.access(packageJsonPath);
|
|
416
|
-
}
|
|
417
|
-
catch (error) {
|
|
418
|
-
return {
|
|
419
|
-
text: personaIndicator + '❌ **Backup Validation Failed**\n\n' +
|
|
420
|
-
'The backup appears to be corrupted or incomplete.\n\n' +
|
|
421
|
-
'**Details:**\n' +
|
|
422
|
-
`Backup path: ${latestBackup.path}\n` +
|
|
423
|
-
`Error: ${error instanceof Error ? error.message : String(error)}\n\n` +
|
|
424
|
-
'**Manual Recovery:**\n' +
|
|
425
|
-
'Try another backup or reinstall manually:\n' +
|
|
426
|
-
'```bash\n' +
|
|
427
|
-
'npm install -g @dollhousemcp/mcp-server@' + (latestBackup.version || '1.4.0') + '\n' +
|
|
428
|
-
'```'
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
// Use transaction for atomic operations
|
|
432
|
-
const tempPath = `${npmGlobalPath}.tmp-${Date.now()}`;
|
|
433
|
-
const backupPath = `${npmGlobalPath}.backup-${Date.now()}`;
|
|
434
|
-
const transaction = FileOperations.createTransaction();
|
|
435
|
-
try {
|
|
436
|
-
// Step 1: Copy backup to temporary location
|
|
437
|
-
await transaction.addCopy(backupPackagePath, tempPath);
|
|
438
|
-
// Step 2: Move current installation to backup (atomic)
|
|
439
|
-
await transaction.addMove(npmGlobalPath, backupPath);
|
|
440
|
-
// Step 3: Move temp to final location (atomic)
|
|
441
|
-
await transaction.addMove(tempPath, npmGlobalPath);
|
|
442
|
-
// All operations successful, commit the transaction
|
|
443
|
-
transaction.commit();
|
|
444
|
-
// Step 4: Clean up old backup (not part of transaction)
|
|
445
|
-
await fs.rm(backupPath, { recursive: true, force: true }).catch(() => {
|
|
446
|
-
logger.warn(`[UpdateManager] Failed to cleanup backup at: ${backupPath}`);
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
catch (rollbackError) {
|
|
450
|
-
logger.error('[UpdateManager] Rollback operation failed, attempting recovery:', rollbackError);
|
|
451
|
-
// Rollback all operations
|
|
452
|
-
if (transaction.hasOperations()) {
|
|
453
|
-
await transaction.rollback();
|
|
454
|
-
}
|
|
455
|
-
// Additional recovery attempt
|
|
456
|
-
try {
|
|
457
|
-
// Check if npm path exists and is accessible
|
|
458
|
-
await fs.access(npmGlobalPath);
|
|
459
|
-
logger.info('[UpdateManager] NPM installation appears intact after rollback');
|
|
460
|
-
}
|
|
461
|
-
catch {
|
|
462
|
-
// Try to restore from backup if main path is missing
|
|
463
|
-
try {
|
|
464
|
-
await fs.rename(backupPath, npmGlobalPath);
|
|
465
|
-
logger.info('[UpdateManager] Restored from backup path');
|
|
466
|
-
}
|
|
467
|
-
catch {
|
|
468
|
-
logger.error('[UpdateManager] Complete rollback failure - manual intervention required');
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
throw rollbackError;
|
|
472
|
-
}
|
|
473
|
-
return {
|
|
474
|
-
text: personaIndicator + '✅ **NPM Rollback Complete!**\n\n' +
|
|
475
|
-
`Restored from backup: ${latestBackup.timestamp}\n` +
|
|
476
|
-
`Backup version: ${latestBackup.version || 'Unknown'}\n\n` +
|
|
477
|
-
'**What was restored:**\n' +
|
|
478
|
-
'• NPM package files\n' +
|
|
479
|
-
'• All dependencies\n\n' +
|
|
480
|
-
'**Next Steps:**\n' +
|
|
481
|
-
'1. The server will restart automatically\n' +
|
|
482
|
-
'2. Check `get_server_status` to verify the version\n' +
|
|
483
|
-
'3. Test your personas to ensure everything works'
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
catch (error) {
|
|
487
|
-
logger.error('[UpdateManager] npm rollback failed:', error);
|
|
488
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
489
|
-
return {
|
|
490
|
-
text: personaIndicator + '❌ **NPM Rollback Failed**\n\n' +
|
|
491
|
-
'Error: ' + errorMessage + '\n\n' +
|
|
492
|
-
'**Manual Recovery:**\n' +
|
|
493
|
-
'1. Check the backups directory: ~/.dollhouse/backups/npm/\n' +
|
|
494
|
-
'2. Reinstall a specific version:\n' +
|
|
495
|
-
' ```\n' +
|
|
496
|
-
' npm install -g @dollhousemcp/mcp-server@1.4.0\n' +
|
|
497
|
-
' ```\n' +
|
|
498
|
-
'3. Contact support if issues persist'
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* Copy directory recursively with progress reporting
|
|
504
|
-
* @deprecated Use FileOperations.copyDirectory instead
|
|
505
|
-
*/
|
|
506
|
-
async copyDirectory(src, dest) {
|
|
507
|
-
await FileOperations.copyDirectory(src, dest, {
|
|
508
|
-
excludePatterns: ['.git', 'node_modules'],
|
|
509
|
-
onProgress: (copied, total, file) => {
|
|
510
|
-
logger.debug(`[UpdateManager] Copying files: ${copied}/${total} - ${path.basename(file)}`);
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* Convert npm installation to git installation
|
|
516
|
-
*/
|
|
517
|
-
async convertToGitInstallation(targetDir, confirm = false, personaIndicator = '') {
|
|
518
|
-
try {
|
|
519
|
-
const installationType = InstallationDetector.getInstallationType();
|
|
520
|
-
if (installationType === 'git') {
|
|
521
|
-
return {
|
|
522
|
-
text: personaIndicator + '⚠️ **Already a Git Installation**\n\n' +
|
|
523
|
-
'This server is already running from a git installation.\n' +
|
|
524
|
-
'No conversion needed.'
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
if (installationType === 'unknown') {
|
|
528
|
-
return {
|
|
529
|
-
text: personaIndicator + '❌ **Installation Type Unknown**\n\n' +
|
|
530
|
-
'Cannot determine the current installation type.\n' +
|
|
531
|
-
'Please check your installation manually.'
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
// Default target directory
|
|
535
|
-
const defaultTargetDir = path.join(process.env.HOME || '', '.dollhouse', 'mcp-server-git');
|
|
536
|
-
const gitTargetDir = targetDir || defaultTargetDir;
|
|
537
|
-
if (!confirm) {
|
|
538
|
-
return {
|
|
539
|
-
text: personaIndicator + '🔄 **Convert to Git Installation**\n\n' +
|
|
540
|
-
'**This will:**\n' +
|
|
541
|
-
`1. Clone DollhouseMCP to: ${gitTargetDir}\n` +
|
|
542
|
-
'2. Copy your portfolio and settings\n' +
|
|
543
|
-
'3. Build the TypeScript code\n' +
|
|
544
|
-
'4. Provide Claude Desktop configuration\n\n' +
|
|
545
|
-
'**Benefits of Git Installation:**\n' +
|
|
546
|
-
'• Full control over updates\n' +
|
|
547
|
-
'• Access to development branches\n' +
|
|
548
|
-
'• Ability to contribute changes\n' +
|
|
549
|
-
'• Rollback to any commit\n\n' +
|
|
550
|
-
'**To proceed:**\n' +
|
|
551
|
-
'`convert_to_git_installation true`\n\n' +
|
|
552
|
-
'**To use custom directory:**\n' +
|
|
553
|
-
'`convert_to_git_installation "/path/to/dir" true`'
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
logger.info(`[UpdateManager] Starting conversion to git installation at: ${gitTargetDir}`);
|
|
557
|
-
// Check if target directory already exists
|
|
558
|
-
try {
|
|
559
|
-
await fs.access(gitTargetDir);
|
|
560
|
-
return {
|
|
561
|
-
text: personaIndicator + '❌ **Target Directory Exists**\n\n' +
|
|
562
|
-
`The directory ${gitTargetDir} already exists.\n\n` +
|
|
563
|
-
'**Options:**\n' +
|
|
564
|
-
'1. Remove the existing directory first\n' +
|
|
565
|
-
'2. Choose a different target directory\n' +
|
|
566
|
-
'3. Use the existing git installation'
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
catch {
|
|
570
|
-
// Directory doesn't exist, good to proceed
|
|
571
|
-
}
|
|
572
|
-
// Progress message builder
|
|
573
|
-
let progressSteps = personaIndicator + '🔄 **Git Installation Progress**\n\n';
|
|
574
|
-
progressSteps += '**Steps:**\n';
|
|
575
|
-
progressSteps += '⏳ Cloning repository...\n';
|
|
576
|
-
progressSteps += '⏳ Installing dependencies...\n';
|
|
577
|
-
progressSteps += '⏳ Building TypeScript...\n';
|
|
578
|
-
progressSteps += '⏳ Setting up configuration...\n\n';
|
|
579
|
-
progressSteps += '**Current Step:** Cloning repository\n';
|
|
580
|
-
progressSteps += '```\n';
|
|
581
|
-
progressSteps += `Target: ${gitTargetDir}\n`;
|
|
582
|
-
progressSteps += 'This may take a few minutes depending on your connection...\n';
|
|
583
|
-
progressSteps += '```\n';
|
|
584
|
-
// Get configuration
|
|
585
|
-
const config = UpdateConfigManager.getInstance();
|
|
586
|
-
// Step 1: Clone the repository
|
|
587
|
-
logger.info('[UpdateManager] Cloning repository...');
|
|
588
|
-
// SECURITY FIX: Validate gitTargetDir to prevent command injection
|
|
589
|
-
// Previously: gitTargetDir passed directly to git clone command
|
|
590
|
-
// Now: Reject paths starting with '--' to prevent git option injection
|
|
591
|
-
if (gitTargetDir.startsWith('--')) {
|
|
592
|
-
throw new Error('Invalid target directory: cannot start with git options');
|
|
593
|
-
}
|
|
594
|
-
await safeExec('git', ['clone', 'https://github.com/DollhouseMCP/mcp-server.git', gitTargetDir], {
|
|
595
|
-
timeout: config.getGitCloneTimeout()
|
|
596
|
-
});
|
|
597
|
-
progressSteps = progressSteps.replace('⏳ Cloning repository...', '✅ Repository cloned');
|
|
598
|
-
progressSteps = progressSteps.replace('**Current Step:** Cloning repository', '**Current Step:** Installing dependencies');
|
|
599
|
-
// Step 2: Install dependencies
|
|
600
|
-
logger.info('[UpdateManager] Installing dependencies...');
|
|
601
|
-
await safeExec('npm', ['install'], {
|
|
602
|
-
cwd: gitTargetDir,
|
|
603
|
-
timeout: config.getNpmInstallTimeout()
|
|
604
|
-
});
|
|
605
|
-
progressSteps = progressSteps.replace('⏳ Installing dependencies...', '✅ Dependencies installed');
|
|
606
|
-
progressSteps = progressSteps.replace('**Current Step:** Installing dependencies', '**Current Step:** Building TypeScript');
|
|
607
|
-
// Step 3: Build TypeScript
|
|
608
|
-
logger.info('[UpdateManager] Building TypeScript...');
|
|
609
|
-
await safeExec('npm', ['run', 'build'], {
|
|
610
|
-
cwd: gitTargetDir,
|
|
611
|
-
timeout: config.getBuildTimeout()
|
|
612
|
-
});
|
|
613
|
-
progressSteps = progressSteps.replace('⏳ Building TypeScript...', '✅ TypeScript built');
|
|
614
|
-
progressSteps = progressSteps.replace('**Current Step:** Building TypeScript', '**Current Step:** Configuration');
|
|
615
|
-
// Step 4: Copy portfolio
|
|
616
|
-
const portfolioSource = path.join(process.env.HOME || '', '.dollhouse', 'portfolio');
|
|
617
|
-
const portfolioTarget = path.join(process.env.HOME || '', '.dollhouse', 'portfolio');
|
|
618
|
-
logger.info('[UpdateManager] Portfolio will remain at: ' + portfolioTarget);
|
|
619
|
-
// Step 5: Generate Claude Desktop config
|
|
620
|
-
const configPath = path.join(gitTargetDir, 'dist', 'index.js');
|
|
621
|
-
const claudeConfig = {
|
|
622
|
-
mcpServers: {
|
|
623
|
-
dollhousemcp: {
|
|
624
|
-
command: 'node',
|
|
625
|
-
args: [configPath]
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
return {
|
|
630
|
-
text: personaIndicator + '✅ **Git Installation Complete!**\n\n' +
|
|
631
|
-
`**Installation Location:** ${gitTargetDir}\n\n` +
|
|
632
|
-
'**Next Steps:**\n\n' +
|
|
633
|
-
'1. **Update Claude Desktop configuration:**\n' +
|
|
634
|
-
' ```json\n' +
|
|
635
|
-
JSON.stringify(claudeConfig, null, 2) + '\n' +
|
|
636
|
-
' ```\n\n' +
|
|
637
|
-
'2. **Restart Claude Desktop**\n\n' +
|
|
638
|
-
'3. **Verify installation:**\n' +
|
|
639
|
-
' After restart, run `get_server_status` to confirm\n\n' +
|
|
640
|
-
'**Your portfolio remains at:**\n' +
|
|
641
|
-
` ${portfolioTarget}\n\n` +
|
|
642
|
-
'**To update in the future:**\n' +
|
|
643
|
-
' ```bash\n' +
|
|
644
|
-
` cd ${gitTargetDir}\n` +
|
|
645
|
-
' git pull\n' +
|
|
646
|
-
' npm install\n' +
|
|
647
|
-
' npm run build\n' +
|
|
648
|
-
' ```\n\n' +
|
|
649
|
-
'💡 **Tip:** You can now use `update_server` to update via git!'
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
catch (error) {
|
|
653
|
-
logger.error('[UpdateManager] Git conversion failed:', error);
|
|
654
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
655
|
-
return {
|
|
656
|
-
text: personaIndicator + '❌ **Conversion Failed**\n\n' +
|
|
657
|
-
'Error: ' + errorMessage + '\n\n' +
|
|
658
|
-
'**Troubleshooting:**\n' +
|
|
659
|
-
'1. Ensure git is installed\n' +
|
|
660
|
-
'2. Check internet connection\n' +
|
|
661
|
-
'3. Verify you have write permissions\n' +
|
|
662
|
-
'4. Try a different target directory'
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* Get current server status
|
|
668
|
-
*/
|
|
669
|
-
async getServerStatus(personaIndicator = '') {
|
|
670
|
-
try {
|
|
671
|
-
const currentVersion = await this.versionManager.getCurrentVersion();
|
|
672
|
-
const dependencies = await this.dependencyChecker.checkDependencies();
|
|
673
|
-
const backups = await this.backupManager.listBackups();
|
|
674
|
-
const rateLimitStatus = this.updateChecker.getRateLimitStatus();
|
|
675
|
-
// Get installation type
|
|
676
|
-
const installationType = InstallationDetector.getInstallationType();
|
|
677
|
-
const installationDesc = InstallationDetector.getInstallationDescription();
|
|
678
|
-
// Get git status (only for git installations)
|
|
679
|
-
let gitStatus = 'N/A';
|
|
680
|
-
let gitBranch = 'N/A';
|
|
681
|
-
let lastCommit = 'N/A';
|
|
682
|
-
if (installationType === 'git') {
|
|
683
|
-
try {
|
|
684
|
-
const { stdout: branchOutput } = await safeExec('git', ['branch', '--show-current'], { cwd: this.rootDir });
|
|
685
|
-
gitBranch = branchOutput.trim() || 'detached';
|
|
686
|
-
const { stdout: statusOutput } = await safeExec('git', ['status', '--porcelain'], { cwd: this.rootDir });
|
|
687
|
-
gitStatus = statusOutput.trim() ? 'Modified' : 'Clean';
|
|
688
|
-
const { stdout: logOutput } = await safeExec('git', ['log', '-1', '--oneline'], { cwd: this.rootDir });
|
|
689
|
-
lastCommit = logOutput.trim();
|
|
690
|
-
}
|
|
691
|
-
catch {
|
|
692
|
-
// Git commands failed, use defaults
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
const statusParts = [
|
|
696
|
-
personaIndicator + '📊 **DollhouseMCP Server Status**\n\n',
|
|
697
|
-
'**Version Information:**\n',
|
|
698
|
-
`• Current Version: ${currentVersion}\n`,
|
|
699
|
-
`• Installation Type: ${installationType} (${installationDesc})\n`,
|
|
700
|
-
`• Git Branch: ${gitBranch}\n`,
|
|
701
|
-
`• Git Status: ${gitStatus}\n`,
|
|
702
|
-
`• Last Commit: ${lastCommit}\n\n`,
|
|
703
|
-
'**Dependencies:**\n',
|
|
704
|
-
this.dependencyChecker.formatDependencyStatus(dependencies),
|
|
705
|
-
'\n\n**Backups:**\n',
|
|
706
|
-
`• Total Backups: ${backups.length}\n`
|
|
707
|
-
];
|
|
708
|
-
if (backups.length > 0) {
|
|
709
|
-
statusParts.push(`• Latest Backup: ${backups[0].timestamp} (v${backups[0].version || 'unknown'})\n`);
|
|
710
|
-
statusParts.push(`• Oldest Backup: ${backups[backups.length - 1].timestamp}\n`);
|
|
711
|
-
}
|
|
712
|
-
statusParts.push('\n**Rate Limit Status:**\n', `• Update Checks Remaining: ${rateLimitStatus.remainingRequests}/10 per hour\n`, `• Rate Limit Resets: ${rateLimitStatus.resetTime.toLocaleTimeString()}\n`);
|
|
713
|
-
if (!rateLimitStatus.allowed && rateLimitStatus.waitTimeSeconds) {
|
|
714
|
-
statusParts.push(`• ⏳ Wait ${rateLimitStatus.waitTimeSeconds} seconds before next check\n`);
|
|
715
|
-
}
|
|
716
|
-
statusParts.push('\n**Available Commands:**\n', '• `check_for_updates` - Check for new versions\n', '• `update_server true` - Update to latest version\n', '• `rollback_update true` - Restore from backup\n');
|
|
717
|
-
return { text: statusParts.join('') };
|
|
718
|
-
}
|
|
719
|
-
catch (error) {
|
|
720
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
721
|
-
return {
|
|
722
|
-
text: personaIndicator + '❌ **Status Check Failed**\n\n' +
|
|
723
|
-
'Error: ' + errorMessage + '\n\n' +
|
|
724
|
-
'The server may be in an inconsistent state.\n' +
|
|
725
|
-
'Try running `update_server true` to fix issues.'
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVXBkYXRlTWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91cGRhdGUvVXBkYXRlTWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMzQyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDckQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzNELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNuRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxjQUFjLEVBQW1CLE1BQU0sNEJBQTRCLENBQUM7QUFDN0UsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFRaEUsTUFBTSxPQUFPLGFBQWE7SUFDaEIsY0FBYyxDQUFpQjtJQUMvQixhQUFhLENBQWdCO0lBQzdCLGlCQUFpQixDQUFvQjtJQUNyQyxhQUFhLENBQWdCO0lBQzdCLE9BQU8sQ0FBUztJQUV4QixZQUFZLE9BQWdCO1FBQzFCLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxJQUFJLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUN4QyxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksY0FBYyxFQUFFLENBQUM7UUFDM0MsSUFBSSxDQUFDLGFBQWEsR0FB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S0FBSyxDQUFDLENBQUM7Z0JBQ3JHLFdBQVcsQ0FBQyxJQUFJLENBQUMsb0JBQW9CLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLFNBQVMsSUFBSSxDQUFDLENBQUM7WUFDbEYsQ0FBQztZQUVELFdBQVcsQ0FBQyxJQUFJLENBQ2QsNEJBQTRCLEVBQzVCLDhCQUE4QixlQUFlLENBQUMsaUJBQWlCLGdCQUFnQixFQUMvRSx3QkFBd0IsZUFBZSxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsRUFBRSxJQUFJLENBQzNFLENBQUM7WUFFRixJQUFJLENBQUMsZUFBZSxDQUFDLE9BQU8sSUFBSSxlQUFlLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ2hFLFdBQVcsQ0FBQyxJQUFJLENBQUMsWUFBWSxlQUFlLENBQUMsZUFBZSw4QkFBOEIsQ0FBQyxDQUFDO1lBQzlGLENBQUM7WUFFRCxXQUFXLENBQUMsSUFBSSxDQUNkLDZCQUE2QixFQUM3QixrREFBa0QsRUFDbEQscURBQXFELEVBQ3JELGtEQUFrRCxDQUNuRCxDQUFDO1lBRUYsT0FBTyxFQUFFLElBQUksRUFBRSxXQUFXLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFFeEMsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLFlBQVksR0FBRyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7WUFFNUUsT0FBTztnQkFDTCxJQUFJLEVBQUUsZ0JBQWdCLEdBQUcsK0JBQStCO29CQUN0RCxTQUFTLEdBQUcsWUFBWSxHQUFHLE1BQU07b0JBQ2pDLCtDQUErQztvQkFDL0MsaURBQWlEO2FBQ3BELENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNYW5hZ2Ugc2VydmVyIHVwZGF0ZXMgYW5kIHJvbGxiYWNrc1xuICovXG5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcy9wcm9taXNlcyc7XG5pbXBvcnQgeyBzYWZlRXhlYyB9IGZyb20gJy4uL3V0aWxzL2dpdC5qcyc7XG5pbXBvcnQgeyBWZXJzaW9uTWFuYWdlciB9IGZyb20gJy4vVmVyc2lvbk1hbmFnZXIuanMnO1xuaW1wb3J0IHsgVXBkYXRlQ2hlY2tlciB9IGZyb20gJy4vVXBkYXRlQ2hlY2tlci5qcyc7XG5pbXBvcnQgeyBEZXBlbmRlbmN5Q2hlY2tlciB9IGZyb20gJy4vRGVwZW5kZW5jeUNoZWNrZXIuanMnO1xuaW1wb3J0IHsgQmFja3VwTWFuYWdlciB9IGZyb20gJy4vQmFja3VwTWFuYWdlci5qcyc7XG5pbXBvcnQgeyBJbnN0YWxsYXRpb25EZXRlY3RvciB9IGZyb20gJy4uL3V0aWxzL2luc3RhbGxhdGlvbi5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgY29tcGFyZVZlcnNpb25zIH0gZnJvbSAnLi4vdXRpbHMvdmVyc2lvbi5qcyc7XG5pbXBvcnQgeyBGaWxlT3BlcmF0aW9ucywgRmlsZVRyYW5zYWN0aW9uIH0gZnJvbSAnLi4vdXRpbHMvZmlsZU9wZXJhdGlvbnMuanMnO1xuaW1wb3J0IHsgVXBkYXRlQ29uZmlnTWFuYWdlciB9IGZyb20gJy4uL2NvbmZpZy91cGRhdGVDb25maWcuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFVwZGF0ZVByb2dyZXNzIHtcbiAgc3RlcDogc3RyaW5nO1xuICBtZXNzYWdlOiBzdHJpbmc7XG4gIGlzQ29tcGxldGU6IGJvb2xlYW47XG59XG5cbmV4cG9ydCBjbGFzcyBVcGRhdGVNYW5hZ2VyIHtcbiAgcHJpdmF0ZSB2ZXJzaW9uTWFuYWdlcjogVmVyc2lvbk1hbmFnZXI7XG4gIHByaXZhdGUgdXBkYXRlQ2hlY2tlcjogVXBkYXRlQ2hlY2tlcjtcbiAgcHJpdmF0ZSBkZXBlbmRlbmN5Q2hlY2tlcjogRGVwZW5kZW5jeUNoZWNrZXI7XG4gIHByaXZhdGUgYmFja3VwTWFuYWdlcjogQmFja3VwTWFuYWdlcjtcbiAgcHJpdmF0ZSByb290RGlyOiBzdHJpbmc7XG4gIFxuICBjb25zdHJ1Y3Rvcihyb290RGlyPzogc3RyaW5nKSB7XG4gICAgdGhpcy5yb290RGlyID0gcm9vdERpciB8fCBwcm9jZXNzLmN3ZCgpO1xuICAgIHRoaXMudmVyc2lvbk1hbmFnZXIgPSBuZXcgVmVyc2lvbk1hbmFnZXIoKTtcbiAgICB0aGlzLnVwZGF0ZUNoZWNrZXIgPSBuZXcgVXBkYXRlQ2hlY2tlcih0aGlzLnZlcnNpb25NYW5hZ2VyKTtcbiAgICB0aGlzLmRlcGVuZGVuY3lDaGVja2VyID0gbmV3IERlcGVuZGVuY3lDaGVja2VyKHRoaXMudmVyc2lvbk1hbmFnZXIpO1xuICAgIHRoaXMuYmFja3VwTWFuYWdlciA9IG5ldyBCYWNrdXBNYW5hZ2VyKHRoaXMucm9vdERpcik7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBmb3IgYXZhaWxhYmxlIHVwZGF0ZXNcbiAgICovXG4gIGFzeW5jIGNoZWNrRm9yVXBkYXRlcygpOiBQcm9taXNlPHsgdGV4dDogc3RyaW5nIH0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy51cGRhdGVDaGVja2VyLmNoZWNrRm9yVXBkYXRlcygpO1xuICAgICAgY29uc3QgdGV4dCA9IHRoaXMudXBkYXRlQ2hlY2tlci5mb3JtYXRVcGRhdGVDaGVja1Jlc3VsdChyZXN1bHQpO1xuICAgICAgcmV0dXJuIHsgdGV4dCB9O1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zdCB0ZXh0ID0gdGhpcy51cGRhdGVDaGVja2VyLmZvcm1hdFVwZGF0ZUNoZWNrUmVzdWx0KG51bGwsIGVycm9yIGFzIEVycm9yKTtcbiAgICAgIHJldHVybiB7IHRleHQgfTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBQZXJmb3JtIHNlcnZlciB1cGRhdGVcbiAgICovXG4gIGFzeW5jIHVwZGF0ZVNlcnZlcihjcmVhdGVCYWNrdXA6IGJvb2xlYW4gPSB0cnVlLCBwZXJzb25hSW5kaWNhdG9yOiBzdHJpbmcgPSAnJyk6IFByb21pc2U8eyB0ZXh0OiBzdHJpbmcgfT4ge1xuICAgIGNvbnN0IHByb2dyZXNzOiBVcGRhdGVQcm9ncmVzc1tdID0gW107XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIC8vIERldGVjdCBpbnN0YWxsYXRpb24gdHlwZVxuICAgICAgY29uc3QgaW5zdGFsbGF0aW9uVHlwZSA9IEluc3RhbGxhdGlvbkRldGVjdG9yLmdldEluc3RhbGxhdGlvblR5cGUoKTtcbiAgICAgIGxvZ2dlci5pbmZvKGBbVXBkYXRlTWFuYWdlcl0gRGV0ZWN0ZWQgaW5zdGFsbGF0aW9uIHR5cGU6ICR7aW5zdGFsbGF0aW9uVHlwZX1gKTtcbiAgICAgIFxuICAgICAgLy8gSGFuZGxlIG5wbSBpbnN0YWxsYXRpb25zIGRpZmZlcmVudGx5XG4gICAgICBpZiAoaW5zdGFsbGF0aW9uVHlwZSA9PT0gJ25wbScpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudXBkYXRlTnBtSW5zdGFsbGF0aW9uKGNyZWF0ZUJhY2t1cCwgcGVyc29uYUluZGljYXRvcik7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIEZvciBnaXQgaW5zdGFsbGF0aW9ucywgcHJvY2VlZCB3aXRoIGV4aXN0aW5nIGxvZ2ljXG4gICAgICAvLyBTdGVwIDE6IENoZWNrIGRlcGVuZGVuY2llc1xuICAgICAgcHJvZ3Jlc3MucHVzaCh7IHN0ZXA6ICdkZXBlbmRlbmNpZXMnLCBtZXNzYWdlOiAnQ2hlY2tpbmcgc3lzdGVtIGRlcGVuZGVuY2llcy4uLicsIGlzQ29tcGxldGU6IGZhbHNlIH0pO1xuICAgICAgY29uc3QgZGVwZW5kZW5jaWVzID0gYXdhaXQgdGhpcy5kZXBlbmRlbmN5Q2hlY2tlci5jaGVja0RlcGVuZGVuY2llcygpO1xuICAgICAgXG4gICAgICBpZiAoIWRlcGVuZGVuY2llcy5naXQuaW5zdGFsbGVkIHx8IGRlcGVuZGVuY2llcy5naXQuZXJyb3IpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ+KdjCAqKlVwZGF0ZSBGYWlsZWQqKlxcblxcbicgK1xuICAgICAgICAgICAgJ0dpdCBpcyByZXF1aXJlZCBmb3IgdXBkYXRlcyBidXQgaXMgbm90IGF2YWlsYWJsZS5cXG4nICtcbiAgICAgICAgICAgIGRlcGVuZGVuY2llcy5naXQuZXJyb3IgfHwgJ0dpdCBpcyBub3QgaW5zdGFsbGVkLidcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgaWYgKCFkZXBlbmRlbmNpZXMubnBtLmluc3RhbGxlZCB8fCBkZXBlbmRlbmNpZXMubnBtLmVycm9yKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfinYwgKipVcGRhdGUgRmFpbGVkKipcXG5cXG4nICtcbiAgICAgICAgICAgICducG0gaXMgcmVxdWlyZWQgZm9yIHVwZGF0ZXMgYnV0IGlzIG5vdCBhdmFpbGFibGUuXFxuJyArXG4gICAgICAgICAgICBkZXBlbmRlbmNpZXMubnBtLmVycm9yIHx8ICducG0gaXMgbm90IGluc3RhbGxlZC4nXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICBcbiAgICAgIHByb2dyZXNzWzBdLmlzQ29tcGxldGUgPSB0cnVlO1xuICAgICAgXG4gICAgICAvLyBTdGVwIDI6IENyZWF0ZSBiYWNrdXAgaWYgcmVxdWVzdGVkXG4gICAgICBpZiAoY3JlYXRlQmFja3VwKSB7XG4gICAgICAgIHByb2dyZXNzLnB1c2goeyBzdGVwOiAnYmFja3VwJywgbWVzc2FnZTogJ0NyZWF0aW5nIGJhY2t1cC4uLicsIGlzQ29tcGxldGU6IGZhbHNlIH0pO1xuICAgICAgICBcbiAgICAgICAgY29uc3QgY3VycmVudFZlcnNpb24gPSBhd2FpdCB0aGlzLnZlcnNpb25NYW5hZ2VyLmdldEN1cnJlbnRWZXJzaW9uKCk7XG4gICAgICAgIGNvbnN0IGJhY2t1cCA9IGF3YWl0IHRoaXMuYmFja3VwTWFuYWdlci5jcmVhdGVCYWNrdXAoY3VycmVudFZlcnNpb24pO1xuICAgICAgICBcbiAgICAgICAgcHJvZ3Jlc3NbMV0uaXNDb21wbGV0ZSA9IHRydWU7XG4gICAgICAgIHByb2dyZXNzWzFdLm1lc3NhZ2UgPSBgQmFja3VwIGNyZWF0ZWQgYXQ6ICR7YmFja3VwLnRpbWVzdGFtcH1gO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBTdGVwIDM6IEdpdCBmZXRjaFxuICAgICAgcHJvZ3Jlc3MucHVzaCh7IHN0ZXA6ICdmZXRjaCcsIG1lc3NhZ2U6ICdGZXRjaGluZyBsYXRlc3QgY2hhbmdlcy4uLicsIGlzQ29tcGxldGU6IGZhbHNlIH0pO1xuICAgICAgYXdhaXQgc2FmZUV4ZWMoJ2dpdCcsIFsnZmV0Y2gnLCAnb3JpZ2luJ10sIHsgY3dkOiB0aGlzLnJvb3REaXIgfSk7XG4gICAgICBwcm9ncmVzc1twcm9ncmVzcy5sZW5ndGggLSAxXS5pc0NvbXBsZXRlID0gdHJ1ZTtcbiAgICAgIFxuICAgICAgLy8gU3RlcCA0OiBDaGVjayBmb3IgdW5jb21taXR0ZWQgY2hhbmdlc1xuICAgICAgcHJvZ3Jlc3MucHVzaCh7IHN0ZXA6ICdjaGVjaycsIG1lc3NhZ2U6ICdDaGVja2luZyBmb3IgdW5jb21taXR0ZWQgY2hhbmdlcy4uLicsIGlzQ29tcGxldGU6IGZhbHNlIH0pO1xuICAgICAgY29uc3QgeyBzdGRvdXQ6IHN0YXR1c091dHB1dCB9ID0gYXdhaXQgc2FmZUV4ZWMoJ2dpdCcsIFsnc3RhdHVzJywgJy0tcG9yY2VsYWluJ10sIHsgY3dkOiB0aGlzLnJvb3REaXIgfSk7XG4gICAgICBcbiAgICAgIGlmIChzdGF0dXNPdXRwdXQudHJpbSgpKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfinYwgKipVcGRhdGUgRmFpbGVkKipcXG5cXG4nICtcbiAgICAgICAgICAgICdZb3UgaGF2ZSB1bmNvbW1pdHRlZCBjaGFuZ2VzLiBQbGVhc2UgY29tbWl0IG9yIHN0YXNoIHRoZW0gYmVmb3JlIHVwZGF0aW5nLlxcblxcbicgK1xuICAgICAgICAgICAgJ01vZGlmaWVkIGZpbGVzOlxcbicgKyBzdGF0dXNPdXRwdXRcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICAgIHByb2dyZXNzW3Byb2dyZXNzLmxlbmd0aCAtIDFdLmlzQ29tcGxldGUgPSB0cnVlO1xuICAgICAgXG4gICAgICAvLyBTdGVwIDU6IEdpdCBwdWxsXG4gICAgICBwcm9ncmVzcy5wdXNoKHsgc3RlcDogJ3B1bGwnLCBtZXNzYWdlOiAnUHVsbGluZyBsYXRlc3QgY2hhbmdlcy4uLicsIGlzQ29tcGxldGU6IGZhbHNlIH0pO1xuICAgICAgY29uc3QgeyBzdGRvdXQ6IHB1bGxPdXRwdXQgfSA9IGF3YWl0IHNhZmVFeGVjKCdnaXQnLCBbJ3B1bGwnLCAnb3JpZ2luJywgJ21haW4nXSwgeyBjd2Q6IHRoaXMucm9vdERpciB9KTtcbiAgICAgIHByb2dyZXNzW3Byb2dyZXNzLmxlbmd0aCAtIDFdLmlzQ29tcGxldGUgPSB0cnVlO1xuICAgICAgXG4gICAgICAvLyBDaGVjayBpZiBhbHJlYWR5IHVwIHRvIGRhdGVcbiAgICAgIGlmIChwdWxsT3V0cHV0LmluY2x1ZGVzKCdBbHJlYWR5IHVwIHRvIGRhdGUnKSkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4pyFICoqQWxyZWFkeSBVcCB0byBEYXRlKipcXG5cXG4nICtcbiAgICAgICAgICAgICdZb3VyIERvbGxob3VzZU1DUCBpbnN0YWxsYXRpb24gaXMgYWxyZWFkeSBhdCB0aGUgbGF0ZXN0IHZlcnNpb24uXFxuXFxuJyArXG4gICAgICAgICAgICAnTm8gY2hhbmdlcyB3ZXJlIHB1bGxlZCBmcm9tIHRoZSByZXBvc2l0b3J5LidcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gU3RlcCA2OiBucG0gaW5zdGFsbFxuICAgICAgcHJvZ3Jlc3MucHVzaCh7IHN0ZXA6ICdpbnN0YWxsJywgbWVzc2FnZTogJ0luc3RhbGxpbmcgZGVwZW5kZW5jaWVzLi4uJywgaXNDb21wbGV0ZTogZmFsc2UgfSk7XG4gICAgICBhd2FpdCBzYWZlRXhlYygnbnBtJywgWydpbnN0YWxsJ10sIHsgY3dkOiB0aGlzLnJvb3REaXIgfSk7XG4gICAgICBwcm9ncmVzc1twcm9ncmVzcy5sZW5ndGggLSAxXS5pc0NvbXBsZXRlID0gdHJ1ZTtcbiAgICAgIFxuICAgICAgLy8gU3RlcCA3OiBCdWlsZFxuICAgICAgcHJvZ3Jlc3MucHVzaCh7IHN0ZXA6ICdidWlsZCcsIG1lc3NhZ2U6ICdCdWlsZGluZyBUeXBlU2NyaXB0Li4uJywgaXNDb21wbGV0ZTogZmFsc2UgfSk7XG4gICAgICBhd2FpdCBzYWZlRXhlYygnbnBtJywgWydydW4nLCAnYnVpbGQnXSwgeyBjd2Q6IHRoaXMucm9vdERpciB9KTtcbiAgICAgIHByb2dyZXNzW3Byb2dyZXNzLmxlbmd0aCAtIDFdLmlzQ29tcGxldGUgPSB0cnVlO1xuICAgICAgXG4gICAgICAvLyBTdGVwIDg6IENsZWFudXAgb2xkIGJhY2t1cHNcbiAgICAgIGlmIChjcmVhdGVCYWNrdXApIHtcbiAgICAgICAgcHJvZ3Jlc3MucHVzaCh7IHN0ZXA6ICdjbGVhbnVwJywgbWVzc2FnZTogJ0NsZWFuaW5nIHVwIG9sZCBiYWNrdXBzLi4uJywgaXNDb21wbGV0ZTogZmFsc2UgfSk7XG4gICAgICAgIGNvbnN0IGRlbGV0ZWRDb3VudCA9IGF3YWl0IHRoaXMuYmFja3VwTWFuYWdlci5jbGVhbnVwT2xkQmFja3VwcygpO1xuICAgICAgICBwcm9ncmVzc1twcm9ncmVzcy5sZW5ndGggLSAxXS5pc0NvbXBsZXRlID0gdHJ1ZTtcbiAgICAgICAgcHJvZ3Jlc3NbcHJvZ3Jlc3MubGVuZ3RoIC0gMV0ubWVzc2FnZSA9IGBDbGVhbmVkIHVwICR7ZGVsZXRlZENvdW50fSBvbGQgYmFja3VwKHMpYDtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gRm9ybWF0IHN1Y2Nlc3MgbWVzc2FnZVxuICAgICAgY29uc3Qgc3VjY2Vzc1BhcnRzID0gW1xuICAgICAgICBwZXJzb25hSW5kaWNhdG9yICsgJ+KchSAqKlVwZGF0ZSBDb21wbGV0ZSEqKlxcblxcbicsXG4gICAgICAgICcqKlVwZGF0ZSBTdW1tYXJ5OioqXFxuJ1xuICAgICAgXTtcbiAgICAgIFxuICAgICAgcHJvZ3Jlc3MuZm9yRWFjaChwID0+IHtcbiAgICAgICAgc3VjY2Vzc1BhcnRzLnB1c2goYCR7cC5pc0NvbXBsZXRlID8gJ+KchScgOiAn4p2MJ30gJHtwLm1lc3NhZ2V9XFxuYCk7XG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgc3VjY2Vzc1BhcnRzLnB1c2goXG4gICAgICAgICdcXG4qKk5leHQgU3RlcHM6KipcXG4nLFxuICAgICAgICAnMS4gVGhlIHNlcnZlciB3aWxsIHJlc3RhcnQgYXV0b21hdGljYWxseVxcbicsXG4gICAgICAgICcyLiBBbGwgcGVyc29uYXMgd2lsbCBiZSByZWxvYWRlZFxcbicsXG4gICAgICAgICczLiBDaGVjayBgZ2V0X3NlcnZlcl9zdGF0dXNgIHRvIHZlcmlmeSB0aGUgbmV3IHZlcnNpb25cXG5cXG4nLFxuICAgICAgICAn8J+SoSAqKlRpcDoqKiBJZiB5b3UgZW5jb3VudGVyIGlzc3VlcywgdXNlIGByb2xsYmFja191cGRhdGUgdHJ1ZWAgdG8gcmVzdG9yZSB0aGUgcHJldmlvdXMgdmVyc2lvbi4nXG4gICAgICApO1xuICAgICAgXG4gICAgICByZXR1cm4geyB0ZXh0OiBzdWNjZXNzUGFydHMuam9pbignJykgfTtcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcik7XG4gICAgICBcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4p2MICoqVXBkYXRlIEZhaWxlZCoqXFxuXFxuJyArXG4gICAgICAgICAgJ0Vycm9yOiAnICsgZXJyb3JNZXNzYWdlICsgJ1xcblxcbicgK1xuICAgICAgICAgICcqKlByb2dyZXNzOioqXFxuJyArIFxuICAgICAgICAgIHByb2dyZXNzLm1hcChwID0+IGAke3AuaXNDb21wbGV0ZSA/ICfinIUnIDogJ+KdjCd9ICR7cC5tZXNzYWdlfWApLmpvaW4oJ1xcbicpICsgJ1xcblxcbicgK1xuICAgICAgICAgICcqKlJlY292ZXJ5IE9wdGlvbnM6KipcXG4nICtcbiAgICAgICAgICAn4oCiIFRyeSBydW5uaW5nIHRoZSB1cGRhdGUgYWdhaW5cXG4nICtcbiAgICAgICAgICAn4oCiIENoZWNrIHlvdXIgaW50ZXJuZXQgY29ubmVjdGlvblxcbicgK1xuICAgICAgICAgICfigKIgRW5zdXJlIHlvdSBoYXZlIHByb3BlciBwZXJtaXNzaW9uc1xcbicgK1xuICAgICAgICAgICfigKIgSWYgYSBiYWNrdXAgd2FzIGNyZWF0ZWQsIHVzZSBgcm9sbGJhY2tfdXBkYXRlIHRydWVgIHRvIHJlc3RvcmUnXG4gICAgICB9O1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIFJvbGxiYWNrIHRvIHByZXZpb3VzIHZlcnNpb25cbiAgICovXG4gIGFzeW5jIHJvbGxiYWNrVXBkYXRlKGZvcmNlOiBib29sZWFuID0gZmFsc2UsIHBlcnNvbmFJbmRpY2F0b3I6IHN0cmluZyA9ICcnKTogUHJvbWlzZTx7IHRleHQ6IHN0cmluZyB9PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIENoZWNrIGluc3RhbGxhdGlvbiB0eXBlXG4gICAgICBjb25zdCBpbnN0YWxsYXRpb25UeXBlID0gSW5zdGFsbGF0aW9uRGV0ZWN0b3IuZ2V0SW5zdGFsbGF0aW9uVHlwZSgpO1xuICAgICAgXG4gICAgICBpZiAoaW5zdGFsbGF0aW9uVHlwZSA9PT0gJ25wbScpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucm9sbGJhY2tOcG1JbnN0YWxsYXRpb24oZm9yY2UsIHBlcnNvbmFJbmRpY2F0b3IpO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBGb3IgZ2l0IGluc3RhbGxhdGlvbnMsIHVzZSBleGlzdGluZyBsb2dpY1xuICAgICAgLy8gR2V0IGxhdGVzdCBiYWNrdXBcbiAgICAgIGNvbnN0IGxhdGVzdEJhY2t1cCA9IGF3YWl0IHRoaXMuYmFja3VwTWFuYWdlci5nZXRMYXRlc3RCYWNrdXAoKTtcbiAgICAgIFxuICAgICAgaWYgKCFsYXRlc3RCYWNrdXApIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ+KdjCAqKk5vIEJhY2t1cHMgRm91bmQqKlxcblxcbicgK1xuICAgICAgICAgICAgJ1RoZXJlIGFyZSBubyBiYWNrdXBzIGF2YWlsYWJsZSB0byByZXN0b3JlLlxcblxcbicgK1xuICAgICAgICAgICAgJ0JhY2t1cHMgYXJlIGNyZWF0ZWQgYXV0b21hdGljYWxseSB3aGVuIHlvdSBydW4gYHVwZGF0ZV9zZXJ2ZXIgdHJ1ZWAuJ1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBDaGVjayBpZiByb2xsYmFjayBpcyBuZWVkZWRcbiAgICAgIGlmICghZm9yY2UpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAvLyBUZXN0IGlmIHRoZSBzZXJ2ZXIgaXMgd29ya2luZyBieSBjaGVja2luZyB2ZXJzaW9uXG4gICAgICAgICAgYXdhaXQgdGhpcy52ZXJzaW9uTWFuYWdlci5nZXRDdXJyZW50VmVyc2lvbigpO1xuICAgICAgICAgIFxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ+KaoO+4jyAqKlJvbGxiYWNrIENvbmZpcm1hdGlvbiBSZXF1aXJlZCoqXFxuXFxuJyArXG4gICAgICAgICAgICAgICdUaGUgc2VydmVyIGFwcGVhcnMgdG8gYmUgd29ya2luZyBub3JtYWxseS5cXG5cXG4nICtcbiAgICAgICAgICAgICAgYCoqTGF0ZXN0IEJhY2t1cDoqKiAke2xhdGVzdEJhY2t1cC50aW1lc3RhbXB9XFxuYCArXG4gICAgICAgICAgICAgIGAqKkJhY2t1cCBWZXJzaW9uOioqICR7bGF0ZXN0QmFja3VwLnZlcnNpb24gfHwgJ1Vua25vd24nfVxcblxcbmAgK1xuICAgICAgICAgICAgICAnVG8gZm9yY2Ugcm9sbGJhY2sgYW55d2F5LCB1c2U6IGByb2xsYmFja191cGRhdGUgdHJ1ZWBcXG5cXG4nICtcbiAgICAgICAgICAgICAgJ+KaoO+4jyAqKldhcm5pbmc6KiogVGhpcyB3aWxsIHJlc3RvcmUgYWxsIGZpbGVzIHRvIHRoZSBiYWNrdXAgc3RhdGUuJ1xuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIFNlcnZlciBpcyBicm9rZW4sIHByb2NlZWQgd2l0aCByb2xsYmFja1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFBlcmZvcm0gcm9sbGJhY2tcbiAgICAgIGF3YWl0IHRoaXMuYmFja3VwTWFuYWdlci5yZXN0b3JlQmFja3VwKGxhdGVzdEJhY2t1cC5wYXRoKTtcbiAgICAgIFxuICAgICAgLy8gUmVpbnN0YWxsIGRlcGVuZGVuY2llc1xuICAgICAgYXdhaXQgc2FmZUV4ZWMoJ25wbScsIFsnaW5zdGFsbCddLCB7IGN3ZDogdGhpcy5yb290RGlyIH0pO1xuICAgICAgXG4gICAgICAvLyBSZWJ1aWxkXG4gICAgICBhd2FpdCBzYWZlRXhlYygnbnBtJywgWydydW4nLCAnYnVpbGQnXSwgeyBjd2Q6IHRoaXMucm9vdERpciB9KTtcbiAgICAgIFxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfinIUgKipSb2xsYmFjayBDb21wbGV0ZSEqKlxcblxcbicgK1xuICAgICAgICAgIGBSZXN0b3JlZCBmcm9tIGJhY2t1cDogJHtsYXRlc3RCYWNrdXAudGltZXN0YW1wfVxcbmAgK1xuICAgICAgICAgIGBCYWNrdXAgdmVyc2lvbjogJHtsYXRlc3RCYWNrdXAudmVyc2lvbiB8fCAnVW5rbm93bid9XFxuXFxuYCArXG4gICAgICAgICAgJyoqV2hhdCB3YXMgcmVzdG9yZWQ6KipcXG4nICtcbiAgICAgICAgICAn4oCiIEFsbCBzb3VyY2UgZmlsZXNcXG4nICtcbiAgICAgICAgICAn4oCiIENvbmZpZ3VyYXRpb24gZmlsZXNcXG4nICtcbiAgICAgICAgICAn4oCiIERlcGVuZGVuY2llcyByZWluc3RhbGxlZFxcbicgK1xuICAgICAgICAgICfigKIgVHlwZVNjcmlwdCByZWJ1aWx0XFxuXFxuJyArXG4gICAgICAgICAgJyoqTmV4dCBTdGVwczoqKlxcbicgK1xuICAgICAgICAgICcxLiBUaGUgc2VydmVyIHdpbGwgcmVzdGFydCBhdXRvbWF0aWNhbGx5XFxuJyArXG4gICAgICAgICAgJzIuIENoZWNrIGBnZXRfc2VydmVyX3N0YXR1c2AgdG8gdmVyaWZ5IHRoZSB2ZXJzaW9uXFxuJyArXG4gICAgICAgICAgJzMuIFRlc3QgeW91ciBwZXJzb25hcyB0byBlbnN1cmUgZXZlcnl0aGluZyB3b3JrcydcbiAgICAgIH07XG4gICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc3QgZXJyb3JNZXNzYWdlID0gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpO1xuICAgICAgXG4gICAgICByZXR1cm4ge1xuICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ+KdjCAqKlJvbGxiYWNrIEZhaWxlZCoqXFxuXFxuJyArXG4gICAgICAgICAgJ0Vycm9yOiAnICsgZXJyb3JNZXNzYWdlICsgJ1xcblxcbicgK1xuICAgICAgICAgICcqKk1hbnVhbCBSZWNvdmVyeToqKlxcbicgK1xuICAgICAgICAgICcxLiBDaGVjayB0aGUgYmFja3VwcyBkaXJlY3Rvcnk6IC4uL2RvbGxob3VzZW1jcC1iYWNrdXBzL1xcbicgK1xuICAgICAgICAgICcyLiBNYW51YWxseSByZXN0b3JlIGZpbGVzIGlmIG5lZWRlZFxcbicgK1xuICAgICAgICAgICczLiBSdW4gYG5wbSBpbnN0YWxsYCBhbmQgYG5wbSBydW4gYnVpbGRgXFxuJyArXG4gICAgICAgICAgJzQuIENvbnRhY3Qgc3VwcG9ydCBpZiBpc3N1ZXMgcGVyc2lzdCdcbiAgICAgIH07XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogVXBkYXRlIG5wbSBpbnN0YWxsYXRpb25cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdXBkYXRlTnBtSW5zdGFsbGF0aW9uKGNyZWF0ZUJhY2t1cDogYm9vbGVhbiwgcGVyc29uYUluZGljYXRvcjogc3RyaW5nID0gJycpOiBQcm9taXNlPHsgdGV4dDogc3RyaW5nIH0+IHtcbiAgICB0cnkge1xuICAgICAgbG9nZ2VyLmluZm8oJ1tVcGRhdGVNYW5hZ2VyXSBTdGFydGluZyBucG0gdXBkYXRlIHByb2Nlc3MnKTtcbiAgICAgIFxuICAgICAgLy8gQ2hlY2sgbnBtIGlzIGF2YWlsYWJsZVxuICAgICAgY29uc3QgZGVwZW5kZW5jaWVzID0gYXdhaXQgdGhpcy5kZXBlbmRlbmN5Q2hlY2tlci5jaGVja0RlcGVuZGVuY2llcygpO1xuICAgICAgaWYgKCFkZXBlbmRlbmNpZXMubnBtLmluc3RhbGxlZCB8fCBkZXBlbmRlbmNpZXMubnBtLmVycm9yKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfinYwgKipVcGRhdGUgRmFpbGVkKipcXG5cXG4nICtcbiAgICAgICAgICAgICducG0gaXMgcmVxdWlyZWQgZm9yIHVwZGF0ZXMgYnV0IGlzIG5vdCBhdmFpbGFibGUuXFxuJyArXG4gICAgICAgICAgICBkZXBlbmRlbmNpZXMubnBtLmVycm9yIHx8ICducG0gaXMgbm90IGluc3RhbGxlZC4nXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIEdldCBjdXJyZW50IHZlcnNpb25cbiAgICAgIGNvbnN0IGN1cnJlbnRWZXJzaW9uID0gYXdhaXQgdGhpcy52ZXJzaW9uTWFuYWdlci5nZXRDdXJyZW50VmVyc2lvbigpO1xuICAgICAgbG9nZ2VyLmluZm8oYFtVcGRhdGVNYW5hZ2VyXSBDdXJyZW50IHZlcnNpb246ICR7Y3VycmVudFZlcnNpb259YCk7XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGxhdGVzdCB2ZXJzaW9uIGZyb20gbnBtIHJlZ2lzdHJ5XG4gICAgICBsb2dnZXIuaW5mbygnW1VwZGF0ZU1hbmFnZXJdIENoZWNraW5nIG5wbSByZWdpc3RyeSBmb3IgbGF0ZXN0IHZlcnNpb24nKTtcbiAgICAgIFxuICAgICAgLy8gU2VjdXJpdHk6IFZhbGlkYXRlIHBhY2thZ2UgbmFtZSB0byBwcmV2ZW50IGFueSBwb3RlbnRpYWwgaW5qZWN0aW9uXG4gICAgICBjb25zdCBwYWNrYWdlTmFtZSA9ICdAZG9sbGhvdXNlbWNwL21jcC1zZXJ2ZXInO1xuICAgICAgaWYgKCEvXkBbYS16MC05LV0rXFwvW2EtejAtOS1dKyQvLnRlc3QocGFja2FnZU5hbWUpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBwYWNrYWdlIG5hbWUgZm9ybWF0Jyk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIGNvbnN0IHsgc3Rkb3V0OiBucG1WaWV3T3V0cHV0IH0gPSBhd2FpdCBzYWZlRXhlYygnbnBtJywgWyd2aWV3JywgcGFja2FnZU5hbWUsICd2ZXJzaW9uJ10sIHtcbiAgICAgICAgY3dkOiB0aGlzLnJvb3REaXIsXG4gICAgICAgIHRpbWVvdXQ6IDMwMDAwXG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgY29uc3QgbGF0ZXN0VmVyc2lvbiA9IG5wbVZpZXdPdXRwdXQudHJpbSgpO1xuICAgICAgbG9nZ2VyLmluZm8oYFtVcGRhdGVNYW5hZ2VyXSBMYXRlc3QgbnBtIHZlcnNpb246ICR7bGF0ZXN0VmVyc2lvbn1gKTtcbiAgICAgIFxuICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xuICAgICAgY29uc3QgY29tcGFyaXNvbiA9IGNvbXBhcmVWZXJzaW9ucyhjdXJyZW50VmVyc2lvbiwgbGF0ZXN0VmVyc2lvbik7XG4gICAgICBcbiAgICAgIGlmIChjb21wYXJpc29uID49IDApIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ+KchSAqKkFscmVhZHkgdXAgdG8gZGF0ZSEqKlxcblxcbicgK1xuICAgICAgICAgICAgYEN1cnJlbnQgdmVyc2lvbjogJHtjdXJyZW50VmVyc2lvbn1cXG5gICtcbiAgICAgICAgICAgIGBMYXRlc3QgdmVyc2lvbjogJHtsYXRlc3RWZXJzaW9ufVxcblxcbmAgK1xuICAgICAgICAgICAgJ05vIHVwZGF0ZSBuZWVkZWQuJ1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBHZXQgY29uZmlndXJhdGlvblxuICAgICAgY29uc3QgY29uZmlnID0gVXBkYXRlQ29uZmlnTWFuYWdlci5nZXRJbnN0YW5jZSgpO1xuICAgICAgXG4gICAgICAvLyBQcm9ncmVzcyB0cmFja2luZ1xuICAgICAgbGV0IHByb2dyZXNzTWVzc2FnZSA9IHBlcnNvbmFJbmRpY2F0b3IgKyAn8J+UhCAqKk5QTSBVcGRhdGUgaW4gUHJvZ3Jlc3MqKlxcblxcbic7XG4gICAgICBwcm9ncmVzc01lc3NhZ2UgKz0gJyoqU3RlcHM6KipcXG4nO1xuICAgICAgcHJvZ3Jlc3NNZXNzYWdlICs9ICfinIUgVmVyc2lvbiBjaGVjayBjb21wbGV0ZVxcbic7XG4gICAgICBwcm9ncmVzc01lc3NhZ2UgKz0gJ+KPsyBDcmVhdGluZyBiYWNrdXAuLi5cXG4nO1xuICAgICAgXG4gICAgICAvLyBGb3IgbnBtIGluc3RhbGxhdGlvbnMsIGJhY2t1cCBpcyBtYW5kYXRvcnkgZm9yIHNhZmV0eVxuICAgICAgbG9nZ2VyLmluZm8oJ1tVcGRhdGVNYW5hZ2VyXSBDcmVhdGluZyBiYWNrdXAgYmVmb3JlIG5wbSB1cGRhdGUnKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIEZvciBucG0gaW5zdGFsbGF0aW9ucywgd2UgYmFja3VwIHRoZSBnbG9iYWwgaW5zdGFsbGF0aW9uIGRpcmVjdG9yeVxuICAgICAgICBjb25zdCBucG1HbG9iYWxQYXRoID0gSW5zdGFsbGF0aW9uRGV0ZWN0b3IuZ2V0TnBtR2xvYmFsUGF0aCgpO1xuICAgICAgICBpZiAoIW5wbUdsb2JhbFBhdGgpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBkZXRlcm1pbmUgbnBtIGdsb2JhbCBpbnN0YWxsYXRpb24gcGF0aCcpO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAvLyBDcmVhdGUgbnBtLXNwZWNpZmljIGJhY2t1cCB3aXRoIHByb2dyZXNzXG4gICAgICAgIGNvbnN0IGJhY2t1cFBhdGggPSBhd2FpdCB0aGlzLmJhY2t1cE1hbmFnZXIuY3JlYXRlTnBtQmFja3VwKG5wbUdsb2JhbFBhdGgsIGN1cnJlbnRWZXJzaW9uKTtcbiAgICAgICAgbG9nZ2VyLmluZm8oYFtVcGRhdGVNYW5hZ2VyXSBCYWNrdXAgY3JlYXRlZCBhdDogJHtiYWNrdXBQYXRofWApO1xuICAgICAgICBcbiAgICAgICAgcHJvZ3Jlc3NNZXNzYWdlID0gcHJvZ3Jlc3NNZXNzYWdlLnJlcGxhY2UoJ+KPsyBDcmVhdGluZyBiYWNrdXAuLi4nLCAn4pyFIEJhY2t1cCBjcmVhdGVkJyk7XG4gICAgICAgIHByb2dyZXNzTWVzc2FnZSArPSAn4o+zIERvd25sb2FkaW5nIGFuZCBpbnN0YWxsaW5nIHVwZGF0ZS4uLlxcbic7XG4gICAgICAgIFxuICAgICAgfSBjYXRjaCAoYmFja3VwRXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdbVXBkYXRlTWFuYWdlcl0gQmFja3VwIGZhaWxlZDonLCBiYWNrdXBFcnJvcik7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfinYwgKipVcGRhdGUgRmFpbGVkKipcXG5cXG4nICtcbiAgICAgICAgICAgICdGYWlsZWQgdG8gY3JlYXRlIGJhY2t1cCBiZWZvcmUgdXBkYXRlLlxcbicgK1xuICAgICAgICAgICAgJ0Vycm9yOiAnICsgKGJhY2t1cEVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBiYWNrdXBFcnJvci5tZXNzYWdlIDogU3RyaW5nKGJhY2t1cEVycm9yKSkgKyAnXFxuXFxuJyArXG4gICAgICAgICAgICAnKipOb3RlOioqIEJhY2t1cCBpcyBtYW5kYXRvcnkgZm9yIG5wbSBpbnN0YWxsYXRpb25zIHRvIGVuc3VyZSBzYWZlIHJvbGxiYWNrLlxcbicgK1xuICAgICAgICAgICAgJ1BsZWFzZSBjaGVjayBkaXNrIHNwYWNlIGFuZCBwZXJtaXNzaW9ucy4nXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFBlcmZvcm0gbnBtIHVwZGF0ZSB3aXRoIHByb2dyZXNzXG4gICAgICBsb2dnZXIuaW5mbygnW1VwZGF0ZU1hbmFnZXJdIFJ1bm5pbmcgbnBtIHVwZGF0ZSAtZyBAZG9sbGhvdXNlbWNwL21jcC1zZXJ2ZXInKTtcbiAgICAgIHByb2dyZXNzTWVzc2FnZSArPSAnXFxuKipQcm9ncmVzczoqKlxcbic7XG4gICAgICBwcm9ncmVzc01lc3NhZ2UgKz0gJ2BgYFxcbic7XG4gICAgICBwcm9ncmVzc01lc3NhZ2UgKz0gJ1J1bm5pbmc6IG5wbSB1cGRhdGUgLWcgQGRvbGxob3VzZW1jcC9tY3Atc2VydmVyXFxuJztcbiAgICAgIHByb2dyZXNzTWVzc2FnZSArPSAnVGhpcyBtYXkgdGFrZSBhIGZldyBtaW51dGVzLi4uXFxuJztcbiAgICAgIHByb2dyZXNzTWVzc2FnZSArPSAnYGBgXFxuJztcbiAgICAgIFxuICAgICAgY29uc3QgdXBkYXRlUmVzdWx0ID0gYXdhaXQgc2FmZUV4ZWMoJ25wbScsIFsndXBkYXRlJywgJy1nJywgJ0Bkb2xsaG91c2VtY3AvbWNwLXNlcnZlciddLCB7XG4gICAgICAgIGN3ZDogdGhpcy5yb290RGlyLFxuICAgICAgICB0aW1lb3V0OiBjb25maWcuZ2V0TnBtVXBkYXRlVGltZW91dCgpXG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgbG9nZ2VyLmluZm8oJ1tVcGRhdGVNYW5hZ2VyXSBucG0gdXBkYXRlIGNvbXBsZXRlZCcsIHVwZGF0ZVJlc3VsdCk7XG4gICAgICBcbiAgICAgIC8vIFZlcmlmeSB1cGRhdGUgc3VjY2VlZGVkXG4gICAgICBjb25zdCB7IHN0ZG91dDogdmVyaWZ5T3V0cHV0IH0gPSBhd2FpdCBzYWZlRXhlYygnbnBtJywgWydsaXN0JywgJy1nJywgJ0Bkb2xsaG91c2VtY3AvbWNwLXNlcnZlcicsICctLWRlcHRoPTAnXSwge1xuICAgICAgICBjd2Q6IHRoaXMucm9vdERpcixcbiAgICAgICAgdGltZW91dDogMzAwMDBcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICBjb25zdCB2ZXJzaW9uTWF0Y2ggPSB2ZXJpZnlPdXRwdXQubWF0Y2goL0Bkb2xsaG91c2VtY3BcXC9tY3Atc2VydmVyQChcXGQrXFwuXFxkK1xcLlxcZCspLyk7XG4gICAgICBjb25zdCBpbnN0YWxsZWRWZXJzaW9uID0gdmVyc2lvbk1hdGNoID8gdmVyc2lvbk1hdGNoWzFdIDogJ3Vua25vd24nO1xuICAgICAgXG4gICAgICBpZiAoaW5zdGFsbGVkVmVyc2lvbiAhPT0gbGF0ZXN0VmVyc2lvbikge1xuICAgICAgICBsb2dnZXIud2FybihgW1VwZGF0ZU1hbmFnZXJdIFZlcnNpb24gbWlzbWF0Y2ggYWZ0ZXIgdXBkYXRlLiBFeHBlY3RlZDogJHtsYXRlc3RWZXJzaW9ufSwgR290OiAke2luc3RhbGxlZFZlcnNpb259YCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4pyFICoqVXBkYXRlIENvbXBsZXRlISoqXFxuXFxuJyArXG4gICAgICAgICAgYFVwZGF0ZWQgZnJvbSB2JHtjdXJyZW50VmVyc2lvbn0gdG8gdiR7bGF0ZXN0VmVyc2lvbn1cXG5cXG5gICtcbiAgICAgICAgICAnKipXaGF0IHdhcyB1cGRhdGVkOioqXFxuJyArXG4gICAgICAgICAgJ+KAoiBEb2xsaG91c2VNQ1Agc2VydmVyIHBhY2thZ2VcXG4nICtcbiAgICAgICAgICAn4oCiIEFsbCBkZXBlbmRlbmNpZXNcXG5cXG4nICtcbiAgICAgICAgICAnKipOZXh0IFN0ZXBzOioqXFxuJyArXG4gICAgICAgICAgJzEuIFRoZSBzZXJ2ZXIgd2lsbCByZXN0YXJ0IGF1dG9tYXRpY2FsbHlcXG4nICtcbiAgICAgICAgICAnMi4gQ2hlY2sgYGdldF9zZXJ2ZXJfc3RhdHVzYCB0byB2ZXJpZnkgdGhlIG5ldyB2ZXJzaW9uXFxuJyArXG4gICAgICAgICAgJzMuIFRlc3QgeW91ciBwZXJzb25hcyB0byBlbnN1cmUgZXZlcnl0aGluZyB3b3Jrc1xcblxcbicgK1xuICAgICAgICAgICfwn5KhICoqVGlwOioqIElmIHlvdSBlbmNvdW50ZXIgaXNzdWVzLCB1c2UgYHJvbGxiYWNrX3VwZGF0ZSB0cnVlYCB0byByZXN0b3JlIHRoZSBwcmV2aW91cyB2ZXJzaW9uLidcbiAgICAgIH07XG4gICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdbVXBkYXRlTWFuYWdlcl0gbnBtIHVwZGF0ZSBmYWlsZWQ6JywgZXJyb3IpO1xuICAgICAgY29uc3QgZXJyb3JNZXNzYWdlID0gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpO1xuICAgICAgXG4gICAgICByZXR1cm4ge1xuICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ+KdjCAqKlVwZGF0ZSBGYWlsZWQqKlxcblxcbicgK1xuICAgICAgICAgICdFcnJvcjogJyArIGVycm9yTWVzc2FnZSArICdcXG5cXG4nICtcbiAgICAgICAgICAnKipUcm91Ymxlc2hvb3Rpbmc6KipcXG4nICtcbiAgICAgICAgICAnMS4gRW5zdXJlIHlvdSBoYXZlIHBlcm1pc3Npb24gdG8gdXBkYXRlIGdsb2JhbCBucG0gcGFja2FnZXNcXG4nICtcbiAgICAgICAgICAnMi4gVHJ5IHJ1bm5pbmcgd2l0aCBzdWRvIGlmIG9uIG1hY09TL0xpbnV4XFxuJyArXG4gICAgICAgICAgJzMuIENoZWNrIHlvdXIgaW50ZXJuZXQgY29ubmVjdGlvblxcbicgK1xuICAgICAgICAgICc0LiBWZXJpZnkgbnBtIHJlZ2lzdHJ5IGlzIGFjY2Vzc2libGVcXG5cXG4nICtcbiAgICAgICAgICAnKipNYW51YWwgVXBkYXRlOioqXFxuJyArXG4gICAgICAgICAgJ2BgYFxcbicgK1xuICAgICAgICAgICducG0gdXBkYXRlIC1nIEBkb2xsaG91c2VtY3AvbWNwLXNlcnZlclxcbicgK1xuICAgICAgICAgICdgYGAnXG4gICAgICB9O1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIFJvbGxiYWNrIG5wbSBpbnN0YWxsYXRpb25cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcm9sbGJhY2tOcG1JbnN0YWxsYXRpb24oZm9yY2U6IGJvb2xlYW4sIHBlcnNvbmFJbmRpY2F0b3I6IHN0cmluZyA9ICcnKTogUHJvbWlzZTx7IHRleHQ6IHN0cmluZyB9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGxvZ2dlci5pbmZvKCdbVXBkYXRlTWFuYWdlcl0gU3RhcnRpbmcgbnBtIHJvbGxiYWNrIHByb2Nlc3MnKTtcbiAgICAgIFxuICAgICAgLy8gR2V0IG5wbSBiYWNrdXAgbWFuaWZlc3QgZnJvbSBjb25maWd1cmF0aW9uXG4gICAgICBjb25zdCBjb25maWcgPSBVcGRhdGVDb25maWdNYW5hZ2VyLmdldEluc3RhbmNlKCk7XG4gICAgICBjb25zdCBucG1CYWNrdXBzRGlyID0gY29uZmlnLmdldE5wbUJhY2t1cERpcigpO1xuICAgICAgY29uc3QgbWFuaWZlc3RQYXRoID0gcGF0aC5qb2luKG5wbUJhY2t1cHNEaXIsICdtYW5pZmVzdC5qc29uJyk7XG4gICAgICBcbiAgICAgIGxldCBtYW5pZmVzdDtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShtYW5pZmVzdFBhdGgsICd1dGYtOCcpO1xuICAgICAgICBtYW5pZmVzdCA9IEpTT04ucGFyc2UoY29udGVudCk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4p2MICoqTm8gTlBNIEJhY2t1cHMgRm91bmQqKlxcblxcbicgK1xuICAgICAgICAgICAgJ1RoZXJlIGFyZSBubyBucG0gYmFja3VwcyBhdmFpbGFibGUgdG8gcmVzdG9yZS5cXG5cXG4nICtcbiAgICAgICAgICAgICdCYWNrdXBzIGFyZSBjcmVhdGVkIGF1dG9tYXRpY2FsbHkgd2hlbiB5b3UgcnVuIGB1cGRhdGVfc2VydmVyIHRydWVgIHdpdGggbnBtIGluc3RhbGxhdGlvbnMuJ1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICBpZiAoIW1hbmlmZXN0LmJhY2t1cHMgfHwgbWFuaWZlc3QuYmFja3Vwcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ+KdjCAqKk5vIE5QTSBCYWNrdXBzIEZvdW5kKipcXG5cXG4nICtcbiAgICAgICAgICAgICdUaGUgYmFja3VwIG1hbmlmZXN0IGlzIGVtcHR5LlxcblxcbicgK1xuICAgICAgICAgICAgJ0JhY2t1cHMgYXJlIGNyZWF0ZWQgYXV0b21hdGljYWxseSB3aGVuIHlvdSBydW4gYHVwZGF0ZV9zZXJ2ZXIgdHJ1ZWAuJ1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBHZXQgbGF0ZXN0IGJhY2t1cFxuICAgICAgY29uc3QgbGF0ZXN0QmFja3VwID0gbWFuaWZlc3QuYmFja3Vwc1swXTtcbiAgICAgIFxuICAgICAgLy8gQ2hlY2sgaWYgcm9sbGJhY2sgaXMgbmVlZGVkXG4gICAgICBpZiAoIWZvcmNlKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgLy8gVGVzdCBpZiB0aGUgc2VydmVyIGlzIHdvcmtpbmdcbiAgICAgICAgICBhd2FpdCB0aGlzLnZlcnNpb25NYW5hZ2VyLmdldEN1cnJlbnRWZXJzaW9uKCk7XG4gICAgICAgICAgXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4pqg77iPICoqUm9sbGJhY2sgQ29uZmlybWF0aW9uIFJlcXVpcmVkKipcXG5cXG4nICtcbiAgICAgICAgICAgICAgJ1RoZSBzZXJ2ZXIgYXBwZWFycyB0byBiZSB3b3JraW5nIG5vcm1hbGx5LlxcblxcbicgK1xuICAgICAgICAgICAgICBgKipMYXRlc3QgQmFja3VwOioqICR7bGF0ZXN0QmFja3VwLnRpbWVzdGFtcH1cXG5gICtcbiAgICAgICAgICAgICAgYCoqQmFja3VwIFZlcnNpb246KiogJHtsYXRlc3RCYWNrdXAudmVyc2lvbiB8fCAnVW5rbm93bid9XFxuXFxuYCArXG4gICAgICAgICAgICAgICdUbyBmb3JjZSByb2xsYmFjayBhbnl3YXksIHVzZTogYHJvbGxiYWNrX3VwZGF0ZSB0cnVlYFxcblxcbicgK1xuICAgICAgICAgICAgICAn4pqg77iPICoqV2FybmluZzoqKiBUaGlzIHdpbGwgcmVzdG9yZSB0aGUgbnBtIHBhY2thZ2UgdG8gdGhlIGJhY2t1cCBzdGF0ZS4nXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gU2VydmVyIGlzIGJyb2tlbiwgcHJvY2VlZCB3aXRoIHJvbGxiYWNrXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gR2V0IG5wbSBnbG9iYWwgcGF0aFxuICAgICAgY29uc3QgbnBtR2xvYmFsUGF0aCA9IEluc3RhbGxhdGlvbkRldGVjdG9yLmdldE5wbUdsb2JhbFBhdGgoKTtcbiAgICAgIGlmICghbnBtR2xvYmFsUGF0aCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4p2MICoqUm9sbGJhY2sgRmFpbGVkKipcXG5cXG4nICtcbiAgICAgICAgICAgICdDb3VsZCBub3QgZGV0ZXJtaW5lIG5wbSBnbG9iYWwgaW5zdGFsbGF0aW9uIHBhdGguXFxuXFxuJyArXG4gICAgICAgICAgICAnUGxlYXNlIHJlaW5zdGFsbCBtYW51YWxseTpcXG4nICtcbiAgICAgICAgICAgICdgYGBcXG4nICtcbiAgICAgICAgICAgICducG0gaW5zdGFsbCAtZyBAZG9sbGhvdXNlbWNwL21jcC1zZXJ2ZXJAJyArIChsYXRlc3RCYWNrdXAudmVyc2lvbiB8fCAnbGF0ZXN0JykgKyAnXFxuJyArXG4gICAgICAgICAgICAnYGBgJ1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICBsb2dnZXIuaW5mbyhgW1VwZGF0ZU1hbmFnZXJdIFJlc3RvcmluZyBucG0gYmFja3VwIGZyb206ICR7bGF0ZXN0QmFja3VwLnBhdGh9YCk7XG4gICAgICBcbiAgICAgIC8vIEZpcnN0IHZhbGlkYXRlIHRoYXQgdGhlIGJhY2t1cCBpcyByZXN0b3JhYmxlXG4gICAgICBjb25zdCBiYWNrdXBQYWNrYWdlUGF0aCA9IHBhdGguam9pbihsYXRlc3RCYWNrdXAucGF0aCwgJ3BhY2thZ2UnKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZzLmFjY2VzcyhiYWNrdXBQYWNrYWdlUGF0aCk7XG4gICAgICAgIGNvbnN0IHBhY2thZ2VKc29uUGF0aCA9IHBhdGguam9pbihiYWNrdXBQYWNrYWdlUGF0aCwgJ3BhY2thZ2UuanNvbicpO1xuICAgICAgICBhd2FpdCBmcy5hY2Nlc3MocGFja2FnZUpzb25QYXRoKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfinYwgKipCYWNrdXAgVmFsaWRhdGlvbiBGYWlsZWQqKlxcblxcbicgK1xuICAgICAgICAgICAgJ1RoZSBiYWNrdXAgYXBwZWFycyB0byBiZSBjb3JydXB0ZWQgb3IgaW5jb21wbGV0ZS5cXG5cXG4nICtcbiAgICAgICAgICAgICcqKkRldGFpbHM6KipcXG4nICtcbiAgICAgICAgICAgIGBCYWNrdXAgcGF0aDogJHtsYXRlc3RCYWNrdXAucGF0aH1cXG5gICtcbiAgICAgICAgICAgIGBFcnJvcjogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9XFxuXFxuYCArXG4gICAgICAgICAgICAnKipNYW51YWwgUmVjb3Zlcnk6KipcXG4nICtcbiAgICAgICAgICAgICdUcnkgYW5vdGhlciBiYWNrdXAgb3IgcmVpbnN0YWxsIG1hbnVhbGx5OlxcbicgK1xuICAgICAgICAgICAgJ2BgYGJhc2hcXG4nICtcbiAgICAgICAgICAgICducG0gaW5zdGFsbCAtZyBAZG9sbGhvdXNlbWNwL21jcC1zZXJ2ZXJAJyArIChsYXRlc3RCYWNrdXAudmVyc2lvbiB8fCAnMS40LjAnKSArICdcXG4nICtcbiAgICAgICAgICAgICdgYGAnXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFVzZSB0cmFuc2FjdGlvbiBmb3IgYXRvbWljIG9wZXJhdGlvbnNcbiAgICAgIGNvbnN0IHRlbXBQYXRoID0gYCR7bnBtR2xvYmFsUGF0aH0udG1wLSR7RGF0ZS5ub3coKX1gO1xuICAgICAgY29uc3QgYmFja3VwUGF0aCA9IGAke25wbUdsb2JhbFBhdGh9LmJhY2t1cC0ke0RhdGUubm93KCl9YDtcbiAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gRmlsZU9wZXJhdGlvbnMuY3JlYXRlVHJhbnNhY3Rpb24oKTtcbiAgICAgIFxuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gU3RlcCAxOiBDb3B5IGJhY2t1cCB0byB0ZW1wb3JhcnkgbG9jYXRpb25cbiAgICAgICAgYXdhaXQgdHJhbnNhY3Rpb24uYWRkQ29weShiYWNrdXBQYWNrYWdlUGF0aCwgdGVtcFBhdGgpO1xuICAgICAgICBcbiAgICAgICAgLy8gU3RlcCAyOiBNb3ZlIGN1cnJlbnQgaW5zdGFsbGF0aW9uIHRvIGJhY2t1cCAoYXRvbWljKVxuICAgICAgICBhd2FpdCB0cmFuc2FjdGlvbi5hZGRNb3ZlKG5wbUdsb2JhbFBhdGgsIGJhY2t1cFBhdGgpO1xuICAgICAgICBcbiAgICAgICAgLy8gU3RlcCAzOiBNb3ZlIHRlbXAgdG8gZmluYWwgbG9jYXRpb24gKGF0b21pYylcbiAgICAgICAgYXdhaXQgdHJhbnNhY3Rpb24uYWRkTW92ZSh0ZW1wUGF0aCwgbnBtR2xvYmFsUGF0aCk7XG4gICAgICAgIFxuICAgICAgICAvLyBBbGwgb3BlcmF0aW9ucyBzdWNjZXNzZnVsLCBjb21taXQgdGhlIHRyYW5zYWN0aW9uXG4gICAgICAgIHRyYW5zYWN0aW9uLmNvbW1pdCgpO1xuICAgICAgICBcbiAgICAgICAgLy8gU3RlcCA0OiBDbGVhbiB1cCBvbGQgYmFja3VwIChub3QgcGFydCBvZiB0cmFuc2FjdGlvbilcbiAgICAgICAgYXdhaXQgZnMucm0oYmFja3VwUGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pLmNhdGNoKCgpID0+IHtcbiAgICAgICAgICBsb2dnZXIud2FybihgW1VwZGF0ZU1hbmFnZXJdIEZhaWxlZCB0byBjbGVhbnVwIGJhY2t1cCBhdDogJHtiYWNrdXBQYXRofWApO1xuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKHJvbGxiYWNrRXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdbVXBkYXRlTWFuYWdlcl0gUm9sbGJhY2sgb3BlcmF0aW9uIGZhaWxlZCwgYXR0ZW1wdGluZyByZWNvdmVyeTonLCByb2xsYmFja0Vycm9yKTtcbiAgICAgICAgXG4gICAgICAgIC8vIFJvbGxiYWNrIGFsbCBvcGVyYXRpb25zXG4gICAgICAgIGlmICh0cmFuc2FjdGlvbi5oYXNPcGVyYXRpb25zKCkpIHtcbiAgICAgICAgICBhd2FpdCB0cmFuc2FjdGlvbi5yb2xsYmFjaygpO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAvLyBBZGRpdGlvbmFsIHJlY292ZXJ5IGF0dGVtcHRcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAvLyBDaGVjayBpZiBucG0gcGF0aCBleGlzdHMgYW5kIGlzIGFjY2Vzc2libGVcbiAgICAgICAgICBhd2FpdCBmcy5hY2Nlc3MobnBtR2xvYmFsUGF0aCk7XG4gICAgICAgICAgbG9nZ2VyLmluZm8oJ1tVcGRhdGVNYW5hZ2VyXSBOUE0gaW5zdGFsbGF0aW9uIGFwcGVhcnMgaW50YWN0IGFmdGVyIHJvbGxiYWNrJyk7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIFRyeSB0byByZXN0b3JlIGZyb20gYmFja3VwIGlmIG1haW4gcGF0aCBpcyBtaXNzaW5nXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IGZzLnJlbmFtZShiYWNrdXBQYXRoLCBucG1HbG9iYWxQYXRoKTtcbiAgICAgICAgICAgIGxvZ2dlci5pbmZvKCdbVXBkYXRlTWFuYWdlcl0gUmVzdG9yZWQgZnJvbSBiYWNrdXAgcGF0aCcpO1xuICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgbG9nZ2VyLmVycm9yKCdbVXBkYXRlTWFuYWdlcl0gQ29tcGxldGUgcm9sbGJhY2sgZmFpbHVyZSAtIG1hbnVhbCBpbnRlcnZlbnRpb24gcmVxdWlyZWQnKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIHRocm93IHJvbGxiYWNrRXJyb3I7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4pyFICoqTlBNIFJvbGxiYWNrIENvbXBsZXRlISoqXFxuXFxuJyArXG4gICAgICAgICAgYFJlc3RvcmVkIGZyb20gYmFja3VwOiAke2xhdGVzdEJhY2t1cC50aW1lc3RhbXB9XFxuYCArXG4gICAgICAgICAgYEJhY2t1cCB2ZXJzaW9uOiAke2xhdGVzdEJhY2t1cC52ZXJzaW9uIHx8ICdVbmtub3duJ31cXG5cXG5gICtcbiAgICAgICAgICAnKipXaGF0IHdhcyByZXN0b3JlZDoqKlxcbicgK1xuICAgICAgICAgICfigKIgTlBNIHBhY2thZ2UgZmlsZXNcXG4nICtcbiAgICAgICAgICAn4oCiIEFsbCBkZXBlbmRlbmNpZXNcXG5cXG4nICtcbiAgICAgICAgICAnKipOZXh0IFN0ZXBzOioqXFxuJyArXG4gICAgICAgICAgJzEuIFRoZSBzZXJ2ZXIgd2lsbCByZXN0YXJ0IGF1dG9tYXRpY2FsbHlcXG4nICtcbiAgICAgICAgICAnMi4gQ2hlY2sgYGdldF9zZXJ2ZXJfc3RhdHVzYCB0byB2ZXJpZnkgdGhlIHZlcnNpb25cXG4nICtcbiAgICAgICAgICAnMy4gVGVzdCB5b3VyIHBlcnNvbmFzIHRvIGVuc3VyZSBldmVyeXRoaW5nIHdvcmtzJ1xuICAgICAgfTtcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ1tVcGRhdGVNYW5hZ2VyXSBucG0gcm9sbGJhY2sgZmFpbGVkOicsIGVycm9yKTtcbiAgICAgIGNvbnN0IGVycm9yTWVzc2FnZSA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKTtcbiAgICAgIFxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfinYwgKipOUE0gUm9sbGJhY2sgRmFpbGVkKipcXG5cXG4nICtcbiAgICAgICAgICAnRXJyb3I6ICcgKyBlcnJvck1lc3NhZ2UgKyAnXFxuXFxuJyArXG4gICAgICAgICAgJyoqTWFudWFsIFJlY292ZXJ5OioqXFxuJyArXG4gICAgICAgICAgJzEuIENoZWNrIHRoZSBiYWNrdXBzIGRpcmVjdG9yeTogfi8uZG9sbGhvdXNlL2JhY2t1cHMvbnBtL1xcbicgK1xuICAgICAgICAgICcyLiBSZWluc3RhbGwgYSBzcGVjaWZpYyB2ZXJzaW9uOlxcbicgK1xuICAgICAgICAgICcgICBgYGBcXG4nICtcbiAgICAgICAgICAnICAgbnBtIGluc3RhbGwgLWcgQGRvbGxob3VzZW1jcC9tY3Atc2VydmVyQDEuNC4wXFxuJyArXG4gICAgICAgICAgJyAgIGBgYFxcbicgK1xuICAgICAgICAgICczLiBDb250YWN0IHN1cHBvcnQgaWYgaXNzdWVzIHBlcnNpc3QnXG4gICAgICB9O1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENvcHkgZGlyZWN0b3J5IHJlY3Vyc2l2ZWx5IHdpdGggcHJvZ3Jlc3MgcmVwb3J0aW5nXG4gICAqIEBkZXByZWNhdGVkIFVzZSBGaWxlT3BlcmF0aW9ucy5jb3B5RGlyZWN0b3J5IGluc3RlYWRcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgY29weURpcmVjdG9yeShzcmM6IHN0cmluZywgZGVzdDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgRmlsZU9wZXJhdGlvbnMuY29weURpcmVjdG9yeShzcmMsIGRlc3QsIHtcbiAgICAgIGV4Y2x1ZGVQYXR0ZXJuczogWycuZ2l0JywgJ25vZGVfbW9kdWxlcyddLFxuICAgICAgb25Qcm9ncmVzczogKGNvcGllZCwgdG90YWwsIGZpbGUpID0+IHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKGBbVXBkYXRlTWFuYWdlcl0gQ29weWluZyBmaWxlczogJHtjb3BpZWR9LyR7dG90YWx9IC0gJHtwYXRoLmJhc2VuYW1lKGZpbGUpfWApO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogQ29udmVydCBucG0gaW5zdGFsbGF0aW9uIHRvIGdpdCBpbnN0YWxsYXRpb25cbiAgICovXG4gIGFzeW5jIGNvbnZlcnRUb0dpdEluc3RhbGxhdGlvbih0YXJnZXREaXI/OiBzdHJpbmcsIGNvbmZpcm06IGJvb2xlYW4gPSBmYWxzZSwgcGVyc29uYUluZGljYXRvcjogc3RyaW5nID0gJycpOiBQcm9taXNlPHsgdGV4dDogc3RyaW5nIH0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgaW5zdGFsbGF0aW9uVHlwZSA9IEluc3RhbGxhdGlvbkRldGVjdG9yLmdldEluc3RhbGxhdGlvblR5cGUoKTtcbiAgICAgIFxuICAgICAgaWYgKGluc3RhbGxhdGlvblR5cGUgPT09ICdnaXQnKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfimqDvuI8gKipBbHJlYWR5IGEgR2l0IEluc3RhbGxhdGlvbioqXFxuXFxuJyArXG4gICAgICAgICAgICAnVGhpcyBzZXJ2ZXIgaXMgYWxyZWFkeSBydW5uaW5nIGZyb20gYSBnaXQgaW5zdGFsbGF0aW9uLlxcbicgK1xuICAgICAgICAgICAgJ05vIGNvbnZlcnNpb24gbmVlZGVkLidcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgaWYgKGluc3RhbGxhdGlvblR5cGUgPT09ICd1bmtub3duJykge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4p2MICoqSW5zdGFsbGF0aW9uIFR5cGUgVW5rbm93bioqXFxuXFxuJyArXG4gICAgICAgICAgICAnQ2Fubm90IGRldGVybWluZSB0aGUgY3VycmVudCBpbnN0YWxsYXRpb24gdHlwZS5cXG4nICtcbiAgICAgICAgICAgICdQbGVhc2UgY2hlY2sgeW91ciBpbnN0YWxsYXRpb24gbWFudWFsbHkuJ1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBEZWZhdWx0IHRhcmdldCBkaXJlY3RvcnlcbiAgICAgIGNvbnN0IGRlZmF1bHRUYXJnZXREaXIgPSBwYXRoLmpvaW4ocHJvY2Vzcy5lbnYuSE9NRSB8fCAnJywgJy5kb2xsaG91c2UnLCAnbWNwLXNlcnZlci1naXQnKTtcbiAgICAgIGNvbnN0IGdpdFRhcmdldERpciA9IHRhcmdldERpciB8fCBkZWZhdWx0VGFyZ2V0RGlyO1xuICAgICAgXG4gICAgICBpZiAoIWNvbmZpcm0pIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ/CflIQgKipDb252ZXJ0IHRvIEdpdCBJbnN0YWxsYXRpb24qKlxcblxcbicgK1xuICAgICAgICAgICAgJyoqVGhpcyB3aWxsOioqXFxuJyArXG4gICAgICAgICAgICBgMS4gQ2xvbmUgRG9sbGhvdXNlTUNQIHRvOiAke2dpdFRhcmdldERpcn1cXG5gICtcbiAgICAgICAgICAgICcyLiBDb3B5IHlvdXIgcG9ydGZvbGlvIGFuZCBzZXR0aW5nc1xcbicgK1xuICAgICAgICAgICAgJzMuIEJ1aWxkIHRoZSBUeXBlU2NyaXB0IGNvZGVcXG4nICtcbiAgICAgICAgICAgICc0LiBQcm92aWRlIENsYXVkZSBEZXNrdG9wIGNvbmZpZ3VyYXRpb25cXG5cXG4nICtcbiAgICAgICAgICAgICcqKkJlbmVmaXRzIG9mIEdpdCBJbnN0YWxsYXRpb246KipcXG4nICtcbiAgICAgICAgICAgICfigKIgRnVsbCBjb250cm9sIG92ZXIgdXBkYXRlc1xcbicgK1xuICAgICAgICAgICAgJ+KAoiBBY2Nlc3MgdG8gZGV2ZWxvcG1lbnQgYnJhbmNoZXNcXG4nICtcbiAgICAgICAgICAgICfigKIgQWJpbGl0eSB0byBjb250cmlidXRlIGNoYW5nZXNcXG4nICtcbiAgICAgICAgICAgICfigKIgUm9sbGJhY2sgdG8gYW55IGNvbW1pdFxcblxcbicgK1xuICAgICAgICAgICAgJyoqVG8gcHJvY2VlZDoqKlxcbicgK1xuICAgICAgICAgICAgJ2Bjb252ZXJ0X3RvX2dpdF9pbnN0YWxsYXRpb24gdHJ1ZWBcXG5cXG4nICtcbiAgICAgICAgICAgICcqKlRvIHVzZSBjdXN0b20gZGlyZWN0b3J5OioqXFxuJyArXG4gICAgICAgICAgICAnYGNvbnZlcnRfdG9fZ2l0X2luc3RhbGxhdGlvbiBcIi9wYXRoL3RvL2RpclwiIHRydWVgJ1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICBsb2dnZXIuaW5mbyhgW1VwZGF0ZU1hbmFnZXJdIFN0YXJ0aW5nIGNvbnZlcnNpb24gdG8gZ2l0IGluc3RhbGxhdGlvbiBhdDogJHtnaXRUYXJnZXREaXJ9YCk7XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGlmIHRhcmdldCBkaXJlY3RvcnkgYWxyZWFkeSBleGlzdHNcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZzLmFjY2VzcyhnaXRUYXJnZXREaXIpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4p2MICoqVGFyZ2V0IERpcmVjdG9yeSBFeGlzdHMqKlxcblxcbicgK1xuICAgICAgICAgICAgYFRoZSBkaXJlY3RvcnkgJHtnaXRUYXJnZXREaXJ9IGFscmVhZHkgZXhpc3RzLlxcblxcbmAgK1xuICAgICAgICAgICAgJyoqT3B0aW9uczoqKlxcbicgK1xuICAgICAgICAgICAgJzEuIFJlbW92ZSB0aGUgZXhpc3RpbmcgZGlyZWN0b3J5IGZpcnN0XFxuJyArXG4gICAgICAgICAgICAnMi4gQ2hvb3NlIGEgZGlmZmVyZW50IHRhcmdldCBkaXJlY3RvcnlcXG4nICtcbiAgICAgICAgICAgICczLiBVc2UgdGhlIGV4aXN0aW5nIGdpdCBpbnN0YWxsYXRpb24nXG4gICAgICAgIH07XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gRGlyZWN0b3J5IGRvZXNuJ3QgZXhpc3QsIGdvb2QgdG8gcHJvY2VlZFxuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBQcm9ncmVzcyBtZXNzYWdlIGJ1aWxkZXJcbiAgICAgIGxldCBwcm9ncmVzc1N0ZXBzID0gcGVyc29uYUluZGljYXRvciArICfwn5SEICoqR2l0IEluc3RhbGxhdGlvbiBQcm9ncmVzcyoqXFxuXFxuJztcbiAgICAgIHByb2dyZXNzU3RlcHMgKz0gJyoqU3RlcHM6KipcXG4nO1xuICAgICAgcHJvZ3Jlc3NTdGVwcyArPSAn4o+zIENsb25pbmcgcmVwb3NpdG9yeS4uLlxcbic7XG4gICAgICBwcm9ncmVzc1N0ZXBzICs9ICfij7MgSW5zdGFsbGluZyBkZXBlbmRlbmNpZXMuLi5cXG4nO1xuICAgICAgcHJvZ3Jlc3NTdGVwcyArPSAn4o+zIEJ1aWxkaW5nIFR5cGVTY3JpcHQuLi5cXG4nO1xuICAgICAgcHJvZ3Jlc3NTdGVwcyArPSAn4o+zIFNldHRpbmcgdXAgY29uZmlndXJhdGlvbi4uLlxcblxcbic7XG4gICAgICBwcm9ncmVzc1N0ZXBzICs9ICcqKkN1cnJlbnQgU3RlcDoqKiBDbG9uaW5nIHJlcG9zaXRvcnlcXG4nO1xuICAgICAgcHJvZ3Jlc3NTdGVwcyArPSAnYGBgXFxuJztcbiAgICAgIHByb2dyZXNzU3RlcHMgKz0gYFRhcmdldDogJHtnaXRUYXJnZXREaXJ9XFxuYDtcbiAgICAgIHByb2dyZXNzU3RlcHMgKz0gJ1RoaXMgbWF5IHRha2UgYSBmZXcgbWludXRlcyBkZXBlbmRpbmcgb24geW91ciBjb25uZWN0aW9uLi4uXFxuJztcbiAgICAgIHByb2dyZXNzU3RlcHMgKz0gJ2BgYFxcbic7XG4gICAgICBcbiAgICAgIC8vIEdldCBjb25maWd1cmF0aW9uXG4gICAgICBjb25zdCBjb25maWcgPSBVcGRhdGVDb25maWdNYW5hZ2VyLmdldEluc3RhbmNlKCk7XG4gICAgICBcbiAgICAgIC8vIFN0ZXAgMTogQ2xvbmUgdGhlIHJlcG9zaXRvcnlcbiAgICAgIGxvZ2dlci5pbmZvKCdbVXBkYXRlTWFuYWdlcl0gQ2xvbmluZyByZXBvc2l0b3J5Li4uJyk7XG4gICAgICBcbiAgICAgIC8vIFNFQ1VSSVRZIEZJWDogVmFsaWRhdGUgZ2l0VGFyZ2V0RGlyIHRvIHByZXZlbnQgY29tbWFuZCBpbmplY3Rpb25cbiAgICAgIC8vIFByZXZpb3VzbHk6IGdpdFRhcmdldERpciBwYXNzZWQgZGlyZWN0bHkgdG8gZ2l0IGNsb25lIGNvbW1hbmRcbiAgICAgIC8vIE5vdzogUmVqZWN0IHBhdGhzIHN0YXJ0aW5nIHdpdGggJy0tJyB0byBwcmV2ZW50IGdpdCBvcHRpb24gaW5qZWN0aW9uXG4gICAgICBpZiAoZ2l0VGFyZ2V0RGlyLnN0YXJ0c1dpdGgoJy0tJykpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHRhcmdldCBkaXJlY3Rvcnk6IGNhbm5vdCBzdGFydCB3aXRoIGdpdCBvcHRpb25zJyk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIGF3YWl0IHNhZmVFeGVjKCdnaXQnLCBbJ2Nsb25lJywgJ2h0dHBzOi8vZ2l0aHViLmNvbS9Eb2xsaG91c2VNQ1AvbWNwLXNlcnZlci5naXQnLCBnaXRUYXJnZXREaXJdLCB7XG4gICAgICAgIHRpbWVvdXQ6IGNvbmZpZy5nZXRHaXRDbG9uZVRpbWVvdXQoKVxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIHByb2dyZXNzU3RlcHMgPSBwcm9ncmVzc1N0ZXBzLnJlcGxhY2UoJ+KPsyBDbG9uaW5nIHJlcG9zaXRvcnkuLi4nLCAn4pyFIFJlcG9zaXRvcnkgY2xvbmVkJyk7XG4gICAgICBwcm9ncmVzc1N0ZXBzID0gcHJvZ3Jlc3NTdGVwcy5yZXBsYWNlKCcqKkN1cnJlbnQgU3RlcDoqKiBDbG9uaW5nIHJlcG9zaXRvcnknLCAnKipDdXJyZW50IFN0ZXA6KiogSW5zdGFsbGluZyBkZXBlbmRlbmNpZXMnKTtcbiAgICAgIFxuICAgICAgLy8gU3RlcCAyOiBJbnN0YWxsIGRlcGVuZGVuY2llc1xuICAgICAgbG9nZ2VyLmluZm8oJ1tVcGRhdGVNYW5hZ2VyXSBJbnN0YWxsaW5nIGRlcGVuZGVuY2llcy4uLicpO1xuICAgICAgYXdhaXQgc2FmZUV4ZWMoJ25wbScsIFsnaW5zdGFsbCddLCB7XG4gICAgICAgIGN3ZDogZ2l0VGFyZ2V0RGlyLFxuICAgICAgICB0aW1lb3V0OiBjb25maWcuZ2V0TnBtSW5zdGFsbFRpbWVvdXQoKVxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIHByb2dyZXNzU3RlcHMgPSBwcm9ncmVzc1N0ZXBzLnJlcGxhY2UoJ+KPsyBJbnN0YWxsaW5nIGRlcGVuZGVuY2llcy4uLicsICfinIUgRGVwZW5kZW5jaWVzIGluc3RhbGxlZCcpO1xuICAgICAgcHJvZ3Jlc3NTdGVwcyA9IHByb2dyZXNzU3RlcHMucmVwbGFjZSgnKipDdXJyZW50IFN0ZXA6KiogSW5zdGFsbGluZyBkZXBlbmRlbmNpZXMnLCAnKipDdXJyZW50IFN0ZXA6KiogQnVpbGRpbmcgVHlwZVNjcmlwdCcpO1xuICAgICAgXG4gICAgICAvLyBTdGVwIDM6IEJ1aWxkIFR5cGVTY3JpcHRcbiAgICAgIGxvZ2dlci5pbmZvKCdbVXBkYXRlTWFuYWdlcl0gQnVpbGRpbmcgVHlwZVNjcmlwdC4uLicpO1xuICAgICAgYXdhaXQgc2FmZUV4ZWMoJ25wbScsIFsncnVuJywgJ2J1aWxkJ10sIHtcbiAgICAgICAgY3dkOiBnaXRUYXJnZXREaXIsXG4gICAgICAgIHRpbWVvdXQ6IGNvbmZpZy5nZXRCdWlsZFRpbWVvdXQoKVxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIHByb2dyZXNzU3RlcHMgPSBwcm9ncmVzc1N0ZXBzLnJlcGxhY2UoJ+KPsyBCdWlsZGluZyBUeXBlU2NyaXB0Li4uJywgJ+KchSBUeXBlU2NyaXB0IGJ1aWx0Jyk7XG4gICAgICBwcm9ncmVzc1N0ZXBzID0gcHJvZ3Jlc3NTdGVwcy5yZXBsYWNlKCcqKkN1cnJlbnQgU3RlcDoqKiBCdWlsZGluZyBUeXBlU2NyaXB0JywgJyoqQ3VycmVudCBTdGVwOioqIENvbmZpZ3VyYXRpb24nKTtcbiAgICAgIFxuICAgICAgLy8gU3RlcCA0OiBDb3B5IHBvcnRmb2xpb1xuICAgICAgY29uc3QgcG9ydGZvbGlvU291cmNlID0gcGF0aC5qb2luKHByb2Nlc3MuZW52LkhPTUUgfHwgJycsICcuZG9sbGhvdXNlJywgJ3BvcnRmb2xpbycpO1xuICAgICAgY29uc3QgcG9ydGZvbGlvVGFyZ2V0ID0gcGF0aC5qb2luKHByb2Nlc3MuZW52LkhPTUUgfHwgJycsICcuZG9sbGhvdXNlJywgJ3BvcnRmb2xpbycpO1xuICAgICAgXG4gICAgICBsb2dnZXIuaW5mbygnW1VwZGF0ZU1hbmFnZXJdIFBvcnRmb2xpbyB3aWxsIHJlbWFpbiBhdDogJyArIHBvcnRmb2xpb1RhcmdldCk7XG4gICAgICBcbiAgICAgIC8vIFN0ZXAgNTogR2VuZXJhdGUgQ2xhdWRlIERlc2t0b3AgY29uZmlnXG4gICAgICBjb25zdCBjb25maWdQYXRoID0gcGF0aC5qb2luKGdpdFRhcmdldERpciwgJ2Rpc3QnLCAnaW5kZXguanMnKTtcbiAgICAgIGNvbnN0IGNsYXVkZUNvbmZpZyA9IHtcbiAgICAgICAgbWNwU2VydmVyczoge1xuICAgICAgICAgIGRvbGxob3VzZW1jcDoge1xuICAgICAgICAgICAgY29tbWFuZDogJ25vZGUnLFxuICAgICAgICAgICAgYXJnczogW2NvbmZpZ1BhdGhdXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgXG4gICAgICByZXR1cm4ge1xuICAgICAgICB0ZXh0OiBwZXJzb25hSW5kaWNhdG9yICsgJ+KchSAqKkdpdCBJbnN0YWxsYXRpb24gQ29tcGxldGUhKipcXG5cXG4nICtcbiAgICAgICAgICBgKipJbnN0YWxsYXRpb24gTG9jYXRpb246KiogJHtnaXRUYXJnZXREaXJ9XFxuXFxuYCArXG4gICAgICAgICAgJyoqTmV4dCBTdGVwczoqKlxcblxcbicgK1xuICAgICAgICAgICcxLiAqKlVwZGF0ZSBDbGF1ZGUgRGVza3RvcCBjb25maWd1cmF0aW9uOioqXFxuJyArXG4gICAgICAgICAgJyAgIGBgYGpzb25cXG4nICtcbiAgICAgICAgICBKU09OLnN0cmluZ2lmeShjbGF1ZGVDb25maWcsIG51bGwsIDIpICsgJ1xcbicgK1xuICAgICAgICAgICcgICBgYGBcXG5cXG4nICtcbiAgICAgICAgICAnMi4gKipSZXN0YXJ0IENsYXVkZSBEZXNrdG9wKipcXG5cXG4nICtcbiAgICAgICAgICAnMy4gKipWZXJpZnkgaW5zdGFsbGF0aW9uOioqXFxuJyArXG4gICAgICAgICAgJyAgIEFmdGVyIHJlc3RhcnQsIHJ1biBgZ2V0X3NlcnZlcl9zdGF0dXNgIHRvIGNvbmZpcm1cXG5cXG4nICtcbiAgICAgICAgICAnKipZb3VyIHBvcnRmb2xpbyByZW1haW5zIGF0OioqXFxuJyArXG4gICAgICAgICAgYCAgICR7cG9ydGZvbGlvVGFyZ2V0fVxcblxcbmAgK1xuICAgICAgICAgICcqKlRvIHVwZGF0ZSBpbiB0aGUgZnV0dXJlOioqXFxuJyArXG4gICAgICAgICAgJyAgIGBgYGJhc2hcXG4nICtcbiAgICAgICAgICBgICAgY2QgJHtnaXRUYXJnZXREaXJ9XFxuYCArXG4gICAgICAgICAgJyAgIGdpdCBwdWxsXFxuJyArXG4gICAgICAgICAgJyAgIG5wbSBpbnN0YWxsXFxuJyArXG4gICAgICAgICAgJyAgIG5wbSBydW4gYnVpbGRcXG4nICtcbiAgICAgICAgICAnICAgYGBgXFxuXFxuJyArXG4gICAgICAgICAgJ/CfkqEgKipUaXA6KiogWW91IGNhbiBub3cgdXNlIGB1cGRhdGVfc2VydmVyYCB0byB1cGRhdGUgdmlhIGdpdCEnXG4gICAgICB9O1xuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignW1VwZGF0ZU1hbmFnZXJdIEdpdCBjb252ZXJzaW9uIGZhaWxlZDonLCBlcnJvcik7XG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcik7XG4gICAgICBcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHRleHQ6IHBlcnNvbmFJbmRpY2F0b3IgKyAn4p2MICoqQ29udmVyc2lvbiBGYWlsZWQqKlxcblxcbicgK1xuICAgICAgICAgICdFcnJvcjogJyArIGVycm9yTWVzc2FnZSArICdcXG5cXG4nICtcbiAgICAgICAgICAnKipUcm91Ymxlc2hvb3Rpbmc6KipcXG4nICtcbiAgICAgICAgICAnMS4gRW5zdXJlIGdpdCBpcyBpbnN0YWxsZWRcXG4nICtcbiAgICAgICAgICAnMi4gQ2hlY2sgaW50ZXJuZXQgY29ubmVjdGlvblxcbicgK1xuICAgICAgICAgICczLiBWZXJpZnkgeW91IGhhdmUgd3JpdGUgcGVybWlzc2lvbnNcXG4nICtcbiAgICAgICAgICAnNC4gVHJ5IGEgZGlmZmVyZW50IHRhcmdldCBkaXJlY3RvcnknXG4gICAgICB9O1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBjdXJyZW50IHNlcnZlciBzdGF0dXNcbiAgICovXG4gIGFzeW5jIGdldFNlcnZlclN0YXR1cyhwZXJzb25hSW5kaWNhdG9yOiBzdHJpbmcgPSAnJyk6IFByb21pc2U8eyB0ZXh0OiBzdHJpbmcgfT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBjdXJyZW50VmVyc2lvbiA9IGF3YWl0IHRoaXMudmVyc2lvbk1hbmFnZXIuZ2V0Q3VycmVudFZlcnNpb24oKTtcbiAgICAgIGNvbnN0IGRlcGVuZGVuY2llcyA9IGF3YWl0IHRoaXMuZGVwZW5kZW5jeUNoZWNrZXIuY2hlY2tEZXBlbmRlbmNpZXMoKTtcbiAgICAgIGNvbnN0IGJhY2t1cHMgPSBhd2FpdCB0aGlzLmJhY2t1cE1hbmFnZXIubGlzdEJhY2t1cHMoKTtcbiAgICAgIGNvbnN0IHJhdGVMaW1pdFN0YXR1cyA9IHRoaXMudXBkYXRlQ2hlY2tlci5nZXRSYXRlTGltaXRTdGF0dXMoKTtcbiAgICAgIFxuICAgICAgLy8gR2V0IGluc3RhbGxhdGlvbiB0eXBlXG4gICAgICBjb25zdCBpbnN0YWxsYXRpb25UeXBlID0gSW5zdGFsbGF0aW9uRGV0ZWN0b3IuZ2V0SW5zdGFsbGF0aW9uVHlwZSgpO1xuICAgICAgY29uc3QgaW5zdGFsbGF0aW9uRGVzYyA9IEluc3RhbGxhdGlvbkRldGVjdG9yLmdldEluc3RhbGxhdGlvbkRlc2NyaXB0aW9uKCk7XG4gICAgICBcbiAgICAgIC8vIEdldCBnaXQgc3RhdHVzIChvbmx5IGZvciBnaXQgaW5zdGFsbGF0aW9ucylcbiAgICAgIGxldCBnaXRTdGF0dXMgPSAnTi9BJztcbiAgICAgIGxldCBnaXRCcmFuY2ggPSAnTi9BJztcbiAgICAgIGxldCBsYXN0Q29tbWl0ID0gJ04vQSc7XG4gICAgICBcbiAgICAgIGlmIChpbnN0YWxsYXRpb25UeXBlID09PSAnZ2l0Jykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHsgc3Rkb3V0OiBicmFuY2hPdXRwdXQgfSA9IGF3YWl0IHNhZmVFeGVjKCdnaXQnLCBbJ2JyYW5jaCcsICctLXNob3ctY3VycmVudCddLCB7IGN3ZDogdGhpcy5yb290RGlyIH0pO1xuICAgICAgICAgIGdpdEJyYW5jaCA9IGJyYW5jaE91dHB1dC50cmltKCkgfHwgJ2RldGFjaGVkJztcbiAgICAgICAgICBcbiAgICAgICAgICBjb25zdCB7IHN0ZG91dDogc3RhdHVzT3V0cHV0IH0gPSBhd2FpdCBzYWZlRXhlYygnZ2l0JywgWydzdGF0dXMnLCAnLS1wb3JjZWxhaW4nXSwgeyBjd2Q6IHRoaXMucm9vdERpciB9KTtcbiAgICAgICAgICBnaXRTdGF0dXMgPSBzdGF0dXNPdXRwdXQudHJpbSgpID8gJ01vZGlmaWVkJyA6ICdDbGVhbic7XG4gICAgICAgICAgXG4gICAgICAgICAgY29uc3QgeyBzdGRvdXQ6IGxvZ091dHB1dCB9ID0gYXdhaXQgc2FmZUV4ZWMoJ2dpdCcsIFsnbG9nJywgJy0xJywgJy0tb25lbGluZSddLCB7IGN3ZDogdGhpcy5yb290RGlyIH0pO1xuICAgICAgICAgIGxhc3RDb21taXQgPSBsb2dPdXRwdXQudHJpbSgpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvLyBHaXQgY29tbWFuZHMgZmFpbGVkLCB1c2UgZGVmYXVsdHNcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgXG4gICAgICBjb25zdCBzdGF0dXNQYXJ0cyA9IFtcbiAgICAgICAgcGVyc29uYUluZGljYXRvciArICfwn5OKICoqRG9sbGhvdXNlTUNQIFNlcnZlciBTdGF0dXMqKlxcblxcbicsXG4gICAgICAgICcqKlZlcnNpb24gSW5mb3JtYXRpb246KipcXG4nLFxuICAgICAgICBg4oCiIEN1cnJlbnQgVmVyc2lvbjogJHtjdXJyZW50VmVyc2lvbn1cXG5gLFxuICAgICAgICBg4oCiIEluc3RhbGxhdGlvbiBUeXBlOiAke2luc3RhbGxhdGlvblR5cGV9ICgke2luc3RhbGxhdGlvbkRlc2N9KVxcbmAsXG4gICAgICAgIGDigKIgR2l0IEJyYW5jaDogJHtnaXRCcmFuY2h9XFxuYCxcbiAgICAgICAgYOKAoiBHaXQgU3RhdHVzOiAke2dpdFN0YXR1c31cXG5gLFxuICAgICAgICBg4oCiIExhc3QgQ29tbWl0OiAke2xhc3RDb21taXR9XFxuXFxuYCxcbiAgICAgICAgJyoqRGVwZW5kZW5jaWVzOioqXFxuJyxcbiAgICAgICAgdGhpcy5kZXBlbmRlbmN5Q2hlY2tlci5mb3JtYXREZXBlbmRlbmN5U3RhdHVzKGRlcGVuZGVuY2llcyksXG4gICAgICAgICdcXG5cXG4qKkJhY2t1cHM6KipcXG4nLFxuICAgICAgICBg4oCiIFRvdGFsIEJhY2t1cHM6ICR7YmFja3Vwcy5sZW5ndGh9XFxuYFxuICAgICAgXTtcbiAgICAgIFxuICAgICAgaWYgKGJhY2t1cHMubGVuZ3RoID4gMCkge1xuICAgICAgICBzdGF0dXNQYXJ0cy5wdXNoKGDigKIgTGF0ZXN0IEJhY2t1cDogJHtiYWNrdXBzWzBdLnRpbWVzdGFtcH0gKHYke2JhY2t1cHNbMF0udmVyc2lvbiB8fCAndW5rbm93bid9KVxcbmApO1xuICAgICAgICBzdGF0dXNQYXJ0cy5wdXNoKGDigKIgT2xkZXN0IEJhY2t1cDogJHtiYWNrdXBzW2JhY2t1cHMubGVuZ3RoIC0gMV0udGltZXN0YW1wfVxcbmApO1xuICAgICAgfVxuICAgICAgXG4gICAgICBzdGF0dXNQYXJ0cy5wdXNoKFxuICAgICAgICAnXFxuKipSYXRlIExpbWl0IFN0YXR1czoqKlxcbicsXG4gICAgICAgIGDigKIgVXBkYXRlIENoZWNrcyBSZW1haW5pbmc6ICR7cmF0ZUxpbWl0U3RhdHVzLnJlbWFpbmluZ1JlcXVlc3RzfS8xMCBwZXIgaG91clxcbmAsXG4gICAgICAgIGDigKIgUmF0ZSBMaW1pdCBSZXNldHM6ICR7cmF0ZUxpbWl0U3RhdHVzLnJlc2V0VGltZS50b0xvY2FsZVRpbWVTdHJpbmcoKX1cXG5gXG4gICAgICApO1xuICAgICAgXG4gICAgICBpZiAoIXJhdGVMaW1pdFN0YXR1cy5hbGxvd2VkICYmIHJhdGVMaW1pdFN0YXR1cy53YWl0VGltZVNlY29uZHMpIHtcbiAgICAgICAgc3RhdHVzUGFydHMucHVzaChg4oCiIOKPsyBXYWl0ICR7cmF0ZUxpbWl0U3RhdHVzLndhaXRUaW1lU2Vjb25kc30gc2Vjb25kcyBiZWZvcmUgbmV4dCBjaGVja1xcbmApO1xuICAgICAgfVxuICAgICAgXG4gICAgICBzdGF0dXNQYXJ0cy5wdXNoKFxuICAgICAgICAnXFxuKipBdmFpbGFibGUgQ29tbWFuZHM6KipcXG4nLFxuICAgICAgICAn4oCiIGBjaGVja19mb3JfdXBkYXRlc2AgLSBDaGVjayBmb3IgbmV3IHZlcnNpb25zXFxuJyxcbiAgICAgICAgJ+KAoiBgdXBkYXRlX3NlcnZlciB0cnVlYCAtIFVwZGF0ZSB0byBsYXRlc3QgdmVyc2lvblxcbicsXG4gICAgICAgICfigKIgYHJvbGxiYWNrX3VwZGF0ZSB0cnVlYCAtIFJlc3RvcmUgZnJvbSBiYWNrdXBcXG4nXG4gICAgICApO1xuICAgICAgXG4gICAgICByZXR1cm4geyB0ZXh0OiBzdGF0dXNQYXJ0cy5qb2luKCcnKSB9O1xuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnN0IGVycm9yTWVzc2FnZSA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKTtcbiAgICAgIFxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdGV4dDogcGVyc29uYUluZGljYXRvciArICfinYwgKipTdGF0dXMgQ2hlY2sgRmFpbGVkKipcXG5cXG4nICtcbiAgICAgICAgICAnRXJyb3I6ICcgKyBlcnJvck1lc3NhZ2UgKyAnXFxuXFxuJyArXG4gICAgICAgICAgJ1RoZSBzZXJ2ZXIgbWF5IGJlIGluIGFuIGluY29uc2lzdGVudCBzdGF0ZS5cXG4nICtcbiAgICAgICAgICAnVHJ5IHJ1bm5pbmcgYHVwZGF0ZV9zZXJ2ZXIgdHJ1ZWAgdG8gZml4IGlzc3Vlcy4nXG4gICAgICB9O1xuICAgIH1cbiAgfVxufSJdfQ==
|