PluginOptionsFlow.tsx
commands/plugin/PluginOptionsFlow.tsx
135
Lines
18702
Bytes
2
Exports
8
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file lives in the command layer. It likely turns a user action into concrete program behavior.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with commands. It contains 135 lines, 8 detected imports, and 2 detected exports.
Important relationships
Detected exports
findPluginOptionsTargetPluginOptionsFlow
Keywords
pluginpluginidcurrentondonereactchannelsavepluginoptionsdialogstepsloadedplugin
Detected imports
react../../types/plugin.js../../utils/errors.js../../utils/plugins/mcpbHandler.js../../utils/plugins/mcpPluginIntegration.js../../utils/plugins/pluginLoader.js../../utils/plugins/pluginOptionsStorage.js./PluginOptionsDialog.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
/**
* Post-install/post-enable config prompt.
*
* Given a LoadedPlugin, checks both the top-level manifest.userConfig and the
* channel-specific userConfig. Walks PluginOptionsDialog through each
* unconfigured item, saving via the appropriate storage function. Calls
* onDone('skipped') immediately if nothing needs filling.
*/
import * as React from 'react';
import type { LoadedPlugin } from '../../types/plugin.js';
import { errorMessage } from '../../utils/errors.js';
import { loadMcpServerUserConfig, saveMcpServerUserConfig } from '../../utils/plugins/mcpbHandler.js';
import { getUnconfiguredChannels, type UnconfiguredChannel } from '../../utils/plugins/mcpPluginIntegration.js';
import { loadAllPlugins } from '../../utils/plugins/pluginLoader.js';
import { getUnconfiguredOptions, loadPluginOptions, type PluginOptionSchema, type PluginOptionValues, savePluginOptions } from '../../utils/plugins/pluginOptionsStorage.js';
import { PluginOptionsDialog } from './PluginOptionsDialog.js';
/**
* Post-install lookup: return the LoadedPlugin for the just-installed
* pluginId so the caller can divert to PluginOptionsFlow. Returns undefined
* if the plugin somehow didn't make it into the fresh load — callers treat
* undefined as "carry on closing."
*
* Install should have cleared caches already; loadAllPlugins reads fresh.
*/
export async function findPluginOptionsTarget(pluginId: string): Promise<LoadedPlugin | undefined> {
const {
enabled,
disabled
} = await loadAllPlugins();
return [...enabled, ...disabled].find(p => p.repository === pluginId || p.source === pluginId);
}
/**
* A single dialog step in the walk. Top-level options and channels both
* collapse to this shape — the only difference is which save function runs.
*/
type ConfigStep = {
key: string;
title: string;
subtitle: string;
schema: PluginOptionSchema;
/** Returns any already-saved values so PluginOptionsDialog can pre-fill and
* skip unchanged sensitive fields on reconfigure. */
load: () => PluginOptionValues | undefined;
save: (values: PluginOptionValues) => void;
};
type Props = {
plugin: LoadedPlugin;
/** `name@marketplace` — the savePluginOptions / saveMcpServerUserConfig key. */
pluginId: string;
/**
* `configured` = user filled all fields. `skipped` = nothing needed
* configuring, or user hit cancel. `error` = save threw.
*/
onDone: (outcome: 'configured' | 'skipped' | 'error', detail?: string) => void;
};
export function PluginOptionsFlow({
plugin,
pluginId,
onDone
}: Props): React.ReactNode {
// Build the step list once at mount. Re-calling after a save would drop the
// item we just configured.
const [steps] = React.useState<ConfigStep[]>(() => {
const result: ConfigStep[] = [];
// Top-level manifest.userConfig
const unconfigured = getUnconfiguredOptions(plugin);
if (Object.keys(unconfigured).length > 0) {
result.push({
key: 'top-level',
title: `Configure ${plugin.name}`,
subtitle: 'Plugin options',
schema: unconfigured,
load: () => loadPluginOptions(pluginId),
save: values => savePluginOptions(pluginId, values, plugin.manifest.userConfig!)
});
}
// Per-channel userConfig (assistant-mode channels)
const channels: UnconfiguredChannel[] = getUnconfiguredChannels(plugin);
for (const channel of channels) {
result.push({
key: `channel:${channel.server}`,
title: `Configure ${channel.displayName}`,
subtitle: `Plugin: ${plugin.name}`,
schema: channel.configSchema,
load: () => loadMcpServerUserConfig(pluginId, channel.server) ?? undefined,
save: values_0 => saveMcpServerUserConfig(pluginId, channel.server, values_0, channel.configSchema)
});
}
return result;
});
const [index, setIndex] = React.useState(0);
// Latest-ref: lets the effect close over the current onDone without
// re-running when the parent re-renders.
const onDoneRef = React.useRef(onDone);
onDoneRef.current = onDone;
// Nothing to configure → tell the caller and render nothing. Effect,
// not inline call: calling setState in the parent during our render
// is a React rules-of-hooks violation.
React.useEffect(() => {
if (steps.length === 0) {
onDoneRef.current('skipped');
}
}, [steps.length]);
if (steps.length === 0) {
return null;
}
const current = steps[index]!;
function handleSave(values_1: PluginOptionValues): void {
try {
current.save(values_1);
} catch (err) {
onDone('error', errorMessage(err));
return;
}
const next = index + 1;
if (next < steps.length) {
setIndex(next);
} else {
onDone('configured');
}
}
// key forces a remount when advancing to the next step — React would
// otherwise reuse the instance and carry PluginOptionsDialog's
// internal useState (field index, typed values) over.
return <PluginOptionsDialog key={current.key} title={current.title} subtitle={current.subtitle} configSchema={current.schema} initialValues={current.load()} onSave={handleSave} onCancel={() => onDone('skipped')} />;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvYWRlZFBsdWdpbiIsImVycm9yTWVzc2FnZSIsImxvYWRNY3BTZXJ2ZXJVc2VyQ29uZmlnIiwic2F2ZU1jcFNlcnZlclVzZXJDb25maWciLCJnZXRVbmNvbmZpZ3VyZWRDaGFubmVscyIsIlVuY29uZmlndXJlZENoYW5uZWwiLCJsb2FkQWxsUGx1Z2lucyIsImdldFVuY29uZmlndXJlZE9wdGlvbnMiLCJsb2FkUGx1Z2luT3B0aW9ucyIsIlBsdWdpbk9wdGlvblNjaGVtYSIsIlBsdWdpbk9wdGlvblZhbHVlcyIsInNhdmVQbHVnaW5PcHRpb25zIiwiUGx1Z2luT3B0aW9uc0RpYWxvZyIsImZpbmRQbHVnaW5PcHRpb25zVGFyZ2V0IiwicGx1Z2luSWQiLCJQcm9taXNlIiwiZW5hYmxlZCIsImRpc2FibGVkIiwiZmluZCIsInAiLCJyZXBvc2l0b3J5Iiwic291cmNlIiwiQ29uZmlnU3RlcCIsImtleSIsInRpdGxlIiwic3VidGl0bGUiLCJzY2hlbWEiLCJsb2FkIiwic2F2ZSIsInZhbHVlcyIsIlByb3BzIiwicGx1Z2luIiwib25Eb25lIiwib3V0Y29tZSIsImRldGFpbCIsIlBsdWdpbk9wdGlvbnNGbG93IiwiUmVhY3ROb2RlIiwic3RlcHMiLCJ1c2VTdGF0ZSIsInJlc3VsdCIsInVuY29uZmlndXJlZCIsIk9iamVjdCIsImtleXMiLCJsZW5ndGgiLCJwdXNoIiwibmFtZSIsIm1hbmlmZXN0IiwidXNlckNvbmZpZyIsImNoYW5uZWxzIiwiY2hhbm5lbCIsInNlcnZlciIsImRpc3BsYXlOYW1lIiwiY29uZmlnU2NoZW1hIiwidW5kZWZpbmVkIiwiaW5kZXgiLCJzZXRJbmRleCIsIm9uRG9uZVJlZiIsInVzZVJlZiIsImN1cnJlbnQiLCJ1c2VFZmZlY3QiLCJoYW5kbGVTYXZlIiwiZXJyIiwibmV4dCJdLCJzb3VyY2VzIjpbIlBsdWdpbk9wdGlvbnNGbG93LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBvc3QtaW5zdGFsbC9wb3N0LWVuYWJsZSBjb25maWcgcHJvbXB0LlxuICpcbiAqIEdpdmVuIGEgTG9hZGVkUGx1Z2luLCBjaGVja3MgYm90aCB0aGUgdG9wLWxldmVsIG1hbmlmZXN0LnVzZXJDb25maWcgYW5kIHRoZVxuICogY2hhbm5lbC1zcGVjaWZpYyB1c2VyQ29uZmlnLiBXYWxrcyBQbHVnaW5PcHRpb25zRGlhbG9nIHRocm91Z2ggZWFjaFxuICogdW5jb25maWd1cmVkIGl0ZW0sIHNhdmluZyB2aWEgdGhlIGFwcHJvcHJpYXRlIHN0b3JhZ2UgZnVuY3Rpb24uIENhbGxzXG4gKiBvbkRvbmUoJ3NraXBwZWQnKSBpbW1lZGlhdGVseSBpZiBub3RoaW5nIG5lZWRzIGZpbGxpbmcuXG4gKi9cblxuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IExvYWRlZFBsdWdpbiB9IGZyb20gJy4uLy4uL3R5cGVzL3BsdWdpbi5qcydcbmltcG9ydCB7IGVycm9yTWVzc2FnZSB9IGZyb20gJy4uLy4uL3V0aWxzL2Vycm9ycy5qcydcbmltcG9ydCB7XG4gIGxvYWRNY3BTZXJ2ZXJVc2VyQ29uZmlnLFxuICBzYXZlTWNwU2VydmVyVXNlckNvbmZpZyxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvcGx1Z2lucy9tY3BiSGFuZGxlci5qcydcbmltcG9ydCB7XG4gIGdldFVuY29uZmlndXJlZENoYW5uZWxzLFxuICB0eXBlIFVuY29uZmlndXJlZENoYW5uZWwsXG59IGZyb20gJy4uLy4uL3V0aWxzL3BsdWdpbnMvbWNwUGx1Z2luSW50ZWdyYXRpb24uanMnXG5pbXBvcnQgeyBsb2FkQWxsUGx1Z2lucyB9IGZyb20gJy4uLy4uL3V0aWxzL3BsdWdpbnMvcGx1Z2luTG9hZGVyLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0VW5jb25maWd1cmVkT3B0aW9ucyxcbiAgbG9hZFBsdWdpbk9wdGlvbnMsXG4gIHR5cGUgUGx1Z2luT3B0aW9uU2NoZW1hLFxuICB0eXBlIFBsdWdpbk9wdGlvblZhbHVlcyxcbiAgc2F2ZVBsdWdpbk9wdGlvbnMsXG59IGZyb20gJy4uLy4uL3V0aWxzL3BsdWdpbnMvcGx1Z2luT3B0aW9uc1N0b3JhZ2UuanMnXG5pbXBvcnQgeyBQbHVnaW5PcHRpb25zRGlhbG9nIH0gZnJvbSAnLi9QbHVnaW5PcHRpb25zRGlhbG9nLmpzJ1xuXG4vKipcbiAqIFBvc3QtaW5zdGFsbCBsb29rdXA6IHJldHVybiB0aGUgTG9hZGVkUGx1Z2luIGZvciB0aGUganVzdC1pbnN0YWxsZWRcbiAqIHBsdWdpbklkIHNvIHRoZSBjYWxsZXIgY2FuIGRpdmVydCB0byBQbHVnaW5PcHRpb25zRmxvdy4gUmV0dXJucyB1bmRlZmluZWRcbiAqIGlmIHRoZSBwbHVnaW4gc29tZWhvdyBkaWRuJ3QgbWFrZSBpdCBpbnRvIHRoZSBmcmVzaCBsb2FkIOKAlCBjYWxsZXJzIHRyZWF0XG4gKiB1bmRlZmluZWQgYXMgXCJjYXJyeSBvbiBjbG9zaW5nLlwiXG4gKlxuICogSW5zdGFsbCBzaG91bGQgaGF2ZSBjbGVhcmVkIGNhY2hlcyBhbHJlYWR5OyBsb2FkQWxsUGx1Z2lucyByZWFkcyBmcmVzaC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbmRQbHVnaW5PcHRpb25zVGFyZ2V0KFxuICBwbHVnaW5JZDogc3RyaW5nLFxuKTogUHJvbWlzZTxMb2FkZWRQbHVnaW4gfCB1bmRlZmluZWQ+IHtcbiAgY29uc3QgeyBlbmFibGVkLCBkaXNhYmxlZCB9ID0gYXdhaXQgbG9hZEFsbFBsdWdpbnMoKVxuICByZXR1cm4gWy4uLmVuYWJsZWQsIC4uLmRpc2FibGVkXS5maW5kKFxuICAgIHAgPT4gcC5yZXBvc2l0b3J5ID09PSBwbHVnaW5JZCB8fCBwLnNvdXJjZSA9PT0gcGx1Z2luSWQsXG4gIClcbn1cblxuLyoqXG4gKiBBIHNpbmdsZSBkaWFsb2cgc3RlcCBpbiB0aGUgd2Fsay4gVG9wLWxldmVsIG9wdGlvbnMgYW5kIGNoYW5uZWxzIGJvdGhcbiAqIGNvbGxhcHNlIHRvIHRoaXMgc2hhcGUg4oCUIHRoZSBvbmx5IGRpZmZlcmVuY2UgaXMgd2hpY2ggc2F2ZSBmdW5jdGlvbiBydW5zLlxuICovXG50eXBlIENvbmZpZ1N0ZXAgPSB7XG4gIGtleTogc3RyaW5nXG4gIHRpdGxlOiBzdHJpbmdcbiAgc3VidGl0bGU6IHN0cmluZ1xuICBzY2hlbWE6IFBsdWdpbk9wdGlvblNjaGVtYVxuICAvKiogUmV0dXJucyBhbnkgYWxyZWFkeS1zYXZlZCB2YWx1ZXMgc28gUGx1Z2luT3B0aW9uc0RpYWxvZyBjYW4gcHJlLWZpbGwgYW5kXG4gICAqICBza2lwIHVuY2hhbmdlZCBzZW5zaXRpdmUgZmllbGRzIG9uIHJlY29uZmlndXJlLiAqL1xuICBsb2FkOiAoKSA9PiBQbHVnaW5PcHRpb25WYWx1ZXMgfCB1bmRlZmluZWRcbiAgc2F2ZTogKHZhbHVlczogUGx1Z2luT3B0aW9uVmFsdWVzKSA9PiB2b2lkXG59XG5cbnR5cGUgUHJvcHMgPSB7XG4gIHBsdWdpbjogTG9hZGVkUGx1Z2luXG4gIC8qKiBgbmFtZUBtYXJrZXRwbGFjZWAg4oCUIHRoZSBzYXZlUGx1Z2luT3B0aW9ucyAvIHNhdmVNY3BTZXJ2ZXJVc2VyQ29uZmlnIGtleS4gKi9cbiAgcGx1Z2luSWQ6IHN0cmluZ1xuICAvKipcbiAgICogYGNvbmZpZ3VyZWRgID0gdXNlciBmaWxsZWQgYWxsIGZpZWxkcy4gYHNraXBwZWRgID0gbm90aGluZyBuZWVkZWRcbiAgICogY29uZmlndXJpbmcsIG9yIHVzZXIgaGl0IGNhbmNlbC4gYGVycm9yYCA9IHNhdmUgdGhyZXcuXG4gICAqL1xuICBvbkRvbmU6IChvdXRjb21lOiAnY29uZmlndXJlZCcgfCAnc2tpcHBlZCcgfCAnZXJyb3InLCBkZXRhaWw/OiBzdHJpbmcpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFBsdWdpbk9wdGlvbnNGbG93KHtcbiAgcGx1Z2luLFxuICBwbHVnaW5JZCxcbiAgb25Eb25lLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBCdWlsZCB0aGUgc3RlcCBsaXN0IG9uY2UgYXQgbW91bnQuIFJlLWNhbGxpbmcgYWZ0ZXIgYSBzYXZlIHdvdWxkIGRyb3AgdGhlXG4gIC8vIGl0ZW0gd2UganVzdCBjb25maWd1cmVkLlxuICBjb25zdCBbc3RlcHNdID0gUmVhY3QudXNlU3RhdGU8Q29uZmlnU3RlcFtdPigoKSA9PiB7XG4gICAgY29uc3QgcmVzdWx0OiBDb25maWdTdGVwW10gPSBbXVxuXG4gICAgLy8gVG9wLWxldmVsIG1hbmlmZXN0LnVzZXJDb25maWdcbiAgICBjb25zdCB1bmNvbmZpZ3VyZWQgPSBnZXRVbmNvbmZpZ3VyZWRPcHRpb25zKHBsdWdpbilcbiAgICBpZiAoT2JqZWN0LmtleXModW5jb25maWd1cmVkKS5sZW5ndGggPiAwKSB7XG4gICAgICByZXN1bHQucHVzaCh7XG4gICAgICAgIGtleTogJ3RvcC1sZXZlbCcsXG4gICAgICAgIHRpdGxlOiBgQ29uZmlndXJlICR7cGx1Z2luLm5hbWV9YCxcbiAgICAgICAgc3VidGl0bGU6ICdQbHVnaW4gb3B0aW9ucycsXG4gICAgICAgIHNjaGVtYTogdW5jb25maWd1cmVkLFxuICAgICAgICBsb2FkOiAoKSA9PiBsb2FkUGx1Z2luT3B0aW9ucyhwbHVnaW5JZCksXG4gICAgICAgIHNhdmU6IHZhbHVlcyA9PlxuICAgICAgICAgIHNhdmVQbHVnaW5PcHRpb25zKHBsdWdpbklkLCB2YWx1ZXMsIHBsdWdpbi5tYW5pZmVzdC51c2VyQ29uZmlnISksXG4gICAgICB9KVxuICAgIH1cblxuICAgIC8vIFBlci1jaGFubmVsIHVzZXJDb25maWcgKGFzc2lzdGFudC1tb2RlIGNoYW5uZWxzKVxuICAgIGNvbnN0IGNoYW5uZWxzOiBVbmNvbmZpZ3VyZWRDaGFubmVsW10gPSBnZXRVbmNvbmZpZ3VyZWRDaGFubmVscyhwbHVnaW4pXG4gICAgZm9yIChjb25zdCBjaGFubmVsIG9mIGNoYW5uZWxzKSB7XG4gICAgICByZXN1bHQucHVzaCh7XG4gICAgICAgIGtleTogYGNoYW5uZWw6JHtjaGFubmVsLnNlcnZlcn1gLFxuICAgICAgICB0aXRsZTogYENvbmZpZ3VyZSAke2NoYW5uZWwuZGlzcGxheU5hbWV9YCxcbiAgICAgICAgc3VidGl0bGU6IGBQbHVnaW46ICR7cGx1Z2luLm5hbWV9YCxcbiAgICAgICAgc2NoZW1hOiBjaGFubmVsLmNvbmZpZ1NjaGVtYSxcbiAgICAgICAgbG9hZDogKCkgPT5cbiAgICAgICAgICBsb2FkTWNwU2VydmVyVXNlckNvbmZpZyhwbHVnaW5JZCwgY2hhbm5lbC5zZXJ2ZXIpID8/IHVuZGVmaW5lZCxcbiAgICAgICAgc2F2ZTogdmFsdWVzID0+XG4gICAgICAgICAgc2F2ZU1jcFNlcnZlclVzZXJDb25maWcoXG4gICAgICAgICAgICBwbHVnaW5JZCxcbiAgICAgICAgICAgIGNoYW5uZWwuc2VydmVyLFxuICAgICAgICAgICAgdmFsdWVzLFxuICAgICAgICAgICAgY2hhbm5lbC5jb25maWdTY2hlbWEsXG4gICAgICAgICAgKSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdFxuICB9KVxuXG4gIGNvbnN0IFtpbmRleCwgc2V0SW5kZXhdID0gUmVhY3QudXNlU3RhdGUoMClcblxuICAvLyBMYXRlc3QtcmVmOiBsZXRzIHRoZSBlZmZlY3QgY2xvc2Ugb3ZlciB0aGUgY3VycmVudCBvbkRvbmUgd2l0aG91dFxuICAvLyByZS1ydW5uaW5nIHdoZW4gdGhlIHBhcmVudCByZS1yZW5kZXJzLlxuICBjb25zdCBvbkRvbmVSZWYgPSBSZWFjdC51c2VSZWYob25Eb25lKVxuICBvbkRvbmVSZWYuY3VycmVudCA9IG9uRG9uZVxuXG4gIC8vIE5vdGhpbmcgdG8gY29uZmlndXJlIOKGkiB0ZWxsIHRoZSBjYWxsZXIgYW5kIHJlbmRlciBub3RoaW5nLiBFZmZlY3QsXG4gIC8vIG5vdCBpbmxpbmUgY2FsbDogY2FsbGluZyBzZXRTdGF0ZSBpbiB0aGUgcGFyZW50IGR1cmluZyBvdXIgcmVuZGVyXG4gIC8vIGlzIGEgUmVhY3QgcnVsZXMtb2YtaG9va3MgdmlvbGF0aW9uLlxuICBSZWFjdC51c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChzdGVwcy5sZW5ndGggPT09IDApIHtcbiAgICAgIG9uRG9uZVJlZi5jdXJyZW50KCdza2lwcGVkJylcbiAgICB9XG4gIH0sIFtzdGVwcy5sZW5ndGhdKVxuXG4gIGlmIChzdGVwcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgY29uc3QgY3VycmVudCA9IHN0ZXBzW2luZGV4XSFcblxuICBmdW5jdGlvbiBoYW5kbGVTYXZlKHZhbHVlczogUGx1Z2luT3B0aW9uVmFsdWVzKTogdm9pZCB7XG4gICAgdHJ5IHtcbiAgICAgIGN1cnJlbnQuc2F2ZSh2YWx1ZXMpXG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBvbkRvbmUoJ2Vycm9yJywgZXJyb3JNZXNzYWdlKGVycikpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgY29uc3QgbmV4dCA9IGluZGV4ICsgMVxuICAgIGlmIChuZXh0IDwgc3RlcHMubGVuZ3RoKSB7XG4gICAgICBzZXRJbmRleChuZXh0KVxuICAgIH0gZWxzZSB7XG4gICAgICBvbkRvbmUoJ2NvbmZpZ3VyZWQnKVxuICAgIH1cbiAgfVxuXG4gIC8vIGtleSBmb3JjZXMgYSByZW1vdW50IHdoZW4gYWR2YW5jaW5nIHRvIHRoZSBuZXh0IHN0ZXAg4oCUIFJlYWN0IHdvdWxkXG4gIC8vIG90aGVyd2lzZSByZXVzZSB0aGUgaW5zdGFuY2UgYW5kIGNhcnJ5IFBsdWdpbk9wdGlvbnNEaWFsb2cnc1xuICAvLyBpbnRlcm5hbCB1c2VTdGF0ZSAoZmllbGQgaW5kZXgsIHR5cGVkIHZhbHVlcykgb3Zlci5cbiAgcmV0dXJuIChcbiAgICA8UGx1Z2luT3B0aW9uc0RpYWxvZ1xuICAgICAga2V5PXtjdXJyZW50LmtleX1cbiAgICAgIHRpdGxlPXtjdXJyZW50LnRpdGxlfVxuICAgICAgc3VidGl0bGU9e2N1cnJlbnQuc3VidGl0bGV9XG4gICAgICBjb25maWdTY2hlbWE9e2N1cnJlbnQuc2NoZW1hfVxuICAgICAgaW5pdGlhbFZhbHVlcz17Y3VycmVudC5sb2FkKCl9XG4gICAgICBvblNhdmU9e2hhbmRsZVNhdmV9XG4gICAgICBvbkNhbmNlbD17KCkgPT4gb25Eb25lKCdza2lwcGVkJyl9XG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsY0FBY0MsWUFBWSxRQUFRLHVCQUF1QjtBQUN6RCxTQUFTQyxZQUFZLFFBQVEsdUJBQXVCO0FBQ3BELFNBQ0VDLHVCQUF1QixFQUN2QkMsdUJBQXVCLFFBQ2xCLG9DQUFvQztBQUMzQyxTQUNFQyx1QkFBdUIsRUFDdkIsS0FBS0MsbUJBQW1CLFFBQ25CLDZDQUE2QztBQUNwRCxTQUFTQyxjQUFjLFFBQVEscUNBQXFDO0FBQ3BFLFNBQ0VDLHNCQUFzQixFQUN0QkMsaUJBQWlCLEVBQ2pCLEtBQUtDLGtCQUFrQixFQUN2QixLQUFLQyxrQkFBa0IsRUFDdkJDLGlCQUFpQixRQUNaLDZDQUE2QztBQUNwRCxTQUFTQyxtQkFBbUIsUUFBUSwwQkFBMEI7O0FBRTlEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLGVBQWVDLHVCQUF1QkEsQ0FDM0NDLFFBQVEsRUFBRSxNQUFNLENBQ2pCLEVBQUVDLE9BQU8sQ0FBQ2YsWUFBWSxHQUFHLFNBQVMsQ0FBQyxDQUFDO0VBQ25DLE1BQU07SUFBRWdCLE9BQU87SUFBRUM7RUFBUyxDQUFDLEdBQUcsTUFBTVgsY0FBYyxDQUFDLENBQUM7RUFDcEQsT0FBTyxDQUFDLEdBQUdVLE9BQU8sRUFBRSxHQUFHQyxRQUFRLENBQUMsQ0FBQ0MsSUFBSSxDQUNuQ0MsQ0FBQyxJQUFJQSxDQUFDLENBQUNDLFVBQVUsS0FBS04sUUFBUSxJQUFJSyxDQUFDLENBQUNFLE1BQU0sS0FBS1AsUUFDakQsQ0FBQztBQUNIOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBS1EsVUFBVSxHQUFHO0VBQ2hCQyxHQUFHLEVBQUUsTUFBTTtFQUNYQyxLQUFLLEVBQUUsTUFBTTtFQUNiQyxRQUFRLEVBQUUsTUFBTTtFQUNoQkMsTUFBTSxFQUFFakIsa0JBQWtCO0VBQzFCO0FBQ0Y7RUFDRWtCLElBQUksRUFBRSxHQUFHLEdBQUdqQixrQkFBa0IsR0FBRyxTQUFTO0VBQzFDa0IsSUFBSSxFQUFFLENBQUNDLE1BQU0sRUFBRW5CLGtCQUFrQixFQUFFLEdBQUcsSUFBSTtBQUM1QyxDQUFDO0FBRUQsS0FBS29CLEtBQUssR0FBRztFQUNYQyxNQUFNLEVBQUUvQixZQUFZO0VBQ3BCO0VBQ0FjLFFBQVEsRUFBRSxNQUFNO0VBQ2hCO0FBQ0Y7QUFDQTtBQUNBO0VBQ0VrQixNQUFNLEVBQUUsQ0FBQ0MsT0FBTyxFQUFFLFlBQVksR0FBRyxTQUFTLEdBQUcsT0FBTyxFQUFFQyxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0FBQ2hGLENBQUM7QUFFRCxPQUFPLFNBQVNDLGlCQUFpQkEsQ0FBQztFQUNoQ0osTUFBTTtFQUNOakIsUUFBUTtFQUNSa0I7QUFDSyxDQUFOLEVBQUVGLEtBQUssQ0FBQyxFQUFFL0IsS0FBSyxDQUFDcUMsU0FBUyxDQUFDO0VBQ3pCO0VBQ0E7RUFDQSxNQUFNLENBQUNDLEtBQUssQ0FBQyxHQUFHdEMsS0FBSyxDQUFDdUMsUUFBUSxDQUFDaEIsVUFBVSxFQUFFLENBQUMsQ0FBQyxNQUFNO0lBQ2pELE1BQU1pQixNQUFNLEVBQUVqQixVQUFVLEVBQUUsR0FBRyxFQUFFOztJQUUvQjtJQUNBLE1BQU1rQixZQUFZLEdBQUdqQyxzQkFBc0IsQ0FBQ3dCLE1BQU0sQ0FBQztJQUNuRCxJQUFJVSxNQUFNLENBQUNDLElBQUksQ0FBQ0YsWUFBWSxDQUFDLENBQUNHLE1BQU0sR0FBRyxDQUFDLEVBQUU7TUFDeENKLE1BQU0sQ0FBQ0ssSUFBSSxDQUFDO1FBQ1ZyQixHQUFHLEVBQUUsV0FBVztRQUNoQkMsS0FBSyxFQUFFLGFBQWFPLE1BQU0sQ0FBQ2MsSUFBSSxFQUFFO1FBQ2pDcEIsUUFBUSxFQUFFLGdCQUFnQjtRQUMxQkMsTUFBTSxFQUFFYyxZQUFZO1FBQ3BCYixJQUFJLEVBQUVBLENBQUEsS0FBTW5CLGlCQUFpQixDQUFDTSxRQUFRLENBQUM7UUFDdkNjLElBQUksRUFBRUMsTUFBTSxJQUNWbEIsaUJBQWlCLENBQUNHLFFBQVEsRUFBRWUsTUFBTSxFQUFFRSxNQUFNLENBQUNlLFFBQVEsQ0FBQ0MsVUFBVSxDQUFDO01BQ25FLENBQUMsQ0FBQztJQUNKOztJQUVBO0lBQ0EsTUFBTUMsUUFBUSxFQUFFM0MsbUJBQW1CLEVBQUUsR0FBR0QsdUJBQXVCLENBQUMyQixNQUFNLENBQUM7SUFDdkUsS0FBSyxNQUFNa0IsT0FBTyxJQUFJRCxRQUFRLEVBQUU7TUFDOUJULE1BQU0sQ0FBQ0ssSUFBSSxDQUFDO1FBQ1ZyQixHQUFHLEVBQUUsV0FBVzBCLE9BQU8sQ0FBQ0MsTUFBTSxFQUFFO1FBQ2hDMUIsS0FBSyxFQUFFLGFBQWF5QixPQUFPLENBQUNFLFdBQVcsRUFBRTtRQUN6QzFCLFFBQVEsRUFBRSxXQUFXTSxNQUFNLENBQUNjLElBQUksRUFBRTtRQUNsQ25CLE1BQU0sRUFBRXVCLE9BQU8sQ0FBQ0csWUFBWTtRQUM1QnpCLElBQUksRUFBRUEsQ0FBQSxLQUNKekIsdUJBQXVCLENBQUNZLFFBQVEsRUFBRW1DLE9BQU8sQ0FBQ0MsTUFBTSxDQUFDLElBQUlHLFNBQVM7UUFDaEV6QixJQUFJLEVBQUVDLFFBQU0sSUFDVjFCLHVCQUF1QixDQUNyQlcsUUFBUSxFQUNSbUMsT0FBTyxDQUFDQyxNQUFNLEVBQ2RyQixRQUFNLEVBQ05vQixPQUFPLENBQUNHLFlBQ1Y7TUFDSixDQUFDLENBQUM7SUFDSjtJQUVBLE9BQU9iLE1BQU07RUFDZixDQUFDLENBQUM7RUFFRixNQUFNLENBQUNlLEtBQUssRUFBRUMsUUFBUSxDQUFDLEdBQUd4RCxLQUFLLENBQUN1QyxRQUFRLENBQUMsQ0FBQyxDQUFDOztFQUUzQztFQUNBO0VBQ0EsTUFBTWtCLFNBQVMsR0FBR3pELEtBQUssQ0FBQzBELE1BQU0sQ0FBQ3pCLE1BQU0sQ0FBQztFQUN0Q3dCLFNBQVMsQ0FBQ0UsT0FBTyxHQUFHMUIsTUFBTTs7RUFFMUI7RUFDQTtFQUNBO0VBQ0FqQyxLQUFLLENBQUM0RCxTQUFTLENBQUMsTUFBTTtJQUNwQixJQUFJdEIsS0FBSyxDQUFDTSxNQUFNLEtBQUssQ0FBQyxFQUFFO01BQ3RCYSxTQUFTLENBQUNFLE9BQU8sQ0FBQyxTQUFTLENBQUM7SUFDOUI7RUFDRixDQUFDLEVBQUUsQ0FBQ3JCLEtBQUssQ0FBQ00sTUFBTSxDQUFDLENBQUM7RUFFbEIsSUFBSU4sS0FBSyxDQUFDTSxNQUFNLEtBQUssQ0FBQyxFQUFFO0lBQ3RCLE9BQU8sSUFBSTtFQUNiO0VBRUEsTUFBTWUsT0FBTyxHQUFHckIsS0FBSyxDQUFDaUIsS0FBSyxDQUFDLENBQUM7RUFFN0IsU0FBU00sVUFBVUEsQ0FBQy9CLFFBQU0sRUFBRW5CLGtCQUFrQixDQUFDLEVBQUUsSUFBSSxDQUFDO0lBQ3BELElBQUk7TUFDRmdELE9BQU8sQ0FBQzlCLElBQUksQ0FBQ0MsUUFBTSxDQUFDO0lBQ3RCLENBQUMsQ0FBQyxPQUFPZ0MsR0FBRyxFQUFFO01BQ1o3QixNQUFNLENBQUMsT0FBTyxFQUFFL0IsWUFBWSxDQUFDNEQsR0FBRyxDQUFDLENBQUM7TUFDbEM7SUFDRjtJQUNBLE1BQU1DLElBQUksR0FBR1IsS0FBSyxHQUFHLENBQUM7SUFDdEIsSUFBSVEsSUFBSSxHQUFHekIsS0FBSyxDQUFDTSxNQUFNLEVBQUU7TUFDdkJZLFFBQVEsQ0FBQ08sSUFBSSxDQUFDO0lBQ2hCLENBQUMsTUFBTTtNQUNMOUIsTUFBTSxDQUFDLFlBQVksQ0FBQztJQUN0QjtFQUNGOztFQUVBO0VBQ0E7RUFDQTtFQUNBLE9BQ0UsQ0FBQyxtQkFBbUIsQ0FDbEIsR0FBRyxDQUFDLENBQUMwQixPQUFPLENBQUNuQyxHQUFHLENBQUMsQ0FDakIsS0FBSyxDQUFDLENBQUNtQyxPQUFPLENBQUNsQyxLQUFLLENBQUMsQ0FDckIsUUFBUSxDQUFDLENBQUNrQyxPQUFPLENBQUNqQyxRQUFRLENBQUMsQ0FDM0IsWUFBWSxDQUFDLENBQUNpQyxPQUFPLENBQUNoQyxNQUFNLENBQUMsQ0FDN0IsYUFBYSxDQUFDLENBQUNnQyxPQUFPLENBQUMvQixJQUFJLENBQUMsQ0FBQyxDQUFDLENBQzlCLE1BQU0sQ0FBQyxDQUFDaUMsVUFBVSxDQUFDLENBQ25CLFFBQVEsQ0FBQyxDQUFDLE1BQU01QixNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsR0FDbEM7QUFFTiIsImlnbm9yZUxpc3QiOltdfQ==