mirror of
https://github.com/hotwa/luci-app-openclaw.git
synced 2026-03-31 04:52:33 +00:00
release: v1.0.5 — 修复 spawn script ENOENT、补全依赖
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@@ -4,6 +4,17 @@
|
|||||||
|
|
||||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
||||||
|
|
||||||
|
## [1.0.5] - 2026-03-05
|
||||||
|
|
||||||
|
### 修复配置管理页面 "spawn script ENOENT" 启动失败 (#3, #4)
|
||||||
|
|
||||||
|
#### 修复
|
||||||
|
- **Web PTY 启动失败**: `web-pty.js` 硬编码依赖 `script` 命令 (来自 `util-linux-script`),但部分 OpenWrt 固件默认不包含该命令,导致 `spawn script ENOENT` 错误并无限循环重启
|
||||||
|
- 新增 `script` 命令自动检测,不存在时回退到 `sh` 直接执行 `oc-config.sh`
|
||||||
|
- 新增连续失败计数器 (最多 5 次),防止启动失败时的无限重试循环
|
||||||
|
- 失败时向用户终端显示明确的错误提示和修复命令
|
||||||
|
- **Makefile 依赖补全**: `LUCI_DEPENDS` 新增 `+util-linux-script`,确保新安装自动拉取 `script` 命令
|
||||||
|
|
||||||
## [1.0.4] - 2026-03-05
|
## [1.0.4] - 2026-03-05
|
||||||
|
|
||||||
### 适配 OpenClaw 2026.3.2
|
### 适配 OpenClaw 2026.3.2
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -13,7 +13,7 @@ PKG_MAINTAINER:=10000ge10000 <10000ge10000@users.noreply.github.com>
|
|||||||
PKG_LICENSE:=GPL-3.0
|
PKG_LICENSE:=GPL-3.0
|
||||||
|
|
||||||
LUCI_TITLE:=OpenClaw AI 网关 LuCI 管理插件
|
LUCI_TITLE:=OpenClaw AI 网关 LuCI 管理插件
|
||||||
LUCI_DEPENDS:=+luci-compat +luci-base +curl +openssl-util
|
LUCI_DEPENDS:=+luci-compat +luci-base +curl +openssl-util +util-linux-script
|
||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
# 优先使用 luci.mk (feeds 模式), 不可用时回退 package.mk
|
# 优先使用 luci.mk (feeds 模式), 不可用时回退 package.mk
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ class PtySession {
|
|||||||
this.rows = 24;
|
this.rows = 24;
|
||||||
this.buffer = Buffer.alloc(0);
|
this.buffer = Buffer.alloc(0);
|
||||||
this.alive = true;
|
this.alive = true;
|
||||||
|
this._spawnFailCount = 0;
|
||||||
|
this._MAX_SPAWN_RETRIES = 5;
|
||||||
activeSessions++;
|
activeSessions++;
|
||||||
console.log(`[oc-config] Session created (active: ${activeSessions}/${MAX_SESSIONS})`);
|
console.log(`[oc-config] Session created (active: ${activeSessions}/${MAX_SESSIONS})`);
|
||||||
this._setupWSReader();
|
this._setupWSReader();
|
||||||
@@ -153,14 +155,36 @@ class PtySession {
|
|||||||
OPENCLAW_CONFIG_PATH: `${OC_DATA}/.openclaw/openclaw.json`,
|
OPENCLAW_CONFIG_PATH: `${OC_DATA}/.openclaw/openclaw.json`,
|
||||||
PATH: `${NODE_BASE}/bin:${OC_GLOBAL}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`,
|
PATH: `${NODE_BASE}/bin:${OC_GLOBAL}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`,
|
||||||
};
|
};
|
||||||
this.proc = spawn('script', ['-qc', `stty rows ${this.rows} cols ${this.cols} 2>/dev/null; printf '\\e[?2004l'; sh "${SCRIPT_PATH}"`, '/dev/null'],
|
// 检测 script 命令是否可用 (OpenWrt 默认不包含 util-linux-script)
|
||||||
{ stdio: ['pipe', 'pipe', 'pipe'], env, detached: true });
|
// 如不可用则回退到直接用 sh 执行,牺牲 PTY 但保证功能可用
|
||||||
|
const hasScript = (() => {
|
||||||
|
try {
|
||||||
|
const { execFileSync } = require('child_process');
|
||||||
|
execFileSync('which', ['script'], { stdio: 'pipe', timeout: 2000 });
|
||||||
|
return true;
|
||||||
|
} catch { return false; }
|
||||||
|
})();
|
||||||
|
if (hasScript) {
|
||||||
|
this.proc = spawn('script', ['-qc', `stty rows ${this.rows} cols ${this.cols} 2>/dev/null; printf '\\e[?2004l'; sh "${SCRIPT_PATH}"`, '/dev/null'],
|
||||||
|
{ stdio: ['pipe', 'pipe', 'pipe'], env, detached: true });
|
||||||
|
} else {
|
||||||
|
console.log('[oc-config] "script" command not found, falling back to sh (install util-linux-script for full PTY support)');
|
||||||
|
this.proc = spawn('sh', [SCRIPT_PATH],
|
||||||
|
{ stdio: ['pipe', 'pipe', 'pipe'], env, detached: true });
|
||||||
|
}
|
||||||
|
|
||||||
this.proc.stdout.on('data', (d) => { if (this.alive) this.socket.write(encodeWSFrame(d, 0x01)); });
|
this.proc.stdout.on('data', (d) => { if (this.alive) { this._spawnFailCount = 0; this.socket.write(encodeWSFrame(d, 0x01)); } });
|
||||||
this.proc.stderr.on('data', (d) => { if (this.alive) this.socket.write(encodeWSFrame(d, 0x01)); });
|
this.proc.stderr.on('data', (d) => { if (this.alive) { this._spawnFailCount = 0; this.socket.write(encodeWSFrame(d, 0x01)); } });
|
||||||
this.proc.on('close', (code) => {
|
this.proc.on('close', (code) => {
|
||||||
if (!this.alive) return;
|
if (!this.alive) return;
|
||||||
console.log(`[oc-config] Script exited with code ${code}, auto-restarting...`);
|
this._spawnFailCount++;
|
||||||
|
if (this._spawnFailCount > this._MAX_SPAWN_RETRIES) {
|
||||||
|
console.log(`[oc-config] Script failed ${this._spawnFailCount} times, stopping retries`);
|
||||||
|
this.socket.write(encodeWSFrame(`\r\n\x1b[31m配置脚本连续启动失败 ${this._spawnFailCount} 次,已停止重试。\r\n请检查是否已安装 util-linux-script 包: opkg install coreutils-script\x1b[0m\r\n`, 0x01));
|
||||||
|
this.proc = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`[oc-config] Script exited with code ${code}, auto-restarting (attempt ${this._spawnFailCount}/${this._MAX_SPAWN_RETRIES})...`);
|
||||||
this.socket.write(encodeWSFrame(`\r\n\x1b[33m配置脚本已退出 (code: ${code}),正在自动重启...\x1b[0m\r\n`, 0x01));
|
this.socket.write(encodeWSFrame(`\r\n\x1b[33m配置脚本已退出 (code: ${code}),正在自动重启...\x1b[0m\r\n`, 0x01));
|
||||||
this.proc = null;
|
this.proc = null;
|
||||||
// 自动重启脚本,保持 WebSocket 连接
|
// 自动重启脚本,保持 WebSocket 连接
|
||||||
@@ -171,6 +195,7 @@ class PtySession {
|
|||||||
}, 1500);
|
}, 1500);
|
||||||
});
|
});
|
||||||
this.proc.on('error', (err) => {
|
this.proc.on('error', (err) => {
|
||||||
|
this._spawnFailCount++;
|
||||||
if (this.alive) this.socket.write(encodeWSFrame(`\r\n\x1b[31m启动失败: ${err.message}\x1b[0m\r\n`, 0x01));
|
if (this.alive) this.socket.write(encodeWSFrame(`\r\n\x1b[31m启动失败: ${err.message}\x1b[0m\r\n`, 0x01));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ mkdir -p "$CTRL_DIR"
|
|||||||
cat > "$CTRL_DIR/control" << EOF
|
cat > "$CTRL_DIR/control" << EOF
|
||||||
Package: ${PKG_NAME}
|
Package: ${PKG_NAME}
|
||||||
Version: ${PKG_VERSION}-${PKG_RELEASE}
|
Version: ${PKG_VERSION}-${PKG_RELEASE}
|
||||||
Depends: luci-compat, luci-base, curl, openssl-util
|
Depends: luci-compat, luci-base, curl, openssl-util, util-linux-script
|
||||||
Source: https://github.com/10000ge10000/luci-app-openclaw
|
Source: https://github.com/10000ge10000/luci-app-openclaw
|
||||||
SourceName: ${PKG_NAME}
|
SourceName: ${PKG_NAME}
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
|
|||||||
Reference in New Issue
Block a user