@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.
Files changed (272) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +494 -111
  3. package/data/agents/code-reviewer.md +8 -1
  4. package/data/agents/research-assistant.md +8 -1
  5. package/data/agents/task-manager.md +8 -1
  6. package/data/ensembles/business-advisor.md +8 -1
  7. package/data/ensembles/creative-studio.md +8 -1
  8. package/data/ensembles/development-team.md +8 -1
  9. package/data/ensembles/security-analysis-team.md +8 -1
  10. package/data/memories/conversation-history.md +8 -1
  11. package/data/memories/learning-progress.md +8 -1
  12. package/data/memories/project-context.md +8 -1
  13. package/data/personas/business-consultant.md +8 -1
  14. package/data/personas/creative-writer.md +8 -1
  15. package/data/personas/debug-detective.md +8 -1
  16. package/data/personas/eli5-explainer.md +8 -1
  17. package/data/personas/security-analyst.md +8 -1
  18. package/data/personas/technical-analyst.md +8 -1
  19. package/data/skills/code-review.md +8 -1
  20. package/data/skills/creative-writing.md +8 -1
  21. package/data/skills/data-analysis.md +8 -1
  22. package/data/skills/penetration-testing.md +8 -1
  23. package/data/skills/research.md +8 -1
  24. package/data/skills/threat-modeling.md +8 -1
  25. package/data/skills/translation.md +8 -1
  26. package/data/templates/code-documentation.md +8 -1
  27. package/data/templates/email-professional.md +8 -1
  28. package/data/templates/meeting-notes.md +8 -1
  29. package/data/templates/penetration-test-report.md +8 -1
  30. package/data/templates/project-brief.md +8 -1
  31. package/data/templates/report-executive.md +8 -1
  32. package/data/templates/security-vulnerability-report.md +8 -1
  33. package/data/templates/threat-assessment-report.md +8 -1
  34. package/dist/auth/GitHubAuthManager.d.ts +6 -1
  35. package/dist/auth/GitHubAuthManager.d.ts.map +1 -1
  36. package/dist/auth/GitHubAuthManager.js +45 -18
  37. package/dist/benchmarks/IndexPerformanceBenchmark.d.ts +98 -0
  38. package/dist/benchmarks/IndexPerformanceBenchmark.d.ts.map +1 -0
  39. package/dist/benchmarks/IndexPerformanceBenchmark.js +531 -0
  40. package/dist/cache/CollectionCache.d.ts.map +1 -1
  41. package/dist/cache/CollectionCache.js +13 -3
  42. package/dist/cache/CollectionIndexCache.d.ts +77 -0
  43. package/dist/cache/CollectionIndexCache.d.ts.map +1 -0
  44. package/dist/cache/CollectionIndexCache.js +349 -0
  45. package/dist/cache/LRUCache.d.ts +93 -0
  46. package/dist/cache/LRUCache.d.ts.map +1 -0
  47. package/dist/cache/LRUCache.js +299 -0
  48. package/dist/cache/index.d.ts +1 -0
  49. package/dist/cache/index.d.ts.map +1 -1
  50. package/dist/cache/index.js +2 -1
  51. package/dist/collection/CollectionBrowser.d.ts +21 -1
  52. package/dist/collection/CollectionBrowser.d.ts.map +1 -1
  53. package/dist/collection/CollectionBrowser.js +130 -10
  54. package/dist/collection/CollectionIndexManager.d.ts +151 -0
  55. package/dist/collection/CollectionIndexManager.d.ts.map +1 -0
  56. package/dist/collection/CollectionIndexManager.js +499 -0
  57. package/dist/collection/CollectionSearch.d.ts +55 -0
  58. package/dist/collection/CollectionSearch.d.ts.map +1 -1
  59. package/dist/collection/CollectionSearch.js +338 -13
  60. package/dist/collection/CollectionSeeder.d.ts.map +1 -1
  61. package/dist/collection/CollectionSeeder.js +38 -1
  62. package/dist/collection/ElementInstaller.d.ts +31 -0
  63. package/dist/collection/ElementInstaller.d.ts.map +1 -1
  64. package/dist/collection/ElementInstaller.js +77 -15
  65. package/dist/collection/PersonaSubmitter.d.ts +1 -1
  66. package/dist/collection/PersonaSubmitter.d.ts.map +1 -1
  67. package/dist/collection/PersonaSubmitter.js +2 -2
  68. package/dist/collection/index.d.ts +1 -0
  69. package/dist/collection/index.d.ts.map +1 -1
  70. package/dist/collection/index.js +2 -1
  71. package/dist/config/ConfigManager.d.ts +78 -0
  72. package/dist/config/ConfigManager.d.ts.map +1 -0
  73. package/dist/config/ConfigManager.js +216 -0
  74. package/dist/config/element-types.d.ts +135 -0
  75. package/dist/config/element-types.d.ts.map +1 -0
  76. package/dist/config/element-types.js +108 -0
  77. package/dist/config/index.d.ts +2 -0
  78. package/dist/config/index.d.ts.map +1 -1
  79. package/dist/config/index.js +3 -1
  80. package/dist/config/portfolio-constants.d.ts +83 -0
  81. package/dist/config/portfolio-constants.d.ts.map +1 -0
  82. package/dist/config/portfolio-constants.js +99 -0
  83. package/dist/elements/BaseElement.d.ts +14 -2
  84. package/dist/elements/BaseElement.d.ts.map +1 -1
  85. package/dist/elements/BaseElement.js +88 -6
  86. package/dist/elements/agents/Agent.d.ts +10 -1
  87. package/dist/elements/agents/Agent.d.ts.map +1 -1
  88. package/dist/elements/agents/Agent.js +66 -19
  89. package/dist/elements/agents/AgentManager.d.ts +2 -0
  90. package/dist/elements/agents/AgentManager.d.ts.map +1 -1
  91. package/dist/elements/agents/AgentManager.js +12 -10
  92. package/dist/elements/skills/Skill.d.ts +10 -1
  93. package/dist/elements/skills/Skill.d.ts.map +1 -1
  94. package/dist/elements/skills/Skill.js +40 -3
  95. package/dist/elements/skills/SkillManager.d.ts +1 -0
  96. package/dist/elements/skills/SkillManager.d.ts.map +1 -1
  97. package/dist/elements/skills/SkillManager.js +10 -4
  98. package/dist/elements/templates/Template.d.ts +10 -1
  99. package/dist/elements/templates/Template.d.ts.map +1 -1
  100. package/dist/elements/templates/Template.js +35 -18
  101. package/dist/elements/templates/TemplateManager.d.ts +1 -1
  102. package/dist/elements/templates/TemplateManager.d.ts.map +1 -1
  103. package/dist/elements/templates/TemplateManager.js +6 -5
  104. package/dist/generated/version.d.ts +2 -2
  105. package/dist/generated/version.js +3 -3
  106. package/dist/index.barrel.d.ts +1 -2
  107. package/dist/index.barrel.d.ts.map +1 -1
  108. package/dist/index.barrel.js +2 -4
  109. package/dist/index.d.ts +143 -25
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +1883 -310
  112. package/dist/persona/PersonaElement.d.ts +10 -0
  113. package/dist/persona/PersonaElement.d.ts.map +1 -1
  114. package/dist/persona/PersonaElement.js +55 -32
  115. package/dist/persona/PersonaElementManager.d.ts.map +1 -1
  116. package/dist/persona/PersonaElementManager.js +13 -11
  117. package/dist/persona/PersonaLoader.d.ts.map +1 -1
  118. package/dist/persona/PersonaLoader.js +8 -2
  119. package/dist/persona/export-import/PersonaImporter.d.ts.map +1 -1
  120. package/dist/persona/export-import/PersonaImporter.js +24 -5
  121. package/dist/persona/export-import/PersonaSharer.d.ts +21 -0
  122. package/dist/persona/export-import/PersonaSharer.d.ts.map +1 -1
  123. package/dist/persona/export-import/PersonaSharer.js +198 -22
  124. package/dist/portfolio/DefaultElementProvider.d.ts +90 -0
  125. package/dist/portfolio/DefaultElementProvider.d.ts.map +1 -1
  126. package/dist/portfolio/DefaultElementProvider.js +499 -7
  127. package/dist/portfolio/GitHubPortfolioIndexer.d.ts +129 -0
  128. package/dist/portfolio/GitHubPortfolioIndexer.d.ts.map +1 -0
  129. package/dist/portfolio/GitHubPortfolioIndexer.js +475 -0
  130. package/dist/portfolio/MigrationManager.d.ts.map +1 -1
  131. package/dist/portfolio/MigrationManager.js +136 -3
  132. package/dist/portfolio/PortfolioIndexManager.d.ts +130 -0
  133. package/dist/portfolio/PortfolioIndexManager.d.ts.map +1 -0
  134. package/dist/portfolio/PortfolioIndexManager.js +478 -0
  135. package/dist/portfolio/PortfolioManager.d.ts +5 -0
  136. package/dist/portfolio/PortfolioManager.d.ts.map +1 -1
  137. package/dist/portfolio/PortfolioManager.js +61 -20
  138. package/dist/portfolio/PortfolioRepoManager.d.ts +75 -0
  139. package/dist/portfolio/PortfolioRepoManager.d.ts.map +1 -0
  140. package/dist/portfolio/PortfolioRepoManager.js +337 -0
  141. package/dist/portfolio/UnifiedIndexManager.d.ts +388 -0
  142. package/dist/portfolio/UnifiedIndexManager.d.ts.map +1 -0
  143. package/dist/portfolio/UnifiedIndexManager.js +1434 -0
  144. package/dist/portfolio/index.d.ts +15 -0
  145. package/dist/portfolio/index.d.ts.map +1 -0
  146. package/dist/portfolio/index.js +15 -0
  147. package/dist/portfolio/types.d.ts +7 -0
  148. package/dist/portfolio/types.d.ts.map +1 -1
  149. package/dist/portfolio/types.js +6 -1
  150. package/dist/security/InputValidator.d.ts.map +1 -1
  151. package/dist/security/InputValidator.js +50 -48
  152. package/dist/security/audit/SecurityAuditor.d.ts.map +1 -1
  153. package/dist/security/audit/SecurityAuditor.js +17 -9
  154. package/dist/security/audit/config/suppressions.d.ts.map +1 -1
  155. package/dist/security/audit/config/suppressions.js +19 -3
  156. package/dist/security/contentValidator.d.ts +2 -0
  157. package/dist/security/contentValidator.d.ts.map +1 -1
  158. package/dist/security/contentValidator.js +115 -4
  159. package/dist/security/secureYamlParser.d.ts +1 -0
  160. package/dist/security/secureYamlParser.d.ts.map +1 -1
  161. package/dist/security/secureYamlParser.js +29 -7
  162. package/dist/security/securityMonitor.d.ts +1 -1
  163. package/dist/security/securityMonitor.d.ts.map +1 -1
  164. package/dist/security/securityMonitor.js +1 -1
  165. package/dist/security/tokenManager.d.ts +1 -1
  166. package/dist/security/tokenManager.d.ts.map +1 -1
  167. package/dist/security/tokenManager.js +30 -10
  168. package/dist/server/ServerSetup.d.ts +22 -2
  169. package/dist/server/ServerSetup.d.ts.map +1 -1
  170. package/dist/server/ServerSetup.js +77 -12
  171. package/dist/server/tools/AuthTools.d.ts.map +1 -1
  172. package/dist/server/tools/AuthTools.js +33 -1
  173. package/dist/server/tools/BuildInfoTools.d.ts +25 -0
  174. package/dist/server/tools/BuildInfoTools.d.ts.map +1 -0
  175. package/dist/server/tools/BuildInfoTools.js +36 -0
  176. package/dist/server/tools/CollectionTools.d.ts.map +1 -1
  177. package/dist/server/tools/CollectionTools.js +55 -46
  178. package/dist/server/tools/ConfigTools.d.ts.map +1 -1
  179. package/dist/server/tools/ConfigTools.js +29 -1
  180. package/dist/server/tools/PersonaTools.d.ts +4 -2
  181. package/dist/server/tools/PersonaTools.d.ts.map +1 -1
  182. package/dist/server/tools/PersonaTools.js +5 -152
  183. package/dist/server/tools/PortfolioTools.d.ts +12 -0
  184. package/dist/server/tools/PortfolioTools.d.ts.map +1 -0
  185. package/dist/server/tools/PortfolioTools.js +221 -0
  186. package/dist/server/tools/index.d.ts +3 -1
  187. package/dist/server/tools/index.d.ts.map +1 -1
  188. package/dist/server/tools/index.js +4 -2
  189. package/dist/server/types.d.ts +40 -5
  190. package/dist/server/types.d.ts.map +1 -1
  191. package/dist/server/types.js +1 -1
  192. package/dist/services/BuildInfoService.d.ts +84 -0
  193. package/dist/services/BuildInfoService.d.ts.map +1 -0
  194. package/dist/services/BuildInfoService.js +271 -0
  195. package/dist/tools/portfolio/PortfolioElementAdapter.d.ts +54 -0
  196. package/dist/tools/portfolio/PortfolioElementAdapter.d.ts.map +1 -0
  197. package/dist/tools/portfolio/PortfolioElementAdapter.js +229 -0
  198. package/dist/tools/portfolio/submitToPortfolioTool.d.ts +164 -0
  199. package/dist/tools/portfolio/submitToPortfolioTool.d.ts.map +1 -0
  200. package/dist/tools/portfolio/submitToPortfolioTool.js +1523 -0
  201. package/dist/tools/portfolio/types.d.ts +41 -0
  202. package/dist/tools/portfolio/types.d.ts.map +1 -0
  203. package/dist/tools/portfolio/types.js +15 -0
  204. package/dist/types/collection.d.ts +51 -0
  205. package/dist/types/collection.d.ts.map +1 -1
  206. package/dist/types/collection.js +1 -1
  207. package/dist/utils/EarlyTerminationSearch.d.ts +41 -0
  208. package/dist/utils/EarlyTerminationSearch.d.ts.map +1 -0
  209. package/dist/utils/EarlyTerminationSearch.js +164 -0
  210. package/dist/utils/ErrorHandler.d.ts +86 -0
  211. package/dist/utils/ErrorHandler.d.ts.map +1 -0
  212. package/dist/utils/ErrorHandler.js +201 -0
  213. package/dist/utils/FileDiscoveryUtil.d.ts +53 -0
  214. package/dist/utils/FileDiscoveryUtil.d.ts.map +1 -0
  215. package/dist/utils/FileDiscoveryUtil.js +169 -0
  216. package/dist/utils/GitHubRateLimiter.d.ts +88 -0
  217. package/dist/utils/GitHubRateLimiter.d.ts.map +1 -0
  218. package/dist/utils/GitHubRateLimiter.js +315 -0
  219. package/dist/utils/PerformanceMonitor.d.ts +134 -0
  220. package/dist/utils/PerformanceMonitor.d.ts.map +1 -0
  221. package/dist/utils/PerformanceMonitor.js +347 -0
  222. package/dist/utils/RateLimiter.d.ts.map +1 -0
  223. package/dist/utils/RateLimiter.js +172 -0
  224. package/dist/utils/SecureDownloader.d.ts +241 -0
  225. package/dist/utils/SecureDownloader.d.ts.map +1 -0
  226. package/dist/utils/SecureDownloader.js +759 -0
  227. package/dist/utils/ToolCache.d.ts +82 -0
  228. package/dist/utils/ToolCache.d.ts.map +1 -0
  229. package/dist/utils/ToolCache.js +196 -0
  230. package/dist/utils/errorCodes.d.ts +136 -0
  231. package/dist/utils/errorCodes.d.ts.map +1 -0
  232. package/dist/utils/errorCodes.js +87 -0
  233. package/dist/utils/index.d.ts +3 -0
  234. package/dist/utils/index.d.ts.map +1 -1
  235. package/dist/utils/index.js +4 -1
  236. package/dist/utils/installation.d.ts +1 -1
  237. package/dist/utils/installation.d.ts.map +1 -1
  238. package/dist/utils/installation.js +9 -8
  239. package/dist/utils/searchUtils.d.ts +31 -0
  240. package/dist/utils/searchUtils.d.ts.map +1 -1
  241. package/dist/utils/searchUtils.js +62 -1
  242. package/package.json +17 -7
  243. package/dist/config/updateConfig.d.ts +0 -84
  244. package/dist/config/updateConfig.d.ts.map +0 -1
  245. package/dist/config/updateConfig.js +0 -148
  246. package/dist/server/tools/UpdateTools.d.ts +0 -10
  247. package/dist/server/tools/UpdateTools.d.ts.map +0 -1
  248. package/dist/server/tools/UpdateTools.js +0 -85
  249. package/dist/update/BackupManager.d.ts +0 -63
  250. package/dist/update/BackupManager.d.ts.map +0 -1
  251. package/dist/update/BackupManager.js +0 -370
  252. package/dist/update/DependencyChecker.d.ts +0 -41
  253. package/dist/update/DependencyChecker.d.ts.map +0 -1
  254. package/dist/update/DependencyChecker.js +0 -132
  255. package/dist/update/RateLimiter.d.ts.map +0 -1
  256. package/dist/update/RateLimiter.js +0 -172
  257. package/dist/update/SignatureVerifier.d.ts +0 -71
  258. package/dist/update/SignatureVerifier.d.ts.map +0 -1
  259. package/dist/update/SignatureVerifier.js +0 -214
  260. package/dist/update/UpdateChecker.d.ts +0 -132
  261. package/dist/update/UpdateChecker.d.ts.map +0 -1
  262. package/dist/update/UpdateChecker.js +0 -506
  263. package/dist/update/UpdateManager.d.ts +0 -60
  264. package/dist/update/UpdateManager.d.ts.map +0 -1
  265. package/dist/update/UpdateManager.js +0 -730
  266. package/dist/update/VersionManager.d.ts +0 -31
  267. package/dist/update/VersionManager.d.ts.map +0 -1
  268. package/dist/update/VersionManager.js +0 -181
  269. package/dist/update/index.d.ts +0 -9
  270. package/dist/update/index.d.ts.map +0 -1
  271. package/dist/update/index.js +0 -9
  272. /package/dist/{update → utils}/RateLimiter.d.ts +0 -0
@@ -1,506 +0,0 @@
1
- /**
2
- * UpdateChecker - Secure GitHub release update checking with comprehensive sanitization
3
- *
4
- * Security measures implemented:
5
- * 1. XSS Protection: DOMPurify with strict no-tags/no-attributes policy
6
- * 2. Command Injection Prevention: Multiple regex patterns for various escape sequences
7
- * 3. URL Validation: Whitelist approach allowing only http/https schemes
8
- * 4. Information Disclosure Prevention: Sanitized logging of sensitive data
9
- * 5. Length Limits: Configurable limits to prevent DoS attacks
10
- * 6. OWASP Patterns: Protection against PHP, ASP, hex, unicode, and octal escapes
11
- *
12
- * Performance optimizations:
13
- * - Cached DOMPurify instance to avoid recreation overhead
14
- * - Single-pass regex processing for injection patterns
15
- * - Exponential backoff for network retries
16
- */
17
- import { RELEASES_API_URL } from '../config/constants.js';
18
- import { RateLimiterFactory } from './RateLimiter.js';
19
- import { SignatureVerifier } from './SignatureVerifier.js';
20
- import DOMPurify from 'dompurify';
21
- import { JSDOM } from 'jsdom';
22
- import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
23
- export class UpdateChecker {
24
- versionManager;
25
- rateLimiter;
26
- signatureVerifier;
27
- // Static cache for DOMPurify to improve performance
28
- // We use 'any' for JSDOM window to avoid complex type conflicts
29
- // but maintain type safety for DOMPurify instance
30
- static purifyWindow = null;
31
- static purify = null;
32
- /**
33
- * Lazily initialize DOMPurify to prevent crashes during startup
34
- * CRITICAL FIX: Prevents jsdom from crashing during MCP initialization
35
- */
36
- initializeDOMPurify() {
37
- if (UpdateChecker.purify)
38
- return;
39
- try {
40
- // Lazy load dependencies only when needed
41
- const DOMPurify = require('dompurify');
42
- const { JSDOM } = require('jsdom');
43
- const dom = new JSDOM('');
44
- UpdateChecker.purifyWindow = dom.window;
45
- UpdateChecker.purify = DOMPurify(UpdateChecker.purifyWindow);
46
- console.log('[UpdateChecker] DOMPurify initialized successfully');
47
- }
48
- catch (error) {
49
- console.error('[DollhouseMCP] Failed to initialize DOMPurify:', error);
50
- // Continue without HTML sanitization - better than crashing
51
- UpdateChecker.purify = {
52
- sanitize: (str) => {
53
- // Basic fallback sanitization - escape HTML entities
54
- // This is safer than trying to remove tags with regex
55
- return str
56
- .replace(/&/g, '&')
57
- .replace(/</g, '&lt;')
58
- .replace(/>/g, '&gt;')
59
- .replace(/"/g, '&quot;')
60
- .replace(/'/g, '&#039;');
61
- }
62
- };
63
- }
64
- }
65
- // Security configuration with sensible defaults
66
- releaseNotesMaxLength;
67
- urlMaxLength;
68
- securityLogger;
69
- requireSignedReleases;
70
- constructor(versionManager, options) {
71
- if (!versionManager) {
72
- throw new Error('VersionManager is required');
73
- }
74
- this.versionManager = versionManager;
75
- // Apply options with defaults and validation
76
- this.releaseNotesMaxLength = options?.releaseNotesMaxLength ?? 5000;
77
- this.urlMaxLength = options?.urlMaxLength ?? 2048;
78
- this.securityLogger = options?.securityLogger;
79
- // Use provided rate limiter or create default
80
- this.rateLimiter = options?.rateLimiter || RateLimiterFactory.createUpdateCheckLimiter();
81
- // Determine if we're in production environment
82
- const isProduction = process.env.NODE_ENV === 'production' ||
83
- process.env.CI === 'true' ||
84
- !process.env.ALLOW_UNSIGNED_RELEASES;
85
- // Use provided signature verifier or create default
86
- this.signatureVerifier = options?.signatureVerifier || new SignatureVerifier({
87
- // In production, we should require signed releases
88
- allowUnsignedInDev: !isProduction
89
- });
90
- // Whether to require signed releases (default: true in production)
91
- this.requireSignedReleases = options?.requireSignedReleases ?? isProduction;
92
- // Validate configuration for security
93
- if (this.releaseNotesMaxLength < 100) {
94
- throw new Error('releaseNotesMaxLength must be at least 100 characters for security');
95
- }
96
- if (this.urlMaxLength < 50) {
97
- throw new Error('urlMaxLength must be at least 50 characters');
98
- }
99
- // Initialize cached DOMPurify instance for performance
100
- // This avoids creating a new JSDOM window for each sanitization
101
- if (!UpdateChecker.purifyWindow) {
102
- const dom = new JSDOM('');
103
- UpdateChecker.purifyWindow = dom.window;
104
- // DOMPurify expects a Window-like object from JSDOM
105
- UpdateChecker.purify = DOMPurify(UpdateChecker.purifyWindow);
106
- }
107
- }
108
- /**
109
- * Execute a network operation with retry logic and exponential backoff
110
- * @param operation - The async operation to execute
111
- * @param maxRetries - Maximum number of retry attempts (default: 3)
112
- * @param baseDelay - Base delay in milliseconds for exponential backoff (default: 1000ms)
113
- * @returns Promise resolving to the operation result
114
- * @throws The last error if all retries fail
115
- */
116
- async retryNetworkOperation(operation, maxRetries = 3, baseDelay = 1000) {
117
- let lastError;
118
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
119
- try {
120
- return await operation();
121
- }
122
- catch (error) {
123
- lastError = error;
124
- // Don't retry on the last attempt
125
- if (attempt === maxRetries) {
126
- break;
127
- }
128
- // Don't retry certain errors (like 404, 401)
129
- if (error instanceof Error && error.message.includes('404')) {
130
- break;
131
- }
132
- // Calculate delay with exponential backoff
133
- const delay = baseDelay * Math.pow(2, attempt);
134
- await new Promise(resolve => setTimeout(resolve, delay));
135
- }
136
- }
137
- throw lastError;
138
- }
139
- /**
140
- * Check for updates from GitHub releases with security and error handling
141
- * @returns UpdateCheckResult if update info is available, null if no releases found
142
- * @throws Error for network or API failures or rate limit exceeded
143
- */
144
- async checkForUpdates() {
145
- // Check rate limit before making API request
146
- const rateLimitStatus = this.rateLimiter.checkLimit();
147
- if (!rateLimitStatus.allowed) {
148
- const waitTime = Math.ceil(rateLimitStatus.retryAfterMs / 1000);
149
- const waitMinutes = Math.floor(waitTime / 60);
150
- const waitSeconds = waitTime % 60;
151
- const timeStr = waitMinutes > 0
152
- ? `${waitMinutes} minute${waitMinutes > 1 ? 's' : ''} ${waitSeconds} second${waitSeconds !== 1 ? 's' : ''}`
153
- : `${waitSeconds} second${waitSeconds !== 1 ? 's' : ''}`;
154
- throw new Error(`Rate limit exceeded. Please wait ${timeStr} before checking for updates again. ` +
155
- `(${rateLimitStatus.remainingTokens} requests remaining, resets at ${rateLimitStatus.resetTime.toLocaleTimeString()})`);
156
- }
157
- // Consume a rate limit token
158
- this.rateLimiter.consumeToken();
159
- const currentVersion = await this.versionManager.getCurrentVersion();
160
- // Check GitHub releases API for latest version with retry logic
161
- const response = await this.retryNetworkOperation(async () => {
162
- const controller = new AbortController();
163
- const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
164
- try {
165
- const response = await fetch(RELEASES_API_URL, {
166
- headers: {
167
- 'Accept': 'application/vnd.github.v3+json',
168
- 'User-Agent': 'DollhouseMCP/1.0'
169
- },
170
- signal: controller.signal
171
- });
172
- clearTimeout(timeoutId);
173
- return response;
174
- }
175
- catch (error) {
176
- clearTimeout(timeoutId);
177
- throw error;
178
- }
179
- });
180
- if (!response.ok) {
181
- if (response.status === 404) {
182
- return null; // No releases found
183
- }
184
- throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
185
- }
186
- const releaseData = await response.json();
187
- const tagName = releaseData.tag_name;
188
- const latestVersion = tagName?.replace(/^v/, '') || releaseData.name;
189
- // Use consistent date formatting method
190
- const publishedAt = releaseData.published_at;
191
- // Compare versions
192
- const isUpdateAvailable = this.versionManager.compareVersions(currentVersion, latestVersion) < 0;
193
- const releaseNotes = releaseData.body || 'See release notes on GitHub';
194
- // Verify release signature if we have a tag
195
- let signatureVerified = false;
196
- let signerInfo;
197
- if (tagName) {
198
- try {
199
- const verificationResult = await this.signatureVerifier.verifyTagSignature(tagName);
200
- signatureVerified = verificationResult.verified;
201
- if (verificationResult.signerEmail) {
202
- signerInfo = verificationResult.signerEmail;
203
- if (verificationResult.signerKey) {
204
- signerInfo += ` (${verificationResult.signerKey})`;
205
- }
206
- }
207
- // Log signature verification
208
- if (this.securityLogger) {
209
- this.securityLogger('signature_verification', {
210
- tagName,
211
- verified: signatureVerified,
212
- signerKey: verificationResult.signerKey,
213
- error: verificationResult.error
214
- });
215
- }
216
- // If signature verification is required and failed, throw error
217
- if (this.requireSignedReleases && !signatureVerified) {
218
- throw new Error(`Release signature verification failed: ${verificationResult.error || 'Unknown error'}. ` +
219
- 'Only signed releases are accepted in production mode.');
220
- }
221
- }
222
- catch (error) {
223
- // If we can't verify the signature and it's required, fail
224
- if (this.requireSignedReleases) {
225
- throw error;
226
- }
227
- // Otherwise, log and continue
228
- if (this.securityLogger) {
229
- this.securityLogger('signature_verification_error', {
230
- tagName,
231
- error: error instanceof Error ? error.message : String(error)
232
- });
233
- }
234
- }
235
- }
236
- return {
237
- currentVersion,
238
- latestVersion,
239
- isUpdateAvailable,
240
- releaseDate: publishedAt, // Will be formatted by formatDate() when displayed
241
- releaseNotes,
242
- releaseUrl: releaseData.html_url,
243
- tagName,
244
- signatureVerified,
245
- signerInfo
246
- };
247
- }
248
- /**
249
- * Get current rate limit status
250
- * @returns Current rate limit status including remaining requests and reset time
251
- */
252
- getRateLimitStatus() {
253
- const status = this.rateLimiter.getStatus();
254
- return {
255
- allowed: status.allowed,
256
- remainingRequests: status.remainingTokens,
257
- resetTime: status.resetTime,
258
- waitTimeSeconds: status.retryAfterMs ? Math.ceil(status.retryAfterMs / 1000) : undefined
259
- };
260
- }
261
- /**
262
- * Format update check results for display with comprehensive sanitization
263
- * @param result - The update check result to format
264
- * @param error - Optional error from update check
265
- * @param personaIndicator - Optional persona indicator prefix
266
- * @returns Formatted string safe for display
267
- */
268
- formatUpdateCheckResult(result, error, personaIndicator = '') {
269
- if (error) {
270
- const isAbortError = error.name === 'AbortError';
271
- const errorMessage = error.message || String(error);
272
- const isRateLimitError = errorMessage.includes('Rate limit exceeded');
273
- if (isRateLimitError) {
274
- return personaIndicator +
275
- '⏳ **Rate Limit Exceeded**\n\n' +
276
- error.message + '\n\n' +
277
- '**Why this happens:**\n' +
278
- '• Update checks are limited to prevent API abuse\n' +
279
- '• GitHub API has rate limits for all applications\n\n' +
280
- '**What you can do:**\n' +
281
- '• Wait for the specified time before checking again\n' +
282
- '• Use `get_server_status` to see current version without API calls\n' +
283
- '• Visit https://github.com/DollhouseMCP/mcp-server/releases directly';
284
- }
285
- return personaIndicator +
286
- '❌ **Update Check Failed**\n\n' +
287
- 'Error: ' + errorMessage + '\n\n' +
288
- (isAbortError
289
- ? 'The request timed out. Please check your internet connection and try again.'
290
- : 'Tips:\n' +
291
- '• Check your internet connection\n' +
292
- '• Ensure GitHub.com is accessible\n' +
293
- '• Try running `update_server true` for manual update\n' +
294
- '• Visit https://github.com/DollhouseMCP/mcp-server/releases');
295
- }
296
- if (!result) {
297
- const currentVersion = 'unknown';
298
- return personaIndicator +
299
- '📦 **Update Check Complete**\n\n' +
300
- '🔄 **Current Version:** ' + currentVersion + '\n' +
301
- '📡 **Remote Status:** No releases found on GitHub\n' +
302
- 'ℹ️ **Note:** This may be a development version or releases haven\'t been published yet.\n\n' +
303
- '**Manual Update:**\n' +
304
- 'Use `update_server true` to pull latest changes from main branch.';
305
- }
306
- const statusParts = [
307
- personaIndicator + '📦 **Update Check Complete**\n\n',
308
- '🔄 **Current Version:** ' + result.currentVersion + '\n',
309
- '📡 **Latest Version:** ' + result.latestVersion + '\n',
310
- '📅 **Released:** ' + this.formatDate(result.releaseDate) + '\n'
311
- ];
312
- // Add signature verification status
313
- if (result.signatureVerified !== undefined) {
314
- if (result.signatureVerified) {
315
- statusParts.push('✅ **Signature:** Verified');
316
- if (result.signerInfo) {
317
- statusParts.push(` by ${result.signerInfo}`);
318
- }
319
- statusParts.push('\n');
320
- }
321
- else {
322
- statusParts.push('⚠️ **Signature:** Not verified\n');
323
- }
324
- }
325
- statusParts.push('\n');
326
- if (result.isUpdateAvailable) {
327
- statusParts.push('✨ **Update Available!**\n\n', '**What\'s New:**\n' + this.sanitizeReleaseNotes(result.releaseNotes) + '\n\n', '**To Update:**\n', '• Use: `update_server true`\n', '• Or visit: ' + this.sanitizeUrl(result.releaseUrl) + '\n\n', '⚠️ **Note:** Update will restart the server and reload all personas.');
328
- }
329
- else {
330
- statusParts.push('✅ **You\'re Up to Date!**\n\n', 'Your DollhouseMCP installation is current.\n', 'Check back later for new features and improvements.');
331
- }
332
- return statusParts.join('');
333
- }
334
- /**
335
- * Sanitize URLs to prevent dangerous schemes and information disclosure
336
- *
337
- * Security measures:
338
- * - Length validation to prevent DoS
339
- * - Whitelist approach: only http/https allowed
340
- * - Sanitized logging to prevent sensitive data exposure
341
- *
342
- * @param url - The URL to sanitize
343
- * @returns Empty string if invalid/dangerous, original URL if safe
344
- */
345
- sanitizeUrl(url) {
346
- if (!url)
347
- return '';
348
- // Check URL length
349
- if (url.length > this.urlMaxLength) {
350
- this.logSecurityEvent('url_too_long', {
351
- length: url.length,
352
- maxLength: this.urlMaxLength,
353
- urlPrefix: url.substring(0, 50) + '...' // Only log first 50 chars
354
- });
355
- return ''; // URL too long
356
- }
357
- // Only allow http and https schemes
358
- const allowedSchemes = ['http:', 'https:'];
359
- try {
360
- const parsed = new URL(url);
361
- if (!allowedSchemes.includes(parsed.protocol)) {
362
- this.logSecurityEvent('dangerous_url_scheme', {
363
- scheme: parsed.protocol,
364
- host: parsed.hostname // Log only hostname, not full URL
365
- });
366
- return ''; // Return empty string for dangerous schemes
367
- }
368
- return url;
369
- }
370
- catch {
371
- this.logSecurityEvent('invalid_url', {
372
- urlLength: url.length // Log length only, not content
373
- });
374
- return ''; // Invalid URL
375
- }
376
- }
377
- /**
378
- * Sanitize release notes to prevent XSS, command injection, and DoS
379
- *
380
- * Security layers:
381
- * 1. Length limiting (configurable, default 5000 chars)
382
- * 2. HTML/JS sanitization via DOMPurify (no tags/attributes allowed)
383
- * 3. Command injection pattern removal (backticks, command substitution)
384
- * 4. OWASP pattern removal (PHP, ASP, hex/unicode/octal escapes)
385
- *
386
- * @param notes - The release notes to sanitize
387
- * @returns Sanitized release notes safe for display
388
- */
389
- sanitizeReleaseNotes(notes) {
390
- if (!notes)
391
- return 'See release notes on GitHub';
392
- // First, normalize Unicode to prevent bypass attacks
393
- const unicodeResult = UnicodeValidator.normalize(notes);
394
- let sanitized = unicodeResult.normalizedContent;
395
- if (unicodeResult.detectedIssues && unicodeResult.detectedIssues.length > 0) {
396
- this.logSecurityEvent('unicode_issues_in_release_notes', {
397
- issues: unicodeResult.detectedIssues,
398
- severity: unicodeResult.severity
399
- });
400
- }
401
- // Apply length limit
402
- if (sanitized.length > this.releaseNotesMaxLength) {
403
- this.logSecurityEvent('release_notes_truncated', {
404
- originalLength: sanitized.length,
405
- maxLength: this.releaseNotesMaxLength
406
- });
407
- sanitized = sanitized.substring(0, this.releaseNotesMaxLength) + '...';
408
- }
409
- // Use cached DOMPurify instance with automatic recovery
410
- if (!UpdateChecker.purify || !UpdateChecker.purifyWindow) {
411
- // Reinitialize if somehow corrupted - provides resilience
412
- const dom = new JSDOM('');
413
- UpdateChecker.purifyWindow = dom.window;
414
- UpdateChecker.purify = DOMPurify(UpdateChecker.purifyWindow);
415
- }
416
- const beforeSanitize = sanitized;
417
- // CRITICAL FIX: Initialize DOMPurify lazily to prevent startup crashes
418
- this.initializeDOMPurify();
419
- // DOMPurify configuration for maximum security
420
- // ALLOWED_TAGS: [] strips all HTML tags
421
- // ALLOWED_ATTR: [] strips all attributes
422
- // Additional options for extra security
423
- if (UpdateChecker.purify && UpdateChecker.purify.sanitize) {
424
- sanitized = UpdateChecker.purify.sanitize(sanitized, {
425
- ALLOWED_TAGS: [], // Strip all HTML tags
426
- ALLOWED_ATTR: [], // Strip all attributes
427
- FORBID_TAGS: ['style', 'script', 'iframe', 'object', 'embed', 'link'],
428
- FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover']
429
- });
430
- }
431
- if (beforeSanitize !== sanitized) {
432
- this.logSecurityEvent('html_content_removed', {
433
- removedLength: beforeSanitize.length - sanitized.length
434
- });
435
- }
436
- // Additional sanitization for command injection patterns
437
- // Single-pass processing for performance while maintaining security
438
- // These patterns cover various injection vectors beyond HTML/JS
439
- // Length limits added to prevent ReDoS attacks
440
- const patterns = [
441
- /`[^`]{0,1000}`/g, // Backtick expressions (limited to 1000 chars)
442
- /\$\([^)]{0,1000}\)/g, // Command substitution (limited to 1000 chars)
443
- /\$\{[^}]{0,1000}\}/g, // Variable expansion (limited to 1000 chars)
444
- /<\?[^>]{0,1000}\?>/g, // PHP tags (OWASP) (limited to 1000 chars)
445
- /&lt;%[^>]{0,1000}%&gt;/g, // ASP tags (HTML-encoded by DOMPurify) (limited to 1000 chars)
446
- /<%[^>]{0,1000}%>/g, // ASP tags (raw) (limited to 1000 chars)
447
- /\\x[0-9a-fA-F]{2}/g, // Hex escapes (OWASP) - already limited by {2}
448
- /\\u[0-9a-fA-F]{4}/g, // Unicode escapes - already limited by {4}
449
- /\\[0-7]{1,3}/g // Octal escapes - already limited by {1,3}
450
- ];
451
- const beforePatterns = sanitized;
452
- for (const pattern of patterns) {
453
- sanitized = sanitized.replace(pattern, '');
454
- }
455
- if (beforePatterns !== sanitized) {
456
- this.logSecurityEvent('injection_patterns_removed', {
457
- removedLength: beforePatterns.length - sanitized.length
458
- });
459
- }
460
- return sanitized;
461
- }
462
- /**
463
- * Format date to human-readable format with consistent timezone handling
464
- * @param dateStr - ISO date string to format
465
- * @returns Human-readable date string (e.g., "January 5, 2025")
466
- */
467
- formatDate(dateStr) {
468
- try {
469
- const date = new Date(dateStr);
470
- if (isNaN(date.getTime())) {
471
- return dateStr; // Return original if invalid
472
- }
473
- // Use UTC methods to ensure consistent timezone handling
474
- return date.toLocaleDateString('en-US', {
475
- year: 'numeric',
476
- month: 'long',
477
- day: 'numeric',
478
- timeZone: 'UTC' // Ensure consistent timezone
479
- });
480
- }
481
- catch {
482
- return dateStr; // Return original on error
483
- }
484
- }
485
- /**
486
- * Log security events for monitoring and alerting
487
- * Only logs if securityLogger callback was provided in constructor
488
- * @param event - The security event type
489
- * @param details - Event details (sanitized to prevent info disclosure)
490
- */
491
- logSecurityEvent(event, details) {
492
- if (this.securityLogger) {
493
- this.securityLogger(event, details);
494
- }
495
- }
496
- /**
497
- * Reset static DOMPurify cache (useful for long-running processes)
498
- * This prevents memory accumulation in services that run for extended periods
499
- * @static
500
- */
501
- static resetCache() {
502
- UpdateChecker.purifyWindow = null;
503
- UpdateChecker.purify = null;
504
- }
505
- }
506
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVXBkYXRlQ2hlY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91cGRhdGUvVXBkYXRlQ2hlY2tlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFFSCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUUxRCxPQUFPLEVBQWUsa0JBQWtCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNuRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMzRCxPQUFPLFNBQVMsTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLE9BQU8sQ0FBQztBQUM5QixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQWlCOUUsTUFBTSxPQUFPLGFBQWE7SUFDaEIsY0FBYyxDQUFpQjtJQUMvQixXQUFXLENBQWM7SUFDekIsaUJBQWlCLENBQW9CO0lBRTdDLG9EQUFvRDtJQUNwRCxnRUFBZ0U7SUFDaEUsa0RBQWtEO0lBQzFDLE1BQU0sQ0FBQyxZQUFZLEdBQVEsSUFBSSxDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxNQUFNLEdBQTZCLElBQUksQ0FBQztJQUV2RDs7O09BR0c7SUFDSyxtQkFBbUI7UUFDekIsSUFBSSxhQUFhLENBQUMsTUFBTTtZQUFFLE9BQU87UUFFakMsSUFBSSxDQUFDO1lBQ0gsMENBQTBDO1lBQzFDLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBRW5DLE1BQU0sR0FBRyxHQUFHLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzFCLGFBQWEsQ0FBQyxZQUFZLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQztZQUN4QyxhQUFhLENBQUMsTUFBTSxHQUFH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QUFLLENBQUUsNkJBQTZCO2FBQy9DLENBQUMsQ0FBQztRQUNMLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxPQUFPLE9BQU8sQ0FBQyxDQUFFLDJCQUEyQjtRQUM5QyxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ssZ0JBQWdCLENBQUMsS0FBYSxFQUFFLE9BQVk7UUFDbEQsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDeEIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDdEMsQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLFVBQVU7UUFDdEIsYUFBYSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUM7UUFDbEMsYUFBYSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7SUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVXBkYXRlQ2hlY2tlciAtIFNlY3VyZSBHaXRIdWIgcmVsZWFzZSB1cGRhdGUgY2hlY2tpbmcgd2l0aCBjb21wcmVoZW5zaXZlIHNhbml0aXphdGlvblxuICogXG4gKiBTZWN1cml0eSBtZWFzdXJlcyBpbXBsZW1lbnRlZDpcbiAqIDEuIFhTUyBQcm90ZWN0aW9uOiBET01QdXJpZnkgd2l0aCBzdHJpY3Qgbm8tdGFncy9uby1hdHRyaWJ1dGVzIHBvbGljeVxuICogMi4gQ29tbWFuZCBJbmplY3Rpb24gUHJldmVudGlvbjogTXVsdGlwbGUgcmVnZXggcGF0dGVybnMgZm9yIHZhcmlvdXMgZXNjYXBlIHNlcXVlbmNlc1xuICogMy4gVVJMIFZhbGlkYXRpb246IFdoaXRlbGlzdCBhcHByb2FjaCBhbGxvd2luZyBvbmx5IGh0dHAvaHR0cHMgc2NoZW1lc1xuICogNC4gSW5mb3JtYXRpb24gRGlzY2xvc3VyZSBQcmV2ZW50aW9uOiBTYW5pdGl6ZWQgbG9nZ2luZyBvZiBzZW5zaXRpdmUgZGF0YVxuICogNS4gTGVuZ3RoIExpbWl0czogQ29uZmlndXJhYmxlIGxpbWl0cyB0byBwcmV2ZW50IERvUyBhdHRhY2tzXG4gKiA2LiBPV0FTUCBQYXR0ZXJuczogUHJvdGVjdGlvbiBhZ2FpbnN0IFBIUCwgQVNQLCBoZXgsIHVuaWNvZGUsIGFuZCBvY3RhbCBlc2NhcGVzXG4gKiBcbiAqIFBlcmZvcm1hbmNlIG9wdGltaXphdGlvbnM6XG4gKiAtIENhY2hlZCBET01QdXJpZnkgaW5zdGFuY2UgdG8gYXZvaWQgcmVjcmVhdGlvbiBvdmVyaGVhZFxuICogLSBTaW5nbGUtcGFzcyByZWdleCBwcm9jZXNzaW5nIGZvciBpbmplY3Rpb24gcGF0dGVybnNcbiAqIC0gRXhwb25lbnRpYWwgYmFja29mZiBmb3IgbmV0d29yayByZXRyaWVzXG4gKi9cblxuaW1wb3J0IHsgUkVMRUFTRVNfQVBJX1VSTCB9IGZyb20gJy4uL2NvbmZpZy9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgVmVyc2lvbk1hbmFnZXIgfSBmcm9tICcuL1ZlcnNpb25NYW5hZ2VyLmpzJztcbmltcG9ydCB7IFJhdGVMaW1pdGVyLCBSYXRlTGltaXRlckZhY3RvcnkgfSBmcm9tICcuL1JhdGVMaW1pdGVyLmpzJztcbmltcG9ydCB7IFNpZ25hdHVyZVZlcmlmaWVyIH0gZnJvbSAnLi9TaWduYXR1cmVWZXJpZmllci5qcyc7XG5pbXBvcnQgRE9NUHVyaWZ5IGZyb20gJ2RvbXB1cmlmeSc7XG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFVwZGF0ZUNoZWNrUmVzdWx0IHtcbiAgY3VycmVudFZlcnNpb246IHN0cmluZztcbiAgbGF0ZXN0VmVyc2lvbjogc3RyaW5nO1xuICBpc1VwZGF0ZUF2YWlsYWJsZTogYm9vbGVhbjtcbiAgcmVsZWFzZURhdGU6IHN0cmluZztcbiAgcmVsZWFzZU5vdGVzOiBzdHJpbmc7XG4gIHJlbGVhc2VVcmw6IHN0cmluZztcbiAgdGFnTmFtZT86IHN0cmluZztcbiAgc2lnbmF0dXJlVmVyaWZpZWQ/OiBib29sZWFuO1xuICBzaWduZXJJbmZvPzogc3RyaW5nO1xufVxuXG4vLyBUeXBlIGRlY2xhcmF0aW9ucyBmb3IgYmV0dGVyIHR5cGUgc2FmZXR5XG50eXBlIERPTVB1cmlmeUluc3RhbmNlID0gUmV0dXJuVHlwZTx0eXBlb2YgRE9NUHVyaWZ5PjtcblxuZXhwb3J0IGNsYXNzIFVwZGF0ZUNoZWNrZXIge1xuICBwcml2YXRlIHZlcnNpb25NYW5hZ2VyOiBWZXJzaW9uTWFuYWdlcjtcbiAgcHJpdmF0ZSByYXRlTGltaXRlcjogUmF0ZUxpbWl0ZXI7XG4gIHByaXZhdGUgc2lnbmF0dXJlVmVyaWZpZXI6IFNpZ25hdHVyZVZlcmlmaWVyO1xuICBcbiAgLy8gU3RhdGljIGNhY2hlIGZvciBET01QdXJpZnkgdG8gaW1wcm92ZSBwZXJmb3JtYW5jZVxuICAvLyBXZSB1c2UgJ2FueScgZm9yIEpTRE9NIHdpbmRvdyB0byBhdm9pZCBjb21wbGV4IHR5cGUgY29uZmxpY3RzXG4gIC8vIGJ1dCBtYWludGFpbiB0eXBlIHNhZmV0eSBmb3IgRE9NUHVyaWZ5IGluc3RhbmNlXG4gIHByaXZhdGUgc3RhdGljIHB1cmlmeVdpbmRvdzogYW55ID0gbnVsbDtcbiAgcHJpdmF0ZSBzdGF0aWMgcHVyaWZ5OiBET01QdXJpZnlJbnN0YW5jZSB8IG51bGwgPSBudWxsO1xuICBcbiAgLyoqXG4gICAqIExhemlseSBpbml0aWFsaXplIERPTVB1cmlmeSB0byBwcmV2ZW50IGNyYXNoZXMgZHVyaW5nIHN0YXJ0dXBcbiAgICogQ1JJVElDQUwgRklYOiBQcmV2ZW50cyBqc2RvbSBmcm9tIGNyYXNoaW5nIGR1cmluZyBNQ1AgaW5pdGlhbGl6YXRpb25cbiAgICovXG4gIHByaXZhdGUgaW5pdGlhbGl6ZURPTVB1cmlmeSgpOiB2b2lkIHtcbiAgICBpZiAoVXBkYXRlQ2hlY2tlci5wdXJpZnkpIHJldHVybjtcbiAgICBcbiAgICB0cnkge1xuICAgICAgLy8gTGF6eSBsb2FkIGRlcGVuZGVuY2llcyBvbmx5IHdoZW4gbmVlZGVkXG4gICAgICBjb25zdCBET01QdXJpZnkgPSByZXF1aXJlKCdkb21wdXJpZnknKTtcbiAgICAgIGNvbnN0IHsgSlNET00gfSA9IHJlcXVpcmUoJ2pzZG9tJyk7XG4gICAgICBcbiAgICAgIGNvbnN0IGRvbSA9IG5ldyBKU0RPTSgnJyk7XG4gICAgICBVcGRhdGVDaGVja2VyLnB1cmlmeVdpbmRvdyA9IGRvbS53aW5kb3c7XG4gICAgICBVcGRhdGVDaGVja2VyLnB1cmlmeSA9IERPTVB1cmlmeShVcGRhdGVDaGVja2VyLnB1cmlmeVdpbmRvdyk7XG4gICAgICBcbiAgICAgIGNvbnNvbGUubG9nKCdbVXBkYXRlQ2hlY2tlcl0gRE9NUHVyaWZ5IGluaXRpYWxpemVkIHN1Y2Nlc3NmdWxseScpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdbRG9sbGhvdXNlTUNQXSBGYWlsZWQgdG8gaW5pdGlhbGl6ZSBET01QdXJpZnk6JywgZXJyb3IpO1xuICAgICAgLy8gQ29udGludWUgd2l0aG91dCBIVE1MIHNhbml0aXphdGlvbiAtIGJldHRlciB0aGFuIGNyYXNoaW5nXG4gICAgICBVcGRhdGVDaGVja2VyLnB1cmlmeSA9IHtcbiAgICAgICAgc2FuaXRpemU6IChzdHI6IHN0cmluZykgPT4ge1xuICAgICAgICAgIC8vIEJhc2ljIGZhbGxiYWNrIHNhbml0aXphdGlvbiAtIGVzY2FwZSBIVE1MIGVudGl0aWVzXG4gICAgICAgICAgLy8gVGhpcyBpcyBzYWZlciB0aGFuIHRyeWluZyB0byByZW1vdmUgdGFncyB3aXRoIHJlZ2V4XG4gICAgICAgICAgcmV0dXJuIHN0clxuICAgICAgICAgICAgLnJlcGxhY2UoLyYvZywgJyZhbXA7JylcbiAgICAgICAgICAgIC5yZXBsYWNlKC88L2csICcmbHQ7JylcbiAgICAgICAgICAgIC5yZXBsYWNlKC8+L2csICcmZ3Q7JylcbiAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJnF1b3Q7JylcbiAgICAgICAgICAgIC5yZXBsYWNlKC8nL2csICcmIzAzOTsnKTtcbiAgICAgICAgfVxuICAgICAgfSBhcyBhbnk7XG4gICAgfVxuICB9XG4gIFxuICAvLyBTZWN1cml0eSBjb25maWd1cmF0aW9uIHdpdGggc2Vuc2libGUgZGVmYXVsdHNcbiAgcHJpdmF0ZSByZWFkb25seSByZWxlYXNlTm90ZXNNYXhMZW5ndGg6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSB1cmxNYXhMZW5ndGg6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBzZWN1cml0eUxvZ2dlcj86IChldmVudDogc3RyaW5nLCBkZXRhaWxzOiBhbnkpID0+IHZvaWQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgcmVxdWlyZVNpZ25lZFJlbGVhc2VzOiBib29sZWFuO1xuICBcbiAgY29uc3RydWN0b3IoXG4gICAgdmVyc2lvbk1hbmFnZXI6IFZlcnNpb25NYW5hZ2VyLFxuICAgIG9wdGlvbnM/OiB7XG4gICAgICByZWxlYXNlTm90ZXNNYXhMZW5ndGg/OiBudW1iZXI7XG4gICAgICB1cmxNYXhMZW5ndGg/OiBudW1iZXI7XG4gICAgICBzZWN1cml0eUxvZ2dlcj86IChldmVudDogc3RyaW5nLCBkZXRhaWxzOiBhbnkpID0+IHZvaWQ7XG4gICAgICByYXRlTGltaXRlcj86IFJhdGVMaW1pdGVyO1xuICAgICAgc2lnbmF0dXJlVmVyaWZpZXI/OiBTaWduYXR1cmVWZXJpZmllcjtcbiAgICAgIHJlcXVpcmVTaWduZWRSZWxlYXNlcz86IGJvb2xlYW47XG4gICAgfVxuICApIHtcbiAgICBpZiAoIXZlcnNpb25NYW5hZ2VyKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1ZlcnNpb25NYW5hZ2VyIGlzIHJlcXVpcmVkJyk7XG4gICAgfVxuICAgIHRoaXMudmVyc2lvbk1hbmFnZXIgPSB2ZXJzaW9uTWFuYWdlcjtcbiAgICBcbiAgICAvLyBBcHBseSBvcHRpb25zIHdpdGggZGVmYXVsdHMgYW5kIHZhbGlkYXRpb25cbiAgICB0aGlzLnJlbGVhc2VOb3Rlc01heExlbmd0aCA9IG9wdGlvbnM/LnJlbGVhc2VOb3Rlc01heExlbmd0aCA/PyA1MDAwO1xuICAgIHRoaXMudXJsTWF4TGVuZ3RoID0gb3B0aW9ucz8udXJsTWF4TGVuZ3RoID8/IDIwNDg7XG4gICAgdGhpcy5zZWN1cml0eUxvZ2dlciA9IG9wdGlvbnM/LnNlY3VyaXR5TG9nZ2VyO1xuICAgIFxuICAgIC8vIFVzZSBwcm92aWRlZCByYXRlIGxpbWl0ZXIgb3IgY3JlYXRlIGRlZmF1bHRcbiAgICB0aGlzLnJhdGVMaW1pdGVyID0gb3B0aW9ucz8ucmF0ZUxpbWl0ZXIgfHwgUmF0ZUxpbWl0ZXJGYWN0b3J5LmNyZWF0ZVVwZGF0ZUNoZWNrTGltaXRlcigpO1xuICAgIFxuICAgIC8vIERldGVybWluZSBpZiB3ZSdyZSBpbiBwcm9kdWN0aW9uIGVudmlyb25tZW50XG4gICAgY29uc3QgaXNQcm9kdWN0aW9uID0gcHJvY2Vzcy5lbnYuTk9ERV9FTlYgPT09ICdwcm9kdWN0aW9uJyB8fCBcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb2Nlc3MuZW52LkNJID09PSAndHJ1ZScgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICFwcm9jZXNzLmVudi5BTExPV19VTlNJR05FRF9SRUxFQVNFUztcbiAgICBcbiAgICAvLyBVc2UgcHJvdmlkZWQgc2lnbmF0dXJlIHZlcmlmaWVyIG9yIGNyZWF0ZSBkZWZhdWx0XG4gICAgdGhpcy5zaWduYXR1cmVWZXJpZmllciA9IG9wdGlvbnM/LnNpZ25hdHVyZVZlcmlmaWVyIHx8IG5ldyBTaWduYXR1cmVWZXJpZmllcih7XG4gICAgICAvLyBJbiBwcm9kdWN0aW9uLCB3ZSBzaG91bGQgcmVxdWlyZSBzaWduZWQgcmVsZWFzZXNcbiAgICAgIGFsbG93VW5zaWduZWRJbkRldjogIWlzUHJvZHVjdGlvblxuICAgIH0pO1xuICAgIFxuICAgIC8vIFdoZXRoZXIgdG8gcmVxdWlyZSBzaWduZWQgcmVsZWFzZXMgKGRlZmF1bHQ6IHRydWUgaW4gcHJvZHVjdGlvbilcbiAgICB0aGlzLnJlcXVpcmVTaWduZWRSZWxlYXNlcyA9IG9wdGlvbnM/LnJlcXVpcmVTaWduZWRSZWxlYXNlcyA/PyBpc1Byb2R1Y3Rpb247XG4gICAgXG4gICAgLy8gVmFsaWRhdGUgY29uZmlndXJhdGlvbiBmb3Igc2VjdXJpdHlcbiAgICBpZiAodGhpcy5yZWxlYXNlTm90ZXNNYXhMZW5ndGggPCAxMDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigncmVsZWFzZU5vdGVzTWF4TGVuZ3RoIG11c3QgYmUgYXQgbGVhc3QgMTAwIGNoYXJhY3RlcnMgZm9yIHNlY3VyaXR5Jyk7XG4gICAgfVxuICAgIGlmICh0aGlzLnVybE1heExlbmd0aCA8IDUwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3VybE1heExlbmd0aCBtdXN0IGJlIGF0IGxlYXN0IDUwIGNoYXJhY3RlcnMnKTtcbiAgICB9XG4gICAgXG4gICAgLy8gSW5pdGlhbGl6ZSBjYWNoZWQgRE9NUHVyaWZ5IGluc3RhbmNlIGZvciBwZXJmb3JtYW5jZVxuICAgIC8vIFRoaXMgYXZvaWRzIGNyZWF0aW5nIGEgbmV3IEpTRE9NIHdpbmRvdyBmb3IgZWFjaCBzYW5pdGl6YXRpb25cbiAgICBpZiAoIVVwZGF0ZUNoZWNrZXIucHVyaWZ5V2luZG93KSB7XG4gICAgICBjb25zdCBkb20gPSBuZXcgSlNET00oJycpO1xuICAgICAgVXBkYXRlQ2hlY2tlci5wdXJpZnlXaW5kb3cgPSBkb20ud2luZG93O1xuICAgICAgLy8gRE9NUHVyaWZ5IGV4cGVjdHMgYSBXaW5kb3ctbGlrZSBvYmplY3QgZnJvbSBKU0RPTVxuICAgICAgVXBkYXRlQ2hlY2tlci5wdXJpZnkgPSBET01QdXJpZnkoVXBkYXRlQ2hlY2tlci5wdXJpZnlXaW5kb3cpO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIEV4ZWN1dGUgYSBuZXR3b3JrIG9wZXJhdGlvbiB3aXRoIHJldHJ5IGxvZ2ljIGFuZCBleHBvbmVudGlhbCBiYWNrb2ZmXG4gICAqIEBwYXJhbSBvcGVyYXRpb24gLSBUaGUgYXN5bmMgb3BlcmF0aW9uIHRvIGV4ZWN1dGVcbiAgICogQHBhcmFtIG1heFJldHJpZXMgLSBNYXhpbXVtIG51bWJlciBvZiByZXRyeSBhdHRlbXB0cyAoZGVmYXVsdDogMylcbiAgICogQHBhcmFtIGJhc2VEZWxheSAtIEJhc2UgZGVsYXkgaW4gbWlsbGlzZWNvbmRzIGZvciBleHBvbmVudGlhbCBiYWNrb2ZmIChkZWZhdWx0OiAxMDAwbXMpXG4gICAqIEByZXR1cm5zIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBvcGVyYXRpb24gcmVzdWx0XG4gICAqIEB0aHJvd3MgVGhlIGxhc3QgZXJyb3IgaWYgYWxsIHJldHJpZXMgZmFpbFxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZXRyeU5ldHdvcmtPcGVyYXRpb248VD4oXG4gICAgb3BlcmF0aW9uOiAoKSA9PiBQcm9taXNlPFQ+LCBcbiAgICBtYXhSZXRyaWVzOiBudW1iZXIgPSAzLFxuICAgIGJhc2VEZWxheTogbnVtYmVyID0gMTAwMFxuICApOiBQcm9taXNlPFQ+IHtcbiAgICBsZXQgbGFzdEVycm9yOiBFcnJvcjtcbiAgICBcbiAgICBmb3IgKGxldCBhdHRlbXB0ID0gMDsgYXR0ZW1wdCA8PSBtYXhSZXRyaWVzOyBhdHRlbXB0KyspIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCBvcGVyYXRpb24oKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxhc3RFcnJvciA9IGVycm9yIGFzIEVycm9yO1xuICAgICAgICBcbiAgICAgICAgLy8gRG9uJ3QgcmV0cnkgb24gdGhlIGxhc3QgYXR0ZW1wdFxuICAgICAgICBpZiAoYXR0ZW1wdCA9PT0gbWF4UmV0cmllcykge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAvLyBEb24ndCByZXRyeSBjZXJ0YWluIGVycm9ycyAobGlrZSA0MDQsIDQwMSlcbiAgICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IgJiYgZXJyb3IubWVzc2FnZS5pbmNsdWRlcygnNDA0JykpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLy8gQ2FsY3VsYXRlIGRlbGF5IHdpdGggZXhwb25lbnRpYWwgYmFja29mZlxuICAgICAgICBjb25zdCBkZWxheSA9IGJhc2VEZWxheSAqIE1hdGgucG93KDIsIGF0dGVtcHQpO1xuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgZGVsYXkpKTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgdGhyb3cgbGFzdEVycm9yITtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGZvciB1cGRhdGVzIGZyb20gR2l0SHViIHJlbGVhc2VzIHdpdGggc2VjdXJpdHkgYW5kIGVycm9yIGhhbmRsaW5nXG4gICAqIEByZXR1cm5zIFVwZGF0ZUNoZWNrUmVzdWx0IGlmIHVwZGF0ZSBpbmZvIGlzIGF2YWlsYWJsZSwgbnVsbCBpZiBubyByZWxlYXNlcyBmb3VuZFxuICAgKiBAdGhyb3dzIEVycm9yIGZvciBuZXR3b3JrIG9yIEFQSSBmYWlsdXJlcyBvciByYXRlIGxpbWl0IGV4Y2VlZGVkXG4gICAqL1xuICBhc3luYyBjaGVja0ZvclVwZGF0ZXMoKTogUHJvbWlzZTxVcGRhdGVDaGVja1Jlc3VsdCB8IG51bGw+IHtcbiAgICAvLyBDaGVjayByYXRlIGxpbWl0IGJlZm9yZSBtYWtpbmcgQVBJIHJlcXVlc3RcbiAgICBjb25zdCByYXRlTGltaXRTdGF0dXMgPSB0aGlzLnJhdGVMaW1pdGVyLmNoZWNrTGltaXQoKTtcbiAgICBpZiAoIXJhdGVMaW1pdFN0YXR1cy5hbGxvd2VkKSB7XG4gICAgICBjb25zdCB3YWl0VGltZSA9IE1hdGguY2VpbChyYXRlTGltaXRTdGF0dXMucmV0cnlBZnRlck1zISAvIDEwMDApO1xuICAgICAgY29uc3Qgd2FpdE1pbnV0ZXMgPSBNYXRoLmZsb29yKHdhaXRUaW1lIC8gNjApO1xuICAgICAgY29uc3Qgd2FpdFNlY29uZHMgPSB3YWl0VGltZSAlIDYwO1xuICAgICAgXG4gICAgICBjb25zdCB0aW1lU3RyID0gd2FpdE1pbnV0ZXMgPiAwIFxuICAgICAgICA/IGAke3dhaXRNaW51dGVzfSBtaW51dGUke3dhaXRNaW51dGVzID4gMSA/ICdzJyA6ICcnfSAke3dhaXRTZWNvbmRzfSBzZWNvbmQke3dhaXRTZWNvbmRzICE9PSAxID8gJ3MnIDogJyd9YFxuICAgICAgICA6IGAke3dhaXRTZWNvbmRzfSBzZWNvbmQke3dhaXRTZWNvbmRzICE9PSAxID8gJ3MnIDogJyd9YDtcbiAgICAgIFxuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgUmF0ZSBsaW1pdCBleGNlZWRlZC4gUGxlYXNlIHdhaXQgJHt0aW1lU3RyfSBiZWZvcmUgY2hlY2tpbmcgZm9yIHVwZGF0ZXMgYWdhaW4uIGAgK1xuICAgICAgICBgKCR7cmF0ZUxpbWl0U3RhdHVzLnJlbWFpbmluZ1Rva2Vuc30gcmVxdWVzdHMgcmVtYWluaW5nLCByZXNldHMgYXQgJHtyYXRlTGltaXRTdGF0dXMucmVzZXRUaW1lLnRvTG9jYWxlVGltZVN0cmluZygpfSlgXG4gICAgICApO1xuICAgIH1cbiAgICBcbiAgICAvLyBDb25zdW1lIGEgcmF0ZSBsaW1pdCB0b2tlblxuICAgIHRoaXMucmF0ZUxpbWl0ZXIuY29uc3VtZVRva2VuKCk7XG4gICAgXG4gICAgY29uc3QgY3VycmVudFZlcnNpb24gPSBhd2FpdCB0aGlzLnZlcnNpb25NYW5hZ2VyLmdldEN1cnJlbnRWZXJzaW9uKCk7XG4gICAgXG4gICAgLy8gQ2hlY2sgR2l0SHViIHJlbGVhc2VzIEFQSSBmb3IgbGF0ZXN0IHZlcnNpb24gd2l0aCByZXRyeSBsb2dpY1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5yZXRyeU5ldHdvcmtPcGVyYXRpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgIGNvbnN0IHRpbWVvdXRJZCA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCAxMDAwMCk7IC8vIDEwIHNlY29uZCB0aW1lb3V0XG4gICAgICBcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goUkVMRUFTRVNfQVBJX1VSTCwge1xuICAgICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICAgICdBY2NlcHQnOiAnYXBwbGljYXRpb24vdm5kLmdpdGh1Yi52Mytqc29uJyxcbiAgICAgICAgICAgICdVc2VyLUFnZW50JzogJ0RvbGxob3VzZU1DUC8xLjAnXG4gICAgICAgICAgfSxcbiAgICAgICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsXG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG4gICAgICAgIHJldHVybiByZXNwb25zZTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0SWQpO1xuICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBcbiAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA0MDQpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7IC8vIE5vIHJlbGVhc2VzIGZvdW5kXG4gICAgICB9XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEdpdEh1YiBBUEkgZXJyb3I6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IHJlbGVhc2VEYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgIGNvbnN0IHRhZ05hbWUgPSByZWxlYXNlRGF0YS50YWdfbmFtZTtcbiAgICBjb25zdCBsYXRlc3RWZXJzaW9uID0gdGFnTmFtZT8ucmVwbGFjZSgvXnYvLCAnJykgfHwgcmVsZWFzZURhdGEubmFtZTtcbiAgICAvLyBVc2UgY29uc2lzdGVudCBkYXRlIGZvcm1hdHRpbmcgbWV0aG9kXG4gICAgY29uc3QgcHVibGlzaGVkQXQgPSByZWxlYXNlRGF0YS5wdWJsaXNoZWRfYXQ7XG4gICAgXG4gICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xuICAgIGNvbnN0IGlzVXBkYXRlQXZhaWxhYmxlID0gdGhpcy52ZXJzaW9uTWFuYWdlci5jb21wYXJlVmVyc2lvbnMoY3VycmVudFZlcnNpb24sIGxhdGVzdFZlcnNpb24pIDwgMDtcbiAgICBcbiAgICBjb25zdCByZWxlYXNlTm90ZXMgPSByZWxlYXNlRGF0YS5ib2R5IHx8ICdTZWUgcmVsZWFzZSBub3RlcyBvbiBHaXRIdWInO1xuICAgIFxuICAgIC8vIFZlcmlmeSByZWxlYXNlIHNpZ25hdHVyZSBpZiB3ZSBoYXZlIGEgdGFnXG4gICAgbGV0IHNpZ25hdHVyZVZlcmlmaWVkID0gZmFsc2U7XG4gICAgbGV0IHNpZ25lckluZm86IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICBcbiAgICBpZiAodGFnTmFtZSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgdmVyaWZpY2F0aW9uUmVzdWx0ID0gYXdhaXQgdGhpcy5zaWduYXR1cmVWZXJpZmllci52ZXJpZnlUYWdTaWduYXR1cmUodGFnTmFtZSk7XG4gICAgICAgIHNpZ25hdHVyZVZlcmlmaWVkID0gdmVyaWZpY2F0aW9uUmVzdWx0LnZlcmlmaWVkO1xuICAgICAgICBcbiAgICAgICAgaWYgKHZlcmlmaWNhdGlvblJlc3VsdC5zaWduZXJFbWFpbCkge1xuICAgICAgICAgIHNpZ25lckluZm8gPSB2ZXJpZmljYXRpb25SZXN1bHQuc2lnbmVyRW1haWw7XG4gICAgICAgICAgaWYgKHZlcmlmaWNhdGlvblJlc3VsdC5zaWduZXJLZXkpIHtcbiAgICAgICAgICAgIHNpZ25lckluZm8gKz0gYCAoJHt2ZXJpZmljYXRpb25SZXN1bHQuc2lnbmVyS2V5fSlgO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLy8gTG9nIHNpZ25hdHVyZSB2ZXJpZmljYXRpb25cbiAgICAgICAgaWYgKHRoaXMuc2VjdXJpdHlMb2dnZXIpIHtcbiAgICAgICAgICB0aGlzLnNlY3VyaXR5TG9nZ2VyKCdzaWduYXR1cmVfdmVyaWZpY2F0aW9uJywge1xuICAgICAgICAgICAgdGFnTmFtZSxcbiAgICAgICAgICAgIHZlcmlmaWVkOiBzaWduYXR1cmVWZXJpZmllZCxcbiAgICAgICAgICAgIHNpZ25lcktleTogdmVyaWZpY2F0aW9uUmVzdWx0LnNpZ25lcktleSxcbiAgICAgICAgICAgIGVycm9yOiB2ZXJpZmljYXRpb25SZXN1bHQuZXJyb3JcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLy8gSWYgc2lnbmF0dXJlIHZlcmlmaWNhdGlvbiBpcyByZXF1aXJlZCBhbmQgZmFpbGVkLCB0aHJvdyBlcnJvclxuICAgICAgICBpZiAodGhpcy5yZXF1aXJlU2lnbmVkUmVsZWFzZXMgJiYgIXNpZ25hdHVyZVZlcmlmaWVkKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYFJlbGVhc2Ugc2lnbmF0dXJlIHZlcmlmaWNhdGlvbiBmYWlsZWQ6ICR7dmVyaWZpY2F0aW9uUmVzdWx0LmVycm9yIHx8ICdVbmtub3duIGVycm9yJ30uIGAgK1xuICAgICAgICAgICAgJ09ubHkgc2lnbmVkIHJlbGVhc2VzIGFyZSBhY2NlcHRlZCBpbiBwcm9kdWN0aW9uIG1vZGUuJ1xuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIC8vIElmIHdlIGNhbid0IHZlcmlmeSB0aGUgc2lnbmF0dXJlIGFuZCBpdCdzIHJlcXVpcmVkLCBmYWlsXG4gICAgICAgIGlmICh0aGlzLnJlcXVpcmVTaWduZWRSZWxlYXNlcykge1xuICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG4gICAgICAgIC8vIE90aGVyd2lzZSwgbG9nIGFuZCBjb250aW51ZVxuICAgICAgICBpZiAodGhpcy5zZWN1cml0eUxvZ2dlcikge1xuICAgICAgICAgIHRoaXMuc2VjdXJpdHlMb2dnZXIoJ3NpZ25hdHVyZV92ZXJpZmljYXRpb25fZXJyb3InLCB7XG4gICAgICAgICAgICB0YWdOYW1lLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiB7XG4gICAgICBjdXJyZW50VmVyc2lvbixcbiAgICAgIGxhdGVzdFZlcnNpb24sXG4gICAgICBpc1VwZGF0ZUF2YWlsYWJsZSxcbiAgICAgIHJlbGVhc2VEYXRlOiBwdWJsaXNoZWRBdCwgIC8vIFdpbGwgYmUgZm9ybWF0dGVkIGJ5IGZvcm1hdERhdGUoKSB3aGVuIGRpc3BsYXllZFxuICAgICAgcmVsZWFzZU5vdGVzLFxuICAgICAgcmVsZWFzZVVybDogcmVsZWFzZURhdGEuaHRtbF91cmwsXG4gICAgICB0YWdOYW1lLFxuICAgICAgc2lnbmF0dXJlVmVyaWZpZWQsXG4gICAgICBzaWduZXJJbmZvXG4gICAgfTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBjdXJyZW50IHJhdGUgbGltaXQgc3RhdHVzXG4gICAqIEByZXR1cm5zIEN1cnJlbnQgcmF0ZSBsaW1pdCBzdGF0dXMgaW5jbHVkaW5nIHJlbWFpbmluZyByZXF1ZXN0cyBhbmQgcmVzZXQgdGltZVxuICAgKi9cbiAgZ2V0UmF0ZUxpbWl0U3RhdHVzKCk6IHsgXG4gICAgYWxsb3dlZDogYm9vbGVhbjsgXG4gICAgcmVtYWluaW5nUmVxdWVzdHM6IG51bWJlcjsgXG4gICAgcmVzZXRUaW1lOiBEYXRlO1xuICAgIHdhaXRUaW1lU2Vjb25kcz86IG51bWJlcjtcbiAgfSB7XG4gICAgY29uc3Qgc3RhdHVzID0gdGhpcy5yYXRlTGltaXRlci5nZXRTdGF0dXMoKTtcbiAgICByZXR1cm4ge1xuICAgICAgYWxsb3dlZDogc3RhdHVzLmFsbG93ZWQsXG4gICAgICByZW1haW5pbmdSZXF1ZXN0czogc3RhdHVzLnJlbWFpbmluZ1Rva2VucyxcbiAgICAgIHJlc2V0VGltZTogc3RhdHVzLnJlc2V0VGltZSxcbiAgICAgIHdhaXRUaW1lU2Vjb25kczogc3RhdHVzLnJldHJ5QWZ0ZXJNcyA/IE1hdGguY2VpbChzdGF0dXMucmV0cnlBZnRlck1zIC8gMTAwMCkgOiB1bmRlZmluZWRcbiAgICB9O1xuICB9XG4gIFxuICAvKipcbiAgICogRm9ybWF0IHVwZGF0ZSBjaGVjayByZXN1bHRzIGZvciBkaXNwbGF5IHdpdGggY29tcHJlaGVuc2l2ZSBzYW5pdGl6YXRpb25cbiAgICogQHBhcmFtIHJlc3VsdCAtIFRoZSB1cGRhdGUgY2hlY2sgcmVzdWx0IHRvIGZvcm1hdFxuICAgKiBAcGFyYW0gZXJyb3IgLSBPcHRpb25hbCBlcnJvciBmcm9tIHVwZGF0ZSBjaGVja1xuICAgKiBAcGFyYW0gcGVyc29uYUluZGljYXRvciAtIE9wdGlvbmFsIHBlcnNvbmEgaW5kaWNhdG9yIHByZWZpeFxuICAgKiBAcmV0dXJucyBGb3JtYXR0ZWQgc3RyaW5nIHNhZmUgZm9yIGRpc3BsYXlcbiAgICovXG4gIGZvcm1hdFVwZGF0ZUNoZWNrUmVzdWx0KHJlc3VsdDogVXBkYXRlQ2hlY2tSZXN1bHQgfCBudWxsLCBlcnJvcj86IEVycm9yLCBwZXJzb25hSW5kaWNhdG9yOiBzdHJpbmcgPSAnJyk6IHN0cmluZyB7XG4gICAgaWYgKGVycm9yKSB7XG4gICAgICBjb25zdCBpc0Fib3J0RXJyb3IgPSBlcnJvci5uYW1lID09PSAnQWJvcnRFcnJvcic7XG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlIHx8IFN0cmluZyhlcnJvcik7XG4gICAgICBjb25zdCBpc1JhdGVMaW1pdEVycm9yID0gZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdSYXRlIGxpbWl0IGV4Y2VlZGVkJyk7XG4gICAgICBcbiAgICAgIGlmIChpc1JhdGVMaW1pdEVycm9yKSB7XG4gICAgICAgIHJldHVybiBwZXJzb25hSW5kaWNhdG9yICsgXG4gICAgICAgICAgJ+KPsyAqKlJhdGUgTGltaXQgRXhjZWVkZWQqKlxcblxcbicgK1xuICAgICAgICAgIGVycm9yLm1lc3NhZ2UgKyAnXFxuXFxuJyArXG4gICAgICAgICAgJyoqV2h5IHRoaXMgaGFwcGVuczoqKlxcbicgK1xuICAgICAgICAgICfigKIgVXBkYXRlIGNoZWNrcyBhcmUgbGltaXRlZCB0byBwcmV2ZW50IEFQSSBhYnVzZVxcbicgK1xuICAgICAgICAgICfigKIgR2l0SHViIEFQSSBoYXMgcmF0ZSBsaW1pdHMgZm9yIGFsbCBhcHBsaWNhdGlvbnNcXG5cXG4nICtcbiAgICAgICAgICAnKipXaGF0IHlvdSBjYW4gZG86KipcXG4nICtcbiAgICAgICAgICAn4oCiIFdhaXQgZm9yIHRoZSBzcGVjaWZpZWQgdGltZSBiZWZvcmUgY2hlY2tpbmcgYWdhaW5cXG4nICtcbiAgICAgICAgICAn4oCiIFVzZSBgZ2V0X3NlcnZlcl9zdGF0dXNgIHRvIHNlZSBjdXJyZW50IHZlcnNpb24gd2l0aG91dCBBUEkgY2FsbHNcXG4nICtcbiAgICAgICAgICAn4oCiIFZpc2l0IGh0dHBzOi8vZ2l0aHViLmNvbS9Eb2xsaG91c2VNQ1AvbWNwLXNlcnZlci9yZWxlYXNlcyBkaXJlY3RseSc7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHJldHVybiBwZXJzb25hSW5kaWNhdG9yICsgXG4gICAgICAgICfinYwgKipVcGRhdGUgQ2hlY2sgRmFpbGVkKipcXG5cXG4nICtcbiAgICAgICAgJ0Vycm9yOiAnICsgZXJyb3JNZXNzYWdlICsgJ1xcblxcbicgK1xuICAgICAgICAoaXNBYm9ydEVycm9yIFxuICAgICAgICAgID8gJ1RoZSByZXF1ZXN0IHRpbWVkIG91dC4gUGxlYXNlIGNoZWNrIHlvdXIgaW50ZXJuZXQgY29ubmVjdGlvbiBhbmQgdHJ5IGFnYWluLidcbiAgICAgICAgICA6ICdUaXBzOlxcbicgK1xuICAgICAgICAgICAgJ+KAoiBDaGVjayB5b3VyIGludGVybmV0IGNvbm5lY3Rpb25cXG4nICtcbiAgICAgICAgICAgICfigKIgRW5zdXJlIEdpdEh1Yi5jb20gaXMgYWNjZXNzaWJsZVxcbicgK1xuICAgICAgICAgICAgJ+KAoiBUcnkgcnVubmluZyBgdXBkYXRlX3NlcnZlciB0cnVlYCBmb3IgbWFudWFsIHVwZGF0ZVxcbicgK1xuICAgICAgICAgICAgJ+KAoiBWaXNpdCBodHRwczovL2dpdGh1Yi5jb20vRG9sbGhvdXNlTUNQL21jcC1zZXJ2ZXIvcmVsZWFzZXMnKTtcbiAgICB9XG4gICAgXG4gICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgIGNvbnN0IGN1cnJlbnRWZXJzaW9uID0gJ3Vua25vd24nO1xuICAgICAgcmV0dXJuIHBlcnNvbmFJbmRpY2F0b3IgKyBcbiAgICAgICAgJ/Cfk6YgKipVcGRhdGUgQ2hlY2sgQ29tcGxldGUqKlxcblxcbicgK1xuICAgICAgICAn8J+UhCAqKkN1cnJlbnQgVmVyc2lvbjoqKiAnICsgY3VycmVudFZlcnNpb24gKyAnXFxuJyArXG4gICAgICAgICfwn5OhICoqUmVtb3RlIFN0YXR1czoqKiBObyByZWxlYXNlcyBmb3VuZCBvbiBHaXRIdWJcXG4nICtcbiAgICAgICAgJ+KEue+4jyAqKk5vdGU6KiogVGhpcyBtYXkgYmUgYSBkZXZlbG9wbWVudCB2ZXJzaW9uIG9yIHJlbGVhc2VzIGhhdmVuXFwndCBiZWVuIHB1Ymxpc2hlZCB5ZXQuXFxuXFxuJyArXG4gICAgICAgICcqKk1hbnVhbCBVcGRhdGU6KipcXG4nICtcbiAgICAgICAgJ1VzZSBgdXBkYXRlX3NlcnZlciB0cnVlYCB0byBwdWxsIGxhdGVzdCBjaGFuZ2VzIGZyb20gbWFpbiBicmFuY2guJztcbiAgICB9XG4gICAgXG4gICAgY29uc3Qgc3RhdHVzUGFydHMgPSBbXG4gICAgICBwZXJzb25hSW5kaWNhdG9yICsgJ/Cfk6YgKipVcGRhdGUgQ2hlY2sgQ29tcGxldGUqKlxcblxcbicsXG4gICAgICAn8J+UhCAqKkN1cnJlbnQgVmVyc2lvbjoqKiAnICsgcmVzdWx0LmN1cnJlbnRWZXJzaW9uICsgJ1xcbicsXG4gICAgICAn8J+ToSAqKkxhdGVzdCBWZXJzaW9uOioqICcgKyByZXN1bHQubGF0ZXN0VmVyc2lvbiArICdcXG4nLFxuICAgICAgJ/Cfk4UgKipSZWxlYXNlZDoqKiAnICsgdGhpcy5mb3JtYXREYXRlKHJlc3VsdC5yZWxlYXNlRGF0ZSkgKyAnXFxuJ1xuICAgIF07XG4gICAgXG4gICAgLy8gQWRkIHNpZ25hdHVyZSB2ZXJpZmljYXRpb24gc3RhdHVzXG4gICAgaWYgKHJlc3VsdC5zaWduYXR1cmVWZXJpZmllZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAocmVzdWx0LnNpZ25hdHVyZVZlcmlmaWVkKSB7XG4gICAgICAgIHN0YXR1c1BhcnRzLnB1c2goJ+KchSAqKlNpZ25hdHVyZToqKiBWZXJpZmllZCcpO1xuICAgICAgICBpZiAocmVzdWx0LnNpZ25lckluZm8pIHtcbiAgICAgICAgICBzdGF0dXNQYXJ0cy5wdXNoKGAgYnkgJHtyZXN1bHQuc2lnbmVySW5mb31gKTtcbiAgICAgICAgfVxuICAgICAgICBzdGF0dXNQYXJ0cy5wdXNoKCdcXG4nKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHN0YXR1c1BhcnRzLnB1c2goJ+KaoO+4jyAqKlNpZ25hdHVyZToqKiBOb3QgdmVyaWZpZWRcXG4nKTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgc3RhdHVzUGFydHMucHVzaCgnXFxuJyk7XG4gICAgXG4gICAgaWYgKHJlc3VsdC5pc1VwZGF0ZUF2YWlsYWJsZSkge1xuICAgICAgc3RhdHVzUGFydHMucHVzaChcbiAgICAgICAgJ+KcqCAqKlVwZGF0ZSBBdmFpbGFibGUhKipcXG5cXG4nLFxuICAgICAgICAnKipXaGF0XFwncyBOZXc6KipcXG4nICsgdGhpcy5zYW5pdGl6ZVJlbGVhc2VOb3RlcyhyZXN1bHQucmVsZWFzZU5vdGVzKSArICdcXG5cXG4nLFxuICAgICAgICAnKipUbyBVcGRhdGU6KipcXG4nLFxuICAgICAgICAn4oCiIFVzZTogYHVwZGF0ZV9zZXJ2ZXIgdHJ1ZWBcXG4nLFxuICAgICAgICAn4oCiIE9yIHZpc2l0OiAnICsgdGhpcy5zYW5pdGl6ZVVybChyZXN1bHQucmVsZWFzZVVybCkgKyAnXFxuXFxuJyxcbiAgICAgICAgJ+KaoO+4jyAqKk5vdGU6KiogVXBkYXRlIHdpbGwgcmVzdGFydCB0aGUgc2VydmVyIGFuZCByZWxvYWQgYWxsIHBlcnNvbmFzLidcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN0YXR1c1BhcnRzLnB1c2goXG4gICAgICAgICfinIUgKipZb3VcXCdyZSBVcCB0byBEYXRlISoqXFxuXFxuJyxcbiAgICAgICAgJ1lvdXIgRG9sbGhvdXNlTUNQIGluc3RhbGxhdGlvbiBpcyBjdXJyZW50LlxcbicsXG4gICAgICAgICdDaGVjayBiYWNrIGxhdGVyIGZvciBuZXcgZmVhdHVyZXMgYW5kIGltcHJvdmVtZW50cy4nXG4gICAgICApO1xuICAgIH1cbiAgICBcbiAgICByZXR1cm4gc3RhdHVzUGFydHMuam9pbignJyk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBTYW5pdGl6ZSBVUkxzIHRvIHByZXZlbnQgZGFuZ2Vyb3VzIHNjaGVtZXMgYW5kIGluZm9ybWF0aW9uIGRpc2Nsb3N1cmVcbiAgICogXG4gICAqIFNlY3VyaXR5IG1lYXN1cmVzOlxuICAgKiAtIExlbmd0aCB2YWxpZGF0aW9uIHRvIHByZXZlbnQgRG9TXG4gICAqIC0gV2hpdGVsaXN0IGFwcHJvYWNoOiBvbmx5IGh0dHAvaHR0cHMgYWxsb3dlZFxuICAgKiAtIFNhbml0aXplZCBsb2dnaW5nIHRvIHByZXZlbnQgc2Vuc2l0aXZlIGRhdGEgZXhwb3N1cmVcbiAgICogXG4gICAqIEBwYXJhbSB1cmwgLSBUaGUgVVJMIHRvIHNhbml0aXplXG4gICAqIEByZXR1cm5zIEVtcHR5IHN0cmluZyBpZiBpbnZhbGlkL2Rhbmdlcm91cywgb3JpZ2luYWwgVVJMIGlmIHNhZmVcbiAgICovXG4gIHByaXZhdGUgc2FuaXRpemVVcmwodXJsOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGlmICghdXJsKSByZXR1cm4gJyc7XG4gICAgXG4gICAgLy8gQ2hlY2sgVVJMIGxlbmd0aFxuICAgIGlmICh1cmwubGVuZ3RoID4gdGhpcy51cmxNYXhMZW5ndGgpIHtcbiAgICAgIHRoaXMubG9nU2VjdXJpdHlFdmVudCgndXJsX3Rvb19sb25nJywgeyBcbiAgICAgICAgbGVuZ3RoOiB1cmwubGVuZ3RoLCBcbiAgICAgICAgbWF4TGVuZ3RoOiB0aGlzLnVybE1heExlbmd0aCxcbiAgICAgICAgdXJsUHJlZml4OiB1cmwuc3Vic3RyaW5nKDAsIDUwKSArICcuLi4nICAvLyBPbmx5IGxvZyBmaXJzdCA1MCBjaGFyc1xuICAgICAgfSk7XG4gICAgICByZXR1cm4gJyc7ICAvLyBVUkwgdG9vIGxvbmdcbiAgICB9XG4gICAgXG4gICAgLy8gT25seSBhbGxvdyBodHRwIGFuZCBodHRwcyBzY2hlbWVzXG4gICAgY29uc3QgYWxsb3dlZFNjaGVtZXMgPSBbJ2h0dHA6JywgJ2h0dHBzOiddO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBwYXJzZWQgPSBuZXcgVVJMKHVybCk7XG4gICAgICBpZiAoIWFsbG93ZWRTY2hlbWVzLmluY2x1ZGVzKHBhcnNlZC5wcm90b2NvbCkpIHtcbiAgICAgICAgdGhpcy5sb2dTZWN1cml0eUV2ZW50KCdkYW5nZXJvdXNfdXJsX3NjaGVtZScsIHsgXG4gICAgICAgICAgc2NoZW1lOiBwYXJzZWQucHJvdG9jb2wsXG4gICAgICAgICAgaG9zdDogcGFyc2VkLmhvc3RuYW1lICAvLyBMb2cgb25seSBob3N0bmFtZSwgbm90IGZ1bGwgVVJMXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gJyc7ICAvLyBSZXR1cm4gZW1wdHkgc3RyaW5nIGZvciBkYW5nZXJvdXMgc2NoZW1lc1xuICAgICAgfVxuICAgICAgcmV0dXJuIHVybDtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHRoaXMubG9nU2VjdXJpdHlFdmVudCgnaW52YWxpZF91cmwnLCB7IFxuICAgICAgICB1cmxMZW5ndGg6IHVybC5sZW5ndGggIC8vIExvZyBsZW5ndGggb25seSwgbm90IGNvbnRlbnRcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuICcnOyAgLy8gSW52YWxpZCBVUkxcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBTYW5pdGl6ZSByZWxlYXNlIG5vdGVzIHRvIHByZXZlbnQgWFNTLCBjb21tYW5kIGluamVjdGlvbiwgYW5kIERvU1xuICAgKiBcbiAgICogU2VjdXJpdHkgbGF5ZXJzOlxuICAgKiAxLiBMZW5ndGggbGltaXRpbmcgKGNvbmZpZ3VyYWJsZSwgZGVmYXVsdCA1MDAwIGNoYXJzKVxuICAgKiAyLiBIVE1ML0pTIHNhbml0aXphdGlvbiB2aWEgRE9NUHVyaWZ5IChubyB0YWdzL2F0dHJpYnV0ZXMgYWxsb3dlZClcbiAgICogMy4gQ29tbWFuZCBpbmplY3Rpb24gcGF0dGVybiByZW1vdmFsIChiYWNrdGlja3MsIGNvbW1hbmQgc3Vic3RpdHV0aW9uKVxuICAgKiA0LiBPV0FTUCBwYXR0ZXJuIHJlbW92YWwgKFBIUCwgQVNQLCBoZXgvdW5pY29kZS9vY3RhbCBlc2NhcGVzKVxuICAgKiBcbiAgICogQHBhcmFtIG5vdGVzIC0gVGhlIHJlbGVhc2Ugbm90ZXMgdG8gc2FuaXRpemVcbiAgICogQHJldHVybnMgU2FuaXRpemVkIHJlbGVhc2Ugbm90ZXMgc2FmZSBmb3IgZGlzcGxheVxuICAgKi9cbiAgcHJpdmF0ZSBzYW5pdGl6ZVJlbGVhc2VOb3Rlcyhub3Rlczogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBpZiAoIW5vdGVzKSByZXR1cm4gJ1NlZSByZWxlYXNlIG5vdGVzIG9uIEdpdEh1Yic7XG4gICAgXG4gICAgLy8gRmlyc3QsIG5vcm1hbGl6ZSBVbmljb2RlIHRvIHByZXZlbnQgYnlwYXNzIGF0dGFja3NcbiAgICBjb25zdCB1bmljb2RlUmVzdWx0ID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUobm90ZXMpO1xuICAgIGxldCBzYW5pdGl6ZWQgPSB1bmljb2RlUmVzdWx0Lm5vcm1hbGl6ZWRDb250ZW50O1xuICAgIFxuICAgIGlmICh1bmljb2RlUmVzdWx0LmRldGVjdGVkSXNzdWVzICYmIHVuaWNvZGVSZXN1bHQuZGV0ZWN0ZWRJc3N1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5sb2dTZWN1cml0eUV2ZW50KCd1bmljb2RlX2lzc3Vlc19pbl9yZWxlYXNlX25vdGVzJywge1xuICAgICAgICBpc3N1ZXM6IHVuaWNvZGVSZXN1bHQuZGV0ZWN0ZWRJc3N1ZXMsXG4gICAgICAgIHNldmVyaXR5OiB1bmljb2RlUmVzdWx0LnNldmVyaXR5XG4gICAgICB9KTtcbiAgICB9XG4gICAgXG4gICAgLy8gQXBwbHkgbGVuZ3RoIGxpbWl0XG4gICAgaWYgKHNhbml0aXplZC5sZW5ndGggPiB0aGlzLnJlbGVhc2VOb3Rlc01heExlbmd0aCkge1xuICAgICAgdGhpcy5sb2dTZWN1cml0eUV2ZW50KCdyZWxlYXNlX25vdGVzX3RydW5jYXRlZCcsIHsgXG4gICAgICAgIG9yaWdpbmFsTGVuZ3RoOiBzYW5pdGl6ZWQubGVuZ3RoLCBcbiAgICAgICAgbWF4TGVuZ3RoOiB0aGlzLnJlbGVhc2VOb3Rlc01heExlbmd0aCBcbiAgICAgIH0pO1xuICAgICAgc2FuaXRpemVkID0gc2FuaXRpemVkLnN1YnN0cmluZygwLCB0aGlzLnJlbGVhc2VOb3Rlc01heExlbmd0aCkgKyAnLi4uJztcbiAgICB9XG4gICAgXG4gICAgLy8gVXNlIGNhY2hlZCBET01QdXJpZnkgaW5zdGFuY2Ugd2l0aCBhdXRvbWF0aWMgcmVjb3ZlcnlcbiAgICBpZiAoIVVwZGF0ZUNoZWNrZXIucHVyaWZ5IHx8ICFVcGRhdGVDaGVja2VyLnB1cmlmeVdpbmRvdykge1xuICAgICAgLy8gUmVpbml0aWFsaXplIGlmIHNvbWVob3cgY29ycnVwdGVkIC0gcHJvdmlkZXMgcmVzaWxpZW5jZVxuICAgICAgY29uc3QgZG9tID0gbmV3IEpTRE9NKCcnKTtcbiAgICAgIFVwZGF0ZUNoZWNrZXIucHVyaWZ5V2luZG93ID0gZG9tLndpbmRvdztcbiAgICAgIFVwZGF0ZUNoZWNrZXIucHVyaWZ5ID0gRE9NUHVyaWZ5KFVwZGF0ZUNoZWNrZXIucHVyaWZ5V2luZG93KTtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgYmVmb3JlU2FuaXRpemUgPSBzYW5pdGl6ZWQ7XG4gICAgXG4gICAgLy8gQ1JJVElDQUwgRklYOiBJbml0aWFsaXplIERPTVB1cmlmeSBsYXppbHkgdG8gcHJldmVudCBzdGFydHVwIGNyYXNoZXNcbiAgICB0aGlzLmluaXRpYWxpemVET01QdXJpZnkoKTtcbiAgICBcbiAgICAvLyBET01QdXJpZnkgY29uZmlndXJhdGlvbiBmb3IgbWF4aW11bSBzZWN1cml0eVxuICAgIC8vIEFMTE9XRURfVEFHUzogW10gc3RyaXBzIGFsbCBIVE1MIHRhZ3NcbiAgICAvLyBBTExPV0VEX0FUVFI6IFtdIHN0cmlwcyBhbGwgYXR0cmlidXRlc1xuICAgIC8vIEFkZGl0aW9uYWwgb3B0aW9ucyBmb3IgZXh0cmEgc2VjdXJpdHlcbiAgICBpZiAoVXBkYXRlQ2hlY2tlci5wdXJpZnkgJiYgVXBkYXRlQ2hlY2tlci5wdXJpZnkuc2FuaXRpemUpIHtcbiAgICAgIHNhbml0aXplZCA9IFVwZGF0ZUNoZWNrZXIucHVyaWZ5LnNhbml0aXplKHNhbml0aXplZCwgeyBcbiAgICAgICAgQUxMT1dFRF9UQUdTOiBbXSwgICAgICAvLyBTdHJpcCBhbGwgSFRNTCB0YWdzXG4gICAgICAgIEFMTE9XRURfQVRUUjogW10sICAgICAgLy8gU3RyaXAgYWxsIGF0dHJpYnV0ZXNcbiAgICAgICAgRk9SQklEX1RBR1M6IFsnc3R5bGUnLCAnc2NyaXB0JywgJ2lmcmFtZScsICdvYmplY3QnLCAnZW1iZWQnLCAnbGluayddLFxuICAgICAgICBGT1JCSURfQVRUUjogWydvbmVycm9yJywgJ29ubG9hZCcsICdvbmNsaWNrJywgJ29ubW91c2VvdmVyJ11cbiAgICAgIH0pO1xuICAgIH1cbiAgICBcbiAgICBpZiAoYmVmb3JlU2FuaXRpemUgIT09IHNhbml0aXplZCkge1xuICAgICAgdGhpcy5sb2dTZWN1cml0eUV2ZW50KCdodG1sX2NvbnRlbnRfcmVtb3ZlZCcsIHsgXG4gICAgICAgIHJlbW92ZWRMZW5ndGg6IGJlZm9yZVNhbml0aXplLmxlbmd0aCAtIHNhbml0aXplZC5sZW5ndGggXG4gICAgICB9KTtcbiAgICB9XG4gICAgXG4gICAgLy8gQWRkaXRpb25hbCBzYW5pdGl6YXRpb24gZm9yIGNvbW1hbmQgaW5qZWN0aW9uIHBhdHRlcm5zXG4gICAgLy8gU2luZ2xlLXBhc3MgcHJvY2Vzc2luZyBmb3IgcGVyZm9ybWFuY2Ugd2hpbGUgbWFpbnRhaW5pbmcgc2VjdXJpdHlcbiAgICAvLyBUaGVzZSBwYXR0ZXJucyBjb3ZlciB2YXJpb3VzIGluamVjdGlvbiB2ZWN0b3JzIGJleW9uZCBIVE1ML0pTXG4gICAgLy8gTGVuZ3RoIGxpbWl0cyBhZGRlZCB0byBwcmV2ZW50IFJlRG9TIGF0dGFja3NcbiAgICBjb25zdCBwYXR0ZXJucyA9IFtcbiAgICAgIC9gW15gXXswLDEwMDB9YC9nLCAgICAgICAgICAgLy8gQmFja3RpY2sgZXhwcmVzc2lvbnMgKGxpbWl0ZWQgdG8gMTAwMCBjaGFycylcbiAgICAgIC9cXCRcXChbXildezAsMTAwMH1cXCkvZywgICAgICAgLy8gQ29tbWFuZCBzdWJzdGl0dXRpb24gKGxpbWl0ZWQgdG8gMTAwMCBjaGFycylcbiAgICAgIC9cXCRcXHtbXn1dezAsMTAwMH1cXH0vZywgICAgICAgLy8gVmFyaWFibGUgZXhwYW5zaW9uIChsaW1pdGVkIHRvIDEwMDAgY2hhcnMpXG4gICAgICAvPFxcP1tePl17MCwxMDAwfVxcPz4vZywgICAgICAgLy8gUEhQIHRhZ3MgKE9XQVNQKSAobGltaXRlZCB0byAxMDAwIGNoYXJzKVxuICAgICAgLyZsdDslW14+XXswLDEwMDB9JSZndDsvZywgICAvLyBBU1AgdGFncyAoSFRNTC1lbmNvZGVkIGJ5IERPTVB1cmlmeSkgKGxpbWl0ZWQgdG8gMTAwMCBjaGFycylcbiAgICAgIC88JVtePl17MCwxMDAwfSU+L2csICAgICAgICAgLy8gQVNQIHRhZ3MgKHJhdykgKGxpbWl0ZWQgdG8gMTAwMCBjaGFycylcbiAgICAgIC9cXFxceFswLTlhLWZBLUZdezJ9L2csICAgICAgICAvLyBIZXggZXNjYXBlcyAoT1dBU1ApIC0gYWxyZWFkeSBsaW1pdGVkIGJ5IHsyfVxuICAgICAgL1xcXFx1WzAtOWEtZkEtRl17NH0vZywgICAgICAgIC8vIFVuaWNvZGUgZXNjYXBlcyAtIGFscmVhZHkgbGltaXRlZCBieSB7NH1cbiAgICAgIC9cXFxcWzAtN117MSwzfS9nICAgICAgICAgICAgICAvLyBPY3RhbCBlc2NhcGVzIC0gYWxyZWFkeSBsaW1pdGVkIGJ5IHsxLDN9XG4gICAgXTtcbiAgICBcbiAgICBjb25zdCBiZWZvcmVQYXR0ZXJucyA9IHNhbml0aXplZDtcbiAgICBmb3IgKGNvbnN0IHBhdHRlcm4gb2YgcGF0dGVybnMpIHtcbiAgICAgIHNhbml0aXplZCA9IHNhbml0aXplZC5yZXBsYWNlKHBhdHRlcm4sICcnKTtcbiAgICB9XG4gICAgXG4gICAgaWYgKGJlZm9yZVBhdHRlcm5zICE9PSBzYW5pdGl6ZWQpIHtcbiAgICAgIHRoaXMubG9nU2VjdXJpdHlFdmVudCgnaW5qZWN0aW9uX3BhdHRlcm5zX3JlbW92ZWQnLCB7IFxuICAgICAgICByZW1vdmVkTGVuZ3RoOiBiZWZvcmVQYXR0ZXJucy5sZW5ndGggLSBzYW5pdGl6ZWQubGVuZ3RoIFxuICAgICAgfSk7XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBzYW5pdGl6ZWQ7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBGb3JtYXQgZGF0ZSB0byBodW1hbi1yZWFkYWJsZSBmb3JtYXQgd2l0aCBjb25zaXN0ZW50IHRpbWV6b25lIGhhbmRsaW5nXG4gICAqIEBwYXJhbSBkYXRlU3RyIC0gSVNPIGRhdGUgc3RyaW5nIHRvIGZvcm1hdFxuICAgKiBAcmV0dXJucyBIdW1hbi1yZWFkYWJsZSBkYXRlIHN0cmluZyAoZS5nLiwgXCJKYW51YXJ5IDUsIDIwMjVcIilcbiAgICovXG4gIHByaXZhdGUgZm9ybWF0RGF0ZShkYXRlU3RyOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoZGF0ZVN0cik7XG4gICAgICBpZiAoaXNOYU4oZGF0ZS5nZXRUaW1lKCkpKSB7XG4gICAgICAgIHJldHVybiBkYXRlU3RyOyAgLy8gUmV0dXJuIG9yaWdpbmFsIGlmIGludmFsaWRcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gVXNlIFVUQyBtZXRob2RzIHRvIGVuc3VyZSBjb25zaXN0ZW50IHRpbWV6b25lIGhhbmRsaW5nXG4gICAgICByZXR1cm4gZGF0ZS50b0xvY2FsZURhdGVTdHJpbmcoJ2VuLVVTJywge1xuICAgICAgICB5ZWFyOiAnbnVtZXJpYycsXG4gICAgICAgIG1vbnRoOiAnbG9uZycsXG4gICAgICAgIGRheTogJ251bWVyaWMnLFxuICAgICAgICB0aW1lWm9uZTogJ1VUQycgIC8vIEVuc3VyZSBjb25zaXN0ZW50IHRpbWV6b25lXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBkYXRlU3RyOyAgLy8gUmV0dXJuIG9yaWdpbmFsIG9uIGVycm9yXG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogTG9nIHNlY3VyaXR5IGV2ZW50cyBmb3IgbW9uaXRvcmluZyBhbmQgYWxlcnRpbmdcbiAgICogT25seSBsb2dzIGlmIHNlY3VyaXR5TG9nZ2VyIGNhbGxiYWNrIHdhcyBwcm92aWRlZCBpbiBjb25zdHJ1Y3RvclxuICAgKiBAcGFyYW0gZXZlbnQgLSBUaGUgc2VjdXJpdHkgZXZlbnQgdHlwZVxuICAgKiBAcGFyYW0gZGV0YWlscyAtIEV2ZW50IGRldGFpbHMgKHNhbml0aXplZCB0byBwcmV2ZW50IGluZm8gZGlzY2xvc3VyZSlcbiAgICovXG4gIHByaXZhdGUgbG9nU2VjdXJpdHlFdmVudChldmVudDogc3RyaW5nLCBkZXRhaWxzOiBhbnkpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zZWN1cml0eUxvZ2dlcikge1xuICAgICAgdGhpcy5zZWN1cml0eUxvZ2dlcihldmVudCwgZGV0YWlscyk7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogUmVzZXQgc3RhdGljIERPTVB1cmlmeSBjYWNoZSAodXNlZnVsIGZvciBsb25nLXJ1bm5pbmcgcHJvY2Vzc2VzKVxuICAgKiBUaGlzIHByZXZlbnRzIG1lbW9yeSBhY2N1bXVsYXRpb24gaW4gc2VydmljZXMgdGhhdCBydW4gZm9yIGV4dGVuZGVkIHBlcmlvZHNcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcHVibGljIHN0YXRpYyByZXNldENhY2hlKCk6IHZvaWQge1xuICAgIFVwZGF0ZUNoZWNrZXIucHVyaWZ5V2luZG93ID0gbnVsbDtcbiAgICBVcGRhdGVDaGVja2VyLnB1cmlmeSA9IG51bGw7XG4gIH1cbn0iXX0=