@dbx-tools/appkit-mastra 0.6.8 → 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/index.ts +3 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -1
- package/lib/src/config.d.ts +22 -0
- package/lib/src/config.js +5 -1
- package/lib/src/databricks-aitools.d.ts +83 -0
- package/lib/src/databricks-aitools.js +150 -0
- package/lib/src/plugin.js +14 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/config.ts +28 -0
- package/src/databricks-aitools.ts +216 -0
- package/src/plugin.ts +14 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup provisioning of Databricks AI Tools skills for a Mastra workspace.
|
|
3
|
+
*
|
|
4
|
+
* Databricks AI Tools (`databricks aitools`) are Databricks-owned Agent-Skill
|
|
5
|
+
* (`SKILL.md`) trees installed and kept up to date through the Databricks CLI.
|
|
6
|
+
* The CLI installs them globally under `~/.databricks/aitools/skills/<name>/`
|
|
7
|
+
* (with a `.state.json` manifest) and can also write a resolved, agent-agnostic
|
|
8
|
+
* copy to any directory with `databricks aitools install --path <dir>
|
|
9
|
+
* --skills-only`.
|
|
10
|
+
*
|
|
11
|
+
* {@link provisionDatabricksAITools} folds those skills into a Mastra workspace
|
|
12
|
+
* as extra LOCAL skill scan paths so an agent can use them without anyone
|
|
13
|
+
* hand-copying `SKILL.md` files. It never reimplements the CLI's sourcing: it
|
|
14
|
+
* either points Mastra at the already-installed global tree, or shells out to
|
|
15
|
+
* the CLI to materialize a curated subset into a temp dir.
|
|
16
|
+
*
|
|
17
|
+
* The option is `false | true | "auto" | DatabricksAIToolsOptions`:
|
|
18
|
+
*
|
|
19
|
+
* - `false` (default) - off.
|
|
20
|
+
* - `"auto"` - enable only when the global tree already exists OR the
|
|
21
|
+
* `databricks` CLI is resolvable; otherwise silently no-op. The "on if the
|
|
22
|
+
* CLI is around" default.
|
|
23
|
+
* - `true` - required: fail startup (subject to `failOnError`) when neither the
|
|
24
|
+
* installed tree nor the CLI can supply skills.
|
|
25
|
+
* - an options bag for a specific `skills` subset, `experimental` skills, a
|
|
26
|
+
* `refresh` that re-runs the CLI even when a tree exists, or an explicit
|
|
27
|
+
* `path`.
|
|
28
|
+
*
|
|
29
|
+
* @module
|
|
30
|
+
*/
|
|
31
|
+
import { mkdtemp } from "node:fs/promises";
|
|
32
|
+
import { existsSync } from "node:fs";
|
|
33
|
+
import { homedir, tmpdir } from "node:os";
|
|
34
|
+
import { join } from "node:path";
|
|
35
|
+
import { spawn } from "@dbx-tools/core";
|
|
36
|
+
import { error, log, string } from "@dbx-tools/shared-core";
|
|
37
|
+
const logger = log.logger("mastra/databricks-aitools");
|
|
38
|
+
/** Global skills tree the `databricks aitools` CLI installs into. */
|
|
39
|
+
const GLOBAL_AITOOLS_SKILLS_PATH = join(homedir(), ".databricks", "aitools", "skills");
|
|
40
|
+
/** The Databricks CLI binary name; resolved on `PATH`. */
|
|
41
|
+
const DATABRICKS_CLI = "databricks";
|
|
42
|
+
/* ------------------------------- helpers ------------------------------- */
|
|
43
|
+
/** Normalize the `databricksAITools` option into a flat options bag, or `undefined` when off. */
|
|
44
|
+
export function normalizeDatabricksAIToolsOption(option) {
|
|
45
|
+
if (option === undefined || option === false)
|
|
46
|
+
return undefined;
|
|
47
|
+
if (option === true)
|
|
48
|
+
return { mode: "require" };
|
|
49
|
+
if (option === "auto")
|
|
50
|
+
return { mode: "auto" };
|
|
51
|
+
return { mode: option.mode ?? "auto", ...option };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Materialize Databricks AI Tools skills at app startup and return them as
|
|
55
|
+
* extra local skill scan paths.
|
|
56
|
+
*
|
|
57
|
+
* Resolution: reuse the installed global tree when present (no CLI call) unless
|
|
58
|
+
* `refresh`/`skills`/`path` ask for a fresh CLI materialization; otherwise run
|
|
59
|
+
* `databricks aitools install --path <dir> --skills-only` when the CLI resolves.
|
|
60
|
+
* A `"require"` provision that yields nothing fails startup unless
|
|
61
|
+
* `failOnError: false`.
|
|
62
|
+
*/
|
|
63
|
+
export async function provisionDatabricksAITools(option) {
|
|
64
|
+
const options = normalizeDatabricksAIToolsOption(option);
|
|
65
|
+
const empty = { localSkillPaths: [] };
|
|
66
|
+
if (!options)
|
|
67
|
+
return empty;
|
|
68
|
+
const mode = options.mode ?? "auto";
|
|
69
|
+
const failOnError = options.failOnError ?? mode === "require";
|
|
70
|
+
const wantsCliFetch = options.refresh === true ||
|
|
71
|
+
options.path !== undefined ||
|
|
72
|
+
string.parseList(options.skills).length > 0 ||
|
|
73
|
+
options.experimental === true;
|
|
74
|
+
try {
|
|
75
|
+
// Fast path: reuse the already-installed global tree, no CLI call.
|
|
76
|
+
if (!wantsCliFetch && installedTreeExists()) {
|
|
77
|
+
logger.debug("using installed aitools tree", { path: GLOBAL_AITOOLS_SKILLS_PATH });
|
|
78
|
+
return { localSkillPaths: [GLOBAL_AITOOLS_SKILLS_PATH], source: "installed" };
|
|
79
|
+
}
|
|
80
|
+
const cliPath = await resolveCli(options.cli);
|
|
81
|
+
if (cliPath) {
|
|
82
|
+
const dir = await materializeViaCli(cliPath, options);
|
|
83
|
+
if (dir)
|
|
84
|
+
return { localSkillPaths: [dir], source: "cli" };
|
|
85
|
+
}
|
|
86
|
+
// Nothing fetched. Fall back to the installed tree if it's there.
|
|
87
|
+
if (installedTreeExists()) {
|
|
88
|
+
return { localSkillPaths: [GLOBAL_AITOOLS_SKILLS_PATH], source: "installed" };
|
|
89
|
+
}
|
|
90
|
+
if (failOnError) {
|
|
91
|
+
throw new Error(`Databricks AI Tools requested but no installed skills tree at "${GLOBAL_AITOOLS_SKILLS_PATH}" and the "${DATABRICKS_CLI}" CLI is not resolvable. Install with \`databricks aitools install\`, or set databricksAITools: "auto".`);
|
|
92
|
+
}
|
|
93
|
+
logger.debug("databricks aitools unavailable; skipping");
|
|
94
|
+
return empty;
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (failOnError) {
|
|
98
|
+
throw new Error(`failed to provision Databricks AI Tools skills: ${error.errorMessage(err)}`, { cause: error.toError(err) });
|
|
99
|
+
}
|
|
100
|
+
logger.warn("skipped", { error: error.errorMessage(err) });
|
|
101
|
+
return empty;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Whether the CLI's global skills tree exists and holds at least one skill. */
|
|
105
|
+
function installedTreeExists() {
|
|
106
|
+
return existsSync(GLOBAL_AITOOLS_SKILLS_PATH);
|
|
107
|
+
}
|
|
108
|
+
/** Resolve the `databricks` CLI, returning its invocation path or `undefined`. */
|
|
109
|
+
async function resolveCli(cli) {
|
|
110
|
+
const command = string.trimToNull(cli) ?? DATABRICKS_CLI;
|
|
111
|
+
try {
|
|
112
|
+
const result = await spawn(command, ["aitools", "version"], {
|
|
113
|
+
stdout: "capture",
|
|
114
|
+
stderr: "capture",
|
|
115
|
+
check: false,
|
|
116
|
+
});
|
|
117
|
+
if (result.exitCode === 0)
|
|
118
|
+
return command;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// CLI absent or `aitools` unsupported - caller falls back / fails.
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Run `databricks aitools install --path <dir> --skills-only` to materialize a
|
|
127
|
+
* resolved, agent-agnostic skill set into a directory (no agents, no state).
|
|
128
|
+
*/
|
|
129
|
+
async function materializeViaCli(cli, options) {
|
|
130
|
+
const dir = string.trimToNull(options.path) ?? (await mkdtemp(join(tmpdir(), "databricks-aitools-")));
|
|
131
|
+
const args = ["aitools", "install", "--path", dir, "--skills-only"];
|
|
132
|
+
const skills = string.parseList(options.skills);
|
|
133
|
+
if (skills.length > 0)
|
|
134
|
+
args.push("--skills", skills.join(","));
|
|
135
|
+
if (options.experimental)
|
|
136
|
+
args.push("--experimental");
|
|
137
|
+
const result = await spawn(cli, args, {
|
|
138
|
+
stdout: "capture",
|
|
139
|
+
stderr: "capture",
|
|
140
|
+
check: false,
|
|
141
|
+
});
|
|
142
|
+
if (result.exitCode !== 0) {
|
|
143
|
+
throw new Error(`databricks aitools install failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);
|
|
144
|
+
}
|
|
145
|
+
if (!existsSync(dir))
|
|
146
|
+
return undefined;
|
|
147
|
+
logger.debug("materialized aitools skills via CLI", { path: dir, skills });
|
|
148
|
+
return dir;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YWJyaWNrcy1haXRvb2xzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RhdGFicmlja3MtYWl0b29scy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E2Qkc7QUFFSCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDM0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNyQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUMxQyxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ2pDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN4QyxPQUFPLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUU1RCxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLDJCQUEyQixDQUFDLENBQUM7QUFFdkQscUVBQXFFO0FBQ3JFLE1BQU0sMEJBQTBCLEdBQUcsSUFBSSxDQUFDLE9BQU8sRUFBRSxFQUFFLGFBQWEsRUFBRSxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFFdkYsMERBQTBEO0FBQzFELE1BQU0sY0FBYyxHQUFHLFlBQVksQ0FBQztBQWdEcEMsNkVBQTZFO0FBRTdFLGlHQUFpRztBQUNqRyxNQUFNLFVBQVUsZ0NBQWdDLENBQzlDLE1BQTJDO0lBRTNDLElBQUksTUFBTSxLQUFLLFNBQVMsSUFBSSxNQUFNLEtBQUssS0FBSztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQy9ELElBQUksTUFBTSxLQUFLLElBQUk7UUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO0lBQ2hELElBQUksTUFBTSxLQUFLLE1BQU07UUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBQy9DLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksSUFBSSxNQUFNLEVBQUUsR0FBRyxNQUFNLEVBQUUsQ0FBQztBQUNwRCxDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSwwQkFBMEIsQ0FDOUMsTUFBMkM7SUFFM0MsTUFBTSxPQUFPLEdBQUcsZ0NBQWdDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDekQsTUFBTSxLQUFLLEdBQWlDLEVBQUUsZUFBZSxFQUFFLEVBQUUsRUFBRSxDQUFDO0lBQ3BFLElBQUksQ0FBQyxPQUFPO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFFM0IsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksSUFBSSxNQUFNLENBQUM7SUFDcEMsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLFdBQVcsSUFBSSxJQUFJLEtBQUssU0FBUyxDQUFDO0lBQzlELE1BQU0sYUFBYSxHQUNqQixPQUFPLENBQUMsT0FBTyxLQUFLLElBQUk7UUFDeEIsT0FBTyxDQUFDLElBQUksS0FBSyxTQUFTO1FBQzFCLE1BQU0sQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDO1FBQzNDLE9BQU8sQ0FBQyxZQUFZLEtBQUssSUFBSSxDQUFDO0lBRWhDLElBQUksQ0FBQztRQUNILG1FQUFtRTtRQUNuRSxJQUFJLENBQUMsYUFBYSxJQUFJLG1CQUFtQixFQUFFLEVBQUUsQ0FBQztZQUM1QyxNQUFNLENBQUMsS0FBSyxDQUFDLDhCQUE4QixFQUFFLEVBQUUsSUFBSSxFQUFFLDBCQUEwQixFQUFFLENBQUMsQ0FBQztZQUNuRixPQUFPLEVBQUUsZUFBZSxFQUFFLENBQUMsMEJBQTBCLENBQUMsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLENBQUM7UUFDaEYsQ0FBQztRQUVELE1BQU0sT0FBTyxHQUFHLE1BQU0sVUFBVSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM5QyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osTUFBTSxHQUFHLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDdEQsSUFBSSxHQUFHO2dCQUFFLE9BQU8sRUFBRSxlQUFlLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDNUQsQ0FBQztRQUVELGtFQUFrRTtRQUNsRSxJQUFJLG1CQUFtQixFQUFFLEVBQUUsQ0FBQztZQUMxQixPQUFPLEVBQUUsZUFBZSxFQUFFLENBQUMsMEJBQTBCLENBQUMsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLENBQUM7UUFDaEYsQ0FBQztRQUVELElBQUksV0FBVyxFQUFFLENBQUM7WUFDaEIsTUFBTSxJQUFJLEtBQUssQ0FDYixrRUFBa0UsMEJBQTBCLGNBQWMsY0FBYyx5R0FBeUcsQ0FDbE8sQ0FBQztRQUNKLENBQUM7UUFDRCxNQUFNLENBQUMsS0FBSyxDQUFDLDBDQUEwQyxDQUFDLENBQUM7UUFDekQsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLElBQUksV0FBVyxFQUFFLENBQUM7WUFDaEIsTUFBTSxJQUFJLEtBQUssQ0FDYixtREFBbUQsS0FBSyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUM1RSxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQzlCLENBQUM7UUFDSixDQUFDO1FBQ0QsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDM0QsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0FBQ0gsQ0FBQztBQUVELGdGQUFnRjtBQUNoRixTQUFTLG1CQUFtQjtJQUMxQixPQUFPLFVBQVUsQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO0FBQ2hELENBQUM7QUFFRCxrRkFBa0Y7QUFDbEYsS0FBSyxVQUFVLFVBQVUsQ0FBQyxHQUF1QjtJQUMvQyxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxJQUFJLGNBQWMsQ0FBQztJQUN6RCxJQUFJLENBQUM7UUFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLEVBQUU7WUFDMUQsTUFBTSxFQUFFLFNBQVM7WUFDakIsTUFBTSxFQUFFLFNBQVM7WUFDakIsS0FBSyxFQUFFLEtBQUs7U0FDYixDQUFDLENBQUM7UUFDSCxJQUFJLE1BQU0sQ0FBQyxRQUFRLEtBQUssQ0FBQztZQUFFLE9BQU8sT0FBTyxDQUFDO0lBQzVDLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxtRUFBbUU7SUFDckUsQ0FBQztJQUNELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxLQUFLLFVBQVUsaUJBQWlCLENBQzlCLEdBQVcsRUFDWCxPQUFpQztJQUVqQyxNQUFNLEdBQUcsR0FDUCxNQUFNLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM1RixNQUFNLElBQUksR0FBRyxDQUFDLFNBQVMsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSxlQUFlLENBQUMsQ0FBQztJQUNwRSxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNoRCxJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQztRQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUMvRCxJQUFJLE9BQU8sQ0FBQyxZQUFZO1FBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBRXRELE1BQU0sTUFBTSxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUU7UUFDcEMsTUFBTSxFQUFFLFNBQVM7UUFDakIsTUFBTSxFQUFFLFNBQVM7UUFDakIsS0FBSyxFQUFFLEtBQUs7S0FDYixDQUFDLENBQUM7SUFDSCxJQUFJLE1BQU0sQ0FBQyxRQUFRLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDMUIsTUFBTSxJQUFJLEtBQUssQ0FDYiwyQ0FBMkMsTUFBTSxDQUFDLFFBQVEsTUFBTSxNQUFNLENBQUMsTUFBTSxJQUFJLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FDakcsQ0FBQztJQUNKLENBQUM7SUFDRCxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ3ZDLE1BQU0sQ0FBQyxLQUFLLENBQUMscUNBQXFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDM0UsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBTdGFydHVwIHByb3Zpc2lvbmluZyBvZiBEYXRhYnJpY2tzIEFJIFRvb2xzIHNraWxscyBmb3IgYSBNYXN0cmEgd29ya3NwYWNlLlxuICpcbiAqIERhdGFicmlja3MgQUkgVG9vbHMgKGBkYXRhYnJpY2tzIGFpdG9vbHNgKSBhcmUgRGF0YWJyaWNrcy1vd25lZCBBZ2VudC1Ta2lsbFxuICogKGBTS0lMTC5tZGApIHRyZWVzIGluc3RhbGxlZCBhbmQga2VwdCB1cCB0byBkYXRlIHRocm91Z2ggdGhlIERhdGFicmlja3MgQ0xJLlxuICogVGhlIENMSSBpbnN0YWxscyB0aGVtIGdsb2JhbGx5IHVuZGVyIGB+Ly5kYXRhYnJpY2tzL2FpdG9vbHMvc2tpbGxzLzxuYW1lPi9gXG4gKiAod2l0aCBhIGAuc3RhdGUuanNvbmAgbWFuaWZlc3QpIGFuZCBjYW4gYWxzbyB3cml0ZSBhIHJlc29sdmVkLCBhZ2VudC1hZ25vc3RpY1xuICogY29weSB0byBhbnkgZGlyZWN0b3J5IHdpdGggYGRhdGFicmlja3MgYWl0b29scyBpbnN0YWxsIC0tcGF0aCA8ZGlyPlxuICogLS1za2lsbHMtb25seWAuXG4gKlxuICoge0BsaW5rIHByb3Zpc2lvbkRhdGFicmlja3NBSVRvb2xzfSBmb2xkcyB0aG9zZSBza2lsbHMgaW50byBhIE1hc3RyYSB3b3Jrc3BhY2VcbiAqIGFzIGV4dHJhIExPQ0FMIHNraWxsIHNjYW4gcGF0aHMgc28gYW4gYWdlbnQgY2FuIHVzZSB0aGVtIHdpdGhvdXQgYW55b25lXG4gKiBoYW5kLWNvcHlpbmcgYFNLSUxMLm1kYCBmaWxlcy4gSXQgbmV2ZXIgcmVpbXBsZW1lbnRzIHRoZSBDTEkncyBzb3VyY2luZzogaXRcbiAqIGVpdGhlciBwb2ludHMgTWFzdHJhIGF0IHRoZSBhbHJlYWR5LWluc3RhbGxlZCBnbG9iYWwgdHJlZSwgb3Igc2hlbGxzIG91dCB0b1xuICogdGhlIENMSSB0byBtYXRlcmlhbGl6ZSBhIGN1cmF0ZWQgc3Vic2V0IGludG8gYSB0ZW1wIGRpci5cbiAqXG4gKiBUaGUgb3B0aW9uIGlzIGBmYWxzZSB8IHRydWUgfCBcImF1dG9cIiB8IERhdGFicmlja3NBSVRvb2xzT3B0aW9uc2A6XG4gKlxuICogLSBgZmFsc2VgIChkZWZhdWx0KSAtIG9mZi5cbiAqIC0gYFwiYXV0b1wiYCAtIGVuYWJsZSBvbmx5IHdoZW4gdGhlIGdsb2JhbCB0cmVlIGFscmVhZHkgZXhpc3RzIE9SIHRoZVxuICogICBgZGF0YWJyaWNrc2AgQ0xJIGlzIHJlc29sdmFibGU7IG90aGVyd2lzZSBzaWxlbnRseSBuby1vcC4gVGhlIFwib24gaWYgdGhlXG4gKiAgIENMSSBpcyBhcm91bmRcIiBkZWZhdWx0LlxuICogLSBgdHJ1ZWAgLSByZXF1aXJlZDogZmFpbCBzdGFydHVwIChzdWJqZWN0IHRvIGBmYWlsT25FcnJvcmApIHdoZW4gbmVpdGhlciB0aGVcbiAqICAgaW5zdGFsbGVkIHRyZWUgbm9yIHRoZSBDTEkgY2FuIHN1cHBseSBza2lsbHMuXG4gKiAtIGFuIG9wdGlvbnMgYmFnIGZvciBhIHNwZWNpZmljIGBza2lsbHNgIHN1YnNldCwgYGV4cGVyaW1lbnRhbGAgc2tpbGxzLCBhXG4gKiAgIGByZWZyZXNoYCB0aGF0IHJlLXJ1bnMgdGhlIENMSSBldmVuIHdoZW4gYSB0cmVlIGV4aXN0cywgb3IgYW4gZXhwbGljaXRcbiAqICAgYHBhdGhgLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBta2R0ZW1wIH0gZnJvbSBcIm5vZGU6ZnMvcHJvbWlzZXNcIjtcbmltcG9ydCB7IGV4aXN0c1N5bmMgfSBmcm9tIFwibm9kZTpmc1wiO1xuaW1wb3J0IHsgaG9tZWRpciwgdG1wZGlyIH0gZnJvbSBcIm5vZGU6b3NcIjtcbmltcG9ydCB7IGpvaW4gfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBzcGF3biB9IGZyb20gXCJAZGJ4LXRvb2xzL2NvcmVcIjtcbmltcG9ydCB7IGVycm9yLCBsb2csIHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJtYXN0cmEvZGF0YWJyaWNrcy1haXRvb2xzXCIpO1xuXG4vKiogR2xvYmFsIHNraWxscyB0cmVlIHRoZSBgZGF0YWJyaWNrcyBhaXRvb2xzYCBDTEkgaW5zdGFsbHMgaW50by4gKi9cbmNvbnN0IEdMT0JBTF9BSVRPT0xTX1NLSUxMU19QQVRIID0gam9pbihob21lZGlyKCksIFwiLmRhdGFicmlja3NcIiwgXCJhaXRvb2xzXCIsIFwic2tpbGxzXCIpO1xuXG4vKiogVGhlIERhdGFicmlja3MgQ0xJIGJpbmFyeSBuYW1lOyByZXNvbHZlZCBvbiBgUEFUSGAuICovXG5jb25zdCBEQVRBQlJJQ0tTX0NMSSA9IFwiZGF0YWJyaWNrc1wiO1xuXG4vKiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB0eXBlcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXG4vKiogSG93IGFnZ3Jlc3NpdmVseSB0byBlbmFibGUgRGF0YWJyaWNrcyBBSSBUb29scyBza2lsbHMuICovXG5leHBvcnQgdHlwZSBEYXRhYnJpY2tzQUlUb29sc01vZGUgPSBcImF1dG9cIiB8IFwicmVxdWlyZVwiO1xuXG4vKiogT3B0aW9ucyBmb3Ige0BsaW5rIHByb3Zpc2lvbkRhdGFicmlja3NBSVRvb2xzfS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGF0YWJyaWNrc0FJVG9vbHNPcHRpb25zIHtcbiAgLyoqXG4gICAqIGBcImF1dG9cImAgKGRlZmF1bHQpIGVuYWJsZXMgb25seSB3aGVuIHRoZSBpbnN0YWxsZWQgdHJlZSBleGlzdHMgb3IgdGhlIENMSVxuICAgKiBpcyByZXNvbHZhYmxlOyBgXCJyZXF1aXJlXCJgIGZhaWxzIHN0YXJ0dXAgd2hlbiBuZWl0aGVyIGNhbiBzdXBwbHkgc2tpbGxzLlxuICAgKi9cbiAgbW9kZT86IERhdGFicmlja3NBSVRvb2xzTW9kZTtcbiAgLyoqIEluc3RhbGwgb25seSB0aGVzZSBza2lsbCBuYW1lcyAoY29tbWEvc3BhY2UgbGlzdCBvciBhcnJheSkuICovXG4gIHNraWxscz86IHN0cmluZyB8IHN0cmluZ1tdO1xuICAvKiogSW5jbHVkZSBleHBlcmltZW50YWwgc2tpbGxzIHdoZW4gdGhlIENMSSBoYXMgdG8gZmV0Y2guICovXG4gIGV4cGVyaW1lbnRhbD86IGJvb2xlYW47XG4gIC8qKlxuICAgKiBSZS1ydW4gdGhlIENMSSB0byAocmUpbWF0ZXJpYWxpemUgc2tpbGxzIGludG8gYSBmcmVzaCBkaXIgZXZlbiB3aGVuIHRoZVxuICAgKiBnbG9iYWwgdHJlZSBhbHJlYWR5IGV4aXN0cy4gRGVmYXVsdHMgdG8gYGZhbHNlYCAocmV1c2UgdGhlIGluc3RhbGxlZCB0cmVlKS5cbiAgICovXG4gIHJlZnJlc2g/OiBib29sZWFuO1xuICAvKipcbiAgICogRXhwbGljaXQgZGlyZWN0b3J5IHRvIG1hdGVyaWFsaXplIHNraWxscyBpbnRvIHdpdGggdGhlIENMSS4gRGVmYXVsdHMgdG8gYVxuICAgKiB0ZW1wIGRpci4gSWdub3JlZCB3aGVuIHRoZSBpbnN0YWxsZWQgZ2xvYmFsIHRyZWUgaXMgdXNlZCBhcy1pcy5cbiAgICovXG4gIHBhdGg/OiBzdHJpbmc7XG4gIC8qKiBBYnNvbHV0ZSBwYXRoIHRvIHRoZSBgZGF0YWJyaWNrc2AgQ0xJLiBEZWZhdWx0cyB0byBgZGF0YWJyaWNrc2Agb24gUEFUSC4gKi9cbiAgY2xpPzogc3RyaW5nO1xuICAvKipcbiAgICogRmFpbCBhcHAgc3RhcnR1cCB3aGVuIHJlcXVpcmVkIHNraWxscyBjYW4ndCBiZSBwcm92aXNpb25lZC4gRGVmYXVsdHMgdG9cbiAgICogYHRydWVgIGZvciBgbW9kZTogXCJyZXF1aXJlXCJgIGFuZCBgZmFsc2VgIGZvciBgXCJhdXRvXCJgLlxuICAgKi9cbiAgZmFpbE9uRXJyb3I/OiBib29sZWFuO1xufVxuXG4vKiogVGhlIGBkYXRhYnJpY2tzQUlUb29sc2AgY29uZmlnIG9wdGlvbjogYSB0b2dnbGUsIGBcImF1dG9cImAsIG9yIGFuIG9wdGlvbnMgYmFnLiAqL1xuZXhwb3J0IHR5cGUgRGF0YWJyaWNrc0FJVG9vbHNPcHRpb24gPSBib29sZWFuIHwgXCJhdXRvXCIgfCBEYXRhYnJpY2tzQUlUb29sc09wdGlvbnM7XG5cbi8qKiBXaGF0IHtAbGluayBwcm92aXNpb25EYXRhYnJpY2tzQUlUb29sc30gcmVzb2x2ZWQuICovXG5leHBvcnQgaW50ZXJmYWNlIFByb3Zpc2lvbmVkRGF0YWJyaWNrc0FJVG9vbHMge1xuICAvKiogRXh0cmEgTE9DQUwgc2tpbGwgc2NhbiBwYXRocyB0byBoYW5kIE1hc3RyYS4gRW1wdHkgd2hlbiBkaXNhYmxlZC91bnJlc29sdmVkLiAqL1xuICBsb2NhbFNraWxsUGF0aHM6IHN0cmluZ1tdO1xuICAvKiogSG93IHRoZSBza2lsbHMgd2VyZSBzb3VyY2VkLCBmb3IgbG9nZ2luZy4gKi9cbiAgc291cmNlPzogXCJpbnN0YWxsZWRcIiB8IFwiY2xpXCI7XG59XG5cbi8qIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gaGVscGVycyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovXG5cbi8qKiBOb3JtYWxpemUgdGhlIGBkYXRhYnJpY2tzQUlUb29sc2Agb3B0aW9uIGludG8gYSBmbGF0IG9wdGlvbnMgYmFnLCBvciBgdW5kZWZpbmVkYCB3aGVuIG9mZi4gKi9cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVEYXRhYnJpY2tzQUlUb29sc09wdGlvbihcbiAgb3B0aW9uOiBEYXRhYnJpY2tzQUlUb29sc09wdGlvbiB8IHVuZGVmaW5lZCxcbik6IERhdGFicmlja3NBSVRvb2xzT3B0aW9ucyB8IHVuZGVmaW5lZCB7XG4gIGlmIChvcHRpb24gPT09IHVuZGVmaW5lZCB8fCBvcHRpb24gPT09IGZhbHNlKSByZXR1cm4gdW5kZWZpbmVkO1xuICBpZiAob3B0aW9uID09PSB0cnVlKSByZXR1cm4geyBtb2RlOiBcInJlcXVpcmVcIiB9O1xuICBpZiAob3B0aW9uID09PSBcImF1dG9cIikgcmV0dXJuIHsgbW9kZTogXCJhdXRvXCIgfTtcbiAgcmV0dXJuIHsgbW9kZTogb3B0aW9uLm1vZGUgPz8gXCJhdXRvXCIsIC4uLm9wdGlvbiB9O1xufVxuXG4vKipcbiAqIE1hdGVyaWFsaXplIERhdGFicmlja3MgQUkgVG9vbHMgc2tpbGxzIGF0IGFwcCBzdGFydHVwIGFuZCByZXR1cm4gdGhlbSBhc1xuICogZXh0cmEgbG9jYWwgc2tpbGwgc2NhbiBwYXRocy5cbiAqXG4gKiBSZXNvbHV0aW9uOiByZXVzZSB0aGUgaW5zdGFsbGVkIGdsb2JhbCB0cmVlIHdoZW4gcHJlc2VudCAobm8gQ0xJIGNhbGwpIHVubGVzc1xuICogYHJlZnJlc2hgL2Bza2lsbHNgL2BwYXRoYCBhc2sgZm9yIGEgZnJlc2ggQ0xJIG1hdGVyaWFsaXphdGlvbjsgb3RoZXJ3aXNlIHJ1blxuICogYGRhdGFicmlja3MgYWl0b29scyBpbnN0YWxsIC0tcGF0aCA8ZGlyPiAtLXNraWxscy1vbmx5YCB3aGVuIHRoZSBDTEkgcmVzb2x2ZXMuXG4gKiBBIGBcInJlcXVpcmVcImAgcHJvdmlzaW9uIHRoYXQgeWllbGRzIG5vdGhpbmcgZmFpbHMgc3RhcnR1cCB1bmxlc3NcbiAqIGBmYWlsT25FcnJvcjogZmFsc2VgLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHJvdmlzaW9uRGF0YWJyaWNrc0FJVG9vbHMoXG4gIG9wdGlvbjogRGF0YWJyaWNrc0FJVG9vbHNPcHRpb24gfCB1bmRlZmluZWQsXG4pOiBQcm9taXNlPFByb3Zpc2lvbmVkRGF0YWJyaWNrc0FJVG9vbHM+IHtcbiAgY29uc3Qgb3B0aW9ucyA9IG5vcm1hbGl6ZURhdGFicmlja3NBSVRvb2xzT3B0aW9uKG9wdGlvbik7XG4gIGNvbnN0IGVtcHR5OiBQcm92aXNpb25lZERhdGFicmlja3NBSVRvb2xzID0geyBsb2NhbFNraWxsUGF0aHM6IFtdIH07XG4gIGlmICghb3B0aW9ucykgcmV0dXJuIGVtcHR5O1xuXG4gIGNvbnN0IG1vZGUgPSBvcHRpb25zLm1vZGUgPz8gXCJhdXRvXCI7XG4gIGNvbnN0IGZhaWxPbkVycm9yID0gb3B0aW9ucy5mYWlsT25FcnJvciA/PyBtb2RlID09PSBcInJlcXVpcmVcIjtcbiAgY29uc3Qgd2FudHNDbGlGZXRjaCA9XG4gICAgb3B0aW9ucy5yZWZyZXNoID09PSB0cnVlIHx8XG4gICAgb3B0aW9ucy5wYXRoICE9PSB1bmRlZmluZWQgfHxcbiAgICBzdHJpbmcucGFyc2VMaXN0KG9wdGlvbnMuc2tpbGxzKS5sZW5ndGggPiAwIHx8XG4gICAgb3B0aW9ucy5leHBlcmltZW50YWwgPT09IHRydWU7XG5cbiAgdHJ5IHtcbiAgICAvLyBGYXN0IHBhdGg6IHJldXNlIHRoZSBhbHJlYWR5LWluc3RhbGxlZCBnbG9iYWwgdHJlZSwgbm8gQ0xJIGNhbGwuXG4gICAgaWYgKCF3YW50c0NsaUZldGNoICYmIGluc3RhbGxlZFRyZWVFeGlzdHMoKSkge1xuICAgICAgbG9nZ2VyLmRlYnVnKFwidXNpbmcgaW5zdGFsbGVkIGFpdG9vbHMgdHJlZVwiLCB7IHBhdGg6IEdMT0JBTF9BSVRPT0xTX1NLSUxMU19QQVRIIH0pO1xuICAgICAgcmV0dXJuIHsgbG9jYWxTa2lsbFBhdGhzOiBbR0xPQkFMX0FJVE9PTFNfU0tJTExTX1BBVEhdLCBzb3VyY2U6IFwiaW5zdGFsbGVkXCIgfTtcbiAgICB9XG5cbiAgICBjb25zdCBjbGlQYXRoID0gYXdhaXQgcmVzb2x2ZUNsaShvcHRpb25zLmNsaSk7XG4gICAgaWYgKGNsaVBhdGgpIHtcbiAgICAgIGNvbnN0IGRpciA9IGF3YWl0IG1hdGVyaWFsaXplVmlhQ2xpKGNsaVBhdGgsIG9wdGlvbnMpO1xuICAgICAgaWYgKGRpcikgcmV0dXJuIHsgbG9jYWxTa2lsbFBhdGhzOiBbZGlyXSwgc291cmNlOiBcImNsaVwiIH07XG4gICAgfVxuXG4gICAgLy8gTm90aGluZyBmZXRjaGVkLiBGYWxsIGJhY2sgdG8gdGhlIGluc3RhbGxlZCB0cmVlIGlmIGl0J3MgdGhlcmUuXG4gICAgaWYgKGluc3RhbGxlZFRyZWVFeGlzdHMoKSkge1xuICAgICAgcmV0dXJuIHsgbG9jYWxTa2lsbFBhdGhzOiBbR0xPQkFMX0FJVE9PTFNfU0tJTExTX1BBVEhdLCBzb3VyY2U6IFwiaW5zdGFsbGVkXCIgfTtcbiAgICB9XG5cbiAgICBpZiAoZmFpbE9uRXJyb3IpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYERhdGFicmlja3MgQUkgVG9vbHMgcmVxdWVzdGVkIGJ1dCBubyBpbnN0YWxsZWQgc2tpbGxzIHRyZWUgYXQgXCIke0dMT0JBTF9BSVRPT0xTX1NLSUxMU19QQVRIfVwiIGFuZCB0aGUgXCIke0RBVEFCUklDS1NfQ0xJfVwiIENMSSBpcyBub3QgcmVzb2x2YWJsZS4gSW5zdGFsbCB3aXRoIFxcYGRhdGFicmlja3MgYWl0b29scyBpbnN0YWxsXFxgLCBvciBzZXQgZGF0YWJyaWNrc0FJVG9vbHM6IFwiYXV0b1wiLmAsXG4gICAgICApO1xuICAgIH1cbiAgICBsb2dnZXIuZGVidWcoXCJkYXRhYnJpY2tzIGFpdG9vbHMgdW5hdmFpbGFibGU7IHNraXBwaW5nXCIpO1xuICAgIHJldHVybiBlbXB0eTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgaWYgKGZhaWxPbkVycm9yKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBmYWlsZWQgdG8gcHJvdmlzaW9uIERhdGFicmlja3MgQUkgVG9vbHMgc2tpbGxzOiAke2Vycm9yLmVycm9yTWVzc2FnZShlcnIpfWAsXG4gICAgICAgIHsgY2F1c2U6IGVycm9yLnRvRXJyb3IoZXJyKSB9LFxuICAgICAgKTtcbiAgICB9XG4gICAgbG9nZ2VyLndhcm4oXCJza2lwcGVkXCIsIHsgZXJyb3I6IGVycm9yLmVycm9yTWVzc2FnZShlcnIpIH0pO1xuICAgIHJldHVybiBlbXB0eTtcbiAgfVxufVxuXG4vKiogV2hldGhlciB0aGUgQ0xJJ3MgZ2xvYmFsIHNraWxscyB0cmVlIGV4aXN0cyBhbmQgaG9sZHMgYXQgbGVhc3Qgb25lIHNraWxsLiAqL1xuZnVuY3Rpb24gaW5zdGFsbGVkVHJlZUV4aXN0cygpOiBib29sZWFuIHtcbiAgcmV0dXJuIGV4aXN0c1N5bmMoR0xPQkFMX0FJVE9PTFNfU0tJTExTX1BBVEgpO1xufVxuXG4vKiogUmVzb2x2ZSB0aGUgYGRhdGFicmlja3NgIENMSSwgcmV0dXJuaW5nIGl0cyBpbnZvY2F0aW9uIHBhdGggb3IgYHVuZGVmaW5lZGAuICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlQ2xpKGNsaTogc3RyaW5nIHwgdW5kZWZpbmVkKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgY29uc3QgY29tbWFuZCA9IHN0cmluZy50cmltVG9OdWxsKGNsaSkgPz8gREFUQUJSSUNLU19DTEk7XG4gIHRyeSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgc3Bhd24oY29tbWFuZCwgW1wiYWl0b29sc1wiLCBcInZlcnNpb25cIl0sIHtcbiAgICAgIHN0ZG91dDogXCJjYXB0dXJlXCIsXG4gICAgICBzdGRlcnI6IFwiY2FwdHVyZVwiLFxuICAgICAgY2hlY2s6IGZhbHNlLFxuICAgIH0pO1xuICAgIGlmIChyZXN1bHQuZXhpdENvZGUgPT09IDApIHJldHVybiBjb21tYW5kO1xuICB9IGNhdGNoIHtcbiAgICAvLyBDTEkgYWJzZW50IG9yIGBhaXRvb2xzYCB1bnN1cHBvcnRlZCAtIGNhbGxlciBmYWxscyBiYWNrIC8gZmFpbHMuXG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBSdW4gYGRhdGFicmlja3MgYWl0b29scyBpbnN0YWxsIC0tcGF0aCA8ZGlyPiAtLXNraWxscy1vbmx5YCB0byBtYXRlcmlhbGl6ZSBhXG4gKiByZXNvbHZlZCwgYWdlbnQtYWdub3N0aWMgc2tpbGwgc2V0IGludG8gYSBkaXJlY3RvcnkgKG5vIGFnZW50cywgbm8gc3RhdGUpLlxuICovXG5hc3luYyBmdW5jdGlvbiBtYXRlcmlhbGl6ZVZpYUNsaShcbiAgY2xpOiBzdHJpbmcsXG4gIG9wdGlvbnM6IERhdGFicmlja3NBSVRvb2xzT3B0aW9ucyxcbik6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IGRpciA9XG4gICAgc3RyaW5nLnRyaW1Ub051bGwob3B0aW9ucy5wYXRoKSA/PyAoYXdhaXQgbWtkdGVtcChqb2luKHRtcGRpcigpLCBcImRhdGFicmlja3MtYWl0b29scy1cIikpKTtcbiAgY29uc3QgYXJncyA9IFtcImFpdG9vbHNcIiwgXCJpbnN0YWxsXCIsIFwiLS1wYXRoXCIsIGRpciwgXCItLXNraWxscy1vbmx5XCJdO1xuICBjb25zdCBza2lsbHMgPSBzdHJpbmcucGFyc2VMaXN0KG9wdGlvbnMuc2tpbGxzKTtcbiAgaWYgKHNraWxscy5sZW5ndGggPiAwKSBhcmdzLnB1c2goXCItLXNraWxsc1wiLCBza2lsbHMuam9pbihcIixcIikpO1xuICBpZiAob3B0aW9ucy5leHBlcmltZW50YWwpIGFyZ3MucHVzaChcIi0tZXhwZXJpbWVudGFsXCIpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNwYXduKGNsaSwgYXJncywge1xuICAgIHN0ZG91dDogXCJjYXB0dXJlXCIsXG4gICAgc3RkZXJyOiBcImNhcHR1cmVcIixcbiAgICBjaGVjazogZmFsc2UsXG4gIH0pO1xuICBpZiAocmVzdWx0LmV4aXRDb2RlICE9PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYGRhdGFicmlja3MgYWl0b29scyBpbnN0YWxsIGZhaWxlZCAoZXhpdCAke3Jlc3VsdC5leGl0Q29kZX0pOiAke3Jlc3VsdC5zdGRlcnIgfHwgcmVzdWx0LnN0ZG91dH1gLFxuICAgICk7XG4gIH1cbiAgaWYgKCFleGlzdHNTeW5jKGRpcikpIHJldHVybiB1bmRlZmluZWQ7XG4gIGxvZ2dlci5kZWJ1ZyhcIm1hdGVyaWFsaXplZCBhaXRvb2xzIHNraWxscyB2aWEgQ0xJXCIsIHsgcGF0aDogZGlyLCBza2lsbHMgfSk7XG4gIHJldHVybiBkaXI7XG59XG4iXX0=
|