From c1a4b6641681663d1e2d9c3b8e1edcebbaf9c1e1 Mon Sep 17 00:00:00 2001 From: lukeopen Date: Sun, 2 Aug 2026 22:25:04 +0800 Subject: [PATCH] Initial commit --- .gitignore | 14 ++ README.md | 39 +++ clash | 684 +++++++++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 89 +++++++ 4 files changed, 826 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 clash create mode 100644 install.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93e80b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# 配置文件(从 ~/Mihomo/ 自动获取,不同电脑不同) +config.yaml + +# 运行时数据 +*.pid +*.log +cache.db + +# 代理节点缓存(含订阅链接和节点信息) +proxy_provider/ +rules/ + +# macOS 系统文件 +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..3b1da89 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# Clash — macOS 代理管理脚本 + +一份 YAML 配置同时在 OpenWRT 和 macOS 上使用,启动时自动适配 macOS。 + +## 使用 + +| 命令 | 说明 | +|------|------| +| `clash` | 交互菜单 | +| `clash start` | 启动 | +| `clash stop` | 停止 | +| `clash restart` | 重启 | +| `clash reload` | 热重载 | +| `clash status` | 状态 | +| `clash link` | 源文件路径 | +| `clash cleanup` | 网络修复 | + +## 目录 + +``` +Clash/ +├── clash # 管理脚本 +└── install.sh # 部署脚本 + +~/Mihomo/ ← 放 YAML 配置文件 +``` + +## 新电脑部署 + +```bash +git clone git@github.com:lukeopen/Clash.git +cd Clash && zsh install.sh +cp /path/to/config.yaml ~/Mihomo/ +clash start +``` + +## 依赖 + +- Homebrew + `brew install mihomo` diff --git a/clash b/clash new file mode 100755 index 0000000..1fd7926 --- /dev/null +++ b/clash @@ -0,0 +1,684 @@ +#!/bin/zsh +# ═══════════════════════════════════════════════════════════ +# ███╗ ███╗██╗██╗ ██╗ ██████╗ ███╗ ███╗ ██████╗ +# ████╗ ████║██║██║ ██║██╔═══██╗████╗ ████║██╔═══██╗ +# ██╔████╔██║██║███████║██║ ██║██╔████╔██║██║ ██║ +# ██║╚██╔╝██║██║██╔══██║██║ ██║██║╚██╔╝██║██║ ██║ +# ██║ ╚═╝ ██║██║██║ ██║╚██████╔╝██║ ╚═╝ ██║╚██████╔╝ +# ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ +# +# Mihomo Kernel Manager ─ macOS 终端管理脚本 +# 无参数进入交互菜单 | clash start/stop/status ... +# ═══════════════════════════════════════════════════════════ + +CONFIG_DIR="${HOME}/.config/mihomo" +CONFIG_FILE="${CONFIG_DIR}/config.yaml" +PID_FILE="${CONFIG_DIR}/mihomo.pid" +LOG_FILE="${CONFIG_DIR}/mihomo.log" +LINK_FILE="${CONFIG_DIR}/source.txt" # 记录源 YAML 路径 +MIXED_PORT="7890" +API_PORT="9090" +MIHOMO_BIN="/opt/homebrew/bin/mihomo" + +# ========== 源配置目录(自动检测最新的 YAML)========== +# 把你的 YAML 文件丢到这个目录下,启动时自动找最新的那个 +# 可通过 `clash link /path/to/file.yaml` 固定指向某个文件 +SOURCE_DIR="${HOME}/Mihomo" +SOURCE_CONFIG="" # 由 auto_detect_source 自动填充 + +[[ -d "$CONFIG_DIR" ]] || mkdir -p "$CONFIG_DIR" + +# ========== 颜色 ========== +BOLD='\033[1m' +DIM='\033[2m' +GREEN='\033[0;32m' +BOLD_GREEN='\033[1;32m' +RED='\033[0;31m' +BOLD_RED='\033[1;31m' +YELLOW='\033[0;33m' +BOLD_YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD_BLUE='\033[1;34m' +PURPLE='\033[0;35m' +BOLD_PURPLE='\033[1;35m' +CYAN='\033[0;36m' +BOLD_CYAN='\033[1;36m' +WHITE='\033[1;37m' +NC='\033[0m' # No Color + +# ─── 辅助符号 ─── +ICON_OK="${GREEN}✓${NC}" +ICON_FAIL="${RED}✗${NC}" +ICON_WARN="${YELLOW}⚠${NC}" +ICON_INFO="${BLUE}ℹ${NC}" +ICON_STOP="${RED}■${NC}" +ICON_START="${GREEN}▶${NC}" +SEP="${DIM}──────────────────────────────────────────${NC}" + +# ========== 自动检测源文件 ========== +# 优先级:link > SOURCE_DIR 下最新的 .yaml > 报错 +auto_detect_source() { + # 如果用户通过 clash link 固定了路径 + if [[ -f "$LINK_FILE" ]]; then + local linked=$(cat "$LINK_FILE") + if [[ -f "$linked" ]]; then + echo "$linked" + return 0 + fi + echo "${YELLOW}[!] 链接文件不存在: $linked,尝试自动检测...${NC}" >&2 + fi + + # 从 SOURCE_DIR 找最新的 .yaml 文件 + if [[ -d "$SOURCE_DIR" ]]; then + local latest=$(ls -t "$SOURCE_DIR"/*.yaml(N) "$SOURCE_DIR"/*.yml(N) 2>/dev/null | head -1) + if [[ -n "$latest" && -f "$latest" ]]; then + echo "$latest" + return 0 + fi + fi + + return 1 +} + +# ========== macOS 适配:不修改源文件,复制并做兼容转换 ========== +# 在 OpenWRT/Nikki 上直接使用原始文件,macOS 上启动前调用此函数 +macos_adapt_config() { + local src="${1:-}" + + # 没传参 → 自动检测 + if [[ -z "$src" ]]; then + src=$(auto_detect_source) + fi + + if [[ -z "$src" || ! -f "$src" ]]; then + echo "${RED}[!] 未找到 YAML 配置文件${NC}" + echo " 请把 .yaml 文件放到 ${CYAN}$SOURCE_DIR${NC} 目录下" + echo " 或用 ${CYAN}clash link /path/to/file.yaml${NC} 指定" + return 1 + fi + + echo "" + echo -n " ${ICON_INFO} 正在适配 macOS ... " + + # 复制源文件(不修改原件) + cp "$src" "$CONFIG_FILE" + + # ---- macOS 兼容性修复 ---- + # 1) device: nikki → OpenWRT 专用,macOS 不支持 + sed -i '' '/^[[:space:]]*device: nikki[[:space:]]*$/d' "$CONFIG_FILE" + sed -i '' '/^[[:space:]]*device: nikki$/d' "$CONFIG_FILE" + + # 2) tun.stack: system → macOS 推荐用 gvisor + sed -i '' 's/^\([[:space:]]*stack:\) system/\1 gvisor/' "$CONFIG_FILE" + + # 3) auto-redirect: true → macOS 不支持 + sed -i '' '/^[[:space:]]*auto-redirect: true/d' "$CONFIG_FILE" + + # 4) dns-hijack 中的 tcp:// / udp:// 前缀 → macOS mihomo 不支持 + sed -i '' 's/ - tcp:\/\// - /g' "$CONFIG_FILE" + sed -i '' 's/ - udp:\/\// - /g' "$CONFIG_FILE" + + echo "${BOLD_GREEN}完成${NC}" + echo " $SEP" + printf " ${DIM}%-12s${NC} ${CYAN}%s${NC}\n" "源文件" "$src" + printf " ${DIM}%-12s${NC} ${CYAN}%s${NC}\n" "适配后" "$CONFIG_FILE" + echo " $SEP" + return 0 +} + +# ========== 状态指示 ========== +is_running() { + [[ -f "$PID_FILE" ]] && pgrep -F "$PID_FILE" &>/dev/null +} + +status_tag() { + if is_running; then + echo "${GREEN}● 运行中${NC} (pid: $(cat $PID_FILE))" + else + echo "${RED}○ 未运行${NC}" + fi +} + +# ========== 启动(自动适配 macOS)========== +clash_start() { + local src="${1:-}" + local do_adapt=true + + # 如果传了参数且文件已存在 CONFIG_DIR 下,说明是已适配过的配置,不再重复适配 + if [[ -n "$src" ]]; then + if [[ "$src" == "$CONFIG_FILE" ]] || [[ "$src" == "$(cat "$LINK_FILE" 2>/dev/null)" ]]; then + do_adapt=false + fi + fi + + # 无参数:自动检测源文件 + if [[ -z "$src" ]]; then + src=$(auto_detect_source) + fi + + # 如果源文件和当前配置不同,则做适配 + if $do_adapt && [[ "$src" != "$CONFIG_FILE" ]]; then + macos_adapt_config "$src" || return 1 + elif [[ ! -f "$CONFIG_FILE" ]]; then + echo "${RED}[!] 配置文件不存在: $CONFIG_FILE${NC}" + echo " 试试: clash link /path/to/your.yaml" + return 1 + fi + + if is_running; then + echo "${YELLOW}[!] mihomo 已经在运行了${NC}" + return 0 + fi + + echo "" + echo -n " ${ICON_START} 启动中 ... " + nohup sudo "$MIHOMO_BIN" -d "$CONFIG_DIR" -f "$CONFIG_FILE" > "$LOG_FILE" 2>&1 & + echo $! > "$PID_FILE" + sleep 1 + if is_running; then + echo "${BOLD_GREEN}已就绪${NC}" + echo "" + echo " $SEP" + printf " ${DIM}%-14s${NC} ${CYAN}127.0.0.1:${MIXED_PORT}${NC} ${DIM}(HTTP/SOCKS5)${NC}\n" "代理端口" + printf " ${DIM}%-14s${NC} ${CYAN}127.0.0.1:${API_PORT}${NC}\n" "API 控制" + printf " ${DIM}%-14s${NC} ${CYAN}http://127.0.0.1:${API_PORT}/ui${NC}\n" "管理面板" + echo " $SEP" + echo "" + else + echo "${BOLD_RED}失败${NC}" + echo " ${DIM}查看日志: tail $LOG_FILE${NC}" + rm -f "$PID_FILE" + return 1 + fi +} + +# ========== 网络健康检查 ========== +network_health_check() { + local issues=0 + echo " ${BOLD}── 网络诊断 ──${NC}" + + # DNS 解析 + echo -n " ${DIM}│${NC} DNS ${DIM}........${NC} " + if nslookup baidu.com >/dev/null 2>&1; then + echo "${ICON_OK} ${DIM}baidu.com${NC}" + else + echo "${ICON_FAIL} ${DIM}DNS 解析失败${NC}" + issues=1 + fi + + # 外网连通 + echo -n " ${DIM}│${NC} 外网 ${DIM}.......${NC} " + if ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then + echo "${ICON_OK} ${DIM}8.8.8.8${NC}" + else + echo "${ICON_FAIL} ${DIM}外网不可达${NC}" + issues=1 + fi + + # TUN 残留检查(macOS 正常有 utun0~utun6 左右) + local tun_count=$(ifconfig -l 2>/dev/null | tr ' ' '\n' | grep -c "^utun") + local tun_high=0 + for dev in $(ifconfig -l 2>/dev/null); do + [[ "$dev" != utun* ]] && continue + local idx="${dev#utun}" + [[ "$idx" != <-> ]] && continue + (( idx >= 1024 )) && (( tun_high++ )) + done + echo -n " ${DIM}│${NC} TUN ${DIM}........${NC} " + if [[ $tun_high -eq 0 ]]; then + echo "${ICON_OK} ${DIM}${tun_count} 个(正常)${NC}" + else + echo "${ICON_WARN} ${DIM}${tun_count} 个(${tun_high} 个高编号残留)${NC}" + issues=1 + fi + + echo " ${DIM}└${NC}" + if [[ $issues -eq 1 ]]; then + echo " ${YELLOW}${ICON_WARN} 网络可能有异常,试试: clash cleanup${NC}" + else + echo " ${ICON_OK} ${GREEN}网络一切正常${NC}" + fi +} + +# ========== 网络恢复(TUN/路由/DNS/系统代理全面清理)========== +network_restore() { + local verbose=${1:-true} + + $verbose && echo "" + $verbose && echo " ${BOLD}── 网络恢复 ──${NC}" + + # 1. 杀掉所有 mihomo 残留进程 + local leftovers=$(pgrep -l mihomo 2>/dev/null) + if [[ -n "$leftovers" ]]; then + $verbose && echo " ${ICON_WARN} 发现残留进程,正在清理..." + sudo pkill -9 mihomo 2>/dev/null + rm -f "$PID_FILE" + fi + + # 2. 查找并清理 mihomo 创建的高编号 TUN 设备(如 utun1024+) + # 系统默认 utun0~utun6,高编号的通常是 mihomo/VPN 所创 + local tun_list=$(ifconfig -l 2>/dev/null) + for dev in ${(s: :)tun_list}; do + [[ "$dev" != utun* ]] && continue + local idx="${dev#utun}" + # 确保是纯数字 + [[ "$idx" != <-> ]] && continue + # 只处理高编号(>=1024)的 utun 设备 + if (( idx >= 1024 )); then + $verbose && echo -n " ${ICON_INFO} 清理 TUN: $dev ... " + sudo ifconfig "$dev" down 2>/dev/null + sudo ifconfig "$dev" destroy 2>/dev/null + $verbose && echo "已移除" + fi + done + + # 3. 关闭系统 HTTP/SOCKS 代理(如果指向 127.0.0.1:7890) + local iface="" + for candidate in $(networksetup -listallnetworkservices 2>/dev/null | tail -n +2); do + local clean=$(echo "$candidate" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + local proxy_server=$(networksetup -getwebproxy "$clean" 2>/dev/null | grep "^Server:" | awk '{print $2}') + local proxy_port=$(networksetup -getwebproxy "$clean" 2>/dev/null | grep "^Port:" | awk '{print $2}') + if [[ "$proxy_server" == "127.0.0.1" && "$proxy_port" == "$MIXED_PORT" ]]; then + iface="$clean" + break + fi + done + if [[ -n "$iface" ]]; then + $verbose && echo -n " ${ICON_INFO} 关闭系统代理 ($iface) ... " + sudo networksetup -setwebproxystate "$iface" off 2>/dev/null + sudo networksetup -setsocksfirewallproxystate "$iface" off 2>/dev/null + $verbose && echo "已关闭" + fi + + # 4. 刷新 DNS 缓存 + $verbose && echo -n " ${ICON_INFO} 刷新 DNS 缓存 ... " + sudo dscacheutil -flushcache 2>/dev/null + sudo killall -HUP mDNSResponder 2>/dev/null + $verbose && echo "完成" + + # 5. 还原系统 DNS 设置(如果 mihomo 改了 DNS) + # mihomo TUN 模式通过 scutil 改系统 DNS,检查并还原 + local primary_dns=$(scutil --dns 2>/dev/null | grep "nameserver\[" | head -1 | awk '{print $3}') + if [[ "$primary_dns" == "127.0.0.1" ]] || [[ "$primary_dns" == "198.18."* ]]; then + $verbose && echo -n " ${ICON_INFO} 还原系统 DNS (当前: $primary_dns) ... " + sudo networksetup -setdnsservers "Wi-Fi" empty 2>/dev/null + sudo dscacheutil -flushcache 2>/dev/null + sudo killall -HUP mDNSResponder 2>/dev/null + $verbose && echo "已还原" + fi + + $verbose && echo "" + $verbose && echo " ${BOLD_GREEN}${ICON_OK} 网络已恢复${NC}" +} + +# ========== 停止(带完整网络恢复)========== +clash_stop() { + if ! is_running; then + echo " ${YELLOW}${ICON_WARN} mihomo 没有在运行${NC}" + return 0 + fi + + local pid=$(cat "$PID_FILE") + echo "" + echo -n " ${ICON_STOP} 停止中 ... " + + # 先发 SIGTERM,让 mihomo 优雅退出(它会自动清理 TUN/路由/DNS) + sudo kill "$pid" 2>/dev/null + + # 等最多 5 秒,让它完成清理 + for i in {1..10}; do + if ! pgrep -F "$PID_FILE" &>/dev/null 2>&1; then + echo "${BOLD_GREEN}已停止${NC} ${DIM}(PID: $pid)${NC}" + echo " ${DIM}已自动清理 TUN/路由/DNS${NC}" + rm -f "$PID_FILE" + echo "" + # 依然做一次网络恢复(确保万无一失) + network_restore false + network_health_check + return 0 + fi + sleep 0.5 + done + + # 超时了,强杀 + echo "${BOLD_YELLOW}超时${NC},强制终止..." + sudo kill -9 "$pid" 2>/dev/null + sleep 0.5 + rm -f "$PID_FILE" + + if pgrep -l mihomo >/dev/null 2>&1; then + echo " ${BOLD_RED}停止失败${NC},残留进程仍在运行" + echo " ${DIM}试试: sudo pkill -9 mihomo${NC}" + return 1 + fi + + echo " ${BOLD_YELLOW}已强制停止${NC}" + # 强杀后 mihomo 来不及清理,主动恢复网络 + echo " ${ICON_INFO} 正在恢复网络 ..." + network_restore true + echo "" + network_health_check +} + +# ========== 清理/修复(alias)========== +clash_cleanup() { + network_restore true +} + +# ========== 重启 ========== +clash_restart() { + clash_stop + sleep 1 + clash_start "$@" +} + +# ========== 热重载 ========== +clash_reload() { + local cfg="${1:-$CONFIG_FILE}" + if ! is_running; then + echo "${YELLOW}[!] mihomo 没在运行,直接启动${NC}" + clash_start "$cfg" + return $? + fi + if [[ ! -f "$cfg" ]]; then + echo "${RED}[!] 配置文件不存在: $cfg${NC}" + return 1 + fi + echo "" + echo -n " ${ICON_INFO} 热重载配置 ... " + curl -s -X PUT "http://127.0.0.1:${API_PORT}/configs" \ + -H "Content-Type: application/json" \ + -d "{\"path\":\"${cfg}\"}" > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + echo "${BOLD_GREEN}成功${NC}" + else + echo "${BOLD_RED}失败${NC}" + echo " ${ICON_WARN} API 不可达,尝试重启..." + clash_restart "$cfg" + fi +} + +# ========== 状态 ========== +clash_status() { + if is_running; then + local pid=$(cat "$PID_FILE") + echo "" + echo " ${BOLD_GREEN}● 运行中${NC}" + echo " $SEP" + printf " ${DIM}%-12s${NC} %s\n" "PID" "$pid" + printf " ${DIM}%-12s${NC} ${CYAN}127.0.0.1:${MIXED_PORT}${NC} (HTTP/SOCKS5)\n" "代理端口" + printf " ${DIM}%-12s${NC} ${CYAN}127.0.0.1:${API_PORT}${NC}\n" "API 端口" + printf " ${DIM}%-12s${NC} ${CYAN}http://127.0.0.1:${API_PORT}/ui${NC}\n" "管理面板" + printf " ${DIM}%-12s${NC} %s\n" "配置文件" "$CONFIG_FILE" + printf " ${DIM}%-12s${NC} %s\n" "日志文件" "$LOG_FILE" + local src=$(cat "$LINK_FILE" 2>/dev/null || auto_detect_source 2>/dev/null) + [[ -n "$src" ]] && printf " ${DIM}%-12s${NC} %s\n" "源文件" "$src" + echo "" + else + echo "" + echo " ${BOLD_RED}○ 已停止${NC}" + echo " $SEP" + echo " ${DIM}mihomo 当前未运行${NC}" + echo "" + rm -f "$PID_FILE" + fi +} + +# ========== 导入配置(自动适配 macOS)========== +clash_import() { + local src="" + echo "" + echo " ${BOLD}── 导入配置 ──${NC}" + local current_src=$(cat "$LINK_FILE" 2>/dev/null || auto_detect_source) + echo " ${ICON_INFO} 当前源: ${CYAN}${current_src:-${DIM}未检测到${NC}}" + echo "" + echo -n " ${BOLD}❯${NC} 请输入 YAML 文件路径 ${DIM}(Enter 取消)${NC}: " + read src + if [[ -z "$src" ]]; then + echo " ${YELLOW}${ICON_WARN} 已取消${NC}" + return 0 + fi + # 去掉首尾空格和引号 + src=$(echo "$src" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/^"//;s/"$//') + if [[ ! -f "$src" ]]; then + echo " ${BOLD_RED}${ICON_FAIL} 文件不存在: $src${NC}" + return 1 + fi + # 记录源文件路径 + echo "$src" > "$LINK_FILE" + # 适配并复制到 CONFIG_DIR + macos_adapt_config "$src" || return 1 + echo "" + echo -n " ${ICON_INFO} 立即启动? (y/n) " + read -q ans + echo "" + [[ "$ans" == "y" ]] && clash_start +} + +# ========== 链接源配置 ========== +# 设置共享 YAML 文件路径,启动时自动从该文件适配 +clash_link() { + local src="${1:-}" + if [[ -z "$src" ]]; then + echo "" + echo " ${BOLD}── 源文件链接 ──${NC}" + if [[ -f "$LINK_FILE" ]]; then + echo " ${ICON_INFO} 当前链接: ${CYAN}$(cat "$LINK_FILE")${NC}" + else + local detected=$(auto_detect_source) + if [[ -n "$detected" ]]; then + echo " ${ICON_INFO} 自动检测: ${CYAN}$detected${NC}" + else + echo " ${YELLOW}${ICON_WARN} 未检测到源文件${NC}" + echo " ${DIM}请把 .yaml 文件放到 $SOURCE_DIR${NC}" + fi + fi + echo "" + echo -n " ${BOLD}❯${NC} 输入新的源文件路径 ${DIM}(Enter 不变)${NC}: " + read src + [[ -z "$src" ]] && return 0 + src=$(echo "$src" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/^"//;s/"$//') + fi + if [[ ! -f "$src" ]]; then + echo " ${BOLD_RED}${ICON_FAIL} 文件不存在: $src${NC}" + return 1 + fi + echo "$src" > "$LINK_FILE" + echo "" + echo " ${ICON_OK} ${GREEN}已链接${NC}" + echo " $SEP" + printf " ${DIM}%-12s${NC} ${CYAN}%s${NC}\n" "源文件" "$src" + echo " $SEP" + echo "" + echo -n " ${ICON_INFO} 立即适配并重启? (y/n) " + read -q ans + echo "" + if [[ "$ans" == "y" ]]; then + macos_adapt_config "$src" + if is_running; then + echo "" + echo -n " ${ICON_INFO} 重启 mihomo? (y/n) " + read -q ans2 + echo "" + [[ "$ans2" == "y" ]] && clash_restart + fi + fi +} + +# ========== 关闭 Clash Verge 后台服务 ========== +clash_verge_off() { + echo "" + echo " ${BOLD}── 关闭 Clash Verge ──${NC}" + local svc_pid=$(pgrep -f "clash-verge-service" 2>/dev/null) + if [[ -n "$svc_pid" ]]; then + echo " ${ICON_INFO} 发现 Clash Verge 服务 ${DIM}(PID: $svc_pid)${NC}" + echo -n " ${ICON_STOP} 正在停止 ... " + sudo kill "$svc_pid" 2>/dev/null + sleep 1 + if pgrep -f "clash-verge-service" >/dev/null 2>&1; then + sudo kill -9 "$svc_pid" 2>/dev/null + fi + echo "${GREEN}已停止${NC}" + else + echo " ${ICON_OK} Clash Verge 服务未运行" + fi + + echo "" + echo -n " ${ICON_INFO} 是否同时卸载开机自启? (y/n) " + read -q ans + echo "" + if [[ "$ans" == "y" ]]; then + sudo /bin/launchctl unload /Library/LaunchDaemons/io.github.clash-verge-rev.clash-verge-rev.plist 2>/dev/null + echo " ${ICON_OK} ${GREEN}已卸载开机自启${NC}" + fi + echo "" + echo " ${PURPLE}╭────────────────────────────────────╮${NC}" + echo " ${PURPLE}│${NC} ${BOLD}现在可以纯用 clash 脚本管理了${NC} ${PURPLE}│${NC}" + echo " ${PURPLE}╰────────────────────────────────────╯${NC}" + echo "" +} + +# ========== 日志 ========== +clash_log() { + if [[ -f "$LOG_FILE" ]]; then + echo "" + echo " ${BOLD}── 实时日志 ──${NC} ${DIM}(Ctrl+C 退出)${NC}" + echo " $SEP" + tail -f "$LOG_FILE" + else + echo " ${YELLOW}${ICON_WARN} 日志文件不存在${NC}" + fi +} + +# ========== 编辑配置 ========== +clash_edit() { + echo "" + echo " ${ICON_INFO} 打开编辑器: ${CYAN}$CONFIG_FILE${NC}" + ${EDITOR:-vim} "$CONFIG_FILE" +} + +# ========== 打开面板 ========== +clash_open() { + if is_running; then + open "http://127.0.0.1:${API_PORT}/ui" + echo "" + echo " ${ICON_OK} 已打开管理面板" + echo " ${DIM}${CYAN}http://127.0.0.1:${API_PORT}/ui${NC}" + echo "" + else + echo " ${YELLOW}${ICON_WARN} mihomo 未运行,请先启动${NC}" + fi +} + +# ========== 交互菜单 ========== +show_menu() { + clear + + # ── Mihomo ASCII Banner ── + echo "" + echo " ${BOLD_PURPLE}███╗ ███╗██╗██╗ ██╗ ██████╗ ███╗ ███╗ ██████╗ ${NC}" + echo " ${BOLD_PURPLE}████╗ ████║██║██║ ██║██╔═══██╗████╗ ████║██╔═══██╗${NC}" + echo " ${BOLD_PURPLE}██╔████╔██║██║███████║██║ ██║██╔████╔██║██║ ██║${NC}" + echo " ${BOLD_PURPLE}██║╚██╔╝██║██║██╔══██║██║ ██║██║╚██╔╝██║██║ ██║${NC}" + echo " ${BOLD_PURPLE}██║ ╚═╝ ██║██║██║ ██║╚██████╔╝██║ ╚═╝ ██║╚██████╔╝${NC}" + echo " ${BOLD_PURPLE}╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ${NC}" + echo " ${DIM}┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈${NC}" + + # ── 状态栏 ── + local pid_str="" + if is_running; then + printf " ${BOLD_GREEN}● 运行中${NC} ${DIM}PID: %s${NC} ${DIM}端口: %s${NC} ${DIM}API: %s${NC}\n" \ + "$(cat "$PID_FILE" 2>/dev/null)" \ + "${MIXED_PORT}" \ + "${API_PORT}" + else + echo " ${BOLD_RED}○ 已停止${NC}" + fi + echo "" + + # ── 菜单面板 ── + echo " ${BOLD}╔══════════════════════════════════════════════╗${NC}" + echo " ${BOLD}║${NC} ${CYAN}1${NC} ${WHITE}启动${NC} ${CYAN}6${NC} ${WHITE}实时日志${NC} ${BOLD}║${NC}" + echo " ${BOLD}║${NC} ${CYAN}2${NC} ${WHITE}停止${NC} ${CYAN}7${NC} ${WHITE}导入配置${NC} ${BOLD}║${NC}" + echo " ${BOLD}║${NC} ${CYAN}3${NC} ${WHITE}重启${NC} ${CYAN}8${NC} ${WHITE}管理面板${NC} ${BOLD}║${NC}" + echo " ${BOLD}║${NC} ${CYAN}4${NC} ${WHITE}热重载 ${CYAN}9${NC} ${WHITE}编辑配置${NC} ${BOLD}║${NC}" + echo " ${BOLD}║${NC} ${CYAN}5${NC} ${WHITE}运行状态 ${PURPLE}L${NC} ${WHITE}源文件链接${NC} ${BOLD}║${NC}" + echo " ${BOLD}║${NC} ${BOLD}║${NC}" + echo " ${BOLD}║${NC} ${YELLOW}R${NC} ${WHITE}网络修复${NC} ${BOLD}║${NC}" + echo " ${BOLD}║${NC} ${RED}0${NC} ${WHITE}退出${NC} ${BOLD}║${NC}" + echo " ${BOLD}╚══════════════════════════════════════════════╝${NC}" + echo "" + echo -n " ${BOLD}❯${NC} " +} + +interactive_menu() { + while true; do + show_menu + read choice + echo "" + case "$choice" in + 1) clash_start ;; + 2) clash_stop ;; + 3) clash_restart ;; + 4) clash_reload ;; + 5) clash_status ;; + 6) clash_log ;; + 7) clash_import ;; + 8) clash_open ;; + 9) clash_edit ;; + l|L) clash_link ;; + + r|R) clash_cleanup ;; + 0|q|Q) + echo "" + echo " ${PURPLE}╭──────────────────────────╮${NC}" + echo " ${PURPLE}│${NC} ${BOLD}期待下次相遇 👋${NC} ${PURPLE}│${NC}" + printf " ${PURPLE}│${NC} ${DIM}%s${NC} ${PURPLE}│${NC}\n" "Mihomo $(date +%H:%M)" + echo " ${PURPLE}╰──────────────────────────╯${NC}" + echo "" + break + ;; + *) echo " ${YELLOW}${ICON_WARN} 无效选项,请输入 0-9 或 L/R${NC}" ;; + esac + echo "" + echo -n " 按 Enter 返回菜单..." + read + done +} + +# ========== 入口 ========== +case "${1:-}" in + start) clash_start "$2" ;; + stop) clash_stop ;; + restart) clash_restart "$2" ;; + reload) clash_reload "$2" ;; + status) clash_status ;; + log) clash_log ;; + import) clash_import ;; + link) clash_link "$2" ;; + open) clash_open ;; + edit) clash_edit ;; + cleanup) clash_cleanup ;; + menu|-h|--help|help) + echo "" + echo " ${BOLD_PURPLE}Mihomo Kernel Manager${NC} ${DIM}— macOS${NC}" + echo " $SEP" + echo "" + printf " ${CYAN}%-20s${NC} %s\n" "clash" "进入交互菜单" + printf " ${CYAN}%-20s${NC} %s\n" "clash start" "启动(自动从源文件适配 macOS)" + printf " ${CYAN}%-20s${NC} %s\n" "clash start <文件>" "从指定文件启动" + printf " ${CYAN}%-20s${NC} %s\n" "clash stop" "停止" + printf " ${CYAN}%-20s${NC} %s\n" "clash restart" "重启" + printf " ${CYAN}%-20s${NC} %s\n" "clash reload" "热重载配置" + printf " ${CYAN}%-20s${NC} %s\n" "clash status" "查看运行状态" + printf " ${CYAN}%-20s${NC} %s\n" "clash log" "查看实时日志" + printf " ${CYAN}%-20s${NC} %s\n" "clash import" "导入/链接配置文件" + printf " ${CYAN}%-20s${NC} %s\n" "clash link" "查看/设置源 YAML 路径" + printf " ${CYAN}%-20s${NC} %s\n" "clash verge-off" "关闭 Clash Verge 服务" + printf " ${CYAN}%-20s${NC} %s\n" "clash open" "打开管理面板" + printf " ${CYAN}%-20s${NC} %s\n" "clash edit" "编辑当前配置" + printf " ${CYAN}%-20s${NC} %s\n" "clash cleanup" "网络修复/清理残留" + echo "" + ;; + *) interactive_menu ;; +esac diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..d3a7e32 --- /dev/null +++ b/install.sh @@ -0,0 +1,89 @@ +#!/bin/zsh +# ═══════════════════════════════════════════════════════════ +# Mihomo Kernel Manager — macOS 一键部署脚本 +# 用法: curl -fsSL https://xxx/install.sh | zsh +# 或本地: zsh install.sh +# ═══════════════════════════════════════════════════════════ + +set -e + +BOLD='\033[1m' +GREEN='\033[0;32m' +BOLD_GREEN='\033[1;32m' +RED='\033[0;31m' +YELLOW='\033[0;33m' +CYAN='\033[0;36m' +PURPLE='\033[0;35m' +NC='\033[0m' + +echo "" +echo " ${PURPLE}╭──────────────────────────────────────╮${NC}" +echo " ${PURPLE}│${NC} ${BOLD}Mihomo Kernel Manager 部署脚本${NC} ${PURPLE}│${NC}" +echo " ${PURPLE}╰──────────────────────────────────────╯${NC}" +echo "" + +# ─── 前置检查 ─── +echo " ${BOLD}── 系统检查 ──${NC}" + +# Homebrew +echo -n " ${CYAN}▶${NC} Homebrew ... " +if command -v brew &>/dev/null; then + echo "${GREEN}已安装${NC}" +else + echo "${YELLOW}未安装,正在安装...${NC}" + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +fi + +# mihomo 内核 +echo -n " ${CYAN}▶${NC} mihomo 内核 ... " +if command -v mihomo &>/dev/null; then + echo "${GREEN}已安装 ($(mihomo -v 2>/dev/null | head -1))${NC}" +else + echo "${YELLOW}未安装,正在安装...${NC}" + brew install mihomo + echo " ${GREEN}✓ mihomo 安装完成${NC}" +fi + +# ─── 部署 clash 管理脚本 ─── +echo "" +echo " ${BOLD}── 部署管理脚本 ──${NC}" + +SCRIPT_SRC="$(cd "$(dirname "$0")" && pwd)/clash" +SCRIPT_DST="/opt/homebrew/bin/clash" + +if [[ -f "$SCRIPT_SRC" ]]; then + echo -n " ${CYAN}▶${NC} 安装 clash 脚本 ... " + sudo cp "$SCRIPT_SRC" "$SCRIPT_DST" + sudo chmod +x "$SCRIPT_DST" + echo "${GREEN}已部署 → $SCRIPT_DST${NC}" +else + echo " ${RED}✗ 未找到 clash 脚本文件${NC}" + echo " ${YELLOW}请确保 install.sh 和 clash 在同一目录${NC}" + exit 1 +fi + +# ─── 创建必要目录 ─── +echo "" +echo " ${BOLD}── 初始化目录 ──${NC}" + +echo -n " ${CYAN}▶${NC} 配置目录 (~/.config/mihomo) ... " +mkdir -p "$HOME/.config/mihomo" +echo "${GREEN}已创建${NC}" + +echo -n " ${CYAN}▶${NC} 源文件目录 (~/Mihomo) ... " +mkdir -p "$HOME/Mihomo" +echo "${GREEN}已创建${NC}" + +# ─── 完成提示 ─── +echo "" +echo " ${PURPLE}╭──────────────────────────────────────╮${NC}" +echo " ${PURPLE}│${NC} ${BOLD_GREEN}✅ 部署完成!${NC} ${PURPLE}│${NC}" +echo " ${PURPLE}╰──────────────────────────────────────╯${NC}" +echo "" +echo " 接下来:" +echo " ${CYAN} 1.${NC} 把你的 .yaml 配置文件放入 ${BOLD}~/Mihomo/${NC}" +echo " ${CYAN} 2.${NC} 运行 ${BOLD}clash start${NC} 启动" +echo " ${CYAN} 3.${NC} 运行 ${BOLD}clash${NC} 进入交互菜单" +echo "" +echo " 更多命令: ${BOLD}clash help${NC}" +echo ""