💐 Sync 2026-07-09 02:25:04

This commit is contained in:
github-actions[bot] committed 2026-07-09 02:25:04 +08:00
1 parent 9cc2ec0f09
commit 3d0541ac96
37 files changed
+597 -329

No files matched your search

+1 -1
View File
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=daed
PKG_VERSION:=2026.07.05
PKG_RELEASE:=19
PKG_RELEASE:=20
PKG_SOURCE:=daed-src-2026.07.05-23793fb2bd76.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
+7
View File
@@ -46,6 +46,13 @@ stop_service() {
daed_cleanup_runtime
}
restart() {
stop
sleep 1
daed_cleanup_runtime
start
}
service_triggers() {
procd_add_reload_trigger "$CONF"
}
@@ -0,0 +1,48 @@
From: kenzok8 <kenzok8@noreply>
Date: 2026-07-08
Subject: [PATCH] daed: restart process when dashboard reload hangs
Dashboard "Apply" can leave dae's runtime half-reloaded on some OpenWrt
netns/veth setups. Bound the GraphQL reload request and let procd respawn daed
so daed-guard can clean stale runtime state before starting again.
Reported: https://github.com/kenzok8/openwrt-daede/issues/31
---
--- a/graphql/service/config/mutation_utils.go
+++ b/graphql/service/config/mutation_utils.go
@@ -11,0 +12 @@
+ "os"
@@ -16,0 +18 @@
+ "time"
@@ -260,0 +263,9 @@
+const reloadWaitTimeout = 75 * time.Second
+
+func restartAfterReloadTimeout() {
+ go func() {
+ time.Sleep(time.Second)
+ os.Exit(1)
+ }()
+}
+
@@ -458 +469 @@
- dae.ChReloadConfigs <- &dae.ReloadMessage{
+ reloadMsg := &dae.ReloadMessage{
@@ -462,3 +473,5 @@
- errReload := <-chReloadCallback
- if errReload != nil {
- return 0, fmt.Errorf("failed to load new config: %w; see more in log", errReload)
+ select {
+ case dae.ChReloadConfigs <- reloadMsg:
+ case <-time.After(reloadWaitTimeout):
+ restartAfterReloadTimeout()
+ return 0, fmt.Errorf("failed to submit reload within %s; restarting daed to recover runtime", reloadWaitTimeout)
@@ -465,0 +479,9 @@
+ select {
+ case errReload := <-chReloadCallback:
+ if errReload != nil {
+ return 0, fmt.Errorf("failed to load new config: %w; see more in log", errReload)
+ }
+ case <-time.After(reloadWaitTimeout):
+ restartAfterReloadTimeout()
+ return 0, fmt.Errorf("reload timed out after %s; restarting daed to recover runtime", reloadWaitTimeout)
+ }
+1 -1
View File
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-daede
PKG_VERSION:=1.14.7
PKG_RELEASE:=30
PKG_RELEASE:=31
PKG_MAINTAINER:=kenzok8
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
@@ -4,9 +4,22 @@
'require baseclass';
'require form';
'require fs';
'require ui';
'require uci';
'require view.daede.widgets as widgets';
function applyUciChanges() {
return uci.save().then(function() {
return uci.changes().then(function(ch) {
return (ch && Object.keys(ch).length) ? uci.apply() : null;
});
}).then(function() {
return new Promise(function(resolve) { window.setTimeout(resolve, 1800); });
}).then(function() {
return ui.changes.init();
});
}
function renderDaedSettings() {
let m, s, o;
m = new form.Map('daed', null, null);
@@ -96,12 +109,7 @@ function renderDaedSettings() {
ev.preventDefault();
save.disabled = true;
m.save(null, true)
.then(function() { return uci.save(); })
.then(function() {
return uci.changes().then(function(ch) {
return (ch && Object.keys(ch).length) ? uci.apply() : null;
});
})
.then(applyUciChanges)
.then(function() {
const enabled = uci.get('daed', 'config', 'subscribe_auto_update') === '1';
return fs.exec('/usr/share/luci-app-daede/daed-sub-cron.sh', [ enabled ? 'enable' : 'disable' ]);
@@ -58,9 +58,6 @@ const CSS = [
'.dd-geo-row input[type=text],.dd-geo-row select{font-size:12px;padding:4px 8px;border:1px solid rgba(128,128,128,.35);border-radius:5px;background:transparent;color:inherit;width:100%}',
/* selects cap at 200px on wide screens, shrink to the column on mobile (no overflow) */
'.dd-geo-row select{width:100%!important;max-width:200px;box-sizing:border-box}',
'.dd-geo-auto{display:inline-flex;align-items:center;gap:6px}',
/* Argon shifts label>checkbox down (top:.4rem); reset so flex centers it */
'.dd-geo-auto input[type="checkbox"]{position:static;top:auto;right:auto;margin:0}',
'.dd-geo-actions{margin-top:10px;display:flex;gap:10px;align-items:center}',
'.dd-up-log{margin-top:10px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:11px;padding:10px;border:1px solid rgba(128,128,128,.14);border-radius:8px;max-height:200px;overflow:auto;white-space:pre-wrap;word-break:break-all;display:none;background:inherit;color:#4a8c63}',
'.dd-up-log.show{display:block}',
@@ -436,24 +433,25 @@ return view.extend({
presetSel.addEventListener('change', syncCustom);
syncCustom();
const autoCb = E('input', { 'type': 'checkbox' });
autoCb.checked = auto0;
const freqSel = E('select', {}, [
const autoSel = E('select', {}, [
E('option', { 'value': 'off' }, _('Disabled')),
E('option', { 'value': 'daily' }, _('Daily')),
E('option', { 'value': 'weekly' }, _('Weekly'))
]);
freqSel.value = freq0;
autoSel.value = auto0 ? freq0 : 'off';
const saveBtn = E('button', { 'class': 'dd-up-btn dd-up-btn-primary' }, _('Save'));
saveBtn.addEventListener('click', function() {
const p = presetSel.value;
let gi = '', gs = '';
const geoAuto = autoSel.value !== 'off';
const geoFreq = autoSel.value === 'weekly' ? 'weekly' : 'daily';
if (p === 'v2fly') { gi = GEO_PRESETS.v2fly.geoip; gs = GEO_PRESETS.v2fly.geosite; }
else if (p === 'custom') { gi = giInput.value.trim(); gs = gsInput.value.trim(); }
uci.set('daede', 'config', 'geoip_url', gi);
uci.set('daede', 'config', 'geosite_url', gs);
uci.set('daede', 'config', 'geo_auto', autoCb.checked ? '1' : '0');
uci.set('daede', 'config', 'geo_auto_freq', freqSel.value);
uci.set('daede', 'config', 'geo_auto', geoAuto ? '1' : '0');
uci.set('daede', 'config', 'geo_auto_freq', geoFreq);
const orig = saveBtn.textContent;
saveBtn.disabled = true; saveBtn.textContent = '...';
uci.save().then(function() {
@@ -463,7 +461,7 @@ return view.extend({
if (changes && Object.keys(changes).length)
return uci.apply();
}).then(function() {
return fs.exec('/usr/share/luci-app-daede/geo-cron.sh', [autoCb.checked ? 'enable' : 'disable']);
return fs.exec('/usr/share/luci-app-daede/geo-cron.sh', [geoAuto ? 'enable' : 'disable']);
}).then(function() {
logPane.textContent = _('Geo data source saved.');
logPane.classList.add('show');
@@ -485,8 +483,8 @@ return view.extend({
E('div', { 'class': 'dd-geo-row' }, [ E('label', {}, _('Source')), presetSel ]),
customRows,
E('div', { 'class': 'dd-geo-row' }, [
E('label', { 'class': 'dd-geo-auto' }, [ E('span', {}, _('Auto-update')), autoCb ]),
freqSel
E('label', {}, _('Auto-update')),
autoSel
]),
E('div', { 'class': 'dd-geo-actions' }, [ saveBtn ])
])
@@ -22,6 +22,11 @@ function execInit(be, action) {
}).catch(function() {});
}
function restartService(be) {
return execChecked(be.initd, ['restart'])
.then(function() { return waitForService(be, true, 80); });
}
function rejectIfOtherRunning(be, running) {
const other = be.name === 'dae' ? 'daed' : 'dae';
if (!running || !running[other])
@@ -231,8 +236,21 @@ function renderStatusCard(ctx, listenAddr) {
const restart = E('button', { 'class': 'cbi-button cbi-button-positive' }, _('Restart'));
restart.addEventListener('click', function(ev) {
ev.preventDefault();
if (busy) return;
busy = true;
restart.disabled = true;
execInit(be, 'restart').finally(function() { restart.disabled = false; });
lastError = '';
swErr.style.display = 'none';
restartService(be)
.then(function() {
busy = false;
return refresh(true);
})
.catch(function(e) {
busy = false;
lastError = _('Restart failed: %s').format(e.message || e);
return refresh(true);
});
});
actions.push(restart);
}
+3
View File
@@ -836,6 +836,9 @@ msgstr ""
msgid "Auto-update"
msgstr ""
msgid "Disabled"
msgstr ""
msgid "Daily"
msgstr ""
+3
View File
@@ -1025,6 +1025,9 @@ msgstr "自定义"
msgid "Auto-update"
msgstr "自动更新"
msgid "Disabled"
msgstr "关闭"
msgid "Daily"
msgstr "每天"
+3
View File
@@ -1025,6 +1025,9 @@ msgstr "自定义"
msgid "Auto-update"
msgstr "自动更新"
msgid "Disabled"
msgstr "关闭"
msgid "Daily"
msgstr "每天"
+2
View File
@@ -13,6 +13,8 @@
- 兼容没有 `su` / `runuser` 的 OpenWrt 固件,微信安装、升级、登录和下线改用 `start-stop-daemon` 兜底以 `openclaw` 用户执行。
- 微信安装网络探测优先使用 `curl`,避免低内存/精简 musl 固件上 Node `fetch` 触发 undici Wasm OOM。
- 微信官方 CLI 安装触发 undici Wasm OOM 时,自动改用 npm 直装到 OpenClaw `npm/projects` 并继续注册插件配置。
- 微信插件安装/升级不再预先停止 Gateway,避免反复安装触发 procd crash-loop。
- LuCI 状态页和 `status_service` 增强 procd 状态识别,能区分真实启动中、stale pidfile 和 crash-loop 抑制,不再误显示“正在启动”。
- 微信登录前补充 Node、python3、插件目录、账号状态目录和配置写权限检查,并清理残留登录进程与旧二维码状态。
- 登录失败时在 LuCI 页面展示真实日志详情,不再只显示“登录失败”。
- 二维码链接提取更稳,页面明确提示“点击链接后用微信扫码”。
+16 -1
View File
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-openclaw
PKG_VERSION:=$(strip $(shell cat $(CURDIR)/VERSION 2>/dev/null || echo "1.0.0"))
PKG_RELEASE:=20
PKG_RELEASE:=21
PKG_MAINTAINER:=10000ge10000 <10000ge10000@users.noreply.github.com>
PKG_LICENSE:=GPL-3.0
@@ -85,6 +85,21 @@ define Package/$(PKG_NAME)/postinst
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || {
( . /etc/uci-defaults/99-openclaw ) && rm -f /etc/uci-defaults/99-openclaw
OPENCLAW_INSTALL_BASE="$$(uci -q get openclaw.main.install_path 2>/dev/null || echo /opt)"
if [ -r /usr/libexec/openclaw-paths.sh ]; then
. /usr/libexec/openclaw-paths.sh
oc_load_paths "$${OPENCLAW_INSTALL_BASE}" 2>/dev/null || true
else
OPENCLAW_INSTALL_BASE="$${OPENCLAW_INSTALL_BASE%/}"
OC_DATA="$${OPENCLAW_INSTALL_BASE}/openclaw/data"
fi
if [ -n "$${OC_DATA:-}" ] && [ -d "$${OC_DATA}/.openclaw" ] && [ -x /usr/libexec/openclaw-permissions.sh ]; then
/usr/libexec/openclaw-permissions.sh fix-state "$${OC_DATA}/.openclaw" >/dev/null 2>&1 || true
fi
if [ "$$(uci -q get openclaw.main.enabled 2>/dev/null || echo 0)" = "1" ] && [ -x /etc/init.d/openclaw ]; then
/etc/init.d/openclaw enable >/dev/null 2>&1 || true
/etc/init.d/openclaw start >/dev/null 2>&1 || true
fi
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null
exit 0
}
@@ -191,6 +191,7 @@ fs.writeFileSync(configPath, JSON.stringify(d, null, 2) + '\n');
"REG_RC=$?; " ..
"if [ $REG_RC -eq 0 ]; then " ..
"chown openclaw:openclaw " .. shellquote(config_file) .. " 2>/dev/null; " ..
"[ -x /usr/libexec/openclaw-permissions.sh ] && /usr/libexec/openclaw-permissions.sh fix-state " .. shellquote(oc_data .. "/.openclaw") .. " >/dev/null 2>&1; " ..
"echo 'Registered openclaw-weixin npm plugin in OpenClaw config.' >> " .. shellquote(log_file) .. "; " ..
"else RC=$REG_RC; echo $RC > " .. shellquote(exit_file) .. "; echo 'Failed to register openclaw-weixin npm plugin in OpenClaw config.' >> " .. shellquote(log_file) .. "; fi; " ..
"else RC=127; echo $RC > " .. shellquote(exit_file) .. "; echo 'Node.js not found, cannot register openclaw-weixin plugin.' >> " .. shellquote(log_file) .. "; fi; " ..
@@ -238,7 +239,13 @@ const timer = setTimeout(() => controller.abort(new Error('timeout')), 10000);
end
local function openclaw_user_runner_cmd()
return "_oc_as_openclaw() { " ..
return "_oc_raise_openclaw_limits() { " ..
"ulimit -v unlimited 2>/dev/null || true; " ..
"ulimit -m unlimited 2>/dev/null || true; " ..
"ulimit -d unlimited 2>/dev/null || true; " ..
"}; " ..
"_oc_as_openclaw() { " ..
"_oc_raise_openclaw_limits; " ..
"if command -v su >/dev/null 2>&1; then su -s /bin/sh openclaw -c \"$1\"; " ..
"elif command -v runuser >/dev/null 2>&1; then runuser -u openclaw -- sh -c \"$1\"; " ..
"elif command -v start-stop-daemon >/dev/null 2>&1; then _oc_pid=/tmp/openclaw-user-$$.pid; _oc_cwd=$(pwd); rm -f \"$_oc_pid\"; start-stop-daemon -S -m -p \"$_oc_pid\" -c openclaw:openclaw -d \"$_oc_cwd\" -x /bin/sh -- -c \"$1\"; _oc_rc=$?; rm -f \"$_oc_pid\"; return $_oc_rc; " ..
@@ -267,6 +274,25 @@ local function wechat_npm_fallback_install_cmd(install_path, log_file, exit_file
"fi; "
end
local function wechat_npm_direct_install_cmd(install_path, log_file, exit_file)
local package_json = "{\n \"private\": true,\n \"dependencies\": {\n \"@tencent-weixin/openclaw-weixin\": \"latest\"\n }\n}\n"
return "echo '使用 npm 直装微信插件,避免官方安装器进入扫码等待...' >> " .. shellquote(log_file) .. "; " ..
"OC_WECHAT_NPM_PROJECT=\"$OC_WECHAT_DATA/.openclaw/npm/projects/tencent-weixin-openclaw-weixin-7783ac86ba\"; " ..
"mkdir -p \"$OC_WECHAT_NPM_PROJECT\" \"$OC_WECHAT_DATA/.npm\" \"$OC_WECHAT_DATA/.tmp\" \"$OC_WECHAT_DATA/.openclaw/npm/projects\"; " ..
"chown -R openclaw:openclaw \"$OC_WECHAT_NPM_PROJECT\" \"$OC_WECHAT_DATA/.npm\" \"$OC_WECHAT_DATA/.tmp\" \"$OC_WECHAT_DATA/.openclaw/npm/projects\" 2>/dev/null; " ..
"cat > \"$OC_WECHAT_NPM_PROJECT/package.json\" <<'OC_WECHAT_PACKAGE_JSON'\n" ..
package_json ..
"OC_WECHAT_PACKAGE_JSON\n" ..
"chown openclaw:openclaw \"$OC_WECHAT_NPM_PROJECT/package.json\" 2>/dev/null; " ..
"cd \"$OC_WECHAT_NPM_PROJECT\" && " ..
"_oc_as_openclaw 'HOME=$OC_WECHAT_DATA OPENCLAW_HOME=$OC_WECHAT_DATA OPENCLAW_STATE_DIR=$OC_WECHAT_DATA/.openclaw OPENCLAW_CONFIG_PATH=$OC_WECHAT_DATA/.openclaw/openclaw.json " ..
"NODE_ICU_DATA=" .. install_path .. "/node/share/icu NPM_CONFIG_CACHE=$OC_WECHAT_DATA/.npm npm_config_cache=$OC_WECHAT_DATA/.npm TMPDIR=$OC_WECHAT_DATA/.tmp " ..
"PATH=" .. install_path .. "/node/bin:" .. install_path .. "/global/bin:$PATH " ..
"npm install --omit=dev --omit=peer --legacy-peer-deps --no-audit --no-fund --loglevel=error' >> " .. shellquote(log_file) .. " 2>&1; " ..
"RC=$?; echo $RC > " .. shellquote(exit_file) .. "; " ..
"if [ $RC -eq 0 ]; then echo '✅ npm 直装微信插件完成' >> " .. shellquote(log_file) .. "; else echo '❌ npm 直装微信插件失败 (exit: '$RC')' >> " .. shellquote(log_file) .. "; fi; "
end
local function wechat_tail_detail(text, max_lines)
if not text or text == "" then
return ""
@@ -524,21 +550,36 @@ function action_status()
local gw_check = sys.exec(gw_check_cmd):gsub("%s+", "")
result.gateway_running = (tonumber(gw_check) or 0) > 0
-- 如果端口未监听但 procd 进程存在,说明正在启动中 (gateway 初始化需要数分钟)
-- 如果端口未监听,结合 procd 与真实进程判断状态。
-- 不能只看 pid 字段或 pidfile:procd crash-loop / stale pidfile 会让 LuCI 误显示“正在启动”。
if not result.gateway_running and enabled == "1" then
local procd_pid = sys.exec("ubus call service list '{\"name\":\"openclaw\"}' 2>/dev/null | jsonfilter -e '$.openclaw.instances.gateway.pid' 2>/dev/null"):gsub("%s+", "")
local procd_running = sys.exec("ubus call service list '{\"name\":\"openclaw\"}' 2>/dev/null | jsonfilter -e '$.openclaw.instances.gateway.running' 2>/dev/null"):gsub("%s+", "")
local procd_exit = sys.exec("ubus call service list '{\"name\":\"openclaw\"}' 2>/dev/null | jsonfilter -e '$.openclaw.instances.gateway.exit_code' 2>/dev/null"):gsub("%s+", "")
if procd_pid == "null" or not procd_pid:match("^%d+$") then procd_pid = "" end
if procd_exit == "null" then procd_exit = "" end
result.gateway_exit_code = procd_exit
local procd_pid_alive = false
if procd_pid ~= "" then
procd_pid_alive = (sys.exec("[ -d /proc/" .. procd_pid .. " ] && echo 1 || echo 0"):gsub("%s+", "") == "1")
end
local pidfile_pid = sys.exec("cat /var/run/openclaw.pid 2>/dev/null || true"):gsub("%s+", "")
local pidfile_stale = false
if pidfile_pid ~= "" and pidfile_pid:match("^%d+$") then
pidfile_stale = (sys.exec("[ -d /proc/" .. pidfile_pid .. " ] && echo 0 || echo 1"):gsub("%s+", "") == "1")
end
local crash_loop = sys.exec("logread 2>/dev/null | grep -E 'Instance openclaw::gateway.*crash loop' | tail -1"):gsub("^%s+", ""):gsub("%s+$", "")
if procd_exit ~= "" and tonumber(procd_exit) and tonumber(procd_exit) ~= 0 and procd_running ~= "true" then
result.gateway_failed = true
elseif procd_pid ~= "" or procd_running == "true" then
elseif crash_loop ~= "" and pidfile_stale and procd_running ~= "true" and not procd_pid_alive then
result.gateway_failed = true
result.gateway_crash_loop = true
if result.gateway_exit_code == "" then result.gateway_exit_code = "crash-loop" end
elseif procd_running == "true" or procd_pid_alive then
result.gateway_starting = true
else
local fallback_pid = sys.exec("pgrep -f 'openclaw.*gateway' 2>/dev/null | head -1"):gsub("%s+", "")
if fallback_pid ~= "" then
result.gateway_starting = true
end
result.pid = procd_pid
end
end
@@ -637,10 +678,14 @@ function action_service_ctl()
-- stop 后额外等待确保端口释放
sys.exec("sleep 2")
elseif action == "restart" then
-- 先完整 stop (确保端口释放),再后台 start
sys.exec("/etc/init.d/openclaw stop >/dev/null 2>&1")
sys.exec("sleep 2")
sys.exec("/etc/init.d/openclaw start >/dev/null 2>&1 &")
-- 常规“重启”只重启 Gateway,不重启 Web PTY,避免 stop+start 带来的长时间等待。
-- 如果 procd 没有 gateway 实例,再回退到 start。
local procd_running = sys.exec("ubus call service list '{\"name\":\"openclaw\"}' 2>/dev/null | jsonfilter -e '$.openclaw.instances.gateway.running' 2>/dev/null"):gsub("%s+", "")
if procd_running == "true" then
sys.exec("/etc/init.d/openclaw restart_gateway >/dev/null 2>&1 &")
else
sys.exec("/etc/init.d/openclaw start >/dev/null 2>&1 &")
end
elseif action == "enable" then
sys.exec("/etc/init.d/openclaw enable 2>/dev/null")
elseif action == "disable" then
@@ -1549,10 +1594,7 @@ function action_wechat_install()
return
end
-- 后台执行安装
-- 在启动安装前,确保网关端口可用(自动清理残留 gateway 进程)
local port = uci:get("openclaw", "main", "port") or "18789"
ensure_port_free(port)
-- 后台执行安装。注意:插件安装不需要释放 Gateway 端口,避免误停正在运行的 Gateway 触发 procd crash-loop。
-- 微信插件安装目录路径 (用于安装后权限修复)
local extensions_dir = install_path .. "/data/.openclaw/extensions"
local install_cmd = string.format(
@@ -1569,13 +1611,7 @@ function action_wechat_install()
"else mkdir -p \"$OC_WECHAT_DATA/.npm\" \"$OC_WECHAT_DATA/.tmp\" \"$OC_WECHAT_DATA/.openclaw/extensions\"; chown -R openclaw:openclaw \"$OC_WECHAT_DATA/.npm\" \"$OC_WECHAT_DATA/.tmp\" 2>/dev/null; chown openclaw:openclaw \"$OC_WECHAT_DATA/.openclaw\" 2>/dev/null; fi; " ..
"if [ ! -w %s/.npm ] || [ ! -w %s/.tmp ]; then echo '❌ npm cache/tmp 目录不可写' >> /tmp/openclaw-wechat-install.log; echo 1 > /tmp/openclaw-wechat-install.exit; exit 0; fi; " ..
"_oc_as_openclaw 'test -w %s/.npm && test -w %s/.tmp && test -w %s/.openclaw' || { echo '❌ openclaw 用户无法写入 npm cache/tmp/data 目录' >> /tmp/openclaw-wechat-install.log; echo 1 > /tmp/openclaw-wechat-install.exit; exit 0; }; " ..
"cd %s && " ..
"_oc_as_openclaw 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw " ..
"OPENCLAW_CONFIG_PATH=%s/.openclaw/openclaw.json NPM_CONFIG_CACHE=%s/.npm npm_config_cache=%s/.npm TMPDIR=%s/.tmp " ..
"PATH=%s/node/bin:%s/global/bin:$PATH " ..
"%s -y @tencent-weixin/openclaw-weixin-cli install' >> /tmp/openclaw-wechat-install.log 2>&1; " ..
"RC=$?; echo $RC > /tmp/openclaw-wechat-install.exit; " ..
wechat_npm_fallback_install_cmd(install_path, "/tmp/openclaw-wechat-install.log", "/tmp/openclaw-wechat-install.exit") ..
wechat_npm_direct_install_cmd(install_path, "/tmp/openclaw-wechat-install.log", "/tmp/openclaw-wechat-install.exit") ..
wechat_register_plugin_cmd(install_path, node_bin, "/tmp/openclaw-wechat-install.log") ..
-- 关键修复: 安装完成后强制修复插件目录权限 (确保 Gateway 可读取插件)
-- 原因: npx/npm 以 root 身份创建目录,默认权限 700 导致其他用户无法读取
@@ -1589,8 +1625,6 @@ function action_wechat_install()
oc_data, oc_data,
oc_data, oc_data, oc_data,
install_path, oc_data, oc_data, oc_data,
oc_data, oc_data, oc_data, oc_data,
install_path, install_path, npx_bin,
oc_data
)
sys.exec(install_cmd)
@@ -1731,15 +1765,23 @@ function action_wechat_login()
"if [ $RC -ne 0 ]; then echo '❌ 微信插件注册失败,无法登录' >> /tmp/openclaw-wechat-qrcode.txt; exit 0; fi; " ..
"cd %s && " ..
"_oc_as_openclaw 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw OPENCLAW_CONFIG_PATH=%s/.openclaw/openclaw.json " ..
"NODE_ICU_DATA=%s/node/share/icu " ..
"NPM_CONFIG_CACHE=%s/.npm npm_config_cache=%s/.npm TMPDIR=%s/.tmp PATH=%s/node/bin:%s/global/bin:$PATH " ..
"%s %s channels login --channel openclaw-weixin' >> /tmp/openclaw-wechat-qrcode.txt 2>&1; " ..
"echo $? > /tmp/openclaw-wechat-login.exit; " ..
") >/dev/null 2>&1 & echo $! > /tmp/openclaw-wechat-login.pid",
install_path, oc_entry, wechat_plugin_dir, node_bin,
oc_data, oc_data, oc_data, oc_data, oc_data, oc_data, oc_data,
oc_data, oc_data, oc_data, oc_data, oc_data, oc_data, oc_data,
oc_data, oc_data, oc_data, oc_data, oc_data, oc_data, oc_data, oc_data,
install_path, install_path, node_bin, oc_entry
oc_data, oc_data, oc_data,
oc_data,
oc_data, oc_data, oc_data,
oc_data, oc_data,
oc_data, oc_data, oc_data, oc_data, oc_data,
install_path,
oc_data, oc_data, oc_data, oc_data,
install_path,
oc_data, oc_data, oc_data,
install_path, install_path,
node_bin, oc_entry
)
sys.exec(login_cmd)
@@ -1798,8 +1840,15 @@ function action_wechat_login_status()
end
end
end
-- 检查是否登录成功
local logged_in = qrcode:find("登录成功") ~= nil or qrcode:find("成功登录") ~= nil or qrcode:find("Login success") ~= nil or qrcode:find("Logged in") ~= nil
-- 检查是否登录成功。微信插件在 OpenClaw 2026.6.11 中可能已经保存认证,
-- 但随后通过 Gateway 动态启动渠道时报 "invalid channels.start channel" 并返回
-- 非 0;这种情况账号已配对成功,LuCI 不应误判为失败。
local logged_in = qrcode:find("登录成功") ~= nil
or qrcode:find("成功登录") ~= nil
or qrcode:find("Login success") ~= nil
or qrcode:find("Logged in") ~= nil
or qrcode:find("已将此 OpenClaw 连接到微信") ~= nil
or qrcode:find("Local login saved auth for openclaw%-weixin") ~= nil
local state = "idle"
if logged_in then
@@ -1825,10 +1874,11 @@ function action_wechat_login_status()
end
end
-- 如果刚登录成功,触发一次重启,确保主进程加载微信账号
-- 如果刚登录成功,触发一次轻量重启,确保主进程加载微信账号。
-- 不使用完整 /etc/init.d/openclaw restart,避免同时重启 Web PTY 和拉长等待时间。
if state == "success" and not nixio.fs.stat("/tmp/openclaw-wechat-restarted", "type") then
sys.exec("touch /tmp/openclaw-wechat-restarted")
sys.exec("/etc/init.d/openclaw restart &")
sys.exec("/etc/init.d/openclaw restart_gateway >/dev/null 2>&1 &")
message = "微信登录成功,正在重新加载微信账号"
end
http.prepare_content("application/json")
@@ -2013,10 +2063,7 @@ function action_wechat_upgrade_plugin()
return
end
-- 后台执行升级 (其实就是重新安装最新版)
-- 在启动升级前,确保网关端口可用(自动清理残留 gateway 进程)
local port = uci:get("openclaw", "main", "port") or "18789"
ensure_port_free(port)
-- 后台执行升级 (其实就是重新安装最新版)。不要释放 Gateway 端口,避免误停正在运行的 Gateway。
-- 微信插件安装目录路径 (用于升级后权限修复)
local extensions_dir = install_path .. "/data/.openclaw/extensions"
local upgrade_cmd = string.format(
@@ -2033,13 +2080,7 @@ function action_wechat_upgrade_plugin()
"else mkdir -p \"$OC_WECHAT_DATA/.npm\" \"$OC_WECHAT_DATA/.tmp\" \"$OC_WECHAT_DATA/.openclaw/extensions\"; chown -R openclaw:openclaw \"$OC_WECHAT_DATA/.npm\" \"$OC_WECHAT_DATA/.tmp\" 2>/dev/null; chown openclaw:openclaw \"$OC_WECHAT_DATA/.openclaw\" 2>/dev/null; fi; " ..
"if [ ! -w %s/.npm ] || [ ! -w %s/.tmp ]; then echo '❌ npm cache/tmp 目录不可写' >> /tmp/openclaw-wechat-install.log; echo 1 > /tmp/openclaw-wechat-install.exit; exit 0; fi; " ..
"_oc_as_openclaw 'test -w %s/.npm && test -w %s/.tmp && test -w %s/.openclaw' || { echo '❌ openclaw 用户无法写入 npm cache/tmp/data 目录' >> /tmp/openclaw-wechat-install.log; echo 1 > /tmp/openclaw-wechat-install.exit; exit 0; }; " ..
"cd %s && " ..
"_oc_as_openclaw 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw " ..
"OPENCLAW_CONFIG_PATH=%s/.openclaw/openclaw.json NPM_CONFIG_CACHE=%s/.npm npm_config_cache=%s/.npm TMPDIR=%s/.tmp " ..
"PATH=%s/node/bin:%s/global/bin:$PATH " ..
"%s -y @tencent-weixin/openclaw-weixin-cli install' >> /tmp/openclaw-wechat-install.log 2>&1; " ..
"RC=$?; echo $RC > /tmp/openclaw-wechat-install.exit; " ..
wechat_npm_fallback_install_cmd(install_path, "/tmp/openclaw-wechat-install.log", "/tmp/openclaw-wechat-install.exit") ..
wechat_npm_direct_install_cmd(install_path, "/tmp/openclaw-wechat-install.log", "/tmp/openclaw-wechat-install.exit") ..
wechat_register_plugin_cmd(install_path, node_bin, "/tmp/openclaw-wechat-install.log") ..
-- 关键修复: 升级完成后强制修复插件目录权限 (确保 Gateway 可读取插件)
-- 注意: 保持 root:root 属主 (OpenClaw v2026.4.9+ 安全要求),仅修复权限模式
@@ -2052,8 +2093,6 @@ function action_wechat_upgrade_plugin()
oc_data, oc_data,
oc_data, oc_data, oc_data,
install_path, oc_data, oc_data, oc_data,
oc_data, oc_data, oc_data, oc_data,
install_path, install_path, npx_bin,
oc_data
)
sys.exec(upgrade_cmd)
@@ -2114,10 +2153,10 @@ function action_wechat_logout()
-- 在后台执行 logout
local logout_cmd = string.format(
openclaw_user_runner_cmd() .. "cd %s && _oc_as_openclaw 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw OPENCLAW_CONFIG_PATH=%s/.openclaw/openclaw.json " ..
openclaw_user_runner_cmd() .. "cd %s && _oc_as_openclaw 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw OPENCLAW_CONFIG_PATH=%s/.openclaw/openclaw.json NODE_ICU_DATA=%s/node/share/icu " ..
"PATH=%s/node/bin:%s/global/bin:$PATH " ..
"%s %s channels logout --channel openclaw-weixin --account \"%s\"'",
oc_data, oc_data, oc_data, oc_data, oc_data, install_path, install_path, node_bin, oc_entry, account_id
oc_data, oc_data, oc_data, oc_data, oc_data, install_path, install_path, install_path, node_bin, oc_entry, account_id
)
sys.exec(logout_cmd .. " >/dev/null 2>&1")
@@ -149,7 +149,7 @@ local port = uci:get("openclaw", "main", "port") or "18789"
loading.innerHTML = '<div style="text-align:center;color:#666;">' +
'<div style="font-size:40px;margin-bottom:12px;">⏳</div>' +
'<div style="font-size:15px;margin-bottom:6px;">OpenClaw 网关正在启动中...</div>' +
'<div style="font-size:12px;color:#999;">首次启动可能需要 2~5 分钟,请耐心等待,页面会自动刷新。</div>' +
'<div style="font-size:12px;color:#999;">通常 20~40 秒;首次安装或路由器负载较高时会更久,页面会自动刷新。</div>' +
'</div>';
} else {
statusTextEl.innerHTML = '<span style="color:#cf222e;">● 网关未运行</span>';
@@ -112,7 +112,7 @@
if (d.gateway_running) {
gwEl.innerHTML = '<span class="oc-dot oc-dot-green"></span>监听中 :' + d.port;
} else if (d.gateway_starting) {
gwEl.innerHTML = '<span class="oc-dot oc-dot-gray"></span>初始化中,首次启动可能需要 2~5 分钟...';
gwEl.innerHTML = '<span class="oc-dot oc-dot-gray"></span>初始化中,通常 20~40 秒;首次安装或负载较高时会更久...';
} else if (d.gateway_failed) {
gwEl.innerHTML = '<span class="oc-dot oc-dot-red"></span>启动失败,退出码 ' + (d.gateway_exit_code || '-');
} else {
+45 -6
View File
@@ -448,7 +448,13 @@ if(changed){
fi
fix_openclaw_state_permissions 2>/dev/null || true
rm -rf /tmp/jiti 2>/dev/null || true
# 不再每次启动都清理 /tmp/jiti。
# OpenClaw 在低性能 OpenWrt 设备上依赖 JITI/模块缓存降低冷启动成本;
# 每次 rm -rf 会让 gateway 在第一条 "starting..." 日志前重新编译/装载模块,
# 实测会额外拉长 20 秒以上。需要强制清理时再显式打开 UCI 开关。
if [ "$(uci -q get openclaw.main.clear_jiti_cache_on_start 2>/dev/null || echo 0)" = "1" ]; then
rm -rf /tmp/jiti 2>/dev/null || true
fi
# v2026.4.10: 优化配置迁移 - 只在版本变更时运行 doctor --fix
# 原因: doctor --fix 耗时 ~50 秒,每次重启都运行严重影响启动速度
@@ -637,6 +643,13 @@ _ensure_port_free() {
}
_ensure_port_free "$port"
# Node 22 原生编译缓存。OpenClaw 主入口在路由器上首次 import/compile 较慢;
# 保留该缓存可以显著缩短后续重启时 gateway 第一条日志前的等待时间。
local node_compile_cache="/tmp/openclaw-node-compile-cache"
mkdir -p "$node_compile_cache" 2>/dev/null || true
chown openclaw:openclaw "$node_compile_cache" 2>/dev/null || true
chmod 700 "$node_compile_cache" 2>/dev/null || true
# 启动 OpenClaw Gateway (主服务, 前台运行)
# v2026.4.7/4.9: 环境变量安全验证 (阻止危险变量注入)
_validate_path() {
@@ -660,13 +673,15 @@ _ensure_port_free "$port"
OPENCLAW_STATE_DIR="${OC_DATA}/.openclaw" \
OPENCLAW_CONFIG_PATH="$CONFIG_FILE" \
OPENCLAW_GATEWAY_TOKEN="$gw_token" \
NODE_COMPILE_CACHE="$node_compile_cache" \
NODE_ICU_DATA="${NODE_BASE}/share/icu" \
NODE_BASE="$NODE_BASE" \
OC_GLOBAL="$OC_GLOBAL" \
OC_DATA="$OC_DATA" \
PATH="${NODE_BASE}/bin:${OC_GLOBAL}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
procd_set_param user openclaw
procd_set_param respawn 3600 10 5
procd_set_param term_timeout 2
procd_set_param respawn 3600 1 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param pidfile /var/run/openclaw.pid
@@ -689,6 +704,7 @@ procd_set_param env \
OC_CONFIG_PORT="$pty_port" \
OC_PTY_TOKEN="$pty_token" \
OC_CONFIG_SCRIPT="/usr/share/openclaw/oc-config.sh" \
NODE_COMPILE_CACHE="$node_compile_cache" \
NODE_ICU_DATA="${NODE_BASE}/share/icu" \
NODE_BASE="$NODE_BASE" \
OC_GLOBAL="$OC_GLOBAL" \
@@ -699,6 +715,7 @@ OPENCLAW_STATE_DIR="${OC_DATA}/.openclaw" \
OPENCLAW_CONFIG_PATH="$CONFIG_FILE" \
PATH="${NODE_BASE}/bin:${OC_GLOBAL}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
procd_set_param respawn 3600 5 5
procd_set_param term_timeout 2
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param pidfile /var/run/openclaw-pty.pid
@@ -842,9 +859,10 @@ fi
}
status_service() {
local port pty_port
local port pty_port enabled
port=$(uci -q get openclaw.main.port || echo "18789")
pty_port=$(uci -q get openclaw.main.pty_port || echo "18793")
enabled=$(uci -q get openclaw.main.enabled || echo "0")
echo "=== OpenClaw 服务状态 ==="
@@ -908,10 +926,31 @@ local mins=$(( (uptime % 3600) / 60 ))
echo "运行时间: ${hours}小时 ${mins}分钟"
fi
fi
elif pgrep -f "openclaw.*gateway" >/dev/null 2>&1; then
echo "网关: 正在启动 (首次启动可能需要 2~5 分钟)"
else
echo "网关: 已停止"
local procd_running procd_pid procd_exit procd_pid_alive pidfile_pid pidfile_stale crash_loop
procd_running=$(ubus call service list '{"name":"openclaw"}' 2>/dev/null | jsonfilter -e '$.openclaw.instances.gateway.running' 2>/dev/null | tr -d '[:space:]')
procd_pid=$(ubus call service list '{"name":"openclaw"}' 2>/dev/null | jsonfilter -e '$.openclaw.instances.gateway.pid' 2>/dev/null | tr -d '[:space:]')
procd_exit=$(ubus call service list '{"name":"openclaw"}' 2>/dev/null | jsonfilter -e '$.openclaw.instances.gateway.exit_code' 2>/dev/null | tr -d '[:space:]')
[ "$procd_pid" = "null" ] && procd_pid=""
[ "$procd_exit" = "null" ] && procd_exit=""
procd_pid_alive=0
[ -n "$procd_pid" ] && [ -d "/proc/$procd_pid" ] && procd_pid_alive=1
pidfile_pid=$(cat /var/run/openclaw.pid 2>/dev/null | tr -d '[:space:]')
pidfile_stale=0
[ -n "$pidfile_pid" ] && [ ! -d "/proc/$pidfile_pid" ] && pidfile_stale=1
crash_loop=$(logread 2>/dev/null | grep -E 'Instance openclaw::gateway.*crash loop' | tail -1)
if [ "$enabled" != "1" ]; then
echo "网关: 已禁用"
elif [ -n "$procd_exit" ] && [ "$procd_exit" != "0" ] && [ "$procd_running" != "true" ]; then
echo "网关: 启动失败 (退出码 $procd_exit)"
elif [ -n "$crash_loop" ] && [ "$pidfile_stale" = "1" ] && [ "$procd_running" != "true" ] && [ "$procd_pid_alive" != "1" ]; then
echo "网关: 启动失败 (procd crash-loop 抑制)"
elif [ "$procd_running" = "true" ] || [ "$procd_pid_alive" = "1" ]; then
echo "网关: 正在启动 (通常 20~40 秒,首次安装或负载较高时会更久)"
[ -n "$procd_pid" ] && echo "进程ID: $procd_pid"
else
echo "网关: 已停止"
fi
fi
# PTY port
@@ -229,6 +229,35 @@ assert_node_runtime() {
if command -v oc_assert_node_min_version >/dev/null 2>&1; then
oc_assert_node_min_version "$NODE_BIN" "$required" || exit 1
fi
# OpenClaw CLI 依赖 Unicode property escapes(例如 \p{Extended_Pictographic})。
# ARM64 musl 自托管 Node 包必须带上 NODE_ICU_DATA,否则用户直接执行
# `openclaw status` 等子命令会在 CLI 入口阶段抛 SyntaxError。
if ! NODE_ICU_DATA="${NODE_BASE}/share/icu" "$NODE_BIN" -e "new RegExp('\\\\p{Extended_Pictographic}','u')" >/dev/null 2>&1; then
log_error "Node.js ICU/Unicode 正则能力异常,请重新安装 Node.js 运行时"
exit 1
fi
}
install_openclaw_cli_wrapper() {
local oc_entry
oc_entry=$(find_oc_entry)
[ -n "$oc_entry" ] || return 1
ensure_mkdir "$OC_GLOBAL/bin"
# npm 默认生成的 openclaw 可能是指向 openclaw.mjs 的 symlink。
# 写 wrapper 前必须先 unlink,否则重定向会顺着 symlink 覆盖真实 CLI 入口。
rm -f "$OC_GLOBAL/bin/openclaw" 2>/dev/null || true
cat > "$OC_GLOBAL/bin/openclaw" << EOF
#!/bin/sh
export HOME="${OC_DATA}"
export OPENCLAW_HOME="${OC_DATA}"
export OPENCLAW_STATE_DIR="${OC_DATA}/.openclaw"
export OPENCLAW_CONFIG_PATH="${OC_DATA}/.openclaw/openclaw.json"
export NODE_ICU_DATA="${NODE_BASE}/share/icu"
export PATH="${NODE_BASE}/bin:${OC_GLOBAL}/bin:\$PATH"
exec "${NODE_BIN}" "${oc_entry}" "\$@"
EOF
chmod +x "$OC_GLOBAL/bin/openclaw" 2>/dev/null || true
}
detect_arch() {
@@ -471,6 +500,7 @@ install_openclaw() {
fi
if [ -n "$oc_ver" ]; then
install_openclaw_cli_wrapper || true
log_info "OpenClaw v${oc_ver} 安装成功"
assert_node_runtime
else
@@ -510,6 +540,7 @@ do_setup() {
# 检查是否已安装
if [ -x "$NODE_BIN" ] && [ -n "$(find_oc_entry)" ]; then
install_openclaw_cli_wrapper || true
local oc_ver=""
local oc_entry="$(find_oc_entry)"
local oc_pkg_dir="$(dirname "$oc_entry")"
@@ -665,6 +696,7 @@ do_upgrade() {
echo ""
if [ -n "$new_ver" ]; then
install_openclaw_cli_wrapper || true
assert_node_runtime
if [ "$current_ver" = "$new_ver" ]; then
log_info "当前已是最新版本 v${new_ver}"
@@ -827,6 +859,7 @@ do_setup_offline() {
local oc_entry=$(find_oc_entry)
if [ -n "$oc_entry" ]; then
local oc_ver=$("$NODE_BIN" "$oc_entry" --version 2>/dev/null | tr -d '[:space:]')
install_openclaw_cli_wrapper || true
log_info "OpenClaw v${oc_ver} 安装成功"
assert_node_runtime
else
@@ -338,7 +338,15 @@ async function restartGateway() {
console.log(`\n${C.yellow}正在重启 Gateway...${C.reset}`);
try {
await runCommand('/etc/init.d/openclaw', ['restart_gateway']);
await runCommand('/bin/sh', ['-c', [
"if [ \"$(uci -q get openclaw.main.enabled 2>/dev/null || echo 0)\" != \"1\" ]; then",
" uci -q set openclaw.main.enabled='1';",
" uci -q commit openclaw;",
" /etc/init.d/openclaw enable >/dev/null 2>&1 || true;",
'fi;',
'/etc/init.d/openclaw restart_gateway >/dev/null 2>&1 || true;',
'/etc/init.d/openclaw start >/dev/null 2>&1 || true'
].join(' ')]);
} catch {}
const spin = spinner({ text: 'Gateway 启动中,请稍候 (约 15-30 秒)...' });
@@ -558,12 +558,22 @@ restart_gateway() {
# 修复数据目录权限 (root 用户操作可能改变了文件属主)
fix_openclaw_state_permissions 2>/dev/null || true
# 首次安装后 UCI 默认保持 disabled。用户在配置向导中确认“立即重启”
# 即表示希望当前配置生效,这里同步启用服务,避免提示重启但实际仍为“已禁用”。
if [ "$(uci -q get openclaw.main.enabled 2>/dev/null || echo 0)" != "1" ]; then
echo -e " ${CYAN}检测到 Gateway 未启用,正在启用服务...${NC}"
uci -q set openclaw.main.enabled='1' 2>/dev/null || true
uci -q commit openclaw 2>/dev/null || true
/etc/init.d/openclaw enable >/dev/null 2>&1 || true
fi
local port
port=$(json_get gateway.port)
port=${port:-18789}
# ── kill gateway 进程,让 procd respawn ──
/etc/init.d/openclaw restart_gateway >/dev/null 2>&1
/etc/init.d/openclaw start >/dev/null 2>&1
# ── 等待端口恢复 (最多 30 秒,含端口释放 + Node.js 冷启动) ──
echo -e " ${YELLOW}⏳ Gateway 启动中,请稍候 (约 15-30 秒)...${NC}"
+41 -1
View File
@@ -94,7 +94,9 @@ chmod +x "$DATA_DIR/etc/profile.d/openclaw.sh"
# 计算安装大小
INSTALLED_SIZE=$(du -sk "$DATA_DIR" | awk '{print $1}')
(cd "$DATA_DIR" && tar czf "$STAGING/data.tar.gz" .)
# 强制 data.tar.gz 内文件归属为 root:root,避免 GitHub runner / 本地构建用户
# 的 UID/GID 泄漏到用户机器(例如安装后出现 1001:1001)。
(cd "$DATA_DIR" && tar --owner=0 --group=0 --numeric-owner -czf "$STAGING/data.tar.gz" .)
# ── 构建 control.tar.gz ──
CTRL_DIR="$STAGING/control"
@@ -169,10 +171,48 @@ cat > "$CTRL_DIR/postinst" << 'EOF'
# 清理 LuCI 缓存
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* /tmp/luci-indexcache.*.json 2>/dev/null
# 双保险:确保系统侧文件保持 root:root,避免非 SDK 打包时构建机 UID/GID
# 泄漏到目标机器。不要触碰 /opt/openclaw 运行数据。
for p in \
/etc/init.d/openclaw \
/etc/profile.d/openclaw.sh \
/usr/bin/openclaw-env \
/usr/libexec/openclaw-permissions.sh \
/usr/lib/lua/luci/controller/openclaw.lua \
/usr/lib/lua/luci/model/cbi/openclaw \
/usr/lib/lua/luci/view/openclaw \
/usr/lib/lua/openclaw \
/usr/share/openclaw \
/usr/share/rpcd/acl.d/luci-app-openclaw.json
do
[ -e "$p" ] && chown -R root:root "$p" 2>/dev/null || true
done
# 升级/重装后修复已存在的 OpenClaw 运行数据权限。尤其是 npm 外部插件:
# npm 安装阶段需要 openclaw 可写,运行阶段插件源码目录必须 root:root,
# 否则 OpenClaw 会因为 suspicious ownership 拒绝加载微信插件。
OPENCLAW_INSTALL_BASE="$(uci -q get openclaw.main.install_path 2>/dev/null || echo /opt)"
if [ -r /usr/libexec/openclaw-paths.sh ]; then
. /usr/libexec/openclaw-paths.sh
oc_load_paths "$OPENCLAW_INSTALL_BASE" 2>/dev/null || true
else
OPENCLAW_INSTALL_BASE="${OPENCLAW_INSTALL_BASE%/}"
OC_DATA="${OPENCLAW_INSTALL_BASE}/openclaw/data"
fi
if [ -n "${OC_DATA:-}" ] && [ -d "${OC_DATA}/.openclaw" ] && [ -x /usr/libexec/openclaw-permissions.sh ]; then
/usr/libexec/openclaw-permissions.sh fix-state "${OC_DATA}/.openclaw" >/dev/null 2>&1 || true
fi
# 重启 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
# 如果用户原本启用了 OpenClaw,opkg reinstall/upgrade 后恢复服务。
if [ "$(uci -q get openclaw.main.enabled 2>/dev/null || echo 0)" = "1" ] && [ -x /etc/init.d/openclaw ]; then
/etc/init.d/openclaw enable >/dev/null 2>&1 || true
/etc/init.d/openclaw start >/dev/null 2>&1 || true
fi
exit 0
}
+21 -1
View File
@@ -132,6 +132,24 @@ echo "正在安装文件..."
ARCHIVE=$(awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' "$0")
tail -n +$ARCHIVE "$0" | tar xzf - -C / 2>/dev/null
# 双保险:即使构建环境 tar 不支持 owner 规范化,也不要把构建机 UID/GID
# 带到路由器系统文件中。这里仅修复本插件安装的系统侧文件,不触碰
# /opt/openclaw 运行数据和 OpenClaw npm 插件状态目录。
for p in \
/etc/init.d/openclaw \
/etc/profile.d/openclaw.sh \
/usr/bin/openclaw-env \
/usr/libexec/openclaw-permissions.sh \
/usr/lib/lua/luci/controller/openclaw.lua \
/usr/lib/lua/luci/model/cbi/openclaw \
/usr/lib/lua/luci/view/openclaw \
/usr/lib/lua/openclaw \
/usr/share/openclaw \
/usr/share/rpcd/acl.d/luci-app-openclaw.json
do
[ -e "$p" ] && chown -R root:root "$p" 2>/dev/null || true
done
# UCI 配置文件保护: 升级时不覆盖用户已有配置
if [ -f /etc/config/openclaw ] && [ -f /etc/config/openclaw.default ]; then
# 已有配置, 移除默认文件 (保留用户配置)
@@ -261,7 +279,9 @@ echo "[4/4] 打包..."
mkdir -p "$OUT_DIR"
# 创建 payload tarball
(cd "$STAGING/payload" && tar czf "$STAGING/payload.tar.gz" .)
# 强制 payload 内文件归属为 root:root,避免 GitHub runner / 本地构建用户
# 的 UID/GID 泄漏到用户机器(例如安装后出现 1001:1001)。
(cd "$STAGING/payload" && tar --owner=0 --group=0 --numeric-owner -czf "$STAGING/payload.tar.gz" .)
# 组合: installer header + payload
RUN_FILE="$OUT_DIR/${PKG_NAME}_${PKG_VERSION}.run"
@@ -11,6 +11,12 @@ grep -q "NODE_VERSION_V2=\"22.23.0\"" root/usr/bin/openclaw-env || fail "default
grep -q "OC_NODE_MIN_VERSION=\"\${OC_NODE_MIN_VERSION:-22.19.0}\"" root/usr/bin/openclaw-env || fail "minimum Node.js version not pinned"
grep -q "oc_assert_node_min_version" root/usr/bin/openclaw-env || fail "Node.js minimum version check missing"
grep -q 'oc_node_version_ge "$from_pkg" "$required"' root/usr/bin/openclaw-env || fail "package Node.js requirement must not lower static minimum"
grep -q "install_openclaw_cli_wrapper" root/usr/bin/openclaw-env || fail "OpenClaw CLI wrapper must set runtime env"
grep -q 'export NODE_ICU_DATA="${NODE_BASE}/share/icu"' root/usr/bin/openclaw-env || fail "OpenClaw CLI wrapper must export NODE_ICU_DATA"
grep -q 'rm -f "$OC_GLOBAL/bin/openclaw"' root/usr/bin/openclaw-env || fail "OpenClaw CLI wrapper must unlink npm symlink before writing"
grep -q "Extended_Pictographic" root/usr/bin/openclaw-env || fail "Node runtime must validate Unicode property escapes"
grep -q "uci -q set openclaw.main.enabled='1'" root/usr/share/openclaw/oc-config.sh || fail "traditional config restart must enable gateway after first install"
grep -q "uci -q set openclaw.main.enabled='1'" root/usr/share/openclaw/oc-config-interactive.js || fail "interactive config restart must enable gateway after first install"
if grep -q 'v1_tarball' root/usr/bin/openclaw-env; then
fail "installer must not silently fall back to legacy Node.js tarball"
fi
@@ -18,6 +24,10 @@ fi
grep -q "wechat.htm" Makefile || fail "Makefile must install wechat.htm"
grep -q "luci-app-openclaw.json" Makefile || fail "Makefile must install rpcd ACL"
grep -q "openclaw-permissions.sh" Makefile || fail "Makefile must install permission helper"
grep -q 'openclaw-permissions.sh fix-state "$${OC_DATA}/.openclaw"' Makefile || fail "postinst must repair existing OpenClaw state permissions after reinstall"
grep -q '/etc/init.d/openclaw start >/dev/null 2>&1' Makefile || fail "postinst must restart enabled OpenClaw service after reinstall"
grep -q 'openclaw-permissions.sh fix-state "${OC_DATA}/.openclaw"' scripts/build_ipk.sh || fail "release ipk postinst must repair existing OpenClaw state permissions after reinstall"
grep -q '/etc/init.d/openclaw start >/dev/null 2>&1' scripts/build_ipk.sh || fail "release ipk postinst must restart enabled OpenClaw service after reinstall"
if grep -q "openclaw.zh-cn.lmo" Makefile; then
fail "main package must not install openclaw.zh-cn.lmo"
fi
@@ -52,11 +62,19 @@ grep -q "packageName: '@tencent-weixin/openclaw-weixin'" luasrc/controller/openc
grep -q "channels\\['openclaw-weixin'\\].enabled = true" luasrc/controller/openclaw.lua || fail "wechat channel enable registration missing"
grep -q "Registered openclaw-weixin npm plugin" luasrc/controller/openclaw.lua || fail "wechat install log must confirm config registration"
grep -q "wechat_network_probe_cmd" luasrc/controller/openclaw.lua || fail "wechat network probe helper missing"
grep -q "NODE_ICU_DATA=%s/node/share/icu" luasrc/controller/openclaw.lua || fail "wechat LuCI CLI commands must export NODE_ICU_DATA"
grep -q "openclaw_user_runner_cmd" luasrc/controller/openclaw.lua || fail "wechat commands need user runner compatibility helper"
grep -q "ulimit -v unlimited" luasrc/controller/openclaw.lua || fail "wechat commands must lift LuCI inherited address-space limit before Node CLI"
grep -q "start-stop-daemon -S -m -p" luasrc/controller/openclaw.lua || fail "wechat commands must support OpenWrt without su"
grep -q "command -v curl" luasrc/controller/openclaw.lua || fail "wechat network probe should prefer curl"
grep -q "%%{http_code}" luasrc/controller/openclaw.lua || fail "wechat curl probe percent must be escaped for string.format"
grep -q "wechat_npm_fallback_install_cmd" luasrc/controller/openclaw.lua || fail "wechat install needs npm fallback for OpenWrt Node undici OOM"
if awk '/function action_wechat_install\(\)/,/function action_wechat_install_log\(\)/' luasrc/controller/openclaw.lua | grep -q "ensure_port_free"; then
fail "wechat install must not stop gateway before plugin installation"
fi
if awk '/function action_wechat_upgrade_plugin\(\)/,/function action_wechat_uninstall_plugin\(\)/' luasrc/controller/openclaw.lua | grep -q "ensure_port_free"; then
fail "wechat upgrade must not stop gateway before plugin upgrade"
fi
grep -q "npm install --omit=dev --omit=peer" luasrc/controller/openclaw.lua || fail "wechat npm fallback install command missing"
grep -q "tencent-weixin-openclaw-weixin-7783ac86ba" luasrc/controller/openclaw.lua || fail "wechat npm fallback project dir missing"
grep -q "export OC_WECHAT_DATA" luasrc/controller/openclaw.lua || fail "wechat npm fallback must export data dir into openclaw user shell"
@@ -66,6 +84,9 @@ grep -q "is_newer_version(latest_version, current_version)" luasrc/controller/op
grep -q "ilinkai.weixin.qq.com" luasrc/controller/openclaw.lua || fail "wechat network probe endpoint missing"
grep -q "微信接口连通性检查" luasrc/controller/openclaw.lua || fail "wechat network probe log missing"
grep -q "error_detail" luasrc/controller/openclaw.lua || fail "wechat login failure detail response missing"
grep -q "已将此 OpenClaw 连接到微信" luasrc/controller/openclaw.lua || fail "wechat login status must treat saved auth as success"
grep -q "Local login saved auth for openclaw%-weixin" luasrc/controller/openclaw.lua || fail "wechat login status must tolerate channels.start restart warning"
grep -q "/etc/init.d/openclaw restart_gateway >/dev/null 2>&1 &" luasrc/controller/openclaw.lua || fail "wechat login success should use lightweight gateway restart"
grep -q "self_heal_wechat_npm_plugin_config" root/etc/init.d/openclaw || fail "init.d wechat npm plugin self-heal missing"
grep -q "已自愈注册 openclaw-weixin npm plugin" root/etc/init.d/openclaw || fail "init.d wechat self-heal log missing"
grep -q "OC_WECHAT_PLUGIN_DIR" root/etc/init.d/openclaw || fail "init.d wechat self-heal plugin dir env missing"
@@ -74,6 +95,14 @@ grep -q "fix_openclaw_state_permissions" root/etc/init.d/openclaw || fail "init.
grep -q 'NODE_ICU_DATA="${NODE_BASE}/share/icu" PATH="${NODE_BASE}/bin:${OC_GLOBAL}/bin:$PATH"' root/etc/init.d/openclaw || fail "doctor migration must run with Node ICU data"
grep -q "Token 同步 (doctor 后): UCI -> JSON" root/etc/init.d/openclaw || fail "doctor must restore missing JSON token from UCI"
grep -q 'OPENCLAW_GATEWAY_TOKEN="$gw_token"' root/etc/init.d/openclaw || fail "gateway must receive token env fallback"
grep -q "clear_jiti_cache_on_start" root/etc/init.d/openclaw || fail "JITI cache cleanup must be opt-in to avoid slow startup"
grep -q "procd_set_param term_timeout 2" root/etc/init.d/openclaw || fail "procd term timeout must be bounded for faster restart"
grep -q "procd_set_param respawn 3600 1 5" root/etc/init.d/openclaw || fail "gateway respawn timeout must be short for faster restart_gateway"
grep -q 'openclaw.main.clear_jiti_cache_on_start' root/etc/init.d/openclaw || fail "JITI cleanup must be guarded by UCI flag"
grep -q "openclaw-node-compile-cache" root/etc/init.d/openclaw || fail "Node compile cache must be enabled for faster warm restarts"
grep -q 'NODE_COMPILE_CACHE="$node_compile_cache"' root/etc/init.d/openclaw || fail "gateway and pty must receive Node compile cache env"
grep -q "通常 20~40 秒" root/etc/init.d/openclaw luasrc/view/openclaw/status.htm luasrc/view/openclaw/console.htm || fail "startup hint must match measured OpenWrt timing"
grep -q "restart_gateway >/dev/null 2>&1 &" luasrc/controller/openclaw.lua || fail "LuCI restart action should use lightweight gateway restart"
grep -q "oc_fix_npm_projects_permissions" root/usr/libexec/openclaw-permissions.sh || fail "npm projects permission helper missing"
grep -q "openclaw.plugin.json" root/usr/libexec/openclaw-permissions.sh || fail "npm plugin root detection must use plugin manifest"
grep -q "node_modules/@tencent-weixin/openclaw-weixin" root/usr/libexec/openclaw-permissions.sh || fail "wechat npm plugin compatibility permission fix missing"
@@ -125,6 +154,10 @@ fi
grep -q "root/usr/libexec" scripts/build_ipk.sh || fail "ipk script must package shell helpers"
grep -q "root/usr/libexec" scripts/build_run.sh || fail "run script must package shell helpers"
grep -q "for dep in luci-compat luci-base curl openssl-util script-utils tar libstdcpp6" scripts/build_run.sh || fail ".run installer must install runtime dependencies"
grep -q -- "--owner=0 --group=0 --numeric-owner" scripts/build_run.sh || fail ".run payload must normalize file ownership to root"
grep -q -- "--owner=0 --group=0 --numeric-owner" scripts/build_ipk.sh || fail ".ipk payload must normalize file ownership to root"
grep -q "chown -R root:root" scripts/build_run.sh || fail ".run installer must repair root-owned system files after extraction"
grep -q "chown -R root:root" scripts/build_ipk.sh || fail ".ipk postinst must repair root-owned system files after extraction"
grep -q "先解压到临时目录并确认完整,再替换 NODE_BASE" root/usr/bin/openclaw-env || fail "Node install must not delete existing runtime before extraction succeeds"
grep -q "OC_SETUP_FRESH_ROOT" root/usr/bin/openclaw-env || fail "setup cleanup must preserve existing runtime roots"
@@ -16,10 +16,19 @@ grep -Fq "ubus call service list" "$CONTROLLER" || fail "status API should inspe
grep -Fq "openclaw.instances.gateway" "$CONTROLLER" || fail "status API should inspect gateway instance state"
grep -Fq "gateway_failed" "$CONTROLLER" || fail "status API should report gateway failure state"
grep -Fq "gateway_exit_code" "$CONTROLLER" || fail "status API should expose recent gateway exit code"
grep -Fq "gateway_crash_loop" "$CONTROLLER" || fail "status API should expose procd crash-loop state"
grep -Fq "procd_pid_alive" "$CONTROLLER" || fail "status API should ignore stale procd pidfiles"
grep -Fq "pidfile_stale" "$CONTROLLER" || fail "status API should require current stale pidfile evidence for crash-loop"
grep -Fq 'enabled=$(uci -q get openclaw.main.enabled' "$INIT_SCRIPT" || fail "status_service should honor disabled service state"
grep -Fq "网关: 已禁用" "$INIT_SCRIPT" || fail "status_service should report disabled gateway before crash-loop"
if grep -Fq 'pgrep -f "openclaw.*gateway"' "$CONTROLLER" "$INIT_SCRIPT" || grep -Fq "pgrep -f 'openclaw.*gateway'" "$CONTROLLER" "$INIT_SCRIPT"; then
fail "status checks must not use broad pgrep fallback that can match the status shell itself"
fi
grep -Fq "启动失败" "$STATUS_VIEW" || fail "status panel should distinguish startup failure from startup in progress"
grep -Fq "gateway_failed" "$STATUS_VIEW" || fail "status panel should render gateway failure state"
grep -Fq "ubus call service list" "$INIT_SCRIPT" || fail "status_service should inspect procd state via ubus"
grep -Fq "crash-loop 抑制" "$INIT_SCRIPT" || fail "status_service should report procd crash-loop suppression"
echo "ok"
+2 -2
View File
@@ -4,7 +4,7 @@ LUCI_TITLE:=luci-app-ssr-plus
LUCI_PKGARCH:=all
PKG_NAME:=luci-app-ssr-plus
PKG_VERSION:=196
PKG_RELEASE:=42
PKG_RELEASE:=43
PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
@@ -32,7 +32,7 @@ LUCI_PKGARCH:=all
LUCI_DEPENDS:= \
+libuci-lua +lua +luci-compat +coreutils +coreutils-base64 +dns2tcp +dnsmasq-full \
+jq +ip-full +lua-neturl +libuci-lua +microsocks +ipt2socks +lyaml \
+resolveip +curl +nping +unzip +xz-utils \
+resolveip +curl +nping +unzip +xz-utils +xz \
+PACKAGE_$(PKG_NAME)_INCLUDE_Xray:xray-core \
+PACKAGE_$(PKG_NAME)_INCLUDE_Xray:coreutils-timeout \
+PACKAGE_$(PKG_NAME)_INCLUDE_Http_Proxy:3proxy \
+1 -1
View File
@@ -11,7 +11,7 @@
"test": "node --test tests/*.test.js"
},
"devDependencies": {
"@eamonxg/aurora-tokens": "^1.0.0",
"@eamonxg/aurora-tokens": "^1.0.1",
"@tailwindcss/vite": "^4.1.11",
"@types/node": "^24.0.0",
"browserslist": "^4.28.2",
+112 -226
View File
@@ -7,20 +7,22 @@ settings:
importers:
.:
dependencies:
colorjs.io:
specifier: ^0.6.1
version: 0.6.1
devDependencies:
'@eamonxg/aurora-tokens':
specifier: ^1.0.1
version: 1.0.1
'@tailwindcss/vite':
specifier: ^4.1.11
version: 4.1.18(vite@7.3.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0))
version: 4.3.2(vite@7.3.1(@types/node@24.10.9)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0))
'@types/node':
specifier: ^24.0.0
version: 24.10.9
browserslist:
specifier: ^4.28.2
version: 4.28.2
colorjs.io:
specifier: ^0.6.1
version: 0.6.1
lightningcss:
specifier: ^1.32.0
version: 1.32.0
@@ -32,10 +34,10 @@ importers:
version: 0.7.2(prettier@3.8.1)
tailwind-scrollbar:
specifier: ^4.0.2
version: 4.0.2(react@19.1.1)(tailwindcss@4.1.18)
version: 4.0.2(react@19.1.1)(tailwindcss@4.3.2)
tailwindcss:
specifier: ^4.1.11
version: 4.1.18
version: 4.3.2
terser:
specifier: ^5.43.1
version: 5.46.0
@@ -44,10 +46,13 @@ importers:
version: 1.4.0
vite:
specifier: ^7.1.1
version: 7.3.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)
version: 7.3.1(@types/node@24.10.9)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)
packages:
'@eamonxg/aurora-tokens@1.0.1':
resolution: {integrity: sha512-AbqXZfEEDwxLiSjg+h4vFHeEE0GTb7vwyKvfh7GJK3TKCP0DNTfJpdIqI+6KhiXDLQI5nHuwYvbmx77o+baAjA==}
'@esbuild/aix-ppc64@0.27.2':
resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
engines: {node: '>=18'}
@@ -361,69 +366,69 @@ packages:
cpu: [x64]
os: [win32]
'@tailwindcss/node@4.1.18':
resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==}
'@tailwindcss/node@4.3.2':
resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==}
'@tailwindcss/oxide-android-arm64@4.1.18':
resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-android-arm64@4.3.2':
resolution: {integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [android]
'@tailwindcss/oxide-darwin-arm64@4.1.18':
resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-darwin-arm64@4.3.2':
resolution: {integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [darwin]
'@tailwindcss/oxide-darwin-x64@4.1.18':
resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-darwin-x64@4.3.2':
resolution: {integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==}
engines: {node: '>= 20'}
cpu: [x64]
os: [darwin]
'@tailwindcss/oxide-freebsd-x64@4.1.18':
resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-freebsd-x64@4.3.2':
resolution: {integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==}
engines: {node: '>= 20'}
cpu: [x64]
os: [freebsd]
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2':
resolution: {integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==}
engines: {node: '>= 20'}
cpu: [arm]
os: [linux]
'@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-linux-arm64-gnu@4.3.2':
resolution: {integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-linux-arm64-musl@4.3.2':
resolution: {integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-linux-x64-gnu@4.3.2':
resolution: {integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-linux-x64-musl@4.3.2':
resolution: {integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
'@tailwindcss/oxide-wasm32-wasi@4.3.2':
resolution: {integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
@@ -434,26 +439,26 @@ packages:
- '@emnapi/wasi-threads'
- tslib
'@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-win32-arm64-msvc@4.3.2':
resolution: {integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [win32]
'@tailwindcss/oxide-win32-x64-msvc@4.1.18':
resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==}
engines: {node: '>= 10'}
'@tailwindcss/oxide-win32-x64-msvc@4.3.2':
resolution: {integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==}
engines: {node: '>= 20'}
cpu: [x64]
os: [win32]
'@tailwindcss/oxide@4.1.18':
resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==}
engines: {node: '>= 10'}
'@tailwindcss/oxide@4.3.2':
resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==}
engines: {node: '>= 20'}
'@tailwindcss/vite@4.1.18':
resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==}
'@tailwindcss/vite@4.3.2':
resolution: {integrity: sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==}
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
vite: ^5.2.0 || ^6 || ^7 || ^8
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@@ -502,8 +507,8 @@ packages:
electron-to-chromium@1.5.372:
resolution: {integrity: sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==}
enhanced-resolve@5.18.4:
resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==}
enhanced-resolve@5.21.6:
resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==}
engines: {node: '>=10.13.0'}
esbuild@0.27.2:
@@ -532,77 +537,40 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
jiti@2.6.1:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
jiti@2.7.0:
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
hasBin: true
lightningcss-android-arm64@1.30.2:
resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [android]
lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [android]
lightningcss-darwin-arm64@1.30.2:
resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
lightningcss-darwin-arm64@1.32.0:
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
lightningcss-darwin-x64@1.30.2:
resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
lightningcss-darwin-x64@1.32.0:
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
lightningcss-freebsd-x64@1.30.2:
resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
lightningcss-freebsd-x64@1.32.0:
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
lightningcss-linux-arm-gnueabihf@1.30.2:
resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
lightningcss-linux-arm-gnueabihf@1.32.0:
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
lightningcss-linux-arm64-gnu@1.30.2:
resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-gnu@1.32.0:
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
engines: {node: '>= 12.0.0'}
@@ -610,13 +578,6 @@ packages:
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'}
@@ -624,13 +585,6 @@ packages:
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'}
@@ -638,13 +592,6 @@ packages:
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'}
@@ -652,34 +599,18 @@ packages:
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
lightningcss-win32-x64-msvc@1.30.2:
resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
lightningcss-win32-x64-msvc@1.32.0:
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
lightningcss@1.30.2:
resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
engines: {node: '>= 12.0.0'}
lightningcss@1.32.0:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
@@ -798,11 +729,11 @@ packages:
peerDependencies:
tailwindcss: 4.x
tailwindcss@4.1.18:
resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
tailwindcss@4.3.2:
resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==}
tapable@2.3.0:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
tapable@2.3.3:
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
engines: {node: '>=6'}
terser@5.46.0:
@@ -868,6 +799,10 @@ packages:
snapshots:
'@eamonxg/aurora-tokens@1.0.1':
dependencies:
colorjs.io: 0.6.1
'@esbuild/aix-ppc64@0.27.2':
optional: true
@@ -1045,73 +980,73 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.57.1':
optional: true
'@tailwindcss/node@4.1.18':
'@tailwindcss/node@4.3.2':
dependencies:
'@jridgewell/remapping': 2.3.5
enhanced-resolve: 5.18.4
jiti: 2.6.1
lightningcss: 1.30.2
enhanced-resolve: 5.21.6
jiti: 2.7.0
lightningcss: 1.32.0
magic-string: 0.30.21
source-map-js: 1.2.1
tailwindcss: 4.1.18
tailwindcss: 4.3.2
'@tailwindcss/oxide-android-arm64@4.1.18':
'@tailwindcss/oxide-android-arm64@4.3.2':
optional: true
'@tailwindcss/oxide-darwin-arm64@4.1.18':
'@tailwindcss/oxide-darwin-arm64@4.3.2':
optional: true
'@tailwindcss/oxide-darwin-x64@4.1.18':
'@tailwindcss/oxide-darwin-x64@4.3.2':
optional: true
'@tailwindcss/oxide-freebsd-x64@4.1.18':
'@tailwindcss/oxide-freebsd-x64@4.3.2':
optional: true
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
'@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2':
optional: true
'@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
'@tailwindcss/oxide-linux-arm64-gnu@4.3.2':
optional: true
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
'@tailwindcss/oxide-linux-arm64-musl@4.3.2':
optional: true
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
'@tailwindcss/oxide-linux-x64-gnu@4.3.2':
optional: true
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
'@tailwindcss/oxide-linux-x64-musl@4.3.2':
optional: true
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
'@tailwindcss/oxide-wasm32-wasi@4.3.2':
optional: true
'@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
'@tailwindcss/oxide-win32-arm64-msvc@4.3.2':
optional: true
'@tailwindcss/oxide-win32-x64-msvc@4.1.18':
'@tailwindcss/oxide-win32-x64-msvc@4.3.2':
optional: true
'@tailwindcss/oxide@4.1.18':
'@tailwindcss/oxide@4.3.2':
optionalDependencies:
'@tailwindcss/oxide-android-arm64': 4.1.18
'@tailwindcss/oxide-darwin-arm64': 4.1.18
'@tailwindcss/oxide-darwin-x64': 4.1.18
'@tailwindcss/oxide-freebsd-x64': 4.1.18
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.18
'@tailwindcss/oxide-linux-arm64-musl': 4.1.18
'@tailwindcss/oxide-linux-x64-gnu': 4.1.18
'@tailwindcss/oxide-linux-x64-musl': 4.1.18
'@tailwindcss/oxide-wasm32-wasi': 4.1.18
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.18
'@tailwindcss/oxide-win32-x64-msvc': 4.1.18
'@tailwindcss/oxide-android-arm64': 4.3.2
'@tailwindcss/oxide-darwin-arm64': 4.3.2
'@tailwindcss/oxide-darwin-x64': 4.3.2
'@tailwindcss/oxide-freebsd-x64': 4.3.2
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.2
'@tailwindcss/oxide-linux-arm64-gnu': 4.3.2
'@tailwindcss/oxide-linux-arm64-musl': 4.3.2
'@tailwindcss/oxide-linux-x64-gnu': 4.3.2
'@tailwindcss/oxide-linux-x64-musl': 4.3.2
'@tailwindcss/oxide-wasm32-wasi': 4.3.2
'@tailwindcss/oxide-win32-arm64-msvc': 4.3.2
'@tailwindcss/oxide-win32-x64-msvc': 4.3.2
'@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0))':
'@tailwindcss/vite@4.3.2(vite@7.3.1(@types/node@24.10.9)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0))':
dependencies:
'@tailwindcss/node': 4.1.18
'@tailwindcss/oxide': 4.1.18
tailwindcss: 4.1.18
vite: 7.3.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)
'@tailwindcss/node': 4.3.2
'@tailwindcss/oxide': 4.3.2
tailwindcss: 4.3.2
vite: 7.3.1(@types/node@24.10.9)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)
'@types/estree@1.0.8': {}
@@ -1147,10 +1082,10 @@ snapshots:
electron-to-chromium@1.5.372: {}
enhanced-resolve@5.18.4:
enhanced-resolve@5.21.6:
dependencies:
graceful-fs: 4.2.11
tapable: 2.3.0
tapable: 2.3.3
esbuild@0.27.2:
optionalDependencies:
@@ -1192,90 +1127,41 @@ snapshots:
graceful-fs@4.2.11: {}
jiti@2.6.1: {}
lightningcss-android-arm64@1.30.2:
optional: true
jiti@2.7.0: {}
lightningcss-android-arm64@1.32.0:
optional: true
lightningcss-darwin-arm64@1.30.2:
optional: true
lightningcss-darwin-arm64@1.32.0:
optional: true
lightningcss-darwin-x64@1.30.2:
optional: true
lightningcss-darwin-x64@1.32.0:
optional: true
lightningcss-freebsd-x64@1.30.2:
optional: true
lightningcss-freebsd-x64@1.32.0:
optional: true
lightningcss-linux-arm-gnueabihf@1.30.2:
optional: true
lightningcss-linux-arm-gnueabihf@1.32.0:
optional: true
lightningcss-linux-arm64-gnu@1.30.2:
optional: true
lightningcss-linux-arm64-gnu@1.32.0:
optional: true
lightningcss-linux-arm64-musl@1.30.2:
optional: true
lightningcss-linux-arm64-musl@1.32.0:
optional: true
lightningcss-linux-x64-gnu@1.30.2:
optional: true
lightningcss-linux-x64-gnu@1.32.0:
optional: true
lightningcss-linux-x64-musl@1.30.2:
optional: true
lightningcss-linux-x64-musl@1.32.0:
optional: true
lightningcss-win32-arm64-msvc@1.30.2:
optional: true
lightningcss-win32-arm64-msvc@1.32.0:
optional: true
lightningcss-win32-x64-msvc@1.30.2:
optional: true
lightningcss-win32-x64-msvc@1.32.0:
optional: true
lightningcss@1.30.2:
dependencies:
detect-libc: 2.1.2
optionalDependencies:
lightningcss-android-arm64: 1.30.2
lightningcss-darwin-arm64: 1.30.2
lightningcss-darwin-x64: 1.30.2
lightningcss-freebsd-x64: 1.30.2
lightningcss-linux-arm-gnueabihf: 1.30.2
lightningcss-linux-arm64-gnu: 1.30.2
lightningcss-linux-arm64-musl: 1.30.2
lightningcss-linux-x64-gnu: 1.30.2
lightningcss-linux-x64-musl: 1.30.2
lightningcss-win32-arm64-msvc: 1.30.2
lightningcss-win32-x64-msvc: 1.30.2
lightningcss@1.32.0:
dependencies:
detect-libc: 2.1.2
@@ -1364,16 +1250,16 @@ snapshots:
source-map@0.6.1: {}
tailwind-scrollbar@4.0.2(react@19.1.1)(tailwindcss@4.1.18):
tailwind-scrollbar@4.0.2(react@19.1.1)(tailwindcss@4.3.2):
dependencies:
prism-react-renderer: 2.4.1(react@19.1.1)
tailwindcss: 4.1.18
tailwindcss: 4.3.2
transitivePeerDependencies:
- react
tailwindcss@4.1.18: {}
tailwindcss@4.3.2: {}
tapable@2.3.0: {}
tapable@2.3.3: {}
terser@5.46.0:
dependencies:
@@ -1397,7 +1283,7 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
vite@7.3.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0):
vite@7.3.1(@types/node@24.10.9)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0):
dependencies:
esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3)
@@ -1408,6 +1294,6 @@ snapshots:
optionalDependencies:
'@types/node': 24.10.9
fsevents: 2.3.3
jiti: 2.6.1
jiti: 2.7.0
lightningcss: 1.32.0
terser: 5.46.0
+2 -2
View File
@@ -11,7 +11,7 @@
--text: oklch(0.21 0.02 264);
--brand: oklch(0.58 0.14 233);
--on-brand: oklch(1 0 0);
--link: oklch(0.74 0.238 322.16);
--link: oklch(0.58 0.14 233);
--info: oklch(0.45 0.12 255);
--warning: oklch(0.35 0.08 60);
--success: oklch(0.32 0.09 165);
@@ -53,7 +53,7 @@
--text: oklch(0.985 0.002 264);
--brand: oklch(0.6 0.13 188.745);
--on-brand: oklch(1 0 0);
--link: oklch(0.77 0.14 168);
--link: oklch(0.6 0.13 188.745);
--info: oklch(0.8 0.11 255);
--warning: oklch(0.82 0.13 80);
--success: oklch(0.72 0.13 158);
@@ -6,7 +6,7 @@
}
&:not(.btn):not(.cbi-button) {
@apply border-hairline bg-surface rounded-2xl border;
@apply border-hairline bg-surface min-w-52.5 max-w-full rounded-2xl border md:max-w-100;
& > ul > li {
&[placeholder] {
@apply hidden;
@@ -116,6 +116,10 @@
}
}
&[empty] > .more {
@apply flex-1;
}
&[open] {
& > ul.dropdown > li > form {
@apply flex items-center;
@@ -6,7 +6,7 @@ input[type="text"],
input[type="password"],
.cbi-input-text,
.cbi-input {
@apply text-text border-hairline bg-surface-overlay dark:bg-surface-sunken placeholder-text-muted focus:border-brand focus:ring-focus-ring relative rounded-2xl border px-3 py-1.5 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
@apply text-text border-hairline bg-surface-overlay dark:bg-surface-sunken placeholder-text-muted focus:border-brand focus:ring-focus-ring relative w-52.5 max-w-full rounded-2xl border px-3 py-1.5 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
.table.cbi-section-table & {
@apply w-full;
@@ -1,5 +1,5 @@
select {
@apply text-text border-hairline bg-surface-overlay dark:bg-surface-sunken focus:border-brand focus:ring-focus-ring appearance-none rounded-2xl border px-3 py-1.5 pr-10 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
@apply text-text border-hairline bg-surface-overlay dark:bg-surface-sunken focus:border-brand focus:ring-focus-ring min-w-52.5 max-w-full appearance-none md:max-w-100 rounded-2xl border px-3 py-1.5 pr-10 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
@apply bg-[url('@assets/icons/arrow-down.svg')] bg-size-[16px] bg-position-[right_0.75rem_center] bg-no-repeat dark:bg-[url('@assets/icons/arrow-down-dark.svg')];
&[disabled] {
@apply cursor-not-allowed opacity-40 dark:opacity-30;
@@ -38,6 +38,13 @@ table.table,
@apply max-md:border-hairline max-md:flex max-md:flex-wrap max-md:border-b max-md:px-2 max-md:py-2;
@apply has-[.cbi-progressbar]:max-md:flex has-[.cbi-progressbar]:max-md:items-center has-[.cbi-progressbar]:max-md:justify-center;
&:first-child {
& th,
& .th {
@apply first:rounded-tl-2xl last:rounded-tr-2xl;
}
}
&:last-child {
@apply max-md:border-b-0;
&[data-title] {
@@ -102,7 +109,7 @@ table.table,
& th,
& .th {
@apply border-hairline bg-surface-sunken text-text-subtle border-b px-3 py-3 text-left text-xs font-semibold tracking-wider uppercase first:rounded-tl-2xl last:rounded-tr-2xl max-md:flex-[1_1_50%] max-md:border-0 max-md:bg-transparent;
@apply border-hairline bg-surface-sunken text-text-subtle border-b px-3 py-3 text-left text-xs font-semibold tracking-wider uppercase max-md:flex-[1_1_50%] max-md:border-0 max-md:bg-transparent;
#cbi-samba4 & {
@apply px-0.5 py-1.5;
@@ -152,13 +159,32 @@ table.table,
& > input[type="text"],
& > input[type="password"],
& > select,
& > .cbi-dropdown:not(.btn):not(.cbi-button) {
& select,
& .cbi-dropdown:not(.btn):not(.cbi-button) {
@apply w-full;
}
/* Widgets are nested (td > div > div > widget), so the fixed/min widths
from _select.css/_dropdown.css must yield to the column width here. */
& select,
& .cbi-dropdown:not(.btn):not(.cbi-button) {
@apply min-w-fit;
}
&.cbi-value-field {
@apply break-all;
/* Mobile cards (bootstrap-style): value fields stack one per row —
the row title and long match-rule cells get the full width — while
display-only widgets below pair up two per row. */
@apply break-all max-md:mb-1 max-md:basis-full max-md:self-start max-md:text-left;
&[data-widget="CBI.DummyValue"],
&[data-widget="CBI.Button"],
&[data-widget="CBI.FlagValue"],
&[data-widget="dvalue"],
&[data-widget="button"],
&[data-widget="fvalue"] {
@apply max-md:basis-1/2;
}
}
&.cbi-section-actions {
@@ -1,5 +1,11 @@
.cbi-tooltip {
@apply bg-surface-overlay text-text border-hairline absolute z-110 max-w-xs scale-95 rounded-xl border px-3 py-2 text-xs break-normal whitespace-normal opacity-0 shadow-xl transition-[visibility,opacity,scale] duration-150 ease-out;
/* Hidden bubbles park off-screen (bootstrap's `left:-10000px` trick): a
visibility-hidden absolute box left at its anchor still counts into the
page's scrollable overflow, so the horizontal scrollbar would survive the
bubble's disappearance and the page could never snap back. `:hover`
returns it via `inset-s-auto`; the inset isn't transitioned, so the move
is instant while opacity/scale animate. */
@apply bg-surface-overlay text-text border-hairline absolute inset-s-[-10000px] z-110 w-max max-w-xs scale-95 rounded-xl border px-3 py-2 text-xs break-normal whitespace-normal opacity-0 shadow-xl transition-[visibility,opacity,scale] duration-150 ease-out;
&.error {
@apply border-hairline bg-danger-surface text-danger;
}
@@ -23,6 +29,6 @@
}
&:hover .cbi-tooltip {
@apply visible scale-100 opacity-100;
@apply visible scale-100 opacity-100 inset-s-auto;
}
}
+12 -2
View File
@@ -187,6 +187,16 @@ test("tables get a hairline frame + sunken header, body stays unclipped", () =>
assert.ok(!root.includes("overflow-hidden"), `body clipped: ${root}`);
assert.ok(root.includes("overflow-visible"), `not overflow-visible: ${root}`);
// Header carries the sunken surface.
const th = t.match(/&\s*th,\s*&\s*\.th\s*\{\s*@apply\s+([^;]+);/)?.[1] ?? "";
assert.ok(th.includes("bg-surface-sunken"), `header not sunken: ${th}`);
const thRules = [...t.matchAll(/&\s*th,\s*&\s*\.th\s*\{\s*@apply\s+([^;]+);/g)].map((m) => m[1]);
const th = thRules.find((rule) => rule.includes("bg-surface-sunken")) ?? "";
assert.ok(th, `header not sunken: ${thRules.join(" | ")}`);
// Top corners round only on the table's first row; a second header row
// (e.g. .cbi-section-table-descr) must stay square.
assert.ok(!th.includes("rounded-t"), `generic header cells carry corner radius: ${th}`);
const firstRow =
t.match(/&:first-child\s*\{\s*&\s*th,\s*&\s*\.th\s*\{\s*@apply\s+([^;]+);/)?.[1] ?? "";
assert.ok(
firstRow.includes("first:rounded-tl-2xl") && firstRow.includes("last:rounded-tr-2xl"),
`first-row corner radius missing: ${firstRow}`,
);
});
+2 -2
View File
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CSS)
LUCI_DEPENDS:=+luci-base
PKG_VERSION:=1.0.10
PKG_RELEASE:=45
PKG_VERSION:=1.0.13
PKG_RELEASE:=46
PKG_LICENSE:=Apache-2.0
LUCI_MINIFY_CSS:=
@@ -1,2 +1,2 @@
/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-content:"";--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-tracking:initial}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--font-mono:var(--font-mono);--spacing:var(--spacing);--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-widest:.1em;--radius-sm:calc(var(--radius-base) * .25);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--radius-2xl:calc(var(--radius-base) * 2);--radius-3xl:calc(var(--radius-base) * 3);--radius-4xl:calc(var(--radius-base) * 4);--shadow-sm:var(--app-shadow-sm);--shadow-md:var(--app-shadow-md);--shadow-lg:var(--app-shadow-md);--shadow-xl:var(--app-shadow-lg);--shadow-2xl:var(--app-shadow-lg);--animate-spin:spin 1s linear infinite;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:calc(var(--radius-base) * .5);--container-max-width:var(--container-max-width)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacinLine truncated
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-content:"";--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-tracking:initial}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--font-mono:var(--font-mono);--spacing:var(--spacing);--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-widest:.1em;--radius-sm:calc(var(--radius-base) * .25);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--radius-2xl:calc(var(--radius-base) * 2);--radius-3xl:calc(var(--radius-base) * 3);--radius-4xl:calc(var(--radius-base) * 4);--shadow-sm:var(--app-shadow-sm);--shadow-md:var(--app-shadow-md);--shadow-lg:var(--app-shadow-md);--shadow-xl:var(--app-shadow-lg);--shadow-2xl:var(--app-shadow-lg);--animate-spin:spin 1s linear infinite;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:calc(var(--radius-base) * .5);--container-max-width:var(--container-max-width)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacinLine truncated
@@ -1,2 +1,2 @@
/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0;--tw-scrollbar-thumb:#0000;--tw-scrollbar-track:#0000;--tw-leading:initial;--tw-tracking:initial;--tw-ease:initial;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-border-spacing-x:0;--tw-border-spacing-y:0}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--font-mono:var(--font-mono);--spacing:var(--spacing);--container-xs:20rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:calc(var(--radius-base) * .25);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--radius-2xl:calc(var(--radius-base) * 2);--radius-3xl:calc(var(--radius-base) * 3);--radius-4xl:calc(var(--radius-base) * 4);--shadow-sm:var(--app-shadow-sm);--shadow-md:var(--app-shadow-md);--shadow-lg:var(--app-shadow-md);--shadow-xl:var(--app-shadow-lg);--shadow-2xl:var(--app-shadow-lg);--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-md:12px;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:calc(var(--radius-base) * .5);--container-max-width:var(--container-max-width)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-familyLine truncated
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0;--tw-scrollbar-thumb:#0000;--tw-scrollbar-track:#0000;--tw-leading:initial;--tw-tracking:initial;--tw-ease:initial;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-border-spacing-x:0;--tw-border-spacing-y:0}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--font-mono:var(--font-mono);--spacing:var(--spacing);--container-xs:20rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:calc(var(--radius-base) * .25);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--radius-2xl:calc(var(--radius-base) * 2);--radius-3xl:calc(var(--radius-base) * 3);--radius-4xl:calc(var(--radius-base) * 4);--shadow-sm:var(--app-shadow-sm);--shadow-md:var(--app-shadow-md);--shadow-lg:var(--app-shadow-md);--shadow-xl:var(--app-shadow-lg);--shadow-2xl:var(--app-shadow-lg);--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-md:12px;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:calc(var(--radius-base) * .5);--container-max-width:var(--container-max-width)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-familyLine truncated
+2 -2
View File
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=torrserver
PKG_VERSION:=MatriX.142
PKG_RELEASE:=12
PKG_RELEASE:=13
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/YouROK/TorrServer.git
@@ -73,7 +73,7 @@ define Package/$(PKG_NAME)/description
TorrServer is a tool for streaming torrent files.
endef
GO_HOST:=$(STAGING_DIR_HOSTPKG)/bin/go
GO_HOST=$(firstword $(wildcard $(STAGING_DIR_HOSTPKG)/bin/go) $(wildcard $(STAGING_DIR_HOSTPKG)/bin/go[0-9].[0-9][0-9]) $(STAGING_DIR_HOSTPKG)/bin/go)
UPX:=$(STAGING_DIR_HOST)/bin/upx
UPX_AVAILABLE:=$(shell [ -x "$(UPX)" ] && echo yes || echo no)
TARGET_CC:=$(TARGET_CROSS)gcc