@cartanova/qgrid-cli 2.10.10 → 2.11.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/bundle/dist/_virtual/_rolldown/runtime.js +1 -10
- package/bundle/dist/application/monit/log-buffer.js +12 -6
- package/bundle/dist/application/monit/monit.frame.js +11 -3
- package/bundle/dist/application/monit/monit.types.js +1 -1
- package/bundle/dist/application/qgrid/conv-routing.js +9 -6
- package/bundle/dist/application/qgrid/expired-token-reminder.js +2 -2
- package/bundle/dist/application/qgrid/oauth.js +31 -4
- package/bundle/dist/application/qgrid/qgrid-image-generation.js +1 -1
- package/bundle/dist/application/qgrid/qgrid-response-format.js +21 -19
- package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +40 -33
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +128 -93
- package/bundle/dist/application/qgrid/qgrid.frame.js +930 -49
- package/bundle/dist/application/qgrid/qgrid.types.js +212 -5
- package/bundle/dist/application/qgrid/schema-prompt.js +75 -48
- package/bundle/dist/application/qgrid/slack-user-map.js +1 -1
- package/bundle/dist/application/qgrid/token-death.js +1 -1
- package/bundle/dist/application/qgrid/token-subscriber.js +83 -30
- package/bundle/dist/application/qgrid/token-trigger-setup.js +1 -1
- package/bundle/dist/application/qgrid/token-window-keepalive.js +8 -6
- package/bundle/dist/application/qgrid/tool-emulation-schema.js +13 -13
- package/bundle/dist/application/qgrid/tool-emulation.js +19 -15
- package/bundle/dist/application/request-log/json-schema-type-text.js +29 -12
- package/bundle/dist/application/request-log/request-log.model.js +83 -70
- package/bundle/dist/application/request-log/request-log.types.js +1 -1
- package/bundle/dist/application/request-log/zod-code-format.js +1 -1
- package/bundle/dist/application/request-log-step/request-log-step.model.js +1 -1
- package/bundle/dist/application/request-log-step/request-log-step.types.js +1 -1
- package/bundle/dist/application/setting/setting.constant.js +1 -1
- package/bundle/dist/application/setting/setting.model.js +2 -2
- package/bundle/dist/application/setting/setting.store.js +2 -2
- package/bundle/dist/application/setting/setting.types.js +1 -1
- package/bundle/dist/application/sonamu.generated.js +32 -16
- package/bundle/dist/application/sonamu.generated.sso.js +1 -1
- package/bundle/dist/application/token/token.model.js +26 -9
- package/bundle/dist/application/token/token.types.js +20 -3
- package/bundle/dist/i18n/en.js +15 -1
- package/bundle/dist/i18n/ko.js +15 -1
- package/bundle/dist/i18n/sd.generated.js +1 -1
- package/bundle/dist/migrations/20260807133553_manual__backfill_request_log_cost.js +8 -6
- package/bundle/dist/server-bootstrap.js +4 -2
- package/bundle/dist/server-error-handler.js +1 -1
- package/bundle/dist/sonamu.config.js +37 -8
- package/bundle/dist/startup-migrations.js +17 -7
- package/bundle/dist/testing/global.js +1 -1
- package/bundle/dist/testing/schema-bench.js +0 -0
- package/bundle/dist/testing/test-database-name.js +1 -1
- package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +1 -1
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +11 -5
- package/bundle/dist/utils/providers/anthropic/anthropic-quota.js +4 -4
- package/bundle/dist/utils/providers/anthropic/anthropic-thinking.js +1 -1
- package/bundle/dist/utils/providers/anthropic/claude-session.js +20 -19
- package/bundle/dist/utils/providers/anthropic/fence-strip.js +1 -1
- package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +49 -23
- package/bundle/dist/utils/providers/common/credentials.js +5 -1
- package/bundle/dist/utils/providers/common/effort.js +3 -3
- package/bundle/dist/utils/providers/common/model-cost.js +69 -14
- package/bundle/dist/utils/providers/common/provider-types.js +29 -1
- package/bundle/dist/utils/providers/common/schema-validation.js +78 -40
- package/bundle/dist/utils/providers/common/smooth-weighted-round-robin.js +1 -1
- package/bundle/dist/utils/providers/common/strictifier.js +62 -43
- package/bundle/dist/utils/providers/common/thinking.js +3 -2
- package/bundle/dist/utils/providers/common/ttft.js +1 -1
- package/bundle/dist/utils/providers/grok/grok-backend-protocol.js +427 -0
- package/bundle/dist/utils/providers/grok/grok-constants.js +55 -0
- package/bundle/dist/utils/providers/grok/grok-direct-client.js +196 -0
- package/bundle/dist/utils/providers/grok/grok-dispatcher.js +1313 -0
- package/bundle/dist/utils/providers/grok/grok-headers.js +118 -0
- package/bundle/dist/utils/providers/grok/grok-media-client.js +420 -0
- package/bundle/dist/utils/providers/grok/grok-models.js +114 -0
- package/bundle/dist/utils/providers/grok/grok-oauth.js +455 -0
- package/bundle/dist/utils/providers/grok/grok-quota.js +119 -0
- package/bundle/dist/utils/providers/grok/grok-refresh.js +192 -0
- package/bundle/dist/utils/providers/grok/grok-types.js +186 -0
- package/bundle/dist/utils/providers/grok/grok-voice-client.js +199 -0
- package/bundle/dist/utils/providers/grok/index.js +11 -0
- package/bundle/dist/utils/providers/openai/openai-backend-protocol.js +111 -83
- package/bundle/dist/utils/providers/openai/openai-callback-relay.js +3 -2
- package/bundle/dist/utils/providers/openai/openai-constants.js +1 -1
- package/bundle/dist/utils/providers/openai/openai-direct-client.js +23 -7
- package/bundle/dist/utils/providers/openai/openai-dispatcher.js +143 -106
- package/bundle/dist/utils/providers/openai/openai-image-generation.js +48 -33
- package/bundle/dist/utils/providers/openai/openai-oauth.js +28 -13
- package/bundle/dist/utils/providers/openai/openai-quota.js +38 -27
- package/bundle/dist/utils/providers/openai/openai-refresh.js +17 -9
- package/bundle/dist/utils/providers/openai/openai-sse.js +1 -1
- package/bundle/dist/utils/providers/openai/openai-transport-config.js +1 -1
- package/bundle/dist/utils/quiet-hours.js +1 -1
- package/bundle/dist/utils/slack-notify.js +14 -8
- package/bundle/dist/utils/subset-loaders.js +4 -5
- package/bundle/src/application/monit/log-buffer.ts +23 -9
- package/bundle/src/application/monit/monit.frame.ts +22 -5
- package/bundle/src/application/monit/monit.types.ts +6 -0
- package/bundle/src/application/qgrid/conv-routing.test.ts +76 -4
- package/bundle/src/application/qgrid/conv-routing.ts +35 -9
- package/bundle/src/application/qgrid/expired-token-reminder.ts +9 -1
- package/bundle/src/application/qgrid/oauth.ts +51 -15
- package/bundle/src/application/qgrid/qgrid-image-generation.ts +9 -0
- package/bundle/src/application/qgrid/qgrid-response-format.ts +50 -25
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +19 -0
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +84 -40
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +97 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +274 -126
- package/bundle/src/application/qgrid/qgrid.frame.test.ts +1013 -3
- package/bundle/src/application/qgrid/qgrid.frame.ts +1415 -60
- package/bundle/src/application/qgrid/qgrid.types.ts +335 -0
- package/bundle/src/application/qgrid/schema-prompt.ts +105 -41
- package/bundle/src/application/qgrid/slack-user-map.ts +5 -0
- package/bundle/src/application/qgrid/token-death.ts +4 -0
- package/bundle/src/application/qgrid/token-subscriber.test.ts +21 -19
- package/bundle/src/application/qgrid/token-subscriber.ts +186 -53
- package/bundle/src/application/qgrid/token-trigger-setup.ts +2 -0
- package/bundle/src/application/qgrid/token-window-keepalive.ts +49 -6
- package/bundle/src/application/qgrid/tool-emulation-schema.ts +33 -29
- package/bundle/src/application/qgrid/tool-emulation.ts +52 -27
- package/bundle/src/application/request-log/json-schema-type-text.test.ts +5 -5
- package/bundle/src/application/request-log/json-schema-type-text.ts +81 -26
- package/bundle/src/application/request-log/request-log.model.test.ts +111 -4
- package/bundle/src/application/request-log/request-log.model.ts +277 -146
- package/bundle/src/application/request-log/request-log.types.ts +6 -0
- package/bundle/src/application/request-log/zod-code-format.ts +11 -0
- package/bundle/src/application/request-log-step/request-log-step.model.ts +1 -0
- package/bundle/src/application/request-log-step/request-log-step.types.ts +2 -0
- package/bundle/src/application/setting/setting.constant.ts +9 -0
- package/bundle/src/application/setting/setting.model.ts +12 -2
- package/bundle/src/application/setting/setting.store.ts +5 -1
- package/bundle/src/application/setting/setting.types.ts +8 -0
- package/bundle/src/application/sonamu.generated.http +133 -0
- package/bundle/src/application/sonamu.generated.sso.ts +2 -0
- package/bundle/src/application/sonamu.generated.ts +15 -0
- package/bundle/src/application/token/token.identity.test.ts +20 -0
- package/bundle/src/application/token/token.model.ts +66 -7
- package/bundle/src/application/token/token.types.ts +24 -1
- package/bundle/src/i18n/en.ts +19 -0
- package/bundle/src/i18n/ko.ts +21 -0
- package/bundle/src/i18n/sd.generated.ts +23 -2
- package/bundle/src/migrations/20260807133553_manual__backfill_request_log_cost.ts +16 -8
- package/bundle/src/server-bootstrap.ts +4 -2
- package/bundle/src/server-error-handler.ts +1 -0
- package/bundle/src/sonamu.config.ts +69 -7
- package/bundle/src/startup-migrations.ts +27 -9
- package/bundle/src/testing/global.ts +20 -0
- package/bundle/src/testing/schema-bench.ts +105 -30
- package/bundle/src/testing/test-database-name.ts +2 -0
- package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +8 -0
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +41 -6
- package/bundle/src/utils/providers/anthropic/anthropic-quota.ts +7 -3
- package/bundle/src/utils/providers/anthropic/anthropic-thinking.ts +6 -0
- package/bundle/src/utils/providers/anthropic/claude-session.ts +58 -24
- package/bundle/src/utils/providers/anthropic/fence-strip.ts +11 -0
- package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +110 -45
- package/bundle/src/utils/providers/common/credentials.ts +8 -0
- package/bundle/src/utils/providers/common/effort.ts +9 -2
- package/bundle/src/utils/providers/common/model-cost.test.ts +48 -1
- package/bundle/src/utils/providers/common/model-cost.ts +259 -158
- package/bundle/src/utils/providers/common/provider-dispatcher.ts +98 -2
- package/bundle/src/utils/providers/common/provider-types.ts +46 -3
- package/bundle/src/utils/providers/common/schema-validation.test.ts +5 -1
- package/bundle/src/utils/providers/common/schema-validation.ts +124 -56
- package/bundle/src/utils/providers/common/smooth-weighted-round-robin.ts +4 -0
- package/bundle/src/utils/providers/common/strictifier.ts +185 -59
- package/bundle/src/utils/providers/common/thinking.ts +2 -2
- package/bundle/src/utils/providers/common/ttft.ts +4 -2
- package/bundle/src/utils/providers/grok/__fixtures__/golden-responses-1.0.60.json +164 -0
- package/bundle/src/utils/providers/grok/grok-backend-protocol.test.ts +325 -0
- package/bundle/src/utils/providers/grok/grok-backend-protocol.ts +721 -0
- package/bundle/src/utils/providers/grok/grok-constants.ts +105 -0
- package/bundle/src/utils/providers/grok/grok-direct-client.test.ts +179 -0
- package/bundle/src/utils/providers/grok/grok-direct-client.ts +313 -0
- package/bundle/src/utils/providers/grok/grok-dispatcher.test.ts +1395 -0
- package/bundle/src/utils/providers/grok/grok-dispatcher.ts +2105 -0
- package/bundle/src/utils/providers/grok/grok-headers.test.ts +149 -0
- package/bundle/src/utils/providers/grok/grok-headers.ts +199 -0
- package/bundle/src/utils/providers/grok/grok-media-client.test.ts +446 -0
- package/bundle/src/utils/providers/grok/grok-media-client.ts +703 -0
- package/bundle/src/utils/providers/grok/grok-models.test.ts +96 -0
- package/bundle/src/utils/providers/grok/grok-models.ts +161 -0
- package/bundle/src/utils/providers/grok/grok-oauth.test.ts +406 -0
- package/bundle/src/utils/providers/grok/grok-oauth.ts +771 -0
- package/bundle/src/utils/providers/grok/grok-quota.test.ts +142 -0
- package/bundle/src/utils/providers/grok/grok-quota.ts +198 -0
- package/bundle/src/utils/providers/grok/grok-refresh.test.ts +239 -0
- package/bundle/src/utils/providers/grok/grok-refresh.ts +325 -0
- package/bundle/src/utils/providers/grok/grok-types.test.ts +252 -0
- package/bundle/src/utils/providers/grok/grok-types.ts +374 -0
- package/bundle/src/utils/providers/grok/grok-voice-client.test.ts +321 -0
- package/bundle/src/utils/providers/grok/grok-voice-client.ts +284 -0
- package/bundle/src/utils/providers/grok/index.ts +20 -0
- package/bundle/src/utils/providers/openai/openai-backend-protocol.ts +221 -137
- package/bundle/src/utils/providers/openai/openai-callback-relay.ts +12 -1
- package/bundle/src/utils/providers/openai/openai-constants.ts +2 -0
- package/bundle/src/utils/providers/openai/openai-direct-client.ts +111 -21
- package/bundle/src/utils/providers/openai/openai-dispatcher.ts +322 -169
- package/bundle/src/utils/providers/openai/openai-image-generation.ts +116 -36
- package/bundle/src/utils/providers/openai/openai-oauth.ts +48 -20
- package/bundle/src/utils/providers/openai/openai-quota.ts +77 -46
- package/bundle/src/utils/providers/openai/openai-refresh.ts +42 -18
- package/bundle/src/utils/providers/openai/openai-sse.ts +27 -3
- package/bundle/src/utils/providers/openai/openai-transport-config.ts +2 -0
- package/bundle/src/utils/quiet-hours.ts +12 -1
- package/bundle/src/utils/slack-notify.ts +44 -13
- package/bundle/src/utils/subset-loaders.ts +7 -7
- package/bundle/web-dist/client/assets/clsx-5XM_pHvN.js +1 -0
- package/bundle/web-dist/client/assets/index-Be2qCZmI.css +1 -0
- package/bundle/web-dist/client/assets/index-C96h3feE.js +32 -0
- package/bundle/web-dist/client/assets/logs-DBM6U0QD.js +1 -0
- package/bundle/web-dist/client/assets/monit-BBjXa7vB.js +1 -0
- package/bundle/web-dist/client/assets/routes-Crz_PQzi.js +5 -0
- package/bundle/web-dist/client/assets/{services.generated-NP-ValHd.js → services.generated-B5lc3aIC.js} +5 -5
- package/bundle/web-dist/client/assets/settings-Cdwm3OBB.js +1 -0
- package/bundle/web-dist/client/assets/show-DBUhALT1.js +2 -0
- package/bundle/web-dist/client/assets/tokens-CVQAVJTo.js +1 -0
- package/bundle/web-dist/client/assets/total-cost-MnfKU8IN.js +1 -0
- package/bundle/web-dist/client/assets/use-oauth-login-flow-BfxYI4Oh.js +1 -0
- package/bundle/web-dist/client/assets/use-update-token-mutation-qKBC2sn7.js +1 -0
- package/bundle/web-dist/client/assets/{vendor-tanstack-DJRQAbrY.js → vendor-tanstack-DRPs09ai.js} +1 -1
- package/bundle/web-dist/client/index.html +5 -5
- package/bundle/web-dist/server/assets/clsx-D_N5PN_2.js +16 -0
- package/bundle/web-dist/server/assets/{lazyRouteComponent-CgBdabgd.js → lazyRouteComponent-AKiHfZWp.js} +129 -2
- package/bundle/web-dist/server/assets/{logs-BMyR98rU.js → logs-B7QmGJq3.js} +18 -14
- package/bundle/web-dist/server/assets/{monit-ZPt66W5M.js → monit-DFt7x3bh.js} +13 -12
- package/bundle/web-dist/server/assets/{plus-BZ9I0VgC.js → plus-DaYQRpBt.js} +1 -1
- package/bundle/web-dist/server/assets/{react-dom-Ds75e4Jl.js → react-dom-Cv23mm1K.js} +2 -129
- package/bundle/web-dist/server/assets/{refresh-cw-CztCFis-.js → refresh-cw-BpXNsAy-.js} +1 -1
- package/bundle/web-dist/server/assets/{request-model-DjGldb-h.js → request-model-eHE6YbaF.js} +1 -1
- package/bundle/web-dist/server/assets/{routes-Ct-k7nBA.js → routes-_Y-PJfgO.js} +110 -39
- package/bundle/web-dist/server/assets/{services.generated-BA4AcAf5.js → services.generated-CMJ8AHbO.js} +582 -172
- package/bundle/web-dist/server/assets/{settings-CBUZ47Xx.js → settings-JwJKxn2e.js} +14 -14
- package/bundle/web-dist/server/assets/{show-LSFIzvO2.js → show-BEv6z1v_.js} +90 -60
- package/bundle/web-dist/server/assets/{tokens-DxrBW512.js → tokens-CNT4mI-f.js} +162 -50
- package/bundle/web-dist/server/assets/total-cost-fTE9Xy82.js +18 -0
- package/bundle/web-dist/server/assets/use-oauth-login-flow-jkAkT3X6.js +2345 -0
- package/bundle/web-dist/server/assets/{use-update-token-mutation-pkArIzCP.js → use-update-token-mutation-Sm6kh02m.js} +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +1644 -21655
- package/dist/cli.js +9 -9
- package/package.json +4 -11
- package/postinstall.mjs +12 -0
- package/skills/qgrid/SKILL.md +1 -0
- package/skills/qgrid/references/cli-env-and-server-boot.md +4 -1
- package/skills/qgrid/references/decision-rationale.md +23 -0
- package/skills/qgrid/references/grok-runtime.md +89 -0
- package/skills/qgrid/references/provider-runtime-differences.md +30 -29
- package/skills/qgrid/references/repo-map.md +10 -0
- package/skills/qgrid/references/token-auth-quota-lifecycle.md +56 -3
- package/bundle/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +0 -406
- package/bundle/dist/node_modules/.pnpm/@vitest_expect@4.1.10/node_modules/@vitest/expect/dist/index.js +0 -1507
- package/bundle/dist/node_modules/.pnpm/@vitest_pretty-format@4.1.10/node_modules/@vitest/pretty-format/dist/index.js +0 -887
- package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/chunk-artifact.js +0 -2756
- package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/index.js +0 -8
- package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/utils.js +0 -8
- package/bundle/dist/node_modules/.pnpm/@vitest_snapshot@4.1.10/node_modules/@vitest/snapshot/dist/index.js +0 -922
- package/bundle/dist/node_modules/.pnpm/@vitest_spy@4.1.10/node_modules/@vitest/spy/dist/index.js +0 -386
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +0 -121
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/constants.js +0 -51
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/diff.js +0 -1317
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/display.js +0 -561
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/error.js +0 -40
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/helpers.js +0 -272
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/offset.js +0 -27
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/serialize.js +0 -77
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/source-map.js +0 -381
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/timers.js +0 -51
- package/bundle/dist/node_modules/.pnpm/chai@6.2.2/node_modules/chai/index.js +0 -2986
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.js +0 -10
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.js +0 -109
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.js +0 -10
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.js +0 -10
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.js +0 -10
- package/bundle/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +0 -1126
- package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/index.js +0 -21
- package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js +0 -432
- package/bundle/dist/node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.js +0 -517
- package/bundle/dist/node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.js +0 -101
- package/bundle/dist/node_modules/.pnpm/vite@8.0.3_@emnapi_core@1.11.2_@emnapi_runtime@1.11.2_@types_node@25.0.7_esbuild@0.27.4_5b35fc1fb85d2af4d105e0d74f940f50/node_modules/vite/dist/node/module-runner.js +0 -1265
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js +0 -9
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/benchmark.CX_oY03V.js +0 -41
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/evaluatedModules.Dg1zASAC.js +0 -19
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/index.Chj8NDwU.js +0 -202
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/index.DdgEv5B1.js +0 -46
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js +0 -119
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/test.DNmyFkvJ.js +0 -3044
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js +0 -59
- package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/index.js +0 -28
- package/bundle/dist/testing/setup-mocks.js +0 -16
- package/bundle/src/testing/setup-mocks.ts +0 -44
- package/bundle/web-dist/client/assets/index-Bw2s88Ot.css +0 -1
- package/bundle/web-dist/client/assets/index-DZwzUfI-.js +0 -106
- package/bundle/web-dist/client/assets/logs-Cog79DU5.js +0 -1
- package/bundle/web-dist/client/assets/monit-2auznBys.js +0 -1
- package/bundle/web-dist/client/assets/routes-B0lTeLK-.js +0 -5
- package/bundle/web-dist/client/assets/settings-D8ZxZGSJ.js +0 -1
- package/bundle/web-dist/client/assets/show-CFSERlnM.js +0 -2
- package/bundle/web-dist/client/assets/tokens-CZXUyI7O.js +0 -1
- package/bundle/web-dist/client/assets/use-oauth-login-flow-hboOs3wG.js +0 -1
- package/bundle/web-dist/client/assets/use-update-token-mutation-BQHS0byG.js +0 -1
- package/bundle/web-dist/server/assets/use-oauth-login-flow-Gn8flMK5.js +0 -178
- package/dist/cli.d.ts +0 -10
- /package/bundle/web-dist/client/assets/{cost-DH2jZbrw.js → cost-DB3HbU7T.js} +0 -0
- /package/bundle/web-dist/client/assets/{plus-B62CuqT8.js → plus-BsQdY7PS.js} +0 -0
- /package/bundle/web-dist/client/assets/{preload-helper-ca-nBW7U.js → preload-helper-DWTEM3RW.js} +0 -0
- /package/bundle/web-dist/client/assets/{refresh-cw-NNcU0Cmq.js → refresh-cw-D9IUmT23.js} +0 -0
- /package/bundle/web-dist/client/assets/{request-model-CojtinqK.js → request-model-Bpp_rkK8.js} +0 -0
- /package/bundle/web-dist/server/assets/{cost-eEHeTRT5.js → cost-BC9yzyA9.js} +0 -0
|
@@ -0,0 +1,1313 @@
|
|
|
1
|
+
import { GROK_BUILD_VERSION, GROK_USD_PER_TICK } from "./grok-constants.js";
|
|
2
|
+
import { isJsonObject, jsonString } from "../common/provider-types.js";
|
|
3
|
+
import { QuotaThresholdExceededError } from "../../../application/qgrid/qgrid.types.js";
|
|
4
|
+
import { SmoothWeightedRoundRobin } from "../common/smooth-weighted-round-robin.js";
|
|
5
|
+
import { ThinkingValidationError } from "../common/thinking.js";
|
|
6
|
+
import { buildGrokResponsesRequest } from "./grok-backend-protocol.js";
|
|
7
|
+
import { GrokChatClient, GrokHttpError, GrokIdleTimeoutError } from "./grok-direct-client.js";
|
|
8
|
+
import { buildGrokChatHeaders } from "./grok-headers.js";
|
|
9
|
+
import { GrokCredentials } from "./grok-types.js";
|
|
10
|
+
import { GrokModelCatalog } from "./grok-models.js";
|
|
11
|
+
import { readGrokBilling, readGrokSettings, readGrokSubscription } from "./grok-quota.js";
|
|
12
|
+
import { grokNeedsRefresh } from "./grok-refresh.js";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import { getLogger } from "@logtape/logtape";
|
|
15
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
16
|
+
//#region src/utils/providers/grok/grok-dispatcher.ts
|
|
17
|
+
const logger = getLogger(["qgrid", "grok-dispatcher"]);
|
|
18
|
+
const GROK_LOOKUP_CACHE_TTL_MS = 6e4;
|
|
19
|
+
const GROK_MAX_COOLDOWN_MS = 12e4;
|
|
20
|
+
const GROK_DEFAULT_COOLDOWN_MS = 1e4;
|
|
21
|
+
const GROK_MAX_COOLDOWN_WAIT_MS = 3e4;
|
|
22
|
+
const GROK_QUOTA_BLOCK_FALLBACK_MS = 36e5;
|
|
23
|
+
const GROK_ENTITLEMENT_BLOCK_MS = 36e5;
|
|
24
|
+
const GROK_VERSION_GATE_REPROBE_MS = 6e5;
|
|
25
|
+
const GROK_MAX_HOPS = 2;
|
|
26
|
+
const GROK_MAX_REFRESHES = 1;
|
|
27
|
+
const GROK_MAX_TRANSIENT_FAILOVERS = 1;
|
|
28
|
+
/** A token whose pre-lookup refresh failed transiently sits out selection this long (§5.1). */
|
|
29
|
+
const GROK_REFRESH_FAILED_COOLDOWN_MS = 5e3;
|
|
30
|
+
/** Retries with the token's current credentials after a 401 that a newer bearer superseded. */
|
|
31
|
+
const GROK_MAX_STALE_AUTH_RETRIES = 1;
|
|
32
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 6e5;
|
|
33
|
+
const DEFAULT_IDLE_TIMEOUT_MS = 3e5;
|
|
34
|
+
var GrokInvalidRequestError = class extends Error {
|
|
35
|
+
code = "GROK_INVALID_REQUEST";
|
|
36
|
+
constructor(message) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = "GrokInvalidRequestError";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var GrokRateLimitedError = class extends Error {
|
|
42
|
+
code = "GROK_RATE_LIMITED";
|
|
43
|
+
retryAfterSec;
|
|
44
|
+
constructor(retryAfterSec, message) {
|
|
45
|
+
super(message ?? `All grok tokens are rate limited; retry after ${retryAfterSec}s`);
|
|
46
|
+
this.name = "GrokRateLimitedError";
|
|
47
|
+
this.retryAfterSec = retryAfterSec;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var GrokCapacityError = class extends Error {
|
|
51
|
+
code = "GROK_CAPACITY";
|
|
52
|
+
constructor(message = "All grok tokens are at their in-flight request cap") {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = "GrokCapacityError";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var GrokOnDemandExcludedError = class extends Error {
|
|
58
|
+
code = "GROK_ON_DEMAND_EXCLUDED";
|
|
59
|
+
tokenNames;
|
|
60
|
+
constructor(tokenNames) {
|
|
61
|
+
super(`All grok tokens are excluded because on-demand billing is enabled: ${tokenNames.join(", ")}`);
|
|
62
|
+
this.name = "GrokOnDemandExcludedError";
|
|
63
|
+
this.tokenNames = tokenNames;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* xAI refuses video generation for Zero Data Retention accounts unless the caller supplies its own
|
|
68
|
+
* `output.upload_url` (plan §11), which qgrid does not implement.
|
|
69
|
+
*/
|
|
70
|
+
var GrokZdrVideoUnavailableError = class extends Error {
|
|
71
|
+
code = "GROK_ZDR_VIDEO_UNAVAILABLE";
|
|
72
|
+
tokenNames;
|
|
73
|
+
constructor(tokenNames) {
|
|
74
|
+
super(`Grok video is unavailable because Zero Data Retention is on for: ${tokenNames.join(", ")}. Turn off the data-retention opt-out at xAI, or use a non-ZDR account.`);
|
|
75
|
+
this.name = "GrokZdrVideoUnavailableError";
|
|
76
|
+
this.tokenNames = tokenNames;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/** The upstream 400 body for a ZDR account on POST /v1/videos/generations. */
|
|
80
|
+
const ZDR_VIDEO_REJECTION = /zero data retention/i;
|
|
81
|
+
function isGrokZdrVideoRejection(error) {
|
|
82
|
+
return error.status === 400 && (ZDR_VIDEO_REJECTION.test(error.classification.message ?? "") || ZDR_VIDEO_REJECTION.test(error.bodyText));
|
|
83
|
+
}
|
|
84
|
+
var GrokVersionGateError = class extends Error {
|
|
85
|
+
code = "GROK_VERSION_GATE";
|
|
86
|
+
minClientVersion;
|
|
87
|
+
constructor(minClientVersion) {
|
|
88
|
+
super(`Grok chat proxy rejected client version ${GROK_BUILD_VERSION} (HTTP 426; requires ${minClientVersion ?? "a newer version"} or later). Bump GROK_BUILD_VERSION in grok-constants.ts and re-capture the golden fixture.`);
|
|
89
|
+
this.name = "GrokVersionGateError";
|
|
90
|
+
this.minClientVersion = minClientVersion;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
var GrokAttemptBudgetExceededError = class extends Error {
|
|
94
|
+
code = "GROK_ATTEMPT_BUDGET_EXCEEDED";
|
|
95
|
+
constructor(attempts, cause) {
|
|
96
|
+
super(`Grok request failed after ${attempts} attempts: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
97
|
+
this.name = "GrokAttemptBudgetExceededError";
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var GrokNoWorkersError = class extends Error {
|
|
101
|
+
code = "NO_GROK_WORKERS";
|
|
102
|
+
constructor(message = "NO_GROK_WORKERS") {
|
|
103
|
+
super(message);
|
|
104
|
+
this.name = "GrokNoWorkersError";
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var GrokCapabilityUnavailableError = class extends Error {
|
|
108
|
+
code = "GROK_CAPABILITY_UNAVAILABLE";
|
|
109
|
+
constructor(capability) {
|
|
110
|
+
super(`Grok ${capability} is not configured on this server`);
|
|
111
|
+
this.name = "GrokCapabilityUnavailableError";
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
/** The refresher could not produce usable credentials for a token before the request was sent. */
|
|
115
|
+
var GrokCredentialRefreshError = class GrokCredentialRefreshError extends Error {
|
|
116
|
+
code = "GROK_CREDENTIAL_REFRESH";
|
|
117
|
+
reauthRequired;
|
|
118
|
+
/**
|
|
119
|
+
* `reauthRequired` defaults to the cause's convention: the port signals a dead refresh token with
|
|
120
|
+
* GrokReauthRequiredError (grok-refresh.ts), checked by name so any GrokRefresher implementation
|
|
121
|
+
* can use it.
|
|
122
|
+
*/
|
|
123
|
+
constructor(cause, reauthRequired = cause instanceof Error && cause.name === "GrokReauthRequiredError") {
|
|
124
|
+
super(`Grok credential refresh failed: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
125
|
+
this.name = "GrokCredentialRefreshError";
|
|
126
|
+
this.reauthRequired = reauthRequired;
|
|
127
|
+
}
|
|
128
|
+
/** The refresher answered `reauth_required`: the refresh token is dead. */
|
|
129
|
+
static reauthRequired(reason) {
|
|
130
|
+
return new GrokCredentialRefreshError(reason, true);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var GrokIncompleteResponseError = class extends Error {
|
|
134
|
+
code = "GROK_INCOMPLETE_RESPONSE";
|
|
135
|
+
reason;
|
|
136
|
+
constructor(reason) {
|
|
137
|
+
super(`Grok response incomplete${reason ? `: ${reason}` : ""}`);
|
|
138
|
+
this.name = "GrokIncompleteResponseError";
|
|
139
|
+
this.reason = reason;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const ContentPart = z.discriminatedUnion("type", [
|
|
143
|
+
z.object({
|
|
144
|
+
type: z.literal("input_text"),
|
|
145
|
+
text: z.string()
|
|
146
|
+
}),
|
|
147
|
+
z.object({
|
|
148
|
+
type: z.literal("input_image"),
|
|
149
|
+
image_url: z.string().min(1)
|
|
150
|
+
}),
|
|
151
|
+
z.object({
|
|
152
|
+
type: z.literal("output_text"),
|
|
153
|
+
text: z.string()
|
|
154
|
+
})
|
|
155
|
+
]);
|
|
156
|
+
const HistoryItem = z.union([
|
|
157
|
+
z.object({
|
|
158
|
+
type: z.literal("message").optional(),
|
|
159
|
+
role: z.enum(["user", "assistant"]),
|
|
160
|
+
content: z.union([z.string(), z.array(ContentPart)])
|
|
161
|
+
}),
|
|
162
|
+
z.object({
|
|
163
|
+
type: z.literal("function_call"),
|
|
164
|
+
call_id: z.string().min(1),
|
|
165
|
+
name: z.string().min(1),
|
|
166
|
+
arguments: z.string(),
|
|
167
|
+
id: z.string().optional()
|
|
168
|
+
}),
|
|
169
|
+
z.object({
|
|
170
|
+
type: z.literal("function_call_output"),
|
|
171
|
+
call_id: z.string().min(1),
|
|
172
|
+
output: z.string()
|
|
173
|
+
}),
|
|
174
|
+
z.object({
|
|
175
|
+
type: z.literal("reasoning"),
|
|
176
|
+
id: z.string().optional(),
|
|
177
|
+
summary: z.array(z.object({
|
|
178
|
+
type: z.literal("summary_text"),
|
|
179
|
+
text: z.string()
|
|
180
|
+
})).optional(),
|
|
181
|
+
encrypted_content: z.string().optional()
|
|
182
|
+
})
|
|
183
|
+
]);
|
|
184
|
+
function historyItems(history) {
|
|
185
|
+
return (history ?? []).map((raw, index) => {
|
|
186
|
+
const parsed = HistoryItem.safeParse(raw);
|
|
187
|
+
if (!parsed.success) throw new GrokInvalidRequestError(`Unsupported Grok history item at index ${index}`);
|
|
188
|
+
const item = parsed.data;
|
|
189
|
+
if ("role" in item) return {
|
|
190
|
+
type: "message",
|
|
191
|
+
role: item.role,
|
|
192
|
+
content: item.content
|
|
193
|
+
};
|
|
194
|
+
return item;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function userMessage(req) {
|
|
198
|
+
return {
|
|
199
|
+
type: "message",
|
|
200
|
+
role: "user",
|
|
201
|
+
content: req.coldInput.map((input) => {
|
|
202
|
+
if (input.type === "text") return {
|
|
203
|
+
type: "input_text",
|
|
204
|
+
text: input.text
|
|
205
|
+
};
|
|
206
|
+
if (input.type === "image") return {
|
|
207
|
+
type: "input_image",
|
|
208
|
+
image_url: input.url
|
|
209
|
+
};
|
|
210
|
+
if (input.type === "localImage") throw new GrokInvalidRequestError("Grok does not accept local image paths; send a data URL");
|
|
211
|
+
return {
|
|
212
|
+
type: "input_text",
|
|
213
|
+
text: `${input.name}: ${input.path}`
|
|
214
|
+
};
|
|
215
|
+
})
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function grokTools(tools) {
|
|
219
|
+
if (!tools?.length) return void 0;
|
|
220
|
+
return tools.map((tool) => {
|
|
221
|
+
if (tool.type !== "function") return { type: tool.type };
|
|
222
|
+
const fn = {
|
|
223
|
+
type: "function",
|
|
224
|
+
name: tool.name,
|
|
225
|
+
parameters: tool.parameters,
|
|
226
|
+
strict: tool.strict ?? false
|
|
227
|
+
};
|
|
228
|
+
if (tool.description !== void 0) fn.description = tool.description;
|
|
229
|
+
return fn;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/** Deterministic UUID-shaped id, so one qgrid conversation maps to one Grok conversation. */
|
|
233
|
+
function derivedGuid(namespace, key) {
|
|
234
|
+
const hex = createHash("sha256").update(`${namespace}\0${key}`).digest("hex");
|
|
235
|
+
const variant = (Number.parseInt(hex[16], 16) & 3 | 8).toString(16);
|
|
236
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-${variant}${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
|
|
237
|
+
}
|
|
238
|
+
function grokConversationIds(promptCacheKey) {
|
|
239
|
+
const convId = promptCacheKey ? derivedGuid("grok-conv", promptCacheKey) : randomUUID();
|
|
240
|
+
return {
|
|
241
|
+
convId,
|
|
242
|
+
convGroupId: derivedGuid("grok-conv-group", convId)
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/** USD from cost_in_usd_ticks, rounded to the tick resolution (1e-10). */
|
|
246
|
+
function grokTicksToUsd(ticks) {
|
|
247
|
+
return Number((ticks * GROK_USD_PER_TICK).toFixed(10));
|
|
248
|
+
}
|
|
249
|
+
function parseMaxInflight(value) {
|
|
250
|
+
if (value === void 0 || value.trim() === "") return void 0;
|
|
251
|
+
const parsed = Number(value);
|
|
252
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
253
|
+
logger.warn(`ignoring QGRID_GROK_MAX_INFLIGHT_PER_TOKEN=${value}: not a positive integer`);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
return parsed;
|
|
257
|
+
}
|
|
258
|
+
function abortReason(signal) {
|
|
259
|
+
const reason = signal.reason;
|
|
260
|
+
return reason instanceof Error ? reason : new DOMException("Aborted", "AbortError");
|
|
261
|
+
}
|
|
262
|
+
/** Imagine calls carry the token's session id like grok-build's per-request session header. */
|
|
263
|
+
function mediaAuth(credentials) {
|
|
264
|
+
return {
|
|
265
|
+
bearer: credentials.accessToken,
|
|
266
|
+
sessionId: credentials.sessionId
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function defaultSleep(ms, signal) {
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
if (signal?.aborted) {
|
|
272
|
+
reject(abortReason(signal));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const onAbort = () => {
|
|
276
|
+
clearTimeout(timer);
|
|
277
|
+
reject(abortReason(signal));
|
|
278
|
+
};
|
|
279
|
+
const timer = setTimeout(() => {
|
|
280
|
+
signal?.removeEventListener("abort", onAbort);
|
|
281
|
+
resolve();
|
|
282
|
+
}, ms);
|
|
283
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function quotaMessage(entries) {
|
|
287
|
+
const detail = entries.map((e) => e.threshold === null ? `${e.name} (exhausted)` : `${e.name} (threshold ${e.threshold}%)`).join(", ");
|
|
288
|
+
return detail ? `All grok tokens exceeded quota threshold: ${detail}` : "All grok tokens exceeded quota threshold";
|
|
289
|
+
}
|
|
290
|
+
function mapRecord(map, now) {
|
|
291
|
+
return Object.fromEntries([...map].filter(([, until]) => until > now));
|
|
292
|
+
}
|
|
293
|
+
async function persistGrokReauth(token, reasonCode) {
|
|
294
|
+
const { deactivateAuthDeadToken } = await import("../../../application/qgrid/token-death.js");
|
|
295
|
+
return deactivateAuthDeadToken({
|
|
296
|
+
...token,
|
|
297
|
+
provider: "grok"
|
|
298
|
+
}, reasonCode);
|
|
299
|
+
}
|
|
300
|
+
/** Internal `grok:<code>` reason; provider text may carry account data, so only codes pass. */
|
|
301
|
+
function reauthReasonCode(code) {
|
|
302
|
+
return code !== void 0 && /^[a-z_0-9]{1,80}$/.test(code) ? `grok:${code}` : "grok:unknown";
|
|
303
|
+
}
|
|
304
|
+
const oauthErrorCodeSchema = z.string();
|
|
305
|
+
function oauthErrorOf(cause) {
|
|
306
|
+
return cause instanceof Error && "oauthError" in cause ? oauthErrorCodeSchema.safeParse(cause.oauthError).data : void 0;
|
|
307
|
+
}
|
|
308
|
+
/** The same bearer and refresh token: a failure judged against `b` applies to `a`. */
|
|
309
|
+
function sameCredentials(a, b) {
|
|
310
|
+
return a?.accessToken === b.accessToken && a.refreshToken === b.refreshToken;
|
|
311
|
+
}
|
|
312
|
+
/** Names of the tokens that were ineligible for exactly `reason`. */
|
|
313
|
+
function tokenNamesWithReason(reasons, reason) {
|
|
314
|
+
return [...reasons].flatMap(([token, tokenReason]) => tokenReason === reason ? [token.name] : []);
|
|
315
|
+
}
|
|
316
|
+
var GrokDispatcher = class {
|
|
317
|
+
tokens = /* @__PURE__ */ new Map();
|
|
318
|
+
selector = new SmoothWeightedRoundRobin();
|
|
319
|
+
refresher;
|
|
320
|
+
fetchImpl;
|
|
321
|
+
media;
|
|
322
|
+
voice;
|
|
323
|
+
now;
|
|
324
|
+
maxInflightPerToken;
|
|
325
|
+
sleep;
|
|
326
|
+
client;
|
|
327
|
+
catalog;
|
|
328
|
+
idleTimeoutMs;
|
|
329
|
+
reportReauthRequired;
|
|
330
|
+
pendingBilling = /* @__PURE__ */ new Map();
|
|
331
|
+
pendingSettings = /* @__PURE__ */ new Map();
|
|
332
|
+
pendingSubscription = /* @__PURE__ */ new Map();
|
|
333
|
+
versionGate;
|
|
334
|
+
constructor(options) {
|
|
335
|
+
this.refresher = options.refresher;
|
|
336
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
337
|
+
this.media = options.media;
|
|
338
|
+
this.voice = options.voice;
|
|
339
|
+
this.now = options.now ?? Date.now;
|
|
340
|
+
this.maxInflightPerToken = "maxInflightPerToken" in options ? options.maxInflightPerToken : parseMaxInflight(process.env.QGRID_GROK_MAX_INFLIGHT_PER_TOKEN);
|
|
341
|
+
this.sleep = options.sleep ?? defaultSleep;
|
|
342
|
+
this.client = options.client ?? new GrokChatClient({ fetch: this.fetchImpl });
|
|
343
|
+
this.catalog = options.catalog ?? new GrokModelCatalog();
|
|
344
|
+
this.idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
345
|
+
this.reportReauthRequired = options.reportReauthRequired ?? persistGrokReauth;
|
|
346
|
+
}
|
|
347
|
+
async start() {
|
|
348
|
+
logger.info(`started grok runtime with ${this.tokens.size} tokens`);
|
|
349
|
+
}
|
|
350
|
+
async stop() {
|
|
351
|
+
this.tokens.clear();
|
|
352
|
+
this.pendingBilling.clear();
|
|
353
|
+
this.pendingSettings.clear();
|
|
354
|
+
this.pendingSubscription.clear();
|
|
355
|
+
for (const id of this.selector.weights.keys()) this.selector.removeToken(id);
|
|
356
|
+
this.versionGate = void 0;
|
|
357
|
+
}
|
|
358
|
+
onTokenAdded(id, name, credentials, quotaThreshold, weight) {
|
|
359
|
+
this.upsertToken(id, name, credentials, quotaThreshold, weight);
|
|
360
|
+
}
|
|
361
|
+
onTokenUpdated(id, name, credentials, quotaThreshold, weight) {
|
|
362
|
+
this.upsertToken(id, name, credentials, quotaThreshold, weight);
|
|
363
|
+
}
|
|
364
|
+
onTokenRemoved(id) {
|
|
365
|
+
this.tokens.delete(id);
|
|
366
|
+
this.selector.removeToken(id);
|
|
367
|
+
this.pendingBilling.delete(id);
|
|
368
|
+
this.pendingSettings.delete(id);
|
|
369
|
+
this.pendingSubscription.delete(id);
|
|
370
|
+
}
|
|
371
|
+
replaceTokens(rows) {
|
|
372
|
+
const ids = new Set(rows.map((row) => row.id));
|
|
373
|
+
for (const id of this.tokens.keys()) if (!ids.has(id)) this.onTokenRemoved(id);
|
|
374
|
+
for (const row of rows) this.upsertToken(row.id, row.name, row.credentials, row.quotaThreshold, row.weight);
|
|
375
|
+
}
|
|
376
|
+
upsertToken(id, name, rawCredentials, quotaThreshold, weight) {
|
|
377
|
+
const parsed = GrokCredentials.safeParse(rawCredentials);
|
|
378
|
+
let invalidReason = parsed.success ? void 0 : `invalid credentials at ${[...new Set(parsed.error.issues.map((i) => i.path.join(".") || "<root>"))].join(", ")}`;
|
|
379
|
+
if (!invalidReason && (!Number.isInteger(weight) || weight < 1 || weight > 100)) invalidReason = `invalid weight ${weight}`;
|
|
380
|
+
const credentials = parsed.success ? parsed.data : void 0;
|
|
381
|
+
const old = this.tokens.get(id);
|
|
382
|
+
if (old) {
|
|
383
|
+
const credentialsChanged = old.credentials?.accessToken !== credentials?.accessToken || old.credentials?.refreshToken !== credentials?.refreshToken;
|
|
384
|
+
const thresholdChanged = old.quotaThreshold !== quotaThreshold;
|
|
385
|
+
old.name = name;
|
|
386
|
+
old.weight = weight;
|
|
387
|
+
old.quotaThreshold = quotaThreshold;
|
|
388
|
+
old.credentials = credentials;
|
|
389
|
+
old.invalidReason = invalidReason;
|
|
390
|
+
if (credentialsChanged || thresholdChanged) {
|
|
391
|
+
old.generation++;
|
|
392
|
+
old.billing = void 0;
|
|
393
|
+
old.settings = void 0;
|
|
394
|
+
old.subscription = void 0;
|
|
395
|
+
old.overThreshold = false;
|
|
396
|
+
}
|
|
397
|
+
if (credentialsChanged) {
|
|
398
|
+
old.reauthRequired = false;
|
|
399
|
+
old.refreshFailedUntil = 0;
|
|
400
|
+
old.refreshFailure = void 0;
|
|
401
|
+
old.zdr = null;
|
|
402
|
+
}
|
|
403
|
+
} else this.tokens.set(id, {
|
|
404
|
+
id,
|
|
405
|
+
name,
|
|
406
|
+
weight,
|
|
407
|
+
quotaThreshold,
|
|
408
|
+
credentials,
|
|
409
|
+
invalidReason,
|
|
410
|
+
generation: 1,
|
|
411
|
+
inFlight: 0,
|
|
412
|
+
cooldowns: /* @__PURE__ */ new Map(),
|
|
413
|
+
quotaBlocks: /* @__PURE__ */ new Map(),
|
|
414
|
+
entitlementBlocks: /* @__PURE__ */ new Map(),
|
|
415
|
+
reauthRequired: false,
|
|
416
|
+
refreshFailedUntil: 0,
|
|
417
|
+
refreshFailure: void 0,
|
|
418
|
+
billing: void 0,
|
|
419
|
+
billingNotBefore: 0,
|
|
420
|
+
settings: void 0,
|
|
421
|
+
subscription: void 0,
|
|
422
|
+
zdr: null,
|
|
423
|
+
overThreshold: false,
|
|
424
|
+
lastTier: credentials?.subscriptionTier,
|
|
425
|
+
failoverCount: 0
|
|
426
|
+
});
|
|
427
|
+
if (invalidReason) {
|
|
428
|
+
logger.warn(`grok token ${name}[${id}] treated as inactive: ${invalidReason}`);
|
|
429
|
+
this.selector.removeToken(id);
|
|
430
|
+
} else this.selector.setToken(id, weight);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* `held` counts reservations the caller already holds on `token` (re-checks mid-request).
|
|
434
|
+
* `ungated` skips the admission gates and checks only that the credentials are usable.
|
|
435
|
+
*/
|
|
436
|
+
ineligibility(token, requestClass, now, held = 0, ungated = false) {
|
|
437
|
+
if (!token.credentials || token.invalidReason) return "inactive";
|
|
438
|
+
if (token.reauthRequired) return "reauth";
|
|
439
|
+
if (token.refreshFailedUntil > now) return "refresh_failed";
|
|
440
|
+
if (ungated) return void 0;
|
|
441
|
+
const settings = token.settings?.value;
|
|
442
|
+
if (settings?.kind === "ok" && settings.onDemandEnabled === true) return "on_demand";
|
|
443
|
+
if (requestClass === "video" && token.zdr === true) return "zdr";
|
|
444
|
+
if ((token.entitlementBlocks.get(requestClass) ?? 0) > now) return "entitlement";
|
|
445
|
+
if ((token.quotaBlocks.get(requestClass) ?? 0) > now) return "quota";
|
|
446
|
+
if (token.quotaThreshold !== null && token.overThreshold) return "quota";
|
|
447
|
+
if ((token.cooldowns.get(requestClass) ?? 0) > now) return "cooldown";
|
|
448
|
+
if (this.maxInflightPerToken !== void 0 && token.inFlight - held >= this.maxInflightPerToken) return "capacity";
|
|
449
|
+
}
|
|
450
|
+
isEligible(token, requestClass) {
|
|
451
|
+
return this.ineligibility(token, requestClass, this.now()) === void 0;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Refreshes the 60 s settings/subscription/billing caches. Lookup failures fail open, so an
|
|
455
|
+
* expired bearer is refreshed first: a lookup with it would fail and open every gate. A failed
|
|
456
|
+
* refresh therefore takes the token out of selection (`refresh_failed`) instead of failing open.
|
|
457
|
+
*/
|
|
458
|
+
async prefetch(token, signal) {
|
|
459
|
+
if (!token.credentials || token.invalidReason || token.reauthRequired) return;
|
|
460
|
+
let now = this.now();
|
|
461
|
+
const tasks = [];
|
|
462
|
+
const stale = (entry) => !entry || entry.generation !== token.generation || now - entry.fetchedAt >= 6e4;
|
|
463
|
+
const billingDue = () => (token.quotaThreshold !== null || [...token.quotaBlocks.values()].some((u) => u > now)) && stale(token.billing) && now >= token.billingNotBefore;
|
|
464
|
+
if (!stale(token.settings) && !stale(token.subscription) && !billingDue()) return;
|
|
465
|
+
if (grokNeedsRefresh(token.credentials, now)) {
|
|
466
|
+
if (token.refreshFailedUntil > now) return;
|
|
467
|
+
const presented = token.credentials;
|
|
468
|
+
try {
|
|
469
|
+
await this.freshCredentials(token, signal ?? new AbortController().signal);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
if (signal?.aborted) return;
|
|
472
|
+
if (error instanceof Error && error.name === "GrokReauthRequiredError") this.markReauthRequired(token, presented, reauthReasonCode(oauthErrorOf(error)));
|
|
473
|
+
else this.markRefreshFailed(token, presented, error);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (!token.credentials || token.reauthRequired || this.tokens.get(token.id) !== token) return;
|
|
477
|
+
now = this.now();
|
|
478
|
+
}
|
|
479
|
+
if (stale(token.settings)) tasks.push(this.loadSettings(token));
|
|
480
|
+
if (stale(token.subscription)) tasks.push(this.loadSubscription(token));
|
|
481
|
+
const blocked = [...token.quotaBlocks.values()].some((until) => until > now);
|
|
482
|
+
if ((token.quotaThreshold !== null || blocked) && stale(token.billing)) {
|
|
483
|
+
if (now >= token.billingNotBefore) tasks.push(this.loadBilling(token));
|
|
484
|
+
}
|
|
485
|
+
await Promise.all(tasks);
|
|
486
|
+
}
|
|
487
|
+
/** Transient refresh failure for `presented`; ignored when the token already holds newer ones. */
|
|
488
|
+
markRefreshFailed(token, presented, cause) {
|
|
489
|
+
if (this.tokens.get(token.id) !== token || !sameCredentials(token.credentials, presented)) return;
|
|
490
|
+
token.refreshFailedUntil = this.now() + GROK_REFRESH_FAILED_COOLDOWN_MS;
|
|
491
|
+
token.refreshFailure = cause;
|
|
492
|
+
logger.warn(`grok token ${token.name}[${token.id}] refresh failed; out of selection for ${GROK_REFRESH_FAILED_COOLDOWN_MS}ms: ${cause instanceof Error ? cause.name : "unknown"}`);
|
|
493
|
+
}
|
|
494
|
+
loadSettings(token) {
|
|
495
|
+
const pending = this.pendingSettings.get(token.id);
|
|
496
|
+
if (pending) return pending;
|
|
497
|
+
const generation = token.generation;
|
|
498
|
+
const credentials = token.credentials;
|
|
499
|
+
const promise = readGrokSettings(credentials, { fetch: this.fetchImpl }).then((result) => {
|
|
500
|
+
if (this.tokens.get(token.id) !== token || token.generation !== generation) return;
|
|
501
|
+
token.settings = {
|
|
502
|
+
value: result,
|
|
503
|
+
fetchedAt: this.now(),
|
|
504
|
+
generation
|
|
505
|
+
};
|
|
506
|
+
if (result.kind === "lookup_failed") {
|
|
507
|
+
logger.warn("quota_threshold gate: settings_lookup_fail_open", {
|
|
508
|
+
tokenId: token.id,
|
|
509
|
+
tokenName: token.name,
|
|
510
|
+
lookupReason: result.reason
|
|
511
|
+
});
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (result.subscriptionTierDisplay) token.lastTier = result.subscriptionTierDisplay;
|
|
515
|
+
if (result.onDemandEnabled === true) logger.info(`quota_threshold gate: on_demand_excluded ${token.name}[${token.id}] (on_demand_enabled=true)`);
|
|
516
|
+
}).finally(() => {
|
|
517
|
+
if (this.pendingSettings.get(token.id) === promise) this.pendingSettings.delete(token.id);
|
|
518
|
+
});
|
|
519
|
+
this.pendingSettings.set(token.id, promise);
|
|
520
|
+
return promise;
|
|
521
|
+
}
|
|
522
|
+
/** /user codingDataRetentionOptOut is the primary ZDR signal; a failed read keeps the last value. */
|
|
523
|
+
loadSubscription(token) {
|
|
524
|
+
const pending = this.pendingSubscription.get(token.id);
|
|
525
|
+
if (pending) return pending;
|
|
526
|
+
const generation = token.generation;
|
|
527
|
+
const credentials = token.credentials;
|
|
528
|
+
const promise = readGrokSubscription(credentials, { fetch: this.fetchImpl }).then((result) => {
|
|
529
|
+
if (this.tokens.get(token.id) !== token || token.generation !== generation) return;
|
|
530
|
+
token.subscription = {
|
|
531
|
+
value: result,
|
|
532
|
+
fetchedAt: this.now(),
|
|
533
|
+
generation
|
|
534
|
+
};
|
|
535
|
+
if (result.kind === "lookup_failed") {
|
|
536
|
+
logger.debug(`grok subscription lookup failed for ${token.name}[${token.id}]`, { lookupReason: result.reason });
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (result.dataRetentionOptOut !== null) this.setZdr(token, result.dataRetentionOptOut, "codingDataRetentionOptOut");
|
|
540
|
+
}).finally(() => {
|
|
541
|
+
if (this.pendingSubscription.get(token.id) === promise) this.pendingSubscription.delete(token.id);
|
|
542
|
+
});
|
|
543
|
+
this.pendingSubscription.set(token.id, promise);
|
|
544
|
+
return promise;
|
|
545
|
+
}
|
|
546
|
+
setZdr(token, zdr, source) {
|
|
547
|
+
if (this.tokens.get(token.id) !== token || token.zdr === zdr) return;
|
|
548
|
+
token.zdr = zdr;
|
|
549
|
+
logger.info(zdr ? `grok token ${token.name}[${token.id}] excluded from video: zero data retention (${source})` : `grok token ${token.name}[${token.id}] video eligible: zero data retention off (${source})`);
|
|
550
|
+
}
|
|
551
|
+
/** `x-zero-data-retention: true` confirms ZDR; its absence never clears it (next /user read does). */
|
|
552
|
+
observeResponseHeaders(token, credentials, headers) {
|
|
553
|
+
if (token.credentials?.accessToken !== credentials.accessToken) return;
|
|
554
|
+
if (headers.zeroDataRetention === true) this.setZdr(token, true, "x-zero-data-retention");
|
|
555
|
+
}
|
|
556
|
+
loadBilling(token) {
|
|
557
|
+
const pending = this.pendingBilling.get(token.id);
|
|
558
|
+
if (pending) return pending;
|
|
559
|
+
const generation = token.generation;
|
|
560
|
+
const credentials = token.credentials;
|
|
561
|
+
const promise = readGrokBilling(credentials, { fetch: this.fetchImpl }).then((result) => {
|
|
562
|
+
if (this.tokens.get(token.id) !== token || token.generation !== generation) return;
|
|
563
|
+
token.billing = {
|
|
564
|
+
value: result,
|
|
565
|
+
fetchedAt: this.now(),
|
|
566
|
+
generation
|
|
567
|
+
};
|
|
568
|
+
if (result.kind === "lookup_failed") {
|
|
569
|
+
token.overThreshold = false;
|
|
570
|
+
logger.warn("quota_threshold gate: lookup_fail_open", {
|
|
571
|
+
tokenId: token.id,
|
|
572
|
+
tokenName: token.name,
|
|
573
|
+
lookupReason: result.reason
|
|
574
|
+
});
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const limit = token.quotaThreshold ?? 100;
|
|
578
|
+
const over = token.quotaThreshold !== null && result.utilizationPct >= token.quotaThreshold;
|
|
579
|
+
if (over && !token.overThreshold) logger.info(`quota_threshold gate: over_threshold ${token.name}[${token.id}] (${result.utilizationPct}% >= ${token.quotaThreshold}%)`);
|
|
580
|
+
if (!over && token.overThreshold) logger.info(`quota_threshold gate: recovered ${token.name}[${token.id}] (${result.utilizationPct}% < ${token.quotaThreshold}%)`);
|
|
581
|
+
token.overThreshold = over;
|
|
582
|
+
if (result.utilizationPct < limit && token.quotaBlocks.size) {
|
|
583
|
+
token.quotaBlocks.clear();
|
|
584
|
+
logger.info(`quota_threshold gate: exhaustion_cleared ${token.name}[${token.id}] (${result.utilizationPct}% < ${limit}%)`);
|
|
585
|
+
}
|
|
586
|
+
}).finally(() => {
|
|
587
|
+
if (this.pendingBilling.get(token.id) === promise) this.pendingBilling.delete(token.id);
|
|
588
|
+
});
|
|
589
|
+
this.pendingBilling.set(token.id, promise);
|
|
590
|
+
return promise;
|
|
591
|
+
}
|
|
592
|
+
reserve(token) {
|
|
593
|
+
token.inFlight++;
|
|
594
|
+
let released = false;
|
|
595
|
+
return {
|
|
596
|
+
token,
|
|
597
|
+
release: () => {
|
|
598
|
+
if (released) return;
|
|
599
|
+
released = true;
|
|
600
|
+
token.inFlight = Math.max(0, token.inFlight - 1);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
checkVersionGate(requestClass) {
|
|
605
|
+
if (requestClass !== "chat" || !this.versionGate) return;
|
|
606
|
+
if (this.now() - this.versionGate.trippedAt < 6e5) throw new GrokVersionGateError(this.versionGate.minClientVersion);
|
|
607
|
+
}
|
|
608
|
+
async selectToken(requestClass, options = {}) {
|
|
609
|
+
this.checkVersionGate(requestClass);
|
|
610
|
+
const excluded = options.excluded ?? /* @__PURE__ */ new Set();
|
|
611
|
+
if (options.preferredTokenId !== void 0) {
|
|
612
|
+
const preferred = this.tokens.get(options.preferredTokenId);
|
|
613
|
+
if (preferred && !excluded.has(preferred.id)) for (let round = 0;; round++) {
|
|
614
|
+
await this.prefetch(preferred, options.signal);
|
|
615
|
+
options.signal?.throwIfAborted();
|
|
616
|
+
const now = this.now();
|
|
617
|
+
const reason = this.ineligibility(preferred, requestClass, now, 0, options.ungated);
|
|
618
|
+
if (!reason) return this.reserve(preferred);
|
|
619
|
+
if (!options.requirePreferredToken) break;
|
|
620
|
+
const waitMs = preferred.refreshFailedUntil - now;
|
|
621
|
+
if (reason === "refresh_failed" && round === 0 && waitMs <= 3e4 && (options.deadline === void 0 || now + waitMs < options.deadline)) {
|
|
622
|
+
await this.sleep(Math.max(0, waitMs), options.signal);
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
throw this.preferredError(preferred, reason);
|
|
626
|
+
}
|
|
627
|
+
else if (options.requirePreferredToken) throw new GrokNoWorkersError(`Preferred grok token ${options.preferredTokenId} is not available`);
|
|
628
|
+
}
|
|
629
|
+
for (let round = 0;; round++) {
|
|
630
|
+
const candidates = [...this.tokens.values()].filter((token) => !excluded.has(token.id));
|
|
631
|
+
await Promise.all(candidates.map((token) => this.prefetch(token, options.signal)));
|
|
632
|
+
options.signal?.throwIfAborted();
|
|
633
|
+
const now = this.now();
|
|
634
|
+
const reasons = /* @__PURE__ */ new Map();
|
|
635
|
+
const eligible = /* @__PURE__ */ new Set();
|
|
636
|
+
for (const token of candidates) {
|
|
637
|
+
const reason = this.ineligibility(token, requestClass, now);
|
|
638
|
+
if (reason) reasons.set(token, reason);
|
|
639
|
+
else eligible.add(token.id);
|
|
640
|
+
}
|
|
641
|
+
if (eligible.size) {
|
|
642
|
+
const selected = this.selector.select(eligible);
|
|
643
|
+
const token = selected === null ? void 0 : this.tokens.get(selected);
|
|
644
|
+
if (token) return this.reserve(token);
|
|
645
|
+
}
|
|
646
|
+
const present = new Set(reasons.values());
|
|
647
|
+
if (present.has("capacity")) throw new GrokCapacityError();
|
|
648
|
+
if (present.has("cooldown") || present.has("refresh_failed")) {
|
|
649
|
+
const until = (token, reason) => reason === "cooldown" ? token.cooldowns.get(requestClass) ?? now : reason === "refresh_failed" ? token.refreshFailedUntil : Number.POSITIVE_INFINITY;
|
|
650
|
+
const soonest = Math.min(...[...reasons].map(([token, reason]) => until(token, reason)));
|
|
651
|
+
const waitMs = Math.max(0, Math.min(soonest - now, GROK_MAX_COOLDOWN_WAIT_MS));
|
|
652
|
+
const withinDeadline = options.deadline === void 0 || now + waitMs < options.deadline;
|
|
653
|
+
if (round === 0 && withinDeadline) {
|
|
654
|
+
await this.sleep(waitMs, options.signal);
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
const cooling = [...reasons].filter(([, reason]) => reason === "cooldown");
|
|
658
|
+
if (cooling.length) {
|
|
659
|
+
const soonestCooldown = Math.min(...cooling.map(([token]) => until(token, "cooldown")));
|
|
660
|
+
throw new GrokRateLimitedError(Math.max(1, Math.ceil((soonestCooldown - now) / 1e3)));
|
|
661
|
+
}
|
|
662
|
+
throw new GrokCredentialRefreshError([...reasons].find(([, reason]) => reason === "refresh_failed")?.[0].refreshFailure ?? "refresh failed", false);
|
|
663
|
+
}
|
|
664
|
+
if (present.has("quota")) {
|
|
665
|
+
const over = [...reasons].flatMap(([token, reason]) => reason === "quota" ? [{
|
|
666
|
+
name: token.name,
|
|
667
|
+
threshold: token.overThreshold ? token.quotaThreshold : null
|
|
668
|
+
}] : []);
|
|
669
|
+
logger.warn("quota_threshold gate: all_exceeded", {
|
|
670
|
+
provider: "grok",
|
|
671
|
+
overThresholdTokens: over
|
|
672
|
+
});
|
|
673
|
+
throw new QuotaThresholdExceededError(quotaMessage(over));
|
|
674
|
+
}
|
|
675
|
+
if (present.has("on_demand")) throw new GrokOnDemandExcludedError(tokenNamesWithReason(reasons, "on_demand"));
|
|
676
|
+
if (present.has("zdr")) throw new GrokZdrVideoUnavailableError(tokenNamesWithReason(reasons, "zdr"));
|
|
677
|
+
throw new GrokNoWorkersError();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
preferredError(token, reason) {
|
|
681
|
+
const now = this.now();
|
|
682
|
+
if (reason === "quota") return new QuotaThresholdExceededError(quotaMessage([{
|
|
683
|
+
name: token.name,
|
|
684
|
+
threshold: token.overThreshold ? token.quotaThreshold : null
|
|
685
|
+
}]));
|
|
686
|
+
if (reason === "cooldown") {
|
|
687
|
+
const until = Math.max(...token.cooldowns.values());
|
|
688
|
+
return new GrokRateLimitedError(Math.max(1, Math.ceil((until - now) / 1e3)));
|
|
689
|
+
}
|
|
690
|
+
if (reason === "capacity") return new GrokCapacityError();
|
|
691
|
+
if (reason === "refresh_failed") return new GrokCredentialRefreshError(token.refreshFailure ?? "refresh failed", false);
|
|
692
|
+
if (reason === "on_demand") return new GrokOnDemandExcludedError([token.name]);
|
|
693
|
+
if (reason === "zdr") return new GrokZdrVideoUnavailableError([token.name]);
|
|
694
|
+
return new GrokNoWorkersError(`Preferred grok token ${token.id} is not available (${reason})`);
|
|
695
|
+
}
|
|
696
|
+
applyHttpFailure(token, requestClass, error) {
|
|
697
|
+
const now = this.now();
|
|
698
|
+
const { classification } = error;
|
|
699
|
+
if (classification.kind === "rate_limited") {
|
|
700
|
+
const cooldownMs = Math.min(classification.retryAfterSec === void 0 ? GROK_DEFAULT_COOLDOWN_MS : classification.retryAfterSec * 1e3, GROK_MAX_COOLDOWN_MS);
|
|
701
|
+
token.cooldowns.set(requestClass, now + cooldownMs);
|
|
702
|
+
logger.info(`grok token ${token.name}[${token.id}] cooldown ${cooldownMs}ms (${requestClass})`);
|
|
703
|
+
} else if (classification.kind === "quota_exhausted") {
|
|
704
|
+
const billing = token.billing?.value;
|
|
705
|
+
const periodEnd = billing?.kind === "ok" ? billing.periodEndMs : null;
|
|
706
|
+
const until = periodEnd !== null && periodEnd > now ? periodEnd : now + GROK_QUOTA_BLOCK_FALLBACK_MS;
|
|
707
|
+
token.quotaBlocks.set(requestClass, until);
|
|
708
|
+
token.billing = void 0;
|
|
709
|
+
token.billingNotBefore = now + GROK_LOOKUP_CACHE_TTL_MS;
|
|
710
|
+
logger.info(`quota_threshold gate: exhausted ${token.name}[${token.id}] until ${new Date(until).toISOString()} (${classification.code ?? "429"})`);
|
|
711
|
+
} else if (classification.kind === "entitlement" && requestClass !== "chat") token.entitlementBlocks.set(requestClass, now + GROK_ENTITLEMENT_BLOCK_MS);
|
|
712
|
+
else if (classification.kind === "version_gate" && requestClass === "chat") {
|
|
713
|
+
this.versionGate = {
|
|
714
|
+
trippedAt: now,
|
|
715
|
+
minClientVersion: classification.minClientVersion
|
|
716
|
+
};
|
|
717
|
+
logger.error(`grok chat proxy version gate tripped: ${GROK_BUILD_VERSION} < ${classification.minClientVersion ?? "?"}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
classifyFailure(token, requestClass, cause, presented, refreshedThisRequest, refreshBudgetLeft) {
|
|
721
|
+
if (cause instanceof GrokIdleTimeoutError) return requestClass === "chat" ? {
|
|
722
|
+
kind: "failover",
|
|
723
|
+
error: cause,
|
|
724
|
+
transient: true
|
|
725
|
+
} : {
|
|
726
|
+
kind: "fail",
|
|
727
|
+
error: cause
|
|
728
|
+
};
|
|
729
|
+
if (cause instanceof GrokCredentialRefreshError) {
|
|
730
|
+
if (presented) if (cause.reauthRequired) this.markReauthRequired(token, presented, reauthReasonCode(oauthErrorOf(cause.cause)));
|
|
731
|
+
else this.markRefreshFailed(token, presented, cause.cause);
|
|
732
|
+
return {
|
|
733
|
+
kind: "failover",
|
|
734
|
+
error: cause,
|
|
735
|
+
transient: false
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
if (!(cause instanceof GrokHttpError)) return {
|
|
739
|
+
kind: "fail",
|
|
740
|
+
error: cause
|
|
741
|
+
};
|
|
742
|
+
if (requestClass === "video" && isGrokZdrVideoRejection(cause)) {
|
|
743
|
+
this.setZdr(token, true, "upstream video 400");
|
|
744
|
+
token.entitlementBlocks.set("video", this.now() + GROK_ENTITLEMENT_BLOCK_MS);
|
|
745
|
+
return {
|
|
746
|
+
kind: "failover",
|
|
747
|
+
error: cause,
|
|
748
|
+
transient: false
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
this.applyHttpFailure(token, requestClass, cause);
|
|
752
|
+
const { classification } = cause;
|
|
753
|
+
if (classification.kind === "version_gate") return {
|
|
754
|
+
kind: "fail",
|
|
755
|
+
error: new GrokVersionGateError(classification.minClientVersion)
|
|
756
|
+
};
|
|
757
|
+
if (classification.shouldRetry === false) return {
|
|
758
|
+
kind: "fail",
|
|
759
|
+
error: cause
|
|
760
|
+
};
|
|
761
|
+
switch (classification.kind) {
|
|
762
|
+
case "auth":
|
|
763
|
+
if (!presented || !token.credentials) return {
|
|
764
|
+
kind: "failover",
|
|
765
|
+
error: cause,
|
|
766
|
+
transient: false
|
|
767
|
+
};
|
|
768
|
+
if (token.credentials.accessToken !== presented.accessToken) return {
|
|
769
|
+
kind: "retry",
|
|
770
|
+
error: cause
|
|
771
|
+
};
|
|
772
|
+
if (refreshedThisRequest) {
|
|
773
|
+
this.markReauthRequired(token, presented, "grok:unauthorized_after_refresh");
|
|
774
|
+
return {
|
|
775
|
+
kind: "failover",
|
|
776
|
+
error: cause,
|
|
777
|
+
transient: false
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
if (refreshBudgetLeft) return {
|
|
781
|
+
kind: "refresh",
|
|
782
|
+
presented
|
|
783
|
+
};
|
|
784
|
+
return {
|
|
785
|
+
kind: "failover",
|
|
786
|
+
error: cause,
|
|
787
|
+
transient: false
|
|
788
|
+
};
|
|
789
|
+
case "rate_limited":
|
|
790
|
+
case "quota_exhausted": return {
|
|
791
|
+
kind: "failover",
|
|
792
|
+
error: cause,
|
|
793
|
+
transient: false
|
|
794
|
+
};
|
|
795
|
+
case "entitlement": return requestClass === "chat" ? {
|
|
796
|
+
kind: "fail",
|
|
797
|
+
error: cause
|
|
798
|
+
} : {
|
|
799
|
+
kind: "failover",
|
|
800
|
+
error: cause,
|
|
801
|
+
transient: false
|
|
802
|
+
};
|
|
803
|
+
case "transient":
|
|
804
|
+
if (requestClass === "chat") return {
|
|
805
|
+
kind: "failover",
|
|
806
|
+
error: cause,
|
|
807
|
+
transient: true
|
|
808
|
+
};
|
|
809
|
+
return cause.status === 0 ? {
|
|
810
|
+
kind: "failover",
|
|
811
|
+
error: cause,
|
|
812
|
+
transient: true
|
|
813
|
+
} : {
|
|
814
|
+
kind: "fail",
|
|
815
|
+
error: cause
|
|
816
|
+
};
|
|
817
|
+
default: return {
|
|
818
|
+
kind: "fail",
|
|
819
|
+
error: cause
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Marks `token` dead in memory and, on the first transition, persists `tokens.reauth_required`.
|
|
825
|
+
* `credentials` are the ones the failed attempt presented; a token already holding newer ones
|
|
826
|
+
* (another bearer or refresh token) is left alone.
|
|
827
|
+
*/
|
|
828
|
+
markReauthRequired(token, credentials, reasonCode) {
|
|
829
|
+
if (this.tokens.get(token.id) !== token) return;
|
|
830
|
+
if (!sameCredentials(token.credentials, credentials)) return;
|
|
831
|
+
if (token.reauthRequired) return;
|
|
832
|
+
token.reauthRequired = true;
|
|
833
|
+
logger.warn(`grok token ${token.name}[${token.id}] requires re-authentication (${reasonCode})`);
|
|
834
|
+
this.reportReauthRequired({
|
|
835
|
+
id: token.id,
|
|
836
|
+
name: token.name,
|
|
837
|
+
credentials
|
|
838
|
+
}, reasonCode).catch((cause) => {
|
|
839
|
+
logger.warn(`grok token ${token.name}[${token.id}] reauth_required persist failed: ${cause instanceof Error ? cause.name : "unknown"}`);
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Runs `attempt` with the §5.1 attempt budget: one forced refresh, at most GROK_MAX_HOPS
|
|
844
|
+
* failovers (one of them transient). `canRetry` reports whether upstream execution may have
|
|
845
|
+
* started, in which case nothing is retried.
|
|
846
|
+
*
|
|
847
|
+
* The reservation is held from selection until the request leaves the token (failover) or ends,
|
|
848
|
+
* so a refresh and its same-token retry never step outside the in-flight cap. Whenever the
|
|
849
|
+
* attempt is about to present credentials other than the ones the token's gates were evaluated
|
|
850
|
+
* with (a refresh happened), the lookups and gates are re-run before anything is sent.
|
|
851
|
+
*/
|
|
852
|
+
async withAttempts(requestClass, selection, attempt) {
|
|
853
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
854
|
+
const refreshed = /* @__PURE__ */ new Set();
|
|
855
|
+
let refreshes = 0;
|
|
856
|
+
let hops = 0;
|
|
857
|
+
let transientFailovers = 0;
|
|
858
|
+
let attempts = 0;
|
|
859
|
+
let staleRetries = 0;
|
|
860
|
+
let reservation = await this.selectToken(requestClass, {
|
|
861
|
+
...selection,
|
|
862
|
+
excluded
|
|
863
|
+
});
|
|
864
|
+
let gated = reservation.token.credentials;
|
|
865
|
+
try {
|
|
866
|
+
for (;;) {
|
|
867
|
+
const token = reservation.token;
|
|
868
|
+
const state = {
|
|
869
|
+
upstreamStarted: false,
|
|
870
|
+
forwarded: false,
|
|
871
|
+
presented: token.credentials
|
|
872
|
+
};
|
|
873
|
+
let action;
|
|
874
|
+
attempts++;
|
|
875
|
+
try {
|
|
876
|
+
let credentials;
|
|
877
|
+
try {
|
|
878
|
+
credentials = await this.freshCredentials(token, selection.signal);
|
|
879
|
+
} catch (error) {
|
|
880
|
+
if (selection.signal.aborted) throw error;
|
|
881
|
+
throw new GrokCredentialRefreshError(error);
|
|
882
|
+
}
|
|
883
|
+
state.presented = credentials;
|
|
884
|
+
const blocked = credentials.accessToken === gated?.accessToken ? void 0 : await this.regate(token, requestClass, selection.signal, selection.ungated);
|
|
885
|
+
gated = credentials;
|
|
886
|
+
if (blocked) action = {
|
|
887
|
+
kind: "failover",
|
|
888
|
+
error: blocked,
|
|
889
|
+
transient: false
|
|
890
|
+
};
|
|
891
|
+
else return await attempt(token, credentials, state);
|
|
892
|
+
} catch (error) {
|
|
893
|
+
if (selection.signal.aborted) throw abortReason(selection.signal);
|
|
894
|
+
if (state.forwarded || state.upstreamStarted) throw error;
|
|
895
|
+
action = this.classifyFailure(token, requestClass, error, state.presented, refreshed.has(token.id), refreshes < 1);
|
|
896
|
+
}
|
|
897
|
+
if (action.kind === "fail") throw action.error;
|
|
898
|
+
if (action.kind === "retry") {
|
|
899
|
+
if (staleRetries++ < 1) continue;
|
|
900
|
+
excluded.add(token.id);
|
|
901
|
+
action = {
|
|
902
|
+
kind: "failover",
|
|
903
|
+
error: action.error,
|
|
904
|
+
transient: false
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
if (action.kind === "refresh") {
|
|
908
|
+
refreshes++;
|
|
909
|
+
refreshed.add(token.id);
|
|
910
|
+
const next = await this.tryRefresh(token, action.presented, selection.signal);
|
|
911
|
+
if (next.kind === "refreshed") continue;
|
|
912
|
+
action = {
|
|
913
|
+
kind: "failover",
|
|
914
|
+
error: next.error,
|
|
915
|
+
transient: false
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
if (selection.requirePreferredToken) throw action.error;
|
|
919
|
+
if (action.transient && ++transientFailovers > 1) throw action.error;
|
|
920
|
+
if (++hops > 2) throw new GrokAttemptBudgetExceededError(attempts, action.error);
|
|
921
|
+
if (action.transient || action.error instanceof GrokCredentialRefreshError) excluded.add(token.id);
|
|
922
|
+
token.failoverCount++;
|
|
923
|
+
logger.info(`grok failover from ${token.name}[${token.id}] (${requestClass})`, { reason: action.error instanceof Error ? action.error.message : String(action.error) });
|
|
924
|
+
reservation.release();
|
|
925
|
+
try {
|
|
926
|
+
reservation = await this.selectToken(requestClass, {
|
|
927
|
+
excluded,
|
|
928
|
+
signal: selection.signal,
|
|
929
|
+
deadline: selection.deadline
|
|
930
|
+
});
|
|
931
|
+
} catch (selectionError) {
|
|
932
|
+
if (selectionError instanceof GrokNoWorkersError) throw action.error;
|
|
933
|
+
throw selectionError;
|
|
934
|
+
}
|
|
935
|
+
gated = reservation.token.credentials;
|
|
936
|
+
}
|
|
937
|
+
} finally {
|
|
938
|
+
reservation.release();
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Re-runs the lookups and gates for a token whose credentials changed after selection. Returns
|
|
943
|
+
* the gate's error when the token is no longer eligible; the caller still holds one reservation.
|
|
944
|
+
*/
|
|
945
|
+
async regate(token, requestClass, signal, ungated) {
|
|
946
|
+
await this.prefetch(token, signal);
|
|
947
|
+
signal.throwIfAborted();
|
|
948
|
+
const reason = this.ineligibility(token, requestClass, this.now(), 1, ungated);
|
|
949
|
+
return reason ? this.preferredError(token, reason) : void 0;
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* The one forced refresh after an upstream 401. A refreshed token is retried; otherwise the
|
|
953
|
+
* request fails over. Only a `reauth_required` answer marks the token dead — a thrown refresh
|
|
954
|
+
* failure is treated as transient and leaves the token alive.
|
|
955
|
+
*/
|
|
956
|
+
async tryRefresh(token, presented, signal) {
|
|
957
|
+
let result;
|
|
958
|
+
try {
|
|
959
|
+
result = await this.refresher.refreshAfterUnauthorized({
|
|
960
|
+
tokenId: token.id,
|
|
961
|
+
credentials: presented
|
|
962
|
+
}, signal);
|
|
963
|
+
} catch (error) {
|
|
964
|
+
if (signal.aborted) throw abortReason(signal);
|
|
965
|
+
return {
|
|
966
|
+
kind: "failover",
|
|
967
|
+
error: new GrokCredentialRefreshError(error)
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
if (result.status === "refreshed") {
|
|
971
|
+
this.storeCredentials(token, result.credentials);
|
|
972
|
+
return { kind: "refreshed" };
|
|
973
|
+
}
|
|
974
|
+
this.markReauthRequired(token, presented, reauthReasonCode(result.reason));
|
|
975
|
+
return {
|
|
976
|
+
kind: "failover",
|
|
977
|
+
error: GrokCredentialRefreshError.reauthRequired(result.reason)
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
async freshCredentials(token, signal) {
|
|
981
|
+
const current = token.credentials;
|
|
982
|
+
if (!current) throw new GrokNoWorkersError(`grok token ${token.id} has no valid credentials`);
|
|
983
|
+
const fresh = await this.refresher.ensureFresh({
|
|
984
|
+
tokenId: token.id,
|
|
985
|
+
credentials: current
|
|
986
|
+
}, signal);
|
|
987
|
+
this.storeCredentials(token, fresh);
|
|
988
|
+
return fresh;
|
|
989
|
+
}
|
|
990
|
+
storeCredentials(token, credentials) {
|
|
991
|
+
if (this.tokens.get(token.id) !== token) return;
|
|
992
|
+
if (token.credentials?.accessToken === credentials.accessToken) return;
|
|
993
|
+
token.credentials = credentials;
|
|
994
|
+
token.reauthRequired = false;
|
|
995
|
+
token.generation++;
|
|
996
|
+
this.pendingBilling.delete(token.id);
|
|
997
|
+
this.pendingSettings.delete(token.id);
|
|
998
|
+
this.pendingSubscription.delete(token.id);
|
|
999
|
+
}
|
|
1000
|
+
async generate(req) {
|
|
1001
|
+
return this.runChat(req);
|
|
1002
|
+
}
|
|
1003
|
+
async generateStream(req, cb) {
|
|
1004
|
+
try {
|
|
1005
|
+
cb.onComplete(await this.runChat(req, cb.onDelta));
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
cb.onError(error instanceof Error ? error : new Error(String(error)));
|
|
1008
|
+
throw error;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
buildChatBody(req) {
|
|
1012
|
+
if (req.imageGeneration !== void 0 && req.imageGeneration !== false) throw new GrokInvalidRequestError("imageGeneration is not supported on grok chat models; use the Grok image endpoint");
|
|
1013
|
+
if (req.imageGenerationOptions !== void 0) throw new GrokInvalidRequestError("imageGenerationOptions is not supported on grok chat models");
|
|
1014
|
+
if (req.serviceTier !== void 0) throw new GrokInvalidRequestError("serviceTier is not supported on grok chat models");
|
|
1015
|
+
const xai = req.providerOptions?.xai;
|
|
1016
|
+
if (isJsonObject(xai)) {
|
|
1017
|
+
for (const key of ["serviceTier", "parallelToolCalls"]) if (xai[key] !== void 0 && xai[key] !== null) throw new GrokInvalidRequestError(`providerOptions.xai.${key} is not supported on grok chat models: the subscription proxy's handling is unverified`);
|
|
1018
|
+
}
|
|
1019
|
+
if (req.thinking === false) throw new ThinkingValidationError("thinking:false is unavailable for Grok models: thinking-off support has not been verified on the subscription proxy");
|
|
1020
|
+
const model = this.catalog.resolve(req.model);
|
|
1021
|
+
if (!model.known) logger.info(`grok model "${model.id}" is not in the catalog; forwarding it unvalidated`);
|
|
1022
|
+
let effort = model.defaultEffort;
|
|
1023
|
+
if (req.effort !== void 0) {
|
|
1024
|
+
const validated = this.catalog.validateEffort(model.id, req.effort);
|
|
1025
|
+
if (validated) effort = validated;
|
|
1026
|
+
else logger.debug(`ignoring effort "${req.effort}" for grok/${model.id}`);
|
|
1027
|
+
}
|
|
1028
|
+
const history = historyItems(req.coldHistory);
|
|
1029
|
+
const { convId, convGroupId } = grokConversationIds(req.promptCacheKey);
|
|
1030
|
+
const turnIdx = history.filter((item) => item.type === "message" && item.role === "user").length + 1;
|
|
1031
|
+
const tools = grokTools(req.tools);
|
|
1032
|
+
return {
|
|
1033
|
+
body: buildGrokResponsesRequest({
|
|
1034
|
+
model: model.id,
|
|
1035
|
+
systemPrompt: req.systemPrompt,
|
|
1036
|
+
history: [...history, userMessage(req)],
|
|
1037
|
+
promptCacheKey: convId,
|
|
1038
|
+
effort,
|
|
1039
|
+
outputSchema: req.outputSchema === void 0 ? void 0 : { schema: req.outputSchema },
|
|
1040
|
+
tools
|
|
1041
|
+
}),
|
|
1042
|
+
model,
|
|
1043
|
+
convId,
|
|
1044
|
+
convGroupId,
|
|
1045
|
+
turnIdx
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
async runChat(req, onDelta) {
|
|
1049
|
+
const { body, model, convId, convGroupId, turnIdx } = this.buildChatBody(req);
|
|
1050
|
+
const timeoutMs = req.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
1051
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
1052
|
+
const signal = req.abortSignal ? AbortSignal.any([req.abortSignal, timeout]) : timeout;
|
|
1053
|
+
signal.throwIfAborted();
|
|
1054
|
+
const deadline = this.now() + timeoutMs;
|
|
1055
|
+
return this.withAttempts("chat", {
|
|
1056
|
+
preferredTokenId: req.preferredTokenId,
|
|
1057
|
+
requirePreferredToken: req.requirePreferredToken,
|
|
1058
|
+
signal,
|
|
1059
|
+
deadline
|
|
1060
|
+
}, (token, credentials, state) => this.chatAttempt(token, credentials, state, {
|
|
1061
|
+
req,
|
|
1062
|
+
body,
|
|
1063
|
+
model,
|
|
1064
|
+
convId,
|
|
1065
|
+
convGroupId,
|
|
1066
|
+
turnIdx,
|
|
1067
|
+
signal,
|
|
1068
|
+
onDelta
|
|
1069
|
+
}));
|
|
1070
|
+
}
|
|
1071
|
+
async chatAttempt(token, credentials, state, context) {
|
|
1072
|
+
const { req, model } = context;
|
|
1073
|
+
const startedAt = this.now();
|
|
1074
|
+
const headers = buildGrokChatHeaders({
|
|
1075
|
+
bearer: credentials.accessToken,
|
|
1076
|
+
model: model.id,
|
|
1077
|
+
convId: context.convId,
|
|
1078
|
+
reqId: randomUUID(),
|
|
1079
|
+
sessionId: credentials.sessionId,
|
|
1080
|
+
agentId: credentials.agentId,
|
|
1081
|
+
userId: credentials.userId,
|
|
1082
|
+
convGroupId: context.convGroupId,
|
|
1083
|
+
turnIdx: context.turnIdx,
|
|
1084
|
+
zstd: true
|
|
1085
|
+
});
|
|
1086
|
+
let stream;
|
|
1087
|
+
try {
|
|
1088
|
+
stream = await this.client.stream({
|
|
1089
|
+
body: context.body,
|
|
1090
|
+
headers,
|
|
1091
|
+
signal: context.signal,
|
|
1092
|
+
idleTimeoutMs: this.idleTimeoutMs
|
|
1093
|
+
});
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
if (error instanceof GrokHttpError) this.observeResponseHeaders(token, credentials, error.responseHeaders);
|
|
1096
|
+
throw error;
|
|
1097
|
+
}
|
|
1098
|
+
this.observeResponseHeaders(token, credentials, stream.headers);
|
|
1099
|
+
this.versionGate = void 0;
|
|
1100
|
+
if (this.catalog.isStale(stream.headers.modelsEtag)) this.catalog.refresh(credentials, { fetch: this.fetchImpl }).then((result) => {
|
|
1101
|
+
if (result.kind === "lookup_failed") logger.warn(`grok model catalog refresh failed: ${result.reason}`);
|
|
1102
|
+
});
|
|
1103
|
+
let text = "";
|
|
1104
|
+
let firstDeltaAt;
|
|
1105
|
+
let usage;
|
|
1106
|
+
let servingModel = model.id;
|
|
1107
|
+
let outputMessageAccepted = false;
|
|
1108
|
+
const toolCalls = [];
|
|
1109
|
+
let completed = false;
|
|
1110
|
+
for await (const event of stream.events) {
|
|
1111
|
+
state.upstreamStarted = true;
|
|
1112
|
+
if (event.type === "created") {
|
|
1113
|
+
if (event.model) servingModel = event.model;
|
|
1114
|
+
} else if (event.type === "text-delta") {
|
|
1115
|
+
if (outputMessageAccepted) continue;
|
|
1116
|
+
firstDeltaAt ??= this.now();
|
|
1117
|
+
text += event.text;
|
|
1118
|
+
if (context.onDelta) {
|
|
1119
|
+
state.forwarded = true;
|
|
1120
|
+
context.onDelta(event.text);
|
|
1121
|
+
}
|
|
1122
|
+
} else if (event.type === "output-item" && event.completed) {
|
|
1123
|
+
const item = event.item;
|
|
1124
|
+
if (item.type === "function_call") {
|
|
1125
|
+
const callId = jsonString(item.call_id);
|
|
1126
|
+
const name = jsonString(item.name);
|
|
1127
|
+
const args = jsonString(item.arguments);
|
|
1128
|
+
if (callId !== void 0 && name !== void 0 && args !== void 0) toolCalls.push({
|
|
1129
|
+
toolCallId: callId,
|
|
1130
|
+
toolName: name,
|
|
1131
|
+
input: args
|
|
1132
|
+
});
|
|
1133
|
+
} else if (item.type === "message" && req.stopAfterOutputMessage && !outputMessageAccepted && Array.isArray(item.content)) {
|
|
1134
|
+
const messageText = item.content.map((part) => isJsonObject(part) && part.type === "output_text" ? jsonString(part.text) ?? "" : "").join("");
|
|
1135
|
+
if (messageText) outputMessageAccepted = req.stopAfterOutputMessage(messageText);
|
|
1136
|
+
}
|
|
1137
|
+
} else if (event.type === "completed") {
|
|
1138
|
+
completed = true;
|
|
1139
|
+
if (event.model) servingModel = event.model;
|
|
1140
|
+
usage = event.usage;
|
|
1141
|
+
} else if (event.type === "incomplete") throw new GrokIncompleteResponseError(event.reason);
|
|
1142
|
+
else if (event.type === "error") throw event.error;
|
|
1143
|
+
}
|
|
1144
|
+
if (!completed) throw new GrokIncompleteResponseError("stream ended without response.completed");
|
|
1145
|
+
const ticks = usage?.costInUsdTicks;
|
|
1146
|
+
const contextTokens = usage?.contextInputTokens === void 0 && usage?.contextOutputTokens === void 0 ? void 0 : (usage.contextInputTokens ?? 0) + (usage.contextOutputTokens ?? 0);
|
|
1147
|
+
const contextWindow = stream.headers.contextWindow ?? model.contextWindow;
|
|
1148
|
+
const resultUsage = {
|
|
1149
|
+
totalTokens: usage?.totalTokens ?? 0,
|
|
1150
|
+
inputTokens: usage?.inputTokens ?? 0,
|
|
1151
|
+
cachedInputTokens: usage?.cachedInputTokens ?? 0,
|
|
1152
|
+
outputTokens: usage?.outputTokens ?? 0,
|
|
1153
|
+
reasoningOutputTokens: usage?.reasoningTokens ?? 0
|
|
1154
|
+
};
|
|
1155
|
+
if (contextTokens !== void 0) resultUsage.contextTokens = contextTokens;
|
|
1156
|
+
const xai = {};
|
|
1157
|
+
if (ticks !== void 0) xai.costInUsdTicks = ticks;
|
|
1158
|
+
if (contextWindow !== void 0) xai.contextWindow = contextWindow;
|
|
1159
|
+
const result = {
|
|
1160
|
+
text,
|
|
1161
|
+
tokenName: token.name,
|
|
1162
|
+
usage: resultUsage,
|
|
1163
|
+
durationMs: this.now() - startedAt,
|
|
1164
|
+
ttftMs: firstDeltaAt === void 0 ? null : firstDeltaAt - startedAt,
|
|
1165
|
+
model: servingModel,
|
|
1166
|
+
threadCoord: {
|
|
1167
|
+
workerId: token.id,
|
|
1168
|
+
threadId: req.promptCacheKey ?? "",
|
|
1169
|
+
epoch: -1
|
|
1170
|
+
},
|
|
1171
|
+
providerMetadata: { xai }
|
|
1172
|
+
};
|
|
1173
|
+
if (servingModel !== model.id) result.requestedModel = model.id;
|
|
1174
|
+
if (ticks !== void 0) {
|
|
1175
|
+
result.costUsd = grokTicksToUsd(ticks);
|
|
1176
|
+
result.costSource = "provider";
|
|
1177
|
+
}
|
|
1178
|
+
if (toolCalls.length) result.toolCalls = toolCalls;
|
|
1179
|
+
return result;
|
|
1180
|
+
}
|
|
1181
|
+
async runPort(requestClass, selection, signal, call) {
|
|
1182
|
+
const active = signal ?? new AbortController().signal;
|
|
1183
|
+
return this.withAttempts(requestClass, {
|
|
1184
|
+
preferredTokenId: selection.preferredTokenId,
|
|
1185
|
+
requirePreferredToken: selection.requirePreferredToken,
|
|
1186
|
+
ungated: selection.ungated,
|
|
1187
|
+
signal: active
|
|
1188
|
+
}, async (token, credentials) => ({
|
|
1189
|
+
result: await call(credentials),
|
|
1190
|
+
tokenId: token.id,
|
|
1191
|
+
tokenName: token.name
|
|
1192
|
+
}));
|
|
1193
|
+
}
|
|
1194
|
+
requireMedia() {
|
|
1195
|
+
if (!this.media) throw new GrokCapabilityUnavailableError("media");
|
|
1196
|
+
return this.media;
|
|
1197
|
+
}
|
|
1198
|
+
requireVoice() {
|
|
1199
|
+
if (!this.voice) throw new GrokCapabilityUnavailableError("voice");
|
|
1200
|
+
return this.voice;
|
|
1201
|
+
}
|
|
1202
|
+
async generateImages(request) {
|
|
1203
|
+
const media = this.requireMedia();
|
|
1204
|
+
const { preferredTokenId, requirePreferredToken, ...portRequest } = request;
|
|
1205
|
+
return this.runPort("image", {
|
|
1206
|
+
preferredTokenId,
|
|
1207
|
+
requirePreferredToken
|
|
1208
|
+
}, request.signal, (credentials) => media.generateImages(mediaAuth(credentials), portRequest));
|
|
1209
|
+
}
|
|
1210
|
+
async editImages(request) {
|
|
1211
|
+
const media = this.requireMedia();
|
|
1212
|
+
const { preferredTokenId, requirePreferredToken, ...portRequest } = request;
|
|
1213
|
+
return this.runPort("image", {
|
|
1214
|
+
preferredTokenId,
|
|
1215
|
+
requirePreferredToken
|
|
1216
|
+
}, request.signal, (credentials) => media.editImages(mediaAuth(credentials), portRequest));
|
|
1217
|
+
}
|
|
1218
|
+
async startVideo(request) {
|
|
1219
|
+
const media = this.requireMedia();
|
|
1220
|
+
const { preferredTokenId, requirePreferredToken, ...portRequest } = request;
|
|
1221
|
+
return this.runPort("video", {
|
|
1222
|
+
preferredTokenId,
|
|
1223
|
+
requirePreferredToken
|
|
1224
|
+
}, request.signal, (credentials) => media.startVideo(mediaAuth(credentials), portRequest));
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Polls on the token that started the job; no selection, no failover. The job is already
|
|
1228
|
+
* admitted (and billed), so quota, cooldown and capacity gates must not strand its result.
|
|
1229
|
+
*/
|
|
1230
|
+
async getVideo(tokenId, requestId, signal) {
|
|
1231
|
+
const media = this.requireMedia();
|
|
1232
|
+
return this.runPort("video", {
|
|
1233
|
+
preferredTokenId: tokenId,
|
|
1234
|
+
requirePreferredToken: true,
|
|
1235
|
+
ungated: true
|
|
1236
|
+
}, signal, (credentials) => media.getVideo(mediaAuth(credentials), requestId, signal));
|
|
1237
|
+
}
|
|
1238
|
+
async transcribe(request) {
|
|
1239
|
+
const voice = this.requireVoice();
|
|
1240
|
+
const { preferredTokenId, requirePreferredToken, ...portRequest } = request;
|
|
1241
|
+
return this.runPort("stt", {
|
|
1242
|
+
preferredTokenId,
|
|
1243
|
+
requirePreferredToken
|
|
1244
|
+
}, request.signal, (credentials) => voice.transcribe(credentials.accessToken, portRequest));
|
|
1245
|
+
}
|
|
1246
|
+
async speak(request) {
|
|
1247
|
+
const voice = this.requireVoice();
|
|
1248
|
+
const { preferredTokenId, requirePreferredToken, ...portRequest } = request;
|
|
1249
|
+
return this.runPort("tts", {
|
|
1250
|
+
preferredTokenId,
|
|
1251
|
+
requirePreferredToken
|
|
1252
|
+
}, request.signal, (credentials) => voice.speak(credentials.accessToken, portRequest));
|
|
1253
|
+
}
|
|
1254
|
+
tokenStates() {
|
|
1255
|
+
const now = this.now();
|
|
1256
|
+
return [...this.tokens.values()].map((token) => {
|
|
1257
|
+
const billing = token.billing?.value;
|
|
1258
|
+
const settings = token.settings?.value;
|
|
1259
|
+
return {
|
|
1260
|
+
id: token.id,
|
|
1261
|
+
name: token.name,
|
|
1262
|
+
weight: token.weight,
|
|
1263
|
+
active: token.credentials !== void 0 && token.invalidReason === void 0,
|
|
1264
|
+
invalidReason: token.invalidReason ?? null,
|
|
1265
|
+
inFlight: token.inFlight,
|
|
1266
|
+
cooldownUntil: mapRecord(token.cooldowns, now),
|
|
1267
|
+
quotaBlockedUntil: mapRecord(token.quotaBlocks, now),
|
|
1268
|
+
entitlementBlocks: mapRecord(token.entitlementBlocks, now),
|
|
1269
|
+
lastBilling: billing?.kind === "ok" ? {
|
|
1270
|
+
utilizationPct: billing.utilizationPct,
|
|
1271
|
+
periodType: billing.periodType,
|
|
1272
|
+
periodEnd: billing.periodEnd,
|
|
1273
|
+
fetchedAt: token.billing.fetchedAt
|
|
1274
|
+
} : null,
|
|
1275
|
+
quotaThreshold: token.quotaThreshold,
|
|
1276
|
+
overThreshold: token.overThreshold,
|
|
1277
|
+
onDemandExcluded: settings?.kind === "ok" && settings.onDemandEnabled === true,
|
|
1278
|
+
zdrVideoExcluded: token.zdr === true,
|
|
1279
|
+
lastTier: token.lastTier ?? null,
|
|
1280
|
+
reauthRequired: token.reauthRequired,
|
|
1281
|
+
failoverCount: token.failoverCount
|
|
1282
|
+
};
|
|
1283
|
+
}).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
1284
|
+
}
|
|
1285
|
+
/** Chat-proxy version gate state for the usage endpoint. */
|
|
1286
|
+
versionGateState() {
|
|
1287
|
+
return {
|
|
1288
|
+
tripped: this.versionGate !== void 0,
|
|
1289
|
+
minClientVersion: this.versionGate?.minClientVersion ?? null,
|
|
1290
|
+
trippedAt: this.versionGate?.trippedAt ?? null
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
/** Same shape as OpenAIDispatcher.getQuotaSnapshot; reads caches only. */
|
|
1294
|
+
getQuotaSnapshot() {
|
|
1295
|
+
const now = this.now();
|
|
1296
|
+
return [...this.tokens.values()].map((token) => {
|
|
1297
|
+
const billing = token.billing?.value;
|
|
1298
|
+
const fresh = billing?.kind === "ok" && token.billing.generation === token.generation && now - token.billing.fetchedAt < 6e4;
|
|
1299
|
+
return {
|
|
1300
|
+
name: token.name,
|
|
1301
|
+
usedPercent: fresh ? billing.utilizationPct : null,
|
|
1302
|
+
threshold: token.quotaThreshold,
|
|
1303
|
+
blocked: token.overThreshold || [...token.quotaBlocks.values()].some((until) => until > now),
|
|
1304
|
+
resetsAt: fresh ? billing.periodEndMs : null,
|
|
1305
|
+
zdrVideoExcluded: token.zdr === true
|
|
1306
|
+
};
|
|
1307
|
+
}).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
//#endregion
|
|
1311
|
+
export { GROK_DEFAULT_COOLDOWN_MS, GROK_ENTITLEMENT_BLOCK_MS, GROK_LOOKUP_CACHE_TTL_MS, GROK_MAX_COOLDOWN_MS, GROK_MAX_COOLDOWN_WAIT_MS, GROK_MAX_HOPS, GROK_MAX_REFRESHES, GROK_MAX_STALE_AUTH_RETRIES, GROK_MAX_TRANSIENT_FAILOVERS, GROK_QUOTA_BLOCK_FALLBACK_MS, GROK_REFRESH_FAILED_COOLDOWN_MS, GROK_VERSION_GATE_REPROBE_MS, GrokAttemptBudgetExceededError, GrokCapabilityUnavailableError, GrokCapacityError, GrokCredentialRefreshError, GrokDispatcher, GrokIncompleteResponseError, GrokInvalidRequestError, GrokNoWorkersError, GrokOnDemandExcludedError, GrokRateLimitedError, GrokVersionGateError, GrokZdrVideoUnavailableError, grokConversationIds, grokTicksToUsd, isGrokZdrVideoRejection };
|
|
1312
|
+
|
|
1313
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3Jvay1kaXNwYXRjaGVyLmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy91dGlscy9wcm92aWRlcnMvZ3Jvay9ncm9rLWRpc3BhdGNoZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRlSGFzaCwgcmFuZG9tVVVJRCB9IGZyb20gXCJub2RlOmNyeXB0b1wiO1xuXG5pbXBvcnQgeyBnZXRMb2dnZXIgfSBmcm9tIFwiQGxvZ3RhcGUvbG9ndGFwZVwiO1xuaW1wb3J0IHsgeiB9IGZyb20gXCJ6b2RcIjtcblxuaW1wb3J0IHtcbiAgdHlwZSBHcm9rUHJvdmlkZXJEaXNwYXRjaGVyLFxuICB0eXBlIEdyb2tUb2tlblJvdyxcbn0gZnJvbSBcIi4uLy4uLy4uL2FwcGxpY2F0aW9uL3FncmlkL3FncmlkLmRpc3BhdGNoZXJcIjtcbmltcG9ydCB7IFF1b3RhVGhyZXNob2xkRXhjZWVkZWRFcnJvciB9IGZyb20gXCIuLi8uLi8uLi9hcHBsaWNhdGlvbi9xZ3JpZC9xZ3JpZC50eXBlc1wiO1xuaW1wb3J0IHtcbiAgdHlwZSBHZW5lcmF0ZVJlcXVlc3QsXG4gIHR5cGUgR2VuZXJhdGVSZXN1bHQsXG4gIHR5cGUgR2VuZXJhdGVTdHJlYW1DYWxsYmFja3MsXG4gIHR5cGUgTWVkaWFEaXNwYXRjaGVyLFxuICB0eXBlIE1lZGlhRGlzcGF0Y2hSZXN1bHQsXG4gIHR5cGUgTWVkaWFUb2tlblNlbGVjdGlvbixcbiAgdHlwZSBQcm92aWRlclRvb2xDYWxsLFxuICB0eXBlIFRvb2xEZWZpbml0aW9uLFxuICB0eXBlIFZvaWNlRGlzcGF0Y2hlcixcbiAgdHlwZSBYYWlQcm92aWRlck1ldGFkYXRhLFxufSBmcm9tIFwiLi4vY29tbW9uL3Byb3ZpZGVyLWRpc3BhdGNoZXJcIjtcbmltcG9ydCB7IGlzSnNvbk9iamVjdCwgdHlwZSBKc29uVmFsdWUsIGpzb25TdHJpbmcgfSBmcm9tIFwiLi4vY29tbW9uL3Byb3ZpZGVyLXR5cGVzXCI7XG5pbXBvcnQgeyBTbW9vdGhXZWlnaHRlZFJvdW5kUm9iaW4gfSBmcm9tIFwiLi4vY29tbW9uL3Ntb290aC13ZWlnaHRlZC1yb3VuZC1yb2JpblwiO1xuaW1wb3J0IHsgVGhpbmtpbmdWYWxpZGF0aW9uRXJyb3IgfSBmcm9tIFwiLi4vY29tbW9uL3RoaW5raW5nXCI7XG5pbXBvcnQge1xuICBidWlsZEdyb2tSZXNwb25zZXNSZXF1ZXN0LFxuICB0eXBlIEdyb2tGdW5jdGlvblRvb2wsXG4gIHR5cGUgR3Jva0lucHV0Q29udGVudFBhcnQsXG4gIHR5cGUgR3Jva0lucHV0SXRlbSxcbiAgdHlwZSBHcm9rUmVzcG9uc2VzUmVxdWVzdCxcbiAgdHlwZSBHcm9rVG9vbCxcbiAgdHlwZSBHcm9rVXNhZ2UsXG59IGZyb20gXCIuL2dyb2stYmFja2VuZC1wcm90b2NvbFwiO1xuaW1wb3J0IHsgR1JPS19CVUlMRF9WRVJTSU9OLCBHUk9LX1VTRF9QRVJfVElDSywgdHlwZSBHcm9rUmVhc29uaW5nRWZmb3J0IH0gZnJvbSBcIi4vZ3Jvay1jb25zdGFudHNcIjtcbmltcG9ydCB7IEdyb2tDaGF0Q2xpZW50LCBHcm9rSHR0cEVycm9yLCBHcm9rSWRsZVRpbWVvdXRFcnJvciB9IGZyb20gXCIuL2dyb2stZGlyZWN0LWNsaWVudFwiO1xuaW1wb3J0IHsgYnVpbGRHcm9rQ2hhdEhlYWRlcnMgfSBmcm9tIFwiLi9ncm9rLWhlYWRlcnNcIjtcbmltcG9ydCB7IEdyb2tNb2RlbENhdGFsb2csIHR5cGUgR3Jva1Jlc29sdmVkTW9kZWwgfSBmcm9tIFwiLi9ncm9rLW1vZGVsc1wiO1xuaW1wb3J0IHtcbiAgcmVhZEdyb2tCaWxsaW5nLFxuICByZWFkR3Jva1NldHRpbmdzLFxuICByZWFkR3Jva1N1YnNjcmlwdGlvbixcbiAgdHlwZSBHcm9rUXVvdGFVc2FnZVJlc3VsdCxcbiAgdHlwZSBHcm9rU2V0dGluZ3NSZXN1bHQsXG4gIHR5cGUgR3Jva1N1YnNjcmlwdGlvblJlc3VsdCxcbn0gZnJvbSBcIi4vZ3Jvay1xdW90YVwiO1xuaW1wb3J0IHsgZ3Jva05lZWRzUmVmcmVzaCB9IGZyb20gXCIuL2dyb2stcmVmcmVzaFwiO1xuaW1wb3J0IHtcbiAgR3Jva0NyZWRlbnRpYWxzLFxuICB0eXBlIEdyb2tJbWFnZUVkaXRSZXF1ZXN0LFxuICB0eXBlIEdyb2tJbWFnZUdlbmVyYXRlUmVxdWVzdCxcbiAgdHlwZSBHcm9rSW1hZ2VSZXNwb25zZSxcbiAgdHlwZSBHcm9rTWVkaWFBdXRoLFxuICB0eXBlIEdyb2tNZWRpYVBvcnQsXG4gIHR5cGUgR3Jva1JlZnJlc2hlcixcbiAgdHlwZSBHcm9rU3BlZWNoUmVzdWx0LFxuICB0eXBlIEdyb2tTdHRSZXNwb25zZSxcbiAgdHlwZSBHcm9rVHJhbnNjcmliZVJlcXVlc3QsXG4gIHR5cGUgR3Jva1R0c1JlcXVlc3QsXG4gIHR5cGUgR3Jva1ZpZGVvU3RhcnQsXG4gIHR5cGUgR3Jva1ZpZGVvU3RhcnRSZXF1ZXN0LFxuICB0eXBlIEdyb2tWaWRlb1N0YXR1cyxcbiAgdHlwZSBHcm9rVm9pY2VQb3J0LFxufSBmcm9tIFwiLi9ncm9rLXR5cGVzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGdldExvZ2dlcihbXCJxZ3JpZFwiLCBcImdyb2stZGlzcGF0Y2hlclwiXSk7XG5cbmV4cG9ydCB0eXBlIEdyb2tSZXF1ZXN0Q2xhc3MgPSBcImNoYXRcIiB8IFwiaW1hZ2VcIiB8IFwidmlkZW9cIiB8IFwic3R0XCIgfCBcInR0c1wiO1xuXG5leHBvcnQgY29uc3QgR1JPS19MT09LVVBfQ0FDSEVfVFRMX01TID0gNjBfMDAwO1xuZXhwb3J0IGNvbnN0IEdST0tfTUFYX0NPT0xET1dOX01TID0gMTIwXzAwMDtcbmV4cG9ydCBjb25zdCBHUk9LX0RFRkFVTFRfQ09PTERPV05fTVMgPSAxMF8wMDA7XG5leHBvcnQgY29uc3QgR1JPS19NQVhfQ09PTERPV05fV0FJVF9NUyA9IDMwXzAwMDtcbmV4cG9ydCBjb25zdCBHUk9LX1FVT1RBX0JMT0NLX0ZBTExCQUNLX01TID0gM182MDBfMDAwO1xuZXhwb3J0IGNvbnN0IEdST0tfRU5USVRMRU1FTlRfQkxPQ0tfTVMgPSAzXzYwMF8wMDA7XG5leHBvcnQgY29uc3QgR1JPS19WRVJTSU9OX0dBVEVfUkVQUk9CRV9NUyA9IDYwMF8wMDA7XG5leHBvcnQgY29uc3QgR1JPS19NQVhfSE9QUyA9IDI7XG5leHBvcnQgY29uc3QgR1JPS19NQVhfUkVGUkVTSEVTID0gMTtcbmV4cG9ydCBjb25zdCBHUk9LX01BWF9UUkFOU0lFTlRfRkFJTE9WRVJTID0gMTtcblxuLyoqIEEgdG9rZW4gd2hvc2UgcHJlLWxvb2t1cCByZWZyZXNoIGZhaWxlZCB0cmFuc2llbnRseSBzaXRzIG91dCBzZWxlY3Rpb24gdGhpcyBsb25nICjCpzUuMSkuICovXG5leHBvcnQgY29uc3QgR1JPS19SRUZSRVNIX0ZBSUxFRF9DT09MRE9XTl9NUyA9IDVfMDAwO1xuXG4vKiogUmV0cmllcyB3aXRoIHRoZSB0b2tlbidzIGN1cnJlbnQgY3JlZGVudGlhbHMgYWZ0ZXIgYSA0MDEgdGhhdCBhIG5ld2VyIGJlYXJlciBzdXBlcnNlZGVkLiAqL1xuZXhwb3J0IGNvbnN0IEdST0tfTUFYX1NUQUxFX0FVVEhfUkVUUklFUyA9IDE7XG5jb25zdCBERUZBVUxUX1JFUVVFU1RfVElNRU9VVF9NUyA9IDYwMF8wMDA7XG5jb25zdCBERUZBVUxUX0lETEVfVElNRU9VVF9NUyA9IDMwMF8wMDA7XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gRXJyb3JzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuZXhwb3J0IGNsYXNzIEdyb2tJbnZhbGlkUmVxdWVzdEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBjb2RlID0gXCJHUk9LX0lOVkFMSURfUkVRVUVTVFwiIGFzIGNvbnN0O1xuXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZykge1xuICAgIHN1cGVyKG1lc3NhZ2UpO1xuICAgIHRoaXMubmFtZSA9IFwiR3Jva0ludmFsaWRSZXF1ZXN0RXJyb3JcIjtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgR3Jva1JhdGVMaW1pdGVkRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIHJlYWRvbmx5IGNvZGUgPSBcIkdST0tfUkFURV9MSU1JVEVEXCIgYXMgY29uc3Q7XG4gIHJlYWRvbmx5IHJldHJ5QWZ0ZXJTZWM6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihyZXRyeUFmdGVyU2VjOiBudW1iZXIsIG1lc3NhZ2U/OiBzdHJpbmcpIHtcbiAgICBzdXBlcihtZXNzYWdlID8/IGBBbGwgZ3JvayB0b2tlbnMgYXJlIHJhdGUgbGltaXRlZDsgcmV0cnkgYWZ0ZXIgJHtyZXRyeUFmdGVyU2VjfXNgKTtcbiAgICB0aGlzLm5hbWUgPSBcIkdyb2tSYXRlTGltaXRlZEVycm9yXCI7XG4gICAgdGhpcy5yZXRyeUFmdGVyU2VjID0gcmV0cnlBZnRlclNlYztcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgR3Jva0NhcGFjaXR5RXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIHJlYWRvbmx5IGNvZGUgPSBcIkdST0tfQ0FQQUNJVFlcIiBhcyBjb25zdDtcblxuICBjb25zdHJ1Y3RvcihtZXNzYWdlID0gXCJBbGwgZ3JvayB0b2tlbnMgYXJlIGF0IHRoZWlyIGluLWZsaWdodCByZXF1ZXN0IGNhcFwiKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gXCJHcm9rQ2FwYWNpdHlFcnJvclwiO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBHcm9rT25EZW1hbmRFeGNsdWRlZEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBjb2RlID0gXCJHUk9LX09OX0RFTUFORF9FWENMVURFRFwiIGFzIGNvbnN0O1xuICByZWFkb25seSB0b2tlbk5hbWVzOiBzdHJpbmdbXTtcblxuICBjb25zdHJ1Y3Rvcih0b2tlbk5hbWVzOiBzdHJpbmdbXSkge1xuICAgIHN1cGVyKFxuICAgICAgYEFsbCBncm9rIHRva2VucyBhcmUgZXhjbHVkZWQgYmVjYXVzZSBvbi1kZW1hbmQgYmlsbGluZyBpcyBlbmFibGVkOiAke3Rva2VuTmFtZXMuam9pbihcIiwgXCIpfWAsXG4gICAgKTtcbiAgICB0aGlzLm5hbWUgPSBcIkdyb2tPbkRlbWFuZEV4Y2x1ZGVkRXJyb3JcIjtcbiAgICB0aGlzLnRva2VuTmFtZXMgPSB0b2tlbk5hbWVzO1xuICB9XG59XG5cbi8qKlxuICogeEFJIHJlZnVzZXMgdmlkZW8gZ2VuZXJhdGlvbiBmb3IgWmVybyBEYXRhIFJldGVudGlvbiBhY2NvdW50cyB1bmxlc3MgdGhlIGNhbGxlciBzdXBwbGllcyBpdHMgb3duXG4gKiBgb3V0cHV0LnVwbG9hZF91cmxgIChwbGFuIMKnMTEpLCB3aGljaCBxZ3JpZCBkb2VzIG5vdCBpbXBsZW1lbnQuXG4gKi9cbmV4cG9ydCBjbGFzcyBHcm9rWmRyVmlkZW9VbmF2YWlsYWJsZUVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBjb2RlID0gXCJHUk9LX1pEUl9WSURFT19VTkFWQUlMQUJMRVwiIGFzIGNvbnN0O1xuICByZWFkb25seSB0b2tlbk5hbWVzOiBzdHJpbmdbXTtcblxuICBjb25zdHJ1Y3Rvcih0b2tlbk5hbWVzOiBzdHJpbmdbXSkge1xuICAgIHN1cGVyKFxuICAgICAgYEdyb2sgdmlkZW8gaXMgdW5hdmFpbGFibGUgYmVjYXVzZSBaZXJvIERhdGEgUmV0ZW50aW9uIGlzIG9uIGZvcjogJHt0b2tlbk5hbWVzLmpvaW4oXCIsIFwiKX0uIFR1cm4gb2ZmIHRoZSBkYXRhLXJldGVudGlvbiBvcHQtb3V0IGF0IHhBSSwgb3IgdXNlIGEgbm9uLVpEUiBhY2NvdW50LmAsXG4gICAgKTtcbiAgICB0aGlzLm5hbWUgPSBcIkdyb2taZHJWaWRlb1VuYXZhaWxhYmxlRXJyb3JcIjtcbiAgICB0aGlzLnRva2VuTmFtZXMgPSB0b2tlbk5hbWVzO1xuICB9XG59XG5cbi8qKiBUaGUgdXBzdHJlYW0gNDAwIGJvZHkgZm9yIGEgWkRSIGFjY291bnQgb24gUE9TVCAvdjEvdmlkZW9zL2dlbmVyYXRpb25zLiAqL1xuY29uc3QgWkRSX1ZJREVPX1JFSkVDVElPTiA9IC96ZXJvIGRhdGEgcmV0ZW50aW9uL2k7XG5cbmV4cG9ydCBmdW5jdGlvbiBpc0dyb2taZHJWaWRlb1JlamVjdGlvbihlcnJvcjogR3Jva0h0dHBFcnJvcik6IGJvb2xlYW4ge1xuICByZXR1cm4gKFxuICAgIGVycm9yLnN0YXR1cyA9PT0gNDAwICYmXG4gICAgKFpEUl9WSURFT19SRUpFQ1RJT04udGVzdChlcnJvci5jbGFzc2lmaWNhdGlvbi5tZXNzYWdlID8/IFwiXCIpIHx8XG4gICAgICBaRFJfVklERU9fUkVKRUNUSU9OLnRlc3QoZXJyb3IuYm9keVRleHQpKVxuICApO1xufVxuXG5leHBvcnQgY2xhc3MgR3Jva1ZlcnNpb25HYXRlRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIHJlYWRvbmx5IGNvZGUgPSBcIkdST0tfVkVSU0lPTl9HQVRFXCIgYXMgY29uc3Q7XG4gIHJlYWRvbmx5IG1pbkNsaWVudFZlcnNpb246IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICBjb25zdHJ1Y3RvcihtaW5DbGllbnRWZXJzaW9uOiBzdHJpbmcgfCB1bmRlZmluZWQpIHtcbiAgICBzdXBlcihcbiAgICAgIGBHcm9rIGNoYXQgcHJveHkgcmVqZWN0ZWQgY2xpZW50IHZlcnNpb24gJHtHUk9LX0JVSUxEX1ZFUlNJT059IChIVFRQIDQyNjsgcmVxdWlyZXMgJHtcbiAgICAgICAgbWluQ2xpZW50VmVyc2lvbiA/PyBcImEgbmV3ZXIgdmVyc2lvblwiXG4gICAgICB9IG9yIGxhdGVyKS4gQnVtcCBHUk9LX0JVSUxEX1ZFUlNJT04gaW4gZ3Jvay1jb25zdGFudHMudHMgYW5kIHJlLWNhcHR1cmUgdGhlIGdvbGRlbiBmaXh0dXJlLmAsXG4gICAgKTtcbiAgICB0aGlzLm5hbWUgPSBcIkdyb2tWZXJzaW9uR2F0ZUVycm9yXCI7XG4gICAgdGhpcy5taW5DbGllbnRWZXJzaW9uID0gbWluQ2xpZW50VmVyc2lvbjtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgR3Jva0F0dGVtcHRCdWRnZXRFeGNlZWRlZEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBjb2RlID0gXCJHUk9LX0FUVEVNUFRfQlVER0VUX0VYQ0VFREVEXCIgYXMgY29uc3Q7XG5cbiAgY29uc3RydWN0b3IoYXR0ZW1wdHM6IG51bWJlciwgY2F1c2U6IHVua25vd24pIHtcbiAgICBzdXBlcihcbiAgICAgIGBHcm9rIHJlcXVlc3QgZmFpbGVkIGFmdGVyICR7YXR0ZW1wdHN9IGF0dGVtcHRzOiAke1xuICAgICAgICBjYXVzZSBpbnN0YW5jZW9mIEVycm9yID8gY2F1c2UubWVzc2FnZSA6IFN0cmluZyhjYXVzZSlcbiAgICAgIH1gLFxuICAgICAgeyBjYXVzZSB9LFxuICAgICk7XG4gICAgdGhpcy5uYW1lID0gXCJHcm9rQXR0ZW1wdEJ1ZGdldEV4Y2VlZGVkRXJyb3JcIjtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgR3Jva05vV29ya2Vyc0Vycm9yIGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBjb2RlID0gXCJOT19HUk9LX1dPUktFUlNcIiBhcyBjb25zdDtcblxuICBjb25zdHJ1Y3RvcihtZXNzYWdlID0gXCJOT19HUk9LX1dPUktFUlNcIikge1xuICAgIHN1cGVyKG1lc3NhZ2UpO1xuICAgIHRoaXMubmFtZSA9IFwiR3Jva05vV29ya2Vyc0Vycm9yXCI7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEdyb2tDYXBhYmlsaXR5VW5hdmFpbGFibGVFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgcmVhZG9ubHkgY29kZSA9IFwiR1JPS19DQVBBQklMSVRZX1VOQVZBSUxBQkxFXCIgYXMgY29uc3Q7XG5cbiAgY29uc3RydWN0b3IoY2FwYWJpbGl0eTogc3RyaW5nKSB7XG4gICAgc3VwZXIoYEdyb2sgJHtjYXBhYmlsaXR5fSBpcyBub3QgY29uZmlndXJlZCBvbiB0aGlzIHNlcnZlcmApO1xuICAgIHRoaXMubmFtZSA9IFwiR3Jva0NhcGFiaWxpdHlVbmF2YWlsYWJsZUVycm9yXCI7XG4gIH1cbn1cblxuLyoqIFRoZSByZWZyZXNoZXIgY291bGQgbm90IHByb2R1Y2UgdXNhYmxlIGNyZWRlbnRpYWxzIGZvciBhIHRva2VuIGJlZm9yZSB0aGUgcmVxdWVzdCB3YXMgc2VudC4gKi9cbmV4cG9ydCBjbGFzcyBHcm9rQ3JlZGVudGlhbFJlZnJlc2hFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgcmVhZG9ubHkgY29kZSA9IFwiR1JPS19DUkVERU5USUFMX1JFRlJFU0hcIiBhcyBjb25zdDtcbiAgcmVhZG9ubHkgcmVhdXRoUmVxdWlyZWQ6IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIGByZWF1dGhSZXF1aXJlZGAgZGVmYXVsdHMgdG8gdGhlIGNhdXNlJ3MgY29udmVudGlvbjogdGhlIHBvcnQgc2lnbmFscyBhIGRlYWQgcmVmcmVzaCB0b2tlbiB3aXRoXG4gICAqIEdyb2tSZWF1dGhSZXF1aXJlZEVycm9yIChncm9rLXJlZnJlc2gudHMpLCBjaGVja2VkIGJ5IG5hbWUgc28gYW55IEdyb2tSZWZyZXNoZXIgaW1wbGVtZW50YXRpb25cbiAgICogY2FuIHVzZSBpdC5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIGNhdXNlOiB1bmtub3duLFxuICAgIHJlYXV0aFJlcXVpcmVkID0gY2F1c2UgaW5zdGFuY2VvZiBFcnJvciAmJiBjYXVzZS5uYW1lID09PSBcIkdyb2tSZWF1dGhSZXF1aXJlZEVycm9yXCIsXG4gICkge1xuICAgIHN1cGVyKFxuICAgICAgYEdyb2sgY3JlZGVudGlhbCByZWZyZXNoIGZhaWxlZDogJHtjYXVzZSBpbnN0YW5jZW9mIEVycm9yID8gY2F1c2UubWVzc2FnZSA6IFN0cmluZyhjYXVzZSl9YCxcbiAgICAgIHsgY2F1c2UgfSxcbiAgICApO1xuICAgIHRoaXMubmFtZSA9IFwiR3Jva0NyZWRlbnRpYWxSZWZyZXNoRXJyb3JcIjtcbiAgICB0aGlzLnJlYXV0aFJlcXVpcmVkID0gcmVhdXRoUmVxdWlyZWQ7XG4gIH1cblxuICAvKiogVGhlIHJlZnJlc2hlciBhbnN3ZXJlZCBgcmVhdXRoX3JlcXVpcmVkYDogdGhlIHJlZnJlc2ggdG9rZW4gaXMgZGVhZC4gKi9cbiAgc3RhdGljIHJlYXV0aFJlcXVpcmVkKHJlYXNvbjogc3RyaW5nKTogR3Jva0NyZWRlbnRpYWxSZWZyZXNoRXJyb3Ige1xuICAgIHJldHVybiBuZXcgR3Jva0NyZWRlbnRpYWxSZWZyZXNoRXJyb3IocmVhc29uLCB0cnVlKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgR3Jva0luY29tcGxldGVSZXNwb25zZUVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBjb2RlID0gXCJHUk9LX0lOQ09NUExFVEVfUkVTUE9OU0VcIiBhcyBjb25zdDtcbiAgcmVhZG9ubHkgcmVhc29uOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgY29uc3RydWN0b3IocmVhc29uOiBzdHJpbmcgfCB1bmRlZmluZWQpIHtcbiAgICBzdXBlcihgR3JvayByZXNwb25zZSBpbmNvbXBsZXRlJHtyZWFzb24gPyBgOiAke3JlYXNvbn1gIDogXCJcIn1gKTtcbiAgICB0aGlzLm5hbWUgPSBcIkdyb2tJbmNvbXBsZXRlUmVzcG9uc2VFcnJvclwiO1xuICAgIHRoaXMucmVhc29uID0gcmVhc29uO1xuICB9XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gVG9rZW4gc3RhdGUgKMKnNS4xKVxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbnR5cGUgQ2FjaGVkPFQ+ID0geyB2YWx1ZTogVDsgZmV0Y2hlZEF0OiBudW1iZXI7IGdlbmVyYXRpb246IG51bWJlciB9O1xuXG50eXBlIEluZWxpZ2libGVSZWFzb24gPVxuICB8IFwiaW5hY3RpdmVcIlxuICB8IFwicmVhdXRoXCJcbiAgfCBcInJlZnJlc2hfZmFpbGVkXCJcbiAgfCBcIm9uX2RlbWFuZFwiXG4gIHwgXCJ6ZHJcIlxuICB8IFwiZW50aXRsZW1lbnRcIlxuICB8IFwicXVvdGFcIlxuICB8IFwiY29vbGRvd25cIlxuICB8IFwiY2FwYWNpdHlcIjtcblxuZXhwb3J0IGludGVyZmFjZSBHcm9rVG9rZW5TdGF0ZSB7XG4gIGlkOiBudW1iZXI7XG4gIG5hbWU6IHN0cmluZztcbiAgd2VpZ2h0OiBudW1iZXI7XG4gIHF1b3RhVGhyZXNob2xkOiBudW1iZXIgfCBudWxsO1xuICBjcmVkZW50aWFsczogR3Jva0NyZWRlbnRpYWxzIHwgdW5kZWZpbmVkO1xuICBpbnZhbGlkUmVhc29uOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGdlbmVyYXRpb246IG51bWJlcjtcbiAgaW5GbGlnaHQ6IG51bWJlcjtcbiAgY29vbGRvd25zOiBNYXA8R3Jva1JlcXVlc3RDbGFzcywgbnVtYmVyPjtcbiAgcXVvdGFCbG9ja3M6IE1hcDxHcm9rUmVxdWVzdENsYXNzLCBudW1iZXI+O1xuICBlbnRpdGxlbWVudEJsb2NrczogTWFwPEdyb2tSZXF1ZXN0Q2xhc3MsIG51bWJlcj47XG4gIHJlYXV0aFJlcXVpcmVkOiBib29sZWFuO1xuICAvKipcbiAgICogVW50aWwgd2hlbiB0aGUgdG9rZW4gaXMgb3V0IG9mIHNlbGVjdGlvbiBhZnRlciBhIHRyYW5zaWVudCBwcmUtbG9va3VwIHJlZnJlc2ggZmFpbHVyZTogaXRzXG4gICAqIGdhdGluZyBsb29rdXBzIGNvdWxkIG5vdCBydW4gd2l0aCBhIHZhbGlkIGJlYXJlciwgc28gdGhlIGdhdGVzIGFyZSB1bmtub3duLlxuICAgKi9cbiAgcmVmcmVzaEZhaWxlZFVudGlsOiBudW1iZXI7XG4gIHJlZnJlc2hGYWlsdXJlOiB1bmtub3duO1xuICBiaWxsaW5nOiBDYWNoZWQ8R3Jva1F1b3RhVXNhZ2VSZXN1bHQ+IHwgdW5kZWZpbmVkO1xuICBiaWxsaW5nTm90QmVmb3JlOiBudW1iZXI7XG4gIHNldHRpbmdzOiBDYWNoZWQ8R3Jva1NldHRpbmdzUmVzdWx0PiB8IHVuZGVmaW5lZDtcbiAgc3Vic2NyaXB0aW9uOiBDYWNoZWQ8R3Jva1N1YnNjcmlwdGlvblJlc3VsdD4gfCB1bmRlZmluZWQ7XG4gIC8qKlxuICAgKiBaZXJvIERhdGEgUmV0ZW50aW9uIChwbGFuIMKnMTEpOiB0cnVlIGV4Y2x1ZGVzIHRoZSB0b2tlbiBmcm9tIHZpZGVvLiBMZWFybmVkIGZyb21cbiAgICogL3VzZXIgY29kaW5nRGF0YVJldGVudGlvbk9wdE91dCwgY29uZmlybWVkIGJ5IGB4LXplcm8tZGF0YS1yZXRlbnRpb246IHRydWVgIG9yIHRoZSB1cHN0cmVhbVxuICAgKiBaRFIgNDAwOyBudWxsIGlzIHVua25vd24gYW5kIGVsaWdpYmxlLlxuICAgKi9cbiAgemRyOiBib29sZWFuIHwgbnVsbDtcbiAgb3ZlclRocmVzaG9sZDogYm9vbGVhbjtcbiAgbGFzdFRpZXI6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgZmFpbG92ZXJDb3VudDogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEdyb2tUb2tlblN0YXRlU25hcHNob3Qge1xuICBpZDogbnVtYmVyO1xuICBuYW1lOiBzdHJpbmc7XG4gIHdlaWdodDogbnVtYmVyO1xuICBhY3RpdmU6IGJvb2xlYW47XG4gIGludmFsaWRSZWFzb246IHN0cmluZyB8IG51bGw7XG4gIGluRmxpZ2h0OiBudW1iZXI7XG4gIGNvb2xkb3duVW50aWw6IFBhcnRpYWw8UmVjb3JkPEdyb2tSZXF1ZXN0Q2xhc3MsIG51bWJlcj4+O1xuICBxdW90YUJsb2NrZWRVbnRpbDogUGFydGlhbDxSZWNvcmQ8R3Jva1JlcXVlc3RDbGFzcywgbnVtYmVyPj47XG4gIGVudGl0bGVtZW50QmxvY2tzOiBQYXJ0aWFsPFJlY29yZDxHcm9rUmVxdWVzdENsYXNzLCBudW1iZXI+PjtcbiAgbGFzdEJpbGxpbmc6IHtcbiAgICB1dGlsaXphdGlvblBjdDogbnVtYmVyO1xuICAgIHBlcmlvZFR5cGU6IHN0cmluZztcbiAgICBwZXJpb2RFbmQ6IHN0cmluZztcbiAgICBmZXRjaGVkQXQ6IG51bWJlcjtcbiAgfSB8IG51bGw7XG4gIHF1b3RhVGhyZXNob2xkOiBudW1iZXIgfCBudWxsO1xuICBvdmVyVGhyZXNob2xkOiBib29sZWFuO1xuICBvbkRlbWFuZEV4Y2x1ZGVkOiBib29sZWFuO1xuICB6ZHJWaWRlb0V4Y2x1ZGVkOiBib29sZWFuO1xuICBsYXN0VGllcjogc3RyaW5nIHwgbnVsbDtcbiAgcmVhdXRoUmVxdWlyZWQ6IGJvb2xlYW47XG4gIGZhaWxvdmVyQ291bnQ6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFJlc2VydmF0aW9uIHtcbiAgdG9rZW46IEdyb2tUb2tlblN0YXRlO1xuICByZWxlYXNlKCk6IHZvaWQ7XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gUmVxdWVzdCBtYXBwaW5nXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuY29uc3QgQ29udGVudFBhcnQgPSB6LmRpc2NyaW1pbmF0ZWRVbmlvbihcInR5cGVcIiwgW1xuICB6Lm9iamVjdCh7IHR5cGU6IHoubGl0ZXJhbChcImlucHV0X3RleHRcIiksIHRleHQ6IHouc3RyaW5nKCkgfSksXG4gIHoub2JqZWN0KHsgdHlwZTogei5saXRlcmFsKFwiaW5wdXRfaW1hZ2VcIiksIGltYWdlX3VybDogei5zdHJpbmcoKS5taW4oMSkgfSksXG4gIHoub2JqZWN0KHsgdHlwZTogei5saXRlcmFsKFwib3V0cHV0X3RleHRcIiksIHRleHQ6IHouc3RyaW5nKCkgfSksXG5dKTtcblxuY29uc3QgSGlzdG9yeUl0ZW0gPSB6LnVuaW9uKFtcbiAgei5vYmplY3Qoe1xuICAgIHR5cGU6IHoubGl0ZXJhbChcIm1lc3NhZ2VcIikub3B0aW9uYWwoKSxcbiAgICByb2xlOiB6LmVudW0oW1widXNlclwiLCBcImFzc2lzdGFudFwiXSksXG4gICAgY29udGVudDogei51bmlvbihbei5zdHJpbmcoKSwgei5hcnJheShDb250ZW50UGFydCldKSxcbiAgfSksXG4gIHoub2JqZWN0KHtcbiAgICB0eXBlOiB6LmxpdGVyYWwoXCJmdW5jdGlvbl9jYWxsXCIpLFxuICAgIGNhbGxfaWQ6IHouc3RyaW5nKCkubWluKDEpLFxuICAgIG5hbWU6IHouc3RyaW5nKCkubWluKDEpLFxuICAgIGFyZ3VtZW50czogei5zdHJpbmcoKSxcbiAgICBpZDogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICB9KSxcbiAgei5vYmplY3Qoe1xuICAgIHR5cGU6IHoubGl0ZXJhbChcImZ1bmN0aW9uX2NhbGxfb3V0cHV0XCIpLFxuICAgIGNhbGxfaWQ6IHouc3RyaW5nKCkubWluKDEpLFxuICAgIG91dHB1dDogei5zdHJpbmcoKSxcbiAgfSksXG4gIHoub2JqZWN0KHtcbiAgICB0eXBlOiB6LmxpdGVyYWwoXCJyZWFzb25pbmdcIiksXG4gICAgaWQ6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICBzdW1tYXJ5OiB6LmFycmF5KHoub2JqZWN0KHsgdHlwZTogei5saXRlcmFsKFwic3VtbWFyeV90ZXh0XCIpLCB0ZXh0OiB6LnN0cmluZygpIH0pKS5vcHRpb25hbCgpLFxuICAgIGVuY3J5cHRlZF9jb250ZW50OiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIH0pLFxuXSk7XG5cbmZ1bmN0aW9uIGhpc3RvcnlJdGVtcyhoaXN0b3J5OiByZWFkb25seSBKc29uVmFsdWVbXSB8IHVuZGVmaW5lZCk6IEdyb2tJbnB1dEl0ZW1bXSB7XG4gIHJldHVybiAoaGlzdG9yeSA/PyBbXSkubWFwKChyYXcsIGluZGV4KSA9PiB7XG4gICAgY29uc3QgcGFyc2VkID0gSGlzdG9yeUl0ZW0uc2FmZVBhcnNlKHJhdyk7XG5cbiAgICBpZiAoIXBhcnNlZC5zdWNjZXNzKSB7XG4gICAgICB0aHJvdyBuZXcgR3Jva0ludmFsaWRSZXF1ZXN0RXJyb3IoYFVuc3VwcG9ydGVkIEdyb2sgaGlzdG9yeSBpdGVtIGF0IGluZGV4ICR7aW5kZXh9YCk7XG4gICAgfVxuXG4gICAgY29uc3QgaXRlbSA9IHBhcnNlZC5kYXRhO1xuXG4gICAgaWYgKFwicm9sZVwiIGluIGl0ZW0pIHJldHVybiB7IHR5cGU6IFwibWVzc2FnZVwiLCByb2xlOiBpdGVtLnJvbGUsIGNvbnRlbnQ6IGl0ZW0uY29udGVudCB9O1xuXG4gICAgcmV0dXJuIGl0ZW07XG4gIH0pO1xufVxuXG5mdW5jdGlvbiB1c2VyTWVzc2FnZShyZXE6IEdlbmVyYXRlUmVxdWVzdCk6IEdyb2tJbnB1dEl0ZW0ge1xuICBjb25zdCBjb250ZW50OiBHcm9rSW5wdXRDb250ZW50UGFydFtdID0gcmVxLmNvbGRJbnB1dC5tYXAoKGlucHV0KSA9PiB7XG4gICAgaWYgKGlucHV0LnR5cGUgPT09IFwidGV4dFwiKSByZXR1cm4geyB0eXBlOiBcImlucHV0X3RleHRcIiwgdGV4dDogaW5wdXQudGV4dCB9O1xuXG4gICAgaWYgKGlucHV0LnR5cGUgPT09IFwiaW1hZ2VcIikgcmV0dXJuIHsgdHlwZTogXCJpbnB1dF9pbWFnZVwiLCBpbWFnZV91cmw6IGlucHV0LnVybCB9O1xuXG4gICAgaWYgKGlucHV0LnR5cGUgPT09IFwibG9jYWxJbWFnZVwiKSB7XG4gICAgICB0aHJvdyBuZXcgR3Jva0ludmFsaWRSZXF1ZXN0RXJyb3IoXCJHcm9rIGRvZXMgbm90IGFjY2VwdCBsb2NhbCBpbWFnZSBwYXRoczsgc2VuZCBhIGRhdGEgVVJMXCIpO1xuICAgIH1cblxuICAgIHJldHVybiB7IHR5cGU6IFwiaW5wdXRfdGV4dFwiLCB0ZXh0OiBgJHtpbnB1dC5uYW1lfTogJHtpbnB1dC5wYXRofWAgfTtcbiAgfSk7XG5cbiAgcmV0dXJuIHsgdHlwZTogXCJtZXNzYWdlXCIsIHJvbGU6IFwidXNlclwiLCBjb250ZW50IH07XG59XG5cbmZ1bmN0aW9uIGdyb2tUb29scyh0b29sczogcmVhZG9ubHkgVG9vbERlZmluaXRpb25bXSB8IHVuZGVmaW5lZCk6IEdyb2tUb29sW10gfCB1bmRlZmluZWQge1xuICBpZiAoIXRvb2xzPy5sZW5ndGgpIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgcmV0dXJuIHRvb2xzLm1hcCgodG9vbCk6IEdyb2tUb29sID0+IHtcbiAgICBpZiAodG9vbC50eXBlICE9PSBcImZ1bmN0aW9uXCIpIHJldHVybiB7IHR5cGU6IHRvb2wudHlwZSB9O1xuXG4gICAgY29uc3QgZm46IEdyb2tGdW5jdGlvblRvb2wgPSB7XG4gICAgICB0eXBlOiBcImZ1bmN0aW9uXCIsXG4gICAgICBuYW1lOiB0b29sLm5hbWUsXG4gICAgICBwYXJhbWV0ZXJzOiB0b29sLnBhcmFtZXRlcnMsXG4gICAgICBzdHJpY3Q6IHRvb2wuc3RyaWN0ID8/IGZhbHNlLFxuICAgIH07XG5cbiAgICBpZiAodG9vbC5kZXNjcmlwdGlvbiAhPT0gdW5kZWZpbmVkKSBmbi5kZXNjcmlwdGlvbiA9IHRvb2wuZGVzY3JpcHRpb247XG5cbiAgICByZXR1cm4gZm47XG4gIH0pO1xufVxuXG4vKiogRGV0ZXJtaW5pc3RpYyBVVUlELXNoYXBlZCBpZCwgc28gb25lIHFncmlkIGNvbnZlcnNhdGlvbiBtYXBzIHRvIG9uZSBHcm9rIGNvbnZlcnNhdGlvbi4gKi9cbmZ1bmN0aW9uIGRlcml2ZWRHdWlkKG5hbWVzcGFjZTogc3RyaW5nLCBrZXk6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IGhleCA9IGNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKGAke25hbWVzcGFjZX1cXDAke2tleX1gKS5kaWdlc3QoXCJoZXhcIik7XG4gIGNvbnN0IHZhcmlhbnQgPSAoKE51bWJlci5wYXJzZUludChoZXhbMTZdISwgMTYpICYgMHgzKSB8IDB4OCkudG9TdHJpbmcoMTYpO1xuXG4gIHJldHVybiBgJHtoZXguc2xpY2UoMCwgOCl9LSR7aGV4LnNsaWNlKDgsIDEyKX0tNCR7aGV4LnNsaWNlKDEzLCAxNil9LSR7dmFyaWFudH0ke2hleC5zbGljZSgxNywgMjApfS0ke2hleC5zbGljZSgyMCwgMzIpfWA7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgR3Jva0NvbnZlcnNhdGlvbklkcyB7XG4gIGNvbnZJZDogc3RyaW5nO1xuICBjb252R3JvdXBJZDogc3RyaW5nO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ3Jva0NvbnZlcnNhdGlvbklkcyhwcm9tcHRDYWNoZUtleTogc3RyaW5nIHwgdW5kZWZpbmVkKTogR3Jva0NvbnZlcnNhdGlvbklkcyB7XG4gIGNvbnN0IGNvbnZJZCA9IHByb21wdENhY2hlS2V5ID8gZGVyaXZlZEd1aWQoXCJncm9rLWNvbnZcIiwgcHJvbXB0Q2FjaGVLZXkpIDogcmFuZG9tVVVJRCgpO1xuXG4gIHJldHVybiB7IGNvbnZJZCwgY29udkdyb3VwSWQ6IGRlcml2ZWRHdWlkKFwiZ3Jvay1jb252LWdyb3VwXCIsIGNvbnZJZCkgfTtcbn1cblxuLyoqIFVTRCBmcm9tIGNvc3RfaW5fdXNkX3RpY2tzLCByb3VuZGVkIHRvIHRoZSB0aWNrIHJlc29sdXRpb24gKDFlLTEwKS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBncm9rVGlja3NUb1VzZCh0aWNrczogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIE51bWJlcigodGlja3MgKiBHUk9LX1VTRF9QRVJfVElDSykudG9GaXhlZCgxMCkpO1xufVxuXG5mdW5jdGlvbiBwYXJzZU1heEluZmxpZ2h0KHZhbHVlOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCB2YWx1ZS50cmltKCkgPT09IFwiXCIpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IHBhcnNlZCA9IE51bWJlcih2YWx1ZSk7XG5cbiAgaWYgKCFOdW1iZXIuaXNTYWZlSW50ZWdlcihwYXJzZWQpIHx8IHBhcnNlZCA8IDEpIHtcbiAgICBsb2dnZXIud2FybihgaWdub3JpbmcgUUdSSURfR1JPS19NQVhfSU5GTElHSFRfUEVSX1RPS0VOPSR7dmFsdWV9OiBub3QgYSBwb3NpdGl2ZSBpbnRlZ2VyYCk7XG5cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgcmV0dXJuIHBhcnNlZDtcbn1cblxuZnVuY3Rpb24gYWJvcnRSZWFzb24oc2lnbmFsOiBBYm9ydFNpZ25hbCk6IEVycm9yIHtcbiAgY29uc3QgcmVhc29uOiB1bmtub3duID0gc2lnbmFsLnJlYXNvbjtcblxuICByZXR1cm4gcmVhc29uIGluc3RhbmNlb2YgRXJyb3IgPyByZWFzb24gOiBuZXcgRE9NRXhjZXB0aW9uKFwiQWJvcnRlZFwiLCBcIkFib3J0RXJyb3JcIik7XG59XG5cbi8qKiBJbWFnaW5lIGNhbGxzIGNhcnJ5IHRoZSB0b2tlbidzIHNlc3Npb24gaWQgbGlrZSBncm9rLWJ1aWxkJ3MgcGVyLXJlcXVlc3Qgc2Vzc2lvbiBoZWFkZXIuICovXG5mdW5jdGlvbiBtZWRpYUF1dGgoY3JlZGVudGlhbHM6IEdyb2tDcmVkZW50aWFscyk6IEdyb2tNZWRpYUF1dGgge1xuICByZXR1cm4geyBiZWFyZXI6IGNyZWRlbnRpYWxzLmFjY2Vzc1Rva2VuLCBzZXNzaW9uSWQ6IGNyZWRlbnRpYWxzLnNlc3Npb25JZCB9O1xufVxuXG5mdW5jdGlvbiBkZWZhdWx0U2xlZXAobXM6IG51bWJlciwgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBpZiAoc2lnbmFsPy5hYm9ydGVkKSB7XG4gICAgICByZWplY3QoYWJvcnRSZWFzb24oc2lnbmFsKSk7XG5cbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBvbkFib3J0ID0gKCkgPT4ge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgIHJlamVjdChhYm9ydFJlYXNvbihzaWduYWwhKSk7XG4gICAgfTtcblxuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBzaWduYWw/LnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KTtcbiAgICAgIHJlc29sdmUoKTtcbiAgICB9LCBtcyk7XG5cbiAgICBzaWduYWw/LmFkZEV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0LCB7IG9uY2U6IHRydWUgfSk7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBxdW90YU1lc3NhZ2UoZW50cmllczogQXJyYXk8eyBuYW1lOiBzdHJpbmc7IHRocmVzaG9sZDogbnVtYmVyIHwgbnVsbCB9Pik6IHN0cmluZyB7XG4gIGNvbnN0IGRldGFpbCA9IGVudHJpZXNcbiAgICAubWFwKChlKSA9PlxuICAgICAgZS50aHJlc2hvbGQgPT09IG51bGwgPyBgJHtlLm5hbWV9IChleGhhdXN0ZWQpYCA6IGAke2UubmFtZX0gKHRocmVzaG9sZCAke2UudGhyZXNob2xkfSUpYCxcbiAgICApXG4gICAgLmpvaW4oXCIsIFwiKTtcblxuICByZXR1cm4gZGV0YWlsXG4gICAgPyBgQWxsIGdyb2sgdG9rZW5zIGV4Y2VlZGVkIHF1b3RhIHRocmVzaG9sZDogJHtkZXRhaWx9YFxuICAgIDogXCJBbGwgZ3JvayB0b2tlbnMgZXhjZWVkZWQgcXVvdGEgdGhyZXNob2xkXCI7XG59XG5cbmZ1bmN0aW9uIG1hcFJlY29yZChtYXA6IE1hcDxHcm9rUmVxdWVzdENsYXNzLCBudW1iZXI+LCBub3c6IG51bWJlcikge1xuICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKFsuLi5tYXBdLmZpbHRlcigoWywgdW50aWxdKSA9PiB1bnRpbCA+IG5vdykpO1xufVxuXG4vKipcbiAqIFBlcnNpc3RzIGEgcHJvdmVuIHJlLWxvZ2luIHJlcXVpcmVtZW50IChgdG9rZW5zLnJlYXV0aF9yZXF1aXJlZGApLCBtaXJyb3JpbmcgdGhlIG90aGVyIHByb3ZpZGVycydcbiAqIGF1dGgtZGVhdGggcGF0aC4gYGNyZWRlbnRpYWxzYCBhcmUgdGhlIG9uZXMgdGhlIGZhaWxlZCBhdHRlbXB0IHByZXNlbnRlZDsgdGhlIHN0b3JlIHNraXBzIHRoZVxuICogd3JpdGUgd2hlbiB0aGUgcm93IGhhcyByb3RhdGVkIHNpbmNlLlxuICovXG5leHBvcnQgdHlwZSBHcm9rUmVhdXRoUmVwb3J0ZXIgPSAoXG4gIHRva2VuOiB7IGlkOiBudW1iZXI7IG5hbWU6IHN0cmluZzsgY3JlZGVudGlhbHM6IEdyb2tDcmVkZW50aWFscyB9LFxuICByZWFzb25Db2RlOiBzdHJpbmcsXG4pID0+IFByb21pc2U8Ym9vbGVhbj47XG5cbmFzeW5jIGZ1bmN0aW9uIHBlcnNpc3RHcm9rUmVhdXRoKFxuICB0b2tlbjogeyBpZDogbnVtYmVyOyBuYW1lOiBzdHJpbmc7IGNyZWRlbnRpYWxzOiBHcm9rQ3JlZGVudGlhbHMgfSxcbiAgcmVhc29uQ29kZTogc3RyaW5nLFxuKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIC8vIExvYWRlZCBsYXppbHk6IHRoZSB0b2tlbiBtb2RlbCBwdWxscyBpbiB0aGUgZGF0YWJhc2UgbGF5ZXIsIHdoaWNoIHRoZSBydW50aW1lIGRvZXMgbm90IG5lZWRcbiAgLy8gdW50aWwgYSB0b2tlbiBhY3R1YWxseSBkaWVzLlxuICBjb25zdCB7IGRlYWN0aXZhdGVBdXRoRGVhZFRva2VuIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi8uLi8uLi9hcHBsaWNhdGlvbi9xZ3JpZC90b2tlbi1kZWF0aFwiKTtcblxuICByZXR1cm4gZGVhY3RpdmF0ZUF1dGhEZWFkVG9rZW4oeyAuLi50b2tlbiwgcHJvdmlkZXI6IFwiZ3Jva1wiIH0sIHJlYXNvbkNvZGUpO1xufVxuXG4vKiogSW50ZXJuYWwgYGdyb2s6PGNvZGU+YCByZWFzb247IHByb3ZpZGVyIHRleHQgbWF5IGNhcnJ5IGFjY291bnQgZGF0YSwgc28gb25seSBjb2RlcyBwYXNzLiAqL1xuZnVuY3Rpb24gcmVhdXRoUmVhc29uQ29kZShjb2RlOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBzdHJpbmcge1xuICByZXR1cm4gY29kZSAhPT0gdW5kZWZpbmVkICYmIC9eW2Etel8wLTldezEsODB9JC8udGVzdChjb2RlKSA/IGBncm9rOiR7Y29kZX1gIDogXCJncm9rOnVua25vd25cIjtcbn1cblxuY29uc3Qgb2F1dGhFcnJvckNvZGVTY2hlbWEgPSB6LnN0cmluZygpO1xuXG5mdW5jdGlvbiBvYXV0aEVycm9yT2YoY2F1c2U6IHVua25vd24pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICByZXR1cm4gY2F1c2UgaW5zdGFuY2VvZiBFcnJvciAmJiBcIm9hdXRoRXJyb3JcIiBpbiBjYXVzZVxuICAgID8gb2F1dGhFcnJvckNvZGVTY2hlbWEuc2FmZVBhcnNlKGNhdXNlLm9hdXRoRXJyb3IpLmRhdGFcbiAgICA6IHVuZGVmaW5lZDtcbn1cblxuLyoqIFRoZSBzYW1lIGJlYXJlciBhbmQgcmVmcmVzaCB0b2tlbjogYSBmYWlsdXJlIGp1ZGdlZCBhZ2FpbnN0IGBiYCBhcHBsaWVzIHRvIGBhYC4gKi9cbmZ1bmN0aW9uIHNhbWVDcmVkZW50aWFscyhhOiBHcm9rQ3JlZGVudGlhbHMgfCB1bmRlZmluZWQsIGI6IEdyb2tDcmVkZW50aWFscyk6IGJvb2xlYW4ge1xuICByZXR1cm4gYT8uYWNjZXNzVG9rZW4gPT09IGIuYWNjZXNzVG9rZW4gJiYgYS5yZWZyZXNoVG9rZW4gPT09IGIucmVmcmVzaFRva2VuO1xufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIERpc3BhdGNoZXJcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5leHBvcnQgaW50ZXJmYWNlIEdyb2tEaXNwYXRjaGVyT3B0aW9ucyB7XG4gIHJlZnJlc2hlcjogR3Jva1JlZnJlc2hlcjtcbiAgZmV0Y2g/OiB0eXBlb2YgZmV0Y2g7XG4gIG1lZGlhPzogR3Jva01lZGlhUG9ydDtcbiAgdm9pY2U/OiBHcm9rVm9pY2VQb3J0O1xuICBub3c/OiAoKSA9PiBudW1iZXI7XG4gIC8qKiBEZWZhdWx0OiBRR1JJRF9HUk9LX01BWF9JTkZMSUdIVF9QRVJfVE9LRU47IHVuZGVmaW5lZCBtZWFucyB1bmNhcHBlZCAowqc1LjYpLiAqL1xuICBtYXhJbmZsaWdodFBlclRva2VuPzogbnVtYmVyO1xuICBzbGVlcD86IChtczogbnVtYmVyLCBzaWduYWw/OiBBYm9ydFNpZ25hbCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgY2xpZW50PzogR3Jva0NoYXRDbGllbnQ7XG4gIGNhdGFsb2c/OiBHcm9rTW9kZWxDYXRhbG9nO1xuICBpZGxlVGltZW91dE1zPzogbnVtYmVyO1xuICAvKiogRGVmYXVsdDogZGVhY3RpdmF0ZUF1dGhEZWFkVG9rZW4gKGFwcGxpY2F0aW9uL3FncmlkL3Rva2VuLWRlYXRoLnRzKS4gKi9cbiAgcmVwb3J0UmVhdXRoUmVxdWlyZWQ/OiBHcm9rUmVhdXRoUmVwb3J0ZXI7XG59XG5cbnR5cGUgRmFpbHVyZUFjdGlvbiA9XG4gIHwgeyBraW5kOiBcImZhaWxcIjsgZXJyb3I6IHVua25vd24gfVxuICB8IHsga2luZDogXCJyZWZyZXNoXCI7IHByZXNlbnRlZDogR3Jva0NyZWRlbnRpYWxzIH1cbiAgfCB7IGtpbmQ6IFwicmV0cnlcIjsgZXJyb3I6IHVua25vd24gfVxuICB8IHsga2luZDogXCJmYWlsb3ZlclwiOyBlcnJvcjogdW5rbm93bjsgdHJhbnNpZW50OiBib29sZWFuIH07XG5cbi8qKiBOYW1lcyBvZiB0aGUgdG9rZW5zIHRoYXQgd2VyZSBpbmVsaWdpYmxlIGZvciBleGFjdGx5IGByZWFzb25gLiAqL1xuZnVuY3Rpb24gdG9rZW5OYW1lc1dpdGhSZWFzb24oXG4gIHJlYXNvbnM6IFJlYWRvbmx5TWFwPEdyb2tUb2tlblN0YXRlLCBJbmVsaWdpYmxlUmVhc29uPixcbiAgcmVhc29uOiBJbmVsaWdpYmxlUmVhc29uLFxuKTogc3RyaW5nW10ge1xuICByZXR1cm4gWy4uLnJlYXNvbnNdLmZsYXRNYXAoKFt0b2tlbiwgdG9rZW5SZWFzb25dKSA9PlxuICAgIHRva2VuUmVhc29uID09PSByZWFzb24gPyBbdG9rZW4ubmFtZV0gOiBbXSxcbiAgKTtcbn1cblxuLyoqIEEgdG9rZW4gcm93IHdob3NlIHN0b3JlZCBjcmVkZW50aWFscyBhcmUgdmFsaWRhdGVkIGhlcmUsIG5vdCB0cnVzdGVkLiAqL1xudHlwZSBHcm9rVG9rZW5JbnB1dCA9IE9taXQ8R3Jva1Rva2VuUm93LCBcImNyZWRlbnRpYWxzXCI+ICYgeyBjcmVkZW50aWFsczogSnNvblZhbHVlIH07XG5cbmV4cG9ydCBpbnRlcmZhY2UgR3Jva0NoYXRCb2R5IHtcbiAgYm9keTogR3Jva1Jlc3BvbnNlc1JlcXVlc3Q7XG4gIG1vZGVsOiBHcm9rUmVzb2x2ZWRNb2RlbDtcbiAgY29udklkOiBzdHJpbmc7XG4gIGNvbnZHcm91cElkOiBzdHJpbmc7XG4gIHR1cm5JZHg6IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHcm9rVmVyc2lvbkdhdGVTdGF0ZSB7XG4gIHRyaXBwZWQ6IGJvb2xlYW47XG4gIG1pbkNsaWVudFZlcnNpb246IHN0cmluZyB8IG51bGw7XG4gIHRyaXBwZWRBdDogbnVtYmVyIHwgbnVsbDtcbn1cblxuaW50ZXJmYWNlIEF0dGVtcHRTdGF0ZSB7XG4gIHVwc3RyZWFtU3RhcnRlZDogYm9vbGVhbjtcbiAgZm9yd2FyZGVkOiBib29sZWFuO1xuICAvKiogVGhlIGV4YWN0IGNyZWRlbnRpYWxzIHRoaXMgYXR0ZW1wdCBwcmVzZW50ZWQgKG9yIHRyaWVkIHRvIHJlZnJlc2gpOyBmYWlsdXJlcyBqdWRnZSB0aGVzZS4gKi9cbiAgcHJlc2VudGVkOiBHcm9rQ3JlZGVudGlhbHMgfCB1bmRlZmluZWQ7XG59XG5cbmV4cG9ydCBjbGFzcyBHcm9rRGlzcGF0Y2hlciBpbXBsZW1lbnRzIEdyb2tQcm92aWRlckRpc3BhdGNoZXIsIE1lZGlhRGlzcGF0Y2hlciwgVm9pY2VEaXNwYXRjaGVyIHtcbiAgcmVhZG9ubHkgdG9rZW5zID0gbmV3IE1hcDxudW1iZXIsIEdyb2tUb2tlblN0YXRlPigpO1xuICByZWFkb25seSBzZWxlY3RvciA9IG5ldyBTbW9vdGhXZWlnaHRlZFJvdW5kUm9iaW4oKTtcbiAgcmVhZG9ubHkgcmVmcmVzaGVyOiBHcm9rUmVmcmVzaGVyO1xuICByZWFkb25seSBmZXRjaEltcGw6IHR5cGVvZiBmZXRjaDtcbiAgcmVhZG9ubHkgbWVkaWE6IEdyb2tNZWRpYVBvcnQgfCB1bmRlZmluZWQ7XG4gIHJlYWRvbmx5IHZvaWNlOiBHcm9rVm9pY2VQb3J0IHwgdW5kZWZpbmVkO1xuICByZWFkb25seSBub3c6ICgpID0+IG51bWJlcjtcbiAgcmVhZG9ubHkgbWF4SW5mbGlnaHRQZXJUb2tlbjogbnVtYmVyIHwgdW5kZWZpbmVkO1xuICByZWFkb25seSBzbGVlcDogKG1zOiBudW1iZXIsIHNpZ25hbD86IEFib3J0U2lnbmFsKSA9PiBQcm9taXNlPHZvaWQ+O1xuICByZWFkb25seSBjbGllbnQ6IEdyb2tDaGF0Q2xpZW50O1xuICByZWFkb25seSBjYXRhbG9nOiBHcm9rTW9kZWxDYXRhbG9nO1xuICByZWFkb25seSBpZGxlVGltZW91dE1zOiBudW1iZXI7XG4gIHJlYWRvbmx5IHJlcG9ydFJlYXV0aFJlcXVpcmVkOiBHcm9rUmVhdXRoUmVwb3J0ZXI7XG4gIHJlYWRvbmx5IHBlbmRpbmdCaWxsaW5nID0gbmV3IE1hcDxudW1iZXIsIFByb21pc2U8dm9pZD4+KCk7XG4gIHJlYWRvbmx5IHBlbmRpbmdTZXR0aW5ncyA9IG5ldyBNYXA8bnVtYmVyLCBQcm9taXNlPHZvaWQ+PigpO1xuICByZWFkb25seSBwZW5kaW5nU3Vic2NyaXB0aW9uID0gbmV3IE1hcDxudW1iZXIsIFByb21pc2U8dm9pZD4+KCk7XG4gIHZlcnNpb25HYXRlOiB7IHRyaXBwZWRBdDogbnVtYmVyOyBtaW5DbGllbnRWZXJzaW9uOiBzdHJpbmcgfCB1bmRlZmluZWQgfSB8IHVuZGVmaW5lZDtcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBHcm9rRGlzcGF0Y2hlck9wdGlvbnMpIHtcbiAgICB0aGlzLnJlZnJlc2hlciA9IG9wdGlvbnMucmVmcmVzaGVyO1xuICAgIHRoaXMuZmV0Y2hJbXBsID0gb3B0aW9ucy5mZXRjaCA/PyBmZXRjaDtcbiAgICB0aGlzLm1lZGlhID0gb3B0aW9ucy5tZWRpYTtcbiAgICB0aGlzLnZvaWNlID0gb3B0aW9ucy52b2ljZTtcbiAgICB0aGlzLm5vdyA9IG9wdGlvbnMubm93ID8/IERhdGUubm93O1xuICAgIHRoaXMubWF4SW5mbGlnaHRQZXJUb2tlbiA9XG4gICAgICBcIm1heEluZmxpZ2h0UGVyVG9rZW5cIiBpbiBvcHRpb25zXG4gICAgICAgID8gb3B0aW9ucy5tYXhJbmZsaWdodFBlclRva2VuXG4gICAgICAgIDogcGFyc2VNYXhJbmZsaWdodChwcm9jZXNzLmVudi5RR1JJRF9HUk9LX01BWF9JTkZMSUdIVF9QRVJfVE9LRU4pO1xuICAgIHRoaXMuc2xlZXAgPSBvcHRpb25zLnNsZWVwID8/IGRlZmF1bHRTbGVlcDtcbiAgICB0aGlzLmNsaWVudCA9IG9wdGlvbnMuY2xpZW50ID8/IG5ldyBHcm9rQ2hhdENsaWVudCh7IGZldGNoOiB0aGlzLmZldGNoSW1wbCB9KTtcbiAgICB0aGlzLmNhdGFsb2cgPSBvcHRpb25zLmNhdGFsb2cgPz8gbmV3IEdyb2tNb2RlbENhdGFsb2coKTtcbiAgICB0aGlzLmlkbGVUaW1lb3V0TXMgPSBvcHRpb25zLmlkbGVUaW1lb3V0TXMgPz8gREVGQVVMVF9JRExFX1RJTUVPVVRfTVM7XG4gICAgdGhpcy5yZXBvcnRSZWF1dGhSZXF1aXJlZCA9IG9wdGlvbnMucmVwb3J0UmVhdXRoUmVxdWlyZWQgPz8gcGVyc2lzdEdyb2tSZWF1dGg7XG4gIH1cblxuICAvLyBUb2tlbnMgYXJyaXZlIHRocm91Z2ggR3Jva1Rva2VuU2luayAoVG9rZW5TdWJzY3JpYmVyIHJlY29uY2lsZXMgb24gc3RhcnQpLlxuICBhc3luYyBzdGFydCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBsb2dnZXIuaW5mbyhgc3RhcnRlZCBncm9rIHJ1bnRpbWUgd2l0aCAke3RoaXMudG9rZW5zLnNpemV9IHRva2Vuc2ApO1xuICB9XG5cbiAgYXN5bmMgc3RvcCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLnRva2Vucy5jbGVhcigpO1xuICAgIHRoaXMucGVuZGluZ0JpbGxpbmcuY2xlYXIoKTtcbiAgICB0aGlzLnBlbmRpbmdTZXR0aW5ncy5jbGVhcigpO1xuICAgIHRoaXMucGVuZGluZ1N1YnNjcmlwdGlvbi5jbGVhcigpO1xuXG4gICAgZm9yIChjb25zdCBpZCBvZiB0aGlzLnNlbGVjdG9yLndlaWdodHMua2V5cygpKSB0aGlzLnNlbGVjdG9yLnJlbW92ZVRva2VuKGlkKTtcbiAgICB0aGlzLnZlcnNpb25HYXRlID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgLy8gLS0tLSBHcm9rVG9rZW5TaW5rIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuICBvblRva2VuQWRkZWQoXG4gICAgaWQ6IG51bWJlcixcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgY3JlZGVudGlhbHM6IEpzb25WYWx1ZSxcbiAgICBxdW90YVRocmVzaG9sZDogbnVtYmVyIHwgbnVsbCxcbiAgICB3ZWlnaHQ6IG51bWJlcixcbiAgKTogdm9pZCB7XG4gICAgdGhpcy51cHNlcnRUb2tlbihpZCwgbmFtZSwgY3JlZGVudGlhbHMsIHF1b3RhVGhyZXNob2xkLCB3ZWlnaHQpO1xuICB9XG5cbiAgb25Ub2tlblVwZGF0ZWQoXG4gICAgaWQ6IG51bWJlcixcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgY3JlZGVudGlhbHM6IEpzb25WYWx1ZSxcbiAgICBxdW90YVRocmVzaG9sZDogbnVtYmVyIHwgbnVsbCxcbiAgICB3ZWlnaHQ6IG51bWJlcixcbiAgKTogdm9pZCB7XG4gICAgdGhpcy51cHNlcnRUb2tlbihpZCwgbmFtZSwgY3JlZGVudGlhbHMsIHF1b3RhVGhyZXNob2xkLCB3ZWlnaHQpO1xuICB9XG5cbiAgb25Ub2tlblJlbW92ZWQoaWQ6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMudG9rZW5zLmRlbGV0ZShpZCk7XG4gICAgdGhpcy5zZWxlY3Rvci5yZW1vdmVUb2tlbihpZCk7XG4gICAgdGhpcy5wZW5kaW5nQmlsbGluZy5kZWxldGUoaWQpO1xuICAgIHRoaXMucGVuZGluZ1NldHRpbmdzLmRlbGV0ZShpZCk7XG4gICAgdGhpcy5wZW5kaW5nU3Vic2NyaXB0aW9uLmRlbGV0ZShpZCk7XG4gIH1cblxuICByZXBsYWNlVG9rZW5zKHJvd3M6IEdyb2tUb2tlbklucHV0W10pOiB2b2lkIHtcbiAgICBjb25zdCBpZHMgPSBuZXcgU2V0KHJvd3MubWFwKChyb3cpID0+IHJvdy5pZCkpO1xuXG4gICAgZm9yIChjb25zdCBpZCBvZiB0aGlzLnRva2Vucy5rZXlzKCkpIGlmICghaWRzLmhhcyhpZCkpIHRoaXMub25Ub2tlblJlbW92ZWQoaWQpO1xuXG4gICAgZm9yIChjb25zdCByb3cgb2Ygcm93cylcbiAgICAgIHRoaXMudXBzZXJ0VG9rZW4ocm93LmlkLCByb3cubmFtZSwgcm93LmNyZWRlbnRpYWxzLCByb3cucXVvdGFUaHJlc2hvbGQsIHJvdy53ZWlnaHQpO1xuICB9XG5cbiAgdXBzZXJ0VG9rZW4oXG4gICAgaWQ6IG51bWJlcixcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgcmF3Q3JlZGVudGlhbHM6IEpzb25WYWx1ZSxcbiAgICBxdW90YVRocmVzaG9sZDogbnVtYmVyIHwgbnVsbCxcbiAgICB3ZWlnaHQ6IG51bWJlcixcbiAgKTogdm9pZCB7XG4gICAgY29uc3QgcGFyc2VkID0gR3Jva0NyZWRlbnRpYWxzLnNhZmVQYXJzZShyYXdDcmVkZW50aWFscyk7XG5cbiAgICAvLyBPbmx5IHBhdGhzIGFyZSBsb2dnZWQ7IHZhbHVlcyBtYXkgYmUgc2VjcmV0cy5cbiAgICBsZXQgaW52YWxpZFJlYXNvbiA9IHBhcnNlZC5zdWNjZXNzXG4gICAgICA/IHVuZGVmaW5lZFxuICAgICAgOiBgaW52YWxpZCBjcmVkZW50aWFscyBhdCAke1suLi5uZXcgU2V0KHBhcnNlZC5lcnJvci5pc3N1ZXMubWFwKChpKSA9PiBpLnBhdGguam9pbihcIi5cIikgfHwgXCI8cm9vdD5cIikpXS5qb2luKFwiLCBcIil9YDtcblxuICAgIGlmICghaW52YWxpZFJlYXNvbiAmJiAoIU51bWJlci5pc0ludGVnZXIod2VpZ2h0KSB8fCB3ZWlnaHQgPCAxIHx8IHdlaWdodCA+IDEwMCkpIHtcbiAgICAgIGludmFsaWRSZWFzb24gPSBgaW52YWxpZCB3ZWlnaHQgJHt3ZWlnaHR9YDtcbiAgICB9XG5cbiAgICBjb25zdCBjcmVkZW50aWFscyA9IHBhcnNlZC5zdWNjZXNzID8gcGFyc2VkLmRhdGEgOiB1bmRlZmluZWQ7XG4gICAgY29uc3Qgb2xkID0gdGhpcy50b2tlbnMuZ2V0KGlkKTtcblxuICAgIGlmIChvbGQpIHtcbiAgICAgIGNvbnN0IGNyZWRlbnRpYWxzQ2hhbmdlZCA9XG4gICAgICAgIG9sZC5jcmVkZW50aWFscz8uYWNjZXNzVG9rZW4gIT09IGNyZWRlbnRpYWxzPy5hY2Nlc3NUb2tlbiB8fFxuICAgICAgICBvbGQuY3JlZGVudGlhbHM/LnJlZnJlc2hUb2tlbiAhPT0gY3JlZGVudGlhbHM/LnJlZnJlc2hUb2tlbjtcblxuICAgICAgY29uc3QgdGhyZXNob2xkQ2hhbmdlZCA9IG9sZC5xdW90YVRocmVzaG9sZCAhPT0gcXVvdGFUaHJlc2hvbGQ7XG4gICAgICBvbGQubmFtZSA9IG5hbWU7XG4gICAgICBvbGQud2VpZ2h0ID0gd2VpZ2h0O1xuICAgICAgb2xkLnF1b3RhVGhyZXNob2xkID0gcXVvdGFUaHJlc2hvbGQ7XG4gICAgICBvbGQuY3JlZGVudGlhbHMgPSBjcmVkZW50aWFscztcbiAgICAgIG9sZC5pbnZhbGlkUmVhc29uID0gaW52YWxpZFJlYXNvbjtcblxuICAgICAgaWYgKGNyZWRlbnRpYWxzQ2hhbmdlZCB8fCB0aHJlc2hvbGRDaGFuZ2VkKSB7XG4gICAgICAgIG9sZC5nZW5lcmF0aW9uKys7XG4gICAgICAgIG9sZC5iaWxsaW5nID0gdW5kZWZpbmVkO1xuICAgICAgICBvbGQuc2V0dGluZ3MgPSB1bmRlZmluZWQ7XG4gICAgICAgIG9sZC5zdWJzY3JpcHRpb24gPSB1bmRlZmluZWQ7XG4gICAgICAgIG9sZC5vdmVyVGhyZXNob2xkID0gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIGlmIChjcmVkZW50aWFsc0NoYW5nZWQpIHtcbiAgICAgICAgb2xkLnJlYXV0aFJlcXVpcmVkID0gZmFsc2U7XG4gICAgICAgIG9sZC5yZWZyZXNoRmFpbGVkVW50aWwgPSAwO1xuICAgICAgICBvbGQucmVmcmVzaEZhaWx1cmUgPSB1bmRlZmluZWQ7XG4gICAgICAgIC8vIE5ldyBjcmVkZW50aWFscyBtYXkgYmVsb25nIHRvIGFub3RoZXIgYWNjb3VudDsgWkRSIGlzIGFjY291bnQtbGV2ZWwuXG4gICAgICAgIG9sZC56ZHIgPSBudWxsO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnRva2Vucy5zZXQoaWQsIHtcbiAgICAgICAgaWQsXG4gICAgICAgIG5hbWUsXG4gICAgICAgIHdlaWdodCxcbiAgICAgICAgcXVvdGFUaHJlc2hvbGQsXG4gICAgICAgIGNyZWRlbnRpYWxzLFxuICAgICAgICBpbnZhbGlkUmVhc29uLFxuICAgICAgICBnZW5lcmF0aW9uOiAxLFxuICAgICAgICBpbkZsaWdodDogMCxcbiAgICAgICAgY29vbGRvd25zOiBuZXcgTWFwKCksXG4gICAgICAgIHF1b3RhQmxvY2tzOiBuZXcgTWFwKCksXG4gICAgICAgIGVudGl0bGVtZW50QmxvY2tzOiBuZXcgTWFwKCksXG4gICAgICAgIHJlYXV0aFJlcXVpcmVkOiBmYWxzZSxcbiAgICAgICAgcmVmcmVzaEZhaWxlZFVudGlsOiAwLFxuICAgICAgICByZWZyZXNoRmFpbHVyZTogdW5kZWZpbmVkLFxuICAgICAgICBiaWxsaW5nOiB1bmRlZmluZWQsXG4gICAgICAgIGJpbGxpbmdOb3RCZWZvcmU6IDAsXG4gICAgICAgIHNldHRpbmdzOiB1bmRlZmluZWQsXG4gICAgICAgIHN1YnNjcmlwdGlvbjogdW5kZWZpbmVkLFxuICAgICAgICB6ZHI6IG51bGwsXG4gICAgICAgIG92ZXJUaHJlc2hvbGQ6IGZhbHNlLFxuICAgICAgICBsYXN0VGllcjogY3JlZGVudGlhbHM/LnN1YnNjcmlwdGlvblRpZXIsXG4gICAgICAgIGZhaWxvdmVyQ291bnQ6IDAsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBpZiAoaW52YWxpZFJlYXNvbikge1xuICAgICAgbG9nZ2VyLndhcm4oYGdyb2sgdG9rZW4gJHtuYW1lfVske2lkfV0gdHJlYXRlZCBhcyBpbmFjdGl2ZTogJHtpbnZhbGlkUmVhc29ufWApO1xuICAgICAgdGhpcy5zZWxlY3Rvci5yZW1vdmVUb2tlbihpZCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuc2VsZWN0b3Iuc2V0VG9rZW4oaWQsIHdlaWdodCk7XG4gICAgfVxuICB9XG5cbiAgLy8gLS0tLSBFbGlnaWJpbGl0eSAowqc1LjEpIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuICAvKipcbiAgICogYGhlbGRgIGNvdW50cyByZXNlcnZhdGlvbnMgdGhlIGNhbGxlciBhbHJlYWR5IGhvbGRzIG9uIGB0b2tlbmAgKHJlLWNoZWNrcyBtaWQtcmVxdWVzdCkuXG4gICAqIGB1bmdhdGVkYCBza2lwcyB0aGUgYWRtaXNzaW9uIGdhdGVzIGFuZCBjaGVja3Mgb25seSB0aGF0IHRoZSBjcmVkZW50aWFscyBhcmUgdXNhYmxlLlxuICAgKi9cbiAgaW5lbGlnaWJpbGl0eShcbiAgICB0b2tlbjogR3Jva1Rva2VuU3RhdGUsXG4gICAgcmVxdWVzdENsYXNzOiBHcm9rUmVxdWVzdENsYXNzLFxuICAgIG5vdzogbnVtYmVyLFxuICAgIGhlbGQgPSAwLFxuICAgIHVuZ2F0ZWQgPSBmYWxzZSxcbiAgKTogSW5lbGlnaWJsZVJlYXNvbiB8IHVuZGVmaW5lZCB7XG4gICAgaWYgKCF0b2tlbi5jcmVkZW50aWFscyB8fCB0b2tlbi5pbnZhbGlkUmVhc29uKSByZXR1cm4gXCJpbmFjdGl2ZVwiO1xuXG4gICAgaWYgKHRva2VuLnJlYXV0aFJlcXVpcmVkKSByZXR1cm4gXCJyZWF1dGhcIjtcblxuICAgIGlmICh0b2tlbi5yZWZyZXNoRmFpbGVkVW50aWwgPiBub3cpIHJldHVybiBcInJlZnJlc2hfZmFpbGVkXCI7XG5cbiAgICBpZiAodW5nYXRlZCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICBjb25zdCBzZXR0aW5ncyA9IHRva2VuLnNldHRpbmdzPy52YWx1ZTtcblxuICAgIGlmIChzZXR0aW5ncz8ua2luZCA9PT0gXCJva1wiICYmIHNldHRpbmdzLm9uRGVtYW5kRW5hYmxlZCA9PT0gdHJ1ZSkgcmV0dXJuIFwib25fZGVtYW5kXCI7XG5cbiAgICBpZiAocmVxdWVzdENsYXNzID09PSBcInZpZGVvXCIgJiYgdG9rZW4uemRyID09PSB0cnVlKSByZXR1cm4gXCJ6ZHJcIjtcblxuICAgIGlmICgodG9rZW4uZW50aXRsZW1lbnRCbG9ja3MuZ2V0KHJlcXVlc3RDbGFzcykgPz8gMCkgPiBub3cpIHJldHVybiBcImVudGl0bGVtZW50XCI7XG5cbiAgICBpZiAoKHRva2VuLnF1b3RhQmxvY2tzLmdldChyZXF1ZXN0Q2xhc3MpID8/IDApID4gbm93KSByZXR1cm4gXCJxdW90YVwiO1xuXG4gICAgaWYgKHRva2VuLnF1b3RhVGhyZXNob2xkICE9PSBudWxsICYmIHRva2VuLm92ZXJUaHJlc2hvbGQpIHJldHVybiBcInF1b3RhXCI7XG5cbiAgICBpZiAoKHRva2VuLmNvb2xkb3ducy5nZXQocmVxdWVzdENsYXNzKSA/PyAwKSA+IG5vdykgcmV0dXJuIFwiY29vbGRvd25cIjtcblxuICAgIGlmICh0aGlzLm1heEluZmxpZ2h0UGVyVG9rZW4gIT09IHVuZGVmaW5lZCAmJiB0b2tlbi5pbkZsaWdodCAtIGhlbGQgPj0gdGhpcy5tYXhJbmZsaWdodFBlclRva2VuKVxuICAgICAgcmV0dXJuIFwiY2FwYWNpdHlcIjtcblxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBpc0VsaWdpYmxlKHRva2VuOiBHcm9rVG9rZW5TdGF0ZSwgcmVxdWVzdENsYXNzOiBHcm9rUmVxdWVzdENsYXNzKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuaW5lbGlnaWJpbGl0eSh0b2tlbiwgcmVxdWVzdENsYXNzLCB0aGlzLm5vdygpKSA9PT0gdW5kZWZpbmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlZnJlc2hlcyB0aGUgNjAgcyBzZXR0aW5ncy9zdWJzY3JpcHRpb24vYmlsbGluZyBjYWNoZXMuIExvb2t1cCBmYWlsdXJlcyBmYWlsIG9wZW4sIHNvIGFuXG4gICAqIGV4cGlyZWQgYmVhcmVyIGlzIHJlZnJlc2hlZCBmaXJzdDogYSBsb29rdXAgd2l0aCBpdCB3b3VsZCBmYWlsIGFuZCBvcGVuIGV2ZXJ5IGdhdGUuIEEgZmFpbGVkXG4gICAqIHJlZnJlc2ggdGhlcmVmb3JlIHRha2VzIHRoZSB0b2tlbiBvdXQgb2Ygc2VsZWN0aW9uIChgcmVmcmVzaF9mYWlsZWRgKSBpbnN0ZWFkIG9mIGZhaWxpbmcgb3Blbi5cbiAgICovXG4gIGFzeW5jIHByZWZldGNoKHRva2VuOiBHcm9rVG9rZW5TdGF0ZSwgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRva2VuLmNyZWRlbnRpYWxzIHx8IHRva2VuLmludmFsaWRSZWFzb24gfHwgdG9rZW4ucmVhdXRoUmVxdWlyZWQpIHJldHVybjtcbiAgICBsZXQgbm93ID0gdGhpcy5ub3coKTtcbiAgICBjb25zdCB0YXNrczogUHJvbWlzZTx2b2lkPltdID0gW107XG5cbiAgICBjb25zdCBzdGFsZSA9IChlbnRyeTogQ2FjaGVkPHVua25vd24+IHwgdW5kZWZpbmVkKSA9PlxuICAgICAgIWVudHJ5IHx8XG4gICAgICBlbnRyeS5nZW5lcmF0aW9uICE9PSB0b2tlbi5nZW5lcmF0aW9uIHx8XG4gICAgICBub3cgLSBlbnRyeS5mZXRjaGVkQXQgPj0gR1JPS19MT09LVVBfQ0FDSEVfVFRMX01TO1xuXG4gICAgY29uc3QgYmlsbGluZ0R1ZSA9ICgpID0+XG4gICAgICAodG9rZW4ucXVvdGFUaHJlc2hvbGQgIT09IG51bGwgfHwgWy4uLnRva2VuLnF1b3RhQmxvY2tzLnZhbHVlcygpXS5zb21lKCh1KSA9PiB1ID4gbm93KSkgJiZcbiAgICAgIHN0YWxlKHRva2VuLmJpbGxpbmcpICYmXG4gICAgICBub3cgPj0gdG9rZW4uYmlsbGluZ05vdEJlZm9yZTtcblxuICAgIGlmICghc3RhbGUodG9rZW4uc2V0dGluZ3MpICYmICFzdGFsZSh0b2tlbi5zdWJzY3JpcHRpb24pICYmICFiaWxsaW5nRHVlKCkpIHJldHVybjtcblxuICAgIGlmIChncm9rTmVlZHNSZWZyZXNoKHRva2VuLmNyZWRlbnRpYWxzLCBub3cpKSB7XG4gICAgICAvLyBEbyBub3QgaGFtbWVyIHRoZSBJZFA6IHRoZSBjb29sZG93biBleHBpcmVzIGJlZm9yZSB0aGUgbmV4dCByZWZyZXNoIGlzIHRyaWVkLlxuICAgICAgaWYgKHRva2VuLnJlZnJlc2hGYWlsZWRVbnRpbCA+IG5vdykgcmV0dXJuO1xuICAgICAgY29uc3QgcHJlc2VudGVkID0gdG9rZW4uY3JlZGVudGlhbHM7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMuZnJlc2hDcmVkZW50aWFscyh0b2tlbiwgc2lnbmFsID8/IG5ldyBBYm9ydENvbnRyb2xsZXIoKS5zaWduYWwpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgLy8gTG9va3VwcyB3aXRoIHRoaXMgYmVhcmVyIHdvdWxkIGZhaWwgb3Blbiwgc28gdGhlIGdhdGVzIHN0YXkgdW5rbm93biBhbmQgdGhlIHRva2VuIHNpdHNcbiAgICAgICAgLy8gb3V0IHNlbGVjdGlvbiB1bnRpbCBhIGxhdGVyIHJlZnJlc2ggc3VjY2VlZHMuXG4gICAgICAgIGlmIChzaWduYWw/LmFib3J0ZWQpIHJldHVybjtcblxuICAgICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvciAmJiBlcnJvci5uYW1lID09PSBcIkdyb2tSZWF1dGhSZXF1aXJlZEVycm9yXCIpXG4gICAgICAgICAgdGhpcy5tYXJrUmVhdXRoUmVxdWlyZWQodG9rZW4sIHByZXNlbnRlZCwgcmVhdXRoUmVhc29uQ29kZShvYXV0aEVycm9yT2YoZXJyb3IpKSk7XG4gICAgICAgIGVsc2UgdGhpcy5tYXJrUmVmcmVzaEZhaWxlZCh0b2tlbiwgcHJlc2VudGVkLCBlcnJvcik7XG5cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAoIXRva2VuLmNyZWRlbnRpYWxzIHx8IHRva2VuLnJlYXV0aFJlcXVpcmVkIHx8IHRoaXMudG9rZW5zLmdldCh0b2tlbi5pZCkgIT09IHRva2VuKSByZXR1cm47XG4gICAgICBub3cgPSB0aGlzLm5vdygpO1xuICAgIH1cblxuICAgIGlmIChzdGFsZSh0b2tlbi5zZXR0aW5ncykpIHRhc2tzLnB1c2godGhpcy5sb2FkU2V0dGluZ3ModG9rZW4pKTtcblxuICAgIGlmIChzdGFsZSh0b2tlbi5zdWJzY3JpcHRpb24pKSB0YXNrcy5wdXNoKHRoaXMubG9hZFN1YnNjcmlwdGlvbih0b2tlbikpO1xuICAgIGNvbnN0IGJsb2NrZWQgPSBbLi4udG9rZW4ucXVvdGFCbG9ja3MudmFsdWVzKCldLnNvbWUoKHVudGlsKSA9PiB1bnRpbCA+IG5vdyk7XG5cbiAgICBpZiAoKHRva2VuLnF1b3RhVGhyZXNob2xkICE9PSBudWxsIHx8IGJsb2NrZWQpICYmIHN0YWxlKHRva2VuLmJpbGxpbmcpKSB7XG4gICAgICBpZiAobm93ID49IHRva2VuLmJpbGxpbmdOb3RCZWZvcmUpIHRhc2tzLnB1c2godGhpcy5sb2FkQmlsbGluZyh0b2tlbikpO1xuICAgIH1cblxuICAgIGF3YWl0IFByb21pc2UuYWxsKHRhc2tzKTtcbiAgfVxuXG4gIC8qKiBUcmFuc2llbnQgcmVmcmVzaCBmYWlsdXJlIGZvciBgcHJlc2VudGVkYDsgaWdub3JlZCB3aGVuIHRoZSB0b2tlbiBhbHJlYWR5IGhvbGRzIG5ld2VyIG9uZXMuICovXG4gIG1hcmtSZWZyZXNoRmFpbGVkKHRva2VuOiBHcm9rVG9rZW5TdGF0ZSwgcHJlc2VudGVkOiBHcm9rQ3JlZGVudGlhbHMsIGNhdXNlOiB1bmtub3duKTogdm9pZCB7XG4gICAgaWYgKHRoaXMudG9rZW5zLmdldCh0b2tlbi5pZCkgIT09IHRva2VuIHx8ICFzYW1lQ3JlZGVudGlhbHModG9rZW4uY3JlZGVudGlhbHMsIHByZXNlbnRlZCkpXG4gICAgICByZXR1cm47XG4gICAgdG9rZW4ucmVmcmVzaEZhaWxlZFVudGlsID0gdGhpcy5ub3coKSArIEdST0tfUkVGUkVTSF9GQUlMRURfQ09PTERPV05fTVM7XG4gICAgdG9rZW4ucmVmcmVzaEZhaWx1cmUgPSBjYXVzZTtcbiAgICBsb2dnZXIud2FybihcbiAgICAgIGBncm9rIHRva2VuICR7dG9rZW4ubmFtZX1bJHt0b2tlbi5pZH1dIHJlZnJlc2ggZmFpbGVkOyBvdXQgb2Ygc2VsZWN0aW9uIGZvciAke0dST0tfUkVGUkVTSF9GQUlMRURfQ09PTERPV05fTVN9bXM6ICR7Y2F1c2UgaW5zdGFuY2VvZiBFcnJvciA/IGNhdXNlLm5hbWUgOiBcInVua25vd25cIn1gLFxuICAgICk7XG4gIH1cblxuICBsb2FkU2V0dGluZ3ModG9rZW46IEdyb2tUb2tlblN0YXRlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcGVuZGluZyA9IHRoaXMucGVuZGluZ1NldHRpbmdzLmdldCh0b2tlbi5pZCk7XG5cbiAgICBpZiAocGVuZGluZykgcmV0dXJuIHBlbmRpbmc7XG4gICAgY29uc3QgZ2VuZXJhdGlvbiA9IHRva2VuLmdlbmVyYXRpb247XG4gICAgY29uc3QgY3JlZGVudGlhbHMgPSB0b2tlbi5jcmVkZW50aWFscyE7XG5cbiAgICBjb25zdCBwcm9taXNlID0gcmVhZEdyb2tTZXR0aW5ncyhjcmVkZW50aWFscywgeyBmZXRjaDogdGhpcy5mZXRjaEltcGwgfSlcbiAgICAgIC50aGVuKChyZXN1bHQpID0+IHtcbiAgICAgICAgaWYgKHRoaXMudG9rZW5zLmdldCh0b2tlbi5pZCkgIT09IHRva2VuIHx8IHRva2VuLmdlbmVyYXRpb24gIT09IGdlbmVyYXRpb24pIHJldHVybjtcbiAgICAgICAgdG9rZW4uc2V0dGluZ3MgPSB7IHZhbHVlOiByZXN1bHQsIGZldGNoZWRBdDogdGhpcy5ub3coKSwgZ2VuZXJhdGlvbiB9O1xuXG4gICAgICAgIGlmIChyZXN1bHQua2luZCA9PT0gXCJsb29rdXBfZmFpbGVkXCIpIHtcbiAgICAgICAgICBsb2dnZXIud2FybihcInF1b3RhX3RocmVzaG9sZCBnYXRlOiBzZXR0aW5nc19sb29rdXBfZmFpbF9vcGVuXCIsIHtcbiAgICAgICAgICAgIHRva2VuSWQ6IHRva2VuLmlkLFxuICAgICAgICAgICAgdG9rZW5OYW1lOiB0b2tlbi5uYW1lLFxuICAgICAgICAgICAgbG9va3VwUmVhc29uOiByZXN1bHQucmVhc29uLFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHJlc3VsdC5zdWJzY3JpcHRpb25UaWVyRGlzcGxheSkgdG9rZW4ubGFzdFRpZXIgPSByZXN1bHQuc3Vic2NyaXB0aW9uVGllckRpc3BsYXk7XG5cbiAgICAgICAgaWYgKHJlc3VsdC5vbkRlbWFuZEVuYWJsZWQgPT09IHRydWUpIHtcbiAgICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBxdW90YV90aHJlc2hvbGQgZ2F0ZTogb25fZGVtYW5kX2V4Y2x1ZGVkICR7dG9rZW4ubmFtZX1bJHt0b2tlbi5pZH1dIChvbl9kZW1hbmRfZW5hYmxlZD10cnVlKWAsXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICAgIC5maW5hbGx5KCgpID0+IHtcbiAgICAgICAgaWYgKHRoaXMucGVuZGluZ1NldHRpbmdzLmdldCh0b2tlbi5pZCkgPT09IHByb21pc2UpIHRoaXMucGVuZGluZ1NldHRpbmdzLmRlbGV0ZSh0b2tlbi5pZCk7XG4gICAgICB9KTtcblxuICAgIHRoaXMucGVuZGluZ1NldHRpbmdzLnNldCh0b2tlbi5pZCwgcHJvbWlzZSk7XG5cbiAgICByZXR1cm4gcHJvbWlzZTtcbiAgfVxuXG4gIC8qKiAvdXNlciBjb2RpbmdEYXRhUmV0ZW50aW9uT3B0T3V0IGlzIHRoZSBwcmltYXJ5IFpEUiBzaWduYWw7IGEgZmFpbGVkIHJlYWQga2VlcHMgdGhlIGxhc3QgdmFsdWUuICovXG4gIGxvYWRTdWJzY3JpcHRpb24odG9rZW46IEdyb2tUb2tlblN0YXRlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcGVuZGluZyA9IHRoaXMucGVuZGluZ1N1YnNjcmlwdGlvbi5nZXQodG9rZW4uaWQpO1xuXG4gICAgaWYgKHBlbmRpbmcpIHJldHVybiBwZW5kaW5nO1xuICAgIGNvbnN0IGdlbmVyYXRpb24gPSB0b2tlbi5nZW5lcmF0aW9uO1xuICAgIGNvbnN0IGNyZWRlbnRpYWxzID0gdG9rZW4uY3JlZGVudGlhbHMhO1xuXG4gICAgY29uc3QgcHJvbWlzZSA9IHJlYWRHcm9rU3Vic2NyaXB0aW9uKGNyZWRlbnRpYWxzLCB7IGZldGNoOiB0aGlzLmZldGNoSW1wbCB9KVxuICAgICAgLnRoZW4oKHJlc3VsdCkgPT4ge1xuICAgICAgICBpZiAodGhpcy50b2tlbnMuZ2V0KHRva2VuLmlkKSAhPT0gdG9rZW4gfHwgdG9rZW4uZ2VuZXJhdGlvbiAhPT0gZ2VuZXJhdGlvbikgcmV0dXJuO1xuICAgICAgICB0b2tlbi5zdWJzY3JpcHRpb24gPSB7IHZhbHVlOiByZXN1bHQsIGZldGNoZWRBdDogdGhpcy5ub3coKSwgZ2VuZXJhdGlvbiB9O1xuXG4gICAgICAgIGlmIChyZXN1bHQua2luZCA9PT0gXCJsb29rdXBfZmFpbGVkXCIpIHtcbiAgICAgICAgICBsb2dnZXIuZGVidWcoYGdyb2sgc3Vic2NyaXB0aW9uIGxvb2t1cCBmYWlsZWQgZm9yICR7dG9rZW4ubmFtZX1bJHt0b2tlbi5pZH1dYCwge1xuICAgICAgICAgICAgbG9va3VwUmVhc29uOiByZXN1bHQucmVhc29uLFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHJlc3VsdC5kYXRhUmV0ZW50aW9uT3B0T3V0ICE9PSBudWxsKSB7XG4gICAgICAgICAgdGhpcy5zZXRaZHIodG9rZW4sIHJlc3VsdC5kYXRhUmV0ZW50aW9uT3B0T3V0LCBcImNvZGluZ0RhdGFSZXRlbnRpb25PcHRPdXRcIik7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLnBlbmRpbmdTdWJzY3JpcHRpb24uZ2V0KHRva2VuLmlkKSA9PT0gcHJvbWlzZSlcbiAgICAgICAgICB0aGlzLnBlbmRpbmdTdWJzY3JpcHRpb24uZGVsZXRlKHRva2VuLmlkKTtcbiAgICAgIH0pO1xuXG4gICAgdGhpcy5wZW5kaW5nU3Vic2NyaXB0aW9uLnNldCh0b2tlbi5pZCwgcHJvbWlzZSk7XG5cbiAgICByZXR1cm4gcHJvbWlzZTtcbiAgfVxuXG4gIHNldFpkcih0b2tlbjogR3Jva1Rva2VuU3RhdGUsIHpkcjogYm9vbGVhbiwgc291cmNlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAodGhpcy50b2tlbnMuZ2V0KHRva2VuLmlkKSAhPT0gdG9rZW4gfHwgdG9rZW4uemRyID09PSB6ZHIpIHJldHVybjtcbiAgICB0b2tlbi56ZHIgPSB6ZHI7XG4gICAgbG9nZ2VyLmluZm8oXG4gICAgICB6ZHJcbiAgICAgICAgPyBgZ3JvayB0b2tlbiAke3Rva2VuLm5hbWV9WyR7dG9rZW4uaWR9XSBleGNsdWRlZCBmcm9tIHZpZGVvOiB6ZXJvIGRhdGEgcmV0ZW50aW9uICgke3NvdXJjZX0pYFxuICAgICAgICA6IGBncm9rIHRva2VuICR7dG9rZW4ubmFtZX1bJHt0b2tlbi5pZH1dIHZpZGVvIGVsaWdpYmxlOiB6ZXJvIGRhdGEgcmV0ZW50aW9uIG9mZiAoJHtzb3VyY2V9KWAsXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBgeC16ZXJvLWRhdGEtcmV0ZW50aW9uOiB0cnVlYCBjb25maXJtcyBaRFI7IGl0cyBhYnNlbmNlIG5ldmVyIGNsZWFycyBpdCAobmV4dCAvdXNlciByZWFkIGRvZXMpLiAqL1xuICBvYnNlcnZlUmVzcG9uc2VIZWFkZXJzKFxuICAgIHRva2VuOiBHcm9rVG9rZW5TdGF0ZSxcbiAgICBjcmVkZW50aWFsczogR3Jva0NyZWRlbnRpYWxzLFxuICAgIGhlYWRlcnM6IHsgemVyb0RhdGFSZXRlbnRpb24/OiBib29sZWFuIH0sXG4gICk6IHZvaWQge1xuICAgIGlmICh0b2tlbi5jcmVkZW50aWFscz8uYWNjZXNzVG9rZW4gIT09IGNyZWRlbnRpYWxzLmFjY2Vzc1Rva2VuKSByZXR1cm47XG5cbiAgICBpZiAoaGVhZGVycy56ZXJvRGF0YVJldGVudGlvbiA9PT0gdHJ1ZSkgdGhpcy5zZXRaZHIodG9rZW4sIHRydWUsIFwieC16ZXJvLWRhdGEtcmV0ZW50aW9uXCIpO1xuICB9XG5cbiAgbG9hZEJpbGxpbmcodG9rZW46IEdyb2tUb2tlblN0YXRlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcGVuZGluZyA9IHRoaXMucGVuZGluZ0JpbGxpbmcuZ2V0KHRva2VuLmlkKTtcblxuICAgIGlmIChwZW5kaW5nKSByZXR1cm4gcGVuZGluZztcbiAgICBjb25zdCBnZW5lcmF0aW9uID0gdG9rZW4uZ2VuZXJhdGlvbjtcbiAgICBjb25zdCBjcmVkZW50aWFscyA9IHRva2VuLmNyZWRlbnRpYWxzITtcblxuICAgIGNvbnN0IHByb21pc2UgPSByZWFkR3Jva0JpbGxpbmcoY3JlZGVudGlhbHMsIHsgZmV0Y2g6IHRoaXMuZmV0Y2hJbXBsIH0pXG4gICAgICAudGhlbigocmVzdWx0KSA9PiB7XG4gICAgICAgIGlmICh0aGlzLnRva2Vucy5nZXQodG9rZW4uaWQpICE9PSB0b2tlbiB8fCB0b2tlbi5nZW5lcmF0aW9uICE9PSBnZW5lcmF0aW9uKSByZXR1cm47XG4gICAgICAgIGNvbnN0IG5vdyA9IHRoaXMubm93KCk7XG4gICAgICAgIHRva2VuLmJpbGxpbmcgPSB7IHZhbHVlOiByZXN1bHQsIGZldGNoZWRBdDogbm93LCBnZW5lcmF0aW9uIH07XG5cbiAgICAgICAgaWYgKHJlc3VsdC5raW5kID09PSBcImxvb2t1cF9mYWlsZWRcIikge1xuICAgICAgICAgIHRva2VuLm92ZXJUaHJlc2hvbGQgPSBmYWxzZTtcbiAgICAgICAgICBsb2dnZXIud2FybihcInF1b3RhX3RocmVzaG9sZCBnYXRlOiBsb29rdXBfZmFpbF9vcGVuXCIsIHtcbiAgICAgICAgICAgIHRva2VuSWQ6IHRva2VuLmlkLFxuICAgICAgICAgICAgdG9rZW5OYW1lOiB0b2tlbi5uYW1lLFxuICAgICAgICAgICAgbG9va3VwUmVhc29uOiByZXN1bHQucmVhc29uLFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgbGltaXQgPSB0b2tlbi5xdW90YVRocmVzaG9sZCA/PyAxMDA7XG4gICAgICAgIGNvbnN0IG92ZXIgPSB0b2tlbi5xdW90YVRocmVzaG9sZCAhPT0gbnVsbCAmJiByZXN1bHQudXRpbGl6YXRpb25QY3QgPj0gdG9rZW4ucXVvdGFUaHJlc2hvbGQ7XG5cbiAgICAgICAgaWYgKG92ZXIgJiYgIXRva2VuLm92ZXJUaHJlc2hvbGQpIHtcbiAgICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBxdW90YV90aHJlc2hvbGQgZ2F0ZTogb3Zlcl90aHJlc2hvbGQgJHt0b2tlbi5uYW1lfVske3Rva2VuLmlkfV0gKCR7cmVzdWx0LnV0aWxpemF0aW9uUGN0fSUgPj0gJHt0b2tlbi5xdW90YVRocmVzaG9sZH0lKWAsXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghb3ZlciAmJiB0b2tlbi5vdmVyVGhyZXNob2xkKSB7XG4gICAgICAgICAgbG9nZ2VyLmluZm8oXG4gICAgICAgICAgICBgcXVvdGFfdGhyZXNob2xkIGdhdGU6IHJlY292ZXJlZCAke3Rva2VuLm5hbWV9WyR7dG9rZW4uaWR9XSAoJHtyZXN1bHQudXRpbGl6YXRpb25QY3R9JSA8ICR7dG9rZW4ucXVvdGFUaHJlc2hvbGR9JSlgLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICB0b2tlbi5vdmVyVGhyZXNob2xkID0gb3ZlcjtcblxuICAgICAgICBpZiAocmVzdWx0LnV0aWxpemF0aW9uUGN0IDwgbGltaXQgJiYgdG9rZW4ucXVvdGFCbG9ja3Muc2l6ZSkge1xuICAgICAgICAgIHRva2VuLnF1b3RhQmxvY2tzLmNsZWFyKCk7XG4gICAgICAgICAgbG9nZ2VyLmluZm8oXG4gICAgICAgICAgICBgcXVvdGFfdGhyZXNob2xkIGdhdGU6IGV4aGF1c3Rpb25fY2xlYXJlZCAke3Rva2VuLm5hbWV9WyR7dG9rZW4uaWR9XSAoJHtyZXN1bHQudXRpbGl6YXRpb25QY3R9JSA8ICR7bGltaXR9JSlgLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLnBlbmRpbmdCaWxsaW5nLmdldCh0b2tlbi5pZCkgPT09IHByb21pc2UpIHRoaXMucGVuZGluZ0JpbGxpbmcuZGVsZXRlKHRva2VuLmlkKTtcbiAgICAgIH0pO1xuXG4gICAgdGhpcy5wZW5kaW5nQmlsbGluZy5zZXQodG9rZW4uaWQsIHByb21pc2UpO1xuXG4gICAgcmV0dXJuIHByb21pc2U7XG4gIH1cblxuICAvLyAtLS0tIFNlbGVjdGlvbiAowqc1LjIpIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIHJlc2VydmUodG9rZW46IEdyb2tUb2tlblN0YXRlKTogUmVzZXJ2YXRpb24ge1xuICAgIHRva2VuLmluRmxpZ2h0Kys7XG4gICAgbGV0IHJlbGVhc2VkID0gZmFsc2U7XG5cbiAgICByZXR1cm4ge1xuICAgICAgdG9rZW4sXG4gICAgICByZWxlYXNlOiAoKSA9PiB7XG4gICAgICAgIGlmIChyZWxlYXNlZCkgcmV0dXJuO1xuICAgICAgICByZWxlYXNlZCA9IHRydWU7XG4gICAgICAgIHRva2VuLmluRmxpZ2h0ID0gTWF0aC5tYXgoMCwgdG9rZW4uaW5GbGlnaHQgLSAxKTtcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxuXG4gIGNoZWNrVmVyc2lvbkdhdGUocmVxdWVzdENsYXNzOiBHcm9rUmVxdWVzdENsYXNzKTogdm9pZCB7XG4gICAgaWYgKHJlcXVlc3RDbGFzcyAhPT0gXCJjaGF0XCIgfHwgIXRoaXMudmVyc2lvbkdhdGUpIHJldHVybjtcblxuICAgIGlmICh0aGlzLm5vdygpIC0gdGhpcy52ZXJzaW9uR2F0ZS50cmlwcGVkQXQgPCBHUk9LX1ZFUlNJT05fR0FURV9SRVBST0JFX01TKSB7XG4gICAgICB0aHJvdyBuZXcgR3Jva1ZlcnNpb25HYXRlRXJyb3IodGhpcy52ZXJzaW9uR2F0ZS5taW5DbGllbnRWZXJzaW9uKTtcbiAgICB9XG4gICAgLy8gUmUtcHJvYmU6IGxldCByZXF1ZXN0cyB0aHJvdWdoOyBhIGZyZXNoIDQyNiB0cmlwcyB0aGUgZ2F0ZSBhZ2Fpbi5cbiAgfVxuXG4gIGFzeW5jIHNlbGVjdFRva2VuKFxuICAgIHJlcXVlc3RDbGFzczogR3Jva1JlcXVlc3RDbGFzcyxcbiAgICBvcHRpb25zOiB7XG4gICAgICBwcmVmZXJyZWRUb2tlbklkPzogbnVtYmVyO1xuICAgICAgcmVxdWlyZVByZWZlcnJlZFRva2VuPzogYm9vbGVhbjtcbiAgICAgIGV4Y2x1ZGVkPzogUmVhZG9ubHlTZXQ8bnVtYmVyPjtcbiAgICAgIHNpZ25hbD86IEFib3J0U2lnbmFsO1xuICAgICAgZGVhZGxpbmU/OiBudW1iZXI7XG4gICAgICB1bmdhdGVkPzogYm9vbGVhbjtcbiAgICB9ID0ge30sXG4gICk6IFByb21pc2U8UmVzZXJ2YXRpb24+IHtcbiAgICB0aGlzLmNoZWNrVmVyc2lvbkdhdGUocmVxdWVzdENsYXNzKTtcbiAgICBjb25zdCBleGNsdWRlZCA9IG9wdGlvbnMuZXhjbHVkZWQgPz8gbmV3IFNldDxudW1iZXI+KCk7XG5cbiAgICBpZiAob3B0aW9ucy5wcmVmZXJyZWRUb2tlbklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IHByZWZlcnJlZCA9IHRoaXMudG9rZW5zLmdldChvcHRpb25zLnByZWZlcnJlZFRva2VuSWQpO1xuXG4gICAgICBpZiAocHJlZmVycmVkICYmICFleGNsdWRlZC5oYXMocHJlZmVycmVkLmlkKSkge1xuICAgICAgICBmb3IgKGxldCByb3VuZCA9IDA7IDsgcm91bmQrKykge1xuICAgICAgICAgIGF3YWl0IHRoaXMucHJlZmV0Y2gocHJlZmVycmVkLCBvcHRpb25zLnNpZ25hbCk7XG4gICAgICAgICAgb3B0aW9ucy5zaWduYWw/LnRocm93SWZBYm9ydGVkKCk7XG4gICAgICAgICAgY29uc3Qgbm93ID0gdGhpcy5ub3coKTtcbiAgICAgICAgICBjb25zdCByZWFzb24gPSB0aGlzLmluZWxpZ2liaWxpdHkocHJlZmVycmVkLCByZXF1ZXN0Q2xhc3MsIG5vdywgMCwgb3B0aW9ucy51bmdhdGVkKTtcblxuICAgICAgICAgIGlmICghcmVhc29uKSByZXR1cm4gdGhpcy5yZXNlcnZlKHByZWZlcnJlZCk7XG5cbiAgICAgICAgICBpZiAoIW9wdGlvbnMucmVxdWlyZVByZWZlcnJlZFRva2VuKSBicmVhaztcbiAgICAgICAgICAvLyBBbiBleGFjdCB0YXJnZXQgaGFzIG5vIGFsdGVybmF0aXZlOiB3YWl0IG91dCBvbmUgZmFpbGVkIHJlZnJlc2gsIGxpa2UgYSBjb29sZG93bi5cbiAgICAgICAgICBjb25zdCB3YWl0TXMgPSBwcmVmZXJyZWQucmVmcmVzaEZhaWxlZFVudGlsIC0gbm93O1xuXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgcmVhc29uID09PSBcInJlZnJlc2hfZmFpbGVkXCIgJiZcbiAgICAgICAgICAgIHJvdW5kID09PSAwICYmXG4gICAgICAgICAgICB3YWl0TXMgPD0gR1JPS19NQVhfQ09PTERPV05fV0FJVF9NUyAmJlxuICAgICAgICAgICAgKG9wdGlvbnMuZGVhZGxpbmUgPT09IHVuZGVmaW5lZCB8fCBub3cgKyB3YWl0TXMgPCBvcHRpb25zLmRlYWRsaW5lKVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5zbGVlcChNYXRoLm1heCgwLCB3YWl0TXMpLCBvcHRpb25zLnNpZ25hbCk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB0aHJvdyB0aGlzLnByZWZlcnJlZEVycm9yKHByZWZlcnJlZCwgcmVhc29uKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChvcHRpb25zLnJlcXVpcmVQcmVmZXJyZWRUb2tlbikge1xuICAgICAgICB0aHJvdyBuZXcgR3Jva05vV29ya2Vyc0Vycm9yKFxuICAgICAgICAgIGBQcmVmZXJyZWQgZ3JvayB0b2tlbiAke29wdGlvbnMucHJlZmVycmVkVG9rZW5JZH0gaXMgbm90IGF2YWlsYWJsZWAsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgZm9yIChsZXQgcm91bmQgPSAwOyA7IHJvdW5kKyspIHtcbiAgICAgIGNvbnN0IGNhbmRpZGF0ZXMgPSBbLi4udGhpcy50b2tlbnMudmFsdWVzKCldLmZpbHRlcigodG9rZW4pID0+ICFleGNsdWRlZC5oYXModG9rZW4uaWQpKTtcbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKGNhbmRpZGF0ZXMubWFwKCh0b2tlbikgPT4gdGhpcy5wcmVmZXRjaCh0b2tlbiwgb3B0aW9ucy5zaWduYWwpKSk7XG4gICAgICBvcHRpb25zLnNpZ25hbD8udGhyb3dJZkFib3J0ZWQoKTtcbiAgICAgIC8vIFN5bmNocm9ub3VzIGZyb20gaGVyZSB0byByZXNlcnZlKCk6IGNvbmN1cnJlbnQgc2VsZWN0aW9ucyBjYW5ub3Qgb3ZlcnNob290IHRoZSBjYXAuXG4gICAgICBjb25zdCBub3cgPSB0aGlzLm5vdygpO1xuICAgICAgY29uc3QgcmVhc29ucyA9IG5ldyBNYXA8R3Jva1Rva2VuU3RhdGUsIEluZWxpZ2libGVSZWFzb24+KCk7XG4gICAgICBjb25zdCBlbGlnaWJsZSA9IG5ldyBTZXQ8bnVtYmVyPigpO1xuXG4gICAgICBmb3IgKGNvbnN0IHRva2VuIG9mIGNhbmRpZGF0ZXMpIHtcbiAgICAgICAgY29uc3QgcmVhc29uID0gdGhpcy5pbmVsaWdpYmlsaXR5KHRva2VuLCByZXF1ZXN0Q2xhc3MsIG5vdyk7XG5cbiAgICAgICAgaWYgKHJlYXNvbikgcmVhc29ucy5zZXQodG9rZW4sIHJlYXNvbik7XG4gICAgICAgIGVsc2UgZWxpZ2libGUuYWRkKHRva2VuLmlkKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGVsaWdpYmxlLnNpemUpIHtcbiAgICAgICAgY29uc3Qgc2VsZWN0ZWQgPSB0aGlzLnNlbGVjdG9yLnNlbGVjdChlbGlnaWJsZSk7XG4gICAgICAgIGNvbnN0IHRva2VuID0gc2VsZWN0ZWQgPT09IG51bGwgPyB1bmRlZmluZWQgOiB0aGlzLnRva2Vucy5nZXQoc2VsZWN0ZWQpO1xuXG4gICAgICAgIGlmICh0b2tlbikgcmV0dXJuIHRoaXMucmVzZXJ2ZSh0b2tlbik7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHByZXNlbnQgPSBuZXcgU2V0KHJlYXNvbnMudmFsdWVzKCkpO1xuXG4gICAgICBpZiAocHJlc2VudC5oYXMoXCJjYXBhY2l0eVwiKSkgdGhyb3cgbmV3IEdyb2tDYXBhY2l0eUVycm9yKCk7XG5cbiAgICAgIGlmIChwcmVzZW50LmhhcyhcImNvb2xkb3duXCIpIHx8IHByZXNlbnQuaGFzKFwicmVmcmVzaF9mYWlsZWRcIikpIHtcbiAgICAgICAgLy8gQSBmYWlsZWQgcmVmcmVzaCBpcyBhIHNob3J0IHRyYW5zaWVudCBjb29sZG93bjogd2FpdCBvbmNlLCB0aGVuIHJlLXJ1biB0aGUgbG9va3Vwcy5cbiAgICAgICAgY29uc3QgdW50aWwgPSAodG9rZW46IEdyb2tUb2tlblN0YXRlLCByZWFzb246IEluZWxpZ2libGVSZWFzb24pID0+XG4gICAgICAgICAgcmVhc29uID09PSBcImNvb2xkb3duXCJcbiAgICAgICAgICAgID8gKHRva2VuLmNvb2xkb3ducy5nZXQocmVxdWVzdENsYXNzKSA/PyBub3cpXG4gICAgICAgICAgICA6IHJlYXNvbiA9PT0gXCJyZWZyZXNoX2ZhaWxlZFwiXG4gICAgICAgICAgICAgID8gdG9rZW4ucmVmcmVzaEZhaWxlZFVudGlsXG4gICAgICAgICAgICAgIDogTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuXG4gICAgICAgIGNvbnN0IHNvb25lc3QgPSBNYXRoLm1pbiguLi5bLi4ucmVhc29uc10ubWFwKChbdG9rZW4sIHJlYXNvbl0pID0+IHVudGlsKHRva2VuLCByZWFzb24pKSk7XG4gICAgICAgIGNvbnN0IHdhaXRNcyA9IE1hdGgubWF4KDAsIE1hdGgubWluKHNvb25lc3QgLSBub3csIEdST0tfTUFYX0NPT0xET1dOX1dBSVRfTVMpKTtcbiAgICAgICAgY29uc3Qgd2l0aGluRGVhZGxpbmUgPSBvcHRpb25zLmRlYWRsaW5lID09PSB1bmRlZmluZWQgfHwgbm93ICsgd2FpdE1zIDwgb3B0aW9ucy5kZWFkbGluZTtcblxuICAgICAgICBpZiAocm91bmQgPT09IDAgJiYgd2l0aGluRGVhZGxpbmUpIHtcbiAgICAgICAgICBhd2FpdCB0aGlzLnNsZWVwKHdhaXRNcywgb3B0aW9ucy5zaWduYWwpO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgY29vbGluZyA9IFsuLi5yZWFzb25zXS5maWx0ZXIoKFssIHJlYXNvbl0pID0+IHJlYXNvbiA9PT0gXCJjb29sZG93blwiKTtcblxuICAgICAgICBpZiAoY29vbGluZy5sZW5ndGgpIHtcbiAgICAgICAgICBjb25zdCBzb29uZXN0Q29vbGRvd24gPSBNYXRoLm1pbiguLi5jb29saW5nLm1hcCgoW3Rva2VuXSkgPT4gdW50aWwodG9rZW4sIFwiY29vbGRvd25cIikpKTtcbiAgICAgICAgICB0aHJvdyBuZXcgR3Jva1JhdGVMaW1pdGVkRXJyb3IoTWF0aC5tYXgoMSwgTWF0aC5jZWlsKChzb29uZXN0Q29vbGRvd24gLSBub3cpIC8gMTAwMCkpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGZhaWxlZCA9IFsuLi5yZWFzb25zXS5maW5kKChbLCByZWFzb25dKSA9PiByZWFzb24gPT09IFwicmVmcmVzaF9mYWlsZWRcIik7XG4gICAgICAgIHRocm93IG5ldyBHcm9rQ3JlZGVudGlhbFJlZnJlc2hFcnJvcihmYWlsZWQ/LlswXS5yZWZyZXNoRmFpbHVyZSA/PyBcInJlZnJlc2ggZmFpbGVkXCIsIGZhbHNlKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHByZXNlbnQuaGFzKFwicXVvdGFcIikpIHtcbiAgICAgICAgY29uc3Qgb3ZlciA9IFsuLi5yZWFzb25zXS5mbGF0TWFwKChbdG9rZW4sIHJlYXNvbl0pID0+XG4gICAgICAgICAgcmVhc29uID09PSBcInF1b3RhXCJcbiAgICAgICAgICAgID8gW3sgbmFtZTogdG9rZW4ubmFtZSwgdGhyZXNob2xkOiB0b2tlbi5vdmVyVGhyZXNob2xkID8gdG9rZW4ucXVvdGFUaHJlc2hvbGQgOiBudWxsIH1dXG4gICAgICAgICAgICA6IFtdLFxuICAgICAgICApO1xuXG4gICAgICAgIGxvZ2dlci53YXJuKFwicXVvdGFfdGhyZXNob2xkIGdhdGU6IGFsbF9leGNlZWRlZFwiLCB7XG4gICAgICAgICAgcHJvdmlkZXI6IFwiZ3Jva1wiLFxuICAgICAgICAgIG92ZXJUaHJlc2hvbGRUb2tlbnM6IG92ZXIsXG4gICAgICAgIH0pO1xuICAgICAgICB0aHJvdyBuZXcgUXVvdGFUaHJlc2hvbGRFeGNlZWRlZEVycm9yKHF1b3RhTWVzc2FnZShvdmVyKSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChwcmVzZW50LmhhcyhcIm9uX2RlbWFuZFwiKSkge1xuICAgICAgICB0aHJvdyBuZXcgR3Jva09uRGVtYW5kRXhjbHVkZWRFcnJvcih0b2tlbk5hbWVzV2l0aFJlYXNvbihyZWFzb25zLCBcIm9uX2RlbWFuZFwiKSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChwcmVzZW50LmhhcyhcInpkclwiKSkge1xuICAgICAgICB0aHJvdyBuZXcgR3Jva1pkclZpZGVvVW5hdmFpbGFibGVFcnJvcih0b2tlbk5hbWVzV2l0aFJlYXNvbihyZWFzb25zLCBcInpkclwiKSk7XG4gICAgICB9XG5cbiAgICAgIHRocm93IG5ldyBHcm9rTm9Xb3JrZXJzRXJyb3IoKTtcbiAgICB9XG4gIH1cblxuICBwcmVmZXJyZWRFcnJvcih0b2tlbjogR3Jva1Rva2VuU3RhdGUsIHJlYXNvbjogSW5lbGlnaWJsZVJlYXNvbik6IEVycm9yIHtcbiAgICBjb25zdCBub3cgPSB0aGlzLm5vdygpO1xuXG4gICAgaWYgKHJlYXNvbiA9PT0gXCJxdW90YVwiKSB7XG4gICAgICByZXR1cm4gbmV3IFF1b3RhVGhyZXNob2xkRXhjZWVkZWRFcnJvcihcbiAgICAgICAgcXVvdGFNZXNzYWdlKFtcbiAgICAgICAgICB7IG5hbWU6IHRva2VuLm5hbWUsIHRocmVzaG9sZDogdG9rZW4ub3ZlclRocmVzaG9sZCA/IHRva2VuLnF1b3RhVGhyZXNob2xkIDogbnVsbCB9LFxuICAgICAgICBdKSxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKHJlYXNvbiA9PT0gXCJjb29sZG93blwiKSB7XG4gICAgICBjb25zdCB1bnRpbCA9IE1hdGgubWF4KC4uLnRva2VuLmNvb2xkb3ducy52YWx1ZXMoKSk7XG5cbiAgICAgIHJldHVybiBuZXcgR3Jva1JhdGVMaW1pdGVkRXJyb3IoTWF0aC5tYXgoMSwgTWF0aC5jZWlsKCh1bnRpbCAtIG5vdykgLyAxMDAwKSkpO1xuICAgIH1cblxuICAgIGlmIChyZWFzb24gPT09IFwiY2FwYWNpdHlcIikgcmV0dXJuIG5ldyBHcm9rQ2FwYWNpdHlFcnJvcigpO1xuXG4gICAgaWYgKHJlYXNvbiA9PT0gXCJyZWZyZXNoX2ZhaWxlZFwiKVxuICAgICAgcmV0dXJuIG5ldyBHcm9rQ3JlZGVudGlhbFJlZnJlc2hFcnJvcih0b2tlbi5yZWZyZXNoRmFpbHVyZSA/PyBcInJlZnJlc2ggZmFpbGVkXCIsIGZhbHNlKTtcblxuICAgIGlmIChyZWFzb24gPT09IFwib25fZGVtYW5kXCIpIHJldHVybiBuZXcgR3Jva09uRGVtYW5kRXhjbHVkZWRFcnJvcihbdG9rZW4ubmFtZV0pO1xuXG4gICAgaWYgKHJlYXNvbiA9PT0gXCJ6ZHJcIikgcmV0dXJuIG5ldyBHcm9rWmRyVmlkZW9VbmF2YWlsYWJsZUVycm9yKFt0b2tlbi5uYW1lXSk7XG5cbiAgICByZXR1cm4gbmV3IEdyb2tOb1dvcmtlcnNFcnJvcihgUHJlZmVycmVkIGdyb2sgdG9rZW4gJHt0b2tlbi5pZH0gaXMgbm90IGF2YWlsYWJsZSAoJHtyZWFzb259KWApO1xuICB9XG5cbiAgLy8gLS0tLSBGYWlsdXJlIGhhbmRsaW5nICjCpzUuNCkgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIGFwcGx5SHR0cEZhaWx1cmUoXG4gICAgdG9rZW46IEdyb2tUb2tlblN0YXRlLFxuICAgIHJlcXVlc3RDbGFzczogR3Jva1JlcXVlc3RDbGFzcyxcbiAgICBlcnJvcjogR3Jva0h0dHBFcnJvcixcbiAgKTogdm9pZCB7XG4gICAgY29uc3Qgbm93ID0gdGhpcy5ub3coKTtcbiAgICBjb25zdCB7IGNsYXNzaWZpY2F0aW9uIH0gPSBlcnJvcjtcblxuICAgIGlmIChjbGFzc2lmaWNhdGlvbi5raW5kID09PSBcInJhdGVfbGltaXRlZFwiKSB7XG4gICAgICBjb25zdCBjb29sZG93bk1zID0gTWF0aC5taW4oXG4gICAgICAgIGNsYXNzaWZpY2F0aW9uLnJldHJ5QWZ0ZXJTZWMgPT09IHVuZGVmaW5lZFxuICAgICAgICAgID8gR1JPS19ERUZBVUxUX0NPT0xET1dOX01TXG4gICAgICAgICAgOiBjbGFzc2lmaWNhdGlvbi5yZXRyeUFmdGVyU2VjICogMTAwMCxcbiAgICAgICAgR1JPS19NQVhfQ09PTERPV05fTVMsXG4gICAgICApO1xuXG4gICAgICB0b2tlbi5jb29sZG93bnMuc2V0KHJlcXVlc3RDbGFzcywgbm93ICsgY29vbGRvd25Ncyk7XG4gICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgYGdyb2sgdG9rZW4gJHt0b2tlbi5uYW1lfVske3Rva2VuLmlkfV0gY29vbGRvd24gJHtjb29sZG93bk1zfW1zICgke3JlcXVlc3RDbGFzc30pYCxcbiAgICAgICk7XG4gICAgfSBlbHNlIGlmIChjbGFzc2lmaWNhdGlvbi5raW5kID09PSBcInF1b3RhX2V4aGF1c3RlZFwiKSB7XG4gICAgICBjb25zdCBiaWxsaW5nID0gdG9rZW4uYmlsbGluZz8udmFsdWU7XG4gICAgICBjb25zdCBwZXJpb2RFbmQgPSBiaWxsaW5nPy5raW5kID09PSBcIm9rXCIgPyBiaWxsaW5nLnBlcmlvZEVuZE1zIDogbnVsbDtcblxuICAgICAgY29uc3QgdW50aWwgPVxuICAgICAgICBwZXJpb2RFbmQgIT09IG51bGwgJiYgcGVyaW9kRW5kID4gbm93ID8gcGVyaW9kRW5kIDogbm93ICsgR1JPS19RVU9UQV9CTE9DS19GQUxMQkFDS19NUztcblxuICAgICAgdG9rZW4ucXVvdGFCbG9ja3Muc2V0KHJlcXVlc3RDbGFzcywgdW50aWwpO1xuICAgICAgdG9rZW4uYmlsbGluZyA9IHVuZGVmaW5lZDtcbiAgICAgIC8vIEEgYmlsbGluZyByZWFkIHJpZ2h0IGFmdGVyIHRoZSA0MjkgbWF5IHN0aWxsIGxhZyB0aGUgcHJveHk7IHdhaXQgb25lIGNhY2hlIHBlcmlvZC5cbiAgICAgIHRva2VuLmJpbGxpbmdOb3RCZWZvcmUgPSBub3cgKyBHUk9LX0xPT0tVUF9DQUNIRV9UVExfTVM7XG4gICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgYHF1b3RhX3RocmVzaG9sZCBnYXRlOiBleGhhdXN0ZWQgJHt0b2tlbi5uYW1lfVske3Rva2VuLmlkfV0gdW50aWwgJHtuZXcgRGF0ZSh1bnRpbCkudG9JU09TdHJpbmcoKX0gKCR7Y2xhc3NpZmljYXRpb24uY29kZSA/PyBcIjQyOVwifSlgLFxuICAgICAgKTtcbiAgICB9IGVsc2UgaWYgKGNsYXNzaWZpY2F0aW9uLmtpbmQgPT09IFwiZW50aXRsZW1lbnRcIiAmJiByZXF1ZXN0Q2xhc3MgIT09IFwiY2hhdFwiKSB7XG4gICAgICB0b2tlbi5lbnRpdGxlbWVudEJsb2Nrcy5zZXQocmVxdWVzdENsYXNzLCBub3cgKyBHUk9LX0VOVElUTEVNRU5UX0JMT0NLX01TKTtcbiAgICB9IGVsc2UgaWYgKGNsYXNzaWZpY2F0aW9uLmtpbmQgPT09IFwidmVyc2lvbl9nYXRlXCIgJiYgcmVxdWVzdENsYXNzID09PSBcImNoYXRcIikge1xuICAgICAgdGhpcy52ZXJzaW9uR2F0ZSA9IHsgdHJpcHBlZEF0OiBub3csIG1pbkNsaWVudFZlcnNpb246IGNsYXNzaWZpY2F0aW9uLm1pbkNsaWVudFZlcnNpb24gfTtcbiAgICAgIGxvZ2dlci5lcnJvcihcbiAgICAgICAgYGdyb2sgY2hhdCBwcm94eSB2ZXJzaW9uIGdhdGUgdHJpcHBlZDogJHtHUk9LX0JVSUxEX1ZFUlNJT059IDwgJHtjbGFzc2lmaWNhdGlvbi5taW5DbGllbnRWZXJzaW9uID8/IFwiP1wifWAsXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIGNsYXNzaWZ5RmFpbHVyZShcbiAgICB0b2tlbjogR3Jva1Rva2VuU3RhdGUsXG4gICAgcmVxdWVzdENsYXNzOiBHcm9rUmVxdWVzdENsYXNzLFxuICAgIGNhdXNlOiB1bmtub3duLFxuICAgIHByZXNlbnRlZDogR3Jva0NyZWRlbnRpYWxzIHwgdW5kZWZpbmVkLFxuICAgIHJlZnJlc2hlZFRoaXNSZXF1ZXN0OiBib29sZWFuLFxuICAgIHJlZnJlc2hCdWRnZXRMZWZ0OiBib29sZWFuLFxuICApOiBGYWlsdXJlQWN0aW9uIHtcbiAgICBpZiAoY2F1c2UgaW5zdGFuY2VvZiBHcm9rSWRsZVRpbWVvdXRFcnJvcikge1xuICAgICAgLy8gUmVhY2hlZCBvbmx5IGJlZm9yZSBhbnkgZXZlbnQ6IG5vIHVwc3RyZWFtIGV4ZWN1dGlvbiB3YXMgb2JzZXJ2ZWQuXG4gICAgICByZXR1cm4gcmVxdWVzdENsYXNzID09PSBcImNoYXRcIlxuICAgICAgICA/IHsga2luZDogXCJmYWlsb3ZlclwiLCBlcnJvcjogY2F1c2UsIHRyYW5zaWVudDogdHJ1ZSB9XG4gICAgICAgIDogeyBraW5kOiBcImZhaWxcIiwgZXJyb3I6IGNhdXNlIH07XG4gICAgfVxuXG4gICAgaWYgKGNhdXNlIGluc3RhbmNlb2YgR3Jva0NyZWRlbnRpYWxSZWZyZXNoRXJyb3IpIHtcbiAgICAgIC8vIE5vdGhpbmcgcmVhY2hlZCB1cHN0cmVhbTsgYW5vdGhlciB0b2tlbiBjYW4gc2VydmUgdGhlIHJlcXVlc3QuXG4gICAgICAvLyBKdWRnZWQgYWdhaW5zdCB0aGUgY3JlZGVudGlhbHMgdGhpcyByZWZyZXNoIHN0YXJ0ZWQgZnJvbTogaWYgYSBjb25jdXJyZW50IHJlZnJlc2ggb3IgYVxuICAgICAgLy8gc3Vic2NyaWJlciB1cGRhdGUgYWxyZWFkeSByZXBsYWNlZCB0aGVtLCB0aGUgZmFpbHVyZSBzYXlzIG5vdGhpbmcgYWJvdXQgdGhlIG5ldyBvbmVzLlxuICAgICAgaWYgKHByZXNlbnRlZCkge1xuICAgICAgICBpZiAoY2F1c2UucmVhdXRoUmVxdWlyZWQpXG4gICAgICAgICAgdGhpcy5tYXJrUmVhdXRoUmVxdWlyZWQodG9rZW4sIHByZXNlbnRlZCwgcmVhdXRoUmVhc29uQ29kZShvYXV0aEVycm9yT2YoY2F1c2UuY2F1c2UpKSk7XG4gICAgICAgIGVsc2UgdGhpcy5tYXJrUmVmcmVzaEZhaWxlZCh0b2tlbiwgcHJlc2VudGVkLCBjYXVzZS5jYXVzZSk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7IGtpbmQ6IFwiZmFpbG92ZXJcIiwgZXJyb3I6IGNhdXNlLCB0cmFuc2llbnQ6IGZhbHNlIH07XG4gICAgfVxuXG4gICAgaWYgKCEoY2F1c2UgaW5zdGFuY2VvZiBHcm9rSHR0cEVycm9yKSkgcmV0dXJuIHsga2luZDogXCJmYWlsXCIsIGVycm9yOiBjYXVzZSB9O1xuXG4gICAgaWYgKHJlcXVlc3RDbGFzcyA9PT0gXCJ2aWRlb1wiICYmIGlzR3Jva1pkclZpZGVvUmVqZWN0aW9uKGNhdXNlKSkge1xuICAgICAgLy8gVGhlIFpEUiBzaWduYWwgd2FzIHN0YWxlIG9yIHVua25vd24uIFRoZSA0MDAgaXMgYSBkZWZpbml0ZSByZWplY3Rpb24gKG5vIGpvYiB3YXMgY3JlYXRlZCksXG4gICAgICAvLyBzbyB0aGUgc3RhcnQgbWF5IG1vdmUgdG8gYW5vdGhlciB0b2tlbiB1bmRlciB0aGUgbWVkaWEgZW50aXRsZW1lbnQgcnVsZXMuXG4gICAgICB0aGlzLnNldFpkcih0b2tlbiwgdHJ1ZSwgXCJ1cHN0cmVhbSB2aWRlbyA0MDBcIik7XG4gICAgICB0b2tlbi5lbnRpdGxlbWVudEJsb2Nrcy5zZXQoXCJ2aWRlb1wiLCB0aGlzLm5vdygpICsgR1JPS19FTlRJVExFTUVOVF9CTE9DS19NUyk7XG5cbiAgICAgIHJldHVybiB7IGtpbmQ6IFwiZmFpbG92ZXJcIiwgZXJyb3I6IGNhdXNlLCB0cmFuc2llbnQ6IGZhbHNlIH07XG4gICAgfVxuXG4gICAgdGhpcy5hcHBseUh0dHBGYWlsdXJlKHRva2VuLCByZXF1ZXN0Q2xhc3MsIGNhdXNlKTtcbiAgICBjb25zdCB7IGNsYXNzaWZpY2F0aW9uIH0gPSBjYXVzZTtcblxuICAgIGlmIChjbGFzc2lmaWNhdGlvbi5raW5kID09PSBcInZlcnNpb25fZ2F0ZVwiKSB7XG4gICAgICByZXR1cm4geyBraW5kOiBcImZhaWxcIiwgZXJyb3I6IG5ldyBHcm9rVmVyc2lvbkdhdGVFcnJvcihjbGFzc2lmaWNhdGlvbi5taW5DbGllbnRWZXJzaW9uKSB9O1xuICAgIH1cblxuICAgIGlmIChjbGFzc2lmaWNhdGlvbi5zaG91bGRSZXRyeSA9PT0gZmFsc2UpIHJldHVybiB7IGtpbmQ6IFwiZmFpbFwiLCBlcnJvcjogY2F1c2UgfTtcblxuICAgIHN3aXRjaCAoY2xhc3NpZmljYXRpb24ua2luZCkge1xuICAgICAgY2FzZSBcImF1dGhcIjpcbiAgICAgICAgaWYgKCFwcmVzZW50ZWQgfHwgIXRva2VuLmNyZWRlbnRpYWxzKSB7XG4gICAgICAgICAgcmV0dXJuIHsga2luZDogXCJmYWlsb3ZlclwiLCBlcnJvcjogY2F1c2UsIHRyYW5zaWVudDogZmFsc2UgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0b2tlbi5jcmVkZW50aWFscy5hY2Nlc3NUb2tlbiAhPT0gcHJlc2VudGVkLmFjY2Vzc1Rva2VuKSB7XG4gICAgICAgICAgLy8gU3RhbGUgNDAxOiBhIG5ld2VyIGJlYXJlciB3YXMgaW5zdGFsbGVkIHdoaWxlIHRoaXMgYXR0ZW1wdCB3YXMgaW4gZmxpZ2h0LiBJdCBzYXlzXG4gICAgICAgICAgLy8gbm90aGluZyBhYm91dCB0aGUgY3VycmVudCBjcmVkZW50aWFscywgc28gbmVpdGhlciByZWZyZXNoIG5vciBtYXJrIHRoZW07IHJldHJ5IHRoZW0uXG4gICAgICAgICAgcmV0dXJuIHsga2luZDogXCJyZXRyeVwiLCBlcnJvcjogY2F1c2UgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZWZyZXNoZWRUaGlzUmVxdWVzdCkge1xuICAgICAgICAgIC8vIEEgNDAxIHdpdGgganVzdC1yZWZyZXNoZWQgY3JlZGVudGlhbHM6IG9ubHkgYSBuZXcgbG9naW4gcmVjb3ZlcnMuXG4gICAgICAgICAgdGhpcy5tYXJrUmVhdXRoUmVxdWlyZWQodG9rZW4sIHByZXNlbnRlZCwgXCJncm9rOnVuYXV0aG9yaXplZF9hZnRlcl9yZWZyZXNoXCIpO1xuXG4gICAgICAgICAgcmV0dXJuIHsga2luZDogXCJmYWlsb3ZlclwiLCBlcnJvcjogY2F1c2UsIHRyYW5zaWVudDogZmFsc2UgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZWZyZXNoQnVkZ2V0TGVmdCkgcmV0dXJuIHsga2luZDogXCJyZWZyZXNoXCIsIHByZXNlbnRlZCB9O1xuXG4gICAgICAgIHJldHVybiB7IGtpbmQ6IFwiZmFpbG92ZXJcIiwgZXJyb3I6IGNhdXNlLCB0cmFuc2llbnQ6IGZhbHNlIH07XG4gICAgICBjYXNlIFwicmF0ZV9saW1pdGVkXCI6XG4gICAgICBjYXNlIFwicXVvdGFfZXhoYXVzdGVkXCI6XG4gICAgICAgIHJldHVybiB7IGtpbmQ6IFwiZmFpbG92ZXJcIiwgZXJyb3I6IGNhdXNlLCB0cmFuc2llbnQ6IGZhbHNlIH07XG4gICAgICBjYXNlIFwiZW50aXRsZW1lbnRcIjpcbiAgICAgICAgLy8gQ2hhdCA0MDMgaXMgcGVybWFuZW50IGZvciB0aGUgcmVxdWVzdCAoZ3Jvay1idWlsZCBydWxlKTsgbWVkaWEgZmFpbHMgb3ZlciBvbmNlLlxuICAgICAgICByZXR1cm4gcmVxdWVzdENsYXNzID09PSBcImNoYXRcIlxuICAgICAgICAgID8geyBraW5kOiBcImZhaWxcIiwgZXJyb3I6IGNhdXNlIH1cbiAgICAgICAgICA6IHsga2luZDogXCJmYWlsb3ZlclwiLCBlcnJvcjogY2F1c2UsIHRyYW5zaWVudDogZmFsc2UgfTtcbiAgICAgIGNhc2UgXCJ0cmFuc2llbnRcIjpcbiAgICAgICAgaWYgKHJlcXVlc3RDbGFzcyA9PT0gXCJjaGF0XCIpIHJldHVybiB7IGtpbmQ6IFwiZmFpbG92ZXJcIiwgZXJyb3I6IGNhdXNlLCB0cmFuc2llbnQ6IHRydWUgfTtcblxuICAgICAgICAvLyBNZWRpYS92b2ljZSBQT1NUcyBhcmUgbmV2ZXIgcmVwbGF5ZWQgb25jZSB0aGUgYm9keSBtYXkgaGF2ZSBiZWVuIGV4ZWN1dGVkLiBUaGUgbWVkaWFcbiAgICAgICAgLy8gYW5kIHZvaWNlIGNsaWVudHMgc3VyZmFjZSBzdWNoIGZhaWx1cmVzIGFzIEdyb2tNZWRpYURlbGl2ZXJ5RXJyb3IsIEdyb2tNZWRpYVRpbWVvdXRFcnJvclxuICAgICAgICAvLyBvciBgc2hvdWxkUmV0cnk6IGZhbHNlYCAoYWxsIGhhbmRsZWQgYWJvdmUpOyBhIHN0YXR1cy0wIEdyb2tIdHRwRXJyb3IgdGhhdCByZWFjaGVzIHRoaXNcbiAgICAgICAgLy8gcG9pbnQgaXMgYSBjb25uZWN0IGZhaWx1cmUgdGhhdCBwcm92YWJseSBoYXBwZW5lZCBiZWZvcmUgYW55IGJ5dGUgd2FzIHNlbnQsIHNvIGl0IGZhaWxzXG4gICAgICAgIC8vIG92ZXIgb25jZSAodHJhbnNpZW50OiBHUk9LX01BWF9UUkFOU0lFTlRfRkFJTE9WRVJTKS4gQW4gSFRUUCA1eHggbmV2ZXIgZmFpbHMgb3Zlci5cbiAgICAgICAgcmV0dXJuIGNhdXNlLnN0YXR1cyA9PT0gMFxuICAgICAgICAgID8geyBraW5kOiBcImZhaWxvdmVyXCIsIGVycm9yOiBjYXVzZSwgdHJhbnNpZW50OiB0cnVlIH1cbiAgICAgICAgICA6IHsga2luZDogXCJmYWlsXCIsIGVycm9yOiBjYXVzZSB9O1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIHsga2luZDogXCJmYWlsXCIsIGVycm9yOiBjYXVzZSB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNYXJrcyBgdG9rZW5gIGRlYWQgaW4gbWVtb3J5IGFuZCwgb24gdGhlIGZpcnN0IHRyYW5zaXRpb24sIHBlcnNpc3RzIGB0b2tlbnMucmVhdXRoX3JlcXVpcmVkYC5cbiAgICogYGNyZWRlbnRpYWxzYCBhcmUgdGhlIG9uZXMgdGhlIGZhaWxlZCBhdHRlbXB0IHByZXNlbnRlZDsgYSB0b2tlbiBhbHJlYWR5IGhvbGRpbmcgbmV3ZXIgb25lc1xuICAgKiAoYW5vdGhlciBiZWFyZXIgb3IgcmVmcmVzaCB0b2tlbikgaXMgbGVmdCBhbG9uZS5cbiAgICovXG4gIG1hcmtSZWF1dGhSZXF1aXJlZChcbiAgICB0b2tlbjogR3Jva1Rva2VuU3RhdGUsXG4gICAgY3JlZGVudGlhbHM6IEdyb2tDcmVkZW50aWFscyxcbiAgICByZWFzb25Db2RlOiBzdHJpbmcsXG4gICk6IHZvaWQge1xuICAgIGlmICh0aGlzLnRva2Vucy5nZXQodG9rZW4uaWQpICE9PSB0b2tlbikgcmV0dXJuO1xuXG4gICAgaWYgKCFzYW1lQ3JlZGVudGlhbHModG9rZW4uY3JlZGVudGlhbHMsIGNyZWRlbnRpYWxzKSkgcmV0dXJuO1xuXG4gICAgaWYgKHRva2VuLnJlYXV0aFJlcXVpcmVkKSByZXR1cm47XG4gICAgdG9rZW4ucmVhdXRoUmVxdWlyZWQgPSB0cnVlO1xuICAgIGxvZ2dlci53YXJuKGBncm9rIHRva2VuICR7dG9rZW4ubmFtZX1bJHt0b2tlbi5pZH1dIHJlcXVpcmVzIHJlLWF1dGhlbnRpY2F0aW9uICgke3JlYXNvbkNvZGV9KWApO1xuICAgIHZvaWQgdGhpcy5yZXBvcnRSZWF1dGhSZXF1aXJlZChcbiAgICAgIHsgaWQ6IHRva2VuLmlkLCBuYW1lOiB0b2tlbi5uYW1lLCBjcmVkZW50aWFscyB9LFxuICAgICAgcmVhc29uQ29kZSxcbiAgICApLmNhdGNoKChjYXVzZTogdW5rbm93bikgPT4ge1xuICAgICAgbG9nZ2VyLndhcm4oXG4gICAgICAgIGBncm9rIHRva2VuICR7dG9rZW4ubmFtZX1bJHt0b2tlbi5pZH1dIHJlYXV0aF9yZXF1aXJlZCBwZXJzaXN0IGZhaWxlZDogJHtjYXVzZSBpbnN0YW5jZW9mIEVycm9yID8gY2F1c2UubmFtZSA6IFwidW5rbm93blwifWAsXG4gICAgICApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgYGF0dGVtcHRgIHdpdGggdGhlIMKnNS4xIGF0dGVtcHQgYnVkZ2V0OiBvbmUgZm9yY2VkIHJlZnJlc2gsIGF0IG1vc3QgR1JPS19NQVhfSE9QU1xuICAgKiBmYWlsb3ZlcnMgKG9uZSBvZiB0aGVtIHRyYW5zaWVudCkuIGBjYW5SZXRyeWAgcmVwb3J0cyB3aGV0aGVyIHVwc3RyZWFtIGV4ZWN1dGlvbiBtYXkgaGF2ZVxuICAgKiBzdGFydGVkLCBpbiB3aGljaCBjYXNlIG5vdGhpbmcgaXMgcmV0cmllZC5cbiAgICpcbiAgICogVGhlIHJlc2VydmF0aW9uIGlzIGhlbGQgZnJvbSBzZWxlY3Rpb24gdW50aWwgdGhlIHJlcXVlc3QgbGVhdmVzIHRoZSB0b2tlbiAoZmFpbG92ZXIpIG9yIGVuZHMsXG4gICAqIHNvIGEgcmVmcmVzaCBhbmQgaXRzIHNhbWUtdG9rZW4gcmV0cnkgbmV2ZXIgc3RlcCBvdXRzaWRlIHRoZSBpbi1mbGlnaHQgY2FwLiBXaGVuZXZlciB0aGVcbiAgICogYXR0ZW1wdCBpcyBhYm91dCB0byBwcmVzZW50IGNyZWRlbnRpYWxzIG90aGVyIHRoYW4gdGhlIG9uZXMgdGhlIHRva2VuJ3MgZ2F0ZXMgd2VyZSBldmFsdWF0ZWRcbiAgICogd2l0aCAoYSByZWZyZXNoIGhhcHBlbmVkKSwgdGhlIGxvb2t1cHMgYW5kIGdhdGVzIGFyZSByZS1ydW4gYmVmb3JlIGFueXRoaW5nIGlzIHNlbnQuXG4gICAqL1xuICBhc3luYyB3aXRoQXR0ZW1wdHM8VD4oXG4gICAgcmVxdWVzdENsYXNzOiBHcm9rUmVxdWVzdENsYXNzLFxuICAgIHNlbGVjdGlvbjogTWVkaWFUb2tlblNlbGVjdGlvbiAmIHsgc2lnbmFsOiBBYm9ydFNpZ25hbDsgZGVhZGxpbmU/OiBudW1iZXI7IHVuZ2F0ZWQ/OiBib29sZWFuIH0sXG4gICAgYXR0ZW1wdDogKFxuICAgICAgdG9rZW46IEdyb2tUb2tlblN0YXRlLFxuICAgICAgY3JlZGVudGlhbHM6IEdyb2tDcmVkZW50aWFscyxcbiAgICAgIHN0YXRlOiBBdHRlbXB0U3RhdGUsXG4gICAgKSA9PiBQcm9taXNlPFQ+LFxuICApOiBQcm9taXNlPFQ+IHtcbiAgICBjb25zdCBleGNsdWRlZCA9IG5ldyBTZXQ8bnVtYmVyPigpO1xuICAgIGNvbnN0IHJlZnJlc2hlZCA9IG5ldyBTZXQ8bnVtYmVyPigpO1xuICAgIGxldCByZWZyZXNoZXMgPSAwO1xuICAgIGxldCBob3BzID0gMDtcbiAgICBsZXQgdHJhbnNpZW50RmFpbG92ZXJzID0gMDtcbiAgICBsZXQgYXR0ZW1wdHMgPSAwO1xuICAgIGxldCBzdGFsZVJldHJpZXMgPSAwO1xuICAgIGxldCByZXNlcnZhdGlvbiA9IGF3YWl0IHRoaXMuc2VsZWN0VG9rZW4ocmVxdWVzdENsYXNzLCB7IC4uLnNlbGVjdGlvbiwgZXhjbHVkZWQgfSk7XG4gICAgLy8gVGhlIGNyZWRlbnRpYWxzIHRoZSBjdXJyZW50IHRva2VuJ3MgZ2F0ZXMgd2VyZSBsYXN0IGV2YWx1YXRlZCB3aXRoLlxuICAgIGxldCBnYXRlZCA9IHJlc2VydmF0aW9uLnRva2VuLmNyZWRlbnRpYWxzO1xuXG4gICAgdHJ5IHtcbiAgICAgIGZvciAoOzspIHtcbiAgICAgICAgY29uc3QgdG9rZW4gPSByZXNlcnZhdGlvbi50b2tlbjtcblxuICAgICAgICBjb25zdCBzdGF0ZTogQXR0ZW1wdFN0YXRlID0ge1xuICAgICAgICAgIHVwc3RyZWFtU3RhcnRlZDogZmFsc2UsXG4gICAgICAgICAgZm9yd2FyZGVkOiBmYWxzZSxcbiAgICAgICAgICBwcmVzZW50ZWQ6IHRva2VuLmNyZWRlbnRpYWxzLFxuICAgICAgICB9O1xuXG4gICAgICAgIGxldCBhY3Rpb246IEZhaWx1cmVBY3Rpb247XG4gICAgICAgIGF0dGVtcHRzKys7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsZXQgY3JlZGVudGlhbHM6IEdyb2tDcmVkZW50aWFscztcblxuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjcmVkZW50aWFscyA9IGF3YWl0IHRoaXMuZnJlc2hDcmVkZW50aWFscyh0b2tlbiwgc2VsZWN0aW9uLnNpZ25hbCk7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGlmIChzZWxlY3Rpb24uc2lnbmFsLmFib3J0ZWQpIHRocm93IGVycm9yO1xuICAgICAgICAgICAgdGhyb3cgbmV3IEdyb2tDcmVkZW50aWFsUmVmcmVzaEVycm9yKGVycm9yKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBzdGF0ZS5wcmVzZW50ZWQgPSBjcmVkZW50aWFscztcblxuICAgICAgICAgIGNvbnN0IGJsb2NrZWQgPVxuICAgICAgICAgICAgY3JlZGVudGlhbHMuYWNjZXNzVG9rZW4gPT09IGdhdGVkPy5hY2Nlc3NUb2tlblxuICAgICAgICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICAgICAgICA6IGF3YWl0IHRoaXMucmVnYXRlKHRva2VuLCByZXF1ZXN0Q2xhc3MsIHNlbGVjdGlvbi5zaWduYWwsIHNlbGVjdGlvbi51bmdhdGVkKTtcblxuICAgICAgICAgIGdhdGVkID0gY3JlZGVudGlhbHM7XG5cbiAgICAgICAgICBpZiAoYmxvY2tlZCkgYWN0aW9uID0geyBraW5kOiBcImZhaWxvdmVyXCIsIGVycm9yOiBibG9ja2VkLCB0cmFuc2llbnQ6IGZhbHNlIH07XG4gICAgICAgICAgZWxzZSByZXR1cm4gYXdhaXQgYXR0ZW1wdCh0b2tlbiwgY3JlZGVudGlhbHMsIHN0YXRlKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBpZiAoc2VsZWN0aW9uLnNpZ25hbC5hYm9ydGVkKSB0aHJvdyBhYm9ydFJlYXNvbihzZWxlY3Rpb24uc2lnbmFsKTtcblxuICAgICAgICAgIGlmIChzdGF0ZS5mb3J3YXJkZWQgfHwgc3RhdGUudXBzdHJlYW1TdGFydGVkKSB0aHJvdyBlcnJvcjtcbiAgICAgICAgICBhY3Rpb24gPSB0aGlzLmNsYXNzaWZ5RmFpbHVyZShcbiAgICAgICAgICAgIHRva2VuLFxuICAgICAgICAgICAgcmVxdWVzdENsYXNzLFxuICAgICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgICBzdGF0ZS5wcmVzZW50ZWQsXG4gICAgICAgICAgICByZWZyZXNoZWQuaGFzKHRva2VuLmlkKSxcbiAgICAgICAgICAgIHJlZnJlc2hlcyA8IEdST0tfTUFYX1JFRlJFU0hFUyxcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGFjdGlvbi5raW5kID09PSBcImZhaWxcIikgdGhyb3cgYWN0aW9uLmVycm9yO1xuXG4gICAgICAgIGlmIChhY3Rpb24ua2luZCA9PT0gXCJyZXRyeVwiKSB7XG4gICAgICAgICAgLy8gU2FtZSB0b2tlbiwgY3VycmVudCBjcmVkZW50aWFscywgcmVzZXJ2YXRpb24gc3RpbGwgaGVsZC5cbiAgICAgICAgICBpZiAoc3RhbGVSZXRyaWVzKysgPCBHUk9LX01BWF9TVEFMRV9BVVRIX1JFVFJJRVMpIGNvbnRpbnVlO1xuICAgICAgICAgIC8vIEFsbG93YW5jZSBzcGVudDogdGhpcyB0b2tlbiBtdXN0IG5vdCBiZSByZXNlbGVjdGVkIGJ5IHRoZSBmYWlsb3ZlciBiZWxvdy5cbiAgICAgICAgICBleGNsdWRlZC5hZGQodG9rZW4uaWQpO1xuICAgICAgICAgIGFjdGlvbiA9IHsga2luZDogXCJmYWlsb3ZlclwiLCBlcnJvcjogYWN0aW9uLmVycm9yLCB0cmFuc2llbnQ6IGZhbHNlIH07XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoYWN0aW9uLmtpbmQgPT09IFwicmVmcmVzaFwiKSB7XG4gICAgICAgICAgcmVmcmVzaGVzKys7XG4gICAgICAgICAgcmVmcmVzaGVkLmFkZCh0b2tlbi5pZCk7XG4gICAgICAgICAgLy8gVGhlIHJlc2VydmF0aW9uIHN0YXlzIGhlbGQgYWNyb3NzIHRoZSByZWZyZXNoOiB0aGUgcmV0cnkgaXMgcGFydCBvZiB0aGlzIGF0dGVtcHQuXG4gICAgICAgICAgY29uc3QgbmV4dCA9IGF3YWl0IHRoaXMudHJ5UmVmcmVzaCh0b2tlbiwgYWN0aW9uLnByZXNlbnRlZCwgc2VsZWN0aW9uLnNpZ25hbCk7XG5cbiAgICAgICAgICBpZiAobmV4dC5raW5kID09PSBcInJlZnJlc2hlZFwiKSBjb250aW51ZTtcbiAgICAgICAgICBhY3Rpb24gPSB7IGtpbmQ6IFwiZmFpbG92ZXJcIiwgZXJyb3I6IG5leHQuZXJyb3IsIHRyYW5zaWVudDogZmFsc2UgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEZhaWxvdmVyOiBuZXZlciBwYXN0IGFuIGV4YWN0IHRhcmdldCwgdGhlIGhvcCBidWRnZXQsIG9yIG9uZSB0cmFuc2llbnQgcmV0cnkuXG4gICAgICAgIGlmIChzZWxlY3Rpb24ucmVxdWlyZVByZWZlcnJlZFRva2VuKSB0aHJvdyBhY3Rpb24uZXJyb3I7XG5cbiAgICAgICAgaWYgKGFjdGlvbi50cmFuc2llbnQgJiYgKyt0cmFuc2llbnRGYWlsb3ZlcnMgPiBHUk9LX01BWF9UUkFOU0lFTlRfRkFJTE9WRVJTKVxuICAgICAgICAgIHRocm93IGFjdGlvbi5lcnJvcjtcblxuICAgICAgICBpZiAoKytob3BzID4gR1JPS19NQVhfSE9QUylcbiAgICAgICAgICB0aHJvdyBuZXcgR3Jva0F0dGVtcHRCdWRnZXRFeGNlZWRlZEVycm9yKGF0dGVtcHRzLCBhY3Rpb24uZXJyb3IpO1xuXG4gICAgICAgIC8vIFJhdGUtbGltaXQvcXVvdGEvcmVhdXRoIHN0YXRlIGFscmVhZHkgZXhjbHVkZXMgdGhlIHRva2VuOyBmYWlsdXJlcyB0aGF0IGxlYXZlIG5vIHN0YXRlXG4gICAgICAgIC8vIGJlaGluZCBtdXN0IG5vdCBiZSByZXRyaWVkIG9uIHRoZSBzYW1lIHRva2VuIHdpdGhpbiB0aGlzIHJlcXVlc3QuXG4gICAgICAgIGlmIChhY3Rpb24udHJhbnNpZW50IHx8IGFjdGlvbi5lcnJvciBpbnN0YW5jZW9mIEdyb2tDcmVkZW50aWFsUmVmcmVzaEVycm9yKVxuICAgICAgICAgIGV4Y2x1ZGVkLmFkZCh0b2tlbi5pZCk7XG4gICAgICAgIHRva2VuLmZhaWxvdmVyQ291bnQrKztcbiAgICAgICAgbG9nZ2VyLmluZm8oYGdyb2sgZmFpbG92ZXIgZnJvbSAke3Rva2VuLm5hbWV9WyR7dG9rZW4uaWR9XSAoJHtyZXF1ZXN0Q2xhc3N9KWAsIHtcbiAgICAgICAgICByZWFzb246IGFjdGlvbi5lcnJvciBpbnN0YW5jZW9mIEVycm9yID8gYWN0aW9uLmVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoYWN0aW9uLmVycm9yKSxcbiAgICAgICAgfSk7XG4gICAgICAgIHJlc2VydmF0aW9uLnJlbGVhc2UoKTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIHJlc2VydmF0aW9uID0gYXdhaXQgdGhpcy5zZWxlY3RUb2tlbihyZXF1ZXN0Q2xhc3MsIHtcbiAgICAgICAgICAgIGV4Y2x1ZGVkLFxuICAgICAgICAgICAgc2lnbmFsOiBzZWxlY3Rpb24uc2lnbmFsLFxuICAgICAgICAgICAgZGVhZGxpbmU6IHNlbGVjdGlvbi5kZWFkbGluZSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBjYXRjaCAoc2VsZWN0aW9uRXJyb3IpIHtcbiAgICAgICAgICBpZiAoc2VsZWN0aW9uRXJyb3IgaW5zdGFuY2VvZiBHcm9rTm9Xb3JrZXJzRXJyb3IpIHRocm93IGFjdGlvbi5lcnJvcjtcbiAgICAgICAgICB0aHJvdyBzZWxlY3Rpb25FcnJvcjtcbiAgICAgICAgfVxuXG4gICAgICAgIGdhdGVkID0gcmVzZXJ2YXRpb24udG9rZW4uY3JlZGVudGlhbHM7XG4gICAgICB9XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHJlc2VydmF0aW9uLnJlbGVhc2UoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmUtcnVucyB0aGUgbG9va3VwcyBhbmQgZ2F0ZXMgZm9yIGEgdG9rZW4gd2hvc2UgY3JlZGVudGlhbHMgY2hhbmdlZCBhZnRlciBzZWxlY3Rpb24uIFJldHVybnNcbiAgICogdGhlIGdhdGUncyBlcnJvciB3aGVuIHRoZSB0b2tlbiBpcyBubyBsb25nZXIgZWxpZ2libGU7IHRoZSBjYWxsZXIgc3RpbGwgaG9sZHMgb25lIHJlc2VydmF0aW9uLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZWdhdGUoXG4gICAgdG9rZW46IEdyb2tUb2tlblN0YXRlLFxuICAgIHJlcXVlc3RDbGFzczogR3Jva1JlcXVlc3RDbGFzcyxcbiAgICBzaWduYWw6IEFib3J0U2lnbmFsLFxuICAgIHVuZ2F0ZWQ/OiBib29sZWFuLFxuICApOiBQcm9taXNlPEVycm9yIHwgdW5kZWZpbmVkPiB7XG4gICAgYXdhaXQgdGhpcy5wcmVmZXRjaCh0b2tlbiwgc2lnbmFsKTtcbiAgICBzaWduYWwudGhyb3dJZkFib3J0ZWQoKTtcbiAgICBjb25zdCByZWFzb24gPSB0aGlzLmluZWxpZ2liaWxpdHkodG9rZW4sIHJlcXVlc3RDbGFzcywgdGhpcy5ub3coKSwgMSwgdW5nYXRlZCk7XG5cbiAgICByZXR1cm4gcmVhc29uID8gdGhpcy5wcmVmZXJyZWRFcnJvcih0b2tlbiwgcmVhc29uKSA6IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgb25lIGZvcmNlZCByZWZyZXNoIGFmdGVyIGFuIHVwc3RyZWFtIDQwMS4gQSByZWZyZXNoZWQgdG9rZW4gaXMgcmV0cmllZDsgb3RoZXJ3aXNlIHRoZVxuICAgKiByZXF1ZXN0IGZhaWxzIG92ZXIuIE9ubHkgYSBgcmVhdXRoX3JlcXVpcmVkYCBhbnN3ZXIgbWFya3MgdGhlIHRva2VuIGRlYWQg4oCUIGEgdGhyb3duIHJlZnJlc2hcbiAgICogZmFpbHVyZSBpcyB0cmVhdGVkIGFzIHRyYW5zaWVudCBhbmQgbGVhdmVzIHRoZSB0b2tlbiBhbGl2ZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdHJ5UmVmcmVzaChcbiAgICB0b2tlbjogR3Jva1Rva2VuU3RhdGUsXG4gICAgcHJlc2VudGVkOiBHcm9rQ3JlZGVudGlhbHMsXG4gICAgc2lnbmFsOiBBYm9ydFNpZ25hbCxcbiAgKTogUHJvbWlzZTx7IGtpbmQ6IFwicmVmcmVzaGVkXCIgfSB8IHsga2luZDogXCJmYWlsb3ZlclwiOyBlcnJvcjogR3Jva0NyZWRlbnRpYWxSZWZyZXNoRXJyb3IgfT4ge1xuICAgIGxldCByZXN1bHQ6IEF3YWl0ZWQ8UmV0dXJuVHlwZTxHcm9rUmVmcmVzaGVyW1wicmVmcmVzaEFmdGVyVW5hdXRob3JpemVkXCJdPj47XG5cbiAgICB0cnkge1xuICAgICAgcmVzdWx0ID0gYXdhaXQgdGhpcy5yZWZyZXNoZXIucmVmcmVzaEFmdGVyVW5hdXRob3JpemVkKFxuICAgICAgICB7IHRva2VuSWQ6IHRva2VuLmlkLCBjcmVkZW50aWFsczogcHJlc2VudGVkIH0sXG4gICAgICAgIHNpZ25hbCxcbiAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChzaWduYWwuYWJvcnRlZCkgdGhyb3cgYWJvcnRSZWFzb24oc2lnbmFsKTtcblxuICAgICAgcmV0dXJuIHsga2luZDogXCJmYWlsb3ZlclwiLCBlcnJvcjogbmV3IEdyb2tDcmVkZW50aWFsUmVmcmVzaEVycm9yKGVycm9yKSB9O1xuICAgIH1cblxuICAgIGlmIChyZXN1bHQuc3RhdHVzID09PSBcInJlZnJlc2hlZFwiKSB7XG4gICAgICB0aGlzLnN0b3JlQ3JlZGVudGlhbHModG9rZW4sIHJlc3VsdC5jcmVkZW50aWFscyk7XG5cbiAgICAgIHJldHVybiB7IGtpbmQ6IFwicmVmcmVzaGVkXCIgfTtcbiAgICB9XG5cbiAgICB0aGlzLm1hcmtSZWF1dGhSZXF1aXJlZCh0b2tlbiwgcHJlc2VudGVkLCByZWF1dGhSZWFzb25Db2RlKHJlc3VsdC5yZWFzb24pKTtcblxuICAgIHJldHVybiB7IGtpbmQ6IFwiZmFpbG92ZXJcIiwgZXJyb3I6IEdyb2tDcmVkZW50aWFsUmVmcmVzaEVycm9yLnJlYXV0aFJlcXVpcmVkKHJlc3VsdC5yZWFzb24pIH07XG4gIH1cblxuICBhc3luYyBmcmVzaENyZWRlbnRpYWxzKHRva2VuOiBHcm9rVG9rZW5TdGF0ZSwgc2lnbmFsOiBBYm9ydFNpZ25hbCk6IFByb21pc2U8R3Jva0NyZWRlbnRpYWxzPiB7XG4gICAgY29uc3QgY3VycmVudCA9IHRva2VuLmNyZWRlbnRpYWxzO1xuXG4gICAgaWYgKCFjdXJyZW50KSB0aHJvdyBuZXcgR3Jva05vV29ya2Vyc0Vycm9yKGBncm9rIHRva2VuICR7dG9rZW4uaWR9IGhhcyBubyB2YWxpZCBjcmVkZW50aWFsc2ApO1xuXG4gICAgY29uc3QgZnJlc2ggPSBhd2FpdCB0aGlzLnJlZnJlc2hlci5lbnN1cmVGcmVzaChcbiAgICAgIHsgdG9rZW5JZDogdG9rZW4uaWQsIGNyZWRlbnRpYWxzOiBjdXJyZW50IH0sXG4gICAgICBzaWduYWwsXG4gICAgKTtcblxuICAgIHRoaXMuc3RvcmVDcmVkZW50aWFscyh0b2tlbiwgZnJlc2gpO1xuXG4gICAgcmV0dXJuIGZyZXNoO1xuICB9XG5cbiAgc3RvcmVDcmVkZW50aWFscyh0b2tlbjogR3Jva1Rva2VuU3RhdGUsIGNyZWRlbnRpYWxzOiBHcm9rQ3JlZGVudGlhbHMpOiB2b2lkIHtcbiAgICAvLyBBIHN1YnNjcmliZXIgdXBkYXRlIG1heSBhbHJlYWR5IGhhdmUgcmVwbGFjZWQgdGhlIHJvdzsgbmV2ZXIgb3ZlcndyaXRlIG5ld2VyIGNyZWRlbnRpYWxzLlxuICAgIGlmICh0aGlzLnRva2Vucy5nZXQodG9rZW4uaWQpICE9PSB0b2tlbikgcmV0dXJuO1xuXG4gICAgaWYgKHRva2VuLmNyZWRlbnRpYWxzPy5hY2Nlc3NUb2tlbiA9PT0gY3JlZGVudGlhbHMuYWNjZXNzVG9rZW4pIHJldHVybjtcbiAgICB0b2tlbi5jcmVkZW50aWFscyA9IGNyZWRlbnRpYWxzO1xuICAgIHRva2VuLnJlYXV0aFJlcXVpcmVkID0gZmFsc2U7XG4gICAgLy8gTG9va3VwcyBjYWNoZWQgKG9yIGluIGZsaWdodCkgdW5kZXIgdGhlIG9sZCBiZWFyZXIgbWF5IGJlIGZhaWwtb3BlbiBsb29rdXBfZmFpbGVkIHJlc3VsdHM7XG4gICAgLy8gYSBuZXcgZ2VuZXJhdGlvbiBtYWtlcyB0aGUgbmV4dCBzZWxlY3Rpb24gcmUtcmVhZCB0aGVtIHdpdGggdGhpcyBiZWFyZXIuIFRoZSBjYWNoZWQgdmFsdWVzXG4gICAgLy8gc3RpbGwgZ2F0ZSB1bnRpbCB0aGVuIChzYW1lIGFjY291bnQpLCBhbmQgdGhlIHN1YnNjcmliZXIncyBlY2hvIG9mIHRoaXMgcm93IGlzIGEgbm8tb3AuXG4gICAgdG9rZW4uZ2VuZXJhdGlvbisrO1xuICAgIHRoaXMucGVuZGluZ0JpbGxpbmcuZGVsZXRlKHRva2VuLmlkKTtcbiAgICB0aGlzLnBlbmRpbmdTZXR0aW5ncy5kZWxldGUodG9rZW4uaWQpO1xuICAgIHRoaXMucGVuZGluZ1N1YnNjcmlwdGlvbi5kZWxldGUodG9rZW4uaWQpO1xuICB9XG5cbiAgLy8gLS0tLSBDaGF0IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIGFzeW5jIGdlbmVyYXRlKHJlcTogR2VuZXJhdGVSZXF1ZXN0KTogUHJvbWlzZTxHZW5lcmF0ZVJlc3VsdD4ge1xuICAgIHJldHVybiB0aGlzLnJ1bkNoYXQocmVxKTtcbiAgfVxuXG4gIGFzeW5jIGdlbmVyYXRlU3RyZWFtKHJlcTogR2VuZXJhdGVSZXF1ZXN0LCBjYjogR2VuZXJhdGVTdHJlYW1DYWxsYmFja3MpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY2Iub25Db21wbGV0ZShhd2FpdCB0aGlzLnJ1bkNoYXQocmVxLCBjYi5vbkRlbHRhKSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNiLm9uRXJyb3IoZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yIDogbmV3IEVycm9yKFN0cmluZyhlcnJvcikpKTtcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxuXG4gIGJ1aWxkQ2hhdEJvZHkocmVxOiBHZW5lcmF0ZVJlcXVlc3QpOiBHcm9rQ2hhdEJvZHkge1xuICAgIGlmIChyZXEuaW1hZ2VHZW5lcmF0aW9uICE9PSB1bmRlZmluZWQgJiYgcmVxLmltYWdlR2VuZXJhdGlvbiAhPT0gZmFsc2UpIHtcbiAgICAgIHRocm93IG5ldyBHcm9rSW52YWxpZFJlcXVlc3RFcnJvcihcbiAgICAgICAgXCJpbWFnZUdlbmVyYXRpb24gaXMgbm90IHN1cHBvcnRlZCBvbiBncm9rIGNoYXQgbW9kZWxzOyB1c2UgdGhlIEdyb2sgaW1hZ2UgZW5kcG9pbnRcIixcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKHJlcS5pbWFnZUdlbmVyYXRpb25PcHRpb25zICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBHcm9rSW52YWxpZFJlcXVlc3RFcnJvcihcbiAgICAgICAgXCJpbWFnZUdlbmVyYXRpb25PcHRpb25zIGlzIG5vdCBzdXBwb3J0ZWQgb24gZ3JvayBjaGF0IG1vZGVsc1wiLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAocmVxLnNlcnZpY2VUaWVyICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBHcm9rSW52YWxpZFJlcXVlc3RFcnJvcihcInNlcnZpY2VUaWVyIGlzIG5vdCBzdXBwb3J0ZWQgb24gZ3JvayBjaGF0IG1vZGVsc1wiKTtcbiAgICB9XG5cbiAgICAvLyBwcm92aWRlck9wdGlvbnMueGFpOiB0aGUgcHJveHkncyBhY2NlcHRhbmNlIG9mIHRoZXNlIGZpZWxkcyBpcyB1bnZlcmlmaWVkLCBzbyB0aGV5IGFyZVxuICAgIC8vIHJlamVjdGVkIHJhdGhlciB0aGFuIGZvcndhcmRlZCBvciBzaWxlbnRseSBkcm9wcGVkLlxuICAgIGNvbnN0IHhhaSA9IHJlcS5wcm92aWRlck9wdGlvbnM/LnhhaTtcblxuICAgIGlmIChpc0pzb25PYmplY3QoeGFpKSkge1xuICAgICAgZm9yIChjb25zdCBrZXkgb2YgW1wic2VydmljZVRpZXJcIiwgXCJwYXJhbGxlbFRvb2xDYWxsc1wiXSBhcyBjb25zdCkge1xuICAgICAgICBpZiAoeGFpW2tleV0gIT09IHVuZGVmaW5lZCAmJiB4YWlba2V5XSAhPT0gbnVsbCkge1xuICAgICAgICAgIHRocm93IG5ldyBHcm9rSW52YWxpZFJlcXVlc3RFcnJvcihcbiAgICAgICAgICAgIGBwcm92aWRlck9wdGlvbnMueGFpLiR7a2V5fSBpcyBub3Qgc3VwcG9ydGVkIG9uIGdyb2sgY2hhdCBtb2RlbHM6IHRoZSBzdWJzY3JpcHRpb24gcHJveHkncyBoYW5kbGluZyBpcyB1bnZlcmlmaWVkYCxcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHJlcS50aGlua2luZyA9PT0gZmFsc2UpIHtcbiAgICAgIHRocm93IG5ldyBUaGlua2luZ1ZhbGlkYXRpb25FcnJvcihcbiAgICAgICAgXCJ0aGlua2luZzpmYWxzZSBpcyB1bmF2YWlsYWJsZSBmb3IgR3JvayBtb2RlbHM6IHRoaW5raW5nLW9mZiBzdXBwb3J0IGhhcyBub3QgYmVlbiB2ZXJpZmllZCBvbiB0aGUgc3Vic2NyaXB0aW9uIHByb3h5XCIsXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IG1vZGVsID0gdGhpcy5jYXRhbG9nLnJlc29sdmUocmVxLm1vZGVsKTtcblxuICAgIGlmICghbW9kZWwua25vd24pIHtcbiAgICAgIC8vIFRoZSBwcm94eSBtYXkgc2VydmUgYW5vdGhlciBtb2RlbCBmb3IgYW4gdW5saXN0ZWQgaWQ7IHRoZSByZXN1bHQncyBtb2RlbCBzaG93cyB3aGljaC5cbiAgICAgIGxvZ2dlci5pbmZvKGBncm9rIG1vZGVsIFwiJHttb2RlbC5pZH1cIiBpcyBub3QgaW4gdGhlIGNhdGFsb2c7IGZvcndhcmRpbmcgaXQgdW52YWxpZGF0ZWRgKTtcbiAgICB9XG5cbiAgICBsZXQgZWZmb3J0OiBHcm9rUmVhc29uaW5nRWZmb3J0ID0gbW9kZWwuZGVmYXVsdEVmZm9ydDtcblxuICAgIGlmIChyZXEuZWZmb3J0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IHZhbGlkYXRlZCA9IHRoaXMuY2F0YWxvZy52YWxpZGF0ZUVmZm9ydChtb2RlbC5pZCwgcmVxLmVmZm9ydCk7XG5cbiAgICAgIGlmICh2YWxpZGF0ZWQpIGVmZm9ydCA9IHZhbGlkYXRlZDtcbiAgICAgIGVsc2UgbG9nZ2VyLmRlYnVnKGBpZ25vcmluZyBlZmZvcnQgXCIke3JlcS5lZmZvcnR9XCIgZm9yIGdyb2svJHttb2RlbC5pZH1gKTtcbiAgICB9XG5cbiAgICBjb25zdCBoaXN0b3J5ID0gaGlzdG9yeUl0ZW1zKHJlcS5jb2xkSGlzdG9yeSk7XG4gICAgY29uc3QgeyBjb252SWQsIGNvbnZHcm91cElkIH0gPSBncm9rQ29udmVyc2F0aW9uSWRzKHJlcS5wcm9tcHRDYWNoZUtleSk7XG5cbiAgICBjb25zdCB0dXJuSWR4ID1cbiAgICAgIGhpc3RvcnkuZmlsdGVyKChpdGVtKSA9PiBpdGVtLnR5cGUgPT09IFwibWVzc2FnZVwiICYmIGl0ZW0ucm9sZSA9PT0gXCJ1c2VyXCIpLmxlbmd0aCArIDE7XG5cbiAgICBjb25zdCB0b29scyA9IGdyb2tUb29scyhyZXEudG9vbHMpO1xuXG4gICAgY29uc3QgYm9keSA9IGJ1aWxkR3Jva1Jlc3BvbnNlc1JlcXVlc3Qoe1xuICAgICAgbW9kZWw6IG1vZGVsLmlkLFxuICAgICAgc3lzdGVtUHJvbXB0OiByZXEuc3lzdGVtUHJvbXB0LFxuICAgICAgaGlzdG9yeTogWy4uLmhpc3RvcnksIHVzZXJNZXNzYWdlKHJlcSldLFxuICAgICAgcHJvbXB0Q2FjaGVLZXk6IGNvbnZJZCxcbiAgICAgIGVmZm9ydCxcbiAgICAgIG91dHB1dFNjaGVtYTogcmVxLm91dHB1dFNjaGVtYSA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogeyBzY2hlbWE6IHJlcS5vdXRwdXRTY2hlbWEgfSxcbiAgICAgIHRvb2xzLFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIHsgYm9keSwgbW9kZWwsIGNvbnZJZCwgY29udkdyb3VwSWQsIHR1cm5JZHggfTtcbiAgfVxuXG4gIGFzeW5jIHJ1bkNoYXQocmVxOiBHZW5lcmF0ZVJlcXVlc3QsIG9uRGVsdGE/OiAodGV4dDogc3RyaW5nKSA9PiB2b2lkKTogUHJvbWlzZTxHZW5lcmF0ZVJlc3VsdD4ge1xuICAgIGNvbnN0IHsgYm9keSwgbW9kZWwsIGNvbnZJZCwgY29udkdyb3VwSWQsIHR1cm5JZHggfSA9IHRoaXMuYnVpbGRDaGF0Qm9keShyZXEpO1xuICAgIGNvbnN0IHRpbWVvdXRNcyA9IHJlcS50aW1lb3V0TXMgPz8gREVGQVVMVF9SRVFVRVNUX1RJTUVPVVRfTVM7XG4gICAgY29uc3QgdGltZW91dCA9IEFib3J0U2lnbmFsLnRpbWVvdXQodGltZW91dE1zKTtcbiAgICBjb25zdCBzaWduYWwgPSByZXEuYWJvcnRTaWduYWwgPyBBYm9ydFNpZ25hbC5hbnkoW3JlcS5hYm9ydFNpZ25hbCwgdGltZW91dF0pIDogdGltZW91dDtcbiAgICBzaWduYWwudGhyb3dJZkFib3J0ZWQoKTtcbiAgICBjb25zdCBkZWFkbGluZSA9IHRoaXMubm93KCkgKyB0aW1lb3V0TXM7XG5cbiAgICByZXR1cm4gdGhpcy53aXRoQXR0ZW1wdHMoXG4gICAgICBcImNoYXRcIixcbiAgICAgIHtcbiAgICAgICAgcHJlZmVycmVkVG9rZW5JZDogcmVxLnByZWZlcnJlZFRva2VuSWQsXG4gICAgICAgIHJlcXVpcmVQcmVmZXJyZWRUb2tlbjogcmVxLnJlcXVpcmVQcmVmZXJyZWRUb2tlbixcbiAgICAgICAgc2lnbmFsLFxuICAgICAgICBkZWFkbGluZSxcbiAgICAgIH0sXG4gICAgICAodG9rZW4sIGNyZWRlbnRpYWxzLCBzdGF0ZSkgPT5cbiAgICAgICAgdGhpcy5jaGF0QXR0ZW1wdCh0b2tlbiwgY3JlZGVudGlhbHMsIHN0YXRlLCB7XG4gICAgICAgICAgcmVxLFxuICAgICAgICAgIGJvZHksXG4gICAgICAgICAgbW9kZWwsXG4gICAgICAgICAgY29udklkLFxuICAgICAgICAgIGNvbnZHcm91cElkLFxuICAgICAgICAgIHR1cm5JZHgsXG4gICAgICAgICAgc2lnbmFsLFxuICAgICAgICAgIG9uRGVsdGEsXG4gICAgICAgIH0pLFxuICAgICk7XG4gIH1cblxuICBhc3luYyBjaGF0QXR0ZW1wdChcbiAgICB0b2tlbjogR3Jva1Rva2VuU3RhdGUsXG4gICAgY3JlZGVudGlhbHM6IEdyb2tDcmVkZW50aWFscyxcbiAgICBzdGF0ZTogQXR0ZW1wdFN0YXRlLFxuICAgIGNvbnRleHQ6IHtcbiAgICAgIHJlcTogR2VuZXJhdGVSZXF1ZXN0O1xuICAgICAgYm9keTogR3Jva1Jlc3BvbnNlc1JlcXVlc3Q7XG4gICAgICBtb2RlbDogR3Jva1Jlc29sdmVkTW9kZWw7XG4gICAgICBjb252SWQ6IHN0cmluZztcbiAgICAgIGNvbnZHcm91cElkOiBzdHJpbmc7XG4gICAgICB0dXJuSWR4OiBudW1iZXI7XG4gICAgICBzaWduYWw6IEFib3J0U2lnbmFsO1xuICAgICAgb25EZWx0YT86ICh0ZXh0OiBzdHJpbmcpID0+IHZvaWQ7XG4gICAgfSxcbiAgKTogUHJvbWlzZTxHZW5lcmF0ZVJlc3VsdD4ge1xuICAgIGNvbnN0IHsgcmVxLCBtb2RlbCB9ID0gY29udGV4dDtcbiAgICBjb25zdCBzdGFydGVkQXQgPSB0aGlzLm5vdygpO1xuXG4gICAgY29uc3QgaGVhZGVycyA9IGJ1aWxkR3Jva0NoYXRIZWFkZXJzKHtcbiAgICAgIGJlYXJlcjogY3JlZGVudGlhbHMuYWNjZXNzVG9rZW4sXG4gICAgICBtb2RlbDogbW9kZWwuaWQsXG4gICAgICBjb252SWQ6IGNvbnRleHQuY29udklkLFxuICAgICAgcmVxSWQ6IHJhbmRvbVVVSUQoKSxcbiAgICAgIHNlc3Npb25JZDogY3JlZGVudGlhbHMuc2Vzc2lvbklkLFxuICAgICAgYWdlbnRJZDogY3JlZGVudGlhbHMuYWdlbnRJZCxcbiAgICAgIHVzZXJJZDogY3JlZGVudGlhbHMudXNlcklkLFxuICAgICAgY29udkdyb3VwSWQ6IGNvbnRleHQuY29udkdyb3VwSWQsXG4gICAgICB0dXJuSWR4OiBjb250ZXh0LnR1cm5JZHgsXG4gICAgICB6c3RkOiB0cnVlLFxuICAgIH0pO1xuXG4gICAgbGV0IHN0cmVhbTogQXdhaXRlZDxSZXR1cm5UeXBlPEdyb2tDaGF0Q2xpZW50W1wic3RyZWFtXCJdPj47XG5cbiAgICB0cnkge1xuICAgICAgc3RyZWFtID0gYXdhaXQgdGhpcy5jbGllbnQuc3RyZWFtKHtcbiAgICAgICAgYm9keTogY29udGV4dC5ib2R5LFxuICAgICAgICBoZWFkZXJzLFxuICAgICAgICBzaWduYWw6IGNvbnRleHQuc2lnbmFsLFxuICAgICAgICBpZGxlVGltZW91dE1zOiB0aGlzLmlkbGVUaW1lb3V0TXMsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgR3Jva0h0dHBFcnJvcilcbiAgICAgICAgdGhpcy5vYnNlcnZlUmVzcG9uc2VIZWFkZXJzKHRva2VuLCBjcmVkZW50aWFscywgZXJyb3IucmVzcG9uc2VIZWFkZXJzKTtcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cblxuICAgIHRoaXMub2JzZXJ2ZVJlc3BvbnNlSGVhZGVycyh0b2tlbiwgY3JlZGVudGlhbHMsIHN0cmVhbS5oZWFkZXJzKTtcbiAgICAvLyBBIHN1Y2Nlc3NmdWwgY2hhdCBwcm92ZXMgdGhlIGNsaWVudCB2ZXJzaW9uIGlzIGFjY2VwdGVkIGFnYWluLlxuICAgIHRoaXMudmVyc2lvbkdhdGUgPSB1bmRlZmluZWQ7XG5cbiAgICBpZiAodGhpcy5jYXRhbG9nLmlzU3RhbGUoc3RyZWFtLmhlYWRlcnMubW9kZWxzRXRhZykpIHtcbiAgICAgIHZvaWQgdGhpcy5jYXRhbG9nLnJlZnJlc2goY3JlZGVudGlhbHMsIHsgZmV0Y2g6IHRoaXMuZmV0Y2hJbXBsIH0pLnRoZW4oKHJlc3VsdCkgPT4ge1xuICAgICAgICBpZiAocmVzdWx0LmtpbmQgPT09IFwibG9va3VwX2ZhaWxlZFwiKVxuICAgICAgICAgIGxvZ2dlci53YXJuKGBncm9rIG1vZGVsIGNhdGFsb2cgcmVmcmVzaCBmYWlsZWQ6ICR7cmVzdWx0LnJlYXNvbn1gKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGxldCB0ZXh0ID0gXCJcIjtcbiAgICBsZXQgZmlyc3REZWx0YUF0OiBudW1iZXIgfCB1bmRlZmluZWQ7XG4gICAgbGV0IHVzYWdlOiBHcm9rVXNhZ2UgfCB1bmRlZmluZWQ7XG4gICAgbGV0IHNlcnZpbmdNb2RlbCA9IG1vZGVsLmlkO1xuICAgIGxldCBvdXRwdXRNZXNzYWdlQWNjZXB0ZWQgPSBmYWxzZTtcbiAgICBjb25zdCB0b29sQ2FsbHM6IFByb3ZpZGVyVG9vbENhbGxbXSA9IFtdO1xuICAgIGxldCBjb21wbGV0ZWQgPSBmYWxzZTtcblxuICAgIGZvciBhd2FpdCAoY29uc3QgZXZlbnQgb2Ygc3RyZWFtLmV2ZW50cykge1xuICAgICAgc3RhdGUudXBzdHJlYW1TdGFydGVkID0gdHJ1ZTtcblxuICAgICAgaWYgKGV2ZW50LnR5cGUgPT09IFwiY3JlYXRlZFwiKSB7XG4gICAgICAgIGlmIChldmVudC5tb2RlbCkgc2VydmluZ01vZGVsID0gZXZlbnQubW9kZWw7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LnR5cGUgPT09IFwidGV4dC1kZWx0YVwiKSB7XG4gICAgICAgIGlmIChvdXRwdXRNZXNzYWdlQWNjZXB0ZWQpIGNvbnRpbnVlO1xuICAgICAgICBmaXJzdERlbHRhQXQgPz89IHRoaXMubm93KCk7XG4gICAgICAgIHRleHQgKz0gZXZlbnQudGV4dDtcblxuICAgICAgICBpZiAoY29udGV4dC5vbkRlbHRhKSB7XG4gICAgICAgICAgc3RhdGUuZm9yd2FyZGVkID0gdHJ1ZTtcbiAgICAgICAgICBjb250ZXh0Lm9uRGVsdGEoZXZlbnQudGV4dCk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQudHlwZSA9PT0gXCJvdXRwdXQtaXRlbVwiICYmIGV2ZW50LmNvbXBsZXRlZCkge1xuICAgICAgICBjb25zdCBpdGVtID0gZXZlbnQuaXRlbTtcblxuICAgICAgICBpZiAoaXRlbS50eXBlID09PSBcImZ1bmN0aW9uX2NhbGxcIikge1xuICAgICAgICAgIGNvbnN0IGNhbGxJZCA9IGpzb25TdHJpbmcoaXRlbS5jYWxsX2lkKTtcbiAgICAgICAgICBjb25zdCBuYW1lID0ganNvblN0cmluZyhpdGVtLm5hbWUpO1xuICAgICAgICAgIGNvbnN0IGFyZ3MgPSBqc29uU3RyaW5nKGl0ZW0uYXJndW1lbnRzKTtcblxuICAgICAgICAgIGlmIChjYWxsSWQgIT09IHVuZGVmaW5lZCAmJiBuYW1lICE9PSB1bmRlZmluZWQgJiYgYXJncyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICB0b29sQ2FsbHMucHVzaCh7IHRvb2xDYWxsSWQ6IGNhbGxJZCwgdG9vbE5hbWU6IG5hbWUsIGlucHV0OiBhcmdzIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgICBpdGVtLnR5cGUgPT09IFwibWVzc2FnZVwiICYmXG4gICAgICAgICAgcmVxLnN0b3BBZnRlck91dHB1dE1lc3NhZ2UgJiZcbiAgICAgICAgICAhb3V0cHV0TWVzc2FnZUFjY2VwdGVkICYmXG4gICAgICAgICAgQXJyYXkuaXNBcnJheShpdGVtLmNvbnRlbnQpXG4gICAgICAgICkge1xuICAgICAgICAgIGNvbnN0IG1lc3NhZ2VUZXh0ID0gaXRlbS5jb250ZW50XG4gICAgICAgICAgICAubWFwKChwYXJ0KSA9PlxuICAgICAgICAgICAgICBpc0pzb25PYmplY3QocGFydCkgJiYgcGFydC50eXBlID09PSBcIm91dHB1dF90ZXh0XCJcbiAgICAgICAgICAgICAgICA/IChqc29uU3RyaW5nKHBhcnQudGV4dCkgPz8gXCJcIilcbiAgICAgICAgICAgICAgICA6IFwiXCIsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICAuam9pbihcIlwiKTtcblxuICAgICAgICAgIGlmIChtZXNzYWdlVGV4dCkgb3V0cHV0TWVzc2FnZUFjY2VwdGVkID0gcmVxLnN0b3BBZnRlck91dHB1dE1lc3NhZ2UobWVzc2FnZVRleHQpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LnR5cGUgPT09IFwiY29tcGxldGVkXCIpIHtcbiAgICAgICAgY29tcGxldGVkID0gdHJ1ZTtcblxuICAgICAgICBpZiAoZXZlbnQubW9kZWwpIHNlcnZpbmdNb2RlbCA9IGV2ZW50Lm1vZGVsO1xuICAgICAgICB1c2FnZSA9IGV2ZW50LnVzYWdlO1xuICAgICAgfSBlbHNlIGlmIChldmVudC50eXBlID09PSBcImluY29tcGxldGVcIikge1xuICAgICAgICB0aHJvdyBuZXcgR3Jva0luY29tcGxldGVSZXNwb25zZUVycm9yKGV2ZW50LnJlYXNvbik7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LnR5cGUgPT09IFwiZXJyb3JcIikge1xuICAgICAgICB0aHJvdyBldmVudC5lcnJvcjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIWNvbXBsZXRlZClcbiAgICAgIHRocm93IG5ldyBHcm9rSW5jb21wbGV0ZVJlc3BvbnNlRXJyb3IoXCJzdHJlYW0gZW5kZWQgd2l0aG91dCByZXNwb25zZS5jb21wbGV0ZWRcIik7XG5cbiAgICBjb25zdCB0aWNrcyA9IHVzYWdlPy5jb3N0SW5Vc2RUaWNrcztcblxuICAgIGNvbnN0IGNvbnRleHRUb2tlbnMgPVxuICAgICAgdXNhZ2U/LmNvbnRleHRJbnB1dFRva2VucyA9PT0gdW5kZWZpbmVkICYmIHVzYWdlPy5jb250ZXh0T3V0cHV0VG9rZW5zID09PSB1bmRlZmluZWRcbiAgICAgICAgPyB1bmRlZmluZWRcbiAgICAgICAgOiAodXNhZ2UuY29udGV4dElucHV0VG9rZW5zID8/IDApICsgKHVzYWdlLmNvbnRleHRPdXRwdXRUb2tlbnMgPz8gMCk7XG5cbiAgICBjb25zdCBjb250ZXh0V2luZG93ID0gc3RyZWFtLmhlYWRlcnMuY29udGV4dFdpbmRvdyA/PyBtb2RlbC5jb250ZXh0V2luZG93O1xuXG4gICAgY29uc3QgcmVzdWx0VXNhZ2U6IEdlbmVyYXRlUmVzdWx0W1widXNhZ2VcIl0gPSB7XG4gICAgICB0b3RhbFRva2VuczogdXNhZ2U/LnRvdGFsVG9rZW5zID8/IDAsXG4gICAgICBpbnB1dFRva2VuczogdXNhZ2U/LmlucHV0VG9rZW5zID8/IDAsXG4gICAgICBjYWNoZWRJbnB1dFRva2VuczogdXNhZ2U/LmNhY2hlZElucHV0VG9rZW5zID8/IDAsXG4gICAgICBvdXRwdXRUb2tlbnM6IHVzYWdlPy5vdXRwdXRUb2tlbnMgPz8gMCxcbiAgICAgIHJlYXNvbmluZ091dHB1dFRva2VuczogdXNhZ2U/LnJlYXNvbmluZ1Rva2VucyA/PyAwLFxuICAgIH07XG5cbiAgICBpZiAoY29udGV4dFRva2VucyAhPT0gdW5kZWZpbmVkKSByZXN1bHRVc2FnZS5jb250ZXh0VG9rZW5zID0gY29udGV4dFRva2VucztcblxuICAgIGNvbnN0IHhhaTogWGFpUHJvdmlkZXJNZXRhZGF0YSA9IHt9O1xuXG4gICAgaWYgKHRpY2tzICE9PSB1bmRlZmluZWQpIHhhaS5jb3N0SW5Vc2RUaWNrcyA9IHRpY2tzO1xuXG4gICAgaWYgKGNvbnRleHRXaW5kb3cgIT09IHVuZGVmaW5lZCkgeGFpLmNvbnRleHRXaW5kb3cgPSBjb250ZXh0V2luZG93O1xuXG4gICAgY29uc3QgcmVzdWx0OiBHZW5lcmF0ZVJlc3VsdCA9IHtcbiAgICAgIHRleHQsXG4gICAgICB0b2tlbk5hbWU6IHRva2VuLm5hbWUsXG4gICAgICB1c2FnZTogcmVzdWx0VXNhZ2UsXG4gICAgICBkdXJhdGlvbk1zOiB0aGlzLm5vdygpIC0gc3RhcnRlZEF0LFxuICAgICAgdHRmdE1zOiBmaXJzdERlbHRhQXQgPT09IHVuZGVmaW5lZCA/IG51bGwgOiBmaXJzdERlbHRhQXQgLSBzdGFydGVkQXQsXG4gICAgICBtb2RlbDogc2VydmluZ01vZGVsLFxuICAgICAgdGhyZWFkQ29vcmQ6IHsgd29ya2VySWQ6IHRva2VuLmlkLCB0aHJlYWRJZDogcmVxLnByb21wdENhY2hlS2V5ID8/IFwiXCIsIGVwb2NoOiAtMSB9LFxuICAgICAgcHJvdmlkZXJNZXRhZGF0YTogeyB4YWkgfSxcbiAgICB9O1xuXG4gICAgaWYgKHNlcnZpbmdNb2RlbCAhPT0gbW9kZWwuaWQpIHJlc3VsdC5yZXF1ZXN0ZWRNb2RlbCA9IG1vZGVsLmlkO1xuXG4gICAgaWYgKHRpY2tzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHJlc3VsdC5jb3N0VXNkID0gZ3Jva1RpY2tzVG9Vc2QodGlja3MpO1xuICAgICAgcmVzdWx0LmNvc3RTb3VyY2UgPSBcInByb3ZpZGVyXCI7XG4gICAgfVxuXG4gICAgaWYgKHRvb2xDYWxscy5sZW5ndGgpIHJlc3VsdC50b29sQ2FsbHMgPSB0b29sQ2FsbHM7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLy8gLS0tLSBNZWRpYSBhbmQgdm9pY2UgKMKnNS43KSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIGFzeW5jIHJ1blBvcnQ8VD4oXG4gICAgcmVxdWVzdENsYXNzOiBHcm9rUmVxdWVzdENsYXNzLFxuICAgIHNlbGVjdGlvbjogTWVkaWFUb2tlblNlbGVjdGlvbiAmIHsgdW5nYXRlZD86IGJvb2xlYW4gfSxcbiAgICBzaWduYWw6IEFib3J0U2lnbmFsIHwgdW5kZWZpbmVkLFxuICAgIGNhbGw6IChjcmVkZW50aWFsczogR3Jva0NyZWRlbnRpYWxzKSA9PiBQcm9taXNlPFQ+LFxuICApOiBQcm9taXNlPE1lZGlhRGlzcGF0Y2hSZXN1bHQ8VD4+IHtcbiAgICBjb25zdCBhY3RpdmUgPSBzaWduYWwgPz8gbmV3IEFib3J0Q29udHJvbGxlcigpLnNpZ25hbDtcblxuICAgIHJldHVybiB0aGlzLndpdGhBdHRlbXB0cyhcbiAgICAgIHJlcXVlc3RDbGFzcyxcbiAgICAgIHtcbiAgICAgICAgcHJlZmVycmVkVG9rZW5JZDogc2VsZWN0aW9uLnByZWZlcnJlZFRva2VuSWQsXG4gICAgICAgIHJlcXVpcmVQcmVmZXJyZWRUb2tlbjogc2VsZWN0aW9uLnJlcXVpcmVQcmVmZXJyZWRUb2tlbixcbiAgICAgICAgdW5nYXRlZDogc2VsZWN0aW9uLnVuZ2F0ZWQsXG4gICAgICAgIHNpZ25hbDogYWN0aXZlLFxuICAgICAgfSxcbiAgICAgIGFzeW5jICh0b2tlbiwgY3JlZGVudGlhbHMpID0+ICh7XG4gICAgICAgIHJlc3VsdDogYXdhaXQgY2FsbChjcmVkZW50aWFscyksXG4gICAgICAgIHRva2VuSWQ6IHRva2VuLmlkLFxuICAgICAgICB0b2tlbk5hbWU6IHRva2VuLm5hbWUsXG4gICAgICB9KSxcbiAgICApO1xuICB9XG5cbiAgcmVxdWlyZU1lZGlhKCk6IEdyb2tNZWRpYVBvcnQge1xuICAgIGlmICghdGhpcy5tZWRpYSkgdGhyb3cgbmV3IEdyb2tDYXBhYmlsaXR5VW5hdmFpbGFibGVFcnJvcihcIm1lZGlhXCIpO1xuXG4gICAgcmV0dXJuIHRoaXMubWVkaWE7XG4gIH1cblxuICByZXF1aXJlVm9pY2UoKTogR3Jva1ZvaWNlUG9ydCB7XG4gICAgaWYgKCF0aGlzLnZvaWNlKSB0aHJvdyBuZXcgR3Jva0NhcGFiaWxpdHlVbmF2YWlsYWJsZUVycm9yKFwidm9pY2VcIik7XG5cbiAgICByZXR1cm4gdGhpcy52b2ljZTtcbiAgfVxuXG4gIGFzeW5jIGdlbmVyYXRlSW1hZ2VzKFxuICAgIHJlcXVlc3Q6IEdyb2tJbWFnZUdlbmVyYXRlUmVxdWVzdCAmIE1lZGlhVG9rZW5TZWxlY3Rpb24sXG4gICk6IFByb21pc2U8TWVkaWFEaXNwYXRjaFJlc3VsdDxHcm9rSW1hZ2VSZXNwb25zZT4+IHtcbiAgICBjb25zdCBtZWRpYSA9IHRoaXMucmVxdWlyZU1lZGlhKCk7XG4gICAgY29uc3QgeyBwcmVmZXJyZWRUb2tlbklkLCByZXF1aXJlUHJlZmVycmVkVG9rZW4sIC4uLnBvcnRSZXF1ZXN0IH0gPSByZXF1ZXN0O1xuXG4gICAgcmV0dXJuIHRoaXMucnVuUG9ydChcbiAgICAgIFwiaW1hZ2VcIixcbiAgICAgIHsgcHJlZmVycmVkVG9rZW5JZCwgcmVxdWlyZVByZWZlcnJlZFRva2VuIH0sXG4gICAgICByZXF1ZXN0LnNpZ25hbCxcbiAgICAgIChjcmVkZW50aWFscykgPT4gbWVkaWEuZ2VuZXJhdGVJbWFnZXMobWVkaWFBdXRoKGNyZWRlbnRpYWxzKSwgcG9ydFJlcXVlc3QpLFxuICAgICk7XG4gIH1cblxuICBhc3luYyBlZGl0SW1hZ2VzKFxuICAgIHJlcXVlc3Q6IEdyb2tJbWFnZUVkaXRSZXF1ZXN0ICYgTWVkaWFUb2tlblNlbGVjdGlvbixcbiAgKTogUHJvbWlzZTxNZWRpYURpc3BhdGNoUmVzdWx0PEdyb2tJbWFnZVJlc3BvbnNlPj4ge1xuICAgIGNvbnN0IG1lZGlhID0gdGhpcy5yZXF1aXJlTWVkaWEoKTtcbiAgICBjb25zdCB7IHByZWZlcnJlZFRva2VuSWQsIHJlcXVpcmVQcmVmZXJyZWRUb2tlbiwgLi4ucG9ydFJlcXVlc3QgfSA9IHJlcXVlc3Q7XG5cbiAgICByZXR1cm4gdGhpcy5ydW5Qb3J0KFxuICAgICAgXCJpbWFnZVwiLFxuICAgICAgeyBwcmVmZXJyZWRUb2tlbklkLCByZXF1aXJlUHJlZmVycmVkVG9rZW4gfSxcbiAgICAgIHJlcXVlc3Quc2lnbmFsLFxuICAgICAgKGNyZWRlbnRpYWxzKSA9PiBtZWRpYS5lZGl0SW1hZ2VzKG1lZGlhQXV0aChjcmVkZW50aWFscyksIHBvcnRSZXF1ZXN0KSxcbiAgICApO1xuICB9XG5cbiAgYXN5bmMgc3RhcnRWaWRlbyhcbiAgICByZXF1ZXN0OiBHcm9rVmlkZW9TdGFydFJlcXVlc3QgJiBNZWRpYVRva2VuU2VsZWN0aW9uLFxuICApOiBQcm9taXNlPE1lZGlhRGlzcGF0Y2hSZXN1bHQ8R3Jva1ZpZGVvU3RhcnQ+PiB7XG4gICAgY29uc3QgbWVkaWEgPSB0aGlzLnJlcXVpcmVNZWRpYSgpO1xuICAgIGNvbnN0IHsgcHJlZmVycmVkVG9rZW5JZCwgcmVxdWlyZVByZWZlcnJlZFRva2VuLCAuLi5wb3J0UmVxdWVzdCB9ID0gcmVxdWVzdDtcblxuICAgIHJldHVybiB0aGlzLnJ1blBvcnQoXG4gICAgICBcInZpZGVvXCIsXG4gICAgICB7IHByZWZlcnJlZFRva2VuSWQsIHJlcXVpcmVQcmVmZXJyZWRUb2tlbiB9LFxuICAgICAgcmVxdWVzdC5zaWduYWwsXG4gICAgICAoY3JlZGVudGlhbHMpID0+IG1lZGlhLnN0YXJ0VmlkZW8obWVkaWFBdXRoKGNyZWRlbnRpYWxzKSwgcG9ydFJlcXVlc3QpLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUG9sbHMgb24gdGhlIHRva2VuIHRoYXQgc3RhcnRlZCB0aGUgam9iOyBubyBzZWxlY3Rpb24sIG5vIGZhaWxvdmVyLiBUaGUgam9iIGlzIGFscmVhZHlcbiAgICogYWRtaXR0ZWQgKGFuZCBiaWxsZWQpLCBzbyBxdW90YSwgY29vbGRvd24gYW5kIGNhcGFjaXR5IGdhdGVzIG11c3Qgbm90IHN0cmFuZCBpdHMgcmVzdWx0LlxuICAgKi9cbiAgYXN5bmMgZ2V0VmlkZW8oXG4gICAgdG9rZW5JZDogbnVtYmVyLFxuICAgIHJlcXVlc3RJZDogc3RyaW5nLFxuICAgIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuICApOiBQcm9taXNlPE1lZGlhRGlzcGF0Y2hSZXN1bHQ8R3Jva1ZpZGVvU3RhdHVzPj4ge1xuICAgIGNvbnN0IG1lZGlhID0gdGhpcy5yZXF1aXJlTWVkaWEoKTtcblxuICAgIHJldHVybiB0aGlzLnJ1blBvcnQoXG4gICAgICBcInZpZGVvXCIsXG4gICAgICB7IHByZWZlcnJlZFRva2VuSWQ6IHRva2VuSWQsIHJlcXVpcmVQcmVmZXJyZWRUb2tlbjogdHJ1ZSwgdW5nYXRlZDogdHJ1ZSB9LFxuICAgICAgc2lnbmFsLFxuICAgICAgKGNyZWRlbnRpYWxzKSA9PiBtZWRpYS5nZXRWaWRlbyhtZWRpYUF1dGgoY3JlZGVudGlhbHMpLCByZXF1ZXN0SWQsIHNpZ25hbCksXG4gICAgKTtcbiAgfVxuXG4gIGFzeW5jIHRyYW5zY3JpYmUoXG4gICAgcmVxdWVzdDogR3Jva1RyYW5zY3JpYmVSZXF1ZXN0ICYgTWVkaWFUb2tlblNlbGVjdGlvbixcbiAgKTogUHJvbWlzZTxNZWRpYURpc3BhdGNoUmVzdWx0PEdyb2tTdHRSZXNwb25zZT4+IHtcbiAgICBjb25zdCB2b2ljZSA9IHRoaXMucmVxdWlyZVZvaWNlKCk7XG4gICAgY29uc3QgeyBwcmVmZXJyZWRUb2tlbklkLCByZXF1aXJlUHJlZmVycmVkVG9rZW4sIC4uLnBvcnRSZXF1ZXN0IH0gPSByZXF1ZXN0O1xuXG4gICAgcmV0dXJuIHRoaXMucnVuUG9ydChcbiAgICAgIFwic3R0XCIsXG4gICAgICB7IHByZWZlcnJlZFRva2VuSWQsIHJlcXVpcmVQcmVmZXJyZWRUb2tlbiB9LFxuICAgICAgcmVxdWVzdC5zaWduYWwsXG4gICAgICAoY3JlZGVudGlhbHMpID0+IHZvaWNlLnRyYW5zY3JpYmUoY3JlZGVudGlhbHMuYWNjZXNzVG9rZW4sIHBvcnRSZXF1ZXN0KSxcbiAgICApO1xuICB9XG5cbiAgYXN5bmMgc3BlYWsoXG4gICAgcmVxdWVzdDogR3Jva1R0c1JlcXVlc3QgJiB7IHNpZ25hbD86IEFib3J0U2lnbmFsIH0gJiBNZWRpYVRva2VuU2VsZWN0aW9uLFxuICApOiBQcm9taXNlPE1lZGlhRGlzcGF0Y2hSZXN1bHQ8R3Jva1NwZWVjaFJlc3VsdD4+IHtcbiAgICBjb25zdCB2b2ljZSA9IHRoaXMucmVxdWlyZVZvaWNlKCk7XG4gICAgY29uc3QgeyBwcmVmZXJyZWRUb2tlbklkLCByZXF1aXJlUHJlZmVycmVkVG9rZW4sIC4uLnBvcnRSZXF1ZXN0IH0gPSByZXF1ZXN0O1xuXG4gICAgcmV0dXJuIHRoaXMucnVuUG9ydChcbiAgICAgIFwidHRzXCIsXG4gICAgICB7IHByZWZlcnJlZFRva2VuSWQsIHJlcXVpcmVQcmVmZXJyZWRUb2tlbiB9LFxuICAgICAgcmVxdWVzdC5zaWduYWwsXG4gICAgICAoY3JlZGVudGlhbHMpID0+IHZvaWNlLnNwZWFrKGNyZWRlbnRpYWxzLmFjY2Vzc1Rva2VuLCBwb3J0UmVxdWVzdCksXG4gICAgKTtcbiAgfVxuXG4gIC8vIC0tLS0gT2JzZXJ2YWJpbGl0eSAowqc1LjgpIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgdG9rZW5TdGF0ZXMoKTogR3Jva1Rva2VuU3RhdGVTbmFwc2hvdFtdIHtcbiAgICBjb25zdCBub3cgPSB0aGlzLm5vdygpO1xuXG4gICAgcmV0dXJuIFsuLi50aGlzLnRva2Vucy52YWx1ZXMoKV1cbiAgICAgIC5tYXAoKHRva2VuKTogR3Jva1Rva2VuU3RhdGVTbmFwc2hvdCA9PiB7XG4gICAgICAgIGNvbnN0IGJpbGxpbmcgPSB0b2tlbi5iaWxsaW5nPy52YWx1ZTtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSB0b2tlbi5zZXR0aW5ncz8udmFsdWU7XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBpZDogdG9rZW4uaWQsXG4gICAgICAgICAgbmFtZTogdG9rZW4ubmFtZSxcbiAgICAgICAgICB3ZWlnaHQ6IHRva2VuLndlaWdodCxcbiAgICAgICAgICBhY3RpdmU6IHRva2VuLmNyZWRlbnRpYWxzICE9PSB1bmRlZmluZWQgJiYgdG9rZW4uaW52YWxpZFJlYXNvbiA9PT0gdW5kZWZpbmVkLFxuICAgICAgICAgIGludmFsaWRSZWFzb246IHRva2VuLmludmFsaWRSZWFzb24gPz8gbnVsbCxcbiAgICAgICAgICBpbkZsaWdodDogdG9rZW4uaW5GbGlnaHQsXG4gICAgICAgICAgY29vbGRvd25VbnRpbDogbWFwUmVjb3JkKHRva2VuLmNvb2xkb3ducywgbm93KSxcbiAgICAgICAgICBxdW90YUJsb2NrZWRVbnRpbDogbWFwUmVjb3JkKHRva2VuLnF1b3RhQmxvY2tzLCBub3cpLFxuICAgICAgICAgIGVudGl0bGVtZW50QmxvY2tzOiBtYXBSZWNvcmQodG9rZW4uZW50aXRsZW1lbnRCbG9ja3MsIG5vdyksXG4gICAgICAgICAgbGFzdEJpbGxpbmc6XG4gICAgICAgICAgICBiaWxsaW5nPy5raW5kID09PSBcIm9rXCJcbiAgICAgICAgICAgICAgPyB7XG4gICAgICAgICAgICAgICAgICB1dGlsaXphdGlvblBjdDogYmlsbGluZy51dGlsaXphdGlvblBjdCxcbiAgICAgICAgICAgICAgICAgIHBlcmlvZFR5cGU6IGJpbGxpbmcucGVyaW9kVHlwZSxcbiAgICAgICAgICAgICAgICAgIHBlcmlvZEVuZDogYmlsbGluZy5wZXJpb2RFbmQsXG4gICAgICAgICAgICAgICAgICBmZXRjaGVkQXQ6IHRva2VuLmJpbGxpbmchLmZldGNoZWRBdCxcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDogbnVsbCxcbiAgICAgICAgICBxdW90YVRocmVzaG9sZDogdG9rZW4ucXVvdGFUaHJlc2hvbGQsXG4gICAgICAgICAgb3ZlclRocmVzaG9sZDogdG9rZW4ub3ZlclRocmVzaG9sZCxcbiAgICAgICAgICBvbkRlbWFuZEV4Y2x1ZGVkOiBzZXR0aW5ncz8ua2luZCA9PT0gXCJva1wiICYmIHNldHRpbmdzLm9uRGVtYW5kRW5hYmxlZCA9PT0gdHJ1ZSxcbiAgICAgICAgICB6ZHJWaWRlb0V4Y2x1ZGVkOiB0b2tlbi56ZHIgPT09IHRydWUsXG4gICAgICAgICAgbGFzdFRpZXI6IHRva2VuLmxhc3RUaWVyID8/IG51bGwsXG4gICAgICAgICAgcmVhdXRoUmVxdWlyZWQ6IHRva2VuLnJlYXV0aFJlcXVpcmVkLFxuICAgICAgICAgIGZhaWxvdmVyQ291bnQ6IHRva2VuLmZhaWxvdmVyQ291bnQsXG4gICAgICAgIH07XG4gICAgICB9KVxuICAgICAgLnRvU29ydGVkKChhLCBiKSA9PiBhLm5hbWUubG9jYWxlQ29tcGFyZShiLm5hbWUpKTtcbiAgfVxuXG4gIC8qKiBDaGF0LXByb3h5IHZlcnNpb24gZ2F0ZSBzdGF0ZSBmb3IgdGhlIHVzYWdlIGVuZHBvaW50LiAqL1xuICB2ZXJzaW9uR2F0ZVN0YXRlKCk6IEdyb2tWZXJzaW9uR2F0ZVN0YXRlIHtcbiAgICByZXR1cm4ge1xuICAgICAgdHJpcHBlZDogdGhpcy52ZXJzaW9uR2F0ZSAhPT0gdW5kZWZpbmVkLFxuICAgICAgbWluQ2xpZW50VmVyc2lvbjogdGhpcy52ZXJzaW9uR2F0ZT8ubWluQ2xpZW50VmVyc2lvbiA/PyBudWxsLFxuICAgICAgdHJpcHBlZEF0OiB0aGlzLnZlcnNpb25HYXRlPy50cmlwcGVkQXQgPz8gbnVsbCxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFNhbWUgc2hhcGUgYXMgT3BlbkFJRGlzcGF0Y2hlci5nZXRRdW90YVNuYXBzaG90OyByZWFkcyBjYWNoZXMgb25seS4gKi9cbiAgZ2V0UXVvdGFTbmFwc2hvdCgpOiBBcnJheTx7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIHVzZWRQZXJjZW50OiBudW1iZXIgfCBudWxsO1xuICAgIHRocmVzaG9sZDogbnVtYmVyIHwgbnVsbDtcbiAgICBibG9ja2VkOiBib29sZWFuO1xuICAgIHJlc2V0c0F0OiBudW1iZXIgfCBudWxsO1xuICAgIHpkclZpZGVvRXhjbHVkZWQ6IGJvb2xlYW47XG4gIH0+IHtcbiAgICBjb25zdCBub3cgPSB0aGlzLm5vdygpO1xuXG4gICAgcmV0dXJuIFsuLi50aGlzLnRva2Vucy52YWx1ZXMoKV1cbiAgICAgIC5tYXAoKHRva2VuKSA9PiB7XG4gICAgICAgIGNvbnN0IGJpbGxpbmcgPSB0b2tlbi5iaWxsaW5nPy52YWx1ZTtcblxuICAgICAgICBjb25zdCBmcmVzaCA9XG4gICAgICAgICAgYmlsbGluZz8ua2luZCA9PT0gXCJva1wiICYmXG4gICAgICAgICAgdG9rZW4uYmlsbGluZyEuZ2VuZXJhdGlvbiA9PT0gdG9rZW4uZ2VuZXJhdGlvbiAmJlxuICAgICAgICAgIG5vdyAtIHRva2VuLmJpbGxpbmchLmZldGNoZWRBdCA8IEdST0tfTE9PS1VQX0NBQ0hFX1RUTF9NUztcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIG5hbWU6IHRva2VuLm5hbWUsXG4gICAgICAgICAgdXNlZFBlcmNlbnQ6IGZyZXNoID8gYmlsbGluZy51dGlsaXphdGlvblBjdCA6IG51bGwsXG4gICAgICAgICAgdGhyZXNob2xkOiB0b2tlbi5xdW90YVRocmVzaG9sZCxcbiAgICAgICAgICBibG9ja2VkOlxuICAgICAgICAgICAgdG9rZW4ub3ZlclRocmVzaG9sZCB8fCBbLi4udG9rZW4ucXVvdGFCbG9ja3MudmFsdWVzKCldLnNvbWUoKHVudGlsKSA9PiB1bnRpbCA+IG5vdyksXG4gICAgICAgICAgcmVzZXRzQXQ6IGZyZXNoID8gYmlsbGluZy5wZXJpb2RFbmRNcyA6IG51bGwsXG4gICAgICAgICAgemRyVmlkZW9FeGNsdWRlZDogdG9rZW4uemRyID09PSB0cnVlLFxuICAgICAgICB9O1xuICAgICAgfSlcbiAgICAgIC50b1NvcnRlZCgoYSwgYikgPT4gYS5uYW1lLmxvY2FsZUNvbXBhcmUoYi5uYW1lKSk7XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQWlFQSxNQUFNLFNBQVMsVUFBVSxDQUFDLFNBQVMsaUJBQWlCLENBQUM7QUFJckQsTUFBYSwyQkFBMkI7QUFDeEMsTUFBYSx1QkFBdUI7QUFDcEMsTUFBYSwyQkFBMkI7QUFDeEMsTUFBYSw0QkFBNEI7QUFDekMsTUFBYSwrQkFBK0I7QUFDNUMsTUFBYSw0QkFBNEI7QUFDekMsTUFBYSwrQkFBK0I7QUFDNUMsTUFBYSxnQkFBZ0I7QUFDN0IsTUFBYSxxQkFBcUI7QUFDbEMsTUFBYSwrQkFBK0I7O0FBRzVDLE1BQWEsa0NBQWtDOztBQUcvQyxNQUFhLDhCQUE4QjtBQUMzQyxNQUFNLDZCQUE2QjtBQUNuQyxNQUFNLDBCQUEwQjtBQU1oQyxJQUFhLDBCQUFiLGNBQTZDLE1BQU07Q0FDakQsT0FBZ0I7Q0FFaEIsWUFBWSxTQUFpQjtFQUMzQixNQUFNLE9BQU87RUFDYixLQUFLLE9BQU87Q0FDZDtBQUNGO0FBRUEsSUFBYSx1QkFBYixjQUEwQyxNQUFNO0NBQzlDLE9BQWdCO0NBQ2hC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
|