From 67f04e6bd02ead3eaa6680cd7d8d5bf07a8ba5d4 Mon Sep 17 00:00:00 2001 From: 10000ge10000 <10000ge10000@users.noreply.github.com> Date: Sat, 7 Mar 2026 12:27:09 +0800 Subject: [PATCH] =?UTF-8?q?release:=20v1.0.9=20=E2=80=94=20=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E4=B8=80=E9=94=AE=E5=8D=87=E7=BA=A7=E3=80=81=E7=99=BE?= =?UTF-8?q?=E7=82=BC=E6=A8=A1=E5=9E=8B=E5=88=97=E8=A1=A8=E6=89=A9=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 24 +++++ README.md | 36 +------- VERSION | 2 +- luasrc/controller/openclaw.lua | 127 +++++++++++++++++++++++++++ luasrc/model/cbi/openclaw/basic.lua | 94 ++++++++++++++++++-- root/usr/share/openclaw/oc-config.sh | 47 ++++++++-- root/usr/share/openclaw/web-pty.js | 6 +- scripts/build_ipk.sh | 5 +- scripts/build_run.sh | 27 +++++- 9 files changed, 313 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4d1364..d6e6aac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。 +## [1.0.9] - 2026-03-08 + +### 插件一键升级 & 百炼模型列表扩充 + +#### 新增 +- **插件一键升级**: LuCI 界面"检测升级"发现新版后,可直接点击"⬆️ 升级插件"按钮完成在线升级 + - 后台自动从 GitHub Releases 下载 `.run` 安装包并执行 + - 实时升级日志显示,带容错处理 (安装过程替换 LuCI 文件导致 API 暂时不可用时自动判定成功) + - 同时保留"📥 手动下载"链接作为备选 +- **百炼按量付费模型列表扩充**: 从 4 个模型扩充至 16 个,按类别分组显示 + - 千问商业版: qwen-max、qwen-plus (Qwen3.5)、qwen-flash (Qwen3.5)、qwen-turbo、qwen-long (1000万Token上下文) + - 千问Coder: qwen3-coder-plus (100万上下文)、qwen3-coder-flash + - 推理模型: qwq-plus + - 千问开源版: qwen3-235b-a22b、qwen3-32b、qwen3-30b-a3b + - 第三方模型: deepseek-r1、deepseek-v3、kimi-k2.5、glm-5、MiniMax-M2.5 + +#### 修复 +- **CBI 底部按钮未隐藏**: "保存并应用/保存/复位"按钮在基本设置页仍然显示 + - 根因: `m.submit = false` 和 `m.reset = false` 不被 CBI 框架识别 + - 修复: 改为 `m.pageaction = false` (dispatcher.lua 第 294 行检查的正确属性) +- **插件升级后配置管理无法连接**: 升级后 PTY WebSocket 一直转圈 "等待服务就绪" + - 根因: `.run` 安装器覆盖 `/etc/config/openclaw` 导致 `pty_token` 丢失,PTY 认证失败 + - 修复: 升级时保留用户 UCI 配置 (仅首次安装部署默认配置);安装后自动重启 PTY 服务 + ## [1.0.8] - 2026-03-07 ### 修复第三方模型配置导致 Gateway 崩溃 & 新增 Coding Plan 套餐支持 diff --git a/README.md b/README.md index 61d64b8..b3f8af6 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ | 架构 | x86_64 或 aarch64 (ARM64) | | C 库 | glibc 或 musl(自动检测) | | 依赖 | luci-compat, luci-base, curl, openssl-util | -| 存储 | 1.5GB 以上可用空间 | -| 内存 | 推荐 2GB 及以上 | +| 存储 | **1.5GB 以上可用空间** | +| 内存 | 推荐 1GB 及以上 | ### 🖥️ 兼容性矩阵 @@ -168,38 +168,6 @@ luci-app-openclaw/ └── .github/workflows/build.yml # GitHub Actions ``` -## ❓ 常见问题 - -**安装后 LuCI 菜单没有出现** - -```bash -rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* -``` - -刷新浏览器即可。 - -**提示缺少依赖 luci-compat** - -```bash -opkg update && opkg install luci-compat -``` - -**Node.js 下载失败** - -网络问题,可指定国内镜像: - -```bash -NODE_MIRROR=https://npmmirror.com/mirrors/node openclaw-env setup -``` - -**是否支持 ARM 路由器** - -支持 aarch64(ARM64),包括晶晨 S905 系列、Raspberry Pi 4/5、R4S/R5S 等。ARM64 musl 设备使用项目自托管的 Node.js 包,自带完整依赖库,不依赖系统库版本。**不支持** 32 位 ARM(armv7l/armv6l),Node.js 22 没有 32 位预编译包。 - -**ARM64 设备安装后 Node.js 显示 Segmentation fault** - -旧版 OpenWrt(如 22.03)的系统 musl 版本较低,与新版 Node.js 不兼容。请确保使用最新版本的 `openclaw-env`(v1.0.1+),它会自动下载包含独立 musl 链接器的自托管 Node.js 包。 - ## 🤝 贡献 欢迎提交 Issue 和 Pull Request! diff --git a/VERSION b/VERSION index b0f3d96..66c4c22 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.8 +1.0.9 diff --git a/luasrc/controller/openclaw.lua b/luasrc/controller/openclaw.lua index e775294..4f44b87 100644 --- a/luasrc/controller/openclaw.lua +++ b/luasrc/controller/openclaw.lua @@ -64,6 +64,12 @@ function index() -- 获取网关 Token API (仅认证用户可访问) entry({"admin", "services", "openclaw", "get_token"}, call("action_get_token"), nil).leaf = true + + -- 插件升级 API + entry({"admin", "services", "openclaw", "plugin_upgrade"}, call("action_plugin_upgrade"), nil).leaf = true + + -- 插件升级日志 API (轮询) + entry({"admin", "services", "openclaw", "plugin_upgrade_log"}, call("action_plugin_upgrade_log"), nil).leaf = true end -- ═══════════════════════════════════════════ @@ -466,3 +472,124 @@ function action_get_token() http.prepare_content("application/json") http.write_json({ token = token, pty_token = pty_token }) end + +-- ═══════════════════════════════════════════ +-- 插件升级 API (后台下载 .run 并执行) +-- 参数: version — 目标版本号 (如 1.0.8) +-- ═══════════════════════════════════════════ +function action_plugin_upgrade() + local http = require "luci.http" + local sys = require "luci.sys" + + local version = http.formvalue("version") or "" + if version == "" then + http.prepare_content("application/json") + http.write_json({ status = "error", message = "缺少版本号参数" }) + return + end + + -- 安全检查: version 只允许数字和点 + if not version:match("^[%d%.]+$") then + http.prepare_content("application/json") + http.write_json({ status = "error", message = "版本号格式无效" }) + return + end + + -- 清理旧日志和状态 + sys.exec("rm -f /tmp/openclaw-plugin-upgrade.log /tmp/openclaw-plugin-upgrade.pid /tmp/openclaw-plugin-upgrade.exit") + + -- 后台执行: 下载 .run 并执行安装 + local run_url = "https://github.com/10000ge10000/luci-app-openclaw/releases/download/v" .. version .. "/luci-app-openclaw_" .. version .. ".run" + -- 使用 curl 下载 (-L 跟随重定向), 然后 sh 执行 + sys.exec(string.format( + "( echo '正在下载插件 v%s ...' > /tmp/openclaw-plugin-upgrade.log; " .. + "curl -sL --connect-timeout 15 --max-time 120 -o /tmp/luci-app-openclaw-update.run '%s' >> /tmp/openclaw-plugin-upgrade.log 2>&1; " .. + "RC=$?; " .. + "if [ $RC -ne 0 ]; then " .. + " echo '下载失败 (curl exit: '$RC')' >> /tmp/openclaw-plugin-upgrade.log; " .. + " echo '如果无法访问 GitHub,请手动下载: %s' >> /tmp/openclaw-plugin-upgrade.log; " .. + " echo $RC > /tmp/openclaw-plugin-upgrade.exit; " .. + "else " .. + " FSIZE=$(wc -c < /tmp/luci-app-openclaw-update.run 2>/dev/null | tr -d ' '); " .. + " echo \"下载完成 (${FSIZE} bytes)\" >> /tmp/openclaw-plugin-upgrade.log; " .. + " if [ \"$FSIZE\" -lt 10000 ] 2>/dev/null; then " .. + " echo '文件过小,可能下载失败或链接无效' >> /tmp/openclaw-plugin-upgrade.log; " .. + " echo 1 > /tmp/openclaw-plugin-upgrade.exit; " .. + " else " .. + " echo '' >> /tmp/openclaw-plugin-upgrade.log; " .. + " echo '正在安装...' >> /tmp/openclaw-plugin-upgrade.log; " .. + " sh /tmp/luci-app-openclaw-update.run >> /tmp/openclaw-plugin-upgrade.log 2>&1; " .. + " RC2=$?; echo $RC2 > /tmp/openclaw-plugin-upgrade.exit; " .. + " if [ $RC2 -eq 0 ]; then " .. + " echo '' >> /tmp/openclaw-plugin-upgrade.log; " .. + " echo '✅ 插件升级完成!请刷新浏览器页面。' >> /tmp/openclaw-plugin-upgrade.log; " .. + " else " .. + " echo '安装执行失败 (exit: '$RC2')' >> /tmp/openclaw-plugin-upgrade.log; " .. + " fi; " .. + " fi; " .. + " rm -f /tmp/luci-app-openclaw-update.run; " .. + "fi " .. + ") & echo $! > /tmp/openclaw-plugin-upgrade.pid", + version, run_url, run_url + )) + + http.prepare_content("application/json") + http.write_json({ + status = "ok", + message = "插件升级已在后台启动..." + }) +end + +-- ═══════════════════════════════════════════ +-- 插件升级日志轮询 API +-- ═══════════════════════════════════════════ +function action_plugin_upgrade_log() + local http = require "luci.http" + local sys = require "luci.sys" + + local log = "" + local f = io.open("/tmp/openclaw-plugin-upgrade.log", "r") + if f then + log = f:read("*a") or "" + f:close() + end + + local running = false + local pid_file = io.open("/tmp/openclaw-plugin-upgrade.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-plugin-upgrade.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 diff --git a/luasrc/model/cbi/openclaw/basic.lua b/luasrc/model/cbi/openclaw/basic.lua index 580a13e..794a16f 100644 --- a/luasrc/model/cbi/openclaw/basic.lua +++ b/luasrc/model/cbi/openclaw/basic.lua @@ -5,8 +5,7 @@ m = Map("openclaw", "OpenClaw AI 网关", "OpenClaw 是一个 AI 编程代理网关,支持 GitHub Copilot、Claude、GPT、Gemini 等大模型以及 Telegram、Discord 等多种消息渠道。") -- 隐藏底部的「保存并应用」「保存」「复位」按钮 (本页无可编辑的 UCI 选项) -m.submit = false -m.reset = false +m.pageaction = false -- ═══════════════════════════════════════════ -- 状态面板 @@ -28,6 +27,8 @@ act.cfgvalue = function(self, section) local update_url = luci.dispatcher.build_url("admin", "services", "openclaw", "do_update") local upgrade_log_url = luci.dispatcher.build_url("admin", "services", "openclaw", "upgrade_log") local uninstall_url = luci.dispatcher.build_url("admin", "services", "openclaw", "uninstall") + local plugin_upgrade_url = luci.dispatcher.build_url("admin", "services", "openclaw", "plugin_upgrade") + local plugin_upgrade_log_url = luci.dispatcher.build_url("admin", "services", "openclaw", "plugin_upgrade_log") local html = {} -- 按钮区域 @@ -239,7 +240,7 @@ act.cfgvalue = function(self, section) html[#html+1] = 'else{msgs.push("✅ OpenClaw: v"+r.current+" (已是最新)");}' -- 插件版本检查 html[#html+1] = 'if(r.plugin_current){' - html[#html+1] = 'if(r.plugin_has_update){msgs.push("� 插件: v"+r.plugin_current+" → v"+r.plugin_latest+" (有新版本,请到 GitHub 下载更新)");}' + html[#html+1] = 'if(r.plugin_has_update){msgs.push("🔌 插件: v"+r.plugin_current+" → v"+r.plugin_latest+" (有新版本)");}' html[#html+1] = 'else if(r.plugin_latest){msgs.push("✅ 插件: v"+r.plugin_current+" (已是最新)");}' html[#html+1] = 'else{msgs.push("🔌 插件: v"+r.plugin_current+" (无法检查最新版本)");}' html[#html+1] = '}' @@ -249,10 +250,12 @@ act.cfgvalue = function(self, section) html[#html+1] = 'act.style.display="block";' html[#html+1] = 'act.innerHTML=\'\';' html[#html+1] = '}' - -- 插件有更新时添加 GitHub 链接 + -- 插件有更新时: 一键升级按钮 + GitHub 下载链接 html[#html+1] = 'if(r.plugin_has_update){' html[#html+1] = 'act.style.display="block";' - html[#html+1] = 'act.innerHTML=(act.innerHTML||"")+\' 📥 下载插件 v\'+r.plugin_latest+\'\';' + html[#html+1] = 'window._pluginLatestVer=r.plugin_latest;' + html[#html+1] = 'act.innerHTML=(act.innerHTML||"")+\' \';' + html[#html+1] = 'act.innerHTML=act.innerHTML+\' 📥 手动下载\';' html[#html+1] = '}' html[#html+1] = '}catch(e){el.innerHTML="❌ 检测失败";}' html[#html+1] = '});}' @@ -330,6 +333,87 @@ act.cfgvalue = function(self, section) html[#html+1] = '}' html[#html+1] = '}' + -- ═══ 插件一键升级 ═══ + html[#html+1] = 'var _pluginUpgradeTimer=null;' + + html[#html+1] = 'function ocPluginUpgrade(){' + html[#html+1] = 'var ver=window._pluginLatestVer;' + html[#html+1] = 'if(!ver){alert("无法获取最新版本号");return;}' + html[#html+1] = 'if(!confirm("确定要升级插件到 v"+ver+"?\\n\\n升级会替换插件文件并清除 LuCI 缓存,不会影响正在运行的 OpenClaw 服务。"))return;' + html[#html+1] = 'var btn=document.getElementById("btn-plugin-upgrade");' + html[#html+1] = 'var panel=document.getElementById("setup-log-panel");' + html[#html+1] = 'var logEl=document.getElementById("setup-log-content");' + html[#html+1] = 'var titleEl=document.getElementById("setup-log-title");' + html[#html+1] = 'var statusEl=document.getElementById("setup-log-status");' + html[#html+1] = 'var resultEl=document.getElementById("setup-log-result");' + html[#html+1] = 'btn.disabled=true;btn.textContent="⏳ 正在升级插件...";' + html[#html+1] = 'panel.style.display="block";' + html[#html+1] = 'logEl.textContent="正在启动插件升级...\\n";' + html[#html+1] = 'titleEl.textContent="📋 插件升级日志";' + html[#html+1] = 'statusEl.innerHTML="⏳ 插件升级中...";' + html[#html+1] = 'resultEl.style.display="none";' + html[#html+1] = '(new XHR()).get("' .. plugin_upgrade_url .. '?version="+encodeURIComponent(ver),null,function(x){' + html[#html+1] = 'try{JSON.parse(x.responseText);}catch(e){}' + html[#html+1] = 'ocPollPluginUpgradeLog();' + html[#html+1] = '});' + html[#html+1] = '}' + + -- 轮询插件升级日志 (带容错: 安装时文件被替换可能导致API暂时不可用) + html[#html+1] = 'var _pluginPollErrors=0;' + html[#html+1] = 'function ocPollPluginUpgradeLog(){' + html[#html+1] = 'if(_pluginUpgradeTimer)clearInterval(_pluginUpgradeTimer);' + html[#html+1] = '_pluginPollErrors=0;' + html[#html+1] = '_pluginUpgradeTimer=setInterval(function(){' + html[#html+1] = '(new XHR()).get("' .. plugin_upgrade_log_url .. '",null,function(x){' + html[#html+1] = 'try{' + html[#html+1] = 'var r=JSON.parse(x.responseText);' + html[#html+1] = '_pluginPollErrors=0;' + html[#html+1] = 'var logEl=document.getElementById("setup-log-content");' + html[#html+1] = 'var statusEl=document.getElementById("setup-log-status");' + html[#html+1] = 'if(r.log)logEl.textContent=r.log;' + html[#html+1] = 'logEl.scrollTop=logEl.scrollHeight;' + html[#html+1] = 'if(r.state==="running"){' + html[#html+1] = 'statusEl.innerHTML="⏳ 插件升级中...";' + html[#html+1] = '}else if(r.state==="success"){' + html[#html+1] = 'clearInterval(_pluginUpgradeTimer);_pluginUpgradeTimer=null;' + html[#html+1] = 'ocPluginUpgradeDone(true);' + html[#html+1] = '}else if(r.state==="failed"){' + html[#html+1] = 'clearInterval(_pluginUpgradeTimer);_pluginUpgradeTimer=null;' + html[#html+1] = 'ocPluginUpgradeDone(false);' + html[#html+1] = '}' + html[#html+1] = '}catch(e){' + html[#html+1] = '_pluginPollErrors++;' + html[#html+1] = 'if(_pluginPollErrors>=8){' + html[#html+1] = 'clearInterval(_pluginUpgradeTimer);_pluginUpgradeTimer=null;' + html[#html+1] = 'ocPluginUpgradeDone(true);' + html[#html+1] = '}' + html[#html+1] = '}' + html[#html+1] = '});' + html[#html+1] = '},2000);' + html[#html+1] = '}' + + -- 插件升级完成处理 + html[#html+1] = 'function ocPluginUpgradeDone(ok){' + html[#html+1] = 'var btn=document.getElementById("btn-plugin-upgrade");' + html[#html+1] = 'var statusEl=document.getElementById("setup-log-status");' + html[#html+1] = 'var resultEl=document.getElementById("setup-log-result");' + html[#html+1] = 'if(btn){btn.disabled=false;btn.textContent="⬆️ 升级插件";}' + html[#html+1] = 'resultEl.style.display="block";' + html[#html+1] = 'if(ok){' + html[#html+1] = 'statusEl.innerHTML="✅ 插件升级完成";' + html[#html+1] = 'resultEl.innerHTML="
cat /tmp/openclaw-plugin-upgrade.log