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 000000000..00d875d3e Binary files /dev/null and b/kaiplus/files/logo.png differ diff --git a/linkease-common-bin/Makefile b/linkease-common-bin/Makefile index 47f1617e0..c65c1a54f 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:=12 +PKG_RELEASE:=13 ARCH_HEXCODE:= ifeq ($(ARCH),x86_64) @@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel) ARCH_HEXCODE=1b0c endif -PKG_SOURCE_VERSION:=731629bd526b5215e0a0ad39eb740d45bb458ec5 +PKG_SOURCE_VERSION:=70c21864a1e95ece2ae6e4b144ce2d7a68d0405f 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 4f505e917..3d33e3d44 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:=15 +PKG_RELEASE:=16 ARCH_HEXCODE:= ifeq ($(ARCH),x86_64) @@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel) ARCH_HEXCODE=1b0c endif -PKG_SOURCE_VERSION:=731629bd526b5215e0a0ad39eb740d45bb458ec5 +PKG_SOURCE_VERSION:=70c21864a1e95ece2ae6e4b144ce2d7a68d0405f 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 39e064f0e..e9e4f31f8 100644 --- a/linkeasefull/Makefile +++ b/linkeasefull/Makefile @@ -10,8 +10,8 @@ PKG_ARCH_LINKEASE:=$(ARCH) PKG_NAME:=linkeasefull # use PKG_SOURCE_DATE instead of PKG_VERSION for compitable -PKG_SOURCE_DATE:=3.0.11 -PKG_RELEASE:=14 +PKG_SOURCE_DATE:=3.0.12 +PKG_RELEASE:=15 ARCH_HEXCODE:= ifeq ($(ARCH),x86_64) @@ -24,7 +24,7 @@ LINKEASE_RUNTIME_ARCH:=arm64 PKG_HASH:=skip endif -PKG_SOURCE_VERSION:=731629bd526b5215e0a0ad39eb740d45bb458ec5 +PKG_SOURCE_VERSION:=70c21864a1e95ece2ae6e4b144ce2d7a68d0405f 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-openclaw/CHANGELOG.md b/luci-app-openclaw/CHANGELOG.md index 0feaed78e..ef57a82f6 100644 --- a/luci-app-openclaw/CHANGELOG.md +++ b/luci-app-openclaw/CHANGELOG.md @@ -4,6 +4,32 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。 +## [2.0.12] - 2026-08-22 + +### 适配 OpenClaw v2026.7.1-2 + +- `OC_TESTED_VERSION` 更新为 `2026.7.1-2`(npm `latest` 稳定标签),稳定版安装走该版本号。 +- Node.js 最低要求从 `22.19.0` 提升到 `22.22.3`,对齐 OpenClaw 2026.7.x 的 `engines.node`;默认 Node 仍为 `22.23.0`(musl LTS),无需重新下载。 +- 微信插件维持 `@tencent-weixin/openclaw-weixin@2.4.6`(上游最新稳定版)。 +- 适配 2026.7.x 插件目录所有权安全检查:`doctor --fix`(init.d 版本迁移)与配置终端的 `doctor` 诊断改为以 `openclaw` 服务用户执行,避免 root 上下文把 openclaw 属主的微信插件判为 suspicious ownership 后自动清理 `plugins.allow` / `channels` 配置。 +- doctor 版本标记提取正则保留补丁后缀(`2026.7.1-2`),避免上游补丁版迭代时跳过配置迁移。 +- 修复 CI 中 `OC_TESTED_VERSION` 提取正则无法匹配带连字符版本号(如 `2026.7.1-2`)的问题。 + +### 修复 + +- 修复微信插件安装/登录最后一步必然失败的问题:启用插件的内置 JS 使用了 `path.dirname` 却未 `require('path')`,导致 `ReferenceError`(对应 #88、#96)。 +- 修复 LuCI 停止/重启网关时优雅停止命令失效的问题:裸 `openclaw gateway stop` 在 uhttpd 环境 PATH 不可达,改为调用安装目录 CLI wrapper 全路径(对应 #100)。 +- LuCI 侧安全根目录白名单回退实现与 `paths.lua` 对齐(补 `/openclaw`、`/srv/*/openclaw`、`/overlay/upper/opt/openclaw`)。 +- `/etc/config/openclaw` 补充 `pty_token` 字段声明,配置结构自文档化。 + +### 优化 + +- Node.js 下载增加 SHA256 完整性校验:官方 / unofficial-builds / npmmirror 镜像拉取同目录 `SHASUMS256.txt` 比对;自托管 `node-bins` 资产优先校验 `.sha256` 边车文件,旧资产无哈希时降级为体积检查并告警。 +- CI 构建流程新增契约测试步骤(`tests/` 全部 shell + lua 测试),版本断言不同步将直接阻断发布。 +- README 修正:移除目录结构中不存在的 `download_deps.sh`;Release 产物说明与 CI 实际行为对齐。 + +--- + ## [2.0.11] - 2026-07-10 ### 修复微信扫码后 Gateway 丢失插件 diff --git a/luci-app-openclaw/Makefile b/luci-app-openclaw/Makefile index 6061cb915..7ea54ecbe 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:=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