hive / app.py
Paulhayes's picture
Update app.py
909114b verified
# === BOOTSTRAP + SAFE DATASETS SHIM (must be line 1) =========================
# Load model directly
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
model = AutoModelForCausalLM.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
messages = [
{"role": "user", "content": "Who are you?"},
]
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt",
).to(model.device)
outputs = model.generate(**inputs, max_new_tokens=40)
print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:]))
import os, sys, subprocess, importlib.util, importlib.machinery, types
import sys # make sure this import is near the top of your file
# near the other imports
import os
# Defaults that work for both CLI and UI; can be overridden via env if you want
DEFAULT_EFFECTIVE_ROLE = os.getenv("EFFECTIVE_ROLE", "user")
DEFAULT_CALLER_ID_CLI = os.getenv("CALLER_ID_CLI", "cli")
DEFAULT_CALLER_ID_UI = os.getenv("CALLER_ID_UI", "ui")
def chat_once(hive: "Hive", text: str, *, role: str = DEFAULT_EFFECTIVE_ROLE, caller_id: str = DEFAULT_CALLER_ID_CLI):
"""
Thin wrapper so all calls to Hive.chat() include the required args.
"""
return hive.chat(text, effective_role=role, caller_id=caller_id)
try:
import gradio as gr
except Exception as e:
gr = None
print(f"[ui] Gradio import failed: {e}")
# --- SAFE loader for SentenceTransformer to avoid "meta tensor" crashes ---
import os
from sentence_transformers import SentenceTransformer
def load_sentence_transformer_safely(model_name: str, device: str = "cpu"):
"""
Load a SentenceTransformer without going through an accelerate/empty-weights
path that can yield 'meta' tensors. Never call .to(device) after creation.
"""
# Guardrails — avoid accelerate meta in some containers
os.environ.pop("ACCELERATE_USE_DEEPSPEED", None)
os.environ.pop("ACCELERATE_MIXED_PRECISION", None)
# First attempt: construct directly on the requested device
try:
st = SentenceTransformer(
model_name,
device=device, # <-- construct directly on the device
trust_remote_code=True, # some ST models need this
)
return st
except NotImplementedError as e:
# If we still hit the meta-copy error, load on CPU and keep it there.
print(f"[warn] ST meta-tensor error on device={device}: {e}. Falling back to CPU.")
st = SentenceTransformer(
model_name,
device="cpu",
trust_remote_code=True,
)
return st
def _read_line(prompt="> "):
# Avoid prompting when there’s no interactive terminal (e.g., Hugging Face space)
if not sys.stdin or not sys.stdin.isatty():
prompt = ""
try:
return input(prompt)
except EOFError:
return None
def _pip_install(pkgs):
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q"] + pkgs)
except Exception as e:
print("Bootstrap warning:", e, flush=True)
def _datasets_ok():
try:
spec = importlib.util.find_spec("datasets")
if spec is None:
return False
import datasets as _ds
if getattr(_ds, "__spec__", None) is None:
return False
for name in ("Dataset","DatasetDict","IterableDataset","IterableDatasetDict","Value"):
if not hasattr(_ds, name):
return False
return True
except Exception:
return False
# Try to ensure a real, recent datasets exists
if not _datasets_ok():
_pip_install(["datasets>=2.16,<3"])
# If still not OK, hard-stub it so imports don’t crash
if not _datasets_ok():
ds = types.ModuleType("datasets")
ds.__file__ = "<stub:datasets>"
ds.__path__ = []
ds.__spec__ = importlib.machinery.ModuleSpec("datasets", loader=None, is_package=True)
class _Base: # simple no-op base
def __init__(self, *a, **k): pass
class Dataset(_Base):
def map(self,*a,**k): return self
def filter(self,*a,**k): return self
def select(self,*a,**k): return self
def shuffle(self,*a,**k): return self
def train_test_split(self,*a,**k): return {"train": self, "test": self}
class IterableDataset(_Base): pass
class DatasetDict(dict): pass
class IterableDatasetDict(dict): pass
class Value:
def __init__(self, dtype="string"): self.dtype = dtype
ds.Dataset = Dataset
ds.DatasetDict = DatasetDict
ds.IterableDataset = IterableDataset
ds.IterableDatasetDict = IterableDatasetDict
ds.Value = Value
sys.modules["datasets"] = ds
# ============================================================================
# === LIGHTWEIGHT ST IMPORT + FALLBACK ========================================
# IMPORTANT: avoid "from sentence_transformers import SentenceTransformer"
# (that import triggers cross_encoder -> datasets)
try:
from sentence_transformers.SentenceTransformer import SentenceTransformer
_ST_AVAILABLE = True
except Exception as _e:
print("[WARN] sentence-transformers direct import failed:", _e, flush=True)
SentenceTransformer = None
_ST_AVAILABLE = False
def get_embedder(model_name: str = "sentence-transformers/all-MiniLM-L6-v2", device: str | None = None):
"""
Returns a SentenceTransformer if available, otherwise a pure-Transformers
fallback with mean pooling and L2 normalization (API-compatible encode()).
"""
if _ST_AVAILABLE:
try:
return SentenceTransformer(model_name, device=device)
except Exception as e:
print("[WARN] ST model init failed, falling back to plain Transformers:", e, flush=True)
# Fallback: plain Transformers embedder
from transformers import AutoTokenizer, AutoModel
import torch
tok = AutoTokenizer.from_pretrained(model_name)
mdl = AutoModel.from_pretrained(model_name)
if device:
mdl.to(device)
mdl.eval()
class _Embedder:
def encode(self, texts, convert_to_tensor=False, normalize_embeddings=True, batch_size=32, **kw):
if isinstance(texts, str):
texts = [texts]
out_chunks = []
with torch.no_grad():
for i in range(0, len(texts), batch_size):
batch = texts[i:i+batch_size]
enc = tok(batch, padding=True, truncation=True, return_tensors="pt")
if device:
enc = {k: v.to(device) for k, v in enc.items()}
last_hidden = mdl(**enc).last_hidden_state # [B, T, H]
mask = enc["attention_mask"].unsqueeze(-1) # [B, T, 1]
pooled = (last_hidden * mask).sum(dim=1) / mask.sum(dim=1).clamp(min=1e-9) # mean pooling
if normalize_embeddings:
pooled = torch.nn.functional.normalize(pooled, p=2, dim=1)
out_chunks.append(pooled.detach().cpu())
embs = torch.cat(out_chunks, dim=0)
return embs if convert_to_tensor else embs.numpy()
return _Embedder()
# ============================================================================
# -*- coding: utf-8 -*-
"""
Hive — FULL COMBINED (Original-first, Tutor+ • SAFE v5 BACKGROUND)
- Embeds your fixed base (as `hive_base`)
- Tutor+ (retrieval + gentle phonics/CEFR/essay review)
- Instant boot: `datasets` stubbed at import
- **Background-only** staged condensed-curves builder:
• Streams dataset text -> embeds -> FAISS -> prunes caches
• No UI elements; runs automatically when needed and on a schedule
"""
import os, sys, types, threading, time, base64, json, re, shutil
from pathlib import Path
from typing import List, Dict, Optional
# ---------- Storage policy ----------
MAX_GB = float(os.getenv("HIVE_MAX_CACHE_GB", "8")) # cache budget (GB)
HF_HOME = Path(os.getenv("HF_HOME", str(Path.home() / ".cache" / "huggingface")))
TRANSFORMERS_CACHE = Path(os.getenv("TRANSFORMERS_CACHE", str(HF_HOME / "transformers")))
DATASETS_CACHE = Path(os.getenv("HF_DATASETS_CACHE", str(HF_HOME / "datasets")))
ALLOW_KEEP = [os.getenv("HIVE_MODEL_ID","TinyLlama/TinyLlama-1.1B-Chat-v1.0").split("/")[-1],
os.getenv("HIVE_EMB_ID","sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2").split("/")[-1]]
def _dir_size_bytes(p: Path) -> int:
total = 0
if not p.exists(): return 0
for root, _, files in os.walk(p):
for f in files:
try: total += (Path(root)/f).stat().st_size
except Exception: pass
return total
def _prune_path_to_limit(root: Path, max_bytes: int, allow_keep=None, log=None):
if not root.exists(): return
files = []
for rp, _, fns in os.walk(root):
for fn in fns:
fp = Path(rp)/fn
try:
st = fp.stat()
rel = str(fp).lower()
if allow_keep and any(k.lower() in rel for k in allow_keep):
continue
files.append((st.st_mtime, st.st_size, fp))
except Exception:
pass
files.sort() # oldest first
size = _dir_size_bytes(root)
if log: log(f"[prune] {root} size: {size/1e9:.2f} GB; target: {max_bytes/1e9:.2f} GB")
i = 0
while size > max_bytes and i < len(files):
_, s, fp = files[i]
try:
fp.unlink()
size -= s
if log: log(f"[prune] delete {fp} (-{s/1e6:.1f} MB)")
except Exception as e:
if log: log(f"[prune] skip {fp}: {e}")
i += 1
def enforce_cache_budget(log=None):
max_bytes = int(MAX_GB * (1024**3))
for p in [TRANSFORMERS_CACHE, DATASETS_CACHE]:
_prune_path_to_limit(Path(p), max_bytes, allow_keep=ALLOW_KEEP, log=log)
# ---------- Stub datasets at import for instant boot ----------
if os.getenv("HIVE_DISABLE_DATASETS", "1").lower() in ("1","true","yes","on"):
import importlib.machinery as _mach
ds = types.ModuleType("datasets")
# Provide a proper ModuleSpec so importlib.util.find_spec("datasets") does not crash
ds.__spec__ = _mach.ModuleSpec("datasets", loader=None)
ds.__path__ = [] # mark as package-like for safety
ds.__version__ = "0.0-stub"
class _Empty:
def __iter__(self): return iter([])
def __getitem__(self, k): return []
def map(self, *a, **k): return self
def filter(self, *a, **k): return self
def select(self, *a, **k): return self
def shuffle(self, *a, **k): return self
def train_test_split(self, *a, **k): return {"train": self, "test": self}
def to_pandas(self): import pandas as pd; return pd.DataFrame()
# Expose symbols sentence-transformers expects to import
ds.Dataset = _Empty
ds.IterableDataset = _Empty
ds.DatasetDict = dict
def _disabled_load_dataset(*args, **kwargs):
print("[datasets] disabled via HIVE_DISABLE_DATASETS; returning empty dataset.")
return _Empty()
ds.load_dataset = _disabled_load_dataset
sys.modules["datasets"] = ds
os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1")
os.environ.setdefault("HIVE_DOWNLOAD_DATASETS_ON_START", "0") # handled by staged builder
os.environ.setdefault("HIVE_DATASETS_LIST", "wi_locness")
os.environ.setdefault("HIVE_BUILD_CONDENSED_CURVES_ON_START", "1")
os.environ.setdefault("HIVE_CURVES_TARGET_MIN", "10000") # target #items in index
os.environ.setdefault("HIVE_CURVES_RECHECK_SECS", "1800") # 30 minutes
os.environ.setdefault("HIVE_STAGE_BATCH", "128")
os.environ.setdefault("HIVE_STAGE_SAVE_EVERY", "512")
os.environ.setdefault("HIVE_STAGE_MAX_DOCS_PER_DATASET", "5000")
# ---------- Load original base from Base64 ----------
_HIVE_BASE_B64 = """IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwojIC0tLSBCRUdJTiBNRU1PUlkgTUFOSUZFU1QgKGF1dG8tdXBkYXRlZCkgLS0tCiMgKFRoaXMgYmxvY2sgaXMgYXV0by13cml0dGVuIGJ5IEhpdmUgdG8gcmVjb3JkIHdoYXQgZGF0YXNldHMvZmlsZXMKIyAgaGF2ZSBhbHJlYWR5IGJlZW4gY29udmVydGVkIGludG8gbWVtb3J5IChjdXJ2ZXMpLiBEbyBub3QgZWRpdCBieSBoYW5kLikKTUVNT1JZX01BTklGRVNUID0gewogICAgInVwZGF0ZWRfdHMiOiAwLAogICAgImRhdGFzZXRzX2RvbmUiOiBbXSwKICAgICJ2ZWN0b3JzX3RvdGFsIjogMCwKICAgICJub3RlcyI6ICJTZXQgSElWRV9BTExPV19TRUxGX1dSSVRFX01BTklGRVNUPTAgdG8gc3RvcCBhdXRvLXVwZGF0ZXMuIgp9CiMgLS0tIEVORCBNRU1PUlkgTUFOSUZFU1QgLS0tCgojIC0qLSBjb2Rpbmc6IHV0Zi04IC0qLQojIEhJVkUg4oCUIEZVTEwgTUVSR0VEIEFMTC1JTi1PTkUgKipPUFRJTUlaRUQqKgojIE9mZmxpbmUtZmlyc3QgKyBPbmxpbmUgdXBkYXRlcyArIEF1dG8gV2ktRmkgKyBSQkFDICsgTXVsdGlsaW5ndWFsIFZvaWNlIChBU1IvVFRTICsgUGhvbmljcykKIyArIEludGVybmFsIE9wdGltaXphdGlvbiBTdGFjayAoQ2hhbmdlIE1hbmFnZXI6IHByb3Bvc2Ug4oaSIHNhbmRib3gg4oaSIEEvQiB0ZXN0IOKGkiBhcHBseS9yb2xsYmFjayB3aXRoIE93bmVyIHBvbGljeSkKIyBVcGxvYWQgdGhpcyBzaW5nbGUgZmlsZSBhbmQgcmVxdWlyZW1lbnRzLnR4dCB0byBhIEh1Z2dpbmcgRmFjZSBTcGFjZSAob3IgcnVuIGxvY2FsbHkpLgojICAtIHB5dGhvbiBoaXZlX2Z1bGxfbWVyZ2VkX2FsbF9pbl9vbmVfb3B0aW1pemVkLnB5CgppbXBvcnQgb3MsIHN5cywgcmUsIGpzb24sIHRpbWUsIHNodXRpbCwgdGVtcGZpbGUsIHN1YnByb2Nlc3MsIHBsYXRmb3JtLCBzb2NrZXQsIHRocmVhZGluZywgaW1wb3J0bGliLCBoYXNobGliLCB1bmljb2RlZGF0YSwgdXJsbGliLnJlcXVlc3QKZnJvbSBkYXRhY2xhc3NlcyBpbXBvcnQgZGF0YWNsYXNzCmZyb20gdHlwaW5nIGltcG9ydCBPcHRpb25hbCwgTGlzdCwgRGljdCwgVHVwbGUKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBsaWdodCBib290c3RyYXAgKHNhZmUpIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZGVmIF9lbnN1cmUocGtncyk6CiAgICBmb3IgcCBpbiBwa2dzOgogICAgICAgIG1vZCA9IHAuc3BsaXQoIj09IilbMF0uc3BsaXQoIj49IilbMF0uc3BsaXQoIlsiKVswXQogICAgICAgIHRyeToKICAgICAgICAgICAgaW1wb3J0bGliLmltcG9ydF9tb2R1bGUobW9kKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHN1YnByb2Nlc3MuY2hlY2tfY2FsbChbc3lzLmV4ZWN1dGFibGUsICItbSIsICJwaXAiLCAiaW5zdGFsbCIsICItLXVwZ3JhZGUiLCBwXSkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgIHBhc3MKCl9lbnN1cmUoWyJudW1weT49MS4yNC4wIiwicHN1dGlsPj01LjkuMCIsInJlcXVlc3RzPj0yLjMxLjAiLCJncmFkaW8+PTQuNDQuMCIsInNlbnRlbmNlLXRyYW5zZm9ybWVycz49My4wLjAiLCJmYWlzcy1jcHU+PTEuOC4wIiwKICAgICAgICAgInRyYW5zZm9ybWVycz49NC40NC4wIiwiYWNjZWxlcmF0ZT49MC4zMy4wIiwiZGF0YXNldHM+PTIuMjEuMCIsInNvdW5kZmlsZT49MC4xMi4xIiwiZmFzdGVyLXdoaXNwZXI+PTEuMC4wIiwibGFuZ2lkPj0xLjEuNiIsCiAgICAgICAgICJwaXBlci10dHM+PTEuMi4wIiwiZzJwX2VuPj0yLjEuMCIsImxpYnJvc2E+PTAuMTAuMSIsInNjaWtpdC1sZWFybj49MS4xLjAiLCJmZWVkcGFyc2VyPj02LjAuMTEiLCJkdWNrZHVja2dvX3NlYXJjaD49Ni4yLjEwIiwKICAgICAgICAgImtleXJpbmc+PTI0LjMuMSJdKQoKaW1wb3J0IG51bXB5IGFzIG5wLCBwc3V0aWwsIHJlcXVlc3RzLCBmZWVkcGFyc2VyLCBsYW5naWQsIGxpYnJvc2EsIGdyYWRpbyBhcyBncgpmcm9tIHNlbnRlbmNlX3RyYW5zZm9ybWVycyBpbXBvcnQgU2VudGVuY2VUcmFuc2Zvcm1lcgpmcm9tIGR1Y2tkdWNrZ29fc2VhcmNoIGltcG9ydCBEREdTCmZyb20gdHJhbnNmb3JtZXJzIGltcG9ydCBBdXRvVG9rZW5pemVyLCBBdXRvTW9kZWxGb3JDYXVzYWxMTSwgcGlwZWxpbmUKZnJvbSBmYXN0ZXJfd2hpc3BlciBpbXBvcnQgV2hpc3Blck1vZGVsCmZyb20gcGlwZXIudm9pY2UgaW1wb3J0IFBpcGVyVm9pY2UKZnJvbSBnMnBfZW4gaW1wb3J0IEcycApmcm9tIHNrbGVhcm4ubWV0cmljcy5wYWlyd2lzZSBpbXBvcnQgY29zaW5lX3NpbWlsYXJpdHkKCnRyeToKICAgIGltcG9ydCB0b3JjaApleGNlcHQgRXhjZXB0aW9uOgogICAgdG9yY2g9Tm9uZQoKdHJ5OgogICAgaW1wb3J0IGZhaXNzCmV4Y2VwdCBFeGNlcHRpb246CiAgICBzdWJwcm9jZXNzLmNoZWNrX2NhbGwoW3N5cy5leGVjdXRhYmxlLCItbSIsInBpcCIsImluc3RhbGwiLCItLXVwZ3JhZGUiLCJmYWlzcy1jcHU+PTEuOC4wIl0pCiAgICBpbXBvcnQgZmFpc3MKCiMgT3B0aW9uYWwgdmlzaW9uCnRyeToKICAgIGltcG9ydCBjdjI7IF9IQVZFX0NWPVRydWUKZXhjZXB0IEV4Y2VwdGlvbjoKICAgIF9IQVZFX0NWPUZhbHNlCnRyeToKICAgIGltcG9ydCBweXRlc3NlcmFjdDsgX0hBVkVfVEVTUz1UcnVlCmV4Y2VwdCBFeGNlcHRpb246CiAgICBfSEFWRV9URVNTPUZhbHNlCgp0cnk6CiAgICBpbXBvcnQga2V5cmluZwpleGNlcHQgRXhjZXB0aW9uOgogICAga2V5cmluZz1Ob25lCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIGNvbmZpZyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmRlZiBFTlYobmFtZSwgZGVmYXVsdD1Ob25lLCBjYXN0PXN0cik6CiAgICB2PW9zLmdldGVudihuYW1lLCBkZWZhdWx0KQogICAgaWYgdiBpcyBOb25lOiByZXR1cm4gTm9uZQogICAgaWYgY2FzdCBpcyBib29sOiByZXR1cm4gc3RyKHYpLmxvd2VyKCkgaW4gKCIxIiwidHJ1ZSIsInllcyIsIm9uIikKICAgIGlmIGNhc3QgaXMgaW50OgogICAgICAgIHRyeTogcmV0dXJuIGludCh2KQogICAgICAgIGV4Y2VwdDogcmV0dXJuIGludChmbG9hdCh2KSkKICAgIHJldHVybiB2CgpDRkc9ewogICAgIyBhdXRvLWFyY2hpdmUgbWVtb3J5IHRvIGN1cnZlcy50YXIuZ3oKICAgICJISVZFX0FVVE9fQVJDSElWRSI6IEVOVigiSElWRV9BVVRPX0FSQ0hJVkUiLCAiMSIsIGJvb2wpLAogICAgIkhJVkVfQVVUT19BUkNISVZFX01PREUiOiBFTlYoIkhJVkVfQVVUT19BUkNISVZFX01PREUiLCAicGVyX2NoYWluIiwgc3RyKSwgICMgcGVyX2NoYWluIHwgcGVyX2RhdGFzZXQKICAgICJISVZFX0FSQ0hJVkVfUEFUSCI6IEVOVigiSElWRV9BUkNISVZFX1BBVEgiLCAiY3VydmVzLnRhci5neiIsIHN0ciksCiAgICAjIHN0YWdlZCBpbmdlc3Rpb24gY2hhaW5pbmcgKGF1dG8tcnVuIG11bHRpcGxlIHN0YWdlcyB0aGlzIGJvb3QpCiAgICAiSElWRV9JTkdFU1RfQ0hBSU4iOiBFTlYoIkhJVkVfSU5HRVNUX0NIQUlOIiwgIjEiLCBib29sKSwKICAgICJISVZFX0lOR0VTVF9DSEFJTl9NQVgiOiBFTlYoIkhJVkVfSU5HRVNUX0NIQUlOX01BWCIsICIyIiwgaW50KSwgICMgbWF4IHN0YWdlcyBwZXIgYm9vdAogICAgIyBzdGFnZWQgaW5nZXN0aW9uIGNvbnRyb2xzCiAgICAiSElWRV9JTkdFU1RfU1RBR0VEIjogRU5WKCJISVZFX0lOR0VTVF9TVEFHRUQiLCAiMSIsIGJvb2wpLAogICAgIkhJVkVfSU5HRVNUX1NUQUdFX1NJWkUiOiBFTlYoIkhJVkVfSU5HRVNUX1NUQUdFX1NJWkUiLCAiMyIsIGludCksCiAgICAiSElWRV9JTkdFU1RfTUlOX0ZSRUVfR0IiOiBFTlYoIkhJVkVfSU5HRVNUX01JTl9GUkVFX0dCIiwgIjgiLCBpbnQpLAogICAgIkhJVkVfSU5HRVNUX05FWFQiOiBFTlYoIkhJVkVfSU5HRVNUX05FWFQiLCAiMCIsIGJvb2wpLAoKICAgICMgc2VsZi1lZGl0IG1hbmlmZXN0IGNvbnRyb2xzCiAgICAiSElWRV9BTExPV19TRUxGX1dSSVRFX01BTklGRVNUIjogRU5WKCJISVZFX0FMTE9XX1NFTEZfV1JJVEVfTUFOSUZFU1QiLCAiMSIsIGJvb2wpLAogICAgIkhJVkVfU0VMRl9XUklURV9GSUxFIjogRU5WKCJISVZFX1NFTEZfV1JJVEVfRklMRSIsICIiLCBzdHIpLAoKICAgICMgbWVtb3J5IGF1dG8tcmVzdG9yZSBjb250cm9scyAoYWRtaW4gbWVtb3J5KQogICAgIkNVUlZFU19BVVRPX1JFU1RPUkUiOiBFTlYoIkhJVkVfQ1VSVkVTX0FVVE9fUkVTVE9SRSIsICIxIiwgYm9vbCksCiAgICAiQ1VSVkVTX0FSQ0hJVkVfTE9DQUwiOiBFTlYoIkhJVkVfQ1VSVkVTX0FSQ0hJVkVfTE9DQUwiLCAiY3VydmVzLnRhci5neiIsIHN0ciksCiAgICAiQ1VSVkVTX0FSQ0hJVkVfVVJMIjogRU5WKCJISVZFX0NVUlZFU19BUkNISVZFX1VSTCIsICIiLCBzdHIpLAogICAgIkNVUlZFU19IRl9EQVRBU0VUIjogRU5WKCJISVZFX0NVUlZFU19IRl9EQVRBU0VUIiwgIiIsIHN0ciksCiAgICAiQ1VSVkVTX0hGX1NVQlBBVEgiOiBFTlYoIkhJVkVfQ1VSVkVTX0hGX1NVQlBBVEgiLCAiIiwgc3RyKSwKICAgICJIRl9SRUFEX1RPS0VOIjogRU5WKCJIRl9SRUFEX1RPS0VOIiwgIiIsIHN0ciksCgogICAgIyBtZW1vcnkgZGlyZWN0b3J5IGFsaWFzCiAgICAiTUVNT1JZX0RJUiI6IEVOVigiSElWRV9DVVJWRV9ESVIiLCAiLi9jdXJ2ZXMiKSwKICAgICJDVVJWRV9ESVIiOiBFTlYoIkhJVkVfQ1VSVkVfRElSIiwiLi9jdXJ2ZXMiKSwKICAgICJTVEFURV9ESVIiOiBFTlYoIkhJVkVfU1RBVEVfRElSIiwiLi9zdGF0ZSIpLAogICAgIkxBVU5DSF9VSSI6IEVOVigiSElWRV9MQVVOQ0hfVUkiLCIxIixib29sKSwKICAgICJMTE1fQVVUT1NJWkUiOiBFTlYoIkhJVkVfTExNX0FVVE9TSVpFIiwiMSIsYm9vbCksCiAgICAiTExNX01BWF9WUkFNX0dCIjogRU5WKCJISVZFX0xMTV9NQVhfVlJBTV9HQiIsIjAiKSwKICAgICJNT0RFTF9PVkVSUklERSI6IEVOVigiSElWRV9NT0RFTF9JRCIsIiIpLAogICAgIkNUWF9UT0tFTlMiOiBFTlYoIkhJVkVfQ1RYX1RPS0VOUyIsIjIwNDgiLGludCksCiAgICAiT1dORVJfTkFNRSI6IEVOVigiSElWRV9PV05FUl9VU0VSIiwiUm9zZSIpLAogICAgIk9XTkVSX1BBU1MiOiBFTlYoIkhJVkVfT1dORVJfUEFTUyIsIkZlaHIyMDA4IiksCiAgICAiT1dORVJfU0VDT05EIjogRU5WKCJISVZFX09XTkVSX1NFQ09ORCIsIlBhdWxiZWFyMDEiKSwKICAgICJBR0VOVF9OQU1FIjogRU5WKCJISVZFX0FHRU5UX05BTUUiLCJIaXZlIiksCiAgICAiTk9fUFJPRkFOSVRZIjogRU5WKCJISVZFX05PX1BST0ZBTklUWSIsIjEiLGJvb2wpLAogICAgIkFTUl9TSVpFIjogRU5WKCJISVZFX0FTUl9TSVpFIiwic21hbGwiKSwKICAgICJUVFNfTEFORyI6IEVOVigiSElWRV9UVFNfTEFORyIsImVuIiksCiAgICAiQk9PVFNUUkFQX0lOR0VTVCI6IEVOVigiSElWRV9CT09UU1RSQVBfSU5HRVNUIiwiMSIsYm9vbCksCiAgICAiRk9SQ0VfUkVJTkdFU1QiOiBFTlYoIkhJVkVfRk9SQ0VfUkVJTkdFU1QiLCIwIixib29sKSwKICAgICJJTkdFU1RfU09VUkNFUyI6IEVOVigiSElWRV9JTkdFU1RfU09VUkNFUyIsIiIpLAogICAgIk9OTElORV9FTkFCTEUiOiBFTlYoIkhJVkVfT05MSU5FX0VOQUJMRSIsIjEiLGJvb2wpLAogICAgIk9OTElORV9BVVRPIjogRU5WKCJISVZFX09OTElORV9BVVRPIiwiMCIsYm9vbCksCiAgICAiT05MSU5FX1NPVVJDRVMiOiBFTlYoIkhJVkVfT05MSU5FX1NPVVJDRVMiLCJodHRwczovL2hucnNzLm9yZy9mcm9udHBhZ2UsaHR0cHM6Ly9yc3Mubnl0aW1lcy5jb20vc2VydmljZXMveG1sL3Jzcy9ueXQvV29ybGQueG1sIiksCiAgICAiT05MSU5FX1RJTUVPVVQiOiBFTlYoIkhJVkVfT05MSU5FX1RJTUVPVVQiLCI4IixpbnQpLAogICAgIk9OTElORV9NQVhfUkVTVUxUUyI6IEVOVigiSElWRV9PTkxJTkVfTUFYX1JFU1VMVFMiLCI1IixpbnQpLAogICAgIk9OTElORV9UUklHR0VSIjogRU5WKCJISVZFX09OTElORV9UUklHR0VSIiwiYXV0byIsc3RyKSwKICAgICMgYm91bmRlZCBzZWxmIGdvdmVybmFuY2UKICAgICJBTExPV19TRUxGX1JFQk9PVCI6IEVOVigiSElWRV9BTExPV19TRUxGX1JFQk9PVCIsIjEiLGJvb2wpLAogICAgIkFMTE9XX1JVTlRJTUVfSE9UUEFUQ0giOiBFTlYoIkhJVkVfQUxMT1dfUlVOVElNRV9IT1RQQVRDSCIsIjEiLGJvb2wpLAogICAgIkFVVE9fU0VMRl9PUFRJTUlaRSI6IEVOVigiSElWRV9BVVRPX1NFTEZfT1BUSU1JWkUiLCIxIixib29sKSwKICAgICMgaW50ZXJuYWwgb3B0aW1pemF0aW9uIHdpdGggc2FuZGJveCArIEEvQiAoT3duZXIgcG9saWN5KQogICAgIk9QVF9FTkFCTEUiOiBFTlYoIkhJVkVfT1BUX0VOQUJMRSIsIjEiLGJvb2wpLAogICAgIk9QVF9BVVRPX0FQUExZIjogRU5WKCJISVZFX09QVF9BVVRPX0FQUExZIiwiMCIsYm9vbCksICAjIE9XTkVSIE1BWSBTRVQgVE8gMQogICAgIk9QVF9QS0dfQUxMT1dMSVNUIjogRU5WKCJISVZFX09QVF9QS0dfQUxMT1dMSVNUIiwidHJhbnNmb3JtZXJzLGFjY2VsZXJhdGUsZGF0YXNldHMsc2VudGVuY2UtdHJhbnNmb3JtZXJzLGZhaXNzLWNwdSxkdWNrZHVja2dvX3NlYXJjaCxmZWVkcGFyc2VyLHJlcXVlc3RzLGdyYWRpbyIpLnNwbGl0KCIsIiksCiAgICAiT1BUX01PREVMX0FMTE9XTElTVCI6IEVOVigiSElWRV9PUFRfTU9ERUxfQUxMT1dMSVNUIiwibWV0YS1sbGFtYS9MbGFtYS0zLjItMUIsbWV0YS1sbGFtYS9MbGFtYS0zLjItM0IsbWV0YS1sbGFtYS9MbGFtYS0zLjEtOEItSW5zdHJ1Y3QsbWV0YS1sbGFtYS9MbGFtYS0zLjEtMTNCLUluc3RydWN0LFRpbnlMbGFtYS9UaW55TGxhbWEtMS4xQi1DaGF0LXYxLjAiKS5zcGxpdCgiLCIpLAogICAgIk9QVF9USFJFU0hfTEFURU5DWV9NUyI6IEVOVigiSElWRV9PUFRfVEhSRVNIX0xBVEVOQ1lfTVMiLCIwIixpbnQpLAogICAgIk9QVF9USFJFU0hfVE9LU19QRVJfUyI6IEVOVigiSElWRV9PUFRfVEhSRVNIX1RPS1NfUEVSX1MiLCIwIixmbG9hdCksCiAgICAiT1BUX1RIUkVTSF9RVUFMSVRZIjogRU5WKCJISVZFX09QVF9USFJFU0hfUVVBTElUWSIsIjAuMDIiLGZsb2F0KSwKICAgICJPUFRfU0FOREJPWF9USU1FT1VUIjogRU5WKCJISVZFX09QVF9TQU5EQk9YX1RJTUVPVVQiLCIxODAiLGludCksCn0Kb3MubWFrZWRpcnMoQ0ZHWyJDVVJWRV9ESVIiXSwgZXhpc3Rfb2s9VHJ1ZSkKb3MubWFrZWRpcnMoQ0ZHWyJTVEFURV9ESVIiXSwgZXhpc3Rfb2s9VHJ1ZSkKCk9WRVJMQVlfRElSID0gb3MucGF0aC5qb2luKENGR1siU1RBVEVfRElSIl0sICJydW50aW1lX292ZXJsYXkiKQpSVU5USU1FX09WRVJSSURFUyA9IG9zLnBhdGguam9pbihDRkdbIlNUQVRFX0RJUiJdLCAicnVudGltZV9vdmVycmlkZXMuanNvbiIpCk9QVF9ESVIgPSBvcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwgIm9wdCIpCk9QVF9QUk9QT1NBTFMgPSBvcy5wYXRoLmpvaW4oT1BUX0RJUiwgInByb3Bvc2Fscy5qc29ubCIpCk9QVF9SRVNVTFRTICAgPSBvcy5wYXRoLmpvaW4oT1BUX0RJUiwgInJlc3VsdHMuanNvbmwiKQpmb3IgcCBpbiAoT1ZFUkxBWV9ESVIsIE9QVF9ESVIpOgogICAgb3MubWFrZWRpcnMocCwgZXhpc3Rfb2s9VHJ1ZSkKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBzZW5zaW5nIC8gbW9kZWwgcGljayAtLS0tLS0tLS0tLS0tLS0tLS0tLS0KZGVmIF9oYXNfZ3B1X2VudigpLT5ib29sOgogICAgYWNjZWw9b3MuZ2V0ZW52KCJTUEFDRV9BQ0NFTEVSQVRPUiIsIiIpLmxvd2VyKCkKICAgIGlmIGFjY2VsIGluICgidDQiLCJhMTAiLCJhMTAwIiwibDQiLCJsNDAiLCJoMTAwIik6IHJldHVybiBUcnVlCiAgICB0cnk6IHJldHVybiB0b3JjaCBpcyBub3QgTm9uZSBhbmQgdG9yY2guY3VkYS5pc19hdmFpbGFibGUoKQogICAgZXhjZXB0IEV4Y2VwdGlvbjogcmV0dXJuIEZhbHNlCgpkZWYgcHJvYmVfY2FwcygpOgogICAgZnJlZV9nYiA9IHNodXRpbC5kaXNrX3VzYWdlKCIuIikuZnJlZS8oMTAyNCoqMykKICAgIHJhbV9nYiA9IHBzdXRpbC52aXJ0dWFsX21lbW9yeSgpLmF2YWlsYWJsZS8oMTAyNCoqMykKICAgIHJldHVybiB7ImZyZWVfZ2IiOmZyZWVfZ2IsInJhbV9nYiI6cmFtX2diLCJncHUiOl9oYXNfZ3B1X2VudigpLAogICAgICAgICAgICAibWF4X2RvY3MiOjcwMDAwIGlmIHJhbV9nYj4xNiBlbHNlICg1MDAwMCBpZiByYW1fZ2I+OCBlbHNlIDEyMDAwKSwKICAgICAgICAgICAgImJhdGNoIjo1MTIgaWYgcmFtX2diPjE2IGVsc2UgKDI1NiBpZiByYW1fZ2I+OCBlbHNlIDY0KX0KCkNBTkRJREFURVM9WwogICAgKCJUaW55TGxhbWEvVGlueUxsYW1hLTEuMUItQ2hhdC12MS4wIiwgMCksCiAgICAoIm1ldGEtbGxhbWEvTGxhbWEtMy4yLTFCIiwwKSwKICAgICgibWV0YS1sbGFtYS9MbGFtYS0zLjItM0IiLDApLAogICAgKCJtZXRhLWxsYW1hL0xsYW1hLTMuMS04Qi1JbnN0cnVjdCIsMTIpLAogICAgKCJtZXRhLWxsYW1hL0xsYW1hLTMuMS0xM0ItSW5zdHJ1Y3QiLDIwKQpdCmRlZiBwaWNrX21vZGVsKCktPlR1cGxlW3N0cixkaWN0XToKICAgIGlmIENGR1siTU9ERUxfT1ZFUlJJREUiXToKICAgICAgICByZXR1cm4gQ0ZHWyJNT0RFTF9PVkVSUklERSJdLCB7ImRldmljZSI6ImN1ZGEiIGlmIF9oYXNfZ3B1X2VudigpIGVsc2UgImNwdSJ9CiAgICBtYXhfdnJhbT1pbnQoQ0ZHWyJMTE1fTUFYX1ZSQU1fR0IiXSBvciAiMCIpCiAgICBpZiBfaGFzX2dwdV9lbnYoKToKICAgICAgICBmb3IgbWlkLG5lZWQgaW4gQ0FORElEQVRFUzoKICAgICAgICAgICAgaWYgbmVlZCBhbmQgKG1heF92cmFtPT0wIG9yIG5lZWQ8PW1heF92cmFtKToKICAgICAgICAgICAgICAgIHJldHVybiBtaWQsIHsiZGV2aWNlIjoiY3VkYSJ9CiAgICBlbHNlOgogICAgICAgIHJhbT1wc3V0aWwudmlydHVhbF9tZW1vcnkoKS50b3RhbC8oMTAyNCoqMykKICAgICAgICBmb3IgbWlkLG5lZWQgaW4gQ0FORElEQVRFUzoKICAgICAgICAgICAgaWYgbmVlZD09MCBhbmQgcmFtPj02OiByZXR1cm4gbWlkLCB7ImRldmljZSI6ImNwdSJ9CiAgICByZXR1cm4gIlRpbnlMbGFtYS9UaW55TGxhbWEtMS4xQi1DaGF0LXYxLjAiLCB7ImRldmljZSI6ImNwdSJ9CgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gZW1iZWRkaW5ncyAvIGN1cnZlcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tCl9FTUJfSUQ9b3MuZ2V0ZW52KCJISVZFX0VNQl9JRCIsInNlbnRlbmNlLXRyYW5zZm9ybWVycy9hbGwtTWluaUxNLUw2LXYyIikKY2xhc3MgR0VDOgogICAgZGVmIF9faW5pdF9fKHNlbGYpOiBzZWxmLm1vZGVsPVNlbnRlbmNlVHJhbnNmb3JtZXIoX0VNQl9JRCkKICAgIGRlZiBlbmNvZGUoc2VsZiwgdGV4dHM6IExpc3Rbc3RyXSk6IHJldHVybiBzZWxmLm1vZGVsLmVuY29kZSh0ZXh0cywgbm9ybWFsaXplX2VtYmVkZGluZ3M9VHJ1ZSkKCmNsYXNzIEN1cnZlU3RvcmU6CiAgICBkZWYgX19pbml0X18oc2VsZiwgZCk6CiAgICAgICAgc2VsZi5kaXI9ZDsgb3MubWFrZWRpcnMoZCwgZXhpc3Rfb2s9VHJ1ZSkKICAgICAgICBzZWxmLmlkeF9wYXRoPW9zLnBhdGguam9pbihkLCJmYWlzcy5pbmRleCIpCiAgICAgICAgc2VsZi5tZXRhX3BhdGg9b3MucGF0aC5qb2luKGQsIm1ldGEuanNvbmwiKQogICAgICAgIHNlbGYuZGltPTM4NDsgc2VsZi5nZWM9R0VDKCkKICAgICAgICBzZWxmLmluZGV4PWZhaXNzLnJlYWRfaW5kZXgoc2VsZi5pZHhfcGF0aCkgaWYgb3MucGF0aC5leGlzdHMoc2VsZi5pZHhfcGF0aCkgZWxzZSBmYWlzcy5JbmRleEZsYXRJUChzZWxmLmRpbSkKICAgIGRlZiBhZGRfdGV4dHMoc2VsZiwgZG9jczpMaXN0W3N0cl0sIG1ldGFzOkxpc3RbRGljdF0pOgogICAgICAgIGlmIG5vdCBkb2NzOiByZXR1cm4KICAgICAgICB2ZWNzPW5wLmFzYXJyYXkoc2VsZi5nZWMuZW5jb2RlKGRvY3MpLCBkdHlwZT0iZmxvYXQzMiIpCiAgICAgICAgc2VsZi5pbmRleC5hZGQodmVjcykKICAgICAgICB3aXRoIG9wZW4oc2VsZi5tZXRhX3BhdGgsImEiLGVuY29kaW5nPSJ1dGYtOCIpIGFzIGY6CiAgICAgICAgICAgIGZvciBtIGluIG1ldGFzOiBmLndyaXRlKGpzb24uZHVtcHMobSwgZW5zdXJlX2FzY2lpPUZhbHNlKSsiXG4iKQogICAgICAgIGZhaXNzLndyaXRlX2luZGV4KHNlbGYuaW5kZXgsIHNlbGYuaWR4X3BhdGgpCiAgICBkZWYgc2VhcmNoKHNlbGYsIHF1ZXJ5OnN0ciwgazppbnQ9NiktPkxpc3RbRGljdF06CiAgICAgICAgaWYgc2VsZi5pbmRleC5udG90YWw9PTA6IHJldHVybiBbXQogICAgICAgIHF2PW5wLmFzYXJyYXkoc2VsZi5nZWMuZW5jb2RlKFtxdWVyeV0pLCBkdHlwZT0iZmxvYXQzMiIpCiAgICAgICAgRCxJPXNlbGYuaW5kZXguc2VhcmNoKHF2LGspCiAgICAgICAgbGluZXM9b3BlbihzZWxmLm1ldGFfcGF0aCwiciIsZW5jb2Rpbmc9InV0Zi04IikucmVhZCgpLnNwbGl0bGluZXMoKSBpZiBvcy5wYXRoLmV4aXN0cyhzZWxmLm1ldGFfcGF0aCkgZWxzZSBbXQogICAgICAgIG91dD1bXQogICAgICAgIGZvciBpIGluIElbMF06CiAgICAgICAgICAgIGlmIDA8PWk8bGVuKGxpbmVzKToKICAgICAgICAgICAgICAgIHRyeTogb3V0LmFwcGVuZChqc29uLmxvYWRzKGxpbmVzW2ldKSkKICAgICAgICAgICAgICAgIGV4Y2VwdDogcGFzcwogICAgICAgIHJldHVybiBvdXQKICAgIGRlZiBzZWFyY2hfd2l0aF9zY29yZXMoc2VsZiwgcXVlcnk6c3RyLCBrOmludD02KToKICAgICAgICBpZiBzZWxmLmluZGV4Lm50b3RhbD09MDogcmV0dXJuIFtdLCBbXQogICAgICAgIHF2PW5wLmFzYXJyYXkoc2VsZi5nZWMuZW5jb2RlKFtxdWVyeV0pLCBkdHlwZT0iZmxvYXQzMiIpCiAgICAgICAgRCxJPXNlbGYuaW5kZXguc2VhcmNoKHF2LGspCiAgICAgICAgbGluZXM9b3BlbihzZWxmLm1ldGFfcGF0aCwiciIsZW5jb2Rpbmc9InV0Zi04IikucmVhZCgpLnNwbGl0bGluZXMoKSBpZiBvcy5wYXRoLmV4aXN0cyhzZWxmLm1ldGFfcGF0aCkgZWxzZSBbXQogICAgICAgIG1ldGFzLCBzY29yZXMgPSBbXSwgW10KICAgICAgICBmb3IgaWR4LCBzYyBpbiB6aXAoSVswXSwgRFswXSk6CiAgICAgICAgICAgIGlmIDA8PWlkeDxsZW4obGluZXMpOgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIG1ldGFzLmFwcGVuZChqc29uLmxvYWRzKGxpbmVzW2lkeF0pKQogICAgICAgICAgICAgICAgICAgIHNjb3Jlcy5hcHBlbmQoZmxvYXQobWF4KDAuMCwgbWluKDEuMCwgc2MpKSkpCiAgICAgICAgICAgICAgICBleGNlcHQ6IHBhc3MKICAgICAgICByZXR1cm4gbWV0YXMsIHNjb3JlcwoKT0ZGTElORV9NQVJLID0gb3MucGF0aC5qb2luKENGR1siQ1VSVkVfRElSIl0sICIub2ZmbGluZV9yZWFkeSIpCmRlZiBfY3VydmVzX3JlYWR5KGN1cnZlX2RpcjpzdHIpLT5ib29sOgogICAgaWR4PW9zLnBhdGguam9pbihjdXJ2ZV9kaXIsImZhaXNzLmluZGV4IikKICAgIGlmIG9zLnBhdGguZXhpc3RzKE9GRkxJTkVfTUFSSyk6CiAgICAgICAgdHJ5OiByZXR1cm4ganNvbi5sb2FkKG9wZW4oT0ZGTElORV9NQVJLKSkuZ2V0KCJvayIsVHJ1ZSkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uOiByZXR1cm4gVHJ1ZQogICAgaWYgb3MucGF0aC5leGlzdHMoaWR4KToKICAgICAgICB0cnk6IHJldHVybiBmYWlzcy5yZWFkX2luZGV4KGlkeCkubnRvdGFsPjAKICAgICAgICBleGNlcHQgRXhjZXB0aW9uOiByZXR1cm4gRmFsc2UKICAgIHJldHVybiBGYWxzZQpkZWYgX21hcmtfb2ZmbGluZV9yZWFkeSgpOgogICAgdHJ5OiBqc29uLmR1bXAoeyJvayI6VHJ1ZSwidHMiOnRpbWUudGltZSgpfSwgb3BlbihPRkZMSU5FX01BUkssInciLGVuY29kaW5nPSJ1dGYtOCIpKQogICAgZXhjZXB0IEV4Y2VwdGlvbjogcGFzcwoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0gSEYgRGF0YXNldHMgYm9vdHN0cmFwIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCkRFRkFVTFRfU09VUkNFUz1bImpodS1jbHNwL2pmbGVnIiwiYmVhMjAxOXN0L3dpX2xvY25lc3MiLCJmY2UtbTcyMTA5L21hc2NvcnB1cyIsInJhanB1cmthci9zcXVhZF92MiIsCiAgICAgICAgICAgICAgICAgIk9wZW5STC9kYWlseV9kaWFsb2ciLCJ0ZXR0aS9zcGVsbGluZy1kYXRhc2V0LWV4dGVuZGVkIiwiSGVsc2lua2ktTkxQL29wdXMtMTAwIiwiZmFjZWJvb2svZmxvcmVzIiwKICAgICAgICAgICAgICAgICAiSHVnZ2luZ0ZhY2VINC9NdWx0aWxpbmd1YWwtVGhpbmtpbmciLCJiaWdzY2llbmNlL3hQMyIsImFsbGVuYWkvc2NpcSIsImFsbGVuYWkvYzQiLAogICAgICAgICAgICAgICAgICJtb3ppbGxhLWZvdW5kYXRpb24vY29tbW9uX3ZvaWNlXzE3XzAiLCJiZW5lLWdlcy9lbl9jbXVkaWN0Iiwib3BlbnNsci9saWJyaXNwZWVjaF9hc3IiLCJjb25jZXB0bmV0NS9jb25jZXB0bmV0NSIsImdyYW1tYXJseS9jb2VkaXQiXQoKZGVmIF9pdGVyX3RleHQoZGF0YXNldF9uYW1lOnN0ciwgc3BsaXQ9InRyYWluIik6CiAgICBmcm9tIGRhdGFzZXRzIGltcG9ydCBsb2FkX2RhdGFzZXQKICAgIHRyeToKICAgICAgICBkcz1sb2FkX2RhdGFzZXQoZGF0YXNldF9uYW1lLCBzcGxpdD1zcGxpdCwgc3RyZWFtaW5nPVRydWUpCiAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgIGRzPWxvYWRfZGF0YXNldChkYXRhc2V0X25hbWUsIHNwbGl0PXNwbGl0KQogICAgZm9yIGV4IGluIGRzOgogICAgICAgIHRleHQgPSBleC5nZXQoInRleHQiKSBvciBleC5nZXQoInNlbnRlbmNlIikgb3IgZXguZ2V0KCJjb250ZW50Iikgb3IgZXguZ2V0KCJxdWVzdGlvbiIpCiAgICAgICAgaWYgbm90IHRleHQ6CiAgICAgICAgICAgIGlmICJ0cmFuc2xhdGlvbiIgaW4gZXggYW5kIGlzaW5zdGFuY2UoZXhbInRyYW5zbGF0aW9uIl0sIGRpY3QpOgogICAgICAgICAgICAgICAgdGRpY3Q9ZXhbInRyYW5zbGF0aW9uIl07IHRleHQ9IiB8ICIuam9pbihbZiJ7a306e3Z9IiBmb3Igayx2IGluIHRkaWN0Lml0ZW1zKCkgaWYgaXNpbnN0YW5jZSh2LHN0cildKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgdGV4dD1zdHIoZXgpCiAgICAgICAgeWllbGQgeyJ0ZXh0Ijogc3RyKHRleHQpfQoKZGVmIF9wbGFuX29yZGVyKHNyY3M6IExpc3Rbc3RyXSktPkxpc3Rbc3RyXToKICAgIGZpcnN0PVsiamh1LWNsc3AvamZsZWciLCJiZWEyMDE5c3Qvd2lfbG9jbmVzcyIsImZjZS1tNzIxMDkvbWFzY29ycHVzIiwicmFqcHVya2FyL3NxdWFkX3YyIiwiT3BlblJML2RhaWx5X2RpYWxvZyIsInRldHRpL3NwZWxsaW5nLWRhdGFzZXQtZXh0ZW5kZWQiXQogICAgb3JkZXJlZD1bcyBmb3IgcyBpbiBmaXJzdCBpZiBzIGluIHNyY3NdCiAgICBmb3IgcyBpbiBzcmNzOgogICAgICAgIGlmIHMgbm90IGluIG9yZGVyZWQ6IG9yZGVyZWQuYXBwZW5kKHMpCiAgICByZXR1cm4gb3JkZXJlZAoKY2xhc3MgTGlicmFyaWFuQ3VydmU6CiAgICBkZWYgX19pbml0X18oc2VsZiwgc3RvcmUpOiBzZWxmLnN0b3JlPXN0b3JlCiAgICBkZWYgaW5nZXN0X3BhaXJzKHNlbGYsIHRleHRzLCBtZXRhcywgc2NvcGUpOgogICAgICAgIG1ldGFzX3Njb3BlZD1bXQogICAgICAgIGZvciBtLHQgaW4gemlwKG1ldGFzLHRleHRzKToKICAgICAgICAgICAgbTI9ZGljdChtKTsgbTJbInNjb3BlIl09c2NvcGU7IG0yWyJ0ZXh0Il09dFs6NTAwXQogICAgICAgICAgICBtZXRhc19zY29wZWQuYXBwZW5kKG0yKQogICAgICAgIHNlbGYuc3RvcmUuYWRkX3RleHRzKHRleHRzLCBtZXRhc19zY29wZWQpCiAgICBkZWYgcmV0cmlldmVfc2NvcGVkX3dpdGhfc2NvcmVzKHNlbGYsIHF1ZXJ5LCBlZmZlY3RpdmVfcm9sZSwgY2FsbGVyX2lkLCBrPTYpOgogICAgICAgIGl0ZW1zLCBzY29yZXMgPSBzZWxmLnN0b3JlLnNlYXJjaF93aXRoX3Njb3JlcyhxdWVyeSwgaz1rKjQpCiAgICAgICAgaWYgZWZmZWN0aXZlX3JvbGU9PSJvd25lciI6IHJldHVybiBpdGVtc1s6a10sIHNjb3Jlc1s6a10KICAgICAgICBhbGxvd2VkPXsiZ2VuZXJhbCJ9CiAgICAgICAgaWYgY2FsbGVyX2lkOiBhbGxvd2VkLmFkZChmInVzZXI6e2NhbGxlcl9pZH0iKQogICAgICAgIGZpbHRfaSxmaWx0X3M9W10sW10KICAgICAgICBmb3IgaXQsc2MgaW4gemlwKGl0ZW1zLCBzY29yZXMpOgogICAgICAgICAgICBpZiBpdC5nZXQoInNjb3BlIiwiZ2VuZXJhbCIpIGluIGFsbG93ZWQ6CiAgICAgICAgICAgICAgICBmaWx0X2kuYXBwZW5kKGl0KTsgZmlsdF9zLmFwcGVuZChzYykKICAgICAgICAgICAgaWYgbGVuKGZpbHRfaSk+PWs6IGJyZWFrCiAgICAgICAgcmV0dXJuIGZpbHRfaSwgZmlsdF9zCgpkZWYgaW5nZXN0X2FsbChjdXJ2ZV9kaXI6c3RyLCBzb3VyY2VzOiBPcHRpb25hbFtMaXN0W3N0cl1dPU5vbmUsIHNjb3BlPSJnZW5lcmFsIik6CiAgICBjYXBzPXByb2JlX2NhcHMoKQogICAgc3RvcmU9Q3VydmVTdG9yZShjdXJ2ZV9kaXIpOyBsaWI9TGlicmFyaWFuQ3VydmUoc3RvcmUpCiAgICBvcy5tYWtlZGlycyhjdXJ2ZV9kaXIsIGV4aXN0X29rPVRydWUpCiAgICBsb2dmPW9zLnBhdGguam9pbihjdXJ2ZV9kaXIsImluZ2VzdF9sb2cuanNvbmwiKQogICAgY291bnRfdG90YWw9MDsgc291cmNlcz1zb3VyY2VzIG9yIERFRkFVTFRfU09VUkNFUwogICAgZm9yIGRzIGluIF9wbGFuX29yZGVyKHNvdXJjZXMpOgogICAgICAgIGNvdW50PTA7IGJ0PVtdOyBibT1bXQogICAgICAgIHRyeToKICAgICAgICAgICAgZm9yIHJlYyBpbiBfaXRlcl90ZXh0KGRzKToKICAgICAgICAgICAgICAgIHR4dD0ocmVjLmdldCgidGV4dCIpIG9yICIiKS5zdHJpcCgpCiAgICAgICAgICAgICAgICBpZiBub3QgdHh0OiBjb250aW51ZQogICAgICAgICAgICAgICAgYnQuYXBwZW5kKHR4dCk7IGJtLmFwcGVuZCh7ImRhdGFzZXQiOmRzLCJ0ZXh0Ijp0eHRbOjUwMF19KQogICAgICAgICAgICAgICAgaWYgbGVuKGJ0KT49Y2Fwc1siYmF0Y2giXToKICAgICAgICAgICAgICAgICAgICBsaWIuaW5nZXN0X3BhaXJzKGJ0LGJtLHNjb3BlKTsgY291bnQrPWxlbihidCk7IGNvdW50X3RvdGFsKz1sZW4oYnQpOyBidCxibT1bXSxbXQogICAgICAgICAgICAgICAgaWYgY291bnQ+PWNhcHNbIm1heF9kb2NzIl06IGJyZWFrCiAgICAgICAgICAgIGlmIGJ0OiBsaWIuaW5nZXN0X3BhaXJzKGJ0LGJtLHNjb3BlKTsgY291bnQrPWxlbihidCk7IGNvdW50X3RvdGFsKz1sZW4oYnQpCiAgICAgICAgICAgIHdpdGggb3Blbihsb2dmLCJhIixlbmNvZGluZz0idXRmLTgiKSBhcyBmOiBmLndyaXRlKGpzb24uZHVtcHMoeyJkYXRhc2V0IjpkcywiaW5nZXN0ZWQiOmNvdW50fSkrIlxuIikKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIHdpdGggb3Blbihsb2dmLCJhIixlbmNvZGluZz0idXRmLTgiKSBhcyBmOiBmLndyaXRlKGpzb24uZHVtcHMoeyJkYXRhc2V0IjpkcywiZXJyb3IiOnN0cihlKX0pKyJcbiIpCiAgICByZXR1cm4gY291bnRfdG90YWwKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLSBsaXZlIHNlYXJjaCArIFJTUyDihpIgY3VydmVzIC0tLS0tLS0tLS0tLS0tLS0tLS0tCk9OTElORV9EQj1vcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwib25saW5lX3NlZW4uanNvbiIpCmRlZiBfbG9hZF9qc29uKHBhdGgsIGRlZmF1bHQpOgogICAgaWYgb3MucGF0aC5leGlzdHMocGF0aCk6CiAgICAgICAgdHJ5OiByZXR1cm4ganNvbi5sb2FkKG9wZW4ocGF0aCwiciIsZW5jb2Rpbmc9InV0Zi04IikpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjogcmV0dXJuIGRlZmF1bHQKICAgIHJldHVybiBkZWZhdWx0CmRlZiBfc2F2ZV9qc29uKHBhdGgsIGRhdGEpOiBqc29uLmR1bXAoZGF0YSwgb3BlbihwYXRoLCJ3IixlbmNvZGluZz0idXRmLTgiKSwgaW5kZW50PTIpCgpkZWYgb25saW5lX2F2YWlsYWJsZSh0aW1lb3V0OmludCktPmJvb2w6CiAgICB0cnk6CiAgICAgICAgcmVxdWVzdHMuZ2V0KCJodHRwczovL2h1Z2dpbmdmYWNlLmNvIiwgdGltZW91dD10aW1lb3V0KQogICAgICAgIHJldHVybiBUcnVlCiAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgIHJldHVybiBGYWxzZQoKZGVmIF9oYXNoKHM6c3RyKS0+c3RyOgogICAgcmV0dXJuIGhhc2hsaWIuc2hhMShzLmVuY29kZSgidXRmLTgiLCJpZ25vcmUiKSkuaGV4ZGlnZXN0KCkKCmRlZiBmZXRjaF9yc3ModXJsczpMaXN0W3N0cl0sIHRpbWVvdXQ6aW50PTgsIGxpbWl0OmludD01MCktPkxpc3RbRGljdF06CiAgICBpdGVtcz1bXQogICAgZm9yIHUgaW4gdXJsczoKICAgICAgICB0cnk6CiAgICAgICAgICAgIGY9ZmVlZHBhcnNlci5wYXJzZSh1KQogICAgICAgICAgICBmb3IgZSBpbiBmLmVudHJpZXNbOmxpbWl0XToKICAgICAgICAgICAgICAgIGl0ZW1zLmFwcGVuZCh7InRpdGxlIjplLmdldCgidGl0bGUiLCIiKSwibGluayI6ZS5nZXQoImxpbmsiLCIiKSwic3VtbWFyeSI6ZS5nZXQoInN1bW1hcnkiKSBvciBlLmdldCgiZGVzY3JpcHRpb24iLCIiKSwicHVibGlzaGVkIjplLmdldCgicHVibGlzaGVkIikgb3IgZS5nZXQoInVwZGF0ZWQiLCIiKSwic291cmNlIjp1fSkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICAgICBwYXNzCiAgICByZXR1cm4gaXRlbXMKCmRlZiB3ZWJfc2VhcmNoX3NuaXBwZXRzKHF1ZXJ5OnN0ciwgbWF4X3Jlc3VsdHM6aW50PTUsIHRpbWVvdXQ6aW50PTgpLT5saXN0OgogICAgb3V0PVtdCiAgICB0cnk6CiAgICAgICAgd2l0aCBEREdTKHRpbWVvdXQ9dGltZW91dCkgYXMgZGRnczoKICAgICAgICAgICAgZm9yIHIgaW4gZGRncy50ZXh0KHF1ZXJ5LCBtYXhfcmVzdWx0cz1tYXhfcmVzdWx0cyk6CiAgICAgICAgICAgICAgICBpZiByIGFuZCByLmdldCgiYm9keSIpOgogICAgICAgICAgICAgICAgICAgIG91dC5hcHBlbmQoeyJ0aXRsZSI6ci5nZXQoInRpdGxlIiwiIiksImhyZWYiOnIuZ2V0KCJocmVmIiwiIiksImJvZHkiOnIuZ2V0KCJib2R5IiwiIil9KQogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICBwYXNzCiAgICByZXR1cm4gb3V0CgojIC0tLS0tLS0tLS0tLS0tLS0tLS0gUkJBQyAvIHVzZXJzIC8gbG9ja291dHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KVVNFUlNfREI9b3MucGF0aC5qb2luKENGR1siU1RBVEVfRElSIl0sInVzZXJzLmpzb24iKQpMT0NLU19EQj1vcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwibG9ja291dHMuanNvbiIpClZPSUNFU19EQj1vcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwidm9pY2VzLmpzb24iKQpBREFQVF9EQj1vcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwic3BlZWNoX2FkYXB0Lmpzb24iKQoKZGVmIF9pbml0X3VzZXJzKCk6CiAgICBkPXsib3duZXIiOnsiaWQiOiJvd25lcjoxIiwibmFtZSI6Q0ZHWyJPV05FUl9OQU1FIl0sInJvbGUiOiJvd25lciIsInBhc3MiOkNGR1siT1dORVJfUEFTUyJdLCJzZWNvbmQiOkNGR1siT1dORVJfU0VDT05EIl0sInByZWZzIjp7ImFjdGl2YXRpb25fbmFtZXMiOltDRkdbIkFHRU5UX05BTUUiXV0sImxhbmd1YWdlIjoiZW4ifX0sCiAgICAgICAiYWRtaW5zX3N1cGVyIjpbXSwiYWRtaW5zX2dlbmVyYWwiOltdLCJ1c2VycyI6W119CiAgICBfc2F2ZV9qc29uKFVTRVJTX0RCLGQpOyByZXR1cm4gZApkZWYgX2xvYWRfdXNlcnMoKToKICAgIGQ9X2xvYWRfanNvbihVU0VSU19EQiwgTm9uZSk7IHJldHVybiBkIGlmIGQgZWxzZSBfaW5pdF91c2VycygpCmRlZiBfZmluZF91c2VyKGQsIG5hbWVfb3JfaWQpOgogICAgcG9vbHM9Wygib3duZXIiLFtkLmdldCgib3duZXIiKV0pLCgiYWRtaW5fc3VwZXIiLGRbImFkbWluc19zdXBlciJdKSwoImFkbWluX2dlbmVyYWwiLGRbImFkbWluc19nZW5lcmFsIl0pLCgidXNlciIsZFsidXNlcnMiXSldCiAgICBmb3Igcm9sZSxwb29sIGluIHBvb2xzOgogICAgICAgIGZvciB1IGluIHBvb2wgb3IgW106CiAgICAgICAgICAgIGlmIHUgYW5kICh1LmdldCgiaWQiKT09bmFtZV9vcl9pZCBvciB1LmdldCgibmFtZSIpPT1uYW1lX29yX2lkKTogcmV0dXJuIHUsIHJvbGUKICAgIHJldHVybiBOb25lLCBOb25lCgpQRVJNUz17CiAgICAib3duZXIiOnsiY2FuX2FkZCI6WyJhZG1pbl9zdXBlciIsImFkbWluX2dlbmVyYWwiLCJ1c2VyIl0sImNhbl9yZW1vdmUiOlsiYWRtaW5fc3VwZXIiLCJhZG1pbl9nZW5lcmFsIiwidXNlciJdLAogICAgICAgICAgICAgImNhbl9lZGl0X3JvbGVfb2YiOlsiYWRtaW5fc3VwZXIiLCJhZG1pbl9nZW5lcmFsIiwidXNlciJdLCJjYW5fZWRpdF9wcm9maWxlX29mIjpbIm93bmVyIiwiYWRtaW5fc3VwZXIiLCJhZG1pbl9nZW5lcmFsIiwidXNlciJdLAogICAgICAgICAgICAgImNhbl92aWV3X3Njb3BlcyI6ImFsbCIsIm1haW50ZW5hbmNlIjoiZnVsbCIsImNvZGVfZWRpdCI6ImFwcHJvdmVfYW5kX2VkaXQifSwKICAgICJhZG1pbl9zdXBlciI6eyJjYW5fYWRkIjpbImFkbWluX2dlbmVyYWwiLCJ1c2VyIl0sImNhbl9yZW1vdmUiOlsiYWRtaW5fZ2VuZXJhbCIsInVzZXIiXSwKICAgICAgICAgICAgICJjYW5fZWRpdF9yb2xlX29mIjpbImFkbWluX2dlbmVyYWwiLCJ1c2VyIl0sImNhbl9lZGl0X3Byb2ZpbGVfb2YiOlsiYWRtaW5fZ2VuZXJhbCIsInVzZXIiXSwKICAgICAgICAgICAgICJjYW5fdmlld19zY29wZXMiOiJzZWxmX29ubHkiLCJtYWludGVuYW5jZSI6ImFkdmFuY2VkIiwiY29kZV9lZGl0Ijoic3VnZ2VzdF9vbmx5In0sCiAgICAiYWRtaW5fZ2VuZXJhbCI6eyJjYW5fYWRkIjpbInVzZXIiXSwiY2FuX3JlbW92ZSI6WyJ1c2VyIl0sImNhbl9lZGl0X3JvbGVfb2YiOlsidXNlciJdLCJjYW5fZWRpdF9wcm9maWxlX29mIjpbInVzZXIiXSwKICAgICAgICAgICAgICJjYW5fdmlld19zY29wZXMiOiJzZWxmX29ubHkiLCJtYWludGVuYW5jZSI6ImJhc2ljIiwiY29kZV9lZGl0Ijoic3VnZ2VzdF9vbmx5In0sCiAgICAidXNlciI6eyJjYW5fYWRkIjpbXSwiY2FuX3JlbW92ZSI6W10sImNhbl9lZGl0X3JvbGVfb2YiOltdLCJjYW5fZWRpdF9wcm9maWxlX29mIjpbInVzZXIiXSwKICAgICAgICAgICAgICJjYW5fdmlld19zY29wZXMiOiJzZWxmX29ubHkiLCJtYWludGVuYW5jZSI6Im5vbmUiLCJjb2RlX2VkaXQiOiJub25lIn0sCiAgICAiZ3Vlc3QiOnsiY2FuX2FkZCI6W10sImNhbl9yZW1vdmUiOltdLCJjYW5fZWRpdF9yb2xlX29mIjpbXSwiY2FuX2VkaXRfcHJvZmlsZV9vZiI6W10sCiAgICAgICAgICAgICAiY2FuX3ZpZXdfc2NvcGVzIjoic2VsZl9vbmx5IiwibWFpbnRlbmFuY2UiOiJub25lIiwiY29kZV9lZGl0Ijoibm9uZSJ9LAp9CgpkZWYgYXR0ZW1wdF9sb2dpbihuYW1lX29yX2lkOnN0ciwgcGFzc3dvcmQ6c3RyPSIiLCBzZWNvbmQ6T3B0aW9uYWxbc3RyXT1Ob25lKToKICAgIGQ9X2xvYWRfdXNlcnMoKTsgbG9ja3M9X2xvYWRfanNvbihMT0NLU19EQix7IH0pCiAgICBkZWYgbG9ja19mYWlsKGxpZCwgbXNnKToKICAgICAgICBzdD1sb2Nrcy5nZXQobGlkLCB7ImZhaWxzIjowLCJ1bnRpbCI6MH0pOyBzdFsiZmFpbHMiXT1zdC5nZXQoImZhaWxzIiwwKSsxCiAgICAgICAgZHVyPTE4MCBpZiBzdFsiZmFpbHMiXT49MyBlbHNlIDA7IHN0WyJ1bnRpbCJdPXRpbWUudGltZSgpK2R1ciBpZiBkdXIgZWxzZSAwCiAgICAgICAgbG9ja3NbbGlkXT1zdDsgX3NhdmVfanNvbihMT0NLU19EQixsb2Nrcyk7IHJldHVybiBGYWxzZSwgbXNnCiAgICB1LF89X2ZpbmRfdXNlcihkLCBuYW1lX29yX2lkKQogICAgaWYgbm90IHU6IHJldHVybiBGYWxzZSwgIlByb2ZpbGUgbm90IGZvdW5kLiIKICAgIHJvbGU9dS5nZXQoInJvbGUiLCJ1c2VyIik7IGxpZD11LmdldCgiaWQiLCB1LmdldCgibmFtZSIpKTsgbm93PXRpbWUudGltZSgpCiAgICBzdD1sb2Nrcy5nZXQobGlkLCB7ImZhaWxzIjowLCJ1bnRpbCI6MH0pCiAgICBpZiBub3cgPCBzdC5nZXQoInVudGlsIiwwKTogcmV0dXJuIEZhbHNlLCBmIkxvY2tlZDsgdHJ5IGFnYWluIGluIH57aW50KHN0Wyd1bnRpbCddLW5vdyl9cy4iCiAgICBpZiByb2xlIGluICgiYWRtaW5fZ2VuZXJhbCIsImFkbWluX3N1cGVyIiwib3duZXIiKToKICAgICAgICBpZiByb2xlPT0ib3duZXIiOgogICAgICAgICAgICBpZiBwYXNzd29yZCE9dS5nZXQoInBhc3MiKSBvciAodS5nZXQoInNlY29uZCIpIGFuZCBzZWNvbmQhPXUuZ2V0KCJzZWNvbmQiKSk6CiAgICAgICAgICAgICAgICByZXR1cm4gbG9ja19mYWlsKGxpZCwgIk93bmVyIGNyZWRlbnRpYWxzIGluY29ycmVjdC4iKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGlmIHBhc3N3b3JkIT11LmdldCgicGFzcyIpOiByZXR1cm4gbG9ja19mYWlsKGxpZCwgIkFkbWluIHBhc3N3b3JkIGluY29ycmVjdC4iKQogICAgbG9ja3NbbGlkXT17ImZhaWxzIjowLCJ1bnRpbCI6MH07IF9zYXZlX2pzb24oTE9DS1NfREIsbG9ja3MpCiAgICByZXR1cm4gVHJ1ZSwgZiJXZWxjb21lLCB7dS5nZXQoJ25hbWUnKX0gKHtyb2xlfSkuIgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tIHZvaWNlOiBBU1IvVFRTL3Bob25pY3MgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCkcyUCA9IEcycCgpCkFTUl9NT0RFTFM9eyJ0aW55IjoidGlueSIsImJhc2UiOiJiYXNlIiwic21hbGwiOiJzbWFsbCIsIm1lZGl1bSI6Im1lZGl1bSIsImxhcmdlLXYzIjoibGFyZ2UtdjMifQpkZWYgX2Fzcl9tb2RlbF9uYW1lKCk6IHJldHVybiBBU1JfTU9ERUxTLmdldChDRkdbIkFTUl9TSVpFIl0sInNtYWxsIikKX0FTUj1Ob25lCmRlZiBnZXRfYXNyKCk6CiAgICBnbG9iYWwgX0FTUgogICAgaWYgX0FTUiBpcyBub3QgTm9uZTogcmV0dXJuIF9BU1IKICAgIHNpemU9X2Fzcl9tb2RlbF9uYW1lKCk7IGRldmljZT0iY3VkYSIgaWYgKF9oYXNfZ3B1X2VudigpKSBlbHNlICJjcHUiCiAgICBjb21wdXRlX3R5cGU9ImZsb2F0MTYiIGlmIGRldmljZT09ImN1ZGEiIGVsc2UgImludDgiCiAgICBfQVNSPVdoaXNwZXJNb2RlbChzaXplLCBkZXZpY2U9ZGV2aWNlLCBjb21wdXRlX3R5cGU9Y29tcHV0ZV90eXBlKTsgcmV0dXJuIF9BU1IKClBJUEVSX01PREVMUz17CiAgICAiZW4iOiAoImh0dHBzOi8vZ2l0aHViLmNvbS9yaGFzc3B5L3BpcGVyL3JlbGVhc2VzL2Rvd25sb2FkL3YwLjAuMi9lbl9VUy1hbXktbG93Lm9ubngiLAogICAgICAgICAgICJodHRwczovL2dpdGh1Yi5jb20vcmhhc3NweS9waXBlci9yZWxlYXNlcy9kb3dubG9hZC92MC4wLjIvZW5fVVMtYW15LWxvdy5vbm54Lmpzb24iKSwKICAgICJlcyI6ICgiaHR0cHM6Ly9naXRodWIuY29tL3JoYXNzcHkvcGlwZXIvcmVsZWFzZXMvZG93bmxvYWQvdjAuMC4yL2VzX0VTLWRhdmVmeC1tZWRpdW0ub25ueCIsCiAgICAgICAgICAgImh0dHBzOi8vZ2l0aHViLmNvbS9yaGFzc3B5L3BpcGVyL3JlbGVhc2VzL2Rvd25sb2FkL3YwLjAuMi9lc19FUy1kYXZlZngtbWVkaXVtLm9ubnguanNvbiIpLAogICAgImZyIjogKCJodHRwczovL2dpdGh1Yi5jb20vcmhhc3NweS9waXBlci9yZWxlYXNlcy9kb3dubG9hZC92MC4wLjIvZnJfRlItZ2lsbGVzLW1lZGl1bS5vbm54IiwKICAgICAgICAgICAiaHR0cHM6Ly9naXRodWIuY29tL3JoYXNzcHkvcGlwZXIvcmVsZWFzZXMvZG93bmxvYWQvdjAuMC4yL2ZyX0ZSLWdpbGxlcy1tZWRpdW0ub25ueC5qc29uIiksCiAgICAiZGUiOiAoImh0dHBzOi8vZ2l0aHViLmNvbS9yaGFzc3B5L3BpcGVyL3JlbGVhc2VzL2Rvd25sb2FkL3YwLjAuMi9kZV9ERS10aG9yc3Rlbi1sb3cub25ueCIsCiAgICAgICAgICAgImh0dHBzOi8vZ2l0aHViLmNvbS9yaGFzc3B5L3BpcGVyL3JlbGVhc2VzL2Rvd25sb2FkL3YwLjAuMi9kZV9ERS10aG9yc3Rlbi1sb3cub25ueC5qc29uIiksCiAgICAiemgiOiAoImh0dHBzOi8vZ2l0aHViLmNvbS9yaGFzc3B5L3BpcGVyL3JlbGVhc2VzL2Rvd25sb2FkL3YwLjAuMi96aF9DTi1odWF5YW4tbG93Lm9ubngiLAogICAgICAgICAgICJodHRwczovL2dpdGh1Yi5jb20vcmhhc3NweS9waXBlci9yZWxlYXNlcy9kb3dubG9hZC92MC4wLjIvemhfQ04taHVheWFuLWxvdy5vbm54Lmpzb24iKSwKfQpkZWYgX2Rvd25sb2FkKHVybCxkc3QpOgogICAgaWYgb3MucGF0aC5leGlzdHMoZHN0KTogcmV0dXJuIGRzdAogICAgb3MubWFrZWRpcnMob3MucGF0aC5kaXJuYW1lKGRzdCksZXhpc3Rfb2s9VHJ1ZSk7IHVybGxpYi5yZXF1ZXN0LnVybHJldHJpZXZlKHVybCxkc3QpOyByZXR1cm4gZHN0Cl9UVFNfQ0FDSEU9e30KZGVmIGdldF90dHMobGFuZz0iZW4iKToKICAgIGxhbmc9bGFuZyBpZiBsYW5nIGluIFBJUEVSX01PREVMUyBlbHNlICJlbiIKICAgIGlmIGxhbmcgaW4gX1RUU19DQUNIRTogcmV0dXJuIF9UVFNfQ0FDSEVbbGFuZ10KICAgIG11LGN1PVBJUEVSX01PREVMU1tsYW5nXTsgbT1fZG93bmxvYWQobXUsZiIuL21vZGVscy9waXBlci97b3MucGF0aC5iYXNlbmFtZShtdSl9Iik7IGM9X2Rvd25sb2FkKGN1LGYiLi9tb2RlbHMvcGlwZXIve29zLnBhdGguYmFzZW5hbWUoY3UpfSIpCiAgICB2PVBpcGVyVm9pY2UubG9hZChtLGMpOyBfVFRTX0NBQ0hFW2xhbmddPXY7IHJldHVybiB2CgpkZWYgX2VtYmVkX21mY2MocGF0aCktPm5wLm5kYXJyYXk6CiAgICB5LCBzciA9IGxpYnJvc2EubG9hZChwYXRoLCBzcj0xNjAwMCkKICAgIG1mPWxpYnJvc2EuZmVhdHVyZS5tZmNjKHk9eSwgc3I9c3IsIG5fbWZjYz0yMCkKICAgIHJldHVybiBtZi5tZWFuKGF4aXM9MSkKZGVmIGVucm9sbF92b2ljZSh1aWQ6c3RyLCBwYXRoOnN0ciktPmJvb2w6CiAgICBkYj1fbG9hZF9qc29uKFZPSUNFU19EQiwge30pOyBkYlt1aWRdPV9lbWJlZF9tZmNjKHBhdGgpLmFzdHlwZShmbG9hdCkudG9saXN0KCk7IF9zYXZlX2pzb24oVk9JQ0VTX0RCLCBkYik7IHJldHVybiBUcnVlCmRlZiBpZGVudGlmeV92b2ljZShwYXRoOnN0ciwgdGhyZXNob2xkOmZsb2F0PTAuNzApLT5PcHRpb25hbFtzdHJdOgogICAgZGI9X2xvYWRfanNvbihWT0lDRVNfREIsIHt9KTsgCiAgICBpZiBub3QgZGI6IHJldHVybiBOb25lCiAgICBlbWI9X2VtYmVkX21mY2MocGF0aCkucmVzaGFwZSgxLC0xKQogICAga2V5cz1saXN0KGRiLmtleXMoKSk7IG1hdHM9bnAudnN0YWNrKFtucC5hcnJheShkYltrXSkucmVzaGFwZSgxLC0xKSBmb3IgayBpbiBrZXlzXSkuc3F1ZWV6ZSgpCiAgICBzaW1zPWNvc2luZV9zaW1pbGFyaXR5KGVtYiwgbWF0cylbMF07IGk9aW50KG5wLmFyZ21heChzaW1zKSk7IHJldHVybiBrZXlzW2ldIGlmIHNpbXNbaV0+PXRocmVzaG9sZCBlbHNlIE5vbmUKCl9CQVNJQz17J2EnOidhIGFzIGluIGFwcGxlIC/Dpi8nLCdlJzonZSBhcyBpbiBlbGVwaGFudCAvyZsvJywnaSc6J2kgYXMgaW4gaWdsb28gL8mqLycsJ28nOidvIGFzIGluIG9jdG9wdXMgL8mSLycsJ3UnOid1IGFzIGluIHVtYnJlbGxhIC/KjC8nLAogICAgICAgICdjJzonYyBhcyBpbiBjYXQgL2svIChiZWZvcmUgZS9pL3kgb2Z0ZW4gL3MvKScsJ2cnOidnIGFzIGluIGdvYXQgL2cvIChiZWZvcmUgZS9pL3kgb2Z0ZW4gc29mdCAvZMqSLyknLCd5JzoneSBhcyBpbiB5ZWxsb3cgL2ovIG9yIGhhcHB5IC9pLyd9CmRlZiBwaG9uaWNzKHdvcmQ6c3RyKS0+c3RyOgogICAgdG9rcz1HMlAod29yZCk7IHBob25lcz1bdCBmb3IgdCBpbiB0b2tzIGlmIHJlLm1hdGNoKHIiW0EtWl0rWzAtMl0/JCIsIHQpXQogICAgaGludHM9W107IAogICAgZm9yIGNoIGluIHdvcmQubG93ZXIoKToKICAgICAgICBpZiBjaCBpbiBfQkFTSUMgYW5kIF9CQVNJQ1tjaF0gbm90IGluIGhpbnRzOiBoaW50cy5hcHBlbmQoX0JBU0lDW2NoXSkKICAgIHJldHVybiBmIlBob25lbWVzOiB7JyAnLmpvaW4ocGhvbmVzKX0gfCBIaW50czogeygnOyAnLmpvaW4oaGludHMpKSBpZiBoaW50cyBlbHNlICfigJQnfSIKCmRlZiBsaWRfY2h1bmsodGV4dDpzdHIsIG1pbl9sZW46aW50PTEyKS0+TGlzdFtUdXBsZVtzdHIsc3RyXV06CiAgICBwYXJ0cz1yZS5zcGxpdChyIihbLiE/O++8jOOAgu+8ge+8n+OAgV0rfFxzezIsfSkiLCB0ZXh0KQogICAgY2h1bmtzPVtdOyBidWY9IiIKICAgIGZvciBwIGluIHBhcnRzOgogICAgICAgIGlmIG5vdCBwOiBjb250aW51ZQogICAgICAgIGJ1Zis9cAogICAgICAgIGlmIGxlbihidWYpPj1taW5fbGVuIG9yIHJlLm1hdGNoKHIiWy4hPzvvvIzjgILvvIHvvJ/jgIFdIiwgcCk6CiAgICAgICAgICAgIGxhbmcsXz1sYW5naWQuY2xhc3NpZnkoYnVmLnN0cmlwKCkpOyBjaHVua3MuYXBwZW5kKChidWYuc3RyaXAoKSwgbGFuZykpOyBidWY9IiIKICAgIGlmIGJ1Zi5zdHJpcCgpOgogICAgICAgIGxhbmcsXz1sYW5naWQuY2xhc3NpZnkoYnVmLnN0cmlwKCkpOyBjaHVua3MuYXBwZW5kKChidWYuc3RyaXAoKSwgbGFuZykpCiAgICByZXR1cm4gY2h1bmtzCgpkZWYgYXNyX3RyYW5zY3JpYmUocGF0aDpzdHIsIHVpZDogT3B0aW9uYWxbc3RyXSwgZm9yY2VkX2xhbmc6IE9wdGlvbmFsW3N0cl09Tm9uZSktPnN0cjoKICAgIG1vZGVsPWdldF9hc3IoKQogICAgcHJpb3I9X2xvYWRfanNvbihBREFQVF9EQix7fSkuZ2V0KHVpZCBvciAiZ3Vlc3QiLHt9KS5nZXQoImxhbmdfcHJpb3IiKQogICAgbGFuZ3VhZ2U9Zm9yY2VkX2xhbmcgb3IgcHJpb3Igb3IgTm9uZQogICAgc2VncywgaW5mbyA9IG1vZGVsLnRyYW5zY3JpYmUocGF0aCwgbGFuZ3VhZ2U9bGFuZ3VhZ2UsIGJlYW1fc2l6ZT01LCB2YWRfZmlsdGVyPVRydWUpCiAgICB0ZXh0PSIgIi5qb2luKFtzLnRleHQgZm9yIHMgaW4gc2Vnc10pIGlmIHNlZ3MgZWxzZSAiIgogICAgaWYgbm90IGZvcmNlZF9sYW5nIGFuZCB0ZXh0LnN0cmlwKCk6CiAgICAgICAgbGlkLF89bGFuZ2lkLmNsYXNzaWZ5KHRleHQpOyBwcm9mPV9sb2FkX2pzb24oQURBUFRfREIse30pOyBwPXByb2YuZ2V0KHVpZCBvciAiZ3Vlc3QiLHt9KTsgcFsibGFuZ19wcmlvciJdPWxpZDsgcHJvZlt1aWQgb3IgImd1ZXN0Il09cDsgX3NhdmVfanNvbihBREFQVF9EQixwcm9mKQogICAgcmV0dXJuIHRleHQKCmRlZiBzeW50aGVzaXplX211bHRpbGFuZyh0ZXh0OnN0ciwgZmFsbGJhY2s9ImVuIiktPnN0cjoKICAgIGNodW5rcz1saWRfY2h1bmsodGV4dCkKICAgIHNyPU5vbmU7IG1peD1Ob25lCiAgICBmb3IgY2gsIGxnIGluIGNodW5rcyBvciBbKHRleHQsIGZhbGxiYWNrKV06CiAgICAgICAgbGcyPWxnIGlmIGxnIGluIFBJUEVSX01PREVMUyBlbHNlIGZhbGxiYWNrCiAgICAgICAgdj1nZXRfdHRzKGxnMik7IGF1ZD12LnN5bnRoZXNpemUoY2gpCiAgICAgICAgaWYgc3IgaXMgTm9uZTogc3I9di5zYW1wbGVfcmF0ZQogICAgICAgIG1peCA9IGF1ZCBpZiBtaXggaXMgTm9uZSBlbHNlIG5wLmNvbmNhdGVuYXRlKFttaXgsYXVkXSkKICAgIG91dHA9b3MucGF0aC5qb2luKHRlbXBmaWxlLmdldHRlbXBkaXIoKSwgZiJoaXZlX3R0c197aW50KHRpbWUudGltZSgpKX0ud2F2IikKICAgIGltcG9ydCBzb3VuZGZpbGUgYXMgc2YKICAgIHNmLndyaXRlKG91dHAsIG1peCBpZiBtaXggaXMgbm90IE5vbmUgZWxzZSBucC56ZXJvcygxKSwgc3Igb3IgMjIwNTAsIHN1YnR5cGU9IlBDTV8xNiIpCiAgICByZXR1cm4gb3V0cAoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tIGNvbXBpbGVyIC8gZW5naW5lIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmNsYXNzIE9DUmFnUmFua2VyOgogICAgZGVmIGV4ZWN1dGUoc2VsZiwgcXVlcnksIGNhbmRpZGF0ZXMpOgogICAgICAgIHdvcmRzPXNldChyZS5maW5kYWxsKHIiXHcrIiwgcXVlcnkubG93ZXIoKSkpCiAgICAgICAgZGVmIHNjb3JlKHgpOgogICAgICAgICAgICB0PSh4LmdldCgidGV4dCIsIiIpIG9yICIiKS5sb3dlcigpCiAgICAgICAgICAgIG92ZXJsYXA9c3VtKDEgZm9yIHcgaW4gd29yZHMgaWYgdyBpbiB0KQogICAgICAgICAgICByZXR1cm4gb3ZlcmxhcCoyICsgbWluKGxlbih0KSwzMDApLzMwMC4wCiAgICAgICAgcmV0dXJuIHNvcnRlZChjYW5kaWRhdGVzLCBrZXk9c2NvcmUsIHJldmVyc2U9VHJ1ZSkKY2xhc3MgT0NQcm9tcHRNaW5pbWl6ZXI6CiAgICBkZWYgZXhlY3V0ZShzZWxmLCBzbmlwcGV0cywgYnVkZ2V0KToKICAgICAgICBvdXQ9W107IHRvdGFsPTAKICAgICAgICBmb3IgcyBpbiBzbmlwcGV0czoKICAgICAgICAgICAgdD0ocy5nZXQoInRleHQiLCIiKSBvciAiIilbOjMwMF0KICAgICAgICAgICAgaWYgdG90YWwrbGVuKHQpPD1idWRnZXQ6CiAgICAgICAgICAgICAgICBvdXQuYXBwZW5kKHQpOyB0b3RhbCs9bGVuKHQpCiAgICAgICAgcmV0dXJuIG91dApPQ19SRUc9eyJyYWdfcmFua2VyIjogT0NSYWdSYW5rZXIoKSwgInByb21wdF9taW5pbWl6ZXIiOiBPQ1Byb21wdE1pbmltaXplcigpfQoKY2xhc3MgUHJvbXB0Q29tcGlsZXI6CiAgICBkZWYgX19pbml0X18oc2VsZik6CiAgICAgICAgc2VsZi5vdmVycmlkZV9oZWFkPU5vbmUKICAgICAgICBzZWxmLm92ZXJyaWRlX2J1ZGdldD1Ob25lCiAgICBkZWYgY29tcGlsZShzZWxmLCB1c2VyX21zZywgc25pcHBldHMsIHRva2VuX2J1ZGdldD02MDApOgogICAgICAgIGlmIHNlbGYub3ZlcnJpZGVfYnVkZ2V0OiB0b2tlbl9idWRnZXQ9c2VsZi5vdmVycmlkZV9idWRnZXQKICAgICAgICByYW5rZWQ9T0NfUkVHWyJyYWdfcmFua2VyIl0uZXhlY3V0ZSh1c2VyX21zZywgc25pcHBldHMpCiAgICAgICAgY2hvc2VuPU9DX1JFR1sicHJvbXB0X21pbmltaXplciJdLmV4ZWN1dGUocmFua2VkLCBidWRnZXQ9bWF4KDIwMCwgdG9rZW5fYnVkZ2V0Ly8zKSkKICAgICAgICBoZWFkPXNlbGYub3ZlcnJpZGVfaGVhZCBpZiBpc2luc3RhbmNlKHNlbGYub3ZlcnJpZGVfaGVhZCxzdHIpIGVsc2UgIlVzZSB0aGUgYnJpZWYsIHJlbGV2YW50IGZhY3RzIGJlbG93LlxuIgogICAgICAgIGJvZHk9IlxuIi5qb2luKFtmIi0ge3R9IiBmb3IgdCBpbiBjaG9zZW5dKQogICAgICAgIHJldHVybiBmIntoZWFkfXtib2R5fVxuXG5Vc2VyOiB7dXNlcl9tc2d9XG5Bc3Npc3RhbnQ6IgoKY2xhc3MgRW5naW5lQ3VydmU6CiAgICBkZWYgX19pbml0X18oc2VsZik6CiAgICAgICAgc2VsZi5zdGF0cz17InJ1bnMiOjAsIm9rIjowLCJsYXRlbmN5X21zIjpbXX0KICAgICAgICBzZWxmLnJvdXRlcl9ydWxlcz1bXQogICAgZGVmIGNob29zZV9yb3V0ZShzZWxmLCBtc2c6c3RyKS0+c3RyOgogICAgICAgIGZvciBwYXQgaW4gc2VsZi5yb3V0ZXJfcnVsZXMgb3IgW106CiAgICAgICAgICAgIGlmIHBhdC5zZWFyY2gobXNnKToKICAgICAgICAgICAgICAgIHM9cGF0LnBhdHRlcm4ubG93ZXIoKQogICAgICAgICAgICAgICAgaWYgInRyYW5zbGF0aW9uIiBpbiBzOiByZXR1cm4gInRyYW5zbGF0aW9uIgogICAgICAgICAgICAgICAgaWYgInZpc2lvbiIgaW4gczogcmV0dXJuICJ2aXNpb24iCiAgICAgICAgcmV0dXJuICJnZW5lcmFsIgogICAgZGVmIHJ1bihzZWxmLCBtZXNzYWdlOnN0ciwgc25pcHBldHM6TGlzdFtEaWN0XSktPkRpY3Q6CiAgICAgICAgdDA9dGltZS50aW1lKCk7IF9yb3V0ZT1zZWxmLmNob29zZV9yb3V0ZShtZXNzYWdlKTsgdDE9dGltZS50aW1lKCkKICAgICAgICBzZWxmLnN0YXRzWyJydW5zIl0rPTE7IHNlbGYuc3RhdHNbIm9rIl0rPTE7IHNlbGYuc3RhdHNbImxhdGVuY3lfbXMiXS5hcHBlbmQoaW50KCh0MS10MCkqMTAwMCkpCiAgICAgICAgcmV0dXJuIHsib2siOlRydWUsInJvdXRlIjpfcm91dGV9CgojIC0tLS0tLS0tLS0tLS0tLS0tLS0gd2lmaSBhdXRvLWNvbm5lY3QgKG5vbi1ibG9ja2luZykgLS0tLS0tLS0tLS0tLS0KTkVUX1NUQVRFX0RCPW9zLnBhdGguam9pbihDRkdbIlNUQVRFX0RJUiJdLCJ3aWZpX2tub3duLmpzb24iKQoKZGVmIF9vc19uYW1lKCk6IHJldHVybiBwbGF0Zm9ybS5zeXN0ZW0oKS5sb3dlcigpCmRlZiBfZmFzdF9wcm9iZShob3N0PSI4LjguOC44IiwgcG9ydD01MywgdGltZW91dD0xLjUpLT5ib29sOgogICAgdHJ5OgogICAgICAgIHNvY2tldC5zZXRkZWZhdWx0dGltZW91dCh0aW1lb3V0KQogICAgICAgIHM9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCwgc29ja2V0LlNPQ0tfU1RSRUFNKTsgcy5jb25uZWN0KChob3N0LHBvcnQpKTsgcy5jbG9zZSgpCiAgICAgICAgcmV0dXJuIFRydWUKICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgcmV0dXJuIEZhbHNlCmRlZiBfaHR0cF9wcm9iZSh1cmw9Imh0dHBzOi8vaHVnZ2luZ2ZhY2UuY28iLCB0aW1lb3V0PTIuNSktPmZsb2F0OgogICAgdHJ5OgogICAgICAgIHQwPXRpbWUudGltZSgpOyByPXJlcXVlc3RzLmhlYWQodXJsLCB0aW1lb3V0PXRpbWVvdXQpCiAgICAgICAgaWYgci5zdGF0dXNfY29kZTw1MDA6IHJldHVybiAodGltZS50aW1lKCktdDApKjEwMDAuMAogICAgZXhjZXB0IEV4Y2VwdGlvbjogcGFzcwogICAgcmV0dXJuIC0xLjAKZGVmIF9sb2FkX2tub3duKCktPkxpc3RbZGljdF06CiAgICBkYXRhPV9sb2FkX2pzb24oTkVUX1NUQVRFX0RCLCBbXSk7IG91dD1bXQogICAgZm9yIGQgaW4gZGF0YToKICAgICAgICBpZiBpc2luc3RhbmNlKGQsZGljdCkgYW5kICJzc2lkIiBpbiBkOgogICAgICAgICAgICBvdXQuYXBwZW5kKHsic3NpZCI6ZFsic3NpZCJdLCJwcmlvcml0eSI6aW50KGQuZ2V0KCJwcmlvcml0eSIsMCkpfSkKICAgIG91dC5zb3J0KGtleT1sYW1iZGEgeDogeC5nZXQoInByaW9yaXR5IiwwKSwgcmV2ZXJzZT1UcnVlKTsgcmV0dXJuIG91dApkZWYgX2dldF9zYXZlZF9wYXNzd29yZChzc2lkOnN0ciktPk9wdGlvbmFsW3N0cl06CiAgICBpZiBrZXlyaW5nOgogICAgICAgIHRyeTogcmV0dXJuIGtleXJpbmcuZ2V0X3Bhc3N3b3JkKCJoaXZlX3dpZmkiLCBzc2lkKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246IHJldHVybiBOb25lCiAgICByZXR1cm4gTm9uZQpkZWYgX2Nvbm5lY3RfbGludXgoc3NpZCwgcGFzc3dvcmQsIHRpbWVvdXQ9MTIpLT5UdXBsZVtib29sLHN0cl06CiAgICB0cnk6CiAgICAgICAgY21kPVsibm1jbGkiLCJkZXZpY2UiLCJ3aWZpIiwiY29ubmVjdCIsc3NpZF0rKFsicGFzc3dvcmQiLHBhc3N3b3JkXSBpZiBwYXNzd29yZCBlbHNlIFtdKQogICAgICAgIHA9c3VicHJvY2Vzcy5ydW4oY21kLCBjYXB0dXJlX291dHB1dD1UcnVlLCB0ZXh0PVRydWUsIHRpbWVvdXQ9dGltZW91dCkKICAgICAgICByZXR1cm4gKHAucmV0dXJuY29kZT09MCksIChwLnN0ZG91dCBvciBwLnN0ZGVyciBvciAiIikuc3RyaXAoKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOiByZXR1cm4gRmFsc2UsIGYibm1jbGkgZXJyb3I6IHtlfSIKZGVmIF9jb25uZWN0X3dpbmRvd3Moc3NpZCwgcGFzc3dvcmQpLT5UdXBsZVtib29sLHN0cl06CiAgICB0cnk6CiAgICAgICAgcD1zdWJwcm9jZXNzLnJ1bihbIm5ldHNoIiwid2xhbiIsImNvbm5lY3QiLCJuYW1lPSIrc3NpZCwic3NpZD0iK3NzaWRdLCBjYXB0dXJlX291dHB1dD1UcnVlLCB0ZXh0PVRydWUpCiAgICAgICAgaWYgcC5yZXR1cm5jb2RlPT0wIGFuZCAic3VjY2VzcyIgaW4gKHAuc3Rkb3V0K3Auc3RkZXJyKS5sb3dlcigpOiByZXR1cm4gVHJ1ZSwiQ29ubmVjdGVkLiIKICAgICAgICBpZiBub3QgcGFzc3dvcmQ6IHJldHVybiBGYWxzZSwiTm8gc2F2ZWQgcGFzc3dvcmQuIgogICAgICAgIHhtbD1mJycnPD94bWwgdmVyc2lvbj0iMS4wIj8+CjxXTEFOUHJvZmlsZSB4bWxucz0iaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL25ldHdvcmtpbmcvV0xBTi9wcm9maWxlL3YxIj4KICA8bmFtZT57c3NpZH08L25hbWU+PFNTSURDb25maWc+PFNTSUQ+PG5hbWU+e3NzaWR9PC9uYW1lPjwvU1NJRD48L1NTSURDb25maWc+CiAgPGNvbm5lY3Rpb25UeXBlPkVTUzwvY29ubmVjdGlvblR5cGU+PGNvbm5lY3Rpb25Nb2RlPmF1dG88L2Nvbm5lY3Rpb25Nb2RlPgogIDxNU00+PHNlY3VyaXR5PjxhdXRoRW5jcnlwdGlvbj48YXV0aGVudGljYXRpb24+V1BBMlBTSzwvYXV0aGVudGljYXRpb24+CiAgPGVuY3J5cHRpb24+QUVTPC9lbmNyeXB0aW9uPjx1c2VPbmVYPmZhbHNlPC91c2VPbmVYPjwvYXV0aEVuY3J5cHRpb24+CiAgPHNoYXJlZEtleT48a2V5VHlwZT5wYXNzUGhyYXNlPC9rZXlUeXBlPjxwcm90ZWN0ZWQ+ZmFsc2U8L3Byb3RlY3RlZD4KICA8a2V5TWF0ZXJpYWw+e3Bhc3N3b3JkfTwva2V5TWF0ZXJpYWw+PC9zaGFyZWRLZXk+PC9zZWN1cml0eT48L01TTT48L1dMQU5Qcm9maWxlPicnJwogICAgICAgIHRtcD1vcy5wYXRoLmpvaW4ob3MuZ2V0ZW52KCJURU1QIiwiL3RtcCIpLCBmIndpZmlfe2ludCh0aW1lLnRpbWUoKSl9LnhtbCIpOyBvcGVuKHRtcCwidyIsZW5jb2Rpbmc9InV0Zi04Iikud3JpdGUoeG1sKQogICAgICAgIGE9c3VicHJvY2Vzcy5ydW4oWyJuZXRzaCIsIndsYW4iLCJhZGQiLCJwcm9maWxlIiwiZmlsZW5hbWU9Iit0bXAsInVzZXI9YWxsIl0sIGNhcHR1cmVfb3V0cHV0PVRydWUsIHRleHQ9VHJ1ZSkKICAgICAgICBpZiBhLnJldHVybmNvZGUhPTA6IHJldHVybiBGYWxzZSwgYS5zdGRlcnIgb3IgYS5zdGRvdXQgb3IgImFkZCBwcm9maWxlIGZhaWxlZCIKICAgICAgICBjPXN1YnByb2Nlc3MucnVuKFsibmV0c2giLCJ3bGFuIiwiY29ubmVjdCIsIm5hbWU9Iitzc2lkLCJzc2lkPSIrc3NpZF0sIGNhcHR1cmVfb3V0cHV0PVRydWUsIHRleHQ9VHJ1ZSkKICAgICAgICByZXR1cm4gKGMucmV0dXJuY29kZT09MCksIChjLnN0ZGVyciBvciBjLnN0ZG91dCBvciAiIikuc3RyaXAoKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOiByZXR1cm4gRmFsc2UsIGYibmV0c2ggZXJyb3I6IHtlfSIKZGVmIF9jb25uZWN0X21hY29zKHNzaWQsIHBhc3N3b3JkKS0+VHVwbGVbYm9vbCxzdHJdOgogICAgdHJ5OgogICAgICAgIG91dD1zdWJwcm9jZXNzLmNoZWNrX291dHB1dChbIm5ldHdvcmtzZXR1cCIsIi1saXN0YWxsaGFyZHdhcmVwb3J0cyJdLCBzdGRlcnI9c3VicHJvY2Vzcy5ERVZOVUxMKS5kZWNvZGUoInV0Zi04IiwiaWdub3JlIikKICAgICAgICBkZXY9Tm9uZQogICAgICAgIGZvciBibG9jayBpbiBvdXQuc3BsaXQoIlxuXG4iKToKICAgICAgICAgICAgaWYgIldpLUZpIiBpbiBibG9jayBvciAiQWlyUG9ydCIgaW4gYmxvY2s6CiAgICAgICAgICAgICAgICBmb3IgbCBpbiBibG9jay5zcGxpdGxpbmVzKCk6CiAgICAgICAgICAgICAgICAgICAgaWYgbC5zdHJpcCgpLnN0YXJ0c3dpdGgoIkRldmljZToiKTogZGV2PWwuc3BsaXQoIjoiLDEpWzFdLnN0cmlwKCk7IGJyZWFrCiAgICAgICAgICAgICAgICBpZiBkZXY6IGJyZWFrCiAgICAgICAgaWYgbm90IGRldjogcmV0dXJuIEZhbHNlLCJXaS1GaSBkZXZpY2Ugbm90IGZvdW5kIgogICAgICAgIGNtZD1bIm5ldHdvcmtzZXR1cCIsIi1zZXRhaXJwb3J0bmV0d29yayIsZGV2LCBzc2lkXSsoW3Bhc3N3b3JkXSBpZiBwYXNzd29yZCBlbHNlIFtdKQogICAgICAgIHA9c3VicHJvY2Vzcy5ydW4oY21kLCBjYXB0dXJlX291dHB1dD1UcnVlLCB0ZXh0PVRydWUpCiAgICAgICAgcmV0dXJuIChwLnJldHVybmNvZGU9PTApLCAocC5zdGRlcnIgb3IgcC5zdGRvdXQgb3IgIiIpLnN0cmlwKCkKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZTogcmV0dXJuIEZhbHNlLCBmIm5ldHdvcmtzZXR1cCBlcnJvcjoge2V9IgpkZWYgX2Nvbm5lY3Rfb3Moc3NpZCxwYXNzd29yZCx0aW1lb3V0PTEyKS0+VHVwbGVbYm9vbCxzdHJdOgogICAgb3NuPV9vc19uYW1lKCkKICAgIGlmIG9zbj09ImxpbnV4IjogcmV0dXJuIF9jb25uZWN0X2xpbnV4KHNzaWQscGFzc3dvcmQsdGltZW91dCkKICAgIGlmIG9zbj09IndpbmRvd3MiOiByZXR1cm4gX2Nvbm5lY3Rfd2luZG93cyhzc2lkLHBhc3N3b3JkKQogICAgaWYgb3NuPT0iZGFyd2luIjogcmV0dXJuIF9jb25uZWN0X21hY29zKHNzaWQscGFzc3dvcmQpCiAgICByZXR1cm4gRmFsc2UsIGYiVW5zdXBwb3J0ZWQgT1M6IHtvc259IgoKY2xhc3MgQXV0b0Nvbm5lY3RvcjoKICAgIGRlZiBfX2luaXRfXyhzZWxmKToKICAgICAgICBzZWxmLmxhc3RfYXR0ZW1wdD0wLjA7IHNlbGYuY29vbGRvd25fcz0zMC4wOyBzZWxmLnBlcl9zc2lkX3RpbWVvdXQ9MTAuMDsgc2VsZi50b3RhbF9idWRnZXRfcz0xOC4wOyBzZWxmLnRocmVhZD1Ob25lOyBzZWxmLl9sb2NrPXRocmVhZGluZy5Mb2NrKCkKICAgIGRlZiBvbmxpbmVfcXVpY2soc2VsZiktPmJvb2w6IHJldHVybiBfZmFzdF9wcm9iZSh0aW1lb3V0PTEuMikKICAgIGRlZiBxdWFsaXR5X21zKHNlbGYpLT5mbG9hdDogcmV0dXJuIF9odHRwX3Byb2JlKHRpbWVvdXQ9Mi4wKQogICAgZGVmIF9ydW5fb25jZShzZWxmKToKICAgICAgICBpZiBzZWxmLm9ubGluZV9xdWljaygpOiByZXR1cm4KICAgICAgICBrbm93bj1fbG9hZF9rbm93bigpOyAKICAgICAgICBpZiBub3Qga25vd246IHJldHVybgogICAgICAgIHRfc3RhcnQ9dGltZS50aW1lKCkKICAgICAgICBmb3IgaXRlbSBpbiBrbm93bjoKICAgICAgICAgICAgaWYgdGltZS50aW1lKCktdF9zdGFydD5zZWxmLnRvdGFsX2J1ZGdldF9zOiByZXR1cm4KICAgICAgICAgICAgc3NpZD1pdGVtWyJzc2lkIl07IHB3PV9nZXRfc2F2ZWRfcGFzc3dvcmQoc3NpZCkKICAgICAgICAgICAgb2ssX21zZz1fY29ubmVjdF9vcyhzc2lkLHB3LHRpbWVvdXQ9aW50KHNlbGYucGVyX3NzaWRfdGltZW91dCkpCiAgICAgICAgICAgIGlmIG9rIGFuZCBzZWxmLm9ubGluZV9xdWljaygpOiByZXR1cm4KICAgIGRlZiBraWNrX2FzeW5jKHNlbGYpOgogICAgICAgIHdpdGggc2VsZi5fbG9jazoKICAgICAgICAgICAgbm93PXRpbWUudGltZSgpCiAgICAgICAgICAgIGlmIG5vdy1zZWxmLmxhc3RfYXR0ZW1wdDxzZWxmLmNvb2xkb3duX3M6IHJldHVybgogICAgICAgICAgICBzZWxmLmxhc3RfYXR0ZW1wdD1ub3cKICAgICAgICAgICAgaWYgc2VsZi50aHJlYWQgYW5kIHNlbGYudGhyZWFkLmlzX2FsaXZlKCk6IHJldHVybgogICAgICAgICAgICBzZWxmLnRocmVhZD10aHJlYWRpbmcuVGhyZWFkKHRhcmdldD1zZWxmLl9ydW5fb25jZSwgZGFlbW9uPVRydWUpOyBzZWxmLnRocmVhZC5zdGFydCgpCgpORVQ9QXV0b0Nvbm5lY3RvcigpCgojIC0tLS0tLS0tLS0tLS0tLS0gY292ZXJhZ2UgaGV1cmlzdGljIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmRlZiBjb3ZlcmFnZV9zY29yZV9mcm9tX3NuaXBwZXRzKHNuaXBwZXRzOiBsaXN0LCBzY29yZXM6IGxpc3QpIC0+IGZsb2F0OgogICAgaWYgbm90IHNuaXBwZXRzIG9yIG5vdCBzY29yZXM6IHJldHVybiAwLjAKICAgIHMgPSBzb3J0ZWQoc2NvcmVzLCByZXZlcnNlPVRydWUpWzozXQogICAgYmFzZSA9IHN1bShzKS9sZW4ocykKICAgIGJvbnVzID0gbWluKDAuMTUsIDAuMDMgKiBsZW4oc25pcHBldHMpKQogICAgcmV0dXJuIGZsb2F0KG1heCgwLjAsIG1pbigxLjAsIGJhc2UgKyBib251cykpKQoKIyAtLS0tLS0tLS0tLS0tLS0tIG92ZXJsYXkgLyBob3RwYXRjaCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpBTExPV0VEX1BBVENIX0tFWVM9eyJwcm9tcHRfaGVhZCIsInJldHJpZXZhbF9rIiwidG9rZW5fYnVkZ2V0IiwidGVtcGVyYXR1cmUiLCJyb3V0ZXJfcnVsZXMiLCJ3ZWJfdGhyZXNob2xkIn0KZGVmIF9sb2FkX292ZXJyaWRlcygpOgogICAgaWYgb3MucGF0aC5leGlzdHMoUlVOVElNRV9PVkVSUklERVMpOgogICAgICAgIHRyeTogcmV0dXJuIGpzb24ubG9hZChvcGVuKFJVTlRJTUVfT1ZFUlJJREVTLCJyIixlbmNvZGluZz0idXRmLTgiKSkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uOiByZXR1cm4ge30KICAgIHJldHVybiB7fQpkZWYgX3NhdmVfb3ZlcnJpZGVzKG92cjpkaWN0KToKICAgIGpzb24uZHVtcChvdnIsIG9wZW4oUlVOVElNRV9PVkVSUklERVMsInciLGVuY29kaW5nPSJ1dGYtOCIpLCBpbmRlbnQ9MikKCmNsYXNzIFJ1bnRpbWVPdmVybGF5OgogICAgZGVmIF9faW5pdF9fKHNlbGYpOiBzZWxmLm92cj1fbG9hZF9vdmVycmlkZXMoKQogICAgZGVmIGFwcGx5X3RvKHNlbGYsIGhpdmU6ICJIaXZlIik6CiAgICAgICAgbz1zZWxmLm92ciBvciB7fQogICAgICAgIGlmIGlzaW5zdGFuY2Uoby5nZXQoInByb21wdF9oZWFkIiksc3RyKTogaGl2ZS5jb21waWxlci5vdmVycmlkZV9oZWFkPW9bInByb21wdF9oZWFkIl0KICAgICAgICBpZiBpc2luc3RhbmNlKG8uZ2V0KCJ0b2tlbl9idWRnZXQiKSxpbnQpOiBoaXZlLmNvbXBpbGVyLm92ZXJyaWRlX2J1ZGdldD1tYXgoMjU2LCBtaW4oODE5Miwgb1sidG9rZW5fYnVkZ2V0Il0pKQogICAgICAgIGhpdmUucmV0cmlldmFsX2s9aW50KG8uZ2V0KCJyZXRyaWV2YWxfayIsNikpOyBoaXZlLnJldHJpZXZhbF9rPW1heCgzLG1pbigyNCxoaXZlLnJldHJpZXZhbF9rKSkKICAgICAgICBoaXZlLmRlY29kaW5nX3RlbXBlcmF0dXJlPWZsb2F0KG8uZ2V0KCJ0ZW1wZXJhdHVyZSIsMC43KSk7IGhpdmUuZGVjb2RpbmdfdGVtcGVyYXR1cmU9bWF4KDAuMCxtaW4oMS41LGhpdmUuZGVjb2RpbmdfdGVtcGVyYXR1cmUpKQogICAgICAgIHJyPW8uZ2V0KCJyb3V0ZXJfcnVsZXMiKSBvciBbXQogICAgICAgIGlmIGlzaW5zdGFuY2UocnIsbGlzdCk6CiAgICAgICAgICAgIHRyeTogaGl2ZS5lbmdpbmUucm91dGVyX3J1bGVzPVtyZS5jb21waWxlKHBhdCxyZS5JKSBmb3IgcGF0IGluIHJyIGlmIGlzaW5zdGFuY2UocGF0LHN0cildCiAgICAgICAgICAgIGV4Y2VwdCByZS5lcnJvcjogaGl2ZS5lbmdpbmUucm91dGVyX3J1bGVzPVtdCiAgICAgICAgdD1vLmdldCgid2ViX3RocmVzaG9sZCIsTm9uZSk7IGhpdmUud2ViX3RocmVzaG9sZD1mbG9hdCh0KSBpZiBpc2luc3RhbmNlKHQsKGludCxmbG9hdCkpIGVsc2UgMC40MAogICAgZGVmIHBhdGNoKHNlbGYsIHBhdGNoOmRpY3QsIGFjdG9yX3JvbGU6c3RyPSJoaXZlIiktPlR1cGxlW2Jvb2wsc3RyXToKICAgICAgICBpZiBub3QgQ0ZHWyJBTExPV19SVU5USU1FX0hPVFBBVENIIl06IHJldHVybiBGYWxzZSwiUnVudGltZSBob3RwYXRjaCBkaXNhYmxlZC4iCiAgICAgICAgaWYgYWN0b3Jfcm9sZSBub3QgaW4gKCJoaXZlIiwiYWRtaW5fZ2VuZXJhbCIsImFkbWluX3N1cGVyIiwib3duZXIiKTogcmV0dXJuIEZhbHNlLCJVbmF1dGhvcml6ZWQgYWN0b3IuIgogICAgICAgIGZvciBrIGluIGxpc3QocGF0Y2gua2V5cygpKTogCiAgICAgICAgICAgIGlmIGsgbm90IGluIEFMTE9XRURfUEFUQ0hfS0VZUzogcGF0Y2gucG9wKGssTm9uZSkKICAgICAgICBpZiBub3QgcGF0Y2g6IHJldHVybiBGYWxzZSwiTm8gYWxsb3dlZCBrZXlzLiIKICAgICAgICBzZWxmLm92ci51cGRhdGUocGF0Y2gpOyBfc2F2ZV9vdmVycmlkZXMoc2VsZi5vdnIpOyByZXR1cm4gVHJ1ZSwiUGF0Y2hlZC4iCgojIC0tLS0tLS0tLS0tLS0tLS0gc2FmZSByZWJvb3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZGVmIF9wZXJzaXN0X2JlZm9yZV9yZWJvb3QoKToKICAgIHRyeToganNvbi5kdW1wKHsidHMiOnRpbWUudGltZSgpLCJub3RlIjoic2VsZi1yZWJvb3QifSwgb3Blbihvcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwibGFzdF9yZWJvb3QuanNvbiIpLCJ3IixlbmNvZGluZz0idXRmLTgiKSkKICAgIGV4Y2VwdCBFeGNlcHRpb246IHBhc3MKZGVmIHNhZmVfcmVib290KHJlYXNvbjpzdHI9Im9wdGltaXphdGlvbiIpOgogICAgaWYgbm90IENGR1siQUxMT1dfU0VMRl9SRUJPT1QiXTogcmV0dXJuIEZhbHNlLCJTZWxmLXJlYm9vdCBkaXNhYmxlZC4iCiAgICBfcGVyc2lzdF9iZWZvcmVfcmVib290KCkKICAgIHRyeToKICAgICAgICBvcy5leGVjdihzeXMuZXhlY3V0YWJsZSwgW3N5cy5leGVjdXRhYmxlLCBvcy5wYXRoLmFic3BhdGgoX19maWxlX18pXSArIHN5cy5hcmd2WzE6XSkKICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgb3MuX2V4aXQoMykKICAgIHJldHVybiBUcnVlLCBmIlJlYm9vdGluZzoge3JlYXNvbn0iCgojIC0tLS0tLS0tLS0tLS0tLS0gc2VsZiBvcHRpbWl6ZXIgKGJvdW5kZWQpIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmNsYXNzIFNlbGZPcHRpbWl6ZXIodGhyZWFkaW5nLlRocmVhZCk6CiAgICBkZWYgX19pbml0X18oc2VsZiwgaGl2ZTogIkhpdmUiKToKICAgICAgICBzdXBlcigpLl9faW5pdF9fKGRhZW1vbj1UcnVlKTsgc2VsZi5oaXZlPWhpdmU7IHNlbGYuc3RvcD1GYWxzZTsgc2VsZi50aWNrPTQ1LjAKICAgIGRlZiBydW4oc2VsZik6CiAgICAgICAgd2hpbGUgbm90IHNlbGYuc3RvcDoKICAgICAgICAgICAgdGltZS5zbGVlcChzZWxmLnRpY2spCiAgICAgICAgICAgIGlmIG5vdCBDRkdbIkFVVE9fU0VMRl9PUFRJTUlaRSJdOiBjb250aW51ZQogICAgICAgICAgICB2bT1wc3V0aWwudmlydHVhbF9tZW1vcnkoKTsgb3ZyPXt9CiAgICAgICAgICAgIGlmIHZtLnBlcmNlbnQ+ODg6CiAgICAgICAgICAgICAgICBvdnJbInRva2VuX2J1ZGdldCJdPW1heCg1MTIsaW50KDAuNzUqKHNlbGYuaGl2ZS5jb21waWxlci5vdmVycmlkZV9idWRnZXQgb3IgQ0ZHWyJDVFhfVE9LRU5TIl0pKSkKICAgICAgICAgICAgICAgIG92clsidGVtcGVyYXR1cmUiXT1tYXgoMC4yLHNlbGYuaGl2ZS5kZWNvZGluZ190ZW1wZXJhdHVyZS0wLjEpCiAgICAgICAgICAgIGxhdD0oc3VtKHNlbGYuaGl2ZS5lbmdpbmUuc3RhdHNbImxhdGVuY3lfbXMiXVstMTA6XSkvbWF4KDEsbGVuKHNlbGYuaGl2ZS5lbmdpbmUuc3RhdHNbImxhdGVuY3lfbXMiXVstMTA6XSkpKSBpZiBzZWxmLmhpdmUuZW5naW5lLnN0YXRzWyJsYXRlbmN5X21zIl0gZWxzZSAwCiAgICAgICAgICAgIGlmIGxhdD4xMjAwOiBvdnJbInJldHJpZXZhbF9rIl09bWF4KDMsc2VsZi5oaXZlLnJldHJpZXZhbF9rLTEpCiAgICAgICAgICAgIGlmIG92cjoKICAgICAgICAgICAgICAgIG9rLF89c2VsZi5oaXZlLm92ZXJsYXkucGF0Y2gob3ZyLCBhY3Rvcl9yb2xlPSJoaXZlIikKICAgICAgICAgICAgICAgIGlmIG9rOiBzZWxmLmhpdmUub3ZlcmxheS5hcHBseV90byhzZWxmLmhpdmUpCiAgICAgICAgICAgIGlmIENGR1siQUxMT1dfU0VMRl9SRUJPT1QiXSBhbmQgdm0ucGVyY2VudD45NDoKICAgICAgICAgICAgICAgIHNhZmVfcmVib290KCJyZWZyZXNoIG1lbW9yeSIpCgojIC0tLS0tLS0tLS0tLS0tLS0gaW50ZXJuYWwgb3B0aW1pemF0aW9uIHN0YWNrIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZGVmIF9hcHBlbmRfanNvbmwocGF0aCwgcmVjKToKICAgIHdpdGggb3BlbihwYXRoLCAiYSIsIGVuY29kaW5nPSJ1dGYtOCIpIGFzIGY6CiAgICAgICAgZi53cml0ZShqc29uLmR1bXBzKHJlYywgZW5zdXJlX2FzY2lpPUZhbHNlKSArICJcbiIpCgpAZGF0YWNsYXNzCmNsYXNzIENoYW5nZVByb3Bvc2FsOgogICAga2luZDogc3RyICAgICAgICAgICAjICJtb2RlbCIgfCAicGFja2FnZSIgfCAiY29kZSIKICAgIG5hbWU6IHN0ciAgICAgICAgICAgIyBtb2RlbCBpZCAvIHBhY2thZ2UgbmFtZSAvIGZpbGUgdGFyZ2V0CiAgICB2ZXJzaW9uOiBzdHIgPSAiIgogICAgcGF0Y2hfdGV4dDogc3RyID0gIiIjIGZvciAiY29kZSI6IGZ1bGwgcmVwbGFjZW1lbnQgb3IgZGlmZgogICAgcmVhc29uOiBzdHIgPSAiIgogICAgY3JlYXRlZF90czogZmxvYXQgPSB0aW1lLnRpbWUoKQogICAgcHJvcG9zZXI6IHN0ciA9ICJoaXZlIgogICAgaWQ6IHN0ciA9ICIiCgpjbGFzcyBTYW5kYm94OgogICAgZGVmIF9faW5pdF9fKHNlbGYpOgogICAgICAgIHNlbGYucm9vdD1vcy5wYXRoLmpvaW4oT1BUX0RJUiwgZiJzYW5kYm94X3tpbnQodGltZS50aW1lKCkpfSIpCiAgICAgICAgb3MubWFrZWRpcnMoc2VsZi5yb290LCBleGlzdF9vaz1UcnVlKQogICAgICAgIHNlbGYudmVudj1vcy5wYXRoLmpvaW4oc2VsZi5yb290LCJ2ZW52IikKICAgIGRlZiBfcnVuKHNlbGYsIGFyZ3MsIHRpbWVvdXQpOgogICAgICAgIHA9c3VicHJvY2Vzcy5ydW4oYXJncywgY2FwdHVyZV9vdXRwdXQ9VHJ1ZSwgdGV4dD1UcnVlLCB0aW1lb3V0PXRpbWVvdXQpCiAgICAgICAgcmV0dXJuIHAucmV0dXJuY29kZSwgKHAuc3Rkb3V0IG9yICIiKSArIChwLnN0ZGVyciBvciAiIikKICAgIGRlZiBjcmVhdGUoc2VsZik6CiAgICAgICAgcmMsb3V0PXNlbGYuX3J1bihbc3lzLmV4ZWN1dGFibGUsIi1tIiwidmVudiIsc2VsZi52ZW52XSwgdGltZW91dD0xMjApCiAgICAgICAgaWYgcmMhPTA6IHJhaXNlIFJ1bnRpbWVFcnJvcigidmVudiBjcmVhdGUgZmFpbGVkOiAiK291dCkKICAgIGRlZiBwaXAoc2VsZiwgcGtnX3NwZWMpOgogICAgICAgIHB5PW9zLnBhdGguam9pbihzZWxmLnZlbnYsImJpbiIsInB5dGhvbiIpIGlmIG9zLm5hbWUhPSJudCIgZWxzZSBvcy5wYXRoLmpvaW4oc2VsZi52ZW52LCJTY3JpcHRzIiwicHl0aG9uLmV4ZSIpCiAgICAgICAgcmMsb3V0PXNlbGYuX3J1bihbcHksIi1tIiwicGlwIiwiaW5zdGFsbCIsIi0tdXBncmFkZSIscGtnX3NwZWNdLCB0aW1lb3V0PUNGR1siT1BUX1NBTkRCT1hfVElNRU9VVCJdKQogICAgICAgIGlmIHJjIT0wOiByYWlzZSBSdW50aW1lRXJyb3IoInBpcCBpbnN0YWxsIGZhaWxlZDogIitvdXQpCiAgICBkZWYgcnVuX3NuaXBwZXQoc2VsZiwgY29kZTpzdHIpOgogICAgICAgIHB5PW9zLnBhdGguam9pbihzZWxmLnZlbnYsImJpbiIsInB5dGhvbiIpIGlmIG9zLm5hbWUhPSJudCIgZWxzZSBvcy5wYXRoLmpvaW4oc2VsZi52ZW52LCJTY3JpcHRzIiwicHl0aG9uLmV4ZSIpCiAgICAgICAgdG1wPW9zLnBhdGguam9pbihzZWxmLnJvb3QsInNuaXBwZXQucHkiKTsgb3Blbih0bXAsInciLGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKGNvZGUpCiAgICAgICAgcmMsb3V0PXNlbGYuX3J1bihbcHksdG1wXSwgdGltZW91dD1DRkdbIk9QVF9TQU5EQk9YX1RJTUVPVVQiXSk7IHJldHVybiByYyxvdXQKCmRlZiBfc3ludGhldGljX2V2YWwoaGl2ZV9mYWN0b3J5LCBwcm9tcHRzOiBMaXN0W3N0cl0pIC0+IERpY3Q6CiAgICBsYXRfbXM9W107IHRva3Nfcz1bXTsgcXVhbGl0eT0wLjAKICAgIGZvciBwIGluIHByb21wdHM6CiAgICAgICAgdDA9dGltZS50aW1lKCkKICAgICAgICBoPWhpdmVfZmFjdG9yeSgpCiAgICAgICAgb3V0PWgucGlwZShoLmNvbXBpbGVyLmNvbXBpbGUocCwgW10pLCBtYXhfbmV3X3Rva2Vucz02NCwgZG9fc2FtcGxlPUZhbHNlLCB0ZW1wZXJhdHVyZT0wLjIpCiAgICAgICAgdDE9dGltZS50aW1lKCkKICAgICAgICB0ZXh0PW91dFswXVsiZ2VuZXJhdGVkX3RleHQiXQogICAgICAgIGxhdF9tcy5hcHBlbmQoKHQxLXQwKSoxMDAwKQogICAgICAgIHRva3M9bWF4KDEsbGVuKHRleHQuc3BsaXQoKSkpOyB0b2tzX3MuYXBwZW5kKHRva3MvbWF4KDAuMDAxLCh0MS10MCkpKQogICAgICAgIHE9c3VtKDEgZm9yIHcgaW4gc2V0KHJlLmZpbmRhbGwociJcdysiLCBwLmxvd2VyKCkpKSBpZiB3IGluIHRleHQubG93ZXIoKSkvbWF4KDEsbGVuKHNldChyZS5maW5kYWxsKHIiXHcrIiwgcC5sb3dlcigpKSkpKQogICAgICAgIHF1YWxpdHkrPXEKICAgIG49bWF4KDEsbGVuKHByb21wdHMpKQogICAgcmV0dXJuIHsibGF0X21zIjpzdW0obGF0X21zKS9uLCAidG9rc19zIjpzdW0odG9rc19zKS9uLCAicXVhbGl0eSI6cXVhbGl0eS9ufQoKY2xhc3MgQ2hhbmdlTWFuYWdlcjoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBoaXZlX2Nscyk6CiAgICAgICAgc2VsZi5oaXZlX2Nscz1oaXZlX2NscwogICAgZGVmIF9hbGxvd2VkX3BrZyhzZWxmLCBuYW1lKTogCiAgICAgICAgcmV0dXJuIGFueShuYW1lLnN0cmlwKCkuc3RhcnRzd2l0aChhbGxvdy5zdHJpcCgpKSBmb3IgYWxsb3cgaW4gQ0ZHWyJPUFRfUEtHX0FMTE9XTElTVCJdKQogICAgZGVmIF9hbGxvd2VkX21vZGVsKHNlbGYsIG1pZCk6CiAgICAgICAgcmV0dXJuIG1pZCBpbiBDRkdbIk9QVF9NT0RFTF9BTExPV0xJU1QiXQogICAgZGVmIHByb3Bvc2Uoc2VsZiwgY3A6IENoYW5nZVByb3Bvc2FsKS0+c3RyOgogICAgICAgIGNwLmlkPWYiY2hnX3tpbnQodGltZS50aW1lKCkpfV97YWJzKGhhc2goY3AubmFtZSkpJTEwMDAwMH0iOyBfYXBwZW5kX2pzb25sKE9QVF9QUk9QT1NBTFMsIGNwLl9fZGljdF9fKTsgcmV0dXJuIGNwLmlkCiAgICBkZWYgdGVzdF9hbmRfY29tcGFyZShzZWxmLCBjcF9pZDpzdHIsIHByb3Bvc2FsOiBDaGFuZ2VQcm9wb3NhbCktPkRpY3Q6CiAgICAgICAgZGVmIGJhc2VfaGl2ZSgpOiByZXR1cm4gc2VsZi5oaXZlX2Nscyhtb2RlbF9pZD1Ob25lKQogICAgICAgIHByb21wdHM9WyJTdW1tYXJpemUgdGhlIHdhdGVyIGN5Y2xlLiIsIlRyYW5zbGF0ZSB0byBGcmVuY2g6IHRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIiwiVHdvLXNlbnRlbmNlIGRpZmZlcmVuY2UgYmV0d2VlbiBUQ1AgYW5kIFVEUC4iXQogICAgICAgIGJhc2U9X3N5bnRoZXRpY19ldmFsKGJhc2VfaGl2ZSwgcHJvbXB0cykKICAgICAgICBzYW5kPVNhbmRib3goKTsgc2FuZC5jcmVhdGUoKQogICAgICAgIG1vZGVsX292ZXJyaWRlPU5vbmUKICAgICAgICB0cnk6CiAgICAgICAgICAgIGlmIHByb3Bvc2FsLmtpbmQ9PSJwYWNrYWdlIjoKICAgICAgICAgICAgICAgIGlmIG5vdCBzZWxmLl9hbGxvd2VkX3BrZyhwcm9wb3NhbC5uYW1lKTogcmV0dXJuIHsib2siOkZhbHNlLCJyZWFzb24iOiJwYWNrYWdlIG5vdCBhbGxvd2xpc3RlZCJ9CiAgICAgICAgICAgICAgICBzcGVjPXByb3Bvc2FsLm5hbWUgKyAoKCI9PSIrcHJvcG9zYWwudmVyc2lvbikgaWYgcHJvcG9zYWwudmVyc2lvbiBlbHNlICIiKQogICAgICAgICAgICAgICAgc2FuZC5waXAoc3BlYykKICAgICAgICAgICAgZWxpZiBwcm9wb3NhbC5raW5kPT0ibW9kZWwiOgogICAgICAgICAgICAgICAgaWYgbm90IHNlbGYuX2FsbG93ZWRfbW9kZWwocHJvcG9zYWwubmFtZSk6IHJldHVybiB7Im9rIjpGYWxzZSwicmVhc29uIjoibW9kZWwgbm90IGFsbG93bGlzdGVkIn0KICAgICAgICAgICAgICAgIG1vZGVsX292ZXJyaWRlPXByb3Bvc2FsLm5hbWUKICAgICAgICAgICAgZWxpZiBwcm9wb3NhbC5raW5kPT0iY29kZSI6CiAgICAgICAgICAgICAgICB0YXJnZXQ9b3MucGF0aC5iYXNlbmFtZShfX2ZpbGVfXyk7IHBhdGNoZWQ9b3MucGF0aC5qb2luKHNhbmQucm9vdCx0YXJnZXQpCiAgICAgICAgICAgICAgICBvcGVuKHBhdGNoZWQsInciLGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKHByb3Bvc2FsLnBhdGNoX3RleHQgb3IgIiIpCiAgICAgICAgICAgICAgICBjb2RlPWYiaW1wb3J0IGltcG9ydGxpYi51dGlsLCBqc29uOyBwPXIne3BhdGNoZWR9Jzsgc3BlYz1pbXBvcnRsaWIudXRpbC5zcGVjX2Zyb21fZmlsZV9sb2NhdGlvbignaG1vZCcscCk7IG09aW1wb3J0bGliLnV0aWwubW9kdWxlX2Zyb21fc3BlYyhzcGVjKTsgc3BlYy5sb2FkZXIuZXhlY19tb2R1bGUobSk7IGg9bS5IaXZlKCk7IHByaW50KGpzb24uZHVtcHMoe3snb2snOlRydWV9fSkpIgogICAgICAgICAgICAgICAgcmMsb3V0PXNhbmQucnVuX3NuaXBwZXQoY29kZSkKICAgICAgICAgICAgICAgIGlmIHJjIT0wIG9yICcib2siOiB0cnVlJyBub3QgaW4gb3V0Lmxvd2VyKCk6IHJldHVybiB7Im9rIjpGYWxzZSwicmVhc29uIjoicGF0Y2ggc21va2UgdGVzdCBmYWlsZWQiLCJvdXQiOm91dH0KICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIHJldHVybiB7Im9rIjpGYWxzZSwicmVhc29uIjpmInNhbmRib3ggZmFpbGVkOiB7ZX0ifQogICAgICAgIGRlZiBjYW5kX2hpdmUoKTogcmV0dXJuIHNlbGYuaGl2ZV9jbHMobW9kZWxfaWQ9bW9kZWxfb3ZlcnJpZGUpIGlmIG1vZGVsX292ZXJyaWRlIGVsc2Ugc2VsZi5oaXZlX2Nscyhtb2RlbF9pZD1Ob25lKQogICAgICAgIGNhbmQ9X3N5bnRoZXRpY19ldmFsKGNhbmRfaGl2ZSwgcHJvbXB0cykKICAgICAgICBkZWx0YT17ImxhdF9tcyI6IGJhc2VbImxhdF9tcyJdLWNhbmRbImxhdF9tcyJdLCAidG9rc19zIjogY2FuZFsidG9rc19zIl0tYmFzZVsidG9rc19zIl0sICJxdWFsaXR5IjogY2FuZFsicXVhbGl0eSJdLWJhc2VbInF1YWxpdHkiXX0KICAgICAgICBwYXNzZWQ9VHJ1ZQogICAgICAgIGlmIENGR1siT1BUX1RIUkVTSF9MQVRFTkNZX01TIl0+MCBhbmQgZGVsdGFbImxhdF9tcyJdPENGR1siT1BUX1RIUkVTSF9MQVRFTkNZX01TIl06IHBhc3NlZD1GYWxzZQogICAgICAgIGlmIENGR1siT1BUX1RIUkVTSF9UT0tTX1BFUl9TIl0+MCBhbmQgZGVsdGFbInRva3NfcyJdPENGR1siT1BUX1RIUkVTSF9UT0tTX1BFUl9TIl06IHBhc3NlZD1GYWxzZQogICAgICAgIGlmIGRlbHRhWyJxdWFsaXR5Il08Q0ZHWyJPUFRfVEhSRVNIX1FVQUxJVFkiXTogcGFzc2VkPUZhbHNlCiAgICAgICAgcmVzdWx0PXsib2siOlRydWUsInByb3Bvc2FsIjpwcm9wb3NhbC5fX2RpY3RfXywiYmFzZSI6YmFzZSwiY2FuZCI6Y2FuZCwiZGVsdGEiOmRlbHRhLCJwYXNzZWQiOnBhc3NlZH0KICAgICAgICBfYXBwZW5kX2pzb25sKE9QVF9SRVNVTFRTLCByZXN1bHQpOyByZXR1cm4gcmVzdWx0CiAgICBkZWYgYXBwbHkoc2VsZiwgcmVzdWx0OkRpY3QpLT5UdXBsZVtib29sLHN0cl06CiAgICAgICAgcHJvcD1yZXN1bHQuZ2V0KCJwcm9wb3NhbCIse30pOyBraW5kPXByb3AuZ2V0KCJraW5kIik7IG5hbWU9cHJvcC5nZXQoIm5hbWUiLCIiKQogICAgICAgIGlmIG5vdCByZXN1bHQuZ2V0KCJwYXNzZWQiKTogcmV0dXJuIEZhbHNlLCJkaWQgbm90IG1lZXQgdGhyZXNob2xkcyIKICAgICAgICBpZiBraW5kPT0icGFja2FnZSI6CiAgICAgICAgICAgIGlmIG5vdCBzZWxmLl9hbGxvd2VkX3BrZyhuYW1lKTogcmV0dXJuIEZhbHNlLCJwYWNrYWdlIG5vdCBhbGxvd2xpc3RlZCIKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgc3VicHJvY2Vzcy5jaGVja19jYWxsKFtzeXMuZXhlY3V0YWJsZSwiLW0iLCJwaXAiLCJpbnN0YWxsIiwiLS11cGdyYWRlIiwgbmFtZSArICgoIj09Iitwcm9wLmdldCgidmVyc2lvbiIsIiIpKSBpZiBwcm9wLmdldCgidmVyc2lvbiIpIGVsc2UgIiIpXSkKICAgICAgICAgICAgICAgIHJldHVybiBUcnVlLCJwYWNrYWdlIGluc3RhbGxlZCIKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOiByZXR1cm4gRmFsc2UsZiJwaXAgZmFpbGVkOiB7ZX0iCiAgICAgICAgaWYga2luZD09Im1vZGVsIjoKICAgICAgICAgICAgaWYgbm90IHNlbGYuX2FsbG93ZWRfbW9kZWwobmFtZSk6IHJldHVybiBGYWxzZSwibW9kZWwgbm90IGFsbG93bGlzdGVkIgogICAgICAgICAgICBwcmVmPW9zLnBhdGguam9pbihPUFRfRElSLCJwcmVmZXJyZWRfbW9kZWwuanNvbiIpOyBqc29uLmR1bXAoeyJtb2RlbF9pZCI6bmFtZSwidHMiOnRpbWUudGltZSgpfSwgb3BlbihwcmVmLCJ3IixlbmNvZGluZz0idXRmLTgiKSkKICAgICAgICAgICAgcmV0dXJuIFRydWUsIm1vZGVsIHByZWZlcmVuY2UgcmVjb3JkZWQgKHRha2VzIGVmZmVjdCBhZnRlciByZXN0YXJ0KSIKICAgICAgICBpZiBraW5kPT0iY29kZSI6CiAgICAgICAgICAgIGlmIG5vdCBDRkdbIk9QVF9BVVRPX0FQUExZIl06IHJldHVybiBGYWxzZSwiYXdhaXRpbmcgT3duZXIgYXBwcm92YWwgZm9yIGNvZGUgY2hhbmdlcyIKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgdGFyZ2V0PW9zLnBhdGguYWJzcGF0aChfX2ZpbGVfXyk7IGJhY2t1cD10YXJnZXQrZiIuYmFrX3tpbnQodGltZS50aW1lKCkpfSI7IHNodXRpbC5jb3B5ZmlsZSh0YXJnZXQsYmFja3VwKQogICAgICAgICAgICAgICAgb3Blbih0YXJnZXQsInciLGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKHByb3AuZ2V0KCJwYXRjaF90ZXh0IiwiIikpOyByZXR1cm4gVHJ1ZSwiY29kZSB1cGRhdGVkIChiYWNrdXAgY3JlYXRlZCk7IHJlc3RhcnQgcmVjb21tZW5kZWQiCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZTogcmV0dXJuIEZhbHNlLGYiY29kZSB3cml0ZSBmYWlsZWQ6IHtlfSIKICAgICAgICByZXR1cm4gRmFsc2UsInVua25vd24gY2hhbmdlIHR5cGUiCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0gSGl2ZSBjb3JlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAtLS0gTWVtb3J5ICYgTWFuaWZlc3QgSGVscGVycyAoYXV0by1pbnNlcnRlZCkgLS0tCmltcG9ydCB0ZW1wZmlsZSwgdXJsbGliLnJlcXVlc3QsIHRhcmZpbGUsIHppcGZpbGUKZnJvbSBwYXRobGliIGltcG9ydCBQYXRoIGFzIF9QYXRoCgpkZWYgX2h1bWFuX3RzKHRzOiBpbnQpIC0+IHN0cjoKICAgIGltcG9ydCBkYXRldGltZQogICAgdHJ5OgogICAgICAgIHJldHVybiBkYXRldGltZS5kYXRldGltZS51dGNmcm9tdGltZXN0YW1wKHRzKS5zdHJmdGltZSgiJVktJW0tJWQgJUg6JU06JVMgVVRDIikKICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgcmV0dXJuIHN0cih0cykKCklOR0VTVF9QUk9HUkVTUyA9IG9zLnBhdGguam9pbihDRkcuZ2V0KCJTVEFURV9ESVIiLCIuL3N0YXRlIiksICJpbmdlc3RfcHJvZ3Jlc3MuanNvbiIpCgpkZWYgX2xvYWRfcHJvZ3Jlc3MoKToKICAgIHRyeToKICAgICAgICBpZiBvcy5wYXRoLmV4aXN0cyhJTkdFU1RfUFJPR1JFU1MpOgogICAgICAgICAgICByZXR1cm4ganNvbi5sb2FkKG9wZW4oSU5HRVNUX1BST0dSRVNTLCAiciIsIGVuY29kaW5nPSJ1dGYtOCIpKQogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICBwYXNzCiAgICByZXR1cm4geyJkb25lIjogW10sICJzdGFnZSI6IDAsICJ0cyI6IDB9CgpkZWYgX3NhdmVfcHJvZ3Jlc3MocCk6CiAgICB0cnk6CiAgICAgICAganNvbi5kdW1wKHAsIG9wZW4oSU5HRVNUX1BST0dSRVNTLCAidyIsIGVuY29kaW5nPSJ1dGYtOCIpLCBpbmRlbnQ9MikKICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgcGFzcwoKZGVmIHVwZGF0ZV9zZWxmX21hbmlmZXN0KGRhdGFzZXRzX2RvbmU6IGxpc3QsIHZlY3RvcnNfdG90YWw6IGludCk6CiAgICAiIiJSZXdyaXRlIHRoZSBNRU1PUllfTUFOSUZFU1QgYmxvY2sgaW5zaWRlIHRoaXMgc2NyaXB0LiIiIgogICAgaWYgbm90IENGRy5nZXQoIkhJVkVfQUxMT1dfU0VMRl9XUklURV9NQU5JRkVTVCIsIFRydWUpOgogICAgICAgIHJldHVybiBGYWxzZSwgInNlbGYtd3JpdGUgZGlzYWJsZWQiCgogICAgdGFyZ2V0ID0gQ0ZHLmdldCgiSElWRV9TRUxGX1dSSVRFX0ZJTEUiKSBvciBvcy5wYXRoLmFic3BhdGgoX19maWxlX18pCiAgICB0cnk6CiAgICAgICAgd2l0aCBvcGVuKHRhcmdldCwgInIiLCBlbmNvZGluZz0idXRmLTgiKSBhcyBmOgogICAgICAgICAgICBzcmMgPSBmLnJlYWQoKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHJldHVybiBGYWxzZSwgZiJyZWFkIGVycm9yOiB7ZX0iCgogICAgc3RhcnRfdGFnID0gIiMgLS0tIEJFR0lOIE1FTU9SWSBNQU5JRkVTVCAoYXV0by11cGRhdGVkKSAtLS0iCiAgICBlbmRfdGFnICAgPSAiIyAtLS0gRU5EIE1FTU9SWSBNQU5JRkVTVCAtLS0iCiAgICBpZiBzdGFydF90YWcgbm90IGluIHNyYyBvciBlbmRfdGFnIG5vdCBpbiBzcmM6CiAgICAgICAgcmV0dXJuIEZhbHNlLCAibWFuaWZlc3QgbWFya2VycyBub3QgZm91bmQiCgogICAgaGVhZCwgcmVzdCA9IHNyYy5zcGxpdChzdGFydF90YWcsIDEpCiAgICBfLCB0YWlsID0gcmVzdC5zcGxpdChlbmRfdGFnLCAxKQoKICAgIHBheWxvYWQgPSB7CiAgICAgICAgInVwZGF0ZWRfdHMiOiBpbnQodGltZS50aW1lKCkpLAogICAgICAgICJkYXRhc2V0c19kb25lIjogc29ydGVkKGxpc3QoeypkYXRhc2V0c19kb25lfSkpLAogICAgICAgICJ2ZWN0b3JzX3RvdGFsIjogaW50KHZlY3RvcnNfdG90YWwpLAogICAgICAgICJub3RlcyI6ICJTZXQgSElWRV9BTExPV19TRUxGX1dSSVRFX01BTklGRVNUPTAgdG8gc3RvcCBhdXRvLXVwZGF0ZXMuIgogICAgfQoKICAgIGJsb2NrID0gc3RhcnRfdGFnICsgIlxuIyAoVGhpcyBibG9jayBpcyBhdXRvLXdyaXR0ZW4gYnkgSGl2ZSB0byByZWNvcmQgd2hhdCBkYXRhc2V0cy9maWxlc1xuIyAgaGF2ZSBhbHJlYWR5IGJlZW4gY29udmVydGVkIGludG8gbWVtb3J5IChjdXJ2ZXMpLiBEbyBub3QgZWRpdCBieSBoYW5kLilcbiIKICAgIGJsb2NrICs9ICJNRU1PUllfTUFOSUZFU1QgPSAiICsganNvbi5kdW1wcyhwYXlsb2FkLCBpbmRlbnQ9NCwgZW5zdXJlX2FzY2lpPUZhbHNlKSArICJcbiIKICAgIGJsb2NrICs9IGVuZF90YWcKCiAgICBuZXdfc3JjID0gaGVhZCArIGJsb2NrICsgdGFpbAogICAgdG1wID0gdGFyZ2V0ICsgIi50bXAiCiAgICB0cnk6CiAgICAgICAgd2l0aCBvcGVuKHRtcCwgInciLCBlbmNvZGluZz0idXRmLTgiKSBhcyBmOgogICAgICAgICAgICBmLndyaXRlKG5ld19zcmMpCiAgICAgICAgb3MucmVwbGFjZSh0bXAsIHRhcmdldCkKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICByZXR1cm4gRmFsc2UsIGYid3JpdGUgZXJyb3I6IHtlfSIKCiAgICByZXR1cm4gVHJ1ZSwgZiJtYW5pZmVzdCB1cGRhdGVkICh7X2h1bWFuX3RzKHBheWxvYWRbJ3VwZGF0ZWRfdHMnXSl9KSIKCmRlZiBfY3VydmVzX3ByZXNlbnQoY3VydmVfZGlyOiBzdHIpIC0+IGJvb2w6CiAgICBpZHggPSBvcy5wYXRoLmpvaW4oY3VydmVfZGlyLCAiZmFpc3MuaW5kZXgiKQogICAgbWV0YSA9IG9zLnBhdGguam9pbihjdXJ2ZV9kaXIsICJtZXRhLmpzb25sIikKICAgIHJldHVybiBvcy5wYXRoLmV4aXN0cyhpZHgpIGFuZCBvcy5wYXRoLmdldHNpemUoaWR4KSA+IDAgYW5kIG9zLnBhdGguZXhpc3RzKG1ldGEpCgpkZWYgX2V4dHJhY3RfYXJjaGl2ZShhcmNoaXZlX3BhdGg6IHN0ciwgZGVzdF9kaXI6IHN0cikgLT4gYm9vbDoKICAgIG9zLm1ha2VkaXJzKGRlc3RfZGlyLCBleGlzdF9vaz1UcnVlKQogICAgdHJ5OgogICAgICAgIGlmIGFyY2hpdmVfcGF0aC5lbmRzd2l0aCgiLnRhci5neiIpIG9yIGFyY2hpdmVfcGF0aC5lbmRzd2l0aCgiLnRneiIpOgogICAgICAgICAgICB3aXRoIHRhcmZpbGUub3BlbihhcmNoaXZlX3BhdGgsICJyOmd6IikgYXMgdGY6CiAgICAgICAgICAgICAgICB0Zi5leHRyYWN0YWxsKGRlc3RfZGlyKQogICAgICAgICAgICByZXR1cm4gVHJ1ZQogICAgICAgIGlmIGFyY2hpdmVfcGF0aC5lbmRzd2l0aCgiLnppcCIpOgogICAgICAgICAgICB3aXRoIHppcGZpbGUuWmlwRmlsZShhcmNoaXZlX3BhdGgsICJyIikgYXMgejoKICAgICAgICAgICAgICAgIHouZXh0cmFjdGFsbChkZXN0X2RpcikKICAgICAgICAgICAgcmV0dXJuIFRydWUKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICBvcGVuKG9zLnBhdGguam9pbihDRkcuZ2V0KCJTVEFURV9ESVIiLCIuL3N0YXRlIiksICJyZXN0b3JlX2Vycm9yLmxvZyIpLCAiYSIsIGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKGYiZXh0cmFjdDoge2V9XG4iKQogICAgcmV0dXJuIEZhbHNlCgpkZWYgX3Jlc3RvcmVfZnJvbV9sb2NhbF9hcmNoaXZlKGN1cnZlX2Rpcjogc3RyKToKICAgIGFyYyA9IENGRy5nZXQoIkNVUlZFU19BUkNISVZFX0xPQ0FMIikgb3IgImN1cnZlcy50YXIuZ3oiCiAgICBpZiBub3QgYXJjIG9yIG5vdCBvcy5wYXRoLmV4aXN0cyhhcmMpOgogICAgICAgIHJldHVybiBGYWxzZSwgIm5vIGxvY2FsIGFyY2hpdmUiCiAgICBvayA9IF9leHRyYWN0X2FyY2hpdmUoYXJjLCBjdXJ2ZV9kaXIpCiAgICByZXR1cm4gKG9rLCAicmVzdG9yZWQgZnJvbSBsb2NhbCBhcmNoaXZlIiBpZiBvayBlbHNlICJsb2NhbCBleHRyYWN0IGZhaWxlZCIpCgpkZWYgX3Jlc3RvcmVfZnJvbV91cmwoY3VydmVfZGlyOiBzdHIpOgogICAgdXJsID0gKENGRy5nZXQoIkNVUlZFU19BUkNISVZFX1VSTCIpIG9yICIiKS5zdHJpcCgpCiAgICBpZiBub3QgdXJsOgogICAgICAgIHJldHVybiBGYWxzZSwgIm5vIFVSTCBwcm92aWRlZCIKICAgIHRyeToKICAgICAgICB0bXAgPSBvcy5wYXRoLmpvaW4odGVtcGZpbGUuZ2V0dGVtcGRpcigpLCBmImN1cnZlc197aW50KHRpbWUudGltZSgpKX0ucGtnIikKICAgICAgICB1cmxsaWIucmVxdWVzdC51cmxyZXRyaWV2ZSh1cmwsIHRtcCkKICAgICAgICBvayA9IF9leHRyYWN0X2FyY2hpdmUodG1wLCBjdXJ2ZV9kaXIpCiAgICAgICAgdHJ5OiBvcy5yZW1vdmUodG1wKQogICAgICAgIGV4Y2VwdDogcGFzcwogICAgICAgIHJldHVybiAob2ssICJyZXN0b3JlZCBmcm9tIFVSTCIgaWYgb2sgZWxzZSAiVVJMIGV4dHJhY3QgZmFpbGVkIikKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICBvcGVuKG9zLnBhdGguam9pbihDRkcuZ2V0KCJTVEFURV9ESVIiLCIuL3N0YXRlIiksICJyZXN0b3JlX2Vycm9yLmxvZyIpLCAiYSIsIGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKGYidXJsOiB7ZX1cbiIpCiAgICAgICAgcmV0dXJuIEZhbHNlLCAiVVJMIGRvd25sb2FkIGVycm9yIgoKZGVmIF9yZXN0b3JlX2Zyb21faGZfZGF0YXNldChjdXJ2ZV9kaXI6IHN0cik6CiAgICByZXBvX2lkID0gKENGRy5nZXQoIkNVUlZFU19IRl9EQVRBU0VUIikgb3IgIiIpLnN0cmlwKCkKICAgIHN1YiA9IChDRkcuZ2V0KCJDVVJWRVNfSEZfU1VCUEFUSCIpIG9yICIiKS5zdHJpcCgpCiAgICBpZiBub3QgcmVwb19pZDoKICAgICAgICByZXR1cm4gRmFsc2UsICJubyBkYXRhc2V0IHJlcG8iCiAgICB0cnk6CiAgICAgICAgZnJvbSBodWdnaW5nZmFjZV9odWIgaW1wb3J0IHNuYXBzaG90X2Rvd25sb2FkLCBoZl9odWJfZG93bmxvYWQKICAgICAgICBjYWNoZSA9IG9zLnBhdGguam9pbigiL3RtcCIsICJoZl9jdXJ2ZXNfY2FjaGUiKQogICAgICAgIHRva2VuID0gQ0ZHLmdldCgiSEZfUkVBRF9UT0tFTiIpIG9yIE5vbmUKICAgICAgICBmb3IgZm5hbWUgaW4gWyJjdXJ2ZXMudGFyLmd6IiwgImN1cnZlcy56aXAiXToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgZnAgPSBoZl9odWJfZG93bmxvYWQocmVwb19pZD1yZXBvX2lkLCBmaWxlbmFtZT0oc3ViICsgIi8iICsgZm5hbWUpIGlmIHN1YiBlbHNlIGZuYW1lLCB0b2tlbj10b2tlbiwgbG9jYWxfZGlyPWNhY2hlLCBsb2NhbF9kaXJfdXNlX3N5bWxpbmtzPUZhbHNlKQogICAgICAgICAgICAgICAgaWYgX2V4dHJhY3RfYXJjaGl2ZShmcCwgY3VydmVfZGlyKToKICAgICAgICAgICAgICAgICAgICByZXR1cm4gVHJ1ZSwgZiJyZXN0b3JlZCBmcm9tIEhGIGRhdGFzZXQgZmlsZSB7Zm5hbWV9IgogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICAgICAgICAgcGFzcwoKICAgICAgICAgICAgIyBhdXRvLWFyY2hpdmUgYWZ0ZXIgZWFjaCBkYXRhc2V0IGlmIGNvbmZpZ3VyZWQKICAgICAgICAgICAgaWYgQ0ZHLmdldCgiSElWRV9BVVRPX0FSQ0hJVkUiLCBUcnVlKSBhbmQgc3RyKENGRy5nZXQoIkhJVkVfQVVUT19BUkNISVZFX01PREUiLCJwZXJfY2hhaW4iKSkubG93ZXIoKSA9PSAicGVyX2RhdGFzZXQiOgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIF9va19hcmMsIF9hcCA9IF9hcmNoaXZlX21lbW9yeShjdXJ2ZV9kaXIpCiAgICAgICAgICAgICAgICAgICAgb3Blbihvcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwgImFyY2hpdmVfc3RhdHVzLmxvZyIpLCAiYSIsIGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKAogICAgICAgICAgICAgICAgICAgICAgICBqc29uLmR1bXBzKHsidHMiOiB0aW1lLnRpbWUoKSwgIm1vZGUiOiAicGVyX2RhdGFzZXQiLCAib2siOiBfb2tfYXJjLCAicGF0aCI6IF9hcH0pICsgIlxuIgogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgX2VfYXJjOgogICAgICAgICAgICAgICAgICAgIG9wZW4ob3MucGF0aC5qb2luKENGR1siU1RBVEVfRElSIl0sICJhcmNoaXZlX2Vycm9yLmxvZyIpLCAiYSIsIGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKAogICAgICAgICAgICAgICAgICAgICAgICAicGVyX2RhdGFzZXQ6ICIgKyBzdHIoX2VfYXJjKSArICJcbiIKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgc3JjID0gb3MucGF0aC5qb2luKGxvY2FsX2Rpciwgc3ViKSBpZiBzdWIgZWxzZSBsb2NhbF9kaXIKICAgICAgICBpZiBvcy5wYXRoLmlzZGlyKHNyYyk6CiAgICAgICAgICAgIGZvciByb290LCBkaXJzLCBmaWxlcyBpbiBvcy53YWxrKHNyYyk6CiAgICAgICAgICAgICAgICByZWwgPSBvcy5wYXRoLnJlbHBhdGgocm9vdCwgc3JjKQogICAgICAgICAgICAgICAgZGVzdF9yb290ID0gb3MucGF0aC5qb2luKGN1cnZlX2RpciwgcmVsKSBpZiByZWwgIT0gIi4iIGVsc2UgY3VydmVfZGlyCiAgICAgICAgICAgICAgICBvcy5tYWtlZGlycyhkZXN0X3Jvb3QsIGV4aXN0X29rPVRydWUpCiAgICAgICAgICAgICAgICBmb3IgZm4gaW4gZmlsZXM6CiAgICAgICAgICAgICAgICAgICAgc2h1dGlsLmNvcHkyKG9zLnBhdGguam9pbihyb290LCBmbiksIG9zLnBhdGguam9pbihkZXN0X3Jvb3QsIGZuKSkKICAgICAgICAgICAgcmV0dXJuIFRydWUsICJyZXN0b3JlZCBmcm9tIEhGIGRhdGFzZXQgc25hcHNob3QiCiAgICAgICAgcmV0dXJuIEZhbHNlLCAiSEYgc25hcHNob3QgbWlzc2luZyBzdWJwYXRoIgogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIG9wZW4ob3MucGF0aC5qb2luKENGRy5nZXQoIlNUQVRFX0RJUiIsIi4vc3RhdGUiKSwgInJlc3RvcmVfZXJyb3IubG9nIiksICJhIiwgZW5jb2Rpbmc9InV0Zi04Iikud3JpdGUoZiJoZjoge2V9XG4iKQogICAgICAgIHJldHVybiBGYWxzZSwgIkhGIHJlc3RvcmUgZXJyb3IiCgpkZWYgcmVzdG9yZV9jdXJ2ZXNfaWZfbWlzc2luZyhjdXJ2ZV9kaXI6IHN0cik6CgogICAgaWYgbm90IENGRy5nZXQoIkhJVkVfQ1VSVkVTX0FVVE9fUkVTVE9SRSIsIFRydWUpOgogICAgICAgIHJldHVybiBGYWxzZSwgImF1dG8tcmVzdG9yZSBkaXNhYmxlZCIKICAgIGlmIF9jdXJ2ZXNfcHJlc2VudChjdXJ2ZV9kaXIpOgogICAgICAgIHJldHVybiBUcnVlLCAibWVtb3J5IHByZXNlbnQiCiAgICBvaywgbXNnID0gX3Jlc3RvcmVfZnJvbV9sb2NhbF9hcmNoaXZlKGN1cnZlX2RpcikKICAgIGlmIG9rIGFuZCBfY3VydmVzX3ByZXNlbnQoY3VydmVfZGlyKToKICAgICAgICByZXR1cm4gVHJ1ZSwgbXNnCiAgICBvaywgbXNnID0gX3Jlc3RvcmVfZnJvbV91cmwoY3VydmVfZGlyKQogICAgaWYgb2sgYW5kIF9jdXJ2ZXNfcHJlc2VudChjdXJ2ZV9kaXIpOgogICAgICAgIHJldHVybiBUcnVlLCBtc2cKICAgIG9rLCBtc2cgPSBfcmVzdG9yZV9mcm9tX2hmX2RhdGFzZXQoY3VydmVfZGlyKQogICAgaWYgb2sgYW5kIF9jdXJ2ZXNfcHJlc2VudChjdXJ2ZV9kaXIpOgogICAgICAgIHJldHVybiBUcnVlLCBtc2cKICAgIHJldHVybiBGYWxzZSwgIm5vIHJlc3RvcmUgc291cmNlIHN1Y2NlZWRlZCIKZGVmIF9hcmNoaXZlX21lbW9yeShjdXJ2ZV9kaXI6IHN0ciwgYXJjaGl2ZV9wYXRoOiBzdHI9Tm9uZSkgLT4gdHVwbGU6CiAgICAiIiJUYXIrZ3ppcCB0aGUgbWVtb3J5IGRpcmVjdG9yeSB0byBhcmNoaXZlX3BhdGggKGRlZmF1bHQgY3VydmVzLnRhci5neikuIiIiCiAgICB0cnk6CiAgICAgICAgaW1wb3J0IHRhcmZpbGUsIHRlbXBmaWxlIGFzIF90ZgogICAgICAgIGFwID0gYXJjaGl2ZV9wYXRoIG9yIENGRy5nZXQoIkhJVkVfQVJDSElWRV9QQVRIIiwiY3VydmVzLnRhci5neiIpIG9yICJjdXJ2ZXMudGFyLmd6IgogICAgICAgICMgd3JpdGUgdG8gdGVtcCB0aGVuIG1vdmUgZm9yIGF0b21pY2l0eQogICAgICAgIHRtcCA9IG9zLnBhdGguam9pbihfdGYuZ2V0dGVtcGRpcigpLCBmImN1cnZlc197aW50KHRpbWUudGltZSgpKX0udGFyLmd6IikKICAgICAgICB3aXRoIHRhcmZpbGUub3Blbih0bXAsICJ3Omd6IikgYXMgdGFyOgogICAgICAgICAgICB0YXIuYWRkKGN1cnZlX2RpciwgYXJjbmFtZT0iY3VydmVzIikKICAgICAgICBvcy5yZXBsYWNlKHRtcCwgYXApCiAgICAgICAgcmV0dXJuIFRydWUsIGFwCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgdHJ5OgogICAgICAgICAgICBvcGVuKG9zLnBhdGguam9pbihDRkdbIlNUQVRFX0RJUiJdLCAiYXJjaGl2ZV9lcnJvci5sb2ciKSwgImEiLCBlbmNvZGluZz0idXRmLTgiKS53cml0ZShzdHIoZSkrIlxuIikKICAgICAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICAgICBwYXNzCiAgICAgICAgcmV0dXJuIEZhbHNlLCBzdHIoZSkKCgogICAgaWYgbm90IENGRy5nZXQoIkNVUlZFU19BVVRPX1JFU1RPUkUiLCBUcnVlKToKICAgICAgICByZXR1cm4gRmFsc2UsICJhdXRvLXJlc3RvcmUgZGlzYWJsZWQiCiAgICBpZiBfY3VydmVzX3ByZXNlbnQoY3VydmVfZGlyKToKICAgICAgICByZXR1cm4gVHJ1ZSwgImN1cnZlcyBhbHJlYWR5IHByZXNlbnQiCiAgICBvaywgbXNnID0gX3Jlc3RvcmVfZnJvbV9sb2NhbF9hcmNoaXZlKGN1cnZlX2RpcikKICAgIGlmIG9rIGFuZCBfY3VydmVzX3ByZXNlbnQoY3VydmVfZGlyKTogcmV0dXJuIFRydWUsIG1zZwogICAgb2ssIG1zZyA9IF9yZXN0b3JlX2Zyb21fdXJsKGN1cnZlX2RpcikKICAgIGlmIG9rIGFuZCBfY3VydmVzX3ByZXNlbnQoY3VydmVfZGlyKTogcmV0dXJuIFRydWUsIG1zZwogICAgb2ssIG1zZyA9IF9yZXN0b3JlX2Zyb21faGZfZGF0YXNldChjdXJ2ZV9kaXIpCiAgICBpZiBvayBhbmQgX2N1cnZlc19wcmVzZW50KGN1cnZlX2Rpcik6IHJldHVybiBUcnVlLCBtc2cKICAgIHJldHVybiBGYWxzZSwgIm5vIHJlc3RvcmUgc291cmNlIHN1Y2NlZWRlZCIKIyAtLS0gRW5kIE1lbW9yeSAmIE1hbmlmZXN0IEhlbHBlcnMgLS0tCgoKIyAtLS0gU3RhZ2VkIEluZ2VzdGlvbiBPcmNoZXN0cmF0b3IgKGF1dG8pIC0tLQpkZWYgX3BsYW5fc291cmNlcygpOgogICAgc3JjcyA9IFtzLnN0cmlwKCkgZm9yIHMgaW4gKENGRy5nZXQoIklOR0VTVF9TT1VSQ0VTIikgb3IgIiIpLnNwbGl0KCIsIikgaWYgcy5zdHJpcCgpXQogICAgcmV0dXJuIHNyY3Mgb3IgKERFRkFVTFRfU09VUkNFUyBpZiAiREVGQVVMVF9TT1VSQ0VTIiBpbiBnbG9iYWxzKCkgZWxzZSBbXSkKCmRlZiBfbmV4dF9iYXRjaChkb25lOiBsaXN0LCBhbGxfc291cmNlczogbGlzdCwgazogaW50KToKICAgIHRvZG8gPSBbcyBmb3IgcyBpbiBhbGxfc291cmNlcyBpZiBzIG5vdCBpbiBzZXQoZG9uZSldCiAgICByZXR1cm4gdG9kb1s6bWF4KGssMCldCgpkZWYgc3RhZ2VkX2luZ2VzdF9vbmNlKGN1cnZlX2Rpcjogc3RyKSAtPiBkaWN0OgogICAgIiIiSW5nZXN0IGEgc2luZ2xlIHN0YWdlICh1cCB0byBISVZFX0lOR0VTVF9TVEFHRV9TSVpFIGRhdGFzZXRzKSwgcmVzcGVjdGluZyBkaXNrIGZsb29yLiBVcGRhdGVzIHByb2dyZXNzICsgbWFuaWZlc3QuIiIiCiAgICB0cnk6CiAgICAgICAgaW1wb3J0IHNodXRpbCwgdGltZSBhcyBfdAogICAgICAgIGZsb29yID0gaW50KENGRy5nZXQoIkhJVkVfSU5HRVNUX01JTl9GUkVFX0dCIiwgOCkpCiAgICAgICAgZnJlZV9nYiA9IHNodXRpbC5kaXNrX3VzYWdlKCIuIikuZnJlZSAvICgxMDI0KiozKQogICAgICAgIGlmIGZyZWVfZ2IgPCBmbG9vcjoKICAgICAgICAgICAgcmV0dXJuIHsib2siOiBGYWxzZSwgInJlYXNvbiI6IGYiZnJlZSBkaXNrIHtmcmVlX2diOi4xZn0gR0IgPCBmbG9vciB7Zmxvb3J9IEdCIn0KICAgICAgICBhbGxfc291cmNlcyA9IF9wbGFuX3NvdXJjZXMoKQogICAgICAgIHByb2cgPSBfbG9hZF9wcm9ncmVzcygpCiAgICAgICAgYmF0Y2ggPSBfbmV4dF9iYXRjaChwcm9nLmdldCgiZG9uZSIsIFtdKSwgYWxsX3NvdXJjZXMsIGludChDRkcuZ2V0KCJISVZFX0lOR0VTVF9TVEFHRV9TSVpFIiwzKSkpCiAgICAgICAgaWYgbm90IGJhdGNoOgogICAgICAgICAgICByZXR1cm4geyJvayI6IFRydWUsICJyZWFzb24iOiAiYWxsIHNvdXJjZXMgYWxyZWFkeSBpbmdlc3RlZCIsICJkb25lIjogcHJvZy5nZXQoImRvbmUiLCBbXSl9CiAgICAgICAgdG90YWxfYWRkZWQgPSAwCiAgICAgICAgYWN0dWFsbHlfaW5nZXN0ZWQgPSBbXQogICAgICAgIGZvciBkcyBpbiBiYXRjaDoKICAgICAgICAgICAgYWRkZWQgPSBpbmdlc3RfYWxsKGN1cnZlX2RpciwgW2RzXSwgc2NvcGU9ImdlbmVyYWwiKQogICAgICAgICAgICB0b3RhbF9hZGRlZCArPSBhZGRlZAogICAgICAgICAgICBhY3R1YWxseV9pbmdlc3RlZC5hcHBlbmQoZHMpCiAgICAgICAgICAgIHByb2dbImRvbmUiXS5hcHBlbmQoZHMpCiAgICAgICAgICAgICMgY2hlY2sgZGlzayBhZnRlciBlYWNoIGRhdGFzZXQKICAgICAgICAgICAgZnJlZV9nYiA9IHNodXRpbC5kaXNrX3VzYWdlKCIuIikuZnJlZSAvICgxMDI0KiozKQogICAgICAgICAgICBpZiBmcmVlX2diIDwgZmxvb3I6CiAgICAgICAgICAgICAgICBicmVhawogICAgICAgIHByb2dbInN0YWdlIl0gPSBpbnQocHJvZy5nZXQoInN0YWdlIiwgMCkpICsgMQogICAgICAgIHByb2dbInRzIl0gPSBpbnQoX3QudGltZSgpKQogICAgICAgIF9zYXZlX3Byb2dyZXNzKHByb2cpCiAgICAgICAgIyBtYW5pZmVzdCB1cGRhdGUKICAgICAgICB0cnk6CiAgICAgICAgICAgIHZlY3MgPSAwCiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHZlY3MgPSBDdXJ2ZVN0b3JlKGN1cnZlX2RpcikuaW5kZXgubnRvdGFsCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgICAgICBwYXNzCiAgICAgICAgICAgIHVwZGF0ZV9zZWxmX21hbmlmZXN0KHByb2cuZ2V0KCJkb25lIiwgW10pLCBpbnQodmVjcykpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgcGFzcwogICAgICAgIHJldHVybiB7Im9rIjogVHJ1ZSwgImluZ2VzdGVkIjogYWN0dWFsbHlfaW5nZXN0ZWQsICJhZGRlZF92ZWN0b3JzX2VzdCI6IHRvdGFsX2FkZGVkLCAic3RhZ2UiOiBwcm9nWyJzdGFnZSJdfQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBfZToKICAgICAgICB0cnk6CiAgICAgICAgICAgIG9wZW4ob3MucGF0aC5qb2luKENGRy5nZXQoIlNUQVRFX0RJUiIsIi4vc3RhdGUiKSwgImluZ2VzdF9lcnJvci5sb2ciKSwgImEiLCBlbmNvZGluZz0idXRmLTgiKS53cml0ZShzdHIoX2UpKyJcbiIpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgcGFzcwogICAgICAgIHJldHVybiB7Im9rIjogRmFsc2UsICJlcnJvciI6IHN0cihfZSl9CgpkZWYgc3RhZ2VkX2luZ2VzdF9jaGFpbl9pZl9lbmFibGVkKGN1cnZlX2Rpcjogc3RyKSAtPiBkaWN0OgogICAgIiIiUnVuIDAuLk4gc3RhZ2VzIHRoaXMgYm9vdCBkZXBlbmRpbmcgb24gSElWRV9JTkdFU1RfQ0hBSU4gYW5kIEhJVkVfSU5HRVNUX0NIQUlOX01BWCwgd2l0aCBzYWZldHkgY2hlY2tzLiIiIgogICAgaWYgbm90IENGRy5nZXQoIkhJVkVfSU5HRVNUX1NUQUdFRCIsIFRydWUpOgogICAgICAgIHJldHVybiB7Im9rIjogVHJ1ZSwgInJlYXNvbiI6ICJzdGFnZWQgZGlzYWJsZWQifQogICAgcmVzdWx0cyA9IFtdCiAgICBtYXhfc3RhZ2VzID0gbWF4KDAsIGludChDRkcuZ2V0KCJISVZFX0lOR0VTVF9DSEFJTl9NQVgiLCAyKSkpIGlmIENGRy5nZXQoIkhJVkVfSU5HRVNUX0NIQUlOIiwgVHJ1ZSkgZWxzZSAoMSBpZiBDRkcuZ2V0KCJISVZFX0lOR0VTVF9ORVhUIikgZWxzZSAwKQogICAgZm9yIGkgaW4gcmFuZ2UobWF4X3N0YWdlcyk6CiAgICAgICAgciA9IHN0YWdlZF9pbmdlc3Rfb25jZShjdXJ2ZV9kaXIpCiAgICAgICAgcmVzdWx0cy5hcHBlbmQocikKICAgICAgICBpZiBub3Qgci5nZXQoIm9rIiwgRmFsc2UpOgogICAgICAgICAgICBicmVhawogICAgICAgIGlmIHIuZ2V0KCJyZWFzb24iKSA9PSAiYWxsIHNvdXJjZXMgYWxyZWFkeSBpbmdlc3RlZCI6CiAgICAgICAgICAgIGJyZWFrCiAgICAgICAgIyBzdG9wIGlmIG5vIGl0ZW1zIHdlcmUgaW5nZXN0ZWQgKGUuZy4sIGRpc2sgZmxvb3IgaGl0IGltbWVkaWF0ZWx5KQogICAgICAgIGlmIG5vdCByLmdldCgiaW5nZXN0ZWQiKToKICAgICAgICAgICAgYnJlYWsKICAgICMgYXV0by1hcmNoaXZlIGFmdGVyIGNoYWluIGlmIGNvbmZpZ3VyZWQKICAgIGlmIENGRy5nZXQoIkhJVkVfQVVUT19BUkNISVZFIiwgVHJ1ZSkgYW5kIHN0cihDRkcuZ2V0KCJISVZFX0FVVE9fQVJDSElWRV9NT0RFIiwicGVyX2NoYWluIikpLmxvd2VyKCkgaW4gKCJwZXJfY2hhaW4iLCJwZXJkYXRhc2V0IiwicGVyLWRhdGFzZXQiKToKICAgICAgICB0cnk6CiAgICAgICAgICAgIF9va19hcmMsIF9hcCA9IF9hcmNoaXZlX21lbW9yeShjdXJ2ZV9kaXIpCiAgICAgICAgICAgIG9wZW4ob3MucGF0aC5qb2luKENGR1siU1RBVEVfRElSIl0sICJhcmNoaXZlX3N0YXR1cy5sb2ciKSwgImEiLCBlbmNvZGluZz0idXRmLTgiKS53cml0ZShqc29uLmR1bXBzKHsidHMiOnRpbWUudGltZSgpLCJtb2RlIjoicGVyX2NoYWluIiwib2siOl9va19hcmMsInBhdGgiOl9hcH0pKyJcbiIpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBfZV9hcmM6CiAgICAgICAgICAgIG9wZW4ob3MucGF0aC5qb2luKENGR1siU1RBVEVfRElSIl0sICJhcmNoaXZlX2Vycm9yLmxvZyIpLCAiYSIsIGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKCJwZXJfY2hhaW46ICIrc3RyKF9lX2FyYykrIlxuIikKCiAgICByZXR1cm4geyJvayI6IFRydWUsICJjaGFpbl9yZXN1bHRzIjogcmVzdWx0c30KIyAtLS0gRW5kIFN0YWdlZCBJbmdlc3Rpb24gT3JjaGVzdHJhdG9yIC0tLQoKCmNsYXNzIEhpdmU6CiAgICBkZWYgX19pbml0X18oc2VsZiwgbW9kZWxfaWQ6IE9wdGlvbmFsW3N0cl09Tm9uZSwgZGV2aWNlOiBPcHRpb25hbFtzdHJdPU5vbmUpOgogICAgICAgICMgLS0tIHRyeSByZXN0b3JpbmcgbWVtb3J5IGlmIG1pc3NpbmcgKGxvY2FsIGFyY2hpdmUgLyBVUkwgLyBIRiBkYXRhc2V0KSAtLS0KICAgICAgICB0cnk6CiAgICAgICAgICAgIG9rX3Jlc3RvcmVkLCByZXN0b3JlX21zZyA9IHJlc3RvcmVfY3VydmVzX2lmX21pc3NpbmcoQ0ZHWyJDVVJWRV9ESVIiXSBpZiAiQ1VSVkVfRElSIiBpbiBDRkcgZWxzZSBDRkcuZ2V0KCJNRU1PUllfRElSIiwiLi9jdXJ2ZXMiKSkKICAgICAgICAgICAgb3Blbihvcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwgInJlc3RvcmVfc3RhdHVzLmxvZyIpLCAiYSIsIGVuY29kaW5nPSJ1dGYtOCIpLndyaXRlKGpzb24uZHVtcHMoeyJvayI6Ym9vbChva19yZXN0b3JlZCksIm1zZyI6cmVzdG9yZV9tc2csInRzIjp0aW1lLnRpbWUoKX0pKyJcbiIpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBvcGVuKG9zLnBhdGguam9pbihDRkdbIlNUQVRFX0RJUiJdLCAicmVzdG9yZV9lcnJvci5sb2ciKSwgImEiLCBlbmNvZGluZz0idXRmLTgiKS53cml0ZSgicmVzdG9yZTogIitzdHIoZSkrIlxuIikKICAgICAgICAjIC0tLSBzdGFnZWQgaW5nZXN0aW9uIGNoYWluaW5nIChydW4gbmV4dCBzdGFnZXMgYXV0b21hdGljYWxseSBpZiBlbmFibGVkKSAtLS0KICAgICAgICB0cnk6CiAgICAgICAgICAgIF9pbmdfY2hhaW4gPSBzdGFnZWRfaW5nZXN0X2NoYWluX2lmX2VuYWJsZWQoQ0ZHWyJDVVJWRV9ESVIiXSBpZiAiQ1VSVkVfRElSIiBpbiBDRkcgZWxzZSBDRkcuZ2V0KCJNRU1PUllfRElSIiwiLi9jdXJ2ZXMiKSkKICAgICAgICAgICAgb3Blbihvcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwgImluZ2VzdF9jaGFpbl9zdGF0dXMubG9nIiksICJhIiwgZW5jb2Rpbmc9InV0Zi04Iikud3JpdGUoanNvbi5kdW1wcyh7InRzIjp0aW1lLnRpbWUoKSwiY2hhaW4iOl9pbmdfY2hhaW59KSsiXG4iKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgb3Blbihvcy5wYXRoLmpvaW4oQ0ZHWyJTVEFURV9ESVIiXSwgImluZ2VzdF9lcnJvci5sb2ciKSwgImEiLCBlbmNvZGluZz0idXRmLTgiKS53cml0ZSgiY2hhaW46ICIrc3RyKGUpKyJcbiIpCgogICAgICAgICAgICBpZiBva19yZXN0b3JlZDoKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICBpZiBDdXJ2ZVN0b3JlKENGR1siQ1VSVkVfRElSIl0gaWYgIkNVUlZFX0RJUiIgaW4gQ0ZHIGVsc2UgQ0ZHLmdldCgiTUVNT1JZX0RJUiIsIi4vY3VydmVzIikpLmluZGV4Lm50b3RhbCA+IDA6CiAgICAgICAgICAgICAgICAgICAgICAgIF9tYXJrX29mZmxpbmVfcmVhZHkoKQogICAgICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgICAgICBwYXNzCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICBvcGVuKG9zLnBhdGguam9pbihDRkdbIlNUQVRFX0RJUiJdLCAicmVzdG9yZV9lcnJvci5sb2ciKSwgImEiLCBlbmNvZGluZz0idXRmLTgiKS53cml0ZShmInJlc3RvcmU6IHtlfVxuIikKCiAgICAgICAgbmVlZF9pbmdlc3Q9RmFsc2UKICAgICAgICBpZiBDRkdbIkZPUkNFX1JFSU5HRVNUIl06IG5lZWRfaW5nZXN0PVRydWUKICAgICAgICBlbHNlOgogICAgICAgICAgICBpZiBub3QgX2N1cnZlc19yZWFkeShDRkdbIkNVUlZFX0RJUiJdKSBhbmQgQ0ZHWyJCT09UU1RSQVBfSU5HRVNUIl06IG5lZWRfaW5nZXN0PVRydWUKICAgICAgICBpZiBuZWVkX2luZ2VzdDoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgc3Jjcz1bcy5zdHJpcCgpIGZvciBzIGluIChDRkdbIklOR0VTVF9TT1VSQ0VTIl0gb3IgIiIpLnNwbGl0KCIsIikgaWYgcy5zdHJpcCgpXSBvciBERUZBVUxUX1NPVVJDRVMKICAgICAgICAgICAgICAgIGluZ2VzdF9hbGwoQ0ZHWyJDVVJWRV9ESVIiXSwgc3Jjcywgc2NvcGU9ImdlbmVyYWwiKQogICAgICAgICAgICAgICAgaWYgQ3VydmVTdG9yZShDRkdbIkNVUlZFX0RJUiJdKS5pbmRleC5udG90YWw+MDogX21hcmtfb2ZmbGluZV9yZWFkeSgpCiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIG9wZW4ob3MucGF0aC5qb2luKENGR1siQ1VSVkVfRElSIl0sImluZ2VzdF9lcnJvci5sb2ciKSwiYSIsZW5jb2Rpbmc9InV0Zi04Iikud3JpdGUoc3RyKGUpKyJcbiIpCgogICAgICAgIHNlbGYuc3RvcmU9Q3VydmVTdG9yZShDRkdbIkNVUlZFX0RJUiJdKTsgc2VsZi5saWJyYXJpYW49TGlicmFyaWFuQ3VydmUoc2VsZi5zdG9yZSkKICAgICAgICBzZWxmLmNvbXBpbGVyPVByb21wdENvbXBpbGVyKCk7IHNlbGYuZW5naW5lPUVuZ2luZUN1cnZlKCkKICAgICAgICBpZiBub3QgbW9kZWxfaWQ6CiAgICAgICAgICAgIG1vZGVsX2lkLCBpbmZvID0gcGlja19tb2RlbCgpIGlmIENGR1siTExNX0FVVE9TSVpFIl0gZWxzZSAoQ0FORElEQVRFU1swXVswXSwgeyJkZXZpY2UiOiJjcHUifSkKICAgICAgICAgICAgZGV2aWNlID0gaW5mby5nZXQoImRldmljZSIsImNwdSIpCiAgICAgICAgc2VsZi5tb2RlbF9pZD1tb2RlbF9pZCBvciBDRkdbIk1PREVMX09WRVJSSURFIl0gb3IgQ0FORElEQVRFU1swXVswXQogICAgICAgIHRydXN0PVRydWU7IGt3YXJncz17fQogICAgICAgIGlmIHRvcmNoIGFuZCB0b3JjaC5jdWRhLmlzX2F2YWlsYWJsZSgpIGFuZCBkZXZpY2U9PSJjdWRhIjoKICAgICAgICAgICAga3dhcmdzLnVwZGF0ZShkaWN0KHRvcmNoX2R0eXBlPXRvcmNoLmZsb2F0MTYsIGRldmljZV9tYXA9ImF1dG8iKSkKICAgICAgICAKICAgICAgICAjIC0tLSBNb2RlbCAvIFRva2VuaXplciBpbml0aWFsaXphdGlvbiAoc3VwcG9ydHMgbG9jYWwgdHJhbnNmb3JtZXJzIG9yIEh1Z2dpbmcgRmFjZSBJbmZlcmVuY2UgQVBJKSAtLS0KICAgICAgICB1c2VfcmVtb3RlID0gc3RyKG9zLmdldGVudigiSElWRV9VU0VfSEZfSU5GRVJFTkNFIiwiMCIpKS5zdHJpcCgpIGluICgiMSIsInRydWUiLCJ5ZXMiKQogICAgICAgIGlmIHVzZV9yZW1vdGU6CiAgICAgICAgICAgICMgUmVtb3RlIHBhdGggdXNpbmcgaHVnZ2luZ2ZhY2VfaHViLkluZmVyZW5jZUNsaWVudAogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBmcm9tIGh1Z2dpbmdmYWNlX2h1YiBpbXBvcnQgSW5mZXJlbmNlQ2xpZW50CiAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgICAgIHJhaXNlIFJ1bnRpbWVFcnJvcihmIkhJVkVfVVNFX0hGX0lORkVSRU5DRT0xIGJ1dCBodWdnaW5nZmFjZV9odWIgaXMgbWlzc2luZzoge2V9IikKICAgICAgICAgICAgZW5kcG9pbnQgPSBvcy5nZXRlbnYoIkhJVkVfSEZfRU5EUE9JTlQiLCIiKS5zdHJpcCgpIG9yIE5vbmUKICAgICAgICAgICAgdG9rZW4gPSBvcy5nZXRlbnYoIkhGX1RPS0VOIikgb3Igb3MuZ2V0ZW52KCJIVUdHSU5HX0ZBQ0VfSFVCX1RPS0VOIikgb3IgTm9uZQogICAgICAgICAgICBzZWxmLmNsaWVudCA9IEluZmVyZW5jZUNsaWVudChtb2RlbD1zZWxmLm1vZGVsX2lkIGlmIGVuZHBvaW50IGlzIE5vbmUgZWxzZSBOb25lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b2tlbj10b2tlbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZW91dD0gaW50KG9zLmdldGVudigiSElWRV9IRl9USU1FT1VUIiwiNjAiKSBvciAiNjAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFzZV91cmw9ZW5kcG9pbnQpCiAgICAgICAgICAgICMgZGVmaW5lIGEgdGhpbiB3cmFwcGVyIHNvIGRvd25zdHJlYW0gY29kZSBjYW4gY2FsbCBzZWxmLnBpcGUocHJvbXB0LCAqKmdlbl9rd2FyZ3MpCiAgICAgICAgICAgIGRlZiBfcmVtb3RlX3BpcGUocHJvbXB0LCBtYXhfbmV3X3Rva2Vucz0yNTYsIGRvX3NhbXBsZT1UcnVlLCB0ZW1wZXJhdHVyZT0wLjcsICoqa3cpOgogICAgICAgICAgICAgICAgIyBTb21lIGVuZHBvaW50cyByZXF1aXJlIGEgc3RvcCBzZXF1ZW5jZTsgZmFsbCBiYWNrIHRvICJBc3Npc3RhbnQ6IiB3aGljaCBvdXIgY29tcGlsZXIgdXNlcy4KICAgICAgICAgICAgICAgIHN0b3AgPSBrdy5nZXQoInN0b3Bfc2VxdWVuY2VzIikgb3IgWyI8L3M+IiwgIkFzc2lzdGFudDoiXQogICAgICAgICAgICAgICAgcmVzcCA9IHNlbGYuY2xpZW50LnRleHRfZ2VuZXJhdGlvbigKICAgICAgICAgICAgICAgICAgICBwcm9tcHQsCiAgICAgICAgICAgICAgICAgICAgbWF4X25ld190b2tlbnM9aW50KG1heF9uZXdfdG9rZW5zKSwKICAgICAgICAgICAgICAgICAgICB0ZW1wZXJhdHVyZT1mbG9hdCh0ZW1wZXJhdHVyZSksCiAgICAgICAgICAgICAgICAgICAgZG9fc2FtcGxlPWJvb2woZG9fc2FtcGxlKSwKICAgICAgICAgICAgICAgICAgICBzdG9wX3NlcXVlbmNlcz1zdG9wLAogICAgICAgICAgICAgICAgICAgIHN0cmVhbT1GYWxzZSwKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIHJldHVybiBbeyJnZW5lcmF0ZWRfdGV4dCI6IHJlc3B9XQogICAgICAgICAgICBzZWxmLnBpcGUgPSBfcmVtb3RlX3BpcGUKICAgICAgICAgICAgc2VsZi50b2sgPSBOb25lCiAgICAgICAgICAgIHNlbGYubW9kZWwgPSBOb25lCiAgICAgICAgZWxzZToKICAgICAgICAgICAgIyBMb2NhbCBwYXRoIHVzaW5nIHRyYW5zZm9ybWVycwogICAgICAgICAgICBzZWxmLnRvayA9IEF1dG9Ub2tlbml6ZXIuZnJvbV9wcmV0cmFpbmVkKHNlbGYubW9kZWxfaWQsIHRydXN0X3JlbW90ZV9jb2RlPXRydXN0KQogICAgICAgICAgICAjIFByZWZlciBoYWxmIHByZWNpc2lvbiBvbiBHUFUKICAgICAgICAgICAgc2VsZi5tb2RlbCA9IEF1dG9Nb2RlbEZvckNhdXNhbExNLmZyb21fcHJldHJhaW5lZChzZWxmLm1vZGVsX2lkLCB0cnVzdF9yZW1vdGVfY29kZT10cnVzdCwgKiprd2FyZ3MpCiAgICAgICAgICAgIHNlbGYucGlwZSA9IHBpcGVsaW5lKAogICAgICAgICAgICAgICAgInRleHQtZ2VuZXJhdGlvbiIsCiAgICAgICAgICAgICAgICBtb2RlbD1zZWxmLm1vZGVsLAogICAgICAgICAgICAgICAgdG9rZW5pemVyPXNlbGYudG9rLAogICAgICAgICAgICAgICAgZGV2aWNlPTAgaWYgKHRvcmNoIGFuZCB0b3JjaC5jdWRhLmlzX2F2YWlsYWJsZSgpIGFuZCBkZXZpY2U9PSJjdWRhIikgZWxzZSAtMQogICAgICAgICAgICApCiAgICAgICAgc2VsZi5vdmVybGF5PVJ1bnRpbWVPdmVybGF5KCkKICAgICAgICBzZWxmLnJldHJpZXZhbF9rPTY7IHNlbGYuZGVjb2RpbmdfdGVtcGVyYXR1cmU9MC43OyBzZWxmLndlYl90aHJlc2hvbGQ9MC40MAogICAgICAgIHNlbGYub3ZlcmxheS5hcHBseV90byhzZWxmKQoKICAgICAgICBzZWxmLnN0YXRlX3BhdGg9b3MucGF0aC5qb2luKENGR1siU1RBVEVfRElSIl0sImxhc3Rfc3RhdGUuanNvbiIpCiAgICAgICAgaWYgbm90IG9zLnBhdGguZXhpc3RzKHNlbGYuc3RhdGVfcGF0aCk6IF9zYXZlX2pzb24oc2VsZi5zdGF0ZV9wYXRoLCB7Im9rIjpUcnVlLCJ0cyI6dGltZS50aW1lKCl9KQoKICAgICAgICAjIFByZWZlcnJlZCBtb2RlbCAocmVjb3JkIGV4aXN0cykKICAgICAgICB0cnk6CiAgICAgICAgICAgIHByZWY9anNvbi5sb2FkKG9wZW4ob3MucGF0aC5qb2luKE9QVF9ESVIsInByZWZlcnJlZF9tb2RlbC5qc29uIiksInIiLGVuY29kaW5nPSJ1dGYtOCIpKQogICAgICAgICAgICBpZiBpc2luc3RhbmNlKHByZWYsZGljdCkgYW5kIHByZWYuZ2V0KCJtb2RlbF9pZCIpIGluIENGR1siT1BUX01PREVMX0FMTE9XTElTVCJdOgogICAgICAgICAgICAgICAgcGFzcwogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgIHBhc3MKCiAgICAgICAgc2VsZi5jaGFuZ2VzPUNoYW5nZU1hbmFnZXIoSGl2ZSkKCiAgICAgICAgdHJ5OgogICAgICAgICAgICBzZWxmLnNlbGZvcHQ9U2VsZk9wdGltaXplcihzZWxmKTsgc2VsZi5zZWxmb3B0LnN0YXJ0KCkKICAgICAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICAgICBwYXNzCgogICAgZGVmIHN1bW1hcml6ZV9mb3JfbWVtb3J5KHNlbGYsIHRleHQ6c3RyLCBtYXhfbmV3X3Rva2VuczppbnQ9MTYwKS0+c3RyOgogICAgICAgIHByb21wdD0oIkNvbmRlbnNlIHRoZSBmb2xsb3dpbmcgY29udGVudCBpbnRvIDTigJM2IGJ1bGxldCBwb2ludHMgd2l0aCBuYW1lcywgZGF0ZXMsIG51bWJlcnMsIGFuZCBhIG9uZS1saW5lIHRha2Vhd2F5LiBLZWVwIGl0IGZhY3R1YWwuXG5cbiIKICAgICAgICAgICAgICAgIGYie3RleHRbOjMwMDBdfVxuXG5TdW1tYXJ5OiIpCiAgICAgICAgb3V0PXNlbGYucGlwZShwcm9tcHQsIG1heF9uZXdfdG9rZW5zPW1heF9uZXdfdG9rZW5zLCBkb19zYW1wbGU9RmFsc2UsIHRlbXBlcmF0dXJlPTAuMDEpCiAgICAgICAgcmV0dXJuIG91dFswXVsiZ2VuZXJhdGVkX3RleHQiXS5zcGxpdCgiU3VtbWFyeToiLDEpWy0xXS5zdHJpcCgpCgogICAgZGVmIGFkZF9jdXJ2ZShzZWxmLCB0ZXh0OnN0ciwgbWV0YTpEaWN0LCBzY29wZTpzdHI9ImdlbmVyYWwiKToKICAgICAgICBzZWxmLmxpYnJhcmlhbi5pbmdlc3RfcGFpcnMoW3RleHRdLFttZXRhXSxzY29wZSkKCiAgICBkZWYgb25saW5lX3VwZGF0ZShzZWxmLCBxdWVyeV9oaW50OiBPcHRpb25hbFtzdHJdPU5vbmUpLT5EaWN0OgogICAgICAgIGlmIG5vdCBDRkdbIk9OTElORV9FTkFCTEUiXTogcmV0dXJuIHsib2siOkZhbHNlLCJyZWFzb24iOiJvbmxpbmUgZGlzYWJsZWQifQogICAgICAgIGlmIG5vdCBvbmxpbmVfYXZhaWxhYmxlKENGR1siT05MSU5FX1RJTUVPVVQiXSk6IHJldHVybiB7Im9rIjpGYWxzZSwicmVhc29uIjoib2ZmbGluZSJ9CiAgICAgICAgc2Vlbj1fbG9hZF9qc29uKE9OTElORV9EQiwge30pCiAgICAgICAgdXJscz1bdS5zdHJpcCgpIGZvciB1IGluIChDRkdbIk9OTElORV9TT1VSQ0VTIl0gb3IgIiIpLnNwbGl0KCIsIikgaWYgdS5zdHJpcCgpXQogICAgICAgIGl0ZW1zPWZldGNoX3Jzcyh1cmxzLCB0aW1lb3V0PUNGR1siT05MSU5FX1RJTUVPVVQiXSwgbGltaXQ9MzApCiAgICAgICAgYWRkZWQ9MAogICAgICAgIGZvciBpdCBpbiBpdGVtczoKICAgICAgICAgICAga2V5PWhhc2hsaWIuc2hhMSgoKGl0LmdldCgibGluayIpIG9yICIiKSsoaXQuZ2V0KCJ0aXRsZSIpIG9yICIiKSkuZW5jb2RlKCJ1dGYtOCIsImlnbm9yZSIpKS5oZXhkaWdlc3QoKQogICAgICAgICAgICBpZiBrZXkgaW4gc2VlbjogY29udGludWUKICAgICAgICAgICAgYmFzZT0oaXQuZ2V0KCJ0aXRsZSIsIiIpKyJcblxuIitpdC5nZXQoInN1bW1hcnkiLCIiKSkuc3RyaXAoKQogICAgICAgICAgICBzdW1tPXNlbGYuc3VtbWFyaXplX2Zvcl9tZW1vcnkoYmFzZSkKICAgICAgICAgICAgc2VsZi5hZGRfY3VydmUoc3VtbSwgeyJkYXRhc2V0Ijoib25saW5lX3JzcyIsInVybCI6aXQuZ2V0KCJsaW5rIiksInRpdGxlIjppdC5nZXQoInRpdGxlIiksInB1Ymxpc2hlZCI6aXQuZ2V0KCJwdWJsaXNoZWQiKX0sIHNjb3BlPSJnZW5lcmFsIikKICAgICAgICAgICAgc2VlbltrZXldPWludCh0aW1lLnRpbWUoKSk7IGFkZGVkKz0xCiAgICAgICAgX3NhdmVfanNvbihPTkxJTkVfREIsIHNlZW4pOyByZXR1cm4geyJvayI6VHJ1ZSwiYWRkZWQiOmFkZGVkfQoKICAgIGRlZiB3ZWJfdXBkYXRlX2FuZF9zdG9yZShzZWxmLCBxdWVyeTpzdHIsIG1heF9kb2NzOmludCwgdGltZW91dDppbnQpLT5pbnQ6CiAgICAgICAgaWYgbm90IChDRkdbIk9OTElORV9FTkFCTEUiXSBhbmQgb25saW5lX2F2YWlsYWJsZSh0aW1lb3V0KSk6IHJldHVybiAwCiAgICAgICAgaGl0cz13ZWJfc2VhcmNoX3NuaXBwZXRzKHF1ZXJ5LCBtYXhfcmVzdWx0cz1tYXhfZG9jcywgdGltZW91dD10aW1lb3V0KTsgYWRkZWQ9MAogICAgICAgIGZvciBoIGluIGhpdHM6CiAgICAgICAgICAgIGJvZHk9KGguZ2V0KCJ0aXRsZSIsIiIpKyJcblxuIitoLmdldCgiYm9keSIsIiIpKS5zdHJpcCgpCiAgICAgICAgICAgIGlmIG5vdCBib2R5OiBjb250aW51ZQogICAgICAgICAgICBzdW1tPXNlbGYuc3VtbWFyaXplX2Zvcl9tZW1vcnkoYm9keSkKICAgICAgICAgICAgbWV0YT17ImRhdGFzZXQiOiJ3ZWJfdXBkYXRlIiwic291cmNlIjpoLmdldCgiaHJlZiIsIiIpLCJ0aXRsZSI6aC5nZXQoInRpdGxlIiwiIiksInRzIjp0aW1lLnRpbWUoKX0KICAgICAgICAgICAgc2VsZi5hZGRfY3VydmUoc3VtbSwgbWV0YSwgc2NvcGU9ImdlbmVyYWwiKTsgYWRkZWQrPTEKICAgICAgICByZXR1cm4gYWRkZWQKCiAgICBkZWYgY2hhdChzZWxmLCBtZXNzYWdlOnN0ciwgZWZmZWN0aXZlX3JvbGU6c3RyLCBjYWxsZXJfaWQ6IE9wdGlvbmFsW3N0cl0sCiAgICAgICAgICAgICBrOmludD1Ob25lLCBtYXhfbmV3X3Rva2VuczppbnQ9MjU2LCB0ZW1wZXJhdHVyZTpmbG9hdD1Ob25lKS0+c3RyOgogICAgICAgIG9ubGluZV9ub3c9TkVULm9ubGluZV9xdWljaygpCiAgICAgICAgaWYgbm90IG9ubGluZV9ub3c6IE5FVC5raWNrX2FzeW5jKCkKICAgICAgICBrayA9IGsgaWYgayBpcyBub3QgTm9uZSBlbHNlIHNlbGYucmV0cmlldmFsX2sKICAgICAgICB0ZW1wID0gdGVtcGVyYXR1cmUgaWYgdGVtcGVyYXR1cmUgaXMgbm90IE5vbmUgZWxzZSBzZWxmLmRlY29kaW5nX3RlbXBlcmF0dXJlCiAgICAgICAgc25pcHBldHMsIHNjb3JlcyA9IHNlbGYubGlicmFyaWFuLnJldHJpZXZlX3Njb3BlZF93aXRoX3Njb3JlcyhtZXNzYWdlLCBlZmZlY3RpdmVfcm9sZSwgY2FsbGVyX2lkLCBrPWtrKQogICAgICAgIGNvdj1jb3ZlcmFnZV9zY29yZV9mcm9tX3NuaXBwZXRzKHNuaXBwZXRzLCBzY29yZXMpCiAgICAgICAgU0hPVUxEX1RSWV9XRUI9KENGR1siT05MSU5FX1RSSUdHRVIiXS5sb3dlcigpPT0iYXV0byIpIGFuZCBDRkdbIk9OTElORV9FTkFCTEUiXSBhbmQgb25saW5lX25vdwogICAgICAgIGlmIGNvdiA8IHNlbGYud2ViX3RocmVzaG9sZCBhbmQgU0hPVUxEX1RSWV9XRUI6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHNlbGYud2ViX3VwZGF0ZV9hbmRfc3RvcmUobWVzc2FnZSwgbWF4X2RvY3M9aW50KENGR1siT05MSU5FX01BWF9SRVNVTFRTIl0gb3IgNSksIHRpbWVvdXQ9aW50KENGR1siT05MSU5FX1RJTUVPVVQiXSBvciA4KSkKICAgICAgICAgICAgICAgIHNuaXBwZXRzLCBzY29yZXMgPSBzZWxmLmxpYnJhcmlhbi5yZXRyaWV2ZV9zY29wZWRfd2l0aF9zY29yZXMobWVzc2FnZSwgZWZmZWN0aXZlX3JvbGUsIGNhbGxlcl9pZCwgaz1raykKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICAgICAgICAgIHBhc3MKICAgICAgICBwcm9tcHQ9c2VsZi5jb21waWxlci5jb21waWxlKG1lc3NhZ2UsIHNuaXBwZXRzLCB0b2tlbl9idWRnZXQ9Q0ZHWyJDVFhfVE9LRU5TIl0pCiAgICAgICAgXz1zZWxmLmVuZ2luZS5ydW4obWVzc2FnZSwgc25pcHBldHMpCiAgICAgICAgb3V0PXNlbGYucGlwZShwcm9tcHQsIG1heF9uZXdfdG9rZW5zPW1heF9uZXdfdG9rZW5zLCBkb19zYW1wbGU9VHJ1ZSwgdGVtcGVyYXR1cmU9dGVtcCkKICAgICAgICByZXBseT1vdXRbMF1bImdlbmVyYXRlZF90ZXh0Il0uc3BsaXQoIkFzc2lzdGFudDoiLDEpWy0xXS5zdHJpcCgpCiAgICAgICAgaWYgQ0ZHWyJOT19QUk9GQU5JVFkiXToKICAgICAgICAgICAgcmVwbHk9cmUuc3ViKHIiXGIoZnVja3xzaGl0fGJpdGNofGFzc2hvbGV8Y3VudHxkaWNrfHB1c3N5fG5pZ2dlcnxtb3RoZXJmdWNrZXIpXGIiLCJbY2Vuc29yZWRdIixyZXBseSwgZmxhZ3M9cmUuSSkKICAgICAgICByZXR1cm4gcmVwbHkKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tIE9DUiBoZWxwZXIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpkZWYgb2NyX3RleHRfZnJvbV9pbWFnZV9iZ3IoaW1hZ2VfYmdyKS0+c3RyOgogICAgaWYgbm90IChfSEFWRV9DViBhbmQgX0hBVkVfVEVTUyk6IHJldHVybiAiIgogICAgZ3JheT1jdjIuY3Z0Q29sb3IoaW1hZ2VfYmdyLCBjdjIuQ09MT1JfQkdSMkdSQVkpCiAgICByZXR1cm4gcHl0ZXNzZXJhY3QuaW1hZ2VfdG9fc3RyaW5nKGdyYXkpIG9yICIiCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIFVJIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KSEVMUD1mIiIiCioqQWRtaW4vVXNlciBtb2RlKio6IEFkbWlucyAoZ2VuZXJhbC9zdXBlcikgYW5kIE93bmVyIGxvZyBpbiB3aXRoIHBhc3N3b3JkIChPd25lciBhbHNvIG5lZWRzIHNlY29uZCBmYWN0b3IpLiBBZnRlciBsb2dpbiBjaG9vc2UgQWRtaW4gb3IgVXNlciBtb2RlLiAgCioqT3duZXItb25seSBjb2RlIGVkaXRzKiogYXJlIGVuZm9yY2VkIHZpYSBDaGFuZ2UgTWFuYWdlciBwb2xpY3kuIEhpdmUgY2FuIHNhbmRib3gsIHRlc3QsIGFuZCBwcm9wb3NlOyBjb2RlIHdyaXRlcyByZXF1aXJlIE93bmVyIGFwcHJvdmFsIChgT1BUX0FVVE9fQVBQTFk9MWApIHVubGVzcyBPd25lciBhcHBsaWVzIG1hbnVhbGx5LgoKKipPZmZsaW5lL09ubGluZSoqOiBXb3JrcyBmdWxseSBvZmZsaW5lIGZyb20gY3VydmVzLiBJZiBvbmxpbmUgYW5kIGVuYWJsZWQsIGZldGNoZXMgUlNTL3dlYiBzbmlwcGV0cyDihpIgc3VtbWFyaXplcyBsb2NhbGx5IOKGkiBzYXZlcyB0byBjdXJ2ZXMgKHBlcnNpc3RzIG9mZmxpbmUpLiAgCioqVm9pY2UqKjogRmFzdGVyLVdoaXNwZXIgQVNSIChhdXRvIGxhbmd1YWdlKSwgUGlwZXIgVFRTIG1peGVkLWxhbmd1YWdlLCBwaG9uaWNzIGhpbnRzIChFbmdsaXNoKS4gIAoqKlByaXZhY3kqKjogU2Vuc2l0aXZlL2ZpcnN0LXBlcnNvbiBpbnB1dHMgcm91dGUgdG8gdXNlci1wcml2YXRlIGxpYnJhcnk7IG5ldXRyYWwgaW5mbyB0byBnZW5lcmFsLiAgCiIiIgoKZGVmIGxhdW5jaF91aSgpOgogICAgaGl2ZT1IaXZlKCk7IHN0b3JlPUN1cnZlU3RvcmUoQ0ZHWyJDVVJWRV9ESVIiXSk7IGxpYj1MaWJyYXJpYW5DdXJ2ZShzdG9yZSkKCiAgICB3aXRoIGdyLkJsb2Nrcyh0aXRsZT0iSGl2ZSDigJQgRnVsbCBNZXJnZWQgT3B0aW1pemVkIikgYXMgZGVtbzoKICAgICAgICBnci5NYXJrZG93bihmIiMjIHtDRkdbJ0FHRU5UX05BTUUnXX0g4oCUIEZ1bGwgTWVyZ2VkLCBPZmZsaW5lLWZpcnN0ICsgT25saW5lIHVwZGF0ZXMgKyBJbnRlcm5hbCBPcHRpbWl6YXRpb24iKQoKICAgICAgICB3aXRoIGdyLlJvdygpOgogICAgICAgICAgICBsb2dpbl9uYW1lPWdyLlRleHRib3gobGFiZWw9Ik5hbWUgb3IgSUQiKQogICAgICAgICAgICBsb2dpbl9wYXNzPWdyLlRleHRib3gobGFiZWw9IlBhc3N3b3JkIChhZG1pbnMgb25seSkiLCB0eXBlPSJwYXNzd29yZCIpCiAgICAgICAgICAgIGxvZ2luX3NlY29uZD1nci5UZXh0Ym94KGxhYmVsPSJTZWNvbmQgKG93bmVyIG9ubHkpIiwgdHlwZT0icGFzc3dvcmQiKQogICAgICAgICAgICBsb2dpbl9idG49Z3IuQnV0dG9uKCJMb2dpbiIpCiAgICAgICAgbG9naW5fc3RhdHVzPWdyLk1hcmtkb3duKCkKICAgICAgICB1aWRfc3RhdGU9Z3IuU3RhdGUoTm9uZSk7IHJvbGVfc3RhdGU9Z3IuU3RhdGUoImd1ZXN0Iik7IG1vZGVfc3RhdGU9Z3IuU3RhdGUoInVzZXIiKTsgcGhvbmljc19zdGF0ZT1nci5TdGF0ZShGYWxzZSkKCiAgICAgICAgZGVmIGRvX2xvZ2luKG5tLHB3LHNlYyk6CiAgICAgICAgICAgIG9rLCBpbmZvPWF0dGVtcHRfbG9naW4obm0gb3IgIiIsIHB3IG9yICIiLCBzZWMgb3IgTm9uZSkKICAgICAgICAgICAgZD1fbG9hZF91c2VycygpOyB1LF89X2ZpbmRfdXNlcihkLCBubSBvciAiIikKICAgICAgICAgICAgcm9sZT11WyJyb2xlIl0gaWYgdSBlbHNlICJndWVzdCIKICAgICAgICAgICAgcHJvZj1fbG9hZF9qc29uKEFEQVBUX0RCLHt9KS5nZXQodVsiaWQiXSBpZiB1IGVsc2UgImd1ZXN0Iix7fSk7IHBob25fb249Ym9vbChwcm9mLmdldCgicGhvbmljc19vbiIsRmFsc2UpKQogICAgICAgICAgICByZXR1cm4gaW5mbywodVsiaWQiXSBpZiB1IGVsc2UgTm9uZSkscm9sZSwidXNlciIscGhvbl9vbgogICAgICAgIGxvZ2luX2J0bi5jbGljayhkb19sb2dpbixbbG9naW5fbmFtZSxsb2dpbl9wYXNzLGxvZ2luX3NlY29uZF0sW2xvZ2luX3N0YXR1cywgdWlkX3N0YXRlLCByb2xlX3N0YXRlLCBtb2RlX3N0YXRlLCBwaG9uaWNzX3N0YXRlXSkKCiAgICAgICAgbW9kZV9waWNrZXI9Z3IuUmFkaW8oY2hvaWNlcz1bInVzZXIiLCJhZG1pbiJdLCB2YWx1ZT0idXNlciIsIGxhYmVsPSJNb2RlIChhZG1pbnMvb3duZXIgb25seSkiKQogICAgICAgIGRlZiBzZXRfbW9kZShyb2xlLCBwaWNrKToKICAgICAgICAgICAgaWYgcm9sZSBub3QgaW4gKCJhZG1pbl9nZW5lcmFsIiwiYWRtaW5fc3VwZXIiLCJvd25lciIpOiByZXR1cm4gInVzZXIiCiAgICAgICAgICAgIHJldHVybiBwaWNrCiAgICAgICAgbW9kZV9waWNrZXIuY2hhbmdlKHNldF9tb2RlLCBbcm9sZV9zdGF0ZSwgbW9kZV9waWNrZXJdLCBbbW9kZV9zdGF0ZV0pCgogICAgICAgIHdpdGggZ3IuVGFiKCJDaGF0Iik6CiAgICAgICAgICAgIGNoYXQ9Z3IuQ2hhdGJvdChoZWlnaHQ9NDIwKQogICAgICAgICAgICBtc2c9Z3IuVGV4dGJveChwbGFjZWhvbGRlcj1mIlRhbGsgdG8ge0NGR1snQUdFTlRfTkFNRSddfSIpCiAgICAgICAgICAgIGRlZiB0YWxrKG0sIHVpZCwgcm9sZSwgbW9kZSwgaGlzdCk6CiAgICAgICAgICAgICAgICBlZmYgPSByb2xlIGlmIG1vZGU9PSJhZG1pbiIgZWxzZSAidXNlciIKICAgICAgICAgICAgICAgIHJlcGx5PWhpdmUuY2hhdChtIG9yICIiLCBlZmYsIGNhbGxlcl9pZD11aWQpCiAgICAgICAgICAgICAgICAjIHByaXZhY3kgcm91dGluZwogICAgICAgICAgICAgICAgcGVyc29uYWwgPSBGYWxzZQogICAgICAgICAgICAgICAgaWYgcmUuc2VhcmNoKHIiXGIobXl8bWluZXxtZXxJfG91cnx3ZSlcYiIsIChtIG9yICIiKSwgcmUuSSkgYW5kIHJlLnNlYXJjaChyIlxiKHBhc3N3b3JkfGFkZHJlc3N8ZW1haWx8cGhvbmV8c3NufHNjaG9vbHxraWR8bWVkaWNhbHxiYW5rfGNhcmR8cGFzc3BvcnQpXGIiLCAobSBvciAiIiksIHJlLkkpOgogICAgICAgICAgICAgICAgICAgIHBlcnNvbmFsID0gVHJ1ZQogICAgICAgICAgICAgICAgc2NvcGUgPSBmInVzZXI6e3VpZH0iIGlmICh1aWQgYW5kIHBlcnNvbmFsKSBlbHNlICJnZW5lcmFsIgogICAgICAgICAgICAgICAgbGliLmluZ2VzdF9wYWlycyhbbV0sW3siZGF0YXNldCI6ImNoYXQifV0sIHNjb3BlPXNjb3BlKQogICAgICAgICAgICAgICAgcmV0dXJuIGhpc3QrW1ttLCByZXBseV1dLCAiIgogICAgICAgICAgICBtc2cuc3VibWl0KHRhbGssW21zZyx1aWRfc3RhdGUscm9sZV9zdGF0ZSxtb2RlX3N0YXRlLGNoYXRdLFtjaGF0LG1zZ10pCgogICAgICAgIHdpdGggZ3IuVGFiKCJWb2ljZSIpOgogICAgICAgICAgICBnci5NYXJrZG93bigiIyMjIFZvaWNlIGxvZ2luIC8gQVNSIC8gTWl4ZWQtbGFuZ3VhZ2UgVFRTIC8gUGhvbmljcyIpCiAgICAgICAgICAgIG1pYz1nci5BdWRpbyhzb3VyY2VzPVsibWljcm9waG9uZSJdLCB0eXBlPSJmaWxlcGF0aCIsIGxhYmVsPSJTcGVhayAoNeKAkzEwcykiKQogICAgICAgICAgICBhc3JfbGFuZz1nci5Ecm9wZG93bihjaG9pY2VzPVsiYXV0byIsImVuIiwiZXMiLCJmciIsImRlIiwiemgiXSwgdmFsdWU9ImF1dG8iLCBsYWJlbD0iQVNSIGxhbmd1YWdlIChmb3JjZSBvciBhdXRvKSIpCiAgICAgICAgICAgIHBob25pY3NfdG9nZ2xlPWdyLkNoZWNrYm94KHZhbHVlPUZhbHNlLCBsYWJlbD0iRW5hYmxlIFBob25pY3MgYXNzaXN0IChFbmdsaXNoIHByb251bmNpYXRpb24gaGVscCkiKQogICAgICAgICAgICB0cmFuc2NyaWJlX2J0bj1nci5CdXR0b24oIlRyYW5zY3JpYmUiKQogICAgICAgICAgICB0cmFuc2NyaXB0PWdyLlRleHRib3gobGFiZWw9IlRyYW5zY3JpcHQiKQogICAgICAgICAgICB3aG9fYnRuPWdyLkJ1dHRvbigiTG9naW4gYnkgVm9pY2UgKHVzZXJzIG9ubHkpIikKICAgICAgICAgICAgd2hvX3N0YXR1cz1nci5NYXJrZG93bigpCiAgICAgICAgICAgIHJlcGx5X2J0bj1nci5CdXR0b24oIlJlcGx5ICsgU3BlYWsiKQogICAgICAgICAgICByZXBseV90ZXh0PWdyLlRleHRib3gobGFiZWw9IkFzc2lzdGFudCBSZXBseSIpCiAgICAgICAgICAgIHJlcGx5X2F1ZGlvPWdyLkF1ZGlvKHR5cGU9ImZpbGVwYXRoIiwgbGFiZWw9IkFzc2lzdGFudCBWb2ljZSIpCgogICAgICAgICAgICBkZWYgZG9fdHJhbnNjcmliZShwYXRoLCBhc3JfbGcsIHVpZCk6CiAgICAgICAgICAgICAgICBpZiBub3QgcGF0aDogcmV0dXJuICIiCiAgICAgICAgICAgICAgICB0ZXh0PWFzcl90cmFuc2NyaWJlKHBhdGgsIHVpZCwgTm9uZSBpZiBhc3JfbGc9PSJhdXRvIiBlbHNlIGFzcl9sZykKICAgICAgICAgICAgICAgIHByb2Y9X2xvYWRfanNvbihBREFQVF9EQix7fSk7IHA9cHJvZi5nZXQodWlkIG9yICJndWVzdCIse30pCiAgICAgICAgICAgICAgICBkdXI9bGlicm9zYS5nZXRfZHVyYXRpb24oZmlsZW5hbWU9cGF0aCkgb3IgMC4wMDEKICAgICAgICAgICAgICAgIHN5bD1sZW4ocmUuZmluZGFsbChyIlthZWlvdXlBRUlPVVldKyIsIHRleHQpKTsgcmF0ZT0oc3lsL2R1cikKICAgICAgICAgICAgICAgIHBbInJhdGUiXT0wLjgqcC5nZXQoInJhdGUiLCByYXRlKSswLjIqcmF0ZQogICAgICAgICAgICAgICAgY2h1bmtzPWxpZF9jaHVuayh0ZXh0KTsgZW5fbGVuPXN1bShsZW4oYykgZm9yIGMsbCBpbiBjaHVua3MgaWYgbC5zdGFydHN3aXRoKCJlbiIpKTsgYWxsX2xlbj1zdW0obGVuKGMpIGZvciBjLGwgaW4gY2h1bmtzKQogICAgICAgICAgICAgICAgaWYgYWxsX2xlbj4wOgogICAgICAgICAgICAgICAgICAgIHJhdGlvPWVuX2xlbi9hbGxfbGVuOyBwWyJjb2Rlc3dpdGNoX2VuIl09MC44KnAuZ2V0KCJjb2Rlc3dpdGNoX2VuIixyYXRpbykrMC4yKnJhdGlvCiAgICAgICAgICAgICAgICBwcm9mW3VpZCBvciAiZ3Vlc3QiXT1wOyBfc2F2ZV9qc29uKEFEQVBUX0RCLHByb2YpCiAgICAgICAgICAgICAgICBzY29wZT0idXNlcjoiK3VpZCBpZiB1aWQgYW5kICgibXkgIiBpbiB0ZXh0Lmxvd2VyKCkgb3IgIkkgIiBpbiB0ZXh0KSBlbHNlICJnZW5lcmFsIgogICAgICAgICAgICAgICAgbGliLmluZ2VzdF9wYWlycyhbdGV4dF0sW3siZGF0YXNldCI6InZvaWNlX2FzciJ9XSwgc2NvcGU9c2NvcGUpCiAgICAgICAgICAgICAgICByZXR1cm4gdGV4dAogICAgICAgICAgICB0cmFuc2NyaWJlX2J0bi5jbGljayhkb190cmFuc2NyaWJlLFttaWMsYXNyX2xhbmcsdWlkX3N0YXRlXSxbdHJhbnNjcmlwdF0pCgogICAgICAgICAgICBkZWYgZG9fbG9naW5fdm9pY2UocGF0aCk6CiAgICAgICAgICAgICAgICBpZiBub3QgcGF0aDogcmV0dXJuICJObyBhdWRpby4iLCBOb25lLCAiZ3Vlc3QiLCAidXNlciIKICAgICAgICAgICAgICAgIHVpZHY9aWRlbnRpZnlfdm9pY2UocGF0aCkKICAgICAgICAgICAgICAgIGlmIG5vdCB1aWR2OiByZXR1cm4gIlZvaWNlIG5vdCByZWNvZ25pemVkLiBZb3UgY2FuIGVucm9sbCBhcyBhIG5ldyB1c2VyLiIsIE5vbmUsICJndWVzdCIsICJ1c2VyIgogICAgICAgICAgICAgICAgZD1fbG9hZF91c2VycygpCiAgICAgICAgICAgICAgICBmb3IgZ3JwIGluIFsidXNlcnMiLCJhZG1pbnNfZ2VuZXJhbCIsImFkbWluc19zdXBlciJdOgogICAgICAgICAgICAgICAgICAgIGZvciB1IGluIGQuZ2V0KGdycCxbXSk6CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIHVbImlkIl09PXVpZHY6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiB1WyJyb2xlIl0gaW4gKCJhZG1pbl9nZW5lcmFsIiwiYWRtaW5fc3VwZXIiKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gIkFkbWluIHJvbGVzIHJlcXVpcmUgcGFzc3dvcmQgbG9naW4uIiwgTm9uZSwgImd1ZXN0IiwgInVzZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZiJXZWxjb21lIGJhY2ssIHt1WyduYW1lJ119ICh1c2VyKS4iLCB1aWR2LCAidXNlciIsICJ1c2VyIgogICAgICAgICAgICAgICAgaWYgZFsib3duZXIiXVsiaWQiXT09dWlkdjogcmV0dXJuICJPd25lciBtdXN0IGxvZ2luIHdpdGggcGFzc3dvcmQgKyBzZWNvbmQgZmFjdG9yLiIsIE5vbmUsICJndWVzdCIsICJ1c2VyIgogICAgICAgICAgICAgICAgcmV0dXJuICJNYXRjaGVkIHVua25vd24gaWQ7IHBsZWFzZSBsb2dpbiBtYW51YWxseS4iLCBOb25lLCAiZ3Vlc3QiLCAidXNlciIKICAgICAgICAgICAgd2hvX2J0bi5jbGljayhkb19sb2dpbl92b2ljZSxbbWljXSxbd2hvX3N0YXR1cywgdWlkX3N0YXRlLCByb2xlX3N0YXRlLCBtb2RlX3N0YXRlXSkKCiAgICAgICAgICAgIGRlZiBkb19yZXBseSh1aWQsIHJvbGUsIG1vZGUsIHRleHQsIHBob25fdG9nZ2xlKToKICAgICAgICAgICAgICAgIGlmIG5vdCB0ZXh0OiByZXR1cm4gIiIsIE5vbmUKICAgICAgICAgICAgICAgIGVmZiA9IHJvbGUgaWYgbW9kZT09ImFkbWluIiBlbHNlICJ1c2VyIgogICAgICAgICAgICAgICAgcHJvbXB0PXRleHQKICAgICAgICAgICAgICAgIGlmIHBob25fdG9nZ2xlOgogICAgICAgICAgICAgICAgICAgIG5vdGVzPVtdCiAgICAgICAgICAgICAgICAgICAgZm9yIGNoLCBsZyBpbiBsaWRfY2h1bmsodGV4dCk6CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIGxnLnN0YXJ0c3dpdGgoImVuIik6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3IgdyBpbiByZS5maW5kYWxsKHIiXGJbQS1aYS16XVtBLVphLXpcLSddK1xiIiwgY2gpOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIGxlbih3KT49NiBvciB3WzBdLmlzdXBwZXIoKTogbm90ZXMuYXBwZW5kKGYie3d9OiB7cGhvbmljcyh3KX0iKQogICAgICAgICAgICAgICAgICAgIGlmIG5vdGVzOiBwcm9tcHQgKz0gIlxuXG4oUGhvbmljcylcbiIgKyAiXG4iLmpvaW4oZiItIHtufSIgZm9yIG4gaW4gbm90ZXNbOjEwXSkKICAgICAgICAgICAgICAgICAgICBwcm9mPV9sb2FkX2pzb24oQURBUFRfREIse30pOyBwPXByb2YuZ2V0KHVpZCBvciAiZ3Vlc3QiLHt9KTsgcFsicGhvbmljc19vbiJdPVRydWU7IHByb2ZbdWlkIG9yICJndWVzdCJdPXA7IF9zYXZlX2pzb24oQURBUFRfREIscHJvZikKICAgICAgICAgICAgICAgIGFucz1oaXZlLmNoYXQocHJvbXB0LCBlZmYsIGNhbGxlcl9pZD11aWQpCiAgICAgICAgICAgICAgICB3YXY9c3ludGhlc2l6ZV9tdWx0aWxhbmcoYW5zLCBDRkdbIlRUU19MQU5HIl0pOyByZXR1cm4gYW5zLCB3YXYKICAgICAgICAgICAgcmVwbHlfYnRuLmNsaWNrKGRvX3JlcGx5LFt1aWRfc3RhdGUsIHJvbGVfc3RhdGUsIG1vZGVfc3RhdGUsIHRyYW5zY3JpcHQsIHBob25pY3NfdG9nZ2xlXSxbcmVwbHlfdGV4dCwgcmVwbHlfYXVkaW9dKQoKICAgICAgICAgICAgd2l0aCBnci5BY2NvcmRpb24oIlZvaWNlIGVucm9sbG1lbnQgKGFkZCB5b3VyIHZvaWNlcHJpbnQpIiwgb3Blbj1GYWxzZSk6CiAgICAgICAgICAgICAgICBlbnJvbGxfYXVkaW89Z3IuQXVkaW8oc291cmNlcz1bIm1pY3JvcGhvbmUiXSwgdHlwZT0iZmlsZXBhdGgiLCBsYWJlbD0iUmVjb3JkIDXigJMxMHMiKQogICAgICAgICAgICAgICAgZW5yb2xsX2J0bj1nci5CdXR0b24oIkVucm9sbCB2b2ljZSBmb3IgY3VycmVudCB1c2VyIik7IGVucm9sbF9zdGF0dXM9Z3IuTWFya2Rvd24oKQogICAgICAgICAgICAgICAgZGVmIGRvX2Vucm9sbCh1aWQsIHBhdGgpOgogICAgICAgICAgICAgICAgICAgIGlmIG5vdCB1aWQ6IHJldHVybiAiTG9naW4gb3Igc3BlY2lmeSB1c2VyIGZpcnN0LiIKICAgICAgICAgICAgICAgICAgICBpZiBub3QgcGF0aDogcmV0dXJuICJObyBhdWRpby4iCiAgICAgICAgICAgICAgICAgICAgZW5yb2xsX3ZvaWNlKHVpZCwgcGF0aCk7IHJldHVybiAiVm9pY2UgZW5yb2xsZWQuIgogICAgICAgICAgICAgICAgZW5yb2xsX2J0bi5jbGljayhkb19lbnJvbGwsW3VpZF9zdGF0ZSwgZW5yb2xsX2F1ZGlvXSxbZW5yb2xsX3N0YXR1c10pCgogICAgICAgICAgICB3aXRoIGdyLkFjY29yZGlvbigiTmV3IHVzZXIgYnkgdm9pY2UgKG5vIHBhc3N3b3JkKSIsIG9wZW49RmFsc2UpOgogICAgICAgICAgICAgICAgbnVfYXVkaW89Z3IuQXVkaW8oc291cmNlcz1bIm1pY3JvcGhvbmUiXSwgdHlwZT0iZmlsZXBhdGgiLCBsYWJlbD0iUmVjb3JkIDXigJMxMHMiKQogICAgICAgICAgICAgICAgbnVfbmFtZT1nci5UZXh0Ym94KGxhYmVsPSJZb3VyIG5hbWUiKQogICAgICAgICAgICAgICAgbnVfbGFuZz1nci5Ecm9wZG93bihjaG9pY2VzPVsiZW4iLCJlcyIsImZyIiwiZGUiLCJ6aCJdLCB2YWx1ZT0iZW4iLCBsYWJlbD0iUHJlZmVycmVkIGxhbmd1YWdlIikKICAgICAgICAgICAgICAgIG51X2J0bj1nci5CdXR0b24oIkNyZWF0ZSB1c2VyIGZyb20gbXkgdm9pY2UiKTsgbnVfc3RhdHVzPWdyLk1hcmtkb3duKCkKICAgICAgICAgICAgICAgIGRlZiBkb19uZXdfdXNlcihwYXRoLCBuYW1lLCBsYW5nKToKICAgICAgICAgICAgICAgICAgICBpZiBub3QgcGF0aCBvciBub3QgbmFtZTogcmV0dXJuICJQcm92aWRlIGF1ZGlvIGFuZCBhIG5hbWUuIgogICAgICAgICAgICAgICAgICAgIGQ9X2xvYWRfdXNlcnMoKTsgdWlkPWYidXNlcjp7aW50KHRpbWUudGltZSgpKX0iCiAgICAgICAgICAgICAgICAgICAgZW50cnk9eyJpZCI6dWlkLCJuYW1lIjpuYW1lLCJyb2xlIjoidXNlciIsInBhc3MiOiIiLCJwcmVmcyI6eyJhY3RpdmF0aW9uX25hbWVzIjpbQ0ZHWydBR0VOVF9OQU1FJ11dLCJsYW5ndWFnZSI6bGFuZ319CiAgICAgICAgICAgICAgICAgICAgZFsidXNlcnMiXS5hcHBlbmQoZW50cnkpOyBfc2F2ZV9qc29uKFVTRVJTX0RCLGQpOyBlbnJvbGxfdm9pY2UodWlkLCBwYXRoKQogICAgICAgICAgICAgICAgICAgIHJldHVybiBmIkNyZWF0ZWQgdXNlciB7bmFtZX0gKHt1aWR9KSB3aXRoIGVucm9sbGVkIHZvaWNlLiIKICAgICAgICAgICAgICAgIG51X2J0bi5jbGljayhkb19uZXdfdXNlcixbbnVfYXVkaW8sIG51X25hbWUsIG51X2xhbmddLFtudV9zdGF0dXNdKQoKICAgICAgICB3aXRoIGdyLlRhYigiT25saW5lICYgV2ktRmkiKToKICAgICAgICAgICAgZ3IuTWFya2Rvd24oIiMjIyBBdXRvLWNvbm5lY3QgdG8ga25vd24gV2ktRmkgKG5vbi1ibG9ja2luZykgYW5kIGZldGNoIG9ubGluZSB1cGRhdGVzIikKICAgICAgICAgICAgd2lmaV9zdGF0dXM9Z3IuTWFya2Rvd24oIldpLUZpOiBjaGVja2luZy4uLiIpCiAgICAgICAgICAgIGNvbm5lY3Rfbm93PWdyLkJ1dHRvbigiVHJ5IGF1dG8tY29ubmVjdCBub3cgKG5vbi1ibG9ja2luZykiKQogICAgICAgICAgICBvbmxpbmVfbm93PWdyLkJ1dHRvbigiRmV0Y2ggdXBkYXRlcyBub3ciKTsgb25saW5lX3N0YXR1cz1nci5NYXJrZG93bigpCiAgICAgICAgICAgIGNvbm5lY3Rfbm93LmNsaWNrKGxhbWJkYTogKE5FVC5raWNrX2FzeW5jKCkgb3IgVHJ1ZSkgYW5kICJBdXRvLWNvbm5lY3Qgc3RhcnRlZCBpbiBiYWNrZ3JvdW5kLiIsIFtdLCBbd2lmaV9zdGF0dXNdKQogICAgICAgICAgICBvbmxpbmVfbm93LmNsaWNrKGxhbWJkYTogKCJBZGRlZCAlcyBuZXcgc3VtbWFyaWVzIHRvIGN1cnZlcy4iICUgKEhpdmUoKS5vbmxpbmVfdXBkYXRlKCkuZ2V0KCJhZGRlZCIsMCkpKSwgW10sIFtvbmxpbmVfc3RhdHVzXSkKCiAgICAgICAgd2l0aCBnci5UYWIoIkhlbHAiKTogZ3IuTWFya2Rvd24oSEVMUCkKCiAgICAgICAgIyAtLS0tLS0tLSBBZG1pbiBDb250cm9scyAobm8gc2VwYXJhdGUgdGFiOyB2aXNpYmxlIGluIEFkbWluIG1vZGUpIC0tLS0tLS0tCiAgICAgICAgd2l0aCBnci5BY2NvcmRpb24oIkFkbWluIENvbnRyb2xzIChzd2l0Y2ggdG8gQWRtaW4gbW9kZSB0byBlbmFibGUpIiwgb3Blbj1GYWxzZSwgdmlzaWJsZT1UcnVlKSBhcyBhZG1pbl9jb250cm9sczoKICAgICAgICAgICAgYWRtaW5faW5mbz1nci5NYXJrZG93bigiU3dpdGNoIHRvICoqQWRtaW4gbW9kZSoqIGFib3ZlIHRvIHVzZSB0aGVzZSB0b29scy4iKQogICAgICAgICAgICB0YXJnZXQ9Z3IuVGV4dGJveChsYWJlbD0iVGFyZ2V0IG5hbWUgb3IgaWQiKQogICAgICAgICAgICBuZXdfbmFtZT1nci5UZXh0Ym94KGxhYmVsPSJOZXcgbmFtZSIpCiAgICAgICAgICAgIG5ld19wYXNzPWdyLlRleHRib3gobGFiZWw9Ik5ldyBwYXNzd29yZCIpCiAgICAgICAgICAgIG5ld19yb2xlPWdyLkRyb3Bkb3duKGNob2ljZXM9WyJvd25lciIsImFkbWluX3N1cGVyIiwiYWRtaW5fZ2VuZXJhbCIsInVzZXIiXSwgdmFsdWU9InVzZXIiLCBsYWJlbD0iTmV3IHJvbGUiKQogICAgICAgICAgICBhZGRfbmFtZT1nci5UZXh0Ym94KGxhYmVsPSJBZGQ6IG5hbWUiKQogICAgICAgICAgICBhZGRfcm9sZT1nci5Ecm9wZG93bihjaG9pY2VzPVsiYWRtaW5fc3VwZXIiLCJhZG1pbl9nZW5lcmFsIiwidXNlciJdLCB2YWx1ZT0idXNlciIsIGxhYmVsPSJBZGQgcm9sZSIpCiAgICAgICAgICAgIGFkZF9wYXNzPWdyLlRleHRib3gobGFiZWw9IkFkZCBwYXNzd29yZCAoYWRtaW5zIG9ubHkpIikKICAgICAgICAgICAgYWRkX2J0bj1nci5CdXR0b24oIkFkZCB1c2VyL2FkbWluIikKICAgICAgICAgICAgcmVuYW1lX2J0bj1nci5CdXR0b24oIlJlbmFtZSIpCiAgICAgICAgICAgIHBhc3NfYnRuPWdyLkJ1dHRvbigiQ2hhbmdlIHBhc3N3b3JkIikKICAgICAgICAgICAgcm9sZV9idG49Z3IuQnV0dG9uKCJDaGFuZ2Ugcm9sZSIpCiAgICAgICAgICAgIG91dD1nci5NYXJrZG93bigpCgogICAgICAgICAgICBkZWYgaXNfYWRtaW4obW9kZSwgcm9sZSk6IHJldHVybiAobW9kZT09ImFkbWluIikgYW5kIChyb2xlIGluICgiYWRtaW5fZ2VuZXJhbCIsImFkbWluX3N1cGVyIiwib3duZXIiKSkKCiAgICAgICAgICAgIGRlZiBkb19hZGQobW9kZSwgcm9sZSwgY2FsbGVyLCBubSwgcmwsIHB3KToKICAgICAgICAgICAgICAgIGlmIG5vdCBpc19hZG1pbihtb2RlLCByb2xlKTogcmV0dXJuICJTd2l0Y2ggdG8gQWRtaW4gbW9kZSB0byB1c2UgdGhpcy4iCiAgICAgICAgICAgICAgICBkPV9sb2FkX3VzZXJzKCk7IGN1LF89X2ZpbmRfdXNlcihkLCBjYWxsZXIgb3IgIiIpCiAgICAgICAgICAgICAgICBpZiBub3QgY3U6IHJldHVybiAiTG9naW4gZmlyc3QgYXMgYWRtaW4uIgogICAgICAgICAgICAgICAgaWYgcmwgbm90IGluIFBFUk1TLmdldChjdVsicm9sZSJdLHt9KS5nZXQoImNhbl9hZGQiLFtdKTogcmV0dXJuIGYie2N1Wydyb2xlJ119IGNhbm5vdCBhZGQge3JsfS4iCiAgICAgICAgICAgICAgICB1aWQ9ZiJ7cmx9OntpbnQodGltZS50aW1lKCkpfSIKICAgICAgICAgICAgICAgIGVudHJ5PXsiaWQiOnVpZCwibmFtZSI6bm0sInJvbGUiOnJsLCJwYXNzIjpwdyBpZiBybCE9J3VzZXInIGVsc2UgIiIsICJwcmVmcyI6eyJhY3RpdmF0aW9uX25hbWVzIjpbQ0ZHWyJBR0VOVF9OQU1FIl1dLCJsYW5ndWFnZSI6ImVuIn19CiAgICAgICAgICAgICAgICBpZiBybD09Im93bmVyIjogZFsib3duZXIiXT1lbnRyeQogICAgICAgICAgICAgICAgZWxpZiBybD09ImFkbWluX3N1cGVyIjogZFsiYWRtaW5zX3N1cGVyIl0uYXBwZW5kKGVudHJ5KQogICAgICAgICAgICAgICAgZWxpZiBybD09ImFkbWluX2dlbmVyYWwiOiBkWyJhZG1pbnNfZ2VuZXJhbCJdLmFwcGVuZChlbnRyeSkKICAgICAgICAgICAgICAgIGVsc2U6IGRbInVzZXJzIl0uYXBwZW5kKGVudHJ5KQogICAgICAgICAgICAgICAgX3NhdmVfanNvbihVU0VSU19EQixkKTsgcmV0dXJuIGYiQWRkZWQge3JsfToge25tfSIKICAgICAgICAgICAgYWRkX2J0bi5jbGljayhkb19hZGQsIFttb2RlX3N0YXRlLCByb2xlX3N0YXRlLCB1aWRfc3RhdGUsIGFkZF9uYW1lLCBhZGRfcm9sZSwgYWRkX3Bhc3NdLCBbb3V0XSkKCiAgICAgICAgICAgIGRlZiBkb19yZW5hbWUobW9kZSwgcm9sZSwgY2FsbGVyLCB0Z3QsIG5tKToKICAgICAgICAgICAgICAgIGlmIG5vdCBpc19hZG1pbihtb2RlLCByb2xlKTogcmV0dXJuICJTd2l0Y2ggdG8gQWRtaW4gbW9kZSB0byB1c2UgdGhpcy4iCiAgICAgICAgICAgICAgICBkPV9sb2FkX3VzZXJzKCk7IHUsXz1fZmluZF91c2VyKGQsIHRndCBvciAiIikKICAgICAgICAgICAgICAgIGlmIG5vdCB1OiByZXR1cm4gIlRhcmdldCBub3QgZm91bmQuIgogICAgICAgICAgICAgICAgY3UsXz1fZmluZF91c2VyKGQsIGNhbGxlciBvciAiIikKICAgICAgICAgICAgICAgIGlmIG5vdCBjdTogcmV0dXJuICJMb2dpbiBmaXJzdC4iCiAgICAgICAgICAgICAgICBpZiB1WyJyb2xlIl0gaW4gUEVSTVMuZ2V0KGN1WyJyb2xlIl0se30pLmdldCgiY2FuX2VkaXRfcHJvZmlsZV9vZiIsW10pOiAKICAgICAgICAgICAgICAgICAgICB1WyJuYW1lIl09bm07IF9zYXZlX2pzb24oVVNFUlNfREIsZCk7IHJldHVybiAiUmVuYW1lZC4iCiAgICAgICAgICAgICAgICByZXR1cm4gIk5vdCBhbGxvd2VkLiIKICAgICAgICAgICAgcmVuYW1lX2J0bi5jbGljayhkb19yZW5hbWUsW21vZGVfc3RhdGUsIHJvbGVfc3RhdGUsIHVpZF9zdGF0ZSwgdGFyZ2V0LCBuZXdfbmFtZV0sW291dF0pCgogICAgICAgICAgICBkZWYgZG9fcGFzcyhtb2RlLCByb2xlLCBjYWxsZXIsIHRndCwgcHcpOgogICAgICAgICAgICAgICAgaWYgbm90IGlzX2FkbWluKG1vZGUsIHJvbGUpOiByZXR1cm4gIlN3aXRjaCB0byBBZG1pbiBtb2RlIHRvIHVzZSB0aGlzLiIKICAgICAgICAgICAgICAgIGQ9X2xvYWRfdXNlcnMoKTsgdSxfPV9maW5kX3VzZXIoZCwgdGd0IG9yICIiKQogICAgICAgICAgICAgICAgaWYgbm90IHU6IHJldHVybiAiVGFyZ2V0IG5vdCBmb3VuZC4iCiAgICAgICAgICAgICAgICBjdSxfPV9maW5kX3VzZXIoZCwgY2FsbGVyIG9yICIiKQogICAgICAgICAgICAgICAgaWYgbm90IGN1OiByZXR1cm4gIkxvZ2luIGZpcnN0LiIKICAgICAgICAgICAgICAgIGlmIHVbInJvbGUiXSBpbiBQRVJNUy5nZXQoY3VbInJvbGUiXSx7fSkuZ2V0KCJjYW5fZWRpdF9wcm9maWxlX29mIixbXSk6IAogICAgICAgICAgICAgICAgICAgIHVbInBhc3MiXT1wdzsgX3NhdmVfanNvbihVU0VSU19EQixkKTsgcmV0dXJuICJQYXNzd29yZCBjaGFuZ2VkLiIKICAgICAgICAgICAgICAgIHJldHVybiAiTm90IGFsbG93ZWQuIgogICAgICAgICAgICBwYXNzX2J0bi5jbGljayhkb19wYXNzLFttb2RlX3N0YXRlLCByb2xlX3N0YXRlLCB1aWRfc3RhdGUsIHRhcmdldCwgbmV3X3Bhc3NdLFtvdXRdKQoKICAgICAgICAgICAgZGVmIGRvX3JvbGUobW9kZSwgcm9sZSwgY2FsbGVyLCB0Z3QsIHJsKToKICAgICAgICAgICAgICAgIGlmIG5vdCBpc19hZG1pbihtb2RlLCByb2xlKTogcmV0dXJuICJTd2l0Y2ggdG8gQWRtaW4gbW9kZSB0byB1c2UgdGhpcy4iCiAgICAgICAgICAgICAgICBkPV9sb2FkX3VzZXJzKCk7IHUsXz1fZmluZF91c2VyKGQsIHRndCBvciAiIikKICAgICAgICAgICAgICAgIGlmIG5vdCB1OiByZXR1cm4gIlRhcmdldCBub3QgZm91bmQuIgogICAgICAgICAgICAgICAgY3UsXz1fZmluZF91c2VyKGQsIGNhbGxlciBvciAiIik7IAogICAgICAgICAgICAgICAgaWYgbm90IGN1OiByZXR1cm4gIkxvZ2luIGZpcnN0LiIKICAgICAgICAgICAgICAgIGFsbG93ZWRfbmV3ID0geyJvd25lciI6WyJvd25lciIsImFkbWluX3N1cGVyIiwiYWRtaW5fZ2VuZXJhbCIsInVzZXIiXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZG1pbl9zdXBlciI6WyJhZG1pbl9nZW5lcmFsIiwidXNlciJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFkbWluX2dlbmVyYWwiOlsiYWRtaW5fZ2VuZXJhbCIsInVzZXIiXX0uZ2V0KGN1WyJyb2xlIl0sIFtdKQogICAgICAgICAgICAgICAgaWYgdVsicm9sZSJdIG5vdCBpbiBQRVJNUy5nZXQoY3VbInJvbGUiXSx7fSkuZ2V0KCJjYW5fZWRpdF9yb2xlX29mIixbXSkgb3Igcmwgbm90IGluIGFsbG93ZWRfbmV3OgogICAgICAgICAgICAgICAgICAgIHJldHVybiBmIk5vdCBhbGxvd2VkIHRvIHNldCB7cmx9LiIKICAgICAgICAgICAgICAgIGZvciBncnAgaW4gWyJhZG1pbnNfc3VwZXIiLCJhZG1pbnNfZ2VuZXJhbCIsInVzZXJzIl06CiAgICAgICAgICAgICAgICAgICAgZFtncnBdPVt4IGZvciB4IGluIGRbZ3JwXSBpZiB4WyJpZCJdIT11WyJpZCJdXQogICAgICAgICAgICAgICAgaWYgcmw9PSJvd25lciI6IGRbIm93bmVyIl09dTsgdVsicm9sZSJdPSJvd25lciIKICAgICAgICAgICAgICAgIGVsaWYgcmw9PSJhZG1pbl9zdXBlciI6IGRbImFkbWluc19zdXBlciJdLmFwcGVuZCh1KTsgdVsicm9sZSJdPSJhZG1pbl9zdXBlciIKICAgICAgICAgICAgICAgIGVsaWYgcmw9PSJhZG1pbl9nZW5lcmFsIjogZFsiYWRtaW5zX2dlbmVyYWwiXS5hcHBlbmQodSk7IHVbInJvbGUiXT0iYWRtaW5fZ2VuZXJhbCIKICAgICAgICAgICAgICAgIGVsc2U6IGRbInVzZXJzIl0uYXBwZW5kKHUpOyB1WyJyb2xlIl09InVzZXIiCiAgICAgICAgICAgICAgICBfc2F2ZV9qc29uKFVTRVJTX0RCLGQpOyByZXR1cm4gZiJSb2xlIHNldCB0byB7cmx9LiIKICAgICAgICAgICAgcm9sZV9idG4uY2xpY2soZG9fcm9sZSxbbW9kZV9zdGF0ZSwgcm9sZV9zdGF0ZSwgdWlkX3N0YXRlLCB0YXJnZXQsIG5ld19yb2xlXSxbb3V0XSkKCiAgICAgICAgICAgICMgLS0tLS0tLSBJbnRlcm5hbCBPcHRpbWl6YXRpb24gY29udHJvbHMgKE93bmVyLWdhdGVkKSAtLS0tLS0tCiAgICAgICAgICAgIGdyLk1hcmtkb3duKCIjIyMgSW50ZXJuYWwgT3B0aW1pemF0aW9uIChDaGFuZ2UgTWFuYWdlcikiKQogICAgICAgICAgICBwcm9wX2tpbmQ9Z3IuRHJvcGRvd24oY2hvaWNlcz1bIm1vZGVsIiwicGFja2FnZSIsImNvZGUiXSwgdmFsdWU9Im1vZGVsIiwgbGFiZWw9IlByb3Bvc2FsIHR5cGUiKQogICAgICAgICAgICBwcm9wX25hbWU9Z3IuVGV4dGJveChsYWJlbD0iTW9kZWwgSUQgLyBQYWNrYWdlIE5hbWUiKQogICAgICAgICAgICBwcm9wX3Zlcj1nci5UZXh0Ym94KGxhYmVsPSJQYWNrYWdlIHZlcnNpb24gKG9wdGlvbmFsKSIpCiAgICAgICAgICAgIHByb3BfcmVhc29uPWdyLlRleHRib3gobGFiZWw9IldoeSB0aGlzIGNoYW5nZT8iKQogICAgICAgICAgICBwcm9wX3BhdGNoPWdyLkNvZGUobGFiZWw9IkNvZGUgcGF0Y2ggKGZvciAnY29kZScgcHJvcG9zYWxzKTogcGFzdGUgZnVsbCByZXBsYWNlbWVudCBvciBkaWZmIikKICAgICAgICAgICAgcHJvcG9zZV9idG49Z3IuQnV0dG9uKCJQcm9wb3NlIik7IHRlc3RfYnRuPWdyLkJ1dHRvbigiVGVzdCBpbiBzYW5kYm94Iik7IGFwcGx5X2J0bj1nci5CdXR0b24oIkFwcGx5IChwb2xpY3ktY2hlY2tlZCkiKQogICAgICAgICAgICBvcHRfb3V0PWdyLk1hcmtkb3duKCkKICAgICAgICAgICAgX2xhc3QgPSB7ImlkIjogTm9uZSwgIm9iaiI6IE5vbmV9CiAgICAgICAgICAgIGRlZiBkb19wcm9wb3NlKGtpbmQsbmFtZSx2ZXIscmVhc29uLHBhdGNoKToKICAgICAgICAgICAgICAgIGNwPUNoYW5nZVByb3Bvc2FsKGtpbmQ9a2luZCxuYW1lPW5hbWUgb3IgIiIsdmVyc2lvbj12ZXIgb3IgIiIscmVhc29uPXJlYXNvbiBvciAiIixwYXRjaF90ZXh0PXBhdGNoIG9yICIiKQogICAgICAgICAgICAgICAgcGlkPWhpdmUuY2hhbmdlcy5wcm9wb3NlKGNwKTsgX2xhc3RbImlkIl09cGlkOyBfbGFzdFsib2JqIl09Y3AKICAgICAgICAgICAgICAgIHJldHVybiBmIlByb3Bvc2VkIHtraW5kfToge25hbWUgb3IgJyhjb2RlIHBhdGNoKSd9IChpZD17cGlkfSkiCiAgICAgICAgICAgIGRlZiBkb190ZXN0KCk6CiAgICAgICAgICAgICAgICBpZiBub3QgX2xhc3RbIm9iaiJdOiByZXR1cm4gIk5vIHByb3Bvc2FsIGluIG1lbW9yeS4gU3VibWl0IG9uZSBmaXJzdC4iCiAgICAgICAgICAgICAgICByZXM9aGl2ZS5jaGFuZ2VzLnRlc3RfYW5kX2NvbXBhcmUoX2xhc3RbImlkIl0sIF9sYXN0WyJvYmoiXSk7IHJldHVybiBqc29uLmR1bXBzKHJlcywgaW5kZW50PTIpCiAgICAgICAgICAgIGRlZiBkb19hcHBseShyb2xlLCBtb2RlKToKICAgICAgICAgICAgICAgIGlmIHJvbGUgbm90IGluICgiYWRtaW5fc3VwZXIiLCJvd25lciIpIG9yIG1vZGUhPSJhZG1pbiI6IHJldHVybiAiT25seSBhZG1pbl9zdXBlciBvciBvd25lciBtYXkgYXBwbHkuIgogICAgICAgICAgICAgICAgaWYgbm90IF9sYXN0WyJvYmoiXTogcmV0dXJuICJObyBwcm9wb3NhbCBsb2FkZWQuIgogICAgICAgICAgICAgICAgcmVzPWhpdmUuY2hhbmdlcy50ZXN0X2FuZF9jb21wYXJlKF9sYXN0WyJpZCJdLCBfbGFzdFsib2JqIl0pCiAgICAgICAgICAgICAgICBpZiBub3QgcmVzLmdldCgib2siKTogcmV0dXJuIGYiVGVzdCBmYWlsZWQ6IHtyZXMuZ2V0KCdyZWFzb24nLCd1bmtub3duJyl9IgogICAgICAgICAgICAgICAgaWYgX2xhc3RbIm9iaiJdLmtpbmQ9PSJjb2RlIiBhbmQgcm9sZSE9Im93bmVyIiBhbmQgbm90IENGR1siT1BUX0FVVE9fQVBQTFkiXTogcmV0dXJuICJBd2FpdGluZyBPd25lciBhcHByb3ZhbCBmb3IgY29kZSBjaGFuZ2VzLiIKICAgICAgICAgICAgICAgIG9rLG1zZz1oaXZlLmNoYW5nZXMuYXBwbHkocmVzKTsgcmV0dXJuIG1zZyBpZiBvayBlbHNlIGYiQXBwbHkgZmFpbGVkOiB7bXNnfSIKICAgICAgICAgICAgcHJvcG9zZV9idG4uY2xpY2soZG9fcHJvcG9zZSwgW3Byb3Bfa2luZCxwcm9wX25hbWUscHJvcF92ZXIscHJvcF9yZWFzb24scHJvcF9wYXRjaF0sW29wdF9vdXRdKQogICAgICAgICAgICB0ZXN0X2J0bi5jbGljayhsYW1iZGE6IGRvX3Rlc3QoKSwgW10sIFtvcHRfb3V0XSkKICAgICAgICAgICAgYXBwbHlfYnRuLmNsaWNrKGRvX2FwcGx5LCBbcm9sZV9zdGF0ZSwgbW9kZV9zdGF0ZV0sIFtvcHRfb3V0XSkKCiAgICBkZW1vLmxhdW5jaChzZXJ2ZXJfbmFtZT0iMC4wLjAuMCIsIHNlcnZlcl9wb3J0PWludChvcy5lbnZpcm9uLmdldCgiUE9SVCIsIjc4NjAiKSkpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLSBlbnRyeSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KaWYgX19uYW1lX189PSJfX21haW5fXyI6CiAgICBpZiBFTlYoIkhJVkVfTEFVTkNIX1VJIiwiMSIsYm9vbCk6CiAgICAgICAgbGF1bmNoX3VpKCkKICAgIGVsc2U6CiAgICAgICAgaD1IaXZlKCk7IHByaW50KCJDTEkgbW9kZS4gVHlwZSBhbmQgcHJlc3MgRW50ZXIgKEN0cmwrQyB0byBleGl0KS4iKQogICAgICAgIHRyeToKICAgICAgICAgICAgd2hpbGUgVHJ1ZToKICAgICAgICAgICAgICAgIHM9aW5wdXQoIj4gIikuc3RyaXAoKQogICAgICAgICAgICAgICAgaWYgbm90IHM6IGNvbnRpbnVlCiAgICAgICAgICAgICAgICBwcmludChoLmNoYXQocywgZWZmZWN0aXZlX3JvbGU9InVzZXIiLCBjYWxsZXJfaWQ9Tm9uZSkpCiAgICAgICAgZXhjZXB0IEtleWJvYXJkSW50ZXJydXB0OgogICAgICAgICAgICBwYXNz"""
_HIVE_BASE_SOURCE = base64.b64decode(_HIVE_BASE_B64.encode("utf-8")).decode("utf-8", errors="strict")
hive_base = types.ModuleType("hive_base")
hive_base.__dict__["__name__"] = "hive_base"
exec(compile(_HIVE_BASE_SOURCE, "hive_tinyllama_hf.py", "exec"), hive_base.__dict__)
sys.modules["hive_base"] = hive_base
# ---------- Compatibility Monkey Patch for Hive.chat ----------
# Some UI callbacks may call Hive.chat(...) without the required keyword-only
# args added in newer versions. Patch it so defaults are injected, preventing
# errors like: Hive.chat() missing 'effective_role' and 'caller_id'.
try:
DEFAULT_EFFECTIVE_ROLE = os.getenv("EFFECTIVE_ROLE", "user")
DEFAULT_CALLER_ID_CLI = os.getenv("CALLER_ID_CLI", "cli")
DEFAULT_CALLER_ID_UI = os.getenv("CALLER_ID_UI", "ui")
if hasattr(hive_base, "Hive"):
_Hive = hive_base.Hive
if hasattr(_Hive, "chat"):
_orig_chat = _Hive.chat
def _chat_compat(self, *args, **kwargs):
# Inject defaults only if missing
if "effective_role" not in kwargs or kwargs.get("effective_role") is None:
kwargs["effective_role"] = DEFAULT_EFFECTIVE_ROLE
if "caller_id" not in kwargs or kwargs.get("caller_id") is None:
# Prefer UI caller id if we are in the web app
if os.getenv("RUNNING_IN_UI", "0") == "1":
kwargs["caller_id"] = DEFAULT_CALLER_ID_UI
else:
kwargs["caller_id"] = DEFAULT_CALLER_ID_CLI
return _orig_chat(self, *args, **kwargs)
# Tag to avoid double-patching
if getattr(_Hive.chat, "_compat_patched", False) is False:
_chat_compat._compat_patched = True
_Hive.chat = _chat_compat
except Exception as _e:
# Do not fail the app if patching isn't possible
pass
# -------------------------------------------------------------
# ---------- Tutor+ Layer & Curves builder (no UI) ----------
try:
import numpy as np
except Exception:
np = None
try:
import faiss
_FAISS = True
except Exception:
faiss = None; _FAISS = False
try:
from sentence_transformers import SentenceTransformer
except Exception:
SentenceTransformer = None
def ENV(k, d=None, cast=str):
v = os.getenv(k, None)
if v is None: return d
if cast is bool: return str(v).lower() in ("1","true","yes","on")
if cast is int:
try: return int(v)
except: return int(float(v))
return v
CFG = {
"CURVE_DIR": ENV("HIVE_CURVE_DIR","./state/curves"),
"EMB_ID": ENV("HIVE_EMB_ID","sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"),
"EMB_LOCAL_DIR": ENV("HIVE_EMB_LOCAL_DIR",""),
"KNN_TOPK": ENV("HIVE_KNN_TOPK","8", int),
"KNN_LAMBDA": float(ENV("HIVE_KNN_LAMBDA","0.35")),
"PHONICS_MAX_LINES": ENV("HIVE_PHONICS_MAX_LINES","2", int),
"STAGE_BATCH": ENV("HIVE_STAGE_BATCH","128", int),
"STAGE_SAVE_EVERY": ENV("HIVE_STAGE_SAVE_EVERY","512", int),
"STAGE_MAX_DOCS_PER_DATASET": ENV("HIVE_STAGE_MAX_DOCS_PER_DATASET","5000", int),
"CURVES_TARGET_MIN": ENV("HIVE_CURVES_TARGET_MIN","10000", int),
"CURVES_RECHECK_SECS": ENV("HIVE_CURVES_RECHECK_SECS","1800", int),
}
try: os.makedirs(CFG["CURVE_DIR"], exist_ok=True)
except Exception: pass
class _EmbMux:
def __init__(self):
if SentenceTransformer is None:
self.model=None; self.dim=384
else:
p=CFG["EMB_LOCAL_DIR"].strip()
self.model = SentenceTransformer(p) if (p and os.path.isdir(p)) else SentenceTransformer(CFG["EMB_ID"])
try:
v=self.model.encode(["hi"], normalize_embeddings=True)
self.dim=int(getattr(v,"shape",[1,384])[1])
except Exception:
self.dim=384
def encode(self, texts: List[str]):
if self.model is None:
dim=getattr(self,"dim",384); out=[]
for t in texts:
h=abs(hash(t))%1000003; vec=[0.0]*dim; vec[h%dim]=1.0; out.append(vec)
return out
return self.model.encode(texts, normalize_embeddings=True)
class _CurveStore:
def __init__(self, root:str):
self.root=root
try: os.makedirs(self.root, exist_ok=True)
except Exception: pass
self.emb=_EmbMux()
self.dim=getattr(self.emb,"dim",384)
self.idx_path=os.path.join(self.root,"faiss.index")
self.meta_path=os.path.join(self.root,"meta.jsonl")
self.idx=self._load()
def _load(self):
if not _FAISS or faiss is None: return None
if os.path.exists(self.idx_path):
try: return faiss.read_index(self.idx_path)
except Exception: pass
return faiss.IndexFlatIP(self.dim)
def save_index(self):
if _FAISS and self.idx is not None:
try: faiss.write_index(self.idx, self.idx_path)
except Exception: pass
def add_vectors(self, vecs, metas: List[Dict]):
if not _FAISS or self.idx is None or np is None: return 0
try:
v=np.asarray(vecs, dtype="float32")
self.idx.add(v)
with open(self.meta_path,"a",encoding="utf-8") as f:
for m in metas: f.write(json.dumps(m, ensure_ascii=False)+"\n")
return len(metas)
except Exception:
return 0
def add_texts(self, texts: List[str], tag="stage", scope="general"):
if not texts: return 0
vecs=self.emb.encode(texts)
metas=[{"scope":scope, "tag":tag, "text":t[:500]} for t in texts]
n=self.add_vectors(vecs, metas); self.save_index(); return n
@property
def count(self)->int:
try:
return int(self.idx.ntotal) if (self.idx is not None) else 0
except Exception:
return 0
def _extract_text(rec: dict):
for k in ("text","sentence","sentences","content","input","inputs","prompt","source","article","document","review","body"):
if k in rec and isinstance(rec[k], str) and rec[k].strip():
return rec[k]
if k in rec and isinstance(rec[k], list) and rec[k] and isinstance(rec[k][0], str):
return " ".join(rec[k])
parts=[]
for k,v in rec.items():
if isinstance(v,str) and 5<=len(v)<=2000: parts.append(v)
return " ".join(parts) if parts else ""
def _iter_texts(name: str, max_docs:int):
# un-stub datasets
if "datasets" in sys.modules and getattr(sys.modules["datasets"].load_dataset, "__name__", "") == "_disabled_load_dataset":
del sys.modules["datasets"]
import datasets as _ds
for split in ("train","validation","test"):
# prefer streaming
try:
ds = _ds.load_dataset(name, split=split, streaming=True)
cnt=0
for rec in ds:
try:
txt = _extract_text(rec)
if txt:
yield txt
cnt += 1
if cnt >= max_docs: return
except Exception: continue
except Exception:
try:
ds = _ds.load_dataset(name, split=split)
cnt=0
for rec in ds:
try:
txt = _extract_text(rec)
if txt:
yield txt
cnt += 1
if cnt >= max_docs: return
except Exception: continue
except Exception:
continue
def build_condensed_curves(datasets_csv=None, curve_dir=None, log_cb=None):
def log(m):
if log_cb: log_cb(m)
enforce_cache_budget(log)
curve_dir = curve_dir or CFG["CURVE_DIR"]
names = [x.strip() for x in (datasets_csv or os.getenv("HIVE_DATASETS_LIST","wi_locness")).split(",") if x.strip()]
max_docs = int(CFG["STAGE_MAX_DOCS_PER_DATASET"])
batch = int(CFG["STAGE_BATCH"])
save_every = int(CFG["STAGE_SAVE_EVERY"])
store = _CurveStore(curve_dir)
total_added=0
for name in names:
log(f"[staged] building condensed curves from '{name}' (max {max_docs} docs)…")
buf=[]; added=0; last_save=0
for txt in _iter_texts(name, max_docs):
buf.append(txt)
if len(buf) >= batch:
n = store.add_texts(buf, tag=f"ds:{name}", scope="general")
added += n; total_added += n; buf = []
if added - last_save >= save_every:
store.save_index(); enforce_cache_budget(log); last_save = added
log(f"[staged] progress '{name}': {added} items … (index={store.count})")
if buf:
n = store.add_texts(buf, tag=f"ds:{name}", scope="general")
added += n; total_added += n; buf = []
store.save_index(); enforce_cache_budget(log)
log(f"[staged] '{name}' done: {added} items condensed (index={store.count}).")
log(f"[staged] total added: {total_added} (index={store.count})")
return True
# Background supervisor: runs at start and on interval; only builds if below target
def _should_build(curve_dir=None)->bool:
store=_CurveStore(curve_dir or CFG["CURVE_DIR"])
return store.count < int(CFG["CURVES_TARGET_MIN"])
def _background_supervisor(log_cb=None):
def log(m):
if log_cb: log_cb(m)
interval = int(CFG["CURVES_RECHECK_SECS"])
while True:
try:
if _should_build():
log("[staged] target not met; starting condensed-curves build…")
build_condensed_curves(log_cb=log)
else:
log("[staged] target met; no build needed.")
except Exception as e:
log(f"[staged] supervisor error: {e}")
time.sleep(interval)
def _kickoff_background_if_enabled(log_cb=None):
if os.getenv("HIVE_BUILD_CONDENSED_CURVES_ON_START","0").lower() not in ("1","true","yes","on"):
return
threading.Thread(target=_background_supervisor, args=(log_cb,), daemon=True).start()
# ---------- Tutor+ light additions (no UI) ----------
def _ipa_or_hyphenate(text:str)->str:
try:
import eng_to_ipa as ipa
ipa_text=ipa.convert(text)
if ipa_text and ipa_text!=text:
return f"{text} /{ipa_text}/"
except Exception:
pass
try:
import pyphen
dic=pyphen.Pyphen(lang="en"); return dic.inserted(text)
except Exception:
return text
def _gentle_phonics_block(text:str, max_lines:int)->str:
import re as _re
words=_re.findall(r"[A-Za-z][A-Za-z\-']{2,}", text or "")
words=sorted(set(words), key=lambda w:(-len(w), w.lower()))
picks=words[:max_lines]
if not picks: return ""
return "\n".join([f"- {_ipa_or_hyphenate(w)}" for w in picks])
def _route_intent(txt:str)->str:
import re as _re
if _re.search(r"\b(spell|spelling|how\s+do\s+you\s+spell)\b", txt or "", _re.I): return "direct_spell"
if _re.search(r"\b(pronounc(e|iation)|ipa|phonics|how\s+do\s+you\s+say)\b", txt or "", _re.I): return "pronounce"
if _re.search(r"\b(essay|review|evaluate|feedback|improv(e|ements?)|revise|critique|proofread\s+my\s+essay)\b", txt or "", _re.I): return "essay_review"
if _re.search(r"\b(grammar|correct|fix|proofread|mistakes?)\b", txt or "", _re.I): return "direct_grammar"
return "tutor"
BaseHive = getattr(hive_base, "Hive", object)
class Hive(BaseHive):
def __init__(self, *a, **k):
super().__init__(*a, **k)
def chat(self, message:str, *a, **k)->str:
mode=_route_intent(message or "")
try:
reply=super().chat(message, *a, **k)
except Exception as e:
reply=f"[Base chat failed: {e}]"
if isinstance(reply,str) and (mode in ("pronounce","direct_spell") or (mode=="tutor" and len(reply.split())<=40)):
hints=_gentle_phonics_block(reply, int(CFG["PHONICS_MAX_LINES"]))
if hints: reply += "\n\n**Phonics hints (brief)**\n" + hints
return reply
# ---------- Entrypoint ----------
def build_ui():
# Respect user's base UI if present; we do NOT add any tabs here.
try:
import gradio as gr
except Exception:
return None
for name in ("build_ui","launch_ui","get_ui","make_ui"):
if hasattr(hive_base, name):
try:
ui = getattr(hive_base, name)()
return ui if isinstance(ui, gr.Blocks) else None
except Exception:
pass
return None
# --- BOTTOM OF FILE: replace your old `if __name__ == "__main__":` with this ---
# --- BOTTOM OF FILE: replace any old REPL/main block with this ---
import os
import sys
import time
import argparse
def _read_line(prompt="> "):
# Avoid prompting when there’s no interactive terminal (e.g., Spaces)
if not sys.stdin or not sys.stdin.isatty():
prompt = ""
try:
return input(prompt)
except EOFError:
return None
def handle_user_input(s: str) -> str:
# minimal glue to your backend
global _HIVE_SINGLETON
if _HIVE_SINGLETON is None:
_HIVE_SINGLETON = Hive()
return _HIVE_SINGLETON.chat(s)
def run_cli_loop():
while True:
s = _read_line("> ")
if s is None: # No stdin / non-interactive environment
break # fall through to headless wait
s = s.strip()
if not s:
continue
reply = handle_user_input(s)
print(reply, flush=True)
def run_headless_wait():
print("APP_READY: initialized (headless). Waiting for requests...", flush=True)
while True:
time.sleep(3600)
_HIVE_SINGLETON = None # global lazy instance for CLI & UI
def build_ui():
try:
import gradio as gr
except Exception as e:
print(f"[ui] Gradio import failed: {e}")
return None
with gr.Blocks() as demo:
state = gr.State(None) # we'll lazily create Hive on first use
chatbox = gr.Chatbot(height=400)
msg = gr.Textbox(placeholder="Type your message…", label="Message")
send = gr.Button("Send")
def on_send(user_msg, st, history):
global _HIVE_SINGLETON
if _HIVE_SINGLETON is None:
_HIVE_SINGLETON = Hive() # <- construct here, not at import time
if not user_msg.strip():
return history, ""
reply = _HIVE_SINGLETON.chat(user_msg)
history = (history or []) + [[user_msg, reply]]
return history, ""
send.click(on_send, inputs=[msg, state, chatbox], outputs=[chatbox, msg])
return demo
if __name__ == "__main__":
import os, sys, argparse
parser = argparse.ArgumentParser()
parser.add_argument("--ui", action="store_true", help="Force-launch Gradio UI")
args = parser.parse_args()
# Detect headless container (no interactive stdin/TTY)
HEADLESS = (not sys.stdin) or (not sys.stdin.isatty())
# Decide whether to bring up the web UI:
# - if user passed --ui
# - OR if we're headless (no stdin)
# - OR if FORCE_UI env var is set (FORCE_UI=1/true/yes)
force_env = os.getenv("FORCE_UI", "").lower() in ("1", "true", "yes")
WANTS_UI = args.ui or HEADLESS or force_env
if WANTS_UI:
ui = build_ui()
if ui is None:
print("Gradio not installed; falling back to CLI.")
run_cli_loop()
if HEADLESS:
run_headless_wait()
else:
# Start any background tasks after app is live (ignore if not defined)
try:
_kickoff_background_if_enabled(log_cb=print)
except NameError:
pass
# Respect platform port if provided
port = int(os.getenv("PORT", "7860"))
os.environ.setdefault("RUNNING_IN_UI","1"); ui.queue().launch(server_name="0.0.0.0", server_port=port)
else:
# CLI mode when a TTY exists
print("Hive (Original-first, Tutor+ • SAFE v5 BACKGROUND) ready. Type to chat.")
try:
_kickoff_background_if_enabled(log_cb=print)
except NameError:
pass
run_cli_loop()
# If the CLI loop ended because stdin vanished, keep app alive
if HEADLESS:
run_headless_wait()