@ruvector/ruvllm 4.0.1 → 4.2.0

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 (171) hide show
  1. package/README.md +446 -160
  2. package/bin/cli.js +1066 -0
  3. package/bin/gpu-delegate.js +67 -0
  4. package/bin/ruvllm-adapters.js +5 -0
  5. package/bin/ruvllm-gpu-loop.js +5 -0
  6. package/bin/serve.js +265 -0
  7. package/dist/cjs/benchmarks/embedding-benchmark.d.ts +102 -0
  8. package/dist/cjs/benchmarks/embedding-benchmark.d.ts.map +1 -0
  9. package/dist/cjs/benchmarks/embedding-benchmark.js +436 -0
  10. package/dist/cjs/benchmarks/index.d.ts +34 -0
  11. package/dist/cjs/benchmarks/index.d.ts.map +1 -0
  12. package/dist/cjs/benchmarks/index.js +132 -0
  13. package/dist/cjs/benchmarks/model-comparison.d.ts +71 -0
  14. package/dist/cjs/benchmarks/model-comparison.d.ts.map +1 -0
  15. package/dist/cjs/benchmarks/model-comparison.js +476 -0
  16. package/dist/cjs/benchmarks/routing-benchmark.d.ts +70 -0
  17. package/dist/cjs/benchmarks/routing-benchmark.d.ts.map +1 -0
  18. package/dist/cjs/benchmarks/routing-benchmark.js +289 -0
  19. package/dist/cjs/contrastive.d.ts +229 -0
  20. package/dist/cjs/contrastive.d.ts.map +1 -0
  21. package/dist/cjs/contrastive.js +589 -0
  22. package/dist/cjs/engine.d.ts +160 -0
  23. package/dist/cjs/engine.d.ts.map +1 -0
  24. package/dist/cjs/engine.js +552 -0
  25. package/dist/cjs/export.d.ts +182 -0
  26. package/dist/cjs/export.d.ts.map +1 -0
  27. package/dist/cjs/export.js +422 -0
  28. package/dist/cjs/federated.d.ts +233 -0
  29. package/dist/cjs/federated.d.ts.map +1 -0
  30. package/dist/cjs/federated.js +525 -0
  31. package/dist/cjs/index.d.ts +68 -0
  32. package/dist/cjs/index.d.ts.map +1 -0
  33. package/dist/cjs/index.js +104 -0
  34. package/dist/cjs/intelligence.d.ts +136 -0
  35. package/dist/cjs/intelligence.d.ts.map +1 -0
  36. package/dist/cjs/intelligence.js +226 -0
  37. package/dist/cjs/lora.d.ts +251 -0
  38. package/dist/cjs/lora.d.ts.map +1 -0
  39. package/dist/cjs/lora.js +509 -0
  40. package/dist/cjs/models.d.ts +129 -0
  41. package/dist/cjs/models.d.ts.map +1 -0
  42. package/dist/cjs/models.js +323 -0
  43. package/dist/cjs/native.d.ts +140 -0
  44. package/dist/cjs/native.d.ts.map +1 -0
  45. package/dist/cjs/native.js +98 -0
  46. package/dist/cjs/session.d.ts +80 -0
  47. package/dist/cjs/session.d.ts.map +1 -0
  48. package/dist/cjs/session.js +203 -0
  49. package/dist/cjs/simd.d.ts +90 -0
  50. package/dist/cjs/simd.d.ts.map +1 -0
  51. package/dist/cjs/simd.js +209 -0
  52. package/dist/cjs/sona.d.ts +223 -0
  53. package/dist/cjs/sona.d.ts.map +1 -0
  54. package/dist/cjs/sona.js +562 -0
  55. package/dist/cjs/streaming.d.ts +55 -0
  56. package/dist/cjs/streaming.d.ts.map +1 -0
  57. package/dist/cjs/streaming.js +131 -0
  58. package/dist/cjs/training.d.ts +312 -0
  59. package/dist/cjs/training.d.ts.map +1 -0
  60. package/dist/cjs/training.js +610 -0
  61. package/dist/cjs/types.d.ts +696 -0
  62. package/dist/cjs/types.d.ts.map +1 -0
  63. package/dist/cjs/types.js +6 -0
  64. package/dist/esm/benchmarks/embedding-benchmark.d.ts +102 -0
  65. package/dist/esm/benchmarks/embedding-benchmark.d.ts.map +1 -0
  66. package/dist/esm/benchmarks/embedding-benchmark.js +427 -0
  67. package/dist/esm/benchmarks/index.d.ts +34 -0
  68. package/dist/esm/benchmarks/index.d.ts.map +1 -0
  69. package/dist/esm/benchmarks/index.js +110 -0
  70. package/dist/esm/benchmarks/model-comparison.d.ts +71 -0
  71. package/dist/esm/benchmarks/model-comparison.d.ts.map +1 -0
  72. package/dist/esm/benchmarks/model-comparison.js +434 -0
  73. package/dist/esm/benchmarks/routing-benchmark.d.ts +70 -0
  74. package/dist/esm/benchmarks/routing-benchmark.d.ts.map +1 -0
  75. package/dist/esm/benchmarks/routing-benchmark.js +283 -0
  76. package/dist/esm/contrastive.d.ts +229 -0
  77. package/dist/esm/contrastive.d.ts.map +1 -0
  78. package/dist/esm/contrastive.js +578 -0
  79. package/dist/esm/engine.d.ts +160 -0
  80. package/dist/esm/engine.d.ts.map +1 -0
  81. package/dist/esm/engine.js +548 -0
  82. package/dist/esm/export.d.ts +182 -0
  83. package/dist/esm/export.d.ts.map +1 -0
  84. package/dist/esm/export.js +414 -0
  85. package/dist/esm/federated.d.ts +233 -0
  86. package/dist/esm/federated.d.ts.map +1 -0
  87. package/dist/esm/federated.js +520 -0
  88. package/dist/esm/index.d.ts +68 -0
  89. package/dist/esm/index.d.ts.map +1 -0
  90. package/dist/esm/index.js +84 -0
  91. package/dist/esm/intelligence.d.ts +136 -0
  92. package/dist/esm/intelligence.d.ts.map +1 -0
  93. package/dist/esm/intelligence.js +188 -0
  94. package/dist/esm/lora.d.ts +251 -0
  95. package/dist/esm/lora.d.ts.map +1 -0
  96. package/dist/esm/lora.js +504 -0
  97. package/dist/esm/models.d.ts +129 -0
  98. package/dist/esm/models.d.ts.map +1 -0
  99. package/dist/esm/models.js +282 -0
  100. package/dist/esm/native.d.ts +140 -0
  101. package/dist/esm/native.d.ts.map +1 -0
  102. package/dist/esm/native.js +100 -0
  103. package/dist/esm/session.d.ts +80 -0
  104. package/dist/esm/session.d.ts.map +1 -0
  105. package/dist/esm/session.js +199 -0
  106. package/dist/esm/simd.d.ts +90 -0
  107. package/dist/esm/simd.d.ts.map +1 -0
  108. package/dist/esm/simd.js +205 -0
  109. package/dist/esm/sona.d.ts +223 -0
  110. package/dist/esm/sona.d.ts.map +1 -0
  111. package/dist/esm/sona.js +556 -0
  112. package/dist/esm/streaming.d.ts +55 -0
  113. package/dist/esm/streaming.d.ts.map +1 -0
  114. package/dist/esm/streaming.js +126 -0
  115. package/dist/esm/training.d.ts +312 -0
  116. package/dist/esm/training.d.ts.map +1 -0
  117. package/dist/esm/training.js +603 -0
  118. package/dist/esm/types.d.ts +696 -0
  119. package/dist/esm/types.d.ts.map +1 -0
  120. package/dist/esm/types.js +5 -0
  121. package/gpu/_load.cjs +38 -0
  122. package/gpu/_load.mjs +33 -0
  123. package/gpu/adapters.cjs +3 -0
  124. package/gpu/adapters.mjs +5 -0
  125. package/gpu/engine.cjs +3 -0
  126. package/gpu/engine.mjs +5 -0
  127. package/gpu/fixture.cjs +3 -0
  128. package/gpu/fixture.mjs +5 -0
  129. package/gpu/gpu-host.cjs +3 -0
  130. package/gpu/gpu-host.mjs +5 -0
  131. package/gpu/gpu-loop.cjs +3 -0
  132. package/gpu/gpu-loop.mjs +5 -0
  133. package/gpu/gpu-spool-adapter.cjs +3 -0
  134. package/gpu/gpu-spool-adapter.mjs +5 -0
  135. package/gpu/vast-host-adapter.cjs +3 -0
  136. package/gpu/vast-host-adapter.mjs +5 -0
  137. package/package.json +159 -49
  138. package/scripts/ensemble-model-compare.js +287 -0
  139. package/scripts/fix-esm-extensions.mjs +220 -0
  140. package/scripts/huggingface/README.md +537 -0
  141. package/scripts/huggingface/publish.sh +112 -0
  142. package/scripts/hybrid-model-compare.js +373 -0
  143. package/scripts/improved-model-compare.js +288 -0
  144. package/scripts/optimized-model-compare.js +364 -0
  145. package/scripts/real-model-compare.js +280 -0
  146. package/scripts/smoke-esm.mjs +82 -0
  147. package/scripts/training/agentic-flow-capabilities.json +329 -0
  148. package/scripts/training/claude-code-synth.js +505 -0
  149. package/scripts/training/claude-flow-capabilities.json +1322 -0
  150. package/scripts/training/claude-hard-negatives.js +222 -0
  151. package/scripts/training/contrastive-finetune.js +517 -0
  152. package/scripts/training/ecosystem-triplets.jsonl +1467 -0
  153. package/scripts/training/generate-ecosystem-triplets.js +1324 -0
  154. package/scripts/training/routing-dataset.js +634 -0
  155. package/scripts/training/ruvector-capabilities.json +798 -0
  156. package/scripts/training/validate-ecosystem.js +381 -0
  157. package/scripts/training/validation-results.json +23 -0
  158. package/GPU-LOOP.md +0 -229
  159. package/LICENSE +0 -201
  160. package/NOTICE +0 -9
  161. package/examples/gpu-loop.example.json +0 -26
  162. package/src/adapters-cli.mjs +0 -23
  163. package/src/adapters.mjs +0 -91
  164. package/src/cli.mjs +0 -30
  165. package/src/engine.mjs +0 -238
  166. package/src/fixture.mjs +0 -38
  167. package/src/gpu-host.mjs +0 -47
  168. package/src/gpu-loop-cli.mjs +0 -67
  169. package/src/gpu-loop.mjs +0 -184
  170. package/src/gpu-spool-adapter.mjs +0 -80
  171. package/src/vast-host-adapter.mjs +0 -274
@@ -0,0 +1,589 @@
1
+ "use strict";
2
+ /**
3
+ * Contrastive Fine-tuning for RuvLTRA Claude Code Router
4
+ *
5
+ * Uses triplet loss to fine-tune embeddings:
6
+ * - Anchor: task description
7
+ * - Positive: correct agent description
8
+ * - Negative: wrong agent description (hard negative)
9
+ *
10
+ * Goal: minimize distance(anchor, positive) and maximize distance(anchor, negative)
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { ContrastiveTrainer, tripletLoss, infoNCELoss } from '@ruvector/ruvllm';
15
+ *
16
+ * const trainer = new ContrastiveTrainer({
17
+ * epochs: 10,
18
+ * batchSize: 16,
19
+ * margin: 0.5,
20
+ * });
21
+ *
22
+ * // Add triplets
23
+ * trainer.addTriplet(anchorEmb, positiveEmb, negativeEmb, true);
24
+ *
25
+ * // Train and export
26
+ * const results = trainer.train();
27
+ * trainer.exportTrainingData('./output');
28
+ * ```
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.AGENT_TRAINING_DATA = exports.ContrastiveTrainer = void 0;
32
+ exports.cosineSimilarity = cosineSimilarity;
33
+ exports.tripletLoss = tripletLoss;
34
+ exports.infoNCELoss = infoNCELoss;
35
+ exports.computeGradient = computeGradient;
36
+ exports.generateTrainingDataset = generateTrainingDataset;
37
+ exports.generateContrastivePairs = generateContrastivePairs;
38
+ exports.getDatasetStats = getDatasetStats;
39
+ const fs_1 = require("fs");
40
+ const path_1 = require("path");
41
+ /**
42
+ * Default contrastive config
43
+ */
44
+ const DEFAULT_CONTRASTIVE_CONFIG = {
45
+ epochs: 10,
46
+ batchSize: 16,
47
+ learningRate: 0.0001,
48
+ margin: 0.5,
49
+ temperature: 0.07,
50
+ hardNegativeRatio: 0.7,
51
+ outputPath: './training-output',
52
+ };
53
+ /**
54
+ * Compute cosine similarity between two embeddings
55
+ */
56
+ function cosineSimilarity(a, b) {
57
+ if (!a || !b || a.length !== b.length)
58
+ return 0;
59
+ let dot = 0, normA = 0, normB = 0;
60
+ for (let i = 0; i < a.length; i++) {
61
+ dot += a[i] * b[i];
62
+ normA += a[i] * a[i];
63
+ normB += b[i] * b[i];
64
+ }
65
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB) || 1);
66
+ }
67
+ /**
68
+ * Compute triplet loss
69
+ * L = max(0, margin + d(anchor, positive) - d(anchor, negative))
70
+ */
71
+ function tripletLoss(anchorEmb, positiveEmb, negativeEmb, margin = 0.5) {
72
+ const posDist = 1 - cosineSimilarity(anchorEmb, positiveEmb);
73
+ const negDist = 1 - cosineSimilarity(anchorEmb, negativeEmb);
74
+ return Math.max(0, margin + posDist - negDist);
75
+ }
76
+ /**
77
+ * Compute InfoNCE loss (contrastive)
78
+ */
79
+ function infoNCELoss(anchorEmb, positiveEmb, negativeEmbs, temperature = 0.07) {
80
+ const posSim = cosineSimilarity(anchorEmb, positiveEmb) / temperature;
81
+ const negSims = negativeEmbs.map(neg => cosineSimilarity(anchorEmb, neg) / temperature);
82
+ // Softmax denominator
83
+ const maxSim = Math.max(posSim, ...negSims);
84
+ const expPos = Math.exp(posSim - maxSim);
85
+ const expNegs = negSims.map(sim => Math.exp(sim - maxSim));
86
+ const denominator = expPos + expNegs.reduce((a, b) => a + b, 0);
87
+ // Cross-entropy loss
88
+ return -Math.log(expPos / denominator);
89
+ }
90
+ /**
91
+ * Compute gradient for embedding update (simplified)
92
+ */
93
+ function computeGradient(anchorEmb, positiveEmb, negativeEmb, lr = 0.0001) {
94
+ const dim = anchorEmb.length;
95
+ const gradient = new Array(dim).fill(0);
96
+ // Pull anchor towards positive
97
+ for (let i = 0; i < dim; i++) {
98
+ gradient[i] += lr * (positiveEmb[i] - anchorEmb[i]);
99
+ }
100
+ // Push anchor away from negative
101
+ for (let i = 0; i < dim; i++) {
102
+ gradient[i] -= lr * 0.5 * (negativeEmb[i] - anchorEmb[i]);
103
+ }
104
+ return gradient;
105
+ }
106
+ /**
107
+ * Contrastive Trainer for RuvLTRA models
108
+ *
109
+ * Implements triplet loss and InfoNCE loss for embedding fine-tuning.
110
+ */
111
+ class ContrastiveTrainer {
112
+ constructor(config) {
113
+ this.triplets = [];
114
+ this.history = [];
115
+ this.agentEmbeddings = new Map();
116
+ this.config = { ...DEFAULT_CONTRASTIVE_CONFIG, ...config };
117
+ }
118
+ /**
119
+ * Add a training triplet
120
+ */
121
+ addTriplet(anchor, anchorEmb, positive, positiveEmb, negative, negativeEmb, isHard = false) {
122
+ this.triplets.push({
123
+ anchor,
124
+ anchorEmb,
125
+ positive,
126
+ positiveEmb,
127
+ negative,
128
+ negativeEmb,
129
+ isHard,
130
+ });
131
+ }
132
+ /**
133
+ * Add agent embedding for reference
134
+ */
135
+ addAgentEmbedding(agentName, embedding) {
136
+ this.agentEmbeddings.set(agentName, embedding);
137
+ }
138
+ /**
139
+ * Get all agent embeddings
140
+ */
141
+ getAgentEmbeddings() {
142
+ return this.agentEmbeddings;
143
+ }
144
+ /**
145
+ * Get triplet count
146
+ */
147
+ getTripletCount() {
148
+ return this.triplets.length;
149
+ }
150
+ /**
151
+ * Simulate training (compute losses without actual backprop)
152
+ * In a full implementation, this would use proper gradient descent
153
+ */
154
+ train() {
155
+ const startTime = Date.now();
156
+ const { epochs, batchSize, margin } = this.config;
157
+ if (this.triplets.length === 0) {
158
+ return {
159
+ tripletCount: 0,
160
+ finalLoss: 0,
161
+ initialLoss: 0,
162
+ improvement: 0,
163
+ history: [],
164
+ durationMs: 0,
165
+ };
166
+ }
167
+ for (let epoch = 0; epoch < epochs; epoch++) {
168
+ let epochLoss = 0;
169
+ let batchCount = 0;
170
+ // Shuffle triplets
171
+ const shuffled = [...this.triplets].sort(() => Math.random() - 0.5);
172
+ for (let i = 0; i < shuffled.length; i += batchSize) {
173
+ const batch = shuffled.slice(i, i + batchSize);
174
+ let batchLoss = 0;
175
+ for (const triplet of batch) {
176
+ const loss = tripletLoss(triplet.anchorEmb, triplet.positiveEmb, triplet.negativeEmb, margin);
177
+ batchLoss += loss;
178
+ }
179
+ epochLoss += batchLoss / batch.length;
180
+ batchCount++;
181
+ }
182
+ const avgLoss = epochLoss / batchCount;
183
+ this.history.push({ epoch: epoch + 1, loss: avgLoss });
184
+ }
185
+ const initialLoss = this.history[0]?.loss || 0;
186
+ const finalLoss = this.history[this.history.length - 1]?.loss || 0;
187
+ const improvement = initialLoss > 0 ? (1 - finalLoss / initialLoss) * 100 : 0;
188
+ return {
189
+ tripletCount: this.triplets.length,
190
+ finalLoss,
191
+ initialLoss,
192
+ improvement,
193
+ history: this.history,
194
+ durationMs: Date.now() - startTime,
195
+ };
196
+ }
197
+ /**
198
+ * Export training data for external fine-tuning tools
199
+ */
200
+ exportTrainingData(outputPath) {
201
+ const outDir = outputPath || this.config.outputPath;
202
+ if (!(0, fs_1.existsSync)(outDir)) {
203
+ (0, fs_1.mkdirSync)(outDir, { recursive: true });
204
+ }
205
+ // JSONL format for fine-tuning
206
+ const jsonlData = this.triplets.map(t => ({
207
+ anchor: t.anchor,
208
+ positive: t.positive,
209
+ negative: t.negative,
210
+ isHard: t.isHard,
211
+ }));
212
+ // CSV format for analysis
213
+ const csvData = [
214
+ 'anchor,positive,negative,is_hard',
215
+ ...this.triplets.map(t => `"${t.anchor.replace(/"/g, '""')}",${t.positive},${t.negative},${t.isHard}`),
216
+ ].join('\n');
217
+ // Embedding matrix for direct training
218
+ const embeddingData = {
219
+ anchors: this.triplets.map(t => t.anchorEmb),
220
+ positives: this.triplets.map(t => t.positiveEmb),
221
+ negatives: this.triplets.map(t => t.negativeEmb),
222
+ labels: this.triplets.map(t => t.positive),
223
+ };
224
+ (0, fs_1.writeFileSync)((0, path_1.join)(outDir, 'triplets.jsonl'), jsonlData.map(item => JSON.stringify(item)).join('\n'));
225
+ (0, fs_1.writeFileSync)((0, path_1.join)(outDir, 'triplets.csv'), csvData);
226
+ (0, fs_1.writeFileSync)((0, path_1.join)(outDir, 'embeddings.json'), JSON.stringify(embeddingData, null, 2));
227
+ return outDir;
228
+ }
229
+ /**
230
+ * Generate LoRA adapter configuration
231
+ */
232
+ generateLoRAConfig(outputPath) {
233
+ const outDir = outputPath || this.config.outputPath;
234
+ const loraConfig = {
235
+ model_type: 'qwen2',
236
+ base_model: 'Qwen/Qwen2.5-0.5B',
237
+ output_dir: outDir,
238
+ lora_r: 8,
239
+ lora_alpha: 16,
240
+ lora_dropout: 0.05,
241
+ target_modules: ['q_proj', 'v_proj', 'k_proj', 'o_proj'],
242
+ learning_rate: this.config.learningRate,
243
+ num_train_epochs: this.config.epochs,
244
+ per_device_train_batch_size: this.config.batchSize,
245
+ gradient_accumulation_steps: 4,
246
+ warmup_ratio: 0.1,
247
+ loss_type: 'triplet',
248
+ margin: this.config.margin,
249
+ temperature: this.config.temperature,
250
+ train_data: (0, path_1.join)(outDir, 'triplets.jsonl'),
251
+ eval_data: (0, path_1.join)(outDir, 'eval.jsonl'),
252
+ };
253
+ if (!(0, fs_1.existsSync)(outDir)) {
254
+ (0, fs_1.mkdirSync)(outDir, { recursive: true });
255
+ }
256
+ (0, fs_1.writeFileSync)((0, path_1.join)(outDir, 'lora_config.json'), JSON.stringify(loraConfig, null, 2));
257
+ return loraConfig;
258
+ }
259
+ /**
260
+ * Generate training script for external tools
261
+ */
262
+ generateTrainingScript(outputPath) {
263
+ const outDir = outputPath || this.config.outputPath;
264
+ const script = `#!/bin/bash
265
+ # RuvLTRA Fine-tuning Script
266
+ # Prerequisites: pip install transformers peft accelerate
267
+
268
+ set -e
269
+
270
+ MODEL_PATH="${outDir}"
271
+ BASE_MODEL="Qwen/Qwen2.5-0.5B"
272
+
273
+ echo "=== RuvLTRA Contrastive Fine-tuning ==="
274
+ echo "Base model: $BASE_MODEL"
275
+ echo "Output: $MODEL_PATH"
276
+
277
+ # Check for training data
278
+ if [ ! -f "$MODEL_PATH/triplets.jsonl" ]; then
279
+ echo "Error: Training data not found at $MODEL_PATH/triplets.jsonl"
280
+ exit 1
281
+ fi
282
+
283
+ # Install dependencies if needed
284
+ python3 -c "import transformers, peft" 2>/dev/null || {
285
+ echo "Installing dependencies..."
286
+ pip install transformers peft accelerate sentencepiece
287
+ }
288
+
289
+ # Fine-tune with LoRA
290
+ python3 << 'PYTHON'
291
+ import json
292
+ import torch
293
+ from pathlib import Path
294
+ from transformers import AutoModelForCausalLM, AutoTokenizer
295
+ from peft import LoraConfig, get_peft_model, TaskType
296
+
297
+ # Load config
298
+ config_path = Path("${outDir}/lora_config.json")
299
+ with open(config_path) as f:
300
+ config = json.load(f)
301
+
302
+ print(f"Loading base model: {config['base_model']}")
303
+
304
+ # Load model and tokenizer
305
+ tokenizer = AutoTokenizer.from_pretrained(config['base_model'])
306
+ model = AutoModelForCausalLM.from_pretrained(
307
+ config['base_model'],
308
+ torch_dtype=torch.float16,
309
+ device_map='auto'
310
+ )
311
+
312
+ # Configure LoRA
313
+ lora_config = LoraConfig(
314
+ r=config['lora_r'],
315
+ lora_alpha=config['lora_alpha'],
316
+ lora_dropout=config['lora_dropout'],
317
+ target_modules=config['target_modules'],
318
+ task_type=TaskType.CAUSAL_LM,
319
+ )
320
+
321
+ model = get_peft_model(model, lora_config)
322
+ model.print_trainable_parameters()
323
+
324
+ print("Model ready for fine-tuning!")
325
+ print(f"Training data: {config['train_data']}")
326
+ print("Note: Full training requires GPU. This script validates the setup.")
327
+ PYTHON
328
+
329
+ echo ""
330
+ echo "=== Setup Complete ==="
331
+ echo "To train on GPU, run the full training pipeline."
332
+ echo "Training data exported to: $MODEL_PATH/triplets.jsonl"
333
+ `;
334
+ if (!(0, fs_1.existsSync)(outDir)) {
335
+ (0, fs_1.mkdirSync)(outDir, { recursive: true });
336
+ }
337
+ const scriptPath = (0, path_1.join)(outDir, 'train.sh');
338
+ (0, fs_1.writeFileSync)(scriptPath, script);
339
+ return scriptPath;
340
+ }
341
+ /**
342
+ * Get training history
343
+ */
344
+ getHistory() {
345
+ return [...this.history];
346
+ }
347
+ /**
348
+ * Reset trainer
349
+ */
350
+ reset() {
351
+ this.triplets = [];
352
+ this.history = [];
353
+ }
354
+ }
355
+ exports.ContrastiveTrainer = ContrastiveTrainer;
356
+ /**
357
+ * Agent Training Data for Claude Code Router
358
+ */
359
+ exports.AGENT_TRAINING_DATA = {
360
+ coder: {
361
+ description: 'Implementation specialist for writing clean, efficient code. Handles coding tasks, feature implementation, and code generation.',
362
+ keywords: ['implement', 'code', 'write', 'build', 'create', 'develop', 'function', 'class', 'component', 'feature'],
363
+ examples: [
364
+ 'Implement a binary search function',
365
+ 'Write a React component for user registration',
366
+ 'Create a REST API endpoint for user authentication',
367
+ 'Build a caching layer for the database queries',
368
+ ],
369
+ confusing_with: ['refactorer', 'debugger'],
370
+ },
371
+ tester: {
372
+ description: 'Testing specialist for writing and maintaining tests. Creates unit tests, integration tests, and ensures code quality through testing.',
373
+ keywords: ['test', 'unit test', 'integration test', 'coverage', 'mock', 'assertion', 'spec', 'jest', 'pytest'],
374
+ examples: [
375
+ 'Write unit tests for the authentication module',
376
+ 'Add integration tests for the payment gateway',
377
+ 'Create test coverage for the user service',
378
+ 'Write e2e tests for the checkout flow',
379
+ ],
380
+ confusing_with: ['reviewer'],
381
+ },
382
+ reviewer: {
383
+ description: 'Code review specialist for analyzing code quality, identifying issues, and suggesting improvements.',
384
+ keywords: ['review', 'analyze', 'check', 'inspect', 'audit', 'evaluate', 'assess', 'critique'],
385
+ examples: [
386
+ 'Review the pull request for code quality',
387
+ 'Check the code for potential security vulnerabilities',
388
+ 'Analyze the implementation for best practices',
389
+ 'Evaluate the architecture decisions in this PR',
390
+ ],
391
+ confusing_with: ['tester', 'security-architect'],
392
+ },
393
+ researcher: {
394
+ description: 'Research specialist for investigating technologies, gathering information, and analyzing options.',
395
+ keywords: ['research', 'investigate', 'explore', 'analyze', 'study', 'compare', 'evaluate', 'learn'],
396
+ examples: [
397
+ 'Research best practices for React state management',
398
+ 'Investigate the performance issues in the dashboard',
399
+ 'Compare different authentication strategies',
400
+ 'Study the codebase architecture for the new feature',
401
+ ],
402
+ confusing_with: ['planner'],
403
+ },
404
+ architect: {
405
+ description: 'System architect for designing software architecture, making technical decisions, and planning system structure.',
406
+ keywords: ['design', 'architect', 'structure', 'plan', 'schema', 'model', 'pattern', 'system'],
407
+ examples: [
408
+ 'Design the database schema for user profiles',
409
+ 'Plan the architecture for real-time notifications',
410
+ 'Create a system design for the microservices migration',
411
+ 'Design the API structure for the new product catalog',
412
+ ],
413
+ confusing_with: ['planner'],
414
+ },
415
+ debugger: {
416
+ description: 'Debugging specialist for finding and fixing bugs, analyzing errors, and troubleshooting issues.',
417
+ keywords: ['debug', 'fix', 'bug', 'error', 'issue', 'crash', 'exception', 'troubleshoot'],
418
+ examples: [
419
+ 'Fix the null pointer exception in the login handler',
420
+ 'Debug the memory leak in the WebSocket handler',
421
+ 'Troubleshoot the race condition in the payment processor',
422
+ 'Find the root cause of the intermittent test failures',
423
+ ],
424
+ confusing_with: ['coder'],
425
+ },
426
+ 'security-architect': {
427
+ description: 'Security specialist for auditing code security, identifying vulnerabilities, and implementing security measures.',
428
+ keywords: ['security', 'vulnerability', 'xss', 'sql injection', 'auth', 'encryption', 'audit', 'penetration'],
429
+ examples: [
430
+ 'Audit the API endpoints for XSS vulnerabilities',
431
+ 'Review the authentication flow for security issues',
432
+ 'Implement input validation for the user forms',
433
+ 'Check for SQL injection vulnerabilities in the search',
434
+ ],
435
+ confusing_with: ['reviewer'],
436
+ },
437
+ documenter: {
438
+ description: 'Documentation specialist for writing technical documentation, comments, and API docs.',
439
+ keywords: ['document', 'comment', 'jsdoc', 'readme', 'docs', 'explain', 'describe', 'annotate'],
440
+ examples: [
441
+ 'Write JSDoc comments for the utility functions',
442
+ 'Create README documentation for the new module',
443
+ 'Document the API endpoints with examples',
444
+ 'Add inline comments explaining the algorithm',
445
+ ],
446
+ confusing_with: ['api-docs'],
447
+ },
448
+ refactorer: {
449
+ description: 'Refactoring specialist for improving code structure, cleaning up technical debt, and modernizing codebases.',
450
+ keywords: ['refactor', 'clean', 'restructure', 'modernize', 'improve', 'simplify', 'extract', 'rename'],
451
+ examples: [
452
+ 'Refactor the payment module to use async/await',
453
+ 'Clean up the legacy authentication code',
454
+ 'Extract common logic into a shared utility',
455
+ 'Simplify the complex conditional logic in checkout',
456
+ ],
457
+ confusing_with: ['coder'],
458
+ },
459
+ optimizer: {
460
+ description: 'Performance optimization specialist for improving speed, reducing memory usage, and optimizing queries.',
461
+ keywords: ['optimize', 'performance', 'speed', 'memory', 'cache', 'index', 'query', 'latency'],
462
+ examples: [
463
+ 'Optimize the database queries for the dashboard',
464
+ 'Improve the page load time for the homepage',
465
+ 'Add caching to reduce API response times',
466
+ 'Reduce memory usage in the image processing pipeline',
467
+ ],
468
+ confusing_with: ['researcher'],
469
+ },
470
+ devops: {
471
+ description: 'DevOps specialist for CI/CD pipelines, deployment automation, and infrastructure management.',
472
+ keywords: ['deploy', 'ci/cd', 'pipeline', 'docker', 'kubernetes', 'terraform', 'aws', 'infrastructure'],
473
+ examples: [
474
+ 'Set up the CI/CD pipeline for the microservices',
475
+ 'Configure Docker containers for the application',
476
+ 'Deploy the application to the staging environment',
477
+ 'Create Terraform scripts for the AWS infrastructure',
478
+ ],
479
+ confusing_with: [],
480
+ },
481
+ 'api-docs': {
482
+ description: 'API documentation specialist for creating OpenAPI specs, Swagger documentation, and API references.',
483
+ keywords: ['openapi', 'swagger', 'api docs', 'endpoint', 'specification', 'schema', 'rest'],
484
+ examples: [
485
+ 'Generate OpenAPI documentation for the REST API',
486
+ 'Create Swagger specs for the user endpoints',
487
+ 'Document the API authentication requirements',
488
+ 'Update the API reference with new endpoints',
489
+ ],
490
+ confusing_with: ['documenter'],
491
+ },
492
+ planner: {
493
+ description: 'Project planning specialist for creating task plans, sprint planning, and roadmap development.',
494
+ keywords: ['plan', 'roadmap', 'sprint', 'milestone', 'timeline', 'estimate', 'breakdown', 'prioritize'],
495
+ examples: [
496
+ 'Create a sprint plan for the next two weeks',
497
+ 'Break down the feature into smaller tasks',
498
+ 'Estimate the effort for the migration project',
499
+ 'Prioritize the bug fixes for the release',
500
+ ],
501
+ confusing_with: ['architect', 'researcher'],
502
+ },
503
+ };
504
+ /**
505
+ * Generate training dataset from agent data
506
+ */
507
+ function generateTrainingDataset() {
508
+ const examples = [];
509
+ for (const [agent, data] of Object.entries(exports.AGENT_TRAINING_DATA)) {
510
+ // Add direct examples
511
+ for (const example of data.examples) {
512
+ examples.push({
513
+ task: example,
514
+ agent,
515
+ complexity: 'medium',
516
+ });
517
+ }
518
+ // Generate variations with keywords
519
+ for (const keyword of data.keywords) {
520
+ examples.push({
521
+ task: `${keyword} a solution for the authentication system`,
522
+ agent,
523
+ complexity: 'low',
524
+ });
525
+ }
526
+ // Add confusing pairs for hard negatives
527
+ if (data.confusing_with) {
528
+ for (const confusingAgent of data.confusing_with) {
529
+ for (const example of data.examples.slice(0, 2)) {
530
+ examples.push({
531
+ task: example,
532
+ agent,
533
+ complexity: 'hard',
534
+ confusing_with: confusingAgent,
535
+ });
536
+ }
537
+ }
538
+ }
539
+ }
540
+ return examples;
541
+ }
542
+ /**
543
+ * Generate contrastive pairs for training
544
+ */
545
+ function generateContrastivePairs() {
546
+ const pairs = [];
547
+ const agents = Object.keys(exports.AGENT_TRAINING_DATA);
548
+ for (const [agent, data] of Object.entries(exports.AGENT_TRAINING_DATA)) {
549
+ for (const example of data.examples) {
550
+ // Hard negatives from confusing agents
551
+ if (data.confusing_with) {
552
+ for (const negAgent of data.confusing_with) {
553
+ pairs.push({
554
+ anchor: example,
555
+ positive: agent,
556
+ negative: negAgent,
557
+ isHard: true,
558
+ });
559
+ }
560
+ }
561
+ // Random negatives
562
+ const randomNegs = agents.filter(a => a !== agent).slice(0, 2);
563
+ for (const negAgent of randomNegs) {
564
+ pairs.push({
565
+ anchor: example,
566
+ positive: agent,
567
+ negative: negAgent,
568
+ isHard: false,
569
+ });
570
+ }
571
+ }
572
+ }
573
+ return pairs;
574
+ }
575
+ /**
576
+ * Get dataset statistics
577
+ */
578
+ function getDatasetStats() {
579
+ const examples = generateTrainingDataset();
580
+ const pairs = generateContrastivePairs();
581
+ const agents = Object.keys(exports.AGENT_TRAINING_DATA);
582
+ return {
583
+ totalExamples: examples.length,
584
+ contrastivePairs: pairs.length,
585
+ agentTypes: agents.length,
586
+ agents,
587
+ };
588
+ }
589
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udHJhc3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29udHJhc3RpdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyQkc7OztBQTRHSCw0Q0FTQztBQU1ELGtDQVNDO0FBS0Qsa0NBaUJDO0FBS0QsMENBb0JDO0FBbWVELDBEQXNDQztBQUtELDREQXFDQztBQUtELDBDQVdDO0FBcHZCRCwyQkFBMEQ7QUFDMUQsK0JBQTRCO0FBeUY1Qjs7R0FFRztBQUNILE1BQU0sMEJBQTBCLEdBQWdDO0lBQzlELE1BQU0sRUFBRSxFQUFFO0lBQ1YsU0FBUyxFQUFFLEVBQUU7SUFDYixZQUFZLEVBQUUsTUFBTTtJQUNwQixNQUFNLEVBQUUsR0FBRztJQUNYLFdBQVcsRUFBRSxJQUFJO0lBQ2pCLGlCQUFpQixFQUFFLEdBQUc7SUFDdEIsVUFBVSxFQUFFLG1CQUFtQjtDQUNoQyxDQUFDO0FBRUY7O0dBRUc7QUFDSCxTQUFnQixnQkFBZ0IsQ0FBQyxDQUFZLEVBQUUsQ0FBWTtJQUN6RCxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDLE1BQU07UUFBRSxPQUFPLENBQUMsQ0FBQztJQUNoRCxJQUFJLEdBQUcsR0FBRyxDQUFDLEVBQUUsS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQ2xDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDbEMsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbkIsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDckIs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QUssR0FBRyx3QkFBd0IsRUFBRSxDQUFDO0lBQ3pDLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsMkJBQW1CLENBQUMsQ0FBQztJQUVoRCxPQUFPO1FBQ0wsYUFBYSxFQUFFLFFBQVEsQ0FBQyxNQUFNO1FBQzlCLGdCQUFnQixFQUFFLEtBQUssQ0FBQyxNQUFNO1FBQzlCLFVBQVUsRUFBRSxNQUFNLENBQUMsTUFBTTtRQUN6QixNQUFNO0tBQ1AsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvbnRyYXN0aXZlIEZpbmUtdHVuaW5nIGZvciBSdXZMVFJBIENsYXVkZSBDb2RlIFJvdXRlclxuICpcbiAqIFVzZXMgdHJpcGxldCBsb3NzIHRvIGZpbmUtdHVuZSBlbWJlZGRpbmdzOlxuICogLSBBbmNob3I6IHRhc2sgZGVzY3JpcHRpb25cbiAqIC0gUG9zaXRpdmU6IGNvcnJlY3QgYWdlbnQgZGVzY3JpcHRpb25cbiAqIC0gTmVnYXRpdmU6IHdyb25nIGFnZW50IGRlc2NyaXB0aW9uIChoYXJkIG5lZ2F0aXZlKVxuICpcbiAqIEdvYWw6IG1pbmltaXplIGRpc3RhbmNlKGFuY2hvciwgcG9zaXRpdmUpIGFuZCBtYXhpbWl6ZSBkaXN0YW5jZShhbmNob3IsIG5lZ2F0aXZlKVxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBDb250cmFzdGl2ZVRyYWluZXIsIHRyaXBsZXRMb3NzLCBpbmZvTkNFTG9zcyB9IGZyb20gJ0BydXZlY3Rvci9ydXZsbG0nO1xuICpcbiAqIGNvbnN0IHRyYWluZXIgPSBuZXcgQ29udHJhc3RpdmVUcmFpbmVyKHtcbiAqICAgZXBvY2hzOiAxMCxcbiAqICAgYmF0Y2hTaXplOiAxNixcbiAqICAgbWFyZ2luOiAwLjUsXG4gKiB9KTtcbiAqXG4gKiAvLyBBZGQgdHJpcGxldHNcbiAqIHRyYWluZXIuYWRkVHJpcGxldChhbmNob3JFbWIsIHBvc2l0aXZlRW1iLCBuZWdhdGl2ZUVtYiwgdHJ1ZSk7XG4gKlxuICogLy8gVHJhaW4gYW5kIGV4cG9ydFxuICogY29uc3QgcmVzdWx0cyA9IHRyYWluZXIudHJhaW4oKTtcbiAqIHRyYWluZXIuZXhwb3J0VHJhaW5pbmdEYXRhKCcuL291dHB1dCcpO1xuICogYGBgXG4gKi9cblxuaW1wb3J0IHsgd3JpdGVGaWxlU3luYywgbWtkaXJTeW5jLCBleGlzdHNTeW5jIH0gZnJvbSAnZnMnO1xuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgRW1iZWRkaW5nIH0gZnJvbSAnLi90eXBlcyc7XG5cbi8qKlxuICogQ29udHJhc3RpdmUgdHJhaW5pbmcgY29uZmlndXJhdGlvblxuICovXG5leHBvcnQgaW50ZXJmYWNlIENvbnRyYXN0aXZlQ29uZmlnIHtcbiAgLyoqIE51bWJlciBvZiB0cmFpbmluZyBlcG9jaHMgKGRlZmF1bHQ6IDEwKSAqL1xuICBlcG9jaHM/OiBudW1iZXI7XG4gIC8qKiBCYXRjaCBzaXplIChkZWZhdWx0OiAxNikgKi9cbiAgYmF0Y2hTaXplPzogbnVtYmVyO1xuICAvKiogTGVhcm5pbmcgcmF0ZSAoZGVmYXVsdDogMC4wMDAxKSAqL1xuICBsZWFybmluZ1JhdGU/OiBudW1iZXI7XG4gIC8qKiBUcmlwbGV0IGxvc3MgbWFyZ2luIChkZWZhdWx0OiAwLjUpICovXG4gIG1hcmdpbj86IG51bWJlcjtcbiAgLyoqIEluZm9OQ0UgdGVtcGVyYXR1cmUgKGRlZmF1bHQ6IDAuMDcpICovXG4gIHRlbXBlcmF0dXJlPzogbnVtYmVyO1xuICAvKiogUmF0aW8gb2YgaGFyZCBuZWdhdGl2ZXMgKGRlZmF1bHQ6IDAuNykgKi9cbiAgaGFyZE5lZ2F0aXZlUmF0aW8/OiBudW1iZXI7XG4gIC8qKiBPdXRwdXQgZGlyZWN0b3J5IGZvciB0cmFpbmluZyBkYXRhICovXG4gIG91dHB1dFBhdGg/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogVHJhaW5pbmcgdHJpcGxldFxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRyYWluaW5nVHJpcGxldCB7XG4gIC8qKiBBbmNob3IgZW1iZWRkaW5nICh0YXNrKSAqL1xuICBhbmNob3I6IHN0cmluZztcbiAgYW5jaG9yRW1iOiBFbWJlZGRpbmc7XG4gIC8qKiBQb3NpdGl2ZSBleGFtcGxlIChjb3JyZWN0IGFnZW50KSAqL1xuICBwb3NpdGl2ZTogc3RyaW5nO1xuICBwb3NpdGl2ZUVtYjogRW1iZWRkaW5nO1xuICAvKiogTmVnYXRpdmUgZXhhbXBsZSAod3JvbmcgYWdlbnQpICovXG4gIG5lZ2F0aXZlOiBzdHJpbmc7XG4gIG5lZ2F0aXZlRW1iOiBFbWJlZGRpbmc7XG4gIC8qKiBXaGV0aGVyIHRoaXMgaXMgYSBoYXJkIG5lZ2F0aXZlICovXG4gIGlzSGFyZDogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBUcmFpbmluZyBoaXN0b3J5IGVudHJ5XG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVHJhaW5pbmdIaXN0b3J5RW50cnkge1xuICBlcG9jaDogbnVtYmVyO1xuICBsb3NzOiBudW1iZXI7XG59XG5cbi8qKlxuICogQ29udHJhc3RpdmUgdHJhaW5pbmcgcmVzdWx0c1xuICovXG5leHBvcnQgaW50ZXJmYWNlIENvbnRyYXN0aXZlVHJhaW5pbmdSZXN1bHQge1xuICAvKiogVG90YWwgdHJpcGxldHMgdHJhaW5lZCBvbiAqL1xuICB0cmlwbGV0Q291bnQ6IG51bWJlcjtcbiAgLyoqIEZpbmFsIGxvc3MgdmFsdWUgKi9cbiAgZmluYWxMb3NzOiBudW1iZXI7XG4gIC8qKiBJbml0aWFsIGxvc3MgdmFsdWUgKi9cbiAgaW5pdGlhbExvc3M6IG51bWJlcjtcbiAgLyoqIEltcHJvdmVtZW50IHBlcmNlbnRhZ2UgKi9cbiAgaW1wcm92ZW1lbnQ6IG51bWJlcjtcbiAgLyoqIFRyYWluaW5nIGhpc3RvcnkgKi9cbiAgaGlzdG9yeTogVHJhaW5pbmdIaXN0b3J5RW50cnlbXTtcbiAgLyoqIER1cmF0aW9uIGluIG1zICovXG4gIGR1cmF0aW9uTXM6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBMb1JBIGNvbmZpZ3VyYXRpb24gZm9yIGZpbmUtdHVuaW5nXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTG9SQUV4cG9ydENvbmZpZyB7XG4gIG1vZGVsX3R5cGU6IHN0cmluZztcbiAgYmFzZV9tb2RlbDogc3RyaW5nO1xuICBvdXRwdXRfZGlyOiBzdHJpbmc7XG4gIGxvcmFfcjogbnVtYmVyO1xuICBsb3JhX2FscGhhOiBudW1iZXI7XG4gIGxvcmFfZHJvcG91dDogbnVtYmVyO1xuICB0YXJnZXRfbW9kdWxlczogc3RyaW5nW107XG4gIGxlYXJuaW5nX3JhdGU6IG51bWJlcjtcbiAgbnVtX3RyYWluX2Vwb2NoczogbnVtYmVyO1xuICBwZXJfZGV2aWNlX3RyYWluX2JhdGNoX3NpemU6IG51bWJlcjtcbiAgZ3JhZGllbnRfYWNjdW11bGF0aW9uX3N0ZXBzOiBudW1iZXI7XG4gIHdhcm11cF9yYXRpbzogbnVtYmVyO1xuICBsb3NzX3R5cGU6IHN0cmluZztcbiAgbWFyZ2luOiBudW1iZXI7XG4gIHRlbXBlcmF0dXJlOiBudW1iZXI7XG4gIHRyYWluX2RhdGE6IHN0cmluZztcbiAgZXZhbF9kYXRhOiBzdHJpbmc7XG59XG5cbi8qKlxuICogRGVmYXVsdCBjb250cmFzdGl2ZSBjb25maWdcbiAqL1xuY29uc3QgREVGQVVMVF9DT05UUkFTVElWRV9DT05GSUc6IFJlcXVpcmVkPENvbnRyYXN0aXZlQ29uZmlnPiA9IHtcbiAgZXBvY2hzOiAxMCxcbiAgYmF0Y2hTaXplOiAxNixcbiAgbGVhcm5pbmdSYXRlOiAwLjAwMDEsXG4gIG1hcmdpbjogMC41LFxuICB0ZW1wZXJhdHVyZTogMC4wNyxcbiAgaGFyZE5lZ2F0aXZlUmF0aW86IDAuNyxcbiAgb3V0cHV0UGF0aDogJy4vdHJhaW5pbmctb3V0cHV0Jyxcbn07XG5cbi8qKlxuICogQ29tcHV0ZSBjb3NpbmUgc2ltaWxhcml0eSBiZXR3ZWVuIHR3byBlbWJlZGRpbmdzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb3NpbmVTaW1pbGFyaXR5KGE6IEVtYmVkZGluZywgYjogRW1iZWRkaW5nKTogbnVtYmVyIHtcbiAgaWYgKCFhIHx8ICFiIHx8IGEubGVuZ3RoICE9PSBiLmxlbmd0aCkgcmV0dXJuIDA7XG4gIGxldCBkb3QgPSAwLCBub3JtQSA9IDAsIG5vcm1CID0gMDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBhLmxlbmd0aDsgaSsrKSB7XG4gICAgZG90ICs9IGFbaV0gKiBiW2ldO1xuICAgIG5vcm1BICs9IGFbaV0gKiBhW2ldO1xuICAgIG5vcm1CICs9IGJbaV0gKiBiW2ldO1xuICB9XG4gIHJldHVybiBkb3QgLyAoTWF0aC5zcXJ0KG5vcm1BKSAqIE1hdGguc3FydChub3JtQikgfHwgMSk7XG59XG5cbi8qKlxuICogQ29tcHV0ZSB0cmlwbGV0IGxvc3NcbiAqIEwgPSBtYXgoMCwgbWFyZ2luICsgZChhbmNob3IsIHBvc2l0aXZlKSAtIGQoYW5jaG9yLCBuZWdhdGl2ZSkpXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0cmlwbGV0TG9zcyhcbiAgYW5jaG9yRW1iOiBFbWJlZGRpbmcsXG4gIHBvc2l0aXZlRW1iOiBFbWJlZGRpbmcsXG4gIG5lZ2F0aXZlRW1iOiBFbWJlZGRpbmcsXG4gIG1hcmdpbjogbnVtYmVyID0gMC41XG4pOiBudW1iZXIge1xuICBjb25zdCBwb3NEaXN0ID0gMSAtIGNvc2luZVNpbWlsYXJpdHkoYW5jaG9yRW1iLCBwb3NpdGl2ZUVtYik7XG4gIGNvbnN0IG5lZ0Rpc3QgPSAxIC0gY29zaW5lU2ltaWxhcml0eShhbmNob3JFbWIsIG5lZ2F0aXZlRW1iKTtcbiAgcmV0dXJuIE1hdGgubWF4KDAsIG1hcmdpbiArIHBvc0Rpc3QgLSBuZWdEaXN0KTtcbn1cblxuLyoqXG4gKiBDb21wdXRlIEluZm9OQ0UgbG9zcyAoY29udHJhc3RpdmUpXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbmZvTkNFTG9zcyhcbiAgYW5jaG9yRW1iOiBFbWJlZGRpbmcsXG4gIHBvc2l0aXZlRW1iOiBFbWJlZGRpbmcsXG4gIG5lZ2F0aXZlRW1iczogRW1iZWRkaW5nW10sXG4gIHRlbXBlcmF0dXJlOiBudW1iZXIgPSAwLjA3XG4pOiBudW1iZXIge1xuICBjb25zdCBwb3NTaW0gPSBjb3NpbmVTaW1pbGFyaXR5KGFuY2hvckVtYiwgcG9zaXRpdmVFbWIpIC8gdGVtcGVyYXR1cmU7XG4gIGNvbnN0IG5lZ1NpbXMgPSBuZWdhdGl2ZUVtYnMubWFwKG5lZyA9PiBjb3NpbmVTaW1pbGFyaXR5KGFuY2hvckVtYiwgbmVnKSAvIHRlbXBlcmF0dXJlKTtcblxuICAvLyBTb2Z0bWF4IGRlbm9taW5hdG9yXG4gIGNvbnN0IG1heFNpbSA9IE1hdGgubWF4KHBvc1NpbSwgLi4ubmVnU2ltcyk7XG4gIGNvbnN0IGV4cFBvcyA9IE1hdGguZXhwKHBvc1NpbSAtIG1heFNpbSk7XG4gIGNvbnN0IGV4cE5lZ3MgPSBuZWdTaW1zLm1hcChzaW0gPT4gTWF0aC5leHAoc2ltIC0gbWF4U2ltKSk7XG4gIGNvbnN0IGRlbm9taW5hdG9yID0gZXhwUG9zICsgZXhwTmVncy5yZWR1Y2UoKGEsIGIpID0+IGEgKyBiLCAwKTtcblxuICAvLyBDcm9zcy1lbnRyb3B5IGxvc3NcbiAgcmV0dXJuIC1NYXRoLmxvZyhleHBQb3MgLyBkZW5vbWluYXRvcik7XG59XG5cbi8qKlxuICogQ29tcHV0ZSBncmFkaWVudCBmb3IgZW1iZWRkaW5nIHVwZGF0ZSAoc2ltcGxpZmllZClcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVHcmFkaWVudChcbiAgYW5jaG9yRW1iOiBFbWJlZGRpbmcsXG4gIHBvc2l0aXZlRW1iOiBFbWJlZGRpbmcsXG4gIG5lZ2F0aXZlRW1iOiBFbWJlZGRpbmcsXG4gIGxyOiBudW1iZXIgPSAwLjAwMDFcbik6IEVtYmVkZGluZyB7XG4gIGNvbnN0IGRpbSA9IGFuY2hvckVtYi5sZW5ndGg7XG4gIGNvbnN0IGdyYWRpZW50OiBudW1iZXJbXSA9IG5ldyBBcnJheShkaW0pLmZpbGwoMCk7XG5cbiAgLy8gUHVsbCBhbmNob3IgdG93YXJkcyBwb3NpdGl2ZVxuICBmb3IgKGxldCBpID0gMDsgaSA8IGRpbTsgaSsrKSB7XG4gICAgZ3JhZGllbnRbaV0gKz0gbHIgKiAocG9zaXRpdmVFbWJbaV0gLSBhbmNob3JFbWJbaV0pO1xuICB9XG5cbiAgLy8gUHVzaCBhbmNob3IgYXdheSBmcm9tIG5lZ2F0aXZlXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZGltOyBpKyspIHtcbiAgICBncmFkaWVudFtpXSAtPSBsciAqIDAuNSAqIChuZWdhdGl2ZUVtYltpXSAtIGFuY2hvckVtYltpXSk7XG4gIH1cblxuICByZXR1cm4gZ3JhZGllbnQ7XG59XG5cbi8qKlxuICogQ29udHJhc3RpdmUgVHJhaW5lciBmb3IgUnV2TFRSQSBtb2RlbHNcbiAqXG4gKiBJbXBsZW1lbnRzIHRyaXBsZXQgbG9zcyBhbmQgSW5mb05DRSBsb3NzIGZvciBlbWJlZGRpbmcgZmluZS10dW5pbmcuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb250cmFzdGl2ZVRyYWluZXIge1xuICBwcml2YXRlIGNvbmZpZzogUmVxdWlyZWQ8Q29udHJhc3RpdmVDb25maWc+O1xuICBwcml2YXRlIHRyaXBsZXRzOiBUcmFpbmluZ1RyaXBsZXRbXSA9IFtdO1xuICBwcml2YXRlIGhpc3Rvcnk6IFRyYWluaW5nSGlzdG9yeUVudHJ5W10gPSBbXTtcbiAgcHJpdmF0ZSBhZ2VudEVtYmVkZGluZ3M6IE1hcDxzdHJpbmcsIEVtYmVkZGluZz4gPSBuZXcgTWFwKCk7XG5cbiAgY29uc3RydWN0b3IoY29uZmlnPzogQ29udHJhc3RpdmVDb25maWcpIHtcbiAgICB0aGlzLmNvbmZpZyA9IHsgLi4uREVGQVVMVF9DT05UUkFTVElWRV9DT05GSUcsIC4uLmNvbmZpZyB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBhIHRyYWluaW5nIHRyaXBsZXRcbiAgICovXG4gIGFkZFRyaXBsZXQoXG4gICAgYW5jaG9yOiBzdHJpbmcsXG4gICAgYW5jaG9yRW1iOiBFbWJlZGRpbmcsXG4gICAgcG9zaXRpdmU6IHN0cmluZyxcbiAgICBwb3NpdGl2ZUVtYjogRW1iZWRkaW5nLFxuICAgIG5lZ2F0aXZlOiBzdHJpbmcsXG4gICAgbmVnYXRpdmVFbWI6IEVtYmVkZGluZyxcbiAgICBpc0hhcmQ6IGJvb2xlYW4gPSBmYWxzZVxuICApOiB2b2lkIHtcbiAgICB0aGlzLnRyaXBsZXRzLnB1c2goe1xuICAgICAgYW5jaG9yLFxuICAgICAgYW5jaG9yRW1iLFxuICAgICAgcG9zaXRpdmUsXG4gICAgICBwb3NpdGl2ZUVtYixcbiAgICAgIG5lZ2F0aXZlLFxuICAgICAgbmVnYXRpdmVFbWIsXG4gICAgICBpc0hhcmQsXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQWRkIGFnZW50IGVtYmVkZGluZyBmb3IgcmVmZXJlbmNlXG4gICAqL1xuICBhZGRBZ2VudEVtYmVkZGluZyhhZ2VudE5hbWU6IHN0cmluZywgZW1iZWRkaW5nOiBFbWJlZGRpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmFnZW50RW1iZWRkaW5ncy5zZXQoYWdlbnROYW1lLCBlbWJlZGRpbmcpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbGwgYWdlbnQgZW1iZWRkaW5nc1xuICAgKi9cbiAgZ2V0QWdlbnRFbWJlZGRpbmdzKCk6IE1hcDxzdHJpbmcsIEVtYmVkZGluZz4ge1xuICAgIHJldHVybiB0aGlzLmFnZW50RW1iZWRkaW5ncztcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdHJpcGxldCBjb3VudFxuICAgKi9cbiAgZ2V0VHJpcGxldENvdW50KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMudHJpcGxldHMubGVuZ3RoO1xuICB9XG5cbiAgLyoqXG4gICAqIFNpbXVsYXRlIHRyYWluaW5nIChjb21wdXRlIGxvc3NlcyB3aXRob3V0IGFjdHVhbCBiYWNrcHJvcClcbiAgICogSW4gYSBmdWxsIGltcGxlbWVudGF0aW9uLCB0aGlzIHdvdWxkIHVzZSBwcm9wZXIgZ3JhZGllbnQgZGVzY2VudFxuICAgKi9cbiAgdHJhaW4oKTogQ29udHJhc3RpdmVUcmFpbmluZ1Jlc3VsdCB7XG4gICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCB7IGVwb2NocywgYmF0Y2hTaXplLCBtYXJnaW4gfSA9IHRoaXMuY29uZmlnO1xuXG4gICAgaWYgKHRoaXMudHJpcGxldHMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB0cmlwbGV0Q291bnQ6IDAsXG4gICAgICAgIGZpbmFsTG9zczogMCxcbiAgICAgICAgaW5pdGlhbExvc3M6IDAsXG4gICAgICAgIGltcHJvdmVtZW50OiAwLFxuICAgICAgICBoaXN0b3J5OiBbXSxcbiAgICAgICAgZHVyYXRpb25NczogMCxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgZm9yIChsZXQgZXBvY2ggPSAwOyBlcG9jaCA8IGVwb2NoczsgZXBvY2grKykge1xuICAgICAgbGV0IGVwb2NoTG9zcyA9IDA7XG4gICAgICBsZXQgYmF0Y2hDb3VudCA9IDA7XG5cbiAgICAgIC8vIFNodWZmbGUgdHJpcGxldHNcbiAgICAgIGNvbnN0IHNodWZmbGVkID0gWy4uLnRoaXMudHJpcGxldHNdLnNvcnQoKCkgPT4gTWF0aC5yYW5kb20oKSAtIDAuNSk7XG5cbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc2h1ZmZsZWQubGVuZ3RoOyBpICs9IGJhdGNoU2l6ZSkge1xuICAgICAgICBjb25zdCBiYXRjaCA9IHNodWZmbGVkLnNsaWNlKGksIGkgKyBiYXRjaFNpemUpO1xuICAgICAgICBsZXQgYmF0Y2hMb3NzID0gMDtcblxuICAgICAgICBmb3IgKGNvbnN0IHRyaXBsZXQgb2YgYmF0Y2gpIHtcbiAgICAgICAgICBjb25zdCBsb3NzID0gdHJpcGxldExvc3MoXG4gICAgICAgICAgICB0cmlwbGV0LmFuY2hvckVtYixcbiAgICAgICAgICAgIHRyaXBsZXQucG9zaXRpdmVFbWIsXG4gICAgICAgICAgICB0cmlwbGV0Lm5lZ2F0aXZlRW1iLFxuICAgICAgICAgICAgbWFyZ2luXG4gICAgICAgICAgKTtcbiAgICAgICAgICBiYXRjaExvc3MgKz0gbG9zcztcbiAgICAgICAgfVxuXG4gICAgICAgIGVwb2NoTG9zcyArPSBiYXRjaExvc3MgLyBiYXRjaC5sZW5ndGg7XG4gICAgICAgIGJhdGNoQ291bnQrKztcbiAgICAgIH1cblxuICAgICAgY29uc3QgYXZnTG9zcyA9IGVwb2NoTG9zcyAvIGJhdGNoQ291bnQ7XG4gICAgICB0aGlzLmhpc3RvcnkucHVzaCh7IGVwb2NoOiBlcG9jaCArIDEsIGxvc3M6IGF2Z0xvc3MgfSk7XG4gICAgfVxuXG4gICAgY29uc3QgaW5pdGlhbExvc3MgPSB0aGlzLmhpc3RvcnlbMF0/Lmxvc3MgfHwgMDtcbiAgICBjb25zdCBmaW5hbExvc3MgPSB0aGlzLmhpc3RvcnlbdGhpcy5oaXN0b3J5Lmxlbmd0aCAtIDFdPy5sb3NzIHx8IDA7XG4gICAgY29uc3QgaW1wcm92ZW1lbnQgPSBpbml0aWFsTG9zcyA+IDAgPyAoMSAtIGZpbmFsTG9zcyAvIGluaXRpYWxMb3NzKSAqIDEwMCA6IDA7XG5cbiAgICByZXR1cm4ge1xuICAgICAgdHJpcGxldENvdW50OiB0aGlzLnRyaXBsZXRzLmxlbmd0aCxcbiAgICAgIGZpbmFsTG9zcyxcbiAgICAgIGluaXRpYWxMb3NzLFxuICAgICAgaW1wcm92ZW1lbnQsXG4gICAgICBoaXN0b3J5OiB0aGlzLmhpc3RvcnksXG4gICAgICBkdXJhdGlvbk1zOiBEYXRlLm5vdygpIC0gc3RhcnRUaW1lLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogRXhwb3J0IHRyYWluaW5nIGRhdGEgZm9yIGV4dGVybmFsIGZpbmUtdHVuaW5nIHRvb2xzXG4gICAqL1xuICBleHBvcnRUcmFpbmluZ0RhdGEob3V0cHV0UGF0aD86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3Qgb3V0RGlyID0gb3V0cHV0UGF0aCB8fCB0aGlzLmNvbmZpZy5vdXRwdXRQYXRoO1xuXG4gICAgaWYgKCFleGlzdHNTeW5jKG91dERpcikpIHtcbiAgICAgIG1rZGlyU3luYyhvdXREaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIH1cblxuICAgIC8vIEpTT05MIGZvcm1hdCBmb3IgZmluZS10dW5pbmdcbiAgICBjb25zdCBqc29ubERhdGEgPSB0aGlzLnRyaXBsZXRzLm1hcCh0ID0+ICh7XG4gICAgICBhbmNob3I6IHQuYW5jaG9yLFxuICAgICAgcG9zaXRpdmU6IHQucG9zaXRpdmUsXG4gICAgICBuZWdhdGl2ZTogdC5uZWdhdGl2ZSxcbiAgICAgIGlzSGFyZDogdC5pc0hhcmQsXG4gICAgfSkpO1xuXG4gICAgLy8gQ1NWIGZvcm1hdCBmb3IgYW5hbHlzaXNcbiAgICBjb25zdCBjc3ZEYXRhID0gW1xuICAgICAgJ2FuY2hvcixwb3NpdGl2ZSxuZWdhdGl2ZSxpc19oYXJkJyxcbiAgICAgIC4uLnRoaXMudHJpcGxldHMubWFwKHQgPT5cbiAgICAgICAgYFwiJHt0LmFuY2hvci5yZXBsYWNlKC9cIi9nLCAnXCJcIicpfVwiLCR7dC5wb3NpdGl2ZX0sJHt0Lm5lZ2F0aXZlfSwke3QuaXNIYXJkfWBcbiAgICAgICksXG4gICAgXS5qb2luKCdcXG4nKTtcblxuICAgIC8vIEVtYmVkZGluZyBtYXRyaXggZm9yIGRpcmVjdCB0cmFpbmluZ1xuICAgIGNvbnN0IGVtYmVkZGluZ0RhdGEgPSB7XG4gICAgICBhbmNob3JzOiB0aGlzLnRyaXBsZXRzLm1hcCh0ID0+IHQuYW5jaG9yRW1iKSxcbiAgICAgIHBvc2l0aXZlczogdGhpcy50cmlwbGV0cy5tYXAodCA9PiB0LnBvc2l0aXZlRW1iKSxcbiAgICAgIG5lZ2F0aXZlczogdGhpcy50cmlwbGV0cy5tYXAodCA9PiB0Lm5lZ2F0aXZlRW1iKSxcbiAgICAgIGxhYmVsczogdGhpcy50cmlwbGV0cy5tYXAodCA9PiB0LnBvc2l0aXZlKSxcbiAgICB9O1xuXG4gICAgd3JpdGVGaWxlU3luYyhqb2luKG91dERpciwgJ3RyaXBsZXRzLmpzb25sJyksIGpzb25sRGF0YS5tYXAoaXRlbSA9PiBKU09OLnN0cmluZ2lmeShpdGVtKSkuam9pbignXFxuJykpO1xuICAgIHdyaXRlRmlsZVN5bmMoam9pbihvdXREaXIsICd0cmlwbGV0cy5jc3YnKSwgY3N2RGF0YSk7XG4gICAgd3JpdGVGaWxlU3luYyhqb2luKG91dERpciwgJ2VtYmVkZGluZ3MuanNvbicpLCBKU09OLnN0cmluZ2lmeShlbWJlZGRpbmdEYXRhLCBudWxsLCAyKSk7XG5cbiAgICByZXR1cm4gb3V0RGlyO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlIExvUkEgYWRhcHRlciBjb25maWd1cmF0aW9uXG4gICAqL1xuICBnZW5lcmF0ZUxvUkFDb25maWcob3V0cHV0UGF0aD86IHN0cmluZyk6IExvUkFFeHBvcnRDb25maWcge1xuICAgIGNvbnN0IG91dERpciA9IG91dHB1dFBhdGggfHwgdGhpcy5jb25maWcub3V0cHV0UGF0aDtcblxuICAgIGNvbnN0IGxvcmFDb25maWc6IExvUkFFeHBvcnRDb25maWcgPSB7XG4gICAgICBtb2RlbF90eXBlOiAncXdlbjInLFxuICAgICAgYmFzZV9tb2RlbDogJ1F3ZW4vUXdlbjIuNS0wLjVCJyxcbiAgICAgIG91dHB1dF9kaXI6IG91dERpcixcbiAgICAgIGxvcmFfcjogOCxcbiAgICAgIGxvcmFfYWxwaGE6IDE2LFxuICAgICAgbG9yYV9kcm9wb3V0OiAwLjA1LFxuICAgICAgdGFyZ2V0X21vZHVsZXM6IFsncV9wcm9qJywgJ3ZfcHJvaicsICdrX3Byb2onLCAnb19wcm9qJ10sXG4gICAgICBsZWFybmluZ19yYXRlOiB0aGlzLmNvbmZpZy5sZWFybmluZ1JhdGUsXG4gICAgICBudW1fdHJhaW5fZXBvY2hzOiB0aGlzLmNvbmZpZy5lcG9jaHMsXG4gICAgICBwZXJfZGV2aWNlX3RyYWluX2JhdGNoX3NpemU6IHRoaXMuY29uZmlnLmJhdGNoU2l6ZSxcbiAgICAgIGdyYWRpZW50X2FjY3VtdWxhdGlvbl9zdGVwczogNCxcbiAgICAgIHdhcm11cF9yYXRpbzogMC4xLFxuICAgICAgbG9zc190eXBlOiAndHJpcGxldCcsXG4gICAgICBtYXJnaW46IHRoaXMuY29uZmlnLm1hcmdpbixcbiAgICAgIHRlbXBlcmF0dXJlOiB0aGlzLmNvbmZpZy50ZW1wZXJhdHVyZSxcbiAgICAgIHRyYWluX2RhdGE6IGpvaW4ob3V0RGlyLCAndHJpcGxldHMuanNvbmwnKSxcbiAgICAgIGV2YWxfZGF0YTogam9pbihvdXREaXIsICdldmFsLmpzb25sJyksXG4gICAgfTtcblxuICAgIGlmICghZXhpc3RzU3luYyhvdXREaXIpKSB7XG4gICAgICBta2RpclN5bmMob3V0RGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICB9XG5cbiAgICB3cml0ZUZpbGVTeW5jKGpvaW4ob3V0RGlyLCAnbG9yYV9jb25maWcuanNvbicpLCBKU09OLnN0cmluZ2lmeShsb3JhQ29uZmlnLCBudWxsLCAyKSk7XG4gICAgcmV0dXJuIGxvcmFDb25maWc7XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgdHJhaW5pbmcgc2NyaXB0IGZvciBleHRlcm5hbCB0b29sc1xuICAgKi9cbiAgZ2VuZXJhdGVUcmFpbmluZ1NjcmlwdChvdXRwdXRQYXRoPzogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBvdXREaXIgPSBvdXRwdXRQYXRoIHx8IHRoaXMuY29uZmlnLm91dHB1dFBhdGg7XG5cbiAgICBjb25zdCBzY3JpcHQgPSBgIyEvYmluL2Jhc2hcbiMgUnV2TFRSQSBGaW5lLXR1bmluZyBTY3JpcHRcbiMgUHJlcmVxdWlzaXRlczogcGlwIGluc3RhbGwgdHJhbnNmb3JtZXJzIHBlZnQgYWNjZWxlcmF0ZVxuXG5zZXQgLWVcblxuTU9ERUxfUEFUSD1cIiR7b3V0RGlyfVwiXG5CQVNFX01PREVMPVwiUXdlbi9Rd2VuMi41LTAuNUJcIlxuXG5lY2hvIFwiPT09IFJ1dkxUUkEgQ29udHJhc3RpdmUgRmluZS10dW5pbmcgPT09XCJcbmVjaG8gXCJCYXNlIG1vZGVsOiAkQkFTRV9NT0RFTFwiXG5lY2hvIFwiT3V0cHV0OiAkTU9ERUxfUEFUSFwiXG5cbiMgQ2hlY2sgZm9yIHRyYWluaW5nIGRhdGFcbmlmIFsgISAtZiBcIiRNT0RFTF9QQVRIL3RyaXBsZXRzLmpzb25sXCIgXTsgdGhlblxuICBlY2hvIFwiRXJyb3I6IFRyYWluaW5nIGRhdGEgbm90IGZvdW5kIGF0ICRNT0RFTF9QQVRIL3RyaXBsZXRzLmpzb25sXCJcbiAgZXhpdCAxXG5maVxuXG4jIEluc3RhbGwgZGVwZW5kZW5jaWVzIGlmIG5lZWRlZFxucHl0aG9uMyAtYyBcImltcG9ydCB0cmFuc2Zvcm1lcnMsIHBlZnRcIiAyPi9kZXYvbnVsbCB8fCB7XG4gIGVjaG8gXCJJbnN0YWxsaW5nIGRlcGVuZGVuY2llcy4uLlwiXG4gIHBpcCBpbnN0YWxsIHRyYW5zZm9ybWVycyBwZWZ0IGFjY2VsZXJhdGUgc2VudGVuY2VwaWVjZVxufVxuXG4jIEZpbmUtdHVuZSB3aXRoIExvUkFcbnB5dGhvbjMgPDwgJ1BZVEhPTidcbmltcG9ydCBqc29uXG5pbXBvcnQgdG9yY2hcbmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aFxuZnJvbSB0cmFuc2Zvcm1lcnMgaW1wb3J0IEF1dG9Nb2RlbEZvckNhdXNhbExNLCBBdXRvVG9rZW5pemVyXG5mcm9tIHBlZnQgaW1wb3J0IExvcmFDb25maWcsIGdldF9wZWZ0X21vZGVsLCBUYXNrVHlwZVxuXG4jIExvYWQgY29uZmlnXG5jb25maWdfcGF0aCA9IFBhdGgoXCIke291dERpcn0vbG9yYV9jb25maWcuanNvblwiKVxud2l0aCBvcGVuKGNvbmZpZ19wYXRoKSBhcyBmOlxuICAgIGNvbmZpZyA9IGpzb24ubG9hZChmKVxuXG5wcmludChmXCJMb2FkaW5nIGJhc2UgbW9kZWw6IHtjb25maWdbJ2Jhc2VfbW9kZWwnXX1cIilcblxuIyBMb2FkIG1vZGVsIGFuZCB0b2tlbml6ZXJcbnRva2VuaXplciA9IEF1dG9Ub2tlbml6ZXIuZnJvbV9wcmV0cmFpbmVkKGNvbmZpZ1snYmFzZV9tb2RlbCddKVxubW9kZWwgPSBBdXRvTW9kZWxGb3JDYXVzYWxMTS5mcm9tX3ByZXRyYWluZWQoXG4gICAgY29uZmlnWydiYXNlX21vZGVsJ10sXG4gICAgdG9yY2hfZHR5cGU9dG9yY2guZmxvYXQxNixcbiAgICBkZXZpY2VfbWFwPSdhdXRvJ1xuKVxuXG4jIENvbmZpZ3VyZSBMb1JBXG5sb3JhX2NvbmZpZyA9IExvcmFDb25maWcoXG4gICAgcj1jb25maWdbJ2xvcmFfciddLFxuICAgIGxvcmFfYWxwaGE9Y29uZmlnWydsb3JhX2FscGhhJ10sXG4gICAgbG9yYV9kcm9wb3V0PWNvbmZpZ1snbG9yYV9kcm9wb3V0J10sXG4gICAgdGFyZ2V0X21vZHVsZXM9Y29uZmlnWyd0YXJnZXRfbW9kdWxlcyddLFxuICAgIHRhc2tfdHlwZT1UYXNrVHlwZS5DQVVTQUxfTE0sXG4pXG5cbm1vZGVsID0gZ2V0X3BlZnRfbW9kZWwobW9kZWwsIGxvcmFfY29uZmlnKVxubW9kZWwucHJpbnRfdHJhaW5hYmxlX3BhcmFtZXRlcnMoKVxuXG5wcmludChcIk1vZGVsIHJlYWR5IGZvciBmaW5lLXR1bmluZyFcIilcbnByaW50KGZcIlRyYWluaW5nIGRhdGE6IHtjb25maWdbJ3RyYWluX2RhdGEnXX1cIilcbnByaW50KFwiTm90ZTogRnVsbCB0cmFpbmluZyByZXF1aXJlcyBHUFUuIFRoaXMgc2NyaXB0IHZhbGlkYXRlcyB0aGUgc2V0dXAuXCIpXG5QWVRIT05cblxuZWNobyBcIlwiXG5lY2hvIFwiPT09IFNldHVwIENvbXBsZXRlID09PVwiXG5lY2hvIFwiVG8gdHJhaW4gb24gR1BVLCBydW4gdGhlIGZ1bGwgdHJhaW5pbmcgcGlwZWxpbmUuXCJcbmVjaG8gXCJUcmFpbmluZyBkYXRhIGV4cG9ydGVkIHRvOiAkTU9ERUxfUEFUSC90cmlwbGV0cy5qc29ubFwiXG5gO1xuXG4gICAgaWYgKCFleGlzdHNTeW5jKG91dERpcikpIHtcbiAgICAgIG1rZGlyU3luYyhvdXREaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIH1cblxuICAgIGNvbnN0IHNjcmlwdFBhdGggPSBqb2luKG91dERpciwgJ3RyYWluLnNoJyk7XG4gICAgd3JpdGVGaWxlU3luYyhzY3JpcHRQYXRoLCBzY3JpcHQpO1xuXG4gICAgcmV0dXJuIHNjcmlwdFBhdGg7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRyYWluaW5nIGhpc3RvcnlcbiAgICovXG4gIGdldEhpc3RvcnkoKTogVHJhaW5pbmdIaXN0b3J5RW50cnlbXSB7XG4gICAgcmV0dXJuIFsuLi50aGlzLmhpc3RvcnldO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc2V0IHRyYWluZXJcbiAgICovXG4gIHJlc2V0KCk6IHZvaWQge1xuICAgIHRoaXMudHJpcGxldHMgPSBbXTtcbiAgICB0aGlzLmhpc3RvcnkgPSBbXTtcbiAgfVxufVxuXG4vKipcbiAqIEFnZW50IFRyYWluaW5nIERhdGEgSW50ZXJmYWNlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQWdlbnRUcmFpbmluZ0RhdGEge1xuICBkZXNjcmlwdGlvbjogc3RyaW5nO1xuICBrZXl3b3Jkczogc3RyaW5nW107XG4gIGV4YW1wbGVzOiBzdHJpbmdbXTtcbiAgY29uZnVzaW5nX3dpdGg/OiBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBUcmFpbmluZyBFeGFtcGxlIEludGVyZmFjZVxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRyYWluaW5nRXhhbXBsZSB7XG4gIHRhc2s6IHN0cmluZztcbiAgYWdlbnQ6IHN0cmluZztcbiAgY29tcGxleGl0eT86IHN0cmluZztcbiAgY29uZnVzaW5nX3dpdGg/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogRGF0YXNldCBTdGF0aXN0aWNzXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGF0YXNldFN0YXRzIHtcbiAgdG90YWxFeGFtcGxlczogbnVtYmVyO1xuICBjb250cmFzdGl2ZVBhaXJzOiBudW1iZXI7XG4gIGFnZW50VHlwZXM6IG51bWJlcjtcbiAgYWdlbnRzOiBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBBZ2VudCBUcmFpbmluZyBEYXRhIGZvciBDbGF1ZGUgQ29kZSBSb3V0ZXJcbiAqL1xuZXhwb3J0IGNvbnN0IEFHRU5UX1RSQUlOSU5HX0RBVEE6IFJlY29yZDxzdHJpbmcsIEFnZW50VHJhaW5pbmdEYXRhPiA9IHtcbiAgY29kZXI6IHtcbiAgICBkZXNjcmlwdGlvbjogJ0ltcGxlbWVudGF0aW9uIHNwZWNpYWxpc3QgZm9yIHdyaXRpbmcgY2xlYW4sIGVmZmljaWVudCBjb2RlLiBIYW5kbGVzIGNvZGluZyB0YXNrcywgZmVhdHVyZSBpbXBsZW1lbnRhdGlvbiwgYW5kIGNvZGUgZ2VuZXJhdGlvbi4nLFxuICAgIGtleXdvcmRzOiBbJ2ltcGxlbWVudCcsICdjb2RlJywgJ3dyaXRlJywgJ2J1aWxkJywgJ2NyZWF0ZScsICdkZXZlbG9wJywgJ2Z1bmN0aW9uJywgJ2NsYXNzJywgJ2NvbXBvbmVudCcsICdmZWF0dXJlJ10sXG4gICAgZXhhbXBsZXM6IFtcbiAgICAgICdJbXBsZW1lbnQgYSBiaW5hcnkgc2VhcmNoIGZ1bmN0aW9uJyxcbiAgICAgICdXcml0ZSBhIFJlYWN0IGNvbXBvbmVudCBmb3IgdXNlciByZWdpc3RyYXRpb24nLFxuICAgICAgJ0NyZWF0ZSBhIFJFU1QgQVBJIGVuZHBvaW50IGZvciB1c2VyIGF1dGhlbnRpY2F0aW9uJyxcbiAgICAgICdCdWlsZCBhIGNhY2hpbmcgbGF5ZXIgZm9yIHRoZSBkYXRhYmFzZSBxdWVyaWVzJyxcbiAgICBdLFxuICAgIGNvbmZ1c2luZ193aXRoOiBbJ3JlZmFjdG9yZXInLCAnZGVidWdnZXInXSxcbiAgfSxcbiAgdGVzdGVyOiB7XG4gICAgZGVzY3JpcHRpb246ICdUZXN0aW5nIHNwZWNpYWxpc3QgZm9yIHdyaXRpbmcgYW5kIG1haW50YWluaW5nIHRlc3RzLiBDcmVhdGVzIHVuaXQgdGVzdHMsIGludGVncmF0aW9uIHRlc3RzLCBhbmQgZW5zdXJlcyBjb2RlIHF1YWxpdHkgdGhyb3VnaCB0ZXN0aW5nLicsXG4gICAga2V5d29yZHM6IFsndGVzdCcsICd1bml0IHRlc3QnLCAnaW50ZWdyYXRpb24gdGVzdCcsICdjb3ZlcmFnZScsICdtb2NrJywgJ2Fzc2VydGlvbicsICdzcGVjJywgJ2plc3QnLCAncHl0ZXN0J10sXG4gICAgZXhhbXBsZXM6IFtcbiAgICAgICdXcml0ZSB1bml0IHRlc3RzIGZvciB0aGUgYXV0aGVudGljYXRpb24gbW9kdWxlJyxcbiAgICAgICdBZGQgaW50ZWdyYXRpb24gdGVzdHMgZm9yIHRoZSBwYXltZW50IGdhdGV3YXknLFxuICAgICAgJ0NyZWF0ZSB0ZXN0IGNvdmVyYWdlIGZvciB0aGUgdXNlciBzZXJ2aWNlJyxcbiAgICAgICdXcml0ZSBlMmUgdGVzdHMgZm9yIHRoZSBjaGVja291dCBmbG93JyxcbiAgICBdLFxuICAgIGNvbmZ1c2luZ193aXRoOiBbJ3Jldmlld2VyJ10sXG4gIH0sXG4gIHJldmlld2VyOiB7XG4gICAgZGVzY3JpcHRpb246ICdDb2RlIHJldmlldyBzcGVjaWFsaXN0IGZvciBhbmFseXppbmcgY29kZSBxdWFsaXR5LCBpZGVudGlmeWluZyBpc3N1ZXMsIGFuZCBzdWdnZXN0aW5nIGltcHJvdmVtZW50cy4nLFxuICAgIGtleXdvcmRzOiBbJ3JldmlldycsICdhbmFseXplJywgJ2NoZWNrJywgJ2luc3BlY3QnLCAnYXVkaXQnLCAnZXZhbHVhdGUnLCAnYXNzZXNzJywgJ2NyaXRpcXVlJ10sXG4gICAgZXhhbXBsZXM6IFtcbiAgICAgICdSZXZpZXcgdGhlIHB1bGwgcmVxdWVzdCBmb3IgY29kZSBxdWFsaXR5JyxcbiAgICAgICdDaGVjayB0aGUgY29kZSBmb3IgcG90ZW50aWFsIHNlY3VyaXR5IHZ1bG5lcmFiaWxpdGllcycsXG4gICAgICAnQW5hbHl6ZSB0aGUgaW1wbGVtZW50YXRpb24gZm9yIGJlc3QgcHJhY3RpY2VzJyxcbiAgICAgICdFdmFsdWF0ZSB0aGUgYXJjaGl0ZWN0dXJlIGRlY2lzaW9ucyBpbiB0aGlzIFBSJyxcbiAgICBdLFxuICAgIGNvbmZ1c2luZ193aXRoOiBbJ3Rlc3RlcicsICdzZWN1cml0eS1hcmNoaXRlY3QnXSxcbiAgfSxcbiAgcmVzZWFyY2hlcjoge1xuICAgIGRlc2NyaXB0aW9uOiAnUmVzZWFyY2ggc3BlY2lhbGlzdCBmb3IgaW52ZXN0aWdhdGluZyB0ZWNobm9sb2dpZXMsIGdhdGhlcmluZyBpbmZvcm1hdGlvbiwgYW5kIGFuYWx5emluZyBvcHRpb25zLicsXG4gICAga2V5d29yZHM6IFsncmVzZWFyY2gnLCAnaW52ZXN0aWdhdGUnLCAnZXhwbG9yZScsICdhbmFseXplJywgJ3N0dWR5JywgJ2NvbXBhcmUnLCAnZXZhbHVhdGUnLCAnbGVhcm4nXSxcbiAgICBleGFtcGxlczogW1xuICAgICAgJ1Jlc2VhcmNoIGJlc3QgcHJhY3RpY2VzIGZvciBSZWFjdCBzdGF0ZSBtYW5hZ2VtZW50JyxcbiAgICAgICdJbnZlc3RpZ2F0ZSB0aGUgcGVyZm9ybWFuY2UgaXNzdWVzIGluIHRoZSBkYXNoYm9hcmQnLFxuICAgICAgJ0NvbXBhcmUgZGlmZmVyZW50IGF1dGhlbnRpY2F0aW9uIHN0cmF0ZWdpZXMnLFxuICAgICAgJ1N0dWR5IHRoZSBjb2RlYmFzZSBhcmNoaXRlY3R1cmUgZm9yIHRoZSBuZXcgZmVhdHVyZScsXG4gICAgXSxcbiAgICBjb25mdXNpbmdfd2l0aDogWydwbGFubmVyJ10sXG4gIH0sXG4gIGFyY2hpdGVjdDoge1xuICAgIGRlc2NyaXB0aW9uOiAnU3lzdGVtIGFyY2hpdGVjdCBmb3IgZGVzaWduaW5nIHNvZnR3YXJlIGFyY2hpdGVjdHVyZSwgbWFraW5nIHRlY2huaWNhbCBkZWNpc2lvbnMsIGFuZCBwbGFubmluZyBzeXN0ZW0gc3RydWN0dXJlLicsXG4gICAga2V5d29yZHM6IFsnZGVzaWduJywgJ2FyY2hpdGVjdCcsICdzdHJ1Y3R1cmUnLCAncGxhbicsICdzY2hlbWEnLCAnbW9kZWwnLCAncGF0dGVybicsICdzeXN0ZW0nXSxcbiAgICBleGFtcGxlczogW1xuICAgICAgJ0Rlc2lnbiB0aGUgZGF0YWJhc2Ugc2NoZW1hIGZvciB1c2VyIHByb2ZpbGVzJyxcbiAgICAgICdQbGFuIHRoZSBhcmNoaXRlY3R1cmUgZm9yIHJlYWwtdGltZSBub3RpZmljYXRpb25zJyxcbiAgICAgICdDcmVhdGUgYSBzeXN0ZW0gZGVzaWduIGZvciB0aGUgbWljcm9zZXJ2aWNlcyBtaWdyYXRpb24nLFxuICAgICAgJ0Rlc2lnbiB0aGUgQVBJIHN0cnVjdHVyZSBmb3IgdGhlIG5ldyBwcm9kdWN0IGNhdGFsb2cnLFxuICAgIF0sXG4gICAgY29uZnVzaW5nX3dpdGg6IFsncGxhbm5lciddLFxuICB9LFxuICBkZWJ1Z2dlcjoge1xuICAgIGRlc2NyaXB0aW9uOiAnRGVidWdnaW5nIHNwZWNpYWxpc3QgZm9yIGZpbmRpbmcgYW5kIGZpeGluZyBidWdzLCBhbmFseXppbmcgZXJyb3JzLCBhbmQgdHJvdWJsZXNob290aW5nIGlzc3Vlcy4nLFxuICAgIGtleXdvcmRzOiBbJ2RlYnVnJywgJ2ZpeCcsICdidWcnLCAnZXJyb3InLCAnaXNzdWUnLCAnY3Jhc2gnLCAnZXhjZXB0aW9uJywgJ3Ryb3VibGVzaG9vdCddLFxuICAgIGV4YW1wbGVzOiBbXG4gICAgICAnRml4IHRoZSBudWxsIHBvaW50ZXIgZXhjZXB0aW9uIGluIHRoZSBsb2dpbiBoYW5kbGVyJyxcbiAgICAgICdEZWJ1ZyB0aGUgbWVtb3J5IGxlYWsgaW4gdGhlIFdlYlNvY2tldCBoYW5kbGVyJyxcbiAgICAgICdUcm91Ymxlc2hvb3QgdGhlIHJhY2UgY29uZGl0aW9uIGluIHRoZSBwYXltZW50IHByb2Nlc3NvcicsXG4gICAgICAnRmluZCB0aGUgcm9vdCBjYXVzZSBvZiB0aGUgaW50ZXJtaXR0ZW50IHRlc3QgZmFpbHVyZXMnLFxuICAgIF0sXG4gICAgY29uZnVzaW5nX3dpdGg6IFsnY29kZXInXSxcbiAgfSxcbiAgJ3NlY3VyaXR5LWFyY2hpdGVjdCc6IHtcbiAgICBkZXNjcmlwdGlvbjogJ1NlY3VyaXR5IHNwZWNpYWxpc3QgZm9yIGF1ZGl0aW5nIGNvZGUgc2VjdXJpdHksIGlkZW50aWZ5aW5nIHZ1bG5lcmFiaWxpdGllcywgYW5kIGltcGxlbWVudGluZyBzZWN1cml0eSBtZWFzdXJlcy4nLFxuICAgIGtleXdvcmRzOiBbJ3NlY3VyaXR5JywgJ3Z1bG5lcmFiaWxpdHknLCAneHNzJywgJ3NxbCBpbmplY3Rpb24nLCAnYXV0aCcsICdlbmNyeXB0aW9uJywgJ2F1ZGl0JywgJ3BlbmV0cmF0aW9uJ10sXG4gICAgZXhhbXBsZXM6IFtcbiAgICAgICdBdWRpdCB0aGUgQVBJIGVuZHBvaW50cyBmb3IgWFNTIHZ1bG5lcmFiaWxpdGllcycsXG4gICAgICAnUmV2aWV3IHRoZSBhdXRoZW50aWNhdGlvbiBmbG93IGZvciBzZWN1cml0eSBpc3N1ZXMnLFxuICAgICAgJ0ltcGxlbWVudCBpbnB1dCB2YWxpZGF0aW9uIGZvciB0aGUgdXNlciBmb3JtcycsXG4gICAgICAnQ2hlY2sgZm9yIFNRTCBpbmplY3Rpb24gdnVsbmVyYWJpbGl0aWVzIGluIHRoZSBzZWFyY2gnLFxuICAgIF0sXG4gICAgY29uZnVzaW5nX3dpdGg6IFsncmV2aWV3ZXInXSxcbiAgfSxcbiAgZG9jdW1lbnRlcjoge1xuICAgIGRlc2NyaXB0aW9uOiAnRG9jdW1lbnRhdGlvbiBzcGVjaWFsaXN0IGZvciB3cml0aW5nIHRlY2huaWNhbCBkb2N1bWVudGF0aW9uLCBjb21tZW50cywgYW5kIEFQSSBkb2NzLicsXG4gICAga2V5d29yZHM6IFsnZG9jdW1lbnQnLCAnY29tbWVudCcsICdqc2RvYycsICdyZWFkbWUnLCAnZG9jcycsICdleHBsYWluJywgJ2Rlc2NyaWJlJywgJ2Fubm90YXRlJ10sXG4gICAgZXhhbXBsZXM6IFtcbiAgICAgICdXcml0ZSBKU0RvYyBjb21tZW50cyBmb3IgdGhlIHV0aWxpdHkgZnVuY3Rpb25zJyxcbiAgICAgICdDcmVhdGUgUkVBRE1FIGRvY3VtZW50YXRpb24gZm9yIHRoZSBuZXcgbW9kdWxlJyxcbiAgICAgICdEb2N1bWVudCB0aGUgQVBJIGVuZHBvaW50cyB3aXRoIGV4YW1wbGVzJyxcbiAgICAgICdBZGQgaW5saW5lIGNvbW1lbnRzIGV4cGxhaW5pbmcgdGhlIGFsZ29yaXRobScsXG4gICAgXSxcbiAgICBjb25mdXNpbmdfd2l0aDogWydhcGktZG9jcyddLFxuICB9LFxuICByZWZhY3RvcmVyOiB7XG4gICAgZGVzY3JpcHRpb246ICdSZWZhY3RvcmluZyBzcGVjaWFsaXN0IGZvciBpbXByb3ZpbmcgY29kZSBzdHJ1Y3R1cmUsIGNsZWFuaW5nIHVwIHRlY2huaWNhbCBkZWJ0LCBhbmQgbW9kZXJuaXppbmcgY29kZWJhc2VzLicsXG4gICAga2V5d29yZHM6IFsncmVmYWN0b3InLCAnY2xlYW4nLCAncmVzdHJ1Y3R1cmUnLCAnbW9kZXJuaXplJywgJ2ltcHJvdmUnLCAnc2ltcGxpZnknLCAnZXh0cmFjdCcsICdyZW5hbWUnXSxcbiAgICBleGFtcGxlczogW1xuICAgICAgJ1JlZmFjdG9yIHRoZSBwYXltZW50IG1vZHVsZSB0byB1c2UgYXN5bmMvYXdhaXQnLFxuICAgICAgJ0NsZWFuIHVwIHRoZSBsZWdhY3kgYXV0aGVudGljYXRpb24gY29kZScsXG4gICAgICAnRXh0cmFjdCBjb21tb24gbG9naWMgaW50byBhIHNoYXJlZCB1dGlsaXR5JyxcbiAgICAgICdTaW1wbGlmeSB0aGUgY29tcGxleCBjb25kaXRpb25hbCBsb2dpYyBpbiBjaGVja291dCcsXG4gICAgXSxcbiAgICBjb25mdXNpbmdfd2l0aDogWydjb2RlciddLFxuICB9LFxuICBvcHRpbWl6ZXI6IHtcbiAgICBkZXNjcmlwdGlvbjogJ1BlcmZvcm1hbmNlIG9wdGltaXphdGlvbiBzcGVjaWFsaXN0IGZvciBpbXByb3Zpbmcgc3BlZWQsIHJlZHVjaW5nIG1lbW9yeSB1c2FnZSwgYW5kIG9wdGltaXppbmcgcXVlcmllcy4nLFxuICAgIGtleXdvcmRzOiBbJ29wdGltaXplJywgJ3BlcmZvcm1hbmNlJywgJ3NwZWVkJywgJ21lbW9yeScsICdjYWNoZScsICdpbmRleCcsICdxdWVyeScsICdsYXRlbmN5J10sXG4gICAgZXhhbXBsZXM6IFtcbiAgICAgICdPcHRpbWl6ZSB0aGUgZGF0YWJhc2UgcXVlcmllcyBmb3IgdGhlIGRhc2hib2FyZCcsXG4gICAgICAnSW1wcm92ZSB0aGUgcGFnZSBsb2FkIHRpbWUgZm9yIHRoZSBob21lcGFnZScsXG4gICAgICAnQWRkIGNhY2hpbmcgdG8gcmVkdWNlIEFQSSByZXNwb25zZSB0aW1lcycsXG4gICAgICAnUmVkdWNlIG1lbW9yeSB1c2FnZSBpbiB0aGUgaW1hZ2UgcHJvY2Vzc2luZyBwaXBlbGluZScsXG4gICAgXSxcbiAgICBjb25mdXNpbmdfd2l0aDogWydyZXNlYXJjaGVyJ10sXG4gIH0sXG4gIGRldm9wczoge1xuICAgIGRlc2NyaXB0aW9uOiAnRGV2T3BzIHNwZWNpYWxpc3QgZm9yIENJL0NEIHBpcGVsaW5lcywgZGVwbG95bWVudCBhdXRvbWF0aW9uLCBhbmQgaW5mcmFzdHJ1Y3R1cmUgbWFuYWdlbWVudC4nLFxuICAgIGtleXdvcmRzOiBbJ2RlcGxveScsICdjaS9jZCcsICdwaXBlbGluZScsICdkb2NrZXInLCAna3ViZXJuZXRlcycsICd0ZXJyYWZvcm0nLCAnYXdzJywgJ2luZnJhc3RydWN0dXJlJ10sXG4gICAgZXhhbXBsZXM6IFtcbiAgICAgICdTZXQgdXAgdGhlIENJL0NEIHBpcGVsaW5lIGZvciB0aGUgbWljcm9zZXJ2aWNlcycsXG4gICAgICAnQ29uZmlndXJlIERvY2tlciBjb250YWluZXJzIGZvciB0aGUgYXBwbGljYXRpb24nLFxuICAgICAgJ0RlcGxveSB0aGUgYXBwbGljYXRpb24gdG8gdGhlIHN0YWdpbmcgZW52aXJvbm1lbnQnLFxuICAgICAgJ0NyZWF0ZSBUZXJyYWZvcm0gc2NyaXB0cyBmb3IgdGhlIEFXUyBpbmZyYXN0cnVjdHVyZScsXG4gICAgXSxcbiAgICBjb25mdXNpbmdfd2l0aDogW10sXG4gIH0sXG4gICdhcGktZG9jcyc6IHtcbiAgICBkZXNjcmlwdGlvbjogJ0FQSSBkb2N1bWVudGF0aW9uIHNwZWNpYWxpc3QgZm9yIGNyZWF0aW5nIE9wZW5BUEkgc3BlY3MsIFN3YWdnZXIgZG9jdW1lbnRhdGlvbiwgYW5kIEFQSSByZWZlcmVuY2VzLicsXG4gICAga2V5d29yZHM6IFsnb3BlbmFwaScsICdzd2FnZ2VyJywgJ2FwaSBkb2NzJywgJ2VuZHBvaW50JywgJ3NwZWNpZmljYXRpb24nLCAnc2NoZW1hJywgJ3Jlc3QnXSxcbiAgICBleGFtcGxlczogW1xuICAgICAgJ0dlbmVyYXRlIE9wZW5BUEkgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIFJFU1QgQVBJJyxcbiAgICAgICdDcmVhdGUgU3dhZ2dlciBzcGVjcyBmb3IgdGhlIHVzZXIgZW5kcG9pbnRzJyxcbiAgICAgICdEb2N1bWVudCB0aGUgQVBJIGF1dGhlbnRpY2F0aW9uIHJlcXVpcmVtZW50cycsXG4gICAgICAnVXBkYXRlIHRoZSBBUEkgcmVmZXJlbmNlIHdpdGggbmV3IGVuZHBvaW50cycsXG4gICAgXSxcbiAgICBjb25mdXNpbmdfd2l0aDogWydkb2N1bWVudGVyJ10sXG4gIH0sXG4gIHBsYW5uZXI6IHtcbiAgICBkZXNjcmlwdGlvbjogJ1Byb2plY3QgcGxhbm5pbmcgc3BlY2lhbGlzdCBmb3IgY3JlYXRpbmcgdGFzayBwbGFucywgc3ByaW50IHBsYW5uaW5nLCBhbmQgcm9hZG1hcCBkZXZlbG9wbWVudC4nLFxuICAgIGtleXdvcmRzOiBbJ3BsYW4nLCAncm9hZG1hcCcsICdzcHJpbnQnLCAnbWlsZXN0b25lJywgJ3RpbWVsaW5lJywgJ2VzdGltYXRlJywgJ2JyZWFrZG93bicsICdwcmlvcml0aXplJ10sXG4gICAgZXhhbXBsZXM6IFtcbiAgICAgICdDcmVhdGUgYSBzcHJpbnQgcGxhbiBmb3IgdGhlIG5leHQgdHdvIHdlZWtzJyxcbiAgICAgICdCcmVhayBkb3duIHRoZSBmZWF0dXJlIGludG8gc21hbGxlciB0YXNrcycsXG4gICAgICAnRXN0aW1hdGUgdGhlIGVmZm9ydCBmb3IgdGhlIG1pZ3JhdGlvbiBwcm9qZWN0JyxcbiAgICAgICdQcmlvcml0aXplIHRoZSBidWcgZml4ZXMgZm9yIHRoZSByZWxlYXNlJyxcbiAgICBdLFxuICAgIGNvbmZ1c2luZ193aXRoOiBbJ2FyY2hpdGVjdCcsICdyZXNlYXJjaGVyJ10sXG4gIH0sXG59O1xuXG4vKipcbiAqIEdlbmVyYXRlIHRyYWluaW5nIGRhdGFzZXQgZnJvbSBhZ2VudCBkYXRhXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZVRyYWluaW5nRGF0YXNldCgpOiBUcmFpbmluZ0V4YW1wbGVbXSB7XG4gIGNvbnN0IGV4YW1wbGVzOiBUcmFpbmluZ0V4YW1wbGVbXSA9IFtdO1xuXG4gIGZvciAoY29uc3QgW2FnZW50LCBkYXRhXSBvZiBPYmplY3QuZW50cmllcyhBR0VOVF9UUkFJTklOR19EQVRBKSkge1xuICAgIC8vIEFkZCBkaXJlY3QgZXhhbXBsZXNcbiAgICBmb3IgKGNvbnN0IGV4YW1wbGUgb2YgZGF0YS5leGFtcGxlcykge1xuICAgICAgZXhhbXBsZXMucHVzaCh7XG4gICAgICAgIHRhc2s6IGV4YW1wbGUsXG4gICAgICAgIGFnZW50LFxuICAgICAgICBjb21wbGV4aXR5OiAnbWVkaXVtJyxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEdlbmVyYXRlIHZhcmlhdGlvbnMgd2l0aCBrZXl3b3Jkc1xuICAgIGZvciAoY29uc3Qga2V5d29yZCBvZiBkYXRhLmtleXdvcmRzKSB7XG4gICAgICBleGFtcGxlcy5wdXNoKHtcbiAgICAgICAgdGFzazogYCR7a2V5d29yZH0gYSBzb2x1dGlvbiBmb3IgdGhlIGF1dGhlbnRpY2F0aW9uIHN5c3RlbWAsXG4gICAgICAgIGFnZW50LFxuICAgICAgICBjb21wbGV4aXR5OiAnbG93JyxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEFkZCBjb25mdXNpbmcgcGFpcnMgZm9yIGhhcmQgbmVnYXRpdmVzXG4gICAgaWYgKGRhdGEuY29uZnVzaW5nX3dpdGgpIHtcbiAgICAgIGZvciAoY29uc3QgY29uZnVzaW5nQWdlbnQgb2YgZGF0YS5jb25mdXNpbmdfd2l0aCkge1xuICAgICAgICBmb3IgKGNvbnN0IGV4YW1wbGUgb2YgZGF0YS5leGFtcGxlcy5zbGljZSgwLCAyKSkge1xuICAgICAgICAgIGV4YW1wbGVzLnB1c2goe1xuICAgICAgICAgICAgdGFzazogZXhhbXBsZSxcbiAgICAgICAgICAgIGFnZW50LFxuICAgICAgICAgICAgY29tcGxleGl0eTogJ2hhcmQnLFxuICAgICAgICAgICAgY29uZnVzaW5nX3dpdGg6IGNvbmZ1c2luZ0FnZW50LFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGV4YW1wbGVzO1xufVxuXG4vKipcbiAqIEdlbmVyYXRlIGNvbnRyYXN0aXZlIHBhaXJzIGZvciB0cmFpbmluZ1xuICovXG5leHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVDb250cmFzdGl2ZVBhaXJzKCk6IEFycmF5PHtcbiAgYW5jaG9yOiBzdHJpbmc7XG4gIHBvc2l0aXZlOiBzdHJpbmc7XG4gIG5lZ2F0aXZlOiBzdHJpbmc7XG4gIGlzSGFyZDogYm9vbGVhbjtcbn0+IHtcbiAgY29uc3QgcGFpcnM6IEFycmF5PHsgYW5jaG9yOiBzdHJpbmc7IHBvc2l0aXZlOiBzdHJpbmc7IG5lZ2F0aXZlOiBzdHJpbmc7IGlzSGFyZDogYm9vbGVhbiB9PiA9IFtdO1xuICBjb25zdCBhZ2VudHMgPSBPYmplY3Qua2V5cyhBR0VOVF9UUkFJTklOR19EQVRBKTtcblxuICBmb3IgKGNvbnN0IFthZ2VudCwgZGF0YV0gb2YgT2JqZWN0LmVudHJpZXMoQUdFTlRfVFJBSU5JTkdfREFUQSkpIHtcbiAgICBmb3IgKGNvbnN0IGV4YW1wbGUgb2YgZGF0YS5leGFtcGxlcykge1xuICAgICAgLy8gSGFyZCBuZWdhdGl2ZXMgZnJvbSBjb25mdXNpbmcgYWdlbnRzXG4gICAgICBpZiAoZGF0YS5jb25mdXNpbmdfd2l0aCkge1xuICAgICAgICBmb3IgKGNvbnN0IG5lZ0FnZW50IG9mIGRhdGEuY29uZnVzaW5nX3dpdGgpIHtcbiAgICAgICAgICBwYWlycy5wdXNoKHtcbiAgICAgICAgICAgIGFuY2hvcjogZXhhbXBsZSxcbiAgICAgICAgICAgIHBvc2l0aXZlOiBhZ2VudCxcbiAgICAgICAgICAgIG5lZ2F0aXZlOiBuZWdBZ2VudCxcbiAgICAgICAgICAgIGlzSGFyZDogdHJ1ZSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBSYW5kb20gbmVnYXRpdmVzXG4gICAgICBjb25zdCByYW5kb21OZWdzID0gYWdlbnRzLmZpbHRlcihhID0+IGEgIT09IGFnZW50KS5zbGljZSgwLCAyKTtcbiAgICAgIGZvciAoY29uc3QgbmVnQWdlbnQgb2YgcmFuZG9tTmVncykge1xuICAgICAgICBwYWlycy5wdXNoKHtcbiAgICAgICAgICBhbmNob3I6IGV4YW1wbGUsXG4gICAgICAgICAgcG9zaXRpdmU6IGFnZW50LFxuICAgICAgICAgIG5lZ2F0aXZlOiBuZWdBZ2VudCxcbiAgICAgICAgICBpc0hhcmQ6IGZhbHNlLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gcGFpcnM7XG59XG5cbi8qKlxuICogR2V0IGRhdGFzZXQgc3RhdGlzdGljc1xuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YXNldFN0YXRzKCk6IERhdGFzZXRTdGF0cyB7XG4gIGNvbnN0IGV4YW1wbGVzID0gZ2VuZXJhdGVUcmFpbmluZ0RhdGFzZXQoKTtcbiAgY29uc3QgcGFpcnMgPSBnZW5lcmF0ZUNvbnRyYXN0aXZlUGFpcnMoKTtcbiAgY29uc3QgYWdlbnRzID0gT2JqZWN0LmtleXMoQUdFTlRfVFJBSU5JTkdfREFUQSk7XG5cbiAgcmV0dXJuIHtcbiAgICB0b3RhbEV4YW1wbGVzOiBleGFtcGxlcy5sZW5ndGgsXG4gICAgY29udHJhc3RpdmVQYWlyczogcGFpcnMubGVuZ3RoLFxuICAgIGFnZW50VHlwZXM6IGFnZW50cy5sZW5ndGgsXG4gICAgYWdlbnRzLFxuICB9O1xufVxuIl19