@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.
Files changed (35) hide show
  1. package/bundle/dist/application/qgrid/conv-routing.js +2 -6
  2. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +74 -43
  3. package/bundle/dist/application/qgrid/token-subscriber.js +14 -1
  4. package/bundle/dist/application/qgrid/tool-emulation.js +25 -20
  5. package/bundle/dist/application/request-log/request-log.model.js +54 -8
  6. package/bundle/dist/sonamu.config.js +11 -2
  7. package/bundle/dist/testing/global.js +5 -2
  8. package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +30 -0
  9. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +215 -0
  10. package/bundle/dist/utils/providers/anthropic/claude-session.js +220 -0
  11. package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +177 -0
  12. package/bundle/dist/utils/providers/common/model-cost.js +35 -18
  13. package/bundle/dist/utils/providers/openai/openai-refresh.js +21 -9
  14. package/bundle/src/application/qgrid/conv-routing.test.ts +85 -0
  15. package/bundle/src/application/qgrid/conv-routing.ts +3 -2
  16. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +110 -0
  17. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +112 -64
  18. package/bundle/src/application/qgrid/token-subscriber.ts +23 -1
  19. package/bundle/src/application/qgrid/tool-emulation.test.ts +46 -0
  20. package/bundle/src/application/qgrid/tool-emulation.ts +15 -3
  21. package/bundle/src/application/request-log/request-log.model.ts +98 -13
  22. package/bundle/src/sonamu.config.ts +13 -2
  23. package/bundle/src/testing/global.ts +16 -2
  24. package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +38 -0
  25. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +564 -0
  26. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +365 -0
  27. package/bundle/src/utils/providers/anthropic/claude-session.test.ts +263 -0
  28. package/bundle/src/utils/providers/anthropic/claude-session.ts +327 -0
  29. package/bundle/src/utils/providers/anthropic/stream-json-adapter.test.ts +373 -0
  30. package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +344 -0
  31. package/bundle/src/utils/providers/common/model-cost.test.ts +39 -0
  32. package/bundle/src/utils/providers/common/model-cost.ts +107 -19
  33. package/bundle/src/utils/providers/common/provider-dispatcher.ts +13 -2
  34. package/bundle/src/utils/providers/openai/openai-refresh.ts +34 -9
  35. 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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