diff --git a/scripts/gen-cn-nft.sh b/scripts/gen-cn-nft.sh
deleted file mode 100644
index ea9055c99..000000000
--- a/scripts/gen-cn-nft.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/sh
-
-set -eu
-
-ROOT_DIR="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"
-OUT_DIR="${ROOT_DIR}/clashoo/files/usr/share/clashoo/nftables"
-TMP_DIR="$(mktemp -d)"
-V2DAT_BIN="${V2DAT_BIN:-}"
-GEOIP_DAT_URL="${GEOIP_DAT_URL:-https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat}"
-V2DAT_PKG="${V2DAT_PKG:-github.com/kenzok8/v2dat}"
-
-cleanup() {
- rm -rf "$TMP_DIR"
-}
-
-trap cleanup EXIT INT TERM
-
-ensure_v2dat() {
- if [ -n "$V2DAT_BIN" ] && [ -x "$V2DAT_BIN" ]; then
- return 0
- fi
- if command -v v2dat >/dev/null 2>&1; then
- V2DAT_BIN="$(command -v v2dat)"
- return 0
- fi
- export GOBIN="${TMP_DIR}/bin"
- mkdir -p "$GOBIN"
- go install "${V2DAT_PKG}@latest"
- V2DAT_BIN="${GOBIN}/v2dat"
-}
-
-render_nft_set() {
- local family="$1"
- local input_file="$2"
- local output_file="$3"
- local set_name="$4"
-
- awk -v set_name="$set_name" -v set_type="$family" '
- BEGIN {
- print "set " set_name " {"
- print "\ttype " set_type ";"
- print "\tflags interval;"
- print "\tauto-merge;"
- print "\telements = {"
- first = 1
- }
- !/^[[:space:]]*$/ && !/^[[:space:]]*#/ {
- gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0)
- if ($0 == "")
- next
- if (!first)
- print ","
- printf "\t\t%s", $0
- first = 0
- }
- END {
- if (!first)
- print ""
- print "\t}"
- print "}"
- }
- ' "$input_file" > "$output_file"
-}
-
-mkdir -p "$OUT_DIR" "${TMP_DIR}/out"
-ensure_v2dat
-
-curl -fsSL "$GEOIP_DAT_URL" -o "${TMP_DIR}/geoip.dat"
-"$V2DAT_BIN" unpack geoip -o "${TMP_DIR}/out" -f cn "${TMP_DIR}/geoip.dat"
-
-CN_TXT="$(find "${TMP_DIR}/out" -type f -name '*_cn.txt' | head -1)"
-[ -n "${CN_TXT}" ] && [ -f "${CN_TXT}" ] || {
- echo "failed to unpack CN CIDRs from geoip.dat" >&2
- exit 1
-}
-
-awk 'index($0, ":") == 0 { print }' "${CN_TXT}" > "${TMP_DIR}/cn_ipv4.txt"
-awk 'index($0, ":") != 0 { print }' "${CN_TXT}" > "${TMP_DIR}/cn_ipv6.txt"
-
-render_nft_set ipv4_addr "${TMP_DIR}/cn_ipv4.txt" "${OUT_DIR}/geoip_cn.nft" clashoo_china
-render_nft_set ipv6_addr "${TMP_DIR}/cn_ipv6.txt" "${OUT_DIR}/geoip6_cn.nft" clashoo_china6
-
-echo "generated:"
-echo " ${OUT_DIR}/geoip_cn.nft"
-echo " ${OUT_DIR}/geoip6_cn.nft"
diff --git a/scripts/gen-singbox-rulesets.sh b/scripts/gen-singbox-rulesets.sh
deleted file mode 100755
index ce5a7c805..000000000
--- a/scripts/gen-singbox-rulesets.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/sh
-
-set -eu
-
-ROOT_DIR="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"
-RULESET_DIR="${ROOT_DIR}/clashoo/files/usr/share/clashoo/ruleset"
-TMP_DIR="$(mktemp -d)"
-GEOSITE_BASE="${GEOSITE_BASE:-https://cdn.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite}"
-GEOIP_BASE="${GEOIP_BASE:-https://cdn.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geoip}"
-# mihomo .mrs lives on the meta branch (binary, not .srs)
-META_GEOSITE_BASE="${META_GEOSITE_BASE:-https://cdn.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geosite}"
-
-cleanup() {
- rm -rf "$TMP_DIR"
-}
-
-trap cleanup EXIT INT TERM
-
-fetch_ruleset() {
- tag="$1"
- base="$2"
- remote_name="${3:-$tag}"
- url="${base}/${remote_name}.srs"
- tmp_file="${TMP_DIR}/${tag}.srs"
- out_file="${RULESET_DIR}/${tag}.srs"
-
- echo "fetch ${tag}.srs"
- curl -fsSL --retry 3 --retry-delay 2 "$url" -o "$tmp_file"
- [ -s "$tmp_file" ] || {
- echo "empty ruleset: $url" >&2
- exit 1
- }
- mv "$tmp_file" "$out_file"
-}
-
-mkdir -p "$RULESET_DIR"
-rm -f "${RULESET_DIR}"/*.srs
-
-# Minimal built-in set:
-# - geolocation-!cn: required by fake-ip DNS and broad non-CN routing.
-# - geosite-cn: local alias for subscription tags geolocation-cn/cn.
-# - cn-ip/private-ip: keep China/private IP direct without remote downloads.
-fetch_ruleset "geolocation-!cn" "$GEOSITE_BASE"
-fetch_ruleset "geosite-cn" "$GEOSITE_BASE" geolocation-cn
-fetch_ruleset "private-ip" "$GEOIP_BASE" private
-fetch_ruleset "cn-ip" "$GEOIP_BASE" cn
-
-# mihomo cn.mrs replaces geosite:cn in fake-ip-filter so the core skips geosite.dat
-echo "fetch cn.mrs"
-curl -fsSL --retry 3 --retry-delay 2 "${META_GEOSITE_BASE}/cn.mrs" -o "${TMP_DIR}/cn.mrs"
-[ -s "${TMP_DIR}/cn.mrs" ] || {
- echo "empty ruleset: ${META_GEOSITE_BASE}/cn.mrs" >&2
- exit 1
-}
-mv "${TMP_DIR}/cn.mrs" "${RULESET_DIR}/cn.mrs"
-
-echo "generated sing-box rule sets in ${RULESET_DIR}"
diff --git a/scripts/install.sh b/scripts/install.sh
deleted file mode 100755
index a36bed277..000000000
--- a/scripts/install.sh
+++ /dev/null
@@ -1,281 +0,0 @@
-#!/bin/sh
-
-set -eu
-
-FEED_BASE_URL="https://down.dllkids.xyz/openwrt-feed/clashoo"
-GITHUB_API_URL="https://api.github.com/repos/kenzok8/openwrt-clashoo/releases/latest"
-GITHUB_PROXY_PREFIX="${GITHUB_PROXY_PREFIX:-https://ghfast.top/}"
-TMP_DIR="/tmp/clashoo-install"
-
-fetch_text() {
- url="$1"
- if command -v curl >/dev/null 2>&1; then
- curl -fsSL "$url"
- return $?
- fi
- if command -v wget >/dev/null 2>&1; then
- wget -qO- "$url"
- return $?
- fi
- return 127
-}
-
-download_file() {
- url="$1"
- out="$2"
- if command -v curl >/dev/null 2>&1; then
- curl -fL "$url" -o "$out"
- return $?
- fi
- wget -qO "$out" "$url"
-}
-
-download_url() {
- url="$1"
- case "$url" in
- https://github.com/*)
- printf '%s%s\n' "$GITHUB_PROXY_PREFIX" "$url"
- ;;
- *)
- printf '%s\n' "$url"
- ;;
- esac
-}
-
-detect_manager() {
- if command -v opkg >/dev/null 2>&1; then
- echo opkg
- return
- fi
- if command -v apk >/dev/null 2>&1; then
- echo apk
- return
- fi
- echo "unsupported"
-}
-
-detect_arch() {
- pm="$1"
- if [ "$pm" = "opkg" ]; then
- opkg print-architecture | awk '/^arch / {print $2}' | tail -n 1
- return
- fi
- # apk --print-arch 只回 CPU family(如 aarch64),丢掉 subtarget 后缀;
- # feed/release 产物用完整 target arch(aarch64_cortex-a53),优先读 DISTRIB_ARCH
- distrib_arch="$(sed -n "s/^DISTRIB_ARCH=['\"]\([^'\"]*\)['\"].*/\1/p" /etc/openwrt_release 2>/dev/null | head -n 1)"
- if [ -n "$distrib_arch" ]; then
- printf '%s\n' "$distrib_arch"
- else
- apk --print-arch
- fi
-}
-
-detect_sdk() {
- if [ ! -r /etc/openwrt_release ]; then
- return 1
- fi
-
- release="$(sed -n "s/^DISTRIB_RELEASE=['\"]\\([^'\"]*\\)['\"]$/\\1/p" /etc/openwrt_release | head -n 1)"
- [ -n "$release" ] || return 1
-
- sdk="$(printf '%s\n' "$release" | grep -Eo '[0-9]+\.[0-9]+' | head -n 1)"
- [ -n "$sdk" ] || return 1
- printf '%s\n' "$sdk"
-}
-
-find_feed_filename() {
- package_name="$1"
- packages_text="$2"
- printf '%s\n' "$packages_text" | awk -v pkg="$package_name" '
- $0 == "Package: " pkg {
- in_pkg = 1
- next
- }
- in_pkg && /^Filename: / {
- sub(/^Filename: /, "")
- print
- exit
- }
- in_pkg && /^$/ {
- in_pkg = 0
- }
- '
-}
-
-find_manifest_value() {
- key="$1"
- manifest_text="$2"
- printf '%s\n' "$manifest_text" | sed -n "s/^${key}=//p" | head -n 1
-}
-
-append_unique_word() {
- value="$1"
- list="$2"
- [ -n "$value" ] || {
- printf '%s\n' "$list"
- return
- }
- case " $list " in
- *" $value "*) ;;
- *) list="${list}${list:+ }${value}" ;;
- esac
- printf '%s\n' "$list"
-}
-
-build_sdk_candidates() {
- pm="$1"
- candidates=""
- detected_sdk="$(detect_sdk || true)"
- candidates="$(append_unique_word "$detected_sdk" "$candidates")"
-
- if [ "$pm" = "opkg" ]; then
- for sdk in 24.10 23.05 22.03 21.02; do
- candidates="$(append_unique_word "$sdk" "$candidates")"
- done
- else
- for sdk in 25.12 24.10; do
- candidates="$(append_unique_word "$sdk" "$candidates")"
- done
- fi
-
- printf '%s\n' "$candidates"
-}
-
-# Feed 结构:/// —— 与 dllkids setup.sh 对齐,不嵌 feedname 子目录
-feed_base_for() {
- printf '%s/%s/%s' "$FEED_BASE_URL" "$1" "$2"
-}
-
-load_manifest_urls() {
- sdk="$1"
- arch="$2"
- base="$(feed_base_for "$sdk" "$arch")"
- manifest_url="${base}/manifest-clashoo.txt"
- manifest_text="$(fetch_text "$manifest_url" || true)"
- [ -n "$manifest_text" ] || return 1
-
- core_file="$(find_manifest_value "core" "$manifest_text")"
- luci_file="$(find_manifest_value "luci" "$manifest_text")"
- i18n_file="$(find_manifest_value "i18n" "$manifest_text")"
-
- [ -n "$core_file" ] || return 1
- [ -n "$luci_file" ] || return 1
-
- CORE_URL="${base}/${core_file}"
- LUCI_URL="${base}/${luci_file}"
- I18N_URL=""
- [ -n "$i18n_file" ] && I18N_URL="${base}/${i18n_file}"
- return 0
-}
-
-load_opkg_feed_urls() {
- sdk="$1"
- arch="$2"
- base="$(feed_base_for "$sdk" "$arch")"
- packages_url="${base}/Packages.gz"
- packages_text="$(fetch_text "$packages_url" || true)"
- if ! printf '%s' "$packages_text" | grep -q '^Package: '; then
- return 1
- fi
-
- core_file="$(find_feed_filename "clashoo" "$packages_text")"
- luci_file="$(find_feed_filename "luci-app-clashoo" "$packages_text")"
- i18n_file="$(find_feed_filename "luci-i18n-clashoo-zh-cn" "$packages_text")"
-
- [ -n "$core_file" ] || return 1
- [ -n "$luci_file" ] || return 1
-
- CORE_URL="${base}/${core_file}"
- LUCI_URL="${base}/${luci_file}"
- I18N_URL=""
- [ -n "$i18n_file" ] && I18N_URL="${base}/${i18n_file}"
- return 0
-}
-
-load_github_urls() {
- arch="$1"
- ext="$2"
- payload="$(fetch_text "$GITHUB_API_URL" || true)"
- [ -n "$payload" ] || return 1
-
- urls="$(printf '%s\n' "$payload" | sed -n 's/.*"browser_download_url":[[:space:]]*"\([^"]*\)".*/\1/p')"
- [ -n "$urls" ] || return 1
-
- if [ "$ext" = "apk" ]; then
- CORE_URL="$(printf '%s\n' "$urls" | grep -E '/clashoo-[^-]+.*-r[0-9]+-'"$arch"'\.apk$' | head -n 1)"
- LUCI_URL="$(printf '%s\n' "$urls" | grep -E '/luci-app-clashoo-[^-]+.*-r[0-9]+-('"$arch"'|all)\.apk$' | head -n 1)"
- I18N_URL="$(printf '%s\n' "$urls" | grep -E '/luci-i18n-clashoo-zh-cn-[^-]+.*-r[0-9]+-('"$arch"'|all)\.apk$' | head -n 1)"
- else
- CORE_URL="$(printf '%s\n' "$urls" | grep -E '/clashoo_.*_'"$arch"'\.ipk$' | head -n 1)"
- LUCI_URL="$(printf '%s\n' "$urls" | grep -E '/luci-app-clashoo_.*_all\.ipk$' | head -n 1)"
- I18N_URL="$(printf '%s\n' "$urls" | grep -E '/luci-i18n-clashoo-zh-cn_.*_all\.ipk$' | head -n 1)"
- fi
-
- [ -n "$CORE_URL" ] || return 1
- [ -n "$LUCI_URL" ] || return 1
- return 0
-}
-
-PM="$(detect_manager)"
-if [ "$PM" = "unsupported" ]; then
- echo "No supported package manager found (opkg/apk)."
- exit 1
-fi
-
-ARCH="$(detect_arch "$PM")"
-[ -n "$ARCH" ] || { echo "Cannot detect architecture"; exit 1; }
-
-SDK_CANDIDATES="$(build_sdk_candidates "$PM")"
-
-EXT="ipk"
-[ "$PM" = "apk" ] && EXT="apk"
-
-CORE_URL=""
-LUCI_URL=""
-I18N_URL=""
-
-if [ -n "$SDK_CANDIDATES" ]; then
- for SDK in $SDK_CANDIDATES; do
- if load_manifest_urls "$SDK" "$ARCH"; then
- echo "Using R2 feed manifest: ${SDK}/${ARCH}"
- break
- fi
- if [ "$PM" = "opkg" ] && load_opkg_feed_urls "$SDK" "$ARCH"; then
- echo "Using R2 feed: ${SDK}/${ARCH}"
- break
- fi
- done
-fi
-
-if [ -z "$CORE_URL" ] || [ -z "$LUCI_URL" ]; then
- if load_github_urls "$ARCH" "$EXT"; then
- echo "Using GitHub latest release"
- else
- echo "Cannot find required packages for arch: $ARCH"
- echo "Detected package manager: $PM"
- echo "Tried SDKs: $SDK_CANDIDATES"
- exit 1
- fi
-fi
-
-rm -rf "$TMP_DIR"
-mkdir -p "$TMP_DIR"
-
-echo "Downloading packages..."
-download_file "$(download_url "$CORE_URL")" "$TMP_DIR/core.${EXT}"
-download_file "$(download_url "$LUCI_URL")" "$TMP_DIR/luci.${EXT}"
-if [ -n "$I18N_URL" ]; then
- download_file "$(download_url "$I18N_URL")" "$TMP_DIR/i18n.${EXT}"
-fi
-
-echo "Installing packages with $PM..."
-if [ "$PM" = "opkg" ]; then
- # Refresh package lists so deps (yq, coreutils-nohup, kmod-*) resolve on fresh systems
- opkg update || echo "Warning: opkg update failed, dependency resolution may break"
- opkg install "$TMP_DIR/core.${EXT}" "$TMP_DIR/luci.${EXT}" ${I18N_URL:+"$TMP_DIR/i18n.${EXT}"}
-else
- apk update || echo "Warning: apk update failed, dependency resolution may break"
- apk add --allow-untrusted "$TMP_DIR/core.${EXT}" "$TMP_DIR/luci.${EXT}" ${I18N_URL:+"$TMP_DIR/i18n.${EXT}"}
-fi
-
-echo "Install complete."
diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh
deleted file mode 100755
index 8f65c41fa..000000000
--- a/scripts/uninstall.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/sh
-
-set -eu
-
-echo "Stopping clashoo service..."
-/etc/init.d/clashoo stop >/dev/null 2>&1 || true
-/etc/init.d/clashoo disable >/dev/null 2>&1 || true
-
-core_still_installed() {
- if command -v apk >/dev/null 2>&1; then
- apk list -I 2>/dev/null | grep -q "^clashoo-[0-9]"
- elif command -v opkg >/dev/null 2>&1; then
- opkg list-installed 2>/dev/null | grep -q "^clashoo "
- else
- return 1
- fi
-}
-
-if command -v opkg >/dev/null 2>&1; then
- echo "Removing packages with opkg..."
- opkg remove luci-i18n-clashoo-zh-cn luci-app-clashoo clashoo >/dev/null 2>&1 || true
-elif command -v apk >/dev/null 2>&1; then
- echo "Removing packages with apk..."
- apk del luci-i18n-clashoo-zh-cn luci-app-clashoo clashoo >/dev/null 2>&1 || true
-else
- echo "No supported package manager found (opkg/apk), skip package removal."
-fi
-
-# Always safe: purely runtime/temp files, never owned by the package manager.
-echo "Cleaning runtime temp files..."
-rm -rf /tmp/clashoo /tmp/clashoo-install /tmp/clashoo_* /tmp/luci* 2>/dev/null || true
-
-# CRITICAL: only delete package-owned files (config / init.d / dirs) when the
-# core package was actually removed. If it's still installed - e.g. nikki, which
-# depends on the clashoo-provided `mihomo`, blocks `apk del clashoo` - deleting
-# these behind the package manager's back corrupts apk's state: the db keeps
-# owning files that no longer exist, so a later same-version reinstall only
-# writes `.apk-new` and never restores them, leaving the UI broken
-# ("kernel not installed" / start stuck). Leave them in place instead.
-if core_still_installed; then
- echo "WARNING: 'clashoo' is still installed because other packages depend on it"
- echo " (e.g. nikki depends on the clashoo-provided 'mihomo')."
- echo " Kept /etc/config/clashoo, /etc/init.d/clashoo and /etc/clashoo intact"
- echo " to avoid corrupting package state. Remove nikki first for a full"
- echo " uninstall, or just reinstall clashoo to refresh it."
-else
- echo "Cleaning package files..."
- rm -rf /etc/clashoo /usr/share/clashoo /usr/share/clashbackup 2>/dev/null || true
- rm -f /etc/init.d/clashoo /etc/config/clashoo 2>/dev/null || true
-fi
-
-/etc/init.d/rpcd restart >/dev/null 2>&1 || true
-/etc/init.d/uhttpd restart >/dev/null 2>&1 || true
-/etc/init.d/dnsmasq restart >/dev/null 2>&1 || true
-/etc/init.d/firewall restart >/dev/null 2>&1 || true
-
-echo "Uninstall & reset complete."