From 5a64dc251dec49ca37fcdedf75240f8171750661 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 22 Aug 2026 20:30:10 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=81=20Sync=202026-08-22=2020:30:10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- baidudrive/Makefile | 19 ++++- baidudrive/files/baidudrive-plugin.json | 33 +++++++++ filebrowser/Makefile | 2 +- kaiplus/Makefile | 21 ++++-- kaiplus/files/kaiplus-plugin.json | 33 +++++++++ kaiplus/files/logo.png | Bin 0 -> 23310 bytes linkease-common-bin/Makefile | 4 +- linkease/Makefile | 4 +- linkeasefull/Makefile | 6 +- luci-app-openclaw/CHANGELOG.md | 26 +++++++ luci-app-openclaw/Makefile | 2 +- luci-app-openclaw/README.md | 8 +-- luci-app-openclaw/VERSION | 2 +- .../luasrc/controller/openclaw.lua | 16 ++++- luci-app-openclaw/root/etc/config/openclaw | 1 + luci-app-openclaw/root/etc/init.d/openclaw | 30 ++++++-- luci-app-openclaw/root/usr/bin/openclaw-env | 67 ++++++++++++++++-- .../root/usr/share/openclaw/oc-config.sh | 26 ++++++- .../tests/test_openclaw_contracts.sh | 10 ++- wwand/Makefile | 4 +- 20 files changed, 272 insertions(+), 42 deletions(-) create mode 100644 baidudrive/files/baidudrive-plugin.json create mode 100644 kaiplus/files/kaiplus-plugin.json create mode 100644 kaiplus/files/logo.png diff --git a/baidudrive/Makefile b/baidudrive/Makefile index 5d48db354..8098ae700 100644 --- a/baidudrive/Makefile +++ b/baidudrive/Makefile @@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk PKG_ARCH_BAIDUDRIVE:=$(ARCH) PKG_NAME:=baidudrive -PKG_VERSION:=linkeasefull-runtime-v3.0.11 -PKG_RELEASE:=14 +PKG_VERSION:=kaiplus-runtime-v1.0.5 +PKG_RELEASE:=15 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/baidudrive-runtime-v$(PKG_VERSION)/ PKG_HASH:=skip @@ -41,6 +41,16 @@ define Package/$(PKG_NAME)/conffiles /etc/config/baidudrive endef +define Package/$(PKG_NAME)/prerm +#!/bin/sh +if [ "$${IPKG_INSTROOT}" = "" ]; then + if [ "$$(readlink /usr/share/linkeasefull/desktop-apps.d/10-baidudrive-plugin.json 2>/dev/null)" = "/usr/share/baidudrive/baidudrive-plugin.json" ]; then + rm -f /usr/share/linkeasefull/desktop-apps.d/10-baidudrive-plugin.json + fi +fi +exit 0 +endef + define Build/Configure endef @@ -48,8 +58,11 @@ define Build/Compile endef define Package/$(PKG_NAME)/install - $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/libexec/baidudrive $(1)/opt/baidunas-sdk $(1)/opt/baidunas-glibc $(1)/etc/init.d $(1)/etc/uci-defaults $(1)/etc/config + $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/libexec/baidudrive $(1)/usr/share/baidudrive/www $(1)/usr/share/linkeasefull/desktop-apps.d $(1)/opt/baidunas-sdk $(1)/opt/baidunas-glibc $(1)/etc/init.d $(1)/etc/uci-defaults $(1)/etc/config $(INSTALL_BIN) $(PKG_BUILD_DIR)/baidudrive.$(PKG_ARCH_BAIDUDRIVE) $(1)/usr/sbin/baidudrive + $(CP) $(PKG_BUILD_DIR)/web/dist/. $(1)/usr/share/baidudrive/www/ + $(INSTALL_DATA) ./files/baidudrive-plugin.json $(1)/usr/share/baidudrive/baidudrive-plugin.json + ln -sf /usr/share/baidudrive/baidudrive-plugin.json $(1)/usr/share/linkeasefull/desktop-apps.d/10-baidudrive-plugin.json $(INSTALL_BIN) $(PKG_BUILD_DIR)/nas_sdk/$(PKG_ARCH_BAIDUDRIVE)/baiduNas $(1)/opt/baidunas-sdk/baiduNas $(INSTALL_BIN) $(PKG_BUILD_DIR)/nas_sdk/$(PKG_ARCH_BAIDUDRIVE)/P2PClient.bin $(1)/opt/baidunas-sdk/P2PClient.bin $(INSTALL_DATA) $(PKG_BUILD_DIR)/nas_sdk/$(PKG_ARCH_BAIDUDRIVE)/libkernel.so $(1)/opt/baidunas-sdk/libkernel.so diff --git a/baidudrive/files/baidudrive-plugin.json b/baidudrive/files/baidudrive-plugin.json new file mode 100644 index 000000000..7d9ff4769 --- /dev/null +++ b/baidudrive/files/baidudrive-plugin.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": 1, + "id": "baidudrive", + "name": "BaiduDrive", + "icon": "baidu-drive.png", + "staticRoot": "/usr/share/baidudrive/www", + "desktop": { + "mode": "module", + "entry": "desktop-entry.js", + "isolation": "shadow-dom" + }, + "standalone": { + "entry": "index.html", + "basePath": "/apps/baidudrive/" + }, + "backend": { + "type": "http", + "scheme": "http", + "host": "127.0.0.1", + "portFromUci": "baidudrive.@baidudrive[0].port", + "defaultPort": 10780, + "upstreamBasePath": "/apps/baidudrive/", + "apiPath": "api/", + "pathMode": "preserve" + }, + "window": { + "width": 1180, + "height": 760, + "singleton": true + }, + "capabilities": ["cloud-drive", "sync", "transfer"], + "categories": ["other"] +} diff --git a/filebrowser/Makefile b/filebrowser/Makefile index f74ad5cde..120576c96 100644 --- a/filebrowser/Makefile +++ b/filebrowser/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=filebrowser -PKG_VERSION:=1.5.2-stable +PKG_VERSION:=1.5.3-stable PKG_RELEASE=1 ifeq ($(ARCH),aarch64) diff --git a/kaiplus/Makefile b/kaiplus/Makefile index 4c45191c5..92a280948 100644 --- a/kaiplus/Makefile +++ b/kaiplus/Makefile @@ -9,10 +9,10 @@ include $(TOPDIR)/rules.mk PKG_ARCH_kaiplus:=$(ARCH) PKG_NAME:=kaiplus -PKG_VERSION:=1.0.5 -PKG_RELEASE:=4 +PKG_VERSION:=kaiplus-runtime-v1.0.5 +PKG_RELEASE:=5 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/kaiplus/ +PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kaiplus-runtime-v$(PKG_VERSION)/ PKG_HASH:=skip PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION) @@ -37,6 +37,16 @@ define Package/$(PKG_NAME)/conffiles /etc/config/kaiplus endef +define Package/$(PKG_NAME)/prerm +#!/bin/sh +if [ "$${IPKG_INSTROOT}" = "" ]; then + if [ "$$(readlink /usr/share/linkeasefull/desktop-apps.d/00-kaiplus-plugin.json 2>/dev/null)" = "/usr/share/kaiplus/kaiplus-plugin.json" ]; then + rm -f /usr/share/linkeasefull/desktop-apps.d/00-kaiplus-plugin.json + fi +fi +exit 0 +endef + define Build/Configure endef @@ -44,11 +54,14 @@ define Build/Compile endef define Package/$(PKG_NAME)/install - $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/kaiplus/www $(1)/usr/share/kaiplus/defaults $(1)/usr/share/kaiplus/helpers $(1)/etc/init.d $(1)/etc/config + $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/kaiplus/www $(1)/usr/share/kaiplus/defaults $(1)/usr/share/kaiplus/helpers $(1)/usr/share/linkeasefull/desktop-apps.d $(1)/etc/init.d $(1)/etc/config $(INSTALL_BIN) $(PKG_BUILD_DIR)/kaiplus_bin.$(PKG_ARCH_kaiplus) $(1)/usr/sbin/kaiplus_bin $(CP) $(PKG_BUILD_DIR)/www/* $(1)/usr/share/kaiplus/www/ + $(INSTALL_DATA) ./files/logo.png $(1)/usr/share/kaiplus/www/logo.png $(CP) $(PKG_BUILD_DIR)/defaults/* $(1)/usr/share/kaiplus/defaults/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/helpers/kaiplus_workspace_tool.* $(1)/usr/share/kaiplus/helpers/ + $(INSTALL_DATA) ./files/kaiplus-plugin.json $(1)/usr/share/kaiplus/kaiplus-plugin.json + ln -sf /usr/share/kaiplus/kaiplus-plugin.json $(1)/usr/share/linkeasefull/desktop-apps.d/00-kaiplus-plugin.json $(INSTALL_BIN) ./files/kaiplus.init $(1)/etc/init.d/kaiplus $(INSTALL_CONF) ./files/kaiplus.config $(1)/etc/config/kaiplus endef diff --git a/kaiplus/files/kaiplus-plugin.json b/kaiplus/files/kaiplus-plugin.json new file mode 100644 index 000000000..696da148e --- /dev/null +++ b/kaiplus/files/kaiplus-plugin.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": 1, + "id": "kaiplus", + "name": "KaiPlus", + "icon": "logo.png", + "staticRoot": "/usr/share/kaiplus/www", + "desktop": { + "mode": "module", + "entry": "desktop-entry.js", + "isolation": "shadow-dom" + }, + "standalone": { + "entry": "index.html", + "basePath": "/apps/kaiplus/" + }, + "backend": { + "type": "http", + "scheme": "http", + "host": "127.0.0.1", + "portFromUci": "kaiplus.@kaiplus[0].port", + "defaultPort": 8189, + "upstreamBasePath": "/apps/kaiplus/", + "apiPath": "api/", + "pathMode": "preserve" + }, + "window": { + "width": 1180, + "height": 760, + "singleton": true + }, + "capabilities": ["assistant", "chat"], + "categories": ["other"] +} diff --git a/kaiplus/files/logo.png b/kaiplus/files/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..00d875d3e3dd91f773c88dd98a84ad46d99bc5ec GIT binary patch literal 23310 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/dev/null || echo "1.0.0")) -PKG_RELEASE:=24 +PKG_RELEASE:=25 PKG_MAINTAINER:=10000ge10000 <10000ge10000@users.noreply.github.com> PKG_LICENSE:=GPL-3.0 diff --git a/luci-app-openclaw/README.md b/luci-app-openclaw/README.md index 47bab26a4..030e0823a 100644 --- a/luci-app-openclaw/README.md +++ b/luci-app-openclaw/README.md @@ -27,8 +27,8 @@ | 组件 | 默认版本 | 说明 | |------|----------|------| -| OpenClaw | `2026.6.11` | npm `latest` 稳定标签 | -| Node.js | `22.23.0` | Alpine musl LTS 包;OpenClaw 2026.6.x 要求 `>=22.19.0`,安装后会按 `engines.node` 做强校验 | +| OpenClaw | `2026.7.1-2` | npm `latest` 稳定标签 | +| Node.js | `22.23.0` | Alpine musl LTS 包;OpenClaw 2026.7.x 要求 `>=22.22.3`,安装后会按 `engines.node` 做强校验 | | 微信插件 | `@tencent-weixin/openclaw-weixin@2.4.6` | CLI 使用 `@tencent-weixin/openclaw-weixin-cli@2.1.4` | ## 📦 安装 @@ -143,7 +143,7 @@ opkg install python3-light ## 已知说明 - OpenClaw 的 diagnostic heartbeat 可能在日志中出现类似周期性探测记录。它不是一次真实用户对话请求;如需降低噪音,优先在 OpenClaw 配置或日志采集侧降低诊断日志级别,不建议直接修改模型调用逻辑。 -- 当前仓库提供源码、OpenWrt feeds 集成方式、本地 `.run` / `.ipk` 构建脚本入口;本次维护不自动生成 Release 产物。 +- 当前仓库提供源码、OpenWrt feeds 集成方式、本地 `.run` / `.ipk` 构建脚本入口;推送 `v*` 标签后 CI 会自动构建并发布 Release 产物。 ## 📂 目录结构 @@ -171,7 +171,7 @@ luci-app-openclaw/ ├── scripts/ │ ├── build_ipk.sh # 本地 IPK 构建 │ ├── build_run.sh # .run 安装包构建 -│ ├── download_deps.sh # 下载离线依赖 (Node.js + OpenClaw) +│ ├── gen-release-body.sh # Release 说明生成 │ └── build-node-musl.sh # 编译 Node.js musl 静态链接版本 └── .github/workflows/ ├── build.yml # 在线构建 + 发布 diff --git a/luci-app-openclaw/VERSION b/luci-app-openclaw/VERSION index 6cbacdc8d..280a1e336 100644 --- a/luci-app-openclaw/VERSION +++ b/luci-app-openclaw/VERSION @@ -1 +1 @@ -2.0.11 +2.0.12 diff --git a/luci-app-openclaw/luasrc/controller/openclaw.lua b/luci-app-openclaw/luasrc/controller/openclaw.lua index 56896033f..d45775503 100644 --- a/luci-app-openclaw/luasrc/controller/openclaw.lua +++ b/luci-app-openclaw/luasrc/controller/openclaw.lua @@ -49,7 +49,9 @@ local function is_safe_openclaw_root(value) if paths_ok and oc_paths.is_safe_openclaw_root then return oc_paths.is_safe_openclaw_root(value) end - return value == "/opt/openclaw" or value:match("^/mnt/[^/]+/openclaw$") ~= nil or value:match("^/media/[^/]+/openclaw$") ~= nil + -- 与 luasrc/openclaw/paths.lua 的白名单保持一致(此处仅作模块加载失败时的兜底) + if value == "/openclaw" or value == "/opt/openclaw" or value == "/overlay/upper/opt/openclaw" then return true end + return value:match("^/mnt/[^/]+/openclaw$") ~= nil or value:match("^/media/[^/]+/openclaw$") ~= nil or value:match("^/srv/[^/]+/openclaw$") ~= nil end local function compare_versions(a, b) @@ -120,6 +122,7 @@ local function wechat_enable_plugin_config_cmd(install_path, node_bin, log_file, local config_file = oc_data .. "/.openclaw/openclaw.json" local register_js = [[ const fs = require('fs'); +const path = require('path'); const configPath = process.env.OC_CONFIG; let d = {}; try { @@ -385,12 +388,19 @@ local function get_install_path() end -- 确保网关端口可用:检测占用并尝试优雅停止或强制杀死占用进程 +-- 优雅停止必须走安装目录里的 CLI wrapper 全路径:uhttpd 进程不加载 +-- /etc/profile.d,裸 `openclaw` 在 LuCI 环境下必然 PATH 不可达。 +local function gateway_graceful_stop_cmd() + local oc_cli = get_install_path() .. "/global/bin/openclaw" + return "if [ -x " .. shellquote(oc_cli) .. " ]; then " .. shellquote(oc_cli) .. " gateway stop >/dev/null 2>&1 || true; fi" +end + local function ensure_port_free(port) local sys = require "luci.sys" if not port or port == "" then return end if not tostring(port):match("^%d+$") then return end -- 优先尝试使用 openclaw 自身的 stop 命令(如果已安装) - sys.exec("openclaw gateway stop >/dev/null 2>&1 || true") + sys.exec(gateway_graceful_stop_cmd()) -- 查询占用端口的行 local check_cmd = "" @@ -407,7 +417,7 @@ local function ensure_port_free(port) pid = pid and pid:gsub("%s+", "") or nil if pid and pid ~= "" then -- 再次尝试优雅停止 - sys.exec("openclaw gateway stop >/dev/null 2>&1 || true") + sys.exec(gateway_graceful_stop_cmd()) -- 发送 SIGTERM sys.exec("kill -TERM " .. pid .. " >/dev/null 2>&1 || true") -- 等待释放,最多等待 5 次(每次 1s) diff --git a/luci-app-openclaw/root/etc/config/openclaw b/luci-app-openclaw/root/etc/config/openclaw index f5eef5aab..979117481 100644 --- a/luci-app-openclaw/root/etc/config/openclaw +++ b/luci-app-openclaw/root/etc/config/openclaw @@ -4,4 +4,5 @@ config openclaw 'main' option bind 'lan' option token '' option pty_port '18793' + option pty_token '' option install_path '/opt' diff --git a/luci-app-openclaw/root/etc/init.d/openclaw b/luci-app-openclaw/root/etc/init.d/openclaw index f9d14b3fe..5d92ef608 100755 --- a/luci-app-openclaw/root/etc/init.d/openclaw +++ b/luci-app-openclaw/root/etc/init.d/openclaw @@ -607,13 +607,35 @@ if(changed){ # v2026.4.10: 优化配置迁移 - 只在版本变更时运行 doctor --fix # 原因: doctor --fix 耗时 ~50 秒,每次重启都运行严重影响启动速度 # 策略: 使用标记文件记录已运行的版本,只在版本变化时才执行迁移 + # v2026.7.1: doctor --fix 改为以 openclaw 服务用户执行。 + # OpenClaw 2026.7.x 的插件安全检查按当前进程 uid 校验插件目录属主 + # (属主必须等于进程 uid 或为 root)。root 身份运行 doctor 会把 + # openclaw 属主的微信插件目录判为 suspicious ownership,进而自动 + # 清理 plugins.allow / channels 中的微信配置。 + _run_doctor_fix() { + local oc_entry="$1" + local _doc_cmd="HOME=\"${OC_DATA}\" OPENCLAW_HOME=\"${OC_DATA}\" OPENCLAW_STATE_DIR=\"${OC_DATA}/.openclaw\" OPENCLAW_CONFIG_PATH=\"${CONFIG_FILE}\" NODE_ICU_DATA=\"${NODE_BASE}/share/icu\" \"${NODE_BIN}\" \"${oc_entry}\" doctor --fix" + if id openclaw >/dev/null 2>&1; then + if command -v su >/dev/null 2>&1; then + su -s /bin/sh openclaw -c "${_doc_cmd}" >/dev/null 2>&1 + elif command -v runuser >/dev/null 2>&1; then + runuser -u openclaw -- sh -c "${_doc_cmd}" >/dev/null 2>&1 + else + start-stop-daemon -S -m -p /tmp/openclaw-doctor.pid -c openclaw:openclaw -x /bin/sh -- -c "${_doc_cmd}" >/dev/null 2>&1 + fi + else + # openclaw 用户不存在时退回 root 执行,保证迁移仍可运行 + eval "${_doc_cmd}" >/dev/null 2>&1 + fi + } + _run_config_migration() { local oc_entry="$1" [ -z "$oc_entry" ] && return - # 获取当前 OpenClaw 版本号 (只取数字版本,如 2026.4.9) + # 获取当前 OpenClaw 版本号 (含补丁后缀,如 2026.7.1-2) local current_ver="" - current_ver=$("$NODE_BIN" "$oc_entry" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + current_ver=$("$NODE_BIN" "$oc_entry" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?' | head -1) [ -z "$current_ver" ] && current_ver="unknown" # 检查标记文件 @@ -626,9 +648,7 @@ if(changed){ # 只在版本变化时才运行 doctor --fix if [ "$current_ver" != "$last_ver" ]; then logger -t openclaw "检测到版本变更 ($last_ver -> $current_ver),执行配置迁移 (doctor --fix)..." - OPENCLAW_HOME="$OC_DATA" OPENCLAW_CONFIG_PATH="$CONFIG_FILE" \ - NODE_ICU_DATA="${NODE_BASE}/share/icu" PATH="${NODE_BASE}/bin:${OC_GLOBAL}/bin:$PATH" \ - "$NODE_BIN" "$oc_entry" doctor --fix 2>/dev/null && \ + _run_doctor_fix "$oc_entry" && \ logger -t openclaw "配置迁移完成" || \ logger -t openclaw "配置迁移失败,请手动检查" # 更新标记文件 diff --git a/luci-app-openclaw/root/usr/bin/openclaw-env b/luci-app-openclaw/root/usr/bin/openclaw-env index d6f6c6a5e..be3506122 100755 --- a/luci-app-openclaw/root/usr/bin/openclaw-env +++ b/luci-app-openclaw/root/usr/bin/openclaw-env @@ -7,22 +7,22 @@ # openclaw-env upgrade — 升级 OpenClaw 到最新版 # openclaw-env node — 仅下载/更新 Node.js # 环境变量: -# OC_VERSION — 指定 OpenClaw 版本 (如 2026.6.11),不设置则安装已测试稳定版 +# OC_VERSION — 指定 OpenClaw 版本 (如 2026.7.1-2),不设置则安装已测试稳定版 # ============================================================================ set -e # ── Node.js 版本策略 (双版本兼容) ── -# V2: 当前推荐版本,用于 OpenClaw v2026.6.11+ (要求 >= 22.19.0,使用 Alpine musl LTS 包确保可运行) +# V2: 当前推荐版本,用于 OpenClaw v2026.7.1-2+ (要求 >= 22.22.3,使用 Alpine musl LTS 包确保可运行) # V1: 旧版兼容,用于 OpenClaw v2026.3.8 及更早版本 NODE_VERSION_V2="22.23.0" NODE_VERSION_V1="22.15.1" # 默认使用 V2 版本 (可通过 NODE_VERSION 环境变量覆盖) NODE_VERSION="${NODE_VERSION:-${NODE_VERSION_V2}}" # 经过验证的 OpenClaw 稳定版本 (更新此值需同步测试) -OC_TESTED_VERSION="2026.6.11" -# OpenClaw 2026.6.x package.json engines.node 的最低要求。 +OC_TESTED_VERSION="2026.7.1-2" +# OpenClaw 2026.7.x package.json engines.node 的最低要求。 # 如果未来上游提高要求,安装成功后还会从 package.json 再做一次强校验。 -OC_NODE_MIN_VERSION="${OC_NODE_MIN_VERSION:-22.19.0}" +OC_NODE_MIN_VERSION="${OC_NODE_MIN_VERSION:-22.22.3}" # 用户可通过 OC_VERSION 环境变量覆盖安装版本 OC_VERSION="${OC_VERSION:-}" @@ -279,6 +279,55 @@ detect_arch() { esac } +# 计算文件 sha256 (优先 sha256sum,兜底 openssl;openssl-util 是本包硬依赖) +oc_sum256() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" 2>/dev/null | awk '{print $1}' + elif command -v openssl >/dev/null 2>&1; then + openssl dgst -sha256 -r "$1" 2>/dev/null | awk '{print $1}' + else + return 1 + fi +} + +# 校验 Node tarball 完整性: +# - 官方 / unofficial-builds / npmmirror 镜像: 拉取同目录 SHASUMS256.txt 比对 +# - 自托管 node-bins: 优先下载同名 .sha256 边车文件; 旧资产没有哈希时降级为体积检查 +# - 哈希源本身不可用时仅告警不阻断 (离线/镜像异常场景仍可继续) +verify_node_tarball() { + local file="$1" mirror_url="$2" filename + filename=$(basename "$mirror_url") + local actual + actual=$(oc_sum256 "$file") || { log_warn "无可用 sha256 工具,跳过完整性校验"; return 0; } + + local expected="" + if [ "${mirror_url#*node-bins}" != "$mirror_url" ]; then + # 自托管 Release 资产: 尝试 .sha256 边车文件 + if curl -fsSL --connect-timeout 10 --max-time 30 -o "${file}.sha256" "${mirror_url}.sha256" 2>/dev/null; then + expected=$(awk '{print $1}' "${file}.sha256" 2>/dev/null) + rm -f "${file}.sha256" + fi + else + # 官方系镜像: 同版本目录下的 SHASUMS256.txt + local shasum_url="${mirror_url%/*}/SHASUMS256.txt" + if curl -fsSL --connect-timeout 10 --max-time 30 -o "${file}.sums" "$shasum_url" 2>/dev/null; then + expected=$(awk -v f="$filename" '$2 == f {print $1}' "${file}.sums" 2>/dev/null) + rm -f "${file}.sums" + fi + fi + + if [ -z "$expected" ]; then + log_warn "未获取到 ${filename} 的官方哈希,跳过强校验 (仅体积检查)" + return 0 + fi + if [ "$actual" = "$expected" ]; then + log_info "SHA256 校验通过" + return 0 + fi + log_error "SHA256 校验失败: ${filename}" + return 1 +} + download_node() { local node_ver="$1" local node_arch @@ -347,8 +396,12 @@ download_node() { # 校验文件大小 (Node.js xz 压缩包至少 5MB; Alpine 精简包约 12MB, 官方完整包约 30MB) local fsize=$(wc -c < "$tmp_file" 2>/dev/null || echo 0) if [ "$fsize" -gt 5000000 ] 2>/dev/null; then - downloaded=1 - break + if verify_node_tarball "$tmp_file" "$mirror_url"; then + downloaded=1 + break + else + rm -f "$tmp_file" + fi else log_warn "文件大小异常 (${fsize} bytes), 跳过" rm -f "$tmp_file" 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 974514e91..3a24ad236 100755 --- a/luci-app-openclaw/root/usr/share/openclaw/oc-config.sh +++ b/luci-app-openclaw/root/usr/share/openclaw/oc-config.sh @@ -102,6 +102,30 @@ oc_cmd() { fi } +# 以 openclaw 服务用户运行官方诊断。 +# OpenClaw 2026.7.x 的插件安全检查按当前进程 uid 校验插件目录属主 +# (属主必须等于进程 uid 或为 root)。本脚本在 PTY 中以 root 运行, +# root 身份执行 doctor 会把 openclaw 属主的微信插件目录判为 +# suspicious ownership 并自动清理 plugins.allow / channels 配置。 +oc_doctor_as_openclaw() { + if [ -z "$OC_ENTRY" ] || [ ! -x "$NODE_BIN" ]; then + echo "ERROR: OpenClaw 未安装或 Node.js 不可用" + return 1 + fi + if ! id openclaw >/dev/null 2>&1; then + oc_cmd doctor + return $? + fi + local _doc_cmd="HOME=\"${OC_DATA}\" OPENCLAW_HOME=\"${OC_DATA}\" OPENCLAW_STATE_DIR=\"${OC_DATA}/.openclaw\" OPENCLAW_CONFIG_PATH=\"${CONFIG_FILE}\" NODE_ICU_DATA=\"${NODE_BASE}/share/icu\" \"${NODE_BIN}\" \"${OC_ENTRY}\" doctor" + if command -v su >/dev/null 2>&1; then + su -s /bin/sh openclaw -c "${_doc_cmd}" 2>&1 + elif command -v runuser >/dev/null 2>&1; then + runuser -u openclaw -- sh -c "${_doc_cmd}" 2>&1 + else + start-stop-daemon -S -m -p /tmp/openclaw-doctor-$$.pid -c openclaw:openclaw -x /bin/sh -- -c "${_doc_cmd}" 2>&1 + fi +} + # ── JSON 读写 (使用 Node.js) ── json_get() { if [ ! -f "$CONFIG_FILE" ]; then echo ""; return; fi @@ -2175,7 +2199,7 @@ health_check() { echo "" echo -e " ${CYAN}运行官方诊断...${NC}" - oc_cmd doctor 2>/dev/null || true + oc_doctor_as_openclaw 2>/dev/null || true echo "" echo -e " ${CYAN}最近日志 (最后 10 行):${NC}" diff --git a/luci-app-openclaw/tests/test_openclaw_contracts.sh b/luci-app-openclaw/tests/test_openclaw_contracts.sh index b716c896c..ccbd3c8b8 100755 --- a/luci-app-openclaw/tests/test_openclaw_contracts.sh +++ b/luci-app-openclaw/tests/test_openclaw_contracts.sh @@ -6,9 +6,9 @@ fail() { exit 1 } -grep -q "OC_TESTED_VERSION=\"2026.6.11\"" root/usr/bin/openclaw-env || fail "tested OpenClaw version not pinned" +grep -q "OC_TESTED_VERSION=\"2026.7.1-2\"" root/usr/bin/openclaw-env || fail "tested OpenClaw version not pinned" grep -q "NODE_VERSION_V2=\"22.23.0\"" root/usr/bin/openclaw-env || fail "default Node.js version not pinned" -grep -q "OC_NODE_MIN_VERSION=\"\${OC_NODE_MIN_VERSION:-22.19.0}\"" root/usr/bin/openclaw-env || fail "minimum Node.js version not pinned" +grep -q "OC_NODE_MIN_VERSION=\"\${OC_NODE_MIN_VERSION:-22.22.3}\"" root/usr/bin/openclaw-env || fail "minimum Node.js version not pinned" grep -q "oc_assert_node_min_version" root/usr/bin/openclaw-env || fail "Node.js minimum version check missing" grep -q 'oc_node_version_ge "$from_pkg" "$required"' root/usr/bin/openclaw-env || fail "package Node.js requirement must not lower static minimum" grep -q "install_openclaw_cli_wrapper" root/usr/bin/openclaw-env || fail "OpenClaw CLI wrapper must set runtime env" @@ -56,6 +56,10 @@ if grep -q "delete d.plugins.entries\\['openclaw-weixin'\\]" root/etc/init.d/ope fi grep -q "npm/projects" root/etc/init.d/openclaw || fail "npm plugin project ownership fix missing" grep -q "! -path.*npm/projects" root/usr/libexec/openclaw-permissions.sh || fail "npm plugin projects should be excluded from generic ownership reset" +grep -q "_run_doctor_fix" root/etc/init.d/openclaw || fail "doctor --fix must run through the service-user wrapper" +grep -q "su -s /bin/sh openclaw -c" root/etc/init.d/openclaw || fail "doctor --fix must run as the openclaw service user (2026.7.x plugin ownership check)" +grep -q "oc_doctor_as_openclaw" root/usr/share/openclaw/oc-config.sh || fail "oc-config doctor must run as the openclaw service user" +grep -q "(-\[0-9]+)?" root/etc/init.d/openclaw || fail "doctor version marker must keep the patch suffix (e.g. 2026.7.1-2)" grep -q "archived-extensions" root/etc/init.d/openclaw || fail "legacy weixin extension archive missing" grep -q "find_wechat_plugin_dir" luasrc/controller/openclaw.lua || fail "wechat npm plugin detection missing" grep -q "wechat_openclaw_plugin_install_cmd" luasrc/controller/openclaw.lua || fail "wechat install must use OpenClaw plugin installer" @@ -105,7 +109,7 @@ grep -q "微信插件注册状态已有效,跳过重复刷新" root/etc/init.d grep -q "d.plugins.entries\['openclaw-weixin'\].enabled===true" root/etc/init.d/openclaw || fail "init.d fast path must require the official enable entry" grep -q "openclaw-permissions.sh" root/etc/init.d/openclaw || fail "init.d must use permission helper" grep -q "fix_openclaw_state_permissions" root/etc/init.d/openclaw || fail "init.d final permission helper call missing" -grep -q 'NODE_ICU_DATA="${NODE_BASE}/share/icu" PATH="${NODE_BASE}/bin:${OC_GLOBAL}/bin:$PATH"' root/etc/init.d/openclaw || fail "doctor migration must run with Node ICU data" +grep -q 'NODE_ICU_DATA=\\"${NODE_BASE}/share/icu\\"' root/etc/init.d/openclaw || fail "doctor migration must run with Node ICU data" grep -q "Token 同步 (doctor 后): UCI -> JSON" root/etc/init.d/openclaw || fail "doctor must restore missing JSON token from UCI" grep -q 'OPENCLAW_GATEWAY_TOKEN="$gw_token"' root/etc/init.d/openclaw || fail "gateway must receive token env fallback" grep -q "clear_jiti_cache_on_start" root/etc/init.d/openclaw || fail "JITI cache cleanup must be opt-in to avoid slow startup" diff --git a/wwand/Makefile b/wwand/Makefile index 21d0b0d03..643c887dd 100644 --- a/wwand/Makefile +++ b/wwand/Makefile @@ -30,11 +30,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=wwand -PKG_RELEASE:=5 +PKG_RELEASE:=6 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git -PKG_SOURCE_VERSION:=d83bad8714c85db500dfe429fc64428d4f7a26bc +PKG_SOURCE_VERSION:=57196fbe92f8d6fa18a3c3e4e8269c54328f2ffe PKG_SOURCE_DATE:=2026-08-21 PKG_MIRROR_HASH:=skip