# ═══════════════════════════════════════════════════════════════════════════════ # Chat2API — HuggingFace Space 版(单文件部署版) # 基于 Node.js,集成 Chat2API 项目 + code-server 按需启停 IDE # 持久化存储:HuggingFace Dataset(每小时自动备份,重启自动恢复) # # 修复说明: # 原版 Dockerfile 用 RUN cat <<'EOF'> 写脚本,内部嵌套 heredoc 时 # BuildKit 会提前截断,导致脚本为空文件,运行时报 exit 127。 # 本版改用 RUN python3 -c "import base64; open(...).write(base64.b64decode(...))" # 将所有脚本以 base64 形式内嵌,彻底绕开 heredoc 嵌套问题。 # ═══════════════════════════════════════════════════════════════════════════════ FROM node:24-slim # ── 1. 基础系统依赖 + Electron/Chromium 运行库 + Xvfb ───────────────────────── RUN apt-get update && apt-get install -y --no-install-recommends \ git openssh-client build-essential python3 python3-pip \ g++ make ca-certificates curl wget nginx \ xvfb \ libgbm1 libglib2.0-0 libnss3 libatk1.0-0 libatk-bridge2.0-0 \ libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \ libxfixes3 libxrandr2 libpangocairo-1.0-0 libcairo2 \ libasound2 libxtst6 libx11-xcb1 libxcb-dri3-0 \ fonts-liberation libappindicator3-1 xdg-utils \ && rm -rf /var/lib/apt/lists/* # ── 1.1. 安装 code-server ───────────────────────────────────────────────────── RUN curl -fsSL https://code-server.dev/install.sh | sh # ── 2. 安装 GitHub CLI ──────────────────────────────────────────────────────── RUN mkdir -p -m 755 /etc/apt/keyrings \ && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \ && cat $out | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ && mkdir -p -m 755 /etc/apt/sources.list.d \ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ && apt-get update \ && apt-get install -y --no-install-recommends gh \ && rm -rf /var/lib/apt/lists/* \ && gh --version # ── 3. 安装 HuggingFace Hub ─────────────────────────────────────────────────── RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages # ── 4. 环境与 Git 配置 ──────────────────────────────────────────────────────── RUN update-ca-certificates && \ git config --global http.sslVerify false && \ git config --global url."https://github.com/".insteadOf ssh://git@github.com/ # ── 5. 克隆并构建 Chat2API 项目 ─────────────────────────────────────────────── WORKDIR /app/chat2api RUN git clone --depth=1 https://github.com/xiaoY233/Chat2API.git . && \ npm install && \ npm run build:linux 2>/dev/null || true # ── 6. 环境变量默认值 ───────────────────────────────────────────────────────── ENV PORT=7860 \ HOME=/root \ PYTHONUNBUFFERED=1 \ DISPLAY=:99 # ── 7. 内嵌所有脚本(base64 编码,彻底规避 BuildKit heredoc 嵌套截断问题)────── # 每个 RUN 指令用 python3 一行完成:decode → 写文件 → 设权限 # 原理:base64 字符串中不含任何 shell 特殊字符,不受 heredoc 影响。 RUN python3 -c "import base64,os; d=base64.b64decode('aW1wb3J0IG9zLCBzeXMsIHRhcmZpbGUsIHNodXRpbApmcm9tIGh1Z2dpbmdmYWNlX2h1YiBpbXBvcnQgSGZBcGksIGhmX2h1Yl9kb3dubG9hZApmcm9tIGRhdGV0aW1lIGltcG9ydCBkYXRldGltZSwgdGltZWRlbHRhCgphcGkgPSBIZkFwaSgpCnJlcG9faWQgPSBvcy5nZXRlbnYoIkhGX0RBVEFTRVQiKQp0b2tlbiAgID0gb3MuZ2V0ZW52KCJIRl9UT0tFTiIpCgpEQVRBX0RJUiA9ICIvcm9vdC8uY2hhdDJhcGkiCgojIOKUgOKUgCDlt6Xlhbflh73mlbAg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgpkZWYgX3BhcnNlX3NraXBfbGlzdChlbnZfdmFyKToKICAgIHJhdyA9IG9zLmdldGVudihlbnZfdmFyLCAiIikuc3RyaXAoKQogICAgaWYgbm90IHJhdzoKICAgICAgICByZXR1cm4gc2V0KCkKICAgIHJldHVybiB7cy5zdHJpcCgpLnN0cmlwKCIvIikgZm9yIHMgaW4gcmF3LnNwbGl0KCIsIikgaWYgcy5zdHJpcCgpfQoKZGVmIF9pc19za2lwcGVkKHJlbF9wYXRoLCBza2lwX3NldCk6CiAgICByZWwgPSByZWxfcGF0aC5zdHJpcCgiLyIpCiAgICBmb3Igc2tpcCBpbiBza2lwX3NldDoKICAgICAgICBpZiByZWwgPT0gc2tpcCBvciByZWwuc3RhcnRzd2l0aChza2lwICsgIi8iKToKICAgICAgICAgICAgcmV0dXJuIFRydWUKICAgIHJldHVybiBGYWxzZQoKZGVmIF93YWxrX2xvY2FsKGJhc2VfZGlyLCBza2lwX3NldD1Ob25lKToKICAgIHJlc3VsdHMgPSBbXQogICAgaWYgbm90IG9zLnBhdGguaXNkaXIoYmFzZV9kaXIpOgogICAgICAgIHJldHVybiByZXN1bHRzCiAgICBmb3IgZGlycGF0aCwgZGlybmFtZXMsIGZpbGVuYW1lcyBpbiBvcy53YWxrKGJhc2VfZGlyKToKICAgICAgICBmb3IgZm5hbWUgaW4gZmlsZW5hbWVzOgogICAgICAgICAgICBhYnNfcGF0aCA9IG9zLnBhdGguam9pbihkaXJwYXRoLCBmbmFtZSkKICAgICAgICAgICAgcmVsX3RvX2Jhc2UgPSBvcy5wYXRoLnJlbHBhdGgoYWJzX3BhdGgsIGJhc2VfZGlyKQogICAgICAgICAgICBpZiBza2lwX3NldCBpcyBub3QgTm9uZToKICAgICAgICAgICAgICAgIHJlbF90b19kYXRhID0gb3MucGF0aC5yZWxwYXRoKGFic19wYXRoLCBEQVRBX0RJUikKICAgICAgICAgICAgICAgIGlmIF9pc19za2lwcGVkKHJlbF90b19kYXRhLCBza2lwX3NldCk6CiAgICAgICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgcmVzdWx0cy5hcHBlbmQoKGFic19wYXRoLCByZWxfdG9fYmFzZSkpCiAgICByZXR1cm4gcmVzdWx0cwoKIyDilIDilIAgcmVzdG9yZSgpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKZGVmIHJlc3RvcmUoKToKICAgIGlmIG5vdCByZXBvX2lkIG9yIG5vdCB0b2tlbjoKICAgICAgICBwcmludCgiU2tpcCBSZXN0b3JlOiBIRl9EQVRBU0VUIG9yIEhGX1RPS0VOIG5vdCBzZXQiKQogICAgICAgIHJldHVybgoKICAgIHJlc3RvcmVfc2tpcF9yYXcgPSBvcy5nZXRlbnYoIlJFU1RPUkVfU0tJUCIsICIiKS5zdHJpcCgpCiAgICBpZiByZXN0b3JlX3NraXBfcmF3ID09ICJhbGwiOgogICAgICAgIHByaW50KCJSZXN0b3JlIHNraXA6IFJFU1RPUkVfU0tJUD1hbGwsIHNraXBwaW5nIGFsbCByZXN0b3JlLiIpCiAgICAgICAgcmV0dXJuCgogICAgSU5JVF9GTEFHID0gImluaXRpYWxpemVkLmZsYWciCiAgICBmb3JjZV9yZXN0b3JlID0gb3MuZ2V0ZW52KCJGT1JDRV9SRVNUT1JFIiwgIiIpLnN0cmlwKCkubG93ZXIoKSBpbiAoInRydWUiLCAiMSIsICJ5ZXMiKQoKICAgIHRyeToKICAgICAgICBhbGxfZmlsZXMgPSBsaXN0KGFwaS5saXN0X3JlcG9fZmlsZXMocmVwb19pZD1yZXBvX2lkLCByZXBvX3R5cGU9ImRhdGFzZXQiLCB0b2tlbj10b2tlbikpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcHJpbnQoZiJSZXN0b3JlIEVycm9yIChsaXN0X3JlcG9fZmlsZXMpOiB7ZX0iKQogICAgICAgIHJldHVybgoKICAgIGZsYWdfZXhpc3RzID0gSU5JVF9GTEFHIGluIGFsbF9maWxlcwoKICAgIGlmIG5vdCBmbGFnX2V4aXN0cyBhbmQgbm90IGZvcmNlX3Jlc3RvcmU6CiAgICAgICAgcHJpbnQoIlJlc3RvcmUgc2tpcDogaW5pdGlhbGl6ZWQuZmxhZyBub3QgZm91bmQsIGZpcnN0IGRlcGxveS4iKQogICAgICAgIGltcG9ydCBpbwogICAgICAgIGFwaS51cGxvYWRfZmlsZSgKICAgICAgICAgICAgcGF0aF9vcl9maWxlb2JqPWlvLkJ5dGVzSU8oYiJpbml0aWFsaXplZFxuIiksCiAgICAgICAgICAgIHBhdGhfaW5fcmVwbz1JTklUX0ZMQUcsCiAgICAgICAgICAgIHJlcG9faWQ9cmVwb19pZCwKICAgICAgICAgICAgcmVwb190eXBlPSJkYXRhc2V0IiwKICAgICAgICAgICAgdG9rZW49dG9rZW4sCiAgICAgICAgICAgIGNvbW1pdF9tZXNzYWdlPSJDcmVhdGUgaW5pdGlhbGl6ZWQuZmxhZyBvbiBmaXJzdCBkZXBsb3kiLAogICAgICAgICkKICAgICAgICBwcmludCgiaW5pdGlhbGl6ZWQuZmxhZyBjcmVhdGVkIGluIERhdGFzZXQuIikKICAgICAgICByZXR1cm4KCiAgICBpZiBmb3JjZV9yZXN0b3JlOgogICAgICAgIHByaW50KCJSZXN0b3JlOiBGT1JDRV9SRVNUT1JFPXRydWUsIGlnbm9yaW5nIGluaXRpYWxpemVkLmZsYWcuIikKICAgIGVsc2U6CiAgICAgICAgcHJpbnQoIlJlc3RvcmU6IGluaXRpYWxpemVkLmZsYWcgZm91bmQsIG5vcm1hbCByZXN0YXJ0LiIpCgogICAgc2tpcF9zZXQgPSBfcGFyc2Vfc2tpcF9saXN0KCJSRVNUT1JFX1NLSVAiKQoKICAgIHRyeToKICAgICAgICBub3cgPSBkYXRldGltZS5ub3coKQogICAgICAgIGZvciBpIGluIHJhbmdlKDUpOgogICAgICAgICAgICBkYXkgID0gKG5vdyAtIHRpbWVkZWx0YShkYXlzPWkpKS5zdHJmdGltZSgiJVktJW0tJWQiKQogICAgICAgICAgICBuYW1lID0gZiJiYWNrdXBfe2RheX0udGFyLmd6IgogICAgICAgICAgICBpZiBuYW1lIGluIGFsbF9maWxlczoKICAgICAgICAgICAgICAgIHByaW50KGYiRG93bmxvYWRpbmcge25hbWV9Li4uIikKICAgICAgICAgICAgICAgIHBhdGggPSBoZl9odWJfZG93bmxvYWQocmVwb19pZD1yZXBvX2lkLCBmaWxlbmFtZT1uYW1lLCByZXBvX3R5cGU9ImRhdGFzZXQiLCB0b2tlbj10b2tlbikKICAgICAgICAgICAgICAgIG9zLm1ha2VkaXJzKERBVEFfRElSLCBleGlzdF9vaz1UcnVlKQogICAgICAgICAgICAgICAgd2l0aCB0YXJmaWxlLm9wZW4ocGF0aCwgInI6Z3oiKSBhcyB0YXI6CiAgICAgICAgICAgICAgICAgICAgZm9yIG1lbWJlciBpbiB0YXIuZ2V0bWVtYmVycygpOgogICAgICAgICAgICAgICAgICAgICAgICBpZiBfaXNfc2tpcHBlZChtZW1iZXIubmFtZSwgc2tpcF9zZXQpOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbnQoZiJSZXN0b3JlIHNraXA6IHttZW1iZXIubmFtZX0iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgICAgICAgICAgICAgdGFyLmV4dHJhY3QobWVtYmVyLCBwYXRoPURBVEFfRElSKQogICAgICAgICAgICAgICAgcHJpbnQoZiJSZXN0b3JlZCBmcm9tIHtuYW1lfSIpCiAgICAgICAgICAgICAgICBicmVhawoKICAgICAgICBpbXBvcnQgaW8KICAgICAgICBhcGkudXBsb2FkX2ZpbGUoCiAgICAgICAgICAgIHBhdGhfb3JfZmlsZW9iaj1pby5CeXRlc0lPKGIiaW5pdGlhbGl6ZWQgYXQgY29udGFpbmVyIHN0YXJ0dXBcbiIpLAogICAgICAgICAgICBwYXRoX2luX3JlcG89SU5JVF9GTEFHLAogICAgICAgICAgICByZXBvX2lkPXJlcG9faWQsCiAgICAgICAgICAgIHJlcG9fdHlwZT0iZGF0YXNldCIsCiAgICAgICAgICAgIHRva2VuPXRva2VuLAogICAgICAgICAgICBjb21taXRfbWVzc2FnZT0iU2V0IGluaXRpYWxpemVkLmZsYWciLAogICAgICAgICkKICAgICAgICBwcmludCgiaW5pdGlhbGl6ZWQuZmxhZyB1cGRhdGVkLiIpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcHJpbnQoZiJSZXN0b3JlIEVycm9yOiB7ZX0iKQoKCiMg4pSA4pSAIGJhY2t1cCgpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKZGVmIGJhY2t1cCgpOgogICAgaWYgbm90IHJlcG9faWQgb3Igbm90IHRva2VuOgogICAgICAgIHByaW50KCJTa2lwIEJhY2t1cDogSEZfREFUQVNFVCBvciBIRl9UT0tFTiBub3Qgc2V0IikKICAgICAgICByZXR1cm4KCiAgICBiYWNrdXBfdGFyX3NraXBfcmF3ID0gb3MuZ2V0ZW52KCJCQUNLVVBfVEFSX1NLSVAiLCAiIikuc3RyaXAoKQogICAgaWYgYmFja3VwX3Rhcl9za2lwX3JhdyA9PSAiYWxsIjoKICAgICAgICBwcmludCgiQmFja3VwIHNraXA6IEJBQ0tVUF9UQVJfU0tJUD1hbGwiKQogICAgICAgIHJldHVybgogICAgdHJ5OgogICAgICAgIHRhcl9za2lwX3NldCA9IF9wYXJzZV9za2lwX2xpc3QoIkJBQ0tVUF9UQVJfU0tJUCIpCiAgICAgICAgZGF5ICA9IGRhdGV0aW1lLm5vdygpLnN0cmZ0aW1lKCIlWS0lbS0lZCIpCiAgICAgICAgbmFtZSA9IGYiYmFja3VwX3tkYXl9LnRhci5neiIKICAgICAgICB3aXRoIHRhcmZpbGUub3BlbihuYW1lLCAidzpneiIpIGFzIHRhcjoKICAgICAgICAgICAgZm9yIGFic19wYXRoLCByZWxfdG9fYmFzZSBpbiBfd2Fsa19sb2NhbChEQVRBX0RJUiwgc2tpcF9zZXQ9dGFyX3NraXBfc2V0KToKICAgICAgICAgICAgICAgIHRhci5hZGQoYWJzX3BhdGgsIGFyY25hbWU9cmVsX3RvX2Jhc2UpCiAgICAgICAgYXBpLnVwbG9hZF9maWxlKHBhdGhfb3JfZmlsZW9iaj1uYW1lLCBwYXRoX2luX3JlcG89bmFtZSwgcmVwb19pZD1yZXBvX2lkLCByZXBvX3R5cGU9ImRhdGFzZXQiLCB0b2tlbj10b2tlbikKICAgICAgICBwcmludChmIkJhY2t1cCB7bmFtZX0gZG9uZS4iKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHByaW50KGYiQmFja3VwIEVycm9yOiB7ZX0iKQoKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBpZiBsZW4oc3lzLmFyZ3YpID4gMSBhbmQgc3lzLmFyZ3ZbMV0gPT0gImJhY2t1cCI6CiAgICAgICAgYmFja3VwKCkKICAgIGVsc2U6CiAgICAgICAgcmVzdG9yZSgpCg=='); open('/usr/local/bin/sync.py','wb').write(d)" RUN python3 -c "import base64,os,stat; d=base64.b64decode('IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoiIiIKY3MtbWFuYWdlcjogY29kZS1zZXJ2ZXIg5oyJ6ZyA5ZCv5YGc5a6I5oqk6L+b56iLCgrnm5HlkKwgVW5peCBzb2NrZXTvvIzmj5DkvpvkuKTkuKogSFRUUCDnq6/ngrnvvJoKICBHRVQgL3dha2V1cCAgICAtIOinpuWPkeWQr+WKqCBjb2RlLXNlcnZlcu+8iOiLpeacqui/kOihjO+8ie+8jOi/lOWbniLlkK/liqjkuK0i562J5b6F6aG1CiAgR0VUIC9oZWFydGJlYXQgLSDmm7TmlrDmnIDlkI7mtLvot4Pml7bpl7TvvIhuZ2lueCDmr4/mrKHmiJDlip/ku6PnkIYgL2lkZS8g5ZCO6LCD55So77yJCgrlkI7lj7Dlrprml7bku7vliqHvvJoKICDmr48gNjAg56eS5qOA5p+l5LiA5qyh77yM6Iul6LaF6L+HIElERV9JRExFX01JTlVURVMg5YiG6ZKf5pegIGhlYXJ0YmVhdO+8jGtpbGwgY29kZS1zZXJ2ZXIKIiIiCgppbXBvcnQgb3MsIHN5cywgdGltZSwgc2lnbmFsLCBzdWJwcm9jZXNzLCB0aHJlYWRpbmcsIHNvY2tldCwgcmUKZnJvbSBodHRwLnNlcnZlciBpbXBvcnQgSFRUUFNlcnZlciwgQmFzZUhUVFBSZXF1ZXN0SGFuZGxlcgoKIyDilIDilIAg6YWN572uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApTT0NLX1BBVEggICAgICAgID0gIi90bXAvY3MtbWFuYWdlci5zb2NrIgpDU19QT1JUICAgICAgICAgID0gaW50KG9zLmVudmlyb24uZ2V0KCJDT0RFX1NFUlZFUl9QT1JUIiwgIjEzMzM3IikpCkNTX1BBU1NXT1JEICAgICAgPSBvcy5lbnZpcm9uLmdldCgiQ09ERV9TRVJWRVJfUEFTU1dPUkQiLCAiY2hhbmdlbWUxMjMhIikKSURMRV9NSU5VVEVTICAgICA9IGludChvcy5lbnZpcm9uLmdldCgiSURFX0lETEVfTUlOVVRFUyIsICIzMCIpKQpDSEVDS19JTlRFUlZBTCAgID0gNjAgICAjIOenkgoKIyDilIDilIAg5YWo5bGA54q25oCBIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjc19wcm9jICAgICAgICAgID0gTm9uZQpsYXN0X2hlYXJ0YmVhdCAgID0gMC4wCmxvY2sgICAgICAgICAgICAgPSB0aHJlYWRpbmcuTG9jaygpCgojIOKUgOKUgCBjb2RlLXNlcnZlciDlkK/lgZwg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgpkZWYgc3RhcnRfY3MoKToKICAgIGdsb2JhbCBjc19wcm9jLCBsYXN0X2hlYXJ0YmVhdAogICAgd2l0aCBsb2NrOgogICAgICAgIGlmIGNzX3Byb2MgaXMgbm90IE5vbmUgYW5kIGNzX3Byb2MucG9sbCgpIGlzIE5vbmU6CiAgICAgICAgICAgIHJldHVybiAgIyBhbHJlYWR5IHJ1bm5pbmcKICAgICAgICBwcmludChmIltjcy1tYW5hZ2VyXSBTdGFydGluZyBjb2RlLXNlcnZlciBvbiBwb3J0IHtDU19QT1JUfSIsIGZsdXNoPVRydWUpCiAgICAgICAgZW52ID0gb3MuZW52aXJvbi5jb3B5KCkKICAgICAgICBlbnZbIlBBU1NXT1JEIl0gPSBDU19QQVNTV09SRAogICAgICAgIGNzX3Byb2MgPSBzdWJwcm9jZXNzLlBvcGVuKAogICAgICAgICAgICBbImNvZGUtc2VydmVyIiwKICAgICAgICAgICAgICItLWJpbmQtYWRkciIsIGYiMTI3LjAuMC4xOntDU19QT1JUfSIsCiAgICAgICAgICAgICAiLS1hdXRoIiwgInBhc3N3b3JkIiwKICAgICAgICAgICAgICItLWRpc2FibGUtdGVsZW1ldHJ5IiwKICAgICAgICAgICAgICIvYXBwL2NoYXQyYXBpIl0sCiAgICAgICAgICAgIGVudj1lbnYsCiAgICAgICAgICAgIHN0ZG91dD1zdWJwcm9jZXNzLkRFVk5VTEwsCiAgICAgICAgICAgIHN0ZGVycj1zdWJwcm9jZXNzLkRFVk5VTEwsCiAgICAgICAgKQogICAgICAgIGxhc3RfaGVhcnRiZWF0ID0gdGltZS50aW1lKCkKICAgICAgICBwcmludChmIltjcy1tYW5hZ2VyXSBjb2RlLXNlcnZlciBwaWQ9e2NzX3Byb2MucGlkfSIsIGZsdXNoPVRydWUpCgpkZWYgc3RvcF9jcygpOgogICAgZ2xvYmFsIGNzX3Byb2MKICAgIHdpdGggbG9jazoKICAgICAgICBpZiBjc19wcm9jIGlzIE5vbmUgb3IgY3NfcHJvYy5wb2xsKCkgaXMgbm90IE5vbmU6CiAgICAgICAgICAgIHJldHVybgogICAgICAgIHByaW50KCJbY3MtbWFuYWdlcl0gU3RvcHBpbmcgaWRsZSBjb2RlLXNlcnZlciIsIGZsdXNoPVRydWUpCiAgICAgICAgY3NfcHJvYy50ZXJtaW5hdGUoKQogICAgICAgIHRyeToKICAgICAgICAgICAgY3NfcHJvYy53YWl0KHRpbWVvdXQ9MTApCiAgICAgICAgZXhjZXB0IHN1YnByb2Nlc3MuVGltZW91dEV4cGlyZWQ6CiAgICAgICAgICAgIGNzX3Byb2Mua2lsbCgpCiAgICAgICAgY3NfcHJvYyA9IE5vbmUKCmRlZiBpc19jc19ydW5uaW5nKCk6CiAgICB3aXRoIGxvY2s6CiAgICAgICAgcmV0dXJuIGNzX3Byb2MgaXMgbm90IE5vbmUgYW5kIGNzX3Byb2MucG9sbCgpIGlzIE5vbmUKCiMg4pSA4pSAIEhUVFAgaGFuZGxlciDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCldBS0VVUF9IVE1MID0gIiIiXAo8IURPQ1RZUEUgaHRtbD48aHRtbD48aGVhZD4KPG1ldGEgY2hhcnNldD0idXRmLTgiPgo8bWV0YSBodHRwLWVxdWl2PSJyZWZyZXNoIiBjb250ZW50PSIzIj4KPHRpdGxlPklERSDlkK/liqjkuK3igKY8L3RpdGxlPgo8c3R5bGU+CmJvZHl7Zm9udC1mYW1pbHk6c2Fucy1zZXJpZjtkaXNwbGF5OmZsZXg7YWxpZ24taXRlbXM6Y2VudGVyO2p1c3RpZnktY29udGVudDpjZW50ZXI7CiAgICAgaGVpZ2h0OjEwMHZoO21hcmdpbjowO2JhY2tncm91bmQ6IzFlMWUyZTtjb2xvcjojY2RkNmY0fQouYm94e3RleHQtYWxpZ246Y2VudGVyfS5zcGlubmVye3dpZHRoOjQ4cHg7aGVpZ2h0OjQ4cHg7Ym9yZGVyOjVweCBzb2xpZCAjMzEzMjQ0OwogICAgIGJvcmRlci10b3AtY29sb3I6Izg5YjRmYTtib3JkZXItcmFkaXVzOjUwJTthbmltYXRpb246c3BpbiAxcyBsaW5lYXIgaW5maW5pdGU7CiAgICAgbWFyZ2luOjAgYXV0byAyMHB4fQpAa2V5ZnJhbWVzIHNwaW57dG97dHJhbnNmb3JtOnJvdGF0ZSgzNjBkZWcpfX0KPC9zdHlsZT48L2hlYWQ+PGJvZHk+CjxkaXYgY2xhc3M9ImJveCI+CiAgPGRpdiBjbGFzcz0ic3Bpbm5lciI+PC9kaXY+CiAgPGgyPlZTIENvZGUgSURFIOWQr+WKqOS4reKApjwvaDI+CiAgPHA+6aG16Z2i5bCG5ZyoIDMg56eS5ZCO6Ieq5Yqo5Yi35pawPC9wPgo8L2Rpdj48L2JvZHk+PC9odG1sPgoiIiIKCmNsYXNzIEhhbmRsZXIoQmFzZUhUVFBSZXF1ZXN0SGFuZGxlcik6CiAgICBkZWYgbG9nX21lc3NhZ2Uoc2VsZiwgZm10LCAqYXJncyk6CiAgICAgICAgcGFzcyAgIyDpnZnpu5jorr/pl67ml6Xlv5cKCiAgICBkZWYgZG9fR0VUKHNlbGYpOgogICAgICAgIGdsb2JhbCBsYXN0X2hlYXJ0YmVhdAogICAgICAgIHBhdGggPSBzZWxmLnBhdGguc3BsaXQoIj8iKVswXQoKICAgICAgICBpZiBwYXRoID09ICIvaGVhcnRiZWF0IjoKICAgICAgICAgICAgbGFzdF9oZWFydGJlYXQgPSB0aW1lLnRpbWUoKQogICAgICAgICAgICBzZWxmLl9yZXNwb25kKDIwMCwgInRleHQvcGxhaW4iLCBiIm9rIikKCiAgICAgICAgZWxpZiBwYXRoID09ICIvd2FrZXVwIjoKICAgICAgICAgICAgaWYgbm90IGlzX2NzX3J1bm5pbmcoKToKICAgICAgICAgICAgICAgIHN0YXJ0X2NzKCkKICAgICAgICAgICAgICAgICMg562J5b6FIGNvZGUtc2VydmVyIOWwsee7qu+8iOacgOWkmiAzMCDnp5LvvIkKICAgICAgICAgICAgICAgIGZvciBfIGluIHJhbmdlKDMwKToKICAgICAgICAgICAgICAgICAgICB0aW1lLnNsZWVwKDEpCiAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICBpbXBvcnQgdXJsbGliLnJlcXVlc3QKICAgICAgICAgICAgICAgICAgICAgICAgdXJsbGliLnJlcXVlc3QudXJsb3BlbihmImh0dHA6Ly8xMjcuMC4wLjE6e0NTX1BPUlR9LyIsIHRpbWVvdXQ9MSkKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICAgICAgICAgICAgICAgICBwYXNzCiAgICAgICAgICAgIHNlbGYuX3Jlc3BvbmQoMjAwLCAidGV4dC9odG1sIiwgV0FLRVVQX0hUTUwuZW5jb2RlKCkpCgogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHNlbGYuX3Jlc3BvbmQoNDA0LCAidGV4dC9wbGFpbiIsIGIibm90IGZvdW5kIikKCiAgICBkZWYgX3Jlc3BvbmQoc2VsZiwgY29kZSwgY3R5cGUsIGJvZHkpOgogICAgICAgIHNlbGYuc2VuZF9yZXNwb25zZShjb2RlKQogICAgICAgIHNlbGYuc2VuZF9oZWFkZXIoIkNvbnRlbnQtVHlwZSIsIGN0eXBlKQogICAgICAgIHNlbGYuc2VuZF9oZWFkZXIoIkNvbnRlbnQtTGVuZ3RoIiwgc3RyKGxlbihib2R5KSkpCiAgICAgICAgc2VsZi5lbmRfaGVhZGVycygpCiAgICAgICAgc2VsZi53ZmlsZS53cml0ZShib2R5KQoKIyDilIDilIAgVW5peCBzb2NrZXQgc2VydmVyIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKY2xhc3MgVW5peEhUVFBTZXJ2ZXIoSFRUUFNlcnZlcik6CiAgICBhZGRyZXNzX2ZhbWlseSA9IHNvY2tldC5BRl9VTklYCgogICAgZGVmIHNlcnZlcl9iaW5kKHNlbGYpOgogICAgICAgIGlmIG9zLnBhdGguZXhpc3RzKFNPQ0tfUEFUSCk6CiAgICAgICAgICAgIG9zLnJlbW92ZShTT0NLX1BBVEgpCiAgICAgICAgc3VwZXIoKS5zZXJ2ZXJfYmluZCgpCiAgICAgICAgb3MuY2htb2QoU09DS19QQVRILCAwbzY2NikKCiMg4pSA4pSAIOepuumXsuajgOafpeW+queOryDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCmRlZiBpZGxlX3dhdGNoZXIoKToKICAgIHdoaWxlIFRydWU6CiAgICAgICAgdGltZS5zbGVlcChDSEVDS19JTlRFUlZBTCkKICAgICAgICBpZiBpc19jc19ydW5uaW5nKCk6CiAgICAgICAgICAgIGlkbGVfc2VjcyA9IHRpbWUudGltZSgpIC0gbGFzdF9oZWFydGJlYXQKICAgICAgICAgICAgaWYgaWRsZV9zZWNzID4gSURMRV9NSU5VVEVTICogNjA6CiAgICAgICAgICAgICAgICBwcmludChmIltjcy1tYW5hZ2VyXSBJZGxlIHtpZGxlX3NlY3MvNjA6LjFmfSBtaW4gPiB7SURMRV9NSU5VVEVTfSBtaW4sIHN0b3BwaW5nLiIsIGZsdXNoPVRydWUpCiAgICAgICAgICAgICAgICBzdG9wX2NzKCkKCiMg4pSA4pSAIOS4u+WFpeWPoyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICB3YXRjaGVyID0gdGhyZWFkaW5nLlRocmVhZCh0YXJnZXQ9aWRsZV93YXRjaGVyLCBkYWVtb249VHJ1ZSkKICAgIHdhdGNoZXIuc3RhcnQoKQoKICAgIHNlcnZlciA9IFVuaXhIVFRQU2VydmVyKFNPQ0tfUEFUSCwgSGFuZGxlcikKICAgIHByaW50KGYiW2NzLW1hbmFnZXJdIExpc3RlbmluZyBvbiB7U09DS19QQVRIfSIsIGZsdXNoPVRydWUpCiAgICB0cnk6CiAgICAgICAgc2VydmVyLnNlcnZlX2ZvcmV2ZXIoKQogICAgZXhjZXB0IEtleWJvYXJkSW50ZXJydXB0OgogICAgICAgIHBhc3MK'); p='/usr/local/bin/cs-manager'; open(p,'wb').write(d); os.chmod(p, os.stat(p).st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)" RUN python3 -c "import base64,os,stat; d=base64.b64decode('IyEvYmluL2Jhc2gKc2V0IC1lCgojIOKUgOKUgCDnjq/looPlj5jph48g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACkxJU1RFTl9QT1JUPSIke1BPUlQ6LTc4NjB9IgpDSEFUMkFQSV9QT1JUPTc4NjIKQ09ERV9TRVJWRVJfUE9SVD0iJHtDT0RFX1NFUlZFUl9QT1JUOi0xMzMzN30iCklERV9JRExFX01JTlVURVM9IiR7SURFX0lETEVfTUlOVVRFUzotMzB9IgoKZXhwb3J0IENPREVfU0VSVkVSX1BPUlQgSURFX0lETEVfTUlOVVRFUwpleHBvcnQgQ09ERV9TRVJWRVJfUEFTU1dPUkQ9IiR7Q09ERV9TRVJWRVJfUEFTU1dPUkQ6LWNoYW5nZW1lMTIzIX0iCmV4cG9ydCBDSEFUMkFQSV9EQVRBX0RJUj0iL3Jvb3QvLmNoYXQyYXBpIgoKZWNobyAiPT09IENoYXQyQVBJIEh1Z2dpbmdGYWNlIFNwYWNlIOWQr+WKqCA9PT0iCmVjaG8gIuWklumDqOebkeWQrOerr+WPoyAgICA6ICR7TElTVEVOX1BPUlR9IgplY2hvICJDaGF0MkFQSeWGhemDqOerr+WPozogJHtDSEFUMkFQSV9QT1JUfSIKZWNobyAiSURF5YaF6YOo56uv5Y+jICAgICA6ICR7Q09ERV9TRVJWRVJfUE9SVH0iCmVjaG8gIklERemXsue9ruiHquWKqOWFs+mXrSA6ICR7SURFX0lETEVfTUlOVVRFU30gbWluIgoKIyDilIDilIAg5q2l6aqkIDHvvJrku44gSEYgRGF0YXNldCDmgaLlpI3mjIHkuYXljJbmlbDmja4g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmVjaG8gIi0tLSDmgaLlpI3mjIHkuYXljJbphY3nva7mlbDmja4gLS0tIgpweXRob24zIC91c3IvbG9jYWwvYmluL3N5bmMucHkgcmVzdG9yZSB8fCB0cnVlCgojIOKUgOKUgCDmraXpqqQgMu+8muWHhuWkhyBDaGF0MkFQSSDmlbDmja7nm67lvZUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACm1rZGlyIC1wICIke0NIQVQyQVBJX0RBVEFfRElSfS9sb2dzIiAiJHtDSEFUMkFQSV9EQVRBX0RJUn0vc2Vzc2lvbnMiCgojIOiLpeaVsOaNruebruW9leS4jeWtmOWcqOmFjee9ru+8jOS7jiBIRiBTcGFjZSDlj5jph4/nlJ/miJDliJ3lp4vphY3nva4KQ09ORklHX0ZJTEU9IiR7Q0hBVDJBUElfREFUQV9ESVJ9L2NvbmZpZy5qc29uIgppZiBbICEgLWYgIiRDT05GSUdfRklMRSIgXTsgdGhlbgogICAgZWNobyAi55Sf5oiQ5Yid5aeLIGNvbmZpZy5qc29uLi4uIgogICAgcHl0aG9uMyAvdXNyL2xvY2FsL2Jpbi9pbml0LWNvbmZpZy5weQpmaQoKIyDoi6XmlbDmja7nm67lvZXkuI3lrZjlnKjotKbmiLfliJfooajvvIzku44gSEYgU3BhY2Ug5Y+Y6YeP5rOo5YWl5Yid5aeL6LSm5oi3CkFDQ09VTlRTX0ZJTEU9IiR7Q0hBVDJBUElfREFUQV9ESVJ9L2FjY291bnRzLmpzb24iCmlmIFsgISAtZiAiJEFDQ09VTlRTX0ZJTEUiIF07IHRoZW4KICAgIGVjaG8gIueUn+aIkOWIneWniyBhY2NvdW50cy5qc29uLi4uIgogICAgcHl0aG9uMyAvdXNyL2xvY2FsL2Jpbi9pbml0LWFjY291bnRzLnB5CmZpCgojIOKUgOKUgCDmraXpqqQgM++8muWQr+WKqOiZmuaLn+aYvuekuu+8iEVsZWN0cm9uIOmcgOimgSBYIGRpc3BsYXnvvInilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKZWNobyAiLS0tIOWQr+WKqCBYdmZiIOiZmuaLn+aYvuekuiAtLS0iClh2ZmIgOjk5IC1zY3JlZW4gMCAxMjgweDgwMHgyNCAtYWMgK2V4dGVuc2lvbiBHTFggK3JlbmRlciAtbm9yZXNldCAmClhWRkJfUElEPSQhCmV4cG9ydCBESVNQTEFZPTo5OQplY2hvICLnrYnlvoUgWHZmYiDlsLHnu6ouLi4iCnNsZWVwIDIKCiMg4pSA4pSAIOatpemqpCA077ya5ZCv5YqoIENoYXQyQVBJ77yIRWxlY3Ryb24gQXBwSW1hZ2XvvIzlkI7lj7Dov5DooYzvvInilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKZWNobyAiLS0tIOWQr+WKqCBDaGF0MkFQSSDmnI3liqEgKOWGhemDqOerr+WPoyAke0NIQVQyQVBJX1BPUlR9KSAtLS0iCmNkIC9hcHAvY2hhdDJhcGkKCiMg5p+l5om+5p6E5bu65Lqn54mp77ya5LyY5YWI55SoIEFwcEltYWdl77yM5YW25qyh55SoIHVucGFja2VkIOebruW9leWGheeahCBlbGVjdHJvbiDlj6/miafooYzmlofku7YKQVBQSU1BR0U9JChmaW5kIGRpc3QgLW5hbWUgIiouQXBwSW1hZ2UiIC1tYXhkZXB0aCAxIDI+L2Rldi9udWxsIHwgaGVhZCAtMSkKCmlmIFsgLW4gIiRBUFBJTUFHRSIgXTsgdGhlbgogICAgZWNobyAi5L2/55SoIEFwcEltYWdlOiAkQVBQSU1BR0UiCiAgICBDSEFUMkFQSV9QT1JUPSIke0NIQVQyQVBJX1BPUlR9IiBcCiAgICBDSEFUMkFQSV9EQVRBPSIke0NIQVQyQVBJX0RBVEFfRElSfSIgXAogICAgIiRBUFBJTUFHRSIgLS1uby1zYW5kYm94IC0tZGlzYWJsZS1ncHUgMj4mMSBcCiAgICAgICAgfCB0ZWUgIiR7Q0hBVDJBUElfREFUQV9ESVJ9L2xvZ3MvY2hhdDJhcGkubG9nIiAmCmVsc2UKICAgICMg5Zue6YCA77ya5L2/55SoIGVsZWN0cm9uLXZpdGUgZGV2IOaooeW8j++8iOS7jemcgCBESVNQTEFZ77yJCiAgICBlY2hvICJBcHBJbWFnZSDmnKrmib7liLDvvIzlm57pgIDliLAgZWxlY3Ryb24tdml0ZSBkZXYg5qih5byPIgogICAgQ0hBVDJBUElfUE9SVD0iJHtDSEFUMkFQSV9QT1JUfSIgXAogICAgQ0hBVDJBUElfREFUQT0iJHtDSEFUMkFQSV9EQVRBX0RJUn0iIFwKICAgIG5weCBlbGVjdHJvbiAuIC0tbm8tc2FuZGJveCAtLWRpc2FibGUtZ3B1IDI+JjEgXAogICAgICAgIHwgdGVlICIke0NIQVQyQVBJX0RBVEFfRElSfS9sb2dzL2NoYXQyYXBpLmxvZyIgJgpmaQplY2hvICJDaGF0MkFQSSDlt7LlnKjlkI7lj7DlkK/liqgiCgojIOKUgOKUgCDmraXpqqQgNe+8muWQr+WKqCBjcy1tYW5hZ2VyIOWuiOaKpOi/m+eoi++8iOWQjuWPsO+8ieKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAplY2hvICItLS0g5ZCv5YqoIElERSDmjInpnIDnrqHnkIblmaggLS0tIgpweXRob24zIC91c3IvbG9jYWwvYmluL2NzLW1hbmFnZXIgMj4mMSB8IHRlZSAiJHtDSEFUMkFQSV9EQVRBX0RJUn0vbG9ncy9jcy1tYW5hZ2VyLmxvZyIgJgoKIyDnrYnlvoUgY3MtbWFuYWdlciBzb2NrZXQg5bCx57uq77yI5pyA5aSaIDEwIOenku+8iQpmb3IgaSBpbiAkKHNlcSAxIDIwKTsgZG8KICBpZiBbIC1TIC90bXAvY3MtbWFuYWdlci5zb2NrIF07IHRoZW4KICAgIGVjaG8gImNzLW1hbmFnZXIgc29ja2V0IOWwsee7qiIKICAgIGJyZWFrCiAgZmkKICBzbGVlcCAwLjUKZG9uZQoKIyDilIDilIAg5q2l6aqkIDbvvJrnrYnlvoUgQ2hhdDJBUEkg5pyN5Yqh5bCx57uq77yI5pyA5aSaIDEyMCDnp5LvvInilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKZWNobyAi562J5b6FIENoYXQyQVBJIOacjeWKoeWQr+WKqC4uLiIKZm9yIGkgaW4gJChzZXEgMSA2MCk7IGRvCiAgaWYgY3VybCAtZnNTICJodHRwOi8vMTI3LjAuMC4xOiR7Q0hBVDJBUElfUE9SVH0vIiA+L2Rldi9udWxsIDI+JjE7IHRoZW4KICAgIGVjaG8gIkNoYXQyQVBJIOacjeWKoeW3suWwsee7qu+8iCR7aX0qMnPvvIkiCiAgICBicmVhawogIGZpCiAgc2xlZXAgMgpkb25lCgojIOKUgOKUgCDmraXpqqQgN++8muWQr+WKqOWumuaXtuWkh+S7veW+queOr++8iOavjyA2MCDliIbpkp/vvInilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKKHdoaWxlIHRydWU7IGRvIHNsZWVwIDM2MDA7IHB5dGhvbjMgL3Vzci9sb2NhbC9iaW4vc3luYy5weSBiYWNrdXAgfHwgdHJ1ZTsgZG9uZSkgJgplY2hvICLmjIHkuYXljJblpIfku73lvqrnjq/lt7LlkK/liqjvvIjmr4/lsI/ml7blpIfku73kuIDmrKHvvIkiCgojIOKUgOKUgCDmraXpqqQgOO+8mueUn+aIkCBuZ2lueCDphY3nva7lubblkK/liqgg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnJtIC1mIC9ldGMvbmdpbngvc2l0ZXMtZW5hYmxlZC9kZWZhdWx0IC9ldGMvbmdpbngvY29uZi5kL2RlZmF1bHQuY29uZgoKTkdJTlhfQ09ORj0iL2V0Yy9uZ2lueC9jb25mLmQvY2hhdDJhcGkuY29uZiIKCnB5dGhvbjMgLSA8PFBZRU9GCmltcG9ydCBvcwpsaXN0ZW4gICA9IG9zLmVudmlyb24uZ2V0KCJQT1JUIiwgIjc4NjAiKQpjc19wb3J0ICA9IG9zLmVudmlyb24uZ2V0KCJDT0RFX1NFUlZFUl9QT1JUIiwgIjEzMzM3IikKYXBpX3BvcnQgPSAiNzg2MiIKCmNvbmYgPSBmIiIiCnVwc3RyZWFtIGNzX21hbmFnZXIge3sKICAgIHNlcnZlciB1bml4Oi90bXAvY3MtbWFuYWdlci5zb2NrOwp9fQoKc2VydmVyIHt7CiAgICBsaXN0ZW4ge2xpc3Rlbn07CiAgICBzZXJ2ZXJfbmFtZSBfOwogICAgY2xpZW50X21heF9ib2R5X3NpemUgMTAwTTsKCiAgICBhY2Nlc3NfbG9nIC9kZXYvc3Rkb3V0OwogICAgZXJyb3JfbG9nICAvZGV2L3N0ZGVyciB3YXJuOwoKICAgIGFic29sdXRlX3JlZGlyZWN0IG9mZjsKICAgIHBvcnRfaW5fcmVkaXJlY3Qgb2ZmOwoKICAgIGxvY2F0aW9uIC9pZGUvIHt7CiAgICAgICAgcHJveHlfcGFzcyBodHRwOi8vMTI3LjAuMC4xOntjc19wb3J0fS87CiAgICAgICAgcHJveHlfaHR0cF92ZXJzaW9uIDEuMTsKICAgICAgICBwcm94eV9zZXRfaGVhZGVyIEhvc3QgXCRob3N0OwogICAgICAgIHByb3h5X3NldF9oZWFkZXIgVXBncmFkZSBcJGh0dHBfdXBncmFkZTsKICAgICAgICBwcm94eV9zZXRfaGVhZGVyIENvbm5lY3Rpb24gInVwZ3JhZGUiOwogICAgICAgIHByb3h5X3NldF9oZWFkZXIgWC1Gb3J3YXJkZWQtRm9yIFwkcHJveHlfYWRkX3hfZm9yd2FyZGVkX2ZvcjsKICAgICAgICBwcm94eV9zZXRfaGVhZGVyIFgtRm9yd2FyZGVkLVByb3RvIFwkc2NoZW1lOwogICAgICAgIHByb3h5X3JlZGlyZWN0IC8gL2lkZS87CiAgICAgICAgcHJveHlfcmVhZF90aW1lb3V0IDg2NDAwOwogICAgICAgIHByb3h5X2Nvbm5lY3RfdGltZW91dCAyczsKICAgICAgICBlcnJvcl9wYWdlIDUwMiA1MDQgQGlkZV93YWtldXA7CiAgICAgICAgcG9zdF9hY3Rpb24gL2lkZS1oZWFydGJlYXQvOwogICAgfX0KCiAgICBsb2NhdGlvbiAvaWRlLWhlYXJ0YmVhdC8ge3sKICAgICAgICBpbnRlcm5hbDsKICAgICAgICByZXdyaXRlIF4gL2hlYXJ0YmVhdCBicmVhazsKICAgICAgICBwcm94eV9wYXNzIGh0dHA6Ly9jc19tYW5hZ2VyOwogICAgICAgIHByb3h5X2Nvbm5lY3RfdGltZW91dCAxczsKICAgICAgICBwcm94eV9yZWFkX3RpbWVvdXQgMnM7CiAgICB9fQoKICAgIGxvY2F0aW9uIEBpZGVfd2FrZXVwIHt7CiAgICAgICAgcmV3cml0ZSBeIC93YWtldXAgYnJlYWs7CiAgICAgICAgcHJveHlfcGFzcyBodHRwOi8vY3NfbWFuYWdlcjsKICAgICAgICBwcm94eV9odHRwX3ZlcnNpb24gMS4xOwogICAgICAgIHByb3h5X3NldF9oZWFkZXIgSG9zdCBcJGhvc3Q7CiAgICAgICAgcHJveHlfY29ubmVjdF90aW1lb3V0IDVzOwogICAgICAgIHByb3h5X3JlYWRfdGltZW91dCAzMHM7CiAgICB9fQoKICAgIGxvY2F0aW9uIC8ge3sKICAgICAgICBwcm94eV9wYXNzIGh0dHA6Ly8xMjcuMC4wLjE6e2FwaV9wb3J0fS87CiAgICAgICAgcHJveHlfaHR0cF92ZXJzaW9uIDEuMTsKICAgICAgICBwcm94eV9zZXRfaGVhZGVyIEhvc3QgXCRob3N0OwogICAgICAgIHByb3h5X3NldF9oZWFkZXIgVXBncmFkZSBcJGh0dHBfdXBncmFkZTsKICAgICAgICBwcm94eV9zZXRfaGVhZGVyIENvbm5lY3Rpb24gInVwZ3JhZGUiOwogICAgICAgIHByb3h5X3NldF9oZWFkZXIgWC1Gb3J3YXJkZWQtRm9yIFwkcHJveHlfYWRkX3hfZm9yd2FyZGVkX2ZvcjsKICAgICAgICBwcm94eV9zZXRfaGVhZGVyIFgtRm9yd2FyZGVkLVByb3RvIFwkc2NoZW1lOwogICAgICAgIHByb3h5X3JlYWRfdGltZW91dCA4NjQwMDsKICAgIH19Cn19CiIiIgoKd2l0aCBvcGVuKCIvZXRjL25naW54L2NvbmYuZC9jaGF0MmFwaS5jb25mIiwgInciKSBhcyBmOgogICAgZi53cml0ZShjb25mKQpwcmludCgibmdpbnggY29uZmlnIHdyaXR0ZW4uIikKUFlFT0YKCmVjaG8gIm5naW54IOmFjee9ruW3sueUn+aIkCIKbmdpbnggLXQKZWNobyAi5ZCv5YqoIG5naW5477yI5YmN5Y+w6L+Q6KGM77yM5L+d5oyB5a655Zmo5a2Y5rS777yJLi4uIgpleGVjIG5naW54IC1nICdkYWVtb24gb2ZmOyBlcnJvcl9sb2cgL2Rldi9zdGRlcnIgd2FybjsnCg=='); p='/usr/local/bin/start-chat2api'; open(p,'wb').write(d); os.chmod(p, os.stat(p).st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)" RUN python3 -c "import base64,os; d=base64.b64decode('aW1wb3J0IG9zLCBqc29uCgpjb25maWcgPSB7CiAgICAicG9ydCI6IGludChvcy5nZXRlbnYoIkNIQVQyQVBJX1BPUlQiLCAiNzg2MiIpKSwKICAgICJhcGlLZXkiOiBvcy5nZXRlbnYoIkNIQVQyQVBJX0FQSV9LRVkiLCAiIiksCiAgICAibG9hZEJhbGFuY2UiOiB7CiAgICAgICAgInN0cmF0ZWd5Ijogb3MuZ2V0ZW52KCJMQl9TVFJBVEVHWSIsICJyb3VuZC1yb2JpbiIpCiAgICB9Cn0KZGF0YV9kaXIgPSBvcy5nZXRlbnYoIkNIQVQyQVBJX0RBVEFfRElSIiwgIi9yb290Ly5jaGF0MmFwaSIpCm9zLm1ha2VkaXJzKGRhdGFfZGlyLCBleGlzdF9vaz1UcnVlKQp3aXRoIG9wZW4ob3MucGF0aC5qb2luKGRhdGFfZGlyLCAiY29uZmlnLmpzb24iKSwgInciKSBhcyBmOgogICAganNvbi5kdW1wKGNvbmZpZywgZiwgaW5kZW50PTIpCnByaW50KCJjb25maWcuanNvbiBpbml0aWFsaXplZC4iKQo='); open('/usr/local/bin/init-config.py','wb').write(d)" RUN python3 -c "import base64,os; d=base64.b64decode('aW1wb3J0IG9zLCBqc29uCgphY2NvdW50cyA9IFtdCmZvciBpIGluIHJhbmdlKDEsIDExKToKICAgIHB0eXBlID0gb3MuZ2V0ZW52KGYiUFJPVklERVJfe2l9X1RZUEUiLCAiIikuc3RyaXAoKQogICAgdG9rZW4gPSBvcy5nZXRlbnYoZiJQUk9WSURFUl97aX1fVE9LRU4iLCAiIikuc3RyaXAoKQogICAgaWYgcHR5cGUgYW5kIHRva2VuOgogICAgICAgIGFjY291bnRzLmFwcGVuZCh7InByb3ZpZGVyIjogcHR5cGUsICJ0b2tlbiI6IHRva2VuLCAiZW5hYmxlZCI6IFRydWV9KQoKZGF0YV9kaXIgPSBvcy5nZXRlbnYoIkNIQVQyQVBJX0RBVEFfRElSIiwgIi9yb290Ly5jaGF0MmFwaSIpCm9zLm1ha2VkaXJzKGRhdGFfZGlyLCBleGlzdF9vaz1UcnVlKQp3aXRoIG9wZW4ob3MucGF0aC5qb2luKGRhdGFfZGlyLCAiYWNjb3VudHMuanNvbiIpLCAidyIpIGFzIGY6CiAgICBqc29uLmR1bXAoYWNjb3VudHMsIGYsIGluZGVudD0yKQpwcmludChmImFjY291bnRzLmpzb24gaW5pdGlhbGl6ZWQgd2l0aCB7bGVuKGFjY291bnRzKX0gYWNjb3VudChzKS4iKQo='); open('/usr/local/bin/init-accounts.py','wb').write(d)" # ── 8. 验证脚本写入完整(构建时校验,有问题立即报错)──────────────────────────── RUN for f in /usr/local/bin/sync.py /usr/local/bin/cs-manager /usr/local/bin/start-chat2api /usr/local/bin/init-config.py /usr/local/bin/init-accounts.py; do [ -s "$f" ] || (echo "MISSING or EMPTY: $f" && exit 1); echo "OK $(wc -c < $f)B $f"; done && [ -x /usr/local/bin/cs-manager ] && [ -x /usr/local/bin/start-chat2api ] && echo "All scripts verified." # ── 9. 暴露端口 ────────────────────────────────────────────────────────────── EXPOSE 7860 # ── 访问说明 ───────────────────────────────────────────────────────────────── # Space 启动后访问方式: # 主界面 (Chat2API 管理面板) : https://.hf.space/ # API 接入端点 : https://.hf.space/v1/ # VS Code IDE : https://.hf.space/ide/ # # 必填 HF Space Secrets: # HF_TOKEN —— HuggingFace 访问令牌(write 权限) # HF_DATASET —— Dataset repo ID,格式:username/dataset-name # # 可选 Secrets: # CHAT2API_API_KEY —— Chat2API 访问密钥(留空则无需认证) # CODE_SERVER_PASSWORD —— VS Code IDE 登录密码(默认:changeme123!) # IDE_IDLE_MINUTES —— IDE 闲置自动关闭分钟数(默认:30) # PROVIDER_1_TYPE / PROVIDER_1_TOKEN —— 预注入服务商(最多 10 个) # LB_STRATEGY —— 负载均衡策略:round-robin / fill-first / failover # FORCE_RESTORE —— 设为 true 强制从 Dataset 覆盖恢复 CMD ["/usr/local/bin/start-chat2api"]