🎁 Sync 2026-03-23 17:00:57

This commit is contained in:
github-actions[bot] committed 2026-03-23 17:00:57 +08:00
1 parent 42786ced0b
commit 190ca87b14
42 files changed
+244 -2159

No files matched your search

+2 -2
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=0.1.5
PKG_RELEASE:=6
PKG_VERSION:=0.1.6
PKG_RELEASE:=7
LUCI_TITLE:=LuCI support for OpenClaw Launcher
LUCI_PKGARCH:=all
@@ -130,12 +130,14 @@
if (status.installing) return "安装中";
if (!status || !status.installed) return "未安装";
if (status.running) return "运行中";
if (status.reachable) return "运行中(未托管)";
return "已停止";
}
function statusDotClass(status) {
if (status && status.installing) return "";
if (status && status.running) return "is-success";
if (status && status.reachable) return "";
return "is-danger";
}
@@ -382,11 +384,8 @@
var savingAi = state.savingSection === "ai";
var savingAccess = state.savingSection === "access";
var serviceButtons = showServiceActions ? (
(!status.running ? '<button class="oclm-button" type="button" data-op="start">启动服务</button>' : '') +
(status.running ? '<button class="oclm-button" type="button" data-op="stop">停止服务</button>' : '') +
'<button class="oclm-button" type="button" data-op="restart">重启服务</button>'
) : '';
var canStartService = !status.running && !status.reachable;
var canStopService = !!status.running;
root.innerHTML =
'<style>' + styleText + '</style>' +
@@ -415,7 +414,7 @@
'<button id="oclm-install-btn" class="oclm-button oclm-button-primary" type="button" data-install-action="1"' + (status.installing ? ' disabled' : '') + '>' + installLabel + '</button>' +
'<label class="oclm-check"><input type="checkbox" id="oclm-install-accelerated"' + (installAcceleratedChecked ? ' checked' : '') + ' />Kspeeder 加速安装</label>' +
'</div>' +
'<button id="oclm-open-console" class="oclm-button oclm-button-primary' + (status.running ? '' : ' oclm-hidden') + '" type="button" data-open-console="1"' + (state.consoleReady ? '' : ' disabled') + '>' + openclawIcon("oclm-button-icon") + (state.consoleReady ? '打开控制台' : '控制台准备中…') + '</button>' +
'<button id="oclm-open-console" class="oclm-button oclm-button-primary' + ((status.running || status.reachable) ? '' : ' oclm-hidden') + '" type="button" data-open-console="1"' + (state.consoleReady ? '' : ' disabled') + '>' + openclawIcon("oclm-button-icon") + (state.consoleReady ? '打开控制台' : '控制台准备中…') + '</button>' +
'<button id="oclm-cancel-install" class="oclm-button oclm-button-danger' + (status.installing ? '' : ' oclm-hidden') + '" type="button" data-op="cancel_install">停止安装</button>' +
'</div>' +
'<div id="oclm-status-note" class="oclm-status-note' + (status.installing ? '' : ' oclm-hidden') + '">安装任务正在后台运行,点击“安装中”可继续查看日志。</div>' +
@@ -438,7 +437,15 @@
["auto", "自动"]
])) +
fieldInput("数据目录", '<input class="oclm-control" type="text" id="oclm-base-dir" list="oclm-base-dir-options" value="' + escapeAttr(form.base_dir || "") + '" /><datalist id="oclm-base-dir-options">' + baseDirOptions + '</datalist>') +
'<div class="oclm-section-submit"><button class="oclm-button oclm-button-primary" type="button" id="oclm-save-basic"' + (savingBasic ? ' disabled' : '') + '>' + (savingBasic ? '应用中…' : '保存并应用') + '</button>' + serviceButtons + (state.lastAppliedAt ? '<span class="oclm-applied-hint">已于 ' + escapeHtml(state.lastAppliedAt) + ' 更新配置</span>' : '') + '</div>' +
'<div class="oclm-section-submit">' +
'<button class="oclm-button oclm-button-primary" type="button" id="oclm-save-basic"' + (savingBasic ? ' disabled' : '') + '>' + (savingBasic ? '应用中…' : '保存并应用') + '</button>' +
'<span id="oclm-service-actions" class="oclm-inline-buttons' + (showServiceActions ? '' : ' oclm-hidden') + '">' +
'<button id="oclm-btn-start" class="oclm-button' + (canStartService ? '' : ' oclm-hidden') + '" type="button" data-op="start">启动服务</button>' +
'<button id="oclm-btn-stop" class="oclm-button' + (canStopService ? '' : ' oclm-hidden') + '" type="button" data-op="stop">停止服务</button>' +
'<button id="oclm-btn-restart" class="oclm-button" type="button" data-op="restart">重启服务</button>' +
'</span>' +
(state.lastAppliedAt ? '<span class="oclm-applied-hint">已于 ' + escapeHtml(state.lastAppliedAt) + ' 更新配置</span>' : '') +
'</div>' +
'</div></div>' +
'<div class="' + (activeTab === "ai" ? '' : 'oclm-hidden') + '">' +
@@ -485,6 +492,7 @@
function updateStatusDom(status) {
status = status || {};
var gatewayActive = !!(status.running || status.reachable);
function byId(id) {
if (root.getElementById) return root.getElementById(id);
@@ -565,15 +573,40 @@
else cancelInstallBtn.classList.add("oclm-hidden");
}
if (openConsoleBtn) {
if (status.running) openConsoleBtn.classList.remove("oclm-hidden");
if (gatewayActive) openConsoleBtn.classList.remove("oclm-hidden");
else openConsoleBtn.classList.add("oclm-hidden");
openConsoleBtn.disabled = !(status.running && state.consoleReady);
openConsoleBtn.disabled = !state.consoleReady;
openConsoleBtn.innerHTML = openclawIcon("oclm-button-icon") + (state.consoleReady ? "打开控制台" : "控制台准备中…");
}
if (noteEl) {
if (status.installing) noteEl.classList.remove("oclm-hidden");
else noteEl.classList.add("oclm-hidden");
}
var svcWrap = byId("oclm-service-actions");
var btnStart = byId("oclm-btn-start");
var btnStop = byId("oclm-btn-stop");
var btnRestart = byId("oclm-btn-restart");
var showServiceActions = !!(status.installed && !status.installing);
var canStartService = !!(!status.running && !status.reachable);
var canStopService = !!status.running;
if (svcWrap) {
if (showServiceActions) svcWrap.classList.remove("oclm-hidden");
else svcWrap.classList.add("oclm-hidden");
}
if (btnStart) {
if (showServiceActions && canStartService) btnStart.classList.remove("oclm-hidden");
else btnStart.classList.add("oclm-hidden");
}
if (btnStop) {
if (showServiceActions && canStopService) btnStop.classList.remove("oclm-hidden");
else btnStop.classList.add("oclm-hidden");
}
if (btnRestart) {
if (showServiceActions) btnRestart.classList.remove("oclm-hidden");
else btnRestart.classList.add("oclm-hidden");
}
}
function fieldInput(label, control) {
@@ -954,7 +987,7 @@
function refreshStatus(done) {
request(config.statusUrl).then(function(data) {
state.status = data || {};
if (state.status && state.status.running) {
if (state.status && (state.status.running || state.status.reachable)) {
if (!state.consoleReady) {
pollConsoleReady(20);
}
@@ -353,6 +353,46 @@ local function get_pid_uptime_human(pid)
return fmt_elapsed(elapsed)
end
local function probe_gateway_ready(port, base_dir, bind)
local sys = require "luci.sys"
local util = require "luci.util"
if not port or not tostring(port):match("^%d+$") then
return false, "", ""
end
if not base_dir or base_dir == "" then
return false, "", ""
end
local base_path = configured_base_path(base_dir)
local candidates = {}
if bind == "lan" or bind == "auto" then
local ip = lan_ipv4()
if ip ~= "" then
candidates[#candidates + 1] = ip
end
end
candidates[#candidates + 1] = "127.0.0.1"
local last_code, last_url = "", ""
for _, host in ipairs(candidates) do
local url = "http://" .. host .. ":" .. port .. base_path
last_url = url
local cmd = string.format(
"curl -fsS -o /dev/null --connect-timeout 1 --max-time 2 -w '%%{http_code}' %s 2>/dev/null",
util.shellquote(url)
)
local code = sys.exec(cmd):gsub("%s+$", "")
last_code = code
local n = tonumber(code) or 0
if n >= 200 and n < 400 then
return true, code, url
end
end
return false, last_code, last_url
end
function action_status()
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
@@ -373,6 +413,11 @@ function action_status()
running = (st == "running")
installed = (st == "running" or st == "stopped")
end
local reachable, reachable_code, reachable_url = false, "", ""
if installed and not running then
reachable, reachable_code, reachable_url = probe_gateway_ready(port, base_dir, bind)
end
local lock_running, lock_pid = installer_lock_running()
local installing = (task.running and (task.op == "install" or task.op == "upgrade")) or lock_running
if not installing and task.running and not installed and not running then
@@ -405,6 +450,9 @@ function action_status()
enabled = enabled,
installed = installed,
running = running,
reachable = reachable,
reachable_http_code = reachable_code,
reachable_url = reachable_url,
task_running = task.running,
task_op = task.op,
installing = installing,
@@ -423,11 +471,10 @@ function action_status()
end
function action_ready()
local sys = require "luci.sys"
local util = require "luci.util"
local uci = require "luci.model.uci".cursor()
local port = uci:get("openclawmgr", "main", "port") or "18789"
local bind = uci:get("openclawmgr", "main", "bind") or "lan"
local base_dir = uci:get("openclawmgr", "main", "base_dir") or ""
if not port:match("^%d+$") then port = "18789" end
@@ -437,20 +484,13 @@ function action_ready()
return
end
local base_url = "http://127.0.0.1:" .. port .. configured_base_path(base_dir)
local cmd = string.format(
"curl -fsS -o /dev/null --connect-timeout 1 --max-time 2 -w '%%{http_code}' %s 2>/dev/null",
util.shellquote(base_url)
)
local code = sys.exec(cmd):gsub("%s+$", "")
local n = tonumber(code) or 0
local ready = (n >= 200 and n < 400)
local ready, code, url = probe_gateway_ready(port, base_dir, bind)
write_json({
ok = true,
ready = ready,
http_code = code,
url = base_url,
url = url,
})
end
@@ -559,15 +559,14 @@ if selected_env_key and selected_api_key ~= "" then
end
cfg.env = next(env) and env or nil
local models = {}
models.mode = "merge"
local providers = {}
models.providers = providers
cfg.models = models
local models = ensure_table(cfg, "models")
if type(models.mode) ~= "string" or models.mode == "" then
models.mode = "merge"
end
local providers = ensure_table(models, "providers")
local current_provider_id = os.getenv("DEFAULT_AGENT_NAME") or "anthropic"
local current_provider = {}
providers[current_provider_id] = current_provider
local current_provider_id = os.getenv("DEFAULT_AGENT_NAME") or "anthropic"
local current_provider = ensure_table(providers, current_provider_id)
if current_provider_id == "openai" then
current_provider.api = "openai-completions"
@@ -600,13 +599,30 @@ elseif current_provider_id == "moonshot" then
}
end
local override_base = os.getenv("DEFAULT_AGENT_OVERRIDE_BASE_URL") or ""
local base_url_mode = os.getenv("DEFAULT_AGENT_BASE_URL_MODE") or "default"
if base_url_mode == "override" and override_base ~= "" then
current_provider.baseUrl = override_base
elseif base_url_mode == "default" then
current_provider.baseUrl = os.getenv("DEFAULT_AGENT_BASE_URL") or current_provider.baseUrl
end
local override_base = os.getenv("DEFAULT_AGENT_OVERRIDE_BASE_URL") or ""
local base_url_mode = os.getenv("DEFAULT_AGENT_BASE_URL_MODE") or "default"
if base_url_mode == "override" and override_base ~= "" then
current_provider.baseUrl = override_base
elseif base_url_mode == "default" then
current_provider.baseUrl = os.getenv("DEFAULT_AGENT_BASE_URL") or current_provider.baseUrl
end
-- luci.jsonc encodes empty Lua tables as JSON arrays ([]). To avoid producing
-- invalid provider entries like {"deepseek/deepseek-chat":[]}, drop provider
-- records that are empty or look like a provider/model string.
do
local to_del = {}
for k, v in pairs(providers) do
if type(k) == "string" and k:find("/", 1, true) then
table.insert(to_del, k)
elseif type(v) == "table" and next(v) == nil then
table.insert(to_del, k)
end
end
for _, k in ipairs(to_del) do
providers[k] = nil
end
end
local agents = ensure_table(cfg, "agents")
local defaults = ensure_table(agents, "defaults")
@@ -0,0 +1,6 @@
#!/bin/sh
# Backward-compatible wrapper. New entry: openclawmgr-cli.sh
set -eu
exec /usr/share/openclawmgr/openclawmgr-cli.sh "$@"