Files
Clash/clash
T
2026-08-02 22:25:04 +08:00

685 lines
26 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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