own-rag-cli 0.0.1-snapshot

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.
@@ -0,0 +1,1129 @@
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # rag-setup-macos.run — Instalador macOS auto-suficiente (ChromaDB + MCP + RAG)
4
+ # =============================================================================
5
+ # Gerado automaticamente por build_run_macos.sh
6
+ # Versão: 2026-03-06 17:03
7
+ # MCP checksum (payload sem shebang): 3246eeb57f901742d915e0bce37fa96f059e149a57bbce73095ff4e5ea51d8d4
8
+ #
9
+ # Uso:
10
+ # chmod +x rag-setup-macos.run
11
+ # ./rag-setup-macos.run [path/to/project] [--skip-index] [--only-index]
12
+ # ./rag-setup-macos.run --reinstall
13
+ # ./rag-setup-macos.run --change-model|-cm
14
+ # =============================================================================
15
+
16
+ set -euo pipefail
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Argumentos
20
+ # ---------------------------------------------------------------------------
21
+ SKIP_INDEX=false
22
+ ONLY_INDEX=false
23
+ REINSTALL=false
24
+ CHANGE_MODEL=false
25
+ CUSTOM_PROJECT_DIR=""
26
+
27
+ for arg in "$@"; do
28
+ case "$arg" in
29
+ --skip-index) SKIP_INDEX=true ;;
30
+ --only-index) ONLY_INDEX=true ;;
31
+ --reinstall) REINSTALL=true ;;
32
+ --change-model|-cm|--chage-model|-cg) CHANGE_MODEL=true ;;
33
+ --help|-h)
34
+ echo "Usage: $0 [path/to/project] [--skip-index] [--only-index] [--reinstall] [--change-model|-cm]"
35
+ exit 0 ;;
36
+ -*)
37
+ echo "Unknown option: $arg"
38
+ exit 1 ;;
39
+ *)
40
+ CUSTOM_PROJECT_DIR="$arg" ;;
41
+ esac
42
+ done
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Cores
46
+ # ---------------------------------------------------------------------------
47
+ RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
48
+ BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m'
49
+
50
+ UI_LANG="${RAG_SETUP_LANG:-}"
51
+ YES_NO_HINT="[s/N]"
52
+
53
+ set_lang_defaults() {
54
+ if [[ "$UI_LANG" == "en-us" ]]; then
55
+ YES_NO_HINT="[y/N]"
56
+ else
57
+ YES_NO_HINT="[s/N]"
58
+ fi
59
+ }
60
+
61
+ select_ui_language() {
62
+ if [[ -n "$UI_LANG" ]]; then
63
+ UI_LANG="$(echo "$UI_LANG" | tr '[:upper:]' '[:lower:]')"
64
+ case "$UI_LANG" in
65
+ pt-br|pt|en-us|en) ;;
66
+ *) UI_LANG="pt-br" ;;
67
+ esac
68
+ [[ "$UI_LANG" == "pt" ]] && UI_LANG="pt-br"
69
+ [[ "$UI_LANG" == "en" ]] && UI_LANG="en-us"
70
+ set_lang_defaults
71
+ return
72
+ fi
73
+
74
+ if [[ ! -t 0 ]]; then
75
+ UI_LANG="pt-br"
76
+ set_lang_defaults
77
+ return
78
+ fi
79
+
80
+ echo ""
81
+ echo -e "${GREEN}Idioma / Language: [1] PT-BR [2] EN-US (padrão/default: 1)${NC}"
82
+ read -r -p "> " LANG_CHOICE
83
+ case "$LANG_CHOICE" in
84
+ 2|en|EN|en-us|EN-US|english|English) UI_LANG="en-us" ;;
85
+ *) UI_LANG="pt-br" ;;
86
+ esac
87
+ set_lang_defaults
88
+ }
89
+
90
+ t() {
91
+ local key="$1"
92
+ if [[ "$UI_LANG" == "en-us" ]]; then
93
+ case "$key" in
94
+ err_prefix) echo "ERROR" ;;
95
+ title) echo "RAG Local Setup (macOS) — ChromaDB + MCP" ;;
96
+ project) echo "Project" ;;
97
+ platform_only) echo "This installer is only for macOS (Darwin)." ;;
98
+ extracting) echo "Extracting embedded files" ;;
99
+ extracted_to) echo "Extracted to" ;;
100
+ checking_prereq) echo "Checking prerequisites" ;;
101
+ python_missing) echo "Python 3 not found. Install with Homebrew: brew install python" ;;
102
+ python_min) echo "Python 3.10+ required. Current" ;;
103
+ py_venv_missing) echo "Python venv module not found." ;;
104
+ docker_missing) echo "Docker not found. Install Docker Desktop: brew install --cask docker" ;;
105
+ docker_daemon) echo "Docker is not running. Open Docker Desktop and wait until it is ready." ;;
106
+ compose_missing) echo "Docker Compose not available (docker compose)." ;;
107
+ prereq_ok) echo "Prerequisites OK." ;;
108
+ no_curl) echo "curl not found. Chroma healthcheck will be skipped." ;;
109
+ reset_start) echo "Resetting previous environment" ;;
110
+ reset_done) echo "Reset completed." ;;
111
+ op_cancelled) echo "Operation canceled by user." ;;
112
+ change_model_msg) echo "Model change requested. Chroma reset + full reindex required." ;;
113
+ change_model_confirm) echo "Confirm Chroma reset and full reindex?" ;;
114
+ change_model_noninteractive) echo "Non-interactive mode: proceeding with reset for --change-model." ;;
115
+ only_index_mode) echo "Mode: only index" ;;
116
+ venv_not_found) echo "Venv not found at" ;;
117
+ path_not_found) echo "Path not found" ;;
118
+ indexing) echo "Indexing" ;;
119
+ indexing_done) echo "Indexing completed." ;;
120
+ index_failed_code) echo "Indexer failed. Exit code" ;;
121
+ index_oom_title) echo "Indexing stopped due to out-of-memory (OOM killer, exit 137)." ;;
122
+ section_venv) echo "Setting up Python venv (~/.rag_venv)" ;;
123
+ deps_ok) echo "Dependencies already installed. Skipping." ;;
124
+ deps_reinstall) echo "Installing/updating dependencies..." ;;
125
+ creating_venv) echo "Creating venv at" ;;
126
+ venv_created) echo "Venv created." ;;
127
+ upgrading_pip) echo "Upgrading pip..." ;;
128
+ deps_installed) echo "Dependencies installed." ;;
129
+ section_chroma) echo "Setting up ChromaDB (Docker Desktop)" ;;
130
+ compose_installed) echo "docker-compose.yml installed at" ;;
131
+ compose_keep) echo "docker-compose.yml already exists. Keeping current file." ;;
132
+ chroma_port_prompt) echo "Choose ChromaDB host port [1-65535] (default: 8000): " ;;
133
+ chroma_port_selected) echo "Using ChromaDB host port:" ;;
134
+ chroma_port_invalid) echo "Invalid port. Using default 8000." ;;
135
+ chroma_port_in_use) echo "Port is already in use:" ;;
136
+ chroma_port_try_another) echo "Choose another port." ;;
137
+ chroma_port_auto_fallback) echo "Non-interactive mode: using next free port:" ;;
138
+ chroma_port_no_free) echo "Could not find a free TCP port for ChromaDB." ;;
139
+ chroma_running) echo "chromadb-rag container already running." ;;
140
+ chroma_start) echo "Starting ChromaDB..." ;;
141
+ chroma_wait) echo "Waiting ChromaDB to initialize..." ;;
142
+ chroma_ready) echo "ChromaDB is responding at" ;;
143
+ chroma_timeout) echo "ChromaDB did not respond in time. Check Docker Desktop and container logs." ;;
144
+ section_install_mcp) echo "Installing mcp-rag-server globally" ;;
145
+ mcp_keep) echo "mcp-rag-server already up to date. Keeping." ;;
146
+ mcp_outdated) echo "mcp-rag-server is outdated." ;;
147
+ mcp_prompt_update) echo "Update mcp-rag-server now? $YES_NO_HINT " ;;
148
+ mcp_prompt_reinstall) echo "mcp-rag-server is current. Reinstall anyway? $YES_NO_HINT " ;;
149
+ mcp_skip) echo "Keeping current mcp-rag-server." ;;
150
+ mcp_installed) echo "mcp-rag-server installed" ;;
151
+ mod_hf_installed) echo "Download module installed" ;;
152
+ mod_ms_installed) echo "Optional provider module installed" ;;
153
+ shebang_set) echo "Shebang" ;;
154
+ path_added) echo "Added ~/.local/bin to PATH in" ;;
155
+ section_mcp_cfg) echo "Optional MCP configuration (Claude/Cursor)" ;;
156
+ no_cfg_files) echo "No config files detected for automatic MCP update." ;;
157
+ cfg_all_current) echo "MCP 'rag-codebase' already up to date in detected files. Skipping." ;;
158
+ cfg_detected) echo "Config files pending update" ;;
159
+ ask_apply_cfg) echo "Apply MCP 'rag-codebase' update in these files? $YES_NO_HINT " ;;
160
+ noninteractive_cfg_skip) echo "Non-interactive mode: skipping automatic MCP config update." ;;
161
+ cannot_update_cfg) echo "Could not update" ;;
162
+ already_updated) echo "MCP 'rag-codebase' already up to date. Skipping." ;;
163
+ updated_cfg) echo "MCP 'rag-codebase' updated to version" ;;
164
+ replaced_cfg) echo "Old RAG key replaced by 'rag-codebase' (version" ;;
165
+ added_cfg) echo "MCP 'rag-codebase' added (version" ;;
166
+ setup_done) echo "Setup completed!" ;;
167
+ next) echo "Next" ;;
168
+ next_1) echo "Restart Claude Code CLI" ;;
169
+ next_2) echo "Use semantic_search_code" ;;
170
+ next_3) echo "Reindex: ./rag-setup-macos.run --only-index" ;;
171
+ restart_tools) echo "Restart the tools that use your MCP rag." ;;
172
+ hf_detected) echo "HF token detected in environment." ;;
173
+ hf_noninteractive) echo "Non-interactive mode: continuing without HF token prompt." ;;
174
+ hf_title) echo "Hugging Face token (optional)" ;;
175
+ hf_desc) echo "Speeds up model download/rate limits." ;;
176
+ hf_prompt_now) echo "Provide HF token now? $YES_NO_HINT " ;;
177
+ hf_prompt_paste) echo "Paste HF_TOKEN (hidden): " ;;
178
+ hf_set) echo "HF token set for this run." ;;
179
+ hf_empty) echo "Empty token. Continuing without auth." ;;
180
+ hf_skip) echo "Continuing without HF token." ;;
181
+ invalid_option) echo "Invalid option. Use allowed answers." ;;
182
+ *) echo "$key" ;;
183
+ esac
184
+ else
185
+ case "$key" in
186
+ err_prefix) echo "ERRO" ;;
187
+ title) echo "RAG Local Setup (macOS) — ChromaDB + MCP" ;;
188
+ project) echo "Projeto" ;;
189
+ platform_only) echo "Este instalador é apenas para macOS (Darwin)." ;;
190
+ extracting) echo "Extraindo arquivos embutidos" ;;
191
+ extracted_to) echo "Extraído em" ;;
192
+ checking_prereq) echo "Verificando pré-requisitos" ;;
193
+ python_missing) echo "Python 3 não encontrado. Instale via Homebrew: brew install python" ;;
194
+ python_min) echo "Python 3.10+ necessário. Atual" ;;
195
+ py_venv_missing) echo "Módulo venv do Python não encontrado." ;;
196
+ docker_missing) echo "Docker não encontrado. Instale Docker Desktop: brew install --cask docker" ;;
197
+ docker_daemon) echo "Docker não está rodando. Abra o Docker Desktop e aguarde ficar pronto." ;;
198
+ compose_missing) echo "Docker Compose indisponível (docker compose)." ;;
199
+ prereq_ok) echo "Pré-requisitos OK." ;;
200
+ no_curl) echo "curl não encontrado. Healthcheck do Chroma será pulado." ;;
201
+ reset_start) echo "Zerando ambiente anterior" ;;
202
+ reset_done) echo "Reset concluído." ;;
203
+ op_cancelled) echo "Operação cancelada pelo usuário." ;;
204
+ change_model_msg) echo "Troca de modelo solicitada. Reset do Chroma + reindexação total." ;;
205
+ change_model_confirm) echo "Confirmar reset do Chroma e reindexação total?" ;;
206
+ change_model_noninteractive) echo "Modo não interativo: seguindo com reset por --change-model." ;;
207
+ only_index_mode) echo "Modo: apenas indexação" ;;
208
+ venv_not_found) echo "Venv não encontrado em" ;;
209
+ path_not_found) echo "Caminho não encontrado" ;;
210
+ indexing) echo "Indexando" ;;
211
+ indexing_done) echo "Indexação concluída." ;;
212
+ index_failed_code) echo "Indexador falhou. Código de saída" ;;
213
+ index_oom_title) echo "Indexação interrompida por falta de memória (OOM killer, exit 137)." ;;
214
+ section_venv) echo "Configurando venv Python (~/.rag_venv)" ;;
215
+ deps_ok) echo "Dependências já instaladas. Pulando." ;;
216
+ deps_reinstall) echo "Instalando/atualizando dependências..." ;;
217
+ creating_venv) echo "Criando venv em" ;;
218
+ venv_created) echo "Venv criado." ;;
219
+ upgrading_pip) echo "Atualizando pip..." ;;
220
+ deps_installed) echo "Dependências instaladas." ;;
221
+ section_chroma) echo "Configurando ChromaDB (Docker Desktop)" ;;
222
+ compose_installed) echo "docker-compose.yml instalado em" ;;
223
+ compose_keep) echo "docker-compose.yml já existe. Mantendo arquivo atual." ;;
224
+ chroma_port_prompt) echo "Porta do ChromaDB [1-65535] (padrão: 8000): " ;;
225
+ chroma_port_selected) echo "Porta escolhida do ChromaDB:" ;;
226
+ chroma_port_invalid) echo "Porta inválida. Usando padrão 8000." ;;
227
+ chroma_port_in_use) echo "Porta já está em uso:" ;;
228
+ chroma_port_try_another) echo "Escolha outra porta." ;;
229
+ chroma_port_auto_fallback) echo "Modo não interativo: usando próxima porta livre:" ;;
230
+ chroma_port_no_free) echo "Não foi possível encontrar porta TCP livre para o ChromaDB." ;;
231
+ chroma_running) echo "Container chromadb-rag já está rodando." ;;
232
+ chroma_start) echo "Iniciando ChromaDB..." ;;
233
+ chroma_wait) echo "Aguardando ChromaDB inicializar..." ;;
234
+ chroma_ready) echo "ChromaDB respondendo em" ;;
235
+ chroma_timeout) echo "ChromaDB não respondeu no tempo esperado. Verifique Docker Desktop e logs." ;;
236
+ section_install_mcp) echo "Instalando mcp-rag-server globalmente" ;;
237
+ mcp_keep) echo "mcp-rag-server já está atualizado. Mantendo." ;;
238
+ mcp_outdated) echo "mcp-rag-server está desatualizado." ;;
239
+ mcp_prompt_update) echo "Atualizar mcp-rag-server agora? $YES_NO_HINT " ;;
240
+ mcp_prompt_reinstall) echo "mcp-rag-server já atualizado. Reinstalar mesmo assim? $YES_NO_HINT " ;;
241
+ mcp_skip) echo "Mantendo mcp-rag-server atual." ;;
242
+ mcp_installed) echo "mcp-rag-server instalado" ;;
243
+ mod_hf_installed) echo "Módulo de download instalado" ;;
244
+ mod_ms_installed) echo "Módulo de provider opcional instalado" ;;
245
+ shebang_set) echo "Shebang" ;;
246
+ path_added) echo "Adicionado ~/.local/bin ao PATH em" ;;
247
+ section_mcp_cfg) echo "Configuração opcional de MCP (Claude/Cursor)" ;;
248
+ no_cfg_files) echo "Nenhum arquivo de config detectado para atualização automática do MCP." ;;
249
+ cfg_all_current) echo "MCP 'rag-codebase' já está atualizado nos arquivos detectados. Pulando." ;;
250
+ cfg_detected) echo "Arquivos de config pendentes de atualização" ;;
251
+ ask_apply_cfg) echo "Aplicar atualização do MCP 'rag-codebase' nesses arquivos? $YES_NO_HINT " ;;
252
+ noninteractive_cfg_skip) echo "Modo não interativo: pulando atualização automática de config MCP." ;;
253
+ cannot_update_cfg) echo "Não foi possível atualizar" ;;
254
+ already_updated) echo "MCP 'rag-codebase' já atualizado. Ignorando." ;;
255
+ updated_cfg) echo "MCP 'rag-codebase' atualizado para versão" ;;
256
+ replaced_cfg) echo "Chave RAG antiga substituída por 'rag-codebase' (versão" ;;
257
+ added_cfg) echo "MCP 'rag-codebase' adicionado (versão" ;;
258
+ setup_done) echo "Setup concluído!" ;;
259
+ next) echo "Próximos" ;;
260
+ next_1) echo "Reinicie o Claude Code CLI" ;;
261
+ next_2) echo "Use semantic_search_code" ;;
262
+ next_3) echo "Reindexar: ./rag-setup-macos.run --only-index" ;;
263
+ restart_tools) echo "Reinicie as ferramentas que usam seu MCP rag." ;;
264
+ hf_detected) echo "Token HF detectado no ambiente." ;;
265
+ hf_noninteractive) echo "Modo não interativo: seguindo sem prompt de token HF." ;;
266
+ hf_title) echo "Token Hugging Face (opcional)" ;;
267
+ hf_desc) echo "Acelera download de modelos e limites de taxa." ;;
268
+ hf_prompt_now) echo "Informar HF token agora? $YES_NO_HINT " ;;
269
+ hf_prompt_paste) echo "Cole o HF_TOKEN (oculto): " ;;
270
+ hf_set) echo "HF token definido para este run." ;;
271
+ hf_empty) echo "Token vazio. Seguindo sem autenticação." ;;
272
+ hf_skip) echo "Seguindo sem HF token." ;;
273
+ invalid_option) echo "Opção inválida. Use respostas permitidas." ;;
274
+ *) echo "$key" ;;
275
+ esac
276
+ fi
277
+ }
278
+
279
+ log_info() { echo -e "${GREEN}[+]${NC} $*"; }
280
+ log_warn() { echo -e "${YELLOW}[!]${NC} $*"; }
281
+ log_error() { echo -e "${RED}[$(t err_prefix)]${NC} $*" >&2; }
282
+
283
+ is_yes_answer() {
284
+ local ans="$(echo "${1:-}" | tr '[:upper:]' '[:lower:]')"
285
+ if [[ "$UI_LANG" == "en-us" ]]; then
286
+ [[ "$ans" == "y" || "$ans" == "yes" ]]
287
+ else
288
+ [[ "$ans" == "s" || "$ans" == "sim" || "$ans" == "y" || "$ans" == "yes" ]]
289
+ fi
290
+ }
291
+
292
+ is_no_answer() {
293
+ local ans="$(echo "${1:-}" | tr '[:upper:]' '[:lower:]')"
294
+ if [[ -z "$ans" ]]; then
295
+ return 0
296
+ fi
297
+ if [[ "$UI_LANG" == "en-us" ]]; then
298
+ [[ "$ans" == "n" || "$ans" == "no" ]]
299
+ else
300
+ [[ "$ans" == "n" || "$ans" == "nao" || "$ans" == "não" || "$ans" == "no" ]]
301
+ fi
302
+ }
303
+
304
+ ask_yes_no_loop() {
305
+ local prompt="$1"
306
+ local answer=""
307
+ while true; do
308
+ read -r -p "$prompt" answer
309
+ if is_yes_answer "$answer"; then
310
+ return 0
311
+ fi
312
+ if is_no_answer "$answer"; then
313
+ return 1
314
+ fi
315
+ log_warn "$(t invalid_option)"
316
+ done
317
+ }
318
+
319
+ normalize_port() {
320
+ local raw_port="${1:-}"
321
+ if [[ "${raw_port}" =~ ^[0-9]+$ ]] && (( raw_port >= 1 && raw_port <= 65535 )); then
322
+ echo "${raw_port}"
323
+ return 0
324
+ fi
325
+ return 1
326
+ }
327
+
328
+ extract_chroma_port_from_compose() {
329
+ local compose_file="$1"
330
+ local parsed_port=""
331
+
332
+ if [[ ! -f "${compose_file}" ]]; then
333
+ return 1
334
+ fi
335
+
336
+ parsed_port="$(sed -n 's/^[[:space:]]*-[[:space:]]*"\?\([0-9]\{1,5\}\):8000"\?.*/\1/p' "${compose_file}" | head -n 1)"
337
+ if normalize_port "${parsed_port}" >/dev/null; then
338
+ echo "${parsed_port}"
339
+ return 0
340
+ fi
341
+ return 1
342
+ }
343
+
344
+ is_port_in_use() {
345
+ local port="$1"
346
+ local os_name
347
+ os_name="$(uname -s 2>/dev/null || echo Darwin)"
348
+
349
+ if [[ "${os_name}" == "Darwin" ]]; then
350
+ if command -v lsof >/dev/null 2>&1; then
351
+ lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1
352
+ return $?
353
+ fi
354
+ if command -v netstat >/dev/null 2>&1; then
355
+ netstat -an -p tcp 2>/dev/null | grep -E "[\\.:]${port}[[:space:]].*LISTEN" >/dev/null 2>&1
356
+ return $?
357
+ fi
358
+ else
359
+ if command -v ss >/dev/null 2>&1; then
360
+ ss -ltnH "( sport = :${port} )" 2>/dev/null | grep -q .
361
+ return $?
362
+ fi
363
+ if command -v lsof >/dev/null 2>&1; then
364
+ lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1
365
+ return $?
366
+ fi
367
+ if command -v netstat >/dev/null 2>&1; then
368
+ netstat -ltn 2>/dev/null | awk '{print $4}' | grep -E "(^|:)${port}$" >/dev/null 2>&1
369
+ return $?
370
+ fi
371
+ fi
372
+
373
+ return 1
374
+ }
375
+
376
+ find_next_free_port() {
377
+ local start_port="$1"
378
+ local port=0
379
+ for ((port=start_port; port<=65535; port++)); do
380
+ if ! is_port_in_use "${port}"; then
381
+ echo "${port}"
382
+ return 0
383
+ fi
384
+ done
385
+ return 1
386
+ }
387
+
388
+ choose_chroma_port_for_install() {
389
+ local default_port="$1"
390
+ local preferred_port="${2:-}"
391
+ local chosen_port=""
392
+ local normalized=""
393
+ local fallback_port=""
394
+
395
+ chosen_port="${preferred_port}"
396
+
397
+ while true; do
398
+ if [[ -z "${chosen_port}" ]]; then
399
+ if [[ ! -t 0 ]]; then
400
+ chosen_port="${default_port}"
401
+ else
402
+ read -r -p "$(t chroma_port_prompt)" chosen_port
403
+ if [[ -z "${chosen_port}" ]]; then
404
+ chosen_port="${default_port}"
405
+ fi
406
+ fi
407
+ fi
408
+
409
+ if ! normalized="$(normalize_port "${chosen_port}")"; then
410
+ log_warn "$(t chroma_port_invalid)"
411
+ if [[ -t 0 ]]; then
412
+ chosen_port=""
413
+ continue
414
+ fi
415
+ normalized="${default_port}"
416
+ fi
417
+
418
+ if is_port_in_use "${normalized}"; then
419
+ log_warn "$(t chroma_port_in_use) ${normalized}. $(t chroma_port_try_another)"
420
+ if [[ -t 0 ]]; then
421
+ chosen_port=""
422
+ continue
423
+ fi
424
+ if fallback_port="$(find_next_free_port "$((normalized + 1))")"; then
425
+ log_warn "$(t chroma_port_auto_fallback) ${fallback_port}"
426
+ echo "${fallback_port}"
427
+ return 0
428
+ fi
429
+ log_error "$(t chroma_port_no_free)"
430
+ exit 1
431
+ fi
432
+
433
+ echo "${normalized}"
434
+ return 0
435
+ done
436
+ }
437
+
438
+ decode_to_file() {
439
+ local payload="$1"
440
+ local destination="$2"
441
+ printf '%s' "$payload" | python3 - "$destination" <<'PYEOF'
442
+ import base64
443
+ import pathlib
444
+ import sys
445
+ out = pathlib.Path(sys.argv[1])
446
+ out.write_bytes(base64.b64decode(sys.stdin.read().encode("ascii")))
447
+ PYEOF
448
+ }
449
+
450
+ replace_shebang() {
451
+ local file="$1"
452
+ local new_shebang="$2"
453
+ local tmp_file
454
+ tmp_file="$(mktemp "${TMPDIR:-/tmp}/rag-shebang.XXXXXX")"
455
+ {
456
+ printf '%s\n' "#!${new_shebang}"
457
+ tail -n +2 "$file"
458
+ } > "$tmp_file"
459
+ mv "$tmp_file" "$file"
460
+ }
461
+
462
+ detect_current_mcp_version() {
463
+ python3 - "${HOME}" <<'PYEOF'
464
+ import json
465
+ import re
466
+ import sys
467
+ from pathlib import Path
468
+
469
+ home = Path(sys.argv[1]).expanduser()
470
+ paths = [
471
+ home / ".claude.json",
472
+ home / ".cursor" / "mcp.json",
473
+ home / "Library" / "Application Support" / "Cursor" / "User" / "mcp.json",
474
+ home / ".config" / "Cursor" / "User" / "mcp.json",
475
+ ]
476
+ version_re = re.compile(r"^(\d+)\.(\d+)$")
477
+ best = None
478
+
479
+ for p in paths:
480
+ if not p.exists():
481
+ continue
482
+ try:
483
+ data = json.loads(p.read_text(encoding="utf-8"))
484
+ except Exception:
485
+ continue
486
+ if not isinstance(data, dict):
487
+ continue
488
+ servers = data.get("mcpServers")
489
+ if not isinstance(servers, dict):
490
+ continue
491
+ cfg = servers.get("rag-codebase")
492
+ if not isinstance(cfg, dict):
493
+ continue
494
+ version = cfg.get("version")
495
+ if not isinstance(version, str):
496
+ continue
497
+ m = version_re.match(version.strip())
498
+ if not m:
499
+ continue
500
+ parsed = (int(m.group(1)), int(m.group(2)))
501
+ if best is None or parsed > best:
502
+ best = parsed
503
+
504
+ if best is None:
505
+ print("1.0")
506
+ else:
507
+ print(f"{best[0]}.{best[1]}")
508
+ PYEOF
509
+ }
510
+
511
+ select_ui_language
512
+
513
+ USER_HOME="${HOME}"
514
+ VENV_DIR="${USER_HOME}/.rag_venv"
515
+ VENV_PYTHON="${VENV_DIR}/bin/python3"
516
+ VENV_PIP="${VENV_DIR}/bin/pip"
517
+ DOCKER_COMPOSE_DIR="${USER_HOME}/docker-chromadb"
518
+ RAG_DB_DIR="${USER_HOME}/.rag_db"
519
+ CHROMA_HOST="localhost"
520
+ CHROMA_PORT_DEFAULT="8000"
521
+ CHROMA_PORT_FROM_ENV=false
522
+ if CHROMA_PORT_NORMALIZED="$(normalize_port "${MCP_CHROMA_PORT:-}")"; then
523
+ CHROMA_PORT="${CHROMA_PORT_NORMALIZED}"
524
+ CHROMA_PORT_FROM_ENV=true
525
+ else
526
+ CHROMA_PORT="${CHROMA_PORT_DEFAULT}"
527
+ fi
528
+ BIN_DIR="${USER_HOME}/.local/bin"
529
+ MCP_SERVER_DEST="${BIN_DIR}/mcp-rag-server"
530
+ MODEL_DL_HF_DEST="${BIN_DIR}/download_model_from_hugginface.py"
531
+ MODEL_DL_MS_DEST="${BIN_DIR}/download_model_from_modelscope.py"
532
+ MODEL_CACHE_DIR="${USER_HOME}/.cache/my-custom-rag-python/models"
533
+ EXTRACT_DIR="$(mktemp -d "${TMPDIR:-/tmp}/rag-setup-macos.XXXXXX")"
534
+ DOCKER_COMPOSE_FILE_PATH="${DOCKER_COMPOSE_DIR}/docker-compose.yml"
535
+
536
+ if [[ -n "$CUSTOM_PROJECT_DIR" ]]; then
537
+ PROJECT_DIR="$(cd "$CUSTOM_PROJECT_DIR" && pwd)"
538
+ else
539
+ PROJECT_DIR="$(pwd)"
540
+ fi
541
+
542
+ trap 'rm -rf "$EXTRACT_DIR"' EXIT
543
+
544
+ echo ""
545
+ echo -e "${BOLD}${BLUE}================================================================${NC}"
546
+ echo -e "${BOLD}${BLUE} $(t title)${NC}"
547
+ echo -e "${BOLD}${BLUE}================================================================${NC}"
548
+ echo -e " $(t project): ${BOLD}${PROJECT_DIR}${NC}"
549
+ echo ""
550
+
551
+ if [[ "$(uname -s)" != "Darwin" ]]; then
552
+ log_error "$(t platform_only)"
553
+ exit 1
554
+ fi
555
+
556
+ # ---------------------------------------------------------------------------
557
+ # Extração
558
+ # ---------------------------------------------------------------------------
559
+ log_info "$(t extracting)"
560
+
561
+ B64_COMPOSE="c2VydmljZXM6CiAgY2hyb21hZGI6CiAgICBpbWFnZTogY2hyb21hZGIvY2hyb21hOmxhdGVzdAogICAgY29udGFpbmVyX25hbWU6IGNocm9tYWRiLXJhZwogICAgcG9ydHM6CiAgICAgIC0gIjgwMDA6ODAwMCIKICAgIHZvbHVtZXM6CiAgICAgICMgUGVyc2lzdGUgbyBiYW5jbyBkaXJldGFtZW50ZSBuYSBwYXN0YSBkbyB1c3XDoXJpbyBubyBob3N0CiAgICAgIC0gJHtIT01FfS8ucmFnX2RiOi9jaHJvbWEvY2hyb21hCiAgICBlbnZpcm9ubWVudDoKICAgICAgIyBIYWJpbGl0YSBhdXRlbnRpY2HDp8OjbyBhbsO0bmltYSAoc2VtIHRva2VuKSBwYXJhIHVzbyBsb2NhbAogICAgICAtIEFOT05ZTUlaRURfVEVMRU1FVFJZPWZhbHNlCiAgICAgIC0gQ0hST01BX1NFUlZFUl9BVVRITl9DUkVERU5USUFMU19GSUxFPSIiCiAgICAgIC0gQ0hST01BX1NFUlZFUl9BVVRITl9QUk9WSURFUj0iIgogICAgcmVzdGFydDogYWx3YXlzCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQiLCAiY3VybCIsICItZiIsICJodHRwOi8vbG9jYWxob3N0OjgwMDAvYXBpL3YxL2hlYXJ0YmVhdCJdCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMK"
562
+ B64_REQUIREMENTS="IyBDbGllbnRlIEhUVFAgZG8gQ2hyb21hREIgKGNvbmVjdGEgYW8gc2Vydmlkb3IgRG9ja2VyKQpjaHJvbWFkYj49MC41LjAKCiMgTW9kZWxvIGRlIGVtYmVkZGluZ3MgbG9jYWwgcm9kYW5kbyBuYSBDUFUKc2VudGVuY2UtdHJhbnNmb3JtZXJzPj0zLjAuMAojIERlcGVuZMOqbmNpYSB1c2FkYSBwb3IgbW9kZWxvcyBKaW5hIHYzIGNvbSBjw7NkaWdvIHJlbW90bwplaW5vcHM+PTAuNy4wCiMgRXZpdGEgaW5jb21wYXRpYmlsaWRhZGVzIGNvbmhlY2lkYXMgZG8gSmluYSB2MyBjb20gQVBJcyBub3ZhcyBkbyB0cmFuc2Zvcm1lcnMgNS54CnRyYW5zZm9ybWVyczw1CgojIFNwbGl0dGVyIGRlIHRleHRvIHBhcmEgY2h1bmtpbmcgaW50ZWxpZ2VudGUKbGFuZ2NoYWluLXRleHQtc3BsaXR0ZXJzPj0wLjIuMAoKIyBCYXJyYSBkZSBwcm9ncmVzc28gdmlzdWFsIG5vIHRlcm1pbmFsCnRxZG0+PTQuNjYuMAoKIyBNb25pdG9yIGRlIG1lbcOzcmlhIGVtIHRlbXBvIHJlYWwgZHVyYW50ZSBpbmRleGHDp8Ojbwpwc3V0aWw+PTUuOS4wCgojIFNESyBvZmljaWFsIGRvIE1DUCAoTW9kZWwgQ29udGV4dCBQcm90b2NvbCkgZGEgQW50aHJvcGljCm1jcD49MS4wLjAK"
563
+ B64_INDEXER="IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoiIiIKaW5kZXhlcl9mdWxsLnB5IOKAlCBTY3JpcHQgc3RhbmRhbG9uZSBkZSBpbmRleGHDp8OjbyBkbyBSQUcgbG9jYWwuCgpVc286CiAgICBweXRob24gaW5kZXhlcl9mdWxsLnB5IFtjYW1pbmhvX2RvX3Byb2pldG9dCgpTZSBuZW5odW0gY2FtaW5obyBmb3IgcGFzc2FkbywgdXNhIG8gZGlyZXTDs3JpbyBhdHVhbC4KTyBDaHJvbWFEQiBkZXZlIGVzdGFyIHJvZGFuZG8gdmlhIERvY2tlciBlbSBsb2NhbGhvc3Q6ODAwMC4KIiIiCgppbXBvcnQgb3MKaW1wb3J0IHN5cwppbXBvcnQgaGFzaGxpYgppbXBvcnQgYXJncGFyc2UKaW1wb3J0IHNodXRpbAppbXBvcnQgbG9nZ2luZwppbXBvcnQgZ2MKaW1wb3J0IGpzb24KZnJvbSB0aW1lIGltcG9ydCBwZXJmX2NvdW50ZXIsIHRpbWUKZnJvbSBjb2xsZWN0aW9ucy5hYmMgaW1wb3J0IEl0ZXJhdG9yCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aApmcm9tIGRhdGFjbGFzc2VzIGltcG9ydCBkYXRhY2xhc3MKZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUKCiMgRXZpdGEgYXZpc29zICJhZHZpc29yeSIgcnVpZG9zb3MgZG8gdHJhbnNmb3JtZXJzIG5vIGZsdXhvIGludGVyYXRpdm8uCm9zLmVudmlyb24uc2V0ZGVmYXVsdCgiVFJBTlNGT1JNRVJTX05PX0FEVklTT1JZX1dBUk5JTkdTIiwgIjEiKQoKCmNsYXNzIF9Ub3JjaER0eXBlV2FybmluZ0ZpbHRlcihsb2dnaW5nLkZpbHRlcik6CiAgICBkZWYgZmlsdGVyKHNlbGYsIHJlY29yZDogbG9nZ2luZy5Mb2dSZWNvcmQpIC0+IGJvb2w6CiAgICAgICAgcmV0dXJuICJgdG9yY2hfZHR5cGVgIGlzIGRlcHJlY2F0ZWQhIFVzZSBgZHR5cGVgIGluc3RlYWQhIiBub3QgaW4gcmVjb3JkLmdldE1lc3NhZ2UoKQoKCmZvciBfbG9nZ2VyX25hbWUgaW4gKCJ0cmFuc2Zvcm1lcnMuY29uZmlndXJhdGlvbl91dGlscyIsICJ0cmFuc2Zvcm1lcnMubW9kZWxpbmdfdXRpbHMiKToKICAgIGxvZ2dpbmcuZ2V0TG9nZ2VyKF9sb2dnZXJfbmFtZSkuYWRkRmlsdGVyKF9Ub3JjaER0eXBlV2FybmluZ0ZpbHRlcigpKQoKaW1wb3J0IGNocm9tYWRiCmZyb20gc2VudGVuY2VfdHJhbnNmb3JtZXJzIGltcG9ydCBTZW50ZW5jZVRyYW5zZm9ybWVyCmZyb20gbGFuZ2NoYWluX3RleHRfc3BsaXR0ZXJzIGltcG9ydCBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIKZnJvbSB0cWRtIGltcG9ydCB0cWRtCmZyb20gZG93bmxvYWRfbW9kZWxfZnJvbV9odWdnaW5mYWNlIGltcG9ydCBkb3dubG9hZF9tb2RlbF93aXRoX2ZhbGxiYWNrCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENvbmZpZ3VyYcOnw7VlcyBnbG9iYWlzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKZGVmIF9lbnZfaW50KG5hbWU6IHN0ciwgZGVmYXVsdDogaW50LCAqLCBtaW5fdmFsdWU6IGludCA9IDEpIC0+IGludDoKICAgIHJhdyA9IG9zLmVudmlyb24uZ2V0KG5hbWUpCiAgICBpZiByYXcgaXMgTm9uZToKICAgICAgICByZXR1cm4gbWF4KG1pbl92YWx1ZSwgZGVmYXVsdCkKICAgIHRyeToKICAgICAgICByZXR1cm4gbWF4KG1pbl92YWx1ZSwgaW50KHJhdykpCiAgICBleGNlcHQgVmFsdWVFcnJvcjoKICAgICAgICByZXR1cm4gbWF4KG1pbl92YWx1ZSwgZGVmYXVsdCkKCkNIUk9NQV9IT1NUID0gImxvY2FsaG9zdCIKQ0hST01BX1BPUlQgPSBfZW52X2ludCgiTUNQX0NIUk9NQV9QT1JUIiwgODAwMCwgbWluX3ZhbHVlPTEpCkNPTExFQ1RJT05fQ09ERV9KSU5BID0gImNvZGVfdmVjdG9yc19qaW5hIgpDT0xMRUNUSU9OX0RPQ19CR0UgPSAiZG9jX3ZlY3RvcnNfYmdlIgoKIyBQYXN0YXMgZSBleHRlbnPDtWVzIGlnbm9yYWRhcyBkdXJhbnRlIGEgdmFycmVkdXJhCklHTk9SRURfRElSUyA9IHsKICAgICIuZ2l0IiwgIm5vZGVfbW9kdWxlcyIsICJfX3B5Y2FjaGVfXyIsICIudmVudiIsICJ2ZW52IiwgImVudiIsCiAgICAiZGlzdCIsICJidWlsZCIsICJvdXQiLCAiLm5leHQiLCAiLm51eHQiLCAiLmNhY2hlIiwgImNvdmVyYWdlIiwKICAgICIucHl0ZXN0X2NhY2hlIiwgIi5teXB5X2NhY2hlIiwgIi5ydWZmX2NhY2hlIiwgInRhcmdldCIsICJiaW4iLCAib2JqIiwKICAgICIuaWRlYSIsICIudnNjb2RlIiwgIi5EU19TdG9yZSIsICJ2ZW5kb3IiLCAidG1wIiwgInRlbXAiLCAibG9ncyIsCiAgICAiLnJhZ19kYiIsCn0KCklHTk9SRURfRVhURU5TSU9OUyA9IHsKICAgICMgQmluw6FyaW9zIGUgaW1hZ2VucwogICAgIi5wbmciLCAiLmpwZyIsICIuanBlZyIsICIuZ2lmIiwgIi5zdmciLCAiLmljbyIsICIud2VicCIsICIuYm1wIiwKICAgICIubXA0IiwgIi5tcDMiLCAiLndhdiIsICIub2dnIiwgIi5hdmkiLCAiLm1vdiIsCiAgICAjIFBhY290ZXMgZSBjb21waWxhZG9zCiAgICAiLnppcCIsICIudGFyIiwgIi5neiIsICIucmFyIiwgIi43eiIsICIuamFyIiwgIi53YXIiLCAiLmVhciIsCiAgICAiLnB5YyIsICIucHlvIiwgIi5zbyIsICIuZGxsIiwgIi5leGUiLCAiLmJpbiIsCiAgICAjIExvY2tmaWxlcyBlIGdlcmFkb3MKICAgICIubG9jayIsICIuc3VtIiwKICAgICMgQmFuY28gZGUgZGFkb3MKICAgICIuc3FsaXRlIiwgIi5kYiIsICIuc3FsaXRlMyIsCiAgICAjIEZvbnRlcwogICAgIi50dGYiLCAiLndvZmYiLCAiLndvZmYyIiwgIi5lb3QiLAogICAgIyBQREYvRG9jdW1lbnRvcyBiaW7DoXJpb3MKICAgICIucGRmIiwgIi5kb2N4IiwgIi54bHN4IiwgIi5wcHR4IiwKfQoKQ09ERV9FWFRFTlNJT05TID0gewogICAgIi5weSIsICIuanMiLCAiLnRzIiwgIi50c3giLCAiLmpzeCIsICIuamF2YSIsICIuYyIsICIuaCIsICIuY3BwIiwgIi5ocHAiLAogICAgIi5nbyIsICIucnMiLCAiLnJiIiwgIi5waHAiLCAiLmNzIiwgIi5zd2lmdCIsICIua3QiLCAiLmt0cyIsICIuc2NhbGEiLCAiLnNxbCIsCiAgICAiLnNoIiwgIi5iYXNoIiwgIi56c2giLCAiLnBzMSIsICIueWFtbCIsICIueW1sIiwgIi50b21sIiwgIi5pbmkiLCAiLmNvbmYiLAogICAgIi5qc29uIiwgIi54bWwiLCAiLmh0bWwiLCAiLmNzcyIsICIuc2NzcyIsICIuc2FzcyIsICIudnVlIiwgIi5zdmVsdGUiLCAiLmRhcnQiLAogICAgIi5sdWEiLCAiLnIiLCAiLm0iLCAiLm1tIiwKfQoKRE9DX0VYVEVOU0lPTlMgPSB7CiAgICAiLm1kIiwgIi5tZHgiLCAiLnJzdCIsICIudHh0IiwgIi5hZG9jIiwgIi5vcmciLCAiLnRleCIsICIuY3N2IiwKfQoKIyBUYW1hbmhvIG3DoXhpbW8gZGUgYXJxdWl2byAoZXZpdGEgaW5kZXhhciBhcnF1aXZvcyBlbm9ybWVzIGdlcmFkb3MpCk1BWF9GSUxFX1NJWkVfQllURVMgPSA1MDAgKiAxMDI0ICAjIDUwMCBLQgoKIyBQYXLDom1ldHJvcyBkbyBzcGxpdHRlciBlIGJhdGNoIChwZXJmaWwgbG93LW1lbW9yeSBwb3IgcGFkcsOjbykuCkNIVU5LX1NJWkUgPSBfZW52X2ludCgiTUNQX0NIVU5LX1NJWkUiLCAzMDAwLCBtaW5fdmFsdWU9MjU2KQpDSFVOS19PVkVSTEFQID0gbWluKENIVU5LX1NJWkUgLSAxLCBfZW52X2ludCgiTUNQX0NIVU5LX09WRVJMQVAiLCA0MDAsIG1pbl92YWx1ZT0wKSkKRU1CRURESU5HX0JBVENIX1NJWkUgPSBfZW52X2ludCgiTUNQX0VNQkVERElOR19CQVRDSF9TSVpFIiwgNCwgbWluX3ZhbHVlPTEpCkRFRkFVTFRfUEVSRl9QUk9GSUxFID0gImF1dG90dW5lIgpJTkRFWEVSX0NPTkZJR19QQVRIID0gUGF0aCgKICAgIG9zLmVudmlyb24uZ2V0KCJNQ1BfSU5ERVhFUl9DT05GSUdfRklMRSIsIHN0cihQYXRoLmhvbWUoKSAvICIucmFnX2RiIiAvICJpbmRleGVyX3R1bmluZy5qc29uIikpCikuZXhwYW5kdXNlcigpCklOREVYRVJfQ09ORklHX0ZBTExCQUNLX1BBVEggPSBQYXRoLmhvbWUoKSAvICIuY2FjaGUiIC8gIm15LWN1c3RvbS1yYWctcHl0aG9uIiAvICJpbmRleGVyX3R1bmluZy5qc29uIgoKIyBNb2RlbG8gZGUgZW1iZWRkaW5ncyAocm9kYSBuYSBDUFUpCkpJTkFfVjNfRU1CRURESU5HX01PREVMID0gImppbmFhaS9qaW5hLWVtYmVkZGluZ3MtdjMiCkpJTkFfVjJfRU1CRURESU5HX01PREVMID0gImppbmFhaS9qaW5hLWVtYmVkZGluZ3MtdjItYmFzZS1jb2RlIgpCR0VfRU1CRURESU5HX01PREVMID0gIkJBQUkvYmdlLW0zIgpERUZBVUxUX0VNQkVERElOR19NT0RFTF9DSE9JQ0UgPSAiamluYSIKREVGQVVMVF9KSU5BX1FVQU5USVpBVElPTiA9ICJkeW5hbWljLWludDgiCk1PREVMX0NBQ0hFX0JBU0VfRElSID0gUGF0aCgKICAgIG9zLmVudmlyb24uZ2V0KCJNQ1BfTU9ERUxfRElSIiwgc3RyKFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAibXktY3VzdG9tLXJhZy1weXRob24iIC8gIm1vZGVscyIpKQopLmV4cGFuZHVzZXIoKQpKSU5BX1JFQ09NTUVOREVEX1JBTV9HQl9ERUZBVUxUID0gNjQKSklOQV9SRUNPTU1FTkRFRF9SQU1fR0JfRFlOQU1JQ19JTlQ4ID0gNDgKSklOQV9SRUNPTU1FTkRFRF9TV0FQX0dCID0gMTYKSklOQV9NSU5fQVZBSUxBQkxFX1JBTV9HQl9ISU5UID0gMTIKCgpkZWYgX2Vudl9ib29sKG5hbWU6IHN0ciwgZGVmYXVsdDogYm9vbCA9IEZhbHNlKSAtPiBib29sOgogICAgcmF3ID0gb3MuZW52aXJvbi5nZXQobmFtZSkKICAgIGlmIHJhdyBpcyBOb25lOgogICAgICAgIHJldHVybiBkZWZhdWx0CiAgICByZXR1cm4gcmF3LnN0cmlwKCkubG93ZXIoKSBpbiB7IjEiLCAidHJ1ZSIsICJ5ZXMiLCAib24ifQoKCmRlZiBfY2xhbXAodmFsdWU6IGZsb2F0LCBsb3c6IGZsb2F0LCBoaWdoOiBmbG9hdCkgLT4gZmxvYXQ6CiAgICByZXR1cm4gbWF4KGxvdywgbWluKGhpZ2gsIHZhbHVlKSkKCgpkZWYgX2lzX21lbW9yeV9yZWxhdGVkX2Vycm9yKGV4YzogRXhjZXB0aW9uKSAtPiBib29sOgogICAgaWYgaXNpbnN0YW5jZShleGMsIE1lbW9yeUVycm9yKToKICAgICAgICByZXR1cm4gVHJ1ZQogICAgbXNnID0gc3RyKGV4YykubG93ZXIoKQogICAgbWVtb3J5X21hcmtlcnMgPSAoCiAgICAgICAgIm91dCBvZiBtZW1vcnkiLAogICAgICAgICJvb20iLAogICAgICAgICJjYW5ub3QgYWxsb2NhdGUgbWVtb3J5IiwKICAgICAgICAic3RkOjpiYWRfYWxsb2MiLAogICAgICAgICJiYWQgYWxsb2MiLAogICAgICAgICJpbnN1ZmZpY2llbnQgbWVtb3J5IiwKICAgICkKICAgIHJldHVybiBhbnkobWFya2VyIGluIG1zZyBmb3IgbWFya2VyIGluIG1lbW9yeV9tYXJrZXJzKQoKCmRlZiBfaXNfZGltZW5zaW9uX21pc21hdGNoX2Vycm9yKGV4YzogRXhjZXB0aW9uKSAtPiBib29sOgogICAgbXNnID0gc3RyKGV4YykubG93ZXIoKQogICAgcmV0dXJuICgKICAgICAgICAiZXhwZWN0aW5nIGVtYmVkZGluZyB3aXRoIGRpbWVuc2lvbiIgaW4gbXNnCiAgICAgICAgb3IgKCJlbWJlZGRpbmciIGluIG1zZyBhbmQgImRpbWVuc2lvbiIgaW4gbXNnIGFuZCAiZ290IiBpbiBtc2cpCiAgICApCgoKZGVmIF9mb3JtYXRfZXhjZXB0aW9uKGV4YzogRXhjZXB0aW9uKSAtPiBzdHI6CiAgICBtZXNzYWdlID0gc3RyKGV4Yykuc3RyaXAoKQogICAgaWYgbWVzc2FnZToKICAgICAgICByZXR1cm4gbWVzc2FnZQogICAgcmV0dXJuIHJlcHIoZXhjKQoKCkBkYXRhY2xhc3MoZnJvemVuPVRydWUpCmNsYXNzIEluZGV4VGFyZ2V0OgogICAgbW9kZWxfY2hvaWNlOiBzdHIKICAgIGNvbGxlY3Rpb25fbmFtZTogc3RyCiAgICBsYWJlbDogc3RyCgoKZGVmIF9yZXNvbHZlX21vZGVsX2lkKG1vZGVsX2Nob2ljZTogc3RyKSAtPiBzdHI6CiAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImppbmEiOgogICAgICAgIHJldHVybiBKSU5BX1YzX0VNQkVERElOR19NT0RFTAogICAgaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hLXYyIjoKICAgICAgICByZXR1cm4gSklOQV9WMl9FTUJFRERJTkdfTU9ERUwKICAgIGlmIG1vZGVsX2Nob2ljZSA9PSAiYmdlIjoKICAgICAgICByZXR1cm4gQkdFX0VNQkVERElOR19NT0RFTAogICAgcmFpc2UgVmFsdWVFcnJvcihmIk1vZGVsbyBuw6NvIHN1cG9ydGFkbzoge21vZGVsX2Nob2ljZX0iKQoKCmRlZiBfcmVzb2x2ZV9mYWxsYmFja19tb2RlbF9pZChtb2RlbF9jaG9pY2U6IHN0cikgLT4gc3RyOgogICAgcmV0dXJuIEJHRV9FTUJFRERJTkdfTU9ERUwKCgpkZWYgX2Rlc2NyaWJlX2VtYmVkZGluZ19jaG9pY2UobW9kZWxfY2hvaWNlOiBzdHIpIC0+IHN0cjoKICAgIGlmIG1vZGVsX2Nob2ljZSA9PSAiamluYSI6CiAgICAgICAgcmV0dXJuIGYiamluYSAoe0pJTkFfVjNfRU1CRURESU5HX01PREVMfSkiCiAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImJnZSI6CiAgICAgICAgcmV0dXJuIGYiYmdlICh7QkdFX0VNQkVERElOR19NT0RFTH0pIgogICAgaWYgbW9kZWxfY2hvaWNlID09ICJoeWJyaWQiOgogICAgICAgIHJldHVybiBmImh5YnJpZCAoe0pJTkFfVjJfRU1CRURESU5HX01PREVMfSArIHtCR0VfRU1CRURESU5HX01PREVMfSkiCiAgICByZXR1cm4gbW9kZWxfY2hvaWNlCgoKZGVmIF9yZXNvbHZlX2luZGV4X3RhcmdldHMobW9kZWxfY2hvaWNlOiBzdHIpIC0+IGxpc3RbSW5kZXhUYXJnZXRdOgogICAgaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hIjoKICAgICAgICByZXR1cm4gWwogICAgICAgICAgICBJbmRleFRhcmdldCgKICAgICAgICAgICAgICAgIG1vZGVsX2Nob2ljZT0iamluYSIsCiAgICAgICAgICAgICAgICBjb2xsZWN0aW9uX25hbWU9Q09MTEVDVElPTl9DT0RFX0pJTkEsCiAgICAgICAgICAgICAgICBsYWJlbD0iQ29kZS9KaW5hIiwKICAgICAgICAgICAgKQogICAgICAgIF0KICAgIGlmIG1vZGVsX2Nob2ljZSA9PSAiYmdlIjoKICAgICAgICByZXR1cm4gWwogICAgICAgICAgICBJbmRleFRhcmdldCgKICAgICAgICAgICAgICAgIG1vZGVsX2Nob2ljZT0iYmdlIiwKICAgICAgICAgICAgICAgIGNvbGxlY3Rpb25fbmFtZT1DT0xMRUNUSU9OX0RPQ19CR0UsCiAgICAgICAgICAgICAgICBsYWJlbD0iRG9jL0JHRSIsCiAgICAgICAgICAgICkKICAgICAgICBdCiAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImh5YnJpZCI6CiAgICAgICAgcmV0dXJuIFsKICAgICAgICAgICAgSW5kZXhUYXJnZXQoCiAgICAgICAgICAgICAgICBtb2RlbF9jaG9pY2U9ImppbmEtdjIiLAogICAgICAgICAgICAgICAgY29sbGVjdGlvbl9uYW1lPUNPTExFQ1RJT05fQ09ERV9KSU5BLAogICAgICAgICAgICAgICAgbGFiZWw9IkNvZGUvSmluYSB2MiIsCiAgICAgICAgICAgICksCiAgICAgICAgICAgIEluZGV4VGFyZ2V0KAogICAgICAgICAgICAgICAgbW9kZWxfY2hvaWNlPSJiZ2UiLAogICAgICAgICAgICAgICAgY29sbGVjdGlvbl9uYW1lPUNPTExFQ1RJT05fRE9DX0JHRSwKICAgICAgICAgICAgICAgIGxhYmVsPSJEb2MvQkdFIiwKICAgICAgICAgICAgKSwKICAgICAgICBdCiAgICByYWlzZSBWYWx1ZUVycm9yKGYiTW9kZWxvIG7Do28gc3Vwb3J0YWRvOiB7bW9kZWxfY2hvaWNlfSIpCgoKZGVmIF9jbGFzc2lmeV9maWxlX3RhcmdldHMoZmlsZXBhdGg6IFBhdGgsIG1vZGVsX2Nob2ljZTogc3RyKSAtPiBzZXRbc3RyXToKICAgIGlmIG1vZGVsX2Nob2ljZSAhPSAiaHlicmlkIjoKICAgICAgICByZXR1cm4ge21vZGVsX2Nob2ljZX0KCiAgICBzdWZmaXggPSBmaWxlcGF0aC5zdWZmaXgubG93ZXIoKQogICAgaXNfY29kZSA9IHN1ZmZpeCBpbiBDT0RFX0VYVEVOU0lPTlMKICAgIGlzX2RvYyA9IHN1ZmZpeCBpbiBET0NfRVhURU5TSU9OUwoKICAgIGlmIGlzX2NvZGUgYW5kIG5vdCBpc19kb2M6CiAgICAgICAgcmV0dXJuIHsiamluYS12MiJ9CiAgICBpZiBpc19kb2MgYW5kIG5vdCBpc19jb2RlOgogICAgICAgIHJldHVybiB7ImJnZSJ9CgogICAgIyBFeHRlbnPDo28gZGVzY29uaGVjaWRhL2FtYsOtZ3VhOiBpbmRleGEgbm9zIGRvaXMgcmFtb3MgcGFyYSBtYW50ZXIgcmVjYWxsLgogICAgcmV0dXJuIHsiamluYS12MiIsICJiZ2UifQoKCmRlZiBfbW9kZWxfY2FjaGVfZGlyKGJhc2VfZGlyOiBQYXRoLCBtb2RlbF9pZDogc3RyKSAtPiBQYXRoOgogICAgc2FmZV9uYW1lID0gbW9kZWxfaWQucmVwbGFjZSgiLyIsICJfXyIpLnJlcGxhY2UoIjoiLCAiXyIpCiAgICByZXR1cm4gYmFzZV9kaXIgLyBzYWZlX25hbWUKCgpkZWYgX3BpY2tfd2l0aF9wcm9tcHQoCiAgICAqLAogICAgY3VycmVudF92YWx1ZTogc3RyIHwgTm9uZSwKICAgIGRlZmF1bHRfdmFsdWU6IHN0ciwKICAgIHRpdGxlOiBzdHIsCiAgICBvcHRpb25zOiBsaXN0W3R1cGxlW3N0ciwgc3RyXV0sCikgLT4gc3RyOgogICAgaWYgY3VycmVudF92YWx1ZToKICAgICAgICByZXR1cm4gY3VycmVudF92YWx1ZQogICAgaWYgbm90IHN5cy5zdGRpbi5pc2F0dHkoKToKICAgICAgICByZXR1cm4gZGVmYXVsdF92YWx1ZQoKICAgIHByaW50KGYiXG5bQ09ORklHXSB7dGl0bGV9IikKICAgIGZvciBpbmRleCwgKF8sIGRlc2NyaXB0aW9uKSBpbiBlbnVtZXJhdGUob3B0aW9ucywgc3RhcnQ9MSk6CiAgICAgICAgcHJpbnQoZiIgIHtpbmRleH0pIHtkZXNjcmlwdGlvbn0iKQogICAgcHJpbnQoZiIgIEVudGVyID0gcGFkcsOjbyAoe2RlZmF1bHRfdmFsdWV9KSIpCgogICAgYW5zd2VyID0gaW5wdXQoIj4gRXNjb2xoYTogIikuc3RyaXAoKQogICAgaWYgbm90IGFuc3dlcjoKICAgICAgICByZXR1cm4gZGVmYXVsdF92YWx1ZQogICAgaWYgYW5zd2VyLmlzZGlnaXQoKToKICAgICAgICBpZHggPSBpbnQoYW5zd2VyKSAtIDEKICAgICAgICBpZiAwIDw9IGlkeCA8IGxlbihvcHRpb25zKToKICAgICAgICAgICAgcmV0dXJuIG9wdGlvbnNbaWR4XVswXQogICAgbG93ZXJlZCA9IGFuc3dlci5sb3dlcigpCiAgICB2YWxpZF9rZXlzID0ge2sgZm9yIGssIF8gaW4gb3B0aW9uc30KICAgIGlmIGxvd2VyZWQgaW4gdmFsaWRfa2V5czoKICAgICAgICByZXR1cm4gbG93ZXJlZAogICAgcHJpbnQoZiJbQVZJU09dIE9ww6fDo28gaW52w6FsaWRhICd7YW5zd2VyfScuIFVzYW5kbyBwYWRyw6NvOiB7ZGVmYXVsdF92YWx1ZX0iKQogICAgcmV0dXJuIGRlZmF1bHRfdmFsdWUKCgpkZWYgcmVzb2x2ZV9lbWJlZGRpbmdfY29uZmlnKAogICAgbW9kZWxfY2hvaWNlX2FyZzogc3RyIHwgTm9uZSwKICAgIGppbmFfcXVhbnRpemF0aW9uX2FyZzogc3RyIHwgTm9uZSwKICAgIHBlcnNpc3RlZF9jb25maWc6IGRpY3Rbc3RyLCBvYmplY3RdIHwgTm9uZSA9IE5vbmUsCikgLT4gdHVwbGVbc3RyLCBzdHJdOgogICAgcGVyc2lzdGVkX2NvbmZpZyA9IHBlcnNpc3RlZF9jb25maWcgb3Ige30KICAgIG1vZGVsX2Nob2ljZV9mcm9tX2NvbmZpZyA9IHBlcnNpc3RlZF9jb25maWcuZ2V0KCJlbWJlZGRpbmdfbW9kZWwiKQogICAgbW9kZWxfY2hvaWNlID0gbW9kZWxfY2hvaWNlX2FyZyBvciBvcy5lbnZpcm9uLmdldCgiTUNQX0VNQkVERElOR19NT0RFTCIpCiAgICBpZiBub3QgbW9kZWxfY2hvaWNlIGFuZCBpc2luc3RhbmNlKG1vZGVsX2Nob2ljZV9mcm9tX2NvbmZpZywgc3RyKToKICAgICAgICBtb2RlbF9jaG9pY2UgPSBtb2RlbF9jaG9pY2VfZnJvbV9jb25maWcKICAgIGlmIG1vZGVsX2Nob2ljZToKICAgICAgICBtb2RlbF9jaG9pY2UgPSBtb2RlbF9jaG9pY2Uuc3RyaXAoKS5sb3dlcigpCiAgICBtb2RlbF9jaG9pY2UgPSBfcGlja193aXRoX3Byb21wdCgKICAgICAgICBjdXJyZW50X3ZhbHVlPW1vZGVsX2Nob2ljZSwKICAgICAgICBkZWZhdWx0X3ZhbHVlPURFRkFVTFRfRU1CRURESU5HX01PREVMX0NIT0lDRSwKICAgICAgICB0aXRsZT0iRXNjb2xoYSBkbyBtb2RlbG8gZGUgZW1iZWRkaW5ncyIsCiAgICAgICAgb3B0aW9ucz1bCiAgICAgICAgICAgICgKICAgICAgICAgICAgICAgICJqaW5hIiwKICAgICAgICAgICAgICAgIGYiamluYSAoe0pJTkFfVjNfRU1CRURESU5HX01PREVMfSkgLSBmb2NvIGVtIGPDs2RpZ28uIiwKICAgICAgICAgICAgKSwKICAgICAgICAgICAgKAogICAgICAgICAgICAgICAgImJnZSIsCiAgICAgICAgICAgICAgICBmImJnZSAoe0JHRV9FTUJFRERJTkdfTU9ERUx9KSAtIGNvbnRlw7pkbyBtaXN0by4iLAogICAgICAgICAgICApLAogICAgICAgICAgICAoCiAgICAgICAgICAgICAgICAiaHlicmlkIiwKICAgICAgICAgICAgICAgIGYiaHlicmlkIChKaW5hIHYyIHtKSU5BX1YyX0VNQkVERElOR19NT0RFTH0gKyBCR0UpIC0gZHVhcyBjb2xlw6fDtWVzLiIsCiAgICAgICAgICAgICksCiAgICAgICAgXSwKICAgICkKICAgIGlmIG1vZGVsX2Nob2ljZSBub3QgaW4geyJqaW5hIiwgImJnZSIsICJoeWJyaWQifToKICAgICAgICBwcmludChmIltBVklTT10gTUNQX0VNQkVERElOR19NT0RFTCBpbnbDoWxpZG8gJ3ttb2RlbF9jaG9pY2V9Jy4gVXNhbmRvICd7REVGQVVMVF9FTUJFRERJTkdfTU9ERUxfQ0hPSUNFfScuIikKICAgICAgICBtb2RlbF9jaG9pY2UgPSBERUZBVUxUX0VNQkVERElOR19NT0RFTF9DSE9JQ0UKCiAgICBxdWFudGl6YXRpb25fZnJvbV9jb25maWcgPSBwZXJzaXN0ZWRfY29uZmlnLmdldCgiamluYV9xdWFudGl6YXRpb24iKQogICAgamluYV9xdWFudGl6YXRpb24gPSBqaW5hX3F1YW50aXphdGlvbl9hcmcgb3Igb3MuZW52aXJvbi5nZXQoIk1DUF9KSU5BX1FVQU5USVpBVElPTiIpCiAgICBpZiBub3QgamluYV9xdWFudGl6YXRpb24gYW5kIGlzaW5zdGFuY2UocXVhbnRpemF0aW9uX2Zyb21fY29uZmlnLCBzdHIpOgogICAgICAgIGppbmFfcXVhbnRpemF0aW9uID0gcXVhbnRpemF0aW9uX2Zyb21fY29uZmlnCiAgICBpZiBqaW5hX3F1YW50aXphdGlvbjoKICAgICAgICBqaW5hX3F1YW50aXphdGlvbiA9IGppbmFfcXVhbnRpemF0aW9uLnN0cmlwKCkubG93ZXIoKS5yZXBsYWNlKCJfIiwgIi0iKQoKICAgIGlmIG1vZGVsX2Nob2ljZSA9PSAiamluYSI6CiAgICAgICAgamluYV9xdWFudGl6YXRpb24gPSBfcGlja193aXRoX3Byb21wdCgKICAgICAgICAgICAgY3VycmVudF92YWx1ZT1qaW5hX3F1YW50aXphdGlvbiwKICAgICAgICAgICAgZGVmYXVsdF92YWx1ZT1ERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OLAogICAgICAgICAgICB0aXRsZT0iUXVhbnRpemFjYW8gZG8gSmluYSAoYXBlbmFzIHBhcmEgQ1BVKSIsCiAgICAgICAgICAgIG9wdGlvbnM9WwogICAgICAgICAgICAgICAgKCJkZWZhdWx0IiwgImRlZmF1bHQgKHNlbSBxdWFudGl6YWNhbykgLSBtYWlvciBxdWFsaWRhZGUsIGluZGV4YWNhbyBtYWlzIGxlbnRhLiIpLAogICAgICAgICAgICAgICAgKCJkeW5hbWljLWludDgiLCAiZHluYW1pYy1pbnQ4IC0gaW5kZXhhY2FvIG1haXMgcmFwaWRhIGUgbWVub3IgdXNvIGRlIFJBTSwgY29tIHBlcXVlbmEgcGVyZGEgZGUgcXVhbGlkYWRlLiIpLAogICAgICAgICAgICBdLAogICAgICAgICkKICAgICAgICBpZiBqaW5hX3F1YW50aXphdGlvbiBub3QgaW4geyJkZWZhdWx0IiwgImR5bmFtaWMtaW50OCJ9OgogICAgICAgICAgICBwcmludCgKICAgICAgICAgICAgICAgIGYiW0FWSVNPXSBNQ1BfSklOQV9RVUFOVElaQVRJT04gaW52w6FsaWRvICd7amluYV9xdWFudGl6YXRpb259Jy4gIgogICAgICAgICAgICAgICAgZiJVc2FuZG8gJ3tERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OfScuIgogICAgICAgICAgICApCiAgICAgICAgICAgIGppbmFfcXVhbnRpemF0aW9uID0gREVGQVVMVF9KSU5BX1FVQU5USVpBVElPTgogICAgZWxzZToKICAgICAgICBqaW5hX3F1YW50aXphdGlvbiA9ICJkZWZhdWx0IgoKICAgIHJldHVybiBtb2RlbF9jaG9pY2UsIGppbmFfcXVhbnRpemF0aW9uCgoKZGVmIF9pbmRleGVyX2NvbmZpZ19jYW5kaWRhdGVzKCkgLT4gbGlzdFtQYXRoXToKICAgIGNhbmRpZGF0ZXMgPSBbSU5ERVhFUl9DT05GSUdfUEFUSF0KICAgIGlmIElOREVYRVJfQ09ORklHX0ZBTExCQUNLX1BBVEggbm90IGluIGNhbmRpZGF0ZXM6CiAgICAgICAgY2FuZGlkYXRlcy5hcHBlbmQoSU5ERVhFUl9DT05GSUdfRkFMTEJBQ0tfUEFUSCkKICAgIHJldHVybiBjYW5kaWRhdGVzCgoKZGVmIGxvYWRfaW5kZXhlcl90dW5pbmdfY29uZmlnKGZvcmNlX3JlY29uZmlndXJlOiBib29sKSAtPiBkaWN0W3N0ciwgb2JqZWN0XToKICAgIGlmIGZvcmNlX3JlY29uZmlndXJlOgogICAgICAgIHJldHVybiB7fQogICAgZm9yIGNhbmRpZGF0ZSBpbiBfaW5kZXhlcl9jb25maWdfY2FuZGlkYXRlcygpOgogICAgICAgIHRyeToKICAgICAgICAgICAgaWYgbm90IGNhbmRpZGF0ZS5leGlzdHMoKToKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIGRhdGEgPSBqc29uLmxvYWRzKGNhbmRpZGF0ZS5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04IikpCiAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UoZGF0YSwgZGljdCk6CiAgICAgICAgICAgICAgICByZXR1cm4gZGF0YQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgIGNvbnRpbnVlCiAgICByZXR1cm4ge30KCgpkZWYgc2F2ZV9pbmRleGVyX3R1bmluZ19jb25maWcoY29uZmlnOiBkaWN0W3N0ciwgb2JqZWN0XSkgLT4gTm9uZToKICAgIHBheWxvYWQgPSB7CiAgICAgICAgKipjb25maWcsCiAgICAgICAgInVwZGF0ZWRfYXQiOiBpbnQodGltZSgpKSwKICAgIH0KICAgIHdyaXRlX2Vycm9yczogbGlzdFt0dXBsZVtQYXRoLCBFeGNlcHRpb25dXSA9IFtdCgogICAgZm9yIGNhbmRpZGF0ZSBpbiBfaW5kZXhlcl9jb25maWdfY2FuZGlkYXRlcygpOgogICAgICAgIHRyeToKICAgICAgICAgICAgY2FuZGlkYXRlLnBhcmVudC5ta2RpcihwYXJlbnRzPVRydWUsIGV4aXN0X29rPVRydWUpCiAgICAgICAgICAgIGNhbmRpZGF0ZS53cml0ZV90ZXh0KGpzb24uZHVtcHMocGF5bG9hZCwgZW5zdXJlX2FzY2lpPUZhbHNlLCBpbmRlbnQ9MikgKyAiXG4iLCBlbmNvZGluZz0idXRmLTgiKQogICAgICAgICAgICBpZiBjYW5kaWRhdGUgPT0gSU5ERVhFUl9DT05GSUdfUEFUSDoKICAgICAgICAgICAgICAgIHByaW50KGYiW0NPTkZJR10gQ29uZmlndXJhw6fDo28gcGVyc2lzdGlkYSBlbToge2NhbmRpZGF0ZX0iKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICAgICAgZiJbQ09ORklHXSBDb25maWd1cmHDp8OjbyBwZXJzaXN0aWRhIGVtIGZhbGxiYWNrOiB7Y2FuZGlkYXRlfSAiCiAgICAgICAgICAgICAgICAgICAgZiIoZGVzdGlubyBwcmltw6FyaW8gc2VtIHBlcm1pc3PDo286IHtJTkRFWEVSX0NPTkZJR19QQVRIfSkiCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgIHJldHVybgogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgd3JpdGVfZXJyb3JzLmFwcGVuZCgoY2FuZGlkYXRlLCBlKSkKCiAgICBkZXRhaWxzID0gIiB8ICIuam9pbihmIntwYXRofToge19mb3JtYXRfZXhjZXB0aW9uKGVycil9IiBmb3IgcGF0aCwgZXJyIGluIHdyaXRlX2Vycm9ycykKICAgIHByaW50KGYiW0FWSVNPXSBOw6NvIGZvaSBwb3Nzw612ZWwgcGVyc2lzdGlyIGNvbmZpZ3VyYcOnw6NvOiB7ZGV0YWlsc30iKQoKCmRlZiByZXNvbHZlX3BlcmZfcHJvZmlsZShwZXJmX3Byb2ZpbGVfYXJnOiBzdHIgfCBOb25lLCBwZXJzaXN0ZWRfY29uZmlnOiBkaWN0W3N0ciwgb2JqZWN0XSkgLT4gc3RyOgogICAgcHJvZmlsZV9mcm9tX2NvbmZpZyA9IHBlcnNpc3RlZF9jb25maWcuZ2V0KCJwZXJmX3Byb2ZpbGUiKQogICAgcHJvZmlsZSA9IHBlcmZfcHJvZmlsZV9hcmcgb3Igb3MuZW52aXJvbi5nZXQoIk1DUF9QRVJGX1BST0ZJTEUiKQogICAgaWYgbm90IHByb2ZpbGUgYW5kIGlzaW5zdGFuY2UocHJvZmlsZV9mcm9tX2NvbmZpZywgc3RyKToKICAgICAgICBwcm9maWxlID0gcHJvZmlsZV9mcm9tX2NvbmZpZwogICAgaWYgcHJvZmlsZToKICAgICAgICBwcm9maWxlID0gcHJvZmlsZS5zdHJpcCgpLmxvd2VyKCkKCiAgICBwcm9maWxlID0gX3BpY2tfd2l0aF9wcm9tcHQoCiAgICAgICAgY3VycmVudF92YWx1ZT1wcm9maWxlLAogICAgICAgIGRlZmF1bHRfdmFsdWU9REVGQVVMVF9QRVJGX1BST0ZJTEUsCiAgICAgICAgdGl0bGU9IlBlcmZpbCBkZSBwZXJmb3JtYW5jZSBkYSBpbmRleGHDp8OjbyIsCiAgICAgICAgb3B0aW9ucz1bCiAgICAgICAgICAgICgKICAgICAgICAgICAgICAgICJhdXRvdHVuZSIsCiAgICAgICAgICAgICAgICAiYXV0b3R1bmUgLSBlcXVpbMOtYnJpbyAocmVjb21lbmRhZG8pLiIsCiAgICAgICAgICAgICksCiAgICAgICAgICAgICgKICAgICAgICAgICAgICAgICJtYXgtcGVyZm9ybWFuY2UiLAogICAgICAgICAgICAgICAgIm1heC1wZXJmb3JtYW5jZSAtIG3DoXhpbW8gdGhyb3VnaHB1dCAobWFpcyBSQU0pLiIsCiAgICAgICAgICAgICksCiAgICAgICAgXSwKICAgICkKICAgIGlmIHByb2ZpbGUgbm90IGluIHsiYXV0b3R1bmUiLCAibWF4LXBlcmZvcm1hbmNlIn06CiAgICAgICAgcHJpbnQoZiJbQVZJU09dIFBlcmZpbCBpbnbDoWxpZG8gJ3twcm9maWxlfScuIFVzYW5kbyAne0RFRkFVTFRfUEVSRl9QUk9GSUxFfScuIikKICAgICAgICBwcm9maWxlID0gREVGQVVMVF9QRVJGX1BST0ZJTEUKICAgIHJldHVybiBwcm9maWxlCgoKZGVmIF9wYXJzZV9jb25maWdfaW50KGNvbmZpZzogZGljdFtzdHIsIG9iamVjdF0sIGtleTogc3RyKSAtPiBpbnQgfCBOb25lOgogICAgcmF3ID0gY29uZmlnLmdldChrZXkpCiAgICBpZiBpc2luc3RhbmNlKHJhdywgaW50KToKICAgICAgICByZXR1cm4gcmF3CiAgICBpZiBpc2luc3RhbmNlKHJhdywgc3RyKSBhbmQgcmF3LmlzZGlnaXQoKToKICAgICAgICByZXR1cm4gaW50KHJhdykKICAgIHJldHVybiBOb25lCgoKZGVmIF9yZWFkX21lbWluZm9fZ2liKCkgLT4gdHVwbGVbZmxvYXQgfCBOb25lLCBmbG9hdCB8IE5vbmUsIGZsb2F0IHwgTm9uZV06CiAgICAiIiJSZXRvcm5hIChtZW1fdG90YWwsIG1lbV9hdmFpbGFibGUsIHN3YXBfdG90YWwpIGVtIEdpQiwgcXVhbmRvIGRpc3BvbsOtdmVsLiIiIgogICAgbWVtX3RvdGFsX2tpYjogaW50IHwgTm9uZSA9IE5vbmUKICAgIG1lbV9hdmFpbGFibGVfa2liOiBpbnQgfCBOb25lID0gTm9uZQogICAgc3dhcF90b3RhbF9raWI6IGludCB8IE5vbmUgPSBOb25lCgogICAgdHJ5OgogICAgICAgIGZvciBsaW5lIGluIFBhdGgoIi9wcm9jL21lbWluZm8iKS5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04Iikuc3BsaXRsaW5lcygpOgogICAgICAgICAgICBpZiBsaW5lLnN0YXJ0c3dpdGgoIk1lbVRvdGFsOiIpOgogICAgICAgICAgICAgICAgbWVtX3RvdGFsX2tpYiA9IGludChsaW5lLnNwbGl0KClbMV0pCiAgICAgICAgICAgIGVsaWYgbGluZS5zdGFydHN3aXRoKCJNZW1BdmFpbGFibGU6Iik6CiAgICAgICAgICAgICAgICBtZW1fYXZhaWxhYmxlX2tpYiA9IGludChsaW5lLnNwbGl0KClbMV0pCiAgICAgICAgICAgIGVsaWYgbGluZS5zdGFydHN3aXRoKCJTd2FwVG90YWw6Iik6CiAgICAgICAgICAgICAgICBzd2FwX3RvdGFsX2tpYiA9IGludChsaW5lLnNwbGl0KClbMV0pCiAgICBleGNlcHQgKE9TRXJyb3IsIFZhbHVlRXJyb3IsIEluZGV4RXJyb3IpOgogICAgICAgIHJldHVybiBOb25lLCBOb25lLCBOb25lCgogICAgdG9fZ2liID0gbGFtYmRhIGtpYjogKGtpYiAvICgxMDI0ICogMTAyNCkpIGlmIGtpYiBpcyBub3QgTm9uZSBlbHNlIE5vbmUKICAgIHJldHVybiB0b19naWIobWVtX3RvdGFsX2tpYiksIHRvX2dpYihtZW1fYXZhaWxhYmxlX2tpYiksIHRvX2dpYihzd2FwX3RvdGFsX2tpYikKCgpkZWYgd2Fybl9pZl9qaW5hX21lbW9yeV9yaXNrKG1vZGVsX2Nob2ljZTogc3RyLCBqaW5hX3F1YW50aXphdGlvbjogc3RyKSAtPiBOb25lOgogICAgIiIiTW9zdHJhIGF2aXNvIGRlIHJpc2NvIGRlIE9PTSBwYXJhIG8gbW9kZWxvIEppbmEgZW0gbcOhcXVpbmFzIGNvbSBwb3VjYSBtZW3Ds3JpYS4iIiIKICAgIGlmIG1vZGVsX2Nob2ljZSBub3QgaW4geyJqaW5hIiwgImh5YnJpZCJ9OgogICAgICAgIHJldHVybgoKICAgIG1lbV90b3RhbF9naWIsIG1lbV9hdmFpbGFibGVfZ2liLCBzd2FwX3RvdGFsX2dpYiA9IF9yZWFkX21lbWluZm9fZ2liKCkKICAgIGlmIG1lbV90b3RhbF9naWIgaXMgTm9uZToKICAgICAgICByZXR1cm4KCiAgICByZWNvbW1lbmRlZF9yYW1fZ2liID0gKAogICAgICAgIEpJTkFfUkVDT01NRU5ERURfUkFNX0dCX0RFRkFVTFQKICAgICAgICBpZiBqaW5hX3F1YW50aXphdGlvbiA9PSAiZGVmYXVsdCIKICAgICAgICBlbHNlIEpJTkFfUkVDT01NRU5ERURfUkFNX0dCX0RZTkFNSUNfSU5UOAogICAgKQoKICAgIHJlYXNvbnM6IGxpc3Rbc3RyXSA9IFtdCiAgICBpZiBtZW1fdG90YWxfZ2liIDwgcmVjb21tZW5kZWRfcmFtX2dpYjoKICAgICAgICByZWFzb25zLmFwcGVuZCgKICAgICAgICAgICAgZiJSQU0gdG90YWwgZGV0ZWN0YWRhOiB7bWVtX3RvdGFsX2dpYjouMWZ9IEdpQiAocmVjb21lbmRhZG8gPj0ge3JlY29tbWVuZGVkX3JhbV9naWJ9IEdpQiBwYXJhIEppbmEve2ppbmFfcXVhbnRpemF0aW9ufSkuIgogICAgICAgICkKICAgIGlmIHN3YXBfdG90YWxfZ2liIGlzIG5vdCBOb25lIGFuZCBzd2FwX3RvdGFsX2dpYiA8IEpJTkFfUkVDT01NRU5ERURfU1dBUF9HQjoKICAgICAgICByZWFzb25zLmFwcGVuZCgKICAgICAgICAgICAgZiJTd2FwIGRldGVjdGFkYToge3N3YXBfdG90YWxfZ2liOi4xZn0gR2lCIChyZWNvbWVuZGFkbyA+PSB7SklOQV9SRUNPTU1FTkRFRF9TV0FQX0dCfSBHaUIpLiIKICAgICAgICApCiAgICBpZiBtZW1fYXZhaWxhYmxlX2dpYiBpcyBub3QgTm9uZSBhbmQgbWVtX2F2YWlsYWJsZV9naWIgPCBKSU5BX01JTl9BVkFJTEFCTEVfUkFNX0dCX0hJTlQ6CiAgICAgICAgcmVhc29ucy5hcHBlbmQoCiAgICAgICAgICAgIGYiUkFNIGxpdnJlIGF0dWFsOiB7bWVtX2F2YWlsYWJsZV9naWI6LjFmfSBHaUIgKGJhaXhvIHBhcmEgYSBjYXJnYSBpbmljaWFsIGRvIEppbmEpLiIKICAgICAgICApCgogICAgaWYgbm90IHJlYXNvbnM6CiAgICAgICAgcmV0dXJuCgogICAgcHJpbnQoIltBVklTT10gQWx0byByaXNjbyBkZSBPT00gY29tIEppbmEgbmVzdGEgbcOhcXVpbmEvY2FyZ2EuIikKICAgIGZvciByZWFzb24gaW4gcmVhc29uczoKICAgICAgICBwcmludChmIiAgICAgICAgLSB7cmVhc29ufSIpCiAgICBwcmludCgiICAgICAgICAtIFNlIG9jb3JyZXIgJ0tpbGxlZCcgKGV4aXQgMTM3KSwgdXNlIEJHRTogLS1lbWJlZGRpbmctbW9kZWwgYmdlIikKICAgIHByaW50KCIgICAgICAgIC0gT3Ugcm9kZSBvIEppbmEgZW0gbcOhcXVpbmEgY29tIG1haXMgUkFNL3N3YXAgZSBtZW5vcyBwcm9jZXNzb3MgY29uY29ycmVudGVzLiIpCgoKQGRhdGFjbGFzcyhmcm96ZW49VHJ1ZSkKY2xhc3MgUnVudGltZUluZGV4aW5nUGFyYW1zOgogICAgY2h1bmtfc2l6ZTogaW50CiAgICBjaHVua19vdmVybGFwOiBpbnQKICAgIGVtYmVkZGluZ19iYXRjaF9zaXplOiBpbnQKICAgIHJlYXNvbnM6IGxpc3Rbc3RyXQoKCmRlZiBfcmVzb2x2ZV9tYXhfcGVyZm9ybWFuY2VfcGFyYW1zKAogICAgKiwKICAgIGNodW5rX3NpemVfbG9ja2VkOiBib29sLAogICAgY2h1bmtfb3ZlcmxhcF9sb2NrZWQ6IGJvb2wsCiAgICBiYXRjaF9zaXplX2xvY2tlZDogYm9vbCwKICAgIGNodW5rX3NpemU6IGludCwKICAgIGNodW5rX292ZXJsYXA6IGludCwKICAgIGVtYmVkZGluZ19iYXRjaF9zaXplOiBpbnQsCikgLT4gUnVudGltZUluZGV4aW5nUGFyYW1zOgogICAgbWVtX3RvdGFsX2dpYiwgbWVtX2F2YWlsYWJsZV9naWIsIF8gPSBfcmVhZF9tZW1pbmZvX2dpYigpCiAgICByZWFzb25zID0gWwogICAgICAgICJQZXJmaWwgc2VsZWNpb25hZG86IG1heC1wZXJmb3JtYW5jZS4iLAogICAgICAgICJNb2RvIHBvZGUgZWxldmFyIGNvbnNpZGVyYXZlbG1lbnRlIG8gY29uc3VtbyBkZSBtZW3Ds3JpYSBlIGNhdXNhciBlbmNlcnJhbWVudG8gcG9yIE9PTSAoZXhpdCAxMzcpLiIsCiAgICBdCgogICAgdHVuZWRfY2h1bmtfc2l6ZSA9IGNodW5rX3NpemUKICAgIHR1bmVkX2NodW5rX292ZXJsYXAgPSBjaHVua19vdmVybGFwCiAgICB0dW5lZF9iYXRjaCA9IGVtYmVkZGluZ19iYXRjaF9zaXplCgogICAgaWYgbm90IGNodW5rX3NpemVfbG9ja2VkOgogICAgICAgIGlmIG1lbV90b3RhbF9naWIgaXMgbm90IE5vbmUgYW5kIG1lbV90b3RhbF9naWIgPj0gNjQgYW5kIChtZW1fYXZhaWxhYmxlX2dpYiBvciAwKSA+PSAxNjoKICAgICAgICAgICAgdHVuZWRfY2h1bmtfc2l6ZSA9IDcwMDAKICAgICAgICBlbHNlOgogICAgICAgICAgICB0dW5lZF9jaHVua19zaXplID0gNjAwMAogICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiY2h1bmtfc2l6ZSBhanVzdGFkbyBwYXJhIHt0dW5lZF9jaHVua19zaXplfSBubyBwZXJmaWwgbWF4LXBlcmZvcm1hbmNlLiIpCgogICAgaWYgbm90IGNodW5rX292ZXJsYXBfbG9ja2VkOgogICAgICAgIHR1bmVkX2NodW5rX292ZXJsYXAgPSBtaW4odHVuZWRfY2h1bmtfc2l6ZSAtIDEsIG1heCgzMDAsIGludCh0dW5lZF9jaHVua19zaXplICogMC4xNSkpKQogICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiY2h1bmtfb3ZlcmxhcCBhanVzdGFkbyBwYXJhIHt0dW5lZF9jaHVua19vdmVybGFwfS4iKQoKICAgIGlmIG5vdCBiYXRjaF9zaXplX2xvY2tlZDoKICAgICAgICBpZiBtZW1fdG90YWxfZ2liIGlzIG5vdCBOb25lIGFuZCBtZW1fdG90YWxfZ2liID49IDY0IGFuZCAobWVtX2F2YWlsYWJsZV9naWIgb3IgMCkgPj0gMTY6CiAgICAgICAgICAgIHR1bmVkX2JhdGNoID0gMjQKICAgICAgICBlbGlmIG1lbV90b3RhbF9naWIgaXMgbm90IE5vbmUgYW5kIG1lbV90b3RhbF9naWIgPj0gMzI6CiAgICAgICAgICAgIHR1bmVkX2JhdGNoID0gMTYKICAgICAgICBlbHNlOgogICAgICAgICAgICB0dW5lZF9iYXRjaCA9IDEyCiAgICAgICAgcmVhc29ucy5hcHBlbmQoZiJlbWJlZGRpbmdfYmF0Y2hfc2l6ZSBhanVzdGFkbyBwYXJhIHt0dW5lZF9iYXRjaH0uIikKCiAgICByZXR1cm4gUnVudGltZUluZGV4aW5nUGFyYW1zKAogICAgICAgIGNodW5rX3NpemU9dHVuZWRfY2h1bmtfc2l6ZSwKICAgICAgICBjaHVua19vdmVybGFwPXR1bmVkX2NodW5rX292ZXJsYXAsCiAgICAgICAgZW1iZWRkaW5nX2JhdGNoX3NpemU9bWF4KDEsIHR1bmVkX2JhdGNoKSwKICAgICAgICByZWFzb25zPXJlYXNvbnMsCiAgICApCgoKZGVmIF9yZXNvbHZlX2F1dG90dW5lZF9wYXJhbXMoCiAgICAqLAogICAgbW9kZWw6IFNlbnRlbmNlVHJhbnNmb3JtZXIsCiAgICBjaHVua19zaXplX2xvY2tlZDogYm9vbCwKICAgIGNodW5rX292ZXJsYXBfbG9ja2VkOiBib29sLAogICAgYmF0Y2hfc2l6ZV9sb2NrZWQ6IGJvb2wsCiAgICBjaHVua19zaXplOiBpbnQsCiAgICBjaHVua19vdmVybGFwOiBpbnQsCiAgICBlbWJlZGRpbmdfYmF0Y2hfc2l6ZTogaW50LAopIC0+IFJ1bnRpbWVJbmRleGluZ1BhcmFtczoKICAgIHJlYXNvbnM6IGxpc3Rbc3RyXSA9IFsiUGVyZmlsIHNlbGVjaW9uYWRvOiBhdXRvdHVuZSAoY3VzdG8tYmVuZWbDrWNpbykuIl0KICAgIHZlcmJvc2VfYXV0b3R1bmUgPSBfZW52X2Jvb2woIk1DUF9BVVRPVFVORV9WRVJCT1NFIiwgZGVmYXVsdD1GYWxzZSkKCiAgICB0cnk6CiAgICAgICAgaW1wb3J0IHBzdXRpbCAgIyB0eXBlOiBpZ25vcmUKICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgcmVhc29ucy5hcHBlbmQoInBzdXRpbCBpbmRpc3BvbsOtdmVsOyBtYW50ZW5kbyBwYXLDom1ldHJvcyBhdHVhaXMgc2VtIGJlbmNobWFyay4iKQogICAgICAgIHJldHVybiBSdW50aW1lSW5kZXhpbmdQYXJhbXMoCiAgICAgICAgICAgIGNodW5rX3NpemU9Y2h1bmtfc2l6ZSwKICAgICAgICAgICAgY2h1bmtfb3ZlcmxhcD1jaHVua19vdmVybGFwLAogICAgICAgICAgICBlbWJlZGRpbmdfYmF0Y2hfc2l6ZT1lbWJlZGRpbmdfYmF0Y2hfc2l6ZSwKICAgICAgICAgICAgcmVhc29ucz1yZWFzb25zLAogICAgICAgICkKCiAgICB2bSA9IHBzdXRpbC52aXJ0dWFsX21lbW9yeSgpCiAgICBzd2FwID0gcHN1dGlsLnN3YXBfbWVtb3J5KCkKICAgIG1lbV90b3RhbF9naWIgPSB2bS50b3RhbCAvICgxMDI0KiozKQogICAgbWVtX2F2YWlsYWJsZV9naWIgPSB2bS5hdmFpbGFibGUgLyAoMTAyNCoqMykKICAgIHN3YXBfdG90YWxfZ2liID0gc3dhcC50b3RhbCAvICgxMDI0KiozKQoKICAgIHRhcmdldF9yYW1fcGVyY2VudCA9IF9jbGFtcCgKICAgICAgICBmbG9hdChvcy5lbnZpcm9uLmdldCgiTUNQX0FVVE9UVU5FX1RBUkdFVF9SQU1fUEVSQ0VOVCIsICI2OCIpKSwKICAgICAgICA2MC4wLAogICAgICAgIDc1LjAsCiAgICApCiAgICBpZiBtZW1fYXZhaWxhYmxlX2dpYiA8IDYgb3Igc3dhcF90b3RhbF9naWIgPCA0OgogICAgICAgIHRhcmdldF9yYW1fcGVyY2VudCA9IG1pbih0YXJnZXRfcmFtX3BlcmNlbnQsIDYzLjApCiAgICByZWFzb25zLmFwcGVuZCgKICAgICAgICBmIk1lbcOzcmlhIGRldGVjdGFkYTogdG90YWw9e21lbV90b3RhbF9naWI6LjFmfSBHaUIsIGxpdnJlPXttZW1fYXZhaWxhYmxlX2dpYjouMWZ9IEdpQiwgIgogICAgICAgIGYic3dhcD17c3dhcF90b3RhbF9naWI6LjFmfSBHaUIsIGFsdm89e3RhcmdldF9yYW1fcGVyY2VudDouMWZ9JS4iCiAgICApCgogICAgdHVuZWRfY2h1bmtfc2l6ZSA9IGNodW5rX3NpemUKICAgIHR1bmVkX2NodW5rX292ZXJsYXAgPSBjaHVua19vdmVybGFwCiAgICB0dW5lZF9iYXRjaCA9IGVtYmVkZGluZ19iYXRjaF9zaXplCgogICAgaWYgbm90IGNodW5rX3NpemVfbG9ja2VkOgogICAgICAgIGlmIG1lbV90b3RhbF9naWIgPCA4IG9yIG1lbV9hdmFpbGFibGVfZ2liIDwgMzoKICAgICAgICAgICAgdHVuZWRfY2h1bmtfc2l6ZSA9IDE4MDAKICAgICAgICBlbGlmIG1lbV90b3RhbF9naWIgPCAxNiBvciBtZW1fYXZhaWxhYmxlX2dpYiA8IDY6CiAgICAgICAgICAgIHR1bmVkX2NodW5rX3NpemUgPSAyNDAwCiAgICAgICAgZWxpZiBtZW1fdG90YWxfZ2liIDwgMzIgb3IgbWVtX2F2YWlsYWJsZV9naWIgPCAxMjoKICAgICAgICAgICAgdHVuZWRfY2h1bmtfc2l6ZSA9IDMyMDAKICAgICAgICBlbHNlOgogICAgICAgICAgICB0dW5lZF9jaHVua19zaXplID0gNDIwMAogICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiY2h1bmtfc2l6ZSBhdXRvdHVuYWRvIHBhcmEge3R1bmVkX2NodW5rX3NpemV9LiIpCgogICAgaWYgbm90IGNodW5rX292ZXJsYXBfbG9ja2VkOgogICAgICAgIHR1bmVkX2NodW5rX292ZXJsYXAgPSBtaW4odHVuZWRfY2h1bmtfc2l6ZSAtIDEsIG1heCgxMjAsIGludCh0dW5lZF9jaHVua19zaXplICogMC4xNSkpKQogICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiY2h1bmtfb3ZlcmxhcCBhdXRvdHVuYWRvIHBhcmEge3R1bmVkX2NodW5rX292ZXJsYXB9LiIpCgogICAgaWYgbm90IGJhdGNoX3NpemVfbG9ja2VkOgogICAgICAgIG1heF9jYW5kaWRhdGUgPSAxNgogICAgICAgIGlmIG1lbV90b3RhbF9naWIgPCA4IG9yIG1lbV9hdmFpbGFibGVfZ2liIDwgMyBvciBzd2FwX3RvdGFsX2dpYiA8IDI6CiAgICAgICAgICAgIG1heF9jYW5kaWRhdGUgPSAyCiAgICAgICAgZWxpZiBtZW1fdG90YWxfZ2liIDwgMTYgb3IgbWVtX2F2YWlsYWJsZV9naWIgPCA2OgogICAgICAgICAgICBtYXhfY2FuZGlkYXRlID0gNAogICAgICAgIGVsaWYgbWVtX3RvdGFsX2dpYiA8IDMyIG9yIG1lbV9hdmFpbGFibGVfZ2liIDwgMTA6CiAgICAgICAgICAgIG1heF9jYW5kaWRhdGUgPSA4CgogICAgICAgIGNhbmRpZGF0ZXMgPSBbMiwgNCwgNiwgOCwgMTIsIDE2XQogICAgICAgIGNhbmRpZGF0ZXMgPSBbYyBmb3IgYyBpbiBjYW5kaWRhdGVzIGlmIGMgPD0gbWF4X2NhbmRpZGF0ZV0KICAgICAgICBpZiBub3QgY2FuZGlkYXRlczoKICAgICAgICAgICAgY2FuZGlkYXRlcyA9IFsyXQoKICAgICAgICBwcm9jZXNzID0gcHN1dGlsLlByb2Nlc3MoKQogICAgICAgIHNhbXBsZV9zaXplID0gbWluKG1heCg1MTIsIHR1bmVkX2NodW5rX3NpemUpLCAzMDAwKQogICAgICAgIHNhbXBsZV90ZXh0ID0gKCIjIGF1dG90dW5lLXNhbXBsZVxuIiArICgieCIgKiBzYW1wbGVfc2l6ZSkpCgogICAgICAgIGJlc3RfYmF0Y2ggPSBjYW5kaWRhdGVzWzBdCiAgICAgICAgYmVzdF9zY29yZSA9IC0xLjAKICAgICAgICBiZXN0X21lbW9yeV9wY3QgPSAxMDAuMAogICAgICAgIHNlbGVjdGVkX2JlbmNobWFya19saW5lOiBzdHIgfCBOb25lID0gTm9uZQogICAgICAgIGJlbmNobWFya19saW5lczogbGlzdFtzdHJdID0gW10KCiAgICAgICAgIyBXYXJtdXAgY3VydG8gcGFyYSBlc3RhYmlsaXphciBjYWNoZSBpbnRlcm5vLgogICAgICAgIHRyeToKICAgICAgICAgICAgXyA9IG1vZGVsLmVuY29kZShbc2FtcGxlX3RleHRdLCBzaG93X3Byb2dyZXNzX2Jhcj1GYWxzZSwgYmF0Y2hfc2l6ZT0xKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgIHBhc3MKCiAgICAgICAgZm9yIGNhbmRpZGF0ZSBpbiBjYW5kaWRhdGVzOgogICAgICAgICAgICBkb2NzID0gW3NhbXBsZV90ZXh0XSAqIGNhbmRpZGF0ZQogICAgICAgICAgICBnYy5jb2xsZWN0KCkKICAgICAgICAgICAgYmVmb3JlX3ZtID0gcHN1dGlsLnZpcnR1YWxfbWVtb3J5KCkucGVyY2VudAogICAgICAgICAgICBiZWZvcmVfcnNzID0gcHJvY2Vzcy5tZW1vcnlfaW5mbygpLnJzcyAvICgxMDI0KioyKQogICAgICAgICAgICBzdGFydGVkID0gcGVyZl9jb3VudGVyKCkKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgZW1iZWRkaW5ncyA9IG1vZGVsLmVuY29kZSgKICAgICAgICAgICAgICAgICAgICBkb2NzLAogICAgICAgICAgICAgICAgICAgIHNob3dfcHJvZ3Jlc3NfYmFyPUZhbHNlLAogICAgICAgICAgICAgICAgICAgIGJhdGNoX3NpemU9Y2FuZGlkYXRlLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBiZW5jaG1hcmtfbGluZXMuYXBwZW5kKGYiYmF0Y2g9e2NhbmRpZGF0ZX06IGVycm8gKHtlfSkiKQogICAgICAgICAgICAgICAgY29udGludWUKCiAgICAgICAgICAgIGVsYXBzZWQgPSBtYXgocGVyZl9jb3VudGVyKCkgLSBzdGFydGVkLCAxZS02KQogICAgICAgICAgICBhZnRlcl92bSA9IHBzdXRpbC52aXJ0dWFsX21lbW9yeSgpLnBlcmNlbnQKICAgICAgICAgICAgYWZ0ZXJfcnNzID0gcHJvY2Vzcy5tZW1vcnlfaW5mbygpLnJzcyAvICgxMDI0KioyKQogICAgICAgICAgICBkZWwgZW1iZWRkaW5ncwogICAgICAgICAgICBnYy5jb2xsZWN0KCkKCiAgICAgICAgICAgIHRocm91Z2hwdXQgPSBjYW5kaWRhdGUgLyBlbGFwc2VkCiAgICAgICAgICAgIHNhZmUgPSBhZnRlcl92bSA8PSAodGFyZ2V0X3JhbV9wZXJjZW50ICsgMy4wKQogICAgICAgICAgICBiZW5jaG1hcmtfbGluZXMuYXBwZW5kKAogICAgICAgICAgICAgICAgZiJiYXRjaD17Y2FuZGlkYXRlfToge3Rocm91Z2hwdXQ6LjJmfSBpdGVucy9zLCB2bT17YWZ0ZXJfdm06LjFmfSUsIHJzc19kZWx0YT17YWZ0ZXJfcnNzIC0gYmVmb3JlX3JzczorLjFmfSBNaUIiCiAgICAgICAgICAgICkKCiAgICAgICAgICAgIGlmIHNhZmUgYW5kIHRocm91Z2hwdXQgPiBiZXN0X3Njb3JlOgogICAgICAgICAgICAgICAgYmVzdF9zY29yZSA9IHRocm91Z2hwdXQKICAgICAgICAgICAgICAgIGJlc3RfYmF0Y2ggPSBjYW5kaWRhdGUKICAgICAgICAgICAgICAgIGJlc3RfbWVtb3J5X3BjdCA9IGFmdGVyX3ZtCiAgICAgICAgICAgICAgICBzZWxlY3RlZF9iZW5jaG1hcmtfbGluZSA9IGJlbmNobWFya19saW5lc1stMV0KICAgICAgICAgICAgZWxpZiBiZXN0X3Njb3JlIDwgMCBhbmQgYWZ0ZXJfdm0gPCBiZXN0X21lbW9yeV9wY3Q6CiAgICAgICAgICAgICAgICAjIFNlIG5lbmh1bSBjYW5kaWRhdG8gZmljb3UgInNhZmUiLCBlc2NvbGhlIG8gbWVub3MgYWdyZXNzaXZvIGVtIG1lbcOzcmlhLgogICAgICAgICAgICAgICAgYmVzdF9iYXRjaCA9IGNhbmRpZGF0ZQogICAgICAgICAgICAgICAgYmVzdF9tZW1vcnlfcGN0ID0gYWZ0ZXJfdm0KICAgICAgICAgICAgICAgIHNlbGVjdGVkX2JlbmNobWFya19saW5lID0gYmVuY2htYXJrX2xpbmVzWy0xXQoKICAgICAgICAgICAgIyBTZSBqw6EgcGFzc291IG11aXRvIGRvIGxpbWl0ZSwgZXZpdGEgdGVudGFyIGJhdGNoZXMgbWFpb3Jlcy4KICAgICAgICAgICAgaWYgYWZ0ZXJfdm0gPiB0YXJnZXRfcmFtX3BlcmNlbnQgKyA4LjA6CiAgICAgICAgICAgICAgICBicmVhawoKICAgICAgICAgICAgIyBFdml0YSBlc2NvbGhlciBjYW5kaWRhdG8gcXVlIGrDoSBjb21lw6dvdSBhY2ltYSBkbyBsaW1pdGUuCiAgICAgICAgICAgIGlmIGJlZm9yZV92bSA+IHRhcmdldF9yYW1fcGVyY2VudCArIDUuMDoKICAgICAgICAgICAgICAgIGJyZWFrCgogICAgICAgIHR1bmVkX2JhdGNoID0gbWF4KDEsIGJlc3RfYmF0Y2gpCiAgICAgICAgaWYgdmVyYm9zZV9hdXRvdHVuZToKICAgICAgICAgICAgcmVhc29ucy5leHRlbmQoYmVuY2htYXJrX2xpbmVzKQogICAgICAgIGVsaWYgc2VsZWN0ZWRfYmVuY2htYXJrX2xpbmU6CiAgICAgICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiTWljcm8tYmVuY2htYXJrOiB7c2VsZWN0ZWRfYmVuY2htYXJrX2xpbmV9IikKICAgICAgICByZWFzb25zLmFwcGVuZCgKICAgICAgICAgICAgZiJlbWJlZGRpbmdfYmF0Y2hfc2l6ZSBhdXRvdHVuYWRvIHBhcmEge3R1bmVkX2JhdGNofSAoYWx2byBkZSBtZW3Ds3JpYToge3RhcmdldF9yYW1fcGVyY2VudDouMWZ9JSkuIgogICAgICAgICkKCiAgICByZXR1cm4gUnVudGltZUluZGV4aW5nUGFyYW1zKAogICAgICAgIGNodW5rX3NpemU9dHVuZWRfY2h1bmtfc2l6ZSwKICAgICAgICBjaHVua19vdmVybGFwPXR1bmVkX2NodW5rX292ZXJsYXAsCiAgICAgICAgZW1iZWRkaW5nX2JhdGNoX3NpemU9bWF4KDEsIHR1bmVkX2JhdGNoKSwKICAgICAgICByZWFzb25zPXJlYXNvbnMsCiAgICApCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBGdW7Dp8O1ZXMgYXV4aWxpYXJlcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKZGVmIGdldF90ZXh0X3NwbGl0dGVyKGNodW5rX3NpemU6IGludCwgY2h1bmtfb3ZlcmxhcDogaW50KSAtPiBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXI6CiAgICAiIiJSZXRvcm5hIG8gc3BsaXR0ZXIgY29tcGFydGlsaGFkbyBjb20gYXMgY29uZmlndXJhw6fDtWVzIHBhZHLDo28gZG8gcHJvamV0by4iIiIKICAgIHJldHVybiBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIoCiAgICAgICAgY2h1bmtfc2l6ZT1jaHVua19zaXplLAogICAgICAgIGNodW5rX292ZXJsYXA9Y2h1bmtfb3ZlcmxhcCwKICAgICAgICBsZW5ndGhfZnVuY3Rpb249bGVuLAogICAgICAgIHNlcGFyYXRvcnM9WyJcblxuIiwgIlxuIiwgIiAiLCAiIl0sCiAgICApCgoKZGVmIGxvYWRfZW1iZWRkaW5nX21vZGVsKG1vZGVsX2Nob2ljZTogc3RyLCBqaW5hX3F1YW50aXphdGlvbjogc3RyKSAtPiBTZW50ZW5jZVRyYW5zZm9ybWVyOgogICAgIiIiQ2FycmVnYSBvIG1vZGVsbyBkZSBlbWJlZGRpbmdzIGZvcsOnYW5kbyB1c28gZGUgQ1BVLiIiIgogICAgZW1iZWRkaW5nX21vZGVsX2lkID0gX3Jlc29sdmVfbW9kZWxfaWQobW9kZWxfY2hvaWNlKQogICAgZmFsbGJhY2tfbW9kZWxfaWQgPSBfcmVzb2x2ZV9mYWxsYmFja19tb2RlbF9pZChtb2RlbF9jaG9pY2UpCgogICAgbW9kZWxfYmFzZV9kaXIgPSBNT0RFTF9DQUNIRV9CQVNFX0RJUgogICAgbW9kZWxfYmFzZV9kaXIubWtkaXIocGFyZW50cz1UcnVlLCBleGlzdF9vaz1UcnVlKQogICAgcHJlZmVycmVkX21vZGVsX2NhY2hlX2RpciA9IF9tb2RlbF9jYWNoZV9kaXIobW9kZWxfYmFzZV9kaXIsIGVtYmVkZGluZ19tb2RlbF9pZCkKCiAgICBwcmludChmIlsrXSBCYWl4YW5kbyBtb2RlbG8gcHJlZmVyaWRvOiB7ZW1iZWRkaW5nX21vZGVsX2lkfSIpCiAgICBwcmludChmIlsrXSBEaXJldMOzcmlvIGRlIGRvd25sb2FkL2NhY2hlIGRvIG1vZGVsbzoge3ByZWZlcnJlZF9tb2RlbF9jYWNoZV9kaXJ9IikKICAgIHNlbGVjdGlvbiA9IGRvd25sb2FkX21vZGVsX3dpdGhfZmFsbGJhY2soCiAgICAgICAgcHJlZmVycmVkX21vZGVsX2lkPWVtYmVkZGluZ19tb2RlbF9pZCwKICAgICAgICBmYWxsYmFja19tb2RlbF9pZD1mYWxsYmFja19tb2RlbF9pZCwKICAgICAgICBsb2NhbF9kaXI9bW9kZWxfYmFzZV9kaXIsCiAgICApCiAgICBzZWxlY3RlZF9tb2RlbF9kaXIgPSBzZWxlY3Rpb24ubG9jYWxfZGlyCiAgICBwcmludCgKICAgICAgICBmIlsrXSBNb2RlbG8gc2VsZWNpb25hZG86IHtzZWxlY3Rpb24ubW9kZWxfaWR9ICIKICAgICAgICBmIihwcm92aWRlcj17c2VsZWN0aW9uLnByb3ZpZGVyfSwgcGF0aD17c2VsZWN0ZWRfbW9kZWxfZGlyfSkiCiAgICApCgogICAgZGVmIF9jbGVhcl9oZl9keW5hbWljX21vZHVsZXNfY2FjaGUoKSAtPiBOb25lOgogICAgICAgIGNhY2hlX2RpciA9IFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAiaHVnZ2luZ2ZhY2UiIC8gIm1vZHVsZXMiIC8gInRyYW5zZm9ybWVyc19tb2R1bGVzIgogICAgICAgIGlmIGNhY2hlX2Rpci5leGlzdHMoKToKICAgICAgICAgICAgcHJpbnQoZiJbIV0gTGltcGFuZG8gY2FjaGUgZGUgbcOzZHVsb3MgZGluw6JtaWNvcyBkbyBIdWdnaW5nIEZhY2U6IHtjYWNoZV9kaXJ9IikKICAgICAgICAgICAgc2h1dGlsLnJtdHJlZShjYWNoZV9kaXIsIGlnbm9yZV9lcnJvcnM9VHJ1ZSkKCiAgICBkZWYgX2xvYWRfZnJvbV9sb2NhbF9kaXIobW9kZWxfaWQ6IHN0cikgLT4gU2VudGVuY2VUcmFuc2Zvcm1lcjoKICAgICAgICAjIE8gbW9kZWxvIGRhIEppbmEgZGVwZW5kZSBkZSBjw7NkaWdvIHJlbW90bzsgZmFsbGJhY2sgbm9ybWFsbWVudGUgbsOjby4KICAgICAgICB0cnVzdF9yZW1vdGVfY29kZSA9IG1vZGVsX2lkLnN0YXJ0c3dpdGgoImppbmFhaS8iKQogICAgICAgIHRva2VuaXplcl9rd2FyZ3MgPSB7ImZpeF9taXN0cmFsX3JlZ2V4IjogVHJ1ZX0KCiAgICAgICAgZGVmIF9pbnN0YW50aWF0ZV9tb2RlbCgpIC0+IFNlbnRlbmNlVHJhbnNmb3JtZXI6CiAgICAgICAgICAgIHJldHVybiBTZW50ZW5jZVRyYW5zZm9ybWVyKAogICAgICAgICAgICAgICAgc3RyKHNlbGVjdGVkX21vZGVsX2RpciksCiAgICAgICAgICAgICAgICBkZXZpY2U9ImNwdSIsCiAgICAgICAgICAgICAgICB0cnVzdF9yZW1vdGVfY29kZT10cnVzdF9yZW1vdGVfY29kZSwKICAgICAgICAgICAgICAgIHRva2VuaXplcl9rd2FyZ3M9dG9rZW5pemVyX2t3YXJncywKICAgICAgICAgICAgKQoKICAgICAgICBkZWYgX2xvYWRfd2l0aF9taXN0cmFsX3JlZ2V4X3BhdGNoKCkgLT4gU2VudGVuY2VUcmFuc2Zvcm1lcjoKICAgICAgICAgICAgIyBPIGPDs2RpZ28gcmVtb3RvIGRhIEppbmEgaW5zdGFuY2lhIHVtIHRva2VuaXplciBpbnRlcm5vIHNlbSByZXBhc3NhciB0b2tlbml6ZXJfa3dhcmdzLgogICAgICAgICAgICBpZiBub3QgdHJ1c3RfcmVtb3RlX2NvZGU6CiAgICAgICAgICAgICAgICByZXR1cm4gX2luc3RhbnRpYXRlX21vZGVsKCkKCiAgICAgICAgICAgIGZyb20gdHJhbnNmb3JtZXJzIGltcG9ydCBBdXRvTW9kZWwsIEF1dG9Ub2tlbml6ZXIKICAgICAgICAgICAgZnJvbSB0cmFuc2Zvcm1lcnMubW9kZWxpbmdfdXRpbHMgaW1wb3J0IFByZVRyYWluZWRNb2RlbAoKICAgICAgICAgICAgb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkID0gQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgb3JpZ2luYWxfbW9kZWxfZnJvbV9wcmV0cmFpbmVkID0gQXV0b01vZGVsLmZyb21fcHJldHJhaW5lZAogICAgICAgICAgICBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fcHJldHJhaW5lZCA9IFByZVRyYWluZWRNb2RlbC5mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZyA9IFByZVRyYWluZWRNb2RlbC5fZnJvbV9jb25maWcKICAgICAgICAgICAgbW9kZWxfcmVmcyA9IHtzdHIoc2VsZWN0ZWRfbW9kZWxfZGlyKSwgc3RyKHNlbGVjdGVkX21vZGVsX2Rpci5yZXNvbHZlKCkpfQoKICAgICAgICAgICAgZGVmIF9wYXRjaGVkX2Zyb21fcHJldHJhaW5lZCgqYXJncywgKiprd2FyZ3MpOgogICAgICAgICAgICAgICAgbW9kZWxfcmVmID0gYXJnc1swXSBpZiBhcmdzIGVsc2Uga3dhcmdzLmdldCgicHJldHJhaW5lZF9tb2RlbF9uYW1lX29yX3BhdGgiKQogICAgICAgICAgICAgICAgaWYgbW9kZWxfcmVmIGlzIG5vdCBOb25lIGFuZCBzdHIobW9kZWxfcmVmKSBpbiBtb2RlbF9yZWZzOgogICAgICAgICAgICAgICAgICAgIGt3YXJncy5zZXRkZWZhdWx0KCJmaXhfbWlzdHJhbF9yZWdleCIsIFRydWUpCiAgICAgICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkKCphcmdzLCAqKmt3YXJncykKCiAgICAgICAgICAgIGRlZiBfcGF0Y2hlZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQoKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICAgICAgICAgIG1vZGVsX3JlZiA9IGFyZ3NbMF0gaWYgYXJncyBlbHNlIGt3YXJncy5nZXQoInByZXRyYWluZWRfbW9kZWxfbmFtZV9vcl9wYXRoIikKICAgICAgICAgICAgICAgIGlmIG1vZGVsX3JlZiBpcyBub3QgTm9uZSBhbmQgc3RyKG1vZGVsX3JlZikgaW4gbW9kZWxfcmVmcyBhbmQgInRvcmNoX2R0eXBlIiBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICAgICAga3dhcmdzID0gZGljdChrd2FyZ3MpCiAgICAgICAgICAgICAgICAgICAgaWYgImR0eXBlIiBub3QgaW4ga3dhcmdzOgogICAgICAgICAgICAgICAgICAgICAgICBrd2FyZ3NbImR0eXBlIl0gPSBrd2FyZ3NbInRvcmNoX2R0eXBlIl0KICAgICAgICAgICAgICAgICAgICBrd2FyZ3MucG9wKCJ0b3JjaF9kdHlwZSIsIE5vbmUpCiAgICAgICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxfbW9kZWxfZnJvbV9wcmV0cmFpbmVkKCphcmdzLCAqKmt3YXJncykKCiAgICAgICAgICAgIG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkX2ZuID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQuX19mdW5jX18KCiAgICAgICAgICAgIEBjbGFzc21ldGhvZAogICAgICAgICAgICBkZWYgX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQoY2xzLCAqYXJncywgKiprd2FyZ3MpOgogICAgICAgICAgICAgICAgaWYgInRvcmNoX2R0eXBlIiBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICAgICAga3dhcmdzID0gZGljdChrd2FyZ3MpCiAgICAgICAgICAgICAgICAgICAgaWYgImR0eXBlIiBub3QgaW4ga3dhcmdzOgogICAgICAgICAgICAgICAgICAgICAgICBrd2FyZ3NbImR0eXBlIl0gPSBrd2FyZ3NbInRvcmNoX2R0eXBlIl0KICAgICAgICAgICAgICAgICAgICBrd2FyZ3MucG9wKCJ0b3JjaF9kdHlwZSIsIE5vbmUpCiAgICAgICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWRfZm4oY2xzLCAqYXJncywgKiprd2FyZ3MpCgogICAgICAgICAgICBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnX2ZuID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZy5fX2Z1bmNfXwoKICAgICAgICAgICAgQGNsYXNzbWV0aG9kCiAgICAgICAgICAgIGRlZiBfcGF0Y2hlZF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnKGNscywgKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICAgICAgICAgIGlmICJ0b3JjaF9kdHlwZSIgaW4ga3dhcmdzOgogICAgICAgICAgICAgICAgICAgIGt3YXJncyA9IGRpY3Qoa3dhcmdzKQogICAgICAgICAgICAgICAgICAgIGlmICJkdHlwZSIgbm90IGluIGt3YXJnczoKICAgICAgICAgICAgICAgICAgICAgICAga3dhcmdzWyJkdHlwZSJdID0ga3dhcmdzWyJ0b3JjaF9kdHlwZSJdCiAgICAgICAgICAgICAgICAgICAga3dhcmdzLnBvcCgidG9yY2hfZHR5cGUiLCBOb25lKQogICAgICAgICAgICAgICAgcmV0dXJuIG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9jb25maWdfZm4oY2xzLCAqYXJncywgKiprd2FyZ3MpCgogICAgICAgICAgICBBdXRvVG9rZW5pemVyLmZyb21fcHJldHJhaW5lZCA9IF9wYXRjaGVkX2Zyb21fcHJldHJhaW5lZAogICAgICAgICAgICBBdXRvTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gX3BhdGNoZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgICAgIFByZVRyYWluZWRNb2RlbC5mcm9tX3ByZXRyYWluZWQgPSBfcGF0Y2hlZF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fcHJldHJhaW5lZAogICAgICAgICAgICBQcmVUcmFpbmVkTW9kZWwuX2Zyb21fY29uZmlnID0gX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZwogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICByZXR1cm4gX2luc3RhbnRpYXRlX21vZGVsKCkKICAgICAgICAgICAgZmluYWxseToKICAgICAgICAgICAgICAgIEF1dG9Ub2tlbml6ZXIuZnJvbV9wcmV0cmFpbmVkID0gb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgICAgICAgICBBdXRvTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gb3JpZ2luYWxfbW9kZWxfZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgICAgICAgICBQcmVUcmFpbmVkTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgICAgIFByZVRyYWluZWRNb2RlbC5fZnJvbV9jb25maWcgPSBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnCgogICAgICAgIHByaW50KGYiWytdIENhcnJlZ2FuZG8gbW9kZWxvIGRlIGVtYmVkZGluZ3MgYSBwYXJ0aXIgZGU6IHtzZWxlY3RlZF9tb2RlbF9kaXJ9IChDUFUpLi4uIikKICAgICAgICB0cnk6CiAgICAgICAgICAgIHJldHVybiBfbG9hZF93aXRoX21pc3RyYWxfcmVnZXhfcGF0Y2goKQogICAgICAgIGV4Y2VwdCBGaWxlTm90Rm91bmRFcnJvciBhcyBlOgogICAgICAgICAgICAjIENvcnJpZ2UgY29ycnVww6fDo28vaW5jb21wbGV0dWRlIG5vIGNhY2hlIGRpbsOibWljbyBkbyB0cmFuc2Zvcm1lcnMuCiAgICAgICAgICAgIGlmIHRydXN0X3JlbW90ZV9jb2RlIGFuZCAidHJhbnNmb3JtZXJzX21vZHVsZXMiIGluIHN0cihlKToKICAgICAgICAgICAgICAgIHByaW50KGYiWyFdIENhY2hlIGRpbsOibWljbyBpbmNvbnNpc3RlbnRlIGRldGVjdGFkbzoge2V9IikKICAgICAgICAgICAgICAgIF9jbGVhcl9oZl9keW5hbWljX21vZHVsZXNfY2FjaGUoKQogICAgICAgICAgICAgICAgcmV0dXJuIF9sb2FkX3dpdGhfbWlzdHJhbF9yZWdleF9wYXRjaCgpCiAgICAgICAgICAgIHJhaXNlCgogICAgZGVmIF9hcHBseV9qaW5hX3F1YW50aXphdGlvbl9pZl9uZWVkZWQobW9kZWw6IFNlbnRlbmNlVHJhbnNmb3JtZXIsIG1vZGVsX2lkOiBzdHIpIC0+IFNlbnRlbmNlVHJhbnNmb3JtZXI6CiAgICAgICAgaWYgbW9kZWxfaWQgIT0gSklOQV9WM19FTUJFRERJTkdfTU9ERUwgb3IgamluYV9xdWFudGl6YXRpb24gPT0gImRlZmF1bHQiOgogICAgICAgICAgICByZXR1cm4gbW9kZWwKICAgICAgICB0cnk6CiAgICAgICAgICAgIGltcG9ydCB0b3JjaAogICAgICAgICAgICBpbXBvcnQgd2FybmluZ3MKCiAgICAgICAgICAgIHF1YW50aXplZF9sYXllcnMgPSAwCiAgICAgICAgICAgIGZvciBtb2R1bGUgaW4gbW9kZWwubW9kdWxlcygpOgogICAgICAgICAgICAgICAgaWYgdHlwZShtb2R1bGUpLl9fbmFtZV9fICE9ICJQYXJhbWV0cml6ZWRMaW5lYXIiOgogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCgogICAgICAgICAgICAgICAgZmxvYXRfbGluZWFyID0gdG9yY2gubm4uTGluZWFyKAogICAgICAgICAgICAgICAgICAgIG1vZHVsZS5pbl9mZWF0dXJlcywKICAgICAgICAgICAgICAgICAgICBtb2R1bGUub3V0X2ZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgIGJpYXM9bW9kdWxlLmJpYXMgaXMgbm90IE5vbmUsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICB3aXRoIHRvcmNoLm5vX2dyYWQoKToKICAgICAgICAgICAgICAgICAgICBmbG9hdF9saW5lYXIud2VpZ2h0LmNvcHlfKG1vZHVsZS53ZWlnaHQuZGV0YWNoKCkudG8odG9yY2guZmxvYXQzMikpCiAgICAgICAgICAgICAgICAgICAgaWYgbW9kdWxlLmJpYXMgaXMgbm90IE5vbmU6CiAgICAgICAgICAgICAgICAgICAgICAgIGZsb2F0X2xpbmVhci5iaWFzLmNvcHlfKG1vZHVsZS5iaWFzLmRldGFjaCgpLnRvKHRvcmNoLmZsb2F0MzIpKQoKICAgICAgICAgICAgICAgIHdpdGggd2FybmluZ3MuY2F0Y2hfd2FybmluZ3MoKToKICAgICAgICAgICAgICAgICAgICB3YXJuaW5ncy5maWx0ZXJ3YXJuaW5ncygiaWdub3JlIiwgY2F0ZWdvcnk9RGVwcmVjYXRpb25XYXJuaW5nKQogICAgICAgICAgICAgICAgICAgIHF1YW50aXplZF9saW5lYXIgPSB0b3JjaC5xdWFudGl6YXRpb24ucXVhbnRpemVfZHluYW1pYygKICAgICAgICAgICAgICAgICAgICAgICAgdG9yY2gubm4uU2VxdWVudGlhbChmbG9hdF9saW5lYXIpLAogICAgICAgICAgICAgICAgICAgICAgICB7dG9yY2gubm4uTGluZWFyfSwKICAgICAgICAgICAgICAgICAgICAgICAgZHR5cGU9dG9yY2gucWludDgsCiAgICAgICAgICAgICAgICAgICAgKVswXQoKICAgICAgICAgICAgICAgIG1vZHVsZS5fZHluYW1pY19pbnQ4X2xpbmVhciA9IHF1YW50aXplZF9saW5lYXIKCiAgICAgICAgICAgICAgICBkZWYgX2ZvcndhcmRfZHluYW1pY19pbnQ4KHNlbGYsIGlucHV0LCB0YXNrX2lkPU5vbmUsIHJlc2lkdWFsPUZhbHNlKToKICAgICAgICAgICAgICAgICAgICBvdXQgPSBzZWxmLl9keW5hbWljX2ludDhfbGluZWFyKGlucHV0KQogICAgICAgICAgICAgICAgICAgIGlmIHJlc2lkdWFsOgogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb3V0LCBpbnB1dAogICAgICAgICAgICAgICAgICAgIHJldHVybiBvdXQKCiAgICAgICAgICAgICAgICBtb2R1bGUuZm9yd2FyZCA9IF9mb3J3YXJkX2R5bmFtaWNfaW50OC5fX2dldF9fKG1vZHVsZSwgbW9kdWxlLl9fY2xhc3NfXykKICAgICAgICAgICAgICAgIHF1YW50aXplZF9sYXllcnMgKz0gMQoKICAgICAgICAgICAgaWYgcXVhbnRpemVkX2xheWVycyA9PSAwOgogICAgICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICAgICAgIltBVklTT10gTmVuaHVtYSBjYW1hZGEgUGFyYW1ldHJpemVkTGluZWFyIGVuY29udHJhZGEgcGFyYSBkeW5hbWljLWludDg7IHVzYW5kbyBtb2RlbG8gcGFkcmFvLiIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIHJldHVybiBtb2RlbAoKICAgICAgICAgICAgcHJpbnQoZiJbK10gUXVhbnRpemFjYW8gSmluYSBhcGxpY2FkYTogZHluYW1pYy1pbnQ4IChDUFUsIHtxdWFudGl6ZWRfbGF5ZXJzfSBjYW1hZGFzKS4iKQogICAgICAgICAgICByZXR1cm4gbW9kZWwKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIHF1YW50X2Vycm9yOgogICAgICAgICAgICBwcmludChmIltBVklTT10gRmFsaGEgYW8gYXBsaWNhciBkeW5hbWljLWludDggKHtxdWFudF9lcnJvcn0pOyB1c2FuZG8gbW9kZWxvIHBhZHJhby4iKQogICAgICAgICAgICByZXR1cm4gbW9kZWwKCiAgICB0cnk6CiAgICAgICAgbW9kZWwgPSBfbG9hZF9mcm9tX2xvY2FsX2RpcihzZWxlY3Rpb24ubW9kZWxfaWQpCiAgICAgICAgbW9kZWwgPSBfYXBwbHlfamluYV9xdWFudGl6YXRpb25faWZfbmVlZGVkKG1vZGVsLCBzZWxlY3Rpb24ubW9kZWxfaWQpCiAgICAgICAgcHJpbnQoIlsrXSBNb2RlbG8gY2FycmVnYWRvIGNvbSBzdWNlc3NvLiIpCiAgICAgICAgcmV0dXJuIG1vZGVsCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGZpcnN0X2Vycm9yOgogICAgICAgIGlmIHNlbGVjdGlvbi5tb2RlbF9pZCA9PSBmYWxsYmFja19tb2RlbF9pZDoKICAgICAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKAogICAgICAgICAgICAgICAgZiJGYWxoYSBhbyBjYXJyZWdhciBvIG1vZGVsbyBmYWxsYmFjayAne2ZhbGxiYWNrX21vZGVsX2lkfSc6IHtmaXJzdF9lcnJvcn0iCiAgICAgICAgICAgICkgZnJvbSBmaXJzdF9lcnJvcgoKICAgICAgICBwcmludCgKICAgICAgICAgICAgZiJbIV0gRmFsaGEgYW8gY2FycmVnYXIgJ3tzZWxlY3Rpb24ubW9kZWxfaWR9Jzoge19mb3JtYXRfZXhjZXB0aW9uKGZpcnN0X2Vycm9yKX1cbiIKICAgICAgICAgICAgZiIgICAgVGVudGFuZG8gZmFsbGJhY2sgZGUgY2FycmVnYW1lbnRvOiB7ZmFsbGJhY2tfbW9kZWxfaWR9IgogICAgICAgICkKICAgICAgICBmYWxsYmFja19zZWxlY3Rpb24gPSBkb3dubG9hZF9tb2RlbF93aXRoX2ZhbGxiYWNrKAogICAgICAgICAgICBwcmVmZXJyZWRfbW9kZWxfaWQ9ZmFsbGJhY2tfbW9kZWxfaWQsCiAgICAgICAgICAgIGZhbGxiYWNrX21vZGVsX2lkPWZhbGxiYWNrX21vZGVsX2lkLAogICAgICAgICAgICBsb2NhbF9kaXI9bW9kZWxfYmFzZV9kaXIsCiAgICAgICAgKQogICAgICAgIHNlbGVjdGVkX21vZGVsX2RpciA9IGZhbGxiYWNrX3NlbGVjdGlvbi5sb2NhbF9kaXIKICAgICAgICBwcmludCgKICAgICAgICAgICAgZiJbK10gTW9kZWxvIHNlbGVjaW9uYWRvOiB7ZmFsbGJhY2tfc2VsZWN0aW9uLm1vZGVsX2lkfSAiCiAgICAgICAgICAgIGYiKHByb3ZpZGVyPXtmYWxsYmFja19zZWxlY3Rpb24ucHJvdmlkZXJ9LCBwYXRoPXtzZWxlY3RlZF9tb2RlbF9kaXJ9KSIKICAgICAgICApCiAgICAgICAgbW9kZWwgPSBfbG9hZF9mcm9tX2xvY2FsX2RpcihmYWxsYmFja19zZWxlY3Rpb24ubW9kZWxfaWQpCiAgICAgICAgbW9kZWwgPSBfYXBwbHlfamluYV9xdWFudGl6YXRpb25faWZfbmVlZGVkKG1vZGVsLCBmYWxsYmFja19zZWxlY3Rpb24ubW9kZWxfaWQpCiAgICAgICAgcHJpbnQoIlsrXSBNb2RlbG8gZmFsbGJhY2sgY2FycmVnYWRvIGNvbSBzdWNlc3NvLiIpCiAgICAgICAgcmV0dXJuIG1vZGVsCgoKZGVmIGNvbm5lY3RfdG9fY2hyb21hKCkgLT4gY2hyb21hZGIuSHR0cENsaWVudDoKICAgICIiIkNvbmVjdGEgYW8gQ2hyb21hREIgdmlhIEhUVFAgZSB2YWxpZGEgYSBjb25leMOjby4iIiIKICAgIHRyeToKICAgICAgICBjbGllbnQgPSBjaHJvbWFkYi5IdHRwQ2xpZW50KGhvc3Q9Q0hST01BX0hPU1QsIHBvcnQ9Q0hST01BX1BPUlQpCiAgICAgICAgIyBGYXogdW0gaGVhcnRiZWF0IHBhcmEgY29uZmlybWFyIHF1ZSBvIHNlcnZpZG9yIGVzdMOhIG5vIGFyCiAgICAgICAgY2xpZW50LmhlYXJ0YmVhdCgpCiAgICAgICAgcHJpbnQoZiJbK10gQ29uZWN0YWRvIGFvIENocm9tYURCIGVtIHtDSFJPTUFfSE9TVH06e0NIUk9NQV9QT1JUfSIpCiAgICAgICAgcmV0dXJuIGNsaWVudAogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHByaW50KGYiW0VSUk9dIE7Do28gZm9pIHBvc3PDrXZlbCBjb25lY3RhciBhbyBDaHJvbWFEQjoge2V9IikKICAgICAgICBwcmludCgiICAgICAgIFZlcmlmaXF1ZSBzZSBvIGNvbnRhaW5lciBEb2NrZXIgZXN0w6Egcm9kYW5kbzoiKQogICAgICAgIHByaW50KCIgICAgICAgZG9ja2VyIGNvbXBvc2UgdXAgLWQiKQogICAgICAgIHN5cy5leGl0KDEpCgoKZGVmIHNjYW5fZmlsZXMocm9vdF9wYXRoOiBQYXRoKSAtPiBJdGVyYXRvcltQYXRoXToKICAgICIiIgogICAgVmFycmUgcmVjdXJzaXZhbWVudGUgbyBkaXJldMOzcmlvIHJhaXosIHJldG9ybmFuZG8gZW0gc3RyZWFtaW5nCiAgICBvcyBhcnF1aXZvcyBkZSB0ZXh0byByZWxldmFudGVzIHBhcmEgaW5kZXhhw6fDo28uCiAgICAiIiIKICAgIGZvciBkaXJwYXRoLCBkaXJuYW1lcywgZmlsZW5hbWVzIGluIG9zLndhbGsocm9vdF9wYXRoKToKICAgICAgICAjIFJlbW92ZSBkaXJzIGlnbm9yYWRvcyBpbi1wbGFjZSBwYXJhIHF1ZSBvcy53YWxrIG7Do28gZGVzw6dhIG5lbGVzCiAgICAgICAgZGlybmFtZXNbOl0gPSBbCiAgICAgICAgICAgIGQgZm9yIGQgaW4gZGlybmFtZXMKICAgICAgICAgICAgaWYgZCBub3QgaW4gSUdOT1JFRF9ESVJTIGFuZCBub3QgZC5zdGFydHN3aXRoKCIuIikKICAgICAgICBdCiAgICAgICAgZGlybmFtZXMuc29ydCgpCgogICAgICAgIGZvciBmaWxlbmFtZSBpbiBzb3J0ZWQoZmlsZW5hbWVzKToKICAgICAgICAgICAgZmlsZXBhdGggPSBQYXRoKGRpcnBhdGgpIC8gZmlsZW5hbWUKCiAgICAgICAgICAgICMgSWdub3JhIHBvciBleHRlbnPDo28KICAgICAgICAgICAgaWYgZmlsZXBhdGguc3VmZml4Lmxvd2VyKCkgaW4gSUdOT1JFRF9FWFRFTlNJT05TOgogICAgICAgICAgICAgICAgY29udGludWUKCiAgICAgICAgICAgICMgSWdub3JhIGFycXVpdm9zIG11aXRvIGdyYW5kZXMKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgaWYgZmlsZXBhdGguc3RhdCgpLnN0X3NpemUgPiBNQVhfRklMRV9TSVpFX0JZVEVTOgogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yOgogICAgICAgICAgICAgICAgY29udGludWUKCiAgICAgICAgICAgIHlpZWxkIGZpbGVwYXRoCgoKZGVmIG1ha2VfY2h1bmtfaWQoZmlsZV9wYXRoOiBzdHIsIGNodW5rX2luZGV4OiBpbnQpIC0+IHN0cjoKICAgICIiIkdlcmEgdW0gSUQgZGV0ZXJtaW7DrXN0aWNvIHBhcmEgY2FkYSBjaHVuayBiYXNlYWRvIG5vIGNhbWluaG8gKyDDrW5kaWNlLiIiIgogICAgcmF3ID0gZiJ7ZmlsZV9wYXRofTo6Y2h1bms6OntjaHVua19pbmRleH0iCiAgICByZXR1cm4gaGFzaGxpYi5tZDUocmF3LmVuY29kZSgpKS5oZXhkaWdlc3QoKQoKCmRlZiByZWFkX2ZpbGVfc2FmZShmaWxlcGF0aDogUGF0aCkgLT4gc3RyIHwgTm9uZToKICAgICIiIkzDqiB1bSBhcnF1aXZvIGRlIHRleHRvLCB0ZW50YW5kbyBtw7psdGlwbG9zIGVuY29kaW5ncy4iIiIKICAgIGZvciBlbmNvZGluZyBpbiAoInV0Zi04IiwgImxhdGluLTEiLCAiY3AxMjUyIik6CiAgICAgICAgdHJ5OgogICAgICAgICAgICByZXR1cm4gZmlsZXBhdGgucmVhZF90ZXh0KGVuY29kaW5nPWVuY29kaW5nKQogICAgICAgIGV4Y2VwdCBVbmljb2RlRGVjb2RlRXJyb3I6CiAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgZXhjZXB0IE9TRXJyb3IgYXMgZToKICAgICAgICAgICAgcHJpbnQoZiIgIFtBVklTT10gTsOjbyBmb2kgcG9zc8OtdmVsIGxlciB7ZmlsZXBhdGh9OiB7ZX0iKQogICAgICAgICAgICByZXR1cm4gTm9uZQogICAgIyBTZSBuZW5odW0gZW5jb2RpbmcgZnVuY2lvbm91LCDDqSBwcm92YXZlbG1lbnRlIGJpbsOhcmlvIGRpc2ZhcsOnYWRvCiAgICByZXR1cm4gTm9uZQoKCmRlZiBkZWxldGVfZmlsZV9jaHVua3MoY29sbGVjdGlvbjogY2hyb21hZGIuQ29sbGVjdGlvbiwgZmlsZV9wYXRoOiBzdHIpIC0+IE5vbmU6CiAgICAiIiJSZW1vdmUgdG9kb3Mgb3MgY2h1bmtzIGRlIHVtIGFycXVpdm8gZXNwZWPDrWZpY28gZGEgY29sZcOnw6NvLiIiIgogICAgdHJ5OgogICAgICAgICMgUGVkZSBzb21lbnRlIElEcyBwYXJhIGV2aXRhciBtYXRlcmlhbGl6YXIgZG9jcy9tZXRhZGF0YSBuYSBtZW3Ds3JpYS4KICAgICAgICByZXN1bHRzID0gY29sbGVjdGlvbi5nZXQod2hlcmU9eyJmaWxlX3BhdGgiOiBmaWxlX3BhdGh9LCBpbmNsdWRlPVtdKQogICAgICAgIGlmIHJlc3VsdHMgYW5kIHJlc3VsdHNbImlkcyJdOgogICAgICAgICAgICBjb2xsZWN0aW9uLmRlbGV0ZShpZHM9cmVzdWx0c1siaWRzIl0pCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcHJpbnQoZiIgIFtBVklTT10gRXJybyBhbyBkZWxldGFyIGNodW5rcyBkZSB7ZmlsZV9wYXRofToge19mb3JtYXRfZXhjZXB0aW9uKGUpfSIpCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBJbmRleGHDp8OjbyBkZSB1bSDDum5pY28gYXJxdWl2bwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKZGVmIGluZGV4X2ZpbGUoCiAgICBmaWxlcGF0aDogUGF0aCwKICAgIGNvbGxlY3Rpb246IGNocm9tYWRiLkNvbGxlY3Rpb24sCiAgICBtb2RlbDogU2VudGVuY2VUcmFuc2Zvcm1lciwKICAgIHNwbGl0dGVyOiBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIsCiAgICByb290X3BhdGg6IFBhdGgsCiAgICBlbWJlZGRpbmdfYmF0Y2hfc2l6ZTogaW50LAopIC0+IGludDoKICAgICIiIgogICAgSW5kZXhhIHVtIMO6bmljbyBhcnF1aXZvOiBsw6osIGRpdmlkZSBlbSBjaHVua3MsIGdlcmEgZW1iZWRkaW5ncyBlIGZheiB1cHNlcnQuCiAgICBSZXRvcm5hIG8gbsO6bWVybyBkZSBjaHVua3MgaW5kZXhhZG9zLgogICAgIiIiCiAgICBjb250ZW50ID0gcmVhZF9maWxlX3NhZmUoZmlsZXBhdGgpCiAgICBpZiBub3QgY29udGVudCBvciBub3QgY29udGVudC5zdHJpcCgpOgogICAgICAgIHJldHVybiAwCgogICAgIyBVc2EgY2FtaW5obyBhYnNvbHV0byBjb21vIG1ldGFkYWRvCiAgICBhYnNfcGF0aCA9IHN0cihmaWxlcGF0aC5yZXNvbHZlKCkpCgogICAgIyBSZW1vdmUgY2h1bmtzIGFudGlnb3MgZGVzdGUgYXJxdWl2byAoYXR1YWxpemHDp8OjbyBpZGVtcG90ZW50ZSkKICAgIGRlbGV0ZV9maWxlX2NodW5rcyhjb2xsZWN0aW9uLCBhYnNfcGF0aCkKCiAgICBjaHVua3MgPSBzcGxpdHRlci5zcGxpdF90ZXh0KGNvbnRlbnQpCiAgICBpZiBub3QgY2h1bmtzOgogICAgICAgIHJldHVybiAwCgogICAgcmVsYXRpdmVfcGF0aCA9IHN0cihmaWxlcGF0aC5yZWxhdGl2ZV90byhyb290X3BhdGgpKQogICAgaW5zZXJ0ZWRfY2h1bmtzID0gMAogICAgYmF0Y2hfaWRzOiBsaXN0W3N0cl0gPSBbXQogICAgYmF0Y2hfZG9jczogbGlzdFtzdHJdID0gW10KICAgIGJhdGNoX21ldGFkYXRhczogbGlzdFtkaWN0W3N0ciwgb2JqZWN0XV0gPSBbXQoKICAgIGRlZiBfZmx1c2hfYmF0Y2goKSAtPiBOb25lOgogICAgICAgIG5vbmxvY2FsIGluc2VydGVkX2NodW5rcwogICAgICAgIGlmIG5vdCBiYXRjaF9pZHM6CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBlbWJlZGRpbmdzID0gbW9kZWwuZW5jb2RlKAogICAgICAgICAgICBiYXRjaF9kb2NzLAogICAgICAgICAgICBzaG93X3Byb2dyZXNzX2Jhcj1GYWxzZSwKICAgICAgICAgICAgYmF0Y2hfc2l6ZT1lbWJlZGRpbmdfYmF0Y2hfc2l6ZSwKICAgICAgICApLnRvbGlzdCgpCiAgICAgICAgY29sbGVjdGlvbi51cHNlcnQoCiAgICAgICAgICAgIGlkcz1iYXRjaF9pZHMsCiAgICAgICAgICAgIGVtYmVkZGluZ3M9ZW1iZWRkaW5ncywKICAgICAgICAgICAgZG9jdW1lbnRzPWJhdGNoX2RvY3MsCiAgICAgICAgICAgIG1ldGFkYXRhcz1iYXRjaF9tZXRhZGF0YXMsCiAgICAgICAgKQogICAgICAgIGluc2VydGVkX2NodW5rcyArPSBsZW4oYmF0Y2hfaWRzKQogICAgICAgIGRlbCBlbWJlZGRpbmdzCiAgICAgICAgYmF0Y2hfaWRzLmNsZWFyKCkKICAgICAgICBiYXRjaF9kb2NzLmNsZWFyKCkKICAgICAgICBiYXRjaF9tZXRhZGF0YXMuY2xlYXIoKQogICAgICAgIGdjLmNvbGxlY3QoKQoKICAgIGZvciBpLCBjaHVuayBpbiBlbnVtZXJhdGUoY2h1bmtzKToKICAgICAgICBiYXRjaF9pZHMuYXBwZW5kKG1ha2VfY2h1bmtfaWQoYWJzX3BhdGgsIGkpKQogICAgICAgIGJhdGNoX2RvY3MuYXBwZW5kKGNodW5rKQogICAgICAgIGJhdGNoX21ldGFkYXRhcy5hcHBlbmQoCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJmaWxlX3BhdGgiOiBhYnNfcGF0aCwKICAgICAgICAgICAgICAgICJjaHVua19pbmRleCI6IGksCiAgICAgICAgICAgICAgICAiZmlsZV9uYW1lIjogZmlsZXBhdGgubmFtZSwKICAgICAgICAgICAgICAgICMgQ2FtaW5obyByZWxhdGl2byDDoCByYWl6IGRvIHByb2pldG8gcGFyYSBleGliacOnw6NvIGNvbXBhY3RhCiAgICAgICAgICAgICAgICAicmVsYXRpdmVfcGF0aCI6IHJlbGF0aXZlX3BhdGgsCiAgICAgICAgICAgIH0KICAgICAgICApCiAgICAgICAgaWYgbGVuKGJhdGNoX2lkcykgPj0gZW1iZWRkaW5nX2JhdGNoX3NpemU6CiAgICAgICAgICAgIF9mbHVzaF9iYXRjaCgpCgogICAgX2ZsdXNoX2JhdGNoKCkKICAgIHJldHVybiBpbnNlcnRlZF9jaHVua3MKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvbnRvIGRlIGVudHJhZGEgcHJpbmNpcGFsCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpkZWYgbWFpbigpOgogICAgcGFyc2VyID0gYXJncGFyc2UuQXJndW1lbnRQYXJzZXIoCiAgICAgICAgZGVzY3JpcHRpb249IkluZGV4YSB1bSBwcm9qZXRvIGRlIGPDs2RpZ28gbm8gQ2hyb21hREIgcGFyYSBSQUcgbG9jYWwuIgogICAgKQogICAgcGFyc2VyLmFkZF9hcmd1bWVudCgKICAgICAgICAicHJvamVjdF9wYXRoIiwKICAgICAgICBuYXJncz0iPyIsCiAgICAgICAgZGVmYXVsdD0iLiIsCiAgICAgICAgaGVscD0iQ2FtaW5obyByYWl6IGRvIHByb2pldG8gYSBpbmRleGFyIChwYWRyw6NvOiBkaXJldMOzcmlvIGF0dWFsKSIsCiAgICApCiAgICBwYXJzZXIuYWRkX2FyZ3VtZW50KAogICAgICAgICItLWNsZWFyIiwKICAgICAgICBhY3Rpb249InN0b3JlX3RydWUiLAogICAgICAgIGhlbHA9IkxpbXBhIHRvZGEgYSBjb2xlw6fDo28gYW50ZXMgZGUgcmVpbmRleGFyIiwKICAgICkKICAgIHBhcnNlci5hZGRfYXJndW1lbnQoCiAgICAgICAgIi0tZW1iZWRkaW5nLW1vZGVsIiwKICAgICAgICBjaG9pY2VzPVsiamluYSIsICJiZ2UiLCAiaHlicmlkIl0sCiAgICAgICAgaGVscD0oCiAgICAgICAgICAgICJNb2RlbG8gZGUgZW1iZWRkaW5nczogJ2ppbmEnIChjb2RpZ28pLCAiCiAgICAgICAgICAgICInYmdlJyAoY29udGV1ZG8gbWlzdG8pIG91ICdoeWJyaWQnIChkdWFzIGNvbGVjb2VzOiBKaW5hIHYyICsgQkdFKS4iCiAgICAgICAgKSwKICAgICkKICAgIHBhcnNlci5hZGRfYXJndW1lbnQoCiAgICAgICAgIi0tamluYS1xdWFudGl6YXRpb24iLAogICAgICAgIGNob2ljZXM9WyJkZWZhdWx0IiwgImR5bmFtaWMtaW50OCJdLAogICAgICAgIGhlbHA9IlF1YW50aXphY2FvIHBhcmEgSmluYTogJ2RlZmF1bHQnIChtYWlzIHF1YWxpZGFkZSkgb3UgJ2R5bmFtaWMtaW50OCcgKG1haXMgdmVsb2NpZGFkZSkuIiwKICAgICkKICAgIHBhcnNlci5hZGRfYXJndW1lbnQoCiAgICAgICAgIi0tcGVyZi1wcm9maWxlIiwKICAgICAgICBjaG9pY2VzPVsiYXV0b3R1bmUiLCAibWF4LXBlcmZvcm1hbmNlIl0sCiAgICAgICAgaGVscD0oCiAgICAgICAgICAgICJQZXJmaWwgZGUgcGVyZm9ybWFuY2UgZGEgaW5kZXhhw6fDo286ICIKICAgICAgICAgICAgIidhdXRvdHVuZScgKGN1c3RvLWJlbmVmw61jaW8pIG91ICdtYXgtcGVyZm9ybWFuY2UnIChtYWlzIHRocm91Z2hwdXQsIG1haW9yIHVzbyBkZSBSQU0pLiIKICAgICAgICApLAogICAgKQogICAgYXJncyA9IHBhcnNlci5wYXJzZV9hcmdzKCkKCiAgICByb290X3BhdGggPSBQYXRoKGFyZ3MucHJvamVjdF9wYXRoKS5yZXNvbHZlKCkKICAgIGlmIG5vdCByb290X3BhdGguaXNfZGlyKCk6CiAgICAgICAgcHJpbnQoZiJbRVJST10gQ2FtaW5obyBuw6NvIGV4aXN0ZSBvdSBuw6NvIMOpIHVtIGRpcmV0w7NyaW86IHtyb290X3BhdGh9IikKICAgICAgICBzeXMuZXhpdCgxKQoKICAgIHByaW50KGYiXG57Jz0nKjYwfSIpCiAgICBwcmludChmIiAgUkFHIEluZGV4ZXIg4oCUIFByb2pldG86IHtyb290X3BhdGh9IikKICAgIHByaW50KGYieyc9Jyo2MH1cbiIpCiAgICBpbmRleF9zdGFydGVkX2F0ID0gZGF0ZXRpbWUubm93KCkKICAgIHByaW50KGYiW0lORk9dIEluw61jaW86IHtpbmRleF9zdGFydGVkX2F0LnN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycpfSIpCgogICAgZm9yY2VfbW9kZWxfcmVjb25maWd1cmUgPSBfZW52X2Jvb2woIk1DUF9GT1JDRV9NT0RFTF9SRUNPTkZJRyIsIGRlZmF1bHQ9RmFsc2UpCiAgICBwZXJzaXN0ZWRfY29uZmlnID0gbG9hZF9pbmRleGVyX3R1bmluZ19jb25maWcoZm9yY2VfbW9kZWxfcmVjb25maWd1cmUpCiAgICBtb2RlbF9jaG9pY2UsIGppbmFfcXVhbnRpemF0aW9uID0gcmVzb2x2ZV9lbWJlZGRpbmdfY29uZmlnKAogICAgICAgIGFyZ3MuZW1iZWRkaW5nX21vZGVsLAogICAgICAgIGFyZ3MuamluYV9xdWFudGl6YXRpb24sCiAgICAgICAgcGVyc2lzdGVkX2NvbmZpZz1wZXJzaXN0ZWRfY29uZmlnLAogICAgKQogICAgcGVyZl9wcm9maWxlID0gcmVzb2x2ZV9wZXJmX3Byb2ZpbGUoYXJncy5wZXJmX3Byb2ZpbGUsIHBlcnNpc3RlZF9jb25maWcpCgogICAgY2h1bmtfc2l6ZV9sb2NrZWQgPSAiTUNQX0NIVU5LX1NJWkUiIGluIG9zLmVudmlyb24KICAgIGNodW5rX292ZXJsYXBfbG9ja2VkID0gIk1DUF9DSFVOS19PVkVSTEFQIiBpbiBvcy5lbnZpcm9uCiAgICBiYXRjaF9zaXplX2xvY2tlZCA9ICJNQ1BfRU1CRURESU5HX0JBVENIX1NJWkUiIGluIG9zLmVudmlyb24KCiAgICBwZXJzaXN0ZWRfY2h1bmtfc2l6ZSA9IF9wYXJzZV9jb25maWdfaW50KHBlcnNpc3RlZF9jb25maWcsICJjaHVua19zaXplIikKICAgIHBlcnNpc3RlZF9jaHVua19vdmVybGFwID0gX3BhcnNlX2NvbmZpZ19pbnQocGVyc2lzdGVkX2NvbmZpZywgImNodW5rX292ZXJsYXAiKQogICAgcGVyc2lzdGVkX2JhdGNoX3NpemUgPSBfcGFyc2VfY29uZmlnX2ludChwZXJzaXN0ZWRfY29uZmlnLCAiZW1iZWRkaW5nX2JhdGNoX3NpemUiKQoKICAgIGVmZmVjdGl2ZV9jaHVua19zaXplID0gQ0hVTktfU0laRQogICAgaWYgbm90IGNodW5rX3NpemVfbG9ja2VkIGFuZCBwZXJzaXN0ZWRfY2h1bmtfc2l6ZSBpcyBub3QgTm9uZToKICAgICAgICBlZmZlY3RpdmVfY2h1bmtfc2l6ZSA9IG1heCgyNTYsIHBlcnNpc3RlZF9jaHVua19zaXplKQoKICAgIGVmZmVjdGl2ZV9jaHVua19vdmVybGFwID0gQ0hVTktfT1ZFUkxBUAogICAgaWYgbm90IGNodW5rX292ZXJsYXBfbG9ja2VkIGFuZCBwZXJzaXN0ZWRfY2h1bmtfb3ZlcmxhcCBpcyBub3QgTm9uZToKICAgICAgICBlZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcCA9IG1heCgwLCBtaW4oZWZmZWN0aXZlX2NodW5rX3NpemUgLSAxLCBwZXJzaXN0ZWRfY2h1bmtfb3ZlcmxhcCkpCgogICAgZWZmZWN0aXZlX2JhdGNoX3NpemUgPSBFTUJFRERJTkdfQkFUQ0hfU0laRQogICAgaWYgbm90IGJhdGNoX3NpemVfbG9ja2VkIGFuZCBwZXJzaXN0ZWRfYmF0Y2hfc2l6ZSBpcyBub3QgTm9uZToKICAgICAgICBlZmZlY3RpdmVfYmF0Y2hfc2l6ZSA9IG1heCgxLCBwZXJzaXN0ZWRfYmF0Y2hfc2l6ZSkKCiAgICBwcmludCgKICAgICAgICBmIltDT05GSUddIE1vZGVsbyBlc2NvbGhpZG86IHttb2RlbF9jaG9pY2V9ICIKICAgICAgICBmIih7X2Rlc2NyaWJlX2VtYmVkZGluZ19jaG9pY2UobW9kZWxfY2hvaWNlKX0pIgogICAgKQogICAgaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hIjoKICAgICAgICBwcmludChmIltDT05GSUddIFF1YW50aXphY2FvIEppbmE6IHtqaW5hX3F1YW50aXphdGlvbn0iKQogICAgZWxpZiBtb2RlbF9jaG9pY2UgPT0gImh5YnJpZCI6CiAgICAgICAgcHJpbnQoIltDT05GSUddIFF1YW50aXphY2FvIEppbmE6IG5hbyBhcGxpY2F2ZWwgbm8gaHlicmlkIChKaW5hIHYyICsgQkdFKSIpCiAgICBlbHNlOgogICAgICAgIHByaW50KCJbQ09ORklHXSBRdWFudGl6YWNhbyBKaW5hOiBuYW8gYXBsaWNhdmVsIChtb2RlbG8gQkdFIHNlbGVjaW9uYWRvKSIpCiAgICBwcmludChmIltDT05GSUddIFBlcmZpbCBkZSBwZXJmb3JtYW5jZToge3BlcmZfcHJvZmlsZX0iKQogICAgaWYgcGVyZl9wcm9maWxlID09ICJtYXgtcGVyZm9ybWFuY2UiOgogICAgICAgIHByaW50KAogICAgICAgICAgICAiW0FWSVNPXSBFc3RlIG1vZG8gcG9kZSBlbGV2YXIgY29uc2lkZXJhdmVsbWVudGUgbyBjb25zdW1vIGRlIG1lbcOzcmlhICIKICAgICAgICAgICAgImUgY2F1c2FyIGVuY2VycmFtZW50byBwb3IgT09NIChleGl0IDEzNykuIgogICAgICAgICkKICAgIHdhcm5faWZfamluYV9tZW1vcnlfcmlzayhtb2RlbF9jaG9pY2UsIGppbmFfcXVhbnRpemF0aW9uKQoKICAgICMgSW5pY2lhbGl6YSBjb21wb25lbnRlcwogICAgY2xpZW50ID0gY29ubmVjdF90b19jaHJvbWEoKQogICAgdGFyZ2V0cyA9IF9yZXNvbHZlX2luZGV4X3RhcmdldHMobW9kZWxfY2hvaWNlKQoKICAgICMgT2J0w6ltIG91IHJlY3JpYSBhcyBjb2xlw6fDtWVzIGVudm9sdmlkYXMuCiAgICBjb2xsZWN0aW9uczogZGljdFtzdHIsIGNocm9tYWRiLkNvbGxlY3Rpb25dID0ge30KICAgIGNvbGxlY3Rpb25fZGltZW5zaW9uX3Jlc2V0X2RvbmU6IGRpY3Rbc3RyLCBib29sXSA9IHt9CiAgICBmb3IgdGFyZ2V0IGluIHRhcmdldHM6CiAgICAgICAgaWYgYXJncy5jbGVhcjoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgY2xpZW50LmRlbGV0ZV9jb2xsZWN0aW9uKHRhcmdldC5jb2xsZWN0aW9uX25hbWUpCiAgICAgICAgICAgICAgICBwcmludChmIlshXSBDb2xlw6fDo28gJ3t0YXJnZXQuY29sbGVjdGlvbl9uYW1lfScgcmVtb3ZpZGEgcGFyYSByZWluZGV4YcOnw6NvIGxpbXBhLiIpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgICAgICBwYXNzCiAgICAgICAgY29sbGVjdGlvbnNbdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0gPSBjbGllbnQuZ2V0X29yX2NyZWF0ZV9jb2xsZWN0aW9uKAogICAgICAgICAgICBuYW1lPXRhcmdldC5jb2xsZWN0aW9uX25hbWUsCiAgICAgICAgICAgIG1ldGFkYXRhPXsiaG5zdzpzcGFjZSI6ICJjb3NpbmUifSwKICAgICAgICApCiAgICAgICAgY29sbGVjdGlvbl9kaW1lbnNpb25fcmVzZXRfZG9uZVt0YXJnZXQuY29sbGVjdGlvbl9uYW1lXSA9IEZhbHNlCgogICAgIyBDYXJyZWdhIG1vZGVsb3MgZGUgZm9ybWEgbGF6eSBlIHJlYXByb3ZlaXRhIHBvciB0YXJnZXQuCiAgICBsb2FkZWRfbW9kZWxzOiBkaWN0W3N0ciwgU2VudGVuY2VUcmFuc2Zvcm1lcl0gPSB7fQogICAgdG90YWxfY2h1bmtzID0gMAogICAgZXJyb3JzID0gMAogICAgZmlsZXNfc2Nhbm5lZCA9IDAKICAgIGZpbGVzX3Byb2Nlc3NlZF90b3RhbCA9IDAKICAgIGNodW5rc19ieV9jb2xsZWN0aW9uID0ge3RhcmdldC5jb2xsZWN0aW9uX25hbWU6IDAgZm9yIHRhcmdldCBpbiB0YXJnZXRzfQogICAgZmlsZXNfYnlfY29sbGVjdGlvbiA9IHt0YXJnZXQuY29sbGVjdGlvbl9uYW1lOiAwIGZvciB0YXJnZXQgaW4gdGFyZ2V0c30KICAgIGZpbGVzX2VsaWdpYmxlX2J5X2NvbGxlY3Rpb24gPSB7dGFyZ2V0LmNvbGxlY3Rpb25fbmFtZTogMCBmb3IgdGFyZ2V0IGluIHRhcmdldHN9CiAgICBlcnJvcnNfYnlfY29sbGVjdGlvbiA9IHt0YXJnZXQuY29sbGVjdGlvbl9uYW1lOiAwIGZvciB0YXJnZXQgaW4gdGFyZ2V0c30KICAgIGVycm9yX3NhbXBsZXNfYnlfY29sbGVjdGlvbjogZGljdFtzdHIsIGxpc3Rbc3RyXV0gPSB7dGFyZ2V0LmNvbGxlY3Rpb25fbmFtZTogW10gZm9yIHRhcmdldCBpbiB0YXJnZXRzfQogICAgdGFyZ2V0X2J5X21vZGVsID0ge3RhcmdldC5tb2RlbF9jaG9pY2U6IHRhcmdldCBmb3IgdGFyZ2V0IGluIHRhcmdldHN9CgogICAgIyBDYXJyZWdhIG8gcHJpbWVpcm8gbW9kZWxvIGFudGVzIHBhcmEgYXV0b3R1bmUgY29tIG1pY3JvLWJlbmNobWFyay4KICAgIHByaW1hcnlfdGFyZ2V0ID0gdGFyZ2V0c1swXQogICAgcHJpbWFyeV9xdWFudGl6YXRpb24gPSBqaW5hX3F1YW50aXphdGlvbiBpZiBwcmltYXJ5X3RhcmdldC5tb2RlbF9jaG9pY2UgPT0gImppbmEiIGVsc2UgImRlZmF1bHQiCiAgICBsb2FkZWRfbW9kZWxzW3ByaW1hcnlfdGFyZ2V0Lm1vZGVsX2Nob2ljZV0gPSBsb2FkX2VtYmVkZGluZ19tb2RlbChwcmltYXJ5X3RhcmdldC5tb2RlbF9jaG9pY2UsIHByaW1hcnlfcXVhbnRpemF0aW9uKQogICAgcHJpbWFyeV9tb2RlbCA9IGxvYWRlZF9tb2RlbHNbcHJpbWFyeV90YXJnZXQubW9kZWxfY2hvaWNlXQoKICAgIGlmIHBlcmZfcHJvZmlsZSA9PSAiYXV0b3R1bmUiOgogICAgICAgIHR1bmVkID0gX3Jlc29sdmVfYXV0b3R1bmVkX3BhcmFtcygKICAgICAgICAgICAgbW9kZWw9cHJpbWFyeV9tb2RlbCwKICAgICAgICAgICAgY2h1bmtfc2l6ZV9sb2NrZWQ9Y2h1bmtfc2l6ZV9sb2NrZWQsCiAgICAgICAgICAgIGNodW5rX292ZXJsYXBfbG9ja2VkPWNodW5rX292ZXJsYXBfbG9ja2VkLAogICAgICAgICAgICBiYXRjaF9zaXplX2xvY2tlZD1iYXRjaF9zaXplX2xvY2tlZCwKICAgICAgICAgICAgY2h1bmtfc2l6ZT1lZmZlY3RpdmVfY2h1bmtfc2l6ZSwKICAgICAgICAgICAgY2h1bmtfb3ZlcmxhcD1lZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcCwKICAgICAgICAgICAgZW1iZWRkaW5nX2JhdGNoX3NpemU9ZWZmZWN0aXZlX2JhdGNoX3NpemUsCiAgICAgICAgKQogICAgZWxzZToKICAgICAgICB0dW5lZCA9IF9yZXNvbHZlX21heF9wZXJmb3JtYW5jZV9wYXJhbXMoCiAgICAgICAgICAgIGNodW5rX3NpemVfbG9ja2VkPWNodW5rX3NpemVfbG9ja2VkLAogICAgICAgICAgICBjaHVua19vdmVybGFwX2xvY2tlZD1jaHVua19vdmVybGFwX2xvY2tlZCwKICAgICAgICAgICAgYmF0Y2hfc2l6ZV9sb2NrZWQ9YmF0Y2hfc2l6ZV9sb2NrZWQsCiAgICAgICAgICAgIGNodW5rX3NpemU9ZWZmZWN0aXZlX2NodW5rX3NpemUsCiAgICAgICAgICAgIGNodW5rX292ZXJsYXA9ZWZmZWN0aXZlX2NodW5rX292ZXJsYXAsCiAgICAgICAgICAgIGVtYmVkZGluZ19iYXRjaF9zaXplPWVmZmVjdGl2ZV9iYXRjaF9zaXplLAogICAgICAgICkKCiAgICBlZmZlY3RpdmVfY2h1bmtfc2l6ZSA9IG1heCgyNTYsIHR1bmVkLmNodW5rX3NpemUpCiAgICBlZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcCA9IG1heCgwLCBtaW4oZWZmZWN0aXZlX2NodW5rX3NpemUgLSAxLCB0dW5lZC5jaHVua19vdmVybGFwKSkKICAgIGVmZmVjdGl2ZV9iYXRjaF9zaXplID0gbWF4KDEsIHR1bmVkLmVtYmVkZGluZ19iYXRjaF9zaXplKQoKICAgIGZvciByZWFzb24gaW4gdHVuZWQucmVhc29uczoKICAgICAgICBwcmludChmIltDT05GSUddIHtyZWFzb259IikKCiAgICBwcmludCgKICAgICAgICBmIltDT05GSUddIFBhcsOibWV0cm9zIGZpbmFpczogIgogICAgICAgIGYiY2h1bmtfc2l6ZT17ZWZmZWN0aXZlX2NodW5rX3NpemV9LCBjaHVua19vdmVybGFwPXtlZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcH0sICIKICAgICAgICBmImVtYmVkZGluZ19iYXRjaD17ZWZmZWN0aXZlX2JhdGNoX3NpemV9IgogICAgKQoKICAgIHNhdmVfaW5kZXhlcl90dW5pbmdfY29uZmlnKAogICAgICAgIHsKICAgICAgICAgICAgImVtYmVkZGluZ19tb2RlbCI6IG1vZGVsX2Nob2ljZSwKICAgICAgICAgICAgImppbmFfcXVhbnRpemF0aW9uIjogamluYV9xdWFudGl6YXRpb24sCiAgICAgICAgICAgICJwZXJmX3Byb2ZpbGUiOiBwZXJmX3Byb2ZpbGUsCiAgICAgICAgICAgICJjaHVua19zaXplIjogZWZmZWN0aXZlX2NodW5rX3NpemUsCiAgICAgICAgICAgICJjaHVua19vdmVybGFwIjogZWZmZWN0aXZlX2NodW5rX292ZXJsYXAsCiAgICAgICAgICAgICJlbWJlZGRpbmdfYmF0Y2hfc2l6ZSI6IGVmZmVjdGl2ZV9iYXRjaF9zaXplLAogICAgICAgIH0KICAgICkKCiAgICBzcGxpdHRlciA9IGdldF90ZXh0X3NwbGl0dGVyKGVmZmVjdGl2ZV9jaHVua19zaXplLCBlZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcCkKCiAgICBwcmludChmIlxuWytdIFZhcnJlbmRvIGUgaW5kZXhhbmRvIGFycXVpdm9zIGVtOiB7cm9vdF9wYXRofSIpCiAgICBmaWxlcyA9IGxpc3Qoc2Nhbl9maWxlcyhyb290X3BhdGgpKQogICAgZmlsZXNfc2Nhbm5lZCA9IGxlbihmaWxlcykKICAgIGlmIGZpbGVzX3NjYW5uZWQgPT0gMDoKICAgICAgICBwcmludCgiW0FWSVNPXSBOZW5odW0gYXJxdWl2byBlbmNvbnRyYWRvLiBWZXJpZmlxdWUgbyBjYW1pbmhvIGUgb3MgZmlsdHJvcy4iKQogICAgICAgIHN5cy5leGl0KDApCgogICAgcHJpbnQoZiJbK10ge2ZpbGVzX3NjYW5uZWR9IGFycXVpdm8ocykgZWxlZ8OtdmVsKGlzKSBwYXJhIGluZGV4YcOnw6NvLiIpCiAgICB3aXRoIHRxZG0oCiAgICAgICAgdG90YWw9ZmlsZXNfc2Nhbm5lZCwKICAgICAgICBkZXNjPSJJbmRleGFuZG8iLAogICAgICAgIHVuaXQ9ImFycXVpdm8iLAogICAgICAgIGJhcl9mb3JtYXQ9IntsX2Jhcn17YmFyfXwge25fZm10fS97dG90YWxfZm10fSBbe2VsYXBzZWR9LCB7cmF0ZV9mbXR9XSIsCiAgICApIGFzIHBiYXI6CiAgICAgICAgZm9yIGZpbGVwYXRoIGluIGZpbGVzOgogICAgICAgICAgICB0YXJnZXRfbW9kZWxzID0gX2NsYXNzaWZ5X2ZpbGVfdGFyZ2V0cyhmaWxlcGF0aCwgbW9kZWxfY2hvaWNlKQoKICAgICAgICAgICAgZm9yIHRhcmdldF9tb2RlbCBpbiB0YXJnZXRfbW9kZWxzOgogICAgICAgICAgICAgICAgdGFyZ2V0ID0gdGFyZ2V0X2J5X21vZGVsLmdldCh0YXJnZXRfbW9kZWwpCiAgICAgICAgICAgICAgICBpZiB0YXJnZXQgaXMgTm9uZToKICAgICAgICAgICAgICAgICAgICBjb250aW51ZQoKICAgICAgICAgICAgICAgIGlmIHRhcmdldC5tb2RlbF9jaG9pY2Ugbm90IGluIGxvYWRlZF9tb2RlbHM6CiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X3F1YW50aXphdGlvbiA9IGppbmFfcXVhbnRpemF0aW9uIGlmIHRhcmdldC5tb2RlbF9jaG9pY2UgPT0gImppbmEiIGVsc2UgImRlZmF1bHQiCiAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICBsb2FkZWRfbW9kZWxzW3RhcmdldC5tb2RlbF9jaG9pY2VdID0gbG9hZF9lbWJlZGRpbmdfbW9kZWwoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXQubW9kZWxfY2hvaWNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X3F1YW50aXphdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgbG9hZF9lcnJvcjoKICAgICAgICAgICAgICAgICAgICAgICAgIyBFbSBoeWJyaWQsIHBvZGUgZmFsdGFyIFJBTSBhbyBtYW50ZXIgZG9pcyBtb2RlbG9zIGdyYW5kZXMgc2ltdWx0YW5lYW1lbnRlLgogICAgICAgICAgICAgICAgICAgICAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImh5YnJpZCIgYW5kIGxvYWRlZF9tb2RlbHMgYW5kIF9pc19tZW1vcnlfcmVsYXRlZF9lcnJvcihsb2FkX2Vycm9yKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW50KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJbQVZJU09dIEZhbGhhIGFvIGNhcnJlZ2FyIG1vZGVsbyBhZGljaW9uYWwgbm8gaHlicmlkIHBvciBtZW3Ds3JpYS4gIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaWJlcmFuZG8gbW9kZWxvIGFudGVyaW9yIGUgdGVudGFuZG8gbm92YW1lbnRlLiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlZF9tb2RlbHMuY2xlYXIoKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2MuY29sbGVjdCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkZWRfbW9kZWxzW3RhcmdldC5tb2RlbF9jaG9pY2VdID0gbG9hZF9lbWJlZGRpbmdfbW9kZWwoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0Lm1vZGVsX2Nob2ljZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfcXVhbnRpemF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFpc2UKCiAgICAgICAgICAgICAgICBtb2RlbCA9IGxvYWRlZF9tb2RlbHNbdGFyZ2V0Lm1vZGVsX2Nob2ljZV0KICAgICAgICAgICAgICAgIGNvbGxlY3Rpb24gPSBjb2xsZWN0aW9uc1t0YXJnZXQuY29sbGVjdGlvbl9uYW1lXQogICAgICAgICAgICAgICAgZmlsZXNfZWxpZ2libGVfYnlfY29sbGVjdGlvblt0YXJnZXQuY29sbGVjdGlvbl9uYW1lXSArPSAxCgogICAgICAgICAgICAgICAgd2hpbGUgVHJ1ZToKICAgICAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgICAgIG5fY2h1bmtzID0gaW5kZXhfZmlsZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVwYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGVjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXR0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb290X3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbWJlZGRpbmdfYmF0Y2hfc2l6ZT1lZmZlY3RpdmVfYmF0Y2hfc2l6ZSwKICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9jaHVua3MgKz0gbl9jaHVua3MKICAgICAgICAgICAgICAgICAgICAgICAgZmlsZXNfcHJvY2Vzc2VkX3RvdGFsICs9IDEKICAgICAgICAgICAgICAgICAgICAgICAgY2h1bmtzX2J5X2NvbGxlY3Rpb25bdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0gKz0gbl9jaHVua3MKICAgICAgICAgICAgICAgICAgICAgICAgZmlsZXNfYnlfY29sbGVjdGlvblt0YXJnZXQuY29sbGVjdGlvbl9uYW1lXSArPSAxCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgICAgICAjIEZhbGxiYWNrIGF1dG9tw6F0aWNvIHBhcmEgZXZpdGFyIHF1ZWJyYSB0b3RhbCBlbSBtw6FxdWluYXMgbm8gbGltaXRlIGRlIFJBTS4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm90IGJhdGNoX3NpemVfbG9ja2VkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgZWZmZWN0aXZlX2JhdGNoX3NpemUgPiAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgX2lzX21lbW9yeV9yZWxhdGVkX2Vycm9yKGUpCiAgICAgICAgICAgICAgICAgICAgICAgICk6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfYmF0Y2ggPSBtYXgoMSwgZWZmZWN0aXZlX2JhdGNoX3NpemUgLy8gMikKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIG5ld19iYXRjaCA8IGVmZmVjdGl2ZV9iYXRjaF9zaXplOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRxZG0ud3JpdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYiICBbQUpVU1RFXSBNZW3Ds3JpYSBhbHRhIGVtIHt0YXJnZXQubGFiZWx9LiAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYiQmF0Y2ggcmVkdXppZG8ge2VmZmVjdGl2ZV9iYXRjaF9zaXplfSAtPiB7bmV3X2JhdGNofS4iCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdGl2ZV9iYXRjaF9zaXplID0gbmV3X2JhdGNoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2MuY29sbGVjdCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWUKCiAgICAgICAgICAgICAgICAgICAgICAgIGlmICgKICAgICAgICAgICAgICAgICAgICAgICAgICAgIF9pc19kaW1lbnNpb25fbWlzbWF0Y2hfZXJyb3IoZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZCBub3QgY29sbGVjdGlvbl9kaW1lbnNpb25fcmVzZXRfZG9uZVt0YXJnZXQuY29sbGVjdGlvbl9uYW1lXQogICAgICAgICAgICAgICAgICAgICAgICApOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHFkbS53cml0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmIiAgW0FKVVNURV0gRGltZW5zw6NvIGluY29tcGF0w612ZWwgZGV0ZWN0YWRhIGVtICd7dGFyZ2V0LmNvbGxlY3Rpb25fbmFtZX0nLiAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJlY3JpYW5kbyBjb2xlw6fDo28gZSB0ZW50YW5kbyBub3ZhbWVudGUuIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaWVudC5kZWxldGVfY29sbGVjdGlvbih0YXJnZXQuY29sbGVjdGlvbl9uYW1lKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXNzCgogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGVjdGlvbnNbdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0gPSBjbGllbnQuZ2V0X29yX2NyZWF0ZV9jb2xsZWN0aW9uKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU9dGFyZ2V0LmNvbGxlY3Rpb25fbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YT17Imhuc3c6c3BhY2UiOiAiY29zaW5lIn0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsZWN0aW9uX2RpbWVuc2lvbl9yZXNldF9kb25lW3RhcmdldC5jb2xsZWN0aW9uX25hbWVdID0gVHJ1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2MuY29sbGVjdCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZQoKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JzICs9IDEKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JzX2J5X2NvbGxlY3Rpb25bdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0gKz0gMQogICAgICAgICAgICAgICAgICAgICAgICBpZiBsZW4oZXJyb3Jfc2FtcGxlc19ieV9jb2xsZWN0aW9uW3RhcmdldC5jb2xsZWN0aW9uX25hbWVdKSA8IDM6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvcl9zYW1wbGVzX2J5X2NvbGxlY3Rpb25bdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0uYXBwZW5kKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYie2ZpbGVwYXRoLm5hbWV9OiB7X2Zvcm1hdF9leGNlcHRpb24oZSl9IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICB0cWRtLndyaXRlKGYiICBbRVJST10ge2ZpbGVwYXRofSBbe3RhcmdldC5sYWJlbH1dOiB7X2Zvcm1hdF9leGNlcHRpb24oZSl9IikKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKCiAgICAgICAgICAgIHBiYXIuc2V0X3Bvc3RmaXgoeyJjaHVua3MiOiB0b3RhbF9jaHVua3MsICJhdHVhbCI6IGZpbGVwYXRoLm5hbWVbOjIwXX0pCiAgICAgICAgICAgIHBiYXIudXBkYXRlKDEpCgogICAgZm9yIHRhcmdldCBpbiB0YXJnZXRzOgogICAgICAgIGNvbGxlY3Rpb25fbmFtZSA9IHRhcmdldC5jb2xsZWN0aW9uX25hbWUKICAgICAgICBlbGlnaWJsZSA9IGZpbGVzX2VsaWdpYmxlX2J5X2NvbGxlY3Rpb25bY29sbGVjdGlvbl9uYW1lXQogICAgICAgIHByb2Nlc3NlZCA9IGZpbGVzX2J5X2NvbGxlY3Rpb25bY29sbGVjdGlvbl9uYW1lXQogICAgICAgIHRhcmdldF9lcnJvcnMgPSBlcnJvcnNfYnlfY29sbGVjdGlvbltjb2xsZWN0aW9uX25hbWVdCgogICAgICAgIGlmIGVsaWdpYmxlID09IDA6CiAgICAgICAgICAgIHByaW50KGYiW0FWSVNPXSBOZW5odW0gYXJxdWl2byBlbGVnw612ZWwgcGFyYSB7dGFyZ2V0LmxhYmVsfTsgZXRhcGEgaWdub3JhZGEuIikKICAgICAgICBlbGlmIHByb2Nlc3NlZCA9PSAwIGFuZCB0YXJnZXRfZXJyb3JzID4gMDoKICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICBmIltBVklTT10ge2VsaWdpYmxlfSBhcnF1aXZvKHMpIGVsZWfDrXZlbChpcykgcGFyYSB7dGFyZ2V0LmxhYmVsfSwgIgogICAgICAgICAgICAgICAgIm1hcyB0b2RvcyBmYWxoYXJhbS4iCiAgICAgICAgICAgICkKCiAgICAgICAgaWYgdGFyZ2V0X2Vycm9yczoKICAgICAgICAgICAgcHJpbnQoZiJbQVZJU09dIHt0YXJnZXRfZXJyb3JzfSBlcnJvKHMpIGR1cmFudGUgYSBpbmRleGHDp8OjbyBkbyB0YXJnZXQge3RhcmdldC5sYWJlbH0uIikKICAgICAgICAgICAgZm9yIHNhbXBsZSBpbiBlcnJvcl9zYW1wbGVzX2J5X2NvbGxlY3Rpb25bY29sbGVjdGlvbl9uYW1lXToKICAgICAgICAgICAgICAgIHByaW50KGYiICAgICAgICAtIHtzYW1wbGV9IikKCiAgICBpbmRleF9maW5pc2hlZF9hdCA9IGRhdGV0aW1lLm5vdygpCiAgICBlbGFwc2VkX3NlY29uZHMgPSBpbnQoKGluZGV4X2ZpbmlzaGVkX2F0IC0gaW5kZXhfc3RhcnRlZF9hdCkudG90YWxfc2Vjb25kcygpKQogICAgZWxhcHNlZF9oID0gZWxhcHNlZF9zZWNvbmRzIC8vIDM2MDAKICAgIGVsYXBzZWRfbSA9IChlbGFwc2VkX3NlY29uZHMgJSAzNjAwKSAvLyA2MAogICAgZWxhcHNlZF9zID0gZWxhcHNlZF9zZWNvbmRzICUgNjAKICAgIHByaW50KGYiXG57Jz0nKjYwfSIpCiAgICBwcmludChmIiAgSW5kZXhhw6fDo28gY29uY2x1w61kYSEiKQogICAgcHJpbnQoZiIgIEluw61jaW8gICAgICAgICAgICAgICA6IHtpbmRleF9zdGFydGVkX2F0LnN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycpfSIpCiAgICBwcmludChmIiAgRmltICAgICAgICAgICAgICAgICAgOiB7aW5kZXhfZmluaXNoZWRfYXQuc3RyZnRpbWUoJyVZLSVtLSVkICVIOiVNOiVTJyl9IikKICAgIHByaW50KGYiICBEdXJhw6fDo28gICAgICAgICAgICAgIDoge2VsYXBzZWRfaDowMmR9OntlbGFwc2VkX206MDJkfTp7ZWxhcHNlZF9zOjAyZH0iKQogICAgcHJpbnQoZiIgIEFycXVpdm9zIHZhcnJpZG9zICAgIDoge2ZpbGVzX3NjYW5uZWR9IikKICAgIHByaW50KGYiICBBcnF1aXZvcyBwcm9jZXNzYWRvcyA6IHtmaWxlc19wcm9jZXNzZWRfdG90YWx9IikKICAgIHByaW50KGYiICBUb3RhbCBkZSBjaHVua3MgICAgICA6IHt0b3RhbF9jaHVua3N9IikKICAgIHByaW50KGYiICBFcnJvcyAgICAgICAgICAgICAgICA6IHtlcnJvcnN9IikKICAgIGZvciB0YXJnZXQgaW4gdGFyZ2V0czoKICAgICAgICBjb2xsZWN0aW9uX25hbWUgPSB0YXJnZXQuY29sbGVjdGlvbl9uYW1lCiAgICAgICAgcHJpbnQoCiAgICAgICAgICAgIGYiICBDb2xlw6fDo28gQ2hyb21hREIgICAgIDogJ3tjb2xsZWN0aW9uX25hbWV9JyAiCiAgICAgICAgICAgIGYiKGVsZWfDrXZlaXM9e2ZpbGVzX2VsaWdpYmxlX2J5X2NvbGxlY3Rpb24uZ2V0KGNvbGxlY3Rpb25fbmFtZSwgMCl9LCAiCiAgICAgICAgICAgIGYiYXJxdWl2b3M9e2ZpbGVzX2J5X2NvbGxlY3Rpb24uZ2V0KGNvbGxlY3Rpb25fbmFtZSwgMCl9LCAiCiAgICAgICAgICAgIGYiY2h1bmtzPXtjaHVua3NfYnlfY29sbGVjdGlvbi5nZXQoY29sbGVjdGlvbl9uYW1lLCAwKX0pIgogICAgICAgICkKICAgIHByaW50KGYieyc9Jyo2MH1cbiIpCgoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIHRyeToKICAgICAgICBtYWluKCkKICAgIGV4Y2VwdCBNZW1vcnlFcnJvcjoKICAgICAgICBwcmludCgKICAgICAgICAgICAgIltFUlJPXSBGYWxoYSBkZSBtZW3Ds3JpYSBkdXJhbnRlIGEgaW5kZXhhw6fDo28uICIKICAgICAgICAgICAgIlVzZSAtLWVtYmVkZGluZy1tb2RlbCBiZ2Ugb3UgZXhlY3V0ZSBvIEppbmEgZW0gbcOhcXVpbmEgY29tIG1haXMgUkFNL3N3YXAuIgogICAgICAgICkKICAgICAgICBzeXMuZXhpdCgxKQo="
564
+ B64_MCP="IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCiIiIgptY3Bfc2VydmVyLnB5IOKAlCBTZXJ2aWRvciBNQ1AgcGFyYSBSQUcgbG9jYWwgZGUgY29kZWJhc2UuCgpFeHDDtWUgZmVycmFtZW50YXMgZGUgYnVzY2Egc2Vtw6JudGljYSBlIGluZGV4YcOnw6NvIHZpYSBzdGRpbyBwYXJhIG8gQ2xhdWRlIENvZGUgQ0xJLgpDb25lY3RhLXNlIGFvIENocm9tYURCIHJvZGFuZG8gZW0gRG9ja2VyIChsb2NhbGhvc3Q6ODAwMCkuCgpOb3ZpZGFkZTogbW9kbyBow61icmlkbyBlbnNlbWJsZSBjb20gZHVhcyBjb2xlw6fDtWVzIHNlcGFyYWRhcyArIFJSRiArIHJlcmFua2luZyBsZXZlLgoiIiIKCmltcG9ydCBzeXMKaW1wb3J0IG9zCmltcG9ydCBoYXNobGliCmltcG9ydCBqc29uCmltcG9ydCBsb2dnaW5nCmltcG9ydCBnZXRwYXNzCmltcG9ydCBzaHV0aWwKZnJvbSBjb2xsZWN0aW9ucy5hYmMgaW1wb3J0IEl0ZXJhdG9yCmZyb20gY29uY3VycmVudC5mdXR1cmVzIGltcG9ydCBUaHJlYWRQb29sRXhlY3V0b3IsIGFzX2NvbXBsZXRlZApmcm9tIGRhdGFjbGFzc2VzIGltcG9ydCBkYXRhY2xhc3MKZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUsIHRpbWV6b25lCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAoKIyBFdml0YSBtZW5zYWdlbnMgYWR2aXNvcnkgZG8gdHJhbnNmb3JtZXJzIGVtIHN0ZGVyciBkdXJhbnRlIGEgY2FyZ2EgZG8gbW9kZWxvLgpvcy5lbnZpcm9uLnNldGRlZmF1bHQoIlRSQU5TRk9STUVSU19OT19BRFZJU09SWV9XQVJOSU5HUyIsICIxIikKCgpjbGFzcyBfVG9yY2hEdHlwZVdhcm5pbmdGaWx0ZXIobG9nZ2luZy5GaWx0ZXIpOgogICAgZGVmIGZpbHRlcihzZWxmLCByZWNvcmQ6IGxvZ2dpbmcuTG9nUmVjb3JkKSAtPiBib29sOgogICAgICAgIHJldHVybiAiYHRvcmNoX2R0eXBlYCBpcyBkZXByZWNhdGVkISBVc2UgYGR0eXBlYCBpbnN0ZWFkISIgbm90IGluIHJlY29yZC5nZXRNZXNzYWdlKCkKCgpmb3IgX2xvZ2dlcl9uYW1lIGluICgidHJhbnNmb3JtZXJzLmNvbmZpZ3VyYXRpb25fdXRpbHMiLCAidHJhbnNmb3JtZXJzLm1vZGVsaW5nX3V0aWxzIik6CiAgICBsb2dnaW5nLmdldExvZ2dlcihfbG9nZ2VyX25hbWUpLmFkZEZpbHRlcihfVG9yY2hEdHlwZVdhcm5pbmdGaWx0ZXIoKSkKCmltcG9ydCBjaHJvbWFkYgpmcm9tIHNlbnRlbmNlX3RyYW5zZm9ybWVycyBpbXBvcnQgQ3Jvc3NFbmNvZGVyLCBTZW50ZW5jZVRyYW5zZm9ybWVyCmZyb20gbGFuZ2NoYWluX3RleHRfc3BsaXR0ZXJzIGltcG9ydCBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIKZnJvbSBtY3Auc2VydmVyLmZhc3RtY3AgaW1wb3J0IEZhc3RNQ1AKZnJvbSBkb3dubG9hZF9tb2RlbF9mcm9tX2h1Z2dpbmZhY2UgaW1wb3J0IGRvd25sb2FkX21vZGVsX3dpdGhfZmFsbGJhY2sKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQ29uZmlndXJhw6fDo28gZGUgbG9nZ2luZyAoc3RkZXJyIHBhcmEgbsOjbyBwb2x1aXIgbyBwcm90b2NvbG8gc3RkaW8pCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpsb2dnaW5nLmJhc2ljQ29uZmlnKAogICAgbGV2ZWw9bG9nZ2luZy5JTkZPLAogICAgZm9ybWF0PSJbTUNQLVJBR10gJShhc2N0aW1lKXMgJShsZXZlbG5hbWUpczogJShtZXNzYWdlKXMiLAogICAgc3RyZWFtPXN5cy5zdGRlcnIsCikKbG9nID0gbG9nZ2luZy5nZXRMb2dnZXIoX19uYW1lX18pCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIExvZyBlc3RydXR1cmFkbyBkZSB1c28gTUNQIChKU09OTCkKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCk1DUF9VU0FHRV9MT0dfUEFUSCA9IFBhdGgoCiAgICBvcy5lbnZpcm9uLmdldCgiTUNQX1VTQUdFX0xPRyIsIHN0cihQYXRoLmhvbWUoKSAvICIucmFnX2RiIiAvICJtY3BfdXNhZ2UubG9nIikpCikuZXhwYW5kdXNlcigpCgoKZGVmIF9zYWZlX3ByZXZpZXcodmFsdWU6IHN0ciwgbGltaXQ6IGludCA9IDEyMCkgLT4gc3RyOgogICAgaWYgbGVuKHZhbHVlKSA8PSBsaW1pdDoKICAgICAgICByZXR1cm4gdmFsdWUKICAgIHJldHVybiB2YWx1ZVs6bGltaXRdICsgIi4uLlt0cnVuY2F0ZWRdIgoKCmRlZiBfZ2V0X3BhcmVudF9jbWRsaW5lKCkgLT4gc3RyOgogICAgcHBpZCA9IG9zLmdldHBwaWQoKQogICAgY21kbGluZV9wYXRoID0gUGF0aChmIi9wcm9jL3twcGlkfS9jbWRsaW5lIikKICAgIHRyeToKICAgICAgICByYXcgPSBjbWRsaW5lX3BhdGgucmVhZF9ieXRlcygpCiAgICAgICAgaWYgbm90IHJhdzoKICAgICAgICAgICAgcmV0dXJuICJ1bmtub3duIgogICAgICAgIHBhcnRzID0gW3AuZGVjb2RlKCJ1dGYtOCIsIGVycm9ycz0iaWdub3JlIikgZm9yIHAgaW4gcmF3LnNwbGl0KGIiXHgwMCIpIGlmIHBdCiAgICAgICAgcmV0dXJuICIgIi5qb2luKHBhcnRzKSBpZiBwYXJ0cyBlbHNlICJ1bmtub3duIgogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICByZXR1cm4gInVua25vd24iCgoKZGVmIF9pbmZlcl9hY3RvcigpIC0+IGRpY3Rbc3RyLCBzdHJdOgogICAgYWN0b3IgPSBvcy5lbnZpcm9uLmdldCgiTUNQX0NMSUVOVF9OQU1FIikgb3Igb3MuZW52aXJvbi5nZXQoIkNMQVVERV9VU0VSIikgb3IgZ2V0cGFzcy5nZXR1c2VyKCkKICAgIHNvdXJjZSA9ICgKICAgICAgICAiTUNQX0NMSUVOVF9OQU1FIiBpZiBvcy5lbnZpcm9uLmdldCgiTUNQX0NMSUVOVF9OQU1FIikKICAgICAgICBlbHNlICJDTEFVREVfVVNFUiIgaWYgb3MuZW52aXJvbi5nZXQoIkNMQVVERV9VU0VSIikKICAgICAgICBlbHNlICJzeXN0ZW1fdXNlciIKICAgICkKICAgIHJldHVybiB7CiAgICAgICAgImFjdG9yIjogYWN0b3IsCiAgICAgICAgImFjdG9yX3NvdXJjZSI6IHNvdXJjZSwKICAgICAgICAiY2xpZW50X3Byb2Nlc3MiOiBfZ2V0X3BhcmVudF9jbWRsaW5lKCksCiAgICB9CgoKZGVmIF9sb2dfdG9vbF91c2FnZShldmVudDogc3RyLCB0b29sX25hbWU6IHN0ciwgZGV0YWlsczogZGljdFtzdHIsIG9iamVjdF0gfCBOb25lID0gTm9uZSkgLT4gTm9uZToKICAgIHRyeToKICAgICAgICBNQ1BfVVNBR0VfTE9HX1BBVEgucGFyZW50Lm1rZGlyKHBhcmVudHM9VHJ1ZSwgZXhpc3Rfb2s9VHJ1ZSkKICAgICAgICBwYXlsb2FkOiBkaWN0W3N0ciwgb2JqZWN0XSA9IHsKICAgICAgICAgICAgInRpbWVzdGFtcCI6IGRhdGV0aW1lLm5vdyh0aW1lem9uZS51dGMpLmlzb2Zvcm1hdCgpLAogICAgICAgICAgICAiZXZlbnQiOiBldmVudCwKICAgICAgICAgICAgInRvb2wiOiB0b29sX25hbWUsCiAgICAgICAgICAgICJwaWQiOiBvcy5nZXRwaWQoKSwKICAgICAgICAgICAgKipfaW5mZXJfYWN0b3IoKSwKICAgICAgICB9CiAgICAgICAgaWYgZGV0YWlsczoKICAgICAgICAgICAgcGF5bG9hZFsiZGV0YWlscyJdID0gZGV0YWlscwoKICAgICAgICB3aXRoIE1DUF9VU0FHRV9MT0dfUEFUSC5vcGVuKCJhIiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgICAgICAgICAgZi53cml0ZShqc29uLmR1bXBzKHBheWxvYWQsIGVuc3VyZV9hc2NpaT1UcnVlKSArICJcbiIpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgbG9nLndhcm5pbmcoIkZhbGhhIGFvIHJlZ2lzdHJhciB1c28gTUNQIGVtICVzOiAlcyIsIE1DUF9VU0FHRV9MT0dfUEFUSCwgZSkKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENvbmZpZ3VyYcOnw7VlcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCklOREVYRVJfQ09ORklHX1BBVEggPSBQYXRoKAogICAgb3MuZW52aXJvbi5nZXQoIk1DUF9JTkRFWEVSX0NPTkZJR19GSUxFIiwgc3RyKFBhdGguaG9tZSgpIC8gIi5yYWdfZGIiIC8gImluZGV4ZXJfdHVuaW5nLmpzb24iKSkKKS5leHBhbmR1c2VyKCkKCgpkZWYgX2xvYWRfaW5kZXhlcl90dW5pbmdfY29uZmlnKCkgLT4gZGljdFtzdHIsIG9iamVjdF06CiAgICB0cnk6CiAgICAgICAgaWYgbm90IElOREVYRVJfQ09ORklHX1BBVEguZXhpc3RzKCk6CiAgICAgICAgICAgIHJldHVybiB7fQogICAgICAgIHBheWxvYWQgPSBqc29uLmxvYWRzKElOREVYRVJfQ09ORklHX1BBVEgucmVhZF90ZXh0KGVuY29kaW5nPSJ1dGYtOCIpKQogICAgICAgIHJldHVybiBwYXlsb2FkIGlmIGlzaW5zdGFuY2UocGF5bG9hZCwgZGljdCkgZWxzZSB7fQogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICByZXR1cm4ge30KCgpJTkRFWEVSX1RVTklOR19DT05GSUcgPSBfbG9hZF9pbmRleGVyX3R1bmluZ19jb25maWcoKQoKCmRlZiBfY29uZmlnX3N0cihlbnZfbmFtZTogc3RyLCBjb25maWdfa2V5OiBzdHIsIGRlZmF1bHQ6IHN0cikgLT4gc3RyOgogICAgZW52X3JhdyA9IG9zLmVudmlyb24uZ2V0KGVudl9uYW1lKQogICAgaWYgZW52X3JhdyBpcyBub3QgTm9uZSBhbmQgZW52X3Jhdy5zdHJpcCgpOgogICAgICAgIHJldHVybiBlbnZfcmF3CiAgICBjZmdfcmF3ID0gSU5ERVhFUl9UVU5JTkdfQ09ORklHLmdldChjb25maWdfa2V5KQogICAgaWYgaXNpbnN0YW5jZShjZmdfcmF3LCBzdHIpIGFuZCBjZmdfcmF3LnN0cmlwKCk6CiAgICAgICAgcmV0dXJuIGNmZ19yYXcKICAgIHJldHVybiBkZWZhdWx0CgoKZGVmIF9jb25maWdfaW50KGVudl9uYW1lOiBzdHIsIGNvbmZpZ19rZXk6IHN0ciwgZGVmYXVsdDogaW50LCAqLCBtaW5fdmFsdWU6IGludCA9IDEpIC0+IGludDoKICAgIGVudl9yYXcgPSBvcy5lbnZpcm9uLmdldChlbnZfbmFtZSkKICAgIGlmIGVudl9yYXcgaXMgbm90IE5vbmUgYW5kIGVudl9yYXcuc3RyaXAoKToKICAgICAgICB0cnk6CiAgICAgICAgICAgIHJldHVybiBtYXgobWluX3ZhbHVlLCBpbnQoZW52X3JhdykpCiAgICAgICAgZXhjZXB0IFZhbHVlRXJyb3I6CiAgICAgICAgICAgIHBhc3MKCiAgICBjZmdfcmF3ID0gSU5ERVhFUl9UVU5JTkdfQ09ORklHLmdldChjb25maWdfa2V5KQogICAgaWYgaXNpbnN0YW5jZShjZmdfcmF3LCBpbnQpOgogICAgICAgIHJldHVybiBtYXgobWluX3ZhbHVlLCBjZmdfcmF3KQogICAgaWYgaXNpbnN0YW5jZShjZmdfcmF3LCBzdHIpOgogICAgICAgIHRyeToKICAgICAgICAgICAgcmV0dXJuIG1heChtaW5fdmFsdWUsIGludChjZmdfcmF3KSkKICAgICAgICBleGNlcHQgVmFsdWVFcnJvcjoKICAgICAgICAgICAgcGFzcwoKICAgIHJldHVybiBtYXgobWluX3ZhbHVlLCBkZWZhdWx0KQoKCkNIUk9NQV9IT1NUID0gb3MuZW52aXJvbi5nZXQoIkNIUk9NQV9IT1NUIiwgImxvY2FsaG9zdCIpCkNIUk9NQV9QT1JUID0gaW50KG9zLmVudmlyb24uZ2V0KCJDSFJPTUFfUE9SVCIsICI4MDAwIikpCgojIENvbGXDp8O1ZXMgc2VwYXJhZGFzIHBvciBlc3BlY2lhbGl6YcOnw6NvIGRlIGVtYmVkZGluZwpDT0xMRUNUSU9OX0NPREVfSklOQSA9ICJjb2RlX3ZlY3RvcnNfamluYSIKQ09MTEVDVElPTl9ET0NfQkdFID0gImRvY192ZWN0b3JzX2JnZSIKCkpJTkFfVjNfRU1CRURESU5HX01PREVMID0gImppbmFhaS9qaW5hLWVtYmVkZGluZ3MtdjMiCkpJTkFfVjJfRU1CRURESU5HX01PREVMID0gImppbmFhaS9qaW5hLWVtYmVkZGluZ3MtdjItYmFzZS1jb2RlIgpCR0VfRU1CRURESU5HX01PREVMID0gIkJBQUkvYmdlLW0zIgoKREVGQVVMVF9FTUJFRERJTkdfTU9ERUxfQ0hPSUNFID0gImppbmEiCkRFRkFVTFRfSklOQV9RVUFOVElaQVRJT04gPSAiZHluYW1pYy1pbnQ4IgpERUZBVUxUX1NFQVJDSF9NT0RFID0gInNpbmdsZSIgICMgc2luZ2xlIHwgZW5zZW1ibGUKCl9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID0gX2NvbmZpZ19zdHIoCiAgICAiTUNQX0VNQkVERElOR19NT0RFTCIsCiAgICAiZW1iZWRkaW5nX21vZGVsIiwKICAgIERFRkFVTFRfRU1CRURESU5HX01PREVMX0NIT0lDRSwKKS5zdHJpcCgpLmxvd2VyKCkKaWYgX2VtYmVkZGluZ19tb2RlbF9jaG9pY2Ugbm90IGluIHsiamluYSIsICJiZ2UiLCAiaHlicmlkIn06CiAgICBsb2cud2FybmluZygKICAgICAgICAiTUNQX0VNQkVERElOR19NT0RFTCBpbnZhbGlkbyAnJXMnLiBVc2FuZG8gJyVzJy4iLAogICAgICAgIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlLAogICAgICAgIERFRkFVTFRfRU1CRURESU5HX01PREVMX0NIT0lDRSwKICAgICkKICAgIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID0gREVGQVVMVF9FTUJFRERJTkdfTU9ERUxfQ0hPSUNFCgpfcmF3X2ppbmFfcXVhbnRpemF0aW9uID0gX2NvbmZpZ19zdHIoCiAgICAiTUNQX0pJTkFfUVVBTlRJWkFUSU9OIiwKICAgICJqaW5hX3F1YW50aXphdGlvbiIsCiAgICBERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OLAopCkpJTkFfUVVBTlRJWkFUSU9OID0gX3Jhd19qaW5hX3F1YW50aXphdGlvbi5zdHJpcCgpLmxvd2VyKCkucmVwbGFjZSgiXyIsICItIikKaWYgSklOQV9RVUFOVElaQVRJT04gbm90IGluIHsiZGVmYXVsdCIsICJkeW5hbWljLWludDgifToKICAgIGxvZy53YXJuaW5nKAogICAgICAgICJNQ1BfSklOQV9RVUFOVElaQVRJT04gaW52YWxpZG8gJyVzJy4gVXNhbmRvICclcycuIiwKICAgICAgICBKSU5BX1FVQU5USVpBVElPTiwKICAgICAgICBERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OLAogICAgKQogICAgSklOQV9RVUFOVElaQVRJT04gPSBERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OCgpTRUFSQ0hfTU9ERV9ERUZBVUxUID0gb3MuZW52aXJvbi5nZXQoIk1DUF9TRUFSQ0hfTU9ERSIsIERFRkFVTFRfU0VBUkNIX01PREUpLnN0cmlwKCkubG93ZXIoKQppZiBTRUFSQ0hfTU9ERV9ERUZBVUxUIG5vdCBpbiB7InNpbmdsZSIsICJlbnNlbWJsZSJ9OgogICAgU0VBUkNIX01PREVfREVGQVVMVCA9IERFRkFVTFRfU0VBUkNIX01PREUKCmlmIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID09ICJoeWJyaWQiIGFuZCAiTUNQX1NFQVJDSF9NT0RFIiBub3QgaW4gb3MuZW52aXJvbjoKICAgICMgTm8gbW9kbyBow61icmlkbywgbyBjb21wb3J0YW1lbnRvIGVzcGVyYWRvIGNvc3R1bWEgc2VyIGVuc2VtYmxlIHBvciBwYWRyw6NvLgogICAgU0VBUkNIX01PREVfREVGQVVMVCA9ICJlbnNlbWJsZSIKClJFUkFOS19NT0RFTF9JRCA9IG9zLmVudmlyb24uZ2V0KCJNQ1BfUkVSQU5LX01PREVMIiwgImNyb3NzLWVuY29kZXIvbXMtbWFyY28tTWluaUxNLUwtNi12MiIpClJFUkFOS19FTkFCTEVEID0gb3MuZW52aXJvbi5nZXQoIk1DUF9SRVJBTktfRU5BQkxFRCIsICJ0cnVlIikuc3RyaXAoKS5sb3dlcigpIGluIHsiMSIsICJ0cnVlIiwgInllcyIsICJvbiJ9ClJFUkFOS19DQU5ESURBVEVfTVVMVElQTElFUiA9IGludChvcy5lbnZpcm9uLmdldCgiTUNQX1JFUkFOS19DQU5ESURBVEVfTVVMVElQTElFUiIsICIzIikpClJFUkFOS19NQVhfQ0FORElEQVRFUyA9IGludChvcy5lbnZpcm9uLmdldCgiTUNQX1JFUkFOS19NQVhfQ0FORElEQVRFUyIsICI0MCIpKQpSRVJBTktFUl9NQVhfTEVOR1RIID0gaW50KG9zLmVudmlyb24uZ2V0KCJNQ1BfUkVSQU5LX01BWF9MRU5HVEgiLCAiNTEyIikpClJFUkFOS0VSX1FVQU5USVpBVElPTiA9IG9zLmVudmlyb24uZ2V0KCJNQ1BfUkVSQU5LX1FVQU5USVpBVElPTiIsICJkeW5hbWljLWludDgiKS5zdHJpcCgpLmxvd2VyKCkKaWYgUkVSQU5LRVJfUVVBTlRJWkFUSU9OIG5vdCBpbiB7ImRlZmF1bHQiLCAiZHluYW1pYy1pbnQ4In06CiAgICBSRVJBTktFUl9RVUFOVElaQVRJT04gPSAiZHluYW1pYy1pbnQ4IgoKUlJGX0sgPSBpbnQob3MuZW52aXJvbi5nZXQoIk1DUF9SUkZfSyIsICI2MCIpKQpFTUJFRERJTkdfQkFUQ0hfU0laRSA9IF9jb25maWdfaW50KCJNQ1BfRU1CRURESU5HX0JBVENIX1NJWkUiLCAiZW1iZWRkaW5nX2JhdGNoX3NpemUiLCA0LCBtaW5fdmFsdWU9MSkKCl9lbnZfbW9kZWxfZGlyID0gb3MuZW52aXJvbi5nZXQoIk1DUF9NT0RFTF9ESVIiKQpNT0RFTF9ESVIgPSAoCiAgICBQYXRoKF9lbnZfbW9kZWxfZGlyKS5leHBhbmR1c2VyKCkKICAgIGlmIF9lbnZfbW9kZWxfZGlyCiAgICBlbHNlIFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAibXktY3VzdG9tLXJhZy1weXRob24iIC8gIm1vZGVscyIKKQoKIyBQYXLDom1ldHJvcyBkbyBzcGxpdHRlciAoYWxpbmhhZG9zIGNvbSBpbmRleGVyX2Z1bGwucHksIHBlcmZpbCBsb3ctbWVtb3J5KQpDSFVOS19TSVpFID0gX2NvbmZpZ19pbnQoIk1DUF9DSFVOS19TSVpFIiwgImNodW5rX3NpemUiLCAzMDAwLCBtaW5fdmFsdWU9MjU2KQpDSFVOS19PVkVSTEFQID0gbWluKENIVU5LX1NJWkUgLSAxLCBfY29uZmlnX2ludCgiTUNQX0NIVU5LX09WRVJMQVAiLCAiY2h1bmtfb3ZlcmxhcCIsIDQwMCwgbWluX3ZhbHVlPTApKQoKTUFYX0ZJTEVfU0laRV9CWVRFUyA9IDUwMCAqIDEwMjQgICMgNTAwIEtCClRPUF9LX1JFU1VMVFMgPSA3Ck1BWF9RVUVSWV9SRVNVTFRTID0gMzAKCiMgRmlsdHJvcyBkZSB2YXJyZWR1cmEKSUdOT1JFRF9ESVJTID0gewogICAgIi5naXQiLCAibm9kZV9tb2R1bGVzIiwgIl9fcHljYWNoZV9fIiwgIi52ZW52IiwgInZlbnYiLCAiZW52IiwKICAgICJkaXN0IiwgImJ1aWxkIiwgIm91dCIsICIubmV4dCIsICIubnV4dCIsICIuY2FjaGUiLCAiY292ZXJhZ2UiLAogICAgIi5weXRlc3RfY2FjaGUiLCAiLm15cHlfY2FjaGUiLCAiLnJ1ZmZfY2FjaGUiLCAidGFyZ2V0IiwgImJpbiIsICJvYmoiLAogICAgIi5pZGVhIiwgIi52c2NvZGUiLCAidmVuZG9yIiwgInRtcCIsICJ0ZW1wIiwgImxvZ3MiLCAiLnJhZ19kYiIsCn0KCklHTk9SRURfRVhURU5TSU9OUyA9IHsKICAgICIucG5nIiwgIi5qcGciLCAiLmpwZWciLCAiLmdpZiIsICIuc3ZnIiwgIi5pY28iLCAiLndlYnAiLCAiLmJtcCIsCiAgICAiLm1wNCIsICIubXAzIiwgIi53YXYiLCAiLm9nZyIsICIuYXZpIiwgIi5tb3YiLAogICAgIi56aXAiLCAiLnRhciIsICIuZ3oiLCAiLnJhciIsICIuN3oiLCAiLmphciIsICIud2FyIiwKICAgICIucHljIiwgIi5weW8iLCAiLnNvIiwgIi5kbGwiLCAiLmV4ZSIsICIuYmluIiwKICAgICIubG9jayIsICIuc3VtIiwgIi5zcWxpdGUiLCAiLmRiIiwgIi5zcWxpdGUzIiwKICAgICIudHRmIiwgIi53b2ZmIiwgIi53b2ZmMiIsICIuZW90IiwKICAgICIucGRmIiwgIi5kb2N4IiwgIi54bHN4IiwgIi5wcHR4IiwKfQoKQ09ERV9FWFRFTlNJT05TID0gewogICAgIi5weSIsICIuanMiLCAiLnRzIiwgIi50c3giLCAiLmpzeCIsICIuamF2YSIsICIuYyIsICIuaCIsICIuY3BwIiwgIi5ocHAiLAogICAgIi5nbyIsICIucnMiLCAiLnJiIiwgIi5waHAiLCAiLmNzIiwgIi5zd2lmdCIsICIua3QiLCAiLmt0cyIsICIuc2NhbGEiLCAiLnNxbCIsCiAgICAiLnNoIiwgIi5iYXNoIiwgIi56c2giLCAiLnBzMSIsICIueWFtbCIsICIueW1sIiwgIi50b21sIiwgIi5pbmkiLCAiLmNvbmYiLAogICAgIi5qc29uIiwgIi54bWwiLCAiLmh0bWwiLCAiLmNzcyIsICIuc2NzcyIsICIuc2FzcyIsICIudnVlIiwgIi5zdmVsdGUiLCAiLmRhcnQiLAogICAgIi5sdWEiLCAiLnIiLCAiLm0iLCAiLm1tIiwKfQoKRE9DX0VYVEVOU0lPTlMgPSB7CiAgICAiLm1kIiwgIi5tZHgiLCAiLnJzdCIsICIudHh0IiwgIi5hZG9jIiwgIi5vcmciLCAiLnRleCIsICIuY3N2IiwKfQoKCkBkYXRhY2xhc3MoZnJvemVuPVRydWUpCmNsYXNzIEJyYW5jaFNwZWM6CiAgICBrZXk6IHN0cgogICAgbW9kZWxfY2hvaWNlOiBzdHIKICAgIG1vZGVsX2lkOiBzdHIKICAgIGNvbGxlY3Rpb25fbmFtZTogc3RyCiAgICBjb250ZW50X2RvbWFpbjogc3RyCiAgICBsYWJlbDogc3RyCgoKSklOQV9DT0RFX0JSQU5DSF9NT0RFTF9DSE9JQ0UgPSAiamluYV92MiIgaWYgX2VtYmVkZGluZ19tb2RlbF9jaG9pY2UgPT0gImh5YnJpZCIgZWxzZSAiamluYSIKSklOQV9DT0RFX0JSQU5DSF9NT0RFTF9JRCA9IEpJTkFfVjJfRU1CRURESU5HX01PREVMIGlmIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID09ICJoeWJyaWQiIGVsc2UgSklOQV9WM19FTUJFRERJTkdfTU9ERUwKCkJSQU5DSF9TUEVDUzogZGljdFtzdHIsIEJyYW5jaFNwZWNdID0gewogICAgImppbmFfY29kZSI6IEJyYW5jaFNwZWMoCiAgICAgICAga2V5PSJqaW5hX2NvZGUiLAogICAgICAgIG1vZGVsX2Nob2ljZT1KSU5BX0NPREVfQlJBTkNIX01PREVMX0NIT0lDRSwKICAgICAgICBtb2RlbF9pZD1KSU5BX0NPREVfQlJBTkNIX01PREVMX0lELAogICAgICAgIGNvbGxlY3Rpb25fbmFtZT1DT0xMRUNUSU9OX0NPREVfSklOQSwKICAgICAgICBjb250ZW50X2RvbWFpbj0iY29kZSIsCiAgICAgICAgbGFiZWw9IkppbmEgdjIgQ29kZSIgaWYgX2VtYmVkZGluZ19tb2RlbF9jaG9pY2UgPT0gImh5YnJpZCIgZWxzZSAiSmluYSB2MyBDb2RlIiwKICAgICksCiAgICAiYmdlX2RvYyI6IEJyYW5jaFNwZWMoCiAgICAgICAga2V5PSJiZ2VfZG9jIiwKICAgICAgICBtb2RlbF9jaG9pY2U9ImJnZSIsCiAgICAgICAgbW9kZWxfaWQ9QkdFX0VNQkVERElOR19NT0RFTCwKICAgICAgICBjb2xsZWN0aW9uX25hbWU9Q09MTEVDVElPTl9ET0NfQkdFLAogICAgICAgIGNvbnRlbnRfZG9tYWluPSJkb2MiLAogICAgICAgIGxhYmVsPSJCR0UgRG9jcyIsCiAgICApLAp9CgpERUZBVUxUX1NJTkdMRV9CUkFOQ0hfS0VZID0gImJnZV9kb2MiIGlmIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID09ICJiZ2UiIGVsc2UgImppbmFfY29kZSIKCgpAZGF0YWNsYXNzCmNsYXNzIFJldHJpZXZlZEhpdDoKICAgIGtleTogc3RyCiAgICBkb2N1bWVudDogc3RyCiAgICBtZXRhZGF0YTogZGljdFtzdHIsIG9iamVjdF0KICAgIGRpc3RhbmNlOiBmbG9hdCB8IE5vbmUKICAgIHNpbWlsYXJpdHk6IGZsb2F0IHwgTm9uZQogICAgYnJhbmNoOiBCcmFuY2hTcGVjCiAgICByYW5rOiBpbnQKCgpAZGF0YWNsYXNzCmNsYXNzIEZ1c2VkSGl0OgogICAga2V5OiBzdHIKICAgIGRvY3VtZW50OiBzdHIKICAgIG1ldGFkYXRhOiBkaWN0W3N0ciwgb2JqZWN0XQogICAgcnJmX3Njb3JlOiBmbG9hdAogICAgc291cmNlX2RldGFpbHM6IGRpY3Rbc3RyLCBkaWN0W3N0ciwgb2JqZWN0XV0KICAgIHJlcmFua19zY29yZTogZmxvYXQgfCBOb25lID0gTm9uZQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgUnVudGltZSBjYWNoZXMgKGxhenkgbG9hZGluZyBwYXJhIGVjb25vbWl6YXIgUkFNKQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKX2Nocm9tYV9jbGllbnQ6IGNocm9tYWRiLkh0dHBDbGllbnQgfCBOb25lID0gTm9uZQpfY29sbGVjdGlvbnM6IGRpY3Rbc3RyLCBjaHJvbWFkYi5Db2xsZWN0aW9uXSA9IHt9Cl9tb2RlbHM6IGRpY3Rbc3RyLCBTZW50ZW5jZVRyYW5zZm9ybWVyXSA9IHt9Cl9tb2RlbF9sb2FkX2Vycm9yczogZGljdFtzdHIsIHN0cl0gPSB7fQpfc3BsaXR0ZXI6IFJlY3Vyc2l2ZUNoYXJhY3RlclRleHRTcGxpdHRlciB8IE5vbmUgPSBOb25lCl9yZXJhbmtlcjogQ3Jvc3NFbmNvZGVyIHwgTm9uZSA9IE5vbmUKX3JlcmFua2VyX2Vycm9yOiBzdHIgfCBOb25lID0gTm9uZQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQ2hyb21hIGUgbW9kZWxvcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCmRlZiBfbW9kZWxfY2FjaGVfZGlyKGJhc2VfZGlyOiBQYXRoLCBtb2RlbF9pZDogc3RyKSAtPiBQYXRoOgogICAgc2FmZV9uYW1lID0gbW9kZWxfaWQucmVwbGFjZSgiLyIsICJfXyIpLnJlcGxhY2UoIjoiLCAiXyIpCiAgICByZXR1cm4gYmFzZV9kaXIgLyBzYWZlX25hbWUKCgpkZWYgX2dldF9jaHJvbWFfY2xpZW50KCkgLT4gY2hyb21hZGIuSHR0cENsaWVudDoKICAgIGdsb2JhbCBfY2hyb21hX2NsaWVudAogICAgaWYgX2Nocm9tYV9jbGllbnQgaXMgTm9uZToKICAgICAgICBfY2hyb21hX2NsaWVudCA9IGNocm9tYWRiLkh0dHBDbGllbnQoaG9zdD1DSFJPTUFfSE9TVCwgcG9ydD1DSFJPTUFfUE9SVCkKICAgICAgICBfY2hyb21hX2NsaWVudC5oZWFydGJlYXQoKQogICAgICAgIGxvZy5pbmZvKCJDb25lY3RhZG8gYW8gQ2hyb21hREIgZW0gJXM6JXMiLCBDSFJPTUFfSE9TVCwgQ0hST01BX1BPUlQpCiAgICByZXR1cm4gX2Nocm9tYV9jbGllbnQKCgpkZWYgZ2V0X2Nocm9tYV9jb2xsZWN0aW9uKGNvbGxlY3Rpb25fbmFtZTogc3RyKSAtPiBjaHJvbWFkYi5Db2xsZWN0aW9uOgogICAgaWYgY29sbGVjdGlvbl9uYW1lIGluIF9jb2xsZWN0aW9uczoKICAgICAgICByZXR1cm4gX2NvbGxlY3Rpb25zW2NvbGxlY3Rpb25fbmFtZV0KCiAgICB0cnk6CiAgICAgICAgY2xpZW50ID0gX2dldF9jaHJvbWFfY2xpZW50KCkKICAgICAgICBjb2xsZWN0aW9uID0gY2xpZW50LmdldF9vcl9jcmVhdGVfY29sbGVjdGlvbigKICAgICAgICAgICAgbmFtZT1jb2xsZWN0aW9uX25hbWUsCiAgICAgICAgICAgIG1ldGFkYXRhPXsiaG5zdzpzcGFjZSI6ICJjb3NpbmUifSwKICAgICAgICApCiAgICAgICAgX2NvbGxlY3Rpb25zW2NvbGxlY3Rpb25fbmFtZV0gPSBjb2xsZWN0aW9uCiAgICAgICAgcmV0dXJuIGNvbGxlY3Rpb24KICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICByYWlzZSBSdW50aW1lRXJyb3IoCiAgICAgICAgICAgIGYiTsOjbyBmb2kgcG9zc8OtdmVsIGFjZXNzYXIgYSBjb2xlw6fDo28gJ3tjb2xsZWN0aW9uX25hbWV9JyBubyBDaHJvbWFEQiAiCiAgICAgICAgICAgIGYiKHtDSFJPTUFfSE9TVH06e0NIUk9NQV9QT1JUfSkuIEVycm86IHtlfSIKICAgICAgICApCgoKZGVmIF9sb2FkX3NlbnRlbmNlX3RyYW5zZm9ybWVyX2Zyb21fbG9jYWwobW9kZWxfaWQ6IHN0ciwgbG9jYWxfbW9kZWxfZGlyOiBQYXRoKSAtPiBTZW50ZW5jZVRyYW5zZm9ybWVyOgogICAgdHJ1c3RfcmVtb3RlX2NvZGUgPSBtb2RlbF9pZC5zdGFydHN3aXRoKCJqaW5hYWkvIikKICAgIHRva2VuaXplcl9rd2FyZ3MgPSB7ImZpeF9taXN0cmFsX3JlZ2V4IjogVHJ1ZX0KCiAgICBkZWYgX2luc3RhbnRpYXRlX21vZGVsKCkgLT4gU2VudGVuY2VUcmFuc2Zvcm1lcjoKICAgICAgICByZXR1cm4gU2VudGVuY2VUcmFuc2Zvcm1lcigKICAgICAgICAgICAgc3RyKGxvY2FsX21vZGVsX2RpciksCiAgICAgICAgICAgIGRldmljZT0iY3B1IiwKICAgICAgICAgICAgdHJ1c3RfcmVtb3RlX2NvZGU9dHJ1c3RfcmVtb3RlX2NvZGUsCiAgICAgICAgICAgIHRva2VuaXplcl9rd2FyZ3M9dG9rZW5pemVyX2t3YXJncywKICAgICAgICApCgogICAgZGVmIF9jbGVhcl9oZl9keW5hbWljX21vZHVsZXNfY2FjaGUoKSAtPiBOb25lOgogICAgICAgIGNhY2hlX2RpciA9IFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAiaHVnZ2luZ2ZhY2UiIC8gIm1vZHVsZXMiIC8gInRyYW5zZm9ybWVyc19tb2R1bGVzIgogICAgICAgIGlmIGNhY2hlX2Rpci5leGlzdHMoKToKICAgICAgICAgICAgbG9nLndhcm5pbmcoIkxpbXBhbmRvIGNhY2hlIGRpbsOibWljbyBkbyBIdWdnaW5nIEZhY2UgZW0gJXMiLCBjYWNoZV9kaXIpCiAgICAgICAgICAgIHNodXRpbC5ybXRyZWUoY2FjaGVfZGlyLCBpZ25vcmVfZXJyb3JzPVRydWUpCgogICAgZGVmIF9sb2FkX3dpdGhfamluYV9wYXRjaCgpIC0+IFNlbnRlbmNlVHJhbnNmb3JtZXI6CiAgICAgICAgaWYgbm90IHRydXN0X3JlbW90ZV9jb2RlOgogICAgICAgICAgICByZXR1cm4gX2luc3RhbnRpYXRlX21vZGVsKCkKCiAgICAgICAgZnJvbSB0cmFuc2Zvcm1lcnMgaW1wb3J0IEF1dG9Nb2RlbCwgQXV0b1Rva2VuaXplcgogICAgICAgIGZyb20gdHJhbnNmb3JtZXJzLm1vZGVsaW5nX3V0aWxzIGltcG9ydCBQcmVUcmFpbmVkTW9kZWwKCiAgICAgICAgb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkID0gQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWQKICAgICAgICBvcmlnaW5hbF9tb2RlbF9mcm9tX3ByZXRyYWluZWQgPSBBdXRvTW9kZWwuZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQgPSBQcmVUcmFpbmVkTW9kZWwuZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZyA9IFByZVRyYWluZWRNb2RlbC5fZnJvbV9jb25maWcKICAgICAgICBtb2RlbF9yZWZzID0ge3N0cihsb2NhbF9tb2RlbF9kaXIpLCBzdHIobG9jYWxfbW9kZWxfZGlyLnJlc29sdmUoKSl9CgogICAgICAgIGRlZiBfcGF0Y2hlZF9mcm9tX3ByZXRyYWluZWQoKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICAgICAgbW9kZWxfcmVmID0gYXJnc1swXSBpZiBhcmdzIGVsc2Uga3dhcmdzLmdldCgicHJldHJhaW5lZF9tb2RlbF9uYW1lX29yX3BhdGgiKQogICAgICAgICAgICBpZiBtb2RlbF9yZWYgaXMgbm90IE5vbmUgYW5kIHN0cihtb2RlbF9yZWYpIGluIG1vZGVsX3JlZnM6CiAgICAgICAgICAgICAgICBrd2FyZ3Muc2V0ZGVmYXVsdCgiZml4X21pc3RyYWxfcmVnZXgiLCBUcnVlKQogICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkKCphcmdzLCAqKmt3YXJncykKCiAgICAgICAgZGVmIF9wYXRjaGVkX21vZGVsX2Zyb21fcHJldHJhaW5lZCgqYXJncywgKiprd2FyZ3MpOgogICAgICAgICAgICBtb2RlbF9yZWYgPSBhcmdzWzBdIGlmIGFyZ3MgZWxzZSBrd2FyZ3MuZ2V0KCJwcmV0cmFpbmVkX21vZGVsX25hbWVfb3JfcGF0aCIpCiAgICAgICAgICAgIGlmIG1vZGVsX3JlZiBpcyBub3QgTm9uZSBhbmQgc3RyKG1vZGVsX3JlZikgaW4gbW9kZWxfcmVmcyBhbmQgInRvcmNoX2R0eXBlIiBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICBrd2FyZ3MgPSBkaWN0KGt3YXJncykKICAgICAgICAgICAgICAgIGlmICJkdHlwZSIgbm90IGluIGt3YXJnczoKICAgICAgICAgICAgICAgICAgICBrd2FyZ3NbImR0eXBlIl0gPSBrd2FyZ3NbInRvcmNoX2R0eXBlIl0KICAgICAgICAgICAgICAgIGt3YXJncy5wb3AoInRvcmNoX2R0eXBlIiwgTm9uZSkKICAgICAgICAgICAgcmV0dXJuIG9yaWdpbmFsX21vZGVsX2Zyb21fcHJldHJhaW5lZCgqYXJncywgKiprd2FyZ3MpCgogICAgICAgIG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkX2ZuID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQuX19mdW5jX18KCiAgICAgICAgQGNsYXNzbWV0aG9kCiAgICAgICAgZGVmIF9wYXRjaGVkX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkKGNscywgKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICAgICAgaWYgInRvcmNoX2R0eXBlIiBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICBrd2FyZ3MgPSBkaWN0KGt3YXJncykKICAgICAgICAgICAgICAgIGlmICJkdHlwZSIgbm90IGluIGt3YXJnczoKICAgICAgICAgICAgICAgICAgICBrd2FyZ3NbImR0eXBlIl0gPSBrd2FyZ3NbInRvcmNoX2R0eXBlIl0KICAgICAgICAgICAgICAgIGt3YXJncy5wb3AoInRvcmNoX2R0eXBlIiwgTm9uZSkKICAgICAgICAgICAgcmV0dXJuIG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkX2ZuKGNscywgKmFyZ3MsICoqa3dhcmdzKQoKICAgICAgICBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnX2ZuID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZy5fX2Z1bmNfXwoKICAgICAgICBAY2xhc3NtZXRob2QKICAgICAgICBkZWYgX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZyhjbHMsICphcmdzLCAqKmt3YXJncyk6CiAgICAgICAgICAgIGlmICJ0b3JjaF9kdHlwZSIgaW4ga3dhcmdzOgogICAgICAgICAgICAgICAga3dhcmdzID0gZGljdChrd2FyZ3MpCiAgICAgICAgICAgICAgICBpZiAiZHR5cGUiIG5vdCBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICAgICAga3dhcmdzWyJkdHlwZSJdID0ga3dhcmdzWyJ0b3JjaF9kdHlwZSJdCiAgICAgICAgICAgICAgICBrd2FyZ3MucG9wKCJ0b3JjaF9kdHlwZSIsIE5vbmUpCiAgICAgICAgICAgIHJldHVybiBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnX2ZuKGNscywgKmFyZ3MsICoqa3dhcmdzKQoKICAgICAgICBBdXRvVG9rZW5pemVyLmZyb21fcHJldHJhaW5lZCA9IF9wYXRjaGVkX2Zyb21fcHJldHJhaW5lZAogICAgICAgIEF1dG9Nb2RlbC5mcm9tX3ByZXRyYWluZWQgPSBfcGF0Y2hlZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICBQcmVUcmFpbmVkTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICBQcmVUcmFpbmVkTW9kZWwuX2Zyb21fY29uZmlnID0gX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZwogICAgICAgIHRyeToKICAgICAgICAgICAgcmV0dXJuIF9pbnN0YW50aWF0ZV9tb2RlbCgpCiAgICAgICAgZmluYWxseToKICAgICAgICAgICAgQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWQgPSBvcmlnaW5hbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgQXV0b01vZGVsLmZyb21fcHJldHJhaW5lZCA9IG9yaWdpbmFsX21vZGVsX2Zyb21fcHJldHJhaW5lZAogICAgICAgICAgICBQcmVUcmFpbmVkTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgUHJlVHJhaW5lZE1vZGVsLl9mcm9tX2NvbmZpZyA9IG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9jb25maWcKCiAgICB0cnk6CiAgICAgICAgcmV0dXJuIF9sb2FkX3dpdGhfamluYV9wYXRjaCgpCiAgICBleGNlcHQgRmlsZU5vdEZvdW5kRXJyb3IgYXMgZToKICAgICAgICBpZiB0cnVzdF9yZW1vdGVfY29kZSBhbmQgInRyYW5zZm9ybWVyc19tb2R1bGVzIiBpbiBzdHIoZSk6CiAgICAgICAgICAgIGxvZy53YXJuaW5nKCJDYWNoZSBkaW7Dom1pY28gaW5jb25zaXN0ZW50ZSBkZXRlY3RhZG86ICVzIiwgZSkKICAgICAgICAgICAgX2NsZWFyX2hmX2R5bmFtaWNfbW9kdWxlc19jYWNoZSgpCiAgICAgICAgICAgIHJldHVybiBfbG9hZF93aXRoX2ppbmFfcGF0Y2goKQogICAgICAgIHJhaXNlCgoKZGVmIF9hcHBseV9qaW5hX3F1YW50aXphdGlvbl9pZl9uZWVkZWQobW9kZWw6IFNlbnRlbmNlVHJhbnNmb3JtZXIsIG1vZGVsX2lkOiBzdHIpIC0+IFNlbnRlbmNlVHJhbnNmb3JtZXI6CiAgICBpZiBtb2RlbF9pZCAhPSBKSU5BX1YzX0VNQkVERElOR19NT0RFTCBvciBKSU5BX1FVQU5USVpBVElPTiA9PSAiZGVmYXVsdCI6CiAgICAgICAgcmV0dXJuIG1vZGVsCgogICAgdHJ5OgogICAgICAgIGltcG9ydCB0b3JjaAogICAgICAgIGltcG9ydCB3YXJuaW5ncwoKICAgICAgICBxdWFudGl6ZWRfbGF5ZXJzID0gMAogICAgICAgIGZvciBtb2R1bGUgaW4gbW9kZWwubW9kdWxlcygpOgogICAgICAgICAgICBpZiB0eXBlKG1vZHVsZSkuX19uYW1lX18gIT0gIlBhcmFtZXRyaXplZExpbmVhciI6CiAgICAgICAgICAgICAgICBjb250aW51ZQoKICAgICAgICAgICAgZmxvYXRfbGluZWFyID0gdG9yY2gubm4uTGluZWFyKAogICAgICAgICAgICAgICAgbW9kdWxlLmluX2ZlYXR1cmVzLAogICAgICAgICAgICAgICAgbW9kdWxlLm91dF9mZWF0dXJlcywKICAgICAgICAgICAgICAgIGJpYXM9bW9kdWxlLmJpYXMgaXMgbm90IE5vbmUsCiAgICAgICAgICAgICkKICAgICAgICAgICAgd2l0aCB0b3JjaC5ub19ncmFkKCk6CiAgICAgICAgICAgICAgICBmbG9hdF9saW5lYXIud2VpZ2h0LmNvcHlfKG1vZHVsZS53ZWlnaHQuZGV0YWNoKCkudG8odG9yY2guZmxvYXQzMikpCiAgICAgICAgICAgICAgICBpZiBtb2R1bGUuYmlhcyBpcyBub3QgTm9uZToKICAgICAgICAgICAgICAgICAgICBmbG9hdF9saW5lYXIuYmlhcy5jb3B5Xyhtb2R1bGUuYmlhcy5kZXRhY2goKS50byh0b3JjaC5mbG9hdDMyKSkKCiAgICAgICAgICAgIHdpdGggd2FybmluZ3MuY2F0Y2hfd2FybmluZ3MoKToKICAgICAgICAgICAgICAgIHdhcm5pbmdzLmZpbHRlcndhcm5pbmdzKCJpZ25vcmUiLCBjYXRlZ29yeT1EZXByZWNhdGlvbldhcm5pbmcpCiAgICAgICAgICAgICAgICBxdWFudGl6ZWRfbGluZWFyID0gdG9yY2gucXVhbnRpemF0aW9uLnF1YW50aXplX2R5bmFtaWMoCiAgICAgICAgICAgICAgICAgICAgdG9yY2gubm4uU2VxdWVudGlhbChmbG9hdF9saW5lYXIpLAogICAgICAgICAgICAgICAgICAgIHt0b3JjaC5ubi5MaW5lYXJ9LAogICAgICAgICAgICAgICAgICAgIGR0eXBlPXRvcmNoLnFpbnQ4LAogICAgICAgICAgICAgICAgKVswXQoKICAgICAgICAgICAgbW9kdWxlLl9keW5hbWljX2ludDhfbGluZWFyID0gcXVhbnRpemVkX2xpbmVhcgoKICAgICAgICAgICAgZGVmIF9mb3J3YXJkX2R5bmFtaWNfaW50OChzZWxmLCBpbnB1dCwgdGFza19pZD1Ob25lLCByZXNpZHVhbD1GYWxzZSk6CiAgICAgICAgICAgICAgICBvdXQgPSBzZWxmLl9keW5hbWljX2ludDhfbGluZWFyKGlucHV0KQogICAgICAgICAgICAgICAgaWYgcmVzaWR1YWw6CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG91dCwgaW5wdXQKICAgICAgICAgICAgICAgIHJldHVybiBvdXQKCiAgICAgICAgICAgIG1vZHVsZS5mb3J3YXJkID0gX2ZvcndhcmRfZHluYW1pY19pbnQ4Ll9fZ2V0X18obW9kdWxlLCBtb2R1bGUuX19jbGFzc19fKQogICAgICAgICAgICBxdWFudGl6ZWRfbGF5ZXJzICs9IDEKCiAgICAgICAgaWYgcXVhbnRpemVkX2xheWVycyA9PSAwOgogICAgICAgICAgICBsb2cud2FybmluZygiTmVuaHVtYSBjYW1hZGEgUGFyYW1ldHJpemVkTGluZWFyIGVuY29udHJhZGEgcGFyYSBkeW5hbWljLWludDggbm8gSmluYS4iKQogICAgICAgICAgICByZXR1cm4gbW9kZWwKCiAgICAgICAgbG9nLmluZm8oIlF1YW50aXphY2FvIEppbmEgYXBsaWNhZGE6IGR5bmFtaWMtaW50OCAoQ1BVLCAlcyBjYW1hZGFzKS4iLCBxdWFudGl6ZWRfbGF5ZXJzKQogICAgICAgIHJldHVybiBtb2RlbAogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBxdWFudF9lcnJvcjoKICAgICAgICBsb2cud2FybmluZygiRmFsaGEgYW8gYXBsaWNhciBkeW5hbWljLWludDggbm8gSmluYSAoJXMpOyB1c2FuZG8gbW9kZWxvIHBhZHJhby4iLCBxdWFudF9lcnJvcikKICAgICAgICByZXR1cm4gbW9kZWwKCgpkZWYgZ2V0X2VtYmVkZGluZ19tb2RlbChtb2RlbF9jaG9pY2U6IHN0cikgLT4gU2VudGVuY2VUcmFuc2Zvcm1lcjoKICAgIGlmIG1vZGVsX2Nob2ljZSBpbiBfbW9kZWxzOgogICAgICAgIHJldHVybiBfbW9kZWxzW21vZGVsX2Nob2ljZV0KCiAgICBpZiBtb2RlbF9jaG9pY2UgaW4gX21vZGVsX2xvYWRfZXJyb3JzOgogICAgICAgIHJhaXNlIFJ1bnRpbWVFcnJvcihfbW9kZWxfbG9hZF9lcnJvcnNbbW9kZWxfY2hvaWNlXSkKCiAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImppbmEiOgogICAgICAgIG1vZGVsX2lkID0gSklOQV9WM19FTUJFRERJTkdfTU9ERUwKICAgIGVsaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hX3YyIjoKICAgICAgICBtb2RlbF9pZCA9IEpJTkFfVjJfRU1CRURESU5HX01PREVMCiAgICBlbGlmIG1vZGVsX2Nob2ljZSA9PSAiYmdlIjoKICAgICAgICBtb2RlbF9pZCA9IEJHRV9FTUJFRERJTkdfTU9ERUwKICAgIGVsc2U6CiAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKGYiTW9kZWxvIG7Do28gc3Vwb3J0YWRvOiB7bW9kZWxfY2hvaWNlfSIpCgogICAgdHJ5OgogICAgICAgIE1PREVMX0RJUi5ta2RpcihwYXJlbnRzPVRydWUsIGV4aXN0X29rPVRydWUpCiAgICAgICAgcHJlZmVycmVkX21vZGVsX2NhY2hlX2RpciA9IF9tb2RlbF9jYWNoZV9kaXIoTU9ERUxfRElSLCBtb2RlbF9pZCkKICAgICAgICBsb2cuaW5mbygiQ2FycmVnYW5kbyBlbWJlZGRpbmdzICclcycgZW0gQ1BVIChjYWNoZTogJXMpIiwgbW9kZWxfaWQsIHByZWZlcnJlZF9tb2RlbF9jYWNoZV9kaXIpCgogICAgICAgIHNlbGVjdGlvbiA9IGRvd25sb2FkX21vZGVsX3dpdGhfZmFsbGJhY2soCiAgICAgICAgICAgIHByZWZlcnJlZF9tb2RlbF9pZD1tb2RlbF9pZCwKICAgICAgICAgICAgZmFsbGJhY2tfbW9kZWxfaWQ9bW9kZWxfaWQsCiAgICAgICAgICAgIGxvY2FsX2Rpcj1NT0RFTF9ESVIsCiAgICAgICAgKQogICAgICAgIG1vZGVsID0gX2xvYWRfc2VudGVuY2VfdHJhbnNmb3JtZXJfZnJvbV9sb2NhbChzZWxlY3Rpb24ubW9kZWxfaWQsIHNlbGVjdGlvbi5sb2NhbF9kaXIpCiAgICAgICAgaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hIjoKICAgICAgICAgICAgbW9kZWwgPSBfYXBwbHlfamluYV9xdWFudGl6YXRpb25faWZfbmVlZGVkKG1vZGVsLCBzZWxlY3Rpb24ubW9kZWxfaWQpCgogICAgICAgIF9tb2RlbHNbbW9kZWxfY2hvaWNlXSA9IG1vZGVsCiAgICAgICAgbG9nLmluZm8oCiAgICAgICAgICAgICJNb2RlbG8gZGUgZW1iZWRkaW5ncyBwcm9udG86ICVzIChwcm92aWRlcj0lcywgcGF0aD0lcykiLAogICAgICAgICAgICBzZWxlY3Rpb24ubW9kZWxfaWQsCiAgICAgICAgICAgIHNlbGVjdGlvbi5wcm92aWRlciwKICAgICAgICAgICAgc2VsZWN0aW9uLmxvY2FsX2RpciwKICAgICAgICApCiAgICAgICAgcmV0dXJuIG1vZGVsCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgbWVzc2FnZSA9IGYiRmFsaGEgYW8gY2FycmVnYXIgbW9kZWxvICd7bW9kZWxfY2hvaWNlfScgKHttb2RlbF9pZH0pOiB7ZX0iCiAgICAgICAgX21vZGVsX2xvYWRfZXJyb3JzW21vZGVsX2Nob2ljZV0gPSBtZXNzYWdlCiAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKG1lc3NhZ2UpCgoKZGVmIGdldF9yZXJhbmtlcigpIC0+IENyb3NzRW5jb2RlciB8IE5vbmU6CiAgICBnbG9iYWwgX3JlcmFua2VyLCBfcmVyYW5rZXJfZXJyb3IKCiAgICBpZiBub3QgUkVSQU5LX0VOQUJMRUQ6CiAgICAgICAgcmV0dXJuIE5vbmUKICAgIGlmIF9yZXJhbmtlciBpcyBub3QgTm9uZToKICAgICAgICByZXR1cm4gX3JlcmFua2VyCiAgICBpZiBfcmVyYW5rZXJfZXJyb3IgaXMgbm90IE5vbmU6CiAgICAgICAgcmV0dXJuIE5vbmUKCiAgICB0cnk6CiAgICAgICAgTU9ERUxfRElSLm1rZGlyKHBhcmVudHM9VHJ1ZSwgZXhpc3Rfb2s9VHJ1ZSkKICAgICAgICBzZWxlY3Rpb24gPSBkb3dubG9hZF9tb2RlbF93aXRoX2ZhbGxiYWNrKAogICAgICAgICAgICBwcmVmZXJyZWRfbW9kZWxfaWQ9UkVSQU5LX01PREVMX0lELAogICAgICAgICAgICBmYWxsYmFja19tb2RlbF9pZD1SRVJBTktfTU9ERUxfSUQsCiAgICAgICAgICAgIGxvY2FsX2Rpcj1NT0RFTF9ESVIsCiAgICAgICAgKQoKICAgICAgICByZXJhbmtlciA9IENyb3NzRW5jb2RlcigKICAgICAgICAgICAgc3RyKHNlbGVjdGlvbi5sb2NhbF9kaXIpLAogICAgICAgICAgICBkZXZpY2U9ImNwdSIsCiAgICAgICAgICAgIG1heF9sZW5ndGg9UkVSQU5LRVJfTUFYX0xFTkdUSCwKICAgICAgICAgICAgdHJ1c3RfcmVtb3RlX2NvZGU9RmFsc2UsCiAgICAgICAgKQoKICAgICAgICBpZiBSRVJBTktFUl9RVUFOVElaQVRJT04gPT0gImR5bmFtaWMtaW50OCI6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGltcG9ydCB0b3JjaAoKICAgICAgICAgICAgICAgIHJlcmFua2VyLm1vZGVsID0gdG9yY2gucXVhbnRpemF0aW9uLnF1YW50aXplX2R5bmFtaWMoCiAgICAgICAgICAgICAgICAgICAgcmVyYW5rZXIubW9kZWwsCiAgICAgICAgICAgICAgICAgICAge3RvcmNoLm5uLkxpbmVhcn0sCiAgICAgICAgICAgICAgICAgICAgZHR5cGU9dG9yY2gucWludDgsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBsb2cuaW5mbygiUmVyYW5rZXIgY29tIHF1YW50aXphY2FvIGR5bmFtaWMtaW50OCBoYWJpbGl0YWRhLiIpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgcXVhbnRfZXJyb3I6CiAgICAgICAgICAgICAgICBsb2cud2FybmluZygiRmFsaGEgYW8gcXVhbnRpemFyIHJlcmFua2VyICglcykuIFNlZ3VpbmRvIHNlbSBxdWFudGl6YWNhby4iLCBxdWFudF9lcnJvcikKCiAgICAgICAgX3JlcmFua2VyID0gcmVyYW5rZXIKICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgIlJlcmFua2VyIHByb250bzogJXMgKHByb3ZpZGVyPSVzLCBwYXRoPSVzKSIsCiAgICAgICAgICAgIHNlbGVjdGlvbi5tb2RlbF9pZCwKICAgICAgICAgICAgc2VsZWN0aW9uLnByb3ZpZGVyLAogICAgICAgICAgICBzZWxlY3Rpb24ubG9jYWxfZGlyLAogICAgICAgICkKICAgICAgICByZXR1cm4gX3JlcmFua2VyCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgX3JlcmFua2VyX2Vycm9yID0gc3RyKGUpCiAgICAgICAgbG9nLndhcm5pbmcoIlJlcmFua2VyIGluZGlzcG9uw612ZWwuIEJ1c2NhIHNlZ3VpcsOhIHNlbSByZXJhbmtpbmcuIEVycm86ICVzIiwgZSkKICAgICAgICByZXR1cm4gTm9uZQoKCmRlZiBnZXRfc3BsaXR0ZXIoKSAtPiBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXI6CiAgICBnbG9iYWwgX3NwbGl0dGVyCiAgICBpZiBfc3BsaXR0ZXIgaXMgTm9uZToKICAgICAgICBfc3BsaXR0ZXIgPSBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIoCiAgICAgICAgICAgIGNodW5rX3NpemU9Q0hVTktfU0laRSwKICAgICAgICAgICAgY2h1bmtfb3ZlcmxhcD1DSFVOS19PVkVSTEFQLAogICAgICAgICAgICBsZW5ndGhfZnVuY3Rpb249bGVuLAogICAgICAgICAgICBzZXBhcmF0b3JzPVsiXG5cbiIsICJcbiIsICIgIiwgIiJdLAogICAgICAgICkKICAgIHJldHVybiBfc3BsaXR0ZXIKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEluZGV4YcOnw6NvIGludGVybmEKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpkZWYgX21ha2VfY2h1bmtfaWQoZmlsZV9wYXRoOiBzdHIsIGNodW5rX2luZGV4OiBpbnQpIC0+IHN0cjoKICAgIHJhdyA9IGYie2ZpbGVfcGF0aH06OmNodW5rOjp7Y2h1bmtfaW5kZXh9IgogICAgcmV0dXJuIGhhc2hsaWIubWQ1KHJhdy5lbmNvZGUoKSkuaGV4ZGlnZXN0KCkKCgpkZWYgX21ha2VfcmVzdWx0X2tleShtZXRhZGF0YTogZGljdFtzdHIsIG9iamVjdF0sIGZhbGxiYWNrX2lkOiBzdHIpIC0+IHN0cjoKICAgIGZpbGVfcGF0aCA9IHN0cihtZXRhZGF0YS5nZXQoImZpbGVfcGF0aCIsICIiKSkKICAgIGNodW5rX2luZGV4ID0gc3RyKG1ldGFkYXRhLmdldCgiY2h1bmtfaW5kZXgiLCAiIikpCiAgICBpZiBmaWxlX3BhdGggYW5kIGNodW5rX2luZGV4OgogICAgICAgIHJldHVybiBmIntmaWxlX3BhdGh9OjpjaHVuazo6e2NodW5rX2luZGV4fSIKICAgIHJldHVybiBmYWxsYmFja19pZAoKCmRlZiBfZGVsZXRlX2ZpbGVfY2h1bmtzKGNvbGxlY3Rpb246IGNocm9tYWRiLkNvbGxlY3Rpb24sIGZpbGVfcGF0aDogc3RyKSAtPiBpbnQ6CiAgICAjIFBlZGUgYXBlbmFzIElEcyBwYXJhIG7Do28gbWF0ZXJpYWxpemFyIGRvY3VtZW50b3MvbWV0YWRhdGEgZGVzbmVjZXNzw6FyaW9zIGVtIFJBTS4KICAgIHJlc3VsdHMgPSBjb2xsZWN0aW9uLmdldCh3aGVyZT17ImZpbGVfcGF0aCI6IGZpbGVfcGF0aH0sIGluY2x1ZGU9W10pCiAgICBpZHMgPSByZXN1bHRzLmdldCgiaWRzIiwgW10pIGlmIHJlc3VsdHMgZWxzZSBbXQogICAgaWYgaWRzOgogICAgICAgIGNvbGxlY3Rpb24uZGVsZXRlKGlkcz1pZHMpCiAgICByZXR1cm4gbGVuKGlkcykKCgpkZWYgX3JlYWRfZmlsZV9zYWZlKGZpbGVwYXRoOiBQYXRoKSAtPiBzdHIgfCBOb25lOgogICAgZm9yIGVuY29kaW5nIGluICgidXRmLTgiLCAibGF0aW4tMSIsICJjcDEyNTIiKToKICAgICAgICB0cnk6CiAgICAgICAgICAgIHJldHVybiBmaWxlcGF0aC5yZWFkX3RleHQoZW5jb2Rpbmc9ZW5jb2RpbmcpCiAgICAgICAgZXhjZXB0IFVuaWNvZGVEZWNvZGVFcnJvcjoKICAgICAgICAgICAgY29udGludWUKICAgICAgICBleGNlcHQgT1NFcnJvcjoKICAgICAgICAgICAgcmV0dXJuIE5vbmUKICAgIHJldHVybiBOb25lCgoKZGVmIF9zY2FuX2ZvbGRlcihmb2xkZXJfcGF0aDogUGF0aCkgLT4gSXRlcmF0b3JbUGF0aF06CiAgICBmb3IgZGlycGF0aCwgZGlybmFtZXMsIGZpbGVuYW1lcyBpbiBvcy53YWxrKGZvbGRlcl9wYXRoKToKICAgICAgICBkaXJuYW1lc1s6XSA9IFsKICAgICAgICAgICAgZCBmb3IgZCBpbiBkaXJuYW1lcwogICAgICAgICAgICBpZiBkIG5vdCBpbiBJR05PUkVEX0RJUlMgYW5kIG5vdCBkLnN0YXJ0c3dpdGgoIi4iKQogICAgICAgIF0KICAgICAgICBkaXJuYW1lcy5zb3J0KCkKICAgICAgICBmb3IgZmlsZW5hbWUgaW4gc29ydGVkKGZpbGVuYW1lcyk6CiAgICAgICAgICAgIGZwID0gUGF0aChkaXJwYXRoKSAvIGZpbGVuYW1lCiAgICAgICAgICAgIGlmIGZwLnN1ZmZpeC5sb3dlcigpIGluIElHTk9SRURfRVhURU5TSU9OUzoKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGlmIGZwLnN0YXQoKS5zdF9zaXplID4gTUFYX0ZJTEVfU0laRV9CWVRFUzoKICAgICAgICAgICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICBleGNlcHQgT1NFcnJvcjoKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIHlpZWxkIGZwCgoKZGVmIF9jbGFzc2lmeV9maWxlX3RhcmdldHMoZmlsZXBhdGg6IFBhdGgpIC0+IGxpc3RbQnJhbmNoU3BlY106CiAgICBzdWZmaXggPSBmaWxlcGF0aC5zdWZmaXgubG93ZXIoKQogICAgaXNfY29kZSA9IHN1ZmZpeCBpbiBDT0RFX0VYVEVOU0lPTlMKICAgIGlzX2RvYyA9IHN1ZmZpeCBpbiBET0NfRVhURU5TSU9OUwoKICAgIGlmIGlzX2NvZGUgYW5kIG5vdCBpc19kb2M6CiAgICAgICAgcmV0dXJuIFtCUkFOQ0hfU1BFQ1NbImppbmFfY29kZSJdXQogICAgaWYgaXNfZG9jIGFuZCBub3QgaXNfY29kZToKICAgICAgICByZXR1cm4gW0JSQU5DSF9TUEVDU1siYmdlX2RvYyJdXQoKICAgICMgQXJxdWl2b3MgYW1iw61ndW9zL2V4dGVuc8OjbyBkZXNjb25oZWNpZGE6IGluZGV4YSBlbSBhbWJhcyBwYXJhIG7Do28gcGVyZGVyIHJlY2FsbC4KICAgIHJldHVybiBbQlJBTkNIX1NQRUNTWyJqaW5hX2NvZGUiXSwgQlJBTkNIX1NQRUNTWyJiZ2VfZG9jIl1dCgoKZGVmIF9pbmRleF9zaW5nbGVfZmlsZV9mb3JfYnJhbmNoKAogICAgZmlsZXBhdGg6IFBhdGgsCiAgICBicmFuY2g6IEJyYW5jaFNwZWMsCiAgICBzcGxpdHRlcjogUmVjdXJzaXZlQ2hhcmFjdGVyVGV4dFNwbGl0dGVyLAogICAgKiwKICAgIGRlbGV0ZV9leGlzdGluZzogYm9vbCA9IFRydWUsCikgLT4gaW50OgogICAgY29udGVudCA9IF9yZWFkX2ZpbGVfc2FmZShmaWxlcGF0aCkKICAgIGlmIG5vdCBjb250ZW50IG9yIG5vdCBjb250ZW50LnN0cmlwKCk6CiAgICAgICAgcmV0dXJuIDAKCiAgICBhYnNfcGF0aCA9IHN0cihmaWxlcGF0aC5yZXNvbHZlKCkpCiAgICBtb2RlbCA9IGdldF9lbWJlZGRpbmdfbW9kZWwoYnJhbmNoLm1vZGVsX2Nob2ljZSkKICAgIGNvbGxlY3Rpb24gPSBnZXRfY2hyb21hX2NvbGxlY3Rpb24oYnJhbmNoLmNvbGxlY3Rpb25fbmFtZSkKCiAgICBjaHVua3MgPSBzcGxpdHRlci5zcGxpdF90ZXh0KGNvbnRlbnQpCiAgICBpZiBub3QgY2h1bmtzOgogICAgICAgIHJldHVybiAwCgogICAgIyBBdHVhbGl6YcOnw6NvIGlkZW1wb3RlbnRlIHBvciBhcnF1aXZvIGVtIGNhZGEgY29sZcOnw6NvLgogICAgaWYgZGVsZXRlX2V4aXN0aW5nOgogICAgICAgIF9kZWxldGVfZmlsZV9jaHVua3MoY29sbGVjdGlvbiwgYWJzX3BhdGgpCgogICAgaW5zZXJ0ZWRfY2h1bmtzID0gMAogICAgYmF0Y2hfaWRzOiBsaXN0W3N0cl0gPSBbXQogICAgYmF0Y2hfZG9jczogbGlzdFtzdHJdID0gW10KICAgIGJhdGNoX21ldGFkYXRhczogbGlzdFtkaWN0W3N0ciwgb2JqZWN0XV0gPSBbXQoKICAgIGRlZiBfZmx1c2hfYmF0Y2goKSAtPiBOb25lOgogICAgICAgIG5vbmxvY2FsIGluc2VydGVkX2NodW5rcwogICAgICAgIGlmIG5vdCBiYXRjaF9pZHM6CiAgICAgICAgICAgIHJldHVybgogICAgICAgIGVtYmVkZGluZ3MgPSBtb2RlbC5lbmNvZGUoCiAgICAgICAgICAgIGJhdGNoX2RvY3MsCiAgICAgICAgICAgIHNob3dfcHJvZ3Jlc3NfYmFyPUZhbHNlLAogICAgICAgICAgICBiYXRjaF9zaXplPUVNQkVERElOR19CQVRDSF9TSVpFLAogICAgICAgICkudG9saXN0KCkKICAgICAgICBjb2xsZWN0aW9uLnVwc2VydCgKICAgICAgICAgICAgaWRzPWJhdGNoX2lkcywKICAgICAgICAgICAgZW1iZWRkaW5ncz1lbWJlZGRpbmdzLAogICAgICAgICAgICBkb2N1bWVudHM9YmF0Y2hfZG9jcywKICAgICAgICAgICAgbWV0YWRhdGFzPWJhdGNoX21ldGFkYXRhcywKICAgICAgICApCiAgICAgICAgaW5zZXJ0ZWRfY2h1bmtzICs9IGxlbihiYXRjaF9pZHMpCiAgICAgICAgZGVsIGVtYmVkZGluZ3MKICAgICAgICBiYXRjaF9pZHMuY2xlYXIoKQogICAgICAgIGJhdGNoX2RvY3MuY2xlYXIoKQogICAgICAgIGJhdGNoX21ldGFkYXRhcy5jbGVhcigpCgogICAgZm9yIGksIGNodW5rIGluIGVudW1lcmF0ZShjaHVua3MpOgogICAgICAgIGJhdGNoX2lkcy5hcHBlbmQoX21ha2VfY2h1bmtfaWQoYWJzX3BhdGgsIGkpKQogICAgICAgIGJhdGNoX2RvY3MuYXBwZW5kKGNodW5rKQogICAgICAgIGJhdGNoX21ldGFkYXRhcy5hcHBlbmQoCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJmaWxlX3BhdGgiOiBhYnNfcGF0aCwKICAgICAgICAgICAgICAgICJmaWxlX25hbWUiOiBmaWxlcGF0aC5uYW1lLAogICAgICAgICAgICAgICAgImNodW5rX2luZGV4IjogaSwKICAgICAgICAgICAgICAgICJzb3VyY2VfY29sbGVjdGlvbiI6IGJyYW5jaC5jb2xsZWN0aW9uX25hbWUsCiAgICAgICAgICAgICAgICAic291cmNlX21vZGVsX2Nob2ljZSI6IGJyYW5jaC5tb2RlbF9jaG9pY2UsCiAgICAgICAgICAgICAgICAic291cmNlX21vZGVsX2lkIjogYnJhbmNoLm1vZGVsX2lkLAogICAgICAgICAgICAgICAgImNvbnRlbnRfZG9tYWluIjogYnJhbmNoLmNvbnRlbnRfZG9tYWluLAogICAgICAgICAgICB9CiAgICAgICAgKQogICAgICAgIGlmIGxlbihiYXRjaF9pZHMpID49IEVNQkVERElOR19CQVRDSF9TSVpFOgogICAgICAgICAgICBfZmx1c2hfYmF0Y2goKQoKICAgIF9mbHVzaF9iYXRjaCgpCiAgICByZXR1cm4gaW5zZXJ0ZWRfY2h1bmtzCgoKZGVmIF9yZW1vdmVfZmlsZV9mcm9tX2FsbF9jb2xsZWN0aW9ucyhhYnNfcGF0aDogc3RyKSAtPiB0dXBsZVtkaWN0W3N0ciwgaW50XSwgbGlzdFtzdHJdXToKICAgIGRlbGV0ZWRfcGVyX2JyYW5jaDogZGljdFtzdHIsIGludF0gPSB7fQogICAgZXJyb3JzOiBsaXN0W3N0cl0gPSBbXQoKICAgIGZvciBicmFuY2ggaW4gQlJBTkNIX1NQRUNTLnZhbHVlcygpOgogICAgICAgIHRyeToKICAgICAgICAgICAgY29sbGVjdGlvbiA9IGdldF9jaHJvbWFfY29sbGVjdGlvbihicmFuY2guY29sbGVjdGlvbl9uYW1lKQogICAgICAgICAgICBkZWxldGVkID0gX2RlbGV0ZV9maWxlX2NodW5rcyhjb2xsZWN0aW9uLCBhYnNfcGF0aCkKICAgICAgICAgICAgZGVsZXRlZF9wZXJfYnJhbmNoW2JyYW5jaC5rZXldID0gZGVsZXRlZAogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgZXJyb3JzLmFwcGVuZChmInticmFuY2gua2V5fToge2V9IikKICAgIHJldHVybiBkZWxldGVkX3Blcl9icmFuY2gsIGVycm9ycwoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQnVzY2Egc2Vtw6JudGljYSBow61icmlkYQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCmRlZiBfcXVlcnlfYnJhbmNoKGJyYW5jaDogQnJhbmNoU3BlYywgcXVlcnk6IHN0ciwgbl9yZXN1bHRzOiBpbnQpIC0+IHR1cGxlW2xpc3RbUmV0cmlldmVkSGl0XSwgc3RyIHwgTm9uZV06CiAgICB0cnk6CiAgICAgICAgY29sbGVjdGlvbiA9IGdldF9jaHJvbWFfY29sbGVjdGlvbihicmFuY2guY29sbGVjdGlvbl9uYW1lKQogICAgICAgIG1vZGVsID0gZ2V0X2VtYmVkZGluZ19tb2RlbChicmFuY2gubW9kZWxfY2hvaWNlKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHJldHVybiBbXSwgZiJ7YnJhbmNoLmtleX06IHJlY3Vyc28gaW5kaXNwb27DrXZlbCAoe2V9KSIKCiAgICB0cnk6CiAgICAgICAgcXVlcnlfZW1iZWRkaW5nID0gbW9kZWwuZW5jb2RlKFtxdWVyeV0sIHNob3dfcHJvZ3Jlc3NfYmFyPUZhbHNlKS50b2xpc3QoKQogICAgICAgIHJlc3VsdHMgPSBjb2xsZWN0aW9uLnF1ZXJ5KAogICAgICAgICAgICBxdWVyeV9lbWJlZGRpbmdzPXF1ZXJ5X2VtYmVkZGluZywKICAgICAgICAgICAgbl9yZXN1bHRzPW5fcmVzdWx0cywKICAgICAgICAgICAgaW5jbHVkZT1bImRvY3VtZW50cyIsICJtZXRhZGF0YXMiLCAiZGlzdGFuY2VzIl0sCiAgICAgICAgKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHJldHVybiBbXSwgZiJ7YnJhbmNoLmtleX06IGZhbGhhIG5hIHF1ZXJ5ICh7ZX0pIgoKICAgIGRvY3VtZW50cyA9IHJlc3VsdHMuZ2V0KCJkb2N1bWVudHMiLCBbW11dKVswXQogICAgbWV0YWRhdGFzID0gcmVzdWx0cy5nZXQoIm1ldGFkYXRhcyIsIFtbXV0pWzBdCiAgICBkaXN0YW5jZXMgPSByZXN1bHRzLmdldCgiZGlzdGFuY2VzIiwgW1tdXSlbMF0KICAgIGlkcyA9IHJlc3VsdHMuZ2V0KCJpZHMiLCBbW11dKVswXQoKICAgIGhpdHM6IGxpc3RbUmV0cmlldmVkSGl0XSA9IFtdCiAgICBmb3IgaWR4LCAoZG9jLCBtZXRhLCBkaXN0KSBpbiBlbnVtZXJhdGUoemlwKGRvY3VtZW50cywgbWV0YWRhdGFzLCBkaXN0YW5jZXMpLCBzdGFydD0xKToKICAgICAgICBtZXRhZGF0YSA9IG1ldGEgb3Ige30KICAgICAgICBmYWxsYmFja19pZCA9IGlkc1tpZHggLSAxXSBpZiBpZHggLSAxIDwgbGVuKGlkcykgZWxzZSBmInticmFuY2gua2V5fTp7aWR4fSIKICAgICAgICBrZXkgPSBfbWFrZV9yZXN1bHRfa2V5KG1ldGFkYXRhLCBmYWxsYmFja19pZCkKCiAgICAgICAgc2ltaWxhcml0eSA9IE5vbmUKICAgICAgICBpZiBkaXN0IGlzIG5vdCBOb25lOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBzaW1pbGFyaXR5ID0gMS4wIC0gZmxvYXQoZGlzdCkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgIHNpbWlsYXJpdHkgPSBOb25lCgogICAgICAgIGhpdHMuYXBwZW5kKAogICAgICAgICAgICBSZXRyaWV2ZWRIaXQoCiAgICAgICAgICAgICAgICBrZXk9a2V5LAogICAgICAgICAgICAgICAgZG9jdW1lbnQ9KGRvYyBvciAiIiksCiAgICAgICAgICAgICAgICBtZXRhZGF0YT1tZXRhZGF0YSwKICAgICAgICAgICAgICAgIGRpc3RhbmNlPWZsb2F0KGRpc3QpIGlmIGRpc3QgaXMgbm90IE5vbmUgZWxzZSBOb25lLAogICAgICAgICAgICAgICAgc2ltaWxhcml0eT1zaW1pbGFyaXR5LAogICAgICAgICAgICAgICAgYnJhbmNoPWJyYW5jaCwKICAgICAgICAgICAgICAgIHJhbms9aWR4LAogICAgICAgICAgICApCiAgICAgICAgKQoKICAgIHJldHVybiBoaXRzLCBOb25lCgoKZGVmIF9ycmZfZnVzZShoaXRzX2J5X2JyYW5jaDogZGljdFtzdHIsIGxpc3RbUmV0cmlldmVkSGl0XV0sIHRvcF9saW1pdDogaW50KSAtPiBsaXN0W0Z1c2VkSGl0XToKICAgIGZ1c2VkOiBkaWN0W3N0ciwgRnVzZWRIaXRdID0ge30KCiAgICBmb3IgYnJhbmNoX2tleSwgaGl0cyBpbiBoaXRzX2J5X2JyYW5jaC5pdGVtcygpOgogICAgICAgIF8gPSBicmFuY2hfa2V5CiAgICAgICAgZm9yIHJhbmssIGhpdCBpbiBlbnVtZXJhdGUoaGl0cywgc3RhcnQ9MSk6CiAgICAgICAgICAgIGNvbnRyaWJ1dGlvbiA9IDEuMCAvIChSUkZfSyArIHJhbmspCiAgICAgICAgICAgIGVudHJ5ID0gZnVzZWQuZ2V0KGhpdC5rZXkpCgogICAgICAgICAgICBpZiBlbnRyeSBpcyBOb25lOgogICAgICAgICAgICAgICAgZW50cnkgPSBGdXNlZEhpdCgKICAgICAgICAgICAgICAgICAgICBrZXk9aGl0LmtleSwKICAgICAgICAgICAgICAgICAgICBkb2N1bWVudD1oaXQuZG9jdW1lbnQsCiAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGE9ZGljdChoaXQubWV0YWRhdGEpLAogICAgICAgICAgICAgICAgICAgIHJyZl9zY29yZT0wLjAsCiAgICAgICAgICAgICAgICAgICAgc291cmNlX2RldGFpbHM9e30sCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBmdXNlZFtoaXQua2V5XSA9IGVudHJ5CgogICAgICAgICAgICBlbnRyeS5ycmZfc2NvcmUgKz0gY29udHJpYnV0aW9uCiAgICAgICAgICAgIGVudHJ5LnNvdXJjZV9kZXRhaWxzW2hpdC5icmFuY2gua2V5XSA9IHsKICAgICAgICAgICAgICAgICJyYW5rIjogcmFuaywKICAgICAgICAgICAgICAgICJkaXN0YW5jZSI6IGhpdC5kaXN0YW5jZSwKICAgICAgICAgICAgICAgICJzaW1pbGFyaXR5IjogaGl0LnNpbWlsYXJpdHksCiAgICAgICAgICAgICAgICAiY29sbGVjdGlvbiI6IGhpdC5icmFuY2guY29sbGVjdGlvbl9uYW1lLAogICAgICAgICAgICAgICAgIm1vZGVsX2Nob2ljZSI6IGhpdC5icmFuY2gubW9kZWxfY2hvaWNlLAogICAgICAgICAgICAgICAgIm1vZGVsX2lkIjogaGl0LmJyYW5jaC5tb2RlbF9pZCwKICAgICAgICAgICAgICAgICJjb250ZW50X2RvbWFpbiI6IGhpdC5icmFuY2guY29udGVudF9kb21haW4sCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgICMgVXNhIG1ldGFkYWRvcyBkbyBoaXQgY29tIG1lbGhvciBzaW1pbGFyaWRhZGUgbG9jYWwgY29tbyBiYXNlIHByaW5jaXBhbC4KICAgICAgICAgICAgY3VycmVudF9zaW0gPSBlbnRyeS5tZXRhZGF0YS5nZXQoIl9iZXN0X3NpbWlsYXJpdHkiLCAtMTAuMCkKICAgICAgICAgICAgY2FuZGlkYXRlX3NpbSA9IGhpdC5zaW1pbGFyaXR5IGlmIGhpdC5zaW1pbGFyaXR5IGlzIG5vdCBOb25lIGVsc2UgLTEwLjAKICAgICAgICAgICAgaWYgY2FuZGlkYXRlX3NpbSA+IGN1cnJlbnRfc2ltOgogICAgICAgICAgICAgICAgZW50cnkuZG9jdW1lbnQgPSBoaXQuZG9jdW1lbnQKICAgICAgICAgICAgICAgIGVudHJ5Lm1ldGFkYXRhID0gZGljdChoaXQubWV0YWRhdGEpCiAgICAgICAgICAgICAgICBlbnRyeS5tZXRhZGF0YVsiX2Jlc3Rfc2ltaWxhcml0eSJdID0gY2FuZGlkYXRlX3NpbQoKICAgIGZ1c2VkX2hpdHMgPSBsaXN0KGZ1c2VkLnZhbHVlcygpKQogICAgZnVzZWRfaGl0cy5zb3J0KGtleT1sYW1iZGEgaDogaC5ycmZfc2NvcmUsIHJldmVyc2U9VHJ1ZSkKCiAgICAjIExpbWl0YSBvIHBvb2wgYW50ZXMgZG8gcmVyYW5raW5nIHBhcmEgcmVkdXppciBDUFUvUkFNLgogICAgcmV0dXJuIGZ1c2VkX2hpdHNbOnRvcF9saW1pdF0KCgpkZWYgX2FwcGx5X3JlcmFuayhxdWVyeTogc3RyLCBmdXNlZF9oaXRzOiBsaXN0W0Z1c2VkSGl0XSwgdG9wX2s6IGludCkgLT4gdHVwbGVbbGlzdFtGdXNlZEhpdF0sIGJvb2wsIHN0ciB8IE5vbmVdOgogICAgaWYgbm90IGZ1c2VkX2hpdHM6CiAgICAgICAgcmV0dXJuIFtdLCBGYWxzZSwgTm9uZQoKICAgIHJlcmFua2VyID0gZ2V0X3JlcmFua2VyKCkKICAgIGlmIHJlcmFua2VyIGlzIE5vbmU6CiAgICAgICAgcmVhc29uID0gX3JlcmFua2VyX2Vycm9yIGlmIF9yZXJhbmtlcl9lcnJvciBlbHNlICJyZXJhbmtlcl9kZXNhYmlsaXRhZG8iCiAgICAgICAgcmV0dXJuIGZ1c2VkX2hpdHNbOnRvcF9rXSwgRmFsc2UsIHJlYXNvbgoKICAgIHRyeToKICAgICAgICBwYWlycyA9IFsocXVlcnksIGhpdC5kb2N1bWVudCkgZm9yIGhpdCBpbiBmdXNlZF9oaXRzXQogICAgICAgIHNjb3JlcyA9IHJlcmFua2VyLnByZWRpY3QocGFpcnMsIHNob3dfcHJvZ3Jlc3NfYmFyPUZhbHNlLCBjb252ZXJ0X3RvX251bXB5PVRydWUpCgogICAgICAgIGZvciBoaXQsIHNjb3JlIGluIHppcChmdXNlZF9oaXRzLCBzY29yZXMpOgogICAgICAgICAgICBoaXQucmVyYW5rX3Njb3JlID0gZmxvYXQoc2NvcmUpCgogICAgICAgIGZ1c2VkX2hpdHMuc29ydCgKICAgICAgICAgICAga2V5PWxhbWJkYSBoOiAoCiAgICAgICAgICAgICAgICBoLnJlcmFua19zY29yZSBpZiBoLnJlcmFua19zY29yZSBpcyBub3QgTm9uZSBlbHNlIC0xZTksCiAgICAgICAgICAgICAgICBoLnJyZl9zY29yZSwKICAgICAgICAgICAgKSwKICAgICAgICAgICAgcmV2ZXJzZT1UcnVlLAogICAgICAgICkKICAgICAgICByZXR1cm4gZnVzZWRfaGl0c1s6dG9wX2tdLCBUcnVlLCBOb25lCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcmV0dXJuIGZ1c2VkX2hpdHNbOnRvcF9rXSwgRmFsc2UsIHN0cihlKQoKCmRlZiBfZm9ybWF0X3NpbWlsYXJpdHkoc2ltaWxhcml0eTogZmxvYXQgfCBOb25lKSAtPiBzdHI6CiAgICBpZiBzaW1pbGFyaXR5IGlzIE5vbmU6CiAgICAgICAgcmV0dXJuICJuL2EiCiAgICByZXR1cm4gZiJ7cm91bmQoc2ltaWxhcml0eSAqIDEwMCwgMSl9JSIKCgpkZWYgX2Zvcm1hdF9mdXNlZF9yZXN1bHRzKAogICAgKiwKICAgIHF1ZXJ5OiBzdHIsCiAgICBtb2RlOiBzdHIsCiAgICBoaXRzOiBsaXN0W0Z1c2VkSGl0XSwKICAgIGJyYW5jaF9lcnJvcnM6IGxpc3Rbc3RyXSwKICAgIHJlcmFua19hcHBsaWVkOiBib29sLAogICAgcmVyYW5rX2Vycm9yOiBzdHIgfCBOb25lLAopIC0+IHN0cjoKICAgIGlmIG5vdCBoaXRzOgogICAgICAgIG1zZyA9ICJOZW5odW0gcmVzdWx0YWRvIGVuY29udHJhZG8uIEFzIGNvbGXDp8O1ZXMgcG9kZW0gZXN0YXIgdmF6aWFzLiIKICAgICAgICBpZiBicmFuY2hfZXJyb3JzOgogICAgICAgICAgICBtc2cgKz0gIlxuRmFsaGFzIGRldGVjdGFkYXM6ICIgKyAiIHwgIi5qb2luKGJyYW5jaF9lcnJvcnMpCiAgICAgICAgcmV0dXJuIG1zZwoKICAgIGxpbmVzOiBsaXN0W3N0cl0gPSBbZiIjIFJlc3VsdGFkb3MgcGFyYTogJ3txdWVyeX0nIiwgZiIqKk1vZG86Kioge21vZGV9Il0KCiAgICBpZiBicmFuY2hfZXJyb3JzOgogICAgICAgIGxpbmVzLmFwcGVuZCgiKipBdmlzb3MgZGUgYnJhbmNoOioqICIgKyAiIHwgIi5qb2luKGJyYW5jaF9lcnJvcnMpKQoKICAgIGlmIG1vZGUgPT0gImVuc2VtYmxlIjoKICAgICAgICBpZiByZXJhbmtfYXBwbGllZDoKICAgICAgICAgICAgbGluZXMuYXBwZW5kKGYiKipSZXJhbmtpbmc6KiogYXRpdm8gKHtSRVJBTktfTU9ERUxfSUR9KSIpCiAgICAgICAgZWxzZToKICAgICAgICAgICAgbGluZXMuYXBwZW5kKGYiKipSZXJhbmtpbmc6KiogaW5kaXNwb27DrXZlbCAoe3JlcmFua19lcnJvciBvciAnc2VtIGRldGFsaGVzJ30pIikKCiAgICBsaW5lcy5hcHBlbmQoIiIpCgogICAgZm9yIGlkeCwgaGl0IGluIGVudW1lcmF0ZShoaXRzLCBzdGFydD0xKToKICAgICAgICBtZXRhZGF0YSA9IGRpY3QoaGl0Lm1ldGFkYXRhKQogICAgICAgIG1ldGFkYXRhLnBvcCgiX2Jlc3Rfc2ltaWxhcml0eSIsIE5vbmUpCgogICAgICAgIGZpbGVfcGF0aCA9IHN0cihtZXRhZGF0YS5nZXQoImZpbGVfcGF0aCIsICJkZXNjb25oZWNpZG8iKSkKICAgICAgICBjaHVua19pbmRleCA9IG1ldGFkYXRhLmdldCgiY2h1bmtfaW5kZXgiLCAiPyIpCiAgICAgICAgZmlsZV9uYW1lID0gc3RyKG1ldGFkYXRhLmdldCgiZmlsZV9uYW1lIiwgUGF0aChmaWxlX3BhdGgpLm5hbWUgaWYgZmlsZV9wYXRoICE9ICJkZXNjb25oZWNpZG8iIGVsc2UgIj8iKSkKCiAgICAgICAgc291cmNlX21vZGVscyA9IHNvcnRlZCh7c3RyKHYuZ2V0KCJtb2RlbF9jaG9pY2UiLCAiPyIpKSBmb3IgdiBpbiBoaXQuc291cmNlX2RldGFpbHMudmFsdWVzKCl9KQogICAgICAgIHNvdXJjZV9jb2xsZWN0aW9ucyA9IHNvcnRlZCh7c3RyKHYuZ2V0KCJjb2xsZWN0aW9uIiwgIj8iKSkgZm9yIHYgaW4gaGl0LnNvdXJjZV9kZXRhaWxzLnZhbHVlcygpfSkKCiAgICAgICAgc291cmNlX3BhcnRzOiBsaXN0W3N0cl0gPSBbXQogICAgICAgIGZvciBzb3VyY2Vfa2V5LCBkZXRhaWxzIGluIHNvcnRlZCgKICAgICAgICAgICAgaGl0LnNvdXJjZV9kZXRhaWxzLml0ZW1zKCksCiAgICAgICAgICAgIGtleT1sYW1iZGEgaXRlbTogaW50KGl0ZW1bMV0uZ2V0KCJyYW5rIiwgOTk5OTk5KSksCiAgICAgICAgKToKICAgICAgICAgICAgc291cmNlX3BhcnRzLmFwcGVuZCgKICAgICAgICAgICAgICAgIGYie3NvdXJjZV9rZXl9KHJhbms9e2RldGFpbHMuZ2V0KCdyYW5rJyl9LCBzaW09e19mb3JtYXRfc2ltaWxhcml0eShkZXRhaWxzLmdldCgnc2ltaWxhcml0eScpKX0pIgogICAgICAgICAgICApCgogICAgICAgIHNuaXBwZXQgPSBoaXQuZG9jdW1lbnQuc3RyaXAoKQogICAgICAgIGlmIGxlbihzbmlwcGV0KSA+IDgwMDoKICAgICAgICAgICAgc25pcHBldCA9IHNuaXBwZXRbOjgwMF0gKyAiXG4uLi4gW3RydW5jYWRvXSIKCiAgICAgICAgc2NvcmVfbGluZSA9IGYiUlJGPXtoaXQucnJmX3Njb3JlOi40Zn0iCiAgICAgICAgaWYgaGl0LnJlcmFua19zY29yZSBpcyBub3QgTm9uZToKICAgICAgICAgICAgc2NvcmVfbGluZSArPSBmIiB8IHJlcmFuaz17aGl0LnJlcmFua19zY29yZTouNGZ9IgoKICAgICAgICBsaW5lcy5hcHBlbmQoZiIjIyBbe2lkeH1dIHtmaWxlX3BhdGh9IikKICAgICAgICBsaW5lcy5hcHBlbmQoZiIqKlNjb3JlczoqKiB7c2NvcmVfbGluZX0iKQogICAgICAgIGxpbmVzLmFwcGVuZChmIioqRm9udGVzIGRlIHJlY3VwZXJhw6fDo286KiogeycsICcuam9pbihzb3VyY2VfcGFydHMpfSIpCiAgICAgICAgbGluZXMuYXBwZW5kKAogICAgICAgICAgICAiKipNZXRhZGFkb3MgdW5pZmljYWRvczoqKiAiCiAgICAgICAgICAgIGYiZmlsZV9uYW1lPXtmaWxlX25hbWV9IHwgY2h1bmtfaW5kZXg9e2NodW5rX2luZGV4fSB8ICIKICAgICAgICAgICAgZiJzb3VyY2VfbW9kZWxzPXtzb3VyY2VfbW9kZWxzfSB8IHNvdXJjZV9jb2xsZWN0aW9ucz17c291cmNlX2NvbGxlY3Rpb25zfSIKICAgICAgICApCiAgICAgICAgbGluZXMuYXBwZW5kKCIiKQogICAgICAgIGxpbmVzLmFwcGVuZChmImBgYFxue3NuaXBwZXR9XG5gYGAiKQogICAgICAgIGxpbmVzLmFwcGVuZCgiIikKCiAgICByZXR1cm4gIlxuIi5qb2luKGxpbmVzKQoKCmRlZiBfcnVuX3NpbmdsZV9tb2RlKHF1ZXJ5OiBzdHIsIHRvcF9rOiBpbnQpIC0+IHR1cGxlW2xpc3RbRnVzZWRIaXRdLCBsaXN0W3N0cl0sIGJvb2wsIHN0ciB8IE5vbmVdOgogICAgcHJpbWFyeV9icmFuY2ggPSBCUkFOQ0hfU1BFQ1NbREVGQVVMVF9TSU5HTEVfQlJBTkNIX0tFWV0KCiAgICBoaXRzLCBlcnJvciA9IF9xdWVyeV9icmFuY2gocHJpbWFyeV9icmFuY2gsIHF1ZXJ5LCB0b3BfaykKICAgIGVycm9yczogbGlzdFtzdHJdID0gW10KICAgIGlmIGVycm9yOgogICAgICAgIGVycm9ycy5hcHBlbmQoZXJyb3IpCgogICAgIyBGYWxsYmFjayBhdXRvbcOhdGljbyBwYXJhIGEgYnJhbmNoIGFsdGVybmF0aXZhLCBwcmVzZXJ2YW5kbyBkaXNwb25pYmlsaWRhZGUuCiAgICBpZiBub3QgaGl0czoKICAgICAgICBmYWxsYmFja19icmFuY2hfa2V5ID0gImJnZV9kb2MiIGlmIHByaW1hcnlfYnJhbmNoLmtleSA9PSAiamluYV9jb2RlIiBlbHNlICJqaW5hX2NvZGUiCiAgICAgICAgZmFsbGJhY2tfaGl0cywgZmFsbGJhY2tfZXJyb3IgPSBfcXVlcnlfYnJhbmNoKEJSQU5DSF9TUEVDU1tmYWxsYmFja19icmFuY2hfa2V5XSwgcXVlcnksIHRvcF9rKQogICAgICAgIGlmIGZhbGxiYWNrX2Vycm9yOgogICAgICAgICAgICBlcnJvcnMuYXBwZW5kKGZhbGxiYWNrX2Vycm9yKQogICAgICAgIGlmIGZhbGxiYWNrX2hpdHM6CiAgICAgICAgICAgIGhpdHMgPSBmYWxsYmFja19oaXRzCgogICAgaWYgbm90IGhpdHM6CiAgICAgICAgcmV0dXJuIFtdLCBlcnJvcnMsIEZhbHNlLCBOb25lCgogICAgZnVzZWQgPSBfcnJmX2Z1c2UoeyJzaW5nbGUiOiBoaXRzfSwgdG9wX2spCiAgICByZXR1cm4gZnVzZWQsIGVycm9ycywgRmFsc2UsIE5vbmUKCgpkZWYgX3J1bl9lbnNlbWJsZV9tb2RlKHF1ZXJ5OiBzdHIsIHRvcF9rOiBpbnQpIC0+IHR1cGxlW2xpc3RbRnVzZWRIaXRdLCBsaXN0W3N0cl0sIGJvb2wsIHN0ciB8IE5vbmVdOgogICAgcGVyX2JyYW5jaF9rID0gbWluKE1BWF9RVUVSWV9SRVNVTFRTLCBtYXgodG9wX2sgKiAyLCB0b3BfaykpCiAgICBicmFuY2hlcyA9IFtCUkFOQ0hfU1BFQ1NbImppbmFfY29kZSJdLCBCUkFOQ0hfU1BFQ1NbImJnZV9kb2MiXV0KCiAgICBoaXRzX2J5X2JyYW5jaDogZGljdFtzdHIsIGxpc3RbUmV0cmlldmVkSGl0XV0gPSB7fQogICAgYnJhbmNoX2Vycm9yczogbGlzdFtzdHJdID0gW10KCiAgICB3aXRoIFRocmVhZFBvb2xFeGVjdXRvcihtYXhfd29ya2Vycz1sZW4oYnJhbmNoZXMpKSBhcyBleGVjdXRvcjoKICAgICAgICBmdXR1cmVzID0gewogICAgICAgICAgICBleGVjdXRvci5zdWJtaXQoX3F1ZXJ5X2JyYW5jaCwgYnJhbmNoLCBxdWVyeSwgcGVyX2JyYW5jaF9rKTogYnJhbmNoCiAgICAgICAgICAgIGZvciBicmFuY2ggaW4gYnJhbmNoZXMKICAgICAgICB9CiAgICAgICAgZm9yIGZ1dHVyZSBpbiBhc19jb21wbGV0ZWQoZnV0dXJlcyk6CiAgICAgICAgICAgIGJyYW5jaCA9IGZ1dHVyZXNbZnV0dXJlXQogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBoaXRzLCBlcnJvciA9IGZ1dHVyZS5yZXN1bHQoKQogICAgICAgICAgICAgICAgaWYgZXJyb3I6CiAgICAgICAgICAgICAgICAgICAgYnJhbmNoX2Vycm9ycy5hcHBlbmQoZXJyb3IpCiAgICAgICAgICAgICAgICBpZiBoaXRzOgogICAgICAgICAgICAgICAgICAgIGhpdHNfYnlfYnJhbmNoW2JyYW5jaC5rZXldID0gaGl0cwogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBicmFuY2hfZXJyb3JzLmFwcGVuZChmInticmFuY2gua2V5fTogZmFsaGEgaW5lc3BlcmFkYSAoe2V9KSIpCgogICAgaWYgbm90IGhpdHNfYnlfYnJhbmNoOgogICAgICAgIHJldHVybiBbXSwgYnJhbmNoX2Vycm9ycywgRmFsc2UsIE5vbmUKCiAgICBjYW5kaWRhdGVfcG9vbCA9IG1pbihSRVJBTktfTUFYX0NBTkRJREFURVMsIG1heCh0b3BfaywgdG9wX2sgKiBSRVJBTktfQ0FORElEQVRFX01VTFRJUExJRVIpKQogICAgZnVzZWRfY2FuZGlkYXRlcyA9IF9ycmZfZnVzZShoaXRzX2J5X2JyYW5jaCwgY2FuZGlkYXRlX3Bvb2wpCiAgICByZXJhbmtlZF9oaXRzLCByZXJhbmtfYXBwbGllZCwgcmVyYW5rX2Vycm9yID0gX2FwcGx5X3JlcmFuayhxdWVyeSwgZnVzZWRfY2FuZGlkYXRlcywgdG9wX2spCiAgICByZXR1cm4gcmVyYW5rZWRfaGl0cywgYnJhbmNoX2Vycm9ycywgcmVyYW5rX2FwcGxpZWQsIHJlcmFua19lcnJvcgoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgU2Vydmlkb3IgTUNQIHZpYSBGYXN0TUNQCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgptY3AgPSBGYXN0TUNQKAogICAgbmFtZT0icmFnLWNvZGViYXNlIiwKICAgIGluc3RydWN0aW9ucz0oCiAgICAgICAgIlNlcnZpZG9yIFJBRyBwYXJhIGJ1c2NhIHNlbcOibnRpY2EgZW0gY8OzZGlnby1mb250ZSBsb2NhbCBjb20gc3Vwb3J0ZSBhIGVuc2VtYmxlIGjDrWJyaWRvLiAiCiAgICAgICAgIk5vIG1vZG8gaHlicmlkLCBhIGJyYW5jaCBkZSBjw7NkaWdvIHVzYSBKaW5hIHYyIGUgYSBkZSBkb2N1bWVudGHDp8OjbyB1c2EgQkdFLiAiCiAgICAgICAgIlVzZSBzZW1hbnRpY19zZWFyY2hfY29kZShxdWVyeSwgdG9wX2ssIG1vZGU9J2Vuc2VtYmxlJykgcGFyYSBjb21iaW5hciBKaW5hK0JHRSBjb20gUlJGIGUgcmVyYW5raW5nLiAiCiAgICAgICAgIlVzZSB1cGRhdGVfZmlsZV9pbmRleCBhcMOzcyBlZGl0YXIgdW0gYXJxdWl2byBwYXJhIG1hbnRlciBhcyBkdWFzIGNvbGXDp8O1ZXMgc2luY3Jvbml6YWRhcy4gIgogICAgICAgICJVc2UgaW5kZXhfc3BlY2lmaWNfZm9sZGVyIHBhcmEgaW5kZXhhw6fDo28gcmVjdXJzaXZhIHNvYiBkZW1hbmRhLiIKICAgICksCikKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFRvb2wgMTogc2VtYW50aWNfc2VhcmNoX2NvZGUKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkBtY3AudG9vbCgpCmRlZiBzZW1hbnRpY19zZWFyY2hfY29kZShxdWVyeTogc3RyLCB0b3BfazogaW50ID0gVE9QX0tfUkVTVUxUUywgbW9kZTogc3RyID0gU0VBUkNIX01PREVfREVGQVVMVCkgLT4gc3RyOgogICAgIiIiCiAgICBCdXNjYSBzZW3Dom50aWNhIG5vIMOtbmRpY2UgdmV0b3JpYWwgbG9jYWwuCgogICAgTW9kb3M6CiAgICAtIHNpbmdsZTogdXNhIGFwZW5hcyB1bWEgYnJhbmNoIChKaW5hL0JHRSBjb25mb3JtZSBNQ1BfRU1CRURESU5HX01PREVMOyBubyBoeWJyaWQsIEppbmEgdjIpLgogICAgLSBlbnNlbWJsZTogY29uc3VsdGEgZW0gcGFyYWxlbG8gY29kZV92ZWN0b3JzX2ppbmEgKyBkb2NfdmVjdG9yc19iZ2UsCiAgICAgIGZheiBmdXPDo28gdmlhIFJlY2lwcm9jYWwgUmFuayBGdXNpb24gKFJSRikgZSByZXJhbmtpbmcgbGV2ZS4KCiAgICBBcmdzOgogICAgICAgIHF1ZXJ5OiBEZXNjcmnDp8OjbyBkbyBxdWUgcHJvY3VyYXIuCiAgICAgICAgdG9wX2s6IFF1YW50aWRhZGUgZmluYWwgZGUgcmVzdWx0YWRvcy4KICAgICAgICBtb2RlOiAic2luZ2xlIiAocGFkcsOjbykgb3UgImVuc2VtYmxlIi4KCiAgICBSZXR1cm5zOgogICAgICAgIFJlc3VsdGFkbyB0ZXh0dWFsIGZvcm1hdGFkbyBwYXJhIGNvbnN1bW8gcGVsbyBMTE0uCiAgICAiIiIKICAgIHJhd19xdWVyeSA9IChxdWVyeSBvciAiIikuc3RyaXAoKQogICAgc2VhcmNoX21vZGUgPSAobW9kZSBvciBTRUFSQ0hfTU9ERV9ERUZBVUxUKS5zdHJpcCgpLmxvd2VyKCkKCiAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9zdGFydCIsCiAgICAgICAgdG9vbF9uYW1lPSJzZW1hbnRpY19zZWFyY2hfY29kZSIsCiAgICAgICAgZGV0YWlscz17CiAgICAgICAgICAgICJxdWVyeV9wcmV2aWV3IjogX3NhZmVfcHJldmlldyhyYXdfcXVlcnkpLAogICAgICAgICAgICAicXVlcnlfbGVuIjogbGVuKHJhd19xdWVyeSksCiAgICAgICAgICAgICJ0b3BfayI6IHRvcF9rLAogICAgICAgICAgICAibW9kZSI6IHNlYXJjaF9tb2RlLAogICAgICAgIH0sCiAgICApCgogICAgaWYgbm90IHJhd19xdWVyeToKICAgICAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfZW5kIiwKICAgICAgICAgICAgdG9vbF9uYW1lPSJzZW1hbnRpY19zZWFyY2hfY29kZSIsCiAgICAgICAgICAgIGRldGFpbHM9eyJzdGF0dXMiOiAiZXJyb3IiLCAicmVhc29uIjogImVtcHR5X3F1ZXJ5In0sCiAgICAgICAgKQogICAgICAgIHJldHVybiAiRXJybzogYSBxdWVyeSBuw6NvIHBvZGUgc2VyIHZhemlhLiIKCiAgICB0b3BfayA9IG1heCgxLCBtaW4odG9wX2ssIDIwKSkKICAgIGlmIHNlYXJjaF9tb2RlIG5vdCBpbiB7InNpbmdsZSIsICJlbnNlbWJsZSJ9OgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InNlbWFudGljX3NlYXJjaF9jb2RlIiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAiaW52YWxpZF9tb2RlIiwgIm1vZGUiOiBzZWFyY2hfbW9kZX0sCiAgICAgICAgKQogICAgICAgIHJldHVybiAiRXJybzogbW9kZSBpbnbDoWxpZG8uIFVzZSAnc2luZ2xlJyBvdSAnZW5zZW1ibGUnLiIKCiAgICB0cnk6CiAgICAgICAgaWYgc2VhcmNoX21vZGUgPT0gImVuc2VtYmxlIjoKICAgICAgICAgICAgaGl0cywgYnJhbmNoX2Vycm9ycywgcmVyYW5rX2FwcGxpZWQsIHJlcmFua19lcnJvciA9IF9ydW5fZW5zZW1ibGVfbW9kZShyYXdfcXVlcnksIHRvcF9rKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGhpdHMsIGJyYW5jaF9lcnJvcnMsIHJlcmFua19hcHBsaWVkLCByZXJhbmtfZXJyb3IgPSBfcnVuX3NpbmdsZV9tb2RlKHJhd19xdWVyeSwgdG9wX2spCgogICAgICAgIHJlc3VsdF90ZXh0ID0gX2Zvcm1hdF9mdXNlZF9yZXN1bHRzKAogICAgICAgICAgICBxdWVyeT1yYXdfcXVlcnksCiAgICAgICAgICAgIG1vZGU9c2VhcmNoX21vZGUsCiAgICAgICAgICAgIGhpdHM9aGl0cywKICAgICAgICAgICAgYnJhbmNoX2Vycm9ycz1icmFuY2hfZXJyb3JzLAogICAgICAgICAgICByZXJhbmtfYXBwbGllZD1yZXJhbmtfYXBwbGllZCwKICAgICAgICAgICAgcmVyYW5rX2Vycm9yPXJlcmFua19lcnJvciwKICAgICAgICApCgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InNlbWFudGljX3NlYXJjaF9jb2RlIiwKICAgICAgICAgICAgZGV0YWlscz17CiAgICAgICAgICAgICAgICAic3RhdHVzIjogIm9rIiwKICAgICAgICAgICAgICAgICJtb2RlIjogc2VhcmNoX21vZGUsCiAgICAgICAgICAgICAgICAicmVzdWx0X2NvdW50IjogbGVuKGhpdHMpLAogICAgICAgICAgICAgICAgImJyYW5jaF9lcnJvcnMiOiBsZW4oYnJhbmNoX2Vycm9ycyksCiAgICAgICAgICAgICAgICAicmVyYW5rX2FwcGxpZWQiOiByZXJhbmtfYXBwbGllZCwKICAgICAgICAgICAgfSwKICAgICAgICApCiAgICAgICAgcmV0dXJuIHJlc3VsdF90ZXh0CiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgX2xvZ190b29sX3VzYWdlKAogICAgICAgICAgICBldmVudD0idG9vbF9jYWxsX2VuZCIsCiAgICAgICAgICAgIHRvb2xfbmFtZT0ic2VtYW50aWNfc2VhcmNoX2NvZGUiLAogICAgICAgICAgICBkZXRhaWxzPXsic3RhdHVzIjogImVycm9yIiwgInJlYXNvbiI6ICJzZWFyY2hfZmFpbGVkIiwgImVycm9yIjogc3RyKGUpLCAibW9kZSI6IHNlYXJjaF9tb2RlfSwKICAgICAgICApCiAgICAgICAgcmV0dXJuIGYiRXJybyBhbyBleGVjdXRhciBidXNjYSBzZW3Dom50aWNhICh7c2VhcmNoX21vZGV9KToge2V9IgoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgVG9vbCAyOiB1cGRhdGVfZmlsZV9pbmRleAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQG1jcC50b29sKCkKZGVmIHVwZGF0ZV9maWxlX2luZGV4KGZpbGVfcGF0aDogc3RyKSAtPiBzdHI6CiAgICAiIiIKICAgIEF0dWFsaXphIG8gw61uZGljZSBSQUcgcGFyYSB1bSBhcnF1aXZvIGVzcGVjw61maWNvLgoKICAgIE8gYXJxdWl2byDDqSBjbGFzc2lmaWNhZG8gY29tbyBjw7NkaWdvL2RvYyBlIGluZGV4YWRvIG5hIGNvbGXDp8OjbyBhcHJvcHJpYWRhLgogICAgUGFyYSBleHRlbnPDtWVzIGFtYsOtZ3VhcywgaW5kZXhhIGVtIGFtYmFzIGFzIGNvbGXDp8O1ZXMuCiAgICAiIiIKICAgIGZpbGVwYXRoID0gUGF0aChmaWxlX3BhdGgpLnJlc29sdmUoKQogICAgYWJzX3BhdGggPSBzdHIoZmlsZXBhdGgpCgogICAgX2xvZ190b29sX3VzYWdlKAogICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfc3RhcnQiLAogICAgICAgIHRvb2xfbmFtZT0idXBkYXRlX2ZpbGVfaW5kZXgiLAogICAgICAgIGRldGFpbHM9eyJmaWxlX3BhdGgiOiBhYnNfcGF0aH0sCiAgICApCgogICAgaWYgbm90IGZpbGVwYXRoLmV4aXN0cygpOgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InVwZGF0ZV9maWxlX2luZGV4IiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAiZmlsZV9ub3RfZm91bmQiLCAiZmlsZV9wYXRoIjogYWJzX3BhdGh9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJFcnJvOiBhcnF1aXZvIG7Do28gZW5jb250cmFkbzoge2ZpbGVwYXRofSIKCiAgICBpZiBub3QgZmlsZXBhdGguaXNfZmlsZSgpOgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InVwZGF0ZV9maWxlX2luZGV4IiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAibm90X2FfZmlsZSIsICJmaWxlX3BhdGgiOiBhYnNfcGF0aH0sCiAgICAgICAgKQogICAgICAgIHJldHVybiBmIkVycm86IG8gY2FtaW5obyBuw6NvIGFwb250YSBwYXJhIHVtIGFycXVpdm86IHtmaWxlcGF0aH0iCgogICAgaWYgZmlsZXBhdGguc3RhdCgpLnN0X3NpemUgPiBNQVhfRklMRV9TSVpFX0JZVEVTOgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InVwZGF0ZV9maWxlX2luZGV4IiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAiZmlsZV90b29fbGFyZ2UiLCAiZmlsZV9wYXRoIjogYWJzX3BhdGh9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJFcnJvOiBhcnF1aXZvIG11aXRvIGdyYW5kZSAoPntNQVhfRklMRV9TSVpFX0JZVEVTIC8vIDEwMjR9S0IpOiB7ZmlsZXBhdGh9IgoKICAgIHNwbGl0dGVyID0gZ2V0X3NwbGl0dGVyKCkKICAgIHRhcmdldHMgPSBfY2xhc3NpZnlfZmlsZV90YXJnZXRzKGZpbGVwYXRoKQoKICAgIGRlbGV0ZWRfcGVyX2JyYW5jaCwgZGVsZXRpb25fZXJyb3JzID0gX3JlbW92ZV9maWxlX2Zyb21fYWxsX2NvbGxlY3Rpb25zKGFic19wYXRoKQoKICAgIGluc2VydGVkX3Blcl9icmFuY2g6IGRpY3Rbc3RyLCBpbnRdID0ge30KICAgIGluZGV4X2Vycm9yczogbGlzdFtzdHJdID0gW10KICAgIGZvciBicmFuY2ggaW4gdGFyZ2V0czoKICAgICAgICB0cnk6CiAgICAgICAgICAgIGluc2VydGVkID0gX2luZGV4X3NpbmdsZV9maWxlX2Zvcl9icmFuY2goCiAgICAgICAgICAgICAgICBmaWxlcGF0aCwKICAgICAgICAgICAgICAgIGJyYW5jaCwKICAgICAgICAgICAgICAgIHNwbGl0dGVyLAogICAgICAgICAgICAgICAgZGVsZXRlX2V4aXN0aW5nPUZhbHNlLCAgIyBqw6EgcmVtb3ZpZG8gZW0gdG9kYXMgYXMgY29sZcOnw7VlcyBhY2ltYQogICAgICAgICAgICApCiAgICAgICAgICAgIGluc2VydGVkX3Blcl9icmFuY2hbYnJhbmNoLmtleV0gPSBpbnNlcnRlZAogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgaW5kZXhfZXJyb3JzLmFwcGVuZChmInticmFuY2gua2V5fToge2V9IikKCiAgICBzdWNjZXNzX2JyYW5jaGVzID0gW2sgZm9yIGssIHYgaW4gaW5zZXJ0ZWRfcGVyX2JyYW5jaC5pdGVtcygpIGlmIHYgPiAwXQoKICAgIGRldGFpbHMgPSB7CiAgICAgICAgInN0YXR1cyI6ICJvayIgaWYgc3VjY2Vzc19icmFuY2hlcyBlbHNlICJlcnJvciIsCiAgICAgICAgImZpbGVfcGF0aCI6IGFic19wYXRoLAogICAgICAgICJ0YXJnZXRzIjogW2Iua2V5IGZvciBiIGluIHRhcmdldHNdLAogICAgICAgICJkZWxldGVkX3Blcl9icmFuY2giOiBkZWxldGVkX3Blcl9icmFuY2gsCiAgICAgICAgImluc2VydGVkX3Blcl9icmFuY2giOiBpbnNlcnRlZF9wZXJfYnJhbmNoLAogICAgICAgICJkZWxldGlvbl9lcnJvcnMiOiBsZW4oZGVsZXRpb25fZXJyb3JzKSwKICAgICAgICAiaW5kZXhfZXJyb3JzIjogbGVuKGluZGV4X2Vycm9ycyksCiAgICB9CiAgICBfbG9nX3Rvb2xfdXNhZ2UoZXZlbnQ9InRvb2xfY2FsbF9lbmQiLCB0b29sX25hbWU9InVwZGF0ZV9maWxlX2luZGV4IiwgZGV0YWlscz1kZXRhaWxzKQoKICAgIGlmIG5vdCBzdWNjZXNzX2JyYW5jaGVzIGFuZCBpbmRleF9lcnJvcnM6CiAgICAgICAgcmV0dXJuICgKICAgICAgICAgICAgIkVycm86IG7Do28gZm9pIHBvc3PDrXZlbCByZWluZGV4YXIgbyBhcnF1aXZvIGVtIG5lbmh1bWEgY29sZcOnw6NvLlxuIgogICAgICAgICAgICBmIkFycXVpdm86IHtmaWxlcGF0aH1cbiIKICAgICAgICAgICAgIkZhbGhhczogIiArICIgfCAiLmpvaW4oaW5kZXhfZXJyb3JzKQogICAgICAgICkKCiAgICBsaW5lcyA9IFsKICAgICAgICAiQXJxdWl2byByZWluZGV4YWRvLiIsCiAgICAgICAgZiIgIEFycXVpdm8gOiB7ZmlsZXBhdGh9IiwKICAgICAgICBmIiAgQ29sZcOnw7VlcyBhbHZvOiB7W2IuY29sbGVjdGlvbl9uYW1lIGZvciBiIGluIHRhcmdldHNdfSIsCiAgICAgICAgZiIgIFJlbW/Dp8O1ZXMgcG9yIGNvbGXDp8Ojbzoge2RlbGV0ZWRfcGVyX2JyYW5jaH0iLAogICAgICAgIGYiICBJbnNlcsOnw7VlcyBwb3IgY29sZcOnw6NvOiB7aW5zZXJ0ZWRfcGVyX2JyYW5jaH0iLAogICAgXQogICAgaWYgZGVsZXRpb25fZXJyb3JzOgogICAgICAgIGxpbmVzLmFwcGVuZCgiICBBdmlzb3MgbmEgcmVtb8Onw6NvOiAiICsgIiB8ICIuam9pbihkZWxldGlvbl9lcnJvcnMpKQogICAgaWYgaW5kZXhfZXJyb3JzOgogICAgICAgIGxpbmVzLmFwcGVuZCgiICBBdmlzb3MgbmEgaW5kZXhhw6fDo286ICIgKyAiIHwgIi5qb2luKGluZGV4X2Vycm9ycykpCiAgICByZXR1cm4gIlxuIi5qb2luKGxpbmVzKQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgVG9vbCAzOiBkZWxldGVfZmlsZV9pbmRleAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQG1jcC50b29sKCkKZGVmIGRlbGV0ZV9maWxlX2luZGV4KGZpbGVfcGF0aDogc3RyKSAtPiBzdHI6CiAgICAiIiIKICAgIFJlbW92ZSB1bSBhcnF1aXZvIGRvIMOtbmRpY2UgZW0gdG9kYXMgYXMgY29sZcOnw7VlcyBnZXJlbmNpYWRhcy4KICAgICIiIgogICAgZmlsZXBhdGggPSBQYXRoKGZpbGVfcGF0aCkucmVzb2x2ZSgpCiAgICBhYnNfcGF0aCA9IHN0cihmaWxlcGF0aCkKCiAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9zdGFydCIsCiAgICAgICAgdG9vbF9uYW1lPSJkZWxldGVfZmlsZV9pbmRleCIsCiAgICAgICAgZGV0YWlscz17ImZpbGVfcGF0aCI6IGFic19wYXRofSwKICAgICkKCiAgICBkZWxldGVkX3Blcl9icmFuY2gsIGVycm9ycyA9IF9yZW1vdmVfZmlsZV9mcm9tX2FsbF9jb2xsZWN0aW9ucyhhYnNfcGF0aCkKICAgIHRvdGFsX2RlbGV0ZWQgPSBzdW0oZGVsZXRlZF9wZXJfYnJhbmNoLnZhbHVlcygpKQoKICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICBldmVudD0idG9vbF9jYWxsX2VuZCIsCiAgICAgICAgdG9vbF9uYW1lPSJkZWxldGVfZmlsZV9pbmRleCIsCiAgICAgICAgZGV0YWlscz17CiAgICAgICAgICAgICJzdGF0dXMiOiAib2siIGlmIHRvdGFsX2RlbGV0ZWQgPiAwIGVsc2UgIndhcm5pbmciLAogICAgICAgICAgICAiZmlsZV9wYXRoIjogYWJzX3BhdGgsCiAgICAgICAgICAgICJkZWxldGVkX3Blcl9icmFuY2giOiBkZWxldGVkX3Blcl9icmFuY2gsCiAgICAgICAgICAgICJlcnJvcnMiOiBsZW4oZXJyb3JzKSwKICAgICAgICB9LAogICAgKQoKICAgIGlmIHRvdGFsX2RlbGV0ZWQgPT0gMDoKICAgICAgICBiYXNlID0gZiJOZW5odW0gY2h1bmsgZW5jb250cmFkbyBwYXJhIG8gYXJxdWl2bzoge2Fic19wYXRofSIKICAgICAgICBpZiBlcnJvcnM6CiAgICAgICAgICAgIGJhc2UgKz0gIlxuRmFsaGFzIHBhcmNpYWlzOiAiICsgIiB8ICIuam9pbihlcnJvcnMpCiAgICAgICAgcmV0dXJuIGJhc2UKCiAgICBvdXQgPSBbCiAgICAgICAgIlJlbW92aWRvIGRvIMOtbmRpY2UgY29tIHN1Y2Vzc28uIiwKICAgICAgICBmIiAgQXJxdWl2byA6IHthYnNfcGF0aH0iLAogICAgICAgIGYiICBEZWxlw6fDtWVzIHBvciBjb2xlw6fDo286IHtkZWxldGVkX3Blcl9icmFuY2h9IiwKICAgIF0KICAgIGlmIGVycm9yczoKICAgICAgICBvdXQuYXBwZW5kKCIgIEF2aXNvczogIiArICIgfCAiLmpvaW4oZXJyb3JzKSkKICAgIHJldHVybiAiXG4iLmpvaW4ob3V0KQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgVG9vbCA0OiBpbmRleF9zcGVjaWZpY19mb2xkZXIKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkBtY3AudG9vbCgpCmRlZiBpbmRleF9zcGVjaWZpY19mb2xkZXIoZm9sZGVyX3BhdGg6IHN0cikgLT4gc3RyOgogICAgIiIiCiAgICBJbmRleGEgcmVjdXJzaXZhbWVudGUgdW1hIHBhc3RhIGVtIGNvbGXDp8O1ZXMgc2VwYXJhZGFzIHBvciBkb23DrW5pby4KICAgICIiIgogICAgZm9sZGVyID0gUGF0aChmb2xkZXJfcGF0aCkucmVzb2x2ZSgpCgogICAgX2xvZ190b29sX3VzYWdlKAogICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfc3RhcnQiLAogICAgICAgIHRvb2xfbmFtZT0iaW5kZXhfc3BlY2lmaWNfZm9sZGVyIiwKICAgICAgICBkZXRhaWxzPXsiZm9sZGVyX3BhdGgiOiBzdHIoZm9sZGVyKX0sCiAgICApCgogICAgaWYgbm90IGZvbGRlci5leGlzdHMoKToKICAgICAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfZW5kIiwKICAgICAgICAgICAgdG9vbF9uYW1lPSJpbmRleF9zcGVjaWZpY19mb2xkZXIiLAogICAgICAgICAgICBkZXRhaWxzPXsic3RhdHVzIjogImVycm9yIiwgInJlYXNvbiI6ICJmb2xkZXJfbm90X2ZvdW5kIiwgImZvbGRlcl9wYXRoIjogc3RyKGZvbGRlcil9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJFcnJvOiBwYXN0YSBuw6NvIGVuY29udHJhZGE6IHtmb2xkZXJ9IgoKICAgIGlmIG5vdCBmb2xkZXIuaXNfZGlyKCk6CiAgICAgICAgX2xvZ190b29sX3VzYWdlKAogICAgICAgICAgICBldmVudD0idG9vbF9jYWxsX2VuZCIsCiAgICAgICAgICAgIHRvb2xfbmFtZT0iaW5kZXhfc3BlY2lmaWNfZm9sZGVyIiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAibm90X2FfZm9sZGVyIiwgImZvbGRlcl9wYXRoIjogc3RyKGZvbGRlcil9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJFcnJvOiBvIGNhbWluaG8gbsOjbyDDqSB1bSBkaXJldMOzcmlvOiB7Zm9sZGVyfSIKCiAgICBzcGxpdHRlciA9IGdldF9zcGxpdHRlcigpCgogICAgcHJvY2Vzc2VkX2ZpbGVzID0gMAogICAgYnJhbmNoX2ZpbGVfY291bnRzID0ge2tleTogMCBmb3Iga2V5IGluIEJSQU5DSF9TUEVDU30KICAgIGJyYW5jaF9jaHVua19jb3VudHMgPSB7a2V5OiAwIGZvciBrZXkgaW4gQlJBTkNIX1NQRUNTfQogICAgZXJyb3JfY291bnQgPSAwCiAgICBlcnJvcl9zYW1wbGVzOiBsaXN0W3N0cl0gPSBbXQoKICAgIGZvciBmaWxlcGF0aCBpbiBfc2Nhbl9mb2xkZXIoZm9sZGVyKToKICAgICAgICBwcm9jZXNzZWRfZmlsZXMgKz0gMQogICAgICAgIHRhcmdldHMgPSBfY2xhc3NpZnlfZmlsZV90YXJnZXRzKGZpbGVwYXRoKQoKICAgICAgICBmb3IgYnJhbmNoIGluIHRhcmdldHM6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIG5fY2h1bmtzID0gX2luZGV4X3NpbmdsZV9maWxlX2Zvcl9icmFuY2goZmlsZXBhdGgsIGJyYW5jaCwgc3BsaXR0ZXIpCiAgICAgICAgICAgICAgICBicmFuY2hfZmlsZV9jb3VudHNbYnJhbmNoLmtleV0gKz0gMQogICAgICAgICAgICAgICAgYnJhbmNoX2NodW5rX2NvdW50c1ticmFuY2gua2V5XSArPSBuX2NodW5rcwogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBlcnJvcl9jb3VudCArPSAxCiAgICAgICAgICAgICAgICBpZiBsZW4oZXJyb3Jfc2FtcGxlcykgPCAxMDoKICAgICAgICAgICAgICAgICAgICBlcnJvcl9zYW1wbGVzLmFwcGVuZChmIntmaWxlcGF0aC5uYW1lfSBbe2JyYW5jaC5rZXl9XToge2V9IikKCiAgICBpZiBwcm9jZXNzZWRfZmlsZXMgPT0gMDoKICAgICAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfZW5kIiwKICAgICAgICAgICAgdG9vbF9uYW1lPSJpbmRleF9zcGVjaWZpY19mb2xkZXIiLAogICAgICAgICAgICBkZXRhaWxzPXsic3RhdHVzIjogIm9rIiwgImZvbGRlcl9wYXRoIjogc3RyKGZvbGRlciksICJmaWxlc19wcm9jZXNzZWQiOiAwLCAiY2h1bmtzIjogMCwgImVycm9ycyI6IDB9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJOZW5odW0gYXJxdWl2byBpbmRleMOhdmVsIGVuY29udHJhZG8gZW06IHtmb2xkZXJ9IgoKICAgIHRvdGFsX2NodW5rcyA9IHN1bShicmFuY2hfY2h1bmtfY291bnRzLnZhbHVlcygpKQoKICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICBldmVudD0idG9vbF9jYWxsX2VuZCIsCiAgICAgICAgdG9vbF9uYW1lPSJpbmRleF9zcGVjaWZpY19mb2xkZXIiLAogICAgICAgIGRldGFpbHM9ewogICAgICAgICAgICAic3RhdHVzIjogIm9rIiwKICAgICAgICAgICAgImZvbGRlcl9wYXRoIjogc3RyKGZvbGRlciksCiAgICAgICAgICAgICJmaWxlc19wcm9jZXNzZWQiOiBwcm9jZXNzZWRfZmlsZXMsCiAgICAgICAgICAgICJjaHVua3MiOiB0b3RhbF9jaHVua3MsCiAgICAgICAgICAgICJlcnJvcnMiOiBlcnJvcl9jb3VudCwKICAgICAgICAgICAgImJyYW5jaF9maWxlX2NvdW50cyI6IGJyYW5jaF9maWxlX2NvdW50cywKICAgICAgICAgICAgImJyYW5jaF9jaHVua19jb3VudHMiOiBicmFuY2hfY2h1bmtfY291bnRzLAogICAgICAgIH0sCiAgICApCgogICAgcmVwb3J0ID0gWwogICAgICAgICJJbmRleGHDp8OjbyBkYSBwYXN0YSBjb25jbHXDrWRhLiIsCiAgICAgICAgZiIgIFBhc3RhOiB7Zm9sZGVyfSIsCiAgICAgICAgZiIgIEFycXVpdm9zIHByb2Nlc3NhZG9zOiB7cHJvY2Vzc2VkX2ZpbGVzfSIsCiAgICAgICAgZiIgIFRvdGFsIGRlIGNodW5rczoge3RvdGFsX2NodW5rc30iLAogICAgICAgIGYiICBBcnF1aXZvcyBwb3IgYnJhbmNoOiB7YnJhbmNoX2ZpbGVfY291bnRzfSIsCiAgICAgICAgZiIgIENodW5rcyBwb3IgYnJhbmNoOiB7YnJhbmNoX2NodW5rX2NvdW50c30iLAogICAgXQoKICAgIGlmIGVycm9yX2NvdW50OgogICAgICAgIHJlcG9ydC5hcHBlbmQoZiIgIEVycm9zICh7ZXJyb3JfY291bnR9KToiKQogICAgICAgIGZvciBlcnIgaW4gZXJyb3Jfc2FtcGxlczoKICAgICAgICAgICAgcmVwb3J0LmFwcGVuZChmIiAgICAtIHtlcnJ9IikKICAgICAgICBpZiBlcnJvcl9jb3VudCA+IGxlbihlcnJvcl9zYW1wbGVzKToKICAgICAgICAgICAgcmVwb3J0LmFwcGVuZChmIiAgICAuLi4gZSBtYWlzIHtlcnJvcl9jb3VudCAtIGxlbihlcnJvcl9zYW1wbGVzKX0gZXJyb3MuIikKCiAgICByZXR1cm4gIlxuIi5qb2luKHJlcG9ydCkKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvbnRvIGRlIGVudHJhZGEKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBsb2cuaW5mbygiSW5pY2lhbmRvIHNlcnZpZG9yIE1DUCBSQUcgKHN0ZGlvKS4uLiIpCiAgICBsb2cuaW5mbygiQ2hyb21hREI6ICVzOiVzIiwgQ0hST01BX0hPU1QsIENIUk9NQV9QT1JUKQogICAgbG9nLmluZm8oCiAgICAgICAgIkNvbGXDp8O1ZXM6ICVzICglcyksICVzICglcykiLAogICAgICAgIENPTExFQ1RJT05fQ09ERV9KSU5BLAogICAgICAgIEJSQU5DSF9TUEVDU1siamluYV9jb2RlIl0ubW9kZWxfaWQsCiAgICAgICAgQ09MTEVDVElPTl9ET0NfQkdFLAogICAgICAgIEJSQU5DSF9TUEVDU1siYmdlX2RvYyJdLm1vZGVsX2lkLAogICAgKQogICAgbG9nLmluZm8oIk1vZG8gcGFkcsOjbyBkZSBidXNjYTogJXMiLCBTRUFSQ0hfTU9ERV9ERUZBVUxUKQogICAgbG9nLmluZm8oIk1vZGVsbyBzaW5nbGUgcGFkcsOjbzogJXMiLCBCUkFOQ0hfU1BFQ1NbREVGQVVMVF9TSU5HTEVfQlJBTkNIX0tFWV0ubW9kZWxfaWQpCiAgICBsb2cuaW5mbygiUXVhbnRpemFjYW8gSmluYTogJXMiLCBKSU5BX1FVQU5USVpBVElPTikKICAgIGxvZy5pbmZvKCJDb25maWcgZGUgdHVuaW5nIGNhcnJlZ2FkYSBkZTogJXMgKGZvdW5kPSVzKSIsIElOREVYRVJfQ09ORklHX1BBVEgsIGJvb2woSU5ERVhFUl9UVU5JTkdfQ09ORklHKSkKICAgIGxvZy5pbmZvKCJFbWJlZGRpbmcgYmF0Y2ggc2l6ZTogJXMiLCBFTUJFRERJTkdfQkFUQ0hfU0laRSkKICAgIGxvZy5pbmZvKCJDaHVuayBwYXJhbXM6IHNpemU9JXMgb3ZlcmxhcD0lcyIsIENIVU5LX1NJWkUsIENIVU5LX09WRVJMQVApCiAgICBsb2cuaW5mbygiUmVyYW5rZXI6ICVzIChlbmFibGVkPSVzLCBxdWFudD0lcykiLCBSRVJBTktfTU9ERUxfSUQsIFJFUkFOS19FTkFCTEVELCBSRVJBTktFUl9RVUFOVElaQVRJT04pCiAgICBsb2cuaW5mbygiUGFzdGEgZGUgbW9kZWxvcyBsb2NhaXM6ICVzIiwgTU9ERUxfRElSKQogICAgbG9nLmluZm8oIlVzbyBNQ1Agc2Vyw6EgcmVnaXN0cmFkbyBlbTogJXMiLCBNQ1BfVVNBR0VfTE9HX1BBVEgpCgogICAgIyBQcsOpLWFxdWVjZSBzb21lbnRlIGNvbmV4w6NvIENocm9tYTsgbW9kZWxvcyBmaWNhbSBsYXp5IHBhcmEgcG91cGFyIFJBTS4KICAgIHRyeToKICAgICAgICBfZ2V0X2Nocm9tYV9jbGllbnQoKQogICAgICAgIGdldF9jaHJvbWFfY29sbGVjdGlvbihDT0xMRUNUSU9OX0NPREVfSklOQSkKICAgICAgICBnZXRfY2hyb21hX2NvbGxlY3Rpb24oQ09MTEVDVElPTl9ET0NfQkdFKQogICAgICAgIGxvZy5pbmZvKCJDb25leMOjbyBDaHJvbWEgaW5pY2lhbGl6YWRhLiBNb2RlbG9zIHNlcsOjbyBjYXJyZWdhZG9zIHNvYiBkZW1hbmRhLiIpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgbG9nLmVycm9yKCJGYWxoYSBhbyBpbmljaWFsaXphciBDaHJvbWFEQjogJXMiLCBlKQogICAgICAgIGxvZy5lcnJvcigiTyBzZXJ2aWRvciBjb250aW51YXLDoSwgbWFzIGFzIGZlcnJhbWVudGFzIHJldG9ybmFyw6NvIGVycm8gYXTDqSBvIENocm9tYURCIGVzdGFyIGRpc3BvbsOtdmVsLiIpCgogICAgbWNwLnJ1bih0cmFuc3BvcnQ9InN0ZGlvIikK"
565
+ B64_MODEL_DL_HF="IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCgoiIiIKZG93bmxvYWRfbW9kZWxfZnJvbV9odWdnaW5mYWNlLnB5CgpDYW1hZGEgZGUgZG93bmxvYWQgZGUgbW9kZWxvcyBjb20gcHJpb3JpZGFkZSBkZSBwcm92ZWRvcmVzIGUgZmFsbGJhY2sgZGUgbW9kZWxvLgpGbHV4byBwYWRyw6NvOgoxKSB0ZW50YSBiYWl4YXIgbyBtb2RlbG8gcHJlZmVyaWRvIHZpYSBIdWdnaW5nIEZhY2U7CjIpIHNlIGZhbGhhciwgdGVudGEgcHJvdmVkb3JlcyBhbHRlcm5hdGl2b3MgKHF1YW5kbyBkaXNwb27DrXZlaXMpOwozKSBzZSBvIG1vZGVsbyBwcmVmZXJpZG8gZmFsaGFyIGVtIHRvZG9zIG9zIHByb3ZlZG9yZXMsIHRlbnRhIG1vZGVsbyBmYWxsYmFjay4KIiIiCgpmcm9tIGRhdGFjbGFzc2VzIGltcG9ydCBkYXRhY2xhc3MKaW1wb3J0IGdldHBhc3MKaW1wb3J0IG9zCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAppbXBvcnQgc2h1dGlsCmltcG9ydCBzeXMKZnJvbSB0eXBpbmcgaW1wb3J0IFByb3RvY29sCgoKY2xhc3MgTW9kZWxEb3dubG9hZFN0cmF0ZWd5KFByb3RvY29sKToKICAgIG5hbWU6IHN0cgoKICAgIGRlZiBkb3dubG9hZChzZWxmLCBtb2RlbF9pZDogc3RyLCBsb2NhbF9kaXI6IFBhdGgpIC0+IE5vbmU6CiAgICAgICAgIiIiQmFpeGEgbW9kZWxfaWQgcGFyYSBsb2NhbF9kaXIgb3UgbGV2YW50YSBleGNlw6fDo28uIiIiCgoKY2xhc3MgSHVnZ2luZ0ZhY2VEb3dubG9hZFN0cmF0ZWd5OgogICAgbmFtZSA9ICJodWdnaW5nZmFjZSIKCiAgICBkZWYgZG93bmxvYWQoc2VsZiwgbW9kZWxfaWQ6IHN0ciwgbG9jYWxfZGlyOiBQYXRoKSAtPiBOb25lOgogICAgICAgIGZyb20gaHVnZ2luZ2ZhY2VfaHViIGltcG9ydCBzbmFwc2hvdF9kb3dubG9hZAoKICAgICAgICBoZl90b2tlbiA9IG9zLmVudmlyb24uZ2V0KCJIRl9UT0tFTiIpIG9yIG9zLmVudmlyb24uZ2V0KCJIVUdHSU5HX0ZBQ0VfSFVCX1RPS0VOIikKICAgICAgICBfZG93bmxvYWRfd2l0aF9oZl90b2tlbl9yZWNvdmVyeSgKICAgICAgICAgICAgcmVwb19pZD1tb2RlbF9pZCwKICAgICAgICAgICAgbG9jYWxfZGlyPWxvY2FsX2RpciwKICAgICAgICAgICAgaGZfdG9rZW49aGZfdG9rZW4sCiAgICAgICAgICAgIHNuYXBzaG90X2Rvd25sb2FkPXNuYXBzaG90X2Rvd25sb2FkLAogICAgICAgICkKCgpAZGF0YWNsYXNzKGZyb3plbj1UcnVlKQpjbGFzcyBEb3dubG9hZFNlbGVjdGlvbjoKICAgIG1vZGVsX2lkOiBzdHIKICAgIHByb3ZpZGVyOiBzdHIKICAgIGxvY2FsX2RpcjogUGF0aAoKCmRlZiBfbG9hZF9vcHRpb25hbF9zdHJhdGVnaWVzKCkgLT4gbGlzdFtNb2RlbERvd25sb2FkU3RyYXRlZ3ldOgogICAgc3RyYXRlZ2llczogbGlzdFtNb2RlbERvd25sb2FkU3RyYXRlZ3ldID0gW10KCiAgICB0cnk6CiAgICAgICAgZnJvbSBkb3dubG9hZF9tb2RlbF9mcm9tX21vZGVsc2NvcGUgaW1wb3J0IE1vZGVsU2NvcGVEb3dubG9hZFN0cmF0ZWd5CgogICAgICAgIHN0cmF0ZWdpZXMuYXBwZW5kKE1vZGVsU2NvcGVEb3dubG9hZFN0cmF0ZWd5KCkpCiAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICMgUHJvdmlkZXIgb3BjaW9uYWw6IGlnbm9yYSBzZSBuw6NvIGVzdGl2ZXIgZGlzcG9uw612ZWwgbm8gYW1iaWVudGUuCiAgICAgICAgcGFzcwoKICAgIHJldHVybiBzdHJhdGVnaWVzCgoKZGVmIGJ1aWxkX2RlZmF1bHRfc3RyYXRlZ2llcygpIC0+IGxpc3RbTW9kZWxEb3dubG9hZFN0cmF0ZWd5XToKICAgICIiIkZhY3Rvcnkgc2ltcGxlczogb3JkZW0gZGUgcHJpb3JpZGFkZSBkZSBwcm92ZWRvcmVzIGRlIGRvd25sb2FkLiIiIgogICAgcmV0dXJuIFtIdWdnaW5nRmFjZURvd25sb2FkU3RyYXRlZ3koKSwgKl9sb2FkX29wdGlvbmFsX3N0cmF0ZWdpZXMoKV0KCgpfTU9ERUxfUkVBRFlfTUFSS0VSID0gIi5kb3dubG9hZF9jb21wbGV0ZSIKCgpkZWYgX3ByZXBhcmVfZGVzdGluYXRpb24obG9jYWxfZGlyOiBQYXRoLCAqLCBjbGVhbjogYm9vbCkgLT4gTm9uZToKICAgIGlmIGNsZWFuIGFuZCBsb2NhbF9kaXIuZXhpc3RzKCk6CiAgICAgICAgc2h1dGlsLnJtdHJlZShsb2NhbF9kaXIpCiAgICBsb2NhbF9kaXIubWtkaXIocGFyZW50cz1UcnVlLCBleGlzdF9vaz1UcnVlKQoKCmRlZiBfbW9kZWxfY2FjaGVfZGlyKGJhc2VfZGlyOiBQYXRoLCBtb2RlbF9pZDogc3RyKSAtPiBQYXRoOgogICAgIyBFdml0YSBjb2xpc8OjbyBkZSBub21lcyBlIG1hbnTDqW0gZGlyZXTDs3JpbyBzZWd1cm8gZW0gcXVhbHF1ZXIgU08uCiAgICBzYWZlX25hbWUgPSBtb2RlbF9pZC5yZXBsYWNlKCIvIiwgIl9fIikucmVwbGFjZSgiOiIsICJfIikKICAgIHJldHVybiBiYXNlX2RpciAvIHNhZmVfbmFtZQoKCmRlZiBfY2FjaGVfcmVhZHkobG9jYWxfZGlyOiBQYXRoKSAtPiBib29sOgogICAgbWFya2VyID0gbG9jYWxfZGlyIC8gX01PREVMX1JFQURZX01BUktFUgogICAgaWYgbm90IG1hcmtlci5leGlzdHMoKSBvciBub3QgbG9jYWxfZGlyLmV4aXN0cygpOgogICAgICAgIHJldHVybiBGYWxzZQogICAgcmV0dXJuIGFueShwLm5hbWUgIT0gX01PREVMX1JFQURZX01BUktFUiBmb3IgcCBpbiBsb2NhbF9kaXIuaXRlcmRpcigpKQoKCmRlZiBfbWFya19jYWNoZV9yZWFkeShsb2NhbF9kaXI6IFBhdGgpIC0+IE5vbmU6CiAgICAobG9jYWxfZGlyIC8gX01PREVMX1JFQURZX01BUktFUikud3JpdGVfdGV4dCgib2tcbiIsIGVuY29kaW5nPSJ1dGYtOCIpCgoKZGVmIF9zdGF0dXNfY29kZV9mcm9tX2Vycm9yKGV4YzogRXhjZXB0aW9uKSAtPiBpbnQgfCBOb25lOgogICAgcmVzcG9uc2UgPSBnZXRhdHRyKGV4YywgInJlc3BvbnNlIiwgTm9uZSkKICAgIGlmIHJlc3BvbnNlIGlzIE5vbmU6CiAgICAgICAgcmV0dXJuIE5vbmUKICAgIHN0YXR1c19jb2RlID0gZ2V0YXR0cihyZXNwb25zZSwgInN0YXR1c19jb2RlIiwgTm9uZSkKICAgIGlmIGlzaW5zdGFuY2Uoc3RhdHVzX2NvZGUsIGludCk6CiAgICAgICAgcmV0dXJuIHN0YXR1c19jb2RlCiAgICByZXR1cm4gTm9uZQoKCmRlZiBfaXNfaW52YWxpZF9oZl90b2tlbl9lcnJvcihleGM6IEV4Y2VwdGlvbikgLT4gYm9vbDoKICAgIG1lc3NhZ2UgPSBzdHIoZXhjKS5sb3dlcigpCiAgICBzdGF0dXNfY29kZSA9IF9zdGF0dXNfY29kZV9mcm9tX2Vycm9yKGV4YykKICAgIHRva2VuX2tleXdvcmRzID0gKCJpbnZhbGlkIHRva2VuIiwgInRva2VuIGlzIGludmFsaWQiLCAidW5hdXRob3JpemVkIiwgIjQwMSIpCiAgICBpZiBzdGF0dXNfY29kZSA9PSA0MDE6CiAgICAgICAgcmV0dXJuIFRydWUKICAgIHJldHVybiBhbnkoa2V5d29yZCBpbiBtZXNzYWdlIGZvciBrZXl3b3JkIGluIHRva2VuX2tleXdvcmRzKQoKCmRlZiBfcHJvbXB0X3JlY292ZXJfaW52YWxpZF9oZl90b2tlbigpIC0+IHR1cGxlW3N0ciwgc3RyIHwgTm9uZV06CiAgICBpZiBub3Qgc3lzLnN0ZGluLmlzYXR0eSgpOgogICAgICAgIHJldHVybiAoIm5vLXRva2VuIiwgTm9uZSkKCiAgICB3aGlsZSBUcnVlOgogICAgICAgIHByaW50KAogICAgICAgICAgICAiWyFdIE8gdG9rZW4gZG8gSHVnZ2luZ0ZhY2UgcGFyZWNlIGludsOhbGlkby4gRXNjb2xoYTogIgogICAgICAgICAgICAiWzFdIGluZm9ybWFyIG5vdm8gdG9rZW4sIFsyXSBjb250aW51YXIgc2VtIHRva2VuLiIsCiAgICAgICAgICAgIGZpbGU9c3lzLnN0ZGVyciwKICAgICAgICApCiAgICAgICAgYW5zd2VyID0gaW5wdXQoIj4gRXNjb2xoYSBbMS8yXTogIikuc3RyaXAoKS5sb3dlcigpCiAgICAgICAgaWYgYW5zd2VyIGluIHsiMSIsICJub3ZvIiwgIm5ldyJ9OgogICAgICAgICAgICBuZXdfdG9rZW4gPSBnZXRwYXNzLmdldHBhc3MoIkNvbGUgbyBub3ZvIEhGX1RPS0VOOiAiKS5zdHJpcCgpCiAgICAgICAgICAgIGlmIG5ld190b2tlbjoKICAgICAgICAgICAgICAgIHJldHVybiAoIm5ldy10b2tlbiIsIG5ld190b2tlbikKICAgICAgICAgICAgcHJpbnQoIlshXSBUb2tlbiB2YXppby4gVGVudGUgbm92YW1lbnRlLiIsIGZpbGU9c3lzLnN0ZGVycikKICAgICAgICAgICAgY29udGludWUKICAgICAgICBpZiBhbnN3ZXIgaW4geyIyIiwgIiIsICJzZW0iLCAibm8ifToKICAgICAgICAgICAgcmV0dXJuICgibm8tdG9rZW4iLCBOb25lKQogICAgICAgIHByaW50KCJbIV0gT3DDp8OjbyBpbnbDoWxpZGEuIERpZ2l0ZSAxIG91IDIuIiwgZmlsZT1zeXMuc3RkZXJyKQoKCmRlZiBfZG93bmxvYWRfd2l0aF9oZl90b2tlbl9yZWNvdmVyeSgKICAgICosCiAgICByZXBvX2lkOiBzdHIsCiAgICBsb2NhbF9kaXI6IFBhdGgsCiAgICBoZl90b2tlbjogc3RyIHwgTm9uZSwKICAgIHNuYXBzaG90X2Rvd25sb2FkLAopIC0+IE5vbmU6CiAgICBhdHRlbXB0X3Rva2VuID0gaGZfdG9rZW4KCiAgICB3aGlsZSBUcnVlOgogICAgICAgIHRyeToKICAgICAgICAgICAgc25hcHNob3RfZG93bmxvYWQoCiAgICAgICAgICAgICAgICByZXBvX2lkPXJlcG9faWQsCiAgICAgICAgICAgICAgICBsb2NhbF9kaXI9c3RyKGxvY2FsX2RpciksCiAgICAgICAgICAgICAgICB0b2tlbj1hdHRlbXB0X3Rva2VuLAogICAgICAgICAgICApCiAgICAgICAgICAgIGlmIGF0dGVtcHRfdG9rZW46CiAgICAgICAgICAgICAgICBvcy5lbnZpcm9uWyJIRl9UT0tFTiJdID0gYXR0ZW1wdF90b2tlbgogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgb3MuZW52aXJvbi5wb3AoIkhGX1RPS0VOIiwgTm9uZSkKICAgICAgICAgICAgcmV0dXJuCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgICAgIGlmIGF0dGVtcHRfdG9rZW4gYW5kIF9pc19pbnZhbGlkX2hmX3Rva2VuX2Vycm9yKGV4Yyk6CiAgICAgICAgICAgICAgICBwcmludCgKICAgICAgICAgICAgICAgICAgICAiWyFdIEZhbGhhIGRlIGF1dGVudGljYcOnw6NvIG5vIEh1Z2dpbmdGYWNlIGNvbSBvIHRva2VuIGF0dWFsLiAiCiAgICAgICAgICAgICAgICAgICAgIlZvY8OqIHBvZGUgaW5mb3JtYXIgb3V0cm8gdG9rZW4gb3Ugc2VndWlyIHNlbSB0b2tlbi4iLAogICAgICAgICAgICAgICAgICAgIGZpbGU9c3lzLnN0ZGVyciwKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGFjdGlvbiwgcmVwbGFjZW1lbnQgPSBfcHJvbXB0X3JlY292ZXJfaW52YWxpZF9oZl90b2tlbigpCiAgICAgICAgICAgICAgICBpZiBhY3Rpb24gPT0gIm5ldy10b2tlbiIgYW5kIHJlcGxhY2VtZW50OgogICAgICAgICAgICAgICAgICAgIGF0dGVtcHRfdG9rZW4gPSByZXBsYWNlbWVudAogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICBhdHRlbXB0X3Rva2VuID0gTm9uZQogICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgcmFpc2UKCgpkZWYgZG93bmxvYWRfbW9kZWxfd2l0aF9mYWxsYmFjaygKICAgIHByZWZlcnJlZF9tb2RlbF9pZDogc3RyLAogICAgZmFsbGJhY2tfbW9kZWxfaWQ6IHN0ciwKICAgIGxvY2FsX2RpcjogUGF0aCwKICAgIHN0cmF0ZWdpZXM6IGxpc3RbTW9kZWxEb3dubG9hZFN0cmF0ZWd5XSB8IE5vbmUgPSBOb25lLAopIC0+IERvd25sb2FkU2VsZWN0aW9uOgogICAgIiIiCiAgICBUZW50YSBiYWl4YXIgYHByZWZlcnJlZF9tb2RlbF9pZGA7IHNlIGZhbGhhciBlbSB0b2RvcyBvcyBwcm92ZWRvcmVzLAogICAgdGVudGEgYGZhbGxiYWNrX21vZGVsX2lkYC4KICAgICIiIgogICAgYmFzZV9kaXIgPSBsb2NhbF9kaXIuZXhwYW5kdXNlcigpCiAgICBiYXNlX2Rpci5ta2RpcihwYXJlbnRzPVRydWUsIGV4aXN0X29rPVRydWUpCiAgICBwcm92aWRlcnMgPSBzdHJhdGVnaWVzIG9yIGJ1aWxkX2RlZmF1bHRfc3RyYXRlZ2llcygpCiAgICBlcnJvcnM6IGxpc3Rbc3RyXSA9IFtdCgogICAgZm9yIG1vZGVsX2lkIGluIChwcmVmZXJyZWRfbW9kZWxfaWQsIGZhbGxiYWNrX21vZGVsX2lkKToKICAgICAgICBtb2RlbF9sb2NhbF9kaXIgPSBfbW9kZWxfY2FjaGVfZGlyKGJhc2VfZGlyLCBtb2RlbF9pZCkKICAgICAgICBpZiBfY2FjaGVfcmVhZHkobW9kZWxfbG9jYWxfZGlyKToKICAgICAgICAgICAgcmV0dXJuIERvd25sb2FkU2VsZWN0aW9uKAogICAgICAgICAgICAgICAgbW9kZWxfaWQ9bW9kZWxfaWQsCiAgICAgICAgICAgICAgICBwcm92aWRlcj0ibG9jYWwtY2FjaGUiLAogICAgICAgICAgICAgICAgbG9jYWxfZGlyPW1vZGVsX2xvY2FsX2RpciwKICAgICAgICAgICAgKQoKICAgICAgICBmb3Igc3RyYXRlZ3kgaW4gcHJvdmlkZXJzOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBwcmludCgKICAgICAgICAgICAgICAgICAgICBmIlsrXSBJbmljaWFuZG8gZG93bmxvYWQgZG8gbW9kZWxvICd7bW9kZWxfaWR9JyB2aWEge3N0cmF0ZWd5Lm5hbWV9IGVtOiB7bW9kZWxfbG9jYWxfZGlyfSIsCiAgICAgICAgICAgICAgICAgICAgZmlsZT1zeXMuc3RkZXJyLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgX3ByZXBhcmVfZGVzdGluYXRpb24obW9kZWxfbG9jYWxfZGlyLCBjbGVhbj1UcnVlKQogICAgICAgICAgICAgICAgc3RyYXRlZ3kuZG93bmxvYWQobW9kZWxfaWQ9bW9kZWxfaWQsIGxvY2FsX2Rpcj1tb2RlbF9sb2NhbF9kaXIpCiAgICAgICAgICAgICAgICBfbWFya19jYWNoZV9yZWFkeShtb2RlbF9sb2NhbF9kaXIpCiAgICAgICAgICAgICAgICByZXR1cm4gRG93bmxvYWRTZWxlY3Rpb24oCiAgICAgICAgICAgICAgICAgICAgbW9kZWxfaWQ9bW9kZWxfaWQsCiAgICAgICAgICAgICAgICAgICAgcHJvdmlkZXI9c3RyYXRlZ3kubmFtZSwKICAgICAgICAgICAgICAgICAgICBsb2NhbF9kaXI9bW9kZWxfbG9jYWxfZGlyLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICAgICAgICAgIGVycm9ycy5hcHBlbmQoZiJ7c3RyYXRlZ3kubmFtZX06e21vZGVsX2lkfToge2V4Y30iKQoKICAgIHJhaXNlIFJ1bnRpbWVFcnJvcigKICAgICAgICAiRmFsaGEgbm8gZG93bmxvYWQgZG9zIG1vZGVsb3MgZW0gdG9kb3Mgb3MgcHJvdmVkb3JlcyBjb25maWd1cmFkb3MuICIKICAgICAgICArICIgfCAiLmpvaW4oZXJyb3JzKQogICAgKQo="
566
+ B64_MODEL_DL_MS="IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCgoiIiIKUHJvdmlkZXIgb3BjaW9uYWwgZGUgZG93bmxvYWQgdmlhIE1vZGVsU2NvcGUuClVzYWRvIGFwZW5hcyBzZSBvIHBhY290ZSBgbW9kZWxzY29wZWAgZXN0aXZlciBpbnN0YWxhZG8uCiIiIgoKZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgoKY2xhc3MgTW9kZWxTY29wZURvd25sb2FkU3RyYXRlZ3k6CiAgICBuYW1lID0gIm1vZGVsc2NvcGUiCgogICAgZGVmIGRvd25sb2FkKHNlbGYsIG1vZGVsX2lkOiBzdHIsIGxvY2FsX2RpcjogUGF0aCkgLT4gTm9uZToKICAgICAgICB0cnk6CiAgICAgICAgICAgIGZyb20gbW9kZWxzY29wZS5odWIuc25hcHNob3RfZG93bmxvYWQgaW1wb3J0IHNuYXBzaG90X2Rvd25sb2FkCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgICAgIHJhaXNlIFJ1bnRpbWVFcnJvcigKICAgICAgICAgICAgICAgICJQYWNvdGUgYG1vZGVsc2NvcGVgIGluZGlzcG9uw612ZWwgcGFyYSBwcm92aWRlciBhbHRlcm5hdGl2byIKICAgICAgICAgICAgKSBmcm9tIGV4YwoKICAgICAgICBzbmFwc2hvdF9kb3dubG9hZCgKICAgICAgICAgICAgbW9kZWxfaWQ9bW9kZWxfaWQsCiAgICAgICAgICAgIGxvY2FsX2Rpcj1zdHIobG9jYWxfZGlyKSwKICAgICAgICApCg=="
567
+
568
+ decode_to_file "$B64_COMPOSE" "${EXTRACT_DIR}/docker-compose.yml"
569
+ decode_to_file "$B64_REQUIREMENTS" "${EXTRACT_DIR}/requirements.txt"
570
+ decode_to_file "$B64_INDEXER" "${EXTRACT_DIR}/indexer_full.py"
571
+ decode_to_file "$B64_MCP" "${EXTRACT_DIR}/mcp_server.py"
572
+ decode_to_file "$B64_MODEL_DL_HF" "${EXTRACT_DIR}/download_model_from_hugginface.py"
573
+ decode_to_file "$B64_MODEL_DL_MS" "${EXTRACT_DIR}/download_model_from_modelscope.py"
574
+
575
+ log_info "$(t extracted_to): ${EXTRACT_DIR}"
576
+ sed -i '' -E 's/^CHROMA_PORT = 8000$/CHROMA_PORT = _env_int("MCP_CHROMA_PORT", 8000, min_value=1)/' "${EXTRACT_DIR}/indexer_full.py" || true
577
+
578
+ # ---------------------------------------------------------------------------
579
+ # Pré-requisitos
580
+ # ---------------------------------------------------------------------------
581
+ log_info "$(t checking_prereq)"
582
+
583
+ if ! command -v python3 >/dev/null 2>&1; then
584
+ log_error "$(t python_missing)"
585
+ exit 1
586
+ fi
587
+
588
+ PY_VER="$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")"
589
+ PY_MAJOR="$(echo "$PY_VER" | cut -d. -f1)"
590
+ PY_MINOR="$(echo "$PY_VER" | cut -d. -f2)"
591
+ if [[ "$PY_MAJOR" -lt 3 ]] || [[ "$PY_MAJOR" -eq 3 && "$PY_MINOR" -lt 10 ]]; then
592
+ log_error "$(t python_min): $PY_VER"
593
+ exit 1
594
+ fi
595
+ if ! python3 -m venv --help >/dev/null 2>&1; then
596
+ log_error "$(t py_venv_missing)"
597
+ exit 1
598
+ fi
599
+
600
+ if ! command -v docker >/dev/null 2>&1; then
601
+ log_error "$(t docker_missing)"
602
+ exit 1
603
+ fi
604
+ if ! docker info >/dev/null 2>&1; then
605
+ log_error "$(t docker_daemon)"
606
+ exit 1
607
+ fi
608
+ if ! docker compose version >/dev/null 2>&1; then
609
+ log_error "$(t compose_missing)"
610
+ exit 1
611
+ fi
612
+ DOCKER_COMPOSE_CMD="docker compose"
613
+
614
+ if ! command -v curl >/dev/null 2>&1; then
615
+ log_warn "$(t no_curl)"
616
+ HAS_CURL=false
617
+ else
618
+ HAS_CURL=true
619
+ fi
620
+
621
+ log_info "$(t prereq_ok)"
622
+
623
+ if EXISTING_CHROMA_PORT="$(extract_chroma_port_from_compose "${DOCKER_COMPOSE_FILE_PATH}")"; then
624
+ CHROMA_PORT="${EXISTING_CHROMA_PORT}"
625
+ fi
626
+
627
+ reset_rag_environment() {
628
+ log_info "$(t reset_start)"
629
+ rm -rf "${VENV_DIR}" "${RAG_DB_DIR}" "${DOCKER_COMPOSE_DIR}" "${MODEL_CACHE_DIR}"
630
+ rm -f "${MCP_SERVER_DEST}" "${MODEL_DL_HF_DEST}" "${MODEL_DL_MS_DEST}"
631
+ log_info "$(t reset_done)"
632
+ }
633
+
634
+ if [[ "$CHANGE_MODEL" == "true" ]]; then
635
+ if [[ "$ONLY_INDEX" == "true" ]]; then
636
+ ONLY_INDEX=false
637
+ fi
638
+ log_info "$(t change_model_msg)"
639
+ if [[ -t 0 ]]; then
640
+ if ! ask_yes_no_loop "$(t change_model_confirm) $YES_NO_HINT "; then
641
+ log_info "$(t op_cancelled)"
642
+ exit 0
643
+ fi
644
+ else
645
+ log_warn "$(t change_model_noninteractive)"
646
+ fi
647
+ export MCP_FORCE_MODEL_RECONFIG=1
648
+ REINSTALL=true
649
+ reset_rag_environment
650
+ fi
651
+
652
+ prompt_optional_hf_token() {
653
+ if [[ -n "${HF_TOKEN_PROMPTED:-}" ]]; then
654
+ return
655
+ fi
656
+ HF_TOKEN_PROMPTED=1
657
+
658
+ if [[ -n "${HF_TOKEN:-}" || -n "${HUGGING_FACE_HUB_TOKEN:-}" ]]; then
659
+ log_info "$(t hf_detected)"
660
+ return
661
+ fi
662
+
663
+ if [[ ! -t 0 ]]; then
664
+ log_info "$(t hf_noninteractive)"
665
+ return
666
+ fi
667
+
668
+ echo ""
669
+ echo -e "${BOLD}$(t hf_title)${NC}"
670
+ echo -e "${DIM}$(t hf_desc)${NC}"
671
+ if ask_yes_no_loop "$(t hf_prompt_now)"; then
672
+ read -r -s -p "$(t hf_prompt_paste)" INPUT_HF_TOKEN
673
+ echo ""
674
+ if [[ -n "${INPUT_HF_TOKEN}" ]]; then
675
+ export HF_TOKEN="${INPUT_HF_TOKEN}"
676
+ log_info "$(t hf_set)"
677
+ else
678
+ log_warn "$(t hf_empty)"
679
+ fi
680
+ else
681
+ log_info "$(t hf_skip)"
682
+ fi
683
+ }
684
+
685
+ run_indexer_with_diagnostics() {
686
+ local target_project_dir="$1"
687
+ local indexer_status=0
688
+
689
+ local tokenizers_parallelism="${TOKENIZERS_PARALLELISM:-false}"
690
+ local force_model_reconfig="${MCP_FORCE_MODEL_RECONFIG:-0}"
691
+
692
+ set +e
693
+ TOKENIZERS_PARALLELISM="${tokenizers_parallelism}" \
694
+ MCP_FORCE_MODEL_RECONFIG="${force_model_reconfig}" \
695
+ MCP_CHROMA_PORT="${CHROMA_PORT}" \
696
+ "${VENV_PYTHON}" "${EXTRACT_DIR}/indexer_full.py" "${target_project_dir}"
697
+ indexer_status=$?
698
+ set -e
699
+
700
+ if [[ "$indexer_status" -eq 137 ]]; then
701
+ log_error "$(t index_oom_title)"
702
+ elif [[ "$indexer_status" -ne 0 ]]; then
703
+ log_error "$(t index_failed_code): ${indexer_status}"
704
+ fi
705
+
706
+ return "$indexer_status"
707
+ }
708
+
709
+ # ---------------------------------------------------------------------------
710
+ # --only-index
711
+ # ---------------------------------------------------------------------------
712
+ if [[ "$ONLY_INDEX" == "true" ]]; then
713
+ log_info "$(t only_index_mode)"
714
+ if [[ ! -f "${VENV_PYTHON}" ]]; then
715
+ log_error "$(t venv_not_found) ${VENV_DIR}"
716
+ exit 1
717
+ fi
718
+ if [[ ! -d "${PROJECT_DIR}" ]]; then
719
+ log_error "$(t path_not_found): ${PROJECT_DIR}"
720
+ exit 1
721
+ fi
722
+ prompt_optional_hf_token
723
+ log_info "$(t indexing): ${PROJECT_DIR}"
724
+ run_indexer_with_diagnostics "${PROJECT_DIR}"
725
+ log_info "$(t indexing_done)"
726
+ exit 0
727
+ fi
728
+
729
+ # ---------------------------------------------------------------------------
730
+ # Venv + dependências
731
+ # ---------------------------------------------------------------------------
732
+ log_info "$(t section_venv)"
733
+
734
+ DEPS_OK=false
735
+ if [[ "$REINSTALL" == "false" ]] && [[ -f "${VENV_PYTHON}" ]]; then
736
+ if "${VENV_PYTHON}" -c "import chromadb, sentence_transformers, langchain_text_splitters, tqdm, mcp, transformers, sys; sys.exit(0 if int(transformers.__version__.split('.')[0]) < 5 else 1)" 2>/dev/null; then
737
+ log_info "$(t deps_ok)"
738
+ DEPS_OK=true
739
+ fi
740
+ fi
741
+
742
+ if [[ "$DEPS_OK" == "false" ]]; then
743
+ if [[ ! -f "${VENV_PYTHON}" ]]; then
744
+ log_info "$(t creating_venv) ${VENV_DIR}"
745
+ python3 -m venv "${VENV_DIR}"
746
+ log_info "$(t venv_created)"
747
+ fi
748
+
749
+ log_info "$(t upgrading_pip)"
750
+ "${VENV_PIP}" install --upgrade pip
751
+
752
+ log_info "$(t deps_reinstall)"
753
+ "${VENV_PIP}" install --progress-bar on -r "${EXTRACT_DIR}/requirements.txt"
754
+
755
+ log_info "$(t deps_installed)"
756
+ fi
757
+
758
+ # ---------------------------------------------------------------------------
759
+ # ChromaDB
760
+ # ---------------------------------------------------------------------------
761
+ log_info "$(t section_chroma)"
762
+
763
+ mkdir -p "${RAG_DB_DIR}" "${DOCKER_COMPOSE_DIR}"
764
+
765
+ if [[ "$REINSTALL" == "true" ]] || [[ ! -f "${DOCKER_COMPOSE_FILE_PATH}" ]]; then
766
+ if [[ "${CHROMA_PORT_FROM_ENV}" == "true" ]]; then
767
+ CHROMA_PORT="$(choose_chroma_port_for_install "${CHROMA_PORT_DEFAULT}" "${CHROMA_PORT}")"
768
+ else
769
+ CHROMA_PORT="$(choose_chroma_port_for_install "${CHROMA_PORT_DEFAULT}")"
770
+ fi
771
+ cp "${EXTRACT_DIR}/docker-compose.yml" "${DOCKER_COMPOSE_FILE_PATH}"
772
+ sed -i '' -E 's/"[0-9]{1,5}:8000"/"'"${CHROMA_PORT}"':8000"/g' "${DOCKER_COMPOSE_FILE_PATH}"
773
+ sed -i '' -E 's|http://localhost:[0-9]{1,5}/api/v1/heartbeat|http://localhost:'"${CHROMA_PORT}"'/api/v1/heartbeat|g' "${DOCKER_COMPOSE_FILE_PATH}"
774
+ log_info "$(t compose_installed) ${DOCKER_COMPOSE_DIR}"
775
+ log_info "$(t chroma_port_selected) ${CHROMA_PORT}"
776
+ else
777
+ log_info "$(t compose_keep)"
778
+ if EXISTING_CHROMA_PORT="$(extract_chroma_port_from_compose "${DOCKER_COMPOSE_FILE_PATH}")"; then
779
+ CHROMA_PORT="${EXISTING_CHROMA_PORT}"
780
+ fi
781
+ log_info "$(t chroma_port_selected) ${CHROMA_PORT}"
782
+ fi
783
+
784
+ if docker ps --format '{{.Names}}' | grep -q '^chromadb-rag$'; then
785
+ log_info "$(t chroma_running)"
786
+ else
787
+ log_info "$(t chroma_start)"
788
+ (cd "${DOCKER_COMPOSE_DIR}" && $DOCKER_COMPOSE_CMD up -d)
789
+
790
+ log_info "$(t chroma_wait)"
791
+ WAITED=0
792
+ while true; do
793
+ if [[ "$HAS_CURL" == "true" ]] && curl -sf "http://${CHROMA_HOST}:${CHROMA_PORT}/api/v1/heartbeat" >/dev/null 2>&1; then
794
+ log_info "$(t chroma_ready) http://${CHROMA_HOST}:${CHROMA_PORT}"
795
+ break
796
+ fi
797
+ if [[ $WAITED -ge 40 ]]; then
798
+ log_warn "$(t chroma_timeout)"
799
+ break
800
+ fi
801
+ sleep 2
802
+ WAITED=$((WAITED+2))
803
+ done
804
+ fi
805
+
806
+ # ---------------------------------------------------------------------------
807
+ # Instala mcp-rag-server
808
+ # ---------------------------------------------------------------------------
809
+ log_info "$(t section_install_mcp)"
810
+
811
+ mkdir -p "${BIN_DIR}"
812
+
813
+ NEEDS_INSTALL=true
814
+ MCP_WAS_OUTDATED=false
815
+ if [[ -f "${MCP_SERVER_DEST}" ]]; then
816
+ if cmp -s <(tail -n +2 "${MCP_SERVER_DEST}") <(tail -n +2 "${EXTRACT_DIR}/mcp_server.py"); then
817
+ log_info "$(t mcp_keep)"
818
+ else
819
+ MCP_WAS_OUTDATED=true
820
+ log_warn "$(t mcp_outdated)"
821
+ fi
822
+
823
+ if [[ "$REINSTALL" == "true" ]]; then
824
+ NEEDS_INSTALL=true
825
+ elif [[ -t 0 ]]; then
826
+ if [[ "$MCP_WAS_OUTDATED" == "true" ]]; then
827
+ if ask_yes_no_loop "$(t mcp_prompt_update)"; then
828
+ NEEDS_INSTALL=true
829
+ else
830
+ NEEDS_INSTALL=false
831
+ log_info "$(t mcp_skip)"
832
+ fi
833
+ else
834
+ if ask_yes_no_loop "$(t mcp_prompt_reinstall)"; then
835
+ NEEDS_INSTALL=true
836
+ else
837
+ NEEDS_INSTALL=false
838
+ log_info "$(t mcp_skip)"
839
+ fi
840
+ fi
841
+ else
842
+ NEEDS_INSTALL=false
843
+ fi
844
+ fi
845
+
846
+ if [[ "$NEEDS_INSTALL" == "true" ]]; then
847
+ cp "${EXTRACT_DIR}/mcp_server.py" "${MCP_SERVER_DEST}"
848
+ cp "${EXTRACT_DIR}/download_model_from_hugginface.py" "${MODEL_DL_HF_DEST}"
849
+ cp "${EXTRACT_DIR}/download_model_from_modelscope.py" "${MODEL_DL_MS_DEST}"
850
+
851
+ replace_shebang "${MCP_SERVER_DEST}" "${VENV_PYTHON}"
852
+ chmod +x "${MCP_SERVER_DEST}"
853
+
854
+ log_info "$(t mcp_installed): ${MCP_SERVER_DEST}"
855
+ log_info "$(t mod_hf_installed): ${MODEL_DL_HF_DEST}"
856
+ log_info "$(t mod_ms_installed): ${MODEL_DL_MS_DEST}"
857
+ log_info "$(t shebang_set): ${VENV_PYTHON}"
858
+ fi
859
+
860
+ for RC in "${USER_HOME}/.zshrc" "${USER_HOME}/.zprofile" "${USER_HOME}/.bash_profile"; do
861
+ if [[ -f "$RC" ]] && ! grep -qF '.local/bin' "$RC"; then
862
+ echo "" >> "$RC"
863
+ echo '# RAG setup — add local bin to PATH' >> "$RC"
864
+ echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$RC"
865
+ log_info "$(t path_added) $RC"
866
+ fi
867
+ done
868
+
869
+ # ---------------------------------------------------------------------------
870
+ # Configuração MCP opcional
871
+ # ---------------------------------------------------------------------------
872
+ log_info "$(t section_mcp_cfg)"
873
+
874
+ CLAUDE_JSON="${USER_HOME}/.claude.json"
875
+ CURSOR_MCP_JSON_1="${USER_HOME}/.cursor/mcp.json"
876
+ CURSOR_MCP_JSON_2="${USER_HOME}/Library/Application Support/Cursor/User/mcp.json"
877
+ MCP_VERSION="$(detect_current_mcp_version)"
878
+
879
+ TARGET_CONFIGS=()
880
+ TARGET_LABELS=()
881
+
882
+ _append_target() {
883
+ local cfg="$1"
884
+ local label="$2"
885
+ for existing in "${TARGET_CONFIGS[@]:-}"; do
886
+ if [[ "$existing" == "$cfg" ]]; then
887
+ return 0
888
+ fi
889
+ done
890
+ TARGET_CONFIGS+=("$cfg")
891
+ TARGET_LABELS+=("$label")
892
+ }
893
+
894
+ if [[ -f "${CLAUDE_JSON}" ]]; then
895
+ _append_target "${CLAUDE_JSON}" "Claude Code"
896
+ fi
897
+ if [[ -f "${CURSOR_MCP_JSON_1}" ]]; then
898
+ _append_target "${CURSOR_MCP_JSON_1}" "Cursor"
899
+ fi
900
+ if [[ -f "${CURSOR_MCP_JSON_2}" ]]; then
901
+ _append_target "${CURSOR_MCP_JSON_2}" "Cursor"
902
+ fi
903
+
904
+ if [[ "${#TARGET_CONFIGS[@]}" -eq 0 ]]; then
905
+ log_info "$(t no_cfg_files)"
906
+ else
907
+ PENDING_CONFIGS=()
908
+ PENDING_LABELS=()
909
+
910
+ for i in "${!TARGET_CONFIGS[@]}"; do
911
+ CFG_PATH="${TARGET_CONFIGS[$i]}"
912
+ CFG_LABEL="${TARGET_LABELS[$i]}"
913
+ CHECK_RESULT=$(
914
+ python3 - "${CFG_PATH}" "${MCP_SERVER_DEST}" "${MCP_VERSION}" "${CHROMA_PORT}" <<'PYEOF'
915
+ import json
916
+ import sys
917
+ from pathlib import Path
918
+
919
+ cfg_path = Path(sys.argv[1]).expanduser()
920
+ mcp_server_command = sys.argv[2]
921
+ mcp_version = sys.argv[3]
922
+ chroma_port = sys.argv[4]
923
+
924
+ try:
925
+ data = json.loads(cfg_path.read_text(encoding="utf-8"))
926
+ except Exception:
927
+ print("needs_update")
928
+ sys.exit(0)
929
+
930
+ if not isinstance(data, dict):
931
+ print("needs_update")
932
+ sys.exit(0)
933
+
934
+ mcp_servers = data.get("mcpServers")
935
+ if mcp_servers is None:
936
+ mcp_servers = {}
937
+ if not isinstance(mcp_servers, dict):
938
+ print("needs_update")
939
+ sys.exit(0)
940
+
941
+ desired = {
942
+ "command": mcp_server_command,
943
+ "args": [],
944
+ "env": {
945
+ "CHROMA_HOST": "localhost",
946
+ "CHROMA_PORT": chroma_port,
947
+ "TOKENIZERS_PARALLELISM": "false",
948
+ },
949
+ "version": mcp_version,
950
+ }
951
+
952
+ def is_rag_server_entry(value):
953
+ if not isinstance(value, dict):
954
+ return False
955
+ cmd = value.get("command")
956
+ return isinstance(cmd, str) and "mcp-rag-server" in cmd
957
+
958
+ if "rag-codebase" in mcp_servers and mcp_servers["rag-codebase"] == desired:
959
+ print("already_up_to_date")
960
+ sys.exit(0)
961
+
962
+ for key, value in mcp_servers.items():
963
+ if key != "rag-codebase" and is_rag_server_entry(value):
964
+ print("needs_update")
965
+ sys.exit(0)
966
+
967
+ print("needs_update")
968
+ PYEOF
969
+ )
970
+
971
+ if [[ "${CHECK_RESULT}" != "already_up_to_date" ]]; then
972
+ PENDING_CONFIGS+=("${CFG_PATH}")
973
+ PENDING_LABELS+=("${CFG_LABEL}")
974
+ fi
975
+ done
976
+
977
+ if [[ "${#PENDING_CONFIGS[@]}" -eq 0 ]]; then
978
+ log_info "$(t cfg_all_current)"
979
+ else
980
+ log_info "$(t cfg_detected)"
981
+ for i in "${!PENDING_CONFIGS[@]}"; do
982
+ echo -e " - ${PENDING_LABELS[$i]}: ${PENDING_CONFIGS[$i]}"
983
+ done
984
+
985
+ APPLY_MCP_CONFIG=false
986
+ if [[ -t 0 ]]; then
987
+ if ask_yes_no_loop "$(t ask_apply_cfg)"; then
988
+ APPLY_MCP_CONFIG=true
989
+ fi
990
+ else
991
+ log_info "$(t noninteractive_cfg_skip)"
992
+ fi
993
+
994
+ if [[ "$APPLY_MCP_CONFIG" == "true" ]]; then
995
+ for i in "${!PENDING_CONFIGS[@]}"; do
996
+ CFG_PATH="${PENDING_CONFIGS[$i]}"
997
+ CFG_LABEL="${PENDING_LABELS[$i]}"
998
+
999
+ if ! RESULT=$(
1000
+ python3 - "${CFG_PATH}" "${MCP_SERVER_DEST}" "${MCP_VERSION}" "${CHROMA_PORT}" <<'PYEOF'
1001
+ import json
1002
+ import sys
1003
+ from pathlib import Path
1004
+
1005
+ cfg_path = Path(sys.argv[1]).expanduser()
1006
+ mcp_server_command = sys.argv[2]
1007
+ mcp_version = sys.argv[3]
1008
+ chroma_port = sys.argv[4]
1009
+
1010
+ try:
1011
+ data = json.loads(cfg_path.read_text(encoding="utf-8"))
1012
+ except Exception as exc:
1013
+ print(f"error:json_invalido:{exc}")
1014
+ sys.exit(2)
1015
+
1016
+ if not isinstance(data, dict):
1017
+ print("error:estrutura_invalida")
1018
+ sys.exit(2)
1019
+
1020
+ mcp_servers = data.get("mcpServers")
1021
+ if mcp_servers is None:
1022
+ mcp_servers = {}
1023
+ if not isinstance(mcp_servers, dict):
1024
+ print("error:mcpServers_invalido")
1025
+ sys.exit(2)
1026
+
1027
+ desired = {
1028
+ "command": mcp_server_command,
1029
+ "args": [],
1030
+ "env": {
1031
+ "CHROMA_HOST": "localhost",
1032
+ "CHROMA_PORT": chroma_port,
1033
+ "TOKENIZERS_PARALLELISM": "false",
1034
+ },
1035
+ "version": mcp_version,
1036
+ }
1037
+
1038
+ def is_rag_server_entry(value: object) -> bool:
1039
+ if not isinstance(value, dict):
1040
+ return False
1041
+ cmd = value.get("command")
1042
+ return isinstance(cmd, str) and "mcp-rag-server" in cmd
1043
+
1044
+ if "rag-codebase" in mcp_servers:
1045
+ if mcp_servers["rag-codebase"] == desired:
1046
+ print("ok:already_exists")
1047
+ sys.exit(0)
1048
+ mcp_servers["rag-codebase"] = desired
1049
+ data["mcpServers"] = mcp_servers
1050
+ cfg_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
1051
+ print("ok:updated_rag_codebase")
1052
+ sys.exit(0)
1053
+
1054
+ old_rag_key = None
1055
+ for key, value in mcp_servers.items():
1056
+ if is_rag_server_entry(value):
1057
+ old_rag_key = key
1058
+ break
1059
+
1060
+ if old_rag_key is not None:
1061
+ del mcp_servers[old_rag_key]
1062
+ mcp_servers["rag-codebase"] = desired
1063
+ data["mcpServers"] = mcp_servers
1064
+ cfg_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
1065
+ print(f"ok:replaced_old:{old_rag_key}")
1066
+ sys.exit(0)
1067
+
1068
+ mcp_servers["rag-codebase"] = desired
1069
+ data["mcpServers"] = mcp_servers
1070
+ cfg_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
1071
+ print("ok:added")
1072
+ PYEOF
1073
+ ); then
1074
+ log_warn "$(t cannot_update_cfg) ${CFG_LABEL} (${CFG_PATH}): ${RESULT}"
1075
+ continue
1076
+ fi
1077
+
1078
+ case "${RESULT}" in
1079
+ ok:already_exists)
1080
+ log_info "${CFG_LABEL}: $(t already_updated)"
1081
+ ;;
1082
+ ok:updated_rag_codebase)
1083
+ log_info "${CFG_LABEL}: $(t updated_cfg) ${MCP_VERSION}."
1084
+ ;;
1085
+ ok:replaced_old:*)
1086
+ log_info "${CFG_LABEL}: $(t replaced_cfg) ${MCP_VERSION})."
1087
+ ;;
1088
+ ok:added)
1089
+ log_info "${CFG_LABEL}: $(t added_cfg) ${MCP_VERSION})."
1090
+ ;;
1091
+ *)
1092
+ log_warn "${CFG_LABEL}: $(t cannot_update_cfg) -> ${RESULT}"
1093
+ ;;
1094
+ esac
1095
+ done
1096
+ fi
1097
+ fi
1098
+ fi
1099
+
1100
+ # ---------------------------------------------------------------------------
1101
+ # Indexação
1102
+ # ---------------------------------------------------------------------------
1103
+ if [[ "$SKIP_INDEX" == "false" ]]; then
1104
+ if [[ ! -d "${PROJECT_DIR}" ]]; then
1105
+ log_warn "$(t path_not_found): ${PROJECT_DIR}"
1106
+ else
1107
+ prompt_optional_hf_token
1108
+ log_info "$(t indexing): ${PROJECT_DIR}"
1109
+ run_indexer_with_diagnostics "${PROJECT_DIR}"
1110
+ log_info "$(t indexing_done)"
1111
+ fi
1112
+ fi
1113
+
1114
+ # ---------------------------------------------------------------------------
1115
+ # Resumo
1116
+ # ---------------------------------------------------------------------------
1117
+ echo ""
1118
+ echo -e "${BOLD}${GREEN}================================================================${NC}"
1119
+ echo -e "${BOLD}${GREEN} $(t setup_done)${NC}"
1120
+ echo -e "${BOLD}${GREEN}================================================================${NC}"
1121
+ echo ""
1122
+ echo -e " ${GREEN}Venv Python${NC} : ${VENV_DIR}"
1123
+ echo -e " ${GREEN}ChromaDB${NC} : http://${CHROMA_HOST}:${CHROMA_PORT} (Docker Desktop)"
1124
+ echo -e " ${GREEN}Dados${NC} : ${RAG_DB_DIR}"
1125
+ echo -e " ${GREEN}MCP Server${NC} : ${MCP_SERVER_DEST}"
1126
+ echo -e " ${GREEN}Projeto${NC} : ${PROJECT_DIR}"
1127
+ echo ""
1128
+ echo -e " $(t restart_tools)"
1129
+ echo ""