From 82407ad3226785db54d3dc330400ceb20b17ea36 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 1 Apr 2026 20:32:41 +0800
Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=85=20Sync=202026-04-01=2020:32:41?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hickory-dns/Makefile | 6 +-
luci-app-openclaw/CHANGELOG.md | 10 +
luci-app-openclaw/Makefile | 2 +-
.../luasrc/controller/openclaw.lua | 583 ++++++++++-
.../luasrc/model/cbi/openclaw/basic.lua | 39 +-
.../luasrc/view/openclaw/wechat.htm | 931 ++++++++++++++++++
.../root/usr/share/openclaw/oc-config.sh | 52 +-
.../root/usr/share/openclaw/web-pty.js | 20 +-
luci-app-openclawmgr/Makefile | 4 +-
.../luci-static/resources/openclawmgr/app.css | 40 +
.../luci-static/resources/openclawmgr/app.js | 141 ++-
.../luasrc/controller/openclawmgr.lua | 25 +-
.../root/usr/libexec/istorec/openclawmgr.sh | 28 +-
mihomo/Makefile | 4 +-
momo/Makefile | 2 +-
momo/files/ucode/hijack.ut | 2 +-
nikki/Makefile | 6 +-
openwrt-caddy/Makefile | 8 +-
.../patches/100-add-Caddy-modules.patch | 40 +-
19 files changed, 1830 insertions(+), 113 deletions(-)
create mode 100644 luci-app-openclaw/luasrc/view/openclaw/wechat.htm
diff --git a/hickory-dns/Makefile b/hickory-dns/Makefile
index cec243f2..46800430 100644
--- a/hickory-dns/Makefile
+++ b/hickory-dns/Makefile
@@ -1,13 +1,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=hickory-dns
-PKG_VERSION:=2026.03.31-b57b7ae
-PKG_RELEASE:=7
+PKG_VERSION:=2026.04.01~b57b7ae
+PKG_RELEASE:=8
HICKORY_COMMIT:=b57b7aec2c6c18d64b90f01c97595171963e0e56
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://codeload.github.com/hickory-dns/hickory-dns/tar.gz/b57b7aec2c6c18d64b90f01c97595171963e0e56
+PKG_SOURCE_URL:=https://codeload.github.com/hickory-dns/hickory-dns/tar.gz/b57b7aec2c6c18d64b90f01c97595171963e0e56?
PKG_HASH:=skip
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-b57b7aec2c6c18d64b90f01c97595171963e0e56
diff --git a/luci-app-openclaw/CHANGELOG.md b/luci-app-openclaw/CHANGELOG.md
index 6bafc8e7..ee1651d7 100644
--- a/luci-app-openclaw/CHANGELOG.md
+++ b/luci-app-openclaw/CHANGELOG.md
@@ -8,6 +8,16 @@
### 新增功能
+#### 适配微信通道插件 (openclaw-weixin)
+
+- **多账号操作面板**: 新增完善的多账号登录登出功能机制,重构 Lua API (`login_cmd`/`logout_cmd`/`install_cmd`/`upgrade_cmd`) 使其支持精细化的多实例运行调度。
+- **登录交互优化**: 解决由于未完成登录流程导致的环境锁定问题,后台新增自愈能力 (`pkill -f 'channels login.*openclaw-weixin'`)。
+- **安全沙箱无缝衔接**: 修复了 Web 触发命令时的 `blocked plugin candidate: suspicious ownership` 安全警告,完整平滑降权至 `openclaw` 用户组执行。
+
+**💡 微信通道使用细节提醒:**
+1. **多开隔离**:所有上号的微信机器人互不干扰,尽管在 Web 控制台大盘上聊天日志看起来像在一起,但由于后端存在独立的 `BotID / AccountID / Session` 多元结构体鉴权区分,他们在底层是**绝对隔离**的记忆!
+2. **退出登录**:遇到登录卡死或想强制下线,点击界面对应微信号的“退出登录”即可强制切除对应后端通道进程。
+
#### 支持自定义安装路径
- **功能描述**: 用户现在可以将 OpenClaw 运行环境安装到自定义路径(如第二块硬盘 `/mnt/data`)
diff --git a/luci-app-openclaw/Makefile b/luci-app-openclaw/Makefile
index df74f38b..26c64721 100644
--- a/luci-app-openclaw/Makefile
+++ b/luci-app-openclaw/Makefile
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-openclaw
PKG_VERSION:=$(strip $(shell cat $(CURDIR)/VERSION 2>/dev/null || echo "1.0.0"))
-PKG_RELEASE:=5
+PKG_RELEASE:=6
PKG_MAINTAINER:=10000ge10000 <10000ge10000@users.noreply.github.com>
PKG_LICENSE:=GPL-3.0
diff --git a/luci-app-openclaw/luasrc/controller/openclaw.lua b/luci-app-openclaw/luasrc/controller/openclaw.lua
index 2b441191..139d9f34 100644
--- a/luci-app-openclaw/luasrc/controller/openclaw.lua
+++ b/luci-app-openclaw/luasrc/controller/openclaw.lua
@@ -12,6 +12,9 @@ function index()
-- 配置管理 (View — 嵌入 oc-config Web 终端)
entry({"admin", "services", "openclaw", "advanced"}, template("openclaw/advanced"), _("配置管理"), 20).leaf = true
+ -- 微信配置 (View — 微信渠道配置向导)
+ entry({"admin", "services", "openclaw", "wechat"}, template("openclaw/wechat"), _("微信配置"), 25).leaf = true
+
-- Web 控制台 (View — 嵌入 OpenClaw Web UI)
entry({"admin", "services", "openclaw", "console"}, template("openclaw/console"), _("Web 控制台"), 30).leaf = true
@@ -44,6 +47,67 @@ function index()
-- 系统配置检测 API (安装前检测)
entry({"admin", "services", "openclaw", "check_system"}, call("action_check_system"), nil).leaf = true
+
+ -- 微信状态 API (检测插件安装和登录状态)
+ entry({"admin", "services", "openclaw", "wechat_status"}, call("action_wechat_status"), nil).leaf = true
+
+ -- 微信插件安装 API (后台安装)
+ entry({"admin", "services", "openclaw", "wechat_install"}, call("action_wechat_install"), nil).leaf = true
+
+ -- 微信安装日志轮询 API
+ entry({"admin", "services", "openclaw", "wechat_install_log"}, call("action_wechat_install_log"), nil).leaf = true
+
+ -- 微信登录 API (启动登录流程)
+ entry({"admin", "services", "openclaw", "wechat_login"}, call("action_wechat_login"), nil).leaf = true
+
+ -- 微信登录状态/二维码 API
+ entry({"admin", "services", "openclaw", "wechat_login_status"}, call("action_wechat_login_status"), nil).leaf = true
+
+ -- 微信插件卸载 API
+ entry({"admin", "services", "openclaw", "wechat_uninstall"}, call("action_wechat_uninstall"), nil).leaf = true
+
+ -- 微信插件检测升级 API
+ entry({"admin", "services", "openclaw", "wechat_check_upgrade"}, call("action_wechat_check_upgrade"), nil).leaf = true
+
+ -- 微信插件升级 API
+ entry({"admin", "services", "openclaw", "wechat_upgrade_plugin"}, call("action_wechat_upgrade_plugin"), nil).leaf = true
+
+ -- 微信退出/删除账号 API
+ entry({"admin", "services", "openclaw", "wechat_logout"}, call("action_wechat_logout"), nil).leaf = true
+end-- ═══════════════════════════════════════════
+-- 获取实际安装路径 (辅助函数)
+-- ═══════════════════════════════════════════
+local function get_actual_install_path()
+ local sys = require "luci.sys"
+ local uci = require "luci.model.uci".cursor()
+
+ -- 从 UCI 读取用户配置的基础路径
+ local install_path_uci = uci:get("openclaw", "main", "install_path") or "/opt"
+ local uci_install_path = install_path_uci .. "/openclaw"
+
+ -- 方法1: 从运行中的 gateway 进程检测实际路径
+ local actual_path = sys.exec("ps w 2>/dev/null | grep -E 'openclaw.*gateway|openclaw-gateway' | grep -v grep | head -1"):gsub("%s+", "")
+ if actual_path and actual_path ~= "" then
+ local matched_path = actual_path:match("(/[^ ]+)/data/")
+ if matched_path and nixio.fs.stat(matched_path .. "/data/.openclaw/openclaw.json", "type") then
+ return matched_path
+ end
+ end
+
+ -- 方法2: 检查常见安装位置 (优先检查实际存在的配置)
+ local possible_paths = {
+ uci_install_path, -- UCI 配置的路径
+ "/mnt/data/openclaw", -- 常见的外置存储路径
+ "/opt/openclaw", -- 默认路径
+ }
+ for _, p in ipairs(possible_paths) do
+ if nixio.fs.stat(p .. "/data/.openclaw/openclaw.json", "type") then
+ return p
+ end
+ end
+
+ -- 方法3: 如果都没找到,返回 UCI 配置的路径 (即使不存在,安装时会创建)
+ return uci_install_path
end
-- ═══════════════════════════════════════════
@@ -57,9 +121,9 @@ function action_status()
local port = uci:get("openclaw", "main", "port") or "18789"
local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
local enabled = uci:get("openclaw", "main", "enabled") or "0"
- local install_path_uci = uci:get("openclaw", "main", "install_path") or "/opt"
- -- 实际安装路径会在用户路径下创建 openclaw 子目录
- local install_path = install_path_uci .. "/openclaw"
+
+ -- 使用 get_actual_install_path 获取实际安装路径
+ local install_path = get_actual_install_path()
-- 验证端口值为纯数字,防止命令注入
if not port:match("^%d+$") then port = "18789" end
@@ -150,6 +214,9 @@ function action_status()
-- 读取已配置的渠道列表
local channels = {}
+ if content:match('"openclaw%-weixin"%s*:%s*{') then
+ channels[#channels+1] = "微信"
+ end
if content:match('"qqbot"%s*:%s*{') and content:match('"appId"%s*:%s*"[^"]+"') then
channels[#channels+1] = "QQ"
end
@@ -952,3 +1019,513 @@ function action_check_system()
http.prepare_content("application/json")
http.write_json(result)
end
+
+-- ═══════════════════════════════════════════
+-- 微信状态 API: 检测插件安装和登录状态
+-- ═══════════════════════════════════════════
+function action_wechat_status()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+ local uci = require "luci.model.uci".cursor()
+
+ -- 获取安装路径 (优先从运行环境检测)
+ local install_path_uci = uci:get("openclaw", "main", "install_path") or "/opt"
+ local install_path = install_path_uci .. "/openclaw"
+
+ -- 检测实际运行的安装路径 (通过查找 gateway 进程)
+ local actual_path = sys.exec("ps w 2>/dev/null | grep -E 'openclaw.*gateway|openclaw-gateway' | grep -v grep | head -1"):gsub("%s+", "")
+ if actual_path and actual_path ~= "" then
+ -- 从进程命令行提取路径
+ local matched_path = actual_path:match("(/[^ ]+)/data/")
+ if matched_path and nixio.fs.stat(matched_path, "type") then
+ install_path = matched_path
+ end
+ end
+
+ -- 也检查常见安装位置
+ local possible_paths = {
+ install_path,
+ "/mnt/data/openclaw",
+ "/opt/openclaw",
+ }
+ for _, p in ipairs(possible_paths) do
+ if nixio.fs.stat(p .. "/data/.openclaw/openclaw.json", "type") then
+ install_path = p
+ break
+ end
+ end
+
+ local result = {
+ plugin_installed = false,
+ logged_in = false,
+ accounts = {},
+ install_path = install_path
+ }
+
+ -- 检测微信插件是否已安装
+ local wechat_ext_dir = install_path .. "/data/.openclaw/extensions/openclaw-weixin"
+ local wechat_plugin_json = wechat_ext_dir .. "/openclaw.plugin.json"
+ local wechat_package_json = wechat_ext_dir .. "/package.json"
+
+ if nixio.fs.stat(wechat_plugin_json, "type") then
+ result.plugin_installed = true
+ -- 尝试读取版本号
+ if nixio.fs.stat(wechat_package_json, "type") then
+ local pf = io.open(wechat_package_json, "r")
+ if pf then
+ local p_content = pf:read("*a")
+ pf:close()
+ local ver = p_content:match('"version"%s*:%s*"([^"]+)"')
+ if ver then
+ result.plugin_version = ver
+ end
+ end
+ end
+ end -- 从 openclaw.json 和 accounts.json 读取微信账号配置
+ local config_file = install_path .. "/data/.openclaw/openclaw.json"
+ local accounts_file = install_path .. "/data/.openclaw/openclaw-weixin/accounts.json"
+
+ -- 新版 OpenClaw WeChat 插件状态读取
+ if nixio.fs.stat(accounts_file, "type") then
+ local af = io.open(accounts_file, "r")
+ if af then
+ local content = af:read("*a")
+ af:close()
+ local count = 0
+ for acc in content:gmatch('"([^"]+)"') do
+ count = count + 1
+ table.insert(result.accounts, {name = acc})
+ end
+ if count > 0 then
+ result.logged_in = true
+ end
+ end
+ end
+
+ local cf = io.open(config_file, "r")
+ if cf then
+ local content = cf:read("*a")
+ cf:close()
+
+ if content:match('"openclaw%-weixin"%s*:%s*{') then
+ -- 兼容旧版
+ local accounts_str = content:match('"accounts"%s*:%s*%[([^%]]*)%]')
+ if accounts_str and accounts_str ~= "" then
+ local count = 0
+ for _ in accounts_str:gmatch('"wxid') do count = count + 1 end
+ for _ in accounts_str:gmatch('"nickName"') do count = count + 1 end
+ if count > 0 and not result.logged_in then
+ result.logged_in = true
+ result.accounts = {{name = "微信账号"}}
+ end
+ end
+
+ if content:match('"credential"%s*:%s*{') and not result.logged_in then
+ result.logged_in = true
+ result.accounts = {{name = "微信账号"}}
+ end
+ end
+ end http.prepare_content("application/json")
+ http.write_json(result)
+end
+
+-- ═══════════════════════════════════════════
+-- 微信插件安装 API (后台安装)
+-- ═══════════════════════════════════════════
+function action_wechat_install()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+
+ local install_path = get_actual_install_path()
+ local node_bin = install_path .. "/node/bin/node"
+ local npx_bin = install_path .. "/node/bin/npx"
+ local oc_data = install_path .. "/data"
+
+ -- 清理旧日志和状态
+ sys.exec("rm -f /tmp/openclaw-wechat-install.log /tmp/openclaw-wechat-install.pid /tmp/openclaw-wechat-install.exit")
+
+ -- 后台执行安装
+ local install_cmd = string.format(
+ "( " ..
+ "echo '开始安装微信插件...' > /tmp/openclaw-wechat-install.log; " ..
+ "echo '安装路径: %s' >> /tmp/openclaw-wechat-install.log; " ..
+ "cd %s && " ..
+ "su -s /bin/sh openclaw -c 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw " ..
+ "PATH=%s/node/bin:%s/global/bin:$PATH " ..
+ "%s -y @tencent-weixin/openclaw-weixin-cli install' >> /tmp/openclaw-wechat-install.log 2>&1; " ..
+ "RC=$?; echo $RC > /tmp/openclaw-wechat-install.exit; " ..
+ "if [ $RC -eq 0 ]; then echo '✅ 微信插件安装成功!' >> /tmp/openclaw-wechat-install.log; " ..
+ "else echo '❌ 安装失败 (exit: '$RC')' >> /tmp/openclaw-wechat-install.log; fi " ..
+ ") & echo $! > /tmp/openclaw-wechat-install.pid",
+ install_path, oc_data, oc_data, oc_data, oc_data, install_path, install_path, npx_bin
+ ) sys.exec(install_cmd)
+
+ http.prepare_content("application/json")
+ http.write_json({ status = "ok", message = "微信插件安装已在后台启动..." })
+end
+
+-- ═══════════════════════════════════════════
+-- 微信安装日志轮询 API
+-- ═══════════════════════════════════════════
+function action_wechat_install_log()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+
+ local log = ""
+ local f = io.open("/tmp/openclaw-wechat-install.log", "r")
+ if f then
+ log = f:read("*a") or ""
+ f:close()
+ end
+
+ local running = false
+ local pid_file = io.open("/tmp/openclaw-wechat-install.pid", "r")
+ if pid_file then
+ local pid = pid_file:read("*a"):gsub("%s+", "")
+ pid_file:close()
+ if pid ~= "" then
+ local check = sys.exec("kill -0 " .. pid .. " 2>/dev/null && echo yes || echo no"):gsub("%s+", "")
+ running = (check == "yes")
+ end
+ end
+
+ local exit_code = -1
+ if not running then
+ local exit_file = io.open("/tmp/openclaw-wechat-install.exit", "r")
+ if exit_file then
+ local code = exit_file:read("*a"):gsub("%s+", "")
+ exit_file:close()
+ exit_code = tonumber(code) or -1
+ end
+ end
+
+ local state = "idle"
+ if running then
+ state = "running"
+ elseif exit_code == 0 then
+ state = "success"
+ elseif exit_code > 0 then
+ state = "failed"
+ end
+
+ http.prepare_content("application/json")
+ http.write_json({
+ status = "ok",
+ log = log,
+ state = state,
+ running = running,
+ exit_code = exit_code
+ })
+end
+
+-- ═══════════════════════════════════════════
+-- 微信登录 API (启动登录流程并获取二维码)
+-- ═══════════════════════════════════════════
+function action_wechat_login()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+
+ local install_path = get_actual_install_path()
+ local node_bin = install_path .. "/node/bin/node"
+ local oc_data = install_path .. "/data"
+ local oc_entry = ""
+
+ -- 查找 openclaw 入口
+ local search_dirs = {
+ install_path .. "/global/lib/node_modules/openclaw",
+ install_path .. "/global/node_modules/openclaw",
+ install_path .. "/node/lib/node_modules/openclaw",
+ }
+ for _, d in ipairs(search_dirs) do
+ if nixio.fs.stat(d .. "/openclaw.mjs", "type") then
+ oc_entry = d .. "/openclaw.mjs"
+ break
+ elseif nixio.fs.stat(d .. "/dist/cli.js", "type") then
+ oc_entry = d .. "/dist/cli.js"
+ break
+ end
+ end
+
+ if oc_entry == "" then
+ http.prepare_content("application/json")
+ http.write_json({ status = "error", message = "OpenClaw 未安装" })
+ return
+ end
+
+ -- 清理旧状态和可能的残留进程
+ sys.exec("kill -9 $(cat /tmp/openclaw-wechat-login.pid 2>/dev/null) 2>/dev/null")
+ sys.exec("pkill -f 'channels login --channel openclaw-weixin' 2>/dev/null")
+ sys.exec("rm -f /tmp/openclaw-wechat-qrcode.txt /tmp/openclaw-wechat-login.pid /tmp/openclaw-wechat-login.exit /tmp/openclaw-wechat-restarted") -- 后台启动登录流程,将二维码输出到文件
+ local login_cmd = string.format(
+ "( " ..
+ "echo '正在启动微信登录...' > /tmp/openclaw-wechat-qrcode.txt; " ..
+ "echo '安装路径: %s' >> /tmp/openclaw-wechat-qrcode.txt; " ..
+ "cd %s && " ..
+ "su -s /bin/sh openclaw -c 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw OPENCLAW_CONFIG_PATH=%s/.openclaw/openclaw.json " ..
+ "PATH=%s/node/bin:%s/global/bin:$PATH " ..
+ "%s %s channels login --channel openclaw-weixin' >> /tmp/openclaw-wechat-qrcode.txt 2>&1; " ..
+ "echo $? > /tmp/openclaw-wechat-login.exit; " ..
+ ") >/dev/null 2>&1 & echo $! > /tmp/openclaw-wechat-login.pid",
+ install_path, oc_data, oc_data, oc_data, oc_data, oc_data, install_path, install_path, node_bin, oc_entry
+ ) sys.exec(login_cmd)
+
+ http.prepare_content("application/json")
+ http.write_json({ status = "ok", message = "微信登录流程已启动" })
+end
+
+-- ═══════════════════════════════════════════
+-- 微信登录状态/二维码 API
+-- ═══════════════════════════════════════════
+function action_wechat_login_status()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+
+ -- 读取二维码输出
+ local qrcode = ""
+ local f = io.open("/tmp/openclaw-wechat-qrcode.txt", "r")
+ if f then
+ qrcode = f:read("*a") or ""
+ f:close()
+ end
+
+ -- 检查进程状态
+ local running = false
+ local pid_file = io.open("/tmp/openclaw-wechat-login.pid", "r")
+ if pid_file then
+ local pid = pid_file:read("*a"):gsub("%s+", "")
+ pid_file:close()
+ if pid ~= "" then
+ local check = sys.exec("kill -0 " .. pid .. " 2>/dev/null && echo yes || echo no"):gsub("%s+", "")
+ running = (check == "yes")
+ end
+ end
+
+ local exit_code = -1
+ if not running then
+ local exit_file = io.open("/tmp/openclaw-wechat-login.exit", "r")
+ if exit_file then
+ local code = exit_file:read("*a"):gsub("%s+", "")
+ exit_file:close()
+ exit_code = tonumber(code) or -1
+ end
+ end
+
+ -- 提取最后生成的二维码 URL
+ local qrcode_url = ""
+ for url in qrcode:gmatch("https?://[^\n\r]+") do
+ qrcode_url = url
+ end -- 检查是否登录成功
+ local logged_in = qrcode:find("登录成功") ~= nil or qrcode:find("成功登录") ~= nil or qrcode:find("Login success") ~= nil or qrcode:find("Logged in") ~= nil
+
+ local state = "idle"
+ if logged_in then
+ state = "success"
+ elseif running and qrcode_url ~= "" then
+ state = "qrcode"
+ elseif running then
+ state = "starting"
+ elseif exit_code == 0 then
+ state = "success"
+ elseif exit_code > 0 then
+ state = "failed"
+ end
+
+ -- 如果刚登录成功,触发一次重启,确保主进程加载微信
+ if state == "success" and not nixio.fs.stat("/tmp/openclaw-wechat-restarted", "type") then
+ sys.exec("touch /tmp/openclaw-wechat-restarted")
+ sys.exec("/etc/init.d/openclaw restart &")
+ end http.prepare_content("application/json")
+ http.write_json({
+ status = "ok",
+ state = state,
+ qrcode = qrcode,
+ qrcode_url = qrcode_url,
+ running = running,
+ exit_code = exit_code,
+ logged_in = logged_in
+ })
+end
+
+-- ═══════════════════════════════════════════
+-- 微信插件卸载 API
+-- ═══════════════════════════════════════════
+function action_wechat_uninstall()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+
+ local install_path = get_actual_install_path()
+
+ -- 删除微信插件目录
+ local wechat_ext_dir = install_path .. "/data/.openclaw/extensions/openclaw-weixin"
+ if nixio.fs.stat(wechat_ext_dir, "type") then
+ sys.exec("rm -rf " .. wechat_ext_dir)
+ end
+
+ -- 从配置中删除微信相关配置
+ local config_file = install_path .. "/data/.openclaw/openclaw.json"
+ if nixio.fs.stat(config_file, "type") then
+ -- 读取配置
+ local cf = io.open(config_file, "r")
+ local content = ""
+ if cf then
+ content = cf:read("*a") or ""
+ cf:close()
+ end
+
+ -- 删除微信配置块 (简单字符串替换)
+ content = content:gsub(',?%s*"openclaw%-weixin"%s*:%s*%b{}', "")
+ content = content:gsub('"openclaw%-weixin"%s*:%s*%b{}%s*,?', "")
+
+ -- 写回配置
+ local wf = io.open(config_file, "w")
+ if wf then
+ wf:write(content)
+ wf:close()
+ end
+ end
+
+ -- 清理临时文件
+ sys.exec("rm -f /tmp/openclaw-wechat-*.log /tmp/openclaw-wechat-*.pid /tmp/openclaw-wechat-*.exit /tmp/openclaw-wechat-qrcode.txt")
+
+ http.prepare_content("application/json")
+ http.write_json({ status = "ok", message = "微信插件已卸载" })
+end
+
+-- ═══════════════════════════════════════════
+-- 微信插件检测升级 API
+-- ═══════════════════════════════════════════
+function action_wechat_check_upgrade()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+
+ local install_path = get_actual_install_path()
+ local npx_bin = install_path .. "/node/bin/npx"
+ local oc_data = install_path .. "/data"
+
+ -- 获取当前已安装版本
+ local current_version = ""
+ local wechat_ext_dir = install_path .. "/data/.openclaw/extensions/openclaw-weixin"
+ local plugin_json = wechat_ext_dir .. "/openclaw.plugin.json"
+ local pf = io.open(plugin_json, "r")
+ if pf then
+ local content = pf:read("*a") or ""
+ pf:close()
+ current_version = content:match('"version"%s*:%s*"([^"]+)"') or ""
+ end
+
+ -- 检测最新版本 (通过 npm view)
+ local latest_version = ""
+ local env_prefix = string.format(
+ "HOME=%s PATH=%s/node/bin:%s/global/bin:$PATH",
+ oc_data, install_path, install_path
+ )
+ local check_cmd = string.format(
+ "%s %s view @tencent-weixin/openclaw-weixin version 2>/dev/null",
+ env_prefix, npx_bin
+ )
+ latest_version = sys.exec(check_cmd):gsub("%s+", "")
+
+ local has_upgrade = false
+ if current_version ~= "" and latest_version ~= "" and current_version ~= latest_version then
+ has_upgrade = true
+ end
+
+ http.prepare_content("application/json")
+ http.write_json({
+ status = "ok",
+ current_version = current_version,
+ latest_version = latest_version,
+ has_upgrade = has_upgrade
+ })
+end
+
+-- ═══════════════════════════════════════════
+-- 微信插件升级 API (后台执行安装命令)
+-- ═══════════════════════════════════════════
+function action_wechat_upgrade_plugin()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+
+ local install_path = get_actual_install_path()
+ local npx_bin = install_path .. "/node/bin/npx"
+ local oc_data = install_path .. "/data"
+
+ -- 清理旧日志和状态
+ sys.exec("rm -f /tmp/openclaw-wechat-install.log /tmp/openclaw-wechat-install.pid /tmp/openclaw-wechat-install.exit")
+
+ -- 后台执行升级 (其实就是重新安装最新版)
+ local upgrade_cmd = string.format(
+ "( " ..
+ "echo '正在升级微信插件...' > /tmp/openclaw-wechat-install.log; " ..
+ "echo '安装路径: %s' >> /tmp/openclaw-wechat-install.log; " ..
+ "cd %s && " ..
+ "su -s /bin/sh openclaw -c 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw " ..
+ "PATH=%s/node/bin:%s/global/bin:$PATH " ..
+ "%s -y @tencent-weixin/openclaw-weixin-cli install' >> /tmp/openclaw-wechat-install.log 2>&1; " ..
+ "RC=$?; echo $RC > /tmp/openclaw-wechat-install.exit; " ..
+ "if [ $RC -eq 0 ]; then echo '✅ 微信插件升级成功!' >> /tmp/openclaw-wechat-install.log; " ..
+ "else echo '❌ 升级失败 (exit: '$RC')' >> /tmp/openclaw-wechat-install.log; fi " ..
+ ") & echo $! > /tmp/openclaw-wechat-install.pid",
+ install_path, oc_data, oc_data, oc_data, oc_data, install_path, install_path, npx_bin
+ ) sys.exec(upgrade_cmd)
+
+ http.prepare_content("application/json")
+ http.write_json({ status = "ok", message = "微信插件升级已在后台启动..." })
+end
+
+-- ═══════════════════════════════════════════
+-- 微信退出/删除账号 API
+-- ═══════════════════════════════════════════
+function action_wechat_logout()
+ local http = require "luci.http"
+ local sys = require "luci.sys"
+ local account_id = http.formvalue("account")
+
+ if not account_id or account_id == "" then
+ http.prepare_content("application/json")
+ http.write_json({ status = "error", message = "参数错误:未提供账号 ID" })
+ return
+ end
+
+ local install_path = get_actual_install_path()
+ local node_bin = install_path .. "/node/bin/node"
+ local oc_data = install_path .. "/data"
+ local oc_entry = ""
+
+ local search_dirs = {
+ install_path .. "/global/lib/node_modules/openclaw",
+ install_path .. "/global/node_modules/openclaw",
+ install_path .. "/node/lib/node_modules/openclaw",
+ }
+ for _, d in ipairs(search_dirs) do
+ if nixio.fs.stat(d .. "/openclaw.mjs", "type") then
+ oc_entry = d .. "/openclaw.mjs"
+ break
+ elseif nixio.fs.stat(d .. "/dist/cli.js", "type") then
+ oc_entry = d .. "/dist/cli.js"
+ break
+ end
+ end
+
+ if oc_entry == "" then
+ http.prepare_content("application/json")
+ http.write_json({ status = "error", message = "OpenClaw 未安装" })
+ return
+ end
+
+ -- 在后台执行 logout
+ local logout_cmd = string.format(
+ "cd %s && su -s /bin/sh openclaw -c 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw OPENCLAW_CONFIG_PATH=%s/.openclaw/openclaw.json " ..
+ "PATH=%s/node/bin:%s/global/bin:$PATH " ..
+ "%s %s channels logout --channel openclaw-weixin --account \"%s\"'",
+ oc_data, oc_data, oc_data, oc_data, oc_data, install_path, install_path, node_bin, oc_entry, account_id
+ )
+
+ sys.exec(logout_cmd .. " >/dev/null 2>&1")
+
+ -- 重启服务
+ sys.exec("/etc/init.d/openclaw restart &")
+
+ http.prepare_content("application/json")
+ http.write_json({ status = "ok", message = "已下线账号: " .. account_id })
+end
diff --git a/luci-app-openclaw/luasrc/model/cbi/openclaw/basic.lua b/luci-app-openclaw/luasrc/model/cbi/openclaw/basic.lua
index 240d1ed6..4b903bbb 100644
--- a/luci-app-openclaw/luasrc/model/cbi/openclaw/basic.lua
+++ b/luci-app-openclaw/luasrc/model/cbi/openclaw/basic.lua
@@ -295,6 +295,37 @@ act.cfgvalue = function(self, section)
html[#html+1] = '}catch(e){el.innerHTML="❌ 错误";}'
html[#html+1] = '});}'
+ -- 简单的 Markdown 转 HTML 函数 (用于渲染 GitHub Release Notes)
+ html[#html+1] = 'function ocMarkdownToHtml(md){'
+ html[#html+1] = 'if(!md)return "";'
+ -- 转义 HTML 特殊字符
+ html[#html+1] = 'var html=md.replace(/&/g,"&").replace(//g,">");'
+ -- 代码块 (```code```)
+ html[#html+1] = 'html=html.replace(/```(\\w*)\\n([\\s\\S]*?)```/g,function(m,lang,code){return""+code.trim()+"
";});'
+ -- 行内代码 (`code`)
+ html[#html+1] = 'html=html.replace(/`([^`]+)`/g,"$1");'
+ -- 标题 (### ## #)
+ html[#html+1] = 'html=html.replace(/^### (.+)$/gm,"$1
");'
+ html[#html+1] = 'html=html.replace(/^## (.+)$/gm,"$1
");'
+ html[#html+1] = 'html=html.replace(/^# (.+)$/gm,"$1
");'
+ -- 粗体和斜体
+ html[#html+1] = 'html=html.replace(/\\*\\*([^*]+)\\*\\*/g,"$1");'
+ html[#html+1] = 'html=html.replace(/\\*([^*]+)\\*/g,"$1");'
+ -- 链接 [text](url)
+ html[#html+1] = 'html=html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g,"$1");'
+ -- 无序列表 (- 或 *)
+ html[#html+1] = 'html=html.replace(/^[*-] (.+)$/gm,"$1");'
+ -- 有序列表 (1. 2. 等)
+ html[#html+1] = 'html=html.replace(/^\\d+\\. (.+)$/gm,"$1");'
+ -- 水平线 (--- 或 ***)
+ html[#html+1] = 'html=html.replace(/^(---|\\*\\*\\*)$/gm,"
");'
+ -- 段落 (连续的非空行合并)
+ html[#html+1] = 'html=html.replace(/\\n\\n/g,"
");'
+ -- 换行
+ html[#html+1] = 'html=html.replace(/\\n/g,"
");'
+ html[#html+1] = 'return"
"+html+"
";'
+ html[#html+1] = '}'
+
-- 检测升级 (只检查插件版本,有新版本时显示更新内容)
html[#html+1] = 'function ocCheckUpdate(){'
html[#html+1] = 'var btn=document.getElementById("btn-check-update");'
@@ -320,10 +351,10 @@ act.cfgvalue = function(self, section)
html[#html+1] = 'window._pluginLatestVer=r.plugin_latest;'
html[#html+1] = 'var notesHtml="";'
html[#html+1] = 'if(r.release_notes){'
- html[#html+1] = 'var escaped=r.release_notes.replace(/&/g,"&").replace(//g,">");'
- html[#html+1] = 'notesHtml=\'\''
- html[#html+1] = '+\'
📋 v\'+r.plugin_latest+\' 更新内容
\''
- html[#html+1] = '+\'
\'+escaped+\'
\''
+ html[#html+1] = 'var rendered=ocMarkdownToHtml(r.release_notes);'
+ html[#html+1] = 'notesHtml=\'
\''
+ html[#html+1] = '+\'
📋 v\'+r.plugin_latest+\' 更新内容
\''
+ html[#html+1] = '+rendered'
html[#html+1] = '+\'
\';'
html[#html+1] = '}'
html[#html+1] = 'act.innerHTML=notesHtml'
diff --git a/luci-app-openclaw/luasrc/view/openclaw/wechat.htm b/luci-app-openclaw/luasrc/view/openclaw/wechat.htm
new file mode 100644
index 00000000..8f455419
--- /dev/null
+++ b/luci-app-openclaw/luasrc/view/openclaw/wechat.htm
@@ -0,0 +1,931 @@
+<%#
+ luci-app-openclaw — 微信渠道配置页面
+-%>
+<%+header%>
+
+<%
+local uci = require "luci.model.uci".cursor()
+local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
+%>
+
+
+
+
+
+
+
📋 前置条件
+
+ - 微信客户端版本 >= 8.0.70 — 旧版本可能不支持插件功能
+ - 启用 ClawBot 插件 — 安装插件后→ 打开微信客户端「扫一扫」扫描屏幕中的二维码
+
+
+ ⚠️ 注意:微信登录有可能会验证账号安全环境,为了防止微信风控封号,尽量使用常用账号且确保账号状态正常。通过上方扫码即可直接连接,无需在手机端手动启用额外功能。
+
+
+
+
+
📊 插件状态
+
+ 微信插件
+ 检测中...
+
+
+ 插件版本
+ —
+
+
+
+ 登录状态
+ 检测中...
+
+
+
+
+
+
+ OpenClaw 版本
+ 检测中...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 正在安装,请稍候...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<%+footer%>
diff --git a/luci-app-openclaw/root/usr/share/openclaw/oc-config.sh b/luci-app-openclaw/root/usr/share/openclaw/oc-config.sh
index dd805fe1..edbcf925 100755
--- a/luci-app-openclaw/root/usr/share/openclaw/oc-config.sh
+++ b/luci-app-openclaw/root/usr/share/openclaw/oc-config.sh
@@ -578,6 +578,14 @@ show_current_config() {
echo -e "${GREEN}├──────────────────────────────────────────────────────────┤${NC}"
echo -e "${GREEN}│${NC} ${BOLD}渠道配置状态${NC}"
+ # 检测微信渠道
+ local wechat_ext_dir="${OC_STATE_DIR}/extensions/openclaw-weixin"
+ if [ -d "$wechat_ext_dir" ] && [ -f "${wechat_ext_dir}/openclaw.plugin.json" ]; then
+ echo -e "${GREEN}│${NC} 微信 .............. ${GREEN}✅ 已配置${NC}"
+ else
+ echo -e "${GREEN}│${NC} 微信 .............. ${YELLOW}❌ 未配置${NC}"
+ fi
+
local tg_token=$(json_get channels.telegram.botToken)
local dc_token=$(json_get channels.discord.botToken)
local fs_appid=$(json_get channels.feishu.appId)
@@ -1084,18 +1092,36 @@ configure_model() {
prompt_with_default "请输入 SiliconFlow API Key" "" api_key
if [ -n "$api_key" ]; then
echo ""
- echo -e " ${CYAN}可用模型:${NC}"
- echo -e " ${CYAN}a)${NC} deepseek-ai/DeepSeek-V3 — DeepSeek V3 (推荐)"
- echo -e " ${CYAN}b)${NC} deepseek-ai/DeepSeek-R1 — DeepSeek R1"
- echo -e " ${CYAN}c)${NC} Qwen/Qwen3-235B-A22B — 通义千问 Qwen3 235B"
- echo -e " ${CYAN}d)${NC} 手动输入模型名"
+ echo -e " ${CYAN}可用模型分类说明:${NC}"
+ echo -e " ${YELLOW}* Pro模型 (带 Pro/ 前缀): 仅支持充值余额支付,并发与速率(Rate Limits)可变。${NC}"
+ echo -e " ${YELLOW}* 非Pro模型 (无 Pro/ 前缀): 支持赠费余额(代金券)和充值余额支付,Rate Limits 固定。${NC}"
+ echo -e " ${GREEN}【建议】如果您是代金券/赠送余额用户,请务必选择【非Pro模型】。${NC}"
echo ""
- prompt_with_default "请选择模型" "a" model_choice
+ echo -e " ${CYAN}── 非Pro模型 (支持代金券/免费额度) ──${NC}"
+ echo -e " ${CYAN}1)${NC} deepseek-ai/DeepSeek-V3 — DeepSeek-V3 (推荐)"
+ echo -e " ${CYAN}2)${NC} deepseek-ai/DeepSeek-R1 — DeepSeek-R1 (推理模型)"
+ echo -e " ${CYAN}3)${NC} Qwen/Qwen2.5-72B-Instruct — 通义千问 2.5 72B"
+ echo -e " ${CYAN}4)${NC} Qwen/Qwen2.5-7B-Instruct — 通义千问 2.5 7B"
+ echo -e " ${CYAN}5)${NC} THUDM/glm-4-9b-chat — 智谱 GLM-4 9B"
+ echo -e " ${CYAN}6)${NC} 01-ai/Yi-1.5-34B-Chat-16K — 零一万物 Yi-1.5 34B"
+ echo ""
+ echo -e " ${CYAN}── Pro模型 (仅支持充值余额) ──${NC}"
+ echo -e " ${CYAN}7)${NC} Pro/deepseek-ai/DeepSeek-V3 — DeepSeek-V3 (Pro增强侧)"
+ echo -e " ${CYAN}8)${NC} Pro/zai-org/GLM-5 — 智谱 GLM-5"
+ echo ""
+ echo -e " ${CYAN}0)${NC} 手动输入其他任意模型名称"
+ echo ""
+ prompt_with_default "请选择模型 [0-8]" "1" model_choice
case "$model_choice" in
- a) model_name="deepseek-ai/DeepSeek-V3" ;;
- b) model_name="deepseek-ai/DeepSeek-R1" ;;
- c) model_name="Qwen/Qwen3-235B-A22B" ;;
- d) prompt_with_default "请输入模型名称" "deepseek-ai/DeepSeek-V3" model_name ;;
+ 1) model_name="deepseek-ai/DeepSeek-V3" ;;
+ 2) model_name="deepseek-ai/DeepSeek-R1" ;;
+ 3) model_name="Qwen/Qwen2.5-72B-Instruct" ;;
+ 4) model_name="Qwen/Qwen2.5-7B-Instruct" ;;
+ 5) model_name="THUDM/glm-4-9b-chat" ;;
+ 6) model_name="01-ai/Yi-1.5-34B-Chat-16K" ;;
+ 7) model_name="Pro/deepseek-ai/DeepSeek-V3" ;;
+ 8) model_name="Pro/zai-org/GLM-5" ;;
+ 0) prompt_with_default "请输入模型详细名称" "deepseek-ai/DeepSeek-V3" model_name ;;
*) model_name="deepseek-ai/DeepSeek-V3" ;;
esac
auth_set_apikey siliconflow "$api_key"
@@ -1910,8 +1936,10 @@ configure_channels() {
echo ""
echo -e " ${BOLD}📡 配置消息渠道${NC}"
echo ""
- echo -e " ${CYAN}1)${NC} QQ 机器人 ${GREEN}(腾讯QQ,推荐国内用户)${NC}"
- echo -e " ${CYAN}2)${NC} Telegram ${GREEN}(最常用,推荐)${NC}"
+ echo -e " ${CYAN}提示: 微信配置请使用 LuCI 界面「微信配置」菜单${NC}"
+ echo ""
+ echo -e " ${CYAN}1)${NC} QQ 机器人 ${GREEN}(腾讯QQ)${NC}"
+ echo -e " ${CYAN}2)${NC} Telegram ${GREEN}(最常用)${NC}"
echo -e " ${CYAN}3)${NC} Discord"
echo -e " ${CYAN}4)${NC} 飞书 (Feishu)"
echo -e " ${CYAN}5)${NC} Slack"
diff --git a/luci-app-openclaw/root/usr/share/openclaw/web-pty.js b/luci-app-openclaw/root/usr/share/openclaw/web-pty.js
index b1525fe4..ff360cfe 100644
--- a/luci-app-openclaw/root/usr/share/openclaw/web-pty.js
+++ b/luci-app-openclaw/root/usr/share/openclaw/web-pty.js
@@ -104,7 +104,7 @@ function encodeWSFrame(data, opcode = 0x01) {
// ── PTY 进程管理 ──
class PtySession {
- constructor(socket) {
+ constructor(socket, initCmd = '') {
this.socket = socket;
this.proc = null;
this.cols = 80;
@@ -115,6 +115,7 @@ class PtySession {
this._MAX_SPAWN_RETRIES = 5;
this._pingTimer = null;
this._pongReceived = true;
+ this.initCmd = initCmd; // 初始命令 (如 'wechat')
activeSessions++;
console.log(`[oc-config] Session created (active: ${activeSessions}/${MAX_SESSIONS})`);
this._setupWSReader();
@@ -193,12 +194,14 @@ class PtySession {
return true;
} catch { return false; }
})();
+ // 构建脚本参数
+ const scriptArgs = this.initCmd ? [SCRIPT_PATH, this.initCmd] : [SCRIPT_PATH];
if (hasScript) {
- this.proc = spawn('script', ['-qc', `stty rows ${this.rows} cols ${this.cols} 2>/dev/null; printf '\\e[?2004l'; sh "${SCRIPT_PATH}"`, '/dev/null'],
+ this.proc = spawn('script', ['-qc', `stty rows ${this.rows} cols ${this.cols} 2>/dev/null; printf '\\e[?2004l'; sh "${scriptArgs.join('" "')}"`, '/dev/null'],
{ stdio: ['pipe', 'pipe', 'pipe'], env, detached: true });
} else {
console.log('[oc-config] "script" command not found, falling back to sh (install util-linux-script for full PTY support)');
- this.proc = spawn('sh', [SCRIPT_PATH],
+ this.proc = spawn('sh', scriptArgs,
{ stdio: ['pipe', 'pipe', 'pipe'], env, detached: true });
}
@@ -284,9 +287,9 @@ function handleUpgrade(req, socket, head) {
// 认证: 验证查询参数中的 token
// 每次连接时实时读取 UCI token (安装/升级可能重新生成 token)
const currentToken = loadAuthToken() || AUTH_TOKEN;
+ const urlObj = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
if (currentToken) {
- const url = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
- const clientToken = url.searchParams.get('token') || '';
+ const clientToken = urlObj.searchParams.get('token') || '';
if (clientToken !== currentToken) {
console.log(`[oc-config] WS auth failed from ${socket.remoteAddress}`);
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
@@ -295,6 +298,9 @@ function handleUpgrade(req, socket, head) {
}
}
+ // 读取初始化命令参数 (如 cmd=wechat)
+ const initCmd = urlObj.searchParams.get('cmd') || '';
+
// 并发会话限制
if (activeSessions >= MAX_SESSIONS) {
console.log(`[oc-config] Max sessions reached (${activeSessions}/${MAX_SESSIONS}), rejecting`);
@@ -315,8 +321,8 @@ function handleUpgrade(req, socket, head) {
socket.write(handshake, () => {
if (head && head.length > 0) socket.unshift(head);
- new PtySession(socket);
- console.log('[oc-config] PTY session started');
+ new PtySession(socket, initCmd);
+ console.log(`[oc-config] PTY session started (cmd=${initCmd || 'menu'})`);
});
}
diff --git a/luci-app-openclawmgr/Makefile b/luci-app-openclawmgr/Makefile
index 1274eee0..e5ce4791 100644
--- a/luci-app-openclawmgr/Makefile
+++ b/luci-app-openclawmgr/Makefile
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
-PKG_VERSION:=0.1.8
-PKG_RELEASE:=10
+PKG_VERSION:=0.1.9
+PKG_RELEASE:=11
LUCI_TITLE:=LuCI support for OpenClaw Launcher
LUCI_PKGARCH:=all
diff --git a/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.css b/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.css
index da0ffdc5..4b3a9c57 100644
--- a/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.css
+++ b/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.css
@@ -301,6 +301,32 @@
border-color: transparent;
}
+.oclm-button-tag {
+ min-height: 30px;
+ height: 30px;
+ padding: 0 12px;
+ border-radius: 999px;
+ font-size: 12px;
+ font-weight: 700;
+ background: rgba(47, 103, 246, 0.08);
+ color: var(--oclm-primary);
+ border: 1px solid rgba(47, 103, 246, 0.18);
+ box-shadow: none;
+}
+
+.oclm-button-tag:hover {
+ color: var(--oclm-primary);
+ border-color: rgba(47, 103, 246, 0.3);
+ background: rgba(47, 103, 246, 0.12);
+ filter: none;
+}
+
+.oclm-app[data-darkmode="true"] .oclm-button-tag {
+ background: rgba(91, 134, 255, 0.12);
+ border-color: rgba(91, 134, 255, 0.24);
+ color: var(--oclm-primary);
+}
+
.oclm-form-grid {
display: grid;
gap: 14px;
@@ -391,6 +417,16 @@
line-height: 1.45;
}
+.oclm-inline-link {
+ color: var(--oclm-primary);
+ text-decoration: none;
+ font-weight: 700;
+}
+
+.oclm-inline-link:hover {
+ text-decoration: underline;
+}
+
.oclm-toggle {
position: relative;
width: 48px;
@@ -519,6 +555,10 @@
flex-wrap: wrap;
}
+.oclm-section-submit-right {
+ justify-content: flex-end;
+}
+
.oclm-applied-hint {
color: var(--oclm-subtle);
font-size: 12px;
diff --git a/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.js b/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.js
index 55801e4b..543f3776 100644
--- a/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.js
+++ b/luci-app-openclawmgr/htdocs/luci-static/resources/openclawmgr/app.js
@@ -211,25 +211,24 @@
function updateActionLabel(status) {
var updateCheck = getUpdateCheck();
- if (updateCheck.upgrading || (status && status.installing && status.task_op === "upgrade")) return "更新中";
if (updateCheck.checking) return "检测中…";
- if (updateCheck.hasUpdate) return "更新 OpenClaw";
return "检测更新";
}
function statusNoteText(status) {
- var updateCheck = getUpdateCheck();
if (status && status.installing) {
return status.task_op === "upgrade"
? "更新任务正在后台运行,可打开任务日志查看进度。"
: "安装任务正在后台运行,可打开任务日志查看进度。";
}
+ return "";
+ }
+
+ function versionNoteText() {
+ var updateCheck = getUpdateCheck();
if (updateCheck.checking) {
return "正在检测远程版本,请稍候…";
}
- if (updateCheck.upgrading) {
- return "更新任务已提交,请稍候查看版本变化。";
- }
if (updateCheck.error) {
return updateCheck.error;
}
@@ -464,6 +463,7 @@
var updateCheck = getUpdateCheck();
var installLabel = status.installing ? (status.task_op === "upgrade" ? "更新中" : "安装中") : "立即安装";
var installAcceleratedChecked = form.install_accelerated == null ? true : form.install_accelerated === true;
+ var installChannel = form.install_channel === "latest" ? "latest" : "stable";
var showInstallAction = !status.installed;
var showUpdateAction = status.installed && !status.installing;
var showServiceActions = status.installed && !status.installing;
@@ -504,7 +504,6 @@
'
' +
'
' +
'' +
- '' +
'' +
'' +
'' +
@@ -516,6 +515,7 @@
'' +
'' +
'' +
+ '' +
'' +
'' +
'' +
@@ -569,6 +569,22 @@
'
' + (state.lastAppliedAt ? '已于 ' + escapeHtml(state.lastAppliedAt) + ' 更新配置' : '') + '
' +
'
' +
+ '' +
+ '
版本管理
openclaw 官方正在快速迭代,最新版本可能不兼容旧版本配置导致无法启动。请
加入交流群了解最新稳定版本号
' +
+ '
' +
+
'' +
'
卸载清理
' +
'
以下操作会影响当前安装环境,请在确认后执行。
' +
@@ -667,7 +683,7 @@
}
if (updateBtn) {
updateBtn.textContent = updateActionLabel(status);
- updateBtn.disabled = !!(updateCheck.checking || updateCheck.upgrading);
+ updateBtn.disabled = !!updateCheck.checking;
if (showUpdateAction) updateBtn.classList.remove("oclm-hidden");
else updateBtn.classList.add("oclm-hidden");
}
@@ -779,6 +795,9 @@
var installAccelEl = getEl("oclm-install-accelerated");
if (installAccelEl) state.form.install_accelerated = !!installAccelEl.checked;
+ var installChannelEl = getEl("oclm-install-channel");
+ if (installChannelEl) state.form.install_channel = installChannelEl.value === "latest" ? "latest" : "stable";
+
var agentEl = getEl("oclm-agent");
if (agentEl) state.form.default_agent = agentEl.value;
@@ -854,6 +873,7 @@
Array.prototype.forEach.call(root.querySelectorAll("[data-install-action]"), function(el) {
el.onclick = function() {
var accelerated = !!(root.getElementById("oclm-install-accelerated") && root.getElementById("oclm-install-accelerated").checked);
+ var installChannel = (root.getElementById("oclm-install-channel") && root.getElementById("oclm-install-channel").value === "latest") ? "latest" : ((state.form && state.form.install_channel === "latest") ? "latest" : "stable");
var baseDirEl = root.getElementById("oclm-base-dir");
var baseDir = baseDirEl && baseDirEl.value ? baseDirEl.value.trim() : "";
if (!baseDir) {
@@ -876,8 +896,9 @@
return;
}
state.form.install_accelerated = accelerated;
+ state.form.install_channel = installChannel;
state.form.base_dir = baseDir;
- return postForm(config.opUrl, { op: "install" });
+ return postForm(config.opUrl, { op: "install", install_channel: installChannel });
}).then(function(rv) {
if (!rv) return;
if (!rv || !rv.ok) {
@@ -901,45 +922,69 @@
};
});
- Array.prototype.forEach.call(root.querySelectorAll("[data-update-action]"), function(el) {
+ Array.prototype.forEach.call(root.querySelectorAll("[data-version-install-action]"), function(el) {
el.onclick = function() {
- var updateCheck = getUpdateCheck();
- if (!state.status || !state.status.installed || state.status.installing) {
+ var accelerated = !!(root.getElementById("oclm-install-accelerated") && root.getElementById("oclm-install-accelerated").checked);
+ var installChannel = (root.getElementById("oclm-install-channel") && root.getElementById("oclm-install-channel").value === "latest") ? "latest" : "stable";
+ var baseDirEl = root.getElementById("oclm-base-dir");
+ var baseDir = baseDirEl && baseDirEl.value ? baseDirEl.value.trim() : "";
+ var op = (state.status && state.status.installed) ? "upgrade" : "install";
+ if (!baseDir) {
+ window.alert("请先选择数据目录并保存应用,再执行安装。");
+ if (baseDirEl) baseDirEl.focus();
return;
}
-
- if (updateCheck.hasUpdate) {
- updateCheck.upgrading = true;
- updateCheck.error = "";
- state.status.installing = true;
- state.status.task_running = true;
- state.status.task_op = "upgrade";
- render();
- postForm(config.opUrl, { op: "upgrade" }).then(function(rv) {
- if (!rv || !rv.ok) {
- updateCheck.upgrading = false;
- state.status.installing = false;
- state.status.task_running = false;
- state.status.task_op = "";
- render();
- if (rv && rv.busy && rv.running_task_id) {
- showTaskLog(rv.running_task_id);
- return;
- }
- window.alert((rv && rv.error) || "启动更新失败");
- return;
- }
- state.lastTaskRunning = true;
- showTaskLog((rv && (rv.running_task_id || rv.task_id)) || "openclawmgr");
- scheduleStatusRefresh(10, 1000);
- }).catch(function() {
- updateCheck.upgrading = false;
+ if (!state.status || state.status.installing) {
+ return;
+ }
+ state.status.installing = true;
+ state.status.task_running = true;
+ state.status.task_op = op;
+ render();
+ postJson(config.configUrl, { base_dir: baseDir, install_accelerated: accelerated }).then(function(cfgRv) {
+ if (!cfgRv || !cfgRv.ok) {
state.status.installing = false;
state.status.task_running = false;
state.status.task_op = "";
render();
- window.alert("启动更新失败");
- });
+ window.alert((cfgRv && cfgRv.error) || "保存安装选项失败");
+ return;
+ }
+ state.form.install_accelerated = accelerated;
+ state.form.install_channel = installChannel;
+ state.form.base_dir = baseDir;
+ return postForm(config.opUrl, { op: op, install_channel: installChannel });
+ }).then(function(rv) {
+ if (!rv) return;
+ if (!rv.ok) {
+ if (rv.busy && rv.running_task_id) {
+ showTaskLog(rv.running_task_id);
+ return;
+ }
+ state.status.installing = false;
+ state.status.task_running = false;
+ state.status.task_op = "";
+ render();
+ window.alert((rv && rv.error) || "启动安装失败");
+ return;
+ }
+ state.lastTaskRunning = true;
+ showTaskLog((rv && (rv.running_task_id || rv.task_id)) || "openclawmgr");
+ scheduleStatusRefresh(10, 1000);
+ }).catch(function() {
+ state.status.installing = false;
+ state.status.task_running = false;
+ state.status.task_op = "";
+ render();
+ window.alert("启动安装失败");
+ });
+ };
+ });
+
+ Array.prototype.forEach.call(root.querySelectorAll("[data-update-action]"), function(el) {
+ el.onclick = function() {
+ var updateCheck = getUpdateCheck();
+ if (!state.status || !state.status.installed || state.status.installing) {
return;
}
@@ -960,7 +1005,6 @@
}
updateCheck.checked = true;
updateCheck.hasUpdate = !!rv.has_update;
- updateCheck.upgrading = false;
updateCheck.error = "";
updateCheck.localVersion = String(rv.local_version || (state.status && state.status.openclaw_version) || "");
updateCheck.remoteVersion = String(rv.remote_version || "");
@@ -994,6 +1038,13 @@
};
}
+ var installChannel = root.getElementById("oclm-install-channel");
+ if (installChannel) {
+ installChannel.onchange = function() {
+ state.form.install_channel = installChannel.value === "latest" ? "latest" : "stable";
+ };
+ }
+
Array.prototype.forEach.call(root.querySelectorAll("[data-copy-token-url]"), function(el) {
el.onclick = function() {
var value = (state.status && (state.status.token_url || state.status.base_url)) || "";
@@ -1178,13 +1229,9 @@
updateCheck.localVersion = String(state.status.openclaw_version || "");
}
if (!state.status || !state.status.installed) {
- updateCheck.upgrading = false;
updateCheck.hasUpdate = false;
updateCheck.checked = false;
- } else if (state.status.installing && state.status.task_op === "upgrade") {
- updateCheck.upgrading = true;
} else if (!state.status.installing) {
- updateCheck.upgrading = false;
if (updateCheck.remoteVersion && updateCheck.localVersion && updateCheck.remoteVersion === updateCheck.localVersion) {
updateCheck.hasUpdate = false;
updateCheck.checked = true;
@@ -1223,7 +1270,9 @@
if (!data || !data.ok) {
return;
}
+ var currentInstallChannel = state.form && state.form.install_channel === "latest" ? "latest" : "stable";
state.form = data.config || {};
+ state.form.install_channel = currentInstallChannel;
state.form.default_model = resolveModelValue(state.form);
state.options = data.options || {};
if (!state.form.base_dir) {
diff --git a/luci-app-openclawmgr/luasrc/controller/openclawmgr.lua b/luci-app-openclawmgr/luasrc/controller/openclawmgr.lua
index 8701a934..9be8a14e 100644
--- a/luci-app-openclawmgr/luasrc/controller/openclawmgr.lua
+++ b/luci-app-openclawmgr/luasrc/controller/openclawmgr.lua
@@ -499,10 +499,12 @@ function action_status()
local node_ver = ""
local oc_ver = ""
+ local target_oc_ver = ""
if base_dir ~= "" then
node_ver = sys.exec("/usr/libexec/istorec/openclawmgr.sh node_version 2>/dev/null"):gsub("%s+$", "")
oc_ver = sys.exec("/usr/libexec/istorec/openclawmgr.sh openclaw_version 2>/dev/null"):gsub("%s+$", "")
end
+ target_oc_ver = sys.exec("/usr/libexec/istorec/openclawmgr.sh latest_openclaw_version 2>/dev/null"):gsub("%s+$", "")
local pid = running and get_running_pid() or ""
if pid == "" and installing and lock_pid ~= "" then
pid = lock_pid
@@ -535,6 +537,7 @@ function action_status()
token = token,
node_version = node_ver,
openclaw_version = oc_ver,
+ target_openclaw_version = target_oc_ver,
pid = pid,
uptime_human = uptime_human,
base_url = base_url,
@@ -569,7 +572,14 @@ end
function action_check_update()
local sys = require "luci.sys"
+ local http = require "luci.http"
+ local util = require "luci.util"
local uci = require "luci.model.uci".cursor()
+ local install_channel = http.formvalue("install_channel") or ""
+ if install_channel ~= "" and install_channel ~= "stable" and install_channel ~= "latest" then
+ write_json({ ok = false, error = "invalid install_channel" })
+ return
+ end
if not require_csrf() then
return
@@ -597,7 +607,9 @@ function action_check_update()
return
end
- local remote_ver = trim(sys.exec("/usr/libexec/istorec/openclawmgr.sh latest_openclaw_version 2>/dev/null"))
+ local remote_channel = install_channel ~= "" and install_channel or "latest"
+ local remote_cmd = "INSTALL_CHANNEL=" .. util.shellquote(remote_channel) .. " /usr/libexec/istorec/openclawmgr.sh latest_openclaw_version 2>/dev/null"
+ local remote_ver = trim(sys.exec(remote_cmd))
if remote_ver == "" then
write_json({ ok = false, error = "获取远程版本失败", installed = true, local_version = local_ver })
return
@@ -871,11 +883,16 @@ function action_op()
end
local op = http.formvalue("op") or ""
+ local install_channel = http.formvalue("install_channel") or ""
local ok_ops = { install = true, upgrade = true, start = true, stop = true, restart = true, apply_config = true, uninstall = true, uninstall_openclaw = true, purge = true, cancel_install = true }
if not ok_ops[op] then
write_json({ ok = false, error = "unknown op" })
return
end
+ if install_channel ~= "" and install_channel ~= "stable" and install_channel ~= "latest" then
+ write_json({ ok = false, error = "invalid install_channel" })
+ return
+ end
local task_id = "openclawmgr"
local script_path = "/usr/libexec/istorec/openclawmgr.sh"
@@ -916,12 +933,18 @@ function action_op()
if not fs.access("/etc/init.d/tasks") then
-- fallback (shouldn't happen on iStoreOS with luci-lib-taskd installed)
local cmd = script_path .. " " .. util.shellquote(op)
+ if install_channel ~= "" and (op == "install" or op == "upgrade") then
+ cmd = "INSTALL_CHANNEL=" .. util.shellquote(install_channel) .. " " .. cmd
+ end
sys.exec("( " .. cmd .. " ) >/dev/null 2>&1 &")
write_json({ ok = true, queued = true, task_id = task_id, warning = "taskd missing; fallback to background exec" })
return
end
local cmd = string.format("\"%s\" %s", script_path, op)
+ if install_channel ~= "" and (op == "install" or op == "upgrade") then
+ cmd = "INSTALL_CHANNEL=" .. util.shellquote(install_channel) .. " " .. cmd
+ end
local rc = sys.call("/etc/init.d/tasks task_add " .. task_id .. " " .. util.shellquote(cmd) .. " >/dev/null 2>&1")
if rc == 0 then
write_json({ ok = true, task_id = task_id })
diff --git a/luci-app-openclawmgr/root/usr/libexec/istorec/openclawmgr.sh b/luci-app-openclawmgr/root/usr/libexec/istorec/openclawmgr.sh
index dc340521..82aa16b1 100755
--- a/luci-app-openclawmgr/root/usr/libexec/istorec/openclawmgr.sh
+++ b/luci-app-openclawmgr/root/usr/libexec/istorec/openclawmgr.sh
@@ -6,6 +6,9 @@ APP="openclawmgr"
UCI_NS="openclawmgr"
LOCK_DIR="/tmp/openclawmgr-installer.lock"
DIAG_PREFIX="/tmp/openclawmgr-diag"
+OPENCLAW_STABLE_NPM_TAG="openclaw@2026.3.28"
+OPENCLAW_STABLE_VERSION="2026.3.28"
+INSTALL_CHANNEL="${INSTALL_CHANNEL:-}"
log_ts() { date "+%Y-%m-%d %H:%M:%S"; }
@@ -391,6 +394,10 @@ local_openclaw_version() {
latest_openclaw_version() {
local npm_registry=""
+ if [ "${INSTALL_CHANNEL:-stable}" != "latest" ]; then
+ printf "%s\n" "$OPENCLAW_STABLE_VERSION"
+ return 0
+ fi
[ -x "$NPM_BIN" ] || return 0
if [ "${INSTALL_ACCELERATED:-1}" = "1" ]; then
npm_registry="https://registry.npmmirror.com"
@@ -406,6 +413,14 @@ latest_openclaw_version() {
fi
}
+target_openclaw_package() {
+ if [ "${INSTALL_CHANNEL:-stable}" = "latest" ]; then
+ printf "%s\n" "openclaw@latest"
+ else
+ printf "%s\n" "$OPENCLAW_STABLE_NPM_TAG"
+ fi
+}
+
have_openclaw_runtime() {
[ -x "$NODE_BIN" ] || return 1
find_entry >/dev/null 2>&1 || return 1
@@ -969,7 +984,9 @@ install_openclaw() {
npm_registry="https://registry.npmmirror.com"
fi
- write_installer_log "Installing OpenClaw from npm"
+ local target_pkg=""
+ target_pkg="$(target_openclaw_package)"
+ write_installer_log "Installing OpenClaw from npm (${target_pkg})"
write_installer_log "Node.js: $(PATH=\"${NODE_DIR}/bin:/usr/sbin:/usr/bin:/sbin:/bin\" \"$NODE_BIN\" --version 2>/dev/null || echo unknown), npm: $(PATH=\"${NODE_DIR}/bin:/usr/sbin:/usr/bin:/sbin:/bin\" \"$NPM_BIN\" --version 2>/dev/null || echo unknown)"
write_installer_log "npm prefix: $GLOBAL_DIR, cache: ${BASE_DIR}/npm-cache, musl: $(is_musl && echo yes || echo no) ${flags:+($flags)}"
write_installer_log "npm registry: ${npm_registry:-default}"
@@ -1008,12 +1025,12 @@ install_openclaw() {
if [ -n "$npm_registry" ]; then
HOME="$DATA_DIR" npm_config_cache="${BASE_DIR}/npm-cache" npm_config_registry="$npm_registry" \
PATH="${NODE_DIR}/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
- "$NPM_BIN" install -g "openclaw@latest" --prefix="$GLOBAL_DIR" \
+ "$NPM_BIN" install -g "$target_pkg" --prefix="$GLOBAL_DIR" \
$flags --no-audit --no-fund --no-progress >"$tmp" 2>&1 &
else
HOME="$DATA_DIR" npm_config_cache="${BASE_DIR}/npm-cache" \
PATH="${NODE_DIR}/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
- "$NPM_BIN" install -g "openclaw@latest" --prefix="$GLOBAL_DIR" \
+ "$NPM_BIN" install -g "$target_pkg" --prefix="$GLOBAL_DIR" \
$flags --no-audit --no-fund --no-progress >"$tmp" 2>&1 &
fi
local npmpid="$!"
@@ -1413,6 +1430,7 @@ NODE_VERSION="24.14.0"
[ -n "$DEFAULT_AGENT" ] || DEFAULT_AGENT="anthropic"
[ -n "$DEFAULT_MODEL" ] || DEFAULT_MODEL=""
[ -n "$INSTALL_ACCELERATED" ] || INSTALL_ACCELERATED="1"
+[ -n "$INSTALL_CHANNEL" ] || INSTALL_CHANNEL="stable"
[ -n "$PROVIDER_API_KEY" ] || PROVIDER_API_KEY=""
[ -n "$PROVIDER_BASE_URL" ] || PROVIDER_BASE_URL=""
@@ -1446,6 +1464,10 @@ case "$INSTALL_ACCELERATED" in
1|true|yes|on) INSTALL_ACCELERATED="1" ;;
*) INSTALL_ACCELERATED="0" ;;
esac
+case "$INSTALL_CHANNEL" in
+ latest) INSTALL_CHANNEL="latest" ;;
+ *) INSTALL_CHANNEL="stable" ;;
+esac
require_base_dir() {
if [ -z "$BASE_DIR" ]; then
diff --git a/mihomo/Makefile b/mihomo/Makefile
index 7f9908b9..0dddf953 100644
--- a/mihomo/Makefile
+++ b/mihomo/Makefile
@@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=mihomo
-PKG_VERSION:=1.19.21
-PKG_RELEASE:=2
+PKG_VERSION:=1.19.22
+PKG_RELEASE:=3
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/metacubex/mihomo/tar.gz/v$(PKG_VERSION)?
diff --git a/momo/Makefile b/momo/Makefile
index e1b7645e..8dbcd7c1 100644
--- a/momo/Makefile
+++ b/momo/Makefile
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=momo
PKG_VERSION:=2026.03.29
-PKG_RELEASE:=3
+PKG_RELEASE:=4
PKG_LICENSE:=GPL-3.0+
PKG_MAINTAINER:=Joseph Mory
diff --git a/momo/files/ucode/hijack.ut b/momo/files/ucode/hijack.ut
index a0ce32ee..9baa1a73 100644
--- a/momo/files/ucode/hijack.ut
+++ b/momo/files/ucode/hijack.ut
@@ -161,7 +161,7 @@
}
-%}
-table inet nikki {
+table inet momo {
set dns_hijack_nfproto {
type nf_proto
flags interval
diff --git a/nikki/Makefile b/nikki/Makefile
index 35f30662..daf6d8bb 100644
--- a/nikki/Makefile
+++ b/nikki/Makefile
@@ -1,14 +1,14 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=nikki
-PKG_VERSION:=2026.03.10
-PKG_RELEASE:=8
+PKG_VERSION:=2026.04.01
+PKG_RELEASE:=9
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git
-PKG_SOURCE_VERSION:=v1.19.21
+PKG_SOURCE_VERSION:=v1.19.22
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL3.0+
diff --git a/openwrt-caddy/Makefile b/openwrt-caddy/Makefile
index 883f9827..d13cafb8 100644
--- a/openwrt-caddy/Makefile
+++ b/openwrt-caddy/Makefile
@@ -6,13 +6,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=caddy
-PKG_VERSION:=2.10.3
-PKG_RELEASE:=1
+PKG_VERSION:=2.11.3
+PKG_RELEASE:=2
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/caddyserver/caddy.git
-PKG_SOURCE_DATE:=2025-10-09
-PKG_SOURCE_VERSION:=935b09de836d5ce001632193ac21c19abf0a57ed
+PKG_SOURCE_DATE:=2026-03-31
+PKG_SOURCE_VERSION:=4f504588669e28373f455b694539475ffe4d2926
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-3.0-or-later
diff --git a/openwrt-caddy/patches/100-add-Caddy-modules.patch b/openwrt-caddy/patches/100-add-Caddy-modules.patch
index f1a9dfdc..41333519 100644
--- a/openwrt-caddy/patches/100-add-Caddy-modules.patch
+++ b/openwrt-caddy/patches/100-add-Caddy-modules.patch
@@ -1,6 +1,6 @@
diff -rNu a/cmd/caddy/main.go b/cmd/caddy/main.go
---- a/cmd/caddy/main.go 2026-01-31 03:24:59.000000000 +0800
-+++ b/cmd/caddy/main.go 2026-01-31 13:14:17.536684835 +0800
+--- a/cmd/caddy/main.go 2026-03-31 13:46:32.000000000 +0800
++++ b/cmd/caddy/main.go 2026-04-01 13:25:45.106981103 +0800
@@ -35,6 +35,8 @@
// plug in Caddy modules here
@@ -11,35 +11,35 @@ diff -rNu a/cmd/caddy/main.go b/cmd/caddy/main.go
func main() {
diff -rNu a/go.mod b/go.mod
---- a/go.mod 2026-01-31 03:24:59.000000000 +0800
-+++ b/go.mod 2026-01-31 13:18:07.050593326 +0800
+--- a/go.mod 2026-03-31 13:46:32.000000000 +0800
++++ b/go.mod 2026-04-01 13:27:36.037581120 +0800
@@ -1,12 +1,13 @@
module github.com/caddyserver/caddy/v2
--go 1.25
-+go 1.25.6
+-go 1.25.0
++go 1.26.1
require (
github.com/BurntSushi/toml v1.6.0
- github.com/DeRuina/timberjack v1.3.9
+ github.com/DeRuina/timberjack v1.4.0
github.com/KimMachineGun/automemlimit v0.7.5
github.com/Masterminds/sprig/v3 v3.3.0
+ github.com/aksdb/caddy-cgi/v2 v2.2.7
- github.com/alecthomas/chroma/v2 v2.21.1
+ github.com/alecthomas/chroma/v2 v2.23.1
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
- github.com/caddyserver/certmagic v0.25.1
+ github.com/caddyserver/certmagic v0.25.2
@@ -19,6 +20,7 @@
- github.com/klauspost/compress v1.18.2
+ github.com/klauspost/compress v1.18.5
github.com/klauspost/cpuid/v2 v2.3.0
- github.com/mholt/acmez/v3 v3.1.4
+ github.com/mholt/acmez/v3 v3.1.6
+ github.com/mholt/caddy-webdav v0.0.0-20260127042217-fa2f366b0d75
github.com/prometheus/client_golang v1.23.2
github.com/quic-go/quic-go v0.59.0
- github.com/smallstep/certificates v0.29.0
+ github.com/smallstep/certificates v0.30.2
diff -rNu a/go.sum b/go.sum
---- a/go.sum 2026-01-31 03:24:59.000000000 +0800
-+++ b/go.sum 2026-01-31 13:18:07.050593326 +0800
-@@ -36,6 +36,8 @@
+--- a/go.sum 2026-03-31 13:46:32.000000000 +0800
++++ b/go.sum 2026-04-01 13:27:36.036581120 +0800
+@@ -40,6 +40,8 @@
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@@ -48,12 +48,12 @@ diff -rNu a/go.sum b/go.sum
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-@@ -234,6 +236,8 @@
+@@ -242,6 +244,8 @@
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
- github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ=
- github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
+ github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk=
+ github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY=
+github.com/mholt/caddy-webdav v0.0.0-20260127042217-fa2f366b0d75 h1:PXPTUbQD59ErghFla/V4czJeRdwURX1VicmMtLsiw/E=
+github.com/mholt/caddy-webdav v0.0.0-20260127042217-fa2f366b0d75/go.mod h1:Btj3NBe2qQGXsg1T8QDqfovMACGLUBH1tFqFJBF52cA=
- github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
- github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
+ github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
+ github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=