@local-labs-jpollock/local-cli 0.0.1
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/addon-dist/bin/mcp-stdio.js +2808 -0
- package/addon-dist/lib/common/constants.d.ts +22 -0
- package/addon-dist/lib/common/constants.js +26 -0
- package/addon-dist/lib/common/theme.d.ts +68 -0
- package/addon-dist/lib/common/theme.js +126 -0
- package/addon-dist/lib/common/types.d.ts +298 -0
- package/addon-dist/lib/common/types.js +6 -0
- package/addon-dist/lib/main/config/ConnectionInfo.d.ts +25 -0
- package/addon-dist/lib/main/config/ConnectionInfo.js +82 -0
- package/addon-dist/lib/main/index.d.ts +12 -0
- package/addon-dist/lib/main/index.js +3322 -0
- package/addon-dist/lib/main/mcp/McpAuth.d.ts +37 -0
- package/addon-dist/lib/main/mcp/McpAuth.js +87 -0
- package/addon-dist/lib/main/mcp/McpServer.d.ts +67 -0
- package/addon-dist/lib/main/mcp/McpServer.js +343 -0
- package/addon-dist/lib/main/mcp/tools/changePhpVersion.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/changePhpVersion.js +81 -0
- package/addon-dist/lib/main/mcp/tools/cloneSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/cloneSite.js +66 -0
- package/addon-dist/lib/main/mcp/tools/createSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/createSite.js +137 -0
- package/addon-dist/lib/main/mcp/tools/deleteSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/deleteSite.js +72 -0
- package/addon-dist/lib/main/mcp/tools/exportDatabase.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/exportDatabase.js +72 -0
- package/addon-dist/lib/main/mcp/tools/exportSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/exportSite.js +103 -0
- package/addon-dist/lib/main/mcp/tools/getLocalInfo.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/getLocalInfo.js +72 -0
- package/addon-dist/lib/main/mcp/tools/getSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/getSite.js +68 -0
- package/addon-dist/lib/main/mcp/tools/getSiteLogs.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/getSiteLogs.js +149 -0
- package/addon-dist/lib/main/mcp/tools/helpers.d.ts +59 -0
- package/addon-dist/lib/main/mcp/tools/helpers.js +179 -0
- package/addon-dist/lib/main/mcp/tools/importDatabase.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/importDatabase.js +109 -0
- package/addon-dist/lib/main/mcp/tools/importSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/importSite.js +149 -0
- package/addon-dist/lib/main/mcp/tools/index.d.ts +26 -0
- package/addon-dist/lib/main/mcp/tools/index.js +117 -0
- package/addon-dist/lib/main/mcp/tools/listBlueprints.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/listBlueprints.js +54 -0
- package/addon-dist/lib/main/mcp/tools/listServices.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/listServices.js +112 -0
- package/addon-dist/lib/main/mcp/tools/listSites.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/listSites.js +62 -0
- package/addon-dist/lib/main/mcp/tools/openAdminer.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/openAdminer.js +59 -0
- package/addon-dist/lib/main/mcp/tools/openSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/openSite.js +62 -0
- package/addon-dist/lib/main/mcp/tools/renameSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/renameSite.js +70 -0
- package/addon-dist/lib/main/mcp/tools/restartSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/restartSite.js +56 -0
- package/addon-dist/lib/main/mcp/tools/saveBlueprint.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/saveBlueprint.js +89 -0
- package/addon-dist/lib/main/mcp/tools/startSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/startSite.js +54 -0
- package/addon-dist/lib/main/mcp/tools/stopSite.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/stopSite.js +54 -0
- package/addon-dist/lib/main/mcp/tools/toggleXdebug.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/toggleXdebug.js +69 -0
- package/addon-dist/lib/main/mcp/tools/trustSsl.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/trustSsl.js +59 -0
- package/addon-dist/lib/main/mcp/tools/wpCli.d.ts +7 -0
- package/addon-dist/lib/main/mcp/tools/wpCli.js +110 -0
- package/addon-dist/lib/main.d.ts +1 -0
- package/addon-dist/lib/main.js +10 -0
- package/addon-dist/lib/renderer/index.d.ts +7 -0
- package/addon-dist/lib/renderer/index.js +479 -0
- package/addon-dist/package.json +73 -0
- package/bin/lwp.js +10 -0
- package/lib/bootstrap/index.d.ts +98 -0
- package/lib/bootstrap/index.js +493 -0
- package/lib/bootstrap/paths.d.ts +28 -0
- package/lib/bootstrap/paths.js +96 -0
- package/lib/client/GraphQLClient.d.ts +38 -0
- package/lib/client/GraphQLClient.js +71 -0
- package/lib/client/index.d.ts +4 -0
- package/lib/client/index.js +10 -0
- package/lib/formatters/index.d.ts +75 -0
- package/lib/formatters/index.js +139 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +1173 -0
- package/package.json +72 -0
|
@@ -0,0 +1,3322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI Bridge Addon - Main Process Entry Point
|
|
4
|
+
*
|
|
5
|
+
* This addon extends Local's capabilities:
|
|
6
|
+
* - GraphQL mutations for deleteSite, wpCli (for local-cli)
|
|
7
|
+
* - MCP Server for AI tool integration (Claude Code, ChatGPT, etc.)
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
43
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.default = default_1;
|
|
47
|
+
const LocalMain = __importStar(require("@getflywheel/local/main"));
|
|
48
|
+
const electron_1 = require("electron");
|
|
49
|
+
const graphql_tag_1 = __importDefault(require("graphql-tag"));
|
|
50
|
+
const McpServer_1 = require("./mcp/McpServer");
|
|
51
|
+
const constants_1 = require("../common/constants");
|
|
52
|
+
const ADDON_NAME = 'MCP Server';
|
|
53
|
+
let mcpServer = null;
|
|
54
|
+
/**
|
|
55
|
+
* GraphQL type definitions for CLI Bridge
|
|
56
|
+
*/
|
|
57
|
+
const typeDefs = (0, graphql_tag_1.default) `
|
|
58
|
+
input DeleteSiteInput {
|
|
59
|
+
"The site ID to delete"
|
|
60
|
+
id: ID!
|
|
61
|
+
"Whether to move site files to trash (true) or just remove from Local (false)"
|
|
62
|
+
trashFiles: Boolean = true
|
|
63
|
+
"Whether to update the hosts file"
|
|
64
|
+
updateHosts: Boolean = true
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type DeleteSiteResult {
|
|
68
|
+
"Whether the deletion was successful"
|
|
69
|
+
success: Boolean!
|
|
70
|
+
"Error message if deletion failed"
|
|
71
|
+
error: String
|
|
72
|
+
"The ID of the deleted site"
|
|
73
|
+
siteId: ID
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
input WpCliInput {
|
|
77
|
+
"The site ID to run WP-CLI against"
|
|
78
|
+
siteId: ID!
|
|
79
|
+
"WP-CLI command and arguments (e.g., ['plugin', 'list', '--format=json'])"
|
|
80
|
+
args: [String!]!
|
|
81
|
+
"Skip loading plugins (default: true)"
|
|
82
|
+
skipPlugins: Boolean = true
|
|
83
|
+
"Skip loading themes (default: true)"
|
|
84
|
+
skipThemes: Boolean = true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type WpCliResult {
|
|
88
|
+
"Whether the command executed successfully"
|
|
89
|
+
success: Boolean!
|
|
90
|
+
"Command output (stdout)"
|
|
91
|
+
output: String
|
|
92
|
+
"Error message if command failed"
|
|
93
|
+
error: String
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
input CreateSiteInput {
|
|
97
|
+
"Site name (required)"
|
|
98
|
+
name: String!
|
|
99
|
+
"PHP version (e.g., '8.2.10'). Uses Local default if not specified."
|
|
100
|
+
phpVersion: String
|
|
101
|
+
"Web server type"
|
|
102
|
+
webServer: String
|
|
103
|
+
"Database type"
|
|
104
|
+
database: String
|
|
105
|
+
"WordPress admin username (default: admin)"
|
|
106
|
+
wpAdminUsername: String
|
|
107
|
+
"WordPress admin password (default: password)"
|
|
108
|
+
wpAdminPassword: String
|
|
109
|
+
"WordPress admin email (default: admin@local.test)"
|
|
110
|
+
wpAdminEmail: String
|
|
111
|
+
"Blueprint name to create site from. Use list_blueprints to see available blueprints."
|
|
112
|
+
blueprint: String
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
type CreateSiteResult {
|
|
116
|
+
"Whether site creation was initiated successfully"
|
|
117
|
+
success: Boolean!
|
|
118
|
+
"Error message if creation failed"
|
|
119
|
+
error: String
|
|
120
|
+
"The created site ID"
|
|
121
|
+
siteId: ID
|
|
122
|
+
"The site name"
|
|
123
|
+
siteName: String
|
|
124
|
+
"The site domain"
|
|
125
|
+
siteDomain: String
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
input OpenSiteInput {
|
|
129
|
+
"The site ID to open"
|
|
130
|
+
siteId: ID!
|
|
131
|
+
"Path to open (default: /, use /wp-admin for admin)"
|
|
132
|
+
path: String = "/"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
type OpenSiteResult {
|
|
136
|
+
"Whether the site was opened successfully"
|
|
137
|
+
success: Boolean!
|
|
138
|
+
"Error message if failed"
|
|
139
|
+
error: String
|
|
140
|
+
"The URL that was opened"
|
|
141
|
+
url: String
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
input CloneSiteInput {
|
|
145
|
+
"The site ID to clone"
|
|
146
|
+
siteId: ID!
|
|
147
|
+
"Name for the cloned site"
|
|
148
|
+
newName: String!
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
type CloneSiteResult {
|
|
152
|
+
"Whether cloning was successful"
|
|
153
|
+
success: Boolean!
|
|
154
|
+
"Error message if failed"
|
|
155
|
+
error: String
|
|
156
|
+
"The new site ID"
|
|
157
|
+
newSiteId: ID
|
|
158
|
+
"The new site name"
|
|
159
|
+
newSiteName: String
|
|
160
|
+
"The new site domain"
|
|
161
|
+
newSiteDomain: String
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
input ExportSiteInput {
|
|
165
|
+
"The site ID to export"
|
|
166
|
+
siteId: ID!
|
|
167
|
+
"Output directory path (default: ~/Downloads)"
|
|
168
|
+
outputPath: String
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
type ExportSiteResult {
|
|
172
|
+
"Whether export was successful"
|
|
173
|
+
success: Boolean!
|
|
174
|
+
"Error message if failed"
|
|
175
|
+
error: String
|
|
176
|
+
"Path to the exported zip file"
|
|
177
|
+
exportPath: String
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
type Blueprint {
|
|
181
|
+
"Blueprint name"
|
|
182
|
+
name: String!
|
|
183
|
+
"Last modified date"
|
|
184
|
+
lastModified: String
|
|
185
|
+
"PHP version"
|
|
186
|
+
phpVersion: String
|
|
187
|
+
"Web server type"
|
|
188
|
+
webServer: String
|
|
189
|
+
"Database type"
|
|
190
|
+
database: String
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
type BlueprintsResult {
|
|
194
|
+
"Whether query was successful"
|
|
195
|
+
success: Boolean!
|
|
196
|
+
"Error message if failed"
|
|
197
|
+
error: String
|
|
198
|
+
"List of blueprints"
|
|
199
|
+
blueprints: [Blueprint!]
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
input SaveBlueprintInput {
|
|
203
|
+
"The site ID to save as blueprint"
|
|
204
|
+
siteId: ID!
|
|
205
|
+
"Name for the blueprint"
|
|
206
|
+
name: String!
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
type SaveBlueprintResult {
|
|
210
|
+
"Whether save was successful"
|
|
211
|
+
success: Boolean!
|
|
212
|
+
"Error message if failed"
|
|
213
|
+
error: String
|
|
214
|
+
"The blueprint name"
|
|
215
|
+
blueprintName: String
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
# Phase 8: WordPress Development Tools
|
|
219
|
+
input ExportDatabaseInput {
|
|
220
|
+
"The site ID"
|
|
221
|
+
siteId: ID!
|
|
222
|
+
"Output file path (optional, defaults to ~/Downloads/<site-name>.sql)"
|
|
223
|
+
outputPath: String
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
type ExportDatabaseResult {
|
|
227
|
+
"Whether export was successful"
|
|
228
|
+
success: Boolean!
|
|
229
|
+
"Error message if failed"
|
|
230
|
+
error: String
|
|
231
|
+
"Path to the exported SQL file"
|
|
232
|
+
outputPath: String
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
input ImportDatabaseInput {
|
|
236
|
+
"The site ID"
|
|
237
|
+
siteId: ID!
|
|
238
|
+
"Path to the SQL file to import"
|
|
239
|
+
sqlPath: String!
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
type ImportDatabaseResult {
|
|
243
|
+
"Whether import was successful"
|
|
244
|
+
success: Boolean!
|
|
245
|
+
"Error message if failed"
|
|
246
|
+
error: String
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
input OpenAdminerInput {
|
|
250
|
+
"The site ID"
|
|
251
|
+
siteId: ID!
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
type OpenAdminerResult {
|
|
255
|
+
"Whether opening was successful"
|
|
256
|
+
success: Boolean!
|
|
257
|
+
"Error message if failed"
|
|
258
|
+
error: String
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
input TrustSslInput {
|
|
262
|
+
"The site ID"
|
|
263
|
+
siteId: ID!
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
type TrustSslResult {
|
|
267
|
+
"Whether trust was successful"
|
|
268
|
+
success: Boolean!
|
|
269
|
+
"Error message if failed"
|
|
270
|
+
error: String
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
input McpRenameSiteInput {
|
|
274
|
+
"The site ID"
|
|
275
|
+
siteId: ID!
|
|
276
|
+
"New name for the site"
|
|
277
|
+
newName: String!
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
type McpRenameSiteResult {
|
|
281
|
+
"Whether rename was successful"
|
|
282
|
+
success: Boolean!
|
|
283
|
+
"Error message if failed"
|
|
284
|
+
error: String
|
|
285
|
+
"The new name"
|
|
286
|
+
newName: String
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
input ChangePhpVersionInput {
|
|
290
|
+
"The site ID"
|
|
291
|
+
siteId: ID!
|
|
292
|
+
"Target PHP version"
|
|
293
|
+
phpVersion: String!
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
type ChangePhpVersionResult {
|
|
297
|
+
"Whether change was successful"
|
|
298
|
+
success: Boolean!
|
|
299
|
+
"Error message if failed"
|
|
300
|
+
error: String
|
|
301
|
+
"The new PHP version"
|
|
302
|
+
phpVersion: String
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
input ImportSiteInput {
|
|
306
|
+
"Path to the zip file to import"
|
|
307
|
+
zipPath: String!
|
|
308
|
+
"Name for the imported site (optional)"
|
|
309
|
+
siteName: String
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
type ImportSiteResult {
|
|
313
|
+
"Whether import was successful"
|
|
314
|
+
success: Boolean!
|
|
315
|
+
"Error message if failed"
|
|
316
|
+
error: String
|
|
317
|
+
"The imported site ID"
|
|
318
|
+
siteId: ID
|
|
319
|
+
"The imported site name"
|
|
320
|
+
siteName: String
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
# Phase 9: Site Configuration & Dev Tools
|
|
324
|
+
input ToggleXdebugInput {
|
|
325
|
+
"The site ID"
|
|
326
|
+
siteId: ID!
|
|
327
|
+
"Whether to enable or disable Xdebug"
|
|
328
|
+
enabled: Boolean!
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
type ToggleXdebugResult {
|
|
332
|
+
"Whether toggle was successful"
|
|
333
|
+
success: Boolean!
|
|
334
|
+
"Error message if failed"
|
|
335
|
+
error: String
|
|
336
|
+
"Current Xdebug state"
|
|
337
|
+
enabled: Boolean
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
input GetSiteLogsInput {
|
|
341
|
+
"The site ID"
|
|
342
|
+
siteId: ID!
|
|
343
|
+
"Type of logs to retrieve (php, nginx, mysql, all)"
|
|
344
|
+
logType: String = "php"
|
|
345
|
+
"Number of lines to return"
|
|
346
|
+
lines: Int = 100
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
type LogEntry {
|
|
350
|
+
"Log type"
|
|
351
|
+
type: String!
|
|
352
|
+
"Log content"
|
|
353
|
+
content: String!
|
|
354
|
+
"Log file path"
|
|
355
|
+
path: String!
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
type GetSiteLogsResult {
|
|
359
|
+
"Whether retrieval was successful"
|
|
360
|
+
success: Boolean!
|
|
361
|
+
"Error message if failed"
|
|
362
|
+
error: String
|
|
363
|
+
"Log entries"
|
|
364
|
+
logs: [LogEntry!]
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
type ServiceInfo {
|
|
368
|
+
"Service role (php, database, webserver)"
|
|
369
|
+
role: String!
|
|
370
|
+
"Service name"
|
|
371
|
+
name: String!
|
|
372
|
+
"Service version"
|
|
373
|
+
version: String!
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
type ListServicesResult {
|
|
377
|
+
"Whether listing was successful"
|
|
378
|
+
success: Boolean!
|
|
379
|
+
"Error message if failed"
|
|
380
|
+
error: String
|
|
381
|
+
"Available services"
|
|
382
|
+
services: [ServiceInfo!]
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
extend type Mutation {
|
|
386
|
+
"Create a new WordPress site with full WordPress installation"
|
|
387
|
+
createSite(input: CreateSiteInput!): CreateSiteResult!
|
|
388
|
+
|
|
389
|
+
"Delete a site from Local"
|
|
390
|
+
deleteSite(input: DeleteSiteInput!): DeleteSiteResult!
|
|
391
|
+
|
|
392
|
+
"Delete multiple sites from Local"
|
|
393
|
+
deleteSites(ids: [ID!]!, trashFiles: Boolean = true): DeleteSiteResult!
|
|
394
|
+
|
|
395
|
+
"Run a WP-CLI command against a site"
|
|
396
|
+
wpCli(input: WpCliInput!): WpCliResult!
|
|
397
|
+
|
|
398
|
+
"Open a site in the default browser"
|
|
399
|
+
openSite(input: OpenSiteInput!): OpenSiteResult!
|
|
400
|
+
|
|
401
|
+
"Clone an existing site"
|
|
402
|
+
cloneSite(input: CloneSiteInput!): CloneSiteResult!
|
|
403
|
+
|
|
404
|
+
"Export a site to a zip file"
|
|
405
|
+
exportSite(input: ExportSiteInput!): ExportSiteResult!
|
|
406
|
+
|
|
407
|
+
"Save a site as a blueprint"
|
|
408
|
+
saveBlueprint(input: SaveBlueprintInput!): SaveBlueprintResult!
|
|
409
|
+
|
|
410
|
+
# Phase 8: WordPress Development Tools
|
|
411
|
+
"Export site database to SQL file"
|
|
412
|
+
exportDatabase(input: ExportDatabaseInput!): ExportDatabaseResult!
|
|
413
|
+
|
|
414
|
+
"Import SQL file into site database"
|
|
415
|
+
importDatabase(input: ImportDatabaseInput!): ImportDatabaseResult!
|
|
416
|
+
|
|
417
|
+
"Open Adminer database management UI"
|
|
418
|
+
openAdminer(input: OpenAdminerInput!): OpenAdminerResult!
|
|
419
|
+
|
|
420
|
+
"Trust site SSL certificate"
|
|
421
|
+
trustSsl(input: TrustSslInput!): TrustSslResult!
|
|
422
|
+
|
|
423
|
+
"Rename a site (MCP version)"
|
|
424
|
+
mcpRenameSite(input: McpRenameSiteInput!): McpRenameSiteResult!
|
|
425
|
+
|
|
426
|
+
"Change site PHP version"
|
|
427
|
+
changePhpVersion(input: ChangePhpVersionInput!): ChangePhpVersionResult!
|
|
428
|
+
|
|
429
|
+
"Import site from zip file"
|
|
430
|
+
importSite(input: ImportSiteInput!): ImportSiteResult!
|
|
431
|
+
|
|
432
|
+
# Phase 9: Site Configuration & Dev Tools
|
|
433
|
+
"Toggle Xdebug for a site"
|
|
434
|
+
toggleXdebug(input: ToggleXdebugInput!): ToggleXdebugResult!
|
|
435
|
+
|
|
436
|
+
"Get site log files"
|
|
437
|
+
getSiteLogs(input: GetSiteLogsInput!): GetSiteLogsResult!
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
extend type Query {
|
|
441
|
+
"Run a WP-CLI command against a site (read-only operations)"
|
|
442
|
+
wpCliQuery(input: WpCliInput!): WpCliResult!
|
|
443
|
+
|
|
444
|
+
"List all available blueprints"
|
|
445
|
+
blueprints: BlueprintsResult!
|
|
446
|
+
|
|
447
|
+
"List available service versions"
|
|
448
|
+
listServices(type: String): ListServicesResult!
|
|
449
|
+
|
|
450
|
+
# Phase 11: WP Engine Connect
|
|
451
|
+
"Check WP Engine authentication status"
|
|
452
|
+
wpeStatus: WpeAuthStatus!
|
|
453
|
+
|
|
454
|
+
"List all sites from WP Engine account"
|
|
455
|
+
listWpeSites(accountId: String): ListWpeSitesResult!
|
|
456
|
+
|
|
457
|
+
# Phase 11b: Site Linking
|
|
458
|
+
"Get WP Engine connection details for a local site"
|
|
459
|
+
getWpeLink(siteId: ID!): GetWpeLinkResult!
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
# Phase 11: WP Engine Connect Types
|
|
463
|
+
type WpeAuthStatus {
|
|
464
|
+
"Whether authenticated with WP Engine"
|
|
465
|
+
authenticated: Boolean!
|
|
466
|
+
"User email if authenticated"
|
|
467
|
+
email: String
|
|
468
|
+
"Account ID if authenticated"
|
|
469
|
+
accountId: String
|
|
470
|
+
"Account name if authenticated"
|
|
471
|
+
accountName: String
|
|
472
|
+
"Token expiry time"
|
|
473
|
+
tokenExpiry: String
|
|
474
|
+
"Error message if status check failed"
|
|
475
|
+
error: String
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
type WpeAuthResult {
|
|
479
|
+
"Whether authentication was successful"
|
|
480
|
+
success: Boolean!
|
|
481
|
+
"User email if successful"
|
|
482
|
+
email: String
|
|
483
|
+
"Message about the authentication result"
|
|
484
|
+
message: String
|
|
485
|
+
"Error message if failed"
|
|
486
|
+
error: String
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
type WpeLogoutResult {
|
|
490
|
+
"Whether logout was successful"
|
|
491
|
+
success: Boolean!
|
|
492
|
+
"Message about the logout result"
|
|
493
|
+
message: String
|
|
494
|
+
"Error message if failed"
|
|
495
|
+
error: String
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
type WpeSite {
|
|
499
|
+
"Install ID"
|
|
500
|
+
id: String!
|
|
501
|
+
"Install name"
|
|
502
|
+
name: String!
|
|
503
|
+
"Environment (production, staging, development)"
|
|
504
|
+
environment: String!
|
|
505
|
+
"PHP version"
|
|
506
|
+
phpVersion: String
|
|
507
|
+
"Primary domain"
|
|
508
|
+
primaryDomain: String
|
|
509
|
+
"Account ID"
|
|
510
|
+
accountId: String
|
|
511
|
+
"Account name"
|
|
512
|
+
accountName: String
|
|
513
|
+
"SFTP host"
|
|
514
|
+
sftpHost: String
|
|
515
|
+
"SFTP user"
|
|
516
|
+
sftpUser: String
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
type ListWpeSitesResult {
|
|
520
|
+
"Whether query was successful"
|
|
521
|
+
success: Boolean!
|
|
522
|
+
"Error message if failed"
|
|
523
|
+
error: String
|
|
524
|
+
"List of WP Engine sites"
|
|
525
|
+
sites: [WpeSite!]
|
|
526
|
+
"Total count of sites"
|
|
527
|
+
count: Int
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
# Phase 11b: Site Linking Types
|
|
531
|
+
type WpeConnection {
|
|
532
|
+
"Remote install ID (UUID from WP Engine)"
|
|
533
|
+
remoteInstallId: String!
|
|
534
|
+
"Install name (human-readable, used in portal URLs)"
|
|
535
|
+
installName: String
|
|
536
|
+
"Environment (production, staging, development)"
|
|
537
|
+
environment: String
|
|
538
|
+
"Account ID"
|
|
539
|
+
accountId: String
|
|
540
|
+
"WP Engine portal URL"
|
|
541
|
+
portalUrl: String
|
|
542
|
+
"Primary domain/CNAME"
|
|
543
|
+
primaryDomain: String
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
"Sync capabilities available for WPE-connected sites"
|
|
547
|
+
type WpeSyncCapabilities {
|
|
548
|
+
"Whether user can push to WP Engine"
|
|
549
|
+
canPush: Boolean!
|
|
550
|
+
"Whether user can pull from WP Engine"
|
|
551
|
+
canPull: Boolean!
|
|
552
|
+
"Available sync modes"
|
|
553
|
+
syncModes: [String!]!
|
|
554
|
+
"Whether Magic Sync (select files) is available"
|
|
555
|
+
magicSyncAvailable: Boolean!
|
|
556
|
+
"Whether database sync is available"
|
|
557
|
+
databaseSyncAvailable: Boolean!
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
type GetWpeLinkResult {
|
|
561
|
+
"Whether site is linked to WP Engine"
|
|
562
|
+
linked: Boolean!
|
|
563
|
+
"Site name"
|
|
564
|
+
siteName: String
|
|
565
|
+
"WP Engine connections"
|
|
566
|
+
connections: [WpeConnection!]
|
|
567
|
+
"Number of connections"
|
|
568
|
+
connectionCount: Int
|
|
569
|
+
"Sync capabilities (only present if linked)"
|
|
570
|
+
capabilities: WpeSyncCapabilities
|
|
571
|
+
"Message (for unlinked sites)"
|
|
572
|
+
message: String
|
|
573
|
+
"Error message if failed"
|
|
574
|
+
error: String
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
# Phase 11c: Sync Operations Types
|
|
578
|
+
type SyncHistoryEvent {
|
|
579
|
+
"Remote install name"
|
|
580
|
+
remoteInstallName: String
|
|
581
|
+
"Unix timestamp"
|
|
582
|
+
timestamp: Float!
|
|
583
|
+
"Environment (production, staging, development)"
|
|
584
|
+
environment: String!
|
|
585
|
+
"Sync direction"
|
|
586
|
+
direction: String!
|
|
587
|
+
"Sync status"
|
|
588
|
+
status: String
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
type GetSyncHistoryResult {
|
|
592
|
+
"Whether the query was successful"
|
|
593
|
+
success: Boolean!
|
|
594
|
+
"Site name"
|
|
595
|
+
siteName: String
|
|
596
|
+
"Sync history events"
|
|
597
|
+
events: [SyncHistoryEvent!]
|
|
598
|
+
"Number of events"
|
|
599
|
+
count: Int
|
|
600
|
+
"Error message if failed"
|
|
601
|
+
error: String
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
type SyncResult {
|
|
605
|
+
"Whether the sync was initiated successfully"
|
|
606
|
+
success: Boolean!
|
|
607
|
+
"Status message"
|
|
608
|
+
message: String
|
|
609
|
+
"Error message if failed"
|
|
610
|
+
error: String
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
# File change detection
|
|
614
|
+
type FileChange {
|
|
615
|
+
"File path relative to site root"
|
|
616
|
+
path: String!
|
|
617
|
+
"Change type: create, upload, download, delete, modify"
|
|
618
|
+
instruction: String!
|
|
619
|
+
"File size in bytes"
|
|
620
|
+
size: Int
|
|
621
|
+
"File type: - (file) or d (directory)"
|
|
622
|
+
type: String
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
type GetSiteChangesResult {
|
|
626
|
+
"Whether the query was successful"
|
|
627
|
+
success: Boolean!
|
|
628
|
+
"Site name"
|
|
629
|
+
siteName: String
|
|
630
|
+
"Direction of comparison"
|
|
631
|
+
direction: String
|
|
632
|
+
"Files that would be added/uploaded"
|
|
633
|
+
added: [FileChange!]
|
|
634
|
+
"Files that would be modified"
|
|
635
|
+
modified: [FileChange!]
|
|
636
|
+
"Files that would be deleted"
|
|
637
|
+
deleted: [FileChange!]
|
|
638
|
+
"Total number of changes"
|
|
639
|
+
totalChanges: Int
|
|
640
|
+
"Summary message"
|
|
641
|
+
message: String
|
|
642
|
+
"Error message if failed"
|
|
643
|
+
error: String
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
# Phase 10: Cloud Backup Types
|
|
647
|
+
type BackupProviderStatus {
|
|
648
|
+
"Whether authenticated with provider"
|
|
649
|
+
authenticated: Boolean!
|
|
650
|
+
"Account ID"
|
|
651
|
+
accountId: String
|
|
652
|
+
"Account email"
|
|
653
|
+
email: String
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
type BackupStatusResult {
|
|
657
|
+
"Whether backups are available"
|
|
658
|
+
available: Boolean!
|
|
659
|
+
"Whether the feature is enabled"
|
|
660
|
+
featureEnabled: Boolean!
|
|
661
|
+
"Dropbox authentication status"
|
|
662
|
+
dropbox: BackupProviderStatus
|
|
663
|
+
"Google Drive authentication status"
|
|
664
|
+
googleDrive: BackupProviderStatus
|
|
665
|
+
"Message if backups unavailable"
|
|
666
|
+
message: String
|
|
667
|
+
"Error message if failed"
|
|
668
|
+
error: String
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
type BackupMetadata {
|
|
672
|
+
"Snapshot ID"
|
|
673
|
+
snapshotId: String!
|
|
674
|
+
"Backup timestamp (ISO format)"
|
|
675
|
+
timestamp: String
|
|
676
|
+
"Backup note/description"
|
|
677
|
+
note: String
|
|
678
|
+
"Site domain"
|
|
679
|
+
siteDomain: String
|
|
680
|
+
"Services info (JSON)"
|
|
681
|
+
services: String
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
type ListBackupsResult {
|
|
685
|
+
"Whether query was successful"
|
|
686
|
+
success: Boolean!
|
|
687
|
+
"Site name"
|
|
688
|
+
siteName: String
|
|
689
|
+
"Backup provider"
|
|
690
|
+
provider: String
|
|
691
|
+
"List of backups"
|
|
692
|
+
backups: [BackupMetadata!]
|
|
693
|
+
"Number of backups"
|
|
694
|
+
count: Int
|
|
695
|
+
"Error message if failed"
|
|
696
|
+
error: String
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
type CreateBackupResult {
|
|
700
|
+
"Whether backup was created successfully"
|
|
701
|
+
success: Boolean!
|
|
702
|
+
"Snapshot ID"
|
|
703
|
+
snapshotId: String
|
|
704
|
+
"Backup timestamp"
|
|
705
|
+
timestamp: String
|
|
706
|
+
"Status message"
|
|
707
|
+
message: String
|
|
708
|
+
"Error message if failed"
|
|
709
|
+
error: String
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
type RestoreBackupResult {
|
|
713
|
+
"Whether restore was successful"
|
|
714
|
+
success: Boolean!
|
|
715
|
+
"Status message"
|
|
716
|
+
message: String
|
|
717
|
+
"Error message if failed"
|
|
718
|
+
error: String
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
type DeleteBackupResult {
|
|
722
|
+
"Whether deletion was successful"
|
|
723
|
+
success: Boolean!
|
|
724
|
+
"Deleted snapshot ID"
|
|
725
|
+
deletedSnapshotId: String
|
|
726
|
+
"Status message"
|
|
727
|
+
message: String
|
|
728
|
+
"Error message if failed"
|
|
729
|
+
error: String
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
type DownloadBackupResult {
|
|
733
|
+
"Whether download was successful"
|
|
734
|
+
success: Boolean!
|
|
735
|
+
"Path to downloaded file"
|
|
736
|
+
filePath: String
|
|
737
|
+
"Status message"
|
|
738
|
+
message: String
|
|
739
|
+
"Error message if failed"
|
|
740
|
+
error: String
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
type EditBackupNoteResult {
|
|
744
|
+
"Whether edit was successful"
|
|
745
|
+
success: Boolean!
|
|
746
|
+
"Updated snapshot ID"
|
|
747
|
+
snapshotId: String
|
|
748
|
+
"Updated note"
|
|
749
|
+
note: String
|
|
750
|
+
"Error message if failed"
|
|
751
|
+
error: String
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
extend type Query {
|
|
755
|
+
# Phase 10: Cloud Backups
|
|
756
|
+
"Check if cloud backups are available and authenticated"
|
|
757
|
+
backupStatus: BackupStatusResult!
|
|
758
|
+
|
|
759
|
+
"List all backups for a site"
|
|
760
|
+
listBackups(siteId: ID!, provider: String!): ListBackupsResult!
|
|
761
|
+
|
|
762
|
+
# Phase 11c: Sync Operations
|
|
763
|
+
"Get sync history for a local site"
|
|
764
|
+
getSyncHistory(siteId: ID!, limit: Int): GetSyncHistoryResult!
|
|
765
|
+
|
|
766
|
+
"Get file changes between local site and WP Engine (dry-run comparison)"
|
|
767
|
+
getSiteChanges(siteId: ID!, direction: String = "push"): GetSiteChangesResult!
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
extend type Mutation {
|
|
771
|
+
# Phase 10: Cloud Backups
|
|
772
|
+
"Create a backup of a site to cloud storage"
|
|
773
|
+
createBackup(siteId: ID!, provider: String!, note: String): CreateBackupResult!
|
|
774
|
+
|
|
775
|
+
"Restore a site from a cloud backup"
|
|
776
|
+
restoreBackup(
|
|
777
|
+
siteId: ID!
|
|
778
|
+
provider: String!
|
|
779
|
+
snapshotId: String!
|
|
780
|
+
confirm: Boolean = false
|
|
781
|
+
): RestoreBackupResult!
|
|
782
|
+
|
|
783
|
+
"Delete a backup from cloud storage"
|
|
784
|
+
deleteBackup(
|
|
785
|
+
siteId: ID!
|
|
786
|
+
provider: String!
|
|
787
|
+
snapshotId: String!
|
|
788
|
+
confirm: Boolean = false
|
|
789
|
+
): DeleteBackupResult!
|
|
790
|
+
|
|
791
|
+
"Download a backup as a ZIP file"
|
|
792
|
+
downloadBackup(siteId: ID!, provider: String!, snapshotId: String!): DownloadBackupResult!
|
|
793
|
+
|
|
794
|
+
"Update the note/description for a backup"
|
|
795
|
+
editBackupNote(
|
|
796
|
+
siteId: ID!
|
|
797
|
+
provider: String!
|
|
798
|
+
snapshotId: String!
|
|
799
|
+
note: String!
|
|
800
|
+
): EditBackupNoteResult!
|
|
801
|
+
|
|
802
|
+
# Phase 11: WP Engine Connect
|
|
803
|
+
"Authenticate with WP Engine (opens browser for OAuth)"
|
|
804
|
+
wpeAuthenticate: WpeAuthResult!
|
|
805
|
+
|
|
806
|
+
"Logout from WP Engine"
|
|
807
|
+
wpeLogout: WpeLogoutResult!
|
|
808
|
+
|
|
809
|
+
# Phase 11c: Sync Operations
|
|
810
|
+
"Push local site to WP Engine"
|
|
811
|
+
pushToWpe(
|
|
812
|
+
localSiteId: ID!
|
|
813
|
+
remoteInstallId: ID!
|
|
814
|
+
includeSql: Boolean = false
|
|
815
|
+
confirm: Boolean = false
|
|
816
|
+
): SyncResult!
|
|
817
|
+
|
|
818
|
+
"Pull from WP Engine to local site"
|
|
819
|
+
pullFromWpe(localSiteId: ID!, remoteInstallId: ID!, includeSql: Boolean = false): SyncResult!
|
|
820
|
+
}
|
|
821
|
+
`;
|
|
822
|
+
/**
|
|
823
|
+
* Create GraphQL resolvers that use Local's internal services
|
|
824
|
+
*/
|
|
825
|
+
function createResolvers(services) {
|
|
826
|
+
const { deleteSite: deleteSiteService, siteData, localLogger, wpCli, siteProcessManager, addSite: addSiteService, cloneSite: cloneSiteService, exportSite: exportSiteService, blueprints: blueprintsService, browserManager, adminer, x509Cert, siteProvisioner, importSite: importSiteService, lightningServices, siteDatabase, importSQLFile: importSQLFileService,
|
|
827
|
+
// Phase 11: WP Engine Connect
|
|
828
|
+
wpeOAuth: wpeOAuthService, capi: capiService,
|
|
829
|
+
// Phase 11c: Sync services
|
|
830
|
+
wpePush: wpePushService, wpePull: wpePullService, connectHistory: connectHistoryService, wpeConnectBase: wpeConnectBaseService,
|
|
831
|
+
// Note: Phase 10 Cloud Backup services are accessed via IPC to the Cloud Backups addon
|
|
832
|
+
// (backupService, dropbox, googleDrive, featureFlags, userData)
|
|
833
|
+
} = services;
|
|
834
|
+
// Helper to invoke IPC calls to the Cloud Backups addon
|
|
835
|
+
// This uses the same pattern as the BackupAIBridge
|
|
836
|
+
// Timeout constants for backup operations (in milliseconds)
|
|
837
|
+
const BACKUP_IPC_TIMEOUT = 600000; // 10 minutes for backup operations
|
|
838
|
+
const DEFAULT_IPC_TIMEOUT = 30000; // 30 seconds for quick operations
|
|
839
|
+
const invokeBackupIPC = async (channel, timeoutMs = BACKUP_IPC_TIMEOUT, ...args) => {
|
|
840
|
+
return new Promise((resolve, reject) => {
|
|
841
|
+
const timestamp = Date.now();
|
|
842
|
+
const random = Math.random().toString(36).substr(2, 9);
|
|
843
|
+
const successReplyChannel = `${channel}-success-${timestamp}-${random}`;
|
|
844
|
+
const errorReplyChannel = `${channel}-error-${timestamp}-${random}`;
|
|
845
|
+
const timeoutSeconds = Math.round(timeoutMs / 1000);
|
|
846
|
+
const timeout = setTimeout(() => {
|
|
847
|
+
electron_1.ipcMain.removeAllListeners(successReplyChannel);
|
|
848
|
+
electron_1.ipcMain.removeAllListeners(errorReplyChannel);
|
|
849
|
+
reject(new Error(`IPC call to ${channel} timed out after ${timeoutSeconds} seconds`));
|
|
850
|
+
}, timeoutMs);
|
|
851
|
+
electron_1.ipcMain.once(successReplyChannel, (_event, result) => {
|
|
852
|
+
clearTimeout(timeout);
|
|
853
|
+
electron_1.ipcMain.removeAllListeners(errorReplyChannel);
|
|
854
|
+
localLogger.info(`[${ADDON_NAME}] IPC success from ${channel}`);
|
|
855
|
+
resolve({ result });
|
|
856
|
+
});
|
|
857
|
+
electron_1.ipcMain.once(errorReplyChannel, (_event, error) => {
|
|
858
|
+
clearTimeout(timeout);
|
|
859
|
+
electron_1.ipcMain.removeAllListeners(successReplyChannel);
|
|
860
|
+
localLogger.error(`[${ADDON_NAME}] IPC error from ${channel}: ${error?.message}`);
|
|
861
|
+
resolve({ error });
|
|
862
|
+
});
|
|
863
|
+
const mockEvent = {
|
|
864
|
+
reply: (replyChannel, data) => {
|
|
865
|
+
electron_1.ipcMain.emit(replyChannel, null, data);
|
|
866
|
+
},
|
|
867
|
+
sender: {
|
|
868
|
+
send: (replyChannel, data) => {
|
|
869
|
+
electron_1.ipcMain.emit(replyChannel, null, data);
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
};
|
|
873
|
+
const replyChannels = { successReplyChannel, errorReplyChannel };
|
|
874
|
+
localLogger.info(`[${ADDON_NAME}] Invoking backup IPC: ${channel}`);
|
|
875
|
+
electron_1.ipcMain.emit(channel, mockEvent, replyChannels, ...args);
|
|
876
|
+
});
|
|
877
|
+
};
|
|
878
|
+
// Helper to get backup providers from the Cloud Backups addon
|
|
879
|
+
const getBackupProviders = async () => {
|
|
880
|
+
try {
|
|
881
|
+
const result = await invokeBackupIPC('backups:enabled-providers', DEFAULT_IPC_TIMEOUT);
|
|
882
|
+
localLogger.info(`[${ADDON_NAME}] Raw IPC result: ${JSON.stringify(result)}`);
|
|
883
|
+
if (result.error) {
|
|
884
|
+
localLogger.error(`[${ADDON_NAME}] Failed to get backup providers: ${result.error.message}`);
|
|
885
|
+
return [];
|
|
886
|
+
}
|
|
887
|
+
// The response is double-nested: result.result.result contains the array
|
|
888
|
+
// Structure: { result: { result: [providers...] } }
|
|
889
|
+
let providers = result.result;
|
|
890
|
+
// Unwrap nested result if present
|
|
891
|
+
if (providers && typeof providers === 'object' && !Array.isArray(providers)) {
|
|
892
|
+
if (Array.isArray(providers.result)) {
|
|
893
|
+
providers = providers.result;
|
|
894
|
+
}
|
|
895
|
+
else if (providers.result && typeof providers.result === 'object') {
|
|
896
|
+
// Even deeper nesting
|
|
897
|
+
providers = providers.result;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
localLogger.info(`[${ADDON_NAME}] Extracted providers: ${JSON.stringify(providers)}`);
|
|
901
|
+
if (Array.isArray(providers)) {
|
|
902
|
+
localLogger.info(`[${ADDON_NAME}] Got ${providers.length} backup providers`);
|
|
903
|
+
return providers;
|
|
904
|
+
}
|
|
905
|
+
localLogger.warn(`[${ADDON_NAME}] Unexpected providers format after unwrapping: ${typeof providers}`);
|
|
906
|
+
return [];
|
|
907
|
+
}
|
|
908
|
+
catch (error) {
|
|
909
|
+
localLogger.error(`[${ADDON_NAME}] Error getting backup providers: ${error.message}`);
|
|
910
|
+
return [];
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
// Shared WP-CLI execution logic
|
|
914
|
+
const executeWpCli = async (_parent, args) => {
|
|
915
|
+
const { siteId, args: wpArgs, skipPlugins = true, skipThemes = true } = args.input;
|
|
916
|
+
try {
|
|
917
|
+
localLogger.info(`[${ADDON_NAME}] Running WP-CLI: wp ${wpArgs.join(' ')}`);
|
|
918
|
+
const site = siteData.getSite(siteId);
|
|
919
|
+
if (!site) {
|
|
920
|
+
return {
|
|
921
|
+
success: false,
|
|
922
|
+
output: null,
|
|
923
|
+
error: `Site not found: ${siteId}`,
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
927
|
+
if (status !== 'running') {
|
|
928
|
+
return {
|
|
929
|
+
success: false,
|
|
930
|
+
output: null,
|
|
931
|
+
error: `Site "${site.name}" is not running. Start it first with: local-cli start ${site.name}`,
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
const output = await wpCli.run(site, wpArgs, {
|
|
935
|
+
skipPlugins,
|
|
936
|
+
skipThemes,
|
|
937
|
+
ignoreErrors: false,
|
|
938
|
+
});
|
|
939
|
+
localLogger.info(`[${ADDON_NAME}] WP-CLI completed successfully`);
|
|
940
|
+
return {
|
|
941
|
+
success: true,
|
|
942
|
+
output: output?.trim() || '',
|
|
943
|
+
error: null,
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
catch (error) {
|
|
947
|
+
localLogger.error(`[${ADDON_NAME}] WP-CLI failed:`, error);
|
|
948
|
+
return {
|
|
949
|
+
success: false,
|
|
950
|
+
output: null,
|
|
951
|
+
error: error.message || 'Unknown error',
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
return {
|
|
956
|
+
Query: {
|
|
957
|
+
wpCliQuery: executeWpCli,
|
|
958
|
+
blueprints: async () => {
|
|
959
|
+
try {
|
|
960
|
+
localLogger.info(`[${ADDON_NAME}] Fetching blueprints`);
|
|
961
|
+
const blueprintsList = await blueprintsService.getBlueprints();
|
|
962
|
+
return {
|
|
963
|
+
success: true,
|
|
964
|
+
error: null,
|
|
965
|
+
blueprints: blueprintsList.map((bp) => ({
|
|
966
|
+
name: bp.name,
|
|
967
|
+
lastModified: bp.lastModified,
|
|
968
|
+
// Handle nested objects - extract just the name/type string
|
|
969
|
+
phpVersion: typeof bp.phpVersion === 'object'
|
|
970
|
+
? bp.phpVersion?.name || bp.phpVersion?.version
|
|
971
|
+
: bp.phpVersion,
|
|
972
|
+
webServer: typeof bp.webServer === 'object'
|
|
973
|
+
? bp.webServer?.name || bp.webServer?.type
|
|
974
|
+
: bp.webServer,
|
|
975
|
+
database: typeof bp.database === 'object'
|
|
976
|
+
? bp.database?.name || bp.database?.type
|
|
977
|
+
: bp.database,
|
|
978
|
+
})),
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
catch (error) {
|
|
982
|
+
localLogger.error(`[${ADDON_NAME}] Failed to fetch blueprints:`, error);
|
|
983
|
+
return {
|
|
984
|
+
success: false,
|
|
985
|
+
error: error.message || 'Unknown error',
|
|
986
|
+
blueprints: [],
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
listServices: async (_parent, args) => {
|
|
991
|
+
const { type = 'all' } = args;
|
|
992
|
+
try {
|
|
993
|
+
localLogger.info(`[${ADDON_NAME}] Listing services (type: ${type})`);
|
|
994
|
+
if (!lightningServices) {
|
|
995
|
+
return {
|
|
996
|
+
success: false,
|
|
997
|
+
error: 'Lightning services not available',
|
|
998
|
+
services: [],
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
const roleMap = {
|
|
1002
|
+
php: 'php',
|
|
1003
|
+
database: 'mysql',
|
|
1004
|
+
webserver: 'nginx',
|
|
1005
|
+
};
|
|
1006
|
+
const roleFilter = type !== 'all' ? roleMap[type] : undefined;
|
|
1007
|
+
const registeredServices = lightningServices.getRegisteredServices(roleFilter);
|
|
1008
|
+
const serviceList = [];
|
|
1009
|
+
for (const [role, versions] of Object.entries(registeredServices)) {
|
|
1010
|
+
for (const [version, info] of Object.entries(versions)) {
|
|
1011
|
+
serviceList.push({
|
|
1012
|
+
role,
|
|
1013
|
+
name: info?.name || role,
|
|
1014
|
+
version,
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return {
|
|
1019
|
+
success: true,
|
|
1020
|
+
error: null,
|
|
1021
|
+
services: serviceList,
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
catch (error) {
|
|
1025
|
+
localLogger.error(`[${ADDON_NAME}] Failed to list services:`, error);
|
|
1026
|
+
return {
|
|
1027
|
+
success: false,
|
|
1028
|
+
error: error.message || 'Unknown error',
|
|
1029
|
+
services: [],
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
// Phase 11: WP Engine Connect
|
|
1034
|
+
wpeStatus: async () => {
|
|
1035
|
+
try {
|
|
1036
|
+
localLogger.info(`[${ADDON_NAME}] Checking WP Engine authentication status`);
|
|
1037
|
+
if (!wpeOAuthService) {
|
|
1038
|
+
return {
|
|
1039
|
+
authenticated: false,
|
|
1040
|
+
email: null,
|
|
1041
|
+
accountId: null,
|
|
1042
|
+
accountName: null,
|
|
1043
|
+
tokenExpiry: null,
|
|
1044
|
+
error: 'WP Engine OAuth service not available',
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
// Check if we have valid credentials by trying to get access token
|
|
1048
|
+
const accessToken = await wpeOAuthService.getAccessToken();
|
|
1049
|
+
if (!accessToken) {
|
|
1050
|
+
return {
|
|
1051
|
+
authenticated: false,
|
|
1052
|
+
email: null,
|
|
1053
|
+
accountId: null,
|
|
1054
|
+
accountName: null,
|
|
1055
|
+
tokenExpiry: null,
|
|
1056
|
+
error: null,
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
// Try to get user info from CAPI if available
|
|
1060
|
+
let email = null;
|
|
1061
|
+
if (capiService) {
|
|
1062
|
+
try {
|
|
1063
|
+
const currentUser = await capiService.getCurrentUser();
|
|
1064
|
+
email = currentUser?.email || null;
|
|
1065
|
+
}
|
|
1066
|
+
catch {
|
|
1067
|
+
// User info not available, but still authenticated
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return {
|
|
1071
|
+
authenticated: true,
|
|
1072
|
+
email,
|
|
1073
|
+
accountId: null,
|
|
1074
|
+
accountName: null,
|
|
1075
|
+
tokenExpiry: null,
|
|
1076
|
+
error: null,
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
catch (error) {
|
|
1080
|
+
localLogger.error(`[${ADDON_NAME}] Failed to check WPE status:`, error);
|
|
1081
|
+
return {
|
|
1082
|
+
authenticated: false,
|
|
1083
|
+
email: null,
|
|
1084
|
+
accountId: null,
|
|
1085
|
+
accountName: null,
|
|
1086
|
+
tokenExpiry: null,
|
|
1087
|
+
error: error.message || 'Unknown error',
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
},
|
|
1091
|
+
listWpeSites: async (_parent, args) => {
|
|
1092
|
+
const { accountId } = args;
|
|
1093
|
+
try {
|
|
1094
|
+
localLogger.info(`[${ADDON_NAME}] Listing WP Engine sites${accountId ? ` for account ${accountId}` : ''}`);
|
|
1095
|
+
if (!wpeOAuthService) {
|
|
1096
|
+
return {
|
|
1097
|
+
success: false,
|
|
1098
|
+
error: 'WP Engine OAuth service not available',
|
|
1099
|
+
sites: [],
|
|
1100
|
+
count: 0,
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
// Check if authenticated by trying to get access token
|
|
1104
|
+
const accessToken = await wpeOAuthService.getAccessToken();
|
|
1105
|
+
if (!accessToken) {
|
|
1106
|
+
return {
|
|
1107
|
+
success: false,
|
|
1108
|
+
error: 'Not authenticated with WP Engine. Use wpe_authenticate first.',
|
|
1109
|
+
sites: [],
|
|
1110
|
+
count: 0,
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
if (!capiService) {
|
|
1114
|
+
return {
|
|
1115
|
+
success: false,
|
|
1116
|
+
error: 'WP Engine CAPI service not available',
|
|
1117
|
+
sites: [],
|
|
1118
|
+
count: 0,
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
// Get installs from CAPI using getInstallList
|
|
1122
|
+
const installs = await capiService.getInstallList();
|
|
1123
|
+
if (!installs) {
|
|
1124
|
+
return {
|
|
1125
|
+
success: true,
|
|
1126
|
+
error: null,
|
|
1127
|
+
sites: [],
|
|
1128
|
+
count: 0,
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
const sites = installs.map((install) => ({
|
|
1132
|
+
id: install.id,
|
|
1133
|
+
name: install.name,
|
|
1134
|
+
environment: install.environment || 'production',
|
|
1135
|
+
phpVersion: install.phpVersion || null,
|
|
1136
|
+
primaryDomain: install.primaryDomain || install.cname || null,
|
|
1137
|
+
accountId: install.accountId || accountId || null,
|
|
1138
|
+
accountName: install.accountName || null,
|
|
1139
|
+
sftpHost: `${install.name}.ssh.wpengine.net`,
|
|
1140
|
+
sftpUser: install.name,
|
|
1141
|
+
}));
|
|
1142
|
+
return {
|
|
1143
|
+
success: true,
|
|
1144
|
+
error: null,
|
|
1145
|
+
sites,
|
|
1146
|
+
count: sites.length,
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
catch (error) {
|
|
1150
|
+
localLogger.error(`[${ADDON_NAME}] Failed to list WPE sites:`, error);
|
|
1151
|
+
return {
|
|
1152
|
+
success: false,
|
|
1153
|
+
error: error.message || 'Unknown error',
|
|
1154
|
+
sites: [],
|
|
1155
|
+
count: 0,
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
// Phase 11b: Site Linking
|
|
1160
|
+
getWpeLink: async (_parent, args) => {
|
|
1161
|
+
const { siteId } = args;
|
|
1162
|
+
try {
|
|
1163
|
+
localLogger.info(`[${ADDON_NAME}] Getting WP Engine link for site ${siteId}`);
|
|
1164
|
+
// Get site from siteData
|
|
1165
|
+
const site = siteData.getSite(siteId);
|
|
1166
|
+
if (!site) {
|
|
1167
|
+
return {
|
|
1168
|
+
linked: false,
|
|
1169
|
+
siteName: null,
|
|
1170
|
+
connections: [],
|
|
1171
|
+
connectionCount: 0,
|
|
1172
|
+
message: null,
|
|
1173
|
+
error: `Site not found: ${siteId}`,
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
// Get hostConnections from site
|
|
1177
|
+
const hostConnections = site.hostConnections || [];
|
|
1178
|
+
const wpeConnections = hostConnections.filter((c) => c.hostId === 'wpe');
|
|
1179
|
+
if (wpeConnections.length === 0) {
|
|
1180
|
+
return {
|
|
1181
|
+
linked: false,
|
|
1182
|
+
siteName: site.name,
|
|
1183
|
+
connections: [],
|
|
1184
|
+
connectionCount: 0,
|
|
1185
|
+
message: 'Site is not linked to any WP Engine environment. Use Connect in Local to pull a site from WPE.',
|
|
1186
|
+
error: null,
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
// Transform connections for output, enriching with CAPI data if available
|
|
1190
|
+
const connections = await Promise.all(wpeConnections.map(async (c) => {
|
|
1191
|
+
let installName = c.remoteSiteId; // Default to UUID
|
|
1192
|
+
let portalUrl = null;
|
|
1193
|
+
let primaryDomain = null;
|
|
1194
|
+
// Try to get install details from CAPI to get the actual name
|
|
1195
|
+
// remoteSiteId matches install.site.id (WPE Site ID), not install.id
|
|
1196
|
+
if (capiService && typeof capiService.getInstallList === 'function') {
|
|
1197
|
+
try {
|
|
1198
|
+
localLogger.info(`[${ADDON_NAME}] Looking for install with site.id=${c.remoteSiteId}, env=${c.remoteSiteEnv}`);
|
|
1199
|
+
const installs = await capiService.getInstallList();
|
|
1200
|
+
localLogger.info(`[${ADDON_NAME}] Got ${installs?.length || 0} installs from CAPI`);
|
|
1201
|
+
if (installs && installs.length > 0) {
|
|
1202
|
+
// Log first install structure for debugging
|
|
1203
|
+
localLogger.info(`[${ADDON_NAME}] Sample install structure: ${JSON.stringify(installs[0], null, 2)}`);
|
|
1204
|
+
// Match by site.id (remoteSiteId is the WPE Site ID, not Install ID)
|
|
1205
|
+
// Also filter by environment if available
|
|
1206
|
+
const matchingInstall = installs.find((i) => i.site?.id === c.remoteSiteId &&
|
|
1207
|
+
(!c.remoteSiteEnv || i.environment === c.remoteSiteEnv));
|
|
1208
|
+
if (matchingInstall) {
|
|
1209
|
+
localLogger.info(`[${ADDON_NAME}] Found match: ${matchingInstall.name}`);
|
|
1210
|
+
installName = matchingInstall.name;
|
|
1211
|
+
portalUrl = `https://my.wpengine.com/installs/${matchingInstall.name}`;
|
|
1212
|
+
primaryDomain =
|
|
1213
|
+
matchingInstall.primary_domain || matchingInstall.cname || null;
|
|
1214
|
+
}
|
|
1215
|
+
else {
|
|
1216
|
+
localLogger.warn(`[${ADDON_NAME}] No matching install found for site.id=${c.remoteSiteId}`);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
catch (e) {
|
|
1221
|
+
localLogger.warn(`[${ADDON_NAME}] Could not look up install from CAPI: ${e.message}`);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
else {
|
|
1225
|
+
localLogger.warn(`[${ADDON_NAME}] capiService or getInstallList not available`);
|
|
1226
|
+
}
|
|
1227
|
+
return {
|
|
1228
|
+
remoteInstallId: c.remoteSiteId,
|
|
1229
|
+
installName,
|
|
1230
|
+
environment: c.remoteSiteEnv || null,
|
|
1231
|
+
accountId: c.accountId || null,
|
|
1232
|
+
portalUrl,
|
|
1233
|
+
primaryDomain,
|
|
1234
|
+
};
|
|
1235
|
+
}));
|
|
1236
|
+
// Capabilities are always the same for WPE-connected sites
|
|
1237
|
+
const capabilities = {
|
|
1238
|
+
canPush: true,
|
|
1239
|
+
canPull: true,
|
|
1240
|
+
syncModes: ['all_files', 'select_files', 'database_only'],
|
|
1241
|
+
magicSyncAvailable: true,
|
|
1242
|
+
databaseSyncAvailable: true,
|
|
1243
|
+
};
|
|
1244
|
+
return {
|
|
1245
|
+
linked: true,
|
|
1246
|
+
siteName: site.name,
|
|
1247
|
+
connections,
|
|
1248
|
+
connectionCount: connections.length,
|
|
1249
|
+
capabilities,
|
|
1250
|
+
message: null,
|
|
1251
|
+
error: null,
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
catch (error) {
|
|
1255
|
+
localLogger.error(`[${ADDON_NAME}] Failed to get WPE link:`, error);
|
|
1256
|
+
return {
|
|
1257
|
+
linked: false,
|
|
1258
|
+
siteName: null,
|
|
1259
|
+
connections: [],
|
|
1260
|
+
connectionCount: 0,
|
|
1261
|
+
message: null,
|
|
1262
|
+
error: error.message || 'Unknown error',
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
// Phase 10: Cloud Backups
|
|
1267
|
+
backupStatus: async () => {
|
|
1268
|
+
try {
|
|
1269
|
+
localLogger.info(`[${ADDON_NAME}] Checking backup status`);
|
|
1270
|
+
// Get providers from Cloud Backups addon via IPC
|
|
1271
|
+
const providers = await getBackupProviders();
|
|
1272
|
+
localLogger.info(`[${ADDON_NAME}] Got ${providers.length} backup providers`);
|
|
1273
|
+
if (providers.length === 0) {
|
|
1274
|
+
return {
|
|
1275
|
+
available: false,
|
|
1276
|
+
featureEnabled: false,
|
|
1277
|
+
dropbox: null,
|
|
1278
|
+
googleDrive: null,
|
|
1279
|
+
message: 'No cloud storage providers configured. Connect Google Drive or Dropbox in Local Hub (hub.localwp.com/addons/cloud-backups).',
|
|
1280
|
+
error: null,
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
// Map provider info to our response format
|
|
1284
|
+
const dropboxProvider = providers.find((p) => p.id === 'dropbox' || p.name?.toLowerCase().includes('dropbox'));
|
|
1285
|
+
const googleProvider = providers.find((p) => p.id === 'google' || p.name?.toLowerCase().includes('google'));
|
|
1286
|
+
const dropboxStatus = dropboxProvider
|
|
1287
|
+
? {
|
|
1288
|
+
authenticated: true,
|
|
1289
|
+
accountId: dropboxProvider.id,
|
|
1290
|
+
email: dropboxProvider.email || null,
|
|
1291
|
+
}
|
|
1292
|
+
: {
|
|
1293
|
+
authenticated: false,
|
|
1294
|
+
accountId: null,
|
|
1295
|
+
email: null,
|
|
1296
|
+
};
|
|
1297
|
+
const googleDriveStatus = googleProvider
|
|
1298
|
+
? {
|
|
1299
|
+
authenticated: true,
|
|
1300
|
+
accountId: googleProvider.id,
|
|
1301
|
+
email: googleProvider.email || null,
|
|
1302
|
+
}
|
|
1303
|
+
: {
|
|
1304
|
+
authenticated: false,
|
|
1305
|
+
accountId: null,
|
|
1306
|
+
email: null,
|
|
1307
|
+
};
|
|
1308
|
+
const hasProvider = providers.length > 0;
|
|
1309
|
+
return {
|
|
1310
|
+
available: hasProvider,
|
|
1311
|
+
featureEnabled: true,
|
|
1312
|
+
dropbox: dropboxStatus,
|
|
1313
|
+
googleDrive: googleDriveStatus,
|
|
1314
|
+
message: hasProvider
|
|
1315
|
+
? null
|
|
1316
|
+
: 'No cloud storage provider authenticated. Connect Dropbox or Google Drive in Local settings.',
|
|
1317
|
+
error: null,
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
catch (error) {
|
|
1321
|
+
localLogger.error(`[${ADDON_NAME}] Failed to check backup status:`, error);
|
|
1322
|
+
return {
|
|
1323
|
+
available: false,
|
|
1324
|
+
featureEnabled: false,
|
|
1325
|
+
dropbox: null,
|
|
1326
|
+
googleDrive: null,
|
|
1327
|
+
message: null,
|
|
1328
|
+
error: error.message || 'Unknown error',
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
listBackups: async (_parent, args) => {
|
|
1333
|
+
const { siteId, provider } = args;
|
|
1334
|
+
try {
|
|
1335
|
+
localLogger.info(`[${ADDON_NAME}] Listing backups for site ${siteId} from ${provider}`);
|
|
1336
|
+
// Get site
|
|
1337
|
+
const site = siteData.getSite(siteId);
|
|
1338
|
+
if (!site) {
|
|
1339
|
+
return {
|
|
1340
|
+
success: false,
|
|
1341
|
+
siteName: null,
|
|
1342
|
+
provider,
|
|
1343
|
+
backups: [],
|
|
1344
|
+
count: 0,
|
|
1345
|
+
error: `Site not found: ${siteId}`,
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
// Get providers from Cloud Backups addon
|
|
1349
|
+
const providers = await getBackupProviders();
|
|
1350
|
+
if (providers.length === 0) {
|
|
1351
|
+
return {
|
|
1352
|
+
success: false,
|
|
1353
|
+
siteName: site.name,
|
|
1354
|
+
provider,
|
|
1355
|
+
backups: [],
|
|
1356
|
+
count: 0,
|
|
1357
|
+
error: 'No cloud storage providers configured. Connect Google Drive or Dropbox in Local Hub.',
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
// Find the matching provider (map 'googleDrive' to 'google' for the addon)
|
|
1361
|
+
const providerMap = { googleDrive: 'google', dropbox: 'dropbox' };
|
|
1362
|
+
const providerId = providerMap[provider] || provider;
|
|
1363
|
+
const matchedProvider = providers.find((p) => p.id === providerId);
|
|
1364
|
+
if (!matchedProvider) {
|
|
1365
|
+
return {
|
|
1366
|
+
success: false,
|
|
1367
|
+
siteName: site.name,
|
|
1368
|
+
provider,
|
|
1369
|
+
backups: [],
|
|
1370
|
+
count: 0,
|
|
1371
|
+
error: `Provider '${provider}' not configured. Available: ${providers.map((p) => p.name).join(', ')}`,
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
// For listing snapshots, use the Hub provider ID directly (e.g., 'google')
|
|
1375
|
+
// NOT the rclone backend name ('drive') - the Hub queries expect the OAuth provider name
|
|
1376
|
+
// Also pass pageOffset parameter (0 for first page)
|
|
1377
|
+
const result = await invokeBackupIPC('backups:provider-snapshots', DEFAULT_IPC_TIMEOUT, siteId, matchedProvider.id, 0);
|
|
1378
|
+
localLogger.info(`[${ADDON_NAME}] Provider snapshots raw result: ${JSON.stringify(result)}`);
|
|
1379
|
+
if (result.error) {
|
|
1380
|
+
return {
|
|
1381
|
+
success: false,
|
|
1382
|
+
siteName: site.name,
|
|
1383
|
+
provider,
|
|
1384
|
+
backups: [],
|
|
1385
|
+
count: 0,
|
|
1386
|
+
error: result.error.message || 'Failed to list backups',
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
// Unwrap nested result structure (similar to providers)
|
|
1390
|
+
let backupsData = result.result;
|
|
1391
|
+
if (backupsData && typeof backupsData === 'object' && !Array.isArray(backupsData)) {
|
|
1392
|
+
// Check for nested result or snapshots array
|
|
1393
|
+
if (Array.isArray(backupsData.result)) {
|
|
1394
|
+
backupsData = backupsData.result;
|
|
1395
|
+
}
|
|
1396
|
+
else if (Array.isArray(backupsData.snapshots)) {
|
|
1397
|
+
backupsData = backupsData.snapshots;
|
|
1398
|
+
}
|
|
1399
|
+
else if (backupsData.result && Array.isArray(backupsData.result.snapshots)) {
|
|
1400
|
+
backupsData = backupsData.result.snapshots;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
const backups = Array.isArray(backupsData) ? backupsData : [];
|
|
1404
|
+
localLogger.info(`[${ADDON_NAME}] Extracted ${backups.length} backups`);
|
|
1405
|
+
return {
|
|
1406
|
+
success: true,
|
|
1407
|
+
siteName: site.name,
|
|
1408
|
+
provider,
|
|
1409
|
+
backups: backups.map((b) => ({
|
|
1410
|
+
// Use hash for snapshotId as that's what restic uses for restore/delete operations
|
|
1411
|
+
// The Hub ID (b.id) is just a database identifier
|
|
1412
|
+
snapshotId: b.hash || b.snapshotId || b.short_id,
|
|
1413
|
+
timestamp: b.updatedAt || b.createdAt || b.timestamp || b.time || b.created,
|
|
1414
|
+
note: b.configObject?.description || b.note || b.description || b.tags?.description || '',
|
|
1415
|
+
siteDomain: b.configObject?.name
|
|
1416
|
+
? `${b.configObject.name}.local`
|
|
1417
|
+
: b.siteDomain || site.domain,
|
|
1418
|
+
services: JSON.stringify(b.configObject?.services || b.services || {}),
|
|
1419
|
+
})),
|
|
1420
|
+
count: backups.length,
|
|
1421
|
+
error: null,
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
catch (error) {
|
|
1425
|
+
localLogger.error(`[${ADDON_NAME}] Failed to list backups:`, error);
|
|
1426
|
+
return {
|
|
1427
|
+
success: false,
|
|
1428
|
+
siteName: null,
|
|
1429
|
+
provider,
|
|
1430
|
+
backups: [],
|
|
1431
|
+
count: 0,
|
|
1432
|
+
error: error.message || 'Unknown error',
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
},
|
|
1436
|
+
// Phase 11c: Sync History
|
|
1437
|
+
getSyncHistory: async (_parent, args) => {
|
|
1438
|
+
const { siteId, limit = 30 } = args;
|
|
1439
|
+
try {
|
|
1440
|
+
localLogger.info(`[${ADDON_NAME}] Getting sync history for site ${siteId}`);
|
|
1441
|
+
// Get site to verify it exists
|
|
1442
|
+
const site = siteData.getSite(siteId);
|
|
1443
|
+
if (!site) {
|
|
1444
|
+
return {
|
|
1445
|
+
success: false,
|
|
1446
|
+
siteName: null,
|
|
1447
|
+
events: [],
|
|
1448
|
+
count: 0,
|
|
1449
|
+
error: `Site not found: ${siteId}`,
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
// Check if connectHistory service is available
|
|
1453
|
+
if (!connectHistoryService || typeof connectHistoryService.getEvents !== 'function') {
|
|
1454
|
+
return {
|
|
1455
|
+
success: false,
|
|
1456
|
+
siteName: site.name,
|
|
1457
|
+
events: [],
|
|
1458
|
+
count: 0,
|
|
1459
|
+
error: 'Sync history service not available',
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
const events = connectHistoryService.getEvents(siteId);
|
|
1463
|
+
const limitedEvents = events.slice(0, limit);
|
|
1464
|
+
return {
|
|
1465
|
+
success: true,
|
|
1466
|
+
siteName: site.name,
|
|
1467
|
+
events: limitedEvents.map((e) => ({
|
|
1468
|
+
remoteInstallName: e.remoteInstallName || null,
|
|
1469
|
+
timestamp: e.timestamp,
|
|
1470
|
+
environment: e.environment,
|
|
1471
|
+
direction: e.direction,
|
|
1472
|
+
status: e.status || null,
|
|
1473
|
+
})),
|
|
1474
|
+
count: limitedEvents.length,
|
|
1475
|
+
error: null,
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
catch (error) {
|
|
1479
|
+
localLogger.error(`[${ADDON_NAME}] Failed to get sync history:`, error);
|
|
1480
|
+
return {
|
|
1481
|
+
success: false,
|
|
1482
|
+
siteName: null,
|
|
1483
|
+
events: [],
|
|
1484
|
+
count: 0,
|
|
1485
|
+
error: error.message || 'Unknown error',
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
},
|
|
1489
|
+
// Get file changes between local and WPE (dry-run comparison)
|
|
1490
|
+
getSiteChanges: async (_parent, args) => {
|
|
1491
|
+
const { siteId, direction = 'push' } = args;
|
|
1492
|
+
try {
|
|
1493
|
+
localLogger.info(`[${ADDON_NAME}] Getting site changes for ${siteId}, direction=${direction}`);
|
|
1494
|
+
// Validate direction
|
|
1495
|
+
if (direction !== 'push' && direction !== 'pull') {
|
|
1496
|
+
return {
|
|
1497
|
+
success: false,
|
|
1498
|
+
siteName: null,
|
|
1499
|
+
direction,
|
|
1500
|
+
added: [],
|
|
1501
|
+
modified: [],
|
|
1502
|
+
deleted: [],
|
|
1503
|
+
totalChanges: 0,
|
|
1504
|
+
message: null,
|
|
1505
|
+
error: 'Invalid direction. Must be "push" or "pull".',
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
// Get site
|
|
1509
|
+
const site = siteData.getSite(siteId);
|
|
1510
|
+
if (!site) {
|
|
1511
|
+
return {
|
|
1512
|
+
success: false,
|
|
1513
|
+
siteName: null,
|
|
1514
|
+
direction,
|
|
1515
|
+
added: [],
|
|
1516
|
+
modified: [],
|
|
1517
|
+
deleted: [],
|
|
1518
|
+
totalChanges: 0,
|
|
1519
|
+
message: null,
|
|
1520
|
+
error: `Site not found: ${siteId}`,
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
// Check WPE connection
|
|
1524
|
+
const wpeConnection = site.hostConnections?.find((c) => c.hostId === 'wpe');
|
|
1525
|
+
if (!wpeConnection) {
|
|
1526
|
+
return {
|
|
1527
|
+
success: false,
|
|
1528
|
+
siteName: site.name,
|
|
1529
|
+
direction,
|
|
1530
|
+
added: [],
|
|
1531
|
+
modified: [],
|
|
1532
|
+
deleted: [],
|
|
1533
|
+
totalChanges: 0,
|
|
1534
|
+
message: null,
|
|
1535
|
+
error: 'Site is not linked to WP Engine. Use Connect in Local to link the site first.',
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
// Check service availability
|
|
1539
|
+
if (!wpeConnectBaseService ||
|
|
1540
|
+
typeof wpeConnectBaseService.listModifications !== 'function') {
|
|
1541
|
+
return {
|
|
1542
|
+
success: false,
|
|
1543
|
+
siteName: site.name,
|
|
1544
|
+
direction,
|
|
1545
|
+
added: [],
|
|
1546
|
+
modified: [],
|
|
1547
|
+
deleted: [],
|
|
1548
|
+
totalChanges: 0,
|
|
1549
|
+
message: null,
|
|
1550
|
+
error: 'WPE Connect service not available',
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
// Get install details from CAPI
|
|
1554
|
+
let installName = wpeConnection.remoteSiteId;
|
|
1555
|
+
let primaryDomain = '';
|
|
1556
|
+
let installId = '';
|
|
1557
|
+
if (capiService && typeof capiService.getInstallList === 'function') {
|
|
1558
|
+
const installs = await capiService.getInstallList();
|
|
1559
|
+
const matchingInstall = installs?.find((i) => i.site?.id === wpeConnection.remoteSiteId &&
|
|
1560
|
+
(!wpeConnection.remoteSiteEnv || i.environment === wpeConnection.remoteSiteEnv));
|
|
1561
|
+
if (matchingInstall) {
|
|
1562
|
+
installName = matchingInstall.name;
|
|
1563
|
+
primaryDomain =
|
|
1564
|
+
matchingInstall.primary_domain ||
|
|
1565
|
+
matchingInstall.cname ||
|
|
1566
|
+
`${matchingInstall.name}.wpengine.com`;
|
|
1567
|
+
installId = matchingInstall.id;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
if (!primaryDomain) {
|
|
1571
|
+
return {
|
|
1572
|
+
success: false,
|
|
1573
|
+
siteName: site.name,
|
|
1574
|
+
direction,
|
|
1575
|
+
added: [],
|
|
1576
|
+
modified: [],
|
|
1577
|
+
deleted: [],
|
|
1578
|
+
totalChanges: 0,
|
|
1579
|
+
message: null,
|
|
1580
|
+
error: 'Could not determine WP Engine install details. Please ensure you are authenticated.',
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
// Call listModifications (dry-run rsync comparison)
|
|
1584
|
+
localLogger.info(`[${ADDON_NAME}] Calling listModifications for ${installName}`);
|
|
1585
|
+
const modifications = await wpeConnectBaseService.listModifications({
|
|
1586
|
+
connectArgs: {
|
|
1587
|
+
wpengineInstallName: installName,
|
|
1588
|
+
wpengineInstallId: installId,
|
|
1589
|
+
wpengineSiteId: wpeConnection.remoteSiteId,
|
|
1590
|
+
wpenginePrimaryDomain: primaryDomain,
|
|
1591
|
+
localSiteId: site.id,
|
|
1592
|
+
},
|
|
1593
|
+
direction: direction,
|
|
1594
|
+
includeIgnored: false,
|
|
1595
|
+
});
|
|
1596
|
+
// Categorize changes
|
|
1597
|
+
const added = modifications
|
|
1598
|
+
.filter((f) => f.instruction === 'create' ||
|
|
1599
|
+
f.instruction === 'upload' ||
|
|
1600
|
+
f.instruction === 'download')
|
|
1601
|
+
.map((f) => ({
|
|
1602
|
+
path: f.path,
|
|
1603
|
+
instruction: f.instruction,
|
|
1604
|
+
size: f.size,
|
|
1605
|
+
type: f.type,
|
|
1606
|
+
}));
|
|
1607
|
+
const modified = modifications
|
|
1608
|
+
.filter((f) => f.instruction === 'modify')
|
|
1609
|
+
.map((f) => ({
|
|
1610
|
+
path: f.path,
|
|
1611
|
+
instruction: f.instruction,
|
|
1612
|
+
size: f.size,
|
|
1613
|
+
type: f.type,
|
|
1614
|
+
}));
|
|
1615
|
+
const deleted = modifications
|
|
1616
|
+
.filter((f) => f.instruction === 'delete')
|
|
1617
|
+
.map((f) => ({
|
|
1618
|
+
path: f.path,
|
|
1619
|
+
instruction: f.instruction,
|
|
1620
|
+
size: f.size,
|
|
1621
|
+
type: f.type,
|
|
1622
|
+
}));
|
|
1623
|
+
const totalChanges = added.length + modified.length + deleted.length;
|
|
1624
|
+
const directionLabel = direction === 'push' ? 'local → WPE' : 'WPE → local';
|
|
1625
|
+
return {
|
|
1626
|
+
success: true,
|
|
1627
|
+
siteName: site.name,
|
|
1628
|
+
direction,
|
|
1629
|
+
added,
|
|
1630
|
+
modified,
|
|
1631
|
+
deleted,
|
|
1632
|
+
totalChanges,
|
|
1633
|
+
message: totalChanges > 0
|
|
1634
|
+
? `${totalChanges} file(s) changed (${directionLabel}): ${added.length} added, ${modified.length} modified, ${deleted.length} deleted`
|
|
1635
|
+
: `No changes detected (${directionLabel})`,
|
|
1636
|
+
error: null,
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
catch (error) {
|
|
1640
|
+
localLogger.error(`[${ADDON_NAME}] Failed to get site changes:`, error);
|
|
1641
|
+
return {
|
|
1642
|
+
success: false,
|
|
1643
|
+
siteName: null,
|
|
1644
|
+
direction,
|
|
1645
|
+
added: [],
|
|
1646
|
+
modified: [],
|
|
1647
|
+
deleted: [],
|
|
1648
|
+
totalChanges: 0,
|
|
1649
|
+
message: null,
|
|
1650
|
+
error: error.message || 'Unknown error',
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
},
|
|
1654
|
+
},
|
|
1655
|
+
Mutation: {
|
|
1656
|
+
wpCli: executeWpCli,
|
|
1657
|
+
createSite: async (_parent, args) => {
|
|
1658
|
+
// DEBUG: Log raw args received
|
|
1659
|
+
localLogger.info(`[${ADDON_NAME}] createSite called with args: ${JSON.stringify(args)}`);
|
|
1660
|
+
const { name, phpVersion, webServer = 'nginx', database = 'mysql', wpAdminUsername = 'admin', wpAdminPassword = 'password', wpAdminEmail = 'admin@local.test', blueprint, } = args.input;
|
|
1661
|
+
// DEBUG: Log destructured values
|
|
1662
|
+
localLogger.info(`[${ADDON_NAME}] Destructured - name: ${name}, blueprint: ${blueprint}, typeof blueprint: ${typeof blueprint}`);
|
|
1663
|
+
try {
|
|
1664
|
+
localLogger.info(`[${ADDON_NAME}] Creating site: ${name}${blueprint ? ` from blueprint: ${blueprint}` : ''}`);
|
|
1665
|
+
// Generate slug and domain from name
|
|
1666
|
+
const siteSlug = name
|
|
1667
|
+
.toLowerCase()
|
|
1668
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
1669
|
+
.replace(/^-|-$/g, '');
|
|
1670
|
+
const siteDomain = `${siteSlug}.local`;
|
|
1671
|
+
const os = require('os');
|
|
1672
|
+
const path = require('path');
|
|
1673
|
+
const fs = require('fs');
|
|
1674
|
+
const sitePath = path.join(os.homedir(), 'Local Sites', siteSlug);
|
|
1675
|
+
// If blueprint is provided, use importSiteService instead of addSiteService
|
|
1676
|
+
if (blueprint) {
|
|
1677
|
+
localLogger.info(`[${ADDON_NAME}] Blueprint parameter received: ${blueprint}`);
|
|
1678
|
+
// Get the userDataPath from electron app
|
|
1679
|
+
const { app } = require('electron');
|
|
1680
|
+
const userDataPath = app.getPath('userData');
|
|
1681
|
+
const blueprintZipPath = path.join(userDataPath, 'blueprints', `${blueprint}.zip`);
|
|
1682
|
+
localLogger.info(`[${ADDON_NAME}] Looking for blueprint at: ${blueprintZipPath}`);
|
|
1683
|
+
// Verify blueprint exists
|
|
1684
|
+
if (!fs.existsSync(blueprintZipPath)) {
|
|
1685
|
+
localLogger.error(`[${ADDON_NAME}] Blueprint not found at: ${blueprintZipPath}`);
|
|
1686
|
+
return {
|
|
1687
|
+
success: false,
|
|
1688
|
+
error: `Blueprint not found: ${blueprint}. Use list_blueprints to see available blueprints.`,
|
|
1689
|
+
siteId: null,
|
|
1690
|
+
siteName: name,
|
|
1691
|
+
siteDomain: null,
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
localLogger.info(`[${ADDON_NAME}] Found blueprint at: ${blueprintZipPath}`);
|
|
1695
|
+
// Read the local-site.json from the blueprint zip to get manifest
|
|
1696
|
+
let localSiteJSON;
|
|
1697
|
+
try {
|
|
1698
|
+
const StreamZip = require('node-stream-zip');
|
|
1699
|
+
localLogger.info(`[${ADDON_NAME}] node-stream-zip loaded successfully`);
|
|
1700
|
+
const zip = new StreamZip.async({ file: blueprintZipPath });
|
|
1701
|
+
const entries = await zip.entries();
|
|
1702
|
+
localLogger.info(`[${ADDON_NAME}] Zip entries loaded, count: ${Object.keys(entries).length}`);
|
|
1703
|
+
const filename = entries['local-site.json']
|
|
1704
|
+
? 'local-site.json'
|
|
1705
|
+
: 'pressmatic-site.json';
|
|
1706
|
+
localLogger.info(`[${ADDON_NAME}] Reading manifest file: ${filename}`);
|
|
1707
|
+
const data = await zip.entryData(filename);
|
|
1708
|
+
localSiteJSON = JSON.parse(data.toString('utf8'));
|
|
1709
|
+
await zip.close();
|
|
1710
|
+
localLogger.info(`[${ADDON_NAME}] Successfully read manifest:`, JSON.stringify(localSiteJSON).substring(0, 200));
|
|
1711
|
+
}
|
|
1712
|
+
catch (zipError) {
|
|
1713
|
+
localLogger.error(`[${ADDON_NAME}] Failed to read blueprint zip: ${zipError.message}`, zipError);
|
|
1714
|
+
return {
|
|
1715
|
+
success: false,
|
|
1716
|
+
error: `Failed to read blueprint manifest: ${zipError.message}`,
|
|
1717
|
+
siteId: null,
|
|
1718
|
+
siteName: name,
|
|
1719
|
+
siteDomain: null,
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
// Build import settings
|
|
1723
|
+
const importSettings = {
|
|
1724
|
+
siteName: name,
|
|
1725
|
+
siteDomain: siteDomain,
|
|
1726
|
+
sitePath: sitePath,
|
|
1727
|
+
zip: blueprintZipPath,
|
|
1728
|
+
importData: {
|
|
1729
|
+
type: 'local-blueprint',
|
|
1730
|
+
oldSite: localSiteJSON,
|
|
1731
|
+
},
|
|
1732
|
+
environment: localSiteJSON.environment || 'flywheel',
|
|
1733
|
+
blueprint: blueprint,
|
|
1734
|
+
};
|
|
1735
|
+
// Copy service versions from blueprint if available
|
|
1736
|
+
if (localSiteJSON.services) {
|
|
1737
|
+
// Extract PHP version
|
|
1738
|
+
const phpService = Object.values(localSiteJSON.services).find((s) => s.role === 'php');
|
|
1739
|
+
if (phpService) {
|
|
1740
|
+
importSettings.phpVersion = phpService.version;
|
|
1741
|
+
}
|
|
1742
|
+
// Extract database
|
|
1743
|
+
const dbService = Object.values(localSiteJSON.services).find((s) => s.role === 'database' || s.role === 'db');
|
|
1744
|
+
if (dbService) {
|
|
1745
|
+
importSettings.database = `${dbService.name}-${dbService.version}`;
|
|
1746
|
+
}
|
|
1747
|
+
// Extract web server
|
|
1748
|
+
const webService = Object.values(localSiteJSON.services).find((s) => s.role === 'http' || s.role === 'web');
|
|
1749
|
+
if (webService) {
|
|
1750
|
+
importSettings.webServer = `${webService.name}-${webService.version}`;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
else if (localSiteJSON.phpVersion) {
|
|
1754
|
+
importSettings.phpVersion = localSiteJSON.phpVersion;
|
|
1755
|
+
}
|
|
1756
|
+
localLogger.info(`[${ADDON_NAME}] Import settings prepared:`, JSON.stringify(importSettings).substring(0, 500));
|
|
1757
|
+
if (!importSiteService) {
|
|
1758
|
+
localLogger.error(`[${ADDON_NAME}] importSiteService is not available!`);
|
|
1759
|
+
return {
|
|
1760
|
+
success: false,
|
|
1761
|
+
error: 'Import service not available',
|
|
1762
|
+
siteId: null,
|
|
1763
|
+
siteName: name,
|
|
1764
|
+
siteDomain: null,
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
localLogger.info(`[${ADDON_NAME}] Calling importSiteService.run()...`);
|
|
1768
|
+
// Use the importSiteService to create from blueprint
|
|
1769
|
+
const importResult = await importSiteService.run(importSettings);
|
|
1770
|
+
localLogger.info(`[${ADDON_NAME}] Import result:`, JSON.stringify(importResult || 'null').substring(0, 500));
|
|
1771
|
+
if (importResult && importResult.id) {
|
|
1772
|
+
localLogger.info(`[${ADDON_NAME}] Successfully created site from blueprint: ${name} (${importResult.id})`);
|
|
1773
|
+
return {
|
|
1774
|
+
success: true,
|
|
1775
|
+
error: null,
|
|
1776
|
+
siteId: importResult.id,
|
|
1777
|
+
siteName: name,
|
|
1778
|
+
siteDomain: siteDomain,
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
else {
|
|
1782
|
+
localLogger.warn(`[${ADDON_NAME}] Import returned but no site ID found`);
|
|
1783
|
+
return {
|
|
1784
|
+
success: true,
|
|
1785
|
+
error: null,
|
|
1786
|
+
siteId: null,
|
|
1787
|
+
siteName: name,
|
|
1788
|
+
siteDomain: siteDomain,
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
// No blueprint - create a fresh site
|
|
1793
|
+
const newSiteInfo = {
|
|
1794
|
+
siteName: name,
|
|
1795
|
+
siteDomain: siteDomain,
|
|
1796
|
+
sitePath: sitePath,
|
|
1797
|
+
webServer: webServer,
|
|
1798
|
+
database: database,
|
|
1799
|
+
};
|
|
1800
|
+
if (phpVersion) {
|
|
1801
|
+
newSiteInfo.phpVersion = phpVersion;
|
|
1802
|
+
}
|
|
1803
|
+
const wpCredentials = {
|
|
1804
|
+
adminUsername: wpAdminUsername,
|
|
1805
|
+
adminPassword: wpAdminPassword,
|
|
1806
|
+
adminEmail: wpAdminEmail,
|
|
1807
|
+
};
|
|
1808
|
+
const site = await addSiteService.addSite({
|
|
1809
|
+
newSiteInfo,
|
|
1810
|
+
wpCredentials,
|
|
1811
|
+
goToSite: false,
|
|
1812
|
+
});
|
|
1813
|
+
localLogger.info(`[${ADDON_NAME}] Successfully created site: ${name} (${site.id})`);
|
|
1814
|
+
return {
|
|
1815
|
+
success: true,
|
|
1816
|
+
error: null,
|
|
1817
|
+
siteId: site.id,
|
|
1818
|
+
siteName: name,
|
|
1819
|
+
siteDomain: siteDomain,
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
catch (error) {
|
|
1823
|
+
localLogger.error(`[${ADDON_NAME}] Failed to create site:`, error);
|
|
1824
|
+
return {
|
|
1825
|
+
success: false,
|
|
1826
|
+
error: error.message || 'Unknown error',
|
|
1827
|
+
siteId: null,
|
|
1828
|
+
siteName: name,
|
|
1829
|
+
siteDomain: null,
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
},
|
|
1833
|
+
deleteSite: async (_parent, args) => {
|
|
1834
|
+
const { id, trashFiles = true, updateHosts = true } = args.input;
|
|
1835
|
+
try {
|
|
1836
|
+
localLogger.info(`[${ADDON_NAME}] Deleting site: ${id}`);
|
|
1837
|
+
const site = siteData.getSite(id);
|
|
1838
|
+
if (!site) {
|
|
1839
|
+
return {
|
|
1840
|
+
success: false,
|
|
1841
|
+
error: `Site not found: ${id}`,
|
|
1842
|
+
siteId: id,
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
await deleteSiteService.deleteSite({
|
|
1846
|
+
site,
|
|
1847
|
+
trashFiles,
|
|
1848
|
+
updateHosts,
|
|
1849
|
+
});
|
|
1850
|
+
localLogger.info(`[${ADDON_NAME}] Successfully deleted site: ${site.name}`);
|
|
1851
|
+
return {
|
|
1852
|
+
success: true,
|
|
1853
|
+
error: null,
|
|
1854
|
+
siteId: id,
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
catch (error) {
|
|
1858
|
+
localLogger.error(`[${ADDON_NAME}] Failed to delete site:`, error);
|
|
1859
|
+
return {
|
|
1860
|
+
success: false,
|
|
1861
|
+
error: error.message || 'Unknown error',
|
|
1862
|
+
siteId: id,
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
},
|
|
1866
|
+
deleteSites: async (_parent, args) => {
|
|
1867
|
+
const { ids, trashFiles = true } = args;
|
|
1868
|
+
try {
|
|
1869
|
+
localLogger.info(`[${ADDON_NAME}] Deleting ${ids.length} sites`);
|
|
1870
|
+
await deleteSiteService.deleteSites({
|
|
1871
|
+
siteIds: ids,
|
|
1872
|
+
trashFiles,
|
|
1873
|
+
updateHosts: true,
|
|
1874
|
+
});
|
|
1875
|
+
localLogger.info(`[${ADDON_NAME}] Successfully deleted ${ids.length} sites`);
|
|
1876
|
+
return {
|
|
1877
|
+
success: true,
|
|
1878
|
+
error: null,
|
|
1879
|
+
siteId: ids.join(','),
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
catch (error) {
|
|
1883
|
+
localLogger.error(`[${ADDON_NAME}] Failed to delete sites:`, error);
|
|
1884
|
+
return {
|
|
1885
|
+
success: false,
|
|
1886
|
+
error: error.message || 'Unknown error',
|
|
1887
|
+
siteId: ids.join(','),
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
},
|
|
1891
|
+
openSite: async (_parent, args) => {
|
|
1892
|
+
const { siteId, path = '/' } = args.input;
|
|
1893
|
+
try {
|
|
1894
|
+
const site = siteData.getSite(siteId);
|
|
1895
|
+
if (!site) {
|
|
1896
|
+
return {
|
|
1897
|
+
success: false,
|
|
1898
|
+
error: `Site not found: ${siteId}`,
|
|
1899
|
+
url: null,
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
// Check if site is running
|
|
1903
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
1904
|
+
if (status !== 'running') {
|
|
1905
|
+
return {
|
|
1906
|
+
success: false,
|
|
1907
|
+
error: `Site "${site.name}" must be running to open in browser. Start it first.`,
|
|
1908
|
+
url: null,
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
const protocol = site.isStarred ? 'https' : 'http';
|
|
1912
|
+
const url = `${protocol}://${site.domain}${path}`;
|
|
1913
|
+
localLogger.info(`[${ADDON_NAME}] Opening site in browser: ${url}`);
|
|
1914
|
+
if (browserManager) {
|
|
1915
|
+
await browserManager.openInBrowser(url);
|
|
1916
|
+
}
|
|
1917
|
+
else {
|
|
1918
|
+
// Fallback to shell.openExternal
|
|
1919
|
+
const { shell } = require('electron');
|
|
1920
|
+
await shell.openExternal(url);
|
|
1921
|
+
}
|
|
1922
|
+
return {
|
|
1923
|
+
success: true,
|
|
1924
|
+
error: null,
|
|
1925
|
+
url,
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
catch (error) {
|
|
1929
|
+
localLogger.error(`[${ADDON_NAME}] Failed to open site:`, error);
|
|
1930
|
+
return {
|
|
1931
|
+
success: false,
|
|
1932
|
+
error: error.message || 'Unknown error',
|
|
1933
|
+
url: null,
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
},
|
|
1937
|
+
cloneSite: async (_parent, args) => {
|
|
1938
|
+
const { siteId, newName } = args.input;
|
|
1939
|
+
try {
|
|
1940
|
+
const site = siteData.getSite(siteId);
|
|
1941
|
+
if (!site) {
|
|
1942
|
+
return {
|
|
1943
|
+
success: false,
|
|
1944
|
+
error: `Site not found: ${siteId}`,
|
|
1945
|
+
newSiteId: null,
|
|
1946
|
+
newSiteName: null,
|
|
1947
|
+
newSiteDomain: null,
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
// Check if site is running - needed for database cloning
|
|
1951
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
1952
|
+
if (status !== 'running') {
|
|
1953
|
+
return {
|
|
1954
|
+
success: false,
|
|
1955
|
+
error: `Site "${site.name}" must be running to clone. Start it first.`,
|
|
1956
|
+
newSiteId: null,
|
|
1957
|
+
newSiteName: null,
|
|
1958
|
+
newSiteDomain: null,
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
localLogger.info(`[${ADDON_NAME}] Cloning site ${site.name} to ${newName}`);
|
|
1962
|
+
const newSite = await cloneSiteService.cloneSite({
|
|
1963
|
+
site,
|
|
1964
|
+
newSiteName: newName,
|
|
1965
|
+
});
|
|
1966
|
+
localLogger.info(`[${ADDON_NAME}] Successfully cloned site: ${newSite.name} (${newSite.id})`);
|
|
1967
|
+
return {
|
|
1968
|
+
success: true,
|
|
1969
|
+
error: null,
|
|
1970
|
+
newSiteId: newSite.id,
|
|
1971
|
+
newSiteName: newSite.name,
|
|
1972
|
+
newSiteDomain: newSite.domain,
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
catch (error) {
|
|
1976
|
+
localLogger.error(`[${ADDON_NAME}] Failed to clone site:`, error);
|
|
1977
|
+
return {
|
|
1978
|
+
success: false,
|
|
1979
|
+
error: error.message || 'Unknown error',
|
|
1980
|
+
newSiteId: null,
|
|
1981
|
+
newSiteName: null,
|
|
1982
|
+
newSiteDomain: null,
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
},
|
|
1986
|
+
exportSite: async (_parent, args) => {
|
|
1987
|
+
const { siteId, outputPath } = args.input;
|
|
1988
|
+
const os = require('os');
|
|
1989
|
+
const path = require('path');
|
|
1990
|
+
try {
|
|
1991
|
+
const site = siteData.getSite(siteId);
|
|
1992
|
+
if (!site) {
|
|
1993
|
+
return {
|
|
1994
|
+
success: false,
|
|
1995
|
+
error: `Site not found: ${siteId}`,
|
|
1996
|
+
exportPath: null,
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
// Check if site is running - needed for database export
|
|
2000
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
2001
|
+
if (status !== 'running') {
|
|
2002
|
+
return {
|
|
2003
|
+
success: false,
|
|
2004
|
+
error: `Site "${site.name}" must be running to export. Start it first.`,
|
|
2005
|
+
exportPath: null,
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
// Default to Downloads folder
|
|
2009
|
+
const outputDir = outputPath || path.join(os.homedir(), 'Downloads');
|
|
2010
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
2011
|
+
const fileName = `${site.name}-${timestamp}.zip`;
|
|
2012
|
+
const fullPath = path.join(outputDir, fileName);
|
|
2013
|
+
localLogger.info(`[${ADDON_NAME}] Exporting site ${site.name} to ${fullPath}`);
|
|
2014
|
+
// Use default export filter (excludes archive files)
|
|
2015
|
+
const defaultExportFilter = '*.zip, *.tar.gz, *.bz2, *.tgz';
|
|
2016
|
+
await exportSiteService.exportSite({
|
|
2017
|
+
site,
|
|
2018
|
+
outputPath: fullPath,
|
|
2019
|
+
filter: defaultExportFilter,
|
|
2020
|
+
});
|
|
2021
|
+
localLogger.info(`[${ADDON_NAME}] Successfully exported site to: ${fullPath}`);
|
|
2022
|
+
return {
|
|
2023
|
+
success: true,
|
|
2024
|
+
error: null,
|
|
2025
|
+
exportPath: fullPath,
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
catch (error) {
|
|
2029
|
+
localLogger.error(`[${ADDON_NAME}] Failed to export site:`, error);
|
|
2030
|
+
return {
|
|
2031
|
+
success: false,
|
|
2032
|
+
error: error.message || 'Unknown error',
|
|
2033
|
+
exportPath: null,
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
},
|
|
2037
|
+
saveBlueprint: async (_parent, args) => {
|
|
2038
|
+
const { siteId, name } = args.input;
|
|
2039
|
+
try {
|
|
2040
|
+
const site = siteData.getSite(siteId);
|
|
2041
|
+
if (!site) {
|
|
2042
|
+
return {
|
|
2043
|
+
success: false,
|
|
2044
|
+
error: `Site not found: ${siteId}`,
|
|
2045
|
+
blueprintName: null,
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
// Check if site is running - needed for database export
|
|
2049
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
2050
|
+
if (status !== 'running') {
|
|
2051
|
+
return {
|
|
2052
|
+
success: false,
|
|
2053
|
+
error: `Site "${site.name}" must be running to save as blueprint. Start it first.`,
|
|
2054
|
+
blueprintName: null,
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
localLogger.info(`[${ADDON_NAME}] Saving site ${site.name} as blueprint: ${name}`);
|
|
2058
|
+
// Use default export filter (excludes archive files)
|
|
2059
|
+
const defaultFilter = '*.zip, *.tar.gz, *.bz2, *.tgz';
|
|
2060
|
+
await blueprintsService.saveBlueprint({
|
|
2061
|
+
name,
|
|
2062
|
+
siteId,
|
|
2063
|
+
filter: defaultFilter,
|
|
2064
|
+
});
|
|
2065
|
+
localLogger.info(`[${ADDON_NAME}] Successfully saved blueprint: ${name}`);
|
|
2066
|
+
return {
|
|
2067
|
+
success: true,
|
|
2068
|
+
error: null,
|
|
2069
|
+
blueprintName: name,
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
catch (error) {
|
|
2073
|
+
localLogger.error(`[${ADDON_NAME}] Failed to save blueprint:`, error);
|
|
2074
|
+
return {
|
|
2075
|
+
success: false,
|
|
2076
|
+
error: error.message || 'Unknown error',
|
|
2077
|
+
blueprintName: null,
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
},
|
|
2081
|
+
// Phase 8: WordPress Development Tools
|
|
2082
|
+
exportDatabase: async (_parent, args) => {
|
|
2083
|
+
const { siteId, outputPath } = args.input;
|
|
2084
|
+
const os = require('os');
|
|
2085
|
+
const pathModule = require('path');
|
|
2086
|
+
try {
|
|
2087
|
+
const site = siteData.getSite(siteId);
|
|
2088
|
+
if (!site) {
|
|
2089
|
+
return {
|
|
2090
|
+
success: false,
|
|
2091
|
+
error: `Site not found: ${siteId}`,
|
|
2092
|
+
outputPath: null,
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
// Check if site is running - database must be accessible
|
|
2096
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
2097
|
+
if (status !== 'running') {
|
|
2098
|
+
return {
|
|
2099
|
+
success: false,
|
|
2100
|
+
error: `Site "${site.name}" must be running to export database. Start it first.`,
|
|
2101
|
+
outputPath: null,
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
// Default to Downloads folder with site name
|
|
2105
|
+
const defaultPath = pathModule.join(os.homedir(), 'Downloads', `${site.name.replace(/[^a-z0-9]/gi, '-')}.sql`);
|
|
2106
|
+
const finalPath = outputPath || defaultPath;
|
|
2107
|
+
localLogger.info(`[${ADDON_NAME}] Exporting database for ${site.name} to ${finalPath}`);
|
|
2108
|
+
// Use siteDatabase.dump() which properly sets up MySQL environment
|
|
2109
|
+
if (!siteDatabase) {
|
|
2110
|
+
return {
|
|
2111
|
+
success: false,
|
|
2112
|
+
error: 'Database service not available',
|
|
2113
|
+
outputPath: null,
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
await siteDatabase.dump(site, finalPath);
|
|
2117
|
+
localLogger.info(`[${ADDON_NAME}] Successfully exported database to: ${finalPath}`);
|
|
2118
|
+
return {
|
|
2119
|
+
success: true,
|
|
2120
|
+
error: null,
|
|
2121
|
+
outputPath: finalPath,
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
catch (error) {
|
|
2125
|
+
localLogger.error(`[${ADDON_NAME}] Failed to export database:`, error);
|
|
2126
|
+
return {
|
|
2127
|
+
success: false,
|
|
2128
|
+
error: error.message || 'Unknown error',
|
|
2129
|
+
outputPath: null,
|
|
2130
|
+
};
|
|
2131
|
+
}
|
|
2132
|
+
},
|
|
2133
|
+
importDatabase: async (_parent, args) => {
|
|
2134
|
+
const { siteId, sqlPath } = args.input;
|
|
2135
|
+
const fs = require('fs');
|
|
2136
|
+
try {
|
|
2137
|
+
const site = siteData.getSite(siteId);
|
|
2138
|
+
if (!site) {
|
|
2139
|
+
return {
|
|
2140
|
+
success: false,
|
|
2141
|
+
error: `Site not found: ${siteId}`,
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
if (!fs.existsSync(sqlPath)) {
|
|
2145
|
+
return {
|
|
2146
|
+
success: false,
|
|
2147
|
+
error: `SQL file not found: ${sqlPath}`,
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
// Check if site is running - database must be accessible
|
|
2151
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
2152
|
+
if (status !== 'running') {
|
|
2153
|
+
return {
|
|
2154
|
+
success: false,
|
|
2155
|
+
error: `Site "${site.name}" must be running to import database. Start it first.`,
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
localLogger.info(`[${ADDON_NAME}] Importing database for ${site.name} from ${sqlPath}`);
|
|
2159
|
+
// Use importSQLFile service which properly sets up MySQL environment
|
|
2160
|
+
if (!importSQLFileService) {
|
|
2161
|
+
return {
|
|
2162
|
+
success: false,
|
|
2163
|
+
error: 'Import SQL file service not available',
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
await importSQLFileService(site, sqlPath);
|
|
2167
|
+
localLogger.info(`[${ADDON_NAME}] Successfully imported database from: ${sqlPath}`);
|
|
2168
|
+
return {
|
|
2169
|
+
success: true,
|
|
2170
|
+
error: null,
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
catch (error) {
|
|
2174
|
+
localLogger.error(`[${ADDON_NAME}] Failed to import database:`, error);
|
|
2175
|
+
return {
|
|
2176
|
+
success: false,
|
|
2177
|
+
error: error.message || 'Unknown error',
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
},
|
|
2181
|
+
openAdminer: async (_parent, args) => {
|
|
2182
|
+
const { siteId } = args.input;
|
|
2183
|
+
try {
|
|
2184
|
+
const site = siteData.getSite(siteId);
|
|
2185
|
+
if (!site) {
|
|
2186
|
+
return {
|
|
2187
|
+
success: false,
|
|
2188
|
+
error: `Site not found: ${siteId}`,
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
// Check if site is running - database must be accessible
|
|
2192
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
2193
|
+
if (status !== 'running') {
|
|
2194
|
+
return {
|
|
2195
|
+
success: false,
|
|
2196
|
+
error: `Site "${site.name}" must be running to open Adminer. Start it first.`,
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
localLogger.info(`[${ADDON_NAME}] Opening Adminer for ${site.name}`);
|
|
2200
|
+
if (adminer) {
|
|
2201
|
+
await adminer.open(site);
|
|
2202
|
+
}
|
|
2203
|
+
else {
|
|
2204
|
+
return {
|
|
2205
|
+
success: false,
|
|
2206
|
+
error: 'Adminer service not available',
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
return {
|
|
2210
|
+
success: true,
|
|
2211
|
+
error: null,
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
catch (error) {
|
|
2215
|
+
localLogger.error(`[${ADDON_NAME}] Failed to open Adminer:`, error);
|
|
2216
|
+
return {
|
|
2217
|
+
success: false,
|
|
2218
|
+
error: error.message || 'Unknown error',
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
},
|
|
2222
|
+
trustSsl: async (_parent, args) => {
|
|
2223
|
+
const { siteId } = args.input;
|
|
2224
|
+
try {
|
|
2225
|
+
const site = siteData.getSite(siteId);
|
|
2226
|
+
if (!site) {
|
|
2227
|
+
return {
|
|
2228
|
+
success: false,
|
|
2229
|
+
error: `Site not found: ${siteId}`,
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
localLogger.info(`[${ADDON_NAME}] Trusting SSL for ${site.name}`);
|
|
2233
|
+
if (x509Cert) {
|
|
2234
|
+
await x509Cert.trustCert(site);
|
|
2235
|
+
}
|
|
2236
|
+
else {
|
|
2237
|
+
return {
|
|
2238
|
+
success: false,
|
|
2239
|
+
error: 'X509 certificate service not available',
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
return {
|
|
2243
|
+
success: true,
|
|
2244
|
+
error: null,
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
catch (error) {
|
|
2248
|
+
localLogger.error(`[${ADDON_NAME}] Failed to trust SSL:`, error);
|
|
2249
|
+
return {
|
|
2250
|
+
success: false,
|
|
2251
|
+
error: error.message || 'Unknown error',
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
},
|
|
2255
|
+
mcpRenameSite: async (_parent, args) => {
|
|
2256
|
+
const { siteId, newName } = args.input;
|
|
2257
|
+
try {
|
|
2258
|
+
const site = siteData.getSite(siteId);
|
|
2259
|
+
if (!site) {
|
|
2260
|
+
return {
|
|
2261
|
+
success: false,
|
|
2262
|
+
error: `Site not found: ${siteId}`,
|
|
2263
|
+
newName: null,
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
localLogger.info(`[${ADDON_NAME}] Renaming ${site.name} to ${newName}`);
|
|
2267
|
+
// Update site name via siteData
|
|
2268
|
+
site.name = newName;
|
|
2269
|
+
await siteData.updateSite(siteId, { name: newName });
|
|
2270
|
+
localLogger.info(`[${ADDON_NAME}] Successfully renamed site to: ${newName}`);
|
|
2271
|
+
return {
|
|
2272
|
+
success: true,
|
|
2273
|
+
error: null,
|
|
2274
|
+
newName,
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2277
|
+
catch (error) {
|
|
2278
|
+
localLogger.error(`[${ADDON_NAME}] Failed to rename site:`, error);
|
|
2279
|
+
return {
|
|
2280
|
+
success: false,
|
|
2281
|
+
error: error.message || 'Unknown error',
|
|
2282
|
+
newName: null,
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
},
|
|
2286
|
+
changePhpVersion: async (_parent, args) => {
|
|
2287
|
+
const { siteId, phpVersion } = args.input;
|
|
2288
|
+
try {
|
|
2289
|
+
const site = siteData.getSite(siteId);
|
|
2290
|
+
if (!site) {
|
|
2291
|
+
return {
|
|
2292
|
+
success: false,
|
|
2293
|
+
error: `Site not found: ${siteId}`,
|
|
2294
|
+
phpVersion: null,
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
localLogger.info(`[${ADDON_NAME}] Changing PHP version for ${site.name} to ${phpVersion}`);
|
|
2298
|
+
if (siteProvisioner) {
|
|
2299
|
+
await siteProvisioner.swapService(site, 'php', phpVersion);
|
|
2300
|
+
}
|
|
2301
|
+
else {
|
|
2302
|
+
return {
|
|
2303
|
+
success: false,
|
|
2304
|
+
error: 'Site provisioner service not available',
|
|
2305
|
+
phpVersion: null,
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
localLogger.info(`[${ADDON_NAME}] Successfully changed PHP version to: ${phpVersion}`);
|
|
2309
|
+
return {
|
|
2310
|
+
success: true,
|
|
2311
|
+
error: null,
|
|
2312
|
+
phpVersion,
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2315
|
+
catch (error) {
|
|
2316
|
+
localLogger.error(`[${ADDON_NAME}] Failed to change PHP version:`, error);
|
|
2317
|
+
return {
|
|
2318
|
+
success: false,
|
|
2319
|
+
error: error.message || 'Unknown error',
|
|
2320
|
+
phpVersion: null,
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
},
|
|
2324
|
+
importSite: async (_parent, args) => {
|
|
2325
|
+
const { zipPath, siteName } = args.input;
|
|
2326
|
+
const fs = require('fs');
|
|
2327
|
+
try {
|
|
2328
|
+
if (!fs.existsSync(zipPath)) {
|
|
2329
|
+
return {
|
|
2330
|
+
success: false,
|
|
2331
|
+
error: `Zip file not found: ${zipPath}`,
|
|
2332
|
+
siteId: null,
|
|
2333
|
+
siteName: null,
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
localLogger.info(`[${ADDON_NAME}] Importing site from ${zipPath}`);
|
|
2337
|
+
if (!importSiteService) {
|
|
2338
|
+
return {
|
|
2339
|
+
success: false,
|
|
2340
|
+
error: 'Import site service not available',
|
|
2341
|
+
siteId: null,
|
|
2342
|
+
siteName: null,
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
const result = await importSiteService.run({
|
|
2346
|
+
zipPath,
|
|
2347
|
+
siteName: siteName || undefined,
|
|
2348
|
+
});
|
|
2349
|
+
localLogger.info(`[${ADDON_NAME}] Successfully imported site: ${result.name}`);
|
|
2350
|
+
return {
|
|
2351
|
+
success: true,
|
|
2352
|
+
error: null,
|
|
2353
|
+
siteId: result.id,
|
|
2354
|
+
siteName: result.name,
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
catch (error) {
|
|
2358
|
+
localLogger.error(`[${ADDON_NAME}] Failed to import site:`, error);
|
|
2359
|
+
return {
|
|
2360
|
+
success: false,
|
|
2361
|
+
error: error.message || 'Unknown error',
|
|
2362
|
+
siteId: null,
|
|
2363
|
+
siteName: null,
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
},
|
|
2367
|
+
// Phase 9: Site Configuration & Dev Tools
|
|
2368
|
+
toggleXdebug: async (_parent, args) => {
|
|
2369
|
+
const { siteId, enabled } = args.input;
|
|
2370
|
+
try {
|
|
2371
|
+
const site = siteData.getSite(siteId);
|
|
2372
|
+
if (!site) {
|
|
2373
|
+
return {
|
|
2374
|
+
success: false,
|
|
2375
|
+
error: `Site not found: ${siteId}`,
|
|
2376
|
+
enabled: null,
|
|
2377
|
+
};
|
|
2378
|
+
}
|
|
2379
|
+
localLogger.info(`[${ADDON_NAME}] ${enabled ? 'Enabling' : 'Disabling'} Xdebug for ${site.name}`);
|
|
2380
|
+
// Update the site's xdebugEnabled property
|
|
2381
|
+
await siteData.updateSite(siteId, { xdebugEnabled: enabled });
|
|
2382
|
+
// Restart the site if it's running to apply the change
|
|
2383
|
+
const status = await siteProcessManager.getSiteStatus(site);
|
|
2384
|
+
if (status === 'running') {
|
|
2385
|
+
localLogger.info(`[${ADDON_NAME}] Restarting site to apply Xdebug change`);
|
|
2386
|
+
await siteProcessManager.restart(site);
|
|
2387
|
+
}
|
|
2388
|
+
localLogger.info(`[${ADDON_NAME}] Successfully ${enabled ? 'enabled' : 'disabled'} Xdebug`);
|
|
2389
|
+
return {
|
|
2390
|
+
success: true,
|
|
2391
|
+
error: null,
|
|
2392
|
+
enabled,
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
catch (error) {
|
|
2396
|
+
localLogger.error(`[${ADDON_NAME}] Failed to toggle Xdebug:`, error);
|
|
2397
|
+
return {
|
|
2398
|
+
success: false,
|
|
2399
|
+
error: error.message || 'Unknown error',
|
|
2400
|
+
enabled: null,
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
},
|
|
2404
|
+
getSiteLogs: async (_parent, args) => {
|
|
2405
|
+
const { siteId, logType = 'php', lines = 100 } = args.input;
|
|
2406
|
+
const fs = require('fs');
|
|
2407
|
+
const fsPromises = fs.promises;
|
|
2408
|
+
const pathModule = require('path');
|
|
2409
|
+
// Helper for async file existence check
|
|
2410
|
+
const fileExists = async (filePath) => {
|
|
2411
|
+
try {
|
|
2412
|
+
await fsPromises.access(filePath);
|
|
2413
|
+
return true;
|
|
2414
|
+
}
|
|
2415
|
+
catch {
|
|
2416
|
+
return false;
|
|
2417
|
+
}
|
|
2418
|
+
};
|
|
2419
|
+
// Helper to read last N lines of a file
|
|
2420
|
+
const readLastLines = async (filePath, numLines) => {
|
|
2421
|
+
try {
|
|
2422
|
+
const content = await fsPromises.readFile(filePath, 'utf-8');
|
|
2423
|
+
const logLines = content.split('\n');
|
|
2424
|
+
return logLines.slice(-numLines).join('\n') || '(empty)';
|
|
2425
|
+
}
|
|
2426
|
+
catch {
|
|
2427
|
+
return '';
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2430
|
+
try {
|
|
2431
|
+
const site = siteData.getSite(siteId);
|
|
2432
|
+
if (!site) {
|
|
2433
|
+
return {
|
|
2434
|
+
success: false,
|
|
2435
|
+
error: `Site not found: ${siteId}`,
|
|
2436
|
+
logs: [],
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
localLogger.info(`[${ADDON_NAME}] Getting ${logType} logs for ${site.name}`);
|
|
2440
|
+
const logs = [];
|
|
2441
|
+
const logsDir = pathModule.join(site.path, 'logs');
|
|
2442
|
+
const logFiles = {
|
|
2443
|
+
php: ['php', 'php-fpm'],
|
|
2444
|
+
nginx: ['nginx'],
|
|
2445
|
+
mysql: ['mysql'],
|
|
2446
|
+
all: ['php', 'php-fpm', 'nginx', 'mysql'],
|
|
2447
|
+
};
|
|
2448
|
+
const targetLogs = logFiles[logType] || logFiles.php;
|
|
2449
|
+
for (const logName of targetLogs) {
|
|
2450
|
+
// Check for error and access logs
|
|
2451
|
+
for (const suffix of ['error.log', 'access.log', '.log']) {
|
|
2452
|
+
const logPath = pathModule.join(logsDir, `${logName}${suffix === '.log' ? '' : '/'}${suffix}`);
|
|
2453
|
+
const altLogPath = pathModule.join(logsDir, `${logName}${suffix}`);
|
|
2454
|
+
let finalPath = null;
|
|
2455
|
+
if (await fileExists(logPath)) {
|
|
2456
|
+
finalPath = logPath;
|
|
2457
|
+
}
|
|
2458
|
+
else if (await fileExists(altLogPath)) {
|
|
2459
|
+
finalPath = altLogPath;
|
|
2460
|
+
}
|
|
2461
|
+
if (finalPath) {
|
|
2462
|
+
const content = await readLastLines(finalPath, lines);
|
|
2463
|
+
if (content) {
|
|
2464
|
+
logs.push({
|
|
2465
|
+
type: logName,
|
|
2466
|
+
content,
|
|
2467
|
+
path: finalPath,
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
if (logs.length === 0) {
|
|
2474
|
+
// Try to find any log files
|
|
2475
|
+
if (await fileExists(logsDir)) {
|
|
2476
|
+
const entries = await fsPromises.readdir(logsDir, { withFileTypes: true });
|
|
2477
|
+
for (const entry of entries) {
|
|
2478
|
+
if (entry.isDirectory()) {
|
|
2479
|
+
const subDir = pathModule.join(logsDir, entry.name);
|
|
2480
|
+
const subEntries = await fsPromises.readdir(subDir);
|
|
2481
|
+
for (const subFile of subEntries) {
|
|
2482
|
+
if (subFile.endsWith('.log')) {
|
|
2483
|
+
const logPath = pathModule.join(subDir, subFile);
|
|
2484
|
+
const content = await readLastLines(logPath, lines);
|
|
2485
|
+
if (content) {
|
|
2486
|
+
logs.push({
|
|
2487
|
+
type: entry.name,
|
|
2488
|
+
content,
|
|
2489
|
+
path: logPath,
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
else if (entry.name.endsWith('.log')) {
|
|
2496
|
+
const logPath = pathModule.join(logsDir, entry.name);
|
|
2497
|
+
const content = await readLastLines(logPath, lines);
|
|
2498
|
+
if (content) {
|
|
2499
|
+
logs.push({
|
|
2500
|
+
type: entry.name.replace('.log', ''),
|
|
2501
|
+
content,
|
|
2502
|
+
path: logPath,
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
return {
|
|
2510
|
+
success: true,
|
|
2511
|
+
error: null,
|
|
2512
|
+
logs,
|
|
2513
|
+
};
|
|
2514
|
+
}
|
|
2515
|
+
catch (error) {
|
|
2516
|
+
localLogger.error(`[${ADDON_NAME}] Failed to get logs:`, error);
|
|
2517
|
+
return {
|
|
2518
|
+
success: false,
|
|
2519
|
+
error: error.message || 'Unknown error',
|
|
2520
|
+
logs: [],
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
},
|
|
2524
|
+
// Phase 10: Cloud Backup Mutations
|
|
2525
|
+
createBackup: async (_parent, args) => {
|
|
2526
|
+
const { siteId, provider, note } = args;
|
|
2527
|
+
try {
|
|
2528
|
+
localLogger.info(`[${ADDON_NAME}] Creating backup for site ${siteId} to ${provider}`);
|
|
2529
|
+
// Get site
|
|
2530
|
+
const site = siteData.getSite(siteId);
|
|
2531
|
+
if (!site) {
|
|
2532
|
+
return {
|
|
2533
|
+
success: false,
|
|
2534
|
+
snapshotId: null,
|
|
2535
|
+
timestamp: null,
|
|
2536
|
+
message: null,
|
|
2537
|
+
error: `Site not found: ${siteId}`,
|
|
2538
|
+
};
|
|
2539
|
+
}
|
|
2540
|
+
// Get providers from Cloud Backups addon
|
|
2541
|
+
const providers = await getBackupProviders();
|
|
2542
|
+
if (providers.length === 0) {
|
|
2543
|
+
return {
|
|
2544
|
+
success: false,
|
|
2545
|
+
snapshotId: null,
|
|
2546
|
+
timestamp: null,
|
|
2547
|
+
message: null,
|
|
2548
|
+
error: 'No cloud storage providers configured. Connect Google Drive or Dropbox in Local Hub.',
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
// Find the matching provider (map 'googleDrive' to 'google' for the addon)
|
|
2552
|
+
const providerMap = { googleDrive: 'google', dropbox: 'dropbox' };
|
|
2553
|
+
const providerId = providerMap[provider] || provider;
|
|
2554
|
+
const matchedProvider = providers.find((p) => p.id === providerId);
|
|
2555
|
+
if (!matchedProvider) {
|
|
2556
|
+
return {
|
|
2557
|
+
success: false,
|
|
2558
|
+
snapshotId: null,
|
|
2559
|
+
timestamp: null,
|
|
2560
|
+
message: null,
|
|
2561
|
+
error: `Provider '${provider}' not configured. Available: ${providers.map((p) => p.name).join(', ')}`,
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
// Map the Hub provider ID to rclone backend name
|
|
2565
|
+
// The addon uses 'google' in enabled-providers but expects 'drive' for backup operations
|
|
2566
|
+
const backupProviderMap = { google: 'drive', dropbox: 'dropbox' };
|
|
2567
|
+
const backupProviderId = backupProviderMap[matchedProvider.id] || matchedProvider.id;
|
|
2568
|
+
localLogger.info(`[${ADDON_NAME}] Using backup provider ID: ${backupProviderId} (from ${matchedProvider.id})`);
|
|
2569
|
+
// Create backup via IPC (use long timeout for backup operations)
|
|
2570
|
+
const description = note || 'Backup created via MCP';
|
|
2571
|
+
const result = await invokeBackupIPC('backups:backup-site', BACKUP_IPC_TIMEOUT, siteId, backupProviderId, description);
|
|
2572
|
+
localLogger.info(`[${ADDON_NAME}] Backup IPC result: ${JSON.stringify(result)}`);
|
|
2573
|
+
// Check for top-level IPC error
|
|
2574
|
+
if (result.error) {
|
|
2575
|
+
return {
|
|
2576
|
+
success: false,
|
|
2577
|
+
snapshotId: null,
|
|
2578
|
+
timestamp: null,
|
|
2579
|
+
message: null,
|
|
2580
|
+
error: result.error.message || 'Backup creation failed',
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
// Unwrap nested result structure - the actual result is at result.result
|
|
2584
|
+
const backupResult = result.result;
|
|
2585
|
+
// Check if the backup result contains an error (nested at result.result.error)
|
|
2586
|
+
if (backupResult?.error) {
|
|
2587
|
+
const errorMsg = backupResult.error.message || backupResult.error.original?.message || 'Backup failed';
|
|
2588
|
+
return {
|
|
2589
|
+
success: false,
|
|
2590
|
+
snapshotId: null,
|
|
2591
|
+
timestamp: null,
|
|
2592
|
+
message: null,
|
|
2593
|
+
error: errorMsg,
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
// Try to extract snapshot ID (may be nested in result.result.result)
|
|
2597
|
+
let snapshotId = backupResult?.snapshotId || backupResult?.id;
|
|
2598
|
+
if (!snapshotId && backupResult?.result) {
|
|
2599
|
+
snapshotId = backupResult.result.snapshotId || backupResult.result.id;
|
|
2600
|
+
}
|
|
2601
|
+
// If no error was returned, the backup succeeded even if no snapshot ID is provided
|
|
2602
|
+
// The addon doesn't always return the snapshot ID in its IPC response
|
|
2603
|
+
return {
|
|
2604
|
+
success: true,
|
|
2605
|
+
snapshotId: snapshotId || null,
|
|
2606
|
+
timestamp: new Date().toISOString(),
|
|
2607
|
+
message: `Backup created successfully to ${matchedProvider.name}`,
|
|
2608
|
+
error: null,
|
|
2609
|
+
};
|
|
2610
|
+
}
|
|
2611
|
+
catch (error) {
|
|
2612
|
+
localLogger.error(`[${ADDON_NAME}] Failed to create backup:`, error);
|
|
2613
|
+
return {
|
|
2614
|
+
success: false,
|
|
2615
|
+
snapshotId: null,
|
|
2616
|
+
timestamp: null,
|
|
2617
|
+
message: null,
|
|
2618
|
+
error: error.message || 'Unknown error',
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
},
|
|
2622
|
+
restoreBackup: async (_parent, args) => {
|
|
2623
|
+
const { siteId, provider, snapshotId, confirm = false } = args;
|
|
2624
|
+
try {
|
|
2625
|
+
localLogger.info(`[${ADDON_NAME}] Restoring backup ${snapshotId} for site ${siteId}`);
|
|
2626
|
+
// Check confirmation
|
|
2627
|
+
if (!confirm) {
|
|
2628
|
+
return {
|
|
2629
|
+
success: false,
|
|
2630
|
+
message: null,
|
|
2631
|
+
error: 'Restore requires confirm=true to prevent accidental data loss. Current site files and database will be overwritten.',
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
// Get site
|
|
2635
|
+
const site = siteData.getSite(siteId);
|
|
2636
|
+
if (!site) {
|
|
2637
|
+
return {
|
|
2638
|
+
success: false,
|
|
2639
|
+
message: null,
|
|
2640
|
+
error: `Site not found: ${siteId}`,
|
|
2641
|
+
};
|
|
2642
|
+
}
|
|
2643
|
+
// Get providers from Cloud Backups addon
|
|
2644
|
+
const providers = await getBackupProviders();
|
|
2645
|
+
if (providers.length === 0) {
|
|
2646
|
+
return {
|
|
2647
|
+
success: false,
|
|
2648
|
+
message: null,
|
|
2649
|
+
error: 'No cloud storage providers configured. Connect Google Drive or Dropbox in Local Hub.',
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
// Find the matching provider (map 'googleDrive' to 'google' for the addon)
|
|
2653
|
+
const providerMap = { googleDrive: 'google', dropbox: 'dropbox' };
|
|
2654
|
+
const providerId = providerMap[provider] || provider;
|
|
2655
|
+
const matchedProvider = providers.find((p) => p.id === providerId);
|
|
2656
|
+
if (!matchedProvider) {
|
|
2657
|
+
return {
|
|
2658
|
+
success: false,
|
|
2659
|
+
message: null,
|
|
2660
|
+
error: `Provider '${provider}' not configured. Available: ${providers.map((p) => p.name).join(', ')}`,
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
// Map the Hub provider ID to rclone backend name
|
|
2664
|
+
const backupProviderMap = { google: 'drive', dropbox: 'dropbox' };
|
|
2665
|
+
const backupProviderId = backupProviderMap[matchedProvider.id] || matchedProvider.id;
|
|
2666
|
+
// Restore backup via IPC (use long timeout for restore operations)
|
|
2667
|
+
const result = await invokeBackupIPC('backups:restore-backup', BACKUP_IPC_TIMEOUT, siteId, backupProviderId, snapshotId);
|
|
2668
|
+
localLogger.info(`[${ADDON_NAME}] Restore result: ${JSON.stringify(result)}`);
|
|
2669
|
+
// Check for errors - can be at result.error or result.result.error (IPC async pattern)
|
|
2670
|
+
const ipcError = result.error || result.result?.error;
|
|
2671
|
+
if (ipcError) {
|
|
2672
|
+
const errorMessage = typeof ipcError === 'string' ? ipcError : ipcError.message || 'Restore failed';
|
|
2673
|
+
return {
|
|
2674
|
+
success: false,
|
|
2675
|
+
message: null,
|
|
2676
|
+
error: errorMessage,
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
return {
|
|
2680
|
+
success: true,
|
|
2681
|
+
message: `Site restored from backup ${snapshotId}`,
|
|
2682
|
+
error: null,
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2685
|
+
catch (error) {
|
|
2686
|
+
localLogger.error(`[${ADDON_NAME}] Failed to restore backup:`, error);
|
|
2687
|
+
return {
|
|
2688
|
+
success: false,
|
|
2689
|
+
message: null,
|
|
2690
|
+
error: error.message || 'Unknown error',
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
},
|
|
2694
|
+
deleteBackup: async (_parent, args) => {
|
|
2695
|
+
const { siteId, provider, snapshotId, confirm = false } = args;
|
|
2696
|
+
try {
|
|
2697
|
+
localLogger.info(`[${ADDON_NAME}] Deleting backup ${snapshotId} for site ${siteId}`);
|
|
2698
|
+
// Check confirmation
|
|
2699
|
+
if (!confirm) {
|
|
2700
|
+
return {
|
|
2701
|
+
success: false,
|
|
2702
|
+
deletedSnapshotId: null,
|
|
2703
|
+
message: null,
|
|
2704
|
+
error: 'Delete requires confirm=true to prevent accidental deletion.',
|
|
2705
|
+
};
|
|
2706
|
+
}
|
|
2707
|
+
// Get site
|
|
2708
|
+
const site = siteData.getSite(siteId);
|
|
2709
|
+
if (!site) {
|
|
2710
|
+
return {
|
|
2711
|
+
success: false,
|
|
2712
|
+
deletedSnapshotId: null,
|
|
2713
|
+
message: null,
|
|
2714
|
+
error: `Site not found: ${siteId}`,
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
// Get providers from Cloud Backups addon
|
|
2718
|
+
const providers = await getBackupProviders();
|
|
2719
|
+
if (providers.length === 0) {
|
|
2720
|
+
return {
|
|
2721
|
+
success: false,
|
|
2722
|
+
deletedSnapshotId: null,
|
|
2723
|
+
message: null,
|
|
2724
|
+
error: 'No cloud storage providers configured. Connect Google Drive or Dropbox in Local Hub.',
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
// Find the matching provider (map 'googleDrive' to 'google' for the addon)
|
|
2728
|
+
const providerMap = { googleDrive: 'google', dropbox: 'dropbox' };
|
|
2729
|
+
const providerId = providerMap[provider] || provider;
|
|
2730
|
+
const matchedProvider = providers.find((p) => p.id === providerId);
|
|
2731
|
+
if (!matchedProvider) {
|
|
2732
|
+
return {
|
|
2733
|
+
success: false,
|
|
2734
|
+
deletedSnapshotId: null,
|
|
2735
|
+
message: null,
|
|
2736
|
+
error: `Provider '${provider}' not configured. Available: ${providers.map((p) => p.name).join(', ')}`,
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
// Map the Hub provider ID to rclone backend name
|
|
2740
|
+
const backupProviderMap = { google: 'drive', dropbox: 'dropbox' };
|
|
2741
|
+
const backupProviderId = backupProviderMap[matchedProvider.id] || matchedProvider.id;
|
|
2742
|
+
// Try to delete backup via IPC (may not be supported by the addon)
|
|
2743
|
+
const result = await invokeBackupIPC('backups:delete-backup', DEFAULT_IPC_TIMEOUT, siteId, backupProviderId, snapshotId);
|
|
2744
|
+
if (result.error) {
|
|
2745
|
+
// If the IPC channel doesn't exist or isn't supported, provide helpful message
|
|
2746
|
+
if (result.error.message?.includes('timed out')) {
|
|
2747
|
+
return {
|
|
2748
|
+
success: false,
|
|
2749
|
+
deletedSnapshotId: null,
|
|
2750
|
+
message: null,
|
|
2751
|
+
error: 'Delete backup operation is not available via MCP. Please delete backups through the Local UI.',
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
return {
|
|
2755
|
+
success: false,
|
|
2756
|
+
deletedSnapshotId: null,
|
|
2757
|
+
message: null,
|
|
2758
|
+
error: result.error.message || 'Delete failed',
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
return {
|
|
2762
|
+
success: true,
|
|
2763
|
+
deletedSnapshotId: snapshotId,
|
|
2764
|
+
message: 'Backup deleted',
|
|
2765
|
+
error: null,
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
catch (error) {
|
|
2769
|
+
localLogger.error(`[${ADDON_NAME}] Failed to delete backup:`, error);
|
|
2770
|
+
return {
|
|
2771
|
+
success: false,
|
|
2772
|
+
deletedSnapshotId: null,
|
|
2773
|
+
message: null,
|
|
2774
|
+
error: error.message || 'Unknown error',
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
},
|
|
2778
|
+
downloadBackup: async (_parent, args) => {
|
|
2779
|
+
const { siteId, provider, snapshotId } = args;
|
|
2780
|
+
try {
|
|
2781
|
+
localLogger.info(`[${ADDON_NAME}] Downloading backup ${snapshotId} for site ${siteId}`);
|
|
2782
|
+
// Get site
|
|
2783
|
+
const site = siteData.getSite(siteId);
|
|
2784
|
+
if (!site) {
|
|
2785
|
+
return {
|
|
2786
|
+
success: false,
|
|
2787
|
+
filePath: null,
|
|
2788
|
+
message: null,
|
|
2789
|
+
error: `Site not found: ${siteId}`,
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
// Get providers from Cloud Backups addon
|
|
2793
|
+
const providers = await getBackupProviders();
|
|
2794
|
+
if (providers.length === 0) {
|
|
2795
|
+
return {
|
|
2796
|
+
success: false,
|
|
2797
|
+
filePath: null,
|
|
2798
|
+
message: null,
|
|
2799
|
+
error: 'No cloud storage providers configured. Connect Google Drive or Dropbox in Local Hub.',
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
// Find the matching provider (map 'googleDrive' to 'google' for the addon)
|
|
2803
|
+
const providerMap = { googleDrive: 'google', dropbox: 'dropbox' };
|
|
2804
|
+
const providerId = providerMap[provider] || provider;
|
|
2805
|
+
const matchedProvider = providers.find((p) => p.id === providerId);
|
|
2806
|
+
if (!matchedProvider) {
|
|
2807
|
+
return {
|
|
2808
|
+
success: false,
|
|
2809
|
+
filePath: null,
|
|
2810
|
+
message: null,
|
|
2811
|
+
error: `Provider '${provider}' not configured. Available: ${providers.map((p) => p.name).join(', ')}`,
|
|
2812
|
+
};
|
|
2813
|
+
}
|
|
2814
|
+
// Map the Hub provider ID to rclone backend name
|
|
2815
|
+
const backupProviderMap = { google: 'drive', dropbox: 'dropbox' };
|
|
2816
|
+
const backupProviderId = backupProviderMap[matchedProvider.id] || matchedProvider.id;
|
|
2817
|
+
// Try to download backup via IPC (use long timeout for downloads)
|
|
2818
|
+
const result = await invokeBackupIPC('backups:download-backup', BACKUP_IPC_TIMEOUT, siteId, backupProviderId, snapshotId);
|
|
2819
|
+
if (result.error) {
|
|
2820
|
+
// If the IPC channel doesn't exist or isn't supported, provide helpful message
|
|
2821
|
+
if (result.error.message?.includes('timed out')) {
|
|
2822
|
+
return {
|
|
2823
|
+
success: false,
|
|
2824
|
+
filePath: null,
|
|
2825
|
+
message: null,
|
|
2826
|
+
error: 'Download backup operation is not available via MCP. Please download backups through the Local UI.',
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
return {
|
|
2830
|
+
success: false,
|
|
2831
|
+
filePath: null,
|
|
2832
|
+
message: null,
|
|
2833
|
+
error: result.error.message || 'Download failed',
|
|
2834
|
+
};
|
|
2835
|
+
}
|
|
2836
|
+
return {
|
|
2837
|
+
success: true,
|
|
2838
|
+
filePath: result.result?.filePath || null,
|
|
2839
|
+
message: 'Backup downloaded to Downloads folder',
|
|
2840
|
+
error: null,
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
catch (error) {
|
|
2844
|
+
localLogger.error(`[${ADDON_NAME}] Failed to download backup:`, error);
|
|
2845
|
+
return {
|
|
2846
|
+
success: false,
|
|
2847
|
+
filePath: null,
|
|
2848
|
+
message: null,
|
|
2849
|
+
error: error.message || 'Unknown error',
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
},
|
|
2853
|
+
editBackupNote: async (_parent, args) => {
|
|
2854
|
+
const { siteId, provider, snapshotId, note } = args;
|
|
2855
|
+
try {
|
|
2856
|
+
localLogger.info(`[${ADDON_NAME}] Editing backup note for ${snapshotId}`);
|
|
2857
|
+
// Get site
|
|
2858
|
+
const site = siteData.getSite(siteId);
|
|
2859
|
+
if (!site) {
|
|
2860
|
+
return {
|
|
2861
|
+
success: false,
|
|
2862
|
+
snapshotId: null,
|
|
2863
|
+
note: null,
|
|
2864
|
+
error: `Site not found: ${siteId}`,
|
|
2865
|
+
};
|
|
2866
|
+
}
|
|
2867
|
+
// Get providers from Cloud Backups addon
|
|
2868
|
+
const providers = await getBackupProviders();
|
|
2869
|
+
if (providers.length === 0) {
|
|
2870
|
+
return {
|
|
2871
|
+
success: false,
|
|
2872
|
+
snapshotId: null,
|
|
2873
|
+
note: null,
|
|
2874
|
+
error: 'No cloud storage providers configured. Connect Google Drive or Dropbox in Local Hub.',
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2877
|
+
// Find the matching provider (map 'googleDrive' to 'google' for the addon)
|
|
2878
|
+
const providerMap = { googleDrive: 'google', dropbox: 'dropbox' };
|
|
2879
|
+
const providerId = providerMap[provider] || provider;
|
|
2880
|
+
const matchedProvider = providers.find((p) => p.id === providerId);
|
|
2881
|
+
if (!matchedProvider) {
|
|
2882
|
+
return {
|
|
2883
|
+
success: false,
|
|
2884
|
+
snapshotId: null,
|
|
2885
|
+
note: null,
|
|
2886
|
+
error: `Provider '${provider}' not configured. Available: ${providers.map((p) => p.name).join(', ')}`,
|
|
2887
|
+
};
|
|
2888
|
+
}
|
|
2889
|
+
// Map the Hub provider ID to rclone backend name
|
|
2890
|
+
const backupProviderMap = { google: 'drive', dropbox: 'dropbox' };
|
|
2891
|
+
const backupProviderId = backupProviderMap[matchedProvider.id] || matchedProvider.id;
|
|
2892
|
+
// Try to edit backup note via IPC (quick metadata operation)
|
|
2893
|
+
const result = await invokeBackupIPC('backups:edit-note', DEFAULT_IPC_TIMEOUT, siteId, backupProviderId, snapshotId, note);
|
|
2894
|
+
if (result.error) {
|
|
2895
|
+
// If the IPC channel doesn't exist or isn't supported, provide helpful message
|
|
2896
|
+
if (result.error.message?.includes('timed out')) {
|
|
2897
|
+
return {
|
|
2898
|
+
success: false,
|
|
2899
|
+
snapshotId: null,
|
|
2900
|
+
note: null,
|
|
2901
|
+
error: 'Edit backup note operation is not available via MCP. Please edit backup notes through the Local UI.',
|
|
2902
|
+
};
|
|
2903
|
+
}
|
|
2904
|
+
return {
|
|
2905
|
+
success: false,
|
|
2906
|
+
snapshotId: null,
|
|
2907
|
+
note: null,
|
|
2908
|
+
error: result.error.message || 'Edit note failed',
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2911
|
+
return {
|
|
2912
|
+
success: true,
|
|
2913
|
+
snapshotId,
|
|
2914
|
+
note,
|
|
2915
|
+
error: null,
|
|
2916
|
+
};
|
|
2917
|
+
}
|
|
2918
|
+
catch (error) {
|
|
2919
|
+
localLogger.error(`[${ADDON_NAME}] Failed to edit backup note:`, error);
|
|
2920
|
+
return {
|
|
2921
|
+
success: false,
|
|
2922
|
+
snapshotId: null,
|
|
2923
|
+
note: null,
|
|
2924
|
+
error: error.message || 'Unknown error',
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
},
|
|
2928
|
+
// Phase 11: WP Engine Connect
|
|
2929
|
+
wpeAuthenticate: async () => {
|
|
2930
|
+
try {
|
|
2931
|
+
localLogger.info(`[${ADDON_NAME}] Initiating WP Engine authentication`);
|
|
2932
|
+
if (!wpeOAuthService) {
|
|
2933
|
+
return {
|
|
2934
|
+
success: false,
|
|
2935
|
+
email: null,
|
|
2936
|
+
message: null,
|
|
2937
|
+
error: 'WP Engine OAuth service not available',
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
// Trigger OAuth flow - this will open browser for user consent
|
|
2941
|
+
// authenticate() returns OAuthTokens on success
|
|
2942
|
+
const tokens = await wpeOAuthService.authenticate();
|
|
2943
|
+
if (tokens && tokens.accessToken) {
|
|
2944
|
+
// Try to get user email from CAPI
|
|
2945
|
+
let email = null;
|
|
2946
|
+
if (capiService) {
|
|
2947
|
+
try {
|
|
2948
|
+
const currentUser = await capiService.getCurrentUser();
|
|
2949
|
+
email = currentUser?.email || null;
|
|
2950
|
+
}
|
|
2951
|
+
catch {
|
|
2952
|
+
// User info not available
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
localLogger.info(`[${ADDON_NAME}] Successfully authenticated with WPE${email ? ` as ${email}` : ''}`);
|
|
2956
|
+
return {
|
|
2957
|
+
success: true,
|
|
2958
|
+
email,
|
|
2959
|
+
message: 'Successfully authenticated with WP Engine',
|
|
2960
|
+
error: null,
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
return {
|
|
2964
|
+
success: true,
|
|
2965
|
+
email: null,
|
|
2966
|
+
message: 'Authentication initiated. Please complete the login in your browser.',
|
|
2967
|
+
error: null,
|
|
2968
|
+
};
|
|
2969
|
+
}
|
|
2970
|
+
catch (error) {
|
|
2971
|
+
localLogger.error(`[${ADDON_NAME}] WPE authentication failed:`, error);
|
|
2972
|
+
return {
|
|
2973
|
+
success: false,
|
|
2974
|
+
email: null,
|
|
2975
|
+
message: null,
|
|
2976
|
+
error: error.message || 'Authentication failed',
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
},
|
|
2980
|
+
wpeLogout: async () => {
|
|
2981
|
+
try {
|
|
2982
|
+
localLogger.info(`[${ADDON_NAME}] Logging out from WP Engine`);
|
|
2983
|
+
if (!wpeOAuthService) {
|
|
2984
|
+
return {
|
|
2985
|
+
success: false,
|
|
2986
|
+
message: null,
|
|
2987
|
+
error: 'WP Engine OAuth service not available',
|
|
2988
|
+
};
|
|
2989
|
+
}
|
|
2990
|
+
// clearTokens() is the logout method
|
|
2991
|
+
await wpeOAuthService.clearTokens();
|
|
2992
|
+
localLogger.info(`[${ADDON_NAME}] Successfully logged out from WPE`);
|
|
2993
|
+
return {
|
|
2994
|
+
success: true,
|
|
2995
|
+
message: 'Logged out from WP Engine',
|
|
2996
|
+
error: null,
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
catch (error) {
|
|
3000
|
+
localLogger.error(`[${ADDON_NAME}] WPE logout failed:`, error);
|
|
3001
|
+
return {
|
|
3002
|
+
success: false,
|
|
3003
|
+
message: null,
|
|
3004
|
+
error: error.message || 'Logout failed',
|
|
3005
|
+
};
|
|
3006
|
+
}
|
|
3007
|
+
},
|
|
3008
|
+
// Phase 11c: Push to WP Engine
|
|
3009
|
+
pushToWpe: async (_parent, args) => {
|
|
3010
|
+
const { localSiteId, remoteInstallId, includeSql = false, confirm = false } = args;
|
|
3011
|
+
try {
|
|
3012
|
+
localLogger.info(`[${ADDON_NAME}] Push to WPE: site=${localSiteId}, remote=${remoteInstallId}, includeSql=${includeSql}`);
|
|
3013
|
+
// Require confirmation for push operations
|
|
3014
|
+
if (!confirm) {
|
|
3015
|
+
return {
|
|
3016
|
+
success: false,
|
|
3017
|
+
message: null,
|
|
3018
|
+
error: 'Push requires confirm=true to prevent accidental overwrites. Set confirm=true to proceed.',
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
// Verify site exists
|
|
3022
|
+
const site = siteData.getSite(localSiteId);
|
|
3023
|
+
if (!site) {
|
|
3024
|
+
return {
|
|
3025
|
+
success: false,
|
|
3026
|
+
message: null,
|
|
3027
|
+
error: `Site not found: ${localSiteId}`,
|
|
3028
|
+
};
|
|
3029
|
+
}
|
|
3030
|
+
// Check WPE connection exists
|
|
3031
|
+
const wpeConnection = site.hostConnections?.find((c) => c.hostId === 'wpe');
|
|
3032
|
+
if (!wpeConnection) {
|
|
3033
|
+
return {
|
|
3034
|
+
success: false,
|
|
3035
|
+
message: null,
|
|
3036
|
+
error: 'Site is not linked to WP Engine. Use Connect in Local to link the site first.',
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
3039
|
+
// Check push service availability
|
|
3040
|
+
if (!wpePushService || typeof wpePushService.push !== 'function') {
|
|
3041
|
+
return {
|
|
3042
|
+
success: false,
|
|
3043
|
+
message: null,
|
|
3044
|
+
error: 'WPE Push service not available',
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
// Get install details from CAPI to get required parameters
|
|
3048
|
+
let installName = remoteInstallId;
|
|
3049
|
+
let primaryDomain = '';
|
|
3050
|
+
let installId = '';
|
|
3051
|
+
if (capiService && typeof capiService.getInstallList === 'function') {
|
|
3052
|
+
const installs = await capiService.getInstallList();
|
|
3053
|
+
const matchingInstall = installs?.find((i) => i.site?.id === wpeConnection.remoteSiteId &&
|
|
3054
|
+
(!wpeConnection.remoteSiteEnv || i.environment === wpeConnection.remoteSiteEnv));
|
|
3055
|
+
if (matchingInstall) {
|
|
3056
|
+
installName = matchingInstall.name;
|
|
3057
|
+
primaryDomain =
|
|
3058
|
+
matchingInstall.primary_domain ||
|
|
3059
|
+
matchingInstall.cname ||
|
|
3060
|
+
`${matchingInstall.name}.wpengine.com`;
|
|
3061
|
+
installId = matchingInstall.id;
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
if (!primaryDomain) {
|
|
3065
|
+
return {
|
|
3066
|
+
success: false,
|
|
3067
|
+
message: null,
|
|
3068
|
+
error: 'Could not determine WP Engine install details. Please ensure you are authenticated.',
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
// Start the push operation (async - returns immediately)
|
|
3072
|
+
wpePushService
|
|
3073
|
+
.push({
|
|
3074
|
+
includeSql,
|
|
3075
|
+
wpengineInstallName: installName,
|
|
3076
|
+
wpengineInstallId: installId,
|
|
3077
|
+
wpengineSiteId: wpeConnection.remoteSiteId,
|
|
3078
|
+
wpenginePrimaryDomain: primaryDomain,
|
|
3079
|
+
localSiteId: site.id,
|
|
3080
|
+
environment: wpeConnection.remoteSiteEnv,
|
|
3081
|
+
isMagicSync: false,
|
|
3082
|
+
})
|
|
3083
|
+
.catch((err) => {
|
|
3084
|
+
localLogger.error(`[${ADDON_NAME}] Push failed:`, err);
|
|
3085
|
+
});
|
|
3086
|
+
return {
|
|
3087
|
+
success: true,
|
|
3088
|
+
message: `Push started to ${installName}. Check Local UI for progress.`,
|
|
3089
|
+
error: null,
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
catch (error) {
|
|
3093
|
+
localLogger.error(`[${ADDON_NAME}] Failed to start push:`, error);
|
|
3094
|
+
return {
|
|
3095
|
+
success: false,
|
|
3096
|
+
message: null,
|
|
3097
|
+
error: error.message || 'Failed to start push',
|
|
3098
|
+
};
|
|
3099
|
+
}
|
|
3100
|
+
},
|
|
3101
|
+
// Phase 11c: Pull from WP Engine
|
|
3102
|
+
pullFromWpe: async (_parent, args) => {
|
|
3103
|
+
const { localSiteId, remoteInstallId, includeSql = false } = args;
|
|
3104
|
+
try {
|
|
3105
|
+
localLogger.info(`[${ADDON_NAME}] Pull from WPE: site=${localSiteId}, remote=${remoteInstallId}, includeSql=${includeSql}`);
|
|
3106
|
+
// Verify site exists
|
|
3107
|
+
const site = siteData.getSite(localSiteId);
|
|
3108
|
+
if (!site) {
|
|
3109
|
+
return {
|
|
3110
|
+
success: false,
|
|
3111
|
+
message: null,
|
|
3112
|
+
error: `Site not found: ${localSiteId}`,
|
|
3113
|
+
};
|
|
3114
|
+
}
|
|
3115
|
+
// Check WPE connection exists
|
|
3116
|
+
const wpeConnection = site.hostConnections?.find((c) => c.hostId === 'wpe');
|
|
3117
|
+
if (!wpeConnection) {
|
|
3118
|
+
return {
|
|
3119
|
+
success: false,
|
|
3120
|
+
message: null,
|
|
3121
|
+
error: 'Site is not linked to WP Engine. Use Connect in Local to link the site first.',
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
// Check pull service availability
|
|
3125
|
+
if (!wpePullService || typeof wpePullService.pull !== 'function') {
|
|
3126
|
+
return {
|
|
3127
|
+
success: false,
|
|
3128
|
+
message: null,
|
|
3129
|
+
error: 'WPE Pull service not available',
|
|
3130
|
+
};
|
|
3131
|
+
}
|
|
3132
|
+
// Get install details from CAPI
|
|
3133
|
+
let installName = remoteInstallId;
|
|
3134
|
+
let primaryDomain = '';
|
|
3135
|
+
let installId = '';
|
|
3136
|
+
if (capiService && typeof capiService.getInstallList === 'function') {
|
|
3137
|
+
const installs = await capiService.getInstallList();
|
|
3138
|
+
const matchingInstall = installs?.find((i) => i.site?.id === wpeConnection.remoteSiteId &&
|
|
3139
|
+
(!wpeConnection.remoteSiteEnv || i.environment === wpeConnection.remoteSiteEnv));
|
|
3140
|
+
if (matchingInstall) {
|
|
3141
|
+
installName = matchingInstall.name;
|
|
3142
|
+
primaryDomain =
|
|
3143
|
+
matchingInstall.primary_domain ||
|
|
3144
|
+
matchingInstall.cname ||
|
|
3145
|
+
`${matchingInstall.name}.wpengine.com`;
|
|
3146
|
+
installId = matchingInstall.id;
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
if (!primaryDomain) {
|
|
3150
|
+
return {
|
|
3151
|
+
success: false,
|
|
3152
|
+
message: null,
|
|
3153
|
+
error: 'Could not determine WP Engine install details. Please ensure you are authenticated.',
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
// Start the pull operation (async - returns immediately)
|
|
3157
|
+
wpePullService
|
|
3158
|
+
.pull({
|
|
3159
|
+
includeSql,
|
|
3160
|
+
wpengineInstallName: installName,
|
|
3161
|
+
wpengineInstallId: installId,
|
|
3162
|
+
wpengineSiteId: wpeConnection.remoteSiteId,
|
|
3163
|
+
wpenginePrimaryDomain: primaryDomain,
|
|
3164
|
+
localSiteId: site.id,
|
|
3165
|
+
environment: wpeConnection.remoteSiteEnv,
|
|
3166
|
+
isMagicSync: false,
|
|
3167
|
+
})
|
|
3168
|
+
.catch((err) => {
|
|
3169
|
+
localLogger.error(`[${ADDON_NAME}] Pull failed:`, err);
|
|
3170
|
+
});
|
|
3171
|
+
return {
|
|
3172
|
+
success: true,
|
|
3173
|
+
message: `Pull started from ${installName}. Check Local UI for progress.`,
|
|
3174
|
+
error: null,
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
catch (error) {
|
|
3178
|
+
localLogger.error(`[${ADDON_NAME}] Failed to start pull:`, error);
|
|
3179
|
+
return {
|
|
3180
|
+
success: false,
|
|
3181
|
+
message: null,
|
|
3182
|
+
error: error.message || 'Failed to start pull',
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
},
|
|
3186
|
+
},
|
|
3187
|
+
};
|
|
3188
|
+
}
|
|
3189
|
+
/**
|
|
3190
|
+
* Start the MCP server
|
|
3191
|
+
*/
|
|
3192
|
+
async function startMcpServer(services, logger) {
|
|
3193
|
+
if (mcpServer) {
|
|
3194
|
+
logger.warn(`[${ADDON_NAME}] MCP server already running`);
|
|
3195
|
+
return;
|
|
3196
|
+
}
|
|
3197
|
+
try {
|
|
3198
|
+
mcpServer = new McpServer_1.McpServer({ port: constants_1.MCP_SERVER.DEFAULT_PORT }, services, logger);
|
|
3199
|
+
await mcpServer.start();
|
|
3200
|
+
const info = mcpServer.getConnectionInfo();
|
|
3201
|
+
logger.info(`[${ADDON_NAME}] MCP server started on port ${info.port}`);
|
|
3202
|
+
logger.info(`[${ADDON_NAME}] MCP connection info saved to: ~/Library/Application Support/Local/mcp-connection-info.json`);
|
|
3203
|
+
logger.info(`[${ADDON_NAME}] Available tools: ${info.tools.join(', ')}`);
|
|
3204
|
+
}
|
|
3205
|
+
catch (error) {
|
|
3206
|
+
logger.error(`[${ADDON_NAME}] Failed to start MCP server:`, error);
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* Stop the MCP server
|
|
3211
|
+
*/
|
|
3212
|
+
async function stopMcpServer(logger) {
|
|
3213
|
+
if (mcpServer) {
|
|
3214
|
+
await mcpServer.stop();
|
|
3215
|
+
mcpServer = null;
|
|
3216
|
+
logger.info(`[${ADDON_NAME}] MCP server stopped`);
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
/**
|
|
3220
|
+
* Register IPC handlers for renderer communication
|
|
3221
|
+
*/
|
|
3222
|
+
function registerIpcHandlers(services, logger) {
|
|
3223
|
+
// Get MCP server status
|
|
3224
|
+
electron_1.ipcMain.handle('mcp:getStatus', async () => {
|
|
3225
|
+
if (!mcpServer) {
|
|
3226
|
+
return { running: false, port: 0, uptime: 0 };
|
|
3227
|
+
}
|
|
3228
|
+
return mcpServer.getStatus();
|
|
3229
|
+
});
|
|
3230
|
+
// Get connection info
|
|
3231
|
+
electron_1.ipcMain.handle('mcp:getConnectionInfo', async () => {
|
|
3232
|
+
if (!mcpServer) {
|
|
3233
|
+
return null;
|
|
3234
|
+
}
|
|
3235
|
+
return mcpServer.getConnectionInfo();
|
|
3236
|
+
});
|
|
3237
|
+
// Start MCP server
|
|
3238
|
+
electron_1.ipcMain.handle('mcp:start', async () => {
|
|
3239
|
+
try {
|
|
3240
|
+
await startMcpServer(services, logger);
|
|
3241
|
+
return { success: true };
|
|
3242
|
+
}
|
|
3243
|
+
catch (error) {
|
|
3244
|
+
return { success: false, error: error.message };
|
|
3245
|
+
}
|
|
3246
|
+
});
|
|
3247
|
+
// Stop MCP server
|
|
3248
|
+
electron_1.ipcMain.handle('mcp:stop', async () => {
|
|
3249
|
+
try {
|
|
3250
|
+
await stopMcpServer(logger);
|
|
3251
|
+
return { success: true };
|
|
3252
|
+
}
|
|
3253
|
+
catch (error) {
|
|
3254
|
+
return { success: false, error: error.message };
|
|
3255
|
+
}
|
|
3256
|
+
});
|
|
3257
|
+
// Restart MCP server
|
|
3258
|
+
electron_1.ipcMain.handle('mcp:restart', async () => {
|
|
3259
|
+
try {
|
|
3260
|
+
await stopMcpServer(logger);
|
|
3261
|
+
await startMcpServer(services, logger);
|
|
3262
|
+
return { success: true };
|
|
3263
|
+
}
|
|
3264
|
+
catch (error) {
|
|
3265
|
+
return { success: false, error: error.message };
|
|
3266
|
+
}
|
|
3267
|
+
});
|
|
3268
|
+
// Regenerate auth token
|
|
3269
|
+
electron_1.ipcMain.handle('mcp:regenerateToken', async () => {
|
|
3270
|
+
if (!mcpServer) {
|
|
3271
|
+
return { success: false, error: 'MCP server not running' };
|
|
3272
|
+
}
|
|
3273
|
+
try {
|
|
3274
|
+
const newToken = await mcpServer.regenerateToken();
|
|
3275
|
+
return { success: true, token: newToken };
|
|
3276
|
+
}
|
|
3277
|
+
catch (error) {
|
|
3278
|
+
return { success: false, error: error.message };
|
|
3279
|
+
}
|
|
3280
|
+
});
|
|
3281
|
+
logger.info(`[${ADDON_NAME}] Registered IPC handlers: mcp:getStatus, mcp:getConnectionInfo, mcp:start, mcp:stop, mcp:restart, mcp:regenerateToken`);
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* Main addon initialization function
|
|
3285
|
+
*/
|
|
3286
|
+
function default_1(_context) {
|
|
3287
|
+
const services = LocalMain.getServiceContainer().cradle;
|
|
3288
|
+
const { localLogger, graphql } = services;
|
|
3289
|
+
try {
|
|
3290
|
+
localLogger.info(`[${ADDON_NAME}] Initializing...`);
|
|
3291
|
+
// Register GraphQL extensions (for local-cli and MCP)
|
|
3292
|
+
const resolvers = createResolvers(services);
|
|
3293
|
+
graphql.registerGraphQLService('mcp-server', typeDefs, resolvers);
|
|
3294
|
+
localLogger.info(`[${ADDON_NAME}] Registered GraphQL: 29 tools (Phase 1-11b)`);
|
|
3295
|
+
// Start MCP server (for AI tools)
|
|
3296
|
+
const localServices = {
|
|
3297
|
+
siteData: services.siteData,
|
|
3298
|
+
siteProcessManager: services.siteProcessManager,
|
|
3299
|
+
wpCli: services.wpCli,
|
|
3300
|
+
deleteSite: services.deleteSite,
|
|
3301
|
+
addSite: services.addSite,
|
|
3302
|
+
localLogger: services.localLogger,
|
|
3303
|
+
adminer: services.adminer,
|
|
3304
|
+
x509Cert: services.x509Cert,
|
|
3305
|
+
siteProvisioner: services.siteProvisioner,
|
|
3306
|
+
importSite: services.importSite,
|
|
3307
|
+
lightningServices: services.lightningServices,
|
|
3308
|
+
// Phase 11: WP Engine Connect
|
|
3309
|
+
wpeOAuth: services.wpeOAuth,
|
|
3310
|
+
capi: services.capi,
|
|
3311
|
+
};
|
|
3312
|
+
// MCP server disabled - CLI-only mode
|
|
3313
|
+
// To enable MCP server for AI tool integration, uncomment the following:
|
|
3314
|
+
// startMcpServer(localServices, localLogger);
|
|
3315
|
+
// registerIpcHandlers(localServices, localLogger);
|
|
3316
|
+
localLogger.info(`[${ADDON_NAME}] Successfully initialized (CLI-only mode)`);
|
|
3317
|
+
}
|
|
3318
|
+
catch (error) {
|
|
3319
|
+
localLogger.error(`[${ADDON_NAME}] Failed to initialize:`, error);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWFpbi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7OztHQU1HOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXdzSEgsNEJBdUNDO0FBN3VIRCxtRUFBcUQ7QUFDckQsdUNBQW1DO0FBQ25DLDhEQUE4QjtBQUM5QiwrQ0FBNEM7QUFDNUMsbURBQWlEO0FBR2pELE1BQU0sVUFBVSxHQUFHLFlBQVksQ0FBQztBQUVoQyxJQUFJLFNBQVMsR0FBcUIsSUFBSSxDQUFDO0FBRXZDOztHQUVHO0FBQ0gsTUFBTSxRQUFRLEdBQUcsSUFBQSxxQkFBRyxFQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7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QVEsQ0FBQyxXQUFXO1lBQ2pDLE9BQU8sRUFBRSxRQUFRLENBQUMsT0FBTztZQUN6QixRQUFRLEVBQUUsUUFBUSxDQUFDLFFBQVE7WUFDM0IsZUFBZSxFQUFFLFFBQVEsQ0FBQyxlQUFlO1lBQ3pDLFVBQVUsRUFBRSxRQUFRLENBQUMsVUFBVTtZQUMvQixpQkFBaUIsRUFBRSxRQUFRLENBQUMsaUJBQWlCO1lBQzdDLDhCQUE4QjtZQUM5QixRQUFRLEVBQUUsUUFBUSxDQUFDLFFBQVE7WUFDM0IsSUFBSSxFQUFFLFFBQVEsQ0FBQyxJQUFJO1NBQ3BCLENBQUM7UUFFRixzQ0FBc0M7UUFDdEMseUVBQXlFO1FBQ3pFLDhDQUE4QztRQUM5QyxtREFBbUQ7UUFFbkQsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLFVBQVUsNENBQTRDLENBQUMsQ0FBQztJQUMvRSxDQUFDO0lBQUMsT0FBTyxLQUFVLEVBQUUsQ0FBQztRQUNwQixXQUFXLENBQUMsS0FBSyxDQUFDLElBQUksVUFBVSx5QkFBeUIsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNwRSxDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ0xJIEJyaWRnZSBBZGRvbiAtIE1haW4gUHJvY2VzcyBFbnRyeSBQb2ludFxuICpcbiAqIFRoaXMgYWRkb24gZXh0ZW5kcyBMb2NhbCdzIGNhcGFiaWxpdGllczpcbiAqIC0gR3JhcGhRTCBtdXRhdGlvbnMgZm9yIGRlbGV0ZVNpdGUsIHdwQ2xpIChmb3IgbG9jYWwtY2xpKVxuICogLSBNQ1AgU2VydmVyIGZvciBBSSB0b29sIGludGVncmF0aW9uIChDbGF1ZGUgQ29kZSwgQ2hhdEdQVCwgZXRjLilcbiAqL1xuXG5pbXBvcnQgKiBhcyBMb2NhbE1haW4gZnJvbSAnQGdldGZseXdoZWVsL2xvY2FsL21haW4nO1xuaW1wb3J0IHsgaXBjTWFpbiB9IGZyb20gJ2VsZWN0cm9uJztcbmltcG9ydCBncWwgZnJvbSAnZ3JhcGhxbC10YWcnO1xuaW1wb3J0IHsgTWNwU2VydmVyIH0gZnJvbSAnLi9tY3AvTWNwU2VydmVyJztcbmltcG9ydCB7IE1DUF9TRVJWRVIgfSBmcm9tICcuLi9jb21tb24vY29uc3RhbnRzJztcbmltcG9ydCB7IExvY2FsU2VydmljZXMgfSBmcm9tICcuLi9jb21tb24vdHlwZXMnO1xuXG5jb25zdCBBRERPTl9OQU1FID0gJ01DUCBTZXJ2ZXInO1xuXG5sZXQgbWNwU2VydmVyOiBNY3BTZXJ2ZXIgfCBudWxsID0gbnVsbDtcblxuLyoqXG4gKiBHcmFwaFFMIHR5cGUgZGVmaW5pdGlvbnMgZm9yIENMSSBCcmlkZ2VcbiAqL1xuY29uc3QgdHlwZURlZnMgPSBncWxgXG4gIGlucHV0IERlbGV0ZVNpdGVJbnB1dCB7XG4gICAgXCJUaGUgc2l0ZSBJRCB0byBkZWxldGVcIlxuICAgIGlkOiBJRCFcbiAgICBcIldoZXRoZXIgdG8gbW92ZSBzaXRlIGZpbGVzIHRvIHRyYXNoICh0cnVlKSBvciBqdXN0IHJlbW92ZSBmcm9tIExvY2FsIChmYWxzZSlcIlxuICAgIHRyYXNoRmlsZXM6IEJvb2xlYW4gPSB0cnVlXG4gICAgXCJXaGV0aGVyIHRvIHVwZGF0ZSB0aGUgaG9zdHMgZmlsZVwiXG4gICAgdXBkYXRlSG9zdHM6IEJvb2xlYW4gPSB0cnVlXG4gIH1cblxuICB0eXBlIERlbGV0ZVNpdGVSZXN1bHQge1xuICAgIFwiV2hldGhlciB0aGUgZGVsZXRpb24gd2FzIHN1Y2Nlc3NmdWxcIlxuICAgIHN1Y2Nlc3M6IEJvb2xlYW4hXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGRlbGV0aW9uIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICAgIFwiVGhlIElEIG9mIHRoZSBkZWxldGVkIHNpdGVcIlxuICAgIHNpdGVJZDogSURcbiAgfVxuXG4gIGlucHV0IFdwQ2xpSW5wdXQge1xuICAgIFwiVGhlIHNpdGUgSUQgdG8gcnVuIFdQLUNMSSBhZ2FpbnN0XCJcbiAgICBzaXRlSWQ6IElEIVxuICAgIFwiV1AtQ0xJIGNvbW1hbmQgYW5kIGFyZ3VtZW50cyAoZS5nLiwgWydwbHVnaW4nLCAnbGlzdCcsICctLWZvcm1hdD1qc29uJ10pXCJcbiAgICBhcmdzOiBbU3RyaW5nIV0hXG4gICAgXCJTa2lwIGxvYWRpbmcgcGx1Z2lucyAoZGVmYXVsdDogdHJ1ZSlcIlxuICAgIHNraXBQbHVnaW5zOiBCb29sZWFuID0gdHJ1ZVxuICAgIFwiU2tpcCBsb2FkaW5nIHRoZW1lcyAoZGVmYXVsdDogdHJ1ZSlcIlxuICAgIHNraXBUaGVtZXM6IEJvb2xlYW4gPSB0cnVlXG4gIH1cblxuICB0eXBlIFdwQ2xpUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgdGhlIGNvbW1hbmQgZXhlY3V0ZWQgc3VjY2Vzc2Z1bGx5XCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiQ29tbWFuZCBvdXRwdXQgKHN0ZG91dClcIlxuICAgIG91dHB1dDogU3RyaW5nXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGNvbW1hbmQgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gIH1cblxuICBpbnB1dCBDcmVhdGVTaXRlSW5wdXQge1xuICAgIFwiU2l0ZSBuYW1lIChyZXF1aXJlZClcIlxuICAgIG5hbWU6IFN0cmluZyFcbiAgICBcIlBIUCB2ZXJzaW9uIChlLmcuLCAnOC4yLjEwJykuIFVzZXMgTG9jYWwgZGVmYXVsdCBpZiBub3Qgc3BlY2lmaWVkLlwiXG4gICAgcGhwVmVyc2lvbjogU3RyaW5nXG4gICAgXCJXZWIgc2VydmVyIHR5cGVcIlxuICAgIHdlYlNlcnZlcjogU3RyaW5nXG4gICAgXCJEYXRhYmFzZSB0eXBlXCJcbiAgICBkYXRhYmFzZTogU3RyaW5nXG4gICAgXCJXb3JkUHJlc3MgYWRtaW4gdXNlcm5hbWUgKGRlZmF1bHQ6IGFkbWluKVwiXG4gICAgd3BBZG1pblVzZXJuYW1lOiBTdHJpbmdcbiAgICBcIldvcmRQcmVzcyBhZG1pbiBwYXNzd29yZCAoZGVmYXVsdDogcGFzc3dvcmQpXCJcbiAgICB3cEFkbWluUGFzc3dvcmQ6IFN0cmluZ1xuICAgIFwiV29yZFByZXNzIGFkbWluIGVtYWlsIChkZWZhdWx0OiBhZG1pbkBsb2NhbC50ZXN0KVwiXG4gICAgd3BBZG1pbkVtYWlsOiBTdHJpbmdcbiAgICBcIkJsdWVwcmludCBuYW1lIHRvIGNyZWF0ZSBzaXRlIGZyb20uIFVzZSBsaXN0X2JsdWVwcmludHMgdG8gc2VlIGF2YWlsYWJsZSBibHVlcHJpbnRzLlwiXG4gICAgYmx1ZXByaW50OiBTdHJpbmdcbiAgfVxuXG4gIHR5cGUgQ3JlYXRlU2l0ZVJlc3VsdCB7XG4gICAgXCJXaGV0aGVyIHNpdGUgY3JlYXRpb24gd2FzIGluaXRpYXRlZCBzdWNjZXNzZnVsbHlcIlxuICAgIHN1Y2Nlc3M6IEJvb2xlYW4hXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGNyZWF0aW9uIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICAgIFwiVGhlIGNyZWF0ZWQgc2l0ZSBJRFwiXG4gICAgc2l0ZUlkOiBJRFxuICAgIFwiVGhlIHNpdGUgbmFtZVwiXG4gICAgc2l0ZU5hbWU6IFN0cmluZ1xuICAgIFwiVGhlIHNpdGUgZG9tYWluXCJcbiAgICBzaXRlRG9tYWluOiBTdHJpbmdcbiAgfVxuXG4gIGlucHV0IE9wZW5TaXRlSW5wdXQge1xuICAgIFwiVGhlIHNpdGUgSUQgdG8gb3BlblwiXG4gICAgc2l0ZUlkOiBJRCFcbiAgICBcIlBhdGggdG8gb3BlbiAoZGVmYXVsdDogLywgdXNlIC93cC1hZG1pbiBmb3IgYWRtaW4pXCJcbiAgICBwYXRoOiBTdHJpbmcgPSBcIi9cIlxuICB9XG5cbiAgdHlwZSBPcGVuU2l0ZVJlc3VsdCB7XG4gICAgXCJXaGV0aGVyIHRoZSBzaXRlIHdhcyBvcGVuZWQgc3VjY2Vzc2Z1bGx5XCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgICBcIlRoZSBVUkwgdGhhdCB3YXMgb3BlbmVkXCJcbiAgICB1cmw6IFN0cmluZ1xuICB9XG5cbiAgaW5wdXQgQ2xvbmVTaXRlSW5wdXQge1xuICAgIFwiVGhlIHNpdGUgSUQgdG8gY2xvbmVcIlxuICAgIHNpdGVJZDogSUQhXG4gICAgXCJOYW1lIGZvciB0aGUgY2xvbmVkIHNpdGVcIlxuICAgIG5ld05hbWU6IFN0cmluZyFcbiAgfVxuXG4gIHR5cGUgQ2xvbmVTaXRlUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgY2xvbmluZyB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gICAgXCJUaGUgbmV3IHNpdGUgSURcIlxuICAgIG5ld1NpdGVJZDogSURcbiAgICBcIlRoZSBuZXcgc2l0ZSBuYW1lXCJcbiAgICBuZXdTaXRlTmFtZTogU3RyaW5nXG4gICAgXCJUaGUgbmV3IHNpdGUgZG9tYWluXCJcbiAgICBuZXdTaXRlRG9tYWluOiBTdHJpbmdcbiAgfVxuXG4gIGlucHV0IEV4cG9ydFNpdGVJbnB1dCB7XG4gICAgXCJUaGUgc2l0ZSBJRCB0byBleHBvcnRcIlxuICAgIHNpdGVJZDogSUQhXG4gICAgXCJPdXRwdXQgZGlyZWN0b3J5IHBhdGggKGRlZmF1bHQ6IH4vRG93bmxvYWRzKVwiXG4gICAgb3V0cHV0UGF0aDogU3RyaW5nXG4gIH1cblxuICB0eXBlIEV4cG9ydFNpdGVSZXN1bHQge1xuICAgIFwiV2hldGhlciBleHBvcnQgd2FzIHN1Y2Nlc3NmdWxcIlxuICAgIHN1Y2Nlc3M6IEJvb2xlYW4hXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICAgIFwiUGF0aCB0byB0aGUgZXhwb3J0ZWQgemlwIGZpbGVcIlxuICAgIGV4cG9ydFBhdGg6IFN0cmluZ1xuICB9XG5cbiAgdHlwZSBCbHVlcHJpbnQge1xuICAgIFwiQmx1ZXByaW50IG5hbWVcIlxuICAgIG5hbWU6IFN0cmluZyFcbiAgICBcIkxhc3QgbW9kaWZpZWQgZGF0ZVwiXG4gICAgbGFzdE1vZGlmaWVkOiBTdHJpbmdcbiAgICBcIlBIUCB2ZXJzaW9uXCJcbiAgICBwaHBWZXJzaW9uOiBTdHJpbmdcbiAgICBcIldlYiBzZXJ2ZXIgdHlwZVwiXG4gICAgd2ViU2VydmVyOiBTdHJpbmdcbiAgICBcIkRhdGFiYXNlIHR5cGVcIlxuICAgIGRhdGFiYXNlOiBTdHJpbmdcbiAgfVxuXG4gIHR5cGUgQmx1ZXByaW50c1Jlc3VsdCB7XG4gICAgXCJXaGV0aGVyIHF1ZXJ5IHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgICBcIkxpc3Qgb2YgYmx1ZXByaW50c1wiXG4gICAgYmx1ZXByaW50czogW0JsdWVwcmludCFdXG4gIH1cblxuICBpbnB1dCBTYXZlQmx1ZXByaW50SW5wdXQge1xuICAgIFwiVGhlIHNpdGUgSUQgdG8gc2F2ZSBhcyBibHVlcHJpbnRcIlxuICAgIHNpdGVJZDogSUQhXG4gICAgXCJOYW1lIGZvciB0aGUgYmx1ZXByaW50XCJcbiAgICBuYW1lOiBTdHJpbmchXG4gIH1cblxuICB0eXBlIFNhdmVCbHVlcHJpbnRSZXN1bHQge1xuICAgIFwiV2hldGhlciBzYXZlIHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgICBcIlRoZSBibHVlcHJpbnQgbmFtZVwiXG4gICAgYmx1ZXByaW50TmFtZTogU3RyaW5nXG4gIH1cblxuICAjIFBoYXNlIDg6IFdvcmRQcmVzcyBEZXZlbG9wbWVudCBUb29sc1xuICBpbnB1dCBFeHBvcnREYXRhYmFzZUlucHV0IHtcbiAgICBcIlRoZSBzaXRlIElEXCJcbiAgICBzaXRlSWQ6IElEIVxuICAgIFwiT3V0cHV0IGZpbGUgcGF0aCAob3B0aW9uYWwsIGRlZmF1bHRzIHRvIH4vRG93bmxvYWRzLzxzaXRlLW5hbWU+LnNxbClcIlxuICAgIG91dHB1dFBhdGg6IFN0cmluZ1xuICB9XG5cbiAgdHlwZSBFeHBvcnREYXRhYmFzZVJlc3VsdCB7XG4gICAgXCJXaGV0aGVyIGV4cG9ydCB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gICAgXCJQYXRoIHRvIHRoZSBleHBvcnRlZCBTUUwgZmlsZVwiXG4gICAgb3V0cHV0UGF0aDogU3RyaW5nXG4gIH1cblxuICBpbnB1dCBJbXBvcnREYXRhYmFzZUlucHV0IHtcbiAgICBcIlRoZSBzaXRlIElEXCJcbiAgICBzaXRlSWQ6IElEIVxuICAgIFwiUGF0aCB0byB0aGUgU1FMIGZpbGUgdG8gaW1wb3J0XCJcbiAgICBzcWxQYXRoOiBTdHJpbmchXG4gIH1cblxuICB0eXBlIEltcG9ydERhdGFiYXNlUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgaW1wb3J0IHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgfVxuXG4gIGlucHV0IE9wZW5BZG1pbmVySW5wdXQge1xuICAgIFwiVGhlIHNpdGUgSURcIlxuICAgIHNpdGVJZDogSUQhXG4gIH1cblxuICB0eXBlIE9wZW5BZG1pbmVyUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgb3BlbmluZyB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gIH1cblxuICBpbnB1dCBUcnVzdFNzbElucHV0IHtcbiAgICBcIlRoZSBzaXRlIElEXCJcbiAgICBzaXRlSWQ6IElEIVxuICB9XG5cbiAgdHlwZSBUcnVzdFNzbFJlc3VsdCB7XG4gICAgXCJXaGV0aGVyIHRydXN0IHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgfVxuXG4gIGlucHV0IE1jcFJlbmFtZVNpdGVJbnB1dCB7XG4gICAgXCJUaGUgc2l0ZSBJRFwiXG4gICAgc2l0ZUlkOiBJRCFcbiAgICBcIk5ldyBuYW1lIGZvciB0aGUgc2l0ZVwiXG4gICAgbmV3TmFtZTogU3RyaW5nIVxuICB9XG5cbiAgdHlwZSBNY3BSZW5hbWVTaXRlUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgcmVuYW1lIHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgICBcIlRoZSBuZXcgbmFtZVwiXG4gICAgbmV3TmFtZTogU3RyaW5nXG4gIH1cblxuICBpbnB1dCBDaGFuZ2VQaHBWZXJzaW9uSW5wdXQge1xuICAgIFwiVGhlIHNpdGUgSURcIlxuICAgIHNpdGVJZDogSUQhXG4gICAgXCJUYXJnZXQgUEhQIHZlcnNpb25cIlxuICAgIHBocFZlcnNpb246IFN0cmluZyFcbiAgfVxuXG4gIHR5cGUgQ2hhbmdlUGhwVmVyc2lvblJlc3VsdCB7XG4gICAgXCJXaGV0aGVyIGNoYW5nZSB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gICAgXCJUaGUgbmV3IFBIUCB2ZXJzaW9uXCJcbiAgICBwaHBWZXJzaW9uOiBTdHJpbmdcbiAgfVxuXG4gIGlucHV0IEltcG9ydFNpdGVJbnB1dCB7XG4gICAgXCJQYXRoIHRvIHRoZSB6aXAgZmlsZSB0byBpbXBvcnRcIlxuICAgIHppcFBhdGg6IFN0cmluZyFcbiAgICBcIk5hbWUgZm9yIHRoZSBpbXBvcnRlZCBzaXRlIChvcHRpb25hbClcIlxuICAgIHNpdGVOYW1lOiBTdHJpbmdcbiAgfVxuXG4gIHR5cGUgSW1wb3J0U2l0ZVJlc3VsdCB7XG4gICAgXCJXaGV0aGVyIGltcG9ydCB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gICAgXCJUaGUgaW1wb3J0ZWQgc2l0ZSBJRFwiXG4gICAgc2l0ZUlkOiBJRFxuICAgIFwiVGhlIGltcG9ydGVkIHNpdGUgbmFtZVwiXG4gICAgc2l0ZU5hbWU6IFN0cmluZ1xuICB9XG5cbiAgIyBQaGFzZSA5OiBTaXRlIENvbmZpZ3VyYXRpb24gJiBEZXYgVG9vbHNcbiAgaW5wdXQgVG9nZ2xlWGRlYnVnSW5wdXQge1xuICAgIFwiVGhlIHNpdGUgSURcIlxuICAgIHNpdGVJZDogSUQhXG4gICAgXCJXaGV0aGVyIHRvIGVuYWJsZSBvciBkaXNhYmxlIFhkZWJ1Z1wiXG4gICAgZW5hYmxlZDogQm9vbGVhbiFcbiAgfVxuXG4gIHR5cGUgVG9nZ2xlWGRlYnVnUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgdG9nZ2xlIHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgICBcIkN1cnJlbnQgWGRlYnVnIHN0YXRlXCJcbiAgICBlbmFibGVkOiBCb29sZWFuXG4gIH1cblxuICBpbnB1dCBHZXRTaXRlTG9nc0lucHV0IHtcbiAgICBcIlRoZSBzaXRlIElEXCJcbiAgICBzaXRlSWQ6IElEIVxuICAgIFwiVHlwZSBvZiBsb2dzIHRvIHJldHJpZXZlIChwaHAsIG5naW54LCBteXNxbCwgYWxsKVwiXG4gICAgbG9nVHlwZTogU3RyaW5nID0gXCJwaHBcIlxuICAgIFwiTnVtYmVyIG9mIGxpbmVzIHRvIHJldHVyblwiXG4gICAgbGluZXM6IEludCA9IDEwMFxuICB9XG5cbiAgdHlwZSBMb2dFbnRyeSB7XG4gICAgXCJMb2cgdHlwZVwiXG4gICAgdHlwZTogU3RyaW5nIVxuICAgIFwiTG9nIGNvbnRlbnRcIlxuICAgIGNvbnRlbnQ6IFN0cmluZyFcbiAgICBcIkxvZyBmaWxlIHBhdGhcIlxuICAgIHBhdGg6IFN0cmluZyFcbiAgfVxuXG4gIHR5cGUgR2V0U2l0ZUxvZ3NSZXN1bHQge1xuICAgIFwiV2hldGhlciByZXRyaWV2YWwgd2FzIHN1Y2Nlc3NmdWxcIlxuICAgIHN1Y2Nlc3M6IEJvb2xlYW4hXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICAgIFwiTG9nIGVudHJpZXNcIlxuICAgIGxvZ3M6IFtMb2dFbnRyeSFdXG4gIH1cblxuICB0eXBlIFNlcnZpY2VJbmZvIHtcbiAgICBcIlNlcnZpY2Ugcm9sZSAocGhwLCBkYXRhYmFzZSwgd2Vic2VydmVyKVwiXG4gICAgcm9sZTogU3RyaW5nIVxuICAgIFwiU2VydmljZSBuYW1lXCJcbiAgICBuYW1lOiBTdHJpbmchXG4gICAgXCJTZXJ2aWNlIHZlcnNpb25cIlxuICAgIHZlcnNpb246IFN0cmluZyFcbiAgfVxuXG4gIHR5cGUgTGlzdFNlcnZpY2VzUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgbGlzdGluZyB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gICAgXCJBdmFpbGFibGUgc2VydmljZXNcIlxuICAgIHNlcnZpY2VzOiBbU2VydmljZUluZm8hXVxuICB9XG5cbiAgZXh0ZW5kIHR5cGUgTXV0YXRpb24ge1xuICAgIFwiQ3JlYXRlIGEgbmV3IFdvcmRQcmVzcyBzaXRlIHdpdGggZnVsbCBXb3JkUHJlc3MgaW5zdGFsbGF0aW9uXCJcbiAgICBjcmVhdGVTaXRlKGlucHV0OiBDcmVhdGVTaXRlSW5wdXQhKTogQ3JlYXRlU2l0ZVJlc3VsdCFcblxuICAgIFwiRGVsZXRlIGEgc2l0ZSBmcm9tIExvY2FsXCJcbiAgICBkZWxldGVTaXRlKGlucHV0OiBEZWxldGVTaXRlSW5wdXQhKTogRGVsZXRlU2l0ZVJlc3VsdCFcblxuICAgIFwiRGVsZXRlIG11bHRpcGxlIHNpdGVzIGZyb20gTG9jYWxcIlxuICAgIGRlbGV0ZVNpdGVzKGlkczogW0lEIV0hLCB0cmFzaEZpbGVzOiBCb29sZWFuID0gdHJ1ZSk6IERlbGV0ZVNpdGVSZXN1bHQhXG5cbiAgICBcIlJ1biBhIFdQLUNMSSBjb21tYW5kIGFnYWluc3QgYSBzaXRlXCJcbiAgICB3cENsaShpbnB1dDogV3BDbGlJbnB1dCEpOiBXcENsaVJlc3VsdCFcblxuICAgIFwiT3BlbiBhIHNpdGUgaW4gdGhlIGRlZmF1bHQgYnJvd3NlclwiXG4gICAgb3BlblNpdGUoaW5wdXQ6IE9wZW5TaXRlSW5wdXQhKTogT3BlblNpdGVSZXN1bHQhXG5cbiAgICBcIkNsb25lIGFuIGV4aXN0aW5nIHNpdGVcIlxuICAgIGNsb25lU2l0ZShpbnB1dDogQ2xvbmVTaXRlSW5wdXQhKTogQ2xvbmVTaXRlUmVzdWx0IVxuXG4gICAgXCJFeHBvcnQgYSBzaXRlIHRvIGEgemlwIGZpbGVcIlxuICAgIGV4cG9ydFNpdGUoaW5wdXQ6IEV4cG9ydFNpdGVJbnB1dCEpOiBFeHBvcnRTaXRlUmVzdWx0IVxuXG4gICAgXCJTYXZlIGEgc2l0ZSBhcyBhIGJsdWVwcmludFwiXG4gICAgc2F2ZUJsdWVwcmludChpbnB1dDogU2F2ZUJsdWVwcmludElucHV0ISk6IFNhdmVCbHVlcHJpbnRSZXN1bHQhXG5cbiAgICAjIFBoYXNlIDg6IFdvcmRQcmVzcyBEZXZlbG9wbWVudCBUb29sc1xuICAgIFwiRXhwb3J0IHNpdGUgZGF0YWJhc2UgdG8gU1FMIGZpbGVcIlxuICAgIGV4cG9ydERhdGFiYXNlKGlucHV0OiBFeHBvcnREYXRhYmFzZUlucHV0ISk6IEV4cG9ydERhdGFiYXNlUmVzdWx0IVxuXG4gICAgXCJJbXBvcnQgU1FMIGZpbGUgaW50byBzaXRlIGRhdGFiYXNlXCJcbiAgICBpbXBvcnREYXRhYmFzZShpbnB1dDogSW1wb3J0RGF0YWJhc2VJbnB1dCEpOiBJbXBvcnREYXRhYmFzZVJlc3VsdCFcblxuICAgIFwiT3BlbiBBZG1pbmVyIGRhdGFiYXNlIG1hbmFnZW1lbnQgVUlcIlxuICAgIG9wZW5BZG1pbmVyKGlucHV0OiBPcGVuQWRtaW5lcklucHV0ISk6IE9wZW5BZG1pbmVyUmVzdWx0IVxuXG4gICAgXCJUcnVzdCBzaXRlIFNTTCBjZXJ0aWZpY2F0ZVwiXG4gICAgdHJ1c3RTc2woaW5wdXQ6IFRydXN0U3NsSW5wdXQhKTogVHJ1c3RTc2xSZXN1bHQhXG5cbiAgICBcIlJlbmFtZSBhIHNpdGUgKE1DUCB2ZXJzaW9uKVwiXG4gICAgbWNwUmVuYW1lU2l0ZShpbnB1dDogTWNwUmVuYW1lU2l0ZUlucHV0ISk6IE1jcFJlbmFtZVNpdGVSZXN1bHQhXG5cbiAgICBcIkNoYW5nZSBzaXRlIFBIUCB2ZXJzaW9uXCJcbiAgICBjaGFuZ2VQaHBWZXJzaW9uKGlucHV0OiBDaGFuZ2VQaHBWZXJzaW9uSW5wdXQhKTogQ2hhbmdlUGhwVmVyc2lvblJlc3VsdCFcblxuICAgIFwiSW1wb3J0IHNpdGUgZnJvbSB6aXAgZmlsZVwiXG4gICAgaW1wb3J0U2l0ZShpbnB1dDogSW1wb3J0U2l0ZUlucHV0ISk6IEltcG9ydFNpdGVSZXN1bHQhXG5cbiAgICAjIFBoYXNlIDk6IFNpdGUgQ29uZmlndXJhdGlvbiAmIERldiBUb29sc1xuICAgIFwiVG9nZ2xlIFhkZWJ1ZyBmb3IgYSBzaXRlXCJcbiAgICB0b2dnbGVYZGVidWcoaW5wdXQ6IFRvZ2dsZVhkZWJ1Z0lucHV0ISk6IFRvZ2dsZVhkZWJ1Z1Jlc3VsdCFcblxuICAgIFwiR2V0IHNpdGUgbG9nIGZpbGVzXCJcbiAgICBnZXRTaXRlTG9ncyhpbnB1dDogR2V0U2l0ZUxvZ3NJbnB1dCEpOiBHZXRTaXRlTG9nc1Jlc3VsdCFcbiAgfVxuXG4gIGV4dGVuZCB0eXBlIFF1ZXJ5IHtcbiAgICBcIlJ1biBhIFdQLUNMSSBjb21tYW5kIGFnYWluc3QgYSBzaXRlIChyZWFkLW9ubHkgb3BlcmF0aW9ucylcIlxuICAgIHdwQ2xpUXVlcnkoaW5wdXQ6IFdwQ2xpSW5wdXQhKTogV3BDbGlSZXN1bHQhXG5cbiAgICBcIkxpc3QgYWxsIGF2YWlsYWJsZSBibHVlcHJpbnRzXCJcbiAgICBibHVlcHJpbnRzOiBCbHVlcHJpbnRzUmVzdWx0IVxuXG4gICAgXCJMaXN0IGF2YWlsYWJsZSBzZXJ2aWNlIHZlcnNpb25zXCJcbiAgICBsaXN0U2VydmljZXModHlwZTogU3RyaW5nKTogTGlzdFNlcnZpY2VzUmVzdWx0IVxuXG4gICAgIyBQaGFzZSAxMTogV1AgRW5naW5lIENvbm5lY3RcbiAgICBcIkNoZWNrIFdQIEVuZ2luZSBhdXRoZW50aWNhdGlvbiBzdGF0dXNcIlxuICAgIHdwZVN0YXR1czogV3BlQXV0aFN0YXR1cyFcblxuICAgIFwiTGlzdCBhbGwgc2l0ZXMgZnJvbSBXUCBFbmdpbmUgYWNjb3VudFwiXG4gICAgbGlzdFdwZVNpdGVzKGFjY291bnRJZDogU3RyaW5nKTogTGlzdFdwZVNpdGVzUmVzdWx0IVxuXG4gICAgIyBQaGFzZSAxMWI6IFNpdGUgTGlua2luZ1xuICAgIFwiR2V0IFdQIEVuZ2luZSBjb25uZWN0aW9uIGRldGFpbHMgZm9yIGEgbG9jYWwgc2l0ZVwiXG4gICAgZ2V0V3BlTGluayhzaXRlSWQ6IElEISk6IEdldFdwZUxpbmtSZXN1bHQhXG4gIH1cblxuICAjIFBoYXNlIDExOiBXUCBFbmdpbmUgQ29ubmVjdCBUeXBlc1xuICB0eXBlIFdwZUF1dGhTdGF0dXMge1xuICAgIFwiV2hldGhlciBhdXRoZW50aWNhdGVkIHdpdGggV1AgRW5naW5lXCJcbiAgICBhdXRoZW50aWNhdGVkOiBCb29sZWFuIVxuICAgIFwiVXNlciBlbWFpbCBpZiBhdXRoZW50aWNhdGVkXCJcbiAgICBlbWFpbDogU3RyaW5nXG4gICAgXCJBY2NvdW50IElEIGlmIGF1dGhlbnRpY2F0ZWRcIlxuICAgIGFjY291bnRJZDogU3RyaW5nXG4gICAgXCJBY2NvdW50IG5hbWUgaWYgYXV0aGVudGljYXRlZFwiXG4gICAgYWNjb3VudE5hbWU6IFN0cmluZ1xuICAgIFwiVG9rZW4gZXhwaXJ5IHRpbWVcIlxuICAgIHRva2VuRXhwaXJ5OiBTdHJpbmdcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgc3RhdHVzIGNoZWNrIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICB9XG5cbiAgdHlwZSBXcGVBdXRoUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgYXV0aGVudGljYXRpb24gd2FzIHN1Y2Nlc3NmdWxcIlxuICAgIHN1Y2Nlc3M6IEJvb2xlYW4hXG4gICAgXCJVc2VyIGVtYWlsIGlmIHN1Y2Nlc3NmdWxcIlxuICAgIGVtYWlsOiBTdHJpbmdcbiAgICBcIk1lc3NhZ2UgYWJvdXQgdGhlIGF1dGhlbnRpY2F0aW9uIHJlc3VsdFwiXG4gICAgbWVzc2FnZTogU3RyaW5nXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICB9XG5cbiAgdHlwZSBXcGVMb2dvdXRSZXN1bHQge1xuICAgIFwiV2hldGhlciBsb2dvdXQgd2FzIHN1Y2Nlc3NmdWxcIlxuICAgIHN1Y2Nlc3M6IEJvb2xlYW4hXG4gICAgXCJNZXNzYWdlIGFib3V0IHRoZSBsb2dvdXQgcmVzdWx0XCJcbiAgICBtZXNzYWdlOiBTdHJpbmdcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gIH1cblxuICB0eXBlIFdwZVNpdGUge1xuICAgIFwiSW5zdGFsbCBJRFwiXG4gICAgaWQ6IFN0cmluZyFcbiAgICBcIkluc3RhbGwgbmFtZVwiXG4gICAgbmFtZTogU3RyaW5nIVxuICAgIFwiRW52aXJvbm1lbnQgKHByb2R1Y3Rpb24sIHN0YWdpbmcsIGRldmVsb3BtZW50KVwiXG4gICAgZW52aXJvbm1lbnQ6IFN0cmluZyFcbiAgICBcIlBIUCB2ZXJzaW9uXCJcbiAgICBwaHBWZXJzaW9uOiBTdHJpbmdcbiAgICBcIlByaW1hcnkgZG9tYWluXCJcbiAgICBwcmltYXJ5RG9tYWluOiBTdHJpbmdcbiAgICBcIkFjY291bnQgSURcIlxuICAgIGFjY291bnRJZDogU3RyaW5nXG4gICAgXCJBY2NvdW50IG5hbWVcIlxuICAgIGFjY291bnROYW1lOiBTdHJpbmdcbiAgICBcIlNGVFAgaG9zdFwiXG4gICAgc2Z0cEhvc3Q6IFN0cmluZ1xuICAgIFwiU0ZUUCB1c2VyXCJcbiAgICBzZnRwVXNlcjogU3RyaW5nXG4gIH1cblxuICB0eXBlIExpc3RXcGVTaXRlc1Jlc3VsdCB7XG4gICAgXCJXaGV0aGVyIHF1ZXJ5IHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgICBcIkxpc3Qgb2YgV1AgRW5naW5lIHNpdGVzXCJcbiAgICBzaXRlczogW1dwZVNpdGUhXVxuICAgIFwiVG90YWwgY291bnQgb2Ygc2l0ZXNcIlxuICAgIGNvdW50OiBJbnRcbiAgfVxuXG4gICMgUGhhc2UgMTFiOiBTaXRlIExpbmtpbmcgVHlwZXNcbiAgdHlwZSBXcGVDb25uZWN0aW9uIHtcbiAgICBcIlJlbW90ZSBpbnN0YWxsIElEIChVVUlEIGZyb20gV1AgRW5naW5lKVwiXG4gICAgcmVtb3RlSW5zdGFsbElkOiBTdHJpbmchXG4gICAgXCJJbnN0YWxsIG5hbWUgKGh1bWFuLXJlYWRhYmxlLCB1c2VkIGluIHBvcnRhbCBVUkxzKVwiXG4gICAgaW5zdGFsbE5hbWU6IFN0cmluZ1xuICAgIFwiRW52aXJvbm1lbnQgKHByb2R1Y3Rpb24sIHN0YWdpbmcsIGRldmVsb3BtZW50KVwiXG4gICAgZW52aXJvbm1lbnQ6IFN0cmluZ1xuICAgIFwiQWNjb3VudCBJRFwiXG4gICAgYWNjb3VudElkOiBTdHJpbmdcbiAgICBcIldQIEVuZ2luZSBwb3J0YWwgVVJMXCJcbiAgICBwb3J0YWxVcmw6IFN0cmluZ1xuICAgIFwiUHJpbWFyeSBkb21haW4vQ05BTUVcIlxuICAgIHByaW1hcnlEb21haW46IFN0cmluZ1xuICB9XG5cbiAgXCJTeW5jIGNhcGFiaWxpdGllcyBhdmFpbGFibGUgZm9yIFdQRS1jb25uZWN0ZWQgc2l0ZXNcIlxuICB0eXBlIFdwZVN5bmNDYXBhYmlsaXRpZXMge1xuICAgIFwiV2hldGhlciB1c2VyIGNhbiBwdXNoIHRvIFdQIEVuZ2luZVwiXG4gICAgY2FuUHVzaDogQm9vbGVhbiFcbiAgICBcIldoZXRoZXIgdXNlciBjYW4gcHVsbCBmcm9tIFdQIEVuZ2luZVwiXG4gICAgY2FuUHVsbDogQm9vbGVhbiFcbiAgICBcIkF2YWlsYWJsZSBzeW5jIG1vZGVzXCJcbiAgICBzeW5jTW9kZXM6IFtTdHJpbmchXSFcbiAgICBcIldoZXRoZXIgTWFnaWMgU3luYyAoc2VsZWN0IGZpbGVzKSBpcyBhdmFpbGFibGVcIlxuICAgIG1hZ2ljU3luY0F2YWlsYWJsZTogQm9vbGVhbiFcbiAgICBcIldoZXRoZXIgZGF0YWJhc2Ugc3luYyBpcyBhdmFpbGFibGVcIlxuICAgIGRhdGFiYXNlU3luY0F2YWlsYWJsZTogQm9vbGVhbiFcbiAgfVxuXG4gIHR5cGUgR2V0V3BlTGlua1Jlc3VsdCB7XG4gICAgXCJXaGV0aGVyIHNpdGUgaXMgbGlua2VkIHRvIFdQIEVuZ2luZVwiXG4gICAgbGlua2VkOiBCb29sZWFuIVxuICAgIFwiU2l0ZSBuYW1lXCJcbiAgICBzaXRlTmFtZTogU3RyaW5nXG4gICAgXCJXUCBFbmdpbmUgY29ubmVjdGlvbnNcIlxuICAgIGNvbm5lY3Rpb25zOiBbV3BlQ29ubmVjdGlvbiFdXG4gICAgXCJOdW1iZXIgb2YgY29ubmVjdGlvbnNcIlxuICAgIGNvbm5lY3Rpb25Db3VudDogSW50XG4gICAgXCJTeW5jIGNhcGFiaWxpdGllcyAob25seSBwcmVzZW50IGlmIGxpbmtlZClcIlxuICAgIGNhcGFiaWxpdGllczogV3BlU3luY0NhcGFiaWxpdGllc1xuICAgIFwiTWVzc2FnZSAoZm9yIHVubGlua2VkIHNpdGVzKVwiXG4gICAgbWVzc2FnZTogU3RyaW5nXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICB9XG5cbiAgIyBQaGFzZSAxMWM6IFN5bmMgT3BlcmF0aW9ucyBUeXBlc1xuICB0eXBlIFN5bmNIaXN0b3J5RXZlbnQge1xuICAgIFwiUmVtb3RlIGluc3RhbGwgbmFtZVwiXG4gICAgcmVtb3RlSW5zdGFsbE5hbWU6IFN0cmluZ1xuICAgIFwiVW5peCB0aW1lc3RhbXBcIlxuICAgIHRpbWVzdGFtcDogRmxvYXQhXG4gICAgXCJFbnZpcm9ubWVudCAocHJvZHVjdGlvbiwgc3RhZ2luZywgZGV2ZWxvcG1lbnQpXCJcbiAgICBlbnZpcm9ubWVudDogU3RyaW5nIVxuICAgIFwiU3luYyBkaXJlY3Rpb25cIlxuICAgIGRpcmVjdGlvbjogU3RyaW5nIVxuICAgIFwiU3luYyBzdGF0dXNcIlxuICAgIHN0YXR1czogU3RyaW5nXG4gIH1cblxuICB0eXBlIEdldFN5bmNIaXN0b3J5UmVzdWx0IHtcbiAgICBcIldoZXRoZXIgdGhlIHF1ZXJ5IHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiU2l0ZSBuYW1lXCJcbiAgICBzaXRlTmFtZTogU3RyaW5nXG4gICAgXCJTeW5jIGhpc3RvcnkgZXZlbnRzXCJcbiAgICBldmVudHM6IFtTeW5jSGlzdG9yeUV2ZW50IV1cbiAgICBcIk51bWJlciBvZiBldmVudHNcIlxuICAgIGNvdW50OiBJbnRcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gIH1cblxuICB0eXBlIFN5bmNSZXN1bHQge1xuICAgIFwiV2hldGhlciB0aGUgc3luYyB3YXMgaW5pdGlhdGVkIHN1Y2Nlc3NmdWxseVwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIlN0YXR1cyBtZXNzYWdlXCJcbiAgICBtZXNzYWdlOiBTdHJpbmdcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gIH1cblxuICAjIEZpbGUgY2hhbmdlIGRldGVjdGlvblxuICB0eXBlIEZpbGVDaGFuZ2Uge1xuICAgIFwiRmlsZSBwYXRoIHJlbGF0aXZlIHRvIHNpdGUgcm9vdFwiXG4gICAgcGF0aDogU3RyaW5nIVxuICAgIFwiQ2hhbmdlIHR5cGU6IGNyZWF0ZSwgdXBsb2FkLCBkb3dubG9hZCwgZGVsZXRlLCBtb2RpZnlcIlxuICAgIGluc3RydWN0aW9uOiBTdHJpbmchXG4gICAgXCJGaWxlIHNpemUgaW4gYnl0ZXNcIlxuICAgIHNpemU6IEludFxuICAgIFwiRmlsZSB0eXBlOiAtIChmaWxlKSBvciBkIChkaXJlY3RvcnkpXCJcbiAgICB0eXBlOiBTdHJpbmdcbiAgfVxuXG4gIHR5cGUgR2V0U2l0ZUNoYW5nZXNSZXN1bHQge1xuICAgIFwiV2hldGhlciB0aGUgcXVlcnkgd2FzIHN1Y2Nlc3NmdWxcIlxuICAgIHN1Y2Nlc3M6IEJvb2xlYW4hXG4gICAgXCJTaXRlIG5hbWVcIlxuICAgIHNpdGVOYW1lOiBTdHJpbmdcbiAgICBcIkRpcmVjdGlvbiBvZiBjb21wYXJpc29uXCJcbiAgICBkaXJlY3Rpb246IFN0cmluZ1xuICAgIFwiRmlsZXMgdGhhdCB3b3VsZCBiZSBhZGRlZC91cGxvYWRlZFwiXG4gICAgYWRkZWQ6IFtGaWxlQ2hhbmdlIV1cbiAgICBcIkZpbGVzIHRoYXQgd291bGQgYmUgbW9kaWZpZWRcIlxuICAgIG1vZGlmaWVkOiBbRmlsZUNoYW5nZSFdXG4gICAgXCJGaWxlcyB0aGF0IHdvdWxkIGJlIGRlbGV0ZWRcIlxuICAgIGRlbGV0ZWQ6IFtGaWxlQ2hhbmdlIV1cbiAgICBcIlRvdGFsIG51bWJlciBvZiBjaGFuZ2VzXCJcbiAgICB0b3RhbENoYW5nZXM6IEludFxuICAgIFwiU3VtbWFyeSBtZXNzYWdlXCJcbiAgICBtZXNzYWdlOiBTdHJpbmdcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gIH1cblxuICAjIFBoYXNlIDEwOiBDbG91ZCBCYWNrdXAgVHlwZXNcbiAgdHlwZSBCYWNrdXBQcm92aWRlclN0YXR1cyB7XG4gICAgXCJXaGV0aGVyIGF1dGhlbnRpY2F0ZWQgd2l0aCBwcm92aWRlclwiXG4gICAgYXV0aGVudGljYXRlZDogQm9vbGVhbiFcbiAgICBcIkFjY291bnQgSURcIlxuICAgIGFjY291bnRJZDogU3RyaW5nXG4gICAgXCJBY2NvdW50IGVtYWlsXCJcbiAgICBlbWFpbDogU3RyaW5nXG4gIH1cblxuICB0eXBlIEJhY2t1cFN0YXR1c1Jlc3VsdCB7XG4gICAgXCJXaGV0aGVyIGJhY2t1cHMgYXJlIGF2YWlsYWJsZVwiXG4gICAgYXZhaWxhYmxlOiBCb29sZWFuIVxuICAgIFwiV2hldGhlciB0aGUgZmVhdHVyZSBpcyBlbmFibGVkXCJcbiAgICBmZWF0dXJlRW5hYmxlZDogQm9vbGVhbiFcbiAgICBcIkRyb3Bib3ggYXV0aGVudGljYXRpb24gc3RhdHVzXCJcbiAgICBkcm9wYm94OiBCYWNrdXBQcm92aWRlclN0YXR1c1xuICAgIFwiR29vZ2xlIERyaXZlIGF1dGhlbnRpY2F0aW9uIHN0YXR1c1wiXG4gICAgZ29vZ2xlRHJpdmU6IEJhY2t1cFByb3ZpZGVyU3RhdHVzXG4gICAgXCJNZXNzYWdlIGlmIGJhY2t1cHMgdW5hdmFpbGFibGVcIlxuICAgIG1lc3NhZ2U6IFN0cmluZ1xuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgfVxuXG4gIHR5cGUgQmFja3VwTWV0YWRhdGEge1xuICAgIFwiU25hcHNob3QgSURcIlxuICAgIHNuYXBzaG90SWQ6IFN0cmluZyFcbiAgICBcIkJhY2t1cCB0aW1lc3RhbXAgKElTTyBmb3JtYXQpXCJcbiAgICB0aW1lc3RhbXA6IFN0cmluZ1xuICAgIFwiQmFja3VwIG5vdGUvZGVzY3JpcHRpb25cIlxuICAgIG5vdGU6IFN0cmluZ1xuICAgIFwiU2l0ZSBkb21haW5cIlxuICAgIHNpdGVEb21haW46IFN0cmluZ1xuICAgIFwiU2VydmljZXMgaW5mbyAoSlNPTilcIlxuICAgIHNlcnZpY2VzOiBTdHJpbmdcbiAgfVxuXG4gIHR5cGUgTGlzdEJhY2t1cHNSZXN1bHQge1xuICAgIFwiV2hldGhlciBxdWVyeSB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIlNpdGUgbmFtZVwiXG4gICAgc2l0ZU5hbWU6IFN0cmluZ1xuICAgIFwiQmFja3VwIHByb3ZpZGVyXCJcbiAgICBwcm92aWRlcjogU3RyaW5nXG4gICAgXCJMaXN0IG9mIGJhY2t1cHNcIlxuICAgIGJhY2t1cHM6IFtCYWNrdXBNZXRhZGF0YSFdXG4gICAgXCJOdW1iZXIgb2YgYmFja3Vwc1wiXG4gICAgY291bnQ6IEludFxuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgfVxuXG4gIHR5cGUgQ3JlYXRlQmFja3VwUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgYmFja3VwIHdhcyBjcmVhdGVkIHN1Y2Nlc3NmdWxseVwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIlNuYXBzaG90IElEXCJcbiAgICBzbmFwc2hvdElkOiBTdHJpbmdcbiAgICBcIkJhY2t1cCB0aW1lc3RhbXBcIlxuICAgIHRpbWVzdGFtcDogU3RyaW5nXG4gICAgXCJTdGF0dXMgbWVzc2FnZVwiXG4gICAgbWVzc2FnZTogU3RyaW5nXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICB9XG5cbiAgdHlwZSBSZXN0b3JlQmFja3VwUmVzdWx0IHtcbiAgICBcIldoZXRoZXIgcmVzdG9yZSB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIlN0YXR1cyBtZXNzYWdlXCJcbiAgICBtZXNzYWdlOiBTdHJpbmdcbiAgICBcIkVycm9yIG1lc3NhZ2UgaWYgZmFpbGVkXCJcbiAgICBlcnJvcjogU3RyaW5nXG4gIH1cblxuICB0eXBlIERlbGV0ZUJhY2t1cFJlc3VsdCB7XG4gICAgXCJXaGV0aGVyIGRlbGV0aW9uIHdhcyBzdWNjZXNzZnVsXCJcbiAgICBzdWNjZXNzOiBCb29sZWFuIVxuICAgIFwiRGVsZXRlZCBzbmFwc2hvdCBJRFwiXG4gICAgZGVsZXRlZFNuYXBzaG90SWQ6IFN0cmluZ1xuICAgIFwiU3RhdHVzIG1lc3NhZ2VcIlxuICAgIG1lc3NhZ2U6IFN0cmluZ1xuICAgIFwiRXJyb3IgbWVzc2FnZSBpZiBmYWlsZWRcIlxuICAgIGVycm9yOiBTdHJpbmdcbiAgfVxuXG4gIHR5cGUgRG93bmxvYWRCYWNrdXBSZXN1bHQge1xuICAgIFwiV2hldGhlciBkb3dubG9hZCB3YXMgc3VjY2Vzc2Z1bFwiXG4gICAgc3VjY2VzczogQm9vbGVhbiFcbiAgICBcIlBhdGggdG8gZG93bmxvYWRlZCBmaWxlXCJcbiAgICBmaWxlUGF0aDogU3RyaW5nXG4gICAgXCJTdGF0dXMgbWVzc2FnZVwiXG4gICAgbWVzc2FnZTogU3RyaW5nXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICB9XG5cbiAgdHlwZSBFZGl0QmFja3VwTm90ZVJlc3VsdCB7XG4gICAgXCJXaGV0aGVyIGVkaXQgd2FzIHN1Y2Nlc3NmdWxcIlxuICAgIHN1Y2Nlc3M6IEJvb2xlYW4hXG4gICAgXCJVcGRhdGVkIHNuYXBzaG90IElEXCJcbiAgICBzbmFwc2hvdElkOiBTdHJpbmdcbiAgICBcIlVwZGF0ZWQgbm90ZVwiXG4gICAgbm90ZTogU3RyaW5nXG4gICAgXCJFcnJvciBtZXNzYWdlIGlmIGZhaWxlZFwiXG4gICAgZXJyb3I6IFN0cmluZ1xuICB9XG5cbiAgZXh0ZW5kIHR5cGUgUXVlcnkge1xuICAgICMgUGhhc2UgMTA6IENsb3VkIEJhY2t1cHNcbiAgICBcIkNoZWNrIGlmIGNsb3VkIGJhY2t1cHMgYXJlIGF2YWlsYWJsZSBhbmQgYXV0aGVudGljYXRlZFwiXG4gICAgYmFja3VwU3RhdHVzOiBCYWNrdXBTdGF0dXNSZXN1bHQhXG5cbiAgICBcIkxpc3QgYWxsIGJhY2t1cHMgZm9yIGEgc2l0ZVwiXG4gICAgbGlzdEJhY2t1cHMoc2l0ZUlkOiBJRCEsIHByb3ZpZGVyOiBTdHJpbmchKTogTGlzdEJhY2t1cHNSZXN1bHQhXG5cbiAgICAjIFBoYXNlIDExYzogU3luYyBPcGVyYXRpb25zXG4gICAgXCJHZXQgc3luYyBoaXN0b3J5IGZvciBhIGxvY2FsIHNpdGVcIlxuICAgIGdldFN5bmNIaXN0b3J5KHNpdGVJZDogSUQhLCBsaW1pdDogSW50KTogR2V0U3luY0hpc3RvcnlSZXN1bHQhXG5cbiAgICBcIkdldCBmaWxlIGNoYW5nZXMgYmV0d2VlbiBsb2NhbCBzaXRlIGFuZCBXUCBFbmdpbmUgKGRyeS1ydW4gY29tcGFyaXNvbilcIlxuICAgIGdldFNpdGVDaGFuZ2VzKHNpdGVJZDogSUQhLCBkaXJlY3Rpb246IFN0cmluZyA9IFwicHVzaFwiKTogR2V0U2l0ZUNoYW5nZXNSZXN1bHQhXG4gIH1cblxuICBleHRlbmQgdHlwZSBNdXRhdGlvbiB7XG4gICAgIyBQaGFzZSAxMDogQ2xvdWQgQmFja3Vwc1xuICAgIFwiQ3JlYXRlIGEgYmFja3VwIG9mIGEgc2l0ZSB0byBjbG91ZCBzdG9yYWdlXCJcbiAgICBjcmVhdGVCYWNrdXAoc2l0ZUlkOiBJRCEsIHByb3ZpZGVyOiBTdHJpbmchLCBub3RlOiBTdHJpbmcpOiBDcmVhdGVCYWNrdXBSZXN1bHQhXG5cbiAgICBcIlJlc3RvcmUgYSBzaXRlIGZyb20gYSBjbG91ZCBiYWNrdXBcIlxuICAgIHJlc3RvcmVCYWNrdXAoXG4gICAgICBzaXRlSWQ6IElEIVxuICAgICAgcHJvdmlkZXI6IFN0cmluZyFcbiAgICAgIHNuYXBzaG90SWQ6IFN0cmluZyFcbiAgICAgIGNvbmZpcm06IEJvb2xlYW4gPSBmYWxzZVxuICAgICk6IFJlc3RvcmVCYWNrdXBSZXN1bHQhXG5cbiAgICBcIkRlbGV0ZSBhIGJhY2t1cCBmcm9tIGNsb3VkIHN0b3JhZ2VcIlxuICAgIGRlbGV0ZUJhY2t1cChcbiAgICAgIHNpdGVJZDogSUQhXG4gICAgICBwcm92aWRlcjogU3RyaW5nIVxuICAgICAgc25hcHNob3RJZDogU3RyaW5nIVxuICAgICAgY29uZmlybTogQm9vbGVhbiA9IGZhbHNlXG4gICAgKTogRGVsZXRlQmFja3VwUmVzdWx0IVxuXG4gICAgXCJEb3dubG9hZCBhIGJhY2t1cCBhcyBhIFpJUCBmaWxlXCJcbiAgICBkb3dubG9hZEJhY2t1cChzaXRlSWQ6IElEISwgcHJvdmlkZXI6IFN0cmluZyEsIHNuYXBzaG90SWQ6IFN0cmluZyEpOiBEb3dubG9hZEJhY2t1cFJlc3VsdCFcblxuICAgIFwiVXBkYXRlIHRoZSBub3RlL2Rlc2NyaXB0aW9uIGZvciBhIGJhY2t1cFwiXG4gICAgZWRpdEJhY2t1cE5vdGUoXG4gICAgICBzaXRlSWQ6IElEIVxuICAgICAgcHJvdmlkZXI6IFN0cmluZyFcbiAgICAgIHNuYXBzaG90SWQ6IFN0cmluZyFcbiAgICAgIG5vdGU6IFN0cmluZyFcbiAgICApOiBFZGl0QmFja3VwTm90ZVJlc3VsdCFcblxuICAgICMgUGhhc2UgMTE6IFdQIEVuZ2luZSBDb25uZWN0XG4gICAgXCJBdXRoZW50aWNhdGUgd2l0aCBXUCBFbmdpbmUgKG9wZW5zIGJyb3dzZXIgZm9yIE9BdXRoKVwiXG4gICAgd3BlQXV0aGVudGljYXRlOiBXcGVBdXRoUmVzdWx0IVxuXG4gICAgXCJMb2dvdXQgZnJvbSBXUCBFbmdpbmVcIlxuICAgIHdwZUxvZ291dDogV3BlTG9nb3V0UmVzdWx0IVxuXG4gICAgIyBQaGFzZSAxMWM6IFN5bmMgT3BlcmF0aW9uc1xuICAgIFwiUHVzaCBsb2NhbCBzaXRlIHRvIFdQIEVuZ2luZVwiXG4gICAgcHVzaFRvV3BlKFxuICAgICAgbG9jYWxTaXRlSWQ6IElEIVxuICAgICAgcmVtb3RlSW5zdGFsbElkOiBJRCFcbiAgICAgIGluY2x1ZGVTcWw6IEJvb2xlYW4gPSBmYWxzZVxuICAgICAgY29uZmlybTogQm9vbGVhbiA9IGZhbHNlXG4gICAgKTogU3luY1Jlc3VsdCFcblxuICAgIFwiUHVsbCBmcm9tIFdQIEVuZ2luZSB0byBsb2NhbCBzaXRlXCJcbiAgICBwdWxsRnJvbVdwZShsb2NhbFNpdGVJZDogSUQhLCByZW1vdGVJbnN0YWxsSWQ6IElEISwgaW5jbHVkZVNxbDogQm9vbGVhbiA9IGZhbHNlKTogU3luY1Jlc3VsdCFcbiAgfVxuYDtcblxuLyoqXG4gKiBDcmVhdGUgR3JhcGhRTCByZXNvbHZlcnMgdGhhdCB1c2UgTG9jYWwncyBpbnRlcm5hbCBzZXJ2aWNlc1xuICovXG5mdW5jdGlvbiBjcmVhdGVSZXNvbHZlcnMoc2VydmljZXM6IGFueSkge1xuICBjb25zdCB7XG4gICAgZGVsZXRlU2l0ZTogZGVsZXRlU2l0ZVNlcnZpY2UsXG4gICAgc2l0ZURhdGEsXG4gICAgbG9jYWxMb2dnZXIsXG4gICAgd3BDbGksXG4gICAgc2l0ZVByb2Nlc3NNYW5hZ2VyLFxuICAgIGFkZFNpdGU6IGFkZFNpdGVTZXJ2aWNlLFxuICAgIGNsb25lU2l0ZTogY2xvbmVTaXRlU2VydmljZSxcbiAgICBleHBvcnRTaXRlOiBleHBvcnRTaXRlU2VydmljZSxcbiAgICBibHVlcHJpbnRzOiBibHVlcHJpbnRzU2VydmljZSxcbiAgICBicm93c2VyTWFuYWdlcixcbiAgICBhZG1pbmVyLFxuICAgIHg1MDlDZXJ0LFxuICAgIHNpdGVQcm92aXNpb25lcixcbiAgICBpbXBvcnRTaXRlOiBpbXBvcnRTaXRlU2VydmljZSxcbiAgICBsaWdodG5pbmdTZXJ2aWNlcyxcbiAgICBzaXRlRGF0YWJhc2UsXG4gICAgaW1wb3J0U1FMRmlsZTogaW1wb3J0U1FMRmlsZVNlcnZpY2UsXG4gICAgLy8gUGhhc2UgMTE6IFdQIEVuZ2luZSBDb25uZWN0XG4gICAgd3BlT0F1dGg6IHdwZU9BdXRoU2VydmljZSxcbiAgICBjYXBpOiBjYXBpU2VydmljZSxcbiAgICAvLyBQaGFzZSAxMWM6IFN5bmMgc2VydmljZXNcbiAgICB3cGVQdXNoOiB3cGVQdXNoU2VydmljZSxcbiAgICB3cGVQdWxsOiB3cGVQdWxsU2VydmljZSxcbiAgICBjb25uZWN0SGlzdG9yeTogY29ubmVjdEhpc3RvcnlTZXJ2aWNlLFxuICAgIHdwZUNvbm5lY3RCYXNlOiB3cGVDb25uZWN0QmFzZVNlcnZpY2UsXG4gICAgLy8gTm90ZTogUGhhc2UgMTAgQ2xvdWQgQmFja3VwIHNlcnZpY2VzIGFyZSBhY2Nlc3NlZCB2aWEgSVBDIHRvIHRoZSBDbG91ZCBCYWNrdXBzIGFkZG9uXG4gICAgLy8gKGJhY2t1cFNlcnZpY2UsIGRyb3Bib3gsIGdvb2dsZURyaXZlLCBmZWF0dXJlRmxhZ3MsIHVzZXJEYXRhKVxuICB9ID0gc2VydmljZXM7XG5cbiAgLy8gSGVscGVyIHRvIGludm9rZSBJUEMgY2FsbHMgdG8gdGhlIENsb3VkIEJhY2t1cHMgYWRkb25cbiAgLy8gVGhpcyB1c2VzIHRoZSBzYW1lIHBhdHRlcm4gYXMgdGhlIEJhY2t1cEFJQnJpZGdlXG4gIC8vIFRpbWVvdXQgY29uc3RhbnRzIGZvciBiYWNrdXAgb3BlcmF0aW9ucyAoaW4gbWlsbGlzZWNvbmRzKVxuICBjb25zdCBCQUNLVVBfSVBDX1RJTUVPVVQgPSA2MDAwMDA7IC8vIDEwIG1pbnV0ZXMgZm9yIGJhY2t1cCBvcGVyYXRpb25zXG4gIGNvbnN0IERFRkFVTFRfSVBDX1RJTUVPVVQgPSAzMDAwMDsgLy8gMzAgc2Vjb25kcyBmb3IgcXVpY2sgb3BlcmF0aW9uc1xuXG4gIGNvbnN0IGludm9rZUJhY2t1cElQQyA9IGFzeW5jIChcbiAgICBjaGFubmVsOiBzdHJpbmcsXG4gICAgdGltZW91dE1zOiBudW1iZXIgPSBCQUNLVVBfSVBDX1RJTUVPVVQsXG4gICAgLi4uYXJnczogYW55W11cbiAgKTogUHJvbWlzZTxhbnk+ID0+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgdGltZXN0YW1wID0gRGF0ZS5ub3coKTtcbiAgICAgIGNvbnN0IHJhbmRvbSA9IE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cigyLCA5KTtcbiAgICAgIGNvbnN0IHN1Y2Nlc3NSZXBseUNoYW5uZWwgPSBgJHtjaGFubmVsfS1zdWNjZXNzLSR7dGltZXN0YW1wfS0ke3JhbmRvbX1gO1xuICAgICAgY29uc3QgZXJyb3JSZXBseUNoYW5uZWwgPSBgJHtjaGFubmVsfS1lcnJvci0ke3RpbWVzdGFtcH0tJHtyYW5kb219YDtcblxuICAgICAgY29uc3QgdGltZW91dFNlY29uZHMgPSBNYXRoLnJvdW5kKHRpbWVvdXRNcyAvIDEwMDApO1xuICAgICAgY29uc3QgdGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICBpcGNNYWluLnJlbW92ZUFsbExpc3RlbmVycyhzdWNjZXNzUmVwbHlDaGFubmVsKTtcbiAgICAgICAgaXBjTWFpbi5yZW1vdmVBbGxMaXN0ZW5lcnMoZXJyb3JSZXBseUNoYW5uZWwpO1xuICAgICAgICByZWplY3QobmV3IEVycm9yKGBJUEMgY2FsbCB0byAke2NoYW5uZWx9IHRpbWVkIG91dCBhZnRlciAke3RpbWVvdXRTZWNvbmRzfSBzZWNvbmRzYCkpO1xuICAgICAgfSwgdGltZW91dE1zKTtcblxuICAgICAgaXBjTWFpbi5vbmNlKHN1Y2Nlc3NSZXBseUNoYW5uZWwsIChfZXZlbnQ6IGFueSwgcmVzdWx0OiBhbnkpID0+IHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgICBpcGNNYWluLnJlbW92ZUFsbExpc3RlbmVycyhlcnJvclJlcGx5Q2hhbm5lbCk7XG4gICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBJUEMgc3VjY2VzcyBmcm9tICR7Y2hhbm5lbH1gKTtcbiAgICAgICAgcmVzb2x2ZSh7IHJlc3VsdCB9KTtcbiAgICAgIH0pO1xuXG4gICAgICBpcGNNYWluLm9uY2UoZXJyb3JSZXBseUNoYW5uZWwsIChfZXZlbnQ6IGFueSwgZXJyb3I6IGFueSkgPT4ge1xuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dCk7XG4gICAgICAgIGlwY01haW4ucmVtb3ZlQWxsTGlzdGVuZXJzKHN1Y2Nlc3NSZXBseUNoYW5uZWwpO1xuICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIElQQyBlcnJvciBmcm9tICR7Y2hhbm5lbH06ICR7ZXJyb3I/Lm1lc3NhZ2V9YCk7XG4gICAgICAgIHJlc29sdmUoeyBlcnJvciB9KTtcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCBtb2NrRXZlbnQgPSB7XG4gICAgICAgIHJlcGx5OiAocmVwbHlDaGFubmVsOiBzdHJpbmcsIGRhdGE6IGFueSkgPT4ge1xuICAgICAgICAgIGlwY01haW4uZW1pdChyZXBseUNoYW5uZWwsIG51bGwsIGRhdGEpO1xuICAgICAgICB9LFxuICAgICAgICBzZW5kZXI6IHtcbiAgICAgICAgICBzZW5kOiAocmVwbHlDaGFubmVsOiBzdHJpbmcsIGRhdGE6IGFueSkgPT4ge1xuICAgICAgICAgICAgaXBjTWFpbi5lbWl0KHJlcGx5Q2hhbm5lbCwgbnVsbCwgZGF0YSk7XG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgIH07XG5cbiAgICAgIGNvbnN0IHJlcGx5Q2hhbm5lbHMgPSB7IHN1Y2Nlc3NSZXBseUNoYW5uZWwsIGVycm9yUmVwbHlDaGFubmVsIH07XG4gICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gSW52b2tpbmcgYmFja3VwIElQQzogJHtjaGFubmVsfWApO1xuICAgICAgaXBjTWFpbi5lbWl0KGNoYW5uZWwsIG1vY2tFdmVudCwgcmVwbHlDaGFubmVscywgLi4uYXJncyk7XG4gICAgfSk7XG4gIH07XG5cbiAgLy8gSGVscGVyIHRvIGdldCBiYWNrdXAgcHJvdmlkZXJzIGZyb20gdGhlIENsb3VkIEJhY2t1cHMgYWRkb25cbiAgY29uc3QgZ2V0QmFja3VwUHJvdmlkZXJzID0gYXN5bmMgKCk6IFByb21pc2U8QXJyYXk8eyBpZDogc3RyaW5nOyBuYW1lOiBzdHJpbmcgfT4+ID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgaW52b2tlQmFja3VwSVBDKCdiYWNrdXBzOmVuYWJsZWQtcHJvdmlkZXJzJywgREVGQVVMVF9JUENfVElNRU9VVCk7XG4gICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gUmF3IElQQyByZXN1bHQ6ICR7SlNPTi5zdHJpbmdpZnkocmVzdWx0KX1gKTtcblxuICAgICAgaWYgKHJlc3VsdC5lcnJvcikge1xuICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihcbiAgICAgICAgICBgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBnZXQgYmFja3VwIHByb3ZpZGVyczogJHtyZXN1bHQuZXJyb3IubWVzc2FnZX1gXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiBbXTtcbiAgICAgIH1cblxuICAgICAgLy8gVGhlIHJlc3BvbnNlIGlzIGRvdWJsZS1uZXN0ZWQ6IHJlc3VsdC5yZXN1bHQucmVzdWx0IGNvbnRhaW5zIHRoZSBhcnJheVxuICAgICAgLy8gU3RydWN0dXJlOiB7IHJlc3VsdDogeyByZXN1bHQ6IFtwcm92aWRlcnMuLi5dIH0gfVxuICAgICAgbGV0IHByb3ZpZGVyczogYW55ID0gcmVzdWx0LnJlc3VsdDtcblxuICAgICAgLy8gVW53cmFwIG5lc3RlZCByZXN1bHQgaWYgcHJlc2VudFxuICAgICAgaWYgKHByb3ZpZGVycyAmJiB0eXBlb2YgcHJvdmlkZXJzID09PSAnb2JqZWN0JyAmJiAhQXJyYXkuaXNBcnJheShwcm92aWRlcnMpKSB7XG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KHByb3ZpZGVycy5yZXN1bHQpKSB7XG4gICAgICAgICAgcHJvdmlkZXJzID0gcHJvdmlkZXJzLnJlc3VsdDtcbiAgICAgICAgfSBlbHNlIGlmIChwcm92aWRlcnMucmVzdWx0ICYmIHR5cGVvZiBwcm92aWRlcnMucmVzdWx0ID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgIC8vIEV2ZW4gZGVlcGVyIG5lc3RpbmdcbiAgICAgICAgICBwcm92aWRlcnMgPSBwcm92aWRlcnMucmVzdWx0O1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBFeHRyYWN0ZWQgcHJvdmlkZXJzOiAke0pTT04uc3RyaW5naWZ5KHByb3ZpZGVycyl9YCk7XG5cbiAgICAgIGlmIChBcnJheS5pc0FycmF5KHByb3ZpZGVycykpIHtcbiAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIEdvdCAke3Byb3ZpZGVycy5sZW5ndGh9IGJhY2t1cCBwcm92aWRlcnNgKTtcbiAgICAgICAgcmV0dXJuIHByb3ZpZGVycztcbiAgICAgIH1cblxuICAgICAgbG9jYWxMb2dnZXIud2FybihcbiAgICAgICAgYFske0FERE9OX05BTUV9XSBVbmV4cGVjdGVkIHByb3ZpZGVycyBmb3JtYXQgYWZ0ZXIgdW53cmFwcGluZzogJHt0eXBlb2YgcHJvdmlkZXJzfWBcbiAgICAgICk7XG4gICAgICByZXR1cm4gW107XG4gICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBFcnJvciBnZXR0aW5nIGJhY2t1cCBwcm92aWRlcnM6ICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH07XG5cbiAgLy8gU2hhcmVkIFdQLUNMSSBleGVjdXRpb24gbG9naWNcbiAgY29uc3QgZXhlY3V0ZVdwQ2xpID0gYXN5bmMgKFxuICAgIF9wYXJlbnQ6IGFueSxcbiAgICBhcmdzOiB7IGlucHV0OiB7IHNpdGVJZDogc3RyaW5nOyBhcmdzOiBzdHJpbmdbXTsgc2tpcFBsdWdpbnM/OiBib29sZWFuOyBza2lwVGhlbWVzPzogYm9vbGVhbiB9IH1cbiAgKSA9PiB7XG4gICAgY29uc3QgeyBzaXRlSWQsIGFyZ3M6IHdwQXJncywgc2tpcFBsdWdpbnMgPSB0cnVlLCBza2lwVGhlbWVzID0gdHJ1ZSB9ID0gYXJncy5pbnB1dDtcblxuICAgIHRyeSB7XG4gICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gUnVubmluZyBXUC1DTEk6IHdwICR7d3BBcmdzLmpvaW4oJyAnKX1gKTtcblxuICAgICAgY29uc3Qgc2l0ZSA9IHNpdGVEYXRhLmdldFNpdGUoc2l0ZUlkKTtcbiAgICAgIGlmICghc2l0ZSkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgIG91dHB1dDogbnVsbCxcbiAgICAgICAgICBlcnJvcjogYFNpdGUgbm90IGZvdW5kOiAke3NpdGVJZH1gLFxuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICBjb25zdCBzdGF0dXMgPSBhd2FpdCBzaXRlUHJvY2Vzc01hbmFnZXIuZ2V0U2l0ZVN0YXR1cyhzaXRlKTtcbiAgICAgIGlmIChzdGF0dXMgIT09ICdydW5uaW5nJykge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgIG91dHB1dDogbnVsbCxcbiAgICAgICAgICBlcnJvcjogYFNpdGUgXCIke3NpdGUubmFtZX1cIiBpcyBub3QgcnVubmluZy4gU3RhcnQgaXQgZmlyc3Qgd2l0aDogbG9jYWwtY2xpIHN0YXJ0ICR7c2l0ZS5uYW1lfWAsXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IG91dHB1dCA9IGF3YWl0IHdwQ2xpLnJ1bihzaXRlLCB3cEFyZ3MsIHtcbiAgICAgICAgc2tpcFBsdWdpbnMsXG4gICAgICAgIHNraXBUaGVtZXMsXG4gICAgICAgIGlnbm9yZUVycm9yczogZmFsc2UsXG4gICAgICB9KTtcblxuICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIFdQLUNMSSBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5YCk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgIG91dHB1dDogb3V0cHV0Py50cmltKCkgfHwgJycsXG4gICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgfTtcbiAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIFdQLUNMSSBmYWlsZWQ6YCwgZXJyb3IpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIG91dHB1dDogbnVsbCxcbiAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgfTtcbiAgICB9XG4gIH07XG5cbiAgcmV0dXJuIHtcbiAgICBRdWVyeToge1xuICAgICAgd3BDbGlRdWVyeTogZXhlY3V0ZVdwQ2xpLFxuXG4gICAgICBibHVlcHJpbnRzOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIEZldGNoaW5nIGJsdWVwcmludHNgKTtcblxuICAgICAgICAgIGNvbnN0IGJsdWVwcmludHNMaXN0ID0gYXdhaXQgYmx1ZXByaW50c1NlcnZpY2UuZ2V0Qmx1ZXByaW50cygpO1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICAgIGJsdWVwcmludHM6IGJsdWVwcmludHNMaXN0Lm1hcCgoYnA6IGFueSkgPT4gKHtcbiAgICAgICAgICAgICAgbmFtZTogYnAubmFtZSxcbiAgICAgICAgICAgICAgbGFzdE1vZGlmaWVkOiBicC5sYXN0TW9kaWZpZWQsXG4gICAgICAgICAgICAgIC8vIEhhbmRsZSBuZXN0ZWQgb2JqZWN0cyAtIGV4dHJhY3QganVzdCB0aGUgbmFtZS90eXBlIHN0cmluZ1xuICAgICAgICAgICAgICBwaHBWZXJzaW9uOlxuICAgICAgICAgICAgICAgIHR5cGVvZiBicC5waHBWZXJzaW9uID09PSAnb2JqZWN0J1xuICAgICAgICAgICAgICAgICAgPyBicC5waHBWZXJzaW9uPy5uYW1lIHx8IGJwLnBocFZlcnNpb24/LnZlcnNpb25cbiAgICAgICAgICAgICAgICAgIDogYnAucGhwVmVyc2lvbixcbiAgICAgICAgICAgICAgd2ViU2VydmVyOlxuICAgICAgICAgICAgICAgIHR5cGVvZiBicC53ZWJTZXJ2ZXIgPT09ICdvYmplY3QnXG4gICAgICAgICAgICAgICAgICA/IGJwLndlYlNlcnZlcj8ubmFtZSB8fCBicC53ZWJTZXJ2ZXI/LnR5cGVcbiAgICAgICAgICAgICAgICAgIDogYnAud2ViU2VydmVyLFxuICAgICAgICAgICAgICBkYXRhYmFzZTpcbiAgICAgICAgICAgICAgICB0eXBlb2YgYnAuZGF0YWJhc2UgPT09ICdvYmplY3QnXG4gICAgICAgICAgICAgICAgICA/IGJwLmRhdGFiYXNlPy5uYW1lIHx8IGJwLmRhdGFiYXNlPy50eXBlXG4gICAgICAgICAgICAgICAgICA6IGJwLmRhdGFiYXNlLFxuICAgICAgICAgICAgfSkpLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBmZXRjaCBibHVlcHJpbnRzOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgICBibHVlcHJpbnRzOiBbXSxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICBsaXN0U2VydmljZXM6IGFzeW5jIChfcGFyZW50OiBhbnksIGFyZ3M6IHsgdHlwZT86IHN0cmluZyB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgdHlwZSA9ICdhbGwnIH0gPSBhcmdzO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIExpc3Rpbmcgc2VydmljZXMgKHR5cGU6ICR7dHlwZX0pYCk7XG5cbiAgICAgICAgICBpZiAoIWxpZ2h0bmluZ1NlcnZpY2VzKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6ICdMaWdodG5pbmcgc2VydmljZXMgbm90IGF2YWlsYWJsZScsXG4gICAgICAgICAgICAgIHNlcnZpY2VzOiBbXSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3Qgcm9sZU1hcDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICAgICAgICAgIHBocDogJ3BocCcsXG4gICAgICAgICAgICBkYXRhYmFzZTogJ215c3FsJyxcbiAgICAgICAgICAgIHdlYnNlcnZlcjogJ25naW54JyxcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgY29uc3Qgcm9sZUZpbHRlciA9IHR5cGUgIT09ICdhbGwnID8gcm9sZU1hcFt0eXBlXSA6IHVuZGVmaW5lZDtcbiAgICAgICAgICBjb25zdCByZWdpc3RlcmVkU2VydmljZXMgPSBsaWdodG5pbmdTZXJ2aWNlcy5nZXRSZWdpc3RlcmVkU2VydmljZXMocm9sZUZpbHRlcik7XG5cbiAgICAgICAgICBjb25zdCBzZXJ2aWNlTGlzdDogQXJyYXk8eyByb2xlOiBzdHJpbmc7IG5hbWU6IHN0cmluZzsgdmVyc2lvbjogc3RyaW5nIH0+ID0gW107XG5cbiAgICAgICAgICBmb3IgKGNvbnN0IFtyb2xlLCB2ZXJzaW9uc10gb2YgT2JqZWN0LmVudHJpZXMocmVnaXN0ZXJlZFNlcnZpY2VzKSkge1xuICAgICAgICAgICAgZm9yIChjb25zdCBbdmVyc2lvbiwgaW5mb10gb2YgT2JqZWN0LmVudHJpZXModmVyc2lvbnMgYXMgUmVjb3JkPHN0cmluZywgYW55PikpIHtcbiAgICAgICAgICAgICAgc2VydmljZUxpc3QucHVzaCh7XG4gICAgICAgICAgICAgICAgcm9sZSxcbiAgICAgICAgICAgICAgICBuYW1lOiBpbmZvPy5uYW1lIHx8IHJvbGUsXG4gICAgICAgICAgICAgICAgdmVyc2lvbixcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICAgIHNlcnZpY2VzOiBzZXJ2aWNlTGlzdCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gbGlzdCBzZXJ2aWNlczpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgICAgc2VydmljZXM6IFtdLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIC8vIFBoYXNlIDExOiBXUCBFbmdpbmUgQ29ubmVjdFxuICAgICAgd3BlU3RhdHVzOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIENoZWNraW5nIFdQIEVuZ2luZSBhdXRoZW50aWNhdGlvbiBzdGF0dXNgKTtcblxuICAgICAgICAgIGlmICghd3BlT0F1dGhTZXJ2aWNlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBhdXRoZW50aWNhdGVkOiBmYWxzZSxcbiAgICAgICAgICAgICAgZW1haWw6IG51bGwsXG4gICAgICAgICAgICAgIGFjY291bnRJZDogbnVsbCxcbiAgICAgICAgICAgICAgYWNjb3VudE5hbWU6IG51bGwsXG4gICAgICAgICAgICAgIHRva2VuRXhwaXJ5OiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogJ1dQIEVuZ2luZSBPQXV0aCBzZXJ2aWNlIG5vdCBhdmFpbGFibGUnLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBDaGVjayBpZiB3ZSBoYXZlIHZhbGlkIGNyZWRlbnRpYWxzIGJ5IHRyeWluZyB0byBnZXQgYWNjZXNzIHRva2VuXG4gICAgICAgICAgY29uc3QgYWNjZXNzVG9rZW4gPSBhd2FpdCB3cGVPQXV0aFNlcnZpY2UuZ2V0QWNjZXNzVG9rZW4oKTtcblxuICAgICAgICAgIGlmICghYWNjZXNzVG9rZW4pIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIGF1dGhlbnRpY2F0ZWQ6IGZhbHNlLFxuICAgICAgICAgICAgICBlbWFpbDogbnVsbCxcbiAgICAgICAgICAgICAgYWNjb3VudElkOiBudWxsLFxuICAgICAgICAgICAgICBhY2NvdW50TmFtZTogbnVsbCxcbiAgICAgICAgICAgICAgdG9rZW5FeHBpcnk6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBUcnkgdG8gZ2V0IHVzZXIgaW5mbyBmcm9tIENBUEkgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgbGV0IGVtYWlsID0gbnVsbDtcbiAgICAgICAgICBpZiAoY2FwaVNlcnZpY2UpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGNvbnN0IGN1cnJlbnRVc2VyID0gYXdhaXQgY2FwaVNlcnZpY2UuZ2V0Q3VycmVudFVzZXIoKTtcbiAgICAgICAgICAgICAgZW1haWwgPSBjdXJyZW50VXNlcj8uZW1haWwgfHwgbnVsbDtcbiAgICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgICAvLyBVc2VyIGluZm8gbm90IGF2YWlsYWJsZSwgYnV0IHN0aWxsIGF1dGhlbnRpY2F0ZWRcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgYXV0aGVudGljYXRlZDogdHJ1ZSxcbiAgICAgICAgICAgIGVtYWlsLFxuICAgICAgICAgICAgYWNjb3VudElkOiBudWxsLFxuICAgICAgICAgICAgYWNjb3VudE5hbWU6IG51bGwsXG4gICAgICAgICAgICB0b2tlbkV4cGlyeTogbnVsbCxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBjaGVjayBXUEUgc3RhdHVzOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgYXV0aGVudGljYXRlZDogZmFsc2UsXG4gICAgICAgICAgICBlbWFpbDogbnVsbCxcbiAgICAgICAgICAgIGFjY291bnRJZDogbnVsbCxcbiAgICAgICAgICAgIGFjY291bnROYW1lOiBudWxsLFxuICAgICAgICAgICAgdG9rZW5FeHBpcnk6IG51bGwsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgbGlzdFdwZVNpdGVzOiBhc3luYyAoX3BhcmVudDogYW55LCBhcmdzOiB7IGFjY291bnRJZD86IHN0cmluZyB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgYWNjb3VudElkIH0gPSBhcmdzO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBbJHtBRERPTl9OQU1FfV0gTGlzdGluZyBXUCBFbmdpbmUgc2l0ZXMke2FjY291bnRJZCA/IGAgZm9yIGFjY291bnQgJHthY2NvdW50SWR9YCA6ICcnfWBcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgaWYgKCF3cGVPQXV0aFNlcnZpY2UpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogJ1dQIEVuZ2luZSBPQXV0aCBzZXJ2aWNlIG5vdCBhdmFpbGFibGUnLFxuICAgICAgICAgICAgICBzaXRlczogW10sXG4gICAgICAgICAgICAgIGNvdW50OiAwLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBDaGVjayBpZiBhdXRoZW50aWNhdGVkIGJ5IHRyeWluZyB0byBnZXQgYWNjZXNzIHRva2VuXG4gICAgICAgICAgY29uc3QgYWNjZXNzVG9rZW4gPSBhd2FpdCB3cGVPQXV0aFNlcnZpY2UuZ2V0QWNjZXNzVG9rZW4oKTtcbiAgICAgICAgICBpZiAoIWFjY2Vzc1Rva2VuKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6ICdOb3QgYXV0aGVudGljYXRlZCB3aXRoIFdQIEVuZ2luZS4gVXNlIHdwZV9hdXRoZW50aWNhdGUgZmlyc3QuJyxcbiAgICAgICAgICAgICAgc2l0ZXM6IFtdLFxuICAgICAgICAgICAgICBjb3VudDogMCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKCFjYXBpU2VydmljZSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIGVycm9yOiAnV1AgRW5naW5lIENBUEkgc2VydmljZSBub3QgYXZhaWxhYmxlJyxcbiAgICAgICAgICAgICAgc2l0ZXM6IFtdLFxuICAgICAgICAgICAgICBjb3VudDogMCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gR2V0IGluc3RhbGxzIGZyb20gQ0FQSSB1c2luZyBnZXRJbnN0YWxsTGlzdFxuICAgICAgICAgIGNvbnN0IGluc3RhbGxzID0gYXdhaXQgY2FwaVNlcnZpY2UuZ2V0SW5zdGFsbExpc3QoKTtcblxuICAgICAgICAgIGlmICghaW5zdGFsbHMpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgICBzaXRlczogW10sXG4gICAgICAgICAgICAgIGNvdW50OiAwLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBzaXRlcyA9IGluc3RhbGxzLm1hcCgoaW5zdGFsbDogYW55KSA9PiAoe1xuICAgICAgICAgICAgaWQ6IGluc3RhbGwuaWQsXG4gICAgICAgICAgICBuYW1lOiBpbnN0YWxsLm5hbWUsXG4gICAgICAgICAgICBlbnZpcm9ubWVudDogaW5zdGFsbC5lbnZpcm9ubWVudCB8fCAncHJvZHVjdGlvbicsXG4gICAgICAgICAgICBwaHBWZXJzaW9uOiBpbnN0YWxsLnBocFZlcnNpb24gfHwgbnVsbCxcbiAgICAgICAgICAgIHByaW1hcnlEb21haW46IGluc3RhbGwucHJpbWFyeURvbWFpbiB8fCBpbnN0YWxsLmNuYW1lIHx8IG51bGwsXG4gICAgICAgICAgICBhY2NvdW50SWQ6IGluc3RhbGwuYWNjb3VudElkIHx8IGFjY291bnRJZCB8fCBudWxsLFxuICAgICAgICAgICAgYWNjb3VudE5hbWU6IGluc3RhbGwuYWNjb3VudE5hbWUgfHwgbnVsbCxcbiAgICAgICAgICAgIHNmdHBIb3N0OiBgJHtpbnN0YWxsLm5hbWV9LnNzaC53cGVuZ2luZS5uZXRgLFxuICAgICAgICAgICAgc2Z0cFVzZXI6IGluc3RhbGwubmFtZSxcbiAgICAgICAgICB9KSk7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgc2l0ZXMsXG4gICAgICAgICAgICBjb3VudDogc2l0ZXMubGVuZ3RoLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBsaXN0IFdQRSBzaXRlczpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgICAgc2l0ZXM6IFtdLFxuICAgICAgICAgICAgY291bnQ6IDAsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgLy8gUGhhc2UgMTFiOiBTaXRlIExpbmtpbmdcbiAgICAgIGdldFdwZUxpbms6IGFzeW5jIChfcGFyZW50OiBhbnksIGFyZ3M6IHsgc2l0ZUlkOiBzdHJpbmcgfSkgPT4ge1xuICAgICAgICBjb25zdCB7IHNpdGVJZCB9ID0gYXJncztcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBHZXR0aW5nIFdQIEVuZ2luZSBsaW5rIGZvciBzaXRlICR7c2l0ZUlkfWApO1xuXG4gICAgICAgICAgLy8gR2V0IHNpdGUgZnJvbSBzaXRlRGF0YVxuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBsaW5rZWQ6IGZhbHNlLFxuICAgICAgICAgICAgICBzaXRlTmFtZTogbnVsbCxcbiAgICAgICAgICAgICAgY29ubmVjdGlvbnM6IFtdLFxuICAgICAgICAgICAgICBjb25uZWN0aW9uQ291bnQ6IDAsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7c2l0ZUlkfWAsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEdldCBob3N0Q29ubmVjdGlvbnMgZnJvbSBzaXRlXG4gICAgICAgICAgY29uc3QgaG9zdENvbm5lY3Rpb25zID0gc2l0ZS5ob3N0Q29ubmVjdGlvbnMgfHwgW107XG4gICAgICAgICAgY29uc3Qgd3BlQ29ubmVjdGlvbnMgPSBob3N0Q29ubmVjdGlvbnMuZmlsdGVyKChjOiBhbnkpID0+IGMuaG9zdElkID09PSAnd3BlJyk7XG5cbiAgICAgICAgICBpZiAod3BlQ29ubmVjdGlvbnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBsaW5rZWQ6IGZhbHNlLFxuICAgICAgICAgICAgICBzaXRlTmFtZTogc2l0ZS5uYW1lLFxuICAgICAgICAgICAgICBjb25uZWN0aW9uczogW10sXG4gICAgICAgICAgICAgIGNvbm5lY3Rpb25Db3VudDogMCxcbiAgICAgICAgICAgICAgbWVzc2FnZTpcbiAgICAgICAgICAgICAgICAnU2l0ZSBpcyBub3QgbGlua2VkIHRvIGFueSBXUCBFbmdpbmUgZW52aXJvbm1lbnQuIFVzZSBDb25uZWN0IGluIExvY2FsIHRvIHB1bGwgYSBzaXRlIGZyb20gV1BFLicsXG4gICAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBUcmFuc2Zvcm0gY29ubmVjdGlvbnMgZm9yIG91dHB1dCwgZW5yaWNoaW5nIHdpdGggQ0FQSSBkYXRhIGlmIGF2YWlsYWJsZVxuICAgICAgICAgIGNvbnN0IGNvbm5lY3Rpb25zID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgICAgICB3cGVDb25uZWN0aW9ucy5tYXAoYXN5bmMgKGM6IGFueSkgPT4ge1xuICAgICAgICAgICAgICBsZXQgaW5zdGFsbE5hbWUgPSBjLnJlbW90ZVNpdGVJZDsgLy8gRGVmYXVsdCB0byBVVUlEXG4gICAgICAgICAgICAgIGxldCBwb3J0YWxVcmwgPSBudWxsO1xuICAgICAgICAgICAgICBsZXQgcHJpbWFyeURvbWFpbiA9IG51bGw7XG5cbiAgICAgICAgICAgICAgLy8gVHJ5IHRvIGdldCBpbnN0YWxsIGRldGFpbHMgZnJvbSBDQVBJIHRvIGdldCB0aGUgYWN0dWFsIG5hbWVcbiAgICAgICAgICAgICAgLy8gcmVtb3RlU2l0ZUlkIG1hdGNoZXMgaW5zdGFsbC5zaXRlLmlkIChXUEUgU2l0ZSBJRCksIG5vdCBpbnN0YWxsLmlkXG4gICAgICAgICAgICAgIGlmIChjYXBpU2VydmljZSAmJiB0eXBlb2YgY2FwaVNlcnZpY2UuZ2V0SW5zdGFsbExpc3QgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgICAgICAgICAgYFske0FERE9OX05BTUV9XSBMb29raW5nIGZvciBpbnN0YWxsIHdpdGggc2l0ZS5pZD0ke2MucmVtb3RlU2l0ZUlkfSwgZW52PSR7Yy5yZW1vdGVTaXRlRW52fWBcbiAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICBjb25zdCBpbnN0YWxscyA9IGF3YWl0IGNhcGlTZXJ2aWNlLmdldEluc3RhbGxMaXN0KCk7XG4gICAgICAgICAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKFxuICAgICAgICAgICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIEdvdCAke2luc3RhbGxzPy5sZW5ndGggfHwgMH0gaW5zdGFsbHMgZnJvbSBDQVBJYFxuICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgIGlmIChpbnN0YWxscyAmJiBpbnN0YWxscy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIExvZyBmaXJzdCBpbnN0YWxsIHN0cnVjdHVyZSBmb3IgZGVidWdnaW5nXG4gICAgICAgICAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICAgICAgICAgICAgYFske0FERE9OX05BTUV9XSBTYW1wbGUgaW5zdGFsbCBzdHJ1Y3R1cmU6ICR7SlNPTi5zdHJpbmdpZnkoaW5zdGFsbHNbMF0sIG51bGwsIDIpfWBcbiAgICAgICAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyBNYXRjaCBieSBzaXRlLmlkIChyZW1vdGVTaXRlSWQgaXMgdGhlIFdQRSBTaXRlIElELCBub3QgSW5zdGFsbCBJRClcbiAgICAgICAgICAgICAgICAgICAgLy8gQWxzbyBmaWx0ZXIgYnkgZW52aXJvbm1lbnQgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IG1hdGNoaW5nSW5zdGFsbCA9IGluc3RhbGxzLmZpbmQoXG4gICAgICAgICAgICAgICAgICAgICAgKGk6IGFueSkgPT5cbiAgICAgICAgICAgICAgICAgICAgICAgIGkuc2l0ZT8uaWQgPT09IGMucmVtb3RlU2l0ZUlkICYmXG4gICAgICAgICAgICAgICAgICAgICAgICAoIWMucmVtb3RlU2l0ZUVudiB8fCBpLmVudmlyb25tZW50ID09PSBjLnJlbW90ZVNpdGVFbnYpXG4gICAgICAgICAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKG1hdGNoaW5nSW5zdGFsbCkge1xuICAgICAgICAgICAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBGb3VuZCBtYXRjaDogJHttYXRjaGluZ0luc3RhbGwubmFtZX1gKTtcbiAgICAgICAgICAgICAgICAgICAgICBpbnN0YWxsTmFtZSA9IG1hdGNoaW5nSW5zdGFsbC5uYW1lO1xuICAgICAgICAgICAgICAgICAgICAgIHBvcnRhbFVybCA9IGBodHRwczovL215LndwZW5naW5lLmNvbS9pbnN0YWxscy8ke21hdGNoaW5nSW5zdGFsbC5uYW1lfWA7XG4gICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeURvbWFpbiA9XG4gICAgICAgICAgICAgICAgICAgICAgICBtYXRjaGluZ0luc3RhbGwucHJpbWFyeV9kb21haW4gfHwgbWF0Y2hpbmdJbnN0YWxsLmNuYW1lIHx8IG51bGw7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgbG9jYWxMb2dnZXIud2FybihcbiAgICAgICAgICAgICAgICAgICAgICAgIGBbJHtBRERPTl9OQU1FfV0gTm8gbWF0Y2hpbmcgaW5zdGFsbCBmb3VuZCBmb3Igc2l0ZS5pZD0ke2MucmVtb3RlU2l0ZUlkfWBcbiAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgICAgICAgICAgICAgICBsb2NhbExvZ2dlci53YXJuKFxuICAgICAgICAgICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIENvdWxkIG5vdCBsb29rIHVwIGluc3RhbGwgZnJvbSBDQVBJOiAke2UubWVzc2FnZX1gXG4gICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBsb2NhbExvZ2dlci53YXJuKGBbJHtBRERPTl9OQU1FfV0gY2FwaVNlcnZpY2Ugb3IgZ2V0SW5zdGFsbExpc3Qgbm90IGF2YWlsYWJsZWApO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICByZW1vdGVJbnN0YWxsSWQ6IGMucmVtb3RlU2l0ZUlkLFxuICAgICAgICAgICAgICAgIGluc3RhbGxOYW1lLFxuICAgICAgICAgICAgICAgIGVudmlyb25tZW50OiBjLnJlbW90ZVNpdGVFbnYgfHwgbnVsbCxcbiAgICAgICAgICAgICAgICBhY2NvdW50SWQ6IGMuYWNjb3VudElkIHx8IG51bGwsXG4gICAgICAgICAgICAgICAgcG9ydGFsVXJsLFxuICAgICAgICAgICAgICAgIHByaW1hcnlEb21haW4sXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICk7XG5cbiAgICAgICAgICAvLyBDYXBhYmlsaXRpZXMgYXJlIGFsd2F5cyB0aGUgc2FtZSBmb3IgV1BFLWNvbm5lY3RlZCBzaXRlc1xuICAgICAgICAgIGNvbnN0IGNhcGFiaWxpdGllcyA9IHtcbiAgICAgICAgICAgIGNhblB1c2g6IHRydWUsXG4gICAgICAgICAgICBjYW5QdWxsOiB0cnVlLFxuICAgICAgICAgICAgc3luY01vZGVzOiBbJ2FsbF9maWxlcycsICdzZWxlY3RfZmlsZXMnLCAnZGF0YWJhc2Vfb25seSddLFxuICAgICAgICAgICAgbWFnaWNTeW5jQXZhaWxhYmxlOiB0cnVlLFxuICAgICAgICAgICAgZGF0YWJhc2VTeW5jQXZhaWxhYmxlOiB0cnVlLFxuICAgICAgICAgIH07XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgbGlua2VkOiB0cnVlLFxuICAgICAgICAgICAgc2l0ZU5hbWU6IHNpdGUubmFtZSxcbiAgICAgICAgICAgIGNvbm5lY3Rpb25zLFxuICAgICAgICAgICAgY29ubmVjdGlvbkNvdW50OiBjb25uZWN0aW9ucy5sZW5ndGgsXG4gICAgICAgICAgICBjYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGdldCBXUEUgbGluazpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGxpbmtlZDogZmFsc2UsXG4gICAgICAgICAgICBzaXRlTmFtZTogbnVsbCxcbiAgICAgICAgICAgIGNvbm5lY3Rpb25zOiBbXSxcbiAgICAgICAgICAgIGNvbm5lY3Rpb25Db3VudDogMCxcbiAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgLy8gUGhhc2UgMTA6IENsb3VkIEJhY2t1cHNcbiAgICAgIGJhY2t1cFN0YXR1czogYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBDaGVja2luZyBiYWNrdXAgc3RhdHVzYCk7XG5cbiAgICAgICAgICAvLyBHZXQgcHJvdmlkZXJzIGZyb20gQ2xvdWQgQmFja3VwcyBhZGRvbiB2aWEgSVBDXG4gICAgICAgICAgY29uc3QgcHJvdmlkZXJzID0gYXdhaXQgZ2V0QmFja3VwUHJvdmlkZXJzKCk7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIEdvdCAke3Byb3ZpZGVycy5sZW5ndGh9IGJhY2t1cCBwcm92aWRlcnNgKTtcblxuICAgICAgICAgIGlmIChwcm92aWRlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBhdmFpbGFibGU6IGZhbHNlLFxuICAgICAgICAgICAgICBmZWF0dXJlRW5hYmxlZDogZmFsc2UsXG4gICAgICAgICAgICAgIGRyb3Bib3g6IG51bGwsXG4gICAgICAgICAgICAgIGdvb2dsZURyaXZlOiBudWxsLFxuICAgICAgICAgICAgICBtZXNzYWdlOlxuICAgICAgICAgICAgICAgICdObyBjbG91ZCBzdG9yYWdlIHByb3ZpZGVycyBjb25maWd1cmVkLiBDb25uZWN0IEdvb2dsZSBEcml2ZSBvciBEcm9wYm94IGluIExvY2FsIEh1YiAoaHViLmxvY2Fsd3AuY29tL2FkZG9ucy9jbG91ZC1iYWNrdXBzKS4nLFxuICAgICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gTWFwIHByb3ZpZGVyIGluZm8gdG8gb3VyIHJlc3BvbnNlIGZvcm1hdFxuICAgICAgICAgIGNvbnN0IGRyb3Bib3hQcm92aWRlciA9IHByb3ZpZGVycy5maW5kKFxuICAgICAgICAgICAgKHA6IGFueSkgPT4gcC5pZCA9PT0gJ2Ryb3Bib3gnIHx8IHAubmFtZT8udG9Mb3dlckNhc2UoKS5pbmNsdWRlcygnZHJvcGJveCcpXG4gICAgICAgICAgKSBhcyBhbnk7XG4gICAgICAgICAgY29uc3QgZ29vZ2xlUHJvdmlkZXIgPSBwcm92aWRlcnMuZmluZChcbiAgICAgICAgICAgIChwOiBhbnkpID0+IHAuaWQgPT09ICdnb29nbGUnIHx8IHAubmFtZT8udG9Mb3dlckNhc2UoKS5pbmNsdWRlcygnZ29vZ2xlJylcbiAgICAgICAgICApIGFzIGFueTtcblxuICAgICAgICAgIGNvbnN0IGRyb3Bib3hTdGF0dXMgPSBkcm9wYm94UHJvdmlkZXJcbiAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgIGF1dGhlbnRpY2F0ZWQ6IHRydWUsXG4gICAgICAgICAgICAgICAgYWNjb3VudElkOiBkcm9wYm94UHJvdmlkZXIuaWQsXG4gICAgICAgICAgICAgICAgZW1haWw6IGRyb3Bib3hQcm92aWRlci5lbWFpbCB8fCBudWxsLFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA6IHtcbiAgICAgICAgICAgICAgICBhdXRoZW50aWNhdGVkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBhY2NvdW50SWQ6IG51bGwgYXMgc3RyaW5nIHwgbnVsbCxcbiAgICAgICAgICAgICAgICBlbWFpbDogbnVsbCBhcyBzdHJpbmcgfCBudWxsLFxuICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgY29uc3QgZ29vZ2xlRHJpdmVTdGF0dXMgPSBnb29nbGVQcm92aWRlclxuICAgICAgICAgICAgPyB7XG4gICAgICAgICAgICAgICAgYXV0aGVudGljYXRlZDogdHJ1ZSxcbiAgICAgICAgICAgICAgICBhY2NvdW50SWQ6IGdvb2dsZVByb3ZpZGVyLmlkLFxuICAgICAgICAgICAgICAgIGVtYWlsOiBnb29nbGVQcm92aWRlci5lbWFpbCB8fCBudWxsLFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA6IHtcbiAgICAgICAgICAgICAgICBhdXRoZW50aWNhdGVkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBhY2NvdW50SWQ6IG51bGwgYXMgc3RyaW5nIHwgbnVsbCxcbiAgICAgICAgICAgICAgICBlbWFpbDogbnVsbCBhcyBzdHJpbmcgfCBudWxsLFxuICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgY29uc3QgaGFzUHJvdmlkZXIgPSBwcm92aWRlcnMubGVuZ3RoID4gMDtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBhdmFpbGFibGU6IGhhc1Byb3ZpZGVyLFxuICAgICAgICAgICAgZmVhdHVyZUVuYWJsZWQ6IHRydWUsXG4gICAgICAgICAgICBkcm9wYm94OiBkcm9wYm94U3RhdHVzLFxuICAgICAgICAgICAgZ29vZ2xlRHJpdmU6IGdvb2dsZURyaXZlU3RhdHVzLFxuICAgICAgICAgICAgbWVzc2FnZTogaGFzUHJvdmlkZXJcbiAgICAgICAgICAgICAgPyBudWxsXG4gICAgICAgICAgICAgIDogJ05vIGNsb3VkIHN0b3JhZ2UgcHJvdmlkZXIgYXV0aGVudGljYXRlZC4gQ29ubmVjdCBEcm9wYm94IG9yIEdvb2dsZSBEcml2ZSBpbiBMb2NhbCBzZXR0aW5ncy4nLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGNoZWNrIGJhY2t1cCBzdGF0dXM6YCwgZXJyb3IpO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBhdmFpbGFibGU6IGZhbHNlLFxuICAgICAgICAgICAgZmVhdHVyZUVuYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgICAgZHJvcGJveDogbnVsbCxcbiAgICAgICAgICAgIGdvb2dsZURyaXZlOiBudWxsLFxuICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdVbmtub3duIGVycm9yJyxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICBsaXN0QmFja3VwczogYXN5bmMgKF9wYXJlbnQ6IGFueSwgYXJnczogeyBzaXRlSWQ6IHN0cmluZzsgcHJvdmlkZXI6IHN0cmluZyB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkLCBwcm92aWRlciB9ID0gYXJncztcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBMaXN0aW5nIGJhY2t1cHMgZm9yIHNpdGUgJHtzaXRlSWR9IGZyb20gJHtwcm92aWRlcn1gKTtcblxuICAgICAgICAgIC8vIEdldCBzaXRlXG4gICAgICAgICAgY29uc3Qgc2l0ZSA9IHNpdGVEYXRhLmdldFNpdGUoc2l0ZUlkKTtcbiAgICAgICAgICBpZiAoIXNpdGUpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBzaXRlTmFtZTogbnVsbCxcbiAgICAgICAgICAgICAgcHJvdmlkZXIsXG4gICAgICAgICAgICAgIGJhY2t1cHM6IFtdLFxuICAgICAgICAgICAgICBjb3VudDogMCxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIG5vdCBmb3VuZDogJHtzaXRlSWR9YCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gR2V0IHByb3ZpZGVycyBmcm9tIENsb3VkIEJhY2t1cHMgYWRkb25cbiAgICAgICAgICBjb25zdCBwcm92aWRlcnMgPSBhd2FpdCBnZXRCYWNrdXBQcm92aWRlcnMoKTtcbiAgICAgICAgICBpZiAocHJvdmlkZXJzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIHNpdGVOYW1lOiBzaXRlLm5hbWUsXG4gICAgICAgICAgICAgIHByb3ZpZGVyLFxuICAgICAgICAgICAgICBiYWNrdXBzOiBbXSxcbiAgICAgICAgICAgICAgY291bnQ6IDAsXG4gICAgICAgICAgICAgIGVycm9yOlxuICAgICAgICAgICAgICAgICdObyBjbG91ZCBzdG9yYWdlIHByb3ZpZGVycyBjb25maWd1cmVkLiBDb25uZWN0IEdvb2dsZSBEcml2ZSBvciBEcm9wYm94IGluIExvY2FsIEh1Yi4nLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBGaW5kIHRoZSBtYXRjaGluZyBwcm92aWRlciAobWFwICdnb29nbGVEcml2ZScgdG8gJ2dvb2dsZScgZm9yIHRoZSBhZGRvbilcbiAgICAgICAgICBjb25zdCBwcm92aWRlck1hcDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHsgZ29vZ2xlRHJpdmU6ICdnb29nbGUnLCBkcm9wYm94OiAnZHJvcGJveCcgfTtcbiAgICAgICAgICBjb25zdCBwcm92aWRlcklkID0gcHJvdmlkZXJNYXBbcHJvdmlkZXJdIHx8IHByb3ZpZGVyO1xuICAgICAgICAgIGNvbnN0IG1hdGNoZWRQcm92aWRlciA9IHByb3ZpZGVycy5maW5kKChwOiBhbnkpID0+IHAuaWQgPT09IHByb3ZpZGVySWQpO1xuXG4gICAgICAgICAgaWYgKCFtYXRjaGVkUHJvdmlkZXIpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBzaXRlTmFtZTogc2l0ZS5uYW1lLFxuICAgICAgICAgICAgICBwcm92aWRlcixcbiAgICAgICAgICAgICAgYmFja3VwczogW10sXG4gICAgICAgICAgICAgIGNvdW50OiAwLFxuICAgICAgICAgICAgICBlcnJvcjogYFByb3ZpZGVyICcke3Byb3ZpZGVyfScgbm90IGNvbmZpZ3VyZWQuIEF2YWlsYWJsZTogJHtwcm92aWRlcnMubWFwKChwOiBhbnkpID0+IHAubmFtZSkuam9pbignLCAnKX1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBGb3IgbGlzdGluZyBzbmFwc2hvdHMsIHVzZSB0aGUgSHViIHByb3ZpZGVyIElEIGRpcmVjdGx5IChlLmcuLCAnZ29vZ2xlJylcbiAgICAgICAgICAvLyBOT1QgdGhlIHJjbG9uZSBiYWNrZW5kIG5hbWUgKCdkcml2ZScpIC0gdGhlIEh1YiBxdWVyaWVzIGV4cGVjdCB0aGUgT0F1dGggcHJvdmlkZXIgbmFtZVxuICAgICAgICAgIC8vIEFsc28gcGFzcyBwYWdlT2Zmc2V0IHBhcmFtZXRlciAoMCBmb3IgZmlyc3QgcGFnZSlcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBpbnZva2VCYWNrdXBJUEMoXG4gICAgICAgICAgICAnYmFja3Vwczpwcm92aWRlci1zbmFwc2hvdHMnLFxuICAgICAgICAgICAgREVGQVVMVF9JUENfVElNRU9VVCxcbiAgICAgICAgICAgIHNpdGVJZCxcbiAgICAgICAgICAgIG1hdGNoZWRQcm92aWRlci5pZCxcbiAgICAgICAgICAgIDBcbiAgICAgICAgICApO1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIFByb3ZpZGVyIHNuYXBzaG90cyByYXcgcmVzdWx0OiAke0pTT04uc3RyaW5naWZ5KHJlc3VsdCl9YFxuICAgICAgICAgICk7XG5cbiAgICAgICAgICBpZiAocmVzdWx0LmVycm9yKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IHNpdGUubmFtZSxcbiAgICAgICAgICAgICAgcHJvdmlkZXIsXG4gICAgICAgICAgICAgIGJhY2t1cHM6IFtdLFxuICAgICAgICAgICAgICBjb3VudDogMCxcbiAgICAgICAgICAgICAgZXJyb3I6IHJlc3VsdC5lcnJvci5tZXNzYWdlIHx8ICdGYWlsZWQgdG8gbGlzdCBiYWNrdXBzJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gVW53cmFwIG5lc3RlZCByZXN1bHQgc3RydWN0dXJlIChzaW1pbGFyIHRvIHByb3ZpZGVycylcbiAgICAgICAgICBsZXQgYmFja3Vwc0RhdGEgPSByZXN1bHQucmVzdWx0O1xuICAgICAgICAgIGlmIChiYWNrdXBzRGF0YSAmJiB0eXBlb2YgYmFja3Vwc0RhdGEgPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGJhY2t1cHNEYXRhKSkge1xuICAgICAgICAgICAgLy8gQ2hlY2sgZm9yIG5lc3RlZCByZXN1bHQgb3Igc25hcHNob3RzIGFycmF5XG4gICAgICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShiYWNrdXBzRGF0YS5yZXN1bHQpKSB7XG4gICAgICAgICAgICAgIGJhY2t1cHNEYXRhID0gYmFja3Vwc0RhdGEucmVzdWx0O1xuICAgICAgICAgICAgfSBlbHNlIGlmIChBcnJheS5pc0FycmF5KGJhY2t1cHNEYXRhLnNuYXBzaG90cykpIHtcbiAgICAgICAgICAgICAgYmFja3Vwc0RhdGEgPSBiYWNrdXBzRGF0YS5zbmFwc2hvdHM7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGJhY2t1cHNEYXRhLnJlc3VsdCAmJiBBcnJheS5pc0FycmF5KGJhY2t1cHNEYXRhLnJlc3VsdC5zbmFwc2hvdHMpKSB7XG4gICAgICAgICAgICAgIGJhY2t1cHNEYXRhID0gYmFja3Vwc0RhdGEucmVzdWx0LnNuYXBzaG90cztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBiYWNrdXBzID0gQXJyYXkuaXNBcnJheShiYWNrdXBzRGF0YSkgPyBiYWNrdXBzRGF0YSA6IFtdO1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBFeHRyYWN0ZWQgJHtiYWNrdXBzLmxlbmd0aH0gYmFja3Vwc2ApO1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBzaXRlTmFtZTogc2l0ZS5uYW1lLFxuICAgICAgICAgICAgcHJvdmlkZXIsXG4gICAgICAgICAgICBiYWNrdXBzOiBiYWNrdXBzLm1hcCgoYjogYW55KSA9PiAoe1xuICAgICAgICAgICAgICAvLyBVc2UgaGFzaCBmb3Igc25hcHNob3RJZCBhcyB0aGF0J3Mgd2hhdCByZXN0aWMgdXNlcyBmb3IgcmVzdG9yZS9kZWxldGUgb3BlcmF0aW9uc1xuICAgICAgICAgICAgICAvLyBUaGUgSHViIElEIChiLmlkKSBpcyBqdXN0IGEgZGF0YWJhc2UgaWRlbnRpZmllclxuICAgICAgICAgICAgICBzbmFwc2hvdElkOiBiLmhhc2ggfHwgYi5zbmFwc2hvdElkIHx8IGIuc2hvcnRfaWQsXG4gICAgICAgICAgICAgIHRpbWVzdGFtcDogYi51cGRhdGVkQXQgfHwgYi5jcmVhdGVkQXQgfHwgYi50aW1lc3RhbXAgfHwgYi50aW1lIHx8IGIuY3JlYXRlZCxcbiAgICAgICAgICAgICAgbm90ZTpcbiAgICAgICAgICAgICAgICBiLmNvbmZpZ09iamVjdD8uZGVzY3JpcHRpb24gfHwgYi5ub3RlIHx8IGIuZGVzY3JpcHRpb24gfHwgYi50YWdzPy5kZXNjcmlwdGlvbiB8fCAnJyxcbiAgICAgICAgICAgICAgc2l0ZURvbWFpbjogYi5jb25maWdPYmplY3Q/Lm5hbWVcbiAgICAgICAgICAgICAgICA/IGAke2IuY29uZmlnT2JqZWN0Lm5hbWV9LmxvY2FsYFxuICAgICAgICAgICAgICAgIDogYi5zaXRlRG9tYWluIHx8IHNpdGUuZG9tYWluLFxuICAgICAgICAgICAgICBzZXJ2aWNlczogSlNPTi5zdHJpbmdpZnkoYi5jb25maWdPYmplY3Q/LnNlcnZpY2VzIHx8IGIuc2VydmljZXMgfHwge30pLFxuICAgICAgICAgICAgfSkpLFxuICAgICAgICAgICAgY291bnQ6IGJhY2t1cHMubGVuZ3RoLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGxpc3QgYmFja3VwczpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgc2l0ZU5hbWU6IG51bGwsXG4gICAgICAgICAgICBwcm92aWRlcixcbiAgICAgICAgICAgIGJhY2t1cHM6IFtdLFxuICAgICAgICAgICAgY291bnQ6IDAsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgLy8gUGhhc2UgMTFjOiBTeW5jIEhpc3RvcnlcbiAgICAgIGdldFN5bmNIaXN0b3J5OiBhc3luYyAoX3BhcmVudDogYW55LCBhcmdzOiB7IHNpdGVJZDogc3RyaW5nOyBsaW1pdD86IG51bWJlciB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkLCBsaW1pdCA9IDMwIH0gPSBhcmdzO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIEdldHRpbmcgc3luYyBoaXN0b3J5IGZvciBzaXRlICR7c2l0ZUlkfWApO1xuXG4gICAgICAgICAgLy8gR2V0IHNpdGUgdG8gdmVyaWZ5IGl0IGV4aXN0c1xuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IG51bGwsXG4gICAgICAgICAgICAgIGV2ZW50czogW10sXG4gICAgICAgICAgICAgIGNvdW50OiAwLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgbm90IGZvdW5kOiAke3NpdGVJZH1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBDaGVjayBpZiBjb25uZWN0SGlzdG9yeSBzZXJ2aWNlIGlzIGF2YWlsYWJsZVxuICAgICAgICAgIGlmICghY29ubmVjdEhpc3RvcnlTZXJ2aWNlIHx8IHR5cGVvZiBjb25uZWN0SGlzdG9yeVNlcnZpY2UuZ2V0RXZlbnRzICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IHNpdGUubmFtZSxcbiAgICAgICAgICAgICAgZXZlbnRzOiBbXSxcbiAgICAgICAgICAgICAgY291bnQ6IDAsXG4gICAgICAgICAgICAgIGVycm9yOiAnU3luYyBoaXN0b3J5IHNlcnZpY2Ugbm90IGF2YWlsYWJsZScsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IGV2ZW50cyA9IGNvbm5lY3RIaXN0b3J5U2VydmljZS5nZXRFdmVudHMoc2l0ZUlkKTtcbiAgICAgICAgICBjb25zdCBsaW1pdGVkRXZlbnRzID0gZXZlbnRzLnNsaWNlKDAsIGxpbWl0KTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgc2l0ZU5hbWU6IHNpdGUubmFtZSxcbiAgICAgICAgICAgIGV2ZW50czogbGltaXRlZEV2ZW50cy5tYXAoKGU6IGFueSkgPT4gKHtcbiAgICAgICAgICAgICAgcmVtb3RlSW5zdGFsbE5hbWU6IGUucmVtb3RlSW5zdGFsbE5hbWUgfHwgbnVsbCxcbiAgICAgICAgICAgICAgdGltZXN0YW1wOiBlLnRpbWVzdGFtcCxcbiAgICAgICAgICAgICAgZW52aXJvbm1lbnQ6IGUuZW52aXJvbm1lbnQsXG4gICAgICAgICAgICAgIGRpcmVjdGlvbjogZS5kaXJlY3Rpb24sXG4gICAgICAgICAgICAgIHN0YXR1czogZS5zdGF0dXMgfHwgbnVsbCxcbiAgICAgICAgICAgIH0pKSxcbiAgICAgICAgICAgIGNvdW50OiBsaW1pdGVkRXZlbnRzLmxlbmd0aCxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBnZXQgc3luYyBoaXN0b3J5OmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBzaXRlTmFtZTogbnVsbCxcbiAgICAgICAgICAgIGV2ZW50czogW10sXG4gICAgICAgICAgICBjb3VudDogMCxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdVbmtub3duIGVycm9yJyxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICAvLyBHZXQgZmlsZSBjaGFuZ2VzIGJldHdlZW4gbG9jYWwgYW5kIFdQRSAoZHJ5LXJ1biBjb21wYXJpc29uKVxuICAgICAgZ2V0U2l0ZUNoYW5nZXM6IGFzeW5jIChfcGFyZW50OiBhbnksIGFyZ3M6IHsgc2l0ZUlkOiBzdHJpbmc7IGRpcmVjdGlvbj86IHN0cmluZyB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkLCBkaXJlY3Rpb24gPSAncHVzaCcgfSA9IGFyZ3M7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKFxuICAgICAgICAgICAgYFske0FERE9OX05BTUV9XSBHZXR0aW5nIHNpdGUgY2hhbmdlcyBmb3IgJHtzaXRlSWR9LCBkaXJlY3Rpb249JHtkaXJlY3Rpb259YFxuICAgICAgICAgICk7XG5cbiAgICAgICAgICAvLyBWYWxpZGF0ZSBkaXJlY3Rpb25cbiAgICAgICAgICBpZiAoZGlyZWN0aW9uICE9PSAncHVzaCcgJiYgZGlyZWN0aW9uICE9PSAncHVsbCcpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBzaXRlTmFtZTogbnVsbCxcbiAgICAgICAgICAgICAgZGlyZWN0aW9uLFxuICAgICAgICAgICAgICBhZGRlZDogW10sXG4gICAgICAgICAgICAgIG1vZGlmaWVkOiBbXSxcbiAgICAgICAgICAgICAgZGVsZXRlZDogW10sXG4gICAgICAgICAgICAgIHRvdGFsQ2hhbmdlczogMCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6ICdJbnZhbGlkIGRpcmVjdGlvbi4gTXVzdCBiZSBcInB1c2hcIiBvciBcInB1bGxcIi4nLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBHZXQgc2l0ZVxuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IG51bGwsXG4gICAgICAgICAgICAgIGRpcmVjdGlvbixcbiAgICAgICAgICAgICAgYWRkZWQ6IFtdLFxuICAgICAgICAgICAgICBtb2RpZmllZDogW10sXG4gICAgICAgICAgICAgIGRlbGV0ZWQ6IFtdLFxuICAgICAgICAgICAgICB0b3RhbENoYW5nZXM6IDAsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7c2l0ZUlkfWAsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENoZWNrIFdQRSBjb25uZWN0aW9uXG4gICAgICAgICAgY29uc3Qgd3BlQ29ubmVjdGlvbiA9IHNpdGUuaG9zdENvbm5lY3Rpb25zPy5maW5kKChjOiBhbnkpID0+IGMuaG9zdElkID09PSAnd3BlJyk7XG4gICAgICAgICAgaWYgKCF3cGVDb25uZWN0aW9uKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IHNpdGUubmFtZSxcbiAgICAgICAgICAgICAgZGlyZWN0aW9uLFxuICAgICAgICAgICAgICBhZGRlZDogW10sXG4gICAgICAgICAgICAgIG1vZGlmaWVkOiBbXSxcbiAgICAgICAgICAgICAgZGVsZXRlZDogW10sXG4gICAgICAgICAgICAgIHRvdGFsQ2hhbmdlczogMCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6XG4gICAgICAgICAgICAgICAgJ1NpdGUgaXMgbm90IGxpbmtlZCB0byBXUCBFbmdpbmUuIFVzZSBDb25uZWN0IGluIExvY2FsIHRvIGxpbmsgdGhlIHNpdGUgZmlyc3QuJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ2hlY2sgc2VydmljZSBhdmFpbGFiaWxpdHlcbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAhd3BlQ29ubmVjdEJhc2VTZXJ2aWNlIHx8XG4gICAgICAgICAgICB0eXBlb2Ygd3BlQ29ubmVjdEJhc2VTZXJ2aWNlLmxpc3RNb2RpZmljYXRpb25zICE9PSAnZnVuY3Rpb24nXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IHNpdGUubmFtZSxcbiAgICAgICAgICAgICAgZGlyZWN0aW9uLFxuICAgICAgICAgICAgICBhZGRlZDogW10sXG4gICAgICAgICAgICAgIG1vZGlmaWVkOiBbXSxcbiAgICAgICAgICAgICAgZGVsZXRlZDogW10sXG4gICAgICAgICAgICAgIHRvdGFsQ2hhbmdlczogMCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6ICdXUEUgQ29ubmVjdCBzZXJ2aWNlIG5vdCBhdmFpbGFibGUnLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBHZXQgaW5zdGFsbCBkZXRhaWxzIGZyb20gQ0FQSVxuICAgICAgICAgIGxldCBpbnN0YWxsTmFtZSA9IHdwZUNvbm5lY3Rpb24ucmVtb3RlU2l0ZUlkO1xuICAgICAgICAgIGxldCBwcmltYXJ5RG9tYWluID0gJyc7XG4gICAgICAgICAgbGV0IGluc3RhbGxJZCA9ICcnO1xuXG4gICAgICAgICAgaWYgKGNhcGlTZXJ2aWNlICYmIHR5cGVvZiBjYXBpU2VydmljZS5nZXRJbnN0YWxsTGlzdCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY29uc3QgaW5zdGFsbHMgPSBhd2FpdCBjYXBpU2VydmljZS5nZXRJbnN0YWxsTGlzdCgpO1xuICAgICAgICAgICAgY29uc3QgbWF0Y2hpbmdJbnN0YWxsID0gaW5zdGFsbHM/LmZpbmQoXG4gICAgICAgICAgICAgIChpOiBhbnkpID0+XG4gICAgICAgICAgICAgICAgaS5zaXRlPy5pZCA9PT0gd3BlQ29ubmVjdGlvbi5yZW1vdGVTaXRlSWQgJiZcbiAgICAgICAgICAgICAgICAoIXdwZUNvbm5lY3Rpb24ucmVtb3RlU2l0ZUVudiB8fCBpLmVudmlyb25tZW50ID09PSB3cGVDb25uZWN0aW9uLnJlbW90ZVNpdGVFbnYpXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgaWYgKG1hdGNoaW5nSW5zdGFsbCkge1xuICAgICAgICAgICAgICBpbnN0YWxsTmFtZSA9IG1hdGNoaW5nSW5zdGFsbC5uYW1lO1xuICAgICAgICAgICAgICBwcmltYXJ5RG9tYWluID1cbiAgICAgICAgICAgICAgICBtYXRjaGluZ0luc3RhbGwucHJpbWFyeV9kb21haW4gfHxcbiAgICAgICAgICAgICAgICBtYXRjaGluZ0luc3RhbGwuY25hbWUgfHxcbiAgICAgICAgICAgICAgICBgJHttYXRjaGluZ0luc3RhbGwubmFtZX0ud3BlbmdpbmUuY29tYDtcbiAgICAgICAgICAgICAgaW5zdGFsbElkID0gbWF0Y2hpbmdJbnN0YWxsLmlkO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICghcHJpbWFyeURvbWFpbikge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIHNpdGVOYW1lOiBzaXRlLm5hbWUsXG4gICAgICAgICAgICAgIGRpcmVjdGlvbixcbiAgICAgICAgICAgICAgYWRkZWQ6IFtdLFxuICAgICAgICAgICAgICBtb2RpZmllZDogW10sXG4gICAgICAgICAgICAgIGRlbGV0ZWQ6IFtdLFxuICAgICAgICAgICAgICB0b3RhbENoYW5nZXM6IDAsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOlxuICAgICAgICAgICAgICAgICdDb3VsZCBub3QgZGV0ZXJtaW5lIFdQIEVuZ2luZSBpbnN0YWxsIGRldGFpbHMuIFBsZWFzZSBlbnN1cmUgeW91IGFyZSBhdXRoZW50aWNhdGVkLicsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENhbGwgbGlzdE1vZGlmaWNhdGlvbnMgKGRyeS1ydW4gcnN5bmMgY29tcGFyaXNvbilcbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gQ2FsbGluZyBsaXN0TW9kaWZpY2F0aW9ucyBmb3IgJHtpbnN0YWxsTmFtZX1gKTtcbiAgICAgICAgICBjb25zdCBtb2RpZmljYXRpb25zID0gYXdhaXQgd3BlQ29ubmVjdEJhc2VTZXJ2aWNlLmxpc3RNb2RpZmljYXRpb25zKHtcbiAgICAgICAgICAgIGNvbm5lY3RBcmdzOiB7XG4gICAgICAgICAgICAgIHdwZW5naW5lSW5zdGFsbE5hbWU6IGluc3RhbGxOYW1lLFxuICAgICAgICAgICAgICB3cGVuZ2luZUluc3RhbGxJZDogaW5zdGFsbElkLFxuICAgICAgICAgICAgICB3cGVuZ2luZVNpdGVJZDogd3BlQ29ubmVjdGlvbi5yZW1vdGVTaXRlSWQsXG4gICAgICAgICAgICAgIHdwZW5naW5lUHJpbWFyeURvbWFpbjogcHJpbWFyeURvbWFpbixcbiAgICAgICAgICAgICAgbG9jYWxTaXRlSWQ6IHNpdGUuaWQsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZGlyZWN0aW9uOiBkaXJlY3Rpb24gYXMgJ3B1c2gnIHwgJ3B1bGwnLFxuICAgICAgICAgICAgaW5jbHVkZUlnbm9yZWQ6IGZhbHNlLFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgLy8gQ2F0ZWdvcml6ZSBjaGFuZ2VzXG4gICAgICAgICAgY29uc3QgYWRkZWQgPSBtb2RpZmljYXRpb25zXG4gICAgICAgICAgICAuZmlsdGVyKFxuICAgICAgICAgICAgICAoZjogYW55KSA9PlxuICAgICAgICAgICAgICAgIGYuaW5zdHJ1Y3Rpb24gPT09ICdjcmVhdGUnIHx8XG4gICAgICAgICAgICAgICAgZi5pbnN0cnVjdGlvbiA9PT0gJ3VwbG9hZCcgfHxcbiAgICAgICAgICAgICAgICBmLmluc3RydWN0aW9uID09PSAnZG93bmxvYWQnXG4gICAgICAgICAgICApXG4gICAgICAgICAgICAubWFwKChmOiBhbnkpID0+ICh7XG4gICAgICAgICAgICAgIHBhdGg6IGYucGF0aCxcbiAgICAgICAgICAgICAgaW5zdHJ1Y3Rpb246IGYuaW5zdHJ1Y3Rpb24sXG4gICAgICAgICAgICAgIHNpemU6IGYuc2l6ZSxcbiAgICAgICAgICAgICAgdHlwZTogZi50eXBlLFxuICAgICAgICAgICAgfSkpO1xuXG4gICAgICAgICAgY29uc3QgbW9kaWZpZWQgPSBtb2RpZmljYXRpb25zXG4gICAgICAgICAgICAuZmlsdGVyKChmOiBhbnkpID0+IGYuaW5zdHJ1Y3Rpb24gPT09ICdtb2RpZnknKVxuICAgICAgICAgICAgLm1hcCgoZjogYW55KSA9PiAoe1xuICAgICAgICAgICAgICBwYXRoOiBmLnBhdGgsXG4gICAgICAgICAgICAgIGluc3RydWN0aW9uOiBmLmluc3RydWN0aW9uLFxuICAgICAgICAgICAgICBzaXplOiBmLnNpemUsXG4gICAgICAgICAgICAgIHR5cGU6IGYudHlwZSxcbiAgICAgICAgICAgIH0pKTtcblxuICAgICAgICAgIGNvbnN0IGRlbGV0ZWQgPSBtb2RpZmljYXRpb25zXG4gICAgICAgICAgICAuZmlsdGVyKChmOiBhbnkpID0+IGYuaW5zdHJ1Y3Rpb24gPT09ICdkZWxldGUnKVxuICAgICAgICAgICAgLm1hcCgoZjogYW55KSA9PiAoe1xuICAgICAgICAgICAgICBwYXRoOiBmLnBhdGgsXG4gICAgICAgICAgICAgIGluc3RydWN0aW9uOiBmLmluc3RydWN0aW9uLFxuICAgICAgICAgICAgICBzaXplOiBmLnNpemUsXG4gICAgICAgICAgICAgIHR5cGU6IGYudHlwZSxcbiAgICAgICAgICAgIH0pKTtcblxuICAgICAgICAgIGNvbnN0IHRvdGFsQ2hhbmdlcyA9IGFkZGVkLmxlbmd0aCArIG1vZGlmaWVkLmxlbmd0aCArIGRlbGV0ZWQubGVuZ3RoO1xuICAgICAgICAgIGNvbnN0IGRpcmVjdGlvbkxhYmVsID0gZGlyZWN0aW9uID09PSAncHVzaCcgPyAnbG9jYWwg4oaSIFdQRScgOiAnV1BFIOKGkiBsb2NhbCc7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIHNpdGVOYW1lOiBzaXRlLm5hbWUsXG4gICAgICAgICAgICBkaXJlY3Rpb24sXG4gICAgICAgICAgICBhZGRlZCxcbiAgICAgICAgICAgIG1vZGlmaWVkLFxuICAgICAgICAgICAgZGVsZXRlZCxcbiAgICAgICAgICAgIHRvdGFsQ2hhbmdlcyxcbiAgICAgICAgICAgIG1lc3NhZ2U6XG4gICAgICAgICAgICAgIHRvdGFsQ2hhbmdlcyA+IDBcbiAgICAgICAgICAgICAgICA/IGAke3RvdGFsQ2hhbmdlc30gZmlsZShzKSBjaGFuZ2VkICgke2RpcmVjdGlvbkxhYmVsfSk6ICR7YWRkZWQubGVuZ3RofSBhZGRlZCwgJHttb2RpZmllZC5sZW5ndGh9IG1vZGlmaWVkLCAke2RlbGV0ZWQubGVuZ3RofSBkZWxldGVkYFxuICAgICAgICAgICAgICAgIDogYE5vIGNoYW5nZXMgZGV0ZWN0ZWQgKCR7ZGlyZWN0aW9uTGFiZWx9KWAsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gZ2V0IHNpdGUgY2hhbmdlczpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgc2l0ZU5hbWU6IG51bGwsXG4gICAgICAgICAgICBkaXJlY3Rpb24sXG4gICAgICAgICAgICBhZGRlZDogW10sXG4gICAgICAgICAgICBtb2RpZmllZDogW10sXG4gICAgICAgICAgICBkZWxldGVkOiBbXSxcbiAgICAgICAgICAgIHRvdGFsQ2hhbmdlczogMCxcbiAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICB9LFxuICAgIE11dGF0aW9uOiB7XG4gICAgICB3cENsaTogZXhlY3V0ZVdwQ2xpLFxuXG4gICAgICBjcmVhdGVTaXRlOiBhc3luYyAoXG4gICAgICAgIF9wYXJlbnQ6IGFueSxcbiAgICAgICAgYXJnczoge1xuICAgICAgICAgIGlucHV0OiB7XG4gICAgICAgICAgICBuYW1lOiBzdHJpbmc7XG4gICAgICAgICAgICBwaHBWZXJzaW9uPzogc3RyaW5nO1xuICAgICAgICAgICAgd2ViU2VydmVyPzogc3RyaW5nO1xuICAgICAgICAgICAgZGF0YWJhc2U/OiBzdHJpbmc7XG4gICAgICAgICAgICB3cEFkbWluVXNlcm5hbWU/OiBzdHJpbmc7XG4gICAgICAgICAgICB3cEFkbWluUGFzc3dvcmQ/OiBzdHJpbmc7XG4gICAgICAgICAgICB3cEFkbWluRW1haWw/OiBzdHJpbmc7XG4gICAgICAgICAgICBibHVlcHJpbnQ/OiBzdHJpbmc7XG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgKSA9PiB7XG4gICAgICAgIC8vIERFQlVHOiBMb2cgcmF3IGFyZ3MgcmVjZWl2ZWRcbiAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIGNyZWF0ZVNpdGUgY2FsbGVkIHdpdGggYXJnczogJHtKU09OLnN0cmluZ2lmeShhcmdzKX1gKTtcblxuICAgICAgICBjb25zdCB7XG4gICAgICAgICAgbmFtZSxcbiAgICAgICAgICBwaHBWZXJzaW9uLFxuICAgICAgICAgIHdlYlNlcnZlciA9ICduZ2lueCcsXG4gICAgICAgICAgZGF0YWJhc2UgPSAnbXlzcWwnLFxuICAgICAgICAgIHdwQWRtaW5Vc2VybmFtZSA9ICdhZG1pbicsXG4gICAgICAgICAgd3BBZG1pblBhc3N3b3JkID0gJ3Bhc3N3b3JkJyxcbiAgICAgICAgICB3cEFkbWluRW1haWwgPSAnYWRtaW5AbG9jYWwudGVzdCcsXG4gICAgICAgICAgYmx1ZXByaW50LFxuICAgICAgICB9ID0gYXJncy5pbnB1dDtcblxuICAgICAgICAvLyBERUJVRzogTG9nIGRlc3RydWN0dXJlZCB2YWx1ZXNcbiAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhcbiAgICAgICAgICBgWyR7QURET05fTkFNRX1dIERlc3RydWN0dXJlZCAtIG5hbWU6ICR7bmFtZX0sIGJsdWVwcmludDogJHtibHVlcHJpbnR9LCB0eXBlb2YgYmx1ZXByaW50OiAke3R5cGVvZiBibHVlcHJpbnR9YFxuICAgICAgICApO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBbJHtBRERPTl9OQU1FfV0gQ3JlYXRpbmcgc2l0ZTogJHtuYW1lfSR7Ymx1ZXByaW50ID8gYCBmcm9tIGJsdWVwcmludDogJHtibHVlcHJpbnR9YCA6ICcnfWBcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgLy8gR2VuZXJhdGUgc2x1ZyBhbmQgZG9tYWluIGZyb20gbmFtZVxuICAgICAgICAgIGNvbnN0IHNpdGVTbHVnID0gbmFtZVxuICAgICAgICAgICAgLnRvTG93ZXJDYXNlKClcbiAgICAgICAgICAgIC5yZXBsYWNlKC9bXmEtejAtOV0rL2csICctJylcbiAgICAgICAgICAgIC5yZXBsYWNlKC9eLXwtJC9nLCAnJyk7XG4gICAgICAgICAgY29uc3Qgc2l0ZURvbWFpbiA9IGAke3NpdGVTbHVnfS5sb2NhbGA7XG5cbiAgICAgICAgICBjb25zdCBvcyA9IHJlcXVpcmUoJ29zJyk7XG4gICAgICAgICAgY29uc3QgcGF0aCA9IHJlcXVpcmUoJ3BhdGgnKTtcbiAgICAgICAgICBjb25zdCBmcyA9IHJlcXVpcmUoJ2ZzJyk7XG4gICAgICAgICAgY29uc3Qgc2l0ZVBhdGggPSBwYXRoLmpvaW4ob3MuaG9tZWRpcigpLCAnTG9jYWwgU2l0ZXMnLCBzaXRlU2x1Zyk7XG5cbiAgICAgICAgICAvLyBJZiBibHVlcHJpbnQgaXMgcHJvdmlkZWQsIHVzZSBpbXBvcnRTaXRlU2VydmljZSBpbnN0ZWFkIG9mIGFkZFNpdGVTZXJ2aWNlXG4gICAgICAgICAgaWYgKGJsdWVwcmludCkge1xuICAgICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIEJsdWVwcmludCBwYXJhbWV0ZXIgcmVjZWl2ZWQ6ICR7Ymx1ZXByaW50fWApO1xuXG4gICAgICAgICAgICAvLyBHZXQgdGhlIHVzZXJEYXRhUGF0aCBmcm9tIGVsZWN0cm9uIGFwcFxuICAgICAgICAgICAgY29uc3QgeyBhcHAgfSA9IHJlcXVpcmUoJ2VsZWN0cm9uJyk7XG4gICAgICAgICAgICBjb25zdCB1c2VyRGF0YVBhdGggPSBhcHAuZ2V0UGF0aCgndXNlckRhdGEnKTtcbiAgICAgICAgICAgIGNvbnN0IGJsdWVwcmludFppcFBhdGggPSBwYXRoLmpvaW4odXNlckRhdGFQYXRoLCAnYmx1ZXByaW50cycsIGAke2JsdWVwcmludH0uemlwYCk7XG5cbiAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBMb29raW5nIGZvciBibHVlcHJpbnQgYXQ6ICR7Ymx1ZXByaW50WmlwUGF0aH1gKTtcblxuICAgICAgICAgICAgLy8gVmVyaWZ5IGJsdWVwcmludCBleGlzdHNcbiAgICAgICAgICAgIGlmICghZnMuZXhpc3RzU3luYyhibHVlcHJpbnRaaXBQYXRoKSkge1xuICAgICAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEJsdWVwcmludCBub3QgZm91bmQgYXQ6ICR7Ymx1ZXByaW50WmlwUGF0aH1gKTtcbiAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBlcnJvcjogYEJsdWVwcmludCBub3QgZm91bmQ6ICR7Ymx1ZXByaW50fS4gVXNlIGxpc3RfYmx1ZXByaW50cyB0byBzZWUgYXZhaWxhYmxlIGJsdWVwcmludHMuYCxcbiAgICAgICAgICAgICAgICBzaXRlSWQ6IG51bGwsXG4gICAgICAgICAgICAgICAgc2l0ZU5hbWU6IG5hbWUsXG4gICAgICAgICAgICAgICAgc2l0ZURvbWFpbjogbnVsbCxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIEZvdW5kIGJsdWVwcmludCBhdDogJHtibHVlcHJpbnRaaXBQYXRofWApO1xuXG4gICAgICAgICAgICAvLyBSZWFkIHRoZSBsb2NhbC1zaXRlLmpzb24gZnJvbSB0aGUgYmx1ZXByaW50IHppcCB0byBnZXQgbWFuaWZlc3RcbiAgICAgICAgICAgIGxldCBsb2NhbFNpdGVKU09OOiBhbnk7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBjb25zdCBTdHJlYW1aaXAgPSByZXF1aXJlKCdub2RlLXN0cmVhbS16aXAnKTtcbiAgICAgICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIG5vZGUtc3RyZWFtLXppcCBsb2FkZWQgc3VjY2Vzc2Z1bGx5YCk7XG5cbiAgICAgICAgICAgICAgY29uc3QgemlwID0gbmV3IFN0cmVhbVppcC5hc3luYyh7IGZpbGU6IGJsdWVwcmludFppcFBhdGggfSk7XG4gICAgICAgICAgICAgIGNvbnN0IGVudHJpZXMgPSBhd2FpdCB6aXAuZW50cmllcygpO1xuICAgICAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKFxuICAgICAgICAgICAgICAgIGBbJHtBRERPTl9OQU1FfV0gWmlwIGVudHJpZXMgbG9hZGVkLCBjb3VudDogJHtPYmplY3Qua2V5cyhlbnRyaWVzKS5sZW5ndGh9YFxuICAgICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAgIGNvbnN0IGZpbGVuYW1lID0gZW50cmllc1snbG9jYWwtc2l0ZS5qc29uJ11cbiAgICAgICAgICAgICAgICA/ICdsb2NhbC1zaXRlLmpzb24nXG4gICAgICAgICAgICAgICAgOiAncHJlc3NtYXRpYy1zaXRlLmpzb24nO1xuICAgICAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gUmVhZGluZyBtYW5pZmVzdCBmaWxlOiAke2ZpbGVuYW1lfWApO1xuXG4gICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCB6aXAuZW50cnlEYXRhKGZpbGVuYW1lKTtcbiAgICAgICAgICAgICAgbG9jYWxTaXRlSlNPTiA9IEpTT04ucGFyc2UoZGF0YS50b1N0cmluZygndXRmOCcpKTtcbiAgICAgICAgICAgICAgYXdhaXQgemlwLmNsb3NlKCk7XG4gICAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICAgICAgYFske0FERE9OX05BTUV9XSBTdWNjZXNzZnVsbHkgcmVhZCBtYW5pZmVzdDpgLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGxvY2FsU2l0ZUpTT04pLnN1YnN0cmluZygwLCAyMDApXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9IGNhdGNoICh6aXBFcnJvcjogYW55KSB7XG4gICAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKFxuICAgICAgICAgICAgICAgIGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIHJlYWQgYmx1ZXByaW50IHppcDogJHt6aXBFcnJvci5tZXNzYWdlfWAsXG4gICAgICAgICAgICAgICAgemlwRXJyb3JcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBlcnJvcjogYEZhaWxlZCB0byByZWFkIGJsdWVwcmludCBtYW5pZmVzdDogJHt6aXBFcnJvci5tZXNzYWdlfWAsXG4gICAgICAgICAgICAgICAgc2l0ZUlkOiBudWxsLFxuICAgICAgICAgICAgICAgIHNpdGVOYW1lOiBuYW1lLFxuICAgICAgICAgICAgICAgIHNpdGVEb21haW46IG51bGwsXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIEJ1aWxkIGltcG9ydCBzZXR0aW5nc1xuICAgICAgICAgICAgY29uc3QgaW1wb3J0U2V0dGluZ3M6IGFueSA9IHtcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IG5hbWUsXG4gICAgICAgICAgICAgIHNpdGVEb21haW46IHNpdGVEb21haW4sXG4gICAgICAgICAgICAgIHNpdGVQYXRoOiBzaXRlUGF0aCxcbiAgICAgICAgICAgICAgemlwOiBibHVlcHJpbnRaaXBQYXRoLFxuICAgICAgICAgICAgICBpbXBvcnREYXRhOiB7XG4gICAgICAgICAgICAgICAgdHlwZTogJ2xvY2FsLWJsdWVwcmludCcsXG4gICAgICAgICAgICAgICAgb2xkU2l0ZTogbG9jYWxTaXRlSlNPTixcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgZW52aXJvbm1lbnQ6IGxvY2FsU2l0ZUpTT04uZW52aXJvbm1lbnQgfHwgJ2ZseXdoZWVsJyxcbiAgICAgICAgICAgICAgYmx1ZXByaW50OiBibHVlcHJpbnQsXG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAvLyBDb3B5IHNlcnZpY2UgdmVyc2lvbnMgZnJvbSBibHVlcHJpbnQgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgICBpZiAobG9jYWxTaXRlSlNPTi5zZXJ2aWNlcykge1xuICAgICAgICAgICAgICAvLyBFeHRyYWN0IFBIUCB2ZXJzaW9uXG4gICAgICAgICAgICAgIGNvbnN0IHBocFNlcnZpY2UgPSBPYmplY3QudmFsdWVzKGxvY2FsU2l0ZUpTT04uc2VydmljZXMpLmZpbmQoXG4gICAgICAgICAgICAgICAgKHM6IGFueSkgPT4gcy5yb2xlID09PSAncGhwJ1xuICAgICAgICAgICAgICApIGFzIGFueTtcbiAgICAgICAgICAgICAgaWYgKHBocFNlcnZpY2UpIHtcbiAgICAgICAgICAgICAgICBpbXBvcnRTZXR0aW5ncy5waHBWZXJzaW9uID0gcGhwU2VydmljZS52ZXJzaW9uO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgLy8gRXh0cmFjdCBkYXRhYmFzZVxuICAgICAgICAgICAgICBjb25zdCBkYlNlcnZpY2UgPSBPYmplY3QudmFsdWVzKGxvY2FsU2l0ZUpTT04uc2VydmljZXMpLmZpbmQoXG4gICAgICAgICAgICAgICAgKHM6IGFueSkgPT4gcy5yb2xlID09PSAnZGF0YWJhc2UnIHx8IHMucm9sZSA9PT0gJ2RiJ1xuICAgICAgICAgICAgICApIGFzIGFueTtcbiAgICAgICAgICAgICAgaWYgKGRiU2VydmljZSkge1xuICAgICAgICAgICAgICAgIGltcG9ydFNldHRpbmdzLmRhdGFiYXNlID0gYCR7ZGJTZXJ2aWNlLm5hbWV9LSR7ZGJTZXJ2aWNlLnZlcnNpb259YDtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIC8vIEV4dHJhY3Qgd2ViIHNlcnZlclxuICAgICAgICAgICAgICBjb25zdCB3ZWJTZXJ2aWNlID0gT2JqZWN0LnZhbHVlcyhsb2NhbFNpdGVKU09OLnNlcnZpY2VzKS5maW5kKFxuICAgICAgICAgICAgICAgIChzOiBhbnkpID0+IHMucm9sZSA9PT0gJ2h0dHAnIHx8IHMucm9sZSA9PT0gJ3dlYidcbiAgICAgICAgICAgICAgKSBhcyBhbnk7XG4gICAgICAgICAgICAgIGlmICh3ZWJTZXJ2aWNlKSB7XG4gICAgICAgICAgICAgICAgaW1wb3J0U2V0dGluZ3Mud2ViU2VydmVyID0gYCR7d2ViU2VydmljZS5uYW1lfS0ke3dlYlNlcnZpY2UudmVyc2lvbn1gO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGxvY2FsU2l0ZUpTT04ucGhwVmVyc2lvbikge1xuICAgICAgICAgICAgICBpbXBvcnRTZXR0aW5ncy5waHBWZXJzaW9uID0gbG9jYWxTaXRlSlNPTi5waHBWZXJzaW9uO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKFxuICAgICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIEltcG9ydCBzZXR0aW5ncyBwcmVwYXJlZDpgLFxuICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShpbXBvcnRTZXR0aW5ncykuc3Vic3RyaW5nKDAsIDUwMClcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIGlmICghaW1wb3J0U2l0ZVNlcnZpY2UpIHtcbiAgICAgICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBpbXBvcnRTaXRlU2VydmljZSBpcyBub3QgYXZhaWxhYmxlIWApO1xuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGVycm9yOiAnSW1wb3J0IHNlcnZpY2Ugbm90IGF2YWlsYWJsZScsXG4gICAgICAgICAgICAgICAgc2l0ZUlkOiBudWxsLFxuICAgICAgICAgICAgICAgIHNpdGVOYW1lOiBuYW1lLFxuICAgICAgICAgICAgICAgIHNpdGVEb21haW46IG51bGwsXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBDYWxsaW5nIGltcG9ydFNpdGVTZXJ2aWNlLnJ1bigpLi4uYCk7XG5cbiAgICAgICAgICAgIC8vIFVzZSB0aGUgaW1wb3J0U2l0ZVNlcnZpY2UgdG8gY3JlYXRlIGZyb20gYmx1ZXByaW50XG4gICAgICAgICAgICBjb25zdCBpbXBvcnRSZXN1bHQgPSBhd2FpdCBpbXBvcnRTaXRlU2VydmljZS5ydW4oaW1wb3J0U2V0dGluZ3MpO1xuXG4gICAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKFxuICAgICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIEltcG9ydCByZXN1bHQ6YCxcbiAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoaW1wb3J0UmVzdWx0IHx8ICdudWxsJykuc3Vic3RyaW5nKDAsIDUwMClcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIGlmIChpbXBvcnRSZXN1bHQgJiYgaW1wb3J0UmVzdWx0LmlkKSB7XG4gICAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICAgICAgYFske0FERE9OX05BTUV9XSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBzaXRlIGZyb20gYmx1ZXByaW50OiAke25hbWV9ICgke2ltcG9ydFJlc3VsdC5pZH0pYFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICAgICAgc2l0ZUlkOiBpbXBvcnRSZXN1bHQuaWQsXG4gICAgICAgICAgICAgICAgc2l0ZU5hbWU6IG5hbWUsXG4gICAgICAgICAgICAgICAgc2l0ZURvbWFpbjogc2l0ZURvbWFpbixcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGxvY2FsTG9nZ2VyLndhcm4oYFske0FERE9OX05BTUV9XSBJbXBvcnQgcmV0dXJuZWQgYnV0IG5vIHNpdGUgSUQgZm91bmRgKTtcbiAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgICAgIHNpdGVJZDogbnVsbCxcbiAgICAgICAgICAgICAgICBzaXRlTmFtZTogbmFtZSxcbiAgICAgICAgICAgICAgICBzaXRlRG9tYWluOiBzaXRlRG9tYWluLFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIE5vIGJsdWVwcmludCAtIGNyZWF0ZSBhIGZyZXNoIHNpdGVcbiAgICAgICAgICBjb25zdCBuZXdTaXRlSW5mbzogYW55ID0ge1xuICAgICAgICAgICAgc2l0ZU5hbWU6IG5hbWUsXG4gICAgICAgICAgICBzaXRlRG9tYWluOiBzaXRlRG9tYWluLFxuICAgICAgICAgICAgc2l0ZVBhdGg6IHNpdGVQYXRoLFxuICAgICAgICAgICAgd2ViU2VydmVyOiB3ZWJTZXJ2ZXIsXG4gICAgICAgICAgICBkYXRhYmFzZTogZGF0YWJhc2UsXG4gICAgICAgICAgfTtcblxuICAgICAgICAgIGlmIChwaHBWZXJzaW9uKSB7XG4gICAgICAgICAgICBuZXdTaXRlSW5mby5waHBWZXJzaW9uID0gcGhwVmVyc2lvbjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCB3cENyZWRlbnRpYWxzID0ge1xuICAgICAgICAgICAgYWRtaW5Vc2VybmFtZTogd3BBZG1pblVzZXJuYW1lLFxuICAgICAgICAgICAgYWRtaW5QYXNzd29yZDogd3BBZG1pblBhc3N3b3JkLFxuICAgICAgICAgICAgYWRtaW5FbWFpbDogd3BBZG1pbkVtYWlsLFxuICAgICAgICAgIH07XG5cbiAgICAgICAgICBjb25zdCBzaXRlID0gYXdhaXQgYWRkU2l0ZVNlcnZpY2UuYWRkU2l0ZSh7XG4gICAgICAgICAgICBuZXdTaXRlSW5mbyxcbiAgICAgICAgICAgIHdwQ3JlZGVudGlhbHMsXG4gICAgICAgICAgICBnb1RvU2l0ZTogZmFsc2UsXG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgc2l0ZTogJHtuYW1lfSAoJHtzaXRlLmlkfSlgKTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICBzaXRlSWQ6IHNpdGUuaWQsXG4gICAgICAgICAgICBzaXRlTmFtZTogbmFtZSxcbiAgICAgICAgICAgIHNpdGVEb21haW46IHNpdGVEb21haW4sXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGNyZWF0ZSBzaXRlOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgICBzaXRlSWQ6IG51bGwsXG4gICAgICAgICAgICBzaXRlTmFtZTogbmFtZSxcbiAgICAgICAgICAgIHNpdGVEb21haW46IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgZGVsZXRlU2l0ZTogYXN5bmMgKFxuICAgICAgICBfcGFyZW50OiBhbnksXG4gICAgICAgIGFyZ3M6IHsgaW5wdXQ6IHsgaWQ6IHN0cmluZzsgdHJhc2hGaWxlcz86IGJvb2xlYW47IHVwZGF0ZUhvc3RzPzogYm9vbGVhbiB9IH1cbiAgICAgICkgPT4ge1xuICAgICAgICBjb25zdCB7IGlkLCB0cmFzaEZpbGVzID0gdHJ1ZSwgdXBkYXRlSG9zdHMgPSB0cnVlIH0gPSBhcmdzLmlucHV0O1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIERlbGV0aW5nIHNpdGU6ICR7aWR9YCk7XG5cbiAgICAgICAgICBjb25zdCBzaXRlID0gc2l0ZURhdGEuZ2V0U2l0ZShpZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIG5vdCBmb3VuZDogJHtpZH1gLFxuICAgICAgICAgICAgICBzaXRlSWQ6IGlkLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBhd2FpdCBkZWxldGVTaXRlU2VydmljZS5kZWxldGVTaXRlKHtcbiAgICAgICAgICAgIHNpdGUsXG4gICAgICAgICAgICB0cmFzaEZpbGVzLFxuICAgICAgICAgICAgdXBkYXRlSG9zdHMsXG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gU3VjY2Vzc2Z1bGx5IGRlbGV0ZWQgc2l0ZTogJHtzaXRlLm5hbWV9YCk7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgc2l0ZUlkOiBpZCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gZGVsZXRlIHNpdGU6YCwgZXJyb3IpO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdVbmtub3duIGVycm9yJyxcbiAgICAgICAgICAgIHNpdGVJZDogaWQsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgZGVsZXRlU2l0ZXM6IGFzeW5jIChfcGFyZW50OiBhbnksIGFyZ3M6IHsgaWRzOiBzdHJpbmdbXTsgdHJhc2hGaWxlcz86IGJvb2xlYW4gfSkgPT4ge1xuICAgICAgICBjb25zdCB7IGlkcywgdHJhc2hGaWxlcyA9IHRydWUgfSA9IGFyZ3M7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gRGVsZXRpbmcgJHtpZHMubGVuZ3RofSBzaXRlc2ApO1xuXG4gICAgICAgICAgYXdhaXQgZGVsZXRlU2l0ZVNlcnZpY2UuZGVsZXRlU2l0ZXMoe1xuICAgICAgICAgICAgc2l0ZUlkczogaWRzLFxuICAgICAgICAgICAgdHJhc2hGaWxlcyxcbiAgICAgICAgICAgIHVwZGF0ZUhvc3RzOiB0cnVlLFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIFN1Y2Nlc3NmdWxseSBkZWxldGVkICR7aWRzLmxlbmd0aH0gc2l0ZXNgKTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICBzaXRlSWQ6IGlkcy5qb2luKCcsJyksXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGRlbGV0ZSBzaXRlczpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgICAgc2l0ZUlkOiBpZHMuam9pbignLCcpLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIG9wZW5TaXRlOiBhc3luYyAoX3BhcmVudDogYW55LCBhcmdzOiB7IGlucHV0OiB7IHNpdGVJZDogc3RyaW5nOyBwYXRoPzogc3RyaW5nIH0gfSkgPT4ge1xuICAgICAgICBjb25zdCB7IHNpdGVJZCwgcGF0aCA9ICcvJyB9ID0gYXJncy5pbnB1dDtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIG5vdCBmb3VuZDogJHtzaXRlSWR9YCxcbiAgICAgICAgICAgICAgdXJsOiBudWxsLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBDaGVjayBpZiBzaXRlIGlzIHJ1bm5pbmdcbiAgICAgICAgICBjb25zdCBzdGF0dXMgPSBhd2FpdCBzaXRlUHJvY2Vzc01hbmFnZXIuZ2V0U2l0ZVN0YXR1cyhzaXRlKTtcbiAgICAgICAgICBpZiAoc3RhdHVzICE9PSAncnVubmluZycpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgXCIke3NpdGUubmFtZX1cIiBtdXN0IGJlIHJ1bm5pbmcgdG8gb3BlbiBpbiBicm93c2VyLiBTdGFydCBpdCBmaXJzdC5gLFxuICAgICAgICAgICAgICB1cmw6IG51bGwsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IHByb3RvY29sID0gc2l0ZS5pc1N0YXJyZWQgPyAnaHR0cHMnIDogJ2h0dHAnO1xuICAgICAgICAgIGNvbnN0IHVybCA9IGAke3Byb3RvY29sfTovLyR7c2l0ZS5kb21haW59JHtwYXRofWA7XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gT3BlbmluZyBzaXRlIGluIGJyb3dzZXI6ICR7dXJsfWApO1xuXG4gICAgICAgICAgaWYgKGJyb3dzZXJNYW5hZ2VyKSB7XG4gICAgICAgICAgICBhd2FpdCBicm93c2VyTWFuYWdlci5vcGVuSW5Ccm93c2VyKHVybCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIEZhbGxiYWNrIHRvIHNoZWxsLm9wZW5FeHRlcm5hbFxuICAgICAgICAgICAgY29uc3QgeyBzaGVsbCB9ID0gcmVxdWlyZSgnZWxlY3Ryb24nKTtcbiAgICAgICAgICAgIGF3YWl0IHNoZWxsLm9wZW5FeHRlcm5hbCh1cmwpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICB1cmwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIG9wZW4gc2l0ZTpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgICAgdXJsOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIGNsb25lU2l0ZTogYXN5bmMgKF9wYXJlbnQ6IGFueSwgYXJnczogeyBpbnB1dDogeyBzaXRlSWQ6IHN0cmluZzsgbmV3TmFtZTogc3RyaW5nIH0gfSkgPT4ge1xuICAgICAgICBjb25zdCB7IHNpdGVJZCwgbmV3TmFtZSB9ID0gYXJncy5pbnB1dDtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIG5vdCBmb3VuZDogJHtzaXRlSWR9YCxcbiAgICAgICAgICAgICAgbmV3U2l0ZUlkOiBudWxsLFxuICAgICAgICAgICAgICBuZXdTaXRlTmFtZTogbnVsbCxcbiAgICAgICAgICAgICAgbmV3U2l0ZURvbWFpbjogbnVsbCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgc2l0ZSBpcyBydW5uaW5nIC0gbmVlZGVkIGZvciBkYXRhYmFzZSBjbG9uaW5nXG4gICAgICAgICAgY29uc3Qgc3RhdHVzID0gYXdhaXQgc2l0ZVByb2Nlc3NNYW5hZ2VyLmdldFNpdGVTdGF0dXMoc2l0ZSk7XG4gICAgICAgICAgaWYgKHN0YXR1cyAhPT0gJ3J1bm5pbmcnKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIFwiJHtzaXRlLm5hbWV9XCIgbXVzdCBiZSBydW5uaW5nIHRvIGNsb25lLiBTdGFydCBpdCBmaXJzdC5gLFxuICAgICAgICAgICAgICBuZXdTaXRlSWQ6IG51bGwsXG4gICAgICAgICAgICAgIG5ld1NpdGVOYW1lOiBudWxsLFxuICAgICAgICAgICAgICBuZXdTaXRlRG9tYWluOiBudWxsLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gQ2xvbmluZyBzaXRlICR7c2l0ZS5uYW1lfSB0byAke25ld05hbWV9YCk7XG5cbiAgICAgICAgICBjb25zdCBuZXdTaXRlID0gYXdhaXQgY2xvbmVTaXRlU2VydmljZS5jbG9uZVNpdGUoe1xuICAgICAgICAgICAgc2l0ZSxcbiAgICAgICAgICAgIG5ld1NpdGVOYW1lOiBuZXdOYW1lLFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBbJHtBRERPTl9OQU1FfV0gU3VjY2Vzc2Z1bGx5IGNsb25lZCBzaXRlOiAke25ld1NpdGUubmFtZX0gKCR7bmV3U2l0ZS5pZH0pYFxuICAgICAgICAgICk7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgbmV3U2l0ZUlkOiBuZXdTaXRlLmlkLFxuICAgICAgICAgICAgbmV3U2l0ZU5hbWU6IG5ld1NpdGUubmFtZSxcbiAgICAgICAgICAgIG5ld1NpdGVEb21haW46IG5ld1NpdGUuZG9tYWluLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBjbG9uZSBzaXRlOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgICBuZXdTaXRlSWQ6IG51bGwsXG4gICAgICAgICAgICBuZXdTaXRlTmFtZTogbnVsbCxcbiAgICAgICAgICAgIG5ld1NpdGVEb21haW46IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgZXhwb3J0U2l0ZTogYXN5bmMgKFxuICAgICAgICBfcGFyZW50OiBhbnksXG4gICAgICAgIGFyZ3M6IHsgaW5wdXQ6IHsgc2l0ZUlkOiBzdHJpbmc7IG91dHB1dFBhdGg/OiBzdHJpbmcgfSB9XG4gICAgICApID0+IHtcbiAgICAgICAgY29uc3QgeyBzaXRlSWQsIG91dHB1dFBhdGggfSA9IGFyZ3MuaW5wdXQ7XG4gICAgICAgIGNvbnN0IG9zID0gcmVxdWlyZSgnb3MnKTtcbiAgICAgICAgY29uc3QgcGF0aCA9IHJlcXVpcmUoJ3BhdGgnKTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIG5vdCBmb3VuZDogJHtzaXRlSWR9YCxcbiAgICAgICAgICAgICAgZXhwb3J0UGF0aDogbnVsbCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgc2l0ZSBpcyBydW5uaW5nIC0gbmVlZGVkIGZvciBkYXRhYmFzZSBleHBvcnRcbiAgICAgICAgICBjb25zdCBzdGF0dXMgPSBhd2FpdCBzaXRlUHJvY2Vzc01hbmFnZXIuZ2V0U2l0ZVN0YXR1cyhzaXRlKTtcbiAgICAgICAgICBpZiAoc3RhdHVzICE9PSAncnVubmluZycpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgXCIke3NpdGUubmFtZX1cIiBtdXN0IGJlIHJ1bm5pbmcgdG8gZXhwb3J0LiBTdGFydCBpdCBmaXJzdC5gLFxuICAgICAgICAgICAgICBleHBvcnRQYXRoOiBudWxsLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBEZWZhdWx0IHRvIERvd25sb2FkcyBmb2xkZXJcbiAgICAgICAgICBjb25zdCBvdXRwdXREaXIgPSBvdXRwdXRQYXRoIHx8IHBhdGguam9pbihvcy5ob21lZGlyKCksICdEb3dubG9hZHMnKTtcbiAgICAgICAgICBjb25zdCB0aW1lc3RhbXAgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkucmVwbGFjZSgvWzouXS9nLCAnLScpLnNsaWNlKDAsIDE5KTtcbiAgICAgICAgICBjb25zdCBmaWxlTmFtZSA9IGAke3NpdGUubmFtZX0tJHt0aW1lc3RhbXB9LnppcGA7XG4gICAgICAgICAgY29uc3QgZnVsbFBhdGggPSBwYXRoLmpvaW4ob3V0cHV0RGlyLCBmaWxlTmFtZSk7XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gRXhwb3J0aW5nIHNpdGUgJHtzaXRlLm5hbWV9IHRvICR7ZnVsbFBhdGh9YCk7XG5cbiAgICAgICAgICAvLyBVc2UgZGVmYXVsdCBleHBvcnQgZmlsdGVyIChleGNsdWRlcyBhcmNoaXZlIGZpbGVzKVxuICAgICAgICAgIGNvbnN0IGRlZmF1bHRFeHBvcnRGaWx0ZXIgPSAnKi56aXAsICoudGFyLmd6LCAqLmJ6MiwgKi50Z3onO1xuXG4gICAgICAgICAgYXdhaXQgZXhwb3J0U2l0ZVNlcnZpY2UuZXhwb3J0U2l0ZSh7XG4gICAgICAgICAgICBzaXRlLFxuICAgICAgICAgICAgb3V0cHV0UGF0aDogZnVsbFBhdGgsXG4gICAgICAgICAgICBmaWx0ZXI6IGRlZmF1bHRFeHBvcnRGaWx0ZXIsXG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gU3VjY2Vzc2Z1bGx5IGV4cG9ydGVkIHNpdGUgdG86ICR7ZnVsbFBhdGh9YCk7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgZXhwb3J0UGF0aDogZnVsbFBhdGgsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGV4cG9ydCBzaXRlOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgICBleHBvcnRQYXRoOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIHNhdmVCbHVlcHJpbnQ6IGFzeW5jIChfcGFyZW50OiBhbnksIGFyZ3M6IHsgaW5wdXQ6IHsgc2l0ZUlkOiBzdHJpbmc7IG5hbWU6IHN0cmluZyB9IH0pID0+IHtcbiAgICAgICAgY29uc3QgeyBzaXRlSWQsIG5hbWUgfSA9IGFyZ3MuaW5wdXQ7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBzaXRlID0gc2l0ZURhdGEuZ2V0U2l0ZShzaXRlSWQpO1xuICAgICAgICAgIGlmICghc2l0ZSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7c2l0ZUlkfWAsXG4gICAgICAgICAgICAgIGJsdWVwcmludE5hbWU6IG51bGwsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENoZWNrIGlmIHNpdGUgaXMgcnVubmluZyAtIG5lZWRlZCBmb3IgZGF0YWJhc2UgZXhwb3J0XG4gICAgICAgICAgY29uc3Qgc3RhdHVzID0gYXdhaXQgc2l0ZVByb2Nlc3NNYW5hZ2VyLmdldFNpdGVTdGF0dXMoc2l0ZSk7XG4gICAgICAgICAgaWYgKHN0YXR1cyAhPT0gJ3J1bm5pbmcnKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIFwiJHtzaXRlLm5hbWV9XCIgbXVzdCBiZSBydW5uaW5nIHRvIHNhdmUgYXMgYmx1ZXByaW50LiBTdGFydCBpdCBmaXJzdC5gLFxuICAgICAgICAgICAgICBibHVlcHJpbnROYW1lOiBudWxsLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gU2F2aW5nIHNpdGUgJHtzaXRlLm5hbWV9IGFzIGJsdWVwcmludDogJHtuYW1lfWApO1xuXG4gICAgICAgICAgLy8gVXNlIGRlZmF1bHQgZXhwb3J0IGZpbHRlciAoZXhjbHVkZXMgYXJjaGl2ZSBmaWxlcylcbiAgICAgICAgICBjb25zdCBkZWZhdWx0RmlsdGVyID0gJyouemlwLCAqLnRhci5neiwgKi5iejIsICoudGd6JztcblxuICAgICAgICAgIGF3YWl0IGJsdWVwcmludHNTZXJ2aWNlLnNhdmVCbHVlcHJpbnQoe1xuICAgICAgICAgICAgbmFtZSxcbiAgICAgICAgICAgIHNpdGVJZCxcbiAgICAgICAgICAgIGZpbHRlcjogZGVmYXVsdEZpbHRlcixcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBTdWNjZXNzZnVsbHkgc2F2ZWQgYmx1ZXByaW50OiAke25hbWV9YCk7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgYmx1ZXByaW50TmFtZTogbmFtZSxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gc2F2ZSBibHVlcHJpbnQ6YCwgZXJyb3IpO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdVbmtub3duIGVycm9yJyxcbiAgICAgICAgICAgIGJsdWVwcmludE5hbWU6IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgLy8gUGhhc2UgODogV29yZFByZXNzIERldmVsb3BtZW50IFRvb2xzXG4gICAgICBleHBvcnREYXRhYmFzZTogYXN5bmMgKFxuICAgICAgICBfcGFyZW50OiBhbnksXG4gICAgICAgIGFyZ3M6IHsgaW5wdXQ6IHsgc2l0ZUlkOiBzdHJpbmc7IG91dHB1dFBhdGg/OiBzdHJpbmcgfSB9XG4gICAgICApID0+IHtcbiAgICAgICAgY29uc3QgeyBzaXRlSWQsIG91dHB1dFBhdGggfSA9IGFyZ3MuaW5wdXQ7XG4gICAgICAgIGNvbnN0IG9zID0gcmVxdWlyZSgnb3MnKTtcbiAgICAgICAgY29uc3QgcGF0aE1vZHVsZSA9IHJlcXVpcmUoJ3BhdGgnKTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIG5vdCBmb3VuZDogJHtzaXRlSWR9YCxcbiAgICAgICAgICAgICAgb3V0cHV0UGF0aDogbnVsbCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgc2l0ZSBpcyBydW5uaW5nIC0gZGF0YWJhc2UgbXVzdCBiZSBhY2Nlc3NpYmxlXG4gICAgICAgICAgY29uc3Qgc3RhdHVzID0gYXdhaXQgc2l0ZVByb2Nlc3NNYW5hZ2VyLmdldFNpdGVTdGF0dXMoc2l0ZSk7XG4gICAgICAgICAgaWYgKHN0YXR1cyAhPT0gJ3J1bm5pbmcnKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIFwiJHtzaXRlLm5hbWV9XCIgbXVzdCBiZSBydW5uaW5nIHRvIGV4cG9ydCBkYXRhYmFzZS4gU3RhcnQgaXQgZmlyc3QuYCxcbiAgICAgICAgICAgICAgb3V0cHV0UGF0aDogbnVsbCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gRGVmYXVsdCB0byBEb3dubG9hZHMgZm9sZGVyIHdpdGggc2l0ZSBuYW1lXG4gICAgICAgICAgY29uc3QgZGVmYXVsdFBhdGggPSBwYXRoTW9kdWxlLmpvaW4oXG4gICAgICAgICAgICBvcy5ob21lZGlyKCksXG4gICAgICAgICAgICAnRG93bmxvYWRzJyxcbiAgICAgICAgICAgIGAke3NpdGUubmFtZS5yZXBsYWNlKC9bXmEtejAtOV0vZ2ksICctJyl9LnNxbGBcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvbnN0IGZpbmFsUGF0aCA9IG91dHB1dFBhdGggfHwgZGVmYXVsdFBhdGg7XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gRXhwb3J0aW5nIGRhdGFiYXNlIGZvciAke3NpdGUubmFtZX0gdG8gJHtmaW5hbFBhdGh9YCk7XG5cbiAgICAgICAgICAvLyBVc2Ugc2l0ZURhdGFiYXNlLmR1bXAoKSB3aGljaCBwcm9wZXJseSBzZXRzIHVwIE15U1FMIGVudmlyb25tZW50XG4gICAgICAgICAgaWYgKCFzaXRlRGF0YWJhc2UpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogJ0RhdGFiYXNlIHNlcnZpY2Ugbm90IGF2YWlsYWJsZScsXG4gICAgICAgICAgICAgIG91dHB1dFBhdGg6IG51bGwsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGF3YWl0IHNpdGVEYXRhYmFzZS5kdW1wKHNpdGUsIGZpbmFsUGF0aCk7XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gU3VjY2Vzc2Z1bGx5IGV4cG9ydGVkIGRhdGFiYXNlIHRvOiAke2ZpbmFsUGF0aH1gKTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICBvdXRwdXRQYXRoOiBmaW5hbFBhdGgsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGV4cG9ydCBkYXRhYmFzZTpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgICAgb3V0cHV0UGF0aDogbnVsbCxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICBpbXBvcnREYXRhYmFzZTogYXN5bmMgKFxuICAgICAgICBfcGFyZW50OiBhbnksXG4gICAgICAgIGFyZ3M6IHsgaW5wdXQ6IHsgc2l0ZUlkOiBzdHJpbmc7IHNxbFBhdGg6IHN0cmluZyB9IH1cbiAgICAgICkgPT4ge1xuICAgICAgICBjb25zdCB7IHNpdGVJZCwgc3FsUGF0aCB9ID0gYXJncy5pbnB1dDtcbiAgICAgICAgY29uc3QgZnMgPSByZXF1aXJlKCdmcycpO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3Qgc2l0ZSA9IHNpdGVEYXRhLmdldFNpdGUoc2l0ZUlkKTtcbiAgICAgICAgICBpZiAoIXNpdGUpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgbm90IGZvdW5kOiAke3NpdGVJZH1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoIWZzLmV4aXN0c1N5bmMoc3FsUGF0aCkpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFNRTCBmaWxlIG5vdCBmb3VuZDogJHtzcWxQYXRofWAsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENoZWNrIGlmIHNpdGUgaXMgcnVubmluZyAtIGRhdGFiYXNlIG11c3QgYmUgYWNjZXNzaWJsZVxuICAgICAgICAgIGNvbnN0IHN0YXR1cyA9IGF3YWl0IHNpdGVQcm9jZXNzTWFuYWdlci5nZXRTaXRlU3RhdHVzKHNpdGUpO1xuICAgICAgICAgIGlmIChzdGF0dXMgIT09ICdydW5uaW5nJykge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBcIiR7c2l0ZS5uYW1lfVwiIG11c3QgYmUgcnVubmluZyB0byBpbXBvcnQgZGF0YWJhc2UuIFN0YXJ0IGl0IGZpcnN0LmAsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBJbXBvcnRpbmcgZGF0YWJhc2UgZm9yICR7c2l0ZS5uYW1lfSBmcm9tICR7c3FsUGF0aH1gKTtcblxuICAgICAgICAgIC8vIFVzZSBpbXBvcnRTUUxGaWxlIHNlcnZpY2Ugd2hpY2ggcHJvcGVybHkgc2V0cyB1cCBNeVNRTCBlbnZpcm9ubWVudFxuICAgICAgICAgIGlmICghaW1wb3J0U1FMRmlsZVNlcnZpY2UpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogJ0ltcG9ydCBTUUwgZmlsZSBzZXJ2aWNlIG5vdCBhdmFpbGFibGUnLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBhd2FpdCBpbXBvcnRTUUxGaWxlU2VydmljZShzaXRlLCBzcWxQYXRoKTtcblxuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBTdWNjZXNzZnVsbHkgaW1wb3J0ZWQgZGF0YWJhc2UgZnJvbTogJHtzcWxQYXRofWApO1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gaW1wb3J0IGRhdGFiYXNlOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgb3BlbkFkbWluZXI6IGFzeW5jIChfcGFyZW50OiBhbnksIGFyZ3M6IHsgaW5wdXQ6IHsgc2l0ZUlkOiBzdHJpbmcgfSB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkIH0gPSBhcmdzLmlucHV0O1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3Qgc2l0ZSA9IHNpdGVEYXRhLmdldFNpdGUoc2l0ZUlkKTtcbiAgICAgICAgICBpZiAoIXNpdGUpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgbm90IGZvdW5kOiAke3NpdGVJZH1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBDaGVjayBpZiBzaXRlIGlzIHJ1bm5pbmcgLSBkYXRhYmFzZSBtdXN0IGJlIGFjY2Vzc2libGVcbiAgICAgICAgICBjb25zdCBzdGF0dXMgPSBhd2FpdCBzaXRlUHJvY2Vzc01hbmFnZXIuZ2V0U2l0ZVN0YXR1cyhzaXRlKTtcbiAgICAgICAgICBpZiAoc3RhdHVzICE9PSAncnVubmluZycpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgXCIke3NpdGUubmFtZX1cIiBtdXN0IGJlIHJ1bm5pbmcgdG8gb3BlbiBBZG1pbmVyLiBTdGFydCBpdCBmaXJzdC5gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gT3BlbmluZyBBZG1pbmVyIGZvciAke3NpdGUubmFtZX1gKTtcblxuICAgICAgICAgIGlmIChhZG1pbmVyKSB7XG4gICAgICAgICAgICBhd2FpdCBhZG1pbmVyLm9wZW4oc2l0ZSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogJ0FkbWluZXIgc2VydmljZSBub3QgYXZhaWxhYmxlJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gb3BlbiBBZG1pbmVyOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgdHJ1c3RTc2w6IGFzeW5jIChfcGFyZW50OiBhbnksIGFyZ3M6IHsgaW5wdXQ6IHsgc2l0ZUlkOiBzdHJpbmcgfSB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkIH0gPSBhcmdzLmlucHV0O1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3Qgc2l0ZSA9IHNpdGVEYXRhLmdldFNpdGUoc2l0ZUlkKTtcbiAgICAgICAgICBpZiAoIXNpdGUpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgbm90IGZvdW5kOiAke3NpdGVJZH1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gVHJ1c3RpbmcgU1NMIGZvciAke3NpdGUubmFtZX1gKTtcblxuICAgICAgICAgIGlmICh4NTA5Q2VydCkge1xuICAgICAgICAgICAgYXdhaXQgeDUwOUNlcnQudHJ1c3RDZXJ0KHNpdGUpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6ICdYNTA5IGNlcnRpZmljYXRlIHNlcnZpY2Ugbm90IGF2YWlsYWJsZScsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIHRydXN0IFNTTDpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIG1jcFJlbmFtZVNpdGU6IGFzeW5jIChfcGFyZW50OiBhbnksIGFyZ3M6IHsgaW5wdXQ6IHsgc2l0ZUlkOiBzdHJpbmc7IG5ld05hbWU6IHN0cmluZyB9IH0pID0+IHtcbiAgICAgICAgY29uc3QgeyBzaXRlSWQsIG5ld05hbWUgfSA9IGFyZ3MuaW5wdXQ7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBzaXRlID0gc2l0ZURhdGEuZ2V0U2l0ZShzaXRlSWQpO1xuICAgICAgICAgIGlmICghc2l0ZSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7c2l0ZUlkfWAsXG4gICAgICAgICAgICAgIG5ld05hbWU6IG51bGwsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBSZW5hbWluZyAke3NpdGUubmFtZX0gdG8gJHtuZXdOYW1lfWApO1xuXG4gICAgICAgICAgLy8gVXBkYXRlIHNpdGUgbmFtZSB2aWEgc2l0ZURhdGFcbiAgICAgICAgICBzaXRlLm5hbWUgPSBuZXdOYW1lO1xuICAgICAgICAgIGF3YWl0IHNpdGVEYXRhLnVwZGF0ZVNpdGUoc2l0ZUlkLCB7IG5hbWU6IG5ld05hbWUgfSk7XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gU3VjY2Vzc2Z1bGx5IHJlbmFtZWQgc2l0ZSB0bzogJHtuZXdOYW1lfWApO1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICAgIG5ld05hbWUsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIHJlbmFtZSBzaXRlOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgICBuZXdOYW1lOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIGNoYW5nZVBocFZlcnNpb246IGFzeW5jIChcbiAgICAgICAgX3BhcmVudDogYW55LFxuICAgICAgICBhcmdzOiB7IGlucHV0OiB7IHNpdGVJZDogc3RyaW5nOyBwaHBWZXJzaW9uOiBzdHJpbmcgfSB9XG4gICAgICApID0+IHtcbiAgICAgICAgY29uc3QgeyBzaXRlSWQsIHBocFZlcnNpb24gfSA9IGFyZ3MuaW5wdXQ7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBzaXRlID0gc2l0ZURhdGEuZ2V0U2l0ZShzaXRlSWQpO1xuICAgICAgICAgIGlmICghc2l0ZSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7c2l0ZUlkfWAsXG4gICAgICAgICAgICAgIHBocFZlcnNpb246IG51bGwsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIENoYW5naW5nIFBIUCB2ZXJzaW9uIGZvciAke3NpdGUubmFtZX0gdG8gJHtwaHBWZXJzaW9ufWBcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgaWYgKHNpdGVQcm92aXNpb25lcikge1xuICAgICAgICAgICAgYXdhaXQgc2l0ZVByb3Zpc2lvbmVyLnN3YXBTZXJ2aWNlKHNpdGUsICdwaHAnLCBwaHBWZXJzaW9uKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIGVycm9yOiAnU2l0ZSBwcm92aXNpb25lciBzZXJ2aWNlIG5vdCBhdmFpbGFibGUnLFxuICAgICAgICAgICAgICBwaHBWZXJzaW9uOiBudWxsLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gU3VjY2Vzc2Z1bGx5IGNoYW5nZWQgUEhQIHZlcnNpb24gdG86ICR7cGhwVmVyc2lvbn1gKTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICBwaHBWZXJzaW9uLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBjaGFuZ2UgUEhQIHZlcnNpb246YCwgZXJyb3IpO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdVbmtub3duIGVycm9yJyxcbiAgICAgICAgICAgIHBocFZlcnNpb246IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgaW1wb3J0U2l0ZTogYXN5bmMgKF9wYXJlbnQ6IGFueSwgYXJnczogeyBpbnB1dDogeyB6aXBQYXRoOiBzdHJpbmc7IHNpdGVOYW1lPzogc3RyaW5nIH0gfSkgPT4ge1xuICAgICAgICBjb25zdCB7IHppcFBhdGgsIHNpdGVOYW1lIH0gPSBhcmdzLmlucHV0O1xuICAgICAgICBjb25zdCBmcyA9IHJlcXVpcmUoJ2ZzJyk7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBpZiAoIWZzLmV4aXN0c1N5bmMoemlwUGF0aCkpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFppcCBmaWxlIG5vdCBmb3VuZDogJHt6aXBQYXRofWAsXG4gICAgICAgICAgICAgIHNpdGVJZDogbnVsbCxcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IG51bGwsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBJbXBvcnRpbmcgc2l0ZSBmcm9tICR7emlwUGF0aH1gKTtcblxuICAgICAgICAgIGlmICghaW1wb3J0U2l0ZVNlcnZpY2UpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogJ0ltcG9ydCBzaXRlIHNlcnZpY2Ugbm90IGF2YWlsYWJsZScsXG4gICAgICAgICAgICAgIHNpdGVJZDogbnVsbCxcbiAgICAgICAgICAgICAgc2l0ZU5hbWU6IG51bGwsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGltcG9ydFNpdGVTZXJ2aWNlLnJ1bih7XG4gICAgICAgICAgICB6aXBQYXRoLFxuICAgICAgICAgICAgc2l0ZU5hbWU6IHNpdGVOYW1lIHx8IHVuZGVmaW5lZCxcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBTdWNjZXNzZnVsbHkgaW1wb3J0ZWQgc2l0ZTogJHtyZXN1bHQubmFtZX1gKTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICBzaXRlSWQ6IHJlc3VsdC5pZCxcbiAgICAgICAgICAgIHNpdGVOYW1lOiByZXN1bHQubmFtZSxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gaW1wb3J0IHNpdGU6YCwgZXJyb3IpO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdVbmtub3duIGVycm9yJyxcbiAgICAgICAgICAgIHNpdGVJZDogbnVsbCxcbiAgICAgICAgICAgIHNpdGVOYW1lOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIC8vIFBoYXNlIDk6IFNpdGUgQ29uZmlndXJhdGlvbiAmIERldiBUb29sc1xuICAgICAgdG9nZ2xlWGRlYnVnOiBhc3luYyAoX3BhcmVudDogYW55LCBhcmdzOiB7IGlucHV0OiB7IHNpdGVJZDogc3RyaW5nOyBlbmFibGVkOiBib29sZWFuIH0gfSkgPT4ge1xuICAgICAgICBjb25zdCB7IHNpdGVJZCwgZW5hYmxlZCB9ID0gYXJncy5pbnB1dDtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIG5vdCBmb3VuZDogJHtzaXRlSWR9YCxcbiAgICAgICAgICAgICAgZW5hYmxlZDogbnVsbCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBbJHtBRERPTl9OQU1FfV0gJHtlbmFibGVkID8gJ0VuYWJsaW5nJyA6ICdEaXNhYmxpbmcnfSBYZGVidWcgZm9yICR7c2l0ZS5uYW1lfWBcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgLy8gVXBkYXRlIHRoZSBzaXRlJ3MgeGRlYnVnRW5hYmxlZCBwcm9wZXJ0eVxuICAgICAgICAgIGF3YWl0IHNpdGVEYXRhLnVwZGF0ZVNpdGUoc2l0ZUlkLCB7IHhkZWJ1Z0VuYWJsZWQ6IGVuYWJsZWQgfSk7XG5cbiAgICAgICAgICAvLyBSZXN0YXJ0IHRoZSBzaXRlIGlmIGl0J3MgcnVubmluZyB0byBhcHBseSB0aGUgY2hhbmdlXG4gICAgICAgICAgY29uc3Qgc3RhdHVzID0gYXdhaXQgc2l0ZVByb2Nlc3NNYW5hZ2VyLmdldFNpdGVTdGF0dXMoc2l0ZSk7XG4gICAgICAgICAgaWYgKHN0YXR1cyA9PT0gJ3J1bm5pbmcnKSB7XG4gICAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gUmVzdGFydGluZyBzaXRlIHRvIGFwcGx5IFhkZWJ1ZyBjaGFuZ2VgKTtcbiAgICAgICAgICAgIGF3YWl0IHNpdGVQcm9jZXNzTWFuYWdlci5yZXN0YXJ0KHNpdGUpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIFN1Y2Nlc3NmdWxseSAke2VuYWJsZWQgPyAnZW5hYmxlZCcgOiAnZGlzYWJsZWQnfSBYZGVidWdgXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICBlbmFibGVkLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byB0b2dnbGUgWGRlYnVnOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgICBlbmFibGVkOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIGdldFNpdGVMb2dzOiBhc3luYyAoXG4gICAgICAgIF9wYXJlbnQ6IGFueSxcbiAgICAgICAgYXJnczogeyBpbnB1dDogeyBzaXRlSWQ6IHN0cmluZzsgbG9nVHlwZT86IHN0cmluZzsgbGluZXM/OiBudW1iZXIgfSB9XG4gICAgICApID0+IHtcbiAgICAgICAgY29uc3QgeyBzaXRlSWQsIGxvZ1R5cGUgPSAncGhwJywgbGluZXMgPSAxMDAgfSA9IGFyZ3MuaW5wdXQ7XG4gICAgICAgIGNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKTtcbiAgICAgICAgY29uc3QgZnNQcm9taXNlcyA9IGZzLnByb21pc2VzO1xuICAgICAgICBjb25zdCBwYXRoTW9kdWxlID0gcmVxdWlyZSgncGF0aCcpO1xuXG4gICAgICAgIC8vIEhlbHBlciBmb3IgYXN5bmMgZmlsZSBleGlzdGVuY2UgY2hlY2tcbiAgICAgICAgY29uc3QgZmlsZUV4aXN0cyA9IGFzeW5jIChmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiA9PiB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IGZzUHJvbWlzZXMuYWNjZXNzKGZpbGVQYXRoKTtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICAvLyBIZWxwZXIgdG8gcmVhZCBsYXN0IE4gbGluZXMgb2YgYSBmaWxlXG4gICAgICAgIGNvbnN0IHJlYWRMYXN0TGluZXMgPSBhc3luYyAoZmlsZVBhdGg6IHN0cmluZywgbnVtTGluZXM6IG51bWJlcik6IFByb21pc2U8c3RyaW5nPiA9PiB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRGaWxlKGZpbGVQYXRoLCAndXRmLTgnKTtcbiAgICAgICAgICAgIGNvbnN0IGxvZ0xpbmVzID0gY29udGVudC5zcGxpdCgnXFxuJyk7XG4gICAgICAgICAgICByZXR1cm4gbG9nTGluZXMuc2xpY2UoLW51bUxpbmVzKS5qb2luKCdcXG4nKSB8fCAnKGVtcHR5KSc7XG4gICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICByZXR1cm4gJyc7XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3Qgc2l0ZSA9IHNpdGVEYXRhLmdldFNpdGUoc2l0ZUlkKTtcbiAgICAgICAgICBpZiAoIXNpdGUpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgbm90IGZvdW5kOiAke3NpdGVJZH1gLFxuICAgICAgICAgICAgICBsb2dzOiBbXSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIEdldHRpbmcgJHtsb2dUeXBlfSBsb2dzIGZvciAke3NpdGUubmFtZX1gKTtcblxuICAgICAgICAgIGNvbnN0IGxvZ3M6IEFycmF5PHsgdHlwZTogc3RyaW5nOyBjb250ZW50OiBzdHJpbmc7IHBhdGg6IHN0cmluZyB9PiA9IFtdO1xuICAgICAgICAgIGNvbnN0IGxvZ3NEaXIgPSBwYXRoTW9kdWxlLmpvaW4oc2l0ZS5wYXRoLCAnbG9ncycpO1xuXG4gICAgICAgICAgY29uc3QgbG9nRmlsZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdPiA9IHtcbiAgICAgICAgICAgIHBocDogWydwaHAnLCAncGhwLWZwbSddLFxuICAgICAgICAgICAgbmdpbng6IFsnbmdpbngnXSxcbiAgICAgICAgICAgIG15c3FsOiBbJ215c3FsJ10sXG4gICAgICAgICAgICBhbGw6IFsncGhwJywgJ3BocC1mcG0nLCAnbmdpbngnLCAnbXlzcWwnXSxcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgY29uc3QgdGFyZ2V0TG9ncyA9IGxvZ0ZpbGVzW2xvZ1R5cGVdIHx8IGxvZ0ZpbGVzLnBocDtcblxuICAgICAgICAgIGZvciAoY29uc3QgbG9nTmFtZSBvZiB0YXJnZXRMb2dzKSB7XG4gICAgICAgICAgICAvLyBDaGVjayBmb3IgZXJyb3IgYW5kIGFjY2VzcyBsb2dzXG4gICAgICAgICAgICBmb3IgKGNvbnN0IHN1ZmZpeCBvZiBbJ2Vycm9yLmxvZycsICdhY2Nlc3MubG9nJywgJy5sb2cnXSkge1xuICAgICAgICAgICAgICBjb25zdCBsb2dQYXRoID0gcGF0aE1vZHVsZS5qb2luKFxuICAgICAgICAgICAgICAgIGxvZ3NEaXIsXG4gICAgICAgICAgICAgICAgYCR7bG9nTmFtZX0ke3N1ZmZpeCA9PT0gJy5sb2cnID8gJycgOiAnLyd9JHtzdWZmaXh9YFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBjb25zdCBhbHRMb2dQYXRoID0gcGF0aE1vZHVsZS5qb2luKGxvZ3NEaXIsIGAke2xvZ05hbWV9JHtzdWZmaXh9YCk7XG5cbiAgICAgICAgICAgICAgbGV0IGZpbmFsUGF0aDogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gICAgICAgICAgICAgIGlmIChhd2FpdCBmaWxlRXhpc3RzKGxvZ1BhdGgpKSB7XG4gICAgICAgICAgICAgICAgZmluYWxQYXRoID0gbG9nUGF0aDtcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChhd2FpdCBmaWxlRXhpc3RzKGFsdExvZ1BhdGgpKSB7XG4gICAgICAgICAgICAgICAgZmluYWxQYXRoID0gYWx0TG9nUGF0aDtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGlmIChmaW5hbFBhdGgpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgcmVhZExhc3RMaW5lcyhmaW5hbFBhdGgsIGxpbmVzKTtcbiAgICAgICAgICAgICAgICBpZiAoY29udGVudCkge1xuICAgICAgICAgICAgICAgICAgbG9ncy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogbG9nTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgY29udGVudCxcbiAgICAgICAgICAgICAgICAgICAgcGF0aDogZmluYWxQYXRoLFxuICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKGxvZ3MubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAvLyBUcnkgdG8gZmluZCBhbnkgbG9nIGZpbGVzXG4gICAgICAgICAgICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhsb2dzRGlyKSkge1xuICAgICAgICAgICAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgZnNQcm9taXNlcy5yZWFkZGlyKGxvZ3NEaXIsIHsgd2l0aEZpbGVUeXBlczogdHJ1ZSB9KTtcbiAgICAgICAgICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgICAgICAgICAgICAgaWYgKGVudHJ5LmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHN1YkRpciA9IHBhdGhNb2R1bGUuam9pbihsb2dzRGlyLCBlbnRyeS5uYW1lKTtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHN1YkVudHJpZXMgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRkaXIoc3ViRGlyKTtcbiAgICAgICAgICAgICAgICAgIGZvciAoY29uc3Qgc3ViRmlsZSBvZiBzdWJFbnRyaWVzKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChzdWJGaWxlLmVuZHNXaXRoKCcubG9nJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCBsb2dQYXRoID0gcGF0aE1vZHVsZS5qb2luKHN1YkRpciwgc3ViRmlsZSk7XG4gICAgICAgICAgICAgICAgICAgICAgY29uc3QgY29udGVudCA9IGF3YWl0IHJlYWRMYXN0TGluZXMobG9nUGF0aCwgbGluZXMpO1xuICAgICAgICAgICAgICAgICAgICAgIGlmIChjb250ZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsb2dzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiBlbnRyeS5uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgICBjb250ZW50LFxuICAgICAgICAgICAgICAgICAgICAgICAgICBwYXRoOiBsb2dQYXRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChlbnRyeS5uYW1lLmVuZHNXaXRoKCcubG9nJykpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGxvZ1BhdGggPSBwYXRoTW9kdWxlLmpvaW4obG9nc0RpciwgZW50cnkubmFtZSk7XG4gICAgICAgICAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgcmVhZExhc3RMaW5lcyhsb2dQYXRoLCBsaW5lcyk7XG4gICAgICAgICAgICAgICAgICBpZiAoY29udGVudCkge1xuICAgICAgICAgICAgICAgICAgICBsb2dzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IGVudHJ5Lm5hbWUucmVwbGFjZSgnLmxvZycsICcnKSxcbiAgICAgICAgICAgICAgICAgICAgICBjb250ZW50LFxuICAgICAgICAgICAgICAgICAgICAgIHBhdGg6IGxvZ1BhdGgsXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgICBsb2dzLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBnZXQgbG9nczpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgICAgbG9nczogW10sXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgLy8gUGhhc2UgMTA6IENsb3VkIEJhY2t1cCBNdXRhdGlvbnNcbiAgICAgIGNyZWF0ZUJhY2t1cDogYXN5bmMgKFxuICAgICAgICBfcGFyZW50OiBhbnksXG4gICAgICAgIGFyZ3M6IHsgc2l0ZUlkOiBzdHJpbmc7IHByb3ZpZGVyOiBzdHJpbmc7IG5vdGU/OiBzdHJpbmcgfVxuICAgICAgKSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkLCBwcm92aWRlciwgbm90ZSB9ID0gYXJncztcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBDcmVhdGluZyBiYWNrdXAgZm9yIHNpdGUgJHtzaXRlSWR9IHRvICR7cHJvdmlkZXJ9YCk7XG5cbiAgICAgICAgICAvLyBHZXQgc2l0ZVxuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgdGltZXN0YW1wOiBudWxsLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgbm90IGZvdW5kOiAke3NpdGVJZH1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBHZXQgcHJvdmlkZXJzIGZyb20gQ2xvdWQgQmFja3VwcyBhZGRvblxuICAgICAgICAgIGNvbnN0IHByb3ZpZGVycyA9IGF3YWl0IGdldEJhY2t1cFByb3ZpZGVycygpO1xuICAgICAgICAgIGlmIChwcm92aWRlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgdGltZXN0YW1wOiBudWxsLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjpcbiAgICAgICAgICAgICAgICAnTm8gY2xvdWQgc3RvcmFnZSBwcm92aWRlcnMgY29uZmlndXJlZC4gQ29ubmVjdCBHb29nbGUgRHJpdmUgb3IgRHJvcGJveCBpbiBMb2NhbCBIdWIuJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gRmluZCB0aGUgbWF0Y2hpbmcgcHJvdmlkZXIgKG1hcCAnZ29vZ2xlRHJpdmUnIHRvICdnb29nbGUnIGZvciB0aGUgYWRkb24pXG4gICAgICAgICAgY29uc3QgcHJvdmlkZXJNYXA6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7IGdvb2dsZURyaXZlOiAnZ29vZ2xlJywgZHJvcGJveDogJ2Ryb3Bib3gnIH07XG4gICAgICAgICAgY29uc3QgcHJvdmlkZXJJZCA9IHByb3ZpZGVyTWFwW3Byb3ZpZGVyXSB8fCBwcm92aWRlcjtcbiAgICAgICAgICBjb25zdCBtYXRjaGVkUHJvdmlkZXIgPSBwcm92aWRlcnMuZmluZCgocDogYW55KSA9PiBwLmlkID09PSBwcm92aWRlcklkKTtcblxuICAgICAgICAgIGlmICghbWF0Y2hlZFByb3ZpZGVyKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgdGltZXN0YW1wOiBudWxsLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogYFByb3ZpZGVyICcke3Byb3ZpZGVyfScgbm90IGNvbmZpZ3VyZWQuIEF2YWlsYWJsZTogJHtwcm92aWRlcnMubWFwKChwOiBhbnkpID0+IHAubmFtZSkuam9pbignLCAnKX1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBNYXAgdGhlIEh1YiBwcm92aWRlciBJRCB0byByY2xvbmUgYmFja2VuZCBuYW1lXG4gICAgICAgICAgLy8gVGhlIGFkZG9uIHVzZXMgJ2dvb2dsZScgaW4gZW5hYmxlZC1wcm92aWRlcnMgYnV0IGV4cGVjdHMgJ2RyaXZlJyBmb3IgYmFja3VwIG9wZXJhdGlvbnNcbiAgICAgICAgICBjb25zdCBiYWNrdXBQcm92aWRlck1hcDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHsgZ29vZ2xlOiAnZHJpdmUnLCBkcm9wYm94OiAnZHJvcGJveCcgfTtcbiAgICAgICAgICBjb25zdCBiYWNrdXBQcm92aWRlcklkID0gYmFja3VwUHJvdmlkZXJNYXBbbWF0Y2hlZFByb3ZpZGVyLmlkXSB8fCBtYXRjaGVkUHJvdmlkZXIuaWQ7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBbJHtBRERPTl9OQU1FfV0gVXNpbmcgYmFja3VwIHByb3ZpZGVyIElEOiAke2JhY2t1cFByb3ZpZGVySWR9IChmcm9tICR7bWF0Y2hlZFByb3ZpZGVyLmlkfSlgXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIC8vIENyZWF0ZSBiYWNrdXAgdmlhIElQQyAodXNlIGxvbmcgdGltZW91dCBmb3IgYmFja3VwIG9wZXJhdGlvbnMpXG4gICAgICAgICAgY29uc3QgZGVzY3JpcHRpb24gPSBub3RlIHx8ICdCYWNrdXAgY3JlYXRlZCB2aWEgTUNQJztcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBpbnZva2VCYWNrdXBJUEMoXG4gICAgICAgICAgICAnYmFja3VwczpiYWNrdXAtc2l0ZScsXG4gICAgICAgICAgICBCQUNLVVBfSVBDX1RJTUVPVVQsXG4gICAgICAgICAgICBzaXRlSWQsXG4gICAgICAgICAgICBiYWNrdXBQcm92aWRlcklkLFxuICAgICAgICAgICAgZGVzY3JpcHRpb25cbiAgICAgICAgICApO1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBCYWNrdXAgSVBDIHJlc3VsdDogJHtKU09OLnN0cmluZ2lmeShyZXN1bHQpfWApO1xuXG4gICAgICAgICAgLy8gQ2hlY2sgZm9yIHRvcC1sZXZlbCBJUEMgZXJyb3JcbiAgICAgICAgICBpZiAocmVzdWx0LmVycm9yKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgdGltZXN0YW1wOiBudWxsLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogcmVzdWx0LmVycm9yLm1lc3NhZ2UgfHwgJ0JhY2t1cCBjcmVhdGlvbiBmYWlsZWQnLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBVbndyYXAgbmVzdGVkIHJlc3VsdCBzdHJ1Y3R1cmUgLSB0aGUgYWN0dWFsIHJlc3VsdCBpcyBhdCByZXN1bHQucmVzdWx0XG4gICAgICAgICAgY29uc3QgYmFja3VwUmVzdWx0ID0gcmVzdWx0LnJlc3VsdDtcblxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSBiYWNrdXAgcmVzdWx0IGNvbnRhaW5zIGFuIGVycm9yIChuZXN0ZWQgYXQgcmVzdWx0LnJlc3VsdC5lcnJvcilcbiAgICAgICAgICBpZiAoYmFja3VwUmVzdWx0Py5lcnJvcikge1xuICAgICAgICAgICAgY29uc3QgZXJyb3JNc2cgPVxuICAgICAgICAgICAgICBiYWNrdXBSZXN1bHQuZXJyb3IubWVzc2FnZSB8fCBiYWNrdXBSZXN1bHQuZXJyb3Iub3JpZ2luYWw/Lm1lc3NhZ2UgfHwgJ0JhY2t1cCBmYWlsZWQnO1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIHNuYXBzaG90SWQ6IG51bGwsXG4gICAgICAgICAgICAgIHRpbWVzdGFtcDogbnVsbCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6IGVycm9yTXNnLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBUcnkgdG8gZXh0cmFjdCBzbmFwc2hvdCBJRCAobWF5IGJlIG5lc3RlZCBpbiByZXN1bHQucmVzdWx0LnJlc3VsdClcbiAgICAgICAgICBsZXQgc25hcHNob3RJZCA9IGJhY2t1cFJlc3VsdD8uc25hcHNob3RJZCB8fCBiYWNrdXBSZXN1bHQ/LmlkO1xuICAgICAgICAgIGlmICghc25hcHNob3RJZCAmJiBiYWNrdXBSZXN1bHQ/LnJlc3VsdCkge1xuICAgICAgICAgICAgc25hcHNob3RJZCA9IGJhY2t1cFJlc3VsdC5yZXN1bHQuc25hcHNob3RJZCB8fCBiYWNrdXBSZXN1bHQucmVzdWx0LmlkO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIElmIG5vIGVycm9yIHdhcyByZXR1cm5lZCwgdGhlIGJhY2t1cCBzdWNjZWVkZWQgZXZlbiBpZiBubyBzbmFwc2hvdCBJRCBpcyBwcm92aWRlZFxuICAgICAgICAgIC8vIFRoZSBhZGRvbiBkb2Vzbid0IGFsd2F5cyByZXR1cm4gdGhlIHNuYXBzaG90IElEIGluIGl0cyBJUEMgcmVzcG9uc2VcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIHNuYXBzaG90SWQ6IHNuYXBzaG90SWQgfHwgbnVsbCxcbiAgICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICAgICAgbWVzc2FnZTogYEJhY2t1cCBjcmVhdGVkIHN1Y2Nlc3NmdWxseSB0byAke21hdGNoZWRQcm92aWRlci5uYW1lfWAsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gY3JlYXRlIGJhY2t1cDpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgc25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgIHRpbWVzdGFtcDogbnVsbCxcbiAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnVW5rbm93biBlcnJvcicsXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgcmVzdG9yZUJhY2t1cDogYXN5bmMgKFxuICAgICAgICBfcGFyZW50OiBhbnksXG4gICAgICAgIGFyZ3M6IHsgc2l0ZUlkOiBzdHJpbmc7IHByb3ZpZGVyOiBzdHJpbmc7IHNuYXBzaG90SWQ6IHN0cmluZzsgY29uZmlybT86IGJvb2xlYW4gfVxuICAgICAgKSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkLCBwcm92aWRlciwgc25hcHNob3RJZCwgY29uZmlybSA9IGZhbHNlIH0gPSBhcmdzO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIFJlc3RvcmluZyBiYWNrdXAgJHtzbmFwc2hvdElkfSBmb3Igc2l0ZSAke3NpdGVJZH1gKTtcblxuICAgICAgICAgIC8vIENoZWNrIGNvbmZpcm1hdGlvblxuICAgICAgICAgIGlmICghY29uZmlybSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOlxuICAgICAgICAgICAgICAgICdSZXN0b3JlIHJlcXVpcmVzIGNvbmZpcm09dHJ1ZSB0byBwcmV2ZW50IGFjY2lkZW50YWwgZGF0YSBsb3NzLiBDdXJyZW50IHNpdGUgZmlsZXMgYW5kIGRhdGFiYXNlIHdpbGwgYmUgb3ZlcndyaXR0ZW4uJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gR2V0IHNpdGVcbiAgICAgICAgICBjb25zdCBzaXRlID0gc2l0ZURhdGEuZ2V0U2l0ZShzaXRlSWQpO1xuICAgICAgICAgIGlmICghc2l0ZSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7c2l0ZUlkfWAsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEdldCBwcm92aWRlcnMgZnJvbSBDbG91ZCBCYWNrdXBzIGFkZG9uXG4gICAgICAgICAgY29uc3QgcHJvdmlkZXJzID0gYXdhaXQgZ2V0QmFja3VwUHJvdmlkZXJzKCk7XG4gICAgICAgICAgaWYgKHByb3ZpZGVycy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjpcbiAgICAgICAgICAgICAgICAnTm8gY2xvdWQgc3RvcmFnZSBwcm92aWRlcnMgY29uZmlndXJlZC4gQ29ubmVjdCBHb29nbGUgRHJpdmUgb3IgRHJvcGJveCBpbiBMb2NhbCBIdWIuJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gRmluZCB0aGUgbWF0Y2hpbmcgcHJvdmlkZXIgKG1hcCAnZ29vZ2xlRHJpdmUnIHRvICdnb29nbGUnIGZvciB0aGUgYWRkb24pXG4gICAgICAgICAgY29uc3QgcHJvdmlkZXJNYXA6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7IGdvb2dsZURyaXZlOiAnZ29vZ2xlJywgZHJvcGJveDogJ2Ryb3Bib3gnIH07XG4gICAgICAgICAgY29uc3QgcHJvdmlkZXJJZCA9IHByb3ZpZGVyTWFwW3Byb3ZpZGVyXSB8fCBwcm92aWRlcjtcbiAgICAgICAgICBjb25zdCBtYXRjaGVkUHJvdmlkZXIgPSBwcm92aWRlcnMuZmluZCgocDogYW55KSA9PiBwLmlkID09PSBwcm92aWRlcklkKTtcblxuICAgICAgICAgIGlmICghbWF0Y2hlZFByb3ZpZGVyKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6IGBQcm92aWRlciAnJHtwcm92aWRlcn0nIG5vdCBjb25maWd1cmVkLiBBdmFpbGFibGU6ICR7cHJvdmlkZXJzLm1hcCgocDogYW55KSA9PiBwLm5hbWUpLmpvaW4oJywgJyl9YCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gTWFwIHRoZSBIdWIgcHJvdmlkZXIgSUQgdG8gcmNsb25lIGJhY2tlbmQgbmFtZVxuICAgICAgICAgIGNvbnN0IGJhY2t1cFByb3ZpZGVyTWFwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0geyBnb29nbGU6ICdkcml2ZScsIGRyb3Bib3g6ICdkcm9wYm94JyB9O1xuICAgICAgICAgIGNvbnN0IGJhY2t1cFByb3ZpZGVySWQgPSBiYWNrdXBQcm92aWRlck1hcFttYXRjaGVkUHJvdmlkZXIuaWRdIHx8IG1hdGNoZWRQcm92aWRlci5pZDtcblxuICAgICAgICAgIC8vIFJlc3RvcmUgYmFja3VwIHZpYSBJUEMgKHVzZSBsb25nIHRpbWVvdXQgZm9yIHJlc3RvcmUgb3BlcmF0aW9ucylcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBpbnZva2VCYWNrdXBJUEMoXG4gICAgICAgICAgICAnYmFja3VwczpyZXN0b3JlLWJhY2t1cCcsXG4gICAgICAgICAgICBCQUNLVVBfSVBDX1RJTUVPVVQsXG4gICAgICAgICAgICBzaXRlSWQsXG4gICAgICAgICAgICBiYWNrdXBQcm92aWRlcklkLFxuICAgICAgICAgICAgc25hcHNob3RJZFxuICAgICAgICAgICk7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIFJlc3RvcmUgcmVzdWx0OiAke0pTT04uc3RyaW5naWZ5KHJlc3VsdCl9YCk7XG5cbiAgICAgICAgICAvLyBDaGVjayBmb3IgZXJyb3JzIC0gY2FuIGJlIGF0IHJlc3VsdC5lcnJvciBvciByZXN1bHQucmVzdWx0LmVycm9yIChJUEMgYXN5bmMgcGF0dGVybilcbiAgICAgICAgICBjb25zdCBpcGNFcnJvciA9IHJlc3VsdC5lcnJvciB8fCByZXN1bHQucmVzdWx0Py5lcnJvcjtcbiAgICAgICAgICBpZiAoaXBjRXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnN0IGVycm9yTWVzc2FnZSA9XG4gICAgICAgICAgICAgIHR5cGVvZiBpcGNFcnJvciA9PT0gJ3N0cmluZycgPyBpcGNFcnJvciA6IGlwY0Vycm9yLm1lc3NhZ2UgfHwgJ1Jlc3RvcmUgZmFpbGVkJztcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogZXJyb3JNZXNzYWdlLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIG1lc3NhZ2U6IGBTaXRlIHJlc3RvcmVkIGZyb20gYmFja3VwICR7c25hcHNob3RJZH1gLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIHJlc3RvcmUgYmFja3VwOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIGRlbGV0ZUJhY2t1cDogYXN5bmMgKFxuICAgICAgICBfcGFyZW50OiBhbnksXG4gICAgICAgIGFyZ3M6IHsgc2l0ZUlkOiBzdHJpbmc7IHByb3ZpZGVyOiBzdHJpbmc7IHNuYXBzaG90SWQ6IHN0cmluZzsgY29uZmlybT86IGJvb2xlYW4gfVxuICAgICAgKSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkLCBwcm92aWRlciwgc25hcHNob3RJZCwgY29uZmlybSA9IGZhbHNlIH0gPSBhcmdzO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIERlbGV0aW5nIGJhY2t1cCAke3NuYXBzaG90SWR9IGZvciBzaXRlICR7c2l0ZUlkfWApO1xuXG4gICAgICAgICAgLy8gQ2hlY2sgY29uZmlybWF0aW9uXG4gICAgICAgICAgaWYgKCFjb25maXJtKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZGVsZXRlZFNuYXBzaG90SWQ6IG51bGwsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOiAnRGVsZXRlIHJlcXVpcmVzIGNvbmZpcm09dHJ1ZSB0byBwcmV2ZW50IGFjY2lkZW50YWwgZGVsZXRpb24uJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gR2V0IHNpdGVcbiAgICAgICAgICBjb25zdCBzaXRlID0gc2l0ZURhdGEuZ2V0U2l0ZShzaXRlSWQpO1xuICAgICAgICAgIGlmICghc2l0ZSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIGRlbGV0ZWRTbmFwc2hvdElkOiBudWxsLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogYFNpdGUgbm90IGZvdW5kOiAke3NpdGVJZH1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBHZXQgcHJvdmlkZXJzIGZyb20gQ2xvdWQgQmFja3VwcyBhZGRvblxuICAgICAgICAgIGNvbnN0IHByb3ZpZGVycyA9IGF3YWl0IGdldEJhY2t1cFByb3ZpZGVycygpO1xuICAgICAgICAgIGlmIChwcm92aWRlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZGVsZXRlZFNuYXBzaG90SWQ6IG51bGwsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOlxuICAgICAgICAgICAgICAgICdObyBjbG91ZCBzdG9yYWdlIHByb3ZpZGVycyBjb25maWd1cmVkLiBDb25uZWN0IEdvb2dsZSBEcml2ZSBvciBEcm9wYm94IGluIExvY2FsIEh1Yi4nLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBGaW5kIHRoZSBtYXRjaGluZyBwcm92aWRlciAobWFwICdnb29nbGVEcml2ZScgdG8gJ2dvb2dsZScgZm9yIHRoZSBhZGRvbilcbiAgICAgICAgICBjb25zdCBwcm92aWRlck1hcDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHsgZ29vZ2xlRHJpdmU6ICdnb29nbGUnLCBkcm9wYm94OiAnZHJvcGJveCcgfTtcbiAgICAgICAgICBjb25zdCBwcm92aWRlcklkID0gcHJvdmlkZXJNYXBbcHJvdmlkZXJdIHx8IHByb3ZpZGVyO1xuICAgICAgICAgIGNvbnN0IG1hdGNoZWRQcm92aWRlciA9IHByb3ZpZGVycy5maW5kKChwOiBhbnkpID0+IHAuaWQgPT09IHByb3ZpZGVySWQpO1xuXG4gICAgICAgICAgaWYgKCFtYXRjaGVkUHJvdmlkZXIpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBkZWxldGVkU25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6IGBQcm92aWRlciAnJHtwcm92aWRlcn0nIG5vdCBjb25maWd1cmVkLiBBdmFpbGFibGU6ICR7cHJvdmlkZXJzLm1hcCgocDogYW55KSA9PiBwLm5hbWUpLmpvaW4oJywgJyl9YCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gTWFwIHRoZSBIdWIgcHJvdmlkZXIgSUQgdG8gcmNsb25lIGJhY2tlbmQgbmFtZVxuICAgICAgICAgIGNvbnN0IGJhY2t1cFByb3ZpZGVyTWFwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0geyBnb29nbGU6ICdkcml2ZScsIGRyb3Bib3g6ICdkcm9wYm94JyB9O1xuICAgICAgICAgIGNvbnN0IGJhY2t1cFByb3ZpZGVySWQgPSBiYWNrdXBQcm92aWRlck1hcFttYXRjaGVkUHJvdmlkZXIuaWRdIHx8IG1hdGNoZWRQcm92aWRlci5pZDtcblxuICAgICAgICAgIC8vIFRyeSB0byBkZWxldGUgYmFja3VwIHZpYSBJUEMgKG1heSBub3QgYmUgc3VwcG9ydGVkIGJ5IHRoZSBhZGRvbilcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBpbnZva2VCYWNrdXBJUEMoXG4gICAgICAgICAgICAnYmFja3VwczpkZWxldGUtYmFja3VwJyxcbiAgICAgICAgICAgIERFRkFVTFRfSVBDX1RJTUVPVVQsXG4gICAgICAgICAgICBzaXRlSWQsXG4gICAgICAgICAgICBiYWNrdXBQcm92aWRlcklkLFxuICAgICAgICAgICAgc25hcHNob3RJZFxuICAgICAgICAgICk7XG5cbiAgICAgICAgICBpZiAocmVzdWx0LmVycm9yKSB7XG4gICAgICAgICAgICAvLyBJZiB0aGUgSVBDIGNoYW5uZWwgZG9lc24ndCBleGlzdCBvciBpc24ndCBzdXBwb3J0ZWQsIHByb3ZpZGUgaGVscGZ1bCBtZXNzYWdlXG4gICAgICAgICAgICBpZiAocmVzdWx0LmVycm9yLm1lc3NhZ2U/LmluY2x1ZGVzKCd0aW1lZCBvdXQnKSkge1xuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGRlbGV0ZWRTbmFwc2hvdElkOiBudWxsLFxuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgICAgZXJyb3I6XG4gICAgICAgICAgICAgICAgICAnRGVsZXRlIGJhY2t1cCBvcGVyYXRpb24gaXMgbm90IGF2YWlsYWJsZSB2aWEgTUNQLiBQbGVhc2UgZGVsZXRlIGJhY2t1cHMgdGhyb3VnaCB0aGUgTG9jYWwgVUkuJyxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBkZWxldGVkU25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6IHJlc3VsdC5lcnJvci5tZXNzYWdlIHx8ICdEZWxldGUgZmFpbGVkJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBkZWxldGVkU25hcHNob3RJZDogc25hcHNob3RJZCxcbiAgICAgICAgICAgIG1lc3NhZ2U6ICdCYWNrdXAgZGVsZXRlZCcsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBGYWlsZWQgdG8gZGVsZXRlIGJhY2t1cDpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZGVsZXRlZFNuYXBzaG90SWQ6IG51bGwsXG4gICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIGRvd25sb2FkQmFja3VwOiBhc3luYyAoXG4gICAgICAgIF9wYXJlbnQ6IGFueSxcbiAgICAgICAgYXJnczogeyBzaXRlSWQ6IHN0cmluZzsgcHJvdmlkZXI6IHN0cmluZzsgc25hcHNob3RJZDogc3RyaW5nIH1cbiAgICAgICkgPT4ge1xuICAgICAgICBjb25zdCB7IHNpdGVJZCwgcHJvdmlkZXIsIHNuYXBzaG90SWQgfSA9IGFyZ3M7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gRG93bmxvYWRpbmcgYmFja3VwICR7c25hcHNob3RJZH0gZm9yIHNpdGUgJHtzaXRlSWR9YCk7XG5cbiAgICAgICAgICAvLyBHZXQgc2l0ZVxuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgZmlsZVBhdGg6IG51bGwsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7c2l0ZUlkfWAsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEdldCBwcm92aWRlcnMgZnJvbSBDbG91ZCBCYWNrdXBzIGFkZG9uXG4gICAgICAgICAgY29uc3QgcHJvdmlkZXJzID0gYXdhaXQgZ2V0QmFja3VwUHJvdmlkZXJzKCk7XG4gICAgICAgICAgaWYgKHByb3ZpZGVycy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBmaWxlUGF0aDogbnVsbCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6XG4gICAgICAgICAgICAgICAgJ05vIGNsb3VkIHN0b3JhZ2UgcHJvdmlkZXJzIGNvbmZpZ3VyZWQuIENvbm5lY3QgR29vZ2xlIERyaXZlIG9yIERyb3Bib3ggaW4gTG9jYWwgSHViLicsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEZpbmQgdGhlIG1hdGNoaW5nIHByb3ZpZGVyIChtYXAgJ2dvb2dsZURyaXZlJyB0byAnZ29vZ2xlJyBmb3IgdGhlIGFkZG9uKVxuICAgICAgICAgIGNvbnN0IHByb3ZpZGVyTWFwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0geyBnb29nbGVEcml2ZTogJ2dvb2dsZScsIGRyb3Bib3g6ICdkcm9wYm94JyB9O1xuICAgICAgICAgIGNvbnN0IHByb3ZpZGVySWQgPSBwcm92aWRlck1hcFtwcm92aWRlcl0gfHwgcHJvdmlkZXI7XG4gICAgICAgICAgY29uc3QgbWF0Y2hlZFByb3ZpZGVyID0gcHJvdmlkZXJzLmZpbmQoKHA6IGFueSkgPT4gcC5pZCA9PT0gcHJvdmlkZXJJZCk7XG5cbiAgICAgICAgICBpZiAoIW1hdGNoZWRQcm92aWRlcikge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIGZpbGVQYXRoOiBudWxsLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogYFByb3ZpZGVyICcke3Byb3ZpZGVyfScgbm90IGNvbmZpZ3VyZWQuIEF2YWlsYWJsZTogJHtwcm92aWRlcnMubWFwKChwOiBhbnkpID0+IHAubmFtZSkuam9pbignLCAnKX1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBNYXAgdGhlIEh1YiBwcm92aWRlciBJRCB0byByY2xvbmUgYmFja2VuZCBuYW1lXG4gICAgICAgICAgY29uc3QgYmFja3VwUHJvdmlkZXJNYXA6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7IGdvb2dsZTogJ2RyaXZlJywgZHJvcGJveDogJ2Ryb3Bib3gnIH07XG4gICAgICAgICAgY29uc3QgYmFja3VwUHJvdmlkZXJJZCA9IGJhY2t1cFByb3ZpZGVyTWFwW21hdGNoZWRQcm92aWRlci5pZF0gfHwgbWF0Y2hlZFByb3ZpZGVyLmlkO1xuXG4gICAgICAgICAgLy8gVHJ5IHRvIGRvd25sb2FkIGJhY2t1cCB2aWEgSVBDICh1c2UgbG9uZyB0aW1lb3V0IGZvciBkb3dubG9hZHMpXG4gICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgaW52b2tlQmFja3VwSVBDKFxuICAgICAgICAgICAgJ2JhY2t1cHM6ZG93bmxvYWQtYmFja3VwJyxcbiAgICAgICAgICAgIEJBQ0tVUF9JUENfVElNRU9VVCxcbiAgICAgICAgICAgIHNpdGVJZCxcbiAgICAgICAgICAgIGJhY2t1cFByb3ZpZGVySWQsXG4gICAgICAgICAgICBzbmFwc2hvdElkXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICAgICAgICAgIC8vIElmIHRoZSBJUEMgY2hhbm5lbCBkb2Vzbid0IGV4aXN0IG9yIGlzbid0IHN1cHBvcnRlZCwgcHJvdmlkZSBoZWxwZnVsIG1lc3NhZ2VcbiAgICAgICAgICAgIGlmIChyZXN1bHQuZXJyb3IubWVzc2FnZT8uaW5jbHVkZXMoJ3RpbWVkIG91dCcpKSB7XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgICAgZmlsZVBhdGg6IG51bGwsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgICBlcnJvcjpcbiAgICAgICAgICAgICAgICAgICdEb3dubG9hZCBiYWNrdXAgb3BlcmF0aW9uIGlzIG5vdCBhdmFpbGFibGUgdmlhIE1DUC4gUGxlYXNlIGRvd25sb2FkIGJhY2t1cHMgdGhyb3VnaCB0aGUgTG9jYWwgVUkuJyxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBmaWxlUGF0aDogbnVsbCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6IHJlc3VsdC5lcnJvci5tZXNzYWdlIHx8ICdEb3dubG9hZCBmYWlsZWQnLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgIGZpbGVQYXRoOiByZXN1bHQucmVzdWx0Py5maWxlUGF0aCB8fCBudWxsLFxuICAgICAgICAgICAgbWVzc2FnZTogJ0JhY2t1cCBkb3dubG9hZGVkIHRvIERvd25sb2FkcyBmb2xkZXInLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGRvd25sb2FkIGJhY2t1cDpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZmlsZVBhdGg6IG51bGwsXG4gICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIGVkaXRCYWNrdXBOb3RlOiBhc3luYyAoXG4gICAgICAgIF9wYXJlbnQ6IGFueSxcbiAgICAgICAgYXJnczogeyBzaXRlSWQ6IHN0cmluZzsgcHJvdmlkZXI6IHN0cmluZzsgc25hcHNob3RJZDogc3RyaW5nOyBub3RlOiBzdHJpbmcgfVxuICAgICAgKSA9PiB7XG4gICAgICAgIGNvbnN0IHsgc2l0ZUlkLCBwcm92aWRlciwgc25hcHNob3RJZCwgbm90ZSB9ID0gYXJncztcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBFZGl0aW5nIGJhY2t1cCBub3RlIGZvciAke3NuYXBzaG90SWR9YCk7XG5cbiAgICAgICAgICAvLyBHZXQgc2l0ZVxuICAgICAgICAgIGNvbnN0IHNpdGUgPSBzaXRlRGF0YS5nZXRTaXRlKHNpdGVJZCk7XG4gICAgICAgICAgaWYgKCFzaXRlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgbm90ZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6IGBTaXRlIG5vdCBmb3VuZDogJHtzaXRlSWR9YCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gR2V0IHByb3ZpZGVycyBmcm9tIENsb3VkIEJhY2t1cHMgYWRkb25cbiAgICAgICAgICBjb25zdCBwcm92aWRlcnMgPSBhd2FpdCBnZXRCYWNrdXBQcm92aWRlcnMoKTtcbiAgICAgICAgICBpZiAocHJvdmlkZXJzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIHNuYXBzaG90SWQ6IG51bGwsXG4gICAgICAgICAgICAgIG5vdGU6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOlxuICAgICAgICAgICAgICAgICdObyBjbG91ZCBzdG9yYWdlIHByb3ZpZGVycyBjb25maWd1cmVkLiBDb25uZWN0IEdvb2dsZSBEcml2ZSBvciBEcm9wYm94IGluIExvY2FsIEh1Yi4nLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBGaW5kIHRoZSBtYXRjaGluZyBwcm92aWRlciAobWFwICdnb29nbGVEcml2ZScgdG8gJ2dvb2dsZScgZm9yIHRoZSBhZGRvbilcbiAgICAgICAgICBjb25zdCBwcm92aWRlck1hcDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHsgZ29vZ2xlRHJpdmU6ICdnb29nbGUnLCBkcm9wYm94OiAnZHJvcGJveCcgfTtcbiAgICAgICAgICBjb25zdCBwcm92aWRlcklkID0gcHJvdmlkZXJNYXBbcHJvdmlkZXJdIHx8IHByb3ZpZGVyO1xuICAgICAgICAgIGNvbnN0IG1hdGNoZWRQcm92aWRlciA9IHByb3ZpZGVycy5maW5kKChwOiBhbnkpID0+IHAuaWQgPT09IHByb3ZpZGVySWQpO1xuXG4gICAgICAgICAgaWYgKCFtYXRjaGVkUHJvdmlkZXIpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBzbmFwc2hvdElkOiBudWxsLFxuICAgICAgICAgICAgICBub3RlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogYFByb3ZpZGVyICcke3Byb3ZpZGVyfScgbm90IGNvbmZpZ3VyZWQuIEF2YWlsYWJsZTogJHtwcm92aWRlcnMubWFwKChwOiBhbnkpID0+IHAubmFtZSkuam9pbignLCAnKX1gLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBNYXAgdGhlIEh1YiBwcm92aWRlciBJRCB0byByY2xvbmUgYmFja2VuZCBuYW1lXG4gICAgICAgICAgY29uc3QgYmFja3VwUHJvdmlkZXJNYXA6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7IGdvb2dsZTogJ2RyaXZlJywgZHJvcGJveDogJ2Ryb3Bib3gnIH07XG4gICAgICAgICAgY29uc3QgYmFja3VwUHJvdmlkZXJJZCA9IGJhY2t1cFByb3ZpZGVyTWFwW21hdGNoZWRQcm92aWRlci5pZF0gfHwgbWF0Y2hlZFByb3ZpZGVyLmlkO1xuXG4gICAgICAgICAgLy8gVHJ5IHRvIGVkaXQgYmFja3VwIG5vdGUgdmlhIElQQyAocXVpY2sgbWV0YWRhdGEgb3BlcmF0aW9uKVxuICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGludm9rZUJhY2t1cElQQyhcbiAgICAgICAgICAgICdiYWNrdXBzOmVkaXQtbm90ZScsXG4gICAgICAgICAgICBERUZBVUxUX0lQQ19USU1FT1VULFxuICAgICAgICAgICAgc2l0ZUlkLFxuICAgICAgICAgICAgYmFja3VwUHJvdmlkZXJJZCxcbiAgICAgICAgICAgIHNuYXBzaG90SWQsXG4gICAgICAgICAgICBub3RlXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICAgICAgICAgIC8vIElmIHRoZSBJUEMgY2hhbm5lbCBkb2Vzbid0IGV4aXN0IG9yIGlzbid0IHN1cHBvcnRlZCwgcHJvdmlkZSBoZWxwZnVsIG1lc3NhZ2VcbiAgICAgICAgICAgIGlmIChyZXN1bHQuZXJyb3IubWVzc2FnZT8uaW5jbHVkZXMoJ3RpbWVkIG91dCcpKSB7XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgICAgc25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgICBub3RlOiBudWxsLFxuICAgICAgICAgICAgICAgIGVycm9yOlxuICAgICAgICAgICAgICAgICAgJ0VkaXQgYmFja3VwIG5vdGUgb3BlcmF0aW9uIGlzIG5vdCBhdmFpbGFibGUgdmlhIE1DUC4gUGxlYXNlIGVkaXQgYmFja3VwIG5vdGVzIHRocm91Z2ggdGhlIExvY2FsIFVJLicsXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgc25hcHNob3RJZDogbnVsbCxcbiAgICAgICAgICAgICAgbm90ZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6IHJlc3VsdC5lcnJvci5tZXNzYWdlIHx8ICdFZGl0IG5vdGUgZmFpbGVkJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBzbmFwc2hvdElkLFxuICAgICAgICAgICAgbm90ZSxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBlZGl0IGJhY2t1cCBub3RlOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBzbmFwc2hvdElkOiBudWxsLFxuICAgICAgICAgICAgbm90ZTogbnVsbCxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdVbmtub3duIGVycm9yJyxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICAvLyBQaGFzZSAxMTogV1AgRW5naW5lIENvbm5lY3RcbiAgICAgIHdwZUF1dGhlbnRpY2F0ZTogYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBJbml0aWF0aW5nIFdQIEVuZ2luZSBhdXRoZW50aWNhdGlvbmApO1xuXG4gICAgICAgICAgaWYgKCF3cGVPQXV0aFNlcnZpY2UpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBlbWFpbDogbnVsbCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6ICdXUCBFbmdpbmUgT0F1dGggc2VydmljZSBub3QgYXZhaWxhYmxlJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gVHJpZ2dlciBPQXV0aCBmbG93IC0gdGhpcyB3aWxsIG9wZW4gYnJvd3NlciBmb3IgdXNlciBjb25zZW50XG4gICAgICAgICAgLy8gYXV0aGVudGljYXRlKCkgcmV0dXJucyBPQXV0aFRva2VucyBvbiBzdWNjZXNzXG4gICAgICAgICAgY29uc3QgdG9rZW5zID0gYXdhaXQgd3BlT0F1dGhTZXJ2aWNlLmF1dGhlbnRpY2F0ZSgpO1xuXG4gICAgICAgICAgaWYgKHRva2VucyAmJiB0b2tlbnMuYWNjZXNzVG9rZW4pIHtcbiAgICAgICAgICAgIC8vIFRyeSB0byBnZXQgdXNlciBlbWFpbCBmcm9tIENBUElcbiAgICAgICAgICAgIGxldCBlbWFpbCA9IG51bGw7XG4gICAgICAgICAgICBpZiAoY2FwaVNlcnZpY2UpIHtcbiAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCBjdXJyZW50VXNlciA9IGF3YWl0IGNhcGlTZXJ2aWNlLmdldEN1cnJlbnRVc2VyKCk7XG4gICAgICAgICAgICAgICAgZW1haWwgPSBjdXJyZW50VXNlcj8uZW1haWwgfHwgbnVsbDtcbiAgICAgICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAgICAgLy8gVXNlciBpbmZvIG5vdCBhdmFpbGFibGVcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKFxuICAgICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIFN1Y2Nlc3NmdWxseSBhdXRoZW50aWNhdGVkIHdpdGggV1BFJHtlbWFpbCA/IGAgYXMgJHtlbWFpbH1gIDogJyd9YFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgIGVtYWlsLFxuICAgICAgICAgICAgICBtZXNzYWdlOiAnU3VjY2Vzc2Z1bGx5IGF1dGhlbnRpY2F0ZWQgd2l0aCBXUCBFbmdpbmUnLFxuICAgICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBlbWFpbDogbnVsbCxcbiAgICAgICAgICAgIG1lc3NhZ2U6ICdBdXRoZW50aWNhdGlvbiBpbml0aWF0ZWQuIFBsZWFzZSBjb21wbGV0ZSB0aGUgbG9naW4gaW4geW91ciBicm93c2VyLicsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBXUEUgYXV0aGVudGljYXRpb24gZmFpbGVkOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBlbWFpbDogbnVsbCxcbiAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnQXV0aGVudGljYXRpb24gZmFpbGVkJyxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICB3cGVMb2dvdXQ6IGFzeW5jICgpID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gTG9nZ2luZyBvdXQgZnJvbSBXUCBFbmdpbmVgKTtcblxuICAgICAgICAgIGlmICghd3BlT0F1dGhTZXJ2aWNlKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6ICdXUCBFbmdpbmUgT0F1dGggc2VydmljZSBub3QgYXZhaWxhYmxlJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gY2xlYXJUb2tlbnMoKSBpcyB0aGUgbG9nb3V0IG1ldGhvZFxuICAgICAgICAgIGF3YWl0IHdwZU9BdXRoU2VydmljZS5jbGVhclRva2VucygpO1xuXG4gICAgICAgICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIFN1Y2Nlc3NmdWxseSBsb2dnZWQgb3V0IGZyb20gV1BFYCk7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBtZXNzYWdlOiAnTG9nZ2VkIG91dCBmcm9tIFdQIEVuZ2luZScsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgbG9jYWxMb2dnZXIuZXJyb3IoYFske0FERE9OX05BTUV9XSBXUEUgbG9nb3V0IGZhaWxlZDpgLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdMb2dvdXQgZmFpbGVkJyxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICAvLyBQaGFzZSAxMWM6IFB1c2ggdG8gV1AgRW5naW5lXG4gICAgICBwdXNoVG9XcGU6IGFzeW5jIChcbiAgICAgICAgX3BhcmVudDogYW55LFxuICAgICAgICBhcmdzOiB7XG4gICAgICAgICAgbG9jYWxTaXRlSWQ6IHN0cmluZztcbiAgICAgICAgICByZW1vdGVJbnN0YWxsSWQ6IHN0cmluZztcbiAgICAgICAgICBpbmNsdWRlU3FsPzogYm9vbGVhbjtcbiAgICAgICAgICBjb25maXJtPzogYm9vbGVhbjtcbiAgICAgICAgfVxuICAgICAgKSA9PiB7XG4gICAgICAgIGNvbnN0IHsgbG9jYWxTaXRlSWQsIHJlbW90ZUluc3RhbGxJZCwgaW5jbHVkZVNxbCA9IGZhbHNlLCBjb25maXJtID0gZmFsc2UgfSA9IGFyZ3M7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5pbmZvKFxuICAgICAgICAgICAgYFske0FERE9OX05BTUV9XSBQdXNoIHRvIFdQRTogc2l0ZT0ke2xvY2FsU2l0ZUlkfSwgcmVtb3RlPSR7cmVtb3RlSW5zdGFsbElkfSwgaW5jbHVkZVNxbD0ke2luY2x1ZGVTcWx9YFxuICAgICAgICAgICk7XG5cbiAgICAgICAgICAvLyBSZXF1aXJlIGNvbmZpcm1hdGlvbiBmb3IgcHVzaCBvcGVyYXRpb25zXG4gICAgICAgICAgaWYgKCFjb25maXJtKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6XG4gICAgICAgICAgICAgICAgJ1B1c2ggcmVxdWlyZXMgY29uZmlybT10cnVlIHRvIHByZXZlbnQgYWNjaWRlbnRhbCBvdmVyd3JpdGVzLiBTZXQgY29uZmlybT10cnVlIHRvIHByb2NlZWQuJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gVmVyaWZ5IHNpdGUgZXhpc3RzXG4gICAgICAgICAgY29uc3Qgc2l0ZSA9IHNpdGVEYXRhLmdldFNpdGUobG9jYWxTaXRlSWQpO1xuICAgICAgICAgIGlmICghc2l0ZSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7bG9jYWxTaXRlSWR9YCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ2hlY2sgV1BFIGNvbm5lY3Rpb24gZXhpc3RzXG4gICAgICAgICAgY29uc3Qgd3BlQ29ubmVjdGlvbiA9IHNpdGUuaG9zdENvbm5lY3Rpb25zPy5maW5kKChjOiBhbnkpID0+IGMuaG9zdElkID09PSAnd3BlJyk7XG4gICAgICAgICAgaWYgKCF3cGVDb25uZWN0aW9uKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6XG4gICAgICAgICAgICAgICAgJ1NpdGUgaXMgbm90IGxpbmtlZCB0byBXUCBFbmdpbmUuIFVzZSBDb25uZWN0IGluIExvY2FsIHRvIGxpbmsgdGhlIHNpdGUgZmlyc3QuJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ2hlY2sgcHVzaCBzZXJ2aWNlIGF2YWlsYWJpbGl0eVxuICAgICAgICAgIGlmICghd3BlUHVzaFNlcnZpY2UgfHwgdHlwZW9mIHdwZVB1c2hTZXJ2aWNlLnB1c2ggIT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogJ1dQRSBQdXNoIHNlcnZpY2Ugbm90IGF2YWlsYWJsZScsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEdldCBpbnN0YWxsIGRldGFpbHMgZnJvbSBDQVBJIHRvIGdldCByZXF1aXJlZCBwYXJhbWV0ZXJzXG4gICAgICAgICAgbGV0IGluc3RhbGxOYW1lID0gcmVtb3RlSW5zdGFsbElkO1xuICAgICAgICAgIGxldCBwcmltYXJ5RG9tYWluID0gJyc7XG4gICAgICAgICAgbGV0IGluc3RhbGxJZCA9ICcnO1xuXG4gICAgICAgICAgaWYgKGNhcGlTZXJ2aWNlICYmIHR5cGVvZiBjYXBpU2VydmljZS5nZXRJbnN0YWxsTGlzdCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY29uc3QgaW5zdGFsbHMgPSBhd2FpdCBjYXBpU2VydmljZS5nZXRJbnN0YWxsTGlzdCgpO1xuICAgICAgICAgICAgY29uc3QgbWF0Y2hpbmdJbnN0YWxsID0gaW5zdGFsbHM/LmZpbmQoXG4gICAgICAgICAgICAgIChpOiBhbnkpID0+XG4gICAgICAgICAgICAgICAgaS5zaXRlPy5pZCA9PT0gd3BlQ29ubmVjdGlvbi5yZW1vdGVTaXRlSWQgJiZcbiAgICAgICAgICAgICAgICAoIXdwZUNvbm5lY3Rpb24ucmVtb3RlU2l0ZUVudiB8fCBpLmVudmlyb25tZW50ID09PSB3cGVDb25uZWN0aW9uLnJlbW90ZVNpdGVFbnYpXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgaWYgKG1hdGNoaW5nSW5zdGFsbCkge1xuICAgICAgICAgICAgICBpbnN0YWxsTmFtZSA9IG1hdGNoaW5nSW5zdGFsbC5uYW1lO1xuICAgICAgICAgICAgICBwcmltYXJ5RG9tYWluID1cbiAgICAgICAgICAgICAgICBtYXRjaGluZ0luc3RhbGwucHJpbWFyeV9kb21haW4gfHxcbiAgICAgICAgICAgICAgICBtYXRjaGluZ0luc3RhbGwuY25hbWUgfHxcbiAgICAgICAgICAgICAgICBgJHttYXRjaGluZ0luc3RhbGwubmFtZX0ud3BlbmdpbmUuY29tYDtcbiAgICAgICAgICAgICAgaW5zdGFsbElkID0gbWF0Y2hpbmdJbnN0YWxsLmlkO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICghcHJpbWFyeURvbWFpbikge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOlxuICAgICAgICAgICAgICAgICdDb3VsZCBub3QgZGV0ZXJtaW5lIFdQIEVuZ2luZSBpbnN0YWxsIGRldGFpbHMuIFBsZWFzZSBlbnN1cmUgeW91IGFyZSBhdXRoZW50aWNhdGVkLicsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIFN0YXJ0IHRoZSBwdXNoIG9wZXJhdGlvbiAoYXN5bmMgLSByZXR1cm5zIGltbWVkaWF0ZWx5KVxuICAgICAgICAgIHdwZVB1c2hTZXJ2aWNlXG4gICAgICAgICAgICAucHVzaCh7XG4gICAgICAgICAgICAgIGluY2x1ZGVTcWwsXG4gICAgICAgICAgICAgIHdwZW5naW5lSW5zdGFsbE5hbWU6IGluc3RhbGxOYW1lLFxuICAgICAgICAgICAgICB3cGVuZ2luZUluc3RhbGxJZDogaW5zdGFsbElkLFxuICAgICAgICAgICAgICB3cGVuZ2luZVNpdGVJZDogd3BlQ29ubmVjdGlvbi5yZW1vdGVTaXRlSWQsXG4gICAgICAgICAgICAgIHdwZW5naW5lUHJpbWFyeURvbWFpbjogcHJpbWFyeURvbWFpbixcbiAgICAgICAgICAgICAgbG9jYWxTaXRlSWQ6IHNpdGUuaWQsXG4gICAgICAgICAgICAgIGVudmlyb25tZW50OiB3cGVDb25uZWN0aW9uLnJlbW90ZVNpdGVFbnYsXG4gICAgICAgICAgICAgIGlzTWFnaWNTeW5jOiBmYWxzZSxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAuY2F0Y2goKGVycjogYW55KSA9PiB7XG4gICAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gUHVzaCBmYWlsZWQ6YCwgZXJyKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBtZXNzYWdlOiBgUHVzaCBzdGFydGVkIHRvICR7aW5zdGFsbE5hbWV9LiBDaGVjayBMb2NhbCBVSSBmb3IgcHJvZ3Jlc3MuYCxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICBsb2NhbExvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBzdGFydCBwdXNoOmAsIGVycm9yKTtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ0ZhaWxlZCB0byBzdGFydCBwdXNoJyxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9LFxuXG4gICAgICAvLyBQaGFzZSAxMWM6IFB1bGwgZnJvbSBXUCBFbmdpbmVcbiAgICAgIHB1bGxGcm9tV3BlOiBhc3luYyAoXG4gICAgICAgIF9wYXJlbnQ6IGFueSxcbiAgICAgICAgYXJnczoge1xuICAgICAgICAgIGxvY2FsU2l0ZUlkOiBzdHJpbmc7XG4gICAgICAgICAgcmVtb3RlSW5zdGFsbElkOiBzdHJpbmc7XG4gICAgICAgICAgaW5jbHVkZVNxbD86IGJvb2xlYW47XG4gICAgICAgIH1cbiAgICAgICkgPT4ge1xuICAgICAgICBjb25zdCB7IGxvY2FsU2l0ZUlkLCByZW1vdGVJbnN0YWxsSWQsIGluY2x1ZGVTcWwgPSBmYWxzZSB9ID0gYXJncztcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICBgWyR7QURET05fTkFNRX1dIFB1bGwgZnJvbSBXUEU6IHNpdGU9JHtsb2NhbFNpdGVJZH0sIHJlbW90ZT0ke3JlbW90ZUluc3RhbGxJZH0sIGluY2x1ZGVTcWw9JHtpbmNsdWRlU3FsfWBcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgLy8gVmVyaWZ5IHNpdGUgZXhpc3RzXG4gICAgICAgICAgY29uc3Qgc2l0ZSA9IHNpdGVEYXRhLmdldFNpdGUobG9jYWxTaXRlSWQpO1xuICAgICAgICAgIGlmICghc2l0ZSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOiBgU2l0ZSBub3QgZm91bmQ6ICR7bG9jYWxTaXRlSWR9YCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ2hlY2sgV1BFIGNvbm5lY3Rpb24gZXhpc3RzXG4gICAgICAgICAgY29uc3Qgd3BlQ29ubmVjdGlvbiA9IHNpdGUuaG9zdENvbm5lY3Rpb25zPy5maW5kKChjOiBhbnkpID0+IGMuaG9zdElkID09PSAnd3BlJyk7XG4gICAgICAgICAgaWYgKCF3cGVDb25uZWN0aW9uKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogbnVsbCxcbiAgICAgICAgICAgICAgZXJyb3I6XG4gICAgICAgICAgICAgICAgJ1NpdGUgaXMgbm90IGxpbmtlZCB0byBXUCBFbmdpbmUuIFVzZSBDb25uZWN0IGluIExvY2FsIHRvIGxpbmsgdGhlIHNpdGUgZmlyc3QuJyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ2hlY2sgcHVsbCBzZXJ2aWNlIGF2YWlsYWJpbGl0eVxuICAgICAgICAgIGlmICghd3BlUHVsbFNlcnZpY2UgfHwgdHlwZW9mIHdwZVB1bGxTZXJ2aWNlLnB1bGwgIT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICBtZXNzYWdlOiBudWxsLFxuICAgICAgICAgICAgICBlcnJvcjogJ1dQRSBQdWxsIHNlcnZpY2Ugbm90IGF2YWlsYWJsZScsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEdldCBpbnN0YWxsIGRldGFpbHMgZnJvbSBDQVBJXG4gICAgICAgICAgbGV0IGluc3RhbGxOYW1lID0gcmVtb3RlSW5zdGFsbElkO1xuICAgICAgICAgIGxldCBwcmltYXJ5RG9tYWluID0gJyc7XG4gICAgICAgICAgbGV0IGluc3RhbGxJZCA9ICcnO1xuXG4gICAgICAgICAgaWYgKGNhcGlTZXJ2aWNlICYmIHR5cGVvZiBjYXBpU2VydmljZS5nZXRJbnN0YWxsTGlzdCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY29uc3QgaW5zdGFsbHMgPSBhd2FpdCBjYXBpU2VydmljZS5nZXRJbnN0YWxsTGlzdCgpO1xuICAgICAgICAgICAgY29uc3QgbWF0Y2hpbmdJbnN0YWxsID0gaW5zdGFsbHM/LmZpbmQoXG4gICAgICAgICAgICAgIChpOiBhbnkpID0+XG4gICAgICAgICAgICAgICAgaS5zaXRlPy5pZCA9PT0gd3BlQ29ubmVjdGlvbi5yZW1vdGVTaXRlSWQgJiZcbiAgICAgICAgICAgICAgICAoIXdwZUNvbm5lY3Rpb24ucmVtb3RlU2l0ZUVudiB8fCBpLmVudmlyb25tZW50ID09PSB3cGVDb25uZWN0aW9uLnJlbW90ZVNpdGVFbnYpXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgaWYgKG1hdGNoaW5nSW5zdGFsbCkge1xuICAgICAgICAgICAgICBpbnN0YWxsTmFtZSA9IG1hdGNoaW5nSW5zdGFsbC5uYW1lO1xuICAgICAgICAgICAgICBwcmltYXJ5RG9tYWluID1cbiAgICAgICAgICAgICAgICBtYXRjaGluZ0luc3RhbGwucHJpbWFyeV9kb21haW4gfHxcbiAgICAgICAgICAgICAgICBtYXRjaGluZ0luc3RhbGwuY25hbWUgfHxcbiAgICAgICAgICAgICAgICBgJHttYXRjaGluZ0luc3RhbGwubmFtZX0ud3BlbmdpbmUuY29tYDtcbiAgICAgICAgICAgICAgaW5zdGFsbElkID0gbWF0Y2hpbmdJbnN0YWxsLmlkO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICghcHJpbWFyeURvbWFpbikge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yOlxuICAgICAgICAgICAgICAgICdDb3VsZCBub3QgZGV0ZXJtaW5lIFdQIEVuZ2luZSBpbnN0YWxsIGRldGFpbHMuIFBsZWFzZSBlbnN1cmUgeW91IGFyZSBhdXRoZW50aWNhdGVkLicsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIFN0YXJ0IHRoZSBwdWxsIG9wZXJhdGlvbiAoYXN5bmMgLSByZXR1cm5zIGltbWVkaWF0ZWx5KVxuICAgICAgICAgIHdwZVB1bGxTZXJ2aWNlXG4gICAgICAgICAgICAucHVsbCh7XG4gICAgICAgICAgICAgIGluY2x1ZGVTcWwsXG4gICAgICAgICAgICAgIHdwZW5naW5lSW5zdGFsbE5hbWU6IGluc3RhbGxOYW1lLFxuICAgICAgICAgICAgICB3cGVuZ2luZUluc3RhbGxJZDogaW5zdGFsbElkLFxuICAgICAgICAgICAgICB3cGVuZ2luZVNpdGVJZDogd3BlQ29ubmVjdGlvbi5yZW1vdGVTaXRlSWQsXG4gICAgICAgICAgICAgIHdwZW5naW5lUHJpbWFyeURvbWFpbjogcHJpbWFyeURvbWFpbixcbiAgICAgICAgICAgICAgbG9jYWxTaXRlSWQ6IHNpdGUuaWQsXG4gICAgICAgICAgICAgIGVudmlyb25tZW50OiB3cGVDb25uZWN0aW9uLnJlbW90ZVNpdGVFbnYsXG4gICAgICAgICAgICAgIGlzTWFnaWNTeW5jOiBmYWxzZSxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAuY2F0Y2goKGVycjogYW55KSA9PiB7XG4gICAgICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gUHVsbCBmYWlsZWQ6YCwgZXJyKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBtZXNzYWdlOiBgUHVsbCBzdGFydGVkIGZyb20gJHtpbnN0YWxsTmFtZX0uIENoZWNrIExvY2FsIFVJIGZvciBwcm9ncmVzcy5gLFxuICAgICAgICAgICAgZXJyb3I6IG51bGwsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIHN0YXJ0IHB1bGw6YCwgZXJyb3IpO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIG1lc3NhZ2U6IG51bGwsXG4gICAgICAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnRmFpbGVkIHRvIHN0YXJ0IHB1bGwnLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBTdGFydCB0aGUgTUNQIHNlcnZlclxuICovXG5hc3luYyBmdW5jdGlvbiBzdGFydE1jcFNlcnZlcihzZXJ2aWNlczogTG9jYWxTZXJ2aWNlcywgbG9nZ2VyOiBhbnkpOiBQcm9taXNlPHZvaWQ+IHtcbiAgaWYgKG1jcFNlcnZlcikge1xuICAgIGxvZ2dlci53YXJuKGBbJHtBRERPTl9OQU1FfV0gTUNQIHNlcnZlciBhbHJlYWR5IHJ1bm5pbmdgKTtcbiAgICByZXR1cm47XG4gIH1cblxuICB0cnkge1xuICAgIG1jcFNlcnZlciA9IG5ldyBNY3BTZXJ2ZXIoeyBwb3J0OiBNQ1BfU0VSVkVSLkRFRkFVTFRfUE9SVCB9LCBzZXJ2aWNlcywgbG9nZ2VyKTtcblxuICAgIGF3YWl0IG1jcFNlcnZlci5zdGFydCgpO1xuXG4gICAgY29uc3QgaW5mbyA9IG1jcFNlcnZlci5nZXRDb25uZWN0aW9uSW5mbygpO1xuICAgIGxvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gTUNQIHNlcnZlciBzdGFydGVkIG9uIHBvcnQgJHtpbmZvLnBvcnR9YCk7XG4gICAgbG9nZ2VyLmluZm8oXG4gICAgICBgWyR7QURET05fTkFNRX1dIE1DUCBjb25uZWN0aW9uIGluZm8gc2F2ZWQgdG86IH4vTGlicmFyeS9BcHBsaWNhdGlvbiBTdXBwb3J0L0xvY2FsL21jcC1jb25uZWN0aW9uLWluZm8uanNvbmBcbiAgICApO1xuICAgIGxvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gQXZhaWxhYmxlIHRvb2xzOiAke2luZm8udG9vbHMuam9pbignLCAnKX1gKTtcbiAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgIGxvZ2dlci5lcnJvcihgWyR7QURET05fTkFNRX1dIEZhaWxlZCB0byBzdGFydCBNQ1Agc2VydmVyOmAsIGVycm9yKTtcbiAgfVxufVxuXG4vKipcbiAqIFN0b3AgdGhlIE1DUCBzZXJ2ZXJcbiAqL1xuYXN5bmMgZnVuY3Rpb24gc3RvcE1jcFNlcnZlcihsb2dnZXI6IGFueSk6IFByb21pc2U8dm9pZD4ge1xuICBpZiAobWNwU2VydmVyKSB7XG4gICAgYXdhaXQgbWNwU2VydmVyLnN0b3AoKTtcbiAgICBtY3BTZXJ2ZXIgPSBudWxsO1xuICAgIGxvZ2dlci5pbmZvKGBbJHtBRERPTl9OQU1FfV0gTUNQIHNlcnZlciBzdG9wcGVkYCk7XG4gIH1cbn1cblxuLyoqXG4gKiBSZWdpc3RlciBJUEMgaGFuZGxlcnMgZm9yIHJlbmRlcmVyIGNvbW11bmljYXRpb25cbiAqL1xuZnVuY3Rpb24gcmVnaXN0ZXJJcGNIYW5kbGVycyhzZXJ2aWNlczogTG9jYWxTZXJ2aWNlcywgbG9nZ2VyOiBhbnkpOiB2b2lkIHtcbiAgLy8gR2V0IE1DUCBzZXJ2ZXIgc3RhdHVzXG4gIGlwY01haW4uaGFuZGxlKCdtY3A6Z2V0U3RhdHVzJywgYXN5bmMgKCkgPT4ge1xuICAgIGlmICghbWNwU2VydmVyKSB7XG4gICAgICByZXR1cm4geyBydW5uaW5nOiBmYWxzZSwgcG9ydDogMCwgdXB0aW1lOiAwIH07XG4gICAgfVxuICAgIHJldHVybiBtY3BTZXJ2ZXIuZ2V0U3RhdHVzKCk7XG4gIH0pO1xuXG4gIC8vIEdldCBjb25uZWN0aW9uIGluZm9cbiAgaXBjTWFpbi5oYW5kbGUoJ21jcDpnZXRDb25uZWN0aW9uSW5mbycsIGFzeW5jICgpID0+IHtcbiAgICBpZiAoIW1jcFNlcnZlcikge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHJldHVybiBtY3BTZXJ2ZXIuZ2V0Q29ubmVjdGlvbkluZm8oKTtcbiAgfSk7XG5cbiAgLy8gU3RhcnQgTUNQIHNlcnZlclxuICBpcGNNYWluLmhhbmRsZSgnbWNwOnN0YXJ0JywgYXN5bmMgKCkgPT4ge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBzdGFydE1jcFNlcnZlcihzZXJ2aWNlcywgbG9nZ2VyKTtcbiAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IHRydWUgfTtcbiAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICB9XG4gIH0pO1xuXG4gIC8vIFN0b3AgTUNQIHNlcnZlclxuICBpcGNNYWluLmhhbmRsZSgnbWNwOnN0b3AnLCBhc3luYyAoKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHN0b3BNY3BTZXJ2ZXIobG9nZ2VyKTtcbiAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IHRydWUgfTtcbiAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICB9XG4gIH0pO1xuXG4gIC8vIFJlc3RhcnQgTUNQIHNlcnZlclxuICBpcGNNYWluLmhhbmRsZSgnbWNwOnJlc3RhcnQnLCBhc3luYyAoKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHN0b3BNY3BTZXJ2ZXIobG9nZ2VyKTtcbiAgICAgIGF3YWl0IHN0YXJ0TWNwU2VydmVyKHNlcnZpY2VzLCBsb2dnZXIpO1xuICAgICAgcmV0dXJuIHsgc3VjY2VzczogdHJ1ZSB9O1xuICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyb3IubWVzc2FnZSB9O1xuICAgIH1cbiAgfSk7XG5cbiAgLy8gUmVnZW5lcmF0ZSBhdXRoIHRva2VuXG4gIGlwY01haW4uaGFuZGxlKCdtY3A6cmVnZW5lcmF0ZVRva2VuJywgYXN5bmMgKCkgPT4ge1xuICAgIGlmICghbWNwU2VydmVyKSB7XG4gICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6ICdNQ1Agc2VydmVyIG5vdCBydW5uaW5nJyB9O1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgY29uc3QgbmV3VG9rZW4gPSBhd2FpdCBtY3BTZXJ2ZXIucmVnZW5lcmF0ZVRva2VuKCk7XG4gICAgICByZXR1cm4geyBzdWNjZXNzOiB0cnVlLCB0b2tlbjogbmV3VG9rZW4gfTtcbiAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICB9XG4gIH0pO1xuXG4gIGxvZ2dlci5pbmZvKFxuICAgIGBbJHtBRERPTl9OQU1FfV0gUmVnaXN0ZXJlZCBJUEMgaGFuZGxlcnM6IG1jcDpnZXRTdGF0dXMsIG1jcDpnZXRDb25uZWN0aW9uSW5mbywgbWNwOnN0YXJ0LCBtY3A6c3RvcCwgbWNwOnJlc3RhcnQsIG1jcDpyZWdlbmVyYXRlVG9rZW5gXG4gICk7XG59XG5cbi8qKlxuICogTWFpbiBhZGRvbiBpbml0aWFsaXphdGlvbiBmdW5jdGlvblxuICovXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiAoX2NvbnRleHQ6IExvY2FsTWFpbi5BZGRvbk1haW5Db250ZXh0KTogdm9pZCB7XG4gIGNvbnN0IHNlcnZpY2VzID0gTG9jYWxNYWluLmdldFNlcnZpY2VDb250YWluZXIoKS5jcmFkbGUgYXMgYW55O1xuICBjb25zdCB7IGxvY2FsTG9nZ2VyLCBncmFwaHFsIH0gPSBzZXJ2aWNlcztcblxuICB0cnkge1xuICAgIGxvY2FsTG9nZ2VyLmluZm8oYFske0FERE9OX05BTUV9XSBJbml0aWFsaXppbmcuLi5gKTtcblxuICAgIC8vIFJlZ2lzdGVyIEdyYXBoUUwgZXh0ZW5zaW9ucyAoZm9yIGxvY2FsLWNsaSBhbmQgTUNQKVxuICAgIGNvbnN0IHJlc29sdmVycyA9IGNyZWF0ZVJlc29sdmVycyhzZXJ2aWNlcyk7XG4gICAgZ3JhcGhxbC5yZWdpc3RlckdyYXBoUUxTZXJ2aWNlKCdtY3Atc2VydmVyJywgdHlwZURlZnMsIHJlc29sdmVycyk7XG4gICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIFJlZ2lzdGVyZWQgR3JhcGhRTDogMjkgdG9vbHMgKFBoYXNlIDEtMTFiKWApO1xuXG4gICAgLy8gU3RhcnQgTUNQIHNlcnZlciAoZm9yIEFJIHRvb2xzKVxuICAgIGNvbnN0IGxvY2FsU2VydmljZXM6IExvY2FsU2VydmljZXMgPSB7XG4gICAgICBzaXRlRGF0YTogc2VydmljZXMuc2l0ZURhdGEsXG4gICAgICBzaXRlUHJvY2Vzc01hbmFnZXI6IHNlcnZpY2VzLnNpdGVQcm9jZXNzTWFuYWdlcixcbiAgICAgIHdwQ2xpOiBzZXJ2aWNlcy53cENsaSxcbiAgICAgIGRlbGV0ZVNpdGU6IHNlcnZpY2VzLmRlbGV0ZVNpdGUsXG4gICAgICBhZGRTaXRlOiBzZXJ2aWNlcy5hZGRTaXRlLFxuICAgICAgbG9jYWxMb2dnZXI6IHNlcnZpY2VzLmxvY2FsTG9nZ2VyLFxuICAgICAgYWRtaW5lcjogc2VydmljZXMuYWRtaW5lcixcbiAgICAgIHg1MDlDZXJ0OiBzZXJ2aWNlcy54NTA5Q2VydCxcbiAgICAgIHNpdGVQcm92aXNpb25lcjogc2VydmljZXMuc2l0ZVByb3Zpc2lvbmVyLFxuICAgICAgaW1wb3J0U2l0ZTogc2VydmljZXMuaW1wb3J0U2l0ZSxcbiAgICAgIGxpZ2h0bmluZ1NlcnZpY2VzOiBzZXJ2aWNlcy5saWdodG5pbmdTZXJ2aWNlcyxcbiAgICAgIC8vIFBoYXNlIDExOiBXUCBFbmdpbmUgQ29ubmVjdFxuICAgICAgd3BlT0F1dGg6IHNlcnZpY2VzLndwZU9BdXRoLFxuICAgICAgY2FwaTogc2VydmljZXMuY2FwaSxcbiAgICB9O1xuXG4gICAgLy8gTUNQIHNlcnZlciBkaXNhYmxlZCAtIENMSS1vbmx5IG1vZGVcbiAgICAvLyBUbyBlbmFibGUgTUNQIHNlcnZlciBmb3IgQUkgdG9vbCBpbnRlZ3JhdGlvbiwgdW5jb21tZW50IHRoZSBmb2xsb3dpbmc6XG4gICAgLy8gc3RhcnRNY3BTZXJ2ZXIobG9jYWxTZXJ2aWNlcywgbG9jYWxMb2dnZXIpO1xuICAgIC8vIHJlZ2lzdGVySXBjSGFuZGxlcnMobG9jYWxTZXJ2aWNlcywgbG9jYWxMb2dnZXIpO1xuXG4gICAgbG9jYWxMb2dnZXIuaW5mbyhgWyR7QURET05fTkFNRX1dIFN1Y2Nlc3NmdWxseSBpbml0aWFsaXplZCAoQ0xJLW9ubHkgbW9kZSlgKTtcbiAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgIGxvY2FsTG9nZ2VyLmVycm9yKGBbJHtBRERPTl9OQU1FfV0gRmFpbGVkIHRvIGluaXRpYWxpemU6YCwgZXJyb3IpO1xuICB9XG59XG4iXX0=
|