@odigos/aws-cdk 0.0.13 → 0.0.15

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/README.md CHANGED
@@ -1,12 +1,24 @@
1
- # Odigos CDK constructs for AWS Lambda
1
+ # Odigos CDK constructs for AWS
2
2
 
3
- Instrument AWS Lambda functions with the Odigos OpenTelemetry layer, from your own CDK app.
3
+ Instrument AWS Lambda functions and ECS Fargate services with OpenTelemetry, from your own CDK app.
4
4
 
5
5
  ```bash
6
6
  npm install @odigos/aws-cdk # TypeScript / JavaScript
7
7
  pip install odigos-aws-cdk # Python
8
8
  ```
9
9
 
10
+ | | Lambda | ECS Fargate |
11
+ |---|---|---|
12
+ | Construct | `OdigosLambdaInstrumentation` | `OdigosFargateInstrumentation` |
13
+ | Mechanism | a layer, plus an exec wrapper or loader variable | agents staged into a shared task volume by a run-to-completion container |
14
+ | Languages | python, nodejs, java, ruby | java, python, nodejs, dotnet, php, ruby |
15
+ | Where telemetry goes | a collector in the same Lambda sandbox, which forwards | your destination, from the application process |
16
+ | Language detection | from the function's declared runtime | none — you state the language |
17
+
18
+ Both write `ODIGOS_IAC` and never the connector's ownership marker: instrumented by Odigos, owned by you.
19
+
20
+ # AWS Lambda
21
+
10
22
  ```ts
11
23
  import { OdigosLambdaInstrumentation } from '@odigos/aws-cdk';
12
24
 
@@ -224,18 +236,213 @@ Pick one per function. Discovery-only (manual) mode is the combination that work
224
236
  reports, your stack owns the configuration. That is what `ODIGOS_IAC` expresses — instrumented by Odigos,
225
237
  owned by you — and it is why these constructs never write the connector's ownership marker.
226
238
 
227
- ## API
239
+ # ECS Fargate
240
+
241
+ ```ts
242
+ import { OdigosFargateInstrumentation, OdigosFargateLanguage } from '@odigos/aws-cdk';
243
+
244
+ const odigos = new OdigosFargateInstrumentation(this, 'Odigos', {
245
+ destination: { httpEndpoint: 'https://otlp.example.com:4318' },
246
+ });
247
+
248
+ odigos.instrument({
249
+ container: apiContainer, // the ecs.ContainerDefinition to instrument
250
+ language: OdigosFargateLanguage.JAVA,
251
+ service: apiService, // read OTEL_SERVICE_NAME off the ECS service
252
+ });
253
+ ```
254
+
255
+ ```python
256
+ from odigos_aws_cdk import OdigosFargateInstrumentation, OdigosFargateLanguage
257
+
258
+ odigos = OdigosFargateInstrumentation(self, "Odigos",
259
+ destination={"http_endpoint": "https://otlp.example.com:4318"},
260
+ )
261
+ odigos.instrument(
262
+ container=api_container,
263
+ language=OdigosFargateLanguage.JAVA,
264
+ service=api_service,
265
+ )
266
+ ```
267
+
268
+ ## What it does to the task definition
269
+
270
+ Four moves, which are one mechanism and only work together:
271
+
272
+ 1. a bare-name task volume, `odigos-agent` — on Fargate an ephemeral-storage bind mount scoped to the task
273
+ (there is no tmpfs);
274
+ 2. a **non-essential** `odigos-agent` container that copies the language agents out of the agents bundle
275
+ into that volume and exits 0;
276
+ 3. the volume mounted read-only at `/var/odigos` in your container, which `DependsOn` the staging container
277
+ with condition `SUCCESS` — legal only against a non-essential container, which is why (2) is
278
+ non-essential;
279
+ 4. the language's activation environment, the OTLP export environment and the `ODIGOS_IAC` marker on your
280
+ container.
281
+
282
+ Marker presence alone is a false green here. A container can carry a complete, correct activation
283
+ environment while the staging container is gone — the application then starts, finds nothing at
284
+ `/var/odigos`, and exports nothing while reporting as instrumented. That is why all four are one construct.
285
+
286
+ ## Where the telemetry goes
287
+
288
+ Straight to your destination, from inside the application process. **This is the substantive difference
289
+ from Lambda**, where the SDK hands batches to a collector in the same sandbox and the export happens after
290
+ the response has already been returned. A Fargate task has no such collector, so a slow or unreachable
291
+ destination is felt by the application's own exporter.
292
+
293
+ ## Naming the service
294
+
295
+ `OTEL_SERVICE_NAME` **must equal the workload's name in Odigos, which for a Fargate workload is the ECS
296
+ service name** — not the task definition family and not the container name. A different value is reported
297
+ as instrumentation drift rather than treated as a naming preference, so there is no default: pass `service`
298
+ to read the name off an `ecs.FargateService`, or `serviceName` when the service is defined elsewhere.
299
+
300
+ The service's physical name has to be one you set. A name CloudFormation generates is a `Ref` to the
301
+ service, and the task definition that service runs cannot reference it — CloudFormation rejects the cycle
302
+ at deploy time — so a generated name throws at synth pointing you at `serviceName`.
303
+
304
+ ## Languages, and stating them
305
+
306
+ | | Activated through | Needs `runtimeVersion` |
307
+ |---|---|---|
308
+ | `JAVA` | `JAVA_TOOL_OPTIONS` | no |
309
+ | `PYTHON` | `PYTHONPATH` | no |
310
+ | `NODEJS` | `NODE_OPTIONS` | no |
311
+ | `DOTNET` | `CORECLR_*` / `DOTNET_*` (glibc only) | no |
312
+ | `PHP` | `PHP_INI_SCAN_DIR` | **yes** — major.minor, e.g. `"8.3"` |
313
+ | `RUBY` | `RUBYOPT` | **yes** — major.minor, e.g. `"3.3"` |
314
+
315
+ There is no aspect and no detection, and unlike Lambda that is not a matter of taste. A function declares
316
+ its runtime; a container declares an image. A language guessed from an image would silently mis-instrument
317
+ whatever it got wrong — a Java agent staged into a Python container leaves it uninstrumented, and a
318
+ `JAVA_TOOL_OPTIONS` on a JVM-less image is simply ignored, so nothing fails loudly enough to notice.
319
+
320
+ php and ruby need `runtimeVersion` because their agents are laid down per runtime version in the bundle,
321
+ and the version selects the path their activation variable points at. It must be exactly `major.minor`;
322
+ anything else names a directory that does not exist, and the container starts with no agent loaded.
323
+
324
+ **Go cannot be instrumented on Fargate at all.** Its only zero-code instrumentation is eBPF, and Fargate
325
+ forbids the privileged containers and host PID an eBPF agent needs. Use the OpenTelemetry Go SDK.
326
+
327
+ ## Loader variables your container already sets
328
+
329
+ The construct reads the value your container was created with and **appends** to it, joined by the
330
+ separator the language's own pattern specifies — a space for `JAVA_TOOL_OPTIONS` and `NODE_OPTIONS`, a
331
+ colon for `PYTHONPATH`:
332
+
333
+ ```ts
334
+ taskDefinition.addContainer('App', {
335
+ image,
336
+ environment: { JAVA_TOOL_OPTIONS: '-Xmx512m' },
337
+ });
338
+ // becomes: -Xmx512m -javaagent:/var/odigos/java/javaagent.jar
339
+ ```
340
+
341
+ Pass `existingLoaderValue` when the value is not visible at synth — set by an `environmentFiles` entry,
342
+ baked into the image's own `ENV`, or supplied through `secrets`.
343
+
344
+ `PHP_INI_SCAN_DIR` and `RUBYOPT` are the exception: Odigos compares them **exactly**, so they are set
345
+ outright and a value already there is reported as a synth warning rather than merged. Fold what your
346
+ application needs into the value Odigos sets.
347
+
348
+ ## Transport
349
+
350
+ Each language's agent has a preferred transport, and giving the destination **both** endpoints lets each
351
+ one take it:
352
+
353
+ ```ts
354
+ destination: {
355
+ endpoint: 'otlp.example.com:4317', // OTLP/gRPC, bare host:port
356
+ httpEndpoint: 'https://otlp.example.com:4318', // OTLP/HTTP, scheme-qualified
357
+ }
358
+ ```
359
+
360
+ Every agent in the bundle prefers `http/protobuf`. java, python, nodejs and dotnet honour
361
+ `OTEL_EXPORTER_OTLP_PROTOCOL` and fall back to gRPC when that is all the destination offers; **php and
362
+ ruby cannot** — their community SDKs are `http/protobuf`-only, so instrumenting one against a gRPC-only
363
+ destination throws at synth rather than deploying a task that exports nothing.
364
+
365
+ Endpoint and protocol are one decision, and are compared as one. Pairing an endpoint with the other
366
+ protocol fails at runtime as a connection reset that points nowhere near the cause, so an endpoint on the
367
+ other protocol's standard port (4317 with `http/protobuf`, 4318 with `grpc`) is a synth warning.
368
+
369
+ Unlike Lambda, `destination.headers` is rendered into `OTEL_EXPORTER_OTLP_HEADERS` on the container. The
370
+ connector does not set or compare that variable when it instruments a Fargate task itself, so it is
371
+ neither reconciled nor reported as drift — it is here because a task with no collector of its own has
372
+ nowhere else to hold a credential. Same exposure caveat as Lambda: `ecs:DescribeTaskDefinition` and the
373
+ console can read it.
374
+
375
+ ## Signals
376
+
377
+ Same defaults as Lambda — traces on, metrics and logs opt-in — and the same requirement that they match
378
+ what Odigos has configured for the destination. All three `OTEL_*_EXPORTER` variables are written
379
+ explicitly here, unlike Lambda where an enabled signal's variable is left unset, because the connector's
380
+ Fargate check compares all three by value.
381
+
382
+ ## A task definition with more than one application
383
+
384
+ Six applications in six languages in one task definition is a real shape. Call `instrument()` once per
385
+ container; there is **one** staging container for the whole task definition, and its command is built
386
+ lazily from the union of every instrumented container's agents, in ascending directory order — which is
387
+ exactly what the connector compares it against.
388
+
389
+ ```ts
390
+ odigos.instrumentAll([
391
+ { container: apiContainer, language: OdigosFargateLanguage.JAVA, service: svc },
392
+ { container: workerContainer, language: OdigosFargateLanguage.PYTHON, service: svc },
393
+ ]);
394
+ ```
395
+
396
+ AWS caps a task definition at 10 containers and the staging container needs one of them; a definition
397
+ already at the limit fails at synth rather than on `RegisterTaskDefinition`.
398
+
399
+ ## The agents image
400
+
401
+ The staging container pulls `registry.odigos.io/odigos-fargate-agents` at this package's release tag — a
402
+ multi-arch manifest list, so amd64 and Graviton tasks both get a matching image. **It is pulled by your
403
+ task's execution role at every task start**, so a task in a private subnet with no NAT and no registry
404
+ endpoint cannot pull it. Mirror it into a registry your tasks can reach and pass `agentsImage`:
405
+
406
+ ```ts
407
+ agentsImage: '111122223333.dkr.ecr.eu-west-1.amazonaws.com/odigos-fargate-agents:v0.0.13',
408
+ ```
409
+
410
+ A mirror has to be the same bundle — the activation environment names exact paths inside it. The bundle is
411
+ Linux-only and glibc-based: a Windows task definition throws at synth, and musl (Alpine) images have no
412
+ .NET native profiler in it.
413
+
414
+ **Whatever image you stage from is recorded in the marker**, as `image`, and Odigos holds the task
415
+ definition to *that* rather than to the bundle the connector's own build pins. Two consequences worth
416
+ knowing: a mirror is judged against itself instead of reading as drift, and upgrading this package ahead of
417
+ the connector is not drift either — the service stays conformant until you re-deploy, exactly as a Lambda
418
+ function pinned to an older layer does. What *is* reported is two containers in one task definition
419
+ recording different images: there is one staging container and it cannot serve two bundles.
420
+
421
+ ## Do not also instrument these services with the connector
422
+
423
+ Same rule as Lambda, with ECS's own cost. The connector instruments a live service by registering a new
424
+ task-definition revision and updating the service to it. A service instrumented here *and* enabled as an
425
+ automatic-mode source is owned by both: each `cdk deploy` reasserts your stack's definition, each
426
+ reconcile reasserts the connector's, and every round trip registers a revision and rolls the service.
427
+ Discovery-only (manual) mode is the combination that works.
428
+
429
+ # API
228
430
 
229
431
  | Export | |
230
432
  |---|---|
231
- | `OdigosLambdaInstrumentation` | the construct; `instrument(fn)`, `instrumentAll(fns)` |
433
+ | `OdigosLambdaInstrumentation` | the Lambda construct; `instrument(fn)`, `instrumentAll(fns)` |
232
434
  | `OdigosInstrumentationAspect` | applies it to every function in a scope |
233
- | `OdigosDestination` | where the collector exports; exactly one of `endpoint` / `httpEndpoint` |
435
+ | `OdigosFargateInstrumentation` | the Fargate construct; `instrument(target)`, `instrumentAll(targets)` |
436
+ | `OdigosFargateTarget` | one container to instrument: `container`, `language`, `service` / `serviceName`, `runtimeVersion`, `existingLoaderValue` |
437
+ | `OdigosFargateLanguage` | `JAVA` \| `PYTHON` \| `NODEJS` \| `DOTNET` \| `PHP` \| `RUBY` |
438
+ | `OdigosDestination` | where telemetry goes; exactly one endpoint on Lambda, either or both on Fargate |
234
439
  | `OdigosLayerSource` | `ODIGOS_PUBLIC` \| `IN_ACCOUNT` |
235
440
  | `OdigosInstrumentationMode` | `HANDLER` \| `WEB_SERVER` |
236
441
  | `OdigosJavaHandlerDistro` | the four Java wrappers |
237
- | `renderCollectorConfig` | the collector config renderer, if you want to inspect what gets set |
442
+ | `renderCollectorConfig` | the Lambda collector config renderer, if you want to inspect what gets set |
443
+ | `sidecarCommand`, `evaluateAppendPattern` | the Fargate staging command and loader-value rendering |
238
444
  | `LAYER_ARNS`, `ODIGOS_VERSION` | the baked layer table and the release it came from |
445
+ | `FARGATE_AGENTS_IMAGE`, `FARGATE_DISTROS_VERSION` | the agents image this build stages, and the distros release its recipes mirror |
239
446
 
240
447
  ## License
241
448
 
@@ -0,0 +1,108 @@
1
+ import { OdigosDestination, OdigosFargateLanguage } from './types';
2
+ /** `OTEL_EXPORTER_OTLP_PROTOCOL` values. Endpoint and protocol are always decided together. */
3
+ export declare const OTLP_PROTOCOL_GRPC = "grpc";
4
+ export declare const OTLP_PROTOCOL_HTTP = "http/protobuf";
5
+ /**
6
+ * An append-style loader variable — `JAVA_TOOL_OPTIONS`, `NODE_OPTIONS`, `PYTHONPATH` — kept as its
7
+ * pattern rather than a finished value, because the value depends on what the container already carries.
8
+ */
9
+ export interface FargateAppendEnvVar {
10
+ /** Variable name. */
11
+ readonly name: string;
12
+ /** The distro's ReplacePattern, placeholders unresolved. */
13
+ readonly pattern: string;
14
+ }
15
+ /**
16
+ * Resolves an append pattern for a given pre-existing value.
17
+ *
18
+ * Port of `distro.EvaluateReplacePattern`, INCLUDING the empty-original case: with nothing to join to,
19
+ * the placeholder and its adjacent delimiter are stripped, so the result never opens with a stray space
20
+ * or colon. `PYTHONPATH=":/var/odigos/python"` would put the current working directory on `sys.path`
21
+ * ahead of the agent, which is a different program than the one intended.
22
+ */
23
+ export declare function evaluateAppendPattern(pattern: string, originalValue: string): string;
24
+ /**
25
+ * The delimiter a pattern joins the container's own value to ours with: the character immediately after
26
+ * the original-value placeholder.
27
+ *
28
+ * DERIVED from the pattern rather than declared, because the pattern is the authority — it comes from the
29
+ * upstream distro, differs by language (a space for `JAVA_TOOL_OPTIONS`/`NODE_OPTIONS`, a colon for
30
+ * `PYTHONPATH`) and can change on somebody else's release. Assuming one would be silently destructive in
31
+ * a way nothing here would catch: a space-joined `PYTHONPATH` is one unusable path rather than two, and
32
+ * the process still starts.
33
+ */
34
+ export declare function appendSeparator(pattern: string): string;
35
+ /** One language's staging and activation, with every platform-supplied parameter already rendered. */
36
+ export interface FargateRecipe {
37
+ /** Canonical language key, and the value recorded in the `ODIGOS_IAC` marker's `lang`. */
38
+ readonly language: string;
39
+ /** Directories under the agents image the staging container copies into the shared volume. */
40
+ readonly agentDirs: string[];
41
+ /** Activation variables set outright. */
42
+ readonly staticEnv: {
43
+ [key: string]: string;
44
+ };
45
+ /** Activation variables our fragment is appended to. */
46
+ readonly appendEnv: FargateAppendEnvVar[];
47
+ /** The transport this agent prefers. */
48
+ readonly otlpProtocol: string;
49
+ /** Whether the agent can speak only `otlpProtocol` and cannot be switched to the other transport. */
50
+ readonly protocolFixed: boolean;
51
+ }
52
+ /** The canonical connector language key for an enum member. The enum's values ARE those keys. */
53
+ export declare function languageKeyOf(language: OdigosFargateLanguage): string;
54
+ /**
55
+ * The recipe for a language, with the runtime version rendered into the agents' paths where the language
56
+ * needs it.
57
+ *
58
+ * @param runtimeVersion the application's runtime `major.minor`. Required for php and ruby, whose agent
59
+ * files are laid down per version, and ignored by every other language.
60
+ */
61
+ export declare function recipeFor(language: OdigosFargateLanguage, runtimeVersion?: string): FargateRecipe;
62
+ /**
63
+ * The staging container's command: create the shared directory, then copy each language's agent
64
+ * directory out of the image into it, then exit 0.
65
+ *
66
+ * ORDER AND SPELLING ARE THE CONTRACT. There is ONE staging container per task definition however many
67
+ * languages it holds, and the connector compares its command against the union of every instrumented
68
+ * container's agent directories in ascending order. A command that differs by a space is reported as
69
+ * drift on the whole task definition, so this is sorted and rendered exactly as `sidecarCommand` in the
70
+ * connector does it.
71
+ */
72
+ export declare function sidecarCommand(agentDirs: string[]): string[];
73
+ /**
74
+ * The resource attributes every span, metric and log from an instrumented task carries. `service.name` is
75
+ * set separately, through `OTEL_SERVICE_NAME`.
76
+ *
77
+ * The region may be an unresolved token on an environment-agnostic stack, which is fine here and unlike
78
+ * the Lambda layer lookup: this value is an environment variable, so the token resolves to a `Ref` to
79
+ * `AWS::Region` and CloudFormation fills in the real region at deploy time.
80
+ */
81
+ export declare function fargateResourceAttributes(region: string): string;
82
+ /** Where an instrumented container exports to, and over which transport. Always decided as a pair. */
83
+ export interface FargateExportTarget {
84
+ /** `scheme://host:port`. */
85
+ readonly endpoint: string;
86
+ /** `grpc` or `http/protobuf`. */
87
+ readonly protocol: string;
88
+ }
89
+ /**
90
+ * Resolves the endpoint and protocol for one recipe against one destination.
91
+ *
92
+ * Port of `exportTargetFor`. A Fargate task has no node-local collector — unlike Lambda, where the SDK
93
+ * exports to a collector in the same sandbox — so the agent exports OTLP straight to the destination, and
94
+ * which transport it can use is a property of the agent as much as of the destination.
95
+ *
96
+ * Each agent's PREFERRED transport is taken when the destination offers it. When it does not, an agent
97
+ * whose community SDK pins the protocol (php, ruby) cannot be switched and throws; every other agent
98
+ * honours `OTEL_EXPORTER_OTLP_PROTOCOL` and falls back to whichever transport exists.
99
+ */
100
+ export declare function exportTargetFor(recipe: FargateRecipe, dest: OdigosDestination): FargateExportTarget;
101
+ /**
102
+ * A warning when an endpoint's port is the well-known port of the OTHER OTLP protocol, or "" when the
103
+ * pairing looks right — including a non-standard port, which cannot be judged.
104
+ *
105
+ * Worth a synth-time warning because the runtime symptom is a connection reset mid-export, which points
106
+ * nowhere near the cause: an HTTP request against an HTTP/2 gRPC listener, or the reverse.
107
+ */
108
+ export declare function portProtocolMismatch(endpoint: string, protocol: string): string;
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OTLP_PROTOCOL_HTTP = exports.OTLP_PROTOCOL_GRPC = void 0;
4
+ exports.evaluateAppendPattern = evaluateAppendPattern;
5
+ exports.appendSeparator = appendSeparator;
6
+ exports.languageKeyOf = languageKeyOf;
7
+ exports.recipeFor = recipeFor;
8
+ exports.sidecarCommand = sidecarCommand;
9
+ exports.fargateResourceAttributes = fargateResourceAttributes;
10
+ exports.exportTargetFor = exportTargetFor;
11
+ exports.portProtocolMismatch = portProtocolMismatch;
12
+ const fargate_1 = require("./generated/fargate");
13
+ /**
14
+ * How a language's agent is staged into an ECS Fargate task and activated in a container.
15
+ *
16
+ * A PORT of `recipeFor` / `applyInstrumentation` in the connector's ECS instrumentor
17
+ * (connectors/providers/aws/pkg/workload/instrumentor/ecs_recipes.go, ecs_inject.go). The data lives in
18
+ * ../../artifacts.json and reaches here through the generated module; the decisions live here.
19
+ *
20
+ * Equivalence with the connector is not cosmetic. Its conformance observer rebuilds each container's
21
+ * desired activation environment and compares it against what the container carries, component by
22
+ * component, so anything rendered differently here is reported to the customer as instrumentation drift
23
+ * on a stack that is in fact deployed exactly as they wrote it.
24
+ */
25
+ /** The distro placeholder for the container's own pre-Odigos value of an append-style loader variable. */
26
+ const ORIGINAL_ENV_VALUE_PLACEHOLDER = '{{ORIGINAL_ENV_VALUE}}';
27
+ /** The distro placeholder for the shared volume's mount path. */
28
+ const AGENTS_DIR_PLACEHOLDER = '{{ODIGOS_AGENTS_DIR}}';
29
+ /**
30
+ * The distro parameter for the application's runtime `major.minor`, as a Go text/template action —
31
+ * verbatim from the distro yaml so ../../artifacts.json can be diffed against it by eye. Only php and
32
+ * ruby carry it: their agents are laid down per runtime version.
33
+ */
34
+ const RUNTIME_VERSION_PLACEHOLDER = '{{.RUNTIME_VERSION_MAJOR_MINOR}}';
35
+ /** `OTEL_EXPORTER_OTLP_PROTOCOL` values. Endpoint and protocol are always decided together. */
36
+ exports.OTLP_PROTOCOL_GRPC = 'grpc';
37
+ exports.OTLP_PROTOCOL_HTTP = 'http/protobuf';
38
+ /** Standard OTLP receiver ports, used only to warn about an endpoint paired with the other protocol. */
39
+ const OTLP_DEFAULT_GRPC_PORT = '4317';
40
+ const OTLP_DEFAULT_HTTP_PORT = '4318';
41
+ /**
42
+ * Resolves an append pattern for a given pre-existing value.
43
+ *
44
+ * Port of `distro.EvaluateReplacePattern`, INCLUDING the empty-original case: with nothing to join to,
45
+ * the placeholder and its adjacent delimiter are stripped, so the result never opens with a stray space
46
+ * or colon. `PYTHONPATH=":/var/odigos/python"` would put the current working directory on `sys.path`
47
+ * ahead of the agent, which is a different program than the one intended.
48
+ */
49
+ function evaluateAppendPattern(pattern, originalValue) {
50
+ const withDir = pattern.split(AGENTS_DIR_PLACEHOLDER).join(fargate_1.FARGATE_AGENTS_DIR);
51
+ if (originalValue.trim() === '') {
52
+ return withDir.split(ORIGINAL_ENV_VALUE_PLACEHOLDER).join('').replace(/^[ :]+/, '');
53
+ }
54
+ return withDir.split(ORIGINAL_ENV_VALUE_PLACEHOLDER).join(originalValue);
55
+ }
56
+ /**
57
+ * The delimiter a pattern joins the container's own value to ours with: the character immediately after
58
+ * the original-value placeholder.
59
+ *
60
+ * DERIVED from the pattern rather than declared, because the pattern is the authority — it comes from the
61
+ * upstream distro, differs by language (a space for `JAVA_TOOL_OPTIONS`/`NODE_OPTIONS`, a colon for
62
+ * `PYTHONPATH`) and can change on somebody else's release. Assuming one would be silently destructive in
63
+ * a way nothing here would catch: a space-joined `PYTHONPATH` is one unusable path rather than two, and
64
+ * the process still starts.
65
+ */
66
+ function appendSeparator(pattern) {
67
+ const i = pattern.indexOf(ORIGINAL_ENV_VALUE_PLACEHOLDER);
68
+ if (i < 0) {
69
+ return '';
70
+ }
71
+ const rest = pattern.slice(i + ORIGINAL_ENV_VALUE_PLACEHOLDER.length);
72
+ return rest === '' ? '' : rest.charAt(0);
73
+ }
74
+ /** The canonical connector language key for an enum member. The enum's values ARE those keys. */
75
+ function languageKeyOf(language) {
76
+ return language;
77
+ }
78
+ /**
79
+ * The recipe for a language, with the runtime version rendered into the agents' paths where the language
80
+ * needs it.
81
+ *
82
+ * @param runtimeVersion the application's runtime `major.minor`. Required for php and ruby, whose agent
83
+ * files are laid down per version, and ignored by every other language.
84
+ */
85
+ function recipeFor(language, runtimeVersion) {
86
+ const key = languageKeyOf(language);
87
+ const spec = fargate_1.FARGATE_LANGUAGES[key];
88
+ if (!spec) {
89
+ // Unreachable through the enum; reachable if the generated table and the enum ever disagree.
90
+ throw new Error(`Odigos: no ECS Fargate agent recipe for language "${key}".`);
91
+ }
92
+ if (spec.requiresRuntimeVersion && !runtimeVersion) {
93
+ throw new Error(`Odigos: the ${key} agent's files are laid down per runtime version, so instrumenting a ${key} ` +
94
+ 'container needs `runtimeVersion` set to the application\'s major.minor (for example "8.3"). ' +
95
+ 'The value selects the agent path the activation environment points at, so a wrong or missing ' +
96
+ 'one leaves the container with an agent that is never loaded.');
97
+ }
98
+ // Rejected here rather than passed through: a "8.3.4" or "php8.3" would be rendered straight into
99
+ // PHP_INI_SCAN_DIR / RUBYOPT as a path that does not exist in the bundle, and the container would start
100
+ // with no agent loaded and no error anywhere near the cause.
101
+ if (runtimeVersion && !/^\d+\.\d+$/.test(runtimeVersion)) {
102
+ throw new Error(`Odigos: runtimeVersion "${runtimeVersion}" must be exactly major.minor (for example "8.3") — it ` +
103
+ 'names a directory in the agents bundle, not a full version.');
104
+ }
105
+ const staticEnv = {};
106
+ for (const [k, v] of Object.entries(spec.staticEnv)) {
107
+ staticEnv[k] = runtimeVersion ? v.split(RUNTIME_VERSION_PLACEHOLDER).join(runtimeVersion) : v;
108
+ }
109
+ return {
110
+ language: key,
111
+ agentDirs: spec.agentDirs,
112
+ staticEnv,
113
+ appendEnv: spec.appendEnv.map((a) => ({ name: a.name, pattern: a.pattern })),
114
+ otlpProtocol: spec.otlpProtocol,
115
+ protocolFixed: spec.protocolFixed,
116
+ };
117
+ }
118
+ /**
119
+ * The staging container's command: create the shared directory, then copy each language's agent
120
+ * directory out of the image into it, then exit 0.
121
+ *
122
+ * ORDER AND SPELLING ARE THE CONTRACT. There is ONE staging container per task definition however many
123
+ * languages it holds, and the connector compares its command against the union of every instrumented
124
+ * container's agent directories in ascending order. A command that differs by a space is reported as
125
+ * drift on the whole task definition, so this is sorted and rendered exactly as `sidecarCommand` in the
126
+ * connector does it.
127
+ */
128
+ function sidecarCommand(agentDirs) {
129
+ const dirs = [...new Set(agentDirs)].sort();
130
+ let script = `mkdir -p ${fargate_1.FARGATE_AGENTS_DIR}`;
131
+ for (const d of dirs) {
132
+ script += ` && cp -r ${fargate_1.FARGATE_IMAGE_AGENTS_DIR}/${d} ${fargate_1.FARGATE_AGENTS_DIR}/${d}`;
133
+ }
134
+ return ['sh', '-c', script];
135
+ }
136
+ /**
137
+ * The resource attributes every span, metric and log from an instrumented task carries. `service.name` is
138
+ * set separately, through `OTEL_SERVICE_NAME`.
139
+ *
140
+ * The region may be an unresolved token on an environment-agnostic stack, which is fine here and unlike
141
+ * the Lambda layer lookup: this value is an environment variable, so the token resolves to a `Ref` to
142
+ * `AWS::Region` and CloudFormation fills in the real region at deploy time.
143
+ */
144
+ function fargateResourceAttributes(region) {
145
+ const attrs = 'cloud.provider=aws,cloud.platform=aws_ecs';
146
+ return region ? `${attrs},cloud.region=${region}` : attrs;
147
+ }
148
+ /**
149
+ * Turns a destination's OTLP/gRPC target (`host:port`) into the URL the OpenTelemetry SDK env accepts,
150
+ * taking the scheme from the destination's TLS setting. A value that is already a URL passes through.
151
+ */
152
+ function grpcEndpointURL(endpoint, insecure) {
153
+ if (endpoint === '') {
154
+ return '';
155
+ }
156
+ if (endpoint.includes('://')) {
157
+ return endpoint;
158
+ }
159
+ return `${insecure === true ? 'http' : 'https'}://${endpoint}`;
160
+ }
161
+ /**
162
+ * Resolves the endpoint and protocol for one recipe against one destination.
163
+ *
164
+ * Port of `exportTargetFor`. A Fargate task has no node-local collector — unlike Lambda, where the SDK
165
+ * exports to a collector in the same sandbox — so the agent exports OTLP straight to the destination, and
166
+ * which transport it can use is a property of the agent as much as of the destination.
167
+ *
168
+ * Each agent's PREFERRED transport is taken when the destination offers it. When it does not, an agent
169
+ * whose community SDK pins the protocol (php, ruby) cannot be switched and throws; every other agent
170
+ * honours `OTEL_EXPORTER_OTLP_PROTOCOL` and falls back to whichever transport exists.
171
+ */
172
+ function exportTargetFor(recipe, dest) {
173
+ const grpcURL = grpcEndpointURL(dest.endpoint ?? '', dest.insecure);
174
+ const httpURL = dest.httpEndpoint ?? '';
175
+ if (recipe.otlpProtocol === exports.OTLP_PROTOCOL_HTTP && httpURL !== '') {
176
+ return { endpoint: httpURL, protocol: exports.OTLP_PROTOCOL_HTTP };
177
+ }
178
+ if (recipe.otlpProtocol === exports.OTLP_PROTOCOL_GRPC && grpcURL !== '') {
179
+ return { endpoint: grpcURL, protocol: exports.OTLP_PROTOCOL_GRPC };
180
+ }
181
+ if (recipe.protocolFixed) {
182
+ throw new Error(`Odigos: the ${recipe.language} agent exports OTLP over ${recipe.otlpProtocol} only — its community ` +
183
+ 'SDK cannot be switched to the other transport — but the destination configures no ' +
184
+ `${recipe.otlpProtocol === exports.OTLP_PROTOCOL_HTTP ? '`httpEndpoint`' : '`endpoint`'}. ` +
185
+ 'Configure one, or leave this container uninstrumented.');
186
+ }
187
+ if (httpURL !== '') {
188
+ return { endpoint: httpURL, protocol: exports.OTLP_PROTOCOL_HTTP };
189
+ }
190
+ if (grpcURL !== '') {
191
+ return { endpoint: grpcURL, protocol: exports.OTLP_PROTOCOL_GRPC };
192
+ }
193
+ throw new Error('Odigos destination has neither `endpoint` (OTLP/gRPC) nor `httpEndpoint` (OTLP/HTTP), so an ' +
194
+ 'instrumented Fargate task would have nowhere to export. Unlike Lambda, a Fargate task has no ' +
195
+ 'in-sandbox collector to buffer for it — the agent exports to this destination directly.');
196
+ }
197
+ /** The port of a `scheme://host:port[/path]` endpoint, or "" when it names none. */
198
+ function endpointPort(endpoint) {
199
+ let rest = endpoint;
200
+ const scheme = rest.indexOf('://');
201
+ if (scheme >= 0) {
202
+ rest = rest.slice(scheme + 3);
203
+ }
204
+ const pathStart = rest.search(/[/?]/);
205
+ if (pathStart >= 0) {
206
+ rest = rest.slice(0, pathStart);
207
+ }
208
+ const colon = rest.lastIndexOf(':');
209
+ return colon < 0 ? '' : rest.slice(colon + 1);
210
+ }
211
+ /**
212
+ * A warning when an endpoint's port is the well-known port of the OTHER OTLP protocol, or "" when the
213
+ * pairing looks right — including a non-standard port, which cannot be judged.
214
+ *
215
+ * Worth a synth-time warning because the runtime symptom is a connection reset mid-export, which points
216
+ * nowhere near the cause: an HTTP request against an HTTP/2 gRPC listener, or the reverse.
217
+ */
218
+ function portProtocolMismatch(endpoint, protocol) {
219
+ const port = endpointPort(endpoint);
220
+ if (protocol === exports.OTLP_PROTOCOL_HTTP && port === OTLP_DEFAULT_GRPC_PORT) {
221
+ return (`endpoint ${endpoint} is on ${OTLP_DEFAULT_GRPC_PORT}, the standard OTLP/gRPC port, but this agent ` +
222
+ `exports over ${exports.OTLP_PROTOCOL_HTTP}; exports will fail as a connection reset. Point it at the ` +
223
+ `OTLP/HTTP port (${OTLP_DEFAULT_HTTP_PORT}).`);
224
+ }
225
+ if (protocol === exports.OTLP_PROTOCOL_GRPC && port === OTLP_DEFAULT_HTTP_PORT) {
226
+ return (`endpoint ${endpoint} is on ${OTLP_DEFAULT_HTTP_PORT}, the standard OTLP/HTTP port, but this agent ` +
227
+ `exports over gRPC; exports will fail. Point it at the OTLP/gRPC port (${OTLP_DEFAULT_GRPC_PORT}).`);
228
+ }
229
+ return '';
230
+ }
231
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFyZ2F0ZS1yZWNpcGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2ZhcmdhdGUtcmVjaXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUE0REEsc0RBTUM7QUFZRCwwQ0FPQztBQW1CRCxzQ0FFQztBQVNELDhCQXNDQztBQVlELHdDQU9DO0FBVUQsOERBR0M7QUFtQ0QsMENBNkJDO0FBd0JELG9EQWdCQztBQWpTRCxpREFJNkI7QUFHN0I7Ozs7Ozs7Ozs7O0dBV0c7QUFFSCwwR0FBMEc7QUFDMUcsTUFBTSw4QkFBOEIsR0FBRyx3QkFBd0IsQ0FBQztBQUVoRSxpRUFBaUU7QUFDakUsTUFBTSxzQkFBc0IsR0FBRyx1QkFBdUIsQ0FBQztBQUV2RDs7OztHQUlHO0FBQ0gsTUFBTSwyQkFBMkIsR0FBRyxrQ0FBa0MsQ0FBQztBQUV2RSwrRkFBK0Y7QUFDbEYsUUFBQSxrQkFBa0IsR0FBRyxNQUFNLENBQUM7QUFDNUIsUUFBQSxrQkFBa0IsR0FBRyxlQUFlLENBQUM7QUFFbEQsd0dBQXdHO0FBQ3hHLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFDO0FBQ3RDLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFDO0FBYXRDOzs7Ozs7O0dBT0c7QUFDSCxTQUFnQixxQkFBcUIsQ0FBQyxPQUFlLEVBQUUsYUFBcUI7SUFDMUUsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDLElBQUksQ0FBQyw0QkFBa0IsQ0FBQyxDQUFDO0lBQy9FLElBQUksYUFBYSxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ2hDLE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3RGLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQyxLQUFLLENBQUMsOEJBQThCLENBQUMsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUM7QUFDM0UsQ0FBQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILFNBQWdCLGVBQWUsQ0FBQyxPQUFlO0lBQzdDLE1BQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUMxRCxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNWLE9BQU8sRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUNELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLDhCQUE4QixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3RFLE9BQU8sSUFBSSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzNDLENBQUM7QUFrQkQsaUdBQWlHO0FBQ2pHLFNBQWdCLGFBQWEsQ0FBQyxRQUErQjtJQUMzRCxPQUFPLFFBQVEsQ0FBQztBQUNsQixDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsU0FBZ0IsU0FBUyxDQUFDLFFBQStCLEVBQUUsY0FBdUI7SUFDaEYsTUFBTSxHQUFHLEdBQUcsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLDJCQUFpQixDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3BDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNWLDZGQUE2RjtRQUM3RixNQUFNLElBQUksS0FBSyxDQUFDLHFEQUFxRCxHQUFHLElBQUksQ0FBQyxDQUFDO0lBQ2hGLENBQUM7SUFDRCxJQUFJLElBQUksQ0FBQyxzQkFBc0IsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ25ELE1BQU0sSUFBSSxLQUFLLENBQ2IsZUFBZSxHQUFHLHdFQUF3RSxHQUFHLEdBQUc7WUFDOUYsOEZBQThGO1lBQzlGLCtGQUErRjtZQUMvRiw4REFBOEQsQ0FDakUsQ0FBQztJQUNKLENBQUM7SUFDRCxrR0FBa0c7SUFDbEcsd0dBQXdHO0lBQ3hHLDZEQUE2RDtJQUM3RCxJQUFJLGNBQWMsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztRQUN6RCxNQUFNLElBQUksS0FBSyxDQUNiLDJCQUEyQixjQUFjLHlEQUF5RDtZQUNoRyw2REFBNkQsQ0FDaEUsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLFNBQVMsR0FBOEIsRUFBRSxDQUFDO0lBQ2hELEtBQUssTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO1FBQ3BELFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsMkJBQTJCLENBQUMsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNoRyxDQUFDO0lBRUQsT0FBTztRQUNMLFFBQVEsRUFBRSxHQUFHO1FBQ2IsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTO1FBQ3pCLFNBQVM7UUFDVCxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFDNUUsWUFBWSxFQUFFLElBQUksQ0FBQyxZQUFZO1FBQy9CLGFBQWEsRUFBRSxJQUFJLENBQUMsYUFBYTtLQUNsQyxDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILFNBQWdCLGNBQWMsQ0FBQyxTQUFtQjtJQUNoRCxNQUFNLElBQUksR0FBRyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUM1QyxJQUFJLE1BQU0sR0FBRyxZQUFZLDRCQUFrQixFQUFFLENBQUM7SUFDOUMsS0FBSyxNQUFNLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUNyQixNQUFNLElBQUksYUFBYSxrQ0FBd0IsSUFBSSxDQUFDLElBQUksNEJBQWtCLElBQUksQ0FBQyxFQUFFLENBQUM7SUFDcEYsQ0FBQztJQUNELE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBQzlCLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsU0FBZ0IseUJBQXlCLENBQUMsTUFBYztJQUN0RCxNQUFNLEtBQUssR0FBRywyQ0FBMkMsQ0FBQztJQUMxRCxPQUFPLE1BQU0sQ0FBQyxDQUFDLENBQUMsR0FBRyxLQUFLLGlCQUFpQixNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDO0FBQzVELENBQUM7QUFVRDs7O0dBR0c7QUFDSCxTQUFTLGVBQWUsQ0FBQyxRQUFnQixFQUFFLFFBQWtCO0lBQzNELElBQUksUUFBUSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ3BCLE9BQU8sRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUNELElBQUksUUFBUSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzdCLE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUM7SUFDRCxPQUFPLEdBQUcsUUFBUSxLQUFLLElBQUksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLE1BQU0sUUFBUSxFQUFFLENBQUM7QUFDakUsQ0FBQztBQUVEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxTQUFnQixlQUFlLENBQUMsTUFBcUIsRUFBRSxJQUF1QjtJQUM1RSxNQUFNLE9BQU8sR0FBRyxlQUFlLENBQUMsSUFBSSxDQUFDLFFBQVEsSUFBSSxFQUFFLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3BFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxZQUFZLElBQUksRUFBRSxDQUFDO0lBRXhDLElBQUksTUFBTSxDQUFDLFlBQVksS0FBSywwQkFBa0IsSUFBSSxPQUFPLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDakUsT0FBTyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLDBCQUFrQixFQUFFLENBQUM7SUFDN0QsQ0FBQztJQUNELElBQUksTUFBTSxDQUFDLFlBQVksS0FBSywwQkFBa0IsSUFBSSxPQUFPLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDakUsT0FBTyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLDBCQUFrQixFQUFFLENBQUM7SUFDN0QsQ0FBQztJQUNELElBQUksTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ3pCLE1BQU0sSUFBSSxLQUFLLENBQ2IsZUFBZSxNQUFNLENBQUMsUUFBUSw0QkFBNEIsTUFBTSxDQUFDLFlBQVksd0JBQXdCO1lBQ25HLG9GQUFvRjtZQUNwRixHQUFHLE1BQU0sQ0FBQyxZQUFZLEtBQUssMEJBQWtCLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxZQUFZLElBQUk7WUFDbkYsd0RBQXdELENBQzNELENBQUM7SUFDSixDQUFDO0lBQ0QsSUFBSSxPQUFPLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDbkIsT0FBTyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLDBCQUFrQixFQUFFLENBQUM7SUFDN0QsQ0FBQztJQUNELElBQUksT0FBTyxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ25CLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSwwQkFBa0IsRUFBRSxDQUFDO0lBQzdELENBQUM7SUFDRCxNQUFNLElBQUksS0FBSyxDQUNiLDhGQUE4RjtRQUM1RiwrRkFBK0Y7UUFDL0YseUZBQXlGLENBQzVGLENBQUM7QUFDSixDQUFDO0FBRUQsb0ZBQW9GO0FBQ3BGLFNBQVMsWUFBWSxDQUFDLFFBQWdCO0lBQ3BDLElBQUksSUFBSSxHQUFHLFFBQVEsQ0FBQztJQUNwQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ25DLElBQUksTUFBTSxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ2hCLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNoQyxDQUFDO0lBQ0QsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN0QyxJQUFJLFNBQVMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUNuQixJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDbEMsQ0FBQztJQUNELE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDcEMsT0FBTyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBQ2hELENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxTQUFnQixvQkFBb0IsQ0FBQyxRQUFnQixFQUFFLFFBQWdCO0lBQ3JFLE1BQU0sSUFBSSxHQUFHLFlBQVksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNwQyxJQUFJLFFBQVEsS0FBSywwQkFBa0IsSUFBSSxJQUFJLEtBQUssc0JBQXNCLEVBQUUsQ0FBQztRQUN2RSxPQUFPLENBQ0wsWUFBWSxRQUFRLFVBQVUsc0JBQXNCLGdEQUFnRDtZQUNwRyxnQkFBZ0IsMEJBQWtCLDZEQUE2RDtZQUMvRixtQkFBbUIsc0JBQXNCLElBQUksQ0FDOUMsQ0FBQztJQUNKLENBQUM7SUFDRCxJQUFJLFFBQVEsS0FBSywwQkFBa0IsSUFBSSxJQUFJLEtBQUssc0JBQXNCLEVBQUUsQ0FBQztRQUN2RSxPQUFPLENBQ0wsWUFBWSxRQUFRLFVBQVUsc0JBQXNCLGdEQUFnRDtZQUNwRyx5RUFBeUUsc0JBQXNCLElBQUksQ0FDcEcsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBGQVJHQVRFX0FHRU5UU19ESVIsXG4gIEZBUkdBVEVfSU1BR0VfQUdFTlRTX0RJUixcbiAgRkFSR0FURV9MQU5HVUFHRVMsXG59IGZyb20gJy4vZ2VuZXJhdGVkL2ZhcmdhdGUnO1xuaW1wb3J0IHsgT2RpZ29zRGVzdGluYXRpb24sIE9kaWdvc0ZhcmdhdGVMYW5ndWFnZSB9IGZyb20gJy4vdHlwZXMnO1xuXG4vKipcbiAqIEhvdyBhIGxhbmd1YWdlJ3MgYWdlbnQgaXMgc3RhZ2VkIGludG8gYW4gRUNTIEZhcmdhdGUgdGFzayBhbmQgYWN0aXZhdGVkIGluIGEgY29udGFpbmVyLlxuICpcbiAqIEEgUE9SVCBvZiBgcmVjaXBlRm9yYCAvIGBhcHBseUluc3RydW1lbnRhdGlvbmAgaW4gdGhlIGNvbm5lY3RvcidzIEVDUyBpbnN0cnVtZW50b3JcbiAqIChjb25uZWN0b3JzL3Byb3ZpZGVycy9hd3MvcGtnL3dvcmtsb2FkL2luc3RydW1lbnRvci9lY3NfcmVjaXBlcy5nbywgZWNzX2luamVjdC5nbykuIFRoZSBkYXRhIGxpdmVzIGluXG4gKiAuLi8uLi9hcnRpZmFjdHMuanNvbiBhbmQgcmVhY2hlcyBoZXJlIHRocm91Z2ggdGhlIGdlbmVyYXRlZCBtb2R1bGU7IHRoZSBkZWNpc2lvbnMgbGl2ZSBoZXJlLlxuICpcbiAqIEVxdWl2YWxlbmNlIHdpdGggdGhlIGNvbm5lY3RvciBpcyBub3QgY29zbWV0aWMuIEl0cyBjb25mb3JtYW5jZSBvYnNlcnZlciByZWJ1aWxkcyBlYWNoIGNvbnRhaW5lcidzXG4gKiBkZXNpcmVkIGFjdGl2YXRpb24gZW52aXJvbm1lbnQgYW5kIGNvbXBhcmVzIGl0IGFnYWluc3Qgd2hhdCB0aGUgY29udGFpbmVyIGNhcnJpZXMsIGNvbXBvbmVudCBieVxuICogY29tcG9uZW50LCBzbyBhbnl0aGluZyByZW5kZXJlZCBkaWZmZXJlbnRseSBoZXJlIGlzIHJlcG9ydGVkIHRvIHRoZSBjdXN0b21lciBhcyBpbnN0cnVtZW50YXRpb24gZHJpZnRcbiAqIG9uIGEgc3RhY2sgdGhhdCBpcyBpbiBmYWN0IGRlcGxveWVkIGV4YWN0bHkgYXMgdGhleSB3cm90ZSBpdC5cbiAqL1xuXG4vKiogVGhlIGRpc3RybyBwbGFjZWhvbGRlciBmb3IgdGhlIGNvbnRhaW5lcidzIG93biBwcmUtT2RpZ29zIHZhbHVlIG9mIGFuIGFwcGVuZC1zdHlsZSBsb2FkZXIgdmFyaWFibGUuICovXG5jb25zdCBPUklHSU5BTF9FTlZfVkFMVUVfUExBQ0VIT0xERVIgPSAne3tPUklHSU5BTF9FTlZfVkFMVUV9fSc7XG5cbi8qKiBUaGUgZGlzdHJvIHBsYWNlaG9sZGVyIGZvciB0aGUgc2hhcmVkIHZvbHVtZSdzIG1vdW50IHBhdGguICovXG5jb25zdCBBR0VOVFNfRElSX1BMQUNFSE9MREVSID0gJ3t7T0RJR09TX0FHRU5UU19ESVJ9fSc7XG5cbi8qKlxuICogVGhlIGRpc3RybyBwYXJhbWV0ZXIgZm9yIHRoZSBhcHBsaWNhdGlvbidzIHJ1bnRpbWUgYG1ham9yLm1pbm9yYCwgYXMgYSBHbyB0ZXh0L3RlbXBsYXRlIGFjdGlvbiDigJRcbiAqIHZlcmJhdGltIGZyb20gdGhlIGRpc3RybyB5YW1sIHNvIC4uLy4uL2FydGlmYWN0cy5qc29uIGNhbiBiZSBkaWZmZWQgYWdhaW5zdCBpdCBieSBleWUuIE9ubHkgcGhwIGFuZFxuICogcnVieSBjYXJyeSBpdDogdGhlaXIgYWdlbnRzIGFyZSBsYWlkIGRvd24gcGVyIHJ1bnRpbWUgdmVyc2lvbi5cbiAqL1xuY29uc3QgUlVOVElNRV9WRVJTSU9OX1BMQUNFSE9MREVSID0gJ3t7LlJVTlRJTUVfVkVSU0lPTl9NQUpPUl9NSU5PUn19JztcblxuLyoqIGBPVEVMX0VYUE9SVEVSX09UTFBfUFJPVE9DT0xgIHZhbHVlcy4gRW5kcG9pbnQgYW5kIHByb3RvY29sIGFyZSBhbHdheXMgZGVjaWRlZCB0b2dldGhlci4gKi9cbmV4cG9ydCBjb25zdCBPVExQX1BST1RPQ09MX0dSUEMgPSAnZ3JwYyc7XG5leHBvcnQgY29uc3QgT1RMUF9QUk9UT0NPTF9IVFRQID0gJ2h0dHAvcHJvdG9idWYnO1xuXG4vKiogU3RhbmRhcmQgT1RMUCByZWNlaXZlciBwb3J0cywgdXNlZCBvbmx5IHRvIHdhcm4gYWJvdXQgYW4gZW5kcG9pbnQgcGFpcmVkIHdpdGggdGhlIG90aGVyIHByb3RvY29sLiAqL1xuY29uc3QgT1RMUF9ERUZBVUxUX0dSUENfUE9SVCA9ICc0MzE3JztcbmNvbnN0IE9UTFBfREVGQVVMVF9IVFRQX1BPUlQgPSAnNDMxOCc7XG5cbi8qKlxuICogQW4gYXBwZW5kLXN0eWxlIGxvYWRlciB2YXJpYWJsZSDigJQgYEpBVkFfVE9PTF9PUFRJT05TYCwgYE5PREVfT1BUSU9OU2AsIGBQWVRIT05QQVRIYCDigJQga2VwdCBhcyBpdHNcbiAqIHBhdHRlcm4gcmF0aGVyIHRoYW4gYSBmaW5pc2hlZCB2YWx1ZSwgYmVjYXVzZSB0aGUgdmFsdWUgZGVwZW5kcyBvbiB3aGF0IHRoZSBjb250YWluZXIgYWxyZWFkeSBjYXJyaWVzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEZhcmdhdGVBcHBlbmRFbnZWYXIge1xuICAvKiogVmFyaWFibGUgbmFtZS4gKi9cbiAgcmVhZG9ubHkgbmFtZTogc3RyaW5nO1xuICAvKiogVGhlIGRpc3RybydzIFJlcGxhY2VQYXR0ZXJuLCBwbGFjZWhvbGRlcnMgdW5yZXNvbHZlZC4gKi9cbiAgcmVhZG9ubHkgcGF0dGVybjogc3RyaW5nO1xufVxuXG4vKipcbiAqIFJlc29sdmVzIGFuIGFwcGVuZCBwYXR0ZXJuIGZvciBhIGdpdmVuIHByZS1leGlzdGluZyB2YWx1ZS5cbiAqXG4gKiBQb3J0IG9mIGBkaXN0cm8uRXZhbHVhdGVSZXBsYWNlUGF0dGVybmAsIElOQ0xVRElORyB0aGUgZW1wdHktb3JpZ2luYWwgY2FzZTogd2l0aCBub3RoaW5nIHRvIGpvaW4gdG8sXG4gKiB0aGUgcGxhY2Vob2xkZXIgYW5kIGl0cyBhZGphY2VudCBkZWxpbWl0ZXIgYXJlIHN0cmlwcGVkLCBzbyB0aGUgcmVzdWx0IG5ldmVyIG9wZW5zIHdpdGggYSBzdHJheSBzcGFjZVxuICogb3IgY29sb24uIGBQWVRIT05QQVRIPVwiOi92YXIvb2RpZ29zL3B5dGhvblwiYCB3b3VsZCBwdXQgdGhlIGN1cnJlbnQgd29ya2luZyBkaXJlY3Rvcnkgb24gYHN5cy5wYXRoYFxuICogYWhlYWQgb2YgdGhlIGFnZW50LCB3aGljaCBpcyBhIGRpZmZlcmVudCBwcm9ncmFtIHRoYW4gdGhlIG9uZSBpbnRlbmRlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV2YWx1YXRlQXBwZW5kUGF0dGVybihwYXR0ZXJuOiBzdHJpbmcsIG9yaWdpbmFsVmFsdWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHdpdGhEaXIgPSBwYXR0ZXJuLnNwbGl0KEFHRU5UU19ESVJfUExBQ0VIT0xERVIpLmpvaW4oRkFSR0FURV9BR0VOVFNfRElSKTtcbiAgaWYgKG9yaWdpbmFsVmFsdWUudHJpbSgpID09PSAnJykge1xuICAgIHJldHVybiB3aXRoRGlyLnNwbGl0KE9SSUdJTkFMX0VOVl9WQUxVRV9QTEFDRUhPTERFUikuam9pbignJykucmVwbGFjZSgvXlsgOl0rLywgJycpO1xuICB9XG4gIHJldHVybiB3aXRoRGlyLnNwbGl0KE9SSUdJTkFMX0VOVl9WQUxVRV9QTEFDRUhPTERFUikuam9pbihvcmlnaW5hbFZhbHVlKTtcbn1cblxuLyoqXG4gKiBUaGUgZGVsaW1pdGVyIGEgcGF0dGVybiBqb2lucyB0aGUgY29udGFpbmVyJ3Mgb3duIHZhbHVlIHRvIG91cnMgd2l0aDogdGhlIGNoYXJhY3RlciBpbW1lZGlhdGVseSBhZnRlclxuICogdGhlIG9yaWdpbmFsLXZhbHVlIHBsYWNlaG9sZGVyLlxuICpcbiAqIERFUklWRUQgZnJvbSB0aGUgcGF0dGVybiByYXRoZXIgdGhhbiBkZWNsYXJlZCwgYmVjYXVzZSB0aGUgcGF0dGVybiBpcyB0aGUgYXV0aG9yaXR5IOKAlCBpdCBjb21lcyBmcm9tIHRoZVxuICogdXBzdHJlYW0gZGlzdHJvLCBkaWZmZXJzIGJ5IGxhbmd1YWdlIChhIHNwYWNlIGZvciBgSkFWQV9UT09MX09QVElPTlNgL2BOT0RFX09QVElPTlNgLCBhIGNvbG9uIGZvclxuICogYFBZVEhPTlBBVEhgKSBhbmQgY2FuIGNoYW5nZSBvbiBzb21lYm9keSBlbHNlJ3MgcmVsZWFzZS4gQXNzdW1pbmcgb25lIHdvdWxkIGJlIHNpbGVudGx5IGRlc3RydWN0aXZlIGluXG4gKiBhIHdheSBub3RoaW5nIGhlcmUgd291bGQgY2F0Y2g6IGEgc3BhY2Utam9pbmVkIGBQWVRIT05QQVRIYCBpcyBvbmUgdW51c2FibGUgcGF0aCByYXRoZXIgdGhhbiB0d28sIGFuZFxuICogdGhlIHByb2Nlc3Mgc3RpbGwgc3RhcnRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYXBwZW5kU2VwYXJhdG9yKHBhdHRlcm46IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IGkgPSBwYXR0ZXJuLmluZGV4T2YoT1JJR0lOQUxfRU5WX1ZBTFVFX1BMQUNFSE9MREVSKTtcbiAgaWYgKGkgPCAwKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG4gIGNvbnN0IHJlc3QgPSBwYXR0ZXJuLnNsaWNlKGkgKyBPUklHSU5BTF9FTlZfVkFMVUVfUExBQ0VIT0xERVIubGVuZ3RoKTtcbiAgcmV0dXJuIHJlc3QgPT09ICcnID8gJycgOiByZXN0LmNoYXJBdCgwKTtcbn1cblxuLyoqIE9uZSBsYW5ndWFnZSdzIHN0YWdpbmcgYW5kIGFjdGl2YXRpb24sIHdpdGggZXZlcnkgcGxhdGZvcm0tc3VwcGxpZWQgcGFyYW1ldGVyIGFscmVhZHkgcmVuZGVyZWQuICovXG5leHBvcnQgaW50ZXJmYWNlIEZhcmdhdGVSZWNpcGUge1xuICAvKiogQ2Fub25pY2FsIGxhbmd1YWdlIGtleSwgYW5kIHRoZSB2YWx1ZSByZWNvcmRlZCBpbiB0aGUgYE9ESUdPU19JQUNgIG1hcmtlcidzIGBsYW5nYC4gKi9cbiAgcmVhZG9ubHkgbGFuZ3VhZ2U6IHN0cmluZztcbiAgLyoqIERpcmVjdG9yaWVzIHVuZGVyIHRoZSBhZ2VudHMgaW1hZ2UgdGhlIHN0YWdpbmcgY29udGFpbmVyIGNvcGllcyBpbnRvIHRoZSBzaGFyZWQgdm9sdW1lLiAqL1xuICByZWFkb25seSBhZ2VudERpcnM6IHN0cmluZ1tdO1xuICAvKiogQWN0aXZhdGlvbiB2YXJpYWJsZXMgc2V0IG91dHJpZ2h0LiAqL1xuICByZWFkb25seSBzdGF0aWNFbnY6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH07XG4gIC8qKiBBY3RpdmF0aW9uIHZhcmlhYmxlcyBvdXIgZnJhZ21lbnQgaXMgYXBwZW5kZWQgdG8uICovXG4gIHJlYWRvbmx5IGFwcGVuZEVudjogRmFyZ2F0ZUFwcGVuZEVudlZhcltdO1xuICAvKiogVGhlIHRyYW5zcG9ydCB0aGlzIGFnZW50IHByZWZlcnMuICovXG4gIHJlYWRvbmx5IG90bHBQcm90b2NvbDogc3RyaW5nO1xuICAvKiogV2hldGhlciB0aGUgYWdlbnQgY2FuIHNwZWFrIG9ubHkgYG90bHBQcm90b2NvbGAgYW5kIGNhbm5vdCBiZSBzd2l0Y2hlZCB0byB0aGUgb3RoZXIgdHJhbnNwb3J0LiAqL1xuICByZWFkb25seSBwcm90b2NvbEZpeGVkOiBib29sZWFuO1xufVxuXG4vKiogVGhlIGNhbm9uaWNhbCBjb25uZWN0b3IgbGFuZ3VhZ2Uga2V5IGZvciBhbiBlbnVtIG1lbWJlci4gVGhlIGVudW0ncyB2YWx1ZXMgQVJFIHRob3NlIGtleXMuICovXG5leHBvcnQgZnVuY3Rpb24gbGFuZ3VhZ2VLZXlPZihsYW5ndWFnZTogT2RpZ29zRmFyZ2F0ZUxhbmd1YWdlKTogc3RyaW5nIHtcbiAgcmV0dXJuIGxhbmd1YWdlO1xufVxuXG4vKipcbiAqIFRoZSByZWNpcGUgZm9yIGEgbGFuZ3VhZ2UsIHdpdGggdGhlIHJ1bnRpbWUgdmVyc2lvbiByZW5kZXJlZCBpbnRvIHRoZSBhZ2VudHMnIHBhdGhzIHdoZXJlIHRoZSBsYW5ndWFnZVxuICogbmVlZHMgaXQuXG4gKlxuICogQHBhcmFtIHJ1bnRpbWVWZXJzaW9uIHRoZSBhcHBsaWNhdGlvbidzIHJ1bnRpbWUgYG1ham9yLm1pbm9yYC4gUmVxdWlyZWQgZm9yIHBocCBhbmQgcnVieSwgd2hvc2UgYWdlbnRcbiAqIGZpbGVzIGFyZSBsYWlkIGRvd24gcGVyIHZlcnNpb24sIGFuZCBpZ25vcmVkIGJ5IGV2ZXJ5IG90aGVyIGxhbmd1YWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVjaXBlRm9yKGxhbmd1YWdlOiBPZGlnb3NGYXJnYXRlTGFuZ3VhZ2UsIHJ1bnRpbWVWZXJzaW9uPzogc3RyaW5nKTogRmFyZ2F0ZVJlY2lwZSB7XG4gIGNvbnN0IGtleSA9IGxhbmd1YWdlS2V5T2YobGFuZ3VhZ2UpO1xuICBjb25zdCBzcGVjID0gRkFSR0FURV9MQU5HVUFHRVNba2V5XTtcbiAgaWYgKCFzcGVjKSB7XG4gICAgLy8gVW5yZWFjaGFibGUgdGhyb3VnaCB0aGUgZW51bTsgcmVhY2hhYmxlIGlmIHRoZSBnZW5lcmF0ZWQgdGFibGUgYW5kIHRoZSBlbnVtIGV2ZXIgZGlzYWdyZWUuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBPZGlnb3M6IG5vIEVDUyBGYXJnYXRlIGFnZW50IHJlY2lwZSBmb3IgbGFuZ3VhZ2UgXCIke2tleX1cIi5gKTtcbiAgfVxuICBpZiAoc3BlYy5yZXF1aXJlc1J1bnRpbWVWZXJzaW9uICYmICFydW50aW1lVmVyc2lvbikge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBPZGlnb3M6IHRoZSAke2tleX0gYWdlbnQncyBmaWxlcyBhcmUgbGFpZCBkb3duIHBlciBydW50aW1lIHZlcnNpb24sIHNvIGluc3RydW1lbnRpbmcgYSAke2tleX0gYCArXG4gICAgICAgICdjb250YWluZXIgbmVlZHMgYHJ1bnRpbWVWZXJzaW9uYCBzZXQgdG8gdGhlIGFwcGxpY2F0aW9uXFwncyBtYWpvci5taW5vciAoZm9yIGV4YW1wbGUgXCI4LjNcIikuICcgK1xuICAgICAgICAnVGhlIHZhbHVlIHNlbGVjdHMgdGhlIGFnZW50IHBhdGggdGhlIGFjdGl2YXRpb24gZW52aXJvbm1lbnQgcG9pbnRzIGF0LCBzbyBhIHdyb25nIG9yIG1pc3NpbmcgJyArXG4gICAgICAgICdvbmUgbGVhdmVzIHRoZSBjb250YWluZXIgd2l0aCBhbiBhZ2VudCB0aGF0IGlzIG5ldmVyIGxvYWRlZC4nLFxuICAgICk7XG4gIH1cbiAgLy8gUmVqZWN0ZWQgaGVyZSByYXRoZXIgdGhhbiBwYXNzZWQgdGhyb3VnaDogYSBcIjguMy40XCIgb3IgXCJwaHA4LjNcIiB3b3VsZCBiZSByZW5kZXJlZCBzdHJhaWdodCBpbnRvXG4gIC8vIFBIUF9JTklfU0NBTl9ESVIgLyBSVUJZT1BUIGFzIGEgcGF0aCB0aGF0IGRvZXMgbm90IGV4aXN0IGluIHRoZSBidW5kbGUsIGFuZCB0aGUgY29udGFpbmVyIHdvdWxkIHN0YXJ0XG4gIC8vIHdpdGggbm8gYWdlbnQgbG9hZGVkIGFuZCBubyBlcnJvciBhbnl3aGVyZSBuZWFyIHRoZSBjYXVzZS5cbiAgaWYgKHJ1bnRpbWVWZXJzaW9uICYmICEvXlxcZCtcXC5cXGQrJC8udGVzdChydW50aW1lVmVyc2lvbikpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgT2RpZ29zOiBydW50aW1lVmVyc2lvbiBcIiR7cnVudGltZVZlcnNpb259XCIgbXVzdCBiZSBleGFjdGx5IG1ham9yLm1pbm9yIChmb3IgZXhhbXBsZSBcIjguM1wiKSDigJQgaXQgYCArXG4gICAgICAgICduYW1lcyBhIGRpcmVjdG9yeSBpbiB0aGUgYWdlbnRzIGJ1bmRsZSwgbm90IGEgZnVsbCB2ZXJzaW9uLicsXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHN0YXRpY0VudjogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfSA9IHt9O1xuICBmb3IgKGNvbnN0IFtrLCB2XSBvZiBPYmplY3QuZW50cmllcyhzcGVjLnN0YXRpY0VudikpIHtcbiAgICBzdGF0aWNFbnZba10gPSBydW50aW1lVmVyc2lvbiA/IHYuc3BsaXQoUlVOVElNRV9WRVJTSU9OX1BMQUNFSE9MREVSKS5qb2luKHJ1bnRpbWVWZXJzaW9uKSA6IHY7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGxhbmd1YWdlOiBrZXksXG4gICAgYWdlbnREaXJzOiBzcGVjLmFnZW50RGlycyxcbiAgICBzdGF0aWNFbnYsXG4gICAgYXBwZW5kRW52OiBzcGVjLmFwcGVuZEVudi5tYXAoKGEpID0+ICh7IG5hbWU6IGEubmFtZSwgcGF0dGVybjogYS5wYXR0ZXJuIH0pKSxcbiAgICBvdGxwUHJvdG9jb2w6IHNwZWMub3RscFByb3RvY29sLFxuICAgIHByb3RvY29sRml4ZWQ6IHNwZWMucHJvdG9jb2xGaXhlZCxcbiAgfTtcbn1cblxuLyoqXG4gKiBUaGUgc3RhZ2luZyBjb250YWluZXIncyBjb21tYW5kOiBjcmVhdGUgdGhlIHNoYXJlZCBkaXJlY3RvcnksIHRoZW4gY29weSBlYWNoIGxhbmd1YWdlJ3MgYWdlbnRcbiAqIGRpcmVjdG9yeSBvdXQgb2YgdGhlIGltYWdlIGludG8gaXQsIHRoZW4gZXhpdCAwLlxuICpcbiAqIE9SREVSIEFORCBTUEVMTElORyBBUkUgVEhFIENPTlRSQUNULiBUaGVyZSBpcyBPTkUgc3RhZ2luZyBjb250YWluZXIgcGVyIHRhc2sgZGVmaW5pdGlvbiBob3dldmVyIG1hbnlcbiAqIGxhbmd1YWdlcyBpdCBob2xkcywgYW5kIHRoZSBjb25uZWN0b3IgY29tcGFyZXMgaXRzIGNvbW1hbmQgYWdhaW5zdCB0aGUgdW5pb24gb2YgZXZlcnkgaW5zdHJ1bWVudGVkXG4gKiBjb250YWluZXIncyBhZ2VudCBkaXJlY3RvcmllcyBpbiBhc2NlbmRpbmcgb3JkZXIuIEEgY29tbWFuZCB0aGF0IGRpZmZlcnMgYnkgYSBzcGFjZSBpcyByZXBvcnRlZCBhc1xuICogZHJpZnQgb24gdGhlIHdob2xlIHRhc2sgZGVmaW5pdGlvbiwgc28gdGhpcyBpcyBzb3J0ZWQgYW5kIHJlbmRlcmVkIGV4YWN0bHkgYXMgYHNpZGVjYXJDb21tYW5kYCBpbiB0aGVcbiAqIGNvbm5lY3RvciBkb2VzIGl0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2lkZWNhckNvbW1hbmQoYWdlbnREaXJzOiBzdHJpbmdbXSk6IHN0cmluZ1tdIHtcbiAgY29uc3QgZGlycyA9IFsuLi5uZXcgU2V0KGFnZW50RGlycyldLnNvcnQoKTtcbiAgbGV0IHNjcmlwdCA9IGBta2RpciAtcCAke0ZBUkdBVEVfQUdFTlRTX0RJUn1gO1xuICBmb3IgKGNvbnN0IGQgb2YgZGlycykge1xuICAgIHNjcmlwdCArPSBgICYmIGNwIC1yICR7RkFSR0FURV9JTUFHRV9BR0VOVFNfRElSfS8ke2R9ICR7RkFSR0FURV9BR0VOVFNfRElSfS8ke2R9YDtcbiAgfVxuICByZXR1cm4gWydzaCcsICctYycsIHNjcmlwdF07XG59XG5cbi8qKlxuICogVGhlIHJlc291cmNlIGF0dHJpYnV0ZXMgZXZlcnkgc3BhbiwgbWV0cmljIGFuZCBsb2cgZnJvbSBhbiBpbnN0cnVtZW50ZWQgdGFzayBjYXJyaWVzLiBgc2VydmljZS5uYW1lYCBpc1xuICogc2V0IHNlcGFyYXRlbHksIHRocm91Z2ggYE9URUxfU0VSVklDRV9OQU1FYC5cbiAqXG4gKiBUaGUgcmVnaW9uIG1heSBiZSBhbiB1bnJlc29sdmVkIHRva2VuIG9uIGFuIGVudmlyb25tZW50LWFnbm9zdGljIHN0YWNrLCB3aGljaCBpcyBmaW5lIGhlcmUgYW5kIHVubGlrZVxuICogdGhlIExhbWJkYSBsYXllciBsb29rdXA6IHRoaXMgdmFsdWUgaXMgYW4gZW52aXJvbm1lbnQgdmFyaWFibGUsIHNvIHRoZSB0b2tlbiByZXNvbHZlcyB0byBhIGBSZWZgIHRvXG4gKiBgQVdTOjpSZWdpb25gIGFuZCBDbG91ZEZvcm1hdGlvbiBmaWxscyBpbiB0aGUgcmVhbCByZWdpb24gYXQgZGVwbG95IHRpbWUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmYXJnYXRlUmVzb3VyY2VBdHRyaWJ1dGVzKHJlZ2lvbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgYXR0cnMgPSAnY2xvdWQucHJvdmlkZXI9YXdzLGNsb3VkLnBsYXRmb3JtPWF3c19lY3MnO1xuICByZXR1cm4gcmVnaW9uID8gYCR7YXR0cnN9LGNsb3VkLnJlZ2lvbj0ke3JlZ2lvbn1gIDogYXR0cnM7XG59XG5cbi8qKiBXaGVyZSBhbiBpbnN0cnVtZW50ZWQgY29udGFpbmVyIGV4cG9ydHMgdG8sIGFuZCBvdmVyIHdoaWNoIHRyYW5zcG9ydC4gQWx3YXlzIGRlY2lkZWQgYXMgYSBwYWlyLiAqL1xuZXhwb3J0IGludGVyZmFjZSBGYXJnYXRlRXhwb3J0VGFyZ2V0IHtcbiAgLyoqIGBzY2hlbWU6Ly9ob3N0OnBvcnRgLiAqL1xuICByZWFkb25seSBlbmRwb2ludDogc3RyaW5nO1xuICAvKiogYGdycGNgIG9yIGBodHRwL3Byb3RvYnVmYC4gKi9cbiAgcmVhZG9ubHkgcHJvdG9jb2w6IHN0cmluZztcbn1cblxuLyoqXG4gKiBUdXJucyBhIGRlc3RpbmF0aW9uJ3MgT1RMUC9nUlBDIHRhcmdldCAoYGhvc3Q6cG9ydGApIGludG8gdGhlIFVSTCB0aGUgT3BlblRlbGVtZXRyeSBTREsgZW52IGFjY2VwdHMsXG4gKiB0YWtpbmcgdGhlIHNjaGVtZSBmcm9tIHRoZSBkZXN0aW5hdGlvbidzIFRMUyBzZXR0aW5nLiBBIHZhbHVlIHRoYXQgaXMgYWxyZWFkeSBhIFVSTCBwYXNzZXMgdGhyb3VnaC5cbiAqL1xuZnVuY3Rpb24gZ3JwY0VuZHBvaW50VVJMKGVuZHBvaW50OiBzdHJpbmcsIGluc2VjdXJlPzogYm9vbGVhbik6IHN0cmluZyB7XG4gIGlmIChlbmRwb2ludCA9PT0gJycpIHtcbiAgICByZXR1cm4gJyc7XG4gIH1cbiAgaWYgKGVuZHBvaW50LmluY2x1ZGVzKCc6Ly8nKSkge1xuICAgIHJldHVybiBlbmRwb2ludDtcbiAgfVxuICByZXR1cm4gYCR7aW5zZWN1cmUgPT09IHRydWUgPyAnaHR0cCcgOiAnaHR0cHMnfTovLyR7ZW5kcG9pbnR9YDtcbn1cblxuLyoqXG4gKiBSZXNvbHZlcyB0aGUgZW5kcG9pbnQgYW5kIHByb3RvY29sIGZvciBvbmUgcmVjaXBlIGFnYWluc3Qgb25lIGRlc3RpbmF0aW9uLlxuICpcbiAqIFBvcnQgb2YgYGV4cG9ydFRhcmdldEZvcmAuIEEgRmFyZ2F0ZSB0YXNrIGhhcyBubyBub2RlLWxvY2FsIGNvbGxlY3RvciDigJQgdW5saWtlIExhbWJkYSwgd2hlcmUgdGhlIFNES1xuICogZXhwb3J0cyB0byBhIGNvbGxlY3RvciBpbiB0aGUgc2FtZSBzYW5kYm94IOKAlCBzbyB0aGUgYWdlbnQgZXhwb3J0cyBPVExQIHN0cmFpZ2h0IHRvIHRoZSBkZXN0aW5hdGlvbiwgYW5kXG4gKiB3aGljaCB0cmFuc3BvcnQgaXQgY2FuIHVzZSBpcyBhIHByb3BlcnR5IG9mIHRoZSBhZ2VudCBhcyBtdWNoIGFzIG9mIHRoZSBkZXN0aW5hdGlvbi5cbiAqXG4gKiBFYWNoIGFnZW50J3MgUFJFRkVSUkVEIHRyYW5zcG9ydCBpcyB0YWtlbiB3aGVuIHRoZSBkZXN0aW5hdGlvbiBvZmZlcnMgaXQuIFdoZW4gaXQgZG9lcyBub3QsIGFuIGFnZW50XG4gKiB3aG9zZSBjb21tdW5pdHkgU0RLIHBpbnMgdGhlIHByb3RvY29sIChwaHAsIHJ1YnkpIGNhbm5vdCBiZSBzd2l0Y2hlZCBhbmQgdGhyb3dzOyBldmVyeSBvdGhlciBhZ2VudFxuICogaG9ub3VycyBgT1RFTF9FWFBPUlRFUl9PVExQX1BST1RPQ09MYCBhbmQgZmFsbHMgYmFjayB0byB3aGljaGV2ZXIgdHJhbnNwb3J0IGV4aXN0cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4cG9ydFRhcmdldEZvcihyZWNpcGU6IEZhcmdhdGVSZWNpcGUsIGRlc3Q6IE9kaWdvc0Rlc3RpbmF0aW9uKTogRmFyZ2F0ZUV4cG9ydFRhcmdldCB7XG4gIGNvbnN0IGdycGNVUkwgPSBncnBjRW5kcG9pbnRVUkwoZGVzdC5lbmRwb2ludCA/PyAnJywgZGVzdC5pbnNlY3VyZSk7XG4gIGNvbnN0IGh0dHBVUkwgPSBkZXN0Lmh0dHBFbmRwb2ludCA/PyAnJztcblxuICBpZiAocmVjaXBlLm90bHBQcm90b2NvbCA9PT0gT1RMUF9QUk9UT0NPTF9IVFRQICYmIGh0dHBVUkwgIT09ICcnKSB7XG4gICAgcmV0dXJuIHsgZW5kcG9pbnQ6IGh0dHBVUkwsIHByb3RvY29sOiBPVExQX1BST1RPQ09MX0hUVFAgfTtcbiAgfVxuICBpZiAocmVjaXBlLm90bHBQcm90b2NvbCA9PT0gT1RMUF9QUk9UT0NPTF9HUlBDICYmIGdycGNVUkwgIT09ICcnKSB7XG4gICAgcmV0dXJuIHsgZW5kcG9pbnQ6IGdycGNVUkwsIHByb3RvY29sOiBPVExQX1BST1RPQ09MX0dSUEMgfTtcbiAgfVxuICBpZiAocmVjaXBlLnByb3RvY29sRml4ZWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgT2RpZ29zOiB0aGUgJHtyZWNpcGUubGFuZ3VhZ2V9IGFnZW50IGV4cG9ydHMgT1RMUCBvdmVyICR7cmVjaXBlLm90bHBQcm90b2NvbH0gb25seSDigJQgaXRzIGNvbW11bml0eSBgICtcbiAgICAgICAgJ1NESyBjYW5ub3QgYmUgc3dpdGNoZWQgdG8gdGhlIG90aGVyIHRyYW5zcG9ydCDigJQgYnV0IHRoZSBkZXN0aW5hdGlvbiBjb25maWd1cmVzIG5vICcgK1xuICAgICAgICBgJHtyZWNpcGUub3RscFByb3RvY29sID09PSBPVExQX1BST1RPQ09MX0hUVFAgPyAnYGh0dHBFbmRwb2ludGAnIDogJ2BlbmRwb2ludGAnfS4gYCArXG4gICAgICAgICdDb25maWd1cmUgb25lLCBvciBsZWF2ZSB0aGlzIGNvbnRhaW5lciB1bmluc3RydW1lbnRlZC4nLFxuICAgICk7XG4gIH1cbiAgaWYgKGh0dHBVUkwgIT09ICcnKSB7XG4gICAgcmV0dXJuIHsgZW5kcG9pbnQ6IGh0dHBVUkwsIHByb3RvY29sOiBPVExQX1BST1RPQ09MX0hUVFAgfTtcbiAgfVxuICBpZiAoZ3JwY1VSTCAhPT0gJycpIHtcbiAgICByZXR1cm4geyBlbmRwb2ludDogZ3JwY1VSTCwgcHJvdG9jb2w6IE9UTFBfUFJPVE9DT0xfR1JQQyB9O1xuICB9XG4gIHRocm93IG5ldyBFcnJvcihcbiAgICAnT2RpZ29zIGRlc3RpbmF0aW9uIGhhcyBuZWl0aGVyIGBlbmRwb2ludGAgKE9UTFAvZ1JQQykgbm9yIGBodHRwRW5kcG9pbnRgIChPVExQL0hUVFApLCBzbyBhbiAnICtcbiAgICAgICdpbnN0cnVtZW50ZWQgRmFyZ2F0ZSB0YXNrIHdvdWxkIGhhdmUgbm93aGVyZSB0byBleHBvcnQuIFVubGlrZSBMYW1iZGEsIGEgRmFyZ2F0ZSB0YXNrIGhhcyBubyAnICtcbiAgICAgICdpbi1zYW5kYm94IGNvbGxlY3RvciB0byBidWZmZXIgZm9yIGl0IOKAlCB0aGUgYWdlbnQgZXhwb3J0cyB0byB0aGlzIGRlc3RpbmF0aW9uIGRpcmVjdGx5LicsXG4gICk7XG59XG5cbi8qKiBUaGUgcG9ydCBvZiBhIGBzY2hlbWU6Ly9ob3N0OnBvcnRbL3BhdGhdYCBlbmRwb2ludCwgb3IgXCJcIiB3aGVuIGl0IG5hbWVzIG5vbmUuICovXG5mdW5jdGlvbiBlbmRwb2ludFBvcnQoZW5kcG9pbnQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIGxldCByZXN0ID0gZW5kcG9pbnQ7XG4gIGNvbnN0IHNjaGVtZSA9IHJlc3QuaW5kZXhPZignOi8vJyk7XG4gIGlmIChzY2hlbWUgPj0gMCkge1xuICAgIHJlc3QgPSByZXN0LnNsaWNlKHNjaGVtZSArIDMpO1xuICB9XG4gIGNvbnN0IHBhdGhTdGFydCA9IHJlc3Quc2VhcmNoKC9bLz9dLyk7XG4gIGlmIChwYXRoU3RhcnQgPj0gMCkge1xuICAgIHJlc3QgPSByZXN0LnNsaWNlKDAsIHBhdGhTdGFydCk7XG4gIH1cbiAgY29uc3QgY29sb24gPSByZXN0Lmxhc3RJbmRleE9mKCc6Jyk7XG4gIHJldHVybiBjb2xvbiA8IDAgPyAnJyA6IHJlc3Quc2xpY2UoY29sb24gKyAxKTtcbn1cblxuLyoqXG4gKiBBIHdhcm5pbmcgd2hlbiBhbiBlbmRwb2ludCdzIHBvcnQgaXMgdGhlIHdlbGwta25vd24gcG9ydCBvZiB0aGUgT1RIRVIgT1RMUCBwcm90b2NvbCwgb3IgXCJcIiB3aGVuIHRoZVxuICogcGFpcmluZyBsb29rcyByaWdodCDigJQgaW5jbHVkaW5nIGEgbm9uLXN0YW5kYXJkIHBvcnQsIHdoaWNoIGNhbm5vdCBiZSBqdWRnZWQuXG4gKlxuICogV29ydGggYSBzeW50aC10aW1lIHdhcm5pbmcgYmVjYXVzZSB0aGUgcnVudGltZSBzeW1wdG9tIGlzIGEgY29ubmVjdGlvbiByZXNldCBtaWQtZXhwb3J0LCB3aGljaCBwb2ludHNcbiAqIG5vd2hlcmUgbmVhciB0aGUgY2F1c2U6IGFuIEhUVFAgcmVxdWVzdCBhZ2FpbnN0IGFuIEhUVFAvMiBnUlBDIGxpc3RlbmVyLCBvciB0aGUgcmV2ZXJzZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBvcnRQcm90b2NvbE1pc21hdGNoKGVuZHBvaW50OiBzdHJpbmcsIHByb3RvY29sOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBwb3J0ID0gZW5kcG9pbnRQb3J0KGVuZHBvaW50KTtcbiAgaWYgKHByb3RvY29sID09PSBPVExQX1BST1RPQ09MX0hUVFAgJiYgcG9ydCA9PT0gT1RMUF9ERUZBVUxUX0dSUENfUE9SVCkge1xuICAgIHJldHVybiAoXG4gICAgICBgZW5kcG9pbnQgJHtlbmRwb2ludH0gaXMgb24gJHtPVExQX0RFRkFVTFRfR1JQQ19QT1JUfSwgdGhlIHN0YW5kYXJkIE9UTFAvZ1JQQyBwb3J0LCBidXQgdGhpcyBhZ2VudCBgICtcbiAgICAgIGBleHBvcnRzIG92ZXIgJHtPVExQX1BST1RPQ09MX0hUVFB9OyBleHBvcnRzIHdpbGwgZmFpbCBhcyBhIGNvbm5lY3Rpb24gcmVzZXQuIFBvaW50IGl0IGF0IHRoZSBgICtcbiAgICAgIGBPVExQL0hUVFAgcG9ydCAoJHtPVExQX0RFRkFVTFRfSFRUUF9QT1JUfSkuYFxuICAgICk7XG4gIH1cbiAgaWYgKHByb3RvY29sID09PSBPVExQX1BST1RPQ09MX0dSUEMgJiYgcG9ydCA9PT0gT1RMUF9ERUZBVUxUX0hUVFBfUE9SVCkge1xuICAgIHJldHVybiAoXG4gICAgICBgZW5kcG9pbnQgJHtlbmRwb2ludH0gaXMgb24gJHtPVExQX0RFRkFVTFRfSFRUUF9QT1JUfSwgdGhlIHN0YW5kYXJkIE9UTFAvSFRUUCBwb3J0LCBidXQgdGhpcyBhZ2VudCBgICtcbiAgICAgIGBleHBvcnRzIG92ZXIgZ1JQQzsgZXhwb3J0cyB3aWxsIGZhaWwuIFBvaW50IGl0IGF0IHRoZSBPVExQL2dSUEMgcG9ydCAoJHtPVExQX0RFRkFVTFRfR1JQQ19QT1JUfSkuYFxuICAgICk7XG4gIH1cbiAgcmV0dXJuICcnO1xufVxuIl19