@cartanova/qgrid-cli 2.1.1 → 2.2.1

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 (38) hide show
  1. package/bundle/dist/application/qgrid/conv-routing.js +68 -0
  2. package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +2 -2
  3. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +16 -15
  4. package/bundle/dist/application/qgrid/qgrid.frame.js +11 -4
  5. package/bundle/dist/application/qgrid/qgrid.types.js +12 -3
  6. package/bundle/dist/application/qgrid/tool-emulation.js +11 -6
  7. package/bundle/dist/utils/providers/common/model-cost.js +32 -17
  8. package/bundle/dist/utils/providers/openai/codex-worker.js +65 -21
  9. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +65 -21
  10. package/bundle/src/application/qgrid/conv-routing.ts +97 -0
  11. package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +2 -1
  12. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +16 -5
  13. package/bundle/src/application/qgrid/qgrid.frame.ts +11 -3
  14. package/bundle/src/application/qgrid/qgrid.types.ts +14 -1
  15. package/bundle/src/application/qgrid/tool-emulation.ts +20 -5
  16. package/bundle/src/application/sonamu.generated.http +14 -2
  17. package/bundle/src/utils/providers/common/model-cost.ts +55 -9
  18. package/bundle/src/utils/providers/common/provider-dispatcher.ts +29 -2
  19. package/bundle/src/utils/providers/openai/codex-worker.test.ts +91 -0
  20. package/bundle/src/utils/providers/openai/codex-worker.ts +105 -19
  21. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +104 -27
  22. package/bundle/web-dist/client/assets/cost-CpjVoAqp.js +1 -0
  23. package/bundle/web-dist/client/assets/{index-ZV-tHpAg.js → index-DeThumA3.js} +2 -2
  24. package/bundle/web-dist/client/assets/logs-CtmOVH58.js +1 -0
  25. package/bundle/web-dist/client/assets/{routes-BMRwPj6F.js → routes-Cfoe9J0I.js} +1 -1
  26. package/bundle/web-dist/client/assets/show-C6OofoAL.js +30 -0
  27. package/bundle/web-dist/client/assets/{tokens-Cg1f0Ga2.js → tokens-BAZq8sTo.js} +1 -1
  28. package/bundle/web-dist/client/index.html +1 -1
  29. package/bundle/web-dist/server/assets/cost-DzjfVtUr.js +24 -0
  30. package/bundle/web-dist/server/assets/{logs-Xt934T5f.js → logs-CHM0fhfO.js} +2 -7
  31. package/bundle/web-dist/server/assets/{routes-B7QYbNIv.js → routes-C9xFMJ_r.js} +1 -1
  32. package/bundle/web-dist/server/assets/{show-Dxz6UJnD.js → show-BZWqH09U.js} +2 -4
  33. package/bundle/web-dist/server/entry-server.generated.js +3 -3
  34. package/package.json +1 -1
  35. package/bundle/web-dist/client/assets/cost-DE_8cLD-.js +0 -1
  36. package/bundle/web-dist/client/assets/logs-HJYkbqom.js +0 -1
  37. package/bundle/web-dist/client/assets/show-DI6iWDzV.js +0 -30
  38. package/bundle/web-dist/server/assets/cost-DdHTzQDR.js +0 -14
@@ -18,6 +18,12 @@ const WORKERS_PER_TOKEN = Math.min(Number(process.env.QGRID_WORKERS_PER_TOKEN ??
18
18
  const QUEUE_TIMEOUT_MS = 6e4;
19
19
  const MAX_QUEUE_SIZE = 50;
20
20
  const SPAWN_INTERVAL_MS = 500;
21
+ const THREAD_REUSE_ENABLED = process.env.QGRID_OPENAI_THREAD_REUSE !== "false";
22
+ const REUSE_WORKER_WAIT_MS = 5e3;
23
+ const REUSE_WORKER_POLL_MS = 50;
24
+ function makeWorkerId(tokenId, workerIndex) {
25
+ return tokenId * 10 + workerIndex;
26
+ }
21
27
  function sleep(ms) {
22
28
  return new Promise((resolve) => setTimeout(resolve, ms));
23
29
  }
@@ -82,6 +88,11 @@ var OpenAIDispatcher = class OpenAIDispatcher {
82
88
  logger.info(`workers activated: token ${id}`);
83
89
  }
84
90
  async generate(req) {
91
+ const reuseWorker = await this.acquireReuseWorker(req);
92
+ if (reuseWorker) {
93
+ logger.info(`↻ ${reuseWorker.tokenName}[${reuseWorker.tokenId}] (reuse thread, ${req.model})`);
94
+ return this.executeAndRelease(reuseWorker, (w) => this.executeTurn(w, req, req.reuse.threadId));
95
+ }
85
96
  const worker = this.acquireIdleWorker();
86
97
  if (worker) {
87
98
  logger.info(`→ ${worker.tokenName}[${worker.tokenId}] (model: ${req.model})`);
@@ -93,6 +104,11 @@ var OpenAIDispatcher = class OpenAIDispatcher {
93
104
  });
94
105
  }
95
106
  async generateStream(req, cb) {
107
+ const reuseWorker = await this.acquireReuseWorker(req);
108
+ if (reuseWorker) {
109
+ logger.info(`↻ ${reuseWorker.tokenName}[${reuseWorker.tokenId}] (reuse thread, [stream])`);
110
+ return this.executeAndRelease(reuseWorker, (w) => this.executeStreamTurn(w, req, cb, req.reuse.threadId));
111
+ }
96
112
  const worker = this.acquireIdleWorker();
97
113
  if (worker) {
98
114
  logger.info(`→ ${worker.tokenName}[${worker.tokenId}] (model: ${req.model}, [stream])`);
@@ -103,6 +119,25 @@ var OpenAIDispatcher = class OpenAIDispatcher {
103
119
  return this.executeStreamTurn(w, req, cb);
104
120
  });
105
121
  }
122
+ async acquireReuseWorker(req) {
123
+ if (!THREAD_REUSE_ENABLED || !req.reuse) return null;
124
+ const { workerId, threadId, epoch } = req.reuse;
125
+ const worker = this.findWorkerById(workerId);
126
+ if (!worker || !worker.isReady || !worker.active) return null;
127
+ if (worker.epoch !== epoch) return null;
128
+ if (!worker.hasThread(threadId)) return null;
129
+ const deadline = Date.now() + REUSE_WORKER_WAIT_MS;
130
+ for (;;) {
131
+ if (worker.epoch !== epoch || !worker.hasThread(threadId)) return null;
132
+ if (worker.tryAcquireTurn()) return worker;
133
+ if (Date.now() >= deadline) return null;
134
+ await sleep(REUSE_WORKER_POLL_MS);
135
+ }
136
+ }
137
+ findWorkerById(workerId) {
138
+ for (const workers of this.workerPool.values()) for (const w of workers) if (makeWorkerId(w.tokenId, w.workerIndex) === workerId) return w;
139
+ return null;
140
+ }
106
141
  async interruptWorkerTurn(threadId, turnId) {
107
142
  const allWorkers = [...this.workerPool.values()].flat().filter((w) => w.isReady);
108
143
  await Promise.allSettled(allWorkers.map((w) => w.interruptTurn(threadId, turnId)));
@@ -179,49 +214,58 @@ var OpenAIDispatcher = class OpenAIDispatcher {
179
214
  this.drainQueue(worker);
180
215
  }
181
216
  }
182
- async executeTurn(worker, req) {
183
- const turnReq = {
184
- input: req.input,
217
+ buildTurnRequest(req, reuseThreadId) {
218
+ const reusing = reuseThreadId !== void 0;
219
+ return {
220
+ input: reusing && req.reuseInput ? req.reuseInput : req.coldInput,
221
+ history: reusing ? void 0 : req.coldHistory,
185
222
  developerInstructions: req.systemPrompt,
186
223
  outputSchema: req.outputSchema,
187
224
  effort: req.effort ?? DEFAULT_EFFORT,
188
225
  model: req.model,
189
- history: req.history,
190
226
  verbosity: req.verbosity,
191
227
  reasoningSummary: req.reasoningSummary,
192
228
  serviceTier: req.serviceTier
193
229
  };
194
- const result = await worker.executeTurn(turnReq);
230
+ }
231
+ async executeTurn(worker, req, reuseThreadId) {
232
+ const turnReq = this.buildTurnRequest(req, reuseThreadId);
233
+ const result = await worker.executeTurn(turnReq, reuseThreadId);
195
234
  return {
196
235
  text: result.text,
197
236
  tokenName: worker.tokenName,
198
237
  usage: result.usage,
199
238
  durationMs: result.durationMs,
200
- model: result.model
239
+ model: result.model,
240
+ threadCoord: {
241
+ workerId: makeWorkerId(worker.tokenId, worker.workerIndex),
242
+ threadId: result.threadId,
243
+ epoch: worker.epoch
244
+ }
201
245
  };
202
246
  }
203
- async executeStreamTurn(worker, req, cb) {
204
- const turnReq = {
205
- input: req.input,
206
- developerInstructions: req.systemPrompt,
207
- outputSchema: req.outputSchema,
208
- effort: req.effort ?? DEFAULT_EFFORT,
209
- model: req.model,
210
- history: req.history,
211
- verbosity: req.verbosity,
212
- reasoningSummary: req.reasoningSummary,
213
- serviceTier: req.serviceTier
214
- };
247
+ async executeStreamTurn(worker, req, cb, reuseThreadId) {
248
+ const turnReq = this.buildTurnRequest(req, reuseThreadId);
249
+ let resolvedThreadId = reuseThreadId ?? "";
215
250
  const wrappedCb = {
216
251
  ...cb,
252
+ onThreadId: (threadId) => {
253
+ resolvedThreadId = threadId;
254
+ cb.onThreadId?.(threadId);
255
+ },
217
256
  onComplete: (result) => {
218
257
  cb.onComplete({
219
258
  ...result,
220
- tokenName: worker.tokenName
259
+ tokenName: worker.tokenName,
260
+ threadCoord: {
261
+ workerId: makeWorkerId(worker.tokenId, worker.workerIndex),
262
+ threadId: resolvedThreadId,
263
+ epoch: worker.epoch
264
+ }
221
265
  });
222
266
  }
223
267
  };
224
- await worker.executeTurnStream(turnReq, wrappedCb);
268
+ await worker.executeTurnStream(turnReq, wrappedCb, reuseThreadId);
225
269
  }
226
270
  async spawnWorkers(tokenId, tokenName, credentials) {
227
271
  const workers = [];
@@ -332,4 +376,4 @@ var OpenAIDispatcher = class OpenAIDispatcher {
332
376
  //#endregion
333
377
  export { OpenAIDispatcher };
334
378
 
335
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFpLWRpc3BhdGNoZXIuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL3Byb3ZpZGVycy9vcGVuYWkvb3BlbmFpLWRpc3BhdGNoZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBPcGVuQUlEaXNwYXRjaGVyIOKAlCBjb2RleCBhcHAtc2VydmVyIHdvcmtlciBwb29sICsg7Yag7YGwIOudvOyasO2MhS5cbiAqXG4gKiAtIO2GoO2BsOuLuSBOIHdvcmtlciAoQ29kZXhBcHBTZXJ2ZXJXb3JrZXIpLCDquLDrs7ggM1xuICogLSBpZGxlIHdvcmtlciByb3VuZC1yb2JpbiDshKDtg50gKyDtgZAg64yA6riwICjsoITrtoAgYnVzeSDsi5wpXG4gKiAtIFRva2VuU3Vic2NyaWJlciDsnbTrsqTtirjroZwgd29ya2VyIHBvb2wg64+Z6riw7ZmUXG4gKiAtIGJhY2twcmVzc3VyZTog7YGQIGZ1bGwg65iQ64qUIHRpbWVvdXQg7IucIFNFUlZFUl9CVVNZXG4gKi9cbmltcG9ydCB7IGdldExvZ2dlciB9IGZyb20gXCJAbG9ndGFwZS9sb2d0YXBlXCI7XG5cbmltcG9ydCB7IFRva2VuTW9kZWwgfSBmcm9tIFwiLi4vLi4vLi4vYXBwbGljYXRpb24vdG9rZW4vdG9rZW4ubW9kZWxcIjtcbmltcG9ydCB7IHR5cGUgT3BlbkFJQ3JlZGVudGlhbHMgfSBmcm9tIFwiLi4vLi4vLi4vYXBwbGljYXRpb24vdG9rZW4vdG9rZW4udHlwZXNcIjtcbmltcG9ydCB7XG4gIHR5cGUgR2VuZXJhdGVSZXF1ZXN0LFxuICB0eXBlIEdlbmVyYXRlUmVzdWx0LFxuICB0eXBlIFByb3ZpZGVyRGlzcGF0Y2hlcixcbn0gZnJvbSBcIi4uL2NvbW1vbi9wcm92aWRlci1kaXNwYXRjaGVyXCI7XG5pbXBvcnQge1xuICBDb2RleEFwcFNlcnZlcldvcmtlcixcbiAgdHlwZSBTdHJlYW1DYWxsYmFja3MsXG4gIHR5cGUgVHVyblJlcXVlc3QsXG4gIHR5cGUgVHVyblJlc3VsdCxcbn0gZnJvbSBcIi4vY29kZXgtd29ya2VyXCI7XG5pbXBvcnQgeyBoYW5kbGVDaGF0Z3B0QXV0aFRva2Vuc1JlZnJlc2ggfSBmcm9tIFwiLi9vcGVuYWktcmVmcmVzaFwiO1xuXG5jb25zdCBsb2dnZXIgPSBnZXRMb2dnZXIoW1wicWdyaWRcIiwgXCJvcGVuYWktZGlzcGF0Y2hlclwiXSk7XG5cbmNvbnN0IERFRkFVTFRfRUZGT1JUID0gXCJsb3dcIjtcbmNvbnN0IE1BWF9XT1JLRVJTX1BFUl9UT0tFTiA9IDU7XG5jb25zdCBXT1JLRVJTX1BFUl9UT0tFTiA9IE1hdGgubWluKFxuICBOdW1iZXIocHJvY2Vzcy5lbnYuUUdSSURfV09SS0VSU19QRVJfVE9LRU4gPz8gMyksXG4gIE1BWF9XT1JLRVJTX1BFUl9UT0tFTixcbik7XG5jb25zdCBRVUVVRV9USU1FT1VUX01TID0gNjBfMDAwO1xuY29uc3QgTUFYX1FVRVVFX1NJWkUgPSA1MDtcbmNvbnN0IFNQQVdOX0lOVEVSVkFMX01TID0gNTAwO1xuXG5mdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCBtcykpO1xufVxuXG50eXBlIFF1ZXVlSXRlbSA9IHtcbiAgcmVzb2x2ZTogKHdvcmtlcjogQ29kZXhBcHBTZXJ2ZXJXb3JrZXIpID0+IHZvaWQ7XG4gIHJlamVjdDogKGVycm9yOiBFcnJvcikgPT4gdm9pZDtcbiAgdGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+O1xuICBhYm9ydENsZWFudXA/OiAoKSA9PiB2b2lkO1xufTtcblxuZXhwb3J0IGNsYXNzIE9wZW5BSURpc3BhdGNoZXIgaW1wbGVtZW50cyBQcm92aWRlckRpc3BhdGNoZXIge1xuICB3b3JrZXJQb29sID0gbmV3IE1hcDxudW1iZXIsIENvZGV4QXBwU2VydmVyV29ya2VyW10+KCk7XG4gIHJyQ3Vyc29yID0gMDtcbiAgcXVldWU6IFF1ZXVlSXRlbVtdID0gW107XG5cbiAgYXN5bmMgc3RhcnQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyByb3dzIH0gPSBhd2FpdCBUb2tlbk1vZGVsLmZpbmRNYW55KFwiQVwiKTtcbiAgICBjb25zdCBvcGVuYWlUb2tlbnMgPSByb3dzLmZpbHRlcigodCkgPT4gdC5wcm92aWRlciA9PT0gXCJvcGVuYWlcIik7XG4gICAgbG9nZ2VyLmluZm8oXG4gICAgICBgc3RhcnRpbmcgJHtvcGVuYWlUb2tlbnMubGVuZ3RofSBvcGVuYWkgdG9rZW5zICgke1dPUktFUlNfUEVSX1RPS0VOfSB3b3JrZXJzIGVhY2gpYCxcbiAgICApO1xuXG4gICAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFxuICAgICAgb3BlbmFpVG9rZW5zLm1hcChhc3luYyAodCkgPT4ge1xuICAgICAgICBhd2FpdCB0aGlzLnNwYXduV29ya2Vycyh0LmlkLCB0Lm5hbWUsIHQuY3JlZGVudGlhbHMgYXMgT3BlbkFJQ3JlZGVudGlhbHMpO1xuICAgICAgICBpZiAoIXQuYWN0aXZlKSB0aGlzLm9uVG9rZW5EZWFjdGl2YXRlZCh0LmlkKTtcbiAgICAgIH0pLFxuICAgICk7XG4gIH1cblxuICBhc3luYyBzdG9wKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMucmVqZWN0QWxsUXVldWVkKFwiRElTUEFUQ0hFUl9TSFVURE9XTlwiKTtcbiAgICBjb25zdCBraWxscyA9IFsuLi50aGlzLndvcmtlclBvb2wudmFsdWVzKCldLmZsYXQoKS5tYXAoKHcpID0+IHcua2lsbCgpKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoa2lsbHMpO1xuICAgIHRoaXMud29ya2VyUG9vbC5jbGVhcigpO1xuICB9XG5cbiAgLy8g4pSA4pSAIFRva2VuIGV2ZW50cyAoZnJvbSBUb2tlblN1YnNjcmliZXIpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGFzeW5jIG9uVG9rZW5BZGRlZChpZDogbnVtYmVyLCBuYW1lOiBzdHJpbmcsIGNyZWRlbnRpYWxzOiBPcGVuQUlDcmVkZW50aWFscyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLndvcmtlclBvb2wuaGFzKGlkKSkgcmV0dXJuO1xuICAgIGF3YWl0IHRoaXMuc3Bhd25Xb3JrZXJzKGlkLCBuYW1lLCBjcmVkZW50aWFscyk7XG4gIH1cblxuICBhc3luYyBvblRva2VuUmVtb3ZlZChpZDogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgd29ya2VycyA9IHRoaXMud29ya2VyUG9vbC5nZXQoaWQpID8/IFtdO1xuICAgIHRoaXMud29ya2VyUG9vbC5kZWxldGUoaWQpO1xuICAgIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZCh3b3JrZXJzLm1hcCgodykgPT4gdy5raWxsKCkpKTtcbiAgICBpZiAodGhpcy5nZXRBbGxSZWFkeUFjdGl2ZVdvcmtlcnMoKS5sZW5ndGggPT09IDApIHtcbiAgICAgIHRoaXMucmVqZWN0QWxsUXVldWVkKFwiTk9fT1BFTkFJX1dPUktFUlNcIik7XG4gICAgfVxuICAgIGlmICh3b3JrZXJzLmxlbmd0aCA+IDApIGxvZ2dlci5pbmZvKGB3b3JrZXJzIHJlbW92ZWQgZm9yIHRva2VuICR7aWR9OiAke3dvcmtlcnMubGVuZ3RofWApO1xuICB9XG5cbiAgYXN5bmMgb25Ub2tlblVwZGF0ZWQoaWQ6IG51bWJlciwgbmFtZTogc3RyaW5nLCBjcmVkZW50aWFsczogT3BlbkFJQ3JlZGVudGlhbHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBleGlzdGluZyA9IHRoaXMud29ya2VyUG9vbC5nZXQoaWQpID8/IFtdO1xuICAgIGlmIChleGlzdGluZy5sZW5ndGggPT09IDApIHtcbiAgICAgIGF3YWl0IHRoaXMuc3Bhd25Xb3JrZXJzKGlkLCBuYW1lLCBjcmVkZW50aWFscyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKGV4aXN0aW5nLmV2ZXJ5KCh3KSA9PiB3LmNhblJldXNlRm9yVG9rZW4obmFtZSwgY3JlZGVudGlhbHMpKSkge1xuICAgICAgZXhpc3RpbmcuZm9yRWFjaCgodykgPT4gdy51cGRhdGVUb2tlblN0YXRlKG5hbWUsIGNyZWRlbnRpYWxzKSk7XG4gICAgICBsb2dnZXIuaW5mbyhgd29ya2VycyB1cGRhdGVkIGluLXBsYWNlIGZvciB0b2tlbiAke2lkfSAoJHtuYW1lfSlgKTtcbiAgICAgIHRoaXMuZHJhaW5RdWV1ZSgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMud29ya2VyUG9vbC5kZWxldGUoaWQpO1xuICAgIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChleGlzdGluZy5tYXAoKHcpID0+IHcua2lsbCgpKSk7XG4gICAgYXdhaXQgdGhpcy5zcGF3bldvcmtlcnMoaWQsIG5hbWUsIGNyZWRlbnRpYWxzKTtcbiAgfVxuXG4gIG9uVG9rZW5EZWFjdGl2YXRlZChpZDogbnVtYmVyKTogdm9pZCB7XG4gICAgKHRoaXMud29ya2VyUG9vbC5nZXQoaWQpID8/IFtdKS5mb3JFYWNoKCh3KSA9PiB7XG4gICAgICB3LmFjdGl2ZSA9IGZhbHNlO1xuICAgIH0pO1xuICAgIGlmICh0aGlzLmdldEFsbFJlYWR5QWN0aXZlV29ya2VycygpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhpcy5yZWplY3RBbGxRdWV1ZWQoXCJOT19BQ1RJVkVfV09SS0VSU1wiKTtcbiAgICB9XG4gICAgbG9nZ2VyLmluZm8oYHdvcmtlcnMgZGVhY3RpdmF0ZWQ6IHRva2VuICR7aWR9YCk7XG4gIH1cblxuICBvblRva2VuQWN0aXZhdGVkKGlkOiBudW1iZXIpOiB2b2lkIHtcbiAgICAodGhpcy53b3JrZXJQb29sLmdldChpZCkgPz8gW10pLmZvckVhY2goKHcpID0+IHtcbiAgICAgIHcuYWN0aXZlID0gdHJ1ZTtcbiAgICB9KTtcbiAgICB0aGlzLmRyYWluUXVldWUoKTtcbiAgICBsb2dnZXIuaW5mbyhgd29ya2VycyBhY3RpdmF0ZWQ6IHRva2VuICR7aWR9YCk7XG4gIH1cblxuICAvLyDilIDilIAgR2VuZXJhdGUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgYXN5bmMgZ2VuZXJhdGUocmVxOiBHZW5lcmF0ZVJlcXVlc3QpOiBQcm9taXNlPEdlbmVyYXRlUmVzdWx0PiB7XG4gICAgY29uc3Qgd29ya2VyID0gdGhpcy5hY3F1aXJlSWRsZVdvcmtlcigpO1xuICAgIGlmICh3b3JrZXIpIHtcbiAgICAgIGxvZ2dlci5pbmZvKGDihpIgJHt3b3JrZXIudG9rZW5OYW1lfVske3dvcmtlci50b2tlbklkfV0gKG1vZGVsOiAke3JlcS5tb2RlbH0pYCk7XG4gICAgICByZXR1cm4gdGhpcy5leGVjdXRlQW5kUmVsZWFzZSh3b3JrZXIsICh3KSA9PiB0aGlzLmV4ZWN1dGVUdXJuKHcsIHJlcSkpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5lbnF1ZXVlKCh3KSA9PiB7XG4gICAgICBsb2dnZXIuaW5mbyhg4oaSICR7dy50b2tlbk5hbWV9WyR7dy50b2tlbklkfV0gKG1vZGVsOiAke3JlcS5tb2RlbH0sIHF1ZXVlZClgKTtcbiAgICAgIHJldHVybiB0aGlzLmV4ZWN1dGVUdXJuKHcsIHJlcSk7XG4gICAgfSk7XG4gIH1cblxuICBhc3luYyBnZW5lcmF0ZVN0cmVhbShyZXE6IEdlbmVyYXRlUmVxdWVzdCwgY2I6IFN0cmVhbUNhbGxiYWNrcyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHdvcmtlciA9IHRoaXMuYWNxdWlyZUlkbGVXb3JrZXIoKTtcbiAgICBpZiAod29ya2VyKSB7XG4gICAgICBsb2dnZXIuaW5mbyhg4oaSICR7d29ya2VyLnRva2VuTmFtZX1bJHt3b3JrZXIudG9rZW5JZH1dIChtb2RlbDogJHtyZXEubW9kZWx9LCBbc3RyZWFtXSlgKTtcbiAgICAgIHJldHVybiB0aGlzLmV4ZWN1dGVBbmRSZWxlYXNlKHdvcmtlciwgKHcpID0+IHRoaXMuZXhlY3V0ZVN0cmVhbVR1cm4odywgcmVxLCBjYikpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5lbnF1ZXVlKCh3KSA9PiB7XG4gICAgICBsb2dnZXIuaW5mbyhg4oaSICR7dy50b2tlbk5hbWV9WyR7dy50b2tlbklkfV0gKG1vZGVsOiAke3JlcS5tb2RlbH0sIFtzdHJlYW1dLCBxdWV1ZWQpYCk7XG4gICAgICByZXR1cm4gdGhpcy5leGVjdXRlU3RyZWFtVHVybih3LCByZXEsIGNiKTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIGludGVycnVwdFdvcmtlclR1cm4odGhyZWFkSWQ6IHN0cmluZywgdHVybklkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBhbGxXb3JrZXJzID0gWy4uLnRoaXMud29ya2VyUG9vbC52YWx1ZXMoKV0uZmxhdCgpLmZpbHRlcigodykgPT4gdy5pc1JlYWR5KTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYWxsV29ya2Vycy5tYXAoKHcpID0+IHcuaW50ZXJydXB0VHVybih0aHJlYWRJZCwgdHVybklkKSkpO1xuICB9XG5cbiAgLy8g4pSA4pSAIFdvcmtlciBzZWxlY3Rpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vICDquLDrs7ggcm91bmQtcm9iaW5cbiAgLy8gVE9ETzog7LaU7ZuEIOuLpOuluCDslYzqs6DrpqzsppjsnLzroZwg67OA6rK9IOqwgOuKpe2VmOqyjCDsp4Dsm5BcbiAgYWNxdWlyZUlkbGVXb3JrZXIoKTogQ29kZXhBcHBTZXJ2ZXJXb3JrZXIgfCBudWxsIHtcbiAgICBjb25zdCBhbGxXb3JrZXJzID0gdGhpcy5nZXRBbGxSZWFkeUFjdGl2ZVdvcmtlcnMoKTtcbiAgICBpZiAoYWxsV29ya2Vycy5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYWxsV29ya2Vycy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgdyA9IGFsbFdvcmtlcnNbKHRoaXMucnJDdXJzb3IgKyBpKSAlIGFsbFdvcmtlcnMubGVuZ3RoXSE7XG4gICAgICBpZiAody50cnlBY3F1aXJlVHVybigpKSB7XG4gICAgICAgIHRoaXMucnJDdXJzb3IgPSAodGhpcy5yckN1cnNvciArIGkgKyAxKSAlIGFsbFdvcmtlcnMubGVuZ3RoO1xuICAgICAgICByZXR1cm4gdztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBnZXRBbGxSZWFkeUFjdGl2ZVdvcmtlcnMoKTogQ29kZXhBcHBTZXJ2ZXJXb3JrZXJbXSB7XG4gICAgcmV0dXJuIFsuLi50aGlzLndvcmtlclBvb2wudmFsdWVzKCldLmZsYXQoKS5maWx0ZXIoKHcpID0+IHcuaXNSZWFkeSAmJiB3LmFjdGl2ZSk7XG4gIH1cblxuICAvLyDilIDilIAgUXVldWUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgZW5xdWV1ZTxUPihleGVjdXRlOiAod29ya2VyOiBDb2RleEFwcFNlcnZlcldvcmtlcikgPT4gUHJvbWlzZTxUPik6IFByb21pc2U8VD4ge1xuICAgIGlmICh0aGlzLmdldEFsbFJlYWR5QWN0aXZlV29ya2VycygpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5PX09QRU5BSV9XT1JLRVJTXCIpKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucXVldWUubGVuZ3RoID49IE1BWF9RVUVVRV9TSVpFKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiU0VSVkVSX0JVU1lcIikpO1xuICAgIH1cblxuICAgIHJldHVybiBuZXcgUHJvbWlzZTxUPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBpdGVtOiBRdWV1ZUl0ZW0gPSB7XG4gICAgICAgIHJlc29sdmU6ICh3b3JrZXIpID0+IHtcbiAgICAgICAgICBjbGVhclRpbWVvdXQoaXRlbS50aW1lcik7XG4gICAgICAgICAgaXRlbS5hYm9ydENsZWFudXA/LigpO1xuICAgICAgICAgIFByb21pc2UucmVzb2x2ZSgpXG4gICAgICAgICAgICAudGhlbigoKSA9PiB0aGlzLmV4ZWN1dGVBbmRSZWxlYXNlKHdvcmtlciwgZXhlY3V0ZSkpXG4gICAgICAgICAgICAudGhlbihyZXNvbHZlLCByZWplY3QpO1xuICAgICAgICB9LFxuICAgICAgICByZWplY3Q6IChlcnIpID0+IHtcbiAgICAgICAgICBjbGVhclRpbWVvdXQoaXRlbS50aW1lcik7XG4gICAgICAgICAgaXRlbS5hYm9ydENsZWFudXA/LigpO1xuICAgICAgICAgIHJlamVjdChlcnIpO1xuICAgICAgICB9LFxuICAgICAgICB0aW1lcjogc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5yZW1vdmVGcm9tUXVldWUoaXRlbSk7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihcIlNFUlZFUl9CVVNZXCIpKTtcbiAgICAgICAgfSwgUVVFVUVfVElNRU9VVF9NUyksXG4gICAgICB9O1xuXG4gICAgICB0aGlzLnF1ZXVlLnB1c2goaXRlbSk7XG4gICAgfSk7XG4gIH1cblxuICByZW1vdmVGcm9tUXVldWUoaXRlbTogUXVldWVJdGVtKTogdm9pZCB7XG4gICAgY2xlYXJUaW1lb3V0KGl0ZW0udGltZXIpO1xuICAgIGl0ZW0uYWJvcnRDbGVhbnVwPy4oKTtcbiAgICBjb25zdCBpZHggPSB0aGlzLnF1ZXVlLmluZGV4T2YoaXRlbSk7XG4gICAgaWYgKGlkeCAhPT0gLTEpIHRoaXMucXVldWUuc3BsaWNlKGlkeCwgMSk7XG4gIH1cblxuICByZWplY3RBbGxRdWV1ZWQocmVhc29uOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBlcnIgPSBuZXcgRXJyb3IocmVhc29uKTtcbiAgICB0aGlzLnF1ZXVlLmZvckVhY2goKGl0ZW0pID0+IHtcbiAgICAgIGNsZWFyVGltZW91dChpdGVtLnRpbWVyKTtcbiAgICAgIGl0ZW0uYWJvcnRDbGVhbnVwPy4oKTtcbiAgICAgIGl0ZW0ucmVqZWN0KGVycik7XG4gICAgfSk7XG4gICAgdGhpcy5xdWV1ZSA9IFtdO1xuICB9XG5cbiAgZHJhaW5RdWV1ZShyZWxlYXNlZFdvcmtlcj86IENvZGV4QXBwU2VydmVyV29ya2VyKTogdm9pZCB7XG4gICAgaWYgKFxuICAgICAgcmVsZWFzZWRXb3JrZXIgJiZcbiAgICAgIHRoaXMucXVldWUubGVuZ3RoID4gMCAmJlxuICAgICAgcmVsZWFzZWRXb3JrZXIuaXNSZWFkeSAmJlxuICAgICAgcmVsZWFzZWRXb3JrZXIuYWN0aXZlICYmXG4gICAgICByZWxlYXNlZFdvcmtlci50cnlBY3F1aXJlVHVybigpXG4gICAgKSB7XG4gICAgICBjb25zdCBuZXh0ID0gdGhpcy5xdWV1ZS5zaGlmdCgpITtcbiAgICAgIG5leHQucmVzb2x2ZShyZWxlYXNlZFdvcmtlcik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgd2hpbGUgKHRoaXMucXVldWUubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3Qgd29ya2VyID0gdGhpcy5hY3F1aXJlSWRsZVdvcmtlcigpO1xuICAgICAgaWYgKCF3b3JrZXIpIGJyZWFrO1xuICAgICAgY29uc3QgbmV4dCA9IHRoaXMucXVldWUuc2hpZnQoKSE7XG4gICAgICBuZXh0LnJlc29sdmUod29ya2VyKTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgRXhlY3V0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGFzeW5jIGV4ZWN1dGVBbmRSZWxlYXNlPFQ+KFxuICAgIHdvcmtlcjogQ29kZXhBcHBTZXJ2ZXJXb3JrZXIsXG4gICAgZXhlY3V0ZTogKHdvcmtlcjogQ29kZXhBcHBTZXJ2ZXJXb3JrZXIpID0+IFByb21pc2U8VD4sXG4gICk6IFByb21pc2U8VD4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgZXhlY3V0ZSh3b3JrZXIpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB3b3JrZXIucmVsZWFzZVR1cm4oKTtcbiAgICAgIHRoaXMuZHJhaW5RdWV1ZSh3b3JrZXIpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGV4ZWN1dGVUdXJuKHdvcmtlcjogQ29kZXhBcHBTZXJ2ZXJXb3JrZXIsIHJlcTogR2VuZXJhdGVSZXF1ZXN0KTogUHJvbWlzZTxHZW5lcmF0ZVJlc3VsdD4ge1xuICAgIGNvbnN0IHR1cm5SZXE6IFR1cm5SZXF1ZXN0ID0ge1xuICAgICAgaW5wdXQ6IHJlcS5pbnB1dCxcbiAgICAgIGRldmVsb3Blckluc3RydWN0aW9uczogcmVxLnN5c3RlbVByb21wdCxcbiAgICAgIG91dHB1dFNjaGVtYTogcmVxLm91dHB1dFNjaGVtYSxcbiAgICAgIGVmZm9ydDogcmVxLmVmZm9ydCA/PyBERUZBVUxUX0VGRk9SVCxcbiAgICAgIG1vZGVsOiByZXEubW9kZWwsXG4gICAgICBoaXN0b3J5OiByZXEuaGlzdG9yeSxcbiAgICAgIHZlcmJvc2l0eTogcmVxLnZlcmJvc2l0eSxcbiAgICAgIHJlYXNvbmluZ1N1bW1hcnk6IHJlcS5yZWFzb25pbmdTdW1tYXJ5LFxuICAgICAgc2VydmljZVRpZXI6IHJlcS5zZXJ2aWNlVGllcixcbiAgICB9O1xuICAgIGNvbnN0IHJlc3VsdDogVHVyblJlc3VsdCA9IGF3YWl0IHdvcmtlci5leGVjdXRlVHVybih0dXJuUmVxKTtcbiAgICByZXR1cm4ge1xuICAgICAgdGV4dDogcmVzdWx0LnRleHQsXG4gICAgICB0b2tlbk5hbWU6IHdvcmtlci50b2tlbk5hbWUsXG4gICAgICB1c2FnZTogcmVzdWx0LnVzYWdlLFxuICAgICAgZHVyYXRpb25NczogcmVzdWx0LmR1cmF0aW9uTXMsXG4gICAgICBtb2RlbDogcmVzdWx0Lm1vZGVsLFxuICAgIH07XG4gIH1cblxuICBhc3luYyBleGVjdXRlU3RyZWFtVHVybihcbiAgICB3b3JrZXI6IENvZGV4QXBwU2VydmVyV29ya2VyLFxuICAgIHJlcTogR2VuZXJhdGVSZXF1ZXN0LFxuICAgIGNiOiBTdHJlYW1DYWxsYmFja3MsXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHR1cm5SZXE6IFR1cm5SZXF1ZXN0ID0ge1xuICAgICAgaW5wdXQ6IHJlcS5pbnB1dCxcbiAgICAgIGRldmVsb3Blckluc3RydWN0aW9uczogcmVxLnN5c3RlbVByb21wdCxcbiAgICAgIG91dHB1dFNjaGVtYTogcmVxLm91dHB1dFNjaGVtYSxcbiAgICAgIGVmZm9ydDogcmVxLmVmZm9ydCA/PyBERUZBVUxUX0VGRk9SVCxcbiAgICAgIG1vZGVsOiByZXEubW9kZWwsXG4gICAgICBoaXN0b3J5OiByZXEuaGlzdG9yeSxcbiAgICAgIHZlcmJvc2l0eTogcmVxLnZlcmJvc2l0eSxcbiAgICAgIHJlYXNvbmluZ1N1bW1hcnk6IHJlcS5yZWFzb25pbmdTdW1tYXJ5LFxuICAgICAgc2VydmljZVRpZXI6IHJlcS5zZXJ2aWNlVGllcixcbiAgICB9O1xuICAgIGNvbnN0IHdyYXBwZWRDYjogU3RyZWFtQ2FsbGJhY2tzID0ge1xuICAgICAgLi4uY2IsXG4gICAgICBvbkNvbXBsZXRlOiAocmVzdWx0KSA9PiB7XG4gICAgICAgIGNiLm9uQ29tcGxldGUoeyAuLi5yZXN1bHQsIHRva2VuTmFtZTogd29ya2VyLnRva2VuTmFtZSB9IGFzIFR1cm5SZXN1bHQgJiB7XG4gICAgICAgICAgdG9rZW5OYW1lOiBzdHJpbmc7XG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICB9O1xuICAgIGF3YWl0IHdvcmtlci5leGVjdXRlVHVyblN0cmVhbSh0dXJuUmVxLCB3cmFwcGVkQ2IpO1xuICB9XG5cbiAgLy8g4pSA4pSAIFNwYXduIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGFzeW5jIHNwYXduV29ya2VycyhcbiAgICB0b2tlbklkOiBudW1iZXIsXG4gICAgdG9rZW5OYW1lOiBzdHJpbmcsXG4gICAgY3JlZGVudGlhbHM6IE9wZW5BSUNyZWRlbnRpYWxzLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB3b3JrZXJzOiBDb2RleEFwcFNlcnZlcldvcmtlcltdID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBXT1JLRVJTX1BFUl9UT0tFTjsgaSsrKSB7XG4gICAgICBpZiAoaSA+IDApIGF3YWl0IHNsZWVwKFNQQVdOX0lOVEVSVkFMX01TKTtcbiAgICAgIGNvbnN0IHdvcmtlciA9IGF3YWl0IHRoaXMuc3Bhd25TaW5nbGVXb3JrZXIodG9rZW5JZCwgdG9rZW5OYW1lLCBjcmVkZW50aWFscywgaSk7XG4gICAgICBpZiAod29ya2VyKSB3b3JrZXJzLnB1c2god29ya2VyKTtcbiAgICB9XG4gICAgaWYgKHdvcmtlcnMubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy53b3JrZXJQb29sLnNldCh0b2tlbklkLCB3b3JrZXJzKTtcbiAgICAgIGxvZ2dlci5pbmZvKGAke3dvcmtlcnMubGVuZ3RofS8ke1dPUktFUlNfUEVSX1RPS0VOfSB3b3JrZXJzIHNwYXduZWQgZm9yICR7dG9rZW5OYW1lfWApO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHNwYXduU2luZ2xlV29ya2VyKFxuICAgIHRva2VuSWQ6IG51bWJlcixcbiAgICB0b2tlbk5hbWU6IHN0cmluZyxcbiAgICBjcmVkZW50aWFsczogT3BlbkFJQ3JlZGVudGlhbHMsXG4gICAgd29ya2VySW5kZXg6IG51bWJlcixcbiAgKTogUHJvbWlzZTxDb2RleEFwcFNlcnZlcldvcmtlciB8IG51bGw+IHtcbiAgICBjb25zdCB3b3JrZXIgPSBuZXcgQ29kZXhBcHBTZXJ2ZXJXb3JrZXIoe1xuICAgICAgdG9rZW5JZCxcbiAgICAgIHRva2VuTmFtZSxcbiAgICAgIGFjY2Vzc1Rva2VuOiBjcmVkZW50aWFscy5hY2Nlc3NUb2tlbixcbiAgICAgIGFjY291bnRJZDogY3JlZGVudGlhbHMuYWNjb3VudElkLFxuICAgICAgcGxhblR5cGU6IGNyZWRlbnRpYWxzLnBsYW5UeXBlLFxuICAgICAgd29ya2VySW5kZXgsXG4gICAgfSk7XG5cbiAgICB3b3JrZXIub25SZWFkeSA9ICgpID0+IHRoaXMuZHJhaW5RdWV1ZSgpO1xuXG4gICAgd29ya2VyLnNldFNlcnZlclJlcXVlc3RIYW5kbGVyKGFzeW5jIChtZXRob2QpID0+IHtcbiAgICAgIGlmIChtZXRob2QgPT09IFwiYWNjb3VudC9jaGF0Z3B0QXV0aFRva2Vucy9yZWZyZXNoXCIpIHtcbiAgICAgICAgcmV0dXJuIGhhbmRsZUNoYXRncHRBdXRoVG9rZW5zUmVmcmVzaCh0b2tlbklkKTtcbiAgICAgIH1cbiAgICAgIHRocm93IG5ldyBFcnJvcihgdW5oYW5kbGVkIHNlcnZlci1yZXF1ZXN0OiAke21ldGhvZH1gKTtcbiAgICB9KTtcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB3b3JrZXIuaW5pdGlhbGl6ZSgpO1xuICAgICAgbG9nZ2VyLmluZm8oYHdvcmtlciBzcGF3bmVkOiAke3Rva2VuTmFtZX1bJHt3b3JrZXJJbmRleH1dIChpZD0ke3Rva2VuSWQgKyAxfSlgKTtcbiAgICAgIHJldHVybiB3b3JrZXI7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgbG9nZ2VyLndhcm4oYHdvcmtlciBzcGF3biBmYWlsZWQ6ICR7dG9rZW5OYW1lfVske3dvcmtlckluZGV4fV06ICR7KGUgYXMgRXJyb3IpLm1lc3NhZ2V9YCk7XG4gICAgICBhd2FpdCB3b3JrZXIua2lsbCgpLmNhdGNoKCgpID0+IHt9KTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8vIOKUgOKUgCBSYXRlIGxpbWl0cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICByYXRlTGltaXRzQ2FjaGUgPSBuZXcgTWFwPHN0cmluZywgeyBkYXRhOiB1bmtub3duOyBjYWNoZWRBdDogbnVtYmVyIH0+KCk7XG4gIHN0YXRpYyByZWFkb25seSBSQVRFX0xJTUlUU19DQUNIRV9UVEwgPSA2MF8wMDA7XG5cbiAgYXN5bmMgZ2V0UmF0ZUxpbWl0cyh0b2tlbk5hbWU/OiBzdHJpbmcpOiBQcm9taXNlPHVua25vd24+IHtcbiAgICBjb25zdCBjYWNoZUtleSA9IHRva2VuTmFtZSA/PyBcIl9kZWZhdWx0XCI7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5yYXRlTGltaXRzQ2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICBpZiAoY2FjaGVkICYmIERhdGUubm93KCkgLSBjYWNoZWQuY2FjaGVkQXQgPCBPcGVuQUlEaXNwYXRjaGVyLlJBVEVfTElNSVRTX0NBQ0hFX1RUTCkge1xuICAgICAgcmV0dXJuIGNhY2hlZC5kYXRhO1xuICAgIH1cblxuICAgIGNvbnN0IGFsbFdvcmtlcnMgPSBbLi4udGhpcy53b3JrZXJQb29sLnZhbHVlcygpXS5mbGF0KCkuZmlsdGVyKCh3KSA9PiB3LmlzUmVhZHkpO1xuICAgIGNvbnN0IHdvcmtlciA9IHRva2VuTmFtZSA/IGFsbFdvcmtlcnMuZmluZCgodykgPT4gdy50b2tlbk5hbWUgPT09IHRva2VuTmFtZSkgOiBhbGxXb3JrZXJzWzBdO1xuICAgIGlmICghd29ya2VyKSB0aHJvdyBuZXcgRXJyb3IoXCJubyByZWFkeSBvcGVuYWkgd29ya2Vyc1wiKTtcbiAgICBjb25zdCBkYXRhID0gYXdhaXQgd29ya2VyLmdldFJhdGVMaW1pdHMoKTtcbiAgICB0aGlzLnJhdGVMaW1pdHNDYWNoZS5zZXQoY2FjaGVLZXksIHsgZGF0YSwgY2FjaGVkQXQ6IERhdGUubm93KCkgfSk7XG4gICAgcmV0dXJuIGRhdGE7XG4gIH1cblxuICAvLyDilIDilIAgQnJvd3NlciBsb2dpbiBmbG93IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIHBlbmRpbmdMb2dpbjoge1xuICAgIHdvcmtlcjogQ29kZXhBcHBTZXJ2ZXJXb3JrZXI7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIHRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PjtcbiAgfSB8IG51bGwgPSBudWxsO1xuXG4gIGFzeW5jIHN0YXJ0QnJvd3NlckxvZ2luKG5hbWU6IHN0cmluZyk6IFByb21pc2U8eyBhdXRoVXJsOiBzdHJpbmcgfT4ge1xuICAgIGlmICh0aGlzLnBlbmRpbmdMb2dpbikge1xuICAgICAgdGhpcy5wZW5kaW5nTG9naW4ud29ya2VyLmtpbGwoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5wZW5kaW5nTG9naW4udGltZXIpO1xuICAgICAgdGhpcy5wZW5kaW5nTG9naW4gPSBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IHRlbXBJZCA9IERhdGUubm93KCk7XG4gICAgY29uc3Qgd29ya2VyID0gbmV3IENvZGV4QXBwU2VydmVyV29ya2VyKHtcbiAgICAgIHRva2VuSWQ6IHRlbXBJZCxcbiAgICAgIHRva2VuTmFtZTogbmFtZSxcbiAgICAgIGFjY2Vzc1Rva2VuOiBcIlwiLFxuICAgICAgYWNjb3VudElkOiBcIlwiLFxuICAgIH0pO1xuXG4gICAgY29uc3QgYXV0aFVybCA9IGF3YWl0IHdvcmtlci5zdGFydEJyb3dzZXJMb2dpbigpO1xuXG4gICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGlmICh0aGlzLnBlbmRpbmdMb2dpbj8ud29ya2VyID09PSB3b3JrZXIpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oYGJyb3dzZXIgbG9naW4gdGltZW91dCBmb3IgJHtuYW1lfSwga2lsbGluZyB3b3JrZXJgKTtcbiAgICAgICAgd29ya2VyLmtpbGwoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgICAgIHRoaXMucGVuZGluZ0xvZ2luID0gbnVsbDtcbiAgICAgIH1cbiAgICB9LCAzMDBfMDAwKTtcblxuICAgIHRoaXMucGVuZGluZ0xvZ2luID0geyB3b3JrZXIsIG5hbWUsIHRpbWVyIH07XG4gICAgcmV0dXJuIHsgYXV0aFVybCB9O1xuICB9XG5cbiAgZ2V0IHBlbmRpbmdMb2dpbk5hbWUoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMucGVuZGluZ0xvZ2luPy5uYW1lID8/IG51bGw7XG4gIH1cblxuICBhc3luYyBjb21wbGV0ZUJyb3dzZXJMb2dpbigpOiBQcm9taXNlPHtcbiAgICBhY2Nlc3NUb2tlbjogc3RyaW5nO1xuICAgIHJlZnJlc2hUb2tlbjogc3RyaW5nO1xuICAgIGlkVG9rZW4/OiBzdHJpbmc7XG4gICAgYWNjb3VudElkOiBzdHJpbmc7XG4gIH0+IHtcbiAgICBpZiAoIXRoaXMucGVuZGluZ0xvZ2luKSB0aHJvdyBuZXcgRXJyb3IoXCJubyBwZW5kaW5nIGxvZ2luXCIpO1xuICAgIGNvbnN0IHsgd29ya2VyLCB0aW1lciB9ID0gdGhpcy5wZW5kaW5nTG9naW47XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgd29ya2VyLndhaXRGb3JCcm93c2VyTG9naW5Db21wbGV0ZSgpO1xuICAgICAgY29uc3QgY3JlZHMgPSBhd2FpdCB3b3JrZXIucmVhZE1hbmFnZWRDcmVkZW50aWFscygpO1xuICAgICAgaWYgKCFjcmVkcykgdGhyb3cgbmV3IEVycm9yKFwiZmFpbGVkIHRvIHJlYWQgY3JlZGVudGlhbHMgYWZ0ZXIgbG9naW5cIik7XG4gICAgICByZXR1cm4gY3JlZHM7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gICAgICB0aGlzLnBlbmRpbmdMb2dpbiA9IG51bGw7XG4gICAgICBhd2FpdCB3b3JrZXIua2lsbCgpLmNhdGNoKCgpID0+IHt9KTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgU3RhdHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgZ2V0IHdvcmtlckNvdW50KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIFsuLi50aGlzLndvcmtlclBvb2wudmFsdWVzKCldLmZsYXQoKS5sZW5ndGg7XG4gIH1cblxuICBnZXQgcmVhZHlXb3JrZXJDb3VudCgpOiBudW1iZXIge1xuICAgIHJldHVybiBbLi4udGhpcy53b3JrZXJQb29sLnZhbHVlcygpXS5mbGF0KCkuZmlsdGVyKCh3KSA9PiB3LmlzUmVhZHkpLmxlbmd0aDtcbiAgfVxuXG4gIGdldCBxdWV1ZUxlbmd0aCgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlz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
379
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFpLWRpc3BhdGNoZXIuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL3Byb3ZpZGVycy9vcGVuYWkvb3BlbmFpLWRpc3BhdGNoZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBPcGVuQUlEaXNwYXRjaGVyIOKAlCBjb2RleCBhcHAtc2VydmVyIHdvcmtlciBwb29sICsg7Yag7YGwIOudvOyasO2MhS5cbiAqXG4gKiAtIO2GoO2BsOuLuSBOIHdvcmtlciAoQ29kZXhBcHBTZXJ2ZXJXb3JrZXIpLCDquLDrs7ggM1xuICogLSBpZGxlIHdvcmtlciByb3VuZC1yb2JpbiDshKDtg50gKyDtgZAg64yA6riwICjsoITrtoAgYnVzeSDsi5wpXG4gKiAtIFRva2VuU3Vic2NyaWJlciDsnbTrsqTtirjroZwgd29ya2VyIHBvb2wg64+Z6riw7ZmUXG4gKiAtIGJhY2twcmVzc3VyZTog7YGQIGZ1bGwg65iQ64qUIHRpbWVvdXQg7IucIFNFUlZFUl9CVVNZXG4gKi9cbmltcG9ydCB7IGdldExvZ2dlciB9IGZyb20gXCJAbG9ndGFwZS9sb2d0YXBlXCI7XG5cbmltcG9ydCB7IFRva2VuTW9kZWwgfSBmcm9tIFwiLi4vLi4vLi4vYXBwbGljYXRpb24vdG9rZW4vdG9rZW4ubW9kZWxcIjtcbmltcG9ydCB7IHR5cGUgT3BlbkFJQ3JlZGVudGlhbHMgfSBmcm9tIFwiLi4vLi4vLi4vYXBwbGljYXRpb24vdG9rZW4vdG9rZW4udHlwZXNcIjtcbmltcG9ydCB7XG4gIHR5cGUgR2VuZXJhdGVSZXF1ZXN0LFxuICB0eXBlIEdlbmVyYXRlUmVzdWx0LFxuICB0eXBlIEdlbmVyYXRlU3RyZWFtQ2FsbGJhY2tzLFxuICB0eXBlIFByb3ZpZGVyRGlzcGF0Y2hlcixcbn0gZnJvbSBcIi4uL2NvbW1vbi9wcm92aWRlci1kaXNwYXRjaGVyXCI7XG5pbXBvcnQgeyBDb2RleEFwcFNlcnZlcldvcmtlciwgdHlwZSBTdHJlYW1DYWxsYmFja3MsIHR5cGUgVHVyblJlcXVlc3QgfSBmcm9tIFwiLi9jb2RleC13b3JrZXJcIjtcbmltcG9ydCB7IGhhbmRsZUNoYXRncHRBdXRoVG9rZW5zUmVmcmVzaCB9IGZyb20gXCIuL29wZW5haS1yZWZyZXNoXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGdldExvZ2dlcihbXCJxZ3JpZFwiLCBcIm9wZW5haS1kaXNwYXRjaGVyXCJdKTtcblxuY29uc3QgREVGQVVMVF9FRkZPUlQgPSBcImxvd1wiO1xuY29uc3QgTUFYX1dPUktFUlNfUEVSX1RPS0VOID0gNTtcbmNvbnN0IFdPUktFUlNfUEVSX1RPS0VOID0gTWF0aC5taW4oXG4gIE51bWJlcihwcm9jZXNzLmVudi5RR1JJRF9XT1JLRVJTX1BFUl9UT0tFTiA/PyAzKSxcbiAgTUFYX1dPUktFUlNfUEVSX1RPS0VOLFxuKTtcbmNvbnN0IFFVRVVFX1RJTUVPVVRfTVMgPSA2MF8wMDA7XG5jb25zdCBNQVhfUVVFVUVfU0laRSA9IDUwO1xuY29uc3QgU1BBV05fSU5URVJWQUxfTVMgPSA1MDA7XG5cbi8vIHRocmVhZCDsnqzsgqzsmqkocHJvbXB0IGNhY2hlIOqzoOyglSkuIOuBhOuptCDquLDsobQgXCLrp6QgdHVybiDsg4ggdGhyZWFkICsgaGlzdG9yeSBpbmplY3RcIiDrj5nsnpEuXG5jb25zdCBUSFJFQURfUkVVU0VfRU5BQkxFRCA9IHByb2Nlc3MuZW52LlFHUklEX09QRU5BSV9USFJFQURfUkVVU0UgIT09IFwiZmFsc2VcIjtcbi8vIOyijO2RnCB3b3JrZXIg6rCAIGJ1c3kg7J28IOuVjCBmcmVlIOulvCDquLDri6TrpqzripQg7LWc64yAIOyLnOqwhC4g7LSI6rO8IOyLnCDsg4ggdGhyZWFkIOuhnCDtj7TrsLEuXG5jb25zdCBSRVVTRV9XT1JLRVJfV0FJVF9NUyA9IDVfMDAwO1xuY29uc3QgUkVVU0VfV09SS0VSX1BPTExfTVMgPSA1MDtcblxuLy8gd29ya2VySWQg7ZWp7ISxOiB0b2tlbklkIOyZgCB3b3JrZXJJbmRleCgwLi40KSDrpbwg7ZWY64KY7J2YIOyViOyglSDsiKvsnpDroZwg7J247L2U65SpLlxuZnVuY3Rpb24gbWFrZVdvcmtlcklkKHRva2VuSWQ6IG51bWJlciwgd29ya2VySW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiB0b2tlbklkICogMTAgKyB3b3JrZXJJbmRleDtcbn1cblxuZnVuY3Rpb24gc2xlZXAobXM6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKTtcbn1cblxudHlwZSBRdWV1ZUl0ZW0gPSB7XG4gIHJlc29sdmU6ICh3b3JrZXI6IENvZGV4QXBwU2VydmVyV29ya2VyKSA9PiB2b2lkO1xuICByZWplY3Q6IChlcnJvcjogRXJyb3IpID0+IHZvaWQ7XG4gIHRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PjtcbiAgYWJvcnRDbGVhbnVwPzogKCkgPT4gdm9pZDtcbn07XG5cbmV4cG9ydCBjbGFzcyBPcGVuQUlEaXNwYXRjaGVyIGltcGxlbWVudHMgUHJvdmlkZXJEaXNwYXRjaGVyIHtcbiAgd29ya2VyUG9vbCA9IG5ldyBNYXA8bnVtYmVyLCBDb2RleEFwcFNlcnZlcldvcmtlcltdPigpO1xuICByckN1cnNvciA9IDA7XG4gIHF1ZXVlOiBRdWV1ZUl0ZW1bXSA9IFtdO1xuXG4gIGFzeW5jIHN0YXJ0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgcm93cyB9ID0gYXdhaXQgVG9rZW5Nb2RlbC5maW5kTWFueShcIkFcIik7XG4gICAgY29uc3Qgb3BlbmFpVG9rZW5zID0gcm93cy5maWx0ZXIoKHQpID0+IHQucHJvdmlkZXIgPT09IFwib3BlbmFpXCIpO1xuICAgIGxvZ2dlci5pbmZvKFxuICAgICAgYHN0YXJ0aW5nICR7b3BlbmFpVG9rZW5zLmxlbmd0aH0gb3BlbmFpIHRva2VucyAoJHtXT1JLRVJTX1BFUl9UT0tFTn0gd29ya2VycyBlYWNoKWAsXG4gICAgKTtcblxuICAgIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChcbiAgICAgIG9wZW5haVRva2Vucy5tYXAoYXN5bmMgKHQpID0+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5zcGF3bldvcmtlcnModC5pZCwgdC5uYW1lLCB0LmNyZWRlbnRpYWxzIGFzIE9wZW5BSUNyZWRlbnRpYWxzKTtcbiAgICAgICAgaWYgKCF0LmFjdGl2ZSkgdGhpcy5vblRva2VuRGVhY3RpdmF0ZWQodC5pZCk7XG4gICAgICB9KSxcbiAgICApO1xuICB9XG5cbiAgYXN5bmMgc3RvcCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLnJlamVjdEFsbFF1ZXVlZChcIkRJU1BBVENIRVJfU0hVVERPV05cIik7XG4gICAgY29uc3Qga2lsbHMgPSBbLi4udGhpcy53b3JrZXJQb29sLnZhbHVlcygpXS5mbGF0KCkubWFwKCh3KSA9PiB3LmtpbGwoKSk7XG4gICAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKGtpbGxzKTtcbiAgICB0aGlzLndvcmtlclBvb2wuY2xlYXIoKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBUb2tlbiBldmVudHMgKGZyb20gVG9rZW5TdWJzY3JpYmVyKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICBhc3luYyBvblRva2VuQWRkZWQoaWQ6IG51bWJlciwgbmFtZTogc3RyaW5nLCBjcmVkZW50aWFsczogT3BlbkFJQ3JlZGVudGlhbHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy53b3JrZXJQb29sLmhhcyhpZCkpIHJldHVybjtcbiAgICBhd2FpdCB0aGlzLnNwYXduV29ya2VycyhpZCwgbmFtZSwgY3JlZGVudGlhbHMpO1xuICB9XG5cbiAgYXN5bmMgb25Ub2tlblJlbW92ZWQoaWQ6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHdvcmtlcnMgPSB0aGlzLndvcmtlclBvb2wuZ2V0KGlkKSA/PyBbXTtcbiAgICB0aGlzLndvcmtlclBvb2wuZGVsZXRlKGlkKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQod29ya2Vycy5tYXAoKHcpID0+IHcua2lsbCgpKSk7XG4gICAgaWYgKHRoaXMuZ2V0QWxsUmVhZHlBY3RpdmVXb3JrZXJzKCkubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aGlzLnJlamVjdEFsbFF1ZXVlZChcIk5PX09QRU5BSV9XT1JLRVJTXCIpO1xuICAgIH1cbiAgICBpZiAod29ya2Vycy5sZW5ndGggPiAwKSBsb2dnZXIuaW5mbyhgd29ya2VycyByZW1vdmVkIGZvciB0b2tlbiAke2lkfTogJHt3b3JrZXJzLmxlbmd0aH1gKTtcbiAgfVxuXG4gIGFzeW5jIG9uVG9rZW5VcGRhdGVkKGlkOiBudW1iZXIsIG5hbWU6IHN0cmluZywgY3JlZGVudGlhbHM6IE9wZW5BSUNyZWRlbnRpYWxzKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgZXhpc3RpbmcgPSB0aGlzLndvcmtlclBvb2wuZ2V0KGlkKSA/PyBbXTtcbiAgICBpZiAoZXhpc3RpbmcubGVuZ3RoID09PSAwKSB7XG4gICAgICBhd2FpdCB0aGlzLnNwYXduV29ya2VycyhpZCwgbmFtZSwgY3JlZGVudGlhbHMpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChleGlzdGluZy5ldmVyeSgodykgPT4gdy5jYW5SZXVzZUZvclRva2VuKG5hbWUsIGNyZWRlbnRpYWxzKSkpIHtcbiAgICAgIGV4aXN0aW5nLmZvckVhY2goKHcpID0+IHcudXBkYXRlVG9rZW5TdGF0ZShuYW1lLCBjcmVkZW50aWFscykpO1xuICAgICAgbG9nZ2VyLmluZm8oYHdvcmtlcnMgdXBkYXRlZCBpbi1wbGFjZSBmb3IgdG9rZW4gJHtpZH0gKCR7bmFtZX0pYCk7XG4gICAgICB0aGlzLmRyYWluUXVldWUoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLndvcmtlclBvb2wuZGVsZXRlKGlkKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoZXhpc3RpbmcubWFwKCh3KSA9PiB3LmtpbGwoKSkpO1xuICAgIGF3YWl0IHRoaXMuc3Bhd25Xb3JrZXJzKGlkLCBuYW1lLCBjcmVkZW50aWFscyk7XG4gIH1cblxuICBvblRva2VuRGVhY3RpdmF0ZWQoaWQ6IG51bWJlcik6IHZvaWQge1xuICAgICh0aGlzLndvcmtlclBvb2wuZ2V0KGlkKSA/PyBbXSkuZm9yRWFjaCgodykgPT4ge1xuICAgICAgdy5hY3RpdmUgPSBmYWxzZTtcbiAgICB9KTtcbiAgICBpZiAodGhpcy5nZXRBbGxSZWFkeUFjdGl2ZVdvcmtlcnMoKS5sZW5ndGggPT09IDApIHtcbiAgICAgIHRoaXMucmVqZWN0QWxsUXVldWVkKFwiTk9fQUNUSVZFX1dPUktFUlNcIik7XG4gICAgfVxuICAgIGxvZ2dlci5pbmZvKGB3b3JrZXJzIGRlYWN0aXZhdGVkOiB0b2tlbiAke2lkfWApO1xuICB9XG5cbiAgb25Ub2tlbkFjdGl2YXRlZChpZDogbnVtYmVyKTogdm9pZCB7XG4gICAgKHRoaXMud29ya2VyUG9vbC5nZXQoaWQpID8/IFtdKS5mb3JFYWNoKCh3KSA9PiB7XG4gICAgICB3LmFjdGl2ZSA9IHRydWU7XG4gICAgfSk7XG4gICAgdGhpcy5kcmFpblF1ZXVlKCk7XG4gICAgbG9nZ2VyLmluZm8oYHdvcmtlcnMgYWN0aXZhdGVkOiB0b2tlbiAke2lkfWApO1xuICB9XG5cbiAgLy8g4pSA4pSAIEdlbmVyYXRlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGFzeW5jIGdlbmVyYXRlKHJlcTogR2VuZXJhdGVSZXF1ZXN0KTogUHJvbWlzZTxHZW5lcmF0ZVJlc3VsdD4ge1xuICAgIC8vIHRocmVhZCDsnqzsgqzsmqkg6rK966GcOiDsooztkZwgd29ya2VyIOulvCDsoJDsnKDtlbQg6riw7KG0IHRocmVhZCDsl5AgdHVybiDrp4wg7Iuk7ZaJLlxuICAgIGNvbnN0IHJldXNlV29ya2VyID0gYXdhaXQgdGhpcy5hY3F1aXJlUmV1c2VXb3JrZXIocmVxKTtcbiAgICBpZiAocmV1c2VXb3JrZXIpIHtcbiAgICAgIGxvZ2dlci5pbmZvKFxuICAgICAgICBg4oa7ICR7cmV1c2VXb3JrZXIudG9rZW5OYW1lfVske3JldXNlV29ya2VyLnRva2VuSWR9XSAocmV1c2UgdGhyZWFkLCAke3JlcS5tb2RlbH0pYCxcbiAgICAgICk7XG4gICAgICByZXR1cm4gdGhpcy5leGVjdXRlQW5kUmVsZWFzZShyZXVzZVdvcmtlciwgKHcpID0+XG4gICAgICAgIHRoaXMuZXhlY3V0ZVR1cm4odywgcmVxLCByZXEucmV1c2UhLnRocmVhZElkKSxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgY29uc3Qgd29ya2VyID0gdGhpcy5hY3F1aXJlSWRsZVdvcmtlcigpO1xuICAgIGlmICh3b3JrZXIpIHtcbiAgICAgIGxvZ2dlci5pbmZvKGDihpIgJHt3b3JrZXIudG9rZW5OYW1lfVske3dvcmtlci50b2tlbklkfV0gKG1vZGVsOiAke3JlcS5tb2RlbH0pYCk7XG4gICAgICByZXR1cm4gdGhpcy5leGVjdXRlQW5kUmVsZWFzZSh3b3JrZXIsICh3KSA9PiB0aGlzLmV4ZWN1dGVUdXJuKHcsIHJlcSkpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5lbnF1ZXVlKCh3KSA9PiB7XG4gICAgICBsb2dnZXIuaW5mbyhg4oaSICR7dy50b2tlbk5hbWV9WyR7dy50b2tlbklkfV0gKG1vZGVsOiAke3JlcS5tb2RlbH0sIHF1ZXVlZClgKTtcbiAgICAgIHJldHVybiB0aGlzLmV4ZWN1dGVUdXJuKHcsIHJlcSk7XG4gICAgfSk7XG4gIH1cblxuICBhc3luYyBnZW5lcmF0ZVN0cmVhbShyZXE6IEdlbmVyYXRlUmVxdWVzdCwgY2I6IEdlbmVyYXRlU3RyZWFtQ2FsbGJhY2tzKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcmV1c2VXb3JrZXIgPSBhd2FpdCB0aGlzLmFjcXVpcmVSZXVzZVdvcmtlcihyZXEpO1xuICAgIGlmIChyZXVzZVdvcmtlcikge1xuICAgICAgbG9nZ2VyLmluZm8oYOKGuyAke3JldXNlV29ya2VyLnRva2VuTmFtZX1bJHtyZXVzZVdvcmtlci50b2tlbklkfV0gKHJldXNlIHRocmVhZCwgW3N0cmVhbV0pYCk7XG4gICAgICByZXR1cm4gdGhpcy5leGVjdXRlQW5kUmVsZWFzZShyZXVzZVdvcmtlciwgKHcpID0+XG4gICAgICAgIHRoaXMuZXhlY3V0ZVN0cmVhbVR1cm4odywgcmVxLCBjYiwgcmVxLnJldXNlIS50aHJlYWRJZCksXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IHdvcmtlciA9IHRoaXMuYWNxdWlyZUlkbGVXb3JrZXIoKTtcbiAgICBpZiAod29ya2VyKSB7XG4gICAgICBsb2dnZXIuaW5mbyhg4oaSICR7d29ya2VyLnRva2VuTmFtZX1bJHt3b3JrZXIudG9rZW5JZH1dIChtb2RlbDogJHtyZXEubW9kZWx9LCBbc3RyZWFtXSlgKTtcbiAgICAgIHJldHVybiB0aGlzLmV4ZWN1dGVBbmRSZWxlYXNlKHdvcmtlciwgKHcpID0+IHRoaXMuZXhlY3V0ZVN0cmVhbVR1cm4odywgcmVxLCBjYikpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5lbnF1ZXVlKCh3KSA9PiB7XG4gICAgICBsb2dnZXIuaW5mbyhg4oaSICR7dy50b2tlbk5hbWV9WyR7dy50b2tlbklkfV0gKG1vZGVsOiAke3JlcS5tb2RlbH0sIFtzdHJlYW1dLCBxdWV1ZWQpYCk7XG4gICAgICByZXR1cm4gdGhpcy5leGVjdXRlU3RyZWFtVHVybih3LCByZXEsIGNiKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8vIHJldXNlIOyijO2RnOqwgCDsnKDtmqjtlZjrqbQg6re4IHdvcmtlciDrpbwgYnVzeSDsoJDsnKDtlbQg67CY7ZmYLiDsl4bqsbDrgpgg7Y+067CxIOuMgOyDgeydtOuptCBudWxsLlxuICAvLyDtj7TrsLEg7IKs7JygOiDquLDriqUgb2ZmIC8g7KKM7ZGcIOyXhuydjCAvIHdvcmtlciDrtoDsnqzCt+yjveydjCAvIGVwb2NoIOu2iOydvOy5mChyZXN0YXJ0KSAvIHRocmVhZCDshozrqbggL1xuICAvLyAgICAgICAgICAgIGJ1c3kg64yA6riwIO2DgOyehOyVhOybgy4gbnVsbCDrsJjtmZgg7IucIO2YuOy2nOu2gOuKlCDsg4ggdGhyZWFkIOqyveuhnOuhnCDsp4TtlokuXG4gIGFzeW5jIGFjcXVpcmVSZXVzZVdvcmtlcihyZXE6IEdlbmVyYXRlUmVxdWVzdCk6IFByb21pc2U8Q29kZXhBcHBTZXJ2ZXJXb3JrZXIgfCBudWxsPiB7XG4gICAgaWYgKCFUSFJFQURfUkVVU0VfRU5BQkxFRCB8fCAhcmVxLnJldXNlKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCB7IHdvcmtlcklkLCB0aHJlYWRJZCwgZXBvY2ggfSA9IHJlcS5yZXVzZTtcblxuICAgIGNvbnN0IHdvcmtlciA9IHRoaXMuZmluZFdvcmtlckJ5SWQod29ya2VySWQpO1xuICAgIGlmICghd29ya2VyIHx8ICF3b3JrZXIuaXNSZWFkeSB8fCAhd29ya2VyLmFjdGl2ZSkgcmV0dXJuIG51bGw7XG4gICAgaWYgKHdvcmtlci5lcG9jaCAhPT0gZXBvY2gpIHJldHVybiBudWxsO1xuICAgIGlmICghd29ya2VyLmhhc1RocmVhZCh0aHJlYWRJZCkpIHJldHVybiBudWxsO1xuXG4gICAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgUkVVU0VfV09SS0VSX1dBSVRfTVM7XG4gICAgZm9yICg7Oykge1xuICAgICAgLy8gcmVzdGFydChlcG9jaCDrs4Dqsr0pIC8gdGhyZWFkIOyGjOupuOydtCDrjIDquLAg7KSR7JeQIOuwnOyDne2VmOuptCDtj7TrsLEuXG4gICAgICBpZiAod29ya2VyLmVwb2NoICE9PSBlcG9jaCB8fCAhd29ya2VyLmhhc1RocmVhZCh0aHJlYWRJZCkpIHJldHVybiBudWxsO1xuICAgICAgaWYgKHdvcmtlci50cnlBY3F1aXJlVHVybigpKSByZXR1cm4gd29ya2VyO1xuICAgICAgaWYgKERhdGUubm93KCkgPj0gZGVhZGxpbmUpIHJldHVybiBudWxsO1xuICAgICAgYXdhaXQgc2xlZXAoUkVVU0VfV09SS0VSX1BPTExfTVMpO1xuICAgIH1cbiAgfVxuXG4gIGZpbmRXb3JrZXJCeUlkKHdvcmtlcklkOiBudW1iZXIpOiBDb2RleEFwcFNlcnZlcldvcmtlciB8IG51bGwge1xuICAgIGZvciAoY29uc3Qgd29ya2VycyBvZiB0aGlzLndvcmtlclBvb2wudmFsdWVzKCkpIHtcbiAgICAgIGZvciAoY29uc3QgdyBvZiB3b3JrZXJzKSB7XG4gICAgICAgIGlmIChtYWtlV29ya2VySWQody50b2tlbklkLCB3LndvcmtlckluZGV4KSA9PT0gd29ya2VySWQpIHJldHVybiB3O1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGFzeW5jIGludGVycnVwdFdvcmtlclR1cm4odGhyZWFkSWQ6IHN0cmluZywgdHVybklkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBhbGxXb3JrZXJzID0gWy4uLnRoaXMud29ya2VyUG9vbC52YWx1ZXMoKV0uZmxhdCgpLmZpbHRlcigodykgPT4gdy5pc1JlYWR5KTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYWxsV29ya2Vycy5tYXAoKHcpID0+IHcuaW50ZXJydXB0VHVybih0aHJlYWRJZCwgdHVybklkKSkpO1xuICB9XG5cbiAgLy8g4pSA4pSAIFdvcmtlciBzZWxlY3Rpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vICDquLDrs7ggcm91bmQtcm9iaW5cbiAgLy8gVE9ETzog7LaU7ZuEIOuLpOuluCDslYzqs6DrpqzsppjsnLzroZwg67OA6rK9IOqwgOuKpe2VmOqyjCDsp4Dsm5BcbiAgYWNxdWlyZUlkbGVXb3JrZXIoKTogQ29kZXhBcHBTZXJ2ZXJXb3JrZXIgfCBudWxsIHtcbiAgICBjb25zdCBhbGxXb3JrZXJzID0gdGhpcy5nZXRBbGxSZWFkeUFjdGl2ZVdvcmtlcnMoKTtcbiAgICBpZiAoYWxsV29ya2Vycy5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYWxsV29ya2Vycy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgdyA9IGFsbFdvcmtlcnNbKHRoaXMucnJDdXJzb3IgKyBpKSAlIGFsbFdvcmtlcnMubGVuZ3RoXSE7XG4gICAgICBpZiAody50cnlBY3F1aXJlVHVybigpKSB7XG4gICAgICAgIHRoaXMucnJDdXJzb3IgPSAodGhpcy5yckN1cnNvciArIGkgKyAxKSAlIGFsbFdvcmtlcnMubGVuZ3RoO1xuICAgICAgICByZXR1cm4gdztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBnZXRBbGxSZWFkeUFjdGl2ZVdvcmtlcnMoKTogQ29kZXhBcHBTZXJ2ZXJXb3JrZXJbXSB7XG4gICAgcmV0dXJuIFsuLi50aGlzLndvcmtlclBvb2wudmFsdWVzKCldLmZsYXQoKS5maWx0ZXIoKHcpID0+IHcuaXNSZWFkeSAmJiB3LmFjdGl2ZSk7XG4gIH1cblxuICAvLyDilIDilIAgUXVldWUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgZW5xdWV1ZTxUPihleGVjdXRlOiAod29ya2VyOiBDb2RleEFwcFNlcnZlcldvcmtlcikgPT4gUHJvbWlzZTxUPik6IFByb21pc2U8VD4ge1xuICAgIGlmICh0aGlzLmdldEFsbFJlYWR5QWN0aXZlV29ya2VycygpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5PX09QRU5BSV9XT1JLRVJTXCIpKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucXVldWUubGVuZ3RoID49IE1BWF9RVUVVRV9TSVpFKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiU0VSVkVSX0JVU1lcIikpO1xuICAgIH1cblxuICAgIHJldHVybiBuZXcgUHJvbWlzZTxUPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBpdGVtOiBRdWV1ZUl0ZW0gPSB7XG4gICAgICAgIHJlc29sdmU6ICh3b3JrZXIpID0+IHtcbiAgICAgICAgICBjbGVhclRpbWVvdXQoaXRlbS50aW1lcik7XG4gICAgICAgICAgaXRlbS5hYm9ydENsZWFudXA/LigpO1xuICAgICAgICAgIFByb21pc2UucmVzb2x2ZSgpXG4gICAgICAgICAgICAudGhlbigoKSA9PiB0aGlzLmV4ZWN1dGVBbmRSZWxlYXNlKHdvcmtlciwgZXhlY3V0ZSkpXG4gICAgICAgICAgICAudGhlbihyZXNvbHZlLCByZWplY3QpO1xuICAgICAgICB9LFxuICAgICAgICByZWplY3Q6IChlcnIpID0+IHtcbiAgICAgICAgICBjbGVhclRpbWVvdXQoaXRlbS50aW1lcik7XG4gICAgICAgICAgaXRlbS5hYm9ydENsZWFudXA/LigpO1xuICAgICAgICAgIHJlamVjdChlcnIpO1xuICAgICAgICB9LFxuICAgICAgICB0aW1lcjogc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5yZW1vdmVGcm9tUXVldWUoaXRlbSk7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihcIlNFUlZFUl9CVVNZXCIpKTtcbiAgICAgICAgfSwgUVVFVUVfVElNRU9VVF9NUyksXG4gICAgICB9O1xuXG4gICAgICB0aGlzLnF1ZXVlLnB1c2goaXRlbSk7XG4gICAgfSk7XG4gIH1cblxuICByZW1vdmVGcm9tUXVldWUoaXRlbTogUXVldWVJdGVtKTogdm9pZCB7XG4gICAgY2xlYXJUaW1lb3V0KGl0ZW0udGltZXIpO1xuICAgIGl0ZW0uYWJvcnRDbGVhbnVwPy4oKTtcbiAgICBjb25zdCBpZHggPSB0aGlzLnF1ZXVlLmluZGV4T2YoaXRlbSk7XG4gICAgaWYgKGlkeCAhPT0gLTEpIHRoaXMucXVldWUuc3BsaWNlKGlkeCwgMSk7XG4gIH1cblxuICByZWplY3RBbGxRdWV1ZWQocmVhc29uOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBlcnIgPSBuZXcgRXJyb3IocmVhc29uKTtcbiAgICB0aGlzLnF1ZXVlLmZvckVhY2goKGl0ZW0pID0+IHtcbiAgICAgIGNsZWFyVGltZW91dChpdGVtLnRpbWVyKTtcbiAgICAgIGl0ZW0uYWJvcnRDbGVhbnVwPy4oKTtcbiAgICAgIGl0ZW0ucmVqZWN0KGVycik7XG4gICAgfSk7XG4gICAgdGhpcy5xdWV1ZSA9IFtdO1xuICB9XG5cbiAgZHJhaW5RdWV1ZShyZWxlYXNlZFdvcmtlcj86IENvZGV4QXBwU2VydmVyV29ya2VyKTogdm9pZCB7XG4gICAgaWYgKFxuICAgICAgcmVsZWFzZWRXb3JrZXIgJiZcbiAgICAgIHRoaXMucXVldWUubGVuZ3RoID4gMCAmJlxuICAgICAgcmVsZWFzZWRXb3JrZXIuaXNSZWFkeSAmJlxuICAgICAgcmVsZWFzZWRXb3JrZXIuYWN0aXZlICYmXG4gICAgICByZWxlYXNlZFdvcmtlci50cnlBY3F1aXJlVHVybigpXG4gICAgKSB7XG4gICAgICBjb25zdCBuZXh0ID0gdGhpcy5xdWV1ZS5zaGlmdCgpITtcbiAgICAgIG5leHQucmVzb2x2ZShyZWxlYXNlZFdvcmtlcik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgd2hpbGUgKHRoaXMucXVldWUubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3Qgd29ya2VyID0gdGhpcy5hY3F1aXJlSWRsZVdvcmtlcigpO1xuICAgICAgaWYgKCF3b3JrZXIpIGJyZWFrO1xuICAgICAgY29uc3QgbmV4dCA9IHRoaXMucXVldWUuc2hpZnQoKSE7XG4gICAgICBuZXh0LnJlc29sdmUod29ya2VyKTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgRXhlY3V0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGFzeW5jIGV4ZWN1dGVBbmRSZWxlYXNlPFQ+KFxuICAgIHdvcmtlcjogQ29kZXhBcHBTZXJ2ZXJXb3JrZXIsXG4gICAgZXhlY3V0ZTogKHdvcmtlcjogQ29kZXhBcHBTZXJ2ZXJXb3JrZXIpID0+IFByb21pc2U8VD4sXG4gICk6IFByb21pc2U8VD4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgZXhlY3V0ZSh3b3JrZXIpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB3b3JrZXIucmVsZWFzZVR1cm4oKTtcbiAgICAgIHRoaXMuZHJhaW5RdWV1ZSh3b3JrZXIpO1xuICAgIH1cbiAgfVxuXG4gIC8vIHJldXNlVGhyZWFkSWQg6rCAIOyeiOycvOuptCDquLDsobQgdGhyZWFkIOyXkCBkZWx0YShyZXVzZUlucHV0KeunjCwg7JeG7Jy866m0IOyDiCB0aHJlYWQg7JeQIOyghOyytFxuICAvLyBwcm9tcHQoY29sZElucHV0KSArIGhpc3RvcnkgaW5qZWN0LiByZXVzZSDqsIAgZGlzcGF0Y2hlciDri6jsl5DshJwg7Y+067Cx65CY66m0IO2bhOyekOuhnCDrlqjslrTsp4Tri6QuXG4gIHByaXZhdGUgYnVpbGRUdXJuUmVxdWVzdChyZXE6IEdlbmVyYXRlUmVxdWVzdCwgcmV1c2VUaHJlYWRJZD86IHN0cmluZyk6IFR1cm5SZXF1ZXN0IHtcbiAgICBjb25zdCByZXVzaW5nID0gcmV1c2VUaHJlYWRJZCAhPT0gdW5kZWZpbmVkO1xuICAgIHJldHVybiB7XG4gICAgICBpbnB1dDogcmV1c2luZyAmJiByZXEucmV1c2VJbnB1dCA/IHJlcS5yZXVzZUlucHV0IDogcmVxLmNvbGRJbnB1dCxcbiAgICAgIGhpc3Rvcnk6IHJldXNpbmcgPyB1bmRlZmluZWQgOiByZXEuY29sZEhpc3RvcnksXG4gICAgICBkZXZlbG9wZXJJbnN0cnVjdGlvbnM6IHJlcS5zeXN0ZW1Qcm9tcHQsXG4gICAgICBvdXRwdXRTY2hlbWE6IHJlcS5vdXRwdXRTY2hlbWEsXG4gICAgICBlZmZvcnQ6IHJlcS5lZmZvcnQgPz8gREVGQVVMVF9FRkZPUlQsXG4gICAgICBtb2RlbDogcmVxLm1vZGVsLFxuICAgICAgdmVyYm9zaXR5OiByZXEudmVyYm9zaXR5LFxuICAgICAgcmVhc29uaW5nU3VtbWFyeTogcmVxLnJlYXNvbmluZ1N1bW1hcnksXG4gICAgICBzZXJ2aWNlVGllcjogcmVxLnNlcnZpY2VUaWVyLFxuICAgIH07XG4gIH1cblxuICBhc3luYyBleGVjdXRlVHVybihcbiAgICB3b3JrZXI6IENvZGV4QXBwU2VydmVyV29ya2VyLFxuICAgIHJlcTogR2VuZXJhdGVSZXF1ZXN0LFxuICAgIHJldXNlVGhyZWFkSWQ/OiBzdHJpbmcsXG4gICk6IFByb21pc2U8R2VuZXJhdGVSZXN1bHQ+IHtcbiAgICBjb25zdCB0dXJuUmVxID0gdGhpcy5idWlsZFR1cm5SZXF1ZXN0KHJlcSwgcmV1c2VUaHJlYWRJZCk7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgd29ya2VyLmV4ZWN1dGVUdXJuKHR1cm5SZXEsIHJldXNlVGhyZWFkSWQpO1xuICAgIHJldHVybiB7XG4gICAgICB0ZXh0OiByZXN1bHQudGV4dCxcbiAgICAgIHRva2VuTmFtZTogd29ya2VyLnRva2VuTmFtZSxcbiAgICAgIHVzYWdlOiByZXN1bHQudXNhZ2UsXG4gICAgICBkdXJhdGlvbk1zOiByZXN1bHQuZHVyYXRpb25NcyxcbiAgICAgIG1vZGVsOiByZXN1bHQubW9kZWwsXG4gICAgICB0aHJlYWRDb29yZDoge1xuICAgICAgICB3b3JrZXJJZDogbWFrZVdvcmtlcklkKHdvcmtlci50b2tlbklkLCB3b3JrZXIud29ya2VySW5kZXgpLFxuICAgICAgICB0aHJlYWRJZDogcmVzdWx0LnRocmVhZElkLFxuICAgICAgICBlcG9jaDogd29ya2VyLmVwb2NoLFxuICAgICAgfSxcbiAgICB9O1xuICB9XG5cbiAgYXN5bmMgZXhlY3V0ZVN0cmVhbVR1cm4oXG4gICAgd29ya2VyOiBDb2RleEFwcFNlcnZlcldvcmtlcixcbiAgICByZXE6IEdlbmVyYXRlUmVxdWVzdCxcbiAgICBjYjogR2VuZXJhdGVTdHJlYW1DYWxsYmFja3MsXG4gICAgcmV1c2VUaHJlYWRJZD86IHN0cmluZyxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdHVyblJlcSA9IHRoaXMuYnVpbGRUdXJuUmVxdWVzdChyZXEsIHJldXNlVGhyZWFkSWQpO1xuICAgIC8vIHdvcmtlciDsnZggVHVyblJlc3VsdCDsl5AgdG9rZW5OYW1lL3RocmVhZENvb3JkIOulvCDrtpnsl6wg7IOB7JyEKEdlbmVyYXRlUmVzdWx0KeuhnCDsmKzrprDri6QuXG4gICAgLy8gdGhyZWFkSWQg64qUIOyDiCB0aHJlYWQg66m0IG9uVGhyZWFkSWQg66GcIO2ZleygleuQmOuvgOuhnCDrr7jrpqwg67O06rSA7ZW0IOuRkOqzoCBvbkNvbXBsZXRlIOuVjCDsnb3ripTri6QuXG4gICAgbGV0IHJlc29sdmVkVGhyZWFkSWQgPSByZXVzZVRocmVhZElkID8/IFwiXCI7XG4gICAgY29uc3Qgd3JhcHBlZENiOiBTdHJlYW1DYWxsYmFja3MgPSB7XG4gICAgICAuLi5jYixcbiAgICAgIG9uVGhyZWFkSWQ6ICh0aHJlYWRJZCkgPT4ge1xuICAgICAgICByZXNvbHZlZFRocmVhZElkID0gdGhyZWFkSWQ7XG4gICAgICAgIGNiLm9uVGhyZWFkSWQ/Lih0aHJlYWRJZCk7XG4gICAgICB9LFxuICAgICAgb25Db21wbGV0ZTogKHJlc3VsdCkgPT4ge1xuICAgICAgICBjYi5vbkNvbXBsZXRlKHtcbiAgICAgICAgICAuLi5yZXN1bHQsXG4gICAgICAgICAgdG9rZW5OYW1lOiB3b3JrZXIudG9rZW5OYW1lLFxuICAgICAgICAgIHRocmVhZENvb3JkOiB7XG4gICAgICAgICAgICB3b3JrZXJJZDogbWFrZVdvcmtlcklkKHdvcmtlci50b2tlbklkLCB3b3JrZXIud29ya2VySW5kZXgpLFxuICAgICAgICAgICAgdGhyZWFkSWQ6IHJlc29sdmVkVGhyZWFkSWQsXG4gICAgICAgICAgICBlcG9jaDogd29ya2VyLmVwb2NoLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICB9O1xuICAgIGF3YWl0IHdvcmtlci5leGVjdXRlVHVyblN0cmVhbSh0dXJuUmVxLCB3cmFwcGVkQ2IsIHJldXNlVGhyZWFkSWQpO1xuICB9XG5cbiAgLy8g4pSA4pSAIFNwYXduIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGFzeW5jIHNwYXduV29ya2VycyhcbiAgICB0b2tlbklkOiBudW1iZXIsXG4gICAgdG9rZW5OYW1lOiBzdHJpbmcsXG4gICAgY3JlZGVudGlhbHM6IE9wZW5BSUNyZWRlbnRpYWxzLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB3b3JrZXJzOiBDb2RleEFwcFNlcnZlcldvcmtlcltdID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBXT1JLRVJTX1BFUl9UT0tFTjsgaSsrKSB7XG4gICAgICBpZiAoaSA+IDApIGF3YWl0IHNsZWVwKFNQQVdOX0lOVEVSVkFMX01TKTtcbiAgICAgIGNvbnN0IHdvcmtlciA9IGF3YWl0IHRoaXMuc3Bhd25TaW5nbGVXb3JrZXIodG9rZW5JZCwgdG9rZW5OYW1lLCBjcmVkZW50aWFscywgaSk7XG4gICAgICBpZiAod29ya2VyKSB3b3JrZXJzLnB1c2god29ya2VyKTtcbiAgICB9XG4gICAgaWYgKHdvcmtlcnMubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy53b3JrZXJQb29sLnNldCh0b2tlbklkLCB3b3JrZXJzKTtcbiAgICAgIGxvZ2dlci5pbmZvKGAke3dvcmtlcnMubGVuZ3RofS8ke1dPUktFUlNfUEVSX1RPS0VOfSB3b3JrZXJzIHNwYXduZWQgZm9yICR7dG9rZW5OYW1lfWApO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHNwYXduU2luZ2xlV29ya2VyKFxuICAgIHRva2VuSWQ6IG51bWJlcixcbiAgICB0b2tlbk5hbWU6IHN0cmluZyxcbiAgICBjcmVkZW50aWFsczogT3BlbkFJQ3JlZGVudGlhbHMsXG4gICAgd29ya2VySW5kZXg6IG51bWJlcixcbiAgKTogUHJvbWlzZTxDb2RleEFwcFNlcnZlcldvcmtlciB8IG51bGw+IHtcbiAgICBjb25zdCB3b3JrZXIgPSBuZXcgQ29kZXhBcHBTZXJ2ZXJXb3JrZXIoe1xuICAgICAgdG9rZW5JZCxcbiAgICAgIHRva2VuTmFtZSxcbiAgICAgIGFjY2Vzc1Rva2VuOiBjcmVkZW50aWFscy5hY2Nlc3NUb2tlbixcbiAgICAgIGFjY291bnRJZDogY3JlZGVudGlhbHMuYWNjb3VudElkLFxuICAgICAgcGxhblR5cGU6IGNyZWRlbnRpYWxzLnBsYW5UeXBlLFxuICAgICAgd29ya2VySW5kZXgsXG4gICAgfSk7XG5cbiAgICB3b3JrZXIub25SZWFkeSA9ICgpID0+IHRoaXMuZHJhaW5RdWV1ZSgpO1xuXG4gICAgd29ya2VyLnNldFNlcnZlclJlcXVlc3RIYW5kbGVyKGFzeW5jIChtZXRob2QpID0+IHtcbiAgICAgIGlmIChtZXRob2QgPT09IFwiYWNjb3VudC9jaGF0Z3B0QXV0aFRva2Vucy9yZWZyZXNoXCIpIHtcbiAgICAgICAgcmV0dXJuIGhhbmRsZUNoYXRncHRBdXRoVG9rZW5zUmVmcmVzaCh0b2tlbklkKTtcbiAgICAgIH1cbiAgICAgIHRocm93IG5ldyBFcnJvcihgdW5oYW5kbGVkIHNlcnZlci1yZXF1ZXN0OiAke21ldGhvZH1gKTtcbiAgICB9KTtcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB3b3JrZXIuaW5pdGlhbGl6ZSgpO1xuICAgICAgbG9nZ2VyLmluZm8oYHdvcmtlciBzcGF3bmVkOiAke3Rva2VuTmFtZX1bJHt3b3JrZXJJbmRleH1dIChpZD0ke3Rva2VuSWQgKyAxfSlgKTtcbiAgICAgIHJldHVybiB3b3JrZXI7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgbG9nZ2VyLndhcm4oYHdvcmtlciBzcGF3biBmYWlsZWQ6ICR7dG9rZW5OYW1lfVske3dvcmtlckluZGV4fV06ICR7KGUgYXMgRXJyb3IpLm1lc3NhZ2V9YCk7XG4gICAgICBhd2FpdCB3b3JrZXIua2lsbCgpLmNhdGNoKCgpID0+IHt9KTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8vIOKUgOKUgCBSYXRlIGxpbWl0cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICByYXRlTGltaXRzQ2FjaGUgPSBuZXcgTWFwPHN0cmluZywgeyBkYXRhOiB1bmtub3duOyBjYWNoZWRBdDogbnVtYmVyIH0+KCk7XG4gIHN0YXRpYyByZWFkb25seSBSQVRFX0xJTUlUU19DQUNIRV9UVEwgPSA2MF8wMDA7XG5cbiAgYXN5bmMgZ2V0UmF0ZUxpbWl0cyh0b2tlbk5hbWU/OiBzdHJpbmcpOiBQcm9taXNlPHVua25vd24+IHtcbiAgICBjb25zdCBjYWNoZUtleSA9IHRva2VuTmFtZSA/PyBcIl9kZWZhdWx0XCI7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5yYXRlTGltaXRzQ2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICBpZiAoY2FjaGVkICYmIERhdGUubm93KCkgLSBjYWNoZWQuY2FjaGVkQXQgPCBPcGVuQUlEaXNwYXRjaGVyLlJBVEVfTElNSVRTX0NBQ0hFX1RUTCkge1xuICAgICAgcmV0dXJuIGNhY2hlZC5kYXRhO1xuICAgIH1cblxuICAgIGNvbnN0IGFsbFdvcmtlcnMgPSBbLi4udGhpcy53b3JrZXJQb29sLnZhbHVlcygpXS5mbGF0KCkuZmlsdGVyKCh3KSA9PiB3LmlzUmVhZHkpO1xuICAgIGNvbnN0IHdvcmtlciA9IHRva2VuTmFtZSA/IGFsbFdvcmtlcnMuZmluZCgodykgPT4gdy50b2tlbk5hbWUgPT09IHRva2VuTmFtZSkgOiBhbGxXb3JrZXJzWzBdO1xuICAgIGlmICghd29ya2VyKSB0aHJvdyBuZXcgRXJyb3IoXCJubyByZWFkeSBvcGVuYWkgd29ya2Vyc1wiKTtcbiAgICBjb25zdCBkYXRhID0gYXdhaXQgd29ya2VyLmdldFJhdGVMaW1pdHMoKTtcbiAgICB0aGlzLnJhdGVMaW1pdHNDYWNoZS5zZXQoY2FjaGVLZXksIHsgZGF0YSwgY2FjaGVkQXQ6IERhdGUubm93KCkgfSk7XG4gICAgcmV0dXJuIGRhdGE7XG4gIH1cblxuICAvLyDilIDilIAgQnJvd3NlciBsb2dpbiBmbG93IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIHBlbmRpbmdMb2dpbjoge1xuICAgIHdvcmtlcjogQ29kZXhBcHBTZXJ2ZXJXb3JrZXI7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIHRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PjtcbiAgfSB8IG51bGwgPSBudWxsO1xuXG4gIGFzeW5jIHN0YXJ0QnJvd3NlckxvZ2luKG5hbWU6IHN0cmluZyk6IFByb21pc2U8eyBhdXRoVXJsOiBzdHJpbmcgfT4ge1xuICAgIGlmICh0aGlzLnBlbmRpbmdMb2dpbikge1xuICAgICAgdGhpcy5wZW5kaW5nTG9naW4ud29ya2VyLmtpbGwoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5wZW5kaW5nTG9naW4udGltZXIpO1xuICAgICAgdGhpcy5wZW5kaW5nTG9naW4gPSBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IHRlbXBJZCA9IERhdGUubm93KCk7XG4gICAgY29uc3Qgd29ya2VyID0gbmV3IENvZGV4QXBwU2VydmVyV29ya2VyKHtcbiAgICAgIHRva2VuSWQ6IHRlbXBJZCxcbiAgICAgIHRva2VuTmFtZTogbmFtZSxcbiAgICAgIGFjY2Vzc1Rva2VuOiBcIlwiLFxuICAgICAgYWNjb3VudElkOiBcIlwiLFxuICAgIH0pO1xuXG4gICAgY29uc3QgYXV0aFVybCA9IGF3YWl0IHdvcmtlci5zdGFydEJyb3dzZXJMb2dpbigpO1xuXG4gICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGlmICh0aGlzLnBlbmRpbmdMb2dpbj8ud29ya2VyID09PSB3b3JrZXIpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oYGJyb3dzZXIgbG9naW4gdGltZW91dCBmb3IgJHtuYW1lfSwga2lsbGluZyB3b3JrZXJgKTtcbiAgICAgICAgd29ya2VyLmtpbGwoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgICAgIHRoaXMucGVuZGluZ0xvZ2luID0gbnVsbDtcbiAgICAgIH1cbiAgICB9LCAzMDBfMDAwKTtcblxuICAgIHRoaXMucGVuZGluZ0xvZ2luID0geyB3b3JrZXIsIG5hbWUsIHRpbWVyIH07XG4gICAgcmV0dXJuIHsgYXV0aFVybCB9O1xuICB9XG5cbiAgZ2V0IHBlbmRpbmdMb2dpbk5hbWUoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMucGVuZGluZ0xvZ2luPy5uYW1lID8/IG51bGw7XG4gIH1cblxuICBhc3luYyBjb21wbGV0ZUJyb3dzZXJMb2dpbigpOiBQcm9taXNlPHtcbiAgICBhY2Nlc3NUb2tlbjogc3RyaW5nO1xuICAgIHJlZnJlc2hUb2tlbjogc3RyaW5nO1xuICAgIGlkVG9rZW4/OiBzdHJpbmc7XG4gICAgYWNjb3VudElkOiBzdHJpbmc7XG4gIH0+IHtcbiAgICBpZiAoIXRoaXMucGVuZGluZ0xvZ2luKSB0aHJvdyBuZXcgRXJyb3IoXCJubyBwZW5kaW5nIGxvZ2luXCIpO1xuICAgIGNvbnN0IHsgd29ya2VyLCB0aW1lciB9ID0gdGhpcy5wZW5kaW5nTG9naW47XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgd29ya2VyLndhaXRGb3JCcm93c2VyTG9naW5Db21wbGV0ZSgpO1xuICAgICAgY29uc3QgY3JlZHMgPSBhd2FpdCB3b3JrZXIucmVhZE1hbmFnZWRDcmVkZW50aWFscygpO1xuICAgICAgaWYgKCFjcmVkcykgdGhyb3cgbmV3IEVycm9yKFwiZmFpbGVkIHRvIHJlYWQgY3JlZGVudGlhbHMgYWZ0ZXIgbG9naW5cIik7XG4gICAgICByZXR1cm4gY3JlZHM7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gICAgICB0aGlzLnBlbmRpbmdMb2dpbiA9IG51bGw7XG4gICAgICBhd2FpdCB3b3JrZXIua2lsbCgpLmNhdGNoKCgpID0+IHt9KTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgU3RhdHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgZ2V0IHdvcmtlckNvdW50KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIFsuLi50aGlzLndvcmtlclBvb2wudmFsdWVzKCldLmZsYXQoKS5sZW5ndGg7XG4gIH1cblxuICBnZXQgcmVhZHlXb3JrZXJDb3VudCgpOiBudW1iZXIge1xuICAgIHJldHVybiBbLi4udGhpcy53b3JrZXJQb29sLnZhbHVlcygpXS5mbGF0KCkuZmlsdGVyKCh3KSA9PiB3LmlzUmVhZHkpLmxlbmd0aDtcbiAgfVxuXG4gIGdldCBxdWV1ZUxlbmd0aCgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnF1ZXVlLmxlbmd0aDtcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7OztBQXFCQSxNQUFNLFNBQVMsVUFBVSxDQUFDLFNBQVMsb0JBQW9CLENBQUM7QUFFeEQsTUFBTSxpQkFBaUI7QUFFdkIsTUFBTSxvQkFBb0IsS0FBSyxJQUM3QixPQUFPLFFBQVEsSUFBSSwyQkFBMkIsRUFBRSxFQUZwQixFQUk3QjtBQUNELE1BQU0sbUJBQW1CO0FBQ3pCLE1BQU0saUJBQWlCO0FBQ3ZCLE1BQU0sb0JBQW9CO0FBRzFCLE1BQU0sdUJBQXVCLFFBQVEsSUFBSSw4QkFBOEI7QUFFdkUsTUFBTSx1QkFBdUI7QUFDN0IsTUFBTSx1QkFBdUI7QUFHN0IsU0FBUyxhQUFhLFNBQWlCLGFBQTZCO0FBQ2xFLFFBQU8sVUFBVSxLQUFLOztBQUd4QixTQUFTLE1BQU0sSUFBMkI7QUFDeEMsUUFBTyxJQUFJLFNBQVMsWUFBWSxXQUFXLFNBQVMsR0FBRyxDQUFDOztBQVUxRCxJQUFhLG1CQUFiLE1BQWEsaUJBQStDO0NBQzFELDZCQUFhLElBQUksS0FBcUM7Q0FDdEQsV0FBVztDQUNYLFFBQXFCLEVBQUU7Q0FFdkIsTUFBTSxRQUF1QjtFQUMzQixNQUFNLEVBQUUsU0FBUyxNQUFNLFdBQVcsU0FBUyxJQUFJO0VBQy9DLE1BQU0sZUFBZSxLQUFLLFFBQVEsTUFBTSxFQUFFLGFBQWEsU0FBUztBQUNoRSxTQUFPLEtBQ0wsWUFBWSxhQUFhLE9BQU8sa0JBQWtCLGtCQUFrQixnQkFDckU7QUFFRCxRQUFNLFFBQVEsV0FDWixhQUFhLElBQUksT0FBTyxNQUFNO0FBQzVCLFNBQU0sS0FBSyxhQUFhLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFB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
@@ -0,0 +1,97 @@
1
+ /**
2
+ * conv-routing — conversation(=codex thread) 재사용을 위한 thread 좌표 검증/구성.
3
+ *
4
+ * 목표: thread(=conversation_id=prompt_cache_key)를 재사용해 OpenAI prompt caching 적중.
5
+ * - 후속 turn: 기존 thread 에 delta(마지막 user / tool 결과)만 보냄 (history inject 없음).
6
+ * - 첫 turn / 검증 실패: 새 thread + history inject (폴백, 정확성 보존).
7
+ *
8
+ * 식별: 서버 발급 불투명 thread 좌표(QgridThreadCoord)를 클라가 그대로 회송.
9
+ * 검증: systemHash(다른 대화 오접속 차단) + (dispatcher의) epoch/worker 생존.
10
+ */
11
+ import { createHash } from "node:crypto";
12
+
13
+ import { type JsonValue } from "../../codex-protocol/serde_json/JsonValue";
14
+ import { type UserInput } from "../../codex-protocol/v2/UserInput";
15
+ import { type ReuseThreadCoord } from "../../utils/providers/common/provider-dispatcher";
16
+ import { type QgridThreadCoord, type QueryInput, type QgridToolResultInput } from "./qgrid.types";
17
+
18
+ export function systemHash(system?: string): string {
19
+ return createHash("sha256")
20
+ .update(system ?? "")
21
+ .digest("hex")
22
+ .slice(0, 16);
23
+ }
24
+
25
+ // tool 결과를 다음 turn 의 input text 로 변환 (codex turn/start.input 은 UserInput 만 받음).
26
+ function toolResultsToText(toolResults: QgridToolResultInput[]): string {
27
+ const lines = toolResults.map((tr) => {
28
+ const name = tr.toolName ? ` (${tr.toolName})` : "";
29
+ const errMark = tr.isError ? " [error]" : "";
30
+ return `Tool result for call ${tr.toolCallId}${name}${errMark}: ${tr.output}`;
31
+ });
32
+ return `${lines.join("\n")}\n\nNow continue answering the user's request using these results.`;
33
+ }
34
+
35
+ export interface ConvDecision {
36
+ // dispatcher 로 넘길 reuse 좌표 (검증 통과 시에만). dispatcher 가 epoch/worker 생존 재검증 후 폴백 가능.
37
+ reuse?: ReuseThreadCoord;
38
+ // 재사용 성공 시 turn/start 에 보낼 input — delta(마지막 user/tool 결과)만. reuse 가 있을 때만 설정.
39
+ reuseInput?: Array<UserInput>;
40
+ // 첫 turn / 재사용 폴백 시 보낼 input — 전체 prompt. 항상 설정(폴백 안전망).
41
+ coldInput: Array<UserInput>;
42
+ // 새 thread 일 때 inject 할 전체 history. 재사용 성공 시엔 dispatcher 가 안 쓴다(thread 가 이미 누적).
43
+ coldHistory?: Array<JsonValue>;
44
+ // 이 요청 시점의 system 해시 (응답에서 thread 좌표 발급 시 사용).
45
+ systemHash: string;
46
+ }
47
+
48
+ // thread 좌표 + 들어온 요청으로 "재사용 가능한가"를 판정하고, reuse/cold 양쪽 payload 를 구성한다.
49
+ // dispatcher 단의 epoch/worker 생존 검증은 여기서 못 하므로(상태 없음) reuse 좌표 + 두 payload 를
50
+ // 모두 넘기고, dispatcher 의 acquireReuseWorker 결과에 따라 reuse(delta) 또는 cold(전체) 를 고른다.
51
+ // 양쪽을 다 들고 가야 reuse 가 실패로 폴백돼도 전체 history 로 문맥을 복구할 수 있다.
52
+ export function decideConvRouting(input: QueryInput): ConvDecision {
53
+ const sysHash = systemHash(input.system);
54
+ const history: Array<JsonValue> | undefined = input.history
55
+ ? (JSON.parse(input.history) as Array<JsonValue>)
56
+ : undefined;
57
+
58
+ const coord = input.runContext?.threadCoord;
59
+
60
+ // 재사용 자격: 좌표 존재 + system 동일. sessionKey 격리 + systemHash 로 대화 동일성이
61
+ // 보장되고, 공통 prefix 는 codex 가 prompt_cache_key 고정으로 알아서 캐시한다.
62
+ const eligible = coord !== undefined && coord.systemHash === sysHash;
63
+
64
+ return {
65
+ reuse:
66
+ eligible && coord
67
+ ? { workerId: coord.workerId, threadId: coord.threadId, epoch: coord.epoch }
68
+ : undefined,
69
+ // 재사용 turn 은 delta(마지막 user/tool 결과)만 — thread 가 이미 이전 turn 들을 누적.
70
+ reuseInput: eligible ? buildDeltaInput(input) : undefined,
71
+ // 첫 turn / 폴백: 전체 prompt + history inject.
72
+ coldInput: [{ type: "text", text: input.prompt, text_elements: [] }],
73
+ coldHistory: history,
74
+ systemHash: sysHash,
75
+ };
76
+ }
77
+
78
+ // 후속 turn 의 input: tool 결과가 있으면 그걸 text 로, 없으면 마지막 user(prompt).
79
+ function buildDeltaInput(input: QueryInput): Array<UserInput> {
80
+ if (input.toolResults && input.toolResults.length > 0) {
81
+ return [{ type: "text", text: toolResultsToText(input.toolResults), text_elements: [] }];
82
+ }
83
+ return [{ type: "text", text: input.prompt, text_elements: [] }];
84
+ }
85
+
86
+ // 응답 threadCoord + 요청 시점 정보로 다음 thread 좌표를 발급한다.
87
+ export function issueConvContext(
88
+ threadCoord: ReuseThreadCoord,
89
+ decision: ConvDecision,
90
+ ): QgridThreadCoord {
91
+ return {
92
+ workerId: threadCoord.workerId,
93
+ threadId: threadCoord.threadId,
94
+ epoch: threadCoord.epoch,
95
+ systemHash: decision.systemHash,
96
+ };
97
+ }
@@ -48,7 +48,8 @@ export async function beforeQuery(args: QueryInput): Promise<{
48
48
  let requestLogId: number;
49
49
  let stepIndex = 0;
50
50
 
51
- if (args.runContext) {
51
+ // requestLogId 가 있을 때만 기존 run 연장. threadCoord 만 있는 conv 는 새 run 으로 시작.
52
+ if (args.runContext?.requestLogId !== undefined) {
52
53
  requestLogId = args.runContext.requestLogId;
53
54
 
54
55
  if (args.toolResults && args.toolResults.length > 0) {
@@ -26,6 +26,7 @@ import { calculateCostUsd } from "../../utils/providers/common/model-cost";
26
26
  import { strictify } from "../../utils/providers/common/strictifier";
27
27
  import { type OpenAIDispatcher } from "../../utils/providers/openai/openai-dispatcher";
28
28
  import { type TokenSubsetA } from "../sonamu.generated";
29
+ import { decideConvRouting, issueConvContext } from "./conv-routing";
29
30
  import { type QueryInput, type QueryOutput, type TokenStats } from "./qgrid.types";
30
31
  import { maskToken, ProcessError, QuotaError, TimeoutError } from "./qgrid.types";
31
32
  import { type TokenSubscriber } from "./token-subscriber";
@@ -130,9 +131,9 @@ export class QgridDispatcherClass {
130
131
  assert(model, "unknown model");
131
132
  if (provider === "openai") {
132
133
  if (!this.openaiDispatcher) throw new QuotaError("OpenAI dispatcher not initialized");
134
+ const decision = decideConvRouting(input);
133
135
  const result = await this.openaiDispatcher.generate({
134
136
  model: model,
135
- input: [{ type: "text" as const, text: input.prompt, text_elements: [] }],
136
137
  systemPrompt: input.system,
137
138
  outputSchema: outputSchema
138
139
  ? (strictify(outputSchema as Parameters<typeof strictify>[0]) as JsonValue)
@@ -141,9 +142,13 @@ export class QgridDispatcherClass {
141
142
  verbosity: input.verbosity,
142
143
  reasoningSummary: input.reasoningSummary,
143
144
  serviceTier: input.serviceTier,
144
- history: input.history ? JSON.parse(input.history) : undefined,
145
+ coldInput: decision.coldInput,
146
+ coldHistory: decision.coldHistory,
147
+ reuse: decision.reuse,
148
+ reuseInput: decision.reuseInput,
145
149
  });
146
150
 
151
+ const issuedCoord = issueConvContext(result.threadCoord, decision);
147
152
  return applyToolCallEmulation(
148
153
  {
149
154
  text: result.text,
@@ -163,6 +168,7 @@ export class QgridDispatcherClass {
163
168
  }),
164
169
  },
165
170
  input.tools,
171
+ issuedCoord,
166
172
  );
167
173
  }
168
174
  }
@@ -230,10 +236,10 @@ export class QgridDispatcherClass {
230
236
  ? (JSON.parse(input.jsonSchema) as JsonValue)
231
237
  : undefined;
232
238
 
239
+ const decision = decideConvRouting(input);
233
240
  await this.openaiDispatcher.generateStream(
234
241
  {
235
242
  model,
236
- input: [{ type: "text" as const, text: input.prompt, text_elements: [] }],
237
243
  systemPrompt: input.system,
238
244
  outputSchema: outputSchema
239
245
  ? (strictify(outputSchema as Parameters<typeof strictify>[0]) as JsonValue)
@@ -242,17 +248,21 @@ export class QgridDispatcherClass {
242
248
  verbosity: input.verbosity,
243
249
  reasoningSummary: input.reasoningSummary,
244
250
  serviceTier: input.serviceTier,
245
- history: input.history ? JSON.parse(input.history) : undefined,
251
+ coldInput: decision.coldInput,
252
+ coldHistory: decision.coldHistory,
253
+ reuse: decision.reuse,
254
+ reuseInput: decision.reuseInput,
246
255
  },
247
256
  {
248
257
  onDelta: cb.onDelta,
249
258
  onThreadId: cb.onThreadId,
250
259
  onTurnId: cb.onTurnId,
251
260
  onComplete: (turnResult) => {
261
+ const issuedCoord = issueConvContext(turnResult.threadCoord, decision);
252
262
  const applied = applyToolCallEmulation(
253
263
  {
254
264
  text: turnResult.text,
255
- tokenName: (turnResult as unknown as { tokenName?: string }).tokenName,
265
+ tokenName: turnResult.tokenName,
256
266
  model: turnResult.model,
257
267
  usage: {
258
268
  input_tokens: turnResult.usage.inputTokens,
@@ -268,6 +278,7 @@ export class QgridDispatcherClass {
268
278
  }),
269
279
  },
270
280
  input.tools,
281
+ issuedCoord,
271
282
  );
272
283
  cb.onComplete(applied);
273
284
  },
@@ -37,6 +37,7 @@ import {
37
37
  type HealthResponse,
38
38
  type OAuthStartResult,
39
39
  type QueryOutput,
40
+ type QgridRunContext,
40
41
  StreamEvents,
41
42
  type TokenStats,
42
43
  type UsageResponse,
@@ -98,7 +99,11 @@ class QgridFrameClass extends BaseFrameClass {
98
99
  try {
99
100
  const result = await QgridDispatcher.query(args, args.timeout);
100
101
  const lifecycle = await afterQuery(requestLogId, stepIndex, args, result);
101
- return { ...result, runContext: lifecycle.runContext };
102
+ // lifecycle.runContext(requestLogId) 와 dispatcher 가 실은 threadCoord 를 병합.
103
+ return {
104
+ ...result,
105
+ runContext: { ...lifecycle.runContext, threadCoord: result.runContext?.threadCoord },
106
+ };
102
107
  } catch (e) {
103
108
  await finishRunWithError(requestLogId, (e as Error).message, args);
104
109
  throw e;
@@ -222,7 +227,10 @@ class QgridFrameClass extends BaseFrameClass {
222
227
 
223
228
  // dispatcher 완료 후 lifecycle 처리 (await 안전)
224
229
  if (streamResult) {
225
- let runContext: { requestLogId: number } | undefined;
230
+ // dispatcher 실은 thread 재사용 좌표 (auto/run 공통으로 클라에 회송해야 함).
231
+ const threadCoord = streamResult.runContext?.threadCoord;
232
+ let runContext: QgridRunContext | undefined =
233
+ threadCoord !== undefined ? { threadCoord } : undefined;
226
234
  if (runInfo) {
227
235
  try {
228
236
  const lifecycle = await afterQuery(
@@ -231,7 +239,7 @@ class QgridFrameClass extends BaseFrameClass {
231
239
  args,
232
240
  streamResult,
233
241
  );
234
- runContext = lifecycle.runContext;
242
+ runContext = { ...lifecycle.runContext, threadCoord };
235
243
  } catch (e) {
236
244
  logger.error(`stream afterQuery failed: ${(e as Error).message}`);
237
245
  }
@@ -43,8 +43,21 @@ export type ServiceTier = z.infer<typeof ServiceTier>;
43
43
  export const QgridLogMode = z.enum(["auto", "run", "none"]);
44
44
  export type QgridLogMode = z.infer<typeof QgridLogMode>;
45
45
 
46
+ // codex thread 좌표. thread 재사용으로 conversation_id(=prompt_cache_key)를 고정해
47
+ // OpenAI prompt caching 을 살리기 위한 핸들. 서버가 발급하고 클라가 다음 요청에 그대로 회송한다.
48
+ // 핸들 미전송 시 기존 "매 turn 새 thread + history inject" 동작으로 폴백(QgridRunContext 참조).
49
+ export const QgridThreadCoord = z.object({
50
+ workerId: z.number(), // 고정 라우팅 (thread 는 worker 프로세스 메모리에만 존재)
51
+ threadId: z.string(), // codex conversation_id = prompt_cache_key
52
+ epoch: z.number(), // worker spawn 카운터. restart 로 thread 증발 감지
53
+ systemHash: z.string(), // system_prompt 해시. 다른 대화 오접속 방지
54
+ });
55
+ export type QgridThreadCoord = z.infer<typeof QgridThreadCoord>;
56
+
46
57
  export const QgridRunContext = z.object({
47
- requestLogId: z.number(),
58
+ // run lifecycle(logMode="run") 에서만 발급. auto 모드(예: IF)는 threadCoord 만 왕복.
59
+ requestLogId: z.number().optional(),
60
+ threadCoord: QgridThreadCoord.optional(),
48
61
  });
49
62
  export type QgridRunContext = z.infer<typeof QgridRunContext>;
50
63
 
@@ -1,7 +1,12 @@
1
1
  import { getLogger } from "@logtape/logtape";
2
2
 
3
3
  import { type JsonValue } from "../../codex-protocol/serde_json/JsonValue";
4
- import { type QgridContent, type QgridTool, type QueryOutput } from "./qgrid.types";
4
+ import {
5
+ type QgridContent,
6
+ type QgridThreadCoord,
7
+ type QgridTool,
8
+ type QueryOutput,
9
+ } from "./qgrid.types";
5
10
 
6
11
  const logger = getLogger(["qgrid", "tool-emulation"]);
7
12
 
@@ -52,12 +57,16 @@ export function buildToolCallSchema(tools: QgridTool[]): JsonValue {
52
57
  }
53
58
 
54
59
  export function applyToolCallEmulation(
55
- result: Omit<QueryOutput, "content" | "finishReason">,
60
+ result: Omit<QueryOutput, "content" | "finishReason" | "runContext">,
56
61
  tools?: QgridTool[],
62
+ threadCoord?: QgridThreadCoord,
57
63
  ): QueryOutput {
64
+ // thread 재사용 좌표를 runContext 로 실어 올린다 (auto 모드는 requestLogId 없이 threadCoord 만).
65
+ const runContext = threadCoord ? { threadCoord } : undefined;
66
+
58
67
  if (!tools?.length) {
59
68
  const content: QgridContent[] = [{ type: "text", text: result.text }];
60
- return { ...result, content, finishReason: "stop" };
69
+ return { ...result, content, finishReason: "stop", runContext };
61
70
  }
62
71
 
63
72
  let parsed: ToolCallResponse;
@@ -65,7 +74,12 @@ export function applyToolCallEmulation(
65
74
  parsed = JSON.parse(result.text) as ToolCallResponse;
66
75
  } catch (e) {
67
76
  logger.warn(`tool-call emulation parse failed, falling back to text: ${(e as Error).message}`);
68
- return { ...result, content: [{ type: "text", text: result.text }], finishReason: "stop" };
77
+ return {
78
+ ...result,
79
+ content: [{ type: "text", text: result.text }],
80
+ finishReason: "stop",
81
+ runContext,
82
+ };
69
83
  }
70
84
 
71
85
  if (parsed.action === "tool_call") {
@@ -82,7 +96,7 @@ export function applyToolCallEmulation(
82
96
  input: toolCall.args,
83
97
  };
84
98
  });
85
- return { ...result, content, finishReason: "tool-calls" };
99
+ return { ...result, content, finishReason: "tool-calls", runContext };
86
100
  }
87
101
 
88
102
  const text = parsed.answer ?? result.text;
@@ -91,5 +105,6 @@ export function applyToolCallEmulation(
91
105
  text,
92
106
  content: [{ type: "text", text }],
93
107
  finishReason: "stop",
108
+ runContext,
94
109
  };
95
110
  }
@@ -227,7 +227,13 @@ Content-Type: application/json
227
227
  "isStep": false,
228
228
  "logMode": "auto",
229
229
  "runContext": {
230
- "requestLogId": 0
230
+ "requestLogId": 0,
231
+ "threadCoord": {
232
+ "workerId": 0,
233
+ "threadId": "THREADID",
234
+ "epoch": 0,
235
+ "systemHash": "SYSTEMHASH"
236
+ }
231
237
  },
232
238
  "toolResults": [
233
239
  {
@@ -268,7 +274,13 @@ Content-Type: application/json
268
274
  "isStep": false,
269
275
  "logMode": "auto",
270
276
  "runContext": {
271
- "requestLogId": 0
277
+ "requestLogId": 0,
278
+ "threadCoord": {
279
+ "workerId": 0,
280
+ "threadId": "THREADID",
281
+ "epoch": 0,
282
+ "systemHash": "SYSTEMHASH"
283
+ }
272
284
  },
273
285
  "toolResults": [
274
286
  {