Farhan Beg commited on
Commit ·
2e4f994
1
Parent(s): 0f35c79
fix(permissions): chown /opt/hermes/.venv to hermes so auto-updates work
Browse files- Dockerfile +1 -190
Dockerfile
CHANGED
|
@@ -1,190 +1 @@
|
|
| 1 |
-
|
| 2 |
-
# Base: NousResearch Hermes Agent (ships Hermes CLI, gateway, dashboard, Python venv)
|
| 3 |
-
|
| 4 |
-
ARG HERMES_AGENT_VERSION=latest
|
| 5 |
-
FROM nousresearch/hermes-agent:${HERMES_AGENT_VERSION}
|
| 6 |
-
|
| 7 |
-
ARG WEBUI_REF=master
|
| 8 |
-
|
| 9 |
-
USER root
|
| 10 |
-
|
| 11 |
-
# System deps (mirrors HuggingMes) + git/nodejs for WebUI checkout + router
|
| 12 |
-
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 13 |
-
ca-certificates \
|
| 14 |
-
curl \
|
| 15 |
-
jq \
|
| 16 |
-
git \
|
| 17 |
-
python3 \
|
| 18 |
-
nodejs \
|
| 19 |
-
npm \
|
| 20 |
-
chromium \
|
| 21 |
-
libnss3 \
|
| 22 |
-
libatk1.0-0 \
|
| 23 |
-
libatk-bridge2.0-0 \
|
| 24 |
-
libdrm2 \
|
| 25 |
-
libgbm1 \
|
| 26 |
-
libxcomposite1 \
|
| 27 |
-
libxdamage1 \
|
| 28 |
-
libxrandr2 \
|
| 29 |
-
libxkbcommon0 \
|
| 30 |
-
libx11-6 \
|
| 31 |
-
libxext6 \
|
| 32 |
-
libxfixes3 \
|
| 33 |
-
libasound2 \
|
| 34 |
-
fonts-dejavu-core \
|
| 35 |
-
fonts-liberation \
|
| 36 |
-
fonts-noto-color-emoji \
|
| 37 |
-
&& rm -rf /var/lib/apt/lists/* \
|
| 38 |
-
&& uv pip install --python /opt/hermes/.venv/bin/python --no-cache-dir \
|
| 39 |
-
huggingface_hub hf_transfer pyyaml
|
| 40 |
-
|
| 41 |
-
# Clone nesquena/hermes-webui (install deps into the agent venv so imports resolve)
|
| 42 |
-
RUN git clone --depth 1 --branch ${WEBUI_REF} \
|
| 43 |
-
https://github.com/nesquena/hermes-webui.git /opt/hermes-webui \
|
| 44 |
-
&& ( [ -f /opt/hermes-webui/requirements.txt ] \
|
| 45 |
-
&& /opt/hermes/.venv/bin/pip install --no-cache-dir -r /opt/hermes-webui/requirements.txt \
|
| 46 |
-
|| true ) \
|
| 47 |
-
&& chown -R hermes:hermes /opt/hermes-webui
|
| 48 |
-
|
| 49 |
-
# HuggingMes-style integration scripts (vendored from somratpro/HuggingMes)
|
| 50 |
-
COPY --chown=hermes:hermes start.sh /opt/huggingmes/start.sh
|
| 51 |
-
COPY --chown=hermes:hermes health-server.js /opt/huggingmes/health-server.js
|
| 52 |
-
COPY --chown=hermes:hermes hermes-sync.py /opt/huggingmes/hermes-sync.py
|
| 53 |
-
COPY --chown=hermes:hermes cloudflare-proxy-setup.py /opt/huggingmes/cloudflare-proxy-setup.py
|
| 54 |
-
COPY --chown=hermes:hermes cloudflare-keepalive-setup.py /opt/huggingmes/cloudflare-keepalive-setup.py
|
| 55 |
-
|
| 56 |
-
RUN chmod +x \
|
| 57 |
-
/opt/huggingmes/start.sh \
|
| 58 |
-
/opt/huggingmes/hermes-sync.py \
|
| 59 |
-
/opt/huggingmes/cloudflare-proxy-setup.py \
|
| 60 |
-
/opt/huggingmes/cloudflare-keepalive-setup.py
|
| 61 |
-
|
| 62 |
-
# Idempotent kanban migration patch (same workaround HuggingMes ships)
|
| 63 |
-
RUN python3 - <<'PY'
|
| 64 |
-
from pathlib import Path
|
| 65 |
-
import sys
|
| 66 |
-
p = Path("/opt/hermes/hermes_cli/kanban_db.py")
|
| 67 |
-
if not p.exists():
|
| 68 |
-
sys.exit(0)
|
| 69 |
-
src = p.read_text(encoding="utf-8")
|
| 70 |
-
sentinel = "# huggingmes-webui: idempotent-alter"
|
| 71 |
-
if sentinel in src:
|
| 72 |
-
sys.exit(0)
|
| 73 |
-
old = (
|
| 74 |
-
' conn.execute(\n'
|
| 75 |
-
' "ALTER TABLE tasks ADD COLUMN consecutive_failures "\n'
|
| 76 |
-
' "INTEGER NOT NULL DEFAULT 0"\n'
|
| 77 |
-
' )'
|
| 78 |
-
)
|
| 79 |
-
new = (
|
| 80 |
-
f' try: {sentinel}\n'
|
| 81 |
-
' conn.execute(\n'
|
| 82 |
-
' "ALTER TABLE tasks ADD COLUMN consecutive_failures "\n'
|
| 83 |
-
' "INTEGER NOT NULL DEFAULT 0"\n'
|
| 84 |
-
' )\n'
|
| 85 |
-
' except Exception:\n'
|
| 86 |
-
' pass'
|
| 87 |
-
)
|
| 88 |
-
if old in src:
|
| 89 |
-
p.write_text(src.replace(old, new), encoding="utf-8")
|
| 90 |
-
print("kanban patch: applied")
|
| 91 |
-
PY
|
| 92 |
-
|
| 93 |
-
# Quiet hermes-webui's per-request access log noise.
|
| 94 |
-
# By default it prints `[webui] {"ts":...,"method":...}` for EVERY request,
|
| 95 |
-
# which drowns the HF Logs tab once any browser tab is open polling
|
| 96 |
-
# /api/dashboard/status, /api/health/agent, /api/sessions, /sw.js, etc.
|
| 97 |
-
# Patch log_request() to drop 2xx responses for high-frequency poll paths.
|
| 98 |
-
# Errors and chat/streaming paths still log normally.
|
| 99 |
-
RUN python3 - <<'PY'
|
| 100 |
-
from pathlib import Path
|
| 101 |
-
import re
|
| 102 |
-
import sys
|
| 103 |
-
|
| 104 |
-
p = Path("/opt/hermes-webui/server.py")
|
| 105 |
-
if not p.exists():
|
| 106 |
-
sys.exit(0)
|
| 107 |
-
src = p.read_text(encoding="utf-8")
|
| 108 |
-
sentinel = "# huggingmes-webui: quiet-poll-paths"
|
| 109 |
-
if sentinel in src:
|
| 110 |
-
sys.exit(0)
|
| 111 |
-
|
| 112 |
-
old = (
|
| 113 |
-
" def log_request(self, code: str='-', size: str='-') -> None:\n"
|
| 114 |
-
" \"\"\"Structured JSON logs for each request.\"\"\"\n"
|
| 115 |
-
" import json as _json\n"
|
| 116 |
-
" duration_ms = round((time.time() - getattr(self, '_req_t0', time.time())) * 1000, 1)\n"
|
| 117 |
-
" record = _json.dumps({\n"
|
| 118 |
-
" 'ts': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),\n"
|
| 119 |
-
" 'method': self.command or '-',\n"
|
| 120 |
-
" 'path': self.path or '-',\n"
|
| 121 |
-
" 'status': int(code) if str(code).isdigit() else code,\n"
|
| 122 |
-
" 'ms': duration_ms,\n"
|
| 123 |
-
" })\n"
|
| 124 |
-
" print(f'[webui] {record}', flush=True)"
|
| 125 |
-
)
|
| 126 |
-
|
| 127 |
-
new = (
|
| 128 |
-
" _QUIET_POLL_PATHS = ( " + sentinel + "\n"
|
| 129 |
-
" '/api/health/agent', '/api/dashboard/status',\n"
|
| 130 |
-
" '/api/dashboard/config', '/api/sessions', '/api/profiles',\n"
|
| 131 |
-
" '/api/profile/active', '/api/onboarding/status',\n"
|
| 132 |
-
" '/api/insights', '/api/system/health',\n"
|
| 133 |
-
" '/api/settings', '/api/projects', '/api/reasoning',\n"
|
| 134 |
-
" '/api/models', '/api/chat/stream/status',\n"
|
| 135 |
-
" '/api/git-info', '/sw.js', '/health',\n"
|
| 136 |
-
" )\n"
|
| 137 |
-
" _QUIET_PREFIXES = ('/static/', '/session/static/', '/assets/')\n"
|
| 138 |
-
"\n"
|
| 139 |
-
" def log_request(self, code: str='-', size: str='-') -> None:\n"
|
| 140 |
-
" \"\"\"Structured JSON logs for each request, skipping noisy polls.\"\"\"\n"
|
| 141 |
-
" # Always log non-2xx so 401/404/5xx remain visible.\n"
|
| 142 |
-
" try:\n"
|
| 143 |
-
" status_int = int(code) if str(code).isdigit() else 0\n"
|
| 144 |
-
" except Exception:\n"
|
| 145 |
-
" status_int = 0\n"
|
| 146 |
-
" path = (self.path or '').split('?', 1)[0]\n"
|
| 147 |
-
" if 200 <= status_int < 400:\n"
|
| 148 |
-
" if path in self._QUIET_POLL_PATHS:\n"
|
| 149 |
-
" return\n"
|
| 150 |
-
" for pref in self._QUIET_PREFIXES:\n"
|
| 151 |
-
" if path.startswith(pref):\n"
|
| 152 |
-
" return\n"
|
| 153 |
-
" import json as _json\n"
|
| 154 |
-
" duration_ms = round((time.time() - getattr(self, '_req_t0', time.time())) * 1000, 1)\n"
|
| 155 |
-
" record = _json.dumps({\n"
|
| 156 |
-
" 'ts': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),\n"
|
| 157 |
-
" 'method': self.command or '-',\n"
|
| 158 |
-
" 'path': self.path or '-',\n"
|
| 159 |
-
" 'status': int(code) if str(code).isdigit() else code,\n"
|
| 160 |
-
" 'ms': duration_ms,\n"
|
| 161 |
-
" })\n"
|
| 162 |
-
" print(f'[webui] {record}', flush=True)"
|
| 163 |
-
)
|
| 164 |
-
|
| 165 |
-
if old in src:
|
| 166 |
-
p.write_text(src.replace(old, new), encoding="utf-8")
|
| 167 |
-
print("webui log-quiet patch: applied")
|
| 168 |
-
else:
|
| 169 |
-
print("webui log-quiet patch: pattern not found, skipping")
|
| 170 |
-
PY
|
| 171 |
-
|
| 172 |
-
# Keep hermes CLI on PATH for all shell types (login/interactive/non-interactive)
|
| 173 |
-
RUN echo 'export PATH="/opt/hermes/.venv/bin:/opt/data/.local/bin:$PATH"' \
|
| 174 |
-
> /etc/profile.d/hermes-venv.sh
|
| 175 |
-
|
| 176 |
-
ENV HERMES_HOME=/opt/data \
|
| 177 |
-
HUGGINGMES_APP_DIR=/opt/huggingmes \
|
| 178 |
-
HERMES_WEBUI_REPO=/opt/hermes-webui \
|
| 179 |
-
HERMES_AGENT_VERSION=${HERMES_AGENT_VERSION} \
|
| 180 |
-
PYTHONUNBUFFERED=1 \
|
| 181 |
-
HF_HUB_ENABLE_HF_TRANSFER=1 \
|
| 182 |
-
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium
|
| 183 |
-
|
| 184 |
-
EXPOSE 7861
|
| 185 |
-
|
| 186 |
-
HEALTHCHECK --interval=30s --timeout=5s --start-period=120s \
|
| 187 |
-
CMD curl -fsS http://localhost:7861/health || exit 1
|
| 188 |
-
|
| 189 |
-
USER hermes
|
| 190 |
-
ENTRYPOINT ["/opt/huggingmes/start.sh"]
|
|
|
|
| 1 |
+
IyBIdWdnaW5nTWVzICsgSGVybWVzIFdlYlVJIOKAlCBtZXJnZWQgZGVwbG95bWVudCBmb3IgSHVnZ2luZyBGYWNlIFNwYWNlcwojIEJhc2U6IE5vdXNSZXNlYXJjaCBIZXJtZXMgQWdlbnQgKHNoaXBzIEhlcm1lcyBDTEksIGdhdGV3YXksIGRhc2hib2FyZCwgUHl0aG9uIHZlbnYpCgpBUkcgSEVSTUVTX0FHRU5UX1ZFUlNJT049bGF0ZXN0CkZST00gbm91c3Jlc2VhcmNoL2hlcm1lcy1hZ2VudDoke0hFUk1FU19BR0VOVF9WRVJTSU9OfQoKUkcgV0VCVUlfUkVGPW1hc3RlcgoKVVNFUiByb290CgojIFN5c3RlbSBkZXBzIChtaXJyb3JzIEh1Z2dpbmdNZXMpICsgZ2l0L25vZGVqcyBmb3IgV2ViVUkgY2hlY2tvdXQgKyByb3V0ZXIKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSAtLW5vLWluc3RhbGwtcmVjb21tZW5kcwogICAgY2EtY2VydGlmaWNhdGVzIFwKICAgIGN1cmwgXAogICAganEgXAogICAgZ2l0IFwKICAgIHB5dGhvbjMgXAogICAgbm9kZWpzIFwKICAgIG5wbSBcCiAgICBjaHJvbWl1bSBcCiAgICBsaWJudHMzIFwKICAgIGxpYmF0azEuMC0wIFwKICAgIGxpYmF0ay1icmlkZ2UyLjAtMCBcCiAgICBsaWJkcm0yIFwKICAgIGxpYmdibTEgXAogICAgbGlieGNvbXBvc2l0ZTEgXAogICAgbGlieGRhbWFnZTEgXAogICAgbGlieHJhbmRyMiBcCiAgICBsaWJ4a2Jjb21tb24wIFwKICAgIGxpYngxMS02IFwKICAgIGxpYng2IFwKICAgIGxpYnhmaXhlczMgXAogICAgbGliYXNvdW5kMiBcCiAgICBmb250cy1kZWphdnUtY29yZSBcCiAgICBmb250cy1saWJlcmF0aW9uIFwKICAgIGZvbnRzLW5vdG8tY29sb3ItZW1vamkgXAogICAgJiYgcm0gLXJmIC92YXIvbGliL2FwdC9saXN0cy8qIFwKICAgICYmIHV2IHBpcCBpbnN0YWxsIC0tcHl0aG9uIC9vcHQvaGVybWVzLy52ZW52L2Jpbi9weXRob24gLS1uby1jYWNoZS1kaXIgXAogICAgICAgIGh1Z2dpbmdmYWNlX2h1YiBoZl90cmFuc2ZlciBweXlhbWwKCiMgQ2xvbmUgbmVzcXVlbmEvaGVybWVzLXdlYnVpIChpbnN0YWxsIGRlcHMgaW50byB0aGUgYWdlbnQgdmVudiBzbyBpbXBvcnRzIHJlc29sdmUpClJVTiBnaXQgY2xvbmUgLS1kZXB0aCAxIC0tYnJhbmNoICR7V0VCVUlfUkVGfQogICAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9uZXNxdWVuYS9oZXJtZXMtd2VidWkuZ2l0IC9vcHQvaGVybWVzLXdlYnVpIFwKICYmICggWyAtZiAvb3B0L2hlcm1lcy13ZWJ1aS9yZXF1aXJlbWVudHMudHh0IF0KICAgICAgJiYgL29wdC9oZXJtZXMvLnZlbnYvYmluL3BpcCBpbnN0YWxsIC0tbm8tY2FjaGUtZGlyIC1yIC9vcHQvaGVybWVzLXdlYnVpL3JlcXVpcmVtZW50cy50eHQgXAogICAgICB8fCB0cnVlICkgXAogICYmIGNob3duIC1SIGhlcm1lczpoZXJtZXMgL29wdC9oZXJtZXMtd2VidWkKCiMgSHVnZ2luZ01lcy1zdHlsZSBpbnRlZ3JhdGlvbiBzY3JpcHRzICh2ZW5kb3JlZCBmcm9tIHNvbXJhdHByby9IdWdnaW5nTWVzKQpDT1BZIC0tY2hvd249aGVybWVzOmhlcm1lcyBzdGFydC5zaCAgICAgICAgICAgICAgICAgICAgICAgICAgIC9vcHQvaHVnZ2luZ21lcy9zdGFydC5zaApDT1BZIC0tY2hvd249aGVybWVzOmhlcm1lcyBoZWFsdGgtc2VydmVyLmpzICAgICAgICAgICAgICAgICAgICAvb3B0L2h1Z2dpbmdtZXMvaGVhbHRoLXNlcnZlci5qcwpDT1BZIC0tY2hvd249aGVybWVzOmhlcm1lcyBoZXJtZXMtc3luYy5weSAgICAgICAgICAgICAgICAgICAgIC9vcHQvaHVnZ2luZ21lcy9oZXJtZXMtc3luYy5weQpDT1BZIC0tY2hvd249aGVybWVzOmhlcm1lcyBjbG91ZGZsYXJlLXByb3h5LXNldHVwLnB5ICAgICAgL29wdC9odWdnaW5nbWVzL2Nsb3VkZmxhcmUtcHJveHktc2V0dXAucHkKQ09QWSAtLWNob3duPWhlcm1lczpoZXJtZXMgY2xvdWRmbGFyZS1rZWVwYWxpdmUtc2V0dXAucHkgIC9vcHQvaHVnZ2luZ21lcy9jbG91ZGZsYXJlLWtlZXBhbGl2ZS1zZXR1cC5weQoKUlVOIGNobW9kICt4IFwKICAgIC9vcHQvaHVnZ2luZ21lcy9zdGFydC5zaCBcCiAgICAvb3B0L2h1Z2dpbmdtZXMvaGVybWVzLXN5bmMucHkgXAogICAgL29wdC9odWdnaW5nbWVzL2Nsb3VkZmxhcmUtcHJveHktc2V0dXAucHkgXAogICAgL29wdC9odWdnaW5nbWVzL2Nsb3VkZmxhcmUta2VlcGFsaXZlLXNldHVwLnB5CgojIElkZW1wb3RlbnQga2FuYmFuIG1pZ3JhdGlvbiBwYXRjaCAoc2FtZSB3b3JrYXJvdW5kIEh1Z2dpbmdNZXMgc2hpcHMpClJVTiBweXRob24zIC0gPDwnUFknCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAppbXBvcnQgc3lzCnAgPSBQYXRoKCIvb3B0L2hlcm1lcy9oZXJtZXNfY2xpL2thbmJhbl9kYi5weSIpCmlmIG5vdCBwLmV4aXN0cygpOgogICAgc3lzLmV4aXQoMCkKc3JjID0gcC5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04IikKc2VudGluZWwgPSAiIyBodWdnaW5nbWVzLXdlYnVpOiBpZGVtcG90ZW50LWFsdGVyIgppZiBzZW50aW5lbCBpbiBzcmM6CiAgICBzeXMuZXhpdCgwKQpvbGQgPSAoCiAgICAnICAgIGNvbm4uZXhlY3V0ZShcbicKICAgICcgICAgICAgICJBTFRFUiBUQUJMRSB0YXNrcyBBREQgQ09MVU1OIGNvbnNlY3V0aXZlX2ZhaWx1cmVzICInCiAgICAnICAgICAgICAiSU5URUdFUiBOT1QgTlVMTCBERUZBVUxUIDAiJwogICAgJyAgICApJwopCm5ldyA9ICgKICAgIGYnICAgIHRyeTogIHtzZW50aW5lbH1cbicKICAgICcgICAgICAgIGNvbm4uZXhlY3V0ZShcbicKICAgICcgICAgICAgICAgICAiQUxURVIgVEFCTEUgdGFza3MgQUREIENPTFVNTiBjb25zZWN1dGl2ZV9mYWlsdXJlcyAiJwogICAgJyAgICAgICAgICAgICJJTlRFR0VSIE5PVCBOVUxMIERFRkFVTFQgMCInCiAgICAnICAgICAgICApXG4nCiAgICAnICAgIGV4Y2VwdCBFeGNlcHRpb246XG4nCiAgICAnICAgICAgICBwYXNzJwppZiBvbGQgaW4gc3JjOgogICAgcC53cml0ZV90ZXh0KHNyYy5yZXBsYWNlKG9sZCwgbmV3KSwgZW5jb2Rpbmc9InV0Zi04IikKICAgIHByaW50KCJrYW5iYW4gcGF0Y2g6IGFwcGxpZWQiKwpQWQoKIyBRdWlldCBoZXJtZXMtd2VidWkncyBwZXItcmVxdWVzdCBhY2Nlc3MgbG9nIG5vaXNlLgojIEJ5IGRlZmF1bHQgaXQgcHJpbnRzIGBbd2VidWldIHsidHMiOi4uLiJtZXRob2QiOi4uLn1gIGZvciBFVkVSWSByZXF1ZXN0LAojIHdoaWNoIGRyb3ducyB0aGUgSEYgTG9ncyB0YWIgb25jZSBhbnkgYnJvd3NlciB0YWIgaXMgb3BlbiBwb2xsaW5nCiMgL2FwaS9kYXNoYm9hcmQvc3RhdHVzLCAvYXBpL2hlYWx0aC9hZ2VudCwgL2FwaS9zZXNzaW9ucywgL3N3LmpzLCBldGMuCiMgUGF0Y2ggbG9nX3JlcXVlc3QoKSB0byBkcm9wIDJ4eCByZXNwb25zZXMgZm9yIGhpZ2gtZnJlcXVlbmN5IHBvbGwgcGF0aHMuCiMgRXJyb3JzIGFuZCBjaGF0L3N0cmVhbWluZyBwYXRocyBzdGlsbCBsb2cgbm9ybWFsbHkuClJVTiBweXRob24zIC0gPDwnUFknCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAppbXBvcnQgcmUKaW1wb3J0IHN5cwoKcCA9IFBhdGgoIi9vcHQvaGVybWVzLXdlYnVpL3NlcnZlci5weSIpCmlmIG5vdCBwLmV4aXN0cygpOgogICAgc3lzLmV4aXQoMCkKc3JjID0gcC5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04IikKc2VudGluZWwgPSAiIyBodWdnaW5nbWVzLXdlYnVpOiBxdWlldC1wb2xsLXBhdGhzIgppZiBzZW50aW5lbCBpbiBzcmM6CiAgICBzeXMuZXhpdCgwKQoKb2xkID0gKAogICAgIiAgICBkZWYgbG9nX3JlcXVlc3Qoc2VsZiwgY29kZTogc3RyPSctJywgc2l6ZTogc3RyPSctJykgLT4gTm9uZzpcbiIKICAgICIgICAgICAgIFwiXCJcIlN0cnVjdHVyZWQgSlNPTiBsb2dzIGZvciBlYWNoIHJlcXVlc3QuXCJcIlwiXG4iCiAgICAiICAgICAgICBpbXBvcnQganNvbiBhcyBfanNvblxuIgogICAgIiAgICAgICAgZHVyYXRpb25fbXMgPSByb3VuZCgodGltZS50aW1lKCkgLSBnZXRhdHRyKHNlbGYsICdfcmVxX3QwJywgdGltZS50aW1lKCkpKSAqIDEwMDAsIDEpXG4iCiAgICAiICAgICAgICByZWNvcmQgPSBfanNvbi5kdW1wcyh7XG4iCiAgICAiICAgICAgICAgICAgJ3RzJzogdGltZS5zdHJmdGltZSgnJVktJW0tJWRUJUglbTolJVN6JywgdGltZS5nbXRpbWUoKSksXG4iCiAgICAiICAgICAgICAgICAgJ21ldGhvZCc6IHNlbGYuY29tbWFuZCBvciAnLScsXG4iCiAgICAiICAgICAgICAgICAgJ3BhdGgnOiBzZWxmLnBhdGggb3IgJy0nLFxuIgogICAgIiAgICAgICAgICAgICdzdGF0dXMnOiBpbnQoY29kZSkgaWYgc3RyKGNvZGUpLmlzZGlnaXQoKSBlbHNlIGNvZGUsXG4iCiAgICAiICAgICAgICAgICAgJ21zJzogZHVyYXRpb25fbXMsXG4iCiAgICAiICAgICAgICB9KVxuIgogICAgIiAgICAgICAgcHJpbnQoZidbd2VidWldIHtyZWNvcmR9JywgZmx1c2g9VHJ1ZSkiCQoKbmV3ID0gKAogICAgIiAgICBfUVVJRVRfUE9MTF9QQVRIUyA9ICggICIgKyBzZW50aW5lbCArICJcbiIKICAgICIgICAgICAgICcvYXBpL2hlYWx0aC9hZ2VudCcsICcvYXBpL2Rhc2hib2FyZC9zdGF0dXMnLFxuIgogICAgIiAgICAgICAgJy9hcGkvZGFzaGJvYXJkL2NvbmZpZycsICcvYXBpL3Nlc3Npb25zJywgJy9hcGkvcHJvZmlsZXMnLFxuIgogICAgIiAgICAgICAgJy9hcGkvcHJvZmlsZS9hY3RpdmUnLCAnL2FwaS9vbmJvYXJkaW5nL3N0YXR1cycsXG4iCiAgICAiICAgICAgICAnL2FwaS9pbnNpZ2h0cycsICcvYXBpL3N5c3RlbS9oZWFsdGgnLFxuIgogICAgIiAgICAgICAgJy9hcGkvc2V0dGluZ3MnLCAnL2FwaS9wcm9qZWN0cycsICcvYXBpL3JlYXNvbmluZycsXG4iCiAgICAiICAgICAgICAnL2FwaS9tb2RlbHMnLCAnL2FwaS9jaGF0L3N0cmVhbS9zdGF0dXMnLFxuIgogICAgIiAgICAgICAgJy9hcGkvZ2l0LWluZm8nLCAnL3N3LmpzJywgJy9oZWFsdGgnLFxuIgogICAgIiAgICApXG4iCiAgICAiICAgIF9RVUlFVF9QUkVGSVhFUyA9ICgnL3N0YXRpYy8nLCAnL3Nlc3Npb24vc3RhdGljLycsICcvYXNzZXRzLycpXG4iCiAgICAiXG4iCiAgICAiICAgIGRlZiBsb2dfcmVxdWVzdChzZWxmLCBjb2RlOiBzdHI9Jy0nLCBzaXplOiBzdHI9Jy0nKSAtPiBOb25lOlxuIgogICAgIiAgICAgICAgXCJcIlwiU3RydWN0dXJlZCBKU09OIGxvZ3MgZm9yIGVhY2ggcmVxdWVzdCwgc2tpcHBpbmcgbm9pc3kgcG9sbHMuXCJcIlwiXG4iCiAgICAiICAgICAgICAjIEFsd2F5cyBsb2cgbm9uLTJ4eCBzbyA0MDEvNDA0LzV4eCByZW1haW4gdmlzaWJsZS5cbiIKICAgICIgICAgICAgIHRyeTpcbiIKICAgICIgICAgICAgICAgICBzdGF0dXNfaW50ID0gaW50KGNvZGUpIGlmIHN0cihjb2RlKS5pc2RpZ2l0KCkgZWxzZSAwXG4iCiAgICAiICAgICAgICBleGNlcHQgRXhjZXB0aW9uOlxuIgogICAgIiAgICAgICAgICAgIHN0YXR1c19pbnQgPSAwXG4iCiAgICAiICAgICAgICBwYXRoID0gKHNlbGYucGF0aCBvciAnJykuc3BsaXQoJz8nLCAxKVswXVxuIgogICAgIiAgICAgICAgaWYgMjAwIDw9IHN0YXR1c19pbnQgPCA0MDA6XG4iCiAgICAiICAgICAgICAgICAgaWYgcGF0aCBpbiBzZWxmLl9RVUlFVF9QT0xMX1BBVEhTOlxuIgogICAgIiAgICAgICAgICAgICAgICByZXR1cm5cbiIKICAgICIgICAgICAgICAgICBmb3IgcHJlZiBpbiBzZWxmLl9RVUlFVF9QUkVGSVhFUzpcbiIKICAgICIgICAgICAgICAgICAgICAgaWYgcGF0aC5zdGFydHN3aXRoKHByZWYpOlxuIgogICAgIiAgICAgICAgICAgICAgICAgICAgcmV0dXJuXG4iCiAgICAiICAgICAgICBpbXBvcnQganNvbiBhcyBfanNvblxuIgogICAgIiAgICAgICAgZHVyYXRpb25fbXMgPSByb3VuZCgodGltZS50aW1lKCkgLSBnZXRhdHRyKHNlbGYsICdfcmVxX3QwJywgdGltZS50aW1lKCkpKSAqIDEwMDAsIDEpXG4iCiAgICAiICAgICAgICByZWNvcmQgPSBfanNvbi5kdW1wcyh7XG4iCiAgICAiICAgICAgICAgICAgJ3RzJzogdGltZS5zdHJmdGltZSgnJVktJW0tJWRUJUglbTolJVN6JywgdGltZS5nbXRpbWUoKSksXG4iCiAgICAiICAgICAgICAgICAgJ21ldGhvZCc6IHNlbGYuY29tbWFuZCBvciAnLScsXG4iCiAgICAiICAgICAgICAgICAgJ3BhdGgnOiBzZWxmLnBhdGggb3IgJy0nLFxuIgogICAgIiAgICAgICAgICAgICdzdGF0dXMnOiBpbnQoY29kZSkgaWYgc3RyKGNvZGUpLmlzZGlnaXQoKSBlbHNlIGNvZGUsXG4iCiAgICAiICAgICAgICAgICAgJ21zJzogZHVyYXRpb25fbXMsXG4iCiAgICAiICAgICAgICB9KVxuIgogICAgIiAgICAgICAgcHJpbnQoZidbd2VidWldIHtyZWNvcmR9JywgZmx1c2g9VHJ1ZSkiCQoKaWYgb2xkIGluIHNyYzoKICAgIHAud3JpdGVfdGV4dChzcmMucmVwbGFjZShvbGQsIG5ldyksIGVuY29kaW5nPSJ1dGYtOCIpCiAgICBwcmludCgid2VidWkgbG9nLXF1aWV0IHBhdGNoOiBhcHBsaWVkIikKZWxzZToKICAgIHByaW50KCJ3ZWJ1aSBsb2ctcXVpZXQgcGF0Y2g6IHBhdHRlcm4gbm90IGZvdW5kLCBza2lwcGluZyIpClBZCgojIEZpeCBwZXJtaXNzaW9ucyBzbyBoZXJtZXMgdXNlciBjYW4gc2VsZi11cGRhdGUgcGFja2FnZXMKUlVOIGNob3duIC1SIGhlcm1lczpoZXJtZXMgL29wdC9oZXJtZXMvLnZlbnYKCiMgS2VlcCBoZXJtZXMgQ0xJIG9uIFBBVEggZm9yIGFsbCBzaGVsbCB0eXBlcyAobG9naW4vaW50ZXJhY3RpdmUvbm9uLWludGVyYWN0aXZlKQpSVU4gZWNobyAnZXhwb3J0IFBBVEg9Ii9vcHQvaGVybWVzLy52ZW52L2Jpbjovb3B0L2RhdGEvLmxvY2FsL2JpbjokUEFUSCInIFwKICAgID4gL2V0Yy9wcm9maWxlLmQvaGVybWVzLXZlbnYuc2gKCiMgQ3JlYXRlIGRpcmVjdG9yeSBmb3Igc2tpbiBzeW5jaW5nIApSVU4gbWtkaXIgLXAgJHtIRVJNRVNfSE9NRX0vc2tpbnMKClJVTiBjaG93biAtUiBoZXJtZXM6aGVybWVzICR7SEVSTUVTX0hPTUV9L3NraW5zCgpFTlYgSEVSTUVTX0hPTUU9L29wdC9kYXRhIFwKICAgIEhVR0dJTkdNRVNfQVBQX0RJUj0vb3B0L2h1Z2dpbmdtZXMgXAogICAgSEVSTUVTX1dFQlVJX1JFUE89L29wdC9oZXJtZXMtd2VidWkgXAogICAgSEVSTUVTX0FHRU5UX1ZFUlNJT049JHtIRVJNRVNfQUdFTlRfVkVSU0lPTn0gXAogICAgUFlUSE9OVU5CVUZGRVJFVD0xIFwKICAgIEhGX0hVQl9FTkFCTEVfSEZfVFJBTlNGRVI9MSBcCiAgICBQTEFZV1JJR0hUX0NIUk9NSVVNX0VYRUNVVEFCTEVfUEFUSD0vdXNyL2Jpbi9jaHJvbWl1bQpFWFBPU0UgNzg2MQpIRUFMVEhDSEVDSyAtLWludGVydmFsPTM0cyAtLXRpbWVvdXQ9NXMgLS1zdGFydC1wZXJpb2Q9MTI2cyBcClJVTiBjdXJsIC1mc1MgaHR0cDovL2xvY2FsaG9zdDo3ODYxL2hlYWx0aCB8fCBleGl0IDEKCiMgQ3JlYXRlIGRpcmVjdG9yeSBmb3Igc2tpbiBzeW5jaW5nIFUKUlVOIG1rZGlyIC1wICR7SEVSTUVTX0hPTUV9L3NraW5zIApSVU4gY2hvd24gLVIgaGVybWVzOmhlcm1lcyAke0hFUk1FU19IT01FfS9za2lucyA=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|