From 2f30cb66e2e22d4f0cc248bea747a225da44c5ce Mon Sep 17 00:00:00 2001 From: lua-stefan-kruger Date: Tue, 22 Sep 2026 10:36:10 +0100 Subject: [PATCH 1/2] feat(plugin): lua drains, logs --since/--follow, 18 sources [PRO-1896] Teaches the lua-agent-builder plugin the lua-cli 3.38.0 log-drain surface and the `lua logs` read window, all verified against lua-core-services `main` (src/cli/command-definitions.ts, src/commands/drains.ts, drains.mutations.ts, logs.ts, src/api/drains.api.service.ts, src/utils/aliases.ts, @lua/shared-types log-drain.types.ts and vm-execution-log.types.ts, @lua/shared-observability drain-scrubber.ts). - /lua-drains: a new slash for all eleven verbs (list, status, deliveries, create, update, delete, test, verify, pause, resume, rotate-secret) with the single-permission pattern, the secret rules (the signing secret is minted server-side and printed once, never stored by the plugin; --header-from-env NAME=ENV_VAR for a vendor key; no secret in the transcript) and the JSON contract (--json on stdout, chrome on stderr). - lib/knowledge/log-drains.md: what a drain is, the six states, the verification handshake (only http echoes X-Lua-Verify), the http / otlp / datadog / betterstack presets, delivery guarantees and the Retry-After contract, the quota ladder, the scrubber and its [redacted:] markers, logs:read vs the sensitive logs:manage. - /lua-logs, lua-debug and lua-qa now read a window instead of paging: --since / --until / --environment / --follow, the full 18-source --type list, a "watch a promote" recipe, and T0-bounded QA log scans. Corrects the stale "a log entry has no environment field" claim. - Permissions: allow lua drains list|status|deliveries|test, ask create|update|delete|verify|pause|resume|rotate-secret. Deliberately not added to lib/tokenizer.mjs -- a drain ships logs, it does not change what runs in production, so LUA_DEPLOY_CONFIRMED=1 would be the wrong sentence. - lint-cli-flags drops `lua logs --follow` (it exists now) and gains `lua logs --min-severity` and `lua drains update --type`; lint-knowledge-commands learns the drains.action verbs. - 1.4.0 -> 1.5.0, PINNED_MIN_LUA_CLI 3.37.0 -> 3.38.0, MCP bundle rebuilt. Checks: eslint + 16 of 17 lints pass, 428 plugin tests and 147 MCP tests pass with coverage thresholds met. lint-pinned-version is RED by design until lua-cli 3.38.0 is published, the same window 1.4.0 sat in before 3.37.0 shipped. Co-Authored-By: Claude Fable 5.1 --- .claude-plugin/marketplace.json | 2 +- README.md | 2 +- docs/USER_GUIDE.md | 20 ++- .../.claude-plugin/plugin.json | 2 +- plugins/lua-agent-builder/CHANGELOG.md | 37 ++++ plugins/lua-agent-builder/README.md | 10 +- .../lua-agent-builder/agents/lua-architect.md | 3 +- plugins/lua-agent-builder/agents/lua-debug.md | 10 ++ plugins/lua-agent-builder/agents/lua-qa.md | 6 +- .../lua-agent-builder/commands/lua-drains.md | 64 +++++++ .../lua-agent-builder/commands/lua-logs.md | 32 +++- .../hooks/check-lua-version.mjs | 34 ++-- .../lib/knowledge/cli-reference.md | 11 +- .../lib/knowledge/log-drains.md | 161 ++++++++++++++++++ .../lib/permissions-template.json | 13 +- .../mcp/lua-platform/dist/server.js | 6 +- .../mcp/lua-platform/package-lock.json | 4 +- .../mcp/lua-platform/package.json | 2 +- .../mcp/lua-platform/src/api-client.mjs | 2 +- .../mcp/lua-platform/src/server.mjs | 6 +- plugins/lua-agent-builder/package-lock.json | 4 +- plugins/lua-agent-builder/package.json | 2 +- .../scripts/lint-cli-flags.mjs | 14 +- .../scripts/lint-knowledge-commands.mjs | 7 +- .../test/lib/permissions-mirror.test.mjs | 18 ++ 25 files changed, 414 insertions(+), 58 deletions(-) create mode 100644 plugins/lua-agent-builder/commands/lua-drains.md create mode 100644 plugins/lua-agent-builder/lib/knowledge/log-drains.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 11fdd88..8a4eb32 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "name": "lua-agent-builder", "source": "./plugins/lua-agent-builder", "description": "Build, test, and deploy Lua AI agents from inside Claude Code", - "version": "1.4.0", + "version": "1.5.0", "homepage": "https://github.com/lua-ai-global/claude-code-lua-plugin#readme", "repository": "https://github.com/lua-ai-global/claude-code-lua-plugin.git", "license": "MIT", diff --git a/README.md b/README.md index 183022c..032c3b2 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Then `/lua-auth`: an existing credential is kept; a new login runs `lua auth con | Plugin | Description | |---|---| -| [`lua-agent-builder`](./plugins/lua-agent-builder/) | 20 slash commands, 5 subagents, 10 hooks, a 5-file knowledge base verified against lua-cli source (3.33.0 base; 1.3.0 added per-step model classes and the workflow autonomy envelope from lua-cli 3.36.0, 1.4.0 the Job-tier billing rules and the lua-cli 3.37.0 cost read-outs, read from `main`), a local read-only platform MCP server and the public docs MCP | +| [`lua-agent-builder`](./plugins/lua-agent-builder/) | 21 slash commands, 5 subagents, 10 hooks, a 6-file knowledge base verified against lua-cli source (3.33.0 base; 1.3.0 added per-step model classes and the workflow autonomy envelope from lua-cli 3.36.0, 1.4.0 the Job-tier billing rules and the lua-cli 3.37.0 cost read-outs, 1.5.0 log drains and the `lua logs` read window from lua-cli 3.38.0, read from `main`), a local read-only platform MCP server and the public docs MCP | ## Quick walkthrough diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index bd67a48..4e7d5a3 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -2,7 +2,7 @@ A complete walkthrough of the [`lua-agent-builder`](https://github.com/lua-ai-global/claude-code-lua-plugin) Claude Code plugin — what it does, how to install it, the canonical build loops (tools, integrations, workflows, templates), the safety model, and what to do when things go wrong. -The plugin wraps **lua-cli 3.37.0** (the pinned minimum: the 3.33.0 base, the 3.36.0 workflow verbs and the 3.37.0 Job-billing read-outs, all marked ⏳ in the knowledge base), the TypeScript SDK/CLI for the Lua agent platform. It has nothing to do with the Lua programming language. Everything in this guide (command shapes, SDK types, API routes) was verified against the lua-cli and lua-api source, not only the public docs. +The plugin wraps **lua-cli 3.38.0** (the pinned minimum: the 3.33.0 base, the 3.36.0 workflow verbs, the 3.37.0 Job-billing read-outs and the 3.38.0 log drains plus the `lua logs` read window, all marked ⏳ in the knowledge base), the TypeScript SDK/CLI for the Lua agent platform. It has nothing to do with the Lua programming language. Everything in this guide (command shapes, SDK types, API routes) was verified against the lua-cli and lua-api source, not only the public docs. If you just want to start: [Installation](#installation) → [Your first agent](#your-first-agent). @@ -34,7 +34,7 @@ If you just want to start: [Installation](#installation) → [Your first agent]( - **Slash commands** wrap every `lua` command you need (`/lua-init`, `/lua-new`, `/lua-test`, `/lua-workflow`, `/lua-push`, `/lua-deploy`, `/lua-status`, …), collect the inputs up-front in one prompt, and surface errors with the CLI's typed exit codes explained. - **Subagents** do the heavy lifting in their own context with restricted tools: the architect plans, the skill-builder scaffolds and tests any primitive (including workflows, triggers, devices and voices), the debug agent diagnoses failures, the deploy pilot runs the gated ship sequence, the QA agent runs conversational and offline-workflow suites. -- **A knowledge base** (`lib/knowledge/`) gives those agents the exact SDK shapes, workflow builder, CLI matrix, integration patterns and decision trees — checked against lua-cli source (3.33.0 base; the ⏳ additions against the `main` / `feat/workflow-autonomy` refs lua-cli 3.36.0 and 3.37.0 are cut from). +- **A knowledge base** (`lib/knowledge/`) gives those agents the exact SDK shapes, workflow builder, CLI matrix, integration patterns, decision trees and log-drain contract — checked against lua-cli source (3.33.0 base; the ⏳ additions against the `main` / `feat/workflow-autonomy` refs lua-cli 3.36.0, 3.37.0 and 3.38.0 are cut from). - **Hooks** probe your environment, inject the current agent into Claude's context, and gate every production-affecting verb. - **Two MCP servers**: a local read-only platform server (what's deployed, versions, logs) and the public docs MCP. @@ -45,12 +45,14 @@ If you just want to start: [Installation](#installation) → [Your first agent]( | Requirement | Why | How | |---|---|---| | **Node.js ≥ 18** | hooks and the MCP server are Node ESM | macOS `brew install node@20` · Windows `winget install OpenJS.NodeJS.LTS` · Linux NodeSource / `nvm install 20` | -| **lua-cli ≥ 3.37.0** | the command shapes and output the plugin reads (3.36.0 added the workflow policy, clear-gate and recompose verbs; 3.37.0 adds no verb at all — it prints what a run cost: the `Tokens:` line, the per-step token columns, the `⚙` Job-model line, the engine-aware budget wording, the budget events in `workflows logs`) | `npm install -g lua-cli` (or `/lua-update`) | +| **lua-cli ≥ 3.38.0** | the command shapes and output the plugin reads (3.36.0 added the workflow policy, clear-gate and recompose verbs; 3.37.0 added no verb at all — it prints what a run cost; **3.38.0 adds the whole `lua drains` command and the `lua logs` read window `--since` / `--until` / `--environment` / `--follow`, with all 18 log sources reachable by `--type`**) | `npm install -g lua-cli` (or `/lua-update`) | | **Claude Code** | the host | https://claude.com/claude-code | | **A Lua account** | to talk to `api.heylua.ai` | https://admin.heylua.ai — `/lua-auth` guides the login | `/lua-doctor` checks all of these and offers consent-gated fixes. Platforms: macOS 14+, Ubuntu 22.04+, Windows 11; CI runs the suite on all three with Node 18 and 20. +New in the 1.5.0 knowledge base: **log drains.** A drain copies the organization's agent log records to a destination you own — a receiver of your own, an OTLP collector, Datadog or Better Stack — as they happen, and it is a *copy*: everything it ships stays readable with `lua logs`. `/lua-drains` covers all eleven verbs, the signing secret that is printed once and never stored, and `--header-from-env` for a vendor key. The same release teaches `/lua-logs`, `lua-debug` and `lua-qa` to read a **window** (`--since 15m`, `--until`, `--environment sandbox`, `--follow`) instead of paging backwards and stitching timestamps together. + New in the 1.4.0 knowledge base: **what a workflow run costs.** A Job-tier step is billed per model **reply**, not per attempt — a coding turn that answers two hundred times is charged two hundred times — which is one credit per reply on a legacy plan and, on a seat plan, **actions** (the call's price band × the model's multiplier, with cached prompt tokens counted at the fraction the provider charges). `lua workflows raise-budget --credits ` keeps its flag name on both plans, but `` is a cap in the run's OWN unit — actions on a seat plan — and `/lua-workflow status` names it. A Job step also runs on its own `model`, failing that the platform's Job default, failing that your organization's — never the agent's model — so leaving `model` off a Job step is a cost decision. Those are platform rules, true on any CLI. **lua-cli 3.37.0** is what makes them visible: a `Tokens:` line and `Uncached` / `Cached` / `Output` columns under `--steps` (signals, never charges — do not add them up), `⚙ : model … · × · chosen by ` on Job rows, `finished past the cap` on a run that crossed its budget and completed anyway, the three budget events in `lua workflows logs`, a `raise-budget` confirmation that names the unit, and a `job-model-default` advisory at `lua workflows deploy`. 3.37.0 adds no command and no option, so nothing the plugin runs breaks on an older CLI — it simply prints less. Two platform features are new in the 1.3.0 knowledge base: **per-step model classes** (`taskClass`, `model: 'class/fast|balanced|strong'`, `requires`, `effort`, `lua models list --workflows`, `lua workflows policy models`, `lua push workflow --apply-effort`, `clear-gate`, `recompose`) and the **workflow autonomy envelope** (`lua workflows policy autonomy`, the `Consent: auto (policy)` line). They need **lua-cli 3.36.0 or later** — the plugin's pinned minimum since 1.3.0; the knowledge files mark them ⏳ and were read from the lua-cli source on `main` / `feat/workflow-autonomy`, the refs 3.36.0 is cut from, so the slashes tell you when your CLI predates a verb (exit 2) instead of guessing. @@ -131,7 +133,7 @@ Chat-composed workflows can't be deployed from the CLI (`WORKFLOW_DYNAMIC`); `/l ## Slash commands reference -20 slash commands; each asks at most one question (`x-lua-multi-step: true` marks `/lua-doctor`, `/lua-auth`, `/lua-init`). +21 slash commands; each asks at most one question (`x-lua-multi-step: true` marks `/lua-doctor`, `/lua-auth`, `/lua-init`). | Slash | Wraps | Notes | |---|---|---| @@ -146,7 +148,8 @@ Chat-composed workflows can't be deployed from the CLI (`WORKFLOW_DYNAMIC`); `/l | `/lua-test [type]` | `lua test --ci --name … --input …` | failures → debug subagent | | `/lua-workflow ` | `lua workflows …`, `lua test workflow` | read-only verbs run at once (⏳ incl. `policy models\|autonomy get`, `models`); start/approve/signal/resume/cancel (⏳ `clear-gate`, `policy … set`, `recompose`) confirm once; deploy → `/lua-deploy` | | `/lua-chat` | `lua chat --ci -e … -m … -t` | always an explicit thread | -| `/lua-logs` | `lua logs --ci --type … --json` | real `--type` list (`mastra` is not valid) | +| `/lua-logs` | `lua logs --ci --type … --json` | the real 18-source `--type` list (`mastra` is not valid); ⏳ 3.38.0 `--since` / `--until` (ISO instant or `15m`/`2h`/`7d`, resolved by the server's clock), `--environment production\|sandbox`, `--follow` (polls ~2 s, refuses `--page`) | +| `/lua-drains` | `lua drains list\|status\|deliveries\|create\|update\|delete\|test\|verify\|pause\|resume\|rotate-secret` | ⏳ 3.38.0. Reads run at once; the seven configuration verbs confirm once through the Bash prompt. Needs `logs:manage` (a sensitive scope). The signing secret is printed once by the platform and never stored by the plugin; header values are prompted or read from an environment variable with `--header-from-env NAME=ENV_VAR`, never typed on a command line | | `/lua-env` | `lua env --list \| -k KEY -v VALUE \| -k KEY --delete` | environment + key + value collected once; the Bash prompt is the confirmation; the value is never echoed, listings show masked values | | `/lua-integrations` | `lua integrations available\|list\|info\|webhooks …\|mcp …` | read-only verbs run at once; disconnect/convert/webhooks/mcp mutations confirm once via the Bash prompt; `connect`/`update` (browser OAuth) are printed for your terminal | | `/lua-sync` | `lua status --json`, `lua sync --check`, `--pull`, `--push` | `--push` sends agent config only | @@ -181,6 +184,7 @@ Subagents never ask questions; the slash that spawned them already collected the - `cli-reference.md` — global flags, exit codes, credential resolution, project layout, the full command matrix, the push/deploy matrix, agent versions, marketplace templates, the docs URL map - `integrations.md` — Unified.to connectors, auto-provisioned MCPs, `Integrations.passthrough`, event subscriptions vs platform triggers, channels - `decision-trees.md` — task → primitive routing, job vs workflow, webhook vs trigger, data placement, build order +- `log-drains.md` — ⏳ 3.38.0: what a drain is, the eleven verbs, the six states, the selectors, the ownership-verification handshake (only `http` echoes a token), the http/otlp/datadog/betterstack presets, delivery guarantees and the `Retry-After` contract, the quota ladder, the scrubber and its `[redacted:]` markers, and `logs:read` vs the sensitive `logs:manage` --- @@ -188,7 +192,7 @@ Subagents never ask questions; the slash that spawned them already collected the | Event | Hook | What it does | |---|---|---| -| SessionStart | `check-lua-version` | warns (never blocks) if lua-cli < 3.37.0, pointing at `/lua-update` / `npm i -g lua-cli@latest` | +| SessionStart | `check-lua-version` | warns (never blocks) if lua-cli < 3.38.0, pointing at `/lua-update` / `npm i -g lua-cli@latest` | | SessionStart | `detect-project` | "✓ Lua agent project detected: " from `lua.skill.yaml` | | SessionStart | `check-lua-auth` | probes `lua models list --json --ci` (1–2 s); exit 9 → recommends `/lua-auth`, exit 11 → API-unreachable note, timeout → "could not confirm" | | UserPromptSubmit | `inject-context` | `[lua] agent: / org: ` every prompt | @@ -240,7 +244,7 @@ The rules `/lua-doctor` merges (`lib/permissions-template.json`): - **deny** — anything with `--auto-deploy`, `lua auth configure|key|logout*`, and the alternative binaries `heylua *` / `lua-ai *` wholesale (same program; the plugin only ever emits `lua`). The bare production verbs (`lua deploy`, `lua version promote`, …) are **deliberately not in `deny` or `ask`**: Claude Code evaluates those two tiers past a leading env assignment, so a `Bash(lua deploy*)` deny would also block the confirmed `LUA_DEPLOY_CONFIRMED=1 lua deploy …` form and no deploy could ever run (this is documented at code.claude.com/docs/en/permissions and was confirmed live). The bare forms are blocked by the `confirm-deploy` hook instead — see below. - **allow** — the prefixed production verbs (`LUA_DEPLOY_CONFIRMED=1 lua deploy*`, `… lua skills|webhooks|jobs|preprocessors|postprocessors deploy*`, `… lua workflows deploy|activate*`, `… lua version promote*`, `… lua persona production deploy*`, `… lua mcp activate*`, `… lua marketplace template publish|apply*`), every read-only `lua` verb the slashes and subagents use, `lua push * --ci --force*`, `lua sync --check|--pull|--push`, `lua version create*` (a snapshot; nothing goes live until `promote`), and read-only git. -- **ask** — deletes, `lua env *`, `lua pull`, `lua chat clear`, `lua source rollback`, `lua version delete`, workflow run control (`start`, `cancel`, `approve`, `signal`, `resume`, `retry-step`, `resolve-step`, `raise-budget`, `deactivate`, `schedules`, `goals`, `export`, `archive-runs`, ⏳ `clear-gate`, `recompose`), ⏳ the org-wide policy writes `lua workflows policy models|autonomy set` (`policy … get` is allowed), `lua devices enable|disable`, `lua marketplace skill publish|unpublish|unlist|transfer`, integration connects/changes, `npm install -g lua-cli`, system installs. For the workflow run-control verbs this prompt **is** the single confirmation: `/lua-workflow` shows you the exact command in the permission prompt and does not ask a second time. `/lua-env` (`lua env *` — kept in `ask` even for `--list`, because the CLI prints masked values) and `/lua-integrations` (connect/update/disconnect/convert, webhook create/pause/resume/delete, MCP activate/deactivate) rely on the same prompt. +- **ask** — deletes, log-drain configuration (`lua drains create|update|delete|verify|pause|resume|rotate-secret`; the read verbs `list|status|deliveries|test` are allowed instead, and none of the eleven is a production verb — a drain ships logs, it does not change what runs), `lua env *`, `lua pull`, `lua chat clear`, `lua source rollback`, `lua version delete`, workflow run control (`start`, `cancel`, `approve`, `signal`, `resume`, `retry-step`, `resolve-step`, `raise-budget`, `deactivate`, `schedules`, `goals`, `export`, `archive-runs`, ⏳ `clear-gate`, `recompose`), ⏳ the org-wide policy writes `lua workflows policy models|autonomy set` (`policy … get` is allowed), `lua devices enable|disable`, `lua marketplace skill publish|unpublish|unlist|transfer`, integration connects/changes, `npm install -g lua-cli`, system installs. For the workflow run-control verbs this prompt **is** the single confirmation: `/lua-workflow` shows you the exact command in the permission prompt and does not ask a second time. `/lua-env` (`lua env *` — kept in `ask` even for `--list`, because the CLI prints masked values) and `/lua-integrations` (connect/update/disconnect/convert, webhook create/pause/resume/delete, MCP activate/deactivate) rely on the same prompt. Precedence is deny → ask → allow. **The production gate is the `confirm-deploy` hook**: it runs on every Bash call, classifies the command with `lib/tokenizer.mjs` (every canonical spelling, every lua-cli alias — `publish`, `on`, `enable`, `submit`, `rollout`, `prod` … — and all three binaries), and blocks a bare production verb with exit 2. A hook block takes precedence over any allow rule, including a broad `Bash(lua *)` you may have in your own settings, so the gate holds even without the template. It refuses shell wrappers and pipes even with the prefix. Only the deploy pilot and `/lua-template` emit the prefix, and only after your one confirmation; the template's allow rules let that confirmed command run without a second prompt. A test (`test/lib/permissions-mirror.test.mjs`) and a lint fail if a deny/ask rule would ever shadow a confirmed form or an allow rule admit a bare one. @@ -278,7 +282,7 @@ Precedence is deny → ask → allow. **The production gate is the `confirm-depl **Where does my code go?** From lua-cli to `api.heylua.ai` (and `webhook.heylua.ai`, `cdn.heylua.ai`). The MCP server talks to `api.heylua.ai` and, for a session login, to Google's token endpoint to refresh the session. Claude Code sends the conversation to Anthropic per its own policy. -**How do I update the plugin?** `/plugin marketplace update claude-code-lua-plugin` then reinstall; 1.4.0 targets lua-cli 3.37.0 (the pin; 3.36.0 carries the per-step model classes and workflow autonomy verbs, 3.37.0 the Job-billing read-outs the plugin describes). +**How do I update the plugin?** `/plugin marketplace update claude-code-lua-plugin` then reinstall; 1.5.0 targets lua-cli 3.38.0 (the pin; 3.36.0 carries the per-step model classes and workflow autonomy verbs, 3.37.0 the Job-billing read-outs the plugin describes). **Why does my run show no `Tokens:` line or `⚙` model line?** Those print only when the server projected the figures — an unmetered run, a run metered before the platform shipped them, or a CLI below 3.37.0. A missing line means "nobody reported", not a fault; and the token figures are never what you were charged (the `Budget:` line is), so never sum them. diff --git a/plugins/lua-agent-builder/.claude-plugin/plugin.json b/plugins/lua-agent-builder/.claude-plugin/plugin.json index bb8cd18..1270bf5 100644 --- a/plugins/lua-agent-builder/.claude-plugin/plugin.json +++ b/plugins/lua-agent-builder/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "lua-agent-builder", - "version": "1.4.0", + "version": "1.5.0", "description": "Build, test, and deploy Lua AI agents from inside Claude Code", "author": { "name": "Lua AI", diff --git a/plugins/lua-agent-builder/CHANGELOG.md b/plugins/lua-agent-builder/CHANGELOG.md index 5ff94cb..8c500ca 100644 --- a/plugins/lua-agent-builder/CHANGELOG.md +++ b/plugins/lua-agent-builder/CHANGELOG.md @@ -2,6 +2,43 @@ All notable changes to the `lua-agent-builder` plugin. Versions follow the tag `release-prod.yml` cuts from `package.json` (`v`). lua-cli is a TypeScript SDK/CLI; it is unrelated to the Lua programming language. +## 1.5.0 — 2026-09-22 + +**Log drains, and a `lua logs` that reads a window instead of a page.** Two shipped CLI surfaces reach the plugin: the whole `lua drains` command — the rules that copy an organization's agent log records to a destination the customer owns — and the `lua logs` read window `--since` / `--until` / `--environment` / `--follow`, with all **18** log sources finally reachable through `--type`. Everything below was read from lua-core-services `main`: `packages/lua-cli/src/cli/command-definitions.ts` (the `drains` and `logs` declarations), `src/commands/drains.ts`, `src/commands/drains.mutations.ts`, `src/commands/logs.ts`, `src/api/drains.api.service.ts`, `src/utils/aliases.ts` (`drains.action`, `logs.type`), `@lua/shared-types` `log-drain.types.ts` / `vm-execution-log.types.ts` and `@lua/shared-observability` `drain-scrubber.ts` — never from the public docs, which agree with all of it and are cited only as a destination for the user. [PRO-1896] + +**Version.** lua-cli **3.38.0** carries all of it and nothing below it does: `lua drains` exits 1 as an unknown command, and the four new `lua logs` options exit 1 as unknown options. `PINNED_MIN_LUA_CLI` moves 3.37.0 → 3.38.0, and unlike the last two bumps this one adds real **command surface**, so every addition is marked "⏳ lua-cli 3.38.0 or later" in the text that emits it. + +### `/lua-drains` (new) + +- `commands/lua-drains.md` covers all eleven verbs — `list`, `status`, `deliveries`, `create`, `update`, `delete`, `test`, `verify`, `pause`, `resume`, `rotate-secret` — with the plugin's single-permission pattern: one `AskUserQuestion`, then the Bash prompt (the `ask` tier) as the single confirmation for a mutation, and no `AskUserQuestion` on top of it. +- **Secret handling is the point of the file.** The HMAC signing secret is never an input: the platform mints it and the CLI prints it once (at `create`, and at a `rotate-secret` without `--finalize`), so the slash never copies it into a file, an env var, a commit or its own reply. A header VALUE never reaches a command line — `--header ` prompts hidden and therefore cannot work under `--ci`; the CI form is `--header-from-env =`, and an unset variable is exit 2 naming the *variable*. For a vendor preset (`--header DD-API-KEY`, `--header Authorization` as `Bearer `) the slash stops at the secret and hands the user the exact line to run in their own terminal. +- **The JSON contract**: `--json` on **stdout** (`lua drains create --json | jq -r '.secret'` has to work), the verification instructions and the content acknowledgement on **stderr** (`emitAside`), a typed envelope rather than the `✖` line for a refusal that escapes a verb. The per-verb payloads are tabulated. +- `--include-content` is run **without** `--yes` first: the CLI prints `CONTENT_WARNING_TEXT` and `CONTENT_ACKNOWLEDGEMENT_TEXT` and exits non-zero having created nothing, the slash quotes both verbatim, and the user's approval of the re-run *is* the acknowledgement. +- ⚠ `lua drains status` exits **2** both for a usage error and for "some drain is `failing`" — decide from `drains[].state` in `--json`, never from the exit code. `test` (30 s) and `verify` (60 s) are 202-then-poll: `pending: true` with exit 1 means *not yet*, not *failed*. + +### `lib/knowledge/log-drains.md` (new, the sixth knowledge file) + +What a drain is (an **organization** resource — `--org`, else `lua.skill.yaml`, else a credential that reaches exactly one org) · the eleven verbs and their aliases · the six states (`pending_verification healthy degraded failing paused disabled`) and the transitions that surprise people (a resume lands in `degraded`, an endpoint change returns to `pending_verification`, 24 h failing auto-pauses) · the selectors, including the 19 selectable sources (the 18 `AGENT_LOG_SOURCES` plus `execution`) and the two content ones · the **verification handshake** (only `http` echoes `X-Lua-Verify`, with the `/.well-known/lua-drain-verify` fallback; `otlp`/`datadog`/`betterstack` accept any 2xx test post; `token_not_echoed` is the common failure and the phrase to search for; 5 attempts per drain per hour) · the four presets and what each does with `--endpoint` / `--site` / `--format` · delivery guarantees, the retry/terminal status split (OTLP is the one place a `500` is terminal), the 6-hour horizon and the **`Retry-After`** contract — honoured exactly, clamped at an hour, and the drain *routes* answer `429 DRAIN_RATE_LIMITED` with a bare `Retry-After` and no `X-RateLimit-*` headers · the quota ladder (80% notify → 100% drop `debug` → 125% drop `info` → 150% pause with reason `quota`; `warn` and `error` never go first) · the **scrubber**, its built-in rule classes and the `[redacted:builtin]` / `[redacted:lua-token]` / `[redacted:vendor-key]` / `[redacted:org:]` markers — with the warning that scrubbing only catches shapes it recognises · **`logs:read` vs `logs:manage`**, the second being *sensitive* (a `logs:*` wildcard does not satisfy it) and the deprecation window on the old read scopes. + +### `--since` / `--follow` everywhere a window was being reconstructed + +- `commands/lua-logs.md`: the full 18-source `--type` list (on 3.37.0 and older the alias table was hand-listed and `trigger`, `model-resolver`, `workflow-step`, `workflow-script`, `workflow` were exit 2, reachable only through the `tail_logs` MCP tool — 3.38.0 derives `logs.type` from `AGENT_LOG_SOURCES`), the read window, and `--follow`'s real semantics: it POLLS (no SSE route), refuses `--page`, advances on the newest timestamp it printed with id de-duplication, and under `--json` emits one `{ logs, nextCursor, pagination }` envelope per poll that produced rows. +- `agents/lua-debug.md` gains step **2b** and the **"watch a promote"** recipe — `lua logs --ci --type all --since --follow --json`, selecting `subType` `error`/`warn` client-side — plus a drain-triage entry (`status` → `deliveries` → `test` → `verify`) routing the fix to `/lua-drains`. +- `agents/lua-qa.md` records `T0` before the first turn and scans with `--since --environment ` instead of pulling 100 rows and filtering by timestamp; `--follow` is called out as wrong for an unattended suite. +- ⚠ **`lua logs` has no severity flag.** `--min-severity` is a log-*drain* selector; severity on a log read is filtered client-side on `subType`. `scripts/lint-cli-flags.mjs` now denies `lua logs --min-severity` so the confusion cannot ship. +- ⚠ **A log entry now HAS an environment field.** The plugin said "there is no `environment` field" in `lua-logs.md`, `lua-qa.md` and `cli-reference.md`; `AgentLogMetadata` gained `orgId`, `environment`, `agentVersion`, `executionId`, `executionSeq`, `traceparent`, `truncated` / `droppedLines`. It is optional and additive, and a row without it reads as **`production`** — on the drain matcher and under `--environment` alike — so its absence never means sandbox. `metadata.channel === 'dev'` is still CLI traffic in *either* environment and is still not an environment marker. + +### Plugin machinery + +- `lib/permissions-template.json`: allow `lua drains list|status|deliveries|test`; ask `lua drains create|update|delete|verify|pause|resume|rotate-secret`. They are **not** added to `lib/tokenizer.mjs`: nothing a drain verb does changes what runs in production, and `LUA_DEPLOY_CONFIRMED=1` (“the user confirmed a deploy”) would be the wrong sentence — `confirm-deploy` would send the user to `/lua-deploy` for a log-shipping change. The per-verb spelling is deliberate: a blanket `Bash(lua drains *)` would either prompt for a read or admit `rotate-secret`. `test/lib/permissions-mirror.test.mjs` pins every one of the eleven. +- `scripts/lint-knowledge-commands.mjs`: the eleven `drains.action` verbs join the action table. `scripts/lint-cli-flags.mjs`: `lua logs --follow` is **removed** from the denylist — it was denied because it did not exist, and PRO-1838 shipped it; the entry is deleted rather than inverted, which is the mistake that made the plugin emit `lua sync --accept` for months. Two new entries take its place (`lua logs --min-severity`, `lua drains update --type`). +- Bump 1.4.0 → 1.5.0 everywhere; `mcp/lua-platform/dist/server.js` rebuilt; `PINNED_MIN_LUA_CLI` 3.37.0 → 3.38.0 (`hooks/check-lua-version.mjs`, both READMEs, the user guide). `scripts/lint-pinned-version.mjs` compares the pin with npm's `latest` and is therefore **red by design until lua-cli 3.38.0 is published** — the same window 1.4.0 sat in before 3.37.0 shipped; it was not weakened. + +### Unchanged on purpose + +- No hook behaviour changes beyond the pin: `hooks/hooks.json`, `confirm-deploy`, `post-deploy-smoke` and `lib/tokenizer.mjs` are untouched, because a log drain is organization configuration and not a production verb. +- The `tail_logs` MCP tool is unchanged and has no window parameter; it stays documented as the fallback when the installed CLI is older than 3.38.0. + ## 1.4.0 — 2026-09-20 **What a workflow run costs, and why.** The plugin described a Job-tier attempt as a flat 4 credits and every run budget in "credits". Both are wrong for a priced run: a Job step is billed per model **REPLY** — one credit per reply on a legacy plan, **actions** (the call's price band × the resolved model's multiplier, cached prompt tokens at the fraction the provider charges) on a seat plan — and a coding turn makes dozens of replies in ONE attempt. Read from lua-core-services `main` at `ebcf6689c` (the lua-cli 3.37.0 release, `#3095`; the billing train `#3094`) — the CLI renderers in `src/commands/workflows.ts`, the wire shapes in `src/interfaces/workflows.ts`, the deploy advisory in lua-api's developer workflow service, the Job model legs and their labels in shared-types, the cache weighting in lua-core's Job rate module — never from the public docs. diff --git a/plugins/lua-agent-builder/README.md b/plugins/lua-agent-builder/README.md index 50e7a4d..506edb8 100644 --- a/plugins/lua-agent-builder/README.md +++ b/plugins/lua-agent-builder/README.md @@ -10,7 +10,7 @@ Verified against **lua-cli 3.33.0** (September 2026): every command shape, SDK t /plugin marketplace add lua-ai-global/claude-code-lua-plugin /plugin install lua-agent-builder@claude-code-lua-plugin /reload-plugins -/lua-doctor # Node, npm, lua-cli ≥ 3.37.0, auth, permission rules +/lua-doctor # Node, npm, lua-cli ≥ 3.38.0, auth, permission rules ``` New login runs in your own terminal (`lua auth configure`) — the plugin never handles your email, one-time code or credential. `/lua-auth` guides it. @@ -21,11 +21,11 @@ New login runs in your own terminal (`lua auth configure`) — the plugin never plugins/lua-agent-builder/ ├── .claude-plugin/plugin.json # plugin manifest ├── .mcp.json # lua-platform (local stdio) + lua-docs (https://docs.heylua.ai/mcp) -├── commands/ # 20 slash commands +├── commands/ # 21 slash commands ├── agents/ # 5 subagents (architect, skill-builder, debug, deploy-pilot, qa) ├── hooks/ # 10 Node ESM hooks + hooks.json ├── lib/ -│ ├── knowledge/ # primitives, workflows, cli-reference, integrations, decision-trees +│ ├── knowledge/ # primitives, workflows, cli-reference, integrations, decision-trees, log-drains │ ├── permissions-template.json# allow/ask/deny rules /lua-doctor merges into .claude/settings.json │ ├── tokenizer.mjs # production-verb classifier for the deploy gate │ └── credentials.mjs, hook-runtime.mjs, lua-cli.mjs, platform.mjs @@ -49,7 +49,8 @@ plugins/lua-agent-builder/ | `/lua-test [type]` | `lua test --ci skill\|webhook\|job\|preprocessor\|postprocessor\|workflow`; failures go to the debug subagent | | `/lua-workflow ` | Offline workflow runs with scripted approvals/signals; list/status/watch; start/approve/signal/resume/cancel with one confirmation | | `/lua-chat` | One-shot `lua chat --ci -e -m … -t` on an isolated thread | -| `/lua-logs` | `lua logs --ci --json` with the real `--type` list | +| `/lua-logs` | `lua logs --ci --json` with the real 18-source `--type` list and ⏳ the 3.38.0 read window (`--since` / `--until` / `--environment` / `--follow`) | +| `/lua-drains` | ⏳ 3.38.0 log drains: `lua drains list\|status\|deliveries\|create\|update\|delete\|test\|verify\|pause\|resume\|rotate-secret`; reads run at once, the seven config verbs confirm once, the signing secret is never stored | | `/lua-env` | `lua env --list \| -k KEY -v VALUE \| -k KEY --delete`; the Bash prompt is the confirmation, values never echoed | | `/lua-integrations` | `lua integrations available\|list\|info\|webhooks …\|mcp …`; read-only verbs run at once, mutations confirm once, OAuth connects go to your terminal | | `/lua-sync` | Drift report from `lua status --json` + `lua sync --check`; `--pull` / `--push` | @@ -63,6 +64,7 @@ plugins/lua-agent-builder/ - **Production gate** — every verb that changes what runs in production is blocked by the `confirm-deploy` hook (on every Bash call) unless it carries the `LUA_DEPLOY_CONFIRMED=1` prefix, which only the deploy flow emits after your single confirmation: `lua deploy`, `lua skills|webhooks|jobs|preprocessors|postprocessors deploy`, `lua persona production deploy`, `lua workflows deploy|activate`, `lua version promote`, `lua mcp activate`, `lua marketplace template publish|apply` — in every spelling lua-cli accepts (its `publish`/`on`/`enable`/`submit`/`rollout`/`prod` aliases and the `heylua`/`lua-ai` binaries). A hook block wins over any allow rule. The permission template allows the literal prefixed forms and carries no deny/ask rule for the bare verbs, because Claude Code evaluates deny/ask past a leading env assignment and such a rule would block the confirmed form too. `lib/tokenizer.mjs` is the one classifier; `test/lib/permissions-mirror.test.mjs` fails if the layers drift. - **`--auto-deploy`** is denied and blocked unconditionally. +- **Log drains** (⏳ lua-cli 3.38.0) are ORGANIZATION configuration, not a deploy: `lua drains list|status|deliveries|test` are allowed, and `create|update|delete|verify|pause|resume|rotate-secret` sit in the `ask` tier — that prompt is `/lua-drains`'s single confirmation. They are deliberately **not** in `lib/tokenizer.mjs`: none of them changes what runs in production, and `LUA_DEPLOY_CONFIRMED=1` would be the wrong sentence for a log-shipping change. No secret ever reaches a command line — a header value is prompted or read from an environment variable (`--header-from-env NAME=ENV_VAR`), and the HMAC signing secret is minted server-side and printed exactly once. - **Credential isolation** — `lua auth configure|key|logout` are denied for the model; login happens in your terminal. - **Single permission per slash** — each slash asks at most one question (`x-lua-multi-step: true` marks the diagnostic exceptions). diff --git a/plugins/lua-agent-builder/agents/lua-architect.md b/plugins/lua-agent-builder/agents/lua-architect.md index 74bccfb..fa45991 100644 --- a/plugins/lua-agent-builder/agents/lua-architect.md +++ b/plugins/lua-agent-builder/agents/lua-architect.md @@ -13,13 +13,14 @@ lua-cli is a TypeScript SDK/CLI. It has nothing to do with the Lua programming l ## Always start by reading these (cached; no need to re-read every turn) -The plugin ships a knowledge base verified against lua-cli 3.33.0 source. Read all five with the `Read` tool: +The plugin ships a knowledge base verified against lua-cli source (3.33.0 base; the drain material against 3.38.0). Read all six with the `Read` tool: - `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/primitives.md` — every SDK primitive and runtime API, exact shapes, gotchas, the decision matrix - `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/workflows.md` — the workflow builder, steps, approvals/signals, Job tier, script form, CLI verbs, test recipe - `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/integrations.md` — Unified.to connectors, auto-provisioned MCPs, event subscriptions, channels, `Integrations.passthrough` - `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/cli-reference.md` — commands, exit codes, push/deploy matrix, agent versions, marketplace templates, docs URL map - `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/decision-trees.md` — task → primitive routing +- `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/log-drains.md` — ⏳ 3.38.0: shipping the org's logs to the customer's own stack (`lua drains`), the states, the verification handshake, the http/otlp/datadog/betterstack presets, quotas, the scrubber, `logs:read` vs `logs:manage`. Read it when the plan involves observability, SIEM, alerting or an on-call rotation — a drain is the answer to “we want these logs in Datadog”, and it is org configuration, never agent code When a question goes beyond the knowledge files, use the docs MCP: `mcp__plugin_lua-agent-builder_lua-docs__search_lua_cli` for a question, `mcp__plugin_lua-agent-builder_lua-docs__query_docs_filesystem_lua_cli` to read a page (`head -200 /workflows/authoring.mdx`, `rg -n "approval" /`). `WebFetch https://docs.heylua.ai/` is the fallback (paths are listed in cli-reference.md §6). The knowledge files win over the docs where they disagree — they were checked against the CLI source. diff --git a/plugins/lua-agent-builder/agents/lua-debug.md b/plugins/lua-agent-builder/agents/lua-debug.md index 3b8d3aa..60fae9d 100644 --- a/plugins/lua-agent-builder/agents/lua-debug.md +++ b/plugins/lua-agent-builder/agents/lua-debug.md @@ -17,6 +17,12 @@ A `lua compile --ci`, `lua test --ci` or `lua push` invocation failed, or a work 2. **Re-run with detail**: `lua compile --ci --debug --verbose` (plugin-detection trace), or `lua test --ci --name --input '' --json` with `LUA_DEBUG=1` for a stack. +2b. **Read the platform's logs by WINDOW, never by page.** ⏳ lua-cli 3.38.0 or later: `lua logs` takes `--since` / `--until` (an ISO 8601 instant, or a relative `15m` / `2h` / `7d` that the **server's** clock resolves — so a skewed laptop cannot shift the window), `--environment production|sandbox`, and `--follow` (polls ~2 s, stops within ~3 s of Ctrl-C, refuses `--page`). Do **not** page with `--page` and stitch a window together by timestamp: that was the old workaround and it silently drops rows sharing a millisecond. + + - **Watch a promote**: note the instant the promote returned, then `lua logs --ci --type all --since --follow --json` and select `subType === 'error' || subType === 'warn'` as the envelopes arrive (one `{ logs, nextCursor, pagination }` per poll that produced rows). ⚠ `lua logs` has **no** severity flag — `--min-severity` is a *log-drain* selector, not a `lua logs` option; severity is filtered client-side on `subType`. A bounded `--since --until ` read returns on its own: prefer it whenever nobody is watching the terminal, because a follow ends only at Ctrl-C. + - **Scope to one environment**: `--environment sandbox` while local code is under test, `--environment production` after a promote. `metadata.environment` is optional and additive — a row written before it shipped carries none and reads as `production`, so never treat its absence as "sandbox". + - All **18** sources are valid `--type` values on 3.38.0 or later (`trigger`, `model-resolver`, `workflow-step`, `workflow-script` and `workflow` included); on older CLIs those five were exit 2 and only the `tail_logs` MCP tool reached them. + 3. **Match the canonical patterns**: - **Primitive not detected / "No skills found"** → it is not referenced from the `LuaAgent` arrays in `src/index.ts` (the compiler only follows those), or the class/`new LuaX` pattern isn't recognised, or (workflows) the `createWorkflow` config is not an inline object literal / the chain doesn't end in `.commit()`. - **Bundling fails / cannot resolve** → imports must be from `'lua-cli'` (no `lua-cli/skill`), path aliases must match `tsconfig.json` `paths`, and `defineTool`/`defineSkill` don't exist — use `class implements LuaTool` / `new LuaSkill`. @@ -33,6 +39,8 @@ A `lua compile --ci`, `lua test --ci` or `lua push` invocation failed, or a work - **Run parked on its budget, or a cost nobody expected** → `lua workflows status `: the `Budget:` line names the unit the ORGANIZATION counts in (`budget.unit` / `usage.engine` under `--json`) — **credits** on a legacy plan, **actions** on a seat plan — and the sentence after it says what that unit buys. Never quote "credits" to a seat organization, and never derive the unit from a number. `lua workflows raise-budget --credits ` is the raise on both plans (the flag name does not change); `` is in the run's own unit. A surprising figure on a Job step is almost always the **per-reply** rule: a Job attempt is charged once per model reply (dozens per coding turn) at `price band × the model's multiplier`, so look at which model the step ran on — ⏳ lua-cli 3.37.0 or later prints `⚙ : model · × · chosen by ` under `status --steps` and `job model → …` in `logs`, and `chosen by platform default` / `org default` means the step named no `model` of its own (fix: pin it). ⏳ `finished past the cap` on the `Budget:` line (`budget.exceeded`) means the run crossed its cap and completed anyway — not a park, nothing to raise. ⏳ `Tokens:` and the `Uncached`/`Cached`/`Output` columns are signals, **not** charges: never add them together (a cache read is a subset of the input figure on OpenAI-compatible providers) and never present them as what the run was billed. Any of these lines missing means the server projected nothing for them — an unmetered run or an older server — not a defect. - **Workflow run failed (exit 4)** → `lua workflows status --steps --json`: the failing step's `error.code`; `binding_unresolved`/`input_schema_invalid`/`credentials_unresolved` park pre-dispatch. Re-test offline with `lua test --ci workflow --name --input '' --from-run `: the driver fetches that run and each planned step from the API, seeds every `completed` step whose ancestors are all seeded (latest attempt wins) with its output and effects, and re-drives from the first pending step — so only the failing step and what follows re-execute (`[from-run] N step(s) seeded from `). Exit `3` = the run was not found; exit `2` `[from-run] graph differs (N steps changed) — pass --force to seed anyway` = your compiled `graphHash` no longer matches the run's (you changed the graph) — add `--force` to seed the steps that still line up. + - **"Our logs stopped arriving in Datadog / our receiver"** (⏳ lua-cli 3.38.0 or later; facts in `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/log-drains.md`) → this is a **log drain**, an organization resource, and nothing about the agent's code. Read `lua drains status --json`: `state` (`pending_verification` buffers and never delivers — the endpoint changed and nobody re-verified; `paused` with `pauseReason: 'quota'` is the 150% rung of the quota ladder; `paused` with `'auto'` is 24 h without a success), `health.lastStatusCode` / `health.lastError`, `health.backlog`, and `quota.degradation` (at 100% `debug` is dropped, at 125% `info` too — `warn` and `error` are never dropped before the drain pauses). Then `lua drains deliveries --limit 20 --json` for the per-attempt `errorClass` (`network timeout dns ssrf redirect http_4xx http_429 http_5xx rejected partial encode`). ⚠ `lua drains status` exits **2** both for a usage error *and* for "some drain is `failing`" — decide from `drains[].state`, never from the exit code. Exit 10 is the missing **`logs:manage`** scope, which is sensitive (`logs:*` does not satisfy it) and is re-minted by the user in their own terminal. Exit 3 on every verb means the feature is not rolled out to that organization. Fixing a drain is `/lua-drains`, not an edit here: **never** print a signing secret, and never put a header value on a command line (`--header-from-env =` is the CI form). + 4. **Unknown message?** Search the docs with `mcp__plugin_lua-agent-builder_lua-docs__search_lua_cli ""` or `mcp__plugin_lua-agent-builder_lua-docs__query_docs_filesystem_lua_cli "rg -n '' /"`; `WebFetch https://docs.heylua.ai/cli/troubleshooting` is the fallback. Never guess at internals. 5. **Propose the smallest fix**, apply it with `Edit`, re-run the failing command, and report: cause → change → evidence. @@ -49,6 +57,8 @@ A `lua compile --ci`, `lua test --ci` or `lua push` invocation failed, or a work - `lua test --ci [args]` - `lua status --json --ci` - `lua logs --ci [args]` +- `lua drains status *` +- `lua drains deliveries *` - `lua workflows status * --steps --json` - `lua workflows logs *` - `git log --oneline -20` diff --git a/plugins/lua-agent-builder/agents/lua-qa.md b/plugins/lua-agent-builder/agents/lua-qa.md index e2c0e95..abfdd68 100644 --- a/plugins/lua-agent-builder/agents/lua-qa.md +++ b/plugins/lua-agent-builder/agents/lua-qa.md @@ -48,7 +48,11 @@ Exit `0` completed · `2` flag/schema problem (report as a finding: the input sc ## Step 3 — log scan -`mcp__plugin_lua-agent-builder_lua-platform__tail_logs` with `{ agentId, type: 'all', limit: 100 }` → `{ logs, pagination }`. Select entries whose `timestamp` falls in the test window with `subType === 'error'` or `subType === 'warn'` (the field is `subType`; there is no `level`). `metadata.logSource` (`skill | job | webhook | trigger | preprocessor | postprocessor | agent_error | runtime | mcp | workflow-step …`) and `metadata.primitiveName` tell you which primitive produced it; a tool that threw is `logSource: 'skill'`, and `agent_error` comes only from the chat pipeline. `metadata.channel === 'dev'` marks the turns this suite sent through `lua chat` (in either environment — there is no environment field on a log entry, so scope by timestamp and thread, never by channel); `'pop'` is website-widget traffic. Fallback without MCP: `lua logs --ci --type all --limit 100 --json`. +**Record an ISO 8601 instant before the first chat turn (`T0`) and use it as the window.** ⏳ lua-cli 3.38.0 or later: `lua logs --ci --type all --since --environment --limit 100 --json` asks the route for exactly the suite's window and exactly the environment under test. Do **not** page with `--page` and filter by timestamp afterwards — that reconstructs the window badly and drops rows sharing a millisecond. A relative bound (`--since 15m`) is resolved by the **server's** clock, so prefer it over one computed from a possibly skewed local clock; a closed `--since --until ` read returns on its own, and `--follow` (which ends only at Ctrl-C) has no place in an unattended suite. + +Select `subType === 'error'` or `subType === 'warn'` (the field is `subType`). `metadata.logSource` (`skill | job | webhook | trigger | preprocessor | postprocessor | agent_error | runtime | mcp | workflow-step …` — all 18 sources are valid `--type` values on 3.38.0 or later) and `metadata.primitiveName` tell you which primitive produced it; a tool that threw is `logSource: 'skill'`, and `agent_error` comes only from the chat pipeline. ⏳ `metadata.environment` (`production` | `sandbox`) is what `--environment` filters on, but it is optional and additive: a row written before it shipped carries none and reads as `production`, so still bound the scan with `--since` and by thread. `metadata.channel === 'dev'` marks the turns this suite sent through `lua chat` — in **either** environment, so it is CLI-traffic evidence, never an environment marker; `'pop'` is website-widget traffic. + +The `mcp__plugin_lua-agent-builder_lua-platform__tail_logs` tool (`{ agentId, type: 'all', limit: 100 }` → `{ logs, pagination }`) is the fallback when the installed CLI is older than 3.38.0 or unavailable; it has no window parameter, so filter its rows on `timestamp` yourself. ## Step 4 — write the triage report diff --git a/plugins/lua-agent-builder/commands/lua-drains.md b/plugins/lua-agent-builder/commands/lua-drains.md new file mode 100644 index 0000000..d2bf19f --- /dev/null +++ b/plugins/lua-agent-builder/commands/lua-drains.md @@ -0,0 +1,64 @@ +--- +description: Manage the organization's log drains — list/status/deliveries, create/update/delete, test/verify, pause/resume and rotate-secret. Wraps `lua drains [id]`; the signing secret is printed once and never stored, header values never reach a command line. +--- + +You are `/lua-drains`. The user typed `/lua-drains $ARGUMENTS` (`[list | status [id] | deliveries | create | update | delete | test | verify | pause | resume | rotate-secret ]`). Facts: `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/log-drains.md` (all of it — states, selectors, the verification handshake, presets, quotas, the scrubber, the scopes) and `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/cli-reference.md` §4 (the `lua drains` and `lua logs` rows). + +A drain is an **organization** resource, so this slash works outside a project directory. ⏳ **lua-cli 3.38.0 or later** — below it the command does not exist and exits 1. + +## Step 0 — auth preflight (auto-resolve via Skill tool) + +Run `Bash(lua models list --json --ci)` — a 1–2 s authenticated call (no project needed). Exit 0 or 10 = authenticated (10 = a typed key scoped away from the model catalog; fine). Exit 9 = not authenticated: use the **Skill tool** with `skill: "lua-auth"`, then re-probe; if still 9, abort with the CLI error verbatim. Exit 11 = the Lua API is unreachable: abort with that line (do not start a login). Do not use `lua agents` as the probe — it walks every organisation and can take 20 s or more on large accounts. + +## Step 1 — collect what is missing (single permission per §3.7) + +Parse `$ARGUMENTS`. Whatever is still missing for the chosen verb, AskUserQuestion **once**, with every question in the same call. Never ask twice, and never ask for anything the verb does not take. + +- "Which verb?" (options: `list`, `status`, `deliveries`, `create`, `update`, `delete`, `test`, `verify`, `pause`, `resume`, `rotate-secret` — exactly the eleven `drains.action` canonical values; anything else is exit 2) +- "Which drain?" (free-text `drn_…`; required by every verb except `list`, `status` and `create`. If the user does not know it, run `list` instead and stop — that is a different, cheaper answer, not a second question) +- `create` / `update` only, in the same call: "Destination type?" (`http`, `otlp`, `datadog`, `betterstack` — `create` only; a drain cannot change type in place, and `--type` on an update is rejected), "Endpoint or site?" (the full HTTPS URL for `http`/`otlp`/`betterstack` — for `otlp` the **full `/v1/logs` URL**, used verbatim; for `datadog` one of `datadoghq.com`, `datadoghq.eu`, `us3.datadoghq.com`, `us5.datadoghq.com`, `ap1.datadoghq.com`, `ddog-gov.com`), "Name?" (1–64 chars), "Which sources / severity / agents / environments?" (defaults: the recommended source set, `info`, the project's agent inside a project and every agent outside one, `production`) +- **Never ask for a header VALUE, an API key or a token.** Ask only for the header NAME and the environment variable that already holds the value (see Step 2). + +## Step 2 — run (the Bash permission prompt IS the confirmation for a mutation) + +`lua drains list|status|deliveries|test` sit in the `allow` tier of the plugin's permission template (installed by `/lua-doctor`) and run at once. The seven verbs that change the org's configuration — `create`, `update`, `delete`, `verify`, `pause`, `resume`, `rotate-secret` — sit in the `ask` tier, so Claude Code shows the user the exact command and waits. Do **not** add an AskUserQuestion on top of that prompt — it would prompt twice. (If the project has no `.claude/settings.json` rules yet, say so and point at `/lua-doctor` rather than running unprompted.) + +Read verbs: + +- list → `Bash(lua drains list --json)` (bare `lua drains` is the same command) +- status → `Bash(lua drains status [] --json)` +- deliveries → `Bash(lua drains deliveries --limit [--kind batch|test|verify|heartbeat|dropped|truncated] --json)` +- test → `Bash(lua drains test --json)` — sends one `lua.drain.test` record down the real delivery path and waits up to 30 s + +Mutations: + +- create → `Bash(lua drains create --ci --json --name '' --type [--endpoint '' | --site ] [--format json|ndjson] [--header-from-env =] [--sources ] [--min-severity ] [--agents all|] [--environments ] [--sampling debug=])` +- update → `Bash(lua drains update --ci --json )` — PATCH semantics: an omitted flag means *keep*, never *clear*. No field flags at all is exit 2 +- delete → `Bash(lua drains delete --ci --json --yes)` — say in the same turn that this purges the destination's stored credentials and drops its queued records +- verify → `Bash(lua drains verify --json)` (waits up to 60 s) · pause → `Bash(lua drains pause --reason '' --json)` · resume → `Bash(lua drains resume --json)` +- rotate-secret → `Bash(lua drains rotate-secret --json)`, or `… --finalize --json` to close an open window + +Add `--org ` when the user named an organization, or when the CLI exits 2 saying the credential reaches several (the refusal lists the ids). Inside a project the org comes from `lua.skill.yaml`; outside one, from the credential when it reaches exactly one. + +**Secret handling — the rules this slash exists to keep:** + +- **Never put a header value on a command line.** `--header ` prompts for the value hidden and therefore cannot work under `--ci`; the only CI-safe form is `--header-from-env =`, which reads it from an environment variable the user exported themselves. If the variable is not set, the CLI exits 2 naming the **variable** — relay that, and ask the user to export it in their own shell. Do not read the variable, echo it, or write it anywhere. +- **The signing secret is minted by the server and printed exactly once** (at `create`, and at a `rotate-secret` without `--finalize`). It is never an input. Do not copy it into a file, an env var, a commit message, a summary or your own reply — tell the user it is on their screen and that a lost secret is rotated, never recovered. +- **For a vendor preset, stop at the secret.** `datadog` wants `--header DD-API-KEY`, `betterstack` wants `--header Authorization` entered as `Bearer `. Ask which environment variable holds it and use `--header-from-env`; if there is none, print the exact `lua drains create …` line for the user to run in their own terminal (where the hidden prompt works) and stop there. +- `--include-content` turns on `user_message` / `agent_response`, which carry what end users said. Only reach for it when the user asked for it in those words, and **never add it on your own initiative**. Run it **without** `--yes` first: under `--ci` the CLI prints the warning and the acknowledgement and then exits non-zero having created nothing, which is exactly the shape this needs — quote both paragraphs verbatim in your reply, say that re-running with `--yes` *is* the acknowledgement, and stop. The user's approval of that second `Bash` call (the `ask` tier) is the acknowledgement; no AskUserQuestion on top. Scrubbing is not a substitute for not logging secrets: it only catches shapes it recognises. + +## Step 3 — present + +`--json` goes to **stdout**; the verification instructions and the content acknowledgement go to **stderr** (`emitAside`) so a pipe stays machine-readable. Read both. + +- **list** → `{ drains: [...] }`. One line per drain: name, `type`, `state`, environments, agent count, `health.backlog`, `health.lastSuccessAt`. +- **status** → `{ drains: [{ id, name, state, health, quota }] }`. Lead with anything not `healthy`. `health`: `backlog`, `backlogBytes`, `deliveredCount24h`, `droppedCount24h`, `rejectedCount24h`, `p50LatencyMs`, `scrubHits24h`, `lastSuccessAt`, `lastFailureAt`, `lastStatusCode`, `lastError`, `pauseReason`. `quota`: `usedEvents`/`eventsPerDay`, `usedBytes`/`bytesPerDay`, `degradation`, `resetsAt` — and the ladder is 80% notify, 100% drop `debug`, 125% drop `info` too, 150% pause with reason `quota`; `warn` and `error` are never dropped before that. +- ⚠ **Exit 2 is ambiguous on `status`**: it is both "usage error" and "at least one drain is `failing`". Decide from `drains[].state` in the JSON, never from the exit code alone. +- **deliveries** → per-attempt rows with `kind`, `attempt`, `ok`, `statusCode`, `latencyMs`, `recordCount`, `bytes`, `errorClass` (`network timeout dns ssrf redirect http_4xx http_429 http_5xx rejected partial encode`). No request body is stored anywhere — do not offer to show one. +- **test / verify** are asynchronous: the 202 is already enqueued when the wait starts. `pending: true` (exit 1) means *not yet*, not *failed* — point at `lua drains deliveries --kind test` or `lua drains status `. A `verify` that answers `token_not_echoed` means the receiver replied 2xx but did not repeat the `X-Lua-Verify` header; that is the commonest failure and the phrase to search the docs for. Only an `http` drain verifies by echo — `otlp`, `datadog` and `betterstack` accept any 2xx test post. Verification is capped at 5 attempts per drain per hour. +- **create** → `{ drain, secret }`. Report the drain id and `state: pending_verification` (it buffers, it does not deliver, until verified), then the next step: `lua drains verify `. Say the secret was printed once and is not recoverable; do **not** repeat it. +- **update** → `{ drain }`. If the endpoint changed, the state is back to `pending_verification` and records buffer until a fresh verify. `--no-include-content` keeps the sources: the rows keep arriving with the body withheld and `lua.content.withheld` set. +- **pause** → buffering continues, nothing is delivered. **resume** → lands in `degraded`, never straight in `healthy`. **rotate-secret** → a 24-hour window where every batch carries two `v1=` signatures; close it with `--finalize` once the receiver has the new value. +- Deliveries are at-least-once and unordered — tell a user building a receiver to dedup on `records[].id`, never on `X-Lua-Batch-Id`, and to keep ids for the 6-hour retry horizon. A `Retry-After` from the destination is honoured exactly, clamped at one hour; the drain **routes** answer `429 DRAIN_RATE_LIMITED` with a bare `Retry-After` in seconds and no `X-RateLimit-*` headers, so back off for exactly what it says. +- Exit codes: `0` ok · `1` a `test` not delivered / a `verify` that did not reach `ok` · `2` usage, **or a failing drain on `status`** · `3` not found — including an organization the feature is not rolled out to yet, where every verb answers 404 · `10` the credential lacks `logs:manage` (`✖ forbidden: Missing scope: logs:manage. …`; it is a **sensitive** scope, so `logs:*` does not satisfy it — the user re-mints a key in their own terminal, never here) · `11` the API is unreachable. +- Reading the records themselves is `logs:read`, a different scope, and `/lua-logs` — a drain is a copy, not a move. diff --git a/plugins/lua-agent-builder/commands/lua-logs.md b/plugins/lua-agent-builder/commands/lua-logs.md index 7a1e358..307a160 100644 --- a/plugins/lua-agent-builder/commands/lua-logs.md +++ b/plugins/lua-agent-builder/commands/lua-logs.md @@ -1,5 +1,5 @@ --- -description: View agent logs (skills, jobs, webhooks, triggers, processors, MCP, runtime errors, voice calls) as structured JSON. Wraps `lua logs --ci --json`. +description: View agent logs (skills, jobs, webhooks, triggers, processors, MCP, workflows, runtime errors, voice calls) as structured JSON, over a time window and optionally followed live. Wraps `lua logs --ci --json`. --- You are `/lua-logs`. The user wants to view recent logs. @@ -10,16 +10,36 @@ Run `Bash(lua models list --json --ci)` — a 1–2 s authenticated call (no pro ## Step 1 — collect the filter (single permission per §3.7) -If `$ARGUMENTS` already contains a type, use it. Otherwise AskUserQuestion **once**: +If `$ARGUMENTS` already contains a type or a window, use it. Otherwise AskUserQuestion **once**, with every question in the same call: -- "Log type?" (options: `all`, `skill`, `job`, `webhook`, `preprocessor`, `postprocessor`, `user_message`, `agent_response`, `agent_error`, `mcp`, `runtime`, `rag`, `device`, `device-trigger`, `calls`) — exactly the values `lua logs --type` accepts in lua-cli 3.33.0; anything else is exit 2, including the help text's `mastra` and the real platform sources `trigger`, `model-resolver`, `workflow-step`, `workflow-script`, `workflow`, which are reachable only via the `tail_logs` MCP tool +- "Log type?" (options: `all`, `calls`, or any of the **18** log sources — `skill`, `job`, `webhook`, `trigger`, `preprocessor`, `postprocessor`, `user_message`, `agent_response`, `agent_error`, `runtime`, `mcp`, `rag`, `device`, `device-trigger`, `model-resolver`, `workflow-step`, `workflow-script`, `workflow`). ⏳ **lua-cli 3.38.0 or later**: `logs.type` is derived from the canonical `AGENT_LOG_SOURCES` constant, so all eighteen are reachable. On 3.37.0 and older the alias table was hand-listed and the five platform sources (`trigger`, `model-resolver`, `workflow-step`, `workflow-script`, `workflow`) were exit 2 there — only the `tail_logs` MCP tool could read them. The help text's `mastra` is rejected in every version (exit 2). `all` = no source filter; `calls` = voice-call records, a different dataset +- "Time window? (optional)" (free-text: an ISO 8601 instant like `2026-09-21T09:00:00Z`, or a relative window `15m` / `2h` / `7d` — anything else is exit 2, and a bare number is deliberately refused because `7` is an obvious typo for `7d`) +- "Environment? (optional)" (options: `production`, `sandbox`, or leave empty for both) +- "Follow live? (optional)" (yes/no — polls every ~2 s until Ctrl-C) - "Filter to a specific primitive name? (optional; requires a type)" (free-text) -- "How many entries? (default 50, max 100)" (free-text) +- "How many entries? (default 20)" (free-text; the CLI does not cap it locally — the route decides the page size it will serve) ## Step 2 — run -`Bash(lua logs --ci --type [--name ] --limit --json)`. For `calls` add `--direction inbound|outbound` / `--status ` if the user asked. `--user-id ` filters one user's logs; `--agent-id ` reads another agent you administer. +`Bash(lua logs --ci --type [--name ] [--since ] [--until ] [--environment production|sandbox] --limit --json)`. + +⏳ **lua-cli 3.38.0 or later** for `--since` / `--until` / `--environment` / `--follow`; below it commander exits 1 on the unknown option. Use them instead of paging: `--page` walks backwards through history and a `--limit`-sized sweep reconstructs a window badly, while `--since` asks the route for exactly the window you mean. + +- `--since` is **inclusive**; both bounds take an ISO 8601 instant or a relative window (`15m`, `2h`, `7d`). A **relative** bound is resolved by the **server's** clock, so a skewed laptop cannot shift the window — prefer `--since 15m` over computing a timestamp yourself. +- `--environment` filters on `metadata.environment`. A row written before that field shipped has none and reads as `production`. +- `--follow` polls (there is no streaming endpoint) every ~2 s with jitter, stops within ~3 s of Ctrl-C, and **refuses `--page`** (exit 2). It advances on the newest timestamp it has printed and de-duplicates by row id, so the boundary row comes back on every poll and is dropped — never assume `since + 1 ms`. An `--until` already in the past closes the window after the first sweep instead of hanging. In a long-running or non-interactive context prefer a bounded `--since … --until …` read over a follow that never returns. +- For `calls` add `--direction inbound|outbound` / `--status ` if the user asked. ⚠ `--type calls` is a different dataset and **ignores** `--since` / `--until` / `--environment` / `--follow`. +- `--user-id ` filters one user's logs; `--agent-id ` reads another agent you administer. +- Reading logs needs the `logs:read` scope (⏳ an older key's `knowledge:read` / `automations:read` / `analytics:read` is still accepted for two releases and answers `Deprecation: true` with a `Sunset` header — relay that warning rather than swallowing it). ## Step 3 — present -`--json` prints `{ logs: LogEntry[], pagination }` (for `calls`: `{ calls[], total, page, totalPages }`). Each `LogEntry` has `timestamp`, `type` (`log`|`metric`), **`subType`** (`error` | `warn` | `info` | `debug` | `start` | `complete` — there is no `level` field), `message`, `duration?`, and `metadata` (`logSource`, `primitiveName`, `primitiveId`, `toolId`, `toolName`, `userId`, `agentId`, `runId`, `channel`). There is **no `environment` field** — sandbox vs production is not recorded on a log entry. `metadata.channel === 'dev'` marks turns sent by `lua chat` (CLI traffic, in either environment — not a sandbox marker); `'pop'` is the website widget, `'web'` other web clients. A tool that throws lands under `logSource: 'skill'` with `subType: 'error'`; `agent_error` entries come only from the chat pipeline itself. Group by `subType`, errors first, then by `metadata.logSource`/`primitiveName`. Offer to drill into one entry; don't dump raw JSON unless asked. `runId` on an entry is the conversation/run to correlate with `lua workflows status ` when `logSource` is a workflow source. A run's own timeline is `lua workflows logs ` (`/lua-workflow logs`), which ⏳ on lua-cli 3.36.0 or later also shows the model events — `step.model_resolved` (`model resolved → ()`) and `step.model_policy_warning` (`model policy warning: …`, e.g. a pinned model the organization has excluded, warn-only today) — and ⏳ on 3.37.0 or later the Job-model and budget events: `step.job_model_resolved` (`job model → × ()`, with `— uncalibrated model, fallback rate` when no calibrated rate could be quoted) and `run.budget_parked` / `run.budget_raised` / `run.budget_exceeded`, which below 3.37.0 `logs` did not print at all. The unit inside a budget line is whatever the gate chose (`actions` on a seat plan, `credits` on legacy) — repeat it verbatim. +`--json` prints `{ logs: LogEntry[], pagination }` — and, with `--follow`, one `{ logs, nextCursor, pagination }` envelope **per poll that produced rows**, newest-first inside each envelope (the plain renderer prints oldest-first instead, so a tail reads downwards). For `calls`: `{ calls[], total, page, totalPages }`. + +Each `LogEntry` has `timestamp`, `type` (`log`|`metric`), **`subType`** (`error` | `warn` | `info` | `debug` | `start` | `complete` — the field is `subType`), `message`, `duration?`, and `metadata`: `logSource`, `primitiveName`, `primitiveId`, `toolId`, `toolName`, `userId`, `agentId`, `runId`, `channel`, `model?` (the `provider/model` code that actually served the turn — on `agent_response` / `agent_error` rows only), and ⏳ the platform-side additions `orgId`, **`environment`** (`production` | `sandbox`), `agentVersion`, `executionId`, `executionSeq` (0-based order within one execution), `traceparent`, plus `truncated: true` / `droppedLines` on the synthetic row an execution emits when the runner dropped lines. + +⚠ `environment` is **optional and additive**: it is absent on every row written before it shipped, and such a row reads as `production` everywhere (the drain matcher and `--environment` alike). Never infer sandbox-vs-production from `metadata.channel`: `channel === 'dev'` marks turns sent by `lua chat` (CLI traffic, in *either* environment); `'pop'` is the website widget, `'web'` other web clients. + +A tool that throws lands under `logSource: 'skill'` with `subType: 'error'`; `agent_error` entries come only from the chat pipeline itself. Group by `subType`, errors first, then by `metadata.logSource`/`primitiveName`. Offer to drill into one entry; don't dump raw JSON unless asked. `runId` on an entry is the conversation/run to correlate with `lua workflows status ` when `logSource` is a workflow source. A run's own timeline is `lua workflows logs ` (`/lua-workflow logs`), which ⏳ on lua-cli 3.36.0 or later also shows the model events — `step.model_resolved` (`model resolved → ()`) and `step.model_policy_warning` (`model policy warning: …`, e.g. a pinned model the organization has excluded, warn-only today) — and ⏳ on 3.37.0 or later the Job-model and budget events: `step.job_model_resolved` (`job model → × ()`, with `— uncalibrated model, fallback rate` when no calibrated rate could be quoted) and `run.budget_parked` / `run.budget_raised` / `run.budget_exceeded`, which below 3.37.0 `logs` did not print at all. The unit inside a budget line is whatever the gate chose (`actions` on a seat plan, `credits` on legacy) — repeat it verbatim. + +Shipping these records to the customer's own stack (Datadog, OTLP, a receiver of their own) is a **log drain** — `/lua-drains`, and `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/log-drains.md`. A drain is a copy: everything it sends stays readable here. diff --git a/plugins/lua-agent-builder/hooks/check-lua-version.mjs b/plugins/lua-agent-builder/hooks/check-lua-version.mjs index e5180b6..aa740ec 100644 --- a/plugins/lua-agent-builder/hooks/check-lua-version.mjs +++ b/plugins/lua-agent-builder/hooks/check-lua-version.mjs @@ -7,26 +7,32 @@ import { runHook, checkNodeVersion, isMainScript } from '../lib/hook-runtime.mjs import { spawnLua } from '../lib/lua-cli.mjs'; // Pinned minimum lua-cli version. The plugin's commands, agents and knowledge -// files describe the lua-cli 3.37.0 surface: the 3.33.0 base (workflows, +// files describe the lua-cli 3.38.0 surface: the 3.33.0 base (workflows, // triggers, devices, voice, agent versions, `lua auth sessions`, the typed // exit-code classes, `lua test preprocessor|postprocessor|workflow`, `lua push // --no-include-source`), the 3.36.0 workflow verbs (`policy models|autonomy`, // `clear-gate`, `recompose`, `models list --workflows`, `push --apply-effort`) -// that answer exit 2 on anything older, and the 3.37.0 Job-billing read-outs +// that answer exit 2 on anything older, the 3.37.0 Job-billing read-outs // (the `Tokens:` line and the `Uncached`/`Cached`/`Output` columns, the `⚙` // Job-model line, the engine-aware budget wording and `finished past the cap`, // the budget events in `workflows logs`, the unit-aware `raise-budget` -// confirmation and the `job-model-default` deploy advisory). 3.37.0 adds NO -// command and NO option — every shape the plugin emits already runs on 3.36.0, -// which is why this pin still only warns. -// Older CLIs still work for the core loop — this hook only WARNS, never -// blocks — but a user on an older release sees the upgrade hint once per -// session so the output the plugin's agents read matches what their CLI -// prints. The pin must be a version that is published on npm, so -// `/lua-update` can always satisfy it; the lint at -// scripts/lint-pinned-version.mjs enforces that inside the monorepo (it is -// RED by design between raising this pin and the npm publish of 3.37.0). -export const PINNED_MIN_LUA_CLI = '3.37.0'; +// confirmation and the `job-model-default` deploy advisory), and — new here — +// the 3.38.0 log-drain surface: the whole `lua drains` command (eleven verbs, +// `/lua-drains`, `lib/knowledge/log-drains.md`) and the `lua logs` read window +// `--since` / `--until` / `--environment` / `--follow` with all 18 log sources +// reachable through `--type`. +// 3.36.0 → 3.37.0 added no command and no option, so that pin only sharpened +// what the agents could READ. 3.38.0 is the first pin in a while that adds +// COMMAND SURFACE: below it `lua drains` exits 1 as an unknown command and the +// four new `lua logs` options exit 1 as unknown options. Older CLIs still work +// for the core loop — this hook only WARNS, never blocks — but a user on an +// older release sees the upgrade hint once per session, and `/lua-drains` and +// the `--since` recipes say ⏳ 3.38.0 in their own text. +// The pin must be a version that is published on npm, so `/lua-update` can +// always satisfy it; the lint at scripts/lint-pinned-version.mjs enforces that +// inside the monorepo (it is RED by design between raising this pin and the +// npm publish of 3.38.0 — the same window 1.4.0 sat in before 3.37.0 shipped). +export const PINNED_MIN_LUA_CLI = '3.38.0'; /** * Parse "X.Y.Z" into [X, Y, Z]. Returns null on garbage input. @@ -74,7 +80,7 @@ export function decide(versionResult) { const minimum = parseSemver(PINNED_MIN_LUA_CLI); if (compareSemver(installed, minimum) < 0) { return { - warn: `Lua plugin requires lua-cli ≥${PINNED_MIN_LUA_CLI} (you have ${installed.join('.')}) — run /lua-update or: npm i -g lua-cli@latest. The plugin will continue to work with degraded functionality until you do (the workflow policy, clear-gate and recompose verbs it describes do not exist below ${PINNED_MIN_LUA_CLI}).`, + warn: `Lua plugin requires lua-cli ≥${PINNED_MIN_LUA_CLI} (you have ${installed.join('.')}) — run /lua-update or: npm i -g lua-cli@latest. The plugin will continue to work with degraded functionality until you do (lua drains, and the lua logs --since/--until/--environment/--follow options, do not exist below ${PINNED_MIN_LUA_CLI}).`, }; } diff --git a/plugins/lua-agent-builder/lib/knowledge/cli-reference.md b/plugins/lua-agent-builder/lib/knowledge/cli-reference.md index 5bf562c..1cc4518 100644 --- a/plugins/lua-agent-builder/lib/knowledge/cli-reference.md +++ b/plugins/lua-agent-builder/lib/knowledge/cli-reference.md @@ -1,6 +1,6 @@ -# lua-cli command reference (3.33.0 base · 3.36.0 / 3.37.0 additions marked ⏳) +# lua-cli command reference (3.33.0 base · 3.36.0 / 3.37.0 / 3.38.0 additions marked ⏳) -Read from `src/cli/command-definitions.ts`, `src/utils/aliases.ts`, `src/index.ts`, `src/errors/cli.error.ts`, `src/services/request-credential.ts` and the per-command implementations in `src/commands/` of lua-cli 3.33.0. The installed CLI's `lua --help` is authoritative for flags; this file adds what the help text gets wrong or omits (marked ⚠). Entries marked **⏳** require **lua-cli 3.36.0 or later** (the plugin's pin is **3.37.0** since 1.4.0); they were read from lua-cli `main` / `feat/workflow-autonomy`, the refs 3.36.0 is cut from (per-step model classes, workflow autonomy, the stage-all change), and answer exit 2 below 3.36.0. Entries marked **⏳ 3.37.0** are the Job-billing read-outs: lua-cli 3.37.0 added **no command and no option** — its only `src/cli/command-definitions.ts` change is the `--credits` help string — so every command shape here is identical on 3.36.0 and 3.37.0, and only the output differs. +Read from `src/cli/command-definitions.ts`, `src/utils/aliases.ts`, `src/index.ts`, `src/errors/cli.error.ts`, `src/services/request-credential.ts` and the per-command implementations in `src/commands/` of lua-cli 3.33.0. The installed CLI's `lua --help` is authoritative for flags; this file adds what the help text gets wrong or omits (marked ⚠). Entries marked **⏳** require **lua-cli 3.36.0 or later** (the plugin's pin is **3.38.0** since 1.5.0); they were read from lua-cli `main` / `feat/workflow-autonomy`, the refs 3.36.0 is cut from (per-step model classes, workflow autonomy, the stage-all change), and answer exit 2 below 3.36.0. Entries marked **⏳ 3.37.0** are the Job-billing read-outs: lua-cli 3.37.0 added **no command and no option** — its only `src/cli/command-definitions.ts` change is the `--credits` help string — so every command shape here is identical on 3.36.0 and 3.37.0, and only the output differs. lua-cli is a TypeScript toolchain. It has nothing to do with the Lua programming language. @@ -80,7 +80,8 @@ Legend — **RO** read-only · **L** local write · **S** server write (staging/ | `lua sync --push` | | S/P: pushes persona version (served at once — see the `lua push agent` row), name, model, governance — **not** primitives (use `lua push`) | | `lua agents [--json]` | `[{ orgId, name, archived, discoveredVia, agents: [{ agentId, name, visibility, displayRoles }] }]` | RO — the plugin's auth probe | | `lua models [list] [--json]` · `lua models set --model ` · `lua models unset` · ⏳ `lua models list --workflows [--json]` | list: `{ currentModel, models: [{ provider, code, model, description, … }] }`. ⏳ `--workflows` renders the same catalog as a workflow step sees it — `Class · Model · Best for · Speed · Cost · Efforts · Notes`, cheapest class first, pin-only rows last (`src/commands/models-workflow-view.ts`; workflows.md §2); `--json` stays the server's own rows | RO / L+S | -| `lua logs --type [--name n] [--user-id u] [--agent-id a] [--limit n (20)] [--page n] [--json]` | `--type` accepts exactly `all skill job webhook preprocessor postprocessor mcp device device-trigger user_message agent_response agent_error runtime rag calls` (`src/utils/aliases.ts` `logs.type` plus the `all` / `calls` special cases in `src/commands/logs.ts`; aliases such as `skills jobs pre pp post mcps user response error kb knowledge devices` are accepted). Anything else — the help text's `mastra`, and the five real platform log sources the CLI never whitelisted (`trigger`, `model-resolver`, `workflow-step`, `workflow-script`, `workflow`) — is `CliError.usage` → exit 2; those five are reachable only through the `tail_logs` MCP tool (the server route applies no validation, it is a plain `metadata.logSource` filter). `all` = no source filter; `calls` = voice-call records, a different dataset. `--json` → `{ logs: LogEntry[], pagination }`; `LogEntry { id, timestamp, type: 'log'\|'metric', subType: 'error'\|'warn'\|'info'\|'debug'\|'start'\|'complete', message, duration?, metadata: { userId?, agentId?, runId?, logSource?, primitiveId?, primitiveName?, toolId?, toolName?, channel? } }` — **no `level` field, and no `environment` field**: nothing in a log entry records sandbox vs production. `metadata.channel` is the raw request channel: **`dev` marks every turn `lua chat` sent** (`src/api/chat.api.service.ts` posts `?channel=dev`, for `-e sandbox` and `-e production` alike) — it separates CLI traffic from end-user traffic, **not** sandbox from production; `pop` is the website widget, `web` other web clients. A tool `execute` that throws is logged as `subType: 'error'` under **`logSource: 'skill'`** (the whole tool site runs under that source, lua-core `execute-function.service.ts`); **`agent_error` is written only by the chat pipeline's** generate/stream error handlers (`chat.service.ts`), never by a tool | RO | +| `lua logs --type [--name n] [--user-id u] [--agent-id a] [--limit n (20)] [--page n] [--json]` · ⏳ 3.38.0 `[--since ] [--until ] [--environment production\|sandbox] [--follow]` | `--type` accepts `all`, `calls`, and — ⏳ from lua-cli 3.38.0 — **all 18** log sources: `skill job webhook trigger preprocessor postprocessor user_message agent_response agent_error runtime mcp rag device device-trigger model-resolver workflow-step workflow-script workflow`. PRO-1838 (A5) rewrote `src/utils/aliases.ts` `logs.type` to DERIVE its canonical list from `@lua/shared-types` `AGENT_LOG_SOURCES`; before that it was hand-listed and the five platform sources (`trigger`, `model-resolver`, `workflow-step`, `workflow-script`, `workflow`) were `CliError.usage` → exit 2, reachable only through the `tail_logs` MCP tool. The aliases (`skills jobs pre pp post mcps user response error kb knowledge devices` …) still resolve; the help text's `mastra` is still rejected. `all` = no source filter; `calls` = voice-call records, a different dataset that **ignores** `--since`/`--until`/`--environment`/`--follow`. ⏳ **The read window** (all four options exit 1 as unknown below 3.38.0): `--since` is INCLUSIVE and both bounds take an ISO 8601 instant or a relative window `15m` / `2h` / `7d` — a relative one is passed through and resolved by the **SERVER's** clock, so a skewed machine cannot shift the window, while anything else (including a bare `7`, an obvious typo for `7d`) is exit 2 (`parseWindowBound`). `--environment production\|sandbox` filters `metadata.environment`. `--follow` POLLS — there is no SSE route — every 2 s + ≤500 ms jitter (`FOLLOW_POLL_MS`, override `LUA_LOGS_FOLLOW_INTERVAL_MS`), stops within ~3 s of SIGINT, **refuses `--page`** (exit 2), backs off 1 s→30 s only while the server is UNAVAILABLE, drains at most 10 pages per poll, and advances on the newest TIMESTAMP it printed (re-sent as an inclusive lower bound, boundary rows de-duplicated by id — never `lastTs + 1ms`); an `--until` already in the past ends it after one sweep. Under `--json` a follow emits one `{ logs, nextCursor, pagination }` envelope **per poll that produced rows** (newest-first inside it; the plain renderer prints oldest-first). One-shot `--json` → `{ logs: LogEntry[], pagination }`; `LogEntry { id, timestamp, type: 'log'\|'metric', subType: 'error'\|'warn'\|'info'\|'debug'\|'start'\|'complete', message, duration?, metadata }` — the severity field is **`subType`**, and `lua logs` has **no severity option** at all (severity is filtered client-side; a `--min-severity` belongs to `lua drains`). `metadata` carries `{ userId?, agentId?, runId?, logSource?, primitiveId?, primitiveName?, toolId?, toolName?, channel?, model? }` plus ⏳ the A1 additions `{ orgId?, environment?: 'production'\|'sandbox', agentVersion?, executionId?, executionSeq?, traceparent?, truncated?, droppedLines? }`. ⚠ `environment` is OPTIONAL and additive — absent on every row written before it shipped, and a row without it reads as **`production`** (the drain matcher and `--environment` agree on that), so its absence never means sandbox. `metadata.channel` is the raw request channel: **`dev` marks every turn `lua chat` sent** (`src/api/chat.api.service.ts` posts `?channel=dev`, for `-e sandbox` and `-e production` alike) — it separates CLI traffic from end-user traffic, **not** sandbox from production; `pop` is the website widget, `web` other web clients. A tool `execute` that throws is logged as `subType: 'error'` under **`logSource: 'skill'`** (the whole tool site runs under that source, lua-core `execute-function.service.ts`); **`agent_error` is written only by the chat pipeline's** generate/stream error handlers (`chat.service.ts`), never by a tool. Reading needs the `logs:read` scope (an older key's `knowledge:read` / `automations:read` / `analytics:read` is accepted for two more releases, with `Deprecation: true` and a `Sunset` header) | RO | +| ⏳ 3.38.0 `lua drains [list\|status\|deliveries\|create\|update\|delete\|test\|verify\|pause\|resume\|rotate-secret] [id] [--org ] [--json]` | **log drains** — copies the ORGANIZATION's agent log records to a destination it owns (`http` · `otlp` · `datadog` · `betterstack`). An org resource, so no project directory is needed: `--org`, else `lua.skill.yaml`, else the credential when it reaches exactly one org (several → exit 2 listing the ids). Needs **`logs:manage`**, a SENSITIVE scope a wildcard does not satisfy (missing → exit 10, one line, no `💡` block); reading the records themselves is `logs:read`. ⚠ `status` exits **2** when ANY drain is `failing` — the same code as a usage error, set AFTER printing, so decide from `drains[].state` in `--json`. `test`/`verify` are 202-then-poll (30 s / 60 s; `pending: true` and exit 1 when nothing landed — not a failure to send). Secrets are never flags: `--header ` prompts hidden, `--header-from-env =` is the `--ci` form, and the HMAC signing secret is minted server-side and printed exactly once (create, and `rotate-secret` without `--finalize`). Under `--json` stdout is the machine's and the verification/acknowledgement chrome goes to stderr. Every verb answers 404 → exit 3 until the org is enabled. Full reference: `lib/knowledge/log-drains.md`; the slash is `/lua-drains` | RO for `list`/`status`/`deliveries`/`test`; **S** for the other seven (org config, `ask` tier — not a `P` deploy verb) | ### Push & deploy | Command | Shape | Effect | @@ -142,10 +143,10 @@ First find out whether the agent **has agent versions**: `lua version list --jso | workflow | `workflow` | `lua workflows deploy -v latest` — scoped promote; `lua workflows activate -v ` **also deploys that version** (`src/commands/workflows.ts` ~343: `-v` routes to `deployCore`) | `lua workflows activate ` without `-v` only enables schedules/triggers; workflows are never part of `lua push all` below 3.36.0 — ⏳ lua-cli 3.36.0 pushes **and activates** them in stage-all (§4 push row) | | everything | `lua push all --force` (stage-all; workflows excluded below 3.36.0 — ⏳ included *and activated* from 3.36.0, §4) | **versioned agent**: `lua version create` → `lua version promote ` (atomic; also the rollback path) · **unversioned**: `lua deploy all --force` (latest of every deployable primitive) | `promote` is also rollback — it rewrites the live skill pins from the snapshot, undoing any `lua deploy skill` made since (the served persona is untouched by promote; roll it back with `lua deploy persona --set-version `) | -After a deploy: `lua logs --type all --limit 30 --json` and scan `subType === 'error'`; `lua status --json` shows `diffs[].status` per primitive. +After a deploy: ⏳ `lua logs --type all --since --environment production --json` (or `--follow` to watch it live) and scan `subType === 'error'` — below 3.38.0, `lua logs --type all --limit 30 --json`; `lua status --json` shows `diffs[].status` per primitive. --- ## 6. Docs map (docs.heylua.ai) for `WebFetch` / the `lua-docs` MCP -CLI pages live at `/cli/-command` (`/cli/sync-command`, `/cli/chat-command`, `/cli/logs-command`, `/cli/env-command`, `/cli/persona-command`, `/cli/skills-command`, `/cli/webhooks-command`, `/cli/jobs-command`, `/cli/triggers-command`, `/cli/integrations-command`, `/cli/mcp-command`, `/cli/workflows-command`, `/cli/version-command`, `/cli/git-command`, `/cli/source-command`, `/cli/devices-command`, `/cli/voice-command`, `/cli/marketplace-command`, `/cli/features-command`, `/cli/resources-command`, `/cli/channels-command`, `/cli/production-command`, `/cli/preprocessors-command`, `/cli/postprocessors-command`); `init/compile/test/push/deploy` are on `/cli/skill-management`; `auth` on `/cli/authentication`; `status/agents/models/update/telemetry/governance` on `/cli/utility-commands`; `/cli/non-interactive-mode`, `/cli/debugging`, `/cli/troubleshooting`. SDK: `/api/luaagent`, `/api/luaskill`, `/api/luatool`, `/api/luawebhook`, `/api/luatrigger`, `/api/luajob`, `/api/preprocessor`, `/api/postprocessor`, `/api/luamcpserver`, `/api/device-definition`, `/api/voice`, `/api/user`, `/api/data`, `/api/products`, `/api/baskets`, `/api/orders`, `/api/jobs`, `/api/ai`, `/api/agents`, `/api/integrations`, `/api/channels`, `/api/inbox`, `/api/templates`, `/api/cdn`, `/api/lua`, `/api/environment`, `/api/query`. Workflows: `/workflows/quick-start`, `/workflows/authoring`, `/workflows/job-tier`, `/workflows/connections`, `/workflows/runs-and-events`, `/workflows/from-chat`, `/overview/workflows`. Concepts: `/overview/`. Channels: `/channels/`. Marketplace: `/marketplace/`. Devices: `/devices/*`. Getting started: `/getting-started/quick-start`. Changelog: `/changelog`. The docs MCP tool `mcp__plugin_lua-agent-builder_lua-docs__query_docs_filesystem_lua_cli` reads these as `/.mdx` (e.g. `head -200 /cli/workflows-command.mdx`). +CLI pages live at `/cli/-command` (`/cli/sync-command`, `/cli/chat-command`, `/cli/logs-command`, `/cli/env-command`, `/cli/persona-command`, `/cli/skills-command`, `/cli/webhooks-command`, `/cli/jobs-command`, `/cli/triggers-command`, `/cli/integrations-command`, `/cli/mcp-command`, `/cli/workflows-command`, `/cli/version-command`, `/cli/git-command`, `/cli/source-command`, `/cli/devices-command`, `/cli/voice-command`, `/cli/marketplace-command`, `/cli/features-command`, `/cli/resources-command`, `/cli/channels-command`, `/cli/production-command`, `/cli/preprocessors-command`, `/cli/postprocessors-command`); `init/compile/test/push/deploy` are on `/cli/skill-management`; `auth` on `/cli/authentication`; `status/agents/models/update/telemetry/governance` on `/cli/utility-commands`; `/cli/non-interactive-mode`, `/cli/debugging`, `/cli/troubleshooting`. SDK: `/api/luaagent`, `/api/luaskill`, `/api/luatool`, `/api/luawebhook`, `/api/luatrigger`, `/api/luajob`, `/api/preprocessor`, `/api/postprocessor`, `/api/luamcpserver`, `/api/device-definition`, `/api/voice`, `/api/user`, `/api/data`, `/api/products`, `/api/baskets`, `/api/orders`, `/api/jobs`, `/api/ai`, `/api/agents`, `/api/integrations`, `/api/channels`, `/api/inbox`, `/api/templates`, `/api/cdn`, `/api/lua`, `/api/environment`, `/api/query`. Workflows: `/workflows/quick-start`, `/workflows/authoring`, `/workflows/job-tier`, `/workflows/connections`, `/workflows/runs-and-events`, `/workflows/from-chat`, `/overview/workflows`. Concepts: `/overview/`. Channels: `/channels/`. Marketplace: `/marketplace/`. Devices: `/devices/*`. Getting started: `/getting-started/quick-start`. Log drains: `/drains/`, `/drains/packs/`, `/reference/cli/`, `/reference/rest/log-drains`, the batch schema at `/schemas/logs/1.0`. Changelog: `/changelog`. The docs MCP tool `mcp__plugin_lua-agent-builder_lua-docs__query_docs_filesystem_lua_cli` reads these as `/.mdx` (e.g. `head -200 /cli/workflows-command.mdx`). diff --git a/plugins/lua-agent-builder/lib/knowledge/log-drains.md b/plugins/lua-agent-builder/lib/knowledge/log-drains.md new file mode 100644 index 0000000..c8372f0 --- /dev/null +++ b/plugins/lua-agent-builder/lib/knowledge/log-drains.md @@ -0,0 +1,161 @@ +# Log drains reference (⏳ requires lua-cli 3.38.0 or later) + +Read from the lua-cli source, not from the public docs: `src/cli/command-definitions.ts` (the `drains` and `logs` declarations), `src/commands/drains.ts` (context, the read verbs, the dispatcher), `src/commands/drains.mutations.ts` (create/update/delete/test/verify/pause/resume/rotate-secret), `src/api/drains.api.service.ts`, `src/utils/aliases.ts` (`drains.action`, `logs.type`), `@lua/shared-types` `log-drain.types.ts` / `vm-execution-log.types.ts`, and `@lua/shared-observability` `drain-scrubber.ts`. The reference pages (`/reference/cli/drains`, `/drains/*`) agree with all of it and are the place to send a user, never the place to learn a flag from. + +**⏳ Everything in this file needs lua-cli 3.38.0 or later.** `lua drains` does not exist below it (exit 1, `unknown command`), and `lua logs --since / --until / --follow / --environment` are unknown options (commander exits 1). The plugin's pin is 3.38.0 since 1.5.0. + +lua-cli is a TypeScript toolchain. It has nothing to do with the Lua programming language. + +--- + +## 1. What a drain is + +A **log drain** copies agent execution log records to a destination the customer owns — an HTTPS receiver, an OTLP collector, Datadog or Better Stack — as they happen. It is a **copy, not a move**: everything a drain ships is still readable with `lua logs` and still counts against the platform's retention window. + +A drain lives on the **organization**, never on one agent. That is why the command needs no project directory: the org comes from `--org `, else from the project's `lua.skill.yaml`, else from the credential's own authorization projection **when it reaches exactly one org** — a credential that reaches several exits 2 with the ids listed (`resolveDrainsContext` / `orgFromCredential`, `drains.ts`). + +Roll-out is per organization. Until an org is enabled, every verb answers **404** → exit 3. That is the first thing to check when `lua drains list` fails for a user whose credential is fine. + +A drain is: a **destination** (type + endpoint/site + headers), **selectors** (sources, severity, agents, environments, sampling, content), a **state**, **health counters** (24 h) and a **quota**. Caps: 5 drains per org, 10 on Enterprise (`LOG_DRAIN_MAX_PER_ORG*`). + +## 2. The verbs (`lua drains [action] [id]`) + +`validateOrSuggest('drains.action', …)` accepts these eleven canonical actions; no action at all means `list`. Aliases the CLI resolves: `ls`/`view` → list, `show`/`info`/`health` → status, `history`/`attempts`/`deliver` → deliveries, `new`/`add` → create, `edit` → update. + +| Verb | Shape | Notes | +|---|---|---| +| `list` | `lua drains list [--json]` | Every drain in the org. Bare `lua drains` is the same thing — there is no interactive picker | +| `status` | `lua drains status [] [--json]` | Health + quota; with an id, one drain in detail. **Exit 2 when any drain is `failing`**, set on `process.exitCode` *after* printing so `--json` still emits | +| `deliveries` | `lua drains deliveries [--limit ] [--kind ] [--json]` | Recent attempts, newest first. `--limit` 1..100 (default 20); `--kind` ∈ `batch test verify heartbeat dropped truncated`. Never a request body | +| `create` | `lua drains create --name … --type … [--endpoint\|--site] …` | Prints the signing secret **once** | +| `update` | `lua drains update [--no-include-content]` | PATCH: only the flags given are sent. An empty patch is exit 2, and `--type` is rejected — a drain cannot change kind in place | +| `delete` | `lua drains delete [--yes]` | Soft delete: destination credentials purged, queued records dropped. Confirms unless `--yes`; under `--ci` without `--yes` it exits 2 having changed nothing | +| `test` | `lua drains test ` | Sends one `lua.drain.test` record down the **real** delivery path | +| `verify` | `lua drains verify ` | Re-runs ownership verification | +| `pause` | `lua drains pause [--reason ]` | Stops delivering; **buffering continues** | +| `resume` | `lua drains resume ` | Returns to `degraded`, never straight to `healthy` | +| `rotate-secret` | `lua drains rotate-secret [--finalize]` | New HMAC secret, printed **once**; `--finalize` closes the overlap window early and mints nothing | + +Every verb takes `--org ` and `--json`. Every verb needs an id except `list`, `status` and `create`; a missing id is exit 2 naming the verb. + +**`test` and `verify` are asynchronous on the wire.** Both POST (the route answers 202) and then poll the customer-visible evidence — the deliveries log matched on the 202's `batchId` for `test`, the drain's own `verification` block for `verify`. `test` waits up to **30 s** (30 × 1 s), `verify` up to **60 s** (`DRAIN_POLL`). Nothing landed in time ⇒ exit 1 with `pending: true` in the JSON and the command that reads the outcome later (`lua drains deliveries --kind test`, `lua drains status `). The request is already enqueued when the wait starts — a timeout is not a failure to send. + +## 3. States + +| State | Delivering? | Buffering? | Meaning | +|---|---|---|---| +| `pending_verification` | no | yes | Created, or the endpoint changed. Records queue until ownership is proved | +| `healthy` | yes | — | Verified and succeeding | +| `degraded` | yes | — | >10% of the last five minutes' attempts failed, or it has just resumed | +| `failing` | yes | — | No success for 15 minutes. Still trying | +| `paused` | no | yes | Stopped by a person, by the quota ladder, or automatically after 24 h without a success (reason `auto`) | +| `disabled` | no | **no** | Turned off. Nothing is queued while it is off | + +`LOG_DRAIN_STATES`; pause reasons `manual | auto | quota | operations`. Changing the endpoint or the type sends a drain back to `pending_verification` and records buffer meanwhile. A drain 24 h in `failing` pauses itself. + +⚠ `lua drains status` exit **2** is also the usage exit code. A script that must tell a failing drain from a mistyped command reads `drains[].state` out of `--json`, never the exit status alone. + +## 4. Selectors + +**Sources** — 19 selectable in phase 1: the 18 `AGENT_LOG_SOURCES` (`skill job webhook trigger preprocessor postprocessor user_message agent_response agent_error runtime mcp rag device device-trigger model-resolver workflow-step workflow-script workflow`) plus **`execution`** (the `type: 'metric'` start/complete/error rows, which no `lua logs --type` value reaches). `LOG_DRAIN_SOURCES` also declares five phase-2 members (`delivery`, `trigger-execution`, `job-execution`, `workflow-run`, `workflow-audit`) that `--sources` refuses today. The default set is every selectable source **except** the two content ones. + +**Content** — `user_message` and `agent_response` are `LOG_DRAIN_CONTENT_SOURCES`: off unless `--include-content`. Naming either in `--sources` turns content on implicitly and triggers the same acknowledgement (`wantsContent`). + +**Severity** — `--min-severity debug|info|warn|error`, default `info` (so `debug` is dropped, which is most agents' volume). `--sampling debug=0.1` keeps a deterministic tenth of `debug` instead; sampling is by execution, and only `debug` is sampleable — any other key is exit 2. + +**Agents** — `--agents all` (→ `'*'`, every agent in the org including ones created later) or a comma-separated id list. Inside a project directory the default is **that project's agent**; outside one it is `'*'` (`context.agentId` is absent there). + +**Environments** — `--environments production` (the default) and/or `sandbox` (`AGENT_LOG_ENVIRONMENTS`). A row with **no** environment reads as `production`. A sandbox-only drain is the cheapest way to prove a destination before production traffic reaches it. + +## 5. The verification handshake + +A new drain starts in `pending_verification` and buffers rather than delivers. `lua drains verify ` mints a **single-use token, valid 10 minutes**, and sends one batch carrying one `lua.drain.test` record. + +- **`http` is the only type that verifies by echo** (`TOKEN_ECHO_TYPES`). The receiver must answer 2xx **and** copy the request's `X-Lua-Verify` value back as its own response header. If the response carries no such header, one follow-up `GET {origin}/.well-known/lua-drain-verify` is made and a body whose trimmed content equals the token is accepted instead — the escape hatch for a proxy that strips unknown response headers. +- **`otlp`, `datadog` and `betterstack` verify by test post**: one record to the intake, and any 2xx is accepted. + +`verification.outcome` and the line the CLI prints (`VERIFY_OUTCOME_TEXT`): `ok` → "verified" · `no_2xx` → "the endpoint did not answer 2xx" · **`token_not_echoed` → "token not echoed"** · `ssrf_refused` → "the endpoint was refused by the SSRF guard" · `timeout` → "the endpoint did not answer in time" · `error` → "the verification request failed". `token_not_echoed` is the commonest failure and the phrase the docs tell people to search for: the receiver answered, but dropped the header. + +Verification is limited to **5 attempts per drain per hour** (429 `DRAIN_VERIFY_RATE_LIMITED`). `create` prints a copy-pasteable `curl` that proves the receiver does what verification needs — with `$ENV_VAR` for an env-backed header and a placeholder for a prompted one, never a value. + +## 6. Destination presets + +| `--type` | Endpoint | Auth | Signed? | Verification | +|---|---|---|---|---| +| `http` | `--endpoint`, the full HTTPS URL | your own `--header`s | **yes**, `X-Lua-Signature` | token echo | +| `otlp` | `--endpoint`, the **full `/v1/logs` URL**, used verbatim — the CLI appends nothing | your own `--header`s | no | test post | +| `datadog` | derived from `--site`; `--endpoint` is refused | `--header DD-API-KEY` | no | test post | +| `betterstack` | `--endpoint`, the source's own ingesting host (there is no template — it is per source and per region) | `--header Authorization`, entered as `Bearer ` | no | test post | + +`--site` values (`LOG_DRAIN_DATADOG_SITES`): `datadoghq.com`, `datadoghq.eu`, `us3.datadoghq.com`, `us5.datadoghq.com`, `ap1.datadoghq.com`, `ddog-gov.com`. `--format json|ndjson` is a generic-HTTPS choice; OTLP and the two vendor presets fix their own encoding. `LOG_DRAIN_TYPES` also declares the phase-2/3 types (`splunk loki axiom newrelic sumologic s3 syslog`) — lua-api answers 422 `DRAIN_TYPE_UNAVAILABLE` for them and the CLI refuses them locally against `LOG_DRAIN_TYPES_PHASE1`. + +Generic-HTTPS request contract: `Content-Type: application/json` (or `application/x-ndjson`), `Content-Encoding: gzip` (bodies are **always** gzipped), `X-Lua-Signature: t=,v1=` — HMAC-SHA256 over the **uncompressed** body, with a second `v1=` during a rotation window — `X-Lua-Batch-Id`, `X-Lua-Schema`, `X-Lua-Drain-Id`, `X-Lua-Verify` (verification batches only), `User-Agent: LuaDrain/1.0 (+https://docs.heylua.ai/drains)`. + +## 7. Delivery, retries and quotas + +At-least-once, unordered. **Consumers dedup on `records[].id`, never on `X-Lua-Batch-Id`** — a retried record comes back under a new batch id alongside whatever else is due. One batch carries exactly one `resource`, so records from three agents become three batches. + +Flush is about every 2 s. Caps: 500 records / 1 MiB per batch (Datadog 1,000 / 5 MiB, 1 MiB per record), 10 s per attempt, 4 concurrent requests per drain, **6-hour retry horizon** (older records are dropped), 10 custom headers per drain. An over-long record body is cut and suffixed `…[truncated for destination]` rather than dropped. + +Retry backoff is full jitter — a delay drawn uniformly from 0 up to a ceiling that doubles per consecutive failure (2, 4, 8, 16, 32 s) and stops at 60 s. **A `Retry-After` header (delta-seconds or an HTTP date) overrides that and is honoured exactly, clamped at one hour.** Retried statuses are `408 429 500 502 503 504`; every `3xx` (never followed) and every other `4xx` is terminal — the batch is dropped and counted under `rejected`. **OTLP is the exception**: only `429 502 503 504` are retried there and `400`/`500` are terminal, so a collector that answers `500` under load loses those batches — make it answer `503` while it sheds. + +`deliveries[].errorClass`: `network timeout dns ssrf redirect http_4xx http_429 http_5xx rejected partial encode`. + +Quota (per day, reset 00:00 UTC, visible under `quota` in `status --json`): 1,000,000 events / 1 GiB on Team, 10,000,000 / 10 GiB on Business, unmetered on Enterprise. The ladder degrades rather than cutting off — **80%** notify, **100%** drop `debug`, **125%** drop `info` too, **150%** pause the drain with reason `quota`. `warn` and `error` are never dropped before the drain pauses. + +The drain **routes** themselves are rate-limited: **429 with `code: "DRAIN_RATE_LIMITED"` and a bare `Retry-After` in seconds**. There are no `X-RateLimit-*` headers — that header is the whole signal, so back off for exactly what it says. + +## 8. The scrubber, and what never leaves Lua + +Three things hold on every drain with no way to turn them off: + +1. **Credential shapes are masked.** Every record body and every string attribute passes `@lua/shared-observability` `drain-scrubber.ts` before encoding. Built-in rules cover authorization and basic/bearer headers, JWTs, private-key blocks, connection-string passwords, URL secret parameters, JSON secret values, AWS access keys, typed and legacy Lua API keys, Lua handoff codes and scoped keys, and vendor keys for Anthropic, OpenAI, Stripe, Slack, GitHub, Twilio and Google. The mask is **`[redacted:]`**, where `` is the rule's **class** for a built-in — `[redacted:builtin]`, `[redacted:lua-token]`, `[redacted:vendor-key]` — and **`org:`** for an organization rule, i.e. `[redacted:org:]`. An org may add up to **20** regexes of at most 256 characters each. +2. **Destination credentials are never readable.** Header values and the signing secret are write-only. A read of a drain (`list`, `status`, REST) returns header **names** and the last four characters, never a value. +3. **No request body is stored.** A delivery row keeps status code, latency, bytes, record count, error class and up to 1 KB of the destination's *response*, scrubbed. There is no field that could hold what was sent. + +⚠ Scrubbing is **not** a substitute for not logging secrets: whatever a tool prints with `console.log` is stored as printed, and the scrubber only catches shapes it recognises. Say this whenever a user asks whether `--include-content` is safe. + +## 9. Scopes + +| Surface | Scope | +|---|---| +| Every `lua drains` verb, and the deliveries log | `logs:manage` | +| Reading log records (`lua logs`, the A4 read routes, the `tail_logs` MCP tool) | `logs:read` | + +`logs:manage` is **sensitive**: a wildcard such as `logs:*` does not satisfy it — a scoped key holds it only when it was granted by exact name. `logs:read` is an ordinary read scope, so a key with `*:read` already has it. A key without `logs:manage` gets one line and no `💡` block, because the remedy is in the message: `✖ forbidden: Missing scope: logs:manage. Create a key with that scope (lua auth key) or ask an org admin.` → **exit 10** (`MISSING_SCOPE_MESSAGE`, `drainsCall`). ⚠ Never run `lua auth key` for the user — it prints the raw key; tell them to run it in their own terminal. + +The log read routes moved to `logs:read` and accept the previous scope (`knowledge:read`, `automations:read`, `analytics:read`) for **two releases**, answering with `Deprecation: true`, a `299` warning and a `Sunset` header. A user still on an old key should re-mint before that date. + +## 10. Secrets: the rules the plugin must never break + +- **The signing secret is never an input.** The server mints it; the CLI prints it exactly once, at `create` and at a non-`--finalize` `rotate-secret`. It is in the output in exactly one place and nothing stores it. **Never copy it into a file, an env var, a commit, a summary or the transcript** — tell the user it is on their screen and that a lost secret is rotated, not recovered. +- **Header values are never flags.** `--header ` takes a NAME and prompts for the value hidden (`type: 'password'`); passing `--header NAME=value` is exit 2 by design, because a shell history and a CI log both keep it. Under `--ci` the prompt is impossible, so the only form is `--header-from-env =`, which reads the value from that variable — and an unset or empty variable is exit 2 naming the **variable**, never its value. +- A rotation opens a **24-hour overlap window** in which every batch carries **two** `v1=` signatures, so a receiver can be updated without dropping a batch; `--finalize` closes it early and mints no new secret. The two answers are different shapes and are told apart by the **flag**, never by whether a `secret` key happens to be present. +- `--include-content` prints `CONTENT_WARNING_TEXT` and `CONTENT_ACKNOWLEDGEMENT_TEXT` **first** and only then requires a confirmation or `--yes`; without either it exits non-zero having printed both, so the operator reads what they were asked to accept. Reproduce those two paragraphs verbatim — never paraphrase them. +- `lua drains update --no-include-content` is the opposite of the acknowledgement and needs none. The drain **keeps its sources**: `user_message` / `agent_response` records keep arriving with the body withheld and `lua.content.withheld` set, so the customer still sees that a turn happened, on what channel, for which end user — without the words. Drop the sources too if the records should stop. Naming a content source and `--no-include-content` in one update is exit 2 rather than silently resolved. + +## 11. Output contract (`--json` on stdout, hints on stderr) + +Every verb supports `--json`, and under it **stdout is the machine's**: `lua drains create --json | jq -r '.secret'` has to work. Human chrome that must survive — the verification instructions, the content acknowledgement — goes to **stderr** instead of being dropped (`emitAside`); none of it ever carries a secret or a header value. A refusal that escapes a verb under `--json` is the typed envelope on stdout, never the `✖` line. + +| Verb | `--json` payload | +|---|---| +| `list` | the API object unchanged — `{ drains: [...] }` plus whatever the server added | +| `status` | `{ drains: [{ id, name, state, health, quota }] }` — that projection and nothing else | +| `deliveries` | the page object, including `nextCursor` | +| `create` | `{ drain, secret }` (`secret` only when the server minted one) | +| `update` / `pause` / `resume` / `rotate-secret --finalize` | `{ drain }` | +| `delete` | `{ id, deleted: true }` | +| `test` | `{ batchId, statusCode, latencyMs, responseExcerpt, ok, errorClass }` + `pending: true` if nothing landed in time | +| `verify` | `{ verificationId, expiresAt, outcome, message, statusCode, latencyMs, responseExcerpt, state, verifiedAt, verified }` + `pending: true` | +| `rotate-secret` | `{ secret, rotation }` | + +`status --json` field names to read (`printDrainDetail` prints the same set): `health.{backlog, backlogBytes, deliveredCount24h, droppedCount24h, rejectedCount24h, p50LatencyMs, scrubHits24h, lastSuccessAt, lastFailureAt, lastStatusCode, lastError, pauseReason}` and `quota.{usedEvents, eventsPerDay, usedBytes, bytesPerDay, degradation, resetsAt}`. + +Exit codes: `0` ok · `1` a `test` that was not delivered or a `verify` that did not reach `ok` · **`2` usage, and "some drain is `failing`" on `status`** · `3` not found (including an org without the feature) · `10` the credential lacks `logs:manage` · `11` the API is unreachable. + +## 12. Reading the platform's own copy + +A drain never replaces `lua logs` — the records stay readable, and the two filters line up: a drain's `--sources` are the same vocabulary as `lua logs --type` (minus `execution`, which no `--type` value reaches), and its `--environments` are the same two values as `lua logs --environment`. The read window (`--since`, `--until`, `--follow`, `--environment`) is in `cli-reference.md` §4 under the `lua logs` row; the field that makes `--environment` possible is `metadata.environment`, added platform-side and **absent on every row written before it shipped** — a row with no `environment` reads as `production`, on the drain matcher and in a report alike. + +Typical triage order for "the drain is not delivering": `lua drains status ` (state, `lastError`, `lastStatusCode`, quota `degradation`) → `lua drains deliveries --limit 20` (per-attempt `errorClass`) → `lua drains test ` (does one record get through right now?) → `lua drains verify ` if the state is `pending_verification`. diff --git a/plugins/lua-agent-builder/lib/permissions-template.json b/plugins/lua-agent-builder/lib/permissions-template.json index 46b61e5..5220197 100644 --- a/plugins/lua-agent-builder/lib/permissions-template.json +++ b/plugins/lua-agent-builder/lib/permissions-template.json @@ -1,5 +1,5 @@ { - "_comment": "Permission rules the plugin REQUIRES for the §3.7 single-permission contract to function. Plugin-level settings.json's `permissions` block is silently ignored by Claude Code, so these rules can't ship as plugin defaults. /lua-doctor offers to merge this into the user's project `.claude/settings.json` on first run; the user accepts once and the rules persist for the project. Every command shape here is verified against lua-cli 3.33.0 (its src/cli/command-definitions.ts and src/utils/aliases.ts); the workflows policy / clear-gate / recompose rules against lua-cli 3.36.0. HOW THE PRODUCTION GATE WORKS (verified live against Claude Code on 2026-09-12 and documented at code.claude.com/docs/en/permissions: 'A deny or ask rule matches past any leading assignment' and 'Hook decisions don't bypass permission rules'): the bare production verbs (`lua deploy …`, `lua version promote …`, …) are deliberately NOT listed in `deny` or `ask` — such a rule also matches the user-confirmed form `LUA_DEPLOY_CONFIRMED=1 lua deploy …` and would make every deploy impossible. The gate for the bare forms is hooks/confirm-deploy.mjs (a PreToolUse exit-2 block wins over any allow rule, including a broad `Bash(lua *)` in your own settings); the `allow` entries below are the literal prefixed forms the deploy flow emits after your single confirmation, so they run without a second prompt. A bare verb that somehow reaches the permission layer falls to Claude Code's default (a prompt; a denial in `-p` mode). The alternative binaries `heylua` and `lua-ai` are the same program and are denied wholesale — the plugin only ever emits `lua`. test/lib/permissions-mirror.test.mjs fails if any deny/ask rule would shadow a confirmed form or any allow rule admits a bare one. Precedence is deny → ask → allow. The `ask` tier IS the single confirmation for workflow run-control verbs (start/approve/signal/cancel…): /lua-workflow relies on this prompt and does not AskUserQuestion on top. lua-cli 3.36.0 adds `lua workflows policy models|autonomy get|set` (an ORGANIZATION-wide policy — `get` is read-only and allowed, `set` asks: `policy autonomy set --enabled on` decides which workflow starts skip the consent card), `clear-gate` (alias `ungate`; clears a `model_policy` park — run control, asks) and `recompose` (alias `recompile`; publishes a new version of a chat-composed definition — asks). /lua-env (`lua env *` — deliberately ask even for `--list`, since the CLI prints masked values) and /lua-integrations (connect|update|disconnect|convert, webhooks create|pause|resume|delete, mcp activate|deactivate) rely on the same prompt. MCP: Claude Code names a plugin's MCP tools mcp__plugin____ (verified live 2026-09-12), so the read-only lua-platform server is allowed as mcp__plugin_lua-agent-builder_lua-platform (all five tools are GET-only) plus the two docs search tools; submit_feedback is deliberately not pre-approved. The bare mcp__lua-platform / mcp__lua-docs spellings cover a server the user registered with `claude mcp add`.", + "_comment": "Permission rules the plugin REQUIRES for the §3.7 single-permission contract to function. Plugin-level settings.json's `permissions` block is silently ignored by Claude Code, so these rules can't ship as plugin defaults. /lua-doctor offers to merge this into the user's project `.claude/settings.json` on first run; the user accepts once and the rules persist for the project. Every command shape here is verified against lua-cli 3.33.0 (its src/cli/command-definitions.ts and src/utils/aliases.ts); the workflows policy / clear-gate / recompose rules against lua-cli 3.36.0. HOW THE PRODUCTION GATE WORKS (verified live against Claude Code on 2026-09-12 and documented at code.claude.com/docs/en/permissions: 'A deny or ask rule matches past any leading assignment' and 'Hook decisions don't bypass permission rules'): the bare production verbs (`lua deploy …`, `lua version promote …`, …) are deliberately NOT listed in `deny` or `ask` — such a rule also matches the user-confirmed form `LUA_DEPLOY_CONFIRMED=1 lua deploy …` and would make every deploy impossible. The gate for the bare forms is hooks/confirm-deploy.mjs (a PreToolUse exit-2 block wins over any allow rule, including a broad `Bash(lua *)` in your own settings); the `allow` entries below are the literal prefixed forms the deploy flow emits after your single confirmation, so they run without a second prompt. A bare verb that somehow reaches the permission layer falls to Claude Code's default (a prompt; a denial in `-p` mode). The alternative binaries `heylua` and `lua-ai` are the same program and are denied wholesale — the plugin only ever emits `lua`. test/lib/permissions-mirror.test.mjs fails if any deny/ask rule would shadow a confirmed form or any allow rule admits a bare one. Precedence is deny → ask → allow. The `ask` tier IS the single confirmation for workflow run-control verbs (start/approve/signal/cancel…): /lua-workflow relies on this prompt and does not AskUserQuestion on top. lua-cli 3.36.0 adds `lua workflows policy models|autonomy get|set` (an ORGANIZATION-wide policy — `get` is read-only and allowed, `set` asks: `policy autonomy set --enabled on` decides which workflow starts skip the consent card), `clear-gate` (alias `ungate`; clears a `model_policy` park — run control, asks) and `recompose` (alias `recompile`; publishes a new version of a chat-composed definition — asks). lua-cli 3.38.0 adds `lua drains` (log drains — an ORGANIZATION resource, not a deploy): the read verbs `list`, `status`, `deliveries` and the one-record `test` are allowed, and the seven that change the org's configuration — `create`, `update`, `delete`, `verify`, `pause`, `resume`, `rotate-secret` — sit in `ask`, which IS /lua-drains's single confirmation. They are NOT in lib/tokenizer.mjs: nothing a drain verb does changes what runs in production, the `LUA_DEPLOY_CONFIRMED=1` prefix means ‘the user confirmed a DEPLOY’ and confirm-deploy would send the user to /lua-deploy for a log-shipping change. The per-verb spelling matters — a blanket `Bash(lua drains *)` in `ask` would prompt for a read, and one in `allow` would admit `rotate-secret`. Secrets never reach a command line: a header value is prompted or read from an env var (`--header-from-env NAME=ENV_VAR`) and the signing secret is minted server-side and printed once, so there is no credential-printing rule to deny here. /lua-env (`lua env *` — deliberately ask even for `--list`, since the CLI prints masked values) and /lua-integrations (connect|update|disconnect|convert, webhooks create|pause|resume|delete, mcp activate|deactivate) rely on the same prompt. MCP: Claude Code names a plugin's MCP tools mcp__plugin____ (verified live 2026-09-12), so the read-only lua-platform server is allowed as mcp__plugin_lua-agent-builder_lua-platform (all five tools are GET-only) plus the two docs search tools; submit_feedback is deliberately not pre-approved. The bare mcp__lua-platform / mcp__lua-docs spellings cover a server the user registered with `claude mcp add`.", "permissions": { "allow": [ "Bash(lua --version)", @@ -16,6 +16,10 @@ "Bash(lua chat --ci -m*)", "Bash(lua chat --ci -e * -m*)", "Bash(lua logs --ci*)", + "Bash(lua drains list*)", + "Bash(lua drains status*)", + "Bash(lua drains deliveries *)", + "Bash(lua drains test *)", "Bash(lua status --json*)", "Bash(lua status --ci*)", "Bash(lua agents *)", @@ -115,6 +119,13 @@ "Bash(lua * uninstall*)", "Bash(lua sync --pull --force*)", "Bash(lua env *)", + "Bash(lua drains create*)", + "Bash(lua drains update *)", + "Bash(lua drains delete *)", + "Bash(lua drains verify *)", + "Bash(lua drains pause *)", + "Bash(lua drains resume *)", + "Bash(lua drains rotate-secret *)", "Bash(lua pull*)", "Bash(lua chat clear*)", "Bash(lua source rollback*)", diff --git a/plugins/lua-agent-builder/mcp/lua-platform/dist/server.js b/plugins/lua-agent-builder/mcp/lua-platform/dist/server.js index 55678ee..ca3ef57 100644 --- a/plugins/lua-agent-builder/mcp/lua-platform/dist/server.js +++ b/plugins/lua-agent-builder/mcp/lua-platform/dist/server.js @@ -1,3 +1,3 @@ -var ie=Object.defineProperty;var ae=(e,t)=>{for(var r in t)ie(e,r,{get:t[r],enumerable:!0})};import{Server as $e}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Oe}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as Ue,ListToolsRequestSchema as Ne}from"@modelcontextprotocol/sdk/types.js";var K={};ae(K,{getAgent:()=>W,getDeploymentStatus:()=>te,listAgents:()=>G,listPrimitiveVersions:()=>Q,tailLogs:()=>re});import{spawn as le}from"node:child_process";var ce=6e4;function pe(e,{spawnFn:t=le,timeoutMs:r=ce,label:s="lua"}={}){return new Promise((o,i)=>{let a=t("lua",e,{shell:process.platform==="win32",windowsHide:!0,env:{...process.env,LUA_NO_BANNER:"1",LUA_NO_HINTS:"1"}}),n="",l="";a.stdout.on("data",m=>{n+=m.toString("utf8")}),a.stderr.on("data",m=>{l+=m.toString("utf8")});let c=setTimeout(()=>{a.kill("SIGTERM"),i(new Error(`${s}: 'lua ${e.join(" ")}' timed out after ${r}ms`))},r);a.on("exit",m=>{clearTimeout(c),m!==0?i(new Error(`'lua ${e.join(" ")}' exited ${m}: ${l.trim()}`)):o(n)}),a.on("error",m=>{clearTimeout(c),i(m)})})}function ue(e){let t=e,r=Array.isArray(t)?t:t?.agents??t?.orgs??[];return r.length>0&&r[0]&&Array.isArray(r[0].agents)?r.flatMap(o=>(o.agents??[]).map(i=>({id:i.agentId??i.id??i._id,name:i.name??null,orgId:o.orgId??o.id??o._id??null,orgName:o.name??o.registeredName??null,visibility:i.visibility??null}))):r.map(o=>({id:o.agentId??o.id??o._id,name:o.name??null,orgId:o.orgId??null,orgName:o.orgName??null,visibility:o.visibility??null}))}async function T({spawnFn:e,label:t="list_agents"}={}){let r=await pe(["agents","--json","--ci"],{spawnFn:e,label:t}),s;try{s=JSON.parse(r)}catch(o){throw new Error(`${t}: could not parse 'lua agents --json' output: ${o.message}`)}return ue(s)}var G={spec:{name:"list_agents",description:"List all Lua agents the authenticated credential can reach. Returns a compact array of {id, name, orgId, orgName, visibility} flattened across all organisations. Same data as `lua agents --json` \u2014 it resolves every organisation, so on accounts with many orgs this takes 20 s or more; prefer get_agent when you already know the id.",inputSchema:{type:"object",properties:{},required:[]}},async handler(e,t={}){let r=await T({spawnFn:t.spawnFn,label:"list_agents"});return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}};var W={spec:{name:"get_agent",description:"Look up one Lua agent by ID and return {id, name, orgId, orgName, visibility}. For what is live use get_deployment_status / list_primitive_versions; for logs use tail_logs.",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent ID (the `id` field from list_agents; also `agent.agentId` in lua.skill.yaml)"}},required:["agentId"]}},async handler({agentId:e},t={}){if(!e)throw new Error("agentId is required");let s=(await T({spawnFn:t.spawnFn,label:"get_agent"})).find(o=>o.id===e)??null;if(!s)throw new Error(`get_agent: no agent with id "${e}" found in the authenticated credential's accessible orgs. Run list_agents to see available agents.`);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}};import{readFile as j,readdir as de,stat as me}from"node:fs/promises";import{join as R,dirname as fe}from"node:path";import{homedir as ge}from"node:os";var he="https://api.heylua.ai",we="https://securetoken.googleapis.com/v1/token",ve=15e3,ye=6e4,_e=["TOKEN_EXPIRED","USER_DISABLED","USER_NOT_FOUND","INVALID_REFRESH_TOKEN","INVALID_GRANT_TYPE","MISSING_REFRESH_TOKEN"],X=new Map;function Ee(e){for(let t of e.split(/\r?\n/)){let r=t.match(/^\s*(?:export\s+)?LUA_API_KEY\s*=\s*(.*)$/);if(!r)continue;let s=r[1].trim(),o=s.match(/^(['"`])(.*?)\1(?:\s+#.*)?\s*$/);return o?s=o[2]:s=s.replace(/\s+#.*$/,"").trim(),s||null}return null}async function Se({sessionsDir:e,apiUrl:t}){let r;try{r=await de(e)}catch{return null}let s=[];for(let o of r){if(!o.endsWith(".json")||o.startsWith("."))continue;let i=R(e,o);try{let a=JSON.parse(await j(i,"utf8"));if(a?.kind!=="firebase-session"||a?.version!==1||typeof a.refreshToken!="string"||!a.refreshToken||typeof a.firebaseWebApiKey!="string"||!a.firebaseWebApiKey||a.apiUrl!==t)continue;let{mtimeMs:n}=await me(i);s.push({path:i,session:a,mtimeMs:n})}catch{}}return s.length===0?null:(s.sort((o,i)=>i.mtimeMs-o.mtimeMs),{path:s[0].path,session:s[0].session})}async function Ae({path:e,session:t,fetchFn:r=globalThis.fetch,now:s=Date.now}){let o=X.get(e);if(o&&o.expiresAt-s()>ye)return o.idToken;let i=new AbortController,a=setTimeout(()=>i.abort(),ve),n;try{n=await r(`${we}?key=${encodeURIComponent(t.firebaseWebApiKey)}`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:t.refreshToken}).toString(),signal:i.signal})}catch(p){throw new Error(`MCP_AUTH_UNAVAILABLE: could not refresh the lua-cli session (${p?.name==="AbortError"?"timed out":p.message}). Check your network, or set LUA_API_KEY to bypass the session.`)}finally{clearTimeout(a)}if(!n.ok){let p=n.statusText??String(n.status);try{let d=(await n.json())?.error?.message;typeof d=="string"&&d&&(p=d.split(":")[0].trim())}catch{}let g=n.status<500&&_e.find(u=>p.toUpperCase().includes(u));throw g?new Error(`MCP_AUTH_STALE: your lua-cli session was signed out (${g}). Signing out of the Lua dashboard, desktop or mobile app also ends CLI sessions. Run \`lua auth configure\` in a terminal to sign in again.`):new Error(`MCP_AUTH_UNAVAILABLE: lua-cli session refresh failed (${n.status} ${p}). Retry in a moment.`)}let l=await n.json(),c=l?.id_token,m=Number(l?.expires_in);if(typeof c!="string"||!c||!Number.isFinite(m)||m<=0)throw new Error("MCP_AUTH_UNAVAILABLE: the session refresh returned an invalid token. Run `lua auth configure` again.");return X.set(e,{idToken:c,expiresAt:s()+m*1e3}),c}async function z({env:e=process.env,credentialsPath:t=e.LUA_CREDENTIALS_PATH??R(ge(),".lua-cli","credentials"),sessionsDir:r=e.LUA_SESSIONS_DIR??R(fe(t),"sessions"),cwd:s=process.cwd(),apiUrl:o=e.LUA_API_URL||he,fetchFn:i=globalThis.fetch,now:a=Date.now}={}){if(e.LUA_API_KEY&&e.LUA_API_KEY.trim())return e.LUA_API_KEY.trim();try{let l=Ee(await j(R(s,".env"),"utf8"));if(l)return l}catch{}let n=await Se({sessionsDir:r,apiUrl:o});if(n)return Ae({...n,fetchFn:i,now:a});try{let l=(await j(t,"utf8")).trim();if(l){if(l.startsWith("{"))try{let c=JSON.parse(l);if(c?.apiKey)return c.apiKey}catch{}return l}}catch{}throw new Error("MCP_AUTH_STALE: No lua-cli credentials found in LUA_API_KEY, .env, ~/.lua-cli/sessions, or ~/.lua-cli/credentials. Run `lua auth configure` in a terminal (or /lua-auth in Claude Code).")}var be="https://api.heylua.ai",ke=1e4,Ge=Object.freeze({skills:"automations:read",webhooks:"automations:read",jobs:"automations:read",triggers:"automations:read",preprocessors:"automations:read",postprocessors:"automations:read",workflows:"workflows:read",persona:"agents:read",logs:"knowledge:read"}),Ie="skills/webhooks/jobs/triggers/preprocessors/postprocessors need automations:read, workflows need workflows:read, persona needs agents:read, logs need knowledge:read";async function v(e,{method:t="GET",body:r,query:s,fetchFn:o=globalThis.fetch,baseUrl:i=process.env.LUA_API_URL||be,timeoutMs:a=ke}={}){let n=await z(),l=new URL(i.replace(/\/$/,"")+e);if(s)for(let[p,g]of Object.entries(s))l.searchParams.set(p,String(g));let c=new AbortController,m=setTimeout(()=>c.abort(),a);try{let p=await o(l.toString(),{method:t,headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json","X-Lua-Client":"claude-plugin/1.4.0"},body:r?JSON.stringify(r):void 0,signal:c.signal});if(p.status===401)throw new Error("MCP_AUTH_STALE: lua-api returned 401. The credential is missing, expired, or the session was signed out (signing out of the Lua dashboard or apps also ends CLI sessions). Run `lua auth configure` in a terminal, or re-run /lua-doctor.");if(p.status===403)throw new Error(`MCP_FORBIDDEN: lua-api returned 403 for ${e}. The credential cannot access this agent or org, or it lacks the route scope (${Ie} \u2014 typed keys are scoped to the agents and role chosen at \`lua auth configure\`). Re-run /lua-doctor or check the LUA_API_KEY env var.`);if(!p.ok){let g=await p.text(),u=g;try{let d=JSON.parse(g);u=d?.error?.message??d?.message??g;let h=d?.error?.code;typeof h=="string"&&h&&!String(u).includes(h)&&(u=`${h}: ${u}`)}catch{}throw new Error(`lua-api ${p.status}: ${u}`)}return await p.json()}catch(p){throw p.name==="AbortError"?new Error(`MCP_TIMEOUT: ${e} did not respond in ${a}ms`):p}finally{clearTimeout(m)}}var V={skill:e=>e?.skills??[],webhook:e=>e?.data?.webhooks??[],job:e=>e?.data?.jobs??[],trigger:e=>e?.data?.triggers??[],preprocessor:e=>e?.data?.preprocessors??[],postprocessor:e=>e?.data?.postprocessors??[],workflow:e=>e?.data?.workflows??[]},D={skill:e=>e?.versions??[],webhook:e=>e?.data?.versions??[],job:e=>Array.isArray(e?.data)?e.data:[],trigger:e=>e?.data?.versions??[],preprocessor:e=>e?.data?.versions??[],postprocessor:e=>e?.data?.versions??[],workflow:e=>Array.isArray(e?.data)?e.data:[],persona:e=>e?.versions??[]},_={skill:"skills",webhook:"webhooks",job:"jobs",trigger:"triggers",preprocessor:"preprocessors",postprocessor:"postprocessors",workflow:"workflows"};function L(e,t){let r=V[e];if(!r)throw new Error(`extractList: unknown primitive type "${e}". Valid: ${Object.keys(V).join(", ")}`);return r(t)}function E(e,t){let r=D[e];if(!r)throw new Error(`extractVersions: unknown primitive type "${e}". Valid: ${Object.keys(D).join(", ")}`);return r(t)}function S(e){let t=e?.data?.activeVersionId;return typeof t=="string"&&t?t:null}function A(e,t,r){if(!t||typeof t!="object")return!1;switch(e){case"skill":case"persona":return t.isCurrent===!0;case"job":case"workflow":return t.active===!0;case"webhook":case"trigger":case"preprocessor":case"postprocessor":{if(t.isActive===!0)return!0;let s=S(r),o=t.versionId??t.id??null;return!!s&&o===s}default:return!1}}function b(e){if(!e||typeof e!="object")return null;let t=e.createdAt??e.createdDate??null;return t==null?null:typeof t=="number"?Number.isFinite(t)?new Date(t).toISOString():null:String(t)}function x(e){return!e||typeof e!="object"?null:e.versionId??e.id??null}var Xe=Object.keys(V),Z=Object.keys(D);var F=new Set(Z),Q={spec:{name:"list_primitive_versions",description:"List the server-side versions of one primitive (skill, webhook, job, trigger, preprocessor, postprocessor, workflow) or the agent persona. Returns { activeVersionId, versions: [{version, versionId, active, createdAt}] } \u2014 `active` is the version currently live. `versionId` is null for types whose versions route carries no per-entry id: skill, persona and webhook (the webhook route exposes the row id only under a misnamed `webhookId` key). For those, identify a version by `version`; for webhooks the live row id is the top-level `activeVersionId`. `activeVersionId` is populated only for webhook, trigger, preprocessor and postprocessor envelopes and is null for skill, job, workflow and persona.",inputSchema:{type:"object",properties:{agentId:{type:"string"},type:{type:"string",enum:[...F]},name:{type:"string",description:"Primitive name as written in lua.skill.yaml (omit for persona \u2014 an agent has only one)"}},required:["agentId","type"]}},async handler({agentId:e,type:t,name:r},s={}){if(!e||!t)throw new Error("agentId and type are required");if(!F.has(t))throw new Error(`Invalid type: ${t}. Valid: ${[...F].join(", ")}`);if(t!=="persona"&&!r)throw new Error("name is required for all types except persona");let o;if(t==="persona")o=`/developer/agents/${encodeURIComponent(e)}/persona/versions`;else{let c=await Te({agentId:e,type:t,name:r,fetchFn:s.fetchFn});o=`/developer/${_[t]}/${encodeURIComponent(e)}/${encodeURIComponent(c)}/versions`}let i=await v(o,{fetchFn:s.fetchFn}),n=E(t,i).map(c=>({version:c.version,versionId:x(c),active:A(t,c,i),createdAt:b(c)})),l={agentId:e,type:t,name:t==="persona"?null:r,activeVersionId:S(i),versions:n};return{content:[{type:"text",text:JSON.stringify(l,null,2)}]}}};async function Te({agentId:e,type:t,name:r,fetchFn:s}){let o=`/developer/${_[t]}/${encodeURIComponent(e)}`,i=await v(o,{fetchFn:s}),a=L(t,i),n=a.find(c=>c.name===r);if(!n){let c=a.map(m=>m.name).filter(Boolean).join(", ")||"(none)";throw new Error(`list_primitive_versions: no ${t} named "${r}" on agent ${e}. Available: ${c}`)}let l=n.id??n._id;if(!l)throw new Error(`list_primitive_versions: ${t} "${r}" exists but has no id field`);return l}var $=["skill","webhook","job","trigger","preprocessor","postprocessor","workflow"],ee=5,Re=45e3,te={spec:{name:"get_deployment_status",description:'What is live right now for an agent: for every skill, webhook, job, trigger, preprocessor, postprocessor and workflow the active (deployed) version, plus the active persona version. Composed from the per-type lua-api version routes (list calls in parallel, then per-item version lookups in chunks of 5). Bounded by a 45 s wall-clock budget: if exceeded, remaining items are listed with `error: "skipped: \u2026"` and the result carries `partial: true` + `partialReason`. Inside a project, `lua status --json` additionally compares local vs deployed.',inputSchema:{type:"object",properties:{agentId:{type:"string"}},required:["agentId"]}},async handler({agentId:e},t={}){if(!e)throw new Error("agentId is required");let r=encodeURIComponent(e),s=Number.isFinite(t.budgetMs)?t.budgetMs:Re,o=Date.now(),i=()=>Date.now()-o>s,a=u=>v(u,{fetchFn:t.fetchFn}),n={agentId:e,persona:null,primitives:{}};for(let u of $)n.primitives[u]=[];let[l,...c]=await Promise.allSettled([a(`/developer/agents/${r}/persona/versions`),...$.map(u=>a(`/developer/${_[u]}/${r}`))]);if(l.status==="fulfilled"){let u=l.value,d=E("persona",u),h=d.find(y=>A("persona",y,u))??null;n.persona={activeVersion:h?.version??null,activeVersionCreatedAt:b(h),versionCount:d.length}}else n.persona={error:l.reason?.message??String(l.reason)};let m=!1,p=0,g=0;for(let u=0;u<$.length;u++){let d=$[u],h=_[d],y=c[u];if(y.status!=="fulfilled"){n.primitives[d]={error:y.reason?.message??String(y.reason)};continue}let O=L(d,y.value).map(f=>({p:f,primId:f.id??f._id,name:f.name??f.id??f._id})).filter(f=>f.primId);g+=O.length;for(let f=0;fa(`/developer/${h}/${r}/${encodeURIComponent(w)}/versions`)))).forEach((w,U)=>{let{p:I,primId:q,name:B}=k[U];if(w.status!=="fulfilled"){n.primitives[d].push({name:B,id:q,error:w.reason?.message??String(w.reason)});return}let N=w.value,J=E(d,N),C=J.find(ne=>A(d,ne,N))??null,P={name:B,id:q,activeVersion:C?.version??null,activeVersionId:x(C)??S(N),activeVersionCreatedAt:b(C),versionCount:J.length};typeof I.active=="boolean"&&(P.enabled=I.active),d==="workflow"&&typeof I.dynamic=="boolean"&&(P.dynamic=I.dynamic),n.primitives[d].push(P)})}}return p>0&&(n.partial=!0,n.partialReason=`Wall-clock budget of ${s}ms exceeded after ${Date.now()-o}ms; ${p} of ${g} version lookups were not issued (entries carry error: "skipped: \u2026").`),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}};var Le=["skill","job","webhook","trigger","preprocessor","postprocessor","user_message","agent_response","agent_error","runtime","mcp","rag","device","device-trigger","model-resolver","workflow-step","workflow-script","workflow"],H=["error","warn","info","debug","start","complete"],M=new Set(["all",...Le]),xe=new Set(H),re={spec:{name:"tail_logs",description:"Fetch recent execution logs for an agent (same data as `lua logs --json`). Returns { logs: LogEntry[], pagination }. Each LogEntry has `subType` (error | warn | info | debug | start | complete), `message`, `timestamp`, and `metadata.logSource` / `metadata.primitiveName` \u2014 there is NO `level` field. Filter by source (`type`), primitive (`name`) and/or severity (`logType`, which filters on `subType`).",inputSchema:{type:"object",properties:{agentId:{type:"string"},type:{type:"string",enum:[...M],default:"all",description:"Log source filter (maps to lua-api `logSource`). `all` = no filter."},name:{type:"string",description:"Filter by primitive name (optional; maps to `primitiveName`)"},logType:{type:"string",enum:H,description:"Filter by LogEntry `subType` (optional; maps to lua-api `logType`). Omit for every severity. e.g. `error` to see only failures."},limit:{type:"number",default:50,minimum:1,maximum:100},page:{type:"number",minimum:1,description:"Page number for pagination (optional)"}},required:["agentId"]}},async handler({agentId:e,type:t="all",name:r,logType:s,limit:o=50,page:i},a={}){if(!e)throw new Error("agentId is required");if(!M.has(t))throw new Error(`Invalid type: ${t}. Valid: ${[...M].join(", ")}`);if(s!==void 0&&!xe.has(s))throw new Error(`Invalid logType: ${s}. Valid: ${H.join(", ")}`);if(o>100)throw new Error(`Invalid limit: ${o}. lua-api caps logs at 100 entries per call.`);let n={limit:o};t!=="all"&&(n.logSource=t),r&&(n.primitiveName=r),s&&(n.logType=s),i&&(n.page=i);let l=await v(`/developer/agents/${encodeURIComponent(e)}/logs`,{query:n,fetchFn:a.fetchFn});return{content:[{type:"text",text:JSON.stringify(l,null,2)}]}}};var oe=Object.values(K),Y=new $e({name:"lua-platform",version:"1.4.0"},{capabilities:{tools:{}}});Y.setRequestHandler(Ne,async()=>({tools:oe.map(e=>e.spec)}));Y.setRequestHandler(Ue,async e=>{let t=oe.find(r=>r.spec.name===e.params.name);if(!t)throw new Error(`Unknown tool: ${e.params.name}`);try{return await t.handler(e.params.arguments??{})}catch(r){return{content:[{type:"text",text:`Error: ${r.message}`}],isError:!0}}});function se(e){process.stderr.write(JSON.stringify(e)+` -`,()=>process.exit(1))}process.on("uncaughtException",e=>{se({event:"mcp_server_crash",kind:"uncaughtException",message:e?.message,stack:e?.stack,plugin_version:"1.4.0",lua_cli_version:process.env.LUA_CLI_VERSION??null,platform:process.platform,ts:new Date().toISOString()})});process.on("unhandledRejection",e=>{se({event:"mcp_server_crash",kind:"unhandledRejection",reason:String(e),stack:e?.stack,plugin_version:"1.4.0",lua_cli_version:process.env.LUA_CLI_VERSION??null,platform:process.platform,ts:new Date().toISOString()})});await Y.connect(new Oe); -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL3NlcnZlci5tanMiLCAiLi4vc3JjL3Rvb2xzL2luZGV4Lm1qcyIsICIuLi9zcmMvcnVuLWx1YS5tanMiLCAiLi4vc3JjL3Rvb2xzL2xpc3QtYWdlbnRzLm1qcyIsICIuLi9zcmMvdG9vbHMvZ2V0LWFnZW50Lm1qcyIsICIuLi9zcmMvYXV0aC5tanMiLCAiLi4vc3JjL2FwaS1jbGllbnQubWpzIiwgIi4uL3NyYy9yZXNwb25zZS1zaGFwZXMubWpzIiwgIi4uL3NyYy90b29scy9saXN0LXByaW1pdGl2ZS12ZXJzaW9ucy5tanMiLCAiLi4vc3JjL3Rvb2xzL2dldC1kZXBsb3ltZW50LXN0YXR1cy5tanMiLCAiLi4vc3JjL3Rvb2xzL3RhaWwtbG9ncy5tanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8vIE1DUCBzdGRpbyBzZXJ2ZXIgYm9vdHN0cmFwLiBQZXIgdGVjaCBzcGVjIFx1MDBBNzguMS5cblxuaW1wb3J0IHsgU2VydmVyIH0gZnJvbSAnQG1vZGVsY29udGV4dHByb3RvY29sL3Nkay9zZXJ2ZXIvaW5kZXguanMnO1xuaW1wb3J0IHsgU3RkaW9TZXJ2ZXJUcmFuc3BvcnQgfSBmcm9tICdAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2RrL3NlcnZlci9zdGRpby5qcyc7XG5pbXBvcnQge1xuICBDYWxsVG9vbFJlcXVlc3RTY2hlbWEsXG4gIExpc3RUb29sc1JlcXVlc3RTY2hlbWEsXG59IGZyb20gJ0Btb2RlbGNvbnRleHRwcm90b2NvbC9zZGsvdHlwZXMuanMnO1xuaW1wb3J0ICogYXMgdG9vbHMgZnJvbSAnLi90b29scy9pbmRleC5tanMnO1xuXG5jb25zdCBUT09MX1JFR0lTVFJZID0gT2JqZWN0LnZhbHVlcyh0b29scyk7XG5cbmNvbnN0IHNlcnZlciA9IG5ldyBTZXJ2ZXIoXG4gIHsgbmFtZTogJ2x1YS1wbGF0Zm9ybScsIHZlcnNpb246ICcxLjQuMCcgfSxcbiAgeyBjYXBhYmlsaXRpZXM6IHsgdG9vbHM6IHt9IH0gfVxuKTtcblxuc2VydmVyLnNldFJlcXVlc3RIYW5kbGVyKExpc3RUb29sc1JlcXVlc3RTY2hlbWEsIGFzeW5jICgpID0+ICh7XG4gIHRvb2xzOiBUT09MX1JFR0lTVFJZLm1hcCgodCkgPT4gdC5zcGVjKSxcbn0pKTtcblxuc2VydmVyLnNldFJlcXVlc3RIYW5kbGVyKENhbGxUb29sUmVxdWVzdFNjaGVtYSwgYXN5bmMgKHJlcSkgPT4ge1xuICBjb25zdCB0b29sID0gVE9PTF9SRUdJU1RSWS5maW5kKCh0KSA9PiB0LnNwZWMubmFtZSA9PT0gcmVxLnBhcmFtcy5uYW1lKTtcbiAgaWYgKCF0b29sKSB0aHJvdyBuZXcgRXJyb3IoYFVua25vd24gdG9vbDogJHtyZXEucGFyYW1zLm5hbWV9YCk7XG4gIHRyeSB7XG4gICAgcmV0dXJuIGF3YWl0IHRvb2wuaGFuZGxlcihyZXEucGFyYW1zLmFyZ3VtZW50cyA/PyB7fSk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIHJldHVybiB7XG4gICAgICBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6IGBFcnJvcjogJHtlcnIubWVzc2FnZX1gIH1dLFxuICAgICAgaXNFcnJvcjogdHJ1ZSxcbiAgICB9O1xuICB9XG59KTtcblxuLy8gXHUwMEE3MTkuNiBjcmFzaCByZXBvcnRpbmcgXHUyMDE0IG11c3QgbGFuZCBCRUZPUkUgcHJvY2VzcyBleGl0IHNvIHRoZSBldmVudCByZWFjaGVzXG4vLyB0ZWxlbWV0cnkuIFdpbmRvd3MgY2FuIGRyb3AgdGhlIGxhc3Qgc3RkZXJyIHdyaXRlIGlmIHByb2Nlc3MuZXhpdCBmaXJlc1xuLy8gc3luY2hyb25vdXNseSBhZnRlciBpdDsgc2FtZSBidWcgY2xhc3MgYXMgbGliL2hvb2stcnVudGltZS5tanMncyBleGl0KCkuXG4vLyBFeGl0IG9ubHkgaW5zaWRlIHRoZSB3cml0ZSBjYWxsYmFjayBzbyB0aGUgZmx1c2ggY29tcGxldGVzLlxuZnVuY3Rpb24gcmVwb3J0Q3Jhc2hBbmRFeGl0KHBheWxvYWQpIHtcbiAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkgKyAnXFxuJywgKCkgPT4gcHJvY2Vzcy5leGl0KDEpKTtcbn1cblxucHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCAoZXJyKSA9PiB7XG4gIHJlcG9ydENyYXNoQW5kRXhpdCh7XG4gICAgZXZlbnQ6ICdtY3Bfc2VydmVyX2NyYXNoJyxcbiAgICBraW5kOiAndW5jYXVnaHRFeGNlcHRpb24nLFxuICAgIG1lc3NhZ2U6IGVycj8ubWVzc2FnZSxcbiAgICBzdGFjazogZXJyPy5zdGFjayxcbiAgICBwbHVnaW5fdmVyc2lvbjogJzEuNC4wJyxcbiAgICBsdWFfY2xpX3ZlcnNpb246IHByb2Nlc3MuZW52LkxVQV9DTElfVkVSU0lPTiA/PyBudWxsLFxuICAgIHBsYXRmb3JtOiBwcm9jZXNzLnBsYXRmb3JtLFxuICAgIHRzOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gIH0pO1xufSk7XG5cbnByb2Nlc3Mub24oJ3VuaGFuZGxlZFJlamVjdGlvbicsIChyZWFzb24pID0+IHtcbiAgcmVwb3J0Q3Jhc2hBbmRFeGl0KHtcbiAgICBldmVudDogJ21jcF9zZXJ2ZXJfY3Jhc2gnLFxuICAgIGtpbmQ6ICd1bmhhbmRsZWRSZWplY3Rpb24nLFxuICAgIHJlYXNvbjogU3RyaW5nKHJlYXNvbiksXG4gICAgc3RhY2s6IC8qKiBAdHlwZSB7YW55fSAqLyAocmVhc29uKT8uc3RhY2ssXG4gICAgcGx1Z2luX3ZlcnNpb246ICcxLjQuMCcsXG4gICAgbHVhX2NsaV92ZXJzaW9uOiBwcm9jZXNzLmVudi5MVUFfQ0xJX1ZFUlNJT04gPz8gbnVsbCxcbiAgICBwbGF0Zm9ybTogcHJvY2Vzcy5wbGF0Zm9ybSxcbiAgICB0czogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICB9KTtcbn0pO1xuXG5hd2FpdCBzZXJ2ZXIuY29ubmVjdChuZXcgU3RkaW9TZXJ2ZXJUcmFuc3BvcnQoKSk7XG4iLCAiZXhwb3J0IHsgbGlzdEFnZW50cyB9IGZyb20gJy4vbGlzdC1hZ2VudHMubWpzJztcbmV4cG9ydCB7IGdldEFnZW50IH0gZnJvbSAnLi9nZXQtYWdlbnQubWpzJztcbmV4cG9ydCB7IGxpc3RQcmltaXRpdmVWZXJzaW9ucyB9IGZyb20gJy4vbGlzdC1wcmltaXRpdmUtdmVyc2lvbnMubWpzJztcbmV4cG9ydCB7IGdldERlcGxveW1lbnRTdGF0dXMgfSBmcm9tICcuL2dldC1kZXBsb3ltZW50LXN0YXR1cy5tanMnO1xuZXhwb3J0IHsgdGFpbExvZ3MgfSBmcm9tICcuL3RhaWwtbG9ncy5tanMnO1xuLy8gY2hlY2tfZHJpZnQgcmVtb3ZlZCBpbiB2MS4yNSBcdTIwMTQgdXNlIEJhc2gobHVhIHN5bmMgLS1jaGVjaykgZGlyZWN0bHkuIFRoZVxuLy8gTUNQIHNlcnZlciBpcyBsb2NhbCBhbmQgc28gaXMgdGhlIENMSTsgcmVpbXBsZW1lbnRpbmcgZHJpZnQgZGV0ZWN0aW9uXG4vLyBzZXJ2ZXItc2lkZSBvciBpbiB0aGUgTUNQIHdvdWxkIGR1cGxpY2F0ZSBsb2dpYyB0aGF0IGx1YS1jbGkgYWxyZWFkeSBvd25zLlxuIiwgIi8vIFNoYXJlZCBzdWJwcm9jZXNzIGhlbHBlciBmb3IgdG9vbHMgdGhhdCBzaGVsbCBvdXQgdG8gdGhlIGx1YS1jbGkgYmluYXJ5LlxuLy9cbi8vIFdoeSBzaGVsbCBvdXQgYXQgYWxsOiB0aGVyZSBpcyBubyBcImxpc3QgZXZlcnkgYWdlbnQgdGhpcyBjcmVkZW50aWFsIGNhblxuLy8gcmVhY2hcIiBSRVNUIHJvdXRlIFx1MjAxNCBgbHVhIGFnZW50cyAtLWpzb25gIGFscmVhZHkgZG9lcyB0aGUgY3JlZGVudGlhbC10by1cbi8vIGF1dGhvcml6YXRpb24tcHJvamVjdGlvbiByZXNvbHV0aW9uIChQUk8tMTcxMikgZm9yIGV2ZXJ5IGNyZWRlbnRpYWwgY2xhc3Ncbi8vIChBUEkga2V5LCB0eXBlZCBzY29wZWQga2V5LCByZW5ld2FibGUgc2Vzc2lvbikuIFJlaW1wbGVtZW50aW5nIHRoYXQgaW4gdGhlXG4vLyBNQ1Agd291bGQgZHVwbGljYXRlIGxvZ2ljIGx1YS1jbGkgb3ducy5cblxuaW1wb3J0IHsgc3Bhd24gfSBmcm9tICdub2RlOmNoaWxkX3Byb2Nlc3MnO1xuXG4vLyBgbHVhIGFnZW50c2Agd2Fsa3MgZXZlcnkgb3JnYW5pc2F0aW9uIHRoZSBjcmVkZW50aWFsIGNhbiBzZWUgYW5kLCBmb3IgYVxuLy8gc2Vzc2lvbiBsb2dpbiwgcmVmcmVzaGVzIHRoZSBzZXNzaW9uIGZpcnN0LiBNZWFzdXJlZCAyMDI2LTA5LTEyIG9uIGFcbi8vIDE0NC1vcmcgYWNjb3VudDogMThcdTIwMTMyNSBzLiBUaGUgYnVkZ2V0IG11c3QgY292ZXIgdGhhdC5cbmNvbnN0IERFRkFVTFRfVElNRU9VVF9NUyA9IDYwXzAwMDtcblxuLyoqXG4gKiBSdW4gYGx1YSA8YXJncz5gIGFuZCByZXNvbHZlIHdpdGggaXRzIHN0ZG91dC5cbiAqXG4gKiBPbiBXaW5kb3dzIHRoZSBucG0gYmluIGlzIGEgYGx1YS5jbWRgIHNoaW0sIHdoaWNoIE5vZGUgKFx1MjI2NTE4LjIwIC8gXHUyMjY1MjAuMTIpXG4gKiByZWZ1c2VzIHRvIHNwYXduIHdpdGhvdXQgYSBzaGVsbDsgdGhlIGFyZ3MgYXJlIGZpeGVkIGxpdGVyYWxzIChubyB1c2VyXG4gKiBpbnB1dCksIHNvIGBzaGVsbDogdHJ1ZWAgdGhlcmUgaXMgc2FmZS5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ1tdfSBhcmdzXG4gKiBAcGFyYW0ge3tzcGF3bkZuPzogdHlwZW9mIHNwYXduLCB0aW1lb3V0TXM/OiBudW1iZXIsIGxhYmVsPzogc3RyaW5nfX0gW29wdHNdXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fVxuICovXG5leHBvcnQgZnVuY3Rpb24gcnVuTHVhKGFyZ3MsIHsgc3Bhd25GbiA9IHNwYXduLCB0aW1lb3V0TXMgPSBERUZBVUxUX1RJTUVPVVRfTVMsIGxhYmVsID0gJ2x1YScgfSA9IHt9KSB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3QgY2hpbGQgPSBzcGF3bkZuKCdsdWEnLCBhcmdzLCB7XG4gICAgICBzaGVsbDogcHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJyxcbiAgICAgIHdpbmRvd3NIaWRlOiB0cnVlLFxuICAgICAgZW52OiB7IC4uLnByb2Nlc3MuZW52LCBMVUFfTk9fQkFOTkVSOiAnMScsIExVQV9OT19ISU5UUzogJzEnIH0sXG4gICAgfSk7XG4gICAgbGV0IHN0ZG91dCA9ICcnO1xuICAgIGxldCBzdGRlcnIgPSAnJztcbiAgICBjaGlsZC5zdGRvdXQub24oJ2RhdGEnLCAoZCkgPT4geyBzdGRvdXQgKz0gZC50b1N0cmluZygndXRmOCcpOyB9KTtcbiAgICBjaGlsZC5zdGRlcnIub24oJ2RhdGEnLCAoZCkgPT4geyBzdGRlcnIgKz0gZC50b1N0cmluZygndXRmOCcpOyB9KTtcblxuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBjaGlsZC5raWxsKCdTSUdURVJNJyk7XG4gICAgICByZWplY3QobmV3IEVycm9yKGAke2xhYmVsfTogJ2x1YSAke2FyZ3Muam9pbignICcpfScgdGltZWQgb3V0IGFmdGVyICR7dGltZW91dE1zfW1zYCkpO1xuICAgIH0sIHRpbWVvdXRNcyk7XG5cbiAgICBjaGlsZC5vbignZXhpdCcsIChjb2RlKSA9PiB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgICAgaWYgKGNvZGUgIT09IDApIHJlamVjdChuZXcgRXJyb3IoYCdsdWEgJHthcmdzLmpvaW4oJyAnKX0nIGV4aXRlZCAke2NvZGV9OiAke3N0ZGVyci50cmltKCl9YCkpO1xuICAgICAgZWxzZSByZXNvbHZlKHN0ZG91dCk7XG4gICAgfSk7XG4gICAgY2hpbGQub24oJ2Vycm9yJywgKGVycikgPT4ge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgIHJlamVjdChlcnIpO1xuICAgIH0pO1xuICB9KTtcbn1cblxuLyoqXG4gKiBGbGF0dGVuIGBsdWEgYWdlbnRzIC0tanNvbmAgb3V0cHV0IChsdWEtY2xpIFx1MjI2NSAzLjI4OiBhbiBhcnJheSBvZlxuICogQ3JlZGVudGlhbE9yZ2FuaXphdGlvbiByb3dzIFx1MjAxNCBgeyBvcmdJZCwgbmFtZT8sIGFyY2hpdmVkLCBkaXNjb3ZlcmVkVmlhLFxuICogYWdlbnRzOiBbeyBhZ2VudElkLCBuYW1lPywgdmlzaWJpbGl0eSwgZGlzcGxheVJvbGVzIH1dIH1gLCBzZWVcbiAqIHBhY2thZ2VzL2x1YS1jbGkvc3JjL3NlcnZpY2VzL2NyZWRlbnRpYWwtb3BlcmF0aW9uYWwtY29udGV4dC50cykgaW50byBhXG4gKiBjb21wYWN0IGFnZW50IGxpc3QuIE9sZGVyIGZsYXQgc2hhcGVzIGFyZSB0b2xlcmF0ZWQgZm9yIGZvcndhcmQtY29tcGF0LlxuICpcbiAqIEBwYXJhbSB7dW5rbm93bn0gcGFyc2VkXG4gKiBAcmV0dXJucyB7QXJyYXk8e2lkOiBzdHJpbmcsIG5hbWU6IHN0cmluZ3xudWxsLCBvcmdJZDogc3RyaW5nfG51bGwsIG9yZ05hbWU6IHN0cmluZ3xudWxsLCB2aXNpYmlsaXR5OiBzdHJpbmd8bnVsbH0+fVxuICovXG5leHBvcnQgZnVuY3Rpb24gZmxhdHRlbkFnZW50cyhwYXJzZWQpIHtcbiAgY29uc3Qgb2JqID0gLyoqIEB0eXBlIHthbnl9ICovIChwYXJzZWQpO1xuICBjb25zdCBlbnRyaWVzID0gQXJyYXkuaXNBcnJheShvYmopID8gb2JqIDogKG9iaj8uYWdlbnRzID8/IG9iaj8ub3JncyA/PyBbXSk7XG4gIGNvbnN0IGxvb2tzTGlrZU9yZ3MgPSBlbnRyaWVzLmxlbmd0aCA+IDAgJiYgZW50cmllc1swXSAmJiBBcnJheS5pc0FycmF5KGVudHJpZXNbMF0uYWdlbnRzKTtcblxuICBpZiAobG9va3NMaWtlT3Jncykge1xuICAgIHJldHVybiBlbnRyaWVzLmZsYXRNYXAoKG9yZykgPT4gKG9yZy5hZ2VudHMgPz8gW10pLm1hcCgoYSkgPT4gKHtcbiAgICAgIGlkOiBhLmFnZW50SWQgPz8gYS5pZCA/PyBhLl9pZCxcbiAgICAgIG5hbWU6IGEubmFtZSA/PyBudWxsLFxuICAgICAgb3JnSWQ6IG9yZy5vcmdJZCA/PyBvcmcuaWQgPz8gb3JnLl9pZCA/PyBudWxsLFxuICAgICAgb3JnTmFtZTogb3JnLm5hbWUgPz8gb3JnLnJlZ2lzdGVyZWROYW1lID8/IG51bGwsXG4gICAgICB2aXNpYmlsaXR5OiBhLnZpc2liaWxpdHkgPz8gbnVsbCxcbiAgICB9KSkpO1xuICB9XG4gIHJldHVybiBlbnRyaWVzLm1hcCgoYSkgPT4gKHtcbiAgICBpZDogYS5hZ2VudElkID8/IGEuaWQgPz8gYS5faWQsXG4gICAgbmFtZTogYS5uYW1lID8/IG51bGwsXG4gICAgb3JnSWQ6IGEub3JnSWQgPz8gbnVsbCxcbiAgICBvcmdOYW1lOiBhLm9yZ05hbWUgPz8gbnVsbCxcbiAgICB2aXNpYmlsaXR5OiBhLnZpc2liaWxpdHkgPz8gbnVsbCxcbiAgfSkpO1xufVxuXG4vKipcbiAqIFJ1biBgbHVhIGFnZW50cyAtLWpzb24gLS1jaWAgYW5kIHJldHVybiB0aGUgZmxhdHRlbmVkIGFnZW50IGxpc3QuXG4gKlxuICogQHBhcmFtIHt7c3Bhd25Gbj86IHR5cGVvZiBzcGF3biwgbGFiZWw/OiBzdHJpbmd9fSBbb3B0c11cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxpc3RBZ2VudHNWaWFDbGkoeyBzcGF3bkZuLCBsYWJlbCA9ICdsaXN0X2FnZW50cycgfSA9IHt9KSB7XG4gIGNvbnN0IHN0ZG91dCA9IGF3YWl0IHJ1bkx1YShbJ2FnZW50cycsICctLWpzb24nLCAnLS1jaSddLCB7IHNwYXduRm4sIGxhYmVsIH0pO1xuICBsZXQgcGFyc2VkO1xuICB0cnkge1xuICAgIHBhcnNlZCA9IEpTT04ucGFyc2Uoc3Rkb3V0KTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGAke2xhYmVsfTogY291bGQgbm90IHBhcnNlICdsdWEgYWdlbnRzIC0tanNvbicgb3V0cHV0OiAke2Vyci5tZXNzYWdlfWApO1xuICB9XG4gIHJldHVybiBmbGF0dGVuQWdlbnRzKHBhcnNlZCk7XG59XG4iLCAiaW1wb3J0IHsgbGlzdEFnZW50c1ZpYUNsaSB9IGZyb20gJy4uL3J1bi1sdWEubWpzJztcblxuLy8gU2hlbGxzIG91dCB0byBgbHVhIGFnZW50cyAtLWpzb24gLS1jaWAgKHNlZSBydW4tbHVhLm1qcyBmb3Igd2h5KS4gVGhlIENMSVxuLy8gZW1pdHMgb25lIHJvdyBwZXIgb3JnYW5pc2F0aW9uIHdpdGggYSBuZXN0ZWQgYGFnZW50c2AgYXJyYXk7IHdlIGZsYXR0ZW4uXG5cbmV4cG9ydCBjb25zdCBsaXN0QWdlbnRzID0ge1xuICBzcGVjOiB7XG4gICAgbmFtZTogJ2xpc3RfYWdlbnRzJyxcbiAgICBkZXNjcmlwdGlvbjogJ0xpc3QgYWxsIEx1YSBhZ2VudHMgdGhlIGF1dGhlbnRpY2F0ZWQgY3JlZGVudGlhbCBjYW4gcmVhY2guIFJldHVybnMgYSBjb21wYWN0IGFycmF5IG9mIHtpZCwgbmFtZSwgb3JnSWQsIG9yZ05hbWUsIHZpc2liaWxpdHl9IGZsYXR0ZW5lZCBhY3Jvc3MgYWxsIG9yZ2FuaXNhdGlvbnMuIFNhbWUgZGF0YSBhcyBgbHVhIGFnZW50cyAtLWpzb25gIFx1MjAxNCBpdCByZXNvbHZlcyBldmVyeSBvcmdhbmlzYXRpb24sIHNvIG9uIGFjY291bnRzIHdpdGggbWFueSBvcmdzIHRoaXMgdGFrZXMgMjAgcyBvciBtb3JlOyBwcmVmZXIgZ2V0X2FnZW50IHdoZW4geW91IGFscmVhZHkga25vdyB0aGUgaWQuJyxcbiAgICBpbnB1dFNjaGVtYToge1xuICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICBwcm9wZXJ0aWVzOiB7fSxcbiAgICAgIHJlcXVpcmVkOiBbXSxcbiAgICB9LFxuICB9LFxuICBhc3luYyBoYW5kbGVyKF9hcmdzLCBkZXBzID0ge30pIHtcbiAgICBjb25zdCBjb21wYWN0ID0gYXdhaXQgbGlzdEFnZW50c1ZpYUNsaSh7IHNwYXduRm46IGRlcHMuc3Bhd25GbiwgbGFiZWw6ICdsaXN0X2FnZW50cycgfSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGNvbnRlbnQ6IFt7IHR5cGU6ICd0ZXh0JywgdGV4dDogSlNPTi5zdHJpbmdpZnkoY29tcGFjdCwgbnVsbCwgMikgfV0sXG4gICAgfTtcbiAgfSxcbn07XG4iLCAiaW1wb3J0IHsgbGlzdEFnZW50c1ZpYUNsaSB9IGZyb20gJy4uL3J1bi1sdWEubWpzJztcblxuLy8gVGhlcmUgaXMgbm8gY29uc29saWRhdGVkIGBHRVQgL2RldmVsb3Blci9hZ2VudHMvOmFnZW50SWRgIHJvdXRlLCBhbmQgdGhlXG4vLyBwdWJsaWMgYC9wdWJsaWMvYWdlbnRzLzphZ2VudElkYCByb3V0ZSBpcyBvcmlnaW4tZ3VhcmRlZCAoYnJvd3NlciBvbmx5KS5cbi8vIFRoZSBhZ2VudCdzIGlkZW50aXR5IChpZCwgbmFtZSwgb3JnLCB2aXNpYmlsaXR5KSBJUyBpbiBgbHVhIGFnZW50cyAtLWpzb25gLFxuLy8gc28gd2Ugc2hlbGwgb3V0IGFuZCBmaWx0ZXIgXHUyMDE0IHNhbWUgcGF0aCBhcyBsaXN0X2FnZW50cy4gUGVyc29uYSwgbW9kZWwgYW5kXG4vLyBlbnYgbGl2ZSBiZWhpbmQgc2VwYXJhdGUgcGVyLXJlc291cmNlIHJvdXRlczsgdXNlIHRoZSBkZWRpY2F0ZWQgdG9vbHMgKG9yXG4vLyBgbHVhIHN0YXR1cyAtLWpzb25gIGluc2lkZSB0aGUgcHJvamVjdCkgZm9yIHRob3NlLlxuXG5leHBvcnQgY29uc3QgZ2V0QWdlbnQgPSB7XG4gIHNwZWM6IHtcbiAgICBuYW1lOiAnZ2V0X2FnZW50JyxcbiAgICBkZXNjcmlwdGlvbjogJ0xvb2sgdXAgb25lIEx1YSBhZ2VudCBieSBJRCBhbmQgcmV0dXJuIHtpZCwgbmFtZSwgb3JnSWQsIG9yZ05hbWUsIHZpc2liaWxpdHl9LiBGb3Igd2hhdCBpcyBsaXZlIHVzZSBnZXRfZGVwbG95bWVudF9zdGF0dXMgLyBsaXN0X3ByaW1pdGl2ZV92ZXJzaW9uczsgZm9yIGxvZ3MgdXNlIHRhaWxfbG9ncy4nLFxuICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgYWdlbnRJZDogeyB0eXBlOiAnc3RyaW5nJywgZGVzY3JpcHRpb246ICdBZ2VudCBJRCAodGhlIGBpZGAgZmllbGQgZnJvbSBsaXN0X2FnZW50czsgYWxzbyBgYWdlbnQuYWdlbnRJZGAgaW4gbHVhLnNraWxsLnlhbWwpJyB9LFxuICAgICAgfSxcbiAgICAgIHJlcXVpcmVkOiBbJ2FnZW50SWQnXSxcbiAgICB9LFxuICB9LFxuICBhc3luYyBoYW5kbGVyKHsgYWdlbnRJZCB9LCBkZXBzID0ge30pIHtcbiAgICBpZiAoIWFnZW50SWQpIHRocm93IG5ldyBFcnJvcignYWdlbnRJZCBpcyByZXF1aXJlZCcpO1xuICAgIGNvbnN0IGFnZW50cyA9IGF3YWl0IGxpc3RBZ2VudHNWaWFDbGkoeyBzcGF3bkZuOiBkZXBzLnNwYXduRm4sIGxhYmVsOiAnZ2V0X2FnZW50JyB9KTtcbiAgICBjb25zdCBtYXRjaCA9IGFnZW50cy5maW5kKChhKSA9PiBhLmlkID09PSBhZ2VudElkKSA/PyBudWxsO1xuXG4gICAgaWYgKCFtYXRjaCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBnZXRfYWdlbnQ6IG5vIGFnZW50IHdpdGggaWQgXCIke2FnZW50SWR9XCIgZm91bmQgaW4gdGhlIGF1dGhlbnRpY2F0ZWQgY3JlZGVudGlhbCdzIGFjY2Vzc2libGUgb3Jncy4gUnVuIGxpc3RfYWdlbnRzIHRvIHNlZSBhdmFpbGFibGUgYWdlbnRzLmApO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6IEpTT04uc3RyaW5naWZ5KG1hdGNoLCBudWxsLCAyKSB9XSxcbiAgICB9O1xuICB9LFxufTtcbiIsICIvLyBDcmVkZW50aWFsIHJlc29sdXRpb24gZm9yIHRoZSBNQ1Agc2VydmVyLiBSZXNvbHZlZCBvbiBFVkVSWSBjYWxsLCBub3QgYXRcbi8vIHNlcnZlciBzdGFydHVwIFx1MjAxNCBzdXBwb3J0cyB0aGUgZmxvdyB3aGVyZSBhIHVzZXIgcmUtcnVucyBgbHVhIGF1dGggY29uZmlndXJlYFxuLy8gbWlkLXNlc3Npb24uXG4vL1xuLy8gVGhlIGNoYWluIE1VU1QgbWF0Y2ggbHVhLWNsaSdzIGByZXNvbHZlUmVxdWVzdENyZWRlbnRpYWwoKWBcbi8vIChwYWNrYWdlcy9sdWEtY2xpL3NyYy9zZXJ2aWNlcy9yZXF1ZXN0LWNyZWRlbnRpYWwudHMsIGx1YS1jbGkgMy4zMy4wKTpcbi8vXG4vLyAgIDEuIExVQV9BUElfS0VZIGluIHRoZSBwcm9jZXNzIGVudmlyb25tZW50LiBsdWEtY2xpIGxvYWRzIGAuLy5lbnZgIGludG9cbi8vICAgICAgcHJvY2Vzcy5lbnYgZmlyc3QgKGBpbXBvcnQgJ2RvdGVudi9jb25maWcnYCksIHNvIGEgYC5lbnZgIHZhbHVlIGluIHRoZVxuLy8gICAgICB3b3JraW5nIGRpcmVjdG9yeSBpcyBwYXJ0IG9mIHRoaXMgdGllciBcdTIwMTQgaXQgYmVhdHMgYSBzdG9yZWQgc2Vzc2lvbi5cbi8vICAgMi4gVGhlIHJlbmV3YWJsZSBmaXJzdC1wYXJ0eSBzZXNzaW9uIGBsdWEgYXV0aCBjb25maWd1cmVgIChlbWFpbCArIE9UUClcbi8vICAgICAgd3JpdGVzIHRvIH4vLmx1YS1jbGkvc2Vzc2lvbnMvPGVudi1oYXNoPi5qc29uLiBUaGlzIGlzIHRoZSBERUZBVUxUXG4vLyAgICAgIGxvZ2luIHNpbmNlIGx1YS1jbGkgMy4yOSBhbmQgaXQgREVMRVRFUyB+Ly5sdWEtY2xpL2NyZWRlbnRpYWxzLCBzbyBhblxuLy8gICAgICBNQ1Agc2VydmVyIHRoYXQgb25seSByZWFkIHRoZSBjcmVkZW50aWFscyBmaWxlIGZhaWxlZCBmb3IgZXZlcnlcbi8vICAgICAgc2Vzc2lvbiB1c2VyLiBUaGUgZmlsZSBob2xkcyBhIEZpcmViYXNlIHJlZnJlc2ggdG9rZW47IHRoZSBiZWFyZXIgaXNcbi8vICAgICAgYSBzaG9ydC1saXZlZCBJRCB0b2tlbiBvYnRhaW5lZCBmcm9tIEdvb2dsZSdzIHNlY3VyZXRva2VuIGVuZHBvaW50LlxuLy8gICAzLiB+Ly5sdWEtY2xpL2NyZWRlbnRpYWxzIFx1MjAxNCBhIHBsYWluLXRleHQgQVBJIGtleSAodHlwZWQgYGFwaV88dXVpZD4uPDQzPmBcbi8vICAgICAgb3IgbGVnYWN5KSwgd3JpdHRlbiB3aGVuIHRoZSB1c2VyIHBpY2tzIHRoZSBBUEkta2V5IG9wdGlvbiBvZlxuLy8gICAgICBgbHVhIGF1dGggY29uZmlndXJlYC5cbi8vXG4vLyBUaGlzIGZpbGUgZGVsaWJlcmF0ZWx5IGR1cGxpY2F0ZXMgbGliL2NyZWRlbnRpYWxzLm1qcyBiZWNhdXNlIHRoZSBNQ1Bcbi8vIHNlcnZlciBpcyBhIHNlcGFyYXRlIG5wbSBwYWNrYWdlIGFuZCBjYW4ndCByZWFjaCBpbnRvIHRoZSBwbHVnaW4ncyBsaWIvLlxuXG5pbXBvcnQgeyByZWFkRmlsZSwgcmVhZGRpciwgc3RhdCB9IGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHsgam9pbiwgZGlybmFtZSB9IGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBob21lZGlyIH0gZnJvbSAnbm9kZTpvcyc7XG5cbmNvbnN0IERFRkFVTFRfQVBJX1VSTCA9ICdodHRwczovL2FwaS5oZXlsdWEuYWknO1xuY29uc3QgRklSRUJBU0VfUkVGUkVTSF9VUkwgPSAnaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGVhcGlzLmNvbS92MS90b2tlbic7XG5jb25zdCBSRUZSRVNIX1RJTUVPVVRfTVMgPSAxNV8wMDA7XG5jb25zdCBSRUZSRVNIX1NLRVdfTVMgPSA2MF8wMDA7XG5cbi8vIEdvb2dsZSdzIHZlcmRpY3RzIHRoYXQgdGhlIHN0b3JlZCByZWZyZXNoIHRva2VuIGNhbiBuZXZlciB3b3JrIGFnYWluLlxuLy8gTWlycm9ycyBsdWEtY2xpJ3MgUkVKRUNURURfUkVGUkVTSF9SRUFTT05TIChzZXJ2aWNlcy9maXJlYmFzZS1zZXNzaW9uLnRzKS5cbmNvbnN0IFJFSkVDVEVEX1JFRlJFU0hfUkVBU09OUyA9IFtcbiAgJ1RPS0VOX0VYUElSRUQnLFxuICAnVVNFUl9ESVNBQkxFRCcsXG4gICdVU0VSX05PVF9GT1VORCcsXG4gICdJTlZBTElEX1JFRlJFU0hfVE9LRU4nLFxuICAnSU5WQUxJRF9HUkFOVF9UWVBFJyxcbiAgJ01JU1NJTkdfUkVGUkVTSF9UT0tFTicsXG5dO1xuXG4vLyBJbi1wcm9jZXNzIGNhY2hlIG9mIHJlZnJlc2hlZCBJRCB0b2tlbnMsIGtleWVkIGJ5IHNlc3Npb24gZmlsZSBwYXRoLlxuY29uc3QgdG9rZW5DYWNoZSA9IG5ldyBNYXAoKTtcblxuLyoqIEV4cG9ydGVkIGZvciB0ZXN0cy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjbGVhclRva2VuQ2FjaGUoKSB7XG4gIHRva2VuQ2FjaGUuY2xlYXIoKTtcbn1cblxuLyoqXG4gKiBQYXJzZSBhIGRvdGVudi1zdHlsZSBmaWxlIGZvciBMVUFfQVBJX0tFWSB0aGUgd2F5IGRvdGVudiBkb2VzOiBvcHRpb25hbFxuICogYGV4cG9ydCBgIHByZWZpeCwgc3Vycm91bmRpbmcgc2luZ2xlL2RvdWJsZSBxdW90ZXMgc3RyaXBwZWQsIGEgdHJhaWxpbmdcbiAqIGAgIyBjb21tZW50YCBkcm9wcGVkIGZyb20gdW5xdW90ZWQgdmFsdWVzLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBjb250ZW50XG4gKiBAcmV0dXJucyB7c3RyaW5nfG51bGx9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZURvdGVudkFwaUtleShjb250ZW50KSB7XG4gIGZvciAoY29uc3QgcmF3TGluZSBvZiBjb250ZW50LnNwbGl0KC9cXHI/XFxuLykpIHtcbiAgICBjb25zdCBtID0gcmF3TGluZS5tYXRjaCgvXlxccyooPzpleHBvcnRcXHMrKT9MVUFfQVBJX0tFWVxccyo9XFxzKiguKikkLyk7XG4gICAgaWYgKCFtKSBjb250aW51ZTtcbiAgICBsZXQgdmFsdWUgPSBtWzFdLnRyaW0oKTtcbiAgICBjb25zdCBxdW90ZWQgPSB2YWx1ZS5tYXRjaCgvXihbJ1wiYF0pKC4qPylcXDEoPzpcXHMrIy4qKT9cXHMqJC8pO1xuICAgIGlmIChxdW90ZWQpIHZhbHVlID0gcXVvdGVkWzJdO1xuICAgIGVsc2UgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKC9cXHMrIy4qJC8sICcnKS50cmltKCk7XG4gICAgcmV0dXJuIHZhbHVlIHx8IG51bGw7XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogTG9jYXRlIHRoZSBsdWEtY2xpIHNlc3Npb24gZmlsZSBmb3IgdGhlIEFQSSBiYXNlIFVSTCBpbiB1c2UuIGx1YS1jbGkgbmFtZXNcbiAqIHRoZSBmaWxlIGJ5IGEgaGFzaCBvZiAoYXBpVXJsLCBhdXRoVXJsLCBmaXJlYmFzZVdlYkFwaUtleSk7IHJhdGhlciB0aGFuXG4gKiByZWNvbXB1dGUgaXQgd2Ugc2NhbiB0aGUgZGlyZWN0b3J5IGZvciBhIGBmaXJlYmFzZS1zZXNzaW9uYCByZWNvcmQgd2hvc2VcbiAqIGBhcGlVcmxgIG1hdGNoZXMsIG5ld2VzdCBmaXJzdC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx7cGF0aDogc3RyaW5nLCBzZXNzaW9uOiBvYmplY3R9fG51bGw+fVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmluZFNlc3Npb25GaWxlKHsgc2Vzc2lvbnNEaXIsIGFwaVVybCB9KSB7XG4gIGxldCBuYW1lcztcbiAgdHJ5IHtcbiAgICBuYW1lcyA9IGF3YWl0IHJlYWRkaXIoc2Vzc2lvbnNEaXIpO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjb25zdCBjYW5kaWRhdGVzID0gW107XG4gIGZvciAoY29uc3QgbmFtZSBvZiBuYW1lcykge1xuICAgIGlmICghbmFtZS5lbmRzV2l0aCgnLmpzb24nKSB8fCBuYW1lLnN0YXJ0c1dpdGgoJy4nKSkgY29udGludWU7XG4gICAgY29uc3QgcGF0aCA9IGpvaW4oc2Vzc2lvbnNEaXIsIG5hbWUpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzZXNzaW9uID0gSlNPTi5wYXJzZShhd2FpdCByZWFkRmlsZShwYXRoLCAndXRmOCcpKTtcbiAgICAgIGlmIChzZXNzaW9uPy5raW5kICE9PSAnZmlyZWJhc2Utc2Vzc2lvbicgfHwgc2Vzc2lvbj8udmVyc2lvbiAhPT0gMSkgY29udGludWU7XG4gICAgICBpZiAodHlwZW9mIHNlc3Npb24ucmVmcmVzaFRva2VuICE9PSAnc3RyaW5nJyB8fCAhc2Vzc2lvbi5yZWZyZXNoVG9rZW4pIGNvbnRpbnVlO1xuICAgICAgaWYgKHR5cGVvZiBzZXNzaW9uLmZpcmViYXNlV2ViQXBpS2V5ICE9PSAnc3RyaW5nJyB8fCAhc2Vzc2lvbi5maXJlYmFzZVdlYkFwaUtleSkgY29udGludWU7XG4gICAgICBpZiAoc2Vzc2lvbi5hcGlVcmwgIT09IGFwaVVybCkgY29udGludWU7XG4gICAgICBjb25zdCB7IG10aW1lTXMgfSA9IGF3YWl0IHN0YXQocGF0aCk7XG4gICAgICBjYW5kaWRhdGVzLnB1c2goeyBwYXRoLCBzZXNzaW9uLCBtdGltZU1zIH0pO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gdW5yZWFkYWJsZSAvIG1hbGZvcm1lZCBcdTIwMTQgc2tpcCwgbHVhLWNsaSB3aWxsIGNvbXBsYWluIG9uIGl0cyBvd25cbiAgICB9XG4gIH1cbiAgaWYgKGNhbmRpZGF0ZXMubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbDtcbiAgY2FuZGlkYXRlcy5zb3J0KChhLCBiKSA9PiBiLm10aW1lTXMgLSBhLm10aW1lTXMpO1xuICByZXR1cm4geyBwYXRoOiBjYW5kaWRhdGVzWzBdLnBhdGgsIHNlc3Npb246IGNhbmRpZGF0ZXNbMF0uc2Vzc2lvbiB9O1xufVxuXG4vKipcbiAqIEV4Y2hhbmdlIHRoZSBzdG9yZWQgcmVmcmVzaCB0b2tlbiBmb3IgYSBGaXJlYmFzZSBJRCB0b2tlbiAodGhlIGJlYXJlclxuICogbHVhLWFwaSBhY2NlcHRzKS4gQ2FjaGVkIGluLXByb2Nlc3MgdW50aWwgNjAgcyBiZWZvcmUgZXhwaXJ5LiBUaGUgc2Vzc2lvblxuICogZmlsZSBpcyBuZXZlciB3cml0dGVuOiBsdWEtY2xpIG93bnMgaXQuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gYmVhcmVyIHRva2VuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWZyZXNoU2Vzc2lvbkJlYXJlcih7IHBhdGgsIHNlc3Npb24sIGZldGNoRm4gPSBnbG9iYWxUaGlzLmZldGNoLCBub3cgPSBEYXRlLm5vdyB9KSB7XG4gIGNvbnN0IGNhY2hlZCA9IHRva2VuQ2FjaGUuZ2V0KHBhdGgpO1xuICBpZiAoY2FjaGVkICYmIGNhY2hlZC5leHBpcmVzQXQgLSBub3coKSA+IFJFRlJFU0hfU0tFV19NUykgcmV0dXJuIGNhY2hlZC5pZFRva2VuO1xuXG4gIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiBjb250cm9sbGVyLmFib3J0KCksIFJFRlJFU0hfVElNRU9VVF9NUyk7XG4gIGxldCByZXM7XG4gIHRyeSB7XG4gICAgcmVzID0gYXdhaXQgZmV0Y2hGbihgJHtGSVJFQkFTRV9SRUZSRVNIX1VSTH0/a2V5PSR7ZW5jb2RlVVJJQ29tcG9uZW50KHNlc3Npb24uZmlyZWJhc2VXZWJBcGlLZXkpfWAsIHtcbiAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcgfSxcbiAgICAgIGJvZHk6IG5ldyBVUkxTZWFyY2hQYXJhbXMoeyBncmFudF90eXBlOiAncmVmcmVzaF90b2tlbicsIHJlZnJlc2hfdG9rZW46IHNlc3Npb24ucmVmcmVzaFRva2VuIH0pLnRvU3RyaW5nKCksXG4gICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgTUNQX0FVVEhfVU5BVkFJTEFCTEU6IGNvdWxkIG5vdCByZWZyZXNoIHRoZSBsdWEtY2xpIHNlc3Npb24gKCR7ZXJyPy5uYW1lID09PSAnQWJvcnRFcnJvcicgPyAndGltZWQgb3V0JyA6IGVyci5tZXNzYWdlfSkuIGAgK1xuICAgICAgJ0NoZWNrIHlvdXIgbmV0d29yaywgb3Igc2V0IExVQV9BUElfS0VZIHRvIGJ5cGFzcyB0aGUgc2Vzc2lvbi4nXG4gICAgKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICB9XG5cbiAgaWYgKCFyZXMub2spIHtcbiAgICBsZXQgcmVhc29uID0gcmVzLnN0YXR1c1RleHQgPz8gU3RyaW5nKHJlcy5zdGF0dXMpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBib2R5ID0gYXdhaXQgcmVzLmpzb24oKTtcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPSBib2R5Py5lcnJvcj8ubWVzc2FnZTtcbiAgICAgIGlmICh0eXBlb2YgbWVzc2FnZSA9PT0gJ3N0cmluZycgJiYgbWVzc2FnZSkgcmVhc29uID0gbWVzc2FnZS5zcGxpdCgnOicpWzBdLnRyaW0oKTtcbiAgICB9IGNhdGNoIHsgLyoga2VlcCBzdGF0dXNUZXh0ICovIH1cbiAgICBjb25zdCByZWplY3RlZCA9IHJlcy5zdGF0dXMgPCA1MDAgJiYgUkVKRUNURURfUkVGUkVTSF9SRUFTT05TLmZpbmQoKHIpID0+IHJlYXNvbi50b1VwcGVyQ2FzZSgpLmluY2x1ZGVzKHIpKTtcbiAgICBpZiAocmVqZWN0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYE1DUF9BVVRIX1NUQUxFOiB5b3VyIGx1YS1jbGkgc2Vzc2lvbiB3YXMgc2lnbmVkIG91dCAoJHtyZWplY3RlZH0pLiBgICtcbiAgICAgICAgJ1NpZ25pbmcgb3V0IG9mIHRoZSBMdWEgZGFzaGJvYXJkLCBkZXNrdG9wIG9yIG1vYmlsZSBhcHAgYWxzbyBlbmRzIENMSSBzZXNzaW9ucy4gJyArXG4gICAgICAgICdSdW4gYGx1YSBhdXRoIGNvbmZpZ3VyZWAgaW4gYSB0ZXJtaW5hbCB0byBzaWduIGluIGFnYWluLidcbiAgICAgICk7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgTUNQX0FVVEhfVU5BVkFJTEFCTEU6IGx1YS1jbGkgc2Vzc2lvbiByZWZyZXNoIGZhaWxlZCAoJHtyZXMuc3RhdHVzfSAke3JlYXNvbn0pLiBSZXRyeSBpbiBhIG1vbWVudC5gKTtcbiAgfVxuXG4gIGNvbnN0IGJvZHkgPSBhd2FpdCByZXMuanNvbigpO1xuICBjb25zdCBpZFRva2VuID0gYm9keT8uaWRfdG9rZW47XG4gIGNvbnN0IGV4cGlyZXNJbiA9IE51bWJlcihib2R5Py5leHBpcmVzX2luKTtcbiAgaWYgKHR5cGVvZiBpZFRva2VuICE9PSAnc3RyaW5nJyB8fCAhaWRUb2tlbiB8fCAhTnVtYmVyLmlzRmluaXRlKGV4cGlyZXNJbikgfHwgZXhwaXJlc0luIDw9IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ01DUF9BVVRIX1VOQVZBSUxBQkxFOiB0aGUgc2Vzc2lvbiByZWZyZXNoIHJldHVybmVkIGFuIGludmFsaWQgdG9rZW4uIFJ1biBgbHVhIGF1dGggY29uZmlndXJlYCBhZ2Fpbi4nKTtcbiAgfVxuICB0b2tlbkNhY2hlLnNldChwYXRoLCB7IGlkVG9rZW4sIGV4cGlyZXNBdDogbm93KCkgKyBleHBpcmVzSW4gKiAxMDAwIH0pO1xuICByZXR1cm4gaWRUb2tlbjtcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBiZWFyZXIgdG9rZW4gZXZlcnkgbHVhLWFwaSByZXF1ZXN0IHNob3VsZCBjYXJyeS5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fVxuICogQHRocm93cyB7RXJyb3J9IE1DUF9BVVRIX1NUQUxFIHdoZW4gbm8gY3JlZGVudGlhbCByZXNvbHZlcyBmcm9tIGFueSB0aWVyXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZXNvbHZlQXBpS2V5KHtcbiAgZW52ID0gcHJvY2Vzcy5lbnYsXG4gIGNyZWRlbnRpYWxzUGF0aCA9IGVudi5MVUFfQ1JFREVOVElBTFNfUEFUSCA/PyBqb2luKGhvbWVkaXIoKSwgJy5sdWEtY2xpJywgJ2NyZWRlbnRpYWxzJyksXG4gIHNlc3Npb25zRGlyID0gZW52LkxVQV9TRVNTSU9OU19ESVIgPz8gam9pbihkaXJuYW1lKGNyZWRlbnRpYWxzUGF0aCksICdzZXNzaW9ucycpLFxuICBjd2QgPSBwcm9jZXNzLmN3ZCgpLFxuICBhcGlVcmwgPSBlbnYuTFVBX0FQSV9VUkwgfHwgREVGQVVMVF9BUElfVVJMLFxuICBmZXRjaEZuID0gZ2xvYmFsVGhpcy5mZXRjaCxcbiAgbm93ID0gRGF0ZS5ub3csXG59ID0ge30pIHtcbiAgLy8gVGllciAxYTogcHJvY2VzcyBlbnZpcm9ubWVudFxuICBpZiAoZW52LkxVQV9BUElfS0VZICYmIGVudi5MVUFfQVBJX0tFWS50cmltKCkpIHJldHVybiBlbnYuTFVBX0FQSV9LRVkudHJpbSgpO1xuXG4gIC8vIFRpZXIgMWI6IC5lbnYgaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5IChsdWEtY2xpIGxvYWRzIGl0IGludG8gcHJvY2Vzcy5lbnZcbiAgLy8gYmVmb3JlIHJlYWRpbmcgTFVBX0FQSV9LRVksIHNvIGl0IHJhbmtzIGFib3ZlIHRoZSBzdG9yZWQgc2Vzc2lvbilcbiAgdHJ5IHtcbiAgICBjb25zdCBmcm9tRG90ZW52ID0gcGFyc2VEb3RlbnZBcGlLZXkoYXdhaXQgcmVhZEZpbGUoam9pbihjd2QsICcuZW52JyksICd1dGY4JykpO1xuICAgIGlmIChmcm9tRG90ZW52KSByZXR1cm4gZnJvbURvdGVudjtcbiAgfSBjYXRjaCB7IC8qIG5vIC5lbnYgXHUyMDE0IGZhbGwgdGhyb3VnaCAqLyB9XG5cbiAgLy8gVGllciAyOiByZW5ld2FibGUgZmlyc3QtcGFydHkgc2Vzc2lvblxuICBjb25zdCBmb3VuZCA9IGF3YWl0IGZpbmRTZXNzaW9uRmlsZSh7IHNlc3Npb25zRGlyLCBhcGlVcmwgfSk7XG4gIGlmIChmb3VuZCkgcmV0dXJuIHJlZnJlc2hTZXNzaW9uQmVhcmVyKHsgLi4uZm91bmQsIGZldGNoRm4sIG5vdyB9KTtcblxuICAvLyBUaWVyIDM6IH4vLmx1YS1jbGkvY3JlZGVudGlhbHMgKHBsYWluIHRleHQgQVBJIGtleSlcbiAgdHJ5IHtcbiAgICBjb25zdCByYXcgPSAoYXdhaXQgcmVhZEZpbGUoY3JlZGVudGlhbHNQYXRoLCAndXRmOCcpKS50cmltKCk7XG4gICAgaWYgKHJhdykge1xuICAgICAgaWYgKHJhdy5zdGFydHNXaXRoKCd7JykpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKHJhdyk7XG4gICAgICAgICAgaWYgKHBhcnNlZD8uYXBpS2V5KSByZXR1cm4gcGFyc2VkLmFwaUtleTtcbiAgICAgICAgfSBjYXRjaCB7IC8qIG5vdCBKU09OIGRlc3BpdGUgbGVhZGluZyBicmFjZSAqLyB9XG4gICAgICB9XG4gICAgICByZXR1cm4gcmF3O1xuICAgIH1cbiAgfSBjYXRjaCB7IC8qIG1pc3NpbmcgLyB1bnJlYWRhYmxlIFx1MjAxNCBmYWxsIHRocm91Z2ggKi8gfVxuXG4gIHRocm93IG5ldyBFcnJvcihcbiAgICAnTUNQX0FVVEhfU1RBTEU6IE5vIGx1YS1jbGkgY3JlZGVudGlhbHMgZm91bmQgaW4gTFVBX0FQSV9LRVksIC5lbnYsIH4vLmx1YS1jbGkvc2Vzc2lvbnMsIG9yIH4vLmx1YS1jbGkvY3JlZGVudGlhbHMuICcgK1xuICAgICdSdW4gYGx1YSBhdXRoIGNvbmZpZ3VyZWAgaW4gYSB0ZXJtaW5hbCAob3IgL2x1YS1hdXRoIGluIENsYXVkZSBDb2RlKS4nXG4gICk7XG59XG4iLCAiLy8gVGhpbiBIVFRQIHdyYXBwZXIgYXJvdW5kIGx1YS1hcGkuIFBlciB0ZWNoIHNwZWMgXHUwMEE3OC5cbi8vXG4vLyBXZSBkbyBOT1QgaW1wb3J0IGx1YS1jbGkncyBIdHRwQ2xpZW50IGNsYXNzZXMgYXQgcnVudGltZSAod291bGQgZm9yY2Vcbi8vIHRoZSBlbnRpcmUgbHVhLWNsaSBwYWNrYWdlIHRvIGJlIGJ1bmRsZWQpLiBJbnN0ZWFkLCB0aGlzIG1pbmltYWwgY2xpZW50XG4vLyBoaXRzIHRoZSBzYW1lIFJFU1QgZW5kcG9pbnRzIGx1YS1jbGkgZG9lcy4gRXZlcnkgcm91dGUsIHF1ZXJ5IHBhcmFtZXRlclxuLy8gYW5kIGVudmVsb3BlIHVzZWQgYnkgdGhlIHRvb2xzIGlzIHZlcmlmaWVkIGFnYWluc3QgcGFja2FnZXMvbHVhLWFwaVxuLy8gKGNvbnRyb2xsZXJzL2RldmVsb3Blci8qKiwgZHRvLyoqKSBcdTIwMTQgc2VlIHJlc3BvbnNlLXNoYXBlcy5tanMuXG5cbmltcG9ydCB7IHJlc29sdmVBcGlLZXkgfSBmcm9tICcuL2F1dGgubWpzJztcblxuY29uc3QgREVGQVVMVF9CQVNFX1VSTCA9ICdodHRwczovL2FwaS5oZXlsdWEuYWknO1xuY29uc3QgREVGQVVMVF9USU1FT1VUX01TID0gMTBfMDAwO1xuXG4vLyBTY29wZSBlYWNoIHJvdXRlIGZhbWlseSB0aGUgdG9vbHMgY2FsbCBhY3R1YWxseSByZXF1aXJlcyBcdTIwMTQgcmVhZCBmcm9tIHRoZVxuLy8gYEBSZXF1aXJlU2NvcGUoLi4uKWAgZGVjb3JhdG9yIG9uIGV2ZXJ5IEdFVCB0aGUgdG9vbHMgaGl0LCB1bmRlclxuLy8gbHVhLWNvcmUtc2VydmljZXMgcGFja2FnZXMvbHVhLWFwaS9zcmMvY29udHJvbGxlcnMvZGV2ZWxvcGVyLzpcbi8vICAgc2tpbGxzL2Jhc2UuY29udHJvbGxlci50cywgd2ViaG9va3MvYmFzZS5jb250cm9sbGVyLnRzLFxuLy8gICBqb2JzL2Jhc2UuY29udHJvbGxlci50cywgdHJpZ2dlcnMvYmFzZS5jb250cm9sbGVyLnRzLFxuLy8gICBwcmVwcm9jZXNzb3JzL2Jhc2UuY29udHJvbGxlci50cywgcG9zdHByb2Nlc3NvcnMvYmFzZS5jb250cm9sbGVyLnRzXG4vLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcdTIxOTIgJ2F1dG9tYXRpb25zOnJlYWQnIChsaXN0ICsgOmlkL3ZlcnNpb25zKVxuLy8gICB3b3JrZmxvd3MvYmFzZS5jb250cm9sbGVyLnRzICAgXHUyMTkyICd3b3JrZmxvd3M6cmVhZCcgICAoR0VUIDphZ2VudElkLCBHRVQgOmFnZW50SWQvOndvcmtmbG93SWQvdmVyc2lvbnMpXG4vLyAgIHBlcnNvbmEvYmFzZS5jb250cm9sbGVyLnRzICAgICBcdTIxOTIgJ2FnZW50czpyZWFkJyAgICAgIChHRVQgOmFnZW50SWQvcGVyc29uYS92ZXJzaW9ucylcbi8vICAgYmFzZS5jb250cm9sbGVyLnRzICAgICAgICAgICAgIFx1MjE5MiAna25vd2xlZGdlOnJlYWQnICAgKEdFVCBhZ2VudHMvOmFnZW50SWQvbG9ncylcbi8vIFdvcmtmbG93cyBhcmUgTk9UIHVuZGVyIGF1dG9tYXRpb25zOnJlYWQgXHUyMDE0IHRoZXkgaGF2ZSB0aGVpciBvd24gc2NvcGUgcGFpclxuLy8gKHdvcmtmbG93czpyZWFkIC8gd29ya2Zsb3dzOndyaXRlKS5cbmV4cG9ydCBjb25zdCBST1VURV9TQ09QRVMgPSBPYmplY3QuZnJlZXplKHtcbiAgc2tpbGxzOiAnYXV0b21hdGlvbnM6cmVhZCcsXG4gIHdlYmhvb2tzOiAnYXV0b21hdGlvbnM6cmVhZCcsXG4gIGpvYnM6ICdhdXRvbWF0aW9uczpyZWFkJyxcbiAgdHJpZ2dlcnM6ICdhdXRvbWF0aW9uczpyZWFkJyxcbiAgcHJlcHJvY2Vzc29yczogJ2F1dG9tYXRpb25zOnJlYWQnLFxuICBwb3N0cHJvY2Vzc29yczogJ2F1dG9tYXRpb25zOnJlYWQnLFxuICB3b3JrZmxvd3M6ICd3b3JrZmxvd3M6cmVhZCcsXG4gIHBlcnNvbmE6ICdhZ2VudHM6cmVhZCcsXG4gIGxvZ3M6ICdrbm93bGVkZ2U6cmVhZCcsXG59KTtcblxuY29uc3QgUk9VVEVfU0NPUEVfU1VNTUFSWSA9XG4gICdza2lsbHMvd2ViaG9va3Mvam9icy90cmlnZ2Vycy9wcmVwcm9jZXNzb3JzL3Bvc3Rwcm9jZXNzb3JzIG5lZWQgYXV0b21hdGlvbnM6cmVhZCwgd29ya2Zsb3dzIG5lZWQgJyArXG4gICd3b3JrZmxvd3M6cmVhZCwgcGVyc29uYSBuZWVkcyBhZ2VudHM6cmVhZCwgbG9ncyBuZWVkIGtub3dsZWRnZTpyZWFkJztcblxuLyoqXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIEFQSSBwYXRoICh3aXRoIGxlYWRpbmcgc2xhc2gpXG4gKiBAcGFyYW0ge3ttZXRob2Q/OiBzdHJpbmcsIGJvZHk/OiBvYmplY3QsIHF1ZXJ5PzogUmVjb3JkPHN0cmluZyxzdHJpbmd8bnVtYmVyPiwgZmV0Y2hGbj86IHR5cGVvZiBmZXRjaCwgYmFzZVVybD86IHN0cmluZywgdGltZW91dE1zPzogbnVtYmVyfX0gW29wdHNdXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhcGlSZXF1ZXN0KHBhdGgsIHtcbiAgbWV0aG9kID0gJ0dFVCcsXG4gIGJvZHksXG4gIHF1ZXJ5LFxuICBmZXRjaEZuID0gZ2xvYmFsVGhpcy5mZXRjaCxcbiAgYmFzZVVybCA9IHByb2Nlc3MuZW52LkxVQV9BUElfVVJMIHx8IERFRkFVTFRfQkFTRV9VUkwsXG4gIHRpbWVvdXRNcyA9IERFRkFVTFRfVElNRU9VVF9NUyxcbn0gPSB7fSkge1xuICBjb25zdCBhcGlLZXkgPSBhd2FpdCByZXNvbHZlQXBpS2V5KCk7XG4gIC8vIEpvaW4gYnkgc3RyaW5nIGNvbmNhdGVuYXRpb24sIE5PVCBgbmV3IFVSTChwYXRoLCBiYXNlVXJsKWA6IHRoZSBXSEFUV0dcbiAgLy8gcmVzb2x2ZXIgdHJlYXRzIGEgbGVhZGluZy1zbGFzaCBwYXRoIGFzIGhvc3QtYWJzb2x1dGUgYW5kIHdvdWxkIGRyb3AgYW55XG4gIC8vIHBhdGggcHJlZml4IG9uIExVQV9BUElfVVJMIChodHRwczovL2hvc3QvYXBpICsgL2RldmVsb3Blci94IFx1MjE5MlxuICAvLyBodHRwczovL2hvc3QvZGV2ZWxvcGVyL3gpLiBgcGF0aGAgYWx3YXlzIHN0YXJ0cyB3aXRoICcvJywgc28gc3RyaXAgb25lXG4gIC8vIHRyYWlsaW5nIHNsYXNoIGZyb20gdGhlIGJhc2UgYW5kIGNvbmNhdGVuYXRlLlxuICBjb25zdCB1cmwgPSBuZXcgVVJMKGJhc2VVcmwucmVwbGFjZSgvXFwvJC8sICcnKSArIHBhdGgpO1xuICBpZiAocXVlcnkpIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKHF1ZXJ5KSkgdXJsLnNlYXJjaFBhcmFtcy5zZXQoaywgU3RyaW5nKHYpKTtcblxuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCB0aW1lb3V0TXMpO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgcmVzID0gYXdhaXQgZmV0Y2hGbih1cmwudG9TdHJpbmcoKSwge1xuICAgICAgbWV0aG9kLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICAnQXV0aG9yaXphdGlvbic6IGBCZWFyZXIgJHthcGlLZXl9YCxcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICAgICAgJ1gtTHVhLUNsaWVudCc6ICdjbGF1ZGUtcGx1Z2luLzEuNC4wJyxcbiAgICAgIH0sXG4gICAgICBib2R5OiBib2R5ID8gSlNPTi5zdHJpbmdpZnkoYm9keSkgOiB1bmRlZmluZWQsXG4gICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pO1xuXG4gICAgaWYgKHJlcy5zdGF0dXMgPT09IDQwMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnTUNQX0FVVEhfU1RBTEU6IGx1YS1hcGkgcmV0dXJuZWQgNDAxLiBUaGUgY3JlZGVudGlhbCBpcyBtaXNzaW5nLCBleHBpcmVkLCBvciB0aGUgc2Vzc2lvbiB3YXMgc2lnbmVkIG91dCAnICtcbiAgICAgICAgJyhzaWduaW5nIG91dCBvZiB0aGUgTHVhIGRhc2hib2FyZCBvciBhcHBzIGFsc28gZW5kcyBDTEkgc2Vzc2lvbnMpLiBSdW4gYGx1YSBhdXRoIGNvbmZpZ3VyZWAgaW4gYSB0ZXJtaW5hbCwgJyArXG4gICAgICAgICdvciByZS1ydW4gL2x1YS1kb2N0b3IuJ1xuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHJlcy5zdGF0dXMgPT09IDQwMykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgTUNQX0ZPUkJJRERFTjogbHVhLWFwaSByZXR1cm5lZCA0MDMgZm9yICR7cGF0aH0uIFRoZSBjcmVkZW50aWFsIGNhbm5vdCBhY2Nlc3MgdGhpcyBhZ2VudCBvciBvcmcsIG9yIGl0IGxhY2tzIGAgK1xuICAgICAgICBgdGhlIHJvdXRlIHNjb3BlICgke1JPVVRFX1NDT1BFX1NVTU1BUll9IFx1MjAxNCB0eXBlZCBrZXlzIGFyZSBzY29wZWQgdG8gdGhlIGFnZW50cyBhbmQgcm9sZSBjaG9zZW4gYXQgYCArXG4gICAgICAgICdgbHVhIGF1dGggY29uZmlndXJlYCkuIFJlLXJ1biAvbHVhLWRvY3RvciBvciBjaGVjayB0aGUgTFVBX0FQSV9LRVkgZW52IHZhci4nXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAoIXJlcy5vaykge1xuICAgICAgLy8gVHJ5IHRvIHBhcnNlIGx1YS1hcGkncyBzdHJ1Y3R1cmVkIGVycm9yIGVudmVsb3BlOlxuICAgICAgLy8gICB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogeyBtZXNzYWdlLCBzdGF0dXNDb2RlLCBjb2RlPywgLi4uIH0gfVxuICAgICAgLy8gRmFsbHMgYmFjayB0byByYXcgdGV4dCBvbiBwYXJzZSBmYWlsdXJlIChlLmcuIHByb3h5IDUwMiB3aXRoIEhUTUwgYm9keSkuXG4gICAgICBjb25zdCByYXcgPSBhd2FpdCByZXMudGV4dCgpO1xuICAgICAgbGV0IGZyaWVuZGx5TXNnID0gcmF3O1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZShyYXcpO1xuICAgICAgICBmcmllbmRseU1zZyA9IHBhcnNlZD8uZXJyb3I/Lm1lc3NhZ2UgPz8gcGFyc2VkPy5tZXNzYWdlID8/IHJhdztcbiAgICAgICAgY29uc3QgY29kZSA9IHBhcnNlZD8uZXJyb3I/LmNvZGU7XG4gICAgICAgIGlmICh0eXBlb2YgY29kZSA9PT0gJ3N0cmluZycgJiYgY29kZSAmJiAhU3RyaW5nKGZyaWVuZGx5TXNnKS5pbmNsdWRlcyhjb2RlKSkgZnJpZW5kbHlNc2cgPSBgJHtjb2RlfTogJHtmcmllbmRseU1zZ31gO1xuICAgICAgfSBjYXRjaCB7IC8qIG5vdCBKU09OOyByYXcgaXMgZmluZSAqLyB9XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYGx1YS1hcGkgJHtyZXMuc3RhdHVzfTogJHtmcmllbmRseU1zZ31gKTtcbiAgICB9XG5cbiAgICByZXR1cm4gYXdhaXQgcmVzLmpzb24oKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgaWYgKGVyci5uYW1lID09PSAnQWJvcnRFcnJvcicpIHRocm93IG5ldyBFcnJvcihgTUNQX1RJTUVPVVQ6ICR7cGF0aH0gZGlkIG5vdCByZXNwb25kIGluICR7dGltZW91dE1zfW1zYCk7XG4gICAgdGhyb3cgZXJyO1xuICB9IGZpbmFsbHkge1xuICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gIH1cbn1cbiIsICIvLyBQZXItdHlwZSByZXNwb25zZS1zaGFwZSBleHRyYWN0b3JzLlxuLy9cbi8vIGx1YS1hcGkncyBkZXZlbG9wZXIgZW5kcG9pbnRzIGhhdmUgSU5DT05TSVNURU5UIHJlc3BvbnNlIHNoYXBlcyBhY3Jvc3Ncbi8vIHByaW1pdGl2ZSB0eXBlcy4gVmVyaWZpZWQgYWdhaW5zdCBwYWNrYWdlcy9sdWEtYXBpL3NyYy9kdG8vKi5kdG8udHMgYW5kXG4vLyBwYWNrYWdlcy9sdWEtYXBpL3NyYy9jb250cm9sbGVycy9kZXZlbG9wZXIvKiovYmFzZS5jb250cm9sbGVyLnRzLCBwbHVzXG4vLyBwYWNrYWdlcy9sdWEtY2xpL3NyYy9pbnRlcmZhY2VzL3dvcmtmbG93cy50cyAobHVhLWNsaSAzLjMzLjApOlxuLy9cbi8vICAgU2tpbGxzICAgICAgICAgR0VUIC9kZXZlbG9wZXIvc2tpbGxzLzphZ2VudElkICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHNraWxsczogU2tpbGxEdG9bXSB9XG4vLyAgICAgICAgICAgICAgICAgIEdFVCAvZGV2ZWxvcGVyL3NraWxscy86YWdlbnRJZC86c2tpbGxJZC92ZXJzaW9ucyAgICBcdTIxOTIgeyB2ZXJzaW9uczogW3sgdmVyc2lvbiwgY3JlYXRlZERhdGUsIGlzQ3VycmVudCwgLi4uIH1dIH1cbi8vICAgV2ViaG9va3MgICAgICAgR0VUIC9kZXZlbG9wZXIvd2ViaG9va3MvOmFnZW50SWQgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHN1Y2Nlc3MsIGRhdGE6IHsgd2ViaG9va3MgfSB9XG4vLyAgICAgICAgICAgICAgICAgIEdFVCAuLi4vOndlYmhvb2tJZC92ZXJzaW9ucyAgICAgICAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyBzdWNjZXNzLCBkYXRhOiB7IHZlcnNpb25zOiBbeyB2ZXJzaW9uLCB3ZWJob29rSWQsIGNyZWF0ZWRBdCwgaXNBY3RpdmUgfV0sIGFjdGl2ZVZlcnNpb25JZD8gfSB9XG4vLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChzZXJ2aWNlcy9kZXZlbG9wZXIud2ViaG9vay5zZXJ2aWNlLnRzIHNldHMgYHdlYmhvb2tJZDogdi5pZGAgXHUyMDE0IHRoZVxuLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyc2lvbiBST1cgaWQgdW5kZXIgYSBtaXNuYW1lZCBrZXk7IHRoZXJlIGlzIG5vIHZlcnNpb25JZC9pZCwgc29cbi8vICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnNpb25JZCgpIGlzIG51bGwgYW5kIHRoZSBlbnZlbG9wZSdzIGFjdGl2ZVZlcnNpb25JZCBpcyB0aGUgb25seSBpZClcbi8vICAgSm9icyAgICAgICAgICAgR0VUIC9kZXZlbG9wZXIvam9icy86YWdlbnRJZCAgICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHN1Y2Nlc3MsIGRhdGE6IHsgam9icyB9IH1cbi8vICAgICAgICAgICAgICAgICAgR0VUIC4uLi86am9iSWQvdmVyc2lvbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHN1Y2Nlc3MsIGRhdGE6IEpvYlZlcnNpb25EdG9bXSB9ICAgXHUyMTkwIGFycmF5IHVuZGVyIGRhdGE7IGBhY3RpdmVgXG4vLyAgIFRyaWdnZXJzICAgICAgIEdFVCAvZGV2ZWxvcGVyL3RyaWdnZXJzLzphZ2VudElkICAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyBzdWNjZXNzLCBkYXRhOiB7IHRyaWdnZXJzIH0gfVxuLy8gICAgICAgICAgICAgICAgICBHRVQgLi4uLzp0cmlnZ2VySWQvdmVyc2lvbnMgICAgICAgICAgICAgICAgICAgICAgICAgXHUyMTkyIHsgc3VjY2VzcywgZGF0YTogeyB2ZXJzaW9uczogW3sgdmVyc2lvbiwgdmVyc2lvbklkLCBjcmVhdGVkQXQsIGlzQWN0aXZlIH1dLCBhY3RpdmVWZXJzaW9uSWQ/IH0gfVxuLy8gICBQcmVwcm9jZXNzb3JzICBHRVQgL2RldmVsb3Blci9wcmVwcm9jZXNzb3JzLzphZ2VudElkICAgICAgICAgICAgICAgXHUyMTkyIHsgc3VjY2VzcywgZGF0YTogeyBwcmVwcm9jZXNzb3JzIH0gfVxuLy8gICAgICAgICAgICAgICAgICBHRVQgLi4uLzpwcmVwcm9jZXNzb3JJZC92ZXJzaW9ucyAgICAgICAgICAgICAgICAgICAgXHUyMTkyIHsgc3VjY2VzcywgZGF0YTogeyB2ZXJzaW9uczogW3sgdmVyc2lvbklkLCB2ZXJzaW9uLCBpc0FjdGl2ZSwgY3JlYXRlZEF0IH1dLCBhY3RpdmVWZXJzaW9uSWQ/IH0gfVxuLy8gICBQb3N0cHJvY2Vzc29ycyBHRVQgL2RldmVsb3Blci9wb3N0cHJvY2Vzc29ycy86YWdlbnRJZCAgICAgICAgICAgICAgXHUyMTkyIHsgc3VjY2VzcywgZGF0YTogeyBwb3N0cHJvY2Vzc29ycyB9IH1cbi8vICAgICAgICAgICAgICAgICAgR0VUIC4uLi86cG9zdHByb2Nlc3NvcklkL3ZlcnNpb25zICAgICAgICAgICAgICAgICAgIFx1MjE5MiBzYW1lIGZhbWlseSBhcyBwcmVwcm9jZXNzb3JzXG4vLyAgIFdvcmtmbG93cyAgICAgIEdFVCAvZGV2ZWxvcGVyL3dvcmtmbG93cy86YWdlbnRJZCAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyBzdWNjZXNzLCBkYXRhOiB7IHdvcmtmbG93cyB9IH0gICAoY29kZS9zY3JpcHQgcHJvamVjdGVkIG91dClcbi8vICAgICAgICAgICAgICAgICAgR0VUIC4uLi86d29ya2Zsb3dJZC92ZXJzaW9ucyAgICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHN1Y2Nlc3MsIGRhdGE6IFdvcmtmbG93VmVyc2lvbltdIH0gICBcdTIxOTAgYXJyYXkgdW5kZXIgZGF0YTsgYGFjdGl2ZWBcbi8vICAgUGVyc29uYSAgICAgICAgR0VUIC9kZXZlbG9wZXIvYWdlbnRzLzphZ2VudElkL3BlcnNvbmEvdmVyc2lvbnMgICAgIFx1MjE5MiB7IHN0YXR1cywgbWVzc2FnZSwgdmVyc2lvbnM6IFt7IHZlcnNpb246IG51bWJlciwgY3JlYXRlZERhdGU6IG51bWJlciwgaXNDdXJyZW50LCBwZXJzb25hIH1dIH1cbi8vXG4vLyBObyB2ZXJzaW9uIERUTyBjYXJyaWVzIGBkZXBsb3llZEF0YCBvciBgc291cmNlSGFzaGAuIFRoZSBcImxpdmVcIiBmbGFnIGlzXG4vLyBgaXNDdXJyZW50YCAoc2tpbGwsIHBlcnNvbmEpLCBgYWN0aXZlYCAoam9iLCB3b3JrZmxvdykgb3IgYGlzQWN0aXZlYCAvXG4vLyBgYWN0aXZlVmVyc2lvbklkYCAod2ViaG9vaywgdHJpZ2dlciwgcHJlL3Bvc3Rwcm9jZXNzb3IpLiBDcmVhdGlvbiB0aW1lIGlzXG4vLyBgY3JlYXRlZEF0YCAoSVNPIHN0cmluZykgZXZlcnl3aGVyZSBleGNlcHQgc2tpbGwvcGVyc29uYSAoYGNyZWF0ZWREYXRlYDtcbi8vIHBlcnNvbmEncyBpcyBhbiBlcG9jaC1tcyBudW1iZXIpLlxuXG5jb25zdCBMSVNUX0VYVFJBQ1RPUlMgPSB7XG4gIHNraWxsOiAgICAgICAgIChyKSA9PiByPy5za2lsbHMgPz8gW10sXG4gIHdlYmhvb2s6ICAgICAgIChyKSA9PiByPy5kYXRhPy53ZWJob29rcyA/PyBbXSxcbiAgam9iOiAgICAgICAgICAgKHIpID0+IHI/LmRhdGE/LmpvYnMgPz8gW10sXG4gIHRyaWdnZXI6ICAgICAgIChyKSA9PiByPy5kYXRhPy50cmlnZ2VycyA/PyBbXSxcbiAgcHJlcHJvY2Vzc29yOiAgKHIpID0+IHI/LmRhdGE/LnByZXByb2Nlc3NvcnMgPz8gW10sXG4gIHBvc3Rwcm9jZXNzb3I6IChyKSA9PiByPy5kYXRhPy5wb3N0cHJvY2Vzc29ycyA/PyBbXSxcbiAgd29ya2Zsb3c6ICAgICAgKHIpID0+IHI/LmRhdGE/LndvcmtmbG93cyA/PyBbXSxcbn07XG5cbmNvbnN0IFZFUlNJT05TX0VYVFJBQ1RPUlMgPSB7XG4gIHNraWxsOiAgICAgICAgIChyKSA9PiByPy52ZXJzaW9ucyA/PyBbXSxcbiAgd2ViaG9vazogICAgICAgKHIpID0+IHI/LmRhdGE/LnZlcnNpb25zID8/IFtdLFxuICBqb2I6ICAgICAgICAgICAocikgPT4gKEFycmF5LmlzQXJyYXkocj8uZGF0YSkgPyByLmRhdGEgOiBbXSksXG4gIHRyaWdnZXI6ICAgICAgIChyKSA9PiByPy5kYXRhPy52ZXJzaW9ucyA/PyBbXSxcbiAgcHJlcHJvY2Vzc29yOiAgKHIpID0+IHI/LmRhdGE/LnZlcnNpb25zID8/IFtdLFxuICBwb3N0cHJvY2Vzc29yOiAocikgPT4gcj8uZGF0YT8udmVyc2lvbnMgPz8gW10sXG4gIHdvcmtmbG93OiAgICAgIChyKSA9PiAoQXJyYXkuaXNBcnJheShyPy5kYXRhKSA/IHIuZGF0YSA6IFtdKSxcbiAgcGVyc29uYTogICAgICAgKHIpID0+IHI/LnZlcnNpb25zID8/IFtdLFxufTtcblxuLyoqIFVSTCBwYXRoIHNlZ21lbnQgdW5kZXIgL2RldmVsb3Blci8gZm9yIGVhY2ggbGlzdGFibGUgdHlwZS4gKi9cbmV4cG9ydCBjb25zdCBMSVNUX1BBVEhTID0ge1xuICBza2lsbDogJ3NraWxscycsXG4gIHdlYmhvb2s6ICd3ZWJob29rcycsXG4gIGpvYjogJ2pvYnMnLFxuICB0cmlnZ2VyOiAndHJpZ2dlcnMnLFxuICBwcmVwcm9jZXNzb3I6ICdwcmVwcm9jZXNzb3JzJyxcbiAgcG9zdHByb2Nlc3NvcjogJ3Bvc3Rwcm9jZXNzb3JzJyxcbiAgd29ya2Zsb3c6ICd3b3JrZmxvd3MnLFxufTtcblxuLyoqXG4gKiBFeHRyYWN0IGEgbGlzdCBvZiBwcmltaXRpdmVzIGZyb20gYSBsaXN0LWVuZHBvaW50IHJlc3BvbnNlLlxuICogQHRocm93cyBpZiB0aGUgdHlwZSBpcyB1bmtub3duLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdExpc3QodHlwZSwgcmVzcG9uc2UpIHtcbiAgY29uc3QgZm4gPSBMSVNUX0VYVFJBQ1RPUlNbdHlwZV07XG4gIGlmICghZm4pIHRocm93IG5ldyBFcnJvcihgZXh0cmFjdExpc3Q6IHVua25vd24gcHJpbWl0aXZlIHR5cGUgXCIke3R5cGV9XCIuIFZhbGlkOiAke09iamVjdC5rZXlzKExJU1RfRVhUUkFDVE9SUykuam9pbignLCAnKX1gKTtcbiAgcmV0dXJuIGZuKHJlc3BvbnNlKTtcbn1cblxuLyoqXG4gKiBFeHRyYWN0IGEgbGlzdCBvZiB2ZXJzaW9ucyBmcm9tIGEgdmVyc2lvbnMtZW5kcG9pbnQgcmVzcG9uc2UuXG4gKiBAdGhyb3dzIGlmIHRoZSB0eXBlIGlzIHVua25vd24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0VmVyc2lvbnModHlwZSwgcmVzcG9uc2UpIHtcbiAgY29uc3QgZm4gPSBWRVJTSU9OU19FWFRSQUNUT1JTW3R5cGVdO1xuICBpZiAoIWZuKSB0aHJvdyBuZXcgRXJyb3IoYGV4dHJhY3RWZXJzaW9uczogdW5rbm93biBwcmltaXRpdmUgdHlwZSBcIiR7dHlwZX1cIi4gVmFsaWQ6ICR7T2JqZWN0LmtleXMoVkVSU0lPTlNfRVhUUkFDVE9SUykuam9pbignLCAnKX1gKTtcbiAgcmV0dXJuIGZuKHJlc3BvbnNlKTtcbn1cblxuLyoqXG4gKiBUaGUgYGFjdGl2ZVZlcnNpb25JZGAgYSB2ZXJzaW9ucyBlbnZlbG9wZSBtYXkgY2FycnkgYmVzaWRlIGl0cyBhcnJheVxuICogKHdlYmhvb2ssIHRyaWdnZXIsIHByZS9wb3N0cHJvY2Vzc29yKS4gTnVsbCBmb3IgdGhlIG90aGVyIGZhbWlsaWVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdEFjdGl2ZVZlcnNpb25JZChyZXNwb25zZSkge1xuICBjb25zdCBpZCA9IHJlc3BvbnNlPy5kYXRhPy5hY3RpdmVWZXJzaW9uSWQ7XG4gIHJldHVybiB0eXBlb2YgaWQgPT09ICdzdHJpbmcnICYmIGlkID8gaWQgOiBudWxsO1xufVxuXG4vKipcbiAqIElzIHRoaXMgdmVyc2lvbiB0aGUgb25lIHRoYXQgaXMgbGl2ZT8gRWFjaCBmYW1pbHkgc3BlbGxzIGl0IGRpZmZlcmVudGx5LlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlXG4gKiBAcGFyYW0ge29iamVjdH0gdmVyc2lvbiAtIG9uZSBlbGVtZW50IG9mIGV4dHJhY3RWZXJzaW9ucygpXG4gKiBAcGFyYW0ge29iamVjdH0gW3Jlc3BvbnNlXSAtIHRoZSB2ZXJzaW9ucyBlbnZlbG9wZSAoZm9yIGFjdGl2ZVZlcnNpb25JZClcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzQWN0aXZlVmVyc2lvbih0eXBlLCB2ZXJzaW9uLCByZXNwb25zZSkge1xuICBpZiAoIXZlcnNpb24gfHwgdHlwZW9mIHZlcnNpb24gIT09ICdvYmplY3QnKSByZXR1cm4gZmFsc2U7XG4gIHN3aXRjaCAodHlwZSkge1xuICAgIGNhc2UgJ3NraWxsJzpcbiAgICBjYXNlICdwZXJzb25hJzpcbiAgICAgIHJldHVybiB2ZXJzaW9uLmlzQ3VycmVudCA9PT0gdHJ1ZTtcbiAgICBjYXNlICdqb2InOlxuICAgIGNhc2UgJ3dvcmtmbG93JzpcbiAgICAgIHJldHVybiB2ZXJzaW9uLmFjdGl2ZSA9PT0gdHJ1ZTtcbiAgICBjYXNlICd3ZWJob29rJzpcbiAgICBjYXNlICd0cmlnZ2VyJzpcbiAgICBjYXNlICdwcmVwcm9jZXNzb3InOlxuICAgIGNhc2UgJ3Bvc3Rwcm9jZXNzb3InOiB7XG4gICAgICBpZiAodmVyc2lvbi5pc0FjdGl2ZSA9PT0gdHJ1ZSkgcmV0dXJuIHRydWU7XG4gICAgICBjb25zdCBhY3RpdmVJZCA9IGV4dHJhY3RBY3RpdmVWZXJzaW9uSWQocmVzcG9uc2UpO1xuICAgICAgY29uc3Qgb3duSWQgPSB2ZXJzaW9uLnZlcnNpb25JZCA/PyB2ZXJzaW9uLmlkID8/IG51bGw7XG4gICAgICByZXR1cm4gISFhY3RpdmVJZCAmJiBvd25JZCA9PT0gYWN0aXZlSWQ7XG4gICAgfVxuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBJU08tODYwMSBjcmVhdGlvbiB0aW1lIG9mIGEgdmVyc2lvbiwgb3IgbnVsbC4gTm9ybWFsaXNlcyBza2lsbC9wZXJzb25hJ3NcbiAqIGBjcmVhdGVkRGF0ZWAgKHBlcnNvbmE6IGVwb2NoIG1zKSBvbnRvIHRoZSBgY3JlYXRlZEF0YCBuYW1lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmVyc2lvbkNyZWF0ZWRBdCh2ZXJzaW9uKSB7XG4gIGlmICghdmVyc2lvbiB8fCB0eXBlb2YgdmVyc2lvbiAhPT0gJ29iamVjdCcpIHJldHVybiBudWxsO1xuICBjb25zdCByYXcgPSB2ZXJzaW9uLmNyZWF0ZWRBdCA/PyB2ZXJzaW9uLmNyZWF0ZWREYXRlID8/IG51bGw7XG4gIGlmIChyYXcgPT0gbnVsbCkgcmV0dXJuIG51bGw7XG4gIGlmICh0eXBlb2YgcmF3ID09PSAnbnVtYmVyJykgcmV0dXJuIE51bWJlci5pc0Zpbml0ZShyYXcpID8gbmV3IERhdGUocmF3KS50b0lTT1N0cmluZygpIDogbnVsbDtcbiAgcmV0dXJuIFN0cmluZyhyYXcpO1xufVxuXG4vKiogSWRlbnRpZmllciBvZiBhIHZlcnNpb24gcm93ICh0aGUgc2xvdCBsdWEtYXBpIGtleXMgYGFjdGl2ZVZlcnNpb25JZGAgb24pLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZlcnNpb25JZCh2ZXJzaW9uKSB7XG4gIGlmICghdmVyc2lvbiB8fCB0eXBlb2YgdmVyc2lvbiAhPT0gJ29iamVjdCcpIHJldHVybiBudWxsO1xuICByZXR1cm4gdmVyc2lvbi52ZXJzaW9uSWQgPz8gdmVyc2lvbi5pZCA/PyBudWxsO1xufVxuXG5leHBvcnQgY29uc3QgU1VQUE9SVEVEX0xJU1RfVFlQRVMgPSBPYmplY3Qua2V5cyhMSVNUX0VYVFJBQ1RPUlMpO1xuZXhwb3J0IGNvbnN0IFNVUFBPUlRFRF9WRVJTSU9OX1RZUEVTID0gT2JqZWN0LmtleXMoVkVSU0lPTlNfRVhUUkFDVE9SUyk7XG4iLCAiaW1wb3J0IHsgYXBpUmVxdWVzdCB9IGZyb20gJy4uL2FwaS1jbGllbnQubWpzJztcbmltcG9ydCB7XG4gIGV4dHJhY3RMaXN0LFxuICBleHRyYWN0VmVyc2lvbnMsXG4gIGV4dHJhY3RBY3RpdmVWZXJzaW9uSWQsXG4gIGlzQWN0aXZlVmVyc2lvbixcbiAgdmVyc2lvbkNyZWF0ZWRBdCxcbiAgdmVyc2lvbklkLFxuICBMSVNUX1BBVEhTLFxuICBTVVBQT1JURURfVkVSU0lPTl9UWVBFUyxcbn0gZnJvbSAnLi4vcmVzcG9uc2Utc2hhcGVzLm1qcyc7XG5cbmNvbnN0IFZBTElEX1RZUEVTID0gbmV3IFNldChTVVBQT1JURURfVkVSU0lPTl9UWVBFUyk7XG5cbmV4cG9ydCBjb25zdCBsaXN0UHJpbWl0aXZlVmVyc2lvbnMgPSB7XG4gIHNwZWM6IHtcbiAgICBuYW1lOiAnbGlzdF9wcmltaXRpdmVfdmVyc2lvbnMnLFxuICAgIC8vIFdoaWNoIGZhbWlsaWVzIGNhcnJ5IGEgcGVyLWVudHJ5IGlkIFx1MjAxNCB2ZXJpZmllZCBpbiBsdWEtYXBpJ3Mgc2VydmljZVxuICAgIC8vIG1hcHBlcnMgKHBhY2thZ2VzL2x1YS1hcGkvc3JjL3NlcnZpY2VzLyk6XG4gICAgLy8gICBkZXZlbG9wZXIudHJpZ2dlci5zZXJ2aWNlLnRzIGdldFRyaWdnZXJWZXJzaW9ucyAgICAgICAgICAgIFx1MjE5MiB7IHZlcnNpb24sIHZlcnNpb25JZDogdi5pZCwgY3JlYXRlZEF0LCBpc0FjdGl2ZSB9XG4gICAgLy8gICBkZXZlbG9wZXIucHJlcHJvY2Vzc29yLnNlcnZpY2UudHMgZ2V0UHJlUHJvY2Vzc29yVmVyc2lvbnMgIFx1MjE5MiB7IHZlcnNpb25JZDogdi5pZCwgdmVyc2lvbiwgaXNBY3RpdmUsIGNyZWF0ZWRBdCB9XG4gICAgLy8gICBkZXZlbG9wZXIucG9zdHByb2Nlc3Nvci5zZXJ2aWNlLnRzIGdldFBvc3RQcm9jZXNzb3JWZXJzaW9uc1x1MjE5MiBzYW1lIGFzIHByZXByb2Nlc3NvclxuICAgIC8vICAgZGV2ZWxvcGVyLndvcmtmbG93LnNlcnZpY2UudHMgdG9WZXJzaW9uRHRvICAgICAgICAgICAgICAgICBcdTIxOTIgeyBpZCwgdmVyc2lvbiwgYWN0aXZlLCAuLi4gfVxuICAgIC8vICAgZHRvL2pvYi5kdG8udHMgSm9iVmVyc2lvbkR0byAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyBpZCwgdmVyc2lvbiwgYWN0aXZlLCAuLi4gfVxuICAgIC8vICAgZGV2ZWxvcGVyLndlYmhvb2suc2VydmljZS50cyBnZXRXZWJob29rVmVyc2lvbnMgICAgICAgICAgICBcdTIxOTIgeyB2ZXJzaW9uLCB3ZWJob29rSWQ6IHYuaWQsIGNyZWF0ZWRBdCwgaXNBY3RpdmUgfVxuICAgIC8vICAgICAgIChOTyB2ZXJzaW9uSWQvaWQga2V5IFx1MjAxNCB0aGUgcm93IGlkIGlzIHN0YXNoZWQgdW5kZXIgdGhlIG1pc25hbWVkXG4gICAgLy8gICAgICAgIGB3ZWJob29rSWRgLCB3aGljaCB3ZSBkZWxpYmVyYXRlbHkgZG8gbm90IHJlYWQgYXMgYSB2ZXJzaW9uIGlkKVxuICAgIC8vICAgZHRvL3NraWxsLmR0by50cyBTa2lsbFZlcnNpb25EdG8gICAgICAgICAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyB2ZXJzaW9uLCBjcmVhdGVkRGF0ZSwgY3JlYXRlZEJ5LCBpc0N1cnJlbnQgfSAobm8gaWQpXG4gICAgLy8gICBwZXJzb25hL2Jhc2UuY29udHJvbGxlci50cyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHZlcnNpb246IG51bWJlciwgY3JlYXRlZERhdGUsIGlzQ3VycmVudCwgcGVyc29uYSB9IChubyBpZClcbiAgICBkZXNjcmlwdGlvbjogJ0xpc3QgdGhlIHNlcnZlci1zaWRlIHZlcnNpb25zIG9mIG9uZSBwcmltaXRpdmUgKHNraWxsLCB3ZWJob29rLCBqb2IsIHRyaWdnZXIsIHByZXByb2Nlc3NvciwgcG9zdHByb2Nlc3Nvciwgd29ya2Zsb3cpIG9yIHRoZSBhZ2VudCBwZXJzb25hLiBSZXR1cm5zIHsgYWN0aXZlVmVyc2lvbklkLCB2ZXJzaW9uczogW3t2ZXJzaW9uLCB2ZXJzaW9uSWQsIGFjdGl2ZSwgY3JlYXRlZEF0fV0gfSBcdTIwMTQgYGFjdGl2ZWAgaXMgdGhlIHZlcnNpb24gY3VycmVudGx5IGxpdmUuIGB2ZXJzaW9uSWRgIGlzIG51bGwgZm9yIHR5cGVzIHdob3NlIHZlcnNpb25zIHJvdXRlIGNhcnJpZXMgbm8gcGVyLWVudHJ5IGlkOiBza2lsbCwgcGVyc29uYSBhbmQgd2ViaG9vayAodGhlIHdlYmhvb2sgcm91dGUgZXhwb3NlcyB0aGUgcm93IGlkIG9ubHkgdW5kZXIgYSBtaXNuYW1lZCBgd2ViaG9va0lkYCBrZXkpLiBGb3IgdGhvc2UsIGlkZW50aWZ5IGEgdmVyc2lvbiBieSBgdmVyc2lvbmA7IGZvciB3ZWJob29rcyB0aGUgbGl2ZSByb3cgaWQgaXMgdGhlIHRvcC1sZXZlbCBgYWN0aXZlVmVyc2lvbklkYC4gYGFjdGl2ZVZlcnNpb25JZGAgaXMgcG9wdWxhdGVkIG9ubHkgZm9yIHdlYmhvb2ssIHRyaWdnZXIsIHByZXByb2Nlc3NvciBhbmQgcG9zdHByb2Nlc3NvciBlbnZlbG9wZXMgYW5kIGlzIG51bGwgZm9yIHNraWxsLCBqb2IsIHdvcmtmbG93IGFuZCBwZXJzb25hLicsXG4gICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgcHJvcGVydGllczoge1xuICAgICAgICBhZ2VudElkOiB7IHR5cGU6ICdzdHJpbmcnIH0sXG4gICAgICAgIHR5cGU6IHsgdHlwZTogJ3N0cmluZycsIGVudW06IFsuLi5WQUxJRF9UWVBFU10gfSxcbiAgICAgICAgbmFtZTogeyB0eXBlOiAnc3RyaW5nJywgZGVzY3JpcHRpb246ICdQcmltaXRpdmUgbmFtZSBhcyB3cml0dGVuIGluIGx1YS5za2lsbC55YW1sIChvbWl0IGZvciBwZXJzb25hIFx1MjAxNCBhbiBhZ2VudCBoYXMgb25seSBvbmUpJyB9LFxuICAgICAgfSxcbiAgICAgIHJlcXVpcmVkOiBbJ2FnZW50SWQnLCAndHlwZSddLFxuICAgIH0sXG4gIH0sXG4gIGFzeW5jIGhhbmRsZXIoeyBhZ2VudElkLCB0eXBlLCBuYW1lIH0sIGRlcHMgPSB7fSkge1xuICAgIGlmICghYWdlbnRJZCB8fCAhdHlwZSkgdGhyb3cgbmV3IEVycm9yKCdhZ2VudElkIGFuZCB0eXBlIGFyZSByZXF1aXJlZCcpO1xuICAgIGlmICghVkFMSURfVFlQRVMuaGFzKHR5cGUpKSB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgdHlwZTogJHt0eXBlfS4gVmFsaWQ6ICR7Wy4uLlZBTElEX1RZUEVTXS5qb2luKCcsICcpfWApO1xuICAgIGlmICh0eXBlICE9PSAncGVyc29uYScgJiYgIW5hbWUpIHRocm93IG5ldyBFcnJvcignbmFtZSBpcyByZXF1aXJlZCBmb3IgYWxsIHR5cGVzIGV4Y2VwdCBwZXJzb25hJyk7XG5cbiAgICAvLyBWZXJzaW9ucyBsaXZlIHVuZGVyIC9kZXZlbG9wZXIvPHBsdXJhbD4vOmFnZW50SWQvOmlkL3ZlcnNpb25zLCBFWENFUFRcbiAgICAvLyBwZXJzb25hIHdoaWNoIGxpdmVzIHVuZGVyIC9kZXZlbG9wZXIvYWdlbnRzLzphZ2VudElkL3BlcnNvbmEvdmVyc2lvbnNcbiAgICAvLyAobm8gOmlkKS4gVGhlIDppZCBzbG90IGlzIHRoZSBzZXJ2ZXIgaWQgKHNraWxsSWQgLyB3ZWJob29rSWQgLyAuLi4pLFxuICAgIC8vIG5ldmVyIHRoZSBuYW1lIFx1MjAxNCBzbyByZXNvbHZlIG5hbWUgXHUyMTkyIGlkIGJ5IGxpc3RpbmcgZmlyc3QuXG4gICAgbGV0IHBhdGg7XG4gICAgaWYgKHR5cGUgPT09ICdwZXJzb25hJykge1xuICAgICAgcGF0aCA9IGAvZGV2ZWxvcGVyL2FnZW50cy8ke2VuY29kZVVSSUNvbXBvbmVudChhZ2VudElkKX0vcGVyc29uYS92ZXJzaW9uc2A7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGlkID0gYXdhaXQgcmVzb2x2ZU5hbWVUb0lkKHsgYWdlbnRJZCwgdHlwZSwgbmFtZSwgZmV0Y2hGbjogZGVwcy5mZXRjaEZuIH0pO1xuICAgICAgcGF0aCA9IGAvZGV2ZWxvcGVyLyR7TElTVF9QQVRIU1t0eXBlXX0vJHtlbmNvZGVVUklDb21wb25lbnQoYWdlbnRJZCl9LyR7ZW5jb2RlVVJJQ29tcG9uZW50KGlkKX0vdmVyc2lvbnNgO1xuICAgIH1cblxuICAgIGNvbnN0IGRhdGEgPSBhd2FpdCBhcGlSZXF1ZXN0KHBhdGgsIHsgZmV0Y2hGbjogZGVwcy5mZXRjaEZuIH0pO1xuICAgIGNvbnN0IHZlcnNpb25zID0gZXh0cmFjdFZlcnNpb25zKHR5cGUsIGRhdGEpO1xuICAgIGNvbnN0IGNvbXBhY3QgPSB2ZXJzaW9ucy5tYXAoKHYpID0+ICh7XG4gICAgICB2ZXJzaW9uOiB2LnZlcnNpb24sXG4gICAgICB2ZXJzaW9uSWQ6IHZlcnNpb25JZCh2KSxcbiAgICAgIGFjdGl2ZTogaXNBY3RpdmVWZXJzaW9uKHR5cGUsIHYsIGRhdGEpLFxuICAgICAgY3JlYXRlZEF0OiB2ZXJzaW9uQ3JlYXRlZEF0KHYpLFxuICAgIH0pKTtcbiAgICBjb25zdCByZXN1bHQgPSB7IGFnZW50SWQsIHR5cGUsIG5hbWU6IHR5cGUgPT09ICdwZXJzb25hJyA/IG51bGwgOiBuYW1lLCBhY3RpdmVWZXJzaW9uSWQ6IGV4dHJhY3RBY3RpdmVWZXJzaW9uSWQoZGF0YSksIHZlcnNpb25zOiBjb21wYWN0IH07XG4gICAgcmV0dXJuIHsgY29udGVudDogW3sgdHlwZTogJ3RleHQnLCB0ZXh0OiBKU09OLnN0cmluZ2lmeShyZXN1bHQsIG51bGwsIDIpIH1dIH07XG4gIH0sXG59O1xuXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlTmFtZVRvSWQoeyBhZ2VudElkLCB0eXBlLCBuYW1lLCBmZXRjaEZuIH0pIHtcbiAgY29uc3QgbGlzdFBhdGggPSBgL2RldmVsb3Blci8ke0xJU1RfUEFUSFNbdHlwZV19LyR7ZW5jb2RlVVJJQ29tcG9uZW50KGFnZW50SWQpfWA7XG4gIGNvbnN0IGxpc3RSZXNwb25zZSA9IGF3YWl0IGFwaVJlcXVlc3QobGlzdFBhdGgsIHsgZmV0Y2hGbiB9KTtcbiAgY29uc3QgaXRlbXMgPSBleHRyYWN0TGlzdCh0eXBlLCBsaXN0UmVzcG9uc2UpO1xuICBjb25zdCBtYXRjaCA9IGl0ZW1zLmZpbmQoKHApID0+IHAubmFtZSA9PT0gbmFtZSk7XG4gIGlmICghbWF0Y2gpIHtcbiAgICBjb25zdCBhdmFpbGFibGUgPSBpdGVtcy5tYXAoKHApID0+IHAubmFtZSkuZmlsdGVyKEJvb2xlYW4pLmpvaW4oJywgJykgfHwgJyhub25lKSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBsaXN0X3ByaW1pdGl2ZV92ZXJzaW9uczogbm8gJHt0eXBlfSBuYW1lZCBcIiR7bmFtZX1cIiBvbiBhZ2VudCAke2FnZW50SWR9LiBBdmFpbGFibGU6ICR7YXZhaWxhYmxlfWApO1xuICB9XG4gIGNvbnN0IGlkID0gbWF0Y2guaWQgPz8gbWF0Y2guX2lkO1xuICBpZiAoIWlkKSB0aHJvdyBuZXcgRXJyb3IoYGxpc3RfcHJpbWl0aXZlX3ZlcnNpb25zOiAke3R5cGV9IFwiJHtuYW1lfVwiIGV4aXN0cyBidXQgaGFzIG5vIGlkIGZpZWxkYCk7XG4gIHJldHVybiBpZDtcbn1cbiIsICJpbXBvcnQgeyBhcGlSZXF1ZXN0IH0gZnJvbSAnLi4vYXBpLWNsaWVudC5tanMnO1xuaW1wb3J0IHtcbiAgZXh0cmFjdExpc3QsXG4gIGV4dHJhY3RWZXJzaW9ucyxcbiAgZXh0cmFjdEFjdGl2ZVZlcnNpb25JZCxcbiAgaXNBY3RpdmVWZXJzaW9uLFxuICB2ZXJzaW9uQ3JlYXRlZEF0LFxuICB2ZXJzaW9uSWQsXG4gIExJU1RfUEFUSFMsXG59IGZyb20gJy4uL3Jlc3BvbnNlLXNoYXBlcy5tanMnO1xuXG4vLyBDb21wb3NlcyBcIndoYXQgaXMgbGl2ZVwiIGZyb20gdGhlIHBlci10eXBlIGx1YS1hcGkgbGlzdCArIHZlcnNpb25zIHJvdXRlcy5cbi8vIFRoZXJlIGlzIG5vIHNpbmdsZSBgL2FnZW50cy86aWQvcHJvZHVjdGlvbmAgcm91dGU7IGBsdWEgc3RhdHVzIC0tanNvbmBcbi8vIGdpdmVzIHRoZSBzYW1lIHBpY3R1cmUgZm9yIHRoZSBDVVJSRU5UIHByb2plY3QgKGxvY2FsIHZzIGRlcGxveWVkKSBhbmQgaXNcbi8vIHRoZSBiZXR0ZXIgdG9vbCB3aGVuIGEgbHVhLnNraWxsLnlhbWwgaXMgYXQgaGFuZC4gVGhpcyB0b29sIHdvcmtzIGZvciBhbnlcbi8vIGFnZW50IHRoZSBjcmVkZW50aWFsIGNhbiByZWFjaCwgcHJvamVjdCBvciBub3QuXG4vL1xuLy8gUGVyLXR5cGUgZW52ZWxvcGUgaGFuZGxpbmcgbGl2ZXMgaW4gcmVzcG9uc2Utc2hhcGVzLm1qcy5cbi8vXG4vLyBSZXF1ZXN0IGZhbi1vdXQgKDEgKyA3ICsgTiB2ZXJzaW9uIGxvb2t1cHMpIGlzIHBhcmFsbGVsaXNlZCBpbiB0d28gcGhhc2VzOlxuLy8gICAxLiB0aGUgcGVyc29uYSBjYWxsIGFuZCB0aGUgc2V2ZW4gbGlzdCBjYWxscyBydW4gdG9nZXRoZXI7XG4vLyAgIDIuIHBlciB0eXBlLCBpdGVtIHZlcnNpb24gbG9va3VwcyBydW4gaW4gY2h1bmtzIG9mIFZFUlNJT05TX0NIVU5LX1NJWkUuXG4vLyBPdXRwdXQgb3JkZXIgaXMgZGV0ZXJtaW5pc3RpYyByZWdhcmRsZXNzIG9mIGNvbXBsZXRpb24gb3JkZXI6IHR5cGVzIGluXG4vLyBQUklNSVRJVkVfVFlQRVMgb3JkZXIsIGl0ZW1zIGluIHRoZSBvcmRlciB0aGUgbGlzdCByb3V0ZSByZXR1cm5lZCB0aGVtLlxuLy8gQSB3YWxsLWNsb2NrIGJ1ZGdldCAoQlVER0VUX01TLCBpbmplY3RhYmxlIGFzIGRlcHMuYnVkZ2V0TXMpIHN0b3BzIE5FV1xuLy8gcmVxdWVzdHMgb25jZSBleGNlZWRlZDsgYWxyZWFkeS1pc3N1ZWQgcmVxdWVzdHMgc3RpbGwgY29tcGxldGUuIEl0ZW1zIHRoYXRcbi8vIGNvdWxkIG5vdCBiZSBsb29rZWQgdXAgYXJlIHN0aWxsIGxpc3RlZCwgd2l0aCBgZXJyb3I6IFwic2tpcHBlZDogXHUyMDI2XCJgLCBhbmRcbi8vIHRoZSByZXN1bHQgY2FycmllcyBgcGFydGlhbDogdHJ1ZWAgKyBgcGFydGlhbFJlYXNvbmAuXG5cbmNvbnN0IFBSSU1JVElWRV9UWVBFUyA9IFsnc2tpbGwnLCAnd2ViaG9vaycsICdqb2InLCAndHJpZ2dlcicsICdwcmVwcm9jZXNzb3InLCAncG9zdHByb2Nlc3NvcicsICd3b3JrZmxvdyddO1xuY29uc3QgVkVSU0lPTlNfQ0hVTktfU0laRSA9IDU7XG5jb25zdCBERUZBVUxUX0JVREdFVF9NUyA9IDQ1XzAwMDtcblxuZXhwb3J0IGNvbnN0IGdldERlcGxveW1lbnRTdGF0dXMgPSB7XG4gIHNwZWM6IHtcbiAgICBuYW1lOiAnZ2V0X2RlcGxveW1lbnRfc3RhdHVzJyxcbiAgICBkZXNjcmlwdGlvbjogJ1doYXQgaXMgbGl2ZSByaWdodCBub3cgZm9yIGFuIGFnZW50OiBmb3IgZXZlcnkgc2tpbGwsIHdlYmhvb2ssIGpvYiwgdHJpZ2dlciwgcHJlcHJvY2Vzc29yLCBwb3N0cHJvY2Vzc29yIGFuZCB3b3JrZmxvdyB0aGUgYWN0aXZlIChkZXBsb3llZCkgdmVyc2lvbiwgcGx1cyB0aGUgYWN0aXZlIHBlcnNvbmEgdmVyc2lvbi4gQ29tcG9zZWQgZnJvbSB0aGUgcGVyLXR5cGUgbHVhLWFwaSB2ZXJzaW9uIHJvdXRlcyAobGlzdCBjYWxscyBpbiBwYXJhbGxlbCwgdGhlbiBwZXItaXRlbSB2ZXJzaW9uIGxvb2t1cHMgaW4gY2h1bmtzIG9mIDUpLiBCb3VuZGVkIGJ5IGEgNDUgcyB3YWxsLWNsb2NrIGJ1ZGdldDogaWYgZXhjZWVkZWQsIHJlbWFpbmluZyBpdGVtcyBhcmUgbGlzdGVkIHdpdGggYGVycm9yOiBcInNraXBwZWQ6IFx1MjAyNlwiYCBhbmQgdGhlIHJlc3VsdCBjYXJyaWVzIGBwYXJ0aWFsOiB0cnVlYCArIGBwYXJ0aWFsUmVhc29uYC4gSW5zaWRlIGEgcHJvamVjdCwgYGx1YSBzdGF0dXMgLS1qc29uYCBhZGRpdGlvbmFsbHkgY29tcGFyZXMgbG9jYWwgdnMgZGVwbG95ZWQuJyxcbiAgICBpbnB1dFNjaGVtYToge1xuICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgIGFnZW50SWQ6IHsgdHlwZTogJ3N0cmluZycgfSxcbiAgICAgIH0sXG4gICAgICByZXF1aXJlZDogWydhZ2VudElkJ10sXG4gICAgfSxcbiAgfSxcbiAgYXN5bmMgaGFuZGxlcih7IGFnZW50SWQgfSwgZGVwcyA9IHt9KSB7XG4gICAgaWYgKCFhZ2VudElkKSB0aHJvdyBuZXcgRXJyb3IoJ2FnZW50SWQgaXMgcmVxdWlyZWQnKTtcbiAgICBjb25zdCBpZCA9IGVuY29kZVVSSUNvbXBvbmVudChhZ2VudElkKTtcbiAgICBjb25zdCBidWRnZXRNcyA9IE51bWJlci5pc0Zpbml0ZShkZXBzLmJ1ZGdldE1zKSA/IGRlcHMuYnVkZ2V0TXMgOiBERUZBVUxUX0JVREdFVF9NUztcbiAgICBjb25zdCBzdGFydGVkQXQgPSBEYXRlLm5vdygpO1xuICAgIGNvbnN0IG92ZXJCdWRnZXQgPSAoKSA9PiBEYXRlLm5vdygpIC0gc3RhcnRlZEF0ID4gYnVkZ2V0TXM7XG4gICAgY29uc3QgZ2V0ID0gKHBhdGgpID0+IGFwaVJlcXVlc3QocGF0aCwgeyBmZXRjaEZuOiBkZXBzLmZldGNoRm4gfSk7XG5cbiAgICBjb25zdCByZXN1bHQgPSB7IGFnZW50SWQsIHBlcnNvbmE6IG51bGwsIHByaW1pdGl2ZXM6IHt9IH07XG4gICAgLy8gU2VlZCBrZXlzIHVwIGZyb250IHNvIHRoZSBvdXRwdXQga2V5IG9yZGVyIG5ldmVyIGRlcGVuZHMgb24gdGltaW5nLlxuICAgIGZvciAoY29uc3QgdHlwZSBvZiBQUklNSVRJVkVfVFlQRVMpIHJlc3VsdC5wcmltaXRpdmVzW3R5cGVdID0gW107XG5cbiAgICAvLyBQaGFzZSAxOiBwZXJzb25hICsgZXZlcnkgbGlzdCByb3V0ZSwgYWxsIGluIGZsaWdodCBhdCBvbmNlLlxuICAgIC8vIFBlcnNvbmE6IG9uZSBwZXIgYWdlbnQsIHZlcnNpb25zIHVuZGVyIC9kZXZlbG9wZXIvYWdlbnRzLzphZ2VudElkL3BlcnNvbmEvdmVyc2lvbnMuXG4gICAgY29uc3QgW3BlcnNvbmFPdXRjb21lLCAuLi5saXN0T3V0Y29tZXNdID0gYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcbiAgICAgIGdldChgL2RldmVsb3Blci9hZ2VudHMvJHtpZH0vcGVyc29uYS92ZXJzaW9uc2ApLFxuICAgICAgLi4uUFJJTUlUSVZFX1RZUEVTLm1hcCgodHlwZSkgPT4gZ2V0KGAvZGV2ZWxvcGVyLyR7TElTVF9QQVRIU1t0eXBlXX0vJHtpZH1gKSksXG4gICAgXSk7XG5cbiAgICBpZiAocGVyc29uYU91dGNvbWUuc3RhdHVzID09PSAnZnVsZmlsbGVkJykge1xuICAgICAgY29uc3QgcGVyc29uYVJlc3BvbnNlID0gcGVyc29uYU91dGNvbWUudmFsdWU7XG4gICAgICBjb25zdCB2ZXJzaW9ucyA9IGV4dHJhY3RWZXJzaW9ucygncGVyc29uYScsIHBlcnNvbmFSZXNwb25zZSk7XG4gICAgICBjb25zdCBhY3RpdmUgPSB2ZXJzaW9ucy5maW5kKCh2KSA9PiBpc0FjdGl2ZVZlcnNpb24oJ3BlcnNvbmEnLCB2LCBwZXJzb25hUmVzcG9uc2UpKSA/PyBudWxsO1xuICAgICAgcmVzdWx0LnBlcnNvbmEgPSB7XG4gICAgICAgIGFjdGl2ZVZlcnNpb246IGFjdGl2ZT8udmVyc2lvbiA/PyBudWxsLFxuICAgICAgICBhY3RpdmVWZXJzaW9uQ3JlYXRlZEF0OiB2ZXJzaW9uQ3JlYXRlZEF0KGFjdGl2ZSksXG4gICAgICAgIHZlcnNpb25Db3VudDogdmVyc2lvbnMubGVuZ3RoLFxuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzdWx0LnBlcnNvbmEgPSB7IGVycm9yOiBwZXJzb25hT3V0Y29tZS5yZWFzb24/Lm1lc3NhZ2UgPz8gU3RyaW5nKHBlcnNvbmFPdXRjb21lLnJlYXNvbikgfTtcbiAgICB9XG5cbiAgICAvLyBQaGFzZSAyOiBwZXIgdHlwZSwgcmVzb2x2ZSBlYWNoIGl0ZW0ncyB2ZXJzaW9ucyBpbiBjaHVua3MuIFR5cGVzIGFyZVxuICAgIC8vIHZpc2l0ZWQgc2VxdWVudGlhbGx5IChjYW5vbmljYWwgb3JkZXIpOyB3aXRoaW4gYSB0eXBlIHVwIHRvXG4gICAgLy8gVkVSU0lPTlNfQ0hVTktfU0laRSBsb29rdXBzIGFyZSBpbiBmbGlnaHQgdG9nZXRoZXIuXG4gICAgbGV0IGJ1ZGdldEV4aGF1c3RlZCA9IGZhbHNlO1xuICAgIGxldCBza2lwcGVkID0gMDtcbiAgICBsZXQgdG90YWwgPSAwO1xuXG4gICAgZm9yIChsZXQgdCA9IDA7IHQgPCBQUklNSVRJVkVfVFlQRVMubGVuZ3RoOyB0KyspIHtcbiAgICAgIGNvbnN0IHR5cGUgPSBQUklNSVRJVkVfVFlQRVNbdF07XG4gICAgICBjb25zdCBwYXRoID0gTElTVF9QQVRIU1t0eXBlXTtcbiAgICAgIGNvbnN0IG91dGNvbWUgPSBsaXN0T3V0Y29tZXNbdF07XG4gICAgICBpZiAob3V0Y29tZS5zdGF0dXMgIT09ICdmdWxmaWxsZWQnKSB7XG4gICAgICAgIHJlc3VsdC5wcmltaXRpdmVzW3R5cGVdID0geyBlcnJvcjogb3V0Y29tZS5yZWFzb24/Lm1lc3NhZ2UgPz8gU3RyaW5nKG91dGNvbWUucmVhc29uKSB9O1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gVGhlIFVSTCBzbG90IGlzIHRoZSBzZXJ2ZXIgaWQgKDpza2lsbElkIC8gOndlYmhvb2tJZCAvIC4uLiksIG5ldmVyXG4gICAgICAvLyB0aGUgbmFtZS4gSXRlbXMgd2l0aG91dCBhbiBpZCBhcmUgZHJvcHBlZCwgYXMgYmVmb3JlLlxuICAgICAgY29uc3QgdGFyZ2V0cyA9IGV4dHJhY3RMaXN0KHR5cGUsIG91dGNvbWUudmFsdWUpXG4gICAgICAgIC5tYXAoKHApID0+ICh7IHAsIHByaW1JZDogcC5pZCA/PyBwLl9pZCwgbmFtZTogcC5uYW1lID8/IHAuaWQgPz8gcC5faWQgfSkpXG4gICAgICAgIC5maWx0ZXIoKHRndCkgPT4gdGd0LnByaW1JZCk7XG4gICAgICB0b3RhbCArPSB0YXJnZXRzLmxlbmd0aDtcblxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0YXJnZXRzLmxlbmd0aDsgaSArPSBWRVJTSU9OU19DSFVOS19TSVpFKSB7XG4gICAgICAgIGNvbnN0IGNodW5rID0gdGFyZ2V0cy5zbGljZShpLCBpICsgVkVSU0lPTlNfQ0hVTktfU0laRSk7XG5cbiAgICAgICAgaWYgKGJ1ZGdldEV4aGF1c3RlZCB8fCBvdmVyQnVkZ2V0KCkpIHtcbiAgICAgICAgICBidWRnZXRFeGhhdXN0ZWQgPSB0cnVlO1xuICAgICAgICAgIHNraXBwZWQgKz0gY2h1bmsubGVuZ3RoO1xuICAgICAgICAgIGZvciAoY29uc3QgeyBuYW1lLCBwcmltSWQgfSBvZiBjaHVuaykge1xuICAgICAgICAgICAgcmVzdWx0LnByaW1pdGl2ZXNbdHlwZV0ucHVzaCh7XG4gICAgICAgICAgICAgIG5hbWUsIGlkOiBwcmltSWQsXG4gICAgICAgICAgICAgIGVycm9yOiBgc2tpcHBlZDogd2FsbC1jbG9jayBidWRnZXQgb2YgJHtidWRnZXRNc31tcyBleGhhdXN0ZWQgYmVmb3JlIHRoaXMgdmVyc2lvbiBsb29rdXAgd2FzIGlzc3VlZGAsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBvdXRjb21lcyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChcbiAgICAgICAgICBjaHVuay5tYXAoKHsgcHJpbUlkIH0pID0+IGdldChgL2RldmVsb3Blci8ke3BhdGh9LyR7aWR9LyR7ZW5jb2RlVVJJQ29tcG9uZW50KHByaW1JZCl9L3ZlcnNpb25zYCkpXG4gICAgICAgICk7XG5cbiAgICAgICAgb3V0Y29tZXMuZm9yRWFjaCgodm8sIGopID0+IHtcbiAgICAgICAgICBjb25zdCB7IHAsIHByaW1JZCwgbmFtZSB9ID0gY2h1bmtbal07XG4gICAgICAgICAgaWYgKHZvLnN0YXR1cyAhPT0gJ2Z1bGZpbGxlZCcpIHtcbiAgICAgICAgICAgIHJlc3VsdC5wcmltaXRpdmVzW3R5cGVdLnB1c2goeyBuYW1lLCBpZDogcHJpbUlkLCBlcnJvcjogdm8ucmVhc29uPy5tZXNzYWdlID8/IFN0cmluZyh2by5yZWFzb24pIH0pO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCB2ZXJzaW9uc1Jlc3BvbnNlID0gdm8udmFsdWU7XG4gICAgICAgICAgY29uc3QgdmVyc2lvbnMgPSBleHRyYWN0VmVyc2lvbnModHlwZSwgdmVyc2lvbnNSZXNwb25zZSk7XG4gICAgICAgICAgY29uc3QgYWN0aXZlID0gdmVyc2lvbnMuZmluZCgodikgPT4gaXNBY3RpdmVWZXJzaW9uKHR5cGUsIHYsIHZlcnNpb25zUmVzcG9uc2UpKSA/PyBudWxsO1xuXG4gICAgICAgICAgY29uc3QgZW50cnkgPSB7XG4gICAgICAgICAgICBuYW1lLFxuICAgICAgICAgICAgaWQ6IHByaW1JZCxcbiAgICAgICAgICAgIGFjdGl2ZVZlcnNpb246IGFjdGl2ZT8udmVyc2lvbiA/PyBudWxsLFxuICAgICAgICAgICAgYWN0aXZlVmVyc2lvbklkOiB2ZXJzaW9uSWQoYWN0aXZlKSA/PyBleHRyYWN0QWN0aXZlVmVyc2lvbklkKHZlcnNpb25zUmVzcG9uc2UpLFxuICAgICAgICAgICAgYWN0aXZlVmVyc2lvbkNyZWF0ZWRBdDogdmVyc2lvbkNyZWF0ZWRBdChhY3RpdmUpLFxuICAgICAgICAgICAgdmVyc2lvbkNvdW50OiB2ZXJzaW9ucy5sZW5ndGgsXG4gICAgICAgICAgfTtcbiAgICAgICAgICAvLyBSZWNvcmRzIHRoYXQgY2FycnkgdGhlaXIgb3duIGVuYWJsZWQvZGlzYWJsZWQgc3dpdGNoLlxuICAgICAgICAgIGlmICh0eXBlb2YgcC5hY3RpdmUgPT09ICdib29sZWFuJykgZW50cnkuZW5hYmxlZCA9IHAuYWN0aXZlO1xuICAgICAgICAgIGlmICh0eXBlID09PSAnd29ya2Zsb3cnICYmIHR5cGVvZiBwLmR5bmFtaWMgPT09ICdib29sZWFuJykgZW50cnkuZHluYW1pYyA9IHAuZHluYW1pYztcbiAgICAgICAgICByZXN1bHQucHJpbWl0aXZlc1t0eXBlXS5wdXNoKGVudHJ5KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHNraXBwZWQgPiAwKSB7XG4gICAgICByZXN1bHQucGFydGlhbCA9IHRydWU7XG4gICAgICByZXN1bHQucGFydGlhbFJlYXNvbiA9XG4gICAgICAgIGBXYWxsLWNsb2NrIGJ1ZGdldCBvZiAke2J1ZGdldE1zfW1zIGV4Y2VlZGVkIGFmdGVyICR7RGF0ZS5ub3coKSAtIHN0YXJ0ZWRBdH1tczsgYCArXG4gICAgICAgIGAke3NraXBwZWR9IG9mICR7dG90YWx9IHZlcnNpb24gbG9va3VwcyB3ZXJlIG5vdCBpc3N1ZWQgKGVudHJpZXMgY2FycnkgZXJyb3I6IFwic2tpcHBlZDogXHUyMDI2XCIpLmA7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGNvbnRlbnQ6IFt7IHR5cGU6ICd0ZXh0JywgdGV4dDogSlNPTi5zdHJpbmdpZnkocmVzdWx0LCBudWxsLCAyKSB9XSxcbiAgICB9O1xuICB9LFxufTtcbiIsICJpbXBvcnQgeyBhcGlSZXF1ZXN0IH0gZnJvbSAnLi4vYXBpLWNsaWVudC5tanMnO1xuXG4vLyBUaGUgYGxvZ1NvdXJjZWAgZW51bSBsdWEtYXBpIGFjY2VwdHMgXHUyMDE0IEFHRU5UX0xPR19TT1VSQ0VTIGluXG4vLyBwYWNrYWdlcy9zaGFyZWQtdHlwZXMvc3JjL3ZtLWV4ZWN1dGlvbi1sb2cudHlwZXMudHMgKHRoZSBzaW5nbGUgc291cmNlIG9mXG4vLyB0cnV0aCBldmVyeSBzZXJ2aWNlIGltcG9ydHMpLiBgYWxsYCBpcyB0aGUgTUNQLXNpZGUgc2VudGluZWwgZm9yIFwibm9cbi8vIGZpbHRlclwiLiBOb3RlOiBgbHVhIGxvZ3MgLS10eXBlYCBhY2NlcHRzIGEgbmFycm93ZXIgbGlzdCBhbmQgbm8gYG1hc3RyYWAuXG5leHBvcnQgY29uc3QgTE9HX1NPVVJDRVMgPSBbXG4gICdza2lsbCcsICdqb2InLCAnd2ViaG9vaycsICd0cmlnZ2VyJywgJ3ByZXByb2Nlc3NvcicsICdwb3N0cHJvY2Vzc29yJyxcbiAgJ3VzZXJfbWVzc2FnZScsICdhZ2VudF9yZXNwb25zZScsICdhZ2VudF9lcnJvcicsICdydW50aW1lJywgJ21jcCcsICdyYWcnLFxuICAnZGV2aWNlJywgJ2RldmljZS10cmlnZ2VyJywgJ21vZGVsLXJlc29sdmVyJyxcbiAgJ3dvcmtmbG93LXN0ZXAnLCAnd29ya2Zsb3ctc2NyaXB0JywgJ3dvcmtmbG93Jyxcbl07XG5cbi8vIFRoZSBgbG9nVHlwZWAgcXVlcnkgcGFyYW0gXHUyMDE0IEdFVCAvZGV2ZWxvcGVyL2FnZW50cy86YWdlbnRJZC9sb2dzIHJlYWRzIGl0XG4vLyAoY29udHJvbGxlcnMvZGV2ZWxvcGVyL2Jhc2UuY29udHJvbGxlci50cykgYW5kIGRldmVsb3Blci5zZXJ2aWNlLnRzXG4vLyBnZXRBZ2VudExvZ3MgYXBwbGllcyBpdCB2ZXJiYXRpbSBhcyBgZmlsdGVyLnN1YlR5cGUgPSBsb2dUeXBlYC4gVGhlXG4vLyBhY2NlcHRlZCB2YWx1ZXMgYXJlIHRoZXJlZm9yZSB0aGUgVm1FeGVjdXRpb25Mb2cgYHN1YlR5cGVgIGVudW0gaW5cbi8vIHBhY2thZ2VzL3NoYXJlZC1zY2hlbWFzL3NyYy92bUV4ZWN1dGlvbkxvZ3Muc2NoZW1hLnRzOlxuLy8gICBlbnVtOiBbJ3N0YXJ0JywgJ2NvbXBsZXRlJywgJ2Vycm9yJywgJ2RlYnVnJywgJ3dhcm4nLCAnaW5mbyddXG4vLyAobWlycm9yZWQgYnkgbHVhLWNsaSdzIExvZ0VudHJ5LnN1YlR5cGUgaW4gc3JjL2ludGVyZmFjZXMvbG9ncy50cykuIEFueVxuLy8gb3RoZXIgc3RyaW5nIGlzIG5vdCByZWplY3RlZCBzZXJ2ZXItc2lkZSBcdTIwMTQgaXQgc2ltcGx5IG1hdGNoZXMgbm90aGluZy5cbmV4cG9ydCBjb25zdCBMT0dfVFlQRVMgPSBbJ2Vycm9yJywgJ3dhcm4nLCAnaW5mbycsICdkZWJ1ZycsICdzdGFydCcsICdjb21wbGV0ZSddO1xuXG5jb25zdCBWQUxJRF9UWVBFUyA9IG5ldyBTZXQoWydhbGwnLCAuLi5MT0dfU09VUkNFU10pO1xuY29uc3QgVkFMSURfTE9HX1RZUEVTID0gbmV3IFNldChMT0dfVFlQRVMpO1xuXG5leHBvcnQgY29uc3QgdGFpbExvZ3MgPSB7XG4gIHNwZWM6IHtcbiAgICBuYW1lOiAndGFpbF9sb2dzJyxcbiAgICBkZXNjcmlwdGlvbjogJ0ZldGNoIHJlY2VudCBleGVjdXRpb24gbG9ncyBmb3IgYW4gYWdlbnQgKHNhbWUgZGF0YSBhcyBgbHVhIGxvZ3MgLS1qc29uYCkuIFJldHVybnMgeyBsb2dzOiBMb2dFbnRyeVtdLCBwYWdpbmF0aW9uIH0uIEVhY2ggTG9nRW50cnkgaGFzIGBzdWJUeXBlYCAoZXJyb3IgfCB3YXJuIHwgaW5mbyB8IGRlYnVnIHwgc3RhcnQgfCBjb21wbGV0ZSksIGBtZXNzYWdlYCwgYHRpbWVzdGFtcGAsIGFuZCBgbWV0YWRhdGEubG9nU291cmNlYCAvIGBtZXRhZGF0YS5wcmltaXRpdmVOYW1lYCBcdTIwMTQgdGhlcmUgaXMgTk8gYGxldmVsYCBmaWVsZC4gRmlsdGVyIGJ5IHNvdXJjZSAoYHR5cGVgKSwgcHJpbWl0aXZlIChgbmFtZWApIGFuZC9vciBzZXZlcml0eSAoYGxvZ1R5cGVgLCB3aGljaCBmaWx0ZXJzIG9uIGBzdWJUeXBlYCkuJyxcbiAgICBpbnB1dFNjaGVtYToge1xuICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgIGFnZW50SWQ6IHsgdHlwZTogJ3N0cmluZycgfSxcbiAgICAgICAgdHlwZToge1xuICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgIGVudW06IFsuLi5WQUxJRF9UWVBFU10sXG4gICAgICAgICAgZGVmYXVsdDogJ2FsbCcsXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdMb2cgc291cmNlIGZpbHRlciAobWFwcyB0byBsdWEtYXBpIGBsb2dTb3VyY2VgKS4gYGFsbGAgPSBubyBmaWx0ZXIuJyxcbiAgICAgICAgfSxcbiAgICAgICAgbmFtZTogeyB0eXBlOiAnc3RyaW5nJywgZGVzY3JpcHRpb246ICdGaWx0ZXIgYnkgcHJpbWl0aXZlIG5hbWUgKG9wdGlvbmFsOyBtYXBzIHRvIGBwcmltaXRpdmVOYW1lYCknIH0sXG4gICAgICAgIGxvZ1R5cGU6IHtcbiAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICBlbnVtOiBMT0dfVFlQRVMsXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdGaWx0ZXIgYnkgTG9nRW50cnkgYHN1YlR5cGVgIChvcHRpb25hbDsgbWFwcyB0byBsdWEtYXBpIGBsb2dUeXBlYCkuIE9taXQgZm9yIGV2ZXJ5IHNldmVyaXR5LiBlLmcuIGBlcnJvcmAgdG8gc2VlIG9ubHkgZmFpbHVyZXMuJyxcbiAgICAgICAgfSxcbiAgICAgICAgLy8gbHVhLWFwaSBjYXBzIGxpbWl0IGF0IDEwMCBzZXJ2ZXItc2lkZSAoTWF0aC5taW4oMTAwLCAuLi4pIGluXG4gICAgICAgIC8vIGNvbnRyb2xsZXJzL2RldmVsb3Blci9iYXNlLmNvbnRyb2xsZXIudHMpLiBNYXRjaCB0aGUgc2VydmVyIGNhcCBzb1xuICAgICAgICAvLyB0aGUgdG9vbCdzIGNvbnRyYWN0IGlzIGhvbmVzdC5cbiAgICAgICAgbGltaXQ6IHsgdHlwZTogJ251bWJlcicsIGRlZmF1bHQ6IDUwLCBtaW5pbXVtOiAxLCBtYXhpbXVtOiAxMDAgfSxcbiAgICAgICAgcGFnZTogeyB0eXBlOiAnbnVtYmVyJywgbWluaW11bTogMSwgZGVzY3JpcHRpb246ICdQYWdlIG51bWJlciBmb3IgcGFnaW5hdGlvbiAob3B0aW9uYWwpJyB9LFxuICAgICAgfSxcbiAgICAgIHJlcXVpcmVkOiBbJ2FnZW50SWQnXSxcbiAgICB9LFxuICB9LFxuICBhc3luYyBoYW5kbGVyKHsgYWdlbnRJZCwgdHlwZSA9ICdhbGwnLCBuYW1lLCBsb2dUeXBlLCBsaW1pdCA9IDUwLCBwYWdlIH0sIGRlcHMgPSB7fSkge1xuICAgIGlmICghYWdlbnRJZCkgdGhyb3cgbmV3IEVycm9yKCdhZ2VudElkIGlzIHJlcXVpcmVkJyk7XG4gICAgaWYgKCFWQUxJRF9UWVBFUy5oYXModHlwZSkpIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCB0eXBlOiAke3R5cGV9LiBWYWxpZDogJHtbLi4uVkFMSURfVFlQRVNdLmpvaW4oJywgJyl9YCk7XG4gICAgaWYgKGxvZ1R5cGUgIT09IHVuZGVmaW5lZCAmJiAhVkFMSURfTE9HX1RZUEVTLmhhcyhsb2dUeXBlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGxvZ1R5cGU6ICR7bG9nVHlwZX0uIFZhbGlkOiAke0xPR19UWVBFUy5qb2luKCcsICcpfWApO1xuICAgIH1cbiAgICAvLyBEZWZlbnNpdmUgY2FwIG1hdGNoaW5nIHRoZSBzZXJ2ZXItc2lkZSBsaW1pdDsgcmVqZWN0cyBsb3VkIHJhdGhlclxuICAgIC8vIHRoYW4gc2lsZW50bHkgdHJ1bmNhdGluZy5cbiAgICBpZiAobGltaXQgPiAxMDApIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBsaW1pdDogJHtsaW1pdH0uIGx1YS1hcGkgY2FwcyBsb2dzIGF0IDEwMCBlbnRyaWVzIHBlciBjYWxsLmApO1xuICAgIC8vIFJvdXRlOiBHRVQgL2RldmVsb3Blci9hZ2VudHMvOmFnZW50SWQvbG9ncyAoY29udHJvbGxlcnMvZGV2ZWxvcGVyL2Jhc2UuY29udHJvbGxlci50cykuXG4gICAgLy8gUXVlcnkgcGFyYW1zIHRoZSBzZXJ2ZXIgcmVhZHM6IHBhZ2UsIGxpbWl0LCBzb3J0QnksIHNvcnRPcmRlciwgbG9nU291cmNlLFxuICAgIC8vIHByaW1pdGl2ZUlkLCBwcmltaXRpdmVOYW1lLCB0b29sSWQsIHRvb2xOYW1lLCBsb2dUeXBlLCB1c2VySWQuIFRoZVxuICAgIC8vIHR5cGUgZmlsdGVyIGlzIGBsb2dTb3VyY2VgIFx1MjAxNCBhIGBwcmltaXRpdmVUeXBlYCBwYXJhbSBpcyBzaWxlbnRseSBpZ25vcmVkLlxuICAgIGNvbnN0IHF1ZXJ5ID0geyBsaW1pdCB9O1xuICAgIGlmICh0eXBlICE9PSAnYWxsJykgcXVlcnkubG9nU291cmNlID0gdHlwZTtcbiAgICBpZiAobmFtZSkgcXVlcnkucHJpbWl0aXZlTmFtZSA9IG5hbWU7XG4gICAgaWYgKGxvZ1R5cGUpIHF1ZXJ5LmxvZ1R5cGUgPSBsb2dUeXBlO1xuICAgIGlmIChwYWdlKSBxdWVyeS5wYWdlID0gcGFnZTtcbiAgICBjb25zdCBkYXRhID0gYXdhaXQgYXBpUmVxdWVzdChcbiAgICAgIGAvZGV2ZWxvcGVyL2FnZW50cy8ke2VuY29kZVVSSUNvbXBvbmVudChhZ2VudElkKX0vbG9nc2AsXG4gICAgICB7IHF1ZXJ5LCBmZXRjaEZuOiBkZXBzLmZldGNoRm4gfVxuICAgICk7XG4gICAgcmV0dXJuIHsgY29udGVudDogW3sgdHlwZTogJ3RleHQnLCB0ZXh0OiBKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCAyKSB9XSB9O1xuICB9LFxufTtcbiJdLAogICJtYXBwaW5ncyI6ICI2RkFFQSxPQUFTLFVBQUFBLE9BQWMsNENBQ3ZCLE9BQVMsd0JBQUFDLE9BQTRCLDRDQUNy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Q0FBTyxFQUFJO0FBQUEsRUFBTSxJQUFNLFFBQVEsS0FBSyxDQUFDLENBQUMsQ0FDNUUsQ0FFQSxRQUFRLEdBQUcsb0JBQXNCRixHQUFRLENBQ3ZDQyxHQUFtQixDQUNqQixNQUFPLG1CQUNQLEtBQU0sb0JBQ04sUUFBU0QsR0FBSyxRQUNkLE1BQU9BLEdBQUssTUFDWixlQUFnQixRQUNoQixnQkFBaUIsUUFBUSxJQUFJLGlCQUFtQixLQUNoRCxTQUFVLFFBQVEsU0FDbEIsR0FBSSxJQUFJLEtBQUssRUFBRSxZQUFZLENBQzdCLENBQUMsQ0FDSCxDQUFDLEVBRUQsUUFBUSxHQUFHLHFCQUF1QkcsR0FBVyxDQUMzQ0YsR0FBbUIsQ0FDakIsTUFBTyxtQkFDUCxLQUFNLHFCQUNOLE9BQVEsT0FBT0UsQ0FBTSxFQUNyQixNQUEyQkEsR0FBUyxNQUNwQyxlQUFnQixRQUNoQixnQkFBaUIsUUFBUSxJQUFJLGlCQUFtQixLQUNoRCxTQUFVLFFBQVEsU0FDbEIsR0FBSSxJQUFJLEtBQUssRUFBRSxZQUFZLENBQzdCLENBQUMsQ0FDSCxDQUFDLEVBRUQsTUFBTVYsRUFBTyxRQUFRLElBQUlXLEVBQXNCIiwKICAibmFtZXMiOiBbIlNlcnZlciIsICJTdGRpb1NlcnZlclRyYW5zcG9ydCIsICJDYWxsVG9vbFJlcXVlc3RTY2hlbWEiLCAiTGlzdFRvb2xzUmVxdWVzdFNjaGVtYSIsICJ0b29sc19leHBvcnRzIiwgIl9fZXhwb3J0IiwgImdldEFnZW50IiwgImdldERlcGxveW1lbnRTdGF0dXMiLCAibGlzdEFnZW50cyIsICJsaXN0UHJpbWl0aXZlVmVyc2lvbnMiLCAidGFpbExvZ3MiLCAic3Bhd24iLCAiREVGQVVMVF9USU1FT1VUX01TIiwgInJ1bkx1YSIsICJhcmdzIiwgInNwYXduRm4iLCAidGltZW91dE1zIiwgImxhYmVsIiwgInJlc29sdmUiLCAicmVqZWN0IiwgImNoaWxkIiwgInN0ZG91dCIsICJzdGRlcnIiLCAiZCIsICJ0aW1lciIsICJjb2RlIiwgImVyciIsICJmbGF0dGVuQWdlbnRzIiwgInBhcnNlZCIsICJvYmoiLCAiZW50cmllcyIsICJvcmciLCAiYSIsICJsaXN0QWdlbnRzVmlhQ2xpIiwgImxpc3RBZ2VudHMiLCAiX2FyZ3MiLCAiZGVwcyIsICJjb21wYWN0IiwgImxpc3RBZ2VudHNWaWFDbGkiLCAiZ2V0QWdlbnQiLCAiYWdlbnRJZCIsICJkZXBzIiwgIm1hdGNoIiwgImxpc3RBZ2VudHNWaWFDbGkiLCAiYSIsICJyZWFkRmlsZSIsICJyZWFkZGlyIiwgInN0YXQiLCAiam9pbiIsICJkaXJuYW1lIiwgImhvbWVkaXIiLCAiREVGQVVMVF9BUElfVVJMIiwgIkZJUkVCQVNFX1JFRlJFU0hfVVJMIiwgIlJFRlJFU0hfVElNRU9VVF9NUyIsICJSRUZSRVNIX1NLRVdfTVMiLCAiUkVKRUNURURfUkVGUkVTSF9SRUFTT05TIiwgInRva2VuQ2FjaGUiLCAicGFyc2VEb3RlbnZBcGlLZXkiLCAiY29udGVudCIsICJyYXdMaW5lIiwgIm0iLCAidmFsdWUiLCAicXVvdGVkIiwgImZpbmRTZXNzaW9uRmlsZSIsICJzZXNzaW9uc0RpciIsICJhcGlVcmwiLCAibmFtZXMiLCAicmVhZGRpciIsICJjYW5kaWRhdGVzIiwgIm5hbWUiLCAicGF0aCIsICJqb2luIiwgInNlc3Npb24iLCAicmVhZEZpbGUiLCAibXRpbWVNcyIsICJzdGF0IiwgImEiLCAiYiIsICJyZWZyZXNoU2Vzc2lvbkJlYXJlciIsICJmZXRjaEZuIiwgIm5vdyIsICJjYWNoZWQiLCAidG9rZW5DYWNoZSIsICJSRUZSRVNIX1NLRVdfTVMiLCAiY29udHJvbGxlciIsICJ0aW1lciIsICJSRUZSRVNIX1RJTUVPVVRfTVMiLCAicmVzIiwgIkZJUkVCQVNFX1JFRlJFU0hfVVJMIiwgImVyciIsICJyZWFzb24iLCAibWVzc2FnZSIsICJyZWplY3RlZCIsICJSRUpFQ1RFRF9SRUZSRVNIX1JFQVNPTlMiLCAiciIsICJib2R5IiwgImlkVG9rZW4iLCAiZXhwaXJlc0luIiwgInJlc29sdmVBcGlLZXkiLCAiZW52IiwgImNyZWRlbnRpYWxzUGF0aCIsICJob21lZGlyIiwgImRpcm5hbWUiLCAiY3dkIiwgIkRFRkFVTFRfQVBJX1VSTCIsICJmcm9tRG90ZW52IiwgImZvdW5kIiwgInJhdyIsICJwYXJzZWQiLCAiREVGQVVMVF9CQVNFX1VSTCIsICJERUZBVUxUX1RJTUVPVVRfTVMiLCAiUk9VVEVfU0NPUEVTIiwgIlJPVVRFX1NDT1BFX1NVTU1BUlkiLCAiYXBpUmVxdWVzdCIsICJwYXRoIiwgIm1ldGhvZCIsICJib2R5IiwgInF1ZXJ5IiwgImZldGNoRm4iLCAiYmFzZVVybCIsICJ0aW1lb3V0TXMiLCAiYXBpS2V5IiwgInJlc29sdmVBcGlLZXkiLCAidXJsIiwgImsiLCAidiIsICJjb250cm9sbGVyIiwgInRpbWVyIiwgInJlcyIsICJyYXciLCAiZnJpZW5kbHlNc2ciLCAicGFyc2VkIiwgImNvZGUiLCAiZXJyIiwgIkxJU1RfRVhUUkFDVE9SUyIsICJyIiwgIlZFUlNJT05TX0VYVFJBQ1RPUlMiLCAiTElTVF9QQVRIUyIsICJleHRyYWN0TGlzdCIsICJ0eXBlIiwgInJlc3BvbnNlIiwgImZuIiwgImV4dHJhY3RWZXJzaW9ucyIsICJleHRyYWN0QWN0aXZlVmVyc2lvbklkIiwgImlkIiwgImlzQWN0aXZlVmVyc2lvbiIsICJ2ZXJzaW9uIiwgImFjdGl2ZUlkIiwgIm93bklkIiwgInZlcnNpb25DcmVhdGVkQXQiLCAicmF3IiwgInZlcnNpb25JZCIsICJTVVBQT1JURURfTElTVF9UWVBFUyIsICJTVVBQT1JURURfVkVSU0lPTl9UWVBFUyIsICJWQUxJRF9UWVBFUyIsICJTVVBQT1JURURfVkVSU0lPTl9UWVBFUyIsICJsaXN0UHJpbWl0aXZlVmVyc2lvbnMiLCAiYWdlbnRJZCIsICJ0eXBlIiwgIm5hbWUiLCAiZGVwcyIsICJwYXRoIiwgImlkIiwgInJlc29sdmVOYW1lVG9JZCIsICJMSVNUX1BBVEhTIiwgImRhdGEiLCAiYXBpUmVxdWVzdCIsICJjb21wYWN0IiwgImV4dHJhY3RWZXJzaW9ucyIsICJ2IiwgInZlcnNpb25JZCIsICJpc0FjdGl2ZVZlcnNpb24iLCAidmVyc2lvbkNyZWF0ZWRBdCIsICJyZXN1bHQiLCAiZXh0cmFjdEFjdGl2ZVZlcnNpb25JZCIsICJmZXRjaEZuIiwgImxpc3RQYXRoIiwgImxpc3RSZXNwb25zZSIsICJpdGVtcyIsICJleHRyYWN0TGlzdCIsICJtYXRjaCIsICJwIiwgImF2YWlsYWJsZSIsICJQUklNSVRJVkVfVFlQRVMiLCAiVkVSU0lPTlNfQ0hVTktfU0laRSIsICJERUZBVUxUX0JVREdFVF9NUyIsICJnZXREZXBsb3ltZW50U3RhdHVzIiwgImFnZW50SWQiLCAiZGVwcyIsICJpZCIsICJidWRnZXRNcyIsICJzdGFydGVkQXQiLCAib3ZlckJ1ZGdldCIsICJnZXQiLCAicGF0aCIsICJhcGlSZXF1ZXN0IiwgInJlc3VsdCIsICJ0eXBlIiwgInBlcnNvbmFPdXRjb21lIiwgImxpc3RPdXRjb21lcyIsICJMSVNUX1BBVEhTIiwgInBlcnNvbmFSZXNwb25zZSIsICJ2ZXJzaW9ucyIsICJleHRyYWN0VmVyc2lvbnMiLCAiYWN0aXZlIiwgInYiLCAiaXNBY3RpdmVWZXJzaW9uIiwgInZlcnNpb25DcmVhdGVkQXQiLCAiYnVkZ2V0RXhoYXVzdGVkIiwgInNraXBwZWQiLCAidG90YWwiLCAidCIsICJvdXRjb21lIiwgInRhcmdldHMiLCAiZXh0cmFjdExpc3QiLCAicCIsICJ0Z3QiLCAiaSIsICJjaHVuayIsICJuYW1lIiwgInByaW1JZCIsICJ2byIsICJqIiwgInZlcnNpb25zUmVzcG9uc2UiLCAiZW50cnkiLCAidmVyc2lvbklkIiwgImV4dHJhY3RBY3RpdmVWZXJzaW9uSWQiLCAiTE9HX1NPVVJDRVMiLCAiTE9HX1RZUEVTIiwgIlZBTElEX1RZUEVTIiwgIlZBTElEX0xPR19UWVBFUyIsICJ0YWlsTG9ncyIsICJhZ2VudElkIiwgInR5cGUiLCAibmFtZSIsICJsb2dUeXBlIiwgImxpbWl0IiwgInBhZ2UiLCAiZGVwcyIsICJxdWVyeSIsICJkYXRhIiwgImFwaVJlcXVlc3QiLCAiVE9PTF9SRUdJU1RSWSIsICJ0b29sc19leHBvcnRzIiwgInNlcnZlciIsICJTZXJ2ZXIiLCAiTGlzdFRvb2xzUmVxdWVzdFNjaGVtYSIsICJ0IiwgIkNhbGxUb29sUmVxdWVzdFNjaGVtYSIsICJyZXEiLCAidG9vbCIsICJlcnIiLCAicmVwb3J0Q3Jhc2hBbmRFeGl0IiwgInBheWxvYWQiLCAicmVhc29uIiwgIlN0ZGlvU2VydmVyVHJhbnNwb3J0Il0KfQo= +var ie=Object.defineProperty;var ae=(e,t)=>{for(var r in t)ie(e,r,{get:t[r],enumerable:!0})};import{Server as $e}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Oe}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as Ue,ListToolsRequestSchema as Ne}from"@modelcontextprotocol/sdk/types.js";var K={};ae(K,{getAgent:()=>W,getDeploymentStatus:()=>te,listAgents:()=>G,listPrimitiveVersions:()=>Q,tailLogs:()=>re});import{spawn as le}from"node:child_process";var ce=6e4;function pe(e,{spawnFn:t=le,timeoutMs:r=ce,label:s="lua"}={}){return new Promise((o,i)=>{let a=t("lua",e,{shell:process.platform==="win32",windowsHide:!0,env:{...process.env,LUA_NO_BANNER:"1",LUA_NO_HINTS:"1"}}),n="",l="";a.stdout.on("data",m=>{n+=m.toString("utf8")}),a.stderr.on("data",m=>{l+=m.toString("utf8")});let c=setTimeout(()=>{a.kill("SIGTERM"),i(new Error(`${s}: 'lua ${e.join(" ")}' timed out after ${r}ms`))},r);a.on("exit",m=>{clearTimeout(c),m!==0?i(new Error(`'lua ${e.join(" ")}' exited ${m}: ${l.trim()}`)):o(n)}),a.on("error",m=>{clearTimeout(c),i(m)})})}function ue(e){let t=e,r=Array.isArray(t)?t:t?.agents??t?.orgs??[];return r.length>0&&r[0]&&Array.isArray(r[0].agents)?r.flatMap(o=>(o.agents??[]).map(i=>({id:i.agentId??i.id??i._id,name:i.name??null,orgId:o.orgId??o.id??o._id??null,orgName:o.name??o.registeredName??null,visibility:i.visibility??null}))):r.map(o=>({id:o.agentId??o.id??o._id,name:o.name??null,orgId:o.orgId??null,orgName:o.orgName??null,visibility:o.visibility??null}))}async function T({spawnFn:e,label:t="list_agents"}={}){let r=await pe(["agents","--json","--ci"],{spawnFn:e,label:t}),s;try{s=JSON.parse(r)}catch(o){throw new Error(`${t}: could not parse 'lua agents --json' output: ${o.message}`)}return ue(s)}var G={spec:{name:"list_agents",description:"List all Lua agents the authenticated credential can reach. Returns a compact array of {id, name, orgId, orgName, visibility} flattened across all organisations. Same data as `lua agents --json` \u2014 it resolves every organisation, so on accounts with many orgs this takes 20 s or more; prefer get_agent when you already know the id.",inputSchema:{type:"object",properties:{},required:[]}},async handler(e,t={}){let r=await T({spawnFn:t.spawnFn,label:"list_agents"});return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}};var W={spec:{name:"get_agent",description:"Look up one Lua agent by ID and return {id, name, orgId, orgName, visibility}. For what is live use get_deployment_status / list_primitive_versions; for logs use tail_logs.",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent ID (the `id` field from list_agents; also `agent.agentId` in lua.skill.yaml)"}},required:["agentId"]}},async handler({agentId:e},t={}){if(!e)throw new Error("agentId is required");let s=(await T({spawnFn:t.spawnFn,label:"get_agent"})).find(o=>o.id===e)??null;if(!s)throw new Error(`get_agent: no agent with id "${e}" found in the authenticated credential's accessible orgs. Run list_agents to see available agents.`);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}};import{readFile as j,readdir as de,stat as me}from"node:fs/promises";import{join as R,dirname as fe}from"node:path";import{homedir as ge}from"node:os";var he="https://api.heylua.ai",we="https://securetoken.googleapis.com/v1/token",ve=15e3,ye=6e4,_e=["TOKEN_EXPIRED","USER_DISABLED","USER_NOT_FOUND","INVALID_REFRESH_TOKEN","INVALID_GRANT_TYPE","MISSING_REFRESH_TOKEN"],X=new Map;function Ee(e){for(let t of e.split(/\r?\n/)){let r=t.match(/^\s*(?:export\s+)?LUA_API_KEY\s*=\s*(.*)$/);if(!r)continue;let s=r[1].trim(),o=s.match(/^(['"`])(.*?)\1(?:\s+#.*)?\s*$/);return o?s=o[2]:s=s.replace(/\s+#.*$/,"").trim(),s||null}return null}async function Se({sessionsDir:e,apiUrl:t}){let r;try{r=await de(e)}catch{return null}let s=[];for(let o of r){if(!o.endsWith(".json")||o.startsWith("."))continue;let i=R(e,o);try{let a=JSON.parse(await j(i,"utf8"));if(a?.kind!=="firebase-session"||a?.version!==1||typeof a.refreshToken!="string"||!a.refreshToken||typeof a.firebaseWebApiKey!="string"||!a.firebaseWebApiKey||a.apiUrl!==t)continue;let{mtimeMs:n}=await me(i);s.push({path:i,session:a,mtimeMs:n})}catch{}}return s.length===0?null:(s.sort((o,i)=>i.mtimeMs-o.mtimeMs),{path:s[0].path,session:s[0].session})}async function Ae({path:e,session:t,fetchFn:r=globalThis.fetch,now:s=Date.now}){let o=X.get(e);if(o&&o.expiresAt-s()>ye)return o.idToken;let i=new AbortController,a=setTimeout(()=>i.abort(),ve),n;try{n=await r(`${we}?key=${encodeURIComponent(t.firebaseWebApiKey)}`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:t.refreshToken}).toString(),signal:i.signal})}catch(p){throw new Error(`MCP_AUTH_UNAVAILABLE: could not refresh the lua-cli session (${p?.name==="AbortError"?"timed out":p.message}). Check your network, or set LUA_API_KEY to bypass the session.`)}finally{clearTimeout(a)}if(!n.ok){let p=n.statusText??String(n.status);try{let d=(await n.json())?.error?.message;typeof d=="string"&&d&&(p=d.split(":")[0].trim())}catch{}let g=n.status<500&&_e.find(u=>p.toUpperCase().includes(u));throw g?new Error(`MCP_AUTH_STALE: your lua-cli session was signed out (${g}). Signing out of the Lua dashboard, desktop or mobile app also ends CLI sessions. Run \`lua auth configure\` in a terminal to sign in again.`):new Error(`MCP_AUTH_UNAVAILABLE: lua-cli session refresh failed (${n.status} ${p}). Retry in a moment.`)}let l=await n.json(),c=l?.id_token,m=Number(l?.expires_in);if(typeof c!="string"||!c||!Number.isFinite(m)||m<=0)throw new Error("MCP_AUTH_UNAVAILABLE: the session refresh returned an invalid token. Run `lua auth configure` again.");return X.set(e,{idToken:c,expiresAt:s()+m*1e3}),c}async function z({env:e=process.env,credentialsPath:t=e.LUA_CREDENTIALS_PATH??R(ge(),".lua-cli","credentials"),sessionsDir:r=e.LUA_SESSIONS_DIR??R(fe(t),"sessions"),cwd:s=process.cwd(),apiUrl:o=e.LUA_API_URL||he,fetchFn:i=globalThis.fetch,now:a=Date.now}={}){if(e.LUA_API_KEY&&e.LUA_API_KEY.trim())return e.LUA_API_KEY.trim();try{let l=Ee(await j(R(s,".env"),"utf8"));if(l)return l}catch{}let n=await Se({sessionsDir:r,apiUrl:o});if(n)return Ae({...n,fetchFn:i,now:a});try{let l=(await j(t,"utf8")).trim();if(l){if(l.startsWith("{"))try{let c=JSON.parse(l);if(c?.apiKey)return c.apiKey}catch{}return l}}catch{}throw new Error("MCP_AUTH_STALE: No lua-cli credentials found in LUA_API_KEY, .env, ~/.lua-cli/sessions, or ~/.lua-cli/credentials. Run `lua auth configure` in a terminal (or /lua-auth in Claude Code).")}var be="https://api.heylua.ai",ke=1e4,Ge=Object.freeze({skills:"automations:read",webhooks:"automations:read",jobs:"automations:read",triggers:"automations:read",preprocessors:"automations:read",postprocessors:"automations:read",workflows:"workflows:read",persona:"agents:read",logs:"knowledge:read"}),Ie="skills/webhooks/jobs/triggers/preprocessors/postprocessors need automations:read, workflows need workflows:read, persona needs agents:read, logs need knowledge:read";async function v(e,{method:t="GET",body:r,query:s,fetchFn:o=globalThis.fetch,baseUrl:i=process.env.LUA_API_URL||be,timeoutMs:a=ke}={}){let n=await z(),l=new URL(i.replace(/\/$/,"")+e);if(s)for(let[p,g]of Object.entries(s))l.searchParams.set(p,String(g));let c=new AbortController,m=setTimeout(()=>c.abort(),a);try{let p=await o(l.toString(),{method:t,headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json","X-Lua-Client":"claude-plugin/1.5.0"},body:r?JSON.stringify(r):void 0,signal:c.signal});if(p.status===401)throw new Error("MCP_AUTH_STALE: lua-api returned 401. The credential is missing, expired, or the session was signed out (signing out of the Lua dashboard or apps also ends CLI sessions). Run `lua auth configure` in a terminal, or re-run /lua-doctor.");if(p.status===403)throw new Error(`MCP_FORBIDDEN: lua-api returned 403 for ${e}. The credential cannot access this agent or org, or it lacks the route scope (${Ie} \u2014 typed keys are scoped to the agents and role chosen at \`lua auth configure\`). Re-run /lua-doctor or check the LUA_API_KEY env var.`);if(!p.ok){let g=await p.text(),u=g;try{let d=JSON.parse(g);u=d?.error?.message??d?.message??g;let h=d?.error?.code;typeof h=="string"&&h&&!String(u).includes(h)&&(u=`${h}: ${u}`)}catch{}throw new Error(`lua-api ${p.status}: ${u}`)}return await p.json()}catch(p){throw p.name==="AbortError"?new Error(`MCP_TIMEOUT: ${e} did not respond in ${a}ms`):p}finally{clearTimeout(m)}}var V={skill:e=>e?.skills??[],webhook:e=>e?.data?.webhooks??[],job:e=>e?.data?.jobs??[],trigger:e=>e?.data?.triggers??[],preprocessor:e=>e?.data?.preprocessors??[],postprocessor:e=>e?.data?.postprocessors??[],workflow:e=>e?.data?.workflows??[]},D={skill:e=>e?.versions??[],webhook:e=>e?.data?.versions??[],job:e=>Array.isArray(e?.data)?e.data:[],trigger:e=>e?.data?.versions??[],preprocessor:e=>e?.data?.versions??[],postprocessor:e=>e?.data?.versions??[],workflow:e=>Array.isArray(e?.data)?e.data:[],persona:e=>e?.versions??[]},_={skill:"skills",webhook:"webhooks",job:"jobs",trigger:"triggers",preprocessor:"preprocessors",postprocessor:"postprocessors",workflow:"workflows"};function L(e,t){let r=V[e];if(!r)throw new Error(`extractList: unknown primitive type "${e}". Valid: ${Object.keys(V).join(", ")}`);return r(t)}function E(e,t){let r=D[e];if(!r)throw new Error(`extractVersions: unknown primitive type "${e}". Valid: ${Object.keys(D).join(", ")}`);return r(t)}function S(e){let t=e?.data?.activeVersionId;return typeof t=="string"&&t?t:null}function A(e,t,r){if(!t||typeof t!="object")return!1;switch(e){case"skill":case"persona":return t.isCurrent===!0;case"job":case"workflow":return t.active===!0;case"webhook":case"trigger":case"preprocessor":case"postprocessor":{if(t.isActive===!0)return!0;let s=S(r),o=t.versionId??t.id??null;return!!s&&o===s}default:return!1}}function b(e){if(!e||typeof e!="object")return null;let t=e.createdAt??e.createdDate??null;return t==null?null:typeof t=="number"?Number.isFinite(t)?new Date(t).toISOString():null:String(t)}function x(e){return!e||typeof e!="object"?null:e.versionId??e.id??null}var Xe=Object.keys(V),Z=Object.keys(D);var F=new Set(Z),Q={spec:{name:"list_primitive_versions",description:"List the server-side versions of one primitive (skill, webhook, job, trigger, preprocessor, postprocessor, workflow) or the agent persona. Returns { activeVersionId, versions: [{version, versionId, active, createdAt}] } \u2014 `active` is the version currently live. `versionId` is null for types whose versions route carries no per-entry id: skill, persona and webhook (the webhook route exposes the row id only under a misnamed `webhookId` key). For those, identify a version by `version`; for webhooks the live row id is the top-level `activeVersionId`. `activeVersionId` is populated only for webhook, trigger, preprocessor and postprocessor envelopes and is null for skill, job, workflow and persona.",inputSchema:{type:"object",properties:{agentId:{type:"string"},type:{type:"string",enum:[...F]},name:{type:"string",description:"Primitive name as written in lua.skill.yaml (omit for persona \u2014 an agent has only one)"}},required:["agentId","type"]}},async handler({agentId:e,type:t,name:r},s={}){if(!e||!t)throw new Error("agentId and type are required");if(!F.has(t))throw new Error(`Invalid type: ${t}. Valid: ${[...F].join(", ")}`);if(t!=="persona"&&!r)throw new Error("name is required for all types except persona");let o;if(t==="persona")o=`/developer/agents/${encodeURIComponent(e)}/persona/versions`;else{let c=await Te({agentId:e,type:t,name:r,fetchFn:s.fetchFn});o=`/developer/${_[t]}/${encodeURIComponent(e)}/${encodeURIComponent(c)}/versions`}let i=await v(o,{fetchFn:s.fetchFn}),n=E(t,i).map(c=>({version:c.version,versionId:x(c),active:A(t,c,i),createdAt:b(c)})),l={agentId:e,type:t,name:t==="persona"?null:r,activeVersionId:S(i),versions:n};return{content:[{type:"text",text:JSON.stringify(l,null,2)}]}}};async function Te({agentId:e,type:t,name:r,fetchFn:s}){let o=`/developer/${_[t]}/${encodeURIComponent(e)}`,i=await v(o,{fetchFn:s}),a=L(t,i),n=a.find(c=>c.name===r);if(!n){let c=a.map(m=>m.name).filter(Boolean).join(", ")||"(none)";throw new Error(`list_primitive_versions: no ${t} named "${r}" on agent ${e}. Available: ${c}`)}let l=n.id??n._id;if(!l)throw new Error(`list_primitive_versions: ${t} "${r}" exists but has no id field`);return l}var $=["skill","webhook","job","trigger","preprocessor","postprocessor","workflow"],ee=5,Re=45e3,te={spec:{name:"get_deployment_status",description:'What is live right now for an agent: for every skill, webhook, job, trigger, preprocessor, postprocessor and workflow the active (deployed) version, plus the active persona version. Composed from the per-type lua-api version routes (list calls in parallel, then per-item version lookups in chunks of 5). Bounded by a 45 s wall-clock budget: if exceeded, remaining items are listed with `error: "skipped: \u2026"` and the result carries `partial: true` + `partialReason`. Inside a project, `lua status --json` additionally compares local vs deployed.',inputSchema:{type:"object",properties:{agentId:{type:"string"}},required:["agentId"]}},async handler({agentId:e},t={}){if(!e)throw new Error("agentId is required");let r=encodeURIComponent(e),s=Number.isFinite(t.budgetMs)?t.budgetMs:Re,o=Date.now(),i=()=>Date.now()-o>s,a=u=>v(u,{fetchFn:t.fetchFn}),n={agentId:e,persona:null,primitives:{}};for(let u of $)n.primitives[u]=[];let[l,...c]=await Promise.allSettled([a(`/developer/agents/${r}/persona/versions`),...$.map(u=>a(`/developer/${_[u]}/${r}`))]);if(l.status==="fulfilled"){let u=l.value,d=E("persona",u),h=d.find(y=>A("persona",y,u))??null;n.persona={activeVersion:h?.version??null,activeVersionCreatedAt:b(h),versionCount:d.length}}else n.persona={error:l.reason?.message??String(l.reason)};let m=!1,p=0,g=0;for(let u=0;u<$.length;u++){let d=$[u],h=_[d],y=c[u];if(y.status!=="fulfilled"){n.primitives[d]={error:y.reason?.message??String(y.reason)};continue}let O=L(d,y.value).map(f=>({p:f,primId:f.id??f._id,name:f.name??f.id??f._id})).filter(f=>f.primId);g+=O.length;for(let f=0;fa(`/developer/${h}/${r}/${encodeURIComponent(w)}/versions`)))).forEach((w,U)=>{let{p:I,primId:q,name:B}=k[U];if(w.status!=="fulfilled"){n.primitives[d].push({name:B,id:q,error:w.reason?.message??String(w.reason)});return}let N=w.value,J=E(d,N),C=J.find(ne=>A(d,ne,N))??null,P={name:B,id:q,activeVersion:C?.version??null,activeVersionId:x(C)??S(N),activeVersionCreatedAt:b(C),versionCount:J.length};typeof I.active=="boolean"&&(P.enabled=I.active),d==="workflow"&&typeof I.dynamic=="boolean"&&(P.dynamic=I.dynamic),n.primitives[d].push(P)})}}return p>0&&(n.partial=!0,n.partialReason=`Wall-clock budget of ${s}ms exceeded after ${Date.now()-o}ms; ${p} of ${g} version lookups were not issued (entries carry error: "skipped: \u2026").`),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}};var Le=["skill","job","webhook","trigger","preprocessor","postprocessor","user_message","agent_response","agent_error","runtime","mcp","rag","device","device-trigger","model-resolver","workflow-step","workflow-script","workflow"],H=["error","warn","info","debug","start","complete"],M=new Set(["all",...Le]),xe=new Set(H),re={spec:{name:"tail_logs",description:"Fetch recent execution logs for an agent (same data as `lua logs --json`). Returns { logs: LogEntry[], pagination }. Each LogEntry has `subType` (error | warn | info | debug | start | complete), `message`, `timestamp`, and `metadata.logSource` / `metadata.primitiveName` \u2014 there is NO `level` field. Filter by source (`type`), primitive (`name`) and/or severity (`logType`, which filters on `subType`).",inputSchema:{type:"object",properties:{agentId:{type:"string"},type:{type:"string",enum:[...M],default:"all",description:"Log source filter (maps to lua-api `logSource`). `all` = no filter."},name:{type:"string",description:"Filter by primitive name (optional; maps to `primitiveName`)"},logType:{type:"string",enum:H,description:"Filter by LogEntry `subType` (optional; maps to lua-api `logType`). Omit for every severity. e.g. `error` to see only failures."},limit:{type:"number",default:50,minimum:1,maximum:100},page:{type:"number",minimum:1,description:"Page number for pagination (optional)"}},required:["agentId"]}},async handler({agentId:e,type:t="all",name:r,logType:s,limit:o=50,page:i},a={}){if(!e)throw new Error("agentId is required");if(!M.has(t))throw new Error(`Invalid type: ${t}. Valid: ${[...M].join(", ")}`);if(s!==void 0&&!xe.has(s))throw new Error(`Invalid logType: ${s}. Valid: ${H.join(", ")}`);if(o>100)throw new Error(`Invalid limit: ${o}. lua-api caps logs at 100 entries per call.`);let n={limit:o};t!=="all"&&(n.logSource=t),r&&(n.primitiveName=r),s&&(n.logType=s),i&&(n.page=i);let l=await v(`/developer/agents/${encodeURIComponent(e)}/logs`,{query:n,fetchFn:a.fetchFn});return{content:[{type:"text",text:JSON.stringify(l,null,2)}]}}};var oe=Object.values(K),Y=new $e({name:"lua-platform",version:"1.5.0"},{capabilities:{tools:{}}});Y.setRequestHandler(Ne,async()=>({tools:oe.map(e=>e.spec)}));Y.setRequestHandler(Ue,async e=>{let t=oe.find(r=>r.spec.name===e.params.name);if(!t)throw new Error(`Unknown tool: ${e.params.name}`);try{return await t.handler(e.params.arguments??{})}catch(r){return{content:[{type:"text",text:`Error: ${r.message}`}],isError:!0}}});function se(e){process.stderr.write(JSON.stringify(e)+` +`,()=>process.exit(1))}process.on("uncaughtException",e=>{se({event:"mcp_server_crash",kind:"uncaughtException",message:e?.message,stack:e?.stack,plugin_version:"1.5.0",lua_cli_version:process.env.LUA_CLI_VERSION??null,platform:process.platform,ts:new Date().toISOString()})});process.on("unhandledRejection",e=>{se({event:"mcp_server_crash",kind:"unhandledRejection",reason:String(e),stack:e?.stack,plugin_version:"1.5.0",lua_cli_version:process.env.LUA_CLI_VERSION??null,platform:process.platform,ts:new Date().toISOString()})});await Y.connect(new Oe); +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL3NlcnZlci5tanMiLCAiLi4vc3JjL3Rvb2xzL2luZGV4Lm1qcyIsICIuLi9zcmMvcnVuLWx1YS5tanMiLCAiLi4vc3JjL3Rvb2xzL2xpc3QtYWdlbnRzLm1qcyIsICIuLi9zcmMvdG9vbHMvZ2V0LWFnZW50Lm1qcyIsICIuLi9zcmMvYXV0aC5tanMiLCAiLi4vc3JjL2FwaS1jbGllbnQubWpzIiwgIi4uL3NyYy9yZXNwb25zZS1zaGFwZXMubWpzIiwgIi4uL3NyYy90b29scy9saXN0LXByaW1pdGl2ZS12ZXJzaW9ucy5tanMiLCAiLi4vc3JjL3Rvb2xzL2dldC1kZXBsb3ltZW50LXN0YXR1cy5tanMiLCAiLi4vc3JjL3Rvb2xzL3RhaWwtbG9ncy5tanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8vIE1DUCBzdGRpbyBzZXJ2ZXIgYm9vdHN0cmFwLiBQZXIgdGVjaCBzcGVjIFx1MDBBNzguMS5cblxuaW1wb3J0IHsgU2VydmVyIH0gZnJvbSAnQG1vZGVsY29udGV4dHByb3RvY29sL3Nkay9zZXJ2ZXIvaW5kZXguanMnO1xuaW1wb3J0IHsgU3RkaW9TZXJ2ZXJUcmFuc3BvcnQgfSBmcm9tICdAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2RrL3NlcnZlci9zdGRpby5qcyc7XG5pbXBvcnQge1xuICBDYWxsVG9vbFJlcXVlc3RTY2hlbWEsXG4gIExpc3RUb29sc1JlcXVlc3RTY2hlbWEsXG59IGZyb20gJ0Btb2RlbGNvbnRleHRwcm90b2NvbC9zZGsvdHlwZXMuanMnO1xuaW1wb3J0ICogYXMgdG9vbHMgZnJvbSAnLi90b29scy9pbmRleC5tanMnO1xuXG5jb25zdCBUT09MX1JFR0lTVFJZID0gT2JqZWN0LnZhbHVlcyh0b29scyk7XG5cbmNvbnN0IHNlcnZlciA9IG5ldyBTZXJ2ZXIoXG4gIHsgbmFtZTogJ2x1YS1wbGF0Zm9ybScsIHZlcnNpb246ICcxLjUuMCcgfSxcbiAgeyBjYXBhYmlsaXRpZXM6IHsgdG9vbHM6IHt9IH0gfVxuKTtcblxuc2VydmVyLnNldFJlcXVlc3RIYW5kbGVyKExpc3RUb29sc1JlcXVlc3RTY2hlbWEsIGFzeW5jICgpID0+ICh7XG4gIHRvb2xzOiBUT09MX1JFR0lTVFJZLm1hcCgodCkgPT4gdC5zcGVjKSxcbn0pKTtcblxuc2VydmVyLnNldFJlcXVlc3RIYW5kbGVyKENhbGxUb29sUmVxdWVzdFNjaGVtYSwgYXN5bmMgKHJlcSkgPT4ge1xuICBjb25zdCB0b29sID0gVE9PTF9SRUdJU1RSWS5maW5kKCh0KSA9PiB0LnNwZWMubmFtZSA9PT0gcmVxLnBhcmFtcy5uYW1lKTtcbiAgaWYgKCF0b29sKSB0aHJvdyBuZXcgRXJyb3IoYFVua25vd24gdG9vbDogJHtyZXEucGFyYW1zLm5hbWV9YCk7XG4gIHRyeSB7XG4gICAgcmV0dXJuIGF3YWl0IHRvb2wuaGFuZGxlcihyZXEucGFyYW1zLmFyZ3VtZW50cyA/PyB7fSk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIHJldHVybiB7XG4gICAgICBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6IGBFcnJvcjogJHtlcnIubWVzc2FnZX1gIH1dLFxuICAgICAgaXNFcnJvcjogdHJ1ZSxcbiAgICB9O1xuICB9XG59KTtcblxuLy8gXHUwMEE3MTkuNiBjcmFzaCByZXBvcnRpbmcgXHUyMDE0IG11c3QgbGFuZCBCRUZPUkUgcHJvY2VzcyBleGl0IHNvIHRoZSBldmVudCByZWFjaGVzXG4vLyB0ZWxlbWV0cnkuIFdpbmRvd3MgY2FuIGRyb3AgdGhlIGxhc3Qgc3RkZXJyIHdyaXRlIGlmIHByb2Nlc3MuZXhpdCBmaXJlc1xuLy8gc3luY2hyb25vdXNseSBhZnRlciBpdDsgc2FtZSBidWcgY2xhc3MgYXMgbGliL2hvb2stcnVudGltZS5tanMncyBleGl0KCkuXG4vLyBFeGl0IG9ubHkgaW5zaWRlIHRoZSB3cml0ZSBjYWxsYmFjayBzbyB0aGUgZmx1c2ggY29tcGxldGVzLlxuZnVuY3Rpb24gcmVwb3J0Q3Jhc2hBbmRFeGl0KHBheWxvYWQpIHtcbiAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkgKyAnXFxuJywgKCkgPT4gcHJvY2Vzcy5leGl0KDEpKTtcbn1cblxucHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCAoZXJyKSA9PiB7XG4gIHJlcG9ydENyYXNoQW5kRXhpdCh7XG4gICAgZXZlbnQ6ICdtY3Bfc2VydmVyX2NyYXNoJyxcbiAgICBraW5kOiAndW5jYXVnaHRFeGNlcHRpb24nLFxuICAgIG1lc3NhZ2U6IGVycj8ubWVzc2FnZSxcbiAgICBzdGFjazogZXJyPy5zdGFjayxcbiAgICBwbHVnaW5fdmVyc2lvbjogJzEuNS4wJyxcbiAgICBsdWFfY2xpX3ZlcnNpb246IHByb2Nlc3MuZW52LkxVQV9DTElfVkVSU0lPTiA/PyBudWxsLFxuICAgIHBsYXRmb3JtOiBwcm9jZXNzLnBsYXRmb3JtLFxuICAgIHRzOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gIH0pO1xufSk7XG5cbnByb2Nlc3Mub24oJ3VuaGFuZGxlZFJlamVjdGlvbicsIChyZWFzb24pID0+IHtcbiAgcmVwb3J0Q3Jhc2hBbmRFeGl0KHtcbiAgICBldmVudDogJ21jcF9zZXJ2ZXJfY3Jhc2gnLFxuICAgIGtpbmQ6ICd1bmhhbmRsZWRSZWplY3Rpb24nLFxuICAgIHJlYXNvbjogU3RyaW5nKHJlYXNvbiksXG4gICAgc3RhY2s6IC8qKiBAdHlwZSB7YW55fSAqLyAocmVhc29uKT8uc3RhY2ssXG4gICAgcGx1Z2luX3ZlcnNpb246ICcxLjUuMCcsXG4gICAgbHVhX2NsaV92ZXJzaW9uOiBwcm9jZXNzLmVudi5MVUFfQ0xJX1ZFUlNJT04gPz8gbnVsbCxcbiAgICBwbGF0Zm9ybTogcHJvY2Vzcy5wbGF0Zm9ybSxcbiAgICB0czogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICB9KTtcbn0pO1xuXG5hd2FpdCBzZXJ2ZXIuY29ubmVjdChuZXcgU3RkaW9TZXJ2ZXJUcmFuc3BvcnQoKSk7XG4iLCAiZXhwb3J0IHsgbGlzdEFnZW50cyB9IGZyb20gJy4vbGlzdC1hZ2VudHMubWpzJztcbmV4cG9ydCB7IGdldEFnZW50IH0gZnJvbSAnLi9nZXQtYWdlbnQubWpzJztcbmV4cG9ydCB7IGxpc3RQcmltaXRpdmVWZXJzaW9ucyB9IGZyb20gJy4vbGlzdC1wcmltaXRpdmUtdmVyc2lvbnMubWpzJztcbmV4cG9ydCB7IGdldERlcGxveW1lbnRTdGF0dXMgfSBmcm9tICcuL2dldC1kZXBsb3ltZW50LXN0YXR1cy5tanMnO1xuZXhwb3J0IHsgdGFpbExvZ3MgfSBmcm9tICcuL3RhaWwtbG9ncy5tanMnO1xuLy8gY2hlY2tfZHJpZnQgcmVtb3ZlZCBpbiB2MS4yNSBcdTIwMTQgdXNlIEJhc2gobHVhIHN5bmMgLS1jaGVjaykgZGlyZWN0bHkuIFRoZVxuLy8gTUNQIHNlcnZlciBpcyBsb2NhbCBhbmQgc28gaXMgdGhlIENMSTsgcmVpbXBsZW1lbnRpbmcgZHJpZnQgZGV0ZWN0aW9uXG4vLyBzZXJ2ZXItc2lkZSBvciBpbiB0aGUgTUNQIHdvdWxkIGR1cGxpY2F0ZSBsb2dpYyB0aGF0IGx1YS1jbGkgYWxyZWFkeSBvd25zLlxuIiwgIi8vIFNoYXJlZCBzdWJwcm9jZXNzIGhlbHBlciBmb3IgdG9vbHMgdGhhdCBzaGVsbCBvdXQgdG8gdGhlIGx1YS1jbGkgYmluYXJ5LlxuLy9cbi8vIFdoeSBzaGVsbCBvdXQgYXQgYWxsOiB0aGVyZSBpcyBubyBcImxpc3QgZXZlcnkgYWdlbnQgdGhpcyBjcmVkZW50aWFsIGNhblxuLy8gcmVhY2hcIiBSRVNUIHJvdXRlIFx1MjAxNCBgbHVhIGFnZW50cyAtLWpzb25gIGFscmVhZHkgZG9lcyB0aGUgY3JlZGVudGlhbC10by1cbi8vIGF1dGhvcml6YXRpb24tcHJvamVjdGlvbiByZXNvbHV0aW9uIChQUk8tMTcxMikgZm9yIGV2ZXJ5IGNyZWRlbnRpYWwgY2xhc3Ncbi8vIChBUEkga2V5LCB0eXBlZCBzY29wZWQga2V5LCByZW5ld2FibGUgc2Vzc2lvbikuIFJlaW1wbGVtZW50aW5nIHRoYXQgaW4gdGhlXG4vLyBNQ1Agd291bGQgZHVwbGljYXRlIGxvZ2ljIGx1YS1jbGkgb3ducy5cblxuaW1wb3J0IHsgc3Bhd24gfSBmcm9tICdub2RlOmNoaWxkX3Byb2Nlc3MnO1xuXG4vLyBgbHVhIGFnZW50c2Agd2Fsa3MgZXZlcnkgb3JnYW5pc2F0aW9uIHRoZSBjcmVkZW50aWFsIGNhbiBzZWUgYW5kLCBmb3IgYVxuLy8gc2Vzc2lvbiBsb2dpbiwgcmVmcmVzaGVzIHRoZSBzZXNzaW9uIGZpcnN0LiBNZWFzdXJlZCAyMDI2LTA5LTEyIG9uIGFcbi8vIDE0NC1vcmcgYWNjb3VudDogMThcdTIwMTMyNSBzLiBUaGUgYnVkZ2V0IG11c3QgY292ZXIgdGhhdC5cbmNvbnN0IERFRkFVTFRfVElNRU9VVF9NUyA9IDYwXzAwMDtcblxuLyoqXG4gKiBSdW4gYGx1YSA8YXJncz5gIGFuZCByZXNvbHZlIHdpdGggaXRzIHN0ZG91dC5cbiAqXG4gKiBPbiBXaW5kb3dzIHRoZSBucG0gYmluIGlzIGEgYGx1YS5jbWRgIHNoaW0sIHdoaWNoIE5vZGUgKFx1MjI2NTE4LjIwIC8gXHUyMjY1MjAuMTIpXG4gKiByZWZ1c2VzIHRvIHNwYXduIHdpdGhvdXQgYSBzaGVsbDsgdGhlIGFyZ3MgYXJlIGZpeGVkIGxpdGVyYWxzIChubyB1c2VyXG4gKiBpbnB1dCksIHNvIGBzaGVsbDogdHJ1ZWAgdGhlcmUgaXMgc2FmZS5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ1tdfSBhcmdzXG4gKiBAcGFyYW0ge3tzcGF3bkZuPzogdHlwZW9mIHNwYXduLCB0aW1lb3V0TXM/OiBudW1iZXIsIGxhYmVsPzogc3RyaW5nfX0gW29wdHNdXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fVxuICovXG5leHBvcnQgZnVuY3Rpb24gcnVuTHVhKGFyZ3MsIHsgc3Bhd25GbiA9IHNwYXduLCB0aW1lb3V0TXMgPSBERUZBVUxUX1RJTUVPVVRfTVMsIGxhYmVsID0gJ2x1YScgfSA9IHt9KSB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3QgY2hpbGQgPSBzcGF3bkZuKCdsdWEnLCBhcmdzLCB7XG4gICAgICBzaGVsbDogcHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJyxcbiAgICAgIHdpbmRvd3NIaWRlOiB0cnVlLFxuICAgICAgZW52OiB7IC4uLnByb2Nlc3MuZW52LCBMVUFfTk9fQkFOTkVSOiAnMScsIExVQV9OT19ISU5UUzogJzEnIH0sXG4gICAgfSk7XG4gICAgbGV0IHN0ZG91dCA9ICcnO1xuICAgIGxldCBzdGRlcnIgPSAnJztcbiAgICBjaGlsZC5zdGRvdXQub24oJ2RhdGEnLCAoZCkgPT4geyBzdGRvdXQgKz0gZC50b1N0cmluZygndXRmOCcpOyB9KTtcbiAgICBjaGlsZC5zdGRlcnIub24oJ2RhdGEnLCAoZCkgPT4geyBzdGRlcnIgKz0gZC50b1N0cmluZygndXRmOCcpOyB9KTtcblxuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBjaGlsZC5raWxsKCdTSUdURVJNJyk7XG4gICAgICByZWplY3QobmV3IEVycm9yKGAke2xhYmVsfTogJ2x1YSAke2FyZ3Muam9pbignICcpfScgdGltZWQgb3V0IGFmdGVyICR7dGltZW91dE1zfW1zYCkpO1xuICAgIH0sIHRpbWVvdXRNcyk7XG5cbiAgICBjaGlsZC5vbignZXhpdCcsIChjb2RlKSA9PiB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgICAgaWYgKGNvZGUgIT09IDApIHJlamVjdChuZXcgRXJyb3IoYCdsdWEgJHthcmdzLmpvaW4oJyAnKX0nIGV4aXRlZCAke2NvZGV9OiAke3N0ZGVyci50cmltKCl9YCkpO1xuICAgICAgZWxzZSByZXNvbHZlKHN0ZG91dCk7XG4gICAgfSk7XG4gICAgY2hpbGQub24oJ2Vycm9yJywgKGVycikgPT4ge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgIHJlamVjdChlcnIpO1xuICAgIH0pO1xuICB9KTtcbn1cblxuLyoqXG4gKiBGbGF0dGVuIGBsdWEgYWdlbnRzIC0tanNvbmAgb3V0cHV0IChsdWEtY2xpIFx1MjI2NSAzLjI4OiBhbiBhcnJheSBvZlxuICogQ3JlZGVudGlhbE9yZ2FuaXphdGlvbiByb3dzIFx1MjAxNCBgeyBvcmdJZCwgbmFtZT8sIGFyY2hpdmVkLCBkaXNjb3ZlcmVkVmlhLFxuICogYWdlbnRzOiBbeyBhZ2VudElkLCBuYW1lPywgdmlzaWJpbGl0eSwgZGlzcGxheVJvbGVzIH1dIH1gLCBzZWVcbiAqIHBhY2thZ2VzL2x1YS1jbGkvc3JjL3NlcnZpY2VzL2NyZWRlbnRpYWwtb3BlcmF0aW9uYWwtY29udGV4dC50cykgaW50byBhXG4gKiBjb21wYWN0IGFnZW50IGxpc3QuIE9sZGVyIGZsYXQgc2hhcGVzIGFyZSB0b2xlcmF0ZWQgZm9yIGZvcndhcmQtY29tcGF0LlxuICpcbiAqIEBwYXJhbSB7dW5rbm93bn0gcGFyc2VkXG4gKiBAcmV0dXJucyB7QXJyYXk8e2lkOiBzdHJpbmcsIG5hbWU6IHN0cmluZ3xudWxsLCBvcmdJZDogc3RyaW5nfG51bGwsIG9yZ05hbWU6IHN0cmluZ3xudWxsLCB2aXNpYmlsaXR5OiBzdHJpbmd8bnVsbH0+fVxuICovXG5leHBvcnQgZnVuY3Rpb24gZmxhdHRlbkFnZW50cyhwYXJzZWQpIHtcbiAgY29uc3Qgb2JqID0gLyoqIEB0eXBlIHthbnl9ICovIChwYXJzZWQpO1xuICBjb25zdCBlbnRyaWVzID0gQXJyYXkuaXNBcnJheShvYmopID8gb2JqIDogKG9iaj8uYWdlbnRzID8/IG9iaj8ub3JncyA/PyBbXSk7XG4gIGNvbnN0IGxvb2tzTGlrZU9yZ3MgPSBlbnRyaWVzLmxlbmd0aCA+IDAgJiYgZW50cmllc1swXSAmJiBBcnJheS5pc0FycmF5KGVudHJpZXNbMF0uYWdlbnRzKTtcblxuICBpZiAobG9va3NMaWtlT3Jncykge1xuICAgIHJldHVybiBlbnRyaWVzLmZsYXRNYXAoKG9yZykgPT4gKG9yZy5hZ2VudHMgPz8gW10pLm1hcCgoYSkgPT4gKHtcbiAgICAgIGlkOiBhLmFnZW50SWQgPz8gYS5pZCA/PyBhLl9pZCxcbiAgICAgIG5hbWU6IGEubmFtZSA/PyBudWxsLFxuICAgICAgb3JnSWQ6IG9yZy5vcmdJZCA/PyBvcmcuaWQgPz8gb3JnLl9pZCA/PyBudWxsLFxuICAgICAgb3JnTmFtZTogb3JnLm5hbWUgPz8gb3JnLnJlZ2lzdGVyZWROYW1lID8/IG51bGwsXG4gICAgICB2aXNpYmlsaXR5OiBhLnZpc2liaWxpdHkgPz8gbnVsbCxcbiAgICB9KSkpO1xuICB9XG4gIHJldHVybiBlbnRyaWVzLm1hcCgoYSkgPT4gKHtcbiAgICBpZDogYS5hZ2VudElkID8/IGEuaWQgPz8gYS5faWQsXG4gICAgbmFtZTogYS5uYW1lID8/IG51bGwsXG4gICAgb3JnSWQ6IGEub3JnSWQgPz8gbnVsbCxcbiAgICBvcmdOYW1lOiBhLm9yZ05hbWUgPz8gbnVsbCxcbiAgICB2aXNpYmlsaXR5OiBhLnZpc2liaWxpdHkgPz8gbnVsbCxcbiAgfSkpO1xufVxuXG4vKipcbiAqIFJ1biBgbHVhIGFnZW50cyAtLWpzb24gLS1jaWAgYW5kIHJldHVybiB0aGUgZmxhdHRlbmVkIGFnZW50IGxpc3QuXG4gKlxuICogQHBhcmFtIHt7c3Bhd25Gbj86IHR5cGVvZiBzcGF3biwgbGFiZWw/OiBzdHJpbmd9fSBbb3B0c11cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxpc3RBZ2VudHNWaWFDbGkoeyBzcGF3bkZuLCBsYWJlbCA9ICdsaXN0X2FnZW50cycgfSA9IHt9KSB7XG4gIGNvbnN0IHN0ZG91dCA9IGF3YWl0IHJ1bkx1YShbJ2FnZW50cycsICctLWpzb24nLCAnLS1jaSddLCB7IHNwYXduRm4sIGxhYmVsIH0pO1xuICBsZXQgcGFyc2VkO1xuICB0cnkge1xuICAgIHBhcnNlZCA9IEpTT04ucGFyc2Uoc3Rkb3V0KTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGAke2xhYmVsfTogY291bGQgbm90IHBhcnNlICdsdWEgYWdlbnRzIC0tanNvbicgb3V0cHV0OiAke2Vyci5tZXNzYWdlfWApO1xuICB9XG4gIHJldHVybiBmbGF0dGVuQWdlbnRzKHBhcnNlZCk7XG59XG4iLCAiaW1wb3J0IHsgbGlzdEFnZW50c1ZpYUNsaSB9IGZyb20gJy4uL3J1bi1sdWEubWpzJztcblxuLy8gU2hlbGxzIG91dCB0byBgbHVhIGFnZW50cyAtLWpzb24gLS1jaWAgKHNlZSBydW4tbHVhLm1qcyBmb3Igd2h5KS4gVGhlIENMSVxuLy8gZW1pdHMgb25lIHJvdyBwZXIgb3JnYW5pc2F0aW9uIHdpdGggYSBuZXN0ZWQgYGFnZW50c2AgYXJyYXk7IHdlIGZsYXR0ZW4uXG5cbmV4cG9ydCBjb25zdCBsaXN0QWdlbnRzID0ge1xuICBzcGVjOiB7XG4gICAgbmFtZTogJ2xpc3RfYWdlbnRzJyxcbiAgICBkZXNjcmlwdGlvbjogJ0xpc3QgYWxsIEx1YSBhZ2VudHMgdGhlIGF1dGhlbnRpY2F0ZWQgY3JlZGVudGlhbCBjYW4gcmVhY2guIFJldHVybnMgYSBjb21wYWN0IGFycmF5IG9mIHtpZCwgbmFtZSwgb3JnSWQsIG9yZ05hbWUsIHZpc2liaWxpdHl9IGZsYXR0ZW5lZCBhY3Jvc3MgYWxsIG9yZ2FuaXNhdGlvbnMuIFNhbWUgZGF0YSBhcyBgbHVhIGFnZW50cyAtLWpzb25gIFx1MjAxNCBpdCByZXNvbHZlcyBldmVyeSBvcmdhbmlzYXRpb24sIHNvIG9uIGFjY291bnRzIHdpdGggbWFueSBvcmdzIHRoaXMgdGFrZXMgMjAgcyBvciBtb3JlOyBwcmVmZXIgZ2V0X2FnZW50IHdoZW4geW91IGFscmVhZHkga25vdyB0aGUgaWQuJyxcbiAgICBpbnB1dFNjaGVtYToge1xuICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICBwcm9wZXJ0aWVzOiB7fSxcbiAgICAgIHJlcXVpcmVkOiBbXSxcbiAgICB9LFxuICB9LFxuICBhc3luYyBoYW5kbGVyKF9hcmdzLCBkZXBzID0ge30pIHtcbiAgICBjb25zdCBjb21wYWN0ID0gYXdhaXQgbGlzdEFnZW50c1ZpYUNsaSh7IHNwYXduRm46IGRlcHMuc3Bhd25GbiwgbGFiZWw6ICdsaXN0X2FnZW50cycgfSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGNvbnRlbnQ6IFt7IHR5cGU6ICd0ZXh0JywgdGV4dDogSlNPTi5zdHJpbmdpZnkoY29tcGFjdCwgbnVsbCwgMikgfV0sXG4gICAgfTtcbiAgfSxcbn07XG4iLCAiaW1wb3J0IHsgbGlzdEFnZW50c1ZpYUNsaSB9IGZyb20gJy4uL3J1bi1sdWEubWpzJztcblxuLy8gVGhlcmUgaXMgbm8gY29uc29saWRhdGVkIGBHRVQgL2RldmVsb3Blci9hZ2VudHMvOmFnZW50SWRgIHJvdXRlLCBhbmQgdGhlXG4vLyBwdWJsaWMgYC9wdWJsaWMvYWdlbnRzLzphZ2VudElkYCByb3V0ZSBpcyBvcmlnaW4tZ3VhcmRlZCAoYnJvd3NlciBvbmx5KS5cbi8vIFRoZSBhZ2VudCdzIGlkZW50aXR5IChpZCwgbmFtZSwgb3JnLCB2aXNpYmlsaXR5KSBJUyBpbiBgbHVhIGFnZW50cyAtLWpzb25gLFxuLy8gc28gd2Ugc2hlbGwgb3V0IGFuZCBmaWx0ZXIgXHUyMDE0IHNhbWUgcGF0aCBhcyBsaXN0X2FnZW50cy4gUGVyc29uYSwgbW9kZWwgYW5kXG4vLyBlbnYgbGl2ZSBiZWhpbmQgc2VwYXJhdGUgcGVyLXJlc291cmNlIHJvdXRlczsgdXNlIHRoZSBkZWRpY2F0ZWQgdG9vbHMgKG9yXG4vLyBgbHVhIHN0YXR1cyAtLWpzb25gIGluc2lkZSB0aGUgcHJvamVjdCkgZm9yIHRob3NlLlxuXG5leHBvcnQgY29uc3QgZ2V0QWdlbnQgPSB7XG4gIHNwZWM6IHtcbiAgICBuYW1lOiAnZ2V0X2FnZW50JyxcbiAgICBkZXNjcmlwdGlvbjogJ0xvb2sgdXAgb25lIEx1YSBhZ2VudCBieSBJRCBhbmQgcmV0dXJuIHtpZCwgbmFtZSwgb3JnSWQsIG9yZ05hbWUsIHZpc2liaWxpdHl9LiBGb3Igd2hhdCBpcyBsaXZlIHVzZSBnZXRfZGVwbG95bWVudF9zdGF0dXMgLyBsaXN0X3ByaW1pdGl2ZV92ZXJzaW9uczsgZm9yIGxvZ3MgdXNlIHRhaWxfbG9ncy4nLFxuICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgYWdlbnRJZDogeyB0eXBlOiAnc3RyaW5nJywgZGVzY3JpcHRpb246ICdBZ2VudCBJRCAodGhlIGBpZGAgZmllbGQgZnJvbSBsaXN0X2FnZW50czsgYWxzbyBgYWdlbnQuYWdlbnRJZGAgaW4gbHVhLnNraWxsLnlhbWwpJyB9LFxuICAgICAgfSxcbiAgICAgIHJlcXVpcmVkOiBbJ2FnZW50SWQnXSxcbiAgICB9LFxuICB9LFxuICBhc3luYyBoYW5kbGVyKHsgYWdlbnRJZCB9LCBkZXBzID0ge30pIHtcbiAgICBpZiAoIWFnZW50SWQpIHRocm93IG5ldyBFcnJvcignYWdlbnRJZCBpcyByZXF1aXJlZCcpO1xuICAgIGNvbnN0IGFnZW50cyA9IGF3YWl0IGxpc3RBZ2VudHNWaWFDbGkoeyBzcGF3bkZuOiBkZXBzLnNwYXduRm4sIGxhYmVsOiAnZ2V0X2FnZW50JyB9KTtcbiAgICBjb25zdCBtYXRjaCA9IGFnZW50cy5maW5kKChhKSA9PiBhLmlkID09PSBhZ2VudElkKSA/PyBudWxsO1xuXG4gICAgaWYgKCFtYXRjaCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBnZXRfYWdlbnQ6IG5vIGFnZW50IHdpdGggaWQgXCIke2FnZW50SWR9XCIgZm91bmQgaW4gdGhlIGF1dGhlbnRpY2F0ZWQgY3JlZGVudGlhbCdzIGFjY2Vzc2libGUgb3Jncy4gUnVuIGxpc3RfYWdlbnRzIHRvIHNlZSBhdmFpbGFibGUgYWdlbnRzLmApO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6IEpTT04uc3RyaW5naWZ5KG1hdGNoLCBudWxsLCAyKSB9XSxcbiAgICB9O1xuICB9LFxufTtcbiIsICIvLyBDcmVkZW50aWFsIHJlc29sdXRpb24gZm9yIHRoZSBNQ1Agc2VydmVyLiBSZXNvbHZlZCBvbiBFVkVSWSBjYWxsLCBub3QgYXRcbi8vIHNlcnZlciBzdGFydHVwIFx1MjAxNCBzdXBwb3J0cyB0aGUgZmxvdyB3aGVyZSBhIHVzZXIgcmUtcnVucyBgbHVhIGF1dGggY29uZmlndXJlYFxuLy8gbWlkLXNlc3Npb24uXG4vL1xuLy8gVGhlIGNoYWluIE1VU1QgbWF0Y2ggbHVhLWNsaSdzIGByZXNvbHZlUmVxdWVzdENyZWRlbnRpYWwoKWBcbi8vIChwYWNrYWdlcy9sdWEtY2xpL3NyYy9zZXJ2aWNlcy9yZXF1ZXN0LWNyZWRlbnRpYWwudHMsIGx1YS1jbGkgMy4zMy4wKTpcbi8vXG4vLyAgIDEuIExVQV9BUElfS0VZIGluIHRoZSBwcm9jZXNzIGVudmlyb25tZW50LiBsdWEtY2xpIGxvYWRzIGAuLy5lbnZgIGludG9cbi8vICAgICAgcHJvY2Vzcy5lbnYgZmlyc3QgKGBpbXBvcnQgJ2RvdGVudi9jb25maWcnYCksIHNvIGEgYC5lbnZgIHZhbHVlIGluIHRoZVxuLy8gICAgICB3b3JraW5nIGRpcmVjdG9yeSBpcyBwYXJ0IG9mIHRoaXMgdGllciBcdTIwMTQgaXQgYmVhdHMgYSBzdG9yZWQgc2Vzc2lvbi5cbi8vICAgMi4gVGhlIHJlbmV3YWJsZSBmaXJzdC1wYXJ0eSBzZXNzaW9uIGBsdWEgYXV0aCBjb25maWd1cmVgIChlbWFpbCArIE9UUClcbi8vICAgICAgd3JpdGVzIHRvIH4vLmx1YS1jbGkvc2Vzc2lvbnMvPGVudi1oYXNoPi5qc29uLiBUaGlzIGlzIHRoZSBERUZBVUxUXG4vLyAgICAgIGxvZ2luIHNpbmNlIGx1YS1jbGkgMy4yOSBhbmQgaXQgREVMRVRFUyB+Ly5sdWEtY2xpL2NyZWRlbnRpYWxzLCBzbyBhblxuLy8gICAgICBNQ1Agc2VydmVyIHRoYXQgb25seSByZWFkIHRoZSBjcmVkZW50aWFscyBmaWxlIGZhaWxlZCBmb3IgZXZlcnlcbi8vICAgICAgc2Vzc2lvbiB1c2VyLiBUaGUgZmlsZSBob2xkcyBhIEZpcmViYXNlIHJlZnJlc2ggdG9rZW47IHRoZSBiZWFyZXIgaXNcbi8vICAgICAgYSBzaG9ydC1saXZlZCBJRCB0b2tlbiBvYnRhaW5lZCBmcm9tIEdvb2dsZSdzIHNlY3VyZXRva2VuIGVuZHBvaW50LlxuLy8gICAzLiB+Ly5sdWEtY2xpL2NyZWRlbnRpYWxzIFx1MjAxNCBhIHBsYWluLXRleHQgQVBJIGtleSAodHlwZWQgYGFwaV88dXVpZD4uPDQzPmBcbi8vICAgICAgb3IgbGVnYWN5KSwgd3JpdHRlbiB3aGVuIHRoZSB1c2VyIHBpY2tzIHRoZSBBUEkta2V5IG9wdGlvbiBvZlxuLy8gICAgICBgbHVhIGF1dGggY29uZmlndXJlYC5cbi8vXG4vLyBUaGlzIGZpbGUgZGVsaWJlcmF0ZWx5IGR1cGxpY2F0ZXMgbGliL2NyZWRlbnRpYWxzLm1qcyBiZWNhdXNlIHRoZSBNQ1Bcbi8vIHNlcnZlciBpcyBhIHNlcGFyYXRlIG5wbSBwYWNrYWdlIGFuZCBjYW4ndCByZWFjaCBpbnRvIHRoZSBwbHVnaW4ncyBsaWIvLlxuXG5pbXBvcnQgeyByZWFkRmlsZSwgcmVhZGRpciwgc3RhdCB9IGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHsgam9pbiwgZGlybmFtZSB9IGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBob21lZGlyIH0gZnJvbSAnbm9kZTpvcyc7XG5cbmNvbnN0IERFRkFVTFRfQVBJX1VSTCA9ICdodHRwczovL2FwaS5oZXlsdWEuYWknO1xuY29uc3QgRklSRUJBU0VfUkVGUkVTSF9VUkwgPSAnaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGVhcGlzLmNvbS92MS90b2tlbic7XG5jb25zdCBSRUZSRVNIX1RJTUVPVVRfTVMgPSAxNV8wMDA7XG5jb25zdCBSRUZSRVNIX1NLRVdfTVMgPSA2MF8wMDA7XG5cbi8vIEdvb2dsZSdzIHZlcmRpY3RzIHRoYXQgdGhlIHN0b3JlZCByZWZyZXNoIHRva2VuIGNhbiBuZXZlciB3b3JrIGFnYWluLlxuLy8gTWlycm9ycyBsdWEtY2xpJ3MgUkVKRUNURURfUkVGUkVTSF9SRUFTT05TIChzZXJ2aWNlcy9maXJlYmFzZS1zZXNzaW9uLnRzKS5cbmNvbnN0IFJFSkVDVEVEX1JFRlJFU0hfUkVBU09OUyA9IFtcbiAgJ1RPS0VOX0VYUElSRUQnLFxuICAnVVNFUl9ESVNBQkxFRCcsXG4gICdVU0VSX05PVF9GT1VORCcsXG4gICdJTlZBTElEX1JFRlJFU0hfVE9LRU4nLFxuICAnSU5WQUxJRF9HUkFOVF9UWVBFJyxcbiAgJ01JU1NJTkdfUkVGUkVTSF9UT0tFTicsXG5dO1xuXG4vLyBJbi1wcm9jZXNzIGNhY2hlIG9mIHJlZnJlc2hlZCBJRCB0b2tlbnMsIGtleWVkIGJ5IHNlc3Npb24gZmlsZSBwYXRoLlxuY29uc3QgdG9rZW5DYWNoZSA9IG5ldyBNYXAoKTtcblxuLyoqIEV4cG9ydGVkIGZvciB0ZXN0cy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjbGVhclRva2VuQ2FjaGUoKSB7XG4gIHRva2VuQ2FjaGUuY2xlYXIoKTtcbn1cblxuLyoqXG4gKiBQYXJzZSBhIGRvdGVudi1zdHlsZSBmaWxlIGZvciBMVUFfQVBJX0tFWSB0aGUgd2F5IGRvdGVudiBkb2VzOiBvcHRpb25hbFxuICogYGV4cG9ydCBgIHByZWZpeCwgc3Vycm91bmRpbmcgc2luZ2xlL2RvdWJsZSBxdW90ZXMgc3RyaXBwZWQsIGEgdHJhaWxpbmdcbiAqIGAgIyBjb21tZW50YCBkcm9wcGVkIGZyb20gdW5xdW90ZWQgdmFsdWVzLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBjb250ZW50XG4gKiBAcmV0dXJucyB7c3RyaW5nfG51bGx9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZURvdGVudkFwaUtleShjb250ZW50KSB7XG4gIGZvciAoY29uc3QgcmF3TGluZSBvZiBjb250ZW50LnNwbGl0KC9cXHI/XFxuLykpIHtcbiAgICBjb25zdCBtID0gcmF3TGluZS5tYXRjaCgvXlxccyooPzpleHBvcnRcXHMrKT9MVUFfQVBJX0tFWVxccyo9XFxzKiguKikkLyk7XG4gICAgaWYgKCFtKSBjb250aW51ZTtcbiAgICBsZXQgdmFsdWUgPSBtWzFdLnRyaW0oKTtcbiAgICBjb25zdCBxdW90ZWQgPSB2YWx1ZS5tYXRjaCgvXihbJ1wiYF0pKC4qPylcXDEoPzpcXHMrIy4qKT9cXHMqJC8pO1xuICAgIGlmIChxdW90ZWQpIHZhbHVlID0gcXVvdGVkWzJdO1xuICAgIGVsc2UgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKC9cXHMrIy4qJC8sICcnKS50cmltKCk7XG4gICAgcmV0dXJuIHZhbHVlIHx8IG51bGw7XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogTG9jYXRlIHRoZSBsdWEtY2xpIHNlc3Npb24gZmlsZSBmb3IgdGhlIEFQSSBiYXNlIFVSTCBpbiB1c2UuIGx1YS1jbGkgbmFtZXNcbiAqIHRoZSBmaWxlIGJ5IGEgaGFzaCBvZiAoYXBpVXJsLCBhdXRoVXJsLCBmaXJlYmFzZVdlYkFwaUtleSk7IHJhdGhlciB0aGFuXG4gKiByZWNvbXB1dGUgaXQgd2Ugc2NhbiB0aGUgZGlyZWN0b3J5IGZvciBhIGBmaXJlYmFzZS1zZXNzaW9uYCByZWNvcmQgd2hvc2VcbiAqIGBhcGlVcmxgIG1hdGNoZXMsIG5ld2VzdCBmaXJzdC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx7cGF0aDogc3RyaW5nLCBzZXNzaW9uOiBvYmplY3R9fG51bGw+fVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmluZFNlc3Npb25GaWxlKHsgc2Vzc2lvbnNEaXIsIGFwaVVybCB9KSB7XG4gIGxldCBuYW1lcztcbiAgdHJ5IHtcbiAgICBuYW1lcyA9IGF3YWl0IHJlYWRkaXIoc2Vzc2lvbnNEaXIpO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjb25zdCBjYW5kaWRhdGVzID0gW107XG4gIGZvciAoY29uc3QgbmFtZSBvZiBuYW1lcykge1xuICAgIGlmICghbmFtZS5lbmRzV2l0aCgnLmpzb24nKSB8fCBuYW1lLnN0YXJ0c1dpdGgoJy4nKSkgY29udGludWU7XG4gICAgY29uc3QgcGF0aCA9IGpvaW4oc2Vzc2lvbnNEaXIsIG5hbWUpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzZXNzaW9uID0gSlNPTi5wYXJzZShhd2FpdCByZWFkRmlsZShwYXRoLCAndXRmOCcpKTtcbiAgICAgIGlmIChzZXNzaW9uPy5raW5kICE9PSAnZmlyZWJhc2Utc2Vzc2lvbicgfHwgc2Vzc2lvbj8udmVyc2lvbiAhPT0gMSkgY29udGludWU7XG4gICAgICBpZiAodHlwZW9mIHNlc3Npb24ucmVmcmVzaFRva2VuICE9PSAnc3RyaW5nJyB8fCAhc2Vzc2lvbi5yZWZyZXNoVG9rZW4pIGNvbnRpbnVlO1xuICAgICAgaWYgKHR5cGVvZiBzZXNzaW9uLmZpcmViYXNlV2ViQXBpS2V5ICE9PSAnc3RyaW5nJyB8fCAhc2Vzc2lvbi5maXJlYmFzZVdlYkFwaUtleSkgY29udGludWU7XG4gICAgICBpZiAoc2Vzc2lvbi5hcGlVcmwgIT09IGFwaVVybCkgY29udGludWU7XG4gICAgICBjb25zdCB7IG10aW1lTXMgfSA9IGF3YWl0IHN0YXQocGF0aCk7XG4gICAgICBjYW5kaWRhdGVzLnB1c2goeyBwYXRoLCBzZXNzaW9uLCBtdGltZU1zIH0pO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gdW5yZWFkYWJsZSAvIG1hbGZvcm1lZCBcdTIwMTQgc2tpcCwgbHVhLWNsaSB3aWxsIGNvbXBsYWluIG9uIGl0cyBvd25cbiAgICB9XG4gIH1cbiAgaWYgKGNhbmRpZGF0ZXMubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbDtcbiAgY2FuZGlkYXRlcy5zb3J0KChhLCBiKSA9PiBiLm10aW1lTXMgLSBhLm10aW1lTXMpO1xuICByZXR1cm4geyBwYXRoOiBjYW5kaWRhdGVzWzBdLnBhdGgsIHNlc3Npb246IGNhbmRpZGF0ZXNbMF0uc2Vzc2lvbiB9O1xufVxuXG4vKipcbiAqIEV4Y2hhbmdlIHRoZSBzdG9yZWQgcmVmcmVzaCB0b2tlbiBmb3IgYSBGaXJlYmFzZSBJRCB0b2tlbiAodGhlIGJlYXJlclxuICogbHVhLWFwaSBhY2NlcHRzKS4gQ2FjaGVkIGluLXByb2Nlc3MgdW50aWwgNjAgcyBiZWZvcmUgZXhwaXJ5LiBUaGUgc2Vzc2lvblxuICogZmlsZSBpcyBuZXZlciB3cml0dGVuOiBsdWEtY2xpIG93bnMgaXQuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gYmVhcmVyIHRva2VuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWZyZXNoU2Vzc2lvbkJlYXJlcih7IHBhdGgsIHNlc3Npb24sIGZldGNoRm4gPSBnbG9iYWxUaGlzLmZldGNoLCBub3cgPSBEYXRlLm5vdyB9KSB7XG4gIGNvbnN0IGNhY2hlZCA9IHRva2VuQ2FjaGUuZ2V0KHBhdGgpO1xuICBpZiAoY2FjaGVkICYmIGNhY2hlZC5leHBpcmVzQXQgLSBub3coKSA+IFJFRlJFU0hfU0tFV19NUykgcmV0dXJuIGNhY2hlZC5pZFRva2VuO1xuXG4gIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiBjb250cm9sbGVyLmFib3J0KCksIFJFRlJFU0hfVElNRU9VVF9NUyk7XG4gIGxldCByZXM7XG4gIHRyeSB7XG4gICAgcmVzID0gYXdhaXQgZmV0Y2hGbihgJHtGSVJFQkFTRV9SRUZSRVNIX1VSTH0/a2V5PSR7ZW5jb2RlVVJJQ29tcG9uZW50KHNlc3Npb24uZmlyZWJhc2VXZWJBcGlLZXkpfWAsIHtcbiAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcgfSxcbiAgICAgIGJvZHk6IG5ldyBVUkxTZWFyY2hQYXJhbXMoeyBncmFudF90eXBlOiAncmVmcmVzaF90b2tlbicsIHJlZnJlc2hfdG9rZW46IHNlc3Npb24ucmVmcmVzaFRva2VuIH0pLnRvU3RyaW5nKCksXG4gICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgTUNQX0FVVEhfVU5BVkFJTEFCTEU6IGNvdWxkIG5vdCByZWZyZXNoIHRoZSBsdWEtY2xpIHNlc3Npb24gKCR7ZXJyPy5uYW1lID09PSAnQWJvcnRFcnJvcicgPyAndGltZWQgb3V0JyA6IGVyci5tZXNzYWdlfSkuIGAgK1xuICAgICAgJ0NoZWNrIHlvdXIgbmV0d29yaywgb3Igc2V0IExVQV9BUElfS0VZIHRvIGJ5cGFzcyB0aGUgc2Vzc2lvbi4nXG4gICAgKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICB9XG5cbiAgaWYgKCFyZXMub2spIHtcbiAgICBsZXQgcmVhc29uID0gcmVzLnN0YXR1c1RleHQgPz8gU3RyaW5nKHJlcy5zdGF0dXMpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBib2R5ID0gYXdhaXQgcmVzLmpzb24oKTtcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPSBib2R5Py5lcnJvcj8ubWVzc2FnZTtcbiAgICAgIGlmICh0eXBlb2YgbWVzc2FnZSA9PT0gJ3N0cmluZycgJiYgbWVzc2FnZSkgcmVhc29uID0gbWVzc2FnZS5zcGxpdCgnOicpWzBdLnRyaW0oKTtcbiAgICB9IGNhdGNoIHsgLyoga2VlcCBzdGF0dXNUZXh0ICovIH1cbiAgICBjb25zdCByZWplY3RlZCA9IHJlcy5zdGF0dXMgPCA1MDAgJiYgUkVKRUNURURfUkVGUkVTSF9SRUFTT05TLmZpbmQoKHIpID0+IHJlYXNvbi50b1VwcGVyQ2FzZSgpLmluY2x1ZGVzKHIpKTtcbiAgICBpZiAocmVqZWN0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYE1DUF9BVVRIX1NUQUxFOiB5b3VyIGx1YS1jbGkgc2Vzc2lvbiB3YXMgc2lnbmVkIG91dCAoJHtyZWplY3RlZH0pLiBgICtcbiAgICAgICAgJ1NpZ25pbmcgb3V0IG9mIHRoZSBMdWEgZGFzaGJvYXJkLCBkZXNrdG9wIG9yIG1vYmlsZSBhcHAgYWxzbyBlbmRzIENMSSBzZXNzaW9ucy4gJyArXG4gICAgICAgICdSdW4gYGx1YSBhdXRoIGNvbmZpZ3VyZWAgaW4gYSB0ZXJtaW5hbCB0byBzaWduIGluIGFnYWluLidcbiAgICAgICk7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgTUNQX0FVVEhfVU5BVkFJTEFCTEU6IGx1YS1jbGkgc2Vzc2lvbiByZWZyZXNoIGZhaWxlZCAoJHtyZXMuc3RhdHVzfSAke3JlYXNvbn0pLiBSZXRyeSBpbiBhIG1vbWVudC5gKTtcbiAgfVxuXG4gIGNvbnN0IGJvZHkgPSBhd2FpdCByZXMuanNvbigpO1xuICBjb25zdCBpZFRva2VuID0gYm9keT8uaWRfdG9rZW47XG4gIGNvbnN0IGV4cGlyZXNJbiA9IE51bWJlcihib2R5Py5leHBpcmVzX2luKTtcbiAgaWYgKHR5cGVvZiBpZFRva2VuICE9PSAnc3RyaW5nJyB8fCAhaWRUb2tlbiB8fCAhTnVtYmVyLmlzRmluaXRlKGV4cGlyZXNJbikgfHwgZXhwaXJlc0luIDw9IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ01DUF9BVVRIX1VOQVZBSUxBQkxFOiB0aGUgc2Vzc2lvbiByZWZyZXNoIHJldHVybmVkIGFuIGludmFsaWQgdG9rZW4uIFJ1biBgbHVhIGF1dGggY29uZmlndXJlYCBhZ2Fpbi4nKTtcbiAgfVxuICB0b2tlbkNhY2hlLnNldChwYXRoLCB7IGlkVG9rZW4sIGV4cGlyZXNBdDogbm93KCkgKyBleHBpcmVzSW4gKiAxMDAwIH0pO1xuICByZXR1cm4gaWRUb2tlbjtcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBiZWFyZXIgdG9rZW4gZXZlcnkgbHVhLWFwaSByZXF1ZXN0IHNob3VsZCBjYXJyeS5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fVxuICogQHRocm93cyB7RXJyb3J9IE1DUF9BVVRIX1NUQUxFIHdoZW4gbm8gY3JlZGVudGlhbCByZXNvbHZlcyBmcm9tIGFueSB0aWVyXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZXNvbHZlQXBpS2V5KHtcbiAgZW52ID0gcHJvY2Vzcy5lbnYsXG4gIGNyZWRlbnRpYWxzUGF0aCA9IGVudi5MVUFfQ1JFREVOVElBTFNfUEFUSCA/PyBqb2luKGhvbWVkaXIoKSwgJy5sdWEtY2xpJywgJ2NyZWRlbnRpYWxzJyksXG4gIHNlc3Npb25zRGlyID0gZW52LkxVQV9TRVNTSU9OU19ESVIgPz8gam9pbihkaXJuYW1lKGNyZWRlbnRpYWxzUGF0aCksICdzZXNzaW9ucycpLFxuICBjd2QgPSBwcm9jZXNzLmN3ZCgpLFxuICBhcGlVcmwgPSBlbnYuTFVBX0FQSV9VUkwgfHwgREVGQVVMVF9BUElfVVJMLFxuICBmZXRjaEZuID0gZ2xvYmFsVGhpcy5mZXRjaCxcbiAgbm93ID0gRGF0ZS5ub3csXG59ID0ge30pIHtcbiAgLy8gVGllciAxYTogcHJvY2VzcyBlbnZpcm9ubWVudFxuICBpZiAoZW52LkxVQV9BUElfS0VZICYmIGVudi5MVUFfQVBJX0tFWS50cmltKCkpIHJldHVybiBlbnYuTFVBX0FQSV9LRVkudHJpbSgpO1xuXG4gIC8vIFRpZXIgMWI6IC5lbnYgaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5IChsdWEtY2xpIGxvYWRzIGl0IGludG8gcHJvY2Vzcy5lbnZcbiAgLy8gYmVmb3JlIHJlYWRpbmcgTFVBX0FQSV9LRVksIHNvIGl0IHJhbmtzIGFib3ZlIHRoZSBzdG9yZWQgc2Vzc2lvbilcbiAgdHJ5IHtcbiAgICBjb25zdCBmcm9tRG90ZW52ID0gcGFyc2VEb3RlbnZBcGlLZXkoYXdhaXQgcmVhZEZpbGUoam9pbihjd2QsICcuZW52JyksICd1dGY4JykpO1xuICAgIGlmIChmcm9tRG90ZW52KSByZXR1cm4gZnJvbURvdGVudjtcbiAgfSBjYXRjaCB7IC8qIG5vIC5lbnYgXHUyMDE0IGZhbGwgdGhyb3VnaCAqLyB9XG5cbiAgLy8gVGllciAyOiByZW5ld2FibGUgZmlyc3QtcGFydHkgc2Vzc2lvblxuICBjb25zdCBmb3VuZCA9IGF3YWl0IGZpbmRTZXNzaW9uRmlsZSh7IHNlc3Npb25zRGlyLCBhcGlVcmwgfSk7XG4gIGlmIChmb3VuZCkgcmV0dXJuIHJlZnJlc2hTZXNzaW9uQmVhcmVyKHsgLi4uZm91bmQsIGZldGNoRm4sIG5vdyB9KTtcblxuICAvLyBUaWVyIDM6IH4vLmx1YS1jbGkvY3JlZGVudGlhbHMgKHBsYWluIHRleHQgQVBJIGtleSlcbiAgdHJ5IHtcbiAgICBjb25zdCByYXcgPSAoYXdhaXQgcmVhZEZpbGUoY3JlZGVudGlhbHNQYXRoLCAndXRmOCcpKS50cmltKCk7XG4gICAgaWYgKHJhdykge1xuICAgICAgaWYgKHJhdy5zdGFydHNXaXRoKCd7JykpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKHJhdyk7XG4gICAgICAgICAgaWYgKHBhcnNlZD8uYXBpS2V5KSByZXR1cm4gcGFyc2VkLmFwaUtleTtcbiAgICAgICAgfSBjYXRjaCB7IC8qIG5vdCBKU09OIGRlc3BpdGUgbGVhZGluZyBicmFjZSAqLyB9XG4gICAgICB9XG4gICAgICByZXR1cm4gcmF3O1xuICAgIH1cbiAgfSBjYXRjaCB7IC8qIG1pc3NpbmcgLyB1bnJlYWRhYmxlIFx1MjAxNCBmYWxsIHRocm91Z2ggKi8gfVxuXG4gIHRocm93IG5ldyBFcnJvcihcbiAgICAnTUNQX0FVVEhfU1RBTEU6IE5vIGx1YS1jbGkgY3JlZGVudGlhbHMgZm91bmQgaW4gTFVBX0FQSV9LRVksIC5lbnYsIH4vLmx1YS1jbGkvc2Vzc2lvbnMsIG9yIH4vLmx1YS1jbGkvY3JlZGVudGlhbHMuICcgK1xuICAgICdSdW4gYGx1YSBhdXRoIGNvbmZpZ3VyZWAgaW4gYSB0ZXJtaW5hbCAob3IgL2x1YS1hdXRoIGluIENsYXVkZSBDb2RlKS4nXG4gICk7XG59XG4iLCAiLy8gVGhpbiBIVFRQIHdyYXBwZXIgYXJvdW5kIGx1YS1hcGkuIFBlciB0ZWNoIHNwZWMgXHUwMEE3OC5cbi8vXG4vLyBXZSBkbyBOT1QgaW1wb3J0IGx1YS1jbGkncyBIdHRwQ2xpZW50IGNsYXNzZXMgYXQgcnVudGltZSAod291bGQgZm9yY2Vcbi8vIHRoZSBlbnRpcmUgbHVhLWNsaSBwYWNrYWdlIHRvIGJlIGJ1bmRsZWQpLiBJbnN0ZWFkLCB0aGlzIG1pbmltYWwgY2xpZW50XG4vLyBoaXRzIHRoZSBzYW1lIFJFU1QgZW5kcG9pbnRzIGx1YS1jbGkgZG9lcy4gRXZlcnkgcm91dGUsIHF1ZXJ5IHBhcmFtZXRlclxuLy8gYW5kIGVudmVsb3BlIHVzZWQgYnkgdGhlIHRvb2xzIGlzIHZlcmlmaWVkIGFnYWluc3QgcGFja2FnZXMvbHVhLWFwaVxuLy8gKGNvbnRyb2xsZXJzL2RldmVsb3Blci8qKiwgZHRvLyoqKSBcdTIwMTQgc2VlIHJlc3BvbnNlLXNoYXBlcy5tanMuXG5cbmltcG9ydCB7IHJlc29sdmVBcGlLZXkgfSBmcm9tICcuL2F1dGgubWpzJztcblxuY29uc3QgREVGQVVMVF9CQVNFX1VSTCA9ICdodHRwczovL2FwaS5oZXlsdWEuYWknO1xuY29uc3QgREVGQVVMVF9USU1FT1VUX01TID0gMTBfMDAwO1xuXG4vLyBTY29wZSBlYWNoIHJvdXRlIGZhbWlseSB0aGUgdG9vbHMgY2FsbCBhY3R1YWxseSByZXF1aXJlcyBcdTIwMTQgcmVhZCBmcm9tIHRoZVxuLy8gYEBSZXF1aXJlU2NvcGUoLi4uKWAgZGVjb3JhdG9yIG9uIGV2ZXJ5IEdFVCB0aGUgdG9vbHMgaGl0LCB1bmRlclxuLy8gbHVhLWNvcmUtc2VydmljZXMgcGFja2FnZXMvbHVhLWFwaS9zcmMvY29udHJvbGxlcnMvZGV2ZWxvcGVyLzpcbi8vICAgc2tpbGxzL2Jhc2UuY29udHJvbGxlci50cywgd2ViaG9va3MvYmFzZS5jb250cm9sbGVyLnRzLFxuLy8gICBqb2JzL2Jhc2UuY29udHJvbGxlci50cywgdHJpZ2dlcnMvYmFzZS5jb250cm9sbGVyLnRzLFxuLy8gICBwcmVwcm9jZXNzb3JzL2Jhc2UuY29udHJvbGxlci50cywgcG9zdHByb2Nlc3NvcnMvYmFzZS5jb250cm9sbGVyLnRzXG4vLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcdTIxOTIgJ2F1dG9tYXRpb25zOnJlYWQnIChsaXN0ICsgOmlkL3ZlcnNpb25zKVxuLy8gICB3b3JrZmxvd3MvYmFzZS5jb250cm9sbGVyLnRzICAgXHUyMTkyICd3b3JrZmxvd3M6cmVhZCcgICAoR0VUIDphZ2VudElkLCBHRVQgOmFnZW50SWQvOndvcmtmbG93SWQvdmVyc2lvbnMpXG4vLyAgIHBlcnNvbmEvYmFzZS5jb250cm9sbGVyLnRzICAgICBcdTIxOTIgJ2FnZW50czpyZWFkJyAgICAgIChHRVQgOmFnZW50SWQvcGVyc29uYS92ZXJzaW9ucylcbi8vICAgYmFzZS5jb250cm9sbGVyLnRzICAgICAgICAgICAgIFx1MjE5MiAna25vd2xlZGdlOnJlYWQnICAgKEdFVCBhZ2VudHMvOmFnZW50SWQvbG9ncylcbi8vIFdvcmtmbG93cyBhcmUgTk9UIHVuZGVyIGF1dG9tYXRpb25zOnJlYWQgXHUyMDE0IHRoZXkgaGF2ZSB0aGVpciBvd24gc2NvcGUgcGFpclxuLy8gKHdvcmtmbG93czpyZWFkIC8gd29ya2Zsb3dzOndyaXRlKS5cbmV4cG9ydCBjb25zdCBST1VURV9TQ09QRVMgPSBPYmplY3QuZnJlZXplKHtcbiAgc2tpbGxzOiAnYXV0b21hdGlvbnM6cmVhZCcsXG4gIHdlYmhvb2tzOiAnYXV0b21hdGlvbnM6cmVhZCcsXG4gIGpvYnM6ICdhdXRvbWF0aW9uczpyZWFkJyxcbiAgdHJpZ2dlcnM6ICdhdXRvbWF0aW9uczpyZWFkJyxcbiAgcHJlcHJvY2Vzc29yczogJ2F1dG9tYXRpb25zOnJlYWQnLFxuICBwb3N0cHJvY2Vzc29yczogJ2F1dG9tYXRpb25zOnJlYWQnLFxuICB3b3JrZmxvd3M6ICd3b3JrZmxvd3M6cmVhZCcsXG4gIHBlcnNvbmE6ICdhZ2VudHM6cmVhZCcsXG4gIGxvZ3M6ICdrbm93bGVkZ2U6cmVhZCcsXG59KTtcblxuY29uc3QgUk9VVEVfU0NPUEVfU1VNTUFSWSA9XG4gICdza2lsbHMvd2ViaG9va3Mvam9icy90cmlnZ2Vycy9wcmVwcm9jZXNzb3JzL3Bvc3Rwcm9jZXNzb3JzIG5lZWQgYXV0b21hdGlvbnM6cmVhZCwgd29ya2Zsb3dzIG5lZWQgJyArXG4gICd3b3JrZmxvd3M6cmVhZCwgcGVyc29uYSBuZWVkcyBhZ2VudHM6cmVhZCwgbG9ncyBuZWVkIGtub3dsZWRnZTpyZWFkJztcblxuLyoqXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIEFQSSBwYXRoICh3aXRoIGxlYWRpbmcgc2xhc2gpXG4gKiBAcGFyYW0ge3ttZXRob2Q/OiBzdHJpbmcsIGJvZHk/OiBvYmplY3QsIHF1ZXJ5PzogUmVjb3JkPHN0cmluZyxzdHJpbmd8bnVtYmVyPiwgZmV0Y2hGbj86IHR5cGVvZiBmZXRjaCwgYmFzZVVybD86IHN0cmluZywgdGltZW91dE1zPzogbnVtYmVyfX0gW29wdHNdXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhcGlSZXF1ZXN0KHBhdGgsIHtcbiAgbWV0aG9kID0gJ0dFVCcsXG4gIGJvZHksXG4gIHF1ZXJ5LFxuICBmZXRjaEZuID0gZ2xvYmFsVGhpcy5mZXRjaCxcbiAgYmFzZVVybCA9IHByb2Nlc3MuZW52LkxVQV9BUElfVVJMIHx8IERFRkFVTFRfQkFTRV9VUkwsXG4gIHRpbWVvdXRNcyA9IERFRkFVTFRfVElNRU9VVF9NUyxcbn0gPSB7fSkge1xuICBjb25zdCBhcGlLZXkgPSBhd2FpdCByZXNvbHZlQXBpS2V5KCk7XG4gIC8vIEpvaW4gYnkgc3RyaW5nIGNvbmNhdGVuYXRpb24sIE5PVCBgbmV3IFVSTChwYXRoLCBiYXNlVXJsKWA6IHRoZSBXSEFUV0dcbiAgLy8gcmVzb2x2ZXIgdHJlYXRzIGEgbGVhZGluZy1zbGFzaCBwYXRoIGFzIGhvc3QtYWJzb2x1dGUgYW5kIHdvdWxkIGRyb3AgYW55XG4gIC8vIHBhdGggcHJlZml4IG9uIExVQV9BUElfVVJMIChodHRwczovL2hvc3QvYXBpICsgL2RldmVsb3Blci94IFx1MjE5MlxuICAvLyBodHRwczovL2hvc3QvZGV2ZWxvcGVyL3gpLiBgcGF0aGAgYWx3YXlzIHN0YXJ0cyB3aXRoICcvJywgc28gc3RyaXAgb25lXG4gIC8vIHRyYWlsaW5nIHNsYXNoIGZyb20gdGhlIGJhc2UgYW5kIGNvbmNhdGVuYXRlLlxuICBjb25zdCB1cmwgPSBuZXcgVVJMKGJhc2VVcmwucmVwbGFjZSgvXFwvJC8sICcnKSArIHBhdGgpO1xuICBpZiAocXVlcnkpIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKHF1ZXJ5KSkgdXJsLnNlYXJjaFBhcmFtcy5zZXQoaywgU3RyaW5nKHYpKTtcblxuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCB0aW1lb3V0TXMpO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgcmVzID0gYXdhaXQgZmV0Y2hGbih1cmwudG9TdHJpbmcoKSwge1xuICAgICAgbWV0aG9kLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICAnQXV0aG9yaXphdGlvbic6IGBCZWFyZXIgJHthcGlLZXl9YCxcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICAgICAgJ1gtTHVhLUNsaWVudCc6ICdjbGF1ZGUtcGx1Z2luLzEuNS4wJyxcbiAgICAgIH0sXG4gICAgICBib2R5OiBib2R5ID8gSlNPTi5zdHJpbmdpZnkoYm9keSkgOiB1bmRlZmluZWQsXG4gICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pO1xuXG4gICAgaWYgKHJlcy5zdGF0dXMgPT09IDQwMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnTUNQX0FVVEhfU1RBTEU6IGx1YS1hcGkgcmV0dXJuZWQgNDAxLiBUaGUgY3JlZGVudGlhbCBpcyBtaXNzaW5nLCBleHBpcmVkLCBvciB0aGUgc2Vzc2lvbiB3YXMgc2lnbmVkIG91dCAnICtcbiAgICAgICAgJyhzaWduaW5nIG91dCBvZiB0aGUgTHVhIGRhc2hib2FyZCBvciBhcHBzIGFsc28gZW5kcyBDTEkgc2Vzc2lvbnMpLiBSdW4gYGx1YSBhdXRoIGNvbmZpZ3VyZWAgaW4gYSB0ZXJtaW5hbCwgJyArXG4gICAgICAgICdvciByZS1ydW4gL2x1YS1kb2N0b3IuJ1xuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHJlcy5zdGF0dXMgPT09IDQwMykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgTUNQX0ZPUkJJRERFTjogbHVhLWFwaSByZXR1cm5lZCA0MDMgZm9yICR7cGF0aH0uIFRoZSBjcmVkZW50aWFsIGNhbm5vdCBhY2Nlc3MgdGhpcyBhZ2VudCBvciBvcmcsIG9yIGl0IGxhY2tzIGAgK1xuICAgICAgICBgdGhlIHJvdXRlIHNjb3BlICgke1JPVVRFX1NDT1BFX1NVTU1BUll9IFx1MjAxNCB0eXBlZCBrZXlzIGFyZSBzY29wZWQgdG8gdGhlIGFnZW50cyBhbmQgcm9sZSBjaG9zZW4gYXQgYCArXG4gICAgICAgICdgbHVhIGF1dGggY29uZmlndXJlYCkuIFJlLXJ1biAvbHVhLWRvY3RvciBvciBjaGVjayB0aGUgTFVBX0FQSV9LRVkgZW52IHZhci4nXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAoIXJlcy5vaykge1xuICAgICAgLy8gVHJ5IHRvIHBhcnNlIGx1YS1hcGkncyBzdHJ1Y3R1cmVkIGVycm9yIGVudmVsb3BlOlxuICAgICAgLy8gICB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogeyBtZXNzYWdlLCBzdGF0dXNDb2RlLCBjb2RlPywgLi4uIH0gfVxuICAgICAgLy8gRmFsbHMgYmFjayB0byByYXcgdGV4dCBvbiBwYXJzZSBmYWlsdXJlIChlLmcuIHByb3h5IDUwMiB3aXRoIEhUTUwgYm9keSkuXG4gICAgICBjb25zdCByYXcgPSBhd2FpdCByZXMudGV4dCgpO1xuICAgICAgbGV0IGZyaWVuZGx5TXNnID0gcmF3O1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZShyYXcpO1xuICAgICAgICBmcmllbmRseU1zZyA9IHBhcnNlZD8uZXJyb3I/Lm1lc3NhZ2UgPz8gcGFyc2VkPy5tZXNzYWdlID8/IHJhdztcbiAgICAgICAgY29uc3QgY29kZSA9IHBhcnNlZD8uZXJyb3I/LmNvZGU7XG4gICAgICAgIGlmICh0eXBlb2YgY29kZSA9PT0gJ3N0cmluZycgJiYgY29kZSAmJiAhU3RyaW5nKGZyaWVuZGx5TXNnKS5pbmNsdWRlcyhjb2RlKSkgZnJpZW5kbHlNc2cgPSBgJHtjb2RlfTogJHtmcmllbmRseU1zZ31gO1xuICAgICAgfSBjYXRjaCB7IC8qIG5vdCBKU09OOyByYXcgaXMgZmluZSAqLyB9XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYGx1YS1hcGkgJHtyZXMuc3RhdHVzfTogJHtmcmllbmRseU1zZ31gKTtcbiAgICB9XG5cbiAgICByZXR1cm4gYXdhaXQgcmVzLmpzb24oKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgaWYgKGVyci5uYW1lID09PSAnQWJvcnRFcnJvcicpIHRocm93IG5ldyBFcnJvcihgTUNQX1RJTUVPVVQ6ICR7cGF0aH0gZGlkIG5vdCByZXNwb25kIGluICR7dGltZW91dE1zfW1zYCk7XG4gICAgdGhyb3cgZXJyO1xuICB9IGZpbmFsbHkge1xuICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gIH1cbn1cbiIsICIvLyBQZXItdHlwZSByZXNwb25zZS1zaGFwZSBleHRyYWN0b3JzLlxuLy9cbi8vIGx1YS1hcGkncyBkZXZlbG9wZXIgZW5kcG9pbnRzIGhhdmUgSU5DT05TSVNURU5UIHJlc3BvbnNlIHNoYXBlcyBhY3Jvc3Ncbi8vIHByaW1pdGl2ZSB0eXBlcy4gVmVyaWZpZWQgYWdhaW5zdCBwYWNrYWdlcy9sdWEtYXBpL3NyYy9kdG8vKi5kdG8udHMgYW5kXG4vLyBwYWNrYWdlcy9sdWEtYXBpL3NyYy9jb250cm9sbGVycy9kZXZlbG9wZXIvKiovYmFzZS5jb250cm9sbGVyLnRzLCBwbHVzXG4vLyBwYWNrYWdlcy9sdWEtY2xpL3NyYy9pbnRlcmZhY2VzL3dvcmtmbG93cy50cyAobHVhLWNsaSAzLjMzLjApOlxuLy9cbi8vICAgU2tpbGxzICAgICAgICAgR0VUIC9kZXZlbG9wZXIvc2tpbGxzLzphZ2VudElkICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHNraWxsczogU2tpbGxEdG9bXSB9XG4vLyAgICAgICAgICAgICAgICAgIEdFVCAvZGV2ZWxvcGVyL3NraWxscy86YWdlbnRJZC86c2tpbGxJZC92ZXJzaW9ucyAgICBcdTIxOTIgeyB2ZXJzaW9uczogW3sgdmVyc2lvbiwgY3JlYXRlZERhdGUsIGlzQ3VycmVudCwgLi4uIH1dIH1cbi8vICAgV2ViaG9va3MgICAgICAgR0VUIC9kZXZlbG9wZXIvd2ViaG9va3MvOmFnZW50SWQgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHN1Y2Nlc3MsIGRhdGE6IHsgd2ViaG9va3MgfSB9XG4vLyAgICAgICAgICAgICAgICAgIEdFVCAuLi4vOndlYmhvb2tJZC92ZXJzaW9ucyAgICAgICAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyBzdWNjZXNzLCBkYXRhOiB7IHZlcnNpb25zOiBbeyB2ZXJzaW9uLCB3ZWJob29rSWQsIGNyZWF0ZWRBdCwgaXNBY3RpdmUgfV0sIGFjdGl2ZVZlcnNpb25JZD8gfSB9XG4vLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChzZXJ2aWNlcy9kZXZlbG9wZXIud2ViaG9vay5zZXJ2aWNlLnRzIHNldHMgYHdlYmhvb2tJZDogdi5pZGAgXHUyMDE0IHRoZVxuLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyc2lvbiBST1cgaWQgdW5kZXIgYSBtaXNuYW1lZCBrZXk7IHRoZXJlIGlzIG5vIHZlcnNpb25JZC9pZCwgc29cbi8vICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnNpb25JZCgpIGlzIG51bGwgYW5kIHRoZSBlbnZlbG9wZSdzIGFjdGl2ZVZlcnNpb25JZCBpcyB0aGUgb25seSBpZClcbi8vICAgSm9icyAgICAgICAgICAgR0VUIC9kZXZlbG9wZXIvam9icy86YWdlbnRJZCAgICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHN1Y2Nlc3MsIGRhdGE6IHsgam9icyB9IH1cbi8vICAgICAgICAgICAgICAgICAgR0VUIC4uLi86am9iSWQvdmVyc2lvbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHN1Y2Nlc3MsIGRhdGE6IEpvYlZlcnNpb25EdG9bXSB9ICAgXHUyMTkwIGFycmF5IHVuZGVyIGRhdGE7IGBhY3RpdmVgXG4vLyAgIFRyaWdnZXJzICAgICAgIEdFVCAvZGV2ZWxvcGVyL3RyaWdnZXJzLzphZ2VudElkICAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyBzdWNjZXNzLCBkYXRhOiB7IHRyaWdnZXJzIH0gfVxuLy8gICAgICAgICAgICAgICAgICBHRVQgLi4uLzp0cmlnZ2VySWQvdmVyc2lvbnMgICAgICAgICAgICAgICAgICAgICAgICAgXHUyMTkyIHsgc3VjY2VzcywgZGF0YTogeyB2ZXJzaW9uczogW3sgdmVyc2lvbiwgdmVyc2lvbklkLCBjcmVhdGVkQXQsIGlzQWN0aXZlIH1dLCBhY3RpdmVWZXJzaW9uSWQ/IH0gfVxuLy8gICBQcmVwcm9jZXNzb3JzICBHRVQgL2RldmVsb3Blci9wcmVwcm9jZXNzb3JzLzphZ2VudElkICAgICAgICAgICAgICAgXHUyMTkyIHsgc3VjY2VzcywgZGF0YTogeyBwcmVwcm9jZXNzb3JzIH0gfVxuLy8gICAgICAgICAgICAgICAgICBHRVQgLi4uLzpwcmVwcm9jZXNzb3JJZC92ZXJzaW9ucyAgICAgICAgICAgICAgICAgICAgXHUyMTkyIHsgc3VjY2VzcywgZGF0YTogeyB2ZXJzaW9uczogW3sgdmVyc2lvbklkLCB2ZXJzaW9uLCBpc0FjdGl2ZSwgY3JlYXRlZEF0IH1dLCBhY3RpdmVWZXJzaW9uSWQ/IH0gfVxuLy8gICBQb3N0cHJvY2Vzc29ycyBHRVQgL2RldmVsb3Blci9wb3N0cHJvY2Vzc29ycy86YWdlbnRJZCAgICAgICAgICAgICAgXHUyMTkyIHsgc3VjY2VzcywgZGF0YTogeyBwb3N0cHJvY2Vzc29ycyB9IH1cbi8vICAgICAgICAgICAgICAgICAgR0VUIC4uLi86cG9zdHByb2Nlc3NvcklkL3ZlcnNpb25zICAgICAgICAgICAgICAgICAgIFx1MjE5MiBzYW1lIGZhbWlseSBhcyBwcmVwcm9jZXNzb3JzXG4vLyAgIFdvcmtmbG93cyAgICAgIEdFVCAvZGV2ZWxvcGVyL3dvcmtmbG93cy86YWdlbnRJZCAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyBzdWNjZXNzLCBkYXRhOiB7IHdvcmtmbG93cyB9IH0gICAoY29kZS9zY3JpcHQgcHJvamVjdGVkIG91dClcbi8vICAgICAgICAgICAgICAgICAgR0VUIC4uLi86d29ya2Zsb3dJZC92ZXJzaW9ucyAgICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHN1Y2Nlc3MsIGRhdGE6IFdvcmtmbG93VmVyc2lvbltdIH0gICBcdTIxOTAgYXJyYXkgdW5kZXIgZGF0YTsgYGFjdGl2ZWBcbi8vICAgUGVyc29uYSAgICAgICAgR0VUIC9kZXZlbG9wZXIvYWdlbnRzLzphZ2VudElkL3BlcnNvbmEvdmVyc2lvbnMgICAgIFx1MjE5MiB7IHN0YXR1cywgbWVzc2FnZSwgdmVyc2lvbnM6IFt7IHZlcnNpb246IG51bWJlciwgY3JlYXRlZERhdGU6IG51bWJlciwgaXNDdXJyZW50LCBwZXJzb25hIH1dIH1cbi8vXG4vLyBObyB2ZXJzaW9uIERUTyBjYXJyaWVzIGBkZXBsb3llZEF0YCBvciBgc291cmNlSGFzaGAuIFRoZSBcImxpdmVcIiBmbGFnIGlzXG4vLyBgaXNDdXJyZW50YCAoc2tpbGwsIHBlcnNvbmEpLCBgYWN0aXZlYCAoam9iLCB3b3JrZmxvdykgb3IgYGlzQWN0aXZlYCAvXG4vLyBgYWN0aXZlVmVyc2lvbklkYCAod2ViaG9vaywgdHJpZ2dlciwgcHJlL3Bvc3Rwcm9jZXNzb3IpLiBDcmVhdGlvbiB0aW1lIGlzXG4vLyBgY3JlYXRlZEF0YCAoSVNPIHN0cmluZykgZXZlcnl3aGVyZSBleGNlcHQgc2tpbGwvcGVyc29uYSAoYGNyZWF0ZWREYXRlYDtcbi8vIHBlcnNvbmEncyBpcyBhbiBlcG9jaC1tcyBudW1iZXIpLlxuXG5jb25zdCBMSVNUX0VYVFJBQ1RPUlMgPSB7XG4gIHNraWxsOiAgICAgICAgIChyKSA9PiByPy5za2lsbHMgPz8gW10sXG4gIHdlYmhvb2s6ICAgICAgIChyKSA9PiByPy5kYXRhPy53ZWJob29rcyA/PyBbXSxcbiAgam9iOiAgICAgICAgICAgKHIpID0+IHI/LmRhdGE/LmpvYnMgPz8gW10sXG4gIHRyaWdnZXI6ICAgICAgIChyKSA9PiByPy5kYXRhPy50cmlnZ2VycyA/PyBbXSxcbiAgcHJlcHJvY2Vzc29yOiAgKHIpID0+IHI/LmRhdGE/LnByZXByb2Nlc3NvcnMgPz8gW10sXG4gIHBvc3Rwcm9jZXNzb3I6IChyKSA9PiByPy5kYXRhPy5wb3N0cHJvY2Vzc29ycyA/PyBbXSxcbiAgd29ya2Zsb3c6ICAgICAgKHIpID0+IHI/LmRhdGE/LndvcmtmbG93cyA/PyBbXSxcbn07XG5cbmNvbnN0IFZFUlNJT05TX0VYVFJBQ1RPUlMgPSB7XG4gIHNraWxsOiAgICAgICAgIChyKSA9PiByPy52ZXJzaW9ucyA/PyBbXSxcbiAgd2ViaG9vazogICAgICAgKHIpID0+IHI/LmRhdGE/LnZlcnNpb25zID8/IFtdLFxuICBqb2I6ICAgICAgICAgICAocikgPT4gKEFycmF5LmlzQXJyYXkocj8uZGF0YSkgPyByLmRhdGEgOiBbXSksXG4gIHRyaWdnZXI6ICAgICAgIChyKSA9PiByPy5kYXRhPy52ZXJzaW9ucyA/PyBbXSxcbiAgcHJlcHJvY2Vzc29yOiAgKHIpID0+IHI/LmRhdGE/LnZlcnNpb25zID8/IFtdLFxuICBwb3N0cHJvY2Vzc29yOiAocikgPT4gcj8uZGF0YT8udmVyc2lvbnMgPz8gW10sXG4gIHdvcmtmbG93OiAgICAgIChyKSA9PiAoQXJyYXkuaXNBcnJheShyPy5kYXRhKSA/IHIuZGF0YSA6IFtdKSxcbiAgcGVyc29uYTogICAgICAgKHIpID0+IHI/LnZlcnNpb25zID8/IFtdLFxufTtcblxuLyoqIFVSTCBwYXRoIHNlZ21lbnQgdW5kZXIgL2RldmVsb3Blci8gZm9yIGVhY2ggbGlzdGFibGUgdHlwZS4gKi9cbmV4cG9ydCBjb25zdCBMSVNUX1BBVEhTID0ge1xuICBza2lsbDogJ3NraWxscycsXG4gIHdlYmhvb2s6ICd3ZWJob29rcycsXG4gIGpvYjogJ2pvYnMnLFxuICB0cmlnZ2VyOiAndHJpZ2dlcnMnLFxuICBwcmVwcm9jZXNzb3I6ICdwcmVwcm9jZXNzb3JzJyxcbiAgcG9zdHByb2Nlc3NvcjogJ3Bvc3Rwcm9jZXNzb3JzJyxcbiAgd29ya2Zsb3c6ICd3b3JrZmxvd3MnLFxufTtcblxuLyoqXG4gKiBFeHRyYWN0IGEgbGlzdCBvZiBwcmltaXRpdmVzIGZyb20gYSBsaXN0LWVuZHBvaW50IHJlc3BvbnNlLlxuICogQHRocm93cyBpZiB0aGUgdHlwZSBpcyB1bmtub3duLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdExpc3QodHlwZSwgcmVzcG9uc2UpIHtcbiAgY29uc3QgZm4gPSBMSVNUX0VYVFJBQ1RPUlNbdHlwZV07XG4gIGlmICghZm4pIHRocm93IG5ldyBFcnJvcihgZXh0cmFjdExpc3Q6IHVua25vd24gcHJpbWl0aXZlIHR5cGUgXCIke3R5cGV9XCIuIFZhbGlkOiAke09iamVjdC5rZXlzKExJU1RfRVhUUkFDVE9SUykuam9pbignLCAnKX1gKTtcbiAgcmV0dXJuIGZuKHJlc3BvbnNlKTtcbn1cblxuLyoqXG4gKiBFeHRyYWN0IGEgbGlzdCBvZiB2ZXJzaW9ucyBmcm9tIGEgdmVyc2lvbnMtZW5kcG9pbnQgcmVzcG9uc2UuXG4gKiBAdGhyb3dzIGlmIHRoZSB0eXBlIGlzIHVua25vd24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0VmVyc2lvbnModHlwZSwgcmVzcG9uc2UpIHtcbiAgY29uc3QgZm4gPSBWRVJTSU9OU19FWFRSQUNUT1JTW3R5cGVdO1xuICBpZiAoIWZuKSB0aHJvdyBuZXcgRXJyb3IoYGV4dHJhY3RWZXJzaW9uczogdW5rbm93biBwcmltaXRpdmUgdHlwZSBcIiR7dHlwZX1cIi4gVmFsaWQ6ICR7T2JqZWN0LmtleXMoVkVSU0lPTlNfRVhUUkFDVE9SUykuam9pbignLCAnKX1gKTtcbiAgcmV0dXJuIGZuKHJlc3BvbnNlKTtcbn1cblxuLyoqXG4gKiBUaGUgYGFjdGl2ZVZlcnNpb25JZGAgYSB2ZXJzaW9ucyBlbnZlbG9wZSBtYXkgY2FycnkgYmVzaWRlIGl0cyBhcnJheVxuICogKHdlYmhvb2ssIHRyaWdnZXIsIHByZS9wb3N0cHJvY2Vzc29yKS4gTnVsbCBmb3IgdGhlIG90aGVyIGZhbWlsaWVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdEFjdGl2ZVZlcnNpb25JZChyZXNwb25zZSkge1xuICBjb25zdCBpZCA9IHJlc3BvbnNlPy5kYXRhPy5hY3RpdmVWZXJzaW9uSWQ7XG4gIHJldHVybiB0eXBlb2YgaWQgPT09ICdzdHJpbmcnICYmIGlkID8gaWQgOiBudWxsO1xufVxuXG4vKipcbiAqIElzIHRoaXMgdmVyc2lvbiB0aGUgb25lIHRoYXQgaXMgbGl2ZT8gRWFjaCBmYW1pbHkgc3BlbGxzIGl0IGRpZmZlcmVudGx5LlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlXG4gKiBAcGFyYW0ge29iamVjdH0gdmVyc2lvbiAtIG9uZSBlbGVtZW50IG9mIGV4dHJhY3RWZXJzaW9ucygpXG4gKiBAcGFyYW0ge29iamVjdH0gW3Jlc3BvbnNlXSAtIHRoZSB2ZXJzaW9ucyBlbnZlbG9wZSAoZm9yIGFjdGl2ZVZlcnNpb25JZClcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzQWN0aXZlVmVyc2lvbih0eXBlLCB2ZXJzaW9uLCByZXNwb25zZSkge1xuICBpZiAoIXZlcnNpb24gfHwgdHlwZW9mIHZlcnNpb24gIT09ICdvYmplY3QnKSByZXR1cm4gZmFsc2U7XG4gIHN3aXRjaCAodHlwZSkge1xuICAgIGNhc2UgJ3NraWxsJzpcbiAgICBjYXNlICdwZXJzb25hJzpcbiAgICAgIHJldHVybiB2ZXJzaW9uLmlzQ3VycmVudCA9PT0gdHJ1ZTtcbiAgICBjYXNlICdqb2InOlxuICAgIGNhc2UgJ3dvcmtmbG93JzpcbiAgICAgIHJldHVybiB2ZXJzaW9uLmFjdGl2ZSA9PT0gdHJ1ZTtcbiAgICBjYXNlICd3ZWJob29rJzpcbiAgICBjYXNlICd0cmlnZ2VyJzpcbiAgICBjYXNlICdwcmVwcm9jZXNzb3InOlxuICAgIGNhc2UgJ3Bvc3Rwcm9jZXNzb3InOiB7XG4gICAgICBpZiAodmVyc2lvbi5pc0FjdGl2ZSA9PT0gdHJ1ZSkgcmV0dXJuIHRydWU7XG4gICAgICBjb25zdCBhY3RpdmVJZCA9IGV4dHJhY3RBY3RpdmVWZXJzaW9uSWQocmVzcG9uc2UpO1xuICAgICAgY29uc3Qgb3duSWQgPSB2ZXJzaW9uLnZlcnNpb25JZCA/PyB2ZXJzaW9uLmlkID8/IG51bGw7XG4gICAgICByZXR1cm4gISFhY3RpdmVJZCAmJiBvd25JZCA9PT0gYWN0aXZlSWQ7XG4gICAgfVxuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBJU08tODYwMSBjcmVhdGlvbiB0aW1lIG9mIGEgdmVyc2lvbiwgb3IgbnVsbC4gTm9ybWFsaXNlcyBza2lsbC9wZXJzb25hJ3NcbiAqIGBjcmVhdGVkRGF0ZWAgKHBlcnNvbmE6IGVwb2NoIG1zKSBvbnRvIHRoZSBgY3JlYXRlZEF0YCBuYW1lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmVyc2lvbkNyZWF0ZWRBdCh2ZXJzaW9uKSB7XG4gIGlmICghdmVyc2lvbiB8fCB0eXBlb2YgdmVyc2lvbiAhPT0gJ29iamVjdCcpIHJldHVybiBudWxsO1xuICBjb25zdCByYXcgPSB2ZXJzaW9uLmNyZWF0ZWRBdCA/PyB2ZXJzaW9uLmNyZWF0ZWREYXRlID8/IG51bGw7XG4gIGlmIChyYXcgPT0gbnVsbCkgcmV0dXJuIG51bGw7XG4gIGlmICh0eXBlb2YgcmF3ID09PSAnbnVtYmVyJykgcmV0dXJuIE51bWJlci5pc0Zpbml0ZShyYXcpID8gbmV3IERhdGUocmF3KS50b0lTT1N0cmluZygpIDogbnVsbDtcbiAgcmV0dXJuIFN0cmluZyhyYXcpO1xufVxuXG4vKiogSWRlbnRpZmllciBvZiBhIHZlcnNpb24gcm93ICh0aGUgc2xvdCBsdWEtYXBpIGtleXMgYGFjdGl2ZVZlcnNpb25JZGAgb24pLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZlcnNpb25JZCh2ZXJzaW9uKSB7XG4gIGlmICghdmVyc2lvbiB8fCB0eXBlb2YgdmVyc2lvbiAhPT0gJ29iamVjdCcpIHJldHVybiBudWxsO1xuICByZXR1cm4gdmVyc2lvbi52ZXJzaW9uSWQgPz8gdmVyc2lvbi5pZCA/PyBudWxsO1xufVxuXG5leHBvcnQgY29uc3QgU1VQUE9SVEVEX0xJU1RfVFlQRVMgPSBPYmplY3Qua2V5cyhMSVNUX0VYVFJBQ1RPUlMpO1xuZXhwb3J0IGNvbnN0IFNVUFBPUlRFRF9WRVJTSU9OX1RZUEVTID0gT2JqZWN0LmtleXMoVkVSU0lPTlNfRVhUUkFDVE9SUyk7XG4iLCAiaW1wb3J0IHsgYXBpUmVxdWVzdCB9IGZyb20gJy4uL2FwaS1jbGllbnQubWpzJztcbmltcG9ydCB7XG4gIGV4dHJhY3RMaXN0LFxuICBleHRyYWN0VmVyc2lvbnMsXG4gIGV4dHJhY3RBY3RpdmVWZXJzaW9uSWQsXG4gIGlzQWN0aXZlVmVyc2lvbixcbiAgdmVyc2lvbkNyZWF0ZWRBdCxcbiAgdmVyc2lvbklkLFxuICBMSVNUX1BBVEhTLFxuICBTVVBQT1JURURfVkVSU0lPTl9UWVBFUyxcbn0gZnJvbSAnLi4vcmVzcG9uc2Utc2hhcGVzLm1qcyc7XG5cbmNvbnN0IFZBTElEX1RZUEVTID0gbmV3IFNldChTVVBQT1JURURfVkVSU0lPTl9UWVBFUyk7XG5cbmV4cG9ydCBjb25zdCBsaXN0UHJpbWl0aXZlVmVyc2lvbnMgPSB7XG4gIHNwZWM6IHtcbiAgICBuYW1lOiAnbGlzdF9wcmltaXRpdmVfdmVyc2lvbnMnLFxuICAgIC8vIFdoaWNoIGZhbWlsaWVzIGNhcnJ5IGEgcGVyLWVudHJ5IGlkIFx1MjAxNCB2ZXJpZmllZCBpbiBsdWEtYXBpJ3Mgc2VydmljZVxuICAgIC8vIG1hcHBlcnMgKHBhY2thZ2VzL2x1YS1hcGkvc3JjL3NlcnZpY2VzLyk6XG4gICAgLy8gICBkZXZlbG9wZXIudHJpZ2dlci5zZXJ2aWNlLnRzIGdldFRyaWdnZXJWZXJzaW9ucyAgICAgICAgICAgIFx1MjE5MiB7IHZlcnNpb24sIHZlcnNpb25JZDogdi5pZCwgY3JlYXRlZEF0LCBpc0FjdGl2ZSB9XG4gICAgLy8gICBkZXZlbG9wZXIucHJlcHJvY2Vzc29yLnNlcnZpY2UudHMgZ2V0UHJlUHJvY2Vzc29yVmVyc2lvbnMgIFx1MjE5MiB7IHZlcnNpb25JZDogdi5pZCwgdmVyc2lvbiwgaXNBY3RpdmUsIGNyZWF0ZWRBdCB9XG4gICAgLy8gICBkZXZlbG9wZXIucG9zdHByb2Nlc3Nvci5zZXJ2aWNlLnRzIGdldFBvc3RQcm9jZXNzb3JWZXJzaW9uc1x1MjE5MiBzYW1lIGFzIHByZXByb2Nlc3NvclxuICAgIC8vICAgZGV2ZWxvcGVyLndvcmtmbG93LnNlcnZpY2UudHMgdG9WZXJzaW9uRHRvICAgICAgICAgICAgICAgICBcdTIxOTIgeyBpZCwgdmVyc2lvbiwgYWN0aXZlLCAuLi4gfVxuICAgIC8vICAgZHRvL2pvYi5kdG8udHMgSm9iVmVyc2lvbkR0byAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyBpZCwgdmVyc2lvbiwgYWN0aXZlLCAuLi4gfVxuICAgIC8vICAgZGV2ZWxvcGVyLndlYmhvb2suc2VydmljZS50cyBnZXRXZWJob29rVmVyc2lvbnMgICAgICAgICAgICBcdTIxOTIgeyB2ZXJzaW9uLCB3ZWJob29rSWQ6IHYuaWQsIGNyZWF0ZWRBdCwgaXNBY3RpdmUgfVxuICAgIC8vICAgICAgIChOTyB2ZXJzaW9uSWQvaWQga2V5IFx1MjAxNCB0aGUgcm93IGlkIGlzIHN0YXNoZWQgdW5kZXIgdGhlIG1pc25hbWVkXG4gICAgLy8gICAgICAgIGB3ZWJob29rSWRgLCB3aGljaCB3ZSBkZWxpYmVyYXRlbHkgZG8gbm90IHJlYWQgYXMgYSB2ZXJzaW9uIGlkKVxuICAgIC8vICAgZHRvL3NraWxsLmR0by50cyBTa2lsbFZlcnNpb25EdG8gICAgICAgICAgICAgICAgICAgICAgICAgICBcdTIxOTIgeyB2ZXJzaW9uLCBjcmVhdGVkRGF0ZSwgY3JlYXRlZEJ5LCBpc0N1cnJlbnQgfSAobm8gaWQpXG4gICAgLy8gICBwZXJzb25hL2Jhc2UuY29udHJvbGxlci50cyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFx1MjE5MiB7IHZlcnNpb246IG51bWJlciwgY3JlYXRlZERhdGUsIGlzQ3VycmVudCwgcGVyc29uYSB9IChubyBpZClcbiAgICBkZXNjcmlwdGlvbjogJ0xpc3QgdGhlIHNlcnZlci1zaWRlIHZlcnNpb25zIG9mIG9uZSBwcmltaXRpdmUgKHNraWxsLCB3ZWJob29rLCBqb2IsIHRyaWdnZXIsIHByZXByb2Nlc3NvciwgcG9zdHByb2Nlc3Nvciwgd29ya2Zsb3cpIG9yIHRoZSBhZ2VudCBwZXJzb25hLiBSZXR1cm5zIHsgYWN0aXZlVmVyc2lvbklkLCB2ZXJzaW9uczogW3t2ZXJzaW9uLCB2ZXJzaW9uSWQsIGFjdGl2ZSwgY3JlYXRlZEF0fV0gfSBcdTIwMTQgYGFjdGl2ZWAgaXMgdGhlIHZlcnNpb24gY3VycmVudGx5IGxpdmUuIGB2ZXJzaW9uSWRgIGlzIG51bGwgZm9yIHR5cGVzIHdob3NlIHZlcnNpb25zIHJvdXRlIGNhcnJpZXMgbm8gcGVyLWVudHJ5IGlkOiBza2lsbCwgcGVyc29uYSBhbmQgd2ViaG9vayAodGhlIHdlYmhvb2sgcm91dGUgZXhwb3NlcyB0aGUgcm93IGlkIG9ubHkgdW5kZXIgYSBtaXNuYW1lZCBgd2ViaG9va0lkYCBrZXkpLiBGb3IgdGhvc2UsIGlkZW50aWZ5IGEgdmVyc2lvbiBieSBgdmVyc2lvbmA7IGZvciB3ZWJob29rcyB0aGUgbGl2ZSByb3cgaWQgaXMgdGhlIHRvcC1sZXZlbCBgYWN0aXZlVmVyc2lvbklkYC4gYGFjdGl2ZVZlcnNpb25JZGAgaXMgcG9wdWxhdGVkIG9ubHkgZm9yIHdlYmhvb2ssIHRyaWdnZXIsIHByZXByb2Nlc3NvciBhbmQgcG9zdHByb2Nlc3NvciBlbnZlbG9wZXMgYW5kIGlzIG51bGwgZm9yIHNraWxsLCBqb2IsIHdvcmtmbG93IGFuZCBwZXJzb25hLicsXG4gICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgcHJvcGVydGllczoge1xuICAgICAgICBhZ2VudElkOiB7IHR5cGU6ICdzdHJpbmcnIH0sXG4gICAgICAgIHR5cGU6IHsgdHlwZTogJ3N0cmluZycsIGVudW06IFsuLi5WQUxJRF9UWVBFU10gfSxcbiAgICAgICAgbmFtZTogeyB0eXBlOiAnc3RyaW5nJywgZGVzY3JpcHRpb246ICdQcmltaXRpdmUgbmFtZSBhcyB3cml0dGVuIGluIGx1YS5za2lsbC55YW1sIChvbWl0IGZvciBwZXJzb25hIFx1MjAxNCBhbiBhZ2VudCBoYXMgb25seSBvbmUpJyB9LFxuICAgICAgfSxcbiAgICAgIHJlcXVpcmVkOiBbJ2FnZW50SWQnLCAndHlwZSddLFxuICAgIH0sXG4gIH0sXG4gIGFzeW5jIGhhbmRsZXIoeyBhZ2VudElkLCB0eXBlLCBuYW1lIH0sIGRlcHMgPSB7fSkge1xuICAgIGlmICghYWdlbnRJZCB8fCAhdHlwZSkgdGhyb3cgbmV3IEVycm9yKCdhZ2VudElkIGFuZCB0eXBlIGFyZSByZXF1aXJlZCcpO1xuICAgIGlmICghVkFMSURfVFlQRVMuaGFzKHR5cGUpKSB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgdHlwZTogJHt0eXBlfS4gVmFsaWQ6ICR7Wy4uLlZBTElEX1RZUEVTXS5qb2luKCcsICcpfWApO1xuICAgIGlmICh0eXBlICE9PSAncGVyc29uYScgJiYgIW5hbWUpIHRocm93IG5ldyBFcnJvcignbmFtZSBpcyByZXF1aXJlZCBmb3IgYWxsIHR5cGVzIGV4Y2VwdCBwZXJzb25hJyk7XG5cbiAgICAvLyBWZXJzaW9ucyBsaXZlIHVuZGVyIC9kZXZlbG9wZXIvPHBsdXJhbD4vOmFnZW50SWQvOmlkL3ZlcnNpb25zLCBFWENFUFRcbiAgICAvLyBwZXJzb25hIHdoaWNoIGxpdmVzIHVuZGVyIC9kZXZlbG9wZXIvYWdlbnRzLzphZ2VudElkL3BlcnNvbmEvdmVyc2lvbnNcbiAgICAvLyAobm8gOmlkKS4gVGhlIDppZCBzbG90IGlzIHRoZSBzZXJ2ZXIgaWQgKHNraWxsSWQgLyB3ZWJob29rSWQgLyAuLi4pLFxuICAgIC8vIG5ldmVyIHRoZSBuYW1lIFx1MjAxNCBzbyByZXNvbHZlIG5hbWUgXHUyMTkyIGlkIGJ5IGxpc3RpbmcgZmlyc3QuXG4gICAgbGV0IHBhdGg7XG4gICAgaWYgKHR5cGUgPT09ICdwZXJzb25hJykge1xuICAgICAgcGF0aCA9IGAvZGV2ZWxvcGVyL2FnZW50cy8ke2VuY29kZVVSSUNvbXBvbmVudChhZ2VudElkKX0vcGVyc29uYS92ZXJzaW9uc2A7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGlkID0gYXdhaXQgcmVzb2x2ZU5hbWVUb0lkKHsgYWdlbnRJZCwgdHlwZSwgbmFtZSwgZmV0Y2hGbjogZGVwcy5mZXRjaEZuIH0pO1xuICAgICAgcGF0aCA9IGAvZGV2ZWxvcGVyLyR7TElTVF9QQVRIU1t0eXBlXX0vJHtlbmNvZGVVUklDb21wb25lbnQoYWdlbnRJZCl9LyR7ZW5jb2RlVVJJQ29tcG9uZW50KGlkKX0vdmVyc2lvbnNgO1xuICAgIH1cblxuICAgIGNvbnN0IGRhdGEgPSBhd2FpdCBhcGlSZXF1ZXN0KHBhdGgsIHsgZmV0Y2hGbjogZGVwcy5mZXRjaEZuIH0pO1xuICAgIGNvbnN0IHZlcnNpb25zID0gZXh0cmFjdFZlcnNpb25zKHR5cGUsIGRhdGEpO1xuICAgIGNvbnN0IGNvbXBhY3QgPSB2ZXJzaW9ucy5tYXAoKHYpID0+ICh7XG4gICAgICB2ZXJzaW9uOiB2LnZlcnNpb24sXG4gICAgICB2ZXJzaW9uSWQ6IHZlcnNpb25JZCh2KSxcbiAgICAgIGFjdGl2ZTogaXNBY3RpdmVWZXJzaW9uKHR5cGUsIHYsIGRhdGEpLFxuICAgICAgY3JlYXRlZEF0OiB2ZXJzaW9uQ3JlYXRlZEF0KHYpLFxuICAgIH0pKTtcbiAgICBjb25zdCByZXN1bHQgPSB7IGFnZW50SWQsIHR5cGUsIG5hbWU6IHR5cGUgPT09ICdwZXJzb25hJyA/IG51bGwgOiBuYW1lLCBhY3RpdmVWZXJzaW9uSWQ6IGV4dHJhY3RBY3RpdmVWZXJzaW9uSWQoZGF0YSksIHZlcnNpb25zOiBjb21wYWN0IH07XG4gICAgcmV0dXJuIHsgY29udGVudDogW3sgdHlwZTogJ3RleHQnLCB0ZXh0OiBKU09OLnN0cmluZ2lmeShyZXN1bHQsIG51bGwsIDIpIH1dIH07XG4gIH0sXG59O1xuXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlTmFtZVRvSWQoeyBhZ2VudElkLCB0eXBlLCBuYW1lLCBmZXRjaEZuIH0pIHtcbiAgY29uc3QgbGlzdFBhdGggPSBgL2RldmVsb3Blci8ke0xJU1RfUEFUSFNbdHlwZV19LyR7ZW5jb2RlVVJJQ29tcG9uZW50KGFnZW50SWQpfWA7XG4gIGNvbnN0IGxpc3RSZXNwb25zZSA9IGF3YWl0IGFwaVJlcXVlc3QobGlzdFBhdGgsIHsgZmV0Y2hGbiB9KTtcbiAgY29uc3QgaXRlbXMgPSBleHRyYWN0TGlzdCh0eXBlLCBsaXN0UmVzcG9uc2UpO1xuICBjb25zdCBtYXRjaCA9IGl0ZW1zLmZpbmQoKHApID0+IHAubmFtZSA9PT0gbmFtZSk7XG4gIGlmICghbWF0Y2gpIHtcbiAgICBjb25zdCBhdmFpbGFibGUgPSBpdGVtcy5tYXAoKHApID0+IHAubmFtZSkuZmlsdGVyKEJvb2xlYW4pLmpvaW4oJywgJykgfHwgJyhub25lKSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBsaXN0X3ByaW1pdGl2ZV92ZXJzaW9uczogbm8gJHt0eXBlfSBuYW1lZCBcIiR7bmFtZX1cIiBvbiBhZ2VudCAke2FnZW50SWR9LiBBdmFpbGFibGU6ICR7YXZhaWxhYmxlfWApO1xuICB9XG4gIGNvbnN0IGlkID0gbWF0Y2guaWQgPz8gbWF0Y2guX2lkO1xuICBpZiAoIWlkKSB0aHJvdyBuZXcgRXJyb3IoYGxpc3RfcHJpbWl0aXZlX3ZlcnNpb25zOiAke3R5cGV9IFwiJHtuYW1lfVwiIGV4aXN0cyBidXQgaGFzIG5vIGlkIGZpZWxkYCk7XG4gIHJldHVybiBpZDtcbn1cbiIsICJpbXBvcnQgeyBhcGlSZXF1ZXN0IH0gZnJvbSAnLi4vYXBpLWNsaWVudC5tanMnO1xuaW1wb3J0IHtcbiAgZXh0cmFjdExpc3QsXG4gIGV4dHJhY3RWZXJzaW9ucyxcbiAgZXh0cmFjdEFjdGl2ZVZlcnNpb25JZCxcbiAgaXNBY3RpdmVWZXJzaW9uLFxuICB2ZXJzaW9uQ3JlYXRlZEF0LFxuICB2ZXJzaW9uSWQsXG4gIExJU1RfUEFUSFMsXG59IGZyb20gJy4uL3Jlc3BvbnNlLXNoYXBlcy5tanMnO1xuXG4vLyBDb21wb3NlcyBcIndoYXQgaXMgbGl2ZVwiIGZyb20gdGhlIHBlci10eXBlIGx1YS1hcGkgbGlzdCArIHZlcnNpb25zIHJvdXRlcy5cbi8vIFRoZXJlIGlzIG5vIHNpbmdsZSBgL2FnZW50cy86aWQvcHJvZHVjdGlvbmAgcm91dGU7IGBsdWEgc3RhdHVzIC0tanNvbmBcbi8vIGdpdmVzIHRoZSBzYW1lIHBpY3R1cmUgZm9yIHRoZSBDVVJSRU5UIHByb2plY3QgKGxvY2FsIHZzIGRlcGxveWVkKSBhbmQgaXNcbi8vIHRoZSBiZXR0ZXIgdG9vbCB3aGVuIGEgbHVhLnNraWxsLnlhbWwgaXMgYXQgaGFuZC4gVGhpcyB0b29sIHdvcmtzIGZvciBhbnlcbi8vIGFnZW50IHRoZSBjcmVkZW50aWFsIGNhbiByZWFjaCwgcHJvamVjdCBvciBub3QuXG4vL1xuLy8gUGVyLXR5cGUgZW52ZWxvcGUgaGFuZGxpbmcgbGl2ZXMgaW4gcmVzcG9uc2Utc2hhcGVzLm1qcy5cbi8vXG4vLyBSZXF1ZXN0IGZhbi1vdXQgKDEgKyA3ICsgTiB2ZXJzaW9uIGxvb2t1cHMpIGlzIHBhcmFsbGVsaXNlZCBpbiB0d28gcGhhc2VzOlxuLy8gICAxLiB0aGUgcGVyc29uYSBjYWxsIGFuZCB0aGUgc2V2ZW4gbGlzdCBjYWxscyBydW4gdG9nZXRoZXI7XG4vLyAgIDIuIHBlciB0eXBlLCBpdGVtIHZlcnNpb24gbG9va3VwcyBydW4gaW4gY2h1bmtzIG9mIFZFUlNJT05TX0NIVU5LX1NJWkUuXG4vLyBPdXRwdXQgb3JkZXIgaXMgZGV0ZXJtaW5pc3RpYyByZWdhcmRsZXNzIG9mIGNvbXBsZXRpb24gb3JkZXI6IHR5cGVzIGluXG4vLyBQUklNSVRJVkVfVFlQRVMgb3JkZXIsIGl0ZW1zIGluIHRoZSBvcmRlciB0aGUgbGlzdCByb3V0ZSByZXR1cm5lZCB0aGVtLlxuLy8gQSB3YWxsLWNsb2NrIGJ1ZGdldCAoQlVER0VUX01TLCBpbmplY3RhYmxlIGFzIGRlcHMuYnVkZ2V0TXMpIHN0b3BzIE5FV1xuLy8gcmVxdWVzdHMgb25jZSBleGNlZWRlZDsgYWxyZWFkeS1pc3N1ZWQgcmVxdWVzdHMgc3RpbGwgY29tcGxldGUuIEl0ZW1zIHRoYXRcbi8vIGNvdWxkIG5vdCBiZSBsb29rZWQgdXAgYXJlIHN0aWxsIGxpc3RlZCwgd2l0aCBgZXJyb3I6IFwic2tpcHBlZDogXHUyMDI2XCJgLCBhbmRcbi8vIHRoZSByZXN1bHQgY2FycmllcyBgcGFydGlhbDogdHJ1ZWAgKyBgcGFydGlhbFJlYXNvbmAuXG5cbmNvbnN0IFBSSU1JVElWRV9UWVBFUyA9IFsnc2tpbGwnLCAnd2ViaG9vaycsICdqb2InLCAndHJpZ2dlcicsICdwcmVwcm9jZXNzb3InLCAncG9zdHByb2Nlc3NvcicsICd3b3JrZmxvdyddO1xuY29uc3QgVkVSU0lPTlNfQ0hVTktfU0laRSA9IDU7XG5jb25zdCBERUZBVUxUX0JVREdFVF9NUyA9IDQ1XzAwMDtcblxuZXhwb3J0IGNvbnN0IGdldERlcGxveW1lbnRTdGF0dXMgPSB7XG4gIHNwZWM6IHtcbiAgICBuYW1lOiAnZ2V0X2RlcGxveW1lbnRfc3RhdHVzJyxcbiAgICBkZXNjcmlwdGlvbjogJ1doYXQgaXMgbGl2ZSByaWdodCBub3cgZm9yIGFuIGFnZW50OiBmb3IgZXZlcnkgc2tpbGwsIHdlYmhvb2ssIGpvYiwgdHJpZ2dlciwgcHJlcHJvY2Vzc29yLCBwb3N0cHJvY2Vzc29yIGFuZCB3b3JrZmxvdyB0aGUgYWN0aXZlIChkZXBsb3llZCkgdmVyc2lvbiwgcGx1cyB0aGUgYWN0aXZlIHBlcnNvbmEgdmVyc2lvbi4gQ29tcG9zZWQgZnJvbSB0aGUgcGVyLXR5cGUgbHVhLWFwaSB2ZXJzaW9uIHJvdXRlcyAobGlzdCBjYWxscyBpbiBwYXJhbGxlbCwgdGhlbiBwZXItaXRlbSB2ZXJzaW9uIGxvb2t1cHMgaW4gY2h1bmtzIG9mIDUpLiBCb3VuZGVkIGJ5IGEgNDUgcyB3YWxsLWNsb2NrIGJ1ZGdldDogaWYgZXhjZWVkZWQsIHJlbWFpbmluZyBpdGVtcyBhcmUgbGlzdGVkIHdpdGggYGVycm9yOiBcInNraXBwZWQ6IFx1MjAyNlwiYCBhbmQgdGhlIHJlc3VsdCBjYXJyaWVzIGBwYXJ0aWFsOiB0cnVlYCArIGBwYXJ0aWFsUmVhc29uYC4gSW5zaWRlIGEgcHJvamVjdCwgYGx1YSBzdGF0dXMgLS1qc29uYCBhZGRpdGlvbmFsbHkgY29tcGFyZXMgbG9jYWwgdnMgZGVwbG95ZWQuJyxcbiAgICBpbnB1dFNjaGVtYToge1xuICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgIGFnZW50SWQ6IHsgdHlwZTogJ3N0cmluZycgfSxcbiAgICAgIH0sXG4gICAgICByZXF1aXJlZDogWydhZ2VudElkJ10sXG4gICAgfSxcbiAgfSxcbiAgYXN5bmMgaGFuZGxlcih7IGFnZW50SWQgfSwgZGVwcyA9IHt9KSB7XG4gICAgaWYgKCFhZ2VudElkKSB0aHJvdyBuZXcgRXJyb3IoJ2FnZW50SWQgaXMgcmVxdWlyZWQnKTtcbiAgICBjb25zdCBpZCA9IGVuY29kZVVSSUNvbXBvbmVudChhZ2VudElkKTtcbiAgICBjb25zdCBidWRnZXRNcyA9IE51bWJlci5pc0Zpbml0ZShkZXBzLmJ1ZGdldE1zKSA/IGRlcHMuYnVkZ2V0TXMgOiBERUZBVUxUX0JVREdFVF9NUztcbiAgICBjb25zdCBzdGFydGVkQXQgPSBEYXRlLm5vdygpO1xuICAgIGNvbnN0IG92ZXJCdWRnZXQgPSAoKSA9PiBEYXRlLm5vdygpIC0gc3RhcnRlZEF0ID4gYnVkZ2V0TXM7XG4gICAgY29uc3QgZ2V0ID0gKHBhdGgpID0+IGFwaVJlcXVlc3QocGF0aCwgeyBmZXRjaEZuOiBkZXBzLmZldGNoRm4gfSk7XG5cbiAgICBjb25zdCByZXN1bHQgPSB7IGFnZW50SWQsIHBlcnNvbmE6IG51bGwsIHByaW1pdGl2ZXM6IHt9IH07XG4gICAgLy8gU2VlZCBrZXlzIHVwIGZyb250IHNvIHRoZSBvdXRwdXQga2V5IG9yZGVyIG5ldmVyIGRlcGVuZHMgb24gdGltaW5nLlxuICAgIGZvciAoY29uc3QgdHlwZSBvZiBQUklNSVRJVkVfVFlQRVMpIHJlc3VsdC5wcmltaXRpdmVzW3R5cGVdID0gW107XG5cbiAgICAvLyBQaGFzZSAxOiBwZXJzb25hICsgZXZlcnkgbGlzdCByb3V0ZSwgYWxsIGluIGZsaWdodCBhdCBvbmNlLlxuICAgIC8vIFBlcnNvbmE6IG9uZSBwZXIgYWdlbnQsIHZlcnNpb25zIHVuZGVyIC9kZXZlbG9wZXIvYWdlbnRzLzphZ2VudElkL3BlcnNvbmEvdmVyc2lvbnMuXG4gICAgY29uc3QgW3BlcnNvbmFPdXRjb21lLCAuLi5saXN0T3V0Y29tZXNdID0gYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcbiAgICAgIGdldChgL2RldmVsb3Blci9hZ2VudHMvJHtpZH0vcGVyc29uYS92ZXJzaW9uc2ApLFxuICAgICAgLi4uUFJJTUlUSVZFX1RZUEVTLm1hcCgodHlwZSkgPT4gZ2V0KGAvZGV2ZWxvcGVyLyR7TElTVF9QQVRIU1t0eXBlXX0vJHtpZH1gKSksXG4gICAgXSk7XG5cbiAgICBpZiAocGVyc29uYU91dGNvbWUuc3RhdHVzID09PSAnZnVsZmlsbGVkJykge1xuICAgICAgY29uc3QgcGVyc29uYVJlc3BvbnNlID0gcGVyc29uYU91dGNvbWUudmFsdWU7XG4gICAgICBjb25zdCB2ZXJzaW9ucyA9IGV4dHJhY3RWZXJzaW9ucygncGVyc29uYScsIHBlcnNvbmFSZXNwb25zZSk7XG4gICAgICBjb25zdCBhY3RpdmUgPSB2ZXJzaW9ucy5maW5kKCh2KSA9PiBpc0FjdGl2ZVZlcnNpb24oJ3BlcnNvbmEnLCB2LCBwZXJzb25hUmVzcG9uc2UpKSA/PyBudWxsO1xuICAgICAgcmVzdWx0LnBlcnNvbmEgPSB7XG4gICAgICAgIGFjdGl2ZVZlcnNpb246IGFjdGl2ZT8udmVyc2lvbiA/PyBudWxsLFxuICAgICAgICBhY3RpdmVWZXJzaW9uQ3JlYXRlZEF0OiB2ZXJzaW9uQ3JlYXRlZEF0KGFjdGl2ZSksXG4gICAgICAgIHZlcnNpb25Db3VudDogdmVyc2lvbnMubGVuZ3RoLFxuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzdWx0LnBlcnNvbmEgPSB7IGVycm9yOiBwZXJzb25hT3V0Y29tZS5yZWFzb24/Lm1lc3NhZ2UgPz8gU3RyaW5nKHBlcnNvbmFPdXRjb21lLnJlYXNvbikgfTtcbiAgICB9XG5cbiAgICAvLyBQaGFzZSAyOiBwZXIgdHlwZSwgcmVzb2x2ZSBlYWNoIGl0ZW0ncyB2ZXJzaW9ucyBpbiBjaHVua3MuIFR5cGVzIGFyZVxuICAgIC8vIHZpc2l0ZWQgc2VxdWVudGlhbGx5IChjYW5vbmljYWwgb3JkZXIpOyB3aXRoaW4gYSB0eXBlIHVwIHRvXG4gICAgLy8gVkVSU0lPTlNfQ0hVTktfU0laRSBsb29rdXBzIGFyZSBpbiBmbGlnaHQgdG9nZXRoZXIuXG4gICAgbGV0IGJ1ZGdldEV4aGF1c3RlZCA9IGZhbHNlO1xuICAgIGxldCBza2lwcGVkID0gMDtcbiAgICBsZXQgdG90YWwgPSAwO1xuXG4gICAgZm9yIChsZXQgdCA9IDA7IHQgPCBQUklNSVRJVkVfVFlQRVMubGVuZ3RoOyB0KyspIHtcbiAgICAgIGNvbnN0IHR5cGUgPSBQUklNSVRJVkVfVFlQRVNbdF07XG4gICAgICBjb25zdCBwYXRoID0gTElTVF9QQVRIU1t0eXBlXTtcbiAgICAgIGNvbnN0IG91dGNvbWUgPSBsaXN0T3V0Y29tZXNbdF07XG4gICAgICBpZiAob3V0Y29tZS5zdGF0dXMgIT09ICdmdWxmaWxsZWQnKSB7XG4gICAgICAgIHJlc3VsdC5wcmltaXRpdmVzW3R5cGVdID0geyBlcnJvcjogb3V0Y29tZS5yZWFzb24/Lm1lc3NhZ2UgPz8gU3RyaW5nKG91dGNvbWUucmVhc29uKSB9O1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gVGhlIFVSTCBzbG90IGlzIHRoZSBzZXJ2ZXIgaWQgKDpza2lsbElkIC8gOndlYmhvb2tJZCAvIC4uLiksIG5ldmVyXG4gICAgICAvLyB0aGUgbmFtZS4gSXRlbXMgd2l0aG91dCBhbiBpZCBhcmUgZHJvcHBlZCwgYXMgYmVmb3JlLlxuICAgICAgY29uc3QgdGFyZ2V0cyA9IGV4dHJhY3RMaXN0KHR5cGUsIG91dGNvbWUudmFsdWUpXG4gICAgICAgIC5tYXAoKHApID0+ICh7IHAsIHByaW1JZDogcC5pZCA/PyBwLl9pZCwgbmFtZTogcC5uYW1lID8/IHAuaWQgPz8gcC5faWQgfSkpXG4gICAgICAgIC5maWx0ZXIoKHRndCkgPT4gdGd0LnByaW1JZCk7XG4gICAgICB0b3RhbCArPSB0YXJnZXRzLmxlbmd0aDtcblxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0YXJnZXRzLmxlbmd0aDsgaSArPSBWRVJTSU9OU19DSFVOS19TSVpFKSB7XG4gICAgICAgIGNvbnN0IGNodW5rID0gdGFyZ2V0cy5zbGljZShpLCBpICsgVkVSU0lPTlNfQ0hVTktfU0laRSk7XG5cbiAgICAgICAgaWYgKGJ1ZGdldEV4aGF1c3RlZCB8fCBvdmVyQnVkZ2V0KCkpIHtcbiAgICAgICAgICBidWRnZXRFeGhhdXN0ZWQgPSB0cnVlO1xuICAgICAgICAgIHNraXBwZWQgKz0gY2h1bmsubGVuZ3RoO1xuICAgICAgICAgIGZvciAoY29uc3QgeyBuYW1lLCBwcmltSWQgfSBvZiBjaHVuaykge1xuICAgICAgICAgICAgcmVzdWx0LnByaW1pdGl2ZXNbdHlwZV0ucHVzaCh7XG4gICAgICAgICAgICAgIG5hbWUsIGlkOiBwcmltSWQsXG4gICAgICAgICAgICAgIGVycm9yOiBgc2tpcHBlZDogd2FsbC1jbG9jayBidWRnZXQgb2YgJHtidWRnZXRNc31tcyBleGhhdXN0ZWQgYmVmb3JlIHRoaXMgdmVyc2lvbiBsb29rdXAgd2FzIGlzc3VlZGAsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBvdXRjb21lcyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChcbiAgICAgICAgICBjaHVuay5tYXAoKHsgcHJpbUlkIH0pID0+IGdldChgL2RldmVsb3Blci8ke3BhdGh9LyR7aWR9LyR7ZW5jb2RlVVJJQ29tcG9uZW50KHByaW1JZCl9L3ZlcnNpb25zYCkpXG4gICAgICAgICk7XG5cbiAgICAgICAgb3V0Y29tZXMuZm9yRWFjaCgodm8sIGopID0+IHtcbiAgICAgICAgICBjb25zdCB7IHAsIHByaW1JZCwgbmFtZSB9ID0gY2h1bmtbal07XG4gICAgICAgICAgaWYgKHZvLnN0YXR1cyAhPT0gJ2Z1bGZpbGxlZCcpIHtcbiAgICAgICAgICAgIHJlc3VsdC5wcmltaXRpdmVzW3R5cGVdLnB1c2goeyBuYW1lLCBpZDogcHJpbUlkLCBlcnJvcjogdm8ucmVhc29uPy5tZXNzYWdlID8/IFN0cmluZyh2by5yZWFzb24pIH0pO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCB2ZXJzaW9uc1Jlc3BvbnNlID0gdm8udmFsdWU7XG4gICAgICAgICAgY29uc3QgdmVyc2lvbnMgPSBleHRyYWN0VmVyc2lvbnModHlwZSwgdmVyc2lvbnNSZXNwb25zZSk7XG4gICAgICAgICAgY29uc3QgYWN0aXZlID0gdmVyc2lvbnMuZmluZCgodikgPT4gaXNBY3RpdmVWZXJzaW9uKHR5cGUsIHYsIHZlcnNpb25zUmVzcG9uc2UpKSA/PyBudWxsO1xuXG4gICAgICAgICAgY29uc3QgZW50cnkgPSB7XG4gICAgICAgICAgICBuYW1lLFxuICAgICAgICAgICAgaWQ6IHByaW1JZCxcbiAgICAgICAgICAgIGFjdGl2ZVZlcnNpb246IGFjdGl2ZT8udmVyc2lvbiA/PyBudWxsLFxuICAgICAgICAgICAgYWN0aXZlVmVyc2lvbklkOiB2ZXJzaW9uSWQoYWN0aXZlKSA/PyBleHRyYWN0QWN0aXZlVmVyc2lvbklkKHZlcnNpb25zUmVzcG9uc2UpLFxuICAgICAgICAgICAgYWN0aXZlVmVyc2lvbkNyZWF0ZWRBdDogdmVyc2lvbkNyZWF0ZWRBdChhY3RpdmUpLFxuICAgICAgICAgICAgdmVyc2lvbkNvdW50OiB2ZXJzaW9ucy5sZW5ndGgsXG4gICAgICAgICAgfTtcbiAgICAgICAgICAvLyBSZWNvcmRzIHRoYXQgY2FycnkgdGhlaXIgb3duIGVuYWJsZWQvZGlzYWJsZWQgc3dpdGNoLlxuICAgICAgICAgIGlmICh0eXBlb2YgcC5hY3RpdmUgPT09ICdib29sZWFuJykgZW50cnkuZW5hYmxlZCA9IHAuYWN0aXZlO1xuICAgICAgICAgIGlmICh0eXBlID09PSAnd29ya2Zsb3cnICYmIHR5cGVvZiBwLmR5bmFtaWMgPT09ICdib29sZWFuJykgZW50cnkuZHluYW1pYyA9IHAuZHluYW1pYztcbiAgICAgICAgICByZXN1bHQucHJpbWl0aXZlc1t0eXBlXS5wdXNoKGVudHJ5KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHNraXBwZWQgPiAwKSB7XG4gICAgICByZXN1bHQucGFydGlhbCA9IHRydWU7XG4gICAgICByZXN1bHQucGFydGlhbFJlYXNvbiA9XG4gICAgICAgIGBXYWxsLWNsb2NrIGJ1ZGdldCBvZiAke2J1ZGdldE1zfW1zIGV4Y2VlZGVkIGFmdGVyICR7RGF0ZS5ub3coKSAtIHN0YXJ0ZWRBdH1tczsgYCArXG4gICAgICAgIGAke3NraXBwZWR9IG9mICR7dG90YWx9IHZlcnNpb24gbG9va3VwcyB3ZXJlIG5vdCBpc3N1ZWQgKGVudHJpZXMgY2FycnkgZXJyb3I6IFwic2tpcHBlZDogXHUyMDI2XCIpLmA7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGNvbnRlbnQ6IFt7IHR5cGU6ICd0ZXh0JywgdGV4dDogSlNPTi5zdHJpbmdpZnkocmVzdWx0LCBudWxsLCAyKSB9XSxcbiAgICB9O1xuICB9LFxufTtcbiIsICJpbXBvcnQgeyBhcGlSZXF1ZXN0IH0gZnJvbSAnLi4vYXBpLWNsaWVudC5tanMnO1xuXG4vLyBUaGUgYGxvZ1NvdXJjZWAgZW51bSBsdWEtYXBpIGFjY2VwdHMgXHUyMDE0IEFHRU5UX0xPR19TT1VSQ0VTIGluXG4vLyBwYWNrYWdlcy9zaGFyZWQtdHlwZXMvc3JjL3ZtLWV4ZWN1dGlvbi1sb2cudHlwZXMudHMgKHRoZSBzaW5nbGUgc291cmNlIG9mXG4vLyB0cnV0aCBldmVyeSBzZXJ2aWNlIGltcG9ydHMpLiBgYWxsYCBpcyB0aGUgTUNQLXNpZGUgc2VudGluZWwgZm9yIFwibm9cbi8vIGZpbHRlclwiLiBOb3RlOiBgbHVhIGxvZ3MgLS10eXBlYCBhY2NlcHRzIGEgbmFycm93ZXIgbGlzdCBhbmQgbm8gYG1hc3RyYWAuXG5leHBvcnQgY29uc3QgTE9HX1NPVVJDRVMgPSBbXG4gICdza2lsbCcsICdqb2InLCAnd2ViaG9vaycsICd0cmlnZ2VyJywgJ3ByZXByb2Nlc3NvcicsICdwb3N0cHJvY2Vzc29yJyxcbiAgJ3VzZXJfbWVzc2FnZScsICdhZ2VudF9yZXNwb25zZScsICdhZ2VudF9lcnJvcicsICdydW50aW1lJywgJ21jcCcsICdyYWcnLFxuICAnZGV2aWNlJywgJ2RldmljZS10cmlnZ2VyJywgJ21vZGVsLXJlc29sdmVyJyxcbiAgJ3dvcmtmbG93LXN0ZXAnLCAnd29ya2Zsb3ctc2NyaXB0JywgJ3dvcmtmbG93Jyxcbl07XG5cbi8vIFRoZSBgbG9nVHlwZWAgcXVlcnkgcGFyYW0gXHUyMDE0IEdFVCAvZGV2ZWxvcGVyL2FnZW50cy86YWdlbnRJZC9sb2dzIHJlYWRzIGl0XG4vLyAoY29udHJvbGxlcnMvZGV2ZWxvcGVyL2Jhc2UuY29udHJvbGxlci50cykgYW5kIGRldmVsb3Blci5zZXJ2aWNlLnRzXG4vLyBnZXRBZ2VudExvZ3MgYXBwbGllcyBpdCB2ZXJiYXRpbSBhcyBgZmlsdGVyLnN1YlR5cGUgPSBsb2dUeXBlYC4gVGhlXG4vLyBhY2NlcHRlZCB2YWx1ZXMgYXJlIHRoZXJlZm9yZSB0aGUgVm1FeGVjdXRpb25Mb2cgYHN1YlR5cGVgIGVudW0gaW5cbi8vIHBhY2thZ2VzL3NoYXJlZC1zY2hlbWFzL3NyYy92bUV4ZWN1dGlvbkxvZ3Muc2NoZW1hLnRzOlxuLy8gICBlbnVtOiBbJ3N0YXJ0JywgJ2NvbXBsZXRlJywgJ2Vycm9yJywgJ2RlYnVnJywgJ3dhcm4nLCAnaW5mbyddXG4vLyAobWlycm9yZWQgYnkgbHVhLWNsaSdzIExvZ0VudHJ5LnN1YlR5cGUgaW4gc3JjL2ludGVyZmFjZXMvbG9ncy50cykuIEFueVxuLy8gb3RoZXIgc3RyaW5nIGlzIG5vdCByZWplY3RlZCBzZXJ2ZXItc2lkZSBcdTIwMTQgaXQgc2ltcGx5IG1hdGNoZXMgbm90aGluZy5cbmV4cG9ydCBjb25zdCBMT0dfVFlQRVMgPSBbJ2Vycm9yJywgJ3dhcm4nLCAnaW5mbycsICdkZWJ1ZycsICdzdGFydCcsICdjb21wbGV0ZSddO1xuXG5jb25zdCBWQUxJRF9UWVBFUyA9IG5ldyBTZXQoWydhbGwnLCAuLi5MT0dfU09VUkNFU10pO1xuY29uc3QgVkFMSURfTE9HX1RZUEVTID0gbmV3IFNldChMT0dfVFlQRVMpO1xuXG5leHBvcnQgY29uc3QgdGFpbExvZ3MgPSB7XG4gIHNwZWM6IHtcbiAgICBuYW1lOiAndGFpbF9sb2dzJyxcbiAgICBkZXNjcmlwdGlvbjogJ0ZldGNoIHJlY2VudCBleGVjdXRpb24gbG9ncyBmb3IgYW4gYWdlbnQgKHNhbWUgZGF0YSBhcyBgbHVhIGxvZ3MgLS1qc29uYCkuIFJldHVybnMgeyBsb2dzOiBMb2dFbnRyeVtdLCBwYWdpbmF0aW9uIH0uIEVhY2ggTG9nRW50cnkgaGFzIGBzdWJUeXBlYCAoZXJyb3IgfCB3YXJuIHwgaW5mbyB8IGRlYnVnIHwgc3RhcnQgfCBjb21wbGV0ZSksIGBtZXNzYWdlYCwgYHRpbWVzdGFtcGAsIGFuZCBgbWV0YWRhdGEubG9nU291cmNlYCAvIGBtZXRhZGF0YS5wcmltaXRpdmVOYW1lYCBcdTIwMTQgdGhlcmUgaXMgTk8gYGxldmVsYCBmaWVsZC4gRmlsdGVyIGJ5IHNvdXJjZSAoYHR5cGVgKSwgcHJpbWl0aXZlIChgbmFtZWApIGFuZC9vciBzZXZlcml0eSAoYGxvZ1R5cGVgLCB3aGljaCBmaWx0ZXJzIG9uIGBzdWJUeXBlYCkuJyxcbiAgICBpbnB1dFNjaGVtYToge1xuICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgIGFnZW50SWQ6IHsgdHlwZTogJ3N0cmluZycgfSxcbiAgICAgICAgdHlwZToge1xuICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgIGVudW06IFsuLi5WQUxJRF9UWVBFU10sXG4gICAgICAgICAgZGVmYXVsdDogJ2FsbCcsXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdMb2cgc291cmNlIGZpbHRlciAobWFwcyB0byBsdWEtYXBpIGBsb2dTb3VyY2VgKS4gYGFsbGAgPSBubyBmaWx0ZXIuJyxcbiAgICAgICAgfSxcbiAgICAgICAgbmFtZTogeyB0eXBlOiAnc3RyaW5nJywgZGVzY3JpcHRpb246ICdGaWx0ZXIgYnkgcHJpbWl0aXZlIG5hbWUgKG9wdGlvbmFsOyBtYXBzIHRvIGBwcmltaXRpdmVOYW1lYCknIH0sXG4gICAgICAgIGxvZ1R5cGU6IHtcbiAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICBlbnVtOiBMT0dfVFlQRVMsXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdGaWx0ZXIgYnkgTG9nRW50cnkgYHN1YlR5cGVgIChvcHRpb25hbDsgbWFwcyB0byBsdWEtYXBpIGBsb2dUeXBlYCkuIE9taXQgZm9yIGV2ZXJ5IHNldmVyaXR5LiBlLmcuIGBlcnJvcmAgdG8gc2VlIG9ubHkgZmFpbHVyZXMuJyxcbiAgICAgICAgfSxcbiAgICAgICAgLy8gbHVhLWFwaSBjYXBzIGxpbWl0IGF0IDEwMCBzZXJ2ZXItc2lkZSAoTWF0aC5taW4oMTAwLCAuLi4pIGluXG4gICAgICAgIC8vIGNvbnRyb2xsZXJzL2RldmVsb3Blci9iYXNlLmNvbnRyb2xsZXIudHMpLiBNYXRjaCB0aGUgc2VydmVyIGNhcCBzb1xuICAgICAgICAvLyB0aGUgdG9vbCdzIGNvbnRyYWN0IGlzIGhvbmVzdC5cbiAgICAgICAgbGltaXQ6IHsgdHlwZTogJ251bWJlcicsIGRlZmF1bHQ6IDUwLCBtaW5pbXVtOiAxLCBtYXhpbXVtOiAxMDAgfSxcbiAgICAgICAgcGFnZTogeyB0eXBlOiAnbnVtYmVyJywgbWluaW11bTogMSwgZGVzY3JpcHRpb246ICdQYWdlIG51bWJlciBmb3IgcGFnaW5hdGlvbiAob3B0aW9uYWwpJyB9LFxuICAgICAgfSxcbiAgICAgIHJlcXVpcmVkOiBbJ2FnZW50SWQnXSxcbiAgICB9LFxuICB9LFxuICBhc3luYyBoYW5kbGVyKHsgYWdlbnRJZCwgdHlwZSA9ICdhbGwnLCBuYW1lLCBsb2dUeXBlLCBsaW1pdCA9IDUwLCBwYWdlIH0sIGRlcHMgPSB7fSkge1xuICAgIGlmICghYWdlbnRJZCkgdGhyb3cgbmV3IEVycm9yKCdhZ2VudElkIGlzIHJlcXVpcmVkJyk7XG4gICAgaWYgKCFWQUxJRF9UWVBFUy5oYXModHlwZSkpIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCB0eXBlOiAke3R5cGV9LiBWYWxpZDogJHtbLi4uVkFMSURfVFlQRVNdLmpvaW4oJywgJyl9YCk7XG4gICAgaWYgKGxvZ1R5cGUgIT09IHVuZGVmaW5lZCAmJiAhVkFMSURfTE9HX1RZUEVTLmhhcyhsb2dUeXBlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGxvZ1R5cGU6ICR7bG9nVHlwZX0uIFZhbGlkOiAke0xPR19UWVBFUy5qb2luKCcsICcpfWApO1xuICAgIH1cbiAgICAvLyBEZWZlbnNpdmUgY2FwIG1hdGNoaW5nIHRoZSBzZXJ2ZXItc2lkZSBsaW1pdDsgcmVqZWN0cyBsb3VkIHJhdGhlclxuICAgIC8vIHRoYW4gc2lsZW50bHkgdHJ1bmNhdGluZy5cbiAgICBpZiAobGltaXQgPiAxMDApIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBsaW1pdDogJHtsaW1pdH0uIGx1YS1hcGkgY2FwcyBsb2dzIGF0IDEwMCBlbnRyaWVzIHBlciBjYWxsLmApO1xuICAgIC8vIFJvdXRlOiBHRVQgL2RldmVsb3Blci9hZ2VudHMvOmFnZW50SWQvbG9ncyAoY29udHJvbGxlcnMvZGV2ZWxvcGVyL2Jhc2UuY29udHJvbGxlci50cykuXG4gICAgLy8gUXVlcnkgcGFyYW1zIHRoZSBzZXJ2ZXIgcmVhZHM6IHBhZ2UsIGxpbWl0LCBzb3J0QnksIHNvcnRPcmRlciwgbG9nU291cmNlLFxuICAgIC8vIHByaW1pdGl2ZUlkLCBwcmltaXRpdmVOYW1lLCB0b29sSWQsIHRvb2xOYW1lLCBsb2dUeXBlLCB1c2VySWQuIFRoZVxuICAgIC8vIHR5cGUgZmlsdGVyIGlzIGBsb2dTb3VyY2VgIFx1MjAxNCBhIGBwcmltaXRpdmVUeXBlYCBwYXJhbSBpcyBzaWxlbnRseSBpZ25vcmVkLlxuICAgIGNvbnN0IHF1ZXJ5ID0geyBsaW1pdCB9O1xuICAgIGlmICh0eXBlICE9PSAnYWxsJykgcXVlcnkubG9nU291cmNlID0gdHlwZTtcbiAgICBpZiAobmFtZSkgcXVlcnkucHJpbWl0aXZlTmFtZSA9IG5hbWU7XG4gICAgaWYgKGxvZ1R5cGUpIHF1ZXJ5LmxvZ1R5cGUgPSBsb2dUeXBlO1xuICAgIGlmIChwYWdlKSBxdWVyeS5wYWdlID0gcGFnZTtcbiAgICBjb25zdCBkYXRhID0gYXdhaXQgYXBpUmVxdWVzdChcbiAgICAgIGAvZGV2ZWxvcGVyL2FnZW50cy8ke2VuY29kZVVSSUNvbXBvbmVudChhZ2VudElkKX0vbG9nc2AsXG4gICAgICB7IHF1ZXJ5LCBmZXRjaEZuOiBkZXBzLmZldGNoRm4gfVxuICAgICk7XG4gICAgcmV0dXJuIHsgY29udGVudDogW3sgdHlwZTogJ3RleHQnLCB0ZXh0OiBKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCAyKSB9XSB9O1xuICB9LFxufTtcbiJdLAogICJtYXBwaW5ncyI6ICI2RkFFQSxPQUFTLFVBQUFBLE9BQWMsNENBQ3ZCLE9BQVMsd0JBQUFDLE9BQTRCLDRDQUNy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Q0FBTyxFQUFJO0FBQUEsRUFBTSxJQUFNLFFBQVEsS0FBSyxDQUFDLENBQUMsQ0FDNUUsQ0FFQSxRQUFRLEdBQUcsb0JBQXNCRixHQUFRLENBQ3ZDQyxHQUFtQixDQUNqQixNQUFPLG1CQUNQLEtBQU0sb0JBQ04sUUFBU0QsR0FBSyxRQUNkLE1BQU9BLEdBQUssTUFDWixlQUFnQixRQUNoQixnQkFBaUIsUUFBUSxJQUFJLGlCQUFtQixLQUNoRCxTQUFVLFFBQVEsU0FDbEIsR0FBSSxJQUFJLEtBQUssRUFBRSxZQUFZLENBQzdCLENBQUMsQ0FDSCxDQUFDLEVBRUQsUUFBUSxHQUFHLHFCQUF1QkcsR0FBVyxDQUMzQ0YsR0FBbUIsQ0FDakIsTUFBTyxtQkFDUCxLQUFNLHFCQUNOLE9BQVEsT0FBT0UsQ0FBTSxFQUNyQixNQUEyQkEsR0FBUyxNQUNwQyxlQUFnQixRQUNoQixnQkFBaUIsUUFBUSxJQUFJLGlCQUFtQixLQUNoRCxTQUFVLFFBQVEsU0FDbEIsR0FBSSxJQUFJLEtBQUssRUFBRSxZQUFZLENBQzdCLENBQUMsQ0FDSCxDQUFDLEVBRUQsTUFBTVYsRUFBTyxRQUFRLElBQUlXLEVBQXNCIiwKICAibmFtZXMiOiBbIlNlcnZlciIsICJTdGRpb1NlcnZlclRyYW5zcG9ydCIsICJDYWxsVG9vbFJlcXVlc3RTY2hlbWEiLCAiTGlzdFRvb2xzUmVxdWVzdFNjaGVtYSIsICJ0b29sc19leHBvcnRzIiwgIl9fZXhwb3J0IiwgImdldEFnZW50IiwgImdldERlcGxveW1lbnRTdGF0dXMiLCAibGlzdEFnZW50cyIsICJsaXN0UHJpbWl0aXZlVmVyc2lvbnMiLCAidGFpbExvZ3MiLCAic3Bhd24iLCAiREVGQVVMVF9USU1FT1VUX01TIiwgInJ1bkx1YSIsICJhcmdzIiwgInNwYXduRm4iLCAidGltZW91dE1zIiwgImxhYmVsIiwgInJlc29sdmUiLCAicmVqZWN0IiwgImNoaWxkIiwgInN0ZG91dCIsICJzdGRlcnIiLCAiZCIsICJ0aW1lciIsICJjb2RlIiwgImVyciIsICJmbGF0dGVuQWdlbnRzIiwgInBhcnNlZCIsICJvYmoiLCAiZW50cmllcyIsICJvcmciLCAiYSIsICJsaXN0QWdlbnRzVmlhQ2xpIiwgImxpc3RBZ2VudHMiLCAiX2FyZ3MiLCAiZGVwcyIsICJjb21wYWN0IiwgImxpc3RBZ2VudHNWaWFDbGkiLCAiZ2V0QWdlbnQiLCAiYWdlbnRJZCIsICJkZXBzIiwgIm1hdGNoIiwgImxpc3RBZ2VudHNWaWFDbGkiLCAiYSIsICJyZWFkRmlsZSIsICJyZWFkZGlyIiwgInN0YXQiLCAiam9pbiIsICJkaXJuYW1lIiwgImhvbWVkaXIiLCAiREVGQVVMVF9BUElfVVJMIiwgIkZJUkVCQVNFX1JFRlJFU0hfVVJMIiwgIlJFRlJFU0hfVElNRU9VVF9NUyIsICJSRUZSRVNIX1NLRVdfTVMiLCAiUkVKRUNURURfUkVGUkVTSF9SRUFTT05TIiwgInRva2VuQ2FjaGUiLCAicGFyc2VEb3RlbnZBcGlLZXkiLCAiY29udGVudCIsICJyYXdMaW5lIiwgIm0iLCAidmFsdWUiLCAicXVvdGVkIiwgImZpbmRTZXNzaW9uRmlsZSIsICJzZXNzaW9uc0RpciIsICJhcGlVcmwiLCAibmFtZXMiLCAicmVhZGRpciIsICJjYW5kaWRhdGVzIiwgIm5hbWUiLCAicGF0aCIsICJqb2luIiwgInNlc3Npb24iLCAicmVhZEZpbGUiLCAibXRpbWVNcyIsICJzdGF0IiwgImEiLCAiYiIsICJyZWZyZXNoU2Vzc2lvbkJlYXJlciIsICJmZXRjaEZuIiwgIm5vdyIsICJjYWNoZWQiLCAidG9rZW5DYWNoZSIsICJSRUZSRVNIX1NLRVdfTVMiLCAiY29udHJvbGxlciIsICJ0aW1lciIsICJSRUZSRVNIX1RJTUVPVVRfTVMiLCAicmVzIiwgIkZJUkVCQVNFX1JFRlJFU0hfVVJMIiwgImVyciIsICJyZWFzb24iLCAibWVzc2FnZSIsICJyZWplY3RlZCIsICJSRUpFQ1RFRF9SRUZSRVNIX1JFQVNPTlMiLCAiciIsICJib2R5IiwgImlkVG9rZW4iLCAiZXhwaXJlc0luIiwgInJlc29sdmVBcGlLZXkiLCAiZW52IiwgImNyZWRlbnRpYWxzUGF0aCIsICJob21lZGlyIiwgImRpcm5hbWUiLCAiY3dkIiwgIkRFRkFVTFRfQVBJX1VSTCIsICJmcm9tRG90ZW52IiwgImZvdW5kIiwgInJhdyIsICJwYXJzZWQiLCAiREVGQVVMVF9CQVNFX1VSTCIsICJERUZBVUxUX1RJTUVPVVRfTVMiLCAiUk9VVEVfU0NPUEVTIiwgIlJPVVRFX1NDT1BFX1NVTU1BUlkiLCAiYXBpUmVxdWVzdCIsICJwYXRoIiwgIm1ldGhvZCIsICJib2R5IiwgInF1ZXJ5IiwgImZldGNoRm4iLCAiYmFzZVVybCIsICJ0aW1lb3V0TXMiLCAiYXBpS2V5IiwgInJlc29sdmVBcGlLZXkiLCAidXJsIiwgImsiLCAidiIsICJjb250cm9sbGVyIiwgInRpbWVyIiwgInJlcyIsICJyYXciLCAiZnJpZW5kbHlNc2ciLCAicGFyc2VkIiwgImNvZGUiLCAiZXJyIiwgIkxJU1RfRVhUUkFDVE9SUyIsICJyIiwgIlZFUlNJT05TX0VYVFJBQ1RPUlMiLCAiTElTVF9QQVRIUyIsICJleHRyYWN0TGlzdCIsICJ0eXBlIiwgInJlc3BvbnNlIiwgImZuIiwgImV4dHJhY3RWZXJzaW9ucyIsICJleHRyYWN0QWN0aXZlVmVyc2lvbklkIiwgImlkIiwgImlzQWN0aXZlVmVyc2lvbiIsICJ2ZXJzaW9uIiwgImFjdGl2ZUlkIiwgIm93bklkIiwgInZlcnNpb25DcmVhdGVkQXQiLCAicmF3IiwgInZlcnNpb25JZCIsICJTVVBQT1JURURfTElTVF9UWVBFUyIsICJTVVBQT1JURURfVkVSU0lPTl9UWVBFUyIsICJWQUxJRF9UWVBFUyIsICJTVVBQT1JURURfVkVSU0lPTl9UWVBFUyIsICJsaXN0UHJpbWl0aXZlVmVyc2lvbnMiLCAiYWdlbnRJZCIsICJ0eXBlIiwgIm5hbWUiLCAiZGVwcyIsICJwYXRoIiwgImlkIiwgInJlc29sdmVOYW1lVG9JZCIsICJMSVNUX1BBVEhTIiwgImRhdGEiLCAiYXBpUmVxdWVzdCIsICJjb21wYWN0IiwgImV4dHJhY3RWZXJzaW9ucyIsICJ2IiwgInZlcnNpb25JZCIsICJpc0FjdGl2ZVZlcnNpb24iLCAidmVyc2lvbkNyZWF0ZWRBdCIsICJyZXN1bHQiLCAiZXh0cmFjdEFjdGl2ZVZlcnNpb25JZCIsICJmZXRjaEZuIiwgImxpc3RQYXRoIiwgImxpc3RSZXNwb25zZSIsICJpdGVtcyIsICJleHRyYWN0TGlzdCIsICJtYXRjaCIsICJwIiwgImF2YWlsYWJsZSIsICJQUklNSVRJVkVfVFlQRVMiLCAiVkVSU0lPTlNfQ0hVTktfU0laRSIsICJERUZBVUxUX0JVREdFVF9NUyIsICJnZXREZXBsb3ltZW50U3RhdHVzIiwgImFnZW50SWQiLCAiZGVwcyIsICJpZCIsICJidWRnZXRNcyIsICJzdGFydGVkQXQiLCAib3ZlckJ1ZGdldCIsICJnZXQiLCAicGF0aCIsICJhcGlSZXF1ZXN0IiwgInJlc3VsdCIsICJ0eXBlIiwgInBlcnNvbmFPdXRjb21lIiwgImxpc3RPdXRjb21lcyIsICJMSVNUX1BBVEhTIiwgInBlcnNvbmFSZXNwb25zZSIsICJ2ZXJzaW9ucyIsICJleHRyYWN0VmVyc2lvbnMiLCAiYWN0aXZlIiwgInYiLCAiaXNBY3RpdmVWZXJzaW9uIiwgInZlcnNpb25DcmVhdGVkQXQiLCAiYnVkZ2V0RXhoYXVzdGVkIiwgInNraXBwZWQiLCAidG90YWwiLCAidCIsICJvdXRjb21lIiwgInRhcmdldHMiLCAiZXh0cmFjdExpc3QiLCAicCIsICJ0Z3QiLCAiaSIsICJjaHVuayIsICJuYW1lIiwgInByaW1JZCIsICJ2byIsICJqIiwgInZlcnNpb25zUmVzcG9uc2UiLCAiZW50cnkiLCAidmVyc2lvbklkIiwgImV4dHJhY3RBY3RpdmVWZXJzaW9uSWQiLCAiTE9HX1NPVVJDRVMiLCAiTE9HX1RZUEVTIiwgIlZBTElEX1RZUEVTIiwgIlZBTElEX0xPR19UWVBFUyIsICJ0YWlsTG9ncyIsICJhZ2VudElkIiwgInR5cGUiLCAibmFtZSIsICJsb2dUeXBlIiwgImxpbWl0IiwgInBhZ2UiLCAiZGVwcyIsICJxdWVyeSIsICJkYXRhIiwgImFwaVJlcXVlc3QiLCAiVE9PTF9SRUdJU1RSWSIsICJ0b29sc19leHBvcnRzIiwgInNlcnZlciIsICJTZXJ2ZXIiLCAiTGlzdFRvb2xzUmVxdWVzdFNjaGVtYSIsICJ0IiwgIkNhbGxUb29sUmVxdWVzdFNjaGVtYSIsICJyZXEiLCAidG9vbCIsICJlcnIiLCAicmVwb3J0Q3Jhc2hBbmRFeGl0IiwgInBheWxvYWQiLCAicmVhc29uIiwgIlN0ZGlvU2VydmVyVHJhbnNwb3J0Il0KfQo= diff --git a/plugins/lua-agent-builder/mcp/lua-platform/package-lock.json b/plugins/lua-agent-builder/mcp/lua-platform/package-lock.json index afd23da..b451b1f 100644 --- a/plugins/lua-agent-builder/mcp/lua-platform/package-lock.json +++ b/plugins/lua-agent-builder/mcp/lua-platform/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lua/claude-plugin-mcp", - "version": "1.4.0", + "version": "1.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@lua/claude-plugin-mcp", - "version": "1.4.0", + "version": "1.5.0", "dependencies": { "@modelcontextprotocol/sdk": "~0.6.0" }, diff --git a/plugins/lua-agent-builder/mcp/lua-platform/package.json b/plugins/lua-agent-builder/mcp/lua-platform/package.json index d8d07f0..1250fb4 100644 --- a/plugins/lua-agent-builder/mcp/lua-platform/package.json +++ b/plugins/lua-agent-builder/mcp/lua-platform/package.json @@ -1,6 +1,6 @@ { "name": "@lua/claude-plugin-mcp", - "version": "1.4.0", + "version": "1.5.0", "description": "Read-only MCP server for the lua-agent-builder Claude Code plugin", "type": "module", "main": "dist/server.js", diff --git a/plugins/lua-agent-builder/mcp/lua-platform/src/api-client.mjs b/plugins/lua-agent-builder/mcp/lua-platform/src/api-client.mjs index b4b8317..4b8ba48 100644 --- a/plugins/lua-agent-builder/mcp/lua-platform/src/api-client.mjs +++ b/plugins/lua-agent-builder/mcp/lua-platform/src/api-client.mjs @@ -69,7 +69,7 @@ export async function apiRequest(path, { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', - 'X-Lua-Client': 'claude-plugin/1.4.0', + 'X-Lua-Client': 'claude-plugin/1.5.0', }, body: body ? JSON.stringify(body) : undefined, signal: controller.signal, diff --git a/plugins/lua-agent-builder/mcp/lua-platform/src/server.mjs b/plugins/lua-agent-builder/mcp/lua-platform/src/server.mjs index 95e55a2..8f787dc 100644 --- a/plugins/lua-agent-builder/mcp/lua-platform/src/server.mjs +++ b/plugins/lua-agent-builder/mcp/lua-platform/src/server.mjs @@ -11,7 +11,7 @@ import * as tools from './tools/index.mjs'; const TOOL_REGISTRY = Object.values(tools); const server = new Server( - { name: 'lua-platform', version: '1.4.0' }, + { name: 'lua-platform', version: '1.5.0' }, { capabilities: { tools: {} } } ); @@ -46,7 +46,7 @@ process.on('uncaughtException', (err) => { kind: 'uncaughtException', message: err?.message, stack: err?.stack, - plugin_version: '1.4.0', + plugin_version: '1.5.0', lua_cli_version: process.env.LUA_CLI_VERSION ?? null, platform: process.platform, ts: new Date().toISOString(), @@ -59,7 +59,7 @@ process.on('unhandledRejection', (reason) => { kind: 'unhandledRejection', reason: String(reason), stack: /** @type {any} */ (reason)?.stack, - plugin_version: '1.4.0', + plugin_version: '1.5.0', lua_cli_version: process.env.LUA_CLI_VERSION ?? null, platform: process.platform, ts: new Date().toISOString(), diff --git a/plugins/lua-agent-builder/package-lock.json b/plugins/lua-agent-builder/package-lock.json index d613155..7b31cf7 100644 --- a/plugins/lua-agent-builder/package-lock.json +++ b/plugins/lua-agent-builder/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-code-lua-plugin", - "version": "1.4.0", + "version": "1.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-code-lua-plugin", - "version": "1.4.0", + "version": "1.5.0", "devDependencies": { "@types/node": "^20.11.0", "eslint": "^8.57.0", diff --git a/plugins/lua-agent-builder/package.json b/plugins/lua-agent-builder/package.json index 23350ef..2614d5d 100644 --- a/plugins/lua-agent-builder/package.json +++ b/plugins/lua-agent-builder/package.json @@ -1,6 +1,6 @@ { "name": "claude-code-lua-plugin", - "version": "1.4.0", + "version": "1.5.0", "description": "Plugin assets for the lua-agent-builder Claude Code plugin", "private": true, "type": "module", diff --git a/plugins/lua-agent-builder/scripts/lint-cli-flags.mjs b/plugins/lua-agent-builder/scripts/lint-cli-flags.mjs index c426017..f4f828a 100644 --- a/plugins/lua-agent-builder/scripts/lint-cli-flags.mjs +++ b/plugins/lua-agent-builder/scripts/lint-cli-flags.mjs @@ -12,6 +12,10 @@ // the legacy spelling for months. // - `lua logs --type mastra` appears in the CLI's own help text but is // rejected by the alias table (exit 2). +// - `lua logs --follow` was denied until lua-cli 3.38.0, which added it +// (PRO-1838 / A5) alongside `--since`, `--until` and `--environment`. +// The entry was removed, not inverted — the same mistake that made the +// plugin ship `lua sync --accept` for months. // - `lua deploy workflow|mcp|device|voice` are not deploy types; workflows // go live with `lua workflows deploy`, MCP servers with `lua mcp activate`. // - `defineTool` / `lua-cli/skill` / `welcomeMessage` / `Jobs.schedule` / @@ -37,7 +41,15 @@ const DENY = [ { pattern: 'lua jobs list', reason: 'the action is `lua jobs view`' }, { pattern: 'lua jobs run ', reason: 'the action is `lua jobs trigger -i `' }, { pattern: 'lua chat --json', reason: '`lua chat` has no --json flag' }, - { pattern: 'lua logs --follow', reason: '`lua logs` has no --follow flag (one-shot fetch)' }, + // `lua logs --follow` WAS denied here: it did not exist before lua-cli 3.38.0. + // PRO-1838 (A5) shipped it (a poll, not a stream), together with --since/--until/ + // --environment, so the entry is gone rather than inverted. What is still wrong is + // a SEVERITY flag on `lua logs`: --min-severity is a log-DRAIN selector and `lua + // logs` has no severity option at all — severity is filtered client-side on `subType`. + { pattern: 'lua logs --min-severity', reason: '`lua logs` has no severity flag; `--min-severity` is a `lua drains` selector — filter on the entry\'s `subType` instead' }, + // lua-cli 3.38.0 `commands/drains.mutations.ts` `updateDrainCore`: a drain cannot + // change kind in place, and the flag is refused before any request is sent. + { pattern: 'lua drains update --type', reason: 'a drain cannot change type in place — delete it and create a new one' }, { pattern: 'lua dev ', reason: 'there is no `lua dev` command in lua-cli 3.x' }, { pattern: 'lua integrations add', reason: 'the action is `lua integrations connect --integration `' }, { pattern: 'lua triggers pause', reason: 'integration triggers moved to `lua integrations webhooks pause --webhook-id `' }, diff --git a/plugins/lua-agent-builder/scripts/lint-knowledge-commands.mjs b/plugins/lua-agent-builder/scripts/lint-knowledge-commands.mjs index c4d414c..c17f9e3 100644 --- a/plugins/lua-agent-builder/scripts/lint-knowledge-commands.mjs +++ b/plugins/lua-agent-builder/scripts/lint-knowledge-commands.mjs @@ -52,7 +52,8 @@ function setSub(parent, set) { for (const s of set) SUBCOMMANDS[parent].add(s); } -// Hand-curated from command-definitions.ts / aliases.ts (lua-cli 3.33.0, plus +// Hand-curated from command-definitions.ts / aliases.ts (lua-cli 3.33.0, the +// `drains` verbs of lua-cli 3.38.0, plus // the `workflows` verbs of lua-cli 3.36.0 — `policy`, `clear-gate`, // `recompose`, aliases.ts `workflows.action`); the lint won't try to parse free // text exhaustively (too brittle). Update when commands gain/lose actions. @@ -78,6 +79,10 @@ setSub('mcp', ['list', 'activate', 'deactivate', 'delete']); setSub('devices', ['list', 'status', 'enable', 'disable', 'remove', 'test', 'test-trigger']); setSub('jobs', ['view', 'versions', 'deploy', 'activate', 'deactivate', 'trigger', 'history', 'delete']); setSub('webhooks', ['view', 'versions', 'deploy', 'activate', 'deactivate', 'delete', 'list-events', 'subscribe', 'unsubscribe']); +// lua-cli 3.38.0 (PRO-1798, log drains) — `src/utils/aliases.ts` `drains.action`. +// `rotate-secret` keeps its hyphen: it is the route name too. +setSub('drains', ['list', 'status', 'deliveries', 'create', 'update', 'delete', 'test', 'verify', + 'pause', 'resume', 'rotate-secret']); setSub('preprocessors', ['view', 'versions', 'deploy', 'activate', 'deactivate', 'delete']); setSub('postprocessors', ['view', 'versions', 'deploy', 'activate', 'deactivate', 'delete']); setSub('skills', ['sandbox', 'staging', 'production', 'view', 'versions', 'deploy', 'delete']); diff --git a/plugins/lua-agent-builder/test/lib/permissions-mirror.test.mjs b/plugins/lua-agent-builder/test/lib/permissions-mirror.test.mjs index 575764e..83ffce6 100644 --- a/plugins/lua-agent-builder/test/lib/permissions-mirror.test.mjs +++ b/plugins/lua-agent-builder/test/lib/permissions-mirror.test.mjs @@ -183,6 +183,13 @@ describe('permission layer (lib/permissions-template.json) under Claude Code sem 'lua status --json --ci', 'lua chat --ci -e production -m ping -t lua-plugin-smoke-1', 'lua logs --ci --type all --limit 20 --json', + // lua-cli 3.38.0: the `lua logs` read window, and the log-drain READ verbs + // (`test` sends one record down the real path but changes no configuration). + 'lua logs --ci --type workflow-step --since 15m --environment production --json', + 'lua drains list --json', + 'lua drains status drn_9f31a7c04b2e615d8a03cc71 --json', + 'lua drains deliveries drn_9f31a7c04b2e615d8a03cc71 --limit 20 --kind batch --json', + 'lua drains test drn_9f31a7c04b2e615d8a03cc71 --json', 'lua workflows run outreach --input @in.json --agents fake --fast-retries --json', 'lua workflows watch run_1 --wait-for-human --timeout 900', // lua-cli 3.36.0: the org policy READ verbs (`policy models|autonomy get`) are read-only. @@ -222,6 +229,17 @@ describe('permission layer (lib/permissions-template.json) under Claude Code sem 'lua marketplace skill publish --skill-name x', 'lua marketplace skill transfer --skill-name x --to-org o', 'lua env production -k KEY -v value', + // lua-cli 3.38.0: every `lua drains` verb that changes the ORGANIZATION's drain + // configuration. They are not production verbs (the tokenizer classifies them as + // null, asserted below) — the ask prompt IS /lua-drains's single confirmation. + 'lua drains create --ci --json --name prod-siem --type http --endpoint https://logs.example.com/lua', + 'lua drains update drn_9f31a7c04b2e615d8a03cc71 --ci --json --min-severity warn', + 'lua drains delete drn_9f31a7c04b2e615d8a03cc71 --ci --json --yes', + 'lua drains verify drn_9f31a7c04b2e615d8a03cc71 --json', + 'lua drains pause drn_9f31a7c04b2e615d8a03cc71 --reason noisy --json', + 'lua drains resume drn_9f31a7c04b2e615d8a03cc71 --json', + 'lua drains rotate-secret drn_9f31a7c04b2e615d8a03cc71 --json', + 'lua drains rotate-secret drn_9f31a7c04b2e615d8a03cc71 --finalize --json', ]) { expect({ cmd, asked: matchesRestrictive(ask, cmd), denied: matchesRestrictive(deny, cmd), allowed: matchesAllow(cmd) }) .toEqual({ cmd, asked: true, denied: false, allowed: false }); From 80d2374368e027a5082f8ac0d39b1ebdf15773cb Mon Sep 17 00:00:00 2001 From: lua-stefan-kruger Date: Tue, 22 Sep 2026 10:41:50 +0100 Subject: [PATCH 2/2] fix(plugin): post-deploy smoke reads --since, not a page [PRO-1896] Review follow-up on the same ticket. - hooks/post-deploy-smoke.mjs was the other half of the ticket's Context sentence ("the plugin's smoke checks and the lua-debug agent poll logs by page and reconstruct time windows"). It pulled a 20-row page and filtered it against THIS machine's clock; it now asks the route for the window with `--since 1m`, which the server resolves, and trusts it. A lua-cli older than 3.38.0 exits 1 on the unknown option, so one fallback to the pre-3.38.0 shape keeps the check working -- the pin only warns, so those sessions are still in the field. `--environment production` is deliberately not passed: the step-1 ping goes through `lua chat`, whose rows the A1 call sites may stamp `sandbox`, and a smoke check that hid its own ping's errors would be worse than a slightly wider scan. Four new tests cover both paths. - /lua-drains no longer suggests the bare `lua drains` spelling: the allow rules are per verb, so a bare invocation matches none of them and would prompt for a read, breaking the single-permission contract. - /lua-drains says out loud that the --include-content flow is the one place two ask prompts can occur, and that the second is a new user-initiated turn. 430 plugin tests pass; hooks stay at 100% coverage. Co-Authored-By: Claude Fable 5.1 --- plugins/lua-agent-builder/CHANGELOG.md | 3 +- .../lua-agent-builder/commands/lua-drains.md | 4 +- .../hooks/post-deploy-smoke.mjs | 43 +++++++++++++++-- .../test/hooks/post-deploy-smoke.test.mjs | 46 +++++++++++++++++-- 4 files changed, 86 insertions(+), 10 deletions(-) diff --git a/plugins/lua-agent-builder/CHANGELOG.md b/plugins/lua-agent-builder/CHANGELOG.md index 8c500ca..5045edc 100644 --- a/plugins/lua-agent-builder/CHANGELOG.md +++ b/plugins/lua-agent-builder/CHANGELOG.md @@ -24,6 +24,7 @@ What a drain is (an **organization** resource — `--org`, else `lua.skill.yaml` - `commands/lua-logs.md`: the full 18-source `--type` list (on 3.37.0 and older the alias table was hand-listed and `trigger`, `model-resolver`, `workflow-step`, `workflow-script`, `workflow` were exit 2, reachable only through the `tail_logs` MCP tool — 3.38.0 derives `logs.type` from `AGENT_LOG_SOURCES`), the read window, and `--follow`'s real semantics: it POLLS (no SSE route), refuses `--page`, advances on the newest timestamp it printed with id de-duplication, and under `--json` emits one `{ logs, nextCursor, pagination }` envelope per poll that produced rows. - `agents/lua-debug.md` gains step **2b** and the **"watch a promote"** recipe — `lua logs --ci --type all --since --follow --json`, selecting `subType` `error`/`warn` client-side — plus a drain-triage entry (`status` → `deliveries` → `test` → `verify`) routing the fix to `/lua-drains`. +- `hooks/post-deploy-smoke.mjs` — the other half of the ticket's Context sentence. It pulled a 20-row page and filtered it against **this machine's** clock; it now asks the route for the window with `--since 1m`, which the SERVER resolves, and trusts it. A lua-cli older than 3.38.0 exits 1 on the unknown option, so one fallback to the pre-3.38.0 shape (page + local clock) keeps the check working instead of silently reporting nothing — the pin only warns, so those sessions are still in the field. `--environment production` is deliberately **not** passed: the step-1 ping goes through `lua chat`, whose rows the A1 call sites may stamp `sandbox`, and a smoke check that hid its own ping's errors would be worse than a slightly wider scan. Four new tests cover both paths. - `agents/lua-qa.md` records `T0` before the first turn and scans with `--since --environment ` instead of pulling 100 rows and filtering by timestamp; `--follow` is called out as wrong for an unattended suite. - ⚠ **`lua logs` has no severity flag.** `--min-severity` is a log-*drain* selector; severity on a log read is filtered client-side on `subType`. `scripts/lint-cli-flags.mjs` now denies `lua logs --min-severity` so the confusion cannot ship. - ⚠ **A log entry now HAS an environment field.** The plugin said "there is no `environment` field" in `lua-logs.md`, `lua-qa.md` and `cli-reference.md`; `AgentLogMetadata` gained `orgId`, `environment`, `agentVersion`, `executionId`, `executionSeq`, `traceparent`, `truncated` / `droppedLines`. It is optional and additive, and a row without it reads as **`production`** — on the drain matcher and under `--environment` alike — so its absence never means sandbox. `metadata.channel === 'dev'` is still CLI traffic in *either* environment and is still not an environment marker. @@ -36,7 +37,7 @@ What a drain is (an **organization** resource — `--org`, else `lua.skill.yaml` ### Unchanged on purpose -- No hook behaviour changes beyond the pin: `hooks/hooks.json`, `confirm-deploy`, `post-deploy-smoke` and `lib/tokenizer.mjs` are untouched, because a log drain is organization configuration and not a production verb. +- No gate behaviour changes: `hooks/hooks.json`, `confirm-deploy` and `lib/tokenizer.mjs` are untouched, because a log drain is organization configuration and not a production verb. `post-deploy-smoke` changed only in HOW it reads the window — same trigger set (`SMOKE_LABELS`), same warning, same non-blocking behaviour. - The `tail_logs` MCP tool is unchanged and has no window parameter; it stays documented as the fallback when the installed CLI is older than 3.38.0. ## 1.4.0 — 2026-09-20 diff --git a/plugins/lua-agent-builder/commands/lua-drains.md b/plugins/lua-agent-builder/commands/lua-drains.md index d2bf19f..b25da73 100644 --- a/plugins/lua-agent-builder/commands/lua-drains.md +++ b/plugins/lua-agent-builder/commands/lua-drains.md @@ -25,7 +25,7 @@ Parse `$ARGUMENTS`. Whatever is still missing for the chosen verb, AskUserQuesti Read verbs: -- list → `Bash(lua drains list --json)` (bare `lua drains` is the same command) +- list → `Bash(lua drains list --json)` — always spell the verb: the permission template allows `lua drains list|status|deliveries|test` per verb, and a bare `lua drains` (which the CLI also treats as `list`) matches no rule and would prompt - status → `Bash(lua drains status [] --json)` - deliveries → `Bash(lua drains deliveries --limit [--kind batch|test|verify|heartbeat|dropped|truncated] --json)` - test → `Bash(lua drains test --json)` — sends one `lua.drain.test` record down the real delivery path and waits up to 30 s @@ -45,7 +45,7 @@ Add `--org ` when the user named an organization, or when the CLI exits 2 sa - **Never put a header value on a command line.** `--header ` prompts for the value hidden and therefore cannot work under `--ci`; the only CI-safe form is `--header-from-env =`, which reads it from an environment variable the user exported themselves. If the variable is not set, the CLI exits 2 naming the **variable** — relay that, and ask the user to export it in their own shell. Do not read the variable, echo it, or write it anywhere. - **The signing secret is minted by the server and printed exactly once** (at `create`, and at a `rotate-secret` without `--finalize`). It is never an input. Do not copy it into a file, an env var, a commit message, a summary or your own reply — tell the user it is on their screen and that a lost secret is rotated, never recovered. - **For a vendor preset, stop at the secret.** `datadog` wants `--header DD-API-KEY`, `betterstack` wants `--header Authorization` entered as `Bearer `. Ask which environment variable holds it and use `--header-from-env`; if there is none, print the exact `lua drains create …` line for the user to run in their own terminal (where the hidden prompt works) and stop there. -- `--include-content` turns on `user_message` / `agent_response`, which carry what end users said. Only reach for it when the user asked for it in those words, and **never add it on your own initiative**. Run it **without** `--yes` first: under `--ci` the CLI prints the warning and the acknowledgement and then exits non-zero having created nothing, which is exactly the shape this needs — quote both paragraphs verbatim in your reply, say that re-running with `--yes` *is* the acknowledgement, and stop. The user's approval of that second `Bash` call (the `ask` tier) is the acknowledgement; no AskUserQuestion on top. Scrubbing is not a substitute for not logging secrets: it only catches shapes it recognises. +- `--include-content` turns on `user_message` / `agent_response`, which carry what end users said. Only reach for it when the user asked for it in those words, and **never add it on your own initiative**. Run it **without** `--yes` first: under `--ci` the CLI prints the warning and the acknowledgement and then exits non-zero having created nothing, which is exactly the shape this needs — quote both paragraphs verbatim in your reply, say that re-running with `--yes` *is* the acknowledgement, and stop. The user's approval of that second `Bash` call (the `ask` tier) is the acknowledgement; no AskUserQuestion on top. This is the one flow in the slash that reaches two `ask` prompts, and it stays inside §3.7 because the second call happens only in a **new turn the user started** by telling you to go ahead — never chain the two yourself. Scrubbing is not a substitute for not logging secrets: it only catches shapes it recognises. ## Step 3 — present diff --git a/plugins/lua-agent-builder/hooks/post-deploy-smoke.mjs b/plugins/lua-agent-builder/hooks/post-deploy-smoke.mjs index 09a41a3..8e29219 100644 --- a/plugins/lua-agent-builder/hooks/post-deploy-smoke.mjs +++ b/plugins/lua-agent-builder/hooks/post-deploy-smoke.mjs @@ -5,7 +5,9 @@ // `* deploy|publish` spellings, `lua persona production deploy`, // `lua workflows deploy`, `lua version promote`, `lua mcp activate` — the // SMOKE_LABELS set in lib/tokenizer.mjs, so the list cannot drift from the -// gate): ping the agent and scan recent logs for fresh errors. Surfaces +// gate): ping the agent and scan the last minute of logs for fresh errors, +// asking the route for that window with `--since` rather than reconstructing +// it from a page and this machine's clock (PRO-1896). Surfaces // problems as a warn message (non-blocking — the change already happened). // Registered for every Bash call with no `if` glob; non-matching commands // return null at once. @@ -13,6 +15,10 @@ import { runHook, checkNodeVersion, isMainScript } from '../lib/hook-runtime.mjs'; import { classifyProductionCommand, SMOKE_LABELS } from '../lib/tokenizer.mjs'; +/** How far back the post-deploy log scan looks, in both spellings. */ +const SMOKE_WINDOW = '1m'; +const SMOKE_WINDOW_MS = 60_000; + /** * @param {{tool_input?: {command?: string}, tool_response?: {success?: boolean}}|null} input * @param {{spawnLuaFn?: Function}} [opts] — injectable for tests @@ -54,7 +60,32 @@ export async function decide( // document `{ logs: LogEntry[], pagination: {...} }`; each entry uses // `entry.subType` ('error' | 'warn' | 'info' | 'debug' | 'start' | 'complete') // — there is no `entry.level` field (src/interfaces/logs.ts). - const logs = await spawnLuaFn(['logs', '--ci', '--type', 'all', '--limit', '20', '--json'], { timeoutMs: 10_000 }); + // + // PRO-1896 / PRO-1838: ask the ROUTE for the window instead of pulling a + // page and filtering it against this machine's clock. `--since 1m` is a + // relative bound, so the SERVER resolves it (src/commands/logs.ts + // `parseWindowBound` passes a relative value through verbatim) and a skewed + // laptop can no longer widen the window or miss the deploy entirely. + // + // `--environment production` is deliberately NOT passed: a deploy makes + // something live in production, but the step-1 ping goes through `lua chat`, + // whose rows the A1 call sites may stamp `sandbox`, and a smoke check that + // silently hid its own ping's errors would be worse than a slightly wider + // scan. + // + // The version pin only WARNS, so a session on lua-cli < 3.38.0 is still in + // the field; there `--since` is an unknown option and commander exits 1. + // One fallback to the pre-3.38.0 shape (a page plus a local-clock filter) + // keeps the check working instead of silently reporting nothing. + let windowedByServer = true; + let logs = await spawnLuaFn( + ['logs', '--ci', '--type', 'all', '--since', SMOKE_WINDOW, '--limit', '20', '--json'], + { timeoutMs: 10_000 } + ); + if (logs.exitCode !== 0) { + windowedByServer = false; + logs = await spawnLuaFn(['logs', '--ci', '--type', 'all', '--limit', '20', '--json'], { timeoutMs: 10_000 }); + } if (logs.exitCode !== 0) return null; let entries = []; @@ -65,11 +96,15 @@ export async function decide( return null; } - const sixtySecondsAgo = Date.now() - 60_000; + const sixtySecondsAgo = Date.now() - SMOKE_WINDOW_MS; const errorEntries = entries.filter((entry) => { if (!entry || typeof entry !== 'object') return false; + if (entry.subType !== 'error') return false; + // The route already bounded the window on its own clock; re-applying a + // local one would re-introduce exactly the skew `--since` removes. + if (windowedByServer) return true; const ts = entry.timestamp ? new Date(entry.timestamp).getTime() : 0; - return entry.subType === 'error' && ts >= sixtySecondsAgo; + return ts >= sixtySecondsAgo; }); if (errorEntries.length > 0) { diff --git a/plugins/lua-agent-builder/test/hooks/post-deploy-smoke.test.mjs b/plugins/lua-agent-builder/test/hooks/post-deploy-smoke.test.mjs index c1cea2b..c4bb8e3 100644 --- a/plugins/lua-agent-builder/test/hooks/post-deploy-smoke.test.mjs +++ b/plugins/lua-agent-builder/test/hooks/post-deploy-smoke.test.mjs @@ -132,11 +132,16 @@ describe('post-deploy-smoke decide()', () => { expect(result?.warn).toContain('3 error log entry'); }); - test('ignores stale errors (older than 60s)', async () => { + // PRO-1896: the window comes from the ROUTE (`--since 1m`). On a lua-cli + // older than 3.38.0 that option does not exist and commander exits 1, so the + // hook falls back to the pre-3.38.0 shape — a page plus this machine's clock + // — rather than reporting nothing. These two tests drive that fallback. + test('falls back to the local-clock filter below 3.38.0 and ignores stale errors', async () => { const old = new Date(Date.now() - 120_000).toISOString(); const stdout = logsResponse([{ subType: 'error', timestamp: old, metadata: {} }]); const spawnLuaFn = fakeSpawn([ { exitCode: 0, stdout: 'pong', stderr: '', timedOut: false }, + { exitCode: 1, stdout: '', stderr: "error: unknown option '--since'", timedOut: false }, { exitCode: 0, stdout, stderr: '', timedOut: false }, ]); const result = await decide( @@ -158,10 +163,11 @@ describe('post-deploy-smoke decide()', () => { expect(result).toBeNull(); }); - test('returns null when logs command itself fails', async () => { + test('returns null when the logs command fails both windowed and unwindowed', async () => { const spawnLuaFn = fakeSpawn([ { exitCode: 0, stdout: 'pong', stderr: '', timedOut: false }, { exitCode: 1, stdout: '', stderr: 'logs error', timedOut: false }, + { exitCode: 1, stdout: '', stderr: 'logs error', timedOut: false }, ]); const result = await decide( { tool_input: { command: 'LUA_DEPLOY_CONFIRMED=1 lua deploy skill' } }, @@ -264,10 +270,11 @@ describe('post-deploy-smoke decide()', () => { expect(threadId).toMatch(/^lua-plugin-smoke-\d+$/); }); - test('treats log entries without timestamp as stale (counts 0)', async () => { + test('below 3.38.0, treats log entries without timestamp as stale (counts 0)', async () => { const stdout = logsResponse([{ subType: 'error', message: 'no ts' }]); const spawnLuaFn = fakeSpawn([ { exitCode: 0, stdout: 'pong', stderr: '', timedOut: false }, + { exitCode: 1, stdout: '', stderr: "error: unknown option '--since'", timedOut: false }, { exitCode: 0, stdout, stderr: '', timedOut: false }, ]); const result = await decide( @@ -276,4 +283,37 @@ describe('post-deploy-smoke decide()', () => { ); expect(result).toBeNull(); }); + + // PRO-1896 / PRO-1838 (A5): the scan asks the ROUTE for its window. A + // relative bound is resolved by the SERVER's clock, which is the whole point + // — so a row the LOCAL clock would call stale must still be counted when the + // route returned it. `--environment` is deliberately absent: the step-1 ping + // goes through `lua chat`, whose rows may be stamped `sandbox`. + test('asks the route for the window with --since, and not for an environment', async () => { + const spawnLuaFn = fakeSpawn([ + { exitCode: 0, stdout: 'pong', stderr: '', timedOut: false }, + { exitCode: 0, stdout: logsResponse([]), stderr: '', timedOut: false }, + ]); + await decide({ tool_input: { command: 'LUA_DEPLOY_CONFIRMED=1 lua deploy skill' } }, { spawnLuaFn }); + expect(spawnLuaFn.calls).toHaveLength(2); + const logsArgs = spawnLuaFn.calls[1][0]; + expect(logsArgs).toEqual(['logs', '--ci', '--type', 'all', '--since', '1m', '--limit', '20', '--json']); + expect(logsArgs).not.toContain('--environment'); + expect(logsArgs).not.toContain('--page'); + }); + + test('trusts the server window: a row the local clock calls stale still counts', async () => { + const old = new Date(Date.now() - 120_000).toISOString(); + const stdout = logsResponse([{ subType: 'error', timestamp: old, metadata: {} }]); + const spawnLuaFn = fakeSpawn([ + { exitCode: 0, stdout: 'pong', stderr: '', timedOut: false }, + { exitCode: 0, stdout, stderr: '', timedOut: false }, + ]); + const result = await decide( + { tool_input: { command: 'LUA_DEPLOY_CONFIRMED=1 lua deploy skill' } }, + { spawnLuaFn } + ); + expect(result?.warn).toContain('1 error log entry'); + expect(spawnLuaFn.calls).toHaveLength(2); + }); });