fmode-ng 0.0.39 → 0.0.41
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/esm2022/fmode-ng.mjs +5 -10
- package/esm2022/lib/aigc/agent/agent.prompt.mjs +122 -10
- package/esm2022/lib/aigc/agent/index.mjs +2 -10
- package/esm2022/lib/aigc/avatar/avatar.module.mjs +45 -10
- package/esm2022/lib/aigc/avatar/comp-avatar-particle/avatar.role.mjs +2 -10
- package/esm2022/lib/aigc/avatar/comp-avatar-particle/comp-avatar-particle.component.mjs +315 -10
- package/esm2022/lib/aigc/avatar/comp-avatar-particle/index.mjs +3 -10
- package/esm2022/lib/aigc/avatar/comp-avatar-particle/role-points.class.mjs +57 -10
- package/esm2022/lib/aigc/avatar/comp-avatar-role-image/comp-avatar-role-image.component.mjs +97 -10
- package/esm2022/lib/aigc/avatar/comp-avatar-role-video/comp-avatar-role-video.component.mjs +104 -10
- package/esm2022/lib/aigc/avatar/comp-avatar-talk/comp-avatar-talk.component.mjs +111 -10
- package/esm2022/lib/aigc/avatar/index.mjs +8 -10
- package/esm2022/lib/aigc/avatar/interface-avatar-role.mjs +2 -10
- package/esm2022/lib/aigc/avatar/modal-chat-voice-input/modal-chat-voice-input.component.mjs +166 -8
- package/esm2022/lib/aigc/chat/chat-header-area/comp-header-area.component.mjs +36 -10
- package/esm2022/lib/aigc/chat/chat-header-area/index.mjs +2 -10
- package/esm2022/lib/aigc/chat/chat-list/chat-list.component.mjs +141 -8
- package/esm2022/lib/aigc/chat/chat-list/index.mjs +2 -10
- package/esm2022/lib/aigc/chat/chat-message-area/comp-message-area.component.mjs +40 -10
- package/esm2022/lib/aigc/chat/chat-message-area/index.mjs +2 -10
- package/esm2022/lib/aigc/chat/chat-message-card/comp-message-card.component.mjs +92 -10
- package/esm2022/lib/aigc/chat/chat-message-card/index.mjs +2 -10
- package/esm2022/lib/aigc/chat/chat-modal-input/index.mjs +2 -10
- package/esm2022/lib/aigc/chat/chat-modal-input/modal-audio-message/modal-audio-message.component.mjs +207 -8
- package/esm2022/lib/aigc/chat/chat-modal-input/modal-input.component.mjs +236 -10
- package/esm2022/lib/aigc/chat/chat-panel/chat-panel.component.mjs +137 -10
- package/esm2022/lib/aigc/chat/comp-role-prompt/comp-role-prompt.component.mjs +69 -10
- package/esm2022/lib/aigc/chat/comp-role-prompt/index.mjs +2 -10
- package/esm2022/lib/aigc/chat/index.mjs +8 -10
- package/esm2022/lib/aigc/comp-markdown-preview/clipboard.service.mjs +82 -10
- package/esm2022/lib/aigc/comp-markdown-preview/markdown-parse.mjs +269 -8
- package/esm2022/lib/aigc/comp-markdown-preview/markdown-preview.component.mjs +51 -10
- package/esm2022/lib/aigc/comp-markdown-preview/markdown-preview.module.mjs +24 -10
- package/esm2022/lib/aigc/comp-markdown-preview/plugins/md-mathjax/index.mjs +94 -10
- package/esm2022/lib/aigc/index.mjs +13 -10
- package/esm2022/lib/aigc/service-fmai/fmai.service.mjs +21 -10
- package/esm2022/lib/aigc/service-fmai/service-chat/chat-class.mjs +736 -8
- package/esm2022/lib/aigc/service-fmai/service-chat/chat.service.mjs +181 -8
- package/esm2022/lib/aigc/service-fmai/service-chat/index.mjs +7 -10
- package/esm2022/lib/aigc/service-fmai/service-chat/mask-list.mjs +194 -9
- package/esm2022/lib/aigc/service-fmai/service-chat/pipes/chat-content.pipe.mjs +27 -10
- package/esm2022/lib/aigc/service-fmai/service-chat/pipes/hidexml.pipe.mjs +27 -10
- package/esm2022/lib/aigc/service-fmai/service-chat/utilnow.pipe.mjs +68 -10
- package/esm2022/lib/aigc/service-fmai/service-imagine/imagine.service.mjs +229 -8
- package/esm2022/lib/aigc/service-fmai/service-imagine/index.mjs +2 -10
- package/esm2022/lib/aigc/voice/audio.player.mjs +52 -10
- package/esm2022/lib/aigc/voice/class-asr.mjs +79 -8
- package/esm2022/lib/aigc/voice/fmode-voice.service.mjs +501 -8
- package/esm2022/lib/aigc/voice/index.mjs +3 -10
- package/esm2022/lib/aigc/voice/lib/pcm2wav.mjs +38 -10
- package/esm2022/lib/aigc/voice/lib/resample.mjs +34 -10
- package/esm2022/lib/aigc/voice/tts/fmode-tts-class.mjs +233 -8
- package/esm2022/lib/aigc/voice/tts/index.mjs +2 -10
- package/esm2022/lib/map/comp-poi-picker/comp-poi-picker.component.mjs +190 -10
- package/esm2022/lib/map/comp-poi-picker/comp-poi-picker.module.mjs +33 -10
- package/esm2022/lib/map/index.mjs +4 -10
- package/esm2022/lib/map/map.module.mjs +61 -10
- package/esm2022/lib/map/page-loca-scatter/page-loca-scatter.component.mjs +110 -10
- package/esm2022/lib/map/page-map.start/page-map.start.component.mjs +98 -8
- package/esm2022/lib/map/page-plan-route/page-plan-route.component.mjs +100 -8
- package/esm2022/lib/nova-cloud/index.mjs +2 -10
- package/esm2022/lib/nova-cloud/nova-cloud.service.mjs +148 -10
- package/esm2022/lib/platform/cross.service.mjs +63 -10
- package/esm2022/lib/platform/index.mjs +2 -10
- package/esm2022/lib/social/index.mjs +2 -10
- package/esm2022/lib/social/wechat/wechat-jssdk.service.mjs +236 -8
- package/esm2022/lib/storage/comp-hwobs-manager/hwobs-manager.component.mjs +59 -10
- package/esm2022/lib/storage/index.mjs +5 -10
- package/esm2022/lib/storage/service-hwobs/hwobs.service.mjs +130 -8
- package/esm2022/lib/storage/service-upload/index.mjs +2 -10
- package/esm2022/lib/storage/service-upload/nova-upload.service.mjs +462 -8
- package/esm2022/lib/storage/service-upload/util-file-md5.mjs +28 -10
- package/esm2022/lib/storage/storage.module.mjs +41 -10
- package/esm2022/lib/user/account/account.service.mjs +221 -10
- package/esm2022/lib/user/captcha/captcha.component.mjs +135 -10
- package/esm2022/lib/user/comp-user-avatar/comp-user-avatar.component.mjs +62 -10
- package/esm2022/lib/user/index.mjs +17 -10
- package/esm2022/lib/user/login/auth.guard.mjs +28 -10
- package/esm2022/lib/user/login/auth.service.mjs +373 -8
- package/esm2022/lib/user/login/login.component.mjs +913 -10
- package/esm2022/lib/user/modal-user-login/modal-user-login.component.mjs +273 -10
- package/esm2022/lib/user/profile/auth-profile.guard.mjs +27 -10
- package/esm2022/lib/user/profile/auth-profile.service.mjs +122 -10
- package/esm2022/lib/user/profile/profile-bind/profile-bind.component.mjs +115 -10
- package/esm2022/lib/user/profile/profile.module.mjs +57 -10
- package/esm2022/lib/user/staff/index.mjs +4 -10
- package/esm2022/lib/user/staff/staff.guard.mjs +26 -10
- package/esm2022/lib/user/staff/staff.module.mjs +18 -10
- package/esm2022/lib/user/staff/staff.service.mjs +85 -10
- package/esm2022/lib/user/user-name.pipe.mjs +29 -10
- package/esm2022/lib/user/user.module.mjs +106 -10
- package/esm2022/lib/video/fm-video/fm-video.component.mjs +67 -10
- package/esm2022/lib/video/index.mjs +2 -10
- package/esm2022/public-api.mjs +13 -10
- package/fesm2022/fmode-ng.mjs +8895 -7
- package/fesm2022/fmode-ng.mjs.map +1 -1
- package/lib/user/login/auth.service.d.ts +18 -3
- package/lib/user/modal-user-login/modal-user-login.component.d.ts +4 -2
- package/package.json +1 -1
- package/LICENSE.md +0 -8
|
@@ -1,10 +1,738 @@
|
|
|
1
|
-
|
|
1
|
+
import { bufferTime, concatMap, Observable, delay, finalize } from "rxjs";
|
|
2
|
+
// var bufferTime:any, concatMap:any, Observable:any, Observer:any,delay:any, finalize:any,Observer:any
|
|
3
|
+
import Parse from "parse";
|
|
4
|
+
import { AgentPrompt } from "../../agent";
|
|
5
|
+
import { FmodeTTS } from "../../voice/tts";
|
|
6
|
+
import { PromptTemplate } from "@langchain/core/prompts";
|
|
7
|
+
// var Parse:any = {}
|
|
8
|
+
// const API_BASE:string = "http://127.0.0.1:7337/api/apig/aigc/gpt"
|
|
9
|
+
const API_BASE = "https://server.fmode.cn/api/apig/aigc/gpt";
|
|
10
|
+
// const API_BASE:string = "https://test.fmode.cn/api/apig/aigc/gpt"
|
|
11
|
+
const agentPrompt = new AgentPrompt();
|
|
12
|
+
const PromptTplTalkSSMLOutputCode = "talk-ssml-output-tpl";
|
|
13
|
+
const PromptTplTalkTextSSMLCode = "talk-text-ssml-tpl";
|
|
14
|
+
export function getMessageContentText(content) {
|
|
15
|
+
let text = "";
|
|
16
|
+
if (typeof content == "string")
|
|
17
|
+
text = content;
|
|
18
|
+
if (typeof content == "object")
|
|
19
|
+
text = content?.find(item => item?.text)?.text || "";
|
|
20
|
+
return text;
|
|
21
|
+
}
|
|
22
|
+
export function getMessageImageUrl(content) {
|
|
23
|
+
if (typeof content == "object")
|
|
24
|
+
return content?.find(item => item?.image_url)?.image_url?.url || "";
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* FmodeChat 聊天对话类
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
export class FmodeChat {
|
|
32
|
+
showAvatar() {
|
|
33
|
+
this.avatarConfig = this.role?.get("avatarConfig");
|
|
34
|
+
if (this.avatarConfig) {
|
|
35
|
+
this.isAvatarShow = true;
|
|
36
|
+
if (this.avatarConfig?.image) {
|
|
37
|
+
this.avatarConfig.image.waiting = this.avatarConfig.image.waiting || this.role?.get("thumb") || this.role?.get("avatar");
|
|
38
|
+
this.avatarMode = "image";
|
|
39
|
+
}
|
|
40
|
+
if (this.avatarConfig?.video) {
|
|
41
|
+
this.avatarConfig.video.waiting = this.avatarConfig.video.waiting;
|
|
42
|
+
this.avatarMode = "video";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
constructor(sessionId, role, chatSession, chatServ, navCtrl, ncloud, uploadServ) {
|
|
47
|
+
this.ChatSession = Parse.Object.extend("ChatSession");
|
|
48
|
+
this.messageList = [{ role: "system", content: "系统提示:AI仅供参考" }];
|
|
49
|
+
this.latestAIResponse = ``;
|
|
50
|
+
this.userInput = ``;
|
|
51
|
+
this.userImage = ``;
|
|
52
|
+
this.isDirect = false;
|
|
53
|
+
/**
|
|
54
|
+
* 虚拟形象展示状态
|
|
55
|
+
*/
|
|
56
|
+
this.isAvatarShow = false;
|
|
57
|
+
this.avatarMode = "";
|
|
58
|
+
/**
|
|
59
|
+
* 预置提示词弹窗是否展示
|
|
60
|
+
*/
|
|
61
|
+
this.isPromptModalOpen = false;
|
|
62
|
+
this.isPromptMessageAreaShow = true;
|
|
63
|
+
this.promptList = [];
|
|
64
|
+
this.leftButtons = [
|
|
65
|
+
// 提示 当角色配置预设提示词时 显示
|
|
66
|
+
{ title: "灵感", icon: "color-wand-outline", onClick: () => {
|
|
67
|
+
this.isPromptModalOpen = true;
|
|
68
|
+
}, show: () => {
|
|
69
|
+
return this?.promptList?.length;
|
|
70
|
+
} },
|
|
71
|
+
{ title: "角色", icon: "people-outline", onClick: () => {
|
|
72
|
+
this.navCtrl?.navigateRoot("/chat/pro/mask");
|
|
73
|
+
}, show: () => { return true; } },
|
|
74
|
+
{ title: "呼叫", icon: "call-outline", onClick: () => {
|
|
75
|
+
this.chatServ?.callRole(this.role);
|
|
76
|
+
}, show: () => {
|
|
77
|
+
return this?.role?.get('voiceConfig');
|
|
78
|
+
} },
|
|
79
|
+
];
|
|
80
|
+
/**
|
|
81
|
+
* 是否开启语音消息模式(单次)
|
|
82
|
+
*/
|
|
83
|
+
this.isVoiceInputMode = false;
|
|
84
|
+
this.isTexting = false;
|
|
85
|
+
this.isTalkMode = false;
|
|
86
|
+
this.SSMLRoleVoice = "zh-CN-XiaoxiaoNeural";
|
|
87
|
+
/**
|
|
88
|
+
* 会话Avatar控制
|
|
89
|
+
*/
|
|
90
|
+
this.playAnimation = (animName) => {
|
|
91
|
+
console.log(animName);
|
|
92
|
+
return;
|
|
93
|
+
};
|
|
94
|
+
this.welcome = async () => {
|
|
95
|
+
let msglist = this.messageList?.filter(item => item?.role == "assistant");
|
|
96
|
+
if (msglist?.length)
|
|
97
|
+
return; // 已有对话不开场问候
|
|
98
|
+
let user = Parse.User.current();
|
|
99
|
+
let person = await this.loadSelf("Person", "userVerify");
|
|
100
|
+
let profile = await this.loadSelf("Profile", "user");
|
|
101
|
+
let name = user?.get("realname") || profile?.get("name") || person?.get("name") || user?.get("nickname") || user?.get("name");
|
|
102
|
+
// 问候语/首个话题
|
|
103
|
+
let tpl = this.role.get("voiceConfig")?.welcome?.prompt;
|
|
104
|
+
if (!tpl)
|
|
105
|
+
return; // 无模板则返回
|
|
106
|
+
let welcomeContent = await PromptTemplate.fromTemplate(tpl, {
|
|
107
|
+
templateFormat: "mustache"
|
|
108
|
+
}).format({
|
|
109
|
+
name: name,
|
|
110
|
+
timeOfDay: this.getTimeOfDay()
|
|
111
|
+
});
|
|
112
|
+
// let callName = name?`${name},`:"";
|
|
113
|
+
// let callTime = `${this.getTimeOfDay()}好`;
|
|
114
|
+
// let welcomeContent = `${callName}${callTime},期待聆听您的人生故事,想和我聊些什么呢?`;
|
|
115
|
+
// 生成ChatVoice并播放
|
|
116
|
+
let voice = await this.getVoiceByContentText(welcomeContent);
|
|
117
|
+
let message = {
|
|
118
|
+
role: "assistant",
|
|
119
|
+
voice: voice,
|
|
120
|
+
content: welcomeContent,
|
|
121
|
+
complete: true
|
|
122
|
+
};
|
|
123
|
+
this.voiceMap[voice?.id];
|
|
124
|
+
this.playChatVoice(this.voiceMap[voice?.id]);
|
|
125
|
+
this.messageList.push(message);
|
|
126
|
+
};
|
|
127
|
+
this.self = {};
|
|
128
|
+
this.voiceMap = {};
|
|
129
|
+
this.chatServ = chatServ;
|
|
130
|
+
this.role = role;
|
|
131
|
+
this.sessionId = sessionId;
|
|
132
|
+
this.navCtrl = navCtrl;
|
|
133
|
+
this.ncloud = ncloud;
|
|
134
|
+
this.uploadServ = uploadServ;
|
|
135
|
+
if (chatSession?.id) {
|
|
136
|
+
this.chatSession = chatSession;
|
|
137
|
+
this.messageList = this.chatSession.get("messageList");
|
|
138
|
+
this.sessionId = chatSession?.id;
|
|
139
|
+
}
|
|
140
|
+
if (this.role?.id) {
|
|
141
|
+
this.voiceConfig = this.role?.get("voiceConfig");
|
|
142
|
+
if (this.voiceConfig?.autoTalk) {
|
|
143
|
+
this.isTalkMode = true;
|
|
144
|
+
this.isDirect = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
getTimeOfDay() {
|
|
149
|
+
const now = new Date();
|
|
150
|
+
const hours = now.getHours();
|
|
151
|
+
if (hours >= 5 && hours < 12) {
|
|
152
|
+
return "早上";
|
|
153
|
+
}
|
|
154
|
+
else if (hours >= 12 && hours < 14) {
|
|
155
|
+
return "中午";
|
|
156
|
+
}
|
|
157
|
+
else if (hours >= 14 && hours < 18) {
|
|
158
|
+
return "下午";
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
return "晚上";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async loadSelf(className, userKey) {
|
|
165
|
+
if (this.self[className])
|
|
166
|
+
return this.self[className];
|
|
167
|
+
let user = Parse.User.current();
|
|
168
|
+
let query = new Parse.Query(className);
|
|
169
|
+
query.equalTo(userKey, user?.id);
|
|
170
|
+
this.self[className] = await query.first();
|
|
171
|
+
}
|
|
2
172
|
/**
|
|
3
|
-
*
|
|
4
|
-
* 版权所有 © 未来飞马 © 江西脑控科技有限公司 Copyright © Fmode Technology Co., Ltd.
|
|
5
|
-
* 保留所有权利 All Rights Reserved.
|
|
6
|
-
* /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/service-fmai/service-chat/chat-class.mjs
|
|
173
|
+
* 对话模型提示词
|
|
7
174
|
*/
|
|
8
|
-
import{bufferTime,concatMap,Observable,delay,finalize}from"rxjs";import Parse from"parse";import{AgentPrompt}from"../../agent";import{FmodeTTS}from"../../voice/tts";import{PromptTemplate}from"@langchain/core/prompts";const API_BASE="https://server.fmode.cn/api/apig/aigc/gpt",agentPrompt=new AgentPrompt,PromptTplTalkSSMLOutputCode="talk-ssml-output-tpl",PromptTplTalkTextSSMLCode="talk-text-ssml-tpl";export function getMessageContentText(t){let e="";return"string"==typeof t&&(e=t),"object"==typeof t&&(e=t?.find((t=>t?.text))?.text||""),e}export function getMessageImageUrl(t){return"object"==typeof t?t?.find((t=>t?.image_url))?.image_url?.url||"":null}export class FmodeChat{showAvatar(){this.avatarConfig=this.role?.get("avatarConfig"),this.avatarConfig&&(this.isAvatarShow=!0,this.avatarConfig?.image&&(this.avatarConfig.image.waiting=this.avatarConfig.image.waiting||this.role?.get("thumb")||this.role?.get("avatar"),this.avatarMode="image"),this.avatarConfig?.video&&(this.avatarConfig.video.waiting=this.avatarConfig.video.waiting,this.avatarMode="video"))}constructor(t,e,s,i,o,n,a){this.ChatSession=Parse.Object.extend("ChatSession"),this.messageList=[{role:"system",content:"系统提示:AI仅供参考"}],this.latestAIResponse="",this.userInput="",this.userImage="",this.isDirect=!1,this.isAvatarShow=!1,this.avatarMode="",this.isPromptModalOpen=!1,this.isPromptMessageAreaShow=!0,this.promptList=[],this.leftButtons=[{title:"灵感",icon:"color-wand-outline",onClick:()=>{this.isPromptModalOpen=!0},show:()=>this?.promptList?.length},{title:"角色",icon:"people-outline",onClick:()=>{this.navCtrl?.navigateRoot("/chat/pro/mask")},show:()=>!0},{title:"呼叫",icon:"call-outline",onClick:()=>{this.chatServ?.callRole(this.role)},show:()=>this?.role?.get("voiceConfig")}],this.isVoiceInputMode=!1,this.isTexting=!1,this.isTalkMode=!1,this.SSMLRoleVoice="zh-CN-XiaoxiaoNeural",this.playAnimation=t=>{console.log(t)},this.welcome=async()=>{let t=this.messageList?.filter((t=>"assistant"==t?.role));if(t?.length)return;let e=Parse.User.current(),s=await this.loadSelf("Person","userVerify"),i=await this.loadSelf("Profile","user"),o=e?.get("realname")||i?.get("name")||s?.get("name")||e?.get("nickname")||e?.get("name"),n=this.role.get("voiceConfig")?.welcome?.prompt;if(!n)return;let a=await PromptTemplate.fromTemplate(n,{templateFormat:"mustache"}).format({name:o,timeOfDay:this.getTimeOfDay()}),r=await this.getVoiceByContentText(a),l={role:"assistant",voice:r,content:a,complete:!0};this.voiceMap[r?.id],this.playChatVoice(this.voiceMap[r?.id]),this.messageList.push(l)},this.self={},this.voiceMap={},this.chatServ=i,this.role=e,this.sessionId=t,this.navCtrl=o,this.ncloud=n,this.uploadServ=a,s?.id&&(this.chatSession=s,this.messageList=this.chatSession.get("messageList"),this.sessionId=s?.id),this.role?.id&&(this.voiceConfig=this.role?.get("voiceConfig"),this.voiceConfig?.autoTalk&&(this.isTalkMode=!0,this.isDirect=!0))}getTimeOfDay(){const t=(new Date).getHours();return t>=5&&t<12?"早上":t>=12&&t<14?"中午":t>=14&&t<18?"下午":"晚上"}async loadSelf(t,e){if(this.self[t])return this.self[t];let s=Parse.User.current(),i=new Parse.Query(t);i.equalTo(e,s?.id),this.self[t]=await i.first()}async loadTalkSystemPrompt(t){if(!this.isTalkMode)return;if(!t)return;"男"==t?.get("gender")?this.SSMLRoleVoice="zh-CN-YunyeNeural":this.SSMLRoleVoice="zh-CN-XiaoxiaoNeural",this.SSMLRoleVoice=t?.get("voiceConfig")?.voice||this.SSMLRoleVoice;let e=await agentPrompt.getFormatTpl("talk-ssml-output-tpl",{SSMLRoleVoice:this.SSMLRoleVoice}),s=t.get("prompt")||"请你扮演飞码AI的人工智能专家。";s+=e;let i={role:"user",content:s,hidden:!0},o=this.messageList?.map((t=>t?.content)).join();if(o.indexOf(s)>-1)return;let n=this.messageList?.findIndex((t=>"system"==t?.role)),a=n+1;this.messageList.splice(a,0,i)}loadRolePrompt(){let t=this.role?.get("prompt"),e={role:"user",content:t,hidden:!0};if(!t)return;let s=this.messageList?.map((t=>t?.content)).join();if(s.indexOf(t)>-1)return;let i=this.messageList?.findIndex((t=>"system"==t?.role)),o=i+1;this.messageList.splice(o,0,e)}async sendMessage(t="FmodeAiTest测试问题",e,s,i,o){if(this.isPromptMessageAreaShow=!1,this.loadRolePrompt(),e){let s={role:"user",content:[{type:"image_url",image_url:{url:e}},{type:"text",text:t}],complete:!0,createdAt:new Date};o&&(s.voice={id:o?.id}),this.messageList.push({role:"user",content:[{type:"image_url",image_url:{url:e}},{type:"text",text:t}],complete:!0,createdAt:new Date})}else{let e={role:"user",content:t,complete:!0,createdAt:new Date};o&&(e.voice={id:o?.id,duration:o?.duration}),this.messageList.push(e)}let n=new FmodeChatCompletion(this.fixMessageList(this.messageList),{model:this.chatServ?.currentModel?.get("code")||"fmode-4.5-128k"});this.userInput="",this.userImage="";let a=this.isDirect||!1;this.isTalkMode&&(a=!0);let r=n.sendCompletion({isDirect:a,onComplete:s||null}).pipe(finalize((async()=>{if(this.isTalkMode){let t=this.messageList[n.indexOfList]?.content,e=await this.getVoiceByContentText(t,i);i?.onSSMLComplete&&i?.onSSMLComplete(e),this.messageList[n.indexOfList].voice=e,this.playChatVoice(this.voiceMap[e?.id])}this.messageList[n.indexOfList].complete=!0}))).subscribe((t=>{this.messageList[n.indexOfList]=t,this.latestAIResponse=this.getContentText(t?.content);let e=this.chatSession?.get("messageList")?.length;this.messageList?.length>e&&this.saveChatSession(),t?.complete&&(this.saveChatSession(),r.unsubscribe())}))}getVoiceByContentText(t,e,s=!1){let i=this.getContentText(t),o=new(Parse.Object.extend("ChatVoice")),n="";return this.SSMLRoleVoice=this.role?.get("voiceConfig")?.voice||this.SSMLRoleVoice,new Promise((async(t,e)=>{let resolveChatVoice=async()=>{o.set("content",i),o.set("ssml",n),o.set("role","assistant");let e=localStorage.getItem("company");e&&o.set("company",{__type:"Pointer",className:"Company",objectId:e}),Parse.User.current()?.id&&o.set("user",Parse.User.current().toPointer()),this.chatSession?.id&&o.set("session",this.chatSession?.toPointer()),o=await o.save(),this.voiceMap[o?.id]=o,t({id:o?.id})};if(0==s&&(n=`<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="zh-CN"><voice name="${this.SSMLRoleVoice}">${i}</voice></speak>`,resolveChatVoice()),1==s){let t=await agentPrompt.getFormatTpl("talk-text-ssml-tpl",{content:i,SSMLRoleVoice:this.SSMLRoleVoice});new FmodeChatCompletion(this.fixMessageList([{role:"user",content:t}]),{model:this.chatServ?.currentModel?.get("code")||"fmode-4.5-128k"}).sendCompletion({isDirect:!0}).subscribe((async t=>{t?.complete&&(n=this.getContentText(t?.content),resolveChatVoice())}))}}))}getContentText(t){return"string"==typeof t?t:t?.[0]?.text||""}async initTTS(){let t=await this.ncloud.apig("voice/tts/token",{company:localStorage.getItem("company")});if(console.log(t),t?.token){let e=new FmodeTTS(t,this.uploadServ);this.tts=e}}async playChatVoice(t,e){if(await this.initTTS(),this.tts)try{this.playAnimation("talking"),await this.tts.speakAsync(t?.get("ssml"),t,{onLoaded:t=>{e?.onLoaded&&e?.onLoaded(t)},onStop:()=>{e?.onStop&&e?.onStop(),this.playAnimation("waiting")}})}catch(t){console.error(t)}}async saveChatSession(){if("new"==this.sessionId&&(this.chatSession=new this.ChatSession),this.chatSession.set("title",this.genTitle()),this.chatSession.set("role",this.role?.toPointer()),this.chatSession.set("messageList",this.messageList),this.chatSession.set("user",Parse.User.current()?.toPointer()),this.chatSession=await this.chatSession.save(),this.sessionId=this.chatSession?.id,this.sessionId){let t=`${window.location.origin}/chat/pro/chat/${this.sessionId}`;window.location?.pathname?.indexOf("chat/session")>-1&&(t=`${window.location.origin}/chat/session/chat/${this.sessionId}`),t=this.getInviteUrl(t),window.history.replaceState(null,null,t+window.location.search);let e={sid:this.chatSession?.id,rid:this.role?.id,name:this.role?.get("name"),message:this.chatSession?.get("messageList")?.[this.chatSession?.get("messageList")?.length-1]?.content?.slice(0,20),latest:this.chatSession?.createdAt};this.chatServ?.chatList?.length||(this.chatServ.chatList=[]);let s=this.chatServ?.chatList?.find((t=>t?.sid==e?.sid));s>-1?this.chatServ.chatList[s]=e:this.chatServ?.chatList.unshift(e)}}getInviteUrl(t){let e="?";e=t?.indexOf("?")>-1?"&":"?";let s=Parse.User?.current()?.id;if(-1==t?.indexOf("invite="+s)){if(!s)return t;t+=e+"invite="+s}return t}genTitle(){if(this.title)return this.title;let t=this.messageList.find((t=>"user"==t.role))?.content;return"string"==typeof t&&(this.title=t?.slice(0,15)||""),"object"==typeof t&&(this.title=t?.find((t=>t?.text))?.text||""),this.title}fixMessageList(t){return t.map((t=>({role:t.role,content:t.content})))}nowStr(){let t=new Date;return`${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`}}export class FmodeChatCompletion{constructor(t,e){this.content="",this.contentBuffer=[],this.isCompleted=!1,this.indexOfList=Number(t.length),this.messages=t,this.model=e?.model||"fmode-4.5-128k"}sendCompletion(t={}){t.intTime=t?.intTime||50,t.isDirect=t?.isDirect||!1,t?.isDirect&&(t.intTime=1);let e={messages:this.messages,stream:!0,model:this.model,temperature:.5,presence_penalty:0,frequency_penalty:0};return new Observable((s=>{let i=RequestFmodeChatApi("/v1/chat/completions",e).subscribe((e=>{let o=String(e);if("data: [DONE]"==o&&(this.isCompleted=!0,t?.isDirect&&this.isCompleted&&(s.next({role:"assistant",content:this.content,complete:!0,createdAt:new Date}),i.unsubscribe(),t?.onComplete&&t.onComplete({role:"assistant",content:this.content,complete:!0,createdAt:new Date}),s.complete())),o.indexOf("data: {")>-1){let e=chunkToJson(o),n=e?.choices?.[0]?.delta?.content||"";this.contentBuffer.push(n),t?.isDirect&&(this.content+=n||"",this.isCompleted||s.next({role:"assistant",cid:e?.id,content:this.content,createdAt:new Date})),t?.isDirect||this.contentPusher||(this.contentPusher=setInterval((()=>{this.isCompleted&&0==this.contentBuffer?.length&&(s.next({role:"assistant",cid:e?.id,content:this.content,complete:!0,createdAt:new Date}),i.unsubscribe(),clearInterval(this.contentPusher),s.complete()),this.contentBuffer?.length>=0&&(this.contentBuffer?.length>0&&(this.content+=this.contentBuffer.shift()),s.next({role:"assistant",cid:e?.id,content:this.content,createdAt:new Date}))}),t?.intTime))}}))})).pipe(bufferTime(100),concatMap((t=>t)),delay(200))}}function chunkToJson(t){let e;try{e=JSON.parse(t.replaceAll("data: ",""))}catch(t){console.error(t)}return e||{}}function RequestFmodeChatApi(t,e,s="POST"){return new Observable((i=>{let o=API_BASE+t,n=`Bearer ${Parse.User.current()?.getSessionToken()||localStorage.getItem("FMODE_AI_TOKEN")}`;return e.token=n,e&&(e=JSON.stringify(e)),fetch(o,{headers:{"Content-Type":"text/plain","Cache-Control":"no-cache"},body:e||null,method:s,credentials:"omit",mode:"cors"}).then((t=>{let e="";{let s=t.body?.getReader();const o=new TextDecoder;let n=new ReadableStream({start(t){!function read(){s.read().then((({done:e,value:s})=>{if(e)return t.close(),void i.complete();t.enqueue(s),read()}))}()}}).getReader();n.read().then((function processStream({done:t,value:s}){if(t)return;!function processData(t){let s=(e+t).split("\n");if(s?.length>1){for(let t=0;t<s.length-1;t++){let e=s[t];i.next(e)}e=s[s.length-1]}}(o.decode(s)),n.read().then(processStream)}))}})).catch((t=>i.error(t))),()=>{}}))}function JsonToFormData(t){const e=new FormData;return function appendFormData(t,s=""){Array.isArray(t)?t.forEach(((t,e)=>{appendFormData(t,`${s}[${e}]`)})):"object"==typeof t&&null!==t?Object.keys(t).forEach((e=>{const i=s?`${s}.${e}`:e;appendFormData(t[e],i)})):e.append(s,t)}(t),e}
|
|
9
|
-
|
|
10
|
-
|
|
175
|
+
async loadTalkSystemPrompt(role) {
|
|
176
|
+
if (!this.isTalkMode)
|
|
177
|
+
return;
|
|
178
|
+
if (!role)
|
|
179
|
+
return;
|
|
180
|
+
// 加载声音模型:默认为晓晓
|
|
181
|
+
if (role?.get('gender') == '男') {
|
|
182
|
+
this.SSMLRoleVoice = "zh-CN-YunyeNeural";
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
this.SSMLRoleVoice = "zh-CN-XiaoxiaoNeural";
|
|
186
|
+
}
|
|
187
|
+
this.SSMLRoleVoice = role?.get("voiceConfig")?.voice || this.SSMLRoleVoice;
|
|
188
|
+
let SSMLPromptTemplate = await agentPrompt.getFormatTpl(PromptTplTalkSSMLOutputCode, {
|
|
189
|
+
SSMLRoleVoice: this.SSMLRoleVoice, // SSML
|
|
190
|
+
});
|
|
191
|
+
let prompt = role.get("prompt") || "请你扮演飞码AI的人工智能专家。";
|
|
192
|
+
prompt += SSMLPromptTemplate;
|
|
193
|
+
let promptMsg = { role: "user", content: prompt, hidden: true };
|
|
194
|
+
// 查重
|
|
195
|
+
let content = this.messageList?.map(item => item?.content).join();
|
|
196
|
+
if (content.indexOf(prompt) > -1) {
|
|
197
|
+
// 提示词已经存在
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// 补全提示词
|
|
201
|
+
let systemIndex = this.messageList?.findIndex(item => item?.role == "system");
|
|
202
|
+
let insertIndex = systemIndex + 1;
|
|
203
|
+
this.messageList.splice(insertIndex, 0, promptMsg);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 角色提示词
|
|
208
|
+
* @returns
|
|
209
|
+
*/
|
|
210
|
+
loadRolePrompt() {
|
|
211
|
+
// 角色提示
|
|
212
|
+
let prompt = this.role?.get("prompt");
|
|
213
|
+
let promptMsg = { role: "user", content: prompt, hidden: true };
|
|
214
|
+
if (!prompt)
|
|
215
|
+
return; // 无提示词无需添加
|
|
216
|
+
// 内容检查
|
|
217
|
+
let content = this.messageList?.map(item => item?.content).join();
|
|
218
|
+
if (content.indexOf(prompt) > -1) {
|
|
219
|
+
// 提示词已经存在
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// 补全提示词
|
|
223
|
+
let systemIndex = this.messageList?.findIndex(item => item?.role == "system");
|
|
224
|
+
let insertIndex = systemIndex + 1;
|
|
225
|
+
this.messageList.splice(insertIndex, 0, promptMsg);
|
|
226
|
+
// console.log(this.messageList)
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* 发送消息
|
|
230
|
+
* @param message
|
|
231
|
+
* @param imageUrl
|
|
232
|
+
*/
|
|
233
|
+
async sendMessage(message = "FmodeAiTest测试问题", imageUrl, onComplete, eventMap, voice) {
|
|
234
|
+
// 为消息列表补全提示词
|
|
235
|
+
// await this.loadTalkSystemPrompt(this.role);
|
|
236
|
+
this.isPromptMessageAreaShow = false; // 发送第一条消息后,关闭提示看板
|
|
237
|
+
this.loadRolePrompt();
|
|
238
|
+
// 用户输入消息,添加到历史消息清单中
|
|
239
|
+
if (!imageUrl) { // 纯文本
|
|
240
|
+
// console.log("纯文本")
|
|
241
|
+
let msg = {
|
|
242
|
+
role: "user",
|
|
243
|
+
content: message,
|
|
244
|
+
complete: true,
|
|
245
|
+
createdAt: new Date()
|
|
246
|
+
};
|
|
247
|
+
if (voice) {
|
|
248
|
+
msg.voice = { id: voice?.id, duration: voice?.duration };
|
|
249
|
+
}
|
|
250
|
+
this.messageList.push(msg);
|
|
251
|
+
}
|
|
252
|
+
else { // 带图片
|
|
253
|
+
let msg = {
|
|
254
|
+
"role": "user",
|
|
255
|
+
"content": [
|
|
256
|
+
{
|
|
257
|
+
"type": "image_url",
|
|
258
|
+
"image_url": { "url": imageUrl },
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"type": "text",
|
|
262
|
+
"text": message
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
complete: true,
|
|
266
|
+
createdAt: new Date()
|
|
267
|
+
};
|
|
268
|
+
if (voice) {
|
|
269
|
+
msg.voice = { id: voice?.id };
|
|
270
|
+
}
|
|
271
|
+
this.messageList.push({
|
|
272
|
+
"role": "user",
|
|
273
|
+
"content": [
|
|
274
|
+
{
|
|
275
|
+
"type": "image_url",
|
|
276
|
+
"image_url": { "url": imageUrl },
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"type": "text",
|
|
280
|
+
"text": message
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
complete: true,
|
|
284
|
+
createdAt: new Date()
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
// 创建并发起一条新的消息补全
|
|
288
|
+
// console.log("send",this.messageList)
|
|
289
|
+
let completion = new FmodeChatCompletion(this.fixMessageList(this.messageList), {
|
|
290
|
+
model: this.chatServ?.currentModel?.get("code") || "fmode-4.5-128k"
|
|
291
|
+
});
|
|
292
|
+
this.userInput = "";
|
|
293
|
+
this.userImage = "";
|
|
294
|
+
// console.log(this.chatServ?.currentModel?.toJSON())
|
|
295
|
+
// 持续更新事件推送的消息体内容至消息列表
|
|
296
|
+
let isDirect = this.isDirect || false;
|
|
297
|
+
if (this.isTalkMode) {
|
|
298
|
+
isDirect = true;
|
|
299
|
+
}
|
|
300
|
+
let send$ = completion.sendCompletion({
|
|
301
|
+
isDirect: isDirect,
|
|
302
|
+
onComplete: onComplete || null
|
|
303
|
+
}).pipe(finalize(async () => {
|
|
304
|
+
if (this.isTalkMode) {
|
|
305
|
+
let content = this.messageList[completion.indexOfList]?.content;
|
|
306
|
+
let voice = await this.getVoiceByContentText(content, eventMap);
|
|
307
|
+
eventMap?.onSSMLComplete && eventMap?.onSSMLComplete(voice);
|
|
308
|
+
this.messageList[completion.indexOfList].voice = voice;
|
|
309
|
+
this.playChatVoice(this.voiceMap[voice?.id]);
|
|
310
|
+
}
|
|
311
|
+
this.messageList[completion.indexOfList].complete = true;
|
|
312
|
+
})).subscribe(message => {
|
|
313
|
+
this.messageList[completion.indexOfList] = message;
|
|
314
|
+
this.latestAIResponse = this.getContentText(message?.content);
|
|
315
|
+
let savedList = this.chatSession?.get("messageList")?.length;
|
|
316
|
+
// 生命周期:会话创建后,有新消息时,创建保存会话
|
|
317
|
+
if (this.messageList?.length > savedList) {
|
|
318
|
+
// console.log("cycle新会话")
|
|
319
|
+
this.saveChatSession();
|
|
320
|
+
}
|
|
321
|
+
if (message?.complete) {
|
|
322
|
+
// 生命周期:消息发送完成后,保存聊天记录
|
|
323
|
+
this.saveChatSession();
|
|
324
|
+
send$.unsubscribe();
|
|
325
|
+
}
|
|
326
|
+
// console.log(message)
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
getVoiceByContentText(content, eventMap, promptEnabled = false) {
|
|
330
|
+
let contentText = this.getContentText(content);
|
|
331
|
+
let ChatVoice = Parse.Object.extend("ChatVoice");
|
|
332
|
+
let chatVoice = new ChatVoice();
|
|
333
|
+
let contentSSML = ``;
|
|
334
|
+
this.SSMLRoleVoice = this.role?.get("voiceConfig")?.voice || this.SSMLRoleVoice;
|
|
335
|
+
return new Promise(async (resolve, reject) => {
|
|
336
|
+
let resolveChatVoice = async () => {
|
|
337
|
+
chatVoice.set("content", contentText);
|
|
338
|
+
chatVoice.set("ssml", contentSSML);
|
|
339
|
+
chatVoice.set("role", "assistant");
|
|
340
|
+
let company = localStorage.getItem("company");
|
|
341
|
+
company && chatVoice.set("company", { __type: "Pointer", className: "Company", objectId: company });
|
|
342
|
+
Parse.User.current()?.id && chatVoice.set("user", Parse.User.current().toPointer());
|
|
343
|
+
this.chatSession?.id && chatVoice.set("session", this.chatSession?.toPointer());
|
|
344
|
+
chatVoice = await chatVoice.save();
|
|
345
|
+
this.voiceMap[chatVoice?.id] = chatVoice;
|
|
346
|
+
resolve({
|
|
347
|
+
id: chatVoice?.id,
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* 方法一:高级语音直接读文本,速度快,但细节情绪标记不足。
|
|
352
|
+
*/
|
|
353
|
+
if (promptEnabled == false) {
|
|
354
|
+
contentSSML = `<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="zh-CN"><voice name="${this.SSMLRoleVoice}">${contentText}</voice></speak>`;
|
|
355
|
+
resolveChatVoice();
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* promptEnabled == true
|
|
359
|
+
* 方法二:通过大模型再次拼接SSML脚本,实现更优质的语音标记,但是生成时间太慢
|
|
360
|
+
*/
|
|
361
|
+
if (promptEnabled == true) {
|
|
362
|
+
// 拼接Prompt
|
|
363
|
+
let TextSSMLPrompt = await agentPrompt.getFormatTpl(PromptTplTalkTextSSMLCode, {
|
|
364
|
+
content: contentText, // 文本内容
|
|
365
|
+
SSMLRoleVoice: this.SSMLRoleVoice, // SSML 演说者
|
|
366
|
+
});
|
|
367
|
+
// 生成SSML
|
|
368
|
+
let completion = new FmodeChatCompletion(this.fixMessageList([{
|
|
369
|
+
role: "user",
|
|
370
|
+
content: TextSSMLPrompt
|
|
371
|
+
}]), {
|
|
372
|
+
model: this.chatServ?.currentModel?.get("code") || "fmode-4.5-128k"
|
|
373
|
+
});
|
|
374
|
+
let send$ = completion.sendCompletion({
|
|
375
|
+
isDirect: true,
|
|
376
|
+
}).subscribe(async (message) => {
|
|
377
|
+
if (message?.complete) {
|
|
378
|
+
contentSSML = this.getContentText(message?.content);
|
|
379
|
+
resolveChatVoice();
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
getContentText(content) {
|
|
386
|
+
if (typeof content == "string") {
|
|
387
|
+
return content;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
return content?.[0]?.text || ``;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async initTTS() {
|
|
394
|
+
// if(this.tts) return // 待明确sts有效期和次数进行优化,避免每次重复获取
|
|
395
|
+
let config = await this.ncloud.apig("voice/tts/token", { company: localStorage.getItem("company") });
|
|
396
|
+
// 有TTS资源,使用情绪合成
|
|
397
|
+
console.log(config);
|
|
398
|
+
if (config?.token) {
|
|
399
|
+
let tts = new FmodeTTS(config, this.uploadServ);
|
|
400
|
+
this.tts = tts;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async playChatVoice(voice, eventMap) {
|
|
404
|
+
await this.initTTS();
|
|
405
|
+
// console.log(this.tts)
|
|
406
|
+
if (this.tts) {
|
|
407
|
+
try {
|
|
408
|
+
// console.log(textOrSSML)
|
|
409
|
+
// 完整的消息,通过TTS合成进行讲话
|
|
410
|
+
this.playAnimation("talking"); // Talking动画,暂时用wating代替
|
|
411
|
+
await this.tts.speakAsync(voice?.get("ssml"), voice, {
|
|
412
|
+
onLoaded: (audio) => {
|
|
413
|
+
eventMap?.onLoaded && eventMap?.onLoaded(audio); // 事件传递
|
|
414
|
+
},
|
|
415
|
+
onStop: () => {
|
|
416
|
+
eventMap?.onStop && eventMap?.onStop(); // 事件传递
|
|
417
|
+
this.playAnimation("waiting"); // Talking动画,暂时用wating代替
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
catch (ttserr) {
|
|
422
|
+
console.error(ttserr);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// 无TSS资源,调用Edge Speech
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* 保存单次会话
|
|
429
|
+
*/
|
|
430
|
+
async saveChatSession() {
|
|
431
|
+
if (this.sessionId == "new") {
|
|
432
|
+
this.chatSession = new this.ChatSession();
|
|
433
|
+
}
|
|
434
|
+
this.chatSession.set("title", this.genTitle());
|
|
435
|
+
this.chatSession.set("role", this.role?.toPointer());
|
|
436
|
+
this.chatSession.set("messageList", this.messageList);
|
|
437
|
+
this.chatSession.set("user", Parse.User.current()?.toPointer());
|
|
438
|
+
this.chatSession = await this.chatSession.save();
|
|
439
|
+
this.sessionId = this.chatSession?.id;
|
|
440
|
+
if (this.sessionId) {
|
|
441
|
+
// 修改URL地址为sessionId,方便分享或切换 角色页面 => 会话页面
|
|
442
|
+
let newHref = `${window.location.origin}/chat/pro/chat/${this.sessionId}`;
|
|
443
|
+
if (window.location?.pathname?.indexOf("chat/session") > -1) {
|
|
444
|
+
newHref = `${window.location.origin}/chat/session/chat/${this.sessionId}`;
|
|
445
|
+
}
|
|
446
|
+
newHref = this.getInviteUrl(newHref);
|
|
447
|
+
window.history.replaceState(null, null, newHref + window.location.search);
|
|
448
|
+
// 修改最新条chatList数据
|
|
449
|
+
let newChat = {
|
|
450
|
+
sid: this.chatSession?.id,
|
|
451
|
+
rid: this.role?.id,
|
|
452
|
+
name: this.role?.get('name'),
|
|
453
|
+
message: this.chatSession?.get('messageList')?.[this.chatSession?.get('messageList')?.length - 1]?.content?.slice(0, 20),
|
|
454
|
+
latest: this.chatSession?.createdAt
|
|
455
|
+
};
|
|
456
|
+
if (!this.chatServ?.chatList?.length)
|
|
457
|
+
this.chatServ.chatList = [];
|
|
458
|
+
let index = this.chatServ?.chatList?.find(item => item?.sid == newChat?.sid);
|
|
459
|
+
if (index > -1) {
|
|
460
|
+
this.chatServ.chatList[index] = newChat;
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
this.chatServ?.chatList.unshift(newChat);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
getInviteUrl(url) {
|
|
468
|
+
// 判断是否有参数
|
|
469
|
+
let connectChar = "?";
|
|
470
|
+
if (url?.indexOf("?") > -1) {
|
|
471
|
+
connectChar = "&";
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
connectChar = "?";
|
|
475
|
+
}
|
|
476
|
+
// 附加invite参数
|
|
477
|
+
let id = Parse.User?.current()?.id;
|
|
478
|
+
if (url?.indexOf("invite=" + id) == -1) {
|
|
479
|
+
if (!id)
|
|
480
|
+
return url;
|
|
481
|
+
url += connectChar + 'invite=' + id;
|
|
482
|
+
}
|
|
483
|
+
return url;
|
|
484
|
+
}
|
|
485
|
+
// 根据聊天内容及问题,生成标题
|
|
486
|
+
genTitle() {
|
|
487
|
+
if (this.title)
|
|
488
|
+
return this.title;
|
|
489
|
+
let content = this.messageList.find(item => item.role == "user")?.content;
|
|
490
|
+
if (typeof content == "string") { // 截图文本内容文字部分
|
|
491
|
+
this.title = content?.slice(0, 15) || "";
|
|
492
|
+
}
|
|
493
|
+
if (typeof content == "object") { // 截图复合内容文字部分
|
|
494
|
+
this.title = content?.find(item => item?.text)?.text || "";
|
|
495
|
+
}
|
|
496
|
+
return this.title;
|
|
497
|
+
}
|
|
498
|
+
fixMessageList(messages) {
|
|
499
|
+
return messages.map(msg => { return { role: msg.role, content: msg.content }; });
|
|
500
|
+
}
|
|
501
|
+
nowStr() {
|
|
502
|
+
let now = new Date();
|
|
503
|
+
return `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()} ${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* FmodeChatCompletion 文本补全类
|
|
508
|
+
* @public
|
|
509
|
+
*/
|
|
510
|
+
export class FmodeChatCompletion {
|
|
511
|
+
constructor(messages, options) {
|
|
512
|
+
this.content = ""; // 本次接收消息结果
|
|
513
|
+
this.contentBuffer = [];
|
|
514
|
+
this.isCompleted = false;
|
|
515
|
+
this.indexOfList = Number(messages.length);
|
|
516
|
+
this.messages = messages;
|
|
517
|
+
this.model = options?.model || "fmode-4.5-128k";
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* @param options
|
|
521
|
+
* @param options.isDirect 是否不等待逐字获取,直接完成内容推送
|
|
522
|
+
* @param options.intTime 是否不等待逐字获取,直接完成内容推送
|
|
523
|
+
* @returns
|
|
524
|
+
*/
|
|
525
|
+
sendCompletion(options = {}) {
|
|
526
|
+
options.intTime = options?.intTime || 50; // 按毫秒逐字推送
|
|
527
|
+
options.isDirect = options?.isDirect || false;
|
|
528
|
+
if (options?.isDirect)
|
|
529
|
+
options.intTime = 1;
|
|
530
|
+
let that = this;
|
|
531
|
+
let opts = {
|
|
532
|
+
"messages": this.messages,
|
|
533
|
+
"stream": true,
|
|
534
|
+
"model": this.model,
|
|
535
|
+
"temperature": 0.5,
|
|
536
|
+
"presence_penalty": 0,
|
|
537
|
+
"frequency_penalty": 0
|
|
538
|
+
};
|
|
539
|
+
// console.log(opts)
|
|
540
|
+
let $messageReceiver = new Observable((observer) => {
|
|
541
|
+
let subscription = RequestFmodeChatApi("/v1/chat/completions", opts)
|
|
542
|
+
.subscribe(data => {
|
|
543
|
+
// Handle each chunk of data
|
|
544
|
+
/** Chunk文本数据格式如下:
|
|
545
|
+
正常消息:
|
|
546
|
+
'data: {"id":"chatcmpl-y2PLKqPDnwAFJIj2L5aqdH5TWK9Yv","object":"chat.completion.chunk","created":1696770162,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"content":"本提示词仅用于测试。"},"finish_reason":null}]}',
|
|
547
|
+
终止原因:
|
|
548
|
+
'data: {"id":"chatcmpl-y2PLKqPDnwAFJIj2L5aqdH5TWK9Yv","object":"chat.completion.chunk","created":1696770162,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}',
|
|
549
|
+
结束消息:
|
|
550
|
+
'data: [DONE]'
|
|
551
|
+
*/
|
|
552
|
+
let chunk = String(data);
|
|
553
|
+
// Check if the completion message is received
|
|
554
|
+
// console.log(chunk)
|
|
555
|
+
if (chunk == 'data: [DONE]') {
|
|
556
|
+
this.isCompleted = true; // 标记完成 => 等待interval推送
|
|
557
|
+
// console.log(options?.isDirect,this.isCompleted)
|
|
558
|
+
if (options?.isDirect && this.isCompleted) {
|
|
559
|
+
observer.next({
|
|
560
|
+
role: "assistant",
|
|
561
|
+
// cid:chunkjson?.['id'],
|
|
562
|
+
content: this.content,
|
|
563
|
+
complete: true, // 推送完成
|
|
564
|
+
createdAt: new Date()
|
|
565
|
+
});
|
|
566
|
+
subscription.unsubscribe(); // Unsubscribe when done
|
|
567
|
+
options?.onComplete && options.onComplete({
|
|
568
|
+
role: "assistant",
|
|
569
|
+
// cid:chunkjson?.['id'],
|
|
570
|
+
content: this.content,
|
|
571
|
+
complete: true, // 推送完成
|
|
572
|
+
createdAt: new Date()
|
|
573
|
+
});
|
|
574
|
+
observer.complete();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// console.log(chunk)
|
|
578
|
+
if (chunk.indexOf("data:\ {") > -1) {
|
|
579
|
+
let chunkjson = chunkToJson(chunk);
|
|
580
|
+
// console.log(chunk)
|
|
581
|
+
// console.log(chunkjson?.choices?.[0]?.delta)
|
|
582
|
+
let words = chunkjson?.choices?.[0]?.delta?.content || "";
|
|
583
|
+
this.contentBuffer.push(words);
|
|
584
|
+
// 消息返回模式:定时器推送,模拟逐字输出
|
|
585
|
+
if (options?.isDirect) {
|
|
586
|
+
this.content += (words || "");
|
|
587
|
+
// 默认累加消息结果
|
|
588
|
+
if (!this.isCompleted) {
|
|
589
|
+
observer.next({
|
|
590
|
+
role: "assistant",
|
|
591
|
+
cid: chunkjson?.['id'],
|
|
592
|
+
content: this.content,
|
|
593
|
+
createdAt: new Date()
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (!options?.isDirect && !this.contentPusher) {
|
|
598
|
+
this.contentPusher = setInterval(() => {
|
|
599
|
+
if (this.isCompleted && this.contentBuffer?.length == 0) { // 推送完毕,清除计时器
|
|
600
|
+
observer.next({
|
|
601
|
+
role: "assistant",
|
|
602
|
+
cid: chunkjson?.['id'],
|
|
603
|
+
content: this.content,
|
|
604
|
+
complete: true, // 推送完成
|
|
605
|
+
createdAt: new Date()
|
|
606
|
+
});
|
|
607
|
+
subscription.unsubscribe(); // Unsubscribe when done
|
|
608
|
+
clearInterval(this.contentPusher);
|
|
609
|
+
observer.complete();
|
|
610
|
+
}
|
|
611
|
+
if (this.contentBuffer?.length >= 0) {
|
|
612
|
+
if (this.contentBuffer?.length > 0) {
|
|
613
|
+
this.content += this.contentBuffer.shift();
|
|
614
|
+
}
|
|
615
|
+
observer.next({
|
|
616
|
+
role: "assistant",
|
|
617
|
+
cid: chunkjson?.['id'],
|
|
618
|
+
content: this.content,
|
|
619
|
+
createdAt: new Date()
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}, options?.intTime);
|
|
623
|
+
}
|
|
624
|
+
// console.log(this.content)
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
return $messageReceiver.pipe(bufferTime(100), // 每100ms收集消息
|
|
629
|
+
concatMap(messages => messages), // 使用 concatMap 逐个发送消息
|
|
630
|
+
delay(200) // 延迟200ms输出每条消息
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
function chunkToJson(chunk) {
|
|
635
|
+
let chunkjson;
|
|
636
|
+
try {
|
|
637
|
+
chunkjson = JSON.parse(chunk.replaceAll("data:\ ", ""));
|
|
638
|
+
}
|
|
639
|
+
catch (errdj) {
|
|
640
|
+
console.error(errdj);
|
|
641
|
+
}
|
|
642
|
+
return chunkjson || {};
|
|
643
|
+
}
|
|
644
|
+
function RequestFmodeChatApi(apipath, body, method = "POST") {
|
|
645
|
+
return new Observable((observer) => {
|
|
646
|
+
let url = API_BASE + apipath;
|
|
647
|
+
let API_TOKEN = Parse.User.current()?.getSessionToken() || localStorage.getItem("FMODE_AI_TOKEN");
|
|
648
|
+
// 通过body传递token参数,避免no-cors模式下Authoriztion头部无效
|
|
649
|
+
let AUTH_TOKEN = `Bearer ${API_TOKEN}`;
|
|
650
|
+
body.token = AUTH_TOKEN;
|
|
651
|
+
if (body)
|
|
652
|
+
body = JSON.stringify(body);
|
|
653
|
+
fetch(url, {
|
|
654
|
+
"headers": {
|
|
655
|
+
// "Authorization": AUTH_TOKEN,
|
|
656
|
+
"Content-Type": "text/plain",
|
|
657
|
+
"Cache-Control": "no-cache"
|
|
658
|
+
},
|
|
659
|
+
"body": body || null,
|
|
660
|
+
"method": method,
|
|
661
|
+
"credentials": "omit",
|
|
662
|
+
"mode": "cors"
|
|
663
|
+
}).then(response => {
|
|
664
|
+
let isStream = true || response.headers?.get("Content-Type")?.indexOf("text/event-stream") > -1;
|
|
665
|
+
let remainingData = ``;
|
|
666
|
+
function processData(data) {
|
|
667
|
+
let combinedData = remainingData + data;
|
|
668
|
+
let messages = combinedData.split('\n');
|
|
669
|
+
if (messages?.length > 1) { // 至少分割2条消息时进行处理
|
|
670
|
+
// 处理每个完整的消息
|
|
671
|
+
for (let i = 0; i < messages.length - 1; i++) {
|
|
672
|
+
let message = messages[i];
|
|
673
|
+
observer.next(message);
|
|
674
|
+
}
|
|
675
|
+
// 保存最后一个不完整的消息
|
|
676
|
+
remainingData = messages[messages.length - 1];
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if (isStream) {
|
|
680
|
+
let greader = response.body?.getReader();
|
|
681
|
+
const decoder = new TextDecoder();
|
|
682
|
+
let rstream = new ReadableStream({
|
|
683
|
+
start(controller) {
|
|
684
|
+
function read() {
|
|
685
|
+
greader.read().then(({ done, value }) => {
|
|
686
|
+
if (done) {
|
|
687
|
+
controller.close();
|
|
688
|
+
observer.complete(); // Complete the observer when stream processing is done
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
controller.enqueue(value);
|
|
692
|
+
read();
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
read();
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
let reader = rstream.getReader();
|
|
699
|
+
function processStream({ done, value }) {
|
|
700
|
+
if (done) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
let text = decoder.decode(value);
|
|
704
|
+
processData(text); // Emit each chunk of data
|
|
705
|
+
reader.read().then(processStream);
|
|
706
|
+
}
|
|
707
|
+
reader.read().then(processStream);
|
|
708
|
+
}
|
|
709
|
+
})
|
|
710
|
+
.catch(error => observer.error(error)); // Handle any errors
|
|
711
|
+
// Return the subscription logic
|
|
712
|
+
return () => {
|
|
713
|
+
// Clean up logic, if needed
|
|
714
|
+
};
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
function JsonToFormData(json) {
|
|
718
|
+
const formData = new FormData();
|
|
719
|
+
function appendFormData(data, path = '') {
|
|
720
|
+
if (Array.isArray(data)) {
|
|
721
|
+
data.forEach((value, index) => {
|
|
722
|
+
appendFormData(value, `${path}[${index}]`);
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
else if (typeof data === 'object' && data !== null) {
|
|
726
|
+
Object.keys(data).forEach(key => {
|
|
727
|
+
const newPath = path ? `${path}.${key}` : key;
|
|
728
|
+
appendFormData(data[key], newPath);
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
formData.append(path, data);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
appendFormData(json);
|
|
736
|
+
return formData;
|
|
737
|
+
}
|
|
738
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhdC1jbGFzcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2Ztb2RlLW5nL3NyYy9saWIvYWlnYy9zZXJ2aWNlLWZtYWkvc2VydmljZS1jaGF0L2NoYXQtY2xhc3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFXLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFDbEYsdUdBQXVHO0FBQ3ZHLE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMxQixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRTFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUczQyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDekQscUJBQXFCO0FBQ3JCLG9FQUFvRTtBQUNwRSxNQUFNLFFBQVEsR0FBVSwyQ0FBMkMsQ0FBQTtBQUNuRSxvRUFBb0U7QUFFcEUsTUFBTSxXQUFXLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztBQUN0QyxNQUFNLDJCQUEyQixHQUFHLHNCQUFzQixDQUFDO0FBQzNELE1BQU0seUJBQXlCLEdBQUcsb0JBQW9CLENBQUM7QUFzQnZELE1BQU0sVUFBVSxxQkFBcUIsQ0FBQyxPQUE4QztJQUNoRixJQUFJLElBQUksR0FBRyxFQUFFLENBQUE7SUFDYixJQUFHLE9BQU8sT0FBTyxJQUFJLFFBQVE7UUFBRSxJQUFJLEdBQUcsT0FBTyxDQUFBO0lBQzdDLElBQUcsT0FBTyxPQUFPLElBQUksUUFBUTtRQUFFLElBQUksR0FBRyxPQUFPLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQSxFQUFFLENBQUEsSUFBSSxFQUFFLElBQUksQ0FBQyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUE7SUFDakYsT0FBTyxJQUFJLENBQUE7QUFDZixDQUFDO0FB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QUksRUFBRSxJQUFJLENBQUMsQ0FBQztRQUM5QixDQUFDO0lBQ0gsQ0FBQztJQUVELGNBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUVyQixPQUFPLFFBQVEsQ0FBQztBQUNsQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgYnVmZmVyVGltZSwgY29uY2F0TWFwLCBPYnNlcnZhYmxlLCBPYnNlcnZlcixkZWxheSwgZmluYWxpemUgfSBmcm9tIFwicnhqc1wiXG4vLyB2YXIgYnVmZmVyVGltZTphbnksIGNvbmNhdE1hcDphbnksIE9ic2VydmFibGU6YW55LCBPYnNlcnZlcjphbnksZGVsYXk6YW55LCBmaW5hbGl6ZTphbnksT2JzZXJ2ZXI6YW55XG5pbXBvcnQgUGFyc2UgZnJvbSBcInBhcnNlXCI7XG5pbXBvcnQgeyBBZ2VudFByb21wdCB9IGZyb20gXCIuLi8uLi9hZ2VudFwiO1xuaW1wb3J0IHsgTmF2Q29udHJvbGxlciB9IGZyb20gXCJAaW9uaWMvYW5ndWxhclwiO1xuaW1wb3J0IHsgRm1vZGVUVFMgfSBmcm9tIFwiLi4vLi4vdm9pY2UvdHRzXCI7XG5pbXBvcnQgeyBOb3ZhQ2xvdWRTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uLy4uL25vdmEtY2xvdWRcIjtcbmltcG9ydCB7IE5vdmFVcGxvYWRTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uLy4uL3N0b3JhZ2Uvc2VydmljZS11cGxvYWQvbm92YS11cGxvYWQuc2VydmljZVwiO1xuaW1wb3J0IHsgUHJvbXB0VGVtcGxhdGUgfSBmcm9tIFwiQGxhbmdjaGFpbi9jb3JlL3Byb21wdHNcIjtcbi8vIHZhciBQYXJzZTphbnkgPSB7fVxuLy8gY29uc3QgQVBJX0JBU0U6c3RyaW5nID0gXCJodHRwOi8vMTI3LjAuMC4xOjczMzcvYXBpL2FwaWcvYWlnYy9ncHRcIlxuY29uc3QgQVBJX0JBU0U6c3RyaW5nID0gXCJodHRwczovL3NlcnZlci5mbW9kZS5jbi9hcGkvYXBpZy9haWdjL2dwdFwiXG4vLyBjb25zdCBBUElfQkFTRTpzdHJpbmcgPSBcImh0dHBzOi8vdGVzdC5mbW9kZS5jbi9hcGkvYXBpZy9haWdjL2dwdFwiXG5cbmNvbnN0IGFnZW50UHJvbXB0ID0gbmV3IEFnZW50UHJvbXB0KCk7XG5jb25zdCBQcm9tcHRUcGxUYWxrU1NNTE91dHB1dENvZGUgPSBcInRhbGstc3NtbC1vdXRwdXQtdHBsXCI7XG5jb25zdCBQcm9tcHRUcGxUYWxrVGV4dFNTTUxDb2RlID0gXCJ0YWxrLXRleHQtc3NtbC10cGxcIjtcbmV4cG9ydCBpbnRlcmZhY2UgRm1vZGVDaGF0TWVzc2FnZVZvaWNle1xuICAgIGlkOnN0cmluZyxcbiAgICBkdXJhdGlvbj86bnVtYmVyLFxuICAgIHNzbWw/OnN0cmluZyxcbiAgICB2b2ljZVVybD86c3RyaW5nLFxufVxuZXhwb3J0IGludGVyZmFjZSBGbW9kZUNoYXRFdmVudE1hcCB7XG4gICAgLy8g5paH5pys6KGl5YWo5LqL5Lu2XG4gICAgb25Db21wbGV0ZT8oRm1vZGVDaGF0TWVzc2FnZSk6dm9pZDtcbiAgICAvLyDor63pn7PlkIjmiJDkuovku7ZcbiAgICBvblNTTUxDb21wbGV0ZT8oRm1vZGVDaGF0TWVzc2FnZVZvaWNlKTp2b2lkOyAvLyBzc21s6ISa5pys5ZCI5oiQXG59IFxuXG5leHBvcnQgaW50ZXJmYWNlIEZtb2RlQ2hhdFZvaWNlQ29uZmlne1xuICAgIHZvaWNlOnN0cmluZyAvLyDlo7Dpn7PmupDop5LoibLku6PnoIFcbiAgICBhdXRvVGFsazpib29sZWFuIC8vIOiHquWKqOivremfs+WbnuWkjVxuICAgIHdlbGNvbWU6e1xuICAgICAgICBlbmFibGVkOnRydWUgLy8g5byA5ZCv5qyi6L+O6K+t6Z+zXG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0TWVzc2FnZUNvbnRlbnRUZXh0KGNvbnRlbnQ6YW55fHN0cmluZ3xBcnJheTxDaGF0SW1hZ2VDb250ZW50SXRlbT4pe1xuICAgIGxldCB0ZXh0ID0gXCJcIlxuICAgIGlmKHR5cGVvZiBjb250ZW50ID09IFwic3RyaW5nXCIpIHRleHQgPSBjb250ZW50XG4gICAgaWYodHlwZW9mIGNvbnRlbnQgPT0gXCJvYmplY3RcIikgdGV4dCA9IGNvbnRlbnQ/LmZpbmQoaXRlbT0+aXRlbT8udGV4dCk/LnRleHQgfHwgXCJcIlxuICAgIHJldHVybiB0ZXh0XG59XG5leHBvcnQgZnVuY3Rpb24gZ2V0TWVzc2FnZUltYWdlVXJsKGNvbnRlbnQ6YW55fHN0cmluZ3xBcnJheTxDaGF0SW1hZ2VDb250ZW50SXRlbT4pe1xuICAgIGlmKHR5cGVvZiBjb250ZW50ID09IFwib2JqZWN0XCIpIHJldHVybiBjb250ZW50Py5maW5kKGl0ZW09Pml0ZW0/LmltYWdlX3VybCk/LmltYWdlX3VybD8udXJsIHx8IFwiXCJcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2hhdEltYWdlQ29udGVudEl0ZW17dHlwZTpzdHJpbmcsdGV4dD86c3RyaW5nLGltYWdlX3VybD86e3VybDpzdHJpbmd9fVxuXG5leHBvcnQgaW50ZXJmYWNlIEZtb2RlQ2hhdE1lc3NhZ2V7XG4gICAgcm9sZTpzdHJpbmcgLy8gdXNlciDnlKjmiLcgYXNzaXN0YW50IEFJIHN5c3RlbSDns7vnu59cbiAgICBjb250ZW50OnN0cmluZ3xBcnJheTxDaGF0SW1hZ2VDb250ZW50SXRlbT4gLy8g5raI5oGv5YaF5a65XG4gICAganNvbj86YW55XG4gICAgaGlkZGVuPzpib29sZWFuXG4gICAgY3JlYXRlZEF0PzpEYXRlIC8vIOWIm+W7uuaXtumXtFxuICAgIGNvbXBsZXRlPzpib29sZWFuIC8vIFN0cmVhbea1geW8j+WKoOi9veWujOaIkFxuICAgIHZvaWNlPzpGbW9kZUNoYXRNZXNzYWdlVm9pY2VcbiAgICAvKipBSeWbnuWkjea2iOaBr+Wtl+autSAqL1xuICAgIGNpZD86c3RyaW5nIC8vIEFJ6KGl5YWo5YaF5a6577yM5YyF5ZCrY2lk77yM5oyH5ZCR5pWw5o2u5bqTIGNoYXRjbXBsLXVEZkdGT3dJSHlpNHZNOUxrQjZrVnhjZzRpMURaXG59XG5cbi8qKlxuICogRm1vZGVDaGF0IOiBiuWkqeWvueivneexu1xuICogQHB1YmxpY1xuICovXG5leHBvcnQgY2xhc3MgRm1vZGVDaGF0e1xuXG4gICAgdGl0bGU6c3RyaW5nXG4gICAgc2Vzc2lvbklkOnN0cmluZ1xuICAgIENoYXRTZXNzaW9uID0gUGFyc2UuT2JqZWN0LmV4dGVuZChcIkNoYXRTZXNzaW9uXCIpXG4gICAgY2hhdFNlc3Npb246UGFyc2UuT2JqZWN0XG4gICAgcm9sZTphbnlcbiAgICBtZXNzYWdlTGlzdDpGbW9kZUNoYXRNZXNzYWdlW10gPSBbe3JvbGU6XCJzeXN0ZW1cIixjb250ZW50Olwi57O757uf5o+Q56S677yaQUnku4Xkvpvlj4LogINcIn1dXG4gICAgbGF0ZXN0QUlSZXNwb25zZTpzdHJpbmd8dW5kZWZpbmVkID0gYGBcbiAgICBjaGF0U2VydjphbnlcbiAgICBwdWJsaWMgdXNlcklucHV0OnN0cmluZyA9IGBgO1xuICAgIHB1YmxpYyB1c2VySW1hZ2U6c3RyaW5nID0gYGA7XG4gICAgaXNEaXJlY3Q6Ym9vbGVhbiA9IGZhbHNlO1xuXG5cbiAgICAvKiogXG4gICAgICog6Jma5ouf5b2i6LGh5bGV56S654q25oCBXG4gICAgICovXG4gICAgaXNBdmF0YXJTaG93OmJvb2xlYW4gPSBmYWxzZTtcbiAgICBhdmF0YXJNb2RlOnN0cmluZyA9IFwiXCI7XG4gICAgYXZhdGFyQ29uZmlnOmFueXx1bmRlZmluZWQ7XG4gICAgc2hvd0F2YXRhcigpe1xuICAgICAgICB0aGlzLmF2YXRhckNvbmZpZyA9IHRoaXMucm9sZT8uZ2V0KFwiYXZhdGFyQ29uZmlnXCIpXG4gICAgICAgIGlmKHRoaXMuYXZhdGFyQ29uZmlnKXtcbiAgICAgICAgICAgIHRoaXMuaXNBdmF0YXJTaG93ID0gdHJ1ZTtcbiAgICAgICAgICAgIGlmKHRoaXMuYXZhdGFyQ29uZmlnPy5pbWFnZSl7XG4gICAgICAgICAgICAgICAgdGhpcy5hdmF0YXJDb25maWcuaW1hZ2Uud2FpdGluZyA9IHRoaXMuYXZhdGFyQ29uZmlnLmltYWdlLndhaXRpbmcgfHwgdGhpcy5yb2xlPy5nZXQoXCJ0aHVtYlwiKSB8fCB0aGlzLnJvbGU/LmdldChcImF2YXRhclwiKVxuICAgICAgICAgICAgICAgIHRoaXMuYXZhdGFyTW9kZSA9IFwiaW1hZ2VcIlxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYodGhpcy5hdmF0YXJDb25maWc/LnZpZGVvKXtcbiAgICAgICAgICAgICAgICB0aGlzLmF2YXRhckNvbmZpZy52aWRlby53YWl0aW5nID0gdGhpcy5hdmF0YXJDb25maWcudmlkZW8ud2FpdGluZ1xuICAgICAgICAgICAgICAgIHRoaXMuYXZhdGFyTW9kZSA9IFwidmlkZW9cIlxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICog6aKE572u5o+Q56S66K+N5by556qX5piv5ZCm5bGV56S6XG4gICAgICovXG4gICAgaXNQcm9tcHRNb2RhbE9wZW46Ym9vbGVhbiA9IGZhbHNlO1xuICAgIGlzUHJvbXB0TWVzc2FnZUFyZWFTaG93OmJvb2xlYW4gPSB0cnVlO1xuICAgIHByb21wdExpc3Q6YW55ID0gW11cblxuICAgIC8qKlxuICAgICAqIOi+k+WFpeaMiemSruWMuuWfn1xuICAgICAqL1xuICAgIG5hdkN0cmw6TmF2Q29udHJvbGxlclxuICAgICBsZWZ0QnV0dG9uczpBcnJheTxhbnk+ID0gW1xuICAgICAgICAvLyDmj5DnpLog5b2T6KeS6Imy6YWN572u6aKE6K6+5o+Q56S66K+N5pe2IOaYvuekulxuICAgICAgICB7dGl0bGU6XCLngbXmhJ9cIixpY29uOlwiY29sb3Itd2FuZC1vdXRsaW5lXCIsb25DbGljazooKT0+e1xuICAgICAgICAgIHRoaXMuaXNQcm9tcHRNb2RhbE9wZW4gPSB0cnVlXG4gICAgICAgIH0sc2hvdzooKT0+e1xuICAgICAgICAgIHJldHVybiB0aGlzPy5wcm9tcHRMaXN0Py5sZW5ndGhcbiAgICAgICAgfX0sXG4gICAgICAgIHt0aXRsZTpcIuinkuiJslwiLGljb246XCJwZW9wbGUtb3V0bGluZVwiLG9uQ2xpY2s6KCk9PntcbiAgICAgICAgICB0aGlzLm5hdkN0cmw/Lm5hdmlnYXRlUm9vdChcIi9jaGF0L3Byby9tYXNrXCIpO1xuICAgICAgICB9LHNob3c6KCk9PntyZXR1cm4gdHJ1ZX19LFxuICAgICAgICB7dGl0bGU6XCLlkbzlj6tcIixpY29uOlwiY2FsbC1vdXRsaW5lXCIsb25DbGljazooKT0+e1xuICAgICAgICAgICAgdGhpcy5jaGF0U2Vydj8uY2FsbFJvbGUodGhpcy5yb2xlKVxuICAgICAgICB9LHNob3c6KCk9PntcbiAgICAgICAgICByZXR1cm4gdGhpcz8ucm9sZT8uZ2V0KCd2b2ljZUNvbmZpZycpO1xuICAgICAgICB9fSxcbiAgICAgIF1cblxuICAgIC8qKlxuICAgICAqIOaYr+WQpuW8gOWQr+ivremfs+a2iOaBr+aooeW8j++8iOWNleasoe+8iVxuICAgICAqL1xuICAgIGlzVm9pY2VJbnB1dE1vZGU6Ym9vbGVhbiA9IGZhbHNlO1xuICAgIGlzVGV4dGluZzpib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKipcbiAgICAgKiDmmK/lkKblvIDlkK/lrp7ml7blr7nor53mqKHlvI/vvIjlrp7ml7bvvIlcbiAgICAgKiBAZGVzY1xuICAgICAqIOW8gOWQr3NzbWwgc3lzdGVt5o+Q56S66K+NXG4gICAgICog5byA5ZCv5Zue562U5paH5pys6ZmkeG1s5pi+56S65qih5byPXG4gICAgICovXG4gICAgdm9pY2VDb25maWc6Rm1vZGVDaGF0Vm9pY2VDb25maWd8dW5kZWZpbmVkXG4gICAgaXNUYWxrTW9kZTpib29sZWFuID0gZmFsc2U7XG4gICAgU1NNTFJvbGVWb2ljZTpzdHJpbmcgPSBcInpoLUNOLVhpYW94aWFvTmV1cmFsXCI7XG5cbiAgICBuY2xvdWQ6Tm92YUNsb3VkU2VydmljZVxuICAgIHVwbG9hZFNlcnY6Tm92YVVwbG9hZFNlcnZpY2VcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBzZXNzaW9uSWQ6c3RyaW5nLHJvbGU/OlBhcnNlLk9iamVjdCxjaGF0U2Vzc2lvbj86UGFyc2UuT2JqZWN0LGNoYXRTZXJ2PzphbnksXG4gICAgICAgIG5hdkN0cmw/Ok5hdkNvbnRyb2xsZXIsXG4gICAgICAgIG5jbG91ZD86Tm92YUNsb3VkU2VydmljZSxcbiAgICAgICAgdXBsb2FkU2Vydj86Tm92YVVwbG9hZFNlcnZpY2UsXG4gICAgICAgICl7XG4gICAgICAgIHRoaXMuY2hhdFNlcnYgPSBjaGF0U2VydlxuICAgICAgICB0aGlzLnJvbGUgPSByb2xlXG4gICAgICAgIHRoaXMuc2Vzc2lvbklkID0gc2Vzc2lvbklkXG4gICAgICAgIHRoaXMubmF2Q3RybCA9IG5hdkN0cmxcbiAgICAgICAgdGhpcy5uY2xvdWQgPSBuY2xvdWRcbiAgICAgICAgdGhpcy51cGxvYWRTZXJ2ID0gdXBsb2FkU2VydlxuICAgICAgICBpZihjaGF0U2Vzc2lvbj8uaWQpe1xuICAgICAgICAgICAgdGhpcy5jaGF0U2Vzc2lvbiA9IGNoYXRTZXNzaW9uO1xuICAgICAgICAgICAgdGhpcy5tZXNzYWdlTGlzdCA9IHRoaXMuY2hhdFNlc3Npb24uZ2V0KFwibWVzc2FnZUxpc3RcIik7XG4gICAgICAgICAgICB0aGlzLnNlc3Npb25JZCA9IGNoYXRTZXNzaW9uPy5pZDtcbiAgICAgICAgfVxuICAgICAgICBpZih0aGlzLnJvbGU/LmlkKXtcbiAgICAgICAgICAgIHRoaXMudm9pY2VDb25maWcgPSB0aGlzLnJvbGU/LmdldChcInZvaWNlQ29uZmlnXCIpXG4gICAgICAgICAgICBpZih0aGlzLnZvaWNlQ29uZmlnPy5hdXRvVGFsayl7XG4gICAgICAgICAgICAgICAgdGhpcy5pc1RhbGtNb2RlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0aGlzLmlzRGlyZWN0ID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiDkvJror51BdmF0YXLmjqfliLZcbiAgICAgKi9cbiAgICBwbGF5QW5pbWF0aW9uID0gKGFuaW1OYW1lOnN0cmluZyk9PntcbiAgICAgICAgY29uc29sZS5sb2coYW5pbU5hbWUpXG4gICAgICAgIHJldHVyblxuICAgIH1cbiAgICB3ZWxjb21lID0gYXN5bmMgKCk9PntcbiAgICAgICAgbGV0IG1zZ2xpc3QgPSB0aGlzLm1lc3NhZ2VMaXN0Py5maWx0ZXIoaXRlbT0+aXRlbT8ucm9sZT09XCJhc3Npc3RhbnRcIik7XG4gICAgICAgIGlmKG1zZ2xpc3Q/Lmxlbmd0aCkgcmV0dXJuIC8vIOW3suacieWvueivneS4jeW8gOWcuumXruWAmVxuXG4gICAgICAgIGxldCB1c2VyID0gUGFyc2UuVXNlci5jdXJyZW50KCk7XG4gICAgICAgIGxldCBwZXJzb24gPSBhd2FpdCB0aGlzLmxvYWRTZWxmKFwiUGVyc29uXCIsXCJ1c2VyVmVyaWZ5XCIpXG4gICAgICAgIGxldCBwcm9maWxlID0gYXdhaXQgdGhpcy5sb2FkU2VsZihcIlByb2ZpbGVcIixcInVzZXJcIilcbiAgICAgICAgbGV0IG5hbWUgPSB1c2VyPy5nZXQoXCJyZWFsbmFtZVwiKSB8fCBwcm9maWxlPy5nZXQoXCJuYW1lXCIpIHx8IHBlcnNvbj8uZ2V0KFwibmFtZVwiKSB8fCB1c2VyPy5nZXQoXCJuaWNrbmFtZVwiKSB8fCB1c2VyPy5nZXQoXCJuYW1lXCIpO1xuXG4gICAgICAgIC8vIOmXruWAmeivrS/pppbkuKror53pophcbiAgICAgICAgbGV0IHRwbCA9IHRoaXMucm9sZS5nZXQoXCJ2b2ljZUNvbmZpZ1wiKT8ud2VsY29tZT8ucHJvbXB0XG4gICAgICAgIGlmKCF0cGwpIHJldHVybiAvLyDml6DmqKHmnb/liJnov5Tlm55cbiAgICAgICAgbGV0IHdlbGNvbWVDb250ZW50ID0gYXdhaXQgUHJvbXB0VGVtcGxhdGUuZnJvbVRlbXBsYXRlKHRwbCx7XG4gICAgICAgICAgICB0ZW1wbGF0ZUZvcm1hdDpcIm11c3RhY2hlXCJcbiAgICAgICAgfSkuZm9ybWF0KHtcbiAgICAgICAgICAgIG5hbWU6bmFtZSxcbiAgICAgICAgICAgIHRpbWVPZkRheTp0aGlzLmdldFRpbWVPZkRheSgpXG4gICAgICAgIH0pXG4gICAgICAgIC8vIGxldCBjYWxsTmFtZSA9IG5hbWU/YCR7bmFtZX3vvIxgOlwiXCI7XG4gICAgICAgIC8vIGxldCBjYWxsVGltZSA9IGAke3RoaXMuZ2V0VGltZU9mRGF5KCl95aW9YDtcbiAgICAgICAgLy8gbGV0IHdlbGNvbWVDb250ZW50ID0gYCR7Y2FsbE5hbWV9JHtjYWxsVGltZX3vvIzmnJ/lvoXogYblkKzmgqjnmoTkurrnlJ/mlYXkuovvvIzmg7PlkozmiJHogYrkupvku4DkuYjlkaLvvJ9gO1xuXG4gICAgICAgIC8vIOeUn+aIkENoYXRWb2ljZeW5tuaSreaUvlxuICAgICAgICBsZXQgdm9pY2UgPSBhd2FpdCB0aGlzLmdldFZvaWNlQnlDb250ZW50VGV4dCh3ZWxjb21lQ29udGVudCk7XG4gICAgICAgIGxldCBtZXNzYWdlID0ge1xuICAgICAgICAgICAgcm9sZTpcImFzc2lzdGFudFwiLFxuICAgICAgICAgICAgdm9pY2U6IHZvaWNlLFxuICAgICAgICAgICAgY29udGVudDp3ZWxjb21lQ29udGVudCxcbiAgICAgICAgICAgIGNvbXBsZXRlOnRydWVcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnZvaWNlTWFwW3ZvaWNlPy5pZF1cbiAgICAgICAgdGhpcy5wbGF5Q2hhdFZvaWNlKHRoaXMudm9pY2VNYXBbdm9pY2U/LmlkXSlcbiAgICAgICAgdGhpcy5tZXNzYWdlTGlzdC5wdXNoKG1lc3NhZ2UpXG4gICAgfVxuICAgIGdldFRpbWVPZkRheSgpIHtcbiAgICAgICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcbiAgICAgICAgY29uc3QgaG91cnMgPSBub3cuZ2V0SG91cnMoKTtcbiAgICAgICAgaWYgKGhvdXJzID49IDUgJiYgaG91cnMgPCAxMikge1xuICAgICAgICAgICAgcmV0dXJuIFwi5pep5LiKXCI7XG4gICAgICAgIH0gZWxzZSBpZiAoaG91cnMgPj0gMTIgJiYgaG91cnMgPCAxNCkge1xuICAgICAgICAgICAgcmV0dXJuIFwi5Lit5Y2IXCI7XG4gICAgICAgIH0gZWxzZSBpZiAoaG91cnMgPj0gMTQgJiYgaG91cnMgPCAxOCkge1xuICAgICAgICAgICAgcmV0dXJuIFwi5LiL5Y2IXCI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gXCLmmZrkuIpcIjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBzZWxmOntcbiAgICAgICAgUGVyc29uPzpQYXJzZS5PYmplY3R8dW5kZWZpbmVkXG4gICAgICAgIFByb2ZpbGU/OlBhcnNlLk9iamVjdHx1bmRlZmluZWRcbiAgICB9ID0ge31cbiAgICBhc3luYyBsb2FkU2VsZihjbGFzc05hbWUsdXNlcktleSl7XG4gICAgICAgIGlmKHRoaXMuc2VsZltjbGFzc05hbWVdKSByZXR1cm4gdGhpcy5zZWxmW2NsYXNzTmFtZV1cbiAgICAgICAgbGV0IHVzZXIgPSBQYXJzZS5Vc2VyLmN1cnJlbnQoKTtcbiAgICAgICAgbGV0IHF1ZXJ5ID0gbmV3IFBhcnNlLlF1ZXJ5KGNsYXNzTmFtZSk7XG4gICAgICAgIHF1ZXJ5LmVxdWFsVG8odXNlcktleSx1c2VyPy5pZCk7XG4gICAgICAgIHRoaXMuc2VsZltjbGFzc05hbWVdID0gYXdhaXQgcXVlcnkuZmlyc3QoKTtcbiAgICB9XG5cblxuICAgIC8qKlxuICAgICAqIOWvueivneaooeWei+aPkOekuuivjVxuICAgICAqL1xuICAgICBhc3luYyBsb2FkVGFsa1N5c3RlbVByb21wdChyb2xlOlBhcnNlLk9iamVjdCl7XG4gICAgICAgIGlmKCF0aGlzLmlzVGFsa01vZGUpIHJldHVyblxuICAgICAgICBpZighcm9sZSkgcmV0dXJuXG4gICAgICAgIC8vIOWKoOi9veWjsOmfs+aooeWei++8mum7mOiupOS4uuaZk+aZk1xuICAgICAgICBpZihyb2xlPy5nZXQoJ2dlbmRlcicpPT0n55S3Jyl7XG4gICAgICAgICAgdGhpcy5TU01MUm9sZVZvaWNlID0gXCJ6aC1DTi1ZdW55ZU5ldXJhbFwiXG4gICAgICAgIH1lbHNle1xuICAgICAgICAgIHRoaXMuU1NNTFJvbGVWb2ljZSA9IFwiemgtQ04tWGlhb3hpYW9OZXVyYWxcIlxuICAgICAgICB9XG4gICAgICAgIHRoaXMuU1NNTFJvbGVWb2ljZSA9IHJvbGU/LmdldChcInZvaWNlQ29uZmlnXCIpPy52b2ljZSB8fCB0aGlzLlNTTUxSb2xlVm9pY2UgXG4gICAgICAgIGxldCBTU01MUHJvbXB0VGVtcGxhdGUgPSBhd2FpdCBhZ2VudFByb21wdC5nZXRGb3JtYXRUcGwoUHJvbXB0VHBsVGFsa1NTTUxPdXRwdXRDb2RlLHtcbiAgICAgICAgICAgIFNTTUxSb2xlVm9pY2U6IHRoaXMuU1NNTFJvbGVWb2ljZSwgLy8gU1NNTCBcbiAgICAgICAgfSlcblxuICAgICAgICBsZXQgcHJvbXB0ID0gcm9sZS5nZXQoXCJwcm9tcHRcIikgfHwgXCLor7fkvaDmia7mvJTpo57noIFBSeeahOS6uuW3peaZuuiDveS4k+WutuOAglwiXG4gICAgICAgIHByb21wdCArPSBTU01MUHJvbXB0VGVtcGxhdGU7XG4gICAgICAgIGxldCBwcm9tcHRNc2c6Rm1vZGVDaGF0TWVzc2FnZSA9IHtyb2xlOlwidXNlclwiLGNvbnRlbnQ6cHJvbXB0LGhpZGRlbjp0cnVlfVxuXG4gICAgICAgIC8vIOafpemHjVxuICAgICAgICBsZXQgY29udGVudCA9IHRoaXMubWVzc2FnZUxpc3Q/Lm1hcChpdGVtPT5pdGVtPy5jb250ZW50KS5qb2luKClcbiAgICAgICAgaWYoY29udGVudC5pbmRleE9mKHByb21wdCk+LTEpe1xuICAgICAgICAgICAgLy8g5o+Q56S66K+N5bey57uP5a2Y5ZyoXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICAvLyDooaXlhajmj5DnpLror41cbiAgICAgICAgbGV0IHN5c3RlbUluZGV4ID0gdGhpcy5tZXNzYWdlTGlzdD8uZmluZEluZGV4KGl0ZW09Pml0ZW0/LnJvbGU9PVwic3lzdGVtXCIpO1xuICAgICAgICBsZXQgaW5zZXJ0SW5kZXggPSBzeXN0ZW1JbmRleCsxXG4gICAgICAgIHRoaXMubWVzc2FnZUxpc3Quc3BsaWNlKGluc2VydEluZGV4LDAscHJvbXB0TXNnKVxuICAgICAgICByZXR1cm4gXG4gICAgfVxuICAgIC8qKlxuICAgICAqIOinkuiJsuaPkOekuuivjVxuICAgICAqIEByZXR1cm5zIFxuICAgICAqL1xuICAgIGxvYWRSb2xlUHJvbXB0KCl7XG4gICAgICAgIC8vIOinkuiJsuaPkOekulxuICAgICAgICBsZXQgcHJvbXB0ID0gdGhpcy5yb2xlPy5nZXQoXCJwcm9tcHRcIilcbiAgICAgICAgbGV0IHByb21wdE1zZzpGbW9kZUNoYXRNZXNzYWdlID0ge3JvbGU6XCJ1c2VyXCIsY29udGVudDpwcm9tcHQsaGlkZGVuOnRydWV9XG4gICAgICAgIGlmKCFwcm9tcHQpIHJldHVybiAvLyDml6Dmj5DnpLror43ml6DpnIDmt7vliqBcbiAgICAgICAgLy8g5YaF5a655qOA5p+lXG4gICAgICAgIGxldCBjb250ZW50ID0gdGhpcy5tZXNzYWdlTGlzdD8ubWFwKGl0ZW09Pml0ZW0/LmNvbnRlbnQpLmpvaW4oKVxuICAgICAgICBpZihjb250ZW50LmluZGV4T2YocHJvbXB0KT4tMSl7XG4gICAgICAgICAgICAvLyDmj5DnpLror43lt7Lnu4/lrZjlnKhcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIOihpeWFqOaPkOekuuivjVxuICAgICAgICBsZXQgc3lzdGVtSW5kZXggPSB0aGlzLm1lc3NhZ2VMaXN0Py5maW5kSW5kZXgoaXRlbT0+aXRlbT8ucm9sZT09XCJzeXN0ZW1cIik7XG4gICAgICAgIGxldCBpbnNlcnRJbmRleCA9IHN5c3RlbUluZGV4KzFcbiAgICAgICAgdGhpcy5tZXNzYWdlTGlzdC5zcGxpY2UoaW5zZXJ0SW5kZXgsMCxwcm9tcHRNc2cpXG4gICAgICAgIC8vIGNvbnNvbGUubG9nKHRoaXMubWVzc2FnZUxpc3QpXG4gICAgfVxuICAgIC8qKlxuICAgICAqIOWPkemAgea2iOaBr1xuICAgICAqIEBwYXJhbSBtZXNzYWdlIFxuICAgICAqIEBwYXJhbSBpbWFnZVVybCBcbiAgICAgKi9cbiAgICBhc3luYyBzZW5kTWVzc2FnZShtZXNzYWdlOnN0cmluZz1cIkZtb2RlQWlUZXN05rWL6K+V6Zeu6aKYXCIsaW1hZ2VVcmw/OnN0cmluZyxvbkNvbXBsZXRlPzpGdW5jdGlvbixldmVudE1hcD86Rm1vZGVDaGF0RXZlbnRNYXAsdm9pY2U/OkZtb2RlQ2hhdE1lc3NhZ2VWb2ljZSl7XG4gICAgICAgIC8vIOS4uua2iOaBr+WIl+ihqOihpeWFqOaPkOekuuivjVxuICAgICAgICAvLyBhd2FpdCB0aGlzLmxvYWRUYWxrU3lzdGVtUHJvbXB0KHRoaXMucm9sZSk7XG4gICAgICAgIHRoaXMuaXNQcm9tcHRNZXNzYWdlQXJlYVNob3cgPSBmYWxzZTsgLy8g5Y+R6YCB56ys5LiA5p2h5raI5oGv5ZCO77yM5YWz6Zet5o+Q56S655yL5p2/XG4gICAgICAgIHRoaXMubG9hZFJvbGVQcm9tcHQoKTtcbiAgICAgICAgLy8g55So5oi36L6T5YWl5raI5oGv77yM5re75Yqg5Yiw5Y6G5Y+y5raI5oGv5riF5Y2V5LitXG4gICAgICAgIGlmKCFpbWFnZVVybCl7IC8vIOe6r+aWh+acrFxuICAgICAgICAgICAgLy8gY29uc29sZS5sb2coXCLnuq/mlofmnKxcIilcbiAgICAgICAgICAgIGxldCBtc2c6Rm1vZGVDaGF0TWVzc2FnZSA9IHtcbiAgICAgICAgICAgICAgICByb2xlOlwidXNlclwiLFxuICAgICAgICAgICAgICAgIGNvbnRlbnQ6bWVzc2FnZSxcbiAgICAgICAgICAgICAgICBjb21wbGV0ZTp0cnVlLFxuICAgICAgICAgICAgICAgIGNyZWF0ZWRBdDpuZXcgRGF0ZSgpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZih2b2ljZSl7XG4gICAgICAgICAgICAgICAgbXNnLnZvaWNlID0ge2lkOnZvaWNlPy5pZCxkdXJhdGlvbjp2b2ljZT8uZHVyYXRpb259XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLm1lc3NhZ2VMaXN0LnB1c2gobXNnKVxuICAgICAgICB9ZWxzZXsgLy8g5bim5Zu+54mHXG4gICAgICAgICAgICBsZXQgbXNnOkZtb2RlQ2hhdE1lc3NhZ2UgPSB7XG4gICAgICAgICAgICAgICAgXCJyb2xlXCI6IFwidXNlclwiLFxuICAgICAgICAgICAgICAgIFwiY29udGVudFwiOiBbXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwidHlwZVwiOiBcImltYWdlX3VybFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJpbWFnZV91cmxcIjoge1widXJsXCI6aW1hZ2VVcmx9LFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBcInR5cGVcIjogXCJ0ZXh0XCIsIFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ0ZXh0XCI6IG1lc3NhZ2VcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBdLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlOnRydWUsXG4gICAgICAgICAgICAgICAgY3JlYXRlZEF0Om5ldyBEYXRlKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmKHZvaWNlKXtcbiAgICAgICAgICAgICAgICBtc2cudm9pY2UgPSB7aWQ6dm9pY2U/LmlkfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5tZXNzYWdlTGlzdC5wdXNoKHtcbiAgICAgICAgICAgICAgICBcInJvbGVcIjogXCJ1c2VyXCIsXG4gICAgICAgICAgICAgICAgXCJjb250ZW50XCI6IFtcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgXCJ0eXBlXCI6IFwiaW1hZ2VfdXJsXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICBcImltYWdlX3VybFwiOiB7XCJ1cmxcIjppbWFnZVVybH0sXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwidHlwZVwiOiBcInRleHRcIiwgXG4gICAgICAgICAgICAgICAgICAgICAgICBcInRleHRcIjogbWVzc2FnZVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIF0sXG4gICAgICAgICAgICAgICAgY29tcGxldGU6dHJ1ZSxcbiAgICAgICAgICAgICAgICBjcmVhdGVkQXQ6bmV3IERhdGUoKVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgICAvLyDliJvlu7rlubblj5HotbfkuIDmnaHmlrDnmoTmtojmga/ooaXlhahcbiAgICAgICAgLy8gY29uc29sZS5sb2coXCJzZW5kXCIsdGhpcy5tZXNzYWdlTGlzdClcbiAgICAgICAgbGV0IGNvbXBsZXRpb24gPSBuZXcgRm1vZGVDaGF0Q29tcGxldGlvbih0aGlzLmZpeE1lc3NhZ2VMaXN0KHRoaXMubWVzc2FnZUxpc3QpLHtcbiAgICAgICAgICAgIG1vZGVsOnRoaXMuY2hhdFNlcnY/LmN1cnJlbnRNb2RlbD8uZ2V0KFwiY29kZVwiKSB8fCBcImZtb2RlLTQuNS0xMjhrXCJcbiAgICAgICAgfSlcblxuICAgICAgICB0aGlzLnVzZXJJbnB1dCA9IFwiXCI7XG4gICAgICAgIHRoaXMudXNlckltYWdlID0gXCJcIjtcbiAgICAgICAgLy8gY29uc29sZS5sb2codGhpcy5jaGF0U2Vydj8uY3VycmVudE1vZGVsPy50b0pTT04oKSlcbiAgICAgICAgLy8g5oyB57ut5pu05paw5LqL5Lu25o6o6YCB55qE5raI5oGv5L2T5YaF5a656Iez5raI5oGv5YiX6KGoXG4gICAgICAgIGxldCBpc0RpcmVjdCA9IHRoaXMuaXNEaXJlY3QgfHwgZmFsc2VcbiAgICAgICAgaWYodGhpcy5pc1RhbGtNb2RlKXtcbiAgICAgICAgICAgIGlzRGlyZWN0ID0gdHJ1ZVxuICAgICAgICB9XG4gICAgICAgIGxldCBzZW5kJCA9IGNvbXBsZXRpb24uc2VuZENvbXBsZXRpb24oe1xuICAgICAgICAgICAgaXNEaXJlY3Q6aXNEaXJlY3QsXG4gICAgICAgICAgICBvbkNvbXBsZXRlOm9uQ29tcGxldGV8fG51bGxcbiAgICAgICAgfSkucGlwZShmaW5hbGl6ZShhc3luYyAoKT0+eyAvLyDnrqHpgZNmaW5hbGl6Zeabv+S7o+S6huaXp+eJiOeahChjb21wbGV0ZSk9Pnt9XG5cbiAgICAgICAgICAgIGlmKHRoaXMuaXNUYWxrTW9kZSl7XG4gICAgICAgICAgICAgICAgbGV0IGNvbnRlbnQgPSB0aGlzLm1lc3NhZ2VMaXN0W2NvbXBsZXRpb24uaW5kZXhPZkxpc3RdPy5jb250ZW50O1xuICAgICAgICAgICAgICAgIGxldCB2b2ljZSA9IGF3YWl0IHRoaXMuZ2V0Vm9pY2VCeUNvbnRlbnRUZXh0KGNvbnRlbnQsZXZlbnRNYXApO1xuICAgICAgICAgICAgICAgIGV2ZW50TWFwPy5vblNTTUxDb21wbGV0ZSYmZXZlbnRNYXA/Lm9uU1NNTENvbXBsZXRlKHZvaWNlKTtcbiAgICAgICAgICAgICAgICB0aGlzLm1lc3NhZ2VMaXN0W2NvbXBsZXRpb24uaW5kZXhPZkxpc3RdLnZvaWNlID0gdm9pY2U7XG4gICAgICAgICAgICAgICAgdGhpcy5wbGF5Q2hhdFZvaWNlKHRoaXMudm9pY2VNYXBbdm9pY2U/LmlkXSlcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy5tZXNzYWdlTGlzdFtjb21wbGV0aW9uLmluZGV4T2ZMaXN0XS5jb21wbGV0ZSA9IHRydWVcbiAgICAgICAgfSkpLnN1YnNjcmliZShtZXNzYWdlPT57XG4gICAgICAgICAgICB0aGlzLm1lc3NhZ2VMaXN0W2NvbXBsZXRpb24uaW5kZXhPZkxpc3RdID0gbWVzc2FnZTtcbiAgICAgICAgICAgIHRoaXMubGF0ZXN0QUlSZXNwb25zZSA9IHRoaXMuZ2V0Q29udGVudFRleHQobWVzc2FnZT8uY29udGVudClcbiAgICAgICAgICAgXG4gICAgICAgICAgICBsZXQgc2F2ZWRMaXN0ID0gdGhpcy5jaGF0U2Vzc2lvbj8uZ2V0KFwibWVzc2FnZUxpc3RcIik/Lmxlbmd0aDtcbiAgICAgICAgICAgICAgICAvLyDnlJ/lkb3lkajmnJ/vvJrkvJror53liJvlu7rlkI7vvIzmnInmlrDmtojmga/ml7bvvIzliJvlu7rkv53lrZjkvJror51cbiAgICAgICAgICAgIGlmKHRoaXMubWVzc2FnZUxpc3Q/Lmxlbmd0aCA+IHNhdmVkTGlzdCl7XG4gICAgICAgICAgICAgICAgLy8gY29uc29sZS5sb2coXCJjeWNsZeaWsOS8muivnVwiKVxuICAgICAgICAgICAgICAgIHRoaXMuc2F2ZUNoYXRTZXNzaW9uKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmKG1lc3NhZ2U/LmNvbXBsZXRlKXtcbiAgICAgICAgICAgICAgICAvLyDnlJ/lkb3lkajmnJ/vvJrmtojmga/lj5HpgIHlrozmiJDlkI7vvIzkv53lrZjogYrlpKnorrDlvZVcbiAgICAgICAgICAgICAgICB0aGlzLnNhdmVDaGF0U2Vzc2lvbigpOyBcbiAgICAgICAgICAgICAgICBzZW5kJC51bnN1YnNjcmliZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gY29uc29sZS5sb2cobWVzc2FnZSlcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBnZXRWb2ljZUJ5Q29udGVudFRleHQoY29udGVudDpzdHJpbmd8Q2hhdEltYWdlQ29udGVudEl0ZW1bXSxldmVudE1hcD86Rm1vZGVDaGF0RXZlbnRNYXAscHJvbXB0RW5hYmxlZDpib29sZWFuPWZhbHNlKTpQcm9taXNlPEZtb2RlQ2hhdE1lc3NhZ2VWb2ljZT57XG4gICAgICAgIGxldCBjb250ZW50VGV4dCA9IHRoaXMuZ2V0Q29udGVudFRleHQoY29udGVudCk7XG4gICAgICAgIGxldCBDaGF0Vm9pY2UgPSBQYXJzZS5PYmplY3QuZXh0ZW5kKFwiQ2hhdFZvaWNlXCIpO1xuICAgICAgICBsZXQgY2hhdFZvaWNlID0gbmV3IENoYXRWb2ljZSgpO1xuICAgICAgICBsZXQgY29udGVudFNTTUwgPSBgYDtcbiAgICAgICAgdGhpcy5TU01MUm9sZVZvaWNlID0gdGhpcy5yb2xlPy5nZXQoXCJ2b2ljZUNvbmZpZ1wiKT8udm9pY2UgfHwgdGhpcy5TU01MUm9sZVZvaWNlIFxuXG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZShhc3luYyAocmVzb2x2ZSxyZWplY3QpPT57XG4gICAgICAgICAgICBsZXQgcmVzb2x2ZUNoYXRWb2ljZSA9IGFzeW5jICgpPT57XG4gICAgICAgICAgICAgICAgY2hhdFZvaWNlLnNldChcImNvbnRlbnRcIixjb250ZW50VGV4dCk7XG4gICAgICAgICAgICAgICAgY2hhdFZvaWNlLnNldChcInNzbWxcIixjb250ZW50U1NNTCk7XG4gICAgICAgICAgICAgICAgY2hhdFZvaWNlLnNldChcInJvbGVcIixcImFzc2lzdGFudFwiKTtcbiAgICAgICAgICAgICAgICBsZXQgY29tcGFueSA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKFwiY29tcGFueVwiKVxuICAgICAgICAgICAgICAgIGNvbXBhbnkmJmNoYXRWb2ljZS5zZXQoXCJjb21wYW55XCIse19fdHlwZTpcIlBvaW50ZXJcIixjbGFzc05hbWU6XCJDb21wYW55XCIsb2JqZWN0SWQ6Y29tcGFueX0pO1xuICAgICAgICAgICAgICAgIFBhcnNlLlVzZXIuY3VycmVudCgpPy5pZCYmY2hhdFZvaWNlLnNldChcInVzZXJcIixQYXJzZS5Vc2VyLmN1cnJlbnQoKS50b1BvaW50ZXIoKSk7XG4gICAgICAgICAgICAgICAgdGhpcy5jaGF0U2Vzc2lvbj8uaWQmJmNoYXRWb2ljZS5zZXQoXCJzZXNzaW9uXCIsdGhpcy5jaGF0U2Vzc2lvbj8udG9Qb2ludGVyKCkpO1xuICAgICAgICAgICAgICAgIGNoYXRWb2ljZSA9IGF3YWl0IGNoYXRWb2ljZS5zYXZlKCk7XG4gICAgICAgICAgICAgICAgdGhpcy52b2ljZU1hcFtjaGF0Vm9pY2U/LmlkXSA9IGNoYXRWb2ljZTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgaWQ6Y2hhdFZvaWNlPy5pZCxcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLyoqXG4gICAgICAgICAgICAgKiDmlrnms5XkuIDvvJrpq5jnuqfor63pn7Pnm7TmjqXor7vmlofmnKzvvIzpgJ/luqblv6vvvIzkvYbnu4boioLmg4Xnu6rmoIforrDkuI3otrPjgIJcbiAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgIGlmKHByb21wdEVuYWJsZWQ9PWZhbHNlKXtcbiAgICAgICAgICAgICAgICBjb250ZW50U1NNTCA9IGA8c3BlYWsgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3N5bnRoZXNpc1wiIHhtbG5zOm1zdHRzPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMS9tc3R0c1wiIHhtbG5zOmVtbz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDkvMTAvZW1vdGlvbm1sXCIgdmVyc2lvbj1cIjEuMFwiIHhtbDpsYW5nPVwiemgtQ05cIj48dm9pY2UgbmFtZT1cIiR7dGhpcy5TU01MUm9sZVZvaWNlfVwiPiR7Y29udGVudFRleHR9PC92b2ljZT48L3NwZWFrPmBcbiAgICAgICAgICAgICAgICByZXNvbHZlQ2hhdFZvaWNlKClcbiAgICAgICAgICAgICB9XG4gICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAqIHByb21wdEVuYWJsZWQgPT0gdHJ1ZVxuICAgICAgICAgICAgICog5pa55rOV5LqM77ya6YCa6L+H5aSn5qih5Z6L5YaN5qyh5ou85o6lU1NNTOiEmuacrO+8jOWunueOsOabtOS8mOi0qOeahOivremfs+agh+iusO+8jOS9huaYr+eUn+aIkOaXtumXtOWkquaFolxuICAgICAgICAgICAgICovXG4gICAgICAgICAgICBpZihwcm9tcHRFbmFibGVkPT10cnVlKXtcbiAgICAgICAgICAgICAgICAvLyDmi7zmjqVQcm9tcHRcbiAgICAgICAgICAgICAgICBsZXQgVGV4dFNTTUxQcm9tcHQgPSBhd2FpdCBhZ2VudFByb21wdC5nZXRGb3JtYXRUcGwoUHJvbXB0VHBsVGFsa1RleHRTU01MQ29kZSx7XG4gICAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6Y29udGVudFRleHQsIC8vIOaWh+acrOWGheWuuVxuICAgICAgICAgICAgICAgICAgICBTU01MUm9sZVZvaWNlOiB0aGlzLlNTTUxSb2xlVm9pY2UsIC8vIFNTTUwg5ryU6K+06ICFXG4gICAgICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgICAgIC8vIOeUn+aIkFNTTUxcbiAgICAgICAgICAgICAgICBsZXQgY29tcGxldGlvbiA9IG5ldyBGbW9kZUNoYXRDb21wbGV0aW9uKHRoaXMuZml4TWVzc2FnZUxpc3QoW3tcbiAgICAgICAgICAgICAgICAgICAgcm9sZTpcInVzZXJcIixcbiAgICAgICAgICAgICAgICAgICAgY29udGVudDpUZXh0U1NNTFByb21wdFxuICAgICAgICAgICAgICAgIH1dKSx7XG4gICAgICAgICAgICAgICAgICAgIG1vZGVsOnRoaXMuY2hhdFNlcnY/LmN1cnJlbnRNb2RlbD8uZ2V0KFwiY29kZVwiKSB8fCBcImZtb2RlLTQuNS0xMjhrXCJcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIGxldCBzZW5kJCA9IGNvbXBsZXRpb24uc2VuZENvbXBsZXRpb24oe1xuICAgICAgICAgICAgICAgICAgICBpc0RpcmVjdDp0cnVlLFxuICAgICAgICAgICAgICAgIH0pLnN1YnNjcmliZShhc3luYyBtZXNzYWdlPT57XG4gICAgICAgICAgICAgICAgICAgIGlmKG1lc3NhZ2U/LmNvbXBsZXRlKXtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRlbnRTU01MID0gdGhpcy5nZXRDb250ZW50VGV4dChtZXNzYWdlPy5jb250ZW50KVxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZUNoYXRWb2ljZSgpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgIH0pXG4gICAgfVxuICAgIGdldENvbnRlbnRUZXh0KGNvbnRlbnQ6c3RyaW5nfENoYXRJbWFnZUNvbnRlbnRJdGVtW10pe1xuICAgICAgICBpZih0eXBlb2YgY29udGVudCA9PSBcInN0cmluZ1wiKXtcbiAgICAgICAgICAgIHJldHVybiBjb250ZW50XG4gICAgICAgIH1lbHNle1xuICAgICAgICAgICAgcmV0dXJuIGNvbnRlbnQ/LlswXT8udGV4dCB8fCBgYFxuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFRUUyAtIOivremfs+WQiOaIkFxuICAgICAqIFxuICAgICAqL1xuICAgIHR0czpGbW9kZVRUU1xuICAgIGFzeW5jIGluaXRUVFMoKXtcbiAgICAvLyBpZih0aGlzLnR0cykgcmV0dXJuIC8vIOW+heaYjuehrnN0c+acieaViOacn+WSjOasoeaVsOi/m+ihjOS8mOWMlu+8jOmBv+WFjeavj+asoemHjeWkjeiOt+WPllxuICAgICAgICBsZXQgY29uZmlnOmFueSA9IGF3YWl0IHRoaXMubmNsb3VkLmFwaWcoXCJ2b2ljZS90dHMvdG9rZW5cIix7Y29tcGFueTpsb2NhbFN0b3JhZ2UuZ2V0SXRlbShcImNvbXBhbnlcIil9KVxuICAgICAgICAvLyDmnIlUVFPotYTmupDvvIzkvb/nlKjmg4Xnu6rlkIjmiJBcbiAgICAgICAgY29uc29sZS5sb2coY29uZmlnKVxuICAgICAgICBpZihjb25maWc/LnRva2VuKXtcbiAgICAgICAgbGV0IHR0cyA9IG5ldyBGbW9kZVRUUyhjb25maWcsdGhpcy51cGxvYWRTZXJ2KTtcbiAgICAgICAgdGhpcy50dHM9dHRzO1xuICAgICAgICB9XG4gICAgfVxuICAgIHZvaWNlTWFwOmFueSA9IHt9XG4gICAgYXN5bmMgcGxheUNoYXRWb2ljZSh2b2ljZTpQYXJzZS5PYmplY3QsZXZlbnRNYXA/OmFueSl7XG4gICAgICAgIGF3YWl0IHRoaXMuaW5pdFRUUygpO1xuICAgICAgICAvLyBjb25zb2xlLmxvZyh0aGlzLnR0cylcbiAgICAgICAgaWYodGhpcy50dHMpe1xuICAgICAgICAgICAgdHJ5e1xuICAgICAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKHRleHRPclNTTUwpXG4gICAgICAgICAgICAgICAgLy8g5a6M5pW055qE5raI5oGv77yM6YCa6L+HVFRT5ZCI5oiQ6L+b6KGM6K6y6K+dXG4gICAgICAgICAgICAgICAgdGhpcy5wbGF5QW5pbWF0aW9uKFwidGFsa2luZ1wiKSAvLyBUYWxraW5n5Yqo55S777yM5pqC5pe255Sod2F0aW5n5Luj5pu/XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy50dHMuc3BlYWtBc3luYyh2b2ljZT8uZ2V0KFwic3NtbFwiKSx2b2ljZSx7XG4gICAgICAgICAgICAgICAgICAgIG9uTG9hZGVkOihhdWRpbzphbnkpPT57XG4gICAgICAgICAgICAgICAgICAgICAgICBldmVudE1hcD8ub25Mb2FkZWQmJmV2ZW50TWFwPy5vbkxvYWRlZChhdWRpbyk7IC8vIOS6i+S7tuS8oOmAklxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBvblN0b3A6ICgpPT57XG4gICAgICAgICAgICAgICAgICAgICAgICBldmVudE1hcD8ub25TdG9wJiZldmVudE1hcD8ub25TdG9wKCk7IC8vIOS6i+S7tuS8oOmAklxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5wbGF5QW5pbWF0aW9uKFwid2FpdGluZ1wiKSAvLyBUYWxraW5n5Yqo55S777yM5pqC5pe255Sod2F0aW5n5Luj5pu/XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfWNhdGNoKHR0c2Vycil7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcih0dHNlcnIpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgIC8vIOaXoFRTU+i1hOa6kO+8jOiwg+eUqEVkZ2UgU3BlZWNoXG4gICAgfVxuICAgIC8qKlxuICAgICAqIOS/neWtmOWNleasoeS8muivnVxuICAgICAqL1xuICAgIGFzeW5jIHNhdmVDaGF0U2Vzc2lvbigpe1xuICAgICAgICBpZih0aGlzLnNlc3Npb25JZCA9PSBcIm5ld1wiKXtcbiAgICAgICAgICAgIHRoaXMuY2hhdFNlc3Npb24gPSBuZXcgdGhpcy5DaGF0U2Vzc2lvbigpXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmNoYXRTZXNzaW9uLnNldChcInRpdGxlXCIsdGhpcy5nZW5UaXRsZSgpKVxuICAgICAgICB0aGlzLmNoYXRTZXNzaW9uLnNldChcInJvbGVcIix0aGlzLnJvbGU/LnRvUG9pbnRlcigpKVxuICAgICAgICB0aGlzLmNoYXRTZXNzaW9uLnNldChcIm1lc3NhZ2VMaXN0XCIsdGhpcy5tZXNzYWdlTGlzdClcbiAgICAgICAgdGhpcy5jaGF0U2Vzc2lvbi5zZXQoXCJ1c2VyXCIsUGFyc2UuVXNlci5jdXJyZW50KCk/LnRvUG9pbnRlcigpKVxuICAgICAgICB0aGlzLmNoYXRTZXNzaW9uID0gYXdhaXQgdGhpcy5jaGF0U2Vzc2lvbi5zYXZlKCk7XG4gICAgICAgIHRoaXMuc2Vzc2lvbklkID0gdGhpcy5jaGF0U2Vzc2lvbj8uaWRcbiAgICAgICAgaWYodGhpcy5zZXNzaW9uSWQpeyBcbiAgICAgICAgICAgIC8vIOS/ruaUuVVSTOWcsOWdgOS4unNlc3Npb25JZO+8jOaWueS+v+WIhuS6q+aIluWIh+aNoiDop5LoibLpobXpnaIgPT4g5Lya6K+d6aG16Z2iXG4gICAgICAgICAgICBsZXQgbmV3SHJlZiA9IGAke3dpbmRvdy5sb2NhdGlvbi5vcmlnaW59L2NoYXQvcHJvL2NoYXQvJHt0aGlzLnNlc3Npb25JZH1gXG4gICAgICAgICAgICBpZih3aW5kb3cubG9jYXRpb24/LnBhdGhuYW1lPy5pbmRleE9mKFwiY2hhdC9zZXNzaW9uXCIpPi0xKXtcbiAgICAgICAgICAgICAgICBuZXdIcmVmID0gYCR7d2luZG93LmxvY2F0aW9uLm9yaWdpbn0vY2hhdC9zZXNzaW9uL2NoYXQvJHt0aGlzLnNlc3Npb25JZH1gXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBuZXdIcmVmID0gdGhpcy5nZXRJbnZpdGVVcmwobmV3SHJlZilcbiAgICAgICAgICAgIHdpbmRvdy5oaXN0b3J5LnJlcGxhY2VTdGF0ZShudWxsLCBudWxsLCBuZXdIcmVmK3dpbmRvdy5sb2NhdGlvbi5zZWFyY2gpO1xuICAgICAgICAgICAgLy8g5L+u5pS55pyA5paw5p2hY2hhdExpc3TmlbDmja5cbiAgICAgICAgICAgIGxldCBuZXdDaGF0ID0ge1xuICAgICAgICAgICAgICAgIHNpZDp0aGlzLmNoYXRTZXNzaW9uPy5pZCxcbiAgICAgICAgICAgICAgICByaWQ6dGhpcy5yb2xlPy5pZCxcbiAgICAgICAgICAgICAgICBuYW1lOnRoaXMucm9sZT8uZ2V0KCduYW1lJyksXG4gICAgICAgICAgICAgICAgbWVzc2FnZTp0aGlzLmNoYXRTZXNzaW9uPy5nZXQoJ21lc3NhZ2VMaXN0Jyk/Llt0aGlzLmNoYXRTZXNzaW9uPy5nZXQoJ21lc3NhZ2VMaXN0Jyk/Lmxlbmd0aC0xXT8uY29udGVudD8uc2xpY2UoMCwyMCksXG4gICAgICAgICAgICAgICAgbGF0ZXN0OnRoaXMuY2hhdFNlc3Npb24/LmNyZWF0ZWRBdFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYoIXRoaXMuY2hhdFNlcnY/LmNoYXRMaXN0Py5sZW5ndGgpIHRoaXMuY2hhdFNlcnYuY2hhdExpc3QgPSBbXVxuICAgICAgICAgICAgbGV0IGluZGV4ID0gdGhpcy5jaGF0U2Vydj8uY2hhdExpc3Q/LmZpbmQoaXRlbT0+aXRlbT8uc2lkPT1uZXdDaGF0Py5zaWQpXG4gICAgICAgICAgICBpZihpbmRleD4tMSl7XG4gICAgICAgICAgICAgICAgdGhpcy5jaGF0U2Vydi5jaGF0TGlzdFtpbmRleF0gPSBuZXdDaGF0O1xuICAgICAgICAgICAgfSBlbHNle1xuICAgICAgICAgICAgICAgIHRoaXMuY2hhdFNlcnY/LmNoYXRMaXN0LnVuc2hpZnQobmV3Q2hhdClcbiAgICAgICAgICAgIH1cblxuICAgICAgICB9XG4gICAgfVxuICAgIGdldEludml0ZVVybCh1cmwpe1xuICAgICAgICAvLyDliKTmlq3mmK/lkKbmnInlj4LmlbBcbiAgICAgICAgbGV0IGNvbm5lY3RDaGFyID0gXCI/XCJcbiAgICAgICAgaWYodXJsPy5pbmRleE9mKFwiP1wiKT4tMSl7XG4gICAgICAgICAgY29ubmVjdENoYXIgPSBcIiZcIlxuICAgICAgICB9ZWxzZXtcbiAgICAgICAgICBjb25uZWN0Q2hhciA9IFwiP1wiXG4gICAgICAgIH1cbiAgICAgICAgLy8g6ZmE5YqgaW52aXRl5Y+C5pWwXG4gICAgICAgIGxldCBpZCA9IFBhcnNlLlVzZXI/LmN1cnJlbnQoKT8uaWRcbiAgICAgICAgaWYodXJsPy5pbmRleE9mKFwiaW52aXRlPVwiK2lkKT09LTEpe1xuICAgICAgICAgIGlmKCFpZCkgcmV0dXJuIHVybFxuICAgICAgICAgIHVybCArPSBjb25uZWN0Q2hhcisgJ2ludml0ZT0nICsgaWRcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdXJsXG4gICAgICB9XG4gICAgLy8g5qC55o2u6IGK5aSp5YaF5a655Y+K6Zeu6aKY77yM55Sf5oiQ5qCH6aKYXG4gICAgZ2VuVGl0bGUoKXtcbiAgICAgICAgaWYodGhpcy50aXRsZSkgcmV0dXJuIHRoaXMudGl0bGVcbiAgICAgICAgbGV0IGNvbnRlbnQ6c3RyaW5nfEFycmF5PENoYXRJbWFnZUNvbnRlbnRJdGVtPiA9ICB0aGlzLm1lc3NhZ2VMaXN0LmZpbmQoaXRlbT0+aXRlbS5yb2xlPT1cInVzZXJcIik/LmNvbnRlbnRcbiAgICAgICAgaWYodHlwZW9mIGNvbnRlbnQ9PVwic3RyaW5nXCIpeyAvLyDmiKrlm77mlofmnKzlhoXlrrnmloflrZfpg6jliIZcbiAgICAgICAgICAgIHRoaXMudGl0bGUgPSBjb250ZW50Py5zbGljZSgwLDE1KSB8fCBcIlwiXG4gICAgICAgIH1cbiAgICAgICAgaWYodHlwZW9mIGNvbnRlbnQ9PVwib2JqZWN0XCIpeyAvLyDmiKrlm77lpI3lkIjlhoXlrrnmloflrZfpg6jliIZcbiAgICAgICAgICAgIHRoaXMudGl0bGUgPSBjb250ZW50Py5maW5kKGl0ZW09Pml0ZW0/LnRleHQpPy50ZXh0IHx8IFwiXCJcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy50aXRsZVxuICAgIH1cbiAgICBmaXhNZXNzYWdlTGlzdChtZXNzYWdlczpGbW9kZUNoYXRNZXNzYWdlW10pe1xuICAgICAgICByZXR1cm4gbWVzc2FnZXMubWFwKG1zZz0+e3JldHVybiB7cm9sZTptc2cucm9sZSxjb250ZW50Om1zZy5jb250ZW50fX0pXG4gICAgfVxuXG4gICAgbm93U3RyKCl7XG4gICAgICAgIGxldCBub3cgPSBuZXcgRGF0ZSgpO1xuICAgICAgICByZXR1cm4gYCR7bm93LmdldEZ1bGxZZWFyKCl9LyR7bm93LmdldE1vbnRoKCkrMX0vJHtub3cuZ2V0RGF0ZSgpfSAke25vdy5nZXRIb3VycygpfToke25vdy5nZXRNaW51dGVzKCl9OiR7bm93LmdldFNlY29uZHMoKX1gXG4gICAgfVxuICAgIFxufVxuXG4vKipcbiAqIEZtb2RlQ2hhdENvbXBsZXRpb24g5paH5pys6KGl5YWo57G7XG4gKiBAcHVibGljXG4gKi9cbmV4cG9ydCBjbGFzcyBGbW9kZUNoYXRDb21wbGV0aW9ue1xuICAgIGluZGV4T2ZMaXN0Om51bWJlclxuICAgIG1vZGVsOnN0cmluZ1xuICAgIG1lc3NhZ2VzOkZtb2RlQ2hhdE1lc3NhZ2VbXSAvLyDooaXlhajliY3mj5DnpLror43liJfooahcbiAgICBjb250ZW50OnN0cmluZyA9IFwiXCIgLy8g5pys5qyh5o6l5pS25raI5oGv57uT5p6cXG4gICAgY29udGVudEJ1ZmZlcjpzdHJpbmdbXSA9IFtdXG4gICAgY29udGVudFB1c2hlcjphbnlcbiAgICBpc0NvbXBsZXRlZDpib29sZWFuID0gZmFsc2U7XG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIG1lc3NhZ2VzOkZtb2RlQ2hhdE1lc3NhZ2VbXSxvcHRpb25zPzp7XG4gICAgICAgICAgICBtb2RlbD86c3RyaW5nXG4gICAgICAgIH1cbiAgICApe1xuICAgICAgICB0aGlzLmluZGV4T2ZMaXN0ID0gTnVtYmVyKG1lc3NhZ2VzLmxlbmd0aClcbiAgICAgICAgdGhpcy5tZXNzYWdlcyA9IG1lc3NhZ2VzXG4gICAgICAgIHRoaXMubW9kZWwgPSBvcHRpb25zPy5tb2RlbCB8fCBcImZtb2RlLTQuNS0xMjhrXCJcbiAgICB9XG4gICAgLyoqXG4gICAgICogQHBhcmFtIG9wdGlvbnNcbiAgICAgKiBAcGFyYW0gb3B0aW9ucy5pc0RpcmVjdCDmmK/lkKbkuI3nrYnlvoXpgJDlrZfojrflj5bvvIznm7TmjqXlrozmiJDlhoXlrrnmjqjpgIFcbiAgICAgKiBAcGFyYW0gb3B0aW9ucy5pbnRUaW1lIOaYr+WQpuS4jeetieW+hemAkOWtl+iOt+WPlu+8jOebtOaOpeWujOaIkOWGheWuueaOqOmAgVxuICAgICAqIEByZXR1cm5zIFxuICAgICAqL1xuICAgIHNlbmRDb21wbGV0aW9uKG9wdGlvbnM6e1xuICAgICAgICBpc0RpcmVjdD86Ym9vbGVhbixcbiAgICAgICAgaW50VGltZT86bnVtYmVyLFxuICAgICAgICBvbkNvbXBsZXRlPzpGdW5jdGlvblxuICAgIH09e30pOk9ic2VydmFibGU8Rm1vZGVDaGF0TWVzc2FnZT57XG4gICAgICAgIG9wdGlvbnMuaW50VGltZSA9IG9wdGlvbnM/LmludFRpbWUgfHwgNTAgLy8g5oyJ5q+r56eS6YCQ5a2X5o6o6YCBXG4gICAgICAgIG9wdGlvbnMuaXNEaXJlY3QgPSBvcHRpb25zPy5pc0RpcmVjdCB8fCBmYWxzZVxuICAgICAgICBpZihvcHRpb25zPy5pc0RpcmVjdCkgb3B0aW9ucy5pbnRUaW1lID0gMVxuXG4gICAgICAgIGxldCB0aGF0ID0gdGhpcztcbiAgICAgICAgbGV0IG9wdHMgPSB7XG4gICAgICAgICAgICBcIm1lc3NhZ2VzXCI6dGhpcy5tZXNzYWdlcyxcbiAgICAgICAgICAgIFwic3RyZWFtXCI6dHJ1ZSxcbiAgICAgICAgICAgIFwibW9kZWxcIjp0aGlzLm1vZGVsLFxuICAgICAgICAgICAgXCJ0ZW1wZXJhdHVyZVwiOjAuNSxcbiAgICAgICAgICAgIFwicHJlc2VuY2VfcGVuYWx0eVwiOjAsXG4gICAgICAgICAgICBcImZyZXF1ZW5jeV9wZW5hbHR5XCI6MFxuICAgICAgICB9XG4gICAgICAgIC8vIGNvbnNvbGUubG9nKG9wdHMpXG4gICAgICAgIGxldCAkbWVzc2FnZVJlY2VpdmVyID0gbmV3IE9ic2VydmFibGUoKG9ic2VydmVyOiBPYnNlcnZlcjxGbW9kZUNoYXRNZXNzYWdlPikgPT4ge1xuICAgICAgICAgICAgbGV0IHN1YnNjcmlwdGlvbiA9IFJlcXVlc3RGbW9kZUNoYXRBcGkoXCIvdjEvY2hhdC9jb21wbGV0aW9uc1wiLCBvcHRzKVxuICAgICAgICAgICAgLnN1YnNjcmliZShkYXRhID0+IHtcbiAgICAgICAgICAgICAgICAvLyBIYW5kbGUgZWFjaCBjaHVuayBvZiBkYXRhXG4gICAgICAgICAgICAgICAgLyoqIENodW5r5paH5pys5pWw5o2u5qC85byP5aaC5LiL77yaXG4gICAgICAgICAgICAgICAg5q2j5bi45raI5oGv77yaXG4gICAgICAgICAgICAgICAgJ2RhdGE6IHtcImlkXCI6XCJjaGF0Y21wbC15MlBMS3FQRG53QUZKSWoyTDVhcWRINVRXSzlZdlwiLFwib2JqZWN0XCI6XCJjaGF0LmNvbXBsZXRpb24uY2h1bmtcIixcImNyZWF0ZWRcIjoxNjk2NzcwMTYyLFwibW9kZWxcIjpcImdwdC0zLjUtdHVyYm8tMDYxM1wiLFwiY2hvaWNlc1wiOlt7XCJpbmRleFwiOjAsXCJkZWx0YVwiOntcImNvbnRlbnRcIjpcIuacrOaPkOekuuivjeS7heeUqOS6jua1i+ivleOAglwifSxcImZpbmlzaF9yZWFzb25cIjpudWxsfV19JyxcbiAgICAgICAgICAgICAgICDnu4jmraLljp/lm6DvvJpcbiAgICAgICAgICAgICAgICAnZGF0YToge1wiaWRcIjpcImNoYXRjbXBsLXkyUExLcVBEbndBRkpJajJMNWFxZEg1VFdLOVl2XCIsXCJvYmplY3RcIjpcImNoYXQuY29tcGxldGlvbi5jaHVua1wiLFwiY3JlYXRlZFwiOjE2OTY3NzAxNjIsXCJtb2RlbFwiOlwiZ3B0LTMuNS10dXJiby0wNjEzXCIsXCJjaG9pY2VzXCI6W3tcImluZGV4XCI6MCxcImRlbHRhXCI6e30sXCJmaW5pc2hfcmVhc29uXCI6XCJzdG9wXCJ9XX0nLFxuICAgICAgICAgICAgICAgIOe7k+adn+a2iOaBr++8mlxuICAgICAgICAgICAgICAgICdkYXRhOiBbRE9ORV0nXG4gICAgICAgICAgICAgICAgKi9cbiAgICAgICAgICAgICAgICBsZXQgY2h1bmsgPSBTdHJpbmcoZGF0YSk7XG4gICAgICAgICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGNvbXBsZXRpb24gbWVzc2FnZSBpcyByZWNlaXZlZFxuICAgICAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKGNodW5rKVxuICAgICAgICAgICAgICAgIGlmIChjaHVuayA9PSAnZGF0YTogW0RPTkVdJykgeyBcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5pc0NvbXBsZXRlZCA9IHRydWU7IC8vIOagh+iusOWujOaIkCA9PiDnrYnlvoVpbnRlcnZhbOaOqOmAgVxuICAgICAgICAgICAgICAgICAgICAvLyBjb25zb2xlLmxvZyhvcHRpb25zPy5pc0RpcmVjdCx0aGlzLmlzQ29tcGxldGVkKVxuICAgICAgICAgICAgICAgICAgICBpZihvcHRpb25zPy5pc0RpcmVjdCAmJiB0aGlzLmlzQ29tcGxldGVkKXtcbiAgICAgICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLm5leHQoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvbGU6XCJhc3Npc3RhbnRcIixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBjaWQ6Y2h1bmtqc29uPy5bJ2lkJ10sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGVudDp0aGlzLmNvbnRlbnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGxldGU6dHJ1ZSwgLy8g5o6o6YCB5a6M5oiQXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JlYXRlZEF0Om5ldyBEYXRlKClcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICBzdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTsgLy8gVW5zdWJzY3JpYmUgd2hlbiBkb25lXG4gICAgICAgICAgICAgICAgICAgICAgICBvcHRpb25zPy5vbkNvbXBsZXRlJiZvcHRpb25zLm9uQ29tcGxldGUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvbGU6XCJhc3Npc3RhbnRcIixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBjaWQ6Y2h1bmtqc29uPy5bJ2lkJ10sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGVudDp0aGlzLmNvbnRlbnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGxldGU6dHJ1ZSwgLy8g5o6o6YCB5a6M5oiQXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JlYXRlZEF0Om5ldyBEYXRlKClcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICBvYnNlcnZlci5jb21wbGV0ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKGNodW5rKVxuICAgICAgICAgICAgICAgIGlmKGNodW5rLmluZGV4T2YoXCJkYXRhOlxcIHtcIik+LTEpe1xuICAgICAgICAgICAgICAgICAgICBsZXQgY2h1bmtqc29uID0gY2h1bmtUb0pzb24oY2h1bmspXG4gICAgICAgICAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKGNodW5rKVxuICAgICAgICAgICAgICAgICAgICAvLyBjb25zb2xlLmxvZyhjaHVua2pzb24/LmNob2ljZXM/LlswXT8uZGVsdGEpXG4gICAgICAgICAgICAgICAgICAgIGxldCB3b3JkcyA9IGNodW5ranNvbj8uY2hvaWNlcz8uWzBdPy5kZWx0YT8uY29udGVudCB8fCBcIlwiXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29udGVudEJ1ZmZlci5wdXNoKHdvcmRzKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyDmtojmga/ov5Tlm57mqKHlvI/vvJrlrprml7blmajmjqjpgIHvvIzmqKHmi5/pgJDlrZfovpPlh7pcbiAgICAgICAgICAgICAgICAgICAgaWYob3B0aW9ucz8uaXNEaXJlY3Qpe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY29udGVudCArPSAod29yZHMgfHwgXCJcIik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyDpu5jorqTntK/liqDmtojmga/nu5PmnpxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZighdGhpcy5pc0NvbXBsZXRlZCl7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLm5leHQoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9sZTpcImFzc2lzdGFudFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2lkOmNodW5ranNvbj8uWydpZCddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGVudDp0aGlzLmNvbnRlbnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcmVhdGVkQXQ6bmV3IERhdGUoKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZighb3B0aW9ucz8uaXNEaXJlY3QmJiF0aGlzLmNvbnRlbnRQdXNoZXIpe1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5jb250ZW50UHVzaGVyID0gc2V0SW50ZXJ2YWwoKCk9PntcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZih0aGlzLmlzQ29tcGxldGVkICYmIHRoaXMuY29udGVudEJ1ZmZlcj8ubGVuZ3RoPT0wKXsgLy8g5o6o6YCB5a6M5q+V77yM5riF6Zmk6K6h5pe25ZmoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLm5leHQoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9sZTpcImFzc2lzdGFudFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2lkOmNodW5ranNvbj8uWydpZCddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGVudDp0aGlzLmNvbnRlbnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wbGV0ZTp0cnVlLCAvLyDmjqjpgIHlrozmiJBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNyZWF0ZWRBdDpuZXcgRGF0ZSgpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpOyAvLyBVbnN1YnNjcmliZSB3aGVuIGRvbmVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmNvbnRlbnRQdXNoZXIpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLmNvbXBsZXRlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKHRoaXMuY29udGVudEJ1ZmZlcj8ubGVuZ3RoPj0wKXtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYodGhpcy5jb250ZW50QnVmZmVyPy5sZW5ndGg+MCl7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbnRlbnQgKz0gdGhpcy5jb250ZW50QnVmZmVyLnNoaWZ0KClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvYnNlcnZlci5uZXh0KHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvbGU6XCJhc3Npc3RhbnRcIixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNpZDpjaHVua2pzb24/LlsnaWQnXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6dGhpcy5jb250ZW50LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JlYXRlZEF0Om5ldyBEYXRlKClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9LG9wdGlvbnM/LmludFRpbWUpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgLy8gY29uc29sZS5sb2codGhpcy5jb250ZW50KVxuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KVxuICAgICAgICByZXR1cm4gJG1lc3NhZ2VSZWNlaXZlci5waXBlKFxuICAgICAgICAgICAgYnVmZmVyVGltZSgxMDApLCAvLyDmr48xMDBtc+aUtumbhua2iOaBr1xuICAgICAgICAgICAgY29uY2F0TWFwKG1lc3NhZ2VzID0+IG1lc3NhZ2VzKSwgLy8g5L2/55SoIGNvbmNhdE1hcCDpgJDkuKrlj5HpgIHmtojmga9cbiAgICAgICAgICAgIGRlbGF5KDIwMCkgLy8g5bu26L+fMjAwbXPovpPlh7rmr4/mnaHmtojmga9cbiAgICAgICAgKVxuICAgIH1cblxufVxuXG5mdW5jdGlvbiBjaHVua1RvSnNvbihjaHVuayl7XG4gICAgbGV0IGNodW5ranNvbjphbnlcbiAgICB0cnl7XG4gICAgICAgIGNodW5ranNvbiA9IEpTT04ucGFyc2UoY2h1bmsucmVwbGFjZUFsbChcImRhdGE6XFwgXCIsXCJcIikpO1xuICAgIH1jYXRjaChlcnJkail7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyZGopXG4gICAgfVxuICAgIHJldHVybiBjaHVua2pzb24gfHwge31cbn1cbmZ1bmN0aW9uIFJlcXVlc3RGbW9kZUNoYXRBcGkoYXBpcGF0aCwgYm9keSwgbWV0aG9kID0gXCJQT1NUXCIpIHtcbiAgICByZXR1cm4gbmV3IE9ic2VydmFibGUoKG9ic2VydmVyOiBPYnNlcnZlcjxhbnk+KSA9PiB7XG4gICAgICAgIGxldCB1cmwgPSBBUElfQkFTRSArIGFwaXBhdGg7XG4gICAgICAgIGxldCBBUElfVE9LRU4gPSBQYXJzZS5Vc2VyLmN1cnJlbnQoKT8uZ2V0U2Vzc2lvblRva2VuKCkgfHwgbG9jYWxTdG9yYWdlLmdldEl0ZW0oXCJGTU9ERV9BSV9UT0tFTlwiKTtcbiAgICAgICAgICAgIC8vIOmAmui/h2JvZHnkvKDpgJJ0b2tlbuWPguaVsO+8jOmBv+WFjW5vLWNvcnPmqKHlvI/kuItBdXRob3JpenRpb27lpLTpg6jml6DmlYhcbiAgICAgICAgbGV0IEFVVEhfVE9LRU4gPSBgQmVhcmVyICR7QVBJX1RPS0VOfWBcbiAgICAgICAgYm9keS50b2tlbiA9IEFVVEhfVE9LRU47XG4gICAgICAgIGlmKGJvZHkpIGJvZHkgPSBKU09OLnN0cmluZ2lmeShib2R5KVxuICAgICAgICBmZXRjaCh1cmwsIHtcbiAgICAgICAgICAgIFwiaGVhZGVyc1wiOiB7XG4gICAgICAgICAgICAgICAgLy8gXCJBdXRob3JpemF0aW9uXCI6IEFVVEhfVE9LRU4sXG4gICAgICAgICAgICAgICAgXCJDb250ZW50LVR5cGVcIjogXCJ0ZXh0L3BsYWluXCIsXG4gICAgICAgICAgICAgICAgXCJDYWNoZS1Db250cm9sXCI6IFwibm8tY2FjaGVcIlxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIFwiYm9keVwiOiBib2R5IHx8IG51bGwsXG4gICAgICAgICAgICBcIm1ldGhvZFwiOiBtZXRob2QsXG4gICAgICAgICAgICBcImNyZWRlbnRpYWxzXCI6XCJvbWl0XCIsXG4gICAgICAgICAgICBcIm1vZGVcIjogXCJjb3JzXCJcbiAgICAgICAgfSkudGhlbihyZXNwb25zZSA9PiB7XG4gICAgICAgICAgICBsZXQgaXNTdHJlYW0gPSB0cnVlIHx8IHJlc3BvbnNlLmhlYWRlcnM/LmdldChcIkNvbnRlbnQtVHlwZVwiKT8uaW5kZXhPZihcInRleHQvZXZlbnQtc3RyZWFtXCIpID4gLTFcbiAgICAgICAgICAgIGxldCByZW1haW5pbmdEYXRhID0gYGA7XG5cbiAgICAgICAgICAgIGZ1bmN0aW9uIHByb2Nlc3NEYXRhKGRhdGEpIHtcbiAgICAgICAgICAgICAgICBsZXQgY29tYmluZWREYXRhID0gcmVtYWluaW5nRGF0YSArIGRhdGE7XG4gICAgICAgICAgICAgICAgbGV0IG1lc3NhZ2VzID0gY29tYmluZWREYXRhLnNwbGl0KCdcXG4nKTtcbiAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgaWYobWVzc2FnZXM/Lmxlbmd0aD4xKXsgLy8g6Iez5bCR5YiG5YmyMuadoea2iOaBr+aXtui/m+ihjOWkhOeQhlxuICAgICAgICAgICAgICAgICAgICAvLyDlpITnkIbmr4/kuKrlrozmlbTnmoTmtojmga9cbiAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBtZXNzYWdlcy5sZW5ndGggLSAxOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBtZXNzYWdlID0gbWVzc2FnZXNbaV07XG4gICAgICAgICAgICAgICAgICAgICAgICBvYnNlcnZlci5uZXh0KG1lc3NhZ2UpXG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyDkv53lrZjmnIDlkI7kuIDkuKrkuI3lrozmlbTnmoTmtojmga9cbiAgICAgICAgICAgICAgICAgICAgcmVtYWluaW5nRGF0YSA9IG1lc3NhZ2VzW21lc3NhZ2VzLmxlbmd0aCAtIDFdO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoaXNTdHJlYW0pIHtcbiAgICAgICAgICAgICAgICBsZXQgZ3JlYWRlciA9IHJlc3BvbnNlLmJvZHk/LmdldFJlYWRlcigpO1xuICAgICAgICAgICAgICAgIGNvbnN0IGRlY29kZXIgPSBuZXcgVGV4dERlY29kZXIoKTtcblxuICAgICAgICAgICAgICAgIGxldCByc3RyZWFtID0gbmV3IFJlYWRhYmxlU3RyZWFtKHtcbiAgICAgICAgICAgICAgICAgICAgc3RhcnQoY29udHJvbGxlcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gcmVhZCgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVhZGVyLnJlYWQoKS50aGVuKCh7IGRvbmUsIHZhbHVlIH0pID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGRvbmUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbGxlci5jbG9zZSgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvYnNlcnZlci5jb21wbGV0ZSgpOyAvLyBDb21wbGV0ZSB0aGUgb2JzZXJ2ZXIgd2hlbiBzdHJlYW0gcHJvY2Vzc2luZyBpcyBkb25lXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sbGVyLmVucXVldWUodmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIHJlYWQoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgbGV0IHJlYWRlciA9IHJzdHJlYW0uZ2V0UmVhZGVyKCk7XG5cbiAgICAgICAgICAgICAgICBmdW5jdGlvbiBwcm9jZXNzU3RyZWFtKHsgZG9uZSwgdmFsdWUgfSkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoZG9uZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgbGV0IHRleHQgPSBkZWNvZGVyLmRlY29kZSh2YWx1ZSlcbiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzc0RhdGEodGV4dCkgLy8gRW1pdCBlYWNoIGNodW5rIG9mIGRhdGFcbiAgICAgICAgICAgICAgICAgICAgcmVhZGVyLnJlYWQoKS50aGVuKHByb2Nlc3NTdHJlYW0pO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJlYWRlci5yZWFkKCkudGhlbihwcm9jZXNzU3RyZWFtKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAuY2F0Y2goZXJyb3IgPT4gb2JzZXJ2ZXIuZXJyb3IoZXJyb3IpKTsgLy8gSGFuZGxlIGFueSBlcnJvcnNcblxuICAgICAgICAvLyBSZXR1cm4gdGhlIHN1YnNjcmlwdGlvbiBsb2dpY1xuICAgICAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICAgICAgLy8gQ2xlYW4gdXAgbG9naWMsIGlmIG5lZWRlZFxuICAgICAgICB9O1xuICAgIH0pO1xuICB9XG4gIFxuICBmdW5jdGlvbiBKc29uVG9Gb3JtRGF0YShqc29uKSB7XG4gICAgY29uc3QgZm9ybURhdGEgPSBuZXcgRm9ybURhdGEoKTtcbiAgXG4gICAgZnVuY3Rpb24gYXBwZW5kRm9ybURhdGEoZGF0YSwgcGF0aCA9ICcnKSB7XG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShkYXRhKSkge1xuICAgICAgICBkYXRhLmZvckVhY2goKHZhbHVlLCBpbmRleCkgPT4ge1xuICAgICAgICAgIGFwcGVuZEZvcm1EYXRhKHZhbHVlLCBgJHtwYXRofVske2luZGV4fV1gKTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKHR5cGVvZiBkYXRhID09PSAnb2JqZWN0JyAmJiBkYXRhICE9PSBudWxsKSB7XG4gICAgICAgIE9iamVjdC5rZXlzKGRhdGEpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgICBjb25zdCBuZXdQYXRoID0gcGF0aCA/IGAke3BhdGh9LiR7a2V5fWAgOiBrZXk7XG4gICAgICAgICAgYXBwZW5kRm9ybURhdGEoZGF0YVtrZXldLCBuZXdQYXRoKTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmb3JtRGF0YS5hcHBlbmQocGF0aCwgZGF0YSk7XG4gICAgICB9XG4gICAgfVxuICBcbiAgICBhcHBlbmRGb3JtRGF0YShqc29uKTtcbiAgXG4gICAgcmV0dXJuIGZvcm1EYXRhO1xuICB9Il19
|