[
  {
    "path": ".editorconfig",
    "content": "root = true\n[*]\nend_of_line = lf\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\nthanks_dev: # Replace with a single thanks.dev username\ncustom: ['https://afdian.com/a/nelvko']# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug 反馈\ndescription: \"提交项目 Bug 报告\"\ntitle: \"[Bug] \"\nlabels: [\"bug\"]\nbody:\n  - type: checkboxes\n    id: ensure\n    attributes:\n      label: 前置确认\n      description: 在提交之前，请勾选以下选项以确认您已完成必要的自助排查\n      options:\n        - label: 我确认这是项目 **Bug**， 而非操作不当或使用不清楚。否则请使用其他 Issue 模板或移步[讨论区](https://github.com/nelvko/clash-for-linux-install/discussions)\n          required: true\n        - label: 我已查阅 [常见问题](https://github.com/nelvko/clash-for-linux-install/wiki/FAQ)，但仍未找到解决方案\n          required: true\n        - label: 我已使用关键字查询相关 [Issues](https://github.com/nelvko/clash-for-linux-install/issues?q=is%3Aissue)，但仍未找到解决方案\n          required: true\n        - label: 我已确认本地与远程仓库最新提交的哈希一致。 (`git rev-parse --short HEAD`)\n          required: true\n\n  - type: input\n    id: branch\n    attributes:\n      label: 使用的分支\n      placeholder: master / ...\n    validations:\n      required: true\n\n  - type: input\n    id: commit_id\n    attributes:\n      label: Commit ID\n      description: |\n        请提供当前 Commit 哈希：`git rev-parse --short HEAD`\n    validations:\n      required: true\n\n  - type: input\n    id: shell\n    attributes:\n      label: Shell 环境\n      placeholder: bash / zsh / ...\n    validations:\n      required: true\n\n  - type: textarea\n    id: system\n    attributes:\n      label: 系统信息\n      description: |\n        请提供系统相关信息，可任选以下命令输出之一：\n        ```bash\n        hostnamectl\n        lsb_release -a\n        uname -a\n        ```\n      render: shell\n    validations:\n      required: true\n\n  - type: textarea\n    id: desc\n    attributes:\n      label: 描述 & 复现\n      description: |\n        请详细描述：\n        1. 操作步骤  \n        2. 期望结果  \n        3. 实际表现  \n        4. ~~必要时~~最好附上截图\n        **请勿泄露订阅、IP 等敏感信息**\n      # placeholder: |\n\n    validations:\n      required: true\n\n  - type: textarea\n    id: logs\n    attributes:\n      label: 日志\n      description: 请提供相关日志输出或截图\n      render: shell\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feat_report.yml",
    "content": "name: 功能建议\ndescription: 为该项目提出建议\ntitle: \"[Feature] \"\nlabels: [\"enhancement\"]\nbody:\n  - type: textarea\n    attributes:\n      label: 描述\n      description: 请提供对于该功能的详细描述。\n    validations:\n      required: true"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/q&a.yml",
    "content": "name: Q&A\ndescription: \"问题反馈与解答\"\ntitle: \"[Q&A] \"\nlabels: []\nbody:\n  - type: checkboxes\n    id: ensure\n    attributes:\n      label: 前置确认\n      description: 大部分使用问题可以在下述选项中得到解决，在提交问题前，请先进行自助排查。\n      options:\n        - label: 我已查阅 [常见问题](https://github.com/nelvko/clash-for-linux-install/wiki/FAQ)，但仍未找到解决方案\n          required: true\n        - label: 我已使用关键字查询相关 [Issues](https://github.com/nelvko/clash-for-linux-install/issues?q=is%3Aissue)，但仍未找到解决方案\n          required: true\n\n  - type: input\n    id: branch\n    attributes:\n      label: 使用的分支\n      placeholder: master / ...\n    validations:\n      required: true\n\n  - type: input\n    id: commit_id\n    attributes:\n      label: Commit ID\n      description: |\n        请提供当前 Commit 哈希：`git rev-parse --short HEAD`\n    validations:\n      required: true\n\n  - type: input\n    id: shell\n    attributes:\n      label: Shell 环境\n      placeholder: bash / zsh / ...\n    validations:\n      required: true\n\n  - type: textarea\n    id: desc\n    attributes:\n      label: 问题描述\n      description: |\n        ~~必要时~~最好附上截图。\n        **请勿泄露订阅、IP 等敏感信息**\n\n    validations:\n      required: true\n  "
  },
  {
    "path": ".github/workflows/stale-issues.yml",
    "content": "name: Close inactive issues\non:\n  schedule:\n    - cron: \"0 9 * * 1-5\"\n  workflow_dispatch: # 允许手动触发\n\njobs:\n  close-issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      actions: write\n    steps:\n      - uses: actions/stale@v9\n        with:\n          exempt-issue-labels: \"bug,documentation,enhancement\"\n          days-before-issue-stale: 7\n          days-before-issue-close: 7\n          stale-issue-label: \"stale\"\n          stale-issue-message: \"长时间未活跃，将在 7 天后自动关闭。若仍需讨论，请回复以保持活跃。\"\n          close-issue-message: \"已自动关闭，如未解决,可重新打开。\"\n          repo-token: ${{ secrets.GITHUB_TOKEN }} # 自动生成\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n.vscode\nconfig.yaml*\nresources/zip/*\n!resources/zip/dist.zip\nresources/dist/\ntest.sh\n\n\n\n!.gitkeep\n"
  },
  {
    "path": ".shellcheckrc",
    "content": "disable=SC1091\ndisable=SC2155\ndisable=SC2296\ndisable=SC2153"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\r\n\r\nCopyright (c) 2024-2026 nelvko\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.\r\n"
  },
  {
    "path": "README.md",
    "content": "# Linux 一键安装 Clash\n\n![GitHub License](https://img.shields.io/github/license/nelvko/clash-for-linux-install)\n![GitHub top language](https://img.shields.io/github/languages/top/nelvko/clash-for-linux-install)\n![GitHub Repo stars](https://img.shields.io/github/stars/nelvko/clash-for-linux-install)\n\n![preview](resources/preview.png)\n\n## ✨ 功能特性\n\n- 支持一键安装 `mihomo` 与 `clash` 代理内核。\n- 兼容 `root` 与普通用户环境。\n- 适配主流 `Linux` 发行版，并兼容 `AutoDL` 等容器化环境。\n- 自动检测端口占用情况，在冲突时随机分配可用端口。\n- 自动识别系统架构与初始化系统，下载匹配的内核与依赖，并生成对应的服务管理配置。\n- 在需要时调用 [subconverter](https://github.com/tindy2013/subconverter) 进行本地订阅转换。\n\n## 🚀 一键安装\n\n在终端中执行以下命令即可完成安装：\n\n```bash\ngit clone --branch master --depth 1 https://gh-proxy.org/https://github.com/nelvko/clash-for-linux-install.git \\\n  && cd clash-for-linux-install \\\n  && bash install.sh\n```\n\n- 上述命令使用了[加速前缀](https://gh-proxy.org/)，如失效可更换其他[可用链接](https://ghproxy.link/)。\n- 可通过 `.env` 文件或脚本参数自定义安装选项。\n- 没有订阅？[click me](https://次元.net/auth/register?code=oUbI)\n\n## ⌨️ 命令一览\n\n```bash\nUsage: \n  clashctl COMMAND [OPTIONS]\n\nCommands:\n    on                    开启代理\n    off                   关闭代理\n    status                内核状况\n    proxy                 系统代理\n    ui                    Web 面板\n    secret                Web 密钥\n    sub                   订阅管理\n    upgrade               升级内核\n    tun                   Tun 模式\n    mixin                 Mixin 配置\n\nGlobal Options:\n    -h, --help            显示帮助信息\n```\n\n💡`clashon` 同 `clashctl on`，`Tab` 补全更方便！\n\n### 优雅启停\n\n```bash\n$ clashon\n😼 已开启代理环境\n\n$ clashoff\n😼 已关闭代理环境\n```\n- 在启停代理内核的同时，同步设置系统代理。\n- 亦可通过 `clashproxy` 单独控制系统代理。\n\n### Web 控制台\n\n```bash\n$ clashui\n╔═══════════════════════════════════════════════╗\n║                😼 Web 控制台                  ║\n║═══════════════════════════════════════════════║\n║                                               ║\n║     🔓 注意放行端口：9090                      ║\n║     🏠 内网：http://192.168.0.1:9090/ui       ║\n║     🌏 公网：http://8.8.8.8:9090/ui          ║\n║     ☁️ 公共：http://board.zash.run.place      ║\n║                                               ║\n╚═══════════════════════════════════════════════╝\n\n$ clashsecret mysecret\n😼 密钥更新成功，已重启生效\n\n$ clashsecret\n😼 当前密钥：mysecret\n```\n\n- 可通过浏览器打开 `Web` 控制台进行可视化操作，例如切换节点、查看日志等。\n- 默认使用 [zashboard](https://github.com/Zephyruso/zashboard) 作为控制台前端，如需更换可自行配置。\n- 若需将控制台暴露到公网，建议定期更换访问密钥，或通过 `SSH` 端口转发方式进行安全访问。\n\n### `Mixin` 配置\n\n```bash\n$ clashmixin\n😼 查看 Mixin 配置\n\n$ clashmixin -e\n😼 编辑 Mixin 配置\n\n$ clashmixin -c\n😼 查看原始订阅配置\n\n$ clashmixin -r\n😼 查看运行时配置\n```\n\n- 通过 `Mixin` 自定义的配置内容会与原始订阅进行深度合并，且 `Mixin` 具有最高优先级，最终生成内核启动时加载的运行时配置。\n- `Mixin` 支持以前置、后置或覆盖的方式，对原始订阅中的规则、节点及策略组进行新增或修改。\n\n### 升级内核\n```bash\n$ clashupgrade\n😼 请求内核升级...\n{\"status\":\"ok\"}\n😼 内核升级成功\n```\n- 升级过程由代理内核自动完成；如需查看详细的升级日志，可添加 `-v` 参数。\n- 建议通过 `clashmixin` 为 `github` 配置代理规则，以避免因网络问题导致请求失败。\n\n### 管理订阅\n\n```bash\n$ clashsub -h\nUsage: \n  clashsub COMMAND [OPTIONS]\n\nCommands:\n  add <url>       添加订阅\n  ls              查看订阅\n  del <id>        删除订阅\n  use <id>        使用订阅\n  update [id]     更新订阅\n  log             订阅日志\n\n\nOptions:\n  update:\n    --auto        配置自动更新\n    --convert     使用订阅转换\n```\n\n- 支持添加本地订阅，例如：`file:///root/clashctl/resources/config.yaml`\n- 当订阅链接解析失败或包含特殊字符时，请使用引号包裹以避免被错误解析。\n- 自动更新任务可通过 `crontab -e` 进行修改和管理。\n\n### `Tun` 模式\n\n```bash\n$ clashtun\n😾 Tun 状态：关闭\n\n$ clashtun on\n😼 Tun 模式已开启\n```\n\n- 作用：实现本机及 `Docker` 等容器的所有流量路由到 `clash` 代理、DNS 劫持等。\n- 原理：[clash-verge-rev](https://www.clashverge.dev/guide/term.html#tun)、 [clash.wiki](https://clash.wiki/premium/tun-device.html)。\n- 注意事项：[#100](https://github.com/nelvko/clash-for-linux-install/issues/100#issuecomment-2782680205)\n\n## 🗑️ 卸载\n\n```bash\nbash uninstall.sh\n```\n\n## 📖 常见问题\n\n👉 [Wiki · FAQ](https://github.com/nelvko/clash-for-linux-install/wiki/FAQ)\n\n## 🔗 引用\n\n- [clash](https://clash.wiki/)\n- [mihomo](https://github.com/MetaCubeX/mihomo)\n- [subconverter](https://github.com/tindy2013/subconverter)\n- [yq](https://github.com/mikefarah/yq)\n- [zashboard](https://github.com/Zephyruso/zashboard)\n\n## ⭐ Star History\n\n<a href=\"https://www.star-history.com/#nelvko/clash-for-linux-install&Date\">\n\n <picture>\n   <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/svg?repos=nelvko/clash-for-linux-install&type=Date&theme=dark\" />\n   <source media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/svg?repos=nelvko/clash-for-linux-install&type=Date\" />\n   <img alt=\"Star History Chart\" src=\"https://api.star-history.com/svg?repos=nelvko/clash-for-linux-install&type=Date\" />\n </picture>\n</a>\n\n## 🙏 Thanks\n\n[@鑫哥](https://github.com/TrackRay)\n\n## ⚠️ 特别声明\n\n1. 编写本项目主要目的为学习和研究 `Shell` 编程，不得将本项目中任何内容用于违反国家/地区/组织等的法律法规或相关规定的其他用途。\n2. 本项目保留随时对免责声明进行补充或更改的权利，直接或间接使用本项目内容的个人或组织，视为接受本项目的特别声明。\n"
  },
  {
    "path": "install.sh",
    "content": "#!/usr/bin/env bash\n\n. scripts/cmd/clashctl.sh\n. scripts/preflight.sh\n\n_valid\n_parse_args \"$@\"\n\n_prepare_zip\n_detect_init\n\n_okcat \"安装内核：$KERNEL_NAME by ${INIT_TYPE}\"\n_okcat '📦' \"安装路径：$CLASH_BASE_DIR\"\n\n/bin/cp -rf . \"$CLASH_BASE_DIR\"\ntouch \"$CLASH_CONFIG_BASE\"\n_set_envs\n_is_regular_sudo && chown -R \"$SUDO_USER\" \"$CLASH_BASE_DIR\"\n\n_install_service\n_apply_rc\n\n\n_merge_config\n_detect_proxy_port\nclashui\nclashsecret \"$(_get_random_val)\" >/dev/null\nclashsecret\n\n_okcat '🎉' 'enjoy 🎉'\nclashctl\n\n_valid_config \"$CLASH_CONFIG_BASE\" && CLASH_CONFIG_URL=\"file://$CLASH_CONFIG_BASE\"\n_quit \"clashsub add $CLASH_CONFIG_URL && clashsub use 1\"\n"
  },
  {
    "path": "resources/mixin.yaml",
    "content": "_custom:\n  system-proxy:\n    enable: true\n\nmixed-port: 7890\n\nexternal-controller: \"0.0.0.0:9090\"\nexternal-ui: dist\nexternal-ui-url: https://github.com/Zephyruso/zashboard/releases/latest/download/dist.zip\nsecret:\n\nallow-lan: false # 若开启务必设置用户验证以防暴露公网后被滥用\nauthentication:\n  # - \"username:password\" # 用户验证（clashon 会自动填充）\n\n# prefix：前置插入\n# suffix：后置插入\n# override：根据 name 匹配并替换原项\nrules:\n  prefix:\n    - DOMAIN,api64.ipify.org,DIRECT # 用于 clashui 获取真实公网 IP\n    # - DOMAIN-KEYWORD,github,proxy   # 用于 clashupgrade 升级内核\n  suffix:\n\nproxies:\n  prefix:\n  suffix:\n  override:\n\nproxy-groups:\n  prefix:\n  suffix:\n  override:\n\n# tun 配置\ntun:\n  enable: false\n  stack: system\n  auto-route: true\n  auto-redir: true # clash\n  auto-redirect: true # mihomo\n  auto-detect-interface: true\n  dns-hijack:\n    - any:53\n    - tcp://any:53\n  strict-route: true\n  route-exclude-address:\n    - 1.1.1.1/32 # 用于 clashui 获取真实内网 IP\n    - 127.0.0.1/32\n  exclude-interface:\n    - docker0 # 避免无法访问容器内服务\n    - podman0 # 详见：https://github.com/nelvko/clash-for-linux-install/issues/100#issuecomment-2995667368\n\n# DNS 配置\ndns:\n  enable: true\n  listen: 0.0.0.0:1053\n  enhanced-mode: fake-ip\n  nameserver:\n    - 114.114.114.114\n    - 8.8.8.8\n"
  },
  {
    "path": "resources/profiles/.gitkeep",
    "content": ""
  },
  {
    "path": "resources/profiles.yaml",
    "content": "# 当前使用的订阅\nuse: \n\n# 订阅列表\nprofiles: "
  },
  {
    "path": "scripts/cmd/clashctl.fish",
    "content": "set fn_arr \\\nclashui \\\nclashstatus \\\nclashsecret \\\nclashtun \\\nclashmixin \\\nclashsub \\\nclashlog \\\nclashupgrade \\\nclashhelp\n\nset -gx fish_version $FISH_VERSION\n\nfor fn in $fn_arr\n    eval \"\n    function $fn\n        bash -i -c '$fn \\\"\\$@\\\"' -- \\$argv\n    end\n    \"\nend\n\n\nfunction clashctl\n    if test -z \"$argv\"\n        clashhelp\n        return\n    end\n\n\n    set suffix $argv[1]\n    set argv $argv[2..-1]\n\n    switch $suffix\n        case on\n            clashon $argv\n        case off\n            clashoff $argv\n        case proxy\n            clashproxy $argv\n        case '*'\n            clash\"$suffix\" $argv\n    end\nend\n\nfunction clashon\n    bash -i -c 'clashon; sudo tee /var/proxy >/dev/null <<EOF\nexport http_proxy=$http_proxy\nexport https_proxy=$http_proxy\nexport HTTP_PROXY=$http_proxy\nexport HTTPS_PROXY=$http_proxy\n\nexport all_proxy=$all_proxy\nexport ALL_PROXY=$all_proxy\n\nexport no_proxy=$no_proxy\nexport NO_PROXY=$no_proxy\nEOF'\n\n    clashproxy on\nend\n\nfunction clashoff\n    bash -i -c 'clashoff'\n    clashproxy off\nend\n\nfunction clashproxy\n    if test (count $argv) -eq 0\n        echo \"Usage: clashproxy [on|off]\"\n        return 1\n    end\n    switch $argv[1]\n        case on\n            source /var/proxy\n            echo '已开启系统代理'\n        case off\n            set -e \\\n            http_proxy \\\n            https_proxy \\\n            HTTP_PROXY \\\n            HTTPS_PROXY \\\n            all_proxy \\\n            ALL_PROXY \\\n            no_proxy \\\n            NO_PROXY\n            echo '已关闭系统代理'\n    end\nend"
  },
  {
    "path": "scripts/cmd/clashctl.sh",
    "content": "#!/usr/bin/env bash\n\nTHIS_SCRIPT_DIR=$(dirname \"$(readlink -f \"${BASH_SOURCE:-${(%):-%N}}\")\")\n. \"$THIS_SCRIPT_DIR/common.sh\"\n\n_set_system_proxy() {\n    local mixed_port=$(\"$BIN_YQ\" '.mixed-port // \"\"' \"$CLASH_CONFIG_RUNTIME\")\n    local http_port=$(\"$BIN_YQ\" '.port // \"\"' \"$CLASH_CONFIG_RUNTIME\")\n    local socks_port=$(\"$BIN_YQ\" '.socks-port // \"\"' \"$CLASH_CONFIG_RUNTIME\")\n\n    local auth=$(\"$BIN_YQ\" '.authentication[0] // \"\"' \"$CLASH_CONFIG_RUNTIME\")\n    [ -n \"$auth\" ] && auth=$auth@\n\n    local bind_addr=$(_get_bind_addr)\n    local http_proxy_addr=\"http://${auth}${bind_addr}:${http_port:-${mixed_port}}\"\n    local socks_proxy_addr=\"socks5h://${auth}${bind_addr}:${socks_port:-${mixed_port}}\"\n    local no_proxy_addr=\"localhost,127.0.0.1,::1\"\n\n    export http_proxy=$http_proxy_addr\n    export HTTP_PROXY=$http_proxy\n\n    export https_proxy=$http_proxy\n    export HTTPS_PROXY=$https_proxy\n\n    export all_proxy=$socks_proxy_addr\n    export ALL_PROXY=$all_proxy\n\n    export no_proxy=$no_proxy_addr\n    export NO_PROXY=$no_proxy\n}\n_unset_system_proxy() {\n    unset http_proxy\n    unset https_proxy\n    unset HTTP_PROXY\n    unset HTTPS_PROXY\n    unset all_proxy\n    unset ALL_PROXY\n    unset no_proxy\n    unset NO_PROXY\n}\n_detect_proxy_port() {\n    local mixed_port=$(\"$BIN_YQ\" '.mixed-port // \"\"' \"$CLASH_CONFIG_RUNTIME\")\n    local http_port=$(\"$BIN_YQ\" '.port // \"\"' \"$CLASH_CONFIG_RUNTIME\")\n    local socks_port=$(\"$BIN_YQ\" '.socks-port // \"\"' \"$CLASH_CONFIG_RUNTIME\")\n    [ -z \"$mixed_port\" ] && [ -z \"$http_port\" ] && [ -z \"$socks_port\" ] && mixed_port=7890\n\n    local newPort count=0\n    local port_list=(\n        \"mixed_port|mixed-port\"\n        \"http_port|port\"\n        \"socks_port|socks-port\"\n    )\n    clashstatus >&/dev/null && local isActive='true'\n    for entry in \"${port_list[@]}\"; do\n        local var_name=\"${entry%|*}\"\n        local yaml_key=\"${entry#*|}\"\n\n        eval \"local var_val=\\${$var_name}\"\n\n        [ -n \"$var_val\" ] && _is_port_used \"$var_val\" && [ \"$isActive\" != \"true\" ] && {\n            newPort=$(_get_random_port)\n            ((count++))\n            _failcat '🎯' \"端口冲突：[$yaml_key] $var_val 🎲 随机分配 $newPort\"\n            \"$BIN_YQ\" -i \".${yaml_key} = $newPort\" \"$CLASH_CONFIG_MIXIN\"\n        }\n    done\n    ((count)) && _merge_config\n}\n\nfunction clashon() {\n    _detect_proxy_port\n    clashstatus >&/dev/null || placeholder_start\n    clashstatus >&/dev/null || {\n        _failcat '启动失败: 执行 clashlog 查看日志'\n        return 1\n    }\n    clashproxy >/dev/null && _set_system_proxy\n    _okcat '已开启代理环境'\n}\n\nwatch_proxy() {\n    [ -z \"$http_proxy\" ] && {\n        # [[ \"$0\" == -* ]] && { # 登录式shell\n        [[ $- == *i* ]] && { # 交互式shell\n            placeholder_watch_proxy\n        }\n    }\n}\n\nfunction clashoff() {\n    clashstatus >&/dev/null && {\n        placeholder_stop >/dev/null\n        clashstatus >&/dev/null && _tunstatus >&/dev/null && {\n            _tunoff || _error_quit \"请先关闭 Tun 模式\"\n        }\n        placeholder_stop >/dev/null\n        clashstatus >&/dev/null && {\n            _failcat '代理环境关闭失败'\n            return 1\n        }\n    }\n    _unset_system_proxy\n    _okcat '已关闭代理环境'\n}\n\nclashrestart() {\n    clashoff >/dev/null\n    clashon\n}\n\nfunction clashproxy() {\n    case \"$1\" in\n    -h | --help)\n        cat <<EOF\n\n- 查看系统代理状态\n  clashproxy\n\n- 开启系统代理\n  clashproxy on\n\n- 关闭系统代理\n  clashproxy off\n\nEOF\n        return 0\n        ;;\n    on)\n        clashstatus >&/dev/null || {\n            _failcat \"$KERNEL_NAME 未运行，请先执行 clashon\"\n            return 1\n        }\n        \"$BIN_YQ\" -i '._custom.system-proxy.enable = true' \"$CLASH_CONFIG_MIXIN\"\n        _set_system_proxy\n        _okcat '已开启系统代理'\n        ;;\n    off)\n        \"$BIN_YQ\" -i '._custom.system-proxy.enable = false' \"$CLASH_CONFIG_MIXIN\"\n        _unset_system_proxy\n        _okcat '已关闭系统代理'\n        ;;\n    *)\n        local system_proxy_enable=$(\"$BIN_YQ\" '._custom.system-proxy.enable' \"$CLASH_CONFIG_MIXIN\" 2>/dev/null)\n        case $system_proxy_enable in\n        true)\n            _okcat \"系统代理：开启\n$(env | grep -i 'proxy=')\"\n            ;;\n        *)\n            _failcat \"系统代理：关闭\"\n            ;;\n        esac\n        ;;\n    esac\n}\n\nfunction clashstatus() {\n    placeholder_status \"$@\"\n    placeholder_is_active >&/dev/null\n}\n\nfunction clashlog() {\n    placeholder_log \"$@\"\n}\n\nfunction clashui() {\n    _detect_ext_addr\n    clashstatus >&/dev/null || clashon >/dev/null\n    local query_url='api64.ipify.org' # ifconfig.me\n    local public_ip=$(curl -s --noproxy \"*\" --location --max-time 2 $query_url)\n    local public_address=\"http://${public_ip:-公网}:${EXT_PORT}/ui\"\n\n    local local_ip=$EXT_IP\n    local local_address=\"http://${local_ip}:${EXT_PORT}/ui\"\n    printf \"\\n\"\n    printf \"╔═══════════════════════════════════════════════╗\\n\"\n    printf \"║                %s                  ║\\n\" \"$(_okcat 'Web 控制台')\"\n    printf \"║═══════════════════════════════════════════════║\\n\"\n    printf \"║                                               ║\\n\"\n    printf \"║     🔓 注意放行端口：%-5s                    ║\\n\" \"$EXT_PORT\"\n    printf \"║     🏠 内网：%-31s  ║\\n\" \"$local_address\"\n    printf \"║     🌏 公网：%-31s  ║\\n\" \"$public_address\"\n    printf \"║     ☁️  公共：%-31s  ║\\n\" \"$URL_CLASH_UI\"\n    printf \"║                                               ║\\n\"\n    printf \"╚═══════════════════════════════════════════════╝\\n\"\n    printf \"\\n\"\n}\n\n_merge_config() {\n    cat \"$CLASH_CONFIG_RUNTIME\" >\"$CLASH_CONFIG_TEMP\" 2>/dev/null\n    # shellcheck disable=SC2016\n    \"$BIN_YQ\" eval-all '\n      ########################################\n      #              Load Files              #\n      ########################################\n      select(fileIndex==0) as $config |\n      select(fileIndex==1) as $mixin |\n      \n      ########################################\n      #              Deep Merge              #\n      ########################################\n      $mixin |= del(._custom) |\n      (($config // {}) * $mixin) as $runtime |\n      $runtime |\n      \n      ########################################\n      #               Rules                  #\n      ########################################\n      .rules = (\n        ($mixin.rules.prefix // []) +\n        ($config.rules // []) +\n        ($mixin.rules.suffix // [])\n      ) |\n      \n      ########################################\n      #                Proxies               #\n      ########################################\n      .proxies = (\n        ($mixin.proxies.prefix // []) +\n        (\n          ($config.proxies // []) as $configList |\n          ($mixin.proxies.override // []) as $overrideList |\n          $configList | map(\n            . as $configItem |\n            (\n              $overrideList[] | select(.name == $configItem.name)\n            ) // $configItem\n          )\n        ) +\n        ($mixin.proxies.suffix // [])\n      ) |\n      \n      ########################################\n      #             ProxyGroups              #\n      ########################################\n      .proxy-groups = (\n        ($mixin.proxy-groups.prefix // []) +\n        (\n          ($config.proxy-groups // []) as $configList |\n          ($mixin.proxy-groups.override // []) as $overrideList |\n          $configList | map(\n            . as $configItem |\n            (\n              $overrideList[] | select(.name == $configItem.name)\n            ) // $configItem\n          )\n        ) +\n        ($mixin.proxy-groups.suffix // [])\n      )\n    ' \"$CLASH_CONFIG_BASE\" \"$CLASH_CONFIG_MIXIN\" >\"$CLASH_CONFIG_RUNTIME\"\n    _valid_config \"$CLASH_CONFIG_RUNTIME\" || {\n        cat \"$CLASH_CONFIG_TEMP\" >\"$CLASH_CONFIG_RUNTIME\"\n        _error_quit \"验证失败：请检查 Mixin 配置\"\n    }\n}\n\n_merge_config_restart() {\n    _merge_config\n    placeholder_stop >/dev/null\n    clashstatus >&/dev/null && _tunstatus >&/dev/null && {\n        _tunoff || _error_quit \"请先关闭 Tun 模式\"\n    }\n    placeholder_stop >/dev/null\n    sleep 0.1\n    placeholder_start >/dev/null\n    sleep 0.1\n}\n_get_secret() {\n    \"$BIN_YQ\" '.secret // \"\"' \"$CLASH_CONFIG_RUNTIME\"\n}\nfunction clashsecret() {\n    case \"$1\" in\n    -h | --help)\n        cat <<EOF\n\n- 查看 Web 密钥\n  clashsecret\n\n- 修改 Web 密钥\n  clashsecret <new_secret>\n\nEOF\n        return 0\n        ;;\n    esac\n\n    case $# in\n    0)\n        _okcat \"当前密钥：$(_get_secret)\"\n        ;;\n    1)\n        \"$BIN_YQ\" -i \".secret = \\\"$1\\\"\" \"$CLASH_CONFIG_MIXIN\" || {\n            _failcat \"密钥更新失败，请重新输入\"\n            return 1\n        }\n        _merge_config_restart\n        _okcat \"密钥更新成功，已重启生效\"\n        ;;\n    *)\n        _failcat \"密钥不要包含空格或使用引号包围\"\n        ;;\n    esac\n}\n\n_tunstatus() {\n    local tun_status=$(\"$BIN_YQ\" '.tun.enable' \"${CLASH_CONFIG_RUNTIME}\")\n    case $tun_status in\n    true)\n        _okcat 'Tun 状态：启用'\n        ;;\n    *)\n        _failcat 'Tun 状态：关闭'\n        ;;\n    esac\n}\n_tunoff() {\n    _tunstatus >/dev/null || return 0\n    sudo placeholder_stop\n    clashstatus >&/dev/null || {\n        \"$BIN_YQ\" -i '.tun.enable = false' \"$CLASH_CONFIG_MIXIN\"\n        _merge_config\n        clashon >/dev/null\n        _okcat \"Tun 模式已关闭\"\n        return 0\n    }\n    _tunstatus >&/dev/null && _failcat \"Tun 模式关闭失败\"\n}\n_sudo_restart() {\n    sudo placeholder_stop\n    placeholder_sudo_start\n    sleep 0.5\n}\n_tunon() {\n    _tunstatus 2>/dev/null && return 0\n    sudo placeholder_stop\n    \"$BIN_YQ\" -i '.tun.enable = true' \"$CLASH_CONFIG_MIXIN\"\n    _merge_config\n    placeholder_sudo_start\n    sleep 0.5\n    clashstatus >&/dev/null || _error_quit \"Tun 模式开启失败\"\n    local fail_msg=\"Start TUN listening error|unsupported kernel version\"\n    local ok_msg=\"Tun adapter listening at|TUN listening iface\"\n    clashlog | grep -E -m1 -qs \"$fail_msg\" && {\n        [ \"$KERNEL_NAME\" = 'mihomo' ] && {\n            \"$BIN_YQ\" -i '.tun.auto-redirect = false' \"$CLASH_CONFIG_MIXIN\"\n            _merge_config\n            _sudo_restart\n        }\n        clashlog | grep -E -m1 -qs \"$ok_msg\" || {\n            clashlog | grep -E -m1 \"$fail_msg\"\n            _tunoff >&/dev/null\n            _error_quit '系统内核版本不支持 Tun 模式'\n        }\n    }\n    _okcat \"Tun 模式已开启\"\n}\n\nfunction clashtun() {\n    case \"$1\" in\n    -h | --help)\n        cat <<EOF\n\n- 查看 Tun 状态\n  clashtun\n\n- 开启 Tun 模式\n  clashtun on\n\n- 关闭 Tun 模式\n  clashtun off\n  \nEOF\n        return 0\n        ;;\n    on)\n        _tunon\n        ;;\n    off)\n        _tunoff\n        ;;\n    *)\n        _tunstatus\n        ;;\n    esac\n}\n\nfunction clashmixin() {\n    case \"$1\" in\n    -h | --help)\n        cat <<EOF\n\n- 查看 Mixin 配置：$CLASH_CONFIG_MIXIN\n  clashmixin\n\n- 编辑 Mixin 配置\n  clashmixin -e\n\n- 查看原始订阅配置：$CLASH_CONFIG_BASE\n  clashmixin -c\n\n- 查看运行时配置：$CLASH_CONFIG_RUNTIME\n  clashmixin -r\n\nEOF\n        return 0\n        ;;\n    -e)\n        vim \"$CLASH_CONFIG_MIXIN\" && {\n            _merge_config_restart && _okcat \"配置更新成功，已重启生效\"\n        }\n        ;;\n    -r)\n        less \"$CLASH_CONFIG_RUNTIME\"\n        ;;\n    -c)\n        less \"$CLASH_CONFIG_BASE\"\n        ;;\n    *)\n        less \"$CLASH_CONFIG_MIXIN\"\n        ;;\n    esac\n}\n\nfunction clashupgrade() {\n    for arg in \"$@\"; do\n        case $arg in\n        -h | --help)\n            cat <<EOF\nUsage:\n  clashupgrade [OPTIONS]\n\nOptions:\n  -v, --verbose       输出内核升级日志\n  -r, --release       升级至稳定版\n  -a, --alpha         升级至测试版\n  -h, --help          显示帮助信息\n\nEOF\n            return 0\n            ;;\n        -v | --verbose)\n            local log_flag=true\n            ;;\n        -r | --release)\n            channel=\"release\"\n            ;;\n        -a | --alpha)\n            channel=\"alpha\"\n            ;;\n        *)\n            channel=\"\"\n            ;;\n        esac\n    done\n\n    _detect_ext_addr\n    clashstatus >&/dev/null || clashon >/dev/null\n    _okcat '⏳' \"请求内核升级...\"\n    [ \"$log_flag\" = true ] && {\n        log_cmd=(placeholder_follow_log)\n        (\"${log_cmd[@]}\" &)\n\n    }\n    local res=$(\n        curl -X POST \\\n            --silent \\\n            --noproxy \"*\" \\\n            --location \\\n            -H \"Authorization: Bearer $(_get_secret)\" \\\n            \"http://${EXT_IP}:${EXT_PORT}/upgrade?channel=$channel\"\n    )\n    [ \"$log_flag\" = true ] && pkill -9 -f \"${log_cmd[*]}\"\n\n    grep '\"status\":\"ok\"' <<<\"$res\" && {\n        _okcat \"内核升级成功\"\n        return 0\n    }\n    grep 'already using latest version' <<<\"$res\" && {\n        _okcat \"已是最新版本\"\n        return 0\n    }\n    _failcat \"内核升级失败，请检查网络或稍后重试\"\n}\n\nfunction clashsub() {\n    case \"$1\" in\n    add)\n        shift\n        _sub_add \"$@\"\n        ;;\n    del)\n        shift\n        _sub_del \"$@\"\n        ;;\n    list | ls | '')\n        shift\n        _sub_list \"$@\"\n        ;;\n    use)\n        shift\n        _sub_use \"$@\"\n        ;;\n    update)\n        shift\n        _sub_update \"$@\"\n        ;;\n    log)\n        shift\n        _sub_log \"$@\"\n        ;;\n    -h | --help | *)\n        cat <<EOF\nclashsub - Clash 订阅管理工具\n\nUsage: \n  clashsub COMMAND [OPTIONS]\n\nCommands:\n  add <url>       添加订阅\n  ls              查看订阅\n  del <id>        删除订阅\n  use <id>        使用订阅\n  update [id]     更新订阅\n  log             订阅日志\n\nOptions:\n  update:\n    --auto        配置自动更新\n    --convert     使用订阅转换\nEOF\n        ;;\n    esac\n}\n_sub_add() {\n    local url=$1\n    [ -z \"$url\" ] && {\n        echo -n \"$(_okcat '✈️ ' '请输入要添加的订阅链接：')\"\n        read -r url\n        [ -z \"$url\" ] && _error_quit \"订阅链接不能为空\"\n    }\n    _get_url_by_id \"$id\" >/dev/null && _error_quit \"该订阅链接已存在\"\n\n    _download_config \"$CLASH_CONFIG_TEMP\" \"$url\"\n    _valid_config \"$CLASH_CONFIG_TEMP\" || _error_quit \"订阅无效，请检查：\n    原始订阅：${CLASH_CONFIG_TEMP}.raw\n    转换订阅：$CLASH_CONFIG_TEMP\n    转换日志：$BIN_SUBCONVERTER_LOG\"\n\n    local id=$(\"$BIN_YQ\" '.profiles // [] | (map(.id) | max) // 0 | . + 1' \"$CLASH_PROFILES_META\")\n    local profile_path=\"${CLASH_PROFILES_DIR}/${id}.yaml\"\n    mv \"$CLASH_CONFIG_TEMP\" \"$profile_path\"\n\n    \"$BIN_YQ\" -i \"\n         .profiles = (.profiles // []) + \n         [{\n           \\\"id\\\": $id,\n           \\\"path\\\": \\\"$profile_path\\\",\n           \\\"url\\\": \\\"$url\\\"\n         }]\n    \" \"$CLASH_PROFILES_META\"\n    _logging_sub \"➕ 已添加订阅：[$id] $url\"\n    _okcat '🎉' \"订阅已添加：[$id] $url\"\n}\n_sub_del() {\n    local id=$1\n    [ -z \"$id\" ] && {\n        echo -n \"$(_okcat '✈️ ' '请输入要删除的订阅 id：')\"\n        read -r id\n        [ -z \"$id\" ] && _error_quit \"订阅 id 不能为空\"\n    }\n    local profile_path url\n    profile_path=$(_get_path_by_id \"$id\") || _error_quit \"订阅 id 不存在，请检查\"\n    url=$(_get_url_by_id \"$id\")\n    use=$(\"$BIN_YQ\" '.use // \"\"' \"$CLASH_PROFILES_META\")\n    [ \"$use\" = \"$id\" ] && _error_quit \"删除失败：订阅 $id 正在使用中，请先切换订阅\"\n    /usr/bin/rm -f \"$profile_path\"\n    \"$BIN_YQ\" -i \"del(.profiles[] | select(.id == \\\"$id\\\"))\" \"$CLASH_PROFILES_META\"\n    _logging_sub \"➖ 已删除订阅：[$id] $url\"\n    _okcat '🎉' \"订阅已删除：[$id] $url\"\n}\n_sub_list() {\n    \"$BIN_YQ\" \"$CLASH_PROFILES_META\"\n}\n_sub_use() {\n    \"$BIN_YQ\" -e '.profiles // [] | length == 0' \"$CLASH_PROFILES_META\" >&/dev/null &&\n        _error_quit \"当前无可用订阅，请先添加订阅\"\n    local id=$1\n    [ -z \"$id\" ] && {\n        clashsub ls\n        echo -n \"$(_okcat '✈️ ' '请输入要使用的订阅 id：')\"\n        read -r id\n        [ -z \"$id\" ] && _error_quit \"订阅 id 不能为空\"\n    }\n    local profile_path url\n    profile_path=$(_get_path_by_id \"$id\") || _error_quit \"订阅 id 不存在，请检查\"\n    url=$(_get_url_by_id \"$id\")\n    cat \"$profile_path\" >\"$CLASH_CONFIG_BASE\"\n    _merge_config_restart\n    \"$BIN_YQ\" -i \".use = $id\" \"$CLASH_PROFILES_META\"\n    _logging_sub \"🔥 订阅已切换为：[$id] $url\"\n    _okcat '🔥' '订阅已生效'\n}\n_get_path_by_id() {\n    \"$BIN_YQ\" -e \".profiles[] | select(.id == \\\"$1\\\") | .path\" \"$CLASH_PROFILES_META\" 2>/dev/null\n}\n_get_url_by_id() {\n    \"$BIN_YQ\" -e \".profiles[] | select(.id == \\\"$1\\\") | .url\" \"$CLASH_PROFILES_META\" 2>/dev/null\n}\n_sub_update() {\n    local arg is_convert\n    for arg in \"$@\"; do\n        case $arg in\n        --auto)\n            command -v crontab >/dev/null || _error_quit \"未检测到 crontab 命令，请先安装 cron 服务\"\n            crontab -l | grep -qs 'clashsub update' || {\n                (\n                    crontab -l 2>/dev/null\n                    echo \"0 0 */2 * * $SHELL -i -c 'clashsub update'\"\n                ) | crontab -\n            }\n            _okcat \"已设置定时更新订阅\"\n            return 0\n            ;;\n        --convert)\n            is_convert=true\n            shift\n            ;;\n        esac\n    done\n    local id=$1\n    [ -z \"$id\" ] && id=$(\"$BIN_YQ\" '.use // 1' \"$CLASH_PROFILES_META\")\n    local url profile_path\n    url=$(_get_url_by_id \"$id\") || _error_quit \"订阅 id 不存在，请检查\"\n    profile_path=$(_get_path_by_id \"$id\")\n    _okcat \"✈️ \" \"更新订阅：[$id] $url\"\n\n    [ \"$is_convert\" = true ] && {\n        _download_convert_config \"$CLASH_CONFIG_TEMP\" \"$url\"\n    }\n    [ \"$is_convert\" != true ] && {\n        _download_config \"$CLASH_CONFIG_TEMP\" \"$url\"\n    }\n    _valid_config \"$CLASH_CONFIG_TEMP\" || {\n        _logging_sub \"❌ 订阅更新失败：[$id] $url\"\n        _error_quit \"订阅无效：请检查：\n    原始订阅：${CLASH_CONFIG_TEMP}.raw\n    转换订阅：$CLASH_CONFIG_TEMP\n    转换日志：$BIN_SUBCONVERTER_LOG\"\n    }\n    _logging_sub \"✅ 订阅更新成功：[$id] $url\"\n    cat \"$CLASH_CONFIG_TEMP\" >\"$profile_path\"\n    use=$(\"$BIN_YQ\" '.use // \"\"' \"$CLASH_PROFILES_META\")\n    [ \"$use\" = \"$id\" ] && clashsub use \"$use\" && return\n    _okcat '订阅已更新'\n}\n_logging_sub() {\n    echo \"$(date +\"%Y-%m-%d %H:%M:%S\") $1\" >>\"${CLASH_PROFILES_LOG}\"\n}\n_sub_log() {\n    tail <\"${CLASH_PROFILES_LOG}\" \"$@\"\n}\n\nfunction clashctl() {\n    case \"$1\" in\n    on)\n        shift\n        clashon\n        ;;\n    off)\n        shift\n        clashoff\n        ;;\n    ui)\n        shift\n        clashui\n        ;;\n    status)\n        shift\n        clashstatus \"$@\"\n        ;;\n    log)\n        shift\n        clashlog \"$@\"\n        ;;\n    proxy)\n        shift\n        clashproxy \"$@\"\n        ;;\n    tun)\n        shift\n        clashtun \"$@\"\n        ;;\n    mixin)\n        shift\n        clashmixin \"$@\"\n        ;;\n    secret)\n        shift\n        clashsecret \"$@\"\n        ;;\n    sub)\n        shift\n        clashsub \"$@\"\n        ;;\n    upgrade)\n        shift\n        clashupgrade \"$@\"\n        ;;\n    *)\n        (($#)) && shift\n        clashhelp \"$@\"\n        ;;\n    esac\n}\n\nclashhelp() {\n    cat <<EOF\n    \nUsage: \n  clashctl COMMAND [OPTIONS]\n\nCommands:\n  on                    开启代理\n  off                   关闭代理\n  proxy                 系统代理\n  status                内核状态\n  ui                    面板地址\n  sub                   订阅管理\n  log                   内核日志\n  tun                   Tun 模式\n  mixin                 Mixin 配置\n  secret                Web 密钥\n  upgrade               升级内核\n\nGlobal Options:\n  -h, --help            显示帮助信息\n\nFor more help on how to use clashctl, head to https://github.com/nelvko/clash-for-linux-install\nEOF\n}\n"
  },
  {
    "path": "scripts/cmd/common.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC2034\n. \"$(dirname \"$(dirname \"$THIS_SCRIPT_DIR\")\")/.env\"\n\nCLASH_RESOURCES_DIR=\"${CLASH_BASE_DIR}/resources\"\nCLASH_CONFIG_BASE=\"${CLASH_RESOURCES_DIR}/config.yaml\"\nCLASH_CONFIG_MIXIN=\"${CLASH_RESOURCES_DIR}/mixin.yaml\"\nCLASH_CONFIG_RUNTIME=\"${CLASH_RESOURCES_DIR}/runtime.yaml\"\nCLASH_CONFIG_TEMP=\"${CLASH_RESOURCES_DIR}/temp.yaml\"\n\nBIN_BASE_DIR=\"${CLASH_BASE_DIR}/bin\"\nBIN_KERNEL=\"${BIN_BASE_DIR}/$KERNEL_NAME\"\nBIN_YQ=\"${BIN_BASE_DIR}/yq\"\nBIN_SUBCONVERTER_DIR=\"${BIN_BASE_DIR}/subconverter\"\nBIN_SUBCONVERTER=\"${BIN_SUBCONVERTER_DIR}/subconverter\"\nBIN_SUBCONVERTER_START=\"$BIN_SUBCONVERTER\"\nBIN_SUBCONVERTER_STOP=\"pkill -9 -f $BIN_SUBCONVERTER\"\nBIN_SUBCONVERTER_CONFIG=\"$BIN_SUBCONVERTER_DIR/pref.yml\"\nBIN_SUBCONVERTER_LOG=\"${BIN_SUBCONVERTER_DIR}/latest.log\"\n\nCLASH_PROFILES_DIR=\"${CLASH_RESOURCES_DIR}/profiles\"\nCLASH_PROFILES_META=\"${CLASH_RESOURCES_DIR}/profiles.yaml\"\nCLASH_PROFILES_LOG=\"${CLASH_RESOURCES_DIR}/profiles.log\"\n\n_is_port_used() {\n    local port=$1\n    { ss -tunl 2>/dev/null || netstat -tunl; } | grep -qs \":${port}\\b\"\n}\n\n_get_random_port() {\n    local randomPort=$(shuf -i 1024-65535 -n 1)\n    ! _is_port_used \"$randomPort\" && { echo \"$randomPort\" && return; }\n    _get_random_port\n}\n\n_get_bind_addr() {\n    local allowLan bindAddr\n    bindAddr=$(\"$BIN_YQ\" '.bind-address // \"*\"' \"$CLASH_CONFIG_RUNTIME\")\n    allowLan=$(\"$BIN_YQ\" '.allow-lan // false' \"$CLASH_CONFIG_RUNTIME\")\n\n    case $allowLan in\n    true)\n        [ \"$bindAddr\" = \"*\" ] && bindAddr=$(_get_local_ip)\n        ;;\n    false)\n        bindAddr=127.0.0.1\n        ;;\n    esac\n    echo \"$bindAddr\"\n}\n\n_get_local_ip() {\n    local local_ip=$(ip route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i==\"src\") print $(i+1)}')\n    [ -z \"$local_ip\" ] && local_ip=$(hostname -I | awk '{print $1}')\n    echo \"$local_ip\"\n}\n\nfunction _detect_ext_addr() {\n    local ext_addr=$(\"$BIN_YQ\" '.external-controller // \"\"' \"$CLASH_CONFIG_RUNTIME\")\n    local ext_ip=${ext_addr%%:*}\n    EXT_IP=$ext_ip\n    EXT_PORT=${ext_addr##*:}\n    [ \"$ext_ip\" = '0.0.0.0' ] && EXT_IP=$(_get_local_ip)\n    _is_port_used \"$EXT_PORT\" && {\n        curl -s --noproxy \"*\" -H \"Authorization: Bearer $(_get_secret)\" \"127.0.0.1:${EXT_PORT}\" | grep -qs \"${KERNEL_NAME}\" && return 0\n        local newPort=$(_get_random_port)\n        _failcat '🎯' \"端口冲突：[external-controller] ${EXT_PORT} 🎲 随机分配 $newPort\"\n        EXT_PORT=$newPort\n        \"$BIN_YQ\" -i \".external-controller = \\\"$ext_ip:$newPort\\\"\" \"$CLASH_CONFIG_MIXIN\"\n        _merge_config\n    }\n}\n\n_color_log() {\n    local color=\"$1\"\n    local msg=\"$2\"\n\n    local hex=\"${color#\\#}\"\n    local r=$((16#${hex:0:2}))\n    local g=$((16#${hex:2:2}))\n    local b=$((16#${hex:4:2}))\n\n    local color_code=\"\\033[38;2;${r};${g};${b}m\"\n    local reset_code=\"\\033[0m\"\n\n    printf \"%b%s%b\\n\" \"$color_code\" \"$msg\" \"$reset_code\"\n}\n\nfunction _okcat() {\n    local color=#c8d6e5\n    local emoji=😼\n    [ $# -gt 1 ] && emoji=$1 && shift\n    local msg=\"${emoji} $1\"\n    _color_log \"$color\" \"$msg\"\n    return 0\n}\n\nfunction _failcat() {\n    local color=#fd79a8\n    local emoji=😾\n    [ $# -gt 1 ] && emoji=$1 && shift\n    local msg=\"${emoji} $1\"\n    _color_log \"$color\" \"$msg\" >&2\n    return 1\n}\n\nfunction _error_quit() {\n    [ $# -gt 0 ] && {\n        local color=#f92f60\n        local emoji=📢\n        [ $# -gt 1 ] && emoji=$1 && shift\n        local msg=\"${emoji} $1\"\n        _color_log \"$color\" \"$msg\"\n    }\n    exec $SHELL -i\n}\n\nfunction _valid_config() {\n    local config=\"$1\"\n    [[ ! -e \"$config\" || \"$(wc -l <\"$config\")\" -lt 1 ]] && return 1\n\n    local test_cmd test_log\n    test_cmd=(\"$BIN_KERNEL\" -d \"$(dirname \"$config\")\" -f \"$config\" -t)\n    test_log=$(\"${test_cmd[@]}\") || {\n        \"${test_cmd[@]}\"\n        grep -qs \"unsupport proxy type\" <<<\"$test_log\" && {\n            local prefix=\"检测到订阅中包含不受支持的代理协议\"\n            [ \"$KERNEL_NAME\" = \"clash\" ] && _error_quit \"${prefix}, 推荐安装使用 mihomo 内核\"\n            _error_quit \"${prefix}, 请检查并升级内核版本\"\n        }\n    }\n}\n\nfunction _download_config() {\n    local dest=$1\n    local url=$2\n    [ \"${url:0:4}\" = 'file' ] || _okcat '⏳' '正在下载...'\n    _download_raw_config \"$dest\" \"$url\" || return 1\n    _okcat '🍃' '验证订阅配置...'\n    _valid_config \"$dest\" || {\n        _failcat '🍂' \"验证失败：尝试订阅转换...\"\n        cat \"$dest\" >\"${dest}.raw\"\n        _download_convert_config \"$dest\" \"$url\"\n    }\n}\n_download_raw_config() {\n    local dest=$1\n    local url=$2\n\n    curl \\\n        --silent \\\n        --show-error \\\n        --fail \\\n        --insecure \\\n        --location \\\n        --max-time 5 \\\n        --retry 1 \\\n        --user-agent \"$CLASH_SUB_UA\" \\\n        --output \"$dest\" \\\n        \"$url\" ||\n        wget \\\n            --no-verbose \\\n            --no-check-certificate \\\n            --timeout 5 \\\n            --tries 1 \\\n            --user-agent \"$CLASH_SUB_UA\" \\\n            --output-document \"$dest\" \\\n            \"$url\"\n}\n_download_convert_config() {\n    local dest=$1\n    local url=$2\n    local flag\n    [ \"${url:0:4}\" = 'file' ] && return 0\n    _start_convert\n    local convert_url=$(\n        target='clash'\n        base_url=\"http://127.0.0.1:${BIN_SUBCONVERTER_PORT}/sub\"\n        curl \\\n            --get \\\n            --silent \\\n            --show-error \\\n            --location \\\n            --output /dev/null \\\n            --data-urlencode \"target=$target\" \\\n            --data-urlencode \"url=$url\" \\\n            --write-out '%{url_effective}' \\\n            \"$base_url\"\n    )\n    curl --user-agent \"$CLASH_SUB_UA\" --silent --output \"$dest\" \"$convert_url\"\n    flag=$?\n    _stop_convert\n    return $flag\n}\n\n_detect_subconverter_port() {\n    BIN_SUBCONVERTER_PORT=$(\"$BIN_YQ\" '.server.port' \"$BIN_SUBCONVERTER_CONFIG\")\n    _is_port_used \"$BIN_SUBCONVERTER_PORT\" && {\n        local newPort=$(_get_random_port)\n        _failcat '🎯' \"端口冲突：[subconverter] ${BIN_SUBCONVERTER_PORT} 🎲 随机分配：$newPort\"\n        BIN_SUBCONVERTER_PORT=$newPort\n        \"$BIN_YQ\" -i \".server.port = $newPort\" \"$BIN_SUBCONVERTER_CONFIG\" 2>/dev/null\n    }\n}\n\n_start_convert() {\n    _detect_subconverter_port\n    local check_cmd=\"curl http://localhost:${BIN_SUBCONVERTER_PORT}/version\"\n    $check_cmd >&/dev/null && return 0\n    (\"$BIN_SUBCONVERTER_START\" >&\"$BIN_SUBCONVERTER_LOG\" &)\n    local start=$(date +%s)\n    while ! $check_cmd >&/dev/null; do\n        sleep 0.5s\n        local now=$(date +%s)\n        [ $((now - start)) -gt 2 ] && _error_quit \"订阅转换服务未启动，请检查日志：$BIN_SUBCONVERTER_LOG\"\n    done\n}\n_stop_convert() {\n    $BIN_SUBCONVERTER_STOP >/dev/null\n}\n\n_set_env() {\n    local key=$1\n    local value=$2\n    local env_path=\"${CLASH_BASE_DIR}/.env\"\n\n    grep -qE \"^${key}=\" \"$env_path\" && {\n        value=${value//&/\\\\&}\n        sed -i \"s|^${key}=.*|${key}=${value}|\" \"$env_path\"\n        return $?\n    }\n    echo \"${key}=${value}\" >>\"$env_path\"\n}\n"
  },
  {
    "path": "scripts/init/OpenRC.sh",
    "content": "#!/sbin/openrc-run\n\ndescription=\"placeholder_kernel_desc\"\ncommand=\"placeholder_cmd_path\"\ncommand_args=\"placeholder_cmd_args\"\npidfile=\"placeholder_pid_file\"\noutput_log=\"placeholder_log_file\"\nerror_log=\"placeholder_log_file\"\ncommand_background=true"
  },
  {
    "path": "scripts/init/SysVinit.sh",
    "content": "### BEGIN INIT INFO\n# Provides: placeholder_kernel_name\n# Required-Start: $network $local_fs $remote_fs\n# Required-Stop: $network $local_fs $remote_fs\n# Default-Start: 2 3 4 5\n# Default-Stop: 0 1 6\n# Short-Description: placeholder_kernel_desc\n# Description: placeholder_kernel_desc\n### END INIT INFO\n\npidfile=\"placeholder_pid_file\"\nlogfile=\"placeholder_log_file\"\ncmd=\"placeholder_cmd_full\"\n\ncase \"$1\" in\nstart)\n  $0 status >&/dev/null && exit 0\n  $cmd >&$logfile &\n  echo $! >$pidfile\n  ;;\nstop)\n  pid=$(cat $pidfile 2>/dev/null)\n  [ -n \"$pid\" ] && kill -9 \"$pid\"\n  rm -f $pidfile\n  ;;\nrestart | reload)\n  $0 stop\n  sleep 0.5\n  $0 start\n  ;;\nstatus)\n  pid=$(cat $pidfile 2>/dev/null)\n  isStart=$(ps ax | awk '{ print $1 }' | grep -e \"^${pid}$\")\n  [ -n \"$isStart\" ] && {\n    echo \"placeholder_kernel_name is running with PID: $pid\"\n    exit 0\n  }\n  echo \"placeholder_kernel_name is not running.\"\n  exit 1\n  ;;\n*)\n  echo \"Usage: $0 {start|stop|restart|status}\"\n  ;;\nesac\n"
  },
  {
    "path": "scripts/init/runit.sh",
    "content": "#!/bin/sh\nexec placeholder_cmd_full >placeholder_log_file 2>&1"
  },
  {
    "path": "scripts/init/systemd.sh",
    "content": "[Unit]\nDescription=placeholder_kernel_desc\nAfter=network.target NetworkManager.service systemd-networkd.service iwd.service\n\n[Service]\nType=simple\nLimitNPROC=500\nLimitNOFILE=1000000\nCapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE\nAmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE\nRestart=always\nExecStartPre=/usr/bin/sleep 1s\nExecStart=placeholder_cmd_full\nExecReload=/bin/kill -HUP $MAINPID\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "scripts/preflight.sh",
    "content": "#!/usr/bin/env bash\n\nRESOURCES_BASE_DIR=\".${CLASH_RESOURCES_DIR#\"$CLASH_BASE_DIR\"}\"\n\nZIP_BASE_DIR=\".${CLASH_RESOURCES_DIR#\"$CLASH_BASE_DIR\"}/zip\"\n\nSCRIPT_BASE_DIR='scripts'\nSCRIPT_INIT_DIR=\"${SCRIPT_BASE_DIR}/init\"\nSCRIPT_CMD_DIR=\"${SCRIPT_BASE_DIR}/cmd\"\nSCRIPT_CMD_FISH=\"${SCRIPT_CMD_DIR}/clashctl.fish\"\n\nCLASH_CMD_DIR=\"${CLASH_BASE_DIR}/$SCRIPT_CMD_DIR\"\n\nFILE_LOG=\"/var/log/${KERNEL_NAME}.log\"\nFILE_PID=\"/run/${KERNEL_NAME}.pid\"\n\n_valid_required() {\n    local required_cmds=(\"xz\" \"pgrep\" \"curl\" \"tar\" 'unzip')\n    local missing=()\n    for cmd in \"${required_cmds[@]}\"; do\n        command -v \"$cmd\" >&/dev/null || missing+=(\"$cmd\")\n    done\n    [ \"${#missing[@]}\" -gt 0 ] && _error_quit \"请先安装以下命令：${missing[*]}\"\n}\n\n_valid() {\n    _valid_required\n\n    [ -d \"$CLASH_BASE_DIR\" ] && _error_quit \"请先执行卸载脚本,以清除安装路径：$CLASH_BASE_DIR\"\n\n    local msg=\"${CLASH_BASE_DIR}：当前路径不可用，请在 .env 中更换安装路径。\"\n    mkdir -p \"$CLASH_BASE_DIR\" || _error_quit \"$msg\"\n    _is_regular_sudo && [[ $CLASH_BASE_DIR == /root* ]] && _error_quit \"$msg\"\n\n    [ -z \"$ZSH_VERSION\" ] && [ -z \"$BASH_VERSION\" ] && _error_quit \"仅支持：bash、zsh 执行\"\n}\n\n_parse_args() {\n    for arg in \"$@\"; do\n        case $arg in\n        mihomo)\n            KERNEL_NAME=mihomo\n            ;;\n        clash)\n            KERNEL_NAME=clash\n            ;;\n        http*)\n            CLASH_CONFIG_URL=$arg\n            ;;\n        esac\n    done\n}\n\n_prepare_zip() {\n    _load_zip >&/dev/null\n    local required_zips=()\n    case \"${KERNEL_NAME}\" in\n    clash)\n        [ ! -f \"$ZIP_CLASH\" ] && required_zips+=(\"clash\")\n        ;;\n    mihomo | *)\n        [ ! -f \"$ZIP_MIHOMO\" ] && required_zips+=(\"mihomo\")\n        ;;\n    esac\n    [ ! -f \"$ZIP_YQ\" ] && required_zips+=(\"yq\")\n    [ ! -f \"$ZIP_SUBCONVERTER\" ] && required_zips+=(\"subconverter\")\n\n    _download_zip \"${required_zips[@]}\"\n\n    case \"${KERNEL_NAME}\" in\n    clash)\n        ZIP_KERNEL=\"$ZIP_CLASH\"\n        ;;\n    mihomo | *)\n        ZIP_KERNEL=\"$ZIP_MIHOMO\"\n        ;;\n    esac\n    BIN_KERNEL=\"${BIN_BASE_DIR}/$KERNEL_NAME\"\n    _unzip_zip\n}\n_load_zip() {\n    ZIP_CLASH=$(echo \"${ZIP_BASE_DIR}\"/clash*)\n    ZIP_MIHOMO=$(echo \"${ZIP_BASE_DIR}\"/mihomo*)\n    ZIP_YQ=$(echo \"${ZIP_BASE_DIR}\"/yq*)\n    ZIP_SUBCONVERTER=$(echo \"${ZIP_BASE_DIR}\"/subconverter*)\n}\n_download_zip() {\n    (($#)) || return 0\n    local url_clash url_mihomo url_yq url_subconverter\n    local arch=$(uname -m)\n    case \"$arch\" in\n    x86_64)\n        local flags=$(grep -m1 '^flags' /proc/cpuinfo)\n        local level=v1\n        grep -qw sse4_2 <<<\"$flags\" && grep -qw popcnt <<<\"$flags\" && level=v2\n        grep -qw avx2 <<<\"$flags\" && grep -qw fma <<<\"$flags\" && level=v3\n        VERSION_MIHOMO=${level}-$VERSION_MIHOMO\n\n        url_clash=https://downloads.clash.wiki/ClashPremium/clash-linux-amd64-2023.08.17.gz\n        url_mihomo=https://github.com/MetaCubeX/mihomo/releases/download/${VERSION_MIHOMO##*-}/mihomo-linux-amd64-${VERSION_MIHOMO}.gz\n        url_yq=https://github.com/mikefarah/yq/releases/download/${VERSION_YQ}/yq_linux_amd64.tar.gz\n        url_subconverter=https://github.com/tindy2013/subconverter/releases/download/${VERSION_SUBCONVERTER}/subconverter_linux64.tar.gz\n        ;;\n    *86*)\n        url_clash=https://downloads.clash.wiki/ClashPremium/clash-linux-386-2023.08.17.gz\n        url_mihomo=https://github.com/MetaCubeX/mihomo/releases/download/${VERSION_MIHOMO##*-}/mihomo-linux-386-${VERSION_MIHOMO}.gz\n        url_yq=https://github.com/mikefarah/yq/releases/download/${VERSION_YQ}/yq_linux_386.tar.gz\n        url_subconverter=https://github.com/tindy2013/subconverter/releases/download/${VERSION_SUBCONVERTER}/subconverter_linux32.tar.gz\n        ;;\n    armv*)\n        url_clash=https://downloads.clash.wiki/ClashPremium/clash-linux-armv5-2023.08.17.gz\n        url_mihomo=https://github.com/MetaCubeX/mihomo/releases/download/${VERSION_MIHOMO##*-}/mihomo-linux-armv7-${VERSION_MIHOMO}.gz\n        url_yq=https://github.com/mikefarah/yq/releases/download/${VERSION_YQ}/yq_linux_arm.tar.gz\n        url_subconverter=https://github.com/tindy2013/subconverter/releases/download/${VERSION_SUBCONVERTER}/subconverter_armv7.tar.gz\n        ;;\n    aarch64)\n        url_clash=https://downloads.clash.wiki/ClashPremium/clash-linux-arm64-2023.08.17.gz\n        url_mihomo=https://github.com/MetaCubeX/mihomo/releases/download/${VERSION_MIHOMO##*-}/mihomo-linux-arm64-${VERSION_MIHOMO}.gz\n        url_yq=https://github.com/mikefarah/yq/releases/download/${VERSION_YQ}/yq_linux_arm64.tar.gz\n        url_subconverter=https://github.com/tindy2013/subconverter/releases/download/${VERSION_SUBCONVERTER}/subconverter_aarch64.tar.gz\n        ;;\n    *)\n        _error_quit \"未知的架构版本：$arch，请自行下载对应版本至 ${ZIP_BASE_DIR} 目录\"\n        ;;\n    esac\n\n    local -A urls=(\n        [clash]=\"$url_clash\"\n        [mihomo]=\"$url_mihomo\"\n        [yq]=\"$url_yq\"\n        [subconverter]=\"$url_subconverter\"\n    )\n\n    local item target_zips=()\n    _okcat '🖥️ ' \"系统架构：$arch $level\"\n    for item in \"$@\"; do\n        local url=\"${urls[$item]}\"\n        local proxy_url=\"${URL_GH_PROXY:+${URL_GH_PROXY%/}/}${url}\"\n        [ \"$item\" != 'clash' ] && url=\"$proxy_url\"\n        _okcat '⏳' \"正在下载：${item}：$url\"\n        local target=\"${ZIP_BASE_DIR}/$(basename \"$url\")\"\n        curl \\\n            --progress-bar \\\n            --show-error \\\n            --fail \\\n            --insecure \\\n            --location \\\n            --retry 1 \\\n            --output \"$target\" \\\n            \"$url\"\n        target_zips+=(\"$target\")\n    done\n    _valid_zip \"${target_zips[@]}\"\n    _load_zip >&/dev/null\n}\n_valid_zip() {\n    (($#)) || return 1\n    local zip fail_zips=()\n    for zip in \"$@\"; do\n        gzip -tq \"$zip\" || unzip -tqq \"$zip\" || fail_zips+=(\"$zip\")\n    done\n\n    ((${#fail_zips[@]})) && _error_quit \"文件验证失败：${fail_zips[*]} 请删除后重试，或自行下载对应版本至 ${ZIP_BASE_DIR} 目录\"\n}\n_unzip_zip() {\n    _valid_zip \"$ZIP_KERNEL\" \"$ZIP_YQ\" \"$ZIP_SUBCONVERTER\" \"$ZIP_UI\"\n    /usr/bin/install -D <(gzip -dc \"$ZIP_KERNEL\") \"$BIN_KERNEL\"\n    tar -xf \"$ZIP_YQ\" -C \"${BIN_BASE_DIR}\"\n    /bin/mv -f \"${BIN_BASE_DIR}\"/yq_* \"${BIN_BASE_DIR}/yq\"\n    tar -xf \"$ZIP_SUBCONVERTER\" -C \"$BIN_BASE_DIR\"\n    /bin/cp \"$BIN_SUBCONVERTER_DIR/pref.example.yml\" \"$BIN_SUBCONVERTER_CONFIG\"\n    unzip -oqq \"$ZIP_UI\" -d \"$RESOURCES_BASE_DIR\" 2>/dev/null || tar -xf \"$ZIP_UI\" -C \"$RESOURCES_BASE_DIR\"\n}\n\n# shellcheck disable=SC2206\n_detect_init() {\n    [ -z \"$INIT_TYPE\" ] && INIT_TYPE=$(readlink /proc/1/exe)\n    grep -qsE \"docker|kubepods|containerd|podman|lxc\" /proc/1/cgroup && INIT_TYPE='nohup'\n    _is_root || {\n        INIT_TYPE='nohup'\n        FILE_LOG=\"${CLASH_RESOURCES_DIR}/${KERNEL_NAME}.log\"\n        FILE_PID=\"${CLASH_RESOURCES_DIR}/${KERNEL_NAME}.pid\"\n    }\n\n    service_log=(less '<' $FILE_LOG)\n    service_follow_log=(tail -f -n 0 $FILE_LOG)\n    service_watch_proxy=(clashon)\n    _is_regular_sudo && {\n        service_watch_proxy=(_failcat \"'未检测到代理变量，可执行 clashon 开启代理环境'\")\n        _SUDO=sudo\n    }\n\n    case \"${INIT_TYPE}\" in\n    *systemd)\n        service_log=($_SUDO journalctl -u \"$KERNEL_NAME\")\n        service_follow_log=(\"${service_log[@]}\" -q -f -n 0)\n        _systemd\n        ;;\n    *init)\n        _sysvinit\n        ;;\n    *busybox)\n        command -v openrc-init >&/dev/null && _openrc\n        ;;\n    *openrc*)\n        _openrc\n        ;;\n    *runit)\n        _runit\n        ;;\n    nohup | *)\n        INIT_TYPE='nohup'\n        _nohup\n        ;;\n    esac\n    INIT_TYPE=$(basename \"$INIT_TYPE\")\n}\n_openrc() {\n    service_src=\"${SCRIPT_INIT_DIR}/OpenRC.sh\"\n    service_target=\"/etc/init.d/$KERNEL_NAME\"\n\n    service_enable=(rc-update add \"$KERNEL_NAME\" default)\n    service_disable=(rc-update del \"$KERNEL_NAME\" default)\n\n    service_start=(rc-service \"$KERNEL_NAME\" start)\n    service_stop=(rc-service \"$KERNEL_NAME\" stop)\n    service_restart=(rc-service \"$KERNEL_NAME\" restart)\n    service_status=(rc-service \"$KERNEL_NAME\" status)\n    service_is_active=(rc-service \"$KERNEL_NAME\" status)\n}\n_runit() {\n    service_src=\"${SCRIPT_INIT_DIR}/runit.sh\"\n    service_target=\"/etc/sv/${KERNEL_NAME}/run\"\n    service_del=(rm -rf \"/etc/sv/${KERNEL_NAME:-mihomo}\")\n\n    service_reload=(sleep 2)\n    service_enable=(ln -s \"$(dirname \"$service_target\")\" \"/etc/runit/runsvdir/default/${KERNEL_NAME}\")\n    service_disable=(rm -f \"/etc/runit/runsvdir/current/${KERNEL_NAME}\")\n\n    service_start=(sv up \"$KERNEL_NAME\")\n    service_stop=(sv down \"$KERNEL_NAME\")\n    service_restart=(sv restart \"$KERNEL_NAME\")\n    service_status=(sv status \"$KERNEL_NAME\")\n    service_is_active=(sv status \"$KERNEL_NAME\" \\| grep -qs '^run')\n}\n_sysvinit() {\n    service_src=\"${SCRIPT_INIT_DIR}/SysVinit.sh\"\n    service_target=\"/etc/init.d/$KERNEL_NAME\"\n\n    command -v chkconfig >&/dev/null && {\n        service_add=(chkconfig --add \"$KERNEL_NAME\")\n        service_del=(chkconfig --del \"$KERNEL_NAME\")\n\n        service_enable=(chkconfig \"$KERNEL_NAME\" on)\n        service_disable=(chkconfig \"$KERNEL_NAME\" off)\n    }\n    command -v update-rc.d >&/dev/null && {\n        service_add=(update-rc.d \"$KERNEL_NAME\" defaults)\n        service_del=(update-rc.d \"$KERNEL_NAME\" remove)\n\n        service_enable=(update-rc.d \"$KERNEL_NAME\" enable)\n        service_disable=(update-rc.d \"$KERNEL_NAME\" disable)\n    }\n\n    service_start=(service \"$KERNEL_NAME\" start)\n    service_stop=(service \"$KERNEL_NAME\" stop)\n    service_restart=(service \"$KERNEL_NAME\" restart)\n    service_status=(service \"$KERNEL_NAME\" status)\n    service_is_active=(service \"$KERNEL_NAME\" status)\n}\n# shellcheck disable=SC2206\n_systemd() {\n    service_src=\"${SCRIPT_INIT_DIR}/systemd.sh\"\n    service_target=\"/etc/systemd/system/${KERNEL_NAME}.service\"\n\n    service_reload=($_SUDO systemctl daemon-reload)\n\n    service_enable=($_SUDO systemctl enable \"$KERNEL_NAME\")\n    service_disable=($_SUDO systemctl disable \"$KERNEL_NAME\")\n\n    service_start=($_SUDO systemctl start \"$KERNEL_NAME\")\n    service_stop=($_SUDO systemctl stop \"$KERNEL_NAME\")\n    service_restart=($_SUDO systemctl restart \"$KERNEL_NAME\")\n    service_status=($_SUDO systemctl status \"$KERNEL_NAME\")\n    service_is_active=($_SUDO systemctl is-active \"$KERNEL_NAME\")\n}\n_nohup() {\n    service_enable=(false)\n    service_disable=(false)\n\n    service_start=('(' nohup \"$BIN_KERNEL\" -d \"$CLASH_RESOURCES_DIR\" -f \"$CLASH_CONFIG_RUNTIME\" '>\\&' \"$FILE_LOG\" '\\&' ')')\n    service_sudo_start=(sudo nohup \"$BIN_KERNEL\" -d \"$CLASH_RESOURCES_DIR\" -f \"$CLASH_CONFIG_RUNTIME\" '>\\&' \"$FILE_LOG\" '\\&')\n    service_status=(pgrep -fa \"$BIN_KERNEL\")\n    service_is_active=(pgrep -fa \"$BIN_KERNEL\")\n    service_stop=(pkill -9 -f \"$BIN_KERNEL\")\n}\n\n_install_service() {\n    local kernel_desc=\"$KERNEL_NAME Daemon, A[nother] Clash Kernel.\"\n\n    local cmd_path=\"${BIN_KERNEL}\"\n    local cmd_arg=\"-d ${CLASH_RESOURCES_DIR} -f ${CLASH_CONFIG_RUNTIME}\"\n    local cmd_full=\"${BIN_KERNEL} -d ${CLASH_RESOURCES_DIR} -f ${CLASH_CONFIG_RUNTIME}\"\n\n    [ -n \"$service_src\" ] && {\n        /usr/bin/install -D -m +x \"$service_src\" \"$service_target\"\n        ((${#service_add[@]})) && \"${service_add[@]}\"\n        sed -i \\\n            -e \"s#placeholder_cmd_path#$cmd_path#g\" \\\n            -e \"s#placeholder_cmd_args#$cmd_arg#g\" \\\n            -e \"s#placeholder_cmd_full#$cmd_full#g\" \\\n            -e \"s#placeholder_log_file#$FILE_LOG#g\" \\\n            -e \"s#placeholder_pid_file#$FILE_PID#g\" \\\n            -e \"s#placeholder_kernel_name#$KERNEL_NAME#g\" \\\n            -e \"s#placeholder_kernel_desc#$kernel_desc#g\" \\\n            \"$service_target\"\n    }\n    [ \"$INIT_TYPE\" != \"nohup\" ] && service_sudo_start=(\"${service_start[@]}\")\n    sed -i \\\n        -e \"s#placeholder_start#${service_start[*]}#g\" \\\n        -e \"s#placeholder_sudo_start#${service_sudo_start[*]}#g\" \\\n        -e \"s#placeholder_status#${service_status[*]}#g\" \\\n        -e \"s#placeholder_is_active#${service_is_active[*]}#g\" \\\n        -e \"s#placeholder_stop#${service_stop[*]}#g\" \\\n        -e \"s#placeholder_log#${service_log[*]}#g\" \\\n        -e \"s#placeholder_follow_log#${service_follow_log[*]}#g\" \\\n        -e \"s#placeholder_watch_proxy#${service_watch_proxy[*]}#g\" \\\n        \"$CLASH_CMD_DIR/clashctl.sh\" \"$CLASH_CMD_DIR/common.sh\"\n\n    \"${service_enable[@]}\" >&/dev/null && _okcat '🚀' '已设置开机自启'\n    ((${#service_reload[@]})) && \"${service_reload[@]}\"\n}\n_uninstall_service() {\n    _detect_init\n    \"${service_disable[@]}\" >&/dev/null\n    ((${#service_del[@]})) && \"${service_del[@]}\"\n    rm -f \"$service_target\"\n    ((${#service_reload[@]})) && \"${service_reload[@]}\"\n}\n\n_detect_rc() {\n    local home=$HOME\n    _is_regular_sudo && home=$(awk -F: -v user=\"$SUDO_USER\" '$1==user{print $6}' /etc/passwd)\n\n    command -v bash >&/dev/null && {\n        SHELL_RC_BASH=\"${home}/.bashrc\"\n    }\n    command -v zsh >&/dev/null && {\n        SHELL_RC_ZSH=\"${home}/.zshrc\"\n    }\n    command -v fish >&/dev/null && {\n        SHELL_RC_FISH=\"${home}/.config/fish/conf.d/clashctl.fish\"\n    }\n    start_flag=\"# clashctl START\"\n    end_flag=\"# clashctl END\"\n}\n_apply_rc() {\n    _detect_rc\n    local source_clashctl=\". $CLASH_CMD_DIR/clashctl.sh\"\n    # shellcheck disable=SC2086\n    tee -a \"$SHELL_RC_BASH\" $SHELL_RC_ZSH >/dev/null <<EOF\n\n$start_flag\n# 加载 clashctl 命令\n$source_clashctl\n# 自动开启代理环境\nwatch_proxy\n$end_flag\nEOF\n    [ -n \"$SHELL_RC_FISH\" ] && /usr/bin/install \"$SCRIPT_CMD_FISH\" \"$SHELL_RC_FISH\"\n    $source_clashctl\n}\n_revoke_rc() {\n    _detect_rc\n    sed -i --follow-symlinks \"/$start_flag/,/$end_flag/d\" \"$SHELL_RC_BASH\" \"$SHELL_RC_ZSH\" 2>/dev/null\n    [ -n \"$SHELL_RC_FISH\" ] && rm -f \"$SHELL_RC_FISH\" 2>/dev/null\n}\n\n_set_envs() {\n    _set_env INIT_TYPE \"$INIT_TYPE\"\n    _set_env KERNEL_NAME \"$KERNEL_NAME\"\n    _set_env CLASH_BASE_DIR \"$CLASH_BASE_DIR\"\n    _set_env VERSION_MIHOMO \"$VERSION_MIHOMO\"\n}\n\n_get_random_val() {\n    cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 6\n}\n\n_is_regular_sudo() {\n    _is_root && [ -n \"$SUDO_USER\" ] && [ \"$SUDO_USER\" != 'root' ]\n}\n_is_root() {\n    [ \"$(id -u)\" -eq 0 ]\n}\n\n_quit() {\n    _is_regular_sudo && exec su \"$SUDO_USER\"\n    exec \"$SHELL\" -i -c \"$*\"\n}\n"
  },
  {
    "path": "uninstall.sh",
    "content": "#!/usr/bin/env bash\n. .env\n. \"$CLASH_BASE_DIR/scripts/cmd/clashctl.sh\" 2>/dev/null\n. scripts/preflight.sh\n\npgrep -f \"$BIN_KERNEL\" -u 0 >/dev/null && ! _is_root && _error_quit \"请先关闭 Tun 模式\"\nclashoff 2>/dev/null\n_uninstall_service\n_revoke_rc\n\ncommand -v crontab >&/dev/null && crontab -l | grep -v \"clashsub\" | crontab -\n\n/usr/bin/rm -rf \"$CLASH_BASE_DIR\"\n\necho '✨' '已卸载，相关配置已清除'\n_quit\n"
  }
]