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