軽量Linux一覧でも紹介した「antiX」。Lubuntuよりも軽快で使いやすいんじゃないかと思ったので実用可能な状態までもっていってみました。
実際にCore 2 Duoでメモリ1GB、HDDのパソコンに入れてみましたが、ギリギリにはなりますけれど、YouTubeで動画再生も安定して行えるほどです。
ISOイメージをダウンロードしてインストール
実際のインストール手順です。
公式サイトからISOイメージをダウンロードして起動します。まずは日本語表示にします。

ちなみにUEFIではなくBIOS環境の場合はこの画面が出ませんのでF2キーを押して日本語表示にします。
キーボードもここでjpにしておくと良いでしょう。

メインメニューに戻ってインストールを進めます。

ライブ環境が立ち上がったらインストールを進めます。

基本的にはデフォルトのまま。対話中でもインストールが進むのであっという間に終わるはず。






インストール完了し起動
インストールが完了したらインストールメディアを取り外して起動します。
なお、antiXは初期化システム(initシステム)をデフォルトのrunitのほか、sysvinitやs6-rc、s6-66、dinitに切り替えて起動することも出来ます。

一応、特徴は下記ですが、デフォルトのrunitが最もトラブルが少なく、GUIのサービス管理ツールも充実しており使いやすいらしいです。
起動速度の比較
| initシステム | 速度順位 | 主な特徴 | 速度が速い/遅い理由 |
|---|---|---|---|
| dinit | 高速 | サービス依存関係の並列処理 | 完全に並列で無駄なく起動するため。 |
| s6-rc (s6) | 高速 | 超軽量・依存関係の並列処理 | コンパイルされた依存関係を最速で処理するため。 |
| runit | 普通 | 単純明快・1サービス1プロセス | 高速だが、複雑な依存関係の並列化は苦手。 |
| SysVinit | 低速 | 伝統的・シェルスクリプト主体 | サービスを1つずつ順番に起動するため。 |
各システムの速度特徴
dinit と s6-rc(最速グループ)
- dinit:C++で書かれており、サービスの依存関係(Aが起動した後にBを起動するなど)を自動で計算し、完全に並列(同時)起動します。無駄な待ち時間が一切ないため、圧倒的に速いです。
- s6-rc:非常に小さく洗練されたC言語のツール群です。起動前に依存関係をバイナリデータとして事前ビルドしておくため、起動時の計算コストがほぼゼロで超高速に動作します。
runit(高速グループ)
- runit:各サービスが独立して非常に素早く起動します。ただし、dinitやs6-rcのような「高度な依存関係の自動並列管理」が標準では備わっていません。そのため、複雑なシステムでは待ち時間が発生し、上記2つに一歩譲ることがあります。
SysVinit(低速グループ)
- SysVinit:数十年前からある古い仕組みです。重いシェルスクリプトを読み込み、サービスを1つずつ順番(直列)に起動していくため、現在のマルチコアCPUを活かせず、起動に最も時間がかかります。
起動後にまず日本語入力の設定
まずは日本語入力を可能にするためにターミナルを起動します。

ターミナルに下記スクリプトを貼り付けて実行します。説明には再ログイン後についても書いていますが、実行後に再起動すれば反映されるはず。
#!/bin/bash
set -euo pipefail
# ============================================================
# 日本語入力(ibus + Mozc)セットアップスクリプト
# 何度実行しても安全。初回はパッケージインストール+設定ファイル生成のみ行われ、
# 再ログイン後にもう一度実行するとエンジン設定(gsettings)が反映されます。
# ============================================================
# ------------------------------------------------------------
# 1. パッケージインストール
# ------------------------------------------------------------
sudo apt update
sudo apt install -y ibus ibus-mozc ibus-gtk ibus-gtk3 ibus-gtk4 fonts-noto-cjk
# ------------------------------------------------------------
# 2. ~/.desktop-session/desktop-session.conf に追記
# ------------------------------------------------------------
mkdir -p ~/.desktop-session
CONF_FILE=~/.desktop-session/desktop-session.conf
touch "$CONF_FILE"
CONF_BLOCK='im_module="ibus"
export GTK_IM_MODULE=$im_module
export QT_IM_MODULE=$im_module
export XMODIFIERS=@im=$im_module
DBUS_SESSION_LAUNCH="false"
eval $(dbus-launch --autolaunch $(cat /var/lib/dbus/machine-id) --sh-syntax --exit-with-session)'
if ! grep -qxF 'im_module="ibus"' "$CONF_FILE"; then
echo "$CONF_BLOCK" >> "$CONF_FILE"
echo "[RUN] desktop-session.conf に追記しました"
else
echo "[SKIP] desktop-session.conf は既に設定済みです"
fi
# ------------------------------------------------------------
# 3. ~/.desktop-session/startup に追記
# ------------------------------------------------------------
STARTUP_FILE=~/.desktop-session/startup
touch "$STARTUP_FILE"
if ! grep -qxF 'ibus-daemon -drx' "$STARTUP_FILE"; then
echo 'ibus-daemon -drx' >> "$STARTUP_FILE"
echo "[RUN] startup に追記しました"
else
echo "[SKIP] startup は既に設定済みです"
fi
# ------------------------------------------------------------
# 4. Mozc起動時のデフォルト入力モードを「ひらがな」に設定
# ------------------------------------------------------------
MOZC_CONF_DIR=~/.config/mozc
MOZC_CONF_FILE="$MOZC_CONF_DIR/ibus_config.textproto"
mkdir -p "$MOZC_CONF_DIR"
if [ -f "$MOZC_CONF_FILE" ] && grep -q "active_on_launch: True" "$MOZC_CONF_FILE"; then
echo "[SKIP] Mozcのひらがなデフォルト設定は既に反映済みです"
else
cat > "$MOZC_CONF_FILE" << 'EOF'
engines {
name : "mozc-jp"
longname : "Mozc"
layout : "default"
layout_variant : ""
layout_option : ""
rank : 80
}
active_on_launch: True
EOF
echo "[RUN] Mozcのひらがなデフォルト設定を書き込みました"
fi
if [ -f ~/.mozc/ibus_config.textproto ]; then
echo "[警告] ~/.mozc/ibus_config.textproto が存在します。こちらが優先されるため、内容を確認してください"
fi
# ------------------------------------------------------------
# 5. 入力エンジンを Mozc / 英語(US) の2つのみに設定(要dbusセッション)
# ------------------------------------------------------------
if gsettings set org.freedesktop.ibus.general preload-engines "['mozc-jp', 'xkb:us::eng']" 2>/dev/null; then
gsettings set org.freedesktop.ibus.general engines-order "['mozc-jp', 'xkb:us::eng']"
echo "[RUN] 入力エンジンを Mozc / 英語(US) の2つに設定しました"
# ------------------------------------------------------------
# 6. 切り替えキーに「半角/全角」を追加(Control+spaceは維持)
# ------------------------------------------------------------
CURRENT_TRIGGERS=$(gsettings get org.freedesktop.ibus.general.hotkey trigger)
if echo "$CURRENT_TRIGGERS" | grep -q "Zenkaku_Hankaku"; then
echo "[SKIP] 半角/全角キーは既に設定済みです"
else
gsettings set org.freedesktop.ibus.general.hotkey trigger "['Control+space', 'Zenkaku_Hankaku']"
echo "[RUN] 切り替えキーに半角/全角を追加しました"
fi
ibus write-cache || echo "[警告] ibus write-cache に失敗しました(無視して続行)"
ibus restart || echo "[警告] ibus restart に失敗しました(無視して続行)"
else
echo ""
echo "[SKIP] gsettings によるエンジン/キー設定は今回スキップされました"
echo " (ibusのdbusセッションがまだ起動していないためと思われます)"
echo " 再ログイン後、このスクリプトをもう一度実行してください"
fi
echo ""
echo "=================================================="
echo " セットアップ完了"
echo " ・パッケージ / desktop-session設定 / Mozcひらがなデフォルト: 反映済み"
echo " ・入力エンジン(2つ限定)とキー設定: dbusセッションが有効な場合のみ反映済み"
echo " 反映を確実にするため、一度ログアウト→再ログイン(またはXの再起動)してください"
echo "=================================================="
再起動したら、テキストエディタなどを開いて確認してみてください。
ちなみに、下記で不要なエンジン削除などについて書いていますが、先ほどのスクリプトで2つになっているはずで、作業は不要なはずです。「半角/全角」でも切り替えられると思いますが、切り替えられない場合や別のキーで切り替えたい場合はどは設定を。

アイコンを右クリックして「設定」を押します。

「入力メソッド」で不要なものを削除します。また「半角/全角」で切り替えられるようにしておくと便利です。

これで日本語入力出来るようになりました。

ちなみに不要なキーボードレイアウトは「コントロールセンター」-「システム」の「システムキーボードのレイアウト設定」で削除出来ます。

Mac用に
MacでVNC経由で接続している時に半角/全角がなく、英数、かなキーが送信されない場合用に。Caps Lockキーで切り替えます。
#!/bin/bash
# Macキーボード用 キーボード設定スクリプト
# - Caps Lockキー → Eisu_toggle (ibus入力切替)
# - ibus triggersにEisu_toggle/Muhenkanを追加
# - Caps Lock初期状態をOFFに
# 1. Caps Lock位置をEisu_toggleにマッピング
xmodmap -e "keycode 66 = Eisu_toggle" 2>/dev/null
# 2. ibus triggers設定(英数・かな・Shift+Space)
gsettings set org.freedesktop.ibus.general.hotkey triggers \
"['Hankaku', 'Eisu_toggle', 'Muhenkan', '<Shift>space']" 2>/dev/null
# 3. ibus preload-engines設定
gsettings set org.freedesktop.ibus.general preload-engines \
"['mozc-jp', 'xkb:us::eng']" 2>/dev/null
# 4. Caps LockをOFFに
if xset q 2>/dev/null | grep -q "Caps Lock:.*on"; then
xdotool key 66 2>/dev/null
fi
LibreOfficeの日本語化
LibreOfficeのメニューが英語なので日本語化します。ターミナルで下記を実行するだけ。
sudo apt install libreoffice-l10n-ja
完了すればLibreOfficeを起動してみましょう。メニューが日本語化されているはずです。

Google Chromeのインストール
Google Chromeを使いたい場合は下記を実行。
#!/bin/bash
set -euo pipefail
# 必要なパッケージを準備
sudo apt update
sudo apt install -y curl gnupg
# GoogleのGPGキーを追加
curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/google-chrome.gpg
# リポジトリを追加
echo "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/google-chrome.gpg] https://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
# インストール
sudo apt update
sudo apt install -y google-chrome-stable
# 起動
google-chrome-stable

Tailscaleのインストール
Tailscaleを利用するなら下記を実行。
#!/bin/bash
set -euo pipefail
# ===== 1. 必要パッケージの準備 =====
sudo apt update
sudo apt install -y curl
# ===== 2. Tailscale インストール =====
curl -fsSL https://tailscale.com/install.sh | sh
# ===== 3. tailscaled 実行パスを確認 =====
TAILSCALED_BIN=$(which tailscaled)
echo "tailscaled のパス: $TAILSCALED_BIN"
# ===== 4. 状態保存用ディレクトリ =====
sudo mkdir -p /var/lib/tailscale
# ===== 5. runit用サービスディレクトリを作成 =====
sudo mkdir -p /etc/sv/tailscaled
sudo tee /etc/sv/tailscaled/run > /dev/null << EOF
#!/bin/sh
exec ${TAILSCALED_BIN} --state=/var/lib/tailscale/tailscaled.state --socket=/run/tailscale/tailscaled.sock 2>&1
EOF
sudo chmod +x /etc/sv/tailscaled/run
# ===== 6. サービス有効化(自動起動設定) =====
sudo ln -sf /etc/sv/tailscaled /etc/service/tailscaled
# ===== 7. 起動待機 & 状態確認 =====
sleep 2
sudo sv status tailscaled
echo ""
echo "=================================================="
echo "tailscaled の起動が確認できたら、以下を実行してログイン認証してください:"
echo ""
echo " sudo tailscale up"
echo ""
echo "表示されたURLをブラウザで開いて認証すれば完了です。"
echo "=================================================="
これでクライアントマシンとしては十分使えるんじゃないでしょうか。
xrdpでリモートデスクトップを有効化
別セッションですし日本語入力が反映されないし、まだ改良の余地はありそうですが。ただ、標準でSSHは有効ですし、そんなに必要性を感じていないので、これ以上は特に追わず一応メモ程度に残しておくだけにしておきます。
#!/bin/bash
set -euo pipefail
# ===== 1. 必要パッケージインストール =====
# xorgxrdp: Xorgバックエンド用モジュール
# tigervnc-standalone-server: Xorgが失敗した場合のXvncフォールバック用
sudo apt update
sudo apt install -y xrdp xorgxrdp tigervnc-standalone-server
# ===== 2. デスクトップ環境の指定(antiX標準のIceWMを起動) =====
# ~/.xsession が無いとxrdpログイン後に何も表示されないことがあるため明示的に指定
cat > ~/.xsession << 'EOF'
exec icewm-session
EOF
chmod +x ~/.xsession
# ===== 3. バイナリパスを確認 =====
XRDP_BIN=$(which xrdp)
SESMAN_BIN=$(which xrdp-sesman)
echo "xrdp のパス: $XRDP_BIN"
echo "xrdp-sesman のパス: $SESMAN_BIN"
# ===== 4. runit用サービスが自動生成されているか確認、無ければ作成 =====
if [ -L /etc/service/xrdp ] && [ -L /etc/service/xrdp-sesman ]; then
echo "runitサービスは既に有効化されています"
else
echo "runitサービスを作成します"
# --- xrdp-sesman(先に起動する必要がある) ---
sudo mkdir -p /etc/sv/xrdp-sesman
sudo tee /etc/sv/xrdp-sesman/run > /dev/null << EOF
#!/bin/sh
exec ${SESMAN_BIN} --nodaemon
EOF
sudo chmod +x /etc/sv/xrdp-sesman/run
sudo ln -sf /etc/sv/xrdp-sesman /etc/service/xrdp-sesman
# --- xrdp本体 ---
sudo mkdir -p /etc/sv/xrdp
sudo tee /etc/sv/xrdp/run > /dev/null << EOF
#!/bin/sh
exec ${XRDP_BIN} --nodaemon
EOF
sudo chmod +x /etc/sv/xrdp/run
sudo ln -sf /etc/sv/xrdp /etc/service/xrdp
fi
# ===== 5. 重複プロセス・残留pidの整理(再実行時の衝突防止) =====
sudo sv stop xrdp || true
sudo sv stop xrdp-sesman || true
sleep 1
sudo pkill -f xrdp-sesman || true
sudo pkill -f "xrdp$" || true
sudo pkill -f "Xorg.*:10" || true
sudo rm -f /var/run/xrdp/xrdp.pid
sleep 1
# ===== 6. サービス起動 =====
sudo sv start xrdp-sesman
sudo sv start xrdp
# ===== 7. 起動確認 =====
sleep 2
sudo sv status xrdp-sesman
sudo sv status xrdp
echo ""
echo "=================================================="
echo "セットアップ完了。Windowsのリモートデスクトップ接続から"
echo "Tailscale IP(tailscale ip -4 で確認)を指定して接続してください。"
echo "=================================================="
ちなみにシステムの解像度の設定などは「コントロールセンター」-「セッション」-「画面解像度の設定」にあります。

VS code
# 必要なパッケージの準備
sudo apt update
sudo apt install -y wget gpg apt-transport-https
# MicrosoftのGPGキーを登録
wget -qO- https://packages.microsoft.com/keys/microsoft.asc \
| gpg --dearmor \
| sudo tee /usr/share/keyrings/microsoft.gpg > /dev/null
# リポジトリを追加
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.gpg] \
https://packages.microsoft.com/repos/code stable main" \
| sudo tee /etc/apt/sources.list.d/vscode.list
# VS Code をインストール
sudo apt update
sudo apt install -y code
# 日本語パックをインストール
code --install-extension MS-CEINTL.vscode-language-pack-ja
# VS Codeを起動
code
code-server
VS codeではなくcode-serverにするならこちら。
sudo mkdir -p /opt/script
sudo chown $USER:$USER /opt/script
chmod u+rwx /opt/script
cd /opt/script
nano install-codeserver.sh
chmod +x install-codeserver.sh
sudo bash install-codeserver.sh
#!/bin/bash
# ============================================================
# Code-Server インストールスクリプト v3
# antiX / runit / Tailscale HTTPS 対応
#
# 改善点(v2 から):
# - root レベル runit サービスで再起動後も確実に起動
# - 既存インストール検出 & 上書き確認
# - サービス起動の自動リトライ
# - ロールバック機能(失敗時クリーンアップ)
#
# 構成:
# - code-server を 127.0.0.1:8089 で待受
# - Tailscale serve で tailnet 内 HTTPS 公開
# - Japanese Language Pack 自動インストール
#
# 前提:
# - tailscale up 済み
# - 実行: sudo bash install-codeserver3.sh
#
# 使い方:
# sudo bash install-codeserver3.sh # 対話モード
# sudo bash install-codeserver3.sh -p mypass # パスワード指定
# sudo bash install-codeserver3.sh -a # 自動生成
# sudo bash install-codeserver3.sh -y # 確認なし
# sudo bash install-codeserver3.sh -p mypass -P 9090 -u vscode
# ============================================================
set -euo pipefail
# --- 定数 ---
VERSION="3.0.0"
SV_DIR="/etc/sv/code-server"
SVC_LINK="/etc/service/code-server"
WRAPPER="/usr/local/bin/code-server-wrapper"
CODE_SERVER_BIN="/usr/bin/code-server"
LOG_FILE="/var/log/codeserver-install.log"
# --- カラー ---
GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m'
RED='\033[0;31m'; DIM='\033[2m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERR]${NC} $*" >&2; exit 1; }
log() { echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; }
# --- オプション解析 ---
PASSWORD=""
CODE_SERVER_PORT=8089
REAL_USER=""
AUTO_PASS=false
YES_MODE=false
usage() {
cat <<EOF
Usage: sudo bash $0 [OPTIONS]
Options:
-p, --password PASS code-server のパスワードを指定
-P, --port PORT 待受ポート(デフォルト: 8089)
-u, --user USER 対象ユーザー(デフォルト: 実行ユーザー)
-a, --auto-password パスワードを自動生成
-y, --yes 確認プロンプトをスキップ
-h, --help ヘルプ表示
Examples:
sudo bash $0 # 対話モード
sudo bash $0 -a # パスワード自動生成
sudo bash $0 -p mypassword # パスワード指定
sudo bash $0 -p pass -P 9090 -u bob # 全パラメータ指定
sudo bash $0 -a -y # 自動生成・確認なし
EOF
exit 0
}
while [[ $# -gt 0 ]]; do
case "$1" in
-p|--password) PASSWORD="$2"; shift 2 ;;
-P|--port) CODE_SERVER_PORT="$2"; shift 2 ;;
-u|--user) REAL_USER="$2"; shift 2 ;;
-a|--auto-password) AUTO_PASS=true; shift ;;
-y|--yes) YES_MODE=true; shift ;;
-h|--help) usage ;;
*) error "不明なオプション: $1(--help で確認)" ;;
esac
done
# --- 前処理 ---
[[ $EUID -ne 0 ]] && error "root権限で実行してください: sudo bash $0"
REAL_USER="${REAL_USER:-${SUDO_USER:-${USER:-$(id -un)}}}"
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
# ログファイル初期化
: > "$LOG_FILE"
log "Installation started (v${VERSION})"
# ============================================================
# ロールバック: 失敗時にクリーンアップ
# ============================================================
cleanup_on_error() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
warn "インストール中にエラーが発生しました(終了コード: ${exit_code})"
log "ERROR: Installation failed with exit code ${exit_code}"
# 部分的に作成されたサービスを停止
sv stop code-server 2>/dev/null || true
rm -f "$SVC_LINK" 2>/dev/null || true
error "インストールに失敗しました。ログ: ${LOG_FILE}"
fi
}
trap cleanup_on_error EXIT
# ============================================================
# 0. 既存インストールの確認
# ============================================================
check_existing() {
if command -v code-server &>/dev/null; then
EXISTING_VER=$(code-server --version 2>/dev/null | head -1 || echo "unknown")
info "code-server が既にインストールされています: ${EXISTING_VER}"
if sv status code-server 2>/dev/null | grep -q "^run:"; then
info "現在稼働中です。"
fi
if [[ "$YES_MODE" == false ]]; then
echo -e " ${YELLOW}[R]${NC} 再インストール(上書き)"
echo -e " ${YELLOW}[K]${NC} キャンセル"
echo ""
read -r -p "選択 [R/K] (Enterでキャンセル): " choice
case "${choice^^}" in
R) info "再インストールを実行します..." ;;
*) info "キャンセルしました。"; exit 0 ;;
esac
fi
# 既存サービスを停止
sv stop code-server 2>/dev/null || true
rm -f "$SVC_LINK" 2>/dev/null || true
log "Existing installation detected, reinstalling"
fi
}
# ============================================================
# 1. パスワード設定
# ============================================================
prompt_password() {
if [[ -n "$PASSWORD" ]]; then
success "コマンドライン引数でパスワードを指定しました"
return
fi
if [[ "$AUTO_PASS" == true ]]; then
PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
success "パスワードを自動生成しました"
return
fi
echo ""
echo -e "${CYAN}╔══════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Code-Server + Tailscale Serve セットアップ ║${NC}"
echo -e "${CYAN}║ v${VERSION} antiX / runit / tailnet 内 HTTPS 公開 ║${NC}"
echo -e "${CYAN}║ 日本語化セットアップ済み ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "インストール対象ユーザー: ${YELLOW}${REAL_USER}${NC} (${REAL_HOME})"
echo ""
echo "code-server のパスワードを設定してください。"
echo -e " ${CYAN}[A]${NC} 自動生成する(ランダム32文字)"
echo -e " ${CYAN}[M]${NC} 手動で入力する"
echo ""
read -r -p "選択 [A/M] (Enterで手動入力): " choice
case "${choice^^}" in
A)
PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
success "パスワードを自動生成しました。(後で表示します)"
;;
*)
while true; do
read -r -s -p "パスワードを入力してください: " PASSWORD
echo ""
read -r -s -p "もう一度入力してください: " PASSWORD2
echo ""
if [[ "$PASSWORD" == "$PASSWORD2" && -n "$PASSWORD" ]]; then
success "パスワードを設定しました。"
break
else
warn "パスワードが一致しないか空です。もう一度入力してください。"
fi
done
;;
esac
echo ""
}
# ============================================================
# 2. 前提条件チェック
# ============================================================
check_prerequisites() {
info "前提条件を確認中..."
command -v tailscale &>/dev/null \
|| error "tailscale がインストールされていません。先に tailscale をセットアップしてください。"
tailscale status &>/dev/null \
|| error "tailscale が起動していません。'tailscale up' を先に実行してください。"
command -v apt-get &>/dev/null \
|| error "apt-get が見つかりません。Debian/Ubuntu 系のシステムが必要です。"
success "前提条件 OK"
}
# ============================================================
# 3. 依存パッケージ
# ============================================================
install_deps() {
info "依存パッケージをインストール中..."
apt-get update -qq
apt-get install -y -qq curl wget openssl
success "依存パッケージ完了"
}
# ============================================================
# 4. code-server インストール
# ============================================================
install_code_server() {
info "最新バージョンを確認中..."
LATEST=$(curl -fsSL https://api.github.com/repos/coder/code-server/releases/latest \
| grep '"tag_name"' | sed 's/.*"v\([^"]*\)".*/\1/')
info "最新バージョン: v${LATEST}"
DEB_URL="https://github.com/coder/code-server/releases/download/v${LATEST}/code-server_${LATEST}_amd64.deb"
info "ダウンロード中..."
wget -q --show-progress -O /tmp/code-server.deb "$DEB_URL"
dpkg -i /tmp/code-server.deb || apt-get -f install -y
rm -f /tmp/code-server.deb
success "code-server v${LATEST} インストール完了"
log "Installed code-server v${LATEST}"
}
# ============================================================
# 5. Japanese Language Pack
# ============================================================
install_japanese_language_pack() {
info "Japanese Language Pack をインストール中..."
EXT_DIR="$REAL_HOME/.local/share/code-server/extensions"
mkdir -p "$EXT_DIR"
chown -R "$REAL_USER:$REAL_USER" "$REAL_HOME/.local/share/code-server"
sudo -u "$REAL_USER" code-server --install-extension MS-CEINTL.vscode-language-pack-ja \
|| warn "Japanese Language Pack のインストールに失敗しました(後で手動インストール可能)"
success "Japanese Language Pack 完了"
}
# ============================================================
# 6. 設定ファイル生成
# ============================================================
setup_config() {
info "config.yaml を生成中..."
CONFIG_DIR="$REAL_HOME/.config/code-server"
mkdir -p "$CONFIG_DIR"
cat > "$CONFIG_DIR/config.yaml" <<EOF
bind-addr: 127.0.0.1:${CODE_SERVER_PORT}
auth: password
password: ${PASSWORD}
cert: false
EOF
chown -R "$REAL_USER:$REAL_USER" "$CONFIG_DIR"
chmod 600 "$CONFIG_DIR/config.yaml"
success "config.yaml 生成完了(127.0.0.1:${CODE_SERVER_PORT})"
info "settings.json(日本語化 + 推奨設定)を配置中..."
VSCODE_USER_DIR="$REAL_HOME/.local/share/code-server/User"
mkdir -p "$VSCODE_USER_DIR"
cat > "$REAL_HOME/.local/share/code-server/argv.json" <<'EOF'
{
"locale": "ja"
}
EOF
cat > "$VSCODE_USER_DIR/locale.json" <<'EOF'
{
"locale": "ja"
}
EOF
cat > "$VSCODE_USER_DIR/settings.json" <<'EOF'
{
"editor.fontSize": 14,
"editor.tabSize": 2,
"editor.formatOnSave": true,
"editor.minimap.enabled": false,
"workbench.colorTheme": "Default Dark Modern",
"terminal.integrated.fontSize": 13,
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000
}
EOF
chown -R "$REAL_USER:$REAL_USER" "$REAL_HOME/.local/share/code-server"
success "settings.json / argv.json / locale.json 配置完了"
}
# ============================================================
# 7. runit サービス(root レベル)
# ============================================================
setup_runit() {
info "runit サービスを登録中(root レベル)..."
NLS_CONFIG='{"locale":"ja","osLocale":"ja","availableLanguages":{"*":"ja"}}'
cat > "$WRAPPER" <<WEOF
#!/bin/bash
export HOME="${REAL_HOME}"
export VSCODE_NLS_CONFIG='${NLS_CONFIG}'
exec ${CODE_SERVER_BIN} "\$@"
WEOF
chmod +x "$WRAPPER"
mkdir -p "$SV_DIR"
cat > "$SV_DIR/run" <<'EOF'
#!/bin/bash
exec 2>&1
exec /usr/local/bin/code-server-wrapper
EOF
chmod +x "$SV_DIR/run"
ln -sfn "$SV_DIR" "$SVC_LINK"
# 既存の user レベルサービスを削除
rm -rf "$REAL_HOME/.runit/service/code-server" "$REAL_HOME/.runit/usersv/code-server" 2>/dev/null || true
# 起動確認 with リトライ
info "サービス起動確認中..."
local retry=0
local max_retry=5
while [[ $retry -lt $max_retry ]]; do
sleep 2
if sv status code-server 2>/dev/null | grep -q "^run:"; then
success "code-server 正常起動(root レベル runit)"
log "runit service started successfully"
return 0
fi
retry=$((retry + 1))
if [[ $retry -lt $max_retry ]]; then
warn "起動確認リトライ (${retry}/${max_retry})..."
sv start code-server 2>/dev/null || true
fi
done
warn "自動起動確認に失敗しました。手動で起動してください: sv start code-server"
warn "ログ確認: sv status code-server -v"
}
# ============================================================
# 8. Tailscale serve 設定
# ============================================================
setup_tailscale_serve() {
info "Tailscale serve に code-server (port ${CODE_SERVER_PORT}) を追加中..."
# 既存の serve 設定があれば削除してから再設定
tailscale serve --https="${CODE_SERVER_PORT}" off 2>/dev/null || true
tailscale serve --bg --https=${CODE_SERVER_PORT} "http://127.0.0.1:${CODE_SERVER_PORT}"
success "Tailscale serve 設定完了"
TS_HOSTNAME=$(tailscale status --json 2>/dev/null | python3 -c "
import sys, json
try:
d = json.load(sys.stdin)
s = d.get('Self', {})
dns = s.get('DNSName', '').rstrip('.')
print(dns if dns else s.get('HostName', 'unknown'))
except:
print('unknown')
" 2>/dev/null || echo "unknown")
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN} [OK] セットアップ完了! (v${VERSION})${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e " アクセスURL(tailnet 内のデバイスからのみアクセス可):"
echo -e " ${CYAN}https://${TS_HOSTNAME}:${CODE_SERVER_PORT}${NC}"
echo ""
echo -e " code-server パスワード: ${YELLOW}${PASSWORD}${NC}"
echo ""
echo -e " 日本語化: インストール時に自動設定済み(再起動不要)"
echo ""
echo -e " 管理コマンド:"
echo -e " 再起動: sv restart code-server"
echo -e " 停止: sv stop code-server"
echo -e " ステータス: sv status code-server"
echo -e " ログ: tail -f /var/log/code-server/current"
echo -e " serve 確認: tailscale serve status"
echo -e " serve 削除: tailscale serve --https=${CODE_SERVER_PORT} off"
echo ""
echo -e " [!] このパスワードを安全な場所に保管してください。"
echo -e " 設定ファイル: ${REAL_HOME}/.config/code-server/config.yaml"
echo ""
log "Installation completed successfully"
}
# ============================================================
# メイン
# ============================================================
main() {
check_existing # 既存インストール確認
prompt_password # ① パスワード設定
check_prerequisites # ② Tailscale確認
install_deps # ③ 依存パッケージ
install_code_server # ④ code-server インストール
install_japanese_language_pack # ⑤ 日本語パック
setup_config # ⑥ 設定ファイル
setup_runit # ⑦ runit 登録(root レベル)
setup_tailscale_serve # ⑧ Tailscale serve 設定
}
main "$@"
日本語化
>Configure Display Language
Incus
デフォルトプールを変更してインストールしたほうが管理しやすいかも。
sudo mkdir -p /opt/script
sudo chown $USER:$USER /opt/script
chmod u+rwx /opt/script
cd /opt/script
nano install-incus1.sh
chmod +x install-incus1.sh
sudo bash install-incus1.sh
#!/usr/bin/env bash
set -euo pipefail
# Incus Installation Script for Debian 13 (trixie)
# Usage: bash install-incus1.sh
log() { echo -e "\e[1;32m[INFO]\e[0m $*"; }
warn() { echo -e "\e[1;33m[WARN]\e[0m $*"; }
err() { echo -e "\e[1;31m[ERROR]\e[0m $*" >&2; exit 1; }
[[ $EUID -eq 0 ]] || err "Run as root."
log "Checking OS..."
. /etc/os-release
[[ "${VERSION_CODENAME:-}" == "trixie" ]] || warn "Designed for Debian 13 (trixie). Proceeding anyway."
log "Updating package lists..."
apt-get update -y
log "Installing incus-base and incus-client..."
apt-get install -y incus-base incus-client
log "Enabling and starting incus.service..."
systemctl enable --now incus.service
log "Adding current user ($SUDO_USER) to incus group..."
usermod -aG incus "${SUDO_USER:-root}"
log "Initializing Incus (default storage: dir, default bridge: incusbr0)..."
incus admin init --minimal || warn "Init may have already been done."
log "Incus installation complete."
incus version
echo
incus storage list
echo
incus network list
デフォルトプールを変更してインストール
sudo mkdir -p /opt/script
sudo chown $USER:$USER /opt/script
chmod u+rwx /opt/script
cd /opt/script
nano install-incus2.sh
chmod +x install-incus2.sh
sudo bash install-incus2.sh
#!/usr/bin/env bash
# =============================================================================
# Incus セットアップスクリプト (antiX / Debian trixie ベース)
# - Zabblyリポジトリ不要: Debian trixie本体 + trixie-backports から取得
# - デフォルトストレージプールを /opt/incuspool に変更
# - 何度実行しても安全 (既存設定はスキップ)
# =============================================================================
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
[[ $EUID -eq 0 ]] || error "root で実行してください (sudo bash $0)"
REAL_USER="${SUDO_USER:-root}"
POOL_DIR="/opt/incuspool"
# =============================================================================
# 0. OS チェック
# =============================================================================
info "=== OS 確認 ==="
. /etc/os-release
if [[ "${VERSION_CODENAME:-}" != "trixie" ]]; then
warn "trixie 向けスクリプトです (検出: ${VERSION_CODENAME:-不明})。続行します"
else
success "Debian trixie を確認しました"
fi
info "systemd は使用しません (runit-antix 環境として動作します)"
# =============================================================================
# 1. Incus インストール (Debian trixie本体 / 可能なら trixie-backports)
# =============================================================================
info "=== Incus パッケージのインストール ==="
apt-get update -qq
if dpkg -s incus-base &>/dev/null; then
success "incus-base は既にインストール済みです ($(dpkg-query -W -f='${Version}' incus-base))"
dpkg -s dnsmasq-base &>/dev/null || apt-get install -y dnsmasq-base
else
info "パッケージを検索します..."
APT_TARGET=""
if apt-cache policy incus-base 2>/dev/null | grep -q "trixie-backports"; then
APT_TARGET="-t trixie-backports"
info "trixie-backports から新しいバージョンを取得します"
fi
if ! apt-get install -y ${APT_TARGET} incus-base incus-client dnsmasq-base; then
warn "backports指定でのインストールに失敗、通常リポジトリで再試行します"
apt-get install -y incus-base incus-client dnsmasq-base
fi
fi
INCUS_VER=$(dpkg-query -W -f='${Version}' incus-base 2>/dev/null || echo "不明")
success "Incus ${INCUS_VER} インストール完了"
# =============================================================================
# 2. ユーザーを incus-admin グループに追加
# =============================================================================
info "=== ユーザー設定 ==="
if [[ "$REAL_USER" != "root" ]]; then
if id -nG "$REAL_USER" | grep -qw incus-admin; then
success "$REAL_USER は既に incus-admin グループのメンバーです"
else
usermod -aG incus-admin "$REAL_USER"
success "$REAL_USER を incus-admin グループに追加しました"
NEED_RELOGIN=true
fi
fi
# =============================================================================
# 3. サービス起動
# =============================================================================
info "=== Incus サービスの起動確認 ==="
# antiX (runit-antix) では incus-base パッケージの postinst が
# runit と連携してデーモンを自動起動するため、systemctl は使わない。
# 念のため未起動なら runit/init.d/service へのフォールバックのみ試みる。
is_incus_up() { incus list &>/dev/null; }
if ! is_incus_up; then
warn "デーモンが自動起動していないようです。手動起動を試みます"
if command -v sv &>/dev/null && [[ -d /etc/sv/incus ]]; then
sv start incus || true
elif [[ -x /etc/init.d/incus ]]; then
/etc/init.d/incus start || true
elif command -v service &>/dev/null; then
service incus start || true
fi
fi
for i in {1..15}; do
if is_incus_up; then break; fi
sleep 2
info "起動待機中... ($i/15)"
done
is_incus_up || error "incus デーモンが起動しませんでした"
success "Incus サービス起動確認"
# =============================================================================
# 4. 初期化 (未初期化の場合のみ)
# =============================================================================
info "=== Incus 初期化 ==="
if incus storage list --format csv 2>/dev/null | grep -q .; then
success "Incus は既に初期化済みです"
else
info "Incus を初期化します (--minimal)..."
incus admin init --minimal
fi
# =============================================================================
# 4b. ネットワークブリッジの確認・修復
# =============================================================================
info "=== ネットワーク設定 ==="
if incus network list --format csv 2>/dev/null | grep -q '^incusbr0,'; then
success "ネットワークブリッジ incusbr0 は既に存在します"
else
info "ネットワークブリッジ incusbr0 を作成します..."
incus network create incusbr0
fi
if incus profile device list default 2>/dev/null | grep -q '^eth0$'; then
success "default プロファイルに eth0 は既に設定済みです"
else
info "default プロファイルに eth0 (incusbr0 経由) を追加します..."
incus profile device add default eth0 nic network=incusbr0 name=eth0
fi
# =============================================================================
# 5. ストレージプールを /opt/incuspool に変更
# =============================================================================
info "=== ストレージプール設定 ==="
mkdir -p "$POOL_DIR"
CURRENT_SOURCE=$(incus storage get default source 2>/dev/null || echo "")
if [[ "$CURRENT_SOURCE" == "$POOL_DIR" ]]; then
success "ストレージプールは既に ${POOL_DIR} を向いています"
else
info "ストレージプールを ${POOL_DIR} に変更します..."
# default プールを参照しているプロファイルデバイスを先に外す
incus profile device remove default root 2>/dev/null || true
incus storage delete default 2>/dev/null || true
incus storage create default dir source="$POOL_DIR"
incus profile device add default root disk path=/ pool=default
success "ストレージプールを ${POOL_DIR} に変更しました"
fi
# =============================================================================
# 完了
# =============================================================================
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo -e "${GREEN} Incus セットアップ完了 (antiX / Debian trixie)${NC}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo " ストレージプール: ${POOL_DIR}"
echo ""
if [[ "${NEED_RELOGIN:-false}" == "true" ]]; then
echo -e "${YELLOW} ※ グループ反映のためログアウトして再ログインしてください${NC}"
echo " または: newgrp incus-admin"
echo ""
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
Easy Incus
sudo mkdir -p /opt/script
sudo chown $USER:$USER /opt/script
chmod u+rwx /opt/script
cd /opt/script
nano install-easyincus1.sh
chmod +x install-easyincus1.sh
sudo bash install-easyincus1.sh
#!/bin/bash
set -euo pipefail
INSTALL_DIR="/opt/easy-incus"
PORT=3329
echo "=== Easy Incus UI Installer v2 ==="
echo ""
# --- Node.js ---
NODE_PATH=""
if command -v node &>/dev/null; then
NODE_PATH=$(command -v node)
echo "Node: $(node -v) ($NODE_PATH)"
else
for candidate in /usr/local/bin/node /usr/bin/node "$HOME/.nvm/versions/node"/*/bin/node /root/.nvm/versions/node/*/bin/node; do
if [ -x "$candidate" ]; then NODE_PATH="$candidate"; echo "Node (found): $NODE_PATH"; break; fi
done
fi
if [ -z "$NODE_PATH" ]; then
echo "Node.js が見つかりません。インストールします..."
command -v curl &>/dev/null || apt-get install -y curl
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
apt-get install -y nodejs
NODE_PATH=$(command -v node)
fi
if [[ "$NODE_PATH" == *"/.nvm/"* ]]; then
ln -sf "$NODE_PATH" /usr/local/bin/node
NODE_PATH=/usr/local/bin/node
fi
[ ! -x "$NODE_PATH" ] && { echo "ERROR: node のインストールに失敗"; exit 1; }
# --- npm ---
if ! command -v npm &>/dev/null; then
echo "npm が見つかりません。インストールします..."
apt-get install -y npm
fi
echo "npm: $(npm -v)"
# --- build tools for node-pty ---
if ! dpkg -s build-essential &>/dev/null 2>&1; then
echo "build-essential をインストール中..."
apt-get install -y build-essential python3
fi
# --- incus ---
command -v incus &>/dev/null || { echo "ERROR: incus is not installed"; exit 1; }
echo "Incus: $(incus version 2>/dev/null || incus --version)"
# --- pciutils ---
if ! command -v lspci &>/dev/null; then
echo "pciutils をインストールします..."
apt-get install -y pciutils
fi
# --- tailscale (Serveに必要) ---
command -v tailscale &>/dev/null || { echo "ERROR: tailscale is not installed"; exit 1; }
# --- subuid/subgid for raw.idmap ---
if ! grep -q "root:1000:1" /etc/subuid 2>/dev/null; then
echo "root:1000:1" >> /etc/subuid
echo "subuid エントリを追加しました"
fi
if ! grep -q "root:1000:1" /etc/subgid 2>/dev/null; then
echo "root:1000:1" >> /etc/subgid
echo "subgid エントリを追加しました"
fi
# --- 既存停止 ---
pgrep -f "node.*easy-incus/server.js" &>/dev/null && { pkill -f "node.*easy-incus/server.js" 2>/dev/null; sleep 1; }
# --- ディレクトリ ---
mkdir -p "$INSTALL_DIR/public"
# --- /opt/ctndata ---
if [ ! -d "/opt/ctndata" ]; then
echo "/opt/ctndata ディレクトリを作成します..."
mkdir -p /opt/ctndata
fi
echo "/opt/ctndata の権限を UID/GID 1000 に設定します..."
chown -R 1000:1000 /opt/ctndata
chmod -R 775 /opt/ctndata
# --- /opt/lxd-data ---
if [ ! -d "/opt/lxd-data" ]; then
echo "/opt/lxd-data ディレクトリを作成します..."
mkdir -p /opt/lxd-data
fi
echo "/opt/lxd-data の権限を UID/GID 1000 に設定します..."
chown -R 1000:1000 /opt/lxd-data
chmod -R 775 /opt/lxd-data
# --- npm パッケージ (WebSocket + PTY) ---
echo "npm パッケージをインストール中..."
cd "$INSTALL_DIR"
npm init -y 2>/dev/null
npm install ws node-pty
echo "npm パッケージ インストール完了"
# --- favicon.svg ---
echo "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+CiAgPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzdiYWFmYyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzNiNmZkNCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPgogIDxyZWN0IHg9IjMiIHk9IjEwIiB3aWR0aD0iMjYiIGhlaWdodD0iMTgiIHJ4PSIzIiBmaWxsPSJ1cmwoI2cpIiBvcGFjaXR5PSIuOSIvPgogIDxyZWN0IHg9IjMiIHk9IjciIHdpZHRoPSIyNiIgaGVpZ2h0PSI2IiByeD0iMyIgZmlsbD0iIzViOGFmNSIvPgogIDxyZWN0IHg9IjgiIHk9IjIiIHdpZHRoPSIxNiIgaGVpZ2h0PSI4IiByeD0iMiIgZmlsbD0iIzNkZDY4YyIgb3BhY2l0eT0iLjg1Ii8+CiAgPHJlY3QgeD0iMTAiIHk9IjE0IiB3aWR0aD0iNSIgaGVpZ2h0PSI1IiByeD0iMSIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iLjI1Ii8+CiAgPHJlY3QgeD0iMTciIHk9IjE0IiB3aWR0aD0iNSIgaGVpZ2h0PSI1IiByeD0iMSIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iLjI1Ii8+CiAgPHJlY3QgeD0iMTAiIHk9IjIxIiB3aWR0aD0iNSIgaGVpZ2h0PSI0IiByeD0iMSIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iLjE1Ii8+CiAgPHJlY3QgeD0iMTciIHk9IjIxIiB3aWR0aD0iNSIgaGVpZ2h0PSI0IiByeD0iMSIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iLjE1Ii8+Cjwvc3ZnPgo=" | base64 -d > "$INSTALL_DIR/public/favicon.svg"
echo "favicon.svg を作成しました"
# --- server.js ---
echo "Y29uc3QgaHR0cCA9IHJlcXVpcmUoJ2h0dHAnKTsKY29uc3QgeyBzcGF3biB9ID0gcmVxdWlyZSgnY2hpbGRfcHJvY2VzcycpOwpjb25zdCBmcyA9IHJlcXVpcmUoJ2ZzJyk7CmNvbnN0IHBhdGggPSByZXF1aXJlKCdwYXRoJyk7CmNvbnN0IFdlYlNvY2tldCA9IHJlcXVpcmUoJ3dzJyk7CmNvbnN0IHB0eSA9IHJlcXVpcmUoJ25vZGUtcHR5Jyk7Cgpjb25zdCBQT1JUID0gMzMyOTsKCmZ1bmN0aW9uIHJ1bihjbWQsIGFyZ3MgPSBbXSwgdGltZW91dCA9IDEyMDAwMCwgb25EYXRhID0gbnVsbCkgewogIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICBjb25zdCBjaGlsZCA9IHNwYXduKGNtZCwgYXJncywgeyB0aW1lb3V0LCBzdGRpbzogWydpZ25vcmUnLCAncGlwZScsICdwaXBlJ10gfSk7CiAgICBsZXQgc3Rkb3V0ID0gJycsIHN0ZGVyciA9ICcnOwogICAgY2hpbGQuc3Rkb3V0Lm9uKCdkYXRhJywgZCA9PiB7IHN0ZG91dCArPSBkOyBpZiAob25EYXRhKSBvbkRhdGEoZCk7IH0pOwogICAgY2hpbGQuc3RkZXJyLm9uKCdkYXRhJywgZCA9PiB7IHN0ZGVyciArPSBkOyBpZiAob25EYXRhKSBvbkRhdGEoZCk7IH0pOwogICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHsgY2hpbGQua2lsbCgnU0lHS0lMTCcpOyByZWplY3QobmV3IEVycm9yKCdDb21tYW5kIHRpbWVkIG91dCcpKTsgfSwgdGltZW91dCk7CiAgICBjaGlsZC5vbignY2xvc2UnLCBjb2RlID0+IHsgY2xlYXJUaW1lb3V0KHRpbWVyKTsgY29kZSAhPT0gMCA/IHJlamVjdChuZXcgRXJyb3Ioc3RkZXJyLnRyaW0oKSB8fCBgQ29tbWFuZCBmYWlsZWQgd2l0aCBleGl0IGNvZGUgJHtjb2RlfWApKSA6IHJlc29sdmUoeyBzdGRvdXQ6IHN0ZG91dC50cmltKCkgfSk7IH0pOwogICAgY2hpbGQub24oJ2Vycm9yJywgZSA9PiB7IGNsZWFyVGltZW91dCh0aW1lcik7IHJlamVjdChlKTsgfSk7CiAgfSk7Cn0KCmZ1bmN0aW9uIGluY3VzKC4uLmFyZ3MpIHsgcmV0dXJuIHJ1bignaW5jdXMnLCBhcmdzKTsgfQoKYXN5bmMgZnVuY3Rpb24gaW5jdXNBcGkobWV0aG9kLCBhcGlQYXRoLCBib2R5ID0gbnVsbCwgZXRhZyA9IG51bGwpIHsKICBjb25zdCBhcmdzID0gWydxdWVyeScsICctLXdhaXQnLCAnLVgnLCBtZXRob2RdOwogIGlmIChldGFnKSBhcmdzLnB1c2goJy1IJywgYElmLU1hdGNoOiAke2V0YWd9YCk7CiAgaWYgKGJvZHkpIGFyZ3MucHVzaCgnLS1kYXRhJywgSlNPTi5zdHJpbmdpZnkoYm9keSkpOwogIGFyZ3MucHVzaChhcGlQYXRoKTsKICBjb25zdCByZXN1bHQgPSBhd2FpdCBydW4oJ2luY3VzJywgYXJncyk7CiAgcmV0dXJuIHJlc3VsdC5zdGRvdXQgPyBKU09OLnBhcnNlKHJlc3VsdC5zdGRvdXQpIDoge307Cn0KCmFzeW5jIGZ1bmN0aW9uIGluY3VzR2V0SW5zdGFuY2UobmFtZSkgewogIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJ1bignaW5jdXMnLCBbJ3F1ZXJ5JywgJy0td2FpdCcsICctLXJhdycsIGAvMS4wL2luc3RhbmNlcy8ke25hbWV9P3JlY3Vyc2lvbj0xYF0pOwogIGNvbnN0IGRhdGEgPSBKU09OLnBhcnNlKHJlc3VsdC5zdGRvdXQpOwogIHJldHVybiBkYXRhLm1ldGFkYXRhIHx8IGRhdGE7Cn0KCmFzeW5jIGZ1bmN0aW9uIGluY3VzVXBkYXRlSW5zdGFuY2UobmFtZSwgdXBkYXRlRmllbGRzKSB7CiAgY29uc3QgaW5zdCA9IGF3YWl0IGluY3VzR2V0SW5zdGFuY2UobmFtZSk7CiAgY29uc3QgcGF5bG9hZCA9IHsKICAgIGRldmljZXM6IHsgLi4uKGluc3QuZGV2aWNlcyB8fCB7fSkgfSwKICAgIGNvbmZpZzogeyAuLi4oaW5zdC5jb25maWcgfHwge30pIH0sCiAgICBwcm9maWxlczogaW5zdC5wcm9maWxlcyB8fCBbJ2RlZmF1bHQnXSwKICAgIGRlc2NyaXB0aW9uOiBpbnN0LmRlc2NyaXB0aW9uIHx8ICcnLAogICAgZXBoZW1lcmFsOiBpbnN0LmVwaGVtZXJhbCB8fCBmYWxzZSwKICAgIHN0YXRlZnVsOiBpbnN0LnN0YXRlZnVsIHx8IGZhbHNlCiAgfTsKICBPYmplY3QuYXNzaWduKHBheWxvYWQsIHVwZGF0ZUZpZWxkcyk7CiAgcmV0dXJuIHJ1bignaW5jdXMnLCBbJ3F1ZXJ5JywgJy0td2FpdCcsICctWCcsICdQVVQnLCAnLS1kYXRhJywgSlNPTi5zdHJpbmdpZnkocGF5bG9hZCksIGAvMS4wL2luc3RhbmNlcy8ke25hbWV9YF0pOwp9CgpmdW5jdGlvbiBwYXJzZUJvZHkocmVxKSB7CiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgIGxldCBkYXRhID0gJyc7CiAgICByZXEub24oJ2RhdGEnLCBjaHVuayA9PiBkYXRhICs9IGNodW5rKTsKICAgIHJlcS5vbignZW5kJywgKCkgPT4geyB0cnkgeyByZXNvbHZlKGRhdGEgPyBKU09OLnBhcnNlKGRhdGEpIDoge30pOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9KTsKICB9KTsKfQoKZnVuY3Rpb24ganNvbihyZXMsIGNvZGUsIG9iaikgewogIHJlcy53cml0ZUhlYWQoY29kZSwgeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9dXRmLTgnIH0pOwogIHJlcy5lbmQoSlNPTi5zdHJpbmdpZnkob2JqKSk7Cn0KCmFzeW5jIGZ1bmN0aW9uIGdldEluc3RhbmNlcygpIHsKICBjb25zdCB7IHN0ZG91dCB9ID0gYXdhaXQgaW5jdXMoJ2xpc3QnLCAnLS1mb3JtYXQnLCAnanNvbicpOwogIHJldHVybiBKU09OLnBhcnNlKHN0ZG91dCkubWFwKGMgPT4gKHsKICAgIG5hbWU6IGMubmFtZSwgc3RhdHVzOiBjLnN0YXR1cywgZXBoZW1lcmFsOiBjLmVwaGVtZXJhbCwgdHlwZTogYy50eXBlLAogICAgYXJjaGl0ZWN0dXJlOiBjLmFyY2hpdGVjdHVyZSwgY3JlYXRlZF9hdDogYy5jcmVhdGVkX2F0LCBwcm9maWxlczogYy5wcm9maWxlcywKICAgIGRldmljZXM6IGMuZGV2aWNlcyB8fCB7fSwKICAgIHN0YXRlOiBjLnN0YXRlID8geyBzdGF0dXM6IGMuc3RhdGUuc3RhdHVzLCBwaWQ6IGMuc3RhdGUucGlkLCBtZW1vcnk6IGMuc3RhdGUubWVtb3J5LCBkaXNrOiBjLnN0YXRlLmRpc2ssIGNwdTogYy5zdGF0ZS5jcHUsIG5ldHdvcms6IGMuc3RhdGUubmV0d29yayB9IDogbnVsbCwKICAgIHNuYXBzaG90czogYy5zbmFwc2hvdHMgfHwgW10KICB9KSk7Cn0KCmFzeW5jIGZ1bmN0aW9uIGdldEluc3RhbmNlKG5hbWUpIHsKICBjb25zdCBpbnN0YW5jZXMgPSBhd2FpdCBnZXRJbnN0YW5jZXMoKTsKICBjb25zdCBpbnN0ID0gaW5zdGFuY2VzLmZpbmQoaSA9PiBpLm5hbWUgPT09IG5hbWUpOwogIGlmICghaW5zdCkgdGhyb3cgbmV3IEVycm9yKCdJbnN0YW5jZSBub3QgZm91bmQnKTsKICByZXR1cm4gaW5zdDsKfQoKYXN5bmMgZnVuY3Rpb24gZ2V0U25hcHNob3RzKGNvbnRhaW5lcikgewogIGNvbnN0IGluc3QgPSBhd2FpdCBnZXRJbnN0YW5jZShjb250YWluZXIpOwogIHJldHVybiAoaW5zdC5zbmFwc2hvdHMgfHwgW10pLm1hcChzID0+ICh7IG5hbWU6IHMubmFtZSwgY3JlYXRlZF9hdDogcy5jcmVhdGVkX2F0LCBzaXplOiBzLnNpemUgfSkpOwp9Cgphc3luYyBmdW5jdGlvbiBpbmN1c0V4ZWMobmFtZSwgc2NyaXB0LCB0aW1lb3V0ID0gMzAwMDAwLCBvbkRhdGEgPSBudWxsKSB7CiAgcmV0dXJuIHJ1bignaW5jdXMnLCBbJ2V4ZWMnLCBuYW1lLCAnLS0nLCAnYmFzaCcsICctZXVvJywgJ3BpcGVmYWlsJywgJy1jJywgc2NyaXB0XSwgdGltZW91dCwgb25EYXRhKTsKfQoKLy8g44Kz44Oe44Oz44OJ44Gu5qiZ5rqW5Ye65YqbL+OCqOODqeODvOWHuuWKm+OCkjHooYzjgZrjgaQgcHJvZ3Jlc3Mg44Ot44Kw44G45rWB44GZ44Gf44KB44Gu44OY44Or44OR44O844CCCi8vIOODkOODg+ODleOCoeOBq+a6nOOBvuOBo+OBn+ODgeODo+ODs+OCr+OCkuaUueihjOWMuuWIh+OCiuOBp+WPluOCiuWHuuOBl+OAgeepuuihjOOBr+eEoeimluOBmeOCi+OAggpmdW5jdGlvbiBzdHJlYW1Ub0xvZyhsb2csIHByZWZpeCA9ICcgICcpIHsKICBsZXQgYnVmID0gJyc7CiAgcmV0dXJuIChjaHVuaykgPT4gewogICAgYnVmICs9IGNodW5rLnRvU3RyaW5nKCk7CiAgICBsZXQgaWR4OwogICAgd2hpbGUgKChpZHggPSBidWYuaW5kZXhPZignXG4nKSkgPj0gMCkgewogICAgICBjb25zdCBsaW5lID0gYnVmLnNsaWNlKDAsIGlkeCkucmVwbGFjZSgvXHIkLywgJycpOwogICAgICBidWYgPSBidWYuc2xpY2UoaWR4ICsgMSk7CiAgICAgIGlmIChsaW5lLnRyaW0oKSkgbG9nKGAke3ByZWZpeH0ke2xpbmV9YCk7CiAgICB9CiAgfTsKfQoKZnVuY3Rpb24gd2FpdFJ1bm5pbmcobmFtZSwgdGltZW91dCA9IDMwKSB7CiAgcmV0dXJuIG5ldyBQcm9taXNlKHJlc29sdmUgPT4gewogICAgbGV0IGVsYXBzZWQgPSAwOwogICAgY29uc3QgaXYgPSBzZXRJbnRlcnZhbChhc3luYyAoKSA9PiB7CiAgICAgIHRyeSB7IGNvbnN0IHsgc3Rkb3V0IH0gPSBhd2FpdCBpbmN1cygnaW5mbycsIG5hbWUpOyBpZiAoL1N0YXR1czpccypSVU5OSU5HLy50ZXN0KHN0ZG91dCkpIHsgY2xlYXJJbnRlcnZhbChpdik7IHJlc29sdmUoKTsgfSB9IGNhdGNoIChlKSB7fQogICAgICBlbGFwc2VkKys7IGlmIChlbGFwc2VkID49IHRpbWVvdXQpIHsgY2xlYXJJbnRlcnZhbChpdik7IHJlc29sdmUoKTsgfQogICAgfSwgMTAwMCk7CiAgfSk7Cn0KCi8vIGluY3VzIGluZm8g44GuIFN0YXR1czogUlVOTklORyDjga/jgrPjg7Pjg4bjg4rjg5fjg63jgrvjgrnjgYzotbfli5XjgZfjgZ/jgZPjgajjgZfjgYvnpLrjgZXjgZrjgIEKLy8gc3lzdGVtZC1yZXNvbHZlZCDnrYnjga7jg43jg4Pjg4jjg6/jg7zjgq8vRE5T5Yid5pyf5YyW44GM57WC44KP44Gj44Gm44GE44KL5L+d6Ki844Gv44Gq44GE44CCCi8vIOi1t+WLleebtOW+jOOBqyBhcHQtZ2V0IHVwZGF0ZSAvIGN1cmwgfCBzaCDjgpLlrp/ooYzjgZnjgovjgagKLy8gIkNvdWxkIG5vdCByZXNvbHZlIGhvc3QiIOOBp+WkseaVl+OBmeOCi+OBk+OBqOOBjOOBguOCi+OBn+OCgeOAgeWun+mam+OBq+WQjeWJjeino+axuui+vOOBv+OBrgovLyDnlo7pgJrnorroqo3jgYzjgafjgY3jgovjgb7jgafjg53jg7zjg6rjg7PjgrDjgZfjgablvoXjgaTjgIIKYXN5bmMgZnVuY3Rpb24gd2FpdE5ldHdvcmtSZWFkeShuYW1lLCB0aW1lb3V0U2VjID0gMzApIHsKICBmb3IgKGxldCBpID0gMTsgaSA8PSB0aW1lb3V0U2VjOyBpKyspIHsKICAgIHRyeSB7CiAgICAgIGF3YWl0IGluY3VzRXhlYyhuYW1lLCAnY3VybCAtZnNTTCAtLW1heC10aW1lIDMgaHR0cHM6Ly9nZXQuZG9ja2VyLmNvbSAtbyAvZGV2L251bGwnLCA1MDAwKTsKICAgICAgcmV0dXJuIHsgb2s6IHRydWUsIHdhaXRlZDogaSB9OwogICAgfSBjYXRjaCAoZSkge30KICAgIGF3YWl0IG5ldyBQcm9taXNlKHIgPT4gc2V0VGltZW91dChyLCAxMDAwKSk7CiAgfQogIHJldHVybiB7IG9rOiBmYWxzZSwgd2FpdGVkOiB0aW1lb3V0U2VjIH07Cn0KCmNvbnN0IFVCVU5UVV9WRVJTSU9OUyA9IFsnMjYuMDQnLCAnMjUuMTAnLCAnMjUuMDQnLCAnMjQuMDQnLCAnMjIuMDQnLCAnMjAuMDQnLCAnMTguMDQnXTsKbGV0IGNhY2hlZEltYWdlcyA9IG51bGw7CgpmdW5jdGlvbiBnZXRJbWFnZXMoKSB7CiAgaWYgKGNhY2hlZEltYWdlcykgcmV0dXJuIGNhY2hlZEltYWdlczsKICByZXR1cm4gVUJVTlRVX1ZFUlNJT05TLm1hcCh2ZXIgPT4gKHsgYWxpYXM6IGBpbWFnZXM6dWJ1bnR1LyR7dmVyfWAsIGRlc2NyaXB0aW9uOiBgVWJ1bnR1ICR7dmVyfSBMVFNgIH0pKTsKfQoKYXN5bmMgZnVuY3Rpb24gcmVmcmVzaEltYWdlcygpIHsKICBjb25zdCB7IHN0ZG91dCB9ID0gYXdhaXQgcnVuKCdpbmN1cycsIFsnaW1hZ2UnLCAnbGlzdCcsICdpbWFnZXM6dWJ1bnR1LycsICctLWZvcm1hdCcsICdqc29uJ10pOwogIGNvbnN0IHJhdyA9IEpTT04ucGFyc2Uoc3Rkb3V0KTsKICBjb25zdCBzZWVuID0gbmV3IFNldCgpOwogIGNvbnN0IGltYWdlcyA9IFtdOwogIGZvciAoY29uc3QgaW1nIG9mIHJhdykgewogICAgaWYgKGltZy5hcmNoaXRlY3R1cmUgIT09ICd4ODZfNjQnKSBjb250aW51ZTsKICAgIGZvciAoY29uc3QgYWxpYXMgb2YgKGltZy5hbGlhc2VzIHx8IFtdKSkgewogICAgICBjb25zdCBuYW1lID0gYWxpYXMubmFtZSB8fCAnJzsKICAgICAgaWYgKG5hbWUuaW5jbHVkZXMoJy8nKSB8fCBzZWVuLmhhcyhuYW1lKSkgY29udGludWU7CiAgICAgIHNlZW4uYWRkKG5hbWUpOwogICAgICBjb25zdCBkZXNjID0gKChpbWcucHJvcGVydGllcyAmJiBpbWcucHJvcGVydGllcy5kZXNjcmlwdGlvbikgfHwgJycpLnJlcGxhY2UoL1xzKlwocmVsZWFzZVwpLywgJycpLnRyaW0oKTsKICAgICAgaW1hZ2VzLnB1c2goeyBhbGlhczogYGltYWdlczp1YnVudHUvJHtuYW1lfWAsIGRlc2NyaXB0aW9uOiBkZXNjIHx8IGBVYnVudHUgJHtuYW1lfWAgfSk7CiAgICB9CiAgfQogIGltYWdlcy5zb3J0KChhLCBiKSA9PiB7IGNvbnN0IHZhID0gYS5hbGlhcy5yZXBsYWNlKC9bXjAtOS5dL2csICcnKTsgY29uc3QgdmIgPSBiLmFsaWFzLnJlcGxhY2UoL1teMC05Ll0vZywgJycpOyByZXR1cm4gdmIubG9jYWxlQ29tcGFyZSh2YSwgdW5kZWZpbmVkLCB7IG51bWVyaWM6IHRydWUgfSk7IH0pOwogIGNhY2hlZEltYWdlcyA9IGltYWdlczsKICByZXR1cm4gaW1hZ2VzOwp9Cgphc3luYyBmdW5jdGlvbiBjcmVhdGVJbnN0YW5jZShvcHRzLCBwcm9ncmVzcykgewogIGNvbnN0IHsgbmFtZSwgaW1hZ2UsIHVwZGF0ZTogZG9VcGRhdGUsIHRhaWxzY2FsZSwgZG9ja2VyLCBtb3VudCwgbW91bnRMeGQgfSA9IG9wdHM7CiAgY29uc3QgaXNVYnVudHUgPSBpbWFnZS5zdGFydHNXaXRoKCdpbWFnZXM6dWJ1bnR1LycpOwogIGNvbnN0IGxvZyA9IHByb2dyZXNzIHx8ICgoKSA9PiB7fSk7CgogIGxvZyhgaW5jdXMgbGF1bmNoICR7aW1hZ2V9ICR7bmFtZX1gKTsKICBhd2FpdCBydW4oJ2luY3VzJywgWydsYXVuY2gnLCBpbWFnZSwgbmFtZV0sIDMwMDAwMCwgc3RyZWFtVG9Mb2cobG9nKSk7CiAgbG9nKCfjgrPjg7Pjg4bjg4rjgpLotbfli5XkuK0uLi4nKTsKICBhd2FpdCB3YWl0UnVubmluZyhuYW1lKTsKICBsb2coJ+i1t+WLleWujOS6hicpOwoKICBpZiAoaXNVYnVudHUgJiYgKGRvVXBkYXRlIHx8IHRhaWxzY2FsZSB8fCBkb2NrZXIpKSB7CiAgICBsb2coJ+ODjeODg+ODiOODr+ODvOOCr+eWjumAmihETlPop6Pmsbop44KS56K66KqN5LitLi4uJyk7CiAgICBjb25zdCBuZXQgPSBhd2FpdCB3YWl0TmV0d29ya1JlYWR5KG5hbWUsIDMwKTsKICAgIGxvZyhuZXQub2sKICAgICAgPyBg44ON44OD44OI44Ov44O844Kv55aO6YCa44KS56K66KqN44GX44G+44GX44GfICgke25ldC53YWl0ZWR956eSKWAKICAgICAgOiBgV0FSTklORzog44ON44OD44OI44Ov44O844Kv55aO6YCa44GMJHtuZXQud2FpdGVkfeenkuS7peWGheOBq+eiuuiqjeOBp+OBjeOBvuOBm+OCk+OBp+OBl+OBn++8iOe2muihjOOBl+OBvuOBmeOBjOOAgXVwZGF0ZS90YWlsc2NhbGUvZG9ja2Vy44GM5aSx5pWX44GZ44KL5Y+v6IO95oCn44GM44GC44KK44G+44GZ77yJYCk7CiAgfQoKICBpZiAoaXNVYnVudHUgJiYgZG9VcGRhdGUpIHsKICAgIGxvZygnYXB0LWdldCB1cGRhdGUgJiYgdXBncmFkZSDlrp/ooYzkuK0uLi4nKTsKICAgIGF3YWl0IGluY3VzRXhlYyhuYW1lLCAnYXB0LWdldCB1cGRhdGUgJiYgYXB0LWdldCB1cGdyYWRlIC15JywgNjAwMDAwLCBzdHJlYW1Ub0xvZyhsb2cpKTsKICAgIGxvZygn44Ki44OD44OX44OH44O844OI5a6M5LqGJyk7CiAgfQogIGlmIChpc1VidW50dSAmJiB0YWlsc2NhbGUpIHsKICAgIGxvZygnVGFpbHNjYWxlIOOCpOODs+OCueODiOODvOODq+S4rS4uLicpOwogICAgYXdhaXQgaW5jdXNFeGVjKG5hbWUsICdjdXJsIC1mc1NMIGh0dHBzOi8vdGFpbHNjYWxlLmNvbS9pbnN0YWxsLnNoIHwgc2ggLXMgLS0gLS1uby1hdXRvc3RhcnQnLCAzMDAwMDAsIHN0cmVhbVRvTG9nKGxvZykpOwogICAgbG9nKCdUYWlsc2NhbGUg44Kk44Oz44K544OI44O844Or5a6M5LqGJyk7CiAgfQogIC8vIOODnuOCpuODs+ODiOOBryBEb2NrZXIg44KI44KK5YWI44Gr5b+F44Ga5a6f6KGM44GZ44KL44CCCiAgLy8gKERvY2tlciDjga7jgqTjg7Pjgrnjg4jjg7zjg6vjgYzlpLHmlZcv44K/44Kk44Og44Ki44Km44OI44GX44Gm5L6L5aSW44KS5oqV44GS44KL44Go44CB5b6M57aa5Yem55CG44GMCiAgLy8gIOS4gOWIh+Wun+ihjOOBleOCjOOBquOBhOOBn+OCgeOAgeODnuOCpuODs+ODiOOBjCBEb2NrZXIg44Gu5b6M44Gr44GC44KL44Go44CM44Oe44Km44Oz44OI44GV44KM44Gq44GE44CNCiAgLy8gIOOBqOOBhOOBhueXh+eKtuOBq+OBquOBo+OBpuOBhOOBn+OAgikKICBpZiAoaXNVYnVudHUgJiYgbW91bnQpIHsKICAgIGxvZygnL29wdC9jdG5kYXRhIOODnuOCpuODs+ODiOioreWumuS4rS4uLicpOwogICAgdHJ5IHsKICAgICAgYXdhaXQgaW5jdXMoJ3N0b3AnLCBuYW1lKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgaWYgKCEvYWxyZWFkeSBzdG9wcGVkL2kudGVzdChlLm1lc3NhZ2UpKSB0aHJvdyBlOwogICAgfQogICAgYXdhaXQgaW5jdXMoJ2NvbmZpZycsICdkZXZpY2UnLCAnYWRkJywgbmFtZSwgJ29wdC1jdG5kYXRhJywgJ2Rpc2snLCAnc291cmNlPS9vcHQvY3RuZGF0YScsICdwYXRoPS9vcHQvY3RuZGF0YScpOwogICAgYXdhaXQgaW5jdXMoJ2NvbmZpZycsICdzZXQnLCBuYW1lLCAncmF3LmlkbWFwJywgJ2JvdGggMTAwMCAxMDAwJyk7CiAgICBhd2FpdCBpbmN1cygnc3RhcnQnLCBuYW1lKTsKICAgIGF3YWl0IHdhaXRSdW5uaW5nKG5hbWUpOwogICAgdHJ5IHsKICAgICAgY29uc3QgeyBzdGRvdXQgfSA9IGF3YWl0IGluY3VzRXhlYyhuYW1lLCAnc3RhdCAtYyAiJVU6JUcgJWEiIC9vcHQvY3RuZGF0YSAyPiYxIHx8IGVjaG8gIueiuuiqjeWkseaVlyInKTsKICAgICAgbG9nKGAvb3B0L2N0bmRhdGEg56K66KqNOiAke3N0ZG91dH1gKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgbG9nKGAvb3B0L2N0bmRhdGEg56K66KqN44Gn44GN44G+44Gb44KT44Gn44GX44GfOiAke2UubWVzc2FnZX1gKTsKICAgIH0KICAgIGxvZygn44Oe44Km44Oz44OI6Kit5a6a5a6M5LqGJyk7CiAgfQogIGlmIChpc1VidW50dSAmJiBtb3VudEx4ZCkgewogICAgbG9nKCcvb3B0L2x4ZC1kYXRhIOODnuOCpuODs+ODiOioreWumuS4rS4uLicpOwogICAgdHJ5IHsKICAgICAgYXdhaXQgaW5jdXMoJ3N0b3AnLCBuYW1lKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgaWYgKCEvYWxyZWFkeSBzdG9wcGVkL2kudGVzdChlLm1lc3NhZ2UpKSB0aHJvdyBlOwogICAgfQogICAgYXdhaXQgaW5jdXMoJ2NvbmZpZycsICdkZXZpY2UnLCAnYWRkJywgbmFtZSwgJ29wdC1seGQtZGF0YScsICdkaXNrJywgJ3NvdXJjZT0vb3B0L2x4ZC1kYXRhJywgJ3BhdGg9L29wdC9seGQtZGF0YScpOwogICAgYXdhaXQgaW5jdXMoJ2NvbmZpZycsICdzZXQnLCBuYW1lLCAncmF3LmlkbWFwJywgJ2JvdGggMTAwMCAxMDAwJyk7CiAgICBhd2FpdCBpbmN1cygnc3RhcnQnLCBuYW1lKTsKICAgIGF3YWl0IHdhaXRSdW5uaW5nKG5hbWUpOwogICAgdHJ5IHsKICAgICAgY29uc3QgeyBzdGRvdXQgfSA9IGF3YWl0IGluY3VzRXhlYyhuYW1lLCAnc3RhdCAtYyAiJVU6JUcgJWEiIC9vcHQvbHhkLWRhdGEgMj4mMSB8fCBlY2hvICLnorroqo3lpLHmlZciJyk7CiAgICAgIGxvZyhgL29wdC9seGQtZGF0YSDnorroqo06ICR7c3Rkb3V0fWApOwogICAgfSBjYXRjaCAoZSkgewogICAgICBsb2coYC9vcHQvbHhkLWRhdGEg56K66KqN44Gn44GN44G+44Gb44KT44Gn44GX44GfOiAke2UubWVzc2FnZX1gKTsKICAgIH0KICAgIGxvZygn44Oe44Km44Oz44OI6Kit5a6a5a6M5LqGJyk7CiAgfQogIGlmIChpc1VidW50dSAmJiBkb2NrZXIpIHsKICAgIGxvZygnRG9ja2VyIOOCpOODs+OCueODiOODvOODq+a6luWCmeS4rSAoc2VjdXJpdHkubmVzdGluZykuLi4nKTsKICAgIHRyeSB7CiAgICAgIGF3YWl0IGluY3VzKCdzdG9wJywgbmFtZSk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIGlmICghL2FscmVhZHkgc3RvcHBlZC9pLnRlc3QoZS5tZXNzYWdlKSkgdGhyb3cgZTsKICAgIH0KICAgIGF3YWl0IGluY3VzKCdjb25maWcnLCAnc2V0JywgbmFtZSwgJ3NlY3VyaXR5Lm5lc3RpbmcnLCAndHJ1ZScpOwogICAgLy8gb3ZlcmxheTIg44K544OI44Os44O844K444OJ44Op44Kk44OQ44GM44ON44K544OI44GV44KM44Gf44Kz44Oz44OG44OK5YaF44Gn5q2j44GX44GP5YuV44GP44KI44GGCiAgICAvLyBta25vZCAvIHNldHhhdHRyIOOBruOCt+OCueODhuODoOOCs+ODvOODq+OCkuODm+OCueODiOWBtOOBp+S7o+ihjOOBmeOCi+ioreWumuOCkui/veWKoOOAggogICAgLy8gc2VjdXJpdHkubmVzdGluZyDjgaDjgZHjgaDjgaggZG9ja2VyZCDjga7otbfli5XjgYzlm7rjgb7jgosv5aSx5pWX44GZ44KL44Kx44O844K544GM44GC44KL44CCCiAgICBhd2FpdCBpbmN1cygnY29uZmlnJywgJ3NldCcsIG5hbWUsICdzZWN1cml0eS5zeXNjYWxscy5pbnRlcmNlcHQubWtub2QnLCAndHJ1ZScpOwogICAgYXdhaXQgaW5jdXMoJ2NvbmZpZycsICdzZXQnLCBuYW1lLCAnc2VjdXJpdHkuc3lzY2FsbHMuaW50ZXJjZXB0LnNldHhhdHRyJywgJ3RydWUnKTsKICAgIGF3YWl0IGluY3VzKCdzdGFydCcsIG5hbWUpOwogICAgYXdhaXQgd2FpdFJ1bm5pbmcobmFtZSk7CiAgICBsb2coJ+ODjeODg+ODiOODr+ODvOOCr+eWjumAmihETlPop6Pmsbop44KS5YaN56K66KqN5LitLi4uJyk7CiAgICBjb25zdCBuZXRBZnRlck5lc3RpbmcgPSBhd2FpdCB3YWl0TmV0d29ya1JlYWR5KG5hbWUsIDMwKTsKICAgIGxvZyhuZXRBZnRlck5lc3Rpbmcub2sKICAgICAgPyBg44ON44OD44OI44Ov44O844Kv55aO6YCa44KS56K66KqN44GX44G+44GX44GfICgke25ldEFmdGVyTmVzdGluZy53YWl0ZWR956eSKWAKICAgICAgOiBgV0FSTklORzog44ON44OD44OI44Ov44O844Kv55aO6YCa44GMJHtuZXRBZnRlck5lc3Rpbmcud2FpdGVkfeenkuS7peWGheOBq+eiuuiqjeOBp+OBjeOBvuOBm+OCk+OBp+OBl+OBn++8iOe2muihjOOBl+OBvuOBme+8iWApOwogICAgdHJ5IHsKICAgICAgbG9nKCdEb2NrZXIg44Kk44Oz44K544OI44O844Or5LitIChjdXJsIC1mc1NMIGh0dHBzOi8vZ2V0LmRvY2tlci5jb20gfCBzaCkuLi4nKTsKICAgICAgLy8gREVCSUFOX0ZST05URU5EIC8gTkVFRFJFU1RBUlRfTU9ERSDjgpLmmI7npLrjgZfjgarjgYTjgajjgIFVYnVudHUgMjIuMDQrIOOBrgogICAgICAvLyBuZWVkcmVzdGFydCDjgYzpnZ5UVFnnkrDlooPjgaflr77oqbHlvoXjgaHjga7jgb7jgb7jg4/jg7PjgrDjgZfjgIHjgr/jgqTjg6DjgqLjgqbjg4jjgZnjgovjgb7jgacKICAgICAgLy8g5L2V44KC6YCy5o2X44GM5Ye644Gq44GE54q25oWL44Gr44Gq44KL44GT44Go44GM44GC44KL44Gf44KB5piO56S655qE44Gr6Ieq5YuV5YyW44GZ44KL44CCCiAgICAgIGF3YWl0IGluY3VzRXhlYygKICAgICAgICBuYW1lLAogICAgICAgICdleHBvcnQgREVCSUFOX0ZST05URU5EPW5vbmludGVyYWN0aXZlIE5FRURSRVNUQVJUX01PREU9YSBORUVEUkVTVEFSVF9TVVNQRU5EPTE7IGN1cmwgLWZzU0wgaHR0cHM6Ly9nZXQuZG9ja2VyLmNvbSB8IHNoJywKICAgICAgICA2MDAwMDAsCiAgICAgICAgc3RyZWFtVG9Mb2cobG9nKQogICAgICApOwogICAgICBsb2coJ0RvY2tlciDjg4fjgqPjg6zjgq/jg4jjg6roqK3lrprkuK0uLi4nKTsKICAgICAgYXdhaXQgaW5jdXNFeGVjKG5hbWUsIGBta2RpciAtcCAvb3B0L2RvY2tlciAmJiBpZiBnZXRlbnQgZ3JvdXAgZG9ja2VyID4vZGV2L251bGwgMj4mMTsgdGhlbiBjaG93biAtUiByb290OmRvY2tlciAvb3B0L2RvY2tlciAmJiBjaG1vZCAtUiA3NzUgL29wdC9kb2NrZXI7IGVsc2UgY2hvd24gLVIgcm9vdDpyb290IC9vcHQvZG9ja2VyICYmIGNobW9kIC1SIDc1NSAvb3B0L2RvY2tlcjsgZmlgLCAzMDAwMDAsIHN0cmVhbVRvTG9nKGxvZykpOwogICAgICBsb2coJ0RvY2tlciDjgqTjg7Pjgrnjg4jjg7zjg6vlrozkuoYnKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgLy8gRG9ja2VyIOOBruOCpOODs+OCueODiOODvOODq+OBq+WkseaVl+OBl+OBpuOCguOAgeaXouOBq+WujOS6huOBl+OBpuOBhOOCi+ODnuOCpuODs+ODiOioreWumuOChAogICAgICAvLyDjgqTjg7Pjgrnjgr/jg7Pjgrnoh6rkvZPjga7kvZzmiJDjga/nhKHpp4TjgavjgZfjgarjgYTjgILjgqjjg6njg7zjga/jg63jgrDjgavmrovjgZfjgablhYjjgavpgLLjgoDjgIIKICAgICAgbG9nKGBXQVJOSU5HOiBEb2NrZXIg44Kk44Oz44K544OI44O844Or44Gr5aSx5pWX44GX44G+44GX44Gf77yI44Kk44Oz44K544K/44Oz44K55L2c5oiQ44Gv57aZ57aa44GX44G+44GZ77yJOiAke2UubWVzc2FnZX1gKTsKICAgIH0KICB9CiAgY29uc3QgZmVhdHVyZXMgPSBbXTsgaWYgKGlzVWJ1bnR1ICYmIGRvVXBkYXRlKSBmZWF0dXJlcy5wdXNoKCd1cGRhdGUnKTsgaWYgKGlzVWJ1bnR1ICYmIHRhaWxzY2FsZSkgZmVhdHVyZXMucHVzaCgndGFpbHNjYWxlJyk7CiAgaWYgKGlzVWJ1bnR1ICYmIGRvY2tlcikgZmVhdHVyZXMucHVzaCgnZG9ja2VyJyk7IGlmIChpc1VidW50dSAmJiBtb3VudCkgZmVhdHVyZXMucHVzaCgnbW91bnQnKTsgaWYgKGlzVWJ1bnR1ICYmIG1vdW50THhkKSBmZWF0dXJlcy5wdXNoKCdseGQtZGF0YScpOwogIHJldHVybiBgSW5zdGFuY2UgJHtuYW1lfSBjcmVhdGVkICgke2ltYWdlfSR7ZmVhdHVyZXMubGVuZ3RoID8gJyArICcgKyBmZWF0dXJlcy5qb2luKCcgKyAnKSA6ICcnfSlgOwp9Cgpjb25zdCBhY3RpdmVUZXJtaW5hbHMgPSBuZXcgTWFwKCk7CmNvbnN0IE1BWF9CVUZGRVIgPSA2NTUzNjsKY29uc3QgU0VTU0lPTl9HUkFDRV9NUyA9IDg2NDAwMDAwOwoKZnVuY3Rpb24ga2lsbFNlc3Npb24oc2Vzc2lvbiwgaW5zdGFuY2VOYW1lKSB7CiAgaWYgKHNlc3Npb24uZ3JhY2VUaW1lcikgeyBjbGVhclRpbWVvdXQoc2Vzc2lvbi5ncmFjZVRpbWVyKTsgc2Vzc2lvbi5ncmFjZVRpbWVyID0gbnVsbDsgfQogIHRyeSB7IHNlc3Npb24udGVybS5raWxsKCk7IH0gY2F0Y2ggKGUpIHt9CiAgYWN0aXZlVGVybWluYWxzLmRlbGV0ZShpbnN0YW5jZU5hbWUpOwp9Cgpjb25zdCBzZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhc3luYyAocmVxLCByZXMpID0+IHsKICBjb25zdCB1cmwgPSBuZXcgVVJMKHJlcS51cmwsIGBodHRwOi8vbG9jYWxob3N0OiR7UE9SVH1gKTsKICBjb25zdCBwYXRobmFtZSA9IHVybC5wYXRobmFtZTsKCiAgaWYgKHBhdGhuYW1lID09PSAnLycgfHwgcGF0aG5hbWUgPT09ICcvaW5kZXguaHRtbCcpIHsKICAgIHJldHVybiByZXMud3JpdGVIZWFkKDIwMCwgeyAnQ29udGVudC1UeXBlJzogJ3RleHQvaHRtbDsgY2hhcnNldD11dGYtOCcgfSkuZW5kKGZzLnJlYWRGaWxlU3luYyhwYXRoLmpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwgJ3V0Zi04JykpOwogIH0KICBpZiAocGF0aG5hbWUgPT09ICcvZmF2aWNvbi5zdmcnKSB7CiAgICByZXR1cm4gcmVzLndyaXRlSGVhZCgyMDAsIHsgJ0NvbnRlbnQtVHlwZSc6ICdpbWFnZS9zdmcreG1sJyB9KS5lbmQoZnMucmVhZEZpbGVTeW5jKHBhdGguam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnLCAnZmF2aWNvbi5zdmcnKSwgJ3V0Zi04JykpOwogIH0KICBpZiAocGF0aG5hbWUgPT09ICcvYXBpL2luc3RhbmNlcycgJiYgcmVxLm1ldGhvZCA9PT0gJ0dFVCcpIHsKICAgIHRyeSB7IHJldHVybiBqc29uKHJlcywgMjAwLCBhd2FpdCBnZXRJbnN0YW5jZXMoKSk7IH0gY2F0Y2ggKGUpIHsgcmV0dXJuIGpzb24ocmVzLCA1MDAsIHsgZXJyb3I6IGUubWVzc2FnZSB9KTsgfQogIH0KCiAgY29uc3QgaW5zdE1hdGNoID0gcGF0aG5hbWUubWF0Y2goL15cL2FwaVwvaW5zdGFuY2VzXC8oW14vXSspXC8oc3RhcnR8c3RvcHxyZXN0YXJ0fGRlbGV0ZSkkLyk7CiAgaWYgKGluc3RNYXRjaCkgewogICAgY29uc3QgWywgbmFtZSwgYWN0aW9uXSA9IGluc3RNYXRjaDsKICAgIHRyeSB7CiAgICAgIGlmIChhY3Rpb24gPT09ICdkZWxldGUnKSB7CiAgICAgICAgdHJ5IHsgYXdhaXQgaW5jdXMoJ3N0b3AnLCBuYW1lLCAnLS1mb3JjZScpOyB9IGNhdGNoIChlKSB7IGlmICghL2FscmVhZHkgc3RvcHBlZC9pLnRlc3QoZS5tZXNzYWdlKSkgdGhyb3cgZTsgfQogICAgICAgIGF3YWl0IGluY3VzKCdkZWxldGUnLCBuYW1lKTsKICAgICAgfQogICAgICBlbHNlIGlmIChhY3Rpb24gPT09ICdzdG9wJykgYXdhaXQgaW5jdXMoJ3N0b3AnLCBuYW1lKTsKICAgICAgZWxzZSBhd2FpdCBpbmN1cyhhY3Rpb24sIG5hbWUpOwogICAgICByZXR1cm4ganNvbihyZXMsIDIwMCwgeyBvazogdHJ1ZSwgbWVzc2FnZTogYCR7YWN0aW9ufSBjb21wbGV0ZWQgZm9yICR7bmFtZX1gIH0pOwogICAgfSBjYXRjaCAoZSkgeyByZXR1cm4ganNvbihyZXMsIDUwMCwgeyBlcnJvcjogZS5tZXNzYWdlIH0pOyB9CiAgfQogIGlmIChwYXRobmFtZSA9PT0gJy9hcGkvaW5zdGFuY2VzL2NyZWF0ZScgJiYgcmVxLm1ldGhvZCA9PT0gJ1BPU1QnKSB7CiAgICB0cnkgewogICAgICBjb25zdCBib2R5ID0gYXdhaXQgcGFyc2VCb2R5KHJlcSk7CiAgICAgIGlmICghYm9keS5uYW1lIHx8ICFib2R5LmltYWdlKSByZXR1cm4ganNvbihyZXMsIDQwMCwgeyBlcnJvcjogJ25hbWUgYW5kIGltYWdlIGFyZSByZXF1aXJlZCcgfSk7CiAgICAgIHJldHVybiBqc29uKHJlcywgMjAwLCB7IG9rOiB0cnVlLCBtZXNzYWdlOiBhd2FpdCBjcmVhdGVJbnN0YW5jZSh7IG5hbWU6IGJvZHkubmFtZSwgaW1hZ2U6IGJvZHkuaW1hZ2UsIHVwZGF0ZTogISFib2R5LnVwZGF0ZSwgdGFpbHNjYWxlOiAhIWJvZHkudGFpbHNjYWxlLCBkb2NrZXI6ICEhYm9keS5kb2NrZXIsIG1vdW50OiAhIWJvZHkubW91bnQsIG1vdW50THhkOiAhIWJvZHkubW91bnRMeGQgfSkgfSk7CiAgICB9IGNhdGNoIChlKSB7IHJldHVybiBqc29uKHJlcywgNTAwLCB7IGVycm9yOiBlLm1lc3NhZ2UgfSk7IH0KICB9CgogIGlmIChwYXRobmFtZSA9PT0gJy9hcGkvaW5zdGFuY2VzL2NyZWF0ZS9zdHJlYW0nICYmIHJlcS5tZXRob2QgPT09ICdQT1NUJykgewogICAgcmVzLndyaXRlSGVhZCgyMDAsIHsKICAgICAgJ0NvbnRlbnQtVHlwZSc6ICd0ZXh0L2V2ZW50LXN0cmVhbTsgY2hhcnNldD11dGYtOCcsCiAgICAgICdDYWNoZS1Db250cm9sJzogJ25vLWNhY2hlJywKICAgICAgJ0Nvbm5lY3Rpb24nOiAna2VlcC1hbGl2ZScsCiAgICAgICdYLUFjY2VsLUJ1ZmZlcmluZyc6ICdubycKICAgIH0pOwogICAgcmVzLndyaXRlKCc6XG5cbicpOwogICAgY29uc3Qgc2VuZCA9IChldnQsIGRhdGEpID0+IHsgdHJ5IHsgcmVzLndyaXRlKGBldmVudDogJHtldnR9XG5kYXRhOiAke0pTT04uc3RyaW5naWZ5KGRhdGEpfVxuXG5gKTsgfSBjYXRjaCAoZSkge30gfTsKICAgIHRyeSB7CiAgICAgIGNvbnN0IGJvZHkgPSBhd2FpdCBwYXJzZUJvZHkocmVxKTsKICAgICAgaWYgKCFib2R5Lm5hbWUgfHwgIWJvZHkuaW1hZ2UpIHsgc2VuZCgnZXJyb3InLCB7IGVycm9yOiAnbmFtZSBhbmQgaW1hZ2UgYXJlIHJlcXVpcmVkJyB9KTsgcmVzLmVuZCgpOyByZXR1cm47IH0KICAgICAgc2VuZCgnbG9nJywgeyBtZXNzYWdlOiBgPT09ICR7Ym9keS5uYW1lfSDjga7kvZzmiJDjgpLplovlp4sgPT09YCB9KTsKICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgY3JlYXRlSW5zdGFuY2UoCiAgICAgICAgeyBuYW1lOiBib2R5Lm5hbWUsIGltYWdlOiBib2R5LmltYWdlLCB1cGRhdGU6ICEhYm9keS51cGRhdGUsIHRhaWxzY2FsZTogISFib2R5LnRhaWxzY2FsZSwgZG9ja2VyOiAhIWJvZHkuZG9ja2VyLCBtb3VudDogISFib2R5Lm1vdW50LCBtb3VudEx4ZDogISFib2R5Lm1vdW50THhkIH0sCiAgICAgICAgKG1zZykgPT4gc2VuZCgnbG9nJywgeyBtZXNzYWdlOiBtc2cgfSkKICAgICAgKTsKICAgICAgc2VuZCgnZG9uZScsIHsgbWVzc2FnZTogcmVzdWx0IH0pOwogICAgfSBjYXRjaCAoZSkgewogICAgICBzZW5kKCdlcnJvcicsIHsgZXJyb3I6IGUubWVzc2FnZSB9KTsKICAgIH0KICAgIHJlcy5lbmQoKTsKICAgIHJldHVybjsKICB9CgogIGNvbnN0IGNsb25lTWF0Y2ggPSBwYXRobmFtZS5tYXRjaCgvXlwvYXBpXC9pbnN0YW5jZXNcLyhbXi9dKylcL2Nsb25lJC8pOwogIGlmIChjbG9uZU1hdGNoICYmIHJlcS5tZXRob2QgPT09ICdQT1NUJykgewogICAgY29uc3QgWywgc3JjTmFtZV0gPSBjbG9uZU1hdGNoOwogICAgdHJ5IHsKICAgICAgY29uc3QgYm9keSA9IGF3YWl0IHBhcnNlQm9keShyZXEpOyBpZiAoIWJvZHkubmV3TmFtZSkgcmV0dXJuIGpzb24ocmVzLCA0MDAsIHsgZXJyb3I6ICduZXdOYW1lIGlzIHJlcXVpcmVkJyB9KTsKICAgICAgYXdhaXQgaW5jdXMoJ2NvcHknLCBzcmNOYW1lLCBib2R5Lm5ld05hbWUsICctLXN0YXRlbGVzcycpOwogICAgICBhd2FpdCBpbmN1cygnY29uZmlnJywgJ3NldCcsIGJvZHkubmV3TmFtZSwgJ3Jhdy5pZG1hcCcsICdib3RoIDEwMDAgMTAwMCcpOyBhd2FpdCBpbmN1cygnc3RhcnQnLCBib2R5Lm5ld05hbWUpOwogICAgICByZXR1cm4ganNvbihyZXMsIDIwMCwgeyBvazogdHJ1ZSwgbWVzc2FnZTogYENsb25lZCAke3NyY05hbWV9IHRvICR7Ym9keS5uZXdOYW1lfWAgfSk7CiAgICB9IGNhdGNoIChlKSB7IHJldHVybiBqc29uKHJlcywgNTAwLCB7IGVycm9yOiBlLm1lc3NhZ2UgfSk7IH0KICB9CgogIGNvbnN0IHNuYXBMaXN0TWF0Y2ggPSBwYXRobmFtZS5tYXRjaCgvXlwvYXBpXC9pbnN0YW5jZXNcLyhbXi9dKylcL3NuYXBzaG90cyQvKTsKICBpZiAoc25hcExpc3RNYXRjaCAmJiByZXEubWV0aG9kID09PSAnR0VUJykgewogICAgdHJ5IHsgcmV0dXJuIGpzb24ocmVzLCAyMDAsIGF3YWl0IGdldFNuYXBzaG90cyhzbmFwTGlzdE1hdGNoWzFdKSk7IH0gY2F0Y2ggKGUpIHsgcmV0dXJuIGpzb24ocmVzLCA1MDAsIHsgZXJyb3I6IGUubWVzc2FnZSB9KTsgfQogIH0KICBpZiAoc25hcExpc3RNYXRjaCAmJiByZXEubWV0aG9kID09PSAnUE9TVCcpIHsKICAgIHRyeSB7CiAgICAgIGNvbnN0IGJvZHkgPSBhd2FpdCBwYXJzZUJvZHkocmVxKTsKICAgICAgY29uc3Qgc25hcCA9IGBzbmFwLSR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpLnJlcGxhY2UoL1s6Ll0vZywgJy0nKS5zbGljZSgwLCAxOSl9JHtib2R5LmNvbW1lbnQgPyAnLScgKyBib2R5LmNvbW1lbnQucmVwbGFjZSgvXHMrL2csICctJykgOiAnJ31gOwogICAgICBhd2FpdCBpbmN1cygnc25hcHNob3QnLCAnY3JlYXRlJywgc25hcExpc3RNYXRjaFsxXSwgc25hcCk7CiAgICAgIHJldHVybiBqc29uKHJlcywgMjAwLCB7IG9rOiB0cnVlLCBtZXNzYWdlOiBgU25hcHNob3QgJHtzbmFwfSBjcmVhdGVkYCwgbmFtZTogc25hcCB9KTsKICAgIH0gY2F0Y2ggKGUpIHsgcmV0dXJuIGpzb24ocmVzLCA1MDAsIHsgZXJyb3I6IGUubWVzc2FnZSB9KTsgfQogIH0KCiAgY29uc3Qgc25hcFJlc3RvcmVNYXRjaCA9IHBhdGhuYW1lLm1hdGNoKC9eXC9hcGlcL2luc3RhbmNlc1wvKFteL10rKVwvc25hcHNob3RzXC8oW14vXSspXC9yZXN0b3JlJC8pOwogIGlmIChzbmFwUmVzdG9yZU1hdGNoICYmIHJlcS5tZXRob2QgPT09ICdQT1NUJykgewogICAgdHJ5IHsKICAgICAgY29uc3QgWywgaW5zdE5hbWUsIHNuYXBOYW1lXSA9IHNuYXBSZXN0b3JlTWF0Y2g7CiAgICAgIGNvbnN0IHNuYXBSYXcgPSBhd2FpdCBpbmN1c0FwaSgnR0VUJywgYC8xLjAvaW5zdGFuY2VzLyR7aW5zdE5hbWV9L3NuYXBzaG90cy8ke3NuYXBOYW1lfWApOwogICAgICBjb25zdCBzbmFwTWV0YSA9IHNuYXBSYXcubWV0YWRhdGEgfHwgc25hcFJhdzsKICAgICAgY29uc3Qgc25hcERldmljZXMgPSBzbmFwTWV0YS5kZXZpY2VzIHx8IHt9OwogICAgICBjb25zdCBzbmFwQ29uZmlnID0gc25hcE1ldGEuY29uZmlnIHx8IHt9OwoKICAgICAgbGV0IHdhc1J1bm5pbmcgPSBmYWxzZTsKICAgICAgdHJ5IHsKICAgICAgICBjb25zdCB7IHN0ZG91dCB9ID0gYXdhaXQgaW5jdXMoJ2luZm8nLCBpbnN0TmFtZSk7CiAgICAgICAgd2FzUnVubmluZyA9IC9TdGF0dXM6XHMqUlVOTklORy8udGVzdChzdGRvdXQpOwogICAgICB9IGNhdGNoIChlKSB7fQoKICAgICAgaWYgKHdhc1J1bm5pbmcpIHsKICAgICAgICBhd2FpdCBpbmN1cygnc3RvcCcsIGluc3ROYW1lLCAnLS1mb3JjZScpOwogICAgICB9CiAgICAgIGF3YWl0IGluY3VzKCdzbmFwc2hvdCcsICdyZXN0b3JlJywgaW5zdE5hbWUsIHNuYXBOYW1lKTsKCiAgICAgIGNvbnN0IGluc3QgPSBhd2FpdCBpbmN1c0dldEluc3RhbmNlKGluc3ROYW1lKTsKICAgICAgY29uc3QgY3VycmVudERldmljZXMgPSBpbnN0LmRldmljZXMgfHwge307CiAgICAgIGNvbnN0IGN1cnJlbnRDb25maWcgPSBpbnN0LmNvbmZpZyB8fCB7fTsKICAgICAgY29uc3QgbmVlZHNVcGRhdGUgPSBKU09OLnN0cmluZ2lmeShzbmFwRGV2aWNlcykgIT09IEpTT04uc3RyaW5naWZ5KGN1cnJlbnREZXZpY2VzKQogICAgICAgICAgICAgICAgICAgICAgIHx8IEpTT04uc3RyaW5naWZ5KHNuYXBDb25maWcpICE9PSBKU09OLnN0cmluZ2lmeShjdXJyZW50Q29uZmlnKTsKICAgICAgaWYgKG5lZWRzVXBkYXRlKSB7CiAgICAgICAgYXdhaXQgaW5jdXNVcGRhdGVJbnN0YW5jZShpbnN0TmFtZSwgeyBkZXZpY2VzOiBzbmFwRGV2aWNlcywgY29uZmlnOiBzbmFwQ29uZmlnIH0pOwogICAgICB9CgogICAgICBpZiAod2FzUnVubmluZykgewogICAgICAgIGF3YWl0IGluY3VzKCdzdGFydCcsIGluc3ROYW1lKTsKICAgICAgfQogICAgICByZXR1cm4ganNvbihyZXMsIDIwMCwgeyBvazogdHJ1ZSwgbWVzc2FnZTogYFJlc3RvcmVkICR7aW5zdE5hbWV9IGZyb20gJHtzbmFwTmFtZX1gIH0pOwogICAgfSBjYXRjaCAoZSkgeyByZXR1cm4ganNvbihyZXMsIDUwMCwgeyBlcnJvcjogZS5tZXNzYWdlIH0pOyB9CiAgfQoKICBjb25zdCBzbmFwRGVsZXRlTWF0Y2ggPSBwYXRobmFtZS5tYXRjaCgvXlwvYXBpXC9pbnN0YW5jZXNcLyhbXi9dKylcL3NuYXBzaG90c1wvKFteL10rKSQvKTsKICBpZiAoc25hcERlbGV0ZU1hdGNoICYmIHJlcS5tZXRob2QgPT09ICdERUxFVEUnKSB7CiAgICB0cnkgeyBhd2FpdCBpbmN1cygnc25hcHNob3QnLCAnZGVsZXRlJywgc25hcERlbGV0ZU1hdGNoWzFdLCBzbmFwRGVsZXRlTWF0Y2hbMl0pOyByZXR1cm4ganNvbihyZXMsIDIwMCwgeyBvazogdHJ1ZSwgbWVzc2FnZTogYERlbGV0ZWQgc25hcHNob3QgJHtzbmFwRGVsZXRlTWF0Y2hbMl19YCB9KTsgfQogICAgY2F0Y2ggKGUpIHsgcmV0dXJuIGpzb24ocmVzLCA1MDAsIHsgZXJyb3I6IGUubWVzc2FnZSB9KTsgfQogIH0KCiAgaWYgKHBhdGhuYW1lID09PSAnL2FwaS9pbWFnZXMnICYmIHJlcS5tZXRob2QgPT09ICdHRVQnKSB7CiAgICB0cnkgeyByZXR1cm4ganNvbihyZXMsIDIwMCwgeyBpbWFnZXM6IGdldEltYWdlcygpLCBjYWNoZWQ6ICEhY2FjaGVkSW1hZ2VzIH0pOyB9IGNhdGNoIChlKSB7IHJldHVybiBqc29uKHJlcywgNTAwLCB7IGVycm9yOiBlLm1lc3NhZ2UgfSk7IH0KICB9CiAgaWYgKHBhdGhuYW1lID09PSAnL2FwaS9pbWFnZXMvcmVmcmVzaCcgJiYgcmVxLm1ldGhvZCA9PT0gJ1BPU1QnKSB7CiAgICB0cnkgeyByZXR1cm4ganNvbihyZXMsIDIwMCwgeyBpbWFnZXM6IGF3YWl0IHJlZnJlc2hJbWFnZXMoKSB9KTsgfSBjYXRjaCAoZSkgeyByZXR1cm4ganNvbihyZXMsIDUwMCwgeyBlcnJvcjogZS5tZXNzYWdlIH0pOyB9CiAgfQoKICBpZiAocGF0aG5hbWUgPT09ICcvYXBpL2dwdXMnICYmIHJlcS5tZXRob2QgPT09ICdHRVQnKSB7CiAgICB0cnkgewogICAgICBjb25zdCByZXNvdXJjZXMgPSBhd2FpdCBpbmN1c0FwaSgnR0VUJywgJy8xLjAvcmVzb3VyY2VzP3JlY3Vyc2lvbj0xJyk7CiAgICAgIGNvbnN0IGdwdXMgPSAocmVzb3VyY2VzLmdwdSB8fCBbXSkubWFwKGdwdSA9PiAoeyBwY2k6IGdwdS5wY2lfYWRkcmVzcyB8fCAnJywgZGVzYzogYCR7Z3B1LnZlbmRvciB8fCAnJ30gJHtncHUucHJvZHVjdCB8fCAnJ31gLnRyaW0oKSwgdmVuZG9yOiBncHUudmVuZG9yIHx8ICcnLCBwcm9kdWN0OiBncHUucHJvZHVjdCB8fCAnJywgZHJpdmVyOiBncHUuZHJpdmVyIHx8ICcnLCBkcml2ZXJWZXJzaW9uOiBncHUuZHJpdmVyX3ZlcnNpb24gfHwgJycsIG51bWFOb2RlOiBncHUubnVtYV9ub2RlIH0pKS5maWx0ZXIoZyA9PiBnLnBjaSk7CiAgICAgIGlmIChncHVzLmxlbmd0aCA9PT0gMCkgewogICAgICAgIGNvbnN0IHsgc3Rkb3V0IH0gPSBhd2FpdCBydW4oJ2xzcGNpJywgWyctRG5uJ10pOwogICAgICAgIHN0ZG91dC5zcGxpdCgnXG4nKS5maWx0ZXIobCA9PiAvVkdBIGNvbXBhdGlibGUgY29udHJvbGxlcnwzRCBjb250cm9sbGVyfERpc3BsYXkgY29udHJvbGxlci9pLnRlc3QobCkpLmZvckVhY2gobGluZSA9PiB7CiAgICAgICAgICBjb25zdCBtYXRjaCA9IGxpbmUubWF0Y2goL14oWzAtOWEtZl17NH06WzAtOWEtZl17Mn06WzAtOWEtZl17Mn1cLlxkKVxzKyguKikvKTsKICAgICAgICAgIGlmIChtYXRjaCkgZ3B1cy5wdXNoKHsgcGNpOiBtYXRjaFsxXSwgZGVzYzogbWF0Y2hbMl0sIHZlbmRvcjogJycsIHByb2R1Y3Q6ICcnLCBkcml2ZXI6ICcnLCBkcml2ZXJWZXJzaW9uOiAnJyB9KTsKICAgICAgICB9KTsKICAgICAgfQogICAgICByZXR1cm4ganNvbihyZXMsIDIwMCwgeyBncHVzIH0pOwogICAgfSBjYXRjaCAoZSkgewogICAgICB0cnkgewogICAgICAgIGNvbnN0IHsgc3Rkb3V0IH0gPSBhd2FpdCBydW4oJ2xzcGNpJywgWyctRG5uJ10pOwogICAgICAgIGNvbnN0IGdwdXMgPSBzdGRvdXQuc3BsaXQoJ1xuJykuZmlsdGVyKGwgPT4gL1ZHQSBjb21wYXRpYmxlIGNvbnRyb2xsZXJ8M0QgY29udHJvbGxlcnxEaXNwbGF5IGNvbnRyb2xsZXIvaS50ZXN0KGwpKS5tYXAobGluZSA9PiB7CiAgICAgICAgICBjb25zdCBtYXRjaCA9IGxpbmUubWF0Y2goL14oWzAtOWEtZl17NH06WzAtOWEtZl17Mn06WzAtOWEtZl17Mn1cLlxkKVxzKyguKikvKTsKICAgICAgICAgIHJldHVybiBtYXRjaCA/IHsgcGNpOiBtYXRjaFsxXSwgZGVzYzogbWF0Y2hbMl0sIHZlbmRvcjogJycsIHByb2R1Y3Q6ICcnLCBkcml2ZXI6ICcnLCBkcml2ZXJWZXJzaW9uOiAnJyB9IDogbnVsbDsKICAgICAgICB9KS5maWx0ZXIoQm9vbGVhbik7CiAgICAgICAgcmV0dXJuIGpzb24ocmVzLCAyMDAsIHsgZ3B1cyB9KTsKICAgICAgfSBjYXRjaCAoZTIpIHsgcmV0dXJuIGpzb24ocmVzLCA1MDAsIHsgZXJyb3I6IGUyLm1lc3NhZ2UgfSk7IH0KICAgIH0KICB9CgogIGNvbnN0IGdwdUFkZE1hdGNoID0gcGF0aG5hbWUubWF0Y2goL15cL2FwaVwvaW5zdGFuY2VzXC8oW14vXSspXC9ncHVcL2FkZCQvKTsKICBpZiAoZ3B1QWRkTWF0Y2ggJiYgcmVxLm1ldGhvZCA9PT0gJ1BPU1QnKSB7CiAgICBjb25zdCBbLCBuYW1lXSA9IGdwdUFkZE1hdGNoOwogICAgdHJ5IHsKICAgICAgY29uc3QgYm9keSA9IGF3YWl0IHBhcnNlQm9keShyZXEpOwogICAgICBpZiAoIWJvZHkucGNpKSByZXR1cm4ganNvbihyZXMsIDQwMCwgeyBlcnJvcjogJ3BjaSBhZGRyZXNzIGlzIHJlcXVpcmVkJyB9KTsKICAgICAgY29uc3QgaW5zdCA9IGF3YWl0IGluY3VzR2V0SW5zdGFuY2UobmFtZSk7CiAgICAgIGNvbnN0IGRldmljZXMgPSBpbnN0LmRldmljZXMgfHwge307CiAgICAgIGxldCBkZXZOYW1lID0gJ2dwdTAnOyBsZXQgbiA9IDA7CiAgICAgIHdoaWxlIChkZXZpY2VzW2BncHUke259YF0pIG4rKzsgZGV2TmFtZSA9IGBncHUke259YDsKICAgICAgYXdhaXQgaW5jdXNVcGRhdGVJbnN0YW5jZShuYW1lLCB7IGRldmljZXM6IHsgLi4uZGV2aWNlcywgW2Rldk5hbWVdOiB7IHR5cGU6ICdncHUnLCBncHV0eXBlOiAncGh5c2ljYWwnLCBwY2k6IGJvZHkucGNpIH0gfSB9KTsKICAgICAgcmV0dXJuIGpzb24ocmVzLCAyMDAsIHsgb2s6IHRydWUsIG1lc3NhZ2U6IGBHUFUgJHtib2R5LnBjaX0gYWRkZWQgYXMgJHtkZXZOYW1lfWAsIGRldmljZU5hbWU6IGRldk5hbWUgfSk7CiAgICB9IGNhdGNoIChlKSB7IHJldHVybiBqc29uKHJlcywgNTAwLCB7IGVycm9yOiBlLm1lc3NhZ2UgfSk7IH0KICB9CgogIGNvbnN0IGdwdVJlbW92ZU1hdGNoID0gcGF0aG5hbWUubWF0Y2goL15cL2FwaVwvaW5zdGFuY2VzXC8oW14vXSspXC9ncHVcL3JlbW92ZSQvKTsKICBpZiAoZ3B1UmVtb3ZlTWF0Y2ggJiYgcmVxLm1ldGhvZCA9PT0gJ1BPU1QnKSB7CiAgICBjb25zdCBbLCBuYW1lXSA9IGdwdVJlbW92ZU1hdGNoOwogICAgdHJ5IHsKICAgICAgY29uc3QgYm9keSA9IGF3YWl0IHBhcnNlQm9keShyZXEpOwogICAgICBpZiAoIWJvZHkuZGV2aWNlTmFtZSkgcmV0dXJuIGpzb24ocmVzLCA0MDAsIHsgZXJyb3I6ICdkZXZpY2VOYW1lIGlzIHJlcXVpcmVkJyB9KTsKICAgICAgY29uc3QgaW5zdCA9IGF3YWl0IGluY3VzR2V0SW5zdGFuY2UobmFtZSk7CiAgICAgIGNvbnN0IGRldmljZXMgPSBpbnN0LmRldmljZXMgfHwge307CiAgICAgIGlmICghZGV2aWNlc1tib2R5LmRldmljZU5hbWVdKSByZXR1cm4ganNvbihyZXMsIDQwMCwgeyBlcnJvcjogYERldmljZSAke2JvZHkuZGV2aWNlTmFtZX0gbm90IGZvdW5kYCB9KTsKICAgICAgY29uc3QgbmV3RGV2aWNlcyA9IHsgLi4uZGV2aWNlcyB9OyBkZWxldGUgbmV3RGV2aWNlc1tib2R5LmRldmljZU5hbWVdOwogICAgICBhd2FpdCBpbmN1c1VwZGF0ZUluc3RhbmNlKG5hbWUsIHsgZGV2aWNlczogbmV3RGV2aWNlcyB9KTsKICAgICAgcmV0dXJuIGpzb24ocmVzLCAyMDAsIHsgb2s6IHRydWUsIG1lc3NhZ2U6IGBHUFUgZGV2aWNlICR7Ym9keS5kZXZpY2VOYW1lfSByZW1vdmVkYCB9KTsKICAgIH0gY2F0Y2ggKGUpIHsgcmV0dXJuIGpzb24ocmVzLCA1MDAsIHsgZXJyb3I6IGUubWVzc2FnZSB9KTsgfQogIH0KCiAgY29uc3QgdGVybVJlc2V0TWF0Y2ggPSBwYXRobmFtZS5tYXRjaCgvXlwvYXBpXC90ZXJtaW5hbFwvcmVzZXRcLyguKykkLyk7CiAgaWYgKHRlcm1SZXNldE1hdGNoICYmIHJlcS5tZXRob2QgPT09ICdQT1NUJykgewogICAgY29uc3QgaW5zdE5hbWUgPSB0ZXJtUmVzZXRNYXRjaFsxXTsKICAgIGNvbnN0IHNlc3Npb24gPSBhY3RpdmVUZXJtaW5hbHMuZ2V0KGluc3ROYW1lKTsKICAgIGlmIChzZXNzaW9uKSB7CiAgICAgIGZvciAoY29uc3QgYyBvZiBzZXNzaW9uLmNsaWVudHMpIHsKICAgICAgICB0cnkgeyBjLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyB0eXBlOiAnZXhpdCcgfSkpOyB9IGNhdGNoIChlKSB7fQogICAgICB9CiAgICAgIGtpbGxTZXNzaW9uKHNlc3Npb24sIGluc3ROYW1lKTsKICAgICAgcmV0dXJuIGpzb24ocmVzLCAyMDAsIHsgb2s6IHRydWUsIG1lc3NhZ2U6IGBUZXJtaW5hbCBzZXNzaW9uIGZvciAke2luc3ROYW1lfSBoYXMgYmVlbiByZXNldGAgfSk7CiAgICB9CiAgICByZXR1cm4ganNvbihyZXMsIDIwMCwgeyBvazogdHJ1ZSwgbWVzc2FnZTogYE5vIGFjdGl2ZSBzZXNzaW9uIGZvciAke2luc3ROYW1lfWAgfSk7CiAgfQoKICBqc29uKHJlcywgNDA0LCB7IGVycm9yOiAnTm90IGZvdW5kJyB9KTsKfSk7CgovLyAtLS0gV2ViU29ja2V0IFRlcm1pbmFsIChzaGFyZWQgc2Vzc2lvbnMgKyByZXBsYXkgYnVmZmVyICsgc2Vzc2lvbiBwZXJzaXN0ZW5jZSkgLS0tCmNvbnN0IHdzcyA9IG5ldyBXZWJTb2NrZXQuU2VydmVyKHsgc2VydmVyIH0pOwoKd3NzLm9uKCdjb25uZWN0aW9uJywgKHdzLCByZXEpID0+IHsKICBjb25zdCB1cmwgPSBuZXcgVVJMKHJlcS51cmwsIGBodHRwOi8vbG9jYWxob3N0OiR7UE9SVH1gKTsKICBjb25zdCBpbnN0YW5jZU5hbWUgPSB1cmwuc2VhcmNoUGFyYW1zLmdldCgnaW5zdGFuY2UnKTsKICBpZiAoIWluc3RhbmNlTmFtZSkgeyB3cy5jbG9zZSgpOyByZXR1cm47IH0KICBjb25zdCBjb2xzID0gcGFyc2VJbnQodXJsLnNlYXJjaFBhcmFtcy5nZXQoJ2NvbHMnKSkgfHwgODA7CiAgY29uc3Qgcm93cyA9IHBhcnNlSW50KHVybC5zZWFyY2hQYXJhbXMuZ2V0KCdyb3dzJykpIHx8IDI0OwoKICBsZXQgc2Vzc2lvbiA9IGFjdGl2ZVRlcm1pbmFscy5nZXQoaW5zdGFuY2VOYW1lKTsKCiAgaWYgKHNlc3Npb24pIHsKICAgIGlmIChzZXNzaW9uLmdyYWNlVGltZXIpIHsgY2xlYXJUaW1lb3V0KHNlc3Npb24uZ3JhY2VUaW1lcik7IHNlc3Npb24uZ3JhY2VUaW1lciA9IG51bGw7IH0KICAgIHNlc3Npb24uY2xpZW50cy5hZGQod3MpOwogICAgaWYgKHNlc3Npb24uYnVmZmVyKSB7CiAgICAgIHdzLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyB0eXBlOiAnb3V0cHV0JywgZGF0YTogc2Vzc2lvbi5idWZmZXIgfSkpOwogICAgfQogICAgd3Muc2VuZChKU09OLnN0cmluZ2lmeSh7IHR5cGU6ICdvdXRwdXQnLCBkYXRhOiAnXHgxYlszM21b44K744OD44K344On44Oz44Gr5YaN5o6l57aa44GX44G+44GX44GfXVx4MWJbMG1cclxuJyB9KSk7CiAgfSBlbHNlIHsKICAgIGxldCB0ZXJtOwogICAgdHJ5IHsKICAgICAgdGVybSA9IHB0eS5zcGF3bignaW5jdXMnLCBbJ2V4ZWMnLCBpbnN0YW5jZU5hbWUsICctLScsICcvYmluL2Jhc2gnXSwgewogICAgICAgIG5hbWU6ICd4dGVybS0yNTZjb2xvcicsCiAgICAgICAgY29scywgcm93cywKICAgICAgICBjd2Q6IHByb2Nlc3MuZW52LkhPTUUgfHwgJy9yb290JywKICAgICAgICBlbnY6IHsgLi4ucHJvY2Vzcy5lbnYsIFRFUk06ICd4dGVybS0yNTZjb2xvcicsIExDX0FMTDogJ2VuX1VTLlVURi04JyB9CiAgICAgIH0pOwogICAgfSBjYXRjaCAoZSkgeyB3cy5jbG9zZSgpOyByZXR1cm47IH0KICAgIHNlc3Npb24gPSB7IHRlcm0sIGNsaWVudHM6IG5ldyBTZXQoW3dzXSksIHJlc2l6ZVRpbWVvdXQ6IG51bGwsIGJ1ZmZlcjogJycsIGdyYWNlVGltZXI6IG51bGwgfTsKICAgIGFjdGl2ZVRlcm1pbmFscy5zZXQoaW5zdGFuY2VOYW1lLCBzZXNzaW9uKTsKICAgIHRlcm0ub25EYXRhKGRhdGEgPT4gewogICAgICBzZXNzaW9uLmJ1ZmZlciArPSBkYXRhOwogICAgICBpZiAoc2Vzc2lvbi5idWZmZXIubGVuZ3RoID4gTUFYX0JVRkZFUikgewogICAgICAgIHNlc3Npb24uYnVmZmVyID0gc2Vzc2lvbi5idWZmZXIuc2xpY2UoLU1BWF9CVUZGRVIpOwogICAgICB9CiAgICAgIGZvciAoY29uc3QgYyBvZiBzZXNzaW9uLmNsaWVudHMpIHsKICAgICAgICB0cnkgeyBjLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyB0eXBlOiAnb3V0cHV0JywgZGF0YSB9KSk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgIH0KICAgIH0pOwogICAgdGVybS5vbkV4aXQoKCkgPT4gewogICAgICBmb3IgKGNvbnN0IGMgb2Ygc2Vzc2lvbi5jbGllbnRzKSB7CiAgICAgICAgdHJ5IHsgYy5zZW5kKEpTT04uc3RyaW5naWZ5KHsgdHlwZTogJ2V4aXQnIH0pKTsgfSBjYXRjaCAoZSkge30KICAgICAgfQogICAgICBhY3RpdmVUZXJtaW5hbHMuZGVsZXRlKGluc3RhbmNlTmFtZSk7CiAgICB9KTsKICB9CgogIHdzLm9uKCdtZXNzYWdlJywgbXNnID0+IHsKICAgIHRyeSB7CiAgICAgIGNvbnN0IG0gPSBKU09OLnBhcnNlKG1zZyk7CiAgICAgIGlmIChtLnR5cGUgPT09ICdpbnB1dCcgJiYgc2Vzc2lvbiAmJiBzZXNzaW9uLnRlcm0pIHNlc3Npb24udGVybS53cml0ZShtLmRhdGEpOwogICAgICBpZiAobS50eXBlID09PSAncmVzaXplJyAmJiBtLmNvbHMgJiYgbS5yb3dzICYmIHNlc3Npb24gJiYgc2Vzc2lvbi50ZXJtKSB7CiAgICAgICAgaWYgKHNlc3Npb24ucmVzaXplVGltZW91dCkgY2xlYXJUaW1lb3V0KHNlc3Npb24ucmVzaXplVGltZW91dCk7CiAgICAgICAgc2Vzc2lvbi5yZXNpemVUaW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7CiAgICAgICAgICB0cnkgeyBzZXNzaW9uLnRlcm0ucmVzaXplKG0uY29scywgbS5yb3dzKTsgfSBjYXRjaCAoZSkge30KICAgICAgICB9LCA1MCk7CiAgICAgIH0KICAgIH0gY2F0Y2ggKGUpIHt9CiAgfSk7CgogIGNvbnN0IHJlbW92ZUNsaWVudCA9ICgpID0+IHsKICAgIGlmICghc2Vzc2lvbikgcmV0dXJuOwogICAgc2Vzc2lvbi5jbGllbnRzLmRlbGV0ZSh3cyk7CiAgICBpZiAoc2Vzc2lvbi5jbGllbnRzLnNpemUgPT09IDApIHsKICAgICAgc2Vzc2lvbi5ncmFjZVRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7CiAgICAgICAgaWYgKHNlc3Npb24uY2xpZW50cy5zaXplID09PSAwKSBraWxsU2Vzc2lvbihzZXNzaW9uLCBpbnN0YW5jZU5hbWUpOwogICAgICB9LCBTRVNTSU9OX0dSQUNFX01TKTsKICAgIH0KICB9OwogIHdzLm9uKCdjbG9zZScsIHJlbW92ZUNsaWVudCk7CiAgd3Mub24oJ2Vycm9yJywgcmVtb3ZlQ2xpZW50KTsKfSk7CgpzZXJ2ZXIubGlzdGVuKFBPUlQsICcxMjcuMC4wLjEnLCAoKSA9PiB7IGNvbnNvbGUubG9nKGBFYXN5IEluY3VzIFVJIHJ1bm5pbmcgb24gaHR0cDovLzEyNy4wLjAuMToke1BPUlR9IChUYWlsc2NhbGUgU2VydmXntYznlLHjgafjga7jgb/lpJbpg6jlhazplospYCk7IH0pOwo=" | base64 -d > "$INSTALL_DIR/server.js"
echo "server.js を作成しました"
# --- index.html ---
echo "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImphIj4KPGhlYWQ+CjxtZXRhIGNoYXJzZXQ9IlVURi04Ij4KPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjAiPgo8bGluayByZWw9Imljb24iIHR5cGU9ImltYWdlL3N2Zyt4bWwiIGhyZWY9Ii9mYXZpY29uLnN2ZyI+Cjx0aXRsZT5FYXN5IEluY3VzPC90aXRsZT4KPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL0B4dGVybS94dGVybUA1LjUuMC9jc3MveHRlcm0ubWluLmNzcyI+CjxzY3JpcHQgc3JjPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL0B4dGVybS94dGVybUA1LjUuMC9saWIveHRlcm0ubWluLmpzIj48L3NjcmlwdD4KPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vQHh0ZXJtL2FkZG9uLWZpdEAwLjEwLjAvbGliL2FkZG9uLWZpdC5taW4uanMiPjwvc2NyaXB0Pgo8c3R5bGU+Cip7bWFyZ2luOjA7cGFkZGluZzowO2JveC1zaXppbmc6Ym9yZGVyLWJveH0KOnJvb3R7LS1iZzojMGYxMTE3Oy0tc3VyZmFjZTojMWExZDI3Oy0tc3VyZmFjZTI6IzI0MjgzNzstLWJvcmRlcjojMmQzMTQ4Oy0tdGV4dDojZTFlNGVkOy0tdGV4dDI6IzhiOTBhNTstLWFjY2VudDojNWI4YWY1Oy0tYWNjZW50LWhvdmVyOiM0YTc3ZTA7LS1ncmVlbjojM2RkNjhjOy0tcmVkOiNmNTViNmE7LS15ZWxsb3c6I2Y1Yzk1YjstLW9yYW5nZTojZjU5YjViOy0tcmFkaXVzOjhweDstLXNoYWRvdzowIDJweCA4cHggcmdiYSgwLDAsMCwuMyl9CmJvZHl7Zm9udC1mYW1pbHk6LWFwcGxlLXN5c3RlbSxCbGlua01hY1N5c3RlbUZvbnQsJ1NlZ29lIFVJJyxSb2JvdG8sc2Fucy1zZXJpZjtiYWNrZ3JvdW5kOnZhcigtLWJnKTtjb2xvcjp2YXIoLS10ZXh0KTttaW4taGVpZ2h0OjEwMHZofQphe2NvbG9yOnZhcigtLWFjY2VudCk7dGV4dC1kZWNvcmF0aW9uOm5vbmV9Ci5oZWFkZXJ7YmFja2dyb3VuZDp2YXIoLS1zdXJmYWNlKTtib3JkZXItYm90dG9tOjFweCBzb2xpZCB2YXIoLS1ib3JkZXIpO3BhZGRpbmc6MTZweCAyNHB4O2Rpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpjZW50ZXI7Z2FwOjE2cHg7cG9zaXRpb246c3RpY2t5O3RvcDowO3otaW5kZXg6MTAwfQouaGVhZGVyIGgxe2ZvbnQtc2l6ZToyMHB4O2ZvbnQtd2VpZ2h0OjYwMH0uaGVhZGVyIC5zdWJ0aXRsZXtjb2xvcjp2YXIoLS10ZXh0Mik7Zm9udC1zaXplOjE0cHh9LmhlYWRlciAuYWN0aW9uc3ttYXJnaW4tbGVmdDphdXRvO2Rpc3BsYXk6ZmxleDtnYXA6OHB4fQouY29udGFpbmVye21heC13aWR0aDoxMjAwcHg7bWFyZ2luOjAgYXV0bztwYWRkaW5nOjI0cHh9Ci5idG57ZGlzcGxheTppbmxpbmUtZmxleDthbGlnbi1pdGVtczpjZW50ZXI7Z2FwOjZweDtwYWRkaW5nOjhweCAxNnB4O2JvcmRlcjpub25lO2JvcmRlci1yYWRpdXM6dmFyKC0tcmFkaXVzKTtmb250LXNpemU6MTRweDtmb250LXdlaWdodDo1MDA7Y3Vyc29yOnBvaW50ZXI7dHJhbnNpdGlvbjphbGwgLjE1c30KLmJ0bjpkaXNhYmxlZHtvcGFjaXR5Oi41O2N1cnNvcjpub3QtYWxsb3dlZH0uYnRuLXByaW1hcnl7YmFja2dyb3VuZDp2YXIoLS1hY2NlbnQpO2NvbG9yOiNmZmZ9LmJ0bi1wcmltYXJ5OmhvdmVyOm5vdCg6ZGlzYWJsZWQpe2JhY2tncm91bmQ6dmFyKC0tYWNjZW50LWhvdmVyKX0KLmJ0bi1kYW5nZXJ7YmFja2dyb3VuZDp2YXIoLS1yZWQpO2NvbG9yOiNmZmZ9LmJ0bi1kYW5nZXI6aG92ZXI6bm90KDpkaXNhYmxlZCl7b3BhY2l0eTouODV9Ci5idG4tZ2hvc3R7YmFja2dyb3VuZDp0cmFuc3BhcmVudDtjb2xvcjp2YXIoLS10ZXh0Mik7Ym9yZGVyOjFweCBzb2xpZCB2YXIoLS1ib3JkZXIpfS5idG4tZ2hvc3Q6aG92ZXI6bm90KDpkaXNhYmxlZCl7YmFja2dyb3VuZDp2YXIoLS1zdXJmYWNlMik7Y29sb3I6dmFyKC0tdGV4dCl9Ci5idG4tcmVzdG9yZXtiYWNrZ3JvdW5kOnJnYmEoNTYsMTg5LDI0OCwuMTUpO2NvbG9yOiMzOGJkZjg7Ym9yZGVyOjFweCBzb2xpZCByZ2JhKDU2LDE4OSwyNDgsLjMpfS5idG4tcmVzdG9yZTpob3Zlcjpub3QoOmRpc2FibGVkKXtiYWNrZ3JvdW5kOnJnYmEoNTYsMTg5LDI0OCwuMjUpO2JvcmRlci1jb2xvcjpyZ2JhKDU2LDE4OSwyNDgsLjUpfQouYnRuLXNte3BhZGRpbmc6NXB4IDEwcHg7Zm9udC1zaXplOjEzcHh9LmJ0bi1pY29ue3BhZGRpbmc6NnB4IDhweH0KLnRhYmxlLXdyYXB7YmFja2dyb3VuZDp2YXIoLS1zdXJmYWNlKTtib3JkZXI6MXB4IHNvbGlkIHZhcigtLWJvcmRlcik7Ym9yZGVyLXJhZGl1czp2YXIoLS1yYWRpdXMpO292ZXJmbG93OmhpZGRlbn0KdGFibGV7d2lkdGg6MTAwJTtib3JkZXItY29sbGFwc2U6Y29sbGFwc2V9CnRoe2JhY2tncm91bmQ6dmFyKC0tc3VyZmFjZTIpO3RleHQtYWxpZ246bGVmdDtwYWRkaW5nOjEycHggMTZweDtmb250LXNpemU6MTNweDtmb250LXdlaWdodDo2MDA7Y29sb3I6dmFyKC0tdGV4dDIpO3RleHQtdHJhbnNmb3JtOnVwcGVyY2FzZTtsZXR0ZXItc3BhY2luZzouNXB4O2JvcmRlci1ib3R0b206MXB4IHNvbGlkIHZhcigtLWJvcmRlcil9CnRke3BhZGRpbmc6MTJweCAxNnB4O2JvcmRlci1ib3R0b206MXB4IHNvbGlkIHZhcigtLWJvcmRlcik7Zm9udC1zaXplOjE1cHg7dmVydGljYWwtYWxpZ246bWlkZGxlfQp0cjpsYXN0LWNoaWxkIHRke2JvcmRlci1ib3R0b206bm9uZX10cjpob3ZlciB0ZHtiYWNrZ3JvdW5kOnJnYmEoOTEsMTM4LDI0NSwuMDQpfQouYmFkZ2V7ZGlzcGxheTppbmxpbmUtYmxvY2s7cGFkZGluZzozcHggMTBweDtib3JkZXItcmFkaXVzOjEycHg7Zm9udC1zaXplOjEycHg7Zm9udC13ZWlnaHQ6NjAwO3RleHQtdHJhbnNmb3JtOnVwcGVyY2FzZTtsZXR0ZXItc3BhY2luZzouM3B4fQouYmFkZ2UtcnVubmluZ3tiYWNrZ3JvdW5kOnJnYmEoNjEsMjE0LDE0MCwuMTUpO2NvbG9yOnZhcigtLWdyZWVuKX0uYmFkZ2Utc3RvcHBlZHtiYWNrZ3JvdW5kOnJnYmEoMjQ1LDkxLDEwNiwuMTUpO2NvbG9yOnZhcigtLXJlZCl9LmJhZGdlLW90aGVye2JhY2tncm91bmQ6cmdiYSgyNDUsMjAxLDkxLC4xNSk7Y29sb3I6dmFyKC0teWVsbG93KX0KLm1vZGFsLW92ZXJsYXl7ZGlzcGxheTpub25lO3Bvc2l0aW9uOmZpeGVkO2luc2V0OjA7YmFja2dyb3VuZDpyZ2JhKDAsMCwwLC42KTt6LWluZGV4OjIwMDtqdXN0aWZ5LWNvbnRlbnQ6Y2VudGVyO2FsaWduLWl0ZW1zOmNlbnRlcn0KLm1vZGFsLW92ZXJsYXkuYWN0aXZle2Rpc3BsYXk6ZmxleH0KLm1vZGFse2JhY2tncm91bmQ6dmFyKC0tc3VyZmFjZSk7Ym9yZGVyOjFweCBzb2xpZCB2YXIoLS1ib3JkZXIpO2JvcmRlci1yYWRpdXM6MTJweDt3aWR0aDo1MjBweDttYXgtd2lkdGg6OTB2dzttYXgtaGVpZ2h0Ojg1dmg7b3ZlcmZsb3cteTphdXRvO2JveC1zaGFkb3c6dmFyKC0tc2hhZG93KX0KLm1vZGFsLWhlYWRlcntwYWRkaW5nOjIwcHggMjRweCAwO2Rpc3BsYXk6ZmxleDtqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjthbGlnbi1pdGVtczpjZW50ZXJ9Lm1vZGFsLWhlYWRlciBoMntmb250LXNpemU6MThweH0KLm1vZGFsLWNsb3Nle2JhY2tncm91bmQ6bm9uZTtib3JkZXI6bm9uZTtjb2xvcjp2YXIoLS10ZXh0Mik7Zm9udC1zaXplOjIycHg7Y3Vyc29yOnBvaW50ZXI7cGFkZGluZzo0cHh9Lm1vZGFsLWNsb3NlOmhvdmVye2NvbG9yOnZhcigtLXRleHQpfQoubW9kYWwtYm9keXtwYWRkaW5nOjIwcHggMjRweH0ubW9kYWwtZm9vdGVye3BhZGRpbmc6MTZweCAyNHB4O2JvcmRlci10b3A6MXB4IHNvbGlkIHZhcigtLWJvcmRlcik7ZGlzcGxheTpmbGV4O2p1c3RpZnktY29udGVudDpmbGV4LWVuZDtnYXA6OHB4fQouZm9ybS1ncm91cHttYXJnaW4tYm90dG9tOjE2cHh9LmZvcm0tZ3JvdXAgbGFiZWx7ZGlzcGxheTpibG9jaztmb250LXNpemU6MTRweDtmb250LXdlaWdodDo1MDA7Y29sb3I6dmFyKC0tdGV4dDIpO21hcmdpbi1ib3R0b206NnB4fQouZm9ybS1ncm91cCBpbnB1dCwuZm9ybS1ncm91cCBzZWxlY3R7d2lkdGg6MTAwJTtwYWRkaW5nOjEwcHggMTJweDtiYWNrZ3JvdW5kOnZhcigtLXN1cmZhY2UyKTtib3JkZXI6MXB4IHNvbGlkIHZhcigtLWJvcmRlcik7Ym9yZGVyLXJhZGl1czp2YXIoLS1yYWRpdXMpO2NvbG9yOnZhcigtLXRleHQpO2ZvbnQtc2l6ZToxNXB4fQouZm9ybS1ncm91cCBpbnB1dDpmb2N1cywuZm9ybS1ncm91cCBzZWxlY3Q6Zm9jdXN7b3V0bGluZTpub25lO2JvcmRlci1jb2xvcjp2YXIoLS1hY2NlbnQpfQouc3Bpbm5lcntkaXNwbGF5OmlubGluZS1ibG9jazt3aWR0aDoxNnB4O2hlaWdodDoxNnB4O2JvcmRlcjoycHggc29saWQgdmFyKC0tYm9yZGVyKTtib3JkZXItdG9wLWNvbG9yOnZhcigtLWFjY2VudCk7Ym9yZGVyLXJhZGl1czo1MCU7YW5pbWF0aW9uOnNwaW4gLjZzIGxpbmVhciBpbmZpbml0ZX0KQGtleWZyYW1lcyBzcGlue3Rve3RyYW5zZm9ybTpyb3RhdGUoMzYwZGVnKX19Ci50b2FzdHtwb3NpdGlvbjpmaXhlZDtib3R0b206MjRweDtyaWdodDoyNHB4O3BhZGRpbmc6MTJweCAyMHB4O2JvcmRlci1yYWRpdXM6dmFyKC0tcmFkaXVzKTtmb250LXNpemU6MTRweDtmb250LXdlaWdodDo1MDA7ei1pbmRleDozMDA7YW5pbWF0aW9uOnNsaWRlVXAgLjNzIGVhc2U7bWF4LXdpZHRoOjQwMHB4O2Rpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpjZW50ZXI7Z2FwOjEwcHh9Ci50b2FzdC1zdWNjZXNze2JhY2tncm91bmQ6dmFyKC0tZ3JlZW4pO2NvbG9yOiMwMDB9LnRvYXN0LWVycm9ye2JhY2tncm91bmQ6dmFyKC0tcmVkKTtjb2xvcjojZmZmfQoudG9hc3Qtd29ya2luZ3tiYWNrZ3JvdW5kOnZhcigtLXN1cmZhY2UyKTtjb2xvcjp2YXIoLS10ZXh0KTtib3JkZXI6MXB4IHNvbGlkIHZhcigtLWFjY2VudCl9LnRvYXN0LXdvcmtpbmcgLnNwaW5uZXJ7Ym9yZGVyLXRvcC1jb2xvcjp2YXIoLS1hY2NlbnQpfQoudG9hc3Qtd29ya2luZy5wdWxzZXthbmltYXRpb246cHVsc2UgMS41cyBlYXNlLWluLW91dCBpbmZpbml0ZX0KQGtleWZyYW1lcyBwdWxzZXswJSwxMDAle29wYWNpdHk6MX01MCV7b3BhY2l0eTouNX19CkBrZXlmcmFtZXMgc2xpZGVVcHtmcm9te3RyYW5zZm9ybTp0cmFuc2xhdGVZKDIwcHgpO29wYWNpdHk6MH10b3t0cmFuc2Zvcm06dHJhbnNsYXRlWSgwKTtvcGFjaXR5OjF9fQpAbWVkaWEobWF4LXdpZHRoOjc2OHB4KXsuaGVhZGVye2ZsZXgtd3JhcDp3cmFwO2dhcDo4cHh9LmhlYWRlciAuYWN0aW9uc3t3aWR0aDoxMDAlfXRke3BhZGRpbmc6OHB4IDEycHg7Zm9udC1zaXplOjEzcHh9LmFjdGlvbnMtY2VsbHtkaXNwbGF5OmZsZXg7ZmxleC13cmFwOndyYXA7Z2FwOjRweH19Ci5zbmFwLXNlY3Rpb257bWFyZ2luLXRvcDo4cHh9LnNuYXAtc2VjdGlvbiBzdW1tYXJ5e2N1cnNvcjpwb2ludGVyO2NvbG9yOnZhcigtLXRleHQyKTtmb250LXNpemU6MTNweDtwYWRkaW5nOjRweCAwfQouc25hcC1saXN0e21hcmdpbi10b3A6NnB4O2Rpc3BsYXk6ZmxleDtmbGV4LWRpcmVjdGlvbjpjb2x1bW47Z2FwOjRweH0KLnNuYXAtaXRlbXtkaXNwbGF5OmZsZXg7YWxpZ24taXRlbXM6Y2VudGVyO2dhcDo4cHg7cGFkZGluZzo2cHggMTBweDtiYWNrZ3JvdW5kOnZhcigtLXN1cmZhY2UyKTtib3JkZXItcmFkaXVzOjZweDtmb250LXNpemU6MTNweH0KLnNuYXAtaXRlbSAuc25hcC1uYW1le2ZsZXg6MTtjb2xvcjp2YXIoLS10ZXh0KX0uc25hcC1pdGVtIC5zbmFwLWRhdGV7Y29sb3I6dmFyKC0tdGV4dDIpO2ZvbnQtc2l6ZToxMnB4fQouaW5zdC1uYW1le2ZvbnQtd2VpZ2h0OjYwMDtmb250LWZhbWlseTonU0YgTW9ubycsQ29uc29sYXMsbW9ub3NwYWNlO2ZvbnQtc2l6ZToxNHB4fQouaW5zdC1uYW1lLmNsaWNrYWJsZXtjdXJzb3I6cG9pbnRlcjt0cmFuc2l0aW9uOmNvbG9yIC4xNXN9Lmluc3QtbmFtZS5jbGlja2FibGU6aG92ZXJ7Y29sb3I6dmFyKC0tYWNjZW50KX0KLm92ZXJ2aWV3LXJvdyB0ZHtwYWRkaW5nOjAgMTZweCAxMnB4ICFpbXBvcnRhbnQ7YmFja2dyb3VuZDpyZ2JhKDkxLDEzOCwyNDUsLjAzKSAhaW1wb3J0YW50fQoub3ZlcnZpZXctcGFuZWx7ZGlzcGxheTpncmlkO2dyaWQtdGVtcGxhdGUtY29sdW1uczoxZnIgMWZyIDFmcjtnYXA6MTZweDtwYWRkaW5nOjEycHg7YmFja2dyb3VuZDp2YXIoLS1zdXJmYWNlMik7Ym9yZGVyOjFweCBzb2xpZCB2YXIoLS1ib3JkZXIpO2JvcmRlci1yYWRpdXM6dmFyKC0tcmFkaXVzKX0KLm92ZXJ2aWV3LXRpdGxle2ZvbnQtc2l6ZToxMnB4O2ZvbnQtd2VpZ2h0OjYwMDtjb2xvcjp2YXIoLS10ZXh0Mik7dGV4dC10cmFuc2Zvcm06dXBwZXJjYXNlO2xldHRlci1zcGFjaW5nOi41cHg7bWFyZ2luLWJvdHRvbTo4cHg7cGFkZGluZy1ib3R0b206NnB4O2JvcmRlci1ib3R0b206MXB4IHNvbGlkIHZhcigtLWJvcmRlcil9Ci5vdmVydmlldy1pdGVte2Rpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpiYXNlbGluZTtnYXA6OHB4O3BhZGRpbmc6M3B4IDA7Zm9udC1zaXplOjE0cHh9Ci5vdmVydmlldy1sYWJlbHtjb2xvcjp2YXIoLS1hY2NlbnQpO2ZvbnQtd2VpZ2h0OjUwMDttaW4td2lkdGg6ODBweDtmb250LWZhbWlseTonU0YgTW9ubycsQ29uc29sYXMsbW9ub3NwYWNlO2ZvbnQtc2l6ZToxM3B4fQoub3ZlcnZpZXctdmFsdWV7Y29sb3I6dmFyKC0tdGV4dCk7Zm9udC1mYW1pbHk6J1NGIE1vbm8nLENvbnNvbGFzLG1vbm9zcGFjZTtmb250LXNpemU6MTNweH0ub3ZlcnZpZXctc2NvcGV7Y29sb3I6dmFyKC0tdGV4dDIpO2ZvbnQtc2l6ZToxMnB4fQoub3ZlcnZpZXctZW1wdHl7Y29sb3I6dmFyKC0tdGV4dDIpO2ZvbnQtc3R5bGU6aXRhbGljO2ZvbnQtc2l6ZToxM3B4fQpAbWVkaWEobWF4LXdpZHRoOjkwMHB4KXsub3ZlcnZpZXctcGFuZWx7Z3JpZC10ZW1wbGF0ZS1jb2x1bW5zOjFmcn19Ci5jb25maXJtLW1zZ3tmb250LXNpemU6MTVweDtsaW5lLWhlaWdodDoxLjY7bWFyZ2luLWJvdHRvbTo4cHh9LmNvbmZpcm0tbmFtZXtmb250LXdlaWdodDo3MDA7Y29sb3I6dmFyKC0tYWNjZW50KX0KLmNoZWNrYm94LWxhYmVse2Rpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpjZW50ZXI7Z2FwOjhweDtwYWRkaW5nOjhweCAxMnB4O2JhY2tncm91bmQ6dmFyKC0tc3VyZmFjZTIpO2JvcmRlcjoxcHggc29saWQgdmFyKC0tYm9yZGVyKTtib3JkZXItcmFkaXVzOnZhcigtLXJhZGl1cyk7Y3Vyc29yOnBvaW50ZXI7Zm9udC1zaXplOjE0cHg7bWFyZ2luLWJvdHRvbTo2cHg7dHJhbnNpdGlvbjphbGwgLjE1c30KLmNoZWNrYm94LWxhYmVsOmhvdmVye2JvcmRlci1jb2xvcjp2YXIoLS1hY2NlbnQpfS5jaGVja2JveC1sYWJlbDpoYXMoaW5wdXQ6Y2hlY2tlZCl7Ym9yZGVyLWNvbG9yOnZhcigtLWFjY2VudCk7YmFja2dyb3VuZDpyZ2JhKDkxLDEzOCwyNDUsLjA4KX0KLmNoZWNrYm94LWxhYmVsIGlucHV0W3R5cGU9Y2hlY2tib3hde2FjY2VudC1jb2xvcjp2YXIoLS1hY2NlbnQpO3dpZHRoOjE2cHg7aGVpZ2h0OjE2cHh9LmNoZWNrYm94LWxhYmVsLmRpc2FibGVke29wYWNpdHk6LjQ7Y3Vyc29yOm5vdC1hbGxvd2VkO3BvaW50ZXItZXZlbnRzOm5vbmV9Ci5jaGVja2JveC1ub3Rle2ZvbnQtc2l6ZToxMnB4O2NvbG9yOnZhcigtLXRleHQyKTttYXJnaW4tdG9wOjRweH0KLmdwdS1pdGVte2Rpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpjZW50ZXI7Z2FwOjhweDtwYWRkaW5nOjhweCAxMnB4O2JhY2tncm91bmQ6dmFyKC0tc3VyZmFjZTIpO2JvcmRlcjoxcHggc29saWQgdmFyKC0tYm9yZGVyKTtib3JkZXItcmFkaXVzOnZhcigtLXJhZGl1cyk7bWFyZ2luLWJvdHRvbTo2cHg7dHJhbnNpdGlvbjphbGwgLjE1c30KLmdwdS1pdGVtOmhvdmVye2JvcmRlci1jb2xvcjp2YXIoLS1hY2NlbnQpfS5ncHUtaXRlbSAuZ3B1LWRlc2N7ZmxleDoxO2ZvbnQtc2l6ZToxNHB4fQouZ3B1LWl0ZW0gLmdwdS1wY2l7Y29sb3I6dmFyKC0tdGV4dDIpO2ZvbnQtc2l6ZToxMnB4O2ZvbnQtZmFtaWx5OidTRiBNb25vJyxDb25zb2xhcyxtb25vc3BhY2V9Ci5ncHUtaXRlbSAuZ3B1LWN1cnJlbnR7Ym9yZGVyLWNvbG9yOnZhcigtLWdyZWVuKTtiYWNrZ3JvdW5kOnJnYmEoNjEsMjE0LDE0MCwuMDgpfS5ncHUtaXRlbSAuZ3B1LWN1cnJlbnQgLmdwdS1sYWJlbHtjb2xvcjp2YXIoLS1ncmVlbik7Zm9udC1zaXplOjEycHg7Zm9udC13ZWlnaHQ6NjAwfQouZ3B1LWl0ZW0gLmdwdS1yZW1vdmV7Y29sb3I6dmFyKC0tcmVkKTtjdXJzb3I6cG9pbnRlcjtmb250LXNpemU6MTNweH0uZ3B1LWl0ZW0gLmdwdS1yZW1vdmU6aG92ZXJ7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZX0KLmdwdS1pdGVtLmNsaWNrYWJsZXtjdXJzb3I6cG9pbnRlcn0uZ3B1LWl0ZW0uY2xpY2thYmxlOmhvdmVye2JvcmRlci1jb2xvcjp2YXIoLS1hY2NlbnQpO2JhY2tncm91bmQ6cmdiYSg5MSwxMzgsMjQ1LC4wOCl9Ci5ncHVzdGF0LWxpbmt7Y3Vyc29yOnBvaW50ZXI7dHJhbnNpdGlvbjpjb2xvciAuMTVzO2NvbG9yOnZhcigtLXRleHQyKX0uZ3B1c3RhdC1saW5rOmhvdmVye2NvbG9yOnZhcigtLWFjY2VudCl9Ci5pbWFnZS1yb3d7ZGlzcGxheTpmbGV4O2dhcDo4cHg7YWxpZ24taXRlbXM6c3RyZXRjaH0uaW1hZ2Utcm93IHNlbGVjdHtmbGV4OjF9LmltYWdlLXJvdyAuYnRue3doaXRlLXNwYWNlOm5vd3JhcH0KLnRlcm1pbmFsLWJhcntkaXNwbGF5OmZsZXg7YWxpZ24taXRlbXM6Y2VudGVyO2p1c3RpZnktY29udGVudDpzcGFjZS1iZXR3ZWVuO3BhZGRpbmc6NnB4IDE2cHg7YmFja2dyb3VuZDp2YXIoLS1zdXJmYWNlMik7Ym9yZGVyLXRvcDoxcHggc29saWQgdmFyKC0tYm9yZGVyKX0KLnRlcm1pbmFsLWJhciAudGVybS10aXRsZXtmb250LXNpemU6MTNweDtmb250LXdlaWdodDo2MDA7Y29sb3I6dmFyKC0tdGV4dDIpfQoudGVybWluYWwtYmFyIC50ZXJtLWFjdGlvbnN7ZGlzcGxheTpmbGV4O2dhcDo2cHg7YWxpZ24taXRlbXM6Y2VudGVyfQojdGVybWluYWxQYW5lbHtkaXNwbGF5Om5vbmU7cG9zaXRpb246Zml4ZWQ7Ym90dG9tOjA7bGVmdDowO3JpZ2h0OjA7aGVpZ2h0OjQwdmg7YmFja2dyb3VuZDp2YXIoLS1zdXJmYWNlKTtib3JkZXItdG9wOm5vbmU7ei1pbmRleDoxNTA7ZmxleC1kaXJlY3Rpb246Y29sdW1ufQojdGVybWluYWxQYW5lbC5hY3RpdmV7ZGlzcGxheTpmbGV4fQojdGVybURyYWdIYW5kbGV7aGVpZ2h0OjZweDtiYWNrZ3JvdW5kOnZhcigtLWJvcmRlcik7Y3Vyc29yOm5zLXJlc2l6ZTtmbGV4LXNocmluazowO3RyYW5zaXRpb246YmFja2dyb3VuZCAuMTVzfQojdGVybURyYWdIYW5kbGU6aG92ZXJ7YmFja2dyb3VuZDp2YXIoLS1hY2NlbnQpfQojdGVybWluYWxDb250YWluZXJ7ZmxleDoxO3BhZGRpbmc6MnB4O292ZXJmbG93OmhpZGRlbjttaW4taGVpZ2h0OjgwcHh9Ci50ZXJtaW5hbC1iYXIgLmJ0bi1naG9zdHtwYWRkaW5nOjRweCAxMHB4O2ZvbnQtc2l6ZToxMnB4fQouY3JlYXRlLXByb2dyZXNzLXBhbmVse3Bvc2l0aW9uOmZpeGVkO2JvdHRvbTowO2xlZnQ6MDtyaWdodDowO2hlaWdodDo1MHZoO2JhY2tncm91bmQ6dmFyKC0tc3VyZmFjZSk7Ym9yZGVyLXRvcDoycHggc29saWQgdmFyKC0tYWNjZW50KTt6LWluZGV4OjE2MDtkaXNwbGF5Om5vbmU7ZmxleC1kaXJlY3Rpb246Y29sdW1ufQouY3JlYXRlLXByb2dyZXNzLXBhbmVsLmFjdGl2ZXtkaXNwbGF5OmZsZXh9Ci5jcmVhdGUtcHJvZ3Jlc3MtaGVhZGVye2Rpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpjZW50ZXI7anVzdGlmeS1jb250ZW50OnNwYWNlLWJldHdlZW47cGFkZGluZzoxMHB4IDE2cHg7YmFja2dyb3VuZDp2YXIoLS1zdXJmYWNlMik7Ym9yZGVyLWJvdHRvbToxcHggc29saWQgdmFyKC0tYm9yZGVyKTtmbGV4LXNocmluazowfQouY3JlYXRlLXByb2dyZXNzLWhlYWRlciAuY3AtdGl0bGV7Zm9udC1zaXplOjE0cHg7Zm9udC13ZWlnaHQ6NjAwO2NvbG9yOnZhcigtLXRleHQpfQouY3JlYXRlLXByb2dyZXNzLWhlYWRlciAuY3Atc3RhdHVze2ZvbnQtc2l6ZToxM3B4O2NvbG9yOnZhcigtLXRleHQyKTtkaXNwbGF5OmZsZXg7YWxpZ24taXRlbXM6Y2VudGVyO2dhcDo4cHh9Ci5jcmVhdGUtcHJvZ3Jlc3MtaGVhZGVyIC5jcC1jbG9zZXtiYWNrZ3JvdW5kOm5vbmU7Ym9yZGVyOm5vbmU7Y29sb3I6dmFyKC0tdGV4dDIpO2ZvbnQtc2l6ZToxOHB4O2N1cnNvcjpwb2ludGVyO3BhZGRpbmc6NHB4IDhweDtib3JkZXItcmFkaXVzOjRweH0uY3JlYXRlLXByb2dyZXNzLWhlYWRlciAuY3AtY2xvc2U6aG92ZXJ7YmFja2dyb3VuZDp2YXIoLS1zdXJmYWNlKTtjb2xvcjp2YXIoLS10ZXh0KX0KI2NyZWF0ZVByb2dyZXNzTG9ne2ZsZXg6MTtvdmVyZmxvdy15OmF1dG87cGFkZGluZzoxMnB4IDE2cHg7Zm9udC1mYW1pbHk6J1NGIE1vbm8nLENvbnNvbGFzLCdDb3VyaWVyIE5ldycsbW9ub3NwYWNlO2ZvbnQtc2l6ZToxM3B4O2xpbmUtaGVpZ2h0OjEuNjtjb2xvcjp2YXIoLS10ZXh0KTt3aGl0ZS1zcGFjZTpwcmUtd3JhcDt3b3JkLWJyZWFrOmJyZWFrLWFsbH0KLmNwLWxvZy1saW5le3BhZGRpbmc6MXB4IDB9LmNwLWxvZy1saW5lLmxvZy10aW1le2NvbG9yOnZhcigtLXRleHQyKX0uY3AtbG9nLWxpbmUubG9nLXN1Y2Nlc3N7Y29sb3I6dmFyKC0tZ3JlZW4pO2ZvbnQtd2VpZ2h0OjYwMH0uY3AtbG9nLWxpbmUubG9nLWVycm9ye2NvbG9yOnZhcigtLXJlZCk7Zm9udC13ZWlnaHQ6NjAwfQo8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5Pgo8ZGl2IGNsYXNzPSJoZWFkZXIiPjxkaXY+PGgxPkVhc3kgSW5jdXM8L2gxPjxkaXYgY2xhc3M9InN1YnRpdGxlIj5JbmN1cyDjgqTjg7Pjgrnjgr/jg7PjgrnnrqHnkIY8L2Rpdj48L2Rpdj48ZGl2IGNsYXNzPSJhY3Rpb25zIj48YnV0dG9uIGNsYXNzPSJidG4gYnRuLWdob3N0IGJ0bi1zbSIgb25jbGljaz0icmVmcmVzaCgpIj7mm7TmlrA8L2J1dHRvbj48YnV0dG9uIGNsYXNzPSJidG4gYnRuLXByaW1hcnkiIG9uY2xpY2s9InNob3dDcmVhdGUoKSI+KyDmlrDopo/jgqTjg7Pjgrnjgr/jg7Pjgrk8L2J1dHRvbj48L2Rpdj48L2Rpdj4KPGRpdiBjbGFzcz0iY29udGFpbmVyIj48ZGl2IGNsYXNzPSJ0YWJsZS13cmFwIj48dGFibGU+PHRoZWFkPjx0cj48dGg+5ZCN5YmNPC90aD48dGg+54q25oWLPC90aD48dGg+5L2c5oiQ5pelPC90aD48dGg+44OX44Ot44OV44Kh44Kk44OrPC90aD48dGg+5pON5L2cPC90aD48L3RyPjwvdGhlYWQ+PHRib2R5IGlkPSJpbnN0YW5jZXMiPjwvdGJvZHk+PC90YWJsZT48L2Rpdj48L2Rpdj4KPGRpdiBpZD0idGVybWluYWxQYW5lbCI+PGRpdiBpZD0idGVybURyYWdIYW5kbGUiPjwvZGl2PjxkaXYgY2xhc3M9InRlcm1pbmFsLWJhciI+PHNwYW4gY2xhc3M9InRlcm0tdGl0bGUiPuOCv+ODvOODn+ODiuODqyDigJQgPHNwYW4gaWQ9InRlcm1JbnN0YW5jZU5hbWUiPjwvc3Bhbj48L3NwYW4+PGRpdiBjbGFzcz0idGVybS1hY3Rpb25zIj48YnV0dG9uIGNsYXNzPSJidG4gYnRuLWdob3N0IGJ0bi1zbSIgb25jbGljaz0icmVzZXRUZXJtaW5hbCgpIiB0aXRsZT0i44K744OD44K344On44Oz44KS44Oq44K744OD44OIIj7in7Mg44Oq44K744OD44OIPC9idXR0b24+PGJ1dHRvbiBjbGFzcz0iYnRuIGJ0bi1naG9zdCBidG4tc20iIG9uY2xpY2s9InRlcm1GaXQoKSIgdGl0bGU9IueUu+mdouOBq+WQiOOCj+OBm+OCiyI+RklUPC9idXR0b24+PGJ1dHRvbiBjbGFzcz0iYnRuIGJ0bi1naG9zdCBidG4tc20iIG9uY2xpY2s9ImNsb3NlVGVybWluYWwoKSI+4pyVIOmWieOBmOOCizwvYnV0dG9uPjwvZGl2PjwvZGl2PjxkaXYgaWQ9InRlcm1pbmFsQ29udGFpbmVyIj48L2Rpdj48L2Rpdj4KPGRpdiBjbGFzcz0iY3JlYXRlLXByb2dyZXNzLXBhbmVsIiBpZD0iY3JlYXRlUHJvZ3Jlc3NQYW5lbCI+PGRpdiBjbGFzcz0iY3JlYXRlLXByb2dyZXNzLWhlYWRlciI+PHNwYW4gY2xhc3M9ImNwLXRpdGxlIj7jgqTjg7Pjgrnjgr/jg7PjgrnkvZzmiJAg4oCUIDxzcGFuIGlkPSJjcEluc3RhbmNlTmFtZSI+PC9zcGFuPjwvc3Bhbj48ZGl2IGNsYXNzPSJjcC1zdGF0dXMiPjxkaXYgY2xhc3M9InNwaW5uZXIiIGlkPSJjcFNwaW5uZXIiPjwvZGl2PjxzcGFuIGlkPSJjcFN0YXR1c1RleHQiPuS9nOaIkOS4rS4uLjwvc3Bhbj48YnV0dG9uIGNsYXNzPSJjcC1jbG9zZSIgb25jbGljaz0iY2xvc2VDcmVhdGVQcm9ncmVzcygpIiB0aXRsZT0i6ZaJ44GY44KLIj4mdGltZXM7PC9idXR0b24+PC9kaXY+PC9kaXY+PGRpdiBpZD0iY3JlYXRlUHJvZ3Jlc3NMb2ciPjwvZGl2PjwvZGl2Pgo8ZGl2IGNsYXNzPSJtb2RhbC1vdmVybGF5IiBpZD0iY3JlYXRlTW9kYWwiPjxkaXYgY2xhc3M9Im1vZGFsIj48ZGl2IGNsYXNzPSJtb2RhbC1oZWFkZXIiPjxoMj7jgqTjg7Pjgrnjgr/jg7PjgrnkvZzmiJA8L2gyPjxidXR0b24gY2xhc3M9Im1vZGFsLWNsb3NlIiBvbmNsaWNrPSJjbG9zZU1vZGFscygpIj4mdGltZXM7PC9idXR0b24+PC9kaXY+PGRpdiBjbGFzcz0ibW9kYWwtYm9keSI+PGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+PGxhYmVsPuOCpOODs+OCueOCv+ODs+OCueWQjTwvbGFiZWw+PGlucHV0IHR5cGU9InRleHQiIGlkPSJuZXdOYW1lIiBwbGFjZWhvbGRlcj0i5L6LOiBteS1zZXJ2ZXIiIHBhdHRlcm49IlthLXpBLVowLTlfLV0rIj48L2Rpdj48ZGl2IGNsYXNzPSJmb3JtLWdyb3VwIj48bGFiZWw+44Kk44Oh44O844K4PC9sYWJlbD48ZGl2IGNsYXNzPSJpbWFnZS1yb3ciPjxzZWxlY3QgaWQ9Im5ld0ltYWdlIj48b3B0aW9uIHZhbHVlPSIiPuiqreOBv+i+vOOBv+S4rS4uLjwvb3B0aW9uPjwvc2VsZWN0PjxidXR0b24gY2xhc3M9ImJ0biBidG4tZ2hvc3QgYnRuLXNtIiBpZD0icmVmcmVzaEltYWdlc0J0biIgb25jbGljaz0iZG9SZWZyZXNoSW1hZ2VzKCkiIHRpdGxlPSLmlrDjgZfjgYTjgqTjg6Hjg7zjgrjjgpLnorroqo0iPuKGuyDnorroqo08L2J1dHRvbj48L2Rpdj48aW5wdXQgdHlwZT0idGV4dCIgaWQ9Im5ld0ltYWdlQ3VzdG9tIiBwbGFjZWhvbGRlcj0i5L6LOiB1YnVudHU6MjQuMDQsIGRlYmlhbjoxMiwgYWxwaW5lLzMuMTgiIHN0eWxlPSJkaXNwbGF5Om5vbmU7bWFyZ2luLXRvcDo4cHgiPjwvZGl2PjxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAiIGlkPSJvcHRzR3JvdXAiIHN0eWxlPSJkaXNwbGF5Om5vbmUiPjxsYWJlbD7jgqrjg5fjgrfjg6fjg7M8L2xhYmVsPjxsYWJlbCBjbGFzcz0iY2hlY2tib3gtbGFiZWwiPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9Im9wdFVwZGF0ZSIgY2hlY2tlZD4g44Ki44OD44OX44OH44O844OIIChhcHQgdXBncmFkZSk8L2xhYmVsPjxsYWJlbCBjbGFzcz0iY2hlY2tib3gtbGFiZWwiPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9Im9wdFRhaWxzY2FsZSIgY2hlY2tlZD4gVGFpbHNjYWxlPC9sYWJlbD48bGFiZWwgY2xhc3M9ImNoZWNrYm94LWxhYmVsIj48aW5wdXQgdHlwZT0iY2hlY2tib3giIGlkPSJvcHREb2NrZXIiPiBEb2NrZXI8L2xhYmVsPjxsYWJlbCBjbGFzcz0iY2hlY2tib3gtbGFiZWwiPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9Im9wdE1vdW50IiBjaGVja2VkPiAvb3B0L2N0bmRhdGEg44Oe44Km44Oz44OIPC9sYWJlbD48bGFiZWwgY2xhc3M9ImNoZWNrYm94LWxhYmVsIj48aW5wdXQgdHlwZT0iY2hlY2tib3giIGlkPSJvcHRNb3VudEx4ZCIgY2hlY2tlZD4gL29wdC9seGQtZGF0YSDjg57jgqbjg7Pjg4g8L2xhYmVsPjwvZGl2PjxkaXYgY2xhc3M9Im1vZGFsLWZvb3RlciI+PGJ1dHRvbiBjbGFzcz0iYnRuIGJ0bi1naG9zdCIgb25jbGljaz0iY2xvc2VNb2RhbHMoKSI+44Kt44Oj44Oz44K744OrPC9idXR0b24+PGJ1dHRvbiBjbGFzcz0iYnRuIGJ0bi1wcmltYXJ5IiBpZD0iY3JlYXRlQnRuIiBvbmNsaWNrPSJkb0NyZWF0ZSgpIiBkaXNhYmxlZD7kvZzmiJA8L2J1dHRvbj48L2Rpdj48L2Rpdj48L2Rpdj4KPGRpdiBjbGFzcz0ibW9kYWwtb3ZlcmxheSIgaWQ9ImNsb25lTW9kYWwiPjxkaXYgY2xhc3M9Im1vZGFsIj48ZGl2IGNsYXNzPSJtb2RhbC1oZWFkZXIiPjxoMj7jgqTjg7Pjgrnjgr/jg7Pjgrnjga7jgq/jg63jg7zjg7M8L2gyPjxidXR0b24gY2xhc3M9Im1vZGFsLWNsb3NlIiBvbmNsaWNrPSJjbG9zZU1vZGFscygpIj4mdGltZXM7PC9idXR0b24+PC9kaXY+PGRpdiBjbGFzcz0ibW9kYWwtYm9keSI+PGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+PGxhYmVsPuOCveODvOOCuTwvbGFiZWw+PGlucHV0IHR5cGU9InRleHQiIGlkPSJjbG9uZVNvdXJjZSIgcmVhZG9ubHk+PC9kaXY+PGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+PGxhYmVsPuaWsOOBl+OBhOOCpOODs+OCueOCv+ODs+OCueWQjTwvbGFiZWw+PGlucHV0IHR5cGU9InRleHQiIGlkPSJjbG9uZU5ld05hbWUiIHBsYWNlaG9sZGVyPSLkvos6IG15LXNlcnZlci1jb3B5Ij48L2Rpdj48L2Rpdj48ZGl2IGNsYXNzPSJtb2RhbC1mb290ZXIiPjxidXR0b24gY2xhc3M9ImJ0biBidG4tZ2hvc3QiIG9uY2xpY2s9ImNsb3NlTW9kYWxzKCkiPuOCreODo+ODs+OCu+ODqzwvYnV0dG9uPjxidXR0b24gY2xhc3M9ImJ0biBidG4tcHJpbWFyeSIgaWQ9ImNsb25lQnRuIiBvbmNsaWNrPSJkb0Nsb25lKCkiPuOCr+ODreODvOODszwvYnV0dG9uPjwvZGl2PjwvZGl2PjwvZGl2Pgo8ZGl2IGNsYXNzPSJtb2RhbC1vdmVybGF5IiBpZD0ic25hcHNob3RNb2RhbCI+PGRpdiBjbGFzcz0ibW9kYWwiPjxkaXYgY2xhc3M9Im1vZGFsLWhlYWRlciI+PGgyPuOCueODiuODg+ODl+OCt+ODp+ODg+ODiOS9nOaIkDwvaDI+PGJ1dHRvbiBjbGFzcz0ibW9kYWwtY2xvc2UiIG9uY2xpY2s9ImNsb3NlTW9kYWxzKCkiPiZ0aW1lczs8L2J1dHRvbj48L2Rpdj48ZGl2IGNsYXNzPSJtb2RhbC1ib2R5Ij48ZGl2IGNsYXNzPSJmb3JtLWdyb3VwIj48bGFiZWw+44Kk44Oz44K544K/44Oz44K5PC9sYWJlbD48aW5wdXQgdHlwZT0idGV4dCIgaWQ9InNuYXBJbnN0YW5jZSIgcmVhZG9ubHk+PC9kaXY+PGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+PGxhYmVsPuOCs+ODoeODs+ODiO+8iOS7u+aEj++8iTwvbGFiZWw+PGlucHV0IHR5cGU9InRleHQiIGlkPSJzbmFwQ29tbWVudCIgcGxhY2Vob2xkZXI9IuS+izogYmVmb3JlLXVwZGF0ZSI+PC9kaXY+PC9kaXY+PGRpdiBjbGFzcz0ibW9kYWwtZm9vdGVyIj48YnV0dG9uIGNsYXNzPSJidG4gYnRuLWdob3N0IiBvbmNsaWNrPSJjbG9zZU1vZGFscygpIj7jgq3jg6Pjg7Pjgrvjg6s8L2J1dHRvbj48YnV0dG9uIGNsYXNzPSJidG4gYnRuLXByaW1hcnkiIGlkPSJzbmFwQnRuIiBvbmNsaWNrPSJkb1NuYXBzaG90KCkiPuS9nOaIkDwvYnV0dG9uPjwvZGl2PjwvZGl2PjwvZGl2Pgo8ZGl2IGNsYXNzPSJtb2RhbC1vdmVybGF5IiBpZD0iY29uZmlybU1vZGFsIj48ZGl2IGNsYXNzPSJtb2RhbCIgc3R5bGU9IndpZHRoOjQwMHB4Ij48ZGl2IGNsYXNzPSJtb2RhbC1oZWFkZXIiPjxoMiBpZD0iY29uZmlybVRpdGxlIj7norroqo08L2gyPjxidXR0b24gY2xhc3M9Im1vZGFsLWNsb3NlIiBvbmNsaWNrPSJjbG9zZU1vZGFscygpIj4mdGltZXM7PC9idXR0b24+PC9kaXY+PGRpdiBjbGFzcz0ibW9kYWwtYm9keSI+PHAgY2xhc3M9ImNvbmZpcm0tbXNnIiBpZD0iY29uZmlybU1zZyI+PC9wPjwvZGl2PjxkaXYgY2xhc3M9Im1vZGFsLWZvb3RlciI+PGJ1dHRvbiBjbGFzcz0iYnRuIGJ0bi1naG9zdCIgb25jbGljaz0iY2xvc2VNb2RhbHMoKSI+44Kt44Oj44Oz44K744OrPC9idXR0b24+PGJ1dHRvbiBjbGFzcz0iYnRuIGJ0bi1kYW5nZXIiIGlkPSJjb25maXJtQnRuIj7norroqo08L2J1dHRvbj48L2Rpdj48L2Rpdj48L2Rpdj4KPGRpdiBjbGFzcz0ibW9kYWwtb3ZlcmxheSIgaWQ9ImdwdU1vZGFsIj48ZGl2IGNsYXNzPSJtb2RhbCIgc3R5bGU9IndpZHRoOjU2MHB4Ij48ZGl2IGNsYXNzPSJtb2RhbC1oZWFkZXIiPjxoMj5HUFUg44OR44K544K544Or44O8PC9oMj48YnV0dG9uIGNsYXNzPSJtb2RhbC1jbG9zZSIgb25jbGljaz0iY2xvc2VNb2RhbHMoKSI+JnRpbWVzOzwvYnV0dG9uPjwvZGl2PjxkaXYgY2xhc3M9Im1vZGFsLWJvZHkiPjxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAiPjxsYWJlbD7jgqTjg7Pjgrnjgr/jg7Pjgrk8L2xhYmVsPjxpbnB1dCB0eXBlPSJ0ZXh0IiBpZD0iZ3B1SW5zdGFuY2UiIHJlYWRvbmx5PjwvZGl2PjxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAiPjxsYWJlbD7nj77lnKjjga4gR1BVIOODh+ODkOOCpOOCuTwvbGFiZWw+PGRpdiBpZD0iZ3B1Q3VycmVudExpc3QiPjwvZGl2PjwvZGl2PjxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAiPjxsYWJlbD7jg5vjgrnjg4jjga7liKnnlKjlj6/og73jgaogR1BVPC9sYWJlbD48ZGl2IGlkPSJncHVBdmFpbGFibGVMaXN0Ij48L2Rpdj48L2Rpdj48L2Rpdj48ZGl2IGNsYXNzPSJtb2RhbC1mb290ZXIiPjxidXR0b24gY2xhc3M9ImJ0biBidG4tZ2hvc3QiIG9uY2xpY2s9ImNsb3NlTW9kYWxzKCkiPumWieOBmOOCizwvYnV0dG9uPjwvZGl2PjwvZGl2PjwvZGl2Pgo8c2NyaXB0PgpsZXQgaW5zdGFuY2VzPVtdLGltYWdlTGlzdD1bXTsKYXN5bmMgZnVuY3Rpb24gYXBpKHBhdGgsb3B0cz17fSl7Y29uc3QgcmVzPWF3YWl0IGZldGNoKHBhdGgse2hlYWRlcnM6eydDb250ZW50LVR5cGUnOidhcHBsaWNhdGlvbi9qc29uJ30sLi4ub3B0cyxib2R5Om9wdHMuYm9keT9KU09OLnN0cmluZ2lmeShvcHRzLmJvZHkpOnVuZGVmaW5lZH0pO3JldHVybiByZXMuanNvbigpfQpmdW5jdGlvbiB0b2FzdChtc2csdHlwZT0nc3VjY2Vzcycpe2NvbnN0IGVsPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO2VsLmNsYXNzTmFtZT1gdG9hc3QgdG9hc3QtJHt0eXBlfWA7ZWwudGV4dENvbnRlbnQ9bXNnO2RvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoZWwpO3NldFRpbWVvdXQoKCk9PmVsLnJlbW92ZSgpLDMwMDApfQpmdW5jdGlvbiB0b2FzdFdvcmtpbmcobXNnKXtjb25zdCBlbD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtlbC5jbGFzc05hbWU9J3RvYXN0IHRvYXN0LXdvcmtpbmcgcHVsc2UnO2VsLmlubmVySFRNTD1gPGRpdiBjbGFzcz0ic3Bpbm5lciI+PC9kaXY+PHNwYW4+JHtlc2MobXNnKX08L3NwYW4+YDtkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGVsKTtyZXR1cm57ZG9uZShva01zZyl7ZWwuY2xhc3NOYW1lPSd0b2FzdCB0b2FzdC1zdWNjZXNzJztlbC5pbm5lckhUTUw9YDxzcGFuPiR7ZXNjKG9rTXNnKX08L3NwYW4+YDtzZXRUaW1lb3V0KCgpPT5lbC5yZW1vdmUoKSwzMDAwKX0sZmFpbChlcnJNc2cpe2VsLmNsYXNzTmFtZT0ndG9hc3QgdG9hc3QtZXJyb3InO2VsLmlubmVySFRNTD1gPHNwYW4+JHtlc2MoZXJyTXNnKX08L3NwYW4+YDtzZXRUaW1lb3V0KCgpPT5lbC5yZW1vdmUoKSw0MDAwKX0scmVtb3ZlKCl7ZWwucmVtb3ZlKCl9fX0KZnVuY3Rpb24gZm9ybWF0RGF0ZShkKXtpZighZClyZXR1cm4nLSc7cmV0dXJuIG5ldyBEYXRlKGQpLnRvTG9jYWxlU3RyaW5nKCdqYS1KUCcse21vbnRoOidzaG9ydCcsZGF5OidudW1lcmljJyxob3VyOicyLWRpZ2l0JyxtaW51dGU6JzItZGlnaXQnfSl9CmZ1bmN0aW9uIHN0YXR1c0JhZGdlKHMpe2NvbnN0IGNscz1zPT09J1J1bm5pbmcnPydydW5uaW5nJzpzPT09J1N0b3BwZWQnPydzdG9wcGVkJzonb3RoZXInO2NvbnN0IGxhYmVscz17UnVubmluZzon5a6f6KGM5LitJyxTdG9wcGVkOiflgZzmraLkuK0nfTtyZXR1cm5gPHNwYW4gY2xhc3M9ImJhZGdlIGJhZGdlLSR7Y2xzfSI+JHtsYWJlbHNbc118fHN9PC9zcGFuPmB9CmFzeW5jIGZ1bmN0aW9uIHJlZnJlc2goKXtjb25zdCB0PWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdpbnN0YW5jZXMnKTt0LmlubmVySFRNTD0nPHRyPjx0ZCBjb2xzcGFuPSI1IiBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Y29sb3I6dmFyKC0tdGV4dDIpO3BhZGRpbmc6MzJweCI+PGRpdiBjbGFzcz0ic3Bpbm5lciI+PC9kaXY+PC90ZD48L3RyPic7dHJ5e2luc3RhbmNlcz1hd2FpdCBhcGkoJy9hcGkvaW5zdGFuY2VzJyk7cmVuZGVyKCl9Y2F0Y2goZSl7dC5pbm5lckhUTUw9YDx0cj48dGQgY29sc3Bhbj0iNSIgc3R5bGU9ImNvbG9yOnZhcigtLXJlZCk7cGFkZGluZzoyNHB4Ij7jgqjjg6njg7w6ICR7ZS5tZXNzYWdlfTwvdGQ+PC90cj5gfX0KYXN5bmMgZnVuY3Rpb24gbG9hZEltYWdlcygpe3RyeXtjb25zdCByPWF3YWl0IGFwaSgnL2FwaS9pbWFnZXMnKTtpbWFnZUxpc3Q9ci5pbWFnZXN8fHI7cmVuZGVySW1hZ2VTZWxlY3QoKX1jYXRjaChlKXtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmV3SW1hZ2UnKS5pbm5lckhUTUw9JzxvcHRpb24gdmFsdWU9Il9fY3VzdG9tX18iPuOCq+OCueOCv+ODoC4uLjwvb3B0aW9uPid9fQpmdW5jdGlvbiByZW5kZXJJbWFnZVNlbGVjdCgpe2NvbnN0IHM9ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25ld0ltYWdlJykscD1zLnZhbHVlO3MuaW5uZXJIVE1MPWltYWdlTGlzdC5tYXAoaT0+YDxvcHRpb24gdmFsdWU9IiR7ZXNjKGkuYWxpYXMpfSI+JHtlc2MoaS5hbGlhcyl9IC0gJHtlc2MoaS5kZXNjcmlwdGlvbil9PC9vcHRpb24+YCkuam9pbignJykrJzxvcHRpb24gdmFsdWU9Il9fY3VzdG9tX18iPuOCq+OCueOCv+ODoC4uLjwvb3B0aW9uPic7aWYocCYmWy4uLnMub3B0aW9uc10uc29tZShvPT5vLnZhbHVlPT09cCkpcy52YWx1ZT1wfQphc3luYyBmdW5jdGlvbiBkb1JlZnJlc2hJbWFnZXMoKXtjb25zdCBiPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdyZWZyZXNoSW1hZ2VzQnRuJyk7Yi5kaXNhYmxlZD10cnVlO2IudGV4dENvbnRlbnQ9J+eiuuiqjeS4rS4uLic7Y29uc3Qgdz10b2FzdFdvcmtpbmcoJ+aWsOOBl+OBhOOCpOODoeODvOOCuOOCkueiuuiqjeS4rS4uLicpO3RyeXtjb25zdCByPWF3YWl0IGFwaSgnL2FwaS9pbWFnZXMvcmVmcmVzaCcse21ldGhvZDonUE9TVCd9KTtpbWFnZUxpc3Q9ci5pbWFnZXN8fHI7cmVuZGVySW1hZ2VTZWxlY3QoKTt3LmRvbmUoYCR7aW1hZ2VMaXN0Lmxlbmd0aH0g5YCL44Gu44Kk44Oh44O844K444GM6KaL44Gk44GL44KK44G+44GX44GfYCl9Y2F0Y2goZSl7dy5mYWlsKGDjgqjjg6njg7w6ICR7ZS5tZXNzYWdlfWApfWZpbmFsbHl7Yi5kaXNhYmxlZD1mYWxzZTtiLnRleHRDb250ZW50PSfihrsg56K66KqNJ319CmZ1bmN0aW9uIHJlbmRlcigpe2NvbnN0IHQ9ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2luc3RhbmNlcycpO2lmKCFpbnN0YW5jZXMubGVuZ3RoKXt0LmlubmVySFRNTD0nPHRyPjx0ZCBjb2xzcGFuPSI1IiBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Y29sb3I6dmFyKC0tdGV4dDIpO3BhZGRpbmc6NDBweCI+44Kk44Oz44K544K/44Oz44K544GM44GC44KK44G+44Gb44KTPC90ZD48L3RyPic7cmV0dXJufXQuaW5uZXJIVE1MPWluc3RhbmNlcy5tYXAoaT0+YDx0cj48dGQ+PHNwYW4gY2xhc3M9Imluc3QtbmFtZSBjbGlja2FibGUiIG9uY2xpY2s9InRvZ2dsZU92ZXJ2aWV3KCcke2VzYyhpLm5hbWUpfScpIj4ke2VzYyhpLm5hbWUpfTwvc3Bhbj48L3RkPjx0ZD4ke3N0YXR1c0JhZGdlKGkuc3RhdHVzKX08L3RkPjx0ZCBzdHlsZT0iY29sb3I6dmFyKC0tdGV4dDIpO2ZvbnQtc2l6ZToxNHB4Ij4ke2Zvcm1hdERhdGUoaS5jcmVhdGVkX2F0KX08L3RkPjx0ZCBzdHlsZT0iY29sb3I6dmFyKC0tdGV4dDIpO2ZvbnQtc2l6ZToxNHB4Ij4keyhpLnByb2ZpbGVzfHxbXSkuam9pbignLCAnKX08L3RkPjx0ZD48ZGl2IGNsYXNzPSJhY3Rpb25zLWNlbGwiPiR7aS5zdGF0dXM9PT0nUnVubmluZyc/YDxidXR0b24gY2xhc3M9ImJ0biBidG4tZ2hvc3QgYnRuLXNtIiBvbmNsaWNrPSJvcGVuVGVybWluYWwoJyR7ZXNjKGkubmFtZSl9JykiIHRpdGxlPSLjgr/jg7zjg5/jg4rjg6siPuKsmyBUZXJtPC9idXR0b24+YDonJ30ke2kuc3RhdHVzPT09J1N0b3BwZWQnP2A8YnV0dG9uIGNsYXNzPSJidG4gYnRuLWdob3N0IGJ0bi1zbSIgb25jbGljaz0iYWN0aW9uKCcke2kubmFtZX0nLCdzdGFydCcpIj7otbfli5U8L2J1dHRvbj5gOmA8YnV0dG9uIGNsYXNzPSJidG4gYnRuLWdob3N0IGJ0bi1zbSIgb25jbGljaz0iYWN0aW9uKCcke2kubmFtZX0nLCdzdG9wJykiPuWBnOatojwvYnV0dG9uPmB9PGJ1dHRvbiBjbGFzcz0iYnRuIGJ0bi1naG9zdCBidG4tc20iIG9uY2xpY2s9ImFjdGlvbignJHtpLm5hbWV9JywncmVzdGFydCcpIj7lho3otbfli5U8L2J1dHRvbj48YnV0dG9uIGNsYXNzPSJidG4gYnRuLWdob3N0IGJ0bi1zbSIgb25jbGljaz0ic2hvd0Nsb25lKCcke2kubmFtZX0nKSI+44Kv44Ot44O844OzPC9idXR0b24+PGJ1dHRvbiBjbGFzcz0iYnRuIGJ0bi1naG9zdCBidG4tc20iIG9uY2xpY2s9InNob3dTbmFwc2hvdCgnJHtpLm5hbWV9JykiPuOCueODiuODg+ODlzwvYnV0dG9uPjxidXR0b24gY2xhc3M9ImJ0biBidG4tZGFuZ2VyIGJ0bi1zbSIgb25jbGljaz0iY29uZmlybUFjdGlvbignJHtpLm5hbWV9JywnZGVsZXRlJykiPuWJiumZpDwvYnV0dG9uPjwvZGl2PjwvdGQ+PC90cj48dHIgaWQ9Im92LSR7ZXNjKGkubmFtZSl9IiBjbGFzcz0ib3ZlcnZpZXctcm93IiBzdHlsZT0iZGlzcGxheTpub25lIj48dGQgY29sc3Bhbj0iNSI+JHtyZW5kZXJPdmVydmlldyhpKX08L3RkPjwvdHI+PHRyIGNsYXNzPSJzbmFwLXJvdyI+PHRkIGNvbHNwYW49IjUiIHN0eWxlPSJwYWRkaW5nOjAgMTZweCA4cHgiPiR7cmVuZGVyU25hcHNob3RzKGkpfTwvdGQ+PC90cj5gKS5qb2luKCcnKX0KZnVuY3Rpb24gcmVuZGVyU25hcHNob3RzKGkpe2NvbnN0IHM9aS5zbmFwc2hvdHN8fFtdO2lmKCFzLmxlbmd0aClyZXR1cm4nJztyZXR1cm5gPGRldGFpbHMgY2xhc3M9InNuYXAtc2VjdGlvbiI+PHN1bW1hcnk+44K544OK44OD44OX44K344On44OD44OIICgke3MubGVuZ3RofSk8L3N1bW1hcnk+PGRpdiBjbGFzcz0ic25hcC1saXN0Ij4ke3MubWFwKHA9PmA8ZGl2IGNsYXNzPSJzbmFwLWl0ZW0iPjxzcGFuIGNsYXNzPSJzbmFwLW5hbWUiPiR7ZXNjKHAubmFtZSl9PC9zcGFuPjxzcGFuIGNsYXNzPSJzbmFwLWRhdGUiPiR7Zm9ybWF0RGF0ZShwLmNyZWF0ZWRfYXQpfTwvc3Bhbj48YnV0dG9uIGNsYXNzPSJidG4gYnRuLXJlc3RvcmUgYnRuLXNtIGJ0bi1pY29uIiBvbmNsaWNrPSJjb25maXJtUmVzdG9yZSgnJHtpLm5hbWV9JywnJHtlc2MocC5uYW1lKX0nKSIgdGl0bGU9IuW+qeWFgyI+4oa6IOW+qeWFgzwvYnV0dG9uPjxidXR0b24gY2xhc3M9ImJ0biBidG4tZGFuZ2VyIGJ0bi1zbSBidG4taWNvbiIgb25jbGljaz0iY29uZmlybURlbGV0ZVNuYXAoJyR7aS5uYW1lfScsJyR7ZXNjKHAubmFtZSl9JykiIHRpdGxlPSLliYrpmaQiPsOXPC9idXR0b24+PC9kaXY+YCkuam9pbignJyl9PC9kaXY+PC9kZXRhaWxzPmB9CmZ1bmN0aW9uIHRvZ2dsZU92ZXJ2aWV3KG4pe2NvbnN0IHI9ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ292LScrbik7aWYoIXIpcmV0dXJuO2NvbnN0IG89ci5zdHlsZS5kaXNwbGF5IT09J25vbmUnO2RvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5vdmVydmlldy1yb3cnKS5mb3JFYWNoKHI9PnIuc3R5bGUuZGlzcGxheT0nbm9uZScpO2lmKCFvKXIuc3R5bGUuZGlzcGxheT0nJ30KZnVuY3Rpb24gcmVuZGVyT3ZlcnZpZXcoaSl7Y29uc3QgbmV0PShpLnN0YXRlJiZpLnN0YXRlLm5ldHdvcmspfHx7fSxkZXZpY2VzPWkuZGV2aWNlc3x8e30saXBzPVtdLG1vdW50cz1bXSxncHVzPVtdO2Zvcihjb25zdFtmLGluZm9db2YgT2JqZWN0LmVudHJpZXMobmV0KSl7aWYoZj09PSdsbycpY29udGludWU7Zm9yKGNvbnN0IGEgb2YoaW5mby5hZGRyZXNzZXN8fFtdKSl7aWYoYS5mYW1pbHk9PT0naW5ldCcpaXBzLnB1c2goe2lmYWNlOmYsYWRkcjphLmFkZHJlc3Msc2NvcGU6YS5zY29wZX0pfX1mb3IoY29uc3RbbixkXW9mIE9iamVjdC5lbnRyaWVzKGRldmljZXMpKXtpZihkLnR5cGU9PT0nZGlzaycpbW91bnRzLnB1c2goe25hbWU6bixzb3VyY2U6ZC5zb3VyY2V8fCctJyxwYXRoOmQucGF0aHx8Jy0nfSk7aWYoZC50eXBlPT09J2dwdScpZ3B1cy5wdXNoKHtuYW1lOm4scGNpOmQucGNpfHwnLScsZ3B1dHlwZTpkLmdwdXR5cGV8fCdwaHlzaWNhbCd9KX1yZXR1cm5gPGRpdiBjbGFzcz0ib3ZlcnZpZXctcGFuZWwiPjxkaXYgY2xhc3M9Im92ZXJ2aWV3LXNlY3Rpb24iPjxkaXYgY2xhc3M9Im92ZXJ2aWV3LXRpdGxlIj5JUCDjgqLjg4njg6zjgrk8L2Rpdj4ke2lwcy5sZW5ndGg/aXBzLm1hcChwPT5gPGRpdiBjbGFzcz0ib3ZlcnZpZXctaXRlbSI+PHNwYW4gY2xhc3M9Im92ZXJ2aWV3LWxhYmVsIj4ke2VzYyhwLmlmYWNlKX08L3NwYW4+PHNwYW4gY2xhc3M9Im92ZXJ2aWV3LXZhbHVlIj4ke2VzYyhwLmFkZHIpfTwvc3Bhbj48c3BhbiBjbGFzcz0ib3ZlcnZpZXctc2NvcGUiPiR7cC5zY29wZX08L3NwYW4+PC9kaXY+YCkuam9pbignJyk6JzxkaXYgY2xhc3M9Im92ZXJ2aWV3LWl0ZW0gb3ZlcnZpZXctZW1wdHkiPklQIOOBquOBl++8iOWBnOatouS4re+8iTwvZGl2Pid9PC9kaXY+PGRpdiBjbGFzcz0ib3ZlcnZpZXctc2VjdGlvbiI+PGRpdiBjbGFzcz0ib3ZlcnZpZXctdGl0bGUiPuODnuOCpuODs+ODiOODh+ODkOOCpOOCuTwvZGl2PiR7bW91bnRzLmxlbmd0aD9tb3VudHMubWFwKG09PmA8ZGl2IGNsYXNzPSJvdmVydmlldy1pdGVtIj48c3BhbiBjbGFzcz0ib3ZlcnZpZXctbGFiZWwiPiR7ZXNjKG0ubmFtZSl9PC9zcGFuPjxzcGFuIGNsYXNzPSJvdmVydmlldy12YWx1ZSI+JHtlc2MobS5zb3VyY2UpfSDihpIgJHtlc2MobS5wYXRoKX08L3NwYW4+PC9kaXY+YCkuam9pbignJyk6JzxkaXYgY2xhc3M9Im92ZXJ2aWV3LWl0ZW0gb3ZlcnZpZXctZW1wdHkiPuOBquOBlzwvZGl2Pid9PC9kaXY+PGRpdiBjbGFzcz0ib3ZlcnZpZXctc2VjdGlvbiI+PGRpdiBjbGFzcz0ib3ZlcnZpZXctdGl0bGUiPkdQVSDjg5Hjgrnjgrnjg6vjg7w8L2Rpdj48ZGl2IGNsYXNzPSJvdmVydmlldy1pdGVtIGdwdXN0YXQtbGluayIgb25jbGljaz0ic2hvd0dwdU1vZGFsKCcke2VzYyhpLm5hbWUpfScpIj4ke2dwdXMubGVuZ3RoP2dwdXMubWFwKGc9PmA8c3BhbiBjbGFzcz0ib3ZlcnZpZXctbGFiZWwiPiR7ZXNjKGcubmFtZSl9PC9zcGFuPjxzcGFuIGNsYXNzPSJvdmVydmlldy12YWx1ZSI+JHtlc2MoZy5ncHV0eXBlKX0gLyAke2VzYyhnLnBjaSl9PC9zcGFuPmApLmpvaW4oJycpOic8c3BhbiBjbGFzcz0ib3ZlcnZpZXctZW1wdHkiPuOBquOBl++8iOOCr+ODquODg+OCr+OBp+ioreWumu+8iTwvc3Bhbj4nfTwvZGl2PjwvZGl2PjwvZGl2PmB9CmZ1bmN0aW9uIGVzYyhzKXtjb25zdCBlPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtlLnRleHRDb250ZW50PXM7cmV0dXJuIGUuaW5uZXJIVE1MfQphc3luYyBmdW5jdGlvbiBhY3Rpb24obixhKXtjb25zdCBsYWJlbHM9e3N0YXJ0Oifotbfli5UnLHN0b3A6J+WBnOatoicscmVzdGFydDon5YaN6LW35YuVJ307Y29uc3Qgdz10b2FzdFdvcmtpbmcoYCR7bGFiZWxzW2FdfHxhfSAke259Li4uYCk7dHJ5e2NvbnN0IHI9YXdhaXQgYXBpKGAvYXBpL2luc3RhbmNlcy8ke259LyR7YX1gLHttZXRob2Q6J1BPU1QnfSk7aWYoci5lcnJvcil0aHJvdyBuZXcgRXJyb3Ioci5lcnJvcik7dy5kb25lKGAke2xhYmVsc1thXXx8YX3lrozkuoZgKTtzZXRUaW1lb3V0KHJlZnJlc2gsNTAwKX1jYXRjaChlKXt3LmZhaWwoYOOCqOODqeODvDogJHtlLm1lc3NhZ2V9YCl9fQpmdW5jdGlvbiB1cGRhdGVDcmVhdGVCdG4oKXtjb25zdCBuPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCduZXdOYW1lJykudmFsdWUudHJpbSgpLGk9Z2V0U2VsZWN0ZWRJbWFnZSgpO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjcmVhdGVCdG4nKS5kaXNhYmxlZD0hbnx8IWl9CmZ1bmN0aW9uIHVwZGF0ZU9wdHNWaXNpYmlsaXR5KCl7Y29uc3QgaT1nZXRTZWxlY3RlZEltYWdlKCk7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ29wdHNHcm91cCcpLnN0eWxlLmRpc3BsYXk9aS5pbmNsdWRlcygndWJ1bnR1Jyk/Jyc6J25vbmUnfQpmdW5jdGlvbiBnZXRTZWxlY3RlZEltYWdlKCl7Y29uc3Qgcz1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmV3SW1hZ2UnKTtyZXR1cm4gcy52YWx1ZT09PSdfX2N1c3RvbV9fJz9kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmV3SW1hZ2VDdXN0b20nKS52YWx1ZS50cmltKCk6cy52YWx1ZX0KYXN5bmMgZnVuY3Rpb24gc2hvd0NyZWF0ZSgpe2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCduZXdOYW1lJykudmFsdWU9Jyc7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NyZWF0ZUJ0bicpLmRpc2FibGVkPXRydWU7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25ld0ltYWdlQ3VzdG9tJykuc3R5bGUuZGlzcGxheT0nbm9uZSc7aWYoIWltYWdlTGlzdC5sZW5ndGgpYXdhaXQgbG9hZEltYWdlcygpO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCduZXdJbWFnZScpLnZhbHVlPWltYWdlTGlzdC5sZW5ndGg/aW1hZ2VMaXN0WzBdLmFsaWFzOidfX2N1c3RvbV9fJztkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmV3SW1hZ2VDdXN0b20nKS52YWx1ZT0nJzt1cGRhdGVPcHRzVmlzaWJpbGl0eSgpO3VwZGF0ZUNyZWF0ZUJ0bigpO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjcmVhdGVNb2RhbCcpLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpfQpkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmV3TmFtZScpLmFkZEV2ZW50TGlzdGVuZXIoJ2lucHV0Jyx1cGRhdGVDcmVhdGVCdG4pOwpkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmV3SW1hZ2UnKS5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCgpPT57Y29uc3QgYz1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmV3SW1hZ2UnKS52YWx1ZT09PSdfX2N1c3RvbV9fJztkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbmV3SW1hZ2VDdXN0b20nKS5zdHlsZS5kaXNwbGF5PWM/Jyc6J25vbmUnO3VwZGF0ZU9wdHNWaXNpYmlsaXR5KCk7dXBkYXRlQ3JlYXRlQnRuKCl9KTsKZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25ld0ltYWdlQ3VzdG9tJykuYWRkRXZlbnRMaXN0ZW5lcignaW5wdXQnLCgpPT57dXBkYXRlT3B0c1Zpc2liaWxpdHkoKTt1cGRhdGVDcmVhdGVCdG4oKX0pOwphc3luYyBmdW5jdGlvbiBkb0NyZWF0ZSgpe2NvbnN0IG49ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ25ld05hbWUnKS52YWx1ZS50cmltKCksaT1nZXRTZWxlY3RlZEltYWdlKCk7aWYoIW58fCFpKXJldHVybjtjb25zdCB1PWkuaW5jbHVkZXMoJ3VidW50dScpLGI9e25hbWU6bixpbWFnZTppfTtpZih1KXtiLnVwZGF0ZT1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnb3B0VXBkYXRlJykuY2hlY2tlZDtiLnRhaWxzY2FsZT1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnb3B0VGFpbHNjYWxlJykuY2hlY2tlZDtiLmRvY2tlcj1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnb3B0RG9ja2VyJykuY2hlY2tlZDtiLm1vdW50PWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdvcHRNb3VudCcpLmNoZWNrZWQ7Yi5tb3VudEx4ZD1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnb3B0TW91bnRMeGQnKS5jaGVja2VkfWNsb3NlTW9kYWxzKCk7c2hvd0NyZWF0ZVByb2dyZXNzKG4pO2NvbnN0IGxvZ0VsPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjcmVhdGVQcm9ncmVzc0xvZycpO3RyeXtjb25zdCByZXM9YXdhaXQgZmV0Y2goJy9hcGkvaW5zdGFuY2VzL2NyZWF0ZS9zdHJlYW0nLHttZXRob2Q6J1BPU1QnLGhlYWRlcnM6eydDb250ZW50LVR5cGUnOidhcHBsaWNhdGlvbi9qc29uJ30sYm9keTpKU09OLnN0cmluZ2lmeShiKX0pO2lmKCFyZXMub2spe2NvbnN0IGVycj1hd2FpdCByZXMuanNvbigpO3Rocm93IG5ldyBFcnJvcihlcnIuZXJyb3J8fCdDcmVhdGUgZmFpbGVkJyl9Y29uc3QgcmVhZGVyPXJlcy5ib2R5LmdldFJlYWRlcigpO2NvbnN0IGRlY29kZXI9bmV3IFRleHREZWNvZGVyKCk7bGV0IGJ1ZmZlcj0nJzt3aGlsZSh0cnVlKXtjb25zdHtkb25lLHZhbHVlfT1hd2FpdCByZWFkZXIucmVhZCgpO2lmKGRvbmUpYnJlYWs7YnVmZmVyKz1kZWNvZGVyLmRlY29kZSh2YWx1ZSx7c3RyZWFtOnRydWV9KTtjb25zdCBsaW5lcz1idWZmZXIuc3BsaXQoJ1xuJyk7YnVmZmVyPWxpbmVzLnBvcCgpO2Zvcihjb25zdCBsaW5lIG9mIGxpbmVzKXtpZihsaW5lLnN0YXJ0c1dpdGgoJ2V2ZW50OicpKXtjb25zdCBldnQ9bGluZS5zbGljZSg2KS50cmltKCl9ZWxzZSBpZihsaW5lLnN0YXJ0c1dpdGgoJ2RhdGE6Jykpe3RyeXtjb25zdCBkYXRhPUpTT04ucGFyc2UobGluZS5zbGljZSg1KSk7aWYoZGF0YS5tZXNzYWdlKWFwcGVuZENyZWF0ZUxvZyhkYXRhLm1lc3NhZ2UpfWNhdGNoKGUpe319fX1pZihidWZmZXIuc3RhcnRzV2l0aCgnZGF0YTonKSl7dHJ5e2NvbnN0IGRhdGE9SlNPTi5wYXJzZShidWZmZXIuc2xpY2UoNSkpO2lmKGRhdGEubWVzc2FnZSlhcHBlbmRDcmVhdGVMb2coZGF0YS5tZXNzYWdlKX1jYXRjaChlKXt9fWZpbmlzaENyZWF0ZVByb2dyZXNzKHRydWUsJ+S9nOaIkOWujOS6hicpfWNhdGNoKGUpe2FwcGVuZENyZWF0ZUxvZygn44Ko44Op44O8OiAnK2UubWVzc2FnZSk7ZmluaXNoQ3JlYXRlUHJvZ3Jlc3MoZmFsc2UsJ+OCqOODqeODvDogJytlLm1lc3NhZ2UpfX0KZnVuY3Rpb24gc2hvd0NyZWF0ZVByb2dyZXNzKG5hbWUpe2NvbnN0IHBhbmVsPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjcmVhdGVQcm9ncmVzc1BhbmVsJyk7cGFuZWwuY2xhc3NMaXN0LmFkZCgnYWN0aXZlJyk7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NwSW5zdGFuY2VOYW1lJykudGV4dENvbnRlbnQ9bmFtZTtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY3JlYXRlUHJvZ3Jlc3NMb2cnKS5pbm5lckhUTUw9Jyc7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NwU3RhdHVzVGV4dCcpLnRleHRDb250ZW50PSfkvZzmiJDkuK0uLi4nO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjcFNwaW5uZXInKS5zdHlsZS5kaXNwbGF5PScnfQpmdW5jdGlvbiBhcHBlbmRDcmVhdGVMb2cobXNnKXtjb25zdCBsb2c9ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NyZWF0ZVByb2dyZXNzTG9nJyk7Y29uc3QgbGluZT1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtsaW5lLmNsYXNzTmFtZT0nY3AtbG9nLWxpbmUnO2NvbnN0IG5vdz1uZXcgRGF0ZSgpLnRvTG9jYWxlVGltZVN0cmluZygnamEtSlAnKTtpZihtc2cuaW5jbHVkZXMoJ+WujOS6hicpfHxtc2cuaW5jbHVkZXMoJ2NyZWF0ZWQnKSl7bGluZS5jbGFzc05hbWU9J2NwLWxvZy1saW5lIGxvZy1zdWNjZXNzJ31lbHNlIGlmKG1zZy5pbmNsdWRlcygn44Ko44Op44O8Jyl8fG1zZy5pbmNsdWRlcygnZXJyb3InKSl7bGluZS5jbGFzc05hbWU9J2NwLWxvZy1saW5lIGxvZy1lcnJvcid9bGluZS50ZXh0Q29udGVudD0nWycrbm93KyddICcrbXNnO2xvZy5hcHBlbmRDaGlsZChsaW5lKTtsb2cuc2Nyb2xsVG9wPWxvZy5zY3JvbGxIZWlnaHR9CmZ1bmN0aW9uIGZpbmlzaENyZWF0ZVByb2dyZXNzKHN1Y2Nlc3MsbXNnKXtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY3BTdGF0dXNUZXh0JykudGV4dENvbnRlbnQ9bXNnO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjcFNwaW5uZXInKS5zdHlsZS5kaXNwbGF5PSdub25lJztpZihzdWNjZXNzKXtyZWZyZXNoKCl9fQpmdW5jdGlvbiBjbG9zZUNyZWF0ZVByb2dyZXNzKCl7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NyZWF0ZVByb2dyZXNzUGFuZWwnKS5jbGFzc0xpc3QucmVtb3ZlKCdhY3RpdmUnKX0KZnVuY3Rpb24gc2hvd0Nsb25lKG4pe2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjbG9uZVNvdXJjZScpLnZhbHVlPW47ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2Nsb25lTmV3TmFtZScpLnZhbHVlPScnO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjbG9uZU1vZGFsJykuY2xhc3NMaXN0LmFkZCgnYWN0aXZlJyl9CmFzeW5jIGZ1bmN0aW9uIGRvQ2xvbmUoKXtjb25zdCBzPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjbG9uZVNvdXJjZScpLnZhbHVlLG49ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2Nsb25lTmV3TmFtZScpLnZhbHVlLnRyaW0oKTtpZighbilyZXR1cm47Y2xvc2VNb2RhbHMoKTtjb25zdCB3PXRvYXN0V29ya2luZyhgIiR7c30iIOOCkiAiJHtufSIg44Gr44Kv44Ot44O844Oz5LitLi4uYCk7dHJ5e2NvbnN0IHI9YXdhaXQgYXBpKGAvYXBpL2luc3RhbmNlcy8ke3N9L2Nsb25lYCx7bWV0aG9kOidQT1NUJyxib2R5OntuZXdOYW1lOm59fSk7aWYoci5lcnJvcil0aHJvdyBuZXcgRXJyb3Ioci5lcnJvcik7dy5kb25lKHIubWVzc2FnZXx8J+OCr+ODreODvOODs+OBl+OBvuOBl+OBnycpO3JlZnJlc2goKX1jYXRjaChlKXt3LmZhaWwoYOOCqOODqeODvDogJHtlLm1lc3NhZ2V9YCl9fQpmdW5jdGlvbiBzaG93U25hcHNob3Qobil7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NuYXBJbnN0YW5jZScpLnZhbHVlPW47ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NuYXBDb21tZW50JykudmFsdWU9Jyc7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NuYXBzaG90TW9kYWwnKS5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKX0KYXN5bmMgZnVuY3Rpb24gZG9TbmFwc2hvdCgpe2NvbnN0IG49ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NuYXBJbnN0YW5jZScpLnZhbHVlLGM9ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NuYXBDb21tZW50JykudmFsdWUudHJpbSgpO2Nsb3NlTW9kYWxzKCk7Y29uc3Qgdz10b2FzdFdvcmtpbmcoYCIke259IiDjga7jgrnjg4rjg4Pjg5fjgrfjg6fjg4Pjg4jjgpLkvZzmiJDkuK0uLi5gKTt0cnl7Y29uc3Qgcj1hd2FpdCBhcGkoYC9hcGkvaW5zdGFuY2VzLyR7bn0vc25hcHNob3RzYCx7bWV0aG9kOidQT1NUJyxib2R5Ontjb21tZW50OmN9fSk7aWYoci5lcnJvcil0aHJvdyBuZXcgRXJyb3Ioci5lcnJvcik7dy5kb25lKHIubWVzc2FnZXx8J+OCueODiuODg+ODl+OCt+ODp+ODg+ODiOOCkuS9nOaIkOOBl+OBvuOBl+OBnycpO3JlZnJlc2goKX1jYXRjaChlKXt3LmZhaWwoYOOCqOODqeODvDogJHtlLm1lc3NhZ2V9YCl9fQpmdW5jdGlvbiBjb25maXJtUmVzdG9yZShpLHMpe2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtVGl0bGUnKS50ZXh0Q29udGVudD0n44K544OK44OD44OX44K344On44OD44OI44Gu5b6p5YWDJztkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybU1zZycpLmlubmVySFRNTD1gPHNwYW4gY2xhc3M9ImNvbmZpcm0tbmFtZSI+JHtlc2MoaSl9PC9zcGFuPiDjgpLjgrnjg4rjg4Pjg5fjgrfjg6fjg4Pjg4ggPHNwYW4gY2xhc3M9ImNvbmZpcm0tbmFtZSI+JHtlc2Mocyl9PC9zcGFuPiDjgYvjgonlvqnlhYPjgZfjgb7jgZnjgYvvvJ88YnI+54++5Zyo44Gu54q25oWL44Gv5aSx44KP44KM44G+44GZ44CCYDtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybUJ0bicpLmNsYXNzTmFtZT0nYnRuIGJ0bi1yZXN0b3JlJztkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybUJ0bicpLm9uY2xpY2s9YXN5bmMoKT0+e2Nsb3NlTW9kYWxzKCk7Y29uc3Qgdz10b2FzdFdvcmtpbmcoYCR7aX0g44KSICR7c30g44GL44KJ5b6p5YWD5LitLi4uYCk7dHJ5e2NvbnN0IHI9YXdhaXQgYXBpKGAvYXBpL2luc3RhbmNlcy8ke2l9L3NuYXBzaG90cy8ke3N9L3Jlc3RvcmVgLHttZXRob2Q6J1BPU1QnfSk7aWYoci5lcnJvcil0aHJvdyBuZXcgRXJyb3Ioci5lcnJvcik7dy5kb25lKCflvqnlhYPjgZfjgb7jgZfjgZ8nKTtyZWZyZXNoKCl9Y2F0Y2goZSl7dy5mYWlsKGDjgqjjg6njg7w6ICR7ZS5tZXNzYWdlfWApfX07ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpcm1Nb2RhbCcpLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpfQpmdW5jdGlvbiBjb25maXJtRGVsZXRlU25hcChpLHMpe2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtVGl0bGUnKS50ZXh0Q29udGVudD0n44K544OK44OD44OX44K344On44OD44OI44Gu5YmK6ZmkJztkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybU1zZycpLmlubmVySFRNTD1gPHNwYW4gY2xhc3M9ImNvbmZpcm0tbmFtZSI+JHtlc2MoaSl9PC9zcGFuPiDjga7jgrnjg4rjg4Pjg5fjgrfjg6fjg4Pjg4ggPHNwYW4gY2xhc3M9ImNvbmZpcm0tbmFtZSI+JHtlc2Mocyl9PC9zcGFuPiDjgpLliYrpmaTjgZfjgb7jgZnjgYvvvJ9gO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtQnRuJykuY2xhc3NOYW1lPSdidG4gYnRuLWRhbmdlcic7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpcm1CdG4nKS5vbmNsaWNrPWFzeW5jKCk9PntjbG9zZU1vZGFscygpO2NvbnN0IHc9dG9hc3RXb3JraW5nKGDjgrnjg4rjg4Pjg5fjgrfjg6fjg4Pjg4ggJHtzfSDjgpLliYrpmaTkuK0uLi5gKTt0cnl7Y29uc3Qgcj1hd2FpdCBhcGkoYC9hcGkvaW5zdGFuY2VzLyR7aX0vc25hcHNob3RzLyR7c31gLHttZXRob2Q6J0RFTEVURSd9KTtpZihyLmVycm9yKXRocm93IG5ldyBFcnJvcihyLmVycm9yKTt3LmRvbmUoJ+WJiumZpOOBl+OBvuOBl+OBnycpO3JlZnJlc2goKX1jYXRjaChlKXt3LmZhaWwoYOOCqOODqeODvDogJHtlLm1lc3NhZ2V9YCl9fTtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybU1vZGFsJykuY2xhc3NMaXN0LmFkZCgnYWN0aXZlJyl9CmZ1bmN0aW9uIGNvbmZpcm1BY3Rpb24obixhKXtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybVRpdGxlJykudGV4dENvbnRlbnQ9J+OCpOODs+OCueOCv+ODs+OCueOBruWJiumZpCc7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpcm1Nc2cnKS5pbm5lckhUTUw9YOOCpOODs+OCueOCv+ODs+OCuSA8c3BhbiBjbGFzcz0iY29uZmlybS1uYW1lIj4ke2VzYyhuKX08L3NwYW4+IOOCkuWJiumZpOOBl+OBvuOBmeOBi++8nzxicj7jgZPjga7mk43kvZzjga/lj5bjgormtojjgZvjgb7jgZvjgpPjgIJgO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtQnRuJykuY2xhc3NOYW1lPSdidG4gYnRuLWRhbmdlcic7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpcm1CdG4nKS5vbmNsaWNrPWFzeW5jKCk9PntjbG9zZU1vZGFscygpO2NvbnN0IHc9dG9hc3RXb3JraW5nKGAke259IOOCkuWJiumZpOS4rS4uLmApO3RyeXtjb25zdCByPWF3YWl0IGFwaShgL2FwaS9pbnN0YW5jZXMvJHtufS9kZWxldGVgLHttZXRob2Q6J1BPU1QnfSk7aWYoci5lcnJvcil0aHJvdyBuZXcgRXJyb3Ioci5lcnJvcik7dy5kb25lKCfliYrpmaTjgZfjgb7jgZfjgZ8nKTtyZWZyZXNoKCl9Y2F0Y2goZSl7dy5mYWlsKGDjgqjjg6njg7w6ICR7ZS5tZXNzYWdlfWApfX07ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpcm1Nb2RhbCcpLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpfQphc3luYyBmdW5jdGlvbiBzaG93R3B1TW9kYWwoaW5zdE5hbWUpe2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdncHVJbnN0YW5jZScpLnZhbHVlPWluc3ROYW1lO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdncHVDdXJyZW50TGlzdCcpLmlubmVySFRNTD0nPGRpdiBjbGFzcz0ib3ZlcnZpZXctZW1wdHkiPuiqreOBv+i+vOOBv+S4rS4uLjwvZGl2Pic7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2dwdUF2YWlsYWJsZUxpc3QnKS5pbm5lckhUTUw9JzxkaXYgY2xhc3M9Im92ZXJ2aWV3LWVtcHR5Ij7oqq3jgb/ovrzjgb/kuK0uLi48L2Rpdj4nO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdncHVNb2RhbCcpLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpO3RyeXtjb25zdCBpPWluc3RhbmNlcy5maW5kKHg9PngubmFtZT09PWluc3ROYW1lKSxkPShpJiZpLmRldmljZXMpfHx7fSxncHVzPU9iamVjdC5lbnRyaWVzKGQpLmZpbHRlcigoWyx2XSk9PnYudHlwZT09PSdncHUnKTtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZ3B1Q3VycmVudExpc3QnKS5pbm5lckhUTUw9Z3B1cy5sZW5ndGg/Z3B1cy5tYXAoKFtuLHZdKT0+YDxkaXYgY2xhc3M9ImdwdS1pdGVtIGdwdS1jdXJyZW50Ij48c3BhbiBjbGFzcz0iZ3B1LWxhYmVsIj4ke2VzYyhuKX08L3NwYW4+PHNwYW4gY2xhc3M9ImdwdS1kZXNjIj4ke2VzYyh2LmdwdXR5cGV8fCdwaHlzaWNhbCcpfSAvICR7ZXNjKHYucGNpfHwnYWxsJyl9PC9zcGFuPjxzcGFuIGNsYXNzPSJncHUtcmVtb3ZlIiBvbmNsaWNrPSJjb25maXJtUmVtb3ZlR3B1KCcke2VzYyhpbnN0TmFtZSl9JywnJHtlc2Mobil9JykiPuWPluOCiuWkluOBmTwvc3Bhbj48L2Rpdj5gKS5qb2luKCcnKTonPGRpdiBjbGFzcz0ib3ZlcnZpZXctZW1wdHkiPkdQVSDjgYzjgqLjgr/jg4Pjg4HjgZXjgozjgabjgYTjgb7jgZvjgpM8L2Rpdj4nfWNhdGNoKGUpe2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdncHVDdXJyZW50TGlzdCcpLmlubmVySFRNTD1gPGRpdiBjbGFzcz0ib3ZlcnZpZXctZW1wdHkiPuOCqOODqeODvDogJHtlc2MoZS5tZXNzYWdlKX08L2Rpdj5gfXRyeXtjb25zdCByPWF3YWl0IGFwaSgnL2FwaS9ncHVzJyksZ3B1cz1yLmdwdXN8fFtdO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdncHVBdmFpbGFibGVMaXN0JykuaW5uZXJIVE1MPWdwdXMubGVuZ3RoP2dwdXMubWFwKGc9PmA8ZGl2IGNsYXNzPSJncHUtaXRlbSBjbGlja2FibGUiIG9uY2xpY2s9ImNvbmZpcm1BZGRHcHUoJyR7ZXNjKGluc3ROYW1lKX0nLCcke2VzYyhnLnBjaSl9JywnJHtlc2MoZy5kZXNjKX0nKSI+PHNwYW4gY2xhc3M9ImdwdS1kZXNjIj4ke2VzYyhnLmRlc2MpfTwvc3Bhbj48c3BhbiBjbGFzcz0iZ3B1LXBjaSI+JHtlc2MoZy5wY2kpfTwvc3Bhbj48L2Rpdj5gKS5qb2luKCcnKTonPGRpdiBjbGFzcz0ib3ZlcnZpZXctZW1wdHkiPuODm+OCueODiOOBqyBHUFUg44GM5qSc5Ye644GV44KM44G+44Gb44KTPC9kaXY+J31jYXRjaChlKXtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZ3B1QXZhaWxhYmxlTGlzdCcpLmlubmVySFRNTD1gPGRpdiBjbGFzcz0ib3ZlcnZpZXctZW1wdHkiPuOCqOODqeODvDogJHtlc2MoZS5tZXNzYWdlKX08L2Rpdj5gfX0KZnVuY3Rpb24gY29uZmlybUFkZEdwdShpbnN0TmFtZSxwY2ksZGVzYyl7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpcm1UaXRsZScpLnRleHRDb250ZW50PSdHUFUg44OR44K544K544Or44O844Gu6L+95YqgJztkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybU1zZycpLmlubmVySFRNTD1gR1BVIDxzcGFuIGNsYXNzPSJjb25maXJtLW5hbWUiPiR7ZXNjKGRlc2MpfTwvc3Bhbj4gKCR7ZXNjKHBjaSl9KSDjgpIgPHNwYW4gY2xhc3M9ImNvbmZpcm0tbmFtZSI+JHtlc2MoaW5zdE5hbWUpfTwvc3Bhbj4g44Gr6L+95Yqg44GX44G+44GZ44GL77yfPGJyPkdQVSDjg5Hjgrnjgrnjg6vjg7zjga/ljbPluqfjgavmnInlirnjgavjgarjgorjgb7jgZnvvIjlho3otbfli5XkuI3opoHvvInjgIJgO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtQnRuJykuY2xhc3NOYW1lPSdidG4gYnRuLWRhbmdlcic7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpcm1CdG4nKS5vbmNsaWNrPWFzeW5jKCk9PntjbG9zZU1vZGFscygpO2NvbnN0IHc9dG9hc3RXb3JraW5nKGBHUFUg44KSICR7aW5zdE5hbWV9IOOBq+i/veWKoOS4rS4uLmApO3RyeXtjb25zdCByPWF3YWl0IGFwaShgL2FwaS9pbnN0YW5jZXMvJHtpbnN0TmFtZX0vZ3B1L2FkZGAse21ldGhvZDonUE9TVCcsYm9keTp7cGNpfX0pO2lmKHIuZXJyb3IpdGhyb3cgbmV3IEVycm9yKHIuZXJyb3IpO3cuZG9uZShyLm1lc3NhZ2V8fCdHUFUg44KS6L+95Yqg44GX44G+44GX44GfJyk7cmVmcmVzaCgpO3NldFRpbWVvdXQoKCk9PnNob3dHcHVNb2RhbChpbnN0TmFtZSksNjAwKX1jYXRjaChlKXt3LmZhaWwoYOOCqOODqeODvDogJHtlLm1lc3NhZ2V9YCl9fTtjbG9zZU1vZGFscygpO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtTW9kYWwnKS5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKX0KZnVuY3Rpb24gY29uZmlybVJlbW92ZUdwdShpbnN0TmFtZSxkZXZpY2VOYW1lKXtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybVRpdGxlJykudGV4dENvbnRlbnQ9J0dQVSDjg5Hjgrnjgrnjg6vjg7zjga7lj5bjgorlpJbjgZcnO2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtTXNnJykuaW5uZXJIVE1MPWBHUFUg44OH44OQ44Kk44K5IDxzcGFuIGNsYXNzPSJjb25maXJtLW5hbWUiPiR7ZXNjKGRldmljZU5hbWUpfTwvc3Bhbj4g44KSIDxzcGFuIGNsYXNzPSJjb25maXJtLW5hbWUiPiR7ZXNjKGluc3ROYW1lKX08L3NwYW4+IOOBi+OCieWPluOCiuWkluOBl+OBvuOBmeOBi++8n2A7ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbmZpcm1CdG4nKS5jbGFzc05hbWU9J2J0biBidG4tZGFuZ2VyJztkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybUJ0bicpLm9uY2xpY2s9YXN5bmMoKT0+e2Nsb3NlTW9kYWxzKCk7Y29uc3Qgdz10b2FzdFdvcmtpbmcoYEdQVSDjgpIgJHtpbnN0TmFtZX0g44GL44KJ5Y+W44KK5aSW44GX5LitLi4uYCk7dHJ5e2NvbnN0IHI9YXdhaXQgYXBpKGAvYXBpL2luc3RhbmNlcy8ke2luc3ROYW1lfS9ncHUvcmVtb3ZlYCx7bWV0aG9kOidQT1NUJyxib2R5OntkZXZpY2VOYW1lfX0pO2lmKHIuZXJyb3IpdGhyb3cgbmV3IEVycm9yKHIuZXJyb3IpO3cuZG9uZSgnR1BVIOOCkuWPluOCiuWkluOBl+OBvuOBl+OBnycpO3JlZnJlc2goKTtzZXRUaW1lb3V0KCgpPT5zaG93R3B1TW9kYWwoaW5zdE5hbWUpLDYwMCl9Y2F0Y2goZSl7dy5mYWlsKGDjgqjjg6njg7w6ICR7ZS5tZXNzYWdlfWApfX07Y2xvc2VNb2RhbHMoKTtkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29uZmlybU1vZGFsJykuY2xhc3NMaXN0LmFkZCgnYWN0aXZlJyl9CmZ1bmN0aW9uIGNsb3NlTW9kYWxzKCl7ZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLm1vZGFsLW92ZXJsYXknKS5mb3JFYWNoKG09Pm0uY2xhc3NMaXN0LnJlbW92ZSgnYWN0aXZlJykpfQpkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcubW9kYWwtb3ZlcmxheScpLmZvckVhY2gobT0+e20uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLGU9PntpZihlLnRhcmdldD09PW0pY2xvc2VNb2RhbHMoKX0pfSk7CgovLyAtLS0gVGVybWluYWwgKHh0ZXJtLmpzICsgV2ViU29ja2V0KSAtLS0KbGV0IHRlcm09bnVsbCwgdGVybVdzPW51bGwsIHRlcm1BZGRvbkZpdD1udWxsOwpmdW5jdGlvbiBvcGVuVGVybWluYWwobmFtZSl7CiAgaWYodGVybVdzKXt0ZXJtV3MuY2xvc2UoKTt0ZXJtV3M9bnVsbH0KICBpZih0ZXJtKXt0ZXJtLmRpc3Bvc2UoKTt0ZXJtPW51bGx9CiAgY29uc3QgcGFuZWw9ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Rlcm1pbmFsUGFuZWwnKTsKICBwYW5lbC5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKTsKICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndGVybUluc3RhbmNlTmFtZScpLnRleHRDb250ZW50PW5hbWU7CiAgY29uc3QgY29udGFpbmVyPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0ZXJtaW5hbENvbnRhaW5lcicpOwogIGNvbnRhaW5lci5pbm5lckhUTUw9Jyc7CiAgdGVybT1uZXcgVGVybWluYWwoe2N1cnNvckJsaW5rOnRydWUsZm9udFNpemU6MTQsZm9udEZhbWlseToiJ1NGIE1vbm8nLENvbnNvbGFzLCdDb3VyaWVyIE5ldycsbW9ub3NwYWNlIix0aGVtZTp7YmFja2dyb3VuZDonIzBkMTExNycsZm9yZWdyb3VuZDonI2M5ZDFkOScsY3Vyc29yOicjNThhNmZmJyxzZWxlY3Rpb25CYWNrZ3JvdW5kOidyZ2JhKDU2LDEzOSwyNTMsMC40KScsYmxhY2s6JyMwZDExMTcnLHJlZDonI2Y4NTE0OScsZ3JlZW46JyMzZmI5NTAnLHllbGxvdzonI2QyOTkyMicsYmx1ZTonIzU4YTZmZicsbWFnZW50YTonI2JjOGNmZicsY3lhbjonIzM5YzVjZicsd2hpdGU6JyNjOWQxZDknLGJyaWdodEJsYWNrOicjNmU3NjgxJyxicmlnaHRSZWQ6JyNmODUxNDknLGJyaWdodEdyZWVuOicjM2ZiOTUwJyxicmlnaHRZZWxsb3c6JyNkMjk5MjInLGJyaWdodEJsdWU6JyM1OGE2ZmYnLGJyaWdodE1hZ2VudGE6JyNiYzhjZmYnLGJyaWdodEN5YW46JyM1NmQ0ZGQnLGJyaWdodFdoaXRlOicjZjBmNmZjJ319KTsKICB0ZXJtQWRkb25GaXQ9bmV3IEZpdEFkZG9uLkZpdEFkZG9uKCk7CiAgdGVybS5sb2FkQWRkb24odGVybUFkZG9uRml0KTsKICB0ZXJtLm9wZW4oY29udGFpbmVyKTsKICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCk9Pnt0ZXJtQWRkb25GaXQuZml0KCk7Y29ubmVjdFRlcm1XcyhuYW1lKX0pOwp9CmZ1bmN0aW9uIGNvbm5lY3RUZXJtV3MobmFtZSl7CiAgY29uc3QgcHJvdG89bG9jYXRpb24ucHJvdG9jb2w9PT0naHR0cHM6Jz8nd3NzOic6J3dzOic7CiAgdGVybVdzPW5ldyBXZWJTb2NrZXQoYCR7cHJvdG99Ly8ke2xvY2F0aW9uLmhvc3R9L3dzL3Rlcm1pbmFsP2luc3RhbmNlPSR7ZW5jb2RlVVJJQ29tcG9uZW50KG5hbWUpfSZjb2xzPSR7dGVybS5jb2xzfSZyb3dzPSR7dGVybS5yb3dzfWApOwogIHRlcm0ub25EYXRhKGQ9PntpZih0ZXJtV3MmJnRlcm1Xcy5yZWFkeVN0YXRlPT09V2ViU29ja2V0Lk9QRU4pdGVybVdzLnNlbmQoSlNPTi5zdHJpbmdpZnkoe3R5cGU6J2lucHV0JyxkYXRhOmR9KSl9KTsKICB0ZXJtLm9uUmVzaXplKCh7Y29scyxyb3dzfSk9PntpZih0ZXJtV3MmJnRlcm1Xcy5yZWFkeVN0YXRlPT09V2ViU29ja2V0Lk9QRU4pdGVybVdzLnNlbmQoSlNPTi5zdHJpbmdpZnkoe3R5cGU6J3Jlc2l6ZScsY29scyxyb3dzfSkpfSk7CiAgdGVybVdzLm9ubWVzc2FnZT1lPT57dHJ5e2NvbnN0IG09SlNPTi5wYXJzZShlLmRhdGEpO2lmKG0udHlwZT09PSdvdXRwdXQnKXRlcm0ud3JpdGUobS5kYXRhKTtpZihtLnR5cGU9PT0nZXhpdCcpdGVybS53cml0ZSgnXHJcblvjgrvjg4Pjgrfjg6fjg7PntYLkuoZdXHJcbicpfWNhdGNoKGV4KXt9fTsKICB0ZXJtV3Mub25jbG9zZT0oKT0+e2lmKHRlcm0pdGVybS53cml0ZSgnXHJcblvmjqXntprjgYzliIfmlq3jgZXjgozjgb7jgZfjgZ9dXHJcbicpfTsKfQpmdW5jdGlvbiB0ZXJtRml0KCl7CiAgaWYodGVybUFkZG9uRml0JiZ0ZXJtKXsKICAgIHRlcm1BZGRvbkZpdC5maXQoKTsKICAgIGlmKHRlcm1XcyYmdGVybVdzLnJlYWR5U3RhdGU9PT1XZWJTb2NrZXQuT1BFTil7CiAgICAgIHRlcm1Xcy5zZW5kKEpTT04uc3RyaW5naWZ5KHt0eXBlOidyZXNpemUnLGNvbHM6dGVybS5jb2xzLHJvd3M6dGVybS5yb3dzfSkpOwogICAgfQogIH0KfQphc3luYyBmdW5jdGlvbiByZXNldFRlcm1pbmFsKCl7CiAgY29uc3QgbmFtZT1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndGVybUluc3RhbmNlTmFtZScpLnRleHRDb250ZW50OwogIGlmKCFuYW1lKXJldHVybjsKICB0cnl7YXdhaXQgZmV0Y2goYC9hcGkvdGVybWluYWwvcmVzZXQvJHtlbmNvZGVVUklDb21wb25lbnQobmFtZSl9YCx7bWV0aG9kOidQT1NUJ30pfWNhdGNoKGUpe30KICBpZih0ZXJtV3Mpe3Rlcm1Xcy5jbG9zZSgpO3Rlcm1Xcz1udWxsfQogIGlmKHRlcm0pe3Rlcm0uZGlzcG9zZSgpO3Rlcm09bnVsbH0KICB0ZXJtQWRkb25GaXQ9bnVsbDsKICBvcGVuVGVybWluYWwobmFtZSk7Cn0KZnVuY3Rpb24gY2xvc2VUZXJtaW5hbCgpewogIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0ZXJtaW5hbFBhbmVsJykuY2xhc3NMaXN0LnJlbW92ZSgnYWN0aXZlJyk7CiAgaWYodGVybVdzKXt0ZXJtV3MuY2xvc2UoKTt0ZXJtV3M9bnVsbH0KICBpZih0ZXJtKXt0ZXJtLmRpc3Bvc2UoKTt0ZXJtPW51bGx9CiAgdGVybUFkZG9uRml0PW51bGw7Cn0KKGZ1bmN0aW9uKCl7CiAgY29uc3QgaGFuZGxlPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0ZXJtRHJhZ0hhbmRsZScpOwogIGNvbnN0IHBhbmVsPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd0ZXJtaW5hbFBhbmVsJyk7CiAgbGV0IGRyYWdnaW5nPWZhbHNlLHN0YXJ0WT0wLHN0YXJ0SD0wOwogIGhhbmRsZS5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWRvd24nLGU9PnsKICAgIGRyYWdnaW5nPXRydWU7c3RhcnRZPWUuY2xpZW50WTtzdGFydEg9cGFuZWwub2Zmc2V0SGVpZ2h0OwogICAgZG9jdW1lbnQuYm9keS5zdHlsZS5jdXJzb3I9J25zLXJlc2l6ZSc7ZG9jdW1lbnQuYm9keS5zdHlsZS51c2VyU2VsZWN0PSdub25lJzsKICAgIGUucHJldmVudERlZmF1bHQoKTsKICB9KTsKICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLGU9PnsKICAgIGlmKCFkcmFnZ2luZylyZXR1cm47CiAgICBjb25zdCBoPU1hdGgubWF4KDEwMCxNYXRoLm1pbih3aW5kb3cuaW5uZXJIZWlnaHQtNTAsc3RhcnRIKyhzdGFydFktZS5jbGllbnRZKSkpOwogICAgcGFuZWwuc3R5bGUuaGVpZ2h0PWgrJ3B4JzsKICAgIGlmKHRlcm1BZGRvbkZpdCYmdGVybSl0ZXJtQWRkb25GaXQuZml0KCk7CiAgfSk7CiAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsKCk9PnsKICAgIGlmKCFkcmFnZ2luZylyZXR1cm47ZHJhZ2dpbmc9ZmFsc2U7CiAgICBkb2N1bWVudC5ib2R5LnN0eWxlLmN1cnNvcj0nJztkb2N1bWVudC5ib2R5LnN0eWxlLnVzZXJTZWxlY3Q9Jyc7CiAgfSk7Cn0pKCk7CgpyZWZyZXNoKCk7bG9hZEltYWdlcygpOwo8L3NjcmlwdD4KPC9ib2R5Pgo8L2h0bWw+Cg==" | base64 -d > "$INSTALL_DIR/public/index.html"
echo "index.html を作成しました"
# --- systemd ---
SERVICE_FILE="/etc/systemd/system/easy-incus.service"
if [ -w /etc/systemd/system ] 2>/dev/null || [ "$(id -u)" -eq 0 ]; then
cat > "$SERVICE_FILE" << SVCEOF
[Unit]
Description=Easy Incus UI
After=network.target incus.service
[Service]
Type=simple
ExecStart=${NODE_PATH} ${INSTALL_DIR}/server.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
SVCEOF
if command -v systemctl &>/dev/null; then
systemctl daemon-reload
systemctl enable easy-incus
systemctl restart easy-incus
echo "Systemd サービスをインストールし起動しました"
else
echo "systemctl が見つかりません。手動で起動します..."
setsid ${NODE_PATH} ${INSTALL_DIR}/server.js </dev/null > /tmp/easy-incus.log 2>&1 &
echo "Easy Incus UI を手動起動しました (PID: $!)"
fi
else
echo ""
echo "NOTE: root 権限がないため systemd サービスを作成できません。"
echo "手動起動: setsid ${NODE_PATH} ${INSTALL_DIR}/server.js </dev/null > /tmp/easy-incus.log 2>&1 &"
fi
# --- Tailscale Serve (HTTPS / Tailnet限定で公開) ---
echo ""
echo "Tailscale Serve を設定中..."
TAILSCALE_PORT=$PORT
tailscale serve --https="${TAILSCALE_PORT}" off >/dev/null 2>&1 || true
tailscale serve --bg --https="${TAILSCALE_PORT}" "http://127.0.0.1:${PORT}"
TAILSCALE_DOMAIN=""
if command -v jq &>/dev/null; then
TAILSCALE_DOMAIN=$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//')
else
TAILSCALE_DOMAIN=$(tailscale status --json | python3 -c "import json,sys;print(json.load(sys.stdin)['Self']['DNSName'].rstrip('.'))" 2>/dev/null || echo "")
fi
if [ -n "$TAILSCALE_DOMAIN" ]; then
echo "Tailscale Serve 設定完了: https://${TAILSCALE_DOMAIN}:${TAILSCALE_PORT}"
else
echo "WARNING: Tailscale ドメインの取得に失敗しました。'tailscale serve status' で確認してください。"
fi
echo ""
echo "=== インストール完了 ==="
echo " Node: ${NODE_PATH} ($(${NODE_PATH} -v))"
if [ -n "$TAILSCALE_DOMAIN" ]; then
echo " URL: https://${TAILSCALE_DOMAIN}:${TAILSCALE_PORT} (Tailnet内のみ)"
else
echo " URL: tailscale serve status で確認してください"
fi
echo " Dir: ${INSTALL_DIR}"
echo ""
opencode
curl -fsSL https://opencode.ai/install | bash


