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.
- package/MCP_USAGE.md +315 -0
- package/README.md +133 -0
- package/bin/docker-compose.yml +21 -0
- package/bin/download_model_from_hugginface.py +219 -0
- package/bin/download_model_from_modelscope.py +26 -0
- package/bin/indexer_full.py +1426 -0
- package/bin/mcp_server.py +1433 -0
- package/bin/postinstall.sh +102 -0
- package/bin/rag-remove.sh +198 -0
- package/bin/rag-wrapper.sh +186 -0
- package/bin/requirements.txt +21 -0
- package/chroma_monitor.sh +857 -0
- package/how-its-work.md +285 -0
- package/package.json +49 -0
- package/rag-setup-macos.run +1129 -0
- package/rag-setup.run +1179 -0
package/rag-setup.run
ADDED
|
@@ -0,0 +1,1179 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# rag-setup.run — Instalador auto-suficiente do RAG local com ChromaDB + MCP
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Gerado automaticamente por build_run.sh
|
|
6
|
+
# Versão: 2026-03-06 16:46
|
|
7
|
+
#
|
|
8
|
+
# Uso:
|
|
9
|
+
# chmod +x rag-setup.run
|
|
10
|
+
# ./rag-setup.run # instala tudo e indexa o diretório atual
|
|
11
|
+
# ./rag-setup.run --skip-index # instala sem indexar
|
|
12
|
+
# ./rag-setup.run --only-index # apenas indexa (infra já instalada)
|
|
13
|
+
# ./rag-setup.run --reinstall # força reinstalação completa
|
|
14
|
+
# ./rag-setup.run --change-model # zera ChromaDB e reconfigura modelo/perfil
|
|
15
|
+
# =============================================================================
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# Argumentos
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
SKIP_INDEX=false
|
|
23
|
+
ONLY_INDEX=false
|
|
24
|
+
REINSTALL=false
|
|
25
|
+
CHANGE_MODEL=false
|
|
26
|
+
CUSTOM_PROJECT_DIR=""
|
|
27
|
+
|
|
28
|
+
for arg in "$@"; do
|
|
29
|
+
case "$arg" in
|
|
30
|
+
--skip-index) SKIP_INDEX=true ;;
|
|
31
|
+
--only-index) ONLY_INDEX=true ;;
|
|
32
|
+
--reinstall) REINSTALL=true ;;
|
|
33
|
+
--change-model|-cm|--chage-model|-cg) CHANGE_MODEL=true ;;
|
|
34
|
+
--help|-h)
|
|
35
|
+
echo "Uso/Usage: $0 [caminho/do/projeto|path/to/project] [--skip-index] [--only-index] [--reinstall] [--change-model|-cm]"
|
|
36
|
+
exit 0 ;;
|
|
37
|
+
-*)
|
|
38
|
+
echo "Opção desconhecida / Unknown option: $arg. Use --help."
|
|
39
|
+
exit 1 ;;
|
|
40
|
+
*)
|
|
41
|
+
# Argumento posicional = caminho do projeto
|
|
42
|
+
CUSTOM_PROJECT_DIR="$arg" ;;
|
|
43
|
+
esac
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Cores
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
|
50
|
+
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m'
|
|
51
|
+
|
|
52
|
+
UI_LANG="${RAG_SETUP_LANG:-}"
|
|
53
|
+
YES_NO_HINT="[s/N]"
|
|
54
|
+
|
|
55
|
+
set_lang_defaults() {
|
|
56
|
+
if [[ "$UI_LANG" == "en-us" ]]; then
|
|
57
|
+
YES_NO_HINT="[y/N]"
|
|
58
|
+
else
|
|
59
|
+
YES_NO_HINT="[s/N]"
|
|
60
|
+
fi
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
select_ui_language() {
|
|
64
|
+
if [[ -n "$UI_LANG" ]]; then
|
|
65
|
+
UI_LANG="$(echo "$UI_LANG" | tr '[:upper:]' '[:lower:]')"
|
|
66
|
+
case "$UI_LANG" in
|
|
67
|
+
pt-br|pt|en-us|en) ;;
|
|
68
|
+
*) UI_LANG="pt-br" ;;
|
|
69
|
+
esac
|
|
70
|
+
[[ "$UI_LANG" == "pt" ]] && UI_LANG="pt-br"
|
|
71
|
+
[[ "$UI_LANG" == "en" ]] && UI_LANG="en-us"
|
|
72
|
+
set_lang_defaults
|
|
73
|
+
return
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
if [[ ! -t 0 ]]; then
|
|
77
|
+
UI_LANG="pt-br"
|
|
78
|
+
set_lang_defaults
|
|
79
|
+
return
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
echo ""
|
|
83
|
+
echo -e "${GREEN}Idioma / Language: [1] PT-BR [2] EN-US (padrão/default: 1)${NC}"
|
|
84
|
+
read -r -p "> " LANG_CHOICE
|
|
85
|
+
case "$LANG_CHOICE" in
|
|
86
|
+
2|en|EN|en-us|EN-US|english|English) UI_LANG="en-us" ;;
|
|
87
|
+
*) UI_LANG="pt-br" ;;
|
|
88
|
+
esac
|
|
89
|
+
set_lang_defaults
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
t() {
|
|
93
|
+
local key="$1"
|
|
94
|
+
if [[ "$UI_LANG" == "en-us" ]]; then
|
|
95
|
+
case "$key" in
|
|
96
|
+
usage) echo "Usage: $0 [path/to/project] [--skip-index] [--only-index] [--reinstall] [--change-model|-cm]" ;;
|
|
97
|
+
unknown_option) echo "Unknown option: $2. Use --help to see available options." ;;
|
|
98
|
+
header_title) echo "RAG Local Setup - ChromaDB + MCP Server" ;;
|
|
99
|
+
header_project) echo "Project to index" ;;
|
|
100
|
+
section_extract) echo "Extracting embedded files" ;;
|
|
101
|
+
extracted_to) echo "Files extracted to" ;;
|
|
102
|
+
section_prereq) echo "Checking prerequisites" ;;
|
|
103
|
+
py_missing) echo "Python 3 not found. Install: sudo apt install python3 python3-venv" ;;
|
|
104
|
+
py_min) echo "Python 3.10+ required. Current:" ;;
|
|
105
|
+
py_venv_missing) echo "python3-venv not found. Install: sudo apt install python3-venv" ;;
|
|
106
|
+
py_ok) echo "Python" ;;
|
|
107
|
+
docker_missing) echo "Docker not found: https://docs.docker.com/engine/install/" ;;
|
|
108
|
+
docker_daemon) echo "Docker daemon is not running. Start it with: sudo systemctl start docker" ;;
|
|
109
|
+
compose_missing) echo "Docker Compose not found: sudo apt install docker-compose-plugin" ;;
|
|
110
|
+
docker_ok) echo "Docker + Compose OK." ;;
|
|
111
|
+
curl_missing) echo "curl not found - ChromaDB healthcheck will be skipped." ;;
|
|
112
|
+
hf_token_detected) echo "HuggingFace token detected in environment. Existing HF_TOKEN will be used." ;;
|
|
113
|
+
non_interactive_no_hf) echo "No interactive terminal; continuing without HF_TOKEN." ;;
|
|
114
|
+
hf_token_title) echo "HuggingFace token (optional)" ;;
|
|
115
|
+
hf_token_desc) echo "Speeds up downloads and increases rate limits on HuggingFace Hub." ;;
|
|
116
|
+
hf_prompt_now) echo "Do you want to provide an HF_TOKEN now? $YES_NO_HINT " ;;
|
|
117
|
+
hf_prompt_paste) echo "Paste your HF_TOKEN (hidden input): " ;;
|
|
118
|
+
hf_set) echo "HF_TOKEN set for this execution." ;;
|
|
119
|
+
hf_empty) echo "Empty token; continuing without Hugging Face authentication." ;;
|
|
120
|
+
hf_continue_no) echo "Continuing without HF_TOKEN." ;;
|
|
121
|
+
change_model_requested) echo "Model change requested (--change-model). ChromaDB reset + model reconfiguration required." ;;
|
|
122
|
+
reset_start) echo "Resetting previous RAG environment" ;;
|
|
123
|
+
reset_done) echo "Environment reset complete." ;;
|
|
124
|
+
only_index_mode) echo "Mode: index only" ;;
|
|
125
|
+
venv_not_found) echo "Venv not found at" ;;
|
|
126
|
+
run_without_only_index) echo "Run without --only-index first to install." ;;
|
|
127
|
+
path_not_found) echo "Path not found:" ;;
|
|
128
|
+
indexing) echo "Indexing:" ;;
|
|
129
|
+
indexing_done) echo "Indexing completed." ;;
|
|
130
|
+
index_failed_code) echo "Indexing failed. Indexer exit code:" ;;
|
|
131
|
+
index_oom_title) echo "Indexing stopped due to out-of-memory (OOM killer)." ;;
|
|
132
|
+
index_oom_reason) echo "The kernel force-killed the Python process (exit 137 / SIGKILL)." ;;
|
|
133
|
+
index_oom_hw_recommend) echo "For Jina in this workload, prefer RAM > 32 GB (practical baseline: 48 GB with dynamic-int8, 64 GB with default) and swap >= 16 GB." ;;
|
|
134
|
+
index_oom_next_1) echo "Immediate fallback: MCP_EMBEDDING_MODEL=bge ./rag-setup.run --only-index" ;;
|
|
135
|
+
index_oom_next_2) echo "If you need Jina, close heavy apps and increase swap before retrying." ;;
|
|
136
|
+
section_venv) echo "Setting up Python virtual environment (~/.rag_venv)" ;;
|
|
137
|
+
deps_ok) echo "Dependencies already installed in venv (transformers<5). Skipping. (use --reinstall to force)" ;;
|
|
138
|
+
deps_incompatible) echo "Existing dependencies are incompatible or incomplete (transformers<5 is required for jinaai/jina-embeddings-v3). Reinstalling." ;;
|
|
139
|
+
creating_venv) echo "Creating venv at" ;;
|
|
140
|
+
venv_created) echo "Venv created." ;;
|
|
141
|
+
upgrading_pip) echo "Upgrading pip..." ;;
|
|
142
|
+
installing_packages) echo "Installing Python packages (this may take a few minutes on first run):" ;;
|
|
143
|
+
model_download_note) echo "sentence-transformers will download the jinaai/jina-embeddings-v3 model (large)" ;;
|
|
144
|
+
deps_installed) echo "All dependencies installed in the venv." ;;
|
|
145
|
+
section_chroma) echo "Setting up ChromaDB (Docker)" ;;
|
|
146
|
+
compose_installed) echo "docker-compose.yml installed at:" ;;
|
|
147
|
+
compose_keep) echo "docker-compose.yml already exists. Keeping current file." ;;
|
|
148
|
+
chroma_port_prompt) echo "Choose ChromaDB host port [1-65535] (default: 8000): " ;;
|
|
149
|
+
chroma_port_selected) echo "Using ChromaDB host port:" ;;
|
|
150
|
+
chroma_port_invalid) echo "Invalid port. Using default 8000." ;;
|
|
151
|
+
chroma_port_in_use) echo "Port is already in use:" ;;
|
|
152
|
+
chroma_port_try_another) echo "Choose another port." ;;
|
|
153
|
+
chroma_port_auto_fallback) echo "Non-interactive mode: using next free port:" ;;
|
|
154
|
+
chroma_port_no_free) echo "Could not find a free TCP port for ChromaDB." ;;
|
|
155
|
+
chroma_running) echo "Container chromadb-rag is already running." ;;
|
|
156
|
+
chroma_start) echo "Starting ChromaDB (restart:always)..." ;;
|
|
157
|
+
chroma_wait) echo "Waiting for ChromaDB to initialize..." ;;
|
|
158
|
+
chroma_ready) echo "ChromaDB responding at" ;;
|
|
159
|
+
chroma_timeout) echo "ChromaDB did not respond in 30s. Check: docker logs chromadb-rag" ;;
|
|
160
|
+
section_install_mcp) echo "Installing mcp-rag-server globally" ;;
|
|
161
|
+
mcp_keep) echo "mcp-rag-server already installed and up to date. Keeping it." ;;
|
|
162
|
+
mcp_outdated) echo "mcp-rag-server already installed but outdated." ;;
|
|
163
|
+
mcp_prompt_update) echo "Do you want to update mcp-rag-server now? $YES_NO_HINT " ;;
|
|
164
|
+
mcp_prompt_reinstall) echo "mcp-rag-server is already up to date. Reinstall/refresh now? $YES_NO_HINT " ;;
|
|
165
|
+
mcp_skip_update) echo "Keeping existing mcp-rag-server version." ;;
|
|
166
|
+
mcp_version_detected) echo "Current MCP version detected:" ;;
|
|
167
|
+
mcp_version_non_interactive) echo "No interactive terminal; using automatic hotfix version:" ;;
|
|
168
|
+
mcp_version_prompt_current) echo "mcp-rag-server was updated. Choose MCP version strategy (current:" ;;
|
|
169
|
+
mcp_version_option_hotfix) echo "Hotfix (next minor):" ;;
|
|
170
|
+
mcp_version_option_major) echo "Major change: 2.0" ;;
|
|
171
|
+
mcp_version_option_keep) echo "Keep current version" ;;
|
|
172
|
+
mcp_version_prompt_select) echo "Select [1/2/3] (default 1): " ;;
|
|
173
|
+
mcp_version_selected) echo "Selected MCP version:" ;;
|
|
174
|
+
mcp_installed) echo "mcp-rag-server installed:" ;;
|
|
175
|
+
mod_dl_installed) echo "Download module installed:" ;;
|
|
176
|
+
mod_provider_installed) echo "Optional provider module installed:" ;;
|
|
177
|
+
shebang) echo "Shebang:" ;;
|
|
178
|
+
path_added) echo "Added ~/.local/bin to PATH in" ;;
|
|
179
|
+
section_mcp_cfg) echo "Optional MCP setup (Claude/Cursor)" ;;
|
|
180
|
+
no_default_cfg) echo "No default config file detected for automatic setup (Claude/Cursor)." ;;
|
|
181
|
+
detected_cfg_files) echo "Detected files available for configuration:" ;;
|
|
182
|
+
ask_apply_cfg) echo "Do you want to add/update MCP 'rag-codebase' in these files? $YES_NO_HINT " ;;
|
|
183
|
+
non_interactive_cfg_skip) echo "No interactive terminal; automatic MCP setup will not be applied." ;;
|
|
184
|
+
mcp_cfg_all_current) echo "MCP 'rag-codebase' already up to date in detected config files. Skipping." ;;
|
|
185
|
+
cannot_update_cfg) echo "Could not update" ;;
|
|
186
|
+
already_updated) echo "MCP 'rag-codebase' already exists and is up to date. Skipping." ;;
|
|
187
|
+
updated_version) echo "Existing MCP 'rag-codebase' was updated to version" ;;
|
|
188
|
+
replaced_old) echo "Old RAG server key replaced by 'rag-codebase' (version" ;;
|
|
189
|
+
added_cfg) echo "MCP 'rag-codebase' added (version" ;;
|
|
190
|
+
unexpected_return) echo "Unexpected return while configuring MCP:" ;;
|
|
191
|
+
user_skipped_cfg) echo "Automatic MCP setup skipped by user." ;;
|
|
192
|
+
path_not_found_skip) echo "Path not found; skipping indexing:" ;;
|
|
193
|
+
section_index_project) echo "Indexing project:" ;;
|
|
194
|
+
section_skip_index) echo "Indexing skipped (--skip-index)" ;;
|
|
195
|
+
how_to_index) echo "To index: ./rag-setup.run /path/to/project --only-index" ;;
|
|
196
|
+
setup_done) echo "Setup complete!" ;;
|
|
197
|
+
summary_next) echo "Next:" ;;
|
|
198
|
+
next_1) echo "Restart Claude Code CLI" ;;
|
|
199
|
+
next_2) echo "Use semantic_search_code" ;;
|
|
200
|
+
next_3) echo "Reindex: ./rag-setup.run --only-index" ;;
|
|
201
|
+
restart_tools) echo "Restart the tools that use your MCP rag." ;;
|
|
202
|
+
err_prefix) echo "ERROR" ;;
|
|
203
|
+
invalid_option) echo "Invalid option. Type one of the allowed answers shown in the prompt. Press Ctrl+C to exit." ;;
|
|
204
|
+
*) echo "$key" ;;
|
|
205
|
+
esac
|
|
206
|
+
else
|
|
207
|
+
case "$key" in
|
|
208
|
+
usage) echo "Uso: $0 [caminho/do/projeto] [--skip-index] [--only-index] [--reinstall] [--change-model|-cm]" ;;
|
|
209
|
+
unknown_option) echo "Opção desconhecida: $2. Use --help para ver as opções." ;;
|
|
210
|
+
header_title) echo "RAG Local Setup — ChromaDB + MCP Server" ;;
|
|
211
|
+
header_project) echo "Projeto a indexar" ;;
|
|
212
|
+
section_extract) echo "Extraindo arquivos embutidos" ;;
|
|
213
|
+
extracted_to) echo "Arquivos extraídos para" ;;
|
|
214
|
+
section_prereq) echo "Verificando pré-requisitos" ;;
|
|
215
|
+
py_missing) echo "Python 3 não encontrado. Instale: sudo apt install python3 python3-venv" ;;
|
|
216
|
+
py_min) echo "Python 3.10+ necessário. Atual:" ;;
|
|
217
|
+
py_venv_missing) echo "python3-venv não encontrado. Instale: sudo apt install python3-venv" ;;
|
|
218
|
+
py_ok) echo "Python" ;;
|
|
219
|
+
docker_missing) echo "Docker não encontrado: https://docs.docker.com/engine/install/" ;;
|
|
220
|
+
docker_daemon) echo "Docker daemon não está rodando. Inicie: sudo systemctl start docker" ;;
|
|
221
|
+
compose_missing) echo "Docker Compose não encontrado: sudo apt install docker-compose-plugin" ;;
|
|
222
|
+
docker_ok) echo "Docker + Compose OK." ;;
|
|
223
|
+
curl_missing) echo "curl não encontrado — healthcheck do ChromaDB será pulado." ;;
|
|
224
|
+
hf_token_detected) echo "Token do HuggingFace detectado no ambiente. O HF_TOKEN existente será usado." ;;
|
|
225
|
+
non_interactive_no_hf) echo "Sem terminal interativo; seguindo sem HF_TOKEN." ;;
|
|
226
|
+
hf_token_title) echo "Token HuggingFace (opcional)" ;;
|
|
227
|
+
hf_token_desc) echo "Acelera download e aumenta limite de requisições no HuggingFace Hub." ;;
|
|
228
|
+
hf_prompt_now) echo "Deseja informar um HF_TOKEN agora? $YES_NO_HINT " ;;
|
|
229
|
+
hf_prompt_paste) echo "Cole seu HF_TOKEN (entrada oculta): " ;;
|
|
230
|
+
hf_set) echo "HF_TOKEN definido para esta execução." ;;
|
|
231
|
+
hf_empty) echo "Token vazio; seguindo sem autenticação no Hugging Face." ;;
|
|
232
|
+
hf_continue_no) echo "Seguindo sem HF_TOKEN." ;;
|
|
233
|
+
change_model_requested) echo "Troca de modelo solicitada (--change-model). É necessário zerar o ChromaDB e reindexar." ;;
|
|
234
|
+
reset_start) echo "Zerando ambiente RAG anterior" ;;
|
|
235
|
+
reset_done) echo "Reset do ambiente concluído." ;;
|
|
236
|
+
only_index_mode) echo "Modo: apenas indexação" ;;
|
|
237
|
+
venv_not_found) echo "Venv não encontrado em" ;;
|
|
238
|
+
run_without_only_index) echo "Execute sem --only-index primeiro para instalar." ;;
|
|
239
|
+
path_not_found) echo "Caminho não encontrado:" ;;
|
|
240
|
+
indexing) echo "Indexando:" ;;
|
|
241
|
+
indexing_done) echo "Indexação concluída." ;;
|
|
242
|
+
index_failed_code) echo "Indexação falhou. Código de saída do indexador:" ;;
|
|
243
|
+
index_oom_title) echo "Indexação interrompida por falta de memória (OOM killer)." ;;
|
|
244
|
+
index_oom_reason) echo "O kernel encerrou o processo Python à força (exit 137 / SIGKILL)." ;;
|
|
245
|
+
index_oom_hw_recommend) echo "Para Jina nesta carga, prefira RAM > 32 GB (referência prática: 48 GB com dynamic-int8, 64 GB com default) e swap >= 16 GB." ;;
|
|
246
|
+
index_oom_next_1) echo "Fallback imediato: MCP_EMBEDDING_MODEL=bge ./rag-setup.run --only-index" ;;
|
|
247
|
+
index_oom_next_2) echo "Se precisar usar Jina, feche apps pesados e aumente swap antes de tentar novamente." ;;
|
|
248
|
+
section_venv) echo "Configurando ambiente virtual Python (~/.rag_venv)" ;;
|
|
249
|
+
deps_ok) echo "Dependências já instaladas no venv (transformers<5). Pulando. (use --reinstall para forçar)" ;;
|
|
250
|
+
deps_incompatible) echo "Dependências existentes incompatíveis ou incompletas (é necessário transformers<5 para jinaai/jina-embeddings-v3). Reinstalando." ;;
|
|
251
|
+
creating_venv) echo "Criando venv em" ;;
|
|
252
|
+
venv_created) echo "Venv criado." ;;
|
|
253
|
+
upgrading_pip) echo "Atualizando pip..." ;;
|
|
254
|
+
installing_packages) echo "Instalando pacotes Python (isso pode levar alguns minutos na 1ª vez):" ;;
|
|
255
|
+
model_download_note) echo "sentence-transformers baixará o modelo jinaai/jina-embeddings-v3 (grande)" ;;
|
|
256
|
+
deps_installed) echo "Todas as dependências instaladas no venv." ;;
|
|
257
|
+
section_chroma) echo "Configurando ChromaDB (Docker)" ;;
|
|
258
|
+
compose_installed) echo "docker-compose.yml instalado em:" ;;
|
|
259
|
+
compose_keep) echo "docker-compose.yml já existe. Mantendo." ;;
|
|
260
|
+
chroma_port_prompt) echo "Porta do ChromaDB [1-65535] (padrão: 8000): " ;;
|
|
261
|
+
chroma_port_selected) echo "Porta escolhida do ChromaDB:" ;;
|
|
262
|
+
chroma_port_invalid) echo "Porta inválida. Usando padrão 8000." ;;
|
|
263
|
+
chroma_port_in_use) echo "Porta já está em uso:" ;;
|
|
264
|
+
chroma_port_try_another) echo "Escolha outra porta." ;;
|
|
265
|
+
chroma_port_auto_fallback) echo "Modo não interativo: usando próxima porta livre:" ;;
|
|
266
|
+
chroma_port_no_free) echo "Não foi possível encontrar porta TCP livre para o ChromaDB." ;;
|
|
267
|
+
chroma_running) echo "Container chromadb-rag já está rodando." ;;
|
|
268
|
+
chroma_start) echo "Iniciando ChromaDB (restart:always)..." ;;
|
|
269
|
+
chroma_wait) echo "Aguardando ChromaDB inicializar..." ;;
|
|
270
|
+
chroma_ready) echo "ChromaDB respondendo em" ;;
|
|
271
|
+
chroma_timeout) echo "ChromaDB não respondeu em 30s. Verifique: docker logs chromadb-rag" ;;
|
|
272
|
+
section_install_mcp) echo "Instalando mcp-rag-server globalmente" ;;
|
|
273
|
+
mcp_keep) echo "mcp-rag-server já instalado e atualizado. Mantendo." ;;
|
|
274
|
+
mcp_outdated) echo "mcp-rag-server já instalado, mas desatualizado." ;;
|
|
275
|
+
mcp_prompt_update) echo "Deseja atualizar o mcp-rag-server agora? $YES_NO_HINT " ;;
|
|
276
|
+
mcp_prompt_reinstall) echo "mcp-rag-server já está atualizado. Deseja reinstalar/atualizar agora? $YES_NO_HINT " ;;
|
|
277
|
+
mcp_skip_update) echo "Mantendo a versão atual do mcp-rag-server." ;;
|
|
278
|
+
mcp_version_detected) echo "Versão MCP atual detectada:" ;;
|
|
279
|
+
mcp_version_non_interactive) echo "Sem terminal interativo; usando versão hotfix automática:" ;;
|
|
280
|
+
mcp_version_prompt_current) echo "mcp-rag-server foi atualizado. Escolha a estratégia de versão MCP (atual:" ;;
|
|
281
|
+
mcp_version_option_hotfix) echo "Hotfix (próximo minor):" ;;
|
|
282
|
+
mcp_version_option_major) echo "Mudança major: 2.0" ;;
|
|
283
|
+
mcp_version_option_keep) echo "Manter versão atual" ;;
|
|
284
|
+
mcp_version_prompt_select) echo "Selecione [1/2/3] (padrão 1): " ;;
|
|
285
|
+
mcp_version_selected) echo "Versão MCP selecionada:" ;;
|
|
286
|
+
mcp_installed) echo "mcp-rag-server instalado:" ;;
|
|
287
|
+
mod_dl_installed) echo "Módulo de download instalado:" ;;
|
|
288
|
+
mod_provider_installed) echo "Módulo de provider opcional instalado:" ;;
|
|
289
|
+
shebang) echo "Shebang:" ;;
|
|
290
|
+
path_added) echo "Adicionado ~/.local/bin ao PATH em" ;;
|
|
291
|
+
section_mcp_cfg) echo "Configuração opcional de MCP (Claude/Cursor)" ;;
|
|
292
|
+
no_default_cfg) echo "Nenhum arquivo default detectado para configuração automática (Claude/Cursor)." ;;
|
|
293
|
+
detected_cfg_files) echo "Arquivos detectados para possível configuração:" ;;
|
|
294
|
+
ask_apply_cfg) echo "Deseja adicionar/atualizar o MCP 'rag-codebase' nesses arquivos? $YES_NO_HINT " ;;
|
|
295
|
+
non_interactive_cfg_skip) echo "Sem terminal interativo; configuração automática de MCP não será aplicada." ;;
|
|
296
|
+
mcp_cfg_all_current) echo "MCP 'rag-codebase' já está atualizado nos arquivos detectados. Pulando." ;;
|
|
297
|
+
cannot_update_cfg) echo "Não foi possível atualizar" ;;
|
|
298
|
+
already_updated) echo "MCP 'rag-codebase' já existe e está atualizado. Ignorando." ;;
|
|
299
|
+
updated_version) echo "MCP 'rag-codebase' existente foi atualizado para a versão" ;;
|
|
300
|
+
replaced_old) echo "Servidor RAG antigo substituído por 'rag-codebase' (versão" ;;
|
|
301
|
+
added_cfg) echo "MCP 'rag-codebase' adicionado (versão" ;;
|
|
302
|
+
unexpected_return) echo "Retorno inesperado ao configurar MCP:" ;;
|
|
303
|
+
user_skipped_cfg) echo "Configuração automática de MCP ignorada pelo usuário." ;;
|
|
304
|
+
path_not_found_skip) echo "Caminho não encontrado. Pulando indexação:" ;;
|
|
305
|
+
section_index_project) echo "Indexando o projeto:" ;;
|
|
306
|
+
section_skip_index) echo "Indexação pulada (--skip-index)" ;;
|
|
307
|
+
how_to_index) echo "Para indexar: ./rag-setup.run /caminho/do/projeto --only-index" ;;
|
|
308
|
+
setup_done) echo "Setup concluído!" ;;
|
|
309
|
+
summary_next) echo "Próximos:" ;;
|
|
310
|
+
next_1) echo "Reinicie o Claude Code CLI" ;;
|
|
311
|
+
next_2) echo "Use semantic_search_code" ;;
|
|
312
|
+
next_3) echo "Reindexar: ./rag-setup.run --only-index" ;;
|
|
313
|
+
restart_tools) echo "Reinicie as ferramentas que usam seu MCP rag." ;;
|
|
314
|
+
err_prefix) echo "ERRO" ;;
|
|
315
|
+
invalid_option) echo "Opção inválida. Digite uma das respostas permitidas exibidas no prompt. Para sair, pressione Ctrl+C." ;;
|
|
316
|
+
*) echo "$key" ;;
|
|
317
|
+
esac
|
|
318
|
+
fi
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
is_yes_answer() {
|
|
322
|
+
local ans="$(echo "${1:-}" | tr '[:upper:]' '[:lower:]')"
|
|
323
|
+
if [[ "$UI_LANG" == "en-us" ]]; then
|
|
324
|
+
[[ "$ans" == "y" || "$ans" == "yes" ]]
|
|
325
|
+
else
|
|
326
|
+
[[ "$ans" == "s" || "$ans" == "sim" || "$ans" == "y" || "$ans" == "yes" ]]
|
|
327
|
+
fi
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
is_no_answer() {
|
|
331
|
+
local ans="$(echo "${1:-}" | tr '[:upper:]' '[:lower:]')"
|
|
332
|
+
if [[ -z "$ans" ]]; then
|
|
333
|
+
return 0
|
|
334
|
+
fi
|
|
335
|
+
if [[ "$UI_LANG" == "en-us" ]]; then
|
|
336
|
+
[[ "$ans" == "n" || "$ans" == "no" ]]
|
|
337
|
+
else
|
|
338
|
+
[[ "$ans" == "n" || "$ans" == "nao" || "$ans" == "não" || "$ans" == "no" ]]
|
|
339
|
+
fi
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
ask_yes_no_loop() {
|
|
343
|
+
local prompt="$1"
|
|
344
|
+
local answer=""
|
|
345
|
+
while true; do
|
|
346
|
+
read -r -p "$prompt" answer
|
|
347
|
+
if is_yes_answer "$answer"; then
|
|
348
|
+
return 0
|
|
349
|
+
fi
|
|
350
|
+
if is_no_answer "$answer"; then
|
|
351
|
+
return 1
|
|
352
|
+
fi
|
|
353
|
+
log_warn "$(t invalid_option)"
|
|
354
|
+
done
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
normalize_port() {
|
|
358
|
+
local raw_port="${1:-}"
|
|
359
|
+
if [[ "${raw_port}" =~ ^[0-9]+$ ]] && (( raw_port >= 1 && raw_port <= 65535 )); then
|
|
360
|
+
echo "${raw_port}"
|
|
361
|
+
return 0
|
|
362
|
+
fi
|
|
363
|
+
return 1
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
extract_chroma_port_from_compose() {
|
|
367
|
+
local compose_file="$1"
|
|
368
|
+
local parsed_port=""
|
|
369
|
+
|
|
370
|
+
if [[ ! -f "${compose_file}" ]]; then
|
|
371
|
+
return 1
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
parsed_port="$(sed -n 's/^[[:space:]]*-[[:space:]]*"\?\([0-9]\{1,5\}\):8000"\?.*/\1/p' "${compose_file}" | head -n 1)"
|
|
375
|
+
if normalize_port "${parsed_port}" >/dev/null; then
|
|
376
|
+
echo "${parsed_port}"
|
|
377
|
+
return 0
|
|
378
|
+
fi
|
|
379
|
+
return 1
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
is_port_in_use() {
|
|
383
|
+
local port="$1"
|
|
384
|
+
local os_name
|
|
385
|
+
os_name="$(uname -s 2>/dev/null || echo Linux)"
|
|
386
|
+
|
|
387
|
+
if [[ "${os_name}" == "Darwin" ]]; then
|
|
388
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
389
|
+
lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1
|
|
390
|
+
return $?
|
|
391
|
+
fi
|
|
392
|
+
if command -v netstat >/dev/null 2>&1; then
|
|
393
|
+
netstat -an -p tcp 2>/dev/null | grep -E "[\\.:]${port}[[:space:]].*LISTEN" >/dev/null 2>&1
|
|
394
|
+
return $?
|
|
395
|
+
fi
|
|
396
|
+
else
|
|
397
|
+
if command -v ss >/dev/null 2>&1; then
|
|
398
|
+
ss -ltnH "( sport = :${port} )" 2>/dev/null | grep -q .
|
|
399
|
+
return $?
|
|
400
|
+
fi
|
|
401
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
402
|
+
lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1
|
|
403
|
+
return $?
|
|
404
|
+
fi
|
|
405
|
+
if command -v netstat >/dev/null 2>&1; then
|
|
406
|
+
netstat -ltn 2>/dev/null | awk '{print $4}' | grep -E "(^|:)${port}$" >/dev/null 2>&1
|
|
407
|
+
return $?
|
|
408
|
+
fi
|
|
409
|
+
fi
|
|
410
|
+
|
|
411
|
+
return 1
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
find_next_free_port() {
|
|
415
|
+
local start_port="$1"
|
|
416
|
+
local port=0
|
|
417
|
+
for ((port=start_port; port<=65535; port++)); do
|
|
418
|
+
if ! is_port_in_use "${port}"; then
|
|
419
|
+
echo "${port}"
|
|
420
|
+
return 0
|
|
421
|
+
fi
|
|
422
|
+
done
|
|
423
|
+
return 1
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
choose_chroma_port_for_install() {
|
|
427
|
+
local default_port="$1"
|
|
428
|
+
local preferred_port="${2:-}"
|
|
429
|
+
local chosen_port=""
|
|
430
|
+
local normalized=""
|
|
431
|
+
local fallback_port=""
|
|
432
|
+
|
|
433
|
+
chosen_port="${preferred_port}"
|
|
434
|
+
|
|
435
|
+
while true; do
|
|
436
|
+
if [[ -z "${chosen_port}" ]]; then
|
|
437
|
+
if [[ ! -t 0 ]]; then
|
|
438
|
+
chosen_port="${default_port}"
|
|
439
|
+
else
|
|
440
|
+
read -r -p "$(t chroma_port_prompt)" chosen_port
|
|
441
|
+
if [[ -z "${chosen_port}" ]]; then
|
|
442
|
+
chosen_port="${default_port}"
|
|
443
|
+
fi
|
|
444
|
+
fi
|
|
445
|
+
fi
|
|
446
|
+
|
|
447
|
+
if ! normalized="$(normalize_port "${chosen_port}")"; then
|
|
448
|
+
log_warn "$(t chroma_port_invalid)"
|
|
449
|
+
if [[ -t 0 ]]; then
|
|
450
|
+
chosen_port=""
|
|
451
|
+
continue
|
|
452
|
+
fi
|
|
453
|
+
normalized="${default_port}"
|
|
454
|
+
fi
|
|
455
|
+
|
|
456
|
+
if is_port_in_use "${normalized}"; then
|
|
457
|
+
log_warn "$(t chroma_port_in_use) ${normalized}. $(t chroma_port_try_another)"
|
|
458
|
+
if [[ -t 0 ]]; then
|
|
459
|
+
chosen_port=""
|
|
460
|
+
continue
|
|
461
|
+
fi
|
|
462
|
+
|
|
463
|
+
if fallback_port="$(find_next_free_port "$((normalized + 1))")"; then
|
|
464
|
+
log_warn "$(t chroma_port_auto_fallback) ${fallback_port}"
|
|
465
|
+
echo "${fallback_port}"
|
|
466
|
+
return 0
|
|
467
|
+
fi
|
|
468
|
+
log_error "$(t chroma_port_no_free)"
|
|
469
|
+
exit 1
|
|
470
|
+
fi
|
|
471
|
+
|
|
472
|
+
echo "${normalized}"
|
|
473
|
+
return 0
|
|
474
|
+
done
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
detect_current_mcp_version() {
|
|
478
|
+
python3 - "${USER_HOME}" <<'PYEOF'
|
|
479
|
+
import json
|
|
480
|
+
import re
|
|
481
|
+
import sys
|
|
482
|
+
from pathlib import Path
|
|
483
|
+
|
|
484
|
+
user_home = Path(sys.argv[1]).expanduser()
|
|
485
|
+
paths = [
|
|
486
|
+
user_home / ".claude.json",
|
|
487
|
+
user_home / ".cursor" / "mcp.json",
|
|
488
|
+
user_home / ".config" / "Cursor" / "User" / "mcp.json",
|
|
489
|
+
]
|
|
490
|
+
version_re = re.compile(r"^(\d+)\.(\d+)$")
|
|
491
|
+
best = None
|
|
492
|
+
|
|
493
|
+
for path in paths:
|
|
494
|
+
if not path.exists():
|
|
495
|
+
continue
|
|
496
|
+
try:
|
|
497
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
498
|
+
except Exception:
|
|
499
|
+
continue
|
|
500
|
+
if not isinstance(data, dict):
|
|
501
|
+
continue
|
|
502
|
+
mcp_servers = data.get("mcpServers")
|
|
503
|
+
if not isinstance(mcp_servers, dict):
|
|
504
|
+
continue
|
|
505
|
+
rag_cfg = mcp_servers.get("rag-codebase")
|
|
506
|
+
if not isinstance(rag_cfg, dict):
|
|
507
|
+
continue
|
|
508
|
+
version = rag_cfg.get("version")
|
|
509
|
+
if not isinstance(version, str):
|
|
510
|
+
continue
|
|
511
|
+
match = version_re.match(version.strip())
|
|
512
|
+
if not match:
|
|
513
|
+
continue
|
|
514
|
+
parsed = (int(match.group(1)), int(match.group(2)))
|
|
515
|
+
if best is None or parsed > best:
|
|
516
|
+
best = parsed
|
|
517
|
+
|
|
518
|
+
if best is None:
|
|
519
|
+
print("1.0")
|
|
520
|
+
else:
|
|
521
|
+
print(f"{best[0]}.{best[1]}")
|
|
522
|
+
PYEOF
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
log_info() { echo -e "${GREEN}[+]${NC} $*"; }
|
|
526
|
+
log_warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
|
527
|
+
log_error() { echo -e "${RED}[$(t err_prefix)]${NC} $*" >&2; }
|
|
528
|
+
log_section() { echo -e "\n${BLUE}${BOLD}==> $*${NC}"; }
|
|
529
|
+
|
|
530
|
+
select_ui_language
|
|
531
|
+
|
|
532
|
+
# ---------------------------------------------------------------------------
|
|
533
|
+
# Configuração
|
|
534
|
+
# ---------------------------------------------------------------------------
|
|
535
|
+
USER_HOME="${HOME}"
|
|
536
|
+
VENV_DIR="${USER_HOME}/.rag_venv"
|
|
537
|
+
VENV_PYTHON="${VENV_DIR}/bin/python3"
|
|
538
|
+
VENV_PIP="${VENV_DIR}/bin/pip"
|
|
539
|
+
DOCKER_COMPOSE_DIR="${USER_HOME}/docker-chromadb"
|
|
540
|
+
RAG_DB_DIR="${USER_HOME}/.rag_db"
|
|
541
|
+
CHROMA_HOST="localhost"
|
|
542
|
+
CHROMA_PORT_DEFAULT="8000"
|
|
543
|
+
CHROMA_PORT_FROM_ENV=false
|
|
544
|
+
if CHROMA_PORT_NORMALIZED="$(normalize_port "${MCP_CHROMA_PORT:-}")"; then
|
|
545
|
+
CHROMA_PORT="${CHROMA_PORT_NORMALIZED}"
|
|
546
|
+
CHROMA_PORT_FROM_ENV=true
|
|
547
|
+
else
|
|
548
|
+
CHROMA_PORT="${CHROMA_PORT_DEFAULT}"
|
|
549
|
+
fi
|
|
550
|
+
BIN_DIR="${USER_HOME}/.local/bin"
|
|
551
|
+
MCP_SERVER_DEST="${BIN_DIR}/mcp-rag-server"
|
|
552
|
+
MODEL_DL_HF_DEST="${BIN_DIR}/download_model_from_hugginface.py"
|
|
553
|
+
MODEL_DL_MS_DEST="${BIN_DIR}/download_model_from_modelscope.py"
|
|
554
|
+
MODEL_CACHE_DIR="${USER_HOME}/.cache/my-custom-rag-python/models"
|
|
555
|
+
EXTRACT_DIR="$(mktemp -d /tmp/rag-setup.XXXXXX)"
|
|
556
|
+
DOCKER_COMPOSE_FILE_PATH="${DOCKER_COMPOSE_DIR}/docker-compose.yml"
|
|
557
|
+
# Usa o argumento posicional se fornecido, senão usa o diretório atual
|
|
558
|
+
if [[ -n "$CUSTOM_PROJECT_DIR" ]]; then
|
|
559
|
+
PROJECT_DIR="$(cd "$CUSTOM_PROJECT_DIR" && pwd)"
|
|
560
|
+
else
|
|
561
|
+
PROJECT_DIR="$(pwd)"
|
|
562
|
+
fi
|
|
563
|
+
|
|
564
|
+
trap 'rm -rf "$EXTRACT_DIR"' EXIT
|
|
565
|
+
|
|
566
|
+
echo ""
|
|
567
|
+
echo -e "${BOLD}${BLUE}================================================================${NC}"
|
|
568
|
+
echo -e "${BOLD}${BLUE} $(t header_title)${NC}"
|
|
569
|
+
echo -e "${BOLD}${BLUE}================================================================${NC}"
|
|
570
|
+
echo -e " $(t header_project): ${BOLD}${PROJECT_DIR}${NC}"
|
|
571
|
+
echo ""
|
|
572
|
+
|
|
573
|
+
# ---------------------------------------------------------------------------
|
|
574
|
+
# Extrai arquivos embutidos
|
|
575
|
+
# ---------------------------------------------------------------------------
|
|
576
|
+
log_section "$(t section_extract)"
|
|
577
|
+
|
|
578
|
+
echo "c2VydmljZXM6CiAgY2hyb21hZGI6CiAgICBpbWFnZTogY2hyb21hZGIvY2hyb21hOmxhdGVzdAogICAgY29udGFpbmVyX25hbWU6IGNocm9tYWRiLXJhZwogICAgcG9ydHM6CiAgICAgIC0gIjgwMDA6ODAwMCIKICAgIHZvbHVtZXM6CiAgICAgICMgUGVyc2lzdGUgbyBiYW5jbyBkaXJldGFtZW50ZSBuYSBwYXN0YSBkbyB1c3XDoXJpbyBubyBob3N0CiAgICAgIC0gJHtIT01FfS8ucmFnX2RiOi9jaHJvbWEvY2hyb21hCiAgICBlbnZpcm9ubWVudDoKICAgICAgIyBIYWJpbGl0YSBhdXRlbnRpY2HDp8OjbyBhbsO0bmltYSAoc2VtIHRva2VuKSBwYXJhIHVzbyBsb2NhbAogICAgICAtIEFOT05ZTUlaRURfVEVMRU1FVFJZPWZhbHNlCiAgICAgIC0gQ0hST01BX1NFUlZFUl9BVVRITl9DUkVERU5USUFMU19GSUxFPSIiCiAgICAgIC0gQ0hST01BX1NFUlZFUl9BVVRITl9QUk9WSURFUj0iIgogICAgcmVzdGFydDogYWx3YXlzCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQiLCAiY3VybCIsICItZiIsICJodHRwOi8vbG9jYWxob3N0OjgwMDAvYXBpL3YxL2hlYXJ0YmVhdCJdCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMK" | base64 -d > "${EXTRACT_DIR}/docker-compose.yml"
|
|
579
|
+
echo "IyBDbGllbnRlIEhUVFAgZG8gQ2hyb21hREIgKGNvbmVjdGEgYW8gc2Vydmlkb3IgRG9ja2VyKQpjaHJvbWFkYj49MC41LjAKCiMgTW9kZWxvIGRlIGVtYmVkZGluZ3MgbG9jYWwgcm9kYW5kbyBuYSBDUFUKc2VudGVuY2UtdHJhbnNmb3JtZXJzPj0zLjAuMAojIERlcGVuZMOqbmNpYSB1c2FkYSBwb3IgbW9kZWxvcyBKaW5hIHYzIGNvbSBjw7NkaWdvIHJlbW90bwplaW5vcHM+PTAuNy4wCiMgRXZpdGEgaW5jb21wYXRpYmlsaWRhZGVzIGNvbmhlY2lkYXMgZG8gSmluYSB2MyBjb20gQVBJcyBub3ZhcyBkbyB0cmFuc2Zvcm1lcnMgNS54CnRyYW5zZm9ybWVyczw1CgojIFNwbGl0dGVyIGRlIHRleHRvIHBhcmEgY2h1bmtpbmcgaW50ZWxpZ2VudGUKbGFuZ2NoYWluLXRleHQtc3BsaXR0ZXJzPj0wLjIuMAoKIyBCYXJyYSBkZSBwcm9ncmVzc28gdmlzdWFsIG5vIHRlcm1pbmFsCnRxZG0+PTQuNjYuMAoKIyBNb25pdG9yIGRlIG1lbcOzcmlhIGVtIHRlbXBvIHJlYWwgZHVyYW50ZSBpbmRleGHDp8Ojbwpwc3V0aWw+PTUuOS4wCgojIFNESyBvZmljaWFsIGRvIE1DUCAoTW9kZWwgQ29udGV4dCBQcm90b2NvbCkgZGEgQW50aHJvcGljCm1jcD49MS4wLjAK" | base64 -d > "${EXTRACT_DIR}/requirements.txt"
|
|
580
|
+
echo "IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoiIiIKaW5kZXhlcl9mdWxsLnB5IOKAlCBTY3JpcHQgc3RhbmRhbG9uZSBkZSBpbmRleGHDp8OjbyBkbyBSQUcgbG9jYWwuCgpVc286CiAgICBweXRob24gaW5kZXhlcl9mdWxsLnB5IFtjYW1pbmhvX2RvX3Byb2pldG9dCgpTZSBuZW5odW0gY2FtaW5obyBmb3IgcGFzc2FkbywgdXNhIG8gZGlyZXTDs3JpbyBhdHVhbC4KTyBDaHJvbWFEQiBkZXZlIGVzdGFyIHJvZGFuZG8gdmlhIERvY2tlciBlbSBsb2NhbGhvc3Q6ODAwMC4KIiIiCgppbXBvcnQgb3MKaW1wb3J0IHN5cwppbXBvcnQgaGFzaGxpYgppbXBvcnQgYXJncGFyc2UKaW1wb3J0IHNodXRpbAppbXBvcnQgbG9nZ2luZwppbXBvcnQgZ2MKaW1wb3J0IGpzb24KZnJvbSB0aW1lIGltcG9ydCBwZXJmX2NvdW50ZXIsIHRpbWUKZnJvbSBjb2xsZWN0aW9ucy5hYmMgaW1wb3J0IEl0ZXJhdG9yCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aApmcm9tIGRhdGFjbGFzc2VzIGltcG9ydCBkYXRhY2xhc3MKZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUKCiMgRXZpdGEgYXZpc29zICJhZHZpc29yeSIgcnVpZG9zb3MgZG8gdHJhbnNmb3JtZXJzIG5vIGZsdXhvIGludGVyYXRpdm8uCm9zLmVudmlyb24uc2V0ZGVmYXVsdCgiVFJBTlNGT1JNRVJTX05PX0FEVklTT1JZX1dBUk5JTkdTIiwgIjEiKQoKCmNsYXNzIF9Ub3JjaER0eXBlV2FybmluZ0ZpbHRlcihsb2dnaW5nLkZpbHRlcik6CiAgICBkZWYgZmlsdGVyKHNlbGYsIHJlY29yZDogbG9nZ2luZy5Mb2dSZWNvcmQpIC0+IGJvb2w6CiAgICAgICAgcmV0dXJuICJgdG9yY2hfZHR5cGVgIGlzIGRlcHJlY2F0ZWQhIFVzZSBgZHR5cGVgIGluc3RlYWQhIiBub3QgaW4gcmVjb3JkLmdldE1lc3NhZ2UoKQoKCmZvciBfbG9nZ2VyX25hbWUgaW4gKCJ0cmFuc2Zvcm1lcnMuY29uZmlndXJhdGlvbl91dGlscyIsICJ0cmFuc2Zvcm1lcnMubW9kZWxpbmdfdXRpbHMiKToKICAgIGxvZ2dpbmcuZ2V0TG9nZ2VyKF9sb2dnZXJfbmFtZSkuYWRkRmlsdGVyKF9Ub3JjaER0eXBlV2FybmluZ0ZpbHRlcigpKQoKaW1wb3J0IGNocm9tYWRiCmZyb20gc2VudGVuY2VfdHJhbnNmb3JtZXJzIGltcG9ydCBTZW50ZW5jZVRyYW5zZm9ybWVyCmZyb20gbGFuZ2NoYWluX3RleHRfc3BsaXR0ZXJzIGltcG9ydCBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIKZnJvbSB0cWRtIGltcG9ydCB0cWRtCmZyb20gZG93bmxvYWRfbW9kZWxfZnJvbV9odWdnaW5mYWNlIGltcG9ydCBkb3dubG9hZF9tb2RlbF93aXRoX2ZhbGxiYWNrCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENvbmZpZ3VyYcOnw7VlcyBnbG9iYWlzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKZGVmIF9lbnZfaW50KG5hbWU6IHN0ciwgZGVmYXVsdDogaW50LCAqLCBtaW5fdmFsdWU6IGludCA9IDEpIC0+IGludDoKICAgIHJhdyA9IG9zLmVudmlyb24uZ2V0KG5hbWUpCiAgICBpZiByYXcgaXMgTm9uZToKICAgICAgICByZXR1cm4gbWF4KG1pbl92YWx1ZSwgZGVmYXVsdCkKICAgIHRyeToKICAgICAgICByZXR1cm4gbWF4KG1pbl92YWx1ZSwgaW50KHJhdykpCiAgICBleGNlcHQgVmFsdWVFcnJvcjoKICAgICAgICByZXR1cm4gbWF4KG1pbl92YWx1ZSwgZGVmYXVsdCkKCkNIUk9NQV9IT1NUID0gImxvY2FsaG9zdCIKQ0hST01BX1BPUlQgPSBfZW52X2ludCgiTUNQX0NIUk9NQV9QT1JUIiwgODAwMCwgbWluX3ZhbHVlPTEpCkNPTExFQ1RJT05fQ09ERV9KSU5BID0gImNvZGVfdmVjdG9yc19qaW5hIgpDT0xMRUNUSU9OX0RPQ19CR0UgPSAiZG9jX3ZlY3RvcnNfYmdlIgoKIyBQYXN0YXMgZSBleHRlbnPDtWVzIGlnbm9yYWRhcyBkdXJhbnRlIGEgdmFycmVkdXJhCklHTk9SRURfRElSUyA9IHsKICAgICIuZ2l0IiwgIm5vZGVfbW9kdWxlcyIsICJfX3B5Y2FjaGVfXyIsICIudmVudiIsICJ2ZW52IiwgImVudiIsCiAgICAiZGlzdCIsICJidWlsZCIsICJvdXQiLCAiLm5leHQiLCAiLm51eHQiLCAiLmNhY2hlIiwgImNvdmVyYWdlIiwKICAgICIucHl0ZXN0X2NhY2hlIiwgIi5teXB5X2NhY2hlIiwgIi5ydWZmX2NhY2hlIiwgInRhcmdldCIsICJiaW4iLCAib2JqIiwKICAgICIuaWRlYSIsICIudnNjb2RlIiwgIi5EU19TdG9yZSIsICJ2ZW5kb3IiLCAidG1wIiwgInRlbXAiLCAibG9ncyIsCiAgICAiLnJhZ19kYiIsCn0KCklHTk9SRURfRVhURU5TSU9OUyA9IHsKICAgICMgQmluw6FyaW9zIGUgaW1hZ2VucwogICAgIi5wbmciLCAiLmpwZyIsICIuanBlZyIsICIuZ2lmIiwgIi5zdmciLCAiLmljbyIsICIud2VicCIsICIuYm1wIiwKICAgICIubXA0IiwgIi5tcDMiLCAiLndhdiIsICIub2dnIiwgIi5hdmkiLCAiLm1vdiIsCiAgICAjIFBhY290ZXMgZSBjb21waWxhZG9zCiAgICAiLnppcCIsICIudGFyIiwgIi5neiIsICIucmFyIiwgIi43eiIsICIuamFyIiwgIi53YXIiLCAiLmVhciIsCiAgICAiLnB5YyIsICIucHlvIiwgIi5zbyIsICIuZGxsIiwgIi5leGUiLCAiLmJpbiIsCiAgICAjIExvY2tmaWxlcyBlIGdlcmFkb3MKICAgICIubG9jayIsICIuc3VtIiwKICAgICMgQmFuY28gZGUgZGFkb3MKICAgICIuc3FsaXRlIiwgIi5kYiIsICIuc3FsaXRlMyIsCiAgICAjIEZvbnRlcwogICAgIi50dGYiLCAiLndvZmYiLCAiLndvZmYyIiwgIi5lb3QiLAogICAgIyBQREYvRG9jdW1lbnRvcyBiaW7DoXJpb3MKICAgICIucGRmIiwgIi5kb2N4IiwgIi54bHN4IiwgIi5wcHR4IiwKfQoKQ09ERV9FWFRFTlNJT05TID0gewogICAgIi5weSIsICIuanMiLCAiLnRzIiwgIi50c3giLCAiLmpzeCIsICIuamF2YSIsICIuYyIsICIuaCIsICIuY3BwIiwgIi5ocHAiLAogICAgIi5nbyIsICIucnMiLCAiLnJiIiwgIi5waHAiLCAiLmNzIiwgIi5zd2lmdCIsICIua3QiLCAiLmt0cyIsICIuc2NhbGEiLCAiLnNxbCIsCiAgICAiLnNoIiwgIi5iYXNoIiwgIi56c2giLCAiLnBzMSIsICIueWFtbCIsICIueW1sIiwgIi50b21sIiwgIi5pbmkiLCAiLmNvbmYiLAogICAgIi5qc29uIiwgIi54bWwiLCAiLmh0bWwiLCAiLmNzcyIsICIuc2NzcyIsICIuc2FzcyIsICIudnVlIiwgIi5zdmVsdGUiLCAiLmRhcnQiLAogICAgIi5sdWEiLCAiLnIiLCAiLm0iLCAiLm1tIiwKfQoKRE9DX0VYVEVOU0lPTlMgPSB7CiAgICAiLm1kIiwgIi5tZHgiLCAiLnJzdCIsICIudHh0IiwgIi5hZG9jIiwgIi5vcmciLCAiLnRleCIsICIuY3N2IiwKfQoKIyBUYW1hbmhvIG3DoXhpbW8gZGUgYXJxdWl2byAoZXZpdGEgaW5kZXhhciBhcnF1aXZvcyBlbm9ybWVzIGdlcmFkb3MpCk1BWF9GSUxFX1NJWkVfQllURVMgPSA1MDAgKiAxMDI0ICAjIDUwMCBLQgoKIyBQYXLDom1ldHJvcyBkbyBzcGxpdHRlciBlIGJhdGNoIChwZXJmaWwgbG93LW1lbW9yeSBwb3IgcGFkcsOjbykuCkNIVU5LX1NJWkUgPSBfZW52X2ludCgiTUNQX0NIVU5LX1NJWkUiLCAzMDAwLCBtaW5fdmFsdWU9MjU2KQpDSFVOS19PVkVSTEFQID0gbWluKENIVU5LX1NJWkUgLSAxLCBfZW52X2ludCgiTUNQX0NIVU5LX09WRVJMQVAiLCA0MDAsIG1pbl92YWx1ZT0wKSkKRU1CRURESU5HX0JBVENIX1NJWkUgPSBfZW52X2ludCgiTUNQX0VNQkVERElOR19CQVRDSF9TSVpFIiwgNCwgbWluX3ZhbHVlPTEpCkRFRkFVTFRfUEVSRl9QUk9GSUxFID0gImF1dG90dW5lIgpJTkRFWEVSX0NPTkZJR19QQVRIID0gUGF0aCgKICAgIG9zLmVudmlyb24uZ2V0KCJNQ1BfSU5ERVhFUl9DT05GSUdfRklMRSIsIHN0cihQYXRoLmhvbWUoKSAvICIucmFnX2RiIiAvICJpbmRleGVyX3R1bmluZy5qc29uIikpCikuZXhwYW5kdXNlcigpCklOREVYRVJfQ09ORklHX0ZBTExCQUNLX1BBVEggPSBQYXRoLmhvbWUoKSAvICIuY2FjaGUiIC8gIm15LWN1c3RvbS1yYWctcHl0aG9uIiAvICJpbmRleGVyX3R1bmluZy5qc29uIgoKIyBNb2RlbG8gZGUgZW1iZWRkaW5ncyAocm9kYSBuYSBDUFUpCkpJTkFfVjNfRU1CRURESU5HX01PREVMID0gImppbmFhaS9qaW5hLWVtYmVkZGluZ3MtdjMiCkpJTkFfVjJfRU1CRURESU5HX01PREVMID0gImppbmFhaS9qaW5hLWVtYmVkZGluZ3MtdjItYmFzZS1jb2RlIgpCR0VfRU1CRURESU5HX01PREVMID0gIkJBQUkvYmdlLW0zIgpERUZBVUxUX0VNQkVERElOR19NT0RFTF9DSE9JQ0UgPSAiamluYSIKREVGQVVMVF9KSU5BX1FVQU5USVpBVElPTiA9ICJkeW5hbWljLWludDgiCk1PREVMX0NBQ0hFX0JBU0VfRElSID0gUGF0aCgKICAgIG9zLmVudmlyb24uZ2V0KCJNQ1BfTU9ERUxfRElSIiwgc3RyKFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAibXktY3VzdG9tLXJhZy1weXRob24iIC8gIm1vZGVscyIpKQopLmV4cGFuZHVzZXIoKQpKSU5BX1JFQ09NTUVOREVEX1JBTV9HQl9ERUZBVUxUID0gNjQKSklOQV9SRUNPTU1FTkRFRF9SQU1fR0JfRFlOQU1JQ19JTlQ4ID0gNDgKSklOQV9SRUNPTU1FTkRFRF9TV0FQX0dCID0gMTYKSklOQV9NSU5fQVZBSUxBQkxFX1JBTV9HQl9ISU5UID0gMTIKCgpkZWYgX2Vudl9ib29sKG5hbWU6IHN0ciwgZGVmYXVsdDogYm9vbCA9IEZhbHNlKSAtPiBib29sOgogICAgcmF3ID0gb3MuZW52aXJvbi5nZXQobmFtZSkKICAgIGlmIHJhdyBpcyBOb25lOgogICAgICAgIHJldHVybiBkZWZhdWx0CiAgICByZXR1cm4gcmF3LnN0cmlwKCkubG93ZXIoKSBpbiB7IjEiLCAidHJ1ZSIsICJ5ZXMiLCAib24ifQoKCmRlZiBfY2xhbXAodmFsdWU6IGZsb2F0LCBsb3c6IGZsb2F0LCBoaWdoOiBmbG9hdCkgLT4gZmxvYXQ6CiAgICByZXR1cm4gbWF4KGxvdywgbWluKGhpZ2gsIHZhbHVlKSkKCgpkZWYgX2lzX21lbW9yeV9yZWxhdGVkX2Vycm9yKGV4YzogRXhjZXB0aW9uKSAtPiBib29sOgogICAgaWYgaXNpbnN0YW5jZShleGMsIE1lbW9yeUVycm9yKToKICAgICAgICByZXR1cm4gVHJ1ZQogICAgbXNnID0gc3RyKGV4YykubG93ZXIoKQogICAgbWVtb3J5X21hcmtlcnMgPSAoCiAgICAgICAgIm91dCBvZiBtZW1vcnkiLAogICAgICAgICJvb20iLAogICAgICAgICJjYW5ub3QgYWxsb2NhdGUgbWVtb3J5IiwKICAgICAgICAic3RkOjpiYWRfYWxsb2MiLAogICAgICAgICJiYWQgYWxsb2MiLAogICAgICAgICJpbnN1ZmZpY2llbnQgbWVtb3J5IiwKICAgICkKICAgIHJldHVybiBhbnkobWFya2VyIGluIG1zZyBmb3IgbWFya2VyIGluIG1lbW9yeV9tYXJrZXJzKQoKCmRlZiBfaXNfZGltZW5zaW9uX21pc21hdGNoX2Vycm9yKGV4YzogRXhjZXB0aW9uKSAtPiBib29sOgogICAgbXNnID0gc3RyKGV4YykubG93ZXIoKQogICAgcmV0dXJuICgKICAgICAgICAiZXhwZWN0aW5nIGVtYmVkZGluZyB3aXRoIGRpbWVuc2lvbiIgaW4gbXNnCiAgICAgICAgb3IgKCJlbWJlZGRpbmciIGluIG1zZyBhbmQgImRpbWVuc2lvbiIgaW4gbXNnIGFuZCAiZ290IiBpbiBtc2cpCiAgICApCgoKZGVmIF9mb3JtYXRfZXhjZXB0aW9uKGV4YzogRXhjZXB0aW9uKSAtPiBzdHI6CiAgICBtZXNzYWdlID0gc3RyKGV4Yykuc3RyaXAoKQogICAgaWYgbWVzc2FnZToKICAgICAgICByZXR1cm4gbWVzc2FnZQogICAgcmV0dXJuIHJlcHIoZXhjKQoKCkBkYXRhY2xhc3MoZnJvemVuPVRydWUpCmNsYXNzIEluZGV4VGFyZ2V0OgogICAgbW9kZWxfY2hvaWNlOiBzdHIKICAgIGNvbGxlY3Rpb25fbmFtZTogc3RyCiAgICBsYWJlbDogc3RyCgoKZGVmIF9yZXNvbHZlX21vZGVsX2lkKG1vZGVsX2Nob2ljZTogc3RyKSAtPiBzdHI6CiAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImppbmEiOgogICAgICAgIHJldHVybiBKSU5BX1YzX0VNQkVERElOR19NT0RFTAogICAgaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hLXYyIjoKICAgICAgICByZXR1cm4gSklOQV9WMl9FTUJFRERJTkdfTU9ERUwKICAgIGlmIG1vZGVsX2Nob2ljZSA9PSAiYmdlIjoKICAgICAgICByZXR1cm4gQkdFX0VNQkVERElOR19NT0RFTAogICAgcmFpc2UgVmFsdWVFcnJvcihmIk1vZGVsbyBuw6NvIHN1cG9ydGFkbzoge21vZGVsX2Nob2ljZX0iKQoKCmRlZiBfcmVzb2x2ZV9mYWxsYmFja19tb2RlbF9pZChtb2RlbF9jaG9pY2U6IHN0cikgLT4gc3RyOgogICAgcmV0dXJuIEJHRV9FTUJFRERJTkdfTU9ERUwKCgpkZWYgX2Rlc2NyaWJlX2VtYmVkZGluZ19jaG9pY2UobW9kZWxfY2hvaWNlOiBzdHIpIC0+IHN0cjoKICAgIGlmIG1vZGVsX2Nob2ljZSA9PSAiamluYSI6CiAgICAgICAgcmV0dXJuIGYiamluYSAoe0pJTkFfVjNfRU1CRURESU5HX01PREVMfSkiCiAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImJnZSI6CiAgICAgICAgcmV0dXJuIGYiYmdlICh7QkdFX0VNQkVERElOR19NT0RFTH0pIgogICAgaWYgbW9kZWxfY2hvaWNlID09ICJoeWJyaWQiOgogICAgICAgIHJldHVybiBmImh5YnJpZCAoe0pJTkFfVjJfRU1CRURESU5HX01PREVMfSArIHtCR0VfRU1CRURESU5HX01PREVMfSkiCiAgICByZXR1cm4gbW9kZWxfY2hvaWNlCgoKZGVmIF9yZXNvbHZlX2luZGV4X3RhcmdldHMobW9kZWxfY2hvaWNlOiBzdHIpIC0+IGxpc3RbSW5kZXhUYXJnZXRdOgogICAgaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hIjoKICAgICAgICByZXR1cm4gWwogICAgICAgICAgICBJbmRleFRhcmdldCgKICAgICAgICAgICAgICAgIG1vZGVsX2Nob2ljZT0iamluYSIsCiAgICAgICAgICAgICAgICBjb2xsZWN0aW9uX25hbWU9Q09MTEVDVElPTl9DT0RFX0pJTkEsCiAgICAgICAgICAgICAgICBsYWJlbD0iQ29kZS9KaW5hIiwKICAgICAgICAgICAgKQogICAgICAgIF0KICAgIGlmIG1vZGVsX2Nob2ljZSA9PSAiYmdlIjoKICAgICAgICByZXR1cm4gWwogICAgICAgICAgICBJbmRleFRhcmdldCgKICAgICAgICAgICAgICAgIG1vZGVsX2Nob2ljZT0iYmdlIiwKICAgICAgICAgICAgICAgIGNvbGxlY3Rpb25fbmFtZT1DT0xMRUNUSU9OX0RPQ19CR0UsCiAgICAgICAgICAgICAgICBsYWJlbD0iRG9jL0JHRSIsCiAgICAgICAgICAgICkKICAgICAgICBdCiAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImh5YnJpZCI6CiAgICAgICAgcmV0dXJuIFsKICAgICAgICAgICAgSW5kZXhUYXJnZXQoCiAgICAgICAgICAgICAgICBtb2RlbF9jaG9pY2U9ImppbmEtdjIiLAogICAgICAgICAgICAgICAgY29sbGVjdGlvbl9uYW1lPUNPTExFQ1RJT05fQ09ERV9KSU5BLAogICAgICAgICAgICAgICAgbGFiZWw9IkNvZGUvSmluYSB2MiIsCiAgICAgICAgICAgICksCiAgICAgICAgICAgIEluZGV4VGFyZ2V0KAogICAgICAgICAgICAgICAgbW9kZWxfY2hvaWNlPSJiZ2UiLAogICAgICAgICAgICAgICAgY29sbGVjdGlvbl9uYW1lPUNPTExFQ1RJT05fRE9DX0JHRSwKICAgICAgICAgICAgICAgIGxhYmVsPSJEb2MvQkdFIiwKICAgICAgICAgICAgKSwKICAgICAgICBdCiAgICByYWlzZSBWYWx1ZUVycm9yKGYiTW9kZWxvIG7Do28gc3Vwb3J0YWRvOiB7bW9kZWxfY2hvaWNlfSIpCgoKZGVmIF9jbGFzc2lmeV9maWxlX3RhcmdldHMoZmlsZXBhdGg6IFBhdGgsIG1vZGVsX2Nob2ljZTogc3RyKSAtPiBzZXRbc3RyXToKICAgIGlmIG1vZGVsX2Nob2ljZSAhPSAiaHlicmlkIjoKICAgICAgICByZXR1cm4ge21vZGVsX2Nob2ljZX0KCiAgICBzdWZmaXggPSBmaWxlcGF0aC5zdWZmaXgubG93ZXIoKQogICAgaXNfY29kZSA9IHN1ZmZpeCBpbiBDT0RFX0VYVEVOU0lPTlMKICAgIGlzX2RvYyA9IHN1ZmZpeCBpbiBET0NfRVhURU5TSU9OUwoKICAgIGlmIGlzX2NvZGUgYW5kIG5vdCBpc19kb2M6CiAgICAgICAgcmV0dXJuIHsiamluYS12MiJ9CiAgICBpZiBpc19kb2MgYW5kIG5vdCBpc19jb2RlOgogICAgICAgIHJldHVybiB7ImJnZSJ9CgogICAgIyBFeHRlbnPDo28gZGVzY29uaGVjaWRhL2FtYsOtZ3VhOiBpbmRleGEgbm9zIGRvaXMgcmFtb3MgcGFyYSBtYW50ZXIgcmVjYWxsLgogICAgcmV0dXJuIHsiamluYS12MiIsICJiZ2UifQoKCmRlZiBfbW9kZWxfY2FjaGVfZGlyKGJhc2VfZGlyOiBQYXRoLCBtb2RlbF9pZDogc3RyKSAtPiBQYXRoOgogICAgc2FmZV9uYW1lID0gbW9kZWxfaWQucmVwbGFjZSgiLyIsICJfXyIpLnJlcGxhY2UoIjoiLCAiXyIpCiAgICByZXR1cm4gYmFzZV9kaXIgLyBzYWZlX25hbWUKCgpkZWYgX3BpY2tfd2l0aF9wcm9tcHQoCiAgICAqLAogICAgY3VycmVudF92YWx1ZTogc3RyIHwgTm9uZSwKICAgIGRlZmF1bHRfdmFsdWU6IHN0ciwKICAgIHRpdGxlOiBzdHIsCiAgICBvcHRpb25zOiBsaXN0W3R1cGxlW3N0ciwgc3RyXV0sCikgLT4gc3RyOgogICAgaWYgY3VycmVudF92YWx1ZToKICAgICAgICByZXR1cm4gY3VycmVudF92YWx1ZQogICAgaWYgbm90IHN5cy5zdGRpbi5pc2F0dHkoKToKICAgICAgICByZXR1cm4gZGVmYXVsdF92YWx1ZQoKICAgIHByaW50KGYiXG5bQ09ORklHXSB7dGl0bGV9IikKICAgIGZvciBpbmRleCwgKF8sIGRlc2NyaXB0aW9uKSBpbiBlbnVtZXJhdGUob3B0aW9ucywgc3RhcnQ9MSk6CiAgICAgICAgcHJpbnQoZiIgIHtpbmRleH0pIHtkZXNjcmlwdGlvbn0iKQogICAgcHJpbnQoZiIgIEVudGVyID0gcGFkcsOjbyAoe2RlZmF1bHRfdmFsdWV9KSIpCgogICAgYW5zd2VyID0gaW5wdXQoIj4gRXNjb2xoYTogIikuc3RyaXAoKQogICAgaWYgbm90IGFuc3dlcjoKICAgICAgICByZXR1cm4gZGVmYXVsdF92YWx1ZQogICAgaWYgYW5zd2VyLmlzZGlnaXQoKToKICAgICAgICBpZHggPSBpbnQoYW5zd2VyKSAtIDEKICAgICAgICBpZiAwIDw9IGlkeCA8IGxlbihvcHRpb25zKToKICAgICAgICAgICAgcmV0dXJuIG9wdGlvbnNbaWR4XVswXQogICAgbG93ZXJlZCA9IGFuc3dlci5sb3dlcigpCiAgICB2YWxpZF9rZXlzID0ge2sgZm9yIGssIF8gaW4gb3B0aW9uc30KICAgIGlmIGxvd2VyZWQgaW4gdmFsaWRfa2V5czoKICAgICAgICByZXR1cm4gbG93ZXJlZAogICAgcHJpbnQoZiJbQVZJU09dIE9ww6fDo28gaW52w6FsaWRhICd7YW5zd2VyfScuIFVzYW5kbyBwYWRyw6NvOiB7ZGVmYXVsdF92YWx1ZX0iKQogICAgcmV0dXJuIGRlZmF1bHRfdmFsdWUKCgpkZWYgcmVzb2x2ZV9lbWJlZGRpbmdfY29uZmlnKAogICAgbW9kZWxfY2hvaWNlX2FyZzogc3RyIHwgTm9uZSwKICAgIGppbmFfcXVhbnRpemF0aW9uX2FyZzogc3RyIHwgTm9uZSwKICAgIHBlcnNpc3RlZF9jb25maWc6IGRpY3Rbc3RyLCBvYmplY3RdIHwgTm9uZSA9IE5vbmUsCikgLT4gdHVwbGVbc3RyLCBzdHJdOgogICAgcGVyc2lzdGVkX2NvbmZpZyA9IHBlcnNpc3RlZF9jb25maWcgb3Ige30KICAgIG1vZGVsX2Nob2ljZV9mcm9tX2NvbmZpZyA9IHBlcnNpc3RlZF9jb25maWcuZ2V0KCJlbWJlZGRpbmdfbW9kZWwiKQogICAgbW9kZWxfY2hvaWNlID0gbW9kZWxfY2hvaWNlX2FyZyBvciBvcy5lbnZpcm9uLmdldCgiTUNQX0VNQkVERElOR19NT0RFTCIpCiAgICBpZiBub3QgbW9kZWxfY2hvaWNlIGFuZCBpc2luc3RhbmNlKG1vZGVsX2Nob2ljZV9mcm9tX2NvbmZpZywgc3RyKToKICAgICAgICBtb2RlbF9jaG9pY2UgPSBtb2RlbF9jaG9pY2VfZnJvbV9jb25maWcKICAgIGlmIG1vZGVsX2Nob2ljZToKICAgICAgICBtb2RlbF9jaG9pY2UgPSBtb2RlbF9jaG9pY2Uuc3RyaXAoKS5sb3dlcigpCiAgICBtb2RlbF9jaG9pY2UgPSBfcGlja193aXRoX3Byb21wdCgKICAgICAgICBjdXJyZW50X3ZhbHVlPW1vZGVsX2Nob2ljZSwKICAgICAgICBkZWZhdWx0X3ZhbHVlPURFRkFVTFRfRU1CRURESU5HX01PREVMX0NIT0lDRSwKICAgICAgICB0aXRsZT0iRXNjb2xoYSBkbyBtb2RlbG8gZGUgZW1iZWRkaW5ncyIsCiAgICAgICAgb3B0aW9ucz1bCiAgICAgICAgICAgICgKICAgICAgICAgICAgICAgICJqaW5hIiwKICAgICAgICAgICAgICAgIGYiamluYSAoe0pJTkFfVjNfRU1CRURESU5HX01PREVMfSkgLSBmb2NvIGVtIGPDs2RpZ28uIiwKICAgICAgICAgICAgKSwKICAgICAgICAgICAgKAogICAgICAgICAgICAgICAgImJnZSIsCiAgICAgICAgICAgICAgICBmImJnZSAoe0JHRV9FTUJFRERJTkdfTU9ERUx9KSAtIGNvbnRlw7pkbyBtaXN0by4iLAogICAgICAgICAgICApLAogICAgICAgICAgICAoCiAgICAgICAgICAgICAgICAiaHlicmlkIiwKICAgICAgICAgICAgICAgIGYiaHlicmlkIChKaW5hIHYyIHtKSU5BX1YyX0VNQkVERElOR19NT0RFTH0gKyBCR0UpIC0gZHVhcyBjb2xlw6fDtWVzLiIsCiAgICAgICAgICAgICksCiAgICAgICAgXSwKICAgICkKICAgIGlmIG1vZGVsX2Nob2ljZSBub3QgaW4geyJqaW5hIiwgImJnZSIsICJoeWJyaWQifToKICAgICAgICBwcmludChmIltBVklTT10gTUNQX0VNQkVERElOR19NT0RFTCBpbnbDoWxpZG8gJ3ttb2RlbF9jaG9pY2V9Jy4gVXNhbmRvICd7REVGQVVMVF9FTUJFRERJTkdfTU9ERUxfQ0hPSUNFfScuIikKICAgICAgICBtb2RlbF9jaG9pY2UgPSBERUZBVUxUX0VNQkVERElOR19NT0RFTF9DSE9JQ0UKCiAgICBxdWFudGl6YXRpb25fZnJvbV9jb25maWcgPSBwZXJzaXN0ZWRfY29uZmlnLmdldCgiamluYV9xdWFudGl6YXRpb24iKQogICAgamluYV9xdWFudGl6YXRpb24gPSBqaW5hX3F1YW50aXphdGlvbl9hcmcgb3Igb3MuZW52aXJvbi5nZXQoIk1DUF9KSU5BX1FVQU5USVpBVElPTiIpCiAgICBpZiBub3QgamluYV9xdWFudGl6YXRpb24gYW5kIGlzaW5zdGFuY2UocXVhbnRpemF0aW9uX2Zyb21fY29uZmlnLCBzdHIpOgogICAgICAgIGppbmFfcXVhbnRpemF0aW9uID0gcXVhbnRpemF0aW9uX2Zyb21fY29uZmlnCiAgICBpZiBqaW5hX3F1YW50aXphdGlvbjoKICAgICAgICBqaW5hX3F1YW50aXphdGlvbiA9IGppbmFfcXVhbnRpemF0aW9uLnN0cmlwKCkubG93ZXIoKS5yZXBsYWNlKCJfIiwgIi0iKQoKICAgIGlmIG1vZGVsX2Nob2ljZSA9PSAiamluYSI6CiAgICAgICAgamluYV9xdWFudGl6YXRpb24gPSBfcGlja193aXRoX3Byb21wdCgKICAgICAgICAgICAgY3VycmVudF92YWx1ZT1qaW5hX3F1YW50aXphdGlvbiwKICAgICAgICAgICAgZGVmYXVsdF92YWx1ZT1ERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OLAogICAgICAgICAgICB0aXRsZT0iUXVhbnRpemFjYW8gZG8gSmluYSAoYXBlbmFzIHBhcmEgQ1BVKSIsCiAgICAgICAgICAgIG9wdGlvbnM9WwogICAgICAgICAgICAgICAgKCJkZWZhdWx0IiwgImRlZmF1bHQgKHNlbSBxdWFudGl6YWNhbykgLSBtYWlvciBxdWFsaWRhZGUsIGluZGV4YWNhbyBtYWlzIGxlbnRhLiIpLAogICAgICAgICAgICAgICAgKCJkeW5hbWljLWludDgiLCAiZHluYW1pYy1pbnQ4IC0gaW5kZXhhY2FvIG1haXMgcmFwaWRhIGUgbWVub3IgdXNvIGRlIFJBTSwgY29tIHBlcXVlbmEgcGVyZGEgZGUgcXVhbGlkYWRlLiIpLAogICAgICAgICAgICBdLAogICAgICAgICkKICAgICAgICBpZiBqaW5hX3F1YW50aXphdGlvbiBub3QgaW4geyJkZWZhdWx0IiwgImR5bmFtaWMtaW50OCJ9OgogICAgICAgICAgICBwcmludCgKICAgICAgICAgICAgICAgIGYiW0FWSVNPXSBNQ1BfSklOQV9RVUFOVElaQVRJT04gaW52w6FsaWRvICd7amluYV9xdWFudGl6YXRpb259Jy4gIgogICAgICAgICAgICAgICAgZiJVc2FuZG8gJ3tERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OfScuIgogICAgICAgICAgICApCiAgICAgICAgICAgIGppbmFfcXVhbnRpemF0aW9uID0gREVGQVVMVF9KSU5BX1FVQU5USVpBVElPTgogICAgZWxzZToKICAgICAgICBqaW5hX3F1YW50aXphdGlvbiA9ICJkZWZhdWx0IgoKICAgIHJldHVybiBtb2RlbF9jaG9pY2UsIGppbmFfcXVhbnRpemF0aW9uCgoKZGVmIF9pbmRleGVyX2NvbmZpZ19jYW5kaWRhdGVzKCkgLT4gbGlzdFtQYXRoXToKICAgIGNhbmRpZGF0ZXMgPSBbSU5ERVhFUl9DT05GSUdfUEFUSF0KICAgIGlmIElOREVYRVJfQ09ORklHX0ZBTExCQUNLX1BBVEggbm90IGluIGNhbmRpZGF0ZXM6CiAgICAgICAgY2FuZGlkYXRlcy5hcHBlbmQoSU5ERVhFUl9DT05GSUdfRkFMTEJBQ0tfUEFUSCkKICAgIHJldHVybiBjYW5kaWRhdGVzCgoKZGVmIGxvYWRfaW5kZXhlcl90dW5pbmdfY29uZmlnKGZvcmNlX3JlY29uZmlndXJlOiBib29sKSAtPiBkaWN0W3N0ciwgb2JqZWN0XToKICAgIGlmIGZvcmNlX3JlY29uZmlndXJlOgogICAgICAgIHJldHVybiB7fQogICAgZm9yIGNhbmRpZGF0ZSBpbiBfaW5kZXhlcl9jb25maWdfY2FuZGlkYXRlcygpOgogICAgICAgIHRyeToKICAgICAgICAgICAgaWYgbm90IGNhbmRpZGF0ZS5leGlzdHMoKToKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIGRhdGEgPSBqc29uLmxvYWRzKGNhbmRpZGF0ZS5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04IikpCiAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UoZGF0YSwgZGljdCk6CiAgICAgICAgICAgICAgICByZXR1cm4gZGF0YQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgIGNvbnRpbnVlCiAgICByZXR1cm4ge30KCgpkZWYgc2F2ZV9pbmRleGVyX3R1bmluZ19jb25maWcoY29uZmlnOiBkaWN0W3N0ciwgb2JqZWN0XSkgLT4gTm9uZToKICAgIHBheWxvYWQgPSB7CiAgICAgICAgKipjb25maWcsCiAgICAgICAgInVwZGF0ZWRfYXQiOiBpbnQodGltZSgpKSwKICAgIH0KICAgIHdyaXRlX2Vycm9yczogbGlzdFt0dXBsZVtQYXRoLCBFeGNlcHRpb25dXSA9IFtdCgogICAgZm9yIGNhbmRpZGF0ZSBpbiBfaW5kZXhlcl9jb25maWdfY2FuZGlkYXRlcygpOgogICAgICAgIHRyeToKICAgICAgICAgICAgY2FuZGlkYXRlLnBhcmVudC5ta2RpcihwYXJlbnRzPVRydWUsIGV4aXN0X29rPVRydWUpCiAgICAgICAgICAgIGNhbmRpZGF0ZS53cml0ZV90ZXh0KGpzb24uZHVtcHMocGF5bG9hZCwgZW5zdXJlX2FzY2lpPUZhbHNlLCBpbmRlbnQ9MikgKyAiXG4iLCBlbmNvZGluZz0idXRmLTgiKQogICAgICAgICAgICBpZiBjYW5kaWRhdGUgPT0gSU5ERVhFUl9DT05GSUdfUEFUSDoKICAgICAgICAgICAgICAgIHByaW50KGYiW0NPTkZJR10gQ29uZmlndXJhw6fDo28gcGVyc2lzdGlkYSBlbToge2NhbmRpZGF0ZX0iKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICAgICAgZiJbQ09ORklHXSBDb25maWd1cmHDp8OjbyBwZXJzaXN0aWRhIGVtIGZhbGxiYWNrOiB7Y2FuZGlkYXRlfSAiCiAgICAgICAgICAgICAgICAgICAgZiIoZGVzdGlubyBwcmltw6FyaW8gc2VtIHBlcm1pc3PDo286IHtJTkRFWEVSX0NPTkZJR19QQVRIfSkiCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgIHJldHVybgogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgd3JpdGVfZXJyb3JzLmFwcGVuZCgoY2FuZGlkYXRlLCBlKSkKCiAgICBkZXRhaWxzID0gIiB8ICIuam9pbihmIntwYXRofToge19mb3JtYXRfZXhjZXB0aW9uKGVycil9IiBmb3IgcGF0aCwgZXJyIGluIHdyaXRlX2Vycm9ycykKICAgIHByaW50KGYiW0FWSVNPXSBOw6NvIGZvaSBwb3Nzw612ZWwgcGVyc2lzdGlyIGNvbmZpZ3VyYcOnw6NvOiB7ZGV0YWlsc30iKQoKCmRlZiByZXNvbHZlX3BlcmZfcHJvZmlsZShwZXJmX3Byb2ZpbGVfYXJnOiBzdHIgfCBOb25lLCBwZXJzaXN0ZWRfY29uZmlnOiBkaWN0W3N0ciwgb2JqZWN0XSkgLT4gc3RyOgogICAgcHJvZmlsZV9mcm9tX2NvbmZpZyA9IHBlcnNpc3RlZF9jb25maWcuZ2V0KCJwZXJmX3Byb2ZpbGUiKQogICAgcHJvZmlsZSA9IHBlcmZfcHJvZmlsZV9hcmcgb3Igb3MuZW52aXJvbi5nZXQoIk1DUF9QRVJGX1BST0ZJTEUiKQogICAgaWYgbm90IHByb2ZpbGUgYW5kIGlzaW5zdGFuY2UocHJvZmlsZV9mcm9tX2NvbmZpZywgc3RyKToKICAgICAgICBwcm9maWxlID0gcHJvZmlsZV9mcm9tX2NvbmZpZwogICAgaWYgcHJvZmlsZToKICAgICAgICBwcm9maWxlID0gcHJvZmlsZS5zdHJpcCgpLmxvd2VyKCkKCiAgICBwcm9maWxlID0gX3BpY2tfd2l0aF9wcm9tcHQoCiAgICAgICAgY3VycmVudF92YWx1ZT1wcm9maWxlLAogICAgICAgIGRlZmF1bHRfdmFsdWU9REVGQVVMVF9QRVJGX1BST0ZJTEUsCiAgICAgICAgdGl0bGU9IlBlcmZpbCBkZSBwZXJmb3JtYW5jZSBkYSBpbmRleGHDp8OjbyIsCiAgICAgICAgb3B0aW9ucz1bCiAgICAgICAgICAgICgKICAgICAgICAgICAgICAgICJhdXRvdHVuZSIsCiAgICAgICAgICAgICAgICAiYXV0b3R1bmUgLSBlcXVpbMOtYnJpbyAocmVjb21lbmRhZG8pLiIsCiAgICAgICAgICAgICksCiAgICAgICAgICAgICgKICAgICAgICAgICAgICAgICJtYXgtcGVyZm9ybWFuY2UiLAogICAgICAgICAgICAgICAgIm1heC1wZXJmb3JtYW5jZSAtIG3DoXhpbW8gdGhyb3VnaHB1dCAobWFpcyBSQU0pLiIsCiAgICAgICAgICAgICksCiAgICAgICAgXSwKICAgICkKICAgIGlmIHByb2ZpbGUgbm90IGluIHsiYXV0b3R1bmUiLCAibWF4LXBlcmZvcm1hbmNlIn06CiAgICAgICAgcHJpbnQoZiJbQVZJU09dIFBlcmZpbCBpbnbDoWxpZG8gJ3twcm9maWxlfScuIFVzYW5kbyAne0RFRkFVTFRfUEVSRl9QUk9GSUxFfScuIikKICAgICAgICBwcm9maWxlID0gREVGQVVMVF9QRVJGX1BST0ZJTEUKICAgIHJldHVybiBwcm9maWxlCgoKZGVmIF9wYXJzZV9jb25maWdfaW50KGNvbmZpZzogZGljdFtzdHIsIG9iamVjdF0sIGtleTogc3RyKSAtPiBpbnQgfCBOb25lOgogICAgcmF3ID0gY29uZmlnLmdldChrZXkpCiAgICBpZiBpc2luc3RhbmNlKHJhdywgaW50KToKICAgICAgICByZXR1cm4gcmF3CiAgICBpZiBpc2luc3RhbmNlKHJhdywgc3RyKSBhbmQgcmF3LmlzZGlnaXQoKToKICAgICAgICByZXR1cm4gaW50KHJhdykKICAgIHJldHVybiBOb25lCgoKZGVmIF9yZWFkX21lbWluZm9fZ2liKCkgLT4gdHVwbGVbZmxvYXQgfCBOb25lLCBmbG9hdCB8IE5vbmUsIGZsb2F0IHwgTm9uZV06CiAgICAiIiJSZXRvcm5hIChtZW1fdG90YWwsIG1lbV9hdmFpbGFibGUsIHN3YXBfdG90YWwpIGVtIEdpQiwgcXVhbmRvIGRpc3BvbsOtdmVsLiIiIgogICAgbWVtX3RvdGFsX2tpYjogaW50IHwgTm9uZSA9IE5vbmUKICAgIG1lbV9hdmFpbGFibGVfa2liOiBpbnQgfCBOb25lID0gTm9uZQogICAgc3dhcF90b3RhbF9raWI6IGludCB8IE5vbmUgPSBOb25lCgogICAgdHJ5OgogICAgICAgIGZvciBsaW5lIGluIFBhdGgoIi9wcm9jL21lbWluZm8iKS5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04Iikuc3BsaXRsaW5lcygpOgogICAgICAgICAgICBpZiBsaW5lLnN0YXJ0c3dpdGgoIk1lbVRvdGFsOiIpOgogICAgICAgICAgICAgICAgbWVtX3RvdGFsX2tpYiA9IGludChsaW5lLnNwbGl0KClbMV0pCiAgICAgICAgICAgIGVsaWYgbGluZS5zdGFydHN3aXRoKCJNZW1BdmFpbGFibGU6Iik6CiAgICAgICAgICAgICAgICBtZW1fYXZhaWxhYmxlX2tpYiA9IGludChsaW5lLnNwbGl0KClbMV0pCiAgICAgICAgICAgIGVsaWYgbGluZS5zdGFydHN3aXRoKCJTd2FwVG90YWw6Iik6CiAgICAgICAgICAgICAgICBzd2FwX3RvdGFsX2tpYiA9IGludChsaW5lLnNwbGl0KClbMV0pCiAgICBleGNlcHQgKE9TRXJyb3IsIFZhbHVlRXJyb3IsIEluZGV4RXJyb3IpOgogICAgICAgIHJldHVybiBOb25lLCBOb25lLCBOb25lCgogICAgdG9fZ2liID0gbGFtYmRhIGtpYjogKGtpYiAvICgxMDI0ICogMTAyNCkpIGlmIGtpYiBpcyBub3QgTm9uZSBlbHNlIE5vbmUKICAgIHJldHVybiB0b19naWIobWVtX3RvdGFsX2tpYiksIHRvX2dpYihtZW1fYXZhaWxhYmxlX2tpYiksIHRvX2dpYihzd2FwX3RvdGFsX2tpYikKCgpkZWYgd2Fybl9pZl9qaW5hX21lbW9yeV9yaXNrKG1vZGVsX2Nob2ljZTogc3RyLCBqaW5hX3F1YW50aXphdGlvbjogc3RyKSAtPiBOb25lOgogICAgIiIiTW9zdHJhIGF2aXNvIGRlIHJpc2NvIGRlIE9PTSBwYXJhIG8gbW9kZWxvIEppbmEgZW0gbcOhcXVpbmFzIGNvbSBwb3VjYSBtZW3Ds3JpYS4iIiIKICAgIGlmIG1vZGVsX2Nob2ljZSBub3QgaW4geyJqaW5hIiwgImh5YnJpZCJ9OgogICAgICAgIHJldHVybgoKICAgIG1lbV90b3RhbF9naWIsIG1lbV9hdmFpbGFibGVfZ2liLCBzd2FwX3RvdGFsX2dpYiA9IF9yZWFkX21lbWluZm9fZ2liKCkKICAgIGlmIG1lbV90b3RhbF9naWIgaXMgTm9uZToKICAgICAgICByZXR1cm4KCiAgICByZWNvbW1lbmRlZF9yYW1fZ2liID0gKAogICAgICAgIEpJTkFfUkVDT01NRU5ERURfUkFNX0dCX0RFRkFVTFQKICAgICAgICBpZiBqaW5hX3F1YW50aXphdGlvbiA9PSAiZGVmYXVsdCIKICAgICAgICBlbHNlIEpJTkFfUkVDT01NRU5ERURfUkFNX0dCX0RZTkFNSUNfSU5UOAogICAgKQoKICAgIHJlYXNvbnM6IGxpc3Rbc3RyXSA9IFtdCiAgICBpZiBtZW1fdG90YWxfZ2liIDwgcmVjb21tZW5kZWRfcmFtX2dpYjoKICAgICAgICByZWFzb25zLmFwcGVuZCgKICAgICAgICAgICAgZiJSQU0gdG90YWwgZGV0ZWN0YWRhOiB7bWVtX3RvdGFsX2dpYjouMWZ9IEdpQiAocmVjb21lbmRhZG8gPj0ge3JlY29tbWVuZGVkX3JhbV9naWJ9IEdpQiBwYXJhIEppbmEve2ppbmFfcXVhbnRpemF0aW9ufSkuIgogICAgICAgICkKICAgIGlmIHN3YXBfdG90YWxfZ2liIGlzIG5vdCBOb25lIGFuZCBzd2FwX3RvdGFsX2dpYiA8IEpJTkFfUkVDT01NRU5ERURfU1dBUF9HQjoKICAgICAgICByZWFzb25zLmFwcGVuZCgKICAgICAgICAgICAgZiJTd2FwIGRldGVjdGFkYToge3N3YXBfdG90YWxfZ2liOi4xZn0gR2lCIChyZWNvbWVuZGFkbyA+PSB7SklOQV9SRUNPTU1FTkRFRF9TV0FQX0dCfSBHaUIpLiIKICAgICAgICApCiAgICBpZiBtZW1fYXZhaWxhYmxlX2dpYiBpcyBub3QgTm9uZSBhbmQgbWVtX2F2YWlsYWJsZV9naWIgPCBKSU5BX01JTl9BVkFJTEFCTEVfUkFNX0dCX0hJTlQ6CiAgICAgICAgcmVhc29ucy5hcHBlbmQoCiAgICAgICAgICAgIGYiUkFNIGxpdnJlIGF0dWFsOiB7bWVtX2F2YWlsYWJsZV9naWI6LjFmfSBHaUIgKGJhaXhvIHBhcmEgYSBjYXJnYSBpbmljaWFsIGRvIEppbmEpLiIKICAgICAgICApCgogICAgaWYgbm90IHJlYXNvbnM6CiAgICAgICAgcmV0dXJuCgogICAgcHJpbnQoIltBVklTT10gQWx0byByaXNjbyBkZSBPT00gY29tIEppbmEgbmVzdGEgbcOhcXVpbmEvY2FyZ2EuIikKICAgIGZvciByZWFzb24gaW4gcmVhc29uczoKICAgICAgICBwcmludChmIiAgICAgICAgLSB7cmVhc29ufSIpCiAgICBwcmludCgiICAgICAgICAtIFNlIG9jb3JyZXIgJ0tpbGxlZCcgKGV4aXQgMTM3KSwgdXNlIEJHRTogLS1lbWJlZGRpbmctbW9kZWwgYmdlIikKICAgIHByaW50KCIgICAgICAgIC0gT3Ugcm9kZSBvIEppbmEgZW0gbcOhcXVpbmEgY29tIG1haXMgUkFNL3N3YXAgZSBtZW5vcyBwcm9jZXNzb3MgY29uY29ycmVudGVzLiIpCgoKQGRhdGFjbGFzcyhmcm96ZW49VHJ1ZSkKY2xhc3MgUnVudGltZUluZGV4aW5nUGFyYW1zOgogICAgY2h1bmtfc2l6ZTogaW50CiAgICBjaHVua19vdmVybGFwOiBpbnQKICAgIGVtYmVkZGluZ19iYXRjaF9zaXplOiBpbnQKICAgIHJlYXNvbnM6IGxpc3Rbc3RyXQoKCmRlZiBfcmVzb2x2ZV9tYXhfcGVyZm9ybWFuY2VfcGFyYW1zKAogICAgKiwKICAgIGNodW5rX3NpemVfbG9ja2VkOiBib29sLAogICAgY2h1bmtfb3ZlcmxhcF9sb2NrZWQ6IGJvb2wsCiAgICBiYXRjaF9zaXplX2xvY2tlZDogYm9vbCwKICAgIGNodW5rX3NpemU6IGludCwKICAgIGNodW5rX292ZXJsYXA6IGludCwKICAgIGVtYmVkZGluZ19iYXRjaF9zaXplOiBpbnQsCikgLT4gUnVudGltZUluZGV4aW5nUGFyYW1zOgogICAgbWVtX3RvdGFsX2dpYiwgbWVtX2F2YWlsYWJsZV9naWIsIF8gPSBfcmVhZF9tZW1pbmZvX2dpYigpCiAgICByZWFzb25zID0gWwogICAgICAgICJQZXJmaWwgc2VsZWNpb25hZG86IG1heC1wZXJmb3JtYW5jZS4iLAogICAgICAgICJNb2RvIHBvZGUgZWxldmFyIGNvbnNpZGVyYXZlbG1lbnRlIG8gY29uc3VtbyBkZSBtZW3Ds3JpYSBlIGNhdXNhciBlbmNlcnJhbWVudG8gcG9yIE9PTSAoZXhpdCAxMzcpLiIsCiAgICBdCgogICAgdHVuZWRfY2h1bmtfc2l6ZSA9IGNodW5rX3NpemUKICAgIHR1bmVkX2NodW5rX292ZXJsYXAgPSBjaHVua19vdmVybGFwCiAgICB0dW5lZF9iYXRjaCA9IGVtYmVkZGluZ19iYXRjaF9zaXplCgogICAgaWYgbm90IGNodW5rX3NpemVfbG9ja2VkOgogICAgICAgIGlmIG1lbV90b3RhbF9naWIgaXMgbm90IE5vbmUgYW5kIG1lbV90b3RhbF9naWIgPj0gNjQgYW5kIChtZW1fYXZhaWxhYmxlX2dpYiBvciAwKSA+PSAxNjoKICAgICAgICAgICAgdHVuZWRfY2h1bmtfc2l6ZSA9IDcwMDAKICAgICAgICBlbHNlOgogICAgICAgICAgICB0dW5lZF9jaHVua19zaXplID0gNjAwMAogICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiY2h1bmtfc2l6ZSBhanVzdGFkbyBwYXJhIHt0dW5lZF9jaHVua19zaXplfSBubyBwZXJmaWwgbWF4LXBlcmZvcm1hbmNlLiIpCgogICAgaWYgbm90IGNodW5rX292ZXJsYXBfbG9ja2VkOgogICAgICAgIHR1bmVkX2NodW5rX292ZXJsYXAgPSBtaW4odHVuZWRfY2h1bmtfc2l6ZSAtIDEsIG1heCgzMDAsIGludCh0dW5lZF9jaHVua19zaXplICogMC4xNSkpKQogICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiY2h1bmtfb3ZlcmxhcCBhanVzdGFkbyBwYXJhIHt0dW5lZF9jaHVua19vdmVybGFwfS4iKQoKICAgIGlmIG5vdCBiYXRjaF9zaXplX2xvY2tlZDoKICAgICAgICBpZiBtZW1fdG90YWxfZ2liIGlzIG5vdCBOb25lIGFuZCBtZW1fdG90YWxfZ2liID49IDY0IGFuZCAobWVtX2F2YWlsYWJsZV9naWIgb3IgMCkgPj0gMTY6CiAgICAgICAgICAgIHR1bmVkX2JhdGNoID0gMjQKICAgICAgICBlbGlmIG1lbV90b3RhbF9naWIgaXMgbm90IE5vbmUgYW5kIG1lbV90b3RhbF9naWIgPj0gMzI6CiAgICAgICAgICAgIHR1bmVkX2JhdGNoID0gMTYKICAgICAgICBlbHNlOgogICAgICAgICAgICB0dW5lZF9iYXRjaCA9IDEyCiAgICAgICAgcmVhc29ucy5hcHBlbmQoZiJlbWJlZGRpbmdfYmF0Y2hfc2l6ZSBhanVzdGFkbyBwYXJhIHt0dW5lZF9iYXRjaH0uIikKCiAgICByZXR1cm4gUnVudGltZUluZGV4aW5nUGFyYW1zKAogICAgICAgIGNodW5rX3NpemU9dHVuZWRfY2h1bmtfc2l6ZSwKICAgICAgICBjaHVua19vdmVybGFwPXR1bmVkX2NodW5rX292ZXJsYXAsCiAgICAgICAgZW1iZWRkaW5nX2JhdGNoX3NpemU9bWF4KDEsIHR1bmVkX2JhdGNoKSwKICAgICAgICByZWFzb25zPXJlYXNvbnMsCiAgICApCgoKZGVmIF9yZXNvbHZlX2F1dG90dW5lZF9wYXJhbXMoCiAgICAqLAogICAgbW9kZWw6IFNlbnRlbmNlVHJhbnNmb3JtZXIsCiAgICBjaHVua19zaXplX2xvY2tlZDogYm9vbCwKICAgIGNodW5rX292ZXJsYXBfbG9ja2VkOiBib29sLAogICAgYmF0Y2hfc2l6ZV9sb2NrZWQ6IGJvb2wsCiAgICBjaHVua19zaXplOiBpbnQsCiAgICBjaHVua19vdmVybGFwOiBpbnQsCiAgICBlbWJlZGRpbmdfYmF0Y2hfc2l6ZTogaW50LAopIC0+IFJ1bnRpbWVJbmRleGluZ1BhcmFtczoKICAgIHJlYXNvbnM6IGxpc3Rbc3RyXSA9IFsiUGVyZmlsIHNlbGVjaW9uYWRvOiBhdXRvdHVuZSAoY3VzdG8tYmVuZWbDrWNpbykuIl0KICAgIHZlcmJvc2VfYXV0b3R1bmUgPSBfZW52X2Jvb2woIk1DUF9BVVRPVFVORV9WRVJCT1NFIiwgZGVmYXVsdD1GYWxzZSkKCiAgICB0cnk6CiAgICAgICAgaW1wb3J0IHBzdXRpbCAgIyB0eXBlOiBpZ25vcmUKICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgcmVhc29ucy5hcHBlbmQoInBzdXRpbCBpbmRpc3BvbsOtdmVsOyBtYW50ZW5kbyBwYXLDom1ldHJvcyBhdHVhaXMgc2VtIGJlbmNobWFyay4iKQogICAgICAgIHJldHVybiBSdW50aW1lSW5kZXhpbmdQYXJhbXMoCiAgICAgICAgICAgIGNodW5rX3NpemU9Y2h1bmtfc2l6ZSwKICAgICAgICAgICAgY2h1bmtfb3ZlcmxhcD1jaHVua19vdmVybGFwLAogICAgICAgICAgICBlbWJlZGRpbmdfYmF0Y2hfc2l6ZT1lbWJlZGRpbmdfYmF0Y2hfc2l6ZSwKICAgICAgICAgICAgcmVhc29ucz1yZWFzb25zLAogICAgICAgICkKCiAgICB2bSA9IHBzdXRpbC52aXJ0dWFsX21lbW9yeSgpCiAgICBzd2FwID0gcHN1dGlsLnN3YXBfbWVtb3J5KCkKICAgIG1lbV90b3RhbF9naWIgPSB2bS50b3RhbCAvICgxMDI0KiozKQogICAgbWVtX2F2YWlsYWJsZV9naWIgPSB2bS5hdmFpbGFibGUgLyAoMTAyNCoqMykKICAgIHN3YXBfdG90YWxfZ2liID0gc3dhcC50b3RhbCAvICgxMDI0KiozKQoKICAgIHRhcmdldF9yYW1fcGVyY2VudCA9IF9jbGFtcCgKICAgICAgICBmbG9hdChvcy5lbnZpcm9uLmdldCgiTUNQX0FVVE9UVU5FX1RBUkdFVF9SQU1fUEVSQ0VOVCIsICI2OCIpKSwKICAgICAgICA2MC4wLAogICAgICAgIDc1LjAsCiAgICApCiAgICBpZiBtZW1fYXZhaWxhYmxlX2dpYiA8IDYgb3Igc3dhcF90b3RhbF9naWIgPCA0OgogICAgICAgIHRhcmdldF9yYW1fcGVyY2VudCA9IG1pbih0YXJnZXRfcmFtX3BlcmNlbnQsIDYzLjApCiAgICByZWFzb25zLmFwcGVuZCgKICAgICAgICBmIk1lbcOzcmlhIGRldGVjdGFkYTogdG90YWw9e21lbV90b3RhbF9naWI6LjFmfSBHaUIsIGxpdnJlPXttZW1fYXZhaWxhYmxlX2dpYjouMWZ9IEdpQiwgIgogICAgICAgIGYic3dhcD17c3dhcF90b3RhbF9naWI6LjFmfSBHaUIsIGFsdm89e3RhcmdldF9yYW1fcGVyY2VudDouMWZ9JS4iCiAgICApCgogICAgdHVuZWRfY2h1bmtfc2l6ZSA9IGNodW5rX3NpemUKICAgIHR1bmVkX2NodW5rX292ZXJsYXAgPSBjaHVua19vdmVybGFwCiAgICB0dW5lZF9iYXRjaCA9IGVtYmVkZGluZ19iYXRjaF9zaXplCgogICAgaWYgbm90IGNodW5rX3NpemVfbG9ja2VkOgogICAgICAgIGlmIG1lbV90b3RhbF9naWIgPCA4IG9yIG1lbV9hdmFpbGFibGVfZ2liIDwgMzoKICAgICAgICAgICAgdHVuZWRfY2h1bmtfc2l6ZSA9IDE4MDAKICAgICAgICBlbGlmIG1lbV90b3RhbF9naWIgPCAxNiBvciBtZW1fYXZhaWxhYmxlX2dpYiA8IDY6CiAgICAgICAgICAgIHR1bmVkX2NodW5rX3NpemUgPSAyNDAwCiAgICAgICAgZWxpZiBtZW1fdG90YWxfZ2liIDwgMzIgb3IgbWVtX2F2YWlsYWJsZV9naWIgPCAxMjoKICAgICAgICAgICAgdHVuZWRfY2h1bmtfc2l6ZSA9IDMyMDAKICAgICAgICBlbHNlOgogICAgICAgICAgICB0dW5lZF9jaHVua19zaXplID0gNDIwMAogICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiY2h1bmtfc2l6ZSBhdXRvdHVuYWRvIHBhcmEge3R1bmVkX2NodW5rX3NpemV9LiIpCgogICAgaWYgbm90IGNodW5rX292ZXJsYXBfbG9ja2VkOgogICAgICAgIHR1bmVkX2NodW5rX292ZXJsYXAgPSBtaW4odHVuZWRfY2h1bmtfc2l6ZSAtIDEsIG1heCgxMjAsIGludCh0dW5lZF9jaHVua19zaXplICogMC4xNSkpKQogICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiY2h1bmtfb3ZlcmxhcCBhdXRvdHVuYWRvIHBhcmEge3R1bmVkX2NodW5rX292ZXJsYXB9LiIpCgogICAgaWYgbm90IGJhdGNoX3NpemVfbG9ja2VkOgogICAgICAgIG1heF9jYW5kaWRhdGUgPSAxNgogICAgICAgIGlmIG1lbV90b3RhbF9naWIgPCA4IG9yIG1lbV9hdmFpbGFibGVfZ2liIDwgMyBvciBzd2FwX3RvdGFsX2dpYiA8IDI6CiAgICAgICAgICAgIG1heF9jYW5kaWRhdGUgPSAyCiAgICAgICAgZWxpZiBtZW1fdG90YWxfZ2liIDwgMTYgb3IgbWVtX2F2YWlsYWJsZV9naWIgPCA2OgogICAgICAgICAgICBtYXhfY2FuZGlkYXRlID0gNAogICAgICAgIGVsaWYgbWVtX3RvdGFsX2dpYiA8IDMyIG9yIG1lbV9hdmFpbGFibGVfZ2liIDwgMTA6CiAgICAgICAgICAgIG1heF9jYW5kaWRhdGUgPSA4CgogICAgICAgIGNhbmRpZGF0ZXMgPSBbMiwgNCwgNiwgOCwgMTIsIDE2XQogICAgICAgIGNhbmRpZGF0ZXMgPSBbYyBmb3IgYyBpbiBjYW5kaWRhdGVzIGlmIGMgPD0gbWF4X2NhbmRpZGF0ZV0KICAgICAgICBpZiBub3QgY2FuZGlkYXRlczoKICAgICAgICAgICAgY2FuZGlkYXRlcyA9IFsyXQoKICAgICAgICBwcm9jZXNzID0gcHN1dGlsLlByb2Nlc3MoKQogICAgICAgIHNhbXBsZV9zaXplID0gbWluKG1heCg1MTIsIHR1bmVkX2NodW5rX3NpemUpLCAzMDAwKQogICAgICAgIHNhbXBsZV90ZXh0ID0gKCIjIGF1dG90dW5lLXNhbXBsZVxuIiArICgieCIgKiBzYW1wbGVfc2l6ZSkpCgogICAgICAgIGJlc3RfYmF0Y2ggPSBjYW5kaWRhdGVzWzBdCiAgICAgICAgYmVzdF9zY29yZSA9IC0xLjAKICAgICAgICBiZXN0X21lbW9yeV9wY3QgPSAxMDAuMAogICAgICAgIHNlbGVjdGVkX2JlbmNobWFya19saW5lOiBzdHIgfCBOb25lID0gTm9uZQogICAgICAgIGJlbmNobWFya19saW5lczogbGlzdFtzdHJdID0gW10KCiAgICAgICAgIyBXYXJtdXAgY3VydG8gcGFyYSBlc3RhYmlsaXphciBjYWNoZSBpbnRlcm5vLgogICAgICAgIHRyeToKICAgICAgICAgICAgXyA9IG1vZGVsLmVuY29kZShbc2FtcGxlX3RleHRdLCBzaG93X3Byb2dyZXNzX2Jhcj1GYWxzZSwgYmF0Y2hfc2l6ZT0xKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgIHBhc3MKCiAgICAgICAgZm9yIGNhbmRpZGF0ZSBpbiBjYW5kaWRhdGVzOgogICAgICAgICAgICBkb2NzID0gW3NhbXBsZV90ZXh0XSAqIGNhbmRpZGF0ZQogICAgICAgICAgICBnYy5jb2xsZWN0KCkKICAgICAgICAgICAgYmVmb3JlX3ZtID0gcHN1dGlsLnZpcnR1YWxfbWVtb3J5KCkucGVyY2VudAogICAgICAgICAgICBiZWZvcmVfcnNzID0gcHJvY2Vzcy5tZW1vcnlfaW5mbygpLnJzcyAvICgxMDI0KioyKQogICAgICAgICAgICBzdGFydGVkID0gcGVyZl9jb3VudGVyKCkKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgZW1iZWRkaW5ncyA9IG1vZGVsLmVuY29kZSgKICAgICAgICAgICAgICAgICAgICBkb2NzLAogICAgICAgICAgICAgICAgICAgIHNob3dfcHJvZ3Jlc3NfYmFyPUZhbHNlLAogICAgICAgICAgICAgICAgICAgIGJhdGNoX3NpemU9Y2FuZGlkYXRlLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBiZW5jaG1hcmtfbGluZXMuYXBwZW5kKGYiYmF0Y2g9e2NhbmRpZGF0ZX06IGVycm8gKHtlfSkiKQogICAgICAgICAgICAgICAgY29udGludWUKCiAgICAgICAgICAgIGVsYXBzZWQgPSBtYXgocGVyZl9jb3VudGVyKCkgLSBzdGFydGVkLCAxZS02KQogICAgICAgICAgICBhZnRlcl92bSA9IHBzdXRpbC52aXJ0dWFsX21lbW9yeSgpLnBlcmNlbnQKICAgICAgICAgICAgYWZ0ZXJfcnNzID0gcHJvY2Vzcy5tZW1vcnlfaW5mbygpLnJzcyAvICgxMDI0KioyKQogICAgICAgICAgICBkZWwgZW1iZWRkaW5ncwogICAgICAgICAgICBnYy5jb2xsZWN0KCkKCiAgICAgICAgICAgIHRocm91Z2hwdXQgPSBjYW5kaWRhdGUgLyBlbGFwc2VkCiAgICAgICAgICAgIHNhZmUgPSBhZnRlcl92bSA8PSAodGFyZ2V0X3JhbV9wZXJjZW50ICsgMy4wKQogICAgICAgICAgICBiZW5jaG1hcmtfbGluZXMuYXBwZW5kKAogICAgICAgICAgICAgICAgZiJiYXRjaD17Y2FuZGlkYXRlfToge3Rocm91Z2hwdXQ6LjJmfSBpdGVucy9zLCB2bT17YWZ0ZXJfdm06LjFmfSUsIHJzc19kZWx0YT17YWZ0ZXJfcnNzIC0gYmVmb3JlX3JzczorLjFmfSBNaUIiCiAgICAgICAgICAgICkKCiAgICAgICAgICAgIGlmIHNhZmUgYW5kIHRocm91Z2hwdXQgPiBiZXN0X3Njb3JlOgogICAgICAgICAgICAgICAgYmVzdF9zY29yZSA9IHRocm91Z2hwdXQKICAgICAgICAgICAgICAgIGJlc3RfYmF0Y2ggPSBjYW5kaWRhdGUKICAgICAgICAgICAgICAgIGJlc3RfbWVtb3J5X3BjdCA9IGFmdGVyX3ZtCiAgICAgICAgICAgICAgICBzZWxlY3RlZF9iZW5jaG1hcmtfbGluZSA9IGJlbmNobWFya19saW5lc1stMV0KICAgICAgICAgICAgZWxpZiBiZXN0X3Njb3JlIDwgMCBhbmQgYWZ0ZXJfdm0gPCBiZXN0X21lbW9yeV9wY3Q6CiAgICAgICAgICAgICAgICAjIFNlIG5lbmh1bSBjYW5kaWRhdG8gZmljb3UgInNhZmUiLCBlc2NvbGhlIG8gbWVub3MgYWdyZXNzaXZvIGVtIG1lbcOzcmlhLgogICAgICAgICAgICAgICAgYmVzdF9iYXRjaCA9IGNhbmRpZGF0ZQogICAgICAgICAgICAgICAgYmVzdF9tZW1vcnlfcGN0ID0gYWZ0ZXJfdm0KICAgICAgICAgICAgICAgIHNlbGVjdGVkX2JlbmNobWFya19saW5lID0gYmVuY2htYXJrX2xpbmVzWy0xXQoKICAgICAgICAgICAgIyBTZSBqw6EgcGFzc291IG11aXRvIGRvIGxpbWl0ZSwgZXZpdGEgdGVudGFyIGJhdGNoZXMgbWFpb3Jlcy4KICAgICAgICAgICAgaWYgYWZ0ZXJfdm0gPiB0YXJnZXRfcmFtX3BlcmNlbnQgKyA4LjA6CiAgICAgICAgICAgICAgICBicmVhawoKICAgICAgICAgICAgIyBFdml0YSBlc2NvbGhlciBjYW5kaWRhdG8gcXVlIGrDoSBjb21lw6dvdSBhY2ltYSBkbyBsaW1pdGUuCiAgICAgICAgICAgIGlmIGJlZm9yZV92bSA+IHRhcmdldF9yYW1fcGVyY2VudCArIDUuMDoKICAgICAgICAgICAgICAgIGJyZWFrCgogICAgICAgIHR1bmVkX2JhdGNoID0gbWF4KDEsIGJlc3RfYmF0Y2gpCiAgICAgICAgaWYgdmVyYm9zZV9hdXRvdHVuZToKICAgICAgICAgICAgcmVhc29ucy5leHRlbmQoYmVuY2htYXJrX2xpbmVzKQogICAgICAgIGVsaWYgc2VsZWN0ZWRfYmVuY2htYXJrX2xpbmU6CiAgICAgICAgICAgIHJlYXNvbnMuYXBwZW5kKGYiTWljcm8tYmVuY2htYXJrOiB7c2VsZWN0ZWRfYmVuY2htYXJrX2xpbmV9IikKICAgICAgICByZWFzb25zLmFwcGVuZCgKICAgICAgICAgICAgZiJlbWJlZGRpbmdfYmF0Y2hfc2l6ZSBhdXRvdHVuYWRvIHBhcmEge3R1bmVkX2JhdGNofSAoYWx2byBkZSBtZW3Ds3JpYToge3RhcmdldF9yYW1fcGVyY2VudDouMWZ9JSkuIgogICAgICAgICkKCiAgICByZXR1cm4gUnVudGltZUluZGV4aW5nUGFyYW1zKAogICAgICAgIGNodW5rX3NpemU9dHVuZWRfY2h1bmtfc2l6ZSwKICAgICAgICBjaHVua19vdmVybGFwPXR1bmVkX2NodW5rX292ZXJsYXAsCiAgICAgICAgZW1iZWRkaW5nX2JhdGNoX3NpemU9bWF4KDEsIHR1bmVkX2JhdGNoKSwKICAgICAgICByZWFzb25zPXJlYXNvbnMsCiAgICApCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBGdW7Dp8O1ZXMgYXV4aWxpYXJlcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKZGVmIGdldF90ZXh0X3NwbGl0dGVyKGNodW5rX3NpemU6IGludCwgY2h1bmtfb3ZlcmxhcDogaW50KSAtPiBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXI6CiAgICAiIiJSZXRvcm5hIG8gc3BsaXR0ZXIgY29tcGFydGlsaGFkbyBjb20gYXMgY29uZmlndXJhw6fDtWVzIHBhZHLDo28gZG8gcHJvamV0by4iIiIKICAgIHJldHVybiBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIoCiAgICAgICAgY2h1bmtfc2l6ZT1jaHVua19zaXplLAogICAgICAgIGNodW5rX292ZXJsYXA9Y2h1bmtfb3ZlcmxhcCwKICAgICAgICBsZW5ndGhfZnVuY3Rpb249bGVuLAogICAgICAgIHNlcGFyYXRvcnM9WyJcblxuIiwgIlxuIiwgIiAiLCAiIl0sCiAgICApCgoKZGVmIGxvYWRfZW1iZWRkaW5nX21vZGVsKG1vZGVsX2Nob2ljZTogc3RyLCBqaW5hX3F1YW50aXphdGlvbjogc3RyKSAtPiBTZW50ZW5jZVRyYW5zZm9ybWVyOgogICAgIiIiQ2FycmVnYSBvIG1vZGVsbyBkZSBlbWJlZGRpbmdzIGZvcsOnYW5kbyB1c28gZGUgQ1BVLiIiIgogICAgZW1iZWRkaW5nX21vZGVsX2lkID0gX3Jlc29sdmVfbW9kZWxfaWQobW9kZWxfY2hvaWNlKQogICAgZmFsbGJhY2tfbW9kZWxfaWQgPSBfcmVzb2x2ZV9mYWxsYmFja19tb2RlbF9pZChtb2RlbF9jaG9pY2UpCgogICAgbW9kZWxfYmFzZV9kaXIgPSBNT0RFTF9DQUNIRV9CQVNFX0RJUgogICAgbW9kZWxfYmFzZV9kaXIubWtkaXIocGFyZW50cz1UcnVlLCBleGlzdF9vaz1UcnVlKQogICAgcHJlZmVycmVkX21vZGVsX2NhY2hlX2RpciA9IF9tb2RlbF9jYWNoZV9kaXIobW9kZWxfYmFzZV9kaXIsIGVtYmVkZGluZ19tb2RlbF9pZCkKCiAgICBwcmludChmIlsrXSBCYWl4YW5kbyBtb2RlbG8gcHJlZmVyaWRvOiB7ZW1iZWRkaW5nX21vZGVsX2lkfSIpCiAgICBwcmludChmIlsrXSBEaXJldMOzcmlvIGRlIGRvd25sb2FkL2NhY2hlIGRvIG1vZGVsbzoge3ByZWZlcnJlZF9tb2RlbF9jYWNoZV9kaXJ9IikKICAgIHNlbGVjdGlvbiA9IGRvd25sb2FkX21vZGVsX3dpdGhfZmFsbGJhY2soCiAgICAgICAgcHJlZmVycmVkX21vZGVsX2lkPWVtYmVkZGluZ19tb2RlbF9pZCwKICAgICAgICBmYWxsYmFja19tb2RlbF9pZD1mYWxsYmFja19tb2RlbF9pZCwKICAgICAgICBsb2NhbF9kaXI9bW9kZWxfYmFzZV9kaXIsCiAgICApCiAgICBzZWxlY3RlZF9tb2RlbF9kaXIgPSBzZWxlY3Rpb24ubG9jYWxfZGlyCiAgICBwcmludCgKICAgICAgICBmIlsrXSBNb2RlbG8gc2VsZWNpb25hZG86IHtzZWxlY3Rpb24ubW9kZWxfaWR9ICIKICAgICAgICBmIihwcm92aWRlcj17c2VsZWN0aW9uLnByb3ZpZGVyfSwgcGF0aD17c2VsZWN0ZWRfbW9kZWxfZGlyfSkiCiAgICApCgogICAgZGVmIF9jbGVhcl9oZl9keW5hbWljX21vZHVsZXNfY2FjaGUoKSAtPiBOb25lOgogICAgICAgIGNhY2hlX2RpciA9IFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAiaHVnZ2luZ2ZhY2UiIC8gIm1vZHVsZXMiIC8gInRyYW5zZm9ybWVyc19tb2R1bGVzIgogICAgICAgIGlmIGNhY2hlX2Rpci5leGlzdHMoKToKICAgICAgICAgICAgcHJpbnQoZiJbIV0gTGltcGFuZG8gY2FjaGUgZGUgbcOzZHVsb3MgZGluw6JtaWNvcyBkbyBIdWdnaW5nIEZhY2U6IHtjYWNoZV9kaXJ9IikKICAgICAgICAgICAgc2h1dGlsLnJtdHJlZShjYWNoZV9kaXIsIGlnbm9yZV9lcnJvcnM9VHJ1ZSkKCiAgICBkZWYgX2xvYWRfZnJvbV9sb2NhbF9kaXIobW9kZWxfaWQ6IHN0cikgLT4gU2VudGVuY2VUcmFuc2Zvcm1lcjoKICAgICAgICAjIE8gbW9kZWxvIGRhIEppbmEgZGVwZW5kZSBkZSBjw7NkaWdvIHJlbW90bzsgZmFsbGJhY2sgbm9ybWFsbWVudGUgbsOjby4KICAgICAgICB0cnVzdF9yZW1vdGVfY29kZSA9IG1vZGVsX2lkLnN0YXJ0c3dpdGgoImppbmFhaS8iKQogICAgICAgIHRva2VuaXplcl9rd2FyZ3MgPSB7ImZpeF9taXN0cmFsX3JlZ2V4IjogVHJ1ZX0KCiAgICAgICAgZGVmIF9pbnN0YW50aWF0ZV9tb2RlbCgpIC0+IFNlbnRlbmNlVHJhbnNmb3JtZXI6CiAgICAgICAgICAgIHJldHVybiBTZW50ZW5jZVRyYW5zZm9ybWVyKAogICAgICAgICAgICAgICAgc3RyKHNlbGVjdGVkX21vZGVsX2RpciksCiAgICAgICAgICAgICAgICBkZXZpY2U9ImNwdSIsCiAgICAgICAgICAgICAgICB0cnVzdF9yZW1vdGVfY29kZT10cnVzdF9yZW1vdGVfY29kZSwKICAgICAgICAgICAgICAgIHRva2VuaXplcl9rd2FyZ3M9dG9rZW5pemVyX2t3YXJncywKICAgICAgICAgICAgKQoKICAgICAgICBkZWYgX2xvYWRfd2l0aF9taXN0cmFsX3JlZ2V4X3BhdGNoKCkgLT4gU2VudGVuY2VUcmFuc2Zvcm1lcjoKICAgICAgICAgICAgIyBPIGPDs2RpZ28gcmVtb3RvIGRhIEppbmEgaW5zdGFuY2lhIHVtIHRva2VuaXplciBpbnRlcm5vIHNlbSByZXBhc3NhciB0b2tlbml6ZXJfa3dhcmdzLgogICAgICAgICAgICBpZiBub3QgdHJ1c3RfcmVtb3RlX2NvZGU6CiAgICAgICAgICAgICAgICByZXR1cm4gX2luc3RhbnRpYXRlX21vZGVsKCkKCiAgICAgICAgICAgIGZyb20gdHJhbnNmb3JtZXJzIGltcG9ydCBBdXRvTW9kZWwsIEF1dG9Ub2tlbml6ZXIKICAgICAgICAgICAgZnJvbSB0cmFuc2Zvcm1lcnMubW9kZWxpbmdfdXRpbHMgaW1wb3J0IFByZVRyYWluZWRNb2RlbAoKICAgICAgICAgICAgb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkID0gQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgb3JpZ2luYWxfbW9kZWxfZnJvbV9wcmV0cmFpbmVkID0gQXV0b01vZGVsLmZyb21fcHJldHJhaW5lZAogICAgICAgICAgICBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fcHJldHJhaW5lZCA9IFByZVRyYWluZWRNb2RlbC5mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZyA9IFByZVRyYWluZWRNb2RlbC5fZnJvbV9jb25maWcKICAgICAgICAgICAgbW9kZWxfcmVmcyA9IHtzdHIoc2VsZWN0ZWRfbW9kZWxfZGlyKSwgc3RyKHNlbGVjdGVkX21vZGVsX2Rpci5yZXNvbHZlKCkpfQoKICAgICAgICAgICAgZGVmIF9wYXRjaGVkX2Zyb21fcHJldHJhaW5lZCgqYXJncywgKiprd2FyZ3MpOgogICAgICAgICAgICAgICAgbW9kZWxfcmVmID0gYXJnc1swXSBpZiBhcmdzIGVsc2Uga3dhcmdzLmdldCgicHJldHJhaW5lZF9tb2RlbF9uYW1lX29yX3BhdGgiKQogICAgICAgICAgICAgICAgaWYgbW9kZWxfcmVmIGlzIG5vdCBOb25lIGFuZCBzdHIobW9kZWxfcmVmKSBpbiBtb2RlbF9yZWZzOgogICAgICAgICAgICAgICAgICAgIGt3YXJncy5zZXRkZWZhdWx0KCJmaXhfbWlzdHJhbF9yZWdleCIsIFRydWUpCiAgICAgICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkKCphcmdzLCAqKmt3YXJncykKCiAgICAgICAgICAgIGRlZiBfcGF0Y2hlZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQoKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICAgICAgICAgIG1vZGVsX3JlZiA9IGFyZ3NbMF0gaWYgYXJncyBlbHNlIGt3YXJncy5nZXQoInByZXRyYWluZWRfbW9kZWxfbmFtZV9vcl9wYXRoIikKICAgICAgICAgICAgICAgIGlmIG1vZGVsX3JlZiBpcyBub3QgTm9uZSBhbmQgc3RyKG1vZGVsX3JlZikgaW4gbW9kZWxfcmVmcyBhbmQgInRvcmNoX2R0eXBlIiBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICAgICAga3dhcmdzID0gZGljdChrd2FyZ3MpCiAgICAgICAgICAgICAgICAgICAgaWYgImR0eXBlIiBub3QgaW4ga3dhcmdzOgogICAgICAgICAgICAgICAgICAgICAgICBrd2FyZ3NbImR0eXBlIl0gPSBrd2FyZ3NbInRvcmNoX2R0eXBlIl0KICAgICAgICAgICAgICAgICAgICBrd2FyZ3MucG9wKCJ0b3JjaF9kdHlwZSIsIE5vbmUpCiAgICAgICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxfbW9kZWxfZnJvbV9wcmV0cmFpbmVkKCphcmdzLCAqKmt3YXJncykKCiAgICAgICAgICAgIG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkX2ZuID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQuX19mdW5jX18KCiAgICAgICAgICAgIEBjbGFzc21ldGhvZAogICAgICAgICAgICBkZWYgX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQoY2xzLCAqYXJncywgKiprd2FyZ3MpOgogICAgICAgICAgICAgICAgaWYgInRvcmNoX2R0eXBlIiBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICAgICAga3dhcmdzID0gZGljdChrd2FyZ3MpCiAgICAgICAgICAgICAgICAgICAgaWYgImR0eXBlIiBub3QgaW4ga3dhcmdzOgogICAgICAgICAgICAgICAgICAgICAgICBrd2FyZ3NbImR0eXBlIl0gPSBrd2FyZ3NbInRvcmNoX2R0eXBlIl0KICAgICAgICAgICAgICAgICAgICBrd2FyZ3MucG9wKCJ0b3JjaF9kdHlwZSIsIE5vbmUpCiAgICAgICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWRfZm4oY2xzLCAqYXJncywgKiprd2FyZ3MpCgogICAgICAgICAgICBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnX2ZuID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZy5fX2Z1bmNfXwoKICAgICAgICAgICAgQGNsYXNzbWV0aG9kCiAgICAgICAgICAgIGRlZiBfcGF0Y2hlZF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnKGNscywgKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICAgICAgICAgIGlmICJ0b3JjaF9kdHlwZSIgaW4ga3dhcmdzOgogICAgICAgICAgICAgICAgICAgIGt3YXJncyA9IGRpY3Qoa3dhcmdzKQogICAgICAgICAgICAgICAgICAgIGlmICJkdHlwZSIgbm90IGluIGt3YXJnczoKICAgICAgICAgICAgICAgICAgICAgICAga3dhcmdzWyJkdHlwZSJdID0ga3dhcmdzWyJ0b3JjaF9kdHlwZSJdCiAgICAgICAgICAgICAgICAgICAga3dhcmdzLnBvcCgidG9yY2hfZHR5cGUiLCBOb25lKQogICAgICAgICAgICAgICAgcmV0dXJuIG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9jb25maWdfZm4oY2xzLCAqYXJncywgKiprd2FyZ3MpCgogICAgICAgICAgICBBdXRvVG9rZW5pemVyLmZyb21fcHJldHJhaW5lZCA9IF9wYXRjaGVkX2Zyb21fcHJldHJhaW5lZAogICAgICAgICAgICBBdXRvTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gX3BhdGNoZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgICAgIFByZVRyYWluZWRNb2RlbC5mcm9tX3ByZXRyYWluZWQgPSBfcGF0Y2hlZF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fcHJldHJhaW5lZAogICAgICAgICAgICBQcmVUcmFpbmVkTW9kZWwuX2Zyb21fY29uZmlnID0gX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZwogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICByZXR1cm4gX2luc3RhbnRpYXRlX21vZGVsKCkKICAgICAgICAgICAgZmluYWxseToKICAgICAgICAgICAgICAgIEF1dG9Ub2tlbml6ZXIuZnJvbV9wcmV0cmFpbmVkID0gb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgICAgICAgICBBdXRvTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gb3JpZ2luYWxfbW9kZWxfZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgICAgICAgICBQcmVUcmFpbmVkTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgICAgIFByZVRyYWluZWRNb2RlbC5fZnJvbV9jb25maWcgPSBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnCgogICAgICAgIHByaW50KGYiWytdIENhcnJlZ2FuZG8gbW9kZWxvIGRlIGVtYmVkZGluZ3MgYSBwYXJ0aXIgZGU6IHtzZWxlY3RlZF9tb2RlbF9kaXJ9IChDUFUpLi4uIikKICAgICAgICB0cnk6CiAgICAgICAgICAgIHJldHVybiBfbG9hZF93aXRoX21pc3RyYWxfcmVnZXhfcGF0Y2goKQogICAgICAgIGV4Y2VwdCBGaWxlTm90Rm91bmRFcnJvciBhcyBlOgogICAgICAgICAgICAjIENvcnJpZ2UgY29ycnVww6fDo28vaW5jb21wbGV0dWRlIG5vIGNhY2hlIGRpbsOibWljbyBkbyB0cmFuc2Zvcm1lcnMuCiAgICAgICAgICAgIGlmIHRydXN0X3JlbW90ZV9jb2RlIGFuZCAidHJhbnNmb3JtZXJzX21vZHVsZXMiIGluIHN0cihlKToKICAgICAgICAgICAgICAgIHByaW50KGYiWyFdIENhY2hlIGRpbsOibWljbyBpbmNvbnNpc3RlbnRlIGRldGVjdGFkbzoge2V9IikKICAgICAgICAgICAgICAgIF9jbGVhcl9oZl9keW5hbWljX21vZHVsZXNfY2FjaGUoKQogICAgICAgICAgICAgICAgcmV0dXJuIF9sb2FkX3dpdGhfbWlzdHJhbF9yZWdleF9wYXRjaCgpCiAgICAgICAgICAgIHJhaXNlCgogICAgZGVmIF9hcHBseV9qaW5hX3F1YW50aXphdGlvbl9pZl9uZWVkZWQobW9kZWw6IFNlbnRlbmNlVHJhbnNmb3JtZXIsIG1vZGVsX2lkOiBzdHIpIC0+IFNlbnRlbmNlVHJhbnNmb3JtZXI6CiAgICAgICAgaWYgbW9kZWxfaWQgIT0gSklOQV9WM19FTUJFRERJTkdfTU9ERUwgb3IgamluYV9xdWFudGl6YXRpb24gPT0gImRlZmF1bHQiOgogICAgICAgICAgICByZXR1cm4gbW9kZWwKICAgICAgICB0cnk6CiAgICAgICAgICAgIGltcG9ydCB0b3JjaAogICAgICAgICAgICBpbXBvcnQgd2FybmluZ3MKCiAgICAgICAgICAgIHF1YW50aXplZF9sYXllcnMgPSAwCiAgICAgICAgICAgIGZvciBtb2R1bGUgaW4gbW9kZWwubW9kdWxlcygpOgogICAgICAgICAgICAgICAgaWYgdHlwZShtb2R1bGUpLl9fbmFtZV9fICE9ICJQYXJhbWV0cml6ZWRMaW5lYXIiOgogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCgogICAgICAgICAgICAgICAgZmxvYXRfbGluZWFyID0gdG9yY2gubm4uTGluZWFyKAogICAgICAgICAgICAgICAgICAgIG1vZHVsZS5pbl9mZWF0dXJlcywKICAgICAgICAgICAgICAgICAgICBtb2R1bGUub3V0X2ZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgIGJpYXM9bW9kdWxlLmJpYXMgaXMgbm90IE5vbmUsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICB3aXRoIHRvcmNoLm5vX2dyYWQoKToKICAgICAgICAgICAgICAgICAgICBmbG9hdF9saW5lYXIud2VpZ2h0LmNvcHlfKG1vZHVsZS53ZWlnaHQuZGV0YWNoKCkudG8odG9yY2guZmxvYXQzMikpCiAgICAgICAgICAgICAgICAgICAgaWYgbW9kdWxlLmJpYXMgaXMgbm90IE5vbmU6CiAgICAgICAgICAgICAgICAgICAgICAgIGZsb2F0X2xpbmVhci5iaWFzLmNvcHlfKG1vZHVsZS5iaWFzLmRldGFjaCgpLnRvKHRvcmNoLmZsb2F0MzIpKQoKICAgICAgICAgICAgICAgIHdpdGggd2FybmluZ3MuY2F0Y2hfd2FybmluZ3MoKToKICAgICAgICAgICAgICAgICAgICB3YXJuaW5ncy5maWx0ZXJ3YXJuaW5ncygiaWdub3JlIiwgY2F0ZWdvcnk9RGVwcmVjYXRpb25XYXJuaW5nKQogICAgICAgICAgICAgICAgICAgIHF1YW50aXplZF9saW5lYXIgPSB0b3JjaC5xdWFudGl6YXRpb24ucXVhbnRpemVfZHluYW1pYygKICAgICAgICAgICAgICAgICAgICAgICAgdG9yY2gubm4uU2VxdWVudGlhbChmbG9hdF9saW5lYXIpLAogICAgICAgICAgICAgICAgICAgICAgICB7dG9yY2gubm4uTGluZWFyfSwKICAgICAgICAgICAgICAgICAgICAgICAgZHR5cGU9dG9yY2gucWludDgsCiAgICAgICAgICAgICAgICAgICAgKVswXQoKICAgICAgICAgICAgICAgIG1vZHVsZS5fZHluYW1pY19pbnQ4X2xpbmVhciA9IHF1YW50aXplZF9saW5lYXIKCiAgICAgICAgICAgICAgICBkZWYgX2ZvcndhcmRfZHluYW1pY19pbnQ4KHNlbGYsIGlucHV0LCB0YXNrX2lkPU5vbmUsIHJlc2lkdWFsPUZhbHNlKToKICAgICAgICAgICAgICAgICAgICBvdXQgPSBzZWxmLl9keW5hbWljX2ludDhfbGluZWFyKGlucHV0KQogICAgICAgICAgICAgICAgICAgIGlmIHJlc2lkdWFsOgogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb3V0LCBpbnB1dAogICAgICAgICAgICAgICAgICAgIHJldHVybiBvdXQKCiAgICAgICAgICAgICAgICBtb2R1bGUuZm9yd2FyZCA9IF9mb3J3YXJkX2R5bmFtaWNfaW50OC5fX2dldF9fKG1vZHVsZSwgbW9kdWxlLl9fY2xhc3NfXykKICAgICAgICAgICAgICAgIHF1YW50aXplZF9sYXllcnMgKz0gMQoKICAgICAgICAgICAgaWYgcXVhbnRpemVkX2xheWVycyA9PSAwOgogICAgICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICAgICAgIltBVklTT10gTmVuaHVtYSBjYW1hZGEgUGFyYW1ldHJpemVkTGluZWFyIGVuY29udHJhZGEgcGFyYSBkeW5hbWljLWludDg7IHVzYW5kbyBtb2RlbG8gcGFkcmFvLiIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIHJldHVybiBtb2RlbAoKICAgICAgICAgICAgcHJpbnQoZiJbK10gUXVhbnRpemFjYW8gSmluYSBhcGxpY2FkYTogZHluYW1pYy1pbnQ4IChDUFUsIHtxdWFudGl6ZWRfbGF5ZXJzfSBjYW1hZGFzKS4iKQogICAgICAgICAgICByZXR1cm4gbW9kZWwKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIHF1YW50X2Vycm9yOgogICAgICAgICAgICBwcmludChmIltBVklTT10gRmFsaGEgYW8gYXBsaWNhciBkeW5hbWljLWludDggKHtxdWFudF9lcnJvcn0pOyB1c2FuZG8gbW9kZWxvIHBhZHJhby4iKQogICAgICAgICAgICByZXR1cm4gbW9kZWwKCiAgICB0cnk6CiAgICAgICAgbW9kZWwgPSBfbG9hZF9mcm9tX2xvY2FsX2RpcihzZWxlY3Rpb24ubW9kZWxfaWQpCiAgICAgICAgbW9kZWwgPSBfYXBwbHlfamluYV9xdWFudGl6YXRpb25faWZfbmVlZGVkKG1vZGVsLCBzZWxlY3Rpb24ubW9kZWxfaWQpCiAgICAgICAgcHJpbnQoIlsrXSBNb2RlbG8gY2FycmVnYWRvIGNvbSBzdWNlc3NvLiIpCiAgICAgICAgcmV0dXJuIG1vZGVsCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGZpcnN0X2Vycm9yOgogICAgICAgIGlmIHNlbGVjdGlvbi5tb2RlbF9pZCA9PSBmYWxsYmFja19tb2RlbF9pZDoKICAgICAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKAogICAgICAgICAgICAgICAgZiJGYWxoYSBhbyBjYXJyZWdhciBvIG1vZGVsbyBmYWxsYmFjayAne2ZhbGxiYWNrX21vZGVsX2lkfSc6IHtmaXJzdF9lcnJvcn0iCiAgICAgICAgICAgICkgZnJvbSBmaXJzdF9lcnJvcgoKICAgICAgICBwcmludCgKICAgICAgICAgICAgZiJbIV0gRmFsaGEgYW8gY2FycmVnYXIgJ3tzZWxlY3Rpb24ubW9kZWxfaWR9Jzoge19mb3JtYXRfZXhjZXB0aW9uKGZpcnN0X2Vycm9yKX1cbiIKICAgICAgICAgICAgZiIgICAgVGVudGFuZG8gZmFsbGJhY2sgZGUgY2FycmVnYW1lbnRvOiB7ZmFsbGJhY2tfbW9kZWxfaWR9IgogICAgICAgICkKICAgICAgICBmYWxsYmFja19zZWxlY3Rpb24gPSBkb3dubG9hZF9tb2RlbF93aXRoX2ZhbGxiYWNrKAogICAgICAgICAgICBwcmVmZXJyZWRfbW9kZWxfaWQ9ZmFsbGJhY2tfbW9kZWxfaWQsCiAgICAgICAgICAgIGZhbGxiYWNrX21vZGVsX2lkPWZhbGxiYWNrX21vZGVsX2lkLAogICAgICAgICAgICBsb2NhbF9kaXI9bW9kZWxfYmFzZV9kaXIsCiAgICAgICAgKQogICAgICAgIHNlbGVjdGVkX21vZGVsX2RpciA9IGZhbGxiYWNrX3NlbGVjdGlvbi5sb2NhbF9kaXIKICAgICAgICBwcmludCgKICAgICAgICAgICAgZiJbK10gTW9kZWxvIHNlbGVjaW9uYWRvOiB7ZmFsbGJhY2tfc2VsZWN0aW9uLm1vZGVsX2lkfSAiCiAgICAgICAgICAgIGYiKHByb3ZpZGVyPXtmYWxsYmFja19zZWxlY3Rpb24ucHJvdmlkZXJ9LCBwYXRoPXtzZWxlY3RlZF9tb2RlbF9kaXJ9KSIKICAgICAgICApCiAgICAgICAgbW9kZWwgPSBfbG9hZF9mcm9tX2xvY2FsX2RpcihmYWxsYmFja19zZWxlY3Rpb24ubW9kZWxfaWQpCiAgICAgICAgbW9kZWwgPSBfYXBwbHlfamluYV9xdWFudGl6YXRpb25faWZfbmVlZGVkKG1vZGVsLCBmYWxsYmFja19zZWxlY3Rpb24ubW9kZWxfaWQpCiAgICAgICAgcHJpbnQoIlsrXSBNb2RlbG8gZmFsbGJhY2sgY2FycmVnYWRvIGNvbSBzdWNlc3NvLiIpCiAgICAgICAgcmV0dXJuIG1vZGVsCgoKZGVmIGNvbm5lY3RfdG9fY2hyb21hKCkgLT4gY2hyb21hZGIuSHR0cENsaWVudDoKICAgICIiIkNvbmVjdGEgYW8gQ2hyb21hREIgdmlhIEhUVFAgZSB2YWxpZGEgYSBjb25leMOjby4iIiIKICAgIHRyeToKICAgICAgICBjbGllbnQgPSBjaHJvbWFkYi5IdHRwQ2xpZW50KGhvc3Q9Q0hST01BX0hPU1QsIHBvcnQ9Q0hST01BX1BPUlQpCiAgICAgICAgIyBGYXogdW0gaGVhcnRiZWF0IHBhcmEgY29uZmlybWFyIHF1ZSBvIHNlcnZpZG9yIGVzdMOhIG5vIGFyCiAgICAgICAgY2xpZW50LmhlYXJ0YmVhdCgpCiAgICAgICAgcHJpbnQoZiJbK10gQ29uZWN0YWRvIGFvIENocm9tYURCIGVtIHtDSFJPTUFfSE9TVH06e0NIUk9NQV9QT1JUfSIpCiAgICAgICAgcmV0dXJuIGNsaWVudAogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHByaW50KGYiW0VSUk9dIE7Do28gZm9pIHBvc3PDrXZlbCBjb25lY3RhciBhbyBDaHJvbWFEQjoge2V9IikKICAgICAgICBwcmludCgiICAgICAgIFZlcmlmaXF1ZSBzZSBvIGNvbnRhaW5lciBEb2NrZXIgZXN0w6Egcm9kYW5kbzoiKQogICAgICAgIHByaW50KCIgICAgICAgZG9ja2VyIGNvbXBvc2UgdXAgLWQiKQogICAgICAgIHN5cy5leGl0KDEpCgoKZGVmIHNjYW5fZmlsZXMocm9vdF9wYXRoOiBQYXRoKSAtPiBJdGVyYXRvcltQYXRoXToKICAgICIiIgogICAgVmFycmUgcmVjdXJzaXZhbWVudGUgbyBkaXJldMOzcmlvIHJhaXosIHJldG9ybmFuZG8gZW0gc3RyZWFtaW5nCiAgICBvcyBhcnF1aXZvcyBkZSB0ZXh0byByZWxldmFudGVzIHBhcmEgaW5kZXhhw6fDo28uCiAgICAiIiIKICAgIGZvciBkaXJwYXRoLCBkaXJuYW1lcywgZmlsZW5hbWVzIGluIG9zLndhbGsocm9vdF9wYXRoKToKICAgICAgICAjIFJlbW92ZSBkaXJzIGlnbm9yYWRvcyBpbi1wbGFjZSBwYXJhIHF1ZSBvcy53YWxrIG7Do28gZGVzw6dhIG5lbGVzCiAgICAgICAgZGlybmFtZXNbOl0gPSBbCiAgICAgICAgICAgIGQgZm9yIGQgaW4gZGlybmFtZXMKICAgICAgICAgICAgaWYgZCBub3QgaW4gSUdOT1JFRF9ESVJTIGFuZCBub3QgZC5zdGFydHN3aXRoKCIuIikKICAgICAgICBdCiAgICAgICAgZGlybmFtZXMuc29ydCgpCgogICAgICAgIGZvciBmaWxlbmFtZSBpbiBzb3J0ZWQoZmlsZW5hbWVzKToKICAgICAgICAgICAgZmlsZXBhdGggPSBQYXRoKGRpcnBhdGgpIC8gZmlsZW5hbWUKCiAgICAgICAgICAgICMgSWdub3JhIHBvciBleHRlbnPDo28KICAgICAgICAgICAgaWYgZmlsZXBhdGguc3VmZml4Lmxvd2VyKCkgaW4gSUdOT1JFRF9FWFRFTlNJT05TOgogICAgICAgICAgICAgICAgY29udGludWUKCiAgICAgICAgICAgICMgSWdub3JhIGFycXVpdm9zIG11aXRvIGdyYW5kZXMKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgaWYgZmlsZXBhdGguc3RhdCgpLnN0X3NpemUgPiBNQVhfRklMRV9TSVpFX0JZVEVTOgogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yOgogICAgICAgICAgICAgICAgY29udGludWUKCiAgICAgICAgICAgIHlpZWxkIGZpbGVwYXRoCgoKZGVmIG1ha2VfY2h1bmtfaWQoZmlsZV9wYXRoOiBzdHIsIGNodW5rX2luZGV4OiBpbnQpIC0+IHN0cjoKICAgICIiIkdlcmEgdW0gSUQgZGV0ZXJtaW7DrXN0aWNvIHBhcmEgY2FkYSBjaHVuayBiYXNlYWRvIG5vIGNhbWluaG8gKyDDrW5kaWNlLiIiIgogICAgcmF3ID0gZiJ7ZmlsZV9wYXRofTo6Y2h1bms6OntjaHVua19pbmRleH0iCiAgICByZXR1cm4gaGFzaGxpYi5tZDUocmF3LmVuY29kZSgpKS5oZXhkaWdlc3QoKQoKCmRlZiByZWFkX2ZpbGVfc2FmZShmaWxlcGF0aDogUGF0aCkgLT4gc3RyIHwgTm9uZToKICAgICIiIkzDqiB1bSBhcnF1aXZvIGRlIHRleHRvLCB0ZW50YW5kbyBtw7psdGlwbG9zIGVuY29kaW5ncy4iIiIKICAgIGZvciBlbmNvZGluZyBpbiAoInV0Zi04IiwgImxhdGluLTEiLCAiY3AxMjUyIik6CiAgICAgICAgdHJ5OgogICAgICAgICAgICByZXR1cm4gZmlsZXBhdGgucmVhZF90ZXh0KGVuY29kaW5nPWVuY29kaW5nKQogICAgICAgIGV4Y2VwdCBVbmljb2RlRGVjb2RlRXJyb3I6CiAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgZXhjZXB0IE9TRXJyb3IgYXMgZToKICAgICAgICAgICAgcHJpbnQoZiIgIFtBVklTT10gTsOjbyBmb2kgcG9zc8OtdmVsIGxlciB7ZmlsZXBhdGh9OiB7ZX0iKQogICAgICAgICAgICByZXR1cm4gTm9uZQogICAgIyBTZSBuZW5odW0gZW5jb2RpbmcgZnVuY2lvbm91LCDDqSBwcm92YXZlbG1lbnRlIGJpbsOhcmlvIGRpc2ZhcsOnYWRvCiAgICByZXR1cm4gTm9uZQoKCmRlZiBkZWxldGVfZmlsZV9jaHVua3MoY29sbGVjdGlvbjogY2hyb21hZGIuQ29sbGVjdGlvbiwgZmlsZV9wYXRoOiBzdHIpIC0+IE5vbmU6CiAgICAiIiJSZW1vdmUgdG9kb3Mgb3MgY2h1bmtzIGRlIHVtIGFycXVpdm8gZXNwZWPDrWZpY28gZGEgY29sZcOnw6NvLiIiIgogICAgdHJ5OgogICAgICAgICMgUGVkZSBzb21lbnRlIElEcyBwYXJhIGV2aXRhciBtYXRlcmlhbGl6YXIgZG9jcy9tZXRhZGF0YSBuYSBtZW3Ds3JpYS4KICAgICAgICByZXN1bHRzID0gY29sbGVjdGlvbi5nZXQod2hlcmU9eyJmaWxlX3BhdGgiOiBmaWxlX3BhdGh9LCBpbmNsdWRlPVtdKQogICAgICAgIGlmIHJlc3VsdHMgYW5kIHJlc3VsdHNbImlkcyJdOgogICAgICAgICAgICBjb2xsZWN0aW9uLmRlbGV0ZShpZHM9cmVzdWx0c1siaWRzIl0pCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcHJpbnQoZiIgIFtBVklTT10gRXJybyBhbyBkZWxldGFyIGNodW5rcyBkZSB7ZmlsZV9wYXRofToge19mb3JtYXRfZXhjZXB0aW9uKGUpfSIpCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBJbmRleGHDp8OjbyBkZSB1bSDDum5pY28gYXJxdWl2bwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKZGVmIGluZGV4X2ZpbGUoCiAgICBmaWxlcGF0aDogUGF0aCwKICAgIGNvbGxlY3Rpb246IGNocm9tYWRiLkNvbGxlY3Rpb24sCiAgICBtb2RlbDogU2VudGVuY2VUcmFuc2Zvcm1lciwKICAgIHNwbGl0dGVyOiBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIsCiAgICByb290X3BhdGg6IFBhdGgsCiAgICBlbWJlZGRpbmdfYmF0Y2hfc2l6ZTogaW50LAopIC0+IGludDoKICAgICIiIgogICAgSW5kZXhhIHVtIMO6bmljbyBhcnF1aXZvOiBsw6osIGRpdmlkZSBlbSBjaHVua3MsIGdlcmEgZW1iZWRkaW5ncyBlIGZheiB1cHNlcnQuCiAgICBSZXRvcm5hIG8gbsO6bWVybyBkZSBjaHVua3MgaW5kZXhhZG9zLgogICAgIiIiCiAgICBjb250ZW50ID0gcmVhZF9maWxlX3NhZmUoZmlsZXBhdGgpCiAgICBpZiBub3QgY29udGVudCBvciBub3QgY29udGVudC5zdHJpcCgpOgogICAgICAgIHJldHVybiAwCgogICAgIyBVc2EgY2FtaW5obyBhYnNvbHV0byBjb21vIG1ldGFkYWRvCiAgICBhYnNfcGF0aCA9IHN0cihmaWxlcGF0aC5yZXNvbHZlKCkpCgogICAgIyBSZW1vdmUgY2h1bmtzIGFudGlnb3MgZGVzdGUgYXJxdWl2byAoYXR1YWxpemHDp8OjbyBpZGVtcG90ZW50ZSkKICAgIGRlbGV0ZV9maWxlX2NodW5rcyhjb2xsZWN0aW9uLCBhYnNfcGF0aCkKCiAgICBjaHVua3MgPSBzcGxpdHRlci5zcGxpdF90ZXh0KGNvbnRlbnQpCiAgICBpZiBub3QgY2h1bmtzOgogICAgICAgIHJldHVybiAwCgogICAgcmVsYXRpdmVfcGF0aCA9IHN0cihmaWxlcGF0aC5yZWxhdGl2ZV90byhyb290X3BhdGgpKQogICAgaW5zZXJ0ZWRfY2h1bmtzID0gMAogICAgYmF0Y2hfaWRzOiBsaXN0W3N0cl0gPSBbXQogICAgYmF0Y2hfZG9jczogbGlzdFtzdHJdID0gW10KICAgIGJhdGNoX21ldGFkYXRhczogbGlzdFtkaWN0W3N0ciwgb2JqZWN0XV0gPSBbXQoKICAgIGRlZiBfZmx1c2hfYmF0Y2goKSAtPiBOb25lOgogICAgICAgIG5vbmxvY2FsIGluc2VydGVkX2NodW5rcwogICAgICAgIGlmIG5vdCBiYXRjaF9pZHM6CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBlbWJlZGRpbmdzID0gbW9kZWwuZW5jb2RlKAogICAgICAgICAgICBiYXRjaF9kb2NzLAogICAgICAgICAgICBzaG93X3Byb2dyZXNzX2Jhcj1GYWxzZSwKICAgICAgICAgICAgYmF0Y2hfc2l6ZT1lbWJlZGRpbmdfYmF0Y2hfc2l6ZSwKICAgICAgICApLnRvbGlzdCgpCiAgICAgICAgY29sbGVjdGlvbi51cHNlcnQoCiAgICAgICAgICAgIGlkcz1iYXRjaF9pZHMsCiAgICAgICAgICAgIGVtYmVkZGluZ3M9ZW1iZWRkaW5ncywKICAgICAgICAgICAgZG9jdW1lbnRzPWJhdGNoX2RvY3MsCiAgICAgICAgICAgIG1ldGFkYXRhcz1iYXRjaF9tZXRhZGF0YXMsCiAgICAgICAgKQogICAgICAgIGluc2VydGVkX2NodW5rcyArPSBsZW4oYmF0Y2hfaWRzKQogICAgICAgIGRlbCBlbWJlZGRpbmdzCiAgICAgICAgYmF0Y2hfaWRzLmNsZWFyKCkKICAgICAgICBiYXRjaF9kb2NzLmNsZWFyKCkKICAgICAgICBiYXRjaF9tZXRhZGF0YXMuY2xlYXIoKQogICAgICAgIGdjLmNvbGxlY3QoKQoKICAgIGZvciBpLCBjaHVuayBpbiBlbnVtZXJhdGUoY2h1bmtzKToKICAgICAgICBiYXRjaF9pZHMuYXBwZW5kKG1ha2VfY2h1bmtfaWQoYWJzX3BhdGgsIGkpKQogICAgICAgIGJhdGNoX2RvY3MuYXBwZW5kKGNodW5rKQogICAgICAgIGJhdGNoX21ldGFkYXRhcy5hcHBlbmQoCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJmaWxlX3BhdGgiOiBhYnNfcGF0aCwKICAgICAgICAgICAgICAgICJjaHVua19pbmRleCI6IGksCiAgICAgICAgICAgICAgICAiZmlsZV9uYW1lIjogZmlsZXBhdGgubmFtZSwKICAgICAgICAgICAgICAgICMgQ2FtaW5obyByZWxhdGl2byDDoCByYWl6IGRvIHByb2pldG8gcGFyYSBleGliacOnw6NvIGNvbXBhY3RhCiAgICAgICAgICAgICAgICAicmVsYXRpdmVfcGF0aCI6IHJlbGF0aXZlX3BhdGgsCiAgICAgICAgICAgIH0KICAgICAgICApCiAgICAgICAgaWYgbGVuKGJhdGNoX2lkcykgPj0gZW1iZWRkaW5nX2JhdGNoX3NpemU6CiAgICAgICAgICAgIF9mbHVzaF9iYXRjaCgpCgogICAgX2ZsdXNoX2JhdGNoKCkKICAgIHJldHVybiBpbnNlcnRlZF9jaHVua3MKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvbnRvIGRlIGVudHJhZGEgcHJpbmNpcGFsCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpkZWYgbWFpbigpOgogICAgcGFyc2VyID0gYXJncGFyc2UuQXJndW1lbnRQYXJzZXIoCiAgICAgICAgZGVzY3JpcHRpb249IkluZGV4YSB1bSBwcm9qZXRvIGRlIGPDs2RpZ28gbm8gQ2hyb21hREIgcGFyYSBSQUcgbG9jYWwuIgogICAgKQogICAgcGFyc2VyLmFkZF9hcmd1bWVudCgKICAgICAgICAicHJvamVjdF9wYXRoIiwKICAgICAgICBuYXJncz0iPyIsCiAgICAgICAgZGVmYXVsdD0iLiIsCiAgICAgICAgaGVscD0iQ2FtaW5obyByYWl6IGRvIHByb2pldG8gYSBpbmRleGFyIChwYWRyw6NvOiBkaXJldMOzcmlvIGF0dWFsKSIsCiAgICApCiAgICBwYXJzZXIuYWRkX2FyZ3VtZW50KAogICAgICAgICItLWNsZWFyIiwKICAgICAgICBhY3Rpb249InN0b3JlX3RydWUiLAogICAgICAgIGhlbHA9IkxpbXBhIHRvZGEgYSBjb2xlw6fDo28gYW50ZXMgZGUgcmVpbmRleGFyIiwKICAgICkKICAgIHBhcnNlci5hZGRfYXJndW1lbnQoCiAgICAgICAgIi0tZW1iZWRkaW5nLW1vZGVsIiwKICAgICAgICBjaG9pY2VzPVsiamluYSIsICJiZ2UiLCAiaHlicmlkIl0sCiAgICAgICAgaGVscD0oCiAgICAgICAgICAgICJNb2RlbG8gZGUgZW1iZWRkaW5nczogJ2ppbmEnIChjb2RpZ28pLCAiCiAgICAgICAgICAgICInYmdlJyAoY29udGV1ZG8gbWlzdG8pIG91ICdoeWJyaWQnIChkdWFzIGNvbGVjb2VzOiBKaW5hIHYyICsgQkdFKS4iCiAgICAgICAgKSwKICAgICkKICAgIHBhcnNlci5hZGRfYXJndW1lbnQoCiAgICAgICAgIi0tamluYS1xdWFudGl6YXRpb24iLAogICAgICAgIGNob2ljZXM9WyJkZWZhdWx0IiwgImR5bmFtaWMtaW50OCJdLAogICAgICAgIGhlbHA9IlF1YW50aXphY2FvIHBhcmEgSmluYTogJ2RlZmF1bHQnIChtYWlzIHF1YWxpZGFkZSkgb3UgJ2R5bmFtaWMtaW50OCcgKG1haXMgdmVsb2NpZGFkZSkuIiwKICAgICkKICAgIHBhcnNlci5hZGRfYXJndW1lbnQoCiAgICAgICAgIi0tcGVyZi1wcm9maWxlIiwKICAgICAgICBjaG9pY2VzPVsiYXV0b3R1bmUiLCAibWF4LXBlcmZvcm1hbmNlIl0sCiAgICAgICAgaGVscD0oCiAgICAgICAgICAgICJQZXJmaWwgZGUgcGVyZm9ybWFuY2UgZGEgaW5kZXhhw6fDo286ICIKICAgICAgICAgICAgIidhdXRvdHVuZScgKGN1c3RvLWJlbmVmw61jaW8pIG91ICdtYXgtcGVyZm9ybWFuY2UnIChtYWlzIHRocm91Z2hwdXQsIG1haW9yIHVzbyBkZSBSQU0pLiIKICAgICAgICApLAogICAgKQogICAgYXJncyA9IHBhcnNlci5wYXJzZV9hcmdzKCkKCiAgICByb290X3BhdGggPSBQYXRoKGFyZ3MucHJvamVjdF9wYXRoKS5yZXNvbHZlKCkKICAgIGlmIG5vdCByb290X3BhdGguaXNfZGlyKCk6CiAgICAgICAgcHJpbnQoZiJbRVJST10gQ2FtaW5obyBuw6NvIGV4aXN0ZSBvdSBuw6NvIMOpIHVtIGRpcmV0w7NyaW86IHtyb290X3BhdGh9IikKICAgICAgICBzeXMuZXhpdCgxKQoKICAgIHByaW50KGYiXG57Jz0nKjYwfSIpCiAgICBwcmludChmIiAgUkFHIEluZGV4ZXIg4oCUIFByb2pldG86IHtyb290X3BhdGh9IikKICAgIHByaW50KGYieyc9Jyo2MH1cbiIpCiAgICBpbmRleF9zdGFydGVkX2F0ID0gZGF0ZXRpbWUubm93KCkKICAgIHByaW50KGYiW0lORk9dIEluw61jaW86IHtpbmRleF9zdGFydGVkX2F0LnN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycpfSIpCgogICAgZm9yY2VfbW9kZWxfcmVjb25maWd1cmUgPSBfZW52X2Jvb2woIk1DUF9GT1JDRV9NT0RFTF9SRUNPTkZJRyIsIGRlZmF1bHQ9RmFsc2UpCiAgICBwZXJzaXN0ZWRfY29uZmlnID0gbG9hZF9pbmRleGVyX3R1bmluZ19jb25maWcoZm9yY2VfbW9kZWxfcmVjb25maWd1cmUpCiAgICBtb2RlbF9jaG9pY2UsIGppbmFfcXVhbnRpemF0aW9uID0gcmVzb2x2ZV9lbWJlZGRpbmdfY29uZmlnKAogICAgICAgIGFyZ3MuZW1iZWRkaW5nX21vZGVsLAogICAgICAgIGFyZ3MuamluYV9xdWFudGl6YXRpb24sCiAgICAgICAgcGVyc2lzdGVkX2NvbmZpZz1wZXJzaXN0ZWRfY29uZmlnLAogICAgKQogICAgcGVyZl9wcm9maWxlID0gcmVzb2x2ZV9wZXJmX3Byb2ZpbGUoYXJncy5wZXJmX3Byb2ZpbGUsIHBlcnNpc3RlZF9jb25maWcpCgogICAgY2h1bmtfc2l6ZV9sb2NrZWQgPSAiTUNQX0NIVU5LX1NJWkUiIGluIG9zLmVudmlyb24KICAgIGNodW5rX292ZXJsYXBfbG9ja2VkID0gIk1DUF9DSFVOS19PVkVSTEFQIiBpbiBvcy5lbnZpcm9uCiAgICBiYXRjaF9zaXplX2xvY2tlZCA9ICJNQ1BfRU1CRURESU5HX0JBVENIX1NJWkUiIGluIG9zLmVudmlyb24KCiAgICBwZXJzaXN0ZWRfY2h1bmtfc2l6ZSA9IF9wYXJzZV9jb25maWdfaW50KHBlcnNpc3RlZF9jb25maWcsICJjaHVua19zaXplIikKICAgIHBlcnNpc3RlZF9jaHVua19vdmVybGFwID0gX3BhcnNlX2NvbmZpZ19pbnQocGVyc2lzdGVkX2NvbmZpZywgImNodW5rX292ZXJsYXAiKQogICAgcGVyc2lzdGVkX2JhdGNoX3NpemUgPSBfcGFyc2VfY29uZmlnX2ludChwZXJzaXN0ZWRfY29uZmlnLCAiZW1iZWRkaW5nX2JhdGNoX3NpemUiKQoKICAgIGVmZmVjdGl2ZV9jaHVua19zaXplID0gQ0hVTktfU0laRQogICAgaWYgbm90IGNodW5rX3NpemVfbG9ja2VkIGFuZCBwZXJzaXN0ZWRfY2h1bmtfc2l6ZSBpcyBub3QgTm9uZToKICAgICAgICBlZmZlY3RpdmVfY2h1bmtfc2l6ZSA9IG1heCgyNTYsIHBlcnNpc3RlZF9jaHVua19zaXplKQoKICAgIGVmZmVjdGl2ZV9jaHVua19vdmVybGFwID0gQ0hVTktfT1ZFUkxBUAogICAgaWYgbm90IGNodW5rX292ZXJsYXBfbG9ja2VkIGFuZCBwZXJzaXN0ZWRfY2h1bmtfb3ZlcmxhcCBpcyBub3QgTm9uZToKICAgICAgICBlZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcCA9IG1heCgwLCBtaW4oZWZmZWN0aXZlX2NodW5rX3NpemUgLSAxLCBwZXJzaXN0ZWRfY2h1bmtfb3ZlcmxhcCkpCgogICAgZWZmZWN0aXZlX2JhdGNoX3NpemUgPSBFTUJFRERJTkdfQkFUQ0hfU0laRQogICAgaWYgbm90IGJhdGNoX3NpemVfbG9ja2VkIGFuZCBwZXJzaXN0ZWRfYmF0Y2hfc2l6ZSBpcyBub3QgTm9uZToKICAgICAgICBlZmZlY3RpdmVfYmF0Y2hfc2l6ZSA9IG1heCgxLCBwZXJzaXN0ZWRfYmF0Y2hfc2l6ZSkKCiAgICBwcmludCgKICAgICAgICBmIltDT05GSUddIE1vZGVsbyBlc2NvbGhpZG86IHttb2RlbF9jaG9pY2V9ICIKICAgICAgICBmIih7X2Rlc2NyaWJlX2VtYmVkZGluZ19jaG9pY2UobW9kZWxfY2hvaWNlKX0pIgogICAgKQogICAgaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hIjoKICAgICAgICBwcmludChmIltDT05GSUddIFF1YW50aXphY2FvIEppbmE6IHtqaW5hX3F1YW50aXphdGlvbn0iKQogICAgZWxpZiBtb2RlbF9jaG9pY2UgPT0gImh5YnJpZCI6CiAgICAgICAgcHJpbnQoIltDT05GSUddIFF1YW50aXphY2FvIEppbmE6IG5hbyBhcGxpY2F2ZWwgbm8gaHlicmlkIChKaW5hIHYyICsgQkdFKSIpCiAgICBlbHNlOgogICAgICAgIHByaW50KCJbQ09ORklHXSBRdWFudGl6YWNhbyBKaW5hOiBuYW8gYXBsaWNhdmVsIChtb2RlbG8gQkdFIHNlbGVjaW9uYWRvKSIpCiAgICBwcmludChmIltDT05GSUddIFBlcmZpbCBkZSBwZXJmb3JtYW5jZToge3BlcmZfcHJvZmlsZX0iKQogICAgaWYgcGVyZl9wcm9maWxlID09ICJtYXgtcGVyZm9ybWFuY2UiOgogICAgICAgIHByaW50KAogICAgICAgICAgICAiW0FWSVNPXSBFc3RlIG1vZG8gcG9kZSBlbGV2YXIgY29uc2lkZXJhdmVsbWVudGUgbyBjb25zdW1vIGRlIG1lbcOzcmlhICIKICAgICAgICAgICAgImUgY2F1c2FyIGVuY2VycmFtZW50byBwb3IgT09NIChleGl0IDEzNykuIgogICAgICAgICkKICAgIHdhcm5faWZfamluYV9tZW1vcnlfcmlzayhtb2RlbF9jaG9pY2UsIGppbmFfcXVhbnRpemF0aW9uKQoKICAgICMgSW5pY2lhbGl6YSBjb21wb25lbnRlcwogICAgY2xpZW50ID0gY29ubmVjdF90b19jaHJvbWEoKQogICAgdGFyZ2V0cyA9IF9yZXNvbHZlX2luZGV4X3RhcmdldHMobW9kZWxfY2hvaWNlKQoKICAgICMgT2J0w6ltIG91IHJlY3JpYSBhcyBjb2xlw6fDtWVzIGVudm9sdmlkYXMuCiAgICBjb2xsZWN0aW9uczogZGljdFtzdHIsIGNocm9tYWRiLkNvbGxlY3Rpb25dID0ge30KICAgIGNvbGxlY3Rpb25fZGltZW5zaW9uX3Jlc2V0X2RvbmU6IGRpY3Rbc3RyLCBib29sXSA9IHt9CiAgICBmb3IgdGFyZ2V0IGluIHRhcmdldHM6CiAgICAgICAgaWYgYXJncy5jbGVhcjoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgY2xpZW50LmRlbGV0ZV9jb2xsZWN0aW9uKHRhcmdldC5jb2xsZWN0aW9uX25hbWUpCiAgICAgICAgICAgICAgICBwcmludChmIlshXSBDb2xlw6fDo28gJ3t0YXJnZXQuY29sbGVjdGlvbl9uYW1lfScgcmVtb3ZpZGEgcGFyYSByZWluZGV4YcOnw6NvIGxpbXBhLiIpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgICAgICBwYXNzCiAgICAgICAgY29sbGVjdGlvbnNbdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0gPSBjbGllbnQuZ2V0X29yX2NyZWF0ZV9jb2xsZWN0aW9uKAogICAgICAgICAgICBuYW1lPXRhcmdldC5jb2xsZWN0aW9uX25hbWUsCiAgICAgICAgICAgIG1ldGFkYXRhPXsiaG5zdzpzcGFjZSI6ICJjb3NpbmUifSwKICAgICAgICApCiAgICAgICAgY29sbGVjdGlvbl9kaW1lbnNpb25fcmVzZXRfZG9uZVt0YXJnZXQuY29sbGVjdGlvbl9uYW1lXSA9IEZhbHNlCgogICAgIyBDYXJyZWdhIG1vZGVsb3MgZGUgZm9ybWEgbGF6eSBlIHJlYXByb3ZlaXRhIHBvciB0YXJnZXQuCiAgICBsb2FkZWRfbW9kZWxzOiBkaWN0W3N0ciwgU2VudGVuY2VUcmFuc2Zvcm1lcl0gPSB7fQogICAgdG90YWxfY2h1bmtzID0gMAogICAgZXJyb3JzID0gMAogICAgZmlsZXNfc2Nhbm5lZCA9IDAKICAgIGZpbGVzX3Byb2Nlc3NlZF90b3RhbCA9IDAKICAgIGNodW5rc19ieV9jb2xsZWN0aW9uID0ge3RhcmdldC5jb2xsZWN0aW9uX25hbWU6IDAgZm9yIHRhcmdldCBpbiB0YXJnZXRzfQogICAgZmlsZXNfYnlfY29sbGVjdGlvbiA9IHt0YXJnZXQuY29sbGVjdGlvbl9uYW1lOiAwIGZvciB0YXJnZXQgaW4gdGFyZ2V0c30KICAgIGZpbGVzX2VsaWdpYmxlX2J5X2NvbGxlY3Rpb24gPSB7dGFyZ2V0LmNvbGxlY3Rpb25fbmFtZTogMCBmb3IgdGFyZ2V0IGluIHRhcmdldHN9CiAgICBlcnJvcnNfYnlfY29sbGVjdGlvbiA9IHt0YXJnZXQuY29sbGVjdGlvbl9uYW1lOiAwIGZvciB0YXJnZXQgaW4gdGFyZ2V0c30KICAgIGVycm9yX3NhbXBsZXNfYnlfY29sbGVjdGlvbjogZGljdFtzdHIsIGxpc3Rbc3RyXV0gPSB7dGFyZ2V0LmNvbGxlY3Rpb25fbmFtZTogW10gZm9yIHRhcmdldCBpbiB0YXJnZXRzfQogICAgdGFyZ2V0X2J5X21vZGVsID0ge3RhcmdldC5tb2RlbF9jaG9pY2U6IHRhcmdldCBmb3IgdGFyZ2V0IGluIHRhcmdldHN9CgogICAgIyBDYXJyZWdhIG8gcHJpbWVpcm8gbW9kZWxvIGFudGVzIHBhcmEgYXV0b3R1bmUgY29tIG1pY3JvLWJlbmNobWFyay4KICAgIHByaW1hcnlfdGFyZ2V0ID0gdGFyZ2V0c1swXQogICAgcHJpbWFyeV9xdWFudGl6YXRpb24gPSBqaW5hX3F1YW50aXphdGlvbiBpZiBwcmltYXJ5X3RhcmdldC5tb2RlbF9jaG9pY2UgPT0gImppbmEiIGVsc2UgImRlZmF1bHQiCiAgICBsb2FkZWRfbW9kZWxzW3ByaW1hcnlfdGFyZ2V0Lm1vZGVsX2Nob2ljZV0gPSBsb2FkX2VtYmVkZGluZ19tb2RlbChwcmltYXJ5X3RhcmdldC5tb2RlbF9jaG9pY2UsIHByaW1hcnlfcXVhbnRpemF0aW9uKQogICAgcHJpbWFyeV9tb2RlbCA9IGxvYWRlZF9tb2RlbHNbcHJpbWFyeV90YXJnZXQubW9kZWxfY2hvaWNlXQoKICAgIGlmIHBlcmZfcHJvZmlsZSA9PSAiYXV0b3R1bmUiOgogICAgICAgIHR1bmVkID0gX3Jlc29sdmVfYXV0b3R1bmVkX3BhcmFtcygKICAgICAgICAgICAgbW9kZWw9cHJpbWFyeV9tb2RlbCwKICAgICAgICAgICAgY2h1bmtfc2l6ZV9sb2NrZWQ9Y2h1bmtfc2l6ZV9sb2NrZWQsCiAgICAgICAgICAgIGNodW5rX292ZXJsYXBfbG9ja2VkPWNodW5rX292ZXJsYXBfbG9ja2VkLAogICAgICAgICAgICBiYXRjaF9zaXplX2xvY2tlZD1iYXRjaF9zaXplX2xvY2tlZCwKICAgICAgICAgICAgY2h1bmtfc2l6ZT1lZmZlY3RpdmVfY2h1bmtfc2l6ZSwKICAgICAgICAgICAgY2h1bmtfb3ZlcmxhcD1lZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcCwKICAgICAgICAgICAgZW1iZWRkaW5nX2JhdGNoX3NpemU9ZWZmZWN0aXZlX2JhdGNoX3NpemUsCiAgICAgICAgKQogICAgZWxzZToKICAgICAgICB0dW5lZCA9IF9yZXNvbHZlX21heF9wZXJmb3JtYW5jZV9wYXJhbXMoCiAgICAgICAgICAgIGNodW5rX3NpemVfbG9ja2VkPWNodW5rX3NpemVfbG9ja2VkLAogICAgICAgICAgICBjaHVua19vdmVybGFwX2xvY2tlZD1jaHVua19vdmVybGFwX2xvY2tlZCwKICAgICAgICAgICAgYmF0Y2hfc2l6ZV9sb2NrZWQ9YmF0Y2hfc2l6ZV9sb2NrZWQsCiAgICAgICAgICAgIGNodW5rX3NpemU9ZWZmZWN0aXZlX2NodW5rX3NpemUsCiAgICAgICAgICAgIGNodW5rX292ZXJsYXA9ZWZmZWN0aXZlX2NodW5rX292ZXJsYXAsCiAgICAgICAgICAgIGVtYmVkZGluZ19iYXRjaF9zaXplPWVmZmVjdGl2ZV9iYXRjaF9zaXplLAogICAgICAgICkKCiAgICBlZmZlY3RpdmVfY2h1bmtfc2l6ZSA9IG1heCgyNTYsIHR1bmVkLmNodW5rX3NpemUpCiAgICBlZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcCA9IG1heCgwLCBtaW4oZWZmZWN0aXZlX2NodW5rX3NpemUgLSAxLCB0dW5lZC5jaHVua19vdmVybGFwKSkKICAgIGVmZmVjdGl2ZV9iYXRjaF9zaXplID0gbWF4KDEsIHR1bmVkLmVtYmVkZGluZ19iYXRjaF9zaXplKQoKICAgIGZvciByZWFzb24gaW4gdHVuZWQucmVhc29uczoKICAgICAgICBwcmludChmIltDT05GSUddIHtyZWFzb259IikKCiAgICBwcmludCgKICAgICAgICBmIltDT05GSUddIFBhcsOibWV0cm9zIGZpbmFpczogIgogICAgICAgIGYiY2h1bmtfc2l6ZT17ZWZmZWN0aXZlX2NodW5rX3NpemV9LCBjaHVua19vdmVybGFwPXtlZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcH0sICIKICAgICAgICBmImVtYmVkZGluZ19iYXRjaD17ZWZmZWN0aXZlX2JhdGNoX3NpemV9IgogICAgKQoKICAgIHNhdmVfaW5kZXhlcl90dW5pbmdfY29uZmlnKAogICAgICAgIHsKICAgICAgICAgICAgImVtYmVkZGluZ19tb2RlbCI6IG1vZGVsX2Nob2ljZSwKICAgICAgICAgICAgImppbmFfcXVhbnRpemF0aW9uIjogamluYV9xdWFudGl6YXRpb24sCiAgICAgICAgICAgICJwZXJmX3Byb2ZpbGUiOiBwZXJmX3Byb2ZpbGUsCiAgICAgICAgICAgICJjaHVua19zaXplIjogZWZmZWN0aXZlX2NodW5rX3NpemUsCiAgICAgICAgICAgICJjaHVua19vdmVybGFwIjogZWZmZWN0aXZlX2NodW5rX292ZXJsYXAsCiAgICAgICAgICAgICJlbWJlZGRpbmdfYmF0Y2hfc2l6ZSI6IGVmZmVjdGl2ZV9iYXRjaF9zaXplLAogICAgICAgIH0KICAgICkKCiAgICBzcGxpdHRlciA9IGdldF90ZXh0X3NwbGl0dGVyKGVmZmVjdGl2ZV9jaHVua19zaXplLCBlZmZlY3RpdmVfY2h1bmtfb3ZlcmxhcCkKCiAgICBwcmludChmIlxuWytdIFZhcnJlbmRvIGUgaW5kZXhhbmRvIGFycXVpdm9zIGVtOiB7cm9vdF9wYXRofSIpCiAgICBmaWxlcyA9IGxpc3Qoc2Nhbl9maWxlcyhyb290X3BhdGgpKQogICAgZmlsZXNfc2Nhbm5lZCA9IGxlbihmaWxlcykKICAgIGlmIGZpbGVzX3NjYW5uZWQgPT0gMDoKICAgICAgICBwcmludCgiW0FWSVNPXSBOZW5odW0gYXJxdWl2byBlbmNvbnRyYWRvLiBWZXJpZmlxdWUgbyBjYW1pbmhvIGUgb3MgZmlsdHJvcy4iKQogICAgICAgIHN5cy5leGl0KDApCgogICAgcHJpbnQoZiJbK10ge2ZpbGVzX3NjYW5uZWR9IGFycXVpdm8ocykgZWxlZ8OtdmVsKGlzKSBwYXJhIGluZGV4YcOnw6NvLiIpCiAgICB3aXRoIHRxZG0oCiAgICAgICAgdG90YWw9ZmlsZXNfc2Nhbm5lZCwKICAgICAgICBkZXNjPSJJbmRleGFuZG8iLAogICAgICAgIHVuaXQ9ImFycXVpdm8iLAogICAgICAgIGJhcl9mb3JtYXQ9IntsX2Jhcn17YmFyfXwge25fZm10fS97dG90YWxfZm10fSBbe2VsYXBzZWR9LCB7cmF0ZV9mbXR9XSIsCiAgICApIGFzIHBiYXI6CiAgICAgICAgZm9yIGZpbGVwYXRoIGluIGZpbGVzOgogICAgICAgICAgICB0YXJnZXRfbW9kZWxzID0gX2NsYXNzaWZ5X2ZpbGVfdGFyZ2V0cyhmaWxlcGF0aCwgbW9kZWxfY2hvaWNlKQoKICAgICAgICAgICAgZm9yIHRhcmdldF9tb2RlbCBpbiB0YXJnZXRfbW9kZWxzOgogICAgICAgICAgICAgICAgdGFyZ2V0ID0gdGFyZ2V0X2J5X21vZGVsLmdldCh0YXJnZXRfbW9kZWwpCiAgICAgICAgICAgICAgICBpZiB0YXJnZXQgaXMgTm9uZToKICAgICAgICAgICAgICAgICAgICBjb250aW51ZQoKICAgICAgICAgICAgICAgIGlmIHRhcmdldC5tb2RlbF9jaG9pY2Ugbm90IGluIGxvYWRlZF9tb2RlbHM6CiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X3F1YW50aXphdGlvbiA9IGppbmFfcXVhbnRpemF0aW9uIGlmIHRhcmdldC5tb2RlbF9jaG9pY2UgPT0gImppbmEiIGVsc2UgImRlZmF1bHQiCiAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICBsb2FkZWRfbW9kZWxzW3RhcmdldC5tb2RlbF9jaG9pY2VdID0gbG9hZF9lbWJlZGRpbmdfbW9kZWwoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXQubW9kZWxfY2hvaWNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X3F1YW50aXphdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgbG9hZF9lcnJvcjoKICAgICAgICAgICAgICAgICAgICAgICAgIyBFbSBoeWJyaWQsIHBvZGUgZmFsdGFyIFJBTSBhbyBtYW50ZXIgZG9pcyBtb2RlbG9zIGdyYW5kZXMgc2ltdWx0YW5lYW1lbnRlLgogICAgICAgICAgICAgICAgICAgICAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImh5YnJpZCIgYW5kIGxvYWRlZF9tb2RlbHMgYW5kIF9pc19tZW1vcnlfcmVsYXRlZF9lcnJvcihsb2FkX2Vycm9yKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW50KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJbQVZJU09dIEZhbGhhIGFvIGNhcnJlZ2FyIG1vZGVsbyBhZGljaW9uYWwgbm8gaHlicmlkIHBvciBtZW3Ds3JpYS4gIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaWJlcmFuZG8gbW9kZWxvIGFudGVyaW9yIGUgdGVudGFuZG8gbm92YW1lbnRlLiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlZF9tb2RlbHMuY2xlYXIoKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2MuY29sbGVjdCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkZWRfbW9kZWxzW3RhcmdldC5tb2RlbF9jaG9pY2VdID0gbG9hZF9lbWJlZGRpbmdfbW9kZWwoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0Lm1vZGVsX2Nob2ljZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfcXVhbnRpemF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFpc2UKCiAgICAgICAgICAgICAgICBtb2RlbCA9IGxvYWRlZF9tb2RlbHNbdGFyZ2V0Lm1vZGVsX2Nob2ljZV0KICAgICAgICAgICAgICAgIGNvbGxlY3Rpb24gPSBjb2xsZWN0aW9uc1t0YXJnZXQuY29sbGVjdGlvbl9uYW1lXQogICAgICAgICAgICAgICAgZmlsZXNfZWxpZ2libGVfYnlfY29sbGVjdGlvblt0YXJnZXQuY29sbGVjdGlvbl9uYW1lXSArPSAxCgogICAgICAgICAgICAgICAgd2hpbGUgVHJ1ZToKICAgICAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgICAgIG5fY2h1bmtzID0gaW5kZXhfZmlsZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVwYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGVjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXR0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb290X3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbWJlZGRpbmdfYmF0Y2hfc2l6ZT1lZmZlY3RpdmVfYmF0Y2hfc2l6ZSwKICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9jaHVua3MgKz0gbl9jaHVua3MKICAgICAgICAgICAgICAgICAgICAgICAgZmlsZXNfcHJvY2Vzc2VkX3RvdGFsICs9IDEKICAgICAgICAgICAgICAgICAgICAgICAgY2h1bmtzX2J5X2NvbGxlY3Rpb25bdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0gKz0gbl9jaHVua3MKICAgICAgICAgICAgICAgICAgICAgICAgZmlsZXNfYnlfY29sbGVjdGlvblt0YXJnZXQuY29sbGVjdGlvbl9uYW1lXSArPSAxCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICAgICAgICAgICAgICAjIEZhbGxiYWNrIGF1dG9tw6F0aWNvIHBhcmEgZXZpdGFyIHF1ZWJyYSB0b3RhbCBlbSBtw6FxdWluYXMgbm8gbGltaXRlIGRlIFJBTS4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm90IGJhdGNoX3NpemVfbG9ja2VkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgZWZmZWN0aXZlX2JhdGNoX3NpemUgPiAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgX2lzX21lbW9yeV9yZWxhdGVkX2Vycm9yKGUpCiAgICAgICAgICAgICAgICAgICAgICAgICk6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfYmF0Y2ggPSBtYXgoMSwgZWZmZWN0aXZlX2JhdGNoX3NpemUgLy8gMikKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIG5ld19iYXRjaCA8IGVmZmVjdGl2ZV9iYXRjaF9zaXplOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRxZG0ud3JpdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYiICBbQUpVU1RFXSBNZW3Ds3JpYSBhbHRhIGVtIHt0YXJnZXQubGFiZWx9LiAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYiQmF0Y2ggcmVkdXppZG8ge2VmZmVjdGl2ZV9iYXRjaF9zaXplfSAtPiB7bmV3X2JhdGNofS4iCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdGl2ZV9iYXRjaF9zaXplID0gbmV3X2JhdGNoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2MuY29sbGVjdCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWUKCiAgICAgICAgICAgICAgICAgICAgICAgIGlmICgKICAgICAgICAgICAgICAgICAgICAgICAgICAgIF9pc19kaW1lbnNpb25fbWlzbWF0Y2hfZXJyb3IoZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZCBub3QgY29sbGVjdGlvbl9kaW1lbnNpb25fcmVzZXRfZG9uZVt0YXJnZXQuY29sbGVjdGlvbl9uYW1lXQogICAgICAgICAgICAgICAgICAgICAgICApOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHFkbS53cml0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmIiAgW0FKVVNURV0gRGltZW5zw6NvIGluY29tcGF0w612ZWwgZGV0ZWN0YWRhIGVtICd7dGFyZ2V0LmNvbGxlY3Rpb25fbmFtZX0nLiAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJlY3JpYW5kbyBjb2xlw6fDo28gZSB0ZW50YW5kbyBub3ZhbWVudGUuIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaWVudC5kZWxldGVfY29sbGVjdGlvbih0YXJnZXQuY29sbGVjdGlvbl9uYW1lKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXNzCgogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGVjdGlvbnNbdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0gPSBjbGllbnQuZ2V0X29yX2NyZWF0ZV9jb2xsZWN0aW9uKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU9dGFyZ2V0LmNvbGxlY3Rpb25fbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YT17Imhuc3c6c3BhY2UiOiAiY29zaW5lIn0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsZWN0aW9uX2RpbWVuc2lvbl9yZXNldF9kb25lW3RhcmdldC5jb2xsZWN0aW9uX25hbWVdID0gVHJ1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2MuY29sbGVjdCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZQoKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JzICs9IDEKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JzX2J5X2NvbGxlY3Rpb25bdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0gKz0gMQogICAgICAgICAgICAgICAgICAgICAgICBpZiBsZW4oZXJyb3Jfc2FtcGxlc19ieV9jb2xsZWN0aW9uW3RhcmdldC5jb2xsZWN0aW9uX25hbWVdKSA8IDM6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvcl9zYW1wbGVzX2J5X2NvbGxlY3Rpb25bdGFyZ2V0LmNvbGxlY3Rpb25fbmFtZV0uYXBwZW5kKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYie2ZpbGVwYXRoLm5hbWV9OiB7X2Zvcm1hdF9leGNlcHRpb24oZSl9IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICB0cWRtLndyaXRlKGYiICBbRVJST10ge2ZpbGVwYXRofSBbe3RhcmdldC5sYWJlbH1dOiB7X2Zvcm1hdF9leGNlcHRpb24oZSl9IikKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKCiAgICAgICAgICAgIHBiYXIuc2V0X3Bvc3RmaXgoeyJjaHVua3MiOiB0b3RhbF9jaHVua3MsICJhdHVhbCI6IGZpbGVwYXRoLm5hbWVbOjIwXX0pCiAgICAgICAgICAgIHBiYXIudXBkYXRlKDEpCgogICAgZm9yIHRhcmdldCBpbiB0YXJnZXRzOgogICAgICAgIGNvbGxlY3Rpb25fbmFtZSA9IHRhcmdldC5jb2xsZWN0aW9uX25hbWUKICAgICAgICBlbGlnaWJsZSA9IGZpbGVzX2VsaWdpYmxlX2J5X2NvbGxlY3Rpb25bY29sbGVjdGlvbl9uYW1lXQogICAgICAgIHByb2Nlc3NlZCA9IGZpbGVzX2J5X2NvbGxlY3Rpb25bY29sbGVjdGlvbl9uYW1lXQogICAgICAgIHRhcmdldF9lcnJvcnMgPSBlcnJvcnNfYnlfY29sbGVjdGlvbltjb2xsZWN0aW9uX25hbWVdCgogICAgICAgIGlmIGVsaWdpYmxlID09IDA6CiAgICAgICAgICAgIHByaW50KGYiW0FWSVNPXSBOZW5odW0gYXJxdWl2byBlbGVnw612ZWwgcGFyYSB7dGFyZ2V0LmxhYmVsfTsgZXRhcGEgaWdub3JhZGEuIikKICAgICAgICBlbGlmIHByb2Nlc3NlZCA9PSAwIGFuZCB0YXJnZXRfZXJyb3JzID4gMDoKICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICBmIltBVklTT10ge2VsaWdpYmxlfSBhcnF1aXZvKHMpIGVsZWfDrXZlbChpcykgcGFyYSB7dGFyZ2V0LmxhYmVsfSwgIgogICAgICAgICAgICAgICAgIm1hcyB0b2RvcyBmYWxoYXJhbS4iCiAgICAgICAgICAgICkKCiAgICAgICAgaWYgdGFyZ2V0X2Vycm9yczoKICAgICAgICAgICAgcHJpbnQoZiJbQVZJU09dIHt0YXJnZXRfZXJyb3JzfSBlcnJvKHMpIGR1cmFudGUgYSBpbmRleGHDp8OjbyBkbyB0YXJnZXQge3RhcmdldC5sYWJlbH0uIikKICAgICAgICAgICAgZm9yIHNhbXBsZSBpbiBlcnJvcl9zYW1wbGVzX2J5X2NvbGxlY3Rpb25bY29sbGVjdGlvbl9uYW1lXToKICAgICAgICAgICAgICAgIHByaW50KGYiICAgICAgICAtIHtzYW1wbGV9IikKCiAgICBpbmRleF9maW5pc2hlZF9hdCA9IGRhdGV0aW1lLm5vdygpCiAgICBlbGFwc2VkX3NlY29uZHMgPSBpbnQoKGluZGV4X2ZpbmlzaGVkX2F0IC0gaW5kZXhfc3RhcnRlZF9hdCkudG90YWxfc2Vjb25kcygpKQogICAgZWxhcHNlZF9oID0gZWxhcHNlZF9zZWNvbmRzIC8vIDM2MDAKICAgIGVsYXBzZWRfbSA9IChlbGFwc2VkX3NlY29uZHMgJSAzNjAwKSAvLyA2MAogICAgZWxhcHNlZF9zID0gZWxhcHNlZF9zZWNvbmRzICUgNjAKICAgIHByaW50KGYiXG57Jz0nKjYwfSIpCiAgICBwcmludChmIiAgSW5kZXhhw6fDo28gY29uY2x1w61kYSEiKQogICAgcHJpbnQoZiIgIEluw61jaW8gICAgICAgICAgICAgICA6IHtpbmRleF9zdGFydGVkX2F0LnN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycpfSIpCiAgICBwcmludChmIiAgRmltICAgICAgICAgICAgICAgICAgOiB7aW5kZXhfZmluaXNoZWRfYXQuc3RyZnRpbWUoJyVZLSVtLSVkICVIOiVNOiVTJyl9IikKICAgIHByaW50KGYiICBEdXJhw6fDo28gICAgICAgICAgICAgIDoge2VsYXBzZWRfaDowMmR9OntlbGFwc2VkX206MDJkfTp7ZWxhcHNlZF9zOjAyZH0iKQogICAgcHJpbnQoZiIgIEFycXVpdm9zIHZhcnJpZG9zICAgIDoge2ZpbGVzX3NjYW5uZWR9IikKICAgIHByaW50KGYiICBBcnF1aXZvcyBwcm9jZXNzYWRvcyA6IHtmaWxlc19wcm9jZXNzZWRfdG90YWx9IikKICAgIHByaW50KGYiICBUb3RhbCBkZSBjaHVua3MgICAgICA6IHt0b3RhbF9jaHVua3N9IikKICAgIHByaW50KGYiICBFcnJvcyAgICAgICAgICAgICAgICA6IHtlcnJvcnN9IikKICAgIGZvciB0YXJnZXQgaW4gdGFyZ2V0czoKICAgICAgICBjb2xsZWN0aW9uX25hbWUgPSB0YXJnZXQuY29sbGVjdGlvbl9uYW1lCiAgICAgICAgcHJpbnQoCiAgICAgICAgICAgIGYiICBDb2xlw6fDo28gQ2hyb21hREIgICAgIDogJ3tjb2xsZWN0aW9uX25hbWV9JyAiCiAgICAgICAgICAgIGYiKGVsZWfDrXZlaXM9e2ZpbGVzX2VsaWdpYmxlX2J5X2NvbGxlY3Rpb24uZ2V0KGNvbGxlY3Rpb25fbmFtZSwgMCl9LCAiCiAgICAgICAgICAgIGYiYXJxdWl2b3M9e2ZpbGVzX2J5X2NvbGxlY3Rpb24uZ2V0KGNvbGxlY3Rpb25fbmFtZSwgMCl9LCAiCiAgICAgICAgICAgIGYiY2h1bmtzPXtjaHVua3NfYnlfY29sbGVjdGlvbi5nZXQoY29sbGVjdGlvbl9uYW1lLCAwKX0pIgogICAgICAgICkKICAgIHByaW50KGYieyc9Jyo2MH1cbiIpCgoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIHRyeToKICAgICAgICBtYWluKCkKICAgIGV4Y2VwdCBNZW1vcnlFcnJvcjoKICAgICAgICBwcmludCgKICAgICAgICAgICAgIltFUlJPXSBGYWxoYSBkZSBtZW3Ds3JpYSBkdXJhbnRlIGEgaW5kZXhhw6fDo28uICIKICAgICAgICAgICAgIlVzZSAtLWVtYmVkZGluZy1tb2RlbCBiZ2Ugb3UgZXhlY3V0ZSBvIEppbmEgZW0gbcOhcXVpbmEgY29tIG1haXMgUkFNL3N3YXAuIgogICAgICAgICkKICAgICAgICBzeXMuZXhpdCgxKQo=" | base64 -d > "${EXTRACT_DIR}/indexer_full.py"
|
|
581
|
+
echo "IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCiIiIgptY3Bfc2VydmVyLnB5IOKAlCBTZXJ2aWRvciBNQ1AgcGFyYSBSQUcgbG9jYWwgZGUgY29kZWJhc2UuCgpFeHDDtWUgZmVycmFtZW50YXMgZGUgYnVzY2Egc2Vtw6JudGljYSBlIGluZGV4YcOnw6NvIHZpYSBzdGRpbyBwYXJhIG8gQ2xhdWRlIENvZGUgQ0xJLgpDb25lY3RhLXNlIGFvIENocm9tYURCIHJvZGFuZG8gZW0gRG9ja2VyIChsb2NhbGhvc3Q6ODAwMCkuCgpOb3ZpZGFkZTogbW9kbyBow61icmlkbyBlbnNlbWJsZSBjb20gZHVhcyBjb2xlw6fDtWVzIHNlcGFyYWRhcyArIFJSRiArIHJlcmFua2luZyBsZXZlLgoiIiIKCmltcG9ydCBzeXMKaW1wb3J0IG9zCmltcG9ydCBoYXNobGliCmltcG9ydCBqc29uCmltcG9ydCBsb2dnaW5nCmltcG9ydCBnZXRwYXNzCmltcG9ydCBzaHV0aWwKZnJvbSBjb2xsZWN0aW9ucy5hYmMgaW1wb3J0IEl0ZXJhdG9yCmZyb20gY29uY3VycmVudC5mdXR1cmVzIGltcG9ydCBUaHJlYWRQb29sRXhlY3V0b3IsIGFzX2NvbXBsZXRlZApmcm9tIGRhdGFjbGFzc2VzIGltcG9ydCBkYXRhY2xhc3MKZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUsIHRpbWV6b25lCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAoKIyBFdml0YSBtZW5zYWdlbnMgYWR2aXNvcnkgZG8gdHJhbnNmb3JtZXJzIGVtIHN0ZGVyciBkdXJhbnRlIGEgY2FyZ2EgZG8gbW9kZWxvLgpvcy5lbnZpcm9uLnNldGRlZmF1bHQoIlRSQU5TRk9STUVSU19OT19BRFZJU09SWV9XQVJOSU5HUyIsICIxIikKCgpjbGFzcyBfVG9yY2hEdHlwZVdhcm5pbmdGaWx0ZXIobG9nZ2luZy5GaWx0ZXIpOgogICAgZGVmIGZpbHRlcihzZWxmLCByZWNvcmQ6IGxvZ2dpbmcuTG9nUmVjb3JkKSAtPiBib29sOgogICAgICAgIHJldHVybiAiYHRvcmNoX2R0eXBlYCBpcyBkZXByZWNhdGVkISBVc2UgYGR0eXBlYCBpbnN0ZWFkISIgbm90IGluIHJlY29yZC5nZXRNZXNzYWdlKCkKCgpmb3IgX2xvZ2dlcl9uYW1lIGluICgidHJhbnNmb3JtZXJzLmNvbmZpZ3VyYXRpb25fdXRpbHMiLCAidHJhbnNmb3JtZXJzLm1vZGVsaW5nX3V0aWxzIik6CiAgICBsb2dnaW5nLmdldExvZ2dlcihfbG9nZ2VyX25hbWUpLmFkZEZpbHRlcihfVG9yY2hEdHlwZVdhcm5pbmdGaWx0ZXIoKSkKCmltcG9ydCBjaHJvbWFkYgpmcm9tIHNlbnRlbmNlX3RyYW5zZm9ybWVycyBpbXBvcnQgQ3Jvc3NFbmNvZGVyLCBTZW50ZW5jZVRyYW5zZm9ybWVyCmZyb20gbGFuZ2NoYWluX3RleHRfc3BsaXR0ZXJzIGltcG9ydCBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIKZnJvbSBtY3Auc2VydmVyLmZhc3RtY3AgaW1wb3J0IEZhc3RNQ1AKZnJvbSBkb3dubG9hZF9tb2RlbF9mcm9tX2h1Z2dpbmZhY2UgaW1wb3J0IGRvd25sb2FkX21vZGVsX3dpdGhfZmFsbGJhY2sKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQ29uZmlndXJhw6fDo28gZGUgbG9nZ2luZyAoc3RkZXJyIHBhcmEgbsOjbyBwb2x1aXIgbyBwcm90b2NvbG8gc3RkaW8pCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpsb2dnaW5nLmJhc2ljQ29uZmlnKAogICAgbGV2ZWw9bG9nZ2luZy5JTkZPLAogICAgZm9ybWF0PSJbTUNQLVJBR10gJShhc2N0aW1lKXMgJShsZXZlbG5hbWUpczogJShtZXNzYWdlKXMiLAogICAgc3RyZWFtPXN5cy5zdGRlcnIsCikKbG9nID0gbG9nZ2luZy5nZXRMb2dnZXIoX19uYW1lX18pCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIExvZyBlc3RydXR1cmFkbyBkZSB1c28gTUNQIChKU09OTCkKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCk1DUF9VU0FHRV9MT0dfUEFUSCA9IFBhdGgoCiAgICBvcy5lbnZpcm9uLmdldCgiTUNQX1VTQUdFX0xPRyIsIHN0cihQYXRoLmhvbWUoKSAvICIucmFnX2RiIiAvICJtY3BfdXNhZ2UubG9nIikpCikuZXhwYW5kdXNlcigpCgoKZGVmIF9zYWZlX3ByZXZpZXcodmFsdWU6IHN0ciwgbGltaXQ6IGludCA9IDEyMCkgLT4gc3RyOgogICAgaWYgbGVuKHZhbHVlKSA8PSBsaW1pdDoKICAgICAgICByZXR1cm4gdmFsdWUKICAgIHJldHVybiB2YWx1ZVs6bGltaXRdICsgIi4uLlt0cnVuY2F0ZWRdIgoKCmRlZiBfZ2V0X3BhcmVudF9jbWRsaW5lKCkgLT4gc3RyOgogICAgcHBpZCA9IG9zLmdldHBwaWQoKQogICAgY21kbGluZV9wYXRoID0gUGF0aChmIi9wcm9jL3twcGlkfS9jbWRsaW5lIikKICAgIHRyeToKICAgICAgICByYXcgPSBjbWRsaW5lX3BhdGgucmVhZF9ieXRlcygpCiAgICAgICAgaWYgbm90IHJhdzoKICAgICAgICAgICAgcmV0dXJuICJ1bmtub3duIgogICAgICAgIHBhcnRzID0gW3AuZGVjb2RlKCJ1dGYtOCIsIGVycm9ycz0iaWdub3JlIikgZm9yIHAgaW4gcmF3LnNwbGl0KGIiXHgwMCIpIGlmIHBdCiAgICAgICAgcmV0dXJuICIgIi5qb2luKHBhcnRzKSBpZiBwYXJ0cyBlbHNlICJ1bmtub3duIgogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICByZXR1cm4gInVua25vd24iCgoKZGVmIF9pbmZlcl9hY3RvcigpIC0+IGRpY3Rbc3RyLCBzdHJdOgogICAgYWN0b3IgPSBvcy5lbnZpcm9uLmdldCgiTUNQX0NMSUVOVF9OQU1FIikgb3Igb3MuZW52aXJvbi5nZXQoIkNMQVVERV9VU0VSIikgb3IgZ2V0cGFzcy5nZXR1c2VyKCkKICAgIHNvdXJjZSA9ICgKICAgICAgICAiTUNQX0NMSUVOVF9OQU1FIiBpZiBvcy5lbnZpcm9uLmdldCgiTUNQX0NMSUVOVF9OQU1FIikKICAgICAgICBlbHNlICJDTEFVREVfVVNFUiIgaWYgb3MuZW52aXJvbi5nZXQoIkNMQVVERV9VU0VSIikKICAgICAgICBlbHNlICJzeXN0ZW1fdXNlciIKICAgICkKICAgIHJldHVybiB7CiAgICAgICAgImFjdG9yIjogYWN0b3IsCiAgICAgICAgImFjdG9yX3NvdXJjZSI6IHNvdXJjZSwKICAgICAgICAiY2xpZW50X3Byb2Nlc3MiOiBfZ2V0X3BhcmVudF9jbWRsaW5lKCksCiAgICB9CgoKZGVmIF9sb2dfdG9vbF91c2FnZShldmVudDogc3RyLCB0b29sX25hbWU6IHN0ciwgZGV0YWlsczogZGljdFtzdHIsIG9iamVjdF0gfCBOb25lID0gTm9uZSkgLT4gTm9uZToKICAgIHRyeToKICAgICAgICBNQ1BfVVNBR0VfTE9HX1BBVEgucGFyZW50Lm1rZGlyKHBhcmVudHM9VHJ1ZSwgZXhpc3Rfb2s9VHJ1ZSkKICAgICAgICBwYXlsb2FkOiBkaWN0W3N0ciwgb2JqZWN0XSA9IHsKICAgICAgICAgICAgInRpbWVzdGFtcCI6IGRhdGV0aW1lLm5vdyh0aW1lem9uZS51dGMpLmlzb2Zvcm1hdCgpLAogICAgICAgICAgICAiZXZlbnQiOiBldmVudCwKICAgICAgICAgICAgInRvb2wiOiB0b29sX25hbWUsCiAgICAgICAgICAgICJwaWQiOiBvcy5nZXRwaWQoKSwKICAgICAgICAgICAgKipfaW5mZXJfYWN0b3IoKSwKICAgICAgICB9CiAgICAgICAgaWYgZGV0YWlsczoKICAgICAgICAgICAgcGF5bG9hZFsiZGV0YWlscyJdID0gZGV0YWlscwoKICAgICAgICB3aXRoIE1DUF9VU0FHRV9MT0dfUEFUSC5vcGVuKCJhIiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgICAgICAgICAgZi53cml0ZShqc29uLmR1bXBzKHBheWxvYWQsIGVuc3VyZV9hc2NpaT1UcnVlKSArICJcbiIpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgbG9nLndhcm5pbmcoIkZhbGhhIGFvIHJlZ2lzdHJhciB1c28gTUNQIGVtICVzOiAlcyIsIE1DUF9VU0FHRV9MT0dfUEFUSCwgZSkKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENvbmZpZ3VyYcOnw7VlcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCklOREVYRVJfQ09ORklHX1BBVEggPSBQYXRoKAogICAgb3MuZW52aXJvbi5nZXQoIk1DUF9JTkRFWEVSX0NPTkZJR19GSUxFIiwgc3RyKFBhdGguaG9tZSgpIC8gIi5yYWdfZGIiIC8gImluZGV4ZXJfdHVuaW5nLmpzb24iKSkKKS5leHBhbmR1c2VyKCkKCgpkZWYgX2xvYWRfaW5kZXhlcl90dW5pbmdfY29uZmlnKCkgLT4gZGljdFtzdHIsIG9iamVjdF06CiAgICB0cnk6CiAgICAgICAgaWYgbm90IElOREVYRVJfQ09ORklHX1BBVEguZXhpc3RzKCk6CiAgICAgICAgICAgIHJldHVybiB7fQogICAgICAgIHBheWxvYWQgPSBqc29uLmxvYWRzKElOREVYRVJfQ09ORklHX1BBVEgucmVhZF90ZXh0KGVuY29kaW5nPSJ1dGYtOCIpKQogICAgICAgIHJldHVybiBwYXlsb2FkIGlmIGlzaW5zdGFuY2UocGF5bG9hZCwgZGljdCkgZWxzZSB7fQogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICByZXR1cm4ge30KCgpJTkRFWEVSX1RVTklOR19DT05GSUcgPSBfbG9hZF9pbmRleGVyX3R1bmluZ19jb25maWcoKQoKCmRlZiBfY29uZmlnX3N0cihlbnZfbmFtZTogc3RyLCBjb25maWdfa2V5OiBzdHIsIGRlZmF1bHQ6IHN0cikgLT4gc3RyOgogICAgZW52X3JhdyA9IG9zLmVudmlyb24uZ2V0KGVudl9uYW1lKQogICAgaWYgZW52X3JhdyBpcyBub3QgTm9uZSBhbmQgZW52X3Jhdy5zdHJpcCgpOgogICAgICAgIHJldHVybiBlbnZfcmF3CiAgICBjZmdfcmF3ID0gSU5ERVhFUl9UVU5JTkdfQ09ORklHLmdldChjb25maWdfa2V5KQogICAgaWYgaXNpbnN0YW5jZShjZmdfcmF3LCBzdHIpIGFuZCBjZmdfcmF3LnN0cmlwKCk6CiAgICAgICAgcmV0dXJuIGNmZ19yYXcKICAgIHJldHVybiBkZWZhdWx0CgoKZGVmIF9jb25maWdfaW50KGVudl9uYW1lOiBzdHIsIGNvbmZpZ19rZXk6IHN0ciwgZGVmYXVsdDogaW50LCAqLCBtaW5fdmFsdWU6IGludCA9IDEpIC0+IGludDoKICAgIGVudl9yYXcgPSBvcy5lbnZpcm9uLmdldChlbnZfbmFtZSkKICAgIGlmIGVudl9yYXcgaXMgbm90IE5vbmUgYW5kIGVudl9yYXcuc3RyaXAoKToKICAgICAgICB0cnk6CiAgICAgICAgICAgIHJldHVybiBtYXgobWluX3ZhbHVlLCBpbnQoZW52X3JhdykpCiAgICAgICAgZXhjZXB0IFZhbHVlRXJyb3I6CiAgICAgICAgICAgIHBhc3MKCiAgICBjZmdfcmF3ID0gSU5ERVhFUl9UVU5JTkdfQ09ORklHLmdldChjb25maWdfa2V5KQogICAgaWYgaXNpbnN0YW5jZShjZmdfcmF3LCBpbnQpOgogICAgICAgIHJldHVybiBtYXgobWluX3ZhbHVlLCBjZmdfcmF3KQogICAgaWYgaXNpbnN0YW5jZShjZmdfcmF3LCBzdHIpOgogICAgICAgIHRyeToKICAgICAgICAgICAgcmV0dXJuIG1heChtaW5fdmFsdWUsIGludChjZmdfcmF3KSkKICAgICAgICBleGNlcHQgVmFsdWVFcnJvcjoKICAgICAgICAgICAgcGFzcwoKICAgIHJldHVybiBtYXgobWluX3ZhbHVlLCBkZWZhdWx0KQoKCkNIUk9NQV9IT1NUID0gb3MuZW52aXJvbi5nZXQoIkNIUk9NQV9IT1NUIiwgImxvY2FsaG9zdCIpCkNIUk9NQV9QT1JUID0gaW50KG9zLmVudmlyb24uZ2V0KCJDSFJPTUFfUE9SVCIsICI4MDAwIikpCgojIENvbGXDp8O1ZXMgc2VwYXJhZGFzIHBvciBlc3BlY2lhbGl6YcOnw6NvIGRlIGVtYmVkZGluZwpDT0xMRUNUSU9OX0NPREVfSklOQSA9ICJjb2RlX3ZlY3RvcnNfamluYSIKQ09MTEVDVElPTl9ET0NfQkdFID0gImRvY192ZWN0b3JzX2JnZSIKCkpJTkFfVjNfRU1CRURESU5HX01PREVMID0gImppbmFhaS9qaW5hLWVtYmVkZGluZ3MtdjMiCkpJTkFfVjJfRU1CRURESU5HX01PREVMID0gImppbmFhaS9qaW5hLWVtYmVkZGluZ3MtdjItYmFzZS1jb2RlIgpCR0VfRU1CRURESU5HX01PREVMID0gIkJBQUkvYmdlLW0zIgoKREVGQVVMVF9FTUJFRERJTkdfTU9ERUxfQ0hPSUNFID0gImppbmEiCkRFRkFVTFRfSklOQV9RVUFOVElaQVRJT04gPSAiZHluYW1pYy1pbnQ4IgpERUZBVUxUX1NFQVJDSF9NT0RFID0gInNpbmdsZSIgICMgc2luZ2xlIHwgZW5zZW1ibGUKCl9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID0gX2NvbmZpZ19zdHIoCiAgICAiTUNQX0VNQkVERElOR19NT0RFTCIsCiAgICAiZW1iZWRkaW5nX21vZGVsIiwKICAgIERFRkFVTFRfRU1CRURESU5HX01PREVMX0NIT0lDRSwKKS5zdHJpcCgpLmxvd2VyKCkKaWYgX2VtYmVkZGluZ19tb2RlbF9jaG9pY2Ugbm90IGluIHsiamluYSIsICJiZ2UiLCAiaHlicmlkIn06CiAgICBsb2cud2FybmluZygKICAgICAgICAiTUNQX0VNQkVERElOR19NT0RFTCBpbnZhbGlkbyAnJXMnLiBVc2FuZG8gJyVzJy4iLAogICAgICAgIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlLAogICAgICAgIERFRkFVTFRfRU1CRURESU5HX01PREVMX0NIT0lDRSwKICAgICkKICAgIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID0gREVGQVVMVF9FTUJFRERJTkdfTU9ERUxfQ0hPSUNFCgpfcmF3X2ppbmFfcXVhbnRpemF0aW9uID0gX2NvbmZpZ19zdHIoCiAgICAiTUNQX0pJTkFfUVVBTlRJWkFUSU9OIiwKICAgICJqaW5hX3F1YW50aXphdGlvbiIsCiAgICBERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OLAopCkpJTkFfUVVBTlRJWkFUSU9OID0gX3Jhd19qaW5hX3F1YW50aXphdGlvbi5zdHJpcCgpLmxvd2VyKCkucmVwbGFjZSgiXyIsICItIikKaWYgSklOQV9RVUFOVElaQVRJT04gbm90IGluIHsiZGVmYXVsdCIsICJkeW5hbWljLWludDgifToKICAgIGxvZy53YXJuaW5nKAogICAgICAgICJNQ1BfSklOQV9RVUFOVElaQVRJT04gaW52YWxpZG8gJyVzJy4gVXNhbmRvICclcycuIiwKICAgICAgICBKSU5BX1FVQU5USVpBVElPTiwKICAgICAgICBERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OLAogICAgKQogICAgSklOQV9RVUFOVElaQVRJT04gPSBERUZBVUxUX0pJTkFfUVVBTlRJWkFUSU9OCgpTRUFSQ0hfTU9ERV9ERUZBVUxUID0gb3MuZW52aXJvbi5nZXQoIk1DUF9TRUFSQ0hfTU9ERSIsIERFRkFVTFRfU0VBUkNIX01PREUpLnN0cmlwKCkubG93ZXIoKQppZiBTRUFSQ0hfTU9ERV9ERUZBVUxUIG5vdCBpbiB7InNpbmdsZSIsICJlbnNlbWJsZSJ9OgogICAgU0VBUkNIX01PREVfREVGQVVMVCA9IERFRkFVTFRfU0VBUkNIX01PREUKCmlmIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID09ICJoeWJyaWQiIGFuZCAiTUNQX1NFQVJDSF9NT0RFIiBub3QgaW4gb3MuZW52aXJvbjoKICAgICMgTm8gbW9kbyBow61icmlkbywgbyBjb21wb3J0YW1lbnRvIGVzcGVyYWRvIGNvc3R1bWEgc2VyIGVuc2VtYmxlIHBvciBwYWRyw6NvLgogICAgU0VBUkNIX01PREVfREVGQVVMVCA9ICJlbnNlbWJsZSIKClJFUkFOS19NT0RFTF9JRCA9IG9zLmVudmlyb24uZ2V0KCJNQ1BfUkVSQU5LX01PREVMIiwgImNyb3NzLWVuY29kZXIvbXMtbWFyY28tTWluaUxNLUwtNi12MiIpClJFUkFOS19FTkFCTEVEID0gb3MuZW52aXJvbi5nZXQoIk1DUF9SRVJBTktfRU5BQkxFRCIsICJ0cnVlIikuc3RyaXAoKS5sb3dlcigpIGluIHsiMSIsICJ0cnVlIiwgInllcyIsICJvbiJ9ClJFUkFOS19DQU5ESURBVEVfTVVMVElQTElFUiA9IGludChvcy5lbnZpcm9uLmdldCgiTUNQX1JFUkFOS19DQU5ESURBVEVfTVVMVElQTElFUiIsICIzIikpClJFUkFOS19NQVhfQ0FORElEQVRFUyA9IGludChvcy5lbnZpcm9uLmdldCgiTUNQX1JFUkFOS19NQVhfQ0FORElEQVRFUyIsICI0MCIpKQpSRVJBTktFUl9NQVhfTEVOR1RIID0gaW50KG9zLmVudmlyb24uZ2V0KCJNQ1BfUkVSQU5LX01BWF9MRU5HVEgiLCAiNTEyIikpClJFUkFOS0VSX1FVQU5USVpBVElPTiA9IG9zLmVudmlyb24uZ2V0KCJNQ1BfUkVSQU5LX1FVQU5USVpBVElPTiIsICJkeW5hbWljLWludDgiKS5zdHJpcCgpLmxvd2VyKCkKaWYgUkVSQU5LRVJfUVVBTlRJWkFUSU9OIG5vdCBpbiB7ImRlZmF1bHQiLCAiZHluYW1pYy1pbnQ4In06CiAgICBSRVJBTktFUl9RVUFOVElaQVRJT04gPSAiZHluYW1pYy1pbnQ4IgoKUlJGX0sgPSBpbnQob3MuZW52aXJvbi5nZXQoIk1DUF9SUkZfSyIsICI2MCIpKQpFTUJFRERJTkdfQkFUQ0hfU0laRSA9IF9jb25maWdfaW50KCJNQ1BfRU1CRURESU5HX0JBVENIX1NJWkUiLCAiZW1iZWRkaW5nX2JhdGNoX3NpemUiLCA0LCBtaW5fdmFsdWU9MSkKCl9lbnZfbW9kZWxfZGlyID0gb3MuZW52aXJvbi5nZXQoIk1DUF9NT0RFTF9ESVIiKQpNT0RFTF9ESVIgPSAoCiAgICBQYXRoKF9lbnZfbW9kZWxfZGlyKS5leHBhbmR1c2VyKCkKICAgIGlmIF9lbnZfbW9kZWxfZGlyCiAgICBlbHNlIFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAibXktY3VzdG9tLXJhZy1weXRob24iIC8gIm1vZGVscyIKKQoKIyBQYXLDom1ldHJvcyBkbyBzcGxpdHRlciAoYWxpbmhhZG9zIGNvbSBpbmRleGVyX2Z1bGwucHksIHBlcmZpbCBsb3ctbWVtb3J5KQpDSFVOS19TSVpFID0gX2NvbmZpZ19pbnQoIk1DUF9DSFVOS19TSVpFIiwgImNodW5rX3NpemUiLCAzMDAwLCBtaW5fdmFsdWU9MjU2KQpDSFVOS19PVkVSTEFQID0gbWluKENIVU5LX1NJWkUgLSAxLCBfY29uZmlnX2ludCgiTUNQX0NIVU5LX09WRVJMQVAiLCAiY2h1bmtfb3ZlcmxhcCIsIDQwMCwgbWluX3ZhbHVlPTApKQoKTUFYX0ZJTEVfU0laRV9CWVRFUyA9IDUwMCAqIDEwMjQgICMgNTAwIEtCClRPUF9LX1JFU1VMVFMgPSA3Ck1BWF9RVUVSWV9SRVNVTFRTID0gMzAKCiMgRmlsdHJvcyBkZSB2YXJyZWR1cmEKSUdOT1JFRF9ESVJTID0gewogICAgIi5naXQiLCAibm9kZV9tb2R1bGVzIiwgIl9fcHljYWNoZV9fIiwgIi52ZW52IiwgInZlbnYiLCAiZW52IiwKICAgICJkaXN0IiwgImJ1aWxkIiwgIm91dCIsICIubmV4dCIsICIubnV4dCIsICIuY2FjaGUiLCAiY292ZXJhZ2UiLAogICAgIi5weXRlc3RfY2FjaGUiLCAiLm15cHlfY2FjaGUiLCAiLnJ1ZmZfY2FjaGUiLCAidGFyZ2V0IiwgImJpbiIsICJvYmoiLAogICAgIi5pZGVhIiwgIi52c2NvZGUiLCAidmVuZG9yIiwgInRtcCIsICJ0ZW1wIiwgImxvZ3MiLCAiLnJhZ19kYiIsCn0KCklHTk9SRURfRVhURU5TSU9OUyA9IHsKICAgICIucG5nIiwgIi5qcGciLCAiLmpwZWciLCAiLmdpZiIsICIuc3ZnIiwgIi5pY28iLCAiLndlYnAiLCAiLmJtcCIsCiAgICAiLm1wNCIsICIubXAzIiwgIi53YXYiLCAiLm9nZyIsICIuYXZpIiwgIi5tb3YiLAogICAgIi56aXAiLCAiLnRhciIsICIuZ3oiLCAiLnJhciIsICIuN3oiLCAiLmphciIsICIud2FyIiwKICAgICIucHljIiwgIi5weW8iLCAiLnNvIiwgIi5kbGwiLCAiLmV4ZSIsICIuYmluIiwKICAgICIubG9jayIsICIuc3VtIiwgIi5zcWxpdGUiLCAiLmRiIiwgIi5zcWxpdGUzIiwKICAgICIudHRmIiwgIi53b2ZmIiwgIi53b2ZmMiIsICIuZW90IiwKICAgICIucGRmIiwgIi5kb2N4IiwgIi54bHN4IiwgIi5wcHR4IiwKfQoKQ09ERV9FWFRFTlNJT05TID0gewogICAgIi5weSIsICIuanMiLCAiLnRzIiwgIi50c3giLCAiLmpzeCIsICIuamF2YSIsICIuYyIsICIuaCIsICIuY3BwIiwgIi5ocHAiLAogICAgIi5nbyIsICIucnMiLCAiLnJiIiwgIi5waHAiLCAiLmNzIiwgIi5zd2lmdCIsICIua3QiLCAiLmt0cyIsICIuc2NhbGEiLCAiLnNxbCIsCiAgICAiLnNoIiwgIi5iYXNoIiwgIi56c2giLCAiLnBzMSIsICIueWFtbCIsICIueW1sIiwgIi50b21sIiwgIi5pbmkiLCAiLmNvbmYiLAogICAgIi5qc29uIiwgIi54bWwiLCAiLmh0bWwiLCAiLmNzcyIsICIuc2NzcyIsICIuc2FzcyIsICIudnVlIiwgIi5zdmVsdGUiLCAiLmRhcnQiLAogICAgIi5sdWEiLCAiLnIiLCAiLm0iLCAiLm1tIiwKfQoKRE9DX0VYVEVOU0lPTlMgPSB7CiAgICAiLm1kIiwgIi5tZHgiLCAiLnJzdCIsICIudHh0IiwgIi5hZG9jIiwgIi5vcmciLCAiLnRleCIsICIuY3N2IiwKfQoKCkBkYXRhY2xhc3MoZnJvemVuPVRydWUpCmNsYXNzIEJyYW5jaFNwZWM6CiAgICBrZXk6IHN0cgogICAgbW9kZWxfY2hvaWNlOiBzdHIKICAgIG1vZGVsX2lkOiBzdHIKICAgIGNvbGxlY3Rpb25fbmFtZTogc3RyCiAgICBjb250ZW50X2RvbWFpbjogc3RyCiAgICBsYWJlbDogc3RyCgoKSklOQV9DT0RFX0JSQU5DSF9NT0RFTF9DSE9JQ0UgPSAiamluYV92MiIgaWYgX2VtYmVkZGluZ19tb2RlbF9jaG9pY2UgPT0gImh5YnJpZCIgZWxzZSAiamluYSIKSklOQV9DT0RFX0JSQU5DSF9NT0RFTF9JRCA9IEpJTkFfVjJfRU1CRURESU5HX01PREVMIGlmIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID09ICJoeWJyaWQiIGVsc2UgSklOQV9WM19FTUJFRERJTkdfTU9ERUwKCkJSQU5DSF9TUEVDUzogZGljdFtzdHIsIEJyYW5jaFNwZWNdID0gewogICAgImppbmFfY29kZSI6IEJyYW5jaFNwZWMoCiAgICAgICAga2V5PSJqaW5hX2NvZGUiLAogICAgICAgIG1vZGVsX2Nob2ljZT1KSU5BX0NPREVfQlJBTkNIX01PREVMX0NIT0lDRSwKICAgICAgICBtb2RlbF9pZD1KSU5BX0NPREVfQlJBTkNIX01PREVMX0lELAogICAgICAgIGNvbGxlY3Rpb25fbmFtZT1DT0xMRUNUSU9OX0NPREVfSklOQSwKICAgICAgICBjb250ZW50X2RvbWFpbj0iY29kZSIsCiAgICAgICAgbGFiZWw9IkppbmEgdjIgQ29kZSIgaWYgX2VtYmVkZGluZ19tb2RlbF9jaG9pY2UgPT0gImh5YnJpZCIgZWxzZSAiSmluYSB2MyBDb2RlIiwKICAgICksCiAgICAiYmdlX2RvYyI6IEJyYW5jaFNwZWMoCiAgICAgICAga2V5PSJiZ2VfZG9jIiwKICAgICAgICBtb2RlbF9jaG9pY2U9ImJnZSIsCiAgICAgICAgbW9kZWxfaWQ9QkdFX0VNQkVERElOR19NT0RFTCwKICAgICAgICBjb2xsZWN0aW9uX25hbWU9Q09MTEVDVElPTl9ET0NfQkdFLAogICAgICAgIGNvbnRlbnRfZG9tYWluPSJkb2MiLAogICAgICAgIGxhYmVsPSJCR0UgRG9jcyIsCiAgICApLAp9CgpERUZBVUxUX1NJTkdMRV9CUkFOQ0hfS0VZID0gImJnZV9kb2MiIGlmIF9lbWJlZGRpbmdfbW9kZWxfY2hvaWNlID09ICJiZ2UiIGVsc2UgImppbmFfY29kZSIKCgpAZGF0YWNsYXNzCmNsYXNzIFJldHJpZXZlZEhpdDoKICAgIGtleTogc3RyCiAgICBkb2N1bWVudDogc3RyCiAgICBtZXRhZGF0YTogZGljdFtzdHIsIG9iamVjdF0KICAgIGRpc3RhbmNlOiBmbG9hdCB8IE5vbmUKICAgIHNpbWlsYXJpdHk6IGZsb2F0IHwgTm9uZQogICAgYnJhbmNoOiBCcmFuY2hTcGVjCiAgICByYW5rOiBpbnQKCgpAZGF0YWNsYXNzCmNsYXNzIEZ1c2VkSGl0OgogICAga2V5OiBzdHIKICAgIGRvY3VtZW50OiBzdHIKICAgIG1ldGFkYXRhOiBkaWN0W3N0ciwgb2JqZWN0XQogICAgcnJmX3Njb3JlOiBmbG9hdAogICAgc291cmNlX2RldGFpbHM6IGRpY3Rbc3RyLCBkaWN0W3N0ciwgb2JqZWN0XV0KICAgIHJlcmFua19zY29yZTogZmxvYXQgfCBOb25lID0gTm9uZQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgUnVudGltZSBjYWNoZXMgKGxhenkgbG9hZGluZyBwYXJhIGVjb25vbWl6YXIgUkFNKQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKX2Nocm9tYV9jbGllbnQ6IGNocm9tYWRiLkh0dHBDbGllbnQgfCBOb25lID0gTm9uZQpfY29sbGVjdGlvbnM6IGRpY3Rbc3RyLCBjaHJvbWFkYi5Db2xsZWN0aW9uXSA9IHt9Cl9tb2RlbHM6IGRpY3Rbc3RyLCBTZW50ZW5jZVRyYW5zZm9ybWVyXSA9IHt9Cl9tb2RlbF9sb2FkX2Vycm9yczogZGljdFtzdHIsIHN0cl0gPSB7fQpfc3BsaXR0ZXI6IFJlY3Vyc2l2ZUNoYXJhY3RlclRleHRTcGxpdHRlciB8IE5vbmUgPSBOb25lCl9yZXJhbmtlcjogQ3Jvc3NFbmNvZGVyIHwgTm9uZSA9IE5vbmUKX3JlcmFua2VyX2Vycm9yOiBzdHIgfCBOb25lID0gTm9uZQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQ2hyb21hIGUgbW9kZWxvcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCmRlZiBfbW9kZWxfY2FjaGVfZGlyKGJhc2VfZGlyOiBQYXRoLCBtb2RlbF9pZDogc3RyKSAtPiBQYXRoOgogICAgc2FmZV9uYW1lID0gbW9kZWxfaWQucmVwbGFjZSgiLyIsICJfXyIpLnJlcGxhY2UoIjoiLCAiXyIpCiAgICByZXR1cm4gYmFzZV9kaXIgLyBzYWZlX25hbWUKCgpkZWYgX2dldF9jaHJvbWFfY2xpZW50KCkgLT4gY2hyb21hZGIuSHR0cENsaWVudDoKICAgIGdsb2JhbCBfY2hyb21hX2NsaWVudAogICAgaWYgX2Nocm9tYV9jbGllbnQgaXMgTm9uZToKICAgICAgICBfY2hyb21hX2NsaWVudCA9IGNocm9tYWRiLkh0dHBDbGllbnQoaG9zdD1DSFJPTUFfSE9TVCwgcG9ydD1DSFJPTUFfUE9SVCkKICAgICAgICBfY2hyb21hX2NsaWVudC5oZWFydGJlYXQoKQogICAgICAgIGxvZy5pbmZvKCJDb25lY3RhZG8gYW8gQ2hyb21hREIgZW0gJXM6JXMiLCBDSFJPTUFfSE9TVCwgQ0hST01BX1BPUlQpCiAgICByZXR1cm4gX2Nocm9tYV9jbGllbnQKCgpkZWYgZ2V0X2Nocm9tYV9jb2xsZWN0aW9uKGNvbGxlY3Rpb25fbmFtZTogc3RyKSAtPiBjaHJvbWFkYi5Db2xsZWN0aW9uOgogICAgaWYgY29sbGVjdGlvbl9uYW1lIGluIF9jb2xsZWN0aW9uczoKICAgICAgICByZXR1cm4gX2NvbGxlY3Rpb25zW2NvbGxlY3Rpb25fbmFtZV0KCiAgICB0cnk6CiAgICAgICAgY2xpZW50ID0gX2dldF9jaHJvbWFfY2xpZW50KCkKICAgICAgICBjb2xsZWN0aW9uID0gY2xpZW50LmdldF9vcl9jcmVhdGVfY29sbGVjdGlvbigKICAgICAgICAgICAgbmFtZT1jb2xsZWN0aW9uX25hbWUsCiAgICAgICAgICAgIG1ldGFkYXRhPXsiaG5zdzpzcGFjZSI6ICJjb3NpbmUifSwKICAgICAgICApCiAgICAgICAgX2NvbGxlY3Rpb25zW2NvbGxlY3Rpb25fbmFtZV0gPSBjb2xsZWN0aW9uCiAgICAgICAgcmV0dXJuIGNvbGxlY3Rpb24KICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICByYWlzZSBSdW50aW1lRXJyb3IoCiAgICAgICAgICAgIGYiTsOjbyBmb2kgcG9zc8OtdmVsIGFjZXNzYXIgYSBjb2xlw6fDo28gJ3tjb2xsZWN0aW9uX25hbWV9JyBubyBDaHJvbWFEQiAiCiAgICAgICAgICAgIGYiKHtDSFJPTUFfSE9TVH06e0NIUk9NQV9QT1JUfSkuIEVycm86IHtlfSIKICAgICAgICApCgoKZGVmIF9sb2FkX3NlbnRlbmNlX3RyYW5zZm9ybWVyX2Zyb21fbG9jYWwobW9kZWxfaWQ6IHN0ciwgbG9jYWxfbW9kZWxfZGlyOiBQYXRoKSAtPiBTZW50ZW5jZVRyYW5zZm9ybWVyOgogICAgdHJ1c3RfcmVtb3RlX2NvZGUgPSBtb2RlbF9pZC5zdGFydHN3aXRoKCJqaW5hYWkvIikKICAgIHRva2VuaXplcl9rd2FyZ3MgPSB7ImZpeF9taXN0cmFsX3JlZ2V4IjogVHJ1ZX0KCiAgICBkZWYgX2luc3RhbnRpYXRlX21vZGVsKCkgLT4gU2VudGVuY2VUcmFuc2Zvcm1lcjoKICAgICAgICByZXR1cm4gU2VudGVuY2VUcmFuc2Zvcm1lcigKICAgICAgICAgICAgc3RyKGxvY2FsX21vZGVsX2RpciksCiAgICAgICAgICAgIGRldmljZT0iY3B1IiwKICAgICAgICAgICAgdHJ1c3RfcmVtb3RlX2NvZGU9dHJ1c3RfcmVtb3RlX2NvZGUsCiAgICAgICAgICAgIHRva2VuaXplcl9rd2FyZ3M9dG9rZW5pemVyX2t3YXJncywKICAgICAgICApCgogICAgZGVmIF9jbGVhcl9oZl9keW5hbWljX21vZHVsZXNfY2FjaGUoKSAtPiBOb25lOgogICAgICAgIGNhY2hlX2RpciA9IFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAiaHVnZ2luZ2ZhY2UiIC8gIm1vZHVsZXMiIC8gInRyYW5zZm9ybWVyc19tb2R1bGVzIgogICAgICAgIGlmIGNhY2hlX2Rpci5leGlzdHMoKToKICAgICAgICAgICAgbG9nLndhcm5pbmcoIkxpbXBhbmRvIGNhY2hlIGRpbsOibWljbyBkbyBIdWdnaW5nIEZhY2UgZW0gJXMiLCBjYWNoZV9kaXIpCiAgICAgICAgICAgIHNodXRpbC5ybXRyZWUoY2FjaGVfZGlyLCBpZ25vcmVfZXJyb3JzPVRydWUpCgogICAgZGVmIF9sb2FkX3dpdGhfamluYV9wYXRjaCgpIC0+IFNlbnRlbmNlVHJhbnNmb3JtZXI6CiAgICAgICAgaWYgbm90IHRydXN0X3JlbW90ZV9jb2RlOgogICAgICAgICAgICByZXR1cm4gX2luc3RhbnRpYXRlX21vZGVsKCkKCiAgICAgICAgZnJvbSB0cmFuc2Zvcm1lcnMgaW1wb3J0IEF1dG9Nb2RlbCwgQXV0b1Rva2VuaXplcgogICAgICAgIGZyb20gdHJhbnNmb3JtZXJzLm1vZGVsaW5nX3V0aWxzIGltcG9ydCBQcmVUcmFpbmVkTW9kZWwKCiAgICAgICAgb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkID0gQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWQKICAgICAgICBvcmlnaW5hbF9tb2RlbF9mcm9tX3ByZXRyYWluZWQgPSBBdXRvTW9kZWwuZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQgPSBQcmVUcmFpbmVkTW9kZWwuZnJvbV9wcmV0cmFpbmVkCiAgICAgICAgb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZyA9IFByZVRyYWluZWRNb2RlbC5fZnJvbV9jb25maWcKICAgICAgICBtb2RlbF9yZWZzID0ge3N0cihsb2NhbF9tb2RlbF9kaXIpLCBzdHIobG9jYWxfbW9kZWxfZGlyLnJlc29sdmUoKSl9CgogICAgICAgIGRlZiBfcGF0Y2hlZF9mcm9tX3ByZXRyYWluZWQoKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICAgICAgbW9kZWxfcmVmID0gYXJnc1swXSBpZiBhcmdzIGVsc2Uga3dhcmdzLmdldCgicHJldHJhaW5lZF9tb2RlbF9uYW1lX29yX3BhdGgiKQogICAgICAgICAgICBpZiBtb2RlbF9yZWYgaXMgbm90IE5vbmUgYW5kIHN0cihtb2RlbF9yZWYpIGluIG1vZGVsX3JlZnM6CiAgICAgICAgICAgICAgICBrd2FyZ3Muc2V0ZGVmYXVsdCgiZml4X21pc3RyYWxfcmVnZXgiLCBUcnVlKQogICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxfZnJvbV9wcmV0cmFpbmVkKCphcmdzLCAqKmt3YXJncykKCiAgICAgICAgZGVmIF9wYXRjaGVkX21vZGVsX2Zyb21fcHJldHJhaW5lZCgqYXJncywgKiprd2FyZ3MpOgogICAgICAgICAgICBtb2RlbF9yZWYgPSBhcmdzWzBdIGlmIGFyZ3MgZWxzZSBrd2FyZ3MuZ2V0KCJwcmV0cmFpbmVkX21vZGVsX25hbWVfb3JfcGF0aCIpCiAgICAgICAgICAgIGlmIG1vZGVsX3JlZiBpcyBub3QgTm9uZSBhbmQgc3RyKG1vZGVsX3JlZikgaW4gbW9kZWxfcmVmcyBhbmQgInRvcmNoX2R0eXBlIiBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICBrd2FyZ3MgPSBkaWN0KGt3YXJncykKICAgICAgICAgICAgICAgIGlmICJkdHlwZSIgbm90IGluIGt3YXJnczoKICAgICAgICAgICAgICAgICAgICBrd2FyZ3NbImR0eXBlIl0gPSBrd2FyZ3NbInRvcmNoX2R0eXBlIl0KICAgICAgICAgICAgICAgIGt3YXJncy5wb3AoInRvcmNoX2R0eXBlIiwgTm9uZSkKICAgICAgICAgICAgcmV0dXJuIG9yaWdpbmFsX21vZGVsX2Zyb21fcHJldHJhaW5lZCgqYXJncywgKiprd2FyZ3MpCgogICAgICAgIG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkX2ZuID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQuX19mdW5jX18KCiAgICAgICAgQGNsYXNzbWV0aG9kCiAgICAgICAgZGVmIF9wYXRjaGVkX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkKGNscywgKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICAgICAgaWYgInRvcmNoX2R0eXBlIiBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICBrd2FyZ3MgPSBkaWN0KGt3YXJncykKICAgICAgICAgICAgICAgIGlmICJkdHlwZSIgbm90IGluIGt3YXJnczoKICAgICAgICAgICAgICAgICAgICBrd2FyZ3NbImR0eXBlIl0gPSBrd2FyZ3NbInRvcmNoX2R0eXBlIl0KICAgICAgICAgICAgICAgIGt3YXJncy5wb3AoInRvcmNoX2R0eXBlIiwgTm9uZSkKICAgICAgICAgICAgcmV0dXJuIG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9wcmV0cmFpbmVkX2ZuKGNscywgKmFyZ3MsICoqa3dhcmdzKQoKICAgICAgICBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnX2ZuID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZy5fX2Z1bmNfXwoKICAgICAgICBAY2xhc3NtZXRob2QKICAgICAgICBkZWYgX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZyhjbHMsICphcmdzLCAqKmt3YXJncyk6CiAgICAgICAgICAgIGlmICJ0b3JjaF9kdHlwZSIgaW4ga3dhcmdzOgogICAgICAgICAgICAgICAga3dhcmdzID0gZGljdChrd2FyZ3MpCiAgICAgICAgICAgICAgICBpZiAiZHR5cGUiIG5vdCBpbiBrd2FyZ3M6CiAgICAgICAgICAgICAgICAgICAga3dhcmdzWyJkdHlwZSJdID0ga3dhcmdzWyJ0b3JjaF9kdHlwZSJdCiAgICAgICAgICAgICAgICBrd2FyZ3MucG9wKCJ0b3JjaF9kdHlwZSIsIE5vbmUpCiAgICAgICAgICAgIHJldHVybiBvcmlnaW5hbF9wcmV0cmFpbmVkX21vZGVsX2Zyb21fY29uZmlnX2ZuKGNscywgKmFyZ3MsICoqa3dhcmdzKQoKICAgICAgICBBdXRvVG9rZW5pemVyLmZyb21fcHJldHJhaW5lZCA9IF9wYXRjaGVkX2Zyb21fcHJldHJhaW5lZAogICAgICAgIEF1dG9Nb2RlbC5mcm9tX3ByZXRyYWluZWQgPSBfcGF0Y2hlZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICBQcmVUcmFpbmVkTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICBQcmVUcmFpbmVkTW9kZWwuX2Zyb21fY29uZmlnID0gX3BhdGNoZWRfcHJldHJhaW5lZF9tb2RlbF9mcm9tX2NvbmZpZwogICAgICAgIHRyeToKICAgICAgICAgICAgcmV0dXJuIF9pbnN0YW50aWF0ZV9tb2RlbCgpCiAgICAgICAgZmluYWxseToKICAgICAgICAgICAgQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWQgPSBvcmlnaW5hbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgQXV0b01vZGVsLmZyb21fcHJldHJhaW5lZCA9IG9yaWdpbmFsX21vZGVsX2Zyb21fcHJldHJhaW5lZAogICAgICAgICAgICBQcmVUcmFpbmVkTW9kZWwuZnJvbV9wcmV0cmFpbmVkID0gb3JpZ2luYWxfcHJldHJhaW5lZF9tb2RlbF9mcm9tX3ByZXRyYWluZWQKICAgICAgICAgICAgUHJlVHJhaW5lZE1vZGVsLl9mcm9tX2NvbmZpZyA9IG9yaWdpbmFsX3ByZXRyYWluZWRfbW9kZWxfZnJvbV9jb25maWcKCiAgICB0cnk6CiAgICAgICAgcmV0dXJuIF9sb2FkX3dpdGhfamluYV9wYXRjaCgpCiAgICBleGNlcHQgRmlsZU5vdEZvdW5kRXJyb3IgYXMgZToKICAgICAgICBpZiB0cnVzdF9yZW1vdGVfY29kZSBhbmQgInRyYW5zZm9ybWVyc19tb2R1bGVzIiBpbiBzdHIoZSk6CiAgICAgICAgICAgIGxvZy53YXJuaW5nKCJDYWNoZSBkaW7Dom1pY28gaW5jb25zaXN0ZW50ZSBkZXRlY3RhZG86ICVzIiwgZSkKICAgICAgICAgICAgX2NsZWFyX2hmX2R5bmFtaWNfbW9kdWxlc19jYWNoZSgpCiAgICAgICAgICAgIHJldHVybiBfbG9hZF93aXRoX2ppbmFfcGF0Y2goKQogICAgICAgIHJhaXNlCgoKZGVmIF9hcHBseV9qaW5hX3F1YW50aXphdGlvbl9pZl9uZWVkZWQobW9kZWw6IFNlbnRlbmNlVHJhbnNmb3JtZXIsIG1vZGVsX2lkOiBzdHIpIC0+IFNlbnRlbmNlVHJhbnNmb3JtZXI6CiAgICBpZiBtb2RlbF9pZCAhPSBKSU5BX1YzX0VNQkVERElOR19NT0RFTCBvciBKSU5BX1FVQU5USVpBVElPTiA9PSAiZGVmYXVsdCI6CiAgICAgICAgcmV0dXJuIG1vZGVsCgogICAgdHJ5OgogICAgICAgIGltcG9ydCB0b3JjaAogICAgICAgIGltcG9ydCB3YXJuaW5ncwoKICAgICAgICBxdWFudGl6ZWRfbGF5ZXJzID0gMAogICAgICAgIGZvciBtb2R1bGUgaW4gbW9kZWwubW9kdWxlcygpOgogICAgICAgICAgICBpZiB0eXBlKG1vZHVsZSkuX19uYW1lX18gIT0gIlBhcmFtZXRyaXplZExpbmVhciI6CiAgICAgICAgICAgICAgICBjb250aW51ZQoKICAgICAgICAgICAgZmxvYXRfbGluZWFyID0gdG9yY2gubm4uTGluZWFyKAogICAgICAgICAgICAgICAgbW9kdWxlLmluX2ZlYXR1cmVzLAogICAgICAgICAgICAgICAgbW9kdWxlLm91dF9mZWF0dXJlcywKICAgICAgICAgICAgICAgIGJpYXM9bW9kdWxlLmJpYXMgaXMgbm90IE5vbmUsCiAgICAgICAgICAgICkKICAgICAgICAgICAgd2l0aCB0b3JjaC5ub19ncmFkKCk6CiAgICAgICAgICAgICAgICBmbG9hdF9saW5lYXIud2VpZ2h0LmNvcHlfKG1vZHVsZS53ZWlnaHQuZGV0YWNoKCkudG8odG9yY2guZmxvYXQzMikpCiAgICAgICAgICAgICAgICBpZiBtb2R1bGUuYmlhcyBpcyBub3QgTm9uZToKICAgICAgICAgICAgICAgICAgICBmbG9hdF9saW5lYXIuYmlhcy5jb3B5Xyhtb2R1bGUuYmlhcy5kZXRhY2goKS50byh0b3JjaC5mbG9hdDMyKSkKCiAgICAgICAgICAgIHdpdGggd2FybmluZ3MuY2F0Y2hfd2FybmluZ3MoKToKICAgICAgICAgICAgICAgIHdhcm5pbmdzLmZpbHRlcndhcm5pbmdzKCJpZ25vcmUiLCBjYXRlZ29yeT1EZXByZWNhdGlvbldhcm5pbmcpCiAgICAgICAgICAgICAgICBxdWFudGl6ZWRfbGluZWFyID0gdG9yY2gucXVhbnRpemF0aW9uLnF1YW50aXplX2R5bmFtaWMoCiAgICAgICAgICAgICAgICAgICAgdG9yY2gubm4uU2VxdWVudGlhbChmbG9hdF9saW5lYXIpLAogICAgICAgICAgICAgICAgICAgIHt0b3JjaC5ubi5MaW5lYXJ9LAogICAgICAgICAgICAgICAgICAgIGR0eXBlPXRvcmNoLnFpbnQ4LAogICAgICAgICAgICAgICAgKVswXQoKICAgICAgICAgICAgbW9kdWxlLl9keW5hbWljX2ludDhfbGluZWFyID0gcXVhbnRpemVkX2xpbmVhcgoKICAgICAgICAgICAgZGVmIF9mb3J3YXJkX2R5bmFtaWNfaW50OChzZWxmLCBpbnB1dCwgdGFza19pZD1Ob25lLCByZXNpZHVhbD1GYWxzZSk6CiAgICAgICAgICAgICAgICBvdXQgPSBzZWxmLl9keW5hbWljX2ludDhfbGluZWFyKGlucHV0KQogICAgICAgICAgICAgICAgaWYgcmVzaWR1YWw6CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG91dCwgaW5wdXQKICAgICAgICAgICAgICAgIHJldHVybiBvdXQKCiAgICAgICAgICAgIG1vZHVsZS5mb3J3YXJkID0gX2ZvcndhcmRfZHluYW1pY19pbnQ4Ll9fZ2V0X18obW9kdWxlLCBtb2R1bGUuX19jbGFzc19fKQogICAgICAgICAgICBxdWFudGl6ZWRfbGF5ZXJzICs9IDEKCiAgICAgICAgaWYgcXVhbnRpemVkX2xheWVycyA9PSAwOgogICAgICAgICAgICBsb2cud2FybmluZygiTmVuaHVtYSBjYW1hZGEgUGFyYW1ldHJpemVkTGluZWFyIGVuY29udHJhZGEgcGFyYSBkeW5hbWljLWludDggbm8gSmluYS4iKQogICAgICAgICAgICByZXR1cm4gbW9kZWwKCiAgICAgICAgbG9nLmluZm8oIlF1YW50aXphY2FvIEppbmEgYXBsaWNhZGE6IGR5bmFtaWMtaW50OCAoQ1BVLCAlcyBjYW1hZGFzKS4iLCBxdWFudGl6ZWRfbGF5ZXJzKQogICAgICAgIHJldHVybiBtb2RlbAogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBxdWFudF9lcnJvcjoKICAgICAgICBsb2cud2FybmluZygiRmFsaGEgYW8gYXBsaWNhciBkeW5hbWljLWludDggbm8gSmluYSAoJXMpOyB1c2FuZG8gbW9kZWxvIHBhZHJhby4iLCBxdWFudF9lcnJvcikKICAgICAgICByZXR1cm4gbW9kZWwKCgpkZWYgZ2V0X2VtYmVkZGluZ19tb2RlbChtb2RlbF9jaG9pY2U6IHN0cikgLT4gU2VudGVuY2VUcmFuc2Zvcm1lcjoKICAgIGlmIG1vZGVsX2Nob2ljZSBpbiBfbW9kZWxzOgogICAgICAgIHJldHVybiBfbW9kZWxzW21vZGVsX2Nob2ljZV0KCiAgICBpZiBtb2RlbF9jaG9pY2UgaW4gX21vZGVsX2xvYWRfZXJyb3JzOgogICAgICAgIHJhaXNlIFJ1bnRpbWVFcnJvcihfbW9kZWxfbG9hZF9lcnJvcnNbbW9kZWxfY2hvaWNlXSkKCiAgICBpZiBtb2RlbF9jaG9pY2UgPT0gImppbmEiOgogICAgICAgIG1vZGVsX2lkID0gSklOQV9WM19FTUJFRERJTkdfTU9ERUwKICAgIGVsaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hX3YyIjoKICAgICAgICBtb2RlbF9pZCA9IEpJTkFfVjJfRU1CRURESU5HX01PREVMCiAgICBlbGlmIG1vZGVsX2Nob2ljZSA9PSAiYmdlIjoKICAgICAgICBtb2RlbF9pZCA9IEJHRV9FTUJFRERJTkdfTU9ERUwKICAgIGVsc2U6CiAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKGYiTW9kZWxvIG7Do28gc3Vwb3J0YWRvOiB7bW9kZWxfY2hvaWNlfSIpCgogICAgdHJ5OgogICAgICAgIE1PREVMX0RJUi5ta2RpcihwYXJlbnRzPVRydWUsIGV4aXN0X29rPVRydWUpCiAgICAgICAgcHJlZmVycmVkX21vZGVsX2NhY2hlX2RpciA9IF9tb2RlbF9jYWNoZV9kaXIoTU9ERUxfRElSLCBtb2RlbF9pZCkKICAgICAgICBsb2cuaW5mbygiQ2FycmVnYW5kbyBlbWJlZGRpbmdzICclcycgZW0gQ1BVIChjYWNoZTogJXMpIiwgbW9kZWxfaWQsIHByZWZlcnJlZF9tb2RlbF9jYWNoZV9kaXIpCgogICAgICAgIHNlbGVjdGlvbiA9IGRvd25sb2FkX21vZGVsX3dpdGhfZmFsbGJhY2soCiAgICAgICAgICAgIHByZWZlcnJlZF9tb2RlbF9pZD1tb2RlbF9pZCwKICAgICAgICAgICAgZmFsbGJhY2tfbW9kZWxfaWQ9bW9kZWxfaWQsCiAgICAgICAgICAgIGxvY2FsX2Rpcj1NT0RFTF9ESVIsCiAgICAgICAgKQogICAgICAgIG1vZGVsID0gX2xvYWRfc2VudGVuY2VfdHJhbnNmb3JtZXJfZnJvbV9sb2NhbChzZWxlY3Rpb24ubW9kZWxfaWQsIHNlbGVjdGlvbi5sb2NhbF9kaXIpCiAgICAgICAgaWYgbW9kZWxfY2hvaWNlID09ICJqaW5hIjoKICAgICAgICAgICAgbW9kZWwgPSBfYXBwbHlfamluYV9xdWFudGl6YXRpb25faWZfbmVlZGVkKG1vZGVsLCBzZWxlY3Rpb24ubW9kZWxfaWQpCgogICAgICAgIF9tb2RlbHNbbW9kZWxfY2hvaWNlXSA9IG1vZGVsCiAgICAgICAgbG9nLmluZm8oCiAgICAgICAgICAgICJNb2RlbG8gZGUgZW1iZWRkaW5ncyBwcm9udG86ICVzIChwcm92aWRlcj0lcywgcGF0aD0lcykiLAogICAgICAgICAgICBzZWxlY3Rpb24ubW9kZWxfaWQsCiAgICAgICAgICAgIHNlbGVjdGlvbi5wcm92aWRlciwKICAgICAgICAgICAgc2VsZWN0aW9uLmxvY2FsX2RpciwKICAgICAgICApCiAgICAgICAgcmV0dXJuIG1vZGVsCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgbWVzc2FnZSA9IGYiRmFsaGEgYW8gY2FycmVnYXIgbW9kZWxvICd7bW9kZWxfY2hvaWNlfScgKHttb2RlbF9pZH0pOiB7ZX0iCiAgICAgICAgX21vZGVsX2xvYWRfZXJyb3JzW21vZGVsX2Nob2ljZV0gPSBtZXNzYWdlCiAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKG1lc3NhZ2UpCgoKZGVmIGdldF9yZXJhbmtlcigpIC0+IENyb3NzRW5jb2RlciB8IE5vbmU6CiAgICBnbG9iYWwgX3JlcmFua2VyLCBfcmVyYW5rZXJfZXJyb3IKCiAgICBpZiBub3QgUkVSQU5LX0VOQUJMRUQ6CiAgICAgICAgcmV0dXJuIE5vbmUKICAgIGlmIF9yZXJhbmtlciBpcyBub3QgTm9uZToKICAgICAgICByZXR1cm4gX3JlcmFua2VyCiAgICBpZiBfcmVyYW5rZXJfZXJyb3IgaXMgbm90IE5vbmU6CiAgICAgICAgcmV0dXJuIE5vbmUKCiAgICB0cnk6CiAgICAgICAgTU9ERUxfRElSLm1rZGlyKHBhcmVudHM9VHJ1ZSwgZXhpc3Rfb2s9VHJ1ZSkKICAgICAgICBzZWxlY3Rpb24gPSBkb3dubG9hZF9tb2RlbF93aXRoX2ZhbGxiYWNrKAogICAgICAgICAgICBwcmVmZXJyZWRfbW9kZWxfaWQ9UkVSQU5LX01PREVMX0lELAogICAgICAgICAgICBmYWxsYmFja19tb2RlbF9pZD1SRVJBTktfTU9ERUxfSUQsCiAgICAgICAgICAgIGxvY2FsX2Rpcj1NT0RFTF9ESVIsCiAgICAgICAgKQoKICAgICAgICByZXJhbmtlciA9IENyb3NzRW5jb2RlcigKICAgICAgICAgICAgc3RyKHNlbGVjdGlvbi5sb2NhbF9kaXIpLAogICAgICAgICAgICBkZXZpY2U9ImNwdSIsCiAgICAgICAgICAgIG1heF9sZW5ndGg9UkVSQU5LRVJfTUFYX0xFTkdUSCwKICAgICAgICAgICAgdHJ1c3RfcmVtb3RlX2NvZGU9RmFsc2UsCiAgICAgICAgKQoKICAgICAgICBpZiBSRVJBTktFUl9RVUFOVElaQVRJT04gPT0gImR5bmFtaWMtaW50OCI6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGltcG9ydCB0b3JjaAoKICAgICAgICAgICAgICAgIHJlcmFua2VyLm1vZGVsID0gdG9yY2gucXVhbnRpemF0aW9uLnF1YW50aXplX2R5bmFtaWMoCiAgICAgICAgICAgICAgICAgICAgcmVyYW5rZXIubW9kZWwsCiAgICAgICAgICAgICAgICAgICAge3RvcmNoLm5uLkxpbmVhcn0sCiAgICAgICAgICAgICAgICAgICAgZHR5cGU9dG9yY2gucWludDgsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBsb2cuaW5mbygiUmVyYW5rZXIgY29tIHF1YW50aXphY2FvIGR5bmFtaWMtaW50OCBoYWJpbGl0YWRhLiIpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgcXVhbnRfZXJyb3I6CiAgICAgICAgICAgICAgICBsb2cud2FybmluZygiRmFsaGEgYW8gcXVhbnRpemFyIHJlcmFua2VyICglcykuIFNlZ3VpbmRvIHNlbSBxdWFudGl6YWNhby4iLCBxdWFudF9lcnJvcikKCiAgICAgICAgX3JlcmFua2VyID0gcmVyYW5rZXIKICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgIlJlcmFua2VyIHByb250bzogJXMgKHByb3ZpZGVyPSVzLCBwYXRoPSVzKSIsCiAgICAgICAgICAgIHNlbGVjdGlvbi5tb2RlbF9pZCwKICAgICAgICAgICAgc2VsZWN0aW9uLnByb3ZpZGVyLAogICAgICAgICAgICBzZWxlY3Rpb24ubG9jYWxfZGlyLAogICAgICAgICkKICAgICAgICByZXR1cm4gX3JlcmFua2VyCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgX3JlcmFua2VyX2Vycm9yID0gc3RyKGUpCiAgICAgICAgbG9nLndhcm5pbmcoIlJlcmFua2VyIGluZGlzcG9uw612ZWwuIEJ1c2NhIHNlZ3VpcsOhIHNlbSByZXJhbmtpbmcuIEVycm86ICVzIiwgZSkKICAgICAgICByZXR1cm4gTm9uZQoKCmRlZiBnZXRfc3BsaXR0ZXIoKSAtPiBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXI6CiAgICBnbG9iYWwgX3NwbGl0dGVyCiAgICBpZiBfc3BsaXR0ZXIgaXMgTm9uZToKICAgICAgICBfc3BsaXR0ZXIgPSBSZWN1cnNpdmVDaGFyYWN0ZXJUZXh0U3BsaXR0ZXIoCiAgICAgICAgICAgIGNodW5rX3NpemU9Q0hVTktfU0laRSwKICAgICAgICAgICAgY2h1bmtfb3ZlcmxhcD1DSFVOS19PVkVSTEFQLAogICAgICAgICAgICBsZW5ndGhfZnVuY3Rpb249bGVuLAogICAgICAgICAgICBzZXBhcmF0b3JzPVsiXG5cbiIsICJcbiIsICIgIiwgIiJdLAogICAgICAgICkKICAgIHJldHVybiBfc3BsaXR0ZXIKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEluZGV4YcOnw6NvIGludGVybmEKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpkZWYgX21ha2VfY2h1bmtfaWQoZmlsZV9wYXRoOiBzdHIsIGNodW5rX2luZGV4OiBpbnQpIC0+IHN0cjoKICAgIHJhdyA9IGYie2ZpbGVfcGF0aH06OmNodW5rOjp7Y2h1bmtfaW5kZXh9IgogICAgcmV0dXJuIGhhc2hsaWIubWQ1KHJhdy5lbmNvZGUoKSkuaGV4ZGlnZXN0KCkKCgpkZWYgX21ha2VfcmVzdWx0X2tleShtZXRhZGF0YTogZGljdFtzdHIsIG9iamVjdF0sIGZhbGxiYWNrX2lkOiBzdHIpIC0+IHN0cjoKICAgIGZpbGVfcGF0aCA9IHN0cihtZXRhZGF0YS5nZXQoImZpbGVfcGF0aCIsICIiKSkKICAgIGNodW5rX2luZGV4ID0gc3RyKG1ldGFkYXRhLmdldCgiY2h1bmtfaW5kZXgiLCAiIikpCiAgICBpZiBmaWxlX3BhdGggYW5kIGNodW5rX2luZGV4OgogICAgICAgIHJldHVybiBmIntmaWxlX3BhdGh9OjpjaHVuazo6e2NodW5rX2luZGV4fSIKICAgIHJldHVybiBmYWxsYmFja19pZAoKCmRlZiBfZGVsZXRlX2ZpbGVfY2h1bmtzKGNvbGxlY3Rpb246IGNocm9tYWRiLkNvbGxlY3Rpb24sIGZpbGVfcGF0aDogc3RyKSAtPiBpbnQ6CiAgICAjIFBlZGUgYXBlbmFzIElEcyBwYXJhIG7Do28gbWF0ZXJpYWxpemFyIGRvY3VtZW50b3MvbWV0YWRhdGEgZGVzbmVjZXNzw6FyaW9zIGVtIFJBTS4KICAgIHJlc3VsdHMgPSBjb2xsZWN0aW9uLmdldCh3aGVyZT17ImZpbGVfcGF0aCI6IGZpbGVfcGF0aH0sIGluY2x1ZGU9W10pCiAgICBpZHMgPSByZXN1bHRzLmdldCgiaWRzIiwgW10pIGlmIHJlc3VsdHMgZWxzZSBbXQogICAgaWYgaWRzOgogICAgICAgIGNvbGxlY3Rpb24uZGVsZXRlKGlkcz1pZHMpCiAgICByZXR1cm4gbGVuKGlkcykKCgpkZWYgX3JlYWRfZmlsZV9zYWZlKGZpbGVwYXRoOiBQYXRoKSAtPiBzdHIgfCBOb25lOgogICAgZm9yIGVuY29kaW5nIGluICgidXRmLTgiLCAibGF0aW4tMSIsICJjcDEyNTIiKToKICAgICAgICB0cnk6CiAgICAgICAgICAgIHJldHVybiBmaWxlcGF0aC5yZWFkX3RleHQoZW5jb2Rpbmc9ZW5jb2RpbmcpCiAgICAgICAgZXhjZXB0IFVuaWNvZGVEZWNvZGVFcnJvcjoKICAgICAgICAgICAgY29udGludWUKICAgICAgICBleGNlcHQgT1NFcnJvcjoKICAgICAgICAgICAgcmV0dXJuIE5vbmUKICAgIHJldHVybiBOb25lCgoKZGVmIF9zY2FuX2ZvbGRlcihmb2xkZXJfcGF0aDogUGF0aCkgLT4gSXRlcmF0b3JbUGF0aF06CiAgICBmb3IgZGlycGF0aCwgZGlybmFtZXMsIGZpbGVuYW1lcyBpbiBvcy53YWxrKGZvbGRlcl9wYXRoKToKICAgICAgICBkaXJuYW1lc1s6XSA9IFsKICAgICAgICAgICAgZCBmb3IgZCBpbiBkaXJuYW1lcwogICAgICAgICAgICBpZiBkIG5vdCBpbiBJR05PUkVEX0RJUlMgYW5kIG5vdCBkLnN0YXJ0c3dpdGgoIi4iKQogICAgICAgIF0KICAgICAgICBkaXJuYW1lcy5zb3J0KCkKICAgICAgICBmb3IgZmlsZW5hbWUgaW4gc29ydGVkKGZpbGVuYW1lcyk6CiAgICAgICAgICAgIGZwID0gUGF0aChkaXJwYXRoKSAvIGZpbGVuYW1lCiAgICAgICAgICAgIGlmIGZwLnN1ZmZpeC5sb3dlcigpIGluIElHTk9SRURfRVhURU5TSU9OUzoKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGlmIGZwLnN0YXQoKS5zdF9zaXplID4gTUFYX0ZJTEVfU0laRV9CWVRFUzoKICAgICAgICAgICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICBleGNlcHQgT1NFcnJvcjoKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIHlpZWxkIGZwCgoKZGVmIF9jbGFzc2lmeV9maWxlX3RhcmdldHMoZmlsZXBhdGg6IFBhdGgpIC0+IGxpc3RbQnJhbmNoU3BlY106CiAgICBzdWZmaXggPSBmaWxlcGF0aC5zdWZmaXgubG93ZXIoKQogICAgaXNfY29kZSA9IHN1ZmZpeCBpbiBDT0RFX0VYVEVOU0lPTlMKICAgIGlzX2RvYyA9IHN1ZmZpeCBpbiBET0NfRVhURU5TSU9OUwoKICAgIGlmIGlzX2NvZGUgYW5kIG5vdCBpc19kb2M6CiAgICAgICAgcmV0dXJuIFtCUkFOQ0hfU1BFQ1NbImppbmFfY29kZSJdXQogICAgaWYgaXNfZG9jIGFuZCBub3QgaXNfY29kZToKICAgICAgICByZXR1cm4gW0JSQU5DSF9TUEVDU1siYmdlX2RvYyJdXQoKICAgICMgQXJxdWl2b3MgYW1iw61ndW9zL2V4dGVuc8OjbyBkZXNjb25oZWNpZGE6IGluZGV4YSBlbSBhbWJhcyBwYXJhIG7Do28gcGVyZGVyIHJlY2FsbC4KICAgIHJldHVybiBbQlJBTkNIX1NQRUNTWyJqaW5hX2NvZGUiXSwgQlJBTkNIX1NQRUNTWyJiZ2VfZG9jIl1dCgoKZGVmIF9pbmRleF9zaW5nbGVfZmlsZV9mb3JfYnJhbmNoKAogICAgZmlsZXBhdGg6IFBhdGgsCiAgICBicmFuY2g6IEJyYW5jaFNwZWMsCiAgICBzcGxpdHRlcjogUmVjdXJzaXZlQ2hhcmFjdGVyVGV4dFNwbGl0dGVyLAogICAgKiwKICAgIGRlbGV0ZV9leGlzdGluZzogYm9vbCA9IFRydWUsCikgLT4gaW50OgogICAgY29udGVudCA9IF9yZWFkX2ZpbGVfc2FmZShmaWxlcGF0aCkKICAgIGlmIG5vdCBjb250ZW50IG9yIG5vdCBjb250ZW50LnN0cmlwKCk6CiAgICAgICAgcmV0dXJuIDAKCiAgICBhYnNfcGF0aCA9IHN0cihmaWxlcGF0aC5yZXNvbHZlKCkpCiAgICBtb2RlbCA9IGdldF9lbWJlZGRpbmdfbW9kZWwoYnJhbmNoLm1vZGVsX2Nob2ljZSkKICAgIGNvbGxlY3Rpb24gPSBnZXRfY2hyb21hX2NvbGxlY3Rpb24oYnJhbmNoLmNvbGxlY3Rpb25fbmFtZSkKCiAgICBjaHVua3MgPSBzcGxpdHRlci5zcGxpdF90ZXh0KGNvbnRlbnQpCiAgICBpZiBub3QgY2h1bmtzOgogICAgICAgIHJldHVybiAwCgogICAgIyBBdHVhbGl6YcOnw6NvIGlkZW1wb3RlbnRlIHBvciBhcnF1aXZvIGVtIGNhZGEgY29sZcOnw6NvLgogICAgaWYgZGVsZXRlX2V4aXN0aW5nOgogICAgICAgIF9kZWxldGVfZmlsZV9jaHVua3MoY29sbGVjdGlvbiwgYWJzX3BhdGgpCgogICAgaW5zZXJ0ZWRfY2h1bmtzID0gMAogICAgYmF0Y2hfaWRzOiBsaXN0W3N0cl0gPSBbXQogICAgYmF0Y2hfZG9jczogbGlzdFtzdHJdID0gW10KICAgIGJhdGNoX21ldGFkYXRhczogbGlzdFtkaWN0W3N0ciwgb2JqZWN0XV0gPSBbXQoKICAgIGRlZiBfZmx1c2hfYmF0Y2goKSAtPiBOb25lOgogICAgICAgIG5vbmxvY2FsIGluc2VydGVkX2NodW5rcwogICAgICAgIGlmIG5vdCBiYXRjaF9pZHM6CiAgICAgICAgICAgIHJldHVybgogICAgICAgIGVtYmVkZGluZ3MgPSBtb2RlbC5lbmNvZGUoCiAgICAgICAgICAgIGJhdGNoX2RvY3MsCiAgICAgICAgICAgIHNob3dfcHJvZ3Jlc3NfYmFyPUZhbHNlLAogICAgICAgICAgICBiYXRjaF9zaXplPUVNQkVERElOR19CQVRDSF9TSVpFLAogICAgICAgICkudG9saXN0KCkKICAgICAgICBjb2xsZWN0aW9uLnVwc2VydCgKICAgICAgICAgICAgaWRzPWJhdGNoX2lkcywKICAgICAgICAgICAgZW1iZWRkaW5ncz1lbWJlZGRpbmdzLAogICAgICAgICAgICBkb2N1bWVudHM9YmF0Y2hfZG9jcywKICAgICAgICAgICAgbWV0YWRhdGFzPWJhdGNoX21ldGFkYXRhcywKICAgICAgICApCiAgICAgICAgaW5zZXJ0ZWRfY2h1bmtzICs9IGxlbihiYXRjaF9pZHMpCiAgICAgICAgZGVsIGVtYmVkZGluZ3MKICAgICAgICBiYXRjaF9pZHMuY2xlYXIoKQogICAgICAgIGJhdGNoX2RvY3MuY2xlYXIoKQogICAgICAgIGJhdGNoX21ldGFkYXRhcy5jbGVhcigpCgogICAgZm9yIGksIGNodW5rIGluIGVudW1lcmF0ZShjaHVua3MpOgogICAgICAgIGJhdGNoX2lkcy5hcHBlbmQoX21ha2VfY2h1bmtfaWQoYWJzX3BhdGgsIGkpKQogICAgICAgIGJhdGNoX2RvY3MuYXBwZW5kKGNodW5rKQogICAgICAgIGJhdGNoX21ldGFkYXRhcy5hcHBlbmQoCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJmaWxlX3BhdGgiOiBhYnNfcGF0aCwKICAgICAgICAgICAgICAgICJmaWxlX25hbWUiOiBmaWxlcGF0aC5uYW1lLAogICAgICAgICAgICAgICAgImNodW5rX2luZGV4IjogaSwKICAgICAgICAgICAgICAgICJzb3VyY2VfY29sbGVjdGlvbiI6IGJyYW5jaC5jb2xsZWN0aW9uX25hbWUsCiAgICAgICAgICAgICAgICAic291cmNlX21vZGVsX2Nob2ljZSI6IGJyYW5jaC5tb2RlbF9jaG9pY2UsCiAgICAgICAgICAgICAgICAic291cmNlX21vZGVsX2lkIjogYnJhbmNoLm1vZGVsX2lkLAogICAgICAgICAgICAgICAgImNvbnRlbnRfZG9tYWluIjogYnJhbmNoLmNvbnRlbnRfZG9tYWluLAogICAgICAgICAgICB9CiAgICAgICAgKQogICAgICAgIGlmIGxlbihiYXRjaF9pZHMpID49IEVNQkVERElOR19CQVRDSF9TSVpFOgogICAgICAgICAgICBfZmx1c2hfYmF0Y2goKQoKICAgIF9mbHVzaF9iYXRjaCgpCiAgICByZXR1cm4gaW5zZXJ0ZWRfY2h1bmtzCgoKZGVmIF9yZW1vdmVfZmlsZV9mcm9tX2FsbF9jb2xsZWN0aW9ucyhhYnNfcGF0aDogc3RyKSAtPiB0dXBsZVtkaWN0W3N0ciwgaW50XSwgbGlzdFtzdHJdXToKICAgIGRlbGV0ZWRfcGVyX2JyYW5jaDogZGljdFtzdHIsIGludF0gPSB7fQogICAgZXJyb3JzOiBsaXN0W3N0cl0gPSBbXQoKICAgIGZvciBicmFuY2ggaW4gQlJBTkNIX1NQRUNTLnZhbHVlcygpOgogICAgICAgIHRyeToKICAgICAgICAgICAgY29sbGVjdGlvbiA9IGdldF9jaHJvbWFfY29sbGVjdGlvbihicmFuY2guY29sbGVjdGlvbl9uYW1lKQogICAgICAgICAgICBkZWxldGVkID0gX2RlbGV0ZV9maWxlX2NodW5rcyhjb2xsZWN0aW9uLCBhYnNfcGF0aCkKICAgICAgICAgICAgZGVsZXRlZF9wZXJfYnJhbmNoW2JyYW5jaC5rZXldID0gZGVsZXRlZAogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgZXJyb3JzLmFwcGVuZChmInticmFuY2gua2V5fToge2V9IikKICAgIHJldHVybiBkZWxldGVkX3Blcl9icmFuY2gsIGVycm9ycwoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQnVzY2Egc2Vtw6JudGljYSBow61icmlkYQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCmRlZiBfcXVlcnlfYnJhbmNoKGJyYW5jaDogQnJhbmNoU3BlYywgcXVlcnk6IHN0ciwgbl9yZXN1bHRzOiBpbnQpIC0+IHR1cGxlW2xpc3RbUmV0cmlldmVkSGl0XSwgc3RyIHwgTm9uZV06CiAgICB0cnk6CiAgICAgICAgY29sbGVjdGlvbiA9IGdldF9jaHJvbWFfY29sbGVjdGlvbihicmFuY2guY29sbGVjdGlvbl9uYW1lKQogICAgICAgIG1vZGVsID0gZ2V0X2VtYmVkZGluZ19tb2RlbChicmFuY2gubW9kZWxfY2hvaWNlKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHJldHVybiBbXSwgZiJ7YnJhbmNoLmtleX06IHJlY3Vyc28gaW5kaXNwb27DrXZlbCAoe2V9KSIKCiAgICB0cnk6CiAgICAgICAgcXVlcnlfZW1iZWRkaW5nID0gbW9kZWwuZW5jb2RlKFtxdWVyeV0sIHNob3dfcHJvZ3Jlc3NfYmFyPUZhbHNlKS50b2xpc3QoKQogICAgICAgIHJlc3VsdHMgPSBjb2xsZWN0aW9uLnF1ZXJ5KAogICAgICAgICAgICBxdWVyeV9lbWJlZGRpbmdzPXF1ZXJ5X2VtYmVkZGluZywKICAgICAgICAgICAgbl9yZXN1bHRzPW5fcmVzdWx0cywKICAgICAgICAgICAgaW5jbHVkZT1bImRvY3VtZW50cyIsICJtZXRhZGF0YXMiLCAiZGlzdGFuY2VzIl0sCiAgICAgICAgKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHJldHVybiBbXSwgZiJ7YnJhbmNoLmtleX06IGZhbGhhIG5hIHF1ZXJ5ICh7ZX0pIgoKICAgIGRvY3VtZW50cyA9IHJlc3VsdHMuZ2V0KCJkb2N1bWVudHMiLCBbW11dKVswXQogICAgbWV0YWRhdGFzID0gcmVzdWx0cy5nZXQoIm1ldGFkYXRhcyIsIFtbXV0pWzBdCiAgICBkaXN0YW5jZXMgPSByZXN1bHRzLmdldCgiZGlzdGFuY2VzIiwgW1tdXSlbMF0KICAgIGlkcyA9IHJlc3VsdHMuZ2V0KCJpZHMiLCBbW11dKVswXQoKICAgIGhpdHM6IGxpc3RbUmV0cmlldmVkSGl0XSA9IFtdCiAgICBmb3IgaWR4LCAoZG9jLCBtZXRhLCBkaXN0KSBpbiBlbnVtZXJhdGUoemlwKGRvY3VtZW50cywgbWV0YWRhdGFzLCBkaXN0YW5jZXMpLCBzdGFydD0xKToKICAgICAgICBtZXRhZGF0YSA9IG1ldGEgb3Ige30KICAgICAgICBmYWxsYmFja19pZCA9IGlkc1tpZHggLSAxXSBpZiBpZHggLSAxIDwgbGVuKGlkcykgZWxzZSBmInticmFuY2gua2V5fTp7aWR4fSIKICAgICAgICBrZXkgPSBfbWFrZV9yZXN1bHRfa2V5KG1ldGFkYXRhLCBmYWxsYmFja19pZCkKCiAgICAgICAgc2ltaWxhcml0eSA9IE5vbmUKICAgICAgICBpZiBkaXN0IGlzIG5vdCBOb25lOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBzaW1pbGFyaXR5ID0gMS4wIC0gZmxvYXQoZGlzdCkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgIHNpbWlsYXJpdHkgPSBOb25lCgogICAgICAgIGhpdHMuYXBwZW5kKAogICAgICAgICAgICBSZXRyaWV2ZWRIaXQoCiAgICAgICAgICAgICAgICBrZXk9a2V5LAogICAgICAgICAgICAgICAgZG9jdW1lbnQ9KGRvYyBvciAiIiksCiAgICAgICAgICAgICAgICBtZXRhZGF0YT1tZXRhZGF0YSwKICAgICAgICAgICAgICAgIGRpc3RhbmNlPWZsb2F0KGRpc3QpIGlmIGRpc3QgaXMgbm90IE5vbmUgZWxzZSBOb25lLAogICAgICAgICAgICAgICAgc2ltaWxhcml0eT1zaW1pbGFyaXR5LAogICAgICAgICAgICAgICAgYnJhbmNoPWJyYW5jaCwKICAgICAgICAgICAgICAgIHJhbms9aWR4LAogICAgICAgICAgICApCiAgICAgICAgKQoKICAgIHJldHVybiBoaXRzLCBOb25lCgoKZGVmIF9ycmZfZnVzZShoaXRzX2J5X2JyYW5jaDogZGljdFtzdHIsIGxpc3RbUmV0cmlldmVkSGl0XV0sIHRvcF9saW1pdDogaW50KSAtPiBsaXN0W0Z1c2VkSGl0XToKICAgIGZ1c2VkOiBkaWN0W3N0ciwgRnVzZWRIaXRdID0ge30KCiAgICBmb3IgYnJhbmNoX2tleSwgaGl0cyBpbiBoaXRzX2J5X2JyYW5jaC5pdGVtcygpOgogICAgICAgIF8gPSBicmFuY2hfa2V5CiAgICAgICAgZm9yIHJhbmssIGhpdCBpbiBlbnVtZXJhdGUoaGl0cywgc3RhcnQ9MSk6CiAgICAgICAgICAgIGNvbnRyaWJ1dGlvbiA9IDEuMCAvIChSUkZfSyArIHJhbmspCiAgICAgICAgICAgIGVudHJ5ID0gZnVzZWQuZ2V0KGhpdC5rZXkpCgogICAgICAgICAgICBpZiBlbnRyeSBpcyBOb25lOgogICAgICAgICAgICAgICAgZW50cnkgPSBGdXNlZEhpdCgKICAgICAgICAgICAgICAgICAgICBrZXk9aGl0LmtleSwKICAgICAgICAgICAgICAgICAgICBkb2N1bWVudD1oaXQuZG9jdW1lbnQsCiAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGE9ZGljdChoaXQubWV0YWRhdGEpLAogICAgICAgICAgICAgICAgICAgIHJyZl9zY29yZT0wLjAsCiAgICAgICAgICAgICAgICAgICAgc291cmNlX2RldGFpbHM9e30sCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBmdXNlZFtoaXQua2V5XSA9IGVudHJ5CgogICAgICAgICAgICBlbnRyeS5ycmZfc2NvcmUgKz0gY29udHJpYnV0aW9uCiAgICAgICAgICAgIGVudHJ5LnNvdXJjZV9kZXRhaWxzW2hpdC5icmFuY2gua2V5XSA9IHsKICAgICAgICAgICAgICAgICJyYW5rIjogcmFuaywKICAgICAgICAgICAgICAgICJkaXN0YW5jZSI6IGhpdC5kaXN0YW5jZSwKICAgICAgICAgICAgICAgICJzaW1pbGFyaXR5IjogaGl0LnNpbWlsYXJpdHksCiAgICAgICAgICAgICAgICAiY29sbGVjdGlvbiI6IGhpdC5icmFuY2guY29sbGVjdGlvbl9uYW1lLAogICAgICAgICAgICAgICAgIm1vZGVsX2Nob2ljZSI6IGhpdC5icmFuY2gubW9kZWxfY2hvaWNlLAogICAgICAgICAgICAgICAgIm1vZGVsX2lkIjogaGl0LmJyYW5jaC5tb2RlbF9pZCwKICAgICAgICAgICAgICAgICJjb250ZW50X2RvbWFpbiI6IGhpdC5icmFuY2guY29udGVudF9kb21haW4sCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgICMgVXNhIG1ldGFkYWRvcyBkbyBoaXQgY29tIG1lbGhvciBzaW1pbGFyaWRhZGUgbG9jYWwgY29tbyBiYXNlIHByaW5jaXBhbC4KICAgICAgICAgICAgY3VycmVudF9zaW0gPSBlbnRyeS5tZXRhZGF0YS5nZXQoIl9iZXN0X3NpbWlsYXJpdHkiLCAtMTAuMCkKICAgICAgICAgICAgY2FuZGlkYXRlX3NpbSA9IGhpdC5zaW1pbGFyaXR5IGlmIGhpdC5zaW1pbGFyaXR5IGlzIG5vdCBOb25lIGVsc2UgLTEwLjAKICAgICAgICAgICAgaWYgY2FuZGlkYXRlX3NpbSA+IGN1cnJlbnRfc2ltOgogICAgICAgICAgICAgICAgZW50cnkuZG9jdW1lbnQgPSBoaXQuZG9jdW1lbnQKICAgICAgICAgICAgICAgIGVudHJ5Lm1ldGFkYXRhID0gZGljdChoaXQubWV0YWRhdGEpCiAgICAgICAgICAgICAgICBlbnRyeS5tZXRhZGF0YVsiX2Jlc3Rfc2ltaWxhcml0eSJdID0gY2FuZGlkYXRlX3NpbQoKICAgIGZ1c2VkX2hpdHMgPSBsaXN0KGZ1c2VkLnZhbHVlcygpKQogICAgZnVzZWRfaGl0cy5zb3J0KGtleT1sYW1iZGEgaDogaC5ycmZfc2NvcmUsIHJldmVyc2U9VHJ1ZSkKCiAgICAjIExpbWl0YSBvIHBvb2wgYW50ZXMgZG8gcmVyYW5raW5nIHBhcmEgcmVkdXppciBDUFUvUkFNLgogICAgcmV0dXJuIGZ1c2VkX2hpdHNbOnRvcF9saW1pdF0KCgpkZWYgX2FwcGx5X3JlcmFuayhxdWVyeTogc3RyLCBmdXNlZF9oaXRzOiBsaXN0W0Z1c2VkSGl0XSwgdG9wX2s6IGludCkgLT4gdHVwbGVbbGlzdFtGdXNlZEhpdF0sIGJvb2wsIHN0ciB8IE5vbmVdOgogICAgaWYgbm90IGZ1c2VkX2hpdHM6CiAgICAgICAgcmV0dXJuIFtdLCBGYWxzZSwgTm9uZQoKICAgIHJlcmFua2VyID0gZ2V0X3JlcmFua2VyKCkKICAgIGlmIHJlcmFua2VyIGlzIE5vbmU6CiAgICAgICAgcmVhc29uID0gX3JlcmFua2VyX2Vycm9yIGlmIF9yZXJhbmtlcl9lcnJvciBlbHNlICJyZXJhbmtlcl9kZXNhYmlsaXRhZG8iCiAgICAgICAgcmV0dXJuIGZ1c2VkX2hpdHNbOnRvcF9rXSwgRmFsc2UsIHJlYXNvbgoKICAgIHRyeToKICAgICAgICBwYWlycyA9IFsocXVlcnksIGhpdC5kb2N1bWVudCkgZm9yIGhpdCBpbiBmdXNlZF9oaXRzXQogICAgICAgIHNjb3JlcyA9IHJlcmFua2VyLnByZWRpY3QocGFpcnMsIHNob3dfcHJvZ3Jlc3NfYmFyPUZhbHNlLCBjb252ZXJ0X3RvX251bXB5PVRydWUpCgogICAgICAgIGZvciBoaXQsIHNjb3JlIGluIHppcChmdXNlZF9oaXRzLCBzY29yZXMpOgogICAgICAgICAgICBoaXQucmVyYW5rX3Njb3JlID0gZmxvYXQoc2NvcmUpCgogICAgICAgIGZ1c2VkX2hpdHMuc29ydCgKICAgICAgICAgICAga2V5PWxhbWJkYSBoOiAoCiAgICAgICAgICAgICAgICBoLnJlcmFua19zY29yZSBpZiBoLnJlcmFua19zY29yZSBpcyBub3QgTm9uZSBlbHNlIC0xZTksCiAgICAgICAgICAgICAgICBoLnJyZl9zY29yZSwKICAgICAgICAgICAgKSwKICAgICAgICAgICAgcmV2ZXJzZT1UcnVlLAogICAgICAgICkKICAgICAgICByZXR1cm4gZnVzZWRfaGl0c1s6dG9wX2tdLCBUcnVlLCBOb25lCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcmV0dXJuIGZ1c2VkX2hpdHNbOnRvcF9rXSwgRmFsc2UsIHN0cihlKQoKCmRlZiBfZm9ybWF0X3NpbWlsYXJpdHkoc2ltaWxhcml0eTogZmxvYXQgfCBOb25lKSAtPiBzdHI6CiAgICBpZiBzaW1pbGFyaXR5IGlzIE5vbmU6CiAgICAgICAgcmV0dXJuICJuL2EiCiAgICByZXR1cm4gZiJ7cm91bmQoc2ltaWxhcml0eSAqIDEwMCwgMSl9JSIKCgpkZWYgX2Zvcm1hdF9mdXNlZF9yZXN1bHRzKAogICAgKiwKICAgIHF1ZXJ5OiBzdHIsCiAgICBtb2RlOiBzdHIsCiAgICBoaXRzOiBsaXN0W0Z1c2VkSGl0XSwKICAgIGJyYW5jaF9lcnJvcnM6IGxpc3Rbc3RyXSwKICAgIHJlcmFua19hcHBsaWVkOiBib29sLAogICAgcmVyYW5rX2Vycm9yOiBzdHIgfCBOb25lLAopIC0+IHN0cjoKICAgIGlmIG5vdCBoaXRzOgogICAgICAgIG1zZyA9ICJOZW5odW0gcmVzdWx0YWRvIGVuY29udHJhZG8uIEFzIGNvbGXDp8O1ZXMgcG9kZW0gZXN0YXIgdmF6aWFzLiIKICAgICAgICBpZiBicmFuY2hfZXJyb3JzOgogICAgICAgICAgICBtc2cgKz0gIlxuRmFsaGFzIGRldGVjdGFkYXM6ICIgKyAiIHwgIi5qb2luKGJyYW5jaF9lcnJvcnMpCiAgICAgICAgcmV0dXJuIG1zZwoKICAgIGxpbmVzOiBsaXN0W3N0cl0gPSBbZiIjIFJlc3VsdGFkb3MgcGFyYTogJ3txdWVyeX0nIiwgZiIqKk1vZG86Kioge21vZGV9Il0KCiAgICBpZiBicmFuY2hfZXJyb3JzOgogICAgICAgIGxpbmVzLmFwcGVuZCgiKipBdmlzb3MgZGUgYnJhbmNoOioqICIgKyAiIHwgIi5qb2luKGJyYW5jaF9lcnJvcnMpKQoKICAgIGlmIG1vZGUgPT0gImVuc2VtYmxlIjoKICAgICAgICBpZiByZXJhbmtfYXBwbGllZDoKICAgICAgICAgICAgbGluZXMuYXBwZW5kKGYiKipSZXJhbmtpbmc6KiogYXRpdm8gKHtSRVJBTktfTU9ERUxfSUR9KSIpCiAgICAgICAgZWxzZToKICAgICAgICAgICAgbGluZXMuYXBwZW5kKGYiKipSZXJhbmtpbmc6KiogaW5kaXNwb27DrXZlbCAoe3JlcmFua19lcnJvciBvciAnc2VtIGRldGFsaGVzJ30pIikKCiAgICBsaW5lcy5hcHBlbmQoIiIpCgogICAgZm9yIGlkeCwgaGl0IGluIGVudW1lcmF0ZShoaXRzLCBzdGFydD0xKToKICAgICAgICBtZXRhZGF0YSA9IGRpY3QoaGl0Lm1ldGFkYXRhKQogICAgICAgIG1ldGFkYXRhLnBvcCgiX2Jlc3Rfc2ltaWxhcml0eSIsIE5vbmUpCgogICAgICAgIGZpbGVfcGF0aCA9IHN0cihtZXRhZGF0YS5nZXQoImZpbGVfcGF0aCIsICJkZXNjb25oZWNpZG8iKSkKICAgICAgICBjaHVua19pbmRleCA9IG1ldGFkYXRhLmdldCgiY2h1bmtfaW5kZXgiLCAiPyIpCiAgICAgICAgZmlsZV9uYW1lID0gc3RyKG1ldGFkYXRhLmdldCgiZmlsZV9uYW1lIiwgUGF0aChmaWxlX3BhdGgpLm5hbWUgaWYgZmlsZV9wYXRoICE9ICJkZXNjb25oZWNpZG8iIGVsc2UgIj8iKSkKCiAgICAgICAgc291cmNlX21vZGVscyA9IHNvcnRlZCh7c3RyKHYuZ2V0KCJtb2RlbF9jaG9pY2UiLCAiPyIpKSBmb3IgdiBpbiBoaXQuc291cmNlX2RldGFpbHMudmFsdWVzKCl9KQogICAgICAgIHNvdXJjZV9jb2xsZWN0aW9ucyA9IHNvcnRlZCh7c3RyKHYuZ2V0KCJjb2xsZWN0aW9uIiwgIj8iKSkgZm9yIHYgaW4gaGl0LnNvdXJjZV9kZXRhaWxzLnZhbHVlcygpfSkKCiAgICAgICAgc291cmNlX3BhcnRzOiBsaXN0W3N0cl0gPSBbXQogICAgICAgIGZvciBzb3VyY2Vfa2V5LCBkZXRhaWxzIGluIHNvcnRlZCgKICAgICAgICAgICAgaGl0LnNvdXJjZV9kZXRhaWxzLml0ZW1zKCksCiAgICAgICAgICAgIGtleT1sYW1iZGEgaXRlbTogaW50KGl0ZW1bMV0uZ2V0KCJyYW5rIiwgOTk5OTk5KSksCiAgICAgICAgKToKICAgICAgICAgICAgc291cmNlX3BhcnRzLmFwcGVuZCgKICAgICAgICAgICAgICAgIGYie3NvdXJjZV9rZXl9KHJhbms9e2RldGFpbHMuZ2V0KCdyYW5rJyl9LCBzaW09e19mb3JtYXRfc2ltaWxhcml0eShkZXRhaWxzLmdldCgnc2ltaWxhcml0eScpKX0pIgogICAgICAgICAgICApCgogICAgICAgIHNuaXBwZXQgPSBoaXQuZG9jdW1lbnQuc3RyaXAoKQogICAgICAgIGlmIGxlbihzbmlwcGV0KSA+IDgwMDoKICAgICAgICAgICAgc25pcHBldCA9IHNuaXBwZXRbOjgwMF0gKyAiXG4uLi4gW3RydW5jYWRvXSIKCiAgICAgICAgc2NvcmVfbGluZSA9IGYiUlJGPXtoaXQucnJmX3Njb3JlOi40Zn0iCiAgICAgICAgaWYgaGl0LnJlcmFua19zY29yZSBpcyBub3QgTm9uZToKICAgICAgICAgICAgc2NvcmVfbGluZSArPSBmIiB8IHJlcmFuaz17aGl0LnJlcmFua19zY29yZTouNGZ9IgoKICAgICAgICBsaW5lcy5hcHBlbmQoZiIjIyBbe2lkeH1dIHtmaWxlX3BhdGh9IikKICAgICAgICBsaW5lcy5hcHBlbmQoZiIqKlNjb3JlczoqKiB7c2NvcmVfbGluZX0iKQogICAgICAgIGxpbmVzLmFwcGVuZChmIioqRm9udGVzIGRlIHJlY3VwZXJhw6fDo286KiogeycsICcuam9pbihzb3VyY2VfcGFydHMpfSIpCiAgICAgICAgbGluZXMuYXBwZW5kKAogICAgICAgICAgICAiKipNZXRhZGFkb3MgdW5pZmljYWRvczoqKiAiCiAgICAgICAgICAgIGYiZmlsZV9uYW1lPXtmaWxlX25hbWV9IHwgY2h1bmtfaW5kZXg9e2NodW5rX2luZGV4fSB8ICIKICAgICAgICAgICAgZiJzb3VyY2VfbW9kZWxzPXtzb3VyY2VfbW9kZWxzfSB8IHNvdXJjZV9jb2xsZWN0aW9ucz17c291cmNlX2NvbGxlY3Rpb25zfSIKICAgICAgICApCiAgICAgICAgbGluZXMuYXBwZW5kKCIiKQogICAgICAgIGxpbmVzLmFwcGVuZChmImBgYFxue3NuaXBwZXR9XG5gYGAiKQogICAgICAgIGxpbmVzLmFwcGVuZCgiIikKCiAgICByZXR1cm4gIlxuIi5qb2luKGxpbmVzKQoKCmRlZiBfcnVuX3NpbmdsZV9tb2RlKHF1ZXJ5OiBzdHIsIHRvcF9rOiBpbnQpIC0+IHR1cGxlW2xpc3RbRnVzZWRIaXRdLCBsaXN0W3N0cl0sIGJvb2wsIHN0ciB8IE5vbmVdOgogICAgcHJpbWFyeV9icmFuY2ggPSBCUkFOQ0hfU1BFQ1NbREVGQVVMVF9TSU5HTEVfQlJBTkNIX0tFWV0KCiAgICBoaXRzLCBlcnJvciA9IF9xdWVyeV9icmFuY2gocHJpbWFyeV9icmFuY2gsIHF1ZXJ5LCB0b3BfaykKICAgIGVycm9yczogbGlzdFtzdHJdID0gW10KICAgIGlmIGVycm9yOgogICAgICAgIGVycm9ycy5hcHBlbmQoZXJyb3IpCgogICAgIyBGYWxsYmFjayBhdXRvbcOhdGljbyBwYXJhIGEgYnJhbmNoIGFsdGVybmF0aXZhLCBwcmVzZXJ2YW5kbyBkaXNwb25pYmlsaWRhZGUuCiAgICBpZiBub3QgaGl0czoKICAgICAgICBmYWxsYmFja19icmFuY2hfa2V5ID0gImJnZV9kb2MiIGlmIHByaW1hcnlfYnJhbmNoLmtleSA9PSAiamluYV9jb2RlIiBlbHNlICJqaW5hX2NvZGUiCiAgICAgICAgZmFsbGJhY2tfaGl0cywgZmFsbGJhY2tfZXJyb3IgPSBfcXVlcnlfYnJhbmNoKEJSQU5DSF9TUEVDU1tmYWxsYmFja19icmFuY2hfa2V5XSwgcXVlcnksIHRvcF9rKQogICAgICAgIGlmIGZhbGxiYWNrX2Vycm9yOgogICAgICAgICAgICBlcnJvcnMuYXBwZW5kKGZhbGxiYWNrX2Vycm9yKQogICAgICAgIGlmIGZhbGxiYWNrX2hpdHM6CiAgICAgICAgICAgIGhpdHMgPSBmYWxsYmFja19oaXRzCgogICAgaWYgbm90IGhpdHM6CiAgICAgICAgcmV0dXJuIFtdLCBlcnJvcnMsIEZhbHNlLCBOb25lCgogICAgZnVzZWQgPSBfcnJmX2Z1c2UoeyJzaW5nbGUiOiBoaXRzfSwgdG9wX2spCiAgICByZXR1cm4gZnVzZWQsIGVycm9ycywgRmFsc2UsIE5vbmUKCgpkZWYgX3J1bl9lbnNlbWJsZV9tb2RlKHF1ZXJ5OiBzdHIsIHRvcF9rOiBpbnQpIC0+IHR1cGxlW2xpc3RbRnVzZWRIaXRdLCBsaXN0W3N0cl0sIGJvb2wsIHN0ciB8IE5vbmVdOgogICAgcGVyX2JyYW5jaF9rID0gbWluKE1BWF9RVUVSWV9SRVNVTFRTLCBtYXgodG9wX2sgKiAyLCB0b3BfaykpCiAgICBicmFuY2hlcyA9IFtCUkFOQ0hfU1BFQ1NbImppbmFfY29kZSJdLCBCUkFOQ0hfU1BFQ1NbImJnZV9kb2MiXV0KCiAgICBoaXRzX2J5X2JyYW5jaDogZGljdFtzdHIsIGxpc3RbUmV0cmlldmVkSGl0XV0gPSB7fQogICAgYnJhbmNoX2Vycm9yczogbGlzdFtzdHJdID0gW10KCiAgICB3aXRoIFRocmVhZFBvb2xFeGVjdXRvcihtYXhfd29ya2Vycz1sZW4oYnJhbmNoZXMpKSBhcyBleGVjdXRvcjoKICAgICAgICBmdXR1cmVzID0gewogICAgICAgICAgICBleGVjdXRvci5zdWJtaXQoX3F1ZXJ5X2JyYW5jaCwgYnJhbmNoLCBxdWVyeSwgcGVyX2JyYW5jaF9rKTogYnJhbmNoCiAgICAgICAgICAgIGZvciBicmFuY2ggaW4gYnJhbmNoZXMKICAgICAgICB9CiAgICAgICAgZm9yIGZ1dHVyZSBpbiBhc19jb21wbGV0ZWQoZnV0dXJlcyk6CiAgICAgICAgICAgIGJyYW5jaCA9IGZ1dHVyZXNbZnV0dXJlXQogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBoaXRzLCBlcnJvciA9IGZ1dHVyZS5yZXN1bHQoKQogICAgICAgICAgICAgICAgaWYgZXJyb3I6CiAgICAgICAgICAgICAgICAgICAgYnJhbmNoX2Vycm9ycy5hcHBlbmQoZXJyb3IpCiAgICAgICAgICAgICAgICBpZiBoaXRzOgogICAgICAgICAgICAgICAgICAgIGhpdHNfYnlfYnJhbmNoW2JyYW5jaC5rZXldID0gaGl0cwogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBicmFuY2hfZXJyb3JzLmFwcGVuZChmInticmFuY2gua2V5fTogZmFsaGEgaW5lc3BlcmFkYSAoe2V9KSIpCgogICAgaWYgbm90IGhpdHNfYnlfYnJhbmNoOgogICAgICAgIHJldHVybiBbXSwgYnJhbmNoX2Vycm9ycywgRmFsc2UsIE5vbmUKCiAgICBjYW5kaWRhdGVfcG9vbCA9IG1pbihSRVJBTktfTUFYX0NBTkRJREFURVMsIG1heCh0b3BfaywgdG9wX2sgKiBSRVJBTktfQ0FORElEQVRFX01VTFRJUExJRVIpKQogICAgZnVzZWRfY2FuZGlkYXRlcyA9IF9ycmZfZnVzZShoaXRzX2J5X2JyYW5jaCwgY2FuZGlkYXRlX3Bvb2wpCiAgICByZXJhbmtlZF9oaXRzLCByZXJhbmtfYXBwbGllZCwgcmVyYW5rX2Vycm9yID0gX2FwcGx5X3JlcmFuayhxdWVyeSwgZnVzZWRfY2FuZGlkYXRlcywgdG9wX2spCiAgICByZXR1cm4gcmVyYW5rZWRfaGl0cywgYnJhbmNoX2Vycm9ycywgcmVyYW5rX2FwcGxpZWQsIHJlcmFua19lcnJvcgoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgU2Vydmlkb3IgTUNQIHZpYSBGYXN0TUNQCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgptY3AgPSBGYXN0TUNQKAogICAgbmFtZT0icmFnLWNvZGViYXNlIiwKICAgIGluc3RydWN0aW9ucz0oCiAgICAgICAgIlNlcnZpZG9yIFJBRyBwYXJhIGJ1c2NhIHNlbcOibnRpY2EgZW0gY8OzZGlnby1mb250ZSBsb2NhbCBjb20gc3Vwb3J0ZSBhIGVuc2VtYmxlIGjDrWJyaWRvLiAiCiAgICAgICAgIk5vIG1vZG8gaHlicmlkLCBhIGJyYW5jaCBkZSBjw7NkaWdvIHVzYSBKaW5hIHYyIGUgYSBkZSBkb2N1bWVudGHDp8OjbyB1c2EgQkdFLiAiCiAgICAgICAgIlVzZSBzZW1hbnRpY19zZWFyY2hfY29kZShxdWVyeSwgdG9wX2ssIG1vZGU9J2Vuc2VtYmxlJykgcGFyYSBjb21iaW5hciBKaW5hK0JHRSBjb20gUlJGIGUgcmVyYW5raW5nLiAiCiAgICAgICAgIlVzZSB1cGRhdGVfZmlsZV9pbmRleCBhcMOzcyBlZGl0YXIgdW0gYXJxdWl2byBwYXJhIG1hbnRlciBhcyBkdWFzIGNvbGXDp8O1ZXMgc2luY3Jvbml6YWRhcy4gIgogICAgICAgICJVc2UgaW5kZXhfc3BlY2lmaWNfZm9sZGVyIHBhcmEgaW5kZXhhw6fDo28gcmVjdXJzaXZhIHNvYiBkZW1hbmRhLiIKICAgICksCikKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFRvb2wgMTogc2VtYW50aWNfc2VhcmNoX2NvZGUKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkBtY3AudG9vbCgpCmRlZiBzZW1hbnRpY19zZWFyY2hfY29kZShxdWVyeTogc3RyLCB0b3BfazogaW50ID0gVE9QX0tfUkVTVUxUUywgbW9kZTogc3RyID0gU0VBUkNIX01PREVfREVGQVVMVCkgLT4gc3RyOgogICAgIiIiCiAgICBCdXNjYSBzZW3Dom50aWNhIG5vIMOtbmRpY2UgdmV0b3JpYWwgbG9jYWwuCgogICAgTW9kb3M6CiAgICAtIHNpbmdsZTogdXNhIGFwZW5hcyB1bWEgYnJhbmNoIChKaW5hL0JHRSBjb25mb3JtZSBNQ1BfRU1CRURESU5HX01PREVMOyBubyBoeWJyaWQsIEppbmEgdjIpLgogICAgLSBlbnNlbWJsZTogY29uc3VsdGEgZW0gcGFyYWxlbG8gY29kZV92ZWN0b3JzX2ppbmEgKyBkb2NfdmVjdG9yc19iZ2UsCiAgICAgIGZheiBmdXPDo28gdmlhIFJlY2lwcm9jYWwgUmFuayBGdXNpb24gKFJSRikgZSByZXJhbmtpbmcgbGV2ZS4KCiAgICBBcmdzOgogICAgICAgIHF1ZXJ5OiBEZXNjcmnDp8OjbyBkbyBxdWUgcHJvY3VyYXIuCiAgICAgICAgdG9wX2s6IFF1YW50aWRhZGUgZmluYWwgZGUgcmVzdWx0YWRvcy4KICAgICAgICBtb2RlOiAic2luZ2xlIiAocGFkcsOjbykgb3UgImVuc2VtYmxlIi4KCiAgICBSZXR1cm5zOgogICAgICAgIFJlc3VsdGFkbyB0ZXh0dWFsIGZvcm1hdGFkbyBwYXJhIGNvbnN1bW8gcGVsbyBMTE0uCiAgICAiIiIKICAgIHJhd19xdWVyeSA9IChxdWVyeSBvciAiIikuc3RyaXAoKQogICAgc2VhcmNoX21vZGUgPSAobW9kZSBvciBTRUFSQ0hfTU9ERV9ERUZBVUxUKS5zdHJpcCgpLmxvd2VyKCkKCiAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9zdGFydCIsCiAgICAgICAgdG9vbF9uYW1lPSJzZW1hbnRpY19zZWFyY2hfY29kZSIsCiAgICAgICAgZGV0YWlscz17CiAgICAgICAgICAgICJxdWVyeV9wcmV2aWV3IjogX3NhZmVfcHJldmlldyhyYXdfcXVlcnkpLAogICAgICAgICAgICAicXVlcnlfbGVuIjogbGVuKHJhd19xdWVyeSksCiAgICAgICAgICAgICJ0b3BfayI6IHRvcF9rLAogICAgICAgICAgICAibW9kZSI6IHNlYXJjaF9tb2RlLAogICAgICAgIH0sCiAgICApCgogICAgaWYgbm90IHJhd19xdWVyeToKICAgICAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfZW5kIiwKICAgICAgICAgICAgdG9vbF9uYW1lPSJzZW1hbnRpY19zZWFyY2hfY29kZSIsCiAgICAgICAgICAgIGRldGFpbHM9eyJzdGF0dXMiOiAiZXJyb3IiLCAicmVhc29uIjogImVtcHR5X3F1ZXJ5In0sCiAgICAgICAgKQogICAgICAgIHJldHVybiAiRXJybzogYSBxdWVyeSBuw6NvIHBvZGUgc2VyIHZhemlhLiIKCiAgICB0b3BfayA9IG1heCgxLCBtaW4odG9wX2ssIDIwKSkKICAgIGlmIHNlYXJjaF9tb2RlIG5vdCBpbiB7InNpbmdsZSIsICJlbnNlbWJsZSJ9OgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InNlbWFudGljX3NlYXJjaF9jb2RlIiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAiaW52YWxpZF9tb2RlIiwgIm1vZGUiOiBzZWFyY2hfbW9kZX0sCiAgICAgICAgKQogICAgICAgIHJldHVybiAiRXJybzogbW9kZSBpbnbDoWxpZG8uIFVzZSAnc2luZ2xlJyBvdSAnZW5zZW1ibGUnLiIKCiAgICB0cnk6CiAgICAgICAgaWYgc2VhcmNoX21vZGUgPT0gImVuc2VtYmxlIjoKICAgICAgICAgICAgaGl0cywgYnJhbmNoX2Vycm9ycywgcmVyYW5rX2FwcGxpZWQsIHJlcmFua19lcnJvciA9IF9ydW5fZW5zZW1ibGVfbW9kZShyYXdfcXVlcnksIHRvcF9rKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGhpdHMsIGJyYW5jaF9lcnJvcnMsIHJlcmFua19hcHBsaWVkLCByZXJhbmtfZXJyb3IgPSBfcnVuX3NpbmdsZV9tb2RlKHJhd19xdWVyeSwgdG9wX2spCgogICAgICAgIHJlc3VsdF90ZXh0ID0gX2Zvcm1hdF9mdXNlZF9yZXN1bHRzKAogICAgICAgICAgICBxdWVyeT1yYXdfcXVlcnksCiAgICAgICAgICAgIG1vZGU9c2VhcmNoX21vZGUsCiAgICAgICAgICAgIGhpdHM9aGl0cywKICAgICAgICAgICAgYnJhbmNoX2Vycm9ycz1icmFuY2hfZXJyb3JzLAogICAgICAgICAgICByZXJhbmtfYXBwbGllZD1yZXJhbmtfYXBwbGllZCwKICAgICAgICAgICAgcmVyYW5rX2Vycm9yPXJlcmFua19lcnJvciwKICAgICAgICApCgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InNlbWFudGljX3NlYXJjaF9jb2RlIiwKICAgICAgICAgICAgZGV0YWlscz17CiAgICAgICAgICAgICAgICAic3RhdHVzIjogIm9rIiwKICAgICAgICAgICAgICAgICJtb2RlIjogc2VhcmNoX21vZGUsCiAgICAgICAgICAgICAgICAicmVzdWx0X2NvdW50IjogbGVuKGhpdHMpLAogICAgICAgICAgICAgICAgImJyYW5jaF9lcnJvcnMiOiBsZW4oYnJhbmNoX2Vycm9ycyksCiAgICAgICAgICAgICAgICAicmVyYW5rX2FwcGxpZWQiOiByZXJhbmtfYXBwbGllZCwKICAgICAgICAgICAgfSwKICAgICAgICApCiAgICAgICAgcmV0dXJuIHJlc3VsdF90ZXh0CiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgX2xvZ190b29sX3VzYWdlKAogICAgICAgICAgICBldmVudD0idG9vbF9jYWxsX2VuZCIsCiAgICAgICAgICAgIHRvb2xfbmFtZT0ic2VtYW50aWNfc2VhcmNoX2NvZGUiLAogICAgICAgICAgICBkZXRhaWxzPXsic3RhdHVzIjogImVycm9yIiwgInJlYXNvbiI6ICJzZWFyY2hfZmFpbGVkIiwgImVycm9yIjogc3RyKGUpLCAibW9kZSI6IHNlYXJjaF9tb2RlfSwKICAgICAgICApCiAgICAgICAgcmV0dXJuIGYiRXJybyBhbyBleGVjdXRhciBidXNjYSBzZW3Dom50aWNhICh7c2VhcmNoX21vZGV9KToge2V9IgoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgVG9vbCAyOiB1cGRhdGVfZmlsZV9pbmRleAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQG1jcC50b29sKCkKZGVmIHVwZGF0ZV9maWxlX2luZGV4KGZpbGVfcGF0aDogc3RyKSAtPiBzdHI6CiAgICAiIiIKICAgIEF0dWFsaXphIG8gw61uZGljZSBSQUcgcGFyYSB1bSBhcnF1aXZvIGVzcGVjw61maWNvLgoKICAgIE8gYXJxdWl2byDDqSBjbGFzc2lmaWNhZG8gY29tbyBjw7NkaWdvL2RvYyBlIGluZGV4YWRvIG5hIGNvbGXDp8OjbyBhcHJvcHJpYWRhLgogICAgUGFyYSBleHRlbnPDtWVzIGFtYsOtZ3VhcywgaW5kZXhhIGVtIGFtYmFzIGFzIGNvbGXDp8O1ZXMuCiAgICAiIiIKICAgIGZpbGVwYXRoID0gUGF0aChmaWxlX3BhdGgpLnJlc29sdmUoKQogICAgYWJzX3BhdGggPSBzdHIoZmlsZXBhdGgpCgogICAgX2xvZ190b29sX3VzYWdlKAogICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfc3RhcnQiLAogICAgICAgIHRvb2xfbmFtZT0idXBkYXRlX2ZpbGVfaW5kZXgiLAogICAgICAgIGRldGFpbHM9eyJmaWxlX3BhdGgiOiBhYnNfcGF0aH0sCiAgICApCgogICAgaWYgbm90IGZpbGVwYXRoLmV4aXN0cygpOgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InVwZGF0ZV9maWxlX2luZGV4IiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAiZmlsZV9ub3RfZm91bmQiLCAiZmlsZV9wYXRoIjogYWJzX3BhdGh9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJFcnJvOiBhcnF1aXZvIG7Do28gZW5jb250cmFkbzoge2ZpbGVwYXRofSIKCiAgICBpZiBub3QgZmlsZXBhdGguaXNfZmlsZSgpOgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InVwZGF0ZV9maWxlX2luZGV4IiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAibm90X2FfZmlsZSIsICJmaWxlX3BhdGgiOiBhYnNfcGF0aH0sCiAgICAgICAgKQogICAgICAgIHJldHVybiBmIkVycm86IG8gY2FtaW5obyBuw6NvIGFwb250YSBwYXJhIHVtIGFycXVpdm86IHtmaWxlcGF0aH0iCgogICAgaWYgZmlsZXBhdGguc3RhdCgpLnN0X3NpemUgPiBNQVhfRklMRV9TSVpFX0JZVEVTOgogICAgICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9lbmQiLAogICAgICAgICAgICB0b29sX25hbWU9InVwZGF0ZV9maWxlX2luZGV4IiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAiZmlsZV90b29fbGFyZ2UiLCAiZmlsZV9wYXRoIjogYWJzX3BhdGh9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJFcnJvOiBhcnF1aXZvIG11aXRvIGdyYW5kZSAoPntNQVhfRklMRV9TSVpFX0JZVEVTIC8vIDEwMjR9S0IpOiB7ZmlsZXBhdGh9IgoKICAgIHNwbGl0dGVyID0gZ2V0X3NwbGl0dGVyKCkKICAgIHRhcmdldHMgPSBfY2xhc3NpZnlfZmlsZV90YXJnZXRzKGZpbGVwYXRoKQoKICAgIGRlbGV0ZWRfcGVyX2JyYW5jaCwgZGVsZXRpb25fZXJyb3JzID0gX3JlbW92ZV9maWxlX2Zyb21fYWxsX2NvbGxlY3Rpb25zKGFic19wYXRoKQoKICAgIGluc2VydGVkX3Blcl9icmFuY2g6IGRpY3Rbc3RyLCBpbnRdID0ge30KICAgIGluZGV4X2Vycm9yczogbGlzdFtzdHJdID0gW10KICAgIGZvciBicmFuY2ggaW4gdGFyZ2V0czoKICAgICAgICB0cnk6CiAgICAgICAgICAgIGluc2VydGVkID0gX2luZGV4X3NpbmdsZV9maWxlX2Zvcl9icmFuY2goCiAgICAgICAgICAgICAgICBmaWxlcGF0aCwKICAgICAgICAgICAgICAgIGJyYW5jaCwKICAgICAgICAgICAgICAgIHNwbGl0dGVyLAogICAgICAgICAgICAgICAgZGVsZXRlX2V4aXN0aW5nPUZhbHNlLCAgIyBqw6EgcmVtb3ZpZG8gZW0gdG9kYXMgYXMgY29sZcOnw7VlcyBhY2ltYQogICAgICAgICAgICApCiAgICAgICAgICAgIGluc2VydGVkX3Blcl9icmFuY2hbYnJhbmNoLmtleV0gPSBpbnNlcnRlZAogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgaW5kZXhfZXJyb3JzLmFwcGVuZChmInticmFuY2gua2V5fToge2V9IikKCiAgICBzdWNjZXNzX2JyYW5jaGVzID0gW2sgZm9yIGssIHYgaW4gaW5zZXJ0ZWRfcGVyX2JyYW5jaC5pdGVtcygpIGlmIHYgPiAwXQoKICAgIGRldGFpbHMgPSB7CiAgICAgICAgInN0YXR1cyI6ICJvayIgaWYgc3VjY2Vzc19icmFuY2hlcyBlbHNlICJlcnJvciIsCiAgICAgICAgImZpbGVfcGF0aCI6IGFic19wYXRoLAogICAgICAgICJ0YXJnZXRzIjogW2Iua2V5IGZvciBiIGluIHRhcmdldHNdLAogICAgICAgICJkZWxldGVkX3Blcl9icmFuY2giOiBkZWxldGVkX3Blcl9icmFuY2gsCiAgICAgICAgImluc2VydGVkX3Blcl9icmFuY2giOiBpbnNlcnRlZF9wZXJfYnJhbmNoLAogICAgICAgICJkZWxldGlvbl9lcnJvcnMiOiBsZW4oZGVsZXRpb25fZXJyb3JzKSwKICAgICAgICAiaW5kZXhfZXJyb3JzIjogbGVuKGluZGV4X2Vycm9ycyksCiAgICB9CiAgICBfbG9nX3Rvb2xfdXNhZ2UoZXZlbnQ9InRvb2xfY2FsbF9lbmQiLCB0b29sX25hbWU9InVwZGF0ZV9maWxlX2luZGV4IiwgZGV0YWlscz1kZXRhaWxzKQoKICAgIGlmIG5vdCBzdWNjZXNzX2JyYW5jaGVzIGFuZCBpbmRleF9lcnJvcnM6CiAgICAgICAgcmV0dXJuICgKICAgICAgICAgICAgIkVycm86IG7Do28gZm9pIHBvc3PDrXZlbCByZWluZGV4YXIgbyBhcnF1aXZvIGVtIG5lbmh1bWEgY29sZcOnw6NvLlxuIgogICAgICAgICAgICBmIkFycXVpdm86IHtmaWxlcGF0aH1cbiIKICAgICAgICAgICAgIkZhbGhhczogIiArICIgfCAiLmpvaW4oaW5kZXhfZXJyb3JzKQogICAgICAgICkKCiAgICBsaW5lcyA9IFsKICAgICAgICAiQXJxdWl2byByZWluZGV4YWRvLiIsCiAgICAgICAgZiIgIEFycXVpdm8gOiB7ZmlsZXBhdGh9IiwKICAgICAgICBmIiAgQ29sZcOnw7VlcyBhbHZvOiB7W2IuY29sbGVjdGlvbl9uYW1lIGZvciBiIGluIHRhcmdldHNdfSIsCiAgICAgICAgZiIgIFJlbW/Dp8O1ZXMgcG9yIGNvbGXDp8Ojbzoge2RlbGV0ZWRfcGVyX2JyYW5jaH0iLAogICAgICAgIGYiICBJbnNlcsOnw7VlcyBwb3IgY29sZcOnw6NvOiB7aW5zZXJ0ZWRfcGVyX2JyYW5jaH0iLAogICAgXQogICAgaWYgZGVsZXRpb25fZXJyb3JzOgogICAgICAgIGxpbmVzLmFwcGVuZCgiICBBdmlzb3MgbmEgcmVtb8Onw6NvOiAiICsgIiB8ICIuam9pbihkZWxldGlvbl9lcnJvcnMpKQogICAgaWYgaW5kZXhfZXJyb3JzOgogICAgICAgIGxpbmVzLmFwcGVuZCgiICBBdmlzb3MgbmEgaW5kZXhhw6fDo286ICIgKyAiIHwgIi5qb2luKGluZGV4X2Vycm9ycykpCiAgICByZXR1cm4gIlxuIi5qb2luKGxpbmVzKQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgVG9vbCAzOiBkZWxldGVfZmlsZV9pbmRleAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQG1jcC50b29sKCkKZGVmIGRlbGV0ZV9maWxlX2luZGV4KGZpbGVfcGF0aDogc3RyKSAtPiBzdHI6CiAgICAiIiIKICAgIFJlbW92ZSB1bSBhcnF1aXZvIGRvIMOtbmRpY2UgZW0gdG9kYXMgYXMgY29sZcOnw7VlcyBnZXJlbmNpYWRhcy4KICAgICIiIgogICAgZmlsZXBhdGggPSBQYXRoKGZpbGVfcGF0aCkucmVzb2x2ZSgpCiAgICBhYnNfcGF0aCA9IHN0cihmaWxlcGF0aCkKCiAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgZXZlbnQ9InRvb2xfY2FsbF9zdGFydCIsCiAgICAgICAgdG9vbF9uYW1lPSJkZWxldGVfZmlsZV9pbmRleCIsCiAgICAgICAgZGV0YWlscz17ImZpbGVfcGF0aCI6IGFic19wYXRofSwKICAgICkKCiAgICBkZWxldGVkX3Blcl9icmFuY2gsIGVycm9ycyA9IF9yZW1vdmVfZmlsZV9mcm9tX2FsbF9jb2xsZWN0aW9ucyhhYnNfcGF0aCkKICAgIHRvdGFsX2RlbGV0ZWQgPSBzdW0oZGVsZXRlZF9wZXJfYnJhbmNoLnZhbHVlcygpKQoKICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICBldmVudD0idG9vbF9jYWxsX2VuZCIsCiAgICAgICAgdG9vbF9uYW1lPSJkZWxldGVfZmlsZV9pbmRleCIsCiAgICAgICAgZGV0YWlscz17CiAgICAgICAgICAgICJzdGF0dXMiOiAib2siIGlmIHRvdGFsX2RlbGV0ZWQgPiAwIGVsc2UgIndhcm5pbmciLAogICAgICAgICAgICAiZmlsZV9wYXRoIjogYWJzX3BhdGgsCiAgICAgICAgICAgICJkZWxldGVkX3Blcl9icmFuY2giOiBkZWxldGVkX3Blcl9icmFuY2gsCiAgICAgICAgICAgICJlcnJvcnMiOiBsZW4oZXJyb3JzKSwKICAgICAgICB9LAogICAgKQoKICAgIGlmIHRvdGFsX2RlbGV0ZWQgPT0gMDoKICAgICAgICBiYXNlID0gZiJOZW5odW0gY2h1bmsgZW5jb250cmFkbyBwYXJhIG8gYXJxdWl2bzoge2Fic19wYXRofSIKICAgICAgICBpZiBlcnJvcnM6CiAgICAgICAgICAgIGJhc2UgKz0gIlxuRmFsaGFzIHBhcmNpYWlzOiAiICsgIiB8ICIuam9pbihlcnJvcnMpCiAgICAgICAgcmV0dXJuIGJhc2UKCiAgICBvdXQgPSBbCiAgICAgICAgIlJlbW92aWRvIGRvIMOtbmRpY2UgY29tIHN1Y2Vzc28uIiwKICAgICAgICBmIiAgQXJxdWl2byA6IHthYnNfcGF0aH0iLAogICAgICAgIGYiICBEZWxlw6fDtWVzIHBvciBjb2xlw6fDo286IHtkZWxldGVkX3Blcl9icmFuY2h9IiwKICAgIF0KICAgIGlmIGVycm9yczoKICAgICAgICBvdXQuYXBwZW5kKCIgIEF2aXNvczogIiArICIgfCAiLmpvaW4oZXJyb3JzKSkKICAgIHJldHVybiAiXG4iLmpvaW4ob3V0KQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgVG9vbCA0OiBpbmRleF9zcGVjaWZpY19mb2xkZXIKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkBtY3AudG9vbCgpCmRlZiBpbmRleF9zcGVjaWZpY19mb2xkZXIoZm9sZGVyX3BhdGg6IHN0cikgLT4gc3RyOgogICAgIiIiCiAgICBJbmRleGEgcmVjdXJzaXZhbWVudGUgdW1hIHBhc3RhIGVtIGNvbGXDp8O1ZXMgc2VwYXJhZGFzIHBvciBkb23DrW5pby4KICAgICIiIgogICAgZm9sZGVyID0gUGF0aChmb2xkZXJfcGF0aCkucmVzb2x2ZSgpCgogICAgX2xvZ190b29sX3VzYWdlKAogICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfc3RhcnQiLAogICAgICAgIHRvb2xfbmFtZT0iaW5kZXhfc3BlY2lmaWNfZm9sZGVyIiwKICAgICAgICBkZXRhaWxzPXsiZm9sZGVyX3BhdGgiOiBzdHIoZm9sZGVyKX0sCiAgICApCgogICAgaWYgbm90IGZvbGRlci5leGlzdHMoKToKICAgICAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfZW5kIiwKICAgICAgICAgICAgdG9vbF9uYW1lPSJpbmRleF9zcGVjaWZpY19mb2xkZXIiLAogICAgICAgICAgICBkZXRhaWxzPXsic3RhdHVzIjogImVycm9yIiwgInJlYXNvbiI6ICJmb2xkZXJfbm90X2ZvdW5kIiwgImZvbGRlcl9wYXRoIjogc3RyKGZvbGRlcil9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJFcnJvOiBwYXN0YSBuw6NvIGVuY29udHJhZGE6IHtmb2xkZXJ9IgoKICAgIGlmIG5vdCBmb2xkZXIuaXNfZGlyKCk6CiAgICAgICAgX2xvZ190b29sX3VzYWdlKAogICAgICAgICAgICBldmVudD0idG9vbF9jYWxsX2VuZCIsCiAgICAgICAgICAgIHRvb2xfbmFtZT0iaW5kZXhfc3BlY2lmaWNfZm9sZGVyIiwKICAgICAgICAgICAgZGV0YWlscz17InN0YXR1cyI6ICJlcnJvciIsICJyZWFzb24iOiAibm90X2FfZm9sZGVyIiwgImZvbGRlcl9wYXRoIjogc3RyKGZvbGRlcil9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJFcnJvOiBvIGNhbWluaG8gbsOjbyDDqSB1bSBkaXJldMOzcmlvOiB7Zm9sZGVyfSIKCiAgICBzcGxpdHRlciA9IGdldF9zcGxpdHRlcigpCgogICAgcHJvY2Vzc2VkX2ZpbGVzID0gMAogICAgYnJhbmNoX2ZpbGVfY291bnRzID0ge2tleTogMCBmb3Iga2V5IGluIEJSQU5DSF9TUEVDU30KICAgIGJyYW5jaF9jaHVua19jb3VudHMgPSB7a2V5OiAwIGZvciBrZXkgaW4gQlJBTkNIX1NQRUNTfQogICAgZXJyb3JfY291bnQgPSAwCiAgICBlcnJvcl9zYW1wbGVzOiBsaXN0W3N0cl0gPSBbXQoKICAgIGZvciBmaWxlcGF0aCBpbiBfc2Nhbl9mb2xkZXIoZm9sZGVyKToKICAgICAgICBwcm9jZXNzZWRfZmlsZXMgKz0gMQogICAgICAgIHRhcmdldHMgPSBfY2xhc3NpZnlfZmlsZV90YXJnZXRzKGZpbGVwYXRoKQoKICAgICAgICBmb3IgYnJhbmNoIGluIHRhcmdldHM6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIG5fY2h1bmtzID0gX2luZGV4X3NpbmdsZV9maWxlX2Zvcl9icmFuY2goZmlsZXBhdGgsIGJyYW5jaCwgc3BsaXR0ZXIpCiAgICAgICAgICAgICAgICBicmFuY2hfZmlsZV9jb3VudHNbYnJhbmNoLmtleV0gKz0gMQogICAgICAgICAgICAgICAgYnJhbmNoX2NodW5rX2NvdW50c1ticmFuY2gua2V5XSArPSBuX2NodW5rcwogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgICAgICBlcnJvcl9jb3VudCArPSAxCiAgICAgICAgICAgICAgICBpZiBsZW4oZXJyb3Jfc2FtcGxlcykgPCAxMDoKICAgICAgICAgICAgICAgICAgICBlcnJvcl9zYW1wbGVzLmFwcGVuZChmIntmaWxlcGF0aC5uYW1lfSBbe2JyYW5jaC5rZXl9XToge2V9IikKCiAgICBpZiBwcm9jZXNzZWRfZmlsZXMgPT0gMDoKICAgICAgICBfbG9nX3Rvb2xfdXNhZ2UoCiAgICAgICAgICAgIGV2ZW50PSJ0b29sX2NhbGxfZW5kIiwKICAgICAgICAgICAgdG9vbF9uYW1lPSJpbmRleF9zcGVjaWZpY19mb2xkZXIiLAogICAgICAgICAgICBkZXRhaWxzPXsic3RhdHVzIjogIm9rIiwgImZvbGRlcl9wYXRoIjogc3RyKGZvbGRlciksICJmaWxlc19wcm9jZXNzZWQiOiAwLCAiY2h1bmtzIjogMCwgImVycm9ycyI6IDB9LAogICAgICAgICkKICAgICAgICByZXR1cm4gZiJOZW5odW0gYXJxdWl2byBpbmRleMOhdmVsIGVuY29udHJhZG8gZW06IHtmb2xkZXJ9IgoKICAgIHRvdGFsX2NodW5rcyA9IHN1bShicmFuY2hfY2h1bmtfY291bnRzLnZhbHVlcygpKQoKICAgIF9sb2dfdG9vbF91c2FnZSgKICAgICAgICBldmVudD0idG9vbF9jYWxsX2VuZCIsCiAgICAgICAgdG9vbF9uYW1lPSJpbmRleF9zcGVjaWZpY19mb2xkZXIiLAogICAgICAgIGRldGFpbHM9ewogICAgICAgICAgICAic3RhdHVzIjogIm9rIiwKICAgICAgICAgICAgImZvbGRlcl9wYXRoIjogc3RyKGZvbGRlciksCiAgICAgICAgICAgICJmaWxlc19wcm9jZXNzZWQiOiBwcm9jZXNzZWRfZmlsZXMsCiAgICAgICAgICAgICJjaHVua3MiOiB0b3RhbF9jaHVua3MsCiAgICAgICAgICAgICJlcnJvcnMiOiBlcnJvcl9jb3VudCwKICAgICAgICAgICAgImJyYW5jaF9maWxlX2NvdW50cyI6IGJyYW5jaF9maWxlX2NvdW50cywKICAgICAgICAgICAgImJyYW5jaF9jaHVua19jb3VudHMiOiBicmFuY2hfY2h1bmtfY291bnRzLAogICAgICAgIH0sCiAgICApCgogICAgcmVwb3J0ID0gWwogICAgICAgICJJbmRleGHDp8OjbyBkYSBwYXN0YSBjb25jbHXDrWRhLiIsCiAgICAgICAgZiIgIFBhc3RhOiB7Zm9sZGVyfSIsCiAgICAgICAgZiIgIEFycXVpdm9zIHByb2Nlc3NhZG9zOiB7cHJvY2Vzc2VkX2ZpbGVzfSIsCiAgICAgICAgZiIgIFRvdGFsIGRlIGNodW5rczoge3RvdGFsX2NodW5rc30iLAogICAgICAgIGYiICBBcnF1aXZvcyBwb3IgYnJhbmNoOiB7YnJhbmNoX2ZpbGVfY291bnRzfSIsCiAgICAgICAgZiIgIENodW5rcyBwb3IgYnJhbmNoOiB7YnJhbmNoX2NodW5rX2NvdW50c30iLAogICAgXQoKICAgIGlmIGVycm9yX2NvdW50OgogICAgICAgIHJlcG9ydC5hcHBlbmQoZiIgIEVycm9zICh7ZXJyb3JfY291bnR9KToiKQogICAgICAgIGZvciBlcnIgaW4gZXJyb3Jfc2FtcGxlczoKICAgICAgICAgICAgcmVwb3J0LmFwcGVuZChmIiAgICAtIHtlcnJ9IikKICAgICAgICBpZiBlcnJvcl9jb3VudCA+IGxlbihlcnJvcl9zYW1wbGVzKToKICAgICAgICAgICAgcmVwb3J0LmFwcGVuZChmIiAgICAuLi4gZSBtYWlzIHtlcnJvcl9jb3VudCAtIGxlbihlcnJvcl9zYW1wbGVzKX0gZXJyb3MuIikKCiAgICByZXR1cm4gIlxuIi5qb2luKHJlcG9ydCkKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvbnRvIGRlIGVudHJhZGEKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBsb2cuaW5mbygiSW5pY2lhbmRvIHNlcnZpZG9yIE1DUCBSQUcgKHN0ZGlvKS4uLiIpCiAgICBsb2cuaW5mbygiQ2hyb21hREI6ICVzOiVzIiwgQ0hST01BX0hPU1QsIENIUk9NQV9QT1JUKQogICAgbG9nLmluZm8oCiAgICAgICAgIkNvbGXDp8O1ZXM6ICVzICglcyksICVzICglcykiLAogICAgICAgIENPTExFQ1RJT05fQ09ERV9KSU5BLAogICAgICAgIEJSQU5DSF9TUEVDU1siamluYV9jb2RlIl0ubW9kZWxfaWQsCiAgICAgICAgQ09MTEVDVElPTl9ET0NfQkdFLAogICAgICAgIEJSQU5DSF9TUEVDU1siYmdlX2RvYyJdLm1vZGVsX2lkLAogICAgKQogICAgbG9nLmluZm8oIk1vZG8gcGFkcsOjbyBkZSBidXNjYTogJXMiLCBTRUFSQ0hfTU9ERV9ERUZBVUxUKQogICAgbG9nLmluZm8oIk1vZGVsbyBzaW5nbGUgcGFkcsOjbzogJXMiLCBCUkFOQ0hfU1BFQ1NbREVGQVVMVF9TSU5HTEVfQlJBTkNIX0tFWV0ubW9kZWxfaWQpCiAgICBsb2cuaW5mbygiUXVhbnRpemFjYW8gSmluYTogJXMiLCBKSU5BX1FVQU5USVpBVElPTikKICAgIGxvZy5pbmZvKCJDb25maWcgZGUgdHVuaW5nIGNhcnJlZ2FkYSBkZTogJXMgKGZvdW5kPSVzKSIsIElOREVYRVJfQ09ORklHX1BBVEgsIGJvb2woSU5ERVhFUl9UVU5JTkdfQ09ORklHKSkKICAgIGxvZy5pbmZvKCJFbWJlZGRpbmcgYmF0Y2ggc2l6ZTogJXMiLCBFTUJFRERJTkdfQkFUQ0hfU0laRSkKICAgIGxvZy5pbmZvKCJDaHVuayBwYXJhbXM6IHNpemU9JXMgb3ZlcmxhcD0lcyIsIENIVU5LX1NJWkUsIENIVU5LX09WRVJMQVApCiAgICBsb2cuaW5mbygiUmVyYW5rZXI6ICVzIChlbmFibGVkPSVzLCBxdWFudD0lcykiLCBSRVJBTktfTU9ERUxfSUQsIFJFUkFOS19FTkFCTEVELCBSRVJBTktFUl9RVUFOVElaQVRJT04pCiAgICBsb2cuaW5mbygiUGFzdGEgZGUgbW9kZWxvcyBsb2NhaXM6ICVzIiwgTU9ERUxfRElSKQogICAgbG9nLmluZm8oIlVzbyBNQ1Agc2Vyw6EgcmVnaXN0cmFkbyBlbTogJXMiLCBNQ1BfVVNBR0VfTE9HX1BBVEgpCgogICAgIyBQcsOpLWFxdWVjZSBzb21lbnRlIGNvbmV4w6NvIENocm9tYTsgbW9kZWxvcyBmaWNhbSBsYXp5IHBhcmEgcG91cGFyIFJBTS4KICAgIHRyeToKICAgICAgICBfZ2V0X2Nocm9tYV9jbGllbnQoKQogICAgICAgIGdldF9jaHJvbWFfY29sbGVjdGlvbihDT0xMRUNUSU9OX0NPREVfSklOQSkKICAgICAgICBnZXRfY2hyb21hX2NvbGxlY3Rpb24oQ09MTEVDVElPTl9ET0NfQkdFKQogICAgICAgIGxvZy5pbmZvKCJDb25leMOjbyBDaHJvbWEgaW5pY2lhbGl6YWRhLiBNb2RlbG9zIHNlcsOjbyBjYXJyZWdhZG9zIHNvYiBkZW1hbmRhLiIpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgbG9nLmVycm9yKCJGYWxoYSBhbyBpbmljaWFsaXphciBDaHJvbWFEQjogJXMiLCBlKQogICAgICAgIGxvZy5lcnJvcigiTyBzZXJ2aWRvciBjb250aW51YXLDoSwgbWFzIGFzIGZlcnJhbWVudGFzIHJldG9ybmFyw6NvIGVycm8gYXTDqSBvIENocm9tYURCIGVzdGFyIGRpc3BvbsOtdmVsLiIpCgogICAgbWNwLnJ1bih0cmFuc3BvcnQ9InN0ZGlvIikK" | base64 -d > "${EXTRACT_DIR}/mcp_server.py"
|
|
582
|
+
echo "IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCgoiIiIKZG93bmxvYWRfbW9kZWxfZnJvbV9odWdnaW5mYWNlLnB5CgpDYW1hZGEgZGUgZG93bmxvYWQgZGUgbW9kZWxvcyBjb20gcHJpb3JpZGFkZSBkZSBwcm92ZWRvcmVzIGUgZmFsbGJhY2sgZGUgbW9kZWxvLgpGbHV4byBwYWRyw6NvOgoxKSB0ZW50YSBiYWl4YXIgbyBtb2RlbG8gcHJlZmVyaWRvIHZpYSBIdWdnaW5nIEZhY2U7CjIpIHNlIGZhbGhhciwgdGVudGEgcHJvdmVkb3JlcyBhbHRlcm5hdGl2b3MgKHF1YW5kbyBkaXNwb27DrXZlaXMpOwozKSBzZSBvIG1vZGVsbyBwcmVmZXJpZG8gZmFsaGFyIGVtIHRvZG9zIG9zIHByb3ZlZG9yZXMsIHRlbnRhIG1vZGVsbyBmYWxsYmFjay4KIiIiCgpmcm9tIGRhdGFjbGFzc2VzIGltcG9ydCBkYXRhY2xhc3MKaW1wb3J0IGdldHBhc3MKaW1wb3J0IG9zCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAppbXBvcnQgc2h1dGlsCmltcG9ydCBzeXMKZnJvbSB0eXBpbmcgaW1wb3J0IFByb3RvY29sCgoKY2xhc3MgTW9kZWxEb3dubG9hZFN0cmF0ZWd5KFByb3RvY29sKToKICAgIG5hbWU6IHN0cgoKICAgIGRlZiBkb3dubG9hZChzZWxmLCBtb2RlbF9pZDogc3RyLCBsb2NhbF9kaXI6IFBhdGgpIC0+IE5vbmU6CiAgICAgICAgIiIiQmFpeGEgbW9kZWxfaWQgcGFyYSBsb2NhbF9kaXIgb3UgbGV2YW50YSBleGNlw6fDo28uIiIiCgoKY2xhc3MgSHVnZ2luZ0ZhY2VEb3dubG9hZFN0cmF0ZWd5OgogICAgbmFtZSA9ICJodWdnaW5nZmFjZSIKCiAgICBkZWYgZG93bmxvYWQoc2VsZiwgbW9kZWxfaWQ6IHN0ciwgbG9jYWxfZGlyOiBQYXRoKSAtPiBOb25lOgogICAgICAgIGZyb20gaHVnZ2luZ2ZhY2VfaHViIGltcG9ydCBzbmFwc2hvdF9kb3dubG9hZAoKICAgICAgICBoZl90b2tlbiA9IG9zLmVudmlyb24uZ2V0KCJIRl9UT0tFTiIpIG9yIG9zLmVudmlyb24uZ2V0KCJIVUdHSU5HX0ZBQ0VfSFVCX1RPS0VOIikKICAgICAgICBfZG93bmxvYWRfd2l0aF9oZl90b2tlbl9yZWNvdmVyeSgKICAgICAgICAgICAgcmVwb19pZD1tb2RlbF9pZCwKICAgICAgICAgICAgbG9jYWxfZGlyPWxvY2FsX2RpciwKICAgICAgICAgICAgaGZfdG9rZW49aGZfdG9rZW4sCiAgICAgICAgICAgIHNuYXBzaG90X2Rvd25sb2FkPXNuYXBzaG90X2Rvd25sb2FkLAogICAgICAgICkKCgpAZGF0YWNsYXNzKGZyb3plbj1UcnVlKQpjbGFzcyBEb3dubG9hZFNlbGVjdGlvbjoKICAgIG1vZGVsX2lkOiBzdHIKICAgIHByb3ZpZGVyOiBzdHIKICAgIGxvY2FsX2RpcjogUGF0aAoKCmRlZiBfbG9hZF9vcHRpb25hbF9zdHJhdGVnaWVzKCkgLT4gbGlzdFtNb2RlbERvd25sb2FkU3RyYXRlZ3ldOgogICAgc3RyYXRlZ2llczogbGlzdFtNb2RlbERvd25sb2FkU3RyYXRlZ3ldID0gW10KCiAgICB0cnk6CiAgICAgICAgZnJvbSBkb3dubG9hZF9tb2RlbF9mcm9tX21vZGVsc2NvcGUgaW1wb3J0IE1vZGVsU2NvcGVEb3dubG9hZFN0cmF0ZWd5CgogICAgICAgIHN0cmF0ZWdpZXMuYXBwZW5kKE1vZGVsU2NvcGVEb3dubG9hZFN0cmF0ZWd5KCkpCiAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICMgUHJvdmlkZXIgb3BjaW9uYWw6IGlnbm9yYSBzZSBuw6NvIGVzdGl2ZXIgZGlzcG9uw612ZWwgbm8gYW1iaWVudGUuCiAgICAgICAgcGFzcwoKICAgIHJldHVybiBzdHJhdGVnaWVzCgoKZGVmIGJ1aWxkX2RlZmF1bHRfc3RyYXRlZ2llcygpIC0+IGxpc3RbTW9kZWxEb3dubG9hZFN0cmF0ZWd5XToKICAgICIiIkZhY3Rvcnkgc2ltcGxlczogb3JkZW0gZGUgcHJpb3JpZGFkZSBkZSBwcm92ZWRvcmVzIGRlIGRvd25sb2FkLiIiIgogICAgcmV0dXJuIFtIdWdnaW5nRmFjZURvd25sb2FkU3RyYXRlZ3koKSwgKl9sb2FkX29wdGlvbmFsX3N0cmF0ZWdpZXMoKV0KCgpfTU9ERUxfUkVBRFlfTUFSS0VSID0gIi5kb3dubG9hZF9jb21wbGV0ZSIKCgpkZWYgX3ByZXBhcmVfZGVzdGluYXRpb24obG9jYWxfZGlyOiBQYXRoLCAqLCBjbGVhbjogYm9vbCkgLT4gTm9uZToKICAgIGlmIGNsZWFuIGFuZCBsb2NhbF9kaXIuZXhpc3RzKCk6CiAgICAgICAgc2h1dGlsLnJtdHJlZShsb2NhbF9kaXIpCiAgICBsb2NhbF9kaXIubWtkaXIocGFyZW50cz1UcnVlLCBleGlzdF9vaz1UcnVlKQoKCmRlZiBfbW9kZWxfY2FjaGVfZGlyKGJhc2VfZGlyOiBQYXRoLCBtb2RlbF9pZDogc3RyKSAtPiBQYXRoOgogICAgIyBFdml0YSBjb2xpc8OjbyBkZSBub21lcyBlIG1hbnTDqW0gZGlyZXTDs3JpbyBzZWd1cm8gZW0gcXVhbHF1ZXIgU08uCiAgICBzYWZlX25hbWUgPSBtb2RlbF9pZC5yZXBsYWNlKCIvIiwgIl9fIikucmVwbGFjZSgiOiIsICJfIikKICAgIHJldHVybiBiYXNlX2RpciAvIHNhZmVfbmFtZQoKCmRlZiBfY2FjaGVfcmVhZHkobG9jYWxfZGlyOiBQYXRoKSAtPiBib29sOgogICAgbWFya2VyID0gbG9jYWxfZGlyIC8gX01PREVMX1JFQURZX01BUktFUgogICAgaWYgbm90IG1hcmtlci5leGlzdHMoKSBvciBub3QgbG9jYWxfZGlyLmV4aXN0cygpOgogICAgICAgIHJldHVybiBGYWxzZQogICAgcmV0dXJuIGFueShwLm5hbWUgIT0gX01PREVMX1JFQURZX01BUktFUiBmb3IgcCBpbiBsb2NhbF9kaXIuaXRlcmRpcigpKQoKCmRlZiBfbWFya19jYWNoZV9yZWFkeShsb2NhbF9kaXI6IFBhdGgpIC0+IE5vbmU6CiAgICAobG9jYWxfZGlyIC8gX01PREVMX1JFQURZX01BUktFUikud3JpdGVfdGV4dCgib2tcbiIsIGVuY29kaW5nPSJ1dGYtOCIpCgoKZGVmIF9zdGF0dXNfY29kZV9mcm9tX2Vycm9yKGV4YzogRXhjZXB0aW9uKSAtPiBpbnQgfCBOb25lOgogICAgcmVzcG9uc2UgPSBnZXRhdHRyKGV4YywgInJlc3BvbnNlIiwgTm9uZSkKICAgIGlmIHJlc3BvbnNlIGlzIE5vbmU6CiAgICAgICAgcmV0dXJuIE5vbmUKICAgIHN0YXR1c19jb2RlID0gZ2V0YXR0cihyZXNwb25zZSwgInN0YXR1c19jb2RlIiwgTm9uZSkKICAgIGlmIGlzaW5zdGFuY2Uoc3RhdHVzX2NvZGUsIGludCk6CiAgICAgICAgcmV0dXJuIHN0YXR1c19jb2RlCiAgICByZXR1cm4gTm9uZQoKCmRlZiBfaXNfaW52YWxpZF9oZl90b2tlbl9lcnJvcihleGM6IEV4Y2VwdGlvbikgLT4gYm9vbDoKICAgIG1lc3NhZ2UgPSBzdHIoZXhjKS5sb3dlcigpCiAgICBzdGF0dXNfY29kZSA9IF9zdGF0dXNfY29kZV9mcm9tX2Vycm9yKGV4YykKICAgIHRva2VuX2tleXdvcmRzID0gKCJpbnZhbGlkIHRva2VuIiwgInRva2VuIGlzIGludmFsaWQiLCAidW5hdXRob3JpemVkIiwgIjQwMSIpCiAgICBpZiBzdGF0dXNfY29kZSA9PSA0MDE6CiAgICAgICAgcmV0dXJuIFRydWUKICAgIHJldHVybiBhbnkoa2V5d29yZCBpbiBtZXNzYWdlIGZvciBrZXl3b3JkIGluIHRva2VuX2tleXdvcmRzKQoKCmRlZiBfcHJvbXB0X3JlY292ZXJfaW52YWxpZF9oZl90b2tlbigpIC0+IHR1cGxlW3N0ciwgc3RyIHwgTm9uZV06CiAgICBpZiBub3Qgc3lzLnN0ZGluLmlzYXR0eSgpOgogICAgICAgIHJldHVybiAoIm5vLXRva2VuIiwgTm9uZSkKCiAgICB3aGlsZSBUcnVlOgogICAgICAgIHByaW50KAogICAgICAgICAgICAiWyFdIE8gdG9rZW4gZG8gSHVnZ2luZ0ZhY2UgcGFyZWNlIGludsOhbGlkby4gRXNjb2xoYTogIgogICAgICAgICAgICAiWzFdIGluZm9ybWFyIG5vdm8gdG9rZW4sIFsyXSBjb250aW51YXIgc2VtIHRva2VuLiIsCiAgICAgICAgICAgIGZpbGU9c3lzLnN0ZGVyciwKICAgICAgICApCiAgICAgICAgYW5zd2VyID0gaW5wdXQoIj4gRXNjb2xoYSBbMS8yXTogIikuc3RyaXAoKS5sb3dlcigpCiAgICAgICAgaWYgYW5zd2VyIGluIHsiMSIsICJub3ZvIiwgIm5ldyJ9OgogICAgICAgICAgICBuZXdfdG9rZW4gPSBnZXRwYXNzLmdldHBhc3MoIkNvbGUgbyBub3ZvIEhGX1RPS0VOOiAiKS5zdHJpcCgpCiAgICAgICAgICAgIGlmIG5ld190b2tlbjoKICAgICAgICAgICAgICAgIHJldHVybiAoIm5ldy10b2tlbiIsIG5ld190b2tlbikKICAgICAgICAgICAgcHJpbnQoIlshXSBUb2tlbiB2YXppby4gVGVudGUgbm92YW1lbnRlLiIsIGZpbGU9c3lzLnN0ZGVycikKICAgICAgICAgICAgY29udGludWUKICAgICAgICBpZiBhbnN3ZXIgaW4geyIyIiwgIiIsICJzZW0iLCAibm8ifToKICAgICAgICAgICAgcmV0dXJuICgibm8tdG9rZW4iLCBOb25lKQogICAgICAgIHByaW50KCJbIV0gT3DDp8OjbyBpbnbDoWxpZGEuIERpZ2l0ZSAxIG91IDIuIiwgZmlsZT1zeXMuc3RkZXJyKQoKCmRlZiBfZG93bmxvYWRfd2l0aF9oZl90b2tlbl9yZWNvdmVyeSgKICAgICosCiAgICByZXBvX2lkOiBzdHIsCiAgICBsb2NhbF9kaXI6IFBhdGgsCiAgICBoZl90b2tlbjogc3RyIHwgTm9uZSwKICAgIHNuYXBzaG90X2Rvd25sb2FkLAopIC0+IE5vbmU6CiAgICBhdHRlbXB0X3Rva2VuID0gaGZfdG9rZW4KCiAgICB3aGlsZSBUcnVlOgogICAgICAgIHRyeToKICAgICAgICAgICAgc25hcHNob3RfZG93bmxvYWQoCiAgICAgICAgICAgICAgICByZXBvX2lkPXJlcG9faWQsCiAgICAgICAgICAgICAgICBsb2NhbF9kaXI9c3RyKGxvY2FsX2RpciksCiAgICAgICAgICAgICAgICB0b2tlbj1hdHRlbXB0X3Rva2VuLAogICAgICAgICAgICApCiAgICAgICAgICAgIGlmIGF0dGVtcHRfdG9rZW46CiAgICAgICAgICAgICAgICBvcy5lbnZpcm9uWyJIRl9UT0tFTiJdID0gYXR0ZW1wdF90b2tlbgogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgb3MuZW52aXJvbi5wb3AoIkhGX1RPS0VOIiwgTm9uZSkKICAgICAgICAgICAgcmV0dXJuCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgICAgIGlmIGF0dGVtcHRfdG9rZW4gYW5kIF9pc19pbnZhbGlkX2hmX3Rva2VuX2Vycm9yKGV4Yyk6CiAgICAgICAgICAgICAgICBwcmludCgKICAgICAgICAgICAgICAgICAgICAiWyFdIEZhbGhhIGRlIGF1dGVudGljYcOnw6NvIG5vIEh1Z2dpbmdGYWNlIGNvbSBvIHRva2VuIGF0dWFsLiAiCiAgICAgICAgICAgICAgICAgICAgIlZvY8OqIHBvZGUgaW5mb3JtYXIgb3V0cm8gdG9rZW4gb3Ugc2VndWlyIHNlbSB0b2tlbi4iLAogICAgICAgICAgICAgICAgICAgIGZpbGU9c3lzLnN0ZGVyciwKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGFjdGlvbiwgcmVwbGFjZW1lbnQgPSBfcHJvbXB0X3JlY292ZXJfaW52YWxpZF9oZl90b2tlbigpCiAgICAgICAgICAgICAgICBpZiBhY3Rpb24gPT0gIm5ldy10b2tlbiIgYW5kIHJlcGxhY2VtZW50OgogICAgICAgICAgICAgICAgICAgIGF0dGVtcHRfdG9rZW4gPSByZXBsYWNlbWVudAogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICBhdHRlbXB0X3Rva2VuID0gTm9uZQogICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgcmFpc2UKCgpkZWYgZG93bmxvYWRfbW9kZWxfd2l0aF9mYWxsYmFjaygKICAgIHByZWZlcnJlZF9tb2RlbF9pZDogc3RyLAogICAgZmFsbGJhY2tfbW9kZWxfaWQ6IHN0ciwKICAgIGxvY2FsX2RpcjogUGF0aCwKICAgIHN0cmF0ZWdpZXM6IGxpc3RbTW9kZWxEb3dubG9hZFN0cmF0ZWd5XSB8IE5vbmUgPSBOb25lLAopIC0+IERvd25sb2FkU2VsZWN0aW9uOgogICAgIiIiCiAgICBUZW50YSBiYWl4YXIgYHByZWZlcnJlZF9tb2RlbF9pZGA7IHNlIGZhbGhhciBlbSB0b2RvcyBvcyBwcm92ZWRvcmVzLAogICAgdGVudGEgYGZhbGxiYWNrX21vZGVsX2lkYC4KICAgICIiIgogICAgYmFzZV9kaXIgPSBsb2NhbF9kaXIuZXhwYW5kdXNlcigpCiAgICBiYXNlX2Rpci5ta2RpcihwYXJlbnRzPVRydWUsIGV4aXN0X29rPVRydWUpCiAgICBwcm92aWRlcnMgPSBzdHJhdGVnaWVzIG9yIGJ1aWxkX2RlZmF1bHRfc3RyYXRlZ2llcygpCiAgICBlcnJvcnM6IGxpc3Rbc3RyXSA9IFtdCgogICAgZm9yIG1vZGVsX2lkIGluIChwcmVmZXJyZWRfbW9kZWxfaWQsIGZhbGxiYWNrX21vZGVsX2lkKToKICAgICAgICBtb2RlbF9sb2NhbF9kaXIgPSBfbW9kZWxfY2FjaGVfZGlyKGJhc2VfZGlyLCBtb2RlbF9pZCkKICAgICAgICBpZiBfY2FjaGVfcmVhZHkobW9kZWxfbG9jYWxfZGlyKToKICAgICAgICAgICAgcmV0dXJuIERvd25sb2FkU2VsZWN0aW9uKAogICAgICAgICAgICAgICAgbW9kZWxfaWQ9bW9kZWxfaWQsCiAgICAgICAgICAgICAgICBwcm92aWRlcj0ibG9jYWwtY2FjaGUiLAogICAgICAgICAgICAgICAgbG9jYWxfZGlyPW1vZGVsX2xvY2FsX2RpciwKICAgICAgICAgICAgKQoKICAgICAgICBmb3Igc3RyYXRlZ3kgaW4gcHJvdmlkZXJzOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBwcmludCgKICAgICAgICAgICAgICAgICAgICBmIlsrXSBJbmljaWFuZG8gZG93bmxvYWQgZG8gbW9kZWxvICd7bW9kZWxfaWR9JyB2aWEge3N0cmF0ZWd5Lm5hbWV9IGVtOiB7bW9kZWxfbG9jYWxfZGlyfSIsCiAgICAgICAgICAgICAgICAgICAgZmlsZT1zeXMuc3RkZXJyLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgX3ByZXBhcmVfZGVzdGluYXRpb24obW9kZWxfbG9jYWxfZGlyLCBjbGVhbj1UcnVlKQogICAgICAgICAgICAgICAgc3RyYXRlZ3kuZG93bmxvYWQobW9kZWxfaWQ9bW9kZWxfaWQsIGxvY2FsX2Rpcj1tb2RlbF9sb2NhbF9kaXIpCiAgICAgICAgICAgICAgICBfbWFya19jYWNoZV9yZWFkeShtb2RlbF9sb2NhbF9kaXIpCiAgICAgICAgICAgICAgICByZXR1cm4gRG93bmxvYWRTZWxlY3Rpb24oCiAgICAgICAgICAgICAgICAgICAgbW9kZWxfaWQ9bW9kZWxfaWQsCiAgICAgICAgICAgICAgICAgICAgcHJvdmlkZXI9c3RyYXRlZ3kubmFtZSwKICAgICAgICAgICAgICAgICAgICBsb2NhbF9kaXI9bW9kZWxfbG9jYWxfZGlyLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICAgICAgICAgIGVycm9ycy5hcHBlbmQoZiJ7c3RyYXRlZ3kubmFtZX06e21vZGVsX2lkfToge2V4Y30iKQoKICAgIHJhaXNlIFJ1bnRpbWVFcnJvcigKICAgICAgICAiRmFsaGEgbm8gZG93bmxvYWQgZG9zIG1vZGVsb3MgZW0gdG9kb3Mgb3MgcHJvdmVkb3JlcyBjb25maWd1cmFkb3MuICIKICAgICAgICArICIgfCAiLmpvaW4oZXJyb3JzKQogICAgKQo=" | base64 -d > "${EXTRACT_DIR}/download_model_from_hugginface.py"
|
|
583
|
+
echo "IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCgoiIiIKUHJvdmlkZXIgb3BjaW9uYWwgZGUgZG93bmxvYWQgdmlhIE1vZGVsU2NvcGUuClVzYWRvIGFwZW5hcyBzZSBvIHBhY290ZSBgbW9kZWxzY29wZWAgZXN0aXZlciBpbnN0YWxhZG8uCiIiIgoKZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgoKY2xhc3MgTW9kZWxTY29wZURvd25sb2FkU3RyYXRlZ3k6CiAgICBuYW1lID0gIm1vZGVsc2NvcGUiCgogICAgZGVmIGRvd25sb2FkKHNlbGYsIG1vZGVsX2lkOiBzdHIsIGxvY2FsX2RpcjogUGF0aCkgLT4gTm9uZToKICAgICAgICB0cnk6CiAgICAgICAgICAgIGZyb20gbW9kZWxzY29wZS5odWIuc25hcHNob3RfZG93bmxvYWQgaW1wb3J0IHNuYXBzaG90X2Rvd25sb2FkCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgICAgIHJhaXNlIFJ1bnRpbWVFcnJvcigKICAgICAgICAgICAgICAgICJQYWNvdGUgYG1vZGVsc2NvcGVgIGluZGlzcG9uw612ZWwgcGFyYSBwcm92aWRlciBhbHRlcm5hdGl2byIKICAgICAgICAgICAgKSBmcm9tIGV4YwoKICAgICAgICBzbmFwc2hvdF9kb3dubG9hZCgKICAgICAgICAgICAgbW9kZWxfaWQ9bW9kZWxfaWQsCiAgICAgICAgICAgIGxvY2FsX2Rpcj1zdHIobG9jYWxfZGlyKSwKICAgICAgICApCg==" | base64 -d > "${EXTRACT_DIR}/download_model_from_modelscope.py"
|
|
584
|
+
|
|
585
|
+
log_info "$(t extracted_to): ${EXTRACT_DIR}"
|
|
586
|
+
sed -i -E 's/^CHROMA_PORT = 8000$/CHROMA_PORT = _env_int("MCP_CHROMA_PORT", 8000, min_value=1)/' "${EXTRACT_DIR}/indexer_full.py" || true
|
|
587
|
+
|
|
588
|
+
# ---------------------------------------------------------------------------
|
|
589
|
+
# Verifica pré-requisitos
|
|
590
|
+
# ---------------------------------------------------------------------------
|
|
591
|
+
log_section "$(t section_prereq)"
|
|
592
|
+
|
|
593
|
+
if ! command -v python3 &>/dev/null; then
|
|
594
|
+
log_error "$(t py_missing)"
|
|
595
|
+
exit 1
|
|
596
|
+
fi
|
|
597
|
+
PY_VER=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
|
|
598
|
+
PY_MAJOR=$(echo "$PY_VER" | cut -d. -f1)
|
|
599
|
+
PY_MINOR=$(echo "$PY_VER" | cut -d. -f2)
|
|
600
|
+
if [[ "$PY_MAJOR" -lt 3 ]] || [[ "$PY_MAJOR" -eq 3 && "$PY_MINOR" -lt 10 ]]; then
|
|
601
|
+
log_error "$(t py_min) $PY_VER"
|
|
602
|
+
exit 1
|
|
603
|
+
fi
|
|
604
|
+
if ! python3 -m venv --help &>/dev/null; then
|
|
605
|
+
log_error "$(t py_venv_missing)"
|
|
606
|
+
exit 1
|
|
607
|
+
fi
|
|
608
|
+
log_info "$(t py_ok) $PY_VER OK."
|
|
609
|
+
|
|
610
|
+
if ! command -v docker &>/dev/null; then
|
|
611
|
+
log_error "$(t docker_missing)"
|
|
612
|
+
exit 1
|
|
613
|
+
fi
|
|
614
|
+
if ! docker info &>/dev/null 2>&1; then
|
|
615
|
+
log_error "$(t docker_daemon)"
|
|
616
|
+
exit 1
|
|
617
|
+
fi
|
|
618
|
+
if docker compose version &>/dev/null 2>&1; then
|
|
619
|
+
DOCKER_COMPOSE_CMD="docker compose"
|
|
620
|
+
elif command -v docker-compose &>/dev/null; then
|
|
621
|
+
DOCKER_COMPOSE_CMD="docker-compose"
|
|
622
|
+
else
|
|
623
|
+
log_error "$(t compose_missing)"
|
|
624
|
+
exit 1
|
|
625
|
+
fi
|
|
626
|
+
log_info "$(t docker_ok)"
|
|
627
|
+
|
|
628
|
+
if ! command -v curl &>/dev/null; then
|
|
629
|
+
log_warn "$(t curl_missing)"
|
|
630
|
+
HAS_CURL=false
|
|
631
|
+
else
|
|
632
|
+
HAS_CURL=true
|
|
633
|
+
fi
|
|
634
|
+
|
|
635
|
+
if EXISTING_CHROMA_PORT="$(extract_chroma_port_from_compose "${DOCKER_COMPOSE_FILE_PATH}")"; then
|
|
636
|
+
CHROMA_PORT="${EXISTING_CHROMA_PORT}"
|
|
637
|
+
fi
|
|
638
|
+
|
|
639
|
+
reset_rag_environment() {
|
|
640
|
+
log_section "$(t reset_start)"
|
|
641
|
+
rm -rf "${VENV_DIR}" "${RAG_DB_DIR}" "${DOCKER_COMPOSE_DIR}" "${MODEL_CACHE_DIR}"
|
|
642
|
+
rm -f "${MCP_SERVER_DEST}" "${MODEL_DL_HF_DEST}" "${MODEL_DL_MS_DEST}"
|
|
643
|
+
log_info "$(t reset_done)"
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if [[ "$CHANGE_MODEL" == "true" ]]; then
|
|
647
|
+
if [[ "$ONLY_INDEX" == "true" ]]; then
|
|
648
|
+
log_warn "Ignoring --only-index because --change-model requires full setup."
|
|
649
|
+
ONLY_INDEX=false
|
|
650
|
+
fi
|
|
651
|
+
log_info "$(t change_model_requested)"
|
|
652
|
+
log_warn "ATENÇÃO/WARNING: trocar o modelo exige zerar o ChromaDB e reindexar todos os projetos."
|
|
653
|
+
if [[ -t 0 ]]; then
|
|
654
|
+
if ! ask_yes_no_loop "Confirmar reset do ChromaDB e nova indexação? $YES_NO_HINT "; then
|
|
655
|
+
log_info "Operação cancelada pelo usuário."
|
|
656
|
+
exit 0
|
|
657
|
+
fi
|
|
658
|
+
else
|
|
659
|
+
log_warn "Sem terminal interativo: seguindo com reset total por --change-model."
|
|
660
|
+
fi
|
|
661
|
+
export MCP_FORCE_MODEL_RECONFIG=1
|
|
662
|
+
REINSTALL=true
|
|
663
|
+
reset_rag_environment
|
|
664
|
+
fi
|
|
665
|
+
|
|
666
|
+
prompt_optional_hf_token() {
|
|
667
|
+
# Evita perguntar duas vezes no mesmo run.
|
|
668
|
+
if [[ -n "${HF_TOKEN_PROMPTED:-}" ]]; then
|
|
669
|
+
return
|
|
670
|
+
fi
|
|
671
|
+
HF_TOKEN_PROMPTED=1
|
|
672
|
+
|
|
673
|
+
# Se já veio no ambiente, reaproveita sem perguntar.
|
|
674
|
+
if [[ -n "${HF_TOKEN:-}" || -n "${HUGGING_FACE_HUB_TOKEN:-}" ]]; then
|
|
675
|
+
log_info "$(t hf_token_detected)"
|
|
676
|
+
return
|
|
677
|
+
fi
|
|
678
|
+
|
|
679
|
+
# Só pergunta em terminal interativo.
|
|
680
|
+
if [[ ! -t 0 ]]; then
|
|
681
|
+
log_info "$(t non_interactive_no_hf)"
|
|
682
|
+
return
|
|
683
|
+
fi
|
|
684
|
+
|
|
685
|
+
echo ""
|
|
686
|
+
echo -e "${BOLD}$(t hf_token_title)${NC}"
|
|
687
|
+
echo -e "${DIM}$(t hf_token_desc)${NC}"
|
|
688
|
+
if ask_yes_no_loop "$(t hf_prompt_now)"; then
|
|
689
|
+
read -r -s -p "$(t hf_prompt_paste)" INPUT_HF_TOKEN
|
|
690
|
+
echo ""
|
|
691
|
+
if [[ -n "${INPUT_HF_TOKEN}" ]]; then
|
|
692
|
+
export HF_TOKEN="${INPUT_HF_TOKEN}"
|
|
693
|
+
log_info "$(t hf_set)"
|
|
694
|
+
else
|
|
695
|
+
log_warn "$(t hf_empty)"
|
|
696
|
+
fi
|
|
697
|
+
else
|
|
698
|
+
log_info "$(t hf_continue_no)"
|
|
699
|
+
fi
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
run_indexer_with_diagnostics() {
|
|
703
|
+
local target_project_dir="$1"
|
|
704
|
+
local indexer_status=0
|
|
705
|
+
|
|
706
|
+
local tokenizers_parallelism="${TOKENIZERS_PARALLELISM:-false}"
|
|
707
|
+
local force_model_reconfig="${MCP_FORCE_MODEL_RECONFIG:-0}"
|
|
708
|
+
|
|
709
|
+
set +e
|
|
710
|
+
TOKENIZERS_PARALLELISM="${tokenizers_parallelism}" \
|
|
711
|
+
MCP_FORCE_MODEL_RECONFIG="${force_model_reconfig}" \
|
|
712
|
+
MCP_CHROMA_PORT="${CHROMA_PORT}" \
|
|
713
|
+
"${VENV_PYTHON}" "${EXTRACT_DIR}/indexer_full.py" "${target_project_dir}"
|
|
714
|
+
indexer_status=$?
|
|
715
|
+
set -e
|
|
716
|
+
|
|
717
|
+
if [[ "$indexer_status" -eq 137 ]]; then
|
|
718
|
+
log_error "$(t index_oom_title)"
|
|
719
|
+
log_error "$(t index_oom_reason)"
|
|
720
|
+
log_error "$(t index_oom_hw_recommend)"
|
|
721
|
+
log_error "$(t index_oom_next_1)"
|
|
722
|
+
log_error "$(t index_oom_next_2)"
|
|
723
|
+
elif [[ "$indexer_status" -ne 0 ]]; then
|
|
724
|
+
log_error "$(t index_failed_code) ${indexer_status}"
|
|
725
|
+
fi
|
|
726
|
+
|
|
727
|
+
return "$indexer_status"
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
# ---------------------------------------------------------------------------
|
|
731
|
+
# Modo --only-index
|
|
732
|
+
# ---------------------------------------------------------------------------
|
|
733
|
+
if [[ "$ONLY_INDEX" == "true" ]]; then
|
|
734
|
+
log_section "$(t only_index_mode)"
|
|
735
|
+
if [[ ! -f "${VENV_PYTHON}" ]]; then
|
|
736
|
+
log_error "$(t venv_not_found) ${VENV_DIR}. $(t run_without_only_index)"
|
|
737
|
+
exit 1
|
|
738
|
+
fi
|
|
739
|
+
if [[ ! -d "${PROJECT_DIR}" ]]; then
|
|
740
|
+
log_error "$(t path_not_found) ${PROJECT_DIR}"
|
|
741
|
+
exit 1
|
|
742
|
+
fi
|
|
743
|
+
prompt_optional_hf_token
|
|
744
|
+
log_info "$(t indexing) ${PROJECT_DIR}"
|
|
745
|
+
run_indexer_with_diagnostics "${PROJECT_DIR}"
|
|
746
|
+
log_info "$(t indexing_done)"
|
|
747
|
+
exit 0
|
|
748
|
+
fi
|
|
749
|
+
|
|
750
|
+
# ---------------------------------------------------------------------------
|
|
751
|
+
# Cria/atualiza venv e instala dependências
|
|
752
|
+
# ---------------------------------------------------------------------------
|
|
753
|
+
log_section "$(t section_venv)"
|
|
754
|
+
|
|
755
|
+
DEPS_OK=false
|
|
756
|
+
if [[ "$REINSTALL" == "false" ]] && [[ -f "${VENV_PYTHON}" ]]; then
|
|
757
|
+
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
|
|
758
|
+
log_info "$(t deps_ok)"
|
|
759
|
+
DEPS_OK=true
|
|
760
|
+
else
|
|
761
|
+
log_warn "$(t deps_incompatible)"
|
|
762
|
+
fi
|
|
763
|
+
fi
|
|
764
|
+
|
|
765
|
+
if [[ "$DEPS_OK" == "false" ]]; then
|
|
766
|
+
if [[ ! -f "${VENV_PYTHON}" ]]; then
|
|
767
|
+
log_info "$(t creating_venv) ${VENV_DIR}..."
|
|
768
|
+
python3 -m venv "${VENV_DIR}"
|
|
769
|
+
log_info "$(t venv_created)"
|
|
770
|
+
fi
|
|
771
|
+
log_info "$(t upgrading_pip)"
|
|
772
|
+
"${VENV_PIP}" install --upgrade pip
|
|
773
|
+
|
|
774
|
+
echo ""
|
|
775
|
+
echo -e "${YELLOW} $(t installing_packages)${NC}"
|
|
776
|
+
echo -e "${DIM} $(t model_download_note)${NC}"
|
|
777
|
+
echo ""
|
|
778
|
+
|
|
779
|
+
# Instala com output visível (sem --quiet) para o usuário acompanhar
|
|
780
|
+
"${VENV_PIP}" install --progress-bar on -r "${EXTRACT_DIR}/requirements.txt"
|
|
781
|
+
|
|
782
|
+
echo ""
|
|
783
|
+
log_info "$(t deps_installed)"
|
|
784
|
+
fi
|
|
785
|
+
|
|
786
|
+
# ---------------------------------------------------------------------------
|
|
787
|
+
# ChromaDB via Docker
|
|
788
|
+
# ---------------------------------------------------------------------------
|
|
789
|
+
log_section "$(t section_chroma)"
|
|
790
|
+
|
|
791
|
+
mkdir -p "${RAG_DB_DIR}" "${DOCKER_COMPOSE_DIR}"
|
|
792
|
+
|
|
793
|
+
if [[ "$REINSTALL" == "true" ]] || [[ ! -f "${DOCKER_COMPOSE_FILE_PATH}" ]]; then
|
|
794
|
+
if [[ "${CHROMA_PORT_FROM_ENV}" == "true" ]]; then
|
|
795
|
+
CHROMA_PORT="$(choose_chroma_port_for_install "${CHROMA_PORT_DEFAULT}" "${CHROMA_PORT}")"
|
|
796
|
+
else
|
|
797
|
+
CHROMA_PORT="$(choose_chroma_port_for_install "${CHROMA_PORT_DEFAULT}")"
|
|
798
|
+
fi
|
|
799
|
+
cp "${EXTRACT_DIR}/docker-compose.yml" "${DOCKER_COMPOSE_FILE_PATH}"
|
|
800
|
+
sed -i -E 's/"[0-9]{1,5}:8000"/"'"${CHROMA_PORT}"':8000"/g' "${DOCKER_COMPOSE_FILE_PATH}"
|
|
801
|
+
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}"
|
|
802
|
+
log_info "$(t compose_installed) ${DOCKER_COMPOSE_DIR}"
|
|
803
|
+
log_info "$(t chroma_port_selected) ${CHROMA_PORT}"
|
|
804
|
+
else
|
|
805
|
+
log_info "$(t compose_keep)"
|
|
806
|
+
if EXISTING_CHROMA_PORT="$(extract_chroma_port_from_compose "${DOCKER_COMPOSE_FILE_PATH}")"; then
|
|
807
|
+
CHROMA_PORT="${EXISTING_CHROMA_PORT}"
|
|
808
|
+
fi
|
|
809
|
+
log_info "$(t chroma_port_selected) ${CHROMA_PORT}"
|
|
810
|
+
fi
|
|
811
|
+
|
|
812
|
+
if docker ps --format '{{.Names}}' | grep -q '^chromadb-rag$'; then
|
|
813
|
+
log_info "$(t chroma_running)"
|
|
814
|
+
else
|
|
815
|
+
log_info "$(t chroma_start)"
|
|
816
|
+
(cd "${DOCKER_COMPOSE_DIR}" && $DOCKER_COMPOSE_CMD up -d)
|
|
817
|
+
|
|
818
|
+
log_info "$(t chroma_wait)"
|
|
819
|
+
WAITED=0
|
|
820
|
+
while true; do
|
|
821
|
+
if [[ "$HAS_CURL" == "true" ]] && curl -sf "http://${CHROMA_HOST}:${CHROMA_PORT}/api/v1/heartbeat" &>/dev/null; then
|
|
822
|
+
log_info "$(t chroma_ready) http://${CHROMA_HOST}:${CHROMA_PORT}"
|
|
823
|
+
break
|
|
824
|
+
fi
|
|
825
|
+
if [[ $WAITED -ge 30 ]]; then
|
|
826
|
+
log_warn "$(t chroma_timeout)"
|
|
827
|
+
break
|
|
828
|
+
fi
|
|
829
|
+
sleep 2; WAITED=$((WAITED+2))
|
|
830
|
+
done
|
|
831
|
+
fi
|
|
832
|
+
|
|
833
|
+
# ---------------------------------------------------------------------------
|
|
834
|
+
# Instala mcp-rag-server com shebang do venv
|
|
835
|
+
# ---------------------------------------------------------------------------
|
|
836
|
+
log_section "$(t section_install_mcp)"
|
|
837
|
+
|
|
838
|
+
mkdir -p "${BIN_DIR}"
|
|
839
|
+
|
|
840
|
+
NEEDS_INSTALL=true
|
|
841
|
+
MCP_WAS_OUTDATED=false
|
|
842
|
+
if [[ -f "${MCP_SERVER_DEST}" ]]; then
|
|
843
|
+
if cmp -s <(tail -n +2 "${MCP_SERVER_DEST}") <(tail -n +2 "${EXTRACT_DIR}/mcp_server.py"); then
|
|
844
|
+
log_info "$(t mcp_keep)"
|
|
845
|
+
else
|
|
846
|
+
MCP_WAS_OUTDATED=true
|
|
847
|
+
log_warn "$(t mcp_outdated)"
|
|
848
|
+
fi
|
|
849
|
+
|
|
850
|
+
if [[ "$REINSTALL" == "true" ]]; then
|
|
851
|
+
NEEDS_INSTALL=true
|
|
852
|
+
elif [[ -t 0 ]]; then
|
|
853
|
+
if [[ "${MCP_WAS_OUTDATED}" == "true" ]]; then
|
|
854
|
+
if ask_yes_no_loop "$(t mcp_prompt_update)"; then
|
|
855
|
+
NEEDS_INSTALL=true
|
|
856
|
+
else
|
|
857
|
+
NEEDS_INSTALL=false
|
|
858
|
+
log_info "$(t mcp_skip_update)"
|
|
859
|
+
fi
|
|
860
|
+
else
|
|
861
|
+
if ask_yes_no_loop "$(t mcp_prompt_reinstall)"; then
|
|
862
|
+
NEEDS_INSTALL=true
|
|
863
|
+
else
|
|
864
|
+
NEEDS_INSTALL=false
|
|
865
|
+
log_info "$(t mcp_skip_update)"
|
|
866
|
+
fi
|
|
867
|
+
fi
|
|
868
|
+
else
|
|
869
|
+
if [[ "${MCP_WAS_OUTDATED}" == "true" ]]; then
|
|
870
|
+
NEEDS_INSTALL=false
|
|
871
|
+
log_info "$(t mcp_skip_update)"
|
|
872
|
+
else
|
|
873
|
+
NEEDS_INSTALL=false
|
|
874
|
+
fi
|
|
875
|
+
fi
|
|
876
|
+
fi
|
|
877
|
+
|
|
878
|
+
if [[ "$NEEDS_INSTALL" == "true" ]]; then
|
|
879
|
+
cp "${EXTRACT_DIR}/mcp_server.py" "${MCP_SERVER_DEST}"
|
|
880
|
+
cp "${EXTRACT_DIR}/download_model_from_hugginface.py" "${MODEL_DL_HF_DEST}"
|
|
881
|
+
cp "${EXTRACT_DIR}/download_model_from_modelscope.py" "${MODEL_DL_MS_DEST}"
|
|
882
|
+
# Shebang aponta para o venv — garante que tem todas as dependências
|
|
883
|
+
sed -i "1s|.*|#!${VENV_PYTHON}|" "${MCP_SERVER_DEST}"
|
|
884
|
+
chmod +x "${MCP_SERVER_DEST}"
|
|
885
|
+
log_info "$(t mcp_installed) ${MCP_SERVER_DEST}"
|
|
886
|
+
log_info "$(t mod_dl_installed) ${MODEL_DL_HF_DEST}"
|
|
887
|
+
log_info "$(t mod_provider_installed) ${MODEL_DL_MS_DEST}"
|
|
888
|
+
log_info "$(t shebang) ${VENV_PYTHON}"
|
|
889
|
+
fi
|
|
890
|
+
|
|
891
|
+
for RC in "${USER_HOME}/.bashrc" "${USER_HOME}/.zshrc"; do
|
|
892
|
+
if [[ -f "$RC" ]] && ! grep -qF '.local/bin' "$RC"; then
|
|
893
|
+
echo "" >> "$RC"
|
|
894
|
+
echo '# RAG setup — adicionado ao PATH' >> "$RC"
|
|
895
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$RC"
|
|
896
|
+
log_info "$(t path_added) $RC"
|
|
897
|
+
fi
|
|
898
|
+
done
|
|
899
|
+
|
|
900
|
+
# ---------------------------------------------------------------------------
|
|
901
|
+
# Configuração opcional do MCP no Claude/Cursor
|
|
902
|
+
# ---------------------------------------------------------------------------
|
|
903
|
+
log_section "$(t section_mcp_cfg)"
|
|
904
|
+
|
|
905
|
+
CLAUDE_JSON="${USER_HOME}/.claude.json"
|
|
906
|
+
CURSOR_MCP_JSON_1="${USER_HOME}/.cursor/mcp.json"
|
|
907
|
+
CURSOR_MCP_JSON_2="${USER_HOME}/.config/Cursor/User/mcp.json"
|
|
908
|
+
MCP_VERSION="$(detect_current_mcp_version)"
|
|
909
|
+
log_info "$(t mcp_version_detected) ${MCP_VERSION}"
|
|
910
|
+
|
|
911
|
+
CLAUDE_APP_INSTALLED=false
|
|
912
|
+
CURSOR_APP_INSTALLED=false
|
|
913
|
+
if command -v claude &>/dev/null || [[ -d "${USER_HOME}/.claude" ]] || [[ -d "${USER_HOME}/.config/Claude" ]]; then
|
|
914
|
+
CLAUDE_APP_INSTALLED=true
|
|
915
|
+
fi
|
|
916
|
+
if command -v cursor &>/dev/null || [[ -d "${USER_HOME}/.cursor" ]] || [[ -d "${USER_HOME}/.config/Cursor" ]]; then
|
|
917
|
+
CURSOR_APP_INSTALLED=true
|
|
918
|
+
fi
|
|
919
|
+
|
|
920
|
+
TARGET_CONFIGS=()
|
|
921
|
+
TARGET_LABELS=()
|
|
922
|
+
|
|
923
|
+
_append_target() {
|
|
924
|
+
local cfg="$1"
|
|
925
|
+
local label="$2"
|
|
926
|
+
for existing in "${TARGET_CONFIGS[@]:-}"; do
|
|
927
|
+
if [[ "$existing" == "$cfg" ]]; then
|
|
928
|
+
return 0
|
|
929
|
+
fi
|
|
930
|
+
done
|
|
931
|
+
TARGET_CONFIGS+=("$cfg")
|
|
932
|
+
TARGET_LABELS+=("$label")
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if [[ "$CLAUDE_APP_INSTALLED" == "true" ]] && [[ -f "${CLAUDE_JSON}" ]]; then
|
|
936
|
+
_append_target "${CLAUDE_JSON}" "Claude Code"
|
|
937
|
+
fi
|
|
938
|
+
if [[ "$CURSOR_APP_INSTALLED" == "true" ]] && [[ -f "${CURSOR_MCP_JSON_1}" ]]; then
|
|
939
|
+
_append_target "${CURSOR_MCP_JSON_1}" "Cursor"
|
|
940
|
+
fi
|
|
941
|
+
if [[ "$CURSOR_APP_INSTALLED" == "true" ]] && [[ -f "${CURSOR_MCP_JSON_2}" ]]; then
|
|
942
|
+
_append_target "${CURSOR_MCP_JSON_2}" "Cursor"
|
|
943
|
+
fi
|
|
944
|
+
|
|
945
|
+
if [[ "${#TARGET_CONFIGS[@]}" -eq 0 ]]; then
|
|
946
|
+
log_info "$(t no_default_cfg)"
|
|
947
|
+
else
|
|
948
|
+
PENDING_CONFIGS=()
|
|
949
|
+
PENDING_LABELS=()
|
|
950
|
+
|
|
951
|
+
for i in "${!TARGET_CONFIGS[@]}"; do
|
|
952
|
+
CFG_PATH="${TARGET_CONFIGS[$i]}"
|
|
953
|
+
CFG_LABEL="${TARGET_LABELS[$i]}"
|
|
954
|
+
CHECK_RESULT=$(
|
|
955
|
+
python3 - "${CFG_PATH}" "${MCP_SERVER_DEST}" "${MCP_VERSION}" "${CHROMA_PORT}" <<'PYEOF'
|
|
956
|
+
import json
|
|
957
|
+
import sys
|
|
958
|
+
from pathlib import Path
|
|
959
|
+
|
|
960
|
+
cfg_path = Path(sys.argv[1]).expanduser()
|
|
961
|
+
mcp_server_command = sys.argv[2]
|
|
962
|
+
mcp_version = sys.argv[3]
|
|
963
|
+
chroma_port = sys.argv[4]
|
|
964
|
+
|
|
965
|
+
try:
|
|
966
|
+
data = json.loads(cfg_path.read_text(encoding="utf-8"))
|
|
967
|
+
except Exception:
|
|
968
|
+
print("needs_update")
|
|
969
|
+
sys.exit(0)
|
|
970
|
+
|
|
971
|
+
if not isinstance(data, dict):
|
|
972
|
+
print("needs_update")
|
|
973
|
+
sys.exit(0)
|
|
974
|
+
|
|
975
|
+
mcp_servers = data.get("mcpServers")
|
|
976
|
+
if mcp_servers is None:
|
|
977
|
+
mcp_servers = {}
|
|
978
|
+
if not isinstance(mcp_servers, dict):
|
|
979
|
+
print("needs_update")
|
|
980
|
+
sys.exit(0)
|
|
981
|
+
|
|
982
|
+
desired = {
|
|
983
|
+
"command": mcp_server_command,
|
|
984
|
+
"args": [],
|
|
985
|
+
"env": {
|
|
986
|
+
"CHROMA_HOST": "localhost",
|
|
987
|
+
"CHROMA_PORT": chroma_port,
|
|
988
|
+
"TOKENIZERS_PARALLELISM": "false",
|
|
989
|
+
},
|
|
990
|
+
"version": mcp_version,
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
def is_rag_server_entry(value: object) -> bool:
|
|
994
|
+
if not isinstance(value, dict):
|
|
995
|
+
return False
|
|
996
|
+
cmd = value.get("command")
|
|
997
|
+
return isinstance(cmd, str) and "mcp-rag-server" in cmd
|
|
998
|
+
|
|
999
|
+
if "rag-codebase" in mcp_servers and mcp_servers["rag-codebase"] == desired:
|
|
1000
|
+
print("already_up_to_date")
|
|
1001
|
+
sys.exit(0)
|
|
1002
|
+
|
|
1003
|
+
for key, value in mcp_servers.items():
|
|
1004
|
+
if key != "rag-codebase" and is_rag_server_entry(value):
|
|
1005
|
+
print("needs_update")
|
|
1006
|
+
sys.exit(0)
|
|
1007
|
+
|
|
1008
|
+
print("needs_update")
|
|
1009
|
+
PYEOF
|
|
1010
|
+
)
|
|
1011
|
+
|
|
1012
|
+
if [[ "${CHECK_RESULT}" != "already_up_to_date" ]]; then
|
|
1013
|
+
PENDING_CONFIGS+=("${CFG_PATH}")
|
|
1014
|
+
PENDING_LABELS+=("${CFG_LABEL}")
|
|
1015
|
+
fi
|
|
1016
|
+
done
|
|
1017
|
+
|
|
1018
|
+
if [[ "${#PENDING_CONFIGS[@]}" -eq 0 ]]; then
|
|
1019
|
+
log_info "$(t mcp_cfg_all_current)"
|
|
1020
|
+
else
|
|
1021
|
+
echo ""
|
|
1022
|
+
log_info "$(t detected_cfg_files)"
|
|
1023
|
+
for i in "${!PENDING_CONFIGS[@]}"; do
|
|
1024
|
+
echo -e " - ${PENDING_LABELS[$i]}: ${PENDING_CONFIGS[$i]}"
|
|
1025
|
+
done
|
|
1026
|
+
|
|
1027
|
+
APPLY_MCP_CONFIG=false
|
|
1028
|
+
if [[ -t 0 ]]; then
|
|
1029
|
+
echo ""
|
|
1030
|
+
if ask_yes_no_loop "$(t ask_apply_cfg)"; then
|
|
1031
|
+
APPLY_MCP_CONFIG=true
|
|
1032
|
+
else
|
|
1033
|
+
APPLY_MCP_CONFIG=false
|
|
1034
|
+
fi
|
|
1035
|
+
else
|
|
1036
|
+
log_info "$(t non_interactive_cfg_skip)"
|
|
1037
|
+
fi
|
|
1038
|
+
|
|
1039
|
+
if [[ "$APPLY_MCP_CONFIG" == "true" ]]; then
|
|
1040
|
+
for i in "${!PENDING_CONFIGS[@]}"; do
|
|
1041
|
+
CFG_PATH="${PENDING_CONFIGS[$i]}"
|
|
1042
|
+
CFG_LABEL="${PENDING_LABELS[$i]}"
|
|
1043
|
+
|
|
1044
|
+
if ! RESULT=$(
|
|
1045
|
+
python3 - "${CFG_PATH}" "${MCP_SERVER_DEST}" "${MCP_VERSION}" "${CHROMA_PORT}" <<'PYEOF'
|
|
1046
|
+
import json
|
|
1047
|
+
import sys
|
|
1048
|
+
from pathlib import Path
|
|
1049
|
+
|
|
1050
|
+
cfg_path = Path(sys.argv[1]).expanduser()
|
|
1051
|
+
mcp_server_command = sys.argv[2]
|
|
1052
|
+
mcp_version = sys.argv[3]
|
|
1053
|
+
chroma_port = sys.argv[4]
|
|
1054
|
+
|
|
1055
|
+
try:
|
|
1056
|
+
data = json.loads(cfg_path.read_text(encoding="utf-8"))
|
|
1057
|
+
except Exception as exc:
|
|
1058
|
+
print(f"error:json_invalido:{exc}")
|
|
1059
|
+
sys.exit(2)
|
|
1060
|
+
|
|
1061
|
+
if not isinstance(data, dict):
|
|
1062
|
+
print("error:estrutura_invalida")
|
|
1063
|
+
sys.exit(2)
|
|
1064
|
+
|
|
1065
|
+
mcp_servers = data.get("mcpServers")
|
|
1066
|
+
if mcp_servers is None:
|
|
1067
|
+
mcp_servers = {}
|
|
1068
|
+
if not isinstance(mcp_servers, dict):
|
|
1069
|
+
print("error:mcpServers_invalido")
|
|
1070
|
+
sys.exit(2)
|
|
1071
|
+
|
|
1072
|
+
desired = {
|
|
1073
|
+
"command": mcp_server_command,
|
|
1074
|
+
"args": [],
|
|
1075
|
+
"env": {
|
|
1076
|
+
"CHROMA_HOST": "localhost",
|
|
1077
|
+
"CHROMA_PORT": chroma_port,
|
|
1078
|
+
"TOKENIZERS_PARALLELISM": "false",
|
|
1079
|
+
},
|
|
1080
|
+
"version": mcp_version,
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
def is_rag_server_entry(value: object) -> bool:
|
|
1084
|
+
if not isinstance(value, dict):
|
|
1085
|
+
return False
|
|
1086
|
+
cmd = value.get("command")
|
|
1087
|
+
return isinstance(cmd, str) and "mcp-rag-server" in cmd
|
|
1088
|
+
|
|
1089
|
+
if "rag-codebase" in mcp_servers:
|
|
1090
|
+
if mcp_servers["rag-codebase"] == desired:
|
|
1091
|
+
print("ok:already_exists")
|
|
1092
|
+
sys.exit(0)
|
|
1093
|
+
mcp_servers["rag-codebase"] = desired
|
|
1094
|
+
data["mcpServers"] = mcp_servers
|
|
1095
|
+
cfg_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
1096
|
+
print("ok:updated_rag_codebase")
|
|
1097
|
+
sys.exit(0)
|
|
1098
|
+
|
|
1099
|
+
old_rag_key = None
|
|
1100
|
+
for key, value in mcp_servers.items():
|
|
1101
|
+
if is_rag_server_entry(value):
|
|
1102
|
+
old_rag_key = key
|
|
1103
|
+
break
|
|
1104
|
+
|
|
1105
|
+
if old_rag_key is not None:
|
|
1106
|
+
del mcp_servers[old_rag_key]
|
|
1107
|
+
mcp_servers["rag-codebase"] = desired
|
|
1108
|
+
data["mcpServers"] = mcp_servers
|
|
1109
|
+
cfg_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
1110
|
+
print(f"ok:replaced_old:{old_rag_key}")
|
|
1111
|
+
sys.exit(0)
|
|
1112
|
+
|
|
1113
|
+
mcp_servers["rag-codebase"] = desired
|
|
1114
|
+
data["mcpServers"] = mcp_servers
|
|
1115
|
+
cfg_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
1116
|
+
print("ok:added")
|
|
1117
|
+
PYEOF
|
|
1118
|
+
); then
|
|
1119
|
+
log_warn "$(t cannot_update_cfg) ${CFG_LABEL} (${CFG_PATH}): ${RESULT}"
|
|
1120
|
+
continue
|
|
1121
|
+
fi
|
|
1122
|
+
|
|
1123
|
+
case "${RESULT}" in
|
|
1124
|
+
ok:already_exists)
|
|
1125
|
+
log_info "${CFG_LABEL}: $(t already_updated)"
|
|
1126
|
+
;;
|
|
1127
|
+
ok:updated_rag_codebase)
|
|
1128
|
+
log_info "${CFG_LABEL}: $(t updated_version) ${MCP_VERSION}."
|
|
1129
|
+
;;
|
|
1130
|
+
ok:replaced_old:*)
|
|
1131
|
+
OLD_KEY="${RESULT#ok:replaced_old:}"
|
|
1132
|
+
log_info "${CFG_LABEL}: $(t replaced_old) ${MCP_VERSION})."
|
|
1133
|
+
;;
|
|
1134
|
+
ok:added)
|
|
1135
|
+
log_info "${CFG_LABEL}: $(t added_cfg) ${MCP_VERSION})."
|
|
1136
|
+
;;
|
|
1137
|
+
*)
|
|
1138
|
+
log_warn "${CFG_LABEL}: $(t unexpected_return) ${RESULT}"
|
|
1139
|
+
;;
|
|
1140
|
+
esac
|
|
1141
|
+
done
|
|
1142
|
+
else
|
|
1143
|
+
log_info "$(t user_skipped_cfg)"
|
|
1144
|
+
fi
|
|
1145
|
+
fi
|
|
1146
|
+
fi
|
|
1147
|
+
|
|
1148
|
+
# ---------------------------------------------------------------------------
|
|
1149
|
+
# Indexa o projeto atual
|
|
1150
|
+
# ---------------------------------------------------------------------------
|
|
1151
|
+
if [[ "$SKIP_INDEX" == "false" ]]; then
|
|
1152
|
+
if [[ ! -d "${PROJECT_DIR}" ]]; then
|
|
1153
|
+
log_warn "$(t path_not_found_skip) ${PROJECT_DIR}"
|
|
1154
|
+
else
|
|
1155
|
+
log_section "$(t section_index_project) ${PROJECT_DIR}"
|
|
1156
|
+
prompt_optional_hf_token
|
|
1157
|
+
run_indexer_with_diagnostics "${PROJECT_DIR}"
|
|
1158
|
+
fi
|
|
1159
|
+
else
|
|
1160
|
+
log_section "$(t section_skip_index)"
|
|
1161
|
+
log_info "$(t how_to_index)"
|
|
1162
|
+
fi
|
|
1163
|
+
|
|
1164
|
+
# ---------------------------------------------------------------------------
|
|
1165
|
+
# Resumo
|
|
1166
|
+
# ---------------------------------------------------------------------------
|
|
1167
|
+
echo ""
|
|
1168
|
+
echo -e "${BOLD}${GREEN}================================================================${NC}"
|
|
1169
|
+
echo -e "${BOLD}${GREEN} $(t setup_done)${NC}"
|
|
1170
|
+
echo -e "${BOLD}${GREEN}================================================================${NC}"
|
|
1171
|
+
echo ""
|
|
1172
|
+
echo -e " ${GREEN}Venv Python${NC} : ${VENV_DIR}"
|
|
1173
|
+
echo -e " ${GREEN}ChromaDB${NC} : http://${CHROMA_HOST}:${CHROMA_PORT} (Docker, auto-start)"
|
|
1174
|
+
echo -e " ${GREEN}Dados${NC} : ${RAG_DB_DIR}"
|
|
1175
|
+
echo -e " ${GREEN}MCP Server${NC} : ${MCP_SERVER_DEST}"
|
|
1176
|
+
echo -e " ${GREEN}Projeto${NC} : ${PROJECT_DIR}"
|
|
1177
|
+
echo ""
|
|
1178
|
+
echo -e " $(t restart_tools)"
|
|
1179
|
+
echo ""
|