update 2026-11-01 17:09:57

This commit is contained in:
action committed 2026-11-01 17:09:57 +08:00
1 parent fd8da734bf
commit a71748a807
210 files changed
+11401 -15592

No files matched your search

+74 -53
View File
@@ -1,79 +1,79 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2026 kenzok8
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=honk PKG_NAME:=honk
PKG_VERSION:=0.1.0_beta4 PKG_VERSION:=2026.11.01
PKG_RELEASE:=1 PKG_RELEASE:=1
# Raw upstream release tag. It keeps the dots in qualifiers (e.g. v0.0.1.beta.49) # Pinned honk-core revision (upstream daeuniverse/honk main), cross-compiled by
# because that is what the GitHub release asset name uses. # ci/build-core.sh. Every release run publishes its core archives under an
HONK_RELEASE_TAG:=v0.1.0-beta.4 # immutable source-<run_id> tag; the tag and SHA-256 values below are the exact
HONK_ASSET_TAG:=v0.1.0-beta.4 # assets behind CORE_COMMIT, so a plain feed build downloads and verifies them
# without any generated file. A staged run may still override them through
# honk/generated-stage.mk, which is generated and never committed.
CORE_COMMIT:=54b2e1cfbe1e7f8a93dba3a0f64fabf2a9f88e38
# SHA256 of each release tarball, kept in sync by scripts/update_honk_version.sh. # Immutable source asset tag holding this pin's core archives.
HONK_HASH_X86_64:=48f694b13b9c5200e8e884ee19431035bd67b0ef01d04caef007047ad11ba02c HONK_SOURCE_TAG?=source-41281523460
HONK_HASH_AARCH64:=6cc92f862a1684ba9c49e9f9c08fb108f53444f338f1e9be84013e2f7fb7423d HONK_SOURCE_URL?=https://github.com/kenzok8/openwrt-honk/releases/download/$(HONK_SOURCE_TAG)
PKG_LICENSE:=AGPL-3.0-only -include $(CURDIR)/generated-stage.mk
PKG_LICENSE_FILE:=LICENSE
PKG_MAINTAINER:=luci-app-honk maintainers
ifeq ($(ARCH),x86_64) HONK_ASSET_X86_64?=honk-core-$(CORE_COMMIT)-x86_64.tar.gz
HONK_TARGET:=x86_64-unknown-linux-musl HONK_ASSET_AARCH64?=honk-core-$(CORE_COMMIT)-aarch64.tar.gz
HONK_ASSET_ARMV7?=honk-core-$(CORE_COMMIT)-armv7.tar.gz
HONK_HASH_X86_64?=a5e46463505cc6ca15d704d77b57eba37b3ffe5d71498af928f052f612724deb
HONK_HASH_AARCH64?=98084529a1186ecc60d0b4882a0d419cc127b7d7c86ad5952ae0eda236709274
HONK_HASH_ARMV7?=e1f2a1dff427220301e4a343202e2c2c2b5efd8e996423ca981c6e0145540d60
# ARCH_PACKAGES is the package arch (e.g. arm_cortex-a7_neon-vfpv4);
# ARCH is the CPU arch (x86_64/aarch64/arm) and coincides for 64-bit.
ifeq ($(ARCH_PACKAGES),x86_64)
HONK_ASSET:=$(HONK_ASSET_X86_64)
HONK_HASH:=$(HONK_HASH_X86_64) HONK_HASH:=$(HONK_HASH_X86_64)
else ifeq ($(ARCH),aarch64) else ifeq ($(filter aarch64%,$(ARCH_PACKAGES)),$(ARCH_PACKAGES))
HONK_TARGET:=aarch64-unknown-linux-musl HONK_ASSET:=$(HONK_ASSET_AARCH64)
HONK_HASH:=$(HONK_HASH_AARCH64) HONK_HASH:=$(HONK_HASH_AARCH64)
else ifeq ($(filter arm_%,$(ARCH_PACKAGES)),$(ARCH_PACKAGES))
HONK_ASSET:=$(HONK_ASSET_ARMV7)
HONK_HASH:=$(HONK_HASH_ARMV7)
endif endif
# mimalloc build: faster QUIC. honk.init sets MIMALLOC_ARENA_RESERVE=0 so the PKG_LICENSE:=AGPL-3.0-only
# 1 GiB arena reservation stays out of VmSize. Use "-stock" to go back. PKG_LICENSE_FILES:=LICENSE
HONK_SUFFIX:= PKG_MAINTAINER:=kenzok8
# Upstream feature variant: -native-api (recommended for OpenWrt gateway), -full, or empty for legacy releases.
HONK_VARIANT:=-native-api
HONK_ASSET:=honk-core-$(HONK_ASSET_TAG)-$(HONK_TARGET)$(HONK_SUFFIX)$(HONK_VARIANT).tar.gz
PKG_SOURCE:=$(HONK_ASSET) PKG_SOURCE:=$(HONK_ASSET)
PKG_SOURCE_URL:=https://github.com/daeuniverse/honk/releases/download/$(HONK_RELEASE_TAG)/ PKG_SOURCE_URL?=$(HONK_SOURCE_URL)/
PKG_HASH:=$(HONK_HASH) PKG_HASH:=$(HONK_HASH)
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/package.mk
define Package/honk define Package/honk
SECTION:=net SECTION:=net
CATEGORY:=Network CATEGORY:=Network
SUBMENU:=Web Servers/Proxies SUBMENU:=Web Servers/Proxies
TITLE:=honk - eBPF transparent proxy engine (dae-compatible) TITLE:=Honk transparent proxy core
URL:=https://github.com/daeuniverse/honk URL:=https://github.com/daeuniverse/honk
DEPENDS:=@(x86_64||aarch64) \ # 不直接依赖 libubox:包名带 ABI 日期戳,会锁死固件;脚本只用 jshn 提供的 jshn.sh。
+ca-bundle +ip-full \ DEPENDS:=@(x86_64||aarch64||arm) +ca-bundle +ip-full +ubus +kmod-sched-core +kmod-sched-bpf +kmod-veth +kmod-nft-queue +jsonfilter +jshn +sqlite3-cli +usign +coreutils-stat +v2ray-geoip +v2ray-geosite +@KERNEL_DEBUG_INFO_BTF
+kmod-sched-core +kmod-sched-bpf +kmod-veth +kmod-nft-queue \
+v2ray-geoip +v2ray-geosite \
+@KERNEL_XDP_SOCKETS \
+HONK_USE_VMLINUX_BTF:vmlinux-btf
endef endef
define Package/honk/config define Package/honk/description
choice Honk is a transparent proxy core for OpenWrt (x86_64, aarch64, armv7).
prompt "BTF source for CO-RE"
default HONK_USE_KERNEL_BTF
depends on PACKAGE_honk
config HONK_USE_KERNEL_BTF
bool "Use kernel BTF (integrated)"
depends on KERNEL_DEBUG_INFO_BTF
config HONK_USE_VMLINUX_BTF
bool "Use vmlinux-btf package"
endchoice
endef endef
define Package/honk/conffiles define Package/honk/conffiles
/etc/honk/config.dae
/etc/honk/config.d/
/etc/honk/state/
/etc/config/honk /etc/config/honk
/etc/honk/config.dae
/etc/honk/system.dae
/etc/honk/manual-config.dae
/etc/honk/managed-config.dae
endef endef
define Build/Prepare define Build/Prepare
@@ -82,25 +82,46 @@ define Build/Prepare
endef endef
define Build/Compile define Build/Compile
true
endef endef
define Package/honk/install define Package/honk/install
$(INSTALL_DIR) $(1)/usr/bin $(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/honk-core $(1)/usr/bin/honk-core $(INSTALL_BIN) $(PKG_BUILD_DIR)/honk-core $(1)/usr/bin/honk-core
$(INSTALL_DIR) $(1)/etc/honk/config.d $(INSTALL_DIR) $(1)/usr/share/licenses/honk
$(INSTALL_CONF) $(CURDIR)/files/config.dae $(1)/etc/honk/config.dae $(INSTALL_DATA) $(PKG_BUILD_DIR)/LICENSE $(1)/usr/share/licenses/honk/LICENSE
$(INSTALL_CONF) $(CURDIR)/files/config.d/node.dae $(1)/etc/honk/config.d/node.dae
$(INSTALL_CONF) $(CURDIR)/files/config.d/route.dae $(1)/etc/honk/config.d/route.dae
$(INSTALL_CONF) $(CURDIR)/files/config.d/dns.dae $(1)/etc/honk/config.d/dns.dae
$(INSTALL_CONF) $(CURDIR)/files/config.d/api.dae $(1)/etc/honk/config.d/api.dae
$(INSTALL_DIR) $(1)/etc/config $(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) $(CURDIR)/files/honk.config $(1)/etc/config/honk $(INSTALL_CONF) $(CURDIR)/files/honk.config $(1)/etc/config/honk
$(INSTALL_DIR) $(1)/etc/honk
$(INSTALL_CONF) $(CURDIR)/files/config.dae $(1)/etc/honk/config.dae
$(INSTALL_CONF) $(CURDIR)/files/system.dae $(1)/etc/honk/system.dae
chmod 0600 $(1)/etc/honk/system.dae
$(INSTALL_DIR) $(1)/etc/honk/state
chmod 0700 $(1)/etc/honk/state
$(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(CURDIR)/files/honk.init $(1)/etc/init.d/honk $(INSTALL_BIN) $(CURDIR)/files/honk.init $(1)/etc/init.d/honk
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) $(CURDIR)/files/90-honk-boot $(1)/etc/uci-defaults/90-honk
$(INSTALL_DIR) $(1)/usr/share/honk
$(INSTALL_DATA) $(CURDIR)/files/config.dae $(1)/usr/share/honk/default-config.dae
# Provenance is only generated by a source build; a plain feed build has
# no such file and must still install cleanly.
if [ -f $(CURDIR)/generated-provenance.json ]; then \
$(INSTALL_DATA) $(CURDIR)/generated-provenance.json $(1)/usr/share/honk/provenance.json; \
fi
$(INSTALL_BIN) $(CURDIR)/files/control.sh $(1)/usr/share/honk/control.sh
$(INSTALL_BIN) $(CURDIR)/files/lock.sh $(1)/usr/share/honk/lock.sh
$(INSTALL_BIN) $(CURDIR)/files/lifecycle.sh $(1)/usr/share/honk/lifecycle.sh
$(INSTALL_BIN) $(CURDIR)/files/maintenance.sh $(1)/usr/share/honk/maintenance.sh
$(INSTALL_BIN) $(CURDIR)/files/job.sh $(1)/usr/share/honk/job.sh
$(INSTALL_BIN) $(CURDIR)/files/update.sh $(1)/usr/share/honk/update.sh
$(INSTALL_BIN) $(CURDIR)/files/redact-log.sh $(1)/usr/share/honk/redact-log.sh
$(INSTALL_BIN) $(CURDIR)/files/log-level-filter.sh $(1)/usr/share/honk/log-level-filter.sh
$(INSTALL_DATA) $(CURDIR)/files/log-lines.sh $(1)/usr/share/honk/log-lines.sh
endef endef
$(eval $(call BuildPackage,honk)) $(eval $(call BuildPackage,honk))
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
# 开机自启是内置行为:装上就 enable,是否真正启动由 honk.main.enabled 决定,
# 不再提供单独的 boot_enabled 开关。
/etc/init.d/honk enable
exit 0
View File
Whitespace-only changes.
-24
View File
@@ -1,24 +0,0 @@
# dns.dae
dns {
#bind: 'tcp+udp://[::]:53530'
client_subnet: auto
max_cache_size: 50000
#ipversion_prefer: 4
upstream {
local: 'udp://127.0.0.1:53' -> direct
remote: 'tcp+udp://dns.google:53'
}
routing {
request {
qtype(https) -> reject
qname(geosite: gfw) -> remote
fallback: local
}
response {
upstream(remote) -> accept
qname(geosite: private) -> accept
ip(geoip: cn) -> accept
fallback: remote
}
}
}
-35
View File
@@ -1,35 +0,0 @@
# node.dae
node {
node1: 'xxx'
node2: 'xxx'
}
subscription {
# 短形式:默认 86400s 刷新一次
sub: 'http://sub'
# 短形式 + UA:仍然只能默认 86400s
sub_ua: 'http://sub'(honk/1.0 like)
# 块形式:可以自定义定时刷新周期
sub2: {
url: 'http://sub'
ua: 'honk/1.0'
interval: '10000s' # 每 10000 秒刷新一次
}
# 禁用定期刷新
sub3: {
url: 'http://sub'
interval: 0
}
}
group {
proxy {
filter: subtag(sub) && !name(keyword: 'ExpireAt:')
filter: name(node1, node2)
policy: score
}
}
-27
View File
@@ -1,27 +0,0 @@
# route.dae
routing {
pname(dnsmasq, zerotier-one) -> direct(must)
dip(224.0.0.0/3, 'ff00::/8', geoip: private) -> direct
domain(geosite: category-bank-cn, geosite: boc@!cn, geosite: synology) -> direct
# block quic, if u node has poor quic support, enable this line
# l4proto(udp) && dport(443) -> block
domain(geosite: spotify) -> proxy
domain(geosite: category-doh) -> proxy
domain(geosite: tiktok) -> proxy
domain(geosite: category-ai-!cn, geosite: github) -> proxy
domain(geosite: gfw) -> proxy
dip(geoip: telegram) -> proxy
dip(geoip: cn) -> direct
# geoip:cn always using chiness asn, can use domain rule to override
# domain(geosite: cn) -> direct
# using proxy for ssh, http, https, and common ports, avoid (bt, or other p2p)
# dport(22, 80, 443, 8080, 6443, 9090) -> proxy
# fallback: direct
fallback: proxy
}
+29 -26
View File
@@ -1,31 +1,34 @@
# config.dae
# Load all honk files placed in ./config.d/.
include { include {
config.d/*.dae system.dae
} }
global { group {
log_level: warn proxy {
check_interval: 600s policy: min_moving_avg
check_tolerance: 30ms
lan_interface: br-lan
wan_interface: auto
auto_config_kernel_parameter: true
disable_waiting_network: false
data_dir: '/etc/honk'
log_file: '/var/log/honk/honk.log'
store_subscribe: true
# if u not using dns bind, set dial_mode to domain++ prefer
dial_mode: domain
bootstrap_resolver: '223.5.5.5:53'
tcp_check_url: 'https://www.gstatic.com/generate_204'
tcp_check_http_method: HEAD
udp_check_dns: 'dns.google.com:53', '8.8.8.8', '2001:4860:4860::8888'
}
assets {
geodata {
geosite: 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/release/geosite.dat'
geoip: 'https://raw.githubusercontent.com/QiuSimons/geoip-moedove/refs/heads/main/geoip.dat'
} }
} }
dns {
upstream {
default: 'https://223.5.5.5/dns-query?tls_server_name=dns.alidns.com' -> direct
remote: 'https://8.8.8.8/dns-query?tls_server_name=dns.google' -> proxy
}
routing {
request {
qname(geosite: private) -> asis
qname(geosite: cn) -> default
fallback: remote
}
}
}
routing {
dip(224.0.0.0/3, 'ff00::/8') -> direct
# 路由器自身地址直连,管理面板不被代理;服务启动时按本机地址重写下面区块
# honk:local-direct-begin
# honk:local-direct-end
dip(geoip: private) && !dport(53) -> direct(must)
dip(geoip: cn) && !dport(53) -> direct(must)
domain(geosite: cn) -> direct
fallback: direct
}
+984
View File
@@ -0,0 +1,984 @@
#!/bin/sh
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
HONK_CORE=/usr/bin/honk-core
HONK_MAINT_DIR=/etc/.honk-maintenance
HONK_MAINT_JOURNAL=$HONK_MAINT_DIR/journal
HONK_MAINT_CAP_FILE=$HONK_MAINT_DIR/capability
. /lib/functions.sh
. /usr/share/honk/lock.sh
. /usr/share/honk/lifecycle.sh
fail() {
echo "honk: $*" >&2
return 1
}
load_settings() {
config_load honk
config_get_bool enabled main enabled 0
config_get_bool boot_enabled main boot_enabled 0
config_get_bool initialized main initialized 0
config_get config_mode main config_mode managed
config_get config_file main config_file /etc/honk/config.dae
config_get listen_port main listen_port 9527
config_get_bool clash_api_enabled main clash_api_enabled 0
config_get clash_api_port main clash_api_port 9090
config_get clash_api_secret main clash_api_secret ''
config_get lan_network main lan_network lan
}
valid_clash_api() {
[ "$clash_api_enabled" = 1 ] || return 0
case "$clash_api_port" in ''|*[!0-9]*) return 1 ;; esac
[ "$clash_api_port" -ge 1024 ] && [ "$clash_api_port" -le 65535 ] || return 1
[ "$clash_api_port" != "$listen_port" ] || return 1
case "$clash_api_secret" in ''|*[!A-Za-z0-9._-]*) return 1 ;; esac
[ "${#clash_api_secret}" -ge 6 ] && [ "${#clash_api_secret}" -le 128 ]
}
valid_network_name() {
case "$lan_network" in
''|*[!A-Za-z0-9_.-]*) return 1 ;;
*) return 0 ;;
esac
}
module_loaded() {
[ -d "/sys/module/$1" ] || grep -q "^$1 " /proc/modules
}
module_available() {
module_loaded "$1" && return 0
module_path=$(printf '%s' "$1" | tr '_' '-')
module_dir=/lib/modules/$(uname -r)
[ -r "$module_dir/modules.builtin" ] && grep -Eq "/${1}\.ko$|/${module_path}\.ko$" "$module_dir/modules.builtin" && return 0
find "$module_dir" -type f \( -name "$1.ko" -o -name "$1.ko.gz" -o -name "$1.ko.xz" -o -name "$1.ko.zst" \
-o -name "$module_path.ko" -o -name "$module_path.ko.gz" -o -name "$module_path.ko.xz" -o -name "$module_path.ko.zst" \) -print 2>/dev/null | grep -q .
}
system_config_readonly() {
permissions=$(ls -ldn /etc/honk/system.dae 2>/dev/null | awk '{print $1}')
[ "$permissions" = '-rw-------' ] && return 0
[ "$permissions" = '-r--r--r--' ] && ! grep -q 'clash_api {' /etc/honk/system.dae
}
no_honk_resources() {
# honk-core removes dae0/dae0peer and the daens netns when it exits cleanly. A
# crash or SIGKILL leaves them behind, and preflight then refuses every later
# start, so the service can never come back without hand surgery. Reclaim them
# here, but only when no core is running to own them.
if ! pidof honk-core >/dev/null 2>&1; then
for stale_link in dae0 dae0peer; do
ip link show "$stale_link" >/dev/null 2>&1 && ip link del "$stale_link" >/dev/null 2>&1
done
if ip netns list 2>/dev/null | awk '{print $1}' | grep -qx daens; then
ip netns del daens >/dev/null 2>&1
fi
fi
for link in dae0 dae0peer; do
ip link show "$link" >/dev/null 2>&1 && { fail "$link 已存在(上次没有正常退出),请先停止服务或执行系统恢复"; return 1; }
done
ip netns list 2>/dev/null | awk '{print $1}' | grep -qx daens && { fail "daens 网络命名空间已存在,请先停止服务或执行系统恢复"; return 1; }
return 0
}
check_port() {
checked_port=${1:-$listen_port}
if command -v netstat >/dev/null 2>&1; then
netstat -lnt 2>/dev/null | awk '{print $4}' | grep -Eq "(^|:)${checked_port}$" && { fail "监听端口已被占用,请换一个端口"; return 1; }
elif command -v ss >/dev/null 2>&1; then
ss -lnt 2>/dev/null | awk '{print $4}' | grep -Eq "(^|:)${checked_port}$" && { fail "监听端口已被占用,请换一个端口"; return 1; }
else
fail "缺少 netstat/ss,无法检查端口占用(装 ip-full 或 net-tools)"
return 1
fi
return 0
}
check_network() {
valid_network_name || { fail "LAN 网络名包含非法字符"; return 1; }
command -v ubus >/dev/null 2>&1 || { fail "ubus 不可用,无法校验网络设置"; return 1; }
ubus call "network.interface.$lan_network" status >/dev/null 2>&1 || { fail "选择的 LAN 网络在 netifd 里不存在,请在服务设置里重新选择"; return 1; }
}
# 本机 LAN/WAN 设备地址(跳过链路本地),与 honk 的 local_direct_cidrs 对齐
local_direct_addresses() {
list=''
. /lib/functions/network.sh
for net in "$lan_network" wan; do
network_get_device dev "$net" || continue
case "$dev" in ''|*[!A-Za-z0-9_.-]*) continue ;; esac
for addr in $(ip -o addr show dev "$dev" 2>/dev/null | awk '/inet6? / { split($4, a, "/"); print a[1] }'); do
case "$addr" in fe80:*|169.254.*) continue ;; esac
case "$addr" in ''|*[!0-9a-fA-F:.]*) continue ;; esac
[ "${#addr}" -le 45 ] || continue
case " $list " in *" $addr "*) continue ;; esac
list="${list:+$list, }$addr"
done
done
printf '%s\n' "$list"
}
# 把标记区块换成当前本机地址,honk 的 LAN self-protection 检查才认
sync_local_direct() {
begin=' # honk:local-direct-begin'
end=' # honk:local-direct-end'
grep -qxF "$begin" /etc/honk/config.dae || return 0
grep -qxF "$end" /etc/honk/config.dae || return 0
ips=$(local_direct_addresses)
[ -n "$ips" ] && rule=" dip($ips) && !dport(53) -> direct(must)" || rule=''
# honk-core 只认 .dae 后缀,临时文件必须带它
temp=/etc/honk/.config.$$.dae
( umask 077; : > "$temp" ) || return 1
awk -v begin="$begin" -v end="$end" -v rule="$rule" '
$0 == begin { print; if (rule != "") print rule; skip = 1; next }
skip && $0 == end { print; skip = 0; next }
skip { next }
{ print }
' /etc/honk/config.dae > "$temp" || { rm -f "$temp"; return 1; }
cmp -s "$temp" /etc/honk/config.dae && { rm -f "$temp"; return 0; }
"$HONK_CORE" config validate --config "$temp" >/dev/null 2>&1 || { rm -f "$temp"; return 1; }
chmod 0600 "$temp"
mv -f "$temp" /etc/honk/config.dae || { rm -f "$temp"; return 1; }
}
check_config() {
[ "$config_file" = /etc/honk/config.dae ] || { fail "配置路径必须为 /etc/honk/config.dae"; return 1; }
[ -f /etc/honk/config.dae ] && [ ! -L /etc/honk/config.dae ] || { fail "/etc/honk/config.dae 不是普通文件(可能被替换成软链)"; return 1; }
case "$config_mode" in
manual)
"$HONK_CORE" config validate --config /etc/honk/config.dae >/dev/null || { fail "手动模式的 config.dae 未通过静态校验,请先修正配置"; return 1; }
return 0
;;
managed) ;;
*) fail "配置模式取值非法(应为 managed 或 manual)"; return 1 ;;
esac
[ -f /etc/honk/system.dae ] && [ ! -L /etc/honk/system.dae ] || { fail "/etc/honk/system.dae 不是普通文件(可能被替换成软链)"; return 1; }
system_config_readonly || { fail "/etc/honk/system.dae 权限不对,应为 0600"; return 1; }
[ -r /usr/share/doona/index.html ] || { fail "缺少 Doona 静态文件(/usr/share/doona/index.html),请重新安装 luci-app-honk"; return 1; }
case "$listen_port" in
''|*[!0-9]*) fail "监听端口必须是数字"; return 1 ;;
esac
[ "$listen_port" -ge 1024 ] && [ "$listen_port" -le 65535 ] || { fail "监听端口必须在 1024-65535 之间"; return 1; }
valid_clash_api || { fail "Clash API 设置不合法(端口 1024-65535,密钥 6-128 位)"; return 1; }
}
check_kernel() {
kernel=$(uname -r | cut -d- -f1)
major=$(printf '%s' "$kernel" | cut -d. -f1)
minor=$(printf '%s' "$kernel" | cut -d. -f2)
[ "$major" -gt 6 ] || { [ "$major" -eq 6 ] && [ "$minor" -ge 12 ]; } || { fail "内核版本过低,需要 Linux 6.12 或更新"; return 1; }
[ -r /sys/kernel/btf/vmlinux ] || { fail "内核缺少 BTF(/sys/kernel/btf/vmlinux),请换用带 BTF 的固件"; return 1; }
[ -d /sys/fs/bpf ] && grep -q ' /sys/fs/bpf bpf ' /proc/mounts || { fail "bpffs 未挂载到 /sys/fs/bpf,请检查内核或手工挂载"; return 1; }
command -v ip >/dev/null 2>&1 || { fail "缺少 ip 命令(装 ip-full)"; return 1; }
command -v modprobe >/dev/null 2>&1 || { fail "缺少 modprobe 命令(装 kmod)"; return 1; }
[ -r /sys/kernel/btf/vmlinux ] || { fail "内核缺少 BTF(/sys/kernel/btf/vmlinux),请换用带 BTF 的固件"; return 1; }
for module in sch_ingress cls_bpf veth nft_queue nfnetlink_queue; do
module_available "$module" || { fail "缺少内核模块 $module,请确认 kmod-sched-core / kmod-sched-bpf / kmod-veth / kmod-nft-queue 已安装"; return 1; }
done
}
check_conflicts() {
for process in dae daed clashoo mihomo sing-box; do
pidof "$process" >/dev/null 2>&1 && { fail "检测到冲突进程 $process 正在运行,请先停掉它(多代理同时开会互相抢流量)"; return 1; }
done
for service in dae daed clashoo mihomo sing-box; do
if [ -x "/etc/init.d/$service" ] && "/etc/init.d/$service" running >/dev/null 2>&1; then
fail "检测到冲突服务 $service 正在运行,请先停用它(多代理同时开会互相抢流量)"
return 1
fi
done
}
maintenance_value() {
awk -F= -v key="$1" '$1 == key { value = substr($0, length($1) + 2); count++ } END { if (count == 1) print value; else exit 1 }' "$2"
}
proc_starttime() {
[ -r "/proc/$1/stat" ] || return 1
awk '{ sub(/^.*\) /, ""); split($0, fields, /[[:space:]]+/); if (fields[20] ~ /^[0-9]+$/) print fields[20]; else exit 1 }' "/proc/$1/stat" 2>/dev/null
}
protected_root_file() {
[ -f "$1" ] && [ ! -L "$1" ] || return 1
protected_permissions=$(ls -ln "$1" 2>/dev/null | awk 'NR == 1 { print $1 ":" $3 ":" $4 }')
[ "$protected_permissions" = '-rw-------:0:0' ]
}
consume_maintenance_capability() {
[ -n "${HONK_MAINT_CAP:-}" ] || return 1
[ -d "$HONK_MAINT_DIR" ] && [ ! -L "$HONK_MAINT_DIR" ] || return 1
protected_root_file "$HONK_MAINT_JOURNAL" && protected_root_file "$HONK_MAINT_CAP_FILE" || return 1
awk -F= 'BEGIN { split("format nonce txn_id phase owner_pid owner_starttime", keys, " ") } NR > 6 || index($0, "=") == 0 || $1 != keys[NR] { exit 1 } END { if (NR != 6) exit 1 }' "$HONK_MAINT_CAP_FILE" || return 1
consumed_cap=$HONK_MAINT_DIR/.capability-consumed.$$
[ ! -e "$consumed_cap" ] || return 1
mv "$HONK_MAINT_CAP_FILE" "$consumed_cap" 2>/dev/null || return 1
# Chain with any existing EXIT trap instead of overwriting it.
_prev_exit_trap=$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")
trap 'rm -f "$consumed_cap"; [ -n "$_prev_exit_trap" ] && eval "$_prev_exit_trap"' 0
cap_format=$(maintenance_value format "$consumed_cap") || return 1
cap_nonce=$(maintenance_value nonce "$consumed_cap") || return 1
cap_txn=$(maintenance_value txn_id "$consumed_cap") || return 1
cap_phase=$(maintenance_value phase "$consumed_cap") || return 1
cap_pid=$(maintenance_value owner_pid "$consumed_cap") || return 1
cap_starttime=$(maintenance_value owner_starttime "$consumed_cap") || return 1
journal_format=$(maintenance_value format "$HONK_MAINT_JOURNAL") || return 1
journal_operation=$(maintenance_value operation "$HONK_MAINT_JOURNAL") || return 1
journal_phase=$(maintenance_value phase "$HONK_MAINT_JOURNAL") || return 1
journal_txn=$(maintenance_value txn_id "$HONK_MAINT_JOURNAL") || return 1
case "$cap_nonce:$cap_txn:$cap_pid:$cap_starttime" in *[!0-9a-f:]*|::*|:|'' ) return 1 ;; esac
case "$cap_nonce" in *[!0-9a-f]*|'') return 1 ;; esac
[ "${#cap_nonce}" -eq 32 ] && [ "${#cap_txn}" -eq 32 ] || return 1
[ "$cap_format" = 1 ] && [ "$journal_format" = 1 ] || return 1
case "$cap_pid:$cap_starttime" in *[!0-9:]*) return 1 ;; esac
[ "$cap_pid" -gt 1 ] && [ "$cap_starttime" -gt 0 ] || return 1
[ "$HONK_MAINT_CAP" = "$cap_nonce" ] && [ "$cap_txn" = "$journal_txn" ] || return 1
case "$cap_phase:$journal_operation:$journal_phase" in
candidate_health_start:restore:new_installed|candidate_health_start:import_apply:new_installed|rollback_old_start:restore:rollback_old_start|rollback_old_start:reset:rollback_old_start|rollback_old_start:import_apply:rollback_old_start) ;;
*) return 1 ;;
esac
read -r lock_pid lock_starttime < "$HONK_LOCK_DIR/pid" || return 1
[ "$lock_pid:$lock_starttime" = "$cap_pid:$cap_starttime" ] || return 1
[ "$(proc_starttime "$cap_pid")" = "$cap_starttime" ] || return 1
return 0
}
maintenance_start_guard() {
if [ -e "$HONK_MAINT_JOURNAL" ] || [ -L "$HONK_MAINT_JOURNAL" ]; then
consume_maintenance_capability || { fail "maintenance_recovery_required"; return 1; }
elif [ -n "${HONK_MAINT_CAP:-}" ]; then
fail "maintenance_capability_without_journal"
return 1
fi
if lock_owner_invalid || honk_lock_is_stale; then
# The recorded owner is gone, so nothing is in flight. Reclaim the lock
# instead of refusing every later start: a caller that timed out cannot
# release it, and leaving it wedges the service until someone SSHes in.
# A live lock still blocks below through honk_lock_acquire.
logger -t honk "reclaiming stale operation lock" 2>/dev/null || :
rm -rf "$HONK_LOCK_DIR"
fi
}
lock_owner_invalid() {
[ -d "$HONK_LOCK_DIR" ] || return 1
lock_owner=$(cat "$HONK_LOCK_DIR/pid" 2>/dev/null)
# pid file now stores "pid starttime"; only the leading PID matters here.
lock_owner=${lock_owner%% *}
case "$lock_owner" in ''|*[!0-9]*) return 0 ;; esac
return 1
}
preflight() {
load_settings
[ "$enabled" = 1 ] && [ "$initialized" = 1 ] || { fail "请先在 LuCI 里启用并完成初始化"; return 1; }
if [ "$config_mode" = manual ]; then
maintenance_start_guard && check_config && check_kernel && no_honk_resources && check_conflicts
return $?
fi
maintenance_start_guard && check_config && check_network && check_kernel && no_honk_resources && check_conflicts && check_port && {
[ "$clash_api_enabled" = 0 ] || check_port "$clash_api_port"
}
}
prepare_runtime() {
preflight || return 1
for module in sch_ingress cls_bpf veth nft_queue nfnetlink_queue; do
module_loaded "$module" || modprobe "$module" || { fail "无法加载内核模块 $module,请确认对应 kmod 包已安装"; return 1; }
done
mkdir -p /etc/honk/state || { fail "无法创建状态目录(/etc/honk/state),请检查磁盘空间与权限"; return 1; }
chmod 0700 /etc/honk/state || return 1
}
valid_username() {
case "$1" in
''|*[!A-Za-z0-9_.-]*) return 1 ;;
esac
[ "$(printf '%s' "$1" | wc -c)" -le 64 ]
}
valid_password() {
password_bytes=$(printf '%s' "$1" | wc -c)
[ "$password_bytes" -ge 8 ] && [ "$password_bytes" -le 512 ]
}
read_credentials() {
json_file=$(mktemp /tmp/honk-credentials.XXXXXX) || return 1
chmod 0600 "$json_file" || { rm -f "$json_file"; return 1; }
cat > "$json_file" || { rm -f "$json_file"; return 1; }
json_bytes=$(wc -c < "$json_file")
[ "$json_bytes" -le 2048 ] || { rm -f "$json_file"; return 1; }
grep -Fq '\u0000' "$json_file" && { rm -f "$json_file"; return 1; }
newline='
'
username_raw=$(jsonfilter -i "$json_file" -e '@.username' 2>/dev/null; printf '__HNK_END__')
password_raw=$(jsonfilter -i "$json_file" -e '@.password' 2>/dev/null; printf '__HNK_END__')
username=${username_raw%__HNK_END__}
password=${password_raw%__HNK_END__}
username=${username%"$newline"}
password=${password%"$newline"}
rm -f "$json_file"
[ -n "$username" ] && [ -n "$password" ] || return 1
valid_username "$username" && valid_password "$password"
}
network_settings() {
. /lib/functions/network.sh
network_get_device lan_device "$lan_network" || return 1
network_get_ipaddr lan_ip "$lan_network" || return 1
case "$lan_device" in
''|*[!A-Za-z0-9_.-]*) return 1 ;;
esac
[ "$(printf '%s' "$lan_device" | wc -c)" -le 15 ] || return 1
printf '%s\n' "$lan_ip" | awk -F. 'NF == 4 { for (i=1;i<=4;i++) if ($i !~ /^[0-9]+$/ || $i > 255) exit 1; exit 0 } { exit 1 }' || return 1
}
ensure_loopback_system() {
current_hash=$(sha256sum /etc/honk/system.dae 2>/dev/null | awk '{print $1}')
[ "$current_hash" = "$system_written_hash" ] || { fail "初始化期间 system.dae 被其他进程改动,已中止"; return 1; }
current_listen=$(sed -n "s/^[[:space:]]*listen:[[:space:]]*'\([^']*\)'.*/\1/p" /etc/honk/system.dae | head -n 1)
[ "$current_listen" = "127.0.0.1:$listen_port" ] && return 0
temp_system=$(mktemp /etc/honk/.system.dae.XXXXXX) || return 1
sed "s/^[[:space:]]*listen:[[:space:]]*'[^']*'.*/ listen: '127.0.0.1:$listen_port'/" /etc/honk/system.dae > "$temp_system" || { rm -f "$temp_system"; return 1; }
chmod 0600 "$temp_system" || { rm -f "$temp_system"; return 1; }
current_hash=$(sha256sum /etc/honk/system.dae 2>/dev/null | awk '{print $1}')
[ "$current_hash" = "$system_written_hash" ] || { rm -f "$temp_system"; fail "初始化期间 system.dae 被其他进程改动,已中止"; return 1; }
mv -f "$temp_system" /etc/honk/system.dae || { rm -f "$temp_system"; return 1; }
system_written_hash=$(sha256sum /etc/honk/system.dae | awk '{print $1}')
}
restore_system() {
[ -n "$system_written_hash" ] || return 0
current_hash=$(sha256sum /etc/honk/system.dae 2>/dev/null | awk '{print $1}')
[ "$current_hash" = "$system_written_hash" ] || { fail "回滚期间 system.dae 被其他进程改动,已中止"; return 1; }
[ "$current_hash" != "$original_system_hash" ] || { system_written_hash=""; return 0; }
if [ -z "$original_system_hash" ]; then
rm -f /etc/honk/system.dae || return 1
system_written_hash=""
return 0
fi
temp_restore=$(mktemp /etc/honk/.system.dae.restore.XXXXXX) || return 1
cp -p "$old_system" "$temp_restore" || { rm -f "$temp_restore"; return 1; }
current_hash=$(sha256sum /etc/honk/system.dae 2>/dev/null | awk '{print $1}')
[ "$current_hash" = "$system_written_hash" ] || { rm -f "$temp_restore"; fail "回滚期间 system.dae 被其他进程改动,已中止"; return 1; }
mv -f "$temp_restore" /etc/honk/system.dae || { rm -f "$temp_restore"; return 1; }
system_written_hash=""
}
cleanup_initialize() {
if mock_stop >/dev/null 2>&1; then
if [ -n "$system_written_hash" ] && ! restore_system; then
logger -t honk "system config rollback refused or failed; inspect /etc/honk/system.dae"
fi
rm -rf "$temp_dir"
honk_lock_release >/dev/null 2>&1
else
logger -t honk "mock process cleanup failed; keeping operation lock and temporary files"
fi
}
write_runtime_system() {
lan_device=$1
lan_ip=$2
system_port=${3:-$listen_port}
system_listen=${4:-$lan_ip}
valid_clash_api || { fail "Clash API 设置不合法(端口 1024-65535,密钥 6-128 位)"; return 1; }
current_hash=$(sha256sum /etc/honk/system.dae 2>/dev/null | awk '{print $1}')
[ "$current_hash" = "$system_written_hash" ] || { fail "写入运行配置时 system.dae 被其他进程改动,已中止"; return 1; }
temp_system=$(mktemp /etc/honk/.system.dae.XXXXXX) || return 1
{
printf "global {\n wan_interface: auto\n lan_interface: '%s'\n" "$lan_device"
printf " log_level: info\n dial_mode: domain\n allow_insecure: false\n"
printf " auto_config_kernel_parameter: true\n data_dir: '/etc/honk'\n"
printf " store_subscribe: true\n nfqueue_enable: true\n}\n\n"
printf "experimental {\n native_api {\n enabled: true\n listen: '%s:%s'\n" "$system_listen" "$system_port"
printf " password_auth: true\n allow_anonymous_loopback: false\n"
printf " config_write: true\n ui: '/usr/share/doona'\n }\n"
if [ "$clash_api_enabled" = 1 ]; then
printf " clash_api {\n external_controller: '%s:%s'\n external_ui: '/usr/share/honk/zashboard'\n secret: '%s'\n default_mode: Rule\n }\n" "$system_listen" "$clash_api_port" "$clash_api_secret"
fi
printf "}\n"
} > "$temp_system" || { rm -f "$temp_system"; return 1; }
chmod 0600 "$temp_system" || { rm -f "$temp_system"; return 1; }
current_hash=$(sha256sum /etc/honk/system.dae 2>/dev/null | awk '{print $1}')
[ "$current_hash" = "$system_written_hash" ] || { rm -f "$temp_system"; fail "写入运行配置时 system.dae 被其他进程改动,已中止"; return 1; }
mv -f "$temp_system" /etc/honk/system.dae || { rm -f "$temp_system"; return 1; }
system_written_hash=$(sha256sum /etc/honk/system.dae | awk '{print $1}')
}
managed_core_pid() {
honk_lifecycle_managed_pid
}
configured_system_port() {
sed -n "s/^[[:space:]]*listen:[[:space:]]*'[^:]*:\([0-9][0-9]*\)'.*/\1/p" /etc/honk/system.dae 2>/dev/null | head -n 1
}
configured_clash_port() {
sed -n "s/^[[:space:]]*external_controller:[[:space:]]*'[^:]*:\([0-9][0-9]*\)'.*/\1/p" /etc/honk/system.dae 2>/dev/null | head -n 1
}
sync_boot_link() {
if [ "$1" = 1 ]; then
/etc/init.d/honk enable
else
/etc/init.d/honk disable
fi
}
restore_old_runtime() {
[ "$old_running" = 1 ] || return 0
runtime_enabled=$(uci -q get honk.main.enabled || echo 0)
if [ "$runtime_enabled" != 1 ]; then uci set honk.main.enabled=1 && uci commit honk || return 1; fi
honk_lifecycle_start 30 || {
[ "$runtime_enabled" = 1 ] || { uci set "honk.main.enabled=$runtime_enabled" && uci commit honk; }
return 1
}
if [ "$runtime_enabled" != 1 ]; then uci set "honk.main.enabled=$runtime_enabled" && uci commit honk || return 1; fi
}
settings_rollback() {
rollback_ok=1
honk_lifecycle_stop 30 || return 1
if [ -n "$system_written_hash" ] && ! restore_system; then rollback_ok=0; fi
if ! uci import honk < "$old_uci" || ! uci commit honk; then rollback_ok=0; fi
if ! sync_boot_link "$old_boot_enabled"; then rollback_ok=0; fi
restore_old_runtime || rollback_ok=0
[ "$rollback_ok" = 1 ]
}
settings() {
if [ -e /etc/.honk-update/journal ] || [ -L /etc/.honk-update/journal ]; then echo '{"ok":false,"message":"update_recovery_required"}'; return 1; fi
load_settings
[ "$config_mode" = manual ] && { echo '{"ok":false,"message":"settings_unavailable_in_manual_mode"}'; return 1; }
. /usr/share/libubox/jshn.sh || { echo '{"ok":false,"message":"jshn_unavailable"}'; return 1; }
# Bound stdin to 64KiB (DoS protection); read one extra byte to detect truncation.
settings_json=$(head -c 65537)
if [ "${#settings_json}" -gt 65536 ]; then
echo '{"ok":false,"message":"settings_too_large"}'; return 1
fi
json_load "$settings_json" || { echo '{"ok":false,"message":"invalid_settings_json"}'; return 1; }
json_get_var new_network lan_network
json_get_type network_type lan_network
json_get_var new_port listen_port
json_get_type port_type listen_port
json_get_var new_boot_enabled boot_enabled
json_get_type boot_enabled_type boot_enabled
json_get_var new_clash_enabled clash_api_enabled
json_get_type clash_enabled_type clash_api_enabled
json_get_var new_clash_port clash_api_port
json_get_type clash_port_type clash_api_port
json_get_var new_clash_secret clash_api_secret
json_get_type clash_secret_type clash_api_secret
[ "$network_type" = string ] || { echo '{"ok":false,"message":"invalid_lan_network"}'; return 1; }
case "$port_type" in int|integer) ;; *) echo '{"ok":false,"message":"invalid_listen_port"}'; return 1 ;; esac
# boot_enabled 已内置(恒为 1),前端不再提交,缺省就按当前值处理
case "$boot_enabled_type" in ''|null|boolean) ;; *) echo '{"ok":false,"message":"invalid_boolean_settings"}'; return 1 ;; esac
case "$clash_enabled_type" in ''|null|boolean) ;; *) echo '{"ok":false,"message":"invalid_clash_api_settings"}'; return 1 ;; esac
case "$clash_port_type" in ''|null|int|integer) ;; *) echo '{"ok":false,"message":"invalid_clash_api_settings"}'; return 1 ;; esac
case "$clash_secret_type" in ''|null|string) ;; *) echo '{"ok":false,"message":"invalid_clash_api_settings"}'; return 1 ;; esac
case "$new_network" in ''|*[!A-Za-z0-9_.-]*) echo '{"ok":false,"message":"invalid_lan_network"}'; return 1 ;; esac
case "$new_port" in ''|*[!0-9]*) echo '{"ok":false,"message":"invalid_listen_port"}'; return 1 ;; esac
[ "$new_port" -ge 1024 ] && [ "$new_port" -le 65535 ] || { echo '{"ok":false,"message":"invalid_listen_port"}'; return 1; }
load_settings
case "$boot_enabled_type" in ''|null) new_boot_enabled=1 ;; esac
new_boot_enabled=1
case "$clash_enabled_type" in ''|null) new_clash_enabled=$clash_api_enabled ;; esac
case "$clash_port_type" in ''|null) new_clash_port=$clash_api_port ;; esac
[ -n "$new_clash_secret" ] || new_clash_secret=$clash_api_secret
new_enabled=$enabled
old_network=$lan_network
old_port=$listen_port
old_enabled=$enabled
old_boot_enabled=$boot_enabled
old_initialized=$initialized
old_clash_enabled=$clash_api_enabled
old_clash_port=$clash_api_port
old_clash_secret=$clash_api_secret
[ "$initialized" = 1 ] || { [ "$new_enabled" = 0 ] || { echo '{"ok":false,"message":"initialize_before_enabling_service"}'; return 1; }; }
lan_network=$new_network
listen_port=$new_port
clash_api_enabled=$new_clash_enabled
clash_api_port=$new_clash_port
clash_api_secret=$new_clash_secret
valid_clash_api || { echo '{"ok":false,"message":"invalid_clash_api_settings"}'; return 1; }
check_network || { echo '{"ok":false,"message":"lan_network_unavailable"}'; return 1; }
network_settings || { echo '{"ok":false,"message":"lan_ipv4_unavailable"}'; return 1; }
old_running=0
if managed_core_pid >/dev/null 2>&1; then
old_running=1
elif pidof honk-core >/dev/null 2>&1; then
echo '{"ok":false,"message":"unmanaged_core_running"}'
return 1
fi
if ! { [ "$old_running" = 1 ] && [ "$(configured_system_port)" = "$new_port" ]; }; then
check_port || { echo '{"ok":false,"message":"listen_port_occupied"}'; return 1; }
fi
if [ "$clash_api_enabled" = 1 ] && ! { [ "$old_running" = 1 ] && [ "$(configured_clash_port)" = "$new_clash_port" ]; }; then
check_port "$new_clash_port" || { echo '{"ok":false,"message":"clash_api_port_occupied"}'; return 1; }
fi
honk_lock_acquire || { echo '{"ok":false,"message":"operation_in_progress"}'; return 1; }
trap 'honk_lock_release >/dev/null 2>&1' 0
load_settings
[ "$lan_network" = "$old_network" ] && [ "$listen_port" = "$old_port" ] && [ "$enabled" = "$old_enabled" ] && [ "$boot_enabled" = "$old_boot_enabled" ] && [ "$initialized" = "$old_initialized" ] && [ "$clash_api_enabled" = "$old_clash_enabled" ] && [ "$clash_api_port" = "$old_clash_port" ] && [ "$clash_api_secret" = "$old_clash_secret" ] || { echo '{"ok":false,"message":"settings_changed_concurrently"}'; return 1; }
new_enabled=$enabled
managed_core_pid >/dev/null 2>&1 && recheck_running=1 || recheck_running=0
[ "$recheck_running" = "$old_running" ] || { echo '{"ok":false,"message":"service_state_changed_concurrently"}'; return 1; }
old_running=$recheck_running
lan_network=$new_network
listen_port=$new_port
clash_api_enabled=$new_clash_enabled
clash_api_port=$new_clash_port
clash_api_secret=$new_clash_secret
check_network && network_settings || { echo '{"ok":false,"message":"lan_network_changed_during_settings"}'; return 1; }
if ! { [ "$old_running" = 1 ] && [ "$(configured_system_port)" = "$new_port" ]; }; then
check_port || { echo '{"ok":false,"message":"listen_port_occupied"}'; return 1; }
fi
if [ "$clash_api_enabled" = 1 ] && ! { [ "$old_running" = 1 ] && [ "$(configured_clash_port)" = "$new_clash_port" ]; }; then
check_port "$new_clash_port" || { echo '{"ok":false,"message":"clash_api_port_occupied"}'; return 1; }
fi
if [ "$initialized" = 1 ] && ! check_config; then echo '{"ok":false,"message":"package_config_unavailable"}'; return 1; fi
umask 077
settings_dir=$(mktemp -d /tmp/honk-settings.XXXXXX) || { echo '{"ok":false,"message":"temporary_storage_unavailable"}'; return 1; }
old_uci=$settings_dir/honk.uci
uci export honk > "$old_uci" || { rm -rf "$settings_dir"; echo '{"ok":false,"message":"uci_snapshot_failed"}'; return 1; }
old_system=$settings_dir/system.dae
system_written_hash=""
if [ "$initialized" = 1 ]; then
[ -f /etc/honk/system.dae ] && [ ! -L /etc/honk/system.dae ] || { rm -rf "$settings_dir"; echo '{"ok":false,"message":"system_config_unavailable"}'; return 1; }
cp -p /etc/honk/system.dae "$old_system" || { rm -rf "$settings_dir"; echo '{"ok":false,"message":"system_snapshot_failed"}'; return 1; }
original_system_hash=$(sha256sum "$old_system" | awk '{print $1}')
system_written_hash=$original_system_hash
fi
if [ "$old_running" = 1 ]; then
honk_lifecycle_stop 30 || {
settings_rollback; rollback_status=$?
if [ "$rollback_status" = 0 ]; then rm -rf "$settings_dir"; echo '{"ok":false,"message":"service_stop_failed_rolled_back"}'; else logger -t honk "settings rollback failed; snapshot retained at $settings_dir"; echo '{"ok":false,"message":"service_stop_and_rollback_failed_snapshot_retained"}'; fi
return 1
}
fi
if [ "$initialized" = 1 ] && ! write_runtime_system "$lan_device" "$lan_ip" "$new_port"; then
settings_rollback; rollback_status=$?
if [ "$rollback_status" = 0 ]; then rm -rf "$settings_dir"; echo '{"ok":false,"message":"system_config_write_failed_rolled_back"}'; else logger -t honk "settings rollback failed; snapshot retained at $settings_dir"; echo '{"ok":false,"message":"system_config_write_and_rollback_failed_snapshot_retained"}'; fi
return 1
fi
uci set "honk.main.lan_network=$new_network" && uci set "honk.main.listen_port=$new_port" && uci set "honk.main.enabled=$new_enabled" && uci set "honk.main.boot_enabled=$new_boot_enabled" && uci set "honk.main.clash_api_enabled=$new_clash_enabled" && uci set "honk.main.clash_api_port=$new_clash_port" && uci set "honk.main.clash_api_secret=$new_clash_secret" && uci commit honk && chmod 0600 /etc/config/honk || {
settings_rollback; rollback_status=$?
if [ "$rollback_status" = 0 ]; then rm -rf "$settings_dir"; echo '{"ok":false,"message":"settings_commit_failed_rolled_back"}'; else logger -t honk "settings rollback failed; snapshot retained at $settings_dir"; echo '{"ok":false,"message":"settings_commit_and_rollback_failed_snapshot_retained"}'; fi
return 1
}
if ! sync_boot_link "$new_boot_enabled"; then
settings_rollback; rollback_status=$?
if [ "$rollback_status" = 0 ]; then rm -rf "$settings_dir"; echo '{"ok":false,"message":"boot_link_update_failed_rolled_back"}'; else logger -t honk "settings rollback failed; snapshot retained at $settings_dir"; echo '{"ok":false,"message":"boot_link_update_and_rollback_failed_snapshot_retained"}'; fi
return 1
fi
if [ "$initialized" = 1 ]; then
service_ok=0
if [ "$new_enabled" = 1 ]; then
honk_lifecycle_start 30 || service_ok=1
else
honk_lifecycle_stop 30 || service_ok=1
fi
if [ "$service_ok" != 0 ]; then
settings_rollback; rollback_status=$?
if [ "$rollback_status" = 0 ]; then rm -rf "$settings_dir"; echo '{"ok":false,"message":"service_apply_failed_rolled_back"}'; else logger -t honk "settings rollback failed; snapshot retained at $settings_dir"; echo '{"ok":false,"message":"service_apply_and_rollback_failed_snapshot_retained"}'; fi
return 1
fi
fi
system_written_hash=""
rm -rf "$settings_dir"
echo '{"ok":true,"message":"settings_saved"}'
}
diagnose() {
load_settings
errors=""
managed_core_pid >/dev/null 2>&1 && running=1 || running=0
check_config >/dev/null 2>&1 || errors="$errors invalid_package_config"
check_kernel >/dev/null 2>&1 || errors="$errors kernel_requirements_unmet"
if [ "$config_mode" = manual ]; then
if [ "$running" != 1 ]; then
no_honk_resources >/dev/null 2>&1 || errors="$errors honk_resources_occupied"
pidof honk-core >/dev/null 2>&1 && errors="$errors unmanaged_core_running"
fi
else
check_network >/dev/null 2>&1 || errors="$errors lan_network_unavailable"
network_settings >/dev/null 2>&1 || errors="$errors lan_ipv4_unavailable"
command -v curl >/dev/null 2>&1 || errors="$errors curl_missing"
if [ "$running" != 1 ]; then
no_honk_resources >/dev/null 2>&1 || errors="$errors honk_resources_occupied"
pidof honk-core >/dev/null 2>&1 && errors="$errors unmanaged_core_running"
check_port >/dev/null 2>&1 || errors="$errors listen_port_occupied"
if [ "$clash_api_enabled" = 1 ]; then
check_port "$clash_api_port" >/dev/null 2>&1 || errors="$errors clash_api_port_occupied"
fi
fi
fi
check_conflicts >/dev/null 2>&1 || errors="$errors proxy_conflict"
if [ -d "$HONK_LOCK_DIR" ]; then
if lock_owner_invalid || honk_lock_is_stale; then
errors="$errors stale_operation_lock maintenance_recovery_required"
else
errors="$errors operation_in_progress"
fi
fi
{ [ -e "$HONK_MAINT_JOURNAL" ] || [ -L "$HONK_MAINT_JOURNAL" ]; } && errors="$errors maintenance_recovery_required"
if [ -n "$errors" ]; then
printf '%s\n' "$errors" | sed 's/^ *//'
return 1
fi
return 0
}
main_has_system_include() {
awk '
/^[[:space:]]*include[[:space:]]*\{/ { inside = 1 }
inside && /(^|[^[:alnum:]_.-])system\.dae([^[:alnum:]_.-]|$)/ { found = 1 }
inside && /\}/ { inside = 0 }
END { exit !found }
' /etc/honk/config.dae
}
clash_system_matches() {
if [ "$clash_api_enabled" = 0 ]; then
! grep -q 'clash_api {' /etc/honk/system.dae
return
fi
permissions=$(ls -ldn /etc/honk/system.dae 2>/dev/null | awk '{print $1}')
[ "$permissions" = '-rw-------' ] || return 1
grep -Fqx " external_controller: '$system_host:$clash_api_port'" /etc/honk/system.dae || return 1
grep -Fqx " external_ui: '/usr/share/honk/zashboard'" /etc/honk/system.dae || return 1
system_secret=$(sed -n "s/^ secret: '\([^']*\)'$/\1/p" /etc/honk/system.dae)
[ "$system_secret" = "$clash_api_secret" ]
}
repair() {
if [ -e /etc/.honk-update/journal ] || [ -L /etc/.honk-update/journal ]; then echo '{"ok":false,"message":"update_recovery_required"}'; return 1; fi
load_settings
if [ "$config_mode" = manual ]; then
check_config && { echo '{"ok":true,"message":"manual_config_valid"}'; return 0; }
echo '{"ok":false,"message":"manual_config_invalid"}'
return 1
fi
old_network=$lan_network
old_port=$listen_port
old_enabled=$enabled
old_boot_enabled=$boot_enabled
old_initialized=$initialized
old_clash_enabled=$clash_api_enabled
old_clash_port=$clash_api_port
old_clash_secret=$clash_api_secret
[ "$config_file" = /etc/honk/config.dae ] || { echo '{"ok":false,"message":"invalid_config_path"}'; return 1; }
[ -f /etc/honk/config.dae ] && [ ! -L /etc/honk/config.dae ] || { echo '{"ok":false,"message":"main_config_unavailable"}'; return 1; }
main_hash=$(sha256sum /etc/honk/config.dae | awk '{print $1}')
main_has_system_include || { echo '{"ok":false,"message":"required_system_include_missing_manual_repair"}'; return 1; }
valid_network_name || { echo '{"ok":false,"message":"invalid_lan_network"}'; return 1; }
case "$listen_port" in ''|*[!0-9]*) echo '{"ok":false,"message":"invalid_listen_port"}'; return 1 ;; esac
[ "$listen_port" -ge 1024 ] && [ "$listen_port" -le 65535 ] || { echo '{"ok":false,"message":"invalid_listen_port"}'; return 1; }
valid_clash_api || { echo '{"ok":false,"message":"invalid_clash_api_settings"}'; return 1; }
check_network || { echo '{"ok":false,"message":"lan_network_unavailable"}'; return 1; }
network_settings || { echo '{"ok":false,"message":"lan_ipv4_unavailable"}'; return 1; }
old_running=0
managed_core_pid >/dev/null 2>&1 && old_running=1
if pidof honk-core >/dev/null 2>&1 && [ "$old_running" = 0 ]; then echo '{"ok":false,"message":"unmanaged_core_running"}'; return 1; fi
if ! { [ "$old_running" = 1 ] && [ "$(configured_system_port)" = "$listen_port" ]; }; then
check_port || { echo '{"ok":false,"message":"listen_port_occupied"}'; return 1; }
fi
if [ "$clash_api_enabled" = 1 ] && ! { [ "$old_running" = 1 ] && [ "$(configured_clash_port)" = "$clash_api_port" ]; }; then
check_port "$clash_api_port" || { echo '{"ok":false,"message":"clash_api_port_occupied"}'; return 1; }
fi
honk_lock_acquire || { echo '{"ok":false,"message":"operation_in_progress"}'; return 1; }
trap 'honk_lock_release >/dev/null 2>&1' 0
load_settings
[ "$lan_network" = "$old_network" ] && [ "$listen_port" = "$old_port" ] && [ "$enabled" = "$old_enabled" ] && [ "$boot_enabled" = "$old_boot_enabled" ] && [ "$initialized" = "$old_initialized" ] && [ "$clash_api_enabled" = "$old_clash_enabled" ] && [ "$clash_api_port" = "$old_clash_port" ] && [ "$clash_api_secret" = "$old_clash_secret" ] || { echo '{"ok":false,"message":"settings_changed_concurrently"}'; return 1; }
managed_core_pid >/dev/null 2>&1 && recheck_running=1 || recheck_running=0
[ "$recheck_running" = "$old_running" ] || { echo '{"ok":false,"message":"service_state_changed_concurrently"}'; return 1; }
[ "$(sha256sum /etc/honk/config.dae | awk '{print $1}')" = "$main_hash" ] || { echo '{"ok":false,"message":"main_config_changed_concurrently"}'; return 1; }
check_network && network_settings || { echo '{"ok":false,"message":"lan_network_changed_during_repair"}'; return 1; }
if ! { [ "$old_running" = 1 ] && [ "$(configured_system_port)" = "$listen_port" ]; }; then
check_port || { echo '{"ok":false,"message":"listen_port_occupied"}'; return 1; }
fi
if [ "$clash_api_enabled" = 1 ] && ! { [ "$old_running" = 1 ] && [ "$(configured_clash_port)" = "$clash_api_port" ]; }; then
check_port "$clash_api_port" || { echo '{"ok":false,"message":"clash_api_port_occupied"}'; return 1; }
fi
if [ "$initialized" = 1 ]; then system_host=$lan_ip; else system_host=127.0.0.1; fi
if [ -r /usr/share/doona/index.html ] && [ -f /etc/honk/system.dae ] && [ ! -L /etc/honk/system.dae ] && system_config_readonly && \
grep -Fqx ' wan_interface: auto' /etc/honk/system.dae && grep -Fqx " lan_interface: '$lan_device'" /etc/honk/system.dae && \
grep -Fqx " data_dir: '/etc/honk'" /etc/honk/system.dae && grep -Fqx ' nfqueue_enable: true' /etc/honk/system.dae && \
grep -Fqx " listen: '$system_host:$listen_port'" /etc/honk/system.dae && \
grep -Fqx " ui: '/usr/share/doona'" /etc/honk/system.dae && grep -Fqx ' password_auth: true' /etc/honk/system.dae && \
grep -Fqx ' allow_anonymous_loopback: false' /etc/honk/system.dae && grep -Fqx ' config_write: true' /etc/honk/system.dae && clash_system_matches; then
echo '{"ok":true,"message":"system_config_healthy"}'
return 0
fi
if [ -e /etc/honk/system.dae ] && { [ ! -f /etc/honk/system.dae ] || [ -L /etc/honk/system.dae ]; }; then
echo '{"ok":false,"message":"system_config_not_regular_file"}'
return 1
fi
[ -r /usr/share/doona/index.html ] || { echo '{"ok":false,"message":"ui_assets_missing"}'; return 1; }
umask 077
repair_dir=$(mktemp -d /tmp/honk-repair.XXXXXX) || { echo '{"ok":false,"message":"temporary_storage_unavailable"}'; return 1; }
old_system=$repair_dir/system.dae
original_system_hash=""
if [ -f /etc/honk/system.dae ]; then
cp -p /etc/honk/system.dae "$old_system" || { rm -rf "$repair_dir"; echo '{"ok":false,"message":"system_snapshot_failed"}'; return 1; }
original_system_hash=$(sha256sum "$old_system" | awk '{print $1}')
fi
system_written_hash=$original_system_hash
if [ "$old_running" = 1 ]; then
if ! honk_lifecycle_stop 30; then
logger -t honk "repair stop/drain unconfirmed; no restart attempted; snapshot retained at $repair_dir"
echo '{"ok":false,"message":"service_stop_unconfirmed_snapshot_retained"}'
return 1
fi
fi
if [ "$initialized" = 1 ]; then system_listen=$lan_ip; else system_listen=127.0.0.1; fi
if ! write_runtime_system "$lan_device" "$lan_ip" "$listen_port" "$system_listen"; then
repair_rollback_ok=1
[ -n "$system_written_hash" ] && ! restore_system && repair_rollback_ok=0
restore_old_runtime || repair_rollback_ok=0
if [ "$repair_rollback_ok" = 1 ]; then
rm -rf "$repair_dir"
echo '{"ok":false,"message":"system_config_repair_failed_rolled_back"}'
else
logger -t honk "repair rollback failed; snapshot retained at $repair_dir"
echo '{"ok":false,"message":"system_config_repair_rollback_failed_snapshot_retained"}'
fi
return 1
fi
if [ "$initialized" = 1 ] && { [ "$old_running" = 1 ] || [ "$enabled" = 1 ]; }; then
if ! honk_lifecycle_start 30; then
repair_rollback_ok=1
honk_lifecycle_stop 30 || repair_rollback_ok=0
if [ "$repair_rollback_ok" = 1 ]; then restore_system || repair_rollback_ok=0; fi
restore_old_runtime || repair_rollback_ok=0
if [ "$repair_rollback_ok" = 1 ]; then
rm -rf "$repair_dir"
echo '{"ok":false,"message":"repair_restart_failed_rolled_back"}'
else
logger -t honk "repair restart rollback failed; snapshot retained at $repair_dir"
echo '{"ok":false,"message":"repair_restart_and_rollback_failed_snapshot_retained"}'
fi
return 1
fi
fi
system_written_hash=""
rm -rf "$repair_dir"
echo '{"ok":true,"message":"system_config_repaired"}'
}
api_discovery() {
curl -fsS --noproxy '*' --connect-timeout 1 --max-time 3 "$1/api" -o "$2"
}
mock_start() {
mock_config=$1
mock_log=$2
DAE_LOCATION_ASSET=/usr/share/v2ray "$HONK_CORE" --config "$mock_config" --mock-ebpf > "$mock_log" 2>&1 &
mock_pid=$!
mock_identity=""
attempt=0
while [ "$attempt" -lt 20 ]; do
if ! kill -0 "$mock_pid" 2>/dev/null; then
return 1
fi
if api_discovery "$3" "$4" 2>/dev/null; then
mock_identity=$(tr '\000' ' ' < "/proc/$mock_pid/cmdline" 2>/dev/null)
case "$mock_identity" in
*"$mock_config"*--mock-ebpf*)
[ "$(readlink -f "/proc/$mock_pid/exe" 2>/dev/null)" = "$HONK_CORE" ] && return 0
;;
esac
return 1
fi
sleep 1
attempt=$((attempt + 1))
done
return 1
}
mock_stop() {
[ -n "$mock_pid" ] || return 0
[ -r "/proc/$mock_pid/cmdline" ] || { mock_pid=""; return 0; }
current=$(tr '\000' ' ' < "/proc/$mock_pid/cmdline" 2>/dev/null)
case "$current" in
*"$mock_config"*--mock-ebpf*)
[ "$(readlink -f "/proc/$mock_pid/exe" 2>/dev/null)" = "$HONK_CORE" ] || return 1
kill -TERM "$mock_pid" 2>/dev/null || return 1
;;
*) return 1 ;;
esac
attempt=0
while kill -0 "$mock_pid" 2>/dev/null && [ "$attempt" -lt 10 ]; do sleep 1; attempt=$((attempt + 1)); done
if kill -0 "$mock_pid" 2>/dev/null; then return 1; fi
wait "$mock_pid" 2>/dev/null || true
mock_pid=""
}
post_credentials() {
endpoint=$1
response_file=$2
json_init
json_add_string username "$username"
json_add_string password "$password"
json_dump | curl -sS --noproxy '*' --connect-timeout 1 --max-time 5 -o "$response_file" -w '%{http_code}' \
-H 'Content-Type: application/json' --data-binary @- "$endpoint"
}
initialize_preflight() {
load_settings
[ "$enabled" = 0 ] && [ "$initialized" = 0 ] || { echo '{"ok":false,"message":"disable_service_before_initialization"}'; return 1; }
[ "$config_file" = /etc/honk/config.dae ] || { echo '{"ok":false,"message":"invalid_config_path"}'; return 1; }
[ -r /usr/share/doona/index.html ] || { echo '{"ok":false,"message":"ui_assets_missing"}'; return 1; }
if [ "$1" = read_credentials ]; then
read_credentials || { echo '{"ok":false,"message":"invalid_credentials"}'; return 1; }
fi
[ -x "$HONK_CORE" ] || { echo '{"ok":false,"message":"core_missing"}'; return 1; }
command -v curl >/dev/null 2>&1 || { echo '{"ok":false,"message":"curl_missing"}'; return 1; }
pidof honk-core >/dev/null 2>&1 && { echo '{"ok":false,"message":"core_already_running"}'; return 1; }
no_honk_resources || { echo '{"ok":false,"message":"honk_resources_already_exist"}'; return 1; }
check_port || { echo '{"ok":false,"message":"listen_port_occupied"}'; return 1; }
check_network || { echo '{"ok":false,"message":"lan_network_unavailable"}'; return 1; }
network_settings || { echo '{"ok":false,"message":"lan_ipv4_unavailable"}'; return 1; }
check_config || { echo '{"ok":false,"message":"invalid_package_config"}'; return 1; }
return 0
}
initialize() {
if [ -e /etc/.honk-update/journal ] || [ -L /etc/.honk-update/journal ]; then echo '{"ok":false,"message":"update_recovery_required"}'; return 1; fi
. /usr/share/libubox/jshn.sh || return 1
initialize_preflight read_credentials || return 1
honk_lock_acquire || { echo '{"ok":false,"message":"operation_in_progress"}'; return 1; }
trap 'honk_lock_release >/dev/null 2>&1' 0
initialize_preflight || return 1
umask 077
temp_dir=$(mktemp -d /tmp/honk-init.XXXXXX) || { echo '{"ok":false,"message":"temporary_storage_unavailable"}'; return 1; }
mock_pid=""
trap cleanup_initialize 0
trap 'exit 1' HUP INT TERM
old_system=$temp_dir/system.dae
cp -p /etc/honk/system.dae "$old_system" || { echo '{"ok":false,"message":"system_config_backup_failed"}'; return 1; }
original_system_hash=$(sha256sum "$old_system" | awk '{print $1}')
system_written_hash=$original_system_hash
ensure_loopback_system || { echo '{"ok":false,"message":"loopback_listener_write_failed"}'; return 1; }
mock_config=$temp_dir/mock.dae
mock_log=$temp_dir/core.log
response_file=$temp_dir/response.json
cat > "$mock_config" <<EOF
global {
wan_interface: auto
lan_interface: ''
data_dir: '/etc/honk'
nfqueue_enable: false
store_subscribe: false
}
routing {
fallback: direct
}
experimental {
native_api {
enabled: true
listen: '127.0.0.1:$listen_port'
password_auth: true
allow_anonymous_loopback: false
config_write: true
}
}
EOF
chmod 0600 "$mock_config" "$mock_log" 2>/dev/null || true
loopback_url="http://127.0.0.1:$listen_port"
if ! mock_start "$mock_config" "$mock_log" "$loopback_url" "$response_file"; then
echo '{"ok":false,"message":"mock_core_start_failed"}'
return 1
fi
setup_required=$(jsonfilter -i "$response_file" -e '@.auth.setup_required' 2>/dev/null)
case "$setup_required" in
true)
status=$(post_credentials "$loopback_url/api/v1/auth/setup" "$response_file")
[ "$status" = 201 ] || { echo '{"ok":false,"message":"administrator_setup_failed"}'; return 1; }
;;
false)
status=$(post_credentials "$loopback_url/api/v1/auth/login" "$response_file")
[ "$status" = 200 ] || { echo '{"ok":false,"message":"administrator_exists_reuse_credentials_or_reset_state"}'; return 1; }
;;
*) echo '{"ok":false,"message":"native_api_setup_state_unavailable"}'; return 1 ;;
esac
api_discovery "$loopback_url" "$response_file" || { echo '{"ok":false,"message":"setup_verification_failed"}'; return 1; }
setup_required=$(jsonfilter -i "$response_file" -e '@.auth.setup_required' 2>/dev/null)
[ "$setup_required" = false ] || { echo '{"ok":false,"message":"setup_verification_failed"}'; return 1; }
mock_stop || { echo '{"ok":false,"message":"mock_core_stop_failed"}'; return 1; }
if ! mock_start "$mock_config" "$mock_log" "$loopback_url" "$response_file"; then
echo '{"ok":false,"message":"mock_core_restart_failed"}'
return 1
fi
api_discovery "$loopback_url" "$response_file" || { echo '{"ok":false,"message":"persistence_verification_failed"}'; return 1; }
setup_required=$(jsonfilter -i "$response_file" -e '@.auth.setup_required' 2>/dev/null)
[ "$setup_required" = false ] || { echo '{"ok":false,"message":"persistence_verification_failed"}'; return 1; }
status=$(post_credentials "$loopback_url/api/v1/auth/login" "$response_file")
[ "$status" = 200 ] || { echo '{"ok":false,"message":"persistence_login_failed"}'; return 1; }
mock_stop || { echo '{"ok":false,"message":"mock_core_stop_failed"}'; return 1; }
write_runtime_system "$lan_device" "$lan_ip" || { restore_system; echo '{"ok":false,"message":"system_config_write_failed"}'; return 1; }
lan_url="http://$lan_ip:$listen_port"
if ! mock_start /etc/honk/config.dae "$mock_log" "$lan_url" "$response_file"; then
restore_system
echo '{"ok":false,"message":"generated_config_invalid_restored_loopback"}'
return 1
fi
api_discovery "$lan_url" "$response_file" || {
mock_stop >/dev/null 2>&1
restore_system
echo '{"ok":false,"message":"lan_api_verification_failed_restored_loopback"}'
return 1
}
mock_stop || {
restore_system
echo '{"ok":false,"message":"mock_core_stop_failed_restored_loopback"}'
return 1
}
uci set honk.main.initialized='1' && uci commit honk || {
restore_system
uci set honk.main.initialized='0' >/dev/null 2>&1
uci commit honk >/dev/null 2>&1
echo '{"ok":false,"message":"uci_commit_failed_restored_loopback"}'
return 1
}
system_written_hash=""
password=""
username=""
echo '{"ok":true,"message":"administrator_initialized"}'
}
case "$1" in
check)
preflight
;;
diagnose)
diagnose
;;
preflight)
prepare_runtime
;;
local-direct)
load_settings
sync_local_direct
;;
initialize)
initialize
;;
settings)
settings
;;
repair)
repair
;;
*)
echo "usage: $0 {check|diagnose|preflight|local-direct|initialize|settings|repair}" >&2
exit 2
;;
esac
+13 -5
View File
@@ -1,7 +1,15 @@
config honk 'main'
config honk 'config'
option enabled '0' option enabled '0'
option advanced '0' option boot_enabled '0'
option dashboard 'none' option initialized '0'
option config_mode 'managed'
option config_file '/etc/honk/config.dae' option config_file '/etc/honk/config.dae'
option listen_port '9527'
option clash_api_enabled '0'
option clash_api_port '9090'
option clash_api_secret ''
option lan_network 'lan'
option geoip_url ''
option geosite_url ''
option geo_auto '0'
option geo_auto_freq 'daily'
Regular → Executable
+70 -109
View File
@@ -1,124 +1,85 @@
#!/bin/sh /etc/rc.common #!/bin/sh /etc/rc.common
USE_PROCD=1 USE_PROCD=1
START=96 START=95
STOP=10 STOP=10
extra_command "hot_reload" "Hot-reload service" . /lib/functions.sh
CONF="honk" HONK_CONTROL=/usr/share/honk/control.sh
PROG="/usr/bin/honk-core" HONK_CONFIG=/etc/honk/config.dae
# honk-core hardcodes dae-compatible datapath names (dae0/dae0peer + daens);
# these are the real interface/netns names, not leftover dae config.
HONK_HOST_IFACE="dae0"
HONK_PEER_IFACE="dae0peer"
HONK_NETNS_NAME="daens"
HONK_NETNS_PATH="/run/netns/$HONK_NETNS_NAME"
RESOLV_HONK="/tmp/resolv.conf.honk"
set_chmod_640() { boot() {
mkdir -p /etc/honk/config.d /etc/honk/state /var/log/honk # 开机自启内置:只要服务是"已启用 + 已初始化"就起来,
chmod -R u=rwX,go= /etc/honk 2>/dev/null || true # 用户主动停掉(enabled=0)后重启也不会被强行拉起。
chmod 700 /etc/honk /etc/honk/config.d /etc/honk/state 2>/dev/null || true start
}
cleanup_netns() {
ip netns del "$HONK_NETNS_NAME" 2>/dev/null || true
umount "$HONK_NETNS_PATH" 2>/dev/null || true
rm -f "$HONK_NETNS_PATH" 2>/dev/null || true
ip link del "$HONK_HOST_IFACE" 2>/dev/null || true
ip link del "$HONK_PEER_IFACE" 2>/dev/null || true
}
hijack_resolv_conf() {
grep -q ' /tmp/resolv\.conf ' /proc/mounts && return 0
local dns_list WANS
. /lib/functions/network.sh
unset -v __NETWORK_CACHE
__network_ifstatus WANS "" "[@.route[@.target='0.0.0.0' && !@.table]].interface" 2>/dev/null
set -- $WANS
if [ $# -eq 1 ]; then
dns_list=$(ubus call "network.interface.$1" status 2>/dev/null | \
jsonfilter -e '@["dns-server"][*]' 2>/dev/null)
if [ -z "$dns_list" ] && [ -f "/tmp/resolv.conf.d/resolv.conf.auto" ]; then
dns_list=$(awk '/^nameserver/{print $2}' "/tmp/resolv.conf.d/resolv.conf.auto")
fi
fi
[ -z "$dns_list" ] && dns_list="119.29.29.29 223.5.5.5"
[ -f /tmp/resolv.conf ] || touch /tmp/resolv.conf
grep -E '^(search|domain) ' /tmp/resolv.conf > "$RESOLV_HONK" 2>/dev/null
for dns in $dns_list; do
echo "nameserver $dns" >> "$RESOLV_HONK"
done
mount --bind "$RESOLV_HONK" /tmp/resolv.conf
mount -o remount,ro,bind /tmp/resolv.conf
}
restore_resolv_conf() {
while grep -q ' /tmp/resolv\.conf ' /proc/mounts; do
mount -o remount,rw,bind /tmp/resolv.conf 2>/dev/null
umount /tmp/resolv.conf 2>/dev/null || break
done
rm -f "$RESOLV_HONK"
} }
start_service() { start_service() {
local enabled config_file local enabled initialized config_file config_mode ready_token ready_hash ready_runtime ready_api_enabled ready_api_listen ready_clash_api_listen ready_clash_api_secret_set old_ready_token
config_load honk
config_get_bool enabled main enabled 0
config_get_bool initialized main initialized 0
[ "$enabled" -eq 1 ] && [ "$initialized" -eq 1 ] || {
logger -t honk "refusing to start before explicit initialization and enablement"
return 1
}
config_get config_file main config_file "$HONK_CONFIG"
config_get config_mode main config_mode managed
[ "$config_file" = "$HONK_CONFIG" ] || {
logger -t honk "refusing non-default config path"
return 1
}
"$HONK_CONTROL" preflight || return 1
"$HONK_CONTROL" local-direct || logger -t honk "local-direct sync skipped"
unset HONK_MAINT_CAP
config_load "$CONF" if [ "$config_mode" = manual ]; then
config_get_bool enabled "config" "enabled" "0" ready_token=$(cat /proc/sys/kernel/random/uuid 2>/dev/null) || return 1
[ "$enabled" -eq "1" ] || return 1 case "$ready_token" in *[!0-9a-f-]*|'') return 1 ;; esac
ready_hash=$(sha256sum "$HONK_CONFIG" 2>/dev/null | awk '{print $1}') || return 1
config_get config_file "config" "config_file" "/etc/honk/config.dae" case "$ready_hash" in *[!0-9a-f]*|'') return 1 ;; esac
ready_runtime=$(/usr/bin/honk-core config validate --config "$HONK_CONFIG" 2>/dev/null) || return 1
if [ ! -x "$PROG" ]; then ready_api_enabled=$(printf '%s' "$ready_runtime" | jsonfilter -e '@.runtime.native_api_enabled' 2>/dev/null)
logger -t honk "not starting: $PROG is missing or not executable" ready_api_listen=$(printf '%s' "$ready_runtime" | jsonfilter -e '@.runtime.native_api_listen' 2>/dev/null)
return 1 ready_clash_api_listen=$(printf '%s' "$ready_runtime" | jsonfilter -e '@.runtime.clash_api_listen' 2>/dev/null)
fi ready_clash_api_secret_set=$(printf '%s' "$ready_runtime" | jsonfilter -e '@.runtime.clash_api_secret_set' 2>/dev/null)
case "$ready_api_enabled" in true|false) ;; *) return 1 ;; esac
set_chmod_640 case "$ready_api_listen" in *[!A-Za-z0-9:._-]*) return 1 ;; esac
cleanup_netns case "$ready_clash_api_listen" in *[!A-Za-z0-9:._-]*) return 1 ;; esac
hijack_resolv_conf case "$ready_clash_api_secret_set" in true|false) ;; *) return 1 ;; esac
mkdir -p /var/run/honk || return 1
procd_open_instance "$CONF" chmod 0700 /var/run/honk || return 1
# mimalloc pre-reserves a 1 GiB arena; VSZ only, but it dwarfs the real if [ -f /var/run/honk/readiness ] && [ ! -L /var/run/honk/readiness ]; then
# footprint in ps. MIMALLOC_ARENA_RESERVE=0: grow on demand instead. read -r old_ready_token _ < /var/run/honk/readiness
procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray" MIMALLOC_ARENA_RESERVE="0" case "$old_ready_token" in ''|*[!0-9a-f-]*) ;; *) rm -f "/var/run/honk/core-ready-$old_ready_token" ;; esac
procd_set_param command "$PROG" --config "$config_file" fi
procd_set_param netdev "$HONK_HOST_IFACE" "$HONK_PEER_IFACE" printf '%s %s\n' "$ready_token" "$ready_hash" > /var/run/honk/readiness || return 1
procd_set_param limits core="unlimited" chmod 0600 /var/run/honk/readiness || return 1
procd_set_param limits nofile="1000000 1000000" fi
procd_set_param term_timeout 8 procd_open_instance main
procd_set_param respawn 3600 5 5 procd_set_param command /usr/bin/honk-core --config "$HONK_CONFIG" --disable-timestamp
procd_set_param stderr 1 # One env call, no empty values: procd drops the whole list when a KEY has an
procd_close_instance # empty value, which silently stripped the readiness hash the core requires.
} ready_env="DAE_LOCATION_ASSET=/usr/share/v2ray"
if [ "$config_mode" = manual ]; then
service_stopped() { ready_env="$ready_env HONK_READY_TOKEN=$ready_token HONK_READY_CONFIG_HASH=$ready_hash HONK_READY_API_ENABLED=$ready_api_enabled HONK_READY_CLASH_API_SECRET_SET=$ready_clash_api_secret_set"
restore_resolv_conf [ -n "$ready_api_listen" ] && ready_env="$ready_env HONK_READY_API_LISTEN=$ready_api_listen"
cleanup_netns [ -n "$ready_clash_api_listen" ] && ready_env="$ready_env HONK_READY_CLASH_API_LISTEN=$ready_clash_api_listen"
fi
# shellcheck disable=SC2086
procd_set_param env $ready_env
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param respawn 3600 5 5
procd_set_param term_timeout 20
procd_close_instance
} }
reload_service() { reload_service() {
stop # honk-core has no hot-reload API wired here; a reload is an explicit
start # restart so existing connections are cycled. The UI only exposes
} # restart, this exists so manual `/etc/init.d/honk reload` is defined.
logger -t honk "reload requested, restarting (no hot-reload)"
service_triggers() { restart
procd_add_reload_trigger "$CONF"
}
hot_reload() {
set_chmod_640
if /etc/init.d/honk running; then
"$PROG" reload
else
start
fi
} }
+127
View File
@@ -0,0 +1,127 @@
#!/bin/sh
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
umask 077
. /usr/share/libubox/jshn.sh
JOBS=/tmp/honk-jobs
MAINT=/usr/share/honk/maintenance.sh
write_status() {
status_state=$1
status_phase=$2
status_ok=$3
status_message=$4
# jshn 的布尔只认 1/0,传 "true" 会被写进状态文件成 false。
case "$status_ok" in true|1) status_ok=1 ;; *) status_ok=0 ;; esac
status_tmp=$job_dir/status.new
{
json_init
json_add_string state "$status_state"
json_add_string phase "$status_phase"
json_add_boolean ok "$status_ok"
json_add_string message "$status_message"
json_dump
} > "$status_tmp" || return 1
chmod 0600 "$status_tmp" || return 1
mv -f "$status_tmp" "$job_dir/status" || return 1
}
job_id=$1
operation=$2
case "$job_id" in
*[!0-9a-f]*|'') exit 2 ;;
esac
[ "${#job_id}" -eq 32 ] || exit 2
case "$operation" in backup|restore|reset|update_check|update_apply) ;; *) exit 2 ;; esac
job_dir=$JOBS/$job_id
[ -d "$JOBS" ] && [ ! -L "$JOBS" ] && [ -d "$job_dir" ] && [ ! -L "$job_dir" ] || exit 2
[ "$(stat -c '%u:%a' "$JOBS" 2>/dev/null)" = 0:700 ] && [ "$(stat -c '%u:%a' "$job_dir" 2>/dev/null)" = 0:700 ] || exit 2
[ "$(id -u)" = 0 ] || exit 2
write_status running preparing true preparing || exit 1
: > "$job_dir/output" || exit 1
chmod 0600 "$job_dir/output" || exit 1
# stderr 单独存:混进 output 会让结果 JSON 解析失败,报成“操作失败”。
: > "$job_dir/stderr" || exit 1
chmod 0600 "$job_dir/stderr" || exit 1
if [ "$operation" = update_apply ]; then
/usr/share/honk/update.sh apply > "$job_dir/output" 2> "$job_dir/stderr" &
elif [ "$operation" = update_check ]; then
/usr/share/honk/update.sh check > "$job_dir/output" 2> "$job_dir/stderr" &
else
"$MAINT" "$operation" > "$job_dir/output" 2> "$job_dir/stderr" &
fi
maint_pid=$!
logger -t honk-maintenance "$operation started" 2>/dev/null || :
# Total watchdog: never poll forever if maintenance.sh hangs.
_poll_deadline=$(($(date +%s) + 1800))
while kill -0 "$maint_pid" 2>/dev/null; do
if [ "$(date +%s)" -ge "$_poll_deadline" ]; then
logger -t honk-maintenance "$operation timed out after 1800s, killing $maint_pid" 2>/dev/null || :
kill -9 "$maint_pid" 2>/dev/null || :
break
fi
journal_phase=$(sed -n 's/^phase=//p' /etc/.honk-maintenance/journal 2>/dev/null | sed -n '1p')
if [ "$operation" = update_apply ]; then
update_phase=$(cat /etc/.honk-update/phase 2>/dev/null)
case "$update_phase" in
downloading) status_phase=downloading ;;
verifying) status_phase=validating ;;
stopping) status_phase=stopping ;;
installing) status_phase=applying ;;
starting) status_phase=starting ;;
*) status_phase=preparing ;;
esac
elif [ "$operation" = update_check ]; then
status_phase=checking_feed
else case "$journal_phase" in
prepared) status_phase=preparing ;;
stopped) status_phase=stopping ;;
old_moved|new_installed) status_phase=applying ;;
uci_applied) status_phase=validating ;;
service_started) status_phase=starting ;;
rollback_old_start) status_phase=rolling_back ;;
committed) status_phase=committing ;;
*) status_phase=preparing ;;
esac; fi
write_status running "$status_phase" true "$status_phase" || :
sleep 1
done
wait "$maint_pid"
operation_rc=$?
json_load "$(cat "$job_dir/output" 2>/dev/null)" 2>/dev/null || {
if [ -e /etc/.honk-maintenance/journal ] || [ -L /etc/.honk-maintenance/journal ] || [ -d /run/honk.lock ]; then
write_status rollback_required recovery false maintenance_recovery_required
else
write_status failed complete false operation_failed
fi
logger -t honk-maintenance "$operation failed (invalid result)" 2>/dev/null || :
exit 1
}
json_get_var operation_ok ok
json_get_var operation_message message
if [ "$operation_rc" -eq 0 ] && [ "$operation_ok" = 1 ]; then
if [ "$operation" = update_check ]; then
json_add_string state succeeded
json_add_string phase complete
json_add_string job_id "$job_id"
json_dump > "$job_dir/status.new" && chmod 0600 "$job_dir/status.new" && mv -f "$job_dir/status.new" "$job_dir/status" || exit 1
logger -t honk-maintenance "$operation completed" 2>/dev/null || :
exit 0
fi
write_status succeeded complete true "$operation_message"
logger -t honk-maintenance "$operation completed" 2>/dev/null || :
exit 0
fi
case "$operation_message" in
*rollback_failed*|*recovery_required*|*snapshot_retained*)
write_status rollback_required recovery false "$operation_message"
logger -t honk-maintenance "$operation requires recovery" 2>/dev/null || :
exit 1
;;
esac
write_status failed complete false "${operation_message:-operation_failed}"
logger -t honk-maintenance "$operation failed" 2>/dev/null || :
exit 1
+268
View File
@@ -0,0 +1,268 @@
#!/bin/sh
HONK_LIFECYCLE_CORE=/usr/bin/honk-core
honk_lifecycle_procd_pid() {
ubus call service list '{"name":"honk"}' 2>/dev/null | jsonfilter -e '@.honk.instances.main.pid' 2>/dev/null
}
honk_lifecycle_procd_running() {
ubus call service list '{"name":"honk"}' 2>/dev/null | jsonfilter -e '@.honk.instances.main.running' 2>/dev/null
}
honk_lifecycle_managed_pid() {
hlp_pid=$(honk_lifecycle_procd_pid)
[ -n "$hlp_pid" ] && [ -r "/proc/$hlp_pid/cmdline" ] || return 1
hlp_cmd=$(tr '\000' ' ' < "/proc/$hlp_pid/cmdline" 2>/dev/null) || return 1
case "$hlp_cmd" in
*/usr/bin/honk-core\ --config\ /etc/honk/config.dae\ --disable-timestamp*) ;;
*) return 1 ;;
esac
case "$hlp_cmd" in *--mock-ebpf*) return 1 ;; esac
[ "$(readlink -f "/proc/$hlp_pid/exe" 2>/dev/null)" = "$HONK_LIFECYCLE_CORE" ] || return 1
printf '%s\n' "$hlp_pid"
}
honk_lifecycle_starttime() {
[ -r "/proc/$1/stat" ] || return 1
awk '{ sub(/^.*\) /, ""); split($0, fields, /[[:space:]]+/); if (fields[20] ~ /^[0-9]+$/) print fields[20]; else exit 1 }' "/proc/$1/stat" 2>/dev/null
}
honk_lifecycle_listen() {
config_load honk
config_get config_mode main config_mode managed
if [ "$config_mode" = manual ]; then
[ -f /var/run/honk/readiness ] && [ ! -L /var/run/honk/readiness ] || return 1
read -r hlp_token _ < /var/run/honk/readiness || return 1
case "$hlp_token" in ''|*[!0-9a-f-]*) return 1 ;; esac
sed -n 's/^api_listen=//p' "/var/run/honk/core-ready-$hlp_token" 2>/dev/null
return
fi
sed -n "s/^[[:space:]]*listen:[[:space:]]*'\([^']*\)'.*/\1/p" /etc/honk/system.dae 2>/dev/null | head -n 1
}
honk_lifecycle_parse_listen() {
config_load honk
config_get config_mode main config_mode managed
if [ "$config_mode" = manual ]; then
HONK_LIFECYCLE_LISTEN=$(honk_lifecycle_listen)
if [ -z "$HONK_LIFECYCLE_LISTEN" ]; then
HONK_LIFECYCLE_PORT=0
return 0
fi
case "$HONK_LIFECYCLE_LISTEN" in *:*) ;; *) return 1 ;; esac
HONK_LIFECYCLE_PORT=${HONK_LIFECYCLE_LISTEN##*:}
case "$HONK_LIFECYCLE_LISTEN" in ''|*[!A-Za-z0-9:._-]*) return 1 ;; esac
case "$HONK_LIFECYCLE_PORT" in ''|*[!0-9]*) return 1 ;; esac
[ "$HONK_LIFECYCLE_PORT" -ge 1024 ] && [ "$HONK_LIFECYCLE_PORT" -le 65535 ]
return $?
fi
HONK_LIFECYCLE_LISTEN=$(honk_lifecycle_listen)
case "$HONK_LIFECYCLE_LISTEN" in *:*) ;; *) return 1 ;; esac
HONK_LIFECYCLE_PORT=${HONK_LIFECYCLE_LISTEN##*:}
case "$HONK_LIFECYCLE_LISTEN" in ''|*[!A-Za-z0-9:._-]*) return 1 ;; esac
case "$HONK_LIFECYCLE_PORT" in ''|*[!0-9]*) return 1 ;; esac
[ "$HONK_LIFECYCLE_PORT" -ge 1024 ] && [ "$HONK_LIFECYCLE_PORT" -le 65535 ]
}
honk_lifecycle_ready_owned() {
hlp_pid=$1
[ -f /var/run/honk/readiness ] && [ ! -L /var/run/honk/readiness ] || return 1
read -r hlp_token hlp_hash < /var/run/honk/readiness || return 1
case "$hlp_token:$hlp_hash" in *[!0-9a-f:-]*|:*|:|*:) return 1 ;; esac
hlp_marker=/var/run/honk/core-ready-$hlp_token
[ -f "$hlp_marker" ] && [ ! -L "$hlp_marker" ] || return 1
grep -Fqx "pid=$hlp_pid" "$hlp_marker" && \
grep -Fqx "token=$hlp_token" "$hlp_marker" && \
grep -Fqx "config_hash=$hlp_hash" "$hlp_marker" && \
grep -Eq '^api_enabled=(true|false)$' "$hlp_marker" && \
grep -Eq '^clash_api_secret_set=(true|false)$' "$hlp_marker"
}
honk_lifecycle_port_open() {
hlp_port=$1
case "$hlp_port" in ''|*[!0-9]*) return 1 ;; esac
awk -v port="$hlp_port" 'BEGIN { printf "%04X\n", port }' | {
IFS= read -r hlp_hex || exit 1
for hlp_table in /proc/net/tcp /proc/net/tcp6; do
[ -r "$hlp_table" ] || continue
awk -v port="$hlp_hex" 'NR > 1 && toupper($2) ~ (":" port "$") && $4 == "0A" { found = 1 } END { exit !found }' "$hlp_table" && exit 0
done
exit 1
}
}
honk_lifecycle_listener_owned() {
hlp_pid=$1
hlp_port=$2
hlp_hex=$(awk -v port="$hlp_port" 'BEGIN { printf "%04X", port }') || return 1
for hlp_table in /proc/net/tcp /proc/net/tcp6; do
[ -r "$hlp_table" ] || continue
for hlp_inode in $(awk -v port="$hlp_hex" 'NR > 1 && toupper($2) ~ (":" port "$") && $4 == "0A" { print $10 }' "$hlp_table"); do
for hlp_fd in /proc/"$hlp_pid"/fd/*; do
[ "$(readlink "$hlp_fd" 2>/dev/null)" = "socket:[$hlp_inode]" ] && return 0
done
done
done
return 1
}
honk_lifecycle_api_owned() {
hlp_pid=$1
honk_lifecycle_parse_listen || return 1
[ "$HONK_LIFECYCLE_PORT" -gt 0 ] || return 1
honk_lifecycle_listener_owned "$hlp_pid" "$HONK_LIFECYCLE_PORT" && \
curl -fsS --noproxy '*' --connect-timeout 1 --max-time 2 "http://$HONK_LIFECYCLE_LISTEN/api" -o /dev/null 2>/dev/null
}
honk_lifecycle_runtime_owned() {
hlp_pid=$1
config_load honk
config_get config_mode main config_mode managed
if [ "$config_mode" = manual ]; then
honk_lifecycle_ready_owned "$hlp_pid"
else
honk_lifecycle_api_owned "$hlp_pid"
fi
}
honk_lifecycle_resources_clear() {
command -v ip >/dev/null 2>&1 || return 1
for hlp_link in dae0 dae0peer; do
ip link show "$hlp_link" >/dev/null 2>&1 && return 1
done
! ip netns list 2>/dev/null | awk '{print $1}' | grep -qx daens
}
honk_lifecycle_drain() {
hlp_timeout=${1:-30}
while [ "$hlp_timeout" -gt 0 ]; do
hlp_wait=0
if [ -n "${HONK_LIFECYCLE_STOP_PID:-}" ] && \
[ "$(honk_lifecycle_starttime "$HONK_LIFECYCLE_STOP_PID" 2>/dev/null)" = "$HONK_LIFECYCLE_STOP_STARTTIME" ]; then
hlp_wait=1
fi
hlp_procd=$(honk_lifecycle_procd_pid)
[ -z "$hlp_procd" ] || hlp_wait=1
hlp_running=$(honk_lifecycle_procd_running)
case "$hlp_running" in true|1) hlp_wait=1 ;; esac
hlp_all=$(pidof honk-core 2>/dev/null)
if [ -n "$hlp_all" ]; then
for hlp_pid in $hlp_all; do
if [ "$hlp_pid" != "${HONK_LIFECYCLE_STOP_PID:-}" ] || \
[ "$(honk_lifecycle_starttime "$hlp_pid" 2>/dev/null)" != "${HONK_LIFECYCLE_STOP_STARTTIME:-}" ]; then
echo "honk lifecycle: unmanaged honk-core process remains" >&2
return 1
fi
done
hlp_wait=1
fi
honk_lifecycle_resources_clear || hlp_wait=1
honk_lifecycle_parse_listen || { echo "honk lifecycle: invalid configured listener" >&2; return 1; }
hlp_port=$HONK_LIFECYCLE_PORT
if honk_lifecycle_port_open "$hlp_port"; then hlp_wait=1; fi
if [ "$hlp_wait" = 0 ]; then
return 0
fi
sleep 1
hlp_timeout=$((hlp_timeout - 1))
done
echo "honk lifecycle: old process or network resources did not drain" >&2
return 1
}
honk_lifecycle_stop() {
hlp_timeout=${1:-30}
HONK_LIFECYCLE_STOP_PID=$(honk_lifecycle_managed_pid 2>/dev/null)
if [ -n "$HONK_LIFECYCLE_STOP_PID" ]; then
HONK_LIFECYCLE_STOP_STARTTIME=$(honk_lifecycle_starttime "$HONK_LIFECYCLE_STOP_PID") || return 1
else
[ -z "$(pidof honk-core 2>/dev/null)" ] || { echo "honk lifecycle: refusing to stop unmanaged honk-core" >&2; return 1; }
[ -z "$(honk_lifecycle_procd_pid)" ] || { echo "honk lifecycle: procd instance is not the expected core" >&2; return 1; }
case "$(honk_lifecycle_procd_running)" in true|1) echo "honk lifecycle: procd instance is still running" >&2; return 1 ;; esac
HONK_LIFECYCLE_STOP_STARTTIME=
fi
if [ -n "$HONK_LIFECYCLE_STOP_PID" ]; then /etc/init.d/honk stop >/dev/null 2>&1 || :; fi
honk_lifecycle_drain "$hlp_timeout"
}
honk_lifecycle_start() {
hlp_timeout=${1:-30}
[ -z "$(pidof honk-core 2>/dev/null)" ] || { echo "honk lifecycle: refusing start while a core process exists" >&2; return 1; }
[ -z "$(honk_lifecycle_procd_pid)" ] || { echo "honk lifecycle: procd instance has not stopped" >&2; return 1; }
case "$(honk_lifecycle_procd_running)" in true|1) echo "honk lifecycle: procd instance is still running" >&2; return 1 ;; esac
honk_lifecycle_resources_clear || { echo "honk lifecycle: network resources have not drained" >&2; return 1; }
honk_lifecycle_parse_listen || { echo "honk lifecycle: invalid configured listener" >&2; return 1; }
hlp_listen=$HONK_LIFECYCLE_LISTEN
hlp_port=$HONK_LIFECYCLE_PORT
config_load honk
config_get config_mode main config_mode managed
if [ "$config_mode" != manual ] && honk_lifecycle_port_open "$hlp_port"; then echo "honk lifecycle: listener port remains occupied" >&2; return 1; fi
if [ -n "${HONK_MAINT_CAP:-}" ]; then export HONK_MAINT_CAP; fi
/etc/init.d/honk start >/dev/null 2>&1
hlp_start_status=$?
unset HONK_MAINT_CAP
[ "$hlp_start_status" = 0 ] || { echo "honk lifecycle: init start failed" >&2; return 1; }
hlp_stable=0
hlp_pid=
hlp_starttime=
# A config the core cannot run crash-loops through procd: every attempt gets a
# new PID, so a fixed timeout would burn the whole budget before the caller can
# be told the start failed. Count distinct PIDs and give up early instead.
hlp_seen_pid=
hlp_attempts=0
while [ "$hlp_timeout" -gt 0 ]; do
hlp_new_pid=$(honk_lifecycle_managed_pid 2>/dev/null)
if [ -n "$hlp_new_pid" ] && [ "$hlp_new_pid" != "$hlp_seen_pid" ]; then
hlp_seen_pid=$hlp_new_pid
hlp_attempts=$((hlp_attempts + 1))
if [ "$hlp_attempts" -gt 3 ]; then
echo "honk lifecycle: core keeps restarting, giving up" >&2
return 1
fi
fi
if [ -n "$hlp_new_pid" ]; then
hlp_new_starttime=$(honk_lifecycle_starttime "$hlp_new_pid") || hlp_new_starttime=
if [ -n "$hlp_new_starttime" ] && \
{ [ -z "${HONK_LIFECYCLE_STOP_PID:-}" ] || [ "$hlp_new_pid:$hlp_new_starttime" != "$HONK_LIFECYCLE_STOP_PID:$HONK_LIFECYCLE_STOP_STARTTIME" ]; } && \
{ [ "$config_mode" = manual ] && honk_lifecycle_ready_owned "$hlp_new_pid" || \
{ [ "$config_mode" != manual ] && honk_lifecycle_listener_owned "$hlp_new_pid" "$hlp_port" && \
curl -fsS --noproxy '*' --connect-timeout 1 --max-time 2 "http://$hlp_listen/api" -o /dev/null 2>/dev/null; }; }; then
if [ "$hlp_pid:$hlp_starttime" = "$hlp_new_pid:$hlp_new_starttime" ]; then
hlp_stable=$((hlp_stable + 1))
else
hlp_pid=$hlp_new_pid
hlp_starttime=$hlp_new_starttime
hlp_stable=1
fi
if [ "$hlp_stable" -ge 3 ]; then
HONK_LIFECYCLE_STOP_PID=$hlp_new_pid
HONK_LIFECYCLE_STOP_STARTTIME=$hlp_new_starttime
return 0
fi
else
hlp_stable=0
hlp_pid=
hlp_starttime=
fi
else
hlp_stable=0
hlp_pid=
hlp_starttime=
fi
sleep 1
hlp_timeout=$((hlp_timeout - 1))
done
echo "honk lifecycle: new managed core did not become stable and own its API listener" >&2
return 1
}
honk_lifecycle_restart() {
if [ -n "$(honk_lifecycle_managed_pid 2>/dev/null)" ]; then
HONK_LIFECYCLE_WAS_RUNNING=1
else
[ -z "$(pidof honk-core 2>/dev/null)" ] || { echo "honk lifecycle: refusing unmanaged honk-core" >&2; return 1; }
HONK_LIFECYCLE_WAS_RUNNING=0
fi
honk_lifecycle_stop "${1:-30}" && honk_lifecycle_start "${1:-30}"
}
+32
View File
@@ -0,0 +1,32 @@
#!/bin/sh
HONK_LOCK_DIR=/run/honk.lock
# PID alone is not enough to identify a process (PID reuse race). We store
# "pid starttime" where starttime is field 22 of /proc/pid/stat.
_honk_proc_starttime() {
cut -d' ' -f22 "/proc/$1/stat" 2>/dev/null
}
honk_lock_acquire() {
mkdir "$HONK_LOCK_DIR" 2>/dev/null || return 1
chmod 0700 "$HONK_LOCK_DIR" || { rmdir "$HONK_LOCK_DIR"; return 1; }
printf '%s %s\n' "$$" "$(_honk_proc_starttime $$)" > "$HONK_LOCK_DIR/pid" || { rm -f "$HONK_LOCK_DIR/pid"; rmdir "$HONK_LOCK_DIR"; return 1; }
}
honk_lock_is_stale() {
[ -r "$HONK_LOCK_DIR/pid" ] || return 1
read -r owner owner_start < "$HONK_LOCK_DIR/pid" || return 1
case "$owner" in ''|*[!0-9]*) return 1 ;; esac
# Dead if the PID is gone, or if it was reused by a newer process.
! kill -0 "$owner" 2>/dev/null && return 0
[ "$(_honk_proc_starttime "$owner")" = "$owner_start" ] || return 0
return 1
}
honk_lock_release() {
[ -r "$HONK_LOCK_DIR/pid" ] || return 1
read -r owner owner_start < "$HONK_LOCK_DIR/pid" || return 1
[ "$owner" = "$$" ] && [ "$owner_start" = "$(_honk_proc_starttime $$)" ] || return 1
rm -f "$HONK_LOCK_DIR/pid" && rmdir "$HONK_LOCK_DIR"
}
+57
View File
@@ -0,0 +1,57 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-only
requested=$1
case "$requested" in all|debug|info|warn|error) ;; *) exit 2 ;; esac
awk -v requested="$requested" '
function rank(level) {
if (level == "DEBUG" || level == "TRACE") return 0
if (level == "INFO" || level == "NOTICE") return 1
if (level == "WARN" || level == "WARNING") return 2
if (level == "ERROR" || level == "FATAL" || level == "CRIT" || level == "CRITICAL" || level == "ALERT" || level == "EMERG") return 3
return -1
}
function from_syslog(header, token, parts, count, level) {
count = split(header, parts, /[[:space:]]+/)
for (i = 1; i <= count; i++) {
token = parts[i]
if (token ~ /^[[:alnum:]_-]+\.(debug|info|notice|warn|warning|err|error|crit|critical|alert|emerg)$/) {
sub(/^.*\./, "", token)
if (token == "err") token = "error"
return rank(toupper(token))
}
}
return -1
}
{
if (requested == "all" || requested == "debug") { print; next }
line = $0
header = line
message = ""
separator = index(line, "]: ")
if (separator > 0) {
header = substr(line, 1, separator + 1)
message = substr(line, separator + 3)
} else {
separator = index(line, ": ")
if (separator > 0) {
header = substr(line, 1, separator - 1)
message = substr(line, separator + 2)
}
}
sub(/^[0-9][0-9][0-9][0-9]-[0-9T:.+Z-]+[[:space:]]+/, "", message)
sub(/^[[:space:]]+/, "", message)
if (match(message, /^(TRACE|DEBUG|INFO|NOTICE|WARN|WARNING|ERROR|FATAL|CRIT|CRITICAL|ALERT|EMERG)([[:space:]:]|$)/)) {
level = message
sub(/[[:space:]:].*$/, "", level)
severity = rank(level)
} else if (match(line, /^<[0-7]>/)) {
priority = substr(line, 2, 1) + 0
severity = priority <= 3 ? 3 : priority == 4 ? 2 : priority <= 6 ? 1 : 0
} else {
severity = from_syslog(header)
}
threshold = requested == "info" ? 1 : requested == "warn" ? 2 : 3
if (severity >= threshold) print
}'
+9
View File
@@ -0,0 +1,9 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-only
honk_add_log_lines() {
[ -s "$1" ] || return 0
while IFS= read -r line; do
json_add_string "" "$line"
done < "$1"
}
+1207
View File
File diff suppressed because it is too large. Load diff
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
exec sed -E \
-e 's#([[:alnum:]+.-]+://)[^[:space:]"<>]+#\1[redacted]#g' \
-e 's/([Bb]earer[[:space:]]+)[^[:space:],;]+/\1[redacted]/g' \
-e 's/([Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd]|[Tt][Oo][Kk][Ee][Nn]|[Ss][Ee][Cc][Rr][Ee][Tt]|[Aa][Uu][Tt][Hh][Oo][Rr][Ii][Zz][Aa][Tt][Ii][Oo][Nn])[^:=]*[=:][[:space:]]*.*/\1=[redacted]/'
+22
View File
@@ -0,0 +1,22 @@
global {
wan_interface: auto
lan_interface: br-lan
log_level: info
dial_mode: domain
allow_insecure: false
auto_config_kernel_parameter: true
data_dir: '/etc/honk'
store_subscribe: true
nfqueue_enable: true
}
experimental {
native_api {
enabled: true
listen: '127.0.0.1:9527'
password_auth: true
allow_anonymous_loopback: false
config_write: true
ui: '/usr/share/doona'
}
}
+292
View File
@@ -0,0 +1,292 @@
#!/bin/sh
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
umask 077
. /usr/share/libubox/jshn.sh
UPDATE_ROOT=/tmp/honk-update
TRUST_ROOT=/etc/honk/update-trust
FEED_FILE=$TRUST_ROOT/feed.url
MANIFEST_KEY=$TRUST_ROOT/manifest.pub
MANIFEST=$UPDATE_ROOT/manifest.json
SIGNATURE=$UPDATE_ROOT/manifest.json.sig
RECORDS=$UPDATE_ROOT/packages.tsv
UPDATE_LOCK_OWNED=0
CHECK_DIR=
update_lock_acquire() {
. /usr/share/honk/lock.sh || return 1
honk_lock_acquire || return 1
UPDATE_LOCK_OWNED=1
}
update_lock_release() {
[ "$UPDATE_LOCK_OWNED" = 1 ] || return 0
honk_lock_release >/dev/null 2>&1 || return 1
UPDATE_LOCK_OWNED=0
}
discard_check_dir() {
case "$CHECK_DIR" in
"$UPDATE_ROOT"/.check.*)
[ -d "$CHECK_DIR" ] && [ ! -L "$CHECK_DIR" ] && rm -rf "$CHECK_DIR"
;;
esac
CHECK_DIR=
}
cleanup_update() {
discard_check_dir
update_lock_release || :
}
emit_update() {
json_init
json_add_boolean ok "$1"
json_add_boolean available "$2"
json_add_boolean apply_enabled "$3"
json_add_string message "$4"
json_add_string reason "$5"
[ -z "$6" ] || json_add_string version "$6"
json_dump
}
safe_file() {
[ -f "$1" ] && [ ! -L "$1" ] && [ "$(stat -c '%u:%a' "$1" 2>/dev/null)" = "0:$2" ]
}
prepare_update_root() {
if [ -e "$UPDATE_ROOT" ] || [ -L "$UPDATE_ROOT" ]; then
[ -d "$UPDATE_ROOT" ] && [ ! -L "$UPDATE_ROOT" ] && [ "$(stat -c '%u:%a' "$UPDATE_ROOT" 2>/dev/null)" = 0:700 ] || return 1
else
mkdir "$UPDATE_ROOT" && chmod 0700 "$UPDATE_ROOT" || return 1
fi
}
read_trust() {
[ -d "$TRUST_ROOT" ] && [ ! -L "$TRUST_ROOT" ] && [ "$(stat -c '%u:%a' "$TRUST_ROOT" 2>/dev/null)" = 0:700 ] || return 1
safe_file "$FEED_FILE" 600 && safe_file "$MANIFEST_KEY" 600 || return 1
[ "$(wc -c < "$FEED_FILE" | tr -d ' ')" -le 512 ] || return 1
feed_url=$(cat "$FEED_FILE") || return 1
[ -n "$feed_url" ] || return 1
case "$feed_url" in *'?'*|*'#'*|*'@'*|*' '*|*'\'*|*'"'*|*"'"*) return 1 ;; esac
echo "$feed_url" | grep -Eq '^https://[A-Za-z0-9.-]+(:[0-9]+)?(/[A-Za-z0-9._~/%+-]*)?$' || return 1
return 0
}
manifest_field() {
jsonfilter -i "$MANIFEST" -e "$1" 2>/dev/null
}
installed_version() {
awk -v wanted="$1" '
$0 == "P:" wanted { found=1; next }
found && /^V:/ { print substr($0, 3); exit }
found && /^P:/ { exit }
' /lib/apk/db/installed 2>/dev/null
}
validate_manifest() {
validate_url=$1
validate_json=$(cat "$MANIFEST") || return 1
json_load "$validate_json" 2>/dev/null || return 1
json_get_type validate_packages_type packages
[ "$validate_packages_type" = array ] || return 1
json_select packages || return 1
json_get_keys validate_package_keys
[ "$(printf '%s\n' "$validate_package_keys" | wc -w | tr -d ' ')" = 2 ] || return 1
json_select .. || return 1
[ "$(manifest_field '@.schema')" = 1 ] || return 1
[ "$(manifest_field '@.product')" = openwrt-honk ] || return 1
# Target must match this device: map uname -m to manifest arch names.
case "$(uname -m)" in
x86_64) _local_arch=x86_64 ;;
aarch64) _local_arch=aarch64 ;;
armv7l) _local_arch=armv7 ;;
*) return 1 ;;
esac
[ "$(manifest_field '@.target.architecture')" = "$_local_arch" ] || return 1
# OpenWrt version must match: 24.10 (opkg/ipk) or 25.12 (apk).
_local_owrt=$(sed -n 's/^DISTRIB_RELEASE="\([0-9]*\.[0-9]*\).*/\1/p' /etc/openwrt_release 2>/dev/null | head -n 1)
case "$_local_owrt" in 24.10|25.12) ;; *) return 1 ;; esac
[ "$(manifest_field '@.target.openwrt')" = "$_local_owrt" ] || return 1
case "$_local_owrt" in
24.10) [ "$(manifest_field '@.target.package_format')" = ipk ] || return 1 ;;
25.12) [ "$(manifest_field '@.target.package_format')" = apk ] || return 1 ;;
esac
[ "$(manifest_field '@.target.kernel_min')" = 6.12 ] || return 1
[ "$(manifest_field '@.target.requires_btf')" = true ] || return 1
[ "$(manifest_field '@.min_updater_api')" = 1 ] || return 1
[ "$(manifest_field '@.state_schema')" = 2 ] || return 1
# (arch and OpenWrt version already validated against local system above)
[ -r /sys/kernel/btf/vmlinux ] || return 1
kernel_version=$(uname -r | sed 's/-.*//')
kernel_major=${kernel_version%%.*}
kernel_minor=${kernel_version#*.}
kernel_minor=${kernel_minor%%.*}
case "$kernel_major:$kernel_minor" in *[!0-9:]*|:*) return 1 ;; esac
[ "$kernel_major" -gt 6 ] || { [ "$kernel_major" = 6 ] && [ "$kernel_minor" -ge 12 ]; } || return 1
validate_commit=$(manifest_field '@.core.commit')
case "$validate_commit" in *[!0-9a-f]*|'') return 1 ;; esac
[ "${#validate_commit}" -eq 40 ] || return 1
validate_commit=$(manifest_field '@.doona.commit')
case "$validate_commit" in *[!0-9a-f]*|'') return 1 ;; esac
[ "${#validate_commit}" -eq 40 ] || return 1
for validate_sha in \
"$(manifest_field '@.core.artifact_sha256')" \
"$(manifest_field '@.doona.patch_sha256')" \
"$(manifest_field '@.doona.artifact_sha256')" \
"$(manifest_field '@.sdk_sha256')"; do
case "$validate_sha" in *[!0-9a-f]*|'') return 1 ;; esac
[ "${#validate_sha}" -eq 64 ] || return 1
done
validate_version=$(manifest_field '@.packages[0].version')
case "$validate_version" in ''|*[!A-Za-z0-9.+~-]*) return 1 ;; esac
: > "$RECORDS" || return 1
validate_seen=' '
validate_available=0
for validate_index in 0 1; do
validate_name=$(manifest_field "@.packages[$validate_index].name")
validate_version=$(manifest_field "@.packages[$validate_index].version")
validate_arch=$(manifest_field "@.packages[$validate_index].arch")
validate_filename=$(manifest_field "@.packages[$validate_index].filename")
validate_size=$(manifest_field "@.packages[$validate_index].size")
validate_sha=$(manifest_field "@.packages[$validate_index].sha256")
validate_package_url=$(manifest_field "@.packages[$validate_index].url")
case "$validate_name" in honk|luci-app-honk) ;; *) return 1 ;; esac
case "$validate_seen" in *" $validate_name "*) return 1 ;; esac
validate_seen="$validate_seen$validate_name "
case "$validate_version" in ''|*[!A-Za-z0-9.+~-]*) return 1 ;; esac
case "$validate_arch" in x86_64|aarch64|armv7|noarch) ;; *) return 1 ;; esac
case "$validate_name:$validate_arch" in honk:x86_64|honk:aarch64|honk:armv7|luci-app-honk:x86_64|luci-app-honk:aarch64|luci-app-honk:armv7|luci-app-honk:noarch) ;; *) return 1 ;; esac
[ "$validate_filename" = "$validate_name-$validate_version.apk" ] || [ "$validate_filename" = "${validate_name}_${validate_version}_${validate_arch}.ipk" ] || return 1
case "$validate_filename" in *'/'*|*'..'*|*[!A-Za-z0-9.+_-]*) return 1 ;; esac
case "$validate_size" in ''|*[!0-9]*) return 1 ;; esac
[ "$validate_size" -gt 0 ] && [ "$validate_size" -le 134217728 ] || return 1
case "$validate_sha" in *[!0-9a-f]*|'') return 1 ;; esac
[ "${#validate_sha}" -eq 64 ] || return 1
[ "$validate_package_url" = "${validate_url%/}/packages/$validate_filename" ] || return 1
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$validate_name" "$validate_version" "$validate_arch" "$validate_filename" "$validate_size" "$validate_sha" "$validate_package_url" >> "$RECORDS" || return 1
[ "$(installed_version "$validate_name")" = "$validate_version" ] || validate_available=1
done
[ "$(wc -l < "$RECORDS" | tr -d ' ')" = 2 ] || return 1
for validate_required in honk luci-app-honk; do
case "$validate_seen" in *" $validate_required "*) ;; *) return 1 ;; esac
done
update_version=$(awk -F '\t' '$1 == "honk" { print $2; exit }' "$RECORDS")
return 0
}
verify_signed_manifest() {
read_trust || return 2
prepare_update_root || return 3
CHECK_DIR=$(mktemp -d "$UPDATE_ROOT/.check.XXXXXX") || return 3
check_dir=$CHECK_DIR
chmod 0700 "$check_dir" || { discard_check_dir; return 3; }
check_manifest=$check_dir/manifest.json
check_signature=$check_dir/manifest.json.sig
if ! curl -fLsS --proto '=https' --proto-redir '=https' --tlsv1.2 --connect-timeout 4 --max-time 12 --max-filesize 65536 \
"${feed_url%/}/manifest.json" -o "$check_manifest" 2>/dev/null ||
! curl -fLsS --proto '=https' --proto-redir '=https' --tlsv1.2 --connect-timeout 4 --max-time 8 --max-filesize 4096 \
"${feed_url%/}/manifest.json.sig" -o "$check_signature" 2>/dev/null ||
! usign -V -m "$check_manifest" -p "$MANIFEST_KEY" -x "$check_signature" >/dev/null 2>&1; then
discard_check_dir
return 4
fi
MANIFEST=$check_manifest
RECORDS=$check_dir/packages.tsv
if ! validate_manifest "$feed_url"; then
discard_check_dir
return 5
fi
cp "$check_manifest" "$UPDATE_ROOT/manifest.json.new" && cp "$check_signature" "$UPDATE_ROOT/manifest.json.sig.new" || { discard_check_dir; return 3; }
cp "$RECORDS" "$UPDATE_ROOT/packages.tsv.new" || { discard_check_dir; return 3; }
printf '%s\n' "$(date +%s)" > "$UPDATE_ROOT/checked-at.new" || { discard_check_dir; return 3; }
chmod 0600 "$UPDATE_ROOT/manifest.json.new" "$UPDATE_ROOT/manifest.json.sig.new" "$UPDATE_ROOT/packages.tsv.new" "$UPDATE_ROOT/checked-at.new" || { discard_check_dir; return 3; }
mv -f "$UPDATE_ROOT/manifest.json.new" "$UPDATE_ROOT/manifest.json" &&
mv -f "$UPDATE_ROOT/manifest.json.sig.new" "$UPDATE_ROOT/manifest.json.sig" &&
mv -f "$UPDATE_ROOT/packages.tsv.new" "$UPDATE_ROOT/packages.tsv" &&
mv -f "$UPDATE_ROOT/checked-at.new" "$UPDATE_ROOT/checked-at" || { discard_check_dir; return 3; }
discard_check_dir
return 0
}
update_check() {
read_trust || { emit_update true false false no_release trusted_feed_unavailable; return 0; }
update_lock_acquire || { emit_update false false false update_check_failed operation_in_progress; return 1; }
verify_signed_manifest
check_rc=$?
update_lock_release
case "$check_rc" in
2) emit_update true false false no_release trusted_feed_unavailable; return 0 ;;
4) emit_update false false false update_check_failed signed_manifest_unavailable; return 1 ;;
3) emit_update false false false update_check_failed update_storage_unavailable; return 1 ;;
5) emit_update false false false update_check_failed manifest_incompatible; return 1 ;;
0) ;;
*) emit_update false false false update_check_failed manifest_invalid; return 1 ;;
esac
if [ "$validate_available" = 1 ]; then
emit_update true true false update_available rollback_tuple_unavailable "$update_version"
else
emit_update true false false no_update already_current "$update_version"
fi
}
update_gate() {
read_trust || { emit_update true false false no_release trusted_feed_unavailable; return 0; }
update_lock_acquire || { emit_update false false false update_check_failed operation_in_progress; return 1; }
gate_ok=1
gate_available=0
gate_reason=
prepare_update_root || { gate_ok=0; gate_reason=update_storage_unavailable; }
if [ "$gate_ok" = 1 ]; then
for gate_file in "$UPDATE_ROOT/manifest.json" "$UPDATE_ROOT/manifest.json.sig" "$UPDATE_ROOT/packages.tsv" "$UPDATE_ROOT/checked-at"; do
safe_file "$gate_file" 600 || { gate_reason=signed_manifest_missing; break; }
done
fi
if [ "$gate_ok" = 1 ] && [ -z "$gate_reason" ]; then
checked_at=$(cat "$UPDATE_ROOT/checked-at")
case "$checked_at" in ''|*[!0-9]*) gate_reason=signed_manifest_expired ;; esac
if [ -z "$gate_reason" ] && [ $(( $(date +%s) - checked_at )) -gt 600 ]; then gate_reason=signed_manifest_expired; fi
fi
if [ "$gate_ok" = 1 ] && [ -z "$gate_reason" ]; then
usign -V -m "$UPDATE_ROOT/manifest.json" -p "$MANIFEST_KEY" -x "$UPDATE_ROOT/manifest.json.sig" >/dev/null 2>&1 || { gate_ok=0; gate_reason=manifest_signature_invalid; }
fi
if [ "$gate_ok" = 1 ] && [ -z "$gate_reason" ]; then
MANIFEST=$UPDATE_ROOT/manifest.json
RECORDS=$UPDATE_ROOT/packages.tsv
validate_manifest "$feed_url" || { gate_ok=0; gate_reason=manifest_incompatible; }
fi
gate_available=${validate_available:-0}
gate_version=${update_version:-}
update_lock_release
if [ "$gate_ok" != 1 ]; then emit_update false false false update_check_failed "$gate_reason"; return 1; fi
if [ -n "$gate_reason" ]; then emit_update true false false no_release "$gate_reason"; return 0; fi
if [ "$gate_available" = 1 ]; then
emit_update true true false update_available rollback_tuple_unavailable "$gate_version"
else
emit_update true false false no_update already_current "$gate_version"
fi
}
update_apply() {
update_gate >/dev/null || { emit_update false false false update_apply_failed trusted_manifest_invalid; return 1; }
[ "$validate_available" = 1 ] || { emit_update false false false update_apply_failed no_update; return 1; }
# No previous signed package tuple or exercised downgrade path is available yet.
# Keep the backend gate closed even when the update manifest itself is compatible.
emit_update false false false update_apply_failed rollback_tuple_unavailable
return 1
}
trap cleanup_update EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
case "$1" in
check) update_check ;;
gate) update_gate ;;
apply) update_apply ;;
*) emit_update false false false update_check_failed invalid_action; exit 2 ;;
esac
+103 -10
View File
@@ -1,19 +1,112 @@
# Copyright (C) 2016 Openwrt.org # SPDX-License-Identifier: GPL-3.0-only
#
# This is free software, licensed under the Apache License, Version 2.0 .
# #
# Copyright (C) 2026 kenzok8
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-honk PKG_NAME:=luci-app-honk
PKG_VERSION:=1.1.4 PKG_VERSION:=2026.11.01
PKG_RELEASE:=11 PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
LUCI_TITLE:=LuCI support for honk PKG_SOURCE:=doona-4569bd91-57b2b25287ba.tar.gz
LUCI_PKGARCH:=all # Each package Makefile is evaluated on its own, so this tag is defined here too
LUCI_DEPENDS:=+honk +luci-base +rpcd-mod-ucode +unzip # instead of relying on the honk package's variable. It must match honk/Makefile:
# scripts/check.sh enforces that.
HONK_SOURCE_TAG?=source-41281523460
PKG_SOURCE_URL?=https://github.com/kenzok8/openwrt-honk/releases/download/$(HONK_SOURCE_TAG)
PKG_SOURCE_SUBDIR:=doona
PKG_HASH:=57b2b25287bae791e9a01e14e0c2deb1351e4135933959214d8996ed05458838
include $(TOPDIR)/feeds/luci/luci.mk -include $(CURDIR)/generated-doona-stage.mk
# call BuildPackage - OpenWrt buildroot signature PKG_BUILD_DEPENDS:=luci-base/host
include $(INCLUDE_DIR)/package.mk
define Package/luci-app-honk
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=LuCI support for Honk
DEPENDS:=+honk +rpcd +luci-base +cgi-io +jsonfilter
PKGARCH:=all
URL:=https://github.com/kenzok8/openwrt-honk
MAINTAINER:=kenzok8
endef
define Package/luci-app-honk/description
LuCI web interface for managing Honk.
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)/root/usr/share/doona
$(TAR) --strip-components=1 -C $(PKG_BUILD_DIR)/root/usr/share/doona -xzf $(DL_DIR)/$(PKG_SOURCE)
$(SED) 's#^Build patch: .*#Build patch: ci/patches/doona-openwrt.patch#' $(PKG_BUILD_DIR)/root/usr/share/doona/SOURCE
$(INSTALL_DIR) $(PKG_BUILD_DIR)/root/usr/share/licenses/honk-doona
$(INSTALL_DATA) $(PKG_BUILD_DIR)/root/usr/share/doona/LICENSE $(PKG_BUILD_DIR)/root/usr/share/licenses/honk-doona/LICENSE
$(INSTALL_DATA) $(PKG_BUILD_DIR)/root/usr/share/doona/NOTICE $(PKG_BUILD_DIR)/root/usr/share/licenses/honk-doona/NOTICE
$(INSTALL_DATA) $(PKG_BUILD_DIR)/root/usr/share/doona/THIRD-PARTY-NOTICES.txt $(PKG_BUILD_DIR)/root/usr/share/licenses/honk-doona/THIRD-PARTY-NOTICES.txt
$(INSTALL_DIR) $(PKG_BUILD_DIR)/root/usr/share/licenses/honk-doona/LICENSES
$(CP) $(PKG_BUILD_DIR)/root/usr/share/doona/LICENSES/* $(PKG_BUILD_DIR)/root/usr/share/licenses/honk-doona/LICENSES/
po2lmo ./po/zh-cn/honk.po $(PKG_BUILD_DIR)/honk.zh-cn.lmo
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/luci-app-honk/install
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
$(INSTALL_BIN) ./root/usr/libexec/rpcd/honk $(1)/usr/libexec/rpcd/honk
$(INSTALL_DIR) $(1)/usr/share/luci-app-honk
$(INSTALL_BIN) ./root/usr/share/luci-app-honk/pkg-info.sh $(1)/usr/share/luci-app-honk/pkg-info.sh
$(INSTALL_BIN) ./root/usr/share/luci-app-honk/refresh-index.sh $(1)/usr/share/luci-app-honk/refresh-index.sh
$(INSTALL_BIN) ./root/usr/share/luci-app-honk/update-pkg.sh $(1)/usr/share/luci-app-honk/update-pkg.sh
$(INSTALL_BIN) ./root/usr/share/luci-app-honk/update-geo.sh $(1)/usr/share/luci-app-honk/update-geo.sh
$(INSTALL_BIN) ./root/usr/share/luci-app-honk/geo-cron.sh $(1)/usr/share/luci-app-honk/geo-cron.sh
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
$(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-honk.json $(1)/usr/share/luci/menu.d/luci-app-honk.json
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
$(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-honk.json $(1)/usr/share/rpcd/acl.d/luci-app-honk.json
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/honk
$(INSTALL_DATA) ./htdocs/luci-static/resources/view/honk/*.js $(1)/www/luci-static/resources/view/honk/
$(INSTALL_DATA) ./htdocs/luci-static/resources/view/honk/*.css $(1)/www/luci-static/resources/view/honk/
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/honk/vendor
$(INSTALL_DATA) ./htdocs/luci-static/resources/view/honk/vendor/* $(1)/www/luci-static/resources/view/honk/vendor/
$(INSTALL_DIR) $(1)/usr/share/doona
$(CP) $(PKG_BUILD_DIR)/root/usr/share/doona/* $(1)/usr/share/doona/
$(INSTALL_DIR) $(1)/usr/share/licenses/honk-doona
$(CP) $(PKG_BUILD_DIR)/root/usr/share/licenses/honk-doona/* $(1)/usr/share/licenses/honk-doona/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
$(INSTALL_DATA) $(PKG_BUILD_DIR)/honk.zh-cn.lmo $(1)/usr/lib/lua/luci/i18n/honk.zh-cn.lmo
endef
define Package/luci-app-honk/postinst
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || { \
rm -f /tmp/luci-indexcache.*
rm -rf /tmp/luci-modulecache/
/etc/init.d/rpcd reload 2>/dev/null
/etc/init.d/uhttpd restart 2>/dev/null
exit 0
}
endef
define Package/luci-app-honk/postrm
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || { \
rm -rf /tmp/luci-indexcache /tmp/luci-indexcache.* /tmp/luci-modulecache
exit 0
}
endef
$(eval $(call BuildPackage,luci-app-honk))
@@ -0,0 +1,798 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require rpc';
'require view';
'require view.honk.rpc as honk';
'require view.honk.converter as converter';
'require ui';
const DAE_LIMIT = 2 * 1024 * 1024;
const PASTE_LIMIT = 2 * 1024 * 1024;
function utf8Length(value) {
return new TextEncoder().encode(value).length;
}
function isHttpUrl(value) {
try {
const url = new URL(value);
return (url.protocol === 'http:' || url.protocol === 'https:') && !!url.hostname;
}
catch (e) {
return false;
}
}
function loadYamlParser() {
if (window.jsyaml && window.jsyaml.load)
return Promise.resolve(window.jsyaml);
return new Promise(function(resolve, reject) {
const script = document.createElement('script');
script.src = L.resource('view/honk/vendor/js-yaml.min.js');
script.onload = function() {
if (window.jsyaml && window.jsyaml.load)
resolve(window.jsyaml);
else
reject(new Error(_('YAML parser failed to load')));
};
script.onerror = function() { reject(new Error(_('YAML parser failed to load'))); };
document.head.appendChild(script);
});
}
function parsePasted(content) {
let text = String(content || '').trim();
if (!text)
return Promise.resolve({ links: [], rejected: 1 });
if (!converter.looksLikeNodeList(text)) {
const decoded = converter.tryBase64Decode(text);
if (decoded && (converter.looksLikeNodeList(decoded) ||
converter.looksLikeClashYaml(decoded) || converter.looksLikeSurge(decoded)))
text = decoded;
}
if (converter.looksLikeMihomoJson(text))
return Promise.resolve(converter.parseMihomoJson(text));
if (converter.looksLikeClashYaml(text)) {
return loadYamlParser().then(function(yaml) {
const doc = yaml.load(text);
const proxies = (doc && doc.proxies) || [];
const links = [];
let rejected = 0;
proxies.forEach(function(node) {
if (converter.isMetadataProxy(node)) {
rejected++;
return;
}
const result = converter.convertProxy(node);
if (result.ok) links.push(result.link);
else rejected++;
});
return { links: links, rejected: rejected };
});
}
if (converter.looksLikeSurge(text))
return Promise.resolve(converter.parseSurgeProxies(text));
return Promise.resolve(converter.parseUriList(text));
}
return view.extend({
handleSave: null,
handleSaveApply: null,
handleReset: null,
render: function() {
honk.installStyles();
const page = E('div', { 'class': 'cbi-map honk-page' });
page.appendChild(E('div', { 'class': 'honk-header' }, [
E('h2', {}, _('Configuration')),
E('p', { 'class': 'honk-header-sub' }, _('Honk reads its nodes, subscriptions and routing from /etc/honk/config.dae.'))
]));
const modeText = E('span', {}, '');
const modeNote = E('p', { 'class': 'honk-note honk-mode-note' }, [ modeText ]);
page.appendChild(modeNote);
/* --- Add subscription --- */
const subStatus = E('p', { 'class': 'honk-note', 'role': 'status', 'aria-live': 'polite' }, '');
const nameInput = E('input', {
'class': 'cbi-input-text', 'name': 'sub_name',
'placeholder': _('Name, e.g. MyAirport'), 'maxlength': '256'
});
const urlInput = E('input', {
'class': 'cbi-input-text', 'name': 'sub_url',
'placeholder': 'https://…', 'inputmode': 'url'
});
const subBtn = E('button', { 'class': 'cbi-button cbi-button-apply', 'type': 'button' }, [ _('Add subscription') ]);
subBtn.addEventListener('click', function(ev) {
ev.preventDefault();
const name = nameInput.value.trim();
const url = urlInput.value.trim();
if (!name) { subStatus.textContent = _('Name is required.'); nameInput.focus(); return; }
if (!isHttpUrl(url)) { subStatus.textContent = _('A valid HTTP or HTTPS URL is required.'); urlInput.focus(); return; }
subBtn.disabled = true;
preflightSubscription(url, subStatus).then(function(proceed) {
if (!proceed)
return null;
subStatus.textContent = _('Adding subscription…');
return withLongTimeout(function() {
return honk.addSubscription(name, url).then(honk.ensureOk);
}).then(function(result) {
subStatus.textContent = honk.resultMessage(result, _('Subscription saved with usable nodes.'));
nameInput.value = '';
urlInput.value = '';
}).catch(function(error) {
subStatus.textContent = honk.errorMessage(error, _('Could not add the subscription.'));
});
}).finally(function() { subBtn.disabled = false; });
});
const subModeHint = E('p', { 'class': 'honk-note honk-mode-hint' }, _('Manual mode: this tool is disabled until you restore managed mode (link at the top).'));
subModeHint.hidden = true;
const subSection = E('section', { 'class': 'honk-card' }, [
E('p', { 'class': 'honk-note' }, _('Enter a name and subscription URL. Honk checks for usable nodes before saving.')),
subModeHint,
E('div', { 'class': 'honk-subscription-add' }, [ nameInput, urlInput, subBtn ]),
subStatus
]);
/* LuCI 的 rpc 默认 20s 超时;核心校验节点、重启服务都可能超过它,
期间客户端一断,服务端处理器会被杀掉(留下半完成状态)。 */
function withLongTimeout(fn) {
const previous = L.env.rpctimeout;
L.env.rpctimeout = 180;
return Promise.resolve().then(fn).finally(function() {
L.env.rpctimeout = previous;
});
}
/* honk 内核支持的节点类型(见 core 的 clash.rs protocol_from_name):
ssr / snell / 带插件的 ss / 小众协议都会被内核判为不支持。 */
const SUPPORTED_PROTOCOLS = {
'socks': 1, 'socks5': 1, 'ss': 1, 'shadowsocks': 1, 'trojan': 1,
'vmess': 1, 'vless': 1, 'hysteria': 1, 'hysteria2': 1, 'hy2': 1,
'tuic': 1, 'juicity': 1, 'anytls': 1
};
/* JSON 订阅里 outbounds 会混进这些非节点类型,不能算作节点。 */
const NON_NODE_TYPES = {
'direct': 1, 'block': 1, 'dns': 1, 'selector': 1, 'urltest': 1,
'url-test': 1, 'fallback': 1, 'load-balance': 1, 'relay': 1, 'reject': 1
};
function analyzeProbe(probe) {
const counts = {};
let total = 0;
let usable = 0;
let unsupported = 0;
const bump = function(type) {
const name = String(type || '').toLowerCase();
if (!name || NON_NODE_TYPES[name])
return;
counts[name] = (counts[name] || 0) + 1;
total++;
if (SUPPORTED_PROTOCOLS[name])
usable++;
else
unsupported++;
};
if (probe.format === 'base64' && probe.content) {
const decoded = converter.tryBase64Decode(probe.content) || '';
(decoded.match(/^[A-Za-z0-9+.-]+:\/\//gim) || []).forEach(function(scheme) {
bump(scheme.replace(/:\/\/$/, ''));
});
}
else if (probe.types) {
String(probe.types).trim().split(/\s+/).forEach(function(pair) {
const parts = pair.split(':');
if (parts.length === 2) {
const n = parseInt(parts[1], 10);
for (let i = 0; i < (isNaN(n) ? 0 : n); i++)
bump(parts[0]);
}
});
}
const labels = {
clash: 'Clash / mihomo YAML',
json: 'JSON(sing-box 等)',
records: 'Surge / Loon 记录式 INI',
uri: 'URI 列表',
base64: 'Base64'
};
const label = labels[probe.format] || _('unknown format');
const detail = Object.keys(counts).sort().map(function(type) {
return type + ' ×' + counts[type] + (SUPPORTED_PROTOCOLS[type] ? '' : ' ' + _('unsupported'));
}).join(', ');
const text = total
? _('Detected %s · %d node(s): %s').format(label, total, detail)
: _('Detected %s.').format(label);
return { text: text, total: total, usable: usable, unsupported: unsupported };
}
/* 保存前预检:能全部用就直接保存;全不可用就拦下;部分可用问一句。
预检本身失败(网络抖动等)不拦保存,退回原来的行为。 */
function preflightSubscription(url, status) {
status.textContent = _('Checking the subscription before saving…');
return honk.probeSubscription(url).then(function(probe) {
const verdict = analyzeProbe(probe);
if (!verdict.total)
return true;
status.textContent = verdict.text;
if (verdict.usable === 0) {
status.textContent = verdict.text + ' ' + _('This core cannot use any of these nodes, so the subscription was not saved.');
return false;
}
if (verdict.unsupported && !confirm(verdict.text + '\n\n' + _('Save the subscription anyway?')))
return false;
return true;
}).catch(function() {
return true;
});
}
/* --- Paste nodes --- */
const pasteStatus = E('p', { 'class': 'honk-note', 'role': 'status', 'aria-live': 'polite' }, '');
const pasteInput = E('textarea', {
'class': 'cbi-input-textarea', 'name': 'paste',
'rows': '10', 'spellcheck': 'false',
'placeholder': _('Paste Clash YAML, Surge INI, Base64 or share links.')
});
const pasteBtn = E('button', { 'class': 'cbi-button cbi-button-apply', 'type': 'button' }, [ _('Import nodes') ]);
pasteBtn.addEventListener('click', function(ev) {
ev.preventDefault();
const content = pasteInput.value.trim();
if (!content) { pasteStatus.textContent = _('Paste nodes or subscription content first.'); pasteInput.focus(); return; }
if (utf8Length(content) > PASTE_LIMIT) { pasteStatus.textContent = _('The pasted content is too large.'); return; }
pasteBtn.disabled = true;
pasteStatus.textContent = _('Parsing nodes…');
parsePasted(content).then(function(parsed) {
const links = (parsed && parsed.links) || [];
if (!links.length) {
pasteStatus.textContent = _('No nodes could be parsed from the input.');
return;
}
pasteStatus.textContent = _('Importing %d node(s)…').format(links.length);
return honk.addNodes(links.join('\n')).then(honk.ensureOk).then(function(result) {
pasteStatus.textContent = honk.resultMessage(result, _('%d node(s) imported.').format(links.length));
pasteInput.value = '';
});
}).catch(function(error) {
pasteStatus.textContent = honk.errorMessage(error, _('Could not import nodes.'));
}).finally(function() { pasteBtn.disabled = false; });
});
const pasteModeHint = E('p', { 'class': 'honk-note honk-mode-hint' }, _('Manual mode: this tool is disabled until you restore managed mode (link at the top).'));
pasteModeHint.hidden = true;
const pasteSection = E('section', { 'class': 'honk-card' }, [
E('p', { 'class': 'honk-note' }, _('Paste Clash YAML, Surge INI, Base64 or share links. They are parsed locally into nodes and appended to /etc/honk/config.dae.')),
pasteModeHint,
pasteInput,
E('div', { 'class': 'honk-actions' }, [ pasteBtn ]),
pasteStatus
]);
/* --- Import a complete configuration and start Honk with it --- */
const importStatus = E('p', { 'class': 'honk-note', 'role': 'status', 'aria-live': 'polite' }, '');
/* 原生 file 控件在各主题下都被拉成一个大盒子、内嵌按钮贴着左上角,
和右边的按钮对不齐。改成隐藏原生控件、用 label 当按钮。 */
const fileInput = E('input', { 'type': 'file', 'name': 'dae', 'accept': '.dae,text/plain', 'hidden': 'hidden' });
const fileName = E('span', { 'class': 'honk-file-name' }, _('No file chosen'));
const filePick = E('label', { 'class': 'cbi-button honk-file-pick' }, [ _('Choose file'), fileInput ]);
fileInput.addEventListener('change', function() {
const picked = fileInput.files && fileInput.files[0];
fileName.textContent = picked ? picked.name : _('No file chosen');
});
const importBtn = E('button', { 'class': 'cbi-button cbi-button-apply', 'type': 'button' }, [ _('Import and start') ]);
const importSection = E('section', { 'class': 'honk-card' }, [
E('p', { 'class': 'honk-note' }, _('Upload a complete dae file (subscriptions, nodes and routing included) and Honk starts with it: the file is validated, written to /etc/honk/config.dae and the service is restarted. To hand-edit or paste instead, use the editor below.')),
E('div', { 'class': 'honk-file-row' }, [ filePick, fileName, importBtn ]),
importStatus
]);
/* --- Direct configuration --- */
const editorStatus = E('p', { 'class': 'honk-note', 'role': 'status', 'aria-live': 'polite' }, '');
const editor = E('textarea', {
'class': 'cbi-input-textarea honk-config-editor', 'rows': '26',
'spellcheck': 'false', 'wrap': 'off', 'aria-label': _('Full dae configuration')
});
const saveBtn = E('button', { 'class': 'cbi-button cbi-button-positive', 'type': 'button' }, [ _('Save and apply') ]);
const reloadBtn = E('button', { 'class': 'cbi-button', 'type': 'button' }, [ _('Reload from disk') ]);
const validateBtn = E('button', { 'class': 'cbi-button cbi-button-link', 'type': 'button' }, [ _('Check configuration') ]);
const managedBtn = E('a', { 'href': '#', 'class': 'honk-inline-link', 'role': 'button' }, [ _('Restore managed mode') ]);
managedBtn.hidden = true;
modeNote.appendChild(managedBtn);
let editorBaseline = '';
function setEditorContent(content, message) {
editor.value = content || '';
editorBaseline = editor.value;
editorStatus.textContent = message || '';
}
function confirmEditorReplace() {
return editor.value === editorBaseline || confirm(_('Discard the unsaved editor changes?'));
}
function isOk(result) {
return !!result && (result.ok === true || result.ok === 1);
}
/* Validation result -> one readable line. Valid configs also report the
runtime facts the core derived from the file, which is what users
actually want to confirm before touching a live router. */
function validationText(result) {
if (isOk(result)) {
const runtime = result.runtime || {};
const facts = [];
if (Array.isArray(runtime.lan_interfaces) && runtime.lan_interfaces.length)
facts.push(_('LAN %s').format(runtime.lan_interfaces.join(', ')));
if (runtime.native_api_enabled)
facts.push(_('Doona %s').format(runtime.native_api_listen || '—'));
if (runtime.clash_api_listen)
facts.push(_('Clash API %s').format(runtime.clash_api_listen));
return facts.length
? _('Configuration is valid: %s').format(facts.join(' · '))
: _('Configuration is valid.');
}
const diagnostics = Array.isArray(result && result.diagnostics) ? result.diagnostics : [];
if (!diagnostics.length)
return honk.resultMessage(result, _('Configuration validation failed.'));
return diagnostics.map(function(item) {
const line = Number.isInteger(item.line) ? _('Line %d').format(item.line) : '';
const setting = item.setting ? String(item.setting) : String(item.code || '');
const message = String(item.message || '');
return [ line, setting, message ].filter(Boolean).join(' · ');
}).join(' | ');
}
/* 本机事实:LuCI 的 Doona 端口、活跃 L3 候选(含所属 netifd 网络)。取一次复用。 */
let runtimeFacts = null;
function runtimeContext() {
if (!runtimeFacts)
runtimeFacts = Promise.all([
honk.status().catch(function() { return {}; }),
honk.lanCandidates().catch(function() { return {}; })
]).then(function(results) {
const status = results[0] || {};
const candidates = ((results[1] && results[1].candidates) || []).filter(function(item) {
return item && item.l3_device;
});
return {
listenPort: String(status.listen_port || 9527),
lanNetwork: status.lan_network || 'lan',
candidates: candidates,
devices: candidates.map(function(item) { return item.l3_device; })
};
});
return runtimeFacts;
}
/* 只在 native_api 块里改 ui,而且值必须**正好**是 embedded(embedded-custom 不算)。 */
function fixNativeUi(text) {
const lines = text.split('\n');
const start = lines.findIndex(function(line) { return /^[ \t]*native_api[ \t]*\{/.test(line); });
if (start < 0)
return null;
const baseIndent = lines[start].match(/^[ \t]*/)[0].length;
for (let i = start + 1; i < lines.length; i++) {
const line = lines[i];
if (!line.trim())
continue;
const indent = line.match(/^[ \t]*/)[0].length;
if (indent <= baseIndent && /^[ \t]*\}/.test(line))
break;
const match = /^([ \t]*ui[ \t]*:[ \t]*)(['"]?)embedded\2[ \t]*(#.*)?$/.exec(line);
if (match) {
lines[i] = match[1] + "'/usr/share/doona'" + (match[3] ? ' ' + match[3] : '');
return { text: lines.join('\n') };
}
}
return null;
}
function locateLanInterface(text) {
const lines = text.split('\n');
for (let i = 0; i < lines.length; i++) {
const match = /^([ \t]*)lan_interface[ \t]*:[ \t]*([^\n#]+?)[ \t]*(#.*)?$/.exec(lines[i]);
if (match)
return { index: i, indent: match[1], value: match[2], comment: match[3] };
}
return null;
}
/* 优先 netifd 里配置的 LAN 网络;不做「取第一个活跃设备」,那可能是 WAN / VPN。 */
function preferredLanDevice(ctx) {
const order = [ ctx.lanNetwork, 'lan' ];
for (let i = 0; i < order.length; i++) {
const hit = ctx.candidates.find(function(item) { return item.network === order[i]; });
if (hit)
return { device: hit.l3_device, source: hit.network };
}
return null;
}
/* 多候选且无法确定时让用户挑;取消返回 null(表示不改这一项)。
ui.showModal() 返回的是 DOM 元素、不是 Promise,所以只能靠按钮回调
resolve;Esc 会被 LuCI 转成点击 .right 里第一个按钮(即取消)。 */
function chooseLanDevice(ctx) {
return new Promise(function(resolve) {
const select = E('select', { 'class': 'cbi-input-select' }, ctx.candidates.map(function(item) {
const label = [ item.network, item.l3_device, item.ipv4 ].filter(Boolean).join(' · ');
return E('option', { 'value': item.l3_device }, label);
}));
ui.showModal(_('Choose the LAN interface'), [
E('div', {}, [
E('p', {}, _('None of the active interfaces belongs to the configured LAN network. Pick the one Honk should see as LAN:')),
select
]),
E('div', { 'class': 'right' }, [
E('button', { 'class': 'cbi-button', 'click': function(ev) {
ev.preventDefault();
ui.hideModal();
resolve(null);
} }, [ _('Cancel') ]),
' ',
E('button', { 'class': 'cbi-button cbi-button-positive', 'click': function(ev) {
ev.preventDefault();
ui.hideModal();
resolve(select.value || null);
} }, [ _('Use this interface') ])
])
]);
});
}
/* 本机适配计划:算出「要改哪几处」和「改完的文本」,但**不落盘**。
allowPrompt = true 时才会弹选择框(校验时不要弹)。 */
function planAdaptations(text, allowPrompt) {
return runtimeContext().then(function(ctx) {
const notes = [];
const warnings = [];
let next = text;
const uiFix = fixNativeUi(next);
if (uiFix) {
next = uiFix.text;
notes.push("native_api.ui: embedded → '/usr/share/doona'");
}
const lan = locateLanInterface(next);
if (!lan)
return { notes: notes, warnings: warnings, text: next };
const named = lan.value.split(',').map(function(item) {
return item.trim().replace(/^['"]|['"]$/g, '');
}).filter(Boolean);
if (!named.length || !ctx.devices.length || named.some(function(name) { return ctx.devices.indexOf(name) >= 0; }))
return { notes: notes, warnings: warnings, text: next };
const preferred = preferredLanDevice(ctx);
let pick;
if (preferred)
pick = Promise.resolve(preferred.device);
else if (ctx.candidates.length === 1)
pick = Promise.resolve(ctx.candidates[0].l3_device);
else if (allowPrompt)
pick = chooseLanDevice(ctx);
else
pick = Promise.resolve(null);
return pick.then(function(device) {
if (!device) {
/* 用户在选择框里按了取消:直接中止这次保存,不要再问一遍。 */
if (allowPrompt && ctx.candidates.length > 1)
return { notes: notes, warnings: warnings, text: next, cancelled: true };
warnings.push(_('lan_interface: cannot tell which interface is the LAN here (candidates: %s); set it yourself').format(ctx.devices.join(', ') || '—'));
return { notes: notes, warnings: warnings, text: next };
}
const lines = next.split('\n');
lines[lan.index] = lan.indent + "lan_interface: '" + device + "'" + (lan.comment ? ' ' + lan.comment : '');
notes.push('lan_interface: ' + named.join(',') + ' → ' + device);
return { notes: notes, warnings: warnings, text: lines.join('\n') };
});
});
}
/* 保存前把适配写进编辑器。返回 false 表示用户拒绝或内容已变,调用方应中止。 */
function adaptEditor() {
const snapshot = editor.value;
return planAdaptations(snapshot, true).then(function(plan) {
if (plan.cancelled) {
editorStatus.textContent = _('Save cancelled.');
return false;
}
if (!plan.notes.length && !plan.warnings.length)
return true;
const blocks = [];
if (plan.notes.length)
blocks.push(_('These lines will be changed for this router:') + '\n' + plan.notes.join('\n'));
if (plan.warnings.length)
blocks.push(_('Needs your attention:') + '\n' + plan.warnings.join('\n'));
blocks.push(_('Apply them in the editor now?'));
if (!confirm(blocks.join('\n\n')))
return false;
/* 等设备查询 / 用户选择期间编辑器可能被改过,别用旧结果覆盖。 */
if (editor.value !== snapshot) {
editorStatus.textContent = _('The editor changed while checking. Nothing was applied; check again.');
return false;
}
if (plan.notes.length) {
editor.value = plan.text;
editor.dispatchEvent(new Event('input'));
editorStatus.textContent = _('Adapted in the editor: %s').format(plan.notes.join('; '));
}
return true;
});
}
function withNotes(text) {
return planAdaptations(editor.value, false).then(function(plan) {
const parts = [];
if (plan.notes.length)
parts.push(_('Saving will adapt for this router: %s').format(plan.notes.join(', ')));
if (plan.warnings.length)
parts.push(plan.warnings.join(' '));
return parts.length ? text + ' ' + parts.join(' ') : text;
});
}
function reportValidation(result, target) {
return withNotes(validationText(result)).then(function(text) {
target.textContent = text;
});
}
function pollStatus() {
return honk.status().catch(function() { return null; });
}
/* Validate -> confirm -> write -> start, shared by the file import and the
editor. `target` is the status line the caller wants the outcome in. */
function applyContent(content, target) {
target.textContent = _('Checking the configuration…');
return honk.configValidate(content).then(function(result) {
if (!isOk(result)) {
target.textContent = validationText(result);
return;
}
return Promise.resolve().then(function() {
const question = _('Write this configuration to /etc/honk/config.dae and start Honk? The previous file is restored if startup fails.');
if (!confirm(question))
return;
return commitContent(content, target);
});
}).catch(function(error) {
target.textContent = honk.errorMessage(error, _('Configuration validation failed.'));
});
}
function commitContent(content, target) {
target.textContent = _('Writing the configuration…');
return withLongTimeout(function() { return honk.configSave(content); }).then(function(result) {
if (!isOk(result)) {
target.textContent = validationText(result);
return;
}
if (content === editor.value)
editorBaseline = editor.value;
target.textContent = _('Starting Honk…');
return withLongTimeout(function() { return honk.configApply().then(honk.ensureOk); }).then(function(applied) {
target.textContent = honk.resultMessage(applied, _('Configuration applied.'));
return pollStatus().then(function(status) {
if (status && status.running)
target.textContent = _('Configuration applied and Honk is running.');
else
target.textContent = _('Configuration was written, but Honk is not running: %s').format(
((status && status.errors) || []).map(honk.statusIssue).join('; ') || _('unknown reason'));
});
}).catch(function(error) {
target.textContent = honk.errorMessage(error, _('Could not apply the configuration.'));
});
}).catch(function(error) {
target.textContent = honk.errorMessage(error, _('Could not save the configuration.'));
}).then(function() {
/* A failed apply rolls the file back, so reload what is really on
disk — but keep the outcome message the user needs to read. */
const outcome = target.textContent;
return refreshMode().then(function() {
return loadCurrent(outcome === editorStatus.textContent ? outcome : '', true).catch(function() {});
});
});
}
function refreshMode() {
return honk.status().then(function(status) {
const manual = !!status && status.config_mode === 'manual';
managedBtn.hidden = !manual;
const tabState = page._honkTabs;
if (tabState) {
tabState.tabs.forEach(function(tab) {
const disabled = manual && tab.managedOnly;
tab.button.disabled = disabled;
tab.panel.hidden = tab.id !== tabState.getActive();
});
if (manual && ['sub', 'nodes'].indexOf(tabState.getActive()) >= 0)
tabState.select('edit');
}
subModeHint.hidden = !manual;
pasteModeHint.hidden = !manual;
subBtn.disabled = manual;
pasteBtn.disabled = manual;
nameInput.disabled = manual;
urlInput.disabled = manual;
pasteInput.disabled = manual;
modeText.textContent = manual
? _('Manual mode: the file below owns nodes, groups, routing and DNS.')
: _('Managed mode: the subscription and paste-nodes tools are active. Saving here switches Honk to manual mode.');
return status;
}).catch(function() {
modeText.textContent = '';
});
}
/* force = false 用于页面初次载入:如果用户已经抢先开始输入,不要用磁盘内容
把他的编辑冲掉(异步回填比手速快)。apply 之后要的是磁盘真身,用 force。 */
function loadCurrent(message, force) {
return honk.configRead().then(honk.ensureOk).then(function(result) {
if (!force && editor.value !== editorBaseline && editor.value !== '')
return result;
setEditorContent(result.content || '', message || _('Current configuration loaded from /etc/honk/config.dae.'));
return result;
});
}
importBtn.addEventListener('click', function(ev) {
ev.preventDefault();
const file = fileInput.files && fileInput.files[0];
if (!file) { importStatus.textContent = _('Choose a .dae file first.'); return; }
if (file.size > DAE_LIMIT) { importStatus.textContent = _('The dae file exceeds the 2 MiB limit.'); return; }
importBtn.disabled = true;
importStatus.textContent = _('Reading %s…').format(file.name);
file.text().then(function(content) {
if (!String(content || '').trim())
throw new Error(_('The file is empty.'));
fileInput.value = '';
setEditorContent(content, _('Loaded %s into the editor.').format(file.name));
const snapshot = editor.value;
return planAdaptations(content, true).then(function(plan) {
if (plan.cancelled) {
importStatus.textContent = _('Import cancelled.');
return null;
}
const blocks = [];
if (plan.notes.length)
blocks.push(_('These lines will be changed for this router:') + '\n' + plan.notes.join('\n'));
if (plan.warnings.length)
blocks.push(_('Needs your attention:') + '\n' + plan.warnings.join('\n'));
if (blocks.length) {
blocks.push(_('Apply them in the editor now?'));
if (!confirm(blocks.join('\n\n')))
return null;
}
if (editor.value !== snapshot) {
importStatus.textContent = _('The editor changed while checking. Nothing was applied; import the file again.');
return null;
}
if (plan.notes.length)
setEditorContent(plan.text, _('Adapted in the editor: %s').format(plan.notes.join('; ')));
return applyContent(editor.value, importStatus).then(function() {
if (importStatus.textContent === _('Configuration applied and Honk is running.'))
importStatus.textContent = _('%s imported and Honk is running.').format(file.name);
});
});
}).catch(function(error) {
importStatus.textContent = honk.errorMessage(error, _('Could not read the configuration file.'));
}).finally(function() { importBtn.disabled = false; });
});
reloadBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (!confirmEditorReplace()) return;
reloadBtn.disabled = true;
loadCurrent(null, true).catch(function(error) {
editorStatus.textContent = honk.errorMessage(error, _('Could not read the saved configuration.'));
}).finally(function() { reloadBtn.disabled = false; });
});
validateBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (!editor.value.trim()) { editorStatus.textContent = _('The editor is empty.'); return; }
validateBtn.disabled = true;
editorStatus.textContent = _('Checking the configuration…');
honk.configValidate(editor.value).then(function(result) {
return reportValidation(result, editorStatus);
}).catch(function(error) {
editorStatus.textContent = honk.errorMessage(error, _('Configuration validation failed.'));
}).finally(function() { validateBtn.disabled = false; });
});
saveBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (!editor.value.trim()) { editorStatus.textContent = _('The editor is empty.'); return; }
saveBtn.disabled = true;
adaptEditor().then(function(proceed) {
if (!proceed) { editorStatus.textContent = _('Save cancelled.'); return null; }
return applyContent(editor.value, editorStatus);
}).finally(function() { saveBtn.disabled = false; });
});
managedBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (!confirm(_('Restore the managed configuration and return to the subscription tools? Honk restarts if it is running.'))) return;
managedBtn.disabled = true;
editorStatus.textContent = _('Restoring the managed configuration…');
withLongTimeout(function() { return honk.configUseManaged().then(honk.ensureOk); }).then(function(result) {
editorStatus.textContent = honk.resultMessage(result, _('Managed mode restored.'));
return refreshMode().then(function() { return loadCurrent(null, true); });
}).catch(function(error) {
editorStatus.textContent = honk.errorMessage(error, _('Could not restore managed mode.'));
}).finally(function() { managedBtn.disabled = false; });
});
const editorSection = E('section', { 'class': 'honk-card' }, [
E('p', { 'class': 'honk-note' }, _('Edit or paste a complete dae file. Check configuration only parses the text; Save and apply writes /etc/honk/config.dae, restarts Honk, and rolls back if startup fails.')),
E('div', { 'class': 'honk-subscription-add' }, [ saveBtn, reloadBtn, validateBtn ]),
editor,
editorStatus
]);
loadCurrent().catch(function(error) {
editorStatus.textContent = honk.errorMessage(error, _('Could not read the saved configuration.'));
});
refreshMode();
/* 一排标签页,只显示当前板块:四张卡片竖着堆太占屏,也不好一眼看出"有几种做法"。 */
const tabs = [
{ id: 'sub', label: _('Add subscription'), panel: subSection, managedOnly: true },
{ id: 'nodes', label: _('Add nodes'), panel: pasteSection, managedOnly: true },
{ id: 'import', label: _('Import config'), panel: importSection, managedOnly: false },
{ id: 'edit', label: _('Edit config'), panel: editorSection, managedOnly: false }
];
let activeTab = 'sub';
const tabBar = E('div', { 'class': 'honk-seg honk-tabs', 'role': 'tablist' });
function selectTab(id) {
activeTab = id;
tabs.forEach(function(tab) {
const active = tab.id === id;
tab.panel.hidden = !active;
tab.button.setAttribute('aria-selected', active ? 'true' : 'false');
tab.button.classList.toggle('is-active', active);
});
}
tabs.forEach(function(tab) {
tab.button = E('button', {
'class': 'honk-seg-btn', 'type': 'button', 'role': 'tab',
'aria-selected': 'false'
}, [ tab.label ]);
tab.button.addEventListener('click', function(ev) {
ev.preventDefault();
if (tab.button.disabled)
return;
selectTab(tab.id);
});
tab.panel.hidden = true;
tabBar.appendChild(tab.button);
});
page.appendChild(E('div', { 'class': 'honk-tabs-wrap' }, [ tabBar ]));
tabs.forEach(function(tab) { page.appendChild(tab.panel); });
selectTab(activeTab);
/* 曝光给 refreshMode:手动模式下订阅/节点两个标签不可用,自动切到编辑配置。 */
page._honkTabs = { tabs: tabs, select: selectTab, getActive: function() { return activeTab; } };
return page;
}
});
@@ -0,0 +1,501 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require baseclass';
function utf8Base64(value) {
const bytes = new TextEncoder().encode(String(value));
let binary = '';
for (let i = 0; i < bytes.length; i++)
binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
function utf8FromBase64(value) {
const binary = atob(value);
const bytes = Uint8Array.from(binary, function(ch) { return ch.charCodeAt(0); });
return new TextDecoder('utf-8').decode(bytes);
}
function base64Url(value) {
return utf8Base64(value).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
function endpoint(server, port) {
const host = String(server).indexOf(':') >= 0 && String(server)[0] !== '['
? '[' + server + ']'
: server;
return host + ':' + port;
}
function requireFields(node, fields) {
for (let i = 0; i < fields.length; i++) {
const key = fields[i];
if (node[key] === undefined || node[key] === null || node[key] === '')
throw new Error('Missing required field: ' + key);
}
}
function setIf(params, key, value) {
if (value !== undefined && value !== null && value !== '')
params.set(key, String(value));
}
function arrayValue(value) {
return Array.isArray(value) ? value.join(',') : value;
}
function addTransport(params, node) {
const network = node.network || 'tcp';
params.set('type', network);
if (network === 'ws') {
const opts = node['ws-opts'] || {};
setIf(params, 'path', opts.path);
setIf(params, 'host', opts.headers && (opts.headers.Host || opts.headers.host));
} else if (network === 'grpc') {
const opts = node['grpc-opts'] || {};
setIf(params, 'serviceName', opts['grpc-service-name'] || opts.serviceName);
} else if (network === 'http' || network === 'h2') {
const opts = node['h2-opts'] || node['http-opts'] || {};
setIf(params, 'path', Array.isArray(opts.path) ? opts.path[0] : opts.path);
setIf(params, 'host', arrayValue(opts.host));
}
}
function convertSs(node) {
requireFields(node, [ 'server', 'port', 'cipher', 'password' ]);
const auth = base64Url(node.cipher + ':' + node.password);
let query = '';
if (node.plugin) {
const fields = [ String(node.plugin) ];
const opts = node['plugin-opts'] || {};
Object.keys(opts).forEach(function(key) {
const value = opts[key];
if (value === true)
fields.push(key);
else if (value !== false && value !== undefined && value !== null && value !== '')
fields.push(key + '=' + value);
});
const params = new URLSearchParams();
params.set('plugin', fields.join(';'));
query = '?' + params.toString();
}
return 'ss://' + auth + '@' + endpoint(node.server, node.port) + query + '#' + encodeURIComponent(node.name || 'Node');
}
function convertSsr(node) {
requireFields(node, [ 'server', 'port', 'cipher', 'password', 'protocol', 'obfs' ]);
const query = [];
query.push('remarks=' + base64Url(node.name || 'Node'));
if (node['protocol-param'])
query.push('protoparam=' + base64Url(node['protocol-param']));
if (node['obfs-param'])
query.push('obfsparam=' + base64Url(node['obfs-param']));
const payload = [
node.server,
node.port,
node.protocol,
node.cipher,
node.obfs,
base64Url(node.password)
].join(':') + '/?' + query.join('&');
return 'ssr://' + base64Url(payload);
}
function convertVmess(node) {
requireFields(node, [ 'server', 'port', 'uuid' ]);
const network = node.network || 'tcp';
const ws = node['ws-opts'] || {};
const grpc = node['grpc-opts'] || {};
const h2 = node['h2-opts'] || {};
const payload = {
v: '2',
ps: String(node.name || 'Node'),
add: String(node.server),
port: String(node.port),
id: String(node.uuid),
aid: Number(node.alterId || node.alter_id || 0),
scy: String(node.cipher || 'auto'),
net: network,
type: 'none',
host: String((ws.headers && (ws.headers.Host || ws.headers.host)) || arrayValue(h2.host) || ''),
path: String(ws.path || grpc['grpc-service-name'] || (Array.isArray(h2.path) ? h2.path[0] : h2.path) || ''),
tls: node.tls ? 'tls' : 'none',
sni: String(node.servername || node.sni || ''),
alpn: String(arrayValue(node.alpn) || ''),
fp: String(node.fingerprint || node['client-fingerprint'] || '')
};
return 'vmess://' + utf8Base64(JSON.stringify(payload));
}
function convertVless(node) {
requireFields(node, [ 'server', 'port', 'uuid' ]);
const params = new URLSearchParams();
addTransport(params, node);
params.set('encryption', String(node.encryption || 'none'));
setIf(params, 'flow', node.flow);
const reality = node['reality-opts'];
if (reality || node.tls) {
params.set('security', reality ? 'reality' : 'tls');
setIf(params, 'sni', node.servername || node.sni);
setIf(params, 'fp', node.fingerprint || node['client-fingerprint']);
setIf(params, 'alpn', arrayValue(node.alpn));
if (node['skip-cert-verify'])
params.set('allowInsecure', '1');
if (reality) {
setIf(params, 'pbk', reality['public-key']);
setIf(params, 'sid', reality['short-id']);
setIf(params, 'spx', reality['spider-x']);
}
}
return 'vless://' + encodeURIComponent(node.uuid) + '@' + endpoint(node.server, node.port) +
'?' + params.toString() + '#' + encodeURIComponent(node.name || 'Node');
}
function convertTrojan(node) {
requireFields(node, [ 'server', 'port', 'password' ]);
const params = new URLSearchParams();
addTransport(params, node);
setIf(params, 'sni', node.sni || node.servername);
setIf(params, 'fp', node.fingerprint || node['client-fingerprint']);
setIf(params, 'alpn', arrayValue(node.alpn));
if (node['skip-cert-verify'])
params.set('allowInsecure', '1');
return 'trojan://' + encodeURIComponent(node.password) + '@' + endpoint(node.server, node.port) +
'?' + params.toString() + '#' + encodeURIComponent(node.name || 'Node');
}
function convertTuic(node) {
requireFields(node, [ 'server', 'port', 'uuid', 'password' ]);
const params = new URLSearchParams();
setIf(params, 'sni', node.sni);
setIf(params, 'alpn', arrayValue(node.alpn));
setIf(params, 'congestion_control', node['congestion-controller'] || node['congestion-control']);
setIf(params, 'udp_relay_mode', node['udp-relay-mode']);
if (node['skip-cert-verify'])
params.set('allow_insecure', '1');
return 'tuic://' + encodeURIComponent(node.uuid) + ':' + encodeURIComponent(node.password) + '@' +
endpoint(node.server, node.port) + (params.toString() ? '?' + params.toString() : '') +
'#' + encodeURIComponent(node.name || 'Node');
}
function convertHysteria2(node) {
requireFields(node, [ 'server', 'port', 'password' ]);
const params = new URLSearchParams();
setIf(params, 'sni', node.sni);
setIf(params, 'alpn', arrayValue(node.alpn));
setIf(params, 'obfs', node.obfs);
setIf(params, 'obfs-password', node['obfs-password']);
if (node['skip-cert-verify'])
params.set('insecure', '1');
return 'hysteria2://' + encodeURIComponent(node.password) + '@' + endpoint(node.server, node.port) +
(params.toString() ? '?' + params.toString() : '') + '#' + encodeURIComponent(node.name || 'Node');
}
function convertAnytls(node) {
requireFields(node, [ 'server', 'port', 'password' ]);
const params = new URLSearchParams();
setIf(params, 'sni', node.sni || node.servername);
setIf(params, 'alpn', arrayValue(node.alpn));
setIf(params, 'client-fingerprint', node['client-fingerprint'] || node.fingerprint);
if (node['skip-cert-verify'])
params.set('insecure', '1');
if (node.udp)
params.set('udp', '1');
return 'anytls://' + encodeURIComponent(node.password) + '@' + endpoint(node.server, node.port) +
(params.toString() ? '?' + params.toString() : '') + '#' + encodeURIComponent(node.name || 'Node');
}
function isMetadataProxy(node) {
const name = String(node && node.name || '').trim();
if (!name)
return false;
return /(?:官网|QQ|流量|续费|应急|重置|到期|过期|剩余|套餐)/i.test(name) ||
/^(?:traffic|bandwidth|expire|expiry|expiration|subscription(?:\s+info)?|official\s+website|website)\s*[::|]/i.test(name) ||
/^(?:(?:剩余|可用|已用|总)?流量|套餐到期|到期时间|到期日|过期时间|有效期|订阅信息)\s*[::|]/i.test(name) ||
/^(?:官方网站|官网地址|官方网址|网站地址)\s*[::|]/i.test(name) ||
/^(?:加入|联系|客服)?\s*QQ\s*(?:群|交流群|客服|联系)\s*[::|]/i.test(name);
}
function convertProxy(node) {
const type = String(node && node.type || '').toLowerCase();
const converters = {
ss: convertSs,
ssr: convertSsr,
vmess: convertVmess,
vless: convertVless,
trojan: convertTrojan,
tuic: convertTuic,
hysteria2: convertHysteria2,
hy2: convertHysteria2,
anytls: convertAnytls
};
try {
if (!node || typeof node !== 'object')
throw new Error('Node must be an object');
if (!converters[type])
throw new Error('Unsupported protocol: ' + (type || 'unknown'));
return {
ok: true,
name: String(node.name || 'Node'),
type: type,
link: converters[type](node),
error: ''
};
} catch (e) {
return {
ok: false,
name: String(node && node.name || 'Node'),
type: type || 'unknown',
link: '',
error: String(e && e.message || e)
};
}
}
function convertProxies(proxies) {
return Array.isArray(proxies) ? proxies.map(convertProxy) : [];
}
// --- Paste-import helpers -------------------------------------------------
const NODE_SCHEMES = /^(ss|ssr|vmess|vless|trojan|tuic|hysteria2?|hy2|anytls|socks5|socks4|juicity|snell):\/\//i;
function looksLikeNodeList(text) {
const lines = String(text || '').split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line && NODE_SCHEMES.test(line))
return true;
}
return false;
}
function looksLikeClashYaml(text) {
return /\bproxies\s*:/.test(String(text || ''));
}
function looksLikeSurge(text) {
return /^\s*\[Proxy\]\s*$/m.test(String(text || ''));
}
function tryBase64Decode(text) {
const compact = String(text || '').replace(/\s+/g, '');
if (compact.length < 4 || !/^[A-Za-z0-9+/_-]+={0,2}$/.test(compact))
return '';
let normalized = compact.replace(/-/g, '+').replace(/_/g, '/');
while (normalized.length % 4)
normalized += '=';
try {
return utf8FromBase64(normalized);
} catch (e) {
return '';
}
}
function parseUriList(text) {
const links = [];
let rejected = 0;
String(text || '').split('\n').forEach(function(raw) {
const line = raw.trim();
if (!line || line[0] === '#' || line[0] === ';' || line.indexOf('//') === 0)
return;
if (NODE_SCHEMES.test(line))
links.push(line);
else
rejected++;
});
return { links: links, rejected: rejected };
}
function splitSurgeFields(value) {
const fields = [];
let current = '';
let quote = '';
for (let i = 0; i < value.length; i++) {
const ch = value[i];
if (ch === '"' || ch === '\'') {
if (quote === ch) quote = '';
else if (quote === '') quote = ch;
current += ch;
} else if (ch === ',' && quote === '') {
fields.push(surgeScalar(current));
current = '';
} else {
current += ch;
}
}
fields.push(surgeScalar(current));
return fields;
}
function surgeScalar(value) {
let t = String(value || '').trim();
if (t.length >= 2 && (t[0] === '"' || t[0] === '\'') && t[0] === t[t.length - 1])
t = t.slice(1, -1);
try { return decodeURIComponent(t); } catch (e) { return t; }
}
function firstNonEmpty() {
for (let i = 0; i < arguments.length; i++)
if (arguments[i] !== undefined && arguments[i] !== null && arguments[i] !== '')
return arguments[i];
return '';
}
// Parse a Surge/Shadowrocket [Proxy] section. Only Shadowsocks lines are
// understood, matching the upstream parser; other protocols are rejected so an
// ordinary URL in the file is never mistaken for a node.
function parseSurgeProxies(text) {
const links = [];
let rejected = 0;
let inside = false;
String(text || '').split('\n').forEach(function(raw) {
const line = raw.trim();
if (!line || line[0] === '#' || line[0] === ';' || line.indexOf('//') === 0)
return;
if (line[0] === '[' && line[line.length - 1] === ']' && line.indexOf('=') < 0) {
inside = line.toLowerCase() === '[proxy]';
return;
}
if (!inside)
return;
const sep = line.indexOf('=');
if (sep < 0)
return;
const name = line.slice(0, sep).trim();
const fields = splitSurgeFields(line.slice(sep + 1));
if (!name || fields.length < 3 || fields[0].toLowerCase() !== 'ss' || !fields[1]) {
rejected++;
return;
}
const port = Number(fields[2]);
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
rejected++;
return;
}
const options = {};
for (let i = 3; i < fields.length; i++) {
const eq = fields[i].indexOf('=');
if (eq < 0)
continue;
options[fields[i].slice(0, eq).trim().toLowerCase()] = fields[i].slice(eq + 1).trim();
}
const node = {
name: name,
type: 'ss',
server: fields[1],
port: port,
cipher: firstNonEmpty(options['encrypt-method'], options.method, options.cipher),
password: options.password,
plugin: options.plugin || options['obfs'],
'plugin-opts': {}
};
if (node.plugin) {
node['plugin-opts'].mode = options['obfs'] || 'http';
if (options['obfs-host'])
node['plugin-opts'].host = options['obfs-host'];
}
if (!node.cipher || !node.password) {
rejected++;
return;
}
const result = convertProxy(node);
if (result.ok) links.push(result.link);
else rejected++;
});
if (!links.length && !rejected)
rejected = 1;
return { links: links, rejected: rejected };
}
// Mihomo/mihomo-format JSON keeps nodes in `outbounds` and names them with
// `tag` instead of `name`, with nested `tls`/`transport` objects. Normalize to
// the Clash shape so the existing convertProxy converters work unchanged.
function looksLikeMihomoJson(text) {
const t = String(text || '').trim();
if (!t || t[0] !== '{')
return false;
try {
const doc = JSON.parse(t);
return !!(doc && Array.isArray(doc.outbounds));
}
catch (e) {
return false;
}
}
function normalizeMihomoNode(node) {
const n = Object.assign({}, node);
if (node.tag !== undefined && node.name === undefined) n.name = node.tag;
if (node.server_port !== undefined && node.port === undefined) n.port = node.server_port;
const tls = node.tls;
if (tls && typeof tls === 'object') {
if (tls.server_name !== undefined && n.servername === undefined) n.servername = tls.server_name;
if (tls.insecure && n['skip-cert-verify'] === undefined) n['skip-cert-verify'] = true;
if (Array.isArray(tls.alpn) && n.alpn === undefined) n.alpn = tls.alpn;
if (tls.utls && tls.utls.fingerprint !== undefined && n['client-fingerprint'] === undefined)
n['client-fingerprint'] = tls.utls.fingerprint;
if (tls.reality && typeof tls.reality === 'object') {
if (!n['reality-opts']) n['reality-opts'] = {};
if (tls.reality.public_key !== undefined) n['reality-opts']['public-key'] = tls.reality.public_key;
if (tls.reality.short_id !== undefined) n['reality-opts']['short-id'] = tls.reality.short_id;
}
}
const transport = node.transport;
if (transport && typeof transport === 'object') {
if (transport.type && n.network === undefined) n.network = transport.type;
if (transport.type === 'ws' && transport.path && !n['ws-opts']) {
n['ws-opts'] = { path: transport.path };
if (transport.headers && transport.headers.Host)
n['ws-opts'].headers = { Host: transport.headers.Host };
}
}
return n;
}
function parseMihomoJson(text) {
let doc;
try {
doc = JSON.parse(String(text || ''));
}
catch (e) {
return { links: [], rejected: 1 };
}
const outbounds = (doc && doc.outbounds) || [];
const links = [];
let rejected = 0;
outbounds.forEach(function(node) {
if (isMetadataProxy(node)) {
rejected++;
return;
}
const result = convertProxy(normalizeMihomoNode(node));
if (result.ok) links.push(result.link);
else rejected++;
});
return { links: links, rejected: rejected };
}
return baseclass.extend({
convertProxy: convertProxy,
convertProxies: convertProxies,
isMetadataProxy: isMetadataProxy,
looksLikeNodeList: looksLikeNodeList,
looksLikeClashYaml: looksLikeClashYaml,
looksLikeSurge: looksLikeSurge,
looksLikeMihomoJson: looksLikeMihomoJson,
tryBase64Decode: tryBase64Decode,
parseUriList: parseUriList,
parseSurgeProxies: parseSurgeProxies,
parseMihomoJson: parseMihomoJson,
normalizeMihomoNode: normalizeMihomoNode
});
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require view';
'require view.honk.rpc as honk';
return view.extend({
handleSave: null,
handleSaveApply: null,
handleReset: null,
load: function() {
return honk.status().then(function(status) {
return { status: status };
}).catch(function() {
return { error: true };
});
},
render: function(data) {
honk.installStyles();
const page = E('div', { 'class': 'cbi-map honk-page' });
function renderInitialized(status) {
const section = E('div', { 'class': 'honk-card' });
const webUi = honk.localWebUi(status);
section.appendChild(E('h3', { 'class': 'honk-card-title' }, _('Doona')));
if (webUi) {
section.appendChild(E('a', {
'class': 'cbi-button cbi-button-action',
'href': webUi,
'target': '_blank',
'rel': 'noopener noreferrer'
}, _('Open Doona')));
}
else if (!status.running) {
section.appendChild(E('p', {}, _('Honk is stopped. Start it from the overview to open Doona.')));
section.appendChild(E('a', {
'class': 'cbi-button cbi-button-action',
'href': L.url('admin/services/honk/overview')
}, _('Go to overview')));
}
else {
section.appendChild(E('p', {}, _('Doona is not ready yet. Check the overview for status.')));
section.appendChild(E('a', {
'class': 'cbi-button cbi-button-action',
'href': L.url('admin/services/honk/overview')
}, _('Go to overview')));
}
page.appendChild(section);
}
if (data.error) {
page.appendChild(E('div', { 'class': 'alert-message' }, _('Could not read Honk status.')));
return page;
}
if (data.status.initialized === true) {
renderInitialized(data.status);
return page;
}
const form = E('form', { 'class': 'honk-card' });
const username = E('input', {
'type': 'text',
'name': 'username',
'autocomplete': 'username',
'class': 'cbi-input-text'
});
const password = E('input', {
'type': 'password',
'name': 'password',
'autocomplete': 'new-password',
'class': 'cbi-input-password'
});
const message = E('p', { 'role': 'status' }, '');
const submit = E('button', { 'class': 'cbi-button cbi-button-positive', 'type': 'submit' }, _('Initialize Honk'));
form.appendChild(E('h3', { 'class': 'honk-card-title' }, _('Initial setup')));
form.appendChild(E('p', {}, _('Create the Honk administrator account. The password is sent only for this request and is not saved by this page.')));
form.appendChild(E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': 'honk-init-username' }, _('Username')),
E('div', { 'class': 'cbi-value-field' }, username)
]));
username.id = 'honk-init-username';
form.appendChild(E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': 'honk-init-password' }, _('Password')),
E('div', { 'class': 'cbi-value-field' }, password)
]));
password.id = 'honk-init-password';
form.appendChild(E('div', { 'class': 'cbi-page-actions' }, submit));
form.appendChild(message);
form.addEventListener('submit', function(ev) {
ev.preventDefault();
const user = username.value.trim();
const secret = password.value;
if (!user || !secret) {
message.textContent = _('Enter both a username and password.');
return;
}
submit.disabled = true;
message.textContent = _('Initializing…');
honk.initialize(user, secret).then(honk.ensureOk).then(function(result) {
username.value = '';
password.value = '';
message.textContent = honk.resultMessage(result, _('Honk initialized.'));
form.remove();
page.appendChild(E('p', { 'role': 'status' }, _('Refreshing status…')));
return honk.status().then(function(status) {
while (page.firstChild)
page.removeChild(page.firstChild);
renderInitialized(status);
}).catch(function() {
while (page.firstChild)
page.removeChild(page.firstChild);
page.appendChild(E('div', { 'class': 'alert-message' }, _('Could not read Honk status.')));
});
}).catch(function() {
password.value = '';
message.textContent = _('Initialization failed. Check the system status.');
}).finally(function() {
submit.disabled = false;
});
});
page.appendChild(form);
return page;
}
});
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,192 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require view';
'require view.honk.rpc as honk';
function detectLogLevel(line) {
// honk-core tracing output (e.g. " ERROR honk_core::...")
if (/\s(?:ERROR|FATAL|PANIC)\s/.test(line)) return 'error';
if (/\sWARN(?:ING)?\s/.test(line)) return 'warn';
if (/\sDEBUG\s/.test(line)) return 'debug';
if (/\sTRACE\s/.test(line)) return 'trace';
if (/\sINFO\s/.test(line)) return 'info';
// syslog facility.level (e.g. "daemon.err")
if (/daemon\.(?:err|crit|alert|emerg)\b/.test(line)) return 'error';
if (/daemon\.(?:warning|warn|notice)\b/.test(line)) return 'warn';
if (/daemon\.debug\b/.test(line)) return 'debug';
if (/daemon\.info\b/.test(line)) return 'info';
return '';
}
function formatLogTs(raw) {
// logread timestamp: "Wed Oct 7 18:58:09 2026" -> "2026-10-07 18:58:09"
const m = String(raw || '').match(/^[A-Z][a-z]{2}\s+([A-Z][a-z]{2})\s+(\d{1,2})\s+(\d{2}:\d{2}:\d{2})\s+(\d{4})$/);
if (!m) return raw;
const months = { Jan: 1, Feb: 2, Mar: 3, Apr: 4, May: 5, Jun: 6, Jul: 7, Aug: 8, Sep: 9, Oct: 10, Nov: 11, Dec: 12 };
const month = months[m[1]];
if (!month) return raw;
return m[4] + '-' + String(month).padStart(2, '0') + '-' + String(Number(m[2])).padStart(2, '0') + ' ' + m[3];
}
function lvlShort(level) {
const u = (level || '').toUpperCase();
if (u === 'WARNING') return 'WARN';
return u || '-';
}
function lvlClass(level) {
const u = (level || '').toUpperCase();
if (u === 'TRACE') return 'honk-log-lvl-trace';
if (u === 'DEBUG') return 'honk-log-lvl-debug';
if (u === 'INFO') return 'honk-log-lvl-info';
if (u === 'WARN' || u === 'WARNING') return 'honk-log-lvl-warn';
return 'honk-log-lvl-error';
}
function parseLogLine(line) {
// "Wed Oct 7 18:58:09 2026 daemon.info honk-core[31377]: INFO honk_core::..."
const m = line.match(/^([A-Z][a-z]{2}\s+[A-Z][a-z]{2}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4})\s+\S+\s+[^:]+:\s*(.*)$/);
if (!m) return { ts: '', lvl: '', msg: line };
let body = m[2] || '';
const lvlMatch = body.match(/^\s*(TRACE|DEBUG|INFO|WARN(?:ING)?|ERROR|FATAL|PANIC)\s+(.*)$/i);
let lvl = '';
if (lvlMatch) {
lvl = lvlMatch[1].toUpperCase();
body = lvlMatch[2];
}
return { ts: formatLogTs(m[1]), lvl: lvl, msg: body };
}
function buildLogLine(line) {
const level = detectLogLevel(line);
const parsed = parseLogLine(line);
const parts = [];
if (parsed.ts)
parts.push(E('span', { 'class': 'honk-log-ts' }, parsed.ts));
if (parsed.lvl)
parts.push(E('span', { 'class': 'honk-log-lvl ' + lvlClass(parsed.lvl) }, lvlShort(parsed.lvl)));
parts.push(E('span', { 'class': 'honk-log-msg' }, parsed.msg));
return E('span', { 'class': 'honk-log-line' + (level ? ' honk-log-' + level : '') }, parts);
}
return view.extend({
handleSave: null,
handleSaveApply: null,
handleReset: null,
_timer: null,
handleUnload: function() {
if (this._timer != null)
clearInterval(this._timer);
this._timer = null;
},
render: function() {
honk.installStyles();
const source = E('select', { 'class': 'cbi-input-select' }, [
E('option', { 'value': 'core' }, _('Core')),
E('option', { 'value': 'service' }, _('Service')),
E('option', { 'value': 'maintenance' }, _('Maintenance'))
]);
const level = E('select', { 'class': 'cbi-input-select' }, [
E('option', { 'value': 'all' }, _('All levels')),
E('option', { 'value': 'debug' }, _('Debug and above')),
E('option', { 'value': 'info' }, _('Info and above')),
E('option', { 'value': 'warn' }, _('Warnings and errors')),
E('option', { 'value': 'error' }, _('Errors only'))
]);
const output = E('pre', {
'class': 'honk-log-output',
'aria-live': 'polite'
}, _('Loading logs…'));
const status = E('p', { 'class': 'honk-status-msg', 'role': 'status' }, '');
const pause = E('button', { 'class': 'cbi-button', 'type': 'button' }, _('Pause'));
const refresh = E('button', { 'class': 'cbi-button', 'type': 'button' }, _('Refresh now'));
const download = E('button', { 'class': 'cbi-button', 'type': 'button' }, _('Download redacted log'));
let paused = false;
let pendingRequests = 0;
let requestSequence = 0;
let latest = [];
download.disabled = true;
function loadLogs(force) {
if (paused && !force || pendingRequests && !force)
return Promise.resolve();
const requestId = ++requestSequence;
const requestedSource = source.value;
const requestedLevel = level.value;
pendingRequests++;
download.disabled = true;
return honk.logs(requestedSource, requestedLevel, 200).then(honk.ensureOk).then(function(result) {
if (requestId !== requestSequence)
return;
latest = Array.isArray(result.lines) ? result.lines.map(String) : [];
output.replaceChildren();
if (latest.length) {
latest.forEach(function(line) {
output.appendChild(buildLogLine(line));
});
}
else {
output.textContent = _('No matching Honk log entries.');
}
status.textContent = result.capped ? _('Output was capped; showing the latest %d entries.').format(latest.length) : _('Showing the latest %d matching entries.').format(latest.length);
}).catch(function(error) {
if (requestId === requestSequence)
status.textContent = honk.errorMessage(error, _('Could not read Honk logs.'));
}).finally(function() {
pendingRequests--;
download.disabled = pendingRequests > 0;
});
}
pause.addEventListener('click', function(ev) {
ev.preventDefault();
paused = !paused;
pause.textContent = paused ? _('Resume') : _('Pause');
status.textContent = paused ? _('Log polling is paused.') : _('Log polling resumed.');
if (!paused)
loadLogs(true);
});
refresh.addEventListener('click', function(ev) {
ev.preventDefault();
loadLogs(true);
});
source.addEventListener('change', function() { loadLogs(true); });
level.addEventListener('change', function() { loadLogs(true); });
download.addEventListener('click', function(ev) {
ev.preventDefault();
const blob = new Blob([ latest.join('\n') + (latest.length ? '\n' : '') ], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = E('a', { 'href': url, 'download': 'honk-redacted.log' });
document.body.appendChild(link);
link.click();
link.remove();
setTimeout(function() { URL.revokeObjectURL(url); }, 1000);
});
source.id = 'honk-log-source';
level.id = 'honk-log-level';
this._timer = setInterval(function() { loadLogs(false); }, 5000);
loadLogs(true);
return E('div', { 'class': 'cbi-map honk-page' }, [
E('div', { 'class': 'honk-header' }, [
E('h2', {}, _('Logs')),
E('p', { 'class': 'honk-header-sub' }, _('Shows up to 200 Honk-specific lines, capped at 32 KiB, and refreshes every 5 seconds while live. Sensitive URLs and credentials are redacted before display.'))
]),
E('section', { 'class': 'honk-card' }, [
E('h3', { 'class': 'honk-card-title' }, _('Live log')),
E('div', { 'class': 'honk-log-toolbar' }, [
E('label', { 'for': source.id }, [ _('Log source'), source ]),
E('label', { 'for': level.id }, [ _('Minimum level'), level ]),
E('div', { 'class': 'honk-actions' }, [ pause, refresh, download ])
]),
status,
output
])
]);
}
});
@@ -0,0 +1,555 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require fs';
'require poll';
'require uci';
'require ui';
'require view';
'require view.honk.rpc as honk';
const BACKUP_PATH = '/tmp/honk-backup.tar.gz';
const RESTORE_PATH = '/tmp/honk-maintenance/restore.tar.gz';
const PKG_INFO = '/usr/share/luci-app-honk/pkg-info.sh';
const UPDATE_PKG = '/usr/share/luci-app-honk/update-pkg.sh';
const REFRESH_INDEX = '/usr/share/luci-app-honk/refresh-index.sh';
const UPDATE_GEO = '/usr/share/luci-app-honk/update-geo.sh';
const GEO_CRON = '/usr/share/luci-app-honk/geo-cron.sh';
const PKGS = [ 'honk', 'luci-app-honk' ];
const PKG_NAMES = { honk: 'Honk', 'luci-app-honk': 'luci-app-honk' };
const DATA_PATHS = {
geoip: '/usr/share/v2ray/geoip.dat',
geosite: '/usr/share/v2ray/geosite.dat'
};
// Geo data source presets. `loyalsoldier` keeps both URLs empty so the actual
// default lives in one place — update-geo.sh — avoiding UI/script drift.
const GEO_PRESETS = {
loyalsoldier: { geoip: '', geosite: '' },
v2fly: {
geoip: 'https://github.com/v2fly/geoip/releases/latest/download/geoip.dat',
geosite: 'https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat'
}
};
function fmtBytes(n) {
if (!n && n !== 0) return '-';
if (n < 1024) return n + ' B';
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
return (n / 1024 / 1024).toFixed(1) + ' MB';
}
function fmtMtime(epoch) {
if (!epoch) return '';
const d = new Date(epoch * 1000);
return d.toISOString().slice(0, 10);
}
// Compare two version strings like `sort -V`. Returns <0 / 0 / >0.
function cmpVer(a, b) {
const ax = String(a).match(/(\d+|\D+)/g) || [];
const bx = String(b).match(/(\d+|\D+)/g) || [];
const n = Math.max(ax.length, bx.length);
for (let i = 0; i < n; i++) {
const as = ax[i], bs = bx[i];
if (as === undefined) return -1;
if (bs === undefined) return 1;
if (/^\d+$/.test(as) && /^\d+$/.test(bs)) {
const d = parseInt(as, 10) - parseInt(bs, 10);
if (d !== 0) return d < 0 ? -1 : 1;
} else if (as !== bs) {
return as < bs ? -1 : 1;
}
}
return 0;
}
return view.extend({
handleSave: null,
handleSaveApply: null,
handleReset: null,
load: function() {
return uci.load('honk').catch(function() {});
},
render: function() {
honk.installStyles();
const page = E('div', { 'class': 'cbi-map honk-page' });
const recoveryMessage = E('p', { 'class': 'alert-message', 'role': 'status' }, '');
recoveryMessage.hidden = true;
const writeControls = [];
let recoveryRequired = false;
function phaseMessage(job, fallback) {
const messages = {
preparing: _('Preparing maintenance…'),
stopping: _('Stopping Honk…'),
applying: _('Applying the staged data…'),
validating: _('Validating the new state…'),
starting: _('Starting Honk…'),
rolling_back: _('Restoring the previous state…'),
committing: _('Finishing the transaction…'),
recovery: _('Recovery is required before another write operation.')
};
return messages[job.phase] || fallback;
}
function blockWrites() {
recoveryRequired = true;
writeControls.forEach(function(button) { button.disabled = true; });
}
/* Shared log pane and row helper for both data and package updates. */
const updateLog = E('pre', { 'class': 'honk-up-log', 'hidden': true }, '');
function mkPkgRow(icon, iconCls, name, meta, btn) {
return E('div', { 'class': 'honk-pkg-row' }, [
E('span', { 'class': 'honk-pkg-icon ' + iconCls }, icon),
E('span', { 'class': 'honk-pkg-name' }, name),
E('span', { 'class': 'honk-pkg-meta', 'title': meta }, meta),
E('span', {}, ''),
btn || E('span', {}, '')
]);
}
function probeFile(path) {
return fs.stat(path).then(function(st) {
return { exists: true, size: st.size, mtime: st.mtime };
}).catch(function() {
return { exists: false, size: 0, mtime: 0 };
});
}
/* --- Geo data card --- */
const dataSection = E('section', { 'class': 'honk-card' });
const dataBody = E('div', { 'id': 'honk-geo' }, E('em', {}, _('Probing…')));
function updateGeo(kind, btn) {
const orig = btn.textContent;
btn.disabled = true;
btn.textContent = '…';
let tries = 0;
const pollLog = function() {
return fs.read_direct('/tmp/luci-app-honk.' + kind + '.log', 'text').then(function(c) {
if (c) {
updateLog.textContent = c;
updateLog.hidden = false;
}
if (/[✓✗]/.test(c)) { refreshData(); return; }
if (tries++ > 90) return;
return new Promise(function(r) { setTimeout(r, 2000); }).then(pollLog);
}).catch(function() {});
};
return fs.exec(UPDATE_GEO, [ kind ]).then(function(res) {
if (res.code === 0) return pollLog();
}).catch(function() {}).finally(function() {
btn.disabled = false;
btn.textContent = orig;
});
}
function refreshData() {
return Promise.all([ probeFile(DATA_PATHS.geoip), probeFile(DATA_PATHS.geosite) ]).then(function(r) {
while (dataBody.firstChild)
dataBody.removeChild(dataBody.firstChild);
[
{ k: 'geoip', name: 'GeoIP', r: r[0] },
{ k: 'geosite', name: 'GeoSite', r: r[1] }
].forEach(function(entry) {
const btn = E('button', { 'class': 'cbi-button cbi-button-action', 'type': 'button' }, _('Update'));
btn.addEventListener('click', function(ev) { ev.preventDefault(); updateGeo(entry.k, btn); });
const meta = entry.r.exists
? fmtBytes(entry.r.size) + (entry.r.mtime ? ' · ' + fmtMtime(entry.r.mtime) : '')
: _('missing — click Update to fetch');
dataBody.appendChild(mkPkgRow(
entry.r.exists ? '✓' : '✗',
entry.r.exists ? 'honk-pkg-ok' : 'honk-pkg-err',
entry.name,
meta,
btn
));
});
});
}
function geoField(label, input) {
return E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': input.id }, label),
E('div', { 'class': 'cbi-value-field' }, input)
]);
}
const geoSettings = (function() {
const gi0 = uci.get('honk', 'main', 'geoip_url') || '';
const gs0 = uci.get('honk', 'main', 'geosite_url') || '';
const auto0 = uci.get('honk', 'main', 'geo_auto') === '1';
const freq0 = uci.get('honk', 'main', 'geo_auto_freq') || 'daily';
const preset0 = (!gi0 && !gs0) ? 'loyalsoldier' :
(gi0 === GEO_PRESETS.v2fly.geoip && gs0 === GEO_PRESETS.v2fly.geosite) ? 'v2fly' : 'custom';
const presetSel = E('select', { 'class': 'cbi-input-select', 'id': 'honk-geo-preset' }, [
E('option', { 'value': 'loyalsoldier' }, 'Loyalsoldier'),
E('option', { 'value': 'v2fly' }, 'v2fly'),
E('option', { 'value': 'custom' }, _('Custom'))
]);
presetSel.value = preset0;
const giInput = E('input', { 'class': 'cbi-input-text', 'type': 'text', 'id': 'honk-geo-ip', 'placeholder': 'https://…/geoip.dat' });
const gsInput = E('input', { 'class': 'cbi-input-text', 'type': 'text', 'id': 'honk-geo-site', 'placeholder': 'https://…/geosite.dat' });
giInput.value = preset0 === 'custom' ? gi0 : '';
gsInput.value = preset0 === 'custom' ? gs0 : '';
const customRows = E('div', {}, [
geoField(_('GeoIP URL'), giInput),
geoField(_('GeoSite URL'), gsInput)
]);
const syncCustom = function() { customRows.style.display = presetSel.value === 'custom' ? '' : 'none'; };
presetSel.addEventListener('change', syncCustom);
syncCustom();
const autoSel = E('select', { 'class': 'cbi-input-select', 'id': 'honk-geo-auto' }, [
E('option', { 'value': 'off' }, _('Disabled')),
E('option', { 'value': 'daily' }, _('Daily')),
E('option', { 'value': 'weekly' }, _('Weekly'))
]);
autoSel.value = auto0 ? freq0 : 'off';
const saveBtn = E('button', { 'class': 'cbi-button cbi-button-action', 'type': 'button' }, _('Save'));
saveBtn.addEventListener('click', function() {
const p = presetSel.value;
let gi = '', gs = '';
const geoAuto = autoSel.value !== 'off';
const geoFreq = autoSel.value === 'weekly' ? 'weekly' : 'daily';
if (p === 'v2fly') { gi = GEO_PRESETS.v2fly.geoip; gs = GEO_PRESETS.v2fly.geosite; }
else if (p === 'custom') {
gi = giInput.value.trim(); gs = gsInput.value.trim();
for (const [label, url] of [['GeoIP URL', gi], ['GeoSite URL', gs]]) {
if (url) {
try {
const u = new URL(url);
if (u.protocol !== 'http:' && u.protocol !== 'https:') throw new Error('bad protocol');
} catch (e) {
document.getElementById('honk-geo-msg').textContent = _('Invalid %s: must be an http(s) URL.').format(label);
return;
}
}
}
}
uci.set('honk', 'main', 'geoip_url', gi);
uci.set('honk', 'main', 'geosite_url', gs);
uci.set('honk', 'main', 'geo_auto', geoAuto ? '1' : '0');
uci.set('honk', 'main', 'geo_auto_freq', geoFreq);
const orig = saveBtn.textContent;
saveBtn.disabled = true; saveBtn.textContent = '…';
uci.save().then(function() {
return uci.changes();
}).then(function(changes) {
if (changes && Object.keys(changes).length)
return uci.apply();
}).then(function() {
return fs.exec(GEO_CRON, [ geoAuto ? 'enable' : 'disable' ]);
}).then(function() {
updateLog.textContent = _('Geo data source saved.');
updateLog.hidden = false;
ui.changes.init();
}).catch(function(e) {
updateLog.textContent = _('Save failed') + ': ' + (e && e.message ? e.message : e);
updateLog.hidden = false;
}).finally(function() {
saveBtn.disabled = false; saveBtn.textContent = orig;
});
});
return E('details', {}, [
E('summary', {}, _('Data source and auto-update')),
E('div', {}, [
geoField(_('Source'), presetSel),
customRows,
geoField(_('Auto-update'), autoSel),
E('div', { 'class': 'honk-actions' }, [ saveBtn ]),
E('p', { 'class': 'honk-note', 'id': 'honk-geo-msg' })
])
]);
})();
dataSection.appendChild(E('h3', { 'class': 'honk-card-title' }, _('Geo data')));
dataSection.appendChild(E('p', { 'class': 'honk-note' }, _('GeoIP and GeoSite routing data can be updated here. Updates run in the background and reload Honk when done.')));
dataSection.appendChild(dataBody);
dataSection.appendChild(geoSettings);
/* --- Software package updates card --- */
const updateSection = E('section', { 'class': 'honk-card' });
const pkgBody = E('div', { 'id': 'honk-pkg' }, E('em', {}, _('Probing…')));
function probePkg(pkg) {
return fs.exec(PKG_INFO, [ pkg ]).then(function(res) {
const out = (res.stdout || '').trim().split('\t');
return { installed: out[0] || '', latest: out[1] || '' };
}).catch(function() {
return { installed: '', latest: '' };
});
}
function upgradePkg(pkg, btn) {
const orig = btn.textContent;
btn.disabled = true;
btn.textContent = '…';
let tries = 0;
const pollLog = function() {
return fs.read_direct('/tmp/luci-app-honk.pkg-' + pkg + '.log', 'text').then(function(c) {
if (c) {
updateLog.textContent = c;
updateLog.hidden = false;
}
if (/[✓✗]/.test(c)) { refreshPkgs(); return; }
if (tries++ > 90) return;
return new Promise(function(r) { setTimeout(r, 2000); }).then(pollLog);
}).catch(function() {});
};
return fs.exec(UPDATE_PKG, [ pkg ]).then(function(res) {
if (res.code === 0) return pollLog();
}).catch(function() {}).finally(function() {
btn.disabled = false;
btn.textContent = orig;
});
}
function refreshPkgs() {
const probes = PKGS.map(probePkg);
return Promise.all(probes).then(function(infos) {
while (pkgBody.firstChild)
pkgBody.removeChild(pkgBody.firstChild);
PKGS.forEach(function(pkg, i) {
const r = infos[i];
const btn = E('button', { 'class': 'cbi-button cbi-button-action', 'type': 'button' }, _('Upgrade'));
btn.addEventListener('click', function(ev) { ev.preventDefault(); upgradePkg(pkg, btn); });
const cmp = (r.installed && r.latest) ? cmpVer(r.latest, r.installed) : null;
const updatable = cmp !== null && cmp > 0;
let meta;
if (!r.installed) {
meta = _('not installed via package manager');
btn.disabled = true;
btn.textContent = _('Unavailable');
} else if (!r.latest) {
meta = _('installed') + ': ' + r.installed + ' · ' + _('latest version unknown');
btn.disabled = true;
} else if (updatable) {
meta = _('installed') + ': ' + r.installed + ' → ' + _('latest') + ': ' + r.latest;
} else {
meta = _('installed') + ': ' + r.installed + ' · ' + _('up to date');
btn.disabled = true;
}
pkgBody.appendChild(mkPkgRow(
updatable ? '↑' : (r.installed ? '✓' : '✗'),
updatable ? 'honk-pkg-new' : (r.installed ? 'honk-pkg-ok' : 'honk-pkg-err'),
PKG_NAMES[pkg],
meta,
btn
));
});
});
}
updateSection.appendChild(E('h3', { 'class': 'honk-card-title' }, _('Package updates')));
updateSection.appendChild(E('p', { 'class': 'honk-note' }, _('Honk core and LuCI can be upgraded here without leaving the management page. The package index refreshes in the background.')));
updateSection.appendChild(pkgBody);
/* --- Config backup card --- */
const backupSection = E('section', { 'class': 'honk-card' });
const backupMessage = E('p', { 'class': 'honk-status-msg', 'role': 'status' }, '');
const exportBtn = E('button', { 'class': 'cbi-button cbi-button-action', 'type': 'button' }, _('Export'));
const importBtn = E('button', { 'class': 'cbi-button', 'type': 'button' }, _('Import'));
const restoreCfgBtn = E('button', { 'class': 'cbi-button cbi-button-negative', 'type': 'button' }, _('Restore config'));
/* 忘记密码用:只清管理员记录,不动订阅和规则 */
const resetAdminBtn = E('button', { 'class': 'cbi-button', 'type': 'button' }, _('Reset administrator'));
const fileInput = E('input', { 'type': 'file', 'accept': '.tar.gz,.gz,application/gzip', 'style': 'display:none' });
let backupBusy = false;
writeControls.push(exportBtn, importBtn, restoreCfgBtn, resetAdminBtn);
function setBackupBusy(busy) {
backupBusy = busy;
[exportBtn, importBtn, restoreCfgBtn].forEach(function(b) {
b.disabled = busy || recoveryRequired;
});
}
exportBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (backupBusy) return;
setBackupBusy(true);
backupMessage.textContent = _('Preparing backup…');
honk.backup().then(honk.ensureOk).then(function(result) {
return honk.waitJob(result.job_id, function(job) {
backupMessage.textContent = job.state === 'running' ? _('Creating backup…') : _('Preparing download…');
});
}).then(function() {
return fs.read_direct(BACKUP_PATH, 'blob');
}).then(function(blob) {
const url = URL.createObjectURL(blob);
const link = E('a', { 'href': url, 'download': 'honk-backup.tar.gz' });
document.body.appendChild(link);
link.click();
link.remove();
setTimeout(function() { URL.revokeObjectURL(url); }, 1000);
backupMessage.textContent = _('Backup downloaded.');
}).catch(function(error) {
if (error && error.recoveryRequired)
blockWrites();
backupMessage.textContent = honk.errorMessage(error, _('Backup failed.'));
}).finally(function() { setBackupBusy(false); });
});
importBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (backupBusy) return;
fileInput.click();
});
fileInput.addEventListener('change', function(ev) {
const file = ev.target.files && ev.target.files[0];
if (!file) return;
if (!confirm(_('Stop Honk before restoring. Restoring replaces Honk user data. Continue?'))) {
fileInput.value = '';
return;
}
setBackupBusy(true);
backupMessage.textContent = _('Restoring…');
honk.restorePrepare().then(honk.ensureOk).then(function() {
return ui.uploadFile(RESTORE_PATH);
}).then(function() {
return honk.restore();
}).then(honk.ensureOk).then(function(result) {
return honk.waitJob(result.job_id, function(job) {
backupMessage.textContent = phaseMessage(job, _('Restoring Honk data…'));
});
}).then(function(result) {
backupMessage.textContent = honk.resultMessage(result, _('Backup restored.'));
}).catch(function(error) {
if (error && error.recoveryRequired)
blockWrites();
backupMessage.textContent = honk.errorMessage(error, _('Restore failed.'));
}).finally(function() {
setBackupBusy(false);
fileInput.value = '';
});
});
resetAdminBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (!confirm(_('Clear the Honk administrator account so the first-setup page opens again? Subscriptions, nodes and routing are kept. Honk will be stopped.')))
return;
setBackupBusy(true);
resetAdminBtn.disabled = true;
backupMessage.textContent = _('Resetting the administrator…');
honk.resetAdmin().then(honk.ensureOk).then(function(result) {
backupMessage.textContent = honk.resultMessage(result, _('Administrator cleared. Open the Overview page and run the first setup again.'));
window.setTimeout(function() { window.location.href = L.url('admin/services/honk/overview'); }, 1200);
}).catch(function(error) {
backupMessage.textContent = honk.errorMessage(error, _('Could not reset the administrator.'));
}).finally(function() {
setBackupBusy(false);
resetAdminBtn.disabled = false;
});
});
restoreCfgBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (!confirm(_('This permanently clears Honk subscriptions, nodes, policies, DNS data, history, and cache, then restores the default configuration. The administrator account is kept, and Honk will be stopped. Continue?')))
return;
setBackupBusy(true);
backupMessage.textContent = _('Restoring default config…');
honk.reset().then(honk.ensureOk).then(function(result) {
return honk.waitJob(result.job_id, function(job) {
backupMessage.textContent = phaseMessage(job, _('Restoring default config…'));
});
}).then(function(result) {
backupMessage.textContent = honk.resultMessage(result, _('Honk data reset. The service is stopped.'));
}).catch(function(error) {
if (error && error.recoveryRequired)
blockWrites();
backupMessage.textContent = honk.errorMessage(error, _('Reset failed.'));
}).finally(function() { setBackupBusy(false); });
});
backupSection.appendChild(E('h3', { 'class': 'honk-card-title' }, _('Config backup')));
backupSection.appendChild(E('p', { 'class': 'honk-note' }, _('Export a backup archive, import one from your computer, or restore Honk to its default configuration.')));
backupSection.appendChild(E('div', { 'class': 'honk-actions' }, [ exportBtn, importBtn, restoreCfgBtn, resetAdminBtn ]));
backupSection.appendChild(fileInput);
backupSection.appendChild(backupMessage);
/* --- System check and repair card --- */
const health = E('section', { 'class': 'honk-card' });
const healthMessage = E('p', { 'class': 'honk-status-msg', 'role': 'status' }, '');
const check = E('button', { 'class': 'cbi-button cbi-button-action' }, _('Run system check'));
check.addEventListener('click', function(ev) {
ev.preventDefault();
check.disabled = true;
healthMessage.textContent = _('Checking…');
honk.check().then(function(result) {
if (result && result.ok === true) {
healthMessage.textContent = _('System check passed.');
}
else if (Array.isArray(result && result.errors) && result.errors.length) {
healthMessage.textContent = result.errors.map(honk.statusIssue).join(';');
}
else {
healthMessage.textContent = _('The backend did not confirm the check.');
}
}).catch(function(error) {
healthMessage.textContent = _('System check failed.');
}).finally(function() { check.disabled = false; });
});
const repairMessage = E('p', { 'class': 'honk-status-msg', 'role': 'status' }, '');
const repair = E('button', { 'class': 'cbi-button cbi-button-action' }, _('Repair'));
writeControls.push(repair);
repair.addEventListener('click', function(ev) {
ev.preventDefault();
repair.disabled = true;
repairMessage.textContent = _('Repairing…');
honk.repair().then(honk.ensureOk).then(function(result) {
repairMessage.textContent = honk.resultMessage(result, _('Repair completed.'));
}).catch(function(error) {
if (error && error.recoveryRequired)
blockWrites();
repairMessage.textContent = honk.errorMessage(error, _('Repair failed.'));
}).finally(function() { repair.disabled = recoveryRequired; });
});
health.appendChild(E('h3', { 'class': 'honk-card-title' }, _('System check and repair')));
health.appendChild(E('p', { 'class': 'honk-note' }, _('Checks Honk system components and reports detected issues.')));
health.appendChild(E('div', { 'class': 'honk-actions' }, [ check, repair ]));
health.appendChild(healthMessage);
health.appendChild(repairMessage);
health.appendChild(E('p', { 'class': 'honk-note' }, _('Repair replaces missing or damaged Honk system files and does not overwrite user configuration.')));
page.appendChild(E('div', { 'class': 'honk-header' }, [
E('h2', {}, _('Maintenance')),
E('p', { 'class': 'honk-header-sub' }, _('Package updates, config backup and system recovery for the Honk installation.'))
]));
page.appendChild(recoveryMessage);
page.appendChild(dataSection);
page.appendChild(updateSection);
page.appendChild(backupSection);
page.appendChild(health);
page.appendChild(updateLog);
// Background index refresh so new versions show on the next poll.
fs.exec(REFRESH_INDEX, []).catch(function() {});
refreshData();
refreshPkgs();
poll.add(refreshData, 15);
poll.add(refreshPkgs, 15);
honk.status().then(function(result) {
const errors = Array.isArray(result && result.errors) ? result.errors : [];
const recoveryToken = errors.indexOf('update_recovery_required') >= 0 ? 'update_recovery_required' :
errors.indexOf('maintenance_recovery_required') >= 0 ? 'maintenance_recovery_required' : null;
if (recoveryToken) {
blockWrites();
recoveryMessage.textContent = honk.statusIssue(recoveryToken);
recoveryMessage.hidden = false;
}
}).catch(function() {});
return page;
}
});
@@ -0,0 +1,424 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require network';
'require poll';
'require view';
'require view.honk.rpc as honk';
'require uci';
function getNetworkNames(networks) {
if (!networks)
return [];
const list = Array.isArray(networks) ? networks : Object.keys(networks).map(function(key) { return networks[key]; });
return list.map(function(net) {
if (net && typeof net.getName === 'function')
return net.getName();
if (net && typeof net.name === 'string')
return net.name;
return typeof net === 'string' ? net : '';
}).filter(function(name, index, names) {
return !!name && names.indexOf(name) === index;
}).sort();
}
function metaItem(label, value) {
return E('span', { 'class': 'honk-meta-item' }, [
E('span', { 'class': 'honk-meta-label' }, label),
value == null || value === '' ? '—' : String(value)
]);
}
function field(label, input, help) {
/* 说明文字放进控件列下方,两栏布局时才不会挤到右边去 */
const cell = E('div', { 'class': 'cbi-value-field' }, [ input ]);
if (help)
cell.appendChild(E('div', { 'class': 'cbi-value-description honk-note' }, help));
return E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': input.id }, label),
cell
]);
}
function captureFields(root) {
const values = {};
Array.prototype.forEach.call(root.querySelectorAll('input, select, textarea'), function(input) {
const key = input.id || input.name;
if (!key)
return;
values[key] = {
value: input.value,
checked: input.checked,
focused: document.activeElement === input,
selectionStart: typeof input.selectionStart === 'number' ? input.selectionStart : null,
selectionEnd: typeof input.selectionEnd === 'number' ? input.selectionEnd : null
};
});
return values;
}
function restoreFields(root, values) {
Object.keys(values).forEach(function(key) {
const input = root.querySelector('#%s'.format(key));
if (!input || input.type === 'file')
return;
input.value = values[key].value;
input.checked = values[key].checked;
if (values[key].focused) {
input.focus();
if (values[key].selectionStart !== null && typeof input.setSelectionRange === 'function')
input.setSelectionRange(values[key].selectionStart, values[key].selectionEnd);
}
});
}
return view.extend({
handleSave: null,
handleSaveApply: null,
handleReset: null,
load: function() {
return Promise.all([
honk.status().then(function(status) { return { status: status }; }).catch(function(error) { return { error: error.message || String(error) }; }),
network.getNetworks().then(getNetworkNames).then(function(networks) { return { networks: networks }; }).catch(function() { return { networks: [] }; }),
uci.load('honk').then(function() { return { clash_secret: uci.get('honk', 'main', 'clash_api_secret') || '' }; }).catch(function() { return { clash_secret: '' }; })
]).then(function(results) {
return Object.assign({}, results[0], results[1], results[2]);
});
},
render: function(data) {
honk.installStyles();
const page = E('div', { 'class': 'cbi-map honk-page' });
const statusArea = E('div');
const actionMessage = E('p', { 'class': 'honk-status-msg', 'role': 'status' }, '');
const setupArea = E('div');
let currentStatus = data.status;
const clashSecret = data.clash_secret || '';
let busy = false;
let setupBusy = false;
let settingsBusy = false;
let setupState = null;
function renderStatus(status, error) {
const fields = captureFields(statusArea);
const focusedAction = statusArea.contains(document.activeElement) && document.activeElement.dataset.action;
while (statusArea.firstChild)
statusArea.removeChild(statusArea.firstChild);
if (error) {
statusArea.appendChild(E('div', { 'class': 'alert-message' }, _('Could not read Honk status.')));
restoreFields(statusArea, fields);
return;
}
currentStatus = status;
const running = status.running === true;
/* Status card */
const badge = E('span', {
'class': 'honk-badge %s'.format(running ? 'honk-badge--run' : 'honk-badge--stop')
}, running ? _('Running') : _('Stopped'));
const webUi = honk.localWebUi(status);
const openDoona = E(webUi ? 'a' : 'button', {
'class': 'cbi-button cbi-button-action',
'href': webUi || null,
'target': webUi ? '_blank' : null,
'rel': webUi ? 'noopener noreferrer' : null,
'type': webUi ? null : 'button',
'disabled': webUi ? null : true
}, _('Open Doona'));
const clashUi = honk.localClashUi(status, clashSecret);
const openClash = E(clashUi ? 'a' : 'button', {
'class': 'cbi-button cbi-button-action',
'href': clashUi || null,
'target': clashUi ? '_blank' : null,
'rel': clashUi ? 'noopener noreferrer' : null,
'type': clashUi ? null : 'button',
'disabled': clashUi ? null : true
}, _('Open Clash panel'));
const toggle = E('button', {
'class': 'cbi-button cbi-button-action',
'type': 'button',
'data-action': running ? 'stop' : 'start'
}, running ? _('Stop') : _('Start'));
toggle.disabled = busy || !status.initialized;
if (!status.initialized)
toggle.title = _('Finish the first setup first: Honk does not start without an administrator.');
toggle.addEventListener('click', function(ev) {
ev.preventDefault();
runAction(running ? 'stop' : 'start');
});
const actions = [ openDoona, openClash, toggle ];
if (running) {
const restart = E('button', { 'class': 'cbi-button', 'type': 'button', 'data-action': 'restart' }, _('Restart'));
restart.disabled = busy;
restart.addEventListener('click', function(ev) {
ev.preventDefault();
runAction('restart');
});
actions.push(restart);
}
const statusCard = E('section', { 'class': 'honk-card' }, [
E('div', { 'class': 'honk-card-head' }, [
E('div', {}, [
E('h3', { 'class': 'honk-card-title' }, _('Status')),
badge
]),
E('div', { 'class': 'honk-actions' }, actions)
]),
E('div', { 'class': 'honk-meta' }, [
metaItem(_('Core version'), status.core_version),
metaItem(_('Doona version'), status.doona_version),
metaItem(_('Kernel'), status.kernel)
])
]);
if (status.clash_api_enabled && running && !clashUi)
statusCard.appendChild(E('p', { 'class': 'honk-note', 'role': 'status' }, status.clash_api_ready ? _('Zashboard is still downloading or unavailable.') : _('The Clash API listener is unavailable. Check the service logs.')));
const errors = (Array.isArray(status.errors) ? status.errors : []).filter(function(token) {
return token !== 'disabled' && token !== 'not_initialized' && token !== 'service_not_running';
});
if (errors.length)
statusCard.appendChild(E('ul', { 'class': 'alert-message' }, errors.map(function(token) {
return E('li', {}, honk.statusIssue(token));
})));
if (!status.initialized)
statusCard.appendChild(E('p', { 'class': 'honk-note', 'role': 'status' }, _('Not initialized yet. Finish the first setup below, then Honk can be started.')));
statusArea.appendChild(statusCard);
statusArea.appendChild(renderServiceSettings(status));
if (focusedAction) {
const replacement = statusArea.querySelector('[data-action="%s"]'.format(focusedAction));
if (replacement)
replacement.focus();
}
restoreFields(statusArea, fields);
}
function renderServiceSettings(status) {
const card = E('section', { 'class': 'honk-card' });
const form = E('form');
const networkSelect = E('select', { 'class': 'cbi-input-select', 'name': 'lan_network', 'id': 'honk-lan-network' });
const portInput = E('input', { 'class': 'cbi-input-text', 'type': 'number', 'name': 'listen_port', 'id': 'honk-listen-port', 'min': '1024', 'max': '65535', 'step': '1' });
const clashEnabledInput = E('input', { 'type': 'checkbox', 'name': 'clash_api_enabled', 'id': 'honk-clash-enabled' });
const clashPortInput = E('input', { 'class': 'cbi-input-text', 'type': 'number', 'name': 'clash_api_port', 'id': 'honk-clash-port', 'min': '1024', 'max': '65535', 'step': '1' });
const clashSecretInput = E('input', { 'class': 'cbi-input-password', 'type': 'password', 'name': 'clash_api_secret', 'id': 'honk-clash-secret', 'autocomplete': 'new-password', 'readonly': 'readonly' });
clashSecretInput.addEventListener('focus', function() { clashSecretInput.removeAttribute('readonly'); });
const message = E('p', { 'class': 'honk-status-msg', 'role': 'status' }, '');
const save = E('button', { 'class': 'cbi-button cbi-button-positive', 'type': 'submit' }, _('Save service settings'));
(data.networks || []).forEach(function(name) {
networkSelect.appendChild(E('option', { 'value': name }, name));
});
if (status.lan_network && !(data.networks || []).includes(status.lan_network))
networkSelect.appendChild(E('option', { 'value': status.lan_network }, status.lan_network));
if (status.lan_network)
networkSelect.value = status.lan_network;
portInput.value = status.listen_port ? String(status.listen_port) : '9527';
clashEnabledInput.checked = status.clash_api_enabled === true;
clashPortInput.value = status.clash_api_port ? String(status.clash_api_port) : '9090';
card.appendChild(E('h3', { 'class': 'honk-card-title' }, _('Service settings')));
/* Manual mode owns the whole runtime from config.dae, so the backend
rejects these settings. Disable them here instead of failing on save. */
const manualMode = status.config_mode === 'manual';
if (manualMode) {
[ networkSelect, portInput, clashEnabledInput, clashPortInput, clashSecretInput ].forEach(function(input) {
input.disabled = true;
});
save.disabled = true;
card.appendChild(E('p', { 'class': 'honk-note honk-mode-hint' }, _('Manual mode: these settings belong to config.dae, so they are read-only here. Restore managed mode on the Configuration page to change them.')));
}
if (!(data.networks || []).length)
networkSelect.appendChild(E('option', { 'value': '' }, _('No network interfaces found')));
form.appendChild(field(_('LAN network'), networkSelect));
form.appendChild(field(_('Listen port'), portInput));
form.appendChild(field(_('Enable Clash API'), clashEnabledInput));
form.appendChild(field(_('Clash API port'), clashPortInput));
form.appendChild(field(_('Clash API secret'), clashSecretInput, status.clash_api_secret_set ? _('A secret is set. Leave blank to keep it.') : _('Required when enabling. Use 6–128 letters, numbers, dots, underscores or hyphens.')));
form.appendChild(E('p', { 'class': 'honk-note' }, _('Use your existing HTTPS reverse proxy for external access. Enter the secret in Zashboard when connecting.')));
form.appendChild(E('div', { 'class': 'cbi-page-actions' }, save));
form.appendChild(message);
form.addEventListener('submit', function(ev) {
ev.preventDefault();
const port = Number(portInput.value);
const clashPort = Number(clashPortInput.value);
const clashSecret = clashSecretInput.value;
if (!networkSelect.value || !Number.isInteger(port) || port < 1024 || port > 65535) {
message.textContent = _('Choose a LAN network and a port from 1024 to 65535.');
return;
}
if (!Number.isInteger(clashPort) || clashPort < 1024 || clashPort > 65535) {
message.textContent = _('Clash API port must be between 1024 and 65535.');
return;
}
if (clashEnabledInput.checked && clashPort === port) {
message.textContent = _('The Clash API port must differ from the listen port.');
return;
}
if (clashEnabledInput.checked && !status.clash_api_secret_set && !clashSecret) {
message.textContent = _('A secret is required when enabling Clash API.');
return;
}
if (clashEnabledInput.checked && clashSecret && !/^[A-Za-z0-9._-]{6,128}$/.test(clashSecret)) {
message.textContent = _('The secret must be 6-128 letters, numbers, dots, underscores or hyphens.');
return;
}
save.disabled = true;
settingsBusy = true;
message.textContent = _('Saving…');
honk.settings(networkSelect.value, port, clashEnabledInput.checked, clashPort, clashSecret).then(honk.ensureOk).then(function(result) {
clashSecretInput.value = '';
message.textContent = honk.resultMessage(result, _('Service settings saved.'));
}).catch(function(error) {
message.textContent = honk.errorMessage(error, _('Settings could not be applied.'));
}).finally(function() {
settingsBusy = false;
save.disabled = false;
refresh();
});
});
card.appendChild(form);
return card;
}
function renderSetup(status) {
if (setupBusy)
return;
if (!status)
return;
const initialized = status.initialized === true;
if (setupState === initialized)
return;
setupState = initialized;
const fields = captureFields(setupArea);
while (setupArea.firstChild)
setupArea.removeChild(setupArea.firstChild);
if (initialized)
return;
const form = E('form', { 'class': 'honk-card' });
const username = E('input', { 'type': 'text', 'name': 'username', 'id': 'honk-init-username', 'autocomplete': 'username', 'required': true, 'class': 'cbi-input-text' });
const password = E('input', { 'type': 'password', 'name': 'password', 'id': 'honk-init-password', 'autocomplete': 'new-password', 'required': true, 'class': 'cbi-input-password' });
const message = E('p', { 'class': 'honk-status-msg', 'role': 'status' }, '');
const submit = E('button', { 'class': 'cbi-button cbi-button-positive', 'type': 'submit' }, _('Initialize Honk'));
form.appendChild(E('h3', { 'class': 'honk-card-title' }, _('Initial setup')));
form.appendChild(E('p', { 'class': 'honk-note' }, _('Create the Honk administrator account. The password is sent only for this request and is not saved by this page.')));
form.appendChild(E('p', { 'class': 'honk-note' }, _('Username: letters, digits, underscore, dot or hyphen (max 64 characters). Password: 8 to 128 characters.')));
form.appendChild(field(_('Username'), username));
form.appendChild(field(_('Password'), password));
form.appendChild(E('div', { 'class': 'cbi-page-actions' }, submit));
form.appendChild(message);
form.addEventListener('submit', function(ev) {
ev.preventDefault();
const user = username.value.trim();
const secret = password.value;
if (!user || !secret) {
message.textContent = _('Enter both a username and password.');
return;
}
/* 和后端 read_credentials 的规则保持一致,避免"看着合法却提交失败" */
if (!/^[A-Za-z0-9_.-]{1,64}$/.test(user)) {
message.textContent = _('Username may only contain letters, digits, underscore, dot or hyphen (max 64 characters).');
return;
}
/* 内核 auth.rs 规定密码 8–128 个字符(按 Unicode 码点计),这里先拦一道 */
const secretLength = Array.from(secret).length;
if (secretLength < 8 || secretLength > 128) {
message.textContent = _('The password must be between 8 and 128 characters.');
return;
}
submit.disabled = true;
setupBusy = true;
message.textContent = _('Initializing…');
honk.initialize(user, secret).then(honk.ensureOk).then(function(result) {
username.value = '';
password.value = '';
message.textContent = honk.resultMessage(result, _('Honk initialized.'));
return honk.status();
}).then(function(updatedStatus) {
setupBusy = false;
currentStatus = updatedStatus;
renderSetup(updatedStatus);
renderStatus(updatedStatus, null);
}).catch(function(error) {
password.value = '';
/* 以前这里把错误整个吞掉,用户只看到「初始化失败」。现在先给出
具体错误码的中文说明,再顺手跑一遍系统检查,把缺什么列出来。 */
message.textContent = honk.errorMessage(error, _('Initialization failed.'));
/* 三态:查出问题 / 检查通过 / 检查根本没跑完。以前 check() 失败被当成"没问题"。 */
return honk.check().then(function(check) {
const issues = (check && Array.isArray(check.errors)) ? check.errors : [];
if (issues.length)
message.textContent += ' ' + _('System check: %s').format(issues.map(honk.statusIssue).join('; '));
else if (check && check.ok === true)
message.textContent += ' ' + _('The system check passed. If it keeps failing, check the core log under Maintenance → Logs and report the code above.');
else
message.textContent += ' ' + _('The system check did not finish. Retry, or check the core log under Maintenance → Logs.');
}, function() {
message.textContent += ' ' + _('The system check did not finish (interface timeout or error). Retry, or check the core log under Maintenance → Logs.');
});
}).finally(function() {
setupBusy = false;
submit.disabled = false;
});
});
setupArea.appendChild(form);
restoreFields(setupArea, fields);
}
function refresh() {
if (busy || setupBusy || settingsBusy)
return Promise.resolve();
return honk.status().then(function(status) {
renderStatus(status, null);
renderSetup(status);
}).catch(function() {
renderStatus(null, true);
});
}
function runAction(method) {
if (busy)
return;
busy = true;
actionMessage.textContent = _('Working…');
renderStatus(currentStatus, null);
honk[method]().then(honk.ensureOk).then(function(result) {
actionMessage.textContent = honk.resultMessage(result, _('Operation completed.'));
}).catch(function(error) {
actionMessage.textContent = honk.errorMessage(error, _('Operation failed. Check Honk status.'));
}).finally(function() {
busy = false;
refresh();
});
}
page.appendChild(E('div', { 'class': 'honk-header' }, [
E('h2', {}, _('Honk')),
E('p', { 'class': 'honk-header-sub' }, _('Transparent proxy core for OpenWrt. Start, stop and manage the Honk service.'))
]));
page.appendChild(statusArea);
page.appendChild(actionMessage);
page.appendChild(setupArea);
renderStatus(data.status, data.error);
if (data.status)
renderSetup(data.status);
poll.add(refresh, 5);
return page;
}
});
@@ -0,0 +1,489 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require baseclass';
'require rpc';
const callStatus = rpc.declare({
object: 'honk',
method: 'status',
expect: { '': {} }
});
const callCheck = rpc.declare({
object: 'honk',
method: 'check',
expect: { '': {} }
});
const callInitialize = rpc.declare({
object: 'honk',
method: 'initialize',
params: [ 'username', 'password' ],
expect: { '': {} }
});
const callStart = rpc.declare({ object: 'honk', method: 'start', expect: { '': {} } });
const callStop = rpc.declare({ object: 'honk', method: 'stop', expect: { '': {} } });
const callRestart = rpc.declare({ object: 'honk', method: 'restart', expect: { '': {} } });
const callSettings = rpc.declare({
object: 'honk',
method: 'settings',
params: [ 'lan_network', 'listen_port', 'clash_api_enabled', 'clash_api_port', 'clash_api_secret' ],
expect: { '': {} }
});
const callProbeSubscription = rpc.declare({
object: 'honk',
method: 'probe_subscription',
params: [ 'url' ],
expect: { '': {} }
});
const callAddSubscription = rpc.declare({
object: 'honk',
method: 'add_subscription',
params: [ 'name', 'url' ],
expect: { '': {} }
});
const callAddNodes = rpc.declare({
object: 'honk',
method: 'add_nodes',
params: [ 'links' ],
expect: { '': {} }
});
const callImportDae = rpc.declare({
object: 'honk',
method: 'import_dae',
expect: { '': {} }
});
const callConfigRead = rpc.declare({ object: 'honk', method: 'config_read', expect: { '': {} } });
const callConfigDraftRead = rpc.declare({ object: 'honk', method: 'config_draft_read', expect: { '': {} } });
const callLanCandidates = rpc.declare({ object: 'honk', method: 'lan_candidates', expect: { '': {} } });
const callConfigValidate = rpc.declare({ object: 'honk', method: 'config_validate', params: [ 'content' ], expect: { '': {} } });
const callConfigSave = rpc.declare({ object: 'honk', method: 'config_save', params: [ 'content' ], expect: { '': {} } });
const callConfigApply = rpc.declare({ object: 'honk', method: 'config_apply', expect: { '': {} } });
const callConfigUseManaged = rpc.declare({ object: 'honk', method: 'config_use_managed', expect: { '': {} } });
const callRepair = rpc.declare({ object: 'honk', method: 'repair', expect: { '': {} } });
const callBackup = rpc.declare({ object: 'honk', method: 'backup', expect: { '': {} } });
const callRestore = rpc.declare({ object: 'honk', method: 'restore', expect: { '': {} } });
const callRestorePrepare = rpc.declare({ object: 'honk', method: 'restore_prepare', expect: { '': {} } });
const callResetAdmin = rpc.declare({ object: 'honk', method: 'reset_admin', expect: { '': {} } });
const callReset = rpc.declare({ object: 'honk', method: 'reset', expect: { '': {} } });
const callJobStatus = rpc.declare({
object: 'honk',
method: 'job_status',
params: [ 'job_id' ],
expect: { '': {} }
});
const callLogs = rpc.declare({
object: 'honk',
method: 'logs',
params: [ 'source', 'level', 'limit' ],
expect: { '': {} }
});
const callUpdateCheck = rpc.declare({ object: 'honk', method: 'update_check', expect: { '': {} } });
const callUpdateApply = rpc.declare({ object: 'honk', method: 'update_apply', expect: { '': {} } });
let pageStylesInstalled = false;
function installStyles() {
if (pageStylesInstalled || document.getElementById('honk-page-styles'))
return;
const stylesheet = document.createElement('link');
stylesheet.id = 'honk-page-styles';
stylesheet.rel = 'stylesheet';
stylesheet.href = L.resource('view/honk/honk.css');
document.head.appendChild(stylesheet);
pageStylesInstalled = true;
}
/* A core bound to 0.0.0.0 (or ::) reports a wildcard authority. A browser
cannot open that, but the router is still reachable at the host LuCI was
loaded from, so substitute that instead of hiding the panel button. */
function wildcardHost(hostname) {
const name = String(hostname || '').replace(/^\[|\]$/g, '').toLowerCase();
return name === '0.0.0.0' || name === '::' || name === '';
}
function urlHost(hostname) {
const name = String(hostname || '');
return name.indexOf(':') >= 0 ? '[' + name.replace(/^\[|\]$/g, '') + ']' : name;
}
function localWebUi(status) {
if (!status || !status.initialized || !status.running || !status.api_ready || !status.api_url)
return null;
try {
const raw = String(status.api_url);
const authority = raw.match(/^https?:\/\/([^/?#]+)/i);
if (!authority || authority[1].indexOf('@') >= 0)
return null;
const portMatch = authority[1].match(/:(\d+)$/);
const url = new URL(raw);
const hostname = url.hostname.replace(/^\[|\]$/g, '').toLowerCase();
const port = portMatch ? Number(portMatch[1]) : 0;
if (!portMatch || !Number.isInteger(port) || port < 1 || port > 65535 ||
url.protocol !== 'http:' && url.protocol !== 'https:' ||
url.username || url.password || url.search || url.hash)
return null;
if (wildcardHost(hostname) && window.location.hostname)
return url.protocol + '//' + urlHost(window.location.hostname) + ':' + port + '/ui/';
const octets = hostname.split('.');
let localIPv4 = false;
if (octets.length === 4 && octets.every(function(part) { return /^\d{1,3}$/.test(part) && Number(part) <= 255; })) {
const ip = octets.map(Number);
localIPv4 = ip[0] === 10 || ip[0] === 127 || ip[0] === 192 && ip[1] === 168 ||
ip[0] === 172 && ip[1] >= 16 && ip[1] <= 31 || ip[0] === 169 && ip[1] === 254;
}
const localIPv6 = hostname === '::1' || /^f[cd][0-9a-f:]*$/.test(hostname) || /^fe[89ab][0-9a-f:]*$/.test(hostname);
if (!localIPv4 && !localIPv6)
return null;
return url.origin.replace(/\/$/, '') + '/ui/';
}
catch (e) {
return null;
}
}
function localClashUi(status, secret) {
if (!status || !status.clash_api_enabled || !status.clash_api_ready || !status.clash_ui_ready)
return null;
const base = localWebUi(Object.assign({}, status, { api_url: status.clash_api_url, api_ready: true }));
if (!base)
return null;
try {
const url = new URL(status.clash_api_url);
const host = wildcardHost(url.hostname)
? String(window.location.hostname || '').replace(/^\[|\]$/g, '').toLowerCase()
: url.hostname.replace(/^\[|\]$/g, '').toLowerCase();
const port = status.clash_api_port || url.port;
const query = [
'host=' + encodeURIComponent(host),
'hostname=' + encodeURIComponent(host),
'port=' + encodeURIComponent(port),
'secret=' + encodeURIComponent(secret || '')
].join('&');
return base + '?' + query + '#/proxies';
}
catch (e) {
return null;
}
}
function tokenMessage(token) {
const messages = {
disabled: _('Honk is disabled.'),
not_initialized: _('Complete initial setup before starting Honk.'),
service_not_running: _('Honk is enabled but currently stopped.'),
api_unavailable: _('Honk is running, but its management interface is not ready.'),
operation_in_progress: _('Another Honk operation is in progress.'),
stale_operation_lock: _('A previous operation did not finish cleanly.'),
started: _('Honk started.'),
already_running: _('Honk is already running.'),
stopped: _('Honk stopped.'),
restarted: _('Honk restarted.'),
settings_saved: _('Settings saved and applied.'),
initialized: _('Honk initialized.'),
administrator_initialized: _('Honk initialized.'),
system_config_healthy: _('System check passed.'),
system_config_repaired: _('Repair completed.'),
backup_created: _('Backup created.'),
restored: _('Backup restored.'),
data_reset: _('Honk data reset. The service is stopped.'),
worker_lost: _('The maintenance worker stopped unexpectedly; check system recovery status before retrying.'),
operation_failed: _('The maintenance operation failed.'),
job_timeout: _('The maintenance operation timed out.'),
maintenance_recovery_required: _('Recovery is required. Run system recovery before starting another maintenance operation.'),
update_recovery_required: _('The signed update did not finish. Keep Honk stopped and reinstall a complete verified package set before retrying.'),
rollback_tuple_unavailable: _('A compatible update is available, but a complete verified rollback package set is not available yet.'),
trusted_feed_unavailable: _('No trusted update source is configured.'),
manifest_incompatible: _('The signed update manifest does not match this device.'),
signed_manifest_unavailable: _('The signed update manifest could not be fetched or verified.'),
update_available: _('A compatible update is available.'),
no_update: _('No compatible update is available.'),
already_current: _('Honk is already up to date.'),
update_apply_failed: _('The update could not be installed.'),
update_check_failed: _('The signed update could not be verified for this device.'),
signed_manifest_missing: _('Check for updates again to refresh the signed manifest.'),
signed_manifest_expired: _('The signed manifest expired. Check for updates again.'),
reset_failed: _('Reset failed; the previous service state was restored.'),
reset_failed_rollback_failed: _('Reset failed and automatic recovery did not finish. Run system recovery before retrying.'),
restore_failed_rollback_failed: _('Restore failed and automatic recovery did not finish. Run system recovery before retrying.'),
logs_unavailable: _('Honk logs are unavailable.'),
unsupported_core: _('This installed core does not support the requested import operation.'),
invalid_request: _('The import request is incomplete or invalid.'),
import_failed: _('The core could not prepare this import preview.'),
share_links_too_large: _('Share links exceed the 16 KiB per request limit.'),
share_links_invalid_or_conflicting: _('A share link is invalid or conflicts with an existing node. Review the input and try again.'),
subscription_invalid: _('The subscription could not be added: the name must be 1-64 characters without single quotes, and the URL must be a valid http(s) link.'),
subscription_invalid_or_conflicting: _('The subscription is invalid or its name already exists.'),
subscription_no_nodes: _('The subscription returned no usable nodes. The configuration was not changed.'),
subscription_added: _('Subscription saved with usable nodes.'),
nodes_added: _('Nodes saved.'),
reload_failed_rolled_back: _('Honk could not restart. The previous configuration and service were restored.'),
reload_and_rollback_failed: _('Honk could not restart or restore the previous service. Check its status and backup.'),
dae_imported: _('Configuration imported and Honk reloaded.'),
config_missing: _('The configuration file is missing.'),
config_write_failed: _('Could not write the configuration file.'),
dae_too_large: _('The dae file exceeds the 2 MiB limit.'),
dae_invalid_or_unsupported: _('The dae file contains invalid sections or unsupported includes.'),
candidate_invalid: _('Honk could not load the proposed configuration.'),
candidate_rejected: _('The imported configuration failed core validation.'),
source_changed: _('Honk or Doona changed the configuration during this operation. Try again.'),
preview_expired: _('The preview expired. Create a new preview.'),
preview_corrupt: _('The saved preview is damaged. Create a new preview.'),
dae_upload_unavailable: _('The dae upload is missing or unavailable. Upload the file again.'),
dae_upload_invalid: _('The uploaded dae file is invalid.'),
dae_upload_changed: _('The uploaded dae file changed. Upload it again.'),
upload_failed: _('Honk could not prepare the upload location. Check the upload directory permissions and try again.'),
upload_directory_unavailable: _('Honk could not prepare a private configuration upload directory.'),
upload_target_unavailable: _('The temporary configuration upload is unavailable. Try again.'),
config_too_large: _('The configuration file exceeds the 2 MiB limit.'),
config_applied: _('Configuration applied and Honk is running.'),
config_apply_failed_rolled_back: _('Honk could not start with this configuration. The previous file was restored.'),
config_apply_rollback_failed_snapshot_retained: _('Startup failed and the automatic rollback did not finish. The snapshot was kept; run system recovery.'),
config_changed_concurrently: _('The configuration changed during the operation. Nothing was applied.'),
config_write_failed_snapshot_retained: _('Could not write the configuration. The snapshot was kept.'),
service_stop_unconfirmed_snapshot_retained: _('Could not confirm that Honk stopped. The operation was aborted and the snapshot was kept.'),
manual_mode_required: _('Manual mode is active. Restore managed mode before using the subscription tools.'),
managed_edit_unavailable: _('Manual mode is active, so this tool cannot edit the configuration. Select Restore managed mode first.'),
administrator_exists_reuse_credentials_or_reset_state: _('已经初始化过了,请用原来的账号登录 Doona。忘记密码:到「维护 → 重置管理员」清掉管理员记录(不动订阅和规则),再回到本页重新初始化。'),
administrator_setup_failed: _('创建 Doona 管理员失败,请到「维护 → 日志」查看核心日志。'),
invalid_credentials: _('用户名只能包含字母、数字、下划线、点、连字符(不超过 64 个字符);密码长度必须是 8 到 128 个字符,可以包含空格。'),
disable_service_before_initialization: _('请先停止 Honk 服务再初始化。'),
honk_resources_already_exist: _('系统里还留着 dae0 / daens 等运行痕迹,请先停止服务或执行系统恢复。'),
core_missing: _('找不到 /usr/bin/honk-core,请重新安装 honk 包。'),
core_already_running: _('honk-core 已经在运行,请先停止服务。'),
jshn_unavailable: _('缺少 libubox 组件(/usr/share/libubox/jshn.sh),请检查固件是否完整。'),
temporary_storage_unavailable: _('临时目录 /tmp 不可写或空间不足。'),
system_config_backup_failed: _('备份现有 system.dae 失败,请检查磁盘空间。'),
system_snapshot_failed: _('备份系统配置失败,请检查磁盘空间。'),
uci_snapshot_failed: _('备份 UCI 配置失败,请检查磁盘空间。'),
loopback_listener_write_failed: _('写入 system.dae 的监听地址失败,请检查文件权限。'),
main_config_unavailable: _('/etc/honk/config.dae 不存在或不是普通文件。'),
main_config_changed_concurrently: _('操作期间 config.dae 被其他进程改动,已中止。'),
invalid_package_config: _('luci-app-honk 的软件包配置不完整,请重新安装。'),
invalid_config_path: _('配置路径不是 /etc/honk/config.dae,拒绝操作。'),
manual_config_valid: _('手动模式配置有效。'),
manual_config_invalid: _('手动模式配置无效,请先修正 config.dae。'),
required_system_include_missing_manual_repair: _('手动模式的配置缺少 include { system.dae },无法修复托管设置。'),
native_api_setup_state_unavailable: _('读不到核心的 API 状态,请查看日志。'),
ui_assets_missing: _('缺少 Doona 静态文件(/usr/share/doona),请重新安装 luci-app-honk。'),
mock_core_start_failed: _('测试用核心启动失败:通常是内核缺 BTF(/sys/kernel/btf/vmlinux)或 eBPF 依赖模块。'),
mock_core_stop_failed: _('测试用核心无法正常停止,请稍后重试。'),
mock_core_restart_failed: _('测试用核心重启失败,请查看日志。'),
mock_core_stop_failed_restored_loopback: _('测试核心停止失败,已恢复原来的监听配置。'),
generated_config_invalid_restored_loopback: _('生成的配置校验不通过,已恢复原来的监听配置。'),
uci_commit_failed_restored_loopback: _('写入 UCI 失败,已恢复原来的监听配置。'),
lan_api_verification_failed_restored_loopback: _('用局域网地址访问 API 验证失败(网卡或 IP 可能对不上),已恢复。'),
persistence_login_failed: _('初始化后登录验证失败,请重试或查看日志。'),
persistence_verification_failed: _('初始化结果没有落盘(可能磁盘空间不足)。'),
setup_verification_failed: _('初始化后的自检没通过,请查看日志。'),
lan_ipv4_unavailable: _('选中的 LAN 网络没有 IPv4 地址。'),
mkdir_failed: _('创建目录失败,请检查磁盘空间与权限。'),
settings_unavailable_in_manual_mode: _('手动模式下这些设置由 config.dae 决定,无法保存。'),
settings_too_large: _('提交的设置数据过大。'),
invalid_settings_json: _('设置的请求格式不正确。'),
invalid_boolean_settings: _('开关类设置取值不合法。'),
invalid_log_level: _('日志级别不合法。'),
invalid_log_limit: _('日志行数不合法。'),
invalid_log_source: _('日志来源不合法。'),
invalid_operation: _('未知的操作类型。'),
unknown_method: _('未知的接口方法。'),
settings_changed_concurrently: _('操作期间设置被改动,已中止。'),
lan_network_changed_during_settings: _('操作期间 LAN 网络被改动,已中止。'),
lan_network_changed_during_repair: _('操作期间 LAN 网络被改动,已中止。'),
service_state_changed_concurrently: _('操作期间服务状态发生变化,已中止。'),
settings_commit_failed_rolled_back: _('设置写入失败,已回滚到原值。'),
settings_commit_and_rollback_failed_snapshot_retained: _('设置写入失败且回滚未完成,快照已保留,请执行系统恢复。'),
service_apply_and_rollback_failed_snapshot_retained: _('应用设置失败且回滚未完成,快照已保留,请执行系统恢复。'),
service_stop_failed_rolled_back: _('停止服务失败,已回滚。'),
service_stop_and_rollback_failed_snapshot_retained: _('停止服务失败且回滚未完成,快照已保留。'),
system_config_write_failed: _('写入 system.dae 失败。'),
system_config_write_failed_rolled_back: _('写入 system.dae 失败,已回滚。'),
system_config_write_and_rollback_failed_snapshot_retained: _('写入 system.dae 失败且回滚未完成,快照已保留。'),
system_config_repair_failed_rolled_back: _('修复 system.dae 失败,已回滚。'),
system_config_repair_rollback_failed_snapshot_retained: _('修复 system.dae 失败且回滚未完成,快照已保留。'),
system_config_not_regular_file: _('system.dae 不是普通文件(可能是软链),拒绝操作。'),
boot_link_update_failed_rolled_back: _('更新开机自启链接失败,已回滚。'),
boot_link_update_and_rollback_failed_snapshot_retained: _('更新开机自启链接失败且回滚未完成,快照已保留。'),
repair_restart_failed_rolled_back: _('修复后重启失败,已回滚。'),
repair_restart_and_rollback_failed_snapshot_retained: _('修复后重启失败且回滚未完成,快照已保留。'),
subscription_unreachable: _('抓不到这个订阅地址:网络不通、需要代理,或链接已失效。'),
subscription_empty: _('订阅内容是空的。'),
subscription_too_large: _('订阅内容超过 4 MiB,拒绝处理。'),
configuration_validation_failed: _('配置没有通过静态校验。'),
config_changed_concurrently_restart_failed_snapshot_retained: _('操作期间配置被改动且重启失败,快照已保留。'),
invalid_job_id: _('任务编号不合法。'),
job_id_unavailable: _('拿不到任务编号。'),
job_not_found: _('找不到该任务。'),
job_status_unavailable: _('读不到任务状态。'),
job_storage_unavailable: _('任务存储不可用(/tmp 空间不足?)。'),
prepared: _('上传目录已准备好。'),
probe_failed: _('预检失败,请稍后重试。'),
managed_switch_failed_rolled_back: _('Could not switch back to managed mode. The previous configuration was restored.'),
managed_switch_rollback_failed_snapshot_retained: _('Switching back to managed mode failed and the rollback did not finish. The snapshot was kept.'),
managed_snapshot_invalid: _('The saved managed configuration is not a valid regular file.'),
config_draft_missing: _('There is no saved draft to apply.'),
config_draft_invalid: _('The saved draft is not a valid regular file.'),
managed_mode_restored: _('Managed mode restored.'),
managed_snapshot_missing: _('There is no saved managed configuration to restore.'),
lan_candidates_unavailable: _('Could not read the list of network interfaces.'),
config_read_failed: _('Honk could not read the current configuration.'),
config_request_invalid: _('The configuration request is empty or invalid.'),
config_validation_failed: _('The configuration did not pass static validation.'),
config_validation_unavailable: _('The core validator did not return a result.'),
config_saved_pending_apply: _('Configuration saved. The running service is unchanged until you apply it.'),
config_loaded: _('Configuration loaded.'),
dae_loaded: _('Configuration file loaded into the editor.'),
upload_ready: _('Upload location ready.'),
upload_cleared: _('Temporary upload cleared.'),
upload_unavailable: _('A private upload location could not be prepared.'),
settings_write_failed: _('Could not save Honk settings.'),
curl_missing: _('The curl command is missing. Install the curl package and try again.'),
start_failed: _('Honk could not start.'),
stop_failed: _('Honk could not stop.'),
restart_failed: _('Honk could not restart.'),
managed_service_unhealthy: _('The managed Honk service is not healthy.'),
unmanaged_core_running: _('An unmanaged Honk core process is running.'),
invalid_lan_network: _('The selected LAN network is invalid or unavailable.'),
lan_network_unavailable: _('The selected LAN network is unavailable.'),
listen_port_occupied: _('The listen port is already in use.'),
clash_api_port_occupied: _('The Clash API port is already in use.'),
invalid_clash_api_settings: _('Enter a different Clash API port and a secret of 6–128 letters, numbers, dots, underscores or hyphens.'),
invalid_listen_port: _('The listen port is invalid.'),
initialize_before_enabling_service: _('Complete initial setup before enabling Honk.'),
system_config_unavailable: _('Honk system configuration is unavailable.'),
package_config_unavailable: _('Honk configuration is unavailable.'),
service_apply_failed_rolled_back: _('Settings could not be applied. The previous settings were restored.')
};
return messages[token] || null;
}
function statusIssue(token) {
return tokenMessage(token) || _('Honk reported a system issue: %s').format(token);
}
function resultMessage(result, fallback) {
return result && tokenMessage(result.message) || fallback;
}
function errorMessage(error, fallback) {
const token = error && error.message;
const mapped = tokenMessage(token);
if (mapped)
return mapped;
/* 没映射过就把错误码原样带出来,否则用户只能看到「操作失败」,没法反馈 */
if (token && /^[a-z0-9_]+$/.test(token))
return _('%s (code: %s)').format(fallback || _('The operation failed.'), token);
return fallback;
}
function jobPhaseMessage(phase) {
const messages = {
queued: _('Waiting for the operation to start…'),
checking_feed: _('Checking the signed update source…'),
downloading: _('Downloading the signed package set…'),
preparing: _('Preparing configuration apply…'),
validating_candidate: _('Validating the candidate in an isolated runtime…'),
stopping: _('Stopping Honk safely…'),
applying: _('Applying the reviewed configuration…'),
validating: _('Checking the updated configuration…'),
starting: _('Restoring the previous service state…'),
rolling_back: _('Restoring the previous configuration…'),
committing: _('Finishing the transaction…'),
recovery: _('Recovery is required before another operation.')
};
return messages[phase] || _('Applying configuration…');
}
function ensureOk(result) {
if (!result || result.ok !== true)
throw new Error(result && result.message ? result.message : _('The backend did not confirm the operation.'));
return result;
}
function waitJob(jobId, onProgress) {
if (!/^[0-9a-f]{32}$/.test(jobId || ''))
return Promise.reject(new Error('invalid_job_id'));
let attempts = 0;
return new Promise(function(resolve, reject) {
function poll() {
callJobStatus(jobId).then(function(result) {
if (!result || typeof result.state !== 'string')
throw new Error('invalid_job_status');
if (onProgress)
onProgress(result);
if (result.state === 'succeeded') {
resolve(result);
return;
}
if (result.state === 'failed' || result.state === 'rollback_required') {
const error = new Error(result.state === 'rollback_required' ? 'maintenance_recovery_required' : result.message || 'operation_failed');
error.recoveryRequired = result.state === 'rollback_required';
reject(error);
return;
}
if (result.state !== 'queued' && result.state !== 'running') {
reject(new Error('invalid_job_status'));
return;
}
if (++attempts >= 1800) {
reject(new Error('job_timeout'));
return;
}
setTimeout(poll, 1000);
}).catch(reject);
}
poll();
});
}
return baseclass.extend({
status: callStatus,
check: callCheck,
initialize: callInitialize,
start: callStart,
stop: callStop,
restart: callRestart,
settings: callSettings,
probeSubscription: callProbeSubscription,
addSubscription: callAddSubscription,
addNodes: callAddNodes,
importDae: callImportDae,
configRead: callConfigRead,
configDraftRead: callConfigDraftRead,
lanCandidates: callLanCandidates,
configValidate: callConfigValidate,
configSave: callConfigSave,
configApply: callConfigApply,
configUseManaged: callConfigUseManaged,
repair: callRepair,
backup: callBackup,
restore: callRestore,
restorePrepare: callRestorePrepare,
resetAdmin: callResetAdmin,
reset: callReset,
logs: callLogs,
updateCheck: callUpdateCheck,
updateApply: callUpdateApply,
waitJob: waitJob,
ensureOk: ensureOk,
localWebUi: localWebUi,
localClashUi: localClashUi,
statusIssue: statusIssue,
resultMessage: resultMessage,
errorMessage: errorMessage,
jobPhaseMessage: jobPhaseMessage,
installStyles: installStyles
});
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require network';
'require view';
'require view.honk.rpc as honk';
function getNetworkNames(networks) {
if (!networks)
return [];
const list = Array.isArray(networks) ? networks : Object.keys(networks).map(function(key) { return networks[key]; });
return list.map(function(net) {
if (net && typeof net.getName === 'function')
return net.getName();
if (net && typeof net.name === 'string')
return net.name;
return typeof net === 'string' ? net : '';
}).filter(function(name, index, names) {
return !!name && names.indexOf(name) === index;
}).sort();
}
function field(label, input) {
return E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': input.id }, label),
E('div', { 'class': 'cbi-value-field' }, input)
]);
}
return view.extend({
handleSave: null,
handleSaveApply: null,
handleReset: null,
load: function() {
return Promise.all([
honk.status().then(function(status) { return { status: status }; }).catch(function(error) { return { statusError: error.message || String(error) }; }),
network.getNetworks().then(getNetworkNames).then(function(names) { return { networks: names }; }).catch(function(error) { return { networkError: error.message || String(error), networks: [] }; })
]).then(function(results) {
return Object.assign({}, results[0], results[1]);
});
},
render: function(data) {
honk.installStyles();
const page = E('div', { 'class': 'cbi-map honk-page' });
const section = E('form', { 'class': 'honk-card' });
const networkSelect = E('select', { 'class': 'cbi-input-select', 'name': 'lan_network', 'id': 'honk-lan-network' });
const portInput = E('input', { 'class': 'cbi-input-text', 'type': 'number', 'name': 'listen_port', 'id': 'honk-listen-port', 'min': '1024', 'max': '65535', 'step': '1' });
const clashEnabledInput = E('input', { 'type': 'checkbox', 'name': 'clash_api_enabled', 'id': 'honk-clash-enabled' });
const clashPortInput = E('input', { 'class': 'cbi-input-text', 'type': 'number', 'name': 'clash_api_port', 'id': 'honk-clash-port', 'min': '1024', 'max': '65535', 'step': '1' });
const clashSecretInput = E('input', { 'class': 'cbi-input-password', 'type': 'password', 'name': 'clash_api_secret', 'id': 'honk-clash-secret', 'autocomplete': 'new-password', 'readonly': 'readonly' });
clashSecretInput.addEventListener('focus', function() { clashSecretInput.removeAttribute('readonly'); });
const message = E('p', { 'class': 'honk-status-msg', 'role': 'status' }, '');
const save = E('button', { 'class': 'cbi-button cbi-button-positive', 'type': 'submit' }, _('Save settings'));
save.disabled = !!data.statusError;
(data.networks || []).forEach(function(name) {
networkSelect.appendChild(E('option', { 'value': name }, name));
});
if (data.status && data.status.lan_network && !(data.networks || []).includes(data.status.lan_network))
networkSelect.appendChild(E('option', { 'value': data.status.lan_network }, data.status.lan_network));
if (data.status && data.status.lan_network)
networkSelect.value = data.status.lan_network;
portInput.value = data.status && data.status.listen_port ? String(data.status.listen_port) : '9527';
clashEnabledInput.checked = !!(data.status && data.status.clash_api_enabled === true);
clashPortInput.value = data.status && data.status.clash_api_port ? String(data.status.clash_api_port) : '9090';
section.appendChild(E('h3', { 'class': 'honk-card-title' }, _('Honk settings')));
/* In manual mode the whole runtime comes from config.dae, so the backend
refuses these settings. Disable them instead of letting the save fail
with an error the user cannot act on. */
const manualMode = !!(data.status && data.status.config_mode === 'manual');
if (manualMode) {
[ networkSelect, portInput, clashEnabledInput, clashPortInput, clashSecretInput ].forEach(function(input) {
input.disabled = true;
});
save.disabled = true;
section.appendChild(E('p', { 'class': 'honk-note honk-mode-hint' }, _('Manual mode: these settings belong to config.dae, so they are read-only here. Restore managed mode on the Configuration page to change them.')));
}
if (data.statusError)
section.appendChild(E('div', { 'class': 'alert-message' }, _('Could not read current settings: %s').format(data.statusError)));
if (data.networkError)
section.appendChild(E('div', { 'class': 'alert-message' }, _('Could not load LAN networks: %s').format(data.networkError)));
if (!(data.networks || []).length)
networkSelect.appendChild(E('option', { 'value': '' }, _('No network interfaces found')));
section.appendChild(field(_('LAN network'), networkSelect));
section.appendChild(field(_('Listen port'), portInput));
section.appendChild(field(_('Enable Clash API'), clashEnabledInput));
section.appendChild(field(_('Clash API port'), clashPortInput));
section.appendChild(field(_('Clash API secret'), clashSecretInput));
section.appendChild(E('p', { 'class': 'honk-note' }, data.status && data.status.clash_api_secret_set ? _('A secret is set. Leave blank to keep it. Use your existing HTTPS reverse proxy for external access.') : _('A secret is required when enabling Clash API. Use 6–128 letters, numbers, dots, underscores or hyphens.')));
section.appendChild(E('div', { 'class': 'cbi-page-actions' }, save));
section.appendChild(message);
section.addEventListener('submit', function(ev) {
ev.preventDefault();
const port = Number(portInput.value);
const clashPort = Number(clashPortInput.value);
const clashSecret = clashSecretInput.value;
if (!networkSelect.value || !Number.isInteger(port) || port < 1024 || port > 65535) {
message.textContent = _('Choose a LAN network and a port from 1024 to 65535.');
return;
}
if (!Number.isInteger(clashPort) || clashPort < 1024 || clashPort > 65535) {
message.textContent = _('Clash API port must be between 1024 and 65535.');
return;
}
if (clashEnabledInput.checked && clashPort === port) {
message.textContent = _('The Clash API port must differ from the listen port.');
return;
}
if (clashEnabledInput.checked && !(data.status && data.status.clash_api_secret_set) && !clashSecret) {
message.textContent = _('A secret is required when enabling Clash API.');
return;
}
if (clashEnabledInput.checked && clashSecret && !/^[A-Za-z0-9._-]{6,128}$/.test(clashSecret)) {
message.textContent = _('The secret must be 6-128 letters, numbers, dots, underscores or hyphens.');
return;
}
save.disabled = true;
message.textContent = _('Saving…');
honk.settings(networkSelect.value, port, clashEnabledInput.checked, clashPort, clashSecret).then(honk.ensureOk).then(function(result) {
clashSecretInput.value = '';
message.textContent = honk.resultMessage(result, _('Settings saved and applied.'));
}).catch(function(error) {
message.textContent = honk.errorMessage(error, _('Save failed. Check the settings and system status.'));
}).finally(function() {
save.disabled = false;
});
});
page.appendChild(E('div', { 'class': 'honk-header' }, [
E('h2', {}, _('Settings')),
E('p', { 'class': 'honk-header-sub' }, _('Network interface, listen port and boot behaviour for the Honk service.'))
]));
page.appendChild(section);
return page;
}
});
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-3.0-only
'use strict';
'require baseclass';
function sha256(input) {
const bytes = new Uint8Array(input);
const bitLength = bytes.length * 8;
const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;
const padded = new Uint8Array(paddedLength);
padded.set(bytes);
padded[bytes.length] = 0x80;
const lowBits = bitLength >>> 0;
const highBits = Math.floor(bitLength / 0x100000000) >>> 0;
for (let i = 0; i < 4; i++) {
padded[paddedLength - 8 + i] = highBits >>> (24 - i * 8);
padded[paddedLength - 4 + i] = lowBits >>> (24 - i * 8);
}
const constants = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];
const state = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ];
const words = new Uint32Array(64);
const rotateRight = function(value, count) { return (value >>> count) | (value << (32 - count)); };
for (let offset = 0; offset < paddedLength; offset += 64) {
for (let i = 0; i < 16; i++) {
const at = offset + i * 4;
words[i] = ((padded[at] << 24) | (padded[at + 1] << 16) | (padded[at + 2] << 8) | padded[at + 3]) >>> 0;
}
for (let i = 16; i < 64; i++) {
const x = words[i - 15];
const y = words[i - 2];
const sigma0 = rotateRight(x, 7) ^ rotateRight(x, 18) ^ (x >>> 3);
const sigma1 = rotateRight(y, 17) ^ rotateRight(y, 19) ^ (y >>> 10);
words[i] = (words[i - 16] + sigma0 + words[i - 7] + sigma1) >>> 0;
}
let a = state[0], b = state[1], c = state[2], d = state[3];
let e = state[4], f = state[5], g = state[6], h = state[7];
for (let i = 0; i < 64; i++) {
const sum1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
const choice = (e & f) ^ (~e & g);
const temp1 = (h + sum1 + choice + constants[i] + words[i]) >>> 0;
const sum0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
const majority = (a & b) ^ (a & c) ^ (b & c);
const temp2 = (sum0 + majority) >>> 0;
h = g; g = f; f = e; e = (d + temp1) >>> 0;
d = c; c = b; b = a; a = (temp1 + temp2) >>> 0;
}
state[0] = (state[0] + a) >>> 0;
state[1] = (state[1] + b) >>> 0;
state[2] = (state[2] + c) >>> 0;
state[3] = (state[3] + d) >>> 0;
state[4] = (state[4] + e) >>> 0;
state[5] = (state[5] + f) >>> 0;
state[6] = (state[6] + g) >>> 0;
state[7] = (state[7] + h) >>> 0;
}
return state.map(function(value) { return value.toString(16).padStart(8, '0'); }).join('');
}
return baseclass.extend({ sha256: sha256 });
@@ -0,0 +1,21 @@
(The MIT License)
Copyright (C) 2011-2015 by Vitaly Puzrin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,33 @@
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.jsyaml={}))})(this,function(e){Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;l<u;l++)d=c[l],!o.call(e,d)&&d!==a&&n(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},l=(e,r,i)=>(i=e==null?{}:t(a(e)),c(r||!e||!e.__esModule?n(i,`default`,{value:e,enumerable:!0}):i,e)),u=s(((e,t)=>{function n(e){return e==null}function r(e){return typeof e==`object`&&!!e}function i(e){return Array.isArray(e)?e:n(e)?[]:[e]}function a(e,t){if(t){let n=Object.keys(t);for(let r=0,i=n.length;r<i;r+=1){let i=n[r];e[i]=t[i]}}return e}function o(e,t){let n=``;for(let r=0;r<t;r+=1)n+=e;return n}function s(e){return e===0&&1/e==-1/0}t.exports.isNothing=n,t.exports.isObject=r,t.exports.toArray=i,t.exports.repeat=o,t.exports.isNegativeZero=s,t.exports.extend=a})),d=s(((e,t)=>{function n(e,t){let n=``,r=e.reason||`(unknown reason)`;return e.mark?(e.mark.name&&(n+=`in "`+e.mark.name+`" `),n+=`(`+(e.mark.line+1)+`:`+(e.mark.column+1)+`)`,!t&&e.mark.snippet&&(n+=`
`+e.mark.snippet),r+` `+n):r}function r(e,t){Error.call(this),this.name=`YAMLException`,this.reason=e,this.mark=t,this.message=n(this,!1),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=Error().stack||``}r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r.prototype.toString=function(e){return this.name+`: `+n(this,e)},t.exports=r})),f=s(((e,t)=>{var n=u();function r(e,t,n,r,i){let a=``,o=``,s=Math.floor(i/2)-1;return r-t>s&&(a=` ... `,t=r-s+a.length),n-r>s&&(o=` ...`,n=r+s-o.length),{str:a+e.slice(t,n).replace(/\t/g,`→`)+o,pos:r-t+a.length}}function i(e,t){return n.repeat(` `,t-e.length)+e}function a(e,t){if(t=Object.create(t||null),!e.buffer)return null;t.maxLength||(t.maxLength=79),typeof t.indent!=`number`&&(t.indent=1),typeof t.linesBefore!=`number`&&(t.linesBefore=3),typeof t.linesAfter!=`number`&&(t.linesAfter=2);let a=/\r?\n|\r|\0/g,o=[0],s=[],c,l=-1;for(;c=a.exec(e.buffer);)s.push(c.index),o.push(c.index+c[0].length),e.position<=c.index&&l<0&&(l=o.length-2);l<0&&(l=o.length-1);let u=``,d=Math.min(e.line+t.linesAfter,s.length).toString().length,f=t.maxLength-(t.indent+d+3);for(let a=1;a<=t.linesBefore&&!(l-a<0);a++){let c=r(e.buffer,o[l-a],s[l-a],e.position-(o[l]-o[l-a]),f);u=n.repeat(` `,t.indent)+i((e.line-a+1).toString(),d)+` | `+c.str+`
`+u}let p=r(e.buffer,o[l],s[l],e.position,f);u+=n.repeat(` `,t.indent)+i((e.line+1).toString(),d)+` | `+p.str+`
`,u+=n.repeat(`-`,t.indent+d+3+p.pos)+`^
`;for(let a=1;a<=t.linesAfter&&!(l+a>=s.length);a++){let c=r(e.buffer,o[l+a],s[l+a],e.position-(o[l]-o[l+a]),f);u+=n.repeat(` `,t.indent)+i((e.line+a+1).toString(),d)+` | `+c.str+`
`}return u.replace(/\n$/,``)}t.exports=a})),p=s(((e,t)=>{var n=d(),r=[`kind`,`multi`,`resolve`,`construct`,`instanceOf`,`predicate`,`represent`,`representName`,`defaultStyle`,`styleAliases`],i=[`scalar`,`sequence`,`mapping`];function a(e){let t={};return e!==null&&Object.keys(e).forEach(function(n){e[n].forEach(function(e){t[String(e)]=n})}),t}function o(e,t){if(t=t||{},Object.keys(t).forEach(function(t){if(r.indexOf(t)===-1)throw new n(`Unknown option "`+t+`" is met in definition of "`+e+`" YAML type.`)}),this.options=t,this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.representName=t.representName||null,this.defaultStyle=t.defaultStyle||null,this.multi=t.multi||!1,this.styleAliases=a(t.styleAliases||null),i.indexOf(this.kind)===-1)throw new n(`Unknown kind "`+this.kind+`" is specified for "`+e+`" YAML type.`)}t.exports=o})),m=s(((e,t)=>{var n=d(),r=p();function i(e,t){let n=[];return e[t].forEach(function(e){let t=n.length;n.forEach(function(n,r){n.tag===e.tag&&n.kind===e.kind&&n.multi===e.multi&&(t=r)}),n[t]=e}),n}function a(){let e={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};function t(t){t.multi?(e.multi[t.kind].push(t),e.multi.fallback.push(t)):e[t.kind][t.tag]=e.fallback[t.tag]=t}for(let e=0,n=arguments.length;e<n;e+=1)arguments[e].forEach(t);return e}function o(e){return this.extend(e)}o.prototype.extend=function(e){let t=[],s=[];if(e instanceof r)s.push(e);else if(Array.isArray(e))s=s.concat(e);else if(e&&(Array.isArray(e.implicit)||Array.isArray(e.explicit)))e.implicit&&(t=t.concat(e.implicit)),e.explicit&&(s=s.concat(e.explicit));else throw new n(`Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })`);t.forEach(function(e){if(!(e instanceof r))throw new n(`Specified list of YAML types (or a single Type object) contains a non-Type object.`);if(e.loadKind&&e.loadKind!==`scalar`)throw new n(`There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.`);if(e.multi)throw new n(`There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.`)}),s.forEach(function(e){if(!(e instanceof r))throw new n(`Specified list of YAML types (or a single Type object) contains a non-Type object.`)});let c=Object.create(o.prototype);return c.implicit=(this.implicit||[]).concat(t),c.explicit=(this.explicit||[]).concat(s),c.compiledImplicit=i(c,`implicit`),c.compiledExplicit=i(c,`explicit`),c.compiledTypeMap=a(c.compiledImplicit,c.compiledExplicit),c},t.exports=o})),h=s(((e,t)=>{t.exports=new(p())(`tag:yaml.org,2002:str`,{kind:`scalar`,construct:function(e){return e===null?``:e}})})),g=s(((e,t)=>{t.exports=new(p())(`tag:yaml.org,2002:seq`,{kind:`sequence`,construct:function(e){return e===null?[]:e}})})),_=s(((e,t)=>{t.exports=new(p())(`tag:yaml.org,2002:map`,{kind:`mapping`,construct:function(e){return e===null?{}:e}})})),v=s(((e,t)=>{t.exports=new(m())({explicit:[h(),g(),_()]})})),y=s(((e,t)=>{var n=p();function r(e){if(e===null)return!0;let t=e.length;return t===1&&e===`~`||t===4&&(e===`null`||e===`Null`||e===`NULL`)}function i(){return null}function a(e){return e===null}t.exports=new n(`tag:yaml.org,2002:null`,{kind:`scalar`,resolve:r,construct:i,predicate:a,represent:{canonical:function(){return`~`},lowercase:function(){return`null`},uppercase:function(){return`NULL`},camelcase:function(){return`Null`},empty:function(){return``}},defaultStyle:`lowercase`})})),b=s(((e,t)=>{var n=p();function r(e){if(e===null)return!1;let t=e.length;return t===4&&(e===`true`||e===`True`||e===`TRUE`)||t===5&&(e===`false`||e===`False`||e===`FALSE`)}function i(e){return e===`true`||e===`True`||e===`TRUE`}function a(e){return Object.prototype.toString.call(e)===`[object Boolean]`}t.exports=new n(`tag:yaml.org,2002:bool`,{kind:`scalar`,resolve:r,construct:i,predicate:a,represent:{lowercase:function(e){return e?`true`:`false`},uppercase:function(e){return e?`TRUE`:`FALSE`},camelcase:function(e){return e?`True`:`False`}},defaultStyle:`lowercase`})})),x=s(((e,t)=>{var n=u(),r=p();function i(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function a(e){return e>=48&&e<=55}function o(e){return e>=48&&e<=57}function s(e){if(e===null)return!1;let t=e.length,n=0,r=!1;if(!t)return!1;let s=e[n];if((s===`-`||s===`+`)&&(s=e[++n]),s===`0`){if(n+1===t)return!0;if(s=e[++n],s===`b`){for(n++;n<t;n++){if(s=e[n],s!==`0`&&s!==`1`)return!1;r=!0}return r&&Number.isFinite(c(e))}if(s===`x`){for(n++;n<t;n++){if(!i(e.charCodeAt(n)))return!1;r=!0}return r&&Number.isFinite(c(e))}if(s===`o`){for(n++;n<t;n++){if(!a(e.charCodeAt(n)))return!1;r=!0}return r&&Number.isFinite(c(e))}}for(;n<t;n++){if(!o(e.charCodeAt(n)))return!1;r=!0}return r?Number.isFinite(c(e)):!1}function c(e){let t=e Line truncated
\r`;function i(e){if(e===null)return!1;let t=0,n=e.length,i=r;for(let r=0;r<n;r++){let n=i.indexOf(e.charAt(r));if(!(n>64)){if(n<0)return!1;t+=6}}return t%8==0}function a(e){let t=e.replace(/[\r\n=]/g,``),n=t.length,i=r,a=0,o=[];for(let e=0;e<n;e++)e%4==0&&e&&(o.push(a>>16&255),o.push(a>>8&255),o.push(a&255)),a=a<<6|i.indexOf(t.charAt(e));let s=n%4*6;return s===0?(o.push(a>>16&255),o.push(a>>8&255),o.push(a&255)):s===18?(o.push(a>>10&255),o.push(a>>2&255)):s===12&&o.push(a>>4&255),new Uint8Array(o)}function o(e){let t=``,n=0,i=e.length,a=r;for(let r=0;r<i;r++)r%3==0&&r&&(t+=a[n>>18&63],t+=a[n>>12&63],t+=a[n>>6&63],t+=a[n&63]),n=(n<<8)+e[r];let o=i%3;return o===0?(t+=a[n>>18&63],t+=a[n>>12&63],t+=a[n>>6&63],t+=a[n&63]):o===2?(t+=a[n>>10&63],t+=a[n>>4&63],t+=a[n<<2&63],t+=a[64]):o===1&&(t+=a[n>>2&63],t+=a[n<<4&63],t+=a[64],t+=a[64]),t}function s(e){return Object.prototype.toString.call(e)===`[object Uint8Array]`}t.exports=new n(`tag:yaml.org,2002:binary`,{kind:`scalar`,resolve:i,construct:a,predicate:s,represent:o})})),O=s(((e,t)=>{var n=p(),r=Object.prototype.hasOwnProperty,i=Object.prototype.toString;function a(e){if(e===null)return!0;let t=[],n=e;for(let e=0,a=n.length;e<a;e+=1){let a=n[e],o=!1;if(i.call(a)!==`[object Object]`)return!1;let s;for(s in a)if(r.call(a,s))if(!o)o=!0;else return!1;if(!o)return!1;if(t.indexOf(s)===-1)t.push(s);else return!1}return!0}function o(e){return e===null?[]:e}t.exports=new n(`tag:yaml.org,2002:omap`,{kind:`sequence`,resolve:a,construct:o})})),k=s(((e,t)=>{var n=p(),r=Object.prototype.toString;function i(e){if(e===null)return!0;let t=e,n=Array(t.length);for(let e=0,i=t.length;e<i;e+=1){let i=t[e];if(r.call(i)!==`[object Object]`)return!1;let a=Object.keys(i);if(a.length!==1)return!1;n[e]=[a[0],i[a[0]]]}return!0}function a(e){if(e===null)return[];let t=e,n=Array(t.length);for(let e=0,r=t.length;e<r;e+=1){let r=t[e],i=Object.keys(r);n[e]=[i[0],r[i[0]]]}return n}t.exports=new n(`tag:yaml.org,2002:pairs`,{kind:`sequence`,resolve:i,construct:a})})),A=s(((e,t)=>{var n=p(),r=Object.prototype.hasOwnProperty;function i(e){if(e===null)return!0;let t=e;for(let e in t)if(r.call(t,e)&&t[e]!==null)return!1;return!0}function a(e){return e===null?{}:e}t.exports=new n(`tag:yaml.org,2002:set`,{kind:`mapping`,resolve:i,construct:a})})),ee=s(((e,t)=>{t.exports=w().extend({implicit:[T(),E()],explicit:[D(),O(),k(),A()]})})),j=s(((e,t)=>{var n=u(),r=d(),i=f(),a=ee(),o=Object.prototype.hasOwnProperty,s=1,c=2,l=3,p=4,m=1,h=2,g=3,_=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,v=/[\x85\u2028\u2029]/,y=/[,\[\]{}]/,b=/^(?:!|!!|![0-9A-Za-z-]+!)$/,x=/^(?:!|[^,\[\]{}])(?:%[0-9a-f]{2}|[0-9a-z\-#;/?:@&=+$,_.!~*'()\[\]])*$/i;function S(e){return Object.prototype.toString.call(e)}function C(e){return e===10||e===13}function w(e){return e===9||e===32}function T(e){return e===9||e===32||e===10||e===13}function E(e){return e===44||e===91||e===93||e===123||e===125}function D(e){if(e>=48&&e<=57)return e-48;let t=e|32;return t>=97&&t<=102?t-97+10:-1}function O(e){return e===120?2:e===117?4:e===85?8:0}function k(e){return e>=48&&e<=57?e-48:-1}function A(e){switch(e){case 48:return`\0`;case 97:return`\x07`;case 98:return`\b`;case 116:return` `;case 9:return` `;case 110:return`
`;case 118:return`\v`;case 102:return`\f`;case 114:return`\r`;case 101:return`\x1B`;case 32:return` `;case 34:return`"`;case 47:return`/`;case 92:return`\\`;case 78:return`…`;case 95:return`\xA0`;case 76:return`\u2028`;case 80:return`\u2029`;default:return``}}function j(e){return e<=65535?String.fromCharCode(e):String.fromCharCode((e-65536>>10)+55296,(e-65536&1023)+56320)}function te(e,t,n){t===`__proto__`?Object.defineProperty(e,t,{configurable:!0,enumerable:!0,writable:!0,value:n}):e[t]=n}var M=Array(256),N=Array(256);for(let e=0;e<256;e++)M[e]=+!!A(e),N[e]=A(e);function ne(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||a,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.maxDepth=typeof t.maxDepth==`number`?t.maxDepth:100,this.maxMergeSeqLength=typeof t.maxMergeSeqLength==`number`?t.maxMergeSeqLength:20,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.depth=0,this.firstTabInLine=-1,this.documents=[],this.anchorMapTransactions=[]}function P(e,t){let n={name:e.filename,buffer:e.input.slice(0,-1),position:e.position,line:e.line,column:e.position-e.lineStart};return n.snippet=i(n),new r(t,n)}function F(e,t){throw P(e,t)}function I(e,t){e.onWarning&&e.onWarning.call(null,P(e,t))}function L(e,t,n){let r=e.anchorMapTransactions;if(r.length!==0){let n=r[r.length-1];o.call(n,t)||(n[t]={existed:o.call(e.anchorMap,t),value:e.anchorMap[t]})}e.anchorMap[t]=n}function R(e){e.anchorMapTransactions.push(Object.create(null))}function re(e){let t=e.anchorMapTransactions.pop(),n=e.anchorMapTransactions;if(n.length===0)return;let r=n[n.length-1],i=Object.keys(t);for(let e=0,n=i.length;e<n;e+=1){let n=i[e];o.call(r,n)||(r[n]=t[n])}}function z(e){let t=e.anchorMapTransactions.pop(),n=Object.keys(t);for(let r=n.length-1;r>=0;--r){let i=t[n[r]];i.existed?e.anchorMap[n[r]]=i.value:delete e.anchorMap[n[r]]}}function B(e){return{position:e.position,line:e.line,lineStart:e.lineStart,lineIndent:e.lineIndent,firstTabInLine:e.firstTabInLine,tag:e.tag,anchor:e.anchor,kind:e.kind,result:e.result}}function V(e,t){e.position=t.position,e.line=t.line,e.lineStart=t.lineStart,e.lineIndent=t.lineIndent,e.firstTabInLine=t.firstTabInLine,e.tag=t.tag,e.anchor=t.anchor,e.kind=t.kind,e.result=t.result}var H={YAML:function(e,t,n){e.version!==null&&F(e,`duplication of %YAML directive`),n.length!==1&&F(e,`YAML directive accepts exactly one argument`);let r=/^([0-9]+)\.([0-9]+)$/.exec(n[0]);r===null&&F(e,`ill-formed argument of the YAML directive`);let i=parseInt(r[1],10),a=parseInt(r[2],10);i!==1&&F(e,`unacceptable YAML version of the document`),e.version=n[0],e.checkLineBreaks=a<2,a!==1&&a!==2&&I(e,`unsupported YAML version of the document`)},TAG:function(e,t,n){let r;n.length!==2&&F(e,`TAG directive accepts exactly two arguments`);let i=n[0];r=n[1],b.test(i)||F(e,`ill-formed tag handle (first argument) of the TAG directive`),o.call(e.tagMap,i)&&F(e,`there is a previously declared suffix for "`+i+`" tag handle`),x.test(r)||F(e,`ill-formed tag prefix (second argument) of the TAG directive`);try{r=decodeURIComponent(r)}catch(t){F(e,`tag prefix is malformed: `+r)}e.tagMap[i]=r}};function U(e,t,n,r){if(t<n){let i=e.input.slice(t,n);if(r)for(let t=0,n=i.length;t<n;t+=1){let n=i.charCodeAt(t);n===9||n>=32&&n<=1114111||F(e,`expected valid JSON character`)}else _.test(i)&&F(e,`the stream contains non-printable characters`);e.result+=i}}function W(e,t,r,i){n.isObject(r)||F(e,`cannot merge mappings; the provided source object is unacceptable`);let a=Object.keys(r);for(let e=0,n=a.length;e<n;e+=1){let n=a[e];o.call(t,n)||(te(t,n,r[n]),i[n]=!0)}}function G(e,t,n,r,i,a,s,c,l){if(Array.isArray(i)){i=Array.prototype.slice.call(i);for(let t=0,n=i.length;t<n;t+=1)Array.isArray(i[t])&&F(e,`nested arrays are not supported inside keys`),typeof i==`object`&&S(i[t])===`[object Object]`&&(i[t]=`[object Object]`)}if(typeof i==`object`&&S(i)===`[object Object]`&&(i=`[object Object]`),i=String(i),t===null&&(t={}),r===`tag:yaml.org,2002:merge`)if(Array.isArray(a)){a.length>e.maxMergeSeqLength&&F(e,`merge sequence length exceeded maxMergeSeqLength (`+e.maxMergeSeqLength+`)`);let r=new Set;for(let i=0,o=a.length;i<o;i+=1){let o=a[i];r.has(o)||(r.add(o),W(e,t,o,n))}}else W(e,t,a,n);else !e.json&&!o.call(n,i)&&o.call(t,i)&&(e.line=s||e.line,e.lineStart=c||e.lineStart,e.position=l||e.position,F(e,`duplicated mapping key`)),te(t,i,a),delete n[i];return t}function K(e){let t=e.input.charCodeAt(e.position);t===10?e.position++:t===13?(e.position++,e.input.charCodeAt(e.position)===10&&e.position++):F(e,`a line break is expected`),e.line+=1,e.lineStart=e.position,e.firstTabInLine=-1}function q(e,t,n){let r=0,i=e.input.charCodeAt(e.position);for(;i!==0;){for(;w(i);)i===9&&e.firstTabInLine===-1&&(e.firstTabInLine=e.position),i=e.input.charCodeAt(++e.position);if(t&&i===35)d Line truncated
`,t-1))}function ie(e,t,n){let r,i,a,o,s,c,l=e.kind,u=e.result,d=e.input.charCodeAt(e.position);if(T(d)||E(d)||d===35||d===38||d===42||d===33||d===124||d===62||d===39||d===34||d===37||d===64||d===96)return!1;if(d===63||d===45){let t=e.input.charCodeAt(e.position+1);if(T(t)||n&&E(t))return!1}for(e.kind=`scalar`,e.result=``,r=i=e.position,a=!1;d!==0;){if(d===58){let t=e.input.charCodeAt(e.position+1);if(T(t)||n&&E(t))break}else if(d===35){if(T(e.input.charCodeAt(e.position-1)))break}else if(e.position===e.lineStart&&J(e)||n&&E(d))break;else if(C(d))if(o=e.line,s=e.lineStart,c=e.lineIndent,q(e,!1,-1),e.lineIndent>=t){a=!0,d=e.input.charCodeAt(e.position);continue}else{e.position=i,e.line=o,e.lineStart=s,e.lineIndent=c;break}a&&(U(e,r,i,!1),Y(e,e.line-o),r=i=e.position,a=!1),w(d)||(i=e.position+1),d=e.input.charCodeAt(++e.position)}return U(e,r,i,!1),e.result?!0:(e.kind=l,e.result=u,!1)}function X(e,t){let n,r,i=e.input.charCodeAt(e.position);if(i!==39)return!1;for(e.kind=`scalar`,e.result=``,e.position++,n=r=e.position;(i=e.input.charCodeAt(e.position))!==0;)if(i===39)if(U(e,n,e.position,!0),i=e.input.charCodeAt(++e.position),i===39)n=e.position,e.position++,r=e.position;else return!0;else C(i)?(U(e,n,r,!0),Y(e,q(e,!1,t)),n=r=e.position):e.position===e.lineStart&&J(e)?F(e,`unexpected end of the document within a single quoted scalar`):(e.position++,w(i)||(r=e.position));F(e,`unexpected end of the stream within a single quoted scalar`)}function ae(e,t){let n,r,i,a=e.input.charCodeAt(e.position);if(a!==34)return!1;for(e.kind=`scalar`,e.result=``,e.position++,n=r=e.position;(a=e.input.charCodeAt(e.position))!==0;)if(a===34)return U(e,n,e.position,!0),e.position++,!0;else if(a===92){if(U(e,n,e.position,!0),a=e.input.charCodeAt(++e.position),C(a))q(e,!1,t);else if(a<256&&M[a])e.result+=N[a],e.position++;else if((i=O(a))>0){let t=i,n=0;for(;t>0;t--)a=e.input.charCodeAt(++e.position),(i=D(a))>=0?n=(n<<4)+i:F(e,`expected hexadecimal character`);e.result+=j(n),e.position++}else F(e,`unknown escape sequence`);n=r=e.position}else C(a)?(U(e,n,r,!0),Y(e,q(e,!1,t)),n=r=e.position):e.position===e.lineStart&&J(e)?F(e,`unexpected end of the document within a double quoted scalar`):(e.position++,w(a)||(r=e.position));F(e,`unexpected end of the stream within a double quoted scalar`)}function oe(e,t){let n=!0,r,i,a,o=e.tag,c,l=e.anchor,u,d,f,p,m=Object.create(null),h,g,_,v=e.input.charCodeAt(e.position);if(v===91)u=93,p=!1,c=[];else if(v===123)u=125,p=!0,c={};else return!1;for(e.anchor!==null&&L(e,e.anchor,c),v=e.input.charCodeAt(++e.position);v!==0;){if(q(e,!0,t),v=e.input.charCodeAt(e.position),v===u)return e.position++,e.tag=o,e.anchor=l,e.kind=p?`mapping`:`sequence`,e.result=c,!0;n?v===44&&F(e,`expected the node content, but found ','`):F(e,`missed comma between flow collection entries`),g=h=_=null,d=f=!1,v===63&&T(e.input.charCodeAt(e.position+1))&&(d=f=!0,e.position++,q(e,!0,t)),r=e.line,i=e.lineStart,a=e.position,Q(e,t,s,!1,!0),g=e.tag,h=e.result,q(e,!0,t),v=e.input.charCodeAt(e.position),(f||e.line===r)&&v===58&&(d=!0,v=e.input.charCodeAt(++e.position),q(e,!0,t),Q(e,t,s,!1,!0),_=e.result),p?G(e,c,m,g,h,_,r,i,a):d?c.push(G(e,null,m,g,h,_,r,i,a)):c.push(h),q(e,!0,t),v=e.input.charCodeAt(e.position),v===44?(n=!0,v=e.input.charCodeAt(++e.position)):n=!1}F(e,`unexpected end of the stream within a flow collection`)}function Z(e,t){let r,i=m,a=!1,o=!1,s=t,c=0,l=!1,u,d=e.input.charCodeAt(e.position);if(d===124)r=!1;else if(d===62)r=!0;else return!1;for(e.kind=`scalar`,e.result=``;d!==0;)if(d=e.input.charCodeAt(++e.position),d===43||d===45)m===i?i=d===43?g:h:F(e,`repeat of a chomping mode identifier`);else if((u=k(d))>=0)u===0?F(e,`bad explicit indentation width of a block scalar; it cannot be less than one`):o?F(e,`repeat of an indentation width identifier`):(s=t+u-1,o=!0);else break;if(w(d)){do d=e.input.charCodeAt(++e.position);while(w(d));if(d===35)do d=e.input.charCodeAt(++e.position);while(!C(d)&&d!==0)}for(;d!==0;){for(K(e),e.lineIndent=0,d=e.input.charCodeAt(e.position);(!o||e.lineIndent<s)&&d===32;)e.lineIndent++,d=e.input.charCodeAt(++e.position);if(!o&&e.lineIndent>s&&(s=e.lineIndent),C(d)){c++;continue}if(!o&&s===0&&F(e,`missing indentation for block scalar`),e.lineIndent<s){i===g?e.result+=n.repeat(`
`,a?1+c:c):i===m&&a&&(e.result+=`
`);break}r?w(d)?(l=!0,e.result+=n.repeat(`
`,a?1+c:c)):l?(l=!1,e.result+=n.repeat(`
`,c+1)):c===0?a&&(e.result+=` `):e.result+=n.repeat(`
`,c):e.result+=n.repeat(`
`,a?1+c:c),a=!0,o=!0,c=0;let t=e.position;for(;!C(d)&&d!==0;)d=e.input.charCodeAt(++e.position);U(e,t,e.position,!1)}return!0}function se(e,t){let n=e.tag,r=e.anchor,i=[],a=!1;if(e.firstTabInLine!==-1)return!1;e.anchor!==null&&L(e,e.anchor,i);let o=e.input.charCodeAt(e.position);for(;o!==0&&(e.firstTabInLine!==-1&&(e.position=e.firstTabInLine,F(e,`tab characters must not be used in indentation`)),!(o!==45||!T(e.input.charCodeAt(e.position+1))));){if(a=!0,e.position++,q(e,!0,-1)&&e.lineIndent<=t){i.push(null),o=e.input.charCodeAt(e.position);continue}let n=e.line;if(Q(e,t,l,!1,!0),i.push(e.result),q(e,!0,-1),o=e.input.charCodeAt(e.position),(e.line===n||e.lineIndent>t)&&o!==0)F(e,`bad indentation of a sequence entry`);else if(e.lineIndent<t)break}return a?(e.tag=n,e.anchor=r,e.kind=`sequence`,e.result=i,!0):!1}function ce(e,t,n){let r,i,a,o,s=e.tag,l=e.anchor,u={},d=Object.create(null),f=null,m=null,h=null,g=!1,_=!1;if(e.firstTabInLine!==-1)return!1;e.anchor!==null&&L(e,e.anchor,u);let v=e.input.charCodeAt(e.position);for(;v!==0;){!g&&e.firstTabInLine!==-1&&(e.position=e.firstTabInLine,F(e,`tab characters must not be used in indentation`));let y=e.input.charCodeAt(e.position+1),b=e.line;if((v===63||v===58)&&T(y))v===63?(g&&(G(e,u,d,f,m,null,i,a,o),f=m=h=null),_=!0,g=!0,r=!0):g?(g=!1,r=!0):F(e,`incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line`),e.position+=1,v=y;else{if(i=e.line,a=e.lineStart,o=e.position,!Q(e,n,c,!1,!0))break;if(e.line===b){for(v=e.input.charCodeAt(e.position);w(v);)v=e.input.charCodeAt(++e.position);if(v===58)v=e.input.charCodeAt(++e.position),T(v)||F(e,`a whitespace character is expected after the key-value separator within a block mapping`),g&&(G(e,u,d,f,m,null,i,a,o),f=m=h=null),_=!0,g=!1,r=!1,f=e.tag,m=e.result;else if(_)F(e,`can not read an implicit mapping pair; a colon is missed`);else return e.tag=s,e.anchor=l,!0}else if(_)F(e,`can not read a block mapping entry; a multiline key may not be an implicit key`);else return e.tag=s,e.anchor=l,!0}if((e.line===b||e.lineIndent>t)&&(g&&(i=e.line,a=e.lineStart,o=e.position),Q(e,t,p,!0,r)&&(g?m=e.result:h=e.result),g||(G(e,u,d,f,m,h,i,a,o),f=m=h=null),q(e,!0,-1),v=e.input.charCodeAt(e.position)),(e.line===b||e.lineIndent>t)&&v!==0)F(e,`bad indentation of a mapping entry`);else if(e.lineIndent<t)break}return g&&G(e,u,d,f,m,null,i,a,o),_&&(e.tag=s,e.anchor=l,e.kind=`mapping`,e.result=u),_}function le(e){let t=!1,n=!1,r,i,a=e.input.charCodeAt(e.position);if(a!==33)return!1;e.tag!==null&&F(e,`duplication of a tag property`),a=e.input.charCodeAt(++e.position),a===60?(t=!0,a=e.input.charCodeAt(++e.position)):a===33?(n=!0,r=`!!`,a=e.input.charCodeAt(++e.position)):r=`!`;let s=e.position;if(t){do a=e.input.charCodeAt(++e.position);while(a!==0&&a!==62);e.position<e.length?(i=e.input.slice(s,e.position),a=e.input.charCodeAt(++e.position)):F(e,`unexpected end of the stream within a verbatim tag`)}else{for(;a!==0&&!T(a);)a===33&&(n?F(e,`tag suffix cannot contain exclamation marks`):(r=e.input.slice(s-1,e.position+1),b.test(r)||F(e,`named tag handle cannot contain such characters`),n=!0,s=e.position+1)),a=e.input.charCodeAt(++e.position);i=e.input.slice(s,e.position),y.test(i)&&F(e,`tag suffix cannot contain flow indicator characters`)}i&&!x.test(i)&&F(e,`tag name cannot contain such characters: `+i);try{i=decodeURIComponent(i)}catch(t){F(e,`tag name is malformed: `+i)}return t?e.tag=i:o.call(e.tagMap,r)?e.tag=e.tagMap[r]+i:r===`!`?e.tag=`!`+i:r===`!!`?e.tag=`tag:yaml.org,2002:`+i:F(e,`undeclared tag handle "`+r+`"`),!0}function ue(e){let t=e.input.charCodeAt(e.position);if(t!==38)return!1;e.anchor!==null&&F(e,`duplication of an anchor property`),t=e.input.charCodeAt(++e.position);let n=e.position;for(;t!==0&&!T(t)&&!E(t);)t=e.input.charCodeAt(++e.position);return e.position===n&&F(e,`name of an anchor node must contain at least one character`),e.anchor=e.input.slice(n,e.position),!0}function de(e){let t=e.input.charCodeAt(e.position);if(t!==42)return!1;t=e.input.charCodeAt(++e.position);let n=e.position;for(;t!==0&&!T(t)&&!E(t);)t=e.input.charCodeAt(++e.position);e.position===n&&F(e,`name of an alias node must contain at least one character`);let r=e.input.slice(n,e.position);return o.call(e.anchorMap,r)||F(e,`unidentified alias "`+r+`"`),e.result=e.anchorMap[r],q(e,!0,-1),!0}function fe(e,t,n,r){let i=B(e);return R(e),V(e,t),e.tag=null,e.anchor=null,e.kind=null,e.result=null,ce(e,n,r)&&e.kind===`mapping`?(re(e),!0):(z(e),V(e,i),!1)}function Q(e,t,n,r,i){let a,u,d=1,f=!1,m=!1,h=null,g,_,v;e.depth>=e.maxDepth&&F(e,`nesting exceeded maxDepth (`+e.maxDepth+`)`),e.depth+=1,e.listener!==null&&e.listener(`open`,e),e.tag=null,e.anchor=null,e.kind=null,e.result=null;let y=a=u=p===n||l===n;if(r&&q(e,!0,-1)&&(f=!0,e.lineIndent>t?d=1:e.lineIndent===t?d=0:e.lineIndent<t&&(d=-1)),d===1)for(;;){let n=e.input.charCodeAt(e.position),r=B(e);if(f&&(n===33&&e.tag!==null||n===38&&e.anchor!==null)||!le(e)&&!ue(e))break;h===nul Line truncated
`),e.charCodeAt(0)===65279&&(e=e.slice(1)));let n=new ne(e,t),r=e.indexOf(`\0`);for(r!==-1&&(n.position=r,F(n,`null byte is not allowed in input`)),n.input+=`\0`;n.input.charCodeAt(n.position)===32;)n.lineIndent+=1,n.position+=1;for(;n.position<n.length-1;)pe(n);return n.documents}function he(e,t,n){typeof t==`object`&&t&&n===void 0&&(n=t,t=null);let r=me(e,n);if(typeof t!=`function`)return r;for(let e=0,n=r.length;e<n;e+=1)t(r[e])}function ge(e,t){let n=me(e,t);if(n.length!==0){if(n.length===1)return n[0];throw new r(`expected a single document in the stream, but found more`)}}t.exports.loadAll=he,t.exports.load=ge})),te=s(((e,t)=>{var n=u(),r=d(),i=ee(),a=Object.prototype.toString,o=Object.prototype.hasOwnProperty,s=65279,c=9,l=10,f=13,p=32,m=33,h=34,g=35,_=37,v=38,y=39,b=42,x=44,S=45,C=58,w=61,T=62,E=63,D=64,O=91,k=93,A=96,j=123,te=124,M=125,N={};N[0]=`\\0`,N[7]=`\\a`,N[8]=`\\b`,N[9]=`\\t`,N[10]=`\\n`,N[11]=`\\v`,N[12]=`\\f`,N[13]=`\\r`,N[27]=`\\e`,N[34]=`\\"`,N[92]=`\\\\`,N[133]=`\\N`,N[160]=`\\_`,N[8232]=`\\L`,N[8233]=`\\P`;var ne=[`y`,`Y`,`yes`,`Yes`,`YES`,`on`,`On`,`ON`,`n`,`N`,`no`,`No`,`NO`,`off`,`Off`,`OFF`],P=/^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/;function F(e,t){if(t===null)return{};let n={},r=Object.keys(t);for(let i=0,a=r.length;i<a;i+=1){let a=r[i],s=String(t[a]);a.slice(0,2)===`!!`&&(a=`tag:yaml.org,2002:`+a.slice(2));let c=e.compiledTypeMap.fallback[a];c&&o.call(c.styleAliases,s)&&(s=c.styleAliases[s]),n[a]=s}return n}function I(e){let t,i,a=e.toString(16).toUpperCase();if(e<=255)t=`x`,i=2;else if(e<=65535)t=`u`,i=4;else if(e<=4294967295)t=`U`,i=8;else throw new r(`code point within a string may not be greater than 0xFFFFFFFF`);return`\\`+t+n.repeat(`0`,i-a.length)+a}var L=1,R=2;function re(e){this.schema=e.schema||i,this.indent=Math.max(1,e.indent||2),this.noArrayIndent=e.noArrayIndent||!1,this.skipInvalid=e.skipInvalid||!1,this.flowLevel=n.isNothing(e.flowLevel)?-1:e.flowLevel,this.styleMap=F(this.schema,e.styles||null),this.sortKeys=e.sortKeys||!1,this.lineWidth=e.lineWidth||80,this.noRefs=e.noRefs||!1,this.noCompatMode=e.noCompatMode||!1,this.condenseFlow=e.condenseFlow||!1,this.quotingType=e.quotingType===`"`?R:L,this.forceQuotes=e.forceQuotes||!1,this.replacer=typeof e.replacer==`function`?e.replacer:null,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result=``,this.duplicates=[],this.usedDuplicates=null}function z(e,t){let r=n.repeat(` `,t),i=0,a=``,o=e.length;for(;i<o;){let t,n=e.indexOf(`
`,i);n===-1?(t=e.slice(i),i=o):(t=e.slice(i,n+1),i=n+1),t.length&&t!==`
`&&(a+=r),a+=t}return a}function B(e,t){return`
`+n.repeat(` `,e.indent*t)}function V(e,t){for(let n=0,r=e.implicitTypes.length;n<r;n+=1)if(e.implicitTypes[n].resolve(t))return!0;return!1}function H(e){return e===p||e===c}function U(e){return e>=32&&e<=126||e>=161&&e<=55295&&e!==8232&&e!==8233||e>=57344&&e<=65533&&e!==s||e>=65536&&e<=1114111}function W(e){return U(e)&&e!==s&&e!==f&&e!==l}function G(e,t,n){let r=W(e),i=r&&!H(e);return(n?r:r&&e!==x&&e!==O&&e!==k&&e!==j&&e!==M)&&e!==g&&!(t===C&&!i)||W(t)&&!H(t)&&e===g||t===C&&i}function K(e){return U(e)&&e!==s&&!H(e)&&e!==S&&e!==E&&e!==C&&e!==x&&e!==O&&e!==k&&e!==j&&e!==M&&e!==g&&e!==v&&e!==b&&e!==m&&e!==te&&e!==w&&e!==T&&e!==y&&e!==h&&e!==_&&e!==D&&e!==A}function q(e){return!H(e)&&e!==C}function J(e,t){let n=e.charCodeAt(t),r;return n>=55296&&n<=56319&&t+1<e.length&&(r=e.charCodeAt(t+1),r>=56320&&r<=57343)?(n-55296)*1024+r-56320+65536:n}function Y(e){return/^\n* /.test(e)}var ie=1,X=2,ae=3,oe=4,Z=5;function se(e,t,n,r,i,a,o,s){let c,u=0,d=null,f=!1,p=!1,m=r!==-1,h=-1,g=K(J(e,0))&&q(J(e,e.length-1));if(t||o)for(c=0;c<e.length;u>=65536?c+=2:c++){if(u=J(e,c),!U(u))return Z;g=g&&G(u,d,s),d=u}else{for(c=0;c<e.length;u>=65536?c+=2:c++){if(u=J(e,c),u===l)f=!0,m&&(p=p||c-h-1>r&&e[h+1]!==` `,h=c);else if(!U(u))return Z;g=g&&G(u,d,s),d=u}p=p||m&&c-h-1>r&&e[h+1]!==` `}return!f&&!p?g&&!o&&!i(e)?ie:a===R?Z:X:n>9&&Y(e)?Z:o?a===R?Z:X:p?oe:ae}function ce(e,t,n,i,a){e.dump=function(){if(t.length===0)return e.quotingType===R?`""`:`''`;if(!e.noCompatMode&&(ne.indexOf(t)!==-1||P.test(t)))return e.quotingType===R?`"`+t+`"`:`'`+t+`'`;let o=e.indent*Math.max(1,n),s=e.lineWidth===-1?-1:Math.max(Math.min(e.lineWidth,40),e.lineWidth-o),c=i||e.flowLevel>-1&&n>=e.flowLevel;function l(t){return V(e,t)}switch(se(t,c,e.indent,s,l,e.quotingType,e.forceQuotes&&!i,a)){case ie:return t;case X:return`'`+t.replace(/'/g,`''`)+`'`;case ae:return`|`+le(t,e.indent)+ue(z(t,o));case oe:return`>`+le(t,e.indent)+ue(z(de(t,s),o));case Z:return`"`+Q(t,s)+`"`;default:throw new r(`impossible error: invalid scalar style`)}}()}function le(e,t){let n=Y(e)?String(t):``,r=e[e.length-1]===`
`;return n+(r&&(e[e.length-2]===`
`||e===`
`)?`+`:r?``:`-`)+`
`}function ue(e){return e[e.length-1]===`
`?e.slice(0,-1):e}function de(e,t){let n=/(\n+)([^\n]*)/g,r=function(){let r=e.indexOf(`
`);return r=r===-1?e.length:r,n.lastIndex=r,fe(e.slice(0,r),t)}(),i=e[0]===`
`||e[0]===` `,a,o;for(;o=n.exec(e);){let e=o[1],n=o[2];a=n[0]===` `,r+=e+(!i&&!a&&n!==``?`
`:``)+fe(n,t),i=a}return r}function fe(e,t){if(e===``||e[0]===` `)return e;let n=/ [^ ]/g,r,i=0,a,o=0,s=0,c=``;for(;r=n.exec(e);)s=r.index,s-i>t&&(a=o>i?o:s,c+=`
`+e.slice(i,a),i=a+1),o=s;return c+=`
`,e.length-i>t&&o>i?c+=e.slice(i,o)+`
`+e.slice(o+1):c+=e.slice(i),c.slice(1)}function Q(e){let t=``,n=0;for(let r=0;r<e.length;n>=65536?r+=2:r++){n=J(e,r);let i=N[n];!i&&U(n)?(t+=e[r],n>=65536&&(t+=e[r+1])):t+=i||I(n)}return t}function pe(e,t,n){let r=``,i=e.tag;for(let i=0,a=n.length;i<a;i+=1){let a=n[i];e.replacer&&(a=e.replacer.call(n,String(i),a)),($(e,t,a,!1,!1)||a===void 0&&$(e,t,null,!1,!1))&&(r!==``&&(r+=`,`+(e.condenseFlow?``:` `)),r+=e.dump)}e.tag=i,e.dump=`[`+r+`]`}function me(e,t,n,r){let i=``,a=e.tag;for(let a=0,o=n.length;a<o;a+=1){let o=n[a];e.replacer&&(o=e.replacer.call(n,String(a),o)),($(e,t+1,o,!0,!0,!1,!0)||o===void 0&&$(e,t+1,null,!0,!0,!1,!0))&&((!r||i!==``)&&(i+=B(e,t)),e.dump&&l===e.dump.charCodeAt(0)?i+=`-`:i+=`- `,i+=e.dump)}e.tag=a,e.dump=i||`[]`}function he(e,t,n){let r=``,i=e.tag,a=Object.keys(n);for(let i=0,o=a.length;i<o;i+=1){let o=``;r!==``&&(o+=`, `),e.condenseFlow&&(o+=`"`);let s=a[i],c=n[s];e.replacer&&(c=e.replacer.call(n,s,c)),$(e,t,s,!1,!1)&&(e.dump.length>1024&&(o+=`? `),o+=e.dump+(e.condenseFlow?`"`:``)+`:`+(e.condenseFlow?``:` `),$(e,t,c,!1,!1)&&(o+=e.dump,r+=o))}e.tag=i,e.dump=`{`+r+`}`}function ge(e,t,n,i){let a=``,o=e.tag,s=Object.keys(n);if(e.sortKeys===!0)s.sort();else if(typeof e.sortKeys==`function`)s.sort(e.sortKeys);else if(e.sortKeys)throw new r(`sortKeys must be a boolean or a function`);for(let r=0,o=s.length;r<o;r+=1){let o=``;(!i||a!==``)&&(o+=B(e,t));let c=s[r],u=n[c];if(e.replacer&&(u=e.replacer.call(n,c,u)),!$(e,t+1,c,!0,!0,!0))continue;let d=e.tag!==null&&e.tag!==`?`||e.dump&&e.dump.length>1024;d&&(e.dump&&l===e.dump.charCodeAt(0)?o+=`?`:o+=`? `),o+=e.dump,d&&(o+=B(e,t)),$(e,t+1,u,!0,d)&&(e.dump&&l===e.dump.charCodeAt(0)?o+=`:`:o+=`: `,o+=e.dump,a+=o)}e.tag=o,e.dump=a||`{}`}function _e(e,t,n){let i=n?e.explicitTypes:e.implicitTypes;for(let s=0,c=i.length;s<c;s+=1){let c=i[s];if((c.instanceOf||c.predicate)&&(!c.instanceOf||typeof t==`object`&&t instanceof c.instanceOf)&&(!c.predicate||c.predicate(t))){if(n?c.multi&&c.representName?e.tag=c.representName(t):e.tag=c.tag:e.tag=`?`,c.represent){let n=e.styleMap[c.tag]||c.defaultStyle,i;if(a.call(c.represent)===`[object Function]`)i=c.represent(t,n);else if(o.call(c.represent,n))i=c.represent[n](t,n);else throw new r(`!<`+c.tag+`> tag resolver accepts not "`+n+`" style`);e.dump=i}return!0}}return!1}function $(e,t,n,i,o,s,c){e.tag=null,e.dump=n,_e(e,n,!1)||_e(e,n,!0);let l=a.call(e.dump),u=i;i&&(i=e.flowLevel<0||e.flowLevel>t);let d=l===`[object Object]`||l===`[object Array]`,f,p;if(d&&(f=e.duplicates.indexOf(n),p=f!==-1),(e.tag!==null&&e.tag!==`?`||p||e.indent!==2&&t>0)&&(o=!1),p&&e.usedDuplicates[f])e.dump=`*ref_`+f;else{if(d&&p&&!e.usedDuplicates[f]&&(e.usedDuplicates[f]=!0),l===`[object Object]`)i&&Object.keys(e.dump).length!==0?(ge(e,t,e.dump,o),p&&(e.dump=`&ref_`+f+e.dump)):(he(e,t,e.dump),p&&(e.dump=`&ref_`+f+` `+e.dump));else if(l===`[object Array]`)i&&e.dump.length!==0?(e.noArrayIndent&&!c&&t>0?me(e,t-1,e.dump,o):me(e,t,e.dump,o),p&&(e.dump=`&ref_`+f+e.dump)):(pe(e,t,e.dump),p&&(e.dump=`&ref_`+f+` `+e.dump));else if(l===`[object String]`)e.tag!==`?`&&ce(e,e.dump,t,s,u);else if(l===`[object Undefined]`)return!1;else{if(e.skipInvalid)return!1;throw new r(`unacceptable kind of an object to dump `+l)}if(e.tag!==null&&e.tag!==`?`){let t=encodeURI(e.tag[0]===`!`?e.tag.slice(1):e.tag).replace(/!/g,`%21`);t=e.tag[0]===`!`?`!`+t:t.slice(0,18)===`tag:yaml.org,2002:`?`!!`+t.slice(18):`!<`+t+`>`,e.dump=t+` `+e.dump}}return!0}function ve(e,t){let n=[],r=[];ye(e,n,r);let i=r.length;for(let e=0;e<i;e+=1)t.duplicates.push(n[r[e]]);t.usedDuplicates=Array(i)}function ye(e,t,n){if(typeof e==`object`&&e){let r=t.indexOf(e);if(r!==-1)n.indexOf(r)===-1&&n.push(r);else if(t.push(e),Array.isArray(e))for(let r=0,i=e.length;r<i;r+=1)ye(e[r],t,n);else{let r=Object.keys(e);for(let i=0,a=r.length;i<a;i+=1)ye(e[r[i]],t,n)}}}function be(e,t){t=t||{};let n=new re(t);n.noRefs||ve(e,n);let r=e;return n.replacer&&(r=n.replacer.call({"":r},``,r)),$(n,0,r,!0,!0)?n.dump+`
`:``}t.exports.dump=be})),M=l(s(((e,t)=>{var n=j(),r=te();function i(e,t){return function(){throw Error(`Function yaml.`+e+` is removed in js-yaml 4. Use yaml.`+t+` instead, which is now safe by default.`)}}t.exports.Type=p(),t.exports.Schema=m(),t.exports.FAILSAFE_SCHEMA=v(),t.exports.JSON_SCHEMA=C(),t.exports.CORE_SCHEMA=w(),t.exports.DEFAULT_SCHEMA=ee(),t.exports.load=n.load,t.exports.loadAll=n.loadAll,t.exports.dump=r.dump,t.exports.YAMLException=d(),t.exports.types={binary:D(),float:S(),map:_(),null:y(),pairs:k(),set:A(),timestamp:T(),bool:b(),int:x(),merge:E(),omap:O(),seq:g(),str:h()},t.exports.safeLoad=i(`safeLoad`,`load`),t.exports.safeLoadAll=i(`safeLoadAll`,`loadAll`),t.exports.safeDump=i(`safeDump`,`dump`)}))(),1),{Type:N,Schema:ne,FAILSAFE_SCHEMA:P,JSON_SCHEMA:F,CORE_SCHEMA:I,DEFAULT_SCHEMA:L,load:R,loadAll:re,dump:z,YAMLException:B,types:V,safeLoad:H,safeLoadAll:U,safeDump:W}=M.default,G=M.default;e.CORE_SCHEMA=I,e.DEFAULT_SCHEMA=L,e.FAILSAFE_SCHEMA=P,e.JSON_SCHEMA=F,e.Schema=ne,e.Type=N,e.YAMLException=B,e.default=G,e.dump=z,e.load=R,e.loadAll=re,e.safeDump=W,e.safeLoad=H,e.safeLoadAll=U,e.types=V});
//# sourceMappingURL=js-yaml.min.js.map
File diff suppressed because it is too large. Load diff
-364
View File
@@ -1,364 +0,0 @@
msgid "HONK"
msgstr "HONK"
msgid "RUNNING"
msgstr "运行中"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "Collecting data..."
msgstr "收集数据..."
msgid "Enabled"
msgstr "启用"
msgid "Reload Service"
msgstr "重载服务"
msgid "Global Settings"
msgstr "全局设置"
msgid "DNS Settings"
msgstr "DNS 设置"
msgid "Node Settings"
msgstr "节点设置"
msgid "Routing Settings"
msgstr "路由设置"
msgid "Global Configuration"
msgstr "全局配置"
msgid "DNS Configuration"
msgstr "DNS 配置"
msgid "Node Configuration"
msgstr "节点配置"
msgid "Route Configuration"
msgstr "路由配置"
msgid "Configure global settings for HONK."
msgstr "调整 HONK 的全局设置。"
msgid "Configure DNS settings for HONK."
msgstr "调整 HONK 的 DNS 设置。"
msgid "Configure nodes and groups for HONK."
msgstr "调整 HONK 的节点与分组设置。"
msgid "Configure routing rules for HONK."
msgstr "调整 HONK 的路由规则。"
msgid "Correctly configure the include field for separate-config to work, or enter complete configuration here."
msgstr "请正确配置 include 字段以启用拆分配置,或在此处填写完整配置。"
msgid "Logs"
msgstr "日志"
msgid "Memory Usage"
msgstr "内存占用"
msgid "Format Code"
msgstr "格式化代码"
msgid "Clear logs"
msgstr "清空日志"
msgid "Logs cleared successfully."
msgstr "日志已清空。"
msgid "Failed to clear logs:"
msgstr "清空日志失败:"
msgid "Configuration applied and service reloaded."
msgstr "配置已生效并重载服务。"
msgid "Status"
msgstr "状态"
msgid "Running Status"
msgstr "运行状态"
msgid "Formatted"
msgstr "已格式化"
msgid "Failed to format code:"
msgstr "格式化代码失败:"
msgid "Checking Dashboard and API configuration..."
msgstr "正在检测控制面板与 API 配置..."
msgid "Enabling Native API and restarting HONK..."
msgstr "正在启用 Native API 并重启 HONK..."
msgid "One-click Enable Default Native API"
msgstr "一键启用默认 Native API 配置"
msgid "Successfully enabled! Restarting service and initializing dashboard..."
msgstr "已成功启用!正在重启服务并初始化面板..."
msgid "Failed to enable:"
msgstr "启用失败:"
msgid "Unknown error"
msgstr "未知错误"
msgid "Dashboard / Native API Unconfigured"
msgstr "控制面板 / Native API 未配置"
msgid "HONK has not enabled Native API in its configuration file. Dashboard requires native API listener and dashboard UI path."
msgstr "HONK 尚未在配置文件中启用 Native API。控制面板需要依赖 Native API 提供的监听端口与面板静态文件路径。"
msgid "Please configure experimental.native_api block in API Settings, and ensure listen and ui are configured."
msgstr "请在【API 设置】的配置文件中配置 experimental.native_api 语法块,并确保配置了 listen 和 ui。"
msgid "Example Configuration (/etc/honk/config.d/api.dae):"
msgstr "参考配置示例(/etc/honk/config.d/api.dae):"
msgid "Configure in API Settings"
msgstr "前往【API 设置】手动配置"
msgid "(Not set)"
msgstr "(未设置)"
msgid "Preparing download..."
msgstr "准备下载..."
msgid "Please enter a valid download URL"
msgstr "请输入有效的下载 URL"
msgid "Processing..."
msgstr "正在处理..."
msgid "Start Download & Install Dashboard"
msgstr "开始下载并安装控制面板"
msgid "%s UI Files Not Found"
msgstr "未检测到 %s 面板文件"
msgid "Listen Address & Port"
msgstr "监听地址与端口"
msgid "API Secret"
msgstr "API 认证密钥"
msgid "Download Source:"
msgstr "下载源:"
msgid "Custom URL"
msgstr "自定义 URL"
msgid "HTTPS access detected:"
msgstr "检测到当前通过 HTTPS 访问 LuCI:"
msgid "Modern browsers may block HTTP iframe resources under HTTPS. If the dashboard fails to display, open it in a new tab."
msgstr "现代浏览器可能会阻止直接加载 HTTP 协议的控制面板。如无法正常显示,请点击右侧按钮在新标签页打开。"
msgid "Open in New Tab"
msgstr "在新标签页打开"
msgid "HONK service is currently not running:"
msgstr "HONK 服务当前未运行:"
msgid "Dashboard API port ("
msgstr "Dashboard API 端口("
msgid ") is not listening. Start the service to display data."
msgstr ")尚未监听。启动服务后即可正常显示数据。"
msgid "Start HONK"
msgstr "前往启动 HONK"
msgid "New Tab"
msgstr "新标签页"
msgid "Open independently in a new tab"
msgstr "在新标签页中独立打开"
msgid "Drag to scroll page"
msgstr "拖拽以滚动页面"
msgid "Update Dashboard"
msgstr "更新控制面板"
msgid "Update to latest"
msgstr "更新至最新版"
msgid "The latest version (%s) will be downloaded and deployed to:"
msgstr "系统将下载最新版本(%s)并重新部署至目标目录:"
msgid "Select download source"
msgstr "选择下载源"
msgid "Cancel"
msgstr "取消"
msgid "Start Update"
msgstr "开始更新"
msgid "Refresh"
msgstr "刷新"
msgid "Refresh dashboard content"
msgstr "刷新面板内容"
msgid "Fullscreen"
msgstr "全屏"
msgid "Toggle fullscreen display"
msgstr "切换全屏显示"
msgid "Initializing download task...\n"
msgstr "正在初始化下载任务...\n"
msgid "Failed to trigger download:"
msgstr "触发下载失败:"
msgid "Installation completed successfully!"
msgstr "安装成功,最新版本已部署。"
msgid "Installation failed. Please check logs."
msgstr "安装失败,请检查日志。"
msgid "Close"
msgstr "关闭"
msgid "Download error:"
msgstr "触发下载异常:"
msgid "Not configured (Empty)"
msgstr "未设置(留空)"
msgid "Reloading..."
msgstr "正在重载..."
msgid "HONK service reload triggered successfully."
msgstr "已成功触发 HONK 服务重载。"
msgid "Failed to reload HONK:"
msgstr "重载 HONK 失败:"
msgid "DNS configuration saved and service reloaded."
msgstr "DNS 配置已保存并重载服务。"
msgid "Node configuration saved and service reloaded."
msgstr "节点配置已保存并重载服务。"
msgid "Routing configuration saved and service reloaded."
msgstr "路由配置已保存并重载服务。"
msgid "Dashboard"
msgstr "控制面板"
msgid "Mirror"
msgstr "镜像源"
msgid "Mirror 1"
msgstr "镜像源 1"
msgid "Mirror 2"
msgstr "镜像源 2"
msgid "Tip: If the dashboard still shows the old version, press Ctrl+F5 or open in a new tab to bypass PWA cache."
msgstr "提示:若更新后仍显示旧版本,请按 Ctrl+F5 强刷或在新标签页中打开以绕过 PWA 缓存。"
msgid "Advanced Settings"
msgstr "高级设置"
msgid "Enable to display full configuration editor and advanced routing/DNS/node tabs."
msgstr "启用后将显示完整的全局配置编辑器以及路由、DNS、节点的高级分页。"
msgid "Target Dashboard"
msgstr "目标面板"
msgid "Dashboard Type"
msgstr "面板类型"
msgid "Select the active web dashboard."
msgstr "选择当前生效的 Web 管理面板。"
msgid "Doona"
msgstr "Doona"
msgid "Doona Lite"
msgstr "Doona Lite"
msgid "Native API is configured, but dashboard files are missing in the UI directory. You can download and deploy it directly."
msgstr "Native API 已配置,但 UI 目录中缺少面板文件。您可以直接下载并部署。"
msgid "Target Directory (ui)"
msgstr "目标目录 (ui)"
msgid "Click to copy API Token"
msgstr "点击复制 API 认证密钥"
msgid "Token copied to clipboard: "
msgstr "认证密钥已复制到剪贴板: "
msgid "Token: "
msgstr "认证密钥: "
msgid "API Settings"
msgstr "API 设置"
msgid "Configure API settings for HONK."
msgstr "调整 HONK 的 API 设置。"
msgid "API Configuration"
msgstr "API 配置"
msgid "API configuration saved and service restarted."
msgstr "API 配置已保存并重启服务。"
msgid "None"
msgstr "无"
msgid "Dashboard Disabled"
msgstr "面板未启用"
msgid "Dashboard type is currently set to \"None\". Web dashboard and API listener are disabled."
msgstr "当前面板类型为“无”,Web 管理面板和 Native API 监听已禁用。"
msgid "Failed to switch dashboard:"
msgstr "切换面板失败:"
msgid "GitHub Release "
msgstr "GitHub Release "
msgid "Service did not accept the request"
msgstr "服务未接受该请求"
msgid "Failed to apply configuration:"
msgstr "应用配置失败:"
msgid "HONK did not come back up; check the logs"
msgstr "HONK 未能重新启动,请检查日志"
msgid "Configuration saved. HONK is disabled, so the service was not started."
msgstr "配置已保存。HONK 处于禁用状态,未启动服务。"
msgid "Download did not finish in time."
msgstr "下载超时未完成。"
msgid "Download task stopped reporting progress."
msgstr "下载任务已停止上报进度。"
msgid "Configuration saved, but the service state could not be confirmed."
msgstr "配置已保存,但无法确认服务状态。"
msgid "Running"
msgstr "运行中"
msgid "Not Running"
msgstr "未运行"
msgid "Zashboard"
msgstr "Zashboard"
msgid "Saved, but the panel files are not installed yet — download the panel first."
msgstr "配置已保存,但面板文件尚未安装,请先下载面板。"
@@ -1,712 +0,0 @@
Licence text shared by 16 packages (MIT)
========================================================================
@codemirror/autocomplete 6.20.3 (MIT) — LICENSE
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@codemirror/commands 6.11.1 (MIT) — LICENSE
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@codemirror/language 6.12.4 (MIT) — LICENSE
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@codemirror/lint 6.9.7 (MIT) — LICENSE
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@codemirror/search 6.7.2 (MIT) — LICENSE
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@codemirror/state 6.7.6 (MIT) — LICENSE
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@codemirror/view 6.43.13 (MIT) — LICENSE
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@lezer/common 1.5.2 (MIT) — LICENSE
Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@lezer/highlight 1.2.5 (MIT) — LICENSE
Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
@marijn/find-cluster-break 1.0.4 (MIT) — LICENSE
Copyright (C) 2024 by Marijn Haverbeke <marijn@haverbeke.berlin>
clsx 2.1.1 (MIT) — license
Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
openapi-fetch 0.17.0 (MIT) — LICENSE
Copyright (c) 2023 Drew Powers
react 19.3.0 (MIT) — LICENSE
Copyright (c) Meta Platforms, Inc. and affiliates.
react-dom 19.3.0 (MIT) — LICENSE
Copyright (c) Meta Platforms, Inc. and affiliates.
scheduler 0.28.0 (MIT) — LICENSE
Copyright (c) Meta Platforms, Inc. and affiliates.
use-sync-external-store 1.7.0 (MIT) — LICENSE
Copyright (c) Meta Platforms, Inc. and affiliates.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Licence text shared by 2 packages (OFL-1.1)
========================================================================
@fontsource-variable/noto-sans-sc 5.3.0 (OFL-1.1) — LICENSES/OFL-1.1.txt
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'.
@fontsource-variable/noto-sans-tc 5.3.0 (OFL-1.1) — LICENSES/OFL-1.1.txt
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'.
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Licence text shared by 5 packages (Apache-2.0)
========================================================================
@internationalized/number 3.6.8 (Apache-2.0) — LICENSE
Copyright 2019 Adobe
@internationalized/string 3.2.10 (Apache-2.0) — LICENSE
Copyright 2019 Adobe
react-aria 3.52.1 (Apache-2.0) — LICENSE
Copyright 2019 Adobe
react-aria-components 1.21.1 (Apache-2.0) — LICENSE
Copyright 2019 Adobe
react-stately 3.50.0 (Apache-2.0) — LICENSE
Copyright 2019 Adobe
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@react-aria/optimize-locales-plugin 2.0.2 (Apache-2.0) — LICENSE
========================================================================
Only the empty.js locale helper emitted by the plugin ships.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019 Adobe
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Licence text shared by 3 packages (MIT)
========================================================================
crelt 1.0.7 (MIT) — LICENSE
Copyright (C) 2020 by Marijn Haverbeke <marijn@haverbeke.berlin>
style-mod 4.1.3 (MIT) — LICENSE
Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
w3c-keyname 2.2.8 (MIT) — LICENSE
Copyright (C) 2016 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Licence text shared by 2 packages (ISC)
========================================================================
d3-path 3.1.0 (ISC) — LICENSE
Copyright 2015-2022 Mike Bostock
d3-shape 3.2.0 (ISC) — LICENSE
Copyright 2010-2022 Mike Bostock
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
rollup 4.63.2 (MIT) — LICENSE.md
========================================================================
Only the module namespace objects that Rollup writes into the build ship.
# Rollup core license
Rollup is released under the MIT license:
The MIT License (MIT)
Copyright (c) 2017 [these people](https://github.com/rollup/rollup/graphs/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
vite 6.4.3 (MIT) — LICENSE.md
========================================================================
Only helper code that Vite writes into the build ships: the module preload polyfill, the preload helper for dynamic imports and the CommonJS interop helper of @rollup/plugin-commonjs, which Vite bundles.
# Vite core license
Vite is released under the MIT license:
MIT License
Copyright (c) 2019-present, VoidZero Inc. and Vite contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## @rollup/plugin-alias, @rollup/plugin-commonjs, @rollup/plugin-dynamic-import-vars, @rollup/pluginutils
License: MIT
By: Johannes Stein
Repository: rollup/plugins
License: MIT
By: Rich Harris
Repository: rollup/plugins
License: MIT
By: LarsDenBakker
Repository: rollup/plugins
License: MIT
By: Rich Harris
Repository: rollup/plugins
> The MIT License (MIT)
>
> Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors)
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
@@ -1 +0,0 @@
import{j as r}from"./vendor-react-64fcanlc.js";import{I as a}from"./index-DFPUSiWe.js";function o(o){return r.jsx(a,{...o,children:r.jsx("path",{fill:"currentColor",d:"M10 1.5a8.5 8.5 0 1 0 0 17 8.5 8.5 0 0 0 0-17m0 15.5a7 7 0 1 1 0-14 7 7 0 0 1 0 14m3.75-7.75h-3v-3a.75.75 0 0 0-1.5 0v3h-3a.75.75 0 0 0 0 1.5h3v3a.75.75 0 0 0 1.5 0v-3h3a.75.75 0 0 0 0-1.5"})})}export{o as A};
@@ -1 +0,0 @@
import{j as e,r as s}from"./vendor-react-64fcanlc.js";import{fz as l,fA as n,fB as a,co as c,cz as t,cq as i,bz as r,eZ as o,cp as u,dv as d,fC as p,dw as h,cr as m,u as x,b5 as b,fD as j,bt as f,cs as v,fE as g,ct as y,cu as C,fF as N,cv as A,T as S,I as M,bi as k,dZ as w,B as L}from"./index-DFPUSiWe.js";import{a as q,b as I}from"./ItemText-u3CUc_6I.js";const D=e=>e>12,z=({orientation:s="vertical"})=>e.jsx(l,{orientation:s,className:"vertical"===s?"rp-vrule":"rp-hrule"}),H=e=>s=>{if("all"===s)return;const l=[...s][0];null!=l&&e(String(l))};function P({item:s,children:l,onAction:n}){return e.jsxs(A,{id:s.id,className:"rp-item",textValue:s.label,onAction:n,children:[e.jsx(q,{}),e.jsx(I,{i:s,children:l})]})}function $({children:l,content:n,label:x,quiet:b,small:j,size:f,chevron:v=!0,isDisabled:g,appearance:y,placement:C="bottom end",count:N,onIntent:A}){const S=N?e.jsx("span",{className:"rp-count","aria-hidden":"true",children:N}):null,M=a(g),[k,w]=s.useState(!1),L=c(),q=t(f),I=s.useRef(null),D=e.jsxs(i,{ref:I,className:y?"rp-select":o({quiet:b,small:j,icon:!v}),"data-size":q,"aria-label":x,"aria-describedby":M?.id,isDisabled:g,onHoverStart:A,onFocus:A,children:[l,S,v&&e.jsx(r,{className:"rp-chev"})]});return e.jsxs(u,{children:[v&&!M?D:e.jsxs(d,{delay:400,isOpen:k,onOpenChange:w,children:[e.jsx(p,{children:e.jsx("span",{className:"rp-tipwrap",tabIndex:g?0:-1,"data-passive":g?void 0:"",onPointerDown:e=>{g&&"touch"===e.pointerType&&w(e=>!e)},children:D})}),e.jsx(h,{triggerRef:I,children:M?.text??x})]}),e.jsx(m,{className:"rp-popover rp-list-popover",placement:C,boundaryElement:L,children:n})]})}function B({label:s,labelledBy:l,sections:n,headers:a=!0,focusedByCaller:c,onAction:t,searchLabel:i,actions:r=[]}){const o="function"==typeof n?n():n,u=!!i&&D(E(o)),d=e.jsxs(v,{"aria-label":s,"aria-labelledby":l??"",className:u?"rp-menu-scroll":void 0,autoFocus:!c&&void 0,children:[o.map(s=>e.jsxs(y,{"aria-label":a&&!s.hideHeader?void 0:s.title,selectionMode:s.selectionMode??"single",selectedKeys:[`/choice-${s.value}`],onSelectionChange:s.onChange&&H(e=>s.onChange(e.slice(8))),children:[a&&!s.hideHeader&&e.jsx(C,{className:"rp-sec-h",children:s.title}),s.items.map(s=>e.jsx(P,{item:{...s,id:`/choice-${s.id}`},onAction:t&&(()=>t(s.id))},s.id))]},s.title)),r.length>0&&e.jsx(z,{orientation:"horizontal"}),r.map(s=>e.jsx(O,{...s},s.label))]});return u?e.jsx(N,{label:i,children:d}):d}const E=e=>e.reduce((e,s)=>e+s.items.length,0);function F(e,s){for(const l of e){const n=l.items.find(e=>e.id===l.value);if(n)return e.length>1&&!l.plain&&n.label!==l.title?s("ui.sectionChoice",{section:l.title,item:n.label}):n.label}return""}const O=({label:s,icon:l,onAction:n})=>e.jsxs(A,{className:"rp-item rp-subitem",textValue:s,onAction:n,shouldCloseOnSelect:!0,children:[e.jsx("span",{className:"ic",children:l}),e.jsx(S,{children:s})]}),V=({id:s,label:l,icon:n,sections:a})=>{const c=x();return e.jsxs(A,{id:s,className:"rp-item rp-subitem",textValue:l,children:[e.jsx("span",{className:"ic",children:n}),e.jsx(S,{children:l}),e.jsxs("span",{className:"desc",children:[F(a,c),e.jsx(r,{className:"rp-chev-end"})]})]})};function R({label:l,submenus:n,actions:a=[]}){const c="function"==typeof n?n():n,t=x(),o=b(j),[u,d]="rtl"===f().direction?["ArrowLeft","ArrowRight"]:["ArrowRight","ArrowLeft"],[{open:p,left:h},y]=s.useState({open:null,left:null}),C=s.useRef(null),N=s.useId(),A=e=>{C.current?.focus(),y({open:e,left:null==e?p:null})};if(s.useEffect(()=>{const e=C.current;if(!e||null==p&&null==h)return;const s=null==p?`[data-key="${CSS.escape(`/submenu-${h}`)}"]`:'[aria-checked="true"], [role="menuitem"]',l=requestAnimationFrame(()=>{document.activeElement===e&&e.querySelector(s)?.focus()});return()=>cancelAnimationFrame(l)},[p,h]),!o)return e.jsxs(v,{"aria-label":l,"aria-labelledby":"",children:[c.map(s=>"sections"in s?e.jsxs(g,{children:[e.jsx(V,{id:`/submenu-${s.label}`,...s}),e.jsx(m,{className:"rp-popover rp-list-popover",offset:-4,crossOffset:-9,children:e.jsx(B,{...s,headers:s.sections.length>1})})]},s.label):e.jsx(O,{...s},s.label)),a.map(s=>e.jsx(O,{...s},s.label))]});const S=null==p?null:c.find(e=>e.label===p);if(S&&"sections"in S){const s=()=>A(null),l=e=>{e.key!==d&&"Escape"!==e.key||(e.preventDefault(),e.stopPropagation(),s())};return e.jsxs("div",{ref:C,className:"rp-drill",tabIndex:-1,onKeyDownCapture:l,children:[e.jsxs(i,{className:"rp-item rp-drill-back","aria-label":t("ui.back"),onPress:s,children:[e.jsx(r,{className:"rp-chev-start"}),e.jsx("span",{id:N,children:S.label})]}),e.jsx(B,{...S,labelledBy:N,headers:S.sections.length>1,focusedByCaller:!0})]})}const M=e=>c.find(s=>`/submenu-${s.label}`===e&&"sections"in s)?.label??null;return e.jsx("div",{ref:C,className:"rp-drill",tabIndex:-1,onKeyDownCapture:e=>{const s=M(e.target.dataset.key);e.key===u&&null!=s&&(e.preventDefault(),A(s))},children:e.jsxs(v,{"aria-label":l,"aria-labelledby":"",autoFocus:null==h&&void 0,shouldCloseOnSelect:!1,onAction:e=>{const s=M(String(e));null!=s&&A(s)},cLine truncated
@@ -1 +0,0 @@
import{r as e,j as t}from"./vendor-react-64fcanlc.js";import{u as a,g as s,gh as n,dD as o,gi as i}from"./index-DFPUSiWe.js";import{a as l,aU as r,aV as c,aW as h,aX as m,aY as u,aZ as p,a_ as d,a$ as x,b0 as f}from"./activity-pUN0wcVd.js";import{G as M,C as g}from"./AreaCurve-DQSHOSpG.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./nodes-HzIJdCpg.js";import"./logs-lI5kXcWp.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./array-C0VuvQyb.js";const j=[30,60,120,300,600,900,1800,3600,7200,21600,43200,86400].map(e=>1e3*e);function y(t,a){const[s,n]=e.useState([]),o=e.useRef("");return e.useLayoutEffect(()=>{const e=[a,...t].join("\0");if(e===o.current)return;o.current=e;const s=document.createElement("span");s.className="rp-area-tick",Object.assign(s.style,{position:"absolute",top:"-20000px",whiteSpace:"pre"}),document.body.appendChild(s),n(t.map(e=>{s.textContent=e;const{width:t,height:a}=s.getBoundingClientRect();return{width:t,height:a}})),s.remove()},[t,a]),s}function v({label:v,series:b,timestamps:w,fmt:k,locale:C,height:D=150,baseline:$="zero",window:N,fill:S,bare:A=!1,inset:L=2}){const T=a(),B=l(),E=e.useId(),[F,z]=s(Math.round,120),{tip:I,showAt:P,hide:W}=r(),[K,O]=e.useState(null),R=K&&K.index<w.length?K.index:null,U=N?N.until-N.since:w.length?w[w.length-1]-w[0]:0,_=N?.since,G=N?.until,H=e.useMemo(()=>void 0!==_&&void 0!==G?function(e,t){const a=t-e,s=j.find(e=>a/e<=6)??j[j.length-1],n=s>=864e5?6e4*new Date(t).getTimezoneOffset():0,o=[];for(let i=Math.ceil((e-n)/s)*s+n;i<=t;i+=s)i-e>=.04*a&&t-i>=.04*a&&o.push(i);return{ticks:o,step:s}}(_,G):void 0,[_,G]),V=H?H.step<6e4:U<18e4,X=H?H.step>=864e5:U>1296e5,Y=e.useMemo(()=>X?n(C):o(C,V),[C,V,X]),{yDomain:Z,yTicks:q}=e.useMemo(()=>{const e=b.flatMap(e=>e.values.filter(e=>null!==e));let t=0,a=(e=>{const t=Math.pow(10,Math.floor(Math.log10(e))),a=e/t;return([1,1.2,1.5,2,2.5,3,4,5,6,8,10].find(e=>a<=e)??10)*t})(1.08*Math.max(1,...e));if("auto"===$&&e.length){const s=Math.min(...e),n=Math.max(...e),o=(e=>{const t=Math.pow(10,Math.floor(Math.log10(Math.max(e,1e-9)))),a=e/t;return(a<=1?.2:a<=2?.5:a<=5?1:2)*t})(Math.max(n-s,.02*n,1));t=Math.max(0,Math.floor(s/o)*o-o),a=Math.ceil(n/o)*o+o}return{yDomain:[t,a],yTicks:"auto"===$?[t,(t+a)/2,a]:[a/2,a]}},[b,$]),J=e.useMemo(()=>{const e=w.length-1;return H?H.ticks:[...new Set((V?[0,Math.round(e/2),e]:[0,Math.round(e/3),Math.round(2*e/3),e]).map(e=>w[e]))]},[H,V,w]),Q=e.useMemo(()=>void 0!==_&&void 0!==G?[_,G]:[Math.min(...w),Math.max(...w)],[_,G,w]),ee=z?.width??0,te=z?.height??D,ae=A?L:20,se=A?2:8,ne=te-(A?2:30),oe=ee-(A?L:64),ie=e.useMemo(()=>[Math.min(Q[0],...w),Math.max(Q[1],...w)],[Q,w]),le=e.useCallback(e=>c(e,ie,[ae,oe]),[ie,ae,oe]),re=e.useCallback(e=>c(e,Z,[ne,se]),[Z,ne,se]),ce=e.useMemo(()=>w.map(le),[w,le]),he=e.useMemo(()=>b.map(e=>w.map((t,a)=>null==e.values[a]?null:{x:ce[a],y:re(e.values[a])})),[b,w,ce,re]),me={x:ae,y:se,width:oe-ae,height:ne-se},ue=(e,t)=>O({index:e,y:t}),pe=()=>{O(null),W()};e.useLayoutEffect(()=>{null!==R&&b.some(e=>null!=e.values[R])?P({x:ce[R],y:K?.y??(se+ne)/2,width:ee,bounds:A&&F.current?m(F.current):me,label:i(C).format(w[R]),lines:h(b,R,k,T)}):W()});const de=u(w.length,ue,pe),xe=e.useMemo(()=>J.map(e=>Y.format(e)),[J,Y]),fe=e.useMemo(()=>q.map(k),[q,k]),Me=y(xe,C),ge=y(fe,C),je=H?p(J.map(le),Me.map(e=>e.width),0,ee,16,!0):function(e,t,a){const s=a=>[e[a]-(t[a]??0)/2,e[a]+(t[a]??0)/2],n=e.length-1,o=[];let i=1/0;for(let l=n;l>=0;l--){const[t,r]=s(l);r+a>i||l>0&&l<n&&t<s(0)[1]+a||(o.unshift({index:l,position:e[l]}),i=t)}return o}(J.map(le),Me.map(e=>e.width),8),ye=p(q.map(re),ge.map(e=>e.height),0,te,5,!1),ve=[...new Set([...(A?q:ye.map(e=>q[e.index])).map(re),se,ne])];return t.jsx("div",{ref:F,style:{height:D,width:"100%",flex:S?"1 0 auto":void 0,position:"relative"},onPointerLeave:de.onPointerLeave,children:w.length>0&&z&&t.jsxs(t.Fragment,{children:[t.jsx(d,{tip:I}),t.jsxs("svg",{className:"rp-activity-surface",width:ee,height:te,viewBox:`0 0 ${ee} ${te}`,style:{display:"block"},role:"application","aria-label":v,tabIndex:0,onFocus:de.onFocus,onBlur:de.onBlur,onKeyDown:de.onKeyDown,onPointerMove:e=>{const{x:t,y:a}=x(e,e.currentTarget,{width:ee,height:te});if(t<ae||t>oe||a<se||a>ne)return void pe();const s=f(ce,t);ue(s,a)},children:[t.jsx("g",{className:"rp-area-grid",children:ve.map(e=>t.jsx("line",{x1:ae,x2:oe,y1:e,y2:e,stroke:B["hl-med"],strokeDasharray:"2 4",fill:"none"},e))}),t.jsx("defs",{children:b.map((e,a)=>t.jsx(M,{id:E+a,color:e.color},e.label))}),b.map((e,a)=>t.jsx(g,{points:he[a],baseline:re(Math.max(0,Z[0])),color:e.color,id:E+a,strokeWidth:2},e.label)),null!==R&&t.jsx("path",{d:`M${ce[R]},${se}L${ce[R]},${ne}`,stroke:B.subtle,strokeDasharray:"3 3",fill:"none",pointerEvents:"none"}),!A&&t.jsxs(t.Fragment,{children:[t.jsx("g",{children:je.map(({index:e,position:a})=>t.jsx("text",{x:a,y:ne+8,className:"rp-area-tick",fill:B.subtle,textAnchor:"middle",children:t.jsx("tspan",{x:a,dy:"0.71em",children:xe[Line truncated
@@ -1 +0,0 @@
import{j as t,r as n}from"./vendor-react-64fcanlc.js";import{w as i,c as e,a as o}from"./array-C0VuvQyb.js";function r(t){this._context=t}function u(t){return new r(t)}function l(t){return t[0]}function s(t){return t[1]}function c(t,n){var r=e(!0),c=null,f=u,h=null,a=i(_);function _(i){var e,u,l,s=(i=o(i)).length,_=!1;for(null==c&&(h=f(l=a())),e=0;e<=s;++e)!(e<s&&r(u=i[e],e,i))===_&&((_=!_)?h.lineStart():h.lineEnd()),_&&h.point(+t(u,e,i),+n(u,e,i));if(l)return h=null,l+""||null}return t="function"==typeof t?t:void 0===t?l:e(t),n="function"==typeof n?n:void 0===n?s:e(n),_.x=function(n){return arguments.length?(t="function"==typeof n?n:e(+n),_):t},_.y=function(t){return arguments.length?(n="function"==typeof t?t:e(+t),_):n},_.defined=function(t){return arguments.length?(r="function"==typeof t?t:e(!!t),_):r},_.curve=function(t){return arguments.length?(f=t,null!=c&&(h=f(c)),_):f},_.context=function(t){return arguments.length?(null==t?c=h=null:h=f(c=t),_):c},_}function f(t){return t<0?-1:1}function h(t,n,i){var e=t._x1-t._x0,o=n-t._x1,r=(t._y1-t._y0)/(e||o<0&&-0),u=(i-t._y1)/(o||e<0&&-0),l=(r*o+u*e)/(e+o);return(f(r)+f(u))*Math.min(Math.abs(r),Math.abs(u),.5*Math.abs(l))||0}function a(t,n){var i=t._x1-t._x0;return i?(3*(t._y1-t._y0)/i-n)/2:n}function _(t,n,i){var e=t._x0,o=t._y0,r=t._x1,u=t._y1,l=(r-e)/3;t._context.bezierCurveTo(e+l,o+l*n,r-l,u-l*i,r,u)}function p(t){this._context=t}function y(t){return new p(t)}function x({id:n,color:i}){return t.jsxs("linearGradient",{id:n,x1:"0",y1:"0",x2:"0",y2:"1",children:[t.jsx("stop",{offset:"0%",stopColor:i,stopOpacity:.35}),t.jsx("stop",{offset:"100%",stopColor:i,stopOpacity:.03})]})}r.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}},p.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:_(this,this._t0,a(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){var i=NaN;if(n=+n,(t=+t)!==this._x1||n!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,_(this,a(this,i=h(this,t,n)),i);break;default:_(this,this._t0,i=h(this,t,n))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n,this._t0=i}}},Object.create(p.prototype).point=function(t,n){p.prototype.point.call(this,n,t)};const d=n.memo(function({points:n,baseline:r,color:f,id:h,strokeWidth:a}){if(1===n.length){const i=n[0];return i?t.jsx("circle",{cx:i.x,cy:i.y,r:3,stroke:f,strokeWidth:a,fill:`url(#${h})`,fillOpacity:.6}):null}const _=t=>null!==t&&Number.isFinite(t.x)&&Number.isFinite(t.y),p=c().defined(_).x(t=>t.x).y(t=>t.y).curve(y).digits(3),x=function(t,n,r){var f=null,h=e(!0),a=null,_=u,p=null,y=i(x);function x(i){var e,u,l,s,c,x=(i=o(i)).length,d=!1,v=new Array(x),g=new Array(x);for(null==a&&(p=_(c=y())),e=0;e<=x;++e){if(!(e<x&&h(s=i[e],e,i))===d)if(d=!d)u=e,p.areaStart(),p.lineStart();else{for(p.lineEnd(),p.lineStart(),l=e-1;l>=u;--l)p.point(v[l],g[l]);p.lineEnd(),p.areaEnd()}d&&(v[e]=+t(s,e,i),g[e]=+n(s,e,i),p.point(f?+f(s,e,i):v[e],r?+r(s,e,i):g[e]))}if(c)return p=null,c+""||null}function d(){return c().defined(h).curve(_).context(a)}return t="function"==typeof t?t:void 0===t?l:e(+t),n="function"==typeof n?n:e(void 0===n?0:+n),r="function"==typeof r?r:void 0===r?s:e(+r),x.x=function(n){return arguments.length?(t="function"==typeof n?n:e(+n),f=null,x):t},x.x0=function(n){return arguments.length?(t="function"==typeof n?n:e(+n),x):t},x.x1=function(t){return arguments.length?(f=null==t?null:"function"==typeof t?t:e(+t),x):f},x.y=function(t){return arguments.length?(n="function"==typeof t?t:e(+t),r=null,x):n},x.y0=function(t){return arguments.length?(n="function"==typeof t?t:e(+t),x):n},x.y1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:e(+t),x):r},x.lineX0=x.lineY0=function(){return d().x(t).y(n)},x.lineY1=function(){return d().x(t).y(r)},x.lineX1=function(){return d().x(f).y(n)},x.defined=function(t){return arguments.length?(h="function"==typeof t?t:e(!!t),x):h},x.curve=function(t){return arguments.length?(_=t,null!=a&&(p=_(a)),x):_},x.context=function(t){return arguments.length?(null==t?a=p=null:p=_(a=t),x):a},x}().defined(_).x(t=>t.x).y0(r).y1(t=>t.y).curve(y).digits(3);return t.jsxs("g",{children:[t.jsx("path",{d:x(n)??void 0,stroke:"none",fill:`url(#${h})`,fillOpacity:.6}),t.jsx("path",{className:"rLine truncated
@@ -1 +0,0 @@
import{j as e}from"./vendor-react-64fcanlc.js";import{a,$ as s}from"./GridList-BuXKU7Uk.js";import{T as i,cn as r,u as t,S as l,ah as c}from"./index-DFPUSiWe.js";function n({label:a,items:i,selected:r,onSelect:n,loading:d,empty:o,children:m}){const p=t();return e.jsx(s,{className:"rp-cardview","aria-label":a,layout:"grid",items:i,dependencies:[m],selectionMode:"single",selectionBehavior:"replace",disallowEmptySelection:!0,selectedKeys:r?[r]:[],onSelectionChange:e=>{if("all"===e)return;const a=[...e][0];null!=a&&n(String(a))},renderEmptyState:()=>d?e.jsx(l,{shape:"cards",count:3,height:224,grid:!0}):e.jsx(c,{children:o??p("ui.empty")}),children:m})}function d({id:s,title:t,titleFull:l,meta:c,actions:n,children:d}){return e.jsxs(a,{id:s,textValue:t,className:r("rp-cardview-card"),children:[e.jsxs("div",{className:"rp-cardview-head",children:[e.jsx(i,{className:"rp-cardview-title",text:l,children:t}),n&&e.jsx("span",{className:"rp-cardview-actions",children:n})]}),c&&e.jsx("div",{className:"rp-cardview-meta",children:c}),d]})}export{d as C,n as a};
@@ -1 +0,0 @@
import{r as s,j as e}from"./vendor-react-64fcanlc.js";import{g as a}from"./useGridSelectionCheckbox-wMhI-U-R.js";import{C as i}from"./ItemText-u3CUc_6I.js";import{dj as r}from"./index-DFPUSiWe.js";function d({label:d,description:o,isSelected:l,onChange:c,isDisabled:n,slot:t}){const p=s.useId(),m=s.useId(),x=r({isDisabled:n});return e.jsxs(a,{className:"rp-radio rp-checkbox",slot:t,isSelected:l,onChange:c,isDisabled:x.isDisabled,"aria-labelledby":d?p:void 0,"aria-describedby":o?m:void 0,children:[e.jsx("span",{className:"rp-checkbox-mark",children:e.jsx(i,{})}),d&&e.jsxs("span",{className:"rp-radio-text",children:[e.jsx("span",{id:p,children:d}),o&&e.jsx("span",{id:m,className:"rp-note",children:o})]})]})}export{d as C};
@@ -1 +0,0 @@
import{j as e,r as s}from"./vendor-react-64fcanlc.js";import{I as i,cC as l,cm as a,B as r,ce as n}from"./index-DFPUSiWe.js";import{C as c}from"./Checkbox-xU-Ds7Hn.js";function d(s){return e.jsx(i,{...s,children:e.jsx("path",{fill:"currentColor",d:"M17.78 1.757C16.51.716 14.555.858 13.33 2.08L3.079 12.334c-.32.32-.559.717-.69 1.15l-1.384 4.584c-.08.265-.007.552.188.747.142.143.334.22.53.22q.11 0 .217-.032l4.582-1.384c.434-.13.832-.368 1.151-.688L18.028 6.575c.648-.647.993-1.545.948-2.46s-.482-1.775-1.195-2.358M2.846 17.163l.98-3.246q.042-.135.113-.257l2.41 2.409q-.123.07-.259.114zM16.966 5.516l-9.525 9.526-2.475-2.476 9.425-9.425c.386-.387.896-.586 1.39-.586.38 0 .752.119 1.05.362.392.321.621.772.646 1.272.025.494-.161.977-.51 1.327"})})}function o({children:s}){return e.jsx(n,{children:s})}function t({children:s,negate:i,onNegate:n,negateLabel:c,removeLabel:d,onRemove:t,isDisabled:b,removeDisabled:h}){return e.jsxs(l,{children:[s,e.jsxs(o,{children:[e.jsx(a,{isDisabled:b,isSelected:i,onChange:n,children:c}),e.jsx("span",{className:"rp-grow"}),e.jsx(r,{small:!0,isDisabled:b||h,onPress:t,children:d})]})]})}function b({label:i,hideLabel:l=!1,items:a,value:r,onChange:n,isDisabled:d}){const o=s.useId();return e.jsxs("div",{className:"rp-checkbox-set",role:"group","aria-labelledby":o,children:[e.jsx("span",{id:o,className:"rp-label",hidden:l,children:i}),e.jsx("div",{className:"rp-checkbox-options",children:a.map(s=>e.jsx(c,{label:s.label,description:s.description,isSelected:r.includes(s.id),isDisabled:d||s.isDisabled,onChange:e=>n(e?[...r,s.id]:r.filter(e=>e!==s.id))},s.id))})]})}export{b as C,d as E,t as a,o as b};
@@ -1 +0,0 @@
import{r as e,j as r}from"./vendor-react-64fcanlc.js";import{bR as t,gs as o,bQ as n,fe as a,bu as c,u as s,ce as i,dt as l,B as d,cs as p,cv as m}from"./index-DFPUSiWe.js";import{S as u,a as f,E as g,C as b,b as v,l as h,c as x,h as y,d as k,r as w,e as j,f as C,g as R,i as A,j as L,s as E,k as M,t as F,m as N,n as O,o as S,p as $,q as B,u as D,v as q,w as I,x as T,y as H,z as V,D as K,R as U,A as z,B as G,F as P,G as W,H as J,I as Q,J as X,K as _,L as Y}from"./vendor-editor-50h2cCk6.js";import{d as Z,s as ee}from"./daeTokens-BVtuNnWZ.js";import{s as re}from"./nodes-HzIJdCpg.js";import{M as te}from"./AppearanceControls-Bpq6OQjk.js";import{n as oe,A as ne}from"./logs-lI5kXcWp.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./index-DUM-iwa2.js";import"./ItemText-u3CUc_6I.js";import"./Select-BFkb6iie.js";const ae=u.define({name:"dae",languageData:{commentTokens:{line:"#"},closeBrackets:{brackets:["(","{","'",'"']}},startState:ee,token:Z}),ce=["global","subscription","node","group","dns","routing"].map(e=>({label:e,type:"keyword"})),se=n.map(e=>({label:e,type:"property",apply:e+": "})),ie=["domain","dip","sip","dport","sport","l4proto","ipversion","pname","mac","dscp","qname","qtype","upstream"].map(e=>({label:e,type:"function",apply:e+"("})),le=["geosite","geoip","suffix","full","keyword","regex"].map(e=>({label:e,type:"constant",apply:e+":"})),de=o.map(e=>({label:e,type:"constant"})),pe=t.map(e=>({label:e,type:"keyword"})),me=["asis","reject"].map(e=>({label:e,type:"keyword"})),ue=["accept","reject"].map(e=>({label:e,type:"keyword"})),fe=[{label:"fallback",type:"keyword",apply:"fallback: "},{label:"include",type:"keyword",apply:"include "}],ge=e=>e.map(e=>({label:e.name,apply:e.written,type:"variable"}));function be(e){return f({override:[r=>function(e,r){const t=e.matchBefore(/[\w.-]*/);if(!t)return null;const o=e.state.doc.lineAt(e.pos),n=o.text.slice(0,e.pos-o.from),a=/->\s*[\w.-]*$/.test(n)||/^\s*fallback:\s*[\w.-]*$/.test(n);if(t.from===t.to&&!e.explicit&&!a)return null;const c=e.state.doc.sliceString(0,e.pos),{blocks:s,tokens:i}=re(c),l=i.at(-1);if(l?.to===e.pos&&("quoted"===l.kind||"comment"===l.kind))return null;const d=[],p=r=>r.close===e.pos;for(let f=s.find(p);f;f=f.children.find(p))d.push(f.name);const m=d[0];let u;if(a){const{groups:e,upstreams:t}=r();u="dns"!==m?[...ge(e),...pe]:[...ge(t),...d.includes("response")?ue:me]}else if(/\b(domain|dip|sip|qname)\(\s*[\w.-]*$/.test(n))u=le;else if(void 0===m)u=ce.map(e=>({...e,apply:(r,t,o,n)=>{const a=e.label+" {\n \n}";r.dispatch({changes:{from:o,to:n,insert:a},selection:{anchor:o+e.label.length+4}})}}));else if("routing"===m||"dns"===m)u=[...ie,...fe];else if("global"===m)u=se;else{if("group"!==m)return null;u=/\bpolicy\s*:\s*[\w.-]*$/.test(n)?de:[...de.map(e=>({...e,apply:"policy: "+e.label})),{label:"filter",type:"property",apply:"filter: "}]}return{from:t.from,options:u,validFor:/^[\w.-]*$/}}(r,e)]})}function ve(e){let r=!1;const t=r=>(r.state.readOnly&&e(),!1);return g.domEventHandlers({keydown:(e,r)=>function(e){return"Backspace"===e.key||"Delete"===e.key||"Enter"===e.key||1===[...e.key].length&&!e.ctrlKey&&!e.metaKey}(e)&&t(r),paste:(e,r)=>t(r),cut:(e,r)=>t(r),pointerdown:e=>(r="touch"===e.pointerType,!1),click:(e,o)=>r&&t(o)})}const he=[["Completions","cm.completions"],["Find","cm.find"],["Replace","cm.replace"],["next","cm.next"],["previous","cm.previous"],["all","cm.all"],["match case","cm.matchCase"],["regexp","cm.regexp"],["by word","cm.byWord"],["replace","cm.replaceOne"],["replace all","cm.replaceAll"],["close","ui.close"],["Go to line","cm.gotoLine"],["go","cm.go"],["current match","cm.currentMatch"],["replaced $ matches","cm.replacedMatches"],["replaced match on line $","cm.replacedOnLine"],["on line","cm.onLine"],["Selection deleted","cm.selectionDeleted"],["Control character","cm.controlCharacter"]],xe=e=>v.phrases.of(Object.fromEntries(he.map(([r,t])=>[r,e(t)]))),ye=g.theme({"&":{backgroundColor:"var(--rp-base)",color:"var(--rp-text)",border:"1px solid var(--rp-hl-high)",borderRadius:"var(--rp-r-md)",fontSize:"var(--rp-text-sm)"},"&.cm-focused":{outline:"none",borderColor:"var(--rp-text)"},".cm-scroller":{fontFamily:"var(--rp-font-mono)",lineHeight:"var(--rp-line-body)",fontVariantLigatures:"none",borderRadius:"calc(var(--rp-r-md) - 1px)"},".cm-content":{padding:"var(--rp-space-2) 0",caretColor:"var(--rp-text)"},".cm-line":{padding:"0 var(--rp-space-3)"},".cm-gutters":{backgroundColor:"var(--rp-base)",color:"var(--rp-muted)",border:"none"},".cm-lineNumbers .cm-gutterElement":{padding:"0 var(--rp-space-2) 0 var(--rp-space-3)",minWidth:"40px"},".cm-activeLine, .cm-focusLine":{backgroundColor:"color-mix(in srgb, var(--rp-hl-med) 60%, transparent)"},".cm-activeLineGutter":{backgroundColor:"transparent",color:"var(--rp-text)"},".cm-selectionBackground, &.cm-focused .cm-selectionBackground, ::selection":{backgroundColor:"var(--rp-hl-high)"},".cm-cursor":{borderLeftColor:"var(--rp-text)"},".cm-matchingBracket":{backgroundColor:"color-mLine truncated
@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./CodeEditor-DW62W8R0.js","./vendor-react-64fcanlc.js","./_commonjsHelpers-CXUWDbkB.js","./index-DFPUSiWe.js","./index-BnzLYrTn.css","./vendor-editor-50h2cCk6.js","./index-DUM-iwa2.js","./daeTokens-BVtuNnWZ.js","./nodes-HzIJdCpg.js","./AppearanceControls-Bpq6OQjk.js","./ItemText-u3CUc_6I.js","./logs-lI5kXcWp.js","./Select-BFkb6iie.js"])))=>i.map(i=>d[i]);
import{r as e,j as n}from"./vendor-react-64fcanlc.js";import{h as i,u as s,an as a,ao as r,r as t,t as o,y as l,c9 as c,w as d,cb as u,Z as g,D as f,aL as p,c7 as m,cI as h,dx as b,dy as x,a3 as v,a2 as j,ab as y,am as w,A as k,cH as N,dz as S,v as C,aM as D,aV as P,B as T,aB as _,E as O,bm as R,ah as E,b7 as M,cC as A,m as H,C as L,c as F,bC as I,dA as $,bJ as q,ce as K,bK as V,ay as W,dt as U,a as B,b as z,x as J,aN as Z,cE as G,aE as Q,bZ as X,aH as Y,p as ee,_ as ne,T as ie}from"./index-DFPUSiWe.js";import{a as se,u as ae,x as re,c as te,J as oe,O as le,B as ce,K as de,D as ue,p as ge,P as fe,S as pe,L as me}from"./logs-lI5kXcWp.js";import{T as he}from"./Tabs-HxpCyCLR.js";import{aI as be,a1 as xe,a9 as ve,aJ as je,x as ye,aK as we,aL as ke,a7 as Ne,D as Se}from"./activity-pUN0wcVd.js";import{s as Ce,P as De,N as Pe,e as Te,r as _e,aj as Oe,ak as Re,al as Ee,am as Me,L as Ae,an as He}from"./nodes-HzIJdCpg.js";import{s as Le,c as Fe,r as Ie,a as $e,b as qe,d as Ke,e as Ve,f as We,g as Ue,h as Be,v as ze,i as Je,j as Ze,k as Ge,l as Qe,m as Xe,n as Ye,o as en,w as nn}from"./nav-C6jMmQ4d.js";import{u as sn,c as an}from"./flows-Cd1KbGP2.js";import{d as rn}from"./files-Dt8K7QfP.js";import{c as tn}from"./limitHint-drqGf1xh.js";import{D as on}from"./Table-6xnX6eUx.js";import{T as ln}from"./TimeCell-CRwMWdtu.js";import{N as cn,t as dn}from"./NumberField-kCp6sey7.js";import{A as un}from"./AddCircle-V0Kluz-m.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./Virtualizer-BH6NJSXn.js";import"./Checkbox-xU-Ds7Hn.js";function gn(e,n,s,a="main"){if(!s.daeText)return[];const{blocks:r,tokens:t}=Ce(e),o=s.globalSettings;return r.flatMap(s=>{if(s.name===o?.name){const a=r.filter(e=>e.name===o.name).indexOf(s),l=De(e,s,t);return l.filter(e=>1===l.filter(n=>n.name===e.name).length&&o.fields.some(n=>n.key===e.name)).map(r=>({kind:s.name,from:r.from,to:r.to,label:r.name,line:e.slice(0,r.from).split("\n").length,href:i("config",{tab:"global",source:n,section:String(a),field:r.name})}))}const l=Le[s.name];return"subscription"===s.name?"main"!==a?[]:Pe(e).filter(e=>e.from>s.open&&e.to<=s.close).map(e=>({kind:s.name,from:e.from,to:e.to,label:e.tag,line:e.line,href:i("nodes",{editSubscriptionTag:e.tag})})):"dns"===s.name?s.children.filter(e=>"routing"===e.name).flatMap(e=>e.children.filter(e=>"request"===e.name||"response"===e.name).map(e=>({kind:s.name,from:e.from,to:e.to,label:e.name,line:e.line+1,href:l}))):l?[{kind:s.name,from:s.from,to:s.to,label:s.name,line:s.line+1,href:l}]:[]})}const fn=()=>{};const pn=new Map;function mn({go:n,query:i}){const m=s(),h=a(),b=r[h],x=t(),v=x.data?.resources,j=ae(o(v,"config",{whileLoading:!0})),y=function(n){const i=s(),a=se(n),r=e.useMemo(()=>{const e=ve(a.error);return e?e.diagnostics??[]:null},[a.error]);return e.useEffect(()=>{if(a.error)if(r){const e=je(r);f("negative",e?i("config.writeRestart",{n:e}):i("ui.writeInvalid",{n:r.filter(e=>"error"===e.level).length}))}else p(a.error,i,i("ui.writeFailed"))},[a.error,r,i]),{...a,diagnostics:r}}(j.refetch),w=e.useMemo(()=>new URLSearchParams(i),[i]),k=e.useMemo(()=>j.data?.sources??[],[j.data]),N=k.find(e=>"main"===e.kind)??null,S=k.find(e=>e.id===w.get("source"))??k[0]??null,C=S?.id??null,D=(e,s)=>n("config",d(i,{tab:"source",source:e,line:null===s?null:String(s)})),P=Number(w.get("line"))||null,T=j.data?.diagnostics??[],_=e.useMemo(()=>{const e=j.data?.diagnostics??[];return{error:e.filter(e=>"error"===e.level).length,warning:e.filter(e=>"warning"===e.level).length}},[j.data]),O=o(v,"config_validate",{whileLoading:!1})&&(v?.config_validate.modes??[]).includes("full"),R=!0===v?.config.writable,E=re(k),M=l().data,A=e.useMemo(()=>Te(M),[M]),H=Fe(!!A.globalSettings,x.data),L=x.data&&tn("config",x.data,M,m,h),F=S?Ie(S,R,E(S),A,m,L):null,I=!!S&&!F,[$,q]=e.useState(pn),[K,V]=e.useState(null),W=K??_.error,U=w.has("source")||void 0===N?.content?"source":"modules",B=c("validate"===w.get("tab")?d(i,{tab:"source"}):i,H.map(e=>e.id),U),z=S?{source:S,sources:k,open:D,diagnostics:T,canValidate:O&&("main"===S.kind||"include"===S.kind),canWrite:I,readOnly:F,isComplete:E,editor:y,focusLine:P,generation:j.data?.generation_id??"",focusDiagnostics:"validate"===w.get("tab"),diagnosticsChoice:$.get(S.id)??null,chooseDiagnostics:e=>q(n=>new Map(n).set(S.id,e)),reportErrors:V}:null,J=!0===v?.config.create&&!0===v.config.writable?{sources:k,refetch:j.refetch,open:e=>D(e,null)}:null;return{error:x.error??j.error,reload:()=>{x.refetch(),j.refetch()},loading:j.loading&&!j.data,ready:!!j.data,tabsReady:!!j.data||H.some(e=>"history"===e.id),metadata:j.data?qe(j.data.revision,m):[],redacted:!!j.data?.secrets_redacted,tabs:H.map(e=>({id:e.id,label:"source"===e.id&&W?m("ui.aside",{text:m(e.titleKey),note:g(W,b)}):m(e.titleKey)})),tab:B,setTab:e=>n("config",d(u(i,e,null),{field:null})),selectedId:C??"",select:e=>n("config",d(i,{soLine truncated
@@ -1 +0,0 @@
import{r as e,j as n}from"./vendor-react-64fcanlc.js";import{i as t,I as o,a as s}from"./ipLookup-47nEpvjK.js";import{N as l,O as a}from"./NodePath-DCKKYRsk.js";import{aU as r,u as i,ao as c,an as u,a2 as d,o as p,aC as m,C as b,E as h,Z as g,aa as f,s as w,O as v,b$ as y,T as x,bT as j,w as k,c0 as _,V as C,c1 as S,c2 as A,a0 as L,a3 as N,r as M,t as T,Q as R,c3 as P,a8 as O,c4 as F,aj as U,H as W,c5 as q,b5 as D,c6 as V,b6 as E,c7 as H,bW as I,c8 as Q,c9 as B,ca as K,M as G,ap as J,cb as Z,D as $,cc as z,aV as X,aM as Y,cd as ee,ce as ne,bK as te,B as oe,cf as se,bA as le,ay as ae}from"./index-DFPUSiWe.js";import{R as re,B as ie,u as ce,h as ue,o as de,g as pe,c as me,S as be,p as he,L as ge,n as fe,K as we}from"./logs-lI5kXcWp.js";import{C as ve}from"./AppearanceControls-Bpq6OQjk.js";import{T as ye}from"./Tabs-HxpCyCLR.js";import{a as xe,D as je,U as ke,F as _e,S as Ce,b as Se,B as Ae,L as Le,t as Ne,r as Me,d as Te,f as Re,p as Pe,o as Oe,k as Fe,g as Ue}from"./activity-pUN0wcVd.js";import{S as We}from"./SearchSelect-CsXd0DEh.js";import{D as qe}from"./Table-6xnX6eUx.js";import{T as De}from"./TimeCell-CRwMWdtu.js";import{Q as Ve,u as Ee,R as He}from"./useQuickRule-BGNvNXBX.js";import{u as Ie}from"./outbounds-BMHzxQr8.js";import{c as Qe,d as Be,e as Ke}from"./files-Dt8K7QfP.js";import{w as Ge}from"./labels-D8xxfRTQ.js";import{u as Je}from"./flows-Cd1KbGP2.js";import{r as Ze}from"./rule-D1hMywhM.js";import{c as $e}from"./nav-BO8viH9G.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./LinkOut-B50sd8cz.js";import"./Tag-BZqiYh1Z.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./nodes-HzIJdCpg.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./SearchList-C88Y6c53.js";import"./Virtualizer-BH6NJSXn.js";import"./Checkbox-xU-Ds7Hn.js";import"./DaeCode-DEjXqvav.js";import"./daeTokens-BVtuNnWZ.js";import"./AddCircle-V0Kluz-m.js";const ze=[0,2,3,1,4,5,6,7],Xe=e.memo(function({records:t,outbounds:o,latency:s,latencyError:l,retryLatency:a,truncated:g,uncounted:f,onSelect:w}){const v=i(),y=xe(),x=c[u()],j=e.useCallback(e=>d(e,x),[x]),k=e.useMemo(()=>function(e,n){const t=new Map;let o,s=0,l=-1n;for(const a of e){if(null===a.upload_bytes||null===a.download_bytes){s++;continue}const e={id:a.id,up:Number(a.upload_bytes),down:Number(a.download_bytes),name:a.domain||a.dst||a.id},n=t.get(a.outbound);n?n.push(e):t.set(a.outbound,[e]);const r=BigInt(a.upload_bytes)+BigInt(a.download_bytes);r>l&&(l=r,o={...e,outbound:a.outbound})}return{series:[...t].map(([e,n])=>({outbound:e,points:n})).sort((e,t)=>r(n)(e.outbound??"",t.outbound??"")),unknown:s,heaviest:o,placed:e.length-s}}(t,x),[t,x]),_=e.useMemo(()=>{const e=o.filter(e=>"block"!==e);return k.series.map(n=>{return{id:n.outbound??"",label:p(n.outbound,v),color:(t=n.outbound,"block"===t?y.negative:y.cat[ze[Math.max(0,e.indexOf(t))%ze.length]]),points:n.points.map(e=>({id:e.id,x:e.up,y:e.down,name:e.name,detail:v("conn.chart.point",{download:j(e.down),upload:j(e.up)})}))};var t})},[k,o,y,v,j]),C=k.heaviest?[{label:v("conn.chart.busiest"),icon:n.jsx(m,{}),tint:"c3",value:k.heaviest.name,caption:p(k.heaviest.outbound,v)},{label:v("ui.download"),value:j(k.heaviest.down),icon:n.jsx(je,{}),tint:"c1"},{label:v("ui.upload"),value:j(k.heaviest.up),icon:n.jsx(ke,{}),tint:"c4"}]:[],S=k.unknown?v("conn.chart.sampleUnknown",{n:t.length,unknown:k.unknown}):v("conn.chart.sample",{n:t.length});return n.jsxs("div",{className:"rp-chart-page",children:[n.jsx(_e,{facts:C,lead:!0}),n.jsxs(b,{title:v("conn.chart.title"),help:f?{title:v("conn.chart.title"),text:v("ui.trafficUncounted")}:void 0,note:S,children:[g&&n.jsx("p",{className:"rp-note",children:v("conn.truncated")}),k.placed>0&&n.jsxs(n.Fragment,{children:[n.jsx(Ce,{label:v("conn.chart.title"),series:_,fmt:j,onSelect:w,regions:{above:v("conn.chart.moreDown"),below:v("conn.chart.moreUp")}}),n.jsx(Se,{series:_}),n.jsx("p",{className:"rp-note",children:v("conn.chart.hint")})]})]}),s?n.jsx(Ye,{latency:s}):l&&n.jsx(b,{title:v("ui.nodeLatency"),children:n.jsx(h,{error:l,onRetry:a})})]})});function Ye({latency:e}){const t=i(),o=xe(),s=e=>f(e,t),l=c[u()],a=n=>!e.chains||n?o.cat[0]:o.muted;return n.jsx(b,{title:t("ui.nodeLatency"),note:t("conn.latency.sample",{n:e.samples.length,missing:e.missing}),children:e.samples.length?n.jsxs(n.Fragment,{children:[n.jsx("p",{className:"rp-note",children:t("conn.latency.percentiles",{p50:s(e.p50),p90:s(e.p90)})}),null!==e.weightedP50&&n.jsx("p",{className:"rp-note",children:e.unplaced?t("conn.latency.weightedUnplaced",{p50:s(e.weightedP50),n:e.unplaced}):t("conn.latency.weighted",{p50:s(e.weightedP50)})}),n.jsx(Ae,{label:t("ui.nodeLatency"),points:e.samples.map(e=>({id:e.node,nodeName:!0,value:e.value,color:a(e.connections>0),lines:[e.name,s(e.value),...e.connections?[t("conn.chart.sample",{n:e.connections})]:[]]})),fmt:s}),e.chains&&n.jsxs("div",{className:"rp-legend",children:[n.jsx(Le,{swatch:a(!0),label:t("conn.latency.inUse"),value:g(e.used,l)}),n.jsx(Le,{swatch:a(!1),label:tLine truncated
@@ -1 +0,0 @@
import{r as c,j as r}from"./vendor-react-64fcanlc.js";import{u as s,dA as o,D as l,I as t}from"./index-DFPUSiWe.js";function a(){const r=s();return c.useCallback(async c=>{const s=await o(c);l(s?"positive":"negative",r(s?"ui.recordCopied":"ui.recordCopyFailed"))},[r])}function e(c){return r.jsxs(t,{...c,children:[r.jsx("path",{fill:"currentColor",d:"m11.75,18h-7.5c-1.24023,0-2.25-1.00977-2.25-2.25v-7.5c0-1.24023,1.00977-2.25,2.25-2.25.41406,0,.75.33594.75.75s-.33594.75-.75.75c-.41309,0-.75.33691-.75.75v7.5c0,.41309.33691.75.75.75h7.5c.41309,0,.75-.33691.75-.75,0-.41406.33594-.75.75-.75s.75.33594.75.75c0,1.24023-1.00977,2.25-2.25,2.25Z"}),r.jsx("path",{fill:"currentColor",d:"m6.75,5c-.41406,0-.75-.33594-.75-.75,0-1.24023,1.00977-2.25,2.25-2.25.41406,0,.75.33594.75.75s-.33594.75-.75.75c-.41309,0-.75.33691-.75.75,0,.41406-.33594.75-.75.75Z"}),r.jsx("path",{fill:"currentColor",d:"m13,3.5h-2c-.41406,0-.75-.33594-.75-.75s.33594-.75.75-.75h2c.41406,0,.75.33594.75.75s-.33594.75-.75.75Z"}),r.jsx("path",{fill:"currentColor",d:"m13,14h-2c-.41406,0-.75-.33594-.75-.75s.33594-.75.75-.75h2c.41406,0,.75.33594.75.75s-.33594.75-.75.75Z"}),r.jsx("path",{fill:"currentColor",d:"m15.75,14c-.41406,0-.75-.33594-.75-.75s.33594-.75.75-.75c.41309,0,.75-.33691.75-.75,0-.41406.33594-.75.75-.75s.75.33594.75.75c0,1.24023-1.00977,2.25-2.25,2.25Z"}),r.jsx("path",{fill:"currentColor",d:"m17.25,5c-.41406,0-.75-.33594-.75-.75,0-.41309-.33691-.75-.75-.75-.41406,0-.75-.33594-.75-.75s.33594-.75.75-.75c1.24023,0,2.25,1.00977,2.25,2.25,0,.41406-.33594.75-.75.75Z"}),r.jsx("path",{fill:"currentColor",d:"m17.25,9.75c-.41406,0-.75-.33594-.75-.75v-2c0-.41406.33594-.75.75-.75s.75.33594.75.75v2c0,.41406-.33594.75-.75.75Z"}),r.jsx("path",{fill:"currentColor",d:"m6.75,9.75c-.41406,0-.75-.33594-.75-.75v-2c0-.41406.33594-.75.75-.75s.75.33594.75.75v2c0,.41406-.33594.75-.75.75Z"}),r.jsx("path",{fill:"currentColor",d:"m8.25,14c-1.24023,0-2.25-1.00977-2.25-2.25,0-.41406.33594-.75.75-.75s.75.33594.75.75c0,.41309.33691.75.75.75.41406,0,.75.33594.75.75s-.33594.75-.75.75Z"})]})}export{e as C,a as u};
@@ -1 +0,0 @@
import{aj as e,b1 as l,V as r,u as s,T as a}from"./index-DFPUSiWe.js";import{j as n}from"./vendor-react-64fcanlc.js";import{L as o}from"./logs-lI5kXcWp.js";const t={userspace_tcp:"flow.userspaceTcp",userspace_udp:"flow.userspaceUdp",kernel_direct:"flow.kernelDirect",kernel_block:"flow.kernelBlock",dns_intercept:"flow.dnsIntercept",kernel_bypass:"flow.kernelBypass"},c={full:"flow.full",partial:"flow.partialVisibility",none:"ui.none"};function p(s,a,n){const o=Object.entries(s.coverage).filter(([,e])=>"full"!==e),p=e(s.dropped_records),i=p?a("flow.dropped",{n:p}):null;return o.length||i?{summary:o.length?a("flow.coverageSummary",{n:o.length}):null,detail:l(n,o.map(([e,l])=>a("ui.valuePair",{label:r(t,e,a),value:r(c,l,a)}))),dropped:i}:null}function i({view:e}){const l=s();return n.jsxs("div",{className:"rp-cluster",role:"group","aria-label":l("flow.coverage"),children:[e.summary&&n.jsx(a,{text:e.detail,children:n.jsx(o,{small:!0,tone:"warn",children:e.summary})}),e.dropped&&n.jsx(o,{small:!0,tone:"warn",children:e.dropped})]})}export{i as C,p as c};
@@ -1 +0,0 @@
import{r as e}from"./vendor-react-64fcanlc.js";import{t}from"./daeTokens-BVtuNnWZ.js";function a({text:a,as:r="span",className:s=""}){return e.createElement(r,{className:`rp-code ${s}`.trim()},t(a).map((t,a)=>t.type?e.createElement("span",{key:a,className:`rp-dae-${t.type}`},t.text):t.text))}export{a as D};
@@ -1 +0,0 @@
import{j as c}from"./vendor-react-64fcanlc.js";import{I as r}from"./index-DFPUSiWe.js";function l(l){return c.jsxs(r,{...l,children:[c.jsx("path",{fill:"currentColor",d:"M8.249 15.021c-.4 0-.733-.317-.748-.72l-.25-6.5c-.017-.414.307-.763.72-.779H8c.4 0 .733.317.748.72l.25 6.5c.017.414-.307.763-.72.778zM11.751 15.021h-.03c-.413-.016-.737-.365-.72-.779l.25-6.5c.015-.403.348-.72.748-.72h.03c.413.016.737.365.72.779l-.25 6.5c-.015.403-.348.72-.748.72"}),c.jsx("path",{fill:"currentColor",d:"M17 4h-3.5v-.75C13.5 2.01 12.49 1 11.25 1h-2.5C7.51 1 6.5 2.01 6.5 3.25V4H3c-.414 0-.75.336-.75.75s.336.75.75.75h.52l.422 10.342C3.99 17.052 4.98 18 6.19 18h7.62c1.211 0 2.2-.948 2.248-2.158L16.48 5.5H17c.414 0 .75-.336.75-.75S17.414 4 17 4m-9-.75c0-.413.337-.75.75-.75h2.5c.413 0 .75.337.75.75V4H8zm6.56 12.531c-.017.404-.346.719-.75.719H6.19c-.404 0-.733-.315-.75-.719L5.02 5.5h9.96z"})]})}export{l as D};
@@ -1 +0,0 @@
.rp-dns-answer{align-items:center;column-gap:var(--rp-space-3);row-gap:var(--rp-space-1);flex-wrap:wrap;display:flex}.rp-dns-answer>.rp-code{overflow-wrap:anywhere;flex:auto;min-width:0}.rp-dns-answer>:not(.rp-code){margin-inline-start:auto}.rp-dns-answer>.meta{gap:var(--rp-space-2);color:var(--rp-subtle);font-size:var(--rp-text-xs);white-space:nowrap;display:flex}
@@ -1 +0,0 @@
import{r as e,j as s}from"./vendor-react-64fcanlc.js";import{O as a,bW as t,V as n,aa as r,a3 as l,Z as i,U as d,aj as c,aM as o,u,ab as m,r as h,c7 as p,A as x,c9 as g,t as y,w as f,h as b,cb as j,ao as v,an as w,cg as _,ch as N,b5 as k,c6 as S,D as C,cd as T,aV as q,ci as M,bV as R,aA as L,ah as A,E as F,cj as E,bj as D,ck as P,C as W,c as O,S as U,ag as B,T as H,bJ as K,cl as I,ce as $,bK as V,cm as Q,B as J,cn as z,ay as G,bA as X,cf as Y}from"./index-DFPUSiWe.js";import{D as Z}from"./Delete-DIaM96Pb.js";import{s as ee,j as se,a as ae,l as te,C as ne,F as re,B as le,L as ie,W as de,m as ce,n as oe,D as ue}from"./activity-pUN0wcVd.js";import{i as me,I as he,a as pe}from"./ipLookup-47nEpvjK.js";import{u as xe,s as ge,k as ye,t as fe,v as be,i as je,S as ve,j as we,p as _e,B as Ne,K as ke,L as Se,A as Ce}from"./logs-lI5kXcWp.js";import{T as Te}from"./Tabs-HxpCyCLR.js";import{D as qe}from"./Table-6xnX6eUx.js";import{T as Me}from"./TimeCell-CRwMWdtu.js";import{c as Re,d as Le,e as Ae}from"./files-Dt8K7QfP.js";import{d as Fe}from"./nav-Bw7F3lhF.js";import{a as Ee}from"./rule-D1hMywhM.js";import{d as De,u as Pe}from"./nodes-HzIJdCpg.js";import{u as We,R as Oe,Q as Ue}from"./useQuickRule-BGNvNXBX.js";/* empty css */import"./_commonjsHelpers-CXUWDbkB.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./LinkOut-B50sd8cz.js";import"./Select-BFkb6iie.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./Virtualizer-BH6NJSXn.js";import"./Checkbox-xU-Ds7Hn.js";import"./DaeCode-DEjXqvav.js";import"./daeTokens-BVtuNnWZ.js";import"./AddCircle-V0Kluz-m.js";import"./SearchSelect-CsXd0DEh.js";import"./SearchList-C88Y6c53.js";import"./flows-Cd1KbGP2.js";function Be(e,s,t,n,r){return{domain:e,dip:null,sip:(r&&a(r))??null,outbound:null,matched:null,dns:{type:s,answers:Ee(t),upstream:n,query:{name:e,type:s}}}}const He=(e,s,a)=>({domain:null,dip:e,sip:null,outbound:null,matched:null,dns:{type:a,answers:[e],upstream:null,query:{name:s,type:a}}}),Ke={forced:"dns.route.forced","dns.routing":"dns.route.rules",default:"dns.route.default"};function Ie(e,s,a){return{fields:[[a("ui.state"),e.status],[a("ui.upstream"),e.upstream??"—"],[a("dns.routeSource"),n(Ke,e.route.source,a)],[a("dns.routeRule"),e.route.rule??"—"],[a("ui.elapsed"),r(e.elapsed_ms,a)]],answers:(e.answers??[]).map(e=>{const n=Ee([e])[0],r=n?t(n):void 0;return{data:e.data,name:(l=e.name,i=s,l.replace(/\.$/,"").toLowerCase()===i.replace(/\.$/,"").toLowerCase()?null:e.name),type:e.type,ttl:a("dns.ttl",{ttl:e.ttl}),lookup:r&&me(r)?r:null};var l,i}),cacheText:a(e.cached?"dns.hit":"dns.miss"),cacheTone:e.cached?void 0:"warn"}}function $e(e,s,a){return e?a instanceof o&&503===a.status?"unavailable":s?"ready":a?"error":"loading":"unlisted"}const Ve=[{id:"full",label:"rule.kind.domain"},{id:"suffix",label:"rule.kind.domainSuffix"},{id:"keyword",label:"rule.kind.domainKeyword"},{id:"regex",label:"dns.matchRegex"}],Qe=e=>e.trim().toLowerCase().replace(/\.$/,"");function Je(e,s){if(!s.trim())return()=>!0;if("regex"===e){const e=function(e){try{return new RegExp(e,"iu")}catch{return null}}(s);return e&&(s=>e.test(Qe(s)))}if("keyword"===e){const e=s.trim().toLowerCase();return s=>Qe(s).includes(e)}const a=Qe(s),t="suffix"===e&&a.startsWith("."),n=t?a.slice(1):a;return n?"full"===e?e=>Qe(e)===n:e=>{const s=Qe(e);return s.endsWith("."+n)||!t&&s===n}:null}function ze({go:s,query:a}){const t=u(),n=m(),r=h(),l=e.useMemo(()=>new URLSearchParams(a),[a]),[i,d]=e.useState(l.get("domain")??""),[c,o]=e.useState(l.get("type")??"A"),[v,w]=e.useState(!1);p(l.get("domain"),e=>d(e??"")),p(l.get("type"),e=>o(e??"A"));const[_,N]=e.useState(null),{busy:k,error:S,run:C}=x({rethrow:!0}),T=r.data?.resources,q=e.useMemo(()=>function(e,s,a,t,n,r){const l=s?.dns_query.record_types??[],i="all"===a?l.length>0:l.includes(a);return{types:l,choices:[...l.map(e=>({id:e,label:e})),{id:"all",label:r("dns.allTypes")}],disabled:n||!s?.dns_query.available||!i||!t.trim(),reason:!s||n?null:s.dns_query.available?i?null:r("all"===a?"dns.noTypes":"dns.typeUnsupported"):r("dns.queryUnavailable"),showCache:!!s?.dns_cache.available,cards:e?.results.map(s=>({id:s.type,title:`${e.domain} ${s.type}`,...Ie(s,e.domain,r),seed:Be(e.domain,s.type,s.answers??[],s.upstream,null),answerSeeds:(s.answers??[]).map(a=>Ee([a]).length?{address:a.data,seed:He(a.data,e.domain,s.type)}:null)}))??[],tabs:Fe(s).map(e=>({id:e.id,label:r(e.titleKey)}))}}(_,T,c,i,!!k,t),[_,T,c,i,k,t]),M=q.tabs.map(e=>e.id),R=l.has("domain")||l.has("device"),L=M.includes("log")?R?"log":"stats":M.includes("query")?"query":M[0]??"query",A=g(a,M,L),F=y(T,"config",{whileLoading:!1}),E=xe("query"===A&&F&&y(T,"dns_query",{whileLoading:!1})),D=e.useMemo(()=>F?function(e,s){const a=[...new Set((e??[]).flatMap(e=>e.content?De(e.content).map(Pe):[]))];return a.length?[{id:"",label:s("dns.upstreamAuto")},...a.map(e=>({id:e,label:e}))]:[]}(E.data?.sources,t):[],[F,E.data,t]),[P,W]=e.useState(""),O=D.some(e=>e.id===P)?P:"";Line truncated
@@ -1 +0,0 @@
import{r as n,j as t}from"./vendor-react-64fcanlc.js";import{u as e,g as r}from"./index-DFPUSiWe.js";import{aU as o,b1 as a,aY as i,a_ as l}from"./activity-pUN0wcVd.js";import{w as u,c,a as s}from"./array-C0VuvQyb.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./nodes-HzIJdCpg.js";import"./logs-lI5kXcWp.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";const f=Math.abs,h=Math.atan2,y=Math.cos,p=Math.max,d=Math.min,x=Math.sin,g=Math.sqrt,v=1e-12,m=Math.PI,M=m/2,A=2*m;function j(n){return n>=1?M:n<=-1?-M:Math.asin(n)}function w(n){return n.innerRadius}function b(n){return n.outerRadius}function P(n){return n.startAngle}function T(n){return n.endAngle}function R(n){return n&&n.padAngle}function L(n,t,e,r,o,a,i){var l=n-e,u=t-r,c=(i?a:-a)/g(l*l+u*u),s=c*u,f=-c*l,h=n+s,y=t+f,d=e+s,x=r+f,v=(h+d)/2,m=(y+x)/2,M=d-h,A=x-y,j=M*M+A*A,w=o-a,b=h*x-d*y,P=(A<0?-1:1)*g(p(0,w*w*j-b*b)),T=(b*A-M*P)/j,R=(-b*M-A*P)/j,L=(b*A+M*P)/j,I=(-b*M+A*P)/j,$=T-v,B=R-m,N=L-v,S=I-m;return $*$+B*B>N*N+S*S&&(T=L,R=I),{cx:T,cy:R,x01:-s,y01:-f,x11:T*(o/w-1),y11:R*(o/w-1)}}function I(){var n=w,t=b,e=c(0),r=null,o=P,a=T,i=R,l=null,s=u(p);function p(){var u,c,p,w=+n.apply(this,arguments),b=+t.apply(this,arguments),P=o.apply(this,arguments)-M,T=a.apply(this,arguments)-M,R=f(T-P),I=T>P;if(l||(l=u=s()),b<w&&(c=b,b=w,w=c),b>v)if(R>A-v)l.moveTo(b*y(P),b*x(P)),l.arc(0,0,b,P,T,!I),w>v&&(l.moveTo(w*y(T),w*x(T)),l.arc(0,0,w,T,P,I));else{var $,B,N=P,S=T,k=P,D=T,E=R,F=R,K=i.apply(this,arguments)/2,U=K>v&&(r?+r.apply(this,arguments):g(w*w+b*b)),_=d(f(b-w)/2,+e.apply(this,arguments)),q=_,z=_;if(U>v){var C=j(U/w*x(K)),H=j(U/b*x(K));(E-=2*C)>v?(k+=C*=I?1:-1,D-=C):(E=0,k=D=(P+T)/2),(F-=2*H)>v?(N+=H*=I?1:-1,S-=H):(F=0,N=S=(P+T)/2)}var V=b*y(N),Y=b*x(N),G=w*y(D),J=w*x(D);if(_>v){var O,Q=b*y(S),W=b*x(S),X=w*y(k),Z=w*x(k);if(R<m)if(O=function(n,t,e,r,o,a,i,l){var u=e-n,c=r-t,s=i-o,f=l-a,h=f*u-s*c;if(!(h*h<v))return[n+(h=(s*(t-a)-f*(n-o))/h)*u,t+h*c]}(V,Y,X,Z,Q,W,G,J)){var nn=V-O[0],tn=Y-O[1],en=Q-O[0],rn=W-O[1],on=1/x(((p=(nn*en+tn*rn)/(g(nn*nn+tn*tn)*g(en*en+rn*rn)))>1?0:p<-1?m:Math.acos(p))/2),an=g(O[0]*O[0]+O[1]*O[1]);q=d(_,(w-an)/(on-1)),z=d(_,(b-an)/(on+1))}else q=z=0}F>v?z>v?($=L(X,Z,V,Y,b,z,I),B=L(Q,W,G,J,b,z,I),l.moveTo($.cx+$.x01,$.cy+$.y01),z<_?l.arc($.cx,$.cy,z,h($.y01,$.x01),h(B.y01,B.x01),!I):(l.arc($.cx,$.cy,z,h($.y01,$.x01),h($.y11,$.x11),!I),l.arc(0,0,b,h($.cy+$.y11,$.cx+$.x11),h(B.cy+B.y11,B.cx+B.x11),!I),l.arc(B.cx,B.cy,z,h(B.y11,B.x11),h(B.y01,B.x01),!I))):(l.moveTo(V,Y),l.arc(0,0,b,N,S,!I)):l.moveTo(V,Y),w>v&&E>v?q>v?($=L(G,J,Q,W,w,-q,I),B=L(V,Y,X,Z,w,-q,I),l.lineTo($.cx+$.x01,$.cy+$.y01),q<_?l.arc($.cx,$.cy,q,h($.y01,$.x01),h(B.y01,B.x01),!I):(l.arc($.cx,$.cy,q,h($.y01,$.x01),h($.y11,$.x11),!I),l.arc(0,0,w,h($.cy+$.y11,$.cx+$.x11),h(B.cy+B.y11,B.cx+B.x11),I),l.arc(B.cx,B.cy,q,h(B.y11,B.x11),h(B.y01,B.x01),!I))):l.arc(0,0,w,D,k,I):l.lineTo(G,J)}else l.moveTo(0,0);if(l.closePath(),u)return l=null,u+""||null}return p.centroid=function(){var e=(+n.apply(this,arguments)+ +t.apply(this,arguments))/2,r=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-m/2;return[y(r)*e,x(r)*e]},p.innerRadius=function(t){return arguments.length?(n="function"==typeof t?t:c(+t),p):n},p.outerRadius=function(n){return arguments.length?(t="function"==typeof n?n:c(+n),p):t},p.cornerRadius=function(n){return arguments.length?(e="function"==typeof n?n:c(+n),p):e},p.padRadius=function(n){return arguments.length?(r=null==n?null:"function"==typeof n?n:c(+n),p):r},p.startAngle=function(n){return arguments.length?(o="function"==typeof n?n:c(+n),p):o},p.endAngle=function(n){return arguments.length?(a="function"==typeof n?n:c(+n),p):a},p.padAngle=function(n){return arguments.length?(i="function"==typeof n?n:c(+n),p):i},p.context=function(n){return arguments.length?(l=null==n?null:n,p):l},p}function $(n,t){return t<n?-1:t>n?1:t>=n?0:NaN}function B(n){return n}function N({label:u,rows:f}){const h=e(),[y,p]=r(Math.round,120),{tip:d,showAt:x,hide:g}=o(),[v,m]=n.useState(null),M=f.filter(n=>null!==n.value&&n.value>0),j=p?.width??0,w=p?.height??0,b=a(M.map(n=>n.value)),P=function(){var n=B,t=$,e=null,r=c(0),o=c(A),a=c(0);function i(i){var l,u,c,f,h,y=(i=s(i)).length,p=0,d=new Array(y),x=new Array(y),g=+r.apply(this,arguments),v=Math.min(A,Math.max(-A,o.apply(this,arguments)-g)),m=Math.min(Math.abs(v)/y,a.apply(this,arguments)),M=m*(v<0?-1:1);for(l=0;l<y;++l)(h=x[d[l]=l]=+n(i[l],l,i))>0&&(p+=h);for(null!=t?d.sort(function(n,e){return t(x[n],x[e])}):null!=e&&d.sort(function(n,t){return e(i[n],i[t])}),l=0,c=p?(v-y*M)/p:0;l<y;++l,g=f)u=d[l],f=g+((h=x[u])>0?h*c:0)+M,x[u]={data:i[u],index:l,value:h,startAngle:g,endAngle:f,padAngle:m};return x}return i.value=function(t){return arguments.length?(n="function"==typeof t?t:c(+t),i):n},i.sortValues=function(n){return arguments.length?(t=n,e=null,i):t},i.sort=function(n){return arguments.length?(e=n,t=null,i):e},i.startAngle=function(n){return arguments.length?(r="function"==typeof n?n:c(+nLine truncated
@@ -1 +0,0 @@
import{r as e,$ as t,a as r,j as n}from"./vendor-react-64fcanlc.js";import{eG as o,gj as i,ev as a,er as s,cZ as l,gk as d,gl as c,gm as p,ex as u,es as g,et as f,eu as m,eH as h,eS as y,gn as D,go as v,eT as b,eC as w,gp as x,g2 as T,fS as k,bt as E,cW as P,cO as K,f4 as O,I as S,aE as C,cq as I,eZ as j,aA as A,bw as R,B as M,g as B,u as L,R as F,cm as $,r as z,dd as N,bO as V,cI as H,dt as X,b7 as Y,m as q,gq as U,P as G,gr as J,D as W}from"./index-DFPUSiWe.js";import{b2 as _,b3 as Z,b4 as Q,b5 as ee,b6 as te,b7 as re,b8 as ne,b9 as oe,ba as ie,bb as ae,bc as se,bd as le,be as de,bf as ce,bg as pe,bh as ue,bi as ge,bj as fe,bk as me,bl as he,bm as ye,bn as De,bo as ve,bp as be,bq as we,br as xe,bs as Te,bt as ke,bu as Ee,bv as Pe,bw as Ke,bx as Oe,by as Se,bz as Ce,bA as Ie,bB as je,bC as Ae}from"./activity-pUN0wcVd.js";import{$ as Re,a as Me}from"./GridList-BuXKU7Uk.js";import{m as Be}from"./Select-BFkb6iie.js";import{D as Le}from"./Delete-DIaM96Pb.js";import{S as Fe,c as $e,$ as ze}from"./logs-lI5kXcWp.js";import{C as Ne}from"./AppearanceControls-Bpq6OQjk.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./nodes-HzIJdCpg.js";import"./ItemText-u3CUc_6I.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";var Ve=function(e){return e[e.none=0]="none",e[e.cancel=0]="cancel",e[e.move=1]="move",e[e.copy=2]="copy",e[e.link=4]="link",e[e.all=7]="all",e}({});const He={...Ve,copyMove:3,copyLink:6,linkMove:5,all:7,uninitialized:7},Xe=Ue(He);Xe[7]="all";const Ye={none:"cancel",link:"link",copy:"copy",move:"move"},qe=Ue(Ye);function Ue(e){let t={};for(let r in e)t[e[r]]=r;return t}const Ge=new Set(["text/plain","text/uri-list","text/html"]),Je="application/vnd.react-aria.items+json",We="application/octet-stream",_e=new WeakMap,Ze=Symbol();function Qe(e){let t=new Set;for(let r of e)for(let e of Object.keys(r))t.add(e);return t}function et(e){return e||(e="virtual"),"pointer"===e&&(e="virtual"),"virtual"===e&&"undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(pointer: coarse)").matches&&(e="touch"),e}function tt(){return et(o())}function rt(){return et(i())}class nt{constructor(e){this.types=new Set;let t=!1;for(let r of e.items)r.type!==Je&&("file"===r.kind&&(t=!0),r.type?this.types.add(r.type):this.types.add(We));this.includesUnknownTypes=!t&&e.types.includes("Files")}has(e){if(Array.isArray(e))return e.some(e=>this.has(e));if(this.includesUnknownTypes||e===Ze&&this.types.has(We)||"*/*"===e)return!0;if("string"==typeof e){if(e.endsWith("/*")){for(let t of this.types)if(t.startsWith(e.slice(0,-2)))return!0;return!1}return this.types.has(e)}return!1}}function ot(e){if(!e)throw new Error("No file provided");return{kind:"file",type:e.type||We,name:e.name,getText:()=>{return"function"==typeof(t=e).text?t.text():new Promise((e,r)=>{let n=new FileReader;n.onload=()=>{e(n.result)},n.onerror=r,n.readAsText(t)});var t},getFile:()=>Promise.resolve(e)}}function it(e){return{kind:"directory",name:e.name,getEntries:()=>async function*(e){let t,r=e.createReader();do{t=await new Promise((e,t)=>{r.readEntries(e,t)});for(let e of t)if(e.isFile){let t=await at(e);yield ot(t)}else e.isDirectory&&(yield it(e))}while(t.length>0)}(e)}}function at(e){return new Promise((t,r)=>e.file(t,r))}let st,lt={draggingKeys:new Set};function dt(e){lt.dropCollectionRef=e}function ct(){lt={draggingKeys:new Set}}function pt(e){let{draggingCollectionRef:t,dropCollectionRef:r}=lt;return null!=t?.current&&t.current===(e?.current||r?.current)}function ut(e){st=e}let gt=Ve.none;function ft(e){gt=e}let mt=new Map,ht=new Map,yt=null,Dt=new Set;function vt(e){return mt.set(e.element,e),yt?.updateValidDropTargets(),()=>{mt.delete(e.element),yt?.updateValidDropTargets()}}function bt(){let[t,r]=e.useState(yt);return e.useEffect(()=>{let e=()=>r(yt);return Dt.add(e),()=>{Dt.delete(e)}},[]),t}function wt(){return!!yt}const xt=["pointerdown","pointermove","pointerenter","pointerleave","pointerover","pointerout","pointerup","mousedown","mousemove","mouseenter","mouseleave","mouseover","mouseout","mouseup","touchstart","touchmove","touchend","focusin","focusout"],Tt=["pointerup","mouseup","touchend"],kt={keyboard:"dragStartedKeyboard",touch:"dragStartedTouch",virtual:"dragStartedVirtual"};class Et{constructor(e,t){this.validDropTargets=[],this.currentDropTarget=null,this.currentDropItem=null,this.dropOperation=null,this.mutationObserver=null,this.restoreAriaHidden=null,this.isVirtualClick=!1,this.dragTarget=e,this.stringFormatter=t,this.onKeyDown=this.onKeyDown.bind(this),this.onKeyUp=this.onKeyUp.bind(this),this.onFocus=this.onFocus.bind(this),this.onBlur=this.onBlur.bind(this),this.onClick=this.onClick.bind(this),this.onPointerDown=this.onPointerDown.bind(this),this.cancelEvent=this.cancelEvent.bind(this),this.initialFocused=!1}setup(){document.addEventListener("keydown",this.onKeyDown,!0),document.addEventListener("keyup",this.onKeyUp,!0),window.addEventListener("focus",this.onFocus,!0),window.addEventListener("blur",this.onBlur,!0)Line truncated
@@ -1 +0,0 @@
.rp-dashboard-body{display:contents}.rp-dashboard-tools{z-index:1;gap:var(--rp-space-2);border-radius:var(--rp-r-md);background:var(--rp-base);display:flex;position:absolute;inset-block-start:var(--rp-space-3);inset-inline-end:var(--rp-space-3)}.rp-dashboard-tools [slot=drag]{touch-action:none;cursor:grab}.rp-grip:before{content:"";inline-size:calc(var(--rp-space-1) / var(--rp-preview-scale,1));block-size:calc(var(--rp-space-6) / var(--rp-preview-scale,1));border-radius:var(--rp-r-pill);background:var(--rp-subtle);box-shadow:0 0 0 calc(var(--rp-space-half) / var(--rp-preview-scale,1)) var(--rp-base)}.rp-sortable-row{outline:none;min-inline-size:0;position:relative}.rp-sortable-row[data-focus-visible]{outline:none}.rp-sortable-row:is([data-focus-visible],[data-selected]):before{content:"";inset:calc(0px - var(--rp-sortable-gap,var(--rp-space-2)) - var(--rp-focus-width));border:var(--rp-focus-width) solid var(--rp-focus-color);pointer-events:none;position:absolute}.rp-sortable-grid{outline:none}.rp-sortable-row>[role=gridcell]{display:contents}.rp-sortable-grid[data-drop-target]{outline:2px dashed var(--rp-accent)}.rp-sortable-grid .rp-dashboard-tools{background:0 0;inset-block-start:calc(var(--rp-line-caption) / 2 - var(--rp-space-1));inset-inline-end:0;translate:0 -50%}.rp-sortable-grid .rp-widget-label{padding-inline-end:calc(var(--rp-control) + var(--rp-space-2))}.rp-sortable-row[data-module=divider] .rp-dashboard-tools{background:var(--rp-surface);padding-inline-start:var(--rp-space-1);inset-block-start:50%}.rp-widget-gallery-grid{gap:var(--rp-space-4);grid-template-columns:repeat(auto-fill,minmax(min(100%,320px),1fr));display:grid}.rp-widget-gallery-tile{gap:var(--rp-space-3);min-inline-size:0;padding:var(--rp-space-4);border:1px solid var(--rp-line);border-radius:var(--rp-r-lg);flex-direction:column;display:flex}.rp-widget-gallery-tile>.rp-cluster{margin-block-start:auto}.rp-widget-gallery-tile[data-added]{background:var(--rp-fill);--rp-subtle:var(--rp-subtle-text)}.rp-widget-gallery-frame{pointer-events:none;block-size:200px;overflow:hidden;mask-image:linear-gradient(#000 80%,#0000)}.rp-widget-gallery-presets{align-items:flex-start;gap:var(--rp-space-3) var(--rp-preset-gap);flex-wrap:wrap;display:flex}.rp-widget-gallery-preset{gap:var(--rp-space-1);flex-direction:column;min-inline-size:0;display:flex}.rp-widget-gallery-thumb{position:relative;overflow:hidden}.rp-widget-gallery-thumb-card{transform-origin:0 0;pointer-events:none;container:rp-tile/inline-size}.rp-widget-gallery-thumb-card:dir(rtl){transform-origin:100% 0}.rp-widget-gallery-thumb-add{border-radius:var(--rp-r-lg);cursor:pointer;background:0 0;border:0;position:absolute;inset:0}.rp-widget-gallery-thumb-add[data-hovered]{outline:2px solid var(--rp-line);outline-offset:2px}.rp-widget-gallery-thumb-add[data-focus-visible]{outline:2px solid var(--rp-accent);outline-offset:2px}.rp-widget-gallery-thumb-add[data-disabled]{cursor:default;outline:none}
@@ -1 +0,0 @@
import{r as e,j as t}from"./vendor-react-64fcanlc.js";import{V as s,a4 as n,a6 as r,a3 as a,h as i,ab as o,u as l,ao as d,an as c,r as m,c7 as u,ad as p,ae as v,dB as h,dC as j,T as x,ay as f,B as b,c as g,ce as w,E as k}from"./index-DFPUSiWe.js";import{e as y,Q as C,K as T,p as N,L as _}from"./logs-lI5kXcWp.js";import{D as S}from"./Table-6xnX6eUx.js";import{d as W,e as H}from"./files-Dt8K7QfP.js";import{i as R,D as K}from"./activity-pUN0wcVd.js";import{u as O,C as D}from"./Copy-DRn0jPgo.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./nodes-HzIJdCpg.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./Virtualizer-BH6NJSXn.js";import"./Checkbox-xU-Ds7Hn.js";const M={buffer_overflow:["event.gap.overflow","event.gapHelp.overflow"],evicted:["event.gap.evicted","event.gapHelp.evicted"]};function P(e,t){const s="flow.gap"===e.event&&Object.hasOwn(M,e.data.reason)?M[e.data.reason]:void 0;return s&&{title:t(s[0]),text:t(s[1])}}function V(e,t,s){const n=e=>e?[{id:"flow",label:t("event.viewFlow"),href:i("flows",{tab:"records",id:e})}]:[];switch(e.event){case"flow.updated":return n(e.data.resource_id);case"flow.gap":return[...n(e.data.resource_id),...s?.runtime_settings.available?[{id:"recording",label:t("ui.recordingSettings"),href:R}]:[]];case"generation.changed":return s?.config.available?[{id:"config",label:t("event.viewConfig"),href:i("config")}]:[];default:return[]}}const B=new WeakMap;function F(e,t,i,o,l,d,c,m,u=!1,p=()=>!1,v){const h="without-runtime"===t||m.includes(t)?t:"all",j=e.filter(e=>"all"===h||("without-runtime"===h?"runtime.updated"!==e.event:e.event===h)).slice(0,l);return{kind:h,rows:j.map(e=>function(e,t,i,o,l){const d=B.get(e);if(d&&d.locale===t&&d.resources===l)return d.row;const c=o?{key:"event.lostHistory"}:r(e,i),m={id:y(e),timestamp:a(e.data.observed_at,t),iso:e.data.observed_at,kind:e.event,kindText:s(n,e.event,i),summary:i(c.key,c.params),help:P(e,i),links:V(e,i,l),event:e};return B.set(e,{locale:t,resources:l,row:m}),m}(e,d,c,p(e),v)),kinds:[{id:"without-runtime",label:c("event.withoutRuntime")},{id:"all",label:c("event.allKinds")},...m.map(e=>({id:e,label:s(n,e,c)}))],status:u&&!1!==o?{tone:"err",text:c("event.disconnected")}:{tone:i?"ok":null===o?"muted":"warn",text:c(!1===o?"event.unavailable":i?"event.connected":null===o?"ui.loading":"event.reconnecting")},limitText:c("event.limit",{n:l}),shown:j}}function J(){const t=o(),s=l(),n=d[c()],r=m(),a=r.data?.resources,i=a?.events.kinds,[x,f]=e.useState("without-runtime"),b="all"!==x&&"runtime.updated"!==x&&i?.includes(x),g=C("without-runtime"!==x&&!b),w=e.useMemo(()=>F(g.events,x,g.connected,g.available,j,n,s,i??[],!!g.error,h,a),[g.events,x,g.connected,g.available,g.error,n,s,i,a]);return u(w.kind,f),{...w,setKind:f,cursor:g.cursor,error:g.error,retry:()=>{p().then(()=>v(t))},loading:!g.error&&!g.connected&&!1!==g.available&&!g.events.length,export:()=>{return W(H("doona-events","json"),(e=w.shown,JSON.stringify(e,null,2)+"\n"),"application/json");var e}}}function L(){const s=l(),n=J(),r=O(),a=e.useMemo(()=>[{id:"t",label:s("ui.time"),minWidth:212,grow:0,drop:1,render:e=>t.jsx(x,{className:"rp-code",text:e.iso,children:e.timestamp})},{id:"k",label:s("event.kind"),minWidth:224,drop:2,render:e=>t.jsx(x,{text:e.kind,children:e.kindText})},{id:"m",label:s("event.summary"),minWidth:176,isRowHeader:!0,render:e=>t.jsx(f,{fill:!0,help:e.help,children:t.jsx(x,{children:e.summary})})},{id:"actions",actions:!0,label:s("ui.actions"),hideLabel:!0,minWidth:64,grow:0,render:e=>t.jsx(b,{small:!0,quiet:!0,icon:!0,label:s("ui.copyRecord"),onPress:()=>{r((e=>JSON.stringify(e,null,2))(e.event))},children:t.jsx(D,{})})}],[s,r]),i=e.useCallback(e=>t.jsxs(t.Fragment,{children:[t.jsx(T,{inline:!0,items:[[s("ui.time"),e.timestamp],[s("event.kind"),e.kindText],[s("event.summary"),e.summary]]}),e.links.length>0&&t.jsx("div",{className:"rp-cluster",children:e.links.map(e=>t.jsx(g,{appearance:"button",href:e.href,children:e.label},e.id))})]}),[s]);return t.jsxs("div",{className:"rp-page",children:[t.jsxs(w,{page:!0,children:[t.jsx(N,{label:s("event.kind"),side:!0,value:n.kind,onChange:n.setKind,items:n.kinds}),t.jsx(_,{small:!0,tone:n.status.tone,children:n.status.text}),n.cursor&&t.jsx(x,{text:n.cursor,children:t.jsx("span",{className:"rp-label",children:s("event.cursor")})}),t.jsx("span",{className:"rp-label",children:n.limitText}),t.jsx("span",{className:"rp-grow"}),t.jsxs(b,{isDisabled:!n.rows.length,onPress:n.export,children:[t.jsx(K,{}),s("event.export")]})]}),n.error&&t.jsx(k,{error:n.error,onRetry:n.retry}),t.jsx(S,{label:s("nav.events"),stream:!0,flow:!0,loading:n.loading,rows:n.rows,empty:s("event.empty"),cols:a,detail:i})]})}export{L as Events};
@@ -1 +0,0 @@
import{r as e,j as a}from"./vendor-react-64fcanlc.js";import{u as s,an as l,cF as o,ao as t,du as n,b7 as i,B as r}from"./index-DFPUSiWe.js";import{S as c}from"./SearchSelect-CsXd0DEh.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./SearchList-C88Y6c53.js";import"./Virtualizer-BH6NJSXn.js";function m({name:i,value:r,automaticFlag:m,onChange:d}){const f=s(),g=l(),p=e.useMemo(()=>o(t[g]),[g]),j=n(i),u=p.find(e=>e.flag===m),h=u?f("flags.detected",{region:u.label}):f("flags.automatic");return a.jsx("div",{className:"rp-list",children:a.jsx(c,{label:f("flags.region"),description:f(j?"flags.embedded":"flags.help"),isDisabled:j,searchLabel:f("flags.search"),value:j?"automatic":r,onChange:d,sections:[{id:"flags",items:[{id:"automatic",label:h,flag:m},{id:"none",label:f("flags.none"),flag:null},...p]}]})})}function d({name:e,onClose:l,...o}){const t=s();return a.jsxs(i,{narrow:!0,title:t("flags.edit"),isOpen:!0,onOpenChange:e=>{e||l()},footer:()=>a.jsx(r,{onPress:l,children:t("ui.close")}),children:[a.jsx("p",{className:"rp-note",children:e}),a.jsx(m,{name:e,...o})]})}export{m as FlagField,d as FlagPicker};
@@ -1 +0,0 @@
.rp-topology{min-width:0;overflow:hidden}.rp-tree{gap:var(--rp-space-2);overscroll-behavior-x:contain;flex-direction:column;min-width:0;display:flex;overflow-x:auto}.rp-tree-hint{color:var(--rp-subtle);font-size:var(--rp-text-xs);line-height:16px;display:none}@media (width<=599.98px){.rp-tree-hint{display:block}}.rp-tree-captions{height:16px;position:relative}.rp-tree-captions>.rp-label{text-transform:uppercase;letter-spacing:.04em;white-space:nowrap;position:absolute;top:0}.rp-tree-canvas{position:relative}.rp-tree-links{pointer-events:none;fill:none;stroke:var(--rp-muted);stroke-linecap:round;position:absolute;inset:0;overflow:visible}.rp-tree-links path{opacity:.55;transition:opacity var(--rp-duration) var(--rp-ease)}.rp-tree-links path[data-kind=direct]{stroke:var(--rp-positive)}.rp-tree-links path[data-kind=block]{stroke:var(--rp-negative)}.rp-tree-links path[data-kind=group]{stroke:var(--rp-iris)}.rp-tree-links path[data-kind=node]{stroke:var(--rp-accent)}.rp-tree-links path[data-state=active]{opacity:1}.rp-tree-links path[data-state=dim]{opacity:.1}.rp-tree-tile{box-sizing:border-box;align-items:center;gap:var(--rp-space-2);height:36px;padding:0 var(--rp-space-2h);border-radius:var(--rp-r-md);background:var(--rp-overlay);color:var(--rp-text);font:inherit;font-size:var(--rp-text-sm);text-align:start;cursor:pointer;border:2px solid #0000;line-height:16px;display:flex;position:absolute}.rp-tree-tile.dim{opacity:.35}.rp-tree-tile[data-hovered]{background:var(--rp-hl-med)}.rp-tree-tile[data-selected]{border-color:var(--rp-accent);background:var(--rp-selected)}.rp-tree-tile[data-focus-visible]{outline:var(--rp-focus-width) solid var(--rp-focus-color);outline-offset:calc(-1 * var(--rp-focus-width));border-color:#0000}.rp-tree-tile .l{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.rp-tree-tile[data-stage=rule] .l{font-family:var(--rp-font-mono);font-size:var(--rp-text-xs);font-variant-ligatures:none}.rp-tree-tile .s{font-size:var(--rp-text-xs);color:var(--rp-subtle);margin-inline-start:var(--rp-space-2)}.rp-tree-tile .s.ok{color:var(--rp-positive-text)}.rp-tree-tile .s.warn{color:var(--rp-notice-text)}.rp-tree-tile .s.err{color:var(--rp-negative-text)}.rp-tree-tile .c{font-variant-numeric:tabular-nums;color:var(--rp-subtle);flex:none;font-weight:700}.rp-cell-wrap>.rp-rule{row-gap:var(--rp-space-1);flex-wrap:wrap}.rp-cell-wrap>.rp-rule>.rp-truncate{white-space:normal;overflow-wrap:anywhere}.rp-table-cards{overscroll-behavior:contain;outline:none;overflow:auto}.rp-table-cards .rp-cardview-card[data-focus-visible]{outline-offset:calc(var(--rp-focus-width) * -1)}.rp-table-cards .rp-truncate{white-space:normal;overflow-wrap:anywhere}.rp-table-cards :is(.rp-rule,.rp-chain){flex-wrap:wrap}.rp-table-cards .rp-rule>span{white-space:normal}
@@ -1 +0,0 @@
import{r as e,j as t}from"./vendor-react-64fcanlc.js";import{N as n,O as a}from"./NodePath-DCKKYRsk.js";import{l as o,q as s,B as r,c as l,g as i,K as d,R as u,S as c,p}from"./logs-lI5kXcWp.js";import{a8 as m,O as f,a5 as g,J as w,H as h,c9 as b,cb as j,w as x,c1 as v,V as y,c3 as k,c4 as _,ao as M,a3 as S,h as N,b1 as L,o as C,aa as T,Z as P,u as R,an as F,j as O,B as H,N as q,b9 as E,r as $,t as W,b5 as A,c6 as D,b6 as I,c8 as B,S as V,ah as U,aH as J,E as K,T as z,ce as G,aF as Q,c as Z,bA as X,C as Y}from"./index-DFPUSiWe.js";import{g as ee,d as te,h as ne,o as ae,i as oe,r as se}from"./activity-pUN0wcVd.js";import{w as re}from"./labels-D8xxfRTQ.js";import{c as le,C as ie}from"./Coverage-BRhvKmi_.js";import{f as de}from"./nav-CISoRdvs.js";import{T as ue}from"./Tabs-HxpCyCLR.js";import{s as ce,D as pe}from"./Table-6xnX6eUx.js";import{T as me}from"./TimeCell-CRwMWdtu.js";import{a as fe,u as ge,b as we}from"./flows-Cd1KbGP2.js";import{u as he}from"./outbounds-BMHzxQr8.js";import{u as be,Q as je,R as xe}from"./useQuickRule-BGNvNXBX.js";import{$ as ve}from"./GridList-BuXKU7Uk.js";import{$ as ye,a as ke}from"./Virtualizer-BH6NJSXn.js";import{C as _e}from"./CardView-CwlxcqMf.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Tag-BZqiYh1Z.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./nodes-HzIJdCpg.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./Checkbox-xU-Ds7Hn.js";import"./DaeCode-DEjXqvav.js";import"./daeTokens-BVtuNnWZ.js";import"./AddCircle-V0Kluz-m.js";import"./SearchSelect-CsXd0DEh.js";import"./SearchList-C88Y6c53.js";import"./rule-D1hMywhM.js";const Me=["client","rule","outbound","node"];function Se(e,t){if(e.rule_id)return null!==e.rule_generation_id&&e.rule_generation_id===t?e.rule_id:e.rule_id+"\0"+e.rule_expression}function Ne(e,t,n,a){switch(t){case"client":{const t=f(e.input?.src??void 0);return{label:t??"unknown",unknown:!t}}case"rule":return e.rule_expression?{label:e.rule_expression,unknown:!1,key:Se(e,a)}:{label:"unknown",unknown:!0};case"outbound":return e.outbound?{label:e.outbound,unknown:!1}:{label:"unknown",unknown:!0};case"node":{if(m(e.outbound))return null;const t=e.chain.at(-1)??null;return t?{label:n.get(t)??t,unknown:!1,key:t}:{label:"unknown",unknown:!0}}}}const Le=(e,t)=>te(e,t.unknown?"":t.key??t.label),Ce=e=>ee(e,Me)?.kind,Te=new Map;function Pe(e,t,n){const a=Ce(t);return a?e.filter(e=>{const o=Ne(e,a,Te,n?.generation_id);return null!==o&&Le(a,o)===t}):[]}function Re(e,t,n,a){const o=ee(e,Me);if(!o||""===o.value)return a(null);const{kind:s,value:r}=o;return"rule"===s?r.includes("\0")?r.slice(r.indexOf("\0")+1):t.find(e=>e.rule_id===r)?.expression??r:"node"===s?n.get(r)??r:"client"===s?r:a(r)}function Fe(e,t,n,a,o="rule"){const s=(e=>new Map(e.map(e=>[e.id,e.name])))(n),r=new Map(t.map(e=>[e.id,e])),l=new Map(t.map(e=>[e.name,e])),i=new Map(n.map(e=>[e.id,e])),d=new Map,u=new Map,c=new Map,p=new Map,f=(e,t)=>{const n=e+">"+t;let a=p.get(n);return a||p.set(n,a={source:e,target:t,count:0}),a},b=(e,t,n=!1)=>{const a=Le("node",{key:e,label:t,unknown:n});let o=c.get(a);if(!o){const s=n?void 0:i.get(e),r=s&&w(s);c.set(a,o={id:a,label:t,count:0,unknown:n||void 0,latency:h(r),unavailable:"unavailable"===r?.state})}return o},j=(e,t=!1)=>{const n=Le("outbound",{label:e,unknown:t});let a=u.get(n);if(a)return a;const o=t?void 0:l.get(e),d=t?{groups:[],member:null}:g(e,o?.selection.tcp_member_id?"tcp":"udp",l,r,i),c=d.groups.map(e=>({name:e.name,kind:e.policy.kind,policy:e.policy.native})),p=d.member;return a={id:n,label:e,count:0,unknown:t||void 0,kind:t?"unknown":m(e)?e:c.length?"group":"unknown",groups:c,node:p&&b(p,s.get(p)??p).id},u.set(n,a),a.node&&f(n,a.node),a},x=(e,t,n=!1)=>{let a=d.get(e);return a||d.set(e,a={id:e,label:t,count:0,unknown:n||void 0,outbound:null,must:!1}),a};if("rule"===o)for(const m of a?.rules??[]){const e=x(Le("rule",{key:m.rule_id,label:m.expression}),m.expression);e.must=m.must,m.outbound&&(e.outbound=j(m.outbound).id,f(e.id,e.outbound))}const v=[o,"outbound","node"];for(const m of e){let e;for(const t of v){const n=Ne(m,t,s,a?.generation_id);if(!n)break;const o="outbound"===t?j(n.label,n.unknown):"node"===t?b(n.key??n.label,n.label,n.unknown):x(Le(t,n),n.label,n.unknown);o.count++,e&&f(e.id,o.id).count++,e=o}}for(const m of t)j(m.name);const y=new Set([...d.values()].map(e=>e.outbound)),k=new Set([...u.values()].flatMap(e=>e.groups.slice(1).map(e=>e.name))),_=[...u.values()].filter(e=>e.count||y.has(e.id)||!k.has(e.label)),M=new Set(_.map(e=>e.id)),S=[...p.values()].filter(e=>M.has(e.source)||M.has(e.target)),N=new Set(S.map(e=>e.target));return{by:o,leaves:[...d.values()],outbounds:_,nodes:[...c.values()].filter(e=>N.has(e.id)),links:S}}const Oe=new WeakMap;function He(e){const t=Oe.get(e);if(t)return t;const n=new Map,a=new Map,o=new Map;for(const l of e.links){for(const[e,t]of[[n,l.source],[a,l.target]]){let n=e.get(t);n||e.set(t,n=[]),n.push(l)}(!o.has(l.source)||o.get(l.souLine truncated
@@ -1 +0,0 @@
import{fG as e,cW as t,cP as r,cO as o,bt as a,fI as i,e0 as l,eE as n,eF as s,ew as d,ex as c,ey as u,cX as p,ez as g,eB as f,eC as y,cZ as v,eA as b,er as h,e5 as m,fL as k,db as D,ea as w,de as E,dd as P,eI as M,d4 as x,d5 as S,d6 as A,eQ as K,d8 as C,ef as T,eK as N,eM as B,e6 as I,eR as F,d0 as R,e7 as L,e4 as V,eq as H,eL as O,eN as U,da as z,eW as $,eX as G,ee as W}from"./index-DFPUSiWe.js";import{a as j,$ as _,b as q,d as X,e as Q,f as Z}from"./useGridSelectionCheckbox-wMhI-U-R.js";import{k as J,f as Y,e as ee,l as te,g as re,i as oe,h as ae}from"./Select-BFkb6iie.js";import{r as ie,$ as le}from"./vendor-react-64fcanlc.js";const ne=new WeakMap;function se(e,t){let{id:r}=ne.get(e)??{};if(!r)throw new Error("Unknown list");return`${r}-${function(e){return"string"==typeof e?e.replace(/\s*/g,""):""+e}(t)}`}const de={ltr:"ArrowRight",rtl:"ArrowLeft"},ce={ltr:"ArrowLeft",rtl:"ArrowRight"};function ue(e,t,r){let{node:m,isVirtualized:k,focusMode:D="row",allowsArrowNavigation:w}=e,{direction:E}=a(),{onAction:P,linkBehavior:M,keyboardNavigationBehavior:x,shouldSelectOnPressUp:S}=ne.get(t),A=i(),K=ie.useRef(null),C=()=>{if(null!==r.current){if("child"===D){if(d(r.current)&&r.current!==c(u(r.current)))return;let e=p(r.current,{tabbable:!0}).firstChild();if(e)return g(e),void f(e,{containingElement:y(e)})}(null!=K.current&&m.key!==K.current||!d(r.current))&&g(r.current)}},T={},N=e.hasChildItems,B=t.selectionManager.isLink(m.key);if(null!=m&&"expandedKeys"in t){let e=t.collection.getChildren?.(m.key);N=N||[...e??[]].length>1,null==P&&!B&&"none"===t.selectionManager.selectionMode&&N&&(P=()=>t.toggleKey(m.key));let r=N?t.expandedKeys.has(m.key):void 0,o=1,a=m.index;if(m.level>=0&&null!=m?.parentKey){let e=t.collection.getItem(m.parentKey);if(e){let r=function(e,t){let r=t.getChildren?.(e.key),o=r?Array.from(r):[],a=o.length>0?o[0]:null,i=[];for(;a;)i.push(a),a=null!=a.nextKey?t.getItem(a.nextKey):null;return i}(e,t.collection);o=[...r].filter(e=>"item"===e.type).length,a>0&&"item"!==r[0].type&&(a-=1)}}else o=[...t.collection].filter(e=>0===e.level&&"item"===e.type).length;T={"aria-expanded":r,"aria-level":m.level+1,"aria-posinset":a+1,"aria-setsize":o}}let{itemProps:I,...F}=l({selectionManager:t.selectionManager,key:m.key,ref:r,isVirtualized:k,shouldSelectOnPressUp:e.shouldSelectOnPressUp||S,onAction:P||m.props?.onAction?n(m.props?.onAction,P?()=>P(m.key):void 0):void 0,focus:C,linkBehavior:M}),R=s(m.props),L=F.hasAction?R:{},V=o(I,L,{role:"row",onKeyDownCapture:"arrow"===x||w?e=>{let o=c(u(r.current));if(!h(e.currentTarget,v(e))||!r.current||!o)return;let a=p(r.current);if(a.currentNode=o,!pe(e,t,m,N,E,o,r.current,w))switch(e.key){case"ArrowLeft":if("arrow"===x){let t="rtl"===E?a.nextNode():a.previousNode();if(t)e.preventDefault(),e.stopPropagation(),g(t),f(t,{containingElement:y(r.current)});else if(e.preventDefault(),e.stopPropagation(),"rtl"===E)g(r.current),f(r.current,{containingElement:y(r.current)});else{a.currentNode=r.current;let e=ge(a);e&&(g(e),f(e,{containingElement:y(r.current)}))}}break;case"ArrowRight":if("arrow"===x){let t="rtl"===E?a.previousNode():a.nextNode();if(t)e.preventDefault(),e.stopPropagation(),g(t),f(t,{containingElement:y(r.current)});else if(e.preventDefault(),e.stopPropagation(),"ltr"===E)g(r.current),f(r.current,{containingElement:y(r.current)});else{a.currentNode=r.current;let e=ge(a);e&&(g(e),f(e,{containingElement:y(r.current)}))}}break;case"ArrowUp":case"ArrowDown":!e.altKey&&h(r.current,v(e))&&(e.stopPropagation(),e.preventDefault(),r.current.parentElement?.dispatchEvent(new KeyboardEvent(e.nativeEvent.type,e.nativeEvent)))}}:void 0,onFocus:e=>{K.current=m.key,v(e)===r.current?"child"===D&&e.relatedTarget&&h(r.current,e.relatedTarget)||requestAnimationFrame(()=>{"child"===D&&c(u(r.current))===r.current&&C()}):b()||t.selectionManager.setFocusedKey(m.key)},"aria-label":m["aria-label"]||m.textValue||void 0,"aria-selected":t.selectionManager.canSelectItem(m.key)?t.selectionManager.isSelected(m.key):void 0,"aria-disabled":t.selectionManager.isDisabled(m.key)||void 0,"aria-labelledby":A&&(m["aria-label"]||m.textValue)?`${se(t,m.key)} ${A}`:void 0,id:se(t,m.key)});"child"===D&&w&&"tab"===x&&(V.tabIndex=-1);let H=V.onKeyDown;if(V.onKeyDown=e=>{(e=>{let o=c(u(r.current));if(h(e.currentTarget,v(e))&&r.current&&o){if("tab"===x){if(v(e)!==r.current&&"Tab"!==e.key)return void e.stopPropagation();if(pe(e,t,m,N,E,o,r.current,w))return}if("Tab"===e.key&&"tab"===x){let t=p(r.current,{tabbable:!0});t.currentNode=o,(e.shiftKey?t.previousNode():t.nextNode())&&e.stopPropagation()}}})(e),e.isPropagationStopped()||H?.(e)},k){let{collection:e}=t,r=[...e];V["aria-rowindex"]=r.find(e=>"section"===e.type)?[...e.getKeys()].filter(t=>"section"!==e.getItem(t)?.type).findIndex(e=>e===m.key)+1:m.index+1}return{rowProps:{...o(V,T)},gridCellProps:{role:"gridcell","aria-colindex":1},descriptionProps:{id:A},...F}}function pe(e,t,r,o,a,i,l,n){if(!("expandedKeys"in t)||!n&&i!==l)return!1;if(e.key===de[a]&&t.selectLine truncated
@@ -1 +0,0 @@
import{j as s}from"./vendor-react-64fcanlc.js";import{I as e,cw as a,N as l,T as c}from"./index-DFPUSiWe.js";function r(a){return s.jsx(e,{...a,children:s.jsx("path",{fill:"currentColor",d:"M7.864 15.734c-.222 0-.433-.098-.576-.27l-3.747-4.497c-.266-.319-.222-.792.096-1.057.317-.265.79-.223 1.056.096l3.154 3.786 7.44-9.469c.255-.326.728-.382 1.052-.127.326.256.383.728.127 1.053L8.454 15.447c-.14.179-.352.284-.579.287z"})})}const t=()=>s.jsx(r,{className:"rp-check-mark"}),i=({i:e,cut:a,reserveFlag:r=!1})=>{const t="path"===a?e.label.lastIndexOf("/")+1:0;return s.jsxs("span",{className:"rp-il",children:[(e.flag||r&&void 0!==e.flag)&&s.jsx("span",{className:"rp-node-flag","data-flag":e.flag,"aria-hidden":"true"}),e.icon&&s.jsx("span",{className:"ic",children:e.icon}),e.nodeName?s.jsx(l,{name:e.label,cut:"start"===a?a:void 0}):t?s.jsxs(c,{className:"rp-path",text:e.label,children:[s.jsx("span",{className:"rp-truncate rp-truncate-start",children:s.jsx("bdi",{children:e.label.slice(0,t)})}),s.jsx("bdi",{children:e.label.slice(t)})]}):s.jsx(c,{cut:"start"===a?a:void 0,tooltipText:e.label,children:e.label})]})},n=({i:e,children:l})=>s.jsxs("span",{className:"rp-item-text",children:[s.jsx(a,{slot:"label",elementType:"span",children:s.jsx(i,{i:e,reserveFlag:!0})}),l??(e.desc&&s.jsx(a,{slot:"description",className:e.aside?"desc aside":"desc",children:e.desc}))]});export{r as C,i as I,t as a,n as b};
@@ -1 +0,0 @@
import{j as s}from"./vendor-react-64fcanlc.js";import{I as r,b9 as e}from"./index-DFPUSiWe.js";function o({className:o,...t}){return s.jsx(r,{width:10,height:10,viewBox:"0 0 12 12",className:e("rp-link-out",o),...t,children:s.jsx("path",{fill:"currentColor",d:"m10.08887,1h-5.51074c-.50293,0-.91113.4082-.91113.91113s.4082.91113.91113.91113h3.31055L1.35547,9.35547c-.35645.35645-.35645.93262,0,1.28906.17773.17773.41113.2666.64453.2666s.4668-.08887.64453-.2666l6.5332-6.5332v3.31055c0,.50293.4082.91113.91113.91113s.91113-.4082.91113-.91113V1.91113c0-.50293-.4082-.91113-.91113-.91113Z"})})}export{o as L};
@@ -1 +0,0 @@
import{r as e,j as n}from"./vendor-react-64fcanlc.js";import{u as t,an as r,bB as s,bC as o,aM as a,bD as i,aV as l,bE as c,bF as u,bG as d,bH as p,c as m,h as g,C as f,b8 as h,E as x,bI as w,bJ as k,bm as j,bK as y,B as b}from"./index-DFPUSiWe.js";import{D as v,s as T,d as S,a as E,o as N}from"./auth-Br_GGGEt.js";import{L as C}from"./LinkOut-B50sd8cz.js";import{L,P as D,S as _}from"./AppearanceControls-Bpq6OQjk.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./ItemText-u3CUc_6I.js";const q=/^[A-Za-z0-9_.-]{1,64}$/;async function A(e,n){try{return T(await S(e,n))}catch(t){if(!function(e){return e instanceof SyntaxError||e instanceof a&&(404===e.status||401===e.status&&(!e.code||"authentication_required"===e.code))}(t))throw t;return t instanceof a&&404===t.status?await E(e,n)?"token":"no-api":"token"}}function I(e,n,t){try{const{profiles:r,activeId:s}=u(),o=function(e,n,t,r){return e.some(e=>e.id===n&&p(e.api)===p(t))?e.map(e=>e.id===n?{...e,token:r.trim()}:e):null}(r,e,n,t);if(!o)return"login.stale";d({profiles:o,activeId:s})}catch{return"settings.saveError"}return null}async function P(e,n,t,r){let s;try{s=await N(n,t,r)}catch(o){return function(e,n){if(!(e instanceof a))return null;if("invalid_credentials"===e.code)return{key:"login.invalidCredentials"};if("setup_required"===e.code)return{key:"login.needsSetup",switchTo:"setup"};if("setup_already_completed"===e.code)return{key:"login.alreadySetUp",switchTo:"login"};if("permission_denied"===e.code&&"setup"===n)return{key:"login.setupPeer"};if("rate_limited"===e.code){const n=e.retryAfter??60;return{key:"login.rateLimited",params:{n:n},wait:n}}return null}(o,t)??{error:o}}try{c(e,n,s.token)}catch{return{key:"settings.saveError"}}return null}function H(e,n){const{kind:t,failure:r,attempt:s,ended:o,rejected:a}=e;return"no-api"===t?null:null!==r?{tone:"negative",text:"string"==typeof r?r:n(r.key,r.params),focus:!0,id:s}:o?{tone:"informative",text:n("login.sessionEnded"),focus:!1,id:0}:a&&"token"===t?{tone:"negative",text:n("login.rejected"),focus:!1,id:0}:null}const R=e=>"string"!=typeof e&&"login.rateLimited"===e.key;function F(n,a,c,u,d){const p=t(),m=r(),[g]=e.useState(()=>s(n,a)),[f,h]=e.useState(null),x=d?"no-api":f,[w,k]=e.useState({attempt:0,error:null});e.useEffect(()=>{if(d)return;const e=new AbortController;return A(a,e.signal).then(e=>h(e),n=>{e.signal.aborted||k(e=>({...e,error:n instanceof Error?n:new Error(String(n))}))}),()=>e.abort()},[a,w.attempt,d]);const[j,y]=e.useState(""),b=i(a),[T,S]=e.useState(b?v.username:""),[E,N]=e.useState(b?v.password:""),[C,L]=e.useState(""),[D,_]=e.useState(!1),[F,M]=e.useState(!1),[U,B]=e.useState({}),[O,z]=e.useState(null),[G,J]=e.useState(0),[K,V]=e.useState(0),[Z,$]=e.useState(Date.now);e.useEffect(()=>{if(K<=Z)return;const e=setTimeout(()=>$(Date.now()),(K-Z)%1e3||1e3);return()=>clearTimeout(e)},[K,Z]);const Q=function(e,n){return Math.max(0,Math.ceil((e-n)/1e3))}(K,Z),W=(e,n)=>t=>{n(t),U[e]&&B(({[e]:n,...t})=>t)},X=async e=>{const t=function(e,n,t,r){const s={};n?q.test(n)||(s.username="login.badUsername"):s.username="login.usernameRequired";const o=[...t].length;return o?"setup"===e&&o<8?s.password="login.passwordShort":o>128||(new TextEncoder).encode(t).length>512?s.password="login.passwordLong":"setup"===e&&t!==r&&(s.confirm="login.mismatch"):s.password="login.passwordRequired",s}(e,T,E,C);if(B(t),Object.keys(t).length)return;M(!0);const r=await P(n,a,e,{username:T,password:E});if(r)if(M(!1),"error"in r)z(p("login.failed",{error:l(r.error,p)}));else{if(r.wait){const e=Date.now();$(e),V(e+1e3*r.wait)}r.switchTo&&(h(r.switchTo),L("")),z(r)}else location.reload()},Y="setup"===x||"login"===x,ee=e=>U[e]?p(U[e]):void 0;return{kind:x,discoveryError:w.error,retryDiscovery:()=>k(e=>({attempt:e.attempt+1,error:null})),title:p("no-api"===x?"login.noApiTitle":"setup"===x?"login.setupTitle":"token"===x?"login.title":"login.passwordTitle"),note:"no-api"===x?p("login.noApiNote"):p("setup"===x?"login.setupNote":"login"===x?"login.connectedTo":"login.note",{backend:c}),demoNote:b&&"login"===x?p("login.demoNote",v):null,alert:H({kind:x,failure:O&&!Q&&R(O)?null:O,attempt:G,ended:g,rejected:u},p),busy:F,token:j,setToken:y,username:T,setUsername:W("username",S),usernameError:ee("username"),password:E,setPassword:W("password",N),passwordError:ee("password"),confirm:C,setConfirm:W("confirm",L),confirmError:ee("confirm"),submit:()=>{F||Q||null===x||"no-api"===x||(z(null),J(e=>e+1),Y?X(x):(()=>{if(!j.trim())return;const e=I(n,a,j);e?z({key:e}):location.reload()})())},canSubmit:null!==x&&"no-api"!==x&&!Q&&(Y||!!j.trim()),submitText:Q?p("login.retryIn",{n:Q}):p("setup"===x?"login.create":"login"===x?"login.signIn":"login.submit"),secretType:D?"text":"password",toggle:()=>_(e=>!e),toggleText:p(Y?D?"login.hidePassword":"login.showPassword":D?"settings.hideToken":"settings.showToken"),guideHref:o(m),requirementsHref:o(m,"no-native-api")}}function M({profileId:e,api:r,backend:s,rejected:o,missingApi:a,lang:i,pickLang:l,dark:c,themeLine truncated
@@ -1 +0,0 @@
import{r as e,j as t}from"./vendor-react-64fcanlc.js";import{bM as r,u as s,ao as l,an as a,dD as o,bS as n,B as i,ck as c,dE as d,C as g,dF as m,V as u,a3 as p,r as h,y as f,cg as x,bX as v,T as j,m as b,ce as w,bK as y,cm as M,E as N}from"./index-DFPUSiWe.js";import{aM as C,a as k,F as T,aN as $,D as S}from"./activity-pUN0wcVd.js";import{C as R}from"./ItemText-u3CUc_6I.js";import{T as A,U as O,L as W,K as D,p as F,A as L}from"./logs-lI5kXcWp.js";import{D as P}from"./Table-6xnX6eUx.js";import{d as E,e as B}from"./files-Dt8K7QfP.js";import{r as H}from"./recorder-BHyqDNp5.js";import{u as U,C as V}from"./Copy-DRn0jPgo.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./nodes-HzIJdCpg.js";import"./AppearanceControls-Bpq6OQjk.js";import"./Select-BFkb6iie.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./Virtualizer-BH6NJSXn.js";import"./Checkbox-xU-Ds7Hn.js";const _=new WeakMap;function q(e){let t=_.get(e);return void 0===t&&_.set(e,t=Date.parse(e.ts)),t}const J=[...r].reverse();function K({records:m,offered:u,minimum:p,setMinimum:h,waiting:f}){const x=s(),v=l[a()],j=k(),b=e.useMemo(()=>function(e,t,r,s=24){const l=r?J.indexOf(r):J.length-1,a=J.filter((e,r)=>t.includes(e)&&r<=l),o=e.map(q).filter(Number.isFinite);if(!o.length)return{rows:a.map(e=>({level:e,counts:[]})),buckets:[],width:0,busiest:null};const n=Math.min(...o),i=Math.max(...o),c=C(n,i,s),d=c.length>1?c[1]-c[0]:Math.max(i-n,6e4),g=new Map(a.map(e=>[e,new Array(c.length).fill(0)])),m=new Array(c.length).fill(0),u=new Array(c.length).fill(0);for(const x of e){const e=q(x),t=g.get(x.level);if(!t||!Number.isFinite(e))continue;const r=Math.min(c.length-1,Math.floor((e-c[0])/d));t[r]++,m[r]++,"error"===x.level&&u[r]++}const p=e=>e.reduce((t,r,s)=>r>e[t]?s:t,0),h=u.reduce((e,t)=>e+t,0),f=p(h?u:m);return{rows:a.map(e=>({level:e,counts:g.get(e)})),buckets:c,width:d,busiest:{start:c[f],count:h?u[f]:m[f],errors:h}}}(m,u,p),[m,u,p]),w=e.useMemo(()=>o(v),[v]),y=e.useCallback(e=>`${w.format(e)}–${w.format(e+b.width)}`,[w,b.width]),M=e.useMemo(()=>{const e=r.map(e=>{const r=x(n[e]);return[e,{text:r,label:t.jsxs(i,{quiet:!0,small:!0,className:e===p?"current":void 0,label:x(e===p?"log.chart.current":"log.chart.minimum",{level:r}),onPress:()=>h(e),children:[e===p&&t.jsx(R,{}),r]})}]});return new Map(e)},[p,h,x]),N=e.useMemo(()=>{const e={error:j.negative,warn:j.notice,info:j.info,debug:j.subtle,trace:j.muted},t=b.buckets.map(y);return{columns:b.buckets.map(e=>w.format(e)),rows:b.rows.map(r=>{const{text:s,label:l}=M.get(r.level);return{id:r.level,color:e[r.level],label:l,counts:r.counts,titles:r.counts.map((e,r)=>x("log.chart.cell",{time:t[r],level:s,n:e}))}})}},[b,y,w,M,j,x]);if(!m.length)return f?t.jsx(z,{heads:M,levels:b.rows.map(e=>e.level),time:w.format(0)}):null;const S=b.busiest,A=S?S.errors?[{label:x("log.chart.errors"),value:x("log.chart.count",{n:S.errors}),icon:t.jsx(c,{}),tone:"negative"},{label:x("log.chart.peakErrors"),icon:t.jsx(d,{}),tint:"c1",value:y(S.start),caption:x("log.chart.count",{n:S.count})}]:[{label:x("log.chart.errors"),value:x("log.chart.count",{n:0}),icon:t.jsx(c,{}),tint:"c5"},{label:x("log.chart.peak"),icon:t.jsx(d,{}),tint:"c1",value:y(S.start),caption:x("log.chart.count",{n:S.count})}]:[];return t.jsxs("div",{className:"rp-chart-page",children:[t.jsx(T,{facts:A}),t.jsx(g,{title:x("log.chart.title"),note:x("log.chart.sample",{n:m.length}),children:t.jsx($,{label:x("log.chart.title"),columns:N.columns,rows:N.rows})})]})}function z({heads:e,levels:r,time:l}){const a=s(),o=[{label:a("log.chart.errors"),value:a("log.chart.count",{n:0}),icon:t.jsx(c,{}),tint:"c5"},{label:a("log.chart.peak"),icon:t.jsx(d,{}),tint:"c1",value:`${l}–${l}`,caption:a("log.chart.count",{n:0})}],n=r.map(t=>({id:t,color:"transparent",label:e.get(t).label,counts:[0],titles:[""]}));return t.jsxs("div",{className:"rp-chart-page",inert:!0,"aria-hidden":"true",children:[t.jsxs("div",{className:"rp-chart-hold",children:[t.jsx(T,{facts:o}),t.jsx(m,{})]}),t.jsxs("div",{className:"rp-chart-hold",children:[t.jsx(g,{title:a("log.chart.title"),note:a("log.chart.sample",{n:0}),children:t.jsx($,{label:a("log.chart.title"),columns:[l],rows:n})}),t.jsx(m,{})]})]})}const G={trace:"muted",debug:"neutral",info:"info",warn:"warn",error:"err"},I=new WeakMap;function X(e,t,s,l,a,o=new Set,i){return{rows:e.flatMap(e=>{const t=function(e,t,r){const s=I.get(e);if(s&&s.locale===t)return s.row;const l={id:e.id,timestamp:p(e.ts,t),iso:e.ts,levelText:u(n,e.level,r),tone:G[e.level],target:e.target,gap:!1,record:e,message:e.message+(e.fields?" "+Object.entries(e.fields).map(([e,t])=>`${e}=${"string"==typeof t?t:JSON.stringify(t)}`).join(" "):"")};return I.set(e,{locale:t,row:l}),l}(e,l,a);return o.has(e)?[{...t,id:`gap ${e.id}`,timestamp:"",iso:"",levelText:"",target:"",gap:!0,message:a("log.gap")},t]:[t]}),levels:t.map(e=>{return{id:e,label:(t=e,i&&r.indexOf(t)<r.indexOf(i)?a("log.levelNotRecorded",{level:u(n,e,a)}):u(n,e,a))};var t}),recordedText:i?a("log.recorded",{level:u(n,i,Line truncated
@@ -1 +0,0 @@
import{j as e}from"./vendor-react-64fcanlc.js";import{u as n,an as r,c as s,bC as t}from"./index-DFPUSiWe.js";import{d as a}from"./limitHint-drqGf1xh.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./nodes-HzIJdCpg.js";function i({reason:i}){const o=n(),c=r(),l=a("mode",i,o,c);return l?e.jsxs(e.Fragment,{children:[e.jsx("p",{children:l.text}),l.setting&&e.jsx("p",{children:l.setting}),e.jsx(s,{external:!0,appearance:"link",href:l.link.href,children:l.link.text})]}):e.jsxs(e.Fragment,{children:[e.jsx("p",{children:o("act.modeReadOnlyReason")}),e.jsx("p",{children:o("act.modeReadOnlyAction")}),e.jsx(s,{external:!0,appearance:"link",href:t(c,"read-only"),children:o("act.readOnlyDocs")})]})}export{i as ModeReadOnlyHint};
@@ -1 +0,0 @@
import{j as e}from"./vendor-react-64fcanlc.js";import{N as r,T as a}from"./index-DFPUSiWe.js";import{L as n}from"./Tag-BZqiYh1Z.js";function s({label:r,href:a}){return a?e.jsx(n,{href:a,children:r}):r}function o({row:n}){const s=n.path??void 0;return n.nodeName?e.jsx(r,{name:n.node,className:"rp-chain",text:s}):e.jsx(a,{className:"rp-chain",text:s,children:n.node})}export{o as N,s as O};
@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./XhttpOptions-BHPc7rZI.js","./vendor-react-64fcanlc.js","./_commonjsHelpers-CXUWDbkB.js","./index-DFPUSiWe.js","./index-BnzLYrTn.css","./logs-lI5kXcWp.js","./Select-BFkb6iie.js","./ItemText-u3CUc_6I.js","./AppearanceControls-Bpq6OQjk.js","./nodes-HzIJdCpg.js","./Tabs-HxpCyCLR.js","./useGridSelectionCheckbox-wMhI-U-R.js","./Table-6xnX6eUx.js","./Virtualizer-BH6NJSXn.js","./Checkbox-xU-Ds7Hn.js","./activity-pUN0wcVd.js","./activity-CKk6X7wg.css","./TimeCell-CRwMWdtu.js","./CardView-CwlxcqMf.js","./GridList-BuXKU7Uk.js","./ProbeOptionsDialog-BDgL7_BI.js","./Tag-BZqiYh1Z.js","./FlagPicker-DZPvPF1E.js","./SearchSelect-CsXd0DEh.js","./SearchList-C88Y6c53.js","./outbounds-BMHzxQr8.js","./probe-Db_cbYvO.js","./nav-CfbmlNWr.js","./SubscriptionFields-CWqR97pB.js","./NumberField-kCp6sey7.js"])))=>i.map(i=>d[i]);
import{dj as e,dk as n,df as s,dl as a,dm as r,cw as o,dn as t,ck as i,ao as l,an as d,r as c,t as u,ch as m,ca as p,u as h,E as g,ah as v,aa as b,cj as f,C as y,S as x,b1 as j,c as k,N as w,B as S,T as N,cl as P,ce as M,I as A,b5 as C,b6 as R,as as L,dp as T,aF as O,dq as _,dr as G,a as F,b as E,bK as D,D as I,aL as q,x as B,cd as $,c7 as H,aU as z,h as J,aV as U,Z as Q,cH as W,cI as K,y as V,w as X,ds as Z,ac as Y,cb as ee,c9 as ne,aO as se,d as ae,aM as re,cJ as oe,aN as te,ay as ie,dt as le,b7 as de,cE as ce,bJ as ue,cm as me,di as pe,m as he,_ as ge}from"./index-DFPUSiWe.js";import{j as ve,r as be}from"./vendor-react-64fcanlc.js";import{T as fe}from"./Tabs-HxpCyCLR.js";import{D as ye,c as xe}from"./Table-6xnX6eUx.js";import{g as je,c as ke,f as we,S as Se,K as Ne,n as Pe,B as Me,L as Ae,A as Ce,p as Re,E as Le,w as Te,u as Oe,F as _e,G as Ge,x as Fe}from"./logs-lI5kXcWp.js";import{ae as Ee,a0 as De,af as Ie,ag as qe,C as Be,F as $e,ah as He,ai as ze,aj as Je,ak as Ue,n as Qe,al as We,am as Ke,an as Ve,ao as Xe,ap as Ze,aq as Ye,ar as en,as as nn,at as sn,au as an,T as rn,av as on,aw as tn,ax as ln,ay as dn,az as cn,aA as un,aB as mn,aC as pn,aD as hn,aE as gn,aF as vn,aG as bn,aH as fn,P as yn}from"./activity-pUN0wcVd.js";import{T as xn}from"./TimeCell-CRwMWdtu.js";import{C as jn,a as kn}from"./CardView-CwlxcqMf.js";import{P as wn}from"./ProbeOptionsDialog-BDgL7_BI.js";import{C as Sn,l as Nn}from"./AppearanceControls-Bpq6OQjk.js";import{F as Pn,T as Mn,L as An}from"./Tag-BZqiYh1Z.js";import{FlagField as Cn}from"./FlagPicker-DZPvPF1E.js";import{S as Rn}from"./SearchSelect-CsXd0DEh.js";import{u as Ln}from"./outbounds-BMHzxQr8.js";import{ad as Tn,B as On,ae as _n,af as Gn,C as Fn,ag as En,ah as Dn,q as In,e as qn,N as Bn,r as $n,x as Hn,o as zn,v as Jn,S as Un,ai as Qn}from"./nodes-HzIJdCpg.js";import{$ as Wn}from"./SearchList-C88Y6c53.js";import{b as Kn,p as Vn}from"./probe-Db_cbYvO.js";import{n as Xn}from"./nav-CfbmlNWr.js";function Zn(l){const{label:d,description:c,error:u,placeholder:m,necessityIndicator:p,...h}=e(l);return ve.jsxs(n,{...h,className:"rp-field",isInvalid:!!u,validationBehavior:"aria",children:[ve.jsxs(s,{className:"rp-label",children:[d,ve.jsx(a,{isRequired:h.isRequired,necessityIndicator:p})]}),ve.jsx(r,{className:"rp-input rp-textarea",rows:3,placeholder:m}),c&&ve.jsx(o,{slot:"description",className:"rp-label",children:c}),u&&ve.jsxs(t,{className:"rp-field-error",children:[ve.jsx(i,{}),u]})]})}function Yn(e,n){return e.split("{names}").map((e,s)=>ve.jsxs(be.Fragment,{children:[s>0&&n,e]},s))}function es(e,n,s,a){const r=j(n,["",""]),o=e.slice(0,6).map((e,n)=>ve.jsxs(be.Fragment,{children:[n>0&&r,ve.jsx(k,{appearance:"link",layout:"inline",href:a.get(e.id)??Ue([e.id]),children:ve.jsx(w,{name:e.name})})]},e.id));if(e.length<=6)return o;const t=ve.jsx(k,{appearance:"link",layout:"inline",href:Ue(e.map(e=>e.id)),children:s("nodes.latency.more",{n:e.length-6})});return s("nodes.latency.andMore",{names:"{names}",more:"{more}"}).split(/(\{names\}|\{more\})/).map((e,n)=>ve.jsx(be.Fragment,{children:"{names}"===e?o:"{more}"===e?t:e},n))}const ns=e=>[{label:e("nodes.latency.lowest"),icon:ve.jsx(f,{}),tint:"c2",value:"",caption:" "},{label:e("nodes.latency.highest"),icon:ve.jsx(Be,{}),tint:"c4",value:"",caption:" "},{label:e("ui.unavailable"),icon:ve.jsx(i,{}),tint:"c5",value:""}];function ss(){const e=h(),n=d(),{nodes:s,by:a,setBy:r,view:o,hrefs:t}=function(){const e=l[d()],n=c().data?.resources,s=je(u(n,"nodes",{whileLoading:!0})),a=ke(u(n,"groups",{whileLoading:!1})),r=we(u(n,"providers",{whileLoading:!0})),[o,t]=be.useState("group"),i=m(),h=p(s.data,i),g=p(a.data,i),v=p(r.data,i),b=be.useMemo(()=>Ee(h??[],g,o,e),[h,g,o,e]),f=be.useMemo(()=>new Map((h??[]).map(e=>[e.id,De(e,v?.providers??[],!0)])),[h,v]);return{nodes:{...s,data:h},by:o,setBy:t,view:b,hrefs:f}}();if(s.error&&!s.data)return ve.jsx(g,{error:s.error,onRetry:s.refetch});const j=!s.data;if(s.data&&!s.data.length)return ve.jsx(v,{children:e("ui.empty")});const k=Ie(o),{measured:w,down:S}=qe(o),N=w[0],P=w.at(-1),M=N&&P?[{label:e("nodes.latency.lowest"),icon:ve.jsx(f,{}),tint:"c2",value:N.name,nodeName:!0,valueRole:"name",caption:b(N.latest,e)},{label:e("nodes.latency.highest"),icon:ve.jsx(Be,{}),tint:"c4",value:P.name,nodeName:!0,valueRole:"name",caption:b(P.latest,e)},{label:e("ui.unavailable"),value:e("nodes.latency.count",{n:S}),icon:ve.jsx(i,{}),tint:"c5",tone:S?"negative":void 0}]:[],A=s=>{const a=s.filter(e=>"unavailable"===e.state),r=s.filter(e=>"unmeasured"===e.state);return[...a.length?[Yn(e("nodes.latency.unavailableList",{n:a.length,names:"{names}"}),es(a,n,e,t))]:[],...r.length?[Yn(e("nodes.latency.unmeasuredList",{n:r.length,names:"{names}"}),es(r,n,e,t))]:[]]};return ve.jsxs("div",{className:"rp-chart-page",children:[ve.jsx($e,{facts:j?ns(e):M,loading:j}),ve.jsx(y,{title:e("ui.nodeLatency"),note:s.data&&e("nodes.latency.sample",{n:s.data.length}),aside:ve.jsx(Se,{label:e("nodes.latency.by"),value:a,onChange:e=>r(e),itLine truncated
@@ -1 +0,0 @@
.rp-textarea{height:auto;min-block-size:calc(3 * var(--rp-line-body) + 2 * var(--rp-space-2));resize:vertical;padding-block:var(--rp-space-2);color:inherit;font:inherit;outline:none}.rp-textarea[data-focus-visible]{outline:var(--rp-focus-width) solid var(--rp-focus-color);outline-offset:var(--rp-focus-offset)}
@@ -1 +0,0 @@
import{r as e,a as t,$ as i,j as a}from"./vendor-react-64fcanlc.js";import{fZ as s,eH as n,f_ as r,cO as l,cZ as o,f8 as u,es as c,et as d,cV as m,f$ as p,ev as v,f4 as h,cW as f,fM as b,g0 as g,cU as y,g1 as N,eD as S,cP as x,fY as P,cQ as D,g2 as w,eS as E,ex as V,fa as M,g3 as F,c_ as R,c$ as I,d0 as C,d1 as B,d2 as O,bt as T,d3 as j,dc as L,d4 as k,d6 as $,g4 as U,g5 as q,d7 as A,eQ as G,d8 as W,d9 as K,d5 as z,I as H,dj as _,u as Z,cz as Y,df as X,dl as J,g6 as Q,cq as ee,b9 as te,cw as ie,dn as ae,ck as se,g7 as ne}from"./index-DFPUSiWe.js";const re=new RegExp("^.*\\(.*\\).*$"),le=["latn","arab","hanidec","deva","beng","fullwide"];class oe{constructor(e,t={}){this.locale=e,this.options=t}parse(e){return ce(this.locale,this.options,e).parse(e)}isValidPartialNumber(e,t,i){return ce(this.locale,this.options,e).isValidPartialNumber(e,t,i)}getNumberingSystem(e){return ce(this.locale,this.options,e).options.numberingSystem}}const ue=new Map;function ce(e,t,i){let a=de(e,t);if(!e.includes("-nu-")&&!a.isValidPartialNumber(i))for(let s of le)if(s!==a.options.numberingSystem){let a=de(e+(e.includes("-u-")?"-nu-":"-u-nu-")+s,t);if(a.isValidPartialNumber(i))return a}return a}function de(e,t){let i=e+(t?Object.entries(t).sort((e,t)=>e[0]<t[0]?-1:1).join():""),a=ue.get(i);return a||(a=new me(e,t),ue.set(i,a)),a}class me{constructor(e,t={}){this.locale=e,1!==t.roundingIncrement&&null!=t.roundingIncrement&&(null==t.maximumFractionDigits&&null==t.minimumFractionDigits?(t.maximumFractionDigits=0,t.minimumFractionDigits=0):null==t.maximumFractionDigits?t.maximumFractionDigits=t.minimumFractionDigits:null==t.minimumFractionDigits&&(t.minimumFractionDigits=t.maximumFractionDigits)),this.formatter=new Intl.NumberFormat(e,t),this.options=this.formatter.resolvedOptions(),this.symbols=function(e,t,i,a){let s=new Intl.NumberFormat(e,{...i,minimumSignificantDigits:1,maximumSignificantDigits:21,roundingIncrement:1,roundingPriority:"auto",roundingMode:"halfExpand",useGrouping:!0}),n=s.formatToParts(-10000.111),r=s.formatToParts(10000.111),l=ve.map(e=>s.formatToParts(e)),o=l.map((e,t)=>{let i=e.find(e=>"unit"===e.type);return i&&!e.some(e=>"integer"===e.type||"fraction"===e.type)?{unit:i.value,value:ve[t]}:null}).filter(e=>!!e),u=n.find(e=>"minusSign"===e.type)?.value??"-",c=r.find(e=>"plusSign"===e.type)?.value;c||"exceptZero"!==a?.signDisplay&&"always"!==a?.signDisplay||(c="+");let d=new Intl.NumberFormat(e,{...i,minimumFractionDigits:2,maximumFractionDigits:2}).formatToParts(.001),m=d.find(e=>"decimal"===e.type)?.value,p=n.find(e=>"group"===e.type)?.value,v=n.filter(e=>!pe.has(e.type)).map(e=>fe(e.value)),h=l.flatMap(e=>e.filter(e=>!pe.has(e.type)).map(e=>fe(e.value))),f=[...new Set([...v,...h])].sort((e,t)=>t.length-e.length),b=0===f.length?new RegExp("\\p{White_Space}|\\p{Cf}","gu"):new RegExp(`${f.join("|")}|\\p{White_Space}|\\p{Cf}`,"gu"),g=[...new Intl.NumberFormat(i.locale,{useGrouping:!1}).format(9876543210)].reverse(),y=new Map(g.map((e,t)=>[e,t])),N=new RegExp(`[${g.join("")}]`,"g");return{minusSign:u,plusSign:c,decimal:m,group:p,literals:b,numeral:N,numerals:g,index:e=>String(y.get(e)),noNumeralUnits:o}}(e,this.formatter,this.options,t),"percent"===this.options.style&&((this.options.minimumFractionDigits??0)>18||(this.options.maximumFractionDigits??0)>18)&&console.warn("NumberParser cannot handle percentages with greater than 18 decimal places, please reduce the number in your options.")}parse(e){let t=this.formatter.resolvedOptions().useGrouping,i=this.sanitize(e);if(!t&&this.symbols.group&&i.includes(this.symbols.group))return NaN;if(this.symbols.group&&(i=i.replaceAll(this.symbols.group,"")),this.symbols.decimal&&(i=i.replace(this.symbols.decimal,".")),this.symbols.minusSign&&(i=i.replace(this.symbols.minusSign,"-")),i=i.replace(this.symbols.numeral,this.symbols.index),"percent"===this.options.style){let e=i.indexOf("-");i=i.replace("-",""),i=i.replace("+","");let t=i.indexOf(".");-1===t&&(t=i.length),i=i.replace(".",""),i=t-2==0?`0.${i}`:t-2==-1?`0.0${i}`:t-2==-2?"0.00":`${i.slice(0,t-2)}.${i.slice(t-2)}`,e>-1&&(i=`-${i}`)}let a=i?+i:NaN;if(isNaN(a))return NaN;if("percent"===this.options.style){let e={...this.options,style:"decimal",minimumFractionDigits:Math.min((this.options.minimumFractionDigits??0)+2,20),maximumFractionDigits:Math.min((this.options.maximumFractionDigits??0)+2,20)};return new oe(this.locale,e).parse(new s(this.locale,e).format(a))}return"accounting"===this.options.currencySign&&re.test(e)&&(a*=-1),a}sanitize(e){let t=this.formatter.resolvedOptions().useGrouping;return this.symbols.noNumeralUnits.length>0&&this.symbols.noNumeralUnits.find(t=>t.unit===e)?this.symbols.noNumeralUnits.find(t=>t.unit===e).value.toString():(e=e.replace(this.symbols.literals,""),this.symbols.minusSign&&(e=e.replace("-",this.symbols.minusSign)),"arab"===this.options.numberingSystem&&(this.symbols.decimal&&(e=he(e,",",this.symbols.decimal),e=he(e,String.fromCharCode(1548),this.symbols.decimal)),this.symbols.group&&t&&(eLine truncated
@@ -1 +0,0 @@
import{r as e,j as t}from"./vendor-react-64fcanlc.js";import{aj as a,U as s,bC as o,aO as i,aZ as n,a_ as r,b0 as l,a3 as d,a2 as c,a7 as p,aX as u,Y as m,h as v,Z as h,b1 as f,u as g,an as b,ao as x,r as j,ap as y,t as w,ar as k,aq as _,y as N,bX as S,E as R,c as H,T as O,m as L,C,bY as I,ah as P,ay as A,aE as D,S as V,bZ as U,P as E,B as F,b_ as W}from"./index-DFPUSiWe.js";import{d as B,e as T}from"./files-Dt8K7QfP.js";import{r as M,l as q,f as X,a as Z,b as K}from"./limitHint-drqGf1xh.js";import{e as Y,c as z,u as G,D as J,R as Q}from"./activity-pUN0wcVd.js";import{e as $}from"./nodes-HzIJdCpg.js";import{D as ee,t as te}from"./Table-6xnX6eUx.js";import{L as ae,K as se,A as oe,B as ie}from"./logs-lI5kXcWp.js";import{D as ne}from"./DaeCode-DEjXqvav.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./Select-BFkb6iie.js";import"./Virtualizer-BH6NJSXn.js";import"./Checkbox-xU-Ds7Hn.js";import"./daeTokens-BVtuNnWZ.js";const re={connections:v("connections",{tab:"list"}),flows:v("flows",{tab:"records"}),routing_trace:v("rules",{tab:"trace"}),dns_query:v("dns",{tab:"query"}),dns_cache:v("dns",{tab:"cache"}),dns_log:v("dns",{tab:"log"}),events:v("events"),probes:v("nodes",{tab:"latency"}),traffic_history:v("activity"),memory_history:v("activity"),runtime_outbounds:v("activity"),logs:v("logs"),providers:v("nodes"),rules:v("rules",{tab:"list"}),config:v("config"),config_validate:v("config",{tab:"source",panel:"diagnostics"}),runtime_settings:v("settings",{card:"runtime"}),geodata:v("settings",{card:"geodata"})},le={ebpf:"ov.v.ebpf",userspace:"ov.v.userspace",mock:"ov.v.mock",active:"ov.v.active",degraded:"ov.v.degraded",detached:"ov.v.detached",failed:"ov.v.failed",disabled:"ov.v.disabled",full:"ov.v.full",partial:"ov.v.partial",none:"ov.v.none",real:"ov.v.real",loaded:"ov.v.loaded",not_loaded:"ov.v.notLoaded",attached:"ov.v.attached",partially_attached:"ov.v.partiallyAttached",published:"ov.v.published",not_published:"ov.v.notPublished",healthy:"ov.v.healthy",ready:"ov.v.ready",error:"ov.v.error",unknown:"ov.v.unknown",ingress:"ov.v.ingress",egress:"ov.v.egress"};function de(e,t){return le[e]?t(le[e]):e}function ce(e,t,a,s,o=!1,i=[]){const n=e.ebpf,r=n?.maps?.conn_state,l=(e,t)=>{const s=function(e,t,a){return"degraded"===e?{title:a("ov.v.degraded"),text:a(t?"ov.degradedHelp.runtime":"ov.degradedHelp.datapath")}:"unknown"===e?{title:a("ov.v.unknown"),text:a("ov.unknownHelp")}:void 0}(t,o,a);return s?{label:a(e),value:de(t,a),help:s}:[a(e),de(t,a)]},d=n?.maps?.state??"unknown",c=n&&function(e,t,a,s){if("unknown"!==e||!t.length)return;const o=new Intl.ListFormat(s,{type:"conjunction"}).format(t);return{title:a("ov.v.notVerified"),text:a("ov.notVerifiedHelp",{hooks:o})}}(n.hooks,i,a,s);return[l("ov.f.kind",e.kind),l("ov.f.state",e.state),..."partial"===e.visibility&&n?[]:[l("ov.f.visibility",e.visibility)],...n?[l("ov.f.backend",n.backend),l("ov.f.programs",n.programs),c?{label:a("ov.f.hooks"),value:a("ov.v.notVerified"),help:c}:l("ov.f.hooks",n.hooks),l("ov.f.routing",n.routing.state),l("ov.f.health",n.health),..."partial"===d&&r&&!r.occupancy_known?[[a("ov.f.maps"),a("ov.mapCapacity",{capacity:h(r.capacity,s)})]]:[l("ov.f.maps",d),[a("ov.f.connState"),r?.occupancy_known&&null!==r.occupancy?a("ui.fraction",{part:h(r.occupancy,s),whole:h(r.capacity,s)}):r?a("ov.occupancyUnknown",{capacity:h(r.capacity,s)}):t]]]:[]]}const pe={service:"ov.v.cgroupService",shared:"ov.v.cgroupShared"},ue={service:"ov.cgroupHelp.service",shared:"ov.cgroupHelp.shared"};function me(e,t,s,o=[],i=!1){const n=e.cgroup?.scope,r=e=>{const t=a(e??null);return null===t?"—":h(t,s)},l=[["ov.f.rss",c(e.process?.rss_bytes??null,s)],["ov.f.cgroupCurrent",c(e.cgroup?.current_bytes??null,s)],["ov.f.cgroupLimit",i&&null===e.cgroup?.limit_bytes?t("ov.noLimit"):c(e.cgroup?.limit_bytes??null,s)],["ov.f.cgroupScope",n&&"unknown"!==n?t(pe[n]):"—"],["ov.f.oomHigh",r(e.cgroup?.events?.high)],["ov.f.oom",r(e.cgroup?.events?.oom)],["ov.f.oomKill",r(e.cgroup?.events?.oom_kill)],["ov.f.ebpfBytes",c(e.kernel?.ebpf_bytes??null,s)]],d=[...o,..."unknown"===n?["ov.f.cgroupScope"]:[],...e.kernel?[]:["ov.f.ebpfBytes"]];return l.filter(([e])=>!d.includes(e)).map(([e,a])=>"ov.f.cgroupScope"===e&&n&&"unknown"!==n?{label:t(e),value:a,help:{title:t(e),text:t("ui.valuePair",{label:a,value:t(ue[n])})}}:[t(e),a])}const ve=new Set(["persistence_unavailable","state_cache_unavailable"]);const he={probes:"ov.lim.h.probes",subscriptions:"ov.lim.h.subscriptions",reload:"ov.lim.h.reload",config_validate:"ov.lim.h.configValidate"};function fe(e,t,a,s,i){const n=e.resources,r=$(t),l=t=>r.reason(t,e,i),d=(e,t)=>({href:o(s,e),text:a(t),external:!0}),c=(e,t)=>({label:a("ov.lim.why"),text:a(e),links:t?[t]:[]}),p=e=>{const t=e.find(e=>void 0!==e),s=t&&Z[t.code];if(!s)return;const o=s.docs?[d(...s.docs)]:[],i=e.flatMap(e=>e&&"settings"in e?e.settings:[]).filter((e,t,a)=>a.fiLine truncated
@@ -1 +0,0 @@
import{r as e,j as t}from"./vendor-react-64fcanlc.js";import{P as n}from"./ProbeOptionsDialog-BDgL7_BI.js";import{C as i,a as s,b as r,E as l}from"./CheckboxSet-CRYfymLL.js";import{A as o}from"./AddCircle-V0Kluz-m.js";import{U as a,S as d,l as c,s as u,u as p,y as m,o as h,m as g,V as f,W as b,T as y,N as v,R as x,r as j,Q as k}from"./nodes-HzIJdCpg.js";import{bt as w,u as S,h as N,I as C,N as M,w as _,aV as I,cx as E,V as D,H as O,aW as L,bU as P,cy as z,b2 as R,aU as T,Z as $,ao as K,an as H,cq as q,b9 as F,j as A,ah as V,ce as G,bK as B,cm as J,cz as U,co as W,cA as Q,bz as X,cr as Y,cB as Z,B as ee,aF as te,bm as ne,c as ie,cC as se,b7 as re,cD as le,cE as oe,aB as ae,m as de,bJ as ce,c5 as ue,ab as pe,cF as me,cG as he,r as ge,t as fe,cH as be,aR as ye,cI as ve,v as xe,D as je,aN as ke,at as we,J as Se,aA as Ne,c7 as Ce,x as Me,cJ as _e,E as Ie,cl as Ee,aH as De,cK as Oe,C as Le,T as Pe,cL as ze,ay as Re,b as Te,a as $e}from"./index-DFPUSiWe.js";import{T as Ke,a as He,L as qe}from"./Tag-BZqiYh1Z.js";import{D as Fe}from"./DaeCode-DEjXqvav.js";import{N as Ae,p as Ve,D as Ge,K as Be,g as Je,f as Ue,u as We,c as Qe,w as Xe,x as Ye,m as Ze,y as et,S as tt,L as nt,B as it,n as st}from"./logs-lI5kXcWp.js";import{C as rt}from"./AppearanceControls-Bpq6OQjk.js";import{$ as lt,S as ot}from"./SearchList-C88Y6c53.js";import{h as at,q as dt,v as ct,w as ut,x as pt,y as mt,z as ht,A as gt,E as ft,G as bt,H as yt,I as vt,J as xt,K as jt,M as kt,N as wt,O as St,P as Nt,Q as Ct,T as Mt,V as _t,X as It,Y as Et}from"./activity-pUN0wcVd.js";import{$ as Dt,a as Ot}from"./GridList-BuXKU7Uk.js";import{b as Lt,c as Pt,d as zt,e as Rt,$ as Tt}from"./Virtualizer-BH6NJSXn.js";import{$ as $t,a as Kt}from"./Select-BFkb6iie.js";import{a as Ht,b as qt}from"./ItemText-u3CUc_6I.js";import{S as Ft}from"./SearchSelect-CsXd0DEh.js";import{N as At,t as Vt,n as Gt}from"./NumberField-kCp6sey7.js";import{p as Bt,a as Jt}from"./probe-Db_cbYvO.js";import{f as Ut}from"./limitHint-drqGf1xh.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Checkbox-xU-Ds7Hn.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./daeTokens-BVtuNnWZ.js";const Wt={minItemSize:new Pt(200,200),maxItemSize:new Pt(1/0,1/0),preserveAspectRatio:!1,minSpace:new Pt(18,18),maxSpace:1/0,maxColumns:1/0,dropIndicatorThickness:2,loaderHeight:48};class Qt extends Lt{shouldInvalidateLayoutOptions(e,t){return e.maxColumns!==t.maxColumns||e.dropIndicatorThickness!==t.dropIndicatorThickness||e.preserveAspectRatio!==t.preserveAspectRatio||!(e.minItemSize||Wt.minItemSize).equals(t.minItemSize||Wt.minItemSize)||!(e.maxItemSize||Wt.maxItemSize).equals(t.maxItemSize||Wt.maxItemSize)||!(e.minSpace||Wt.minSpace).equals(t.minSpace||Wt.minSpace)||e.maxHorizontalSpace!==t.maxHorizontalSpace||e.loaderHeight!==t.loaderHeight||e.direction!==t.direction}update(e){let{minItemSize:t=Wt.minItemSize,maxItemSize:n=Wt.maxItemSize,preserveAspectRatio:i=Wt.preserveAspectRatio,minSpace:s=Wt.minSpace,maxHorizontalSpace:r=Wt.maxSpace,maxColumns:l=Wt.maxColumns,dropIndicatorThickness:o=Wt.dropIndicatorThickness,loaderHeight:a=Wt.loaderHeight,direction:d="ltr"}=e.layoutOptions||{};this.dropIndicatorThickness=o,this.direction=d;let c=this.virtualizer.size.width,u=Math.min(n.width,c),p=Number.isFinite(n.height)?n.height:Math.floor(t.height/t.width*u),m=Math.floor(c/(t.width+s.width)),h=Math.max(1,Math.min(l,m));this.numColumns=h;let g=c-s.width*Math.max(0,h),f=Math.floor(g/h);f=Math.max(t.width,Math.min(u,f));let b=(f-t.width)/Math.max(1,u-t.width),y=t.height+Math.floor((p-t.height)*b);y=Math.max(t.height,Math.min(p,y));let v=Math.min(Math.max(r,s.width),Math.floor((c-h*f)/(h+1)));this.gap=new Pt(v,s.height),this.margin=Math.floor((c-h*f-v*(h+1))/2);let x=this.virtualizer.collection,j=x.size,k=x.getLastKey();for(let E=0;E<2&&null!=k;E++){let e=x.getItem(k);if("skeleton"===e?.type){j++;break}k=x.getKeyBefore(k)}let w=Math.ceil(j/h),S=x[Symbol.iterator](),N=w>0?s.height:0,C=new Map,M=null,_=0;for(let E=0;E<w;E++){let t=0,n=[];for(let s=0;s<h;s++){let r=M||S.next().value;if(!r)break;if("loader"===r.type)continue;"skeleton"===r.type&&(M=r);let l=M?`${M.key}-${_++}`:r.key,o=this.layoutInfos.get(l),a=r;M&&(a=o&&o.content.key===l?o.content:{...M,key:l});let d=v+s*(f+v)+this.margin,c=y,u=!i;o&&u&&(c=o.rect.height,u=e.layoutOptionsChanged||e.sizeChanged||o.estimatedSize||o.content!==a);let p=new Rt(d,N,f,c),m=new zt(r.type,l,p);m.estimatedSize=u,m.allowOverflow=!0,m.content=a,C.set(l,m),n.push(m),t=Math.max(t,m.rect.height)}for(let e of n)e.rect.height=t;N+=t+s.height,M&&E===w-1&&N<this.virtualizer.size.height&&w++}let I=x.getItem(x.getLastKey());if("loader"===I?.type){(_>0||!I.props.isLoading)&&(a=0);let e=new Rt(v,N,c-2*v,a),t=new zt("loader",I.key,e);C.set(I.key,t),N=t.rect.maxY}this.layoutInfos=C,this.contentSize=new Pt(this.virtualizer.size.width,N)}getLayoutInfo(e){return this.layoutInfos.get(e)||null}getContentSize(){return this.contentSize}getVisibleLayoutInfos(e){let t=[];for(let n of this.layoutInfos.valuLine truncated
@@ -1 +0,0 @@
import{r as e,j as s}from"./vendor-react-64fcanlc.js";import{u as o,b7 as i,cD as c,cm as a,B as n}from"./index-DFPUSiWe.js";import{H as r,I as t,p as l}from"./logs-lI5kXcWp.js";function d({model:d}){const m=o(),p=e.useId(),h=r(),[u,b]=e.useState(t(d.choices,h.choice)?.id??""),[j,x]=e.useState(h.cold),[f,g]=e.useState(h.leaves),v=d.busy||!d.choices.some(e=>e.id===u);return s.jsx(i,{narrow:!0,isOpen:!0,onOpenChange:e=>!e&&d.close(),title:m("probe.title"),description:d.name,footer:()=>s.jsxs(s.Fragment,{children:[s.jsx(n,{onPress:d.close,children:m("ui.cancel")}),s.jsx(n,{accent:!0,type:"submit",form:p,isDisabled:v,children:m("probe.submit")})]}),children:s.jsxs(c,{id:p,onSubmit:e=>{e.preventDefault(),v||d.submit({...h,choice:u,cold:j,leaves:f})},children:[s.jsx(l,{label:m("probe.kind"),value:u,onChange:b,items:d.choices.map(e=>({id:e.id,label:m(e.label)}))}),s.jsx(a,{isSelected:j,onChange:x,children:m("probe.cold")}),d.group&&s.jsx(a,{isSelected:f,onChange:g,children:m("probe.leaves")})]})})}export{d as P};
@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./Trace-CDFraj3q.js","./vendor-react-64fcanlc.js","./_commonjsHelpers-CXUWDbkB.js","./index-DFPUSiWe.js","./index-BnzLYrTn.css","./flows-Cd1KbGP2.js","./logs-lI5kXcWp.js","./Select-BFkb6iie.js","./ItemText-u3CUc_6I.js","./AppearanceControls-Bpq6OQjk.js","./nodes-HzIJdCpg.js","./probe-Db_cbYvO.js","./useQuickRule-BGNvNXBX.js","./DaeCode-DEjXqvav.js","./daeTokens-BVtuNnWZ.js","./AddCircle-V0Kluz-m.js","./SearchSelect-CsXd0DEh.js","./SearchList-C88Y6c53.js","./Virtualizer-BH6NJSXn.js","./rule-D1hMywhM.js","./activity-pUN0wcVd.js","./activity-CKk6X7wg.css","./NumberField-kCp6sey7.js","./Table-6xnX6eUx.js","./useGridSelectionCheckbox-wMhI-U-R.js","./Checkbox-xU-Ds7Hn.js","./Tabs-HxpCyCLR.js","./CheckboxSet-CRYfymLL.js","./Coverage-BRhvKmi_.js","./labels-D8xxfRTQ.js","./nav-BLmatpph.js","./recorder-BHyqDNp5.js","./geodataUpdate-BANPx3Of.js","./LinkOut-B50sd8cz.js","./geodata-CvcFemyc.js","./impact-B8feJDtJ.css"])))=>i.map(i=>d[i]);
import{cM as e,cN as s,cO as n,cP as i,cQ as l,cR as a,cS as r,bt as t,cT as o,cU as d,cV as u,cW as c,cX as p,cY as m,cZ as f,c_ as g,c$ as h,d0 as b,d1 as v,d2 as x,d3 as k,d4 as y,d5 as w,d6 as j,d7 as S,d8 as C,d9 as _,da as N,db as D,$ as M,dc as P,dd as R,de as q,aE as H,df as I,cw as O,aB as L,b9 as E,c7 as F,c9 as A,ao as T,Z as V,V as W,J as $,aa as B,b1 as U,a8 as Q,o as G,h as J,t as K,dg as Z,a3 as X,w as z,u as Y,aL as ee,cI as se,D as ne,an as ie,r as le,bX as ae,aP as re,aQ as te,bC as oe,y as de,aO as ue,aM as ce,ad as pe,C as me,bm as fe,bJ as ge,cm as he,ce as be,B as ve,m as xe,cD as ke,cC as ye,c as we,dh as je,bK as Se,di as Ce,b7 as _e,E as Ne,cK as De,ba as Me,aF as Pe,cl as Re,ay as qe,T as He,c8 as Ie,cb as Oe,L as Le,aH as Ee,p as Fe,_ as Ae,ch as Te}from"./index-DFPUSiWe.js";import{r as Ve,$ as We,j as $e}from"./vendor-react-64fcanlc.js";import{Z as Be,_ as Ue,$ as Qe,a0 as Ge,a1 as Je,a2 as Ke,a3 as Ze,a4 as Xe,a5 as ze,a6 as Ye,a7 as es,a8 as ss,a9 as ns,aa as is,ab as ls,s as as,ac as rs,ad as ts}from"./activity-pUN0wcVd.js";import{T as os}from"./Tabs-HxpCyCLR.js";import{D as ds}from"./DaeCode-DEjXqvav.js";import{a as us,c as cs,u as ps,z as ms,x as fs,g as gs,D as hs,L as bs,p as vs,S as xs,B as ks}from"./logs-lI5kXcWp.js";import{D as ys}from"./Table-6xnX6eUx.js";import{a as ws,C as js,E as Ss}from"./CheckboxSet-CRYfymLL.js";import{c as Cs,C as _s}from"./Coverage-BRhvKmi_.js";import{d as Ns,b as Ds,c as Ms,e as Ps,B as Rs,r as qs}from"./rule-D1hMywhM.js";import{u as Hs,a as Is,c as Os}from"./flows-Cd1KbGP2.js";import{f as Ls,u as Es,X as Fs,j as As,c as Ts,Y as Vs,l as Ws,s as $s,g as Bs,Z as Us,_ as Qs,$ as Gs,a0 as Js,a1 as Ks,a2 as Zs,r as Xs,a3 as zs,a4 as Ys,a5 as en,a6 as sn,a as nn,a7 as ln,a8 as an,F as rn,a9 as tn,e as on,aa as dn,ab as un}from"./nodes-HzIJdCpg.js";import{w as cn}from"./labels-D8xxfRTQ.js";import{r as pn}from"./nav-BLmatpph.js";import{r as mn}from"./recorder-BHyqDNp5.js";import{g as fn,a as gn,b as hn}from"./geodataUpdate-BANPx3Of.js";import{C as bn}from"./Checkbox-xU-Ds7Hn.js";/* empty css */import{L as vn}from"./LinkOut-B50sd8cz.js";import{S as xn}from"./SearchSelect-CsXd0DEh.js";import{m as kn,a as yn,b as wn}from"./geodata-CvcFemyc.js";const jn=new WeakMap;let Sn=Math.round(1e10*Math.random()),Cn=0;const _n=Ve.createContext(null),Nn=Ve.createContext(null),Dn=Ve.createContext(null),Mn=Ve.forwardRef(function(e,s){[e,s]=b(e,s,_n);let{validationBehavior:l}=v(x)||{},a=e.validationBehavior??l??"native",r=function(e){let s=Ve.useMemo(()=>e.name||`radio-group-${Sn}-${++Cn}`,[e.name]),[n,i]=g(e.value,e.defaultValue??null,e.onChange),[l]=Ve.useState(n),[a,r]=Ve.useState(null),t=h({...e,value:n}),o=t.displayValidation.isInvalid;return{...t,name:s,selectedValue:n,defaultSelectedValue:void 0!==e.value?l:e.defaultValue??null,setSelectedValue:s=>{e.isReadOnly||e.isDisabled||(i(s),t.commitValidation())},lastFocusedValue:a,setLastFocusedValue:r,isDisabled:e.isDisabled||!1,isReadOnly:e.isReadOnly||!1,isRequired:e.isRequired||!1,validationState:e.validationState||(o?"invalid":null),isInvalid:o}}({...e,validationBehavior:a}),[D,M]=k(!e["aria-label"]&&!e["aria-labelledby"]),{radioGroupProps:P,labelProps:R,descriptionProps:q,errorMessageProps:H,...I}=function(e,s){let{name:l,form:a,isReadOnly:r,isRequired:g,isDisabled:h,orientation:b="vertical",validationBehavior:v="aria"}=e,{direction:x}=t(),{isInvalid:k,validationErrors:y,validationDetails:w}=s.displayValidation,{labelProps:j,fieldProps:S,descriptionProps:C,errorMessageProps:_}=o({...e,labelElementType:"span",isInvalid:s.isInvalid,errorMessage:e.errorMessage||y}),N=i(e,{labelable:!0}),{focusWithinProps:D}=d({onBlurWithin(n){e.onBlur?.(n),s.selectedValue||s.setLastFocusedValue(null)},onFocusWithin:e.onFocus,onFocusWithinChange:e.onFocusChange});function M(e,n){let i,l=p(n.currentTarget,{from:f(n),accept:e=>e instanceof m(e).HTMLInputElement&&"radio"===e.type});return"next"===e?(i=l.nextNode(),i||(l.currentNode=n.currentTarget,i=l.firstChild())):(i=l.previousNode(),i||(l.currentNode=n.currentTarget,i=l.lastChild())),!!i&&(i.focus(),s.setSelectedValue(i.value),!0)}let{keyboardProps:P}=u({shortcuts:{ArrowRight:e=>M("rtl"===x&&"vertical"!==b?"prev":"next",e),ArrowLeft:e=>M("rtl"===x&&"vertical"!==b?"next":"prev",e),ArrowDown:e=>M("next",e),ArrowUp:e=>M("prev",e)},allowRepeats:!0}),R=c(l);return jn.set(s,{name:R,form:a,descriptionId:C.id,errorMessageId:_.id,validationBehavior:v}),{radioGroupProps:n(N,{role:"radiogroup",...P,"aria-invalid":s.isInvalid||void 0,"aria-errormessage":e["aria-errormessage"],"aria-readonly":r||void 0,"aria-required":g||void 0,"aria-disabled":h||void 0,"aria-orientation":b,...S,...D}),labelProps:j,descriptionProps:C,errorMessageProps:_,isInvalid:k,validationErrors:y,validationDetails:w}}({...e,label:M,validationBehavior:a},r),O=y({...e,values:{orientation:e.orientation||"vertical",isDisabled:r.isDisabled,isReadOnly:r.isReadOnly,isRequired:r.isRequired,isInvalid:r.isInvalid,state:r},dLine truncated
@@ -1 +0,0 @@
import{r as e,j as t}from"./vendor-react-64fcanlc.js";import{t as s,gb as a,w as i,gc as r,M as n,c0 as o,b1 as l,V as d,Z as u,ao as p,gd as c,ge as m,gf as f,dK as g,u as h,an as y,r as b,y as v,gg as j,b7 as K,e$ as x,ce as w,bK as M,B as $,aF as k,E as q,bI as C,ah as L,cu as S}from"./index-DFPUSiWe.js";import{$ as _,d as I,a as N}from"./Select-BFkb6iie.js";import{b as R}from"./ItemText-u3CUc_6I.js";import{h as E,g as T,c as O,f as P,u as V}from"./logs-lI5kXcWp.js";import{u as A,c as D}from"./flows-Cd1KbGP2.js";import{X as F,e as G}from"./nodes-HzIJdCpg.js";import{c as Q,o as z,p as B,q as H}from"./nav-C6jMmQ4d.js";import{d as U}from"./nav-Bw7F3lhF.js";import{r as W,a as X}from"./nav-BLmatpph.js";import{f as Z}from"./nav-CISoRdvs.js";import{f as J,r as Y,g as ee,h as te}from"./nav-lSzYOU50.js";import{aT as se,a0 as ae,aR as ie}from"./activity-pUN0wcVd.js";import{c as re}from"./nav-BO8viH9G.js";import{n as ne,a as oe}from"./nav-CfbmlNWr.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./AppearanceControls-Bpq6OQjk.js";const le=e=>s(e,"groups",{whileLoading:!1})&&!0===e?.config.writable?[{id:"policies:new",titleKey:"group.newGroup",parentKey:"nav.policies",route:"policies",params:{new:"1"},aliases:["group editor"]}]:[],de=e=>e.trim().toLowerCase().replace(/\s+/g," "),ue=(e,t,s,a,i,r="",n)=>({hit:{id:t,label:s,description:a,route:i,query:r,...n&&{open:n}},keys:[...new Set(e.flatMap(e=>null==e?[]:[de(e)]))]}),pe=(e,t,s,a,i)=>{const{route:r,query:n}=g(i);return ue(e,t,s,a,r,n)};function ce(e,t,s){const a=[{title:e(t),parent:s&&e(s)}];for(const i of c)m(i.id)&&a.push({title:f(i.id,t),parent:s&&f(i.id,s)});return a.flatMap(({title:e,parent:t})=>t?[e,t+" "+e]:[e])}function me(e,t){let s=3;for(const a of e){if(a===t)return 0;a.startsWith(t)?s=1:3===s&&a.includes(t)&&(s=2)}return s}function fe(t,m){const f=h(),g=y(),[K,x]=e.useState(""),w=b(),M=w.data?.resources,$=E(void 0,s(M,"connections",{whileLoading:!1})),k=T(s(M,"nodes",{whileLoading:!1})),q=O(s(M,"groups",{whileLoading:!1})),C=P(s(M,"providers",{whileLoading:!1})),L=V(s(M,"config",{whileLoading:!1})),S=A(s(M,"rules",{whileLoading:!1})),_=D(s(M,"dns_rules",{whileLoading:!1})),I=[w,$,k,q,C,L,S,_],N=G(v().data),R=!!N.globalSettings,[fe,ge]=e.useState(0);e.useEffect(()=>{let e=!0;for(const t of c)j(t.id).then(()=>e&&ge(e=>e+1),()=>{});return()=>{e=!1}},[]);const he=e.useMemo(()=>function(e,t,s){const n=t=>r(t,e),o=e?.resources,l=[...re().map(e=>({path:"connections",params:{tab:e.id},titleKey:e.titleKey})),...ne(o).map(e=>({path:"nodes",params:{tab:e.id},titleKey:e.titleKey})),...Z(o).map(e=>({path:"flows",params:{tab:e.id},titleKey:e.titleKey})),...W(o).map(e=>({path:"rules",params:{tab:e.id},titleKey:e.titleKey})),...U(o).map(e=>({path:"dns",params:{tab:e.id},titleKey:e.titleKey})),...Q(s,e).map(e=>({path:"config",params:{tab:e.id},titleKey:e.titleKey,aliases:e.aliases})),...J(o).map(e=>({path:"settings",params:{card:e.id},titleKey:e.titleKey,aliases:e.aliases}))],d=e=>a.find(t=>t.path===e).nav.titleKey;return[...a.filter(e=>e.nav&&n(e.path)).map(e=>ue(ce(t,e.nav.titleKey),`page:${e.path}?`,t(e.nav.titleKey),void 0,e.path)),...l.filter(e=>n(e.path)).map(e=>{const s=i("",e.params);return ue([...ce(t,e.titleKey,d(e.path)),...e.aliases??[]],`page:${e.path}?${s}`,t(e.titleKey),t(d(e.path)),e.path,s)})]}(w.data,f,R),[w.data,f,R,fe]),ye=e.useMemo(()=>function(e,t){const s=e?.resources,a=new Map(J(s).map(e=>[e.id,e])),r=new Set(s?.runtime_settings.available?s.runtime_settings.fields??[]:[]),n=Object.entries(Y).filter(([e])=>r.has(e)).map(([e,t])=>({id:e,labelKey:t,card:"runtime",aliases:void 0})),o=ee(s);return[...te.filter(e=>!e.sources||o),...n].flatMap(e=>{const s=a.get(e.card);return s?[ue([...ce(t,e.labelKey),...e.aliases??[]],`setting:${e.id}`,t(e.labelKey),t(s.titleKey),"settings",i("",{card:e.card,field:e.id}))]:[]})}(w.data,f),[w.data,f,fe]),be=e.useMemo(()=>function(e,t,a){return e&&s(t?.resources,"config",{whileLoading:!1})?e.fields.map(e=>ue([...ce(a,e.label),e.key],`global:${e.key}`,a(e.label),a(z[e.group]),"config",i("",{tab:"global",field:e.key}))):[]}(N.globalSettings,w.data,f),[N.globalSettings,w.data,f,fe]),ve=e.useMemo(()=>function(e,t){const s=e?.resources;return[...X(s),...le(s),...oe(s),...B(e),...se].filter(t=>null===t.route||r(t.route,e)).map(e=>ue([...ce(t,e.titleKey,e.parentKey),...(e.aliasKeys??[]).flatMap(e=>ce(t,e)),...e.aliases??[]],`feature:${e.id}`,t(e.titleKey),e.parentKey&&t(e.parentKey),e.route,i("",e.params??{}),e.open))}(w.data,f),[w.data,f,fe]),je=e.useMemo(()=>function(e,t,s){return H(e,t,s).map(e=>pe([e.kind,e.summary],`module:${e.id}`,e.kind,e.range,e.href))}(L.data,N,f),[L.data,N,f]),Ke=e.useMemo(()=>function(e,t){return n(e).map(e=>ue([e.domain,e.dst,e.src],`connection:${e.id}`,e.domain||e.dst||e.src||e.id,o(e,t),"connections",i("",{id:e.id})))}($.data,f),[$.data,f]),xe=e.useMemo(()=>function(e,t,s){const a=t?.providers??[];return(e??[]).map(e=>pe([e.name],`node:${e.id}`,e.name,l(s,e.group_ids)||void 0,ae(e,a,!0)))}(k.datLine truncated
@@ -1 +0,0 @@
import{r as e,j as a}from"./vendor-react-64fcanlc.js";import{eq as t,e_ as r,e$ as s,bK as l}from"./index-DFPUSiWe.js";import{$ as n,a as i}from"./Virtualizer-BH6NJSXn.js";function o(a){let r=t({usage:"search",...a}),s=e.useCallback((e,a)=>0===a.length||(e=e.normalize("NFC"),a=a.normalize("NFC"),0===r.compare(e.slice(0,a.length),a)),[r]),l=e.useCallback((e,a)=>0===a.length||(e=e.normalize("NFC"),a=a.normalize("NFC"),0===r.compare(e.slice(-a.length),a)),[r]),n=e.useCallback((e,a)=>{if(0===a.length)return!0;e=e.normalize("NFC");let t=0,s=(a=a.normalize("NFC")).length;for(;t+s<=e.length;t++){let l=e.slice(t,t+s);if(0===r.compare(a,l))return!0}return!1},[r]);return e.useMemo(()=>({startsWith:s,endsWith:l,contains:n}),[s,l,n])}function c({label:e,children:t}){const{contains:c}=o({sensitivity:"base"});return a.jsx(r,{value:null,children:a.jsxs(s,{filter:c,children:[a.jsx(l,{search:!0,label:e,autoFocus:!0,className:"rp-menu-search"}),a.jsx(n,{layout:i,layoutOptions:{estimatedRowHeight:32,estimatedHeadingHeight:26},children:t})]})})}const u=Object.freeze(Object.defineProperty({__proto__:null,SearchList:c},Symbol.toStringTag,{value:"Module"}));export{o as $,c as S,u as a};
@@ -1 +0,0 @@
import{r as s,j as e}from"./vendor-react-64fcanlc.js";import{cz as a,co as l,df as r,cq as i,bz as c,cw as t,cr as d,cu as n,b9 as o}from"./index-DFPUSiWe.js";import{b as p,c as m,$ as j,d as x,a as h}from"./Select-BFkb6iie.js";import{I as b,a as f,b as u}from"./ItemText-u3CUc_6I.js";import{S as N}from"./SearchList-C88Y6c53.js";const v=s=>e.jsxs(h,{id:s.id,className:"rp-item",textValue:s.keywords?`${s.label} ${s.keywords}`:s.label,children:[e.jsx(f,{}),e.jsx(u,{i:s,children:s.desc&&e.jsx(t,{slot:"description",className:o("desc",s.tone),children:s.desc})})]},s.id);function S({label:h,searchLabel:f,sections:u,value:S,onChange:g,isDisabled:y,description:z,side:k,size:w}){const R=s.useRef(null),$=s.useRef(null),C=a(w),D=l(),I=u.flatMap(s=>s.items).find(s=>s.id===S);return e.jsxs(p,{className:o("rp-picker",k?"rp-cluster":"rp-field"),selectedKey:S,onSelectionChange:s=>{null!=s&&g(String(s))},isDisabled:y,children:[e.jsxs("div",{ref:R,className:k?"rp-cluster":"rp-field",children:[e.jsx(r,{className:k?"rp-label":"lbl",children:h}),e.jsxs(i,{ref:$,className:"rp-selectbtn","data-size":C,children:[e.jsx(m,{children:()=>I?e.jsx(b,{i:I}):S}),e.jsx(c,{})]})]}),z&&e.jsx(t,{slot:"description",className:"rp-label",children:z}),e.jsx(d,{className:"rp-popover rp-search-popover",placement:"bottom start",triggerRef:k?$:R,boundaryElement:D,children:e.jsx(N,{label:f,children:e.jsx(j,{className:"rp-menu-scroll rp-overlay-scroll",children:u.map(s=>s.title?e.jsxs(x,{id:s.id,children:[e.jsx(n,{className:"rp-sec-h",children:s.title}),s.items.map(v)]},s.id):s.items.map(v))})})})]})}export{S};
@@ -1 +0,0 @@
import{es as e,cP as t,fG as l,cU as o,cW as a,fH as s,cO as i,fI as r,fJ as n,e0 as d,eE as c,de as u,eA as p,e1 as g,ek as f,en as y,em as v,d0 as m,e7 as b,e4 as h,eO as D,fK as P,e5 as M,fL as S,db as K,cV as k,fM as E,d4 as C,d5 as F,d6 as B,d8 as x,e6 as I,eR as R,eK as w,bt as A,eq as V,ea as T,eL as O,dd as U,eN as L,fN as z,fO as H,da as N,d3 as q,fP as W,eW as _,eX as j,ee as $,fQ as G,fR as Q,cT as Z,er as J,fS as X,eI as Y,cQ as ee,cR as te,cZ as le,fT as oe,c_ as ae,c$ as se,e8 as ie,d1 as re,et as ne,d2 as de,dc as ce,d7 as ue,eQ as pe,fU as ge,fV as fe,d9 as ye}from"./index-DFPUSiWe.js";import{r as ve,$ as me}from"./vendor-react-64fcanlc.js";var be;be={"ar-AE":e,"bg-BG":e,"cs-CZ":e,"da-DK":e,"de-DE":e,"el-GR":e,"en-US":{selectPlaceholder:"Select an item",tableResizer:"Resizer",dropzoneLabel:"DropZone",colorSwatchPicker:"Color swatches"},"es-ES":e,"et-EE":e,"fi-FI":e,"fr-FR":e,"he-IL":e,"hr-HR":e,"hu-HU":e,"it-IT":e,"ja-JP":e,"ko-KR":e,"lt-LT":e,"lv-LV":e,"nb-NO":e,"nl-NL":e,"pl-PL":e,"pt-BR":e,"pt-PT":e,"ro-RO":e,"ru-RU":e,"sk-SK":e,"sl-SI":e,"sr-SP":e,"sv-SE":e,"tr-TR":e,"uk-UA":e,"zh-CN":{colorSwatchPicker:"颜色色板",dropzoneLabel:"放置区域",selectPlaceholder:"选择一个项目",tableResizer:"尺寸调整器"},"zh-TW":{colorSwatchPicker:"色票",dropzoneLabel:"放置區",selectPlaceholder:"選取項目",tableResizer:"大小調整器"}};const he=ve.createContext({}),De=ve.createContext(null),Pe=ve.forwardRef(function(e,t){let{render:l}=ve.useContext(De);return me.createElement(me.Fragment,null,l(e,t))});function Me(e,t){let l=e?.renderDropIndicator,o=e?.isVirtualDragging?.(),a=ve.useCallback(e=>{if(o||t?.isDropTarget(e))return l?l(e):me.createElement(Pe,{target:e})},[t?.target,o,l]);return e?.useDropIndicator?a:void 0}function Se(e,t,l){let o=e.focusedKey,a=null;if(t?.isVirtualDragging?.()&&"item"===l?.target?.type&&(a=l.target.key,"after"===l.target.dropPosition)){let e=l.collection.getKeyAfter(a),t=null;if(null!=e){let o=l.collection.getItem(a)?.level??0;for(;null!=e;){let a=l.collection.getItem(e);if(!a)break;if("item"===a.type){if((a.level??0)<=o)break;t=e,e=l.collection.getKeyAfter(e)}else e=l.collection.getKeyAfter(e)}}a=e??t??a}return ve.useMemo(()=>new Set([o,a].filter(e=>null!=e)),[o,a])}const Ke=new WeakMap;function ke(e,l,o){let{key:a}=e,s=Ke.get(l),f=e.isDisabled??l.selectionManager.isDisabled(a),y=e.isSelected??l.selectionManager.isSelected(a),v=e.shouldSelectOnPressUp??s?.shouldSelectOnPressUp,m=e.shouldFocusOnHover??s?.shouldFocusOnHover,b=e.shouldUseVirtualFocus??s?.shouldUseVirtualFocus,h=e.isVirtualized??s?.isVirtualized,D=r(),P=r(),M={role:"option","aria-disabled":f||void 0,"aria-selected":"none"!==l.selectionManager.selectionMode?y:void 0,"aria-label":e["aria-label"],"aria-labelledby":D,"aria-describedby":P},S=l.collection.getItem(a);if(h){let e=Number(S?.index);M["aria-posinset"]=Number.isNaN(e)?void 0:e+1,M["aria-setsize"]=n(l.collection)}let K=s?.onAction?()=>s?.onAction?.(a):void 0,k=function(e,t){let l=Ke.get(e);if(!l)throw new Error("Unknown list");return`${l.id}-option-${o=t,"string"==typeof o?o.replace(/\s*/g,""):""+o}`;var o}(l,a),{itemProps:E,isPressed:C,isFocused:F,hasAction:B,allowsSelection:x}=d({selectionManager:l.selectionManager,key:a,ref:o,shouldSelectOnPressUp:v,allowsDifferentPressOrigin:v&&m,isVirtualized:h,shouldUseVirtualFocus:b,isDisabled:f,onAction:K||S?.props?.onAction?c(S?.props?.onAction,K):void 0,linkBehavior:s?.linkBehavior,UNSTABLE_itemBehavior:s?.UNSTABLE_itemBehavior,id:k}),{hoverProps:I}=u({isDisabled:f||!m,onHoverStart(){p()||(l.selectionManager.setFocused(!0),l.selectionManager.setFocusedKey(a))}}),R=t(S?.props);delete R.id;let w=g(S?.props);return{optionProps:{...M,...i(R,E,I,w),id:k},labelProps:{id:D},descriptionProps:{id:P},isFocused:F,isFocusVisible:F&&l.selectionManager.isFocused&&p(),isSelected:y,isDisabled:f,isPressed:C,allowsSelection:x,hasAction:B}}class Ee{constructor(e){this.keyMap=new Map,this.firstKey=null,this.lastKey=null,this.iterable=e;let t=e=>{if(this.keyMap.set(e.key,e),e.childNodes&&"section"===e.type)for(let l of e.childNodes)t(l)};for(let s of e)t(s);let l=null,o=0,a=0;for(let[s,i]of this.keyMap)l?(l.nextKey=s,i.prevKey=l.key):(this.firstKey=s,i.prevKey=void 0),"item"===i.type&&(i.index=o++),"section"!==i.type&&"item"!==i.type||a++,l=i,l.nextKey=void 0;this._size=a,this.lastKey=l?.key??null}*[Symbol.iterator](){yield*this.iterable}get size(){return this._size}getKeys(){return this.keyMap.keys()}getKeyBefore(e){let t=this.keyMap.get(e);return t?t.prevKey??null:null}getKeyAfter(e){let t=this.keyMap.get(e);return t?t.nextKey??null:null}getFirstKey(){return this.firstKey}getLastKey(){return this.lastKey}getItem(e){return this.keyMap.get(e)??null}at(e){const t=[...this.getKeys()];return this.getItem(t[e])}getChildren(e){let t=this.keyMap.get(e);return t?.childNodes||[]}}function Ce(e){let{filter:t,layoutDelegate:l}=e,o=f(e),a=ve.useMemo(()=>e.disabledKeys?new Set(e.disabledKeys):new Set,[e.disabledKeys]),s=ve.Line truncated
@@ -1 +0,0 @@
.rp-swatch{border:1px solid var(--rp-line);border-radius:var(--rp-r-md);flex:none;width:120px;height:72px;display:block;position:relative;overflow:hidden}.rp-swatch>span{clip-path:polygon(0 0,100% 0,0 100%);background:var(--sw-bg);position:absolute;inset:0}.rp-swatch>span[data-dark]{transform:rotate(180deg)}.rp-swatch>span:before,.rp-swatch>span:after,.rp-swatch i,.rp-swatch i:after{content:"";position:absolute}.rp-swatch>span:before{background:color-mix(in srgb, var(--sw-bg), var(--sw-text) 10%);inset:0 85% 0 0}.rp-swatch i{background:linear-gradient(var(--sw-text) 0 0) 14% 50% / 50% 18% no-repeat var(--sw-surface);border-radius:12%/20%;inset:11% 45% 58% 20%}.rp-swatch i:after{background:var(--sw-positive);border-radius:50%;inset:36% 10% 36% 76%}.rp-swatch>span:after{border-radius:var(--rp-r-pill);background:var(--sw-accent);inset:50% 63% 41% 20%}.rp-swatch[data-look]>span{--sw-wall:radial-gradient(circle at 20% 25%, #007aff99, transparent 60%), radial-gradient(circle at 85% 20%, #ff2d558c, transparent 55%), radial-gradient(circle at 70% 95%, #ff950099, transparent 60%), linear-gradient(160deg, #e3eaf8, #f8eee2);background:var(--sw-wall)}.rp-swatch[data-look]>span[data-dark]{--sw-wall:radial-gradient(circle at 20% 25%, #0a84ff80, transparent 60%), radial-gradient(circle at 85% 20%, #ff375f66, transparent 55%), radial-gradient(circle at 70% 95%, #ff9f0a66, transparent 60%), linear-gradient(160deg, #070a17, #05131a)}.rp-swatch[data-look]>span:before{background:var(--sw-surface)}.rp-swatch[data-look=lens] i,.rp-swatch[data-look=lens]>span:after{--sw-rim:#ffffffe6;box-shadow:inset 1px 1px 0 var(--sw-rim), inset -1px -1px 0 color-mix(in srgb, var(--sw-rim), transparent 45%)}.rp-select-boxes{gap:var(--rp-space-4);display:grid}.rp-select-grid{gap:var(--rp-space-4);grid-template-columns:repeat(auto-fill,170px);display:grid}.rp-select-grid>section{display:contents}.rp-select-box-head{margin-top:var(--rp-space-1);grid-column:1/-1;font-weight:700}.rp-select-box{align-items:center;gap:var(--rp-space-1);min-width:0;padding:var(--rp-space-6) var(--rp-space-3) var(--rp-space-3);border-radius:var(--rp-r-lg);background:var(--rp-surface);box-shadow:var(--rp-shadow);text-align:center;cursor:default;transition:box-shadow var(--rp-duration) var(--rp-ease), border-color var(--rp-duration) var(--rp-ease);border:2px solid #0000;flex-direction:column;display:flex;position:relative}.rp-select-box>.rp-swatch{margin-bottom:var(--rp-space-1)}.rp-select-box:is([data-hovered],[data-selected]){box-shadow:var(--rp-elevated)}.rp-select-box[data-selected]{border-color:var(--rp-accent)}.rp-select-box-check{width:var(--rp-space-4);height:var(--rp-space-4);color:var(--rp-accent);visibility:hidden;position:absolute;inset-block-start:var(--rp-space-1);inset-inline-end:var(--rp-space-1)}.rp-select-box[data-selected] .rp-select-box-check{visibility:visible}@media (width<=599.98px){.rp-select-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./WallpaperSettings-Bkh54SrZ.js","./vendor-react-64fcanlc.js","./_commonjsHelpers-CXUWDbkB.js","./index-DFPUSiWe.js","./index-BnzLYrTn.css","./logs-lI5kXcWp.js","./Select-BFkb6iie.js","./ItemText-u3CUc_6I.js","./AppearanceControls-Bpq6OQjk.js","./nodes-HzIJdCpg.js","./wallpaper-CEC7gitM.js"])))=>i.map(i=>d[i]);
import{cu as e,cw as s,bC as t,a2 as a,b1 as i,h as n,Z as r,aM as l,dG as o,dH as d,u as c,dI as g,dJ as u,cI as p,ds as m,bH as f,bD as h,bO as b,D as v,bF as x,dK as j,dL as k,bG as y,dM as N,s as w,dN as _,dO as S,r as C,y as P,as as D,dP as I,dQ as R,dR as L,dS as T,dT as H,cd as U,ao as E,an as F,bX as M,t as q,bM as O,V,bS as A,aL as B,C as W,E as G,c as z,ag as $,S as K,bm as J,a as Y,ce as Z,b as X,bJ as Q,cl as ee,dt as se,B as te,aZ as ae,aO as ie,dU as ne,dV as re,cm as le,aB as oe,T as de,m as ce,bK as ge,bz as ue,dW as pe,dX as me,dY as fe,bZ as he,dZ as be,_ as ve,w as xe}from"./index-DFPUSiWe.js";import{j as je,r as ke,a as ye}from"./vendor-react-64fcanlc.js";import{p as Ne,L as we,z as _e,c as Se,D as Ce,K as Pe,H as De,X as Ie,Y as Re,I as Le}from"./logs-lI5kXcWp.js";import{T as Te}from"./Tabs-HxpCyCLR.js";import{$ as He,d as Ue,a as Ee}from"./Select-BFkb6iie.js";import{C as Fe}from"./ItemText-u3CUc_6I.js";import{d as Me,c as qe}from"./auth-Br_GGGEt.js";import{e as Oe}from"./nodes-HzIJdCpg.js";import{g as Ve,r as Ae,c as Be,s as We,a as Ge,b as ze,d as $e,e as Ke}from"./nav-lSzYOU50.js";import{N as Je,t as Ye,n as Ze}from"./NumberField-kCp6sey7.js";import{aO as Xe,aP as Qe,aQ as es}from"./activity-pUN0wcVd.js";import{D as ss}from"./Table-6xnX6eUx.js";import{T as ts}from"./TimeCell-CRwMWdtu.js";import{l as as,a as is,d as ns,c as rs}from"./geodataUpdate-BANPx3Of.js";import{a as ls,v as os,m as ds,b as cs,g as gs}from"./geodata-CvcFemyc.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./AppearanceControls-Bpq6OQjk.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./Virtualizer-BH6NJSXn.js";import"./Checkbox-xU-Ds7Hn.js";function us({swatch:e,look:s}){return je.jsx("span",{className:"rp-swatch","data-look":s,"aria-hidden":"true",children:e.map((e,s)=>{const[t,a,i,n,r]=e.split(" ");return je.jsx("span",{"data-dark":1===s||void 0,style:{"--sw-bg":t,"--sw-surface":a,"--sw-text":i,"--sw-accent":n,"--sw-positive":r},children:je.jsx("i",{})},s)})})}function ps({label:t,sections:a,value:i,onChange:n,help:r}){const l=ke.useId();return je.jsxs("div",{className:"rp-select-boxes",children:[je.jsx("span",{id:`${l}-label`,className:"rp-label",children:t}),je.jsx(He,{className:"rp-select-grid",layout:"grid",selectionMode:"single",disallowEmptySelection:!0,selectedKeys:[i],onSelectionChange:e=>"all"!==e&&n(String([...e][0])),"aria-labelledby":`${l}-label`,"aria-describedby":r?`${l}-help`:void 0,children:a.map(({title:t,plain:a,items:i})=>{const n=i.length>1||i[0].label!==t;return je.jsxs(Ue,{"aria-label":n?void 0:t,children:[n&&je.jsx(e,{className:"rp-select-box-head",children:t}),i.map(e=>je.jsxs(Ee,{id:e.id,className:"rp-select-box",textValue:e.label,"aria-label":!n||a||e.label.includes(t)?e.label:`${t} ${e.label}`,children:[je.jsx(us,{swatch:e.swatch,look:e.look}),je.jsx("span",{children:e.label}),e.desc&&je.jsx(s,{slot:"description",className:"rp-label rp-truncate",children:e.desc}),je.jsx(Fe,{className:"rp-select-box-check"})]},e.id))]},t)})}),r&&je.jsx("span",{id:`${l}-help`,className:"rp-label",children:r})]})}const ms={"rose-pine/main":["#faf4ed #fffaf3 #464261 #286983 #56949f","#191724 #1f1d2e #e0def4 #31748f #9ccfd8"],"rose-pine/moon":["#faf4ed #fffaf3 #464261 #286983 #56949f","#232136 #2a273f #e0def4 #3e8fb0 #9ccfd8"],"catppuccin/frappe":["#e6e9ef #eff1f5 #4c4f69 #1e66f5 #179299","#292c3c #303446 #c6d0f5 #8caaee #81c8be"],"catppuccin/macchiato":["#e6e9ef #eff1f5 #4c4f69 #1e66f5 #179299","#1e2030 #24273a #cad3f5 #8aadf4 #8bd5ca"],"catppuccin/mocha":["#e6e9ef #eff1f5 #4c4f69 #1e66f5 #179299","#181825 #1e1e2e #cdd6f4 #89b4fa #94e2d5"],"arco/arco":["#ffffff #ffffff #1d2129 #165dff #00b42a","#17171a #232324 rgba(255,255,255,0.9) #3c7eff #27c346"],"semi/semi":["#ffffff #ffffff #1c1f23 #0064fa #3bb346","#16161a #232429 #f9f9f9 #54a9ff #5dc264"],"glass/glass":["rgba(255,255,255,0.4) rgba(255,255,255,0.4) #000000 #007aff #34c759","rgba(30,30,34,0.5) rgba(30,30,34,0.5) #ffffff #0a84ff #30d158"],"glass/clear":["rgba(255,255,255,0.4) rgba(255,255,255,0.4) #000000 #007aff #34c759","rgba(30,30,34,0.5) rgba(30,30,34,0.5) #ffffff #0a84ff #30d158"],"glass/frosted":["rgba(255,255,255,0.6) rgba(255,255,255,0.6) #000000 #007aff #34c759","rgba(23,23,23,0.5) rgba(23,23,23,0.5) #ffffff #0a84ff #30d158"],"glass/tinted":["rgba(255,255,255,0.85) rgba(255,255,255,0.85) #000000 #007aff #34c759","rgba(36,36,40,0.85) rgba(36,36,40,0.85) #ffffff #0a84ff #30d158"],"nord/nord":["#e5e9f0 #eceff4 #2e3440 #5e81ac #8fbcbb","#2e3440 #3b4252 #eceff4 #81a1c1 #8fbcbb"],"kary/kary":["#e7f0f7 #f0f6fb #65758d #3778b7 #428226","#1a1a1a #242424 #cacaca #819dc2 #7da76f"],"antd/antd":["#f5f5f5 #ffffff rgba(0,0,0,0.88) #1677ff #52c41a","#000000 #141414 rgba(255,255,255,0.85) #1668dc #49aa19"],"qiangguo/qiangguo":["#fffaf6 #f8ebe5 #2b0a0a #b0121a #1d6322","#2a0000 #4a0004 #ffd700 #ff6b5b #b8f2a0"],"halloween/halloween":["#fdf6e9 #f7ecd8 #2a1a33 #a84300 #2f6b1f","#140e1c #1d1528 #f3e9dc #ff8c3a #a8d84e"]},fs={"rose-pine/main":"palette.dLine truncated
@@ -1 +0,0 @@
import{r as t,j as e}from"./vendor-react-64fcanlc.js";import{g as s,gi as i}from"./index-DFPUSiWe.js";import{a,aU as r,aV as o,aX as l,a_ as n,a$ as h,b0 as d}from"./activity-pUN0wcVd.js";import{G as m,C as c}from"./AreaCurve-DQSHOSpG.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./nodes-HzIJdCpg.js";import"./logs-lI5kXcWp.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./array-C0VuvQyb.js";function u({values:u,timestamps:p,color:x,fmt:j,locale:y,height:g=32,floor:f=0,inset:v=0,lines:w}){const M=t.useId(),b=a(),[k,C]=t.useState(null),[$,A]=s(Math.round,120),{tip:I,showAt:L,hide:P}=r(),S=t.useMemo(()=>u.filter(t=>null!==t),[u]),T=S.length>1,U=t.useMemo(()=>[f>0?0:.85*Math.min(...S),Math.max(1.05*Math.max(...S)||1,f)],[S,f]),W=A?.width??0,_=(A?.height??g)-2,z=t.useMemo(()=>{const t=[Math.min(...p),Math.max(...p)];return p.map(e=>o(e,t,[v,Math.max(v,W-v)]))},[p,W,v]),B=t.useCallback(t=>o(t,U,[_,2]),[U,_]),D=t.useMemo(()=>u.map((t,e)=>null===t?null:{x:z[e],y:B(t)}),[u,z,B]);return t.useLayoutEffect(()=>{null!==k&&null!=u[k]&&j&&y&&$.current&&A?L({x:z[k],y:B(u[k]),width:A.width,bounds:l($.current),label:i(y).format(p[k]),lines:w?.(k)??[j(u[k])]}):P()}),e.jsxs("div",{ref:$,style:{height:`var(--rp-spark-fill, ${g}px)`,width:"100%",position:"relative"},"aria-hidden":!!T||void 0,onPointerLeave:()=>C(null),children:[A&&!T&&e.jsx("svg",{"aria-hidden":!0,width:A.width,height:A.height,style:{display:"block",position:"absolute",inset:0},children:e.jsx("line",{x1:v,x2:A.width-v,y1:_,y2:_,stroke:b["hl-med"],strokeDasharray:"2 4"})}),T&&A&&e.jsxs(e.Fragment,{children:[e.jsx(n,{tip:I}),e.jsxs("svg",{className:"rp-activity-surface",width:A.width,height:A.height,viewBox:`0 0 ${A.width} ${A.height}`,style:{display:"block",position:"absolute",inset:0},onPointerMove:t=>{const e=h(t,t.currentTarget,A);C(e.x<0||e.x>A.width||e.y<2||e.y>A.height-2?null:d(z,e.x))},children:[e.jsx("defs",{children:e.jsx(m,{id:M,color:x})}),e.jsx(c,{points:D,baseline:B(Math.max(0,U[0])),color:x,id:M,strokeWidth:1.5}),null!==k&&null!=u[k]&&e.jsx("circle",{cx:z[k],cy:B(u[k]),r:4,fill:x,stroke:b.base,strokeWidth:2})]})]})]})}export{u as SparkPlot};
@@ -1 +0,0 @@
import{j as e}from"./vendor-react-64fcanlc.js";import{u as a,ao as s,an as l,bK as r,ce as n,cm as t}from"./index-DFPUSiWe.js";import{N as i,t as o,n as d}from"./NumberField-kCp6sey7.js";import{p as c}from"./logs-lI5kXcWp.js";import{q as u}from"./nodes-HzIJdCpg.js";import{bD as m,bE as p,bF as b,bG as h}from"./activity-pUN0wcVd.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";function g({value:g,onChange:v,fields:j,isDisabled:x,nameError:C,agentError:D,intervalError:f,urlError:E,focusInterval:F}){const N=a(),k=s[l()],S=e=>v({...g,...e}),R=g.route||"routing",y=g.interval||(null==j.interval?"":String(j.interval)),A=m(y);return e.jsxs(e.Fragment,{children:[e.jsx(r,{isDisabled:x,label:N("ui.name"),value:g.name,placeholder:"sub-a",spellCheck:!1,error:C??void 0,onChange:e=>S({name:e})}),e.jsx(r,{isDisabled:x,label:N("nodes.url"),value:g.url,placeholder:"https://example.org/sub?token=…",description:N("nodes.urlScheme"),error:E??void 0,spellCheck:!1,onChange:e=>S({url:e}),onBlur:()=>{const e=u(g.url);e!==g.url&&S({url:e})}}),void 0!==j.interval&&e.jsxs(e.Fragment,{children:[e.jsx(c,{takeFocus:F,label:N("nodes.interval"),items:[...null===j.interval?[{id:"",label:"—"}]:[],...p(j.interval,k,N)],value:A?"typed":y,isDisabled:x,onChange:e=>S({interval:"typed"===e?h(y):e})}),A&&e.jsxs(n,{className:"top",children:[e.jsx(i,{isDisabled:x,label:N("nodes.intervalCount"),width:120,value:d(A.count),minValue:0,maxValue:Number.MAX_SAFE_INTEGER,step:1,error:f??void 0,onChange:e=>S({interval:b(o(e),A.unit)})}),e.jsx(c,{label:N("nodes.intervalUnit"),items:[{id:"m",label:N("nodes.minutes")},{id:"h",label:N("nodes.hours")}],value:A.unit,isDisabled:x,onChange:e=>S({interval:b(A.count,e)})})]})]}),j.agent&&e.jsx(r,{isDisabled:x,label:N("nodes.agent"),value:g.agent,placeholder:j.agent.fallback,description:j.agent.description,error:D??void 0,spellCheck:!1,onChange:e=>S({agent:e})}),void 0!==j.cache&&e.jsxs(e.Fragment,{children:[e.jsx(t,{isSelected:g.cache??j.cache,isDisabled:x,onChange:e=>S({cache:e}),children:N("nodes.cache")}),e.jsx("span",{className:"rp-label",children:N("nodes.cacheHelp")})]}),j.routes&&e.jsxs(e.Fragment,{children:[e.jsx(c,{label:N("settings.geodataRoute"),items:[{id:"routing",label:N("settings.geodataRouteRouting")},{id:"direct",label:N("settings.geodataRouteDirect")},...[...new Set([...j.routes,..."routing"===R||"direct"===R?[]:[R]])].map(e=>({id:e,label:e}))],value:R,isDisabled:x,onChange:e=>S({route:e})}),e.jsx("span",{className:"rp-label",children:N("nodes.routeHelp")})]})]})}export{g as SubscriptionFields};
@@ -1 +0,0 @@
import{r as e,$ as t,b as l,j as n}from"./vendor-react-64fcanlc.js";import{ei as i,ej as o,ek as r,el as s,em as a,en as d,c_ as c,eo as u,ep as h,eq as y,bt as p,e3 as f,cW as g,cP as m,er as b,cZ as w,cO as v,es as x,et as k,eu as C,ev as K,e0 as I,cX as S,ew as M,ex as E,ey as R,ez as z,eA as D,eB as P,eC as N,cM as H,cN as W,eD as T,eE as A,e2 as F,eF as V,cV as L,eG as B,eH as j,eI as O,d0 as $,e4 as U,e7 as _,eJ as Y,eK as q,db as X,$ as G,ea as Z,eL as J,dd as Q,d4 as ee,d6 as te,eM as le,eN as ne,da as ie,eO as oe,eP as re,de as se,e5 as ae,ef as de,d5 as ce,eQ as ue,e6 as he,eR as ye,eS as pe,eT as fe,eU as ge,eV as me,ec as be,eW as we,eX as ve,ee as xe,u as ke,j as Ce,b5 as Ke,b6 as Ie,eY as Se,ah as Me,cq as Ee,aF as Re,aE as ze,bz as De,b9 as Pe,eZ as Ne,T as He}from"./index-DFPUSiWe.js";import{a as We,$ as Te,b as Ae,c as Fe,d as Ve,e as Le,f as Be}from"./useGridSelectionCheckbox-wMhI-U-R.js";import{f as je,g as Oe,h as $e,i as Ue,j as _e}from"./Select-BFkb6iie.js";import{$ as Ye}from"./logs-lI5kXcWp.js";import{a as qe,c as Xe,e as Ge,d as Ze,$ as Je}from"./Virtualizer-BH6NJSXn.js";import{C as Qe}from"./Checkbox-xU-Ds7Hn.js";class et{setup(e,t,l){this.delegate=e,this.state=t,this.direction=l}getDropTargetFromPoint(e,t,l){let n=this.delegate.getDropTargetFromPoint(e,t,l);return n&&"root"!==n.type?this.resolveDropTarget(n,e,t,l):n}resolveDropTarget(e,t,l,n){let i=this.pointerTracking,o=l-i.lastY,r=t-i.lastX,s=i.yDirection,a=i.xDirection;if(Math.abs(o)>5&&(s=o>0?"down":"up",i.yDirection=s,i.lastY=l),Math.abs(r)>10&&(a=r>0?"right":"left",i.xDirection=a,i.lastX=t),"before"===e.dropPosition){let t=this.state.collection.getKeyBefore(e.key);for(;null!=t;){let e=this.state.collection.getItem(t);if("item"===e?.type)break;t=e?.parentKey??null}if(null!=t){let l={type:"item",key:t,dropPosition:"after"};n(l)&&(e=l)}}let d,c=this.getPotentialTargets(e,n);return 0===c.length?{type:"root"}:(c.length>1?d=this.selectTarget(c,e,t,l,s,a):(d=c[0],i.boundaryContext=null),d)}getPotentialTargets(e,t){if("on"===e.dropPosition)return[e];let l=e,n=this.state.collection,i=n.getItem(l.key);for(;i&&"item"!==i?.type&&null!=i.nextKey;)l.key=i.nextKey,i=n.getItem(i.nextKey);let o=[l];if(i&&i.hasChildNodes&&this.state.expandedKeys.has(i.key)&&n.getChildren&&"after"===l.dropPosition){let e=null!=i.firstChildKey?n.getItem(i.firstChildKey):null;for(;e&&"item"!==e.type;)e=null!=e.nextKey?n.getItem(e.nextKey):null;if("item"===e?.type){const l={type:"item",key:e.key,dropPosition:"before"};return t(l)?[l]:[]}}if(null!=i?.nextKey)return[e];let r=i?.parentKey,s=[];for(;null!=r;){let e=n.getItem(r),l=null!=e?.nextKey?n.getItem(e.nextKey):null;if(!l||l.parentKey!==r){if("item"===e?.type){let e={type:"item",key:r,dropPosition:"after"};t(e)&&s.push(e)}if(l)break}r=e?.parentKey}if(s.length>0&&o.push(...s),1===o.length){let e=n.getKeyAfter(l.key),o=null!=e?n.getItem(e):null;if(null!=e&&o&&i&&null!=o.level&&null!=i.level&&o.level>i.level){let l={type:"item",key:e,dropPosition:"before"};if(t(l))return[l]}}return o.filter(t)}selectTarget(e,t,l,n,i,o){if(e.length<2)return e[0];let r=this.pointerTracking,s=this.state.collection.getItem(t.key),a=s?.parentKey;if(null==a)return e[0];if(!r.boundaryContext||r.boundaryContext.parentKey!==a){let t="up"===r.yDirection?e.length-1:0;r.boundaryContext={parentKey:a,preferredTargetIndex:t,lastSwitchY:n,lastSwitchX:l}}let d=r.boundaryContext,c=Math.abs(l-d.lastSwitchX);if(Math.abs(n-d.lastSwitchY)>5&&i){let t=d.preferredTargetIndex||0;"down"===i&&0===t?d.preferredTargetIndex=e.length-1:"up"===i&&t===e.length-1&&(d.preferredTargetIndex=0),r.xDirection=null}if(c>10&&o){let t=d.preferredTargetIndex||0;"left"===o?"ltr"===this.direction?t<e.length-1&&(d.preferredTargetIndex=t+1,d.lastSwitchX=l):t>0&&(d.preferredTargetIndex=t-1,d.lastSwitchX=l):"right"===o&&("ltr"===this.direction?t>0&&(d.preferredTargetIndex=t-1,d.lastSwitchX=l):t<e.length-1&&(d.preferredTargetIndex=t+1,d.lastSwitchX=l)),r.yDirection=null}return e[Math.max(0,Math.min(d.preferredTargetIndex||0,e.length-1))]}constructor(){this.delegate=null,this.state=null,this.direction="ltr",this.pointerTracking={lastY:0,lastX:0,yDirection:null,xDirection:null,boundaryContext:null}}}class tt{constructor(e){this.keyMap=new Map,this.keyMap=new Map,this.columnCount=e?.columnCount,this.rows=[];let t=n=>{let i=this.keyMap.get(n.key);e.visitNode&&(n=e.visitNode(n)),this.keyMap.set(n.key,n);let o=new Set,r=null,s=!1;if("item"===n.type)for(let e of n.childNodes)if(void 0!==e.props?.colSpan){s=!0;break}for(let e of n.childNodes)"cell"===e.type&&s&&(e.colspan=e.props?.colSpan,e.colSpan=e.props?.colSpan,e.colIndex=r?(r.colIndex??r.index)+(r.colSpan??1):e.index),"cell"===e.type&&null==e.parentKey&&(e.parentKey=n.key),o.add(e.key),r?(r.nextKey=e.key,e.prevKey=r.key):e.prevKey=null,t(e),r=e;if(r&&(r.nextKey=null),i)for(let e of i.childNodes)o.has(e.key)||l(e)},l=e=>{this.keyMap.delete(e.key);for(let t of e.childNodes)this.keyMap.get(t.key)===t&&l(t)},n=null;for(let[i,o]of e.itemLine truncated
@@ -1 +0,0 @@
import{r as e,$ as t,j as s}from"./vendor-react-64fcanlc.js";import{e0 as i,cP as l,e1 as a,cN as n,cO as o,e2 as r,bt as d,e3 as c,cW as u,c_ as b,e4 as h,e5 as f,db as p,dd as y,de as v,d4 as g,d5 as K,e6 as m,d0 as S,e7 as x,e8 as P,e9 as D,ea as C,d1 as E,da as w,eb as F,ec as M,d6 as N,ed as I,ee as k,ef as j,be as R,eg as H,ce as L,eh as V}from"./index-DFPUSiWe.js";import{$ as T}from"./useGridSelectionCheckbox-wMhI-U-R.js";import{e as A}from"./Select-BFkb6iie.js";const O=new WeakMap;function z(e,t,s){if(!e)return"";return"string"==typeof t&&(t=t.replace(/\s+/g,"")),`${O.get(e)}-${s}-${t}`}class ${constructor(e,t,s,i=new Set){this.collection=e,this.flipDirection="rtl"===t,this.disabledKeys=i,this.tabDirection="horizontal"===s}getKeyLeftOf(e){return this.flipDirection?this.getNextKey(e):this.getPreviousKey(e)}getKeyRightOf(e){return this.flipDirection?this.getPreviousKey(e):this.getNextKey(e)}isDisabled(e){return this.disabledKeys.has(e)||!!this.collection.getItem(e)?.props?.isDisabled}getFirstKey(){let e=this.collection.getFirstKey();return null!=e&&this.isDisabled(e)&&(e=this.getNextKey(e)),e}getLastKey(){let e=this.collection.getLastKey();return null!=e&&this.isDisabled(e)&&(e=this.getPreviousKey(e)),e}getKeyAbove(e){return this.tabDirection?null:this.getPreviousKey(e)}getKeyBelow(e){return this.tabDirection?null:this.getNextKey(e)}getNextKey(e){let t=e;do{t=this.collection.getKeyAfter(t),null==t&&(t=this.collection.getFirstKey())}while(null!=t&&this.isDisabled(t)&&t!==e);return t}getPreviousKey(e){let t=e;do{t=this.collection.getKeyBefore(t),null==t&&(t=this.collection.getLastKey())}while(null!=t&&this.isDisabled(t)&&t!==e);return t}}function B(t){let s=function(t){let[s,i]=b(t.selectedKey,t.defaultSelectedKey??null,t.onSelectionChange),l=e.useMemo(()=>null!=s?[s]:[],[s]),{collection:a,disabledKeys:n,selectionManager:o}=A({...t,selectionMode:"single",disallowEmptySelection:!0,allowDuplicateSelectionEvents:!0,selectedKeys:l,onSelectionChange:e=>{if("all"===e)return;let l=e.values().next().value??null;l===s&&t.onSelectionChange&&t.onSelectionChange(l),i(l)}}),r=null!=s?a.getItem(s):null;return{collection:a,disabledKeys:n,selectionManager:o,selectedKey:s,setSelectedKey:i,selectedItem:r}}({...t,onSelectionChange:t.onSelectionChange?e=>{null!=e&&t.onSelectionChange?.(e)}:void 0,suppressTextValueWarning:!0,defaultSelectedKey:t.defaultSelectedKey??W(t.collection,t.disabledKeys?new Set(t.disabledKeys):new Set)??void 0}),{selectionManager:i,collection:l,selectedKey:a}=s,n=e.useRef(a);return e.useEffect(()=>{let e=a;null!=t.selectedKey||!i.isEmpty&&null!=e&&l.getItem(e)||(e=W(l,s.disabledKeys),null!=e&&i.setSelectedKeys([e])),(null!=e&&null==i.focusedKey||!i.isFocused&&e!==n.current)&&i.setFocusedKey(e),n.current=e}),{...s,isDisabled:t.isDisabled||!1}}function W(e,t){let s=null;if(e){for(s=e.getFirstKey();null!=s&&(t.has(s)||e.getItem(s)?.props?.isDisabled)&&s!==e.getLastKey();)s=e.getKeyAfter(s);null!=s&&(t.has(s)||e.getItem(s)?.props?.isDisabled)&&s===e.getLastKey()&&(s=e.getFirstKey())}return s}const U=e.createContext(null),G=e.createContext(null),_=e.forwardRef(function(s,i){[s,i]=S(s,i,U);let{children:l,orientation:a="horizontal"}=s;return l=e.useMemo(()=>"function"==typeof l?l({orientation:a,defaultChildren:null}):l,[l,a]),t.createElement(x,{content:l},e=>t.createElement(q,{props:s,collection:e,tabsRef:i}))});function q({props:s,tabsRef:i,collection:a}){let{orientation:n="horizontal"}=s,r=B({...s,collection:a,children:void 0}),{focusProps:d,isFocused:c,isFocusVisible:u}=y({within:!0}),b=e.useMemo(()=>({orientation:n,isFocusWithin:c,isFocusVisible:u}),[n,c,u]),h=g({...s,defaultClassName:"react-aria-Tabs",values:b}),f=l(s,{global:!0});return t.createElement(K.div,{...o(f,h,d),ref:i,slot:s.slot||void 0,"data-focused":c||void 0,"data-orientation":n,"data-focus-visible":u||void 0,"data-disabled":r.isDisabled||void 0},t.createElement(N,{values:[[U,s],[G,r]]},h.children))}const J=e.forwardRef(function(s,i){return e.useContext(G)?t.createElement(Q,{props:s,forwardedRef:i}):t.createElement(h,s)});function Q({props:s,forwardedRef:i}){let a=e.useContext(G),{CollectionRoot:n}=e.useContext(C),{orientation:b="horizontal",keyboardActivation:h="automatic"}=E(U),f=p(i),{tabListProps:y}=function(t,s,i){let{orientation:l="horizontal",keyboardActivation:a="automatic"}=t,{collection:n,selectionManager:b,disabledKeys:h}=s,{direction:f}=d(),p=e.useMemo(()=>new $(n,f,l,h),[n,h,l,f]),{collectionProps:y}=c({ref:i,selectionManager:b,keyboardDelegate:p,selectOnFocus:"automatic"===a,disallowEmptySelection:!0,scrollRef:i,linkBehavior:"selection"}),v=u();O.set(s,v);let g=r({...t,id:v});return{tabListProps:{...o(y,g),role:"tablist","aria-orientation":l,tabIndex:void 0}}}({...s,orientation:b,keyboardActivation:h},a,f),v=g({...s,children:null,defaultClassName:"react-aria-TabList",values:{orientation:b,state:a}}),m=l(s,{global:!0});return delete m.id,t.createElement(K.div,{...o(m,v,y),ref:f,"data-orientation":b||void 0},t.createElement(Line truncated
@@ -1 +0,0 @@
import{r as e,j as n,a as t}from"./vendor-react-64fcanlc.js";import{an as r,b1 as s,c as a,dv as l,cq as c,dw as o,b9 as u}from"./index-DFPUSiWe.js";function i({children:e,tone:t,action:r}){return n.jsxs("span",{className:u("rp-tag",t),children:[n.jsx("span",{className:"rp-tag-label",children:e}),r]})}function g({label:e,children:t}){return n.jsx("div",{className:"rp-tags",role:"group","aria-label":e,children:t})}function h({href:e,children:t}){return n.jsx(a,{href:e,className:"rp-tag rp-tag-link",children:n.jsx("span",{className:"rp-tag-label",children:t})})}function d({count:t,label:r,names:s}){const[a,u]=e.useState(!1);return n.jsxs(l,{delay:400,isOpen:a,onOpenChange:u,shouldCloseOnPress:!1,children:[n.jsx(c,{className:"rp-tag rp-tag-more","aria-label":r,onPress:e=>u(n=>"touch"!==e.pointerType||!n),children:n.jsxs("span",{className:"rp-tag-label",children:["+",t]})}),n.jsx(o,{children:s})]})}const p=(e,n)=>function(e,n,t,r){const s=e=>e<=t+.01,a=[0];for(const c of e)a.push(a[a.length-1]+c+r);if(s(a[e.length]-r))return e.length;let l=e.length-1;for(;l>0&&!s(a[l]+n(e.length-l));)l--;return l}(e.tags,n=>e.more[String(n).length-1],n,e.gap);function f(e,n,t){const r=[...e.children].map(e=>e.getBoundingClientRect().width);return{key:n,tags:r.slice(0,t),more:r.slice(t),gap:Number.parseFloat(getComputedStyle(e).columnGap)||0}}const m=new Map;let b=null;function j(e){const n=e.filter(e=>null!==e);n.length&&t.flushSync(()=>n.forEach(e=>e()))}function x({count:e}){return n.jsx("button",{type:"button",className:"rp-tag rp-tag-more",tabIndex:-1,"aria-hidden":"true",children:n.jsxs("span",{className:"rp-tag-label",children:["+",e]})})}function y({label:t,items:a,more:l}){const c=r(),o=e.useRef(null),u=e.useRef(null),i=e.useRef(new Set),g=`${c}\n${a.map(e=>e.label).join("\n")}`,[y,N]=e.useState({key:g,count:null,probe:!0}),w=y.key===g?y.count:null;e.useLayoutEffect(()=>{const e=o.current;if(y.probe||null!==w||!e?.getClientRects().length)return;const n=f(e,g,a.length);u.current=n,N({key:g,count:p(n,e.getBoundingClientRect().width)})},[y,g,w,a.length]),e.useLayoutEffect(()=>{const e=o.current;if(!e)return;const n=()=>N({key:g,count:null});m.set(e,{remeasure:n,read:()=>{if(!e.getClientRects().length)return null;if(y.probe){const n=f(e,g,a.length);u.current=n;const t=p(n,e.getBoundingClientRect().width);return()=>N({key:g,count:t})}const t=u.current,r=[...e.children].filter(e=>!e.classList.contains("rp-tag-more"));if(t?.key!==g||r.some((e,n)=>Math.abs(e.getBoundingClientRect().width-t.tags[n])>.5))return n;const s=p(t,e.getBoundingClientRect().width);return s===w?null:()=>N({key:g,count:s})}});const t=(b||(b=new ResizeObserver(e=>{j([...new Set(e.map(e=>e.target.closest(".rp-tags[data-fit]")))].map(e=>(e&&m.get(e)?.read())??null))}),document.fonts?.addEventListener("loadingdone",()=>j([...m.values()].map(e=>e.remeasure)))),b),r=new Set([e,...e.children]);for(const s of i.current)r.has(s)||t.unobserve(s);for(const s of r)i.current.has(s)||t.observe(s);i.current=r}),e.useLayoutEffect(()=>{const e=o.current,n=i;return()=>{e&&m.delete(e);for(const e of n.current)b?.unobserve(e);n.current=new Set}},[]);const R=null===w?0:a.length-w,k=R>0?s(c,a.map(e=>e.label)):"",v=null===w&&a.length?Array.from({length:String(a.length).length},(e,n)=>10**(n+1)-1):[];return n.jsxs("div",{ref:o,className:"rp-tags",role:"group","aria-label":t,"data-fit":"",children:[a.slice(0,w??a.length).map(e=>y.probe?n.jsx("span",{className:"rp-tag rp-tag-link","aria-hidden":"true",children:n.jsx("span",{className:"rp-tag-label",children:e.label})},e.id):n.jsx(h,{href:e.href,children:e.label},e.id)),v.map(e=>n.jsx(x,{count:e},e)),R>0&&n.jsx(d,{count:R,label:l(R),names:k})]})}export{y as F,h as L,g as T,i as a};
@@ -1 +0,0 @@
import{j as a}from"./vendor-react-64fcanlc.js";import{ao as s,an as o,dV as r,T as t,dU as n,a3 as e}from"./index-DFPUSiWe.js";function d({at:d}){const i=s[o()],c=r();return a.jsx(t,{text:d?e(d,i):void 0,children:n(d,i,c)})}export{d as T};
@@ -1 +0,0 @@
import{r as e,j as r}from"./vendor-react-64fcanlc.js";import{bW as s,u as a,an as i,ab as l,r as t,t as n,A as o,D as d,cd as u,a8 as m,a5 as p,aL as c,bJ as h,cl as j,ce as b,bK as v,B as x,cn as f,ah as _,T as k,C as y,c as g}from"./index-DFPUSiWe.js";import{u as N,r as w}from"./flows-Cd1KbGP2.js";import{c as C,g as q,E as M,f as A,s as L,R as S,L as D,p as R,D as T,K as V}from"./logs-lI5kXcWp.js";import{e as E,t as F,c as I,n as P,d as O,q as W,a as Q,b as $}from"./Rules-Bt5JZQKX.js";import{b as H,p as K}from"./probe-Db_cbYvO.js";import{u as U,R as z}from"./useQuickRule-BGNvNXBX.js";import{r as B}from"./activity-pUN0wcVd.js";import{N as G,t as J,n as X}from"./NumberField-kCp6sey7.js";import{D as Y}from"./Table-6xnX6eUx.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./nodes-HzIJdCpg.js";import"./Tabs-HxpCyCLR.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./DaeCode-DEjXqvav.js";import"./daeTokens-BVtuNnWZ.js";import"./CheckboxSet-CRYfymLL.js";import"./Checkbox-xU-Ds7Hn.js";import"./Coverage-BRhvKmi_.js";import"./rule-D1hMywhM.js";import"./labels-D8xxfRTQ.js";import"./nav-BLmatpph.js";import"./recorder-BHyqDNp5.js";import"./geodataUpdate-BANPx3Of.js";/* empty css */import"./LinkOut-B50sd8cz.js";import"./SearchSelect-CsXd0DEh.js";import"./SearchList-C88Y6c53.js";import"./Virtualizer-BH6NJSXn.js";import"./geodata-CvcFemyc.js";import"./AddCircle-V0Kluz-m.js";const Z=e=>/^\d+$/.test(e)&&Number(e)>=1&&Number(e)<=65535,ee={none:"rule.resolveNone",live:"rule.resolveLive",query:"rule.resolveQuery"};function re({form:r,setForm:h,advanced:j,setAdvanced:b,touched:v},x){const f=a(),_=i(),k=l(),y=t(),g=y.data?.resources,S=C(n(g,"groups",{whileLoading:!1})),D=q(n(g,"nodes",{whileLoading:!1})),R=N(n(g,"rules",{whileLoading:!1})),T=M(D.refetch),{choices:V}=T,z=U(x),G=e.useMemo(()=>new Map(S.data?.map(e=>[e.name,e])??[]),[S.data]),J=e.useMemo(()=>new Map(S.data?.map(e=>[e.id,e])??[]),[S.data]),X=e.useMemo(()=>new Map(D.data?.map(e=>[e.id,e])??[]),[D.data]),Y=e.useMemo(()=>new Map(R.data?.rules.map(e=>[e.rule_id,e])??[]),[R.data]),[re,se]=e.useState(null),ae=n(g,"providers",{whileLoading:!1}),ie=A(!!re&&ae),le=n(g,"groups",{whileLoading:!1}),{busy:te,error:ne,run:oe}=o(),de=ne??y.error,ue=e.useEffectEvent(e=>d("negative",f("rule.traceFailed"),u(e,f)));e.useEffect(()=>{de&&ue(de)},[de]);const me=r.domain.trim()||r.dst_ip.trim()?r.dst_ip.trim()&&!s(r.dst_ip)?{field:"dst_ip",key:"ui.invalidIp"}:Z(r.dst_port)?r.src_ip.trim()&&!s(r.src_ip)?{field:"src_ip",key:"ui.invalidIp"}:r.src_port.trim()&&!Z(r.src_port)?{field:"src_port",key:"rule.invalidPort"}:!r.dscp.trim()||(pe=r.dscp,/^\d+$/.test(pe)&&Number(pe)>=0&&Number(pe)<=63)?r.mark.trim()&&!(e=>/^(\d+|0x[\da-f]+)$/i.test(e.trim())&&Number(e.trim())<=4294967295)(r.mark)?{field:"mark",key:"rule.invalidMark"}:"live"!==r.resolve&&"query"!==r.resolve||r.domain.trim()?"live"!==r.resolve&&"query"!==r.resolve||!r.dst_ip.trim()?null:{field:"dst_ip",key:"rule.invalidLive"}:{field:"domain",key:"rule.invalidLive"}:{field:"dscp",key:"rule.invalidDscp"}:{field:"dst_port",key:"rule.invalidPort"}:{field:"domain",key:"rule.invalidTarget"};var pe;const ce=v?me:null,he=y.data?.resources.routing_trace,je=he?.resolve_modes??[],be=y.data?.resources.dns_query,ve=be?.record_types,xe=e.useMemo(()=>(ve??[]).filter(e=>"A"===e||"AAAA"===e),[ve]),fe=be?.limits?.max_types_per_request??1,_e=[...je,...!je.includes("live")&&je.includes("none")&&be?.available&&xe.length?["query"]:[]],ke=!1!==he?.available,ye=""!==r.domain.trim()&&!r.dst_ip.trim(),ge=r.resolve??(ye&&_e.includes("live")?"live":ye&&_e.includes("query")?"query":"none"),Ne=!me&&ke&&_e.includes(ge),we=e.useCallback(async()=>{if(te||!Ne)return;const e=function(e){const r=e=>s(e),a={network:e.network,dst_port:Number(e.dst_port),...e.domain.trim()?{domain:e.domain.trim()}:{dst_ip:r(e.dst_ip)}};return e.domain.trim()&&e.dst_ip.trim()&&(a.dst_ip=r(e.dst_ip)),e.src_ip.trim()&&(a.src_ip=r(e.src_ip)),e.src_port.trim()&&(a.src_port=Number(e.src_port)),a.pname=e.pname.trim(),e.dscp.trim()&&(a.dscp=Number(e.dscp)),e.mark.trim()&&(a.mark=Number(e.mark.trim())),a}(r),a=await oe("trace",r=>w({...k,dnsQuery:(e,s)=>L(k.dnsQuery,e,s,fe,r)},{input:e,resolve:ge,recordTypes:xe},r));se(a?{run:a,input:e}:null)},[k,te,Ne,r,ge,xe,fe,oe]),Ce=R.data?.generation_id,qe=e.useMemo(()=>re?.run.traces.flatMap(e=>e.evaluations.map(r=>({evaluation:r,traced:e.generation_id,current:Ce===e.generation_id}))).map(({evaluation:e,traced:r,current:s},a)=>{const i=e.rules.find(e=>"matched"===e.result),l="determinate"!==e.decision&&!e.outbound&&s?(i&&Y.get(i.rule_id)?.outbound)??null:null,t=e.outbound??l,n=t&&!m(t)?p(t,re.input.network,G,J,X):null,o=E(e,a,re.input.domain??void 0,l,n,!!n?.node&&V(n.node).length>0,T.busy,f,_);return{...o,rows:o.rows.map(e=>({...e,href:B(e.id,s&&Y.has(e.id))})),links:I(n,le,ae?ie.data?.providers:[],f),seed:F(re.input,e,r)}})??[],[re,Ce,YLine truncated
@@ -1 +0,0 @@
import{el as t,bt as e,cZ as i,er as n,f0 as s,f1 as o,ey as r,eH as l,eS as a,eU as h,f2 as d,ea as u,f3 as c}from"./index-DFPUSiWe.js";import{r as y,$ as g,a as f}from"./vendor-react-64fcanlc.js";class p{constructor(t=0,e=0){this.x=t,this.y=e}copy(){return new p(this.x,this.y)}equals(t){return this.x===t.x&&this.y===t.y}isOrigin(){return 0===this.x&&0===this.y}}class w{constructor(t=0,e=0,i=0,n=0){this.x=t,this.y=e,this.width=i,this.height=n}get maxX(){return this.x+this.width}get maxY(){return this.y+this.height}get area(){return this.width*this.height}get topLeft(){return new p(this.x,this.y)}get topRight(){return new p(this.maxX,this.y)}get bottomLeft(){return new p(this.x,this.maxY)}get bottomRight(){return new p(this.maxX,this.maxY)}intersects(t){return this.area>0&&t.area>0&&this.x<=t.x+t.width&&t.x<=this.x+this.width&&this.y<=t.y+t.height&&t.y<=this.y+this.height}containsRect(t){return this.x<=t.x&&this.y<=t.y&&this.maxX>=t.maxX&&this.maxY>=t.maxY}containsPoint(t){return this.x<=t.x&&this.y<=t.y&&this.maxX>=t.x&&this.maxY>=t.y}getCornerInRect(t){for(let e of["topLeft","topRight","bottomLeft","bottomRight"])if(t.containsPoint(this[e]))return e;return null}equals(t){return t.x===this.x&&t.y===this.y&&t.width===this.width&&t.height===this.height}pointEquals(t){return this.x===t.x&&this.y===t.y}sizeEquals(t){return this.width===t.width&&this.height===t.height}union(t){let e=Math.min(this.x,t.x),i=Math.min(this.y,t.y),n=Math.max(this.maxX,t.maxX)-e,s=Math.max(this.maxY,t.maxY)-i;return new w(e,i,n,s)}intersection(t){if(!this.intersects(t))return new w(0,0,0,0);let e=Math.max(this.x,t.x),i=Math.max(this.y,t.y);return new w(e,i,Math.min(this.maxX,t.maxX)-e,Math.min(this.maxY,t.maxY)-i)}copy(){return new w(this.x,this.y,this.width,this.height)}}class z{constructor(t=0,e=0){this.width=Math.max(t,0),this.height=Math.max(e,0)}copy(){return new z(this.width,this.height)}equals(t){return this.width===t.width&&this.height===t.height}get area(){return this.width*this.height}}let v=0;class m{constructor(t,e){this.virtualizer=t,this.key=++v,this.viewType=e,this.children=new Set,this.reusableViews=new Map,this.layoutInfo=null,this.content=null,this.rendered=null}prepareForReuse(){this.content=null,this.rendered=null,this.layoutInfo=null}getReusableView(t){let e=this.reusableViews.get(t);return e&&e.length>0?e.shift():new b(this.virtualizer,this,t)}reuseChild(t){t.prepareForReuse();let e=this.reusableViews.get(t.viewType);e||(e=[],this.reusableViews.set(t.viewType,e)),e.push(t)}}class S extends m{constructor(t){super(t,"root")}}class b extends m{constructor(t,e,i){super(t,i),this.parent=e}}class x{setVisibleRect(t){let e=performance.now()-this.startTime;e<500&&(t.x!==this.visibleRect.x&&e>0&&(this.velocity.x=(t.x-this.visibleRect.x)/e),t.y!==this.visibleRect.y&&e>0&&(this.velocity.y=(t.y-this.visibleRect.y)/e)),this.startTime=performance.now(),this.visibleRect=t}getOverscannedRect(){let t=this.visibleRect.copy(),e=this.visibleRect.height/3;t.height+=e,this.velocity.y<0&&(t.y-=e);let i=this.visibleRect.width/3;return t.width+=i,this.velocity.x<0&&(t.x-=i),t}constructor(){this.startTime=0,this.velocity=new p(0,0),this.visibleRect=new w}}function I(t){return"x"===t?"width":"height"}function R(t,e,i,n,s){if("start"===i)return t[n]<=s;let o=I(n);return e[o]-(t[n]+t[o])<=s}function C(t,e,i,n,s,o,r,l,a,h){let d=t=>"x"===e?new w(t,a.y,a.width,a.height):new w(a.x,t,a.width,a.height);if(i){let t=function(t,e,i,n,s){let o=i(t.key);if(!o)return null;let r=o.rect[t.corner][e]-n[e]-t.offset;if(0===r)return null;let l=n[e]+r,a=I(e),h=Math.max(0,s[a]-n[a]),d=Math.max(0,Math.min(h,l));return d!==n[e]?d:null}(i,e,l,a,h);if(null!=t)return d(t)}if(n&&!s&&(!o||r>0)){let i=d(function(t,e,i,n){if("start"===t)return 0;let s=I(e);return Math.max(0,i[s]-n[s])}(t,e,h,a));return i.equals(a)?null:i}return null}class k{reset(){this.hasSnappedToEdge=!1,this.hadEstimatedVisibleItems=!1,this.wasNearAnchorEdge=!1}captureBeforeLayout(t,e,i){return t?function(t,e,i,n,s=()=>!0){let o=I(e),r=null;for(let[l,a]of n){if(!a||!s(a))continue;let n=a.rect.intersection(i)[o];if(a.rect.area>0&&n>=4){let n=a.rect.getCornerInRect(i)??"topLeft",s=a.rect[n][e]-i[e];(!r||("end"===t?s<r.offset:s>r.offset))&&(r={key:l,corner:n,offset:s})}}return r}(t.edge,t.axis,i,e,t.isAnchorable):null}resolveAfterLayout(t){let{anchorInfo:e,anchor:i,postLayoutInfos:n,previousVisibleRect:s,previousContentSize:o,contentSize:r,itemSizeChanged:l,isScrolling:a,getLayoutInfo:h}=t;if(!e)return null;let d=this.hadEstimatedVisibleItems,u=this.wasNearAnchorEdge,c=!1;for(let z of n.values())if(z.estimatedSize){c=!0;break}if(this.hadEstimatedVisibleItems=c,d||(this.wasNearAnchorEdge=R(s,o,e.edge,e.axis,e.threshold)),0===s.area)return null;let y="x"===e.axis?"width":"height",g=r[y]-o[y],f=!this.hasSnappedToEdge;if(this.hasSnappedToEdge=!0,!f&&0===g&&!l)return null;let p=f||d&&u||R(s,o,e.edge,e.axis,e.threshold);d||(this.wasNearAnchorEdge=p);let w=f||d&&u?null:i;return C(e.edge,e.axis,w,p,a,l,g,h,sLine truncated
@@ -1 +0,0 @@
import{r as e,$ as t,j as a}from"./vendor-react-64fcanlc.js";import{fH as n,bt as r,f4 as l,fa as s,fS as i,cO as o,gt as u,cV as c,cN as d,cQ as m,cZ as g,g3 as h,c_ as p,d0 as b,g0 as f,d3 as v,d4 as w,cP as y,d6 as T,d7 as x,d5 as P,de as D,d1 as j,dd as C,aE as V,db as M,gu as S,g7 as E,B as F,cz as R,df as N,u as k,gv as I,bm as H,cm as z}from"./index-DFPUSiWe.js";import{$ as K}from"./logs-lI5kXcWp.js";import{u as B,s as O,M as L,w as A,D as $}from"./wallpaper-CEC7gitM.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./nodes-HzIJdCpg.js";const X=new WeakMap;function Y(e,t){let a=X.get(e);if(!a)throw new Error("Unknown slider state");return`${a.id}-${t}`}const G=0,U=100,q=1;function W(t){const{isDisabled:a=!1,minValue:n=G,maxValue:r=U,numberFormatter:l,step:i=q,orientation:o="horizontal"}=t;let u=e.useMemo(()=>{let e=(r-n)/10;return e=h(e,0,e+i,i),Math.max(e,i)},[i,r,n]),c=e.useCallback(e=>e?.map((t,a)=>{let l=0===a?n:e[a-1],s=a===e.length-1?r:e[a+1];return h(t,l,s,i)}),[n,r,i]),d=e.useMemo(()=>c(Q(t.value)),[t.value,c]),m=e.useMemo(()=>c(Q(t.defaultValue)??[n]),[t.defaultValue,n,c]),g=Z(t.value,t.defaultValue,t.onChange),b=Z(t.value,t.defaultValue,t.onChangeEnd);const[f,v]=p(d,m,g);let[w]=e.useState(f);const[y,T]=e.useState(new Array(f.length).fill(!1)),x=e.useRef(new Array(f.length).fill(!0)),[P,D]=e.useState(void 0),j=e.useRef(f),C=e.useRef(y);function V(e){return(e-n)/(r-n)}function M(e){return 0===e?n:f[e-1]}function S(e){return e===f.length-1?r:f[e+1]}function E(e){return x.current[e]}function F(e,t){if(a||!E(e))return;const n=M(e),r=S(e);t=h(t,n,r,i);let l=_(j.current,e,t);var s;s=l,j.current=s,v(s)}let R=e.useRef(null);function N(e=f){switch("number"==typeof e&&(e=[e]),e.length){case 0:return"";case 1:return l.format(e[0]);case 2:return l.formatRange(e[0],e[1]);default:{let t=e.map(e=>l.format(e)),a=R.current;return a&&a.resolvedOptions().locale===l.resolvedOptions().locale||(a=R.current=new Intl.ListFormat(l.resolvedOptions().locale,{type:"unit"})),a.format(t)}}}function k(e){return s((t=e*(r-n)+n,Math.round((t-n)/i)*i+n),n,r);var t}return{values:f,defaultValues:void 0!==t.defaultValue?m:w,getThumbValue:e=>f[e],setThumbValue:F,setThumbPercent:function(e,t){F(e,k(t))},isThumbDragging:e=>y[e],setThumbDragging:function(e,t){if(a||!E(e))return;t&&(j.current=f);const n=C.current[e];var r;C.current=_(C.current,e,t),r=C.current,C.current=r,T(r),b&&n&&!C.current.some(Boolean)&&b(j.current)},focusedThumb:P,setFocusedThumb:D,getThumbPercent:e=>V(f[e]),getValuePercent:V,getThumbValueLabel:e=>N(f[e]),getFormattedValue:N,getThumbMinValue:M,getThumbMaxValue:S,getPercentValue:k,isThumbEditable:E,setThumbEditable:function(e,t){x.current[e]=t},incrementThumb:function(e,t=1){let a=Math.max(t,i);F(e,h(f[e]+a,n,r,i))},decrementThumb:function(e,t=1){let a=Math.max(t,i);F(e,h(f[e]-a,n,r,i))},step:i,pageSize:u,orientation:o,isDisabled:a}}function _(e,t,a){return e[t]===a?e:[...e.slice(0,t),a,...e.slice(t+1)]}function Q(e){if(null!=e)return Array.isArray(e)?e:[e]}function Z(e,t,a){return n=>{a?.("number"==typeof e||"number"==typeof t?n[0]:n)}}const J=e.createContext(null),ee=e.createContext(null),te=e.createContext(null),ae=e.createContext(null),ne=e.forwardRef(function(a,u){[a,u]=b(a,u,J);let c=e.useRef(null),d=f(a.formatOptions),m=W({...a,numberFormatter:d}),[g,h]=v(!a["aria-label"]&&!a["aria-labelledby"]),{groupProps:p,trackProps:D,labelProps:j,outputProps:C}=function(t,a,u){let{labelProps:c,fieldProps:d}=n(t),m="vertical"===t.orientation;X.set(a,{id:c.id??d.id,"aria-describedby":t["aria-describedby"],"aria-details":t["aria-details"]});let{direction:g}=r(),{addGlobalListener:h,removeGlobalListener:p}=l();const b=e.useRef(null),f="rtl"===g,v=e.useRef(null),{moveProps:w}=K({onMoveStart(){v.current=null},onMove({deltaX:e,deltaY:t}){if(!u.current)return;let{height:n,width:r}=u.current.getBoundingClientRect(),l=m?n:r;null==v.current&&null!=b.current&&(v.current=a.getThumbPercent(b.current)*l);let i=m?t:e;if((m||f)&&(i=-i),v.current+=i,null!=b.current&&u.current){const e=s(v.current/l,0,1);a.setThumbPercent(b.current,e)}},onMoveEnd(){null!=b.current&&(a.setThumbDragging(b.current,!1),b.current=null)}});let y=e.useRef(void 0),T=(e,n,r,l)=>{if(u.current&&!t.isDisabled&&a.values.every((e,t)=>!a.isThumbDragging(t))){let{height:t,width:s,top:i,left:o}=u.current.getBoundingClientRect(),c=((m?l:r)-(m?i:o))/(m?t:s);("rtl"===g||m)&&(c=1-c);let d,p=a.getPercentValue(c),f=a.values.findIndex(e=>p-e<0);if(0===f)d=f;else if(-1===f)d=a.values.length-1;else{let e=a.values[f-1],t=a.values[f];d=Math.abs(e-p)<Math.abs(t-p)?f-1:f}d>=0&&a.isThumbEditable(d)?(e.preventDefault(),b.current=d,a.setFocusedThumb(d),y.current=n,a.setThumbDragging(b.current,!0),a.setThumbValue(d,p),h(window,"mouseup",x,!1),h(window,"touchend",x,!1),h(window,"pointerup",x,!1)):b.current=null}},x=e=>{(e.pointerId??e.changedTouches?.[0].identifier)===y.current&&(null!Line truncated
@@ -1 +0,0 @@
import{j as e}from"./vendor-react-64fcanlc.js";import{u as s,ce as a,bK as o,cm as l}from"./index-DFPUSiWe.js";import{D as t,p as d}from"./logs-lI5kXcWp.js";import{x as n}from"./Nodes-BP4CnthX.js";import"./_commonjsHelpers-CXUWDbkB.js";import"./Select-BFkb6iie.js";import"./ItemText-u3CUc_6I.js";import"./AppearanceControls-Bpq6OQjk.js";import"./nodes-HzIJdCpg.js";import"./Tabs-HxpCyCLR.js";import"./useGridSelectionCheckbox-wMhI-U-R.js";import"./Table-6xnX6eUx.js";import"./Virtualizer-BH6NJSXn.js";import"./Checkbox-xU-Ds7Hn.js";import"./activity-pUN0wcVd.js";import"./TimeCell-CRwMWdtu.js";import"./CardView-CwlxcqMf.js";import"./GridList-BuXKU7Uk.js";import"./ProbeOptionsDialog-BDgL7_BI.js";import"./Tag-BZqiYh1Z.js";import"./FlagPicker-DZPvPF1E.js";import"./SearchSelect-CsXd0DEh.js";import"./SearchList-C88Y6c53.js";import"./outbounds-BMHzxQr8.js";import"./probe-Db_cbYvO.js";import"./nav-CfbmlNWr.js";function i({fields:i,duplicates:r,onChange:p,isDisabled:h}){const c=s(),m=e=>p({download:e}),j=i.mode||"auto",x=(e,s,a)=>{const o=[...i.headers];o[e]=[s,a],p({headers:o.filter(([e,s])=>e||s)})};return e.jsxs(t,{title:c("nodes.xhttp.title"),flush:!0,children:[e.jsx("span",{className:"rp-label",children:c("nodes.xhttp.help")}),e.jsx(d,{label:c("nodes.xhttp.mode"),items:[...new Set([...n,j])].map(e=>({id:e,label:e})),value:j,isDisabled:h,onChange:e=>e!==j&&p({mode:e})}),e.jsxs(a,{className:"top",children:[e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.path"),value:i.path,placeholder:"/",spellCheck:!1,onChange:e=>p({path:e})}),e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.host"),value:i.host,spellCheck:!1,onChange:e=>p({host:e})})]}),[...i.headers,["",""]].map(([s,l],t)=>e.jsxs(a,{className:"top",children:[e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.headerName",{n:t+1}),value:s,spellCheck:!1,error:r.has(t)?c("nodes.xhttp.headerDuplicate"):void 0,onChange:e=>x(t,e,l)}),e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.headerValue",{n:t+1}),value:l,spellCheck:!1,onChange:e=>x(t,s,e)})]},t)),e.jsxs(a,{className:"top",children:[e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.padding"),value:i.xPaddingBytes,placeholder:"100-1000",onChange:e=>p({xPaddingBytes:e})}),e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.postBytes"),value:i.scMaxEachPostBytes,placeholder:"1000000",onChange:e=>p({scMaxEachPostBytes:e})}),e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.postInterval"),value:i.scMinPostsIntervalMs,placeholder:"30",onChange:e=>p({scMinPostsIntervalMs:e})})]}),e.jsx(l,{isSelected:i.noGRPCHeader,isDisabled:h,onChange:e=>p({noGRPCHeader:e}),children:c("nodes.xhttp.noGrpcHeader")}),e.jsx(l,{isSelected:!!i.download,isDisabled:h,description:c("nodes.xhttp.downloadHelp"),onChange:e=>p({download:e?{address:"",port:"443",serverName:"",path:"",host:""}:null}),children:c("nodes.xhttp.download")}),i.download&&e.jsxs(e.Fragment,{children:[e.jsxs(a,{className:"top",children:[e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.downloadAddress"),value:i.download.address,spellCheck:!1,onChange:e=>m({address:e})}),e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.downloadPort"),value:i.download.port,width:96,onChange:e=>m({port:e})})]}),e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.downloadServerName"),value:i.download.serverName,spellCheck:!1,onChange:e=>m({serverName:e})}),e.jsxs(a,{className:"top",children:[e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.downloadPath"),value:i.download.path,placeholder:"/",spellCheck:!1,onChange:e=>m({path:e})}),e.jsx(o,{isDisabled:h,label:c("nodes.xhttp.downloadHost"),value:i.download.host,spellCheck:!1,onChange:e=>m({host:e})})]})]})]})}export{i as XhttpOptions};
@@ -1 +0,0 @@
function e(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}export{e as g};
@@ -1 +0,0 @@
.rp-dashboard{gap:var(--rp-space-6);flex-direction:column;display:flex}.rp-dashboard-cell{flex-direction:column;min-inline-size:0;display:flex}.rp-dashboard-cell>.rp-card,.rp-dashboard-body>.rp-card{flex:1}.rp-module-actions{gap:var(--rp-space-2);flex-wrap:wrap;align-self:flex-start;align-items:center;display:flex}.rp-module-actions>button{flex:none}.rp-module-option{gap:var(--rp-space-2) var(--rp-space-3);flex-flow:column wrap;align-items:start;display:flex}.rp-module-option>:last-child{flex:0 auto;max-inline-size:100%}.rp-module-inspector{gap:var(--rp-space-4);flex-direction:column;display:flex}.rp-dash-section{--rp-dash-gap:var(--rp-space-3);gap:var(--rp-dash-gap);grid-template-columns:minmax(0,1fr);align-items:stretch;display:grid}.rp-dash-section>*{container:rp-tile/inline-size}.rp-dash-section[data-profile=metrics],.rp-dash-section[data-profile=extensions]{grid-template-columns:repeat(2,minmax(0,1fr))}.rp-dash-section[data-profile=metrics]>:is([data-foot=end],[data-size=large]){grid-column:1/-1}@media (width<=599.98px){.rp-dash-section[data-profile=extensions]{grid-template-columns:minmax(0,1fr)}}@media (width>=600px) and (width<=1279.98px){.rp-dash-section[data-profile=extensions]>[data-size=large]{grid-column:1/-1}}@media (width>=640px){.rp-dash-section[data-profile=quick]{grid-template-columns:repeat(2,minmax(0,1fr))}.rp-dash-section[data-profile=quick]>[data-foot=lead]{grid-column:1/-1}}@media (width>=1024px){.rp-dash-section[data-profile=traffic]{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}.rp-dash-section[data-profile=details]{grid-template-columns:repeat(2,minmax(0,1fr))}.rp-dash-section[data-profile=details]>:is([data-foot=end],[data-size=large]){grid-column:1/-1}}@media (width>=1100px){.rp-dash-section[data-profile=quick]{grid-template-columns:minmax(0,1.5fr) repeat(2,minmax(0,1fr))}.rp-dash-section[data-profile=quick]>[data-foot=lead]{grid-column:auto}.rp-dash-section[data-profile=metrics]{grid-template-columns:repeat(6,minmax(0,1fr))}.rp-dash-section[data-profile=metrics]>*{grid-column:span 2}.rp-dash-section[data-profile=metrics]>:is([data-foot=tail],[data-foot=end]){grid-column:span 3}.rp-dash-section[data-profile=metrics]>[data-size=large]{grid-column:span 4}}@media (width>=1280px){.rp-dash-section[data-profile=metrics]{grid-template-columns:repeat(5,minmax(0,1fr))}.rp-dash-section[data-profile=metrics]>:is(*,[data-foot]){grid-column:span 1}.rp-dash-section[data-profile=metrics]>[data-size=large]{grid-column:span 2}.rp-dash-section:is([data-profile=traffic],[data-profile=details],[data-profile=extensions]){grid-template-columns:repeat(12,minmax(0,1fr))}.rp-dash-section:is([data-profile=traffic],[data-profile=details],[data-profile=extensions])>*,.rp-dash-section[data-profile=details]>[data-foot=end]{grid-column:span 4}.rp-dash-section[data-profile=traffic]>:nth-child(odd of .rp-dashboard-cell),.rp-dash-section:is([data-profile=details],[data-profile=extensions])>[data-size=large]{grid-column:span 8}.rp-dash-section[data-profile=extensions]>[data-size=small]{grid-column:span 2}}.rp-dash-section[data-profile]>.rp-dashboard-cell[data-size=wide]{grid-column:1/-1}.rp-dash-section{--rp-dash-snap:none}@media (width>=768px){.rp-dash-section{--rp-dash-snap:6}.rp-dash-section:is([data-grid],[data-fit]){grid-template-columns:repeat(420,minmax(0,1fr));column-gap:0;margin-inline-start:calc(0px - var(--rp-dash-gap))}.rp-dash-section:is([data-grid],[data-fit])[data-profile]>.rp-dashboard-cell{grid-column:span var(--rp-chosen,var(--rp-span));margin-inline-start:var(--rp-dash-gap)}.rp-dash-section:is([data-grid],[data-fit])[data-profile]>.rp-dashboard-cell:not([data-width],[data-size=wide]){margin-inline-start:calc(var(--rp-dash-gap) * (1 - var(--rp-shift,0)));margin-inline-end:calc(var(--rp-dash-gap) * var(--rp-trim,0))}.rp-dash-section>*{--rp-span:210}.rp-dash-section:is([data-profile=traffic],[data-profile=details])>*,.rp-dash-section[data-profile=quick]>[data-foot=lead],.rp-dash-section[data-profile=metrics]>:is([data-foot=end],[data-size=large]),.rp-dash-section[data-profile=extensions]>[data-size=large]{--rp-span:420}.rp-dash-section>:is([data-width="1/5"],[data-width="1/4"],[data-width="1/3"]){--rp-chosen:140}.rp-dash-section>[data-width="1/2"]{--rp-chosen:210}.rp-dash-section>[data-width="2/3"]{--rp-chosen:280}.rp-dash-section>[data-width=full]{--rp-chosen:420}}@media (width>=1024px){.rp-dash-section[data-profile=traffic]>*{--rp-span:140}.rp-dash-section[data-profile=traffic]>:nth-child(odd of .rp-dashboard-cell){--rp-span:280}.rp-dash-section[data-profile=details]>*{--rp-span:210}.rp-dash-section[data-profile=details]>:is([data-foot=end],[data-size=large]){--rp-span:420}}@media (width>=1024px) and (width<=1279.98px){.rp-dash-section[data-profile=traffic]>:nth-child(odd of .rp-dashboard-cell){--rp-trim:1 / 3}.rp-dash-section[data-profile=traffic]>:nth-child(2n of .rp-dashboard-cell){--rp-shift:1 / 3}}@media (width>=1100px){.rp-dash-section[data-profile=quick]>*{--rp-span:120}.rp-dash-sectiLine truncated
@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./AreaChart-BxyW1lgi.js","./vendor-react-64fcanlc.js","./_commonjsHelpers-CXUWDbkB.js","./index-DFPUSiWe.js","./index-BnzLYrTn.css","./AreaCurve-DQSHOSpG.js","./array-C0VuvQyb.js","./nodes-HzIJdCpg.js","./logs-lI5kXcWp.js","./Select-BFkb6iie.js","./ItemText-u3CUc_6I.js","./AppearanceControls-Bpq6OQjk.js","./Sparkline-DULE2gkm.js","./Donut-Cogv0HSG.js","./ModeReadOnlyHint-DDRE5yga.js","./limitHint-drqGf1xh.js","./Editors-CMGR9TtS.js","./GridList-BuXKU7Uk.js","./useGridSelectionCheckbox-wMhI-U-R.js","./Delete-DIaM96Pb.js","./Editors-rPgk_VZK.css","./index-D2MLmo5c.js","./geodata-CvcFemyc.js","./auth-rPpvn4ox.js","./auth-Br_GGGEt.js"])))=>i.map(i=>d[i]);
import{r as e,a as t,j as n}from"./vendor-react-64fcanlc.js";import{d as s,h as a,n as r,o as i,w as o,$ as l,s as c,R as d,S as u,a as m,b as p,I as h,L as f,u as g,c as b,_ as x,e as v,f as y,N as j,T as w,g as k,i as N,j as M,k as _,B as S,E as C,C as E,l as L,P as z,m as P,p as R,q as O,r as A,t as q,v as I,x as D,y as T,z as H,A as F,D as $,F as B,G as V,H as W,J as G,K as U,M as K,O as J,Q as X,U as Y,V as Z,W as Q,X as ee,Y as te,Z as ne,a0 as se,a1 as ae,a2 as re,a3 as ie,a4 as oe,a5 as le,a6 as ce,a7 as de,a8 as ue,a9 as me,aa as pe,ab as he,ac as fe,ad as ge,ae as be,af as xe,ag as ve,ah as ye,ai as je,aj as we,ak as ke,al as Ne,am as Me,an as _e,ao as Se,ap as Ce,aq as Ee,ar as Le,as as ze,at as Pe,au as Re,av as Oe,aw as Ae,ax as qe,ay as Ie,az as De,aA as Te,aB as He,aC as Fe,aD as $e,aE as Be,aF as Ve,aG as We,aH as Ge,aI as Ue,aJ as Ke,aK as Je,aL as Xe,aM as Ye,aN as Ze,aO as Qe,aP as et,aQ as tt,aR as nt,aS as st,aT as at,aU as rt,aV as it,aW as ot,aX as lt,aY as ct,aZ as dt,a_ as ut,a$ as mt,b0 as pt,b1 as ht,b2 as ft,b3 as gt,b4 as bt,b5 as xt,b6 as vt,b7 as yt,b8 as jt,b9 as wt,ba as kt,bb as Nt,bc as Mt,bd as _t,be as St,bf as Ct,bg as Et,bh as Lt,bi as zt,bj as Pt,bk as Rt,bl as Ot,bm as At,bn as qt,bo as It,bp as Dt,bq as Tt,br as Ht,bs as Ft,bt as $t,bu as Bt,bv as Vt,bw as Wt,bx as Gt,by as Ut,bz as Kt,bA as Jt}from"./index-DFPUSiWe.js";import{r as Xt,d as Yt,u as Zt,c as Qt,s as en,e as tn,h as nn,a as sn,i as an,b as rn,f as on,g as ln,j as cn,k as dn,l as un,n as mn,m as pn,o as hn,p as fn,q as gn,t as bn,v as xn,w as vn,x as yn,y as jn,z as wn,A as kn,B as Nn,C as Mn,D as _n}from"./nodes-HzIJdCpg.js";import{W as Sn,S as Cn,L as En,u as Ln,a as zn,b as Pn,c as Rn,e as On,l as An,d as qn,f as In,g as Dn,h as Tn,V as Hn,B as Fn,i as $n,C as Bn,r as Vn,K as Wn,D as Gn,M as Un,$ as Kn,j as Jn,k as Xn,m as Yn,n as Zn}from"./logs-lI5kXcWp.js";import{I as Qn,C as es}from"./ItemText-u3CUc_6I.js";import{C as ts,u as ns,D as ss,L as as,P as rs,S as is,T as os,a as ls,b as cs}from"./AppearanceControls-Bpq6OQjk.js";const ds={"missing-offline-dependency":"config.diagnostic.offlineMissing","offline-dependency-denied":"config.diagnostic.offlineDenied","invalid-offline-dependency":"config.diagnostic.offlineInvalid","unreadable-offline-dependency":"config.diagnostic.offlineUnreadable"},us=e=>Object.hasOwn(ds,e);function ms(e,t,n){return us(e.code)?{summary:t("config.diagnostic.offline",{reason:t(ds[e.code]),which:n?t("config.diagnostic.offlineGeodata",{files:n}):t("config.diagnostic.offlineUsual")})}:s(e,t)}const ps=e=>[...new Set(e.flatMap(({item:e,message:t})=>e.message&&!t.includes(e.message)?[e.message]:[]))],hs=(e,t,n)=>{const s=e.flatMap(e=>{const s=e.id===t?.id?t.content:e.content;return void 0===s?[]:n(s)});return[...new Map(s.map(({name:e,written:t})=>[t,{name:e,written:t}])).values()]},fs=(e,t)=>hs(e,t,Xt),gs=(e,t)=>hs(e,t,e=>Yt(e).map(e=>({name:Zt(e),written:e}))),bs=e=>"<redacted>"===e.path||""===e.path,xs=e=>bs(e)?"main"===e.kind?"config.dae":`${e.kind}-${e.id.slice(0,8)}.dae`:e.path.split("/").pop()||"config.dae";function vs(e,t){const n=e.find(e=>"main"===e.kind);if(!n)return null;const s=[n,...e.filter(e=>"include"===e.kind&&!bs(e))];return t&&!s.some(e=>e.id===t.id)?null:s.map(e=>({id:e.id,...bs(e)?{}:{path:e.path},content:e.id===t?.id?t.content:e.content}))}const ys=e=>e.filter(e=>"error"===e.level&&"restart-required"===e.code).map(e=>({key:/\b[a-z][a-z0-9_]*(?:\.[a-z0-9_]+)+\b/.exec(e.message)?.[0]??(e.message||null),sourceId:e.source_id,line:e.line})),js=e=>ys(e).length,ws={main:"config.kind.main",include:"config.kind.include",subscription:"nodes.kind.subscription",generated:"config.kind.generated"},ks=(e,t)=>`${e}:${t}`;function Ns(e,t){const n=e.indexOf(":"),s=e.slice(0,n);return n>0&&t.includes(s)?{kind:s,value:e.slice(n+1)}:null}const Ms=(e,t)=>t&&e?a("rules",{tab:"list",rule:e}):void 0,_s=(e,t,n=!1)=>a("nodes",{provider:r(e,t),q:e.name,node:n?e.id??null:null});function Ss(e,t){const n=e?.find(e=>e.name===t)?.id;return n?o("",{group:n}):""}function Cs(e,t,n){const s=t?.find(t=>t.name===e);return s?{label:s.name,href:a("policies",{group:s.id})}:{label:i(e,n),href:null}}const Es=["network","domain","dst_ip","dst_port","src_ip","src_port","pname"],Ls=e=>o("",{tab:"trace",...Object.fromEntries(Es.map(t=>[t,e[t]||null]))});function zs(e){const t=new URLSearchParams(e),n=e=>t.get(e)??"";return n("domain")||n("dst_ip")?{network:"udp"===n("network")?"udp":"tcp",domain:n("domain"),dst_ip:n("dst_ip"),dst_port:n("dst_port"),src_ip:n("src_ip"),src_port:n("src_port"),pname:n("pname")}:null}const Ps=a("settings",{card:"runtime"});function Rs(e,t=Qt){return e?Ns(e,t):null}function Os(e,t){const n=e.filter(e=>"main"===e.kind||"include"===e.kind);for(const s of n){const e=en(s.content).blocks.find(e=>e.name===t);if(e)return a("config",{tab:"source",source:s.id,line:String(e.line+1)})}return a("config",{tab:"source",source:n.find(e=>"main"===e.kind)?.id??null})}const As=a("activity",{card:Line truncated
@@ -1 +0,0 @@
function t(t){return function(){return t}}const i=Math.PI,s=2*i,h=1e-6,n=s-h;function e(t){this._+=t[0];for(let i=1,s=t.length;i<s;++i)this._+=arguments[i]+t[i]}class _{constructor(t){this._x0=this._y0=this._x1=this._y1=null,this._="",this._append=null==t?e:function(t){let i=Math.floor(t);if(!(i>=0))throw new Error(`invalid digits: ${t}`);if(i>15)return e;const s=10**i;return function(t){this._+=t[0];for(let i=1,h=t.length;i<h;++i)this._+=Math.round(arguments[i]*s)/s+t[i]}}(t)}moveTo(t,i){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+i}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._append`Z`)}lineTo(t,i){this._append`L${this._x1=+t},${this._y1=+i}`}quadraticCurveTo(t,i,s,h){this._append`Q${+t},${+i},${this._x1=+s},${this._y1=+h}`}bezierCurveTo(t,i,s,h,n,e){this._append`C${+t},${+i},${+s},${+h},${this._x1=+n},${this._y1=+e}`}arcTo(t,s,n,e,_){if(t=+t,s=+s,n=+n,e=+e,(_=+_)<0)throw new Error(`negative radius: ${_}`);let r=this._x1,a=this._y1,$=n-t,o=e-s,l=r-t,p=a-s,u=l*l+p*p;if(null===this._x1)this._append`M${this._x1=t},${this._y1=s}`;else if(u>h)if(Math.abs(p*$-o*l)>h&&_){let c=n-r,d=e-a,f=$*$+o*o,x=c*c+d*d,y=Math.sqrt(f),M=Math.sqrt(u),g=_*Math.tan((i-Math.acos((f+u-x)/(2*y*M)))/2),w=g/M,v=g/y;Math.abs(w-1)>h&&this._append`L${t+w*l},${s+w*p}`,this._append`A${_},${_},0,0,${+(p*c>l*d)},${this._x1=t+v*$},${this._y1=s+v*o}`}else this._append`L${this._x1=t},${this._y1=s}`;else;}arc(t,e,_,r,a,$){if(t=+t,e=+e,$=!!$,(_=+_)<0)throw new Error(`negative radius: ${_}`);let o=_*Math.cos(r),l=_*Math.sin(r),p=t+o,u=e+l,c=1^$,d=$?r-a:a-r;null===this._x1?this._append`M${p},${u}`:(Math.abs(this._x1-p)>h||Math.abs(this._y1-u)>h)&&this._append`L${p},${u}`,_&&(d<0&&(d=d%s+s),d>n?this._append`A${_},${_},0,1,${c},${t-o},${e-l}A${_},${_},0,1,${c},${this._x1=p},${this._y1=u}`:d>h&&this._append`A${_},${_},0,${+(d>=i)},${c},${this._x1=t+_*Math.cos(a)},${this._y1=e+_*Math.sin(a)}`)}rect(t,i,s,h){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+i}h${s=+s}v${+h}h${-s}Z`}toString(){return this._}}function r(t){let i=3;return t.digits=function(s){if(!arguments.length)return i;if(null==s)i=null;else{const t=Math.floor(s);if(!(t>=0))throw new RangeError(`invalid digits: ${s}`);i=t}return t},()=>new _(i)}function a(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}export{a,t as c,r as w};
@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./auth-rPpvn4ox.js","./index-DFPUSiWe.js","./vendor-react-64fcanlc.js","./_commonjsHelpers-CXUWDbkB.js","./index-BnzLYrTn.css"])))=>i.map(i=>d[i]);
import{bD as a,d_ as t,d$ as o,v as n,_ as e}from"./index-DFPUSiWe.js";const s=(a,t)=>new URL(a.replace(/\/+$/,"")+t,globalThis.location?.href),i=()=>e(()=>import("./auth-rPpvn4ox.js"),__vite__mapDeps([0,1,2,3,4]),import.meta.url),c={username:"demo",password:"demo"};async function r(e,c){if(a(e))return(await i()).mockDiscovery();const r=await t(s(e,"/api"),{headers:{Accept:"application/json"},cache:"no-store",signal:c});if(!r.ok)throw await o(r);const p=await r.json();if("dae/honk-native"===p.name)throw new n("login.engineOutdated");return p.auth}async function p(a,o){const n=await t(s(a,"/api/v1/capabilities"),{headers:{Accept:"application/json"},cache:"no-store",signal:o});return n.body?.cancel().catch(()=>{}),404!==n.status}function u(a){return"password"!==a.mode?"token":a.setup_required?"setup":"login"}async function h(n,e,c,r){if(a(n))return(await i()).mockOpenSession(e,c);const p=await t(s(n,`/api/v1/auth/${e}`),{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(c),cache:"no-store",signal:r},"setup"===e);if(!p.ok)throw await o(p,"POST");return p.json()}async function d(n,e,i){if(a(n))return;const c=await t(s(n,"/api/v1/auth/logout"),{method:"POST",headers:{Accept:"application/json",Authorization:"Bearer "+e},cache:"no-store",signal:i});if(!c.ok&&401!==c.status)throw await o(c,"POST");c.body?.cancel().catch(()=>{})}export{c as D,p as a,d as c,r as d,h as o,u as s};
@@ -1 +0,0 @@
import{D as e}from"./auth-Br_GGGEt.js";import{aM as t,bO as o}from"./index-DFPUSiWe.js";import"./vendor-react-64fcanlc.js";import"./_commonjsHelpers-CXUWDbkB.js";const r="demo-session-";function n(){return{mode:"password",setup_required:!1}}const s={auth_setup:"/api/v1/auth/setup",auth_login:"/api/v1/auth/login"};function a(n,s){if("setup"===n)throw new t(409,"setup_already_completed","Setup already completed");if(s.username!==e.username||s.password!==e.password)throw new t(401,"invalid_credentials","Invalid credentials");return{token:r+o(),expires_at:new Date(Date.now()+432e5).toISOString()}}function i(e){return!!e?.startsWith(r)}function u(e){return{...e,discovery:async t=>{const o=await e.discovery(t);return"status"in o?{...o,links:{...o.links,...s,auth_logout:"/api/v1/auth/logout"},auth:{mode:"password",setup_required:!1,anonymous_loopback:!1}}:o}}}function d(e){const o=()=>Promise.reject(new t(401,"authentication_required","Authentication required")),r=async e=>(e?.throwIfAborted(),{name:"daeuniverse/native",api_major:1,links:{...s},auth:{mode:"password",setup_required:!1}});return new Proxy(e,{get:(e,t,n)=>"discovery"===t?r:"function"==typeof Reflect.get(e,t,n)?o:void 0})}export{n as mockDiscovery,a as mockOpenSession,i as mockSessionValid,d as refuseWithoutSession,u as withPasswordAuth};
@@ -1 +0,0 @@
const t=/^(global|dns|upstream|routing|request|response|subscription|node|group|include|fallback)\b(?!\s*\()/,s=()=>({afterArrow:!1});function r(s,r){return s.sol()&&(r.afterArrow=!1),s.eatSpace()?null:s.match("#")?(s.skipToEnd(),"comment"):s.match(/^'[^']*'/)||s.match(/^"[^"]*"/)?"string":s.match("->")?(r.afterArrow=!0,"operator"):r.afterArrow&&s.match(/^[\w.-]+/)?(r.afterArrow=!1,"keyword"):(s.sol()||""===s.string.slice(0,s.pos).trim())&&s.match(t)?"keyword":s.match(/^\d+(\.\d+)*(\/\d+)?[a-z]*/)?"number":s.match(/^[\w.-]+(?=\s*\()/)?"propertyName":s.match(/^[\w.-]+(?=\s*:)/)?"variableName":s.match(/^[A-Za-z_][\w.-]*/)?null:s.match(/^[{}()&|!,:]/)?"punctuation":(s.next(),null)}class e{pos=0;string;constructor(t){this.string=t}sol(){return 0===this.pos}eatSpace(){const t=/^\s+/.exec(this.string.slice(this.pos));return!!t&&(this.pos+=t[0].length,!0)}match(t){const s="string"==typeof t?this.string.slice(this.pos).startsWith(t)&&t:t.exec(this.string.slice(this.pos));return s&&(this.pos+="string"==typeof s?s.length:s[0].length),s}skipToEnd(){this.pos=this.string.length}next(){return this.pos<this.string.length?this.string[this.pos++]:void 0}}function n(t){const s=[],n=t.split("\n"),o={afterArrow:!1};return n.forEach((t,i)=>{const a=new e(t);for(;a.pos<t.length;){const e=a.pos,n=r(a,o);s.push({text:t.slice(e,a.pos),type:n})}i<n.length-1&&s.push({text:"\n",type:null})}),s}export{r as d,s,n as t};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

@@ -1 +0,0 @@
function e(e,t){return`${e}-${(new Date).toISOString().slice(0,19).replace(/[:T]/g,"-")}.${t}`}function t(e,t,n){const r=URL.createObjectURL(new Blob([t],{type:n})),c=document.createElement("a");c.href=r,c.download=e,document.body.append(c),c.click(),c.remove(),setTimeout(()=>URL.revokeObjectURL(r),1e3)}function n(e){return e.map(e=>null==e?"":/[",\r\n]/.test(String(e))?'"'+String(e).replace(/"/g,'""')+'"':String(e)).join(",")}export{n as c,t as d,e};
@@ -1 +0,0 @@
import{ab as e,fj as n,r as t,fk as r,fm as s,at as a,fl as o,dg as i,fx as l}from"./index-DFPUSiWe.js";async function u(e,{input:n,resolve:t,recordTypes:r},s){if("query"!==t)return{traces:[await e.routingTrace({input:n,resolve:t},s)],query:null};const a=await e.dnsQuery(n.domain,r,s),o=[...new Set(["A","AAAA"].flatMap(e=>a.results.filter(n=>n.type===e).map(i).find(Boolean)??[]))],u=[];for(const i of o.length?o:[null])u.push(await e.routingTrace({input:i?{...n,dst_ip:i}:n,resolve:"none"},s));if(u.some(e=>e.instance_id!==u[0].instance_id||e.generation_id!==u[0].generation_id))throw l(409,"snapshot_unavailable","The routing generation changed during simulation; retry the query","ui.errGenerationChanged");return{traces:u,query:a}}function c({connection_id:i,network:l="all",state:u="all"}={},c=!0,f=!1){const d=e(),{data:w,error:p}=t(),y=r(w,w?.resources.flows.max_page_size);return n({key:["flows",{connection_id:i,network:l,state:u,limit:y}],every:a.lists,fetch:e=>o(n=>d.flows({network:l,state:u,connection_id:i,cursor:n,limit:y,detail:"full"},e),(e,n)=>e?(e.flows.push(...n.flows),e):{...n,flows:[...n.flows]})},{...s(w,p,c),paused:f})}function f(t){const r=e();return n({key:["flow",{id:t}],fetch:e=>t?r.flow(t,e):Promise.resolve(null),acceptEvent:e=>"flow.updated"!==e.event||e.data.resource_id===t},{enabled:null!==t})}function d(t=!0){const r=e();return n({key:["rules"],every:0,fetch:e=>r.rules(e)},{enabled:t})}function w(t=!0){const r=e();return n({key:["dnsRules"],every:0,fetch:e=>r.dnsRules(e)},{enabled:t})}export{c as a,f as b,w as c,u as r,d as u};
@@ -1 +0,0 @@
@font-face{font-family:Noto Sans TC;font-style:normal;font-weight:100 900;font-display:optional;src:url(../fonts/noto-sans-tc-cyrillic-wght-normal-DHeihy9e.woff2)format("woff2");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116;ascent-override:100%;descent-override:20%;line-gap-override:0%}@font-face{font-family:Noto Sans TC;font-style:normal;font-weight:100 900;font-display:optional;src:url(../fonts/noto-sans-tc-vietnamese-wght-normal-B77OGAeV.woff2)format("woff2");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB;ascent-override:100%;descent-override:20%;line-gap-override:0%}@font-face{font-family:Noto Sans TC;font-style:normal;font-weight:100 900;font-display:optional;src:url(../fonts/noto-sans-tc-latin-ext-wght-normal-BFabhjDY.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;ascent-override:100%;descent-override:20%;line-gap-override:0%}@font-face{font-family:Noto Sans TC;font-style:normal;font-weight:100 900;font-display:optional;src:url(../fonts/noto-sans-tc-latin-wght-normal-CjMECRfU.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;ascent-override:100%;descent-override:20%;line-gap-override:0%}
Loaded 100 of 210 files, more files were not shown because too many files have changed in this diff. Show more