mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-01 16:44:23 +08:00
🎈 Sync 2026-03-21 18:41:20
This commit is contained in:
1 parent
f9b782d3c8
commit
345b0373ec
9 files changed
+271
-247
No files matched your search
@@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_VERSION:=0.1.3
|
PKG_VERSION:=0.1.5
|
||||||
PKG_RELEASE:=5
|
PKG_RELEASE:=6
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI support for OpenClaw Launcher
|
LUCI_TITLE:=LuCI support for OpenClaw Launcher
|
||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
|
|||||||
@@ -431,7 +431,7 @@
|
|||||||
'<div class="' + (activeTab === "basic" ? '' : 'oclm-hidden') + '">' +
|
'<div class="' + (activeTab === "basic" ? '' : 'oclm-hidden') + '">' +
|
||||||
'<h2>基础配置</h2>' +
|
'<h2>基础配置</h2>' +
|
||||||
'<div class="oclm-form-grid oclm-form-grid-ai" style="margin-top: 26px;">' +
|
'<div class="oclm-form-grid oclm-form-grid-ai" style="margin-top: 26px;">' +
|
||||||
fieldInput("监听端口", '<input class="oclm-control" type="number" min="1" max="65535" id="oclm-port" value="' + escapeHtml(form.port || "18789") + '" />') +
|
fieldInput("监听端口", '<input class="oclm-control" type="number" min="1025" max="65535" id="oclm-port" value="' + escapeHtml(form.port || "18789") + '" />') +
|
||||||
fieldInput("监听范围", selectHtml("oclm-bind", form.bind, [
|
fieldInput("监听范围", selectHtml("oclm-bind", form.bind, [
|
||||||
["lan", "所有地址"],
|
["lan", "所有地址"],
|
||||||
["loopback", "仅本机"],
|
["loopback", "仅本机"],
|
||||||
|
|||||||
@@ -506,8 +506,8 @@ function action_config_data()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
local port_num = tonumber(port) or 0
|
local port_num = tonumber(port) or 0
|
||||||
if port_num < 1 or port_num > 65535 then
|
if port_num < 1025 or port_num > 65535 then
|
||||||
write_json({ ok = false, error = "invalid port" })
|
write_json({ ok = false, error = "invalid port (must be 1025-65535)" })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
uci:set("openclawmgr", section, "port", port)
|
uci:set("openclawmgr", section, "port", port)
|
||||||
|
|||||||
@@ -29,42 +29,29 @@ local node_dir = base_dir .. "/node"
|
|||||||
local global_dir = base_dir .. "/global"
|
local global_dir = base_dir .. "/global"
|
||||||
local data_dir = base_dir .. "/data"
|
local data_dir = base_dir .. "/data"
|
||||||
|
|
||||||
local cmdline = "sh /usr/share/openclawmgr/oc-config.sh"
|
|
||||||
|
|
||||||
s = m:section(SimpleSection)
|
s = m:section(SimpleSection)
|
||||||
|
|
||||||
o = s:option(Value, "mode", translate("Mode"))
|
o = s:option(Value, "mode", translate("Mode"))
|
||||||
o:value("menu", translate("Interactive menu"))
|
o:value("configure", translate("官方配置向导 (openclaw configure)"))
|
||||||
o.default = "menu"
|
o:value("backup", translate("备份配置"))
|
||||||
|
o:value("restore", translate("恢复配置"))
|
||||||
|
o.default = "configure"
|
||||||
o.forcewrite = true
|
o.forcewrite = true
|
||||||
o.write = function(self, section, value)
|
|
||||||
if value == "menu" then
|
|
||||||
cmdline = "sh /usr/share/openclawmgr/oc-config.sh"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o = s:option(DummyValue, "_tip", translate("CLI Config"))
|
o = s:option(DummyValue, "_tip", translate("CLI Config"))
|
||||||
o.rawhtml = true
|
o.rawhtml = true
|
||||||
o.cfgvalue = function()
|
o.cfgvalue = function()
|
||||||
return translate("Starts an interactive OpenClaw configuration menu in a web terminal (LAN only).")
|
return translate("Starts a web terminal (LAN only) for common OpenClaw maintenance tasks.")
|
||||||
end
|
end
|
||||||
|
|
||||||
o = s:option(Button, "connect")
|
local function start_ttyd(action)
|
||||||
o.render = function(self, section, scope)
|
|
||||||
self.inputstyle = "add"
|
|
||||||
self.title = " "
|
|
||||||
self.inputtitle = translate("Connect")
|
|
||||||
Button.render(self, section, scope)
|
|
||||||
end
|
|
||||||
|
|
||||||
o.write = function(self, section)
|
|
||||||
local cmd_ttyd = luci.util.exec("command -v ttyd"):match("^.+ttyd") or nil
|
local cmd_ttyd = luci.util.exec("command -v ttyd"):match("^.+ttyd") or nil
|
||||||
if not cmd_ttyd or cmd_ttyd:match("^%s*$") then
|
if not cmd_ttyd or cmd_ttyd:match("^%s*$") then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local pid = luci.util.trim(luci.util.exec("netstat -lnpt | grep :7682 | grep ttyd | tr -s ' ' | cut -d ' ' -f7 | cut -d'/' -f1"))
|
local pid = luci.util.trim(luci.util.exec("netstat -lnpt | grep :7682 | grep ttyd | tr -s ' ' | cut -d ' ' -f7 | cut -d'/' -f1"))
|
||||||
if pid and pid ~= "" then
|
if pid and pid:match("^%d+$") then
|
||||||
luci.util.exec("kill -9 " .. pid)
|
luci.util.exec("kill -9 " .. pid)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -82,6 +69,7 @@ o.write = function(self, section)
|
|||||||
"PATH=" .. luci.util.shellquote(env_path),
|
"PATH=" .. luci.util.shellquote(env_path),
|
||||||
}, " ")
|
}, " ")
|
||||||
|
|
||||||
|
local cmdline = "sh /usr/share/openclawmgr/openclawmgr-cli.sh " .. luci.util.shellquote(action)
|
||||||
local start_cmd = string.format(
|
local start_cmd = string.format(
|
||||||
"%s %s -d 2 --once -p 7682 sh -lc %s &",
|
"%s %s -d 2 --once -p 7682 sh -lc %s &",
|
||||||
env_prefix,
|
env_prefix,
|
||||||
@@ -96,4 +84,19 @@ o.write = function(self, section)
|
|||||||
o.template = "openclawmgr/cli"
|
o.template = "openclawmgr/cli"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
o = s:option(Button, "connect")
|
||||||
|
o.render = function(self, section, scope)
|
||||||
|
self.inputstyle = "add"
|
||||||
|
self.title = " "
|
||||||
|
self.inputtitle = translate("Connect")
|
||||||
|
Button.render(self, section, scope)
|
||||||
|
end
|
||||||
|
o.write = function(self, section)
|
||||||
|
local mode = m:formvalue("mode") or m:formvalue("cbid.OpenClawCLI.1.mode") or "configure"
|
||||||
|
if mode ~= "configure" and mode ~= "backup" and mode ~= "restore" then
|
||||||
|
mode = "configure"
|
||||||
|
end
|
||||||
|
start_ttyd(mode)
|
||||||
|
end
|
||||||
|
|
||||||
return m
|
return m
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<div class="cbi-map">
|
<div class="cbi-map">
|
||||||
|
<div id="oclm-cli-tip" style="margin: 0 0 10px 0;"></div>
|
||||||
<iframe id="terminal" style="width: 100%; min-height: 600px; border: none; border-radius: 3px;"></iframe>
|
<iframe id="terminal" style="width: 100%; min-height: 600px; border: none; border-radius: 3px;"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@@ -6,6 +7,22 @@
|
|||||||
for (let i = 0; i < el.length; i++) {
|
for (let i = 0; i < el.length; i++) {
|
||||||
el[i].style.display = "none";
|
el[i].style.display = "none";
|
||||||
}
|
}
|
||||||
document.getElementById("terminal").src = "http://" + window.location.hostname + ":7682";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
function ttydHost() {
|
||||||
|
const h = window.location.hostname || "127.0.0.1";
|
||||||
|
return (h.indexOf(":") >= 0 && h[0] !== "[") ? ("[" + h + "]") : h;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = "http://" + ttydHost() + ":7682";
|
||||||
|
const proto = (window.location.protocol || "http:").toLowerCase();
|
||||||
|
const tip = document.getElementById("oclm-cli-tip");
|
||||||
|
const iframe = document.getElementById("terminal");
|
||||||
|
|
||||||
|
if (proto === "https:") {
|
||||||
|
tip.innerHTML = '当前 LuCI 使用 HTTPS,浏览器通常会拦截 iframe 加载 HTTP 终端(混合内容)。请点击打开:<a target="_blank" rel="noopener noreferrer" href="' + url + '">' + url + '</a>';
|
||||||
|
iframe.style.display = "none";
|
||||||
|
} else {
|
||||||
|
tip.innerHTML = '终端地址:<a target="_blank" rel="noopener noreferrer" href="' + url + '">' + url + '</a>';
|
||||||
|
iframe.src = url;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -24,6 +24,14 @@ start_service() {
|
|||||||
|
|
||||||
[ "$enabled" -eq 1 ] || return 0
|
[ "$enabled" -eq 1 ] || return 0
|
||||||
[ -n "$base_dir" ] || return 1
|
[ -n "$base_dir" ] || return 1
|
||||||
|
if echo "$port" | grep -Eq '^[0-9]+$'; then
|
||||||
|
if [ "$port" -le 1024 ] 2>/dev/null; then
|
||||||
|
logger -t openclawmgr "Refusing to start: unsafe port ${port} (must be >1024)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
local node_bin="${base_dir}/node/bin/node"
|
local node_bin="${base_dir}/node/bin/node"
|
||||||
local global_dir="${base_dir}/global"
|
local global_dir="${base_dir}/global"
|
||||||
@@ -52,7 +60,8 @@ start_service() {
|
|||||||
# OpenClaw uses --force to kill conflicting processes, but requires either `lsof`
|
# OpenClaw uses --force to kill conflicting processes, but requires either `lsof`
|
||||||
# or `fuser`. iStoreOS images may not include them by default.
|
# or `fuser`. iStoreOS images may not include them by default.
|
||||||
local force_opt=""
|
local force_opt=""
|
||||||
if command -v lsof >/dev/null 2>&1 || command -v fuser >/dev/null 2>&1; then
|
# Safety: never allow --force on privileged ports (it could kill SSH/LuCI when ports collide).
|
||||||
|
if [ "$port" -gt 1024 ] 2>/dev/null && (command -v lsof >/dev/null 2>&1 || command -v fuser >/dev/null 2>&1); then
|
||||||
force_opt="--force"
|
force_opt="--force"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,27 @@ log_ts() { date "+%Y-%m-%d %H:%M:%S"; }
|
|||||||
|
|
||||||
uci_get() { uci -q get "${UCI_NS}.main.$1" 2>/dev/null || true; }
|
uci_get() { uci -q get "${UCI_NS}.main.$1" 2>/dev/null || true; }
|
||||||
|
|
||||||
|
validate_base_dir() {
|
||||||
|
local dir="$1"
|
||||||
|
dir="$(printf "%s" "$dir" | sed 's/[[:space:]]*$//')"
|
||||||
|
dir="${dir%/}"
|
||||||
|
[ -n "$dir" ] || return 1
|
||||||
|
case "$dir" in
|
||||||
|
/*) ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
[ "$dir" != "/" ] || return 1
|
||||||
|
case "$dir" in
|
||||||
|
*"/../"*|*"/.."|*"/./"*|*"/.") return 1 ;;
|
||||||
|
esac
|
||||||
|
case "$dir" in
|
||||||
|
/bin|/bin/*|/sbin|/sbin/*|/lib|/lib/*|/usr|/usr/*|/etc|/etc/*|/proc|/proc/*|/sys|/sys/*|/dev|/dev/*|/run|/run/*|/tmp|/tmp/*|/var|/var/*|/overlay|/overlay/*|/rom|/rom/*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
uci_get_list() {
|
uci_get_list() {
|
||||||
local key="$1"
|
local key="$1"
|
||||||
uci -q show "${UCI_NS}.main.${key}" 2>/dev/null | \
|
uci -q show "${UCI_NS}.main.${key}" 2>/dev/null | \
|
||||||
@@ -120,6 +141,7 @@ default_gateway() {
|
|||||||
|
|
||||||
ensure_dirs() {
|
ensure_dirs() {
|
||||||
mkdir -p "$BASE_DIR" "$NODE_DIR" "$GLOBAL_DIR" "$DATA_DIR/.openclaw/workspace" "$BASE_DIR/npm-cache" 2>/dev/null || true
|
mkdir -p "$BASE_DIR" "$NODE_DIR" "$GLOBAL_DIR" "$DATA_DIR/.openclaw/workspace" "$BASE_DIR/npm-cache" 2>/dev/null || true
|
||||||
|
printf "%s\n" "managed-by=openclawmgr" >"${BASE_DIR}/.openclawmgr.managed" 2>/dev/null || true
|
||||||
fix_data_permissions || true
|
fix_data_permissions || true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -917,6 +939,7 @@ install_openclaw() {
|
|||||||
fix_data_permissions || true
|
fix_data_permissions || true
|
||||||
# Do not auto-enable/start on first install; let user Save & Apply after choosing base_dir and settings.
|
# Do not auto-enable/start on first install; let user Save & Apply after choosing base_dir and settings.
|
||||||
if [ "${ENABLED:-0}" = "1" ]; then
|
if [ "${ENABLED:-0}" = "1" ]; then
|
||||||
|
ensure_safe_port_for_start || return 1
|
||||||
/etc/init.d/openclawmgr enable >/dev/null 2>&1 || true
|
/etc/init.d/openclawmgr enable >/dev/null 2>&1 || true
|
||||||
/etc/init.d/openclawmgr restart >/dev/null 2>&1 || true
|
/etc/init.d/openclawmgr restart >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
@@ -955,8 +978,15 @@ do_purge() {
|
|||||||
/etc/init.d/openclawmgr stop >/dev/null 2>&1 || true
|
/etc/init.d/openclawmgr stop >/dev/null 2>&1 || true
|
||||||
/etc/init.d/openclawmgr disable >/dev/null 2>&1 || true
|
/etc/init.d/openclawmgr disable >/dev/null 2>&1 || true
|
||||||
uci -q set "${UCI_NS}.main.enabled=0" && uci -q commit "$UCI_NS" || true
|
uci -q set "${UCI_NS}.main.enabled=0" && uci -q commit "$UCI_NS" || true
|
||||||
rm -rf "$BASE_DIR" 2>/dev/null || true
|
# Safety: only remove the whole base_dir when it looks like a dedicated OpenClawMgr directory.
|
||||||
write_installer_log "Base dir removed: $BASE_DIR"
|
# Otherwise, only remove managed subdirectories to avoid accidental system wipe.
|
||||||
|
if [ -f "${BASE_DIR}/.openclawmgr.managed" ] || [ "$(basename "$BASE_DIR" 2>/dev/null || echo "")" = "OpenClawMgr" ]; then
|
||||||
|
rm -rf "$BASE_DIR" 2>/dev/null || true
|
||||||
|
write_installer_log "Base dir removed: $BASE_DIR"
|
||||||
|
else
|
||||||
|
rm -rf "$NODE_DIR" "$GLOBAL_DIR" "$DATA_DIR" "$BASE_DIR/npm-cache" "${BASE_DIR}/.openclawmgr.managed" 2>/dev/null || true
|
||||||
|
write_installer_log "Base dir kept (safety). Removed: node/global/data/npm-cache under $BASE_DIR"
|
||||||
|
fi
|
||||||
write_installer_log "== purge done =="
|
write_installer_log "== purge done =="
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,6 +995,7 @@ do_start() {
|
|||||||
write_installer_log "== start begin =="
|
write_installer_log "== start begin =="
|
||||||
ensure_dirs
|
ensure_dirs
|
||||||
fix_data_permissions || true
|
fix_data_permissions || true
|
||||||
|
ensure_safe_port_for_start || exit 1
|
||||||
/etc/init.d/openclawmgr enable >/dev/null 2>&1 || true
|
/etc/init.d/openclawmgr enable >/dev/null 2>&1 || true
|
||||||
uci -q set "${UCI_NS}.main.enabled=1" && uci -q commit "$UCI_NS" || true
|
uci -q set "${UCI_NS}.main.enabled=1" && uci -q commit "$UCI_NS" || true
|
||||||
ensure_gateway_config || true
|
ensure_gateway_config || true
|
||||||
@@ -986,6 +1017,7 @@ do_restart() {
|
|||||||
write_installer_log "== restart begin =="
|
write_installer_log "== restart begin =="
|
||||||
ensure_dirs
|
ensure_dirs
|
||||||
fix_data_permissions || true
|
fix_data_permissions || true
|
||||||
|
ensure_safe_port_for_start || exit 1
|
||||||
ensure_gateway_config || true
|
ensure_gateway_config || true
|
||||||
/etc/init.d/openclawmgr restart >/dev/null 2>&1 || true
|
/etc/init.d/openclawmgr restart >/dev/null 2>&1 || true
|
||||||
write_installer_log "== restart done =="
|
write_installer_log "== restart done =="
|
||||||
@@ -1001,6 +1033,7 @@ do_restart() {
|
|||||||
local pid=""
|
local pid=""
|
||||||
pid="$(ubus call service list "{\"name\":\"openclawmgr\"}" 2>/dev/null | jsonfilter -e '$.openclawmgr.instances.gateway.pid' 2>/dev/null || true)"
|
pid="$(ubus call service list "{\"name\":\"openclawmgr\"}" 2>/dev/null | jsonfilter -e '$.openclawmgr.instances.gateway.pid' 2>/dev/null || true)"
|
||||||
if [ "${ENABLED:-0}" = "1" ]; then
|
if [ "${ENABLED:-0}" = "1" ]; then
|
||||||
|
ensure_safe_port_for_start || exit 1
|
||||||
/etc/init.d/openclawmgr enable >/dev/null 2>&1 || true
|
/etc/init.d/openclawmgr enable >/dev/null 2>&1 || true
|
||||||
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||||
/etc/init.d/openclawmgr restart >/dev/null 2>&1 || true
|
/etc/init.d/openclawmgr restart >/dev/null 2>&1 || true
|
||||||
@@ -1165,6 +1198,9 @@ NODE_VERSION="24.14.0"
|
|||||||
case "$PORT" in
|
case "$PORT" in
|
||||||
''|*[!0-9]*) PORT="18789" ;;
|
''|*[!0-9]*) PORT="18789" ;;
|
||||||
esac
|
esac
|
||||||
|
if [ "$PORT" -lt 1 ] 2>/dev/null || [ "$PORT" -gt 65535 ] 2>/dev/null; then
|
||||||
|
PORT="18789"
|
||||||
|
fi
|
||||||
case "$BIND" in
|
case "$BIND" in
|
||||||
loopback|lan|auto|tailnet|custom) ;;
|
loopback|lan|auto|tailnet|custom) ;;
|
||||||
*) BIND="lan" ;;
|
*) BIND="lan" ;;
|
||||||
@@ -1195,6 +1231,22 @@ require_base_dir() {
|
|||||||
write_installer_log "base_dir is not configured; please choose a data directory in LuCI and Save & Apply, then retry."
|
write_installer_log "base_dir is not configured; please choose a data directory in LuCI and Save & Apply, then retry."
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
if ! validate_base_dir "$BASE_DIR"; then
|
||||||
|
write_installer_log "Refusing to operate on unsafe base_dir: $BASE_DIR"
|
||||||
|
write_installer_log "Fix: choose a dedicated directory like /root/Configs/OpenClawMgr or /mnt/.../OpenClawMgr"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_safe_port_for_start() {
|
||||||
|
case "$PORT" in
|
||||||
|
''|*[!0-9]*) return 1 ;;
|
||||||
|
esac
|
||||||
|
if [ "$PORT" -le 1024 ] 2>/dev/null; then
|
||||||
|
write_installer_log "Refusing to start with unsafe port $PORT (must be >1024)."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_DIR="${BASE_DIR}/node"
|
NODE_DIR="${BASE_DIR}/node"
|
||||||
|
|||||||
@@ -1,217 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# ============================================================================
|
|
||||||
# OpenClawMgr CLI Config Helper (LAN ttyd)
|
|
||||||
# - Provides an interactive menu to run OpenClaw official configure wizard,
|
|
||||||
# manage config file, and restart gateway.
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Ensure a sensible TERM for ttyd/xterm.js.
|
|
||||||
export TERM="${TERM:-xterm-256color}"
|
|
||||||
|
|
||||||
ESC="$(printf '\033')"
|
|
||||||
RED="${ESC}[0;31m"; GREEN="${ESC}[0;32m"; YELLOW="${ESC}[1;33m"
|
|
||||||
CYAN="${ESC}[0;36m"; BOLD="${ESC}[1m"; DIM="${ESC}[2m"; NC="${ESC}[0m"
|
|
||||||
|
|
||||||
prompt_with_default() {
|
|
||||||
local prompt="$1" def="$2" var="$3" v=""
|
|
||||||
if [ -n "$def" ]; then
|
|
||||||
printf "%s%s[%s]%s: " "$prompt" "$DIM" "$def" "$NC"
|
|
||||||
else
|
|
||||||
printf "%s: " "$prompt"
|
|
||||||
fi
|
|
||||||
# shellcheck disable=SC2162
|
|
||||||
read v
|
|
||||||
[ -z "$v" ] && v="$def"
|
|
||||||
eval "$var=\$v"
|
|
||||||
}
|
|
||||||
|
|
||||||
pause_enter() {
|
|
||||||
printf "\n%s按回车继续...%s" "$DIM" "$NC"
|
|
||||||
# shellcheck disable=SC2162
|
|
||||||
read _
|
|
||||||
}
|
|
||||||
|
|
||||||
uci_get() { uci -q get "openclawmgr.main.$1" 2>/dev/null || true; }
|
|
||||||
|
|
||||||
BASE_DIR="${BASE_DIR:-$(uci_get base_dir)}"
|
|
||||||
if [ -z "$BASE_DIR" ]; then
|
|
||||||
printf '%s\n' "${RED}ERROR: base_dir 未配置。请先在 OpenClaw 启动器页面设置数据目录并保存应用。${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
NODE_DIR="${BASE_DIR}/node"
|
|
||||||
GLOBAL_DIR="${BASE_DIR}/global"
|
|
||||||
DATA_DIR="${BASE_DIR}/data"
|
|
||||||
|
|
||||||
NODE_BIN="${NODE_DIR}/bin/node"
|
|
||||||
OPENCLAW_BIN="${GLOBAL_DIR}/bin/openclaw"
|
|
||||||
CONFIG_FILE="${DATA_DIR}/.openclaw/openclaw.json"
|
|
||||||
BACKUP_DIR="${DATA_DIR}/.openclaw/backups"
|
|
||||||
|
|
||||||
export HOME="$DATA_DIR"
|
|
||||||
export OPENCLAW_HOME="$DATA_DIR"
|
|
||||||
export OPENCLAW_STATE_DIR="${DATA_DIR}/.openclaw"
|
|
||||||
export OPENCLAW_CONFIG_PATH="$CONFIG_FILE"
|
|
||||||
export PATH="${NODE_DIR}/bin:${GLOBAL_DIR}/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
||||||
|
|
||||||
find_entry() {
|
|
||||||
local d="${GLOBAL_DIR}/lib/node_modules/openclaw"
|
|
||||||
[ -f "${d}/openclaw.mjs" ] && { echo "${d}/openclaw.mjs"; return 0; }
|
|
||||||
[ -f "${d}/dist/cli.js" ] && { echo "${d}/dist/cli.js"; return 0; }
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
OPENCLAW_ENTRY="$(find_entry 2>/dev/null || true)"
|
|
||||||
|
|
||||||
oc_cmd() {
|
|
||||||
if [ -x "$OPENCLAW_BIN" ]; then
|
|
||||||
"$OPENCLAW_BIN" "$@"
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
if [ -x "$NODE_BIN" ] && [ -n "$OPENCLAW_ENTRY" ]; then
|
|
||||||
"$NODE_BIN" "$OPENCLAW_ENTRY" "$@"
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
printf '%s\n' "${RED}ERROR: OpenClaw 未安装或运行时不完整。${NC}"
|
|
||||||
printf '%s\n' "HINT: 在 OpenClaw 启动器页面执行安装/升级。"
|
|
||||||
return 127
|
|
||||||
}
|
|
||||||
|
|
||||||
restart_gateway() {
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' "${CYAN}=== 重启 Gateway ===${NC}"
|
|
||||||
/usr/libexec/istorec/openclawmgr.sh restart 2>&1 || true
|
|
||||||
}
|
|
||||||
|
|
||||||
apply_config() {
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' "${CYAN}=== 重新生成/应用配置(以 OpenClawMgr 配置为准)===${NC}"
|
|
||||||
/usr/libexec/istorec/openclawmgr.sh apply_config 2>&1 || true
|
|
||||||
}
|
|
||||||
|
|
||||||
show_config() {
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' "${CYAN}=== 配置文件 ===${NC}"
|
|
||||||
printf '%s\n' "路径: ${DIM}${CONFIG_FILE}${NC}"
|
|
||||||
printf '\n'
|
|
||||||
if [ ! -f "$CONFIG_FILE" ]; then
|
|
||||||
printf '%s\n' "${YELLOW}(配置文件不存在)${NC}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
if [ -x "$NODE_BIN" ]; then
|
|
||||||
"$NODE_BIN" -e "console.log(JSON.stringify(JSON.parse(require('fs').readFileSync(process.env.OPENCLAW_CONFIG_PATH,'utf8')),null,2))" 2>/dev/null \
|
|
||||||
|| cat "$CONFIG_FILE"
|
|
||||||
else
|
|
||||||
cat "$CONFIG_FILE"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
edit_config() {
|
|
||||||
if [ ! -f "$CONFIG_FILE" ]; then
|
|
||||||
printf '%s\n' "${YELLOW}配置文件不存在:${CONFIG_FILE}${NC}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
if command -v vi >/dev/null 2>&1; then
|
|
||||||
vi "$CONFIG_FILE"
|
|
||||||
elif command -v nano >/dev/null 2>&1; then
|
|
||||||
nano "$CONFIG_FILE"
|
|
||||||
else
|
|
||||||
printf '%s\n' "${YELLOW}未找到编辑器(vi/nano)。${NC}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
tail_logs() {
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' "${CYAN}=== 最近日志(logread -e openclaw)===${NC}"
|
|
||||||
printf '\n'
|
|
||||||
logread -e openclaw 2>/dev/null | tail -200 || printf '%s\n' "${YELLOW}(无法读取日志)${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
backup_config() {
|
|
||||||
mkdir -p "$BACKUP_DIR" 2>/dev/null || true
|
|
||||||
if [ ! -f "$CONFIG_FILE" ]; then
|
|
||||||
printf '%s\n' "${YELLOW}配置文件不存在,无法备份。${NC}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
local ts dst
|
|
||||||
ts="$(date -u +%Y-%m-%dT%H-%M-%SZ 2>/dev/null || date +%s)"
|
|
||||||
dst="${BACKUP_DIR}/${ts}-openclaw.json"
|
|
||||||
cp -f "$CONFIG_FILE" "$dst"
|
|
||||||
printf '%s\n' "${GREEN}✅ 已备份到: ${dst}${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
restore_config() {
|
|
||||||
mkdir -p "$BACKUP_DIR" 2>/dev/null || true
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' "${CYAN}=== 可用备份 ===${NC}"
|
|
||||||
ls -lt "$BACKUP_DIR"/*.json 2>/dev/null | head -10 | awk '{print " " $NF}' || printf '%s\n' "${YELLOW}(无备份)${NC}"
|
|
||||||
printf '\n'
|
|
||||||
local fp=""
|
|
||||||
prompt_with_default "请输入要恢复的备份文件路径" "" fp
|
|
||||||
[ -n "$fp" ] || return 0
|
|
||||||
if [ ! -f "$fp" ]; then
|
|
||||||
printf '%s\n' "${YELLOW}文件不存在:${fp}${NC}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
cp -f "$fp" "$CONFIG_FILE"
|
|
||||||
printf '%s\n' "${GREEN}✅ 已恢复配置到: ${CONFIG_FILE}${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
main_menu() {
|
|
||||||
while true; do
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' "${BOLD}OpenClaw AI Gateway — CLI 配置入口(OpenClawMgr)${NC}"
|
|
||||||
printf '%s\n' "${DIM}base_dir: ${BASE_DIR}${NC}"
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' " ${CYAN}1)${NC} 🧭 官方配置向导 ${DIM}(openclaw configure)${NC}"
|
|
||||||
printf '%s\n' " ${CYAN}2)${NC} ℹ️ OpenClaw 版本/帮助 ${DIM}(openclaw --version / --help)${NC}"
|
|
||||||
printf '%s\n' " ${CYAN}3)${NC} 📄 查看配置文件 ${DIM}(openclaw.json)${NC}"
|
|
||||||
printf '%s\n' " ${CYAN}4)${NC} ✍️ 编辑配置文件 ${DIM}(vi/nano)${NC}"
|
|
||||||
printf '%s\n' " ${CYAN}5)${NC} ♻️ 应用/重建配置 ${DIM}(openclawmgr.sh apply_config)${NC}"
|
|
||||||
printf '%s\n' " ${CYAN}6)${NC} 🔄 重启 Gateway ${DIM}(openclawmgr.sh restart)${NC}"
|
|
||||||
printf '%s\n' " ${CYAN}7)${NC} 📋 查看日志 ${DIM}(logread -e openclaw)${NC}"
|
|
||||||
printf '%s\n' " ${CYAN}8)${NC} 💾 备份配置"
|
|
||||||
printf '%s\n' " ${CYAN}9)${NC} 📥 恢复配置"
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' " ${CYAN}0)${NC} 退出"
|
|
||||||
printf '\n'
|
|
||||||
local c=""
|
|
||||||
prompt_with_default "请选择" "1" c
|
|
||||||
case "$c" in
|
|
||||||
1)
|
|
||||||
printf '\n'
|
|
||||||
printf '%s\n' "${CYAN}=== openclaw configure ===${NC}"
|
|
||||||
oc_cmd configure || true
|
|
||||||
pause_enter
|
|
||||||
;;
|
|
||||||
2)
|
|
||||||
printf '\n'
|
|
||||||
oc_cmd --version 2>/dev/null || true
|
|
||||||
printf '\n'
|
|
||||||
oc_cmd --help 2>/dev/null | sed -n '1,120p' || true
|
|
||||||
pause_enter
|
|
||||||
;;
|
|
||||||
3) show_config; pause_enter ;;
|
|
||||||
4) edit_config || true; pause_enter ;;
|
|
||||||
5) apply_config; pause_enter ;;
|
|
||||||
6) restart_gateway; pause_enter ;;
|
|
||||||
7) tail_logs; pause_enter ;;
|
|
||||||
8) backup_config || true; pause_enter ;;
|
|
||||||
9) restore_config || true; pause_enter ;;
|
|
||||||
0) printf '%s\n' "${GREEN}再见!${NC}"; exit 0 ;;
|
|
||||||
*) printf '%s\n' "${YELLOW}无效选择${NC}" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
case "${1:-}" in
|
|
||||||
--help|-h)
|
|
||||||
echo "Usage: oc-config.sh [--help]"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
main_menu
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# OpenClawMgr CLI Config Helper (for ttyd)
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
export TERM="${TERM:-xterm-256color}"
|
||||||
|
|
||||||
|
ESC="$(printf '\033')"
|
||||||
|
RED="${ESC}[0;31m"; GREEN="${ESC}[0;32m"; YELLOW="${ESC}[1;33m"
|
||||||
|
CYAN="${ESC}[0;36m"; BOLD="${ESC}[1m"; DIM="${ESC}[2m"; NC="${ESC}[0m"
|
||||||
|
|
||||||
|
err() { printf '%s\n' "${RED}ERROR: $*${NC}"; }
|
||||||
|
warn() { printf '%s\n' "${YELLOW}$*${NC}"; }
|
||||||
|
info() { printf '%s\n' "${CYAN}$*${NC}"; }
|
||||||
|
ok() { printf '%s\n' "${GREEN}$*${NC}"; }
|
||||||
|
|
||||||
|
pause_enter() {
|
||||||
|
printf "\n%s按回车继续...%s" "$DIM" "$NC"
|
||||||
|
IFS= read -r _ || true
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_default() {
|
||||||
|
local prompt="$1" def="${2:-}" v=""
|
||||||
|
if [ -n "$def" ]; then
|
||||||
|
printf "%s%s[%s]%s: " "$prompt" "$DIM" "$def" "$NC" >&2
|
||||||
|
else
|
||||||
|
printf "%s: " "$prompt" >&2
|
||||||
|
fi
|
||||||
|
IFS= read -r v || v=""
|
||||||
|
[ -z "$v" ] && v="$def"
|
||||||
|
printf "%s" "$v"
|
||||||
|
}
|
||||||
|
|
||||||
|
uci_get() { uci -q get "openclawmgr.main.$1" 2>/dev/null || true; }
|
||||||
|
|
||||||
|
init_paths() {
|
||||||
|
BASE_DIR="${BASE_DIR:-$(uci_get base_dir)}"
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
err "base_dir 未配置。请先在 OpenClaw 启动器页面设置数据目录并保存应用。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
NODE_DIR="${BASE_DIR}/node"
|
||||||
|
GLOBAL_DIR="${BASE_DIR}/global"
|
||||||
|
DATA_DIR="${BASE_DIR}/data"
|
||||||
|
|
||||||
|
NODE_BIN="${NODE_DIR}/bin/node"
|
||||||
|
OPENCLAW_BIN="${GLOBAL_DIR}/bin/openclaw"
|
||||||
|
CONFIG_FILE="${DATA_DIR}/.openclaw/openclaw.json"
|
||||||
|
BACKUP_DIR="${DATA_DIR}/.openclaw/backups"
|
||||||
|
|
||||||
|
export HOME="$DATA_DIR"
|
||||||
|
export OPENCLAW_HOME="$DATA_DIR"
|
||||||
|
export OPENCLAW_STATE_DIR="${DATA_DIR}/.openclaw"
|
||||||
|
export OPENCLAW_CONFIG_PATH="$CONFIG_FILE"
|
||||||
|
export PATH="${NODE_DIR}/bin:${GLOBAL_DIR}/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
find_openclaw_entry() {
|
||||||
|
local d="${GLOBAL_DIR}/lib/node_modules/openclaw"
|
||||||
|
[ -f "${d}/openclaw.mjs" ] && { echo "${d}/openclaw.mjs"; return 0; }
|
||||||
|
[ -f "${d}/dist/cli.js" ] && { echo "${d}/dist/cli.js"; return 0; }
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
openclaw_cmd() {
|
||||||
|
if [ -x "$OPENCLAW_BIN" ]; then
|
||||||
|
"$OPENCLAW_BIN" "$@"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
local entry=""
|
||||||
|
entry="$(find_openclaw_entry 2>/dev/null || true)"
|
||||||
|
if [ -x "$NODE_BIN" ] && [ -n "$entry" ]; then
|
||||||
|
"$NODE_BIN" "$entry" "$@"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
err "OpenClaw 未安装或运行时不完整。"
|
||||||
|
printf '%s\n' "HINT: 在 OpenClaw 启动器页面执行安装/升级。"
|
||||||
|
return 127
|
||||||
|
}
|
||||||
|
|
||||||
|
action_backup_config() {
|
||||||
|
mkdir -p "$BACKUP_DIR" 2>/dev/null || true
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
warn "配置文件不存在,无法备份。"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local ts dst
|
||||||
|
ts="$(date -u +%Y-%m-%dT%H-%M-%SZ 2>/dev/null || date +%s)"
|
||||||
|
dst="${BACKUP_DIR}/${ts}-openclaw.json"
|
||||||
|
cp -f "$CONFIG_FILE" "$dst"
|
||||||
|
ok "已备份到: ${dst}"
|
||||||
|
}
|
||||||
|
|
||||||
|
action_restore_config() {
|
||||||
|
mkdir -p "$BACKUP_DIR" 2>/dev/null || true
|
||||||
|
printf '\n'
|
||||||
|
info "=== 可用备份 ==="
|
||||||
|
ls -lt "$BACKUP_DIR"/*.json 2>/dev/null | head -10 | awk '{print " " $NF}' || warn "(无备份)"
|
||||||
|
printf '\n'
|
||||||
|
local fp=""
|
||||||
|
fp="$(prompt_default "请输入要恢复的备份文件路径" "")"
|
||||||
|
[ -n "$fp" ] || return 0
|
||||||
|
if [ ! -f "$fp" ]; then
|
||||||
|
warn "文件不存在:${fp}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
cp -f "$fp" "$CONFIG_FILE"
|
||||||
|
ok "已恢复配置到: ${CONFIG_FILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
action_configure() {
|
||||||
|
printf '\n'
|
||||||
|
info "=== openclaw configure ==="
|
||||||
|
openclaw_cmd configure || true
|
||||||
|
}
|
||||||
|
|
||||||
|
main_menu() {
|
||||||
|
while true; do
|
||||||
|
printf '\n'
|
||||||
|
printf '%s\n' "${BOLD}OpenClaw AI Gateway — CLI 配置入口(OpenClawMgr)${NC}"
|
||||||
|
printf '%s\n' "${DIM}base_dir: ${BASE_DIR}${NC}"
|
||||||
|
printf '\n'
|
||||||
|
printf '%s\n' " ${CYAN}1)${NC} 🧭 官方配置向导 ${DIM}(openclaw configure)${NC}"
|
||||||
|
printf '%s\n' " ${CYAN}2)${NC} 💾 备份配置"
|
||||||
|
printf '%s\n' " ${CYAN}3)${NC} 📥 恢复配置"
|
||||||
|
printf '\n'
|
||||||
|
printf '%s\n' " ${CYAN}0)${NC} 退出"
|
||||||
|
printf '\n'
|
||||||
|
|
||||||
|
local c=""
|
||||||
|
c="$(prompt_default "请选择" "1")"
|
||||||
|
case "$c" in
|
||||||
|
1) action_configure; pause_enter ;;
|
||||||
|
2) action_backup_config || true; pause_enter ;;
|
||||||
|
3) action_restore_config || true; pause_enter ;;
|
||||||
|
0) ok "再见!"; exit 0 ;;
|
||||||
|
*) warn "无效选择" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
init_paths
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
--help|-h)
|
||||||
|
echo "Usage: openclawmgr-cli.sh [configure|backup|restore]"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
case "${1:-menu}" in
|
||||||
|
menu) main_menu ;;
|
||||||
|
configure) action_configure; pause_enter ;;
|
||||||
|
backup) action_backup_config || true; pause_enter ;;
|
||||||
|
restore) action_restore_config || true; pause_enter ;;
|
||||||
|
*) echo "Unknown command: ${1:-}" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in new issue
Block a user