@pipeline-builder/pipeline-manager 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/LICENSE +202 -0
- package/README.md +145 -0
- package/cdk.json +91 -0
- package/config.yml +94 -0
- package/dist/agent/ai.d.ts +48 -0
- package/dist/agent/ai.d.ts.map +1 -0
- package/dist/agent/ai.js +149 -0
- package/dist/agent/bootstrap.d.ts +39 -0
- package/dist/agent/bootstrap.d.ts.map +1 -0
- package/dist/agent/bootstrap.js +66 -0
- package/dist/agent/env-file.d.ts +17 -0
- package/dist/agent/env-file.d.ts.map +1 -0
- package/dist/agent/env-file.js +47 -0
- package/dist/agent/executor.d.ts +30 -0
- package/dist/agent/executor.d.ts.map +1 -0
- package/dist/agent/executor.js +60 -0
- package/dist/agent/health.d.ts +43 -0
- package/dist/agent/health.d.ts.map +1 -0
- package/dist/agent/health.js +109 -0
- package/dist/agent/ports.d.ts +26 -0
- package/dist/agent/ports.d.ts.map +1 -0
- package/dist/agent/ports.js +132 -0
- package/dist/agent/post-steps.d.ts +61 -0
- package/dist/agent/post-steps.d.ts.map +1 -0
- package/dist/agent/post-steps.js +131 -0
- package/dist/agent/prereqs.d.ts +26 -0
- package/dist/agent/prereqs.d.ts.map +1 -0
- package/dist/agent/prereqs.js +134 -0
- package/dist/agent/targets.d.ts +128 -0
- package/dist/agent/targets.d.ts.map +1 -0
- package/dist/agent/targets.js +265 -0
- package/dist/agent/tools.d.ts +13 -0
- package/dist/agent/tools.d.ts.map +1 -0
- package/dist/agent/tools.js +75 -0
- package/dist/agent/troubleshoot.d.ts +30 -0
- package/dist/agent/troubleshoot.d.ts.map +1 -0
- package/dist/agent/troubleshoot.js +87 -0
- package/dist/boilerplate.d.ts +3 -0
- package/dist/boilerplate.d.ts.map +1 -0
- package/dist/boilerplate.js +54 -0
- package/dist/cdk.json +91 -0
- package/dist/cli.d.ts +62 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +463 -0
- package/dist/commands/audit-stacks.d.ts +24 -0
- package/dist/commands/audit-stacks.d.ts.map +1 -0
- package/dist/commands/audit-stacks.js +182 -0
- package/dist/commands/audit-tokens.d.ts +21 -0
- package/dist/commands/audit-tokens.d.ts.map +1 -0
- package/dist/commands/audit-tokens.js +153 -0
- package/dist/commands/bootstrap.d.ts +11 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/bootstrap.js +159 -0
- package/dist/commands/create-pat.d.ts +27 -0
- package/dist/commands/create-pat.d.ts.map +1 -0
- package/dist/commands/create-pat.js +194 -0
- package/dist/commands/create-pipeline.d.ts +12 -0
- package/dist/commands/create-pipeline.d.ts.map +1 -0
- package/dist/commands/create-pipeline.js +315 -0
- package/dist/commands/deploy.d.ts +15 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +157 -0
- package/dist/commands/entity-get.d.ts +51 -0
- package/dist/commands/entity-get.d.ts.map +1 -0
- package/dist/commands/entity-get.js +69 -0
- package/dist/commands/entity-list.d.ts +39 -0
- package/dist/commands/entity-list.d.ts.map +1 -0
- package/dist/commands/entity-list.js +88 -0
- package/dist/commands/get-pipeline.d.ts +13 -0
- package/dist/commands/get-pipeline.d.ts.map +1 -0
- package/dist/commands/get-pipeline.js +49 -0
- package/dist/commands/get-plugin.d.ts +13 -0
- package/dist/commands/get-plugin.d.ts.map +1 -0
- package/dist/commands/get-plugin.js +51 -0
- package/dist/commands/instantiate-template.d.ts +20 -0
- package/dist/commands/instantiate-template.d.ts.map +1 -0
- package/dist/commands/instantiate-template.js +274 -0
- package/dist/commands/list-pipelines.d.ts +20 -0
- package/dist/commands/list-pipelines.d.ts.map +1 -0
- package/dist/commands/list-pipelines.js +99 -0
- package/dist/commands/list-plugins.d.ts +20 -0
- package/dist/commands/list-plugins.d.ts.map +1 -0
- package/dist/commands/list-plugins.js +92 -0
- package/dist/commands/login.d.ts +21 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +174 -0
- package/dist/commands/new-plugin.d.ts +12 -0
- package/dist/commands/new-plugin.d.ts.map +1 -0
- package/dist/commands/new-plugin.js +129 -0
- package/dist/commands/org-export.d.ts +25 -0
- package/dist/commands/org-export.d.ts.map +1 -0
- package/dist/commands/org-export.js +100 -0
- package/dist/commands/provision.d.ts +122 -0
- package/dist/commands/provision.d.ts.map +1 -0
- package/dist/commands/provision.js +867 -0
- package/dist/commands/redrive-events.d.ts +24 -0
- package/dist/commands/redrive-events.d.ts.map +1 -0
- package/dist/commands/redrive-events.js +127 -0
- package/dist/commands/register.d.ts +38 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +160 -0
- package/dist/commands/setup-events.d.ts +15 -0
- package/dist/commands/setup-events.d.ts.map +1 -0
- package/dist/commands/setup-events.js +168 -0
- package/dist/commands/status.d.ts +11 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +81 -0
- package/dist/commands/store-token.d.ts +26 -0
- package/dist/commands/store-token.d.ts.map +1 -0
- package/dist/commands/store-token.js +305 -0
- package/dist/commands/synth.d.ts +21 -0
- package/dist/commands/synth.d.ts.map +1 -0
- package/dist/commands/synth.js +158 -0
- package/dist/commands/upload-plugin.d.ts +21 -0
- package/dist/commands/upload-plugin.d.ts.map +1 -0
- package/dist/commands/upload-plugin.js +275 -0
- package/dist/commands/validate-plugin.d.ts +12 -0
- package/dist/commands/validate-plugin.d.ts.map +1 -0
- package/dist/commands/validate-plugin.js +175 -0
- package/dist/commands/validate-templates.d.ts +11 -0
- package/dist/commands/validate-templates.d.ts.map +1 -0
- package/dist/commands/validate-templates.js +116 -0
- package/dist/commands/version.d.ts +12 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +216 -0
- package/dist/config/cli.constants.d.ts +116 -0
- package/dist/config/cli.constants.d.ts.map +1 -0
- package/dist/config/cli.constants.js +194 -0
- package/dist/config.yml +94 -0
- package/dist/lambda/token-renew-handler.d.ts +2 -0
- package/dist/lambda/token-renew-handler.d.ts.map +1 -0
- package/dist/lambda/token-renew-handler.js +125 -0
- package/dist/templates/events-stack.json +174 -0
- package/dist/templates/token-renew-stack.json +122 -0
- package/dist/types/config.d.ts +53 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +4 -0
- package/dist/types/error.d.ts +61 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +35 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/pipeline-template.d.ts +48 -0
- package/dist/types/pipeline-template.d.ts.map +1 -0
- package/dist/types/pipeline-template.js +4 -0
- package/dist/types/pipeline.d.ts +121 -0
- package/dist/types/pipeline.d.ts.map +1 -0
- package/dist/types/pipeline.js +4 -0
- package/dist/types/plugin.d.ts +135 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +4 -0
- package/dist/utils/api-client.d.ts +26 -0
- package/dist/utils/api-client.d.ts.map +1 -0
- package/dist/utils/api-client.js +151 -0
- package/dist/utils/audit-log.d.ts +8 -0
- package/dist/utils/audit-log.d.ts.map +1 -0
- package/dist/utils/audit-log.js +57 -0
- package/dist/utils/auth-guard.d.ts +20 -0
- package/dist/utils/auth-guard.d.ts.map +1 -0
- package/dist/utils/auth-guard.js +22 -0
- package/dist/utils/auth-utils.d.ts +68 -0
- package/dist/utils/auth-utils.d.ts.map +1 -0
- package/dist/utils/auth-utils.js +77 -0
- package/dist/utils/aws-env.d.ts +21 -0
- package/dist/utils/aws-env.d.ts.map +1 -0
- package/dist/utils/aws-env.js +34 -0
- package/dist/utils/aws-secrets.d.ts +48 -0
- package/dist/utils/aws-secrets.d.ts.map +1 -0
- package/dist/utils/aws-secrets.js +115 -0
- package/dist/utils/banner.d.ts +19 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +52 -0
- package/dist/utils/cdk-utils.d.ts +74 -0
- package/dist/utils/cdk-utils.d.ts.map +1 -0
- package/dist/utils/cdk-utils.js +152 -0
- package/dist/utils/command-utils.d.ts +75 -0
- package/dist/utils/command-utils.d.ts.map +1 -0
- package/dist/utils/command-utils.js +155 -0
- package/dist/utils/config-loader.d.ts +33 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +154 -0
- package/dist/utils/cron.d.ts +28 -0
- package/dist/utils/cron.d.ts.map +1 -0
- package/dist/utils/cron.js +119 -0
- package/dist/utils/deploy-runner.d.ts +38 -0
- package/dist/utils/deploy-runner.d.ts.map +1 -0
- package/dist/utils/deploy-runner.js +112 -0
- package/dist/utils/error-handler.d.ts +29 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +302 -0
- package/dist/utils/list-command-utils.d.ts +23 -0
- package/dist/utils/list-command-utils.d.ts.map +1 -0
- package/dist/utils/list-command-utils.js +57 -0
- package/dist/utils/output-utils.d.ts +40 -0
- package/dist/utils/output-utils.d.ts.map +1 -0
- package/dist/utils/output-utils.js +250 -0
- package/dist/utils/pipeline-config.d.ts +33 -0
- package/dist/utils/pipeline-config.d.ts.map +1 -0
- package/dist/utils/pipeline-config.js +55 -0
- package/dist/utils/platform-secret.d.ts +27 -0
- package/dist/utils/platform-secret.d.ts.map +1 -0
- package/dist/utils/platform-secret.js +92 -0
- package/dist/utils/plugin-resolver.d.ts +17 -0
- package/dist/utils/plugin-resolver.d.ts.map +1 -0
- package/dist/utils/plugin-resolver.js +101 -0
- package/dist/utils/preflight.d.ts +26 -0
- package/dist/utils/preflight.d.ts.map +1 -0
- package/dist/utils/preflight.js +59 -0
- package/dist/utils/rate-limiter.d.ts +15 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +164 -0
- package/dist/utils/registry.d.ts +86 -0
- package/dist/utils/registry.d.ts.map +1 -0
- package/dist/utils/registry.js +140 -0
- package/dist/utils/response-utils.d.ts +21 -0
- package/dist/utils/response-utils.d.ts.map +1 -0
- package/dist/utils/response-utils.js +70 -0
- package/dist/utils/tls.d.ts +52 -0
- package/dist/utils/tls.d.ts.map +1 -0
- package/dist/utils/tls.js +88 -0
- package/package.json +181 -0
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { readFileSync, existsSync } from 'fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as readline from 'node:readline/promises';
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { diagnoseFailure, isAiConfigured, parseGoal } from '../agent/ai.js';
|
|
8
|
+
import { bootstrapCommand, resolveBootstrap } from '../agent/bootstrap.js';
|
|
9
|
+
import { createEnvFile, envFileMissing } from '../agent/env-file.js';
|
|
10
|
+
import { entrypointExists, executionBlocked, runScript } from '../agent/executor.js';
|
|
11
|
+
import { deriveHealthUrl, waitHealthy, ensureMinikubeGateway } from '../agent/health.js';
|
|
12
|
+
import { checkHostPorts, discoverHostPorts, stackRunning } from '../agent/ports.js';
|
|
13
|
+
import { resolvePostSteps } from '../agent/post-steps.js';
|
|
14
|
+
import { checkPrereqs, gitAvailable, gitSupportsSparseCheckout, prereqsSatisfied } from '../agent/prereqs.js';
|
|
15
|
+
import { assembleCommand, isTargetId, LOAD_STEPS, sparsePathsFor, TARGETS, TARGET_IDS, teardownCommand, } from '../agent/targets.js';
|
|
16
|
+
import { TOOLS_DIR, fetchTool, isFetchable, withToolsOnPath } from '../agent/tools.js';
|
|
17
|
+
import { matchIssues, sesPostDeployGuidance } from '../agent/troubleshoot.js';
|
|
18
|
+
import { shellQuote } from '../config/cli.constants.js';
|
|
19
|
+
import { printCommandHeader } from '../utils/command-utils.js';
|
|
20
|
+
import { ERROR_CODES, handleError } from '../utils/error-handler.js';
|
|
21
|
+
import { printSection, printKeyValue, printInfo, printWarning, printError, printSuccess } from '../utils/output-utils.js';
|
|
22
|
+
const INIT_MODES = ['auto', 'manual', 'skip'];
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the init mode from the new `--init <mode>` flag, falling back to the DEPRECATED
|
|
25
|
+
* aliases (`--no-init` → skip, `--auto-init` → auto, `--no-auto-init` → manual), then the
|
|
26
|
+
* default (`auto`). Returns `null` when `--init` was given an invalid value (caller errors).
|
|
27
|
+
*/
|
|
28
|
+
export function resolveInitMode(options) {
|
|
29
|
+
if (typeof options.init === 'string') { // --init <mode>
|
|
30
|
+
const m = options.init.toLowerCase();
|
|
31
|
+
return INIT_MODES.includes(m) ? m : null;
|
|
32
|
+
}
|
|
33
|
+
if (options.init === false)
|
|
34
|
+
return 'skip'; // deprecated --no-init
|
|
35
|
+
if (options.autoInit === true)
|
|
36
|
+
return 'auto'; // deprecated --auto-init
|
|
37
|
+
if (options.autoInit === false)
|
|
38
|
+
return 'manual'; // deprecated --no-auto-init
|
|
39
|
+
return 'auto'; // default
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read one line of trimmed input from the terminal, owning the readline
|
|
43
|
+
* lifecycle. Used directly for the destructive teardown gate (a y/N is too easy
|
|
44
|
+
* to fat-finger, so we require the operator to TYPE the target id back), and as
|
|
45
|
+
* the basis for `confirm`. Deliberately has no auto-accept.
|
|
46
|
+
*/
|
|
47
|
+
async function ask(question) {
|
|
48
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
49
|
+
try {
|
|
50
|
+
return (await rl.question(question)).trim();
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
rl.close();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Interactive y/N confirmation; auto-true when `autoYes` (the `--yes` flag). */
|
|
57
|
+
async function confirm(question, autoYes) {
|
|
58
|
+
if (autoYes)
|
|
59
|
+
return true;
|
|
60
|
+
const answer = (await ask(`${question} [y/N] `)).toLowerCase();
|
|
61
|
+
return answer === 'y' || answer === 'yes';
|
|
62
|
+
}
|
|
63
|
+
/** Commander collector: accumulate repeated `--post-step` values in order. */
|
|
64
|
+
function collectStep(value, acc) {
|
|
65
|
+
return [...acc, value];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Host-port pre-flight (local/minikube). Ports are DERIVED from the target's cloned
|
|
69
|
+
* deploy source (compose / setup.sh) so the list can't drift — hence this runs AFTER
|
|
70
|
+
* the clone. A taken port makes a container/forward fail to bind mid-deploy (for the
|
|
71
|
+
* gateway, a silent unreachable hang). Prints a ✓/✗ summary and returns false ONLY
|
|
72
|
+
* when the caller must ABORT (a fatal local conflict). minikube ports are managed
|
|
73
|
+
* forwards (setup.sh pkills/restarts them), so a conflict there warns but proceeds.
|
|
74
|
+
* Remote targets (ec2/eks) bind nothing locally → returns true.
|
|
75
|
+
*/
|
|
76
|
+
export async function preflightPorts(spec, target, cwd) {
|
|
77
|
+
const ports = discoverHostPorts(target, cwd, spec);
|
|
78
|
+
if (ports.length === 0)
|
|
79
|
+
return true;
|
|
80
|
+
const portChecks = await checkHostPorts(ports);
|
|
81
|
+
const taken = portChecks.filter((c) => !c.available);
|
|
82
|
+
printSection('Port availability');
|
|
83
|
+
for (const c of portChecks)
|
|
84
|
+
printInfo(`${c.available ? '✓' : '✗'} ${String(c.port).padEnd(5)} — ${c.service}`);
|
|
85
|
+
if (taken.length === 0)
|
|
86
|
+
return true;
|
|
87
|
+
// local PUBLISHES these ports at `docker compose up` time, so a conflict is fatal.
|
|
88
|
+
// minikube's are kubectl port-forwards setup.sh pkills + restarts (and
|
|
89
|
+
// ensureMinikubeGateway recovers), so a stale forward must NOT block a re-run.
|
|
90
|
+
const fatal = target === 'docker';
|
|
91
|
+
// …UNLESS the local stack is already running: those ports are held by YOUR OWN
|
|
92
|
+
// stack, and `docker compose up` no-ops them — this is exactly how you re-run to
|
|
93
|
+
// add loads/options. Don't block (the would-be conflict is a self-conflict).
|
|
94
|
+
if (fatal && stackRunning(target, cwd, spec)) {
|
|
95
|
+
printWarning(`\n${taken.length} port(s) are held by your already-running ${spec.label} stack — re-running just resumes it (no real conflict), so continuing.`);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
const lead = `${taken.length} required port(s) already in use: ${taken.map((c) => c.port).join(', ')}`;
|
|
99
|
+
(fatal ? printError : printWarning)(fatal
|
|
100
|
+
? `\n${lead} — the deploy can't bind them and would fail mid-way. Free them and re-run:`
|
|
101
|
+
: `\n${lead} — on minikube these are usually a previous run's port-forwards (setup.sh restarts them). Free any NON-minikube holder if the gateway stays unreachable:`);
|
|
102
|
+
for (const c of taken)
|
|
103
|
+
printInfo(` • port ${c.port} (${c.service}) — find the holder: lsof -i :${c.port}`);
|
|
104
|
+
if (taken.some((c) => c.port === 5000)) {
|
|
105
|
+
printInfo(' Note: on macOS, port 5000 is usually AirPlay Receiver — System Settings → General → AirDrop & Handoff → AirPlay Receiver (off).');
|
|
106
|
+
}
|
|
107
|
+
return !fatal;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Teardown mode — the destroy counterpart of provisioning, gated HARDER than deploy:
|
|
111
|
+
* AWS targets are irreversible, so the operator must TYPE the target id (--force
|
|
112
|
+
* bypasses for CI; --yes alone does NOT). Terminal: owns its output + exitCode.
|
|
113
|
+
*/
|
|
114
|
+
export async function runTeardown(spec, target, cwd, executionId, opts) {
|
|
115
|
+
const { command: downCommand, destructive } = teardownCommand(target, {
|
|
116
|
+
stackName: opts.stackName,
|
|
117
|
+
clusterName: opts.clusterName,
|
|
118
|
+
region: opts.region,
|
|
119
|
+
// eks teardown (bin/shutdown.sh) also cleans up the ACM cert + Route 53 alias for
|
|
120
|
+
// the domain, so forward them when present.
|
|
121
|
+
domain: opts.domain,
|
|
122
|
+
hostedZoneId: opts.hostedZoneId,
|
|
123
|
+
// Our typed gate IS the confirmation — assumeYes forwards a native `--yes` to any
|
|
124
|
+
// script-based teardown (ec2 delete-stack has no prompt; eks shutdown.sh skips its own).
|
|
125
|
+
assumeYes: true,
|
|
126
|
+
});
|
|
127
|
+
if (opts.json) {
|
|
128
|
+
console.log(JSON.stringify({ success: true, executionId, target, teardown: true, destructive, destroys: spec.destroys, command: downCommand }, null, 2));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
printSection(`Teardown plan — ${spec.label}`);
|
|
132
|
+
printKeyValue({ 'Target': target, 'Destroys': spec.destroys, 'Stops cost': spec.cost });
|
|
133
|
+
printSection('Command to run');
|
|
134
|
+
printInfo(downCommand);
|
|
135
|
+
if (destructive && !opts.force) {
|
|
136
|
+
// Bind the confirmation to the resource actually destroyed: a custom name (ec2
|
|
137
|
+
// --stack-name / eks --cluster-name) must be typed verbatim so a wrong name can't be
|
|
138
|
+
// confirmed by habit; otherwise the target id.
|
|
139
|
+
const resourceName = target === 'eks' ? opts.clusterName : opts.stackName;
|
|
140
|
+
const confirmToken = resourceName ?? target;
|
|
141
|
+
const noun = target === 'eks' ? (opts.clusterName ? 'cluster name' : 'target id') : (opts.stackName ? 'stack name' : 'target id');
|
|
142
|
+
const token = await ask(`\nThis is IRREVERSIBLE. Type "${confirmToken}" to confirm teardown: `);
|
|
143
|
+
if (token !== confirmToken) {
|
|
144
|
+
printWarning(`That didn't match, so nothing was destroyed — you have to type the exact ${noun} to confirm.`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else if (!destructive && !opts.force && !(await confirm('\nProceed with teardown (stops the stack; on-disk data persists)?', opts.yes ?? false))) {
|
|
149
|
+
printWarning('No problem — nothing was changed.');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
printSection('Tearing down');
|
|
153
|
+
const { code: downCode } = await runScript(downCommand, cwd, { capture: false });
|
|
154
|
+
if (downCode !== 0) {
|
|
155
|
+
printError(`\nTeardown failed (exit ${downCode}). Inspect the stack/containers and retry.`);
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
printSuccess('Teardown complete.');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Run the deploy command (real, secrets unmasked, streamed + captured) with a
|
|
163
|
+
* bounded auto-fix + retry loop — the deploy scripts are idempotent, so a fixed
|
|
164
|
+
* re-run resumes rather than starting over. Returns whether it succeeded plus the
|
|
165
|
+
* (possibly fix-adjusted) params, which the caller needs for SES guidance.
|
|
166
|
+
*/
|
|
167
|
+
export async function runDeployWithRetry(spec, url, cwd, params, aiOpts, opts) {
|
|
168
|
+
// Bound retries so an unattended `--yes` run can't auto-re-run a 15–30 min
|
|
169
|
+
// deploy an unbounded number of times on a persistently-retryable signature.
|
|
170
|
+
const MAX_RETRIES = 5;
|
|
171
|
+
const requestedRetries = Math.max(0, parseInt(opts.retries ?? '', 10) || 0);
|
|
172
|
+
const maxAttempts = 1 + Math.min(requestedRetries, MAX_RETRIES);
|
|
173
|
+
let runParams = params;
|
|
174
|
+
let succeeded = false;
|
|
175
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
176
|
+
const realCommand = assembleCommand(spec, runParams, { mask: false }).command;
|
|
177
|
+
printSection(attempt === 1 ? 'Deploying' : `Deploying — retry ${attempt - 1}`);
|
|
178
|
+
if (attempt === 1) {
|
|
179
|
+
printInfo(`Deploying ${spec.label} → ${url}`);
|
|
180
|
+
printInfo(spec.deploys);
|
|
181
|
+
printInfo('Streaming the deploy below — first run is the slow one (image pulls + cert generation).');
|
|
182
|
+
}
|
|
183
|
+
const { code, tail } = await runScript(realCommand, cwd, { capture: true });
|
|
184
|
+
if (code === 0) {
|
|
185
|
+
succeeded = true;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
printError(`\nDeploy failed (exit ${code}).`);
|
|
189
|
+
const issues = matchIssues(tail);
|
|
190
|
+
if (issues.length > 0) {
|
|
191
|
+
printSection('Likely cause');
|
|
192
|
+
for (const i of issues)
|
|
193
|
+
printInfo(`• ${i.cause}\n → ${i.suggestion}`);
|
|
194
|
+
}
|
|
195
|
+
if (isAiConfigured(aiOpts)) {
|
|
196
|
+
const diagnosis = await diagnoseFailure(redactSecrets(tail, spec, runParams), aiOpts);
|
|
197
|
+
if (diagnosis) {
|
|
198
|
+
printSection('Diagnosis');
|
|
199
|
+
printInfo(diagnosis);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (attempt >= maxAttempts)
|
|
203
|
+
break;
|
|
204
|
+
// Auto-fix: a retryable issue with a param we haven't already applied.
|
|
205
|
+
const fix = issues.find((i) => i.retryable && i.paramFix && runParams[i.paramFix.key] !== i.paramFix.value);
|
|
206
|
+
if (fix?.paramFix && await confirm(`\nApply fix and retry — ${fix.suggestion}`, opts.yes ?? false)) {
|
|
207
|
+
runParams = { ...runParams, [fix.paramFix.key]: fix.paramFix.value };
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
// Retryable but no param change (e.g. ACM DNS propagation) — offer a plain re-run.
|
|
211
|
+
if (!fix && issues.some((i) => i.retryable) && await confirm('\nRetry the deploy (safe to re-run — it resumes where it left off)?', opts.yes ?? false)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
return { succeeded, runParams };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Execute the post-install steps (register → loads → smoke → events → custom). On
|
|
220
|
+
* EC2/EKS the register step + the events bundle (store-token → setup-events) are
|
|
221
|
+
* SURFACED as ordered manual next-steps instead of auto-run: ec2 register must run
|
|
222
|
+
* ON the box (the minikube user owns the cluster), and the events bundle needs a
|
|
223
|
+
* registered platform + AWS creds. (eks register itself port-forwards, so it runs
|
|
224
|
+
* from anywhere with kubectl access.) Sets exitCode on a step failure.
|
|
225
|
+
*/
|
|
226
|
+
export async function runPostSteps(postSteps, skippedSteps, target, cwd, adminEnv, opts) {
|
|
227
|
+
// AWS targets with `--init auto`: the deploy self-runs init-platform, so there's no
|
|
228
|
+
// register/loads step to surface here — confirm it and point at where to watch.
|
|
229
|
+
if (opts.autoInit && target === 'ec2') {
|
|
230
|
+
printInfo('\n✓ Init mode: auto — the instance runs init-platform itself on first boot');
|
|
231
|
+
printInfo(' (register admin + build bootstrap image + load plugins/compliance/samples).');
|
|
232
|
+
printInfo(' It takes ~30-60 min; watch progress:');
|
|
233
|
+
printInfo(' aws ssm start-session --target <InstanceId> # InstanceId stack output');
|
|
234
|
+
printInfo(' sudo tail -f /var/log/user-data.log');
|
|
235
|
+
}
|
|
236
|
+
else if (opts.autoInit && target === 'eks') {
|
|
237
|
+
printInfo('\n✓ Init mode: auto — setup.sh runs init-platform in its final phase');
|
|
238
|
+
printInfo(' (register admin + build bootstrap image + load plugins/compliance/samples,');
|
|
239
|
+
printInfo(' over a kubectl port-forward). Pass --init manual to run it yourself instead.');
|
|
240
|
+
}
|
|
241
|
+
if (postSteps.length === 0)
|
|
242
|
+
return;
|
|
243
|
+
for (const s of skippedSteps)
|
|
244
|
+
printWarning(`Skipped post-step ${s.id}: ${s.reason}`);
|
|
245
|
+
const isRemote = (id) => (target === 'ec2' || target === 'eks') && (id === 'register' || id.startsWith('store-token') || id === 'events');
|
|
246
|
+
const runnable = postSteps.filter((s) => !isRemote(s.id));
|
|
247
|
+
const remote = postSteps.filter((s) => isRemote(s.id));
|
|
248
|
+
if (remote.length > 0) {
|
|
249
|
+
if (target === 'ec2') {
|
|
250
|
+
// EC2 runs the platform as a dedicated `minikube` user inside the instance: the
|
|
251
|
+
// cluster, its kubeconfig, and the jwt-secret all belong to that user, and the
|
|
252
|
+
// register step builds+pushes images against it. So it MUST run on the box as
|
|
253
|
+
// `minikube` — print the exact landing sequence rather than a bare command.
|
|
254
|
+
printInfo('\nNext — finish setup ON the instance (it builds images + reads the cluster, so it');
|
|
255
|
+
printInfo('cannot run from here). Land on the box, become the `minikube` user, then run it:');
|
|
256
|
+
printInfo(' aws ssm start-session --target <InstanceId> # InstanceId stack output');
|
|
257
|
+
printInfo(' sudo -iu minikube # owns the cluster + docker');
|
|
258
|
+
printInfo(' cd /opt/pipeline/pipeline-builder # the deployed checkout');
|
|
259
|
+
for (const s of remote)
|
|
260
|
+
printInfo(` ${s.command}`);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
// eks: init-platform port-forwards to svc/nginx, so register runs from anywhere with
|
|
264
|
+
// kubectl access — no in-VPC requirement and no waiting on the ALB/DNS. (A --with-events
|
|
265
|
+
// bundle, if present, additionally needs AWS creds + a registered platform.)
|
|
266
|
+
printInfo('\nNext — run with kubectl access to the cluster (in this order):');
|
|
267
|
+
for (const s of remote)
|
|
268
|
+
printInfo(` • ${s.label} — ${s.command}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (runnable.length === 0)
|
|
272
|
+
return;
|
|
273
|
+
// When the loads were picked interactively (autoRun), the user already opted in — skip
|
|
274
|
+
// the redundant second confirm. Flag-driven / non-interactive runs still gate here.
|
|
275
|
+
if (opts.autoRun || await confirm(`\nRun ${runnable.length} post-install step(s) now?`, opts.yes ?? false)) {
|
|
276
|
+
if (opts.autoRun)
|
|
277
|
+
printInfo(`\nRunning the ${runnable.length} post-install step(s) you selected…`);
|
|
278
|
+
for (const s of runnable) {
|
|
279
|
+
printSection(`Post-step: ${s.label}`);
|
|
280
|
+
// register (local/minikube) logs into the platform, so it needs the admin creds
|
|
281
|
+
// (PLATFORM_IDENTIFIER / PLATFORM_PASSWORD) on top of its step env.
|
|
282
|
+
const needsCreds = s.id === 'register' || s.id.startsWith('store-token');
|
|
283
|
+
const env = needsCreds ? { ...s.env, ...adminEnv } : s.env;
|
|
284
|
+
// Loads/smoke are noisy + non-interactive → run QUIET (capture, show only on
|
|
285
|
+
// failure). Exception: a register with no admin creds prompts, so it must stream.
|
|
286
|
+
const interactiveRegister = s.id === 'register' && !adminEnv.PLATFORM_IDENTIFIER;
|
|
287
|
+
const quiet = !interactiveRegister;
|
|
288
|
+
if (quiet)
|
|
289
|
+
printInfo(`${s.command}\n …running (output shown only if it fails)`);
|
|
290
|
+
else
|
|
291
|
+
printInfo(s.command);
|
|
292
|
+
const { code, tail } = await runScript(s.command, cwd, { quiet, capture: false, env });
|
|
293
|
+
if (code !== 0) {
|
|
294
|
+
if (quiet && tail) {
|
|
295
|
+
printError('\n--- last output ---');
|
|
296
|
+
printInfo(tail);
|
|
297
|
+
}
|
|
298
|
+
printError(`\nPost-step '${s.id}' failed (exit ${code}). The platform is deployed; fix and re-run the step manually.`);
|
|
299
|
+
process.exitCode = 1;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
printSuccess(`${s.id} ✓`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
printInfo('\nSkipped post-install steps. Run the commands above manually when ready.');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Sparse-clone the platform repo (when --repo, or accepted via the interactive
|
|
311
|
+
* offer), repoint cwd into it, and verify the deploy entrypoint exists. Owns the
|
|
312
|
+
* cwd/bootstrapped state. Sets exitCode on a hard failure (clone error / missing
|
|
313
|
+
* entrypoint), matching the old inline behavior; a plain decline leaves exitCode
|
|
314
|
+
* untouched. Returns ok:false when the caller must abort.
|
|
315
|
+
*/
|
|
316
|
+
export async function bootstrapAndLocate(spec, bootstrap, bootstrapCmd, sparsePaths, startCwd, opts) {
|
|
317
|
+
let cwd = startCwd;
|
|
318
|
+
let bootstrapped = false;
|
|
319
|
+
// Shared by `--repo` and the interactive offer. Repoints cwd + sets bootstrapped;
|
|
320
|
+
// `bootstrapped` records whether we created the clone (vs. an existing checkout) —
|
|
321
|
+
// only then can the caller additively sparse-fetch load folders.
|
|
322
|
+
const bootstrapClone = async () => {
|
|
323
|
+
const cmd = bootstrapCommand(bootstrap);
|
|
324
|
+
printSection('Bootstrap');
|
|
325
|
+
printInfo(cmd);
|
|
326
|
+
const { code } = await runScript(cmd, cwd, { capture: false });
|
|
327
|
+
if (code !== 0) {
|
|
328
|
+
printError(`\nThe clone didn't finish (exit ${code}). Double-check the repo URL / ref (or your network) and give it another go.`);
|
|
329
|
+
process.exitCode = 1;
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
cwd = path.resolve(cwd, bootstrap.workdir);
|
|
333
|
+
bootstrapped = true;
|
|
334
|
+
printSuccess(`Repo is ready — continuing from ${cwd}`);
|
|
335
|
+
const absent = sparsePaths.filter((p) => !existsSync(path.join(cwd, p)));
|
|
336
|
+
if (absent.length > 0) {
|
|
337
|
+
printError(`\nThe sparse clone is missing ${absent.join(', ')} — that ref may not have those folders. Try a different --ref.`);
|
|
338
|
+
process.exitCode = 1;
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
return true;
|
|
342
|
+
};
|
|
343
|
+
// `--repo`: clone up front (must precede the entrypoint check).
|
|
344
|
+
if (bootstrapCmd) {
|
|
345
|
+
printInfo(`\nBootstrap → clone ${bootstrap.repo} @ ${bootstrap.ref} into ${bootstrap.workdir} (sparse).`);
|
|
346
|
+
if (!(await confirm('Go ahead with the clone?', opts.yes ?? false))) {
|
|
347
|
+
printWarning('No problem — nothing was changed. Re-run whenever you\'re ready.');
|
|
348
|
+
return { cwd, bootstrapped, ok: false };
|
|
349
|
+
}
|
|
350
|
+
if (!(await bootstrapClone()))
|
|
351
|
+
return { cwd, bootstrapped, ok: false };
|
|
352
|
+
}
|
|
353
|
+
// The deploy entrypoint must exist relative to cwd. If it doesn't and we haven't
|
|
354
|
+
// bootstrapped, gracefully OFFER to clone (interactive only) so a fresh machine can
|
|
355
|
+
// proceed without re-running with --repo.
|
|
356
|
+
if (!entrypointExists(spec, cwd)) {
|
|
357
|
+
const interactive = Boolean(process.stdin.isTTY) && !opts.yes;
|
|
358
|
+
if (!bootstrapCmd && interactive && gitAvailable()) {
|
|
359
|
+
printInfo(`\nLooks like you're not inside a pipeline-builder checkout, so the ${spec.label} deploy scripts aren't here yet.`);
|
|
360
|
+
if (await confirm(`Want me to sparse-clone them into ${bootstrap.workdir} and keep going?`, false)) {
|
|
361
|
+
if (!(await bootstrapClone()))
|
|
362
|
+
return { cwd, bootstrapped, ok: false };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (!entrypointExists(spec, cwd)) {
|
|
366
|
+
printWarning(`\nI couldn't find ${spec.dir}/${spec.entrypoint} from ${cwd}.`);
|
|
367
|
+
printInfo('Two easy ways forward:');
|
|
368
|
+
printInfo(' • cd into your pipeline-builder checkout and re-run, or');
|
|
369
|
+
printInfo(` • add --repo and I'll bootstrap a fresh sparse clone for you${gitAvailable() ? '.' : ' (once git is installed).'}`);
|
|
370
|
+
process.exitCode = 1;
|
|
371
|
+
return { cwd, bootstrapped, ok: false };
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return { cwd, bootstrapped, ok: true };
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Interactive opt-in loads, offered AFTER the clone (so we only ask once the operator
|
|
378
|
+
* has agreed to proceed). Prompts per load, additively sparse-fetches the picked
|
|
379
|
+
* folders into a bootstrapped sparse clone, then re-resolves the post-install steps
|
|
380
|
+
* with the selections. Returns the chosen ids + the recomputed steps.
|
|
381
|
+
*/
|
|
382
|
+
export async function resolveLoadsInteractively(target, url, region, cwd, bootstrapped, bootstrap, postStepFlags) {
|
|
383
|
+
const prompts = {
|
|
384
|
+
plugins: 'Load plugins?',
|
|
385
|
+
samples: 'Load sample pipeline templates?',
|
|
386
|
+
compliance: 'Load compliance rules?',
|
|
387
|
+
};
|
|
388
|
+
printSection('Optional post-install loads');
|
|
389
|
+
const chosen = [];
|
|
390
|
+
for (const s of LOAD_STEPS) {
|
|
391
|
+
if (await confirm(prompts[s.id] ?? `Load ${s.id}?`, false))
|
|
392
|
+
chosen.push(s.id);
|
|
393
|
+
}
|
|
394
|
+
// Additive sparse re-sync: materialize ONLY the picked loads' folders (the partial
|
|
395
|
+
// clone fetches their blobs on demand). Only when we created a SPARSE clone — a
|
|
396
|
+
// normal checkout already has every folder, and the git<2.27 full-clone fallback
|
|
397
|
+
// (bootstrap.full) is non-sparse, where `sparse-checkout add` would switch it to
|
|
398
|
+
// cone mode and PRUNE the already-materialized deploy folders.
|
|
399
|
+
if (bootstrapped && !bootstrap.full) {
|
|
400
|
+
const absent = sparsePathsFor(target, chosen).filter((p) => !existsSync(path.join(cwd, p)));
|
|
401
|
+
if (absent.length > 0) {
|
|
402
|
+
printSection('Fetching selected load folders');
|
|
403
|
+
const addCmd = `git sparse-checkout add ${absent.map((p) => `'${p}'`).join(' ')} && git checkout ${shellQuote(bootstrap.ref)}`;
|
|
404
|
+
printInfo(addCmd);
|
|
405
|
+
const { code } = await runScript(addCmd, cwd, { capture: false });
|
|
406
|
+
if (code !== 0) {
|
|
407
|
+
printWarning(`Couldn't fetch ${absent.join(', ')} — the matching load step(s) may fail. Re-run with --repo to refresh the clone.`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const resolved = resolvePostSteps({
|
|
412
|
+
target,
|
|
413
|
+
url,
|
|
414
|
+
region,
|
|
415
|
+
enabledLoadIds: chosen,
|
|
416
|
+
...postStepFlags,
|
|
417
|
+
buildBootstrap: postStepFlags.buildBootstrap || chosen.includes('plugins'),
|
|
418
|
+
});
|
|
419
|
+
return { enabledLoadIds: chosen, steps: resolved.steps, skipped: resolved.skipped };
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Redact secret param values (e.g. the ghcr token) from captured deploy output
|
|
423
|
+
* before it is sent to the LLM for diagnosis. The deploy runs with secrets
|
|
424
|
+
* unmasked, so although the current scripts don't echo them, this keeps a future
|
|
425
|
+
* script's stray echo (or a verbose sub-tool) from crossing the third-party-LLM
|
|
426
|
+
* boundary.
|
|
427
|
+
*/
|
|
428
|
+
function redactSecrets(text, spec, params) {
|
|
429
|
+
let out = text;
|
|
430
|
+
for (const input of [...spec.required, ...spec.optional]) {
|
|
431
|
+
if (!input.secret)
|
|
432
|
+
continue;
|
|
433
|
+
const value = params[input.key];
|
|
434
|
+
if (typeof value === 'string' && value.length >= 4)
|
|
435
|
+
out = out.split(value).join('***');
|
|
436
|
+
}
|
|
437
|
+
return out;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Registers the `provision` command — an AI-assisted installer for the Pipeline
|
|
441
|
+
* Builder PLATFORM (local/minikube/EC2/EKS). It prints the assembled plan
|
|
442
|
+
* (prereq checks, NL-goal parsing, command assembly) and then DEPLOYS, gated by
|
|
443
|
+
* confirmation prompts (`--yes` to auto-accept; `--json` is the only non-executing
|
|
444
|
+
* mode, printing the plan for tooling). With `--repo` it can bootstrap a fresh
|
|
445
|
+
* machine by sparsely cloning only the deploy folders the selected target +
|
|
446
|
+
* options need, then runs post-install steps (register admin, opt-in loads,
|
|
447
|
+
* smoke test, custom commands). `--teardown` removes a deployment instead.
|
|
448
|
+
*
|
|
449
|
+
* @param program - The root Commander program instance to attach the command to.
|
|
450
|
+
*/
|
|
451
|
+
export function provision(program) {
|
|
452
|
+
program
|
|
453
|
+
.command('provision')
|
|
454
|
+
.description('AI-assisted installer for the platform (local/minikube/EC2/EKS): deploys it (gated by confirmation prompts), or --teardown to remove')
|
|
455
|
+
.option('-t, --target <target>', `Deploy target: ${TARGET_IDS.join(' | ')}`)
|
|
456
|
+
.option('-p, --prompt <text>', 'Natural-language goal (parsed into params when an AI key is set)')
|
|
457
|
+
.option('--region <region>', 'AWS region (EC2/EKS)')
|
|
458
|
+
.option('--domain <domain>', 'Fully-qualified domain name (EC2/EKS)')
|
|
459
|
+
.option('--hosted-zone-id <id>', 'Public Route 53 hosted zone ID (EC2/EKS)')
|
|
460
|
+
.option('--deploy-mode <mode>', 'public | private (EC2/EKS)')
|
|
461
|
+
.option('--key-pair <name>', 'EC2 key pair (EC2)')
|
|
462
|
+
.option('--instance-type <type>', 'EC2 instance type (EC2)')
|
|
463
|
+
.option('--lean', 'EC2: trim optional observability/admin services so the core stack + mesh fits a t3.xlarge (pair with --instance-type t3.xlarge)')
|
|
464
|
+
.option('--ghcr-token <token>', 'GitHub PAT (read:packages) — masked in output')
|
|
465
|
+
.option('--email', 'Enable SES transactional email (on by default for AWS)')
|
|
466
|
+
.option('--no-email', 'Skip SES (transactional email is provisioned by default on AWS)')
|
|
467
|
+
.option('--email-from <addr>', 'SES From address')
|
|
468
|
+
.option('--email-from-name <name>', 'SES From display name')
|
|
469
|
+
.option('--alert-email <addr>', 'Subscribe an address to the bounce/complaint SNS topic')
|
|
470
|
+
.option('--skip-ses-identity', 'Skip SES identity creation (domain already verified)')
|
|
471
|
+
.option('--ai-provider <provider>', 'AI provider (anthropic|openai|google|xai|bedrock)')
|
|
472
|
+
.option('--model <model>', 'AI model id')
|
|
473
|
+
.option('--diagnose <file>', 'Diagnose a CloudFormation/deploy failure from a file (needs an AI key)')
|
|
474
|
+
.option('-y, --yes', 'Auto-accept all confirmation prompts (for CI / non-interactive)', false)
|
|
475
|
+
.option('--retries <n>', 'Auto-fix + retry attempts after a failure (deploy scripts are idempotent)', '1')
|
|
476
|
+
.option('--teardown', 'Tear down an existing deployment instead of creating one (gated; AWS targets require a typed confirmation)', false)
|
|
477
|
+
.option('--stack-name <name>', 'CloudFormation stack name (EC2) for the deploy AND teardown — defaults to pipeline-builder (set it to run a second environment)')
|
|
478
|
+
.option('--cluster-name <name>', 'EKS cluster name for the deploy AND teardown — defaults to pipeline-builder')
|
|
479
|
+
.option('--force', 'Skip the teardown typed-confirmation (DANGEROUS — for CI/automation only)', false)
|
|
480
|
+
.option('--init <mode>', 'Post-deploy initialization: auto (DEFAULT — register admin + load plugins/compliance/samples; on ec2 the instance does it itself on first boot), manual (don\'t self-init — surface the step for you to run, e.g. to set real admin creds), or skip (do nothing). local/minikube/eks run init via provision unless skip.')
|
|
481
|
+
// Deprecated aliases — kept working for back-compat; --init is the documented form.
|
|
482
|
+
.option('--no-init', '[deprecated] alias for --init skip')
|
|
483
|
+
.option('--auto-init', '[deprecated] alias for --init auto')
|
|
484
|
+
.option('--no-auto-init', '[deprecated] alias for --init manual')
|
|
485
|
+
// Bootstrap (sparse clone) — provision a fresh machine in one command.
|
|
486
|
+
.option('--repo [url]', 'Bootstrap: git-clone the platform repo first (sparse — only the needed deploy folders), then run from it (no value = the upstream default)')
|
|
487
|
+
.option('--ref <ref>', 'Git branch/tag to check out when bootstrapping (default: main)')
|
|
488
|
+
.option('--workdir <dir>', 'Directory to clone into / run from when bootstrapping (default: pipeline-builder)')
|
|
489
|
+
// Post-install loads — each opt-in step also adds its deploy folder to the sparse clone.
|
|
490
|
+
.option('--with-plugins', 'Post-install: build + load plugins (adds deploy/plugins, deploy/codebuild)', false)
|
|
491
|
+
.option('--with-compliance', 'Post-install: load sample compliance rules/policies (adds deploy/compliance)', false)
|
|
492
|
+
.option('--with-samples', 'Post-install: load sample pipeline templates (adds deploy/samples)', false)
|
|
493
|
+
.option('--with-all', 'Post-install: plugins + compliance + samples', false)
|
|
494
|
+
.option('--build-bootstrap', 'Build + publish the CodeBuild bootstrap image during register (adds deploy/codebuild)', false)
|
|
495
|
+
.option('--with-smoke-test', 'Post-install: read-only API reachability check', false)
|
|
496
|
+
.option('--with-events', 'Post-install (AWS): event ingestion bundle — store-token (JWT → Secrets Manager) then setup-events (EventBridge/Lambda)', false)
|
|
497
|
+
.option('--post-step <cmd>', 'Run an extra command after the loads (repeatable, in order)', collectStep, [])
|
|
498
|
+
.option('--admin-email <addr>', 'Admin email for non-interactive register (sets PLATFORM_IDENTIFIER)')
|
|
499
|
+
.option('--admin-password <pw>', 'Admin password for non-interactive register (sets PLATFORM_PASSWORD)')
|
|
500
|
+
.option('--json', 'Print the plan as JSON and exit WITHOUT running (the only non-executing mode — for tooling/CI inspection)', false)
|
|
501
|
+
.action(async (options) => {
|
|
502
|
+
const executionId = printCommandHeader('Provision');
|
|
503
|
+
try {
|
|
504
|
+
// Resolve the single post-deploy init mode (auto|manual|skip) from --init or its
|
|
505
|
+
// deprecated aliases. Two internal signals derive from it: whether init happens at
|
|
506
|
+
// all, and whether the DEPLOY self-runs it (vs. we surface it).
|
|
507
|
+
const initMode = resolveInitMode(options);
|
|
508
|
+
if (initMode === null) {
|
|
509
|
+
printError(`Invalid --init value '${String(options.init)}'. Use one of: ${INIT_MODES.join(' | ')}.`);
|
|
510
|
+
process.exitCode = 1;
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const initEnabled = initMode !== 'skip'; // skip → no register/loads at all
|
|
514
|
+
const selfInit = initMode === 'auto'; // auto → the deploy initializes itself
|
|
515
|
+
// 1. Assemble params from explicit flags (flags always win over NL parse).
|
|
516
|
+
const params = {
|
|
517
|
+
region: options.region,
|
|
518
|
+
domain: options.domain,
|
|
519
|
+
hostedZoneId: options.hostedZoneId,
|
|
520
|
+
deployMode: options.deployMode,
|
|
521
|
+
keyPair: options.keyPair,
|
|
522
|
+
instanceType: options.instanceType,
|
|
523
|
+
// ec2 LEAN mode (boolean) → assembleCommand emits `--lean`, which setup.sh
|
|
524
|
+
// forwards to the CFN `Lean` param. Only ec2's spec lists it; other targets ignore it.
|
|
525
|
+
lean: options.lean === true,
|
|
526
|
+
// Deploy-time resource identity: ec2 emits --stack-name (key stackName), eks emits
|
|
527
|
+
// --cluster-name (key clusterName). Both also drive teardown. Undefined → defaults.
|
|
528
|
+
stackName: options.stackName,
|
|
529
|
+
clusterName: options.clusterName,
|
|
530
|
+
ghcrToken: options.ghcrToken,
|
|
531
|
+
// email/noEmail are coerced AFTER the NL merge below — parseGoal can return
|
|
532
|
+
// `email`, so pre-coercing here (never-undefined) would let the merge's
|
|
533
|
+
// `=== undefined` guard silently drop an AI-parsed email choice. Left out of
|
|
534
|
+
// this literal on purpose; resolved a few lines down.
|
|
535
|
+
emailFrom: options.emailFrom,
|
|
536
|
+
emailFromName: options.emailFromName,
|
|
537
|
+
alertEmail: options.alertEmail,
|
|
538
|
+
noCreateSesIdentity: options.skipSesIdentity,
|
|
539
|
+
// The AWS deploys self-init by default (ec2 on first boot, eks in setup.sh's final
|
|
540
|
+
// phase), so we emit the load-bearing `--no-auto-init` when the mode is NOT auto
|
|
541
|
+
// (manual/skip = "don't let the deploy init itself"). `--auto-init` (a no-op reaffirm)
|
|
542
|
+
// is never emitted. Only ec2/eks carry these flags in their spec; other targets ignore it.
|
|
543
|
+
noAutoInit: !selfInit,
|
|
544
|
+
};
|
|
545
|
+
const aiOpts = { provider: options.aiProvider, model: options.model };
|
|
546
|
+
// Bootstrap (sparse clone) + post-install selections.
|
|
547
|
+
const wantBootstrap = options.repo !== undefined && options.repo !== false;
|
|
548
|
+
const withAll = options.withAll === true;
|
|
549
|
+
const anyLoadFlag = withAll || LOAD_STEPS.some((s) => options[s.flag] === true);
|
|
550
|
+
let enabledLoadIds = LOAD_STEPS.filter((s) => withAll || options[s.flag] === true).map((s) => s.id);
|
|
551
|
+
// With no --with-* flags, an interactive run offers each load — but AFTER the
|
|
552
|
+
// clone, so the questions come once you've agreed to proceed (not up front).
|
|
553
|
+
// Each picked load's folder is then fetched via an additive sparse re-sync.
|
|
554
|
+
// Flags / --yes / --json / non-interactive shells skip the prompts.
|
|
555
|
+
let willPromptLoads = !options.yes && !options.json && Boolean(process.stdin.isTTY) && !anyLoadFlag;
|
|
556
|
+
const postStepFlags = {
|
|
557
|
+
init: initEnabled,
|
|
558
|
+
// selfInit (mode auto) drops the surfaced register step on the AWS targets in
|
|
559
|
+
// resolvePostSteps, so a self-initializing ec2/eks deploy doesn't also print a manual
|
|
560
|
+
// register step. For manual mode (or local/minikube) the register step is surfaced.
|
|
561
|
+
autoInit: selfInit,
|
|
562
|
+
buildBootstrap: options.buildBootstrap === true || enabledLoadIds.includes('plugins'),
|
|
563
|
+
smokeTest: options.withSmokeTest === true,
|
|
564
|
+
events: options.withEvents === true,
|
|
565
|
+
steps: (options.postStep ?? []),
|
|
566
|
+
};
|
|
567
|
+
// Admin credentials forwarded as env so register runs non-interactively.
|
|
568
|
+
const adminEnv = {};
|
|
569
|
+
if (typeof options.adminEmail === 'string')
|
|
570
|
+
adminEnv.PLATFORM_IDENTIFIER = options.adminEmail;
|
|
571
|
+
if (typeof options.adminPassword === 'string')
|
|
572
|
+
adminEnv.PLATFORM_PASSWORD = options.adminPassword;
|
|
573
|
+
// 2. Optionally parse a natural-language goal to fill any GAPS (best-effort).
|
|
574
|
+
let target = isTargetId(options.target) ? options.target : undefined;
|
|
575
|
+
if (options.prompt) {
|
|
576
|
+
if (isAiConfigured(aiOpts)) {
|
|
577
|
+
const parsed = await parseGoal(options.prompt, aiOpts);
|
|
578
|
+
if (parsed) {
|
|
579
|
+
if (!target && isTargetId(parsed.target))
|
|
580
|
+
target = parsed.target;
|
|
581
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
582
|
+
if (k !== 'target' && params[k] === undefined && v !== undefined)
|
|
583
|
+
params[k] = v;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
else if (!options.json) {
|
|
588
|
+
// Don't print human text before a --json plan (it would corrupt the stream).
|
|
589
|
+
printWarning('A --prompt was given but no AI key is set — ignoring it and using flags only. Set ANTHROPIC_API_KEY (or --ai-provider + its key).');
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
// Resolve email NOW (after the NL merge): an explicit --email/--no-email flag wins,
|
|
593
|
+
// else an AI-parsed `email` (merged into params.email above) decides, else default-on.
|
|
594
|
+
// Coerce to the two load-bearing keys assembleCommand emits.
|
|
595
|
+
const emailChoice = options.email !== undefined
|
|
596
|
+
? options.email
|
|
597
|
+
: (typeof params.email === 'boolean' ? params.email : undefined);
|
|
598
|
+
params.email = emailChoice === true;
|
|
599
|
+
params.noEmail = emailChoice === false;
|
|
600
|
+
// 3. Optional failure diagnosis (independent of a deploy plan).
|
|
601
|
+
if (options.diagnose) {
|
|
602
|
+
let failureText;
|
|
603
|
+
try {
|
|
604
|
+
failureText = readFileSync(options.diagnose, 'utf-8');
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
printError(`Cannot read --diagnose file: ${options.diagnose}`);
|
|
608
|
+
process.exitCode = 1;
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const diagnosis = isAiConfigured(aiOpts) ? await diagnoseFailure(failureText, aiOpts) : null;
|
|
612
|
+
// With --json, the diagnosis IS the machine-readable output — emit it
|
|
613
|
+
// alone (don't also print text, which would corrupt the JSON stream).
|
|
614
|
+
if (options.json) {
|
|
615
|
+
console.log(JSON.stringify({ success: true, executionId, diagnosis }, null, 2));
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if (diagnosis) {
|
|
619
|
+
printSection('Failure diagnosis');
|
|
620
|
+
printInfo(diagnosis);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
printWarning('Diagnosis unavailable (no AI key configured or the model could not be reached).');
|
|
624
|
+
}
|
|
625
|
+
// --diagnose is a read-only inspection mode: return whether or not a target was
|
|
626
|
+
// given, so it never falls through into a (possibly --yes) deploy.
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
// With `--init auto` (the default) the EC2 instance loads plugins/compliance/samples
|
|
630
|
+
// itself on the box (bootstrap.sh hardcodes LOAD_*=y), so the local load picker — and
|
|
631
|
+
// the load-folder fetch it triggers — is pointless. Skip both (the instance has its
|
|
632
|
+
// own checkout).
|
|
633
|
+
if (selfInit && target === 'ec2') {
|
|
634
|
+
willPromptLoads = false;
|
|
635
|
+
enabledLoadIds = [];
|
|
636
|
+
}
|
|
637
|
+
else if (selfInit && target === 'eks' && !anyLoadFlag) {
|
|
638
|
+
// `--init auto` on eks initializes like ec2: NO interactive picker — default to
|
|
639
|
+
// loading ALL of plugins + compliance + samples (setup.sh's final phase hardcodes
|
|
640
|
+
// LOAD_*=y). setup.sh runs the loads itself (over a kubectl port-forward), but we
|
|
641
|
+
// still resolve them here so the sparse clone fetches the folders setup.sh builds
|
|
642
|
+
// from. Explicit `--with-*` flags override; `--init manual` / `--init skip` opt out.
|
|
643
|
+
willPromptLoads = false;
|
|
644
|
+
enabledLoadIds = LOAD_STEPS.map((s) => s.id);
|
|
645
|
+
postStepFlags.buildBootstrap = options.buildBootstrap === true || enabledLoadIds.includes('plugins');
|
|
646
|
+
}
|
|
647
|
+
// minikube/local keep the interactive picker (local-dev; no `--init auto` load parity).
|
|
648
|
+
// 4. Need a target to assemble a plan.
|
|
649
|
+
if (!target) {
|
|
650
|
+
printError(`No target specified. Pass --target <${TARGET_IDS.join('|')}> or describe the goal with --prompt.`);
|
|
651
|
+
process.exitCode = 1;
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const spec = TARGETS[target];
|
|
655
|
+
let cwd = process.cwd();
|
|
656
|
+
// 4b. Teardown mode — the destroy counterpart of provisioning (see runTeardown).
|
|
657
|
+
if (options.teardown) {
|
|
658
|
+
await runTeardown(spec, target, cwd, executionId, options);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
// Make previously-fetched single-binary tools (e.g. yq) visible to the
|
|
662
|
+
// prereq checks + the deploy — they live in the tools cache, not on the
|
|
663
|
+
// system PATH. (Both `has()` and the deploy's `bash -lc` inherit this.)
|
|
664
|
+
withToolsOnPath();
|
|
665
|
+
// Hard-require plugin prereqs (e.g. yq on minikube) only when plugins is EXPLICITLY
|
|
666
|
+
// selected — NOT on the willPromptLoads guess, which would block/prompt before the
|
|
667
|
+
// user has even been asked whether they want plugins. If they pick plugins in the
|
|
668
|
+
// interactive prompt, we re-check + offer to fetch right after that selection (below).
|
|
669
|
+
let prereqs = checkPrereqs(target, { bootstrap: wantBootstrap, withPlugins: enabledLoadIds.includes('plugins') });
|
|
670
|
+
const { command, missing } = assembleCommand(spec, params);
|
|
671
|
+
const url = deriveHealthUrl(target, params);
|
|
672
|
+
// Sparse bootstrap clone command (common base + target + selected loads).
|
|
673
|
+
// Interactive loads are chosen AFTER the clone, so the clone stays minimal —
|
|
674
|
+
// each picked load's folder is fetched then via an additive sparse re-sync.
|
|
675
|
+
const sparsePaths = sparsePathsFor(target, enabledLoadIds);
|
|
676
|
+
const bootstrap = resolveBootstrap({ repo: typeof options.repo === 'string' ? options.repo : undefined, ref: options.ref, workdir: options.workdir, full: !gitSupportsSparseCheckout() }, sparsePaths);
|
|
677
|
+
const bootstrapCmd = wantBootstrap ? bootstrapCommand(bootstrap) : null;
|
|
678
|
+
// Resolve post-install steps (register → smoke → events → custom). Re-resolved
|
|
679
|
+
// after the post-clone load prompts (see below) when those run interactively.
|
|
680
|
+
let { steps: postSteps, skipped: skippedSteps } = resolvePostSteps({
|
|
681
|
+
target,
|
|
682
|
+
url,
|
|
683
|
+
region: typeof params.region === 'string' ? params.region : undefined,
|
|
684
|
+
enabledLoadIds,
|
|
685
|
+
...postStepFlags,
|
|
686
|
+
});
|
|
687
|
+
if (options.json) {
|
|
688
|
+
console.log(JSON.stringify({
|
|
689
|
+
success: true,
|
|
690
|
+
executionId,
|
|
691
|
+
target,
|
|
692
|
+
prereqs,
|
|
693
|
+
prereqsSatisfied: prereqsSatisfied(prereqs),
|
|
694
|
+
missingInputs: missing.map((m) => ({ flag: m.flag, description: m.description })),
|
|
695
|
+
bootstrap: bootstrapCmd,
|
|
696
|
+
sparsePaths: wantBootstrap ? sparsePaths : null,
|
|
697
|
+
command,
|
|
698
|
+
postSteps: postSteps.map((s) => ({ id: s.id, label: s.label, command: s.command })),
|
|
699
|
+
skippedPostSteps: skippedSteps,
|
|
700
|
+
}, null, 2));
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
// 5. Print the plan (shown before the gated execution below).
|
|
704
|
+
printSection(`Provision plan — ${spec.label}`);
|
|
705
|
+
printKeyValue({ 'Target': target, 'Best for': spec.bestFor, 'Cost': spec.cost });
|
|
706
|
+
printSection('Prerequisites');
|
|
707
|
+
for (const c of prereqs)
|
|
708
|
+
printInfo(`${c.ok ? '✓' : '✗'} ${c.name} — ${c.detail}`);
|
|
709
|
+
if (!prereqsSatisfied(prereqs))
|
|
710
|
+
printWarning('Resolve the failing prerequisites above before deploying.');
|
|
711
|
+
if (missing.length > 0) {
|
|
712
|
+
printSection('Missing required inputs');
|
|
713
|
+
for (const m of missing)
|
|
714
|
+
printInfo(` --${m.flag} (${m.description})`);
|
|
715
|
+
}
|
|
716
|
+
if (bootstrapCmd) {
|
|
717
|
+
printSection('Bootstrap (sparse git clone, runs first)');
|
|
718
|
+
printInfo(`Clone ${bootstrap.repo} @ ${bootstrap.ref} → ${bootstrap.workdir}; folders: ${sparsePaths.join(', ')}`);
|
|
719
|
+
printInfo(bootstrapCmd);
|
|
720
|
+
}
|
|
721
|
+
printSection('Command to run');
|
|
722
|
+
printInfo(command);
|
|
723
|
+
if (postSteps.length > 0 || skippedSteps.length > 0) {
|
|
724
|
+
printSection('Post-install steps');
|
|
725
|
+
for (const s of postSteps)
|
|
726
|
+
printInfo(`• ${s.label}\n ${s.command}`);
|
|
727
|
+
for (const s of skippedSteps)
|
|
728
|
+
printWarning(`Skipped ${s.id}: ${s.reason}`);
|
|
729
|
+
}
|
|
730
|
+
// 6. Any missing required prereq that's a single static binary (e.g. yq)
|
|
731
|
+
// can be fetched into the tools cache instead of a system install — no
|
|
732
|
+
// brew/apt. Offer it, then re-check (the cache dir is already on PATH).
|
|
733
|
+
const fetchable = prereqs.filter((c) => !c.ok && c.required && isFetchable(c.name));
|
|
734
|
+
if (fetchable.length > 0) {
|
|
735
|
+
const names = fetchable.map((c) => c.name).join(', ');
|
|
736
|
+
if (await confirm(`\n${names} not installed — fetch the official static binary into ${TOOLS_DIR} (no system install)?`, options.yes)) {
|
|
737
|
+
for (const c of fetchable) {
|
|
738
|
+
printInfo(`Fetching ${c.name}…`);
|
|
739
|
+
if (!fetchTool(c.name))
|
|
740
|
+
printWarning(`Couldn't fetch ${c.name} — install it manually and re-run.`);
|
|
741
|
+
}
|
|
742
|
+
prereqs = checkPrereqs(target, { bootstrap: wantBootstrap, withPlugins: enabledLoadIds.includes('plugins') });
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// 7b. Gated execution. Check prereqs / required inputs first — this also
|
|
746
|
+
// catches a missing `git` when bootstrapping.
|
|
747
|
+
const blocked = executionBlocked(prereqs, missing);
|
|
748
|
+
if (blocked) {
|
|
749
|
+
printError(`Can't start just yet — ${blocked}. Resolve that and re-run.`);
|
|
750
|
+
process.exitCode = 1;
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
// Commit gate — confirm BEFORE any side effect (clone, .env, deploy) so a "No"
|
|
754
|
+
// leaves nothing behind. The port check + optional loads + .env below are part of
|
|
755
|
+
// executing the agreed-to plan.
|
|
756
|
+
if (!(await confirm(`\nProceed with provisioning ${spec.label}?`, options.yes))) {
|
|
757
|
+
printWarning('No problem — nothing was changed.');
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
// 7a/7b. Sparse-clone (if --repo or accepted interactively) + locate the deploy
|
|
761
|
+
// entrypoint, repointing cwd into the clone. See bootstrapAndLocate.
|
|
762
|
+
const located = await bootstrapAndLocate(spec, bootstrap, bootstrapCmd, sparsePaths, cwd, options);
|
|
763
|
+
cwd = located.cwd;
|
|
764
|
+
const bootstrapped = located.bootstrapped;
|
|
765
|
+
if (!located.ok)
|
|
766
|
+
return;
|
|
767
|
+
// Host-port pre-flight — now that the deploy source is on disk (post-clone),
|
|
768
|
+
// derive the ports from it (compose / setup.sh) and stop on a fatal conflict
|
|
769
|
+
// before deploying. See preflightPorts.
|
|
770
|
+
if (!(await preflightPorts(spec, target, cwd))) {
|
|
771
|
+
process.exitCode = 1;
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
// Opt-in loads — offered AFTER the clone (see resolveLoadsInteractively); it
|
|
775
|
+
// prompts, additively sparse-fetches the picked folders, and re-resolves the
|
|
776
|
+
// post-install steps with the selections.
|
|
777
|
+
if (willPromptLoads) {
|
|
778
|
+
const loaded = await resolveLoadsInteractively(target, url, typeof params.region === 'string' ? params.region : undefined, cwd, bootstrapped, bootstrap, postStepFlags);
|
|
779
|
+
enabledLoadIds = loaded.enabledLoadIds;
|
|
780
|
+
postSteps = loaded.steps;
|
|
781
|
+
skippedSteps = loaded.skipped;
|
|
782
|
+
// Now that plugins may have been picked, re-check the plugin-specific prereqs we
|
|
783
|
+
// deliberately didn't hard-require up front (e.g. yq for minikube plugin builds).
|
|
784
|
+
// Offer to fetch any fetchable one; warn (don't block — the platform still deploys)
|
|
785
|
+
// if it can't be resolved, so the later plugin build doesn't fail opaquely.
|
|
786
|
+
if (enabledLoadIds.includes('plugins')) {
|
|
787
|
+
const pluginPrereqs = checkPrereqs(target, { bootstrap: wantBootstrap, withPlugins: true });
|
|
788
|
+
const pluginGaps = pluginPrereqs.filter((c) => !c.ok && c.required && !prereqs.some((p) => p.name === c.name));
|
|
789
|
+
for (const c of pluginGaps) {
|
|
790
|
+
if (isFetchable(c.name) && await confirm(`\n${c.name} is needed to build plugins but isn't installed — fetch the static binary into ${TOOLS_DIR}?`, options.yes)) {
|
|
791
|
+
printInfo(`Fetching ${c.name}…`);
|
|
792
|
+
if (!fetchTool(c.name))
|
|
793
|
+
printWarning(`Couldn't fetch ${c.name} — install it before the plugin build runs.`);
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
printWarning(`${c.name} missing — the plugin load/build step may fail. ${c.detail}`);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
// local/minikube's setup.sh REQUIRES a `.env` and aborts without it — create
|
|
802
|
+
// it from .env.example with generated secrets so the deploy is non-interactive.
|
|
803
|
+
// ec2 and eks don't have provision generate a local `.env`: ec2 bootstraps its
|
|
804
|
+
// secrets on the instance, and eks's setup.sh creates the k8s secrets itself
|
|
805
|
+
// (a deploy/aws/eks/.env.example + secret step is a setup.sh TODO).
|
|
806
|
+
if ((target === 'docker' || target === 'minikube') && envFileMissing(cwd, spec.dir)) {
|
|
807
|
+
if (await confirm(`\n${spec.dir}/.env not found — create it from .env.example (generates secrets; edit later for optional integrations like OAuth)?`, options.yes)) {
|
|
808
|
+
const n = createEnvFile(cwd, spec.dir);
|
|
809
|
+
printSuccess(`Created ${spec.dir}/.env — ${n} secret(s) generated.`);
|
|
810
|
+
}
|
|
811
|
+
else {
|
|
812
|
+
printWarning('Continuing without .env — setup.sh will abort if it stays missing.');
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// 7c. Run the deploy with a bounded auto-fix + retry loop (see runDeployWithRetry).
|
|
816
|
+
const { succeeded, runParams } = await runDeployWithRetry(spec, url, cwd, params, aiOpts, options);
|
|
817
|
+
if (!succeeded) {
|
|
818
|
+
process.exitCode = 1;
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
printSuccess('Deploy command completed.');
|
|
822
|
+
// 7d. SES post-deploy guidance (async DKIM + sandbox + bounce topic).
|
|
823
|
+
// SES is provisioned BY DEFAULT on AWS deploys, so surface the guidance
|
|
824
|
+
// for ec2/eks unless the operator opted out with --no-email.
|
|
825
|
+
const sesProvisioned = (target === 'ec2' || target === 'eks') && runParams.noEmail !== true;
|
|
826
|
+
if (sesProvisioned) {
|
|
827
|
+
printSection('SES — next steps');
|
|
828
|
+
for (const line of sesPostDeployGuidance())
|
|
829
|
+
printInfo(`• ${line}`);
|
|
830
|
+
}
|
|
831
|
+
// 7e. Verify health (CREATE_COMPLETE != serving).
|
|
832
|
+
if (url) {
|
|
833
|
+
printSection('Verifying health');
|
|
834
|
+
// Minikube reaches the gateway via a kubectl port-forward that setup.sh
|
|
835
|
+
// backgrounds and that can die/fail to bind — (re)start it before polling
|
|
836
|
+
// so we don't sit at the gate on a dead forward while the pods are fine.
|
|
837
|
+
if (target === 'minikube') {
|
|
838
|
+
await ensureMinikubeGateway(url, { onInfo: (m) => printInfo(m) });
|
|
839
|
+
}
|
|
840
|
+
printInfo(`Polling ${url}/health …`);
|
|
841
|
+
// Fresh EKS legitimately needs longer than the 300s default: Karpenter provisions
|
|
842
|
+
// nodes, ~10 services cold-pull images, the ALB provisions (~2-3 min), and the
|
|
843
|
+
// Route 53 alias propagates. Give it 15 min before surfacing the not-reachable note.
|
|
844
|
+
const healthTimeoutMs = target === 'eks' ? 900_000 : undefined;
|
|
845
|
+
const health = await waitHealthy(url, { timeoutMs: healthTimeoutMs, onTick: (m) => printInfo(m) });
|
|
846
|
+
// Green only when fully ready; a "health OK but /ready never came" proceed-
|
|
847
|
+
// anyway state is healthy:true but degraded → warn so it doesn't read as done.
|
|
848
|
+
(health.healthy && health.ready ? printSuccess : printWarning)(`${health.url} — ${health.detail}`);
|
|
849
|
+
}
|
|
850
|
+
// 7f. Post-install steps (register + opt-in loads, smoke test, events, custom).
|
|
851
|
+
// See runPostSteps — it surfaces register + the events bundle as manual in-VPC
|
|
852
|
+
// next-steps on EC2/EKS instead of auto-running (and failing) them locally.
|
|
853
|
+
// autoRun (skip the second confirm) only when the user actually PICKED a load in the
|
|
854
|
+
// interactive prompt — declining every load shouldn't silently run register unprompted.
|
|
855
|
+
const autoRanLoads = willPromptLoads && enabledLoadIds.length > 0;
|
|
856
|
+
await runPostSteps(postSteps, skippedSteps, target, cwd, adminEnv, { yes: options.yes, autoRun: autoRanLoads, autoInit: postStepFlags.autoInit });
|
|
857
|
+
}
|
|
858
|
+
catch (error) {
|
|
859
|
+
handleError(error, ERROR_CODES.GENERAL, {
|
|
860
|
+
debug: program.opts().debug,
|
|
861
|
+
exit: true,
|
|
862
|
+
context: { command: 'provision', executionId },
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlzaW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL3Byb3Zpc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQzlDLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxRQUFRLE1BQU0sd0JBQXdCLENBQUM7QUFDbkQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNwQyxPQUFPLEVBQUUsZUFBZSxFQUFFLGNBQWMsRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUM1RSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQXNCLE1BQU0sdUJBQXVCLENBQUM7QUFDL0YsT0FBTyxFQUFFLGFBQWEsRUFBRSxjQUFjLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNyRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsU0FBUyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDckYsT0FBTyxFQUFFLGVBQWUsRUFBRSxXQUFXLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN6RixPQUFPLEVBQUUsY0FBYyxFQUFFLGlCQUFpQixFQUFFLFlBQVksRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3BGLE9BQU8sRUFBRSxnQkFBZ0IsRUFBbUMsTUFBTSx3QkFBd0IsQ0FBQztBQUMzRixPQUFPLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSx5QkFBeUIsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzlHLE9BQU8sRUFDTCxlQUFlLEVBQ2YsVUFBVSxFQUNWLFVBQVUsRUFDVixjQUFjLEVBQ2QsT0FBTyxFQUNQLFVBQVUsRUFDVixlQUFlLEdBR2hCLE1BQU0scUJB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QUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsV0FBVyxFQUFFO2FBQy9DLENBQUMsQ0FBQztRQUNMLENBQUM7SUFDSCxDQUFDLENBQUMsQ0FBQztBQUNQLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCBleGlzdHNTeW5jIH0gZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0ICogYXMgcmVhZGxpbmUgZnJvbSAnbm9kZTpyZWFkbGluZS9wcm9taXNlcyc7XG5pbXBvcnQgeyBDb21tYW5kIH0gZnJvbSAnY29tbWFuZGVyJztcbmltcG9ydCB7IGRpYWdub3NlRmFpbHVyZSwgaXNBaUNvbmZpZ3VyZWQsIHBhcnNlR29hbCB9IGZyb20gJy4uL2FnZW50L2FpLmpzJztcbmltcG9ydCB7IGJvb3RzdHJhcENvbW1hbmQsIHJlc29sdmVCb290c3RyYXAsIHR5cGUgQm9vdHN0cmFwU3BlYyB9IGZyb20gJy4uL2FnZW50L2Jvb3RzdHJhcC5qcyc7XG5pbXBvcnQgeyBjcmVhdGVFbnZGaWxlLCBlbnZGaWxlTWlzc2luZyB9IGZyb20gJy4uL2FnZW50L2Vudi1maWxlLmpzJztcbmltcG9ydCB7IGVudHJ5cG9pbnRFeGlzdHMsIGV4ZWN1dGlvbkJsb2NrZWQsIHJ1blNjcmlwdCB9IGZyb20gJy4uL2FnZW50L2V4ZWN1dG9yLmpzJztcbmltcG9ydCB7IGRlcml2ZUhlYWx0aFVybCwgd2FpdEhlYWx0aHksIGVuc3VyZU1pbmlrdWJlR2F0ZXdheSB9IGZyb20gJy4uL2FnZW50L2hlYWx0aC5qcyc7XG5pbXBvcnQgeyBjaGVja0hvc3RQb3J0cywgZGlzY292ZXJIb3N0UG9ydHMsIHN0YWNrUnVubmluZyB9IGZyb20gJy4uL2FnZW50L3BvcnRzLmpzJztcbmltcG9ydCB7IHJlc29sdmVQb3N0U3RlcHMsIHR5cGUgUG9zdFN0ZXAsIHR5cGUgU2tpcHBlZFN0ZXAgfSBmcm9tICcuLi9hZ2VudC9wb3N0LXN0ZXBzLmpzJztcbmltcG9ydCB7IGNoZWNrUHJlcmVxcywgZ2l0QXZhaWxhYmxlLCBnaXRTdXBwb3J0c1NwYXJzZUNoZWNrb3V0LCBwcmVyZXFzU2F0aXNmaWVkIH0gZnJvbSAnLi4vYWdlbnQvcHJlcmVxcy5qcyc7XG5pbXBvcnQge1xuICBhc3NlbWJsZUNvbW1hbmQsXG4gIGlzVGFyZ2V0SWQsXG4gIExPQURfU1RFUFMsXG4gIHNwYXJzZVBhdGhzRm9yLFxuICBUQVJHRVRTLFxuICBUQVJHRVRfSURTLFxuICB0ZWFyZG93bkNvbW1hbmQsXG4gIHR5cGUgVGFyZ2V0SWQsXG4gIHR5cGUgVGFyZ2V0U3BlYyxcbn0gZnJvbSAnLi4vYWdlbnQvdGFyZ2V0cy5qcyc7XG5pbXBvcnQgeyBUT09MU19ESVIsIGZldGNoVG9vbCwgaXNGZXRjaGFibGUsIHdpdGhUb29sc09uUGF0aCB9IGZyb20gJy4uL2FnZW50L3Rvb2xzLmpzJztcbmltcG9ydCB7IG1hdGNoSXNzdWVzLCBzZXNQb3N0RGVwbG95R3VpZGFuY2UgfSBmcm9tICcuLi9hZ2VudC90cm91Ymxlc2hvb3QuanMnO1xuaW1wb3J0IHsgc2hlbGxRdW90ZSB9IGZyb20gJy4uL2NvbmZpZy9jbGkuY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IHByaW50Q29tbWFuZEhlYWRlciB9IGZyb20gJy4uL3V0aWxzL2NvbW1hbmQtdXRpbHMuanMnO1xuaW1wb3J0IHsgRVJST1JfQ09ERVMsIGhhbmRsZUVycm9yIH0gZnJvbSAnLi4vdXRpbHMvZXJyb3ItaGFuZGxlci5qcyc7XG5pbXBvcnQgeyBwcmludFNlY3Rpb24sIHByaW50S2V5VmFsdWUsIHByaW50SW5mbywgcHJpbnRXYXJuaW5nLCBwcmludEVycm9yLCBwcmludFN1Y2Nlc3MgfSBmcm9tICcuLi91dGlscy9vdXRwdXQtdXRpbHMuanMnO1xuXG4vKipcbiAqIFBvc3QtZGVwbG95IGluaXRpYWxpemF0aW9uIG1vZGUgKHRoZSBzaW5nbGUgYC0taW5pdCA8bW9kZT5gIGZsYWcpOlxuICogICAtIGBhdXRvYCAgIOKAlCBpbml0LXBsYXRmb3JtIHJ1bnMgb25jZSB0aGUgcGxhdGZvcm0gaXMgdXAgKHJlZ2lzdGVyIGFkbWluICsgbG9hZCBwbHVnaW5zL1xuICogICAgICAgICAgICAgICAgY29tcGxpYW5jZS9zYW1wbGVzKS4gT24gZWMyIHRoZSBpbnN0YW5jZSBkb2VzIGl0IGl0c2VsZiBvbiBmaXJzdCBib290OyBvblxuICogICAgICAgICAgICAgICAgbG9jYWwvbWluaWt1YmUvZWtzIGBwcm92aXNpb25gIHJ1bnMgaXQgZm9yIHlvdS4gVGhpcyBpcyB0aGUgREVGQVVMVC5cbiAqICAgLSBgbWFudWFsYCDigJQgZG9uJ3Qgc2VsZi1pbml0OyBzdXJmYWNlIHRoZSBleGFjdCBzdGVwIGZvciB5b3UgdG8gcnVuIHlvdXJzZWxmLlxuICogICAtIGBza2lwYCAgIOKAlCBkb24ndCBpbml0aWFsaXplIGF0IGFsbCAobm8gcmVnaXN0ZXIsIG5vIGxvYWRzKS5cbiAqL1xuZXhwb3J0IHR5cGUgSW5pdE1vZGUgPSAnYXV0bycgfCAnbWFudWFsJyB8ICdza2lwJztcbmNvbnN0IElOSVRfTU9ERVM6IHJlYWRvbmx5IEluaXRNb2RlW10gPSBbJ2F1dG8nLCAnbWFudWFsJywgJ3NraXAnXTtcblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBpbml0IG1vZGUgZnJvbSB0aGUgbmV3IGAtLWluaXQgPG1vZGU+YCBmbGFnLCBmYWxsaW5nIGJhY2sgdG8gdGhlIERFUFJFQ0FURURcbiAqIGFsaWFzZXMgKGAtLW5vLWluaXRgIOKGkiBza2lwLCBgLS1hdXRvLWluaXRgIOKGkiBhdXRvLCBgLS1uby1hdXRvLWluaXRgIOKGkiBtYW51YWwpLCB0aGVuIHRoZVxuICogZGVmYXVsdCAoYGF1dG9gKS4gUmV0dXJucyBgbnVsbGAgd2hlbiBgLS1pbml0YCB3YXMgZ2l2ZW4gYW4gaW52YWxpZCB2YWx1ZSAoY2FsbGVyIGVycm9ycykuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlSW5pdE1vZGUob3B0aW9uczogeyBpbml0PzogdW5rbm93bjsgYXV0b0luaXQ/OiB1bmtub3duIH0pOiBJbml0TW9kZSB8IG51bGwge1xuICBpZiAodHlwZW9mIG9wdGlvbnMuaW5pdCA9PT0gJ3N0cmluZycpIHsgLy8gLS1pbml0IDxtb2RlPlxuICAgIGNvbnN0IG0gPSBvcHRpb25zLmluaXQudG9Mb3dlckNhc2UoKSBhcyBJbml0TW9kZTtcbiAgICByZXR1cm4gSU5JVF9NT0RFUy5pbmNsdWRlcyhtKSA/IG0gOiBudWxsO1xuICB9XG4gIGlmIChvcHRpb25zLmluaXQgPT09IGZhbHNlKSByZXR1cm4gJ3NraXAnOyAvLyBkZXByZWNhdGVkIC0tbm8taW5pdFxuICBpZiAob3B0aW9ucy5hdXRvSW5pdCA9PT0gdHJ1ZSkgcmV0dXJuICdhdXRvJzsgLy8gZGVwcmVjYXRlZCAtLWF1dG8taW5pdFxuICBpZiAob3B0aW9ucy5hdXRvSW5pdCA9PT0gZmFsc2UpIHJldHVybiAnbWFudWFsJzsgLy8gZGVwcmVjYXRlZCAtLW5vLWF1dG8taW5pdFxuICByZXR1cm4gJ2F1dG8nOyAvLyBkZWZhdWx0XG59XG5cbi8qKlxuICogUmVhZCBvbmUgbGluZSBvZiB0cmltbWVkIGlucHV0IGZyb20gdGhlIHRlcm1pbmFsLCBvd25pbmcgdGhlIHJlYWRsaW5lXG4gKiBsaWZlY3ljbGUuIFVzZWQgZGlyZWN0bHkgZm9yIHRoZSBkZXN0cnVjdGl2ZSB0ZWFyZG93biBnYXRlIChhIHkvTiBpcyB0b28gZWFzeVxuICogdG8gZmF0LWZpbmdlciwgc28gd2UgcmVxdWlyZSB0aGUgb3BlcmF0b3IgdG8gVFlQRSB0aGUgdGFyZ2V0IGlkIGJhY2spLCBhbmQgYXNcbiAqIHRoZSBiYXNpcyBmb3IgYGNvbmZpcm1gLiBEZWxpYmVyYXRlbHkgaGFzIG5vIGF1dG8tYWNjZXB0LlxuICovXG5hc3luYyBmdW5jdGlvbiBhc2socXVlc3Rpb246IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHJsID0gcmVhZGxpbmUuY3JlYXRlSW50ZXJmYWNlKHsgaW5wdXQ6IHByb2Nlc3Muc3RkaW4sIG91dHB1dDogcHJvY2Vzcy5zdGRvdXQgfSk7XG4gIHRyeSB7XG4gICAgcmV0dXJuIChhd2FpdCBybC5xdWVzdGlvbihxdWVzdGlvbikpLnRyaW0oKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBybC5jbG9zZSgpO1xuICB9XG59XG5cbi8qKiBJbnRlcmFjdGl2ZSB5L04gY29uZmlybWF0aW9uOyBhdXRvLXRydWUgd2hlbiBgYXV0b1llc2AgKHRoZSBgLS15ZXNgIGZsYWcpLiAqL1xuYXN5bmMgZnVuY3Rpb24gY29uZmlybShxdWVzdGlvbjogc3RyaW5nLCBhdXRvWWVzOiBib29sZWFuKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGlmIChhdXRvWWVzKSByZXR1cm4gdHJ1ZTtcbiAgY29uc3QgYW5zd2VyID0gKGF3YWl0IGFzayhgJHtxdWVzdGlvbn0gW3kvTl0gYCkpLnRvTG93ZXJDYXNlKCk7XG4gIHJldHVybiBhbnN3ZXIgPT09ICd5JyB8fCBhbnN3ZXIgPT09ICd5ZXMnO1xufVxuXG4vKiogQ29tbWFuZGVyIGNvbGxlY3RvcjogYWNjdW11bGF0ZSByZXBlYXRlZCBgLS1wb3N0LXN0ZXBgIHZhbHVlcyBpbiBvcmRlci4gKi9cbmZ1bmN0aW9uIGNvbGxlY3RTdGVwKHZhbHVlOiBzdHJpbmcsIGFjYzogc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBbLi4uYWNjLCB2YWx1ZV07XG59XG5cbi8qKlxuICogSG9zdC1wb3J0IHByZS1mbGlnaHQgKGxvY2FsL21pbmlrdWJlKS4gUG9ydHMgYXJlIERFUklWRUQgZnJvbSB0aGUgdGFyZ2V0J3MgY2xvbmVkXG4gKiBkZXBsb3kgc291cmNlIChjb21wb3NlIC8gc2V0dXAuc2gpIHNvIHRoZSBsaXN0IGNhbid0IGRyaWZ0IOKAlCBoZW5jZSB0aGlzIHJ1bnMgQUZURVJcbiAqIHRoZSBjbG9uZS4gQSB0YWtlbiBwb3J0IG1ha2VzIGEgY29udGFpbmVyL2ZvcndhcmQgZmFpbCB0byBiaW5kIG1pZC1kZXBsb3kgKGZvciB0aGVcbiAqIGdhdGV3YXksIGEgc2lsZW50IHVucmVhY2hhYmxlIGhhbmcpLiBQcmludHMgYSDinJMv4pyXIHN1bW1hcnkgYW5kIHJldHVybnMgZmFsc2UgT05MWVxuICogd2hlbiB0aGUgY2FsbGVyIG11c3QgQUJPUlQgKGEgZmF0YWwgbG9jYWwgY29uZmxpY3QpLiBtaW5pa3ViZSBwb3J0cyBhcmUgbWFuYWdlZFxuICogZm9yd2FyZHMgKHNldHVwLnNoIHBraWxscy9yZXN0YXJ0cyB0aGVtKSwgc28gYSBjb25mbGljdCB0aGVyZSB3YXJucyBidXQgcHJvY2VlZHMuXG4gKiBSZW1vdGUgdGFyZ2V0cyAoZWMyL2VrcykgYmluZCBub3RoaW5nIGxvY2FsbHkg4oaSIHJldHVybnMgdHJ1ZS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHByZWZsaWdodFBvcnRzKHNwZWM6IFRhcmdldFNwZWMsIHRhcmdldDogVGFyZ2V0SWQsIGN3ZDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGNvbnN0IHBvcnRzID0gZGlzY292ZXJIb3N0UG9ydHModGFyZ2V0LCBjd2QsIHNwZWMpO1xuICBpZiAocG9ydHMubGVuZ3RoID09PSAwKSByZXR1cm4gdHJ1ZTtcbiAgY29uc3QgcG9ydENoZWNrcyA9IGF3YWl0IGNoZWNrSG9zdFBvcnRzKHBvcnRzKTtcbiAgY29uc3QgdGFrZW4gPSBwb3J0Q2hlY2tzLmZpbHRlcigoYykgPT4gIWMuYXZhaWxhYmxlKTtcbiAgcHJpbnRTZWN0aW9uKCdQb3J0IGF2YWlsYWJpbGl0eScpO1xuICBmb3IgKGNvbnN0IGMgb2YgcG9ydENoZWNrcykgcHJpbnRJbmZvKGAke2MuYXZhaWxhYmxlID8gJ+KckycgOiAn4pyXJ30gJHtTdHJpbmcoYy5wb3J0KS5wYWRFbmQoNSl9IOKAlCAke2Muc2VydmljZX1gKTtcbiAgaWYgKHRha2VuLmxlbmd0aCA9PT0gMCkgcmV0dXJuIHRydWU7XG4gIC8vIGxvY2FsIFBVQkxJU0hFUyB0aGVzZSBwb3J0cyBhdCBgZG9ja2VyIGNvbXBvc2UgdXBgIHRpbWUsIHNvIGEgY29uZmxpY3QgaXMgZmF0YWwuXG4gIC8vIG1pbmlrdWJlJ3MgYXJlIGt1YmVjdGwgcG9ydC1mb3J3YXJkcyBzZXR1cC5zaCBwa2lsbHMgKyByZXN0YXJ0cyAoYW5kXG4gIC8vIGVuc3VyZU1pbmlrdWJlR2F0ZXdheSByZWNvdmVycyksIHNvIGEgc3RhbGUgZm9yd2FyZCBtdXN0IE5PVCBibG9jayBhIHJlLXJ1bi5cbiAgY29uc3QgZmF0YWwgPSB0YXJnZXQgPT09ICdkb2NrZXInO1xuICAvLyDigKZVTkxFU1MgdGhlIGxvY2FsIHN0YWNrIGlzIGFscmVhZHkgcnVubmluZzogdGhvc2UgcG9ydHMgYXJlIGhlbGQgYnkgWU9VUiBPV05cbiAgLy8gc3RhY2ssIGFuZCBgZG9ja2VyIGNvbXBvc2UgdXBgIG5vLW9wcyB0aGVtIOKAlCB0aGlzIGlzIGV4YWN0bHkgaG93IHlvdSByZS1ydW4gdG9cbiAgLy8gYWRkIGxvYWRzL29wdGlvbnMuIERvbid0IGJsb2NrICh0aGUgd291bGQtYmUgY29uZmxpY3QgaXMgYSBzZWxmLWNvbmZsaWN0KS5cbiAgaWYgKGZhdGFsICYmIHN0YWNrUnVubmluZyh0YXJnZXQsIGN3ZCwgc3BlYykpIHtcbiAgICBwcmludFdhcm5pbmcoYFxcbiR7dGFrZW4ubGVuZ3RofSBwb3J0KHMpIGFyZSBoZWxkIGJ5IHlvdXIgYWxyZWFkeS1ydW5uaW5nICR7c3BlYy5sYWJlbH0gc3RhY2sg4oCUIHJlLXJ1bm5pbmcganVzdCByZXN1bWVzIGl0IChubyByZWFsIGNvbmZsaWN0KSwgc28gY29udGludWluZy5gKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICBjb25zdCBsZWFkID0gYCR7dGFrZW4ubGVuZ3RofSByZXF1aXJlZCBwb3J0KHMpIGFscmVhZHkgaW4gdXNlOiAke3Rha2VuLm1hcCgoYykgPT4gYy5wb3J0KS5qb2luKCcsICcpfWA7XG4gIChmYXRhbCA/IHByaW50RXJyb3IgOiBwcmludFdhcm5pbmcpKGZhdGFsXG4gICAgPyBgXFxuJHtsZWFkfSDigJQgdGhlIGRlcGxveSBjYW4ndCBiaW5kIHRoZW0gYW5kIHdvdWxkIGZhaWwgbWlkLXdheS4gRnJlZSB0aGVtIGFuZCByZS1ydW46YFxuICAgIDogYFxcbiR7bGVhZH0g4oCUIG9uIG1pbmlrdWJlIHRoZXNlIGFyZSB1c3VhbGx5IGEgcHJldmlvdXMgcnVuJ3MgcG9ydC1mb3J3YXJkcyAoc2V0dXAuc2ggcmVzdGFydHMgdGhlbSkuIEZyZWUgYW55IE5PTi1taW5pa3ViZSBob2xkZXIgaWYgdGhlIGdhdGV3YXkgc3RheXMgdW5yZWFjaGFibGU6YCk7XG4gIGZvciAoY29uc3QgYyBvZiB0YWtlbikgcHJpbnRJbmZvKGAgIOKAoiBwb3J0ICR7Yy5wb3J0fSAoJHtjLnNlcnZpY2V9KSDigJQgZmluZCB0aGUgaG9sZGVyOiAgbHNvZiAtaSA6JHtjLnBvcnR9YCk7XG4gIGlmICh0YWtlbi5zb21lKChjKSA9PiBjLnBvcnQgPT09IDUwMDApKSB7XG4gICAgcHJpbnRJbmZvKCcgIE5vdGU6IG9uIG1hY09TLCBwb3J0IDUwMDAgaXMgdXN1YWxseSBBaXJQbGF5IFJlY2VpdmVyIOKAlCBTeXN0ZW0gU2V0dGluZ3Mg4oaSIEdlbmVyYWwg4oaSIEFpckRyb3AgJiBIYW5kb2ZmIOKGkiBBaXJQbGF5IFJlY2VpdmVyIChvZmYpLicpO1xuICB9XG4gIHJldHVybiAhZmF0YWw7XG59XG5cbi8qKlxuICogVGVhcmRvd24gbW9kZSDigJQgdGhlIGRlc3Ryb3kgY291bnRlcnBhcnQgb2YgcHJvdmlzaW9uaW5nLCBnYXRlZCBIQVJERVIgdGhhbiBkZXBsb3k6XG4gKiBBV1MgdGFyZ2V0cyBhcmUgaXJyZXZlcnNpYmxlLCBzbyB0aGUgb3BlcmF0b3IgbXVzdCBUWVBFIHRoZSB0YXJnZXQgaWQgKC0tZm9yY2VcbiAqIGJ5cGFzc2VzIGZvciBDSTsgLS15ZXMgYWxvbmUgZG9lcyBOT1QpLiBUZXJtaW5hbDogb3ducyBpdHMgb3V0cHV0ICsgZXhpdENvZGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBydW5UZWFyZG93bihcbiAgc3BlYzogVGFyZ2V0U3BlYyxcbiAgdGFyZ2V0OiBUYXJnZXRJZCxcbiAgY3dkOiBzdHJpbmcsXG4gIGV4ZWN1dGlvbklkOiBzdHJpbmcsXG4gIG9wdHM6IHsgc3RhY2tOYW1lPzogc3RyaW5nOyBjbHVzdGVyTmFtZT86IHN0cmluZzsgcmVnaW9uPzogc3RyaW5nOyBkb21haW4/OiBzdHJpbmc7IGhvc3RlZFpvbmVJZD86IHN0cmluZzsgZm9yY2U/OiBib29sZWFuOyB5ZXM/OiBib29sZWFuOyBqc29uPzogYm9vbGVhbiB9LFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHsgY29tbWFuZDogZG93bkNvbW1hbmQsIGRlc3RydWN0aXZlIH0gPSB0ZWFyZG93bkNvbW1hbmQodGFyZ2V0LCB7XG4gICAgc3RhY2tOYW1lOiBvcHRzLnN0YWNrTmFtZSxcbiAgICBjbHVzdGVyTmFtZTogb3B0cy5jbHVzdGVyTmFtZSxcbiAgICByZWdpb246IG9wdHMucmVnaW9uLFxuICAgIC8vIGVrcyB0ZWFyZG93biAoYmluL3NodXRkb3duLnNoKSBhbHNvIGNsZWFucyB1cCB0aGUgQUNNIGNlcnQgKyBSb3V0ZSA1MyBhbGlhcyBmb3JcbiAgICAvLyB0aGUgZG9tYWluLCBzbyBmb3J3YXJkIHRoZW0gd2hlbiBwcmVzZW50LlxuICAgIGRvbWFpbjogb3B0cy5kb21haW4sXG4gICAgaG9zdGVkWm9uZUlkOiBvcHRzLmhvc3RlZFpvbmVJZCxcbiAgICAvLyBPdXIgdHlwZWQgZ2F0ZSBJUyB0aGUgY29uZmlybWF0aW9uIOKAlCBhc3N1bWVZZXMgZm9yd2FyZHMgYSBuYXRpdmUgYC0teWVzYCB0byBhbnlcbiAgICAvLyBzY3JpcHQtYmFzZWQgdGVhcmRvd24gKGVjMiBkZWxldGUtc3RhY2sgaGFzIG5vIHByb21wdDsgZWtzIHNodXRkb3duLnNoIHNraXBzIGl0cyBvd24pLlxuICAgIGFzc3VtZVllczogdHJ1ZSxcbiAgfSk7XG4gIGlmIChvcHRzLmpzb24pIHtcbiAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7IHN1Y2Nlc3M6IHRydWUsIGV4ZWN1dGlvbklkLCB0YXJnZXQsIHRlYXJkb3duOiB0cnVlLCBkZXN0cnVjdGl2ZSwgZGVzdHJveXM6IHNwZWMuZGVzdHJveXMsIGNvbW1hbmQ6IGRvd25Db21tYW5kIH0sIG51bGwsIDIpKTtcbiAgICByZXR1cm47XG4gIH1cbiAgcHJpbnRTZWN0aW9uKGBUZWFyZG93biBwbGFuIOKAlCAke3NwZWMubGFiZWx9YCk7XG4gIHByaW50S2V5VmFsdWUoeyAnVGFyZ2V0JzogdGFyZ2V0LCAnRGVzdHJveXMnOiBzcGVjLmRlc3Ryb3lzLCAnU3RvcHMgY29zdCc6IHNwZWMuY29zdCB9KTtcbiAgcHJpbnRTZWN0aW9uKCdDb21tYW5kIHRvIHJ1bicpO1xuICBwcmludEluZm8oZG93bkNvbW1hbmQpO1xuICBpZiAoZGVzdHJ1Y3RpdmUgJiYgIW9wdHMuZm9yY2UpIHtcbiAgICAvLyBCaW5kIHRoZSBjb25maXJtYXRpb24gdG8gdGhlIHJlc291cmNlIGFjdHVhbGx5IGRlc3Ryb3llZDogYSBjdXN0b20gbmFtZSAoZWMyXG4gICAgLy8gLS1zdGFjay1uYW1lIC8gZWtzIC0tY2x1c3Rlci1uYW1lKSBtdXN0IGJlIHR5cGVkIHZlcmJhdGltIHNvIGEgd3JvbmcgbmFtZSBjYW4ndCBiZVxuICAgIC8vIGNvbmZpcm1lZCBieSBoYWJpdDsgb3RoZXJ3aXNlIHRoZSB0YXJnZXQgaWQuXG4gICAgY29uc3QgcmVzb3VyY2VOYW1lID0gdGFyZ2V0ID09PSAnZWtzJyA/IG9wdHMuY2x1c3Rlck5hbWUgOiBvcHRzLnN0YWNrTmFtZTtcbiAgICBjb25zdCBjb25maXJtVG9rZW4gPSByZXNvdXJjZU5hbWUgPz8gdGFyZ2V0O1xuICAgIGNvbnN0IG5vdW4gPSB0YXJnZXQgPT09ICdla3MnID8gKG9wdHMuY2x1c3Rlck5hbWUgPyAnY2x1c3RlciBuYW1lJyA6ICd0YXJnZXQgaWQnKSA6IChvcHRzLnN0YWNrTmFtZSA/ICdzdGFjayBuYW1lJyA6ICd0YXJnZXQgaWQnKTtcbiAgICBjb25zdCB0b2tlbiA9IGF3YWl0IGFzayhgXFxuVGhpcyBpcyBJUlJFVkVSU0lCTEUuIFR5cGUgXCIke2NvbmZpcm1Ub2tlbn1cIiB0byBjb25maXJtIHRlYXJkb3duOiBgKTtcbiAgICBpZiAodG9rZW4gIT09IGNvbmZpcm1Ub2tlbikge1xuICAgICAgcHJpbnRXYXJuaW5nKGBUaGF0IGRpZG4ndCBtYXRjaCwgc28gbm90aGluZyB3YXMgZGVzdHJveWVkIOKAlCB5b3UgaGF2ZSB0byB0eXBlIHRoZSBleGFjdCAke25vdW59IHRvIGNvbmZpcm0uYCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICB9IGVsc2UgaWYgKCFkZXN0cnVjdGl2ZSAmJiAhb3B0cy5mb3JjZSAmJiAhKGF3YWl0IGNvbmZpcm0oJ1xcblByb2NlZWQgd2l0aCB0ZWFyZG93biAoc3RvcHMgdGhlIHN0YWNrOyBvbi1kaXNrIGRhdGEgcGVyc2lzdHMpPycsIG9wdHMueWVzID8/IGZhbHNlKSkpIHtcbiAgICBwcmludFdhcm5pbmcoJ05vIHByb2JsZW0g4oCUIG5vdGhpbmcgd2FzIGNoYW5nZWQuJyk7XG4gICAgcmV0dXJuO1xuICB9XG4gIHByaW50U2VjdGlvbignVGVhcmluZyBkb3duJyk7XG4gIGNvbnN0IHsgY29kZTogZG93bkNvZGUgfSA9IGF3YWl0IHJ1blNjcmlwdChkb3duQ29tbWFuZCwgY3dkLCB7IGNhcHR1cmU6IGZhbHNlIH0pO1xuICBpZiAoZG93bkNvZGUgIT09IDApIHtcbiAgICBwcmludEVycm9yKGBcXG5UZWFyZG93biBmYWlsZWQgKGV4aXQgJHtkb3duQ29kZX0pLiBJbnNwZWN0IHRoZSBzdGFjay9jb250YWluZXJzIGFuZCByZXRyeS5gKTtcbiAgICBwcm9jZXNzLmV4aXRDb2RlID0gMTtcbiAgICByZXR1cm47XG4gIH1cbiAgcHJpbnRTdWNjZXNzKCdUZWFyZG93biBjb21wbGV0ZS4nKTtcbn1cblxuLyoqXG4gKiBSdW4gdGhlIGRlcGxveSBjb21tYW5kIChyZWFsLCBzZWNyZXRzIHVubWFza2VkLCBzdHJlYW1lZCArIGNhcHR1cmVkKSB3aXRoIGFcbiAqIGJvdW5kZWQgYXV0by1maXggKyByZXRyeSBsb29wIOKAlCB0aGUgZGVwbG95IHNjcmlwdHMgYXJlIGlkZW1wb3RlbnQsIHNvIGEgZml4ZWRcbiAqIHJlLXJ1biByZXN1bWVzIHJhdGhlciB0aGFuIHN0YXJ0aW5nIG92ZXIuIFJldHVybnMgd2hldGhlciBpdCBzdWNjZWVkZWQgcGx1cyB0aGVcbiAqIChwb3NzaWJseSBmaXgtYWRqdXN0ZWQpIHBhcmFtcywgd2hpY2ggdGhlIGNhbGxlciBuZWVkcyBmb3IgU0VTIGd1aWRhbmNlLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcnVuRGVwbG95V2l0aFJldHJ5KFxuICBzcGVjOiBUYXJnZXRTcGVjLFxuICB1cmw6IHN0cmluZyB8IG51bGwsXG4gIGN3ZDogc3RyaW5nLFxuICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICBhaU9wdHM6IHsgcHJvdmlkZXI/OiBzdHJpbmc7IG1vZGVsPzogc3RyaW5nIH0sXG4gIG9wdHM6IHsgcmV0cmllcz86IHN0cmluZzsgeWVzPzogYm9vbGVhbiB9LFxuKTogUHJvbWlzZTx7IHN1Y2NlZWRlZDogYm9vbGVhbjsgcnVuUGFyYW1zOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PiB7XG4gIC8vIEJvdW5kIHJldHJpZXMgc28gYW4gdW5hdHRlbmRlZCBgLS15ZXNgIHJ1biBjYW4ndCBhdXRvLXJlLXJ1biBhIDE14oCTMzAgbWluXG4gIC8vIGRlcGxveSBhbiB1bmJvdW5kZWQgbnVtYmVyIG9mIHRpbWVzIG9uIGEgcGVyc2lzdGVudGx5LXJldHJ5YWJsZSBzaWduYXR1cmUuXG4gIGNvbnN0IE1BWF9SRVRSSUVTID0gNTtcbiAgY29uc3QgcmVxdWVzdGVkUmV0cmllcyA9IE1hdGgubWF4KDAsIHBhcnNlSW50KG9wdHMucmV0cmllcyA/PyAnJywgMTApIHx8IDApO1xuICBjb25zdCBtYXhBdHRlbXB0cyA9IDEgKyBNYXRoLm1pbihyZXF1ZXN0ZWRSZXRyaWVzLCBNQVhfUkVUUklFUyk7XG4gIGxldCBydW5QYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0gcGFyYW1zO1xuICBsZXQgc3VjY2VlZGVkID0gZmFsc2U7XG4gIGZvciAobGV0IGF0dGVtcHQgPSAxOyBhdHRlbXB0IDw9IG1heEF0dGVtcHRzOyBhdHRlbXB0KyspIHtcbiAgICBjb25zdCByZWFsQ29tbWFuZCA9IGFzc2VtYmxlQ29tbWFuZChzcGVjLCBydW5QYXJhbXMsIHsgbWFzazogZmFsc2UgfSkuY29tbWFuZDtcbiAgICBwcmludFNlY3Rpb24oYXR0ZW1wdCA9PT0gMSA/ICdEZXBsb3lpbmcnIDogYERlcGxveWluZyDigJQgcmV0cnkgJHthdHRlbXB0IC0gMX1gKTtcbiAgICBpZiAoYXR0ZW1wdCA9PT0gMSkge1xuICAgICAgcHJpbnRJbmZvKGBEZXBsb3lpbmcgJHtzcGVjLmxhYmVsfSDihpIgJHt1cmx9YCk7XG4gICAgICBwcmludEluZm8oc3BlYy5kZXBsb3lzKTtcbiAgICAgIHByaW50SW5mbygnU3RyZWFtaW5nIHRoZSBkZXBsb3kgYmVsb3cg4oCUIGZpcnN0IHJ1biBpcyB0aGUgc2xvdyBvbmUgKGltYWdlIHB1bGxzICsgY2VydCBnZW5lcmF0aW9uKS4nKTtcbiAgICB9XG4gICAgY29uc3QgeyBjb2RlLCB0YWlsIH0gPSBhd2FpdCBydW5TY3JpcHQocmVhbENvbW1hbmQsIGN3ZCwgeyBjYXB0dXJlOiB0cnVlIH0pO1xuICAgIGlmIChjb2RlID09PSAwKSB7IHN1Y2NlZWRlZCA9IHRydWU7IGJyZWFrOyB9XG5cbiAgICBwcmludEVycm9yKGBcXG5EZXBsb3kgZmFpbGVkIChleGl0ICR7Y29kZX0pLmApO1xuICAgIGNvbnN0IGlzc3VlcyA9IG1hdGNoSXNzdWVzKHRhaWwpO1xuICAgIGlmIChpc3N1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgcHJpbnRTZWN0aW9uKCdMaWtlbHkgY2F1c2UnKTtcbiAgICAgIGZvciAoY29uc3QgaSBvZiBpc3N1ZXMpIHByaW50SW5mbyhg4oCiICR7aS5jYXVzZX1cXG4gIOKGkiAke2kuc3VnZ2VzdGlvbn1gKTtcbiAgICB9XG4gICAgaWYgKGlzQWlDb25maWd1cmVkKGFpT3B0cykpIHtcbiAgICAgIGNvbnN0IGRpYWdub3NpcyA9IGF3YWl0IGRpYWdub3NlRmFpbHVyZShyZWRhY3RTZWNyZXRzKHRhaWwsIHNwZWMsIHJ1blBhcmFtcyksIGFpT3B0cyk7XG4gICAgICBpZiAoZGlhZ25vc2lzKSB7IHByaW50U2VjdGlvbignRGlhZ25vc2lzJyk7IHByaW50SW5mbyhkaWFnbm9zaXMpOyB9XG4gICAgfVxuICAgIGlmIChhdHRlbXB0ID49IG1heEF0dGVtcHRzKSBicmVhaztcblxuICAgIC8vIEF1dG8tZml4OiBhIHJldHJ5YWJsZSBpc3N1ZSB3aXRoIGEgcGFyYW0gd2UgaGF2ZW4ndCBhbHJlYWR5IGFwcGxpZWQuXG4gICAgY29uc3QgZml4ID0gaXNzdWVzLmZpbmQoKGkpID0+IGkucmV0cnlhYmxlICYmIGkucGFyYW1GaXggJiYgcnVuUGFyYW1zW2kucGFyYW1GaXgua2V5XSAhPT0gaS5wYXJhbUZpeC52YWx1ZSk7XG4gICAgaWYgKGZpeD8ucGFyYW1GaXggJiYgYXdhaXQgY29uZmlybShgXFxuQXBwbHkgZml4IGFuZCByZXRyeSDigJQgJHtmaXguc3VnZ2VzdGlvbn1gLCBvcHRzLnllcyA/PyBmYWxzZSkpIHtcbiAgICAgIHJ1blBhcmFtcyA9IHsgLi4ucnVuUGFyYW1zLCBbZml4LnBhcmFtRml4LmtleV06IGZpeC5wYXJhbUZpeC52YWx1ZSB9O1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIC8vIFJldHJ5YWJsZSBidXQgbm8gcGFyYW0gY2hhbmdlIChlLmcuIEFDTSBETlMgcHJvcGFnYXRpb24pIOKAlCBvZmZlciBhIHBsYWluIHJlLXJ1bi5cbiAgICBpZiAoIWZpeCAmJiBpc3N1ZXMuc29tZSgoaSkgPT4gaS5yZXRyeWFibGUpICYmIGF3YWl0IGNvbmZpcm0oJ1xcblJldHJ5IHRoZSBkZXBsb3kgKHNhZmUgdG8gcmUtcnVuIOKAlCBpdCByZXN1bWVzIHdoZXJlIGl0IGxlZnQgb2ZmKT8nLCBvcHRzLnllcyA/PyBmYWxzZSkpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBicmVhaztcbiAgfVxuICByZXR1cm4geyBzdWNjZWVkZWQsIHJ1blBhcmFtcyB9O1xufVxuXG4vKipcbiAqIEV4ZWN1dGUgdGhlIHBvc3QtaW5zdGFsbCBzdGVwcyAocmVnaXN0ZXIg4oaSIGxvYWRzIOKGkiBzbW9rZSDihpIgZXZlbnRzIOKGkiBjdXN0b20pLiBPblxuICogRUMyL0VLUyB0aGUgcmVnaXN0ZXIgc3RlcCArIHRoZSBldmVudHMgYnVuZGxlIChzdG9yZS10b2tlbiDihpIgc2V0dXAtZXZlbnRzKSBhcmVcbiAqIFNVUkZBQ0VEIGFzIG9yZGVyZWQgbWFudWFsIG5leHQtc3RlcHMgaW5zdGVhZCBvZiBhdXRvLXJ1bjogZWMyIHJlZ2lzdGVyIG11c3QgcnVuXG4gKiBPTiB0aGUgYm94ICh0aGUgbWluaWt1YmUgdXNlciBvd25zIHRoZSBjbHVzdGVyKSwgYW5kIHRoZSBldmVudHMgYnVuZGxlIG5lZWRzIGFcbiAqIHJlZ2lzdGVyZWQgcGxhdGZvcm0gKyBBV1MgY3JlZHMuIChla3MgcmVnaXN0ZXIgaXRzZWxmIHBvcnQtZm9yd2FyZHMsIHNvIGl0IHJ1bnNcbiAqIGZyb20gYW55d2hlcmUgd2l0aCBrdWJlY3RsIGFjY2Vzcy4pIFNldHMgZXhpdENvZGUgb24gYSBzdGVwIGZhaWx1cmUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBydW5Qb3N0U3RlcHMoXG4gIHBvc3RTdGVwczogUG9zdFN0ZXBbXSxcbiAgc2tpcHBlZFN0ZXBzOiBTa2lwcGVkU3RlcFtdLFxuICB0YXJnZXQ6IFRhcmdldElkLFxuICBjd2Q6IHN0cmluZyxcbiAgYWRtaW5FbnY6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sXG4gIG9wdHM6IHsgeWVzPzogYm9vbGVhbjsgYXV0b1J1bj86IGJvb2xlYW47IGF1dG9Jbml0PzogYm9vbGVhbiB9LFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIEFXUyB0YXJnZXRzIHdpdGggYC0taW5pdCBhdXRvYDogdGhlIGRlcGxveSBzZWxmLXJ1bnMgaW5pdC1wbGF0Zm9ybSwgc28gdGhlcmUncyBub1xuICAvLyByZWdpc3Rlci9sb2FkcyBzdGVwIHRvIHN1cmZhY2UgaGVyZSDigJQgY29uZmlybSBpdCBhbmQgcG9pbnQgYXQgd2hlcmUgdG8gd2F0Y2guXG4gIGlmIChvcHRzLmF1dG9Jbml0ICYmIHRhcmdldCA9PT0gJ2VjMicpIHtcbiAgICBwcmludEluZm8oJ1xcbuKckyBJbml0IG1vZGU6IGF1dG8g4oCUIHRoZSBpbnN0YW5jZSBydW5zIGluaXQtcGxhdGZvcm0gaXRzZWxmIG9uIGZpcnN0IGJvb3QnKTtcbiAgICBwcmludEluZm8oJyAgKHJlZ2lzdGVyIGFkbWluICsgYnVpbGQgYm9vdHN0cmFwIGltYWdlICsgbG9hZCBwbHVnaW5zL2NvbXBsaWFuY2Uvc2FtcGxlcykuJyk7XG4gICAgcHJpbnRJbmZvKCcgIEl0IHRha2VzIH4zMC02MCBtaW47IHdhdGNoIHByb2dyZXNzOicpO1xuICAgIHByaW50SW5mbygnICAgIGF3cyBzc20gc3RhcnQtc2Vzc2lvbiAtLXRhcmdldCA8SW5zdGFuY2VJZD4gICAjIEluc3RhbmNlSWQgc3RhY2sgb3V0cHV0Jyk7XG4gICAgcHJpbnRJbmZvKCcgICAgc3VkbyB0YWlsIC1mIC92YXIvbG9nL3VzZXItZGF0YS5sb2cnKTtcbiAgfSBlbHNlIGlmIChvcHRzLmF1dG9Jbml0ICYmIHRhcmdldCA9PT0gJ2VrcycpIHtcbiAgICBwcmludEluZm8oJ1xcbuKckyBJbml0IG1vZGU6IGF1dG8g4oCUIHNldHVwLnNoIHJ1bnMgaW5pdC1wbGF0Zm9ybSBpbiBpdHMgZmluYWwgcGhhc2UnKTtcbiAgICBwcmludEluZm8oJyAgKHJlZ2lzdGVyIGFkbWluICsgYnVpbGQgYm9vdHN0cmFwIGltYWdlICsgbG9hZCBwbHVnaW5zL2NvbXBsaWFuY2Uvc2FtcGxlcywnKTtcbiAgICBwcmludEluZm8oJyAgb3ZlciBhIGt1YmVjdGwgcG9ydC1mb3J3YXJkKS4gUGFzcyAtLWluaXQgbWFudWFsIHRvIHJ1biBpdCB5b3Vyc2VsZiBpbnN0ZWFkLicpO1xuICB9XG4gIGlmIChwb3N0U3RlcHMubGVuZ3RoID09PSAwKSByZXR1cm47XG4gIGZvciAoY29uc3QgcyBvZiBza2lwcGVkU3RlcHMpIHByaW50V2FybmluZyhgU2tpcHBlZCBwb3N0LXN0ZXAgJHtzLmlkfTogJHtzLnJlYXNvbn1gKTtcbiAgY29uc3QgaXNSZW1vdGUgPSAoaWQ6IHN0cmluZyk6IGJvb2xlYW4gPT5cbiAgICAodGFyZ2V0ID09PSAnZWMyJyB8fCB0YXJnZXQgPT09ICdla3MnKSAmJiAoaWQgPT09ICdyZWdpc3RlcicgfHwgaWQuc3RhcnRzV2l0aCgnc3RvcmUtdG9rZW4nKSB8fCBpZCA9PT0gJ2V2ZW50cycpO1xuICBjb25zdCBydW5uYWJsZSA9IHBvc3RTdGVwcy5maWx0ZXIoKHMpID0+ICFpc1JlbW90ZShzLmlkKSk7XG4gIGNvbnN0IHJlbW90ZSA9IHBvc3RTdGVwcy5maWx0ZXIoKHMpID0+IGlzUmVtb3RlKHMuaWQpKTtcbiAgaWYgKHJlbW90ZS5sZW5ndGggPiAwKSB7XG4gICAgaWYgKHRhcmdldCA9PT0gJ2VjMicpIHtcbiAgICAgIC8vIEVDMiBydW5zIHRoZSBwbGF0Zm9ybSBhcyBhIGRlZGljYXRlZCBgbWluaWt1YmVgIHVzZXIgaW5zaWRlIHRoZSBpbnN0YW5jZTogdGhlXG4gICAgICAvLyBjbHVzdGVyLCBpdHMga3ViZWNvbmZpZywgYW5kIHRoZSBqd3Qtc2VjcmV0IGFsbCBiZWxvbmcgdG8gdGhhdCB1c2VyLCBhbmQgdGhlXG4gICAgICAvLyByZWdpc3RlciBzdGVwIGJ1aWxkcytwdXNoZXMgaW1hZ2VzIGFnYWluc3QgaXQuIFNvIGl0IE1VU1QgcnVuIG9uIHRoZSBib3ggYXNcbiAgICAgIC8vIGBtaW5pa3ViZWAg4oCUIHByaW50IHRoZSBleGFjdCBsYW5kaW5nIHNlcXVlbmNlIHJhdGhlciB0aGFuIGEgYmFyZSBjb21tYW5kLlxuICAgICAgcHJpbnRJbmZvKCdcXG5OZXh0IOKAlCBmaW5pc2ggc2V0dXAgT04gdGhlIGluc3RhbmNlIChpdCBidWlsZHMgaW1hZ2VzICsgcmVhZHMgdGhlIGNsdXN0ZXIsIHNvIGl0Jyk7XG4gICAgICBwcmludEluZm8oJ2Nhbm5vdCBydW4gZnJvbSBoZXJlKS4gTGFuZCBvbiB0aGUgYm94LCBiZWNvbWUgdGhlIGBtaW5pa3ViZWAgdXNlciwgdGhlbiBydW4gaXQ6Jyk7XG4gICAgICBwcmludEluZm8oJyAgYXdzIHNzbSBzdGFydC1zZXNzaW9uIC0tdGFyZ2V0IDxJbnN0YW5jZUlkPiAgICAgICMgSW5zdGFuY2VJZCBzdGFjayBvdXRwdXQnKTtcbiAgICAgIHByaW50SW5mbygnICBzdWRvIC1pdSBtaW5pa3ViZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBvd25zIHRoZSBjbHVzdGVyICsgZG9ja2VyJyk7XG4gICAgICBwcmludEluZm8oJyAgY2QgL29wdC9waXBlbGluZS9waXBlbGluZS1idWlsZGVyICAgICAgICAgICAgICAgICMgdGhlIGRlcGxveWVkIGNoZWNrb3V0Jyk7XG4gICAgICBmb3IgKGNvbnN0IHMgb2YgcmVtb3RlKSBwcmludEluZm8oYCAgJHtzLmNvbW1hbmR9YCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGVrczogaW5pdC1wbGF0Zm9ybSBwb3J0LWZvcndhcmRzIHRvIHN2Yy9uZ2lueCwgc28gcmVnaXN0ZXIgcnVucyBmcm9tIGFueXdoZXJlIHdpdGhcbiAgICAgIC8vIGt1YmVjdGwgYWNjZXNzIOKAlCBubyBpbi1WUEMgcmVxdWlyZW1lbnQgYW5kIG5vIHdhaXRpbmcgb24gdGhlIEFMQi9ETlMuIChBIC0td2l0aC1ldmVudHNcbiAgICAgIC8vIGJ1bmRsZSwgaWYgcHJlc2VudCwgYWRkaXRpb25hbGx5IG5lZWRzIEFXUyBjcmVkcyArIGEgcmVnaXN0ZXJlZCBwbGF0Zm9ybS4pXG4gICAgICBwcmludEluZm8oJ1xcbk5leHQg4oCUIHJ1biB3aXRoIGt1YmVjdGwgYWNjZXNzIHRvIHRoZSBjbHVzdGVyIChpbiB0aGlzIG9yZGVyKTonKTtcbiAgICAgIGZvciAoY29uc3QgcyBvZiByZW1vdGUpIHByaW50SW5mbyhgICDigKIgJHtzLmxhYmVsfSDigJQgJHtzLmNvbW1hbmR9YCk7XG4gICAgfVxuICB9XG4gIGlmIChydW5uYWJsZS5sZW5ndGggPT09IDApIHJldHVybjtcbiAgLy8gV2hlbiB0aGUgbG9hZHMgd2VyZSBwaWNrZWQgaW50ZXJhY3RpdmVseSAoYXV0b1J1biksIHRoZSB1c2VyIGFscmVhZHkgb3B0ZWQgaW4g4oCUIHNraXBcbiAgLy8gdGhlIHJlZHVuZGFudCBzZWNvbmQgY29uZmlybS4gRmxhZy1kcml2ZW4gLyBub24taW50ZXJhY3RpdmUgcnVucyBzdGlsbCBnYXRlIGhlcmUuXG4gIGlmIChvcHRzLmF1dG9SdW4gfHwgYXdhaXQgY29uZmlybShgXFxuUnVuICR7cnVubmFibGUubGVuZ3RofSBwb3N0LWluc3RhbGwgc3RlcChzKSBub3c/YCwgb3B0cy55ZXMgPz8gZmFsc2UpKSB7XG4gICAgaWYgKG9wdHMuYXV0b1J1bikgcHJpbnRJbmZvKGBcXG5SdW5uaW5nIHRoZSAke3J1bm5hYmxlLmxlbmd0aH0gcG9zdC1pbnN0YWxsIHN0ZXAocykgeW91IHNlbGVjdGVk4oCmYCk7XG4gICAgZm9yIChjb25zdCBzIG9mIHJ1bm5hYmxlKSB7XG4gICAgICBwcmludFNlY3Rpb24oYFBvc3Qtc3RlcDogJHtzLmxhYmVsfWApO1xuICAgICAgLy8gcmVnaXN0ZXIgKGxvY2FsL21pbmlrdWJlKSBsb2dzIGludG8gdGhlIHBsYXRmb3JtLCBzbyBpdCBuZWVkcyB0aGUgYWRtaW4gY3JlZHNcbiAgICAgIC8vIChQTEFURk9STV9JREVOVElGSUVSIC8gUExBVEZPUk1fUEFTU1dPUkQpIG9uIHRvcCBvZiBpdHMgc3RlcCBlbnYuXG4gICAgICBjb25zdCBuZWVkc0NyZWRzID0gcy5pZCA9PT0gJ3JlZ2lzdGVyJyB8fCBzLmlkLnN0YXJ0c1dpdGgoJ3N0b3JlLXRva2VuJyk7XG4gICAgICBjb25zdCBlbnYgPSBuZWVkc0NyZWRzID8geyAuLi5zLmVudiwgLi4uYWRtaW5FbnYgfSA6IHMuZW52O1xuICAgICAgLy8gTG9hZHMvc21va2UgYXJlIG5vaXN5ICsgbm9uLWludGVyYWN0aXZlIOKGkiBydW4gUVVJRVQgKGNhcHR1cmUsIHNob3cgb25seSBvblxuICAgICAgLy8gZmFpbHVyZSkuIEV4Y2VwdGlvbjogYSByZWdpc3RlciB3aXRoIG5vIGFkbWluIGNyZWRzIHByb21wdHMsIHNvIGl0IG11c3Qgc3RyZWFtLlxuICAgICAgY29uc3QgaW50ZXJhY3RpdmVSZWdpc3RlciA9IHMuaWQgPT09ICdyZWdpc3RlcicgJiYgIWFkbWluRW52LlBMQVRGT1JNX0lERU5USUZJRVI7XG4gICAgICBjb25zdCBxdWlldCA9ICFpbnRlcmFjdGl2ZVJlZ2lzdGVyO1xuICAgICAgaWYgKHF1aWV0KSBwcmludEluZm8oYCR7cy5jb21tYW5kfVxcbiAg4oCmcnVubmluZyAob3V0cHV0IHNob3duIG9ubHkgaWYgaXQgZmFpbHMpYCk7XG4gICAgICBlbHNlIHByaW50SW5mbyhzLmNvbW1hbmQpO1xuICAgICAgY29uc3QgeyBjb2RlLCB0YWlsIH0gPSBhd2FpdCBydW5TY3JpcHQocy5jb21tYW5kLCBjd2QsIHsgcXVpZXQsIGNhcHR1cmU6IGZhbHNlLCBlbnYgfSk7XG4gICAgICBpZiAoY29kZSAhPT0gMCkge1xuICAgICAgICBpZiAocXVpZXQgJiYgdGFpbCkgeyBwcmludEVycm9yKCdcXG4tLS0gbGFzdCBvdXRwdXQgLS0tJyk7IHByaW50SW5mbyh0YWlsKTsgfVxuICAgICAgICBwcmludEVycm9yKGBcXG5Qb3N0LXN0ZXAgJyR7cy5pZH0nIGZhaWxlZCAoZXhpdCAke2NvZGV9KS4gVGhlIHBsYXRmb3JtIGlzIGRlcGxveWVkOyBmaXggYW5kIHJlLXJ1biB0aGUgc3RlcCBtYW51YWxseS5gKTtcbiAgICAgICAgcHJvY2Vzcy5leGl0Q29kZSA9IDE7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgcHJpbnRTdWNjZXNzKGAke3MuaWR9IOKck2ApO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICBwcmludEluZm8oJ1xcblNraXBwZWQgcG9zdC1pbnN0YWxsIHN0ZXBzLiBSdW4gdGhlIGNvbW1hbmRzIGFib3ZlIG1hbnVhbGx5IHdoZW4gcmVhZHkuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBTcGFyc2UtY2xvbmUgdGhlIHBsYXRmb3JtIHJlcG8gKHdoZW4gLS1yZXBvLCBvciBhY2NlcHRlZCB2aWEgdGhlIGludGVyYWN0aXZlXG4gKiBvZmZlciksIHJlcG9pbnQgY3dkIGludG8gaXQsIGFuZCB2ZXJpZnkgdGhlIGRlcGxveSBlbnRyeXBvaW50IGV4aXN0cy4gT3ducyB0aGVcbiAqIGN3ZC9ib290c3RyYXBwZWQgc3RhdGUuIFNldHMgZXhpdENvZGUgb24gYSBoYXJkIGZhaWx1cmUgKGNsb25lIGVycm9yIC8gbWlzc2luZ1xuICogZW50cnlwb2ludCksIG1hdGNoaW5nIHRoZSBvbGQgaW5saW5lIGJlaGF2aW9yOyBhIHBsYWluIGRlY2xpbmUgbGVhdmVzIGV4aXRDb2RlXG4gKiB1bnRvdWNoZWQuIFJldHVybnMgb2s6ZmFsc2Ugd2hlbiB0aGUgY2FsbGVyIG11c3QgYWJvcnQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBib290c3RyYXBBbmRMb2NhdGUoXG4gIHNwZWM6IFRhcmdldFNwZWMsXG4gIGJvb3RzdHJhcDogQm9vdHN0cmFwU3BlYyxcbiAgYm9vdHN0cmFwQ21kOiBzdHJpbmcgfCBudWxsLFxuICBzcGFyc2VQYXRoczogcmVhZG9ubHkgc3RyaW5nW10sXG4gIHN0YXJ0Q3dkOiBzdHJpbmcsXG4gIG9wdHM6IHsgeWVzPzogYm9vbGVhbiB9LFxuKTogUHJvbWlzZTx7IGN3ZDogc3RyaW5nOyBib290c3RyYXBwZWQ6IGJvb2xlYW47IG9rOiBib29sZWFuIH0+IHtcbiAgbGV0IGN3ZCA9IHN0YXJ0Q3dkO1xuICBsZXQgYm9vdHN0cmFwcGVkID0gZmFsc2U7XG4gIC8vIFNoYXJlZCBieSBgLS1yZXBvYCBhbmQgdGhlIGludGVyYWN0aXZlIG9mZmVyLiBSZXBvaW50cyBjd2QgKyBzZXRzIGJvb3RzdHJhcHBlZDtcbiAgLy8gYGJvb3RzdHJhcHBlZGAgcmVjb3JkcyB3aGV0aGVyIHdlIGNyZWF0ZWQgdGhlIGNsb25lICh2cy4gYW4gZXhpc3RpbmcgY2hlY2tvdXQpIOKAlFxuICAvLyBvbmx5IHRoZW4gY2FuIHRoZSBjYWxsZXIgYWRkaXRpdmVseSBzcGFyc2UtZmV0Y2ggbG9hZCBmb2xkZXJzLlxuICBjb25zdCBib290c3RyYXBDbG9uZSA9IGFzeW5jICgpOiBQcm9taXNlPGJvb2xlYW4+ID0+IHtcbiAgICBjb25zdCBjbWQgPSBib290c3RyYXBDb21tYW5kKGJvb3RzdHJhcCk7XG4gICAgcHJpbnRTZWN0aW9uKCdCb290c3RyYXAnKTtcbiAgICBwcmludEluZm8oY21kKTtcbiAgICBjb25zdCB7IGNvZGUgfSA9IGF3YWl0IHJ1blNjcmlwdChjbWQsIGN3ZCwgeyBjYXB0dXJlOiBmYWxzZSB9KTtcbiAgICBpZiAoY29kZSAhPT0gMCkge1xuICAgICAgcHJpbnRFcnJvcihgXFxuVGhlIGNsb25lIGRpZG4ndCBmaW5pc2ggKGV4aXQgJHtjb2RlfSkuIERvdWJsZS1jaGVjayB0aGUgcmVwbyBVUkwgLyByZWYgKG9yIHlvdXIgbmV0d29yaykgYW5kIGdpdmUgaXQgYW5vdGhlciBnby5gKTtcbiAgICAgIHByb2Nlc3MuZXhpdENvZGUgPSAxO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBjd2QgPSBwYXRoLnJlc29sdmUoY3dkLCBib290c3RyYXAud29ya2Rpcik7XG4gICAgYm9vdHN0cmFwcGVkID0gdHJ1ZTtcbiAgICBwcmludFN1Y2Nlc3MoYFJlcG8gaXMgcmVhZHkg4oCUIGNvbnRpbnVpbmcgZnJvbSAke2N3ZH1gKTtcbiAgICBjb25zdCBhYnNlbnQgPSBzcGFyc2VQYXRocy5maWx0ZXIoKHApID0+ICFleGlzdHNTeW5jKHBhdGguam9pbihjd2QsIHApKSk7XG4gICAgaWYgKGFic2VudC5sZW5ndGggPiAwKSB7XG4gICAgICBwcmludEVycm9yKGBcXG5UaGUgc3BhcnNlIGNsb25lIGlzIG1pc3NpbmcgJHthYnNlbnQuam9pbignLCAnKX0g4oCUIHRoYXQgcmVmIG1heSBub3QgaGF2ZSB0aG9zZSBmb2xkZXJzLiBUcnkgYSBkaWZmZXJlbnQgLS1yZWYuYCk7XG4gICAgICBwcm9jZXNzLmV4aXRDb2RlID0gMTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG4gIH07XG5cbiAgLy8gYC0tcmVwb2A6IGNsb25lIHVwIGZyb250IChtdXN0IHByZWNlZGUgdGhlIGVudHJ5cG9pbnQgY2hlY2spLlxuICBpZiAoYm9vdHN0cmFwQ21kKSB7XG4gICAgcHJpbnRJbmZvKGBcXG5Cb290c3RyYXAg4oaSIGNsb25lICR7Ym9vdHN0cmFwLnJlcG99IEAgJHtib290c3RyYXAucmVmfSBpbnRvICR7Ym9vdHN0cmFwLndvcmtkaXJ9IChzcGFyc2UpLmApO1xuICAgIGlmICghKGF3YWl0IGNvbmZpcm0oJ0dvIGFoZWFkIHdpdGggdGhlIGNsb25lPycsIG9wdHMueWVzID8/IGZhbHNlKSkpIHtcbiAgICAgIHByaW50V2FybmluZygnTm8gcHJvYmxlbSDigJQgbm90aGluZyB3YXMgY2hhbmdlZC4gUmUtcnVuIHdoZW5ldmVyIHlvdVxcJ3JlIHJlYWR5LicpO1xuICAgICAgcmV0dXJuIHsgY3dkLCBib290c3RyYXBwZWQsIG9rOiBmYWxzZSB9O1xuICAgIH1cbiAgICBpZiAoIShhd2FpdCBib290c3RyYXBDbG9uZSgpKSkgcmV0dXJuIHsgY3dkLCBib290c3RyYXBwZWQsIG9rOiBmYWxzZSB9O1xuICB9XG5cbiAgLy8gVGhlIGRlcGxveSBlbnRyeXBvaW50IG11c3QgZXhpc3QgcmVsYXRpdmUgdG8gY3dkLiBJZiBpdCBkb2Vzbid0IGFuZCB3ZSBoYXZlbid0XG4gIC8vIGJvb3RzdHJhcHBlZCwgZ3JhY2VmdWxseSBPRkZFUiB0byBjbG9uZSAoaW50ZXJhY3RpdmUgb25seSkgc28gYSBmcmVzaCBtYWNoaW5lIGNhblxuICAvLyBwcm9jZWVkIHdpdGhvdXQgcmUtcnVubmluZyB3aXRoIC0tcmVwby5cbiAgaWYgKCFlbnRyeXBvaW50RXhpc3RzKHNwZWMsIGN3ZCkpIHtcbiAgICBjb25zdCBpbnRlcmFjdGl2ZSA9IEJvb2xlYW4ocHJvY2Vzcy5zdGRpbi5pc1RUWSkgJiYgIW9wdHMueWVzO1xuICAgIGlmICghYm9vdHN0cmFwQ21kICYmIGludGVyYWN0aXZlICYmIGdpdEF2YWlsYWJsZSgpKSB7XG4gICAgICBwcmludEluZm8oYFxcbkxvb2tzIGxpa2UgeW91J3JlIG5vdCBpbnNpZGUgYSBwaXBlbGluZS1idWlsZGVyIGNoZWNrb3V0LCBzbyB0aGUgJHtzcGVjLmxhYmVsfSBkZXBsb3kgc2NyaXB0cyBhcmVuJ3QgaGVyZSB5ZXQuYCk7XG4gICAgICBpZiAoYXdhaXQgY29uZmlybShgV2FudCBtZSB0byBzcGFyc2UtY2xvbmUgdGhlbSBpbnRvICR7Ym9vdHN0cmFwLndvcmtkaXJ9IGFuZCBrZWVwIGdvaW5nP2AsIGZhbHNlKSkge1xuICAgICAgICBpZiAoIShhd2FpdCBib290c3RyYXBDbG9uZSgpKSkgcmV0dXJuIHsgY3dkLCBib290c3RyYXBwZWQsIG9rOiBmYWxzZSB9O1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoIWVudHJ5cG9pbnRFeGlzdHMoc3BlYywgY3dkKSkge1xuICAgICAgcHJpbnRXYXJuaW5nKGBcXG5JIGNvdWxkbid0IGZpbmQgJHtzcGVjLmRpcn0vJHtzcGVjLmVudHJ5cG9pbnR9IGZyb20gJHtjd2R9LmApO1xuICAgICAgcHJpbnRJbmZvKCdUd28gZWFzeSB3YXlzIGZvcndhcmQ6Jyk7XG4gICAgICBwcmludEluZm8oJyAg4oCiIGNkIGludG8geW91ciBwaXBlbGluZS1idWlsZGVyIGNoZWNrb3V0IGFuZCByZS1ydW4sIG9yJyk7XG4gICAgICBwcmludEluZm8oYCAg4oCiIGFkZCAtLXJlcG8gYW5kIEknbGwgYm9vdHN0cmFwIGEgZnJlc2ggc3BhcnNlIGNsb25lIGZvciB5b3Uke2dpdEF2YWlsYWJsZSgpID8gJy4nIDogJyAob25jZSBnaXQgaXMgaW5zdGFsbGVkKS4nfWApO1xuICAgICAgcHJvY2Vzcy5leGl0Q29kZSA9IDE7XG4gICAgICByZXR1cm4geyBjd2QsIGJvb3RzdHJhcHBlZCwgb2s6IGZhbHNlIH07XG4gICAgfVxuICB9XG4gIHJldHVybiB7IGN3ZCwgYm9vdHN0cmFwcGVkLCBvazogdHJ1ZSB9O1xufVxuXG4vKipcbiAqIEludGVyYWN0aXZlIG9wdC1pbiBsb2Fkcywgb2ZmZXJlZCBBRlRFUiB0aGUgY2xvbmUgKHNvIHdlIG9ubHkgYXNrIG9uY2UgdGhlIG9wZXJhdG9yXG4gKiBoYXMgYWdyZWVkIHRvIHByb2NlZWQpLiBQcm9tcHRzIHBlciBsb2FkLCBhZGRpdGl2ZWx5IHNwYXJzZS1mZXRjaGVzIHRoZSBwaWNrZWRcbiAqIGZvbGRlcnMgaW50byBhIGJvb3RzdHJhcHBlZCBzcGFyc2UgY2xvbmUsIHRoZW4gcmUtcmVzb2x2ZXMgdGhlIHBvc3QtaW5zdGFsbCBzdGVwc1xuICogd2l0aCB0aGUgc2VsZWN0aW9ucy4gUmV0dXJucyB0aGUgY2hvc2VuIGlkcyArIHRoZSByZWNvbXB1dGVkIHN0ZXBzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZUxvYWRzSW50ZXJhY3RpdmVseShcbiAgdGFyZ2V0OiBUYXJnZXRJZCxcbiAgdXJsOiBzdHJpbmcgfCBudWxsLFxuICByZWdpb246IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgY3dkOiBzdHJpbmcsXG4gIGJvb3RzdHJhcHBlZDogYm9vbGVhbixcbiAgYm9vdHN0cmFwOiBCb290c3RyYXBTcGVjLFxuICBwb3N0U3RlcEZsYWdzOiB7IGluaXQ6IGJvb2xlYW47IGF1dG9Jbml0OiBib29sZWFuOyBidWlsZEJvb3RzdHJhcDogYm9vbGVhbjsgc21va2VUZXN0OiBib29sZWFuOyBldmVudHM6IGJvb2xlYW47IHN0ZXBzOiBzdHJpbmdbXSB9LFxuKTogUHJvbWlzZTx7IGVuYWJsZWRMb2FkSWRzOiBzdHJpbmdbXTsgc3RlcHM6IFBvc3RTdGVwW107IHNraXBwZWQ6IFNraXBwZWRTdGVwW10gfT4ge1xuICBjb25zdCBwcm9tcHRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgIHBsdWdpbnM6ICdMb2FkIHBsdWdpbnM/JyxcbiAgICBzYW1wbGVzOiAnTG9hZCBzYW1wbGUgcGlwZWxpbmUgdGVtcGxhdGVzPycsXG4gICAgY29tcGxpYW5jZTogJ0xvYWQgY29tcGxpYW5jZSBydWxlcz8nLFxuICB9O1xuICBwcmludFNlY3Rpb24oJ09wdGlvbmFsIHBvc3QtaW5zdGFsbCBsb2FkcycpO1xuICBjb25zdCBjaG9zZW46IHN0cmluZ1tdID0gW107XG4gIGZvciAoY29uc3QgcyBvZiBMT0FEX1NURVBTKSB7XG4gICAgaWYgKGF3YWl0IGNvbmZpcm0ocHJvbXB0c1tzLmlkXSA/PyBgTG9hZCAke3MuaWR9P2AsIGZhbHNlKSkgY2hvc2VuLnB1c2gocy5pZCk7XG4gIH1cbiAgLy8gQWRkaXRpdmUgc3BhcnNlIHJlLXN5bmM6IG1hdGVyaWFsaXplIE9OTFkgdGhlIHBpY2tlZCBsb2FkcycgZm9sZGVycyAodGhlIHBhcnRpYWxcbiAgLy8gY2xvbmUgZmV0Y2hlcyB0aGVpciBibG9icyBvbiBkZW1hbmQpLiBPbmx5IHdoZW4gd2UgY3JlYXRlZCBhIFNQQVJTRSBjbG9uZSDigJQgYVxuICAvLyBub3JtYWwgY2hlY2tvdXQgYWxyZWFkeSBoYXMgZXZlcnkgZm9sZGVyLCBhbmQgdGhlIGdpdDwyLjI3IGZ1bGwtY2xvbmUgZmFsbGJhY2tcbiAgLy8gKGJvb3RzdHJhcC5mdWxsKSBpcyBub24tc3BhcnNlLCB3aGVyZSBgc3BhcnNlLWNoZWNrb3V0IGFkZGAgd291bGQgc3dpdGNoIGl0IHRvXG4gIC8vIGNvbmUgbW9kZSBhbmQgUFJVTkUgdGhlIGFscmVhZHktbWF0ZXJpYWxpemVkIGRlcGxveSBmb2xkZXJzLlxuICBpZiAoYm9vdHN0cmFwcGVkICYmICFib290c3RyYXAuZnVsbCkge1xuICAgIGNvbnN0IGFic2VudCA9IHNwYXJzZVBhdGhzRm9yKHRhcmdldCwgY2hvc2VuKS5maWx0ZXIoKHApID0+ICFleGlzdHNTeW5jKHBhdGguam9pbihjd2QsIHApKSk7XG4gICAgaWYgKGFic2VudC5sZW5ndGggPiAwKSB7XG4gICAgICBwcmludFNlY3Rpb24oJ0ZldGNoaW5nIHNlbGVjdGVkIGxvYWQgZm9sZGVycycpO1xuICAgICAgY29uc3QgYWRkQ21kID0gYGdpdCBzcGFyc2UtY2hlY2tvdXQgYWRkICR7YWJzZW50Lm1hcCgocCkgPT4gYCcke3B9J2ApLmpvaW4oJyAnKX0gJiYgZ2l0IGNoZWNrb3V0ICR7c2hlbGxRdW90ZShib290c3RyYXAucmVmKX1gO1xuICAgICAgcHJpbnRJbmZvKGFkZENtZCk7XG4gICAgICBjb25zdCB7IGNvZGUgfSA9IGF3YWl0IHJ1blNjcmlwdChhZGRDbWQsIGN3ZCwgeyBjYXB0dXJlOiBmYWxzZSB9KTtcbiAgICAgIGlmIChjb2RlICE9PSAwKSB7XG4gICAgICAgIHByaW50V2FybmluZyhgQ291bGRuJ3QgZmV0Y2ggJHthYnNlbnQuam9pbignLCAnKX0g4oCUIHRoZSBtYXRjaGluZyBsb2FkIHN0ZXAocykgbWF5IGZhaWwuIFJlLXJ1biB3aXRoIC0tcmVwbyB0byByZWZyZXNoIHRoZSBjbG9uZS5gKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgY29uc3QgcmVzb2x2ZWQgPSByZXNvbHZlUG9zdFN0ZXBzKHtcbiAgICB0YXJnZXQsXG4gICAgdXJsLFxuICAgIHJlZ2lvbixcbiAgICBlbmFibGVkTG9hZElkczogY2hvc2VuLFxuICAgIC4uLnBvc3RTdGVwRmxhZ3MsXG4gICAgYnVpbGRCb290c3RyYXA6IHBvc3RTdGVwRmxhZ3MuYnVpbGRCb290c3RyYXAgfHwgY2hvc2VuLmluY2x1ZGVzKCdwbHVnaW5zJyksXG4gIH0pO1xuICByZXR1cm4geyBlbmFibGVkTG9hZElkczogY2hvc2VuLCBzdGVwczogcmVzb2x2ZWQuc3RlcHMsIHNraXBwZWQ6IHJlc29sdmVkLnNraXBwZWQgfTtcbn1cblxuLyoqXG4gKiBSZWRhY3Qgc2VjcmV0IHBhcmFtIHZhbHVlcyAoZS5nLiB0aGUgZ2hjciB0b2tlbikgZnJvbSBjYXB0dXJlZCBkZXBsb3kgb3V0cHV0XG4gKiBiZWZvcmUgaXQgaXMgc2VudCB0byB0aGUgTExNIGZvciBkaWFnbm9zaXMuIFRoZSBkZXBsb3kgcnVucyB3aXRoIHNlY3JldHNcbiAqIHVubWFza2VkLCBzbyBhbHRob3VnaCB0aGUgY3VycmVudCBzY3JpcHRzIGRvbid0IGVjaG8gdGhlbSwgdGhpcyBrZWVwcyBhIGZ1dHVyZVxuICogc2NyaXB0J3Mgc3RyYXkgZWNobyAob3IgYSB2ZXJib3NlIHN1Yi10b29sKSBmcm9tIGNyb3NzaW5nIHRoZSB0aGlyZC1wYXJ0eS1MTE1cbiAqIGJvdW5kYXJ5LlxuICovXG5mdW5jdGlvbiByZWRhY3RTZWNyZXRzKHRleHQ6IHN0cmluZywgc3BlYzogVGFyZ2V0U3BlYywgcGFyYW1zOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IHN0cmluZyB7XG4gIGxldCBvdXQgPSB0ZXh0O1xuICBmb3IgKGNvbnN0IGlucHV0IG9mIFsuLi5zcGVjLnJlcXVpcmVkLCAuLi5zcGVjLm9wdGlvbmFsXSkge1xuICAgIGlmICghaW5wdXQuc2VjcmV0KSBjb250aW51ZTtcbiAgICBjb25zdCB2YWx1ZSA9IHBhcmFtc1tpbnB1dC5rZXldO1xuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLmxlbmd0aCA+PSA0KSBvdXQgPSBvdXQuc3BsaXQodmFsdWUpLmpvaW4oJyoqKicpO1xuICB9XG4gIHJldHVybiBvdXQ7XG59XG5cbi8qKlxuICogUmVnaXN0ZXJzIHRoZSBgcHJvdmlzaW9uYCBjb21tYW5kIOKAlCBhbiBBSS1hc3Npc3RlZCBpbnN0YWxsZXIgZm9yIHRoZSBQaXBlbGluZVxuICogQnVpbGRlciBQTEFURk9STSAobG9jYWwvbWluaWt1YmUvRUMyL0VLUykuIEl0IHByaW50cyB0aGUgYXNzZW1ibGVkIHBsYW5cbiAqIChwcmVyZXEgY2hlY2tzLCBOTC1nb2FsIHBhcnNpbmcsIGNvbW1hbmQgYXNzZW1ibHkpIGFuZCB0aGVuIERFUExPWVMsIGdhdGVkIGJ5XG4gKiBjb25maXJtYXRpb24gcHJvbXB0cyAoYC0teWVzYCB0byBhdXRvLWFjY2VwdDsgYC0tanNvbmAgaXMgdGhlIG9ubHkgbm9uLWV4ZWN1dGluZ1xuICogbW9kZSwgcHJpbnRpbmcgdGhlIHBsYW4gZm9yIHRvb2xpbmcpLiBXaXRoIGAtLXJlcG9gIGl0IGNhbiBib290c3RyYXAgYSBmcmVzaFxuICogbWFjaGluZSBieSBzcGFyc2VseSBjbG9uaW5nIG9ubHkgdGhlIGRlcGxveSBmb2xkZXJzIHRoZSBzZWxlY3RlZCB0YXJnZXQgK1xuICogb3B0aW9ucyBuZWVkLCB0aGVuIHJ1bnMgcG9zdC1pbnN0YWxsIHN0ZXBzIChyZWdpc3RlciBhZG1pbiwgb3B0LWluIGxvYWRzLFxuICogc21va2UgdGVzdCwgY3VzdG9tIGNvbW1hbmRzKS4gYC0tdGVhcmRvd25gIHJlbW92ZXMgYSBkZXBsb3ltZW50IGluc3RlYWQuXG4gKlxuICogQHBhcmFtIHByb2dyYW0gLSBUaGUgcm9vdCBDb21tYW5kZXIgcHJvZ3JhbSBpbnN0YW5jZSB0byBhdHRhY2ggdGhlIGNvbW1hbmQgdG8uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwcm92aXNpb24ocHJvZ3JhbTogQ29tbWFuZCk6IHZvaWQge1xuICBwcm9ncmFtXG4gICAgLmNvbW1hbmQoJ3Byb3Zpc2lvbicpXG4gICAgLmRlc2NyaXB0aW9uKCdBSS1hc3Npc3RlZCBpbnN0YWxsZXIgZm9yIHRoZSBwbGF0Zm9ybSAobG9jYWwvbWluaWt1YmUvRUMyL0VLUyk6IGRlcGxveXMgaXQgKGdhdGVkIGJ5IGNvbmZpcm1hdGlvbiBwcm9tcHRzKSwgb3IgLS10ZWFyZG93biB0byByZW1vdmUnKVxuICAgIC5vcHRpb24oJy10LCAtLXRhcmdldCA8dGFyZ2V0PicsIGBEZXBsb3kgdGFyZ2V0OiAke1RBUkdFVF9JRFMuam9pbignIHwgJyl9YClcbiAgICAub3B0aW9uKCctcCwgLS1wcm9tcHQgPHRleHQ+JywgJ05hdHVyYWwtbGFuZ3VhZ2UgZ29hbCAocGFyc2VkIGludG8gcGFyYW1zIHdoZW4gYW4gQUkga2V5IGlzIHNldCknKVxuICAgIC5vcHRpb24oJy0tcmVnaW9uIDxyZWdpb24+JywgJ0FXUyByZWdpb24gKEVDMi9FS1MpJylcbiAgICAub3B0aW9uKCctLWRvbWFpbiA8ZG9tYWluPicsICdGdWxseS1xdWFsaWZpZWQgZG9tYWluIG5hbWUgKEVDMi9FS1MpJylcbiAgICAub3B0aW9uKCctLWhvc3RlZC16b25lLWlkIDxpZD4nLCAnUHVibGljIFJvdXRlIDUzIGhvc3RlZCB6b25lIElEIChFQzIvRUtTKScpXG4gICAgLm9wdGlvbignLS1kZXBsb3ktbW9kZSA8bW9kZT4nLCAncHVibGljIHwgcHJpdmF0ZSAoRUMyL0VLUyknKVxuICAgIC5vcHRpb24oJy0ta2V5LXBhaXIgPG5hbWU+JywgJ0VDMiBrZXkgcGFpciAoRUMyKScpXG4gICAgLm9wdGlvbignLS1pbnN0YW5jZS10eXBlIDx0eXBlPicsICdFQzIgaW5zdGFuY2UgdHlwZSAoRUMyKScpXG4gICAgLm9wdGlvbignLS1sZWFuJywgJ0VDMjogdHJpbSBvcHRpb25hbCBvYnNlcnZhYmlsaXR5L2FkbWluIHNlcnZpY2VzIHNvIHRoZSBjb3JlIHN0YWNrICsgbWVzaCBmaXRzIGEgdDMueGxhcmdlIChwYWlyIHdpdGggLS1pbnN0YW5jZS10eXBlIHQzLnhsYXJnZSknKVxuICAgIC5vcHRpb24oJy0tZ2hjci10b2tlbiA8dG9rZW4+JywgJ0dpdEh1YiBQQVQgKHJlYWQ6cGFja2FnZXMpIOKAlCBtYXNrZWQgaW4gb3V0cHV0JylcbiAgICAub3B0aW9uKCctLWVtYWlsJywgJ0VuYWJsZSBTRVMgdHJhbnNhY3Rpb25hbCBlbWFpbCAob24gYnkgZGVmYXVsdCBmb3IgQVdTKScpXG4gICAgLm9wdGlvbignLS1uby1lbWFpbCcsICdTa2lwIFNFUyAodHJhbnNhY3Rpb25hbCBlbWFpbCBpcyBwcm92aXNpb25lZCBieSBkZWZhdWx0IG9uIEFXUyknKVxuICAgIC5vcHRpb24oJy0tZW1haWwtZnJvbSA8YWRkcj4nLCAnU0VTIEZyb20gYWRkcmVzcycpXG4gICAgLm9wdGlvbignLS1lbWFpbC1mcm9tLW5hbWUgPG5hbWU+JywgJ1NFUyBGcm9tIGRpc3BsYXkgbmFtZScpXG4gICAgLm9wdGlvbignLS1hbGVydC1lbWFpbCA8YWRkcj4nLCAnU3Vic2NyaWJlIGFuIGFkZHJlc3MgdG8gdGhlIGJvdW5jZS9jb21wbGFpbnQgU05TIHRvcGljJylcbiAgICAub3B0aW9uKCctLXNraXAtc2VzLWlkZW50aXR5JywgJ1NraXAgU0VTIGlkZW50aXR5IGNyZWF0aW9uIChkb21haW4gYWxyZWFkeSB2ZXJpZmllZCknKVxuICAgIC5vcHRpb24oJy0tYWktcHJvdmlkZXIgPHByb3ZpZGVyPicsICdBSSBwcm92aWRlciAoYW50aHJvcGljfG9wZW5haXxnb29nbGV8eGFpfGJlZHJvY2spJylcbiAgICAub3B0aW9uKCctLW1vZGVsIDxtb2RlbD4nLCAnQUkgbW9kZWwgaWQnKVxuICAgIC5vcHRpb24oJy0tZGlhZ25vc2UgPGZpbGU+JywgJ0RpYWdub3NlIGEgQ2xvdWRGb3JtYXRpb24vZGVwbG95IGZhaWx1cmUgZnJvbSBhIGZpbGUgKG5lZWRzIGFuIEFJIGtleSknKVxuICAgIC5vcHRpb24oJy15LCAtLXllcycsICdBdXRvLWFjY2VwdCBhbGwgY29uZmlybWF0aW9uIHByb21wdHMgKGZvciBDSSAvIG5vbi1pbnRlcmFjdGl2ZSknLCBmYWxzZSlcbiAgICAub3B0aW9uKCctLXJldHJpZXMgPG4+JywgJ0F1dG8tZml4ICsgcmV0cnkgYXR0ZW1wdHMgYWZ0ZXIgYSBmYWlsdXJlIChkZXBsb3kgc2NyaXB0cyBhcmUgaWRlbXBvdGVudCknLCAnMScpXG4gICAgLm9wdGlvbignLS10ZWFyZG93bicsICdUZWFyIGRvd24gYW4gZXhpc3RpbmcgZGVwbG95bWVudCBpbnN0ZWFkIG9mIGNyZWF0aW5nIG9uZSAoZ2F0ZWQ7IEFXUyB0YXJnZXRzIHJlcXVpcmUgYSB0eXBlZCBjb25maXJtYXRpb24pJywgZmFsc2UpXG4gICAgLm9wdGlvbignLS1zdGFjay1uYW1lIDxuYW1lPicsICdDbG91ZEZvcm1hdGlvbiBzdGFjayBuYW1lIChFQzIpIGZvciB0aGUgZGVwbG95IEFORCB0ZWFyZG93biDigJQgZGVmYXVsdHMgdG8gcGlwZWxpbmUtYnVpbGRlciAoc2V0IGl0IHRvIHJ1biBhIHNlY29uZCBlbnZpcm9ubWVudCknKVxuICAgIC5vcHRpb24oJy0tY2x1c3Rlci1uYW1lIDxuYW1lPicsICdFS1MgY2x1c3RlciBuYW1lIGZvciB0aGUgZGVwbG95IEFORCB0ZWFyZG93biDigJQgZGVmYXVsdHMgdG8gcGlwZWxpbmUtYnVpbGRlcicpXG4gICAgLm9wdGlvbignLS1mb3JjZScsICdTa2lwIHRoZSB0ZWFyZG93biB0eXBlZC1jb25maXJtYXRpb24gKERBTkdFUk9VUyDigJQgZm9yIENJL2F1dG9tYXRpb24gb25seSknLCBmYWxzZSlcbiAgICAub3B0aW9uKCctLWluaXQgPG1vZGU+JywgJ1Bvc3QtZGVwbG95IGluaXRpYWxpemF0aW9uOiBhdXRvIChERUZBVUxUIOKAlCByZWdpc3RlciBhZG1pbiArIGxvYWQgcGx1Z2lucy9jb21wbGlhbmNlL3NhbXBsZXM7IG9uIGVjMiB0aGUgaW5zdGFuY2UgZG9lcyBpdCBpdHNlbGYgb24gZmlyc3QgYm9vdCksIG1hbnVhbCAoZG9uXFwndCBzZWxmLWluaXQg4oCUIHN1cmZhY2UgdGhlIHN0ZXAgZm9yIHlvdSB0byBydW4sIGUuZy4gdG8gc2V0IHJlYWwgYWRtaW4gY3JlZHMpLCBvciBza2lwIChkbyBub3RoaW5nKS4gbG9jYWwvbWluaWt1YmUvZWtzIHJ1biBpbml0IHZpYSBwcm92aXNpb24gdW5sZXNzIHNraXAuJylcbiAgICAvLyBEZXByZWNhdGVkIGFsaWFzZXMg4oCUIGtlcHQgd29ya2luZyBmb3IgYmFjay1jb21wYXQ7IC0taW5pdCBpcyB0aGUgZG9jdW1lbnRlZCBmb3JtLlxuICAgIC5vcHRpb24oJy0tbm8taW5pdCcsICdbZGVwcmVjYXRlZF0gYWxpYXMgZm9yIC0taW5pdCBza2lwJylcbiAgICAub3B0aW9uKCctLWF1dG8taW5pdCcsICdbZGVwcmVjYXRlZF0gYWxpYXMgZm9yIC0taW5pdCBhdXRvJylcbiAgICAub3B0aW9uKCctLW5vLWF1dG8taW5pdCcsICdbZGVwcmVjYXRlZF0gYWxpYXMgZm9yIC0taW5pdCBtYW51YWwnKVxuICAgIC8vIEJvb3RzdHJhcCAoc3BhcnNlIGNsb25lKSDigJQgcHJvdmlzaW9uIGEgZnJlc2ggbWFjaGluZSBpbiBvbmUgY29tbWFuZC5cbiAgICAub3B0aW9uKCctLXJlcG8gW3VybF0nLCAnQm9vdHN0cmFwOiBnaXQtY2xvbmUgdGhlIHBsYXRmb3JtIHJlcG8gZmlyc3QgKHNwYXJzZSDigJQgb25seSB0aGUgbmVlZGVkIGRlcGxveSBmb2xkZXJzKSwgdGhlbiBydW4gZnJvbSBpdCAobm8gdmFsdWUgPSB0aGUgdXBzdHJlYW0gZGVmYXVsdCknKVxuICAgIC5vcHRpb24oJy0tcmVmIDxyZWY+JywgJ0dpdCBicmFuY2gvdGFnIHRvIGNoZWNrIG91dCB3aGVuIGJvb3RzdHJhcHBpbmcgKGRlZmF1bHQ6IG1haW4pJylcbiAgICAub3B0aW9uKCctLXdvcmtkaXIgPGRpcj4nLCAnRGlyZWN0b3J5IHRvIGNsb25lIGludG8gLyBydW4gZnJvbSB3aGVuIGJvb3RzdHJhcHBpbmcgKGRlZmF1bHQ6IHBpcGVsaW5lLWJ1aWxkZXIpJylcbiAgICAvLyBQb3N0LWluc3RhbGwgbG9hZHMg4oCUIGVhY2ggb3B0LWluIHN0ZXAgYWxzbyBhZGRzIGl0cyBkZXBsb3kgZm9sZGVyIHRvIHRoZSBzcGFyc2UgY2xvbmUuXG4gICAgLm9wdGlvbignLS13aXRoLXBsdWdpbnMnLCAnUG9zdC1pbnN0YWxsOiBidWlsZCArIGxvYWQgcGx1Z2lucyAoYWRkcyBkZXBsb3kvcGx1Z2lucywgZGVwbG95L2NvZGVidWlsZCknLCBmYWxzZSlcbiAgICAub3B0aW9uKCctLXdpdGgtY29tcGxpYW5jZScsICdQb3N0LWluc3RhbGw6IGxvYWQgc2FtcGxlIGNvbXBsaWFuY2UgcnVsZXMvcG9saWNpZXMgKGFkZHMgZGVwbG95L2NvbXBsaWFuY2UpJywgZmFsc2UpXG4gICAgLm9wdGlvbignLS13aXRoLXNhbXBsZXMnLCAnUG9zdC1pbnN0YWxsOiBsb2FkIHNhbXBsZSBwaXBlbGluZSB0ZW1wbGF0ZXMgKGFkZHMgZGVwbG95L3NhbXBsZXMpJywgZmFsc2UpXG4gICAgLm9wdGlvbignLS13aXRoLWFsbCcsICdQb3N0LWluc3RhbGw6IHBsdWdpbnMgKyBjb21wbGlhbmNlICsgc2FtcGxlcycsIGZhbHNlKVxuICAgIC5vcHRpb24oJy0tYnVpbGQtYm9vdHN0cmFwJywgJ0J1aWxkICsgcHVibGlzaCB0aGUgQ29kZUJ1aWxkIGJvb3RzdHJhcCBpbWFnZSBkdXJpbmcgcmVnaXN0ZXIgKGFkZHMgZGVwbG95L2NvZGVidWlsZCknLCBmYWxzZSlcbiAgICAub3B0aW9uKCctLXdpdGgtc21va2UtdGVzdCcsICdQb3N0LWluc3RhbGw6IHJlYWQtb25seSBBUEkgcmVhY2hhYmlsaXR5IGNoZWNrJywgZmFsc2UpXG4gICAgLm9wdGlvbignLS13aXRoLWV2ZW50cycsICdQb3N0LWluc3RhbGwgKEFXUyk6IGV2ZW50IGluZ2VzdGlvbiBidW5kbGUg4oCUIHN0b3JlLXRva2VuIChKV1Qg4oaSIFNlY3JldHMgTWFuYWdlcikgdGhlbiBzZXR1cC1ldmVudHMgKEV2ZW50QnJpZGdlL0xhbWJkYSknLCBmYWxzZSlcbiAgICAub3B0aW9uKCctLXBvc3Qtc3RlcCA8Y21kPicsICdSdW4gYW4gZXh0cmEgY29tbWFuZCBhZnRlciB0aGUgbG9hZHMgKHJlcGVhdGFibGUsIGluIG9yZGVyKScsIGNvbGxlY3RTdGVwLCBbXSlcbiAgICAub3B0aW9uKCctLWFkbWluLWVtYWlsIDxhZGRyPicsICdBZG1pbiBlbWFpbCBmb3Igbm9uLWludGVyYWN0aXZlIHJlZ2lzdGVyIChzZXRzIFBMQVRGT1JNX0lERU5USUZJRVIpJylcbiAgICAub3B0aW9uKCctLWFkbWluLXBhc3N3b3JkIDxwdz4nLCAnQWRtaW4gcGFzc3dvcmQgZm9yIG5vbi1pbnRlcmFjdGl2ZSByZWdpc3RlciAoc2V0cyBQTEFURk9STV9QQVNTV09SRCknKVxuICAgIC5vcHRpb24oJy0tanNvbicsICdQcmludCB0aGUgcGxhbiBhcyBKU09OIGFuZCBleGl0IFdJVEhPVVQgcnVubmluZyAodGhlIG9ubHkgbm9uLWV4ZWN1dGluZyBtb2RlIOKAlCBmb3IgdG9vbGluZy9DSSBpbnNwZWN0aW9uKScsIGZhbHNlKVxuICAgIC5hY3Rpb24oYXN5bmMgKG9wdGlvbnMpID0+IHtcbiAgICAgIGNvbnN0IGV4ZWN1dGlvbklkID0gcHJpbnRDb21tYW5kSGVhZGVyKCdQcm92aXNpb24nKTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gUmVzb2x2ZSB0aGUgc2luZ2xlIHBvc3QtZGVwbG95IGluaXQgbW9kZSAoYXV0b3xtYW51YWx8c2tpcCkgZnJvbSAtLWluaXQgb3IgaXRzXG4gICAgICAgIC8vIGRlcHJlY2F0ZWQgYWxpYXNlcy4gVHdvIGludGVybmFsIHNpZ25hbHMgZGVyaXZlIGZyb20gaXQ6IHdoZXRoZXIgaW5pdCBoYXBwZW5zIGF0XG4gICAgICAgIC8vIGFsbCwgYW5kIHdoZXRoZXIgdGhlIERFUExPWSBzZWxmLXJ1bnMgaXQgKHZzLiB3ZSBzdXJmYWNlIGl0KS5cbiAgICAgICAgY29uc3QgaW5pdE1vZGUgPSByZXNvbHZlSW5pdE1vZGUob3B0aW9ucyk7XG4gICAgICAgIGlmIChpbml0TW9kZSA9PT0gbnVsbCkge1xuICAgICAgICAgIHByaW50RXJyb3IoYEludmFsaWQgLS1pbml0IHZhbHVlICcke1N0cmluZyhvcHRpb25zLmluaXQpfScuIFVzZSBvbmUgb2Y6ICR7SU5JVF9NT0RFUy5qb2luKCcgfCAnKX0uYCk7XG4gICAgICAgICAgcHJvY2Vzcy5leGl0Q29kZSA9IDE7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGluaXRFbmFibGVkID0gaW5pdE1vZGUgIT09ICdza2lwJzsgLy8gc2tpcCDihpIgbm8gcmVnaXN0ZXIvbG9hZHMgYXQgYWxsXG4gICAgICAgIGNvbnN0IHNlbGZJbml0ID0gaW5pdE1vZGUgPT09ICdhdXRvJzsgLy8gYXV0byDihpIgdGhlIGRlcGxveSBpbml0aWFsaXplcyBpdHNlbGZcblxuICAgICAgICAvLyAxLiBBc3NlbWJsZSBwYXJhbXMgZnJvbSBleHBsaWNpdCBmbGFncyAoZmxhZ3MgYWx3YXlzIHdpbiBvdmVyIE5MIHBhcnNlKS5cbiAgICAgICAgY29uc3QgcGFyYW1zOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHtcbiAgICAgICAgICByZWdpb246IG9wdGlvbnMucmVnaW9uLFxuICAgICAgICAgIGRvbWFpbjogb3B0aW9ucy5kb21haW4sXG4gICAgICAgICAgaG9zdGVkWm9uZUlkOiBvcHRpb25zLmhvc3RlZFpvbmVJZCxcbiAgICAgICAgICBkZXBsb3lNb2RlOiBvcHRpb25zLmRlcGxveU1vZGUsXG4gICAgICAgICAga2V5UGFpcjogb3B0aW9ucy5rZXlQYWlyLFxuICAgICAgICAgIGluc3RhbmNlVHlwZTogb3B0aW9ucy5pbnN0YW5jZVR5cGUsXG4gICAgICAgICAgLy8gZWMyIExFQU4gbW9kZSAoYm9vbGVhbikg4oaSIGFzc2VtYmxlQ29tbWFuZCBlbWl0cyBgLS1sZWFuYCwgd2hpY2ggc2V0dXAuc2hcbiAgICAgICAgICAvLyBmb3J3YXJkcyB0byB0aGUgQ0ZOIGBMZWFuYCBwYXJhbS4gT25seSBlYzIncyBzcGVjIGxpc3RzIGl0OyBvdGhlciB0YXJnZXRzIGlnbm9yZSBpdC5cbiAgICAgICAgICBsZWFuOiBvcHRpb25zLmxlYW4gPT09IHRydWUsXG4gICAgICAgICAgLy8gRGVwbG95LXRpbWUgcmVzb3VyY2UgaWRlbnRpdHk6IGVjMiBlbWl0cyAtLXN0YWNrLW5hbWUgKGtleSBzdGFja05hbWUpLCBla3MgZW1pdHNcbiAgICAgICAgICAvLyAtLWNsdXN0ZXItbmFtZSAoa2V5IGNsdXN0ZXJOYW1lKS4gQm90aCBhbHNvIGRyaXZlIHRlYXJkb3duLiBVbmRlZmluZWQg4oaSIGRlZmF1bHRzLlxuICAgICAgICAgIHN0YWNrTmFtZTogb3B0aW9ucy5zdGFja05hbWUsXG4gICAgICAgICAgY2x1c3Rlck5hbWU6IG9wdGlvbnMuY2x1c3Rlck5hbWUsXG4gICAgICAgICAgZ2hjclRva2VuOiBvcHRpb25zLmdoY3JUb2tlbixcbiAgICAgICAgICAvLyBlbWFpbC9ub0VtYWlsIGFyZSBjb2VyY2VkIEFGVEVSIHRoZSBOTCBtZXJnZSBiZWxvdyDigJQgcGFyc2VHb2FsIGNhbiByZXR1cm5cbiAgICAgICAgICAvLyBgZW1haWxgLCBzbyBwcmUtY29lcmNpbmcgaGVyZSAobmV2ZXItdW5kZWZpbmVkKSB3b3VsZCBsZXQgdGhlIG1lcmdlJ3NcbiAgICAgICAgICAvLyBgPT09IHVuZGVmaW5lZGAgZ3VhcmQgc2lsZW50bHkgZHJvcCBhbiBBSS1wYXJzZWQgZW1haWwgY2hvaWNlLiBMZWZ0IG91dCBvZlxuICAgICAgICAgIC8vIHRoaXMgbGl0ZXJhbCBvbiBwdXJwb3NlOyByZXNvbHZlZCBhIGZldyBsaW5lcyBkb3duLlxuICAgICAgICAgIGVtYWlsRnJvbTogb3B0aW9ucy5lbWFpbEZyb20sXG4gICAgICAgICAgZW1haWxGcm9tTmFtZTogb3B0aW9ucy5lbWFpbEZyb21OYW1lLFxuICAgICAgICAgIGFsZXJ0RW1haWw6IG9wdGlvbnMuYWxlcnRFbWFpbCxcbiAgICAgICAgICBub0NyZWF0ZVNlc0lkZW50aXR5OiBvcHRpb25zLnNraXBTZXNJZGVudGl0eSxcbiAgICAgICAgICAvLyBUaGUgQVdTIGRlcGxveXMgc2VsZi1pbml0IGJ5IGRlZmF1bHQgKGVjMiBvbiBmaXJzdCBib290LCBla3MgaW4gc2V0dXAuc2gncyBmaW5hbFxuICAgICAgICAgIC8vIHBoYXNlKSwgc28gd2UgZW1pdCB0aGUgbG9hZC1iZWFyaW5nIGAtLW5vLWF1dG8taW5pdGAgd2hlbiB0aGUgbW9kZSBpcyBOT1QgYXV0b1xuICAgICAgICAgIC8vIChtYW51YWwvc2tpcCA9IFwiZG9uJ3QgbGV0IHRoZSBkZXBsb3kgaW5pdCBpdHNlbGZcIikuIGAtLWF1dG8taW5pdGAgKGEgbm8tb3AgcmVhZmZpcm0pXG4gICAgICAgICAgLy8gaXMgbmV2ZXIgZW1pdHRlZC4gT25seSBlYzIvZWtzIGNhcnJ5IHRoZXNlIGZsYWdzIGluIHRoZWlyIHNwZWM7IG90aGVyIHRhcmdldHMgaWdub3JlIGl0LlxuICAgICAgICAgIG5vQXV0b0luaXQ6ICFzZWxmSW5pdCxcbiAgICAgICAgfTtcbiAgICAgICAgY29uc3QgYWlPcHRzID0geyBwcm92aWRlcjogb3B0aW9ucy5haVByb3ZpZGVyLCBtb2RlbDogb3B0aW9ucy5tb2RlbCB9O1xuXG4gICAgICAgIC8vIEJvb3RzdHJhcCAoc3BhcnNlIGNsb25lKSArIHBvc3QtaW5zdGFsbCBzZWxlY3Rpb25zLlxuICAgICAgICBjb25zdCB3YW50Qm9vdHN0cmFwID0gb3B0aW9ucy5yZXBvICE9PSB1bmRlZmluZWQgJiYgb3B0aW9ucy5yZXBvICE9PSBmYWxzZTtcbiAgICAgICAgY29uc3Qgd2l0aEFsbCA9IG9wdGlvbnMud2l0aEFsbCA9PT0gdHJ1ZTtcbiAgICAgICAgY29uc3QgYW55TG9hZEZsYWcgPSB3aXRoQWxsIHx8IExPQURfU1RFUFMuc29tZSgocykgPT4gb3B0aW9uc1tzLmZsYWddID09PSB0cnVlKTtcbiAgICAgICAgbGV0IGVuYWJsZWRMb2FkSWRzOiBzdHJpbmdbXSA9IExPQURfU1RFUFMuZmlsdGVyKChzKSA9PiB3aXRoQWxsIHx8IG9wdGlvbnNbcy5mbGFnXSA9PT0gdHJ1ZSkubWFwKChzKSA9PiBzLmlkKTtcbiAgICAgICAgLy8gV2l0aCBubyAtLXdpdGgtKiBmbGFncywgYW4gaW50ZXJhY3RpdmUgcnVuIG9mZmVycyBlYWNoIGxvYWQg4oCUIGJ1dCBBRlRFUiB0aGVcbiAgICAgICAgLy8gY2xvbmUsIHNvIHRoZSBxdWVzdGlvbnMgY29tZSBvbmNlIHlvdSd2ZSBhZ3JlZWQgdG8gcHJvY2VlZCAobm90IHVwIGZyb250KS5cbiAgICAgICAgLy8gRWFjaCBwaWNrZWQgbG9hZCdzIGZvbGRlciBpcyB0aGVuIGZldGNoZWQgdmlhIGFuIGFkZGl0aXZlIHNwYXJzZSByZS1zeW5jLlxuICAgICAgICAvLyBGbGFncyAvIC0teWVzIC8gLS1qc29uIC8gbm9uLWludGVyYWN0aXZlIHNoZWxscyBza2lwIHRoZSBwcm9tcHRzLlxuICAgICAgICBsZXQgd2lsbFByb21wdExvYWRzID0gIW9wdGlvbnMueWVzICYmICFvcHRpb25zLmpzb24gJiYgQm9vbGVhbihwcm9jZXNzLnN0ZGluLmlzVFRZKSAmJiAhYW55TG9hZEZsYWc7XG4gICAgICAgIGNvbnN0IHBvc3RTdGVwRmxhZ3MgPSB7XG4gICAgICAgICAgaW5pdDogaW5pdEVuYWJsZWQsXG4gICAgICAgICAgLy8gc2VsZkluaXQgKG1vZGUgYXV0bykgZHJvcHMgdGhlIHN1cmZhY2VkIHJlZ2lzdGVyIHN0ZXAgb24gdGhlIEFXUyB0YXJnZXRzIGluXG4gICAgICAgICAgLy8gcmVzb2x2ZVBvc3RTdGVwcywgc28gYSBzZWxmLWluaXRpYWxpemluZyBlYzIvZWtzIGRlcGxveSBkb2Vzbid0IGFsc28gcHJpbnQgYSBtYW51YWxcbiAgICAgICAgICAvLyByZWdpc3RlciBzdGVwLiBGb3IgbWFudWFsIG1vZGUgKG9yIGxvY2FsL21pbmlrdWJlKSB0aGUgcmVnaXN0ZXIgc3RlcCBpcyBzdXJmYWNlZC5cbiAgICAgICAgICBhdXRvSW5pdDogc2VsZkluaXQsXG4gICAgICAgICAgYnVpbGRCb290c3RyYXA6IG9wdGlvbnMuYnVpbGRCb290c3RyYXAgPT09IHRydWUgfHwgZW5hYmxlZExvYWRJZHMuaW5jbHVkZXMoJ3BsdWdpbnMnKSxcbiAgICAgICAgICBzbW9rZVRlc3Q6IG9wdGlvbnMud2l0aFNtb2tlVGVzdCA9PT0gdHJ1ZSxcbiAgICAgICAgICBldmVudHM6IG9wdGlvbnMud2l0aEV2ZW50cyA9PT0gdHJ1ZSxcbiAgICAgICAgICBzdGVwczogKG9wdGlvbnMucG9zdFN0ZXAgPz8gW10pIGFzIHN0cmluZ1tdLFxuICAgICAgICB9O1xuICAgICAgICAvLyBBZG1pbiBjcmVkZW50aWFscyBmb3J3YXJkZWQgYXMgZW52IHNvIHJlZ2lzdGVyIHJ1bnMgbm9uLWludGVyYWN0aXZlbHkuXG4gICAgICAgIGNvbnN0IGFkbWluRW52OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gICAgICAgIGlmICh0eXBlb2Ygb3B0aW9ucy5hZG1pbkVtYWlsID09PSAnc3RyaW5nJykgYWRtaW5FbnYuUExBVEZPUk1fSURFTlRJRklFUiA9IG9wdGlvbnMuYWRtaW5FbWFpbDtcbiAgICAgICAgaWYgKHR5cGVvZiBvcHRpb25zLmFkbWluUGFzc3dvcmQgPT09ICdzdHJpbmcnKSBhZG1pbkVudi5QTEFURk9STV9QQVNTV09SRCA9IG9wdGlvbnMuYWRtaW5QYXNzd29yZDtcblxuICAgICAgICAvLyAyLiBPcHRpb25hbGx5IHBhcnNlIGEgbmF0dXJhbC1sYW5ndWFnZSBnb2FsIHRvIGZpbGwgYW55IEdBUFMgKGJlc3QtZWZmb3J0KS5cbiAgICAgICAgbGV0IHRhcmdldDogVGFyZ2V0SWQgfCB1bmRlZmluZWQgPSBpc1RhcmdldElkKG9wdGlvbnMudGFyZ2V0KSA/IG9wdGlvbnMudGFyZ2V0IDogdW5kZWZpbmVkO1xuICAgICAgICBpZiAob3B0aW9ucy5wcm9tcHQpIHtcbiAgICAgICAgICBpZiAoaXNBaUNvbmZpZ3VyZWQoYWlPcHRzKSkge1xuICAgICAgICAgICAgY29uc3QgcGFyc2VkID0gYXdhaXQgcGFyc2VHb2FsKG9wdGlvbnMucHJvbXB0LCBhaU9wdHMpO1xuICAgICAgICAgICAgaWYgKHBhcnNlZCkge1xuICAgICAgICAgICAgICBpZiAoIXRhcmdldCAmJiBpc1RhcmdldElkKHBhcnNlZC50YXJnZXQpKSB0YXJnZXQgPSBwYXJzZWQudGFyZ2V0O1xuICAgICAgICAgICAgICBmb3IgKGNvbnN0IFtrLCB2XSBvZiBPYmplY3QuZW50cmllcyhwYXJzZWQpKSB7XG4gICAgICAgICAgICAgICAgaWYgKGsgIT09ICd0YXJnZXQnICYmIHBhcmFtc1trXSA9PT0gdW5kZWZpbmVkICYmIHYgIT09IHVuZGVmaW5lZCkgcGFyYW1zW2tdID0gdjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAoIW9wdGlvbnMuanNvbikge1xuICAgICAgICAgICAgLy8gRG9uJ3QgcHJpbnQgaHVtYW4gdGV4dCBiZWZvcmUgYSAtLWpzb24gcGxhbiAoaXQgd291bGQgY29ycnVwdCB0aGUgc3RyZWFtKS5cbiAgICAgICAgICAgIHByaW50V2FybmluZygnQSAtLXByb21wdCB3YXMgZ2l2ZW4gYnV0IG5vIEFJIGtleSBpcyBzZXQg4oCUIGlnbm9yaW5nIGl0IGFuZCB1c2luZyBmbGFncyBvbmx5LiBTZXQgQU5USFJPUElDX0FQSV9LRVkgKG9yIC0tYWktcHJvdmlkZXIgKyBpdHMga2V5KS4nKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBSZXNvbHZlIGVtYWlsIE5PVyAoYWZ0ZXIgdGhlIE5MIG1lcmdlKTogYW4gZXhwbGljaXQgLS1lbWFpbC8tLW5vLWVtYWlsIGZsYWcgd2lucyxcbiAgICAgICAgLy8gZWxzZSBhbiBBSS1wYXJzZWQgYGVtYWlsYCAobWVyZ2VkIGludG8gcGFyYW1zLmVtYWlsIGFib3ZlKSBkZWNpZGVzLCBlbHNlIGRlZmF1bHQtb24uXG4gICAgICAgIC8vIENvZXJjZSB0byB0aGUgdHdvIGxvYWQtYmVhcmluZyBrZXlzIGFzc2VtYmxlQ29tbWFuZCBlbWl0cy5cbiAgICAgICAgY29uc3QgZW1haWxDaG9pY2UgPSBvcHRpb25zLmVtYWlsICE9PSB1bmRlZmluZWRcbiAgICAgICAgICA/IG9wdGlvbnMuZW1haWxcbiAgICAgICAgICA6ICh0eXBlb2YgcGFyYW1zLmVtYWlsID09PSAnYm9vbGVhbicgPyBwYXJhbXMuZW1haWwgOiB1bmRlZmluZWQpO1xuICAgICAgICBwYXJhbXMuZW1haWwgPSBlbWFpbENob2ljZSA9PT0gdHJ1ZTtcbiAgICAgICAgcGFyYW1zLm5vRW1haWwgPSBlbWFpbENob2ljZSA9PT0gZmFsc2U7XG5cbiAgICAgICAgLy8gMy4gT3B0aW9uYWwgZmFpbHVyZSBkaWFnbm9zaXMgKGluZGVwZW5kZW50IG9mIGEgZGVwbG95IHBsYW4pLlxuICAgICAgICBpZiAob3B0aW9ucy5kaWFnbm9zZSkge1xuICAgICAgICAgIGxldCBmYWlsdXJlVGV4dDogc3RyaW5nO1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBmYWlsdXJlVGV4dCA9IHJlYWRGaWxlU3luYyhvcHRpb25zLmRpYWdub3NlLCAndXRmLTgnKTtcbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIHByaW50RXJyb3IoYENhbm5vdCByZWFkIC0tZGlhZ25vc2UgZmlsZTogJHtvcHRpb25zLmRpYWdub3NlfWApO1xuICAgICAgICAgICAgcHJvY2Vzcy5leGl0Q29kZSA9IDE7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IGRpYWdub3NpcyA9IGlzQWlDb25maWd1cmVkKGFpT3B0cykgPyBhd2FpdCBkaWFnbm9zZUZhaWx1cmUoZmFpbHVyZVRleHQsIGFpT3B0cykgOiBudWxsO1xuICAgICAgICAgIC8vIFdpdGggLS1qc29uLCB0aGUgZGlhZ25vc2lzIElTIHRoZSBtYWNoaW5lLXJlYWRhYmxlIG91dHB1dCDigJQgZW1pdCBpdFxuICAgICAgICAgIC8vIGFsb25lIChkb24ndCBhbHNvIHByaW50IHRleHQsIHdoaWNoIHdvdWxkIGNvcnJ1cHQgdGhlIEpTT04gc3RyZWFtKS5cbiAgICAgICAgICBpZiAob3B0aW9ucy5qc29uKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7IHN1Y2Nlc3M6IHRydWUsIGV4ZWN1dGlvbklkLCBkaWFnbm9zaXMgfSwgbnVsbCwgMikpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZGlhZ25vc2lzKSB7XG4gICAgICAgICAgICBwcmludFNlY3Rpb24oJ0ZhaWx1cmUgZGlhZ25vc2lzJyk7XG4gICAgICAgICAgICBwcmludEluZm8oZGlhZ25vc2lzKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcHJpbnRXYXJuaW5nKCdEaWFnbm9zaXMgdW5hdmFpbGFibGUgKG5vIEFJIGtleSBjb25maWd1cmVkIG9yIHRoZSBtb2RlbCBjb3VsZCBub3QgYmUgcmVhY2hlZCkuJyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIC0tZGlhZ25vc2UgaXMgYSByZWFkLW9ubHkgaW5zcGVjdGlvbiBtb2RlOiByZXR1cm4gd2hldGhlciBvciBub3QgYSB0YXJnZXQgd2FzXG4gICAgICAgICAgLy8gZ2l2ZW4sIHNvIGl0IG5ldmVyIGZhbGxzIHRocm91Z2ggaW50byBhIChwb3NzaWJseSAtLXllcykgZGVwbG95LlxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFdpdGggYC0taW5pdCBhdXRvYCAodGhlIGRlZmF1bHQpIHRoZSBFQzIgaW5zdGFuY2UgbG9hZHMgcGx1Z2lucy9jb21wbGlhbmNlL3NhbXBsZXNcbiAgICAgICAgLy8gaXRzZWxmIG9uIHRoZSBib3ggKGJvb3RzdHJhcC5zaCBoYXJkY29kZXMgTE9BRF8qPXkpLCBzbyB0aGUgbG9jYWwgbG9hZCBwaWNrZXIg4oCUIGFuZFxuICAgICAgICAvLyB0aGUgbG9hZC1mb2xkZXIgZmV0Y2ggaXQgdHJpZ2dlcnMg4oCUIGlzIHBvaW50bGVzcy4gU2tpcCBib3RoICh0aGUgaW5zdGFuY2UgaGFzIGl0c1xuICAgICAgICAvLyBvd24gY2hlY2tvdXQpLlxuICAgICAgICBpZiAoc2VsZkluaXQgJiYgdGFyZ2V0ID09PSAnZWMyJykge1xuICAgICAgICAgIHdpbGxQcm9tcHRMb2FkcyA9IGZhbHNlO1xuICAgICAgICAgIGVuYWJsZWRMb2FkSWRzID0gW107XG4gICAgICAgIH0gZWxzZSBpZiAoc2VsZkluaXQgJiYgdGFyZ2V0ID09PSAnZWtzJyAmJiAhYW55TG9hZEZsYWcpIHtcbiAgICAgICAgICAvLyBgLS1pbml0IGF1dG9gIG9uIGVrcyBpbml0aWFsaXplcyBsaWtlIGVjMjogTk8gaW50ZXJhY3RpdmUgcGlja2VyIOKAlCBkZWZhdWx0IHRvXG4gICAgICAgICAgLy8gbG9hZGluZyBBTEwgb2YgcGx1Z2lucyArIGNvbXBsaWFuY2UgKyBzYW1wbGVzIChzZXR1cC5zaCdzIGZpbmFsIHBoYXNlIGhhcmRjb2Rlc1xuICAgICAgICAgIC8vIExPQURfKj15KS4gc2V0dXAuc2ggcnVucyB0aGUgbG9hZHMgaXRzZWxmIChvdmVyIGEga3ViZWN0bCBwb3J0LWZvcndhcmQpLCBidXQgd2VcbiAgICAgICAgICAvLyBzdGlsbCByZXNvbHZlIHRoZW0gaGVyZSBzbyB0aGUgc3BhcnNlIGNsb25lIGZldGNoZXMgdGhlIGZvbGRlcnMgc2V0dXAuc2ggYnVpbGRzXG4gICAgICAgICAgLy8gZnJvbS4gRXhwbGljaXQgYC0td2l0aC0qYCBmbGFncyBvdmVycmlkZTsgYC0taW5pdCBtYW51YWxgIC8gYC0taW5pdCBza2lwYCBvcHQgb3V0LlxuICAgICAgICAgIHdpbGxQcm9tcHRMb2FkcyA9IGZhbHNlO1xuICAgICAgICAgIGVuYWJsZWRMb2FkSWRzID0gTE9BRF9TVEVQUy5tYXAoKHMpID0+IHMuaWQpO1xuICAgICAgICAgIHBvc3RTdGVwRmxhZ3MuYnVpbGRCb290c3RyYXAgPSBvcHRpb25zLmJ1aWxkQm9vdHN0cmFwID09PSB0cnVlIHx8IGVuYWJsZWRMb2FkSWRzLmluY2x1ZGVzKCdwbHVnaW5zJyk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gbWluaWt1YmUvbG9jYWwga2VlcCB0aGUgaW50ZXJhY3RpdmUgcGlja2VyIChsb2NhbC1kZXY7IG5vIGAtLWluaXQgYXV0b2AgbG9hZCBwYXJpdHkpLlxuXG4gICAgICAgIC8vIDQuIE5lZWQgYSB0YXJnZXQgdG8gYXNzZW1ibGUgYSBwbGFuLlxuICAgICAgICBpZiAoIXRhcmdldCkge1xuICAgICAgICAgIHByaW50RXJyb3IoYE5vIHRhcmdldCBzcGVjaWZpZWQuIFBhc3MgLS10YXJnZXQgPCR7VEFSR0VUX0lEUy5qb2luKCd8Jyl9PiBvciBkZXNjcmliZSB0aGUgZ29hbCB3aXRoIC0tcHJvbXB0LmApO1xuICAgICAgICAgIHByb2Nlc3MuZXhpdENvZGUgPSAxO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHNwZWMgPSBUQVJHRVRTW3RhcmdldF07XG4gICAgICAgIGxldCBjd2QgPSBwcm9jZXNzLmN3ZCgpO1xuXG4gICAgICAgIC8vIDRiLiBUZWFyZG93biBtb2RlIOKAlCB0aGUgZGVzdHJveSBjb3VudGVycGFydCBvZiBwcm92aXNpb25pbmcgKHNlZSBydW5UZWFyZG93bikuXG4gICAgICAgIGlmIChvcHRpb25zLnRlYXJkb3duKSB7XG4gICAgICAgICAgYXdhaXQgcnVuVGVhcmRvd24oc3BlYywgdGFyZ2V0LCBjd2QsIGV4ZWN1dGlvbklkLCBvcHRpb25zKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBNYWtlIHByZXZpb3VzbHktZmV0Y2hlZCBzaW5nbGUtYmluYXJ5IHRvb2xzIChlLmcuIHlxKSB2aXNpYmxlIHRvIHRoZVxuICAgICAgICAvLyBwcmVyZXEgY2hlY2tzICsgdGhlIGRlcGxveSDigJQgdGhleSBsaXZlIGluIHRoZSB0b29scyBjYWNoZSwgbm90IG9uIHRoZVxuICAgICAgICAvLyBzeXN0ZW0gUEFUSC4gKEJvdGggYGhhcygpYCBhbmQgdGhlIGRlcGxveSdzIGBiYXNoIC1sY2AgaW5oZXJpdCB0aGlzLilcbiAgICAgICAgd2l0aFRvb2xzT25QYXRoKCk7XG4gICAgICAgIC8vIEhhcmQtcmVxdWlyZSBwbHVnaW4gcHJlcmVxcyAoZS5nLiB5cSBvbiBtaW5pa3ViZSkgb25seSB3aGVuIHBsdWdpbnMgaXMgRVhQTElDSVRMWVxuICAgICAgICAvLyBzZWxlY3RlZCDigJQgTk9UIG9uIHRoZSB3aWxsUHJvbXB0TG9hZHMgZ3Vlc3MsIHdoaWNoIHdvdWxkIGJsb2NrL3Byb21wdCBiZWZvcmUgdGhlXG4gICAgICAgIC8vIHVzZXIgaGFzIGV2ZW4gYmVlbiBhc2tlZCB3aGV0aGVyIHRoZXkgd2FudCBwbHVnaW5zLiBJZiB0aGV5IHBpY2sgcGx1Z2lucyBpbiB0aGVcbiAgICAgICAgLy8gaW50ZXJhY3RpdmUgcHJvbXB0LCB3ZSByZS1jaGVjayArIG9mZmVyIHRvIGZldGNoIHJpZ2h0IGFmdGVyIHRoYXQgc2VsZWN0aW9uIChiZWxvdykuXG4gICAgICAgIGxldCBwcmVyZXFzID0gY2hlY2tQcmVyZXFzKHRhcmdldCwgeyBib290c3RyYXA6IHdhbnRCb290c3RyYXAsIHdpdGhQbHVnaW5zOiBlbmFibGVkTG9hZElkcy5pbmNsdWRlcygncGx1Z2lucycpIH0pO1xuICAgICAgICBjb25zdCB7IGNvbW1hbmQsIG1pc3NpbmcgfSA9IGFzc2VtYmxlQ29tbWFuZChzcGVjLCBwYXJhbXMpO1xuICAgICAgICBjb25zdCB1cmwgPSBkZXJpdmVIZWFsdGhVcmwodGFyZ2V0LCBwYXJhbXMpO1xuXG4gICAgICAgIC8vIFNwYXJzZSBib290c3RyYXAgY2xvbmUgY29tbWFuZCAoY29tbW9uIGJhc2UgKyB0YXJnZXQgKyBzZWxlY3RlZCBsb2FkcykuXG4gICAgICAgIC8vIEludGVyYWN0aXZlIGxvYWRzIGFyZSBjaG9zZW4gQUZURVIgdGhlIGNsb25lLCBzbyB0aGUgY2xvbmUgc3RheXMgbWluaW1hbCDigJRcbiAgICAgICAgLy8gZWFjaCBwaWNrZWQgbG9hZCdzIGZvbGRlciBpcyBmZXRjaGVkIHRoZW4gdmlhIGFuIGFkZGl0aXZlIHNwYXJzZSByZS1zeW5jLlxuICAgICAgICBjb25zdCBzcGFyc2VQYXRocyA9IHNwYXJzZVBhdGhzRm9yKHRhcmdldCwgZW5hYmxlZExvYWRJZHMpO1xuICAgICAgICBjb25zdCBib290c3RyYXAgPSByZXNvbHZlQm9vdHN0cmFwKFxuICAgICAgICAgIHsgcmVwbzogdHlwZW9mIG9wdGlvbnMucmVwbyA9PT0gJ3N0cmluZycgPyBvcHRpb25zLnJlcG8gOiB1bmRlZmluZWQsIHJlZjogb3B0aW9ucy5yZWYsIHdvcmtkaXI6IG9wdGlvbnMud29ya2RpciwgZnVsbDogIWdpdFN1cHBvcnRzU3BhcnNlQ2hlY2tvdXQoKSB9LFxuICAgICAgICAgIHNwYXJzZVBhdGhzLFxuICAgICAgICApO1xuICAgICAgICBjb25zdCBib290c3RyYXBDbWQgPSB3YW50Qm9vdHN0cmFwID8gYm9vdHN0cmFwQ29tbWFuZChib290c3RyYXApIDogbnVsbDtcblxuICAgICAgICAvLyBSZXNvbHZlIHBvc3QtaW5zdGFsbCBzdGVwcyAocmVnaXN0ZXIg4oaSIHNtb2tlIOKGkiBldmVudHMg4oaSIGN1c3RvbSkuIFJlLXJlc29sdmVkXG4gICAgICAgIC8vIGFmdGVyIHRoZSBwb3N0LWNsb25lIGxvYWQgcHJvbXB0cyAoc2VlIGJlbG93KSB3aGVuIHRob3NlIHJ1biBpbnRlcmFjdGl2ZWx5LlxuICAgICAgICBsZXQgeyBzdGVwczogcG9zdFN0ZXBzLCBza2lwcGVkOiBza2lwcGVkU3RlcHMgfSA9IHJlc29sdmVQb3N0U3RlcHMoe1xuICAgICAgICAgIHRhcmdldCxcbiAgICAgICAgICB1cmwsXG4gICAgICAgICAgcmVnaW9uOiB0eXBlb2YgcGFyYW1zLnJlZ2lvbiA9PT0gJ3N0cmluZycgPyBwYXJhbXMucmVnaW9uIDogdW5kZWZpbmVkLFxuICAgICAgICAgIGVuYWJsZWRMb2FkSWRzLFxuICAgICAgICAgIC4uLnBvc3RTdGVwRmxhZ3MsXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmIChvcHRpb25zLmpzb24pIHtcbiAgICAgICAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZXhlY3V0aW9uSWQsXG4gICAgICAgICAgICB0YXJnZXQsXG4gICAgICAgICAgICBwcmVyZXFzLFxuICAgICAgICAgICAgcHJlcmVxc1NhdGlzZmllZDogcHJlcmVxc1NhdGlzZmllZChwcmVyZXFzKSxcbiAgICAgICAgICAgIG1pc3NpbmdJbnB1dHM6IG1pc3NpbmcubWFwKChtKSA9PiAoeyBmbGFnOiBtLmZsYWcsIGRlc2NyaXB0aW9uOiBtLmRlc2NyaXB0aW9uIH0pKSxcbiAgICAgICAgICAgIGJvb3RzdHJhcDogYm9vdHN0cmFwQ21kLFxuICAgICAgICAgICAgc3BhcnNlUGF0aHM6IHdhbnRCb290c3RyYXAgPyBzcGFyc2VQYXRocyA6IG51bGwsXG4gICAgICAgICAgICBjb21tYW5kLFxuICAgICAgICAgICAgcG9zdFN0ZXBzOiBwb3N0U3RlcHMubWFwKChzKSA9PiAoeyBpZDogcy5pZCwgbGFiZWw6IHMubGFiZWwsIGNvbW1hbmQ6IHMuY29tbWFuZCB9KSksXG4gICAgICAgICAgICBza2lwcGVkUG9zdFN0ZXBzOiBza2lwcGVkU3RlcHMsXG4gICAgICAgICAgfSwgbnVsbCwgMikpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIDUuIFByaW50IHRoZSBwbGFuIChzaG93biBiZWZvcmUgdGhlIGdhdGVkIGV4ZWN1dGlvbiBiZWxvdykuXG4gICAgICAgIHByaW50U2VjdGlvbihgUHJvdmlzaW9uIHBsYW4g4oCUICR7c3BlYy5sYWJlbH1gKTtcbiAgICAgICAgcHJpbnRLZXlWYWx1ZSh7ICdUYXJnZXQnOiB0YXJnZXQsICdCZXN0IGZvcic6IHNwZWMuYmVzdEZvciwgJ0Nvc3QnOiBzcGVjLmNvc3QgfSk7XG5cbiAgICAgICAgcHJpbnRTZWN0aW9uKCdQcmVyZXF1aXNpdGVzJyk7XG4gICAgICAgIGZvciAoY29uc3QgYyBvZiBwcmVyZXFzKSBwcmludEluZm8oYCR7Yy5vayA/ICfinJMnIDogJ+Kclyd9ICR7Yy5uYW1lfSDigJQgJHtjLmRldGFpbH1gKTtcbiAgICAgICAgaWYgKCFwcmVyZXFzU2F0aXNmaWVkKHByZXJlcXMpKSBwcmludFdhcm5pbmcoJ1Jlc29sdmUgdGhlIGZhaWxpbmcgcHJlcmVxdWlzaXRlcyBhYm92ZSBiZWZvcmUgZGVwbG95aW5nLicpO1xuXG4gICAgICAgIGlmIChtaXNzaW5nLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBwcmludFNlY3Rpb24oJ01pc3NpbmcgcmVxdWlyZWQgaW5wdXRzJyk7XG4gICAgICAgICAgZm9yIChjb25zdCBtIG9mIG1pc3NpbmcpIHByaW50SW5mbyhgICAtLSR7bS5mbGFnfSAgKCR7bS5kZXNjcmlwdGlvbn0pYCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoYm9vdHN0cmFwQ21kKSB7XG4gICAgICAgICAgcHJpbnRTZWN0aW9uKCdCb290c3RyYXAgKHNwYXJzZSBnaXQgY2xvbmUsIHJ1bnMgZmlyc3QpJyk7XG4gICAgICAgICAgcHJpbnRJbmZvKGBDbG9uZSAke2Jvb3RzdHJhcC5yZXBvfSBAICR7Ym9vdHN0cmFwLnJlZn0g4oaSICR7Ym9vdHN0cmFwLndvcmtkaXJ9OyBmb2xkZXJzOiAke3NwYXJzZVBhdGhzLmpvaW4oJywgJyl9YCk7XG4gICAgICAgICAgcHJpbnRJbmZvKGJvb3RzdHJhcENtZCk7XG4gICAgICAgIH1cblxuICAgICAgICBwcmludFNlY3Rpb24oJ0NvbW1hbmQgdG8gcnVuJyk7XG4gICAgICAgIHByaW50SW5mbyhjb21tYW5kKTtcblxuICAgICAgICBpZiAocG9zdFN0ZXBzLmxlbmd0aCA+IDAgfHwgc2tpcHBlZFN0ZXBzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBwcmludFNlY3Rpb24oJ1Bvc3QtaW5zdGFsbCBzdGVwcycpO1xuICAgICAgICAgIGZvciAoY29uc3QgcyBvZiBwb3N0U3RlcHMpIHByaW50SW5mbyhg4oCiICR7cy5sYWJlbH1cXG4gICAgJHtzLmNvbW1hbmR9YCk7XG4gICAgICAgICAgZm9yIChjb25zdCBzIG9mIHNraXBwZWRTdGVwcykgcHJpbnRXYXJuaW5nKGBTa2lwcGVkICR7cy5pZH06ICR7cy5yZWFzb259YCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyA2LiBBbnkgbWlzc2luZyByZXF1aXJlZCBwcmVyZXEgdGhhdCdzIGEgc2luZ2xlIHN0YXRpYyBiaW5hcnkgKGUuZy4geXEpXG4gICAgICAgIC8vIGNhbiBiZSBmZXRjaGVkIGludG8gdGhlIHRvb2xzIGNhY2hlIGluc3RlYWQgb2YgYSBzeXN0ZW0gaW5zdGFsbCDigJQgbm9cbiAgICAgICAgLy8gYnJldy9hcHQuIE9mZmVyIGl0LCB0aGVuIHJlLWNoZWNrICh0aGUgY2FjaGUgZGlyIGlzIGFscmVhZHkgb24gUEFUSCkuXG4gICAgICAgIGNvbnN0IGZldGNoYWJsZSA9IHByZXJlcXMuZmlsdGVyKChjKSA9PiAhYy5vayAmJiBjLnJlcXVpcmVkICYmIGlzRmV0Y2hhYmxlKGMubmFtZSkpO1xuICAgICAgICBpZiAoZmV0Y2hhYmxlLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBjb25zdCBuYW1lcyA9IGZldGNoYWJsZS5tYXAoKGMpID0+IGMubmFtZSkuam9pbignLCAnKTtcbiAgICAgICAgICBpZiAoYXdhaXQgY29uZmlybShgXFxuJHtuYW1lc30gbm90IGluc3RhbGxlZCDigJQgZmV0Y2ggdGhlIG9mZmljaWFsIHN0YXRpYyBiaW5hcnkgaW50byAke1RPT0xTX0RJUn0gKG5vIHN5c3RlbSBpbnN0YWxsKT9gLCBvcHRpb25zLnllcykpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgYyBvZiBmZXRjaGFibGUpIHtcbiAgICAgICAgICAgICAgcHJpbnRJbmZvKGBGZXRjaGluZyAke2MubmFtZX3igKZgKTtcbiAgICAgICAgICAgICAgaWYgKCFmZXRjaFRvb2woYy5uYW1lKSkgcHJpbnRXYXJuaW5nKGBDb3VsZG4ndCBmZXRjaCAke2MubmFtZX0g4oCUIGluc3RhbGwgaXQgbWFudWFsbHkgYW5kIHJlLXJ1bi5gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHByZXJlcXMgPSBjaGVja1ByZXJlcXModGFyZ2V0LCB7IGJvb3RzdHJhcDogd2FudEJvb3RzdHJhcCwgd2l0aFBsdWdpbnM6IGVuYWJsZWRMb2FkSWRzLmluY2x1ZGVzKCdwbHVnaW5zJykgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gN2IuIEdhdGVkIGV4ZWN1dGlvbi4gQ2hlY2sgcHJlcmVxcyAvIHJlcXVpcmVkIGlucHV0cyBmaXJzdCDigJQgdGhpcyBhbHNvXG4gICAgICAgIC8vIGNhdGNoZXMgYSBtaXNzaW5nIGBnaXRgIHdoZW4gYm9vdHN0cmFwcGluZy5cbiAgICAgICAgY29uc3QgYmxvY2tlZCA9IGV4ZWN1dGlvbkJsb2NrZWQocHJlcmVxcywgbWlzc2luZyk7XG4gICAgICAgIGlmIChibG9ja2VkKSB7XG4gICAgICAgICAgcHJpbnRFcnJvcihgQ2FuJ3Qgc3RhcnQganVzdCB5ZXQg4oCUICR7YmxvY2tlZH0uIFJlc29sdmUgdGhhdCBhbmQgcmUtcnVuLmApO1xuICAgICAgICAgIHByb2Nlc3MuZXhpdENvZGUgPSAxO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENvbW1pdCBnYXRlIOKAlCBjb25maXJtIEJFRk9SRSBhbnkgc2lkZSBlZmZlY3QgKGNsb25lLCAuZW52LCBkZXBsb3kpIHNvIGEgXCJOb1wiXG4gICAgICAgIC8vIGxlYXZlcyBub3RoaW5nIGJlaGluZC4gVGhlIHBvcnQgY2hlY2sgKyBvcHRpb25hbCBsb2FkcyArIC5lbnYgYmVsb3cgYXJlIHBhcnQgb2ZcbiAgICAgICAgLy8gZXhlY3V0aW5nIHRoZSBhZ3JlZWQtdG8gcGxhbi5cbiAgICAgICAgaWYgKCEoYXdhaXQgY29uZmlybShgXFxuUHJvY2VlZCB3aXRoIHByb3Zpc2lvbmluZyAke3NwZWMubGFiZWx9P2AsIG9wdGlvbnMueWVzKSkpIHtcbiAgICAgICAgICBwcmludFdhcm5pbmcoJ05vIHByb2JsZW0g4oCUIG5vdGhpbmcgd2FzIGNoYW5nZWQuJyk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gN2EvN2IuIFNwYXJzZS1jbG9uZSAoaWYgLS1yZXBvIG9yIGFjY2VwdGVkIGludGVyYWN0aXZlbHkpICsgbG9jYXRlIHRoZSBkZXBsb3lcbiAgICAgICAgLy8gZW50cnlwb2ludCwgcmVwb2ludGluZyBjd2QgaW50byB0aGUgY2xvbmUuIFNlZSBib290c3RyYXBBbmRMb2NhdGUuXG4gICAgICAgIGNvbnN0IGxvY2F0ZWQgPSBhd2FpdCBib290c3RyYXBBbmRMb2NhdGUoc3BlYywgYm9vdHN0cmFwLCBib290c3RyYXBDbWQsIHNwYXJzZVBhdGhzLCBjd2QsIG9wdGlvbnMpO1xuICAgICAgICBjd2QgPSBsb2NhdGVkLmN3ZDtcbiAgICAgICAgY29uc3QgYm9vdHN0cmFwcGVkID0gbG9jYXRlZC5ib290c3RyYXBwZWQ7XG4gICAgICAgIGlmICghbG9jYXRlZC5vaykgcmV0dXJuO1xuXG4gICAgICAgIC8vIEhvc3QtcG9ydCBwcmUtZmxpZ2h0IOKAlCBub3cgdGhhdCB0aGUgZGVwbG95IHNvdXJjZSBpcyBvbiBkaXNrIChwb3N0LWNsb25lKSxcbiAgICAgICAgLy8gZGVyaXZlIHRoZSBwb3J0cyBmcm9tIGl0IChjb21wb3NlIC8gc2V0dXAuc2gpIGFuZCBzdG9wIG9uIGEgZmF0YWwgY29uZmxpY3RcbiAgICAgICAgLy8gYmVmb3JlIGRlcGxveWluZy4gU2VlIHByZWZsaWdodFBvcnRzLlxuICAgICAgICBpZiAoIShhd2FpdCBwcmVmbGlnaHRQb3J0cyhzcGVjLCB0YXJnZXQsIGN3ZCkpKSB7XG4gICAgICAgICAgcHJvY2Vzcy5leGl0Q29kZSA9IDE7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gT3B0LWluIGxvYWRzIOKAlCBvZmZlcmVkIEFGVEVSIHRoZSBjbG9uZSAoc2VlIHJlc29sdmVMb2Fkc0ludGVyYWN0aXZlbHkpOyBpdFxuICAgICAgICAvLyBwcm9tcHRzLCBhZGRpdGl2ZWx5IHNwYXJzZS1mZXRjaGVzIHRoZSBwaWNrZWQgZm9sZGVycywgYW5kIHJlLXJlc29sdmVzIHRoZVxuICAgICAgICAvLyBwb3N0LWluc3RhbGwgc3RlcHMgd2l0aCB0aGUgc2VsZWN0aW9ucy5cbiAgICAgICAgaWYgKHdpbGxQcm9tcHRMb2Fkcykge1xuICAgICAgICAgIGNvbnN0IGxvYWRlZCA9IGF3YWl0IHJlc29sdmVMb2Fkc0ludGVyYWN0aXZlbHkoXG4gICAgICAgICAgICB0YXJnZXQsXG4gICAgICAgICAgICB1cmwsXG4gICAgICAgICAgICB0eXBlb2YgcGFyYW1zLnJlZ2lvbiA9PT0gJ3N0cmluZycgPyBwYXJhbXMucmVnaW9uIDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgY3dkLFxuICAgICAgICAgICAgYm9vdHN0cmFwcGVkLFxuICAgICAgICAgICAgYm9vdHN0cmFwLFxuICAgICAgICAgICAgcG9zdFN0ZXBGbGFncyxcbiAgICAgICAgICApO1xuICAgICAgICAgIGVuYWJsZWRMb2FkSWRzID0gbG9hZGVkLmVuYWJsZWRMb2FkSWRzO1xuICAgICAgICAgIHBvc3RTdGVwcyA9IGxvYWRlZC5zdGVwcztcbiAgICAgICAgICBza2lwcGVkU3RlcHMgPSBsb2FkZWQuc2tpcHBlZDtcbiAgICAgICAgICAvLyBOb3cgdGhhdCBwbHVnaW5zIG1heSBoYXZlIGJlZW4gcGlja2VkLCByZS1jaGVjayB0aGUgcGx1Z2luLXNwZWNpZmljIHByZXJlcXMgd2VcbiAgICAgICAgICAvLyBkZWxpYmVyYXRlbHkgZGlkbid0IGhhcmQtcmVxdWlyZSB1cCBmcm9udCAoZS5nLiB5cSBmb3IgbWluaWt1YmUgcGx1Z2luIGJ1aWxkcykuXG4gICAgICAgICAgLy8gT2ZmZXIgdG8gZmV0Y2ggYW55IGZldGNoYWJsZSBvbmU7IHdhcm4gKGRvbid0IGJsb2NrIOKAlCB0aGUgcGxhdGZvcm0gc3RpbGwgZGVwbG95cylcbiAgICAgICAgICAvLyBpZiBpdCBjYW4ndCBiZSByZXNvbHZlZCwgc28gdGhlIGxhdGVyIHBsdWdpbiBidWlsZCBkb2Vzbid0IGZhaWwgb3BhcXVlbHkuXG4gICAgICAgICAgaWYgKGVuYWJsZWRMb2FkSWRzLmluY2x1ZGVzKCdwbHVnaW5zJykpIHtcbiAgICAgICAgICAgIGNvbnN0IHBsdWdpblByZXJlcXMgPSBjaGVja1ByZXJlcXModGFyZ2V0LCB7IGJvb3RzdHJhcDogd2FudEJvb3RzdHJhcCwgd2l0aFBsdWdpbnM6IHRydWUgfSk7XG4gICAgICAgICAgICBjb25zdCBwbHVnaW5HYXBzID0gcGx1Z2luUHJlcmVxcy5maWx0ZXIoKGMpID0+ICFjLm9rICYmIGMucmVxdWlyZWQgJiYgIXByZXJlcXMuc29tZSgocCkgPT4gcC5uYW1lID09PSBjLm5hbWUpKTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgYyBvZiBwbHVnaW5HYXBzKSB7XG4gICAgICAgICAgICAgIGlmIChpc0ZldGNoYWJsZShjLm5hbWUpICYmIGF3YWl0IGNvbmZpcm0oYFxcbiR7Yy5uYW1lfSBpcyBuZWVkZWQgdG8gYnVpbGQgcGx1Z2lucyBidXQgaXNuJ3QgaW5zdGFsbGVkIOKAlCBmZXRjaCB0aGUgc3RhdGljIGJpbmFyeSBpbnRvICR7VE9PTFNfRElSfT9gLCBvcHRpb25zLnllcykpIHtcbiAgICAgICAgICAgICAgICBwcmludEluZm8oYEZldGNoaW5nICR7Yy5uYW1lfeKApmApO1xuICAgICAgICAgICAgICAgIGlmICghZmV0Y2hUb29sKGMubmFtZSkpIHByaW50V2FybmluZyhgQ291bGRuJ3QgZmV0Y2ggJHtjLm5hbWV9IOKAlCBpbnN0YWxsIGl0IGJlZm9yZSB0aGUgcGx1Z2luIGJ1aWxkIHJ1bnMuYCk7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcHJpbnRXYXJuaW5nKGAke2MubmFtZX0gbWlzc2luZyDigJQgdGhlIHBsdWdpbiBsb2FkL2J1aWxkIHN0ZXAgbWF5IGZhaWwuICR7Yy5kZXRhaWx9YCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gbG9jYWwvbWluaWt1YmUncyBzZXR1cC5zaCBSRVFVSVJFUyBhIGAuZW52YCBhbmQgYWJvcnRzIHdpdGhvdXQgaXQg4oCUIGNyZWF0ZVxuICAgICAgICAvLyBpdCBmcm9tIC5lbnYuZXhhbXBsZSB3aXRoIGdlbmVyYXRlZCBzZWNyZXRzIHNvIHRoZSBkZXBsb3kgaXMgbm9uLWludGVyYWN0aXZlLlxuICAgICAgICAvLyBlYzIgYW5kIGVrcyBkb24ndCBoYXZlIHByb3Zpc2lvbiBnZW5lcmF0ZSBhIGxvY2FsIGAuZW52YDogZWMyIGJvb3RzdHJhcHMgaXRzXG4gICAgICAgIC8vIHNlY3JldHMgb24gdGhlIGluc3RhbmNlLCBhbmQgZWtzJ3Mgc2V0dXAuc2ggY3JlYXRlcyB0aGUgazhzIHNlY3JldHMgaXRzZWxmXG4gICAgICAgIC8vIChhIGRlcGxveS9hd3MvZWtzLy5lbnYuZXhhbXBsZSArIHNlY3JldCBzdGVwIGlzIGEgc2V0dXAuc2ggVE9ETykuXG4gICAgICAgIGlmICgodGFyZ2V0ID09PSAnZG9ja2VyJyB8fCB0YXJnZXQgPT09ICdtaW5pa3ViZScpICYmIGVudkZpbGVNaXNzaW5nKGN3ZCwgc3BlYy5kaXIpKSB7XG4gICAgICAgICAgaWYgKGF3YWl0IGNvbmZpcm0oYFxcbiR7c3BlYy5kaXJ9Ly5lbnYgbm90IGZvdW5kIOKAlCBjcmVhdGUgaXQgZnJvbSAuZW52LmV4YW1wbGUgKGdlbmVyYXRlcyBzZWNyZXRzOyBlZGl0IGxhdGVyIGZvciBvcHRpb25hbCBpbnRlZ3JhdGlvbnMgbGlrZSBPQXV0aCk/YCwgb3B0aW9ucy55ZXMpKSB7XG4gICAgICAgICAgICBjb25zdCBuID0gY3JlYXRlRW52RmlsZShjd2QsIHNwZWMuZGlyKTtcbiAgICAgICAgICAgIHByaW50U3VjY2VzcyhgQ3JlYXRlZCAke3NwZWMuZGlyfS8uZW52IOKAlCAke259IHNlY3JldChzKSBnZW5lcmF0ZWQuYCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHByaW50V2FybmluZygnQ29udGludWluZyB3aXRob3V0IC5lbnYg4oCUIHNldHVwLnNoIHdpbGwgYWJvcnQgaWYgaXQgc3RheXMgbWlzc2luZy4nKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gN2MuIFJ1biB0aGUgZGVwbG95IHdpdGggYSBib3VuZGVkIGF1dG8tZml4ICsgcmV0cnkgbG9vcCAoc2VlIHJ1bkRlcGxveVdpdGhSZXRyeSkuXG4gICAgICAgIGNvbnN0IHsgc3VjY2VlZGVkLCBydW5QYXJhbXMgfSA9IGF3YWl0IHJ1bkRlcGxveVdpdGhSZXRyeShzcGVjLCB1cmwsIGN3ZCwgcGFyYW1zLCBhaU9wdHMsIG9wdGlvbnMpO1xuICAgICAgICBpZiAoIXN1Y2NlZWRlZCkgeyBwcm9jZXNzLmV4aXRDb2RlID0gMTsgcmV0dXJuOyB9XG4gICAgICAgIHByaW50U3VjY2VzcygnRGVwbG95IGNvbW1hbmQgY29tcGxldGVkLicpO1xuXG4gICAgICAgIC8vIDdkLiBTRVMgcG9zdC1kZXBsb3kgZ3VpZGFuY2UgKGFzeW5jIERLSU0gKyBzYW5kYm94ICsgYm91bmNlIHRvcGljKS5cbiAgICAgICAgLy8gU0VTIGlzIHByb3Zpc2lvbmVkIEJZIERFRkFVTFQgb24gQVdTIGRlcGxveXMsIHNvIHN1cmZhY2UgdGhlIGd1aWRhbmNlXG4gICAgICAgIC8vIGZvciBlYzIvZWtzIHVubGVzcyB0aGUgb3BlcmF0b3Igb3B0ZWQgb3V0IHdpdGggLS1uby1lbWFpbC5cbiAgICAgICAgY29uc3Qgc2VzUHJvdmlzaW9uZWQgPSAodGFyZ2V0ID09PSAnZWMyJyB8fCB0YXJnZXQgPT09ICdla3MnKSAmJiBydW5QYXJhbXMubm9FbWFpbCAhPT0gdHJ1ZTtcbiAgICAgICAgaWYgKHNlc1Byb3Zpc2lvbmVkKSB7XG4gICAgICAgICAgcHJpbnRTZWN0aW9uKCdTRVMg4oCUIG5leHQgc3RlcHMnKTtcbiAgICAgICAgICBmb3IgKGNvbnN0IGxpbmUgb2Ygc2VzUG9zdERlcGxveUd1aWRhbmNlKCkpIHByaW50SW5mbyhg4oCiICR7bGluZX1gKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIDdlLiBWZXJpZnkgaGVhbHRoIChDUkVBVEVfQ09NUExFVEUgIT0gc2VydmluZykuXG4gICAgICAgIGlmICh1cmwpIHtcbiAgICAgICAgICBwcmludFNlY3Rpb24oJ1ZlcmlmeWluZyBoZWFsdGgnKTtcbiAgICAgICAgICAvLyBNaW5pa3ViZSByZWFjaGVzIHRoZSBnYXRld2F5IHZpYSBhIGt1YmVjdGwgcG9ydC1mb3J3YXJkIHRoYXQgc2V0dXAuc2hcbiAgICAgICAgICAvLyBiYWNrZ3JvdW5kcyBhbmQgdGhhdCBjYW4gZGllL2ZhaWwgdG8gYmluZCDigJQgKHJlKXN0YXJ0IGl0IGJlZm9yZSBwb2xsaW5nXG4gICAgICAgICAgLy8gc28gd2UgZG9uJ3Qgc2l0IGF0IHRoZSBnYXRlIG9uIGEgZGVhZCBmb3J3YXJkIHdoaWxlIHRoZSBwb2RzIGFyZSBmaW5lLlxuICAgICAgICAgIGlmICh0YXJnZXQgPT09ICdtaW5pa3ViZScpIHtcbiAgICAgICAgICAgIGF3YWl0IGVuc3VyZU1pbmlrdWJlR2F0ZXdheSh1cmwsIHsgb25JbmZvOiAobSkgPT4gcHJpbnRJbmZvKG0pIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICBwcmludEluZm8oYFBvbGxpbmcgJHt1cmx9L2hlYWx0aCDigKZgKTtcbiAgICAgICAgICAvLyBGcmVzaCBFS1MgbGVnaXRpbWF0ZWx5IG5lZWRzIGxvbmdlciB0aGFuIHRoZSAzMDBzIGRlZmF1bHQ6IEthcnBlbnRlciBwcm92aXNpb25zXG4gICAgICAgICAgLy8gbm9kZXMsIH4xMCBzZXJ2aWNlcyBjb2xkLXB1bGwgaW1hZ2VzLCB0aGUgQUxCIHByb3Zpc2lvbnMgKH4yLTMgbWluKSwgYW5kIHRoZVxuICAgICAgICAgIC8vIFJvdXRlIDUzIGFsaWFzIHByb3BhZ2F0ZXMuIEdpdmUgaXQgMTUgbWluIGJlZm9yZSBzdXJmYWNpbmcgdGhlIG5vdC1yZWFjaGFibGUgbm90ZS5cbiAgICAgICAgICBjb25zdCBoZWFsdGhUaW1lb3V0TXMgPSB0YXJnZXQgPT09ICdla3MnID8gOTAwXzAwMCA6IHVuZGVmaW5lZDtcbiAgICAgICAgICBjb25zdCBoZWFsdGggPSBhd2FpdCB3YWl0SGVhbHRoeSh1cmwsIHsgdGltZW91dE1zOiBoZWFsdGhUaW1lb3V0TXMsIG9uVGljazogKG0pID0+IHByaW50SW5mbyhtKSB9KTtcbiAgICAgICAgICAvLyBHcmVlbiBvbmx5IHdoZW4gZnVsbHkgcmVhZHk7IGEgXCJoZWFsdGggT0sgYnV0IC9yZWFkeSBuZXZlciBjYW1lXCIgcHJvY2VlZC1cbiAgICAgICAgICAvLyBhbnl3YXkgc3RhdGUgaXMgaGVhbHRoeTp0cnVlIGJ1dCBkZWdyYWRlZCDihpIgd2FybiBzbyBpdCBkb2Vzbid0IHJlYWQgYXMgZG9uZS5cbiAgICAgICAgICAoaGVhbHRoLmhlYWx0aHkgJiYgaGVhbHRoLnJlYWR5ID8gcHJpbnRTdWNjZXNzIDogcHJpbnRXYXJuaW5nKShgJHtoZWFsdGgudXJsfSDigJQgJHtoZWFsdGguZGV0YWlsfWApO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gN2YuIFBvc3QtaW5zdGFsbCBzdGVwcyAocmVnaXN0ZXIgKyBvcHQtaW4gbG9hZHMsIHNtb2tlIHRlc3QsIGV2ZW50cywgY3VzdG9tKS5cbiAgICAgICAgLy8gU2VlIHJ1blBvc3RTdGVwcyDigJQgaXQgc3VyZmFjZXMgcmVnaXN0ZXIgKyB0aGUgZXZlbnRzIGJ1bmRsZSBhcyBtYW51YWwgaW4tVlBDXG4gICAgICAgIC8vIG5leHQtc3RlcHMgb24gRUMyL0VLUyBpbnN0ZWFkIG9mIGF1dG8tcnVubmluZyAoYW5kIGZhaWxpbmcpIHRoZW0gbG9jYWxseS5cbiAgICAgICAgLy8gYXV0b1J1biAoc2tpcCB0aGUgc2Vjb25kIGNvbmZpcm0pIG9ubHkgd2hlbiB0aGUgdXNlciBhY3R1YWxseSBQSUNLRUQgYSBsb2FkIGluIHRoZVxuICAgICAgICAvLyBpbnRlcmFjdGl2ZSBwcm9tcHQg4oCUIGRlY2xpbmluZyBldmVyeSBsb2FkIHNob3VsZG4ndCBzaWxlbnRseSBydW4gcmVnaXN0ZXIgdW5wcm9tcHRlZC5cbiAgICAgICAgY29uc3QgYXV0b1JhbkxvYWRzID0gd2lsbFByb21wdExvYWRzICYmIGVuYWJsZWRMb2FkSWRzLmxlbmd0aCA+IDA7XG4gICAgICAgIGF3YWl0IHJ1blBvc3RTdGVwcyhwb3N0U3RlcHMsIHNraXBwZWRTdGVwcywgdGFyZ2V0LCBjd2QsIGFkbWluRW52LCB7IHllczogb3B0aW9ucy55ZXMsIGF1dG9SdW46IGF1dG9SYW5Mb2FkcywgYXV0b0luaXQ6IHBvc3RTdGVwRmxhZ3MuYXV0b0luaXQgfSk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBoYW5kbGVFcnJvcihlcnJvciwgRVJST1JfQ09ERVMuR0VORVJBTCwge1xuICAgICAgICAgIGRlYnVnOiBwcm9ncmFtLm9wdHMoKS5kZWJ1ZyxcbiAgICAgICAgICBleGl0OiB0cnVlLFxuICAgICAgICAgIGNvbnRleHQ6IHsgY29tbWFuZDogJ3Byb3Zpc2lvbicsIGV4ZWN1dGlvbklkIH0sXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xufVxuIl19
|