@cartanova/qgrid-cli 2.2.1 → 2.3.2
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/bundle/dist/application/qgrid/conv-routing.js +2 -6
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +74 -43
- package/bundle/dist/application/qgrid/token-subscriber.js +14 -1
- package/bundle/dist/application/qgrid/tool-emulation.js +25 -20
- package/bundle/dist/application/request-log/request-log.model.js +54 -8
- package/bundle/dist/sonamu.config.js +11 -2
- package/bundle/dist/testing/global.js +5 -2
- package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +30 -0
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +215 -0
- package/bundle/dist/utils/providers/anthropic/claude-session.js +220 -0
- package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +177 -0
- package/bundle/dist/utils/providers/common/model-cost.js +35 -18
- package/bundle/dist/utils/providers/openai/openai-refresh.js +21 -9
- package/bundle/src/application/qgrid/conv-routing.test.ts +85 -0
- package/bundle/src/application/qgrid/conv-routing.ts +3 -2
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +110 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +112 -64
- package/bundle/src/application/qgrid/token-subscriber.ts +23 -1
- package/bundle/src/application/qgrid/tool-emulation.test.ts +46 -0
- package/bundle/src/application/qgrid/tool-emulation.ts +15 -3
- package/bundle/src/application/request-log/request-log.model.ts +98 -13
- package/bundle/src/sonamu.config.ts +13 -2
- package/bundle/src/testing/global.ts +16 -2
- package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +38 -0
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +564 -0
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +365 -0
- package/bundle/src/utils/providers/anthropic/claude-session.test.ts +263 -0
- package/bundle/src/utils/providers/anthropic/claude-session.ts +327 -0
- package/bundle/src/utils/providers/anthropic/stream-json-adapter.test.ts +373 -0
- package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +344 -0
- package/bundle/src/utils/providers/common/model-cost.test.ts +39 -0
- package/bundle/src/utils/providers/common/model-cost.ts +107 -19
- package/bundle/src/utils/providers/common/provider-dispatcher.ts +13 -2
- package/bundle/src/utils/providers/openai/openai-refresh.ts +34 -9
- package/package.json +1 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import "../../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { canonicalAnthropicModel } from "./anthropic-constants.js";
|
|
3
|
+
import { getExpiresAt, getRefreshToken } from "../common/credentials.js";
|
|
4
|
+
import { TokenModel } from "../../../application/token/token.model.js";
|
|
5
|
+
import { compatibilityKey, makeAnthropicWorkerId, runClaudeSession } from "./claude-session.js";
|
|
6
|
+
import { getLogger } from "@logtape/logtape";
|
|
7
|
+
//#region src/utils/providers/anthropic/anthropic-dispatcher.ts
|
|
8
|
+
/**
|
|
9
|
+
* AnthropicDispatcher — Claude(Anthropic) provider 의 ProviderDispatcher 구현.
|
|
10
|
+
*
|
|
11
|
+
* OpenAIDispatcher 패턴을 미러링하되, worker pool 대신 요청별 fresh spawn(U4 runClaudeSession)을
|
|
12
|
+
* 쓴다. 토큰은 자체 풀(Map)로 관리하고 least-used round-robin 으로 고른다.
|
|
13
|
+
*
|
|
14
|
+
* 멀티턴 / 동시성 계약:
|
|
15
|
+
* - **eligibility 는 compatibilityKey(system+model+schema)** 로 판정한다. systemHash 가 아니다.
|
|
16
|
+
* GenerateRequest.reuse 가 들어오면 그 threadId(=claude session-id)에 대해 dispatcher-local
|
|
17
|
+
* `sessionCompat` 에 저장해 둔 호환키와 이번 요청 호환키를 비교 — 일치 시 resume, 불일치/없음 시
|
|
18
|
+
* cold(새 session-id).
|
|
19
|
+
* - **compat 는 threadCoord 로 왕복하지 않는다.** sessionCompat 는 이 dispatcher 인스턴스 메모리에만
|
|
20
|
+
* 산다(TTL sweep + 토큰 제거 시 정리). threadCoord.systemHash 는 상위 issueConvContext 가 따로
|
|
21
|
+
* 채우지만 Anthropic resume eligibility 판정에는 쓰이지 않는다.
|
|
22
|
+
* - **락 소유권**: claude session-id 직렬화는 U4 `runClaudeSession` 이 단독 소유한다.
|
|
23
|
+
* dispatcher 는 resume eligibility / token ownership 만 판정하고 `withSessionLock` 으로 감싸지 않는다.
|
|
24
|
+
* 여기서 다시 감싸면 U4 내부 락과 같은 session-id 로 재진입해 deadlock 이 난다.
|
|
25
|
+
* cold 첫 호출 경쟁을 따로 막지 않는 근거는 run() 의 락 주석 참조(IF/owner scope 의 순차 호출 전제).
|
|
26
|
+
* - 발급한 session-id 와 호환키를 GenerateResult.threadCoord(threadId=session-id, workerId=tokenId,
|
|
27
|
+
* epoch=0)로 올려, 상위(issueConvContext)가 클라에 회송할 좌표를 만든다.
|
|
28
|
+
*/
|
|
29
|
+
const logger = getLogger(["qgrid", "anthropic-dispatcher"]);
|
|
30
|
+
const DEFAULT_TIMEOUT_MS = 12e4;
|
|
31
|
+
const REFRESH_SAFETY_MS = 6e4;
|
|
32
|
+
const ANTHROPIC_PROVIDER = "anthropic";
|
|
33
|
+
const SESSION_COMPAT_TTL_MS = 600 * 1e3;
|
|
34
|
+
function isRetryableResumeFailure(error) {
|
|
35
|
+
const lower = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
36
|
+
if (lower.includes("timeout") || lower.includes("aborted") || lower.includes("spawn error") || lower.includes("quota") || lower.includes("you've hit") || lower.includes("unauthorized") || lower.includes("authentication") || lower.includes("invalid oauth") || lower.includes("permission denied") || lower.includes("max_turns") || lower.includes("max-turns") || lower.includes("schema") || lower.includes("model_error") || lower.includes("model error")) return false;
|
|
37
|
+
return lower.includes("closed without result") || /\b(resume|transcript)\b/.test(lower);
|
|
38
|
+
}
|
|
39
|
+
var AnthropicDispatcher = class {
|
|
40
|
+
tokenPool = /* @__PURE__ */ new Map();
|
|
41
|
+
requestCounts = /* @__PURE__ */ new Map();
|
|
42
|
+
rrIndex = 0;
|
|
43
|
+
sessionCompat = /* @__PURE__ */ new Map();
|
|
44
|
+
async start() {
|
|
45
|
+
const tokens = await TokenModel.findActiveByProvider("A", "anthropic");
|
|
46
|
+
for (const t of tokens) this.tokenPool.set(t.id, {
|
|
47
|
+
id: t.id,
|
|
48
|
+
name: t.name,
|
|
49
|
+
credentials: t.credentials
|
|
50
|
+
});
|
|
51
|
+
logger.info(`anthropic dispatcher started (${this.tokenPool.size} tokens)`);
|
|
52
|
+
}
|
|
53
|
+
async stop() {
|
|
54
|
+
this.tokenPool.clear();
|
|
55
|
+
this.sessionCompat.clear();
|
|
56
|
+
this.requestCounts.clear();
|
|
57
|
+
this.rrIndex = 0;
|
|
58
|
+
}
|
|
59
|
+
get tokenCount() {
|
|
60
|
+
return this.tokenPool.size;
|
|
61
|
+
}
|
|
62
|
+
replaceTokens(rows) {
|
|
63
|
+
const next = new Set(rows.map((r) => r.id));
|
|
64
|
+
const currentIds = Array.from(this.tokenPool.keys());
|
|
65
|
+
for (const id of currentIds) if (!next.has(id)) this.onTokenRemoved(id);
|
|
66
|
+
for (const r of rows) if (this.tokenPool.has(r.id)) this.onTokenUpdated(r.id, r.name, r.credentials);
|
|
67
|
+
else this.onTokenAdded(r.id, r.name, r.credentials);
|
|
68
|
+
}
|
|
69
|
+
onTokenAdded(id, name, credentials) {
|
|
70
|
+
this.tokenPool.set(id, {
|
|
71
|
+
id,
|
|
72
|
+
name,
|
|
73
|
+
credentials
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
onTokenUpdated(id, name, credentials) {
|
|
77
|
+
const existing = this.tokenPool.get(id);
|
|
78
|
+
if (existing !== void 0 && existing.credentials.accountUuid !== credentials.accountUuid) this.clearSessionCompatFor(id);
|
|
79
|
+
this.tokenPool.set(id, {
|
|
80
|
+
id,
|
|
81
|
+
name,
|
|
82
|
+
credentials
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
onTokenRemoved(id) {
|
|
86
|
+
this.tokenPool.delete(id);
|
|
87
|
+
this.requestCounts.delete(id);
|
|
88
|
+
this.clearSessionCompatFor(id);
|
|
89
|
+
}
|
|
90
|
+
clearSessionCompatFor(tokenId) {
|
|
91
|
+
for (const [sid, e] of this.sessionCompat) if (e.tokenId === tokenId) this.sessionCompat.delete(sid);
|
|
92
|
+
}
|
|
93
|
+
countOf(id) {
|
|
94
|
+
return this.requestCounts.get(id) ?? 0;
|
|
95
|
+
}
|
|
96
|
+
selectToken() {
|
|
97
|
+
const rows = [...this.tokenPool.values()];
|
|
98
|
+
if (rows.length === 0) return null;
|
|
99
|
+
const minCount = Math.min(...rows.map((r) => this.countOf(r.id)));
|
|
100
|
+
const idle = rows.filter((r) => this.countOf(r.id) === minCount);
|
|
101
|
+
const picked = idle[this.rrIndex % idle.length];
|
|
102
|
+
this.rrIndex++;
|
|
103
|
+
return this.charge(picked);
|
|
104
|
+
}
|
|
105
|
+
pickToken(id) {
|
|
106
|
+
const token = this.tokenPool.get(id);
|
|
107
|
+
return token ? this.charge(token) : null;
|
|
108
|
+
}
|
|
109
|
+
charge(token) {
|
|
110
|
+
this.requestCounts.set(token.id, this.countOf(token.id) + 1);
|
|
111
|
+
return token;
|
|
112
|
+
}
|
|
113
|
+
sweepSessionCompat() {
|
|
114
|
+
const now = Date.now();
|
|
115
|
+
for (const [sid, e] of this.sessionCompat) if (now - e.lastUsedAt > SESSION_COMPAT_TTL_MS) this.sessionCompat.delete(sid);
|
|
116
|
+
}
|
|
117
|
+
async generate(req) {
|
|
118
|
+
return this.run(req, () => {});
|
|
119
|
+
}
|
|
120
|
+
async generateStream(req, cb) {
|
|
121
|
+
try {
|
|
122
|
+
const result = await this.run(req, cb.onDelta);
|
|
123
|
+
cb.onThreadId?.(result.threadCoord.threadId);
|
|
124
|
+
cb.onComplete(result);
|
|
125
|
+
} catch (e) {
|
|
126
|
+
cb.onError(e);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async run(req, onDelta) {
|
|
130
|
+
this.sweepSessionCompat();
|
|
131
|
+
const model = canonicalAnthropicModel(req.model);
|
|
132
|
+
const jsonSchema = req.outputSchema !== void 0 ? JSON.stringify(req.outputSchema) : void 0;
|
|
133
|
+
const compat = compatibilityKey({
|
|
134
|
+
system: req.systemPrompt,
|
|
135
|
+
model,
|
|
136
|
+
outputSchema: jsonSchema
|
|
137
|
+
});
|
|
138
|
+
const candidate = req.reuse?.threadId;
|
|
139
|
+
const stored = candidate !== void 0 ? this.sessionCompat.get(candidate) : void 0;
|
|
140
|
+
const ownerMatches = stored !== void 0 && stored.tokenId === req.reuse?.workerId;
|
|
141
|
+
const resumeToken = stored?.compat === compat && ownerMatches ? this.pickToken(stored.tokenId) : null;
|
|
142
|
+
const isResume = resumeToken !== null;
|
|
143
|
+
const resumeSessionId = isResume ? candidate : void 0;
|
|
144
|
+
const token = resumeToken ?? this.selectToken();
|
|
145
|
+
if (!token) throw new Error("No anthropic tokens available");
|
|
146
|
+
const exec = async () => {
|
|
147
|
+
let accessToken = token.credentials.accessToken;
|
|
148
|
+
const expiresAt = getExpiresAt(token.credentials);
|
|
149
|
+
if (expiresAt && expiresAt - Date.now() < REFRESH_SAFETY_MS && getRefreshToken(token.credentials)) try {
|
|
150
|
+
const { QgridFrame } = await import("../../../application/qgrid/qgrid.frame.js");
|
|
151
|
+
accessToken = await QgridFrame.refreshToken({
|
|
152
|
+
id: token.id,
|
|
153
|
+
provider: ANTHROPIC_PROVIDER,
|
|
154
|
+
name: token.name,
|
|
155
|
+
credentials: token.credentials
|
|
156
|
+
});
|
|
157
|
+
} catch (e) {
|
|
158
|
+
logger.warn(`refresh failed for ${token.name}: ${e.message}`);
|
|
159
|
+
}
|
|
160
|
+
const runSession = (resume) => {
|
|
161
|
+
logger.info(`→ ${token.name} (model: ${model}, ${resume ? "resume" : "cold"})`);
|
|
162
|
+
return runClaudeSession({
|
|
163
|
+
tokenId: token.id,
|
|
164
|
+
token: accessToken,
|
|
165
|
+
model,
|
|
166
|
+
system: req.systemPrompt,
|
|
167
|
+
jsonSchema,
|
|
168
|
+
effort: req.effort,
|
|
169
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
170
|
+
coldHistory: resume ? void 0 : req.coldHistory,
|
|
171
|
+
input: resume && req.reuseInput ? req.reuseInput : req.coldInput,
|
|
172
|
+
resumeSessionId: resume ? resumeSessionId : void 0,
|
|
173
|
+
abortSignal: req.abortSignal
|
|
174
|
+
}, onDelta);
|
|
175
|
+
};
|
|
176
|
+
let session;
|
|
177
|
+
try {
|
|
178
|
+
session = await runSession(isResume);
|
|
179
|
+
} catch (e) {
|
|
180
|
+
if (!isResume || !isRetryableResumeFailure(e)) throw e;
|
|
181
|
+
if (candidate) this.sessionCompat.delete(candidate);
|
|
182
|
+
logger.warn(`resume failed for ${token.name}; retrying cold once: ${e.message}`);
|
|
183
|
+
session = await runSession(false);
|
|
184
|
+
}
|
|
185
|
+
if (session.quotaExhausted) throw new Error(`quota exhausted (${token.name})`);
|
|
186
|
+
if (session.isError) {
|
|
187
|
+
const detail = session.text || `empty text, outputTokens=${session.usage.outputTokens}`;
|
|
188
|
+
throw new Error(`claude error (${token.name}): ${detail}`);
|
|
189
|
+
}
|
|
190
|
+
this.sessionCompat.set(session.sessionId, {
|
|
191
|
+
compat,
|
|
192
|
+
tokenId: token.id,
|
|
193
|
+
lastUsedAt: Date.now()
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
text: session.text,
|
|
197
|
+
tokenName: token.name,
|
|
198
|
+
usage: session.usage,
|
|
199
|
+
durationMs: session.durationMs,
|
|
200
|
+
costUsd: session.costUsd,
|
|
201
|
+
model,
|
|
202
|
+
threadCoord: {
|
|
203
|
+
workerId: makeAnthropicWorkerId(token.id),
|
|
204
|
+
threadId: session.sessionId,
|
|
205
|
+
epoch: 0
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
return exec();
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
//#endregion
|
|
213
|
+
export { AnthropicDispatcher };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW50aHJvcGljLWRpc3BhdGNoZXIuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL3Byb3ZpZGVycy9hbnRocm9waWMvYW50aHJvcGljLWRpc3BhdGNoZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBBbnRocm9waWNEaXNwYXRjaGVyIOKAlCBDbGF1ZGUoQW50aHJvcGljKSBwcm92aWRlciDsnZggUHJvdmlkZXJEaXNwYXRjaGVyIOq1rO2YhC5cbiAqXG4gKiBPcGVuQUlEaXNwYXRjaGVyIO2MqO2EtOydhCDrr7jrn6zrp4HtlZjrkJgsIHdvcmtlciBwb29sIOuMgOyLoCDsmpTssq3rs4QgZnJlc2ggc3Bhd24oVTQgcnVuQ2xhdWRlU2Vzc2lvbinsnYRcbiAqIOyTtOuLpC4g7Yag7YGw7J2AIOyekOyytCDtkoAoTWFwKeuhnCDqtIDrpqztlZjqs6AgbGVhc3QtdXNlZCByb3VuZC1yb2JpbiDsnLzroZwg6rOg66W464ukLlxuICpcbiAqIOupgO2LsO2EtCAvIOuPmeyLnOyEsSDqs4Tslb06XG4gKiAgLSAqKmVsaWdpYmlsaXR5IOuKlCBjb21wYXRpYmlsaXR5S2V5KHN5c3RlbSttb2RlbCtzY2hlbWEpKiog66GcIO2MkOygle2VnOuLpC4gc3lzdGVtSGFzaCDqsIAg7JWE64uI64ukLlxuICogICAgR2VuZXJhdGVSZXF1ZXN0LnJldXNlIOqwgCDrk6TslrTsmKTrqbQg6re4IHRocmVhZElkKD1jbGF1ZGUgc2Vzc2lvbi1pZCnsl5Ag64yA7ZW0IGRpc3BhdGNoZXItbG9jYWxcbiAqICAgIGBzZXNzaW9uQ29tcGF0YCDsl5Ag7KCA7J6l7ZW0IOuRlCDtmLjtmZjtgqTsmYAg7J2067KIIOyalOyyrSDtmLjtmZjtgqTrpbwg67mE6rWQIOKAlCDsnbzsuZgg7IucIHJlc3VtZSwg67aI7J287LmYL+yXhuydjCDsi5xcbiAqICAgIGNvbGQo7IOIIHNlc3Npb24taWQpLlxuICogIC0gKipjb21wYXQg64qUIHRocmVhZENvb3JkIOuhnCDsmZXrs7XtlZjsp4Ag7JWK64qU64ukLioqIHNlc3Npb25Db21wYXQg64qUIOydtCBkaXNwYXRjaGVyIOyduOyKpO2EtOyKpCDrqZTrqqjrpqzsl5Drp4xcbiAqICAgIOyCsOuLpChUVEwgc3dlZXAgKyDthqDtgbAg7KCc6rGwIOyLnCDsoJXrpqwpLiB0aHJlYWRDb29yZC5zeXN0ZW1IYXNoIOuKlCDsg4HsnIQgaXNzdWVDb252Q29udGV4dCDqsIAg65Sw66GcXG4gKiAgICDssYTsmrDsp4Drp4wgQW50aHJvcGljIHJlc3VtZSBlbGlnaWJpbGl0eSDtjJDsoJXsl5DripQg7JOw7J207KeAIOyViuuKlOuLpC5cbiAqICAtICoq6529IOyGjOycoOq2jCoqOiBjbGF1ZGUgc2Vzc2lvbi1pZCDsp4HroKztmZTripQgVTQgYHJ1bkNsYXVkZVNlc3Npb25gIOydtCDri6jrj4Ug7IaM7Jyg7ZWc64ukLlxuICogICAgZGlzcGF0Y2hlciDripQgcmVzdW1lIGVsaWdpYmlsaXR5IC8gdG9rZW4gb3duZXJzaGlwIOunjCDtjJDsoJXtlZjqs6AgYHdpdGhTZXNzaW9uTG9ja2Ag7Jy866GcIOqwkOyLuOyngCDslYrripTri6QuXG4gKiAgICDsl6zquLDshJwg64uk7IucIOqwkOyLuOuptCBVNCDrgrTrtoAg65296rO8IOqwmeydgCBzZXNzaW9uLWlkIOuhnCDsnqzsp4TsnoXtlbQgZGVhZGxvY2sg7J20IOuCnOuLpC5cbiAqICAgIGNvbGQg7LKrIO2YuOy2nCDqsr3sn4HsnYQg65Sw66GcIOunieyngCDslYrripQg6re86rGw64qUIHJ1bigpIOydmCDrnb0g7KO87ISdIOywuOyhsChJRi9vd25lciBzY29wZSDsnZgg7Iic7LCoIO2YuOy2nCDsoITsoJwpLlxuICogIC0g67Cc6riJ7ZWcIHNlc3Npb24taWQg7JmAIO2YuO2ZmO2CpOulvCBHZW5lcmF0ZVJlc3VsdC50aHJlYWRDb29yZCh0aHJlYWRJZD1zZXNzaW9uLWlkLCB3b3JrZXJJZD10b2tlbklkLFxuICogICAgZXBvY2g9MCnroZwg7Jis66CkLCDsg4HsnIQoaXNzdWVDb252Q29udGV4dCnqsIAg7YG065287JeQIO2ajOyGoe2VoCDsooztkZzrpbwg66eM65Og64ukLlxuICovXG5cbmltcG9ydCB7IGdldExvZ2dlciB9IGZyb20gXCJAbG9ndGFwZS9sb2d0YXBlXCI7XG5cbmltcG9ydCB7IFRva2VuTW9kZWwgfSBmcm9tIFwiLi4vLi4vLi4vYXBwbGljYXRpb24vdG9rZW4vdG9rZW4ubW9kZWxcIjtcbmltcG9ydCB7IHR5cGUgQW50aHJvcGljQ3JlZGVudGlhbHMgfSBmcm9tIFwiLi4vLi4vLi4vYXBwbGljYXRpb24vdG9rZW4vdG9rZW4udHlwZXNcIjtcbmltcG9ydCB7IGdldEV4cGlyZXNBdCwgZ2V0UmVmcmVzaFRva2VuIH0gZnJvbSBcIi4uL2NvbW1vbi9jcmVkZW50aWFsc1wiO1xuaW1wb3J0IHtcbiAgdHlwZSBHZW5lcmF0ZVJlcXVlc3QsXG4gIHR5cGUgR2VuZXJhdGVSZXN1bHQsXG4gIHR5cGUgR2VuZXJhdGVTdHJlYW1DYWxsYmFja3MsXG4gIHR5cGUgUHJvdmlkZXJEaXNwYXRjaGVyLFxufSBmcm9tIFwiLi4vY29tbW9uL3Byb3ZpZGVyLWRpc3BhdGNoZXJcIjtcbmltcG9ydCB7IGNhbm9uaWNhbEFudGhyb3BpY01vZGVsIH0gZnJvbSBcIi4vYW50aHJvcGljLWNvbnN0YW50c1wiO1xuaW1wb3J0IHsgY29tcGF0aWJpbGl0eUtleSwgbWFrZUFudGhyb3BpY1dvcmtlcklkLCBydW5DbGF1ZGVTZXNzaW9uIH0gZnJvbSBcIi4vY2xhdWRlLXNlc3Npb25cIjtcblxuY29uc3QgbG9nZ2VyID0gZ2V0TG9nZ2VyKFtcInFncmlkXCIsIFwiYW50aHJvcGljLWRpc3BhdGNoZXJcIl0pO1xuXG5jb25zdCBERUZBVUxUX1RJTUVPVVRfTVMgPSAxMjBfMDAwO1xuLy8gYWNjZXNzIHRva2VuIOunjOujjCDsnoTrsJUg7J6E6rOEIOKAlCDquLDsobQgc3RhbmRhbG9uZSDqsr3roZwocWdyaWQuZGlzcGF0Y2hlci50cynsmYAg64+Z7J28KDYwcykuXG5jb25zdCBSRUZSRVNIX1NBRkVUWV9NUyA9IDYwXzAwMDtcblxuaW50ZXJmYWNlIFBvb2xlZFRva2VuIHtcbiAgaWQ6IG51bWJlcjtcbiAgbmFtZTogc3RyaW5nO1xuICBjcmVkZW50aWFsczogQW50aHJvcGljQ3JlZGVudGlhbHM7XG59XG5cbi8vIFFncmlkRnJhbWUucmVmcmVzaFRva2VuIOydgCBUb2tlblN1YnNldEEo7Yq57Z6IIHByb3ZpZGVyKeulvCDrsJvslYQgVG9rZW5Nb2RlbC5zYXZlIOyXkCDrhJjquLTri6QuXG4vLyBhbnRocm9waWMg7ZKA7J2YIO2GoO2BsOydgCDtla3sg4EgcHJvdmlkZXI9XCJhbnRocm9waWNcIiDsnbTrr4DroZwgcmVmcmVzaCDsi5wg7J20IOqwkuydhCDqs6DsoJXsnLzroZwg7LGE7Jq064ukLlxuLy8gKHByb3ZpZGVyIOuIhOudvSDsi5wgc2F2ZSDsi6TtjKgg4oaSIOunjOujjOuQnCBhY2Nlc3MgdG9rZW4g7Jy866GcIOynhO2Wie2VmOuKlCDrsoTqt7jqsIAg64Kc64ukLilcbmNvbnN0IEFOVEhST1BJQ19QUk9WSURFUiA9IFwiYW50aHJvcGljXCIgYXMgY29uc3Q7XG5cbi8vIHNlc3Npb24taWQg4oaSIGNvbXBhdCDrp6TtlZEgVFRMLiDshJzrsoQgdGhyZWFkIGlkbGUgVFRMKDEw67aEKeqzvCDsoJXtlakg4oCUIOq3uOuztOuLpCDsmKTrnpjrkJwg7IS47IWY7J2AXG4vLyDslrTssKjtlLwgcmVzdW1lIOu2iOqwgO2VmOuvgOuhnCDtj5DquLDtlbTrj4Qg7JWI7KCEKHNlc3Npb25Db21wYXQg66y07ZWcIOymneqwgCDrsKnsp4ApLlxuY29uc3QgU0VTU0lPTl9DT01QQVRfVFRMX01TID0gMTAgKiA2MCAqIDEwMDA7XG5cbmludGVyZmFjZSBDb21wYXRFbnRyeSB7XG4gIGNvbXBhdDogc3RyaW5nO1xuICB0b2tlbklkOiBudW1iZXI7XG4gIGxhc3RVc2VkQXQ6IG51bWJlcjtcbn1cblxuZnVuY3Rpb24gaXNSZXRyeWFibGVSZXN1bWVGYWlsdXJlKGVycm9yOiB1bmtub3duKTogYm9vbGVhbiB7XG4gIGNvbnN0IG1lc3NhZ2UgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcik7XG4gIGNvbnN0IGxvd2VyID0gbWVzc2FnZS50b0xvd2VyQ2FzZSgpO1xuICBpZiAoXG4gICAgbG93ZXIuaW5jbHVkZXMoXCJ0aW1lb3V0XCIpIHx8XG4gICAgbG93ZXIuaW5jbHVkZXMoXCJhYm9ydGVkXCIpIHx8XG4gICAgbG93ZXIuaW5jbHVkZXMoXCJzcGF3biBlcnJvclwiKSB8fFxuICAgIGxvd2VyLmluY2x1ZGVzKFwicXVvdGFcIikgfHxcbiAgICBsb3dlci5pbmNsdWRlcyhcInlvdSd2ZSBoaXRcIikgfHxcbiAgICBsb3dlci5pbmNsdWRlcyhcInVuYXV0aG9yaXplZFwiKSB8fFxuICAgIGxvd2VyLmluY2x1ZGVzKFwiYXV0aGVudGljYXRpb25cIikgfHxcbiAgICBsb3dlci5pbmNsdWRlcyhcImludmFsaWQgb2F1dGhcIikgfHxcbiAgICBsb3dlci5pbmNsdWRlcyhcInBlcm1pc3Npb24gZGVuaWVkXCIpIHx8XG4gICAgbG93ZXIuaW5jbHVkZXMoXCJtYXhfdHVybnNcIikgfHxcbiAgICBsb3dlci5pbmNsdWRlcyhcIm1heC10dXJuc1wiKSB8fFxuICAgIGxvd2VyLmluY2x1ZGVzKFwic2NoZW1hXCIpIHx8XG4gICAgbG93ZXIuaW5jbHVkZXMoXCJtb2RlbF9lcnJvclwiKSB8fFxuICAgIGxvd2VyLmluY2x1ZGVzKFwibW9kZWwgZXJyb3JcIilcbiAgKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIHJldHVybiBsb3dlci5pbmNsdWRlcyhcImNsb3NlZCB3aXRob3V0IHJlc3VsdFwiKSB8fCAvXFxiKHJlc3VtZXx0cmFuc2NyaXB0KVxcYi8udGVzdChsb3dlcik7XG59XG5cbmV4cG9ydCBjbGFzcyBBbnRocm9waWNEaXNwYXRjaGVyIGltcGxlbWVudHMgUHJvdmlkZXJEaXNwYXRjaGVyIHtcbiAgLy8gdG9rZW5JZCDihpIg7ZKAIO2VreuqqS4gc3RhcnQoKS90b2tlbiDsnbTrsqTtirjroZwg7LGE7Jq064ukLlxuICBwcml2YXRlIHRva2VuUG9vbCA9IG5ldyBNYXA8bnVtYmVyLCBQb29sZWRUb2tlbj4oKTtcbiAgLy8gdG9rZW5JZCDrs4Qg7IKs7JqpIOy5tOyatO2EsChsZWFzdC11c2VkIFJSKS4gbmFtZSDsnbQg7JWE64uI6528IGlkIOq4sOykgCDigJQg7J2066aEIOy2qeuPjCDslYjsoIQuXG4gIHByaXZhdGUgcmVxdWVzdENvdW50cyA9IG5ldyBNYXA8bnVtYmVyLCBudW1iZXI+KCk7XG4gIHByaXZhdGUgcnJJbmRleCA9IDA7XG4gIC8vIGNsYXVkZSBzZXNzaW9uLWlkIOKGkiDqt7gg7IS47IWY7J2EIOunjOuToCDsmpTssq3snZgg7Zi47ZmY7YKkKCt0b2tlbklkL2xhc3RVc2VkQXQpLiByZXN1bWUgZWxpZ2liaWxpdHkg7YyQ7KCVLlxuICAvLyBUVEwgc3dlZXAgKyDthqDtgbAg7KCc6rGwIOyLnCDsoJXrpqzroZwg66y07ZWcIOymneqwgCDrsKnsp4AuXG4gIHByaXZhdGUgc2Vzc2lvbkNvbXBhdCA9IG5ldyBNYXA8c3RyaW5nLCBDb21wYXRFbnRyeT4oKTtcblxuICAvLyDilIDilIAgbGlmZWN5Y2xlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGFzeW5jIHN0YXJ0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIE9wZW5BSURpc3BhdGNoZXIuc3RhcnQoKSDsmYAg64+Z7J287ZWY6rKMIERCIOyXkOyEnCDquLDsobQgYW50aHJvcGljIO2GoO2BsOydhCBzZWxmLWJvb3RzdHJhcCDtlZzri6QuXG4gICAgLy8gKOyEnOuyhCDsnqzsi5zsnpEg7IucIE5PVElGWSDqsIAg7JWIIOyYpOuvgOuhnCDrtoDtirjsiqTtirjrnqnsnbQg7JeG7Jy866m0IO2SgOydtCDruYgg7LGE66GcIOuCqOuKlOuLpC4pXG4gICAgLy8g7J207ZuEIOuzgOqyveu2hOydgCB0b2tlbi1zdWJzY3JpYmVyIOqwgCBvblRva2VuQWRkZWQvVXBkYXRlZC9SZW1vdmVkIOuhnCDsoITri6ztlZzri6QuXG4gICAgLy8gZmluZEFjdGl2ZUJ5UHJvdmlkZXIg6rCAIGFjdGl2ZT10cnVlICsgcHJvdmlkZXIg7ZWE7YSw66W8IERCIOyXkOyEnCDsspjrpqwg4oaSIGluYWN0aXZlIOuKlCDslaDstIjsl5Ag7JWIIOyYqOuLpC5cbiAgICBjb25zdCB0b2tlbnMgPSBhd2FpdCBUb2tlbk1vZGVsLmZpbmRBY3RpdmVCeVByb3ZpZGVyKFwiQVwiLCBcImFudGhyb3BpY1wiKTtcbiAgICBmb3IgKGNvbnN0IHQgb2YgdG9rZW5zKSB7XG4gICAgICB0aGlzLnRva2VuUG9vbC5zZXQodC5pZCwge1xuICAgICAgICBpZDogdC5pZCxcbiAgICAgICAgbmFtZTogdC5uYW1lLFxuICAgICAgICBjcmVkZW50aWFsczogdC5jcmVkZW50aWFscyBhcyBBbnRocm9waWNDcmVkZW50aWFscyxcbiAgICAgIH0pO1xuICAgIH1cbiAgICBsb2dnZXIuaW5mbyhgYW50aHJvcGljIGRpc3BhdGNoZXIgc3RhcnRlZCAoJHt0aGlzLnRva2VuUG9vbC5zaXplfSB0b2tlbnMpYCk7XG4gIH1cblxuICBhc3luYyBzdG9wKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMudG9rZW5Qb29sLmNsZWFyKCk7XG4gICAgdGhpcy5zZXNzaW9uQ29tcGF0LmNsZWFyKCk7XG4gICAgLy8gbGlmZWN5Y2xlIOygle2VqTogUlIg7Lm07Jq07YSwL+yduOuNseyKpOuPhCDruYTsm4wgc3RvcOKGknN0YXJ0IOyerOuTseuhnSDsi5wg7J207KCEIOy5tOyatO2KuOqwgCDslYgg64Ko6rKMIO2VnOuLpC5cbiAgICB0aGlzLnJlcXVlc3RDb3VudHMuY2xlYXIoKTtcbiAgICB0aGlzLnJySW5kZXggPSAwO1xuICB9XG5cbiAgLy8g7ZiE7J6sIO2SgOydmCDthqDtgbAg7IiYKHN0YXJ0dXAg66Gc6re4L2hlYWx0aCDsmqkpLlxuICBnZXQgdG9rZW5Db3VudCgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnRva2VuUG9vbC5zaXplO1xuICB9XG5cbiAgLy8gREIgYWN0aXZlIGFudGhyb3BpYyDthqDtgbAg66qp66Gd7Jy866GcIO2SgOydhCDsnqzrj5nquLDtmZTtlZzri6QocGVyaW9kaWMgcmVjb25jaWxlL+yerOyXsOqysOyaqSkuXG4gIC8vIExJU1RFTi9OT1RJRlkg6rCAIOuBiuq4tCDrj5nslYgg7Jyg7Iuk65CcIOy2lOqwgC/sgq3soJwv67mE7Zmc7ISx7ZmU66W8IERCIOq4sOykgOycvOuhnCDri6Tsi5wg66ee7LaY64ukLlxuICAvLyDsnbTrsqTtirgg7ZW465Ok65+s66W8IOyerOyCrOyaqe2VtCBpZGVudGl0eS1jaGFuZ2UgLyBzZXNzaW9uQ29tcGF0IOygleumrOqwgCDsnbzqtIDrkJjqsowg7KCB7Jqp65CY64+E66GdIO2VnOuLpDpcbiAgLy8gIC0gREIg7JeQIOyXhuuKlOuNsCDtkoDsl5Ag7J6I64qUIO2GoO2BsCDihpIgb25Ub2tlblJlbW92ZWQgKGNvbXBhdCDsoJXrpqwg7Y+s7ZWoKVxuICAvLyAgLSBEQiDsl5Ag7J6I64qUIO2GoO2BsCDihpIg7Iug6rec66m0IG9uVG9rZW5BZGRlZCwg6riw7KG07J2066m0IG9uVG9rZW5VcGRhdGVkKGlkZW50aXR5IOuzgOqyvSDqsJDsp4ApXG4gIHJlcGxhY2VUb2tlbnMoXG4gICAgcm93czogQXJyYXk8eyBpZDogbnVtYmVyOyBuYW1lOiBzdHJpbmc7IGNyZWRlbnRpYWxzOiBBbnRocm9waWNDcmVkZW50aWFscyB9PixcbiAgKTogdm9pZCB7XG4gICAgY29uc3QgbmV4dCA9IG5ldyBTZXQocm93cy5tYXAoKHIpID0+IHIuaWQpKTtcbiAgICAvLyBvblRva2VuUmVtb3ZlZCDqsIAgdG9rZW5Qb29sIOydhCBkZWxldGUg7ZWY66+A66GcIOyInO2ajCDspJEg7IiY7KCV7J2EIO2UvO2VmOugpCDtgqTrpbwg66i87KCAIOyKpOuDheyDt+2VnOuLpC5cbiAgICBjb25zdCBjdXJyZW50SWRzID0gQXJyYXkuZnJvbSh0aGlzLnRva2VuUG9vbC5rZXlzKCkpO1xuICAgIGZvciAoY29uc3QgaWQgb2YgY3VycmVudElkcykge1xuICAgICAgaWYgKCFuZXh0LmhhcyhpZCkpIHRoaXMub25Ub2tlblJlbW92ZWQoaWQpO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IHIgb2Ygcm93cykge1xuICAgICAgaWYgKHRoaXMudG9rZW5Qb29sLmhhcyhyLmlkKSkgdGhpcy5vblRva2VuVXBkYXRlZChyLmlkLCByLm5hbWUsIHIuY3JlZGVudGlhbHMpO1xuICAgICAgZWxzZSB0aGlzLm9uVG9rZW5BZGRlZChyLmlkLCByLm5hbWUsIHIuY3JlZGVudGlhbHMpO1xuICAgIH1cbiAgfVxuXG4gIC8vIOKUgOKUgCB0b2tlbiBldmVudHMgKHRva2VuLXN1YnNjcmliZXIg6rCAIO2YuOy2nCkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgb25Ub2tlbkFkZGVkKGlkOiBudW1iZXIsIG5hbWU6IHN0cmluZywgY3JlZGVudGlhbHM6IEFudGhyb3BpY0NyZWRlbnRpYWxzKTogdm9pZCB7XG4gICAgdGhpcy50b2tlblBvb2wuc2V0KGlkLCB7IGlkLCBuYW1lLCBjcmVkZW50aWFscyB9KTtcbiAgfVxuICBvblRva2VuVXBkYXRlZChpZDogbnVtYmVyLCBuYW1lOiBzdHJpbmcsIGNyZWRlbnRpYWxzOiBBbnRocm9waWNDcmVkZW50aWFscyk6IHZvaWQge1xuICAgIC8vIGFjY2Vzcy9yZWZyZXNoIHRva2VuIHJvdGF0aW9uKOqwmeydgCDqs4TsoJUp7J2066m0IGluLXBsYWNlIOqwseyLoOycvOuhnCDshLjshZjsnYQg7Jyg7KeA7ZWc64ukLlxuICAgIC8vIO2VmOyngOunjCBhY2NvdW50VXVpZCDqsIAg67CU64CQIOyerOuhnOq3uOyduC/qs4TsoJUg6rWQ7LK066m0LCDqsJnsnYAgdG9rZW5JZCDslYTrnpgg7IK07JWE64Ko7J2AIHNlc3Npb25Db21wYXQg6rCAXG4gICAgLy8g7J207KCEIOqzhOygleycvOuhnCDrp4zrk6Agc2Vzc2lvbi1pZCDrpbwg7IOIIOqzhOyglSBjcmVkZW50aWFscyDroZwgcmVzdW1lIOyLnO2CpOuKlCDqsqnrpqwg7JyE67CY7J20IOuQnOuLpC5cbiAgICAvLyBPcGVuQUkgZGlzcGF0Y2hlciDqsIAgbG9naW4gaWRlbnRpdHkg67OA6rK97J2EIHdvcmtlciByZXN0YXJ0IOuhnCDqtazrtoTtlZjripQg6rKD6rO8IOuPmeydvO2VnCDsspjrpqw6XG4gICAgLy8gaWRlbnRpdHkg6rCAIOuwlOuAjOuptCDqt7ggdG9rZW5JZCDsnZgg7IS47IWYIGNvbXBhdCDsnYQg7Y+Q6riw7ZW0IOydtO2bhCDtmLjstpzsnbQgY29sZCDroZwg65ao7Ja07KeA6rKMIO2VnOuLpC5cbiAgICBjb25zdCBleGlzdGluZyA9IHRoaXMudG9rZW5Qb29sLmdldChpZCk7XG4gICAgY29uc3QgaWRlbnRpdHlDaGFuZ2VkID1cbiAgICAgIGV4aXN0aW5nICE9PSB1bmRlZmluZWQgJiYgZXhpc3RpbmcuY3JlZGVudGlhbHMuYWNjb3VudFV1aWQgIT09IGNyZWRlbnRpYWxzLmFjY291bnRVdWlkO1xuICAgIGlmIChpZGVudGl0eUNoYW5nZWQpIHRoaXMuY2xlYXJTZXNzaW9uQ29tcGF0Rm9yKGlkKTtcbiAgICB0aGlzLnRva2VuUG9vbC5zZXQoaWQsIHsgaWQsIG5hbWUsIGNyZWRlbnRpYWxzIH0pO1xuICB9XG4gIG9uVG9rZW5SZW1vdmVkKGlkOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnRva2VuUG9vbC5kZWxldGUoaWQpO1xuICAgIHRoaXMucmVxdWVzdENvdW50cy5kZWxldGUoaWQpO1xuICAgIC8vIOydtCDthqDtgbDsnLzroZwg66eM65OgIOyEuOyFmOydmCBjb21wYXQg7JeU7Yq466as64+EIOygleumrC5cbiAgICB0aGlzLmNsZWFyU2Vzc2lvbkNvbXBhdEZvcihpZCk7XG4gIH1cblxuICAvLyDtirnsoJUgdG9rZW5JZCDroZwg66eM65OgIOuqqOuToCBzZXNzaW9uQ29tcGF0IOyXlO2KuOumrCDtj5DquLAo7Yag7YGwIOygnOqxsCAvIGlkZW50aXR5IOuzgOqyvSDsi5wg6rO17Ya1KS5cbiAgcHJpdmF0ZSBjbGVhclNlc3Npb25Db21wYXRGb3IodG9rZW5JZDogbnVtYmVyKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCBbc2lkLCBlXSBvZiB0aGlzLnNlc3Npb25Db21wYXQpIHtcbiAgICAgIGlmIChlLnRva2VuSWQgPT09IHRva2VuSWQpIHRoaXMuc2Vzc2lvbkNvbXBhdC5kZWxldGUoc2lkKTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgdG9rZW4gc2VsZWN0aW9uIChsZWFzdC11c2VkIFJSLCB0b2tlbklkIOq4sOykgCkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgcHJpdmF0ZSBjb3VudE9mKGlkOiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3RDb3VudHMuZ2V0KGlkKSA/PyAwO1xuICB9XG5cbiAgLy8gY29sZCDqsr3roZw6IGxlYXN0LXVzZWQgUlIg66GcIO2GoO2BsOydhCDqs6Drpbjri6Qo64+Z7KCQ7J2066m0IHJySW5kZXgg66GcIO2ajOyghCkuXG4gIHByaXZhdGUgc2VsZWN0VG9rZW4oKTogUG9vbGVkVG9rZW4gfCBudWxsIHtcbiAgICBjb25zdCByb3dzID0gWy4uLnRoaXMudG9rZW5Qb29sLnZhbHVlcygpXTtcbiAgICBpZiAocm93cy5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuICAgIGNvbnN0IG1pbkNvdW50ID0gTWF0aC5taW4oLi4ucm93cy5tYXAoKHIpID0+IHRoaXMuY291bnRPZihyLmlkKSkpO1xuICAgIGNvbnN0IGlkbGUgPSByb3dzLmZpbHRlcigocikgPT4gdGhpcy5jb3VudE9mKHIuaWQpID09PSBtaW5Db3VudCk7XG4gICAgY29uc3QgcGlja2VkID0gaWRsZVt0aGlzLnJySW5kZXggJSBpZGxlLmxlbmd0aF0hO1xuICAgIHRoaXMucnJJbmRleCsrO1xuICAgIHJldHVybiB0aGlzLmNoYXJnZShwaWNrZWQpO1xuICB9XG5cbiAgLy8gcmVzdW1lIOqyveuhnDog7IS47IWY7J2EIOunjOuToCDthqDtgbDsnYQgaWQg66GcIOqzoOyglSDshKDtg53tlZzri6QuIO2SgOyXkCDsl4bsnLzrqbQgbnVsbCjihpIgY29sZCBmYWxsYmFjaykuXG4gIC8vIHJlc3VtZSDshLjshZjsnYAg6re4IO2GoO2BsOydmCBDTEFVREVfQ09ORklHX0RJUiDsl5AgdHJhbnNjcmlwdCDqsIAg66y27JesIOyeiOyWtCwg64uk66W4IO2GoO2BsOycvOuhnCByZXN1bWUg7ZWY66m0XG4gIC8vIHRyYW5zY3JpcHQg6rCAIOyXhuyWtCDquajsp4Tri6QuIOq3uOuemOyEnCBSUiDsnYQg7JOw7KeAIOyViuqzoCDshozsnKAg7Yag7YGw7J2EIOq3uOuMgOuhnCDsnqHslYTslbwg7ZWc64ukLlxuICBwcml2YXRlIHBpY2tUb2tlbihpZDogbnVtYmVyKTogUG9vbGVkVG9rZW4gfCBudWxsIHtcbiAgICBjb25zdCB0b2tlbiA9IHRoaXMudG9rZW5Qb29sLmdldChpZCk7XG4gICAgcmV0dXJuIHRva2VuID8gdGhpcy5jaGFyZ2UodG9rZW4pIDogbnVsbDtcbiAgfVxuXG4gIC8vIOyEoO2DneuQnCDthqDtgbDsnZgg7IKs7JqpIOy5tOyatO2KuOulvCBhd2FpdCDsoITsl5Ag7ISg67CY7JiBKOuPmeyLnCDsmpTssq3snbQg64uk66W4IO2GoO2BsOydhCDqs6DrpbTrj4TroZ0pLlxuICBwcml2YXRlIGNoYXJnZSh0b2tlbjogUG9vbGVkVG9rZW4pOiBQb29sZWRUb2tlbiB7XG4gICAgdGhpcy5yZXF1ZXN0Q291bnRzLnNldCh0b2tlbi5pZCwgdGhpcy5jb3VudE9mKHRva2VuLmlkKSArIDEpO1xuICAgIHJldHVybiB0b2tlbjtcbiAgfVxuXG4gIC8vIOunjOujjOuQnCBjb21wYXQg7JeU7Yq466asIOygleumrChsYXp5IHN3ZWVwIOKAlCBnZW5lcmF0ZSDsp4TsnoUg7IucIO2YuOy2nCkuXG4gIHByaXZhdGUgc3dlZXBTZXNzaW9uQ29tcGF0KCk6IHZvaWQge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgZm9yIChjb25zdCBbc2lkLCBlXSBvZiB0aGlzLnNlc3Npb25Db21wYXQpIHtcbiAgICAgIGlmIChub3cgLSBlLmxhc3RVc2VkQXQgPiBTRVNTSU9OX0NPTVBBVF9UVExfTVMpIHRoaXMuc2Vzc2lvbkNvbXBhdC5kZWxldGUoc2lkKTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgZ2VuZXJhdGUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgYXN5bmMgZ2VuZXJhdGUocmVxOiBHZW5lcmF0ZVJlcXVlc3QpOiBQcm9taXNlPEdlbmVyYXRlUmVzdWx0PiB7XG4gICAgcmV0dXJuIHRoaXMucnVuKHJlcSwgKCkgPT4ge30pO1xuICB9XG5cbiAgYXN5bmMgZ2VuZXJhdGVTdHJlYW0ocmVxOiBHZW5lcmF0ZVJlcXVlc3QsIGNiOiBHZW5lcmF0ZVN0cmVhbUNhbGxiYWNrcyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLnJ1bihyZXEsIGNiLm9uRGVsdGEpO1xuICAgICAgY2Iub25UaHJlYWRJZD8uKHJlc3VsdC50aHJlYWRDb29yZC50aHJlYWRJZCk7XG4gICAgICBjYi5vbkNvbXBsZXRlKHJlc3VsdCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY2Iub25FcnJvcihlIGFzIEVycm9yKTtcbiAgICB9XG4gIH1cblxuICAvLyBnZW5lcmF0ZS9nZW5lcmF0ZVN0cmVhbSDqs7XthrUg7Iuk7ZaJLiBvbkRlbHRhIOuKlCDsiqTtirjrprzsnbTrqbQg7KCQ7KeEIOuwqey2nCwg67mE7Iqk7Yq466a87J2066m0IG5vLW9wLlxuICBwcml2YXRlIGFzeW5jIHJ1bihyZXE6IEdlbmVyYXRlUmVxdWVzdCwgb25EZWx0YTogKHQ6IHN0cmluZykgPT4gdm9pZCk6IFByb21pc2U8R2VuZXJhdGVSZXN1bHQ+IHtcbiAgICB0aGlzLnN3ZWVwU2Vzc2lvbkNvbXBhdCgpO1xuXG4gICAgLy8gbW9kZWwg7KCV6rec7ZmU66W8IGRpc3BhdGNoZXIg7KeE7J6F7KCQ7JeQ7IScIO2VnCDrsogg7ZWc64ukOiBjb21wYXQgLyByZXN1bHQubW9kZWwgLyBydW5DbGF1ZGVTZXNzaW9uIOyghOu2gFxuICAgIC8vIGNhbm9uaWNhbChwcmVmaXgg7JeG64qUIGNvc3Qg7YKkKSDsnYQg7JOw6rKMIO2GteydvC4g66+47KeA7KCV7J2066m0IEFOVEhST1BJQ19ERUZBVUxUX01PREVMIOKAlCBxZ3JpZC5kaXNwYXRjaGVyXG4gICAgLy8g7J2YIFwic29ubmV0XCIg67OE7LmtIOyasO2ajCDssKjri6guIOygleq3nO2ZlCDqt5zsuZnsnYAgZmFsbGJhY2sg6rK966Gc7JmAIOqzteycoChjYW5vbmljYWxBbnRocm9waWNNb2RlbCkuXG4gICAgY29uc3QgbW9kZWwgPSBjYW5vbmljYWxBbnRocm9waWNNb2RlbChyZXEubW9kZWwpO1xuICAgIGNvbnN0IGpzb25TY2hlbWEgPVxuICAgICAgcmVxLm91dHB1dFNjaGVtYSAhPT0gdW5kZWZpbmVkID8gSlNPTi5zdHJpbmdpZnkocmVxLm91dHB1dFNjaGVtYSkgOiB1bmRlZmluZWQ7XG4gICAgY29uc3QgY29tcGF0ID0gY29tcGF0aWJpbGl0eUtleSh7IHN5c3RlbTogcmVxLnN5c3RlbVByb21wdCwgbW9kZWwsIG91dHB1dFNjaGVtYToganNvblNjaGVtYSB9KTtcblxuICAgIC8vIOKUgOKUgCByZXN1bWUgZWxpZ2liaWxpdHkgKyDthqDtgbAg7ISg7YOdIChyZXN1bWUg64qUIOyEuOyFmCDshozsnKAg7Yag7YGw7Jy866Gc66eMKSDilIDilIBcbiAgICAvLyAgcmVzdW1lIOqwgCDshLHrpr3tlZjroKTrqbQg7IS4IOyhsOqxtOydtCDrqqjrkZAg7LC47J207Ja07JW8IO2VnOuLpDpcbiAgICAvLyAgIChhKSByZXVzZS50aHJlYWRJZCDsnZgg7KCA7J6l65CcIO2YuO2ZmO2CpCA9PSDsnbTrsogg7JqU7LKtIO2YuO2ZmO2CpCAo66qo6424L3N5c3RlbS9zY2hlbWEg67OA6rK9IOyYpOyXvCDrsKnsp4ApXG4gICAgLy8gICAoYikg7KCA7J6l65CcIHRva2VuSWQgPT0gcmV1c2Uud29ya2VySWQgKGNvb3JkIOyZleuztSDrrLTqsrDshLEpIOydtOqzoFxuICAgIC8vICAgKGMpIOq3uCDthqDtgbDsnbQg7JWE7KeBIO2SgOyXkCDsgrTslYTsnojsnYwuXG4gICAgLy8gIO2VmOuCmOudvOuPhCDslrTquIvrgpjrqbQgcmVzdW1lIOu2iOqwgCDihpIgY29sZCDroZwg65ao7Ja07KeA6rOgIFJSIOuhnCDsg4gg7Yag7YGw7J2EIOqzoOuluOuLpC5cbiAgICAvLyAgcmVzdW1lIOydvCDrlYzripQg67CY65Oc7IucIOyEuOyFmOydhCDrp4zrk6Ag6re4IO2GoO2BsChzdG9yZWQudG9rZW5JZCnsnYQg6rOg7KCVIOyEoO2Dne2VnOuLpCDigJQgUlIg66GcIOuLpOuluCDthqDtgbDsnYQg7J6h7Jy866m0XG4gICAgLy8gIOq3uCDthqDtgbDsnZggQ0xBVURFX0NPTkZJR19ESVIg7JeQIHRyYW5zY3JpcHQg6rCAIOyXhuyWtCByZXN1bWUg7J20IOq5qOynhOuLpC5cbiAgICBjb25zdCBjYW5kaWRhdGUgPSByZXEucmV1c2U/LnRocmVhZElkO1xuICAgIGNvbnN0IHN0b3JlZCA9IGNhbmRpZGF0ZSAhPT0gdW5kZWZpbmVkID8gdGhpcy5zZXNzaW9uQ29tcGF0LmdldChjYW5kaWRhdGUpIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IG93bmVyTWF0Y2hlcyA9IHN0b3JlZCAhPT0gdW5kZWZpbmVkICYmIHN0b3JlZC50b2tlbklkID09PSByZXEucmV1c2U/LndvcmtlcklkO1xuICAgIGNvbnN0IHJlc3VtZVRva2VuID1cbiAgICAgIHN0b3JlZD8uY29tcGF0ID09PSBjb21wYXQgJiYgb3duZXJNYXRjaGVzID8gdGhpcy5waWNrVG9rZW4oc3RvcmVkLnRva2VuSWQpIDogbnVsbDtcblxuICAgIGNvbnN0IGlzUmVzdW1lID0gcmVzdW1lVG9rZW4gIT09IG51bGw7XG4gICAgY29uc3QgcmVzdW1lU2Vzc2lvbklkID0gaXNSZXN1bWUgPyBjYW5kaWRhdGUgOiB1bmRlZmluZWQ7XG5cbiAgICAvLyByZXN1bWUg66m0IOyGjOycoCDthqDtgbAg6rOg7KCVLCDslYTri4jrqbQgY29sZCBSUiDshKDtg50uXG4gICAgY29uc3QgdG9rZW4gPSByZXN1bWVUb2tlbiA/PyB0aGlzLnNlbGVjdFRva2VuKCk7XG4gICAgaWYgKCF0b2tlbikgdGhyb3cgbmV3IEVycm9yKFwiTm8gYW50aHJvcGljIHRva2VucyBhdmFpbGFibGVcIik7XG5cbiAgICAvLyDrnb0g7IaM7Jyg6raMOlxuICAgIC8vICBzZXNzaW9uLWlkIOuLqOychCDsp4HroKztmZQg65297J2AICoqVTQgcnVuQ2xhdWRlU2Vzc2lvbiDsnbQg64uo64+FIOyGjOycoCoq7ZWc64ukLiBkaXNwYXRjaGVyIOqwgCDsl6zquLDshJxcbiAgICAvLyAgd2l0aFNlc3Npb25Mb2NrKHJlc3VtZVNlc3Npb25JZCkg7Jy866GcIO2VnCDrsogg642UIOqwkOyLuOuptCwgZXhlYygpIOyViOydmCBydW5DbGF1ZGVTZXNzaW9uIOydtCDqsJnsnYBcbiAgICAvLyAgc2Vzc2lvbi1pZCDroZwg65297JeQIOyerOynhOyehe2VmOuKlOuNsCB3aXRoU2Vzc2lvbkxvY2sg7J2AIHJlZW50cmFudCDqsIAg7JWE64uI6528IOyekOq4sCDsnpDsi6DsnYQg6riw64uk66as64qUXG4gICAgLy8gIGRlYWRsb2NrIOydtCDrgpzri6Qo7LKrIHJlc3VtZSDtmLjstpzsnbQg7JiB6rWsIOygleyngCkuIFUxL1U1IGRpc3BhdGNoZXIg64qUIHRva2VuIG93bmVyc2hpcCAvIGNvbXBhdCDrp4xcbiAgICAvLyAg7YyQ7KCV7ZWY6rOgLCB0cmFuc2NyaXB0IOyngeugrO2ZlOuKlCBVNCDrnb3sl5Ag7JyE7J6E7ZWc64ukLlxuICAgIC8vICBjb2xkIOyyqy3tmLjstpwg64+Z7Iuc7ISx7J2EIGRpc3BhdGNoZXIg6rCAIOuUsOuhnCDrp4nsp4Ag7JWK64qUIOq3vOqxsDogc2Vzc2lvbktleSDripQg7ISc67KE66GcIOyYpOyngCDslYrslYQo7YG06528XG4gICAgLy8gIHN0b3JlIOq0gOumrCwg7KKM7ZGc66eMIO2ajOyGoSkgY29sZCDrs4Qg65297J2YIOyeheugpSDsnpDssrTqsIAg7JeG6rOgLCDtmITsnqwgSUYvb3duZXIg64qUIExMTSDtmLjstpzsnYQg7KCE67aAIOyInOywqOuhnFxuICAgIC8vICDrj4zroKQg64+Z7IucIOyyqyDtmLjstpzsnbQg64KY7KeAIOyViuuKlOuLpCgyMDI2LTA2LTA2IEwyMS9MMjQ4KS4g67OR66Cs7ZmUL+yEnOuyhOyghOuLrOuhnCDrsJTrgIzrqbQg7J6s6rKA7YagLlxuICAgIGNvbnN0IGV4ZWMgPSBhc3luYyAoKTogUHJvbWlzZTxHZW5lcmF0ZVJlc3VsdD4gPT4ge1xuICAgICAgLy8g66eM66OMIOyehOuwlSDthqDtgbAgcHJlZW1wdGl2ZSByZWZyZXNoKOq4sOyhtCBzdGFuZGFsb25lIOqyveuhnOyZgCDrj5nsnbwpLiDsi6TtjKjtlbTrj4Qg7KeE7ZaJLlxuICAgICAgbGV0IGFjY2Vzc1Rva2VuID0gdG9rZW4uY3JlZGVudGlhbHMuYWNjZXNzVG9rZW47XG4gICAgICBjb25zdCBleHBpcmVzQXQgPSBnZXRFeHBpcmVzQXQodG9rZW4uY3JlZGVudGlhbHMpO1xuICAgICAgaWYgKFxuICAgICAgICBleHBpcmVzQXQgJiZcbiAgICAgICAgZXhwaXJlc0F0IC0gRGF0ZS5ub3coKSA8IFJFRlJFU0hfU0FGRVRZX01TICYmXG4gICAgICAgIGdldFJlZnJlc2hUb2tlbih0b2tlbi5jcmVkZW50aWFscylcbiAgICAgICkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHsgUWdyaWRGcmFtZSB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vLi4vLi4vYXBwbGljYXRpb24vcWdyaWQvcWdyaWQuZnJhbWVcIik7XG4gICAgICAgICAgLy8gcHJvdmlkZXIg66W8IOuwmOuTnOyLnCDssYTsm4zslbwgcmVmcmVzaFRva2VuIOuCtOu2gCBUb2tlbk1vZGVsLnNhdmUg6rCAIOyEseqzte2VnOuLpC5cbiAgICAgICAgICAvLyByZWZyZXNoVG9rZW4oVG9rZW5TdWJzZXRBKSDripQgaWQvcHJvdmlkZXIvY3JlZGVudGlhbHMvbmFtZSDrp4wg7IKs7Jqp7ZWY66+A66GcIOq3uCA06rCc66eMIOuEmOq4tOuLpC5cbiAgICAgICAgICBhY2Nlc3NUb2tlbiA9IGF3YWl0IFFncmlkRnJhbWUucmVmcmVzaFRva2VuKHtcbiAgICAgICAgICAgIGlkOiB0b2tlbi5pZCxcbiAgICAgICAgICAgIHByb3ZpZGVyOiBBTlRIUk9QSUNfUFJPVklERVIsXG4gICAgICAgICAgICBuYW1lOiB0b2tlbi5uYW1lLFxuICAgICAgICAgICAgY3JlZGVudGlhbHM6IHRva2VuLmNyZWRlbnRpYWxzLFxuICAgICAgICAgIH0gYXMgUGFyYW1ldGVyczx0eXBlb2YgUWdyaWRGcmFtZS5yZWZyZXNoVG9rZW4+WzBdKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgIGxvZ2dlci53YXJuKGByZWZyZXNoIGZhaWxlZCBmb3IgJHt0b2tlbi5uYW1lfTogJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjb25zdCBydW5TZXNzaW9uID0gKHJlc3VtZTogYm9vbGVhbikgPT4ge1xuICAgICAgICBsb2dnZXIuaW5mbyhg4oaSICR7dG9rZW4ubmFtZX0gKG1vZGVsOiAke21vZGVsfSwgJHtyZXN1bWUgPyBcInJlc3VtZVwiIDogXCJjb2xkXCJ9KWApO1xuICAgICAgICByZXR1cm4gcnVuQ2xhdWRlU2Vzc2lvbihcbiAgICAgICAgICB7XG4gICAgICAgICAgICB0b2tlbklkOiB0b2tlbi5pZCxcbiAgICAgICAgICAgIHRva2VuOiBhY2Nlc3NUb2tlbixcbiAgICAgICAgICAgIG1vZGVsLFxuICAgICAgICAgICAgc3lzdGVtOiByZXEuc3lzdGVtUHJvbXB0LFxuICAgICAgICAgICAganNvblNjaGVtYSxcbiAgICAgICAgICAgIGVmZm9ydDogcmVxLmVmZm9ydCxcbiAgICAgICAgICAgIHRpbWVvdXRNczogREVGQVVMVF9USU1FT1VUX01TLFxuICAgICAgICAgICAgY29sZEhpc3Rvcnk6IHJlc3VtZSA/IHVuZGVmaW5lZCA6IHJlcS5jb2xkSGlzdG9yeSxcbiAgICAgICAgICAgIGlucHV0OiByZXN1bWUgJiYgcmVxLnJldXNlSW5wdXQgPyByZXEucmV1c2VJbnB1dCA6IHJlcS5jb2xkSW5wdXQsXG4gICAgICAgICAgICByZXN1bWVTZXNzaW9uSWQ6IHJlc3VtZSA/IHJlc3VtZVNlc3Npb25JZCA6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGFib3J0U2lnbmFsOiByZXEuYWJvcnRTaWduYWwsXG4gICAgICAgICAgfSxcbiAgICAgICAgICBvbkRlbHRhLFxuICAgICAgICApO1xuICAgICAgfTtcblxuICAgICAgbGV0IHNlc3Npb247XG4gICAgICB0cnkge1xuICAgICAgICBzZXNzaW9uID0gYXdhaXQgcnVuU2Vzc2lvbihpc1Jlc3VtZSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmICghaXNSZXN1bWUgfHwgIWlzUmV0cnlhYmxlUmVzdW1lRmFpbHVyZShlKSkgdGhyb3cgZTtcbiAgICAgICAgaWYgKGNhbmRpZGF0ZSkgdGhpcy5zZXNzaW9uQ29tcGF0LmRlbGV0ZShjYW5kaWRhdGUpO1xuICAgICAgICBsb2dnZXIud2FybihgcmVzdW1lIGZhaWxlZCBmb3IgJHt0b2tlbi5uYW1lfTsgcmV0cnlpbmcgY29sZCBvbmNlOiAkeyhlIGFzIEVycm9yKS5tZXNzYWdlfWApO1xuICAgICAgICBzZXNzaW9uID0gYXdhaXQgcnVuU2Vzc2lvbihmYWxzZSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChzZXNzaW9uLnF1b3RhRXhoYXVzdGVkKSB0aHJvdyBuZXcgRXJyb3IoYHF1b3RhIGV4aGF1c3RlZCAoJHt0b2tlbi5uYW1lfSlgKTtcbiAgICAgIGlmIChzZXNzaW9uLmlzRXJyb3IpIHtcbiAgICAgICAgLy8gdGV4dCDqsIAg67mE7Ja0IOyeiOydhCDsiJgg7J6I7Jy866+A66GcIOynhOuLqCDqsIDriqXtlZwg7ZiV7YOc66GcIOuplOyLnOyngOulvCDrp4zrk6Dri6Qoc3VidHlwZSDrk7HsnYAgcmF3IOuhnOq3uCDssLjsobApLlxuICAgICAgICBjb25zdCBkZXRhaWwgPSBzZXNzaW9uLnRleHQgfHwgYGVtcHR5IHRleHQsIG91dHB1dFRva2Vucz0ke3Nlc3Npb24udXNhZ2Uub3V0cHV0VG9rZW5zfWA7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgY2xhdWRlIGVycm9yICgke3Rva2VuLm5hbWV9KTogJHtkZXRhaWx9YCk7XG4gICAgICB9XG5cbiAgICAgIC8vIOuwnOq4ie2VnCBzZXNzaW9uLWlkIOydmCDtmLjtmZjtgqQg7KCA7J6lKOuLpOydjCByZXN1bWUg7YyQ7KCV7JqpKS4gY29tcGF0IOuKlCDsl6zquLAgZGlzcGF0Y2hlci1sb2NhbCDsl5Drp4xcbiAgICAgIC8vIOuztOyhtO2VnOuLpCDigJQgdGhyZWFkQ29vcmQuc3lzdGVtSGFzaCDroZwg7JmV67O17ZWY7KeAIOyViuuKlOuLpC4g7IOB7JyEIGlzc3VlQ29udkNvbnRleHQg6rCAIHN5c3RlbUhhc2gg66W8XG4gICAgICAvLyDrlLDroZwg7LGE7Jqw7KeA66eMIEFudGhyb3BpYyByZXN1bWUgZWxpZ2liaWxpdHkg64qUIOydtCBzZXNzaW9uQ29tcGF0IOqwgCDri6jrj4Ug7YyQ7KCVLlxuICAgICAgdGhpcy5zZXNzaW9uQ29tcGF0LnNldChzZXNzaW9uLnNlc3Npb25JZCwge1xuICAgICAgICBjb21wYXQsXG4gICAgICAgIHRva2VuSWQ6IHRva2VuLmlkLFxuICAgICAgICBsYXN0VXNlZEF0OiBEYXRlLm5vdygpLFxuICAgICAgfSk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIHRleHQ6IHNlc3Npb24udGV4dCxcbiAgICAgICAgdG9rZW5OYW1lOiB0b2tlbi5uYW1lLFxuICAgICAgICB1c2FnZTogc2Vzc2lvbi51c2FnZSxcbiAgICAgICAgZHVyYXRpb25Nczogc2Vzc2lvbi5kdXJhdGlvbk1zLFxuICAgICAgICBjb3N0VXNkOiBzZXNzaW9uLmNvc3RVc2QsXG4gICAgICAgIG1vZGVsLFxuICAgICAgICAvLyBzeXN0ZW1IYXNoIOuKlCDsg4HsnIQgaXNzdWVDb252Q29udGV4dCDqsIAg7LGE7Jq064ukKOyXrOq4sOyEoCDruYTsmrTri6QpLlxuICAgICAgICB0aHJlYWRDb29yZDoge1xuICAgICAgICAgIHdvcmtlcklkOiBtYWtlQW50aHJvcGljV29ya2VySWQodG9rZW4uaWQpLFxuICAgICAgICAgIHRocmVhZElkOiBzZXNzaW9uLnNlc3Npb25JZCxcbiAgICAgICAgICBlcG9jaDogMCxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfTtcblxuICAgIHJldHVybiBleGVjKCk7XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW9DQSxNQUFNLFNBQVMsVUFBVSxDQUFDLFNBQVMsdUJBQXVCLENBQUM7QUFFM0QsTUFBTSxxQkFBcUI7QUFFM0IsTUFBTSxvQkFBb0I7QUFXMUIsTUFBTSxxQkFBcUI7QUFJM0IsTUFBTSx3QkFBd0IsTUFBVTtBQVF4QyxTQUFTLHlCQUF5QixPQUF5QjtDQUV6RCxNQUFNLFNBRFUsaUJBQWlCLFFBQVEsTUFBTSxVQUFVLE9BQU8sTUFBTSxFQUNoRCxhQUFhO0FBQ25DLEtB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
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import "../../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { ANTHROPIC_CLAUDE_CWD, ANTHROPIC_DISALLOWED_TOOLS, anthropicConfigDir, canonicalAnthropicModel } from "./anthropic-constants.js";
|
|
3
|
+
import { buildStreamJsonInput, handleStreamJsonLine } from "./stream-json-adapter.js";
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
7
|
+
//#region src/utils/providers/anthropic/claude-session.ts
|
|
8
|
+
/**
|
|
9
|
+
* claude-session — Claude CLI 세션 spawn + QgridThreadCoord 매핑 + per-session 락.
|
|
10
|
+
*
|
|
11
|
+
* 멀티턴: 첫 호출 `--session-id <uuid>`, 후속 `--resume <uuid>`. 입력은 U2 어댑터가 만든
|
|
12
|
+
* JSONL 을 stdin 으로 흘리고(`--input-format stream-json`), 출력은 U3 파서로 처리한다.
|
|
13
|
+
*
|
|
14
|
+
* 핵심 계약:
|
|
15
|
+
* - coord 는 임의 저장이 아니라 기존 QgridThreadCoord{workerId,threadId,epoch,systemHash} 에
|
|
16
|
+
* 매핑한다. threadId=세션 uuid, epoch=0 고정(worker restart 개념 없음), workerId=token 기반
|
|
17
|
+
* 안정 합성, systemHash=요청 system 해시. issueConvContext/decideConvRouting 과 round-trip.
|
|
18
|
+
* - resume eligibility 는 systemHash 만으로 부족 → 확장 호환키(system+model+structured 모드)로
|
|
19
|
+
* 판정. 불일치면 cold fallback.
|
|
20
|
+
* - 같은 세션의 동시 turn 은 per-session 락(session-id 단위 직렬화)으로 transcript 오염 방지.
|
|
21
|
+
* - stream-json 직렬화 시 SDK envelope(uuid/session_id/parent_tool_use_id) decorate.
|
|
22
|
+
* - structured output 일 때 출력 파서에 { structuredOutput: true } 전달(없으면 streamObject 깨짐).
|
|
23
|
+
*
|
|
24
|
+
* ⚠️ 첫 호출 동시성: resume 가 아닌 "첫 호출"은 여기서 새 랜덤 session-id 를 발급하므로, 같은
|
|
25
|
+
* qgrid sessionKey 에 대한 두 동시 첫 호출은 서로 다른 id 로 둘 다 실행된다. 현재 IF/owner scope 는
|
|
26
|
+
* LLM 호출을 순차 실행하고 sessionKey 를 서버로 보내지 않으므로, dispatcher 는 cold 락을 두지 않는다.
|
|
27
|
+
* 병렬 첫 호출을 서버가 지원하게 되면 sessionKey 전달/락 설계부터 다시 잡아야 한다.
|
|
28
|
+
*/
|
|
29
|
+
function compatibilityKey(opts) {
|
|
30
|
+
const schemaPart = opts.outputSchema && opts.outputSchema.length > 0 ? opts.outputSchema : "text";
|
|
31
|
+
return createHash("sha256").update(`${opts.system ?? ""}\0${opts.model}\0${schemaPart}`).digest("hex").slice(0, 16);
|
|
32
|
+
}
|
|
33
|
+
const sessionChains = /* @__PURE__ */ new Map();
|
|
34
|
+
async function withSessionLock(sessionId, fn) {
|
|
35
|
+
const next = (sessionChains.get(sessionId) ?? Promise.resolve()).then(fn, fn);
|
|
36
|
+
const tail = next.then(() => {}, () => {});
|
|
37
|
+
sessionChains.set(sessionId, tail);
|
|
38
|
+
try {
|
|
39
|
+
return await next;
|
|
40
|
+
} finally {
|
|
41
|
+
if (sessionChains.get(sessionId) === tail) sessionChains.delete(sessionId);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function makeAnthropicWorkerId(tokenId) {
|
|
45
|
+
return tokenId;
|
|
46
|
+
}
|
|
47
|
+
function decorateAndSerialize(lines, sessionId) {
|
|
48
|
+
return lines.map((line) => JSON.stringify({
|
|
49
|
+
...line,
|
|
50
|
+
session_id: sessionId,
|
|
51
|
+
uuid: randomUUID(),
|
|
52
|
+
parent_tool_use_id: null
|
|
53
|
+
})).join("\n") + "\n";
|
|
54
|
+
}
|
|
55
|
+
function ensureConfigDir(tokenId) {
|
|
56
|
+
const dir = anthropicConfigDir(tokenId);
|
|
57
|
+
mkdirSync(dir, { recursive: true });
|
|
58
|
+
writeFileSync(`${dir}/.claude.json`, "{}");
|
|
59
|
+
writeFileSync(`${dir}/settings.json`, "{}");
|
|
60
|
+
return dir;
|
|
61
|
+
}
|
|
62
|
+
let cwdEnsured = false;
|
|
63
|
+
function ensureCwd() {
|
|
64
|
+
if (cwdEnsured) return;
|
|
65
|
+
mkdirSync(`${ANTHROPIC_CLAUDE_CWD}/.claude`, { recursive: true });
|
|
66
|
+
writeFileSync(`${ANTHROPIC_CLAUDE_CWD}/.claude/settings.json`, "{}");
|
|
67
|
+
cwdEnsured = true;
|
|
68
|
+
}
|
|
69
|
+
function buildClaudeArgs(opts) {
|
|
70
|
+
const useStructured = Boolean(opts.jsonSchema && opts.jsonSchema.length > 0);
|
|
71
|
+
const args = [
|
|
72
|
+
"-p",
|
|
73
|
+
...useStructured ? [
|
|
74
|
+
"--tools",
|
|
75
|
+
"",
|
|
76
|
+
"--allowed-tools",
|
|
77
|
+
"StructuredOutput"
|
|
78
|
+
] : ["--tools", ""],
|
|
79
|
+
"--disallowedTools",
|
|
80
|
+
...ANTHROPIC_DISALLOWED_TOOLS,
|
|
81
|
+
"--input-format",
|
|
82
|
+
"stream-json",
|
|
83
|
+
"--output-format",
|
|
84
|
+
"stream-json",
|
|
85
|
+
"--verbose",
|
|
86
|
+
"--max-turns",
|
|
87
|
+
useStructured ? "2" : "1",
|
|
88
|
+
"--permission-mode",
|
|
89
|
+
"bypassPermissions",
|
|
90
|
+
"--setting-sources",
|
|
91
|
+
"project",
|
|
92
|
+
"--model",
|
|
93
|
+
opts.model,
|
|
94
|
+
"--system-prompt",
|
|
95
|
+
opts.system ?? "",
|
|
96
|
+
"--thinking",
|
|
97
|
+
"disabled",
|
|
98
|
+
"--effort",
|
|
99
|
+
opts.effort ?? "high",
|
|
100
|
+
"--disable-slash-commands",
|
|
101
|
+
...opts.isResume ? ["--resume", opts.sessionId] : ["--session-id", opts.sessionId]
|
|
102
|
+
];
|
|
103
|
+
if (useStructured) args.push("--json-schema", opts.jsonSchema);
|
|
104
|
+
return args;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Claude 세션을 spawn 해 한 turn 을 실행한다. per-session 락으로 직렬화된다.
|
|
108
|
+
* @param onDelta 점진 텍스트 delta(streamText) 또는 structured 부분 JSON(streamObject).
|
|
109
|
+
*/
|
|
110
|
+
function runClaudeSession(req, onDelta) {
|
|
111
|
+
const model = canonicalAnthropicModel(req.model);
|
|
112
|
+
const isResume = Boolean(req.resumeSessionId);
|
|
113
|
+
const sessionId = req.resumeSessionId ?? randomUUID();
|
|
114
|
+
const useStructured = Boolean(req.jsonSchema && req.jsonSchema.length > 0);
|
|
115
|
+
const workerId = makeAnthropicWorkerId(req.tokenId);
|
|
116
|
+
return withSessionLock(sessionId, () => {
|
|
117
|
+
ensureCwd();
|
|
118
|
+
const configDir = ensureConfigDir(req.tokenId);
|
|
119
|
+
const args = buildClaudeArgs({
|
|
120
|
+
model,
|
|
121
|
+
system: req.system,
|
|
122
|
+
effort: req.effort,
|
|
123
|
+
jsonSchema: req.jsonSchema,
|
|
124
|
+
sessionId,
|
|
125
|
+
isResume
|
|
126
|
+
});
|
|
127
|
+
const stdinPayload = decorateAndSerialize(buildStreamJsonInput({
|
|
128
|
+
coldHistory: req.coldHistory,
|
|
129
|
+
input: req.input,
|
|
130
|
+
isResume
|
|
131
|
+
}), sessionId);
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
const child = spawn("claude", args, {
|
|
134
|
+
stdio: [
|
|
135
|
+
"pipe",
|
|
136
|
+
"pipe",
|
|
137
|
+
"pipe"
|
|
138
|
+
],
|
|
139
|
+
cwd: ANTHROPIC_CLAUDE_CWD,
|
|
140
|
+
env: {
|
|
141
|
+
PATH: process.env.PATH,
|
|
142
|
+
TMPDIR: process.env.TMPDIR,
|
|
143
|
+
CLAUDE_CODE_OAUTH_TOKEN: req.token,
|
|
144
|
+
CLAUDE_CONFIG_DIR: configDir,
|
|
145
|
+
CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
|
|
146
|
+
CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING: "1",
|
|
147
|
+
CLAUDE_CODE_DISABLE_1M_CONTEXT: "1",
|
|
148
|
+
CLAUDE_CODE_DISABLE_CLAUDE_MDS: "1",
|
|
149
|
+
CLAUDE_CODE_ATTRIBUTION_HEADER: "0"
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
let settled = false;
|
|
153
|
+
let buffer = "";
|
|
154
|
+
const streamState = {};
|
|
155
|
+
let stderrBuf = "";
|
|
156
|
+
const STDERR_CAP = 4096;
|
|
157
|
+
child.stderr?.on("data", (d) => {
|
|
158
|
+
stderrBuf = (stderrBuf + d.toString()).slice(-STDERR_CAP);
|
|
159
|
+
});
|
|
160
|
+
const stderrSuffix = () => stderrBuf.trim() ? ` — stderr: ${stderrBuf.trim()}` : "";
|
|
161
|
+
const timer = setTimeout(() => {
|
|
162
|
+
if (settled) return;
|
|
163
|
+
settled = true;
|
|
164
|
+
child.kill();
|
|
165
|
+
reject(/* @__PURE__ */ new Error(`Claude session timeout after ${req.timeoutMs / 1e3}s`));
|
|
166
|
+
}, req.timeoutMs);
|
|
167
|
+
const finish = (result) => {
|
|
168
|
+
if (settled) return;
|
|
169
|
+
settled = true;
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
resolve({
|
|
172
|
+
...result,
|
|
173
|
+
sessionId,
|
|
174
|
+
workerId
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
req.abortSignal?.addEventListener("abort", () => {
|
|
178
|
+
if (settled) return;
|
|
179
|
+
settled = true;
|
|
180
|
+
clearTimeout(timer);
|
|
181
|
+
child.kill();
|
|
182
|
+
reject(/* @__PURE__ */ new Error("aborted"));
|
|
183
|
+
});
|
|
184
|
+
child.stdout?.on("data", (d) => {
|
|
185
|
+
if (settled) return;
|
|
186
|
+
buffer += d.toString();
|
|
187
|
+
const lines = buffer.split("\n");
|
|
188
|
+
buffer = lines.pop() ?? "";
|
|
189
|
+
for (const line of lines) {
|
|
190
|
+
const result = handleStreamJsonLine(line, onDelta, {
|
|
191
|
+
structuredOutput: useStructured,
|
|
192
|
+
state: streamState
|
|
193
|
+
});
|
|
194
|
+
if (result) {
|
|
195
|
+
finish(result);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
child.on("close", () => {
|
|
201
|
+
if (settled) return;
|
|
202
|
+
settled = true;
|
|
203
|
+
clearTimeout(timer);
|
|
204
|
+
reject(/* @__PURE__ */ new Error(`Claude session closed without result${stderrSuffix()}`));
|
|
205
|
+
});
|
|
206
|
+
child.on("error", (err) => {
|
|
207
|
+
if (settled) return;
|
|
208
|
+
settled = true;
|
|
209
|
+
clearTimeout(timer);
|
|
210
|
+
reject(/* @__PURE__ */ new Error(`Claude session spawn error: ${err.message}`));
|
|
211
|
+
});
|
|
212
|
+
child.stdin?.write(stdinPayload);
|
|
213
|
+
child.stdin?.end();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
export { buildClaudeArgs, compatibilityKey, decorateAndSerialize, ensureConfigDir, makeAnthropicWorkerId, runClaudeSession, withSessionLock };
|
|
219
|
+
|
|
220
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhdWRlLXNlc3Npb24uanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL3Byb3ZpZGVycy9hbnRocm9waWMvY2xhdWRlLXNlc3Npb24udHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBjbGF1ZGUtc2Vzc2lvbiDigJQgQ2xhdWRlIENMSSDshLjshZggc3Bhd24gKyBRZ3JpZFRocmVhZENvb3JkIOunpO2VkSArIHBlci1zZXNzaW9uIOudvS5cbiAqXG4gKiDrqYDti7DthLQ6IOyyqyDtmLjstpwgYC0tc2Vzc2lvbi1pZCA8dXVpZD5gLCDtm4Tsho0gYC0tcmVzdW1lIDx1dWlkPmAuIOyeheugpeydgCBVMiDslrTrjJHthLDqsIAg66eM65OgXG4gKiBKU09OTCDsnYQgc3RkaW4g7Jy866GcIO2dmOumrOqzoChgLS1pbnB1dC1mb3JtYXQgc3RyZWFtLWpzb25gKSwg7Lac66Cl7J2AIFUzIO2MjOyEnOuhnCDsspjrpqztlZzri6QuXG4gKlxuICog7ZW17IusIOqzhOyVvTpcbiAqICAtIGNvb3JkIOuKlCDsnoTsnZgg7KCA7J6l7J20IOyVhOuLiOudvCDquLDsobQgUWdyaWRUaHJlYWRDb29yZHt3b3JrZXJJZCx0aHJlYWRJZCxlcG9jaCxzeXN0ZW1IYXNofSDsl5BcbiAqICAgIOunpO2Vke2VnOuLpC4gdGhyZWFkSWQ97IS47IWYIHV1aWQsIGVwb2NoPTAg6rOg7KCVKHdvcmtlciByZXN0YXJ0IOqwnOuFkCDsl4bsnYwpLCB3b3JrZXJJZD10b2tlbiDquLDrsJhcbiAqICAgIOyViOyglSDtlanshLEsIHN5c3RlbUhhc2g97JqU7LKtIHN5c3RlbSDtlbTsi5wuIGlzc3VlQ29udkNvbnRleHQvZGVjaWRlQ29udlJvdXRpbmcg6rO8IHJvdW5kLXRyaXAuXG4gKiAgLSByZXN1bWUgZWxpZ2liaWxpdHkg64qUIHN5c3RlbUhhc2gg66eM7Jy866GcIOu2gOyhsSDihpIg7ZmV7J6lIO2YuO2ZmO2CpChzeXN0ZW0rbW9kZWwrc3RydWN0dXJlZCDrqqjrk5wp66GcXG4gKiAgICDtjJDsoJUuIOu2iOydvOy5mOuptCBjb2xkIGZhbGxiYWNrLlxuICogIC0g6rCZ7J2AIOyEuOyFmOydmCDrj5nsi5wgdHVybiDsnYAgcGVyLXNlc3Npb24g6529KHNlc3Npb24taWQg64uo7JyEIOyngeugrO2ZlCnsnLzroZwgdHJhbnNjcmlwdCDsmKTsl7wg67Cp7KeALlxuICogIC0gc3RyZWFtLWpzb24g7KeB66Cs7ZmUIOyLnCBTREsgZW52ZWxvcGUodXVpZC9zZXNzaW9uX2lkL3BhcmVudF90b29sX3VzZV9pZCkgZGVjb3JhdGUuXG4gKiAgLSBzdHJ1Y3R1cmVkIG91dHB1dCDsnbwg65WMIOy2nOugpSDtjIzshJzsl5AgeyBzdHJ1Y3R1cmVkT3V0cHV0OiB0cnVlIH0g7KCE64usKOyXhuycvOuptCBzdHJlYW1PYmplY3Qg6rmo7KeQKS5cbiAqXG4gKiDimqDvuI8g7LKrIO2YuOy2nCDrj5nsi5zshLE6IHJlc3VtZSDqsIAg7JWE64uMIFwi7LKrIO2YuOy2nFwi7J2AIOyXrOq4sOyEnCDsg4gg656c642kIHNlc3Npb24taWQg66W8IOuwnOq4ie2VmOuvgOuhnCwg6rCZ7J2AXG4gKiAgICBxZ3JpZCBzZXNzaW9uS2V5IOyXkCDrjIDtlZwg65GQIOuPmeyLnCDssqsg7Zi47Lac7J2AIOyEnOuhnCDri6TrpbggaWQg66GcIOuRmCDri6Qg7Iuk7ZaJ65Cc64ukLiDtmITsnqwgSUYvb3duZXIgc2NvcGUg64qUXG4gKiAgICBMTE0g7Zi47Lac7J2EIOyInOywqCDsi6TtlontlZjqs6Agc2Vzc2lvbktleSDrpbwg7ISc67KE66GcIOuztOuCtOyngCDslYrsnLzrr4DroZwsIGRpc3BhdGNoZXIg64qUIGNvbGQg65297J2EIOuRkOyngCDslYrripTri6QuXG4gKiAgICDrs5HroKwg7LKrIO2YuOy2nOydhCDshJzrsoTqsIAg7KeA7JuQ7ZWY6rKMIOuQmOuptCBzZXNzaW9uS2V5IOyghOuLrC/rnb0g7ISk6rOE67aA7YSwIOuLpOyLnCDsnqHslYTslbwg7ZWc64ukLlxuICovXG5cbmltcG9ydCB7IHNwYXduIH0gZnJvbSBcIm5vZGU6Y2hpbGRfcHJvY2Vzc1wiO1xuaW1wb3J0IHsgY3JlYXRlSGFzaCwgcmFuZG9tVVVJRCB9IGZyb20gXCJub2RlOmNyeXB0b1wiO1xuaW1wb3J0IHsgbWtkaXJTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIjtcblxuaW1wb3J0IHsgdHlwZSBKc29uVmFsdWUgfSBmcm9tIFwiLi4vLi4vLi4vY29kZXgtcHJvdG9jb2wvc2VyZGVfanNvbi9Kc29uVmFsdWVcIjtcbmltcG9ydCB7IHR5cGUgVG9rZW5Vc2FnZUJyZWFrZG93biB9IGZyb20gXCIuLi8uLi8uLi9jb2RleC1wcm90b2NvbC92Mi9Ub2tlblVzYWdlQnJlYWtkb3duXCI7XG5pbXBvcnQgeyB0eXBlIFVzZXJJbnB1dCB9IGZyb20gXCIuLi8uLi8uLi9jb2RleC1wcm90b2NvbC92Mi9Vc2VySW5wdXRcIjtcbmltcG9ydCB7XG4gIEFOVEhST1BJQ19DTEFVREVfQ1dELFxuICBhbnRocm9waWNDb25maWdEaXIsXG4gIEFOVEhST1BJQ19ERUZBVUxUX0VGRk9SVCxcbiAgQU5USFJPUElDX0RJU0FMTE9XRURfVE9PTFMsXG4gIGNhbm9uaWNhbEFudGhyb3BpY01vZGVsLFxufSBmcm9tIFwiLi9hbnRocm9waWMtY29uc3RhbnRzXCI7XG5pbXBvcnQge1xuICBidWlsZFN0cmVhbUpzb25JbnB1dCxcbiAgdHlwZSBDbGF1ZGVTdHJlYW1Kc29uU3RhdGUsXG4gIHR5cGUgQ2xhdWRlU3RyZWFtSnNvbkxpbmUsXG4gIHR5cGUgQ2xhdWRlU3RyZWFtUmVzdWx0LFxuICBoYW5kbGVTdHJlYW1Kc29uTGluZSxcbn0gZnJvbSBcIi4vc3RyZWFtLWpzb24tYWRhcHRlclwiO1xuXG4vLyDilIDilIAg7Zi47ZmY7YKkIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vLyByZXN1bWUg6rCA64qlIOyXrOu2gOulvCDqsIDrpbTripQg7YKkLiBzeXN0ZW1IYXNoIOunjOycvOuhnOuKlCDqsJnsnYAgc2Vzc2lvbktleSDrpbwg64uk66W4IG1vZGVsL3NjaGVtYSDroZxcbi8vIOyerOyCrOyaqe2VoCDrlYwgY29udGV4dCDsmKTsl7wg4oaSIHN5c3RlbSArIG1vZGVsICsgc3RydWN0dXJlZCDrqqjrk5zquYzsp4Ag66y264qU64ukLlxuZXhwb3J0IGZ1bmN0aW9uIGNvbXBhdGliaWxpdHlLZXkob3B0czoge1xuICBzeXN0ZW0/OiBzdHJpbmc7XG4gIG1vZGVsOiBzdHJpbmc7XG4gIC8vIHN0cnVjdHVyZWQgb3V0cHV0IHNjaGVtYSDrrLjsnpDsl7QuIOyXhuycvOuptCB0ZXh0IOuqqOuTnC4g64K07Jqp7J20IOuLpOultOuptCDri6Trpbgg7YKkLlxuICBvdXRwdXRTY2hlbWE/OiBzdHJpbmc7XG59KTogc3RyaW5nIHtcbiAgY29uc3Qgc2NoZW1hUGFydCA9IG9wdHMub3V0cHV0U2NoZW1hICYmIG9wdHMub3V0cHV0U2NoZW1hLmxlbmd0aCA+IDAgPyBvcHRzLm91dHB1dFNjaGVtYSA6IFwidGV4dFwiO1xuICByZXR1cm4gY3JlYXRlSGFzaChcInNoYTI1NlwiKVxuICAgIC51cGRhdGUoYCR7b3B0cy5zeXN0ZW0gPz8gXCJcIn1cXDAke29wdHMubW9kZWx9XFwwJHtzY2hlbWFQYXJ0fWApXG4gICAgLmRpZ2VzdChcImhleFwiKVxuICAgIC5zbGljZSgwLCAxNik7XG59XG5cbi8vIOKUgOKUgCBwZXItc2Vzc2lvbiDrnb0g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbi8vIHNlc3Npb24taWQg64uo7JyEIOyngeugrO2ZlC4g6rCZ7J2AIOyEuOyFmOydmCDrj5nsi5wgdHVybiDsnbQg6rCZ7J2AIHRyYW5zY3JpcHQg7JeQIOuPmeyLnCByZXN1bWXCt2FwcGVuZCDtlZjripQg6rKDIOuwqeyngC5cbmNvbnN0IHNlc3Npb25DaGFpbnMgPSBuZXcgTWFwPHN0cmluZywgUHJvbWlzZTx1bmtub3duPj4oKTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdpdGhTZXNzaW9uTG9jazxUPihzZXNzaW9uSWQ6IHN0cmluZywgZm46ICgpID0+IFByb21pc2U8VD4pOiBQcm9taXNlPFQ+IHtcbiAgY29uc3QgcHJldiA9IHNlc3Npb25DaGFpbnMuZ2V0KHNlc3Npb25JZCkgPz8gUHJvbWlzZS5yZXNvbHZlKCk7XG4gIC8vIOydtOyghCB0dXJuIOydmCDshLHtjKjsmYAg66y06rSA7ZWY6rKMIOyngeugrCDssrTsnbjsnYQg7J6H64qU64ukLlxuICBjb25zdCBuZXh0ID0gcHJldi50aGVuKGZuLCBmbik7XG4gIC8vIHRhaWw6IOyEse2MqCDrrLTqtIAgc2V0dGxlIOuQmOuKlCDtlbjrk6QuIOydtOqxuCDrp7Xsl5Ag65GQ6rOgLCBmaW5hbGx5IOyXkOyEnCBcIuyXrOyghO2eiCDrgrTqsIAg64GdXCLsnbwg65WM66eMIOyCreygnC5cbiAgLy8gKHNldCDsp4Htm4QgdW5kZWZpbmVkIOqygOyCrOuhnCDtlZjrqbQg7KCI64yAIOyCreygnCDslYgg64+8IOyEuOyFmCBpZCDqsIAg64iE7IiY65CoIOKAlCB0YWlsIOydvOy5mCDqsoDsgqzroZwg7ZqM7ZS8LilcbiAgY29uc3QgdGFpbCA9IG5leHQudGhlbihcbiAgICAoKSA9PiB7fSxcbiAgICAoKSA9PiB7fSxcbiAgKTtcbiAgc2Vzc2lvbkNoYWlucy5zZXQoc2Vzc2lvbklkLCB0YWlsKTtcbiAgdHJ5IHtcbiAgICByZXR1cm4gYXdhaXQgbmV4dDtcbiAgfSBmaW5hbGx5IHtcbiAgICAvLyDqt7gg7IKs7J20IOuLpOuluCB0dXJuIOydtCDssrTsnbjsnYQg7J207Ja067Cb7JWY7Jy866m0IHRhaWwg7J20IOq1kOyytOuPvCDsnojri6Qg4oCUIOq3uOuVjOuKlCDsgq3soJztlZjsp4Ag7JWK64qU64ukLlxuICAgIGlmIChzZXNzaW9uQ2hhaW5zLmdldChzZXNzaW9uSWQpID09PSB0YWlsKSBzZXNzaW9uQ2hhaW5zLmRlbGV0ZShzZXNzaW9uSWQpO1xuICB9XG59XG5cbi8vIOKUgOKUgCBjb29yZCDrp6TtlZEg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbi8vIHRva2VuIOq4sOuwmCDslYjsoJUgd29ya2VySWQg7ZWp7ISxLiBBbnRocm9waWMg7J2AIHdvcmtlciBwb29sIOydtCDsl4bsnLzrr4DroZwgdG9rZW5JZCDrpbwg6re464yA66GcIOyTtOuLpC5cbmV4cG9ydCBmdW5jdGlvbiBtYWtlQW50aHJvcGljV29ya2VySWQodG9rZW5JZDogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIHRva2VuSWQ7XG59XG5cbi8vIOKUgOKUgCBlbnZlbG9wZSBkZWNvcmF0ZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuLy8gc3RyZWFtLWpzb24g7J6F66ClIOudvOyduOyXkCBTREsgZW52ZWxvcGUg67aA7LCpIO2bhCDsp4HroKztmZQuIHNlc3Npb24g7IaM7Jyg7J6Q7J24IOyXrOq4sOyEnCDtlZzri6QuXG5leHBvcnQgZnVuY3Rpb24gZGVjb3JhdGVBbmRTZXJpYWxpemUobGluZXM6IEFycmF5PENsYXVkZVN0cmVhbUpzb25MaW5lPiwgc2Vzc2lvbklkOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gKFxuICAgIGxpbmVzXG4gICAgICAubWFwKChsaW5lKSA9PlxuICAgICAgICBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgLi4ubGluZSxcbiAgICAgICAgICBzZXNzaW9uX2lkOiBzZXNzaW9uSWQsXG4gICAgICAgICAgdXVpZDogcmFuZG9tVVVJRCgpLFxuICAgICAgICAgIHBhcmVudF90b29sX3VzZV9pZDogbnVsbCxcbiAgICAgICAgfSksXG4gICAgICApXG4gICAgICAuam9pbihcIlxcblwiKSArIFwiXFxuXCJcbiAgKTtcbn1cblxuLy8g4pSA4pSAIHNwYXduICsgY29uc3VtZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuZXhwb3J0IGludGVyZmFjZSBDbGF1ZGVTZXNzaW9uUmVxdWVzdCB7XG4gIHRva2VuSWQ6IG51bWJlcjtcbiAgdG9rZW46IHN0cmluZzsgLy8gT0F1dGggYWNjZXNzIHRva2VuXG4gIG1vZGVsOiBzdHJpbmc7XG4gIHN5c3RlbT86IHN0cmluZztcbiAganNvblNjaGVtYT86IHN0cmluZzsgLy8g7J6I7Jy866m0IHN0cnVjdHVyZWQgb3V0cHV0XG4gIGVmZm9ydD86IHN0cmluZztcbiAgdGltZW91dE1zOiBudW1iZXI7XG4gIC8vIOyeheugpTogY29sZChjb2xkSGlzdG9yeStjb2xkSW5wdXQpIOuYkOuKlCByZXN1bWUocmV1c2VJbnB1dCkuXG4gIGNvbGRIaXN0b3J5PzogQXJyYXk8SnNvblZhbHVlPjtcbiAgaW5wdXQ6IEFycmF5PFVzZXJJbnB1dD47XG4gIC8vIHJlc3VtZSDqsr3roZzrqbQg6riw7KG0IHNlc3Npb24taWQsIGNvbGQg66m0IHVuZGVmaW5lZCjsg4jroZwg67Cc6riJKS5cbiAgcmVzdW1lU2Vzc2lvbklkPzogc3RyaW5nO1xuICBhYm9ydFNpZ25hbD86IEFib3J0U2lnbmFsO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENsYXVkZVNlc3Npb25SZXN1bHQgZXh0ZW5kcyBDbGF1ZGVTdHJlYW1SZXN1bHQge1xuICBzZXNzaW9uSWQ6IHN0cmluZztcbiAgd29ya2VySWQ6IG51bWJlcjtcbn1cblxuLy8gcGVyLXRva2VuIENMQVVERV9DT05GSUdfRElSIOuztOyepSjqsqnrpqwpLiDqsr3roZwg6rec7LmZ7J2AIGFudGhyb3BpY0NvbmZpZ0RpcijsiJzsiJgg7ZWo7IiYKeydtCDshozsnKAg4oCUXG4vLyDsl6zquLDshKAg6re4IOqyveuhnOyXkCDruYggc2V0dGluZ3Mg66W8IOq5lOyVhCBjbGF1ZGUtbWVtIGhvb2sg65Ox7J2EIOywqOuLqO2VnOuLpC4gKFIxMCDqsqnrpqwpXG4vLyDsnbQgc2VlZCDtjIzsnbzsnYAgY2xhdWRlLW1lbSBob29rIC8g7IKs7Jqp7J6QIOyEpOyglSDsmKTsl7wg7LCo64uo7J2EIOychO2VnCDqsqnrpqwg6rK96rOE64ukLiDrp6Qg7Zi47Lac66eI64ukIOuLpOyLnCDsjajshJxcbi8vIOyZuOu2gCDrs4DsobDCt+yCreygnCDtm4Tsl5Drj4Qg64uk7J2MIOyLpO2WieyXkOyEnCBzZWxmLWhlYWxpbmcg65CY64+E66GdIOq4sOyhtCBzZW1hbnRpY3Mg66W8IOycoOyngO2VnOuLpC5cbmV4cG9ydCBmdW5jdGlvbiBlbnN1cmVDb25maWdEaXIodG9rZW5JZDogbnVtYmVyKTogc3RyaW5nIHtcbiAgY29uc3QgZGlyID0gYW50aHJvcGljQ29uZmlnRGlyKHRva2VuSWQpO1xuICBta2RpclN5bmMoZGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgd3JpdGVGaWxlU3luYyhgJHtkaXJ9Ly5jbGF1ZGUuanNvbmAsIFwie31cIik7XG4gIHdyaXRlRmlsZVN5bmMoYCR7ZGlyfS9zZXR0aW5ncy5qc29uYCwgXCJ7fVwiKTtcbiAgcmV0dXJuIGRpcjtcbn1cblxubGV0IGN3ZEVuc3VyZWQgPSBmYWxzZTtcbmZ1bmN0aW9uIGVuc3VyZUN3ZCgpOiB2b2lkIHtcbiAgaWYgKGN3ZEVuc3VyZWQpIHJldHVybjtcbiAgbWtkaXJTeW5jKGAke0FOVEhST1BJQ19DTEFVREVfQ1dEfS8uY2xhdWRlYCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gIHdyaXRlRmlsZVN5bmMoYCR7QU5USFJPUElDX0NMQVVERV9DV0R9Ly5jbGF1ZGUvc2V0dGluZ3MuanNvbmAsIFwie31cIik7XG4gIGN3ZEVuc3VyZWQgPSB0cnVlO1xufVxuXG4vLyBzcGF3biBhcmdzIOq1rOyEsS4g66mA7Yuw7YS0L+qyqeumrC9zdHJ1Y3R1cmVkIOuqqOuTnCDrsJjsmIEuXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRDbGF1ZGVBcmdzKG9wdHM6IHtcbiAgbW9kZWw6IHN0cmluZztcbiAgc3lzdGVtPzogc3RyaW5nO1xuICBlZmZvcnQ/OiBzdHJpbmc7XG4gIGpzb25TY2hlbWE/OiBzdHJpbmc7XG4gIHNlc3Npb25JZDogc3RyaW5nO1xuICBpc1Jlc3VtZTogYm9vbGVhbjtcbn0pOiBBcnJheTxzdHJpbmc+IHtcbiAgY29uc3QgdXNlU3RydWN0dXJlZCA9IEJvb2xlYW4ob3B0cy5qc29uU2NoZW1hICYmIG9wdHMuanNvblNjaGVtYS5sZW5ndGggPiAwKTtcbiAgLy8gLS10b29scyBcIlwiIOuhnCDsoITssrQg7LCo64uoLCBzdHJ1Y3R1cmVkKGpzb25TY2hlbWEg7J6I7J2MKSDrqbQgU3RydWN0dXJlZE91dHB1dCDrp4wg7ZeI7JqpLlxuICBjb25zdCB0b29sQXJncyA9IHVzZVN0cnVjdHVyZWRcbiAgICA/IFtcIi0tdG9vbHNcIiwgXCJcIiwgXCItLWFsbG93ZWQtdG9vbHNcIiwgXCJTdHJ1Y3R1cmVkT3V0cHV0XCJdXG4gICAgOiBbXCItLXRvb2xzXCIsIFwiXCJdO1xuXG4gIGNvbnN0IGFyZ3M6IEFycmF5PHN0cmluZz4gPSBbXG4gICAgXCItcFwiLFxuICAgIC4uLnRvb2xBcmdzLFxuICAgIFwiLS1kaXNhbGxvd2VkVG9vbHNcIixcbiAgICAuLi5BTlRIUk9QSUNfRElTQUxMT1dFRF9UT09MUyxcbiAgICBcIi0taW5wdXQtZm9ybWF0XCIsXG4gICAgXCJzdHJlYW0tanNvblwiLFxuICAgIFwiLS1vdXRwdXQtZm9ybWF0XCIsXG4gICAgXCJzdHJlYW0tanNvblwiLFxuICAgIFwiLS12ZXJib3NlXCIsXG4gICAgXCItLW1heC10dXJuc1wiLFxuICAgIHVzZVN0cnVjdHVyZWQgPyBcIjJcIiA6IFwiMVwiLFxuICAgIFwiLS1wZXJtaXNzaW9uLW1vZGVcIixcbiAgICBcImJ5cGFzc1Blcm1pc3Npb25zXCIsXG4gICAgXCItLXNldHRpbmctc291cmNlc1wiLFxuICAgIFwicHJvamVjdFwiLFxuICAgIFwiLS1tb2RlbFwiLFxuICAgIG9wdHMubW9kZWwsXG4gICAgLy8gaW5saW5lIFtTeXN0ZW1dIOq4iOyngCDigJQg7KCV7IudIOyxhOuEkOunjChSNykuIOyDneuetSDsi5wgQ0MgZGVmYXVsdCgyM2spIOyjvOyeheuQmOuvgOuhnCDrsJjrk5zsi5wg66qF7IucLlxuICAgIFwiLS1zeXN0ZW0tcHJvbXB0XCIsXG4gICAgb3B0cy5zeXN0ZW0gPz8gXCJcIixcbiAgICBcIi0tdGhpbmtpbmdcIixcbiAgICBcImRpc2FibGVkXCIsXG4gICAgXCItLWVmZm9ydFwiLFxuICAgIG9wdHMuZWZmb3J0ID8/IEFOVEhST1BJQ19ERUZBVUxUX0VGRk9SVCxcbiAgICBcIi0tZGlzYWJsZS1zbGFzaC1jb21tYW5kc1wiLFxuICAgIC8vIOupgO2LsO2EtDog7LKrIO2YuOy2nOydgCBzZXNzaW9uLWlkIOuwnOq4iSwg7ZuE7IaN7J2AIHJlc3VtZS4gLS1uby1zZXNzaW9uLXBlcnNpc3RlbmNlIOuKlCDsk7Dsp4Ag7JWK64qU64ukLlxuICAgIC4uLihvcHRzLmlzUmVzdW1lID8gW1wiLS1yZXN1bWVcIiwgb3B0cy5zZXNzaW9uSWRdIDogW1wiLS1zZXNzaW9uLWlkXCIsIG9wdHMuc2Vzc2lvbklkXSksXG4gIF07XG4gIGlmICh1c2VTdHJ1Y3R1cmVkKSBhcmdzLnB1c2goXCItLWpzb24tc2NoZW1hXCIsIG9wdHMuanNvblNjaGVtYSEpO1xuICByZXR1cm4gYXJncztcbn1cblxuLyoqXG4gKiBDbGF1ZGUg7IS47IWY7J2EIHNwYXduIO2VtCDtlZwgdHVybiDsnYQg7Iuk7ZaJ7ZWc64ukLiBwZXItc2Vzc2lvbiDrnb3snLzroZwg7KeB66Cs7ZmU65Cc64ukLlxuICogQHBhcmFtIG9uRGVsdGEg7KCQ7KeEIO2FjeyKpO2KuCBkZWx0YShzdHJlYW1UZXh0KSDrmJDripQgc3RydWN0dXJlZCDrtoDrtoQgSlNPTihzdHJlYW1PYmplY3QpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcnVuQ2xhdWRlU2Vzc2lvbihcbiAgcmVxOiBDbGF1ZGVTZXNzaW9uUmVxdWVzdCxcbiAgb25EZWx0YTogKHRleHQ6IHN0cmluZykgPT4gdm9pZCxcbik6IFByb21pc2U8Q2xhdWRlU2Vzc2lvblJlc3VsdD4ge1xuICAvLyDsoJXqt5ztmZQg6rec7LmZ7J2AIGNhbm9uaWNhbEFudGhyb3BpY01vZGVsIOydtCDri6jrj4Ug7IaM7JygIOKAlCBkaXNwYXRjaGVyIOqwgCDsnbTrr7ggY2Fub25pY2FsIOydhCDrhJjquLDsp4Drp4wsXG4gIC8vIOyngeygkSDtmLjstpwo7YWM7Iqk7Yq4L+uvuOuemCBjYWxsZXIpIOuMgOu5hCDrsKnslrTsoIHsnLzroZwg7ZWcIOuyiCDrjZQg7Ya16rO87Iuc7YKo64ukKOydtOuvuCBjYW5vbmljYWwg7J2066m0IG5vLW9wKS5cbiAgY29uc3QgbW9kZWwgPSBjYW5vbmljYWxBbnRocm9waWNNb2RlbChyZXEubW9kZWwpO1xuICBjb25zdCBpc1Jlc3VtZSA9IEJvb2xlYW4ocmVxLnJlc3VtZVNlc3Npb25JZCk7XG4gIGNvbnN0IHNlc3Npb25JZCA9IHJlcS5yZXN1bWVTZXNzaW9uSWQgPz8gcmFuZG9tVVVJRCgpO1xuICBjb25zdCB1c2VTdHJ1Y3R1cmVkID0gQm9vbGVhbihyZXEuanNvblNjaGVtYSAmJiByZXEuanNvblNjaGVtYS5sZW5ndGggPiAwKTtcbiAgY29uc3Qgd29ya2VySWQgPSBtYWtlQW50aHJvcGljV29ya2VySWQocmVxLnRva2VuSWQpO1xuXG4gIHJldHVybiB3aXRoU2Vzc2lvbkxvY2soc2Vzc2lvbklkLCAoKSA9PiB7XG4gICAgZW5zdXJlQ3dkKCk7XG4gICAgY29uc3QgY29uZmlnRGlyID0gZW5zdXJlQ29uZmlnRGlyKHJlcS50b2tlbklkKTtcblxuICAgIGNvbnN0IGFyZ3MgPSBidWlsZENsYXVkZUFyZ3Moe1xuICAgICAgbW9kZWwsXG4gICAgICBzeXN0ZW06IHJlcS5zeXN0ZW0sXG4gICAgICBlZmZvcnQ6IHJlcS5lZmZvcnQsXG4gICAgICBqc29uU2NoZW1hOiByZXEuanNvblNjaGVtYSxcbiAgICAgIHNlc3Npb25JZCxcbiAgICAgIGlzUmVzdW1lLFxuICAgIH0pO1xuXG4gICAgY29uc3QgaW5wdXRMaW5lcyA9IGJ1aWxkU3RyZWFtSnNvbklucHV0KHtcbiAgICAgIGNvbGRIaXN0b3J5OiByZXEuY29sZEhpc3RvcnksXG4gICAgICBpbnB1dDogcmVxLmlucHV0LFxuICAgICAgaXNSZXN1bWUsXG4gICAgfSk7XG4gICAgY29uc3Qgc3RkaW5QYXlsb2FkID0gZGVjb3JhdGVBbmRTZXJpYWxpemUoaW5wdXRMaW5lcywgc2Vzc2lvbklkKTtcblxuICAgIHJldHVybiBuZXcgUHJvbWlzZTxDbGF1ZGVTZXNzaW9uUmVzdWx0PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBjaGlsZCA9IHNwYXduKFwiY2xhdWRlXCIsIGFyZ3MsIHtcbiAgICAgICAgLy8gc3RkZXJyIOuPhCDsuqHsspg6IGludmFsaWQgT0F1dGggLyDsnpjrqrvrkJwg7ZSM656Y6re4IC8gQ0xJIOqygOymnSDsi6TtjKjqsIBcbiAgICAgICAgLy8gXCJjbG9zZWQgd2l0aG91dCByZXN1bHRcIiDroZwg662J6rCc7KeA7KeAIOyViuqyjCBjbG9zZS9lcnJvciDsl5Agc3RkZXJyIOulvCDsi6TslrQg67O064K464ukLlxuICAgICAgICBzdGRpbzogW1wicGlwZVwiLCBcInBpcGVcIiwgXCJwaXBlXCJdLFxuICAgICAgICBjd2Q6IEFOVEhST1BJQ19DTEFVREVfQ1dELFxuICAgICAgICBlbnY6IHtcbiAgICAgICAgICBQQVRIOiBwcm9jZXNzLmVudi5QQVRILFxuICAgICAgICAgIFRNUERJUjogcHJvY2Vzcy5lbnYuVE1QRElSLFxuICAgICAgICAgIENMQVVERV9DT0RFX09BVVRIX1RPS0VOOiByZXEudG9rZW4sXG4gICAgICAgICAgQ0xBVURFX0NPTkZJR19ESVI6IGNvbmZpZ0RpcixcbiAgICAgICAgICBDTEFVREVfQ09ERV9ESVNBQkxFX0FVVE9fTUVNT1JZOiBcIjFcIixcbiAgICAgICAgICBDTEFVREVfQ09ERV9ESVNBQkxFX0FEQVBUSVZFX1RISU5LSU5HOiBcIjFcIixcbiAgICAgICAgICBDTEFVREVfQ09ERV9ESVNBQkxFXzFNX0NPTlRFWFQ6IFwiMVwiLFxuICAgICAgICAgIENMQVVERV9DT0RFX0RJU0FCTEVfQ0xBVURFX01EUzogXCIxXCIsXG4gICAgICAgICAgQ0xBVURFX0NPREVfQVRUUklCVVRJT05fSEVBREVSOiBcIjBcIixcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuXG4gICAgICBsZXQgc2V0dGxlZCA9IGZhbHNlO1xuICAgICAgbGV0IGJ1ZmZlciA9IFwiXCI7XG4gICAgICBjb25zdCBzdHJlYW1TdGF0ZTogQ2xhdWRlU3RyZWFtSnNvblN0YXRlID0ge307XG4gICAgICAvLyBib3VuZGVkIHN0ZGVyciDrsoTtjbwo7LWc6re8IH40S0Lrp4wg7Jyg7KeAIOKAlCDrrLTtlZwg64iE7KCBIOuwqeyngCkuXG4gICAgICBsZXQgc3RkZXJyQnVmID0gXCJcIjtcbiAgICAgIGNvbnN0IFNUREVSUl9DQVAgPSA0MDk2O1xuICAgICAgY2hpbGQuc3RkZXJyPy5vbihcImRhdGFcIiwgKGQ6IEJ1ZmZlcikgPT4ge1xuICAgICAgICBzdGRlcnJCdWYgPSAoc3RkZXJyQnVmICsgZC50b1N0cmluZygpKS5zbGljZSgtU1RERVJSX0NBUCk7XG4gICAgICB9KTtcbiAgICAgIGNvbnN0IHN0ZGVyclN1ZmZpeCA9ICgpID0+IChzdGRlcnJCdWYudHJpbSgpID8gYCDigJQgc3RkZXJyOiAke3N0ZGVyckJ1Zi50cmltKCl9YCA6IFwiXCIpO1xuICAgICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgaWYgKHNldHRsZWQpIHJldHVybjtcbiAgICAgICAgc2V0dGxlZCA9IHRydWU7XG4gICAgICAgIGNoaWxkLmtpbGwoKTtcbiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgQ2xhdWRlIHNlc3Npb24gdGltZW91dCBhZnRlciAke3JlcS50aW1lb3V0TXMgLyAxMDAwfXNgKSk7XG4gICAgICB9LCByZXEudGltZW91dE1zKTtcblxuICAgICAgY29uc3QgZmluaXNoID0gKHJlc3VsdDogQ2xhdWRlU3RyZWFtUmVzdWx0KSA9PiB7XG4gICAgICAgIGlmIChzZXR0bGVkKSByZXR1cm47XG4gICAgICAgIHNldHRsZWQgPSB0cnVlO1xuICAgICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgICAgICByZXNvbHZlKHsgLi4ucmVzdWx0LCBzZXNzaW9uSWQsIHdvcmtlcklkIH0pO1xuICAgICAgfTtcblxuICAgICAgcmVxLmFib3J0U2lnbmFsPy5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgKCkgPT4ge1xuICAgICAgICBpZiAoc2V0dGxlZCkgcmV0dXJuO1xuICAgICAgICBzZXR0bGVkID0gdHJ1ZTtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgICAgY2hpbGQua2lsbCgpO1xuICAgICAgICByZWplY3QobmV3IEVycm9yKFwiYWJvcnRlZFwiKSk7XG4gICAgICB9KTtcblxuICAgICAgY2hpbGQuc3Rkb3V0Py5vbihcImRhdGFcIiwgKGQ6IEJ1ZmZlcikgPT4ge1xuICAgICAgICBpZiAoc2V0dGxlZCkgcmV0dXJuO1xuICAgICAgICBidWZmZXIgKz0gZC50b1N0cmluZygpO1xuICAgICAgICBjb25zdCBsaW5lcyA9IGJ1ZmZlci5zcGxpdChcIlxcblwiKTtcbiAgICAgICAgYnVmZmVyID0gbGluZXMucG9wKCkgPz8gXCJcIjtcbiAgICAgICAgZm9yIChjb25zdCBsaW5lIG9mIGxpbmVzKSB7XG4gICAgICAgICAgY29uc3QgcmVzdWx0ID0gaGFuZGxlU3RyZWFtSnNvbkxpbmUobGluZSwgb25EZWx0YSwge1xuICAgICAgICAgICAgc3RydWN0dXJlZE91dHB1dDogdXNlU3RydWN0dXJlZCxcbiAgICAgICAgICAgIHN0YXRlOiBzdHJlYW1TdGF0ZSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgICBmaW5pc2gocmVzdWx0KTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICBjaGlsZC5vbihcImNsb3NlXCIsICgpID0+IHtcbiAgICAgICAgaWYgKHNldHRsZWQpIHJldHVybjtcbiAgICAgICAgc2V0dGxlZCA9IHRydWU7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gICAgICAgIHJlamVjdChuZXcgRXJyb3IoYENsYXVkZSBzZXNzaW9uIGNsb3NlZCB3aXRob3V0IHJlc3VsdCR7c3RkZXJyU3VmZml4KCl9YCkpO1xuICAgICAgfSk7XG4gICAgICBjaGlsZC5vbihcImVycm9yXCIsIChlcnIpID0+IHtcbiAgICAgICAgaWYgKHNldHRsZWQpIHJldHVybjtcbiAgICAgICAgc2V0dGxlZCA9IHRydWU7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gICAgICAgIHJlamVjdChuZXcgRXJyb3IoYENsYXVkZSBzZXNzaW9uIHNwYXduIGVycm9yOiAke2Vyci5tZXNzYWdlfWApKTtcbiAgICAgIH0pO1xuXG4gICAgICAvLyBzdGRpbiDsnLzroZwg7J6F66ClIEpTT05MIO2dmOumrOqzoCDri6vripTri6QuXG4gICAgICBjaGlsZC5zdGRpbj8ud3JpdGUoc3RkaW5QYXlsb2FkKTtcbiAgICAgIGNoaWxkLnN0ZGluPy5lbmQoKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbi8vIHVzYWdlIOyerOuFuOy2nCjthYzsiqTtirgg7Y647J2YKS5cbmV4cG9ydCB0eXBlIHsgVG9rZW5Vc2FnZUJyZWFrZG93biB9O1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBZ0RBLFNBQWdCLGlCQUFpQixNQUt0QjtDQUNULE1BQU0sYUFBYSxLQUFLLGdCQUFnQixLQUFLLGFBQWEsU0FBUyxJQUFJLEtBQUssZUFBZTtBQUMzRixRQUFPLFdBQVcsU0FBUyxDQUN4QixPQUFPLEdBQUcsS0FBSyxVQUFVLEdBQUcsSUFBSSxLQUFLLE1BQU0sSUFBSSxhQUFhLENBQzVELE9BQU8sTUFBTSxDQUNiLE1BQU0sR0FBRyxHQUFHOztBQU1qQixNQUFNLGdDQUFnQixJQUFJLEtBQStCO0FBRXpELGVBQXNCLGdCQUFtQixXQUFtQixJQUFrQztDQUc1RixNQUFNLFFBRk8sY0FBYyxJQUFJLFVBQVUsSUFBSSxRQUFRLFNBQVMsRUFFNUMsS0FBSyxJQUFJLEdBQUc7Q0FHOUIsTUFBTSxPQUFPLEtBQUssV0FDVixVQUNBLEdBQ1A7QUFDRCxlQUFjLElBQUksV0FBVyxLQUFLO0FBQ2xDLEtBQUk7QUFDRixTQUFPLE1BQU07V0FDTDtBQUVSLE1BQUksY0FBYyxJQUFJLFVBQVUsS0FBSyxLQUFNLGVBQWMsT0FBTyxVQUFVOzs7QUFPOUUsU0FBZ0Isc0JBQXNCLFNBQXlCO0FBQzdELFFBQU87O0FBTVQsU0FBZ0IscUJBQXFCLE9BQW9DLFdBQTJCO0FBQ2xHLFFBQ0UsTUFDRyxLQUFLLFNBQ0osS0FBSyxVQUFVO0VBQ2IsR0FBRztFQUNILFlBQVk7RUFDWixNQUFNLFlBQVk7RUFDbEIsb0JBQW9CO0VBQ3JCLENBQUMsQ0FDSCxDQUNBLEtBQUssS0FB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
|