@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,194 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { randomBytes } from 'crypto';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
// ESM has no synchronous named JSON import (Node only exposes a default export,
|
|
6
|
+
// and importing `../../package.json` would also fall outside `rootDir: src`), so
|
|
7
|
+
// read the version from package.json at load time relative to this module.
|
|
8
|
+
const { version } = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf-8'));
|
|
9
|
+
/**
|
|
10
|
+
* Application metadata
|
|
11
|
+
*/
|
|
12
|
+
export const APP_NAME = 'pipeline-manager';
|
|
13
|
+
export const APP_VERSION = version;
|
|
14
|
+
export const APP_DESCRIPTION = 'A CLI tool to manage pipelines and plugins';
|
|
15
|
+
/**
|
|
16
|
+
* Banner configuration for CLI startup
|
|
17
|
+
*/
|
|
18
|
+
export const BANNER_OPTIONS = {
|
|
19
|
+
font: 'Doom',
|
|
20
|
+
horizontalLayout: 'fitted',
|
|
21
|
+
getWidth: () => process.stdout.columns ?? 80,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Default timeouts (in milliseconds)
|
|
25
|
+
*/
|
|
26
|
+
export const TIMEOUTS = {
|
|
27
|
+
HTTP_REQUEST: 30000,
|
|
28
|
+
HEALTH_CHECK: 5000,
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* File size limits (in bytes)
|
|
32
|
+
*/
|
|
33
|
+
export const FILE_SIZE_LIMITS = {
|
|
34
|
+
PLUGIN: 4 * 1024 * 1024 * 1024, // 4GB
|
|
35
|
+
PIPELINE_PROPS: 10 * 1024 * 1024, // 10MB
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Environment variable names
|
|
39
|
+
*/
|
|
40
|
+
export const ENV_VARS = {
|
|
41
|
+
PLATFORM_TOKEN: 'PLATFORM_TOKEN',
|
|
42
|
+
PLATFORM_BASE_URL: 'PLATFORM_BASE_URL',
|
|
43
|
+
CLI_CONFIG_PATH: 'CLI_CONFIG_PATH',
|
|
44
|
+
TLS_REJECT_UNAUTHORIZED: 'TLS_REJECT_UNAUTHORIZED',
|
|
45
|
+
AWS_REGION: 'AWS_REGION',
|
|
46
|
+
DEBUG: 'DEBUG',
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Check if debug mode is enabled
|
|
50
|
+
*/
|
|
51
|
+
export function isDebugMode(options) {
|
|
52
|
+
return options?.debug ?? process.env.DEBUG === 'true';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Format file size for display
|
|
56
|
+
*/
|
|
57
|
+
export function formatFileSize(bytes) {
|
|
58
|
+
if (bytes === 0)
|
|
59
|
+
return '0 B';
|
|
60
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
61
|
+
const k = 1024;
|
|
62
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
63
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${units[i]}`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Format duration for display
|
|
67
|
+
*/
|
|
68
|
+
export function formatDuration(ms) {
|
|
69
|
+
if (ms < 1000)
|
|
70
|
+
return `${ms}ms`;
|
|
71
|
+
if (ms < 60000)
|
|
72
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
73
|
+
const minutes = Math.floor(ms / 60000);
|
|
74
|
+
const remainingSeconds = ((ms % 60000) / 1000).toFixed(0);
|
|
75
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Generate cryptographically random execution ID for request tracing.
|
|
79
|
+
*/
|
|
80
|
+
export function generateExecutionId() {
|
|
81
|
+
return randomBytes(4).toString('hex').toUpperCase();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Table display options (box-drawing characters)
|
|
85
|
+
*/
|
|
86
|
+
export const TABLE_OPTIONS = {
|
|
87
|
+
border: {
|
|
88
|
+
topBody: '─',
|
|
89
|
+
topJoin: '┬',
|
|
90
|
+
topLeft: '┌',
|
|
91
|
+
topRight: '┐',
|
|
92
|
+
bottomBody: '─',
|
|
93
|
+
bottomJoin: '┴',
|
|
94
|
+
bottomLeft: '└',
|
|
95
|
+
bottomRight: '┘',
|
|
96
|
+
bodyLeft: '│',
|
|
97
|
+
bodyRight: '│',
|
|
98
|
+
bodyJoin: '│',
|
|
99
|
+
joinBody: '─',
|
|
100
|
+
joinLeft: '├',
|
|
101
|
+
joinRight: '┤',
|
|
102
|
+
joinJoin: '┼',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Validate boolean string from CLI input
|
|
107
|
+
*/
|
|
108
|
+
export function validateBoolean(value, fieldName) {
|
|
109
|
+
const normalized = value.toLowerCase().trim();
|
|
110
|
+
if (['true', '1', 'yes', 'y'].includes(normalized))
|
|
111
|
+
return true;
|
|
112
|
+
if (['false', '0', 'no', 'n'].includes(normalized))
|
|
113
|
+
return false;
|
|
114
|
+
throw new Error(`Invalid boolean value for ${fieldName}: "${value}". Use true/false, yes/no, or 1/0.`);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Validate and parse a numeric CLI parameter within optional bounds.
|
|
118
|
+
*/
|
|
119
|
+
export function validateNumber(value, fieldName, min, max) {
|
|
120
|
+
// A missing value (e.g. a list command that forgot its commander default)
|
|
121
|
+
// would otherwise throw a cryptic `undefined.trim()` TypeError — fail with a
|
|
122
|
+
// clear validation message instead.
|
|
123
|
+
if (value === undefined || value === null || value === '') {
|
|
124
|
+
throw new Error(`Invalid ${fieldName}: a value is required`);
|
|
125
|
+
}
|
|
126
|
+
// Reject partial parses ("12abc" -> 12) and non-integers ("5.9" -> 5) that
|
|
127
|
+
// `parseInt` would otherwise accept silently.
|
|
128
|
+
if (typeof value === 'string' && !/^-?\d+$/.test(value.trim())) {
|
|
129
|
+
throw new Error(`Invalid ${fieldName}: must be a whole number`);
|
|
130
|
+
}
|
|
131
|
+
const num = typeof value === 'number' ? value : parseInt(value.trim(), 10);
|
|
132
|
+
if (!Number.isInteger(num)) {
|
|
133
|
+
throw new Error(`Invalid ${fieldName}: must be a whole number`);
|
|
134
|
+
}
|
|
135
|
+
if (min !== undefined && num < min) {
|
|
136
|
+
throw new Error(`Invalid ${fieldName}: must be >= ${min}`);
|
|
137
|
+
}
|
|
138
|
+
if (max !== undefined && num > max) {
|
|
139
|
+
throw new Error(`Invalid ${fieldName}: must be <= ${max}`);
|
|
140
|
+
}
|
|
141
|
+
return num;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Validate sort parameter format (e.g., "createdAt:desc").
|
|
145
|
+
* Returns the validated sort string, or the default if invalid.
|
|
146
|
+
*/
|
|
147
|
+
const SORT_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*:(asc|desc)$/;
|
|
148
|
+
export function validateSort(value) {
|
|
149
|
+
if (!value)
|
|
150
|
+
return undefined;
|
|
151
|
+
if (!SORT_PATTERN.test(value)) {
|
|
152
|
+
throw new Error(`Invalid sort format: "${value}". Expected "field:asc" or "field:desc".`);
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Reject strings containing shell metacharacters.
|
|
158
|
+
* Used to sanitize CLI inputs before passing to shell commands.
|
|
159
|
+
*/
|
|
160
|
+
const SHELL_UNSAFE = /[;&|`$(){}[\]<>!#~*?\\\n\r]/;
|
|
161
|
+
export function assertShellSafe(value, fieldName, opts = {}) {
|
|
162
|
+
if (SHELL_UNSAFE.test(value)) {
|
|
163
|
+
// `redactValue` is for secrets (tokens, passwords) — never echo the offending
|
|
164
|
+
// value into an error/log line when the field is sensitive.
|
|
165
|
+
const detail = opts.redactValue ? '' : `: "${value}"`;
|
|
166
|
+
throw new Error(`${fieldName} contains unsafe characters${detail}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* POSIX single-quote a value (escaping embedded single quotes) for safe shell
|
|
171
|
+
* interpolation. Single source of truth — drift in a shell-quoting helper is
|
|
172
|
+
* security-relevant, so both agent modules import this rather than re-declaring it.
|
|
173
|
+
*/
|
|
174
|
+
export function shellQuote(value) {
|
|
175
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Canonical default pipeline name when none is given — MUST mirror pipeline-core's
|
|
179
|
+
* pipeline-configuration.ts (`${sanitize(org)}-${sanitize(project)}-pipeline`,
|
|
180
|
+
* non-alphanumeric → '_', lowercased), so the CLI's create and registry paths
|
|
181
|
+
* derive the SAME name the CDK builder actually deploys (a mismatch breaks
|
|
182
|
+
* StartPipelineExecution against the registered name).
|
|
183
|
+
*/
|
|
184
|
+
export function defaultPipelineName(organization, project) {
|
|
185
|
+
const sanitize = (s) => s.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase();
|
|
186
|
+
return `${sanitize(organization)}-${sanitize(project)}-pipeline`;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* CloudFormation stack name for the event-ingestion infra. Shared so `setup-events`
|
|
190
|
+
* (which deploys it) and `redrive-events` (which resolves the DLQ/queue from it)
|
|
191
|
+
* can never drift apart.
|
|
192
|
+
*/
|
|
193
|
+
export const EVENTS_STACK_NAME = 'pipeline-builder-events';
|
|
194
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmNvbnN0YW50cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWcvY2xpLmNvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDckMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUV2QyxnRkFBZ0Y7QUFDaEYsaUZBQWlGO0FBQ2pGLDJFQUEyRTtBQUMzRSxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FDNUIsWUFBWSxDQUFDLElBQUksR0FBRyxDQUFDLG9CQUFvQixFQUFFLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxDQUMvQyxDQUFDO0FBRXpCOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLGtCQUFrQixDQUFDO0FBQzNDLE1BQU0sQ0FBQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUM7QUFDbkMsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLDRDQUE0QyxDQUFDO0FBRTVFOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHO0lBQzVCLElBQUksRUFBRSxNQUFlO0lBQ3JCLGdCQUFnQixFQUFFLFFBQWlCO0lBQ25DLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLE9BQU8sSUFBSSxFQUFFO0NBQ3BDLENBQUM7QUFPWDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFFBQVEsR0FBRztJQUN0QixZQUFZLEVBQUUsS0FBSztJQUNuQixZQUFZLEVBQUUsSUFBSTtDQUNWLENBQUM7QUFFWDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHO0lBQzlCLE1BQU0sRUFBRSxDQUFDLEdBQUcsSUFBSSxHQUFHLElBQUksR0FBRyxJQUFJ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QUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sS0FBSyxHQUFHLENBQUM7UUFDdEQsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLFNBQVMsOEJBQThCLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDdEUsQ0FBQztBQUNILENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLFVBQVUsQ0FBQyxLQUFhO0lBQ3RDLE9BQU8sSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDO0FBQzdDLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsbUJBQW1CLENBQUMsWUFBb0IsRUFBRSxPQUFlO0lBQ3ZFLE1BQU0sUUFBUSxHQUFHLENBQUMsQ0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLGVBQWUsRUFBRSxHQUFHLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUM5RSxPQUFPLEdBQUcsUUFBUSxDQUFDLFlBQVksQ0FBQyxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDO0FBQ25FLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcseUJBQXlCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgcmFuZG9tQnl0ZXMgfSBmcm9tICdjcnlwdG8nO1xuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnbm9kZTpmcyc7XG5cbi8vIEVTTSBoYXMgbm8gc3luY2hyb25vdXMgbmFtZWQgSlNPTiBpbXBvcnQgKE5vZGUgb25seSBleHBvc2VzIGEgZGVmYXVsdCBleHBvcnQsXG4vLyBhbmQgaW1wb3J0aW5nIGAuLi8uLi9wYWNrYWdlLmpzb25gIHdvdWxkIGFsc28gZmFsbCBvdXRzaWRlIGByb290RGlyOiBzcmNgKSwgc29cbi8vIHJlYWQgdGhlIHZlcnNpb24gZnJvbSBwYWNrYWdlLmpzb24gYXQgbG9hZCB0aW1lIHJlbGF0aXZlIHRvIHRoaXMgbW9kdWxlLlxuY29uc3QgeyB2ZXJzaW9uIH0gPSBKU09OLnBhcnNlKFxuICByZWFkRmlsZVN5bmMobmV3IFVSTCgnLi4vLi4vcGFja2FnZS5qc29uJywgaW1wb3J0Lm1ldGEudXJsKSwgJ3V0Zi04JyksXG4pIGFzIHsgdmVyc2lvbjogc3RyaW5nIH07XG5cbi8qKlxuICogQXBwbGljYXRpb24gbWV0YWRhdGFcbiAqL1xuZXhwb3J0IGNvbnN0IEFQUF9OQU1FID0gJ3BpcGVsaW5lLW1hbmFnZXInO1xuZXhwb3J0IGNvbnN0IEFQUF9WRVJTSU9OID0gdmVyc2lvbjtcbmV4cG9ydCBjb25zdCBBUFBfREVTQ1JJUFRJT04gPSAnQSBDTEkgdG9vbCB0byBtYW5hZ2UgcGlwZWxpbmVzIGFuZCBwbHVnaW5zJztcblxuLyoqXG4gKiBCYW5uZXIgY29uZmlndXJhdGlvbiBmb3IgQ0xJIHN0YXJ0dXBcbiAqL1xuZXhwb3J0IGNvbnN0IEJBTk5FUl9PUFRJT05TID0ge1xuICBmb250OiAnRG9vbScgYXMgY29uc3QsXG4gIGhvcml6b250YWxMYXlvdXQ6ICdmaXR0ZWQnIGFzIGNvbnN0LFxuICBnZXRXaWR0aDogKCkgPT4gcHJvY2Vzcy5zdGRvdXQuY29sdW1ucyA/PyA4MCxcbn0gYXMgY29uc3Q7XG5cbi8qKlxuICogT3V0cHV0IGZvcm1hdCB0eXBlc1xuICovXG5leHBvcnQgdHlwZSBPdXRwdXRGb3JtYXQgPSAndGFibGUnIHwgJ2pzb24nIHwgJ3lhbWwnIHwgJ2Nzdic7XG5cbi8qKlxuICogRGVmYXVsdCB0aW1lb3V0cyAoaW4gbWlsbGlzZWNvbmRzKVxuICovXG5leHBvcnQgY29uc3QgVElNRU9VVFMgPSB7XG4gIEhUVFBfUkVRVUVTVDogMzAwMDAsXG4gIEhFQUxUSF9DSEVDSzogNTAwMCxcbn0gYXMgY29uc3Q7XG5cbi8qKlxuICogRmlsZSBzaXplIGxpbWl0cyAoaW4gYnl0ZXMpXG4gKi9cbmV4cG9ydCBjb25zdCBGSUxFX1NJWkVfTElNSVRTID0ge1xuICBQTFVHSU46IDQgKiAxMDI0ICogMTAyNCAqIDEwMjQsIC8vIDRHQlxuICBQSVBFTElORV9QUk9QUzogMTAgKiAxMDI0ICogMTAyNCwgLy8gMTBNQlxufSBhcyBjb25zdDtcblxuLyoqXG4gKiBFbnZpcm9ubWVudCB2YXJpYWJsZSBuYW1lc1xuICovXG5leHBvcnQgY29uc3QgRU5WX1ZBUlMgPSB7XG4gIFBMQVRGT1JNX1RPS0VOOiAnUExBVEZPUk1fVE9LRU4nLFxuICBQTEFURk9STV9CQVNFX1VSTDogJ1BMQVRGT1JNX0JBU0VfVVJMJyxcbiAgQ0xJX0NPTkZJR19QQVRIOiAnQ0xJX0NPTkZJR19QQVRIJyxcbiAgVExTX1JFSkVDVF9VTkFVVEhPUklaRUQ6ICdUTFNfUkVKRUNUX1VOQVVUSE9SSVpFRCcsXG4gIEFXU19SRUdJT046ICdBV1NfUkVHSU9OJyxcbiAgREVCVUc6ICdERUJVRycsXG59IGFzIGNvbnN0O1xuXG4vKipcbiAqIENoZWNrIGlmIGRlYnVnIG1vZGUgaXMgZW5hYmxlZFxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNEZWJ1Z01vZGUob3B0aW9ucz86IHsgZGVidWc/OiBib29sZWFuIH0pOiBib29sZWFuIHtcbiAgcmV0dXJuIG9wdGlvbnM/LmRlYnVnID8/IHByb2Nlc3MuZW52LkRFQlVHID09PSAndHJ1ZSc7XG59XG5cbi8qKlxuICogRm9ybWF0IGZpbGUgc2l6ZSBmb3IgZGlzcGxheVxuICovXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0RmlsZVNpemUoYnl0ZXM6IG51bWJlcik6IHN0cmluZyB7XG4gIGlmIChieXRlcyA9PT0gMCkgcmV0dXJuICcwIEInO1xuICBjb25zdCB1bml0cyA9IFsnQicsICdLQicsICdNQicsICdHQicsICdUQiddO1xuICBjb25zdCBrID0gMTAyNDtcbiAgY29uc3QgaSA9IE1hdGguZmxvb3IoTWF0aC5sb2coYnl0ZXMpIC8gTWF0aC5sb2coaykpO1xuICByZXR1cm4gYCR7KGJ5dGVzIC8gTWF0aC5wb3coaywgaSkpLnRvRml4ZWQoMil9ICR7dW5pdHNbaV19YDtcbn1cblxuLyoqXG4gKiBGb3JtYXQgZHVyYXRpb24gZm9yIGRpc3BsYXlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdER1cmF0aW9uKG1zOiBudW1iZXIpOiBzdHJpbmcge1xuICBpZiAobXMgPCAxMDAwKSByZXR1cm4gYCR7bXN9bXNgO1xuICBpZiAobXMgPCA2MDAwMCkgcmV0dXJuIGAkeyhtcyAvIDEwMDApLnRvRml4ZWQoMil9c2A7XG4gIGNvbnN0IG1pbnV0ZXMgPSBNYXRoLmZsb29yKG1zIC8gNjAwMDApO1xuICBjb25zdCByZW1haW5pbmdTZWNvbmRzID0gKChtcyAlIDYwMDAwKSAvIDEwMDApLnRvRml4ZWQoMCk7XG4gIHJldHVybiBgJHttaW51dGVzfW0gJHtyZW1haW5pbmdTZWNvbmRzfXNgO1xufVxuXG4vKipcbiAqIEdlbmVyYXRlIGNyeXB0b2dyYXBoaWNhbGx5IHJhbmRvbSBleGVjdXRpb24gSUQgZm9yIHJlcXVlc3QgdHJhY2luZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlRXhlY3V0aW9uSWQoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHJhbmRvbUJ5dGVzKDQpLnRvU3RyaW5nKCdoZXgnKS50b1VwcGVyQ2FzZSgpO1xufVxuXG4vKipcbiAqIFRhYmxlIGRpc3BsYXkgb3B0aW9ucyAoYm94LWRyYXdpbmcgY2hhcmFjdGVycylcbiAqL1xuZXhwb3J0IGNvbnN0IFRBQkxFX09QVElPTlMgPSB7XG4gIGJvcmRlcjoge1xuICAgIHRvcEJvZHk6ICfilIAnLFxuICAgIHRvcEpvaW46ICfilKwnLFxuICAgIHRvcExlZnQ6ICfilIwnLFxuICAgIHRvcFJpZ2h0OiAn4pSQJyxcbiAgICBib3R0b21Cb2R5OiAn4pSAJyxcbiAgICBib3R0b21Kb2luOiAn4pS0JyxcbiAgICBib3R0b21MZWZ0OiAn4pSUJyxcbiAgICBib3R0b21SaWdodDogJ+KUmCcsXG4gICAgYm9keUxlZnQ6ICfilIInLFxuICAgIGJvZHlSaWdodDogJ+KUgicsXG4gICAgYm9keUpvaW46ICfilIInLFxuICAgIGpvaW5Cb2R5OiAn4pSAJyxcbiAgICBqb2luTGVmdDogJ+KUnCcsXG4gICAgam9pblJpZ2h0OiAn4pSkJyxcbiAgICBqb2luSm9pbjogJ+KUvCcsXG4gIH0sXG59IGFzIGNvbnN0O1xuXG4vKipcbiAqIFZhbGlkYXRlIGJvb2xlYW4gc3RyaW5nIGZyb20gQ0xJIGlucHV0XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUJvb2xlYW4odmFsdWU6IHN0cmluZywgZmllbGROYW1lOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3Qgbm9ybWFsaXplZCA9IHZhbHVlLnRvTG93ZXJDYXNlKCkudHJpbSgpO1xuICBpZiAoWyd0cnVlJywgJzEnLCAneWVzJywgJ3knXS5pbmNsdWRlcyhub3JtYWxpemVkKSkgcmV0dXJuIHRydWU7XG4gIGlmIChbJ2ZhbHNlJywgJzAnLCAnbm8nLCAnbiddLmluY2x1ZGVzKG5vcm1hbGl6ZWQpKSByZXR1cm4gZmFsc2U7XG4gIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBib29sZWFuIHZhbHVlIGZvciAke2ZpZWxkTmFtZX06IFwiJHt2YWx1ZX1cIi4gVXNlIHRydWUvZmFsc2UsIHllcy9ubywgb3IgMS8wLmApO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIGFuZCBwYXJzZSBhIG51bWVyaWMgQ0xJIHBhcmFtZXRlciB3aXRoaW4gb3B0aW9uYWwgYm91bmRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVOdW1iZXIodmFsdWU6IHN0cmluZyB8IG51bWJlciB8IHVuZGVmaW5lZCB8IG51bGwsIGZpZWxkTmFtZTogc3RyaW5nLCBtaW4/OiBudW1iZXIsIG1heD86IG51bWJlcik6IG51bWJlciB7XG4gIC8vIEEgbWlzc2luZyB2YWx1ZSAoZS5nLiBhIGxpc3QgY29tbWFuZCB0aGF0IGZvcmdvdCBpdHMgY29tbWFuZGVyIGRlZmF1bHQpXG4gIC8vIHdvdWxkIG90aGVyd2lzZSB0aHJvdyBhIGNyeXB0aWMgYHVuZGVmaW5lZC50cmltKClgIFR5cGVFcnJvciDigJQgZmFpbCB3aXRoIGFcbiAgLy8gY2xlYXIgdmFsaWRhdGlvbiBtZXNzYWdlIGluc3RlYWQuXG4gIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHZhbHVlID09PSBudWxsIHx8IHZhbHVlID09PSAnJykge1xuICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCAke2ZpZWxkTmFtZX06IGEgdmFsdWUgaXMgcmVxdWlyZWRgKTtcbiAgfVxuICAvLyBSZWplY3QgcGFydGlhbCBwYXJzZXMgKFwiMTJhYmNcIiAtPiAxMikgYW5kIG5vbi1pbnRlZ2VycyAoXCI1LjlcIiAtPiA1KSB0aGF0XG4gIC8vIGBwYXJzZUludGAgd291bGQgb3RoZXJ3aXNlIGFjY2VwdCBzaWxlbnRseS5cbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiYgIS9eLT9cXGQrJC8udGVzdCh2YWx1ZS50cmltKCkpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkICR7ZmllbGROYW1lfTogbXVzdCBiZSBhIHdob2xlIG51bWJlcmApO1xuICB9XG4gIGNvbnN0IG51bSA9IHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicgPyB2YWx1ZSA6IHBhcnNlSW50KHZhbHVlLnRyaW0oKSwgMTApO1xuICBpZiAoIU51bWJlci5pc0ludGVnZXIobnVtKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCAke2ZpZWxkTmFtZX06IG11c3QgYmUgYSB3aG9sZSBudW1iZXJgKTtcbiAgfVxuICBpZiAobWluICE9PSB1bmRlZmluZWQgJiYgbnVtIDwgbWluKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkICR7ZmllbGROYW1lfTogbXVzdCBiZSA+PSAke21pbn1gKTtcbiAgfVxuICBpZiAobWF4ICE9PSB1bmRlZmluZWQgJiYgbnVtID4gbWF4KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkICR7ZmllbGROYW1lfTogbXVzdCBiZSA8PSAke21heH1gKTtcbiAgfVxuICByZXR1cm4gbnVtO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIHNvcnQgcGFyYW1ldGVyIGZvcm1hdCAoZS5nLiwgXCJjcmVhdGVkQXQ6ZGVzY1wiKS5cbiAqIFJldHVybnMgdGhlIHZhbGlkYXRlZCBzb3J0IHN0cmluZywgb3IgdGhlIGRlZmF1bHQgaWYgaW52YWxpZC5cbiAqL1xuY29uc3QgU09SVF9QQVRURVJOID0gL15bYS16QS1aX11bYS16QS1aMC05X10qOihhc2N8ZGVzYykkLztcblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlU29ydCh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCF2YWx1ZSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgaWYgKCFTT1JUX1BBVFRFUk4udGVzdCh2YWx1ZSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgc29ydCBmb3JtYXQ6IFwiJHt2YWx1ZX1cIi4gRXhwZWN0ZWQgXCJmaWVsZDphc2NcIiBvciBcImZpZWxkOmRlc2NcIi5gKTtcbiAgfVxuICByZXR1cm4gdmFsdWU7XG59XG5cbi8qKlxuICogUmVqZWN0IHN0cmluZ3MgY29udGFpbmluZyBzaGVsbCBtZXRhY2hhcmFjdGVycy5cbiAqIFVzZWQgdG8gc2FuaXRpemUgQ0xJIGlucHV0cyBiZWZvcmUgcGFzc2luZyB0byBzaGVsbCBjb21tYW5kcy5cbiAqL1xuY29uc3QgU0hFTExfVU5TQUZFID0gL1s7JnxgJCgpe31bXFxdPD4hI34qP1xcXFxcXG5cXHJdLztcblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydFNoZWxsU2FmZShcbiAgdmFsdWU6IHN0cmluZyxcbiAgZmllbGROYW1lOiBzdHJpbmcsXG4gIG9wdHM6IHsgcmVkYWN0VmFsdWU/OiBib29sZWFuIH0gPSB7fSxcbik6IHZvaWQge1xuICBpZiAoU0hFTExfVU5TQUZFLnRlc3QodmFsdWUpKSB7XG4gICAgLy8gYHJlZGFjdFZhbHVlYCBpcyBmb3Igc2VjcmV0cyAodG9rZW5zLCBwYXNzd29yZHMpIOKAlCBuZXZlciBlY2hvIHRoZSBvZmZlbmRpbmdcbiAgICAvLyB2YWx1ZSBpbnRvIGFuIGVycm9yL2xvZyBsaW5lIHdoZW4gdGhlIGZpZWxkIGlzIHNlbnNpdGl2ZS5cbiAgICBjb25zdCBkZXRhaWwgPSBvcHRzLnJlZGFjdFZhbHVlID8gJycgOiBgOiBcIiR7dmFsdWV9XCJgO1xuICAgIHRocm93IG5ldyBFcnJvcihgJHtmaWVsZE5hbWV9IGNvbnRhaW5zIHVuc2FmZSBjaGFyYWN0ZXJzJHtkZXRhaWx9YCk7XG4gIH1cbn1cblxuLyoqXG4gKiBQT1NJWCBzaW5nbGUtcXVvdGUgYSB2YWx1ZSAoZXNjYXBpbmcgZW1iZWRkZWQgc2luZ2xlIHF1b3RlcykgZm9yIHNhZmUgc2hlbGxcbiAqIGludGVycG9sYXRpb24uIFNpbmdsZSBzb3VyY2Ugb2YgdHJ1dGgg4oCUIGRyaWZ0IGluIGEgc2hlbGwtcXVvdGluZyBoZWxwZXIgaXNcbiAqIHNlY3VyaXR5LXJlbGV2YW50LCBzbyBib3RoIGFnZW50IG1vZHVsZXMgaW1wb3J0IHRoaXMgcmF0aGVyIHRoYW4gcmUtZGVjbGFyaW5nIGl0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2hlbGxRdW90ZSh2YWx1ZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAnJHt2YWx1ZS5yZXBsYWNlKC8nL2csIFwiJ1xcXFwnJ1wiKX0nYDtcbn1cblxuLyoqXG4gKiBDYW5vbmljYWwgZGVmYXVsdCBwaXBlbGluZSBuYW1lIHdoZW4gbm9uZSBpcyBnaXZlbiDigJQgTVVTVCBtaXJyb3IgcGlwZWxpbmUtY29yZSdzXG4gKiBwaXBlbGluZS1jb25maWd1cmF0aW9uLnRzIChgJHtzYW5pdGl6ZShvcmcpfS0ke3Nhbml0aXplKHByb2plY3QpfS1waXBlbGluZWAsXG4gKiBub24tYWxwaGFudW1lcmljIOKGkiAnXycsIGxvd2VyY2FzZWQpLCBzbyB0aGUgQ0xJJ3MgY3JlYXRlIGFuZCByZWdpc3RyeSBwYXRoc1xuICogZGVyaXZlIHRoZSBTQU1FIG5hbWUgdGhlIENESyBidWlsZGVyIGFjdHVhbGx5IGRlcGxveXMgKGEgbWlzbWF0Y2ggYnJlYWtzXG4gKiBTdGFydFBpcGVsaW5lRXhlY3V0aW9uIGFnYWluc3QgdGhlIHJlZ2lzdGVyZWQgbmFtZSkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWZhdWx0UGlwZWxpbmVOYW1lKG9yZ2FuaXphdGlvbjogc3RyaW5nLCBwcm9qZWN0OiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBzYW5pdGl6ZSA9IChzOiBzdHJpbmcpID0+IHMucmVwbGFjZSgvW15hLXpBLVowLTldL2csICdfJykudG9Mb3dlckNhc2UoKTtcbiAgcmV0dXJuIGAke3Nhbml0aXplKG9yZ2FuaXphdGlvbil9LSR7c2FuaXRpemUocHJvamVjdCl9LXBpcGVsaW5lYDtcbn1cblxuLyoqXG4gKiBDbG91ZEZvcm1hdGlvbiBzdGFjayBuYW1lIGZvciB0aGUgZXZlbnQtaW5nZXN0aW9uIGluZnJhLiBTaGFyZWQgc28gYHNldHVwLWV2ZW50c2BcbiAqICh3aGljaCBkZXBsb3lzIGl0KSBhbmQgYHJlZHJpdmUtZXZlbnRzYCAod2hpY2ggcmVzb2x2ZXMgdGhlIERMUS9xdWV1ZSBmcm9tIGl0KVxuICogY2FuIG5ldmVyIGRyaWZ0IGFwYXJ0LlxuICovXG5leHBvcnQgY29uc3QgRVZFTlRTX1NUQUNLX05BTUUgPSAncGlwZWxpbmUtYnVpbGRlci1ldmVudHMnO1xuIl19
|
package/dist/config.yml
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# CLI Configuration File
|
|
2
|
+
# Save this as config.yml in your project root or specify with CLI_CONFIG_PATH
|
|
3
|
+
|
|
4
|
+
# ============================================================================
|
|
5
|
+
# API Configuration
|
|
6
|
+
# ============================================================================
|
|
7
|
+
api:
|
|
8
|
+
# Base URL for the API
|
|
9
|
+
# Can be overridden with PLATFORM_URL environment variable
|
|
10
|
+
baseUrl: https://localhost:8443
|
|
11
|
+
|
|
12
|
+
# Request timeout in milliseconds
|
|
13
|
+
timeout: 30000
|
|
14
|
+
|
|
15
|
+
# SSL/TLS Certificate Validation
|
|
16
|
+
# Set to false to disable certificate validation (DEVELOPMENT ONLY)
|
|
17
|
+
# Default: true (secure)
|
|
18
|
+
#
|
|
19
|
+
# WARNING: Setting this to false disables certificate validation and makes
|
|
20
|
+
# your application vulnerable to man-in-the-middle attacks. Only use this
|
|
21
|
+
# in development environments with self-signed certificates.
|
|
22
|
+
rejectUnauthorized: true
|
|
23
|
+
|
|
24
|
+
# API Endpoints
|
|
25
|
+
#
|
|
26
|
+
# Pipeline Endpoints:
|
|
27
|
+
# pipelineUrl - Single pipeline operations (GET by ID, POST)
|
|
28
|
+
# Used by: get-pipeline, create-pipeline
|
|
29
|
+
# Format: /api/pipeline/{id} or /api/pipeline
|
|
30
|
+
#
|
|
31
|
+
# pipelineListUrl - List/query multiple pipelines with filters
|
|
32
|
+
# Used by: list-pipelines
|
|
33
|
+
# Format: /api/pipelines?filter1=value1&filter2=value2
|
|
34
|
+
# Supports filters: id, accessModifier, isDefault, isActive,
|
|
35
|
+
# project, projectPattern, organizationPattern,
|
|
36
|
+
# limit, offset, sort
|
|
37
|
+
#
|
|
38
|
+
# Plugin Endpoints:
|
|
39
|
+
# pluginUrl - Single plugin operations (GET by ID)
|
|
40
|
+
# Used by: get-plugin
|
|
41
|
+
# Format: /api/plugin/{id}
|
|
42
|
+
#
|
|
43
|
+
# pluginListUrl - List/query multiple plugins with filters
|
|
44
|
+
# Used by: list-plugins
|
|
45
|
+
# Format: /api/plugins?filter1=value1&filter2=value2
|
|
46
|
+
# Supports filters: id, accessModifier, isDefault, isActive,
|
|
47
|
+
# name, namePattern, version, versionRange,
|
|
48
|
+
# imageTag, limit, offset, sort
|
|
49
|
+
#
|
|
50
|
+
# pluginUploadUrl - Plugin upload endpoint
|
|
51
|
+
# Used by: upload-plugin
|
|
52
|
+
# Format: /api/plugin/upload
|
|
53
|
+
#
|
|
54
|
+
# Note: Singular vs Plural URLs
|
|
55
|
+
# - Singular (/api/plugin, /api/pipeline) - Single resource operations
|
|
56
|
+
# - Plural (/api/plugins, /api/pipelines) - List/query operations
|
|
57
|
+
pipelineUrl: /api/pipeline
|
|
58
|
+
pipelineListUrl: /api/pipelines
|
|
59
|
+
pluginUrl: /api/plugin
|
|
60
|
+
pluginListUrl: /api/plugins
|
|
61
|
+
pluginUploadUrl: /api/plugin/upload
|
|
62
|
+
|
|
63
|
+
# ============================================================================
|
|
64
|
+
# AUTHENTICATION
|
|
65
|
+
# ============================================================================
|
|
66
|
+
# Authentication is handled via environment variable ONLY.
|
|
67
|
+
# DO NOT include auth configuration in this file.
|
|
68
|
+
#
|
|
69
|
+
# Set your token with:
|
|
70
|
+
# export PLATFORM_TOKEN='your-token-here'
|
|
71
|
+
#
|
|
72
|
+
# The token should NEVER be committed to version control.
|
|
73
|
+
# ============================================================================
|
|
74
|
+
|
|
75
|
+
# ============================================================================
|
|
76
|
+
# ENVIRONMENT VARIABLES
|
|
77
|
+
# ============================================================================
|
|
78
|
+
# The following environment variables are used by the CLI:
|
|
79
|
+
#
|
|
80
|
+
# REQUIRED:
|
|
81
|
+
# PLATFORM_TOKEN - Authentication token for API access
|
|
82
|
+
# Example: export PLATFORM_TOKEN='eyJhbG...'
|
|
83
|
+
#
|
|
84
|
+
# OPTIONAL:
|
|
85
|
+
# PLATFORM_URL - Override api.baseUrl
|
|
86
|
+
# Example: export PLATFORM_URL='https://api.example.com'
|
|
87
|
+
#
|
|
88
|
+
# CLI_CONFIG_PATH - Path to this config file
|
|
89
|
+
# Example: export CLI_CONFIG_PATH='/path/to/config.yml'
|
|
90
|
+
#
|
|
91
|
+
# TLS_REJECT_UNAUTHORIZED - Set to '0' to disable cert validation
|
|
92
|
+
# Example: export TLS_REJECT_UNAUTHORIZED='0'
|
|
93
|
+
#
|
|
94
|
+
# ============================================================================
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-renew-handler.d.ts","sourceRoot":"","sources":["../../src/lambda/token-renew-handler.ts"],"names":[],"mappings":"AA4DA,eAAO,MAAM,OAAO,QAAa,OAAO,CAAC,IAAI,CAyE5C,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Lambda handler that re-mints the platform JWT in Secrets Manager so it never
|
|
5
|
+
* lapses. Deployed + scheduled (once a day) by `pipeline-manager infra store-token
|
|
6
|
+
* --schedule`. Rather than re-implementing the token flow, it is a thin
|
|
7
|
+
* orchestrator that reuses the tested CLI — each run:
|
|
8
|
+
*
|
|
9
|
+
* 1. Point the `@pipeline-builder` npm scope at public npm.
|
|
10
|
+
* 2. Download `@pipeline-builder/pipeline-manager` from npm into /tmp.
|
|
11
|
+
* 3. Read the current platform JWT from the secret.
|
|
12
|
+
* 4. Run `pipeline-manager infra store-token`, which mints a fresh long-lived token
|
|
13
|
+
* via /api/user/generate-token and writes it back to the same secret.
|
|
14
|
+
*
|
|
15
|
+
* `/tmp` is the only writable path in Lambda, so npm's HOME/cache/prefix all live
|
|
16
|
+
* there. store-token does NOT deploy the renewal stack by default (no --schedule),
|
|
17
|
+
* so the renewal run never redeploys/recurses into its own stack.
|
|
18
|
+
*
|
|
19
|
+
* Env: PLATFORM_SECRET_NAME, PLATFORM_BASE_URL, RENEW_DAYS (default 30),
|
|
20
|
+
* PIPELINE_MANAGER_VERSION (default "latest"), PLATFORM_VERIFY_SSL ("false"
|
|
21
|
+
* to disable TLS verification).
|
|
22
|
+
*/
|
|
23
|
+
import { execFileSync } from 'node:child_process';
|
|
24
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
25
|
+
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
|
|
26
|
+
const PM_PACKAGE = '@pipeline-builder/pipeline-manager';
|
|
27
|
+
const TMP = '/tmp';
|
|
28
|
+
const PM_PREFIX = `${TMP}/pm`;
|
|
29
|
+
const NPM_CACHE = `${TMP}/.npm`;
|
|
30
|
+
const NPMRC = `${TMP}/.npmrc`;
|
|
31
|
+
const CLI = `${PM_PREFIX}/node_modules/${PM_PACKAGE}/dist/cli.js`;
|
|
32
|
+
function required(name) {
|
|
33
|
+
const v = process.env[name];
|
|
34
|
+
if (!v)
|
|
35
|
+
throw new Error(`${name} environment variable is required`);
|
|
36
|
+
return v;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read the `scope` claim from a JWT payload without verifying the signature
|
|
40
|
+
* (the platform re-verifies on use; this only decides whether to re-mint WITH
|
|
41
|
+
* a scope). A scoped credential (e.g. `reporting:ingest`) must renew with the
|
|
42
|
+
* same scope or the fresh token would be a full-privilege platform token and
|
|
43
|
+
* the scoped consumer's calls would break. Returns undefined for an unscoped
|
|
44
|
+
* (platform) token or any decode failure.
|
|
45
|
+
*/
|
|
46
|
+
function decodeJwtScope(jwt) {
|
|
47
|
+
try {
|
|
48
|
+
const payload = jwt.split('.')[1];
|
|
49
|
+
if (!payload)
|
|
50
|
+
return undefined;
|
|
51
|
+
const json = Buffer.from(payload.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8');
|
|
52
|
+
const claims = JSON.parse(json);
|
|
53
|
+
return typeof claims.scope === 'string' && claims.scope ? claims.scope : undefined;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export const handler = async () => {
|
|
60
|
+
const secretName = required('PLATFORM_SECRET_NAME');
|
|
61
|
+
const platformUrl = required('PLATFORM_BASE_URL').replace(/\/$/, '');
|
|
62
|
+
const days = process.env.RENEW_DAYS || '30';
|
|
63
|
+
const version = process.env.PIPELINE_MANAGER_VERSION || 'latest';
|
|
64
|
+
const region = process.env.AWS_REGION || 'us-east-1';
|
|
65
|
+
// npm in Lambda can only write under /tmp; HOME/cache/userconfig point there.
|
|
66
|
+
const npmEnv = { ...process.env, HOME: TMP, npm_config_cache: NPM_CACHE };
|
|
67
|
+
// 1. Scope config: resolve @pipeline-builder from public npm regardless of any
|
|
68
|
+
// inherited registry. Written as a userconfig file (env mapping of scoped
|
|
69
|
+
// keys is unreliable).
|
|
70
|
+
writeFileSync(NPMRC, '@pipeline-builder:registry=https://registry.npmjs.org/\n');
|
|
71
|
+
// 2. Download pipeline-manager into /tmp.
|
|
72
|
+
mkdirSync(PM_PREFIX, { recursive: true });
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
74
|
+
console.log(JSON.stringify({ level: 'INFO', msg: 'installing pipeline-manager', package: `${PM_PACKAGE}@${version}` }));
|
|
75
|
+
execFileSync('npm', ['install', '--prefix', PM_PREFIX, '--no-audit', '--no-fund', '--userconfig', NPMRC, `${PM_PACKAGE}@${version}`], { env: npmEnv, stdio: 'inherit' });
|
|
76
|
+
// 3. Read the current platform JWT — it authenticates the renewal request.
|
|
77
|
+
const sm = new SecretsManagerClient({ region });
|
|
78
|
+
const current = await sm.send(new GetSecretValueCommand({ SecretId: secretName }));
|
|
79
|
+
if (!current.SecretString)
|
|
80
|
+
throw new Error(`Secret "${secretName}" is empty`);
|
|
81
|
+
const jwt = JSON.parse(current.SecretString).password;
|
|
82
|
+
if (!jwt)
|
|
83
|
+
throw new Error(`Secret "${secretName}" missing password (JWT)`);
|
|
84
|
+
// 4. Run store-token (without --schedule, the default) so it ONLY re-mints and
|
|
85
|
+
// writes the secret — it must not redeploy this stack (would recurse). The
|
|
86
|
+
// write uses the Lambda role's creds.
|
|
87
|
+
// store-token no longer takes --secret-name; it reads PLATFORM_SECRET_NAME from
|
|
88
|
+
// the environment (else derives it from the token's org).
|
|
89
|
+
const args = ['infra', 'store-token', '--region', region, '--days', days];
|
|
90
|
+
// Preserve the token's capability scope on renewal (else a scoped ingest
|
|
91
|
+
// credential re-mints as a scopeless platform token and ingest starts 403ing).
|
|
92
|
+
// The re-mint authenticates with THIS token, and a scoped token still carries
|
|
93
|
+
// a userId, so /user/generate-token accepts it and re-issues the same scope.
|
|
94
|
+
const tokenScope = decodeJwtScope(jwt);
|
|
95
|
+
if (tokenScope) {
|
|
96
|
+
args.push('--scope', tokenScope);
|
|
97
|
+
// eslint-disable-next-line no-console
|
|
98
|
+
console.log(JSON.stringify({ level: 'INFO', msg: 'renewing scoped token', scope: tokenScope, secretName }));
|
|
99
|
+
}
|
|
100
|
+
// SECURITY: the spawned store-token POSTs the JWT to the platform. Refuse to
|
|
101
|
+
// disable TLS verification in production so a MITM can't harvest it. Production
|
|
102
|
+
// is this Lambda's normal mode (the renew stack sets NODE_ENV=production), so
|
|
103
|
+
// PLATFORM_VERIFY_SSL=false is honored ONLY outside production. This inlines the
|
|
104
|
+
// same `NODE_ENV==='production'` policy as utils/tls.ts#assertSslDisableAllowed
|
|
105
|
+
// (the handler ships as a single self-contained index.mjs and can't import it).
|
|
106
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
107
|
+
if (process.env.PLATFORM_VERIFY_SSL === 'false') {
|
|
108
|
+
if (isProduction) {
|
|
109
|
+
// eslint-disable-next-line no-console
|
|
110
|
+
console.log(JSON.stringify({ level: 'WARN', msg: 'Refusing PLATFORM_VERIFY_SSL=false in production (NODE_ENV=production) — TLS verification stays enabled' }));
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
args.push('--no-verify-ssl');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
execFileSync('node', [CLI, ...args], {
|
|
117
|
+
// NODE_ENV is inherited via npmEnv (spread of process.env), so the spawned CLI
|
|
118
|
+
// enforces the same production TLS-disable refusal as this handler.
|
|
119
|
+
env: { ...npmEnv, PLATFORM_TOKEN: jwt, PLATFORM_BASE_URL: platformUrl, PLATFORM_SECRET_NAME: secretName },
|
|
120
|
+
stdio: 'inherit',
|
|
121
|
+
});
|
|
122
|
+
// eslint-disable-next-line no-console
|
|
123
|
+
console.log(JSON.stringify({ level: 'INFO', msg: 'platform token renewed', secretName }));
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tcmVuZXctaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sYW1iZGEvdG9rZW4tcmVuZXctaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBQ0gsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ25ELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTlGLE1BQU0sVUFBVSxHQUFHLG9DQUFvQyxDQUFDO0FBQ3hELE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQztBQUNuQixNQUFNLFNBQVMsR0FBRyxHQUFHLEdBQUcsS0FBSyxDQUFDO0FBQzlCLE1BQU0sU0FBUyxHQUFHLEdBQUcsR0FBRyxPQUFPLENBQUM7QUFDaEMsTUFBTSxLQUFLLEdBQUcsR0FBRyxHQUFHLFNBQVMsQ0FBQztBQUM5QixNQUFNLEdBQUcsR0FBRyxHQUFHLFNBQVMsaUJBQWlCLFVBQVUsY0FBYyxDQUFDO0FBRWxFLFNBQVMsUUFBUSxDQUFDLElBQVk7SUFDNUIsTUFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM1QixJQUFJLENBQUMsQ0FBQztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxJQUFJLG1DQUFtQyxDQUFDLENBQUM7SUFDcEUsT0FBTyxDQUFDLENBQUM7QUFDWCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsY0FBYyxDQUFDLEdBQVc7SUFDakMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQyxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU8sU0FBUyxDQUFDO1FBQy9CLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDbkcsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQXdCLENBQUM7UUFDdkQsT0FBTyxPQUFPLE1BQU0sQ0FBQyxLQUFLLEtBQUssUUFBUSxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUNyRixDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztBQUNILENBQUM7QUFFRCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsS0FBSyxJQUFtQixFQUFFO0lBQy9DLE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBQ3BELE1BQU0sV0FBVyxHQUFHLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDckUsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLElBQUksSUFBSSxDQUFDO0lBQzVDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsd0JBQXdCLElBQUksUUFBUSxDQUFDO0lBQ2pFLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxJQUFJLFdBQVcsQ0FBQztJQUVyRCw4RUFBOEU7SUFDOUUsTUFBTSxNQUFNLEdBQUcsRUFBRSxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxnQkFBZ0IsRUFBRSxTQUFTLEVBQUUsQ0FBQztJQUUxRSwrRUFBK0U7SUFDL0UsNkVBQTZFO0lBQzdFLDBCQUEwQjtJQUMxQixhQUFhLENBQUMsS0FBSyxFQUFFLDBEQUEwRCxDQUFDLENBQUM7SUFFakYsMENBQTBDO0lBQzFDLFNBQVMsQ0FBQyxTQUFTLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMxQyxzQ0FBc0M7SUFDdEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsNkJBQTZCLEVBQUUsT0FBTyxFQUFFLEdBQUcsVUFBVSxJQUFJLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3hILFlBQVksQ0FDVixLQUFLLEVBQ0wsQ0FBQyxTQUFTLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLGNBQWMsRUFBRSxLQUFLLEVBQUUsR0FBRyxVQUFVLElBQUksT0FBTyxFQUFFLENBQUMsRUFDaEgsRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsQ0FDbEMsQ0FBQztJQUVGLDJFQUEyRTtJQUMzRSxNQUFNLEVBQUUsR0FBRyxJQUFJLG9CQUFvQixDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUNoRCxNQUFNLE9BQU8sR0FBRyxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxxQkFBcUIsQ0FBQyxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbkYsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZO1FBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxXQUFXLFVBQVUsWUFBWSxDQUFDLENBQUM7SUFDOUUsTUFBTSxHQUFHLEdBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUEyQixDQUFDLFFBQVEsQ0FBQztJQUNqRixJQUFJLENBQUMsR0FBRztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsV0FBVyxVQUFVLDBCQUEwQixDQUFDLENBQUM7SUFFM0UsK0VBQStFO0lBQy9FLDhFQUE4RTtJQUM5RSx5Q0FBeUM7SUFDekMsZ0ZBQWdGO0lBQ2hGLDBEQUEwRDtJQUMxRCxNQUFNLElBQUksR0FBRyxDQUFDLE9BQU8sRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDMUUseUVBQXlFO0lBQ3pFLCtFQUErRTtJQUMvRSw4RUFBOEU7SUFDOUUsNkVBQTZFO0lBQzdFLE1BQU0sVUFBVSxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN2QyxJQUFJLFVBQVUsRUFBRSxDQUFDO1FBQ2YsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDakMsc0NBQXNDO1FBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLHVCQUF1QixFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzlHLENBQUM7SUFDRCw2RUFBNkU7SUFDN0UsZ0ZBQWdGO0lBQ2hGLDhFQUE4RTtJQUM5RSxpRkFBaUY7SUFDakYsZ0ZBQWdGO0lBQ2hGLGdGQUFnRjtJQUNoRixNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsS0FBSyxZQUFZLENBQUM7SUFDM0QsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixLQUFLLE9BQU8sRUFBRSxDQUFDO1FBQ2hELElBQUksWUFBWSxFQUFFLENBQUM7WUFDakIsc0NBQXNDO1lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLHlHQUF5RyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ2pLLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBRUQsWUFBWSxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFO1FBQ25DLCtFQUErRTtRQUMvRSxvRUFBb0U7UUFDcEUsR0FBRyxFQUFFLEVBQUUsR0FBRyxNQUFNLEVBQUUsY0FBYyxFQUFFLEdBQUcsRUFBRSxpQkFBaUIsRUFBRSxXQUFXLEVBQUUsb0JBQW9CLEVBQUUsVUFBVSxFQUFFO1FBQ3pHLEtBQUssRUFBRSxTQUFTO0tBQ2pCLENBQUMsQ0FBQztJQUVILHNDQUFzQztJQUN0QyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSx3QkFBd0IsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDNUYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogTGFtYmRhIGhhbmRsZXIgdGhhdCByZS1taW50cyB0aGUgcGxhdGZvcm0gSldUIGluIFNlY3JldHMgTWFuYWdlciBzbyBpdCBuZXZlclxuICogbGFwc2VzLiBEZXBsb3llZCArIHNjaGVkdWxlZCAob25jZSBhIGRheSkgYnkgYHBpcGVsaW5lLW1hbmFnZXIgaW5mcmEgc3RvcmUtdG9rZW5cbiAqIC0tc2NoZWR1bGVgLiBSYXRoZXIgdGhhbiByZS1pbXBsZW1lbnRpbmcgdGhlIHRva2VuIGZsb3csIGl0IGlzIGEgdGhpblxuICogb3JjaGVzdHJhdG9yIHRoYXQgcmV1c2VzIHRoZSB0ZXN0ZWQgQ0xJIOKAlCBlYWNoIHJ1bjpcbiAqXG4gKiAgIDEuIFBvaW50IHRoZSBgQHBpcGVsaW5lLWJ1aWxkZXJgIG5wbSBzY29wZSBhdCBwdWJsaWMgbnBtLlxuICogICAyLiBEb3dubG9hZCBgQHBpcGVsaW5lLWJ1aWxkZXIvcGlwZWxpbmUtbWFuYWdlcmAgZnJvbSBucG0gaW50byAvdG1wLlxuICogICAzLiBSZWFkIHRoZSBjdXJyZW50IHBsYXRmb3JtIEpXVCBmcm9tIHRoZSBzZWNyZXQuXG4gKiAgIDQuIFJ1biBgcGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlbmAsIHdoaWNoIG1pbnRzIGEgZnJlc2ggbG9uZy1saXZlZCB0b2tlblxuICogICAgICB2aWEgL2FwaS91c2VyL2dlbmVyYXRlLXRva2VuIGFuZCB3cml0ZXMgaXQgYmFjayB0byB0aGUgc2FtZSBzZWNyZXQuXG4gKlxuICogYC90bXBgIGlzIHRoZSBvbmx5IHdyaXRhYmxlIHBhdGggaW4gTGFtYmRhLCBzbyBucG0ncyBIT01FL2NhY2hlL3ByZWZpeCBhbGwgbGl2ZVxuICogdGhlcmUuIHN0b3JlLXRva2VuIGRvZXMgTk9UIGRlcGxveSB0aGUgcmVuZXdhbCBzdGFjayBieSBkZWZhdWx0IChubyAtLXNjaGVkdWxlKSxcbiAqIHNvIHRoZSByZW5ld2FsIHJ1biBuZXZlciByZWRlcGxveXMvcmVjdXJzZXMgaW50byBpdHMgb3duIHN0YWNrLlxuICpcbiAqIEVudjogUExBVEZPUk1fU0VDUkVUX05BTUUsIFBMQVRGT1JNX0JBU0VfVVJMLCBSRU5FV19EQVlTIChkZWZhdWx0IDMwKSxcbiAqICAgICAgUElQRUxJTkVfTUFOQUdFUl9WRVJTSU9OIChkZWZhdWx0IFwibGF0ZXN0XCIpLCBQTEFURk9STV9WRVJJRllfU1NMIChcImZhbHNlXCJcbiAqICAgICAgdG8gZGlzYWJsZSBUTFMgdmVyaWZpY2F0aW9uKS5cbiAqL1xuaW1wb3J0IHsgZXhlY0ZpbGVTeW5jIH0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJztcbmltcG9ydCB7IG1rZGlyU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ25vZGU6ZnMnO1xuaW1wb3J0IHsgU2VjcmV0c01hbmFnZXJDbGllbnQsIEdldFNlY3JldFZhbHVlQ29tbWFuZCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1zZWNyZXRzLW1hbmFnZXInO1xuXG5jb25zdCBQTV9QQUNLQUdFID0gJ0BwaXBlbGluZS1idWlsZGVyL3BpcGVsaW5lLW1hbmFnZXInO1xuY29uc3QgVE1QID0gJy90bXAnO1xuY29uc3QgUE1fUFJFRklYID0gYCR7VE1QfS9wbWA7XG5jb25zdCBOUE1fQ0FDSEUgPSBgJHtUTVB9Ly5ucG1gO1xuY29uc3QgTlBNUkMgPSBgJHtUTVB9Ly5ucG1yY2A7XG5jb25zdCBDTEkgPSBgJHtQTV9QUkVGSVh9L25vZGVfbW9kdWxlcy8ke1BNX1BBQ0tBR0V9L2Rpc3QvY2xpLmpzYDtcblxuZnVuY3Rpb24gcmVxdWlyZWQobmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgdiA9IHByb2Nlc3MuZW52W25hbWVdO1xuICBpZiAoIXYpIHRocm93IG5ldyBFcnJvcihgJHtuYW1lfSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyByZXF1aXJlZGApO1xuICByZXR1cm4gdjtcbn1cblxuLyoqXG4gKiBSZWFkIHRoZSBgc2NvcGVgIGNsYWltIGZyb20gYSBKV1QgcGF5bG9hZCB3aXRob3V0IHZlcmlmeWluZyB0aGUgc2lnbmF0dXJlXG4gKiAodGhlIHBsYXRmb3JtIHJlLXZlcmlmaWVzIG9uIHVzZTsgdGhpcyBvbmx5IGRlY2lkZXMgd2hldGhlciB0byByZS1taW50IFdJVEhcbiAqIGEgc2NvcGUpLiBBIHNjb3BlZCBjcmVkZW50aWFsIChlLmcuIGByZXBvcnRpbmc6aW5nZXN0YCkgbXVzdCByZW5ldyB3aXRoIHRoZVxuICogc2FtZSBzY29wZSBvciB0aGUgZnJlc2ggdG9rZW4gd291bGQgYmUgYSBmdWxsLXByaXZpbGVnZSBwbGF0Zm9ybSB0b2tlbiBhbmRcbiAqIHRoZSBzY29wZWQgY29uc3VtZXIncyBjYWxscyB3b3VsZCBicmVhay4gUmV0dXJucyB1bmRlZmluZWQgZm9yIGFuIHVuc2NvcGVkXG4gKiAocGxhdGZvcm0pIHRva2VuIG9yIGFueSBkZWNvZGUgZmFpbHVyZS5cbiAqL1xuZnVuY3Rpb24gZGVjb2RlSnd0U2NvcGUoand0OiBzdHJpbmcpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICB0cnkge1xuICAgIGNvbnN0IHBheWxvYWQgPSBqd3Quc3BsaXQoJy4nKVsxXTtcbiAgICBpZiAoIXBheWxvYWQpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgY29uc3QganNvbiA9IEJ1ZmZlci5mcm9tKHBheWxvYWQucmVwbGFjZSgvLS9nLCAnKycpLnJlcGxhY2UoL18vZywgJy8nKSwgJ2Jhc2U2NCcpLnRvU3RyaW5nKCd1dGY4Jyk7XG4gICAgY29uc3QgY2xhaW1zID0gSlNPTi5wYXJzZShqc29uKSBhcyB7IHNjb3BlPzogdW5rbm93biB9O1xuICAgIHJldHVybiB0eXBlb2YgY2xhaW1zLnNjb3BlID09PSAnc3RyaW5nJyAmJiBjbGFpbXMuc2NvcGUgPyBjbGFpbXMuc2NvcGUgOiB1bmRlZmluZWQ7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IGhhbmRsZXIgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIGNvbnN0IHNlY3JldE5hbWUgPSByZXF1aXJlZCgnUExBVEZPUk1fU0VDUkVUX05BTUUnKTtcbiAgY29uc3QgcGxhdGZvcm1VcmwgPSByZXF1aXJlZCgnUExBVEZPUk1fQkFTRV9VUkwnKS5yZXBsYWNlKC9cXC8kLywgJycpO1xuICBjb25zdCBkYXlzID0gcHJvY2Vzcy5lbnYuUkVORVdfREFZUyB8fCAnMzAnO1xuICBjb25zdCB2ZXJzaW9uID0gcHJvY2Vzcy5lbnYuUElQRUxJTkVfTUFOQUdFUl9WRVJTSU9OIHx8ICdsYXRlc3QnO1xuICBjb25zdCByZWdpb24gPSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OIHx8ICd1cy1lYXN0LTEnO1xuXG4gIC8vIG5wbSBpbiBMYW1iZGEgY2FuIG9ubHkgd3JpdGUgdW5kZXIgL3RtcDsgSE9NRS9jYWNoZS91c2VyY29uZmlnIHBvaW50IHRoZXJlLlxuICBjb25zdCBucG1FbnYgPSB7IC4uLnByb2Nlc3MuZW52LCBIT01FOiBUTVAsIG5wbV9jb25maWdfY2FjaGU6IE5QTV9DQUNIRSB9O1xuXG4gIC8vIDEuIFNjb3BlIGNvbmZpZzogcmVzb2x2ZSBAcGlwZWxpbmUtYnVpbGRlciBmcm9tIHB1YmxpYyBucG0gcmVnYXJkbGVzcyBvZiBhbnlcbiAgLy8gICAgaW5oZXJpdGVkIHJlZ2lzdHJ5LiBXcml0dGVuIGFzIGEgdXNlcmNvbmZpZyBmaWxlIChlbnYgbWFwcGluZyBvZiBzY29wZWRcbiAgLy8gICAga2V5cyBpcyB1bnJlbGlhYmxlKS5cbiAgd3JpdGVGaWxlU3luYyhOUE1SQywgJ0BwaXBlbGluZS1idWlsZGVyOnJlZ2lzdHJ5PWh0dHBzOi8vcmVnaXN0cnkubnBtanMub3JnL1xcbicpO1xuXG4gIC8vIDIuIERvd25sb2FkIHBpcGVsaW5lLW1hbmFnZXIgaW50byAvdG1wLlxuICBta2RpclN5bmMoUE1fUFJFRklYLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyBsZXZlbDogJ0lORk8nLCBtc2c6ICdpbnN0YWxsaW5nIHBpcGVsaW5lLW1hbmFnZXInLCBwYWNrYWdlOiBgJHtQTV9QQUNLQUdFfUAke3ZlcnNpb259YCB9KSk7XG4gIGV4ZWNGaWxlU3luYyhcbiAgICAnbnBtJyxcbiAgICBbJ2luc3RhbGwnLCAnLS1wcmVmaXgnLCBQTV9QUkVGSVgsICctLW5vLWF1ZGl0JywgJy0tbm8tZnVuZCcsICctLXVzZXJjb25maWcnLCBOUE1SQywgYCR7UE1fUEFDS0FHRX1AJHt2ZXJzaW9ufWBdLFxuICAgIHsgZW52OiBucG1FbnYsIHN0ZGlvOiAnaW5oZXJpdCcgfSxcbiAgKTtcblxuICAvLyAzLiBSZWFkIHRoZSBjdXJyZW50IHBsYXRmb3JtIEpXVCDigJQgaXQgYXV0aGVudGljYXRlcyB0aGUgcmVuZXdhbCByZXF1ZXN0LlxuICBjb25zdCBzbSA9IG5ldyBTZWNyZXRzTWFuYWdlckNsaWVudCh7IHJlZ2lvbiB9KTtcbiAgY29uc3QgY3VycmVudCA9IGF3YWl0IHNtLnNlbmQobmV3IEdldFNlY3JldFZhbHVlQ29tbWFuZCh7IFNlY3JldElkOiBzZWNyZXROYW1lIH0pKTtcbiAgaWYgKCFjdXJyZW50LlNlY3JldFN0cmluZykgdGhyb3cgbmV3IEVycm9yKGBTZWNyZXQgXCIke3NlY3JldE5hbWV9XCIgaXMgZW1wdHlgKTtcbiAgY29uc3Qgand0ID0gKEpTT04ucGFyc2UoY3VycmVudC5TZWNyZXRTdHJpbmcpIGFzIHsgcGFzc3dvcmQ/OiBzdHJpbmcgfSkucGFzc3dvcmQ7XG4gIGlmICghand0KSB0aHJvdyBuZXcgRXJyb3IoYFNlY3JldCBcIiR7c2VjcmV0TmFtZX1cIiBtaXNzaW5nIHBhc3N3b3JkIChKV1QpYCk7XG5cbiAgLy8gNC4gUnVuIHN0b3JlLXRva2VuICh3aXRob3V0IC0tc2NoZWR1bGUsIHRoZSBkZWZhdWx0KSBzbyBpdCBPTkxZIHJlLW1pbnRzIGFuZFxuICAvLyAgICB3cml0ZXMgdGhlIHNlY3JldCDigJQgaXQgbXVzdCBub3QgcmVkZXBsb3kgdGhpcyBzdGFjayAod291bGQgcmVjdXJzZSkuIFRoZVxuICAvLyAgICB3cml0ZSB1c2VzIHRoZSBMYW1iZGEgcm9sZSdzIGNyZWRzLlxuICAvLyBzdG9yZS10b2tlbiBubyBsb25nZXIgdGFrZXMgLS1zZWNyZXQtbmFtZTsgaXQgcmVhZHMgUExBVEZPUk1fU0VDUkVUX05BTUUgZnJvbVxuICAvLyB0aGUgZW52aXJvbm1lbnQgKGVsc2UgZGVyaXZlcyBpdCBmcm9tIHRoZSB0b2tlbidzIG9yZykuXG4gIGNvbnN0IGFyZ3MgPSBbJ2luZnJhJywgJ3N0b3JlLXRva2VuJywgJy0tcmVnaW9uJywgcmVnaW9uLCAnLS1kYXlzJywgZGF5c107XG4gIC8vIFByZXNlcnZlIHRoZSB0b2tlbidzIGNhcGFiaWxpdHkgc2NvcGUgb24gcmVuZXdhbCAoZWxzZSBhIHNjb3BlZCBpbmdlc3RcbiAgLy8gY3JlZGVudGlhbCByZS1taW50cyBhcyBhIHNjb3BlbGVzcyBwbGF0Zm9ybSB0b2tlbiBhbmQgaW5nZXN0IHN0YXJ0cyA0MDNpbmcpLlxuICAvLyBUaGUgcmUtbWludCBhdXRoZW50aWNhdGVzIHdpdGggVEhJUyB0b2tlbiwgYW5kIGEgc2NvcGVkIHRva2VuIHN0aWxsIGNhcnJpZXNcbiAgLy8gYSB1c2VySWQsIHNvIC91c2VyL2dlbmVyYXRlLXRva2VuIGFjY2VwdHMgaXQgYW5kIHJlLWlzc3VlcyB0aGUgc2FtZSBzY29wZS5cbiAgY29uc3QgdG9rZW5TY29wZSA9IGRlY29kZUp3dFNjb3BlKGp3dCk7XG4gIGlmICh0b2tlblNjb3BlKSB7XG4gICAgYXJncy5wdXNoKCctLXNjb3BlJywgdG9rZW5TY29wZSk7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7IGxldmVsOiAnSU5GTycsIG1zZzogJ3JlbmV3aW5nIHNjb3BlZCB0b2tlbicsIHNjb3BlOiB0b2tlblNjb3BlLCBzZWNyZXROYW1lIH0pKTtcbiAgfVxuICAvLyBTRUNVUklUWTogdGhlIHNwYXduZWQgc3RvcmUtdG9rZW4gUE9TVHMgdGhlIEpXVCB0byB0aGUgcGxhdGZvcm0uIFJlZnVzZSB0b1xuICAvLyBkaXNhYmxlIFRMUyB2ZXJpZmljYXRpb24gaW4gcHJvZHVjdGlvbiBzbyBhIE1JVE0gY2FuJ3QgaGFydmVzdCBpdC4gUHJvZHVjdGlvblxuICAvLyBpcyB0aGlzIExhbWJkYSdzIG5vcm1hbCBtb2RlICh0aGUgcmVuZXcgc3RhY2sgc2V0cyBOT0RFX0VOVj1wcm9kdWN0aW9uKSwgc29cbiAgLy8gUExBVEZPUk1fVkVSSUZZX1NTTD1mYWxzZSBpcyBob25vcmVkIE9OTFkgb3V0c2lkZSBwcm9kdWN0aW9uLiBUaGlzIGlubGluZXMgdGhlXG4gIC8vIHNhbWUgYE5PREVfRU5WPT09J3Byb2R1Y3Rpb24nYCBwb2xpY3kgYXMgdXRpbHMvdGxzLnRzI2Fzc2VydFNzbERpc2FibGVBbGxvd2VkXG4gIC8vICh0aGUgaGFuZGxlciBzaGlwcyBhcyBhIHNpbmdsZSBzZWxmLWNvbnRhaW5lZCBpbmRleC5tanMgYW5kIGNhbid0IGltcG9ydCBpdCkuXG4gIGNvbnN0IGlzUHJvZHVjdGlvbiA9IHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSAncHJvZHVjdGlvbic7XG4gIGlmIChwcm9jZXNzLmVudi5QTEFURk9STV9WRVJJRllfU1NMID09PSAnZmFsc2UnKSB7XG4gICAgaWYgKGlzUHJvZHVjdGlvbikge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHsgbGV2ZWw6ICdXQVJOJywgbXNnOiAnUmVmdXNpbmcgUExBVEZPUk1fVkVSSUZZX1NTTD1mYWxzZSBpbiBwcm9kdWN0aW9uIChOT0RFX0VOVj1wcm9kdWN0aW9uKSDigJQgVExTIHZlcmlmaWNhdGlvbiBzdGF5cyBlbmFibGVkJyB9KSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGFyZ3MucHVzaCgnLS1uby12ZXJpZnktc3NsJyk7XG4gICAgfVxuICB9XG5cbiAgZXhlY0ZpbGVTeW5jKCdub2RlJywgW0NMSSwgLi4uYXJnc10sIHtcbiAgICAvLyBOT0RFX0VOViBpcyBpbmhlcml0ZWQgdmlhIG5wbUVudiAoc3ByZWFkIG9mIHByb2Nlc3MuZW52KSwgc28gdGhlIHNwYXduZWQgQ0xJXG4gICAgLy8gZW5mb3JjZXMgdGhlIHNhbWUgcHJvZHVjdGlvbiBUTFMtZGlzYWJsZSByZWZ1c2FsIGFzIHRoaXMgaGFuZGxlci5cbiAgICBlbnY6IHsgLi4ubnBtRW52LCBQTEFURk9STV9UT0tFTjogand0LCBQTEFURk9STV9CQVNFX1VSTDogcGxhdGZvcm1VcmwsIFBMQVRGT1JNX1NFQ1JFVF9OQU1FOiBzZWNyZXROYW1lIH0sXG4gICAgc3RkaW86ICdpbmhlcml0JyxcbiAgfSk7XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyBsZXZlbDogJ0lORk8nLCBtc2c6ICdwbGF0Zm9ybSB0b2tlbiByZW5ld2VkJywgc2VjcmV0TmFtZSB9KSk7XG59O1xuIl19
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
{
|
|
2
|
+
"AWSTemplateFormatVersion": "2010-09-09",
|
|
3
|
+
"Description": "Pipeline Builder event ingestion — EventBridge rule, SQS queue, and Lambda that resolves each pipeline's pb.pipeline-id tag and forwards CodePipeline events to the reporting service API. Lambda code is deployed separately via pipeline-manager infra setup-events.",
|
|
4
|
+
"Parameters": {
|
|
5
|
+
"PlatformBaseUrl": {
|
|
6
|
+
"Type": "String",
|
|
7
|
+
"Description": "Base URL of the platform (e.g. https://app.example.com)"
|
|
8
|
+
},
|
|
9
|
+
"PlatformSecretName": {
|
|
10
|
+
"Type": "String",
|
|
11
|
+
"Description": "Secrets Manager secret name containing the platform JWT token (e.g. pipeline-builder/{orgId}/platform)"
|
|
12
|
+
},
|
|
13
|
+
"DoraEnabled": {
|
|
14
|
+
"Type": "String",
|
|
15
|
+
"Default": "false",
|
|
16
|
+
"AllowedValues": ["false", "true"],
|
|
17
|
+
"Description": "When true, the ingestion Lambda resolves commit timestamps in-account for DORA lead time (extra SCM/secret cost). Enable only for orgs with the advanced_reporting add-on."
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"Conditions": {
|
|
21
|
+
"DoraOn": { "Fn::Equals": [{ "Ref": "DoraEnabled" }, "true"] }
|
|
22
|
+
},
|
|
23
|
+
"Resources": {
|
|
24
|
+
"EventQueue": {
|
|
25
|
+
"Type": "AWS::SQS::Queue",
|
|
26
|
+
"Properties": {
|
|
27
|
+
"QueueName": "pipeline-builder-events",
|
|
28
|
+
"VisibilityTimeout": 300,
|
|
29
|
+
"MessageRetentionPeriod": 1209600,
|
|
30
|
+
"RedrivePolicy": {
|
|
31
|
+
"deadLetterTargetArn": { "Fn::GetAtt": ["DeadLetterQueue", "Arn"] },
|
|
32
|
+
"maxReceiveCount": 3
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"DeadLetterQueue": {
|
|
37
|
+
"Type": "AWS::SQS::Queue",
|
|
38
|
+
"Properties": {
|
|
39
|
+
"QueueName": "pipeline-builder-events-dlq",
|
|
40
|
+
"MessageRetentionPeriod": 1209600
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"EventQueuePolicy": {
|
|
44
|
+
"Type": "AWS::SQS::QueuePolicy",
|
|
45
|
+
"Properties": {
|
|
46
|
+
"Queues": [{ "Ref": "EventQueue" }],
|
|
47
|
+
"PolicyDocument": {
|
|
48
|
+
"Statement": [{
|
|
49
|
+
"Effect": "Allow",
|
|
50
|
+
"Principal": { "Service": "events.amazonaws.com" },
|
|
51
|
+
"Action": "sqs:SendMessage",
|
|
52
|
+
"Resource": { "Fn::GetAtt": ["EventQueue", "Arn"] }
|
|
53
|
+
}]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"EventRule": {
|
|
58
|
+
"Type": "AWS::Events::Rule",
|
|
59
|
+
"Properties": {
|
|
60
|
+
"Name": "pipeline-builder-codepipeline-events",
|
|
61
|
+
"Description": "Capture CodePipeline state changes for reporting",
|
|
62
|
+
"State": "ENABLED",
|
|
63
|
+
"EventPattern": {
|
|
64
|
+
"source": ["aws.codepipeline"],
|
|
65
|
+
"detail-type": [
|
|
66
|
+
"CodePipeline Pipeline Execution State Change",
|
|
67
|
+
"CodePipeline Stage Execution State Change",
|
|
68
|
+
"CodePipeline Action Execution State Change"
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"Targets": [{
|
|
72
|
+
"Id": "pipeline-events-queue",
|
|
73
|
+
"Arn": { "Fn::GetAtt": ["EventQueue", "Arn"] }
|
|
74
|
+
}]
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"LambdaRole": {
|
|
78
|
+
"Type": "AWS::IAM::Role",
|
|
79
|
+
"Properties": {
|
|
80
|
+
"RoleName": "pipeline-builder-event-ingestion",
|
|
81
|
+
"AssumeRolePolicyDocument": {
|
|
82
|
+
"Version": "2012-10-17",
|
|
83
|
+
"Statement": [{
|
|
84
|
+
"Effect": "Allow",
|
|
85
|
+
"Principal": { "Service": "lambda.amazonaws.com" },
|
|
86
|
+
"Action": "sts:AssumeRole"
|
|
87
|
+
}]
|
|
88
|
+
},
|
|
89
|
+
"ManagedPolicyArns": [
|
|
90
|
+
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
91
|
+
],
|
|
92
|
+
"Policies": [{
|
|
93
|
+
"PolicyName": "event-ingestion",
|
|
94
|
+
"PolicyDocument": {
|
|
95
|
+
"Statement": [
|
|
96
|
+
{
|
|
97
|
+
"Effect": "Allow",
|
|
98
|
+
"Action": ["sqs:ReceiveMessage", "sqs:DeleteMessage", "sqs:GetQueueAttributes"],
|
|
99
|
+
"Resource": { "Fn::GetAtt": ["EventQueue", "Arn"] }
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"Effect": "Allow",
|
|
103
|
+
"Action": ["sqs:StartMessageMoveTask", "sqs:ListMessageMoveTasks", "sqs:GetQueueAttributes"],
|
|
104
|
+
"Resource": [
|
|
105
|
+
{ "Fn::GetAtt": ["EventQueue", "Arn"] },
|
|
106
|
+
{ "Fn::GetAtt": ["DeadLetterQueue", "Arn"] }
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"Effect": "Allow",
|
|
111
|
+
"Action": "secretsmanager:GetSecretValue",
|
|
112
|
+
"Resource": [
|
|
113
|
+
{ "Fn::Sub": "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:pipeline-builder/*/platform-*" },
|
|
114
|
+
{ "Fn::Sub": "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:pipeline-builder/*/reporting-ingest-*" },
|
|
115
|
+
{ "Fn::If": ["DoraOn", { "Fn::Sub": "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:pipeline-builder/*/github-token-*" }, { "Ref": "AWS::NoValue" }] }
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"Effect": "Allow",
|
|
120
|
+
"Action": "codepipeline:ListTagsForResource",
|
|
121
|
+
"Resource": { "Fn::Sub": "arn:aws:codepipeline:*:${AWS::AccountId}:*" }
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"Fn::If": ["DoraOn", {
|
|
125
|
+
"Effect": "Allow",
|
|
126
|
+
"Action": "codecommit:GetCommit",
|
|
127
|
+
"Resource": { "Fn::Sub": "arn:aws:codecommit:*:${AWS::AccountId}:*" }
|
|
128
|
+
}, { "Ref": "AWS::NoValue" }]
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
}]
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"EventHandler": {
|
|
136
|
+
"Type": "AWS::Lambda::Function",
|
|
137
|
+
"Properties": {
|
|
138
|
+
"FunctionName": "pipeline-builder-event-ingestion",
|
|
139
|
+
"Runtime": "nodejs24.x",
|
|
140
|
+
"Handler": "index.handler",
|
|
141
|
+
"Code": {
|
|
142
|
+
"ZipFile": "exports.handler=async(event)=>{console.log('Placeholder handler — run pipeline-manager infra setup-events to deploy real code');return{statusCode:200,body:JSON.stringify({message:'hello',records:event.Records?.length||0})}}"
|
|
143
|
+
},
|
|
144
|
+
"Role": { "Fn::GetAtt": ["LambdaRole", "Arn"] },
|
|
145
|
+
"Timeout": 60,
|
|
146
|
+
"MemorySize": 256,
|
|
147
|
+
"Environment": {
|
|
148
|
+
"Variables": {
|
|
149
|
+
"PLATFORM_BASE_URL": { "Ref": "PlatformBaseUrl" },
|
|
150
|
+
"PLATFORM_SECRET_NAME": { "Ref": "PlatformSecretName" },
|
|
151
|
+
"DORA_ENABLED": { "Ref": "DoraEnabled" }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"EventSourceMapping": {
|
|
157
|
+
"Type": "AWS::Lambda::EventSourceMapping",
|
|
158
|
+
"Properties": {
|
|
159
|
+
"EventSourceArn": { "Fn::GetAtt": ["EventQueue", "Arn"] },
|
|
160
|
+
"FunctionName": { "Ref": "EventHandler" },
|
|
161
|
+
"BatchSize": 10,
|
|
162
|
+
"MaximumBatchingWindowInSeconds": 5,
|
|
163
|
+
"Enabled": true
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"Outputs": {
|
|
168
|
+
"EventQueueArn": { "Value": { "Fn::GetAtt": ["EventQueue", "Arn"] } },
|
|
169
|
+
"EventQueueUrl": { "Value": { "Ref": "EventQueue" } },
|
|
170
|
+
"DeadLetterQueueUrl": { "Value": { "Ref": "DeadLetterQueue" } },
|
|
171
|
+
"LambdaFunctionArn": { "Value": { "Fn::GetAtt": ["EventHandler", "Arn"] } },
|
|
172
|
+
"EventRuleName": { "Value": { "Ref": "EventRule" } }
|
|
173
|
+
}
|
|
174
|
+
}
|