release: v1.0.9 — 插件一键升级、百炼模型列表扩充

This commit is contained in:
10000ge10000
2026-03-07 12:27:09 +08:00
parent 4489fe3e02
commit 67f04e6bd0
9 changed files with 313 additions and 55 deletions

View File

@@ -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 套餐支持

View File

@@ -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 路由器**
支持 aarch64ARM64包括晶晨 S905 系列、Raspberry Pi 4/5、R4S/R5S 等。ARM64 musl 设备使用项目自托管的 Node.js 包,自带完整依赖库,不依赖系统库版本。**不支持** 32 位 ARMarmv7l/armv6lNode.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

View File

@@ -1 +1 @@
1.0.8
1.0.9

View File

@@ -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

View File

@@ -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("<span style=\\"color:green\\">✅ OpenClaw: v"+r.current+" (已是最新)</span>");}'
-- 插件版本检查
html[#html+1] = 'if(r.plugin_current){'
html[#html+1] = 'if(r.plugin_has_update){msgs.push("<span style=\\"color:#e36209\\"><EFBFBD> 插件: v"+r.plugin_current+" → v"+r.plugin_latest+" (有新版本,请到 GitHub 下载更新)</span>");}'
html[#html+1] = 'if(r.plugin_has_update){msgs.push("<span style=\\"color:#e36209\\">🔌 插件: v"+r.plugin_current+" → v"+r.plugin_latest+" (有新版本)</span>");}'
html[#html+1] = 'else if(r.plugin_latest){msgs.push("<span style=\\"color:green\\">✅ 插件: v"+r.plugin_current+" (已是最新)</span>");}'
html[#html+1] = 'else{msgs.push("<span style=\\"color:#999\\">🔌 插件: v"+r.plugin_current+" (无法检查最新版本)</span>");}'
html[#html+1] = '}'
@@ -249,10 +250,12 @@ act.cfgvalue = function(self, section)
html[#html+1] = 'act.style.display="block";'
html[#html+1] = 'act.innerHTML=\'<button class="btn cbi-button cbi-button-apply" type="button" onclick="ocDoUpdate()" id="btn-do-update">⬆️ 立即升级 OpenClaw</button>\';'
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||"")+\' <a href="https://github.com/10000ge10000/luci-app-openclaw/releases/latest" target="_blank" rel="noopener" class="btn cbi-button cbi-button-action" style="text-decoration:none;">📥 下载插件 v\'+r.plugin_latest+\'</a>\';'
html[#html+1] = 'window._pluginLatestVer=r.plugin_latest;'
html[#html+1] = 'act.innerHTML=(act.innerHTML||"")+\' <button class="btn cbi-button cbi-button-apply" type="button" onclick="ocPluginUpgrade()" id="btn-plugin-upgrade">⬆️ 升级插件 v\'+r.plugin_latest+\'</button>\';'
html[#html+1] = 'act.innerHTML=act.innerHTML+\' <a href="https://github.com/10000ge10000/luci-app-openclaw/releases/latest" target="_blank" rel="noopener" class="btn cbi-button cbi-button-action" style="text-decoration:none;">📥 手动下载</a>\';'
html[#html+1] = '}'
html[#html+1] = '}catch(e){el.innerHTML="<span style=\\"color:red\\">❌ 检测失败</span>";}'
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="<span style=\\"color:#7aa2f7;\\">⏳ 插件升级中...</span>";'
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="<span style=\\"color:#7aa2f7;\\">⏳ 插件升级中...</span>";'
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="<span style=\\"color:#1a7f37;\\">✅ 插件升级完成</span>";'
html[#html+1] = 'resultEl.innerHTML="<div style=\\"border:1px solid #c6e9c9;background:#e6f7e9;padding:12px 16px;border-radius:6px;\\">"+'
html[#html+1] = '"<strong style=\\"color:#1a7f37;font-size:14px;\\">🎉 插件升级成功!</strong><br/>"+'
html[#html+1] = '"<span style=\\"color:#555;font-size:13px;line-height:1.8;\\">插件文件已更新OpenClaw 服务不受影响。请刷新页面加载新版界面。</span><br/>"+'
html[#html+1] = '"<button class=\\"btn cbi-button cbi-button-apply\\" type=\\"button\\" onclick=\\"location.reload()\\" style=\\"margin-top:10px;\\">🔄 刷新页面</button></div>";'
html[#html+1] = '}else{'
html[#html+1] = 'statusEl.innerHTML="<span style=\\"color:#cf222e;\\">❌ 插件升级失败</span>";'
html[#html+1] = 'resultEl.innerHTML="<div style=\\"border:1px solid #f5c6cb;background:#ffeef0;padding:12px 16px;border-radius:6px;\\">"+'
html[#html+1] = '"<strong style=\\"color:#cf222e;font-size:14px;\\">❌ 插件升级失败</strong><br/>"+'
html[#html+1] = '"<span style=\\"color:#555;font-size:13px;\\">请查看上方日志了解详情。也可手动执行:<code>cat /tmp/openclaw-plugin-upgrade.log</code></span><br/>"+'
html[#html+1] = '"<button class=\\"btn cbi-button cbi-button-apply\\" type=\\"button\\" onclick=\\"location.reload()\\" style=\\"margin-top:10px;\\">🔄 刷新页面</button></div>";'
html[#html+1] = '}'
html[#html+1] = '}'
-- 卸载运行环境
html[#html+1] = 'function ocUninstall(){'
html[#html+1] = 'if(!confirm("确定要卸载 OpenClaw 运行环境?\\n\\n将删除 Node.js、OpenClaw 程序及配置数据(/opt/openclaw 目录),服务将停止运行。\\n\\n插件本身不会被删除之后可重新安装运行环境。"))return;'

View File

@@ -651,20 +651,49 @@ configure_model() {
prompt_with_default "请输入百炼 API Key (sk-...)" "" api_key
if [ -n "$api_key" ]; then
echo ""
echo -e " ${CYAN}可用模型:${NC}"
echo -e " ${CYAN}a)${NC} qwen-max — 通义千问旗舰 (推荐)"
echo -e " ${CYAN}b)${NC} qwen-plus — 性价比之选"
echo -e " ${CYAN}c)${NC} qwen-turbo — 快速响应"
echo -e " ${CYAN}d)${NC} qwen3-235b-a22b — Qwen3 235B MoE"
echo -e " ${CYAN}e)${NC} 手动输入模型名"
echo -e " ${CYAN}── 千问商业版 ──${NC}"
echo -e " ${CYAN}a)${NC} qwen-max — 千问Max 旗舰模型 (推荐)"
echo -e " ${CYAN}b)${NC} qwen-plus — 千问Plus 均衡之选 (已升级Qwen3.5)"
echo -e " ${CYAN}c)${NC} qwen-flash — 千问Flash 速度最快 (已升级Qwen3.5)"
echo -e " ${CYAN}d)${NC} qwen-turbo — 千问Turbo 经济实惠"
echo -e " ${CYAN}e)${NC} qwen-long — 千问Long 超长上下文 (1000万Token)"
echo -e " ${CYAN}── 千问Coder ──${NC}"
echo -e " ${CYAN}f)${NC} qwen3-coder-plus — 代码专用旗舰 (100万上下文)"
echo -e " ${CYAN}g)${NC} qwen3-coder-flash — 代码专用极速"
echo -e " ${CYAN}── 推理模型 ──${NC}"
echo -e " ${CYAN}h)${NC} qwq-plus — QwQ推理模型 (数学/代码强化)"
echo -e " ${CYAN}── 千问开源版 ──${NC}"
echo -e " ${CYAN}i)${NC} qwen3-235b-a22b — Qwen3 235B MoE"
echo -e " ${CYAN}j)${NC} qwen3-32b — Qwen3 32B"
echo -e " ${CYAN}k)${NC} qwen3-30b-a3b — Qwen3 30B MoE"
echo -e " ${CYAN}── 第三方模型 ──${NC}"
echo -e " ${CYAN}l)${NC} deepseek-r1 — DeepSeek R1 推理"
echo -e " ${CYAN}m)${NC} deepseek-v3 — DeepSeek V3"
echo -e " ${CYAN}n)${NC} kimi-k2.5 — Kimi K2.5"
echo -e " ${CYAN}o)${NC} glm-5 — 智谱 GLM-5"
echo -e " ${CYAN}p)${NC} MiniMax-M2.5 — MiniMax M2.5"
echo -e " ${CYAN}────────────${NC}"
echo -e " ${CYAN}z)${NC} 手动输入模型名"
echo ""
prompt_with_default "请选择模型" "a" model_choice
case "$model_choice" in
a) model_name="qwen-max" ;;
b) model_name="qwen-plus" ;;
c) model_name="qwen-turbo" ;;
d) model_name="qwen3-235b-a22b" ;;
e) prompt_with_default "请输入模型名称" "qwen-max" model_name ;;
c) model_name="qwen-flash" ;;
d) model_name="qwen-turbo" ;;
e) model_name="qwen-long" ;;
f) model_name="qwen3-coder-plus" ;;
g) model_name="qwen3-coder-flash" ;;
h) model_name="qwq-plus" ;;
i) model_name="qwen3-235b-a22b" ;;
j) model_name="qwen3-32b" ;;
k) model_name="qwen3-30b-a3b" ;;
l) model_name="deepseek-r1" ;;
m) model_name="deepseek-v3" ;;
n) model_name="kimi-k2.5" ;;
o) model_name="glm-5" ;;
p) model_name="MiniMax-M2.5" ;;
z) prompt_with_default "请输入模型名称" "qwen-max" model_name ;;
*) model_name="qwen-max" ;;
esac
auth_set_apikey dashscope "$api_key"

View File

@@ -252,10 +252,12 @@ function handleUpgrade(req, socket, head) {
if (req.url !== '/ws' && !req.url.startsWith('/ws?')) { socket.destroy(); return; }
// 认证: 验证查询参数中的 token
if (AUTH_TOKEN) {
// 每次连接时实时读取 UCI token (安装/升级可能重新生成 token)
const currentToken = loadAuthToken() || AUTH_TOKEN;
if (currentToken) {
const url = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
const clientToken = url.searchParams.get('token') || '';
if (clientToken !== AUTH_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');
socket.destroy();

View File

@@ -102,7 +102,10 @@ cat > "$CTRL_DIR/postinst" << 'EOF'
#!/bin/sh
[ -n "${IPKG_INSTROOT}" ] || {
( . /etc/uci-defaults/99-openclaw ) && rm -f /etc/uci-defaults/99-openclaw
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* /tmp/luci-indexcache.*.json 2>/dev/null
# 重启 Web PTY (使其加载新文件和新 token)
PTY_PID=$(pgrep -f 'web-pty.js' 2>/dev/null | head -1)
[ -n "$PTY_PID" ] && kill "$PTY_PID" 2>/dev/null || true
exit 0
}
EOF

View File

@@ -29,9 +29,10 @@ trap "rm -rf '$STAGING'" EXIT
install_files() {
local dest="$1"
# UCI config
# UCI config (仅首次安装时部署默认配置; 升级时保留用户配置)
# 安装器会在解压后检测并跳过已有配置, 见 install.sh 中的逻辑
mkdir -p "$dest/etc/config"
cp "$PKG_DIR/root/etc/config/openclaw" "$dest/etc/config/"
cp "$PKG_DIR/root/etc/config/openclaw" "$dest/etc/config/openclaw.default"
# UCI defaults
mkdir -p "$dest/etc/uci-defaults"
@@ -111,6 +112,15 @@ echo "正在安装文件..."
ARCHIVE=$(awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' "$0")
tail -n +$ARCHIVE "$0" | tar xzf - -C / 2>/dev/null
# UCI 配置文件保护: 升级时不覆盖用户已有配置
if [ -f /etc/config/openclaw ] && [ -f /etc/config/openclaw.default ]; then
# 已有配置, 移除默认文件 (保留用户配置)
rm -f /etc/config/openclaw.default
elif [ -f /etc/config/openclaw.default ]; then
# 首次安装, 使用默认配置
mv /etc/config/openclaw.default /etc/config/openclaw
fi
# 注册到 opkg使 iStore 和 opkg 能识别此包
PKG="luci-app-openclaw"
PKG_VER="__PKG_VERSION__"
@@ -178,6 +188,16 @@ fi
# 清除 LuCI 缓存
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null
rm -f /tmp/luci-indexcache.*.json 2>/dev/null
# 重启 Web PTY 服务 (使其加载新文件和新 token)
# PTY 是 procd 管理的实例, kill 后 procd 会自动 respawn
PTY_PID=$(pgrep -f 'web-pty.js' 2>/dev/null | head -1)
if [ -n "$PTY_PID" ]; then
echo "重启配置终端服务..."
kill "$PTY_PID" 2>/dev/null
sleep 1
fi
echo ""
echo "✅ 安装完成!"
@@ -200,7 +220,8 @@ install_files "$STAGING/payload"
echo "[2/4] 生成文件列表..."
# 生成安装文件列表 (供 opkg 卸载时使用)
FILE_LIST=$(cd "$STAGING/payload" && find . -type f | sed 's|^\./|/|' | sort)
# 注: openclaw.default 安装后会变为 openclaw, 文件列表中记录最终路径
FILE_LIST=$(cd "$STAGING/payload" && find . -type f | sed 's|^\./|/|' | sed 's|/etc/config/openclaw.default|/etc/config/openclaw|' | sort)
echo "$(echo "$FILE_LIST" | wc -l | tr -d ' ') 个文件"
echo "[3/4] 创建安装器..."