mirror of
https://github.com/wnlen/clash-for-linux.git
synced 2026-10-10 04:03:04 +08:00
This commit is contained in:
@@ -37,6 +37,7 @@ Usage:
|
||||
mixin 🧩 Mixin 配置管理
|
||||
relay 🔗 多跳节点管理
|
||||
lan 🏠 局域网代理管理
|
||||
ipv6 🌐 IPv6 / IPv6-only 节点管理
|
||||
|
||||
|
||||
📦 Subscription:
|
||||
@@ -49,6 +50,7 @@ Usage:
|
||||
clashui 🕹️ 查看 Web 控制台
|
||||
secret 🔑 查看或设置 Web 密钥
|
||||
lan on|off|status 🏠 开启 / 关闭局域网代理
|
||||
ipv6 on|off|auto|status 🌐 管理内核与 DNS IPv6
|
||||
|
||||
🧪 Transparent proxy:
|
||||
tun 🧪 Tun 模式管理
|
||||
@@ -3290,6 +3292,7 @@ cmd_ui_help_summary() {
|
||||
printf ' %-18s %s\n' "clash ls" "📜 查看订阅列表"
|
||||
echo "📌 高级"
|
||||
printf ' %-18s %s\n' "clash lan" "🏠 局域网代理管理"
|
||||
printf ' %-18s %s\n' "clash ipv6" "🌐 IPv6 / IPv6-only 节点管理"
|
||||
printf ' %-18s %s\n' "clash tun" "🧪 Tun 模式管理"
|
||||
printf ' %-18s %s\n' "clash mixin" "🧩 Mixin 配置管理"
|
||||
printf ' %-18s %s\n' "clash sub" "🧩 订阅高级管理(启用 / 禁用 / 重命名 / 删除)"
|
||||
@@ -4403,6 +4406,84 @@ cmd_lan() {
|
||||
esac
|
||||
}
|
||||
|
||||
print_ipv6_status() {
|
||||
local mode mode_text kernel_ipv6 dns_ipv6 kernel
|
||||
|
||||
mode="$(config_ipv6_mode)"
|
||||
kernel_ipv6="$(runtime_config_ipv6_enabled 2>/dev/null || echo "等待运行配置生成")"
|
||||
dns_ipv6="$(runtime_config_dns_ipv6_enabled 2>/dev/null || echo "等待运行配置生成")"
|
||||
kernel="$(runtime_kernel_type 2>/dev/null || echo unknown)"
|
||||
|
||||
case "$mode" in
|
||||
true) mode_text="已开启" ;;
|
||||
false) mode_text="已关闭" ;;
|
||||
*) mode_text="自动(保留订阅设置)" ;;
|
||||
esac
|
||||
|
||||
ui_title "🌐 IPv6"
|
||||
ui_kv "🔧" "配置模式" "$mode_text"
|
||||
ui_kv "🚀" "当前内核" "$kernel"
|
||||
ui_kv "🌐" "内核 IPv6" "$kernel_ipv6"
|
||||
ui_kv "🔎" "DNS AAAA" "$dns_ipv6"
|
||||
if [ "$kernel" != "mihomo" ]; then
|
||||
ui_warn "Hysteria2 建议使用 Mihomo:clash config kernel mihomo"
|
||||
fi
|
||||
ui_blank
|
||||
}
|
||||
|
||||
cmd_ipv6() {
|
||||
local action
|
||||
prepare
|
||||
|
||||
action="${1:-status}"
|
||||
case "$action" in
|
||||
on|enable)
|
||||
set_config_ipv6_mode true
|
||||
regenerate_config
|
||||
apply_runtime_change_after_config_mutation
|
||||
success "IPv6 与 DNS AAAA 已开启"
|
||||
print_ipv6_status
|
||||
print_config_apply_feedback
|
||||
;;
|
||||
off|disable)
|
||||
set_config_ipv6_mode false
|
||||
regenerate_config
|
||||
apply_runtime_change_after_config_mutation
|
||||
success "IPv6 与 DNS AAAA 已关闭"
|
||||
print_ipv6_status
|
||||
print_config_apply_feedback
|
||||
;;
|
||||
auto|reset)
|
||||
set_config_ipv6_mode auto
|
||||
regenerate_config
|
||||
apply_runtime_change_after_config_mutation
|
||||
success "IPv6 已恢复为自动模式"
|
||||
print_ipv6_status
|
||||
print_config_apply_feedback
|
||||
;;
|
||||
status)
|
||||
print_ipv6_status
|
||||
ui_next "IPv6-only / Hysteria2 节点不可用时执行:clash ipv6 on"
|
||||
ui_blank
|
||||
;;
|
||||
-h|--help|help)
|
||||
echo "📜 用法:"
|
||||
echo " clash ipv6 status"
|
||||
echo " clash ipv6 on"
|
||||
echo " clash ipv6 off"
|
||||
echo " clash ipv6 auto"
|
||||
echo
|
||||
echo "🌐 说明:"
|
||||
echo " on 同时启用内核 IPv6 与 DNS AAAA"
|
||||
echo " off 同时关闭内核 IPv6 与 DNS AAAA"
|
||||
echo " auto 保留订阅中的 IPv6 设置,缺省时沿用兼容默认值"
|
||||
;;
|
||||
*)
|
||||
die_usage "未知的 ipv6 子命令:$action" "clash ipv6 on|off|auto|status"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
print_profile_use_feedback() {
|
||||
local profile="$1"
|
||||
|
||||
@@ -7262,7 +7343,7 @@ cmd_sub() {
|
||||
|
||||
case "${1:-}" in
|
||||
update)
|
||||
regenerate_config
|
||||
regenerate_config "manual-refresh"
|
||||
apply_runtime_change_after_config_mutation
|
||||
print_config_regen_feedback
|
||||
print_config_apply_feedback
|
||||
@@ -8087,6 +8168,7 @@ case "$cmd" in
|
||||
dev) cmd_dev "$@" ;;
|
||||
config) cmd_config "$@" ;;
|
||||
lan) cmd_lan "$@" ;;
|
||||
ipv6) cmd_ipv6 "$@" ;;
|
||||
mixin) cmd_mixin "$@" ;;
|
||||
relay) cmd_relay "$@" ;;
|
||||
profile) cmd_profile "$@" ;;
|
||||
|
||||
@@ -1933,6 +1933,32 @@ runtime_config_allow_lan() {
|
||||
esac
|
||||
}
|
||||
|
||||
runtime_config_ipv6_enabled() {
|
||||
local file
|
||||
local value
|
||||
file="$(runtime_config_file)"
|
||||
[ -s "$file" ] || return 1
|
||||
|
||||
value="$("$(yq_bin)" eval '.ipv6' "$file" 2>/dev/null | head -n 1)"
|
||||
case "$value" in
|
||||
false) echo "false" ;;
|
||||
*) echo "true" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
runtime_config_dns_ipv6_enabled() {
|
||||
local file
|
||||
local value
|
||||
file="$(runtime_config_file)"
|
||||
[ -s "$file" ] || return 1
|
||||
|
||||
value="$("$(yq_bin)" eval '.dns.ipv6' "$file" 2>/dev/null | head -n 1)"
|
||||
case "$value" in
|
||||
true) echo "true" ;;
|
||||
*) echo "false" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
runtime_port_value_is_valid() {
|
||||
local port="${1:-}"
|
||||
|
||||
|
||||
@@ -213,6 +213,17 @@ _clash_for_linux_complete_lan() {
|
||||
fi
|
||||
}
|
||||
|
||||
_clash_for_linux_complete_ipv6() {
|
||||
local cur="$1"
|
||||
local rel_index="$2"
|
||||
|
||||
COMPREPLY=()
|
||||
|
||||
if [ "$rel_index" -eq 1 ]; then
|
||||
_clash_for_linux_add_matches "$cur" on off auto status enable disable reset help -h --help
|
||||
fi
|
||||
}
|
||||
|
||||
_clash_for_linux_complete_mixin() {
|
||||
local cur="$1"
|
||||
local rel_index="$2"
|
||||
@@ -347,7 +358,7 @@ _clash_for_linux_complete_top_level() {
|
||||
COMPREPLY=()
|
||||
_clash_for_linux_add_matches "$cur" \
|
||||
add use ls health select mode test on off status status-next \
|
||||
boot log logs doctor ui secret tun dev config lan mixin \
|
||||
boot log logs doctor ui secret tun dev config lan ipv6 mixin \
|
||||
relay profile sub proxy upgrade update completion help \
|
||||
-h --help
|
||||
}
|
||||
@@ -426,6 +437,7 @@ _clash_for_linux_complete_command() {
|
||||
boot) _clash_for_linux_complete_boot "$cur" "$rel_index" "$arg1" ;;
|
||||
config) _clash_for_linux_complete_config "$cur" "$rel_index" "$arg1" ;;
|
||||
lan) _clash_for_linux_complete_lan "$cur" "$rel_index" ;;
|
||||
ipv6) _clash_for_linux_complete_ipv6 "$cur" "$rel_index" ;;
|
||||
mixin) _clash_for_linux_complete_mixin "$cur" "$rel_index" ;;
|
||||
relay) _clash_for_linux_complete_relay "$cur" "$rel_index" "$arg1" ;;
|
||||
sub) _clash_for_linux_complete_sub "$cur" "$rel_index" "$arg1" ;;
|
||||
|
||||
+66
-7
@@ -276,6 +276,46 @@ config_bool_env_value() {
|
||||
esac
|
||||
}
|
||||
|
||||
config_ipv6_mode() {
|
||||
local value
|
||||
|
||||
value="${CLASH_IPV6:-}"
|
||||
[ -n "${value:-}" ] || value="$(read_env_value "CLASH_IPV6" 2>/dev/null || true)"
|
||||
|
||||
case "${value:-auto}" in
|
||||
true|1|yes|on)
|
||||
echo "true"
|
||||
;;
|
||||
false|0|no|off)
|
||||
echo "false"
|
||||
;;
|
||||
auto|"")
|
||||
echo "auto"
|
||||
;;
|
||||
*)
|
||||
echo "auto"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
set_config_ipv6_mode() {
|
||||
local mode="$1"
|
||||
|
||||
case "$mode" in
|
||||
true|false)
|
||||
write_env_value "CLASH_IPV6" "$mode"
|
||||
export CLASH_IPV6="$mode"
|
||||
;;
|
||||
auto)
|
||||
unset_env_value "CLASH_IPV6" || true
|
||||
unset CLASH_IPV6 2>/dev/null || true
|
||||
;;
|
||||
*)
|
||||
die "IPv6 模式只允许 true / false / auto"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
tun_auto_route() {
|
||||
config_bool_env_value "CLASH_TUN_AUTO_ROUTE" "true"
|
||||
}
|
||||
@@ -483,7 +523,7 @@ local_subscription_share_links_to_subconverter_url() {
|
||||
sub(/[[:space:]]+$/, "")
|
||||
}
|
||||
$0 == "" || $0 ~ /^#/ { next }
|
||||
$0 ~ /^(vmess|vless|trojan|tuic):\/\// {
|
||||
$0 ~ /^(vmess|vless|trojan|tuic|hysteria2|hy2|anytls):\/\// {
|
||||
if (out != "") {
|
||||
out = out "|"
|
||||
}
|
||||
@@ -577,7 +617,7 @@ local_subscription_convert_url_from_url() {
|
||||
fi
|
||||
|
||||
rm -f "$decoded_file" 2>/dev/null || true
|
||||
die "本地订阅不是 Clash YAML,且无法识别为 Base64 或 vmess/vless/trojan/tuic 分享链接:$local_path"
|
||||
die "本地订阅不是 Clash YAML,且无法识别为 Base64 或 vmess/vless/trojan/tuic/hysteria2/hy2/anytls 分享链接:$local_path"
|
||||
}
|
||||
|
||||
download_candidate_probe() {
|
||||
@@ -801,7 +841,8 @@ normalize_runtime_config() {
|
||||
local file="$1"
|
||||
local mixed_port controller tun_enable_value tun_stack_value dns_port_value controller_secret_value
|
||||
local tun_auto_route_value tun_auto_redirect_value tun_strict_route_value tun_dns_hijack_value
|
||||
local dashboard_dir_value dashboard_url_value allow_lan_value
|
||||
local dashboard_dir_value dashboard_url_value allow_lan_value ipv6_mode_value
|
||||
local ipv6_value dns_ipv6_value
|
||||
local resolved_ports err_file output
|
||||
|
||||
[ -s "$file" ] || die "待规范化的配置文件不存在:$file"
|
||||
@@ -822,11 +863,27 @@ normalize_runtime_config() {
|
||||
dashboard_dir_value="$(runtime_dashboard_dir)"
|
||||
dashboard_url_value="$DEFAULT_DASHBOARD_UI_URL"
|
||||
allow_lan_value="$(config_allow_lan 2>/dev/null || echo true)"
|
||||
ipv6_mode_value="$(config_ipv6_mode)"
|
||||
|
||||
case "$ipv6_mode_value" in
|
||||
true|false)
|
||||
ipv6_value="$ipv6_mode_value"
|
||||
dns_ipv6_value="$ipv6_mode_value"
|
||||
;;
|
||||
*)
|
||||
ipv6_value="$("$(yq_bin)" eval '.ipv6' "$file" 2>/dev/null | head -n 1 || true)"
|
||||
dns_ipv6_value="$("$(yq_bin)" eval '.dns.ipv6' "$file" 2>/dev/null | head -n 1 || true)"
|
||||
case "$ipv6_value" in true|false) ;; *) ipv6_value="true" ;; esac
|
||||
case "$dns_ipv6_value" in true|false) ;; *) dns_ipv6_value="false" ;; esac
|
||||
;;
|
||||
esac
|
||||
|
||||
err_file="$(mktemp)"
|
||||
if ! mixed_port="$mixed_port" \
|
||||
controller="$controller" \
|
||||
allow_lan_value="$allow_lan_value" \
|
||||
ipv6_value="$ipv6_value" \
|
||||
dns_ipv6_value="$dns_ipv6_value" \
|
||||
tun_enable_value="$tun_enable_value" \
|
||||
tun_stack_value="$tun_stack_value" \
|
||||
tun_auto_route_value="$tun_auto_route_value" \
|
||||
@@ -847,6 +904,7 @@ normalize_runtime_config() {
|
||||
.["allow-lan"] = (env(allow_lan_value) == "true") |
|
||||
.mode = "rule" |
|
||||
.["log-level"] = (.["log-level"] // "info") |
|
||||
.ipv6 = (env(ipv6_value) == "true") |
|
||||
|
||||
.tun.enable = (env(tun_enable_value) == "true") |
|
||||
.tun.stack = env(tun_stack_value) |
|
||||
@@ -858,7 +916,7 @@ normalize_runtime_config() {
|
||||
|
||||
.dns.enable = (.dns.enable // true) |
|
||||
.dns["enhanced-mode"] = (.dns["enhanced-mode"] // "fake-ip") |
|
||||
.dns.ipv6 = false |
|
||||
.dns.ipv6 = (env(dns_ipv6_value) == "true") |
|
||||
.dns.listen = env(dns_listen_value) |
|
||||
|
||||
.proxies = (.proxies // []) |
|
||||
@@ -3311,7 +3369,7 @@ subscription_yaml_has_no_nodes() {
|
||||
}
|
||||
|
||||
subconverter_default_subscription_user_agent() {
|
||||
echo "clash.meta/1.18.0 clash/1.18.0 subconverter/0.9.0"
|
||||
subscription_user_agent
|
||||
}
|
||||
|
||||
subconverter_subscription_user_agent() {
|
||||
@@ -4044,6 +4102,7 @@ fetch_subscription_source() {
|
||||
|
||||
generate_config() {
|
||||
local active_source
|
||||
local fetch_reason="${1:-auto}"
|
||||
local out_file="$RUNTIME_DIR/config.yaml"
|
||||
local tmp_dir source_file candidate_file
|
||||
local selected_csv="" included_csv="" failed_csv=""
|
||||
@@ -4073,7 +4132,7 @@ generate_config() {
|
||||
|
||||
selected_csv="$active_source"
|
||||
|
||||
if ! fetch_subscription_source "$active_source" "$source_file" "auto"; then
|
||||
if ! fetch_subscription_source "$active_source" "$source_file" "$fetch_reason"; then
|
||||
failed_csv="$active_source"
|
||||
if ! read_compile_error >/dev/null 2>&1; then
|
||||
if [ -n "${SUBCONVERTER_LAST_ERROR_SUMMARY:-}" ]; then
|
||||
@@ -4128,5 +4187,5 @@ generate_config() {
|
||||
}
|
||||
|
||||
regenerate_config() {
|
||||
generate_config
|
||||
generate_config "${1:-auto}"
|
||||
}
|
||||
|
||||
@@ -60,10 +60,14 @@ export RUNTIME_DIR="$tmp_dir/runtime"
|
||||
export BIN_DIR="$tmp_dir/bin"
|
||||
export LOG_DIR="$tmp_dir/logs"
|
||||
export CONFIG_DIR="$tmp_dir/config"
|
||||
unset CLASH_IPV6
|
||||
|
||||
# shellcheck source=scripts/core/config.sh
|
||||
source "$PROJECT_DIR/scripts/core/config.sh"
|
||||
|
||||
# Keep this regression test independent from the developer's project .env.
|
||||
read_env_value() { return 1; }
|
||||
|
||||
resolve_runtime_ports() {
|
||||
printf 'MIXED_PORT_RESOLVED=7891\n'
|
||||
printf 'EXTERNAL_CONTROLLER_RESOLVED=0.0.0.0:9090\n'
|
||||
@@ -90,31 +94,110 @@ mixed-port: 7890
|
||||
external-controller: 0.0.0.0:9090
|
||||
secret: old-secret
|
||||
allow-lan: false
|
||||
proxies: []
|
||||
ipv6: true
|
||||
dns:
|
||||
ipv6: true
|
||||
proxies:
|
||||
- name: hy2-ipv6
|
||||
type: hysteria2
|
||||
server: "2001:db8::1"
|
||||
port: 443
|
||||
password: test-password
|
||||
- name: anytls
|
||||
type: anytls
|
||||
server: 192.0.2.1
|
||||
port: 443
|
||||
password: test-password
|
||||
sni: example.com
|
||||
proxy-groups: []
|
||||
rules: []
|
||||
YAML
|
||||
|
||||
normalize_runtime_config "$sample_config"
|
||||
|
||||
assert_yq() {
|
||||
assert_file_yq() {
|
||||
local name="$1"
|
||||
local expr="$2"
|
||||
local expected="$3"
|
||||
local file="$2"
|
||||
local expr="$3"
|
||||
local expected="$4"
|
||||
local actual
|
||||
|
||||
actual="$("$BIN_DIR/yq" eval "$expr" "$sample_config")"
|
||||
actual="$("$BIN_DIR/yq" eval "$expr" "$file")"
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
echo "not ok - $name: got '$actual', expected '$expected'" >&2
|
||||
"$BIN_DIR/yq" eval '.' "$sample_config" >&2 || true
|
||||
"$BIN_DIR/yq" eval '.' "$file" >&2 || true
|
||||
return 1
|
||||
fi
|
||||
echo "ok - $name"
|
||||
}
|
||||
|
||||
assert_yq() {
|
||||
assert_file_yq "$1" "$sample_config" "$2" "$3"
|
||||
}
|
||||
|
||||
assert_yq "keeps resolved mixed-port" '.["mixed-port"]' "7891"
|
||||
assert_yq "removes legacy port" 'has("port")' "false"
|
||||
assert_yq "removes legacy socks-port" 'has("socks-port")' "false"
|
||||
assert_yq "removes legacy redir-port" 'has("redir-port")' "false"
|
||||
assert_yq "removes legacy tproxy-port" 'has("tproxy-port")' "false"
|
||||
assert_yq "keeps controller normalization" '.["external-controller"]' "0.0.0.0:9090"
|
||||
assert_yq "preserves subscription IPv6" '.ipv6' "true"
|
||||
assert_yq "preserves subscription DNS IPv6" '.dns.ipv6' "true"
|
||||
assert_yq "preserves Hysteria2 proxy type" '.proxies[0].type' "hysteria2"
|
||||
assert_yq "preserves IPv6-only proxy server" '.proxies[0].server' "2001:db8::1"
|
||||
assert_yq "preserves AnyTLS proxy type" '.proxies[1].type' "anytls"
|
||||
assert_yq "preserves AnyTLS SNI" '.proxies[1].sni' "example.com"
|
||||
|
||||
CLASH_IPV6=false normalize_runtime_config "$sample_config"
|
||||
assert_yq "IPv6 off disables kernel IPv6" '.ipv6' "false"
|
||||
assert_yq "IPv6 off disables DNS IPv6" '.dns.ipv6' "false"
|
||||
|
||||
unset CLASH_IPV6
|
||||
normalize_runtime_config "$sample_config"
|
||||
assert_yq "auto preserves disabled kernel IPv6" '.ipv6' "false"
|
||||
assert_yq "auto preserves disabled DNS IPv6" '.dns.ipv6' "false"
|
||||
|
||||
CLASH_IPV6=true normalize_runtime_config "$sample_config"
|
||||
assert_yq "IPv6 on enables kernel IPv6" '.ipv6' "true"
|
||||
assert_yq "IPv6 on enables DNS IPv6" '.dns.ipv6' "true"
|
||||
unset CLASH_IPV6
|
||||
|
||||
default_config="$tmp_dir/default-ipv6-config.yaml"
|
||||
cat > "$default_config" <<'YAML'
|
||||
proxies: []
|
||||
proxy-groups: []
|
||||
rules: []
|
||||
YAML
|
||||
|
||||
normalize_runtime_config "$default_config"
|
||||
assert_file_yq "auto keeps kernel IPv6 default enabled" "$default_config" '.ipv6' "true"
|
||||
assert_file_yq "auto keeps legacy DNS IPv6 default disabled" "$default_config" '.dns.ipv6' "false"
|
||||
|
||||
default_subscription_ua="$(
|
||||
unset CLASH_SUBSCRIPTION_UA CLASH_SUB_UA
|
||||
subconverter_subscription_user_agent
|
||||
)"
|
||||
if [ "$default_subscription_ua" != "clash-verge/v2.4.0" ]; then
|
||||
echo "not ok - modern protocol subscription UA: got '$default_subscription_ua'" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "ok - modern protocol subscription UA"
|
||||
|
||||
share_links="$tmp_dir/modern-share-links.txt"
|
||||
cat > "$share_links" <<'EOF'
|
||||
vmess://legacy
|
||||
hysteria2://password@example.com:443
|
||||
hy2://password@example.com:443
|
||||
anytls://password@example.com:443
|
||||
EOF
|
||||
|
||||
converted_links="$(local_subscription_share_links_to_subconverter_url "$share_links")"
|
||||
case "$converted_links" in
|
||||
*"hysteria2://"*"hy2://"*"anytls://"*)
|
||||
echo "ok - recognizes Hysteria2, hy2 and AnyTLS share links"
|
||||
;;
|
||||
*)
|
||||
echo "not ok - modern share links were dropped: $converted_links" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -19,7 +19,7 @@ calls_file="$tmp_dir/sub-update-calls"
|
||||
output_file="$tmp_dir/sub-update-output"
|
||||
|
||||
prepare() { :; }
|
||||
regenerate_config() { printf 'regenerate\n' >> "$calls_file"; }
|
||||
regenerate_config() { printf 'regenerate:%s\n' "${1:-}" >> "$calls_file"; }
|
||||
apply_runtime_change_after_config_mutation() { printf 'apply\n' >> "$calls_file"; }
|
||||
print_config_regen_feedback() { printf 'regen-feedback\n' >> "$calls_file"; }
|
||||
print_config_apply_feedback() { printf 'apply-feedback\n' >> "$calls_file"; }
|
||||
@@ -31,7 +31,7 @@ if ! ( cmd_sub update ) > "$output_file" 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expected_calls="$(printf '%s\n' regenerate apply regen-feedback apply-feedback)"
|
||||
expected_calls="$(printf '%s\n' regenerate:manual-refresh apply regen-feedback apply-feedback)"
|
||||
actual_calls="$(cat "$calls_file")"
|
||||
if [ "$actual_calls" != "$expected_calls" ]; then
|
||||
echo "not ok - clashctl sub update should follow the config regen path" >&2
|
||||
@@ -40,6 +40,7 @@ if [ "$actual_calls" != "$expected_calls" ]; then
|
||||
fi
|
||||
|
||||
echo "ok - clashctl sub update follows the config regen path"
|
||||
echo "ok - clashctl sub update bypasses stale subscription cache"
|
||||
|
||||
# Verify both the sourced-shell compatibility function and the installed command wrapper.
|
||||
# shellcheck source=../core/common.sh
|
||||
|
||||
Reference in New Issue
Block a user