diff --git a/frp/Makefile b/frp/Makefile index 882d784de..5671459e5 100644 --- a/frp/Makefile +++ b/frp/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=frp PKG_VERSION:=0.71.0 -PKG_RELEASE:=9 +PKG_RELEASE:=10 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/fatedier/frp/tar.gz/v$(PKG_VERSION)? diff --git a/frp/files/frps.config b/frp/files/frps.config index daac0015b..c3cee0490 100644 --- a/frp/files/frps.config +++ b/frp/files/frps.config @@ -19,7 +19,6 @@ config init config conf 'common' option bind_addr '0.0.0.0' option bind_port '7000' - option authentication_method 'token' # option token 'your_token' # Alternatively, load a token from a file or command. Exec token sources @@ -32,12 +31,10 @@ config conf 'common' # list token_source_exec_args '--format' # list token_source_exec_args 'raw' # list token_source_exec_env 'TOKEN_SERVICE=production' - option max_pool_count '5' option tcp_mux 'true' option tls_force 'false' option detailed_errors_to_client 'true' - # Web server is disabled when admin_port is empty or 0. # option admin_addr '127.0.0.1' # option admin_port '7500' @@ -48,15 +45,12 @@ config conf 'common' option enable_prometheus 'false' # option admin_tls_cert_file '/etc/ssl/acme/example.com.fullchain.crt' # option admin_tls_key_file '/etc/ssl/acme/example.com.key' - # Allow ports can be single ports or ranges. # list allow_ports '2000-3000' # list allow_ports '3001' - option log_file 'console' option log_level 'info' option log_max_days '3' - # List options with name "_" will be directly appended as raw TOML lines. # Use this only for options not covered by UCI options above. # Do not duplicate keys generated by UCI options above. @@ -69,7 +63,6 @@ config conf 'common' # option path '/handler' # list ops 'Login' # option tls_verify 'false' - #config http_plugin 'port_manager' # option name 'port-manager' # option addr '127.0.0.1:9001' diff --git a/kaiplus/Makefile b/kaiplus/Makefile index f9c33e82e..f341eda7c 100644 --- a/kaiplus/Makefile +++ b/kaiplus/Makefile @@ -9,8 +9,8 @@ include $(TOPDIR)/rules.mk PKG_ARCH_kaiplus:=$(ARCH) PKG_NAME:=kaiplus -PKG_VERSION:=1.0.7 -PKG_RELEASE:=11 +PKG_VERSION:=1.0.8 +PKG_RELEASE:=12 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kaiplus-runtime-v$(PKG_VERSION)/ PKG_HASH:=skip diff --git a/linkease-common-bin/Makefile b/linkease-common-bin/Makefile index 4d1714eac..4e0e642cd 100644 --- a/linkease-common-bin/Makefile +++ b/linkease-common-bin/Makefile @@ -12,7 +12,7 @@ PKG_ARCH_LINKEASE:=$(ARCH) PKG_NAME:=linkease-common-bin # use PKG_SOURCE_DATE instead of PKG_VERSION for compitable PKG_SOURCE_DATE:=1.7.5 -PKG_RELEASE:=29 +PKG_RELEASE:=30 ARCH_HEXCODE:= ifeq ($(ARCH),x86_64) @@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel) ARCH_HEXCODE=1b0c endif -PKG_SOURCE_VERSION:=af4346ed87096bc76261ca089d336e3724731806 +PKG_SOURCE_VERSION:=07a6d4293d28946ee8c39d0604985613c0c50e2d PKG_SOURCE:=linkease-common-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE).tar.gz PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkease-runtime-v$(PKG_SOURCE_DATE)/ PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-common-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE) diff --git a/linkease/Makefile b/linkease/Makefile index ca1e72571..0f41f72f1 100644 --- a/linkease/Makefile +++ b/linkease/Makefile @@ -12,7 +12,7 @@ PKG_ARCH_LINKEASE:=$(ARCH) PKG_NAME:=linkease # use PKG_SOURCE_DATE instead of PKG_VERSION for compitable PKG_SOURCE_DATE:=1.7.5 -PKG_RELEASE:=32 +PKG_RELEASE:=33 ARCH_HEXCODE:= ifeq ($(ARCH),x86_64) @@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel) ARCH_HEXCODE=1b0c endif -PKG_SOURCE_VERSION:=af4346ed87096bc76261ca089d336e3724731806 +PKG_SOURCE_VERSION:=07a6d4293d28946ee8c39d0604985613c0c50e2d PKG_SOURCE:=linkease-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE).tar.gz PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkease-runtime-v$(PKG_SOURCE_DATE)/ PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE) diff --git a/linkeasefull/Makefile b/linkeasefull/Makefile index 1208245bc..52d0d84d4 100644 --- a/linkeasefull/Makefile +++ b/linkeasefull/Makefile @@ -11,7 +11,7 @@ PKG_ARCH_LINKEASE:=$(ARCH) PKG_NAME:=linkeasefull # use PKG_SOURCE_DATE instead of PKG_VERSION for compitable PKG_SOURCE_DATE:=3.0.19 -PKG_RELEASE:=31 +PKG_RELEASE:=32 ARCH_HEXCODE:= ifeq ($(ARCH),x86_64) @@ -24,7 +24,7 @@ LINKEASE_RUNTIME_ARCH:=arm64 PKG_HASH:=skip endif -PKG_SOURCE_VERSION:=af4346ed87096bc76261ca089d336e3724731806 +PKG_SOURCE_VERSION:=07a6d4293d28946ee8c39d0604985613c0c50e2d PKG_SOURCE:=linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH).tar.gz PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkeasefull-runtime-v$(PKG_SOURCE_DATE)/ PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH) diff --git a/luci-app-frps/htdocs/luci-static/resources/view/frps.js b/luci-app-frps/htdocs/luci-static/resources/view/frps.js index 45592cc8d..b7b29750b 100644 --- a/luci-app-frps/htdocs/luci-static/resources/view/frps.js +++ b/luci-app-frps/htdocs/luci-static/resources/view/frps.js @@ -530,7 +530,7 @@ const commonTransportConf = [ const commonTlsQuicConf = [ [form.Flag, 'tls_force', _('Force TLS'), _('Only accept TLS-encrypted frpc connections.'), - { datatype: 'bool', default: 'false' }], + { datatype: 'bool', default: 'false', optional: false, rmempty: false }], [form.Value, 'tls_cert_file', _('TLS certificate path'), _('Path to the TLS certificate file.'), diff --git a/luci-app-kaiplus/Makefile b/luci-app-kaiplus/Makefile index 1b7bf010c..6c8e9e941 100644 --- a/luci-app-kaiplus/Makefile +++ b/luci-app-kaiplus/Makefile @@ -7,8 +7,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=KaiPlus -PKG_VERSION:=1.0.7 -PKG_RELEASE:=8 +PKG_VERSION:=1.0.8 +PKG_RELEASE:=9 LUCI_DEPENDS:=+kaiplus +luci-compat +luci-lib-linkeaseauth LUCI_MINIFY_CSS:=0 LUCI_MINIFY_JS:=0 diff --git a/luci-app-openclaw/CHANGELOG.md b/luci-app-openclaw/CHANGELOG.md index 8273b6105..3fea9a822 100644 --- a/luci-app-openclaw/CHANGELOG.md +++ b/luci-app-openclaw/CHANGELOG.md @@ -4,6 +4,25 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。 +## [2.2.1] - 2026-09-10 + +本次发布为紧急热修版本,修复基本设置页面渲染崩溃、一键升级无效、设备配对异常处理缺陷,并改善 Web 控制台安全上下文与图片附件支持。 + +### 修复与优化 + +- **修复基本设置页面崩溃与升级失效 (#106)**: + - 修复 `basic.lua` 中未定义 `service_ctl_url` 变量导致页面白屏崩溃的缺陷; + - 绑定正确的请求端点,确保「一键升级 OpenClaw 核心」能够正常向后台发起升级事务请求。 +- **修复设备配对错误处理与假成功语义 (#106)**: + - 控制器端严格校验 `devices list --json` 及 `devices approve` 的执行退出码与 JSON 解析结果,杜绝网关异常时伪装为 0 待配对成功; + - 前端界面区分语义:仅当实际批准设备数大于 0 时提示成功并重新载入控制台,无待配对时仅展示信息提示,杜绝死循环重新载入。 +- **改善 Web 控制台安全上下文与图片附件支持 (#105)**: + - 识别现代浏览器安全上下文限制(图片/附件与语音需 HTTPS 或 localhost),新增「访问条件提示(图片/附件与安全上下文)」面板及使用指引; + - 支持配置反代 HTTPS 控制台地址(`option console_url`),并提供「新窗口直达」入口避免 Mixed Content 限制。 +- **契约测试与健壮性加固**: + - 新增 `test_luci_basic_contract.sh`,在测试流水线中以 Lua 解释器真实执行 CBI 模型渲染,杜绝变量缺失; + - 强化设备配对测试与控制台接入测试断言。 + ## [2.2.0] - 2026-09-06 本次发布全面适配 OpenClaw 2026.9.1 最新稳定版基线,完成安全升级事务、外部 Supervisor 治理、微信插件 2.4.8 与废弃特性清理。 diff --git a/luci-app-openclaw/Makefile b/luci-app-openclaw/Makefile index ae2d3c381..4aebcf995 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:=27 +PKG_RELEASE:=28 PKG_MAINTAINER:=10000ge10000 <10000ge10000@users.noreply.github.com> PKG_LICENSE:=GPL-3.0 diff --git a/luci-app-openclaw/VERSION b/luci-app-openclaw/VERSION index ccbccc3dc..c043eea77 100644 --- a/luci-app-openclaw/VERSION +++ b/luci-app-openclaw/VERSION @@ -1 +1 @@ -2.2.0 +2.2.1 diff --git a/luci-app-openclaw/luasrc/controller/openclaw.lua b/luci-app-openclaw/luasrc/controller/openclaw.lua index ccf23e651..a05b6569d 100644 --- a/luci-app-openclaw/luasrc/controller/openclaw.lua +++ b/luci-app-openclaw/luasrc/controller/openclaw.lua @@ -2396,14 +2396,26 @@ function action_devices_list() local output, exit_code = run_openclaw_devices_cli("devices list --json") http.prepare_content("application/json") - if not output then - http.write_json({ status = "error", message = "执行失败", pending = {}, paired = {} }) + if not output or exit_code ~= 0 then + local err_msg = "查询设备列表失败" + if output and output:gsub("^%s+", ""):gsub("%s+$", "") ~= "" then + err_msg = output:sub(1, 300) + elseif type(exit_code) == "string" and exit_code ~= "" then + err_msg = exit_code + end + http.write_json({ + status = "error", + message = err_msg, + exit_code = type(exit_code) == "number" and exit_code or -1, + pending = {}, + paired = {} + }) return end local json_str = output:match("({%s*\"pending\".*})") or output:match("({.*})") local data = json_str and json.parse(json_str) or nil - if data and (data.pending or data.paired) then + if type(data) == "table" and (data.pending ~= nil or data.paired ~= nil) then http.write_json({ status = "ok", pending = data.pending or {}, @@ -2411,7 +2423,8 @@ function action_devices_list() }) else http.write_json({ - status = "ok", + status = "error", + message = "解析设备列表失败", pending = {}, paired = {}, raw = output:sub(1, 300) @@ -2432,9 +2445,38 @@ function action_devices_approve() if approve_all then local list_out, list_code = run_openclaw_devices_cli("devices list --json") - local json_str = list_out and (list_out:match("({%s*\"pending\".*})") or list_out:match("({.*})")) + if not list_out or list_code ~= 0 then + http.prepare_content("application/json") + local err_msg = "查询设备列表失败" + if list_out and list_out:gsub("^%s+", ""):gsub("%s+$", "") ~= "" then + err_msg = list_out:sub(1, 300) + elseif type(list_code) == "string" and list_code ~= "" then + err_msg = list_code + end + http.write_json({ + status = "error", + message = err_msg, + exit_code = type(list_code) == "number" and list_code or -1, + success_count = 0, + fail_count = 0 + }) + return + end + + local json_str = list_out:match("({%s*\"pending\".*})") or list_out:match("({.*})") local data = json_str and json.parse(json_str) or nil - local pending = (data and data.pending) or {} + if type(data) ~= "table" or data.pending == nil then + http.prepare_content("application/json") + http.write_json({ + status = "error", + message = "解析设备列表失败", + success_count = 0, + fail_count = 0 + }) + return + end + + local pending = data.pending or {} if #pending == 0 then http.prepare_content("application/json") @@ -2447,12 +2489,16 @@ function action_devices_approve() if rid and rid ~= "" and rid:match("^[a-zA-Z0-9%-_]+$") then local out, exit_code = run_openclaw_devices_cli("devices approve " .. shellquote(rid)) local is_ok = (exit_code == 0) and (out and not out:match("No pending device") and not out:match("Error") and not out:match("ERROR")) + local err_detail = out + if (not err_detail or err_detail == "") and type(exit_code) == "string" then + err_detail = exit_code + end if is_ok or (out and out:lower():match("approved")) then success_count = success_count + 1 table.insert(results, { requestId = rid, ok = true, message = out }) else fail_count = fail_count + 1 - table.insert(results, { requestId = rid, ok = false, message = out or "批准失败" }) + table.insert(results, { requestId = rid, ok = false, message = err_detail or "批准失败" }) end end end @@ -2464,12 +2510,16 @@ function action_devices_approve() end local out, exit_code = run_openclaw_devices_cli("devices approve " .. shellquote(request_id)) local is_ok = (exit_code == 0) and (out and not out:match("No pending device") and not out:match("Error") and not out:match("ERROR")) + local err_detail = out + if (not err_detail or err_detail == "") and type(exit_code) == "string" then + err_detail = exit_code + end if is_ok or (out and out:lower():match("approved")) then success_count = 1 table.insert(results, { requestId = request_id, ok = true, message = out }) else fail_count = 1 - table.insert(results, { requestId = request_id, ok = false, message = out or "批准失败" }) + table.insert(results, { requestId = request_id, ok = false, message = err_detail or "批准失败" }) end else http.prepare_content("application/json") diff --git a/luci-app-openclaw/luasrc/model/cbi/openclaw/basic.lua b/luci-app-openclaw/luasrc/model/cbi/openclaw/basic.lua index fa2d44da9..08101b9c3 100644 --- a/luci-app-openclaw/luasrc/model/cbi/openclaw/basic.lua +++ b/luci-app-openclaw/luasrc/model/cbi/openclaw/basic.lua @@ -22,6 +22,7 @@ act = s3:option(DummyValue, "_actions") act.rawhtml = true act.cfgvalue = function(self, section) local ctl_url = luci.dispatcher.build_url("admin", "services", "openclaw", "service_ctl") + local service_ctl_url = ctl_url local log_url = luci.dispatcher.build_url("admin", "services", "openclaw", "setup_log") local check_url = luci.dispatcher.build_url("admin", "services", "openclaw", "check_update") local uninstall_url = luci.dispatcher.build_url("admin", "services", "openclaw", "uninstall") diff --git a/luci-app-openclaw/luasrc/view/openclaw/advanced.htm b/luci-app-openclaw/luasrc/view/openclaw/advanced.htm index c43245580..0b526506a 100644 --- a/luci-app-openclaw/luasrc/view/openclaw/advanced.htm +++ b/luci-app-openclaw/luasrc/view/openclaw/advanced.htm @@ -158,7 +158,12 @@ local pty_token = uci:get("openclaw", "main", "pty_token") or "" (new XHR()).get(devicesListUrl, null, function(x) { try { var data = JSON.parse(x.responseText); - var pending = (data && data.pending) || []; + if (!data || data.status !== 'ok') { + if (advPairingBadge) advPairingBadge.textContent = '!'; + if (advPairingBanner) advPairingBanner.style.display = 'none'; + return; + } + var pending = data.pending || []; if (advPairingBadge) advPairingBadge.textContent = pending.length; if (pending.length > 0) { var html = ''; @@ -204,11 +209,19 @@ local pty_token = uci:get("openclaw", "main", "pty_token") or "" try { var res = JSON.parse(x.responseText); if (res.status === 'ok') { - advPairingMsg.style.color = '#52c41a'; - advPairingMsg.innerHTML = '✅ ' + (res.message || '批准成功!'); - setTimeout(function() { - checkAdvDevices(); - }, 1500); + if ((res.success_count || 0) > 0) { + advPairingMsg.style.color = '#52c41a'; + advPairingMsg.innerHTML = '✅ ' + (res.message || '批准成功!'); + setTimeout(function() { + checkAdvDevices(); + }, 1500); + } else { + advPairingMsg.style.color = '#faad14'; + advPairingMsg.innerHTML = 'ℹ️ ' + (res.message || '当前没有待配对的设备请求'); + setTimeout(function() { + checkAdvDevices(); + }, 1500); + } } else { advPairingMsg.style.color = '#cf222e'; advPairingMsg.innerHTML = '❌ ' + (res.message || '批准失败'); diff --git a/luci-app-openclaw/luasrc/view/openclaw/console.htm b/luci-app-openclaw/luasrc/view/openclaw/console.htm index bbcb6c345..aaa58bbe3 100644 --- a/luci-app-openclaw/luasrc/view/openclaw/console.htm +++ b/luci-app-openclaw/luasrc/view/openclaw/console.htm @@ -7,6 +7,11 @@ <% local uci = require "luci.model.uci".cursor() local port = uci:get("openclaw", "main", "port") or "18789" +local raw_console_url = tostring(uci:get("openclaw", "main", "console_url") or ""):gsub("^%s+", ""):gsub("%s+$", "") +local https_console_url = "" +if raw_console_url ~= "" and raw_console_url:match("^https://[a-zA-Z0-9%-%._~:/?#%[%]@!$&'()*+,;=]+$") then + https_console_url = raw_console_url:gsub("/+$", "") +end %>