@local-labs-jpollock/local-cli 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon-dist/package.json +1 -1
- package/lib/analytics.d.ts +55 -0
- package/lib/analytics.js +447 -0
- package/lib/index.js +110 -10
- package/package.json +1 -1
package/addon-dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@local-labs-jpollock/local-addon-cli",
|
|
3
3
|
"productName": "Local CLI",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"description": "Command-line interface for Local WordPress development",
|
|
6
6
|
"main": "lib/main/index.js",
|
|
7
7
|
"renderer": "lib/renderer/index.js",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anonymous Usage Analytics - Phase 3 (Signed Authentication)
|
|
3
|
+
*
|
|
4
|
+
* Collects anonymous usage data with user consent and transmits to server.
|
|
5
|
+
* All requests are signed with HMAC-SHA256 for authentication.
|
|
6
|
+
*
|
|
7
|
+
* Privacy: Only tracks command names, success/failure, duration, and system info.
|
|
8
|
+
* Never tracks: arguments, site names, paths, or any PII.
|
|
9
|
+
*
|
|
10
|
+
* Identifiers:
|
|
11
|
+
* - installationId: Random UUID, identifies this CLI installation (not the user)
|
|
12
|
+
* - secretKey: Random 32 bytes, used for HMAC signing (never transmitted after registration)
|
|
13
|
+
* - sessionId: Random UUID per CLI invocation, correlates commands in a session
|
|
14
|
+
*/
|
|
15
|
+
export type ErrorCategory = 'site_not_found' | 'site_not_running' | 'local_not_running' | 'timeout' | 'network_error' | 'validation_error' | 'unknown';
|
|
16
|
+
interface AnalyticsEvent {
|
|
17
|
+
command: string;
|
|
18
|
+
success: boolean;
|
|
19
|
+
duration_ms: number;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
installation_id: string;
|
|
22
|
+
session_id: string;
|
|
23
|
+
cli_version: string;
|
|
24
|
+
os: string;
|
|
25
|
+
node_version: string;
|
|
26
|
+
error_category?: ErrorCategory;
|
|
27
|
+
}
|
|
28
|
+
export declare function getInstallationId(): string;
|
|
29
|
+
export declare function getSecretKey(): string;
|
|
30
|
+
export declare function getSessionId(): string;
|
|
31
|
+
export declare function isAnalyticsEnabled(): boolean;
|
|
32
|
+
export declare function setAnalyticsEnabled(enabled: boolean): void;
|
|
33
|
+
export declare function hasBeenPrompted(): boolean;
|
|
34
|
+
export declare function showOptInPrompt(): Promise<boolean>;
|
|
35
|
+
export declare function recordEvent(event: AnalyticsEvent): void;
|
|
36
|
+
export declare function readEvents(): AnalyticsEvent[];
|
|
37
|
+
export declare function clearEvents(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Reset analytics: clear events and regenerate installationId + secretKey
|
|
40
|
+
*/
|
|
41
|
+
export declare function resetAnalytics(): void;
|
|
42
|
+
export declare function startTracking(commandName: string): void;
|
|
43
|
+
export declare function finishTracking(success: boolean, errorCategory?: ErrorCategory): void;
|
|
44
|
+
/**
|
|
45
|
+
* Generate a signed dashboard URL for viewing personal analytics
|
|
46
|
+
* URL expires in 1 hour
|
|
47
|
+
*/
|
|
48
|
+
export declare function getDashboardUrl(): string;
|
|
49
|
+
export declare function getStatus(): {
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
eventCount: number;
|
|
52
|
+
installationId: string;
|
|
53
|
+
};
|
|
54
|
+
export declare function getSummary(): string;
|
|
55
|
+
export {};
|
package/lib/analytics.js
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Anonymous Usage Analytics - Phase 3 (Signed Authentication)
|
|
4
|
+
*
|
|
5
|
+
* Collects anonymous usage data with user consent and transmits to server.
|
|
6
|
+
* All requests are signed with HMAC-SHA256 for authentication.
|
|
7
|
+
*
|
|
8
|
+
* Privacy: Only tracks command names, success/failure, duration, and system info.
|
|
9
|
+
* Never tracks: arguments, site names, paths, or any PII.
|
|
10
|
+
*
|
|
11
|
+
* Identifiers:
|
|
12
|
+
* - installationId: Random UUID, identifies this CLI installation (not the user)
|
|
13
|
+
* - secretKey: Random 32 bytes, used for HMAC signing (never transmitted after registration)
|
|
14
|
+
* - sessionId: Random UUID per CLI invocation, correlates commands in a session
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.getInstallationId = getInstallationId;
|
|
51
|
+
exports.getSecretKey = getSecretKey;
|
|
52
|
+
exports.getSessionId = getSessionId;
|
|
53
|
+
exports.isAnalyticsEnabled = isAnalyticsEnabled;
|
|
54
|
+
exports.setAnalyticsEnabled = setAnalyticsEnabled;
|
|
55
|
+
exports.hasBeenPrompted = hasBeenPrompted;
|
|
56
|
+
exports.showOptInPrompt = showOptInPrompt;
|
|
57
|
+
exports.recordEvent = recordEvent;
|
|
58
|
+
exports.readEvents = readEvents;
|
|
59
|
+
exports.clearEvents = clearEvents;
|
|
60
|
+
exports.resetAnalytics = resetAnalytics;
|
|
61
|
+
exports.startTracking = startTracking;
|
|
62
|
+
exports.finishTracking = finishTracking;
|
|
63
|
+
exports.getDashboardUrl = getDashboardUrl;
|
|
64
|
+
exports.getStatus = getStatus;
|
|
65
|
+
exports.getSummary = getSummary;
|
|
66
|
+
const fs = __importStar(require("fs"));
|
|
67
|
+
const path = __importStar(require("path"));
|
|
68
|
+
const os = __importStar(require("os"));
|
|
69
|
+
const crypto = __importStar(require("crypto"));
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// Constants
|
|
72
|
+
// ============================================================================
|
|
73
|
+
const MAX_EVENTS = 10000;
|
|
74
|
+
const EXCLUDED_PREFIXES = ['wpe.', 'analytics.'];
|
|
75
|
+
const ANALYTICS_BASE_URL = process.env.LWP_ANALYTICS_ENDPOINT?.replace('/v1/events', '') ||
|
|
76
|
+
'https://lwp-analytics.jeremy7746.workers.dev';
|
|
77
|
+
const ANALYTICS_ENDPOINT = `${ANALYTICS_BASE_URL}/v1/events`;
|
|
78
|
+
const TRANSMISSION_TIMEOUT = 5000; // 5 seconds
|
|
79
|
+
// Session ID generated once per CLI invocation
|
|
80
|
+
const SESSION_ID = crypto.randomUUID();
|
|
81
|
+
// CLI version from package.json
|
|
82
|
+
const CLI_VERSION = require('../package.json').version;
|
|
83
|
+
// Lazy-initialized paths (for testability)
|
|
84
|
+
function getLwpDir() {
|
|
85
|
+
return path.join(os.homedir(), '.lwp');
|
|
86
|
+
}
|
|
87
|
+
function getConfigPath() {
|
|
88
|
+
return path.join(getLwpDir(), 'config.json');
|
|
89
|
+
}
|
|
90
|
+
function getEventsDir() {
|
|
91
|
+
return path.join(getLwpDir(), 'analytics');
|
|
92
|
+
}
|
|
93
|
+
function getEventsPath() {
|
|
94
|
+
return path.join(getEventsDir(), 'events.jsonl');
|
|
95
|
+
}
|
|
96
|
+
const CI_ENV_VARS = [
|
|
97
|
+
'CI',
|
|
98
|
+
'GITHUB_ACTIONS',
|
|
99
|
+
'GITLAB_CI',
|
|
100
|
+
'JENKINS_URL',
|
|
101
|
+
'TRAVIS',
|
|
102
|
+
'CIRCLECI',
|
|
103
|
+
'BUILDKITE',
|
|
104
|
+
];
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// Config Management
|
|
107
|
+
// ============================================================================
|
|
108
|
+
function ensureDir(dirPath) {
|
|
109
|
+
if (!fs.existsSync(dirPath)) {
|
|
110
|
+
fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function generateInstallationId() {
|
|
114
|
+
return crypto.randomUUID();
|
|
115
|
+
}
|
|
116
|
+
function generateSecretKey() {
|
|
117
|
+
return crypto.randomBytes(32).toString('base64');
|
|
118
|
+
}
|
|
119
|
+
function readConfig() {
|
|
120
|
+
try {
|
|
121
|
+
const configPath = getConfigPath();
|
|
122
|
+
if (fs.existsSync(configPath)) {
|
|
123
|
+
const data = fs.readFileSync(configPath, 'utf-8');
|
|
124
|
+
const config = JSON.parse(data);
|
|
125
|
+
// Validate structure
|
|
126
|
+
if (typeof config.analytics?.enabled === 'boolean') {
|
|
127
|
+
let needsWrite = false;
|
|
128
|
+
// Ensure installationId exists (migrate from Phase 1/2)
|
|
129
|
+
if (!config.installationId) {
|
|
130
|
+
config.installationId = generateInstallationId();
|
|
131
|
+
needsWrite = true;
|
|
132
|
+
}
|
|
133
|
+
// Ensure secretKey exists (migrate from Phase 2)
|
|
134
|
+
if (!config.secretKey) {
|
|
135
|
+
config.secretKey = generateSecretKey();
|
|
136
|
+
needsWrite = true;
|
|
137
|
+
}
|
|
138
|
+
if (needsWrite) {
|
|
139
|
+
writeConfig(config);
|
|
140
|
+
}
|
|
141
|
+
return config;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// Corrupted config, will regenerate
|
|
147
|
+
}
|
|
148
|
+
// Default to enabled (opt-out model) with new credentials
|
|
149
|
+
return {
|
|
150
|
+
installationId: generateInstallationId(),
|
|
151
|
+
secretKey: generateSecretKey(),
|
|
152
|
+
analytics: { enabled: true, promptedAt: null },
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function writeConfig(config) {
|
|
156
|
+
const configPath = getConfigPath();
|
|
157
|
+
ensureDir(getLwpDir());
|
|
158
|
+
const tempPath = `${configPath}.${process.pid}.tmp`;
|
|
159
|
+
fs.writeFileSync(tempPath, JSON.stringify(config, null, 2));
|
|
160
|
+
fs.chmodSync(tempPath, 0o600);
|
|
161
|
+
fs.renameSync(tempPath, configPath);
|
|
162
|
+
}
|
|
163
|
+
function getInstallationId() {
|
|
164
|
+
return readConfig().installationId || generateInstallationId();
|
|
165
|
+
}
|
|
166
|
+
function getSecretKey() {
|
|
167
|
+
return readConfig().secretKey || generateSecretKey();
|
|
168
|
+
}
|
|
169
|
+
function getSessionId() {
|
|
170
|
+
return SESSION_ID;
|
|
171
|
+
}
|
|
172
|
+
function isAnalyticsEnabled() {
|
|
173
|
+
const override = process.env.LWP_ANALYTICS;
|
|
174
|
+
if (override === '0')
|
|
175
|
+
return false;
|
|
176
|
+
if (override === '1')
|
|
177
|
+
return true;
|
|
178
|
+
if (CI_ENV_VARS.some((v) => process.env[v]))
|
|
179
|
+
return false;
|
|
180
|
+
return readConfig().analytics.enabled;
|
|
181
|
+
}
|
|
182
|
+
function setAnalyticsEnabled(enabled) {
|
|
183
|
+
const config = readConfig();
|
|
184
|
+
config.analytics.enabled = enabled;
|
|
185
|
+
config.analytics.promptedAt = config.analytics.promptedAt || new Date().toISOString();
|
|
186
|
+
writeConfig(config);
|
|
187
|
+
}
|
|
188
|
+
function hasBeenPrompted() {
|
|
189
|
+
return readConfig().analytics.promptedAt !== null;
|
|
190
|
+
}
|
|
191
|
+
// ============================================================================
|
|
192
|
+
// HMAC Signing
|
|
193
|
+
// ============================================================================
|
|
194
|
+
/**
|
|
195
|
+
* Sign data with HMAC-SHA256
|
|
196
|
+
*/
|
|
197
|
+
function signData(data, secretKey) {
|
|
198
|
+
const key = Buffer.from(secretKey, 'base64');
|
|
199
|
+
return crypto.createHmac('sha256', key).update(data).digest('hex');
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Mark this installation as registered
|
|
203
|
+
*/
|
|
204
|
+
function markAsRegistered() {
|
|
205
|
+
const config = readConfig();
|
|
206
|
+
config.registeredAt = new Date().toISOString();
|
|
207
|
+
writeConfig(config);
|
|
208
|
+
}
|
|
209
|
+
// ============================================================================
|
|
210
|
+
// Opt-In Prompt
|
|
211
|
+
// ============================================================================
|
|
212
|
+
async function showOptInPrompt() {
|
|
213
|
+
// Mark as prompted and keep enabled (opt-out model)
|
|
214
|
+
const config = readConfig();
|
|
215
|
+
config.analytics.promptedAt = new Date().toISOString();
|
|
216
|
+
config.analytics.enabled = true;
|
|
217
|
+
writeConfig(config);
|
|
218
|
+
// In non-interactive mode, silently enable without message
|
|
219
|
+
if (!process.stdin.isTTY) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
// Show informational message about analytics
|
|
223
|
+
console.log('');
|
|
224
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
225
|
+
console.log('Anonymous usage analytics enabled');
|
|
226
|
+
console.log('');
|
|
227
|
+
console.log('We collect anonymous data to improve the CLI.');
|
|
228
|
+
console.log('No personal information, site names, or command arguments are collected.');
|
|
229
|
+
console.log('');
|
|
230
|
+
console.log('To disable: lwp analytics off');
|
|
231
|
+
console.log('Learn more: https://github.com/jpollock/local-addon-cli#analytics');
|
|
232
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
233
|
+
console.log('');
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Event Tracking
|
|
238
|
+
// ============================================================================
|
|
239
|
+
function isCommandExcluded(command) {
|
|
240
|
+
return EXCLUDED_PREFIXES.some((prefix) => command.startsWith(prefix));
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Transmit event to analytics server with HMAC signature (fire-and-forget)
|
|
244
|
+
*/
|
|
245
|
+
async function transmitEvent(event) {
|
|
246
|
+
try {
|
|
247
|
+
const config = readConfig();
|
|
248
|
+
const installationId = config.installationId;
|
|
249
|
+
const secretKey = config.secretKey;
|
|
250
|
+
const isFirstRequest = !config.registeredAt;
|
|
251
|
+
const body = JSON.stringify(event);
|
|
252
|
+
const signature = signData(body, secretKey);
|
|
253
|
+
const headers = {
|
|
254
|
+
'Content-Type': 'application/json',
|
|
255
|
+
'X-Installation-Id': installationId,
|
|
256
|
+
'X-Signature': signature,
|
|
257
|
+
};
|
|
258
|
+
// On first request, send the secret key so server can store it
|
|
259
|
+
if (isFirstRequest) {
|
|
260
|
+
headers['X-Secret-Key'] = secretKey;
|
|
261
|
+
}
|
|
262
|
+
const controller = new AbortController();
|
|
263
|
+
const timeoutId = setTimeout(() => controller.abort(), TRANSMISSION_TIMEOUT);
|
|
264
|
+
const response = await fetch(ANALYTICS_ENDPOINT, {
|
|
265
|
+
method: 'POST',
|
|
266
|
+
headers,
|
|
267
|
+
body,
|
|
268
|
+
signal: controller.signal,
|
|
269
|
+
});
|
|
270
|
+
clearTimeout(timeoutId);
|
|
271
|
+
// If successful and this was first request, mark as registered
|
|
272
|
+
if (response.ok && isFirstRequest) {
|
|
273
|
+
markAsRegistered();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// Silently ignore transmission errors - never block CLI
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function recordEvent(event) {
|
|
281
|
+
try {
|
|
282
|
+
if (!isAnalyticsEnabled())
|
|
283
|
+
return;
|
|
284
|
+
if (isCommandExcluded(event.command))
|
|
285
|
+
return;
|
|
286
|
+
const eventsDir = getEventsDir();
|
|
287
|
+
const eventsPath = getEventsPath();
|
|
288
|
+
ensureDir(eventsDir);
|
|
289
|
+
// Check event count and rotate if needed
|
|
290
|
+
if (fs.existsSync(eventsPath)) {
|
|
291
|
+
const content = fs.readFileSync(eventsPath, 'utf-8');
|
|
292
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
293
|
+
if (lines.length >= MAX_EVENTS) {
|
|
294
|
+
// Keep newest 80%
|
|
295
|
+
const keepCount = Math.floor(MAX_EVENTS * 0.8);
|
|
296
|
+
const toKeep = lines.slice(-keepCount);
|
|
297
|
+
fs.writeFileSync(eventsPath, toKeep.join('\n') + '\n');
|
|
298
|
+
fs.chmodSync(eventsPath, 0o600);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Append new event to local storage
|
|
302
|
+
const line = JSON.stringify(event) + '\n';
|
|
303
|
+
fs.appendFileSync(eventsPath, line);
|
|
304
|
+
// Ensure permissions on first write
|
|
305
|
+
fs.chmodSync(eventsPath, 0o600);
|
|
306
|
+
// Transmit to server (fire-and-forget, don't await)
|
|
307
|
+
transmitEvent(event).catch(() => {
|
|
308
|
+
// Ignore transmission errors
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
// Never let analytics errors affect command execution
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function readEvents() {
|
|
316
|
+
try {
|
|
317
|
+
const eventsPath = getEventsPath();
|
|
318
|
+
if (!fs.existsSync(eventsPath))
|
|
319
|
+
return [];
|
|
320
|
+
const content = fs.readFileSync(eventsPath, 'utf-8');
|
|
321
|
+
return content
|
|
322
|
+
.trim()
|
|
323
|
+
.split('\n')
|
|
324
|
+
.filter(Boolean)
|
|
325
|
+
.map((line) => JSON.parse(line));
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function clearEvents() {
|
|
332
|
+
try {
|
|
333
|
+
const eventsPath = getEventsPath();
|
|
334
|
+
if (fs.existsSync(eventsPath)) {
|
|
335
|
+
fs.unlinkSync(eventsPath);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
// Ignore cleanup errors
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Reset analytics: clear events and regenerate installationId + secretKey
|
|
344
|
+
*/
|
|
345
|
+
function resetAnalytics() {
|
|
346
|
+
clearEvents();
|
|
347
|
+
const config = readConfig();
|
|
348
|
+
config.installationId = generateInstallationId();
|
|
349
|
+
config.secretKey = generateSecretKey();
|
|
350
|
+
delete config.registeredAt; // Will need to re-register
|
|
351
|
+
config.analytics.enabled = false;
|
|
352
|
+
writeConfig(config);
|
|
353
|
+
}
|
|
354
|
+
// ============================================================================
|
|
355
|
+
// Command Tracking (for Commander hooks)
|
|
356
|
+
// ============================================================================
|
|
357
|
+
let commandStartTime = null;
|
|
358
|
+
let currentCommandName = null;
|
|
359
|
+
function startTracking(commandName) {
|
|
360
|
+
commandStartTime = Date.now();
|
|
361
|
+
currentCommandName = commandName;
|
|
362
|
+
}
|
|
363
|
+
function finishTracking(success, errorCategory) {
|
|
364
|
+
if (commandStartTime === null || currentCommandName === null)
|
|
365
|
+
return;
|
|
366
|
+
const duration = Date.now() - commandStartTime;
|
|
367
|
+
const event = {
|
|
368
|
+
command: currentCommandName,
|
|
369
|
+
success,
|
|
370
|
+
duration_ms: duration,
|
|
371
|
+
timestamp: new Date().toISOString(),
|
|
372
|
+
installation_id: getInstallationId(),
|
|
373
|
+
session_id: SESSION_ID,
|
|
374
|
+
cli_version: CLI_VERSION,
|
|
375
|
+
os: os.platform(),
|
|
376
|
+
node_version: process.version,
|
|
377
|
+
};
|
|
378
|
+
// Add error category for failures
|
|
379
|
+
if (!success && errorCategory) {
|
|
380
|
+
event.error_category = errorCategory;
|
|
381
|
+
}
|
|
382
|
+
recordEvent(event);
|
|
383
|
+
commandStartTime = null;
|
|
384
|
+
currentCommandName = null;
|
|
385
|
+
}
|
|
386
|
+
// ============================================================================
|
|
387
|
+
// Dashboard URL Generation
|
|
388
|
+
// ============================================================================
|
|
389
|
+
/**
|
|
390
|
+
* Generate a signed dashboard URL for viewing personal analytics
|
|
391
|
+
* URL expires in 1 hour
|
|
392
|
+
*/
|
|
393
|
+
function getDashboardUrl() {
|
|
394
|
+
const config = readConfig();
|
|
395
|
+
const installationId = config.installationId;
|
|
396
|
+
const secretKey = config.secretKey;
|
|
397
|
+
// Expire in 1 hour
|
|
398
|
+
const expiration = Math.floor(Date.now() / 1000) + 3600;
|
|
399
|
+
// Sign: installationId:expiration
|
|
400
|
+
const payload = `${installationId}:${expiration}`;
|
|
401
|
+
const signature = signData(payload, secretKey);
|
|
402
|
+
return `${ANALYTICS_BASE_URL}/dashboard/${installationId}?exp=${expiration}&sig=${signature}`;
|
|
403
|
+
}
|
|
404
|
+
// ============================================================================
|
|
405
|
+
// Analytics Summary
|
|
406
|
+
// ============================================================================
|
|
407
|
+
function getStatus() {
|
|
408
|
+
return {
|
|
409
|
+
enabled: isAnalyticsEnabled(),
|
|
410
|
+
eventCount: readEvents().length,
|
|
411
|
+
installationId: getInstallationId(),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function getSummary() {
|
|
415
|
+
const events = readEvents();
|
|
416
|
+
if (events.length === 0) {
|
|
417
|
+
return 'No analytics data collected yet.';
|
|
418
|
+
}
|
|
419
|
+
const total = events.length;
|
|
420
|
+
const successful = events.filter((e) => e.success).length;
|
|
421
|
+
const successRate = ((successful / total) * 100).toFixed(1);
|
|
422
|
+
// Count commands
|
|
423
|
+
const commandCounts = {};
|
|
424
|
+
events.forEach((e) => {
|
|
425
|
+
commandCounts[e.command] = (commandCounts[e.command] || 0) + 1;
|
|
426
|
+
});
|
|
427
|
+
// Sort by count
|
|
428
|
+
const topCommands = Object.entries(commandCounts)
|
|
429
|
+
.sort((a, b) => b[1] - a[1])
|
|
430
|
+
.slice(0, 5);
|
|
431
|
+
// Count recent failures (last 7 days)
|
|
432
|
+
const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
433
|
+
const recentFailures = events.filter((e) => !e.success && new Date(e.timestamp).getTime() > weekAgo).length;
|
|
434
|
+
let output = `
|
|
435
|
+
Analytics Summary
|
|
436
|
+
─────────────────
|
|
437
|
+
Total commands: ${total}
|
|
438
|
+
Success rate: ${successRate}%
|
|
439
|
+
|
|
440
|
+
Top commands:`;
|
|
441
|
+
topCommands.forEach(([cmd, count]) => {
|
|
442
|
+
output += `\n ${cmd.padEnd(15)} ${count}`;
|
|
443
|
+
});
|
|
444
|
+
output += `\n\nRecent failures: ${recentFailures} in last 7 days`;
|
|
445
|
+
return output;
|
|
446
|
+
}
|
|
447
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5hbHl0aWNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2FuYWx5dGljcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7R0FhRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUE2SkgsOENBRUM7QUFFRCxvQ0FFQztBQUVELG9DQUVDO0FBRUQsZ0RBTUM7QUFFRCxrREFLQztBQUVELDBDQUVDO0FBMkJELDBDQTBCQztBQXVERCxrQ0FxQ0M7QUFFRCxnQ0FhQztBQUVELGtDQVNDO0FBS0Qsd0NBUUM7QUFTRCxzQ0FHQztBQUVELHdDQXlCQztBQVVELDBDQWFDO0FBTUQsOEJBTUM7QUFFRCxnQ0EwQ0M7QUF0ZUQsdUNBQXlCO0FBQ3pCLDJDQUE2QjtBQUM3Qix1Q0FBeUI7QUFDekIsK0NBQWlDO0FBeUNqQywrRUFBK0U7QUFDL0UsWUFBWTtBQUNaLCtFQUErRTtBQUUvRSxNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUM7QUFDekIsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLE1BQU0sRUFBRSxZQUFZLENBQUMsQ0FBQztBQUNqRCxNQUFNLGtCQUFrQixHQUN0QixPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixFQUFFLE9BQU8sQ0FBQyxZQUFZLEVBQUUsRUFBRSxDQUFDO0lBQzdELDhDQUE4QyxDQUFDO0FBQ2pELE1BQU0sa0JBQWtCLEdBQUcsR0FBRyxrQkFBa0IsWUFBWSxDQUFDO0FBQzdELE1BQU0sb0JBQW9CLEdBQUcsSUFBSSxDQUFDLENBQUMsWUFBWTtBQUUvQywrQ0FBK0M7QUFDL0MsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDO0FBRXZDLGdDQUFnQztBQUNoQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxPQUFPLENBQUM7QUFFdkQsMkNBQTJDO0FBQzNDLFNBQVMsU0FBUztJQUNoQixPQUFPLElBQUksQ0FBQyxJQUFJLENB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QyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztJQUNyRCxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUNsQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxJQUFJLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxPQUFPLEVBQUUsR0FBRyxPQUFPLENBQy9ELENBQUMsTUFBTSxDQUFDO0lBRVQsSUFBSSxNQUFNLEdBQUc7OzttQkFHSSxLQUFLO21CQUNMLFdBQVc7O2NBRWhCLENBQUM7SUFFYixXQUFXLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRTtRQUNuQyxNQUFNLElBQUksT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxJQUFJLEtBQUssRUFBRSxDQUFDO0lBQzdDLENBQUMsQ0FBQyxDQUFDO0lBRUgsTUFBTSxJQUFJLHlCQUF5QixjQUFjLGlCQUFpQixDQUFDO0lBRW5FLE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEFub255bW91cyBVc2FnZSBBbmFseXRpY3MgLSBQaGFzZSAzIChTaWduZWQgQXV0aGVudGljYXRpb24pXG4gKlxuICogQ29sbGVjdHMgYW5vbnltb3VzIHVzYWdlIGRhdGEgd2l0aCB1c2VyIGNvbnNlbnQgYW5kIHRyYW5zbWl0cyB0byBzZXJ2ZXIuXG4gKiBBbGwgcmVxdWVzdHMgYXJlIHNpZ25lZCB3aXRoIEhNQUMtU0hBMjU2IGZvciBhdXRoZW50aWNhdGlvbi5cbiAqXG4gKiBQcml2YWN5OiBPbmx5IHRyYWNrcyBjb21tYW5kIG5hbWVzLCBzdWNjZXNzL2ZhaWx1cmUsIGR1cmF0aW9uLCBhbmQgc3lzdGVtIGluZm8uXG4gKiBOZXZlciB0cmFja3M6IGFyZ3VtZW50cywgc2l0ZSBuYW1lcywgcGF0aHMsIG9yIGFueSBQSUkuXG4gKlxuICogSWRlbnRpZmllcnM6XG4gKiAtIGluc3RhbGxhdGlvbklkOiBSYW5kb20gVVVJRCwgaWRlbnRpZmllcyB0aGlzIENMSSBpbnN0YWxsYXRpb24gKG5vdCB0aGUgdXNlcilcbiAqIC0gc2VjcmV0S2V5OiBSYW5kb20gMzIgYnl0ZXMsIHVzZWQgZm9yIEhNQUMgc2lnbmluZyAobmV2ZXIgdHJhbnNtaXR0ZWQgYWZ0ZXIgcmVnaXN0cmF0aW9uKVxuICogLSBzZXNzaW9uSWQ6IFJhbmRvbSBVVUlEIHBlciBDTEkgaW52b2NhdGlvbiwgY29ycmVsYXRlcyBjb21tYW5kcyBpbiBhIHNlc3Npb25cbiAqL1xuXG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0ICogYXMgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFR5cGVzXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmludGVyZmFjZSBBbmFseXRpY3NDb25maWcge1xuICBpbnN0YWxsYXRpb25JZD86IHN0cmluZztcbiAgc2VjcmV0S2V5Pzogc3RyaW5nO1xuICByZWdpc3RlcmVkQXQ/OiBzdHJpbmc7IC8vIFdoZW4gc2VjcmV0S2V5IHdhcyBmaXJzdCBzZW50IHRvIHNlcnZlclxuICBhbmFseXRpY3M6IHtcbiAgICBlbmFibGVkOiBib29sZWFuO1xuICAgIHByb21wdGVkQXQ6IHN0cmluZyB8IG51bGw7XG4gIH07XG59XG5cbmV4cG9ydCB0eXBlIEVycm9yQ2F0ZWdvcnkgPVxuICB8ICdzaXRlX25vdF9mb3VuZCdcbiAgfCAnc2l0ZV9ub3RfcnVubmluZydcbiAgfCAnbG9jYWxfbm90X3J1bm5pbmcnXG4gIHwgJ3RpbWVvdXQnXG4gIHwgJ25ldHdvcmtfZXJyb3InXG4gIHwgJ3ZhbGlkYXRpb25fZXJyb3InXG4gIHwgJ3Vua25vd24nO1xuXG5pbnRlcmZhY2UgQW5hbHl0aWNzRXZlbnQge1xuICAvLyBQaGFzZSAxIGZpZWxkc1xuICBjb21tYW5kOiBzdHJpbmc7XG4gIHN1Y2Nlc3M6IGJvb2xlYW47XG4gIGR1cmF0aW9uX21zOiBudW1iZXI7XG4gIHRpbWVzdGFtcDogc3RyaW5nO1xuXG4gIC8vIFBoYXNlIDIgZmllbGRzXG4gIGluc3RhbGxhdGlvbl9pZDogc3RyaW5nO1xuICBzZXNzaW9uX2lkOiBzdHJpbmc7XG4gIGNsaV92ZXJzaW9uOiBzdHJpbmc7XG4gIG9zOiBzdHJpbmc7XG4gIG5vZGVfdmVyc2lvbjogc3RyaW5nO1xuICBlcnJvcl9jYXRlZ29yeT86IEVycm9yQ2F0ZWdvcnk7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIENvbnN0YW50c1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5jb25zdCBNQVhfRVZFTlRTID0gMTAwMDA7XG5jb25zdCBFWENMVURFRF9QUkVGSVhFUyA9IFsnd3BlLicsICdhbmFseXRpY3MuJ107XG5jb25zdCBBTkFMWVRJQ1NfQkFTRV9VUkwgPVxuICBwcm9jZXNzLmVudi5MV1BfQU5BTFlUSUNTX0VORFBPSU5UPy5yZXBsYWNlKCcvdjEvZXZlbnRzJywgJycpIHx8XG4gICdodHRwczovL2x3cC1hbmFseXRpY3MuamVyZW15Nzc0Ni53b3JrZXJzLmRldic7XG5jb25zdCBBTkFMWVRJQ1NfRU5EUE9JTlQgPSBgJHtBTkFMWVRJQ1NfQkFTRV9VUkx9L3YxL2V2ZW50c2A7XG5jb25zdCBUUkFOU01JU1NJT05fVElNRU9VVCA9IDUwMDA7IC8vIDUgc2Vjb25kc1xuXG4vLyBTZXNzaW9uIElEIGdlbmVyYXRlZCBvbmNlIHBlciBDTEkgaW52b2NhdGlvblxuY29uc3QgU0VTU0lPTl9JRCA9IGNyeXB0by5yYW5kb21VVUlEKCk7XG5cbi8vIENMSSB2ZXJzaW9uIGZyb20gcGFja2FnZS5qc29uXG5jb25zdCBDTElfVkVSU0lPTiA9IHJlcXVpcmUoJy4uL3BhY2thZ2UuanNvbicpLnZlcnNpb247XG5cbi8vIExhenktaW5pdGlhbGl6ZWQgcGF0aHMgKGZvciB0ZXN0YWJpbGl0eSlcbmZ1bmN0aW9uIGdldEx3cERpcigpOiBzdHJpbmcge1xuICByZXR1cm4gcGF0aC5qb2luKG9zLmhvbWVkaXIoKSwgJy5sd3AnKTtcbn1cblxuZnVuY3Rpb24gZ2V0Q29uZmlnUGF0aCgpOiBzdHJpbmcge1xuICByZXR1cm4gcGF0aC5qb2luKGdldEx3cERpcigpLCAnY29uZmlnLmpzb24nKTtcbn1cblxuZnVuY3Rpb24gZ2V0RXZlbnRzRGlyKCk6IHN0cmluZyB7XG4gIHJldHVybiBwYXRoLmpvaW4oZ2V0THdwRGlyKCksICdhbmFseXRpY3MnKTtcbn1cblxuZnVuY3Rpb24gZ2V0RXZlbnRzUGF0aCgpOiBzdHJpbmcge1xuICByZXR1cm4gcGF0aC5qb2luKGdldEV2ZW50c0RpcigpLCAnZXZlbnRzLmpzb25sJyk7XG59XG5cbmNvbnN0IENJX0VOVl9WQVJTID0gW1xuICAnQ0knLFxuICAnR0lUSFVCX0FDVElPTlMnLFxuICAnR0lUTEFCX0NJJyxcbiAgJ0pFTktJTlNfVVJMJyxcbiAgJ1RSQVZJUycsXG4gICdDSVJDTEVDSScsXG4gICdCVUlMREtJVEUnLFxuXTtcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQ29uZmlnIE1hbmFnZW1lbnRcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZnVuY3Rpb24gZW5zdXJlRGlyKGRpclBhdGg6IHN0cmluZyk6IHZvaWQge1xuICBpZiAoIWZzLmV4aXN0c1N5bmMoZGlyUGF0aCkpIHtcbiAgICBmcy5ta2RpclN5bmMoZGlyUGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIG1vZGU6IDBvNzAwIH0pO1xuICB9XG59XG5cbmZ1bmN0aW9uIGdlbmVyYXRlSW5zdGFsbGF0aW9uSWQoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGNyeXB0by5yYW5kb21VVUlEKCk7XG59XG5cbmZ1bmN0aW9uIGdlbmVyYXRlU2VjcmV0S2V5KCk6IHN0cmluZyB7XG4gIHJldHVybiBjcnlwdG8ucmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdiYXNlNjQnKTtcbn1cblxuZnVuY3Rpb24gcmVhZENvbmZpZygpOiBBbmFseXRpY3NDb25maWcge1xuICB0cnkge1xuICAgIGNvbnN0IGNvbmZpZ1BhdGggPSBnZXRDb25maWdQYXRoKCk7XG4gICAgaWYgKGZzLmV4aXN0c1N5bmMoY29uZmlnUGF0aCkpIHtcbiAgICAgIGNvbnN0IGRhdGEgPSBmcy5yZWFkRmlsZVN5bmMoY29uZmlnUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICBjb25zdCBjb25maWcgPSBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgLy8gVmFsaWRhdGUgc3RydWN0dXJlXG4gICAgICBpZiAodHlwZW9mIGNvbmZpZy5hbmFseXRpY3M/LmVuYWJsZWQgPT09ICdib29sZWFuJykge1xuICAgICAgICBsZXQgbmVlZHNXcml0ZSA9IGZhbHNlO1xuXG4gICAgICAgIC8vIEVuc3VyZSBpbnN0YWxsYXRpb25JZCBleGlzdHMgKG1pZ3JhdGUgZnJvbSBQaGFzZSAxLzIpXG4gICAgICAgIGlmICghY29uZmlnLmluc3RhbGxhdGlvbklkKSB7XG4gICAgICAgICAgY29uZmlnLmluc3RhbGxhdGlvbklkID0gZ2VuZXJhdGVJbnN0YWxsYXRpb25JZCgpO1xuICAgICAgICAgIG5lZWRzV3JpdGUgPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gRW5zdXJlIHNlY3JldEtleSBleGlzdHMgKG1pZ3JhdGUgZnJvbSBQaGFzZSAyKVxuICAgICAgICBpZiAoIWNvbmZpZy5zZWNyZXRLZXkpIHtcbiAgICAgICAgICBjb25maWcuc2VjcmV0S2V5ID0gZ2VuZXJhdGVTZWNyZXRLZXkoKTtcbiAgICAgICAgICBuZWVkc1dyaXRlID0gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChuZWVkc1dyaXRlKSB7XG4gICAgICAgICAgd3JpdGVDb25maWcoY29uZmlnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY29uZmlnO1xuICAgICAgfVxuICAgIH1cbiAgfSBjYXRjaCB7XG4gICAgLy8gQ29ycnVwdGVkIGNvbmZpZywgd2lsbCByZWdlbmVyYXRlXG4gIH1cbiAgLy8gRGVmYXVsdCB0byBlbmFibGVkIChvcHQtb3V0IG1vZGVsKSB3aXRoIG5ldyBjcmVkZW50aWFsc1xuICByZXR1cm4ge1xuICAgIGluc3RhbGxhdGlvbklkOiBnZW5lcmF0ZUluc3RhbGxhdGlvbklkKCksXG4gICAgc2VjcmV0S2V5OiBnZW5lcmF0ZVNlY3JldEtleSgpLFxuICAgIGFuYWx5dGljczogeyBlbmFibGVkOiB0cnVlLCBwcm9tcHRlZEF0OiBudWxsIH0sXG4gIH07XG59XG5cbmZ1bmN0aW9uIHdyaXRlQ29uZmlnKGNvbmZpZzogQW5hbHl0aWNzQ29uZmlnKTogdm9pZCB7XG4gIGNvbnN0IGNvbmZpZ1BhdGggPSBnZXRDb25maWdQYXRoKCk7XG4gIGVuc3VyZURpcihnZXRMd3BEaXIoKSk7XG4gIGNvbnN0IHRlbXBQYXRoID0gYCR7Y29uZmlnUGF0aH0uJHtwcm9jZXNzLnBpZH0udG1wYDtcbiAgZnMud3JpdGVGaWxlU3luYyh0ZW1wUGF0aCwgSlNPTi5zdHJpbmdpZnkoY29uZmlnLCBudWxsLCAyKSk7XG4gIGZzLmNobW9kU3luYyh0ZW1wUGF0aCwgMG82MDApO1xuICBmcy5yZW5hbWVTeW5jKHRlbXBQYXRoLCBjb25maWdQYXRoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEluc3RhbGxhdGlvbklkKCk6IHN0cmluZyB7XG4gIHJldHVybiByZWFkQ29uZmlnKCkuaW5zdGFsbGF0aW9uSWQgfHwgZ2VuZXJhdGVJbnN0YWxsYXRpb25JZCgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VjcmV0S2V5KCk6IHN0cmluZyB7XG4gIHJldHVybiByZWFkQ29uZmlnKCkuc2VjcmV0S2V5IHx8IGdlbmVyYXRlU2VjcmV0S2V5KCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTZXNzaW9uSWQoKTogc3RyaW5nIHtcbiAgcmV0dXJuIFNFU1NJT05fSUQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc0FuYWx5dGljc0VuYWJsZWQoKTogYm9vbGVhbiB7XG4gIGNvbnN0IG92ZXJyaWRlID0gcHJvY2Vzcy5lbnYuTFdQX0FOQUxZVElDUztcbiAgaWYgKG92ZXJyaWRlID09PSAnMCcpIHJldHVybiBmYWxzZTtcbiAgaWYgKG92ZXJyaWRlID09PSAnMScpIHJldHVybiB0cnVlO1xuICBpZiAoQ0lfRU5WX1ZBUlMuc29tZSgodikgPT4gcHJvY2Vzcy5lbnZbdl0pKSByZXR1cm4gZmFsc2U7XG4gIHJldHVybiByZWFkQ29uZmlnKCkuYW5hbHl0aWNzLmVuYWJsZWQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzZXRBbmFseXRpY3NFbmFibGVkKGVuYWJsZWQ6IGJvb2xlYW4pOiB2b2lkIHtcbiAgY29uc3QgY29uZmlnID0gcmVhZENvbmZpZygpO1xuICBjb25maWcuYW5hbHl0aWNzLmVuYWJsZWQgPSBlbmFibGVkO1xuICBjb25maWcuYW5hbHl0aWNzLnByb21wdGVkQXQgPSBjb25maWcuYW5hbHl0aWNzLnByb21wdGVkQXQgfHwgbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuICB3cml0ZUNvbmZpZyhjb25maWcpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaGFzQmVlblByb21wdGVkKCk6IGJvb2xlYW4ge1xuICByZXR1cm4gcmVhZENvbmZpZygpLmFuYWx5dGljcy5wcm9tcHRlZEF0ICE9PSBudWxsO1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBITUFDIFNpZ25pbmdcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBTaWduIGRhdGEgd2l0aCBITUFDLVNIQTI1NlxuICovXG5mdW5jdGlvbiBzaWduRGF0YShkYXRhOiBzdHJpbmcsIHNlY3JldEtleTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3Qga2V5ID0gQnVmZmVyLmZyb20oc2VjcmV0S2V5LCAnYmFzZTY0Jyk7XG4gIHJldHVybiBjcnlwdG8uY3JlYXRlSG1hYygnc2hhMjU2Jywga2V5KS51cGRhdGUoZGF0YSkuZGlnZXN0KCdoZXgnKTtcbn1cblxuLyoqXG4gKiBNYXJrIHRoaXMgaW5zdGFsbGF0aW9uIGFzIHJlZ2lzdGVyZWRcbiAqL1xuZnVuY3Rpb24gbWFya0FzUmVnaXN0ZXJlZCgpOiB2b2lkIHtcbiAgY29uc3QgY29uZmlnID0gcmVhZENvbmZpZygpO1xuICBjb25maWcucmVnaXN0ZXJlZEF0ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuICB3cml0ZUNvbmZpZyhjb25maWcpO1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBPcHQtSW4gUHJvbXB0XG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaG93T3B0SW5Qcm9tcHQoKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIC8vIE1hcmsgYXMgcHJvbXB0ZWQgYW5kIGtlZXAgZW5hYmxlZCAob3B0LW91dCBtb2RlbClcbiAgY29uc3QgY29uZmlnID0gcmVhZENvbmZpZygpO1xuICBjb25maWcuYW5hbHl0aWNzLnByb21wdGVkQXQgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG4gIGNvbmZpZy5hbmFseXRpY3MuZW5hYmxlZCA9IHRydWU7XG4gIHdyaXRlQ29uZmlnKGNvbmZpZyk7XG5cbiAgLy8gSW4gbm9uLWludGVyYWN0aXZlIG1vZGUsIHNpbGVudGx5IGVuYWJsZSB3aXRob3V0IG1lc3NhZ2VcbiAgaWYgKCFwcm9jZXNzLnN0ZGluLmlzVFRZKSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvLyBTaG93IGluZm9ybWF0aW9uYWwgbWVzc2FnZSBhYm91dCBhbmFseXRpY3NcbiAgY29uc29sZS5sb2coJycpO1xuICBjb25zb2xlLmxvZygn4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSB4pSBJyk7XG4gIGNvbnNvbGUubG9nKCdBbm9ueW1vdXMgdXNhZ2UgYW5hbHl0aWNzIGVuYWJsZWQnKTtcbiAgY29uc29sZS5sb2coJycpO1xuICBjb25zb2xlLmxvZygnV2UgY29sbGVjdCBhbm9ueW1vdXMgZGF0YSB0byBpbXByb3ZlIHRoZSBDTEkuJyk7XG4gIGNvbnNvbGUubG9nKCdObyBwZXJzb25hbCBpbmZvcm1hdGlvbiwgc2l0ZSBuYW1lcywgb3IgY29tbWFuZCBhcmd1bWVudHMgYXJlIGNvbGxlY3RlZC4nKTtcbiAgY29uc29sZS5sb2coJycpO1xuICBjb25zb2xlLmxvZygnVG8gZGlzYWJsZTogbHdwIGFuYWx5dGljcyBvZmYnKTtcbiAgY29uc29sZS5sb2coJ0xlYXJuIG1vcmU6IGh0dHBzOi8vZ2l0aHViLmNvbS9qcG9sbG9jay9sb2NhbC1hZGRvbi1jbGkjYW5hbHl0aWNzJyk7XG4gIGNvbnNvbGUubG9nKCfilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIHilIEnKTtcbiAgY29uc29sZS5sb2coJycpO1xuXG4gIHJldHVybiB0cnVlO1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBFdmVudCBUcmFja2luZ1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5mdW5jdGlvbiBpc0NvbW1hbmRFeGNsdWRlZChjb21tYW5kOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgcmV0dXJuIEVYQ0xVREVEX1BSRUZJWEVTLnNvbWUoKHByZWZpeCkgPT4gY29tbWFuZC5zdGFydHNXaXRoKHByZWZpeCkpO1xufVxuXG4vKipcbiAqIFRyYW5zbWl0IGV2ZW50IHRvIGFuYWx5dGljcyBzZXJ2ZXIgd2l0aCBITUFDIHNpZ25hdHVyZSAoZmlyZS1hbmQtZm9yZ2V0KVxuICovXG5hc3luYyBmdW5jdGlvbiB0cmFuc21pdEV2ZW50KGV2ZW50OiBBbmFseXRpY3NFdmVudCk6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IGNvbmZpZyA9IHJlYWRDb25maWcoKTtcbiAgICBjb25zdCBpbnN0YWxsYXRpb25JZCA9IGNvbmZpZy5pbnN0YWxsYXRpb25JZCE7XG4gICAgY29uc3Qgc2VjcmV0S2V5ID0gY29uZmlnLnNlY3JldEtleSE7XG4gICAgY29uc3QgaXNGaXJzdFJlcXVlc3QgPSAhY29uZmlnLnJlZ2lzdGVyZWRBdDtcblxuICAgIGNvbnN0IGJvZHkgPSBKU09OLnN0cmluZ2lmeShldmVudCk7XG4gICAgY29uc3Qgc2lnbmF0dXJlID0gc2lnbkRhdGEoYm9keSwgc2VjcmV0S2V5KTtcblxuICAgIGNvbnN0IGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgJ1gtSW5zdGFsbGF0aW9uLUlkJzogaW5zdGFsbGF0aW9uSWQsXG4gICAgICAnWC1TaWduYXR1cmUnOiBzaWduYXR1cmUsXG4gICAgfTtcblxuICAgIC8vIE9uIGZpcnN0IHJlcXVlc3QsIHNlbmQgdGhlIHNlY3JldCBrZXkgc28gc2VydmVyIGNhbiBzdG9yZSBpdFxuICAgIGlmIChpc0ZpcnN0UmVxdWVzdCkge1xuICAgICAgaGVhZGVyc1snWC1TZWNyZXQtS2V5J10gPSBzZWNyZXRLZXk7XG4gICAgfVxuXG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICBjb25zdCB0aW1lb3V0SWQgPSBzZXRUaW1lb3V0KCgpID0+IGNvbnRyb2xsZXIuYWJvcnQoKSwgVFJBTlNNSVNTSU9OX1RJTUVPVVQpO1xuXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChBTkFMWVRJQ1NfRU5EUE9JTlQsIHtcbiAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgaGVhZGVycyxcbiAgICAgIGJvZHksXG4gICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pO1xuXG4gICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG5cbiAgICAvLyBJZiBzdWNjZXNzZnVsIGFuZCB0aGlzIHdhcyBmaXJzdCByZXF1ZXN0LCBtYXJrIGFzIHJlZ2lzdGVyZWRcbiAgICBpZiAocmVzcG9uc2Uub2sgJiYgaXNGaXJzdFJlcXVlc3QpIHtcbiAgICAgIG1hcmtBc1JlZ2lzdGVyZWQoKTtcbiAgICB9XG4gIH0gY2F0Y2gge1xuICAgIC8vIFNpbGVudGx5IGlnbm9yZSB0cmFuc21pc3Npb24gZXJyb3JzIC0gbmV2ZXIgYmxvY2sgQ0xJXG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlY29yZEV2ZW50KGV2ZW50OiBBbmFseXRpY3NFdmVudCk6IHZvaWQge1xuICB0cnkge1xuICAgIGlmICghaXNBbmFseXRpY3NFbmFibGVkKCkpIHJldHVybjtcbiAgICBpZiAoaXNDb21tYW5kRXhjbHVkZWQoZXZlbnQuY29tbWFuZCkpIHJldHVybjtcblxuICAgIGNvbnN0IGV2ZW50c0RpciA9IGdldEV2ZW50c0RpcigpO1xuICAgIGNvbnN0IGV2ZW50c1BhdGggPSBnZXRFdmVudHNQYXRoKCk7XG5cbiAgICBlbnN1cmVEaXIoZXZlbnRzRGlyKTtcblxuICAgIC8vIENoZWNrIGV2ZW50IGNvdW50IGFuZCByb3RhdGUgaWYgbmVlZGVkXG4gICAgaWYgKGZzLmV4aXN0c1N5bmMoZXZlbnRzUGF0aCkpIHtcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmMoZXZlbnRzUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICBjb25zdCBsaW5lcyA9IGNvbnRlbnQudHJpbSgpLnNwbGl0KCdcXG4nKS5maWx0ZXIoQm9vbGVhbik7XG4gICAgICBpZiAobGluZXMubGVuZ3RoID49IE1BWF9FVkVOVFMpIHtcbiAgICAgICAgLy8gS2VlcCBuZXdlc3QgODAlXG4gICAgICAgIGNvbnN0IGtlZXBDb3VudCA9IE1hdGguZmxvb3IoTUFYX0VWRU5UUyAqIDAuOCk7XG4gICAgICAgIGNvbnN0IHRvS2VlcCA9IGxpbmVzLnNsaWNlKC1rZWVwQ291bnQpO1xuICAgICAgICBmcy53cml0ZUZpbGVTeW5jKGV2ZW50c1BhdGgsIHRvS2VlcC5qb2luKCdcXG4nKSArICdcXG4nKTtcbiAgICAgICAgZnMuY2htb2RTeW5jKGV2ZW50c1BhdGgsIDBvNjAwKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBcHBlbmQgbmV3IGV2ZW50IHRvIGxvY2FsIHN0b3JhZ2VcbiAgICBjb25zdCBsaW5lID0gSlNPTi5zdHJpbmdpZnkoZXZlbnQpICsgJ1xcbic7XG4gICAgZnMuYXBwZW5kRmlsZVN5bmMoZXZlbnRzUGF0aCwgbGluZSk7XG5cbiAgICAvLyBFbnN1cmUgcGVybWlzc2lvbnMgb24gZmlyc3Qgd3JpdGVcbiAgICBmcy5jaG1vZFN5bmMoZXZlbnRzUGF0aCwgMG82MDApO1xuXG4gICAgLy8gVHJhbnNtaXQgdG8gc2VydmVyIChmaXJlLWFuZC1mb3JnZXQsIGRvbid0IGF3YWl0KVxuICAgIHRyYW5zbWl0RXZlbnQoZXZlbnQpLmNhdGNoKCgpID0+IHtcbiAgICAgIC8vIElnbm9yZSB0cmFuc21pc3Npb24gZXJyb3JzXG4gICAgfSk7XG4gIH0gY2F0Y2gge1xuICAgIC8vIE5ldmVyIGxldCBhbmFseXRpY3MgZXJyb3JzIGFmZmVjdCBjb21tYW5kIGV4ZWN1dGlvblxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZWFkRXZlbnRzKCk6IEFuYWx5dGljc0V2ZW50W10ge1xuICB0cnkge1xuICAgIGNvbnN0IGV2ZW50c1BhdGggPSBnZXRFdmVudHNQYXRoKCk7XG4gICAgaWYgKCFmcy5leGlzdHNTeW5jKGV2ZW50c1BhdGgpKSByZXR1cm4gW107XG4gICAgY29uc3QgY29udGVudCA9IGZzLnJlYWRGaWxlU3luYyhldmVudHNQYXRoLCAndXRmLTgnKTtcbiAgICByZXR1cm4gY29udGVudFxuICAgICAgLnRyaW0oKVxuICAgICAgLnNwbGl0KCdcXG4nKVxuICAgICAgLmZpbHRlcihCb29sZWFuKVxuICAgICAgLm1hcCgobGluZSkgPT4gSlNPTi5wYXJzZShsaW5lKSk7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBbXTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gY2xlYXJFdmVudHMoKTogdm9pZCB7XG4gIHRyeSB7XG4gICAgY29uc3QgZXZlbnRzUGF0aCA9IGdldEV2ZW50c1BhdGgoKTtcbiAgICBpZiAoZnMuZXhpc3RzU3luYyhldmVudHNQYXRoKSkge1xuICAgICAgZnMudW5saW5rU3luYyhldmVudHNQYXRoKTtcbiAgICB9XG4gIH0gY2F0Y2gge1xuICAgIC8vIElnbm9yZSBjbGVhbnVwIGVycm9yc1xuICB9XG59XG5cbi8qKlxuICogUmVzZXQgYW5hbHl0aWNzOiBjbGVhciBldmVudHMgYW5kIHJlZ2VuZXJhdGUgaW5zdGFsbGF0aW9uSWQgKyBzZWNyZXRLZXlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc2V0QW5hbHl0aWNzKCk6IHZvaWQge1xuICBjbGVhckV2ZW50cygpO1xuICBjb25zdCBjb25maWcgPSByZWFkQ29uZmlnKCk7XG4gIGNvbmZpZy5pbnN0YWxsYXRpb25JZCA9IGdlbmVyYXRlSW5zdGFsbGF0aW9uSWQoKTtcbiAgY29uZmlnLnNlY3JldEtleSA9IGdlbmVyYXRlU2VjcmV0S2V5KCk7XG4gIGRlbGV0ZSBjb25maWcucmVnaXN0ZXJlZEF0OyAvLyBXaWxsIG5lZWQgdG8gcmUtcmVnaXN0ZXJcbiAgY29uZmlnLmFuYWx5dGljcy5lbmFibGVkID0gZmFsc2U7XG4gIHdyaXRlQ29uZmlnKGNvbmZpZyk7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIENvbW1hbmQgVHJhY2tpbmcgKGZvciBDb21tYW5kZXIgaG9va3MpXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmxldCBjb21tYW5kU3RhcnRUaW1lOiBudW1iZXIgfCBudWxsID0gbnVsbDtcbmxldCBjdXJyZW50Q29tbWFuZE5hbWU6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG5leHBvcnQgZnVuY3Rpb24gc3RhcnRUcmFja2luZyhjb21tYW5kTmFtZTogc3RyaW5nKTogdm9pZCB7XG4gIGNvbW1hbmRTdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICBjdXJyZW50Q29tbWFuZE5hbWUgPSBjb21tYW5kTmFtZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZpbmlzaFRyYWNraW5nKHN1Y2Nlc3M6IGJvb2xlYW4sIGVycm9yQ2F0ZWdvcnk/OiBFcnJvckNhdGVnb3J5KTogdm9pZCB7XG4gIGlmIChjb21tYW5kU3RhcnRUaW1lID09PSBudWxsIHx8IGN1cnJlbnRDb21tYW5kTmFtZSA9PT0gbnVsbCkgcmV0dXJuO1xuXG4gIGNvbnN0IGR1cmF0aW9uID0gRGF0ZS5ub3coKSAtIGNvbW1hbmRTdGFydFRpbWU7XG4gIGNvbnN0IGV2ZW50OiBBbmFseXRpY3NFdmVudCA9IHtcbiAgICBjb21tYW5kOiBjdXJyZW50Q29tbWFuZE5hbWUsXG4gICAgc3VjY2VzcyxcbiAgICBkdXJhdGlvbl9tczogZHVyYXRpb24sXG4gICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgaW5zdGFsbGF0aW9uX2lkOiBnZXRJbnN0YWxsYXRpb25JZCgpLFxuICAgIHNlc3Npb25faWQ6IFNFU1NJT05fSUQsXG4gICAgY2xpX3ZlcnNpb246IENMSV9WRVJTSU9OLFxuICAgIG9zOiBvcy5wbGF0Zm9ybSgpLFxuICAgIG5vZGVfdmVyc2lvbjogcHJvY2Vzcy52ZXJzaW9uLFxuICB9O1xuXG4gIC8vIEFkZCBlcnJvciBjYXRlZ29yeSBmb3IgZmFpbHVyZXNcbiAgaWYgKCFzdWNjZXNzICYmIGVycm9yQ2F0ZWdvcnkpIHtcbiAgICBldmVudC5lcnJvcl9jYXRlZ29yeSA9IGVycm9yQ2F0ZWdvcnk7XG4gIH1cblxuICByZWNvcmRFdmVudChldmVudCk7XG5cbiAgY29tbWFuZFN0YXJ0VGltZSA9IG51bGw7XG4gIGN1cnJlbnRDb21tYW5kTmFtZSA9IG51bGw7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIERhc2hib2FyZCBVUkwgR2VuZXJhdGlvblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIEdlbmVyYXRlIGEgc2lnbmVkIGRhc2hib2FyZCBVUkwgZm9yIHZpZXdpbmcgcGVyc29uYWwgYW5hbHl0aWNzXG4gKiBVUkwgZXhwaXJlcyBpbiAxIGhvdXJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldERhc2hib2FyZFVybCgpOiBzdHJpbmcge1xuICBjb25zdCBjb25maWcgPSByZWFkQ29uZmlnKCk7XG4gIGNvbnN0IGluc3RhbGxhdGlvbklkID0gY29uZmlnLmluc3RhbGxhdGlvbklkITtcbiAgY29uc3Qgc2VjcmV0S2V5ID0gY29uZmlnLnNlY3JldEtleSE7XG5cbiAgLy8gRXhwaXJlIGluIDEgaG91clxuICBjb25zdCBleHBpcmF0aW9uID0gTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCkgKyAzNjAwO1xuXG4gIC8vIFNpZ246IGluc3RhbGxhdGlvbklkOmV4cGlyYXRpb25cbiAgY29uc3QgcGF5bG9hZCA9IGAke2luc3RhbGxhdGlvbklkfToke2V4cGlyYXRpb259YDtcbiAgY29uc3Qgc2lnbmF0dXJlID0gc2lnbkRhdGEocGF5bG9hZCwgc2VjcmV0S2V5KTtcblxuICByZXR1cm4gYCR7QU5BTFlUSUNTX0JBU0VfVVJMfS9kYXNoYm9hcmQvJHtpbnN0YWxsYXRpb25JZH0/ZXhwPSR7ZXhwaXJhdGlvbn0mc2lnPSR7c2lnbmF0dXJlfWA7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIEFuYWx5dGljcyBTdW1tYXJ5XG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTdGF0dXMoKTogeyBlbmFibGVkOiBib29sZWFuOyBldmVudENvdW50OiBudW1iZXI7IGluc3RhbGxhdGlvbklkOiBzdHJpbmcgfSB7XG4gIHJldHVybiB7XG4gICAgZW5hYmxlZDogaXNBbmFseXRpY3NFbmFibGVkKCksXG4gICAgZXZlbnRDb3VudDogcmVhZEV2ZW50cygpLmxlbmd0aCxcbiAgICBpbnN0YWxsYXRpb25JZDogZ2V0SW5zdGFsbGF0aW9uSWQoKSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFN1bW1hcnkoKTogc3RyaW5nIHtcbiAgY29uc3QgZXZlbnRzID0gcmVhZEV2ZW50cygpO1xuICBpZiAoZXZlbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiAnTm8gYW5hbHl0aWNzIGRhdGEgY29sbGVjdGVkIHlldC4nO1xuICB9XG5cbiAgY29uc3QgdG90YWwgPSBldmVudHMubGVuZ3RoO1xuICBjb25zdCBzdWNjZXNzZnVsID0gZXZlbnRzLmZpbHRlcigoZSkgPT4gZS5zdWNjZXNzKS5sZW5ndGg7XG4gIGNvbnN0IHN1Y2Nlc3NSYXRlID0gKChzdWNjZXNzZnVsIC8gdG90YWwpICogMTAwKS50b0ZpeGVkKDEpO1xuXG4gIC8vIENvdW50IGNvbW1hbmRzXG4gIGNvbnN0IGNvbW1hbmRDb3VudHM6IFJlY29yZDxzdHJpbmcsIG51bWJlcj4gPSB7fTtcbiAgZXZlbnRzLmZvckVhY2goKGUpID0+IHtcbiAgICBjb21tYW5kQ291bnRzW2UuY29tbWFuZF0gPSAoY29tbWFuZENvdW50c1tlLmNvbW1hbmRdIHx8IDApICsgMTtcbiAgfSk7XG5cbiAgLy8gU29ydCBieSBjb3VudFxuICBjb25zdCB0b3BDb21tYW5kcyA9IE9iamVjdC5lbnRyaWVzKGNvbW1hbmRDb3VudHMpXG4gICAgLnNvcnQoKGEsIGIpID0+IGJbMV0gLSBhWzFdKVxuICAgIC5zbGljZSgwLCA1KTtcblxuICAvLyBDb3VudCByZWNlbnQgZmFpbHVyZXMgKGxhc3QgNyBkYXlzKVxuICBjb25zdCB3ZWVrQWdvID0gRGF0ZS5ub3coKSAtIDcgKiAyNCAqIDYwICogNjAgKiAxMDAwO1xuICBjb25zdCByZWNlbnRGYWlsdXJlcyA9IGV2ZW50cy5maWx0ZXIoXG4gICAgKGUpID0+ICFlLnN1Y2Nlc3MgJiYgbmV3IERhdGUoZS50aW1lc3RhbXApLmdldFRpbWUoKSA+IHdlZWtBZ29cbiAgKS5sZW5ndGg7XG5cbiAgbGV0IG91dHB1dCA9IGBcbkFuYWx5dGljcyBTdW1tYXJ5XG7ilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblRvdGFsIGNvbW1hbmRzOiAgJHt0b3RhbH1cblN1Y2Nlc3MgcmF0ZTogICAgJHtzdWNjZXNzUmF0ZX0lXG5cblRvcCBjb21tYW5kczpgO1xuXG4gIHRvcENvbW1hbmRzLmZvckVhY2goKFtjbWQsIGNvdW50XSkgPT4ge1xuICAgIG91dHB1dCArPSBgXFxuICAke2NtZC5wYWRFbmQoMTUpfSAke2NvdW50fWA7XG4gIH0pO1xuXG4gIG91dHB1dCArPSBgXFxuXFxuUmVjZW50IGZhaWx1cmVzOiAgJHtyZWNlbnRGYWlsdXJlc30gaW4gbGFzdCA3IGRheXNgO1xuXG4gIHJldHVybiBvdXRwdXQ7XG59XG4iXX0=
|