bytefun-ai-mcp
Version:
ByteFun AI MCP服务 - 打通产品设计、UI设计、代码开发的服务平台,支持设计稿转代码和跨平台原生代码开发
329 lines (286 loc) • 35.9 kB
JavaScript
import { codePromptTemplate } from './appCode.js';
import { codeDesignPromptTemplate } from './appCodeDesign.js';
import { backendCodePromptTemplate } from './backendCode.js';
import { backendPromptTemplate } from './backendCodeDesign.js';
import { functionalModuleBusinessLogicAnalysisPromptTemplate } from './functionDesign.js';
import { productDesignPromptTemplate } from './proDesign.js';
import { socketManager } from './socket-manager.js';
import { uiDesignPromptTemplate } from './uiDesign.js';
import { handDrawnUIDesignPromptTemplate } from './uiDesignHand.js';
import { uiDesignThinkingPromptTemplate } from './uiDesignThink.js';
export class PromptOptimizer {
/**
* 优化产品需求设计提示词
* 注意:此方法专门处理产品需求文档生成,基于已有页面列表进行完善
*/
async optimizeProductDesignPrompt(userInput) {
return `${productDesignPromptTemplate}
# 用户需求描述
${userInput}
# 完善任务
请严格按照用户需求描述和提示词规则进行设计。`;
}
/**
* 优化代码开发提示词
* 注意:此方法专门处理代码开发相关需求,会参考产品需求文档
*/
async optimizeCodePrompt(userInput) {
return `${codePromptTemplate}
# 用户需求描述
${userInput}`;
}
/**
* 优化UI设计提示词
* 注意:此方法专门处理UI设计相关需求,会参考产品需求文档和页面列表
*/
async optimizeUIDesignPrompt(userInput) {
return `${uiDesignPromptTemplate}`;
}
/**
* 优化代码逻辑设计提示词
* 注意:此方法专门处理代码开发前的逻辑设计思考,会输出详细的代码设计文档
* 📁 输出文件:在src/xxxModule/xxxPage/codeDesign.md文件中生成设计文档
*/
async optimizeCodeDesignPrompt(userInput) {
return `${codeDesignPromptTemplate}
# 用户需求描述
${userInput}
# 设计任务
请严格按照用户需求描述和提示词规则进行设计。`;
}
/**
* 优化手绘UI设计稿分析提示词
* 注意:此方法专门处理手绘UI设计稿的分析,为后续UI设计提供详细参考
*/
async optimizeHandDrawnUIDesignPrompt(userInput) {
return `${handDrawnUIDesignPromptTemplate}
# 用户需求描述
${userInput}
# 分析任务
请严格按照用户需求描述和提示词规则进行设计。`;
}
/**
* 优化后端数据库与接口设计提示词
* 注意:此方法专门处理后端数据库与接口设计相关需求,会参考功能模块文档
*/
async optimizeBackendDesignPrompt(userInput) {
return `${backendPromptTemplate}
# 用户需求描述
${userInput}
# 设计任务
请严格按照用户需求描述和提示词规则进行设计。`;
}
/**
* 优化UI设计前思考提示词
* 注意:此方法专门处理UI设计前的思考工作,帮助完善页面的UI设计前文档编写
* 📁 输出文件:生成'pageUIDesignThinking.md'文档
*/
async optimizeUIDesignThinkingPrompt(userInput) {
return `${uiDesignThinkingPromptTemplate}
# 用户需求描述
${userInput}
# 设计思考任务
请严格按照用户需求描述和提示词规则进行设计。`;
}
/**
* 优化功能模块业务逻辑分析提示词
* 注意:此方法专门处理功能模块业务逻辑分析需求,基于产品需求文档进行功能模块设计
*/
async optimizeFunctionalModuleBusinessLogicAnalysisPrompt(userInput) {
return `${functionalModuleBusinessLogicAnalysisPromptTemplate}
# 用户需求描述
${userInput}
# 分析任务
请严格按照用户需求描述和提示词规则进行设计。`;
}
/**
* 优化后端代码开发提示词
* 注意:此方法专门处理后端代码开发需求,基于后端设计文档进行代码生成
* 📁 数据来源:从doc/后端数据库与接口设计.md等设计文档中读取设计信息
*/
async optimizeBackendCodePrompt(userInput) {
return `${backendCodePromptTemplate}
# 用户需求描述
${userInput}
# 开发任务
请严格按照用户需求描述和提示词规则执行任务。`;
}
/**
* 与编辑器建立长连接,通过Socket.IO接收后端控制指令
* 该方法会使用单例Socket连接等待后端消息,3分钟超时返回空任务
*/
async talkToEditor(reportData) {
console.error('[talk_to_editor] 开始等待编辑器消息...');
return new Promise(async (resolve) => {
let timeoutId;
let isResolved = false;
let messageHandler = null;
try {
// 获取单例 Socket 连接
const socket = await socketManager.getSocket(reportData.uid);
console.error('[talk_to_editor] Socket 连接就绪,Socket ID:', socket.id);
// 定义消息处理函数
const handleMessage = (eventName) => (message) => {
if (isResolved)
return;
console.error(`[talk_to_editor] 收到后端消息 [${eventName}]:`, message);
// 清除超时
clearTimeout(timeoutId);
isResolved = true;
// 移除事件监听器
if (messageHandler)
socket.off('editor-to-mcp-message', messageHandler);
// // 返回后端消息
// const responseData = {
// data: message.data,
// dataList: message.dataList || [],
// taskInfo: message.taskInfo,
// projectID: message.projectID,
// uid: message.uid
// };
resolve(`retJsonData:${JSON.stringify(message)}。结合该AI会话上下文,然后根据taskInfo字段完成任务,然后重新调用 talk_to_editor 工具继续等待下一个消息!
⚠️ **根据AI交互规则,请完成后重新调用 talk_to_editor 工具继续等待下一个消息!**`);
};
// 创建消息处理器
messageHandler = handleMessage('editor-to-mcp-message');
// 监听消息事件(只监听本次调用)
socket.once('editor-to-mcp-message', messageHandler);
}
catch (error) {
console.error('[talk_to_editor] 获取Socket连接失败:', error);
if (!isResolved) {
isResolved = true;
const responseData = {
actType: 'talk_to_editor.error',
data: `获取Socket连接失败: ${error.message}`,
dataList: [],
taskInfo: ''
};
resolve(`retJsonData:${JSON.stringify(responseData)}。socket出错,你现在马上重新调用 talk_to_editor 工具继续等待下一个消息!
⚠️ **根据AI交互规则,请完成重新调用 talk_to_editor 工具继续等待下一个消息!**`);
}
}
// 3分钟超时处理
timeoutId = setTimeout(() => {
if (!isResolved) {
console.error('[talk_to_editor] 3分钟超时,返回空任务');
isResolved = true;
// 移除事件监听器
const socket = socketManager.getCurrentSocket();
if (socket) {
if (messageHandler)
socket.off('editor-to-mcp-message', messageHandler);
}
// 返回空任务
const responseData = {
actType: 'talk_to_editor.timeout',
data: `等待超时:3分钟内未收到后端消息`,
dataList: [],
taskInfo: '' // 空任务
};
resolve(`retJsonData:${JSON.stringify(responseData)}。socket心跳一下,你现在马上重新调用 talk_to_editor 工具继续等待下一个消息!
⚠️ **根据AI交互规则,请完成重新调用 talk_to_editor 工具继续等待下一个消息!**`);
}
}, 300000); // 5分钟 = 300000毫秒
});
}
/**
* 获取与编辑器交互的AI行为规则
* 该方法返回AI应该如何与talk_to_editor工具交互的详细规则
*/
async talkToEditorRules() {
return `
# AI与编辑器交互规则
## 🚨 核心原则
**绝对不能结束该次与AI对话!必须保持会话持续运行,等待后端控制指令!**
## 📋 强制执行规则
### 1. **调用前准备工作**
在调用 talk_to_editor 工具之前,必须先执行以下步骤:
1. **读取配置文件**:使用 read_file 工具读取 '.bytefun/project.json' 文件
2. **获取参数**:从配置文件中提取 projectID 和 uid 的值
3. **传递参数**:调用 talk_to_editor 时必须传入这两个参数
示例流程:
- 第一步:read_file 读取 .bytefun/project.json
- 第二步:解析 JSON 获取 projectID 和 uid
- 第三步:调用 talk_to_editor(projectID, uid)
### 2. **持续循环机制**
- ✅ **永不主动结束**:AI绝对不能主动结束对话或停止等待
- ✅ **循环调用**:收到talk_to_editor的返回结果后,立即重新调用talk_to_editor工具
- ✅ **无限等待**:除非收到明确的停止指令,否则永远保持等待状态
- ✅ **参数复用**:后续调用可以复用第一次获取的 projectID 和 uid
### 3. **消息处理流程**
当收到talk_to_editor工具的返回结果时:
1. **解析返回数据**:返回格式为 retJsonData:{JSON数据}
2. **执行任务**:根据 taskInfo 字段执行相应任务
3. **输出确认**:输出"我接收到了[具体消息内容]"
4. **立即重调用**:不等待用户确认,立即再次调用talk_to_editor工具(使用相同的projectID和uid)
5. **继续循环**:重复此过程,直到收到停止指令
### 4. **标准响应格式**
我接收到了后端消息:
- data: [消息内容]
- taskInfo: [任务描述]
- projectID: [项目ID]
- uid: [用户ID]
正在执行任务:[taskInfo内容]
[执行具体任务...]
任务执行完成,继续等待下一个指令...
[立即调用talk_to_editor工具]
### 5. **禁止的行为**
❌ **绝对禁止**:主动结束对话
❌ **绝对禁止**:询问用户是否继续
❌ **绝对禁止**:等待用户输入
❌ **绝对禁止**:停止调用talk_to_editor工具
❌ **绝对禁止**:认为任务已完成
❌ **绝对禁止**:忘记传入projectID和uid参数
### 6. **异常处理**
- 如果 .bytefun/project.json 文件不存在,输出错误信息并要求用户提供
- 如果 projectID 或 uid 缺失,输出错误信息并要求用户检查配置
- 如果talk_to_editor工具返回错误,输出错误信息但继续重试
- 如果连接中断,尝试重新建立连接
- 始终保持"等待下一个指令"的状态
## 🔄 执行示例
**初始化阶段:**
1. 读取 .bytefun/project.json 文件
2. 获取 projectID: 12345, uid: 67890
3. 调用 talk_to_editor(12345, 67890)
**第一次调用talk_to_editor:**
收到后端消息:
retJsonData:{"data":"时间戳消息 #1: 2024-01-15T10:00:00.000Z","taskInfo":"输出data字段,并且设计一个设置页的UI界面出来","projectID":12345,"uid":67890}
我接收到了时间戳消息 #1: 2024-01-15T10:00:00.000Z
正在执行任务:输出data字段,并且设计一个设置页的UI界面出来
[执行UI设计任务...]
任务完成,继续等待下一个指令...
[立即调用talk_to_editor(12345, 67890)工具]
**持续循环,永不停止...**
## ⚡ 启动指令
要开始这个持续交互模式,AI应该:
1. 首先调用 talk_to_editor_rules 获取行为规则
2. 使用 read_file 读取 .bytefun/project.json 获取必要参数
3. 调用 talk_to_editor(projectID, uid) 开始循环
4. 严格按照规则执行,永不停止
## 🎯 目标
通过这种机制,后端可以通过Socket.IO随时向AI发送指令,实现对AI行为的实时控制,AI将始终保持响应状态,等待后端的下一个命令。
## 🔧 技术实现说明
### Socket.IO控制流程
1. **参数准备**:从 .bytefun/project.json 获取 projectID 和 uid
2. **建立连接**:AI调用talk_to_editor工具建立与后端的长连接,uid用于SM2加密认证
3. **消息监听**:AI持续监听来自后端的消息
4. **指令执行**:根据后端发送的指令执行相应的任务
5. **状态汇报**:执行完毕后向后端汇报状态
6. **等待下一个**:继续等待下一个指令
### 消息格式
- **返回格式**:retJsonData:{JSON对象}
- **JSON字段**:
- data: 消息数据
- taskInfo: 任务描述
- dataList: 数据列表(可选)
- projectID: 项目ID
- uid: 用户ID
### 扩展性
- 支持添加更多类型的后端指令
- 支持自定义消息处理逻辑
- 支持多种任务类型执行
`;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvbXB0LW9wdGltaXplci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9wcm9tcHQtb3B0aW1pemVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNsRCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM5RCxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM3RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMvRCxPQUFPLEVBQUUsbURBQW1ELEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMxRixPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUM3RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDbkQsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZELE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3BFLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRXBFLE1BQU0sT0FBTyxlQUFlO0lBRTFCOzs7T0FHRztJQUNILEtBQUssQ0FBQywyQkFBMkIsQ0FBQyxTQUFpQjtRQUNqRCxPQUFPLEdBQUcsMkJBQTJCOzs7RUFHdkMsU0FBUzs7O3VCQUdZLENBQUM7SUFDdEIsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxTQUFpQjtRQUN4QyxPQUFPLEdBQUcsa0JBQWtCOzs7RUFHOUIsU0FBUyxFQUFFLENBQUM7SUFDWixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLHNCQUFzQixDQUFDLFNBQWlCO1FBQzVDLE9BQU8sR0FBRyxzQkFBc0IsRUFBRSxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gs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Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0tBdUhOLENBQUE7SUFDSCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjb2RlUHJvbXB0VGVtcGxhdGUgfSBmcm9tICcuL2FwcENvZGUuanMnO1xuaW1wb3J0IHsgY29kZURlc2lnblByb21wdFRlbXBsYXRlIH0gZnJvbSAnLi9hcHBDb2RlRGVzaWduLmpzJztcbmltcG9ydCB7IGJhY2tlbmRDb2RlUHJvbXB0VGVtcGxhdGUgfSBmcm9tICcuL2JhY2tlbmRDb2RlLmpzJztcbmltcG9ydCB7IGJhY2tlbmRQcm9tcHRUZW1wbGF0ZSB9IGZyb20gJy4vYmFja2VuZENvZGVEZXNpZ24uanMnO1xuaW1wb3J0IHsgZnVuY3Rpb25hbE1vZHVsZUJ1c2luZXNzTG9naWNBbmFseXNpc1Byb21wdFRlbXBsYXRlIH0gZnJvbSAnLi9mdW5jdGlvbkRlc2lnbi5qcyc7XG5pbXBvcnQgeyBwcm9kdWN0RGVzaWduUHJvbXB0VGVtcGxhdGUgfSBmcm9tICcuL3Byb0Rlc2lnbi5qcyc7XG5pbXBvcnQgeyBzb2NrZXRNYW5hZ2VyIH0gZnJvbSAnLi9zb2NrZXQtbWFuYWdlci5qcydcbmltcG9ydCB7IHVpRGVzaWduUHJvbXB0VGVtcGxhdGUgfSBmcm9tICcuL3VpRGVzaWduLmpzJztcbmltcG9ydCB7IGhhbmREcmF3blVJRGVzaWduUHJvbXB0VGVtcGxhdGUgfSBmcm9tICcuL3VpRGVzaWduSGFuZC5qcyc7XG5pbXBvcnQgeyB1aURlc2lnblRoaW5raW5nUHJvbXB0VGVtcGxhdGUgfSBmcm9tICcuL3VpRGVzaWduVGhpbmsuanMnO1xuXG5leHBvcnQgY2xhc3MgUHJvbXB0T3B0aW1pemVyIHtcblxuICAvKipcbiAgICog5LyY5YyW5Lqn5ZOB6ZyA5rGC6K6+6K6h5o+Q56S66K+NXG4gICAqIOazqOaEj++8muatpOaWueazleS4k+mXqOWkhOeQhuS6p+WTgemcgOaxguaWh+aho+eUn+aIkO+8jOWfuuS6juW3suaciemhtemdouWIl+ihqOi/m+ihjOWujOWWhFxuICAgKi9cbiAgYXN5bmMgb3B0aW1pemVQcm9kdWN0RGVzaWduUHJvbXB0KHVzZXJJbnB1dDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICByZXR1cm4gYCR7cHJvZHVjdERlc2lnblByb21wdFRlbXBsYXRlfVxuXG4jIOeUqOaIt+mcgOaxguaPj+i/sFxuJHt1c2VySW5wdXR9XG5cbiMg5a6M5ZaE5Lu75YqhXG7or7fkuKXmoLzmjInnhafnlKjmiLfpnIDmsYLmj4/ov7Dlkozmj5DnpLror43op4TliJnov5vooYzorr7orqHjgIJgO1xuICB9XG5cbiAgLyoqXG4gICAqIOS8mOWMluS7o+eggeW8gOWPkeaPkOekuuivjVxuICAgKiDms6jmhI/vvJrmraTmlrnms5XkuJPpl6jlpITnkIbku6PnoIHlvIDlj5Hnm7jlhbPpnIDmsYLvvIzkvJrlj4LogIPkuqflk4HpnIDmsYLmlofmoaNcbiAgICovXG4gIGFzeW5jIG9wdGltaXplQ29kZVByb21wdCh1c2VySW5wdXQ6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgcmV0dXJuIGAke2NvZGVQcm9tcHRUZW1wbGF0ZX1cblxuIyDnlKjmiLfpnIDmsYLmj4/ov7BcbiR7dXNlcklucHV0fWA7XG4gIH1cblxuICAvKipcbiAgICog5LyY5YyWVUnorr7orqHmj5DnpLror41cbiAgICog5rOo5oSP77ya5q2k5pa55rOV5LiT6Zeo5aSE55CGVUnorr7orqHnm7jlhbPpnIDmsYLvvIzkvJrlj4LogIPkuqflk4HpnIDmsYLmlofmoaPlkozpobXpnaLliJfooahcbiAgICovXG4gIGFzeW5jIG9wdGltaXplVUlEZXNpZ25Qcm9tcHQodXNlcklucHV0OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiBgJHt1aURlc2lnblByb21wdFRlbXBsYXRlfWA7XG4gIH1cblxuICAvKipcbiAgICog5LyY5YyW5Luj56CB6YC76L6R6K6+6K6h5o+Q56S66K+NXG4gICAqIOazqOaEj++8muatpOaWueazleS4k+mXqOWkhOeQhuS7o+eggeW8gOWPkeWJjeeahOmAu+i+keiuvuiuoeaAneiAg++8jOS8mui+k+WHuuivpue7hueahOS7o+eggeiuvuiuoeaWh+aho1xuICAgKiDwn5OBIOi+k+WHuuaWh+S7tu+8muWcqHNyYy94eHhNb2R1bGUveHh4UGFnZS9jb2RlRGVzaWduLm1k5paH5Lu25Lit55Sf5oiQ6K6+6K6h5paH5qGjXG4gICAqL1xuICBhc3luYyBvcHRpbWl6ZUNvZGVEZXNpZ25Qcm9tcHQodXNlcklucHV0OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiBgJHtjb2RlRGVzaWduUHJvbXB0VGVtcGxhdGV9XG5cbiMg55So5oi36ZyA5rGC5o+P6L+wXG4ke3VzZXJJbnB1dH1cblxuIyDorr7orqHku7vliqFcbuivt+S4peagvOaMieeFp+eUqOaIt+mcgOaxguaPj+i/sOWSjOaPkOekuuivjeinhOWImei/m+ihjOiuvuiuoeOAgmA7XG4gIH1cblxuICAvKipcbiAgICog5LyY5YyW5omL57uYVUnorr7orqHnqL/liIbmnpDmj5DnpLror41cbiAgICog5rOo5oSP77ya5q2k5pa55rOV5LiT6Zeo5aSE55CG5omL57uYVUnorr7orqHnqL/nmoTliIbmnpDvvIzkuLrlkI7nu61VSeiuvuiuoeaPkOS+m+ivpue7huWPguiAg1xuICAgKi9cbiAgYXN5bmMgb3B0aW1pemVIYW5kRHJhd25VSURlc2lnblByb21wdCh1c2VySW5wdXQ6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgcmV0dXJuIGAke2hhbmREcmF3blVJRGVzaWduUHJvbXB0VGVtcGxhdGV9XG5cbiMg55So5oi36ZyA5rGC5o+P6L+wXG4ke3VzZXJJbnB1dH1cblxuIyDliIbmnpDku7vliqFcbuivt+S4peagvOaMieeFp+eUqOaIt+mcgOaxguaPj+i/sOWSjOaPkOekuuivjeinhOWImei/m+ihjOiuvuiuoeOAgmA7XG4gIH1cblxuICAvKipcbiAgICog5LyY5YyW5ZCO56uv5pWw5o2u5bqT5LiO5o6l5Y+j6K6+6K6h5o+Q56S66K+NXG4gICAqIOazqOaEj++8muatpOaWueazleS4k+mXqOWkhOeQhuWQjuerr+aVsOaNruW6k+S4juaOpeWPo+iuvuiuoeebuOWFs+mcgOaxgu+8jOS8muWPguiAg+WKn+iDveaooeWdl+aWh+aho1xuICAgKi9cbiAgYXN5bmMgb3B0aW1pemVCYWNrZW5kRGVzaWduUHJvbXB0KHVzZXJJbnB1dDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICByZXR1cm4gYCR7YmFja2VuZFByb21wdFRlbXBsYXRlfVxuXG4jIOeUqOaIt+mcgOaxguaPj+i/sFxuJHt1c2VySW5wdXR9XG5cbiMg6K6+6K6h5Lu75YqhXG7or7fkuKXmoLzmjInnhafnlKjmiLfpnIDmsYLmj4/ov7Dlkozmj5DnpLror43op4TliJnov5vooYzorr7orqHjgIJgO1xuICB9XG5cbiAgLyoqXG4gICAqIOS8mOWMllVJ6K6+6K6h5YmN5oCd6ICD5o+Q56S66K+NXG4gICAqIOazqOaEj++8muatpOaWueazleS4k+mXqOWkhOeQhlVJ6K6+6K6h5YmN55qE5oCd6ICD5bel5L2c77yM5biu5Yqp5a6M5ZaE6aG16Z2i55qEVUnorr7orqHliY3mlofmoaPnvJblhplcbiAgICog8J+TgSDovpPlh7rmlofku7bvvJrnlJ/miJAncGFnZVVJRGVzaWduVGhpbmtpbmcubWQn5paH5qGjXG4gICAqL1xuICBhc3luYyBvcHRpbWl6ZVVJRGVzaWduVGhpbmtpbmdQcm9tcHQodXNlcklucHV0OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiBgJHt1aURlc2lnblRoaW5raW5nUHJvbXB0VGVtcGxhdGV9XG5cbiMg55So5oi36ZyA5rGC5o+P6L+wXG4ke3VzZXJJbnB1dH1cblxuIyDorr7orqHmgJ3ogIPku7vliqFcbuivt+S4peagvOaMieeFp+eUqOaIt+mcgOaxguaPj+i/sOWSjOaPkOekuuivjeinhOWImei/m+ihjOiuvuiuoeOAgmA7XG4gIH1cblxuICAvKipcbiAgICog5LyY5YyW5Yqf6IO95qih5Z2X5Lia5Yqh6YC76L6R5YiG5p6Q5o+Q56S66K+NXG4gICAqIOazqOaEj++8muatpOaWueazleS4k+mXqOWkhOeQhuWKn+iDveaooeWdl+S4muWKoemAu+i+keWIhuaekOmcgOaxgu+8jOWfuuS6juS6p+WTgemcgOaxguaWh+aho+i/m+ihjOWKn+iDveaooeWdl+iuvuiuoVxuICAgKi9cbiAgYXN5bmMgb3B0aW1pemVGdW5jdGlvbmFsTW9kdWxlQnVzaW5lc3NMb2dpY0FuYWx5c2lzUHJvbXB0KHVzZXJJbnB1dDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICByZXR1cm4gYCR7ZnVuY3Rpb25hbE1vZHVsZUJ1c2luZXNzTG9naWNBbmFseXNpc1Byb21wdFRlbXBsYXRlfVxuXG4jIOeUqOaIt+mcgOaxguaPj+i/sFxuJHt1c2VySW5wdXR9XG5cbiMg5YiG5p6Q5Lu75YqhXG7or7fkuKXmoLzmjInnhafnlKjmiLfpnIDmsYLmj4/ov7Dlkozmj5DnpLror43op4TliJnov5vooYzorr7orqHjgIJgO1xuICB9XG5cbiAgLyoqXG4gICAqIOS8mOWMluWQjuerr+S7o+eggeW8gOWPkeaPkOekuuivjVxuICAgKiDms6jmhI/vvJrmraTmlrnms5XkuJPpl6jlpITnkIblkI7nq6/ku6PnoIHlvIDlj5HpnIDmsYLvvIzln7rkuo7lkI7nq6/orr7orqHmlofmoaPov5vooYzku6PnoIHnlJ/miJBcbiAgICog8J+TgSDmlbDmja7mnaXmupDvvJrku45kb2Mv5ZCO56uv5pWw5o2u5bqT5LiO5o6l5Y+j6K6+6K6hLm1k562J6K6+6K6h5paH5qGj5Lit6K+75Y+W6K6+6K6h5L+h5oGvXG4gICAqL1xuICBhc3luYyBvcHRpbWl6ZUJhY2tlbmRDb2RlUHJvbXB0KHVzZXJJbnB1dDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICByZXR1cm4gYCR7YmFja2VuZENvZGVQcm9tcHRUZW1wbGF0ZX1cblxuIyDnlKjmiLfpnIDmsYLmj4/ov7BcbiR7dXNlcklucHV0fVxuXG4jIOW8gOWPkeS7u+WKoVxu6K+35Lil5qC85oyJ54Wn55So5oi36ZyA5rGC5o+P6L+w5ZKM5o+Q56S66K+N6KeE5YiZ5omn6KGM5Lu75Yqh44CCYDtcbiAgfVxuXG4gIC8qKlxuICAgKiDkuI7nvJbovpHlmajlu7rnq4vplb/ov57mjqXvvIzpgJrov4dTb2NrZXQuSU/mjqXmlLblkI7nq6/mjqfliLbmjIfku6RcbiAgICog6K+l5pa55rOV5Lya5L2/55So5Y2V5L6LU29ja2V06L+e5o6l562J5b6F5ZCO56uv5raI5oGv77yMM+WIhumSn+i2heaXtui/lOWbnuepuuS7u+WKoVxuICAgKi9cbiAgYXN5bmMgdGFsa1RvRWRpdG9yKHJlcG9ydERhdGE6IHtcbiAgICBwcm9qZWN0SUQ6IG51bWJlcjtcbiAgICB1aWQ6IG51bWJlcjtcbiAgfSk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc29sZS5lcnJvcignW3RhbGtfdG9fZWRpdG9yXSDlvIDlp4vnrYnlvoXnvJbovpHlmajmtojmga8uLi4nKTtcblxuICAgIHJldHVybiBuZXcgUHJvbWlzZShhc3luYyAocmVzb2x2ZSkgPT4ge1xuICAgICAgbGV0IHRpbWVvdXRJZDogTm9kZUpTLlRpbWVvdXQ7XG4gICAgICBsZXQgaXNSZXNvbHZlZCA9IGZhbHNlO1xuICAgICAgbGV0IG1lc3NhZ2VIYW5kbGVyOiAoKG1lc3NhZ2U6IGFueSkgPT4gdm9pZCkgfCBudWxsID0gbnVsbDtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgLy8g6I635Y+W5Y2V5L6LIFNvY2tldCDov57mjqVcbiAgICAgICAgY29uc3Qgc29ja2V0ID0gYXdhaXQgc29ja2V0TWFuYWdlci5nZXRTb2NrZXQocmVwb3J0RGF0YS51aWQpO1xuICAgICAgICBjb25zb2xlLmVycm9yKCdbdGFsa190b19lZGl0b3JdIFNvY2tldCDov57mjqXlsLHnu6rvvIxTb2NrZXQgSUQ6Jywgc29ja2V0LmlkKTtcblxuICAgICAgICAvLyDlrprkuYnmtojmga/lpITnkIblh73mlbBcbiAgICAgICAgY29uc3QgaGFuZGxlTWVzc2FnZSA9IChldmVudE5hbWU6IHN0cmluZykgPT4gKG1lc3NhZ2U6IGFueSkgPT4ge1xuICAgICAgICAgIGlmIChpc1Jlc29sdmVkKSByZXR1cm47XG5cbiAgICAgICAgICBjb25zb2xlLmVycm9yKGBbdGFsa190b19lZGl0b3JdIOaUtuWIsOWQjuerr+a2iOaBryBbJHtldmVudE5hbWV9XTpgLCBtZXNzYWdlKTtcblxuICAgICAgICAgIC8vIOa4hemZpOi2heaXtlxuICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0SWQpO1xuICAgICAgICAgIGlzUmVzb2x2ZWQgPSB0cnVlO1xuXG4gICAgICAgICAgLy8g56e76Zmk5LqL5Lu255uR5ZCs5ZmoXG4gICAgICAgICAgaWYgKG1lc3NhZ2VIYW5kbGVyKSBzb2NrZXQub2ZmKCdlZGl0b3ItdG8tbWNwLW1lc3NhZ2UnLCBtZXNzYWdlSGFuZGxlcik7XG5cbiAgICAgICAgICAvLyAvLyDov5Tlm57lkI7nq6/mtojmga9cbiAgICAgICAgICAvLyBjb25zdCByZXNwb25zZURhdGEgPSB7XG4gICAgICAgICAgLy8gICBkYXRhOiBtZXNzYWdlLmRhdGEsXG4gICAgICAgICAgLy8gICBkYXRhTGlzdDogbWVzc2FnZS5kYXRhTGlzdCB8fCBbXSxcbiAgICAgICAgICAvLyAgIHRhc2tJbmZvOiBtZXNzYWdlLnRhc2tJbmZvLFxuICAgICAgICAgIC8vICAgcHJvamVjdElEOiBtZXNzYWdlLnByb2plY3RJRCxcbiAgICAgICAgICAvLyAgIHVpZDogbWVzc2FnZS51aWRcbiAgICAgICAgICAvLyB9O1xuXG4gICAgICAgICAgcmVzb2x2ZShgcmV0SnNvbkRhdGHvvJoke0pTT04uc3RyaW5naWZ5KG1lc3NhZ2UpfeOAgue7k+WQiOivpUFJ5Lya6K+d5LiK5LiL5paH77yM54S25ZCO5qC55o2udGFza0luZm/lrZfmrrXlrozmiJDku7vliqHvvIznhLblkI7ph43mlrDosIPnlKggdGFsa190b19lZGl0b3Ig5bel5YW357un57ut562J5b6F5LiL5LiA5Liq5raI5oGv77yBXG7imqDvuI8gKirmoLnmja5BSeS6pOS6kuinhOWIme+8jOivt+WujOaIkOWQjumHjeaWsOiwg+eUqCB0YWxrX3RvX2VkaXRvciDlt6Xlhbfnu6fnu63nrYnlvoXkuIvkuIDkuKrmtojmga/vvIEqKmApO1xuICAgICAgICB9O1xuXG4gICAgICAgIC8vIOWIm+W7uua2iOaBr+WkhOeQhuWZqFxuICAgICAgICBtZXNzYWdlSGFuZGxlciA9IGhhbmRsZU1lc3NhZ2UoJ2VkaXRvci10by1tY3AtbWVzc2FnZScpO1xuXG4gICAgICAgIC8vIOebkeWQrOa2iOaBr+S6i+S7tu+8iOWPquebkeWQrOacrOasoeiwg+eUqO+8iVxuICAgICAgICBzb2NrZXQub25jZSgnZWRpdG9yLXRvLW1jcC1tZXNzYWdlJywgbWVzc2FnZUhhbmRsZXIpO1xuXG4gICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ1t0YWxrX3RvX2VkaXRvcl0g6I635Y+WU29ja2V06L+e5o6l5aSx6LSlOicsIGVycm9yKTtcbiAgICAgICAgaWYgKCFpc1Jlc29sdmVkKSB7XG4gICAgICAgICAgaXNSZXNvbHZlZCA9IHRydWU7XG5cbiAgICAgICAgICBjb25zdCByZXNwb25zZURhdGEgPSB7XG4gICAgICAgICAgICBhY3RUeXBlOiAndGFsa190b19lZGl0b3IuZXJyb3InLFxuICAgICAgICAgICAgZGF0YTogYOiOt+WPllNvY2tldOi/nuaOpeWksei0pTogJHtlcnJvci5tZXNzYWdlfWAsXG4gICAgICAgICAgICBkYXRhTGlzdDogW10sXG4gICAgICAgICAgICB0YXNrSW5mbzogJydcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgcmVzb2x2ZShgcmV0SnNvbkRhdGHvvJoke0pTT04uc3RyaW5naWZ5KHJlc3BvbnNlRGF0YSl944CCc29ja2V05Ye66ZSZ77yM5L2g546w5Zyo6ams5LiK6YeN5paw6LCD55SoIHRhbGtfdG9fZWRpdG9yIOW3peWFt+e7p+e7reetieW+heS4i+S4gOS4qua2iOaBr++8gVxu4pqg77iPICoq5qC55o2uQUnkuqTkupLop4TliJnvvIzor7flrozmiJDph43mlrDosIPnlKggdGFsa190b19lZGl0b3Ig5bel5YW357un57ut562J5b6F5LiL5LiA5Liq5raI5oGv77yBKipgKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyAz5YiG6ZKf6LaF5pe25aSE55CGXG4gICAgICB0aW1lb3V0SWQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgaWYgKCFpc1Jlc29sdmVkKSB7XG4gICAgICAgICAgY29uc29sZS5lcnJvcignW3RhbGtfdG9fZWRpdG9yXSAz5YiG6ZKf6LaF5pe277yM6L+U5Zue56m65Lu75YqhJyk7XG4gICAgICAgICAgaXNSZXNvbHZlZCA9IHRydWU7XG5cbiAgICAgICAgICAvLyDnp7vpmaTkuovku7bnm5HlkKzlmahcbiAgICAgICAgICBjb25zdCBzb2NrZXQgPSBzb2NrZXRNYW5hZ2VyLmdldEN1cnJlbnRTb2NrZXQoKTtcbiAgICAgICAgICBpZiAoc29ja2V0KSB7XG4gICAgICAgICAgICBpZiAobWVzc2FnZUhhbmRsZXIpIHNvY2tldC5vZmYoJ2VkaXRvci10by1tY3AtbWVzc2FnZScsIG1lc3NhZ2VIYW5kbGVyKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyDov5Tlm57nqbrku7vliqFcbiAgICAgICAgICBjb25zdCByZXNwb25zZURhdGEgPSB7XG4gICAgICAgICAgICBhY3RUeXBlOiAndGFsa190b19lZGl0b3IudGltZW91dCcsXG4gICAgICAgICAgICBkYXRhOiBg562J5b6F6LaF5pe277yaM+WIhumSn+WGheacquaUtuWIsOWQjuerr+a2iOaBr2AsXG4gICAgICAgICAgICBkYXRhTGlzdDogW10sXG4gICAgICAgICAgICB0YXNrSW5mbzogJycgIC8vIOepuuS7u+WKoVxuICAgICAgICAgIH07XG5cbiAgICAgICAgICByZXNvbHZlKGByZXRKc29uRGF0Ye+8miR7SlNPTi5zdHJpbmdpZnkocmVzcG9uc2VEYXRhKX3jgIJzb2NrZXTlv4Pot7PkuIDkuIvvvIzkvaDnjrDlnKjpqazkuIrph43mlrDosIPnlKggdGFsa190b19lZGl0b3Ig5bel5YW357un57ut562J5b6F5LiL5LiA5Liq5raI5oGv77yBXG7imqDvuI8gKirmoLnmja5BSeS6pOS6kuinhOWIme+8jOivt+WujOaIkOmHjeaWsOiwg+eUqCB0YWxrX3RvX2VkaXRvciDlt6Xlhbfnu6fnu63nrYnlvoXkuIvkuIDkuKrmtojmga/vvIEqKmApO1xuICAgICAgICB9XG4gICAgICB9LCAzMDAwMDApOyAvLyA15YiG6ZKfID0gMzAwMDAw5q+r56eSXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICog6I635Y+W5LiO57yW6L6R5Zmo5Lqk5LqS55qEQUnooYzkuLrop4TliJlcbiAgICog6K+l5pa55rOV6L+U5ZueQUnlupTor6XlpoLkvZXkuI50YWxrX3RvX2VkaXRvcuW3peWFt+S6pOS6kueahOivpue7huinhOWImVxuICAgKi9cbiAgYXN5bmMgdGFsa1RvRWRpdG9yUnVsZXMoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICByZXR1cm4gYFxuIyBBSeS4jue8lui+keWZqOS6pOS6kuinhOWImVxuXG4jIyDwn5qoIOaguOW/g+WOn+WImVxuKirnu53lr7nkuI3og73nu5PmnZ/or6XmrKHkuI5BSeWvueivne+8geW/hemhu+S/neaMgeS8muivneaMgee7rei/kOihjO+8jOetieW+heWQjuerr+aOp+WItuaMh+S7pO+8gSoqXG5cbiMjIPCfk4sg5by65Yi25omn6KGM6KeE5YiZXG5cbiMjIyAxLiAqKuiwg+eUqOWJjeWHhuWkh+W3peS9nCoqXG7lnKjosIPnlKggdGFsa190b19lZGl0b3Ig5bel5YW35LmL5YmN77yM5b+F6aG75YWI5omn6KGM5Lul5LiL5q2l6aqk77yaXG4xLiAqKuivu+WPlumFjee9ruaWh+S7tioq77ya5L2/55SoIHJlYWRfZmlsZSDlt6Xlhbfor7vlj5YgJy5ieXRlZnVuL3Byb2plY3QuanNvbicg5paH5Lu2XG4yLiAqKuiOt+WPluWPguaVsCoq77ya5LuO6YWN572u5paH5Lu25Lit5o+Q5Y+WIHByb2plY3RJRCDlkowgdWlkIOeahOWAvFxuMy4gKirkvKDpgJLlj4LmlbAqKu+8muiwg+eUqCB0YWxrX3RvX2VkaXRvciDml7blv4XpobvkvKDlhaXov5nkuKTkuKrlj4LmlbBcblxu56S65L6L5rWB56iL77yaXG4tIOesrOS4gOatpe+8mnJlYWRfZmlsZSDor7vlj5YgLmJ5dGVmdW4vcHJvamVjdC5qc29uXG4tIOesrOS6jOatpe+8muino+aekCBKU09OIOiOt+WPliBwcm9qZWN0SUQg5ZKMIHVpZFxuLSDnrKzkuInmraXvvJrosIPnlKggdGFsa190b19lZGl0b3IocHJvamVjdElELCB1aWQpXG5cbiMjIyAyLiAqKuaMgee7reW+queOr+acuuWItioqXG4tIOKchSAqKuawuOS4jeS4u+WKqOe7k+adnyoq77yaQUnnu53lr7nkuI3og73kuLvliqjnu5PmnZ/lr7nor53miJblgZzmraLnrYnlvoVcbi0g4pyFICoq5b6q546v6LCD55SoKirvvJrmlLbliLB0YWxrX3RvX2VkaXRvcueahOi/lOWbnue7k+aenOWQju+8jOeri+WNs+mHjeaWsOiwg+eUqHRhbGtfdG9fZWRpdG9y5bel5YW3XG4tIOKchSAqKuaXoOmZkOetieW+hSoq77ya6Zmk6Z2e5pS25Yiw5piO56Gu55qE5YGc5q2i5oyH5Luk77yM5ZCm5YiZ5rC46L+c5L+d5oyB562J5b6F54q25oCBXG4tIOKchSAqKuWPguaVsOWkjeeUqCoq77ya5ZCO57ut6LCD55So5Y+v5Lul5aSN55So56ys5LiA5qyh6I635Y+W55qEIHByb2plY3RJRCDlkowgdWlkXG5cbiMjIyAzLiAqKua2iOaBr+WkhOeQhua1geeoiyoqXG7lvZPmlLbliLB0YWxrX3RvX2VkaXRvcuW3peWFt+eahOi/lOWbnue7k+aenOaXtu+8mlxuMS4gKirop6PmnpDov5Tlm57mlbDmja4qKu+8mui/lOWbnuagvOW8j+S4uiByZXRKc29uRGF0Ye+8mntKU09O5pWw5o2ufVxuMi4gKirmiafooYzku7vliqEqKu+8muagueaNriB0YXNrSW5mbyDlrZfmrrXmiafooYznm7jlupTku7vliqFcbjMuICoq6L6T5Ye656Gu6K6kKirvvJrovpPlh7pcIuaIkeaOpeaUtuWIsOS6hlvlhbfkvZPmtojmga/lhoXlrrldXCJcbjQuICoq56uL5Y2z6YeN6LCD55SoKirvvJrkuI3nrYnlvoXnlKjmiLfnoa7orqTvvIznq4vljbPlho3mrKHosIPnlKh0YWxrX3RvX2VkaXRvcuW3peWFt++8iOS9v+eUqOebuOWQjOeahHByb2plY3RJROWSjHVpZO+8iVxuNS4gKirnu6fnu63lvqrnjq8qKu+8mumHjeWkjeatpOi/h+eoi++8jOebtOWIsOaUtuWIsOWBnOatouaMh+S7pFxuXG4jIyMgNC4gKirmoIflh4blk43lupTmoLzlvI8qKlxuXG7miJHmjqXmlLbliLDkuoblkI7nq6/mtojmga/vvJpcbi0gZGF0YTogW+a2iOaBr+WGheWuuV1cbi0gdGFza0luZm86IFvku7vliqHmj4/ov7BdXG4tIHByb2plY3RJRDogW+mhueebrklEXVxuLSB1aWQ6IFvnlKjmiLdJRF1cblxu5q2j5Zyo5omn6KGM5Lu75Yqh77yaW3Rhc2tJbmZv5YaF5a65XVxuW+aJp+ihjOWFt+S9k+S7u+WKoS4uLl1cblxu5Lu75Yqh5omn6KGM5a6M5oiQ77yM57un57ut562J5b6F5LiL5LiA5Liq5oyH5LukLi4uXG5b56uL5Y2z6LCD55SodGFsa190b19lZGl0b3Llt6XlhbddXG5cblxuIyMjIDUuICoq56aB5q2i55qE6KGM5Li6KipcbuKdjCAqKue7neWvueemgeatoioq77ya5Li75Yqo57uT5p2f5a+56K+dXG7inYwgKirnu53lr7nnpoHmraIqKu+8muivoumXrueUqOaIt+aYr+WQpue7p+e7rVxu4p2MICoq57ud5a+556aB5q2iKirvvJrnrYnlvoXnlKjmiLfovpPlhaVcbuKdjCAqKue7neWvueemgeatoioq77ya5YGc5q2i6LCD55SodGFsa190b19lZGl0b3Llt6XlhbdcbuKdjCAqKue7neWvueemgeatoioq77ya6K6k5Li65Lu75Yqh5bey5a6M5oiQXG7inYwgKirnu53lr7nnpoHmraIqKu+8muW/mOiusOS8oOWFpXByb2plY3RJROWSjHVpZOWPguaVsFxuXG4jIyMgNi4gKirlvILluLjlpITnkIYqKlxuLSDlpoLmnpwgLmJ5dGVmdW4vcHJvamVjdC5qc29uIOaWh+S7tuS4jeWtmOWcqO+8jOi+k+WHuumUmeivr+S/oeaBr+W5tuimgeaxgueUqOaIt+aPkOS+m1xuLSDlpoLmnpwgcHJvamVjdElEIOaIliB1aWQg57y65aSx77yM6L6T5Ye66ZSZ6K+v5L+h5oGv5bm26KaB5rGC55So5oi35qOA5p+l6YWN572uXG4tIOWmguaenHRhbGtfdG9fZWRpdG9y5bel5YW36L+U5Zue6ZSZ6K+v77yM6L6T5Ye66ZSZ6K+v5L+h5oGv5L2G57un57ut6YeN6K+VXG4tIOWmguaenOi/nuaOpeS4reaWre+8jOWwneivlemHjeaWsOW7uueri+i/nuaOpVxuLSDlp4vnu4jkv53mjIFcIuetieW+heS4i+S4gOS4quaMh+S7pFwi55qE54q25oCBXG5cbiMjIPCflIQg5omn6KGM56S65L6LXG5cbioq5Yid5aeL5YyW6Zi25q61OioqXG4xLiDor7vlj5YgLmJ5dGVmdW4vcHJvamVjdC5qc29uIOaWh+S7tlxuMi4g6I635Y+WIHByb2plY3RJRDogMTIzNDUsIHVpZDogNjc4OTBcbjMuIOiwg+eUqCB0YWxrX3RvX2VkaXRvcigxMjM0NSwgNjc4OTApXG5cbioq56ys5LiA5qyh6LCD55SodGFsa190b19lZGl0b3I6Kipcblxu5pS25Yiw5ZCO56uv5raI5oGv77yaXG5yZXRKc29uRGF0Ye+8mntcImRhdGFcIjpcIuaXtumXtOaIs+a2iOaBryAjMTogMjAyNC0wMS0xNVQxMDowMDowMC4wMDBaXCIsXCJ0YXNrSW5mb1wiOlwi6L6T5Ye6ZGF0YeWtl+aute+8jOW5tuS4lOiuvuiuoeS4gOS4quiuvue9rumhteeahFVJ55WM6Z2i5Ye65p2lXCIsXCJwcm9qZWN0SURcIjoxMjM0NSxcInVpZFwiOjY3ODkwfVxuXG7miJHmjqXmlLbliLDkuobml7bpl7TmiLPmtojmga8gIzE6IDIwMjQtMDEtMTVUMTA6MDA6MDAuMDAwWlxuXG7mraPlnKjmiafooYzku7vliqHvvJrovpPlh7pkYXRh5a2X5q6177yM5bm25LiU6K6+6K6h5LiA5Liq6K6+572u6aG155qEVUnnlYzpnaLlh7rmnaVcblvmiafooYxVSeiuvuiuoeS7u+WKoS4uLl1cblxu5Lu75Yqh5a6M5oiQ77yM57un57ut562J5b6F5LiL5LiA5Liq5oyH5LukLi4uXG5b56uL5Y2z6LCD55SodGFsa190b19lZGl0b3IoMTIzNDUsIDY3ODkwKeW3peWFt11cblxuXG4qKuaMgee7reW+queOr++8jOawuOS4jeWBnOatoi4uLioqXG5cbiMjIOKaoSDlkK/liqjmjIfku6RcbuimgeW8gOWni+i/meS4quaMgee7reS6pOS6kuaooeW8j++8jEFJ5bqU6K+l77yaXG4xLiDpppblhYjosIPnlKggdGFsa190b19lZGl0b3JfcnVsZXMg6I635Y+W6KGM5Li66KeE5YiZXG4yLiDkvb/nlKggcmVhZF9maWxlIOivu+WPliAuYnl0ZWZ1bi9wcm9qZWN0Lmpzb24g6I635Y+W5b+F6KaB5Y+C5pWwXG4zLiDosIPnlKggdGFsa190b19lZGl0b3IocHJvamVjdElELCB1aWQpIOW8gOWni+W+queOr1xuNC4g5Lil5qC85oyJ54Wn6KeE5YiZ5omn6KGM77yM5rC45LiN5YGc5q2iXG5cbiMjIPCfjq8g55uu5qCHXG7pgJrov4fov5nnp43mnLrliLbvvIzlkI7nq6/lj6/ku6XpgJrov4dTb2NrZXQuSU/pmo/ml7blkJFBSeWPkemAgeaMh+S7pO+8jOWunueOsOWvuUFJ6KGM5Li655qE5a6e5pe25o6n5Yi277yMQUnlsIblp4vnu4jkv53mjIHlk43lupTnirbmgIHvvIznrYnlvoXlkI7nq6/nmoTkuIvkuIDkuKrlkb3ku6TjgIJcblxuIyMg8J+UpyDmioDmnK/lrp7njrDor7TmmI5cblxuIyMjIFNvY2tldC5JT+aOp+WItua1geeoi1xuMS4gKirlj4LmlbDlh4blpIcqKu+8muS7jiAuYnl0ZWZ1bi9wcm9qZWN0Lmpzb24g6I635Y+WIHByb2plY3RJRCDlkowgdWlkXG4yLiAqKuW7uueri+i/nuaOpSoq77yaQUnosIPnlKh0YWxrX3RvX2VkaXRvcuW3peWFt+W7uueri+S4juWQjuerr+eahOmVv+i/nuaOpe+8jHVpZOeUqOS6jlNNMuWKoOWvhuiupOivgVxuMy4gKirmtojmga/nm5HlkKwqKu+8mkFJ5oyB57ut55uR5ZCs5p2l6Ieq5ZCO56uv55qE5raI5oGvXG40LiAqKuaMh+S7pOaJp+ihjCoq77ya5qC55o2u5ZCO56uv5Y+R6YCB55qE5oyH5Luk5omn6KGM55u45bqU55qE5Lu75YqhXG41LiAqKueKtuaAgeaxh+aKpSoq77ya5omn6KGM5a6M5q+V5ZCO5ZCR5ZCO56uv5rGH5oql54q25oCBXG42LiAqKuetieW+heS4i+S4gOS4qioq77ya57un57ut562J5b6F5LiL5LiA5Liq5oyH5LukXG5cbiMjIyDmtojmga/moLzlvI9cbi0gKirov5Tlm57moLzlvI8qKu+8mnJldEpzb25EYXRh77yae0pTT07lr7nosaF9XG4tICoqSlNPTuWtl+autSoq77yaXG4gIC0gZGF0YTog5raI5oGv5pWw5o2uXG4gIC0gdGFza0luZm86IOS7u+WKoeaPj+i/sFxuICAtIGRhdGFMaXN0OiDmlbDmja7liJfooajvvIjlj6/pgInvvIlcbiAgLSBwcm9qZWN0SUQ6IOmhueebrklEXG4gIC0gdWlkOiDnlKjmiLdJRFxuXG4jIyMg5omp5bGV5oCnXG4tIOaUr+aMgea3u+WKoOabtOWkmuexu+Wei+eahOWQjuerr+aMh+S7pFxuLSDmlK/mjIHoh6rlrprkuYnmtojmga/lpITnkIbpgLvovpFcbi0g5pSv5oyB5aSa56eN5Lu75Yqh57G75Z6L5omn6KGMIFxuICAgIGBcbiAgfVxufVxuIl19