@dollhousemcp/mcp-server 2.0.5 → 2.0.6

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.
@@ -1,204 +0,0 @@
1
- /**
2
- * Embedded HTML template for the DollhouseMCP Log Viewer.
3
- *
4
- * Returns a self-contained vanilla JS/CSS page that connects to the
5
- * SSELogSink's /logs/stream endpoint via EventSource. See docs/LOGGING-DESIGN.md §4.6.
6
- */
7
- export function getViewerHtml(port) {
8
- return /* html */ `<!DOCTYPE html>
9
- <html lang="en">
10
- <head>
11
- <meta charset="utf-8">
12
- <meta name="viewport" content="width=device-width,initial-scale=1">
13
- <title>DollhouseMCP Log Viewer</title>
14
- <style>
15
- *{box-sizing:border-box;margin:0;padding:0}
16
- body{background:#1a1a2e;color:#e0e0e0;font-family:'Cascadia Code','Fira Code',monospace;font-size:13px}
17
- #controls{display:flex;gap:8px;padding:8px 12px;background:#16213e;border-bottom:1px solid #0f3460;align-items:center;flex-wrap:wrap}
18
- #controls label{color:#94a3b8;font-size:12px}
19
- #controls select,#controls input{background:#1a1a2e;color:#e0e0e0;border:1px solid #0f3460;border-radius:4px;padding:4px 8px;font-family:inherit;font-size:12px}
20
- #controls select:focus,#controls input:focus{outline:none;border-color:#e94560}
21
- button{background:#0f3460;color:#e0e0e0;border:1px solid #0f3460;border-radius:4px;padding:4px 12px;cursor:pointer;font-family:inherit;font-size:12px}
22
- button:hover{background:#e94560;border-color:#e94560}
23
- #status{margin-left:auto;font-size:11px;padding:2px 8px;border-radius:10px}
24
- .connected{background:#064e3b;color:#6ee7b7}
25
- .disconnected{background:#7f1d1d;color:#fca5a5}
26
- .paused{background:#78350f;color:#fcd34d}
27
- #log{overflow-y:auto;height:calc(100vh - 44px);padding:8px 12px}
28
- .entry{padding:2px 0;white-space:pre-wrap;word-break:break-word;border-bottom:1px solid #1e293b}
29
- .entry:hover{background:#16213e}
30
- .lvl-error{color:#f87171}
31
- .lvl-warn{color:#fbbf24}
32
- .lvl-info{color:#60a5fa}
33
- .lvl-debug{color:#9ca3af}
34
- .ts{color:#64748b}
35
- .cat{color:#a78bfa}
36
- .src{color:#2dd4bf}
37
- .cid{color:#f472b6;font-size:11px;display:inline-block;width:72px;text-align:right;margin-right:4px}
38
- #search{margin-left:4px}
39
- </style>
40
- </head>
41
- <body>
42
- <div id="controls">
43
- <label>Category
44
- <select id="fCategory">
45
- <option value="">all</option>
46
- <option value="application">application</option>
47
- <option value="security">security</option>
48
- <option value="performance">performance</option>
49
- <option value="telemetry">telemetry</option>
50
- </select>
51
- </label>
52
- <label>Level
53
- <select id="fLevel">
54
- <option value="">all</option>
55
- <option value="debug">debug</option>
56
- <option value="info">info</option>
57
- <option value="warn">warn</option>
58
- <option value="error">error</option>
59
- </select>
60
- </label>
61
- <label>Source <input id="fSource" placeholder="substring" size="14"></label>
62
- <label>RequestId <input id="fCorrelationId" placeholder="correlationId" size="20"></label>
63
- <label>Search <input id="search" placeholder="message filter" size="18"></label>
64
- <button id="btnPause">Pause</button>
65
- <button id="btnClear">Clear</button>
66
- <span id="status" class="disconnected">disconnected</span>
67
- </div>
68
- <div id="log"></div>
69
- <script>
70
- (function(){
71
- var BASE = 'http://127.0.0.1:${port}';
72
- var MAX_ENTRIES = 1000;
73
- var log = document.getElementById('log');
74
- var status = document.getElementById('status');
75
- var fCategory = document.getElementById('fCategory');
76
- var fLevel = document.getElementById('fLevel');
77
- var fSource = document.getElementById('fSource');
78
- var fCorrelationId = document.getElementById('fCorrelationId');
79
- var searchBox = document.getElementById('search');
80
- var btnPause = document.getElementById('btnPause');
81
- var btnClear = document.getElementById('btnClear');
82
- var es = null;
83
- var paused = false;
84
- var buffer = [];
85
-
86
- function escHtml(s){
87
- var d = document.createElement('div');
88
- d.appendChild(document.createTextNode(s));
89
- return d.innerHTML;
90
- }
91
-
92
- function setStatus(s, cls){
93
- status.textContent = s;
94
- status.className = cls;
95
- }
96
-
97
-
98
-
99
- var LEVEL_ORDER = {debug:0, info:1, warn:2, error:3};
100
-
101
- function matchesFilters(entry){
102
- var cat = fCategory.value;
103
- if(cat && entry.category !== cat) return false;
104
- var lvl = fLevel.value;
105
- if(lvl && (LEVEL_ORDER[entry.level]||0) < (LEVEL_ORDER[lvl]||0)) return false;
106
- var src = fSource.value.toLowerCase();
107
- if(src && (!entry.source || entry.source.toLowerCase().indexOf(src) === -1)) return false;
108
- var cid = fCorrelationId.value;
109
- if(cid && entry.correlationId !== cid) return false;
110
- var needle = searchBox.value.toLowerCase();
111
- if(needle && (!entry.message || entry.message.toLowerCase().indexOf(needle) === -1)) return false;
112
- return true;
113
- }
114
-
115
- function refilter(){
116
- var els = log.children;
117
- for(var i = 0; i < els.length; i++){
118
- var data = els[i]._entryData;
119
- if(data){
120
- els[i].style.display = matchesFilters(data) ? '' : 'none';
121
- }
122
- }
123
- }
124
-
125
- function addEntry(entry){
126
- if(!matchesFilters(entry)) var hidden = true;
127
-
128
- var el = document.createElement('div');
129
- el.className = 'entry lvl-' + entry.level;
130
- el._entryData = entry;
131
- if(hidden) el.style.display = 'none';
132
- var ts = entry.timestamp ? entry.timestamp.slice(11, 23) : '';
133
- var cid = entry.correlationId ? entry.correlationId.slice(-8) : '';
134
- el.innerHTML = '<span class="ts">' + escHtml(ts) + '</span> '
135
- + '<span class="cid">' + escHtml(cid) + '</span> '
136
- + '<span class="cat">[' + escHtml(entry.category) + ']</span> '
137
- + '<span class="src">' + escHtml(entry.source) + '</span> '
138
- + escHtml(entry.message);
139
- log.appendChild(el);
140
-
141
- while(log.children.length > MAX_ENTRIES){
142
- log.removeChild(log.firstChild);
143
- }
144
- if(!hidden) log.scrollTop = log.scrollHeight;
145
- }
146
-
147
- function connect(){
148
- if(es){ es.close(); }
149
- es = new EventSource(BASE + '/logs/stream');
150
- es.onopen = function(){ setStatus('connected', 'connected'); };
151
- es.onmessage = function(e){
152
- try{
153
- var entry = JSON.parse(e.data);
154
- if(paused){ buffer.push(entry); }
155
- else { addEntry(entry); }
156
- }catch(err){}
157
- };
158
- es.onerror = function(){ setStatus('disconnected', 'disconnected'); };
159
- }
160
-
161
- var filterTimer = null;
162
- function onFilterChange(){
163
- clearTimeout(filterTimer);
164
- filterTimer = setTimeout(refilter, 50);
165
- }
166
-
167
- fCategory.addEventListener('change', onFilterChange);
168
- fLevel.addEventListener('change', onFilterChange);
169
- fSource.addEventListener('input', function(){
170
- clearTimeout(filterTimer);
171
- filterTimer = setTimeout(refilter, 400);
172
- });
173
- fCorrelationId.addEventListener('input', function(){
174
- clearTimeout(filterTimer);
175
- filterTimer = setTimeout(refilter, 400);
176
- });
177
- searchBox.addEventListener('input', function(){
178
- clearTimeout(filterTimer);
179
- filterTimer = setTimeout(refilter, 400);
180
- });
181
-
182
- btnPause.addEventListener('click', function(){
183
- paused = !paused;
184
- btnPause.textContent = paused ? 'Resume' : 'Pause';
185
- if(paused){
186
- setStatus('paused', 'paused');
187
- } else {
188
- setStatus('connected', 'connected');
189
- buffer.forEach(addEntry);
190
- buffer = [];
191
- }
192
- });
193
-
194
- btnClear.addEventListener('click', function(){
195
- log.innerHTML = '';
196
- });
197
-
198
- connect();
199
- })();
200
- </script>
201
- </body>
202
- </html>`;
203
- }
204
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlld2VySHRtbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9sb2dnaW5nL3ZpZXdlci92aWV3ZXJIdG1sLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7OztHQUtHO0FBRUgsTUFBTSxVQUFVLGFBQWEsQ0FBQyxJQUFZO0lBQ3hDLE9BQU8sVUFBVSxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7aUNBK0RhLElBQUk7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1FBbUk3QixDQUFDO0FBQ1QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRW1iZWRkZWQgSFRNTCB0ZW1wbGF0ZSBmb3IgdGhlIERvbGxob3VzZU1DUCBMb2cgVmlld2VyLlxuICpcbiAqIFJldHVybnMgYSBzZWxmLWNvbnRhaW5lZCB2YW5pbGxhIEpTL0NTUyBwYWdlIHRoYXQgY29ubmVjdHMgdG8gdGhlXG4gKiBTU0VMb2dTaW5rJ3MgL2xvZ3Mvc3RyZWFtIGVuZHBvaW50IHZpYSBFdmVudFNvdXJjZS4gU2VlIGRvY3MvTE9HR0lORy1ERVNJR04ubWQgwqc0LjYuXG4gKi9cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFZpZXdlckh0bWwocG9ydDogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIC8qIGh0bWwgKi8gYDwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG48aGVhZD5cbjxtZXRhIGNoYXJzZXQ9XCJ1dGYtOFwiPlxuPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCxpbml0aWFsLXNjYWxlPTFcIj5cbjx0aXRsZT5Eb2xsaG91c2VNQ1AgTG9nIFZpZXdlcjwvdGl0bGU+XG48c3R5bGU+XG4qe2JveC1zaXppbmc6Ym9yZGVyLWJveDttYXJnaW46MDtwYWRkaW5nOjB9XG5ib2R5e2JhY2tncm91bmQ6IzFhMWEyZTtjb2xvcjojZTBlMGUwO2ZvbnQtZmFtaWx5OidDYXNjYWRpYSBDb2RlJywnRmlyYSBDb2RlJyxtb25vc3BhY2U7Zm9udC1zaXplOjEzcHh9XG4jY29udHJvbHN7ZGlzcGxheTpmbGV4O2dhcDo4cHg7cGFkZGluZzo4cHggMTJweDtiYWNrZ3JvdW5kOiMxNjIxM2U7Ym9yZGVyLWJvdHRvbToxcHggc29saWQgIzBmMzQ2MDthbGlnbi1pdGVtczpjZW50ZXI7ZmxleC13cmFwOndyYXB9XG4jY29udHJvbHMgbGFiZWx7Y29sb3I6Izk0YTNiODtmb250LXNpemU6MTJweH1cbiNjb250cm9scyBzZWxlY3QsI2NvbnRyb2xzIGlucHV0e2JhY2tncm91bmQ6IzFhMWEyZTtjb2xvcjojZTBlMGUwO2JvcmRlcjoxcHggc29saWQgIzBmMzQ2MDtib3JkZXItcmFkaXVzOjRweDtwYWRkaW5nOjRweCA4cHg7Zm9udC1mYW1pbHk6aW5oZXJpdDtmb250LXNpemU6MTJweH1cbiNjb250cm9scyBzZWxlY3Q6Zm9jdXMsI2NvbnRyb2xzIGlucHV0OmZvY3Vze291dGxpbmU6bm9uZTtib3JkZXItY29sb3I6I2U5NDU2MH1cbmJ1dHRvbntiYWNrZ3JvdW5kOiMwZjM0NjA7Y29sb3I6I2UwZTBlMDtib3JkZXI6MXB4IHNvbGlkICMwZjM0NjA7Ym9yZGVyLXJhZGl1czo0cHg7cGFkZGluZzo0cHggMTJweDtjdXJzb3I6cG9pbnRlcjtmb250LWZhbWlseTppbmhlcml0O2ZvbnQtc2l6ZToxMnB4fVxuYnV0dG9uOmhvdmVye2JhY2tncm91bmQ6I2U5NDU2MDtib3JkZXItY29sb3I6I2U5NDU2MH1cbiNzdGF0dXN7bWFyZ2luLWxlZnQ6YXV0bztmb250LXNpemU6MTFweDtwYWRkaW5nOjJweCA4cHg7Ym9yZGVyLXJhZGl1czoxMHB4fVxuLmNvbm5lY3RlZHtiYWNrZ3JvdW5kOiMwNjRlM2I7Y29sb3I6IzZlZTdiN31cbi5kaXNjb25uZWN0ZWR7YmFja2dyb3VuZDojN2YxZDFkO2NvbG9yOiNmY2E1YTV9XG4ucGF1c2Vke2JhY2tncm91bmQ6Izc4MzUwZjtjb2xvcjojZmNkMzRkfVxuI2xvZ3tvdmVyZmxvdy15OmF1dG87aGVpZ2h0OmNhbGMoMTAwdmggLSA0NHB4KTtwYWRkaW5nOjhweCAxMnB4fVxuLmVudHJ5e3BhZGRpbmc6MnB4IDA7d2hpdGUtc3BhY2U6cHJlLXdyYXA7d29yZC1icmVhazpicmVhay13b3JkO2JvcmRlci1ib3R0b206MXB4IHNvbGlkICMxZTI5M2J9XG4uZW50cnk6aG92ZXJ7YmFja2dyb3VuZDojMTYyMTNlfVxuLmx2bC1lcnJvcntjb2xvcjojZjg3MTcxfVxuLmx2bC13YXJue2NvbG9yOiNmYmJmMjR9XG4ubHZsLWluZm97Y29sb3I6IzYwYTVmYX1cbi5sdmwtZGVidWd7Y29sb3I6IzljYTNhZn1cbi50c3tjb2xvcjojNjQ3NDhifVxuLmNhdHtjb2xvcjojYTc4YmZhfVxuLnNyY3tjb2xvcjojMmRkNGJmfVxuLmNpZHtjb2xvcjojZjQ3MmI2O2ZvbnQtc2l6ZToxMXB4O2Rpc3BsYXk6aW5saW5lLWJsb2NrO3dpZHRoOjcycHg7dGV4dC1hbGlnbjpyaWdodDttYXJnaW4tcmlnaHQ6NHB4fVxuI3NlYXJjaHttYXJnaW4tbGVmdDo0cHh9XG48L3N0eWxlPlxuPC9oZWFkPlxuPGJvZHk+XG48ZGl2IGlkPVwiY29udHJvbHNcIj5cbiAgPGxhYmVsPkNhdGVnb3J5XG4gICAgPHNlbGVjdCBpZD1cImZDYXRlZ29yeVwiPlxuICAgICAgPG9wdGlvbiB2YWx1ZT1cIlwiPmFsbDwvb3B0aW9uPlxuICAgICAgPG9wdGlvbiB2YWx1ZT1cImFwcGxpY2F0aW9uXCI+YXBwbGljYXRpb248L29wdGlvbj5cbiAgICAgIDxvcHRpb24gdmFsdWU9XCJzZWN1cml0eVwiPnNlY3VyaXR5PC9vcHRpb24+XG4gICAgICA8b3B0aW9uIHZhbHVlPVwicGVyZm9ybWFuY2VcIj5wZXJmb3JtYW5jZTwvb3B0aW9uPlxuICAgICAgPG9wdGlvbiB2YWx1ZT1cInRlbGVtZXRyeVwiPnRlbGVtZXRyeTwvb3B0aW9uPlxuICAgIDwvc2VsZWN0PlxuICA8L2xhYmVsPlxuICA8bGFiZWw+TGV2ZWxcbiAgICA8c2VsZWN0IGlkPVwiZkxldmVsXCI+XG4gICAgICA8b3B0aW9uIHZhbHVlPVwiXCI+YWxsPC9vcHRpb24+XG4gICAgICA8b3B0aW9uIHZhbHVlPVwiZGVidWdcIj5kZWJ1Zzwvb3B0aW9uPlxuICAgICAgPG9wdGlvbiB2YWx1ZT1cImluZm9cIj5pbmZvPC9vcHRpb24+XG4gICAgICA8b3B0aW9uIHZhbHVlPVwid2FyblwiPndhcm48L29wdGlvbj5cbiAgICAgIDxvcHRpb24gdmFsdWU9XCJlcnJvclwiPmVycm9yPC9vcHRpb24+XG4gICAgPC9zZWxlY3Q+XG4gIDwvbGFiZWw+XG4gIDxsYWJlbD5Tb3VyY2UgPGlucHV0IGlkPVwiZlNvdXJjZVwiIHBsYWNlaG9sZGVyPVwic3Vic3RyaW5nXCIgc2l6ZT1cIjE0XCI+PC9sYWJlbD5cbiAgPGxhYmVsPlJlcXVlc3RJZCA8aW5wdXQgaWQ9XCJmQ29ycmVsYXRpb25JZFwiIHBsYWNlaG9sZGVyPVwiY29ycmVsYXRpb25JZFwiIHNpemU9XCIyMFwiPjwvbGFiZWw+XG4gIDxsYWJlbD5TZWFyY2ggPGlucHV0IGlkPVwic2VhcmNoXCIgcGxhY2Vob2xkZXI9XCJtZXNzYWdlIGZpbHRlclwiIHNpemU9XCIxOFwiPjwvbGFiZWw+XG4gIDxidXR0b24gaWQ9XCJidG5QYXVzZVwiPlBhdXNlPC9idXR0b24+XG4gIDxidXR0b24gaWQ9XCJidG5DbGVhclwiPkNsZWFyPC9idXR0b24+XG4gIDxzcGFuIGlkPVwic3RhdHVzXCIgY2xhc3M9XCJkaXNjb25uZWN0ZWRcIj5kaXNjb25uZWN0ZWQ8L3NwYW4+XG48L2Rpdj5cbjxkaXYgaWQ9XCJsb2dcIj48L2Rpdj5cbjxzY3JpcHQ+XG4oZnVuY3Rpb24oKXtcbiAgdmFyIEJBU0UgPSAnaHR0cDovLzEyNy4wLjAuMToke3BvcnR9JztcbiAgdmFyIE1BWF9FTlRSSUVTID0gMTAwMDtcbiAgdmFyIGxvZyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2cnKTtcbiAgdmFyIHN0YXR1cyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzdGF0dXMnKTtcbiAgdmFyIGZDYXRlZ29yeSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdmQ2F0ZWdvcnknKTtcbiAgdmFyIGZMZXZlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdmTGV2ZWwnKTtcbiAgdmFyIGZTb3VyY2UgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZlNvdXJjZScpO1xuICB2YXIgZkNvcnJlbGF0aW9uSWQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZkNvcnJlbGF0aW9uSWQnKTtcbiAgdmFyIHNlYXJjaEJveCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzZWFyY2gnKTtcbiAgdmFyIGJ0blBhdXNlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2J0blBhdXNlJyk7XG4gIHZhciBidG5DbGVhciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdidG5DbGVhcicpO1xuICB2YXIgZXMgPSBudWxsO1xuICB2YXIgcGF1c2VkID0gZmFsc2U7XG4gIHZhciBidWZmZXIgPSBbXTtcblxuICBmdW5jdGlvbiBlc2NIdG1sKHMpe1xuICAgIHZhciBkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgZC5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShzKSk7XG4gICAgcmV0dXJuIGQuaW5uZXJIVE1MO1xuICB9XG5cbiAgZnVuY3Rpb24gc2V0U3RhdHVzKHMsIGNscyl7XG4gICAgc3RhdHVzLnRleHRDb250ZW50ID0gcztcbiAgICBzdGF0dXMuY2xhc3NOYW1lID0gY2xzO1xuICB9XG5cblxuXG4gIHZhciBMRVZFTF9PUkRFUiA9IHtkZWJ1ZzowLCBpbmZvOjEsIHdhcm46MiwgZXJyb3I6M307XG5cbiAgZnVuY3Rpb24gbWF0Y2hlc0ZpbHRlcnMoZW50cnkpe1xuICAgIHZhciBjYXQgPSBmQ2F0ZWdvcnkudmFsdWU7XG4gICAgaWYoY2F0ICYmIGVudHJ5LmNhdGVnb3J5ICE9PSBjYXQpIHJldHVybiBmYWxzZTtcbiAgICB2YXIgbHZsID0gZkxldmVsLnZhbHVlO1xuICAgIGlmKGx2bCAmJiAoTEVWRUxfT1JERVJbZW50cnkubGV2ZWxdfHwwKSA8IChMRVZFTF9PUkRFUltsdmxdfHwwKSkgcmV0dXJuIGZhbHNlO1xuICAgIHZhciBzcmMgPSBmU291cmNlLnZhbHVlLnRvTG93ZXJDYXNlKCk7XG4gICAgaWYoc3JjICYmICghZW50cnkuc291cmNlIHx8IGVudHJ5LnNvdXJjZS50b0xvd2VyQ2FzZSgpLmluZGV4T2Yoc3JjKSA9PT0gLTEpKSByZXR1cm4gZmFsc2U7XG4gICAgdmFyIGNpZCA9IGZDb3JyZWxhdGlvbklkLnZhbHVlO1xuICAgIGlmKGNpZCAmJiBlbnRyeS5jb3JyZWxhdGlvbklkICE9PSBjaWQpIHJldHVybiBmYWxzZTtcbiAgICB2YXIgbmVlZGxlID0gc2VhcmNoQm94LnZhbHVlLnRvTG93ZXJDYXNlKCk7XG4gICAgaWYobmVlZGxlICYmICghZW50cnkubWVzc2FnZSB8fCBlbnRyeS5tZXNzYWdlLnRvTG93ZXJDYXNlKCkuaW5kZXhPZihuZWVkbGUpID09PSAtMSkpIHJldHVybiBmYWxzZTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlZmlsdGVyKCl7XG4gICAgdmFyIGVscyA9IGxvZy5jaGlsZHJlbjtcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgZWxzLmxlbmd0aDsgaSsrKXtcbiAgICAgIHZhciBkYXRhID0gZWxzW2ldLl9lbnRyeURhdGE7XG4gICAgICBpZihkYXRhKXtcbiAgICAgICAgZWxzW2ldLnN0eWxlLmRpc3BsYXkgPSBtYXRjaGVzRmlsdGVycyhkYXRhKSA/ICcnIDogJ25vbmUnO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGFkZEVudHJ5KGVudHJ5KXtcbiAgICBpZighbWF0Y2hlc0ZpbHRlcnMoZW50cnkpKSB2YXIgaGlkZGVuID0gdHJ1ZTtcblxuICAgIHZhciBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgIGVsLmNsYXNzTmFtZSA9ICdlbnRyeSBsdmwtJyArIGVudHJ5LmxldmVsO1xuICAgIGVsLl9lbnRyeURhdGEgPSBlbnRyeTtcbiAgICBpZihoaWRkZW4pIGVsLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgdmFyIHRzID0gZW50cnkudGltZXN0YW1wID8gZW50cnkudGltZXN0YW1wLnNsaWNlKDExLCAyMykgOiAnJztcbiAgICB2YXIgY2lkID0gZW50cnkuY29ycmVsYXRpb25JZCA/IGVudHJ5LmNvcnJlbGF0aW9uSWQuc2xpY2UoLTgpIDogJyc7XG4gICAgZWwuaW5uZXJIVE1MID0gJzxzcGFuIGNsYXNzPVwidHNcIj4nICsgZXNjSHRtbCh0cykgKyAnPC9zcGFuPiAnXG4gICAgICArICc8c3BhbiBjbGFzcz1cImNpZFwiPicgKyBlc2NIdG1sKGNpZCkgKyAnPC9zcGFuPiAnXG4gICAgICArICc8c3BhbiBjbGFzcz1cImNhdFwiPlsnICsgZXNjSHRtbChlbnRyeS5jYXRlZ29yeSkgKyAnXTwvc3Bhbj4gJ1xuICAgICAgKyAnPHNwYW4gY2xhc3M9XCJzcmNcIj4nICsgZXNjSHRtbChlbnRyeS5zb3VyY2UpICsgJzwvc3Bhbj4gJ1xuICAgICAgKyBlc2NIdG1sKGVudHJ5Lm1lc3NhZ2UpO1xuICAgIGxvZy5hcHBlbmRDaGlsZChlbCk7XG5cbiAgICB3aGlsZShsb2cuY2hpbGRyZW4ubGVuZ3RoID4gTUFYX0VOVFJJRVMpe1xuICAgICAgbG9nLnJlbW92ZUNoaWxkKGxvZy5maXJzdENoaWxkKTtcbiAgICB9XG4gICAgaWYoIWhpZGRlbikgbG9nLnNjcm9sbFRvcCA9IGxvZy5zY3JvbGxIZWlnaHQ7XG4gIH1cblxuICBmdW5jdGlvbiBjb25uZWN0KCl7XG4gICAgaWYoZXMpeyBlcy5jbG9zZSgpOyB9XG4gICAgZXMgPSBuZXcgRXZlbnRTb3VyY2UoQkFTRSArICcvbG9ncy9zdHJlYW0nKTtcbiAgICBlcy5vbm9wZW4gPSBmdW5jdGlvbigpeyBzZXRTdGF0dXMoJ2Nvbm5lY3RlZCcsICdjb25uZWN0ZWQnKTsgfTtcbiAgICBlcy5vbm1lc3NhZ2UgPSBmdW5jdGlvbihlKXtcbiAgICAgIHRyeXtcbiAgICAgICAgdmFyIGVudHJ5ID0gSlNPTi5wYXJzZShlLmRhdGEpO1xuICAgICAgICBpZihwYXVzZWQpeyBidWZmZXIucHVzaChlbnRyeSk7IH1cbiAgICAgICAgZWxzZSB7IGFkZEVudHJ5KGVudHJ5KTsgfVxuICAgICAgfWNhdGNoKGVycil7fVxuICAgIH07XG4gICAgZXMub25lcnJvciA9IGZ1bmN0aW9uKCl7IHNldFN0YXR1cygnZGlzY29ubmVjdGVkJywgJ2Rpc2Nvbm5lY3RlZCcpOyB9O1xuICB9XG5cbiAgdmFyIGZpbHRlclRpbWVyID0gbnVsbDtcbiAgZnVuY3Rpb24gb25GaWx0ZXJDaGFuZ2UoKXtcbiAgICBjbGVhclRpbWVvdXQoZmlsdGVyVGltZXIpO1xuICAgIGZpbHRlclRpbWVyID0gc2V0VGltZW91dChyZWZpbHRlciwgNTApO1xuICB9XG5cbiAgZkNhdGVnb3J5LmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIG9uRmlsdGVyQ2hhbmdlKTtcbiAgZkxldmVsLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIG9uRmlsdGVyQ2hhbmdlKTtcbiAgZlNvdXJjZS5hZGRFdmVudExpc3RlbmVyKCdpbnB1dCcsIGZ1bmN0aW9uKCl7XG4gICAgY2xlYXJUaW1lb3V0KGZpbHRlclRpbWVyKTtcbiAgICBmaWx0ZXJUaW1lciA9IHNldFRpbWVvdXQocmVmaWx0ZXIsIDQwMCk7XG4gIH0pO1xuICBmQ29ycmVsYXRpb25JZC5hZGRFdmVudExpc3RlbmVyKCdpbnB1dCcsIGZ1bmN0aW9uKCl7XG4gICAgY2xlYXJUaW1lb3V0KGZpbHRlclRpbWVyKTtcbiAgICBmaWx0ZXJUaW1lciA9IHNldFRpbWVvdXQocmVmaWx0ZXIsIDQwMCk7XG4gIH0pO1xuICBzZWFyY2hCb3guYWRkRXZlbnRMaXN0ZW5lcignaW5wdXQnLCBmdW5jdGlvbigpe1xuICAgIGNsZWFyVGltZW91dChmaWx0ZXJUaW1lcik7XG4gICAgZmlsdGVyVGltZXIgPSBzZXRUaW1lb3V0KHJlZmlsdGVyLCA0MDApO1xuICB9KTtcblxuICBidG5QYXVzZS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKCl7XG4gICAgcGF1c2VkID0gIXBhdXNlZDtcbiAgICBidG5QYXVzZS50ZXh0Q29udGVudCA9IHBhdXNlZCA/ICdSZXN1bWUnIDogJ1BhdXNlJztcbiAgICBpZihwYXVzZWQpe1xuICAgICAgc2V0U3RhdHVzKCdwYXVzZWQnLCAncGF1c2VkJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNldFN0YXR1cygnY29ubmVjdGVkJywgJ2Nvbm5lY3RlZCcpO1xuICAgICAgYnVmZmVyLmZvckVhY2goYWRkRW50cnkpO1xuICAgICAgYnVmZmVyID0gW107XG4gICAgfVxuICB9KTtcblxuICBidG5DbGVhci5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKCl7XG4gICAgbG9nLmlubmVySFRNTCA9ICcnO1xuICB9KTtcblxuICBjb25uZWN0KCk7XG59KSgpO1xuPC9zY3JpcHQ+XG48L2JvZHk+XG48L2h0bWw+YDtcbn1cbiJdfQ==